From e1189e8d7e9743ce18142e133d008a99bbe05bee Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Wed, 5 Mar 2025 17:06:41 +0800 Subject: [PATCH 01/15] update apcnet --- .../apcnet/pytorch/.gitignore | 120 -- .../apcnet/pytorch/CITATION.cff | 8 - .../apcnet/pytorch/LICENSE | 203 --- .../apcnet/pytorch/README.md | 136 +- .../pytorch/configs/_base_/datasets/ade20k.py | 54 - .../configs/_base_/datasets/ade20k_640x640.py | 54 - .../configs/_base_/datasets/cityscapes.py | 54 - .../_base_/datasets/cityscapes_1024x1024.py | 35 - .../_base_/datasets/cityscapes_768x768.py | 35 - .../_base_/datasets/cityscapes_769x769.py | 35 - .../_base_/datasets/cityscapes_832x832.py | 35 - .../configs/_base_/datasets/coco-stuff10k.py | 57 - .../configs/_base_/datasets/coco-stuff164k.py | 54 - .../configs/_base_/datasets/pascal_context.py | 60 - .../_base_/datasets/pascal_context_59.py | 60 - .../configs/_base_/datasets/pascal_voc12.py | 58 - .../_base_/datasets/pascal_voc12_aug.py | 9 - .../pytorch/configs/_base_/default_runtime.py | 14 - .../configs/_base_/models/apcnet_r50-d8.py | 44 - .../configs/_base_/schedules/schedule_160k.py | 9 - .../configs/_base_/schedules/schedule_1k.py | 9 - .../configs/_base_/schedules/schedule_20k.py | 9 - .../configs/_base_/schedules/schedule_320k.py | 9 - .../configs/_base_/schedules/schedule_40k.py | 9 - .../configs/_base_/schedules/schedule_80k.py | 9 - .../apcnet/pytorch/configs/apcnet/README.md | 58 - .../apcnet/pytorch/configs/apcnet/apcnet.yml | 232 --- .../apcnet_r101-d8_512x1024_40k_cityscapes.py | 2 - .../apcnet_r101-d8_512x1024_80k_cityscapes.py | 2 - .../apcnet_r101-d8_512x512_160k_ade20k.py | 2 - .../apcnet_r101-d8_512x512_80k_ade20k.py | 2 - .../apcnet_r101-d8_769x769_40k_cityscapes.py | 2 - .../apcnet_r101-d8_769x769_80k_cityscapes.py | 2 - .../apcnet_r50-d8_512x1024_1k_cityscapes.py | 4 - .../apcnet_r50-d8_512x1024_40k_cityscapes.py | 4 - .../apcnet_r50-d8_512x1024_80k_cityscapes.py | 4 - .../apcnet_r50-d8_512x512_160k_ade20k.py | 6 - .../apcnet/apcnet_r50-d8_512x512_1k_voc.py | 6 - .../apcnet/apcnet_r50-d8_512x512_20k_voc.py | 6 - .../apcnet_r50-d8_512x512_80k_ade20k.py | 6 - .../apcnet_r50-d8_769x769_40k_cityscapes.py | 9 - .../apcnet_r50-d8_769x769_80k_cityscapes.py | 9 - .../apcnet/pytorch/docker/Dockerfile | 32 - .../apcnet/pytorch/docker/serve/Dockerfile | 49 - .../pytorch/docker/serve/config.properties | 5 - .../apcnet/pytorch/docker/serve/entrypoint.sh | 12 - .../apcnet/pytorch/mmcv/__init__.py | 13 - .../apcnet/pytorch/mmcv/cnn/__init__.py | 21 - .../pytorch/mmcv/cnn/bricks/__init__.py | 35 - .../pytorch/mmcv/cnn/bricks/activation.py | 93 -- .../pytorch/mmcv/cnn/bricks/context_block.py | 125 -- .../apcnet/pytorch/mmcv/cnn/bricks/conv.py | 44 - .../cnn/bricks/conv2d_adaptive_padding.py | 62 - .../pytorch/mmcv/cnn/bricks/conv_module.py | 206 --- .../apcnet/pytorch/mmcv/cnn/bricks/conv_ws.py | 148 -- .../bricks/depthwise_separable_conv_module.py | 96 -- .../apcnet/pytorch/mmcv/cnn/bricks/drop.py | 65 - .../mmcv/cnn/bricks/generalized_attention.py | 412 ----- .../pytorch/mmcv/cnn/bricks/hsigmoid.py | 46 - .../apcnet/pytorch/mmcv/cnn/bricks/hswish.py | 38 - .../pytorch/mmcv/cnn/bricks/non_local.py | 306 ---- .../apcnet/pytorch/mmcv/cnn/bricks/norm.py | 144 -- .../apcnet/pytorch/mmcv/cnn/bricks/padding.py | 36 - .../apcnet/pytorch/mmcv/cnn/bricks/plugin.py | 89 -- .../pytorch/mmcv/cnn/bricks/registry.py | 16 - .../apcnet/pytorch/mmcv/cnn/bricks/scale.py | 21 - .../apcnet/pytorch/mmcv/cnn/bricks/swish.py | 25 - .../pytorch/mmcv/cnn/bricks/transformer.py | 944 ------------ .../pytorch/mmcv/cnn/bricks/upsample.py | 84 -- .../pytorch/mmcv/cnn/bricks/wrappers.py | 180 --- .../apcnet/pytorch/mmcv/cnn/builder.py | 30 - .../apcnet/pytorch/mmcv/cnn/utils/__init__.py | 19 - .../pytorch/mmcv/cnn/utils/flops_counter.py | 599 -------- .../pytorch/mmcv/cnn/utils/fuse_conv_bn.py | 59 - .../apcnet/pytorch/mmcv/cnn/utils/sync_bn.py | 60 - .../pytorch/mmcv/cnn/utils/weight_init.py | 685 --------- .../apcnet/pytorch/mmcv/device/__init__.py | 4 - .../pytorch/mmcv/device/ipu/__init__.py | 14 - .../pytorch/mmcv/device/ipu/dataloader.py | 157 -- .../device/ipu/hierarchical_data_manager.py | 243 --- .../pytorch/mmcv/device/ipu/hook_wrapper.py | 105 -- .../pytorch/mmcv/device/ipu/model_wrapper.py | 721 --------- .../apcnet/pytorch/mmcv/device/ipu/runner.py | 142 -- .../apcnet/pytorch/mmcv/device/ipu/utils.py | 244 --- .../pytorch/mmcv/device/mlu/__init__.py | 9 - .../pytorch/mmcv/device/mlu/_functions.py | 22 - .../pytorch/mmcv/device/mlu/data_parallel.py | 41 - .../pytorch/mmcv/device/mlu/distributed.py | 20 - .../pytorch/mmcv/device/mlu/scatter_gather.py | 59 - .../apcnet/pytorch/mmcv/engine/__init__.py | 8 - .../apcnet/pytorch/mmcv/engine/test.py | 202 --- .../apcnet/pytorch/mmcv/fileio/__init__.py | 11 - .../apcnet/pytorch/mmcv/fileio/file_client.py | 1163 -------------- .../pytorch/mmcv/fileio/handlers/__init__.py | 7 - .../pytorch/mmcv/fileio/handlers/base.py | 30 - .../mmcv/fileio/handlers/json_handler.py | 36 - .../mmcv/fileio/handlers/pickle_handler.py | 28 - .../mmcv/fileio/handlers/yaml_handler.py | 25 - .../apcnet/pytorch/mmcv/fileio/io.py | 151 -- .../apcnet/pytorch/mmcv/fileio/parse.py | 97 -- .../apcnet/pytorch/mmcv/image/__init__.py | 28 - .../apcnet/pytorch/mmcv/image/colorspace.py | 306 ---- .../apcnet/pytorch/mmcv/image/geometric.py | 741 --------- .../apcnet/pytorch/mmcv/image/io.py | 314 ---- .../apcnet/pytorch/mmcv/image/misc.py | 53 - .../apcnet/pytorch/mmcv/image/photometric.py | 428 ------ .../pytorch/mmcv/model_zoo/deprecated.json | 6 - .../apcnet/pytorch/mmcv/model_zoo/mmcls.json | 59 - .../pytorch/mmcv/model_zoo/open_mmlab.json | 50 - .../mmcv/model_zoo/torchvision_0.12.json | 57 - .../apcnet/pytorch/mmcv/ops/__init__.py | 12 - .../apcnet/pytorch/mmcv/ops/cc_attention.py | 84 -- .../apcnet/pytorch/mmcv/ops/csrc/README.md | 170 --- .../csrc/common/cuda/common_cuda_helper.hpp | 120 -- .../csrc/common/cuda/psamask_cuda_kernel.cuh | 141 -- .../cuda/sigmoid_focal_loss_cuda_kernel.cuh | 71 - .../cuda/softmax_focal_loss_cuda_kernel.cuh | 72 - .../csrc/common/cuda/sync_bn_cuda_kernel.cuh | 331 ---- .../ops/csrc/common/pytorch_cpp_helper.hpp | 27 - .../ops/csrc/common/pytorch_cuda_helper.hpp | 19 - .../csrc/common/pytorch_device_registry.hpp | 141 -- .../mmcv/ops/csrc/pytorch/cuda/cudabind.cpp | 210 --- .../ops/csrc/pytorch/cuda/focal_loss_cuda.cu | 111 -- .../ops/csrc/pytorch/cuda/psamask_cuda.cu | 60 - .../ops/csrc/pytorch/cuda/sync_bn_cuda.cu | 110 -- .../mmcv/ops/csrc/pytorch/focal_loss.cpp | 53 - .../pytorch/mmcv/ops/csrc/pytorch/info.cpp | 56 - .../pytorch/mmcv/ops/csrc/pytorch/psamask.cpp | 41 - .../pytorch/mmcv/ops/csrc/pytorch/pybind.cpp | 106 -- .../pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp | 69 - .../pytorch/mmcv/ops/deprecated_wrappers.py | 46 - .../apcnet/pytorch/mmcv/ops/focal_loss.py | 213 --- .../apcnet/pytorch/mmcv/ops/info.py | 36 - .../apcnet/pytorch/mmcv/ops/point_sample.py | 346 ----- .../apcnet/pytorch/mmcv/ops/psa_mask.py | 92 -- .../apcnet/pytorch/mmcv/ops/sync_bn.py | 279 ---- .../apcnet/pytorch/mmcv/parallel/__init__.py | 13 - .../pytorch/mmcv/parallel/_functions.py | 76 - .../apcnet/pytorch/mmcv/parallel/collate.py | 84 -- .../pytorch/mmcv/parallel/data_container.py | 89 -- .../pytorch/mmcv/parallel/data_parallel.py | 97 -- .../pytorch/mmcv/parallel/distributed.py | 138 -- .../mmcv/parallel/distributed_deprecated.py | 70 - .../apcnet/pytorch/mmcv/parallel/registry.py | 8 - .../pytorch/mmcv/parallel/scatter_gather.py | 59 - .../apcnet/pytorch/mmcv/parallel/utils.py | 20 - .../apcnet/pytorch/mmcv/runner/__init__.py | 73 - .../apcnet/pytorch/mmcv/runner/base_module.py | 208 --- .../apcnet/pytorch/mmcv/runner/base_runner.py | 544 ------- .../apcnet/pytorch/mmcv/runner/builder.py | 24 - .../apcnet/pytorch/mmcv/runner/checkpoint.py | 759 ---------- .../mmcv/runner/default_constructor.py | 45 - .../apcnet/pytorch/mmcv/runner/dist_utils.py | 204 --- .../pytorch/mmcv/runner/epoch_based_runner.py | 188 --- .../apcnet/pytorch/mmcv/runner/fp16_utils.py | 423 ------ .../pytorch/mmcv/runner/hooks/__init__.py | 48 - .../pytorch/mmcv/runner/hooks/checkpoint.py | 167 --- .../pytorch/mmcv/runner/hooks/closure.py | 11 - .../apcnet/pytorch/mmcv/runner/hooks/ema.py | 89 -- .../pytorch/mmcv/runner/hooks/evaluation.py | 511 ------- .../apcnet/pytorch/mmcv/runner/hooks/hook.py | 92 -- .../pytorch/mmcv/runner/hooks/iter_timer.py | 31 - .../mmcv/runner/hooks/logger/__init__.py | 18 - .../pytorch/mmcv/runner/hooks/logger/base.py | 167 --- .../mmcv/runner/hooks/logger/clearml.py | 62 - .../mmcv/runner/hooks/logger/dvclive.py | 68 - .../mmcv/runner/hooks/logger/mlflow.py | 80 - .../mmcv/runner/hooks/logger/neptune.py | 88 -- .../pytorch/mmcv/runner/hooks/logger/pavi.py | 132 -- .../mmcv/runner/hooks/logger/segmind.py | 49 - .../mmcv/runner/hooks/logger/tensorboard.py | 69 - .../pytorch/mmcv/runner/hooks/logger/text.py | 256 ---- .../pytorch/mmcv/runner/hooks/logger/wandb.py | 107 -- .../pytorch/mmcv/runner/hooks/lr_updater.py | 730 --------- .../pytorch/mmcv/runner/hooks/memory.py | 25 - .../mmcv/runner/hooks/momentum_updater.py | 566 ------- .../pytorch/mmcv/runner/hooks/optimizer.py | 556 ------- .../pytorch/mmcv/runner/hooks/profiler.py | 180 --- .../pytorch/mmcv/runner/hooks/sampler_seed.py | 20 - .../pytorch/mmcv/runner/hooks/sync_buffer.py | 22 - .../pytorch/mmcv/runner/iter_based_runner.py | 273 ---- .../apcnet/pytorch/mmcv/runner/log_buffer.py | 41 - .../pytorch/mmcv/runner/optimizer/__init__.py | 9 - .../pytorch/mmcv/runner/optimizer/builder.py | 44 - .../runner/optimizer/default_constructor.py | 250 --- .../apcnet/pytorch/mmcv/runner/priority.py | 60 - .../apcnet/pytorch/mmcv/runner/utils.py | 93 -- .../apcnet/pytorch/mmcv/utils/__init__.py | 78 - .../apcnet/pytorch/mmcv/utils/config.py | 719 --------- .../apcnet/pytorch/mmcv/utils/device_type.py | 24 - .../apcnet/pytorch/mmcv/utils/env.py | 120 -- .../apcnet/pytorch/mmcv/utils/ext_loader.py | 72 - .../apcnet/pytorch/mmcv/utils/hub.py | 131 -- .../apcnet/pytorch/mmcv/utils/logging.py | 111 -- .../apcnet/pytorch/mmcv/utils/misc.py | 377 ----- .../apcnet/pytorch/mmcv/utils/parrots_jit.py | 41 - .../pytorch/mmcv/utils/parrots_wrapper.py | 114 -- .../apcnet/pytorch/mmcv/utils/path.py | 101 -- .../apcnet/pytorch/mmcv/utils/progressbar.py | 208 --- .../apcnet/pytorch/mmcv/utils/registry.py | 337 ----- .../apcnet/pytorch/mmcv/utils/seed.py | 23 - .../apcnet/pytorch/mmcv/utils/testing.py | 141 -- .../apcnet/pytorch/mmcv/utils/timer.py | 118 -- .../apcnet/pytorch/mmcv/utils/trace.py | 24 - .../pytorch/mmcv/utils/version_utils.py | 90 -- .../apcnet/pytorch/mmcv/version.py | 35 - .../apcnet/pytorch/mmseg/__init__.py | 62 - .../apcnet/pytorch/mmseg/apis/__init__.py | 11 - .../apcnet/pytorch/mmseg/apis/inference.py | 136 -- .../apcnet/pytorch/mmseg/apis/test.py | 233 --- .../apcnet/pytorch/mmseg/apis/train.py | 196 --- .../apcnet/pytorch/mmseg/core/__init__.py | 11 - .../apcnet/pytorch/mmseg/core/builder.py | 33 - .../pytorch/mmseg/core/evaluation/__init__.py | 11 - .../mmseg/core/evaluation/class_names.py | 316 ---- .../mmseg/core/evaluation/eval_hooks.py | 128 -- .../pytorch/mmseg/core/evaluation/metrics.py | 395 ----- .../pytorch/mmseg/core/optimizers/__init__.py | 7 - .../layer_decay_optimizer_constructor.py | 208 --- .../apcnet/pytorch/mmseg/core/seg/__init__.py | 5 - .../apcnet/pytorch/mmseg/core/seg/builder.py | 9 - .../mmseg/core/seg/sampler/__init__.py | 5 - .../core/seg/sampler/base_pixel_sampler.py | 13 - .../core/seg/sampler/ohem_pixel_sampler.py | 85 -- .../pytorch/mmseg/core/utils/__init__.py | 5 - .../pytorch/mmseg/core/utils/dist_util.py | 46 - .../apcnet/pytorch/mmseg/core/utils/misc.py | 18 - .../apcnet/pytorch/mmseg/datasets/__init__.py | 10 - .../apcnet/pytorch/mmseg/datasets/ade.py | 167 --- .../apcnet/pytorch/mmseg/datasets/builder.py | 191 --- .../pytorch/mmseg/datasets/cityscapes.py | 214 --- .../pytorch/mmseg/datasets/coco_stuff.py | 94 -- .../apcnet/pytorch/mmseg/datasets/custom.py | 487 ------ .../mmseg/datasets/dataset_wrappers.py | 277 ---- .../pytorch/mmseg/datasets/pascal_context.py | 103 -- .../mmseg/datasets/pipelines/__init__.py | 19 - .../mmseg/datasets/pipelines/compose.py | 52 - .../mmseg/datasets/pipelines/formating.py | 9 - .../mmseg/datasets/pipelines/formatting.py | 289 ---- .../mmseg/datasets/pipelines/loading.py | 158 -- .../mmseg/datasets/pipelines/test_time_aug.py | 134 -- .../mmseg/datasets/pipelines/transforms.py | 1335 ----------------- .../mmseg/datasets/samplers/__init__.py | 4 - .../datasets/samplers/distributed_sampler.py | 71 - .../apcnet/pytorch/mmseg/datasets/voc.py | 39 - .../apcnet/pytorch/mmseg/models/__init__.py | 13 - .../mmseg/models/backbones/__init__.py | 4 - .../pytorch/mmseg/models/backbones/resnest.py | 318 ---- .../pytorch/mmseg/models/backbones/resnet.py | 714 --------- .../pytorch/mmseg/models/backbones/resnext.py | 150 -- .../mmseg/models/backbones/timm_backbone.py | 63 - .../apcnet/pytorch/mmseg/models/builder.py | 49 - .../mmseg/models/decode_heads/__init__.py | 6 - .../mmseg/models/decode_heads/apc_head.py | 159 -- .../mmseg/models/decode_heads/aspp_head.py | 122 -- .../mmseg/models/decode_heads/cc_head.py | 43 - .../mmseg/models/decode_heads/decode_head.py | 266 ---- .../mmseg/models/decode_heads/fcn_head.py | 96 -- .../pytorch/mmseg/models/losses/__init__.py | 15 - .../pytorch/mmseg/models/losses/accuracy.py | 92 -- .../mmseg/models/losses/cross_entropy_loss.py | 296 ---- .../pytorch/mmseg/models/losses/dice_loss.py | 137 -- .../pytorch/mmseg/models/losses/focal_loss.py | 327 ---- .../mmseg/models/losses/lovasz_loss.py | 323 ---- .../pytorch/mmseg/models/losses/utils.py | 126 -- .../pytorch/mmseg/models/necks/__init__.py | 11 - .../mmseg/models/necks/featurepyramid.py | 67 - .../apcnet/pytorch/mmseg/models/necks/fpn.py | 213 --- .../pytorch/mmseg/models/necks/ic_neck.py | 148 -- .../apcnet/pytorch/mmseg/models/necks/jpu.py | 131 -- .../pytorch/mmseg/models/necks/mla_neck.py | 118 -- .../mmseg/models/necks/multilevel_neck.py | 78 - .../mmseg/models/segmentors/__init__.py | 6 - .../pytorch/mmseg/models/segmentors/base.py | 291 ---- .../segmentors/cascade_encoder_decoder.py | 88 -- .../models/segmentors/encoder_decoder.py | 284 ---- .../pytorch/mmseg/models/utils/__init__.py | 16 - .../pytorch/mmseg/models/utils/embed.py | 330 ---- .../mmseg/models/utils/inverted_residual.py | 213 --- .../mmseg/models/utils/make_divisible.py | 28 - .../pytorch/mmseg/models/utils/res_layer.py | 96 -- .../pytorch/mmseg/models/utils/se_layer.py | 58 - .../models/utils/self_attention_block.py | 160 -- .../mmseg/models/utils/shape_convert.py | 107 -- .../mmseg/models/utils/up_conv_block.py | 102 -- .../apcnet/pytorch/mmseg/ops/__init__.py | 5 - .../apcnet/pytorch/mmseg/ops/encoding.py | 75 - .../apcnet/pytorch/mmseg/ops/wrappers.py | 51 - .../apcnet/pytorch/mmseg/utils/__init__.py | 10 - .../apcnet/pytorch/mmseg/utils/collect_env.py | 18 - .../apcnet/pytorch/mmseg/utils/logger.py | 28 - .../apcnet/pytorch/mmseg/utils/misc.py | 41 - .../apcnet/pytorch/mmseg/utils/set_env.py | 55 - .../apcnet/pytorch/mmseg/version.py | 18 - .../apcnet/pytorch/requirements.txt | 3 - .../apcnet/pytorch/requirements/apcnet.txt | 4 - .../pytorch/requirements/mmcv/build.txt | 1 - .../apcnet/pytorch/requirements/mmcv/docs.txt | 8 - .../pytorch/requirements/mmcv/optional.txt | 1 - .../requirements/mmcv/requirements.txt | 4 - .../pytorch/requirements/mmcv/runtime.txt | 7 - .../apcnet/pytorch/requirements/mmcv/test.txt | 9 - .../apcnet/pytorch/requirements/mminstall.txt | 2 - .../apcnet/pytorch/requirements/optional.txt | 1 - .../apcnet/pytorch/requirements/runtime.txt | 5 - .../apcnet/pytorch/setup.py | 258 ---- .../apcnet/pytorch/tools/analyze_logs.py | 128 -- .../apcnet/pytorch/tools/benchmark.py | 120 -- .../apcnet/pytorch/tools/confusion_matrix.py | 184 --- .../tools/convert_datasets/cityscapes.py | 56 - .../tools/convert_datasets/coco_stuff10k.py | 307 ---- .../tools/convert_datasets/coco_stuff164k.py | 264 ---- .../tools/convert_datasets/pascal_context.py | 87 -- .../pytorch/tools/convert_datasets/voc_aug.py | 92 -- .../apcnet/pytorch/tools/get_flops.py | 60 - .../apcnet/pytorch/tools/print_config.py | 69 - .../apcnet/pytorch/tools/slurm_test.sh | 24 - .../apcnet/pytorch/tools/slurm_train.sh | 23 - .../apcnet/pytorch/tools/test.py | 319 ---- .../apcnet/pytorch/train.py | 243 --- .../apcnet/pytorch/train.sh | 6 - .../apcnet/pytorch/train_dist.sh | 17 - 322 files changed, 16 insertions(+), 39868 deletions(-) delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/.gitignore delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/CITATION.cff delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/LICENSE delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/ade20k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/ade20k_640x640.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_768x768.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_832x832.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/coco-stuff10k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/coco-stuff164k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_context_59.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_voc12.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/default_runtime.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/models/apcnet_r50-d8.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_160k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_1k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_20k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_320k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_40k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_80k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/README.md delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet.yml delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x1024_40k_cityscapes.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x512_160k_ade20k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x512_80k_ade20k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_769x769_40k_cityscapes.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_769x769_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x1024_1k_cityscapes.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x1024_40k_cityscapes.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_160k_ade20k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_1k_voc.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_20k_voc.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_80k_ade20k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_769x769_40k_cityscapes.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_769x769_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/docker/Dockerfile delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/docker/serve/Dockerfile delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/docker/serve/config.properties delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/docker/serve/entrypoint.sh delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/activation.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/context_block.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv_module.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv_ws.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/drop.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/generalized_attention.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/hsigmoid.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/hswish.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/non_local.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/norm.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/padding.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/plugin.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/registry.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/scale.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/swish.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/transformer.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/upsample.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/wrappers.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/builder.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/flops_counter.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/sync_bn.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/weight_init.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/device/__init__.py delete mode 100755 cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/__init__.py delete mode 100755 cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/dataloader.py delete mode 100755 cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py delete mode 100755 cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/hook_wrapper.py delete mode 100755 cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/model_wrapper.py delete mode 100755 cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/runner.py delete mode 100755 cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/utils.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/_functions.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/data_parallel.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/distributed.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/scatter_gather.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/engine/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/engine/test.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/file_client.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/base.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/json_handler.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/pickle_handler.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/yaml_handler.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/io.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/parse.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/image/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/image/colorspace.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/image/geometric.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/image/io.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/image/misc.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/image/photometric.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/deprecated.json delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/mmcls.json delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/open_mmlab.json delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/torchvision_0.12.json delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/cc_attention.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/README.md delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/deprecated_wrappers.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/focal_loss.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/info.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/point_sample.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/psa_mask.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/sync_bn.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/_functions.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/collate.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/data_container.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/data_parallel.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/distributed.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/distributed_deprecated.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/registry.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/scatter_gather.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/utils.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/base_module.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/base_runner.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/builder.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/checkpoint.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/default_constructor.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/dist_utils.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/epoch_based_runner.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/fp16_utils.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/checkpoint.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/closure.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/ema.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/evaluation.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/hook.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/iter_timer.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/base.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/clearml.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/dvclive.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/mlflow.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/neptune.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/pavi.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/segmind.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/text.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/wandb.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/lr_updater.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/memory.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/momentum_updater.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/optimizer.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/profiler.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/sampler_seed.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/sync_buffer.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/iter_based_runner.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/log_buffer.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/optimizer/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/optimizer/builder.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/optimizer/default_constructor.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/priority.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/utils.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/config.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/device_type.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/env.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/ext_loader.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/hub.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/logging.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/misc.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/parrots_jit.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/parrots_wrapper.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/path.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/progressbar.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/registry.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/seed.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/testing.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/timer.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/trace.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/version_utils.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmcv/version.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/inference.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/test.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/train.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/builder.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/class_names.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/eval_hooks.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/metrics.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/optimizers/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/builder.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/sampler/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/utils/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/utils/dist_util.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/core/utils/misc.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/ade.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/builder.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/coco_stuff.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/custom.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/dataset_wrappers.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/compose.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/formating.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/formatting.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/loading.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/transforms.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/samplers/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/samplers/distributed_sampler.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/voc.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/resnest.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/resnet.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/resnext.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/timm_backbone.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/builder.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/apc_head.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/aspp_head.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/cc_head.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/decode_head.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/fcn_head.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/accuracy.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/cross_entropy_loss.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/dice_loss.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/focal_loss.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/lovasz_loss.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/utils.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/featurepyramid.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/fpn.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/ic_neck.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/jpu.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/mla_neck.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/multilevel_neck.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/base.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/encoder_decoder.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/embed.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/inverted_residual.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/make_divisible.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/res_layer.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/se_layer.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/self_attention_block.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/shape_convert.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/up_conv_block.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/ops/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/ops/encoding.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/ops/wrappers.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/__init__.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/collect_env.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/logger.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/misc.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/set_env.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/mmseg/version.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/requirements.txt delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/requirements/apcnet.txt delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/build.txt delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/docs.txt delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/optional.txt delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/requirements.txt delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/runtime.txt delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/test.txt delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/requirements/mminstall.txt delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/requirements/optional.txt delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/requirements/runtime.txt delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/setup.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/tools/analyze_logs.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/tools/benchmark.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/tools/confusion_matrix.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/coco_stuff10k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/coco_stuff164k.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/voc_aug.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/tools/get_flops.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/tools/print_config.py delete mode 100755 cv/semantic_segmentation/apcnet/pytorch/tools/slurm_test.sh delete mode 100755 cv/semantic_segmentation/apcnet/pytorch/tools/slurm_train.sh delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/tools/test.py delete mode 100644 cv/semantic_segmentation/apcnet/pytorch/train.py delete mode 100755 cv/semantic_segmentation/apcnet/pytorch/train.sh delete mode 100755 cv/semantic_segmentation/apcnet/pytorch/train_dist.sh diff --git a/cv/semantic_segmentation/apcnet/pytorch/.gitignore b/cv/semantic_segmentation/apcnet/pytorch/.gitignore deleted file mode 100644 index 787d13ec6..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/.gitignore +++ /dev/null @@ -1,120 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/en/_build/ -docs/zh_cn/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ -.DS_Store - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -data -.vscode -.idea - -# custom -*.pkl -*.pkl.json -*.log.json -work_dirs/ -mmseg/.mim - -# Pytorch -*.pth diff --git a/cv/semantic_segmentation/apcnet/pytorch/CITATION.cff b/cv/semantic_segmentation/apcnet/pytorch/CITATION.cff deleted file mode 100644 index cfd7cab05..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/CITATION.cff +++ /dev/null @@ -1,8 +0,0 @@ -cff-version: 1.2.0 -message: "If you use this software, please cite it as below." -authors: - - name: "MMSegmentation Contributors" -title: "OpenMMLab Semantic Segmentation Toolbox and Benchmark" -date-released: 2020-07-10 -url: "https://github.com/open-mmlab/mmsegmentation" -license: Apache-2.0 diff --git a/cv/semantic_segmentation/apcnet/pytorch/LICENSE b/cv/semantic_segmentation/apcnet/pytorch/LICENSE deleted file mode 100644 index 38e625bf5..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Copyright 2020 The MMSegmentation Authors. All rights reserved. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020 The MMSegmentation Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cv/semantic_segmentation/apcnet/pytorch/README.md b/cv/semantic_segmentation/apcnet/pytorch/README.md index cfd2bafe1..f8c71a7ed 100644 --- a/cv/semantic_segmentation/apcnet/pytorch/README.md +++ b/cv/semantic_segmentation/apcnet/pytorch/README.md @@ -12,13 +12,18 @@ And then calculates a context vector with these affinities. ### Install packages ```shell -pip3 install -r requirements.txt -``` - -### Build Extension - -```shell -python3 setup.py build && cp build/lib.linux*/mmcv/_ext.cpython* mmcv +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +# install mmsegmentation +git clone -b v1.2.2 https://github.com/open-mmlab/mmsegmentation.git --depth=1 +cd mmsegmentation/ +pip install -v -e . + +pip install ftfy ``` ## Step 2: Prepare Datasets @@ -55,112 +60,15 @@ ln -s /path/to/cityscapes data/cityscapes ## Step 3: Training -**The available configs are as follows:** - -```shell -# VOC2012 -apcnet_r50-d8_512x512_1k_voc.py -apcnet_r50-d8_512x512_20k_voc - -# ADE -apcnet_r50-d8_512x512_80k_ade20k -apcnet_r50-d8_512x512_160k_ade20k -apcnet_r101-d8_512x512_80k_ade20k -apcnet_r101-d8_512x512_160k_ade20k - -# CityScapes -apcnet_r50-d8_512x1024_1k_cityscapes -apcnet_r50-d8_512x1024_40k_cityscapes -apcnet_r50-d8_512x1024_80k_cityscapes -apcnet_r50-d8_769x769_40k_cityscapes -apcnet_r50-d8_769x769_80k_cityscapes -apcnet_r101-d8_512x1024_40k_cityscapes -apcnet_r101-d8_512x1024_80k_cityscapes -apcnet_r101-d8_769x769_40k_cityscapes -apcnet_r101-d8_769x769_80k_cityscapes -``` - - ### Training on single card ```shell -bash train.sh [training args] # config file can be found in the configs directory +python3 tools/train.py configs/apcnet/apcnet_r50-d8_4xb2-80k_cityscapes-512x1024.py ``` ### Training on mutil-cards ```shell -bash train_dist.sh [training args] # config file can be found in the configs directory -``` - -### Example - -```shell -bash train_dist.sh configs/apcnet/apcnet_r50-d8_512x1024_40k_cityscapes.py 8 -``` - -### Training arguments - -```python -# the dir to save logs and models -work-dir: str = None - -# the checkpoint file to load weights from -load-from: str = None - -# the checkpoint file to resume from -resume-from: str = None - -# whether not to evaluate the checkpoint during training -no-validate: bool = False - -# (Deprecated, please use --gpu-id) number of gpus to -# use (only applicable to non-distributed training) -gpus: int = None - -# (Deprecated, please use --gpu-id) ids of gpus to use -# (only applicable to non-distributed training) -gpu-ids: int = None - -# id of gpu to use (only applicable to non-distributed training) -gpu-id: int = 0 - -# random seed -seed: int = None - -# Whether or not set different seeds for different ranks -diff_seed: bool = False - -# whether to set deterministic options for CUDNN backend. -deterministic: bool = False - -# --options is deprecated in favor of --cfg_options' and it -# will not be supported in version v0.22.0. Override some -# settings in the used config, the key-value pair in xxx=yyy -# format will be merged into config file. If the value to be -# overwritten is a list, it should be like key="[a,b]" or key=a,b -# It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" -# Note that the quotation marks are necessary and that no white space -# is allowed. -options: str = None - -# override some settings in the used config, the key-value pair -# in xxx=yyy format will be merged into config file. If the value -# to be overwritten is a list, it should be like key="[a,b]" or key=a,b -# It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" -# Note that the quotation marks are necessary and that no white -# space is allowed. -cfg-options: str = None - -# job launcher -launcher: str = "none" - -# local rank -local_rank: int = 0 - -# distributed backend -dist_backend: str = None - -# resume from the latest checkpoint automatically. -auto-resume: bool = False +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/apcnet/apcnet_r50-d8_4xb2-80k_cityscapes-512x1024.py 8 ``` ## Results @@ -173,17 +81,5 @@ auto-resume: bool = False | ------ | -------- | --------- | ------: | -------- |--------------:| | APCNet | R-50-D8 | 512x1024 | 40000 | 7.7 | 77.53 | - -### VOC2012 - -#### Accuracy - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | mIoU (BI x 4) | -| ------ | -------- |-----------|--------:|----------|--------------:| -| APCNet | R-50-D8 | 512x512 | 1000 | 20.1 | 66.63 | -| APCNet | R-50-D8 | 512x512 | 20000 | 20.1 | 69.68 | - ## Reference --Ref: https://mmsegmentation.readthedocs.io/en/latest/dataset_prepare.html#cityscapes --Ref: [Author Results](configs/apcnet/README.md) --Ref: https://github.com/open-mmlab/mmsegmentation +[mmsegmentation](https://github.com/open-mmlab/mmsegmentation) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/ade20k.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/ade20k.py deleted file mode 100644 index efc8b4bb2..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/ade20k.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'ADE20KDataset' -data_root = 'data/ade/ADEChallengeData2016' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/ade20k_640x640.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/ade20k_640x640.py deleted file mode 100644 index 14a4bb092..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/ade20k_640x640.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'ADE20KDataset' -data_root = 'data/ade/ADEChallengeData2016' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (640, 640) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2560, 640), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2560, 640), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes.py deleted file mode 100644 index f21867c63..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'CityscapesDataset' -data_root = 'data/cityscapes/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 1024) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/train', - ann_dir='gtFine/train', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/val', - ann_dir='gtFine/val', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/val', - ann_dir='gtFine/val', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py deleted file mode 100644 index f98d92972..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (1024, 1024) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_768x768.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_768x768.py deleted file mode 100644 index fde9d7c7d..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_768x768.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (768, 768) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2049, 1025), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py deleted file mode 100644 index 336c7b254..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (769, 769) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2049, 1025), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_832x832.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_832x832.py deleted file mode 100644 index b9325cc00..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/cityscapes_832x832.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (832, 832) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/coco-stuff10k.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/coco-stuff10k.py deleted file mode 100644 index ec0496928..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/coco-stuff10k.py +++ /dev/null @@ -1,57 +0,0 @@ -# dataset settings -dataset_type = 'COCOStuffDataset' -data_root = 'data/coco_stuff10k' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/train2014', - ann_dir='annotations/train2014', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/test2014', - ann_dir='annotations/test2014', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/test2014', - ann_dir='annotations/test2014', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/coco-stuff164k.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/coco-stuff164k.py deleted file mode 100644 index a6a38f2ac..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/coco-stuff164k.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'COCOStuffDataset' -data_root = 'data/coco_stuff164k' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/train2017', - ann_dir='annotations/train2017', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/val2017', - ann_dir='annotations/val2017', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/val2017', - ann_dir='annotations/val2017', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_context.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_context.py deleted file mode 100644 index ff65bad1b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_context.py +++ /dev/null @@ -1,60 +0,0 @@ -# dataset settings -dataset_type = 'PascalContextDataset' -data_root = 'data/VOCdevkit/VOC2010/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) - -img_scale = (520, 520) -crop_size = (480, 480) - -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_context_59.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_context_59.py deleted file mode 100644 index 37585abab..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_context_59.py +++ /dev/null @@ -1,60 +0,0 @@ -# dataset settings -dataset_type = 'PascalContextDataset59' -data_root = 'data/VOCdevkit/VOC2010/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) - -img_scale = (520, 520) -crop_size = (480, 480) - -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_voc12.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_voc12.py deleted file mode 100644 index e5ff704ae..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_voc12.py +++ /dev/null @@ -1,58 +0,0 @@ -# dataset settings - -dataset_type = 'PascalVOCDataset' -data_root = 'data/VOCdevkit/VOC2012' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py deleted file mode 100644 index 3f23b6717..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = './pascal_voc12.py' -# dataset settings -data = dict( - train=dict( - ann_dir=['SegmentationClass', 'SegmentationClassAug'], - split=[ - 'ImageSets/Segmentation/train.txt', - 'ImageSets/Segmentation/aug.txt' - ])) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/default_runtime.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/default_runtime.py deleted file mode 100644 index b564cc4e7..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/default_runtime.py +++ /dev/null @@ -1,14 +0,0 @@ -# yapf:disable -log_config = dict( - interval=50, - hooks=[ - dict(type='TextLoggerHook', by_epoch=False), - # dict(type='TensorboardLoggerHook') - ]) -# yapf:enable -dist_params = dict(backend='nccl') -log_level = 'INFO' -load_from = None -resume_from = None -workflow = [('train', 1)] -cudnn_benchmark = True diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/models/apcnet_r50-d8.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/models/apcnet_r50-d8.py deleted file mode 100644 index c8f5316cb..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/models/apcnet_r50-d8.py +++ /dev/null @@ -1,44 +0,0 @@ -# model settings -norm_cfg = dict(type='SyncBN', requires_grad=True) -model = dict( - type='EncoderDecoder', - pretrained='open-mmlab://resnet50_v1c', - backbone=dict( - type='ResNetV1c', - depth=50, - num_stages=4, - out_indices=(0, 1, 2, 3), - dilations=(1, 1, 2, 4), - strides=(1, 2, 1, 1), - norm_cfg=norm_cfg, - norm_eval=False, - style='pytorch', - contract_dilation=True), - decode_head=dict( - type='APCHead', - in_channels=2048, - in_index=3, - channels=512, - pool_scales=(1, 2, 3, 6), - dropout_ratio=0.1, - num_classes=19, - norm_cfg=dict(type='SyncBN', requires_grad=True), - align_corners=False, - loss_decode=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), - auxiliary_head=dict( - type='FCNHead', - in_channels=1024, - in_index=2, - channels=256, - num_convs=1, - concat_input=False, - dropout_ratio=0.1, - num_classes=19, - norm_cfg=norm_cfg, - align_corners=False, - loss_decode=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), - # model training and testing settings - train_cfg=dict(), - test_cfg=dict(mode='whole')) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_160k.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_160k.py deleted file mode 100644 index 39630f215..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_160k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=160000) -checkpoint_config = dict(by_epoch=False, interval=16000) -evaluation = dict(interval=16000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_1k.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_1k.py deleted file mode 100644 index 04cf41030..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_1k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=1000) -checkpoint_config = dict(by_epoch=False, interval=1000) -evaluation = dict(interval=1000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_20k.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_20k.py deleted file mode 100644 index 73c702197..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_20k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=20000) -checkpoint_config = dict(by_epoch=False, interval=2000) -evaluation = dict(interval=2000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_320k.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_320k.py deleted file mode 100644 index a0b230626..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_320k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=320000) -checkpoint_config = dict(by_epoch=False, interval=32000) -evaluation = dict(interval=32000, metric='mIoU') diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_40k.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_40k.py deleted file mode 100644 index d2c502325..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_40k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=40000) -checkpoint_config = dict(by_epoch=False, interval=4000) -evaluation = dict(interval=4000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_80k.py b/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_80k.py deleted file mode 100644 index 8365a878e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/_base_/schedules/schedule_80k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=80000) -checkpoint_config = dict(by_epoch=False, interval=8000) -evaluation = dict(interval=8000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/README.md b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/README.md deleted file mode 100644 index 5e1fd6b42..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# APCNet - -[Adaptive Pyramid Context Network for Semantic Segmentation](https://openaccess.thecvf.com/content_CVPR_2019/html/He_Adaptive_Pyramid_Context_Network_for_Semantic_Segmentation_CVPR_2019_paper.html) - -## Introduction - - - -Official Repo - -Code Snippet - -## Abstract - - - -Recent studies witnessed that context features can significantly improve the performance of deep semantic segmentation networks. Current context based segmentation methods differ with each other in how to construct context features and perform differently in practice. This paper firstly introduces three desirable properties of context features in segmentation task. Specially, we find that Global-guided Local Affinity (GLA) can play a vital role in constructing effective context features, while this property has been largely ignored in previous works. Based on this analysis, this paper proposes Adaptive Pyramid Context Network (APCNet)for semantic segmentation. APCNet adaptively constructs multi-scale contextual representations with multiple welldesigned Adaptive Context Modules (ACMs). Specifically, each ACM leverages a global image representation as a guidance to estimate the local affinity coefficients for each sub-region, and then calculates a context vector with these affinities. We empirically evaluate our APCNet on three semantic segmentation and scene parsing datasets, including PASCAL VOC 2012, Pascal-Context, and ADE20K dataset. Experimental results show that APCNet achieves state-ofthe-art performance on all three benchmarks, and obtains a new record 84.2% on PASCAL VOC 2012 test set without MS COCO pre-trained and any post-processing. - - -
- -
- -## Citation - -```bibtex -@InProceedings{He_2019_CVPR, -author = {He, Junjun and Deng, Zhongying and Zhou, Lei and Wang, Yali and Qiao, Yu}, -title = {Adaptive Pyramid Context Network for Semantic Segmentation}, -booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)}, -month = {June}, -year = {2019} -} -``` - -## Results and models - -### Cityscapes - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| APCNet | R-50-D8 | 512x1024 | 40000 | 7.7 | 3.57 | 78.02 | 79.26 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/apcnet/apcnet_r50-d8_512x1024_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_512x1024_40k_cityscapes/apcnet_r50-d8_512x1024_40k_cityscapes_20201214_115717-5e88fa33.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_512x1024_40k_cityscapes/apcnet_r50-d8_512x1024_40k_cityscapes-20201214_115717.log.json) | -| APCNet | R-101-D8 | 512x1024 | 40000 | 11.2 | 2.15 | 79.08 | 80.34 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/apcnet/apcnet_r101-d8_512x1024_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_512x1024_40k_cityscapes/apcnet_r101-d8_512x1024_40k_cityscapes_20201214_115716-abc9d111.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_512x1024_40k_cityscapes/apcnet_r101-d8_512x1024_40k_cityscapes-20201214_115716.log.json) | -| APCNet | R-50-D8 | 769x769 | 40000 | 8.7 | 1.52 | 77.89 | 79.75 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/apcnet/apcnet_r50-d8_769x769_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_769x769_40k_cityscapes/apcnet_r50-d8_769x769_40k_cityscapes_20201214_115717-2a2628d7.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_769x769_40k_cityscapes/apcnet_r50-d8_769x769_40k_cityscapes-20201214_115717.log.json) | -| APCNet | R-101-D8 | 769x769 | 40000 | 12.7 | 1.03 | 77.96 | 79.24 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/apcnet/apcnet_r101-d8_769x769_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_769x769_40k_cityscapes/apcnet_r101-d8_769x769_40k_cityscapes_20201214_115718-b650de90.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_769x769_40k_cityscapes/apcnet_r101-d8_769x769_40k_cityscapes-20201214_115718.log.json) | -| APCNet | R-50-D8 | 512x1024 | 80000 | - | - | 78.96 | 79.94 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/apcnet/apcnet_r50-d8_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_512x1024_80k_cityscapes/apcnet_r50-d8_512x1024_80k_cityscapes_20201214_115716-987f51e3.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_512x1024_80k_cityscapes/apcnet_r50-d8_512x1024_80k_cityscapes-20201214_115716.log.json) | -| APCNet | R-101-D8 | 512x1024 | 80000 | - | - | 79.64 | 80.61 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/apcnet/apcnet_r101-d8_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_512x1024_80k_cityscapes/apcnet_r101-d8_512x1024_80k_cityscapes_20201214_115705-b1ff208a.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_512x1024_80k_cityscapes/apcnet_r101-d8_512x1024_80k_cityscapes-20201214_115705.log.json) | -| APCNet | R-50-D8 | 769x769 | 80000 | - | - | 78.79 | 80.35 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/apcnet/apcnet_r50-d8_769x769_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_769x769_80k_cityscapes/apcnet_r50-d8_769x769_80k_cityscapes_20201214_115718-7ea9fa12.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_769x769_80k_cityscapes/apcnet_r50-d8_769x769_80k_cityscapes-20201214_115718.log.json) | -| APCNet | R-101-D8 | 769x769 | 80000 | - | - | 78.45 | 79.91 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/apcnet/apcnet_r101-d8_769x769_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_769x769_80k_cityscapes/apcnet_r101-d8_769x769_80k_cityscapes_20201214_115716-a7fbc2ab.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_769x769_80k_cityscapes/apcnet_r101-d8_769x769_80k_cityscapes-20201214_115716.log.json) | - -### ADE20K - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| APCNet | R-50-D8 | 512x512 | 80000 | 10.1 | 19.61 | 42.20 | 43.30 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/apcnet/apcnet_r50-d8_512x512_80k_ade20k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_512x512_80k_ade20k/apcnet_r50-d8_512x512_80k_ade20k_20201214_115705-a8626293.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_512x512_80k_ade20k/apcnet_r50-d8_512x512_80k_ade20k-20201214_115705.log.json) | -| APCNet | R-101-D8 | 512x512 | 80000 | 13.6 | 13.10 | 45.54 | 46.65 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/apcnet/apcnet_r101-d8_512x512_80k_ade20k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_512x512_80k_ade20k/apcnet_r101-d8_512x512_80k_ade20k_20201214_115704-c656c3fb.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_512x512_80k_ade20k/apcnet_r101-d8_512x512_80k_ade20k-20201214_115704.log.json) | -| APCNet | R-50-D8 | 512x512 | 160000 | - | - | 43.40 | 43.94 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/apcnet/apcnet_r50-d8_512x512_160k_ade20k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_512x512_160k_ade20k/apcnet_r50-d8_512x512_160k_ade20k_20201214_115706-25fb92c2.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_512x512_160k_ade20k/apcnet_r50-d8_512x512_160k_ade20k-20201214_115706.log.json) | -| APCNet | R-101-D8 | 512x512 | 160000 | - | - | 45.41 | 46.63 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/apcnet/apcnet_r101-d8_512x512_160k_ade20k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_512x512_160k_ade20k/apcnet_r101-d8_512x512_160k_ade20k_20201214_115705-73f9a8d7.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_512x512_160k_ade20k/apcnet_r101-d8_512x512_160k_ade20k-20201214_115705.log.json) | diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet.yml b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet.yml deleted file mode 100644 index 7a453a360..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet.yml +++ /dev/null @@ -1,232 +0,0 @@ -Collections: -- Name: APCNet - Metadata: - Training Data: - - Cityscapes - - ADE20K - Paper: - URL: https://openaccess.thecvf.com/content_CVPR_2019/html/He_Adaptive_Pyramid_Context_Network_for_Semantic_Segmentation_CVPR_2019_paper.html - Title: Adaptive Pyramid Context Network for Semantic Segmentation - README: configs/apcnet/README.md - Code: - URL: https://github.com/open-mmlab/mmsegmentation/blob/v0.17.0/mmseg/models/decode_heads/apc_head.py#L111 - Version: v0.17.0 - Converted From: - Code: https://github.com/Junjun2016/APCNet -Models: -- Name: apcnet_r50-d8_512x1024_40k_cityscapes - In Collection: APCNet - Metadata: - backbone: R-50-D8 - crop size: (512,1024) - lr schd: 40000 - inference time (ms/im): - - value: 280.11 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 7.7 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 78.02 - mIoU(ms+flip): 79.26 - Config: configs/apcnet/apcnet_r50-d8_512x1024_40k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_512x1024_40k_cityscapes/apcnet_r50-d8_512x1024_40k_cityscapes_20201214_115717-5e88fa33.pth -- Name: apcnet_r101-d8_512x1024_40k_cityscapes - In Collection: APCNet - Metadata: - backbone: R-101-D8 - crop size: (512,1024) - lr schd: 40000 - inference time (ms/im): - - value: 465.12 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 11.2 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 79.08 - mIoU(ms+flip): 80.34 - Config: configs/apcnet/apcnet_r101-d8_512x1024_40k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_512x1024_40k_cityscapes/apcnet_r101-d8_512x1024_40k_cityscapes_20201214_115716-abc9d111.pth -- Name: apcnet_r50-d8_769x769_40k_cityscapes - In Collection: APCNet - Metadata: - backbone: R-50-D8 - crop size: (769,769) - lr schd: 40000 - inference time (ms/im): - - value: 657.89 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (769,769) - Training Memory (GB): 8.7 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 77.89 - mIoU(ms+flip): 79.75 - Config: configs/apcnet/apcnet_r50-d8_769x769_40k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_769x769_40k_cityscapes/apcnet_r50-d8_769x769_40k_cityscapes_20201214_115717-2a2628d7.pth -- Name: apcnet_r101-d8_769x769_40k_cityscapes - In Collection: APCNet - Metadata: - backbone: R-101-D8 - crop size: (769,769) - lr schd: 40000 - inference time (ms/im): - - value: 970.87 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (769,769) - Training Memory (GB): 12.7 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 77.96 - mIoU(ms+flip): 79.24 - Config: configs/apcnet/apcnet_r101-d8_769x769_40k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_769x769_40k_cityscapes/apcnet_r101-d8_769x769_40k_cityscapes_20201214_115718-b650de90.pth -- Name: apcnet_r50-d8_512x1024_80k_cityscapes - In Collection: APCNet - Metadata: - backbone: R-50-D8 - crop size: (512,1024) - lr schd: 80000 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 78.96 - mIoU(ms+flip): 79.94 - Config: configs/apcnet/apcnet_r50-d8_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_512x1024_80k_cityscapes/apcnet_r50-d8_512x1024_80k_cityscapes_20201214_115716-987f51e3.pth -- Name: apcnet_r101-d8_512x1024_80k_cityscapes - In Collection: APCNet - Metadata: - backbone: R-101-D8 - crop size: (512,1024) - lr schd: 80000 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 79.64 - mIoU(ms+flip): 80.61 - Config: configs/apcnet/apcnet_r101-d8_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_512x1024_80k_cityscapes/apcnet_r101-d8_512x1024_80k_cityscapes_20201214_115705-b1ff208a.pth -- Name: apcnet_r50-d8_769x769_80k_cityscapes - In Collection: APCNet - Metadata: - backbone: R-50-D8 - crop size: (769,769) - lr schd: 80000 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 78.79 - mIoU(ms+flip): 80.35 - Config: configs/apcnet/apcnet_r50-d8_769x769_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_769x769_80k_cityscapes/apcnet_r50-d8_769x769_80k_cityscapes_20201214_115718-7ea9fa12.pth -- Name: apcnet_r101-d8_769x769_80k_cityscapes - In Collection: APCNet - Metadata: - backbone: R-101-D8 - crop size: (769,769) - lr schd: 80000 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 78.45 - mIoU(ms+flip): 79.91 - Config: configs/apcnet/apcnet_r101-d8_769x769_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_769x769_80k_cityscapes/apcnet_r101-d8_769x769_80k_cityscapes_20201214_115716-a7fbc2ab.pth -- Name: apcnet_r50-d8_512x512_80k_ade20k - In Collection: APCNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 50.99 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 10.1 - Results: - - Task: Semantic Segmentation - Dataset: ADE20K - Metrics: - mIoU: 42.2 - mIoU(ms+flip): 43.3 - Config: configs/apcnet/apcnet_r50-d8_512x512_80k_ade20k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_512x512_80k_ade20k/apcnet_r50-d8_512x512_80k_ade20k_20201214_115705-a8626293.pth -- Name: apcnet_r101-d8_512x512_80k_ade20k - In Collection: APCNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 76.34 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 13.6 - Results: - - Task: Semantic Segmentation - Dataset: ADE20K - Metrics: - mIoU: 45.54 - mIoU(ms+flip): 46.65 - Config: configs/apcnet/apcnet_r101-d8_512x512_80k_ade20k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_512x512_80k_ade20k/apcnet_r101-d8_512x512_80k_ade20k_20201214_115704-c656c3fb.pth -- Name: apcnet_r50-d8_512x512_160k_ade20k - In Collection: APCNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 160000 - Results: - - Task: Semantic Segmentation - Dataset: ADE20K - Metrics: - mIoU: 43.4 - mIoU(ms+flip): 43.94 - Config: configs/apcnet/apcnet_r50-d8_512x512_160k_ade20k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r50-d8_512x512_160k_ade20k/apcnet_r50-d8_512x512_160k_ade20k_20201214_115706-25fb92c2.pth -- Name: apcnet_r101-d8_512x512_160k_ade20k - In Collection: APCNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 160000 - Results: - - Task: Semantic Segmentation - Dataset: ADE20K - Metrics: - mIoU: 45.41 - mIoU(ms+flip): 46.63 - Config: configs/apcnet/apcnet_r101-d8_512x512_160k_ade20k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/apcnet/apcnet_r101-d8_512x512_160k_ade20k/apcnet_r101-d8_512x512_160k_ade20k_20201214_115705-73f9a8d7.pth diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x1024_40k_cityscapes.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x1024_40k_cityscapes.py deleted file mode 100644 index 1e1cec673..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x1024_40k_cityscapes.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './apcnet_r50-d8_512x1024_40k_cityscapes.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x1024_80k_cityscapes.py deleted file mode 100644 index 04cb006ba..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './apcnet_r50-d8_512x1024_80k_cityscapes.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x512_160k_ade20k.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x512_160k_ade20k.py deleted file mode 100644 index 1ce2279a0..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x512_160k_ade20k.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './apcnet_r50-d8_512x512_160k_ade20k.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x512_80k_ade20k.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x512_80k_ade20k.py deleted file mode 100644 index 8f10b9840..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_512x512_80k_ade20k.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './apcnet_r50-d8_512x512_80k_ade20k.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_769x769_40k_cityscapes.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_769x769_40k_cityscapes.py deleted file mode 100644 index 5c44ebcaf..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_769x769_40k_cityscapes.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './apcnet_r50-d8_769x769_40k_cityscapes.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_769x769_80k_cityscapes.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_769x769_80k_cityscapes.py deleted file mode 100644 index 616984575..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r101-d8_769x769_80k_cityscapes.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './apcnet_r50-d8_769x769_80k_cityscapes.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x1024_1k_cityscapes.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x1024_1k_cityscapes.py deleted file mode 100644 index ec8c5c3cd..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x1024_1k_cityscapes.py +++ /dev/null @@ -1,4 +0,0 @@ -_base_ = [ - '../_base_/models/apcnet_r50-d8.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_1k.py' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x1024_40k_cityscapes.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x1024_40k_cityscapes.py deleted file mode 100644 index 99c61a942..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x1024_40k_cityscapes.py +++ /dev/null @@ -1,4 +0,0 @@ -_base_ = [ - '../_base_/models/apcnet_r50-d8.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_40k.py' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x1024_80k_cityscapes.py deleted file mode 100644 index 62a0627ae..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,4 +0,0 @@ -_base_ = [ - '../_base_/models/apcnet_r50-d8.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_160k_ade20k.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_160k_ade20k.py deleted file mode 100644 index f7821c559..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_160k_ade20k.py +++ /dev/null @@ -1,6 +0,0 @@ -_base_ = [ - '../_base_/models/apcnet_r50-d8.py', '../_base_/datasets/ade20k.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_160k.py' -] -model = dict( - decode_head=dict(num_classes=150), auxiliary_head=dict(num_classes=150)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_1k_voc.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_1k_voc.py deleted file mode 100644 index 064f837dc..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_1k_voc.py +++ /dev/null @@ -1,6 +0,0 @@ -_base_ = [ - '../_base_/models/apcnet_r50-d8.py', '../_base_/datasets/pascal_voc12.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_1k.py' -] -model = dict( - decode_head=dict(num_classes=21), auxiliary_head=dict(num_classes=21)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_20k_voc.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_20k_voc.py deleted file mode 100644 index 7529b2b19..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_20k_voc.py +++ /dev/null @@ -1,6 +0,0 @@ -_base_ = [ - '../_base_/models/apcnet_r50-d8.py', '../_base_/datasets/pascal_voc12.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_20k.py' -] -model = dict( - decode_head=dict(num_classes=21), auxiliary_head=dict(num_classes=21)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_80k_ade20k.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_80k_ade20k.py deleted file mode 100644 index daafa5fbc..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_512x512_80k_ade20k.py +++ /dev/null @@ -1,6 +0,0 @@ -_base_ = [ - '../_base_/models/apcnet_r50-d8.py', '../_base_/datasets/ade20k.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] -model = dict( - decode_head=dict(num_classes=150), auxiliary_head=dict(num_classes=150)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_769x769_40k_cityscapes.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_769x769_40k_cityscapes.py deleted file mode 100644 index 3db6140cb..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_769x769_40k_cityscapes.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = [ - '../_base_/models/apcnet_r50-d8.py', - '../_base_/datasets/cityscapes_769x769.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_40k.py' -] -model = dict( - decode_head=dict(align_corners=True), - auxiliary_head=dict(align_corners=True), - test_cfg=dict(mode='slide', crop_size=(769, 769), stride=(513, 513))) diff --git a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_769x769_80k_cityscapes.py b/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_769x769_80k_cityscapes.py deleted file mode 100644 index 9cac4254f..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/configs/apcnet/apcnet_r50-d8_769x769_80k_cityscapes.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = [ - '../_base_/models/apcnet_r50-d8.py', - '../_base_/datasets/cityscapes_769x769.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_80k.py' -] -model = dict( - decode_head=dict(align_corners=True), - auxiliary_head=dict(align_corners=True), - test_cfg=dict(mode='slide', crop_size=(769, 769), stride=(513, 513))) diff --git a/cv/semantic_segmentation/apcnet/pytorch/docker/Dockerfile b/cv/semantic_segmentation/apcnet/pytorch/docker/Dockerfile deleted file mode 100644 index 64482b472..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/docker/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -ARG PYTORCH="1.11.0" -ARG CUDA="11.3" -ARG CUDNN="8" - -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ENV TORCH_CUDA_ARCH_LIST="6.0 6.1 7.0+PTX" -ENV TORCH_NVCC_FLAGS="-Xfatbin -compress-all" -ENV CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" - -# To fix GPG key error when running apt-get update -RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub -RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/7fa2af80.pub - -RUN apt-get update && apt-get install -y git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -RUN conda clean --all - -# Install MMCV -ARG PYTORCH -ARG CUDA -ARG MMCV -RUN ["/bin/bash", "-c", "pip install --no-cache-dir mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] - -# Install MMSegmentation -RUN git clone https://github.com/open-mmlab/mmsegmentation.git /mmsegmentation -WORKDIR /mmsegmentation -ENV FORCE_CUDA="1" -RUN pip install -r requirements.txt -RUN pip install --no-cache-dir -e . diff --git a/cv/semantic_segmentation/apcnet/pytorch/docker/serve/Dockerfile b/cv/semantic_segmentation/apcnet/pytorch/docker/serve/Dockerfile deleted file mode 100644 index c1d154528..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/docker/serve/Dockerfile +++ /dev/null @@ -1,49 +0,0 @@ -ARG PYTORCH="1.11.0" -ARG CUDA="11.3" -ARG CUDNN="8" -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ARG MMCV="1.4.8" -ARG MMSEG="0.24.1" - -ENV PYTHONUNBUFFERED TRUE - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ - ca-certificates \ - g++ \ - openjdk-11-jre-headless \ - # MMDet Requirements - ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && rm -rf /var/lib/apt/lists/* - -ENV PATH="/opt/conda/bin:$PATH" -RUN export FORCE_CUDA=1 - -# TORCHSEVER -RUN pip install torchserve torch-model-archiver - -# MMLAB -ARG PYTORCH -ARG CUDA -RUN ["/bin/bash", "-c", "pip install mmcv-full==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] -RUN pip install mmsegmentation==${MMSEG} - -RUN useradd -m model-server \ - && mkdir -p /home/model-server/tmp - -COPY entrypoint.sh /usr/local/bin/entrypoint.sh - -RUN chmod +x /usr/local/bin/entrypoint.sh \ - && chown -R model-server /home/model-server - -COPY config.properties /home/model-server/config.properties -RUN mkdir /home/model-server/model-store && chown -R model-server /home/model-server/model-store - -EXPOSE 8080 8081 8082 - -USER model-server -WORKDIR /home/model-server -ENV TEMP=/home/model-server/tmp -ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] -CMD ["serve"] diff --git a/cv/semantic_segmentation/apcnet/pytorch/docker/serve/config.properties b/cv/semantic_segmentation/apcnet/pytorch/docker/serve/config.properties deleted file mode 100644 index efb9c47e4..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/docker/serve/config.properties +++ /dev/null @@ -1,5 +0,0 @@ -inference_address=http://0.0.0.0:8080 -management_address=http://0.0.0.0:8081 -metrics_address=http://0.0.0.0:8082 -model_store=/home/model-server/model-store -load_models=all diff --git a/cv/semantic_segmentation/apcnet/pytorch/docker/serve/entrypoint.sh b/cv/semantic_segmentation/apcnet/pytorch/docker/serve/entrypoint.sh deleted file mode 100644 index 41ba00b04..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/docker/serve/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - -if [[ "$1" = "serve" ]]; then - shift 1 - torchserve --start --ts-config /home/model-server/config.properties -else - eval "$@" -fi - -# prevent docker exit -tail -f /dev/null diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/__init__.py deleted file mode 100644 index 435429d48..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -from .fileio import * -from .image import * -from .utils import * -from .version import * - -# The following modules are not imported to this level, so mmcv may be used -# without PyTorch. -# - runner -# - parallel -# - op -# - device diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/__init__.py deleted file mode 100644 index 3d5599d9a..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# yapf: disable -from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, - ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, - ConvTranspose2d, ConvTranspose3d, ConvWS2d, - DepthwiseSeparableConvModule, GeneralizedAttention, - HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, - NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, - build_activation_layer, build_conv_layer, - build_norm_layer, build_padding_layer, build_plugin_layer, - build_upsample_layer, conv_ws_2d, is_norm) -from .builder import MODELS, build_model_from_cfg -# yapf: enable -from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, - NormalInit, PretrainedInit, TruncNormalInit, UniformInit, - XavierInit, bias_init_with_prob, caffe2_xavier_init, - constant_init, fuse_conv_bn, get_model_complexity_info, - initialize, kaiming_init, normal_init, trunc_normal_init, - uniform_init, xavier_init) - diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/__init__.py deleted file mode 100644 index 0f33124ed..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .activation import build_activation_layer -from .context_block import ContextBlock -from .conv import build_conv_layer -from .conv2d_adaptive_padding import Conv2dAdaptivePadding -from .conv_module import ConvModule -from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d -from .depthwise_separable_conv_module import DepthwiseSeparableConvModule -from .drop import Dropout, DropPath -from .generalized_attention import GeneralizedAttention -from .hsigmoid import HSigmoid -from .hswish import HSwish -from .non_local import NonLocal1d, NonLocal2d, NonLocal3d -from .norm import build_norm_layer, is_norm -from .padding import build_padding_layer -from .plugin import build_plugin_layer -from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) -from .scale import Scale -from .swish import Swish -from .upsample import build_upsample_layer -from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, - Linear, MaxPool2d, MaxPool3d) - -__all__ = [ - 'ConvModule', 'build_activation_layer', 'build_conv_layer', - 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', - 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', - 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', - 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', - 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', - 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', - 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/activation.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/activation.py deleted file mode 100644 index 26be59581..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/activation.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version -from .registry import ACTIVATION_LAYERS - -for module in [ - nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, - nn.Sigmoid, nn.Tanh -]: - ACTIVATION_LAYERS.register_module(module=module) - - -@ACTIVATION_LAYERS.register_module(name='Clip') -@ACTIVATION_LAYERS.register_module() -class Clamp(nn.Module): - """Clamp activation layer. - - This activation function is to clamp the feature map value within - :math:`[min, max]`. More details can be found in ``torch.clamp()``. - - Args: - min (Number | optional): Lower-bound of the range to be clamped to. - Default to -1. - max (Number | optional): Upper-bound of the range to be clamped to. - Default to 1. - """ - - def __init__(self, min=-1., max=1.): - super(Clamp, self).__init__() - self.min = min - self.max = max - - def forward(self, x): - """Forward function. - - Args: - x (torch.Tensor): The input tensor. - - Returns: - torch.Tensor: Clamped tensor. - """ - return torch.clamp(x, min=self.min, max=self.max) - - -class GELU(nn.Module): - r"""Applies the Gaussian Error Linear Units function: - - .. math:: - \text{GELU}(x) = x * \Phi(x) - where :math:`\Phi(x)` is the Cumulative Distribution Function for - Gaussian Distribution. - - Shape: - - Input: :math:`(N, *)` where `*` means, any number of additional - dimensions - - Output: :math:`(N, *)`, same shape as the input - - .. image:: scripts/activation_images/GELU.png - - Examples:: - - >>> m = nn.GELU() - >>> input = torch.randn(2) - >>> output = m(input) - """ - - def forward(self, input): - return F.gelu(input) - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.4')): - ACTIVATION_LAYERS.register_module(module=GELU) -else: - ACTIVATION_LAYERS.register_module(module=nn.GELU) - - -def build_activation_layer(cfg): - """Build activation layer. - - Args: - cfg (dict): The activation layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate an activation layer. - - Returns: - nn.Module: Created activation layer. - """ - return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/context_block.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/context_block.py deleted file mode 100644 index d60fdb904..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/context_block.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn - -from ..utils import constant_init, kaiming_init -from .registry import PLUGIN_LAYERS - - -def last_zero_init(m): - if isinstance(m, nn.Sequential): - constant_init(m[-1], val=0) - else: - constant_init(m, val=0) - - -@PLUGIN_LAYERS.register_module() -class ContextBlock(nn.Module): - """ContextBlock module in GCNet. - - See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' - (https://arxiv.org/abs/1904.11492) for details. - - Args: - in_channels (int): Channels of the input feature map. - ratio (float): Ratio of channels of transform bottleneck - pooling_type (str): Pooling method for context modeling. - Options are 'att' and 'avg', stand for attention pooling and - average pooling respectively. Default: 'att'. - fusion_types (Sequence[str]): Fusion method for feature fusion, - Options are 'channels_add', 'channel_mul', stand for channelwise - addition and multiplication respectively. Default: ('channel_add',) - """ - - _abbr_ = 'context_block' - - def __init__(self, - in_channels, - ratio, - pooling_type='att', - fusion_types=('channel_add', )): - super(ContextBlock, self).__init__() - assert pooling_type in ['avg', 'att'] - assert isinstance(fusion_types, (list, tuple)) - valid_fusion_types = ['channel_add', 'channel_mul'] - assert all([f in valid_fusion_types for f in fusion_types]) - assert len(fusion_types) > 0, 'at least one fusion should be used' - self.in_channels = in_channels - self.ratio = ratio - self.planes = int(in_channels * ratio) - self.pooling_type = pooling_type - self.fusion_types = fusion_types - if pooling_type == 'att': - self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) - self.softmax = nn.Softmax(dim=2) - else: - self.avg_pool = nn.AdaptiveAvgPool2d(1) - if 'channel_add' in fusion_types: - self.channel_add_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_add_conv = None - if 'channel_mul' in fusion_types: - self.channel_mul_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_mul_conv = None - self.reset_parameters() - - def reset_parameters(self): - if self.pooling_type == 'att': - kaiming_init(self.conv_mask, mode='fan_in') - self.conv_mask.inited = True - - if self.channel_add_conv is not None: - last_zero_init(self.channel_add_conv) - if self.channel_mul_conv is not None: - last_zero_init(self.channel_mul_conv) - - def spatial_pool(self, x): - batch, channel, height, width = x.size() - if self.pooling_type == 'att': - input_x = x - # [N, C, H * W] - input_x = input_x.view(batch, channel, height * width) - # [N, 1, C, H * W] - input_x = input_x.unsqueeze(1) - # [N, 1, H, W] - context_mask = self.conv_mask(x) - # [N, 1, H * W] - context_mask = context_mask.view(batch, 1, height * width) - # [N, 1, H * W] - context_mask = self.softmax(context_mask) - # [N, 1, H * W, 1] - context_mask = context_mask.unsqueeze(-1) - # [N, 1, C, 1] - context = torch.matmul(input_x, context_mask) - # [N, C, 1, 1] - context = context.view(batch, channel, 1, 1) - else: - # [N, C, 1, 1] - context = self.avg_pool(x) - - return context - - def forward(self, x): - # [N, C, 1, 1] - context = self.spatial_pool(x) - - out = x - if self.channel_mul_conv is not None: - # [N, C, 1, 1] - channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) - out = out * channel_mul_term - if self.channel_add_conv is not None: - # [N, C, 1, 1] - channel_add_term = self.channel_add_conv(context) - out = out + channel_add_term - - return out diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv.py deleted file mode 100644 index f6c35fd70..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from .registry import CONV_LAYERS - -CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) -CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) -CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) -CONV_LAYERS.register_module('Conv', module=nn.Conv2d) - - -def build_conv_layer(cfg, *args, **kwargs): - """Build convolution layer. - - Args: - cfg (None or dict): The conv layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an conv layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding conv layer. - - Returns: - nn.Module: Created conv layer. - """ - if cfg is None: - cfg_ = dict(type='Conv2d') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in CONV_LAYERS: - raise KeyError(f'Unrecognized layer type {layer_type}') - else: - conv_layer = CONV_LAYERS.get(layer_type) - - layer = conv_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py deleted file mode 100644 index b45e758ac..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from torch import nn -from torch.nn import functional as F - -from .registry import CONV_LAYERS - - -@CONV_LAYERS.register_module() -class Conv2dAdaptivePadding(nn.Conv2d): - """Implementation of 2D convolution in tensorflow with `padding` as "same", - which applies padding to input (if needed) so that input image gets fully - covered by filter and stride you specified. For stride 1, this will ensure - that output image size is same as input. For stride of 2, output dimensions - will be half, for example. - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the convolving kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If ``True``, adds a learnable bias to the - output. Default: ``True`` - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__(in_channels, out_channels, kernel_size, stride, 0, - dilation, groups, bias) - - def forward(self, x): - img_h, img_w = x.size()[-2:] - kernel_h, kernel_w = self.weight.size()[-2:] - stride_h, stride_w = self.stride - output_h = math.ceil(img_h / stride_h) - output_w = math.ceil(img_w / stride_w) - pad_h = ( - max((output_h - 1) * self.stride[0] + - (kernel_h - 1) * self.dilation[0] + 1 - img_h, 0)) - pad_w = ( - max((output_w - 1) * self.stride[1] + - (kernel_w - 1) * self.dilation[1] + 1 - img_w, 0)) - if pad_h > 0 or pad_w > 0: - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2 - ]) - return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv_module.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv_module.py deleted file mode 100644 index 0078647a1..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv_module.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from mmcv.utils import _BatchNorm, _InstanceNorm -from ..utils import constant_init, kaiming_init -from .activation import build_activation_layer -from .conv import build_conv_layer -from .norm import build_norm_layer -from .padding import build_padding_layer -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class ConvModule(nn.Module): - """A conv block that bundles conv/norm/activation layers. - - This block simplifies the usage of convolution layers, which are commonly - used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). - It is based upon three build methods: `build_conv_layer()`, - `build_norm_layer()` and `build_activation_layer()`. - - Besides, we add some additional features in this module. - 1. Automatically set `bias` of the conv layer. - 2. Spectral norm is supported. - 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only - supports zero and circular padding, and we add "reflect" padding mode. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. - groups (int): Number of blocked connections from input channels to - output channels. Same as that in ``nn._ConvNd``. - bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - inplace (bool): Whether to use inplace mode for activation. - Default: True. - with_spectral_norm (bool): Whether use spectral norm in conv module. - Default: False. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in PyTorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Common examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - Default: ('conv', 'norm', 'act'). - """ - - _abbr_ = 'conv_block' - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias='auto', - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - padding_mode='zeros', - order=('conv', 'norm', 'act')): - super(ConvModule, self).__init__() - assert conv_cfg is None or isinstance(conv_cfg, dict) - assert norm_cfg is None or isinstance(norm_cfg, dict) - assert act_cfg is None or isinstance(act_cfg, dict) - official_padding_mode = ['zeros', 'circular'] - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.with_explicit_padding = padding_mode not in official_padding_mode - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 3 - assert set(order) == set(['conv', 'norm', 'act']) - - self.with_norm = norm_cfg is not None - self.with_activation = act_cfg is not None - # if the conv layer is before a norm layer, bias is unnecessary. - if bias == 'auto': - bias = not self.with_norm - self.with_bias = bias - - if self.with_explicit_padding: - pad_cfg = dict(type=padding_mode) - self.padding_layer = build_padding_layer(pad_cfg, padding) - - # reset padding to 0 for conv module - conv_padding = 0 if self.with_explicit_padding else padding - # build convolution layer - self.conv = build_conv_layer( - conv_cfg, - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=conv_padding, - dilation=dilation, - groups=groups, - bias=bias) - # export the attributes of self.conv to a higher level for convenience - self.in_channels = self.conv.in_channels - self.out_channels = self.conv.out_channels - self.kernel_size = self.conv.kernel_size - self.stride = self.conv.stride - self.padding = padding - self.dilation = self.conv.dilation - self.transposed = self.conv.transposed - self.output_padding = self.conv.output_padding - self.groups = self.conv.groups - - if self.with_spectral_norm: - self.conv = nn.utils.spectral_norm(self.conv) - - # build normalization layers - if self.with_norm: - # norm layer is after conv layer - if order.index('norm') > order.index('conv'): - norm_channels = out_channels - else: - norm_channels = in_channels - self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) - self.add_module(self.norm_name, norm) - if self.with_bias: - if isinstance(norm, (_BatchNorm, _InstanceNorm)): - warnings.warn( - 'Unnecessary conv bias before batch/instance norm') - else: - self.norm_name = None - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - # nn.Tanh has no 'inplace' argument - if act_cfg_['type'] not in [ - 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish', 'GELU' - ]: - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - @property - def norm(self): - if self.norm_name: - return getattr(self, self.norm_name) - else: - return None - - def init_weights(self): - # 1. It is mainly for customized conv layers with their own - # initialization manners by calling their own ``init_weights()``, - # and we do not want ConvModule to override the initialization. - # 2. For customized conv layers without their own initialization - # manners (that is, they don't have their own ``init_weights()``) - # and PyTorch's conv layers, they will be initialized by - # this method with default ``kaiming_init``. - # Note: For PyTorch's conv layers, they will be overwritten by our - # initialization implementation using default ``kaiming_init``. - if not hasattr(self.conv, 'init_weights'): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) - if self.with_norm: - constant_init(self.norm, 1, bias=0) - - def forward(self, x, activate=True, norm=True): - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - x = self.conv(x) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv_ws.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv_ws.py deleted file mode 100644 index a3941e278..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/conv_ws.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from .registry import CONV_LAYERS - - -def conv_ws_2d(input, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - eps=1e-5): - c_in = weight.size(0) - weight_flat = weight.view(c_in, -1) - mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1) - std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1) - weight = (weight - mean) / (std + eps) - return F.conv2d(input, weight, bias, stride, padding, dilation, groups) - - -@CONV_LAYERS.register_module('ConvWS') -class ConvWS2d(nn.Conv2d): - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True, - eps=1e-5): - super(ConvWS2d, self).__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.eps = eps - - def forward(self, x): - return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups, self.eps) - - -@CONV_LAYERS.register_module(name='ConvAWS') -class ConvAWS2d(nn.Conv2d): - """AWS (Adaptive Weight Standardization) - - This is a variant of Weight Standardization - (https://arxiv.org/pdf/1903.10520.pdf) - It is used in DetectoRS to avoid NaN - (https://arxiv.org/pdf/2006.02334.pdf) - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the conv kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If set True, adds a learnable bias to the - output. Default: True - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.register_buffer('weight_gamma', - torch.ones(self.out_channels, 1, 1, 1)) - self.register_buffer('weight_beta', - torch.zeros(self.out_channels, 1, 1, 1)) - - def _get_weight(self, weight): - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - weight = (weight - mean) / std - weight = self.weight_gamma * weight + self.weight_beta - return weight - - def forward(self, x): - weight = self._get_weight(self.weight) - return F.conv2d(x, weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - """Override default load function. - - AWS overrides the function _load_from_state_dict to recover - weight_gamma and weight_beta if they are missing. If weight_gamma and - weight_beta are found in the checkpoint, this function will return - after super()._load_from_state_dict. Otherwise, it will compute the - mean and std of the pretrained weights and store them in weight_beta - and weight_gamma. - """ - - self.weight_gamma.data.fill_(-1) - local_missing_keys = [] - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, local_missing_keys, - unexpected_keys, error_msgs) - if self.weight_gamma.data.mean() > 0: - for k in local_missing_keys: - missing_keys.append(k) - return - weight = self.weight.data - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - self.weight_beta.data.copy_(mean) - self.weight_gamma.data.copy_(std) - missing_gamma_beta = [ - k for k in local_missing_keys - if k.endswith('weight_gamma') or k.endswith('weight_beta') - ] - for k in missing_gamma_beta: - local_missing_keys.remove(k) - for k in local_missing_keys: - missing_keys.append(k) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py deleted file mode 100644 index 722d5d8d7..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .conv_module import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if `norm_cfg` and `act_cfg` are specified. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. Default: 1. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. Default: 0. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super(DepthwiseSeparableConvModule, self).__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/drop.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/drop.py deleted file mode 100644 index b0a026654..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/drop.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from mmcv import build_from_cfg -from .registry import DROPOUT_LAYERS - - -def drop_path(x, drop_prob=0., training=False): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - """ - if drop_prob == 0. or not training: - return x - keep_prob = 1 - drop_prob - # handle tensors with different dimensions, not just 4D tensors. - shape = (x.shape[0], ) + (1, ) * (x.ndim - 1) - random_tensor = keep_prob + torch.rand( - shape, dtype=x.dtype, device=x.device) - output = x.div(keep_prob) * random_tensor.floor() - return output - - -@DROPOUT_LAYERS.register_module() -class DropPath(nn.Module): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - - Args: - drop_prob (float): Probability of the path to be zeroed. Default: 0.1 - """ - - def __init__(self, drop_prob=0.1): - super(DropPath, self).__init__() - self.drop_prob = drop_prob - - def forward(self, x): - return drop_path(x, self.drop_prob, self.training) - - -@DROPOUT_LAYERS.register_module() -class Dropout(nn.Dropout): - """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of - ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with - ``DropPath`` - - Args: - drop_prob (float): Probability of the elements to be - zeroed. Default: 0.5. - inplace (bool): Do the operation inplace or not. Default: False. - """ - - def __init__(self, drop_prob=0.5, inplace=False): - super().__init__(p=drop_prob, inplace=inplace) - - -def build_dropout(cfg, default_args=None): - """Builder for drop out layers.""" - return build_from_cfg(cfg, DROPOUT_LAYERS, default_args) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/generalized_attention.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/generalized_attention.py deleted file mode 100644 index c8a74d268..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/generalized_attention.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import kaiming_init -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class GeneralizedAttention(nn.Module): - """GeneralizedAttention module. - - See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' - (https://arxiv.org/abs/1711.07971) for details. - - Args: - in_channels (int): Channels of the input feature map. - spatial_range (int): The spatial range. -1 indicates no spatial range - constraint. Default: -1. - num_heads (int): The head number of empirical_attention module. - Default: 9. - position_embedding_dim (int): The position embedding dimension. - Default: -1. - position_magnitude (int): A multiplier acting on coord difference. - Default: 1. - kv_stride (int): The feature stride acting on key/value feature map. - Default: 2. - q_stride (int): The feature stride acting on query feature map. - Default: 1. - attention_type (str): A binary indicator string for indicating which - items in generalized empirical_attention module are used. - Default: '1111'. - - - '1000' indicates 'query and key content' (appr - appr) item, - - '0100' indicates 'query content and relative position' - (appr - position) item, - - '0010' indicates 'key content only' (bias - appr) item, - - '0001' indicates 'relative position only' (bias - position) item. - """ - - _abbr_ = 'gen_attention_block' - - def __init__(self, - in_channels, - spatial_range=-1, - num_heads=9, - position_embedding_dim=-1, - position_magnitude=1, - kv_stride=2, - q_stride=1, - attention_type='1111'): - - super(GeneralizedAttention, self).__init__() - - # hard range means local range for non-local operation - self.position_embedding_dim = ( - position_embedding_dim - if position_embedding_dim > 0 else in_channels) - - self.position_magnitude = position_magnitude - self.num_heads = num_heads - self.in_channels = in_channels - self.spatial_range = spatial_range - self.kv_stride = kv_stride - self.q_stride = q_stride - self.attention_type = [bool(int(_)) for _ in attention_type] - self.qk_embed_dim = in_channels // num_heads - out_c = self.qk_embed_dim * num_heads - - if self.attention_type[0] or self.attention_type[1]: - self.query_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.query_conv.kaiming_init = True - - if self.attention_type[0] or self.attention_type[2]: - self.key_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.key_conv.kaiming_init = True - - self.v_dim = in_channels // num_heads - self.value_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=self.v_dim * num_heads, - kernel_size=1, - bias=False) - self.value_conv.kaiming_init = True - - if self.attention_type[1] or self.attention_type[3]: - self.appr_geom_fc_x = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_x.kaiming_init = True - - self.appr_geom_fc_y = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_y.kaiming_init = True - - if self.attention_type[2]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.appr_bias = nn.Parameter(appr_bias_value) - - if self.attention_type[3]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.geom_bias = nn.Parameter(geom_bias_value) - - self.proj_conv = nn.Conv2d( - in_channels=self.v_dim * num_heads, - out_channels=in_channels, - kernel_size=1, - bias=True) - self.proj_conv.kaiming_init = True - self.gamma = nn.Parameter(torch.zeros(1)) - - if self.spatial_range >= 0: - # only works when non local is after 3*3 conv - if in_channels == 256: - max_len = 84 - elif in_channels == 512: - max_len = 42 - - max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) - local_constraint_map = np.ones( - (max_len, max_len, max_len_kv, max_len_kv), dtype=int) - for iy in range(max_len): - for ix in range(max_len): - local_constraint_map[ - iy, ix, - max((iy - self.spatial_range) // - self.kv_stride, 0):min((iy + self.spatial_range + - 1) // self.kv_stride + - 1, max_len), - max((ix - self.spatial_range) // - self.kv_stride, 0):min((ix + self.spatial_range + - 1) // self.kv_stride + - 1, max_len)] = 0 - - self.local_constraint_map = nn.Parameter( - torch.from_numpy(local_constraint_map).byte(), - requires_grad=False) - - if self.q_stride > 1: - self.q_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.q_stride) - else: - self.q_downsample = None - - if self.kv_stride > 1: - self.kv_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.kv_stride) - else: - self.kv_downsample = None - - self.init_weights() - - def get_position_embedding(self, - h, - w, - h_kv, - w_kv, - q_stride, - kv_stride, - device, - dtype, - feat_dim, - wave_length=1000): - # the default type of Tensor is float32, leading to type mismatch - # in fp16 mode. Cast it to support fp16 mode. - h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) - h_idxs = h_idxs.view((h, 1)) * q_stride - - w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) - w_idxs = w_idxs.view((w, 1)) * q_stride - - h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( - device=device, dtype=dtype) - h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride - - w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( - device=device, dtype=dtype) - w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride - - # (h, h_kv, 1) - h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) - h_diff *= self.position_magnitude - - # (w, w_kv, 1) - w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) - w_diff *= self.position_magnitude - - feat_range = torch.arange(0, feat_dim / 4).to( - device=device, dtype=dtype) - - dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) - dim_mat = dim_mat**((4. / feat_dim) * feat_range) - dim_mat = dim_mat.view((1, 1, -1)) - - embedding_x = torch.cat( - ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) - - embedding_y = torch.cat( - ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) - - return embedding_x, embedding_y - - def forward(self, x_input): - num_heads = self.num_heads - - # use empirical_attention - if self.q_downsample is not None: - x_q = self.q_downsample(x_input) - else: - x_q = x_input - n, _, h, w = x_q.shape - - if self.kv_downsample is not None: - x_kv = self.kv_downsample(x_input) - else: - x_kv = x_input - _, _, h_kv, w_kv = x_kv.shape - - if self.attention_type[0] or self.attention_type[1]: - proj_query = self.query_conv(x_q).view( - (n, num_heads, self.qk_embed_dim, h * w)) - proj_query = proj_query.permute(0, 1, 3, 2) - - if self.attention_type[0] or self.attention_type[2]: - proj_key = self.key_conv(x_kv).view( - (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) - - if self.attention_type[1] or self.attention_type[3]: - position_embed_x, position_embed_y = self.get_position_embedding( - h, w, h_kv, w_kv, self.q_stride, self.kv_stride, - x_input.device, x_input.dtype, self.position_embedding_dim) - # (n, num_heads, w, w_kv, dim) - position_feat_x = self.appr_geom_fc_x(position_embed_x).\ - view(1, w, w_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - # (n, num_heads, h, h_kv, dim) - position_feat_y = self.appr_geom_fc_y(position_embed_y).\ - view(1, h, h_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - position_feat_x /= math.sqrt(2) - position_feat_y /= math.sqrt(2) - - # accelerate for saliency only - if (np.sum(self.attention_type) == 1) and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy = torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, h_kv * w_kv) - - h = 1 - w = 1 - else: - # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for - if not self.attention_type[0]: - energy = torch.zeros( - n, - num_heads, - h, - w, - h_kv, - w_kv, - dtype=x_input.dtype, - device=x_input.device) - - # attention_type[0]: appr - appr - # attention_type[1]: appr - position - # attention_type[2]: bias - appr - # attention_type[3]: bias - position - if self.attention_type[0] or self.attention_type[2]: - if self.attention_type[0] and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - energy = torch.matmul(proj_query + appr_bias, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[0]: - energy = torch.matmul(proj_query, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy += torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, 1, h_kv, w_kv) - - if self.attention_type[1] or self.attention_type[3]: - if self.attention_type[1] and self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - - proj_query_reshape = (proj_query + geom_bias).\ - view(n, num_heads, h, w, self.qk_embed_dim) - - energy_x = torch.matmul( - proj_query_reshape.permute(0, 1, 3, 2, 4), - position_feat_x.permute(0, 1, 2, 4, 3)) - energy_x = energy_x.\ - permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul( - proj_query_reshape, - position_feat_y.permute(0, 1, 2, 4, 3)) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[1]: - proj_query_reshape = proj_query.\ - view(n, num_heads, h, w, self.qk_embed_dim) - proj_query_reshape = proj_query_reshape.\ - permute(0, 1, 3, 2, 4) - position_feat_x_reshape = position_feat_x.\ - permute(0, 1, 2, 4, 3) - position_feat_y_reshape = position_feat_y.\ - permute(0, 1, 2, 4, 3) - - energy_x = torch.matmul(proj_query_reshape, - position_feat_x_reshape) - energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul(proj_query_reshape, - position_feat_y_reshape) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, self.qk_embed_dim, 1).\ - repeat(n, 1, 1, 1) - - position_feat_x_reshape = position_feat_x.\ - view(n, num_heads, w * w_kv, self.qk_embed_dim) - - position_feat_y_reshape = position_feat_y.\ - view(n, num_heads, h * h_kv, self.qk_embed_dim) - - energy_x = torch.matmul(position_feat_x_reshape, geom_bias) - energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) - - energy_y = torch.matmul(position_feat_y_reshape, geom_bias) - energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) - - energy += energy_x + energy_y - - energy = energy.view(n, num_heads, h * w, h_kv * w_kv) - - if self.spatial_range >= 0: - cur_local_constraint_map = \ - self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ - contiguous().\ - view(1, 1, h*w, h_kv*w_kv) - - energy = energy.masked_fill_(cur_local_constraint_map, - float('-inf')) - - attention = F.softmax(energy, 3) - - proj_value = self.value_conv(x_kv) - proj_value_reshape = proj_value.\ - view((n, num_heads, self.v_dim, h_kv * w_kv)).\ - permute(0, 1, 3, 2) - - out = torch.matmul(attention, proj_value_reshape).\ - permute(0, 1, 3, 2).\ - contiguous().\ - view(n, self.v_dim * self.num_heads, h, w) - - out = self.proj_conv(out) - - # output is downsampled, upsample back to input size - if self.q_downsample is not None: - out = F.interpolate( - out, - size=x_input.shape[2:], - mode='bilinear', - align_corners=False) - - out = self.gamma * out + x_input - return out - - def init_weights(self): - for m in self.modules(): - if hasattr(m, 'kaiming_init') and m.kaiming_init: - kaiming_init( - m, - mode='fan_in', - nonlinearity='leaky_relu', - bias=0, - distribution='uniform', - a=1) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/hsigmoid.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/hsigmoid.py deleted file mode 100644 index e013d739e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/hsigmoid.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSigmoid(nn.Module): - """Hard Sigmoid Module. Apply the hard sigmoid function: - Hsigmoid(x) = min(max((x + bias) / divisor, min_value), max_value) - Default: Hsigmoid(x) = min(max((x + 3) / 6, 0), 1) - - Note: - In MMCV v1.4.4, we modified the default value of args to align with - PyTorch official. - - Args: - bias (float): Bias of the input feature map. Default: 3.0. - divisor (float): Divisor of the input feature map. Default: 6.0. - min_value (float): Lower bound value. Default: 0.0. - max_value (float): Upper bound value. Default: 1.0. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, bias=3.0, divisor=6.0, min_value=0.0, max_value=1.0): - super(HSigmoid, self).__init__() - warnings.warn( - 'In MMCV v1.4.4, we modified the default value of args to align ' - 'with PyTorch official. Previous Implementation: ' - 'Hsigmoid(x) = min(max((x + 1) / 2, 0), 1). ' - 'Current Implementation: ' - 'Hsigmoid(x) = min(max((x + 3) / 6, 0), 1).') - self.bias = bias - self.divisor = divisor - assert self.divisor != 0 - self.min_value = min_value - self.max_value = max_value - - def forward(self, x): - x = (x + self.bias) / self.divisor - - return x.clamp_(self.min_value, self.max_value) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/hswish.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/hswish.py deleted file mode 100644 index 27096832f..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/hswish.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import ACTIVATION_LAYERS - - -class HSwish(nn.Module): - """Hard Swish Module. - - This module applies the hard swish function: - - .. math:: - Hswish(x) = x * ReLU6(x + 3) / 6 - - Args: - inplace (bool): can optionally do the operation in-place. - Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.act = nn.ReLU6(inplace) - - def forward(self, x): - return x * self.act(x + 3) / 6 - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.7')): - # Hardswish is not supported when PyTorch version < 1.6. - # And Hardswish in PyTorch 1.6 does not support inplace. - ACTIVATION_LAYERS.register_module(module=HSwish) -else: - ACTIVATION_LAYERS.register_module(module=nn.Hardswish, name='HSwish') diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/non_local.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/non_local.py deleted file mode 100644 index 92d00155e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/non_local.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta - -import torch -import torch.nn as nn - -from ..utils import constant_init, normal_init -from .conv_module import ConvModule -from .registry import PLUGIN_LAYERS - - -class _NonLocalNd(nn.Module, metaclass=ABCMeta): - """Basic Non-local module. - - This module is proposed in - "Non-local Neural Networks" - Paper reference: https://arxiv.org/abs/1711.07971 - Code reference: https://github.com/AlexHex7/Non-local_pytorch - - Args: - in_channels (int): Channels of the input feature map. - reduction (int): Channel reduction ratio. Default: 2. - use_scale (bool): Whether to scale pairwise_weight by - `1/sqrt(inter_channels)` when the mode is `embedded_gaussian`. - Default: True. - conv_cfg (None | dict): The config dict for convolution layers. - If not specified, it will use `nn.Conv2d` for convolution layers. - Default: None. - norm_cfg (None | dict): The config dict for normalization layers. - Default: None. (This parameter is only applicable to conv_out.) - mode (str): Options are `gaussian`, `concatenation`, - `embedded_gaussian` and `dot_product`. Default: embedded_gaussian. - """ - - def __init__(self, - in_channels, - reduction=2, - use_scale=True, - conv_cfg=None, - norm_cfg=None, - mode='embedded_gaussian', - **kwargs): - super(_NonLocalNd, self).__init__() - self.in_channels = in_channels - self.reduction = reduction - self.use_scale = use_scale - self.inter_channels = max(in_channels // reduction, 1) - self.mode = mode - - if mode not in [ - 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation' - ]: - raise ValueError("Mode should be in 'gaussian', 'concatenation', " - f"'embedded_gaussian' or 'dot_product', but got " - f'{mode} instead.') - - # g, theta, phi are defaulted as `nn.ConvNd`. - # Here we use ConvModule for potential usage. - self.g = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.conv_out = ConvModule( - self.inter_channels, - self.in_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - if self.mode != 'gaussian': - self.theta = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.phi = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - - if self.mode == 'concatenation': - self.concat_project = ConvModule( - self.inter_channels * 2, - 1, - kernel_size=1, - stride=1, - padding=0, - bias=False, - act_cfg=dict(type='ReLU')) - - self.init_weights(**kwargs) - - def init_weights(self, std=0.01, zeros_init=True): - if self.mode != 'gaussian': - for m in [self.g, self.theta, self.phi]: - normal_init(m.conv, std=std) - else: - normal_init(self.g.conv, std=std) - if zeros_init: - if self.conv_out.norm_cfg is None: - constant_init(self.conv_out.conv, 0) - else: - constant_init(self.conv_out.norm, 0) - else: - if self.conv_out.norm_cfg is None: - normal_init(self.conv_out.conv, std=std) - else: - normal_init(self.conv_out.norm, std=std) - - def gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def embedded_gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - if self.use_scale: - # theta_x.shape[-1] is `self.inter_channels` - pairwise_weight /= theta_x.shape[-1]**0.5 - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def dot_product(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight /= pairwise_weight.shape[-1] - return pairwise_weight - - def concatenation(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - h = theta_x.size(2) - w = phi_x.size(3) - theta_x = theta_x.repeat(1, 1, 1, w) - phi_x = phi_x.repeat(1, 1, h, 1) - - concat_feature = torch.cat([theta_x, phi_x], dim=1) - pairwise_weight = self.concat_project(concat_feature) - n, _, h, w = pairwise_weight.size() - pairwise_weight = pairwise_weight.view(n, h, w) - pairwise_weight /= pairwise_weight.shape[-1] - - return pairwise_weight - - def forward(self, x): - # Assume `reduction = 1`, then `inter_channels = C` - # or `inter_channels = C` when `mode="gaussian"` - - # NonLocal1d x: [N, C, H] - # NonLocal2d x: [N, C, H, W] - # NonLocal3d x: [N, C, T, H, W] - n = x.size(0) - - # NonLocal1d g_x: [N, H, C] - # NonLocal2d g_x: [N, HxW, C] - # NonLocal3d g_x: [N, TxHxW, C] - g_x = self.g(x).view(n, self.inter_channels, -1) - g_x = g_x.permute(0, 2, 1) - - # NonLocal1d theta_x: [N, H, C], phi_x: [N, C, H] - # NonLocal2d theta_x: [N, HxW, C], phi_x: [N, C, HxW] - # NonLocal3d theta_x: [N, TxHxW, C], phi_x: [N, C, TxHxW] - if self.mode == 'gaussian': - theta_x = x.view(n, self.in_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - if self.sub_sample: - phi_x = self.phi(x).view(n, self.in_channels, -1) - else: - phi_x = x.view(n, self.in_channels, -1) - elif self.mode == 'concatenation': - theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) - phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) - else: - theta_x = self.theta(x).view(n, self.inter_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - phi_x = self.phi(x).view(n, self.inter_channels, -1) - - pairwise_func = getattr(self, self.mode) - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = pairwise_func(theta_x, phi_x) - - # NonLocal1d y: [N, H, C] - # NonLocal2d y: [N, HxW, C] - # NonLocal3d y: [N, TxHxW, C] - y = torch.matmul(pairwise_weight, g_x) - # NonLocal1d y: [N, C, H] - # NonLocal2d y: [N, C, H, W] - # NonLocal3d y: [N, C, T, H, W] - y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, - *x.size()[2:]) - - output = x + self.conv_out(y) - - return output - - -class NonLocal1d(_NonLocalNd): - """1D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv1d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv1d'), - **kwargs): - super(NonLocal1d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool1d(kernel_size=2) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -@PLUGIN_LAYERS.register_module() -class NonLocal2d(_NonLocalNd): - """2D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv2d'). - """ - - _abbr_ = 'nonlocal_block' - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv2d'), - **kwargs): - super(NonLocal2d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -class NonLocal3d(_NonLocalNd): - """3D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv3d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv3d'), - **kwargs): - super(NonLocal3d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/norm.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/norm.py deleted file mode 100644 index 51efdc184..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/norm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import torch.nn as nn - -from mmcv.utils import is_tuple_of -from mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm -from .registry import NORM_LAYERS - -NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) -NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) -NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) -NORM_LAYERS.register_module('GN', module=nn.GroupNorm) -NORM_LAYERS.register_module('LN', module=nn.LayerNorm) -NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) -NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - When we build a norm layer with `build_norm_layer()`, we want to preserve - the norm type in variable names, e.g, self.bn1, self.gn. This method will - infer the abbreviation to map class types to abbreviations. - - Rule 1: If the class has the property "_abbr_", return the property. - Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or - InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and - "in" respectively. - Rule 3: If the class name contains "batch", "group", "layer" or "instance", - the abbreviation of this layer will be "bn", "gn", "ln" and "in" - respectively. - Rule 4: Otherwise, the abbreviation falls back to "norm". - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN - return 'in' - elif issubclass(class_type, _BatchNorm): - return 'bn' - elif issubclass(class_type, nn.GroupNorm): - return 'gn' - elif issubclass(class_type, nn.LayerNorm): - return 'ln' - else: - class_name = class_type.__name__.lower() - if 'batch' in class_name: - return 'bn' - elif 'group' in class_name: - return 'gn' - elif 'layer' in class_name: - return 'ln' - elif 'instance' in class_name: - return 'in' - else: - return 'norm_layer' - - -def build_norm_layer(cfg, num_features, postfix=''): - """Build normalization layer. - - Args: - cfg (dict): The norm layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate a norm layer. - - requires_grad (bool, optional): Whether stop gradient updates. - num_features (int): Number of input channels. - postfix (int | str): The postfix to be appended into norm abbreviation - to create named layer. - - Returns: - tuple[str, nn.Module]: The first element is the layer name consisting - of abbreviation and postfix, e.g., bn1, gn. The second element is the - created norm layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in NORM_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - - norm_layer = NORM_LAYERS.get(layer_type) - abbr = infer_abbr(norm_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - requires_grad = cfg_.pop('requires_grad', True) - cfg_.setdefault('eps', 1e-5) - if layer_type != 'GN': - layer = norm_layer(num_features, **cfg_) - if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): - layer._specify_ddp_gpu_num(1) - else: - assert 'num_groups' in cfg_ - layer = norm_layer(num_channels=num_features, **cfg_) - - for param in layer.parameters(): - param.requires_grad = requires_grad - - return name, layer - - -def is_norm(layer, exclude=None): - """Check if a layer is a normalization layer. - - Args: - layer (nn.Module): The layer to be checked. - exclude (type | tuple[type]): Types to be excluded. - - Returns: - bool: Whether the layer is a norm layer. - """ - if exclude is not None: - if not isinstance(exclude, tuple): - exclude = (exclude, ) - if not is_tuple_of(exclude, type): - raise TypeError( - f'"exclude" must be either None or type or a tuple of types, ' - f'but got {type(exclude)}: {exclude}') - - if exclude and isinstance(layer, exclude): - return False - - all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) - return isinstance(layer, all_norm_bases) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/padding.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/padding.py deleted file mode 100644 index e4ac6b28a..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/padding.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import PADDING_LAYERS - -PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) -PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) -PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) - - -def build_padding_layer(cfg, *args, **kwargs): - """Build padding layer. - - Args: - cfg (None or dict): The padding layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate a padding layer. - - Returns: - nn.Module: Created padding layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - - cfg_ = cfg.copy() - padding_type = cfg_.pop('type') - if padding_type not in PADDING_LAYERS: - raise KeyError(f'Unrecognized padding type {padding_type}.') - else: - padding_layer = PADDING_LAYERS.get(padding_type) - - layer = padding_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/plugin.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/plugin.py deleted file mode 100644 index 009f7529b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/plugin.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import platform - -from .registry import PLUGIN_LAYERS - -if platform.system() == 'Windows': - import regex as re -else: - import re - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - This method will infer the abbreviation to map class types to - abbreviations. - - Rule 1: If the class has the property "abbr", return the property. - Rule 2: Otherwise, the abbreviation falls back to snake case of class - name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - - def camel2snack(word): - """Convert camel case word into snack case. - - Modified from `inflection lib - `_. - - Example:: - - >>> camel2snack("FancyBlock") - 'fancy_block' - """ - - word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) - word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) - word = word.replace('-', '_') - return word.lower() - - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - else: - return camel2snack(class_type.__name__) - - -def build_plugin_layer(cfg, postfix='', **kwargs): - """Build plugin layer. - - Args: - cfg (None or dict): cfg should contain: - - - type (str): identify plugin layer type. - - layer args: args needed to instantiate a plugin layer. - postfix (int, str): appended into norm abbreviation to - create named layer. Default: ''. - - Returns: - tuple[str, nn.Module]: The first one is the concatenation of - abbreviation and postfix. The second is the created plugin layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in PLUGIN_LAYERS: - raise KeyError(f'Unrecognized plugin type {layer_type}') - - plugin_layer = PLUGIN_LAYERS.get(layer_type) - abbr = infer_abbr(plugin_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - layer = plugin_layer(**kwargs, **cfg_) - - return name, layer diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/registry.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/registry.py deleted file mode 100644 index c29279776..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/registry.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -CONV_LAYERS = Registry('conv layer') -NORM_LAYERS = Registry('norm layer') -ACTIVATION_LAYERS = Registry('activation layer') -PADDING_LAYERS = Registry('padding layer') -UPSAMPLE_LAYERS = Registry('upsample layer') -PLUGIN_LAYERS = Registry('plugin layer') - -DROPOUT_LAYERS = Registry('drop out layers') -POSITIONAL_ENCODING = Registry('position encoding') -ATTENTION = Registry('attention') -FEEDFORWARD_NETWORK = Registry('feed-forward Network') -TRANSFORMER_LAYER = Registry('transformerLayer') -TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/scale.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/scale.py deleted file mode 100644 index c905fffcc..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/scale.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class Scale(nn.Module): - """A learnable scale parameter. - - This layer scales the input by a learnable factor. It multiplies a - learnable scale parameter of shape (1,) with input of any shape. - - Args: - scale (float): Initial value of scale factor. Default: 1.0 - """ - - def __init__(self, scale=1.0): - super(Scale, self).__init__() - self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) - - def forward(self, x): - return x * self.scale diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/swish.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/swish.py deleted file mode 100644 index e2ca8ed7b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/swish.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class Swish(nn.Module): - """Swish Module. - - This module applies the swish function: - - .. math:: - Swish(x) = x * Sigmoid(x) - - Returns: - Tensor: The output tensor. - """ - - def __init__(self): - super(Swish, self).__init__() - - def forward(self, x): - return x * torch.sigmoid(x) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/transformer.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/transformer.py deleted file mode 100644 index 70c6623c7..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/transformer.py +++ /dev/null @@ -1,944 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings -from typing import Sequence - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.cnn import (Linear, build_activation_layer, build_conv_layer, - build_norm_layer) -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential -from mmcv.utils import (ConfigDict, build_from_cfg, deprecated_api_warning, - to_2tuple) -from .drop import build_dropout -from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, - TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) - -# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file -try: - from mmcv.ops.multi_scale_deform_attn import \ - MultiScaleDeformableAttention # noqa F401 - warnings.warn( - ImportWarning( - '``MultiScaleDeformableAttention`` has been moved to ' - '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 - '``from mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 - 'to ``from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 - )) - -except ImportError: - warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' - '``mmcv.ops.multi_scale_deform_attn``, ' - 'You should install ``mmcv-full`` if you need this module. ') - - -def build_positional_encoding(cfg, default_args=None): - """Builder for Position Encoding.""" - return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) - - -def build_attention(cfg, default_args=None): - """Builder for attention.""" - return build_from_cfg(cfg, ATTENTION, default_args) - - -def build_feedforward_network(cfg, default_args=None): - """Builder for feed-forward network (FFN).""" - return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) - - -def build_transformer_layer(cfg, default_args=None): - """Builder for transformer layer.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) - - -def build_transformer_layer_sequence(cfg, default_args=None): - """Builder for transformer encoder and transformer decoder.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) - - -class AdaptivePadding(nn.Module): - """Applies padding adaptively to the input. - - This module can make input get fully covered by filter - you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad - zero around input. The "corner" mode would pad zero - to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel. Default: 1. - stride (int | tuple): Stride of the filter. Default: 1. - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - super(AdaptivePadding, self).__init__() - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - """Calculate the padding size of input. - - Args: - input_shape (:obj:`torch.Size`): arrange as (H, W). - - Returns: - Tuple[int]: The padding size along the - original H and W directions - """ - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - """Add padding to `x` - - Args: - x (Tensor): Input tensor has shape (B, C, H, W). - - Returns: - Tensor: The tensor with adaptive padding - """ - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The type of convolution - to generate patch embedding. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int): The slide stride of embedding conv. - Default: 16. - padding (int | tuple | string): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only works when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=16, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super(PatchEmbed, self).__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adaptive_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # e.g. when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adaptive_padding: - pad_h, pad_w = self.adaptive_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adaptive_padding: - x = self.adaptive_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map ((used in Swin Transformer)). - Our implementation uses `nn.Unfold` to - merge patches, which is about 25% faster than the original - implementation. However, we need to modify pretrained - models for compatibility. - - Args: - in_channels (int): The num of input channels. - to gets fully covered by filter and stride you specified. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adaptive_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - - if self.adaptive_padding: - x = self.adaptive_padding(x) - H, W = x.shape[-2:] - - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - x = self.sampler(x) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size - - -@ATTENTION.register_module() -class MultiheadAttention(BaseModule): - """A wrapper for ``torch.nn.MultiheadAttention``. - - This module implements MultiheadAttention with identity connection, - and positional encoding is also passed as input. - - Args: - embed_dims (int): The embedding dimension. - num_heads (int): Parallel attention heads. - attn_drop (float): A Dropout layer on attn_output_weights. - Default: 0.0. - proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. - Default: 0.0. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): When it is True, Key, Query and Value are shape of - (batch, n, embed_dim), otherwise (n, batch, embed_dim). - Default to False. - """ - - def __init__(self, - embed_dims, - num_heads, - attn_drop=0., - proj_drop=0., - dropout_layer=dict(type='Dropout', drop_prob=0.), - init_cfg=None, - batch_first=False, - **kwargs): - super(MultiheadAttention, self).__init__(init_cfg) - if 'dropout' in kwargs: - warnings.warn( - 'The arguments `dropout` in MultiheadAttention ' - 'has been deprecated, now you can separately ' - 'set `attn_drop`(float), proj_drop(float), ' - 'and `dropout_layer`(dict) ', DeprecationWarning) - attn_drop = kwargs['dropout'] - dropout_layer['drop_prob'] = kwargs.pop('dropout') - - self.embed_dims = embed_dims - self.num_heads = num_heads - self.batch_first = batch_first - - self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, - **kwargs) - - self.proj_drop = nn.Dropout(proj_drop) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else nn.Identity() - - @deprecated_api_warning({'residual': 'identity'}, - cls_name='MultiheadAttention') - def forward(self, - query, - key=None, - value=None, - identity=None, - query_pos=None, - key_pos=None, - attn_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `MultiheadAttention`. - - **kwargs allow passing a more general data flow when combining - with other operations in `transformerlayer`. - - Args: - query (Tensor): The input query with shape [num_queries, bs, - embed_dims] if self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - If None, the ``query`` will be used. Defaults to None. - value (Tensor): The value tensor with same shape as `key`. - Same in `nn.MultiheadAttention.forward`. Defaults to None. - If None, the `key` will be used. - identity (Tensor): This tensor, with the same shape as x, - will be used for the identity link. - If None, `x` will be used. Defaults to None. - query_pos (Tensor): The positional encoding for query, with - the same shape as `x`. If not None, it will - be added to `x` before forward function. Defaults to None. - key_pos (Tensor): The positional encoding for `key`, with the - same shape as `key`. Defaults to None. If not None, it will - be added to `key` before forward function. If None, and - `query_pos` has the same shape as `key`, then `query_pos` - will be used for `key_pos`. Defaults to None. - attn_mask (Tensor): ByteTensor mask with shape [num_queries, - num_keys]. Same in `nn.MultiheadAttention.forward`. - Defaults to None. - key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. - Defaults to None. - - Returns: - Tensor: forwarded results with shape - [num_queries, bs, embed_dims] - if self.batch_first is False, else - [bs, num_queries embed_dims]. - """ - - if key is None: - key = query - if value is None: - value = key - if identity is None: - identity = query - if key_pos is None: - if query_pos is not None: - # use query_pos if key_pos is not available - if query_pos.shape == key.shape: - key_pos = query_pos - else: - warnings.warn(f'position encoding of key is' - f'missing in {self.__class__.__name__}.') - if query_pos is not None: - query = query + query_pos - if key_pos is not None: - key = key + key_pos - - # Because the dataflow('key', 'query', 'value') of - # ``torch.nn.MultiheadAttention`` is (num_query, batch, - # embed_dims), We should adjust the shape of dataflow from - # batch_first (batch, num_query, embed_dims) to num_query_first - # (num_query ,batch, embed_dims), and recover ``attn_output`` - # from num_query_first to batch_first. - if self.batch_first: - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - out = self.attn( - query=query, - key=key, - value=value, - attn_mask=attn_mask, - key_padding_mask=key_padding_mask)[0] - - if self.batch_first: - out = out.transpose(0, 1) - - return identity + self.dropout_layer(self.proj_drop(out)) - - -@FEEDFORWARD_NETWORK.register_module() -class FFN(BaseModule): - """Implements feed-forward networks (FFNs) with identity connection. - - Args: - embed_dims (int): The feature dimension. Same as - `MultiheadAttention`. Defaults: 256. - feedforward_channels (int): The hidden dimension of FFNs. - Defaults: 1024. - num_fcs (int, optional): The number of fully-connected layers in - FFNs. Default: 2. - act_cfg (dict, optional): The activation config for FFNs. - Default: dict(type='ReLU') - ffn_drop (float, optional): Probability of an element to be - zeroed in FFN. Default 0.0. - add_identity (bool, optional): Whether to add the - identity connection. Default: `True`. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - @deprecated_api_warning( - { - 'dropout': 'ffn_drop', - 'add_residual': 'add_identity' - }, - cls_name='FFN') - def __init__(self, - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - act_cfg=dict(type='ReLU', inplace=True), - ffn_drop=0., - dropout_layer=None, - add_identity=True, - init_cfg=None, - **kwargs): - super(FFN, self).__init__(init_cfg) - assert num_fcs >= 2, 'num_fcs should be no less ' \ - f'than 2. got {num_fcs}.' - self.embed_dims = embed_dims - self.feedforward_channels = feedforward_channels - self.num_fcs = num_fcs - self.act_cfg = act_cfg - self.activate = build_activation_layer(act_cfg) - - layers = [] - in_channels = embed_dims - for _ in range(num_fcs - 1): - layers.append( - Sequential( - Linear(in_channels, feedforward_channels), self.activate, - nn.Dropout(ffn_drop))) - in_channels = feedforward_channels - layers.append(Linear(feedforward_channels, embed_dims)) - layers.append(nn.Dropout(ffn_drop)) - self.layers = Sequential(*layers) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else torch.nn.Identity() - self.add_identity = add_identity - - @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') - def forward(self, x, identity=None): - """Forward function for `FFN`. - - The function would add x to the output tensor if residue is None. - """ - out = self.layers(x) - if not self.add_identity: - return self.dropout_layer(out) - if identity is None: - identity = x - return identity + self.dropout_layer(out) - - -@TRANSFORMER_LAYER.register_module() -class BaseTransformerLayer(BaseModule): - """Base `TransformerLayer` for vision transformer. - - It can be built from `mmcv.ConfigDict` and support more flexible - customization, for example, using any number of `FFN or LN ` and - use different kinds of `attention` by specifying a list of `ConfigDict` - named `attn_cfgs`. It is worth mentioning that it supports `prenorm` - when you specifying `norm` as the first element of `operation_order`. - More details about the `prenorm`: `On Layer Normalization in the - Transformer Architecture `_ . - - Args: - attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for `self_attention` or `cross_attention` modules, - The order of the configs in the list should be consistent with - corresponding attentions in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. Default: None. - ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for FFN, The order of the configs in the list should be - consistent with corresponding ffn in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. - operation_order (tuple[str]): The execution order of operation - in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). - Support `prenorm` when you specifying first element as `norm`. - Default:None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): Key, Query and Value are shape - of (batch, n, embed_dim) - or (n, batch, embed_dim). Default to False. - """ - - def __init__(self, - attn_cfgs=None, - ffn_cfgs=dict( - type='FFN', - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - ffn_drop=0., - act_cfg=dict(type='ReLU', inplace=True), - ), - operation_order=None, - norm_cfg=dict(type='LN'), - init_cfg=None, - batch_first=False, - **kwargs): - - deprecated_args = dict( - feedforward_channels='feedforward_channels', - ffn_dropout='ffn_drop', - ffn_num_fcs='num_fcs') - for ori_name, new_name in deprecated_args.items(): - if ori_name in kwargs: - warnings.warn( - f'The arguments `{ori_name}` in BaseTransformerLayer ' - f'has been deprecated, now you should set `{new_name}` ' - f'and other FFN related arguments ' - f'to a dict named `ffn_cfgs`. ', DeprecationWarning) - ffn_cfgs[new_name] = kwargs[ori_name] - - super(BaseTransformerLayer, self).__init__(init_cfg) - - self.batch_first = batch_first - - assert set(operation_order) & set( - ['self_attn', 'norm', 'ffn', 'cross_attn']) == \ - set(operation_order), f'The operation_order of' \ - f' {self.__class__.__name__} should ' \ - f'contains all four operation type ' \ - f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" - - num_attn = operation_order.count('self_attn') + operation_order.count( - 'cross_attn') - if isinstance(attn_cfgs, dict): - attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] - else: - assert num_attn == len(attn_cfgs), f'The length ' \ - f'of attn_cfg {num_attn} is ' \ - f'not consistent with the number of attention' \ - f'in operation_order {operation_order}.' - - self.num_attn = num_attn - self.operation_order = operation_order - self.norm_cfg = norm_cfg - self.pre_norm = operation_order[0] == 'norm' - self.attentions = ModuleList() - - index = 0 - for operation_name in operation_order: - if operation_name in ['self_attn', 'cross_attn']: - if 'batch_first' in attn_cfgs[index]: - assert self.batch_first == attn_cfgs[index]['batch_first'] - else: - attn_cfgs[index]['batch_first'] = self.batch_first - attention = build_attention(attn_cfgs[index]) - # Some custom attentions used as `self_attn` - # or `cross_attn` can have different behavior. - attention.operation_name = operation_name - self.attentions.append(attention) - index += 1 - - self.embed_dims = self.attentions[0].embed_dims - - self.ffns = ModuleList() - num_ffns = operation_order.count('ffn') - if isinstance(ffn_cfgs, dict): - ffn_cfgs = ConfigDict(ffn_cfgs) - if isinstance(ffn_cfgs, dict): - ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] - assert len(ffn_cfgs) == num_ffns - for ffn_index in range(num_ffns): - if 'embed_dims' not in ffn_cfgs[ffn_index]: - ffn_cfgs[ffn_index]['embed_dims'] = self.embed_dims - else: - assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims - self.ffns.append( - build_feedforward_network(ffn_cfgs[ffn_index], - dict(type='FFN'))) - - self.norms = ModuleList() - num_norms = operation_order.count('norm') - for _ in range(num_norms): - self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) - - def forward(self, - query, - key=None, - value=None, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerDecoderLayer`. - - **kwargs contains some specific arguments of attentions. - - Args: - query (Tensor): The input query with shape - [num_queries, bs, embed_dims] if - self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - value (Tensor): The value tensor with same shape as `key`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor] | None): 2D Tensor used in - calculation of corresponding attention. The length of - it should equal to the number of `attention` in - `operation_order`. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in `self_attn` layer. - Defaults to None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: forwarded results with shape [num_queries, bs, embed_dims]. - """ - - norm_index = 0 - attn_index = 0 - ffn_index = 0 - identity = query - if attn_masks is None: - attn_masks = [None for _ in range(self.num_attn)] - elif isinstance(attn_masks, torch.Tensor): - attn_masks = [ - copy.deepcopy(attn_masks) for _ in range(self.num_attn) - ] - warnings.warn(f'Use same attn_mask in all attentions in ' - f'{self.__class__.__name__} ') - else: - assert len(attn_masks) == self.num_attn, f'The length of ' \ - f'attn_masks {len(attn_masks)} must be equal ' \ - f'to the number of attention in ' \ - f'operation_order {self.num_attn}' - - for layer in self.operation_order: - if layer == 'self_attn': - temp_key = temp_value = query - query = self.attentions[attn_index]( - query, - temp_key, - temp_value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=query_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=query_key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'norm': - query = self.norms[norm_index](query) - norm_index += 1 - - elif layer == 'cross_attn': - query = self.attentions[attn_index]( - query, - key, - value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=key_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'ffn': - query = self.ffns[ffn_index]( - query, identity if self.pre_norm else None) - ffn_index += 1 - - return query - - -@TRANSFORMER_LAYER_SEQUENCE.register_module() -class TransformerLayerSequence(BaseModule): - """Base class for TransformerEncoder and TransformerDecoder in vision - transformer. - - As base-class of Encoder and Decoder in vision transformer. - Support customization such as specifying different kind - of `transformer_layer` in `transformer_coder`. - - Args: - transformerlayer (list[obj:`mmcv.ConfigDict`] | - obj:`mmcv.ConfigDict`): Config of transformerlayer - in TransformerCoder. If it is obj:`mmcv.ConfigDict`, - it would be repeated `num_layer` times to a - list[`mmcv.ConfigDict`]. Default: None. - num_layers (int): The number of `TransformerLayer`. Default: None. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): - super(TransformerLayerSequence, self).__init__(init_cfg) - if isinstance(transformerlayers, dict): - transformerlayers = [ - copy.deepcopy(transformerlayers) for _ in range(num_layers) - ] - else: - assert isinstance(transformerlayers, list) and \ - len(transformerlayers) == num_layers - self.num_layers = num_layers - self.layers = ModuleList() - for i in range(num_layers): - self.layers.append(build_transformer_layer(transformerlayers[i])) - self.embed_dims = self.layers[0].embed_dims - self.pre_norm = self.layers[0].pre_norm - - def forward(self, - query, - key, - value, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerCoder`. - - Args: - query (Tensor): Input query with shape - `(num_queries, bs, embed_dims)`. - key (Tensor): The key tensor with shape - `(num_keys, bs, embed_dims)`. - value (Tensor): The value tensor with shape - `(num_keys, bs, embed_dims)`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor], optional): Each element is 2D Tensor - which is used in calculation of corresponding attention in - operation_order. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in self-attention - Default: None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: results with shape [num_queries, bs, embed_dims]. - """ - for layer in self.layers: - query = layer( - query, - key, - value, - query_pos=query_pos, - key_pos=key_pos, - attn_masks=attn_masks, - query_key_padding_mask=query_key_padding_mask, - key_padding_mask=key_padding_mask, - **kwargs) - return query diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/upsample.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/upsample.py deleted file mode 100644 index 0fd21fbf9..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/upsample.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import xavier_init -from .registry import UPSAMPLE_LAYERS - -UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) -UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) - - -@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') -class PixelShufflePack(nn.Module): - """Pixel Shuffle upsample layer. - - This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to - achieve a simple upsampling with pixel shuffle. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of the conv layer to expand the - channels. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super(PixelShufflePack, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - xavier_init(self.upsample_conv, distribution='uniform') - - def forward(self, x): - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x - - -def build_upsample_layer(cfg, *args, **kwargs): - """Build upsample layer. - - Args: - cfg (dict): The upsample layer config, which should contain: - - - type (str): Layer type. - - scale_factor (int): Upsample ratio, which is not applicable to - deconv. - - layer args: Args needed to instantiate a upsample layer. - args (argument list): Arguments passed to the ``__init__`` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the - ``__init__`` method of the corresponding conv layer. - - Returns: - nn.Module: Created upsample layer. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - raise KeyError( - f'the cfg dict must contain the key "type", but got {cfg}') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in UPSAMPLE_LAYERS: - raise KeyError(f'Unrecognized upsample type {layer_type}') - else: - upsample = UPSAMPLE_LAYERS.get(layer_type) - - if upsample is nn.Upsample: - cfg_['mode'] = layer_type - layer = upsample(*args, **kwargs, **cfg_) - return layer diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/wrappers.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/wrappers.py deleted file mode 100644 index 8aebf67bf..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/bricks/wrappers.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 - -Wrap some nn modules to support empty tensor input. Currently, these wrappers -are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask -heads are trained on only positive RoIs. -""" -import math - -import torch -import torch.nn as nn -from torch.nn.modules.utils import _pair, _triple - -from .registry import CONV_LAYERS, UPSAMPLE_LAYERS - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def obsolete_torch_version(torch_version, version_threshold): - return torch_version == 'parrots' or torch_version <= version_threshold - - -class NewEmptyTensorOp(torch.autograd.Function): - - @staticmethod - def forward(ctx, x, new_shape): - ctx.shape = x.shape - return x.new_empty(new_shape) - - @staticmethod - def backward(ctx, grad): - shape = ctx.shape - return NewEmptyTensorOp.apply(grad, shape), None - - -@CONV_LAYERS.register_module('Conv', force=True) -class Conv2d(nn.Conv2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module('Conv3d', force=True) -class Conv3d(nn.Conv3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv') -@UPSAMPLE_LAYERS.register_module('deconv', force=True) -class ConvTranspose2d(nn.ConvTranspose2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv3d') -@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) -class ConvTranspose3d(nn.ConvTranspose3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -class MaxPool2d(nn.MaxPool2d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), - _pair(self.padding), _pair(self.stride), - _pair(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class MaxPool3d(nn.MaxPool3d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), - _triple(self.padding), - _triple(self.stride), - _triple(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class Linear(torch.nn.Linear): - - def forward(self, x): - # empty tensor forward of Linear layer is supported in Pytorch 1.6 - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): - out_shape = [x.shape[0], self.out_features] - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/builder.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/builder.py deleted file mode 100644 index 7567316c5..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/builder.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..runner import Sequential -from ..utils import Registry, build_from_cfg - - -def build_model_from_cfg(cfg, registry, default_args=None): - """Build a PyTorch model from config dict(s). Different from - ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. - - Args: - cfg (dict, list[dict]): The config of modules, is is either a config - dict or a list of config dicts. If cfg is a list, a - the built modules will be wrapped with ``nn.Sequential``. - registry (:obj:`Registry`): A registry the module belongs to. - default_args (dict, optional): Default arguments to build the module. - Defaults to None. - - Returns: - nn.Module: A built nn module. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return Sequential(*modules) - else: - return build_from_cfg(cfg, registry, default_args) - - -MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/__init__.py deleted file mode 100644 index a263e31c1..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .flops_counter import get_model_complexity_info -from .fuse_conv_bn import fuse_conv_bn -from .sync_bn import revert_sync_batchnorm -from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, - KaimingInit, NormalInit, PretrainedInit, - TruncNormalInit, UniformInit, XavierInit, - bias_init_with_prob, caffe2_xavier_init, - constant_init, initialize, kaiming_init, normal_init, - trunc_normal_init, uniform_init, xavier_init) - -__all__ = [ - 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', - 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', - 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'revert_sync_batchnorm' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/flops_counter.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/flops_counter.py deleted file mode 100644 index a6045db84..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/flops_counter.py +++ /dev/null @@ -1,599 +0,0 @@ -# Modified from flops-counter.pytorch by Vladislav Sovrasov -# original repo: https://github.com/sovrasov/flops-counter.pytorch - -# MIT License - -# Copyright (c) 2018 Vladislav Sovrasov - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -import warnings -from functools import partial - -import numpy as np -import torch -import torch.nn as nn - -import mmcv - - -def get_model_complexity_info(model, - input_shape, - print_per_layer_stat=True, - as_strings=True, - input_constructor=None, - flush=False, - ost=sys.stdout): - """Get complexity information of a model. - - This method can calculate FLOPs and parameter counts of a model with - corresponding input shape. It can also print complexity information for - each layer in a model. - - Supported layers are listed as below: - - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. - - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, - ``nn.LeakyReLU``, ``nn.ReLU6``. - - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, - ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, - ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, - ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, - ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. - - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, - ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, - ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. - - Linear: ``nn.Linear``. - - Deconvolution: ``nn.ConvTranspose2d``. - - Upsample: ``nn.Upsample``. - - Args: - model (nn.Module): The model for complexity calculation. - input_shape (tuple): Input shape used for calculation. - print_per_layer_stat (bool): Whether to print complexity information - for each layer in a model. Default: True. - as_strings (bool): Output FLOPs and params counts in a string form. - Default: True. - input_constructor (None | callable): If specified, it takes a callable - method that generates input. otherwise, it will generate a random - tensor with input shape to calculate FLOPs. Default: None. - flush (bool): same as that in :func:`print`. Default: False. - ost (stream): same as ``file`` param in :func:`print`. - Default: sys.stdout. - - Returns: - tuple[float | str]: If ``as_strings`` is set to True, it will return - FLOPs and parameter counts in a string format. otherwise, it will - return those in a float number format. - """ - assert type(input_shape) is tuple - assert len(input_shape) >= 1 - assert isinstance(model, nn.Module) - flops_model = add_flops_counting_methods(model) - flops_model.eval() - flops_model.start_flops_count() - if input_constructor: - input = input_constructor(input_shape) - _ = flops_model(**input) - else: - try: - batch = torch.ones(()).new_empty( - (1, *input_shape), - dtype=next(flops_model.parameters()).dtype, - device=next(flops_model.parameters()).device) - except StopIteration: - # Avoid StopIteration for models which have no parameters, - # like `nn.Relu()`, `nn.AvgPool2d`, etc. - batch = torch.ones(()).new_empty((1, *input_shape)) - - _ = flops_model(batch) - - flops_count, params_count = flops_model.compute_average_flops_cost() - if print_per_layer_stat: - print_model_with_flops( - flops_model, flops_count, params_count, ost=ost, flush=flush) - flops_model.stop_flops_count() - - if as_strings: - return flops_to_string(flops_count), params_to_string(params_count) - - return flops_count, params_count - - -def flops_to_string(flops, units='GFLOPs', precision=2): - """Convert FLOPs number into a string. - - Note that Here we take a multiply-add counts as one FLOP. - - Args: - flops (float): FLOPs number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', - 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically - choose the most suitable unit for FLOPs. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted FLOPs number with units. - - Examples: - >>> flops_to_string(1e9) - '1.0 GFLOPs' - >>> flops_to_string(2e5, 'MFLOPs') - '0.2 MFLOPs' - >>> flops_to_string(3e-9, None) - '3e-09 FLOPs' - """ - if units is None: - if flops // 10**9 > 0: - return str(round(flops / 10.**9, precision)) + ' GFLOPs' - elif flops // 10**6 > 0: - return str(round(flops / 10.**6, precision)) + ' MFLOPs' - elif flops // 10**3 > 0: - return str(round(flops / 10.**3, precision)) + ' KFLOPs' - else: - return str(flops) + ' FLOPs' - else: - if units == 'GFLOPs': - return str(round(flops / 10.**9, precision)) + ' ' + units - elif units == 'MFLOPs': - return str(round(flops / 10.**6, precision)) + ' ' + units - elif units == 'KFLOPs': - return str(round(flops / 10.**3, precision)) + ' ' + units - else: - return str(flops) + ' FLOPs' - - -def params_to_string(num_params, units=None, precision=2): - """Convert parameter number into a string. - - Args: - num_params (float): Parameter number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'M', - 'K' and ''. If set to None, it will automatically choose the most - suitable unit for Parameter number. Default: None. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted parameter number with units. - - Examples: - >>> params_to_string(1e9) - '1000.0 M' - >>> params_to_string(2e5) - '200.0 k' - >>> params_to_string(3e-9) - '3e-09' - """ - if units is None: - if num_params // 10**6 > 0: - return str(round(num_params / 10**6, precision)) + ' M' - elif num_params // 10**3: - return str(round(num_params / 10**3, precision)) + ' k' - else: - return str(num_params) - else: - if units == 'M': - return str(round(num_params / 10.**6, precision)) + ' ' + units - elif units == 'K': - return str(round(num_params / 10.**3, precision)) + ' ' + units - else: - return str(num_params) - - -def print_model_with_flops(model, - total_flops, - total_params, - units='GFLOPs', - precision=3, - ost=sys.stdout, - flush=False): - """Print a model with FLOPs for each layer. - - Args: - model (nn.Module): The model to be printed. - total_flops (float): Total FLOPs of the model. - total_params (float): Total parameter counts of the model. - units (str | None): Converted FLOPs units. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 3. - ost (stream): same as `file` param in :func:`print`. - Default: sys.stdout. - flush (bool): same as that in :func:`print`. Default: False. - - Example: - >>> class ExampleModel(nn.Module): - - >>> def __init__(self): - >>> super().__init__() - >>> self.conv1 = nn.Conv2d(3, 8, 3) - >>> self.conv2 = nn.Conv2d(8, 256, 3) - >>> self.conv3 = nn.Conv2d(256, 8, 3) - >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) - >>> self.flatten = nn.Flatten() - >>> self.fc = nn.Linear(8, 1) - - >>> def forward(self, x): - >>> x = self.conv1(x) - >>> x = self.conv2(x) - >>> x = self.conv3(x) - >>> x = self.avg_pool(x) - >>> x = self.flatten(x) - >>> x = self.fc(x) - >>> return x - - >>> model = ExampleModel() - >>> x = (3, 16, 16) - to print the complexity information state for each layer, you can use - >>> get_model_complexity_info(model, x) - or directly use - >>> print_model_with_flops(model, 4579784.0, 37361) - ExampleModel( - 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, - (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 - (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) - (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) - (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) - (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) - (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) - ) - """ - - def accumulate_params(self): - if is_supported_instance(self): - return self.__params__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_params() - return sum - - def accumulate_flops(self): - if is_supported_instance(self): - return self.__flops__ / model.__batch_counter__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_flops() - return sum - - def flops_repr(self): - accumulated_num_params = self.accumulate_params() - accumulated_flops_cost = self.accumulate_flops() - return ', '.join([ - params_to_string( - accumulated_num_params, units='M', precision=precision), - '{:.3%} Params'.format(accumulated_num_params / total_params), - flops_to_string( - accumulated_flops_cost, units=units, precision=precision), - '{:.3%} FLOPs'.format(accumulated_flops_cost / total_flops), - self.original_extra_repr() - ]) - - def add_extra_repr(m): - m.accumulate_flops = accumulate_flops.__get__(m) - m.accumulate_params = accumulate_params.__get__(m) - flops_extra_repr = flops_repr.__get__(m) - if m.extra_repr != flops_extra_repr: - m.original_extra_repr = m.extra_repr - m.extra_repr = flops_extra_repr - assert m.extra_repr != m.original_extra_repr - - def del_extra_repr(m): - if hasattr(m, 'original_extra_repr'): - m.extra_repr = m.original_extra_repr - del m.original_extra_repr - if hasattr(m, 'accumulate_flops'): - del m.accumulate_flops - - model.apply(add_extra_repr) - print(model, file=ost, flush=flush) - model.apply(del_extra_repr) - - -def get_model_parameters_number(model): - """Calculate parameter number of a model. - - Args: - model (nn.module): The model for parameter number calculation. - - Returns: - float: Parameter number of the model. - """ - num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - return num_params - - -def add_flops_counting_methods(net_main_module): - # adding additional methods to the existing module object, - # this is done this way so that each function has access to self object - net_main_module.start_flops_count = start_flops_count.__get__( - net_main_module) - net_main_module.stop_flops_count = stop_flops_count.__get__( - net_main_module) - net_main_module.reset_flops_count = reset_flops_count.__get__( - net_main_module) - net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # noqa: E501 - net_main_module) - - net_main_module.reset_flops_count() - - return net_main_module - - -def compute_average_flops_cost(self): - """Compute average FLOPs cost. - - A method to compute average FLOPs cost, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - - Returns: - float: Current mean flops consumption per image. - """ - batches_count = self.__batch_counter__ - flops_sum = 0 - for module in self.modules(): - if is_supported_instance(module): - flops_sum += module.__flops__ - params_sum = get_model_parameters_number(self) - return flops_sum / batches_count, params_sum - - -def start_flops_count(self): - """Activate the computation of mean flops consumption per image. - - A method to activate the computation of mean flops consumption per image. - which will be available after ``add_flops_counting_methods()`` is called on - a desired net object. It should be called before running the network. - """ - add_batch_counter_hook_function(self) - - def add_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - return - - else: - handle = module.register_forward_hook( - get_modules_mapping()[type(module)]) - - module.__flops_handle__ = handle - - self.apply(partial(add_flops_counter_hook_function)) - - -def stop_flops_count(self): - """Stop computing the mean flops consumption per image. - - A method to stop computing the mean flops consumption per image, which will - be available after ``add_flops_counting_methods()`` is called on a desired - net object. It can be called to pause the computation whenever. - """ - remove_batch_counter_hook_function(self) - self.apply(remove_flops_counter_hook_function) - - -def reset_flops_count(self): - """Reset statistics computed so far. - - A method to Reset computed statistics, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - """ - add_batch_counter_variables_or_reset(self) - self.apply(add_flops_counter_variable_or_reset) - - -# ---- Internal functions -def empty_flops_counter_hook(module, input, output): - module.__flops__ += 0 - - -def upsample_flops_counter_hook(module, input, output): - output_size = output[0] - batch_size = output_size.shape[0] - output_elements_count = batch_size - for val in output_size.shape[1:]: - output_elements_count *= val - module.__flops__ += int(output_elements_count) - - -def relu_flops_counter_hook(module, input, output): - active_elements_count = output.numel() - module.__flops__ += int(active_elements_count) - - -def linear_flops_counter_hook(module, input, output): - input = input[0] - output_last_dim = output.shape[ - -1] # pytorch checks dimensions, so here we don't care much - module.__flops__ += int(np.prod(input.shape) * output_last_dim) - - -def pool_flops_counter_hook(module, input, output): - input = input[0] - module.__flops__ += int(np.prod(input.shape)) - - -def norm_flops_counter_hook(module, input, output): - input = input[0] - - batch_flops = np.prod(input.shape) - if (getattr(module, 'affine', False) - or getattr(module, 'elementwise_affine', False)): - batch_flops *= 2 - module.__flops__ += int(batch_flops) - - -def deconv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - input_height, input_width = input.shape[2:] - - kernel_height, kernel_width = conv_module.kernel_size - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = ( - kernel_height * kernel_width * in_channels * filters_per_channel) - - active_elements_count = batch_size * input_height * input_width - overall_conv_flops = conv_per_position_flops * active_elements_count - bias_flops = 0 - if conv_module.bias is not None: - output_height, output_width = output.shape[2:] - bias_flops = out_channels * batch_size * output_height * output_width - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def conv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - output_dims = list(output.shape[2:]) - - kernel_dims = list(conv_module.kernel_size) - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = int( - np.prod(kernel_dims)) * in_channels * filters_per_channel - - active_elements_count = batch_size * int(np.prod(output_dims)) - - overall_conv_flops = conv_per_position_flops * active_elements_count - - bias_flops = 0 - - if conv_module.bias is not None: - - bias_flops = out_channels * active_elements_count - - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def batch_counter_hook(module, input, output): - batch_size = 1 - if len(input) > 0: - # Can have multiple inputs, getting the first one - input = input[0] - batch_size = len(input) - else: - warnings.warn('No positional inputs found for a module, ' - 'assuming batch size is 1.') - module.__batch_counter__ += batch_size - - -def add_batch_counter_variables_or_reset(module): - - module.__batch_counter__ = 0 - - -def add_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - return - - handle = module.register_forward_hook(batch_counter_hook) - module.__batch_counter_handle__ = handle - - -def remove_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - module.__batch_counter_handle__.remove() - del module.__batch_counter_handle__ - - -def add_flops_counter_variable_or_reset(module): - if is_supported_instance(module): - if hasattr(module, '__flops__') or hasattr(module, '__params__'): - warnings.warn('variables __flops__ or __params__ are already ' - 'defined for the module' + type(module).__name__ + - ' ptflops can affect your code!') - module.__flops__ = 0 - module.__params__ = get_model_parameters_number(module) - - -def is_supported_instance(module): - if type(module) in get_modules_mapping(): - return True - return False - - -def remove_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - module.__flops_handle__.remove() - del module.__flops_handle__ - - -def get_modules_mapping(): - return { - # convolutions - nn.Conv1d: conv_flops_counter_hook, - nn.Conv2d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, - nn.Conv3d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, - # activations - nn.ReLU: relu_flops_counter_hook, - nn.PReLU: relu_flops_counter_hook, - nn.ELU: relu_flops_counter_hook, - nn.LeakyReLU: relu_flops_counter_hook, - nn.ReLU6: relu_flops_counter_hook, - # poolings - nn.MaxPool1d: pool_flops_counter_hook, - nn.AvgPool1d: pool_flops_counter_hook, - nn.AvgPool2d: pool_flops_counter_hook, - nn.MaxPool2d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, - nn.MaxPool3d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, - nn.AvgPool3d: pool_flops_counter_hook, - nn.AdaptiveMaxPool1d: pool_flops_counter_hook, - nn.AdaptiveAvgPool1d: pool_flops_counter_hook, - nn.AdaptiveMaxPool2d: pool_flops_counter_hook, - nn.AdaptiveAvgPool2d: pool_flops_counter_hook, - nn.AdaptiveMaxPool3d: pool_flops_counter_hook, - nn.AdaptiveAvgPool3d: pool_flops_counter_hook, - # normalizations - nn.BatchNorm1d: norm_flops_counter_hook, - nn.BatchNorm2d: norm_flops_counter_hook, - nn.BatchNorm3d: norm_flops_counter_hook, - nn.GroupNorm: norm_flops_counter_hook, - nn.InstanceNorm1d: norm_flops_counter_hook, - nn.InstanceNorm2d: norm_flops_counter_hook, - nn.InstanceNorm3d: norm_flops_counter_hook, - nn.LayerNorm: norm_flops_counter_hook, - # FC - nn.Linear: linear_flops_counter_hook, - mmcv.cnn.bricks.Linear: linear_flops_counter_hook, - # Upscale - nn.Upsample: upsample_flops_counter_hook, - # Deconvolution - nn.ConvTranspose2d: deconv_flops_counter_hook, - mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, - } diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py deleted file mode 100644 index cb7076f80..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def _fuse_conv_bn(conv, bn): - """Fuse conv and bn into one module. - - Args: - conv (nn.Module): Conv to be fused. - bn (nn.Module): BN to be fused. - - Returns: - nn.Module: Fused module. - """ - conv_w = conv.weight - conv_b = conv.bias if conv.bias is not None else torch.zeros_like( - bn.running_mean) - - factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) - conv.weight = nn.Parameter(conv_w * - factor.reshape([conv.out_channels, 1, 1, 1])) - conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) - return conv - - -def fuse_conv_bn(module): - """Recursively fuse conv and bn in a module. - - During inference, the functionary of batch norm layers is turned off - but only the mean and var alone channels are used, which exposes the - chance to fuse it with the preceding conv layers to save computations and - simplify network structures. - - Args: - module (nn.Module): Module to be fused. - - Returns: - nn.Module: Fused module. - """ - last_conv = None - last_conv_name = None - - for name, child in module.named_children(): - if isinstance(child, - (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): - if last_conv is None: # only fuse BN that is after Conv - continue - fused_conv = _fuse_conv_bn(last_conv, child) - module._modules[last_conv_name] = fused_conv - # To reduce changes, set BN as Identity instead of deleting it. - module._modules[name] = nn.Identity() - last_conv = None - elif isinstance(child, nn.Conv2d): - last_conv = child - last_conv_name = name - else: - fuse_conv_bn(child) - return module diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/sync_bn.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/sync_bn.py deleted file mode 100644 index 0c52526e9..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/sync_bn.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -import mmcv - - -class _BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): - """A general BatchNorm layer without input dimension check. - - Reproduced from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc - is `_check_input_dim` that is designed for tensor sanity checks. - The check has been bypassed in this class for the convenience of converting - SyncBatchNorm. - """ - - def _check_input_dim(self, input): - return - - -def revert_sync_batchnorm(module): - """Helper function to convert all `SyncBatchNorm` (SyncBN) and - `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to - `BatchNormXd` layers. - - Adapted from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - - Args: - module (nn.Module): The module containing `SyncBatchNorm` layers. - - Returns: - module_output: The converted module with `BatchNormXd` layers. - """ - module_output = module - module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] - if hasattr(mmcv, 'ops'): - module_checklist.append(mmcv.ops.SyncBatchNorm) - if isinstance(module, tuple(module_checklist)): - module_output = _BatchNormXd(module.num_features, module.eps, - module.momentum, module.affine, - module.track_running_stats) - if module.affine: - # no_grad() may not be needed here but - # just to be consistent with `convert_sync_batchnorm()` - with torch.no_grad(): - module_output.weight = module.weight - module_output.bias = module.bias - module_output.running_mean = module.running_mean - module_output.running_var = module.running_var - module_output.num_batches_tracked = module.num_batches_tracked - module_output.training = module.training - # qconfig exists in quantized models - if hasattr(module, 'qconfig'): - module_output.qconfig = module.qconfig - for name, child in module.named_children(): - module_output.add_module(name, revert_sync_batchnorm(child)) - del module - return module_output diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/weight_init.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/weight_init.py deleted file mode 100644 index 0ac08c87f..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/cnn/utils/weight_init.py +++ /dev/null @@ -1,685 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings - -import numpy as np -import torch -import torch.nn as nn -from torch import Tensor - -from mmcv.utils import Registry, build_from_cfg, get_logger, print_log - -INITIALIZERS = Registry('initializer') - - -def update_init_info(module, init_info): - """Update the `_params_init_info` in the module if the value of parameters - are changed. - - Args: - module (obj:`nn.Module`): The module of PyTorch with a user-defined - attribute `_params_init_info` which records the initialization - information. - init_info (str): The string that describes the initialization. - """ - assert hasattr( - module, - '_params_init_info'), f'Can not find `_params_init_info` in {module}' - for name, param in module.named_parameters(): - - assert param in module._params_init_info, ( - f'Find a new :obj:`Parameter` ' - f'named `{name}` during executing the ' - f'`init_weights` of ' - f'`{module.__class__.__name__}`. ' - f'Please do not add or ' - f'replace parameters during executing ' - f'the `init_weights`. ') - - # The parameter has been changed during executing the - # `init_weights` of module - mean_value = param.data.mean() - if module._params_init_info[param]['tmp_mean_value'] != mean_value: - module._params_init_info[param]['init_info'] = init_info - module._params_init_info[param]['tmp_mean_value'] = mean_value - - -def constant_init(module, val, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.constant_(module.weight, val) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def xavier_init(module, gain=1, bias=0, distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.xavier_uniform_(module.weight, gain=gain) - else: - nn.init.xavier_normal_(module.weight, gain=gain) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def normal_init(module, mean=0, std=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.normal_(module.weight, mean, std) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def trunc_normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - trunc_normal_(module.weight, mean, std, a, b) # type: ignore - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) # type: ignore - - -def uniform_init(module, a=0, b=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.uniform_(module.weight, a, b) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def kaiming_init(module, - a=0, - mode='fan_out', - nonlinearity='relu', - bias=0, - distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.kaiming_uniform_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - else: - nn.init.kaiming_normal_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def caffe2_xavier_init(module, bias=0): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - kaiming_init( - module, - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - bias=bias, - distribution='uniform') - - -def bias_init_with_prob(prior_prob): - """initialize conv/fc bias value according to a given probability value.""" - bias_init = float(-np.log((1 - prior_prob) / prior_prob)) - return bias_init - - -def _get_bases_name(m): - return [b.__name__ for b in m.__class__.__bases__] - - -class BaseInit(object): - - def __init__(self, *, bias=0, bias_prob=None, layer=None): - self.wholemodule = False - if not isinstance(bias, (int, float)): - raise TypeError(f'bias must be a number, but got a {type(bias)}') - - if bias_prob is not None: - if not isinstance(bias_prob, float): - raise TypeError(f'bias_prob type must be float, \ - but got {type(bias_prob)}') - - if layer is not None: - if not isinstance(layer, (str, list)): - raise TypeError(f'layer must be a str or a list of str, \ - but got a {type(layer)}') - else: - layer = [] - - if bias_prob is not None: - self.bias = bias_init_with_prob(bias_prob) - else: - self.bias = bias - self.layer = [layer] if isinstance(layer, str) else layer - - def _get_init_info(self): - info = f'{self.__class__.__name__}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Constant') -class ConstantInit(BaseInit): - """Initialize module parameters with constant values. - - Args: - val (int | float): the value to fill the weights in the module with - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, val, **kwargs): - super().__init__(**kwargs) - self.val = val - - def __call__(self, module): - - def init(m): - if self.wholemodule: - constant_init(m, self.val, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - constant_init(m, self.val, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Xavier') -class XavierInit(BaseInit): - r"""Initialize module parameters with values according to the method - described in `Understanding the difficulty of training deep feedforward - neural networks - Glorot, X. & Bengio, Y. (2010). - `_ - - Args: - gain (int | float): an optional scaling factor. Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` - or ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, gain=1, distribution='normal', **kwargs): - super().__init__(**kwargs) - self.gain = gain - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - xavier_init(m, self.gain, self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - xavier_init(m, self.gain, self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: gain={self.gain}, ' \ - f'distribution={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Normal') -class NormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. - - Args: - mean (int | float):the mean of the normal distribution. Defaults to 0. - std (int | float): the standard deviation of the normal distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, mean=0, std=1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.std = std - - def __call__(self, module): - - def init(m): - if self.wholemodule: - normal_init(m, self.mean, self.std, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - normal_init(m, self.mean, self.std, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: mean={self.mean},' \ - f' std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='TruncNormal') -class TruncNormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values - outside :math:`[a, b]`. - - Args: - mean (float): the mean of the normal distribution. Defaults to 0. - std (float): the standard deviation of the normal distribution. - Defaults to 1. - a (float): The minimum cutoff value. - b ( float): The maximum cutoff value. - bias (float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - **kwargs) -> None: - super().__init__(**kwargs) - self.mean = mean - self.std = std - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ - f' mean={self.mean}, std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Uniform') -class UniformInit(BaseInit): - r"""Initialize module parameters with values drawn from the uniform - distribution :math:`\mathcal{U}(a, b)`. - - Args: - a (int | float): the lower bound of the uniform distribution. - Defaults to 0. - b (int | float): the upper bound of the uniform distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, a=0, b=1, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def __call__(self, module): - - def init(m): - if self.wholemodule: - uniform_init(m, self.a, self.b, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - uniform_init(m, self.a, self.b, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a},' \ - f' b={self.b}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Kaiming') -class KaimingInit(BaseInit): - r"""Initialize module parameters with the values according to the method - described in `Delving deep into rectifiers: Surpassing human-level - performance on ImageNet classification - He, K. et al. (2015). - `_ - - Args: - a (int | float): the negative slope of the rectifier used after this - layer (only used with ``'leaky_relu'``). Defaults to 0. - mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing - ``'fan_in'`` preserves the magnitude of the variance of the weights - in the forward pass. Choosing ``'fan_out'`` preserves the - magnitudes in the backwards pass. Defaults to ``'fan_out'``. - nonlinearity (str): the non-linear function (`nn.functional` name), - recommended to use only with ``'relu'`` or ``'leaky_relu'`` . - Defaults to 'relu'. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` or - ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - a=0, - mode='fan_out', - nonlinearity='relu', - distribution='normal', - **kwargs): - super().__init__(**kwargs) - self.a = a - self.mode = mode - self.nonlinearity = nonlinearity - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ - f'nonlinearity={self.nonlinearity}, ' \ - f'distribution ={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Caffe2Xavier') -class Caffe2XavierInit(KaimingInit): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - def __init__(self, **kwargs): - super().__init__( - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='uniform', - **kwargs) - - def __call__(self, module): - super().__call__(module) - - -@INITIALIZERS.register_module(name='Pretrained') -class PretrainedInit(object): - """Initialize module by loading a pretrained model. - - Args: - checkpoint (str): the checkpoint file of the pretrained model should - be load. - prefix (str, optional): the prefix of a sub-module in the pretrained - model. it is for loading a part of the pretrained model to - initialize. For example, if we would like to only load the - backbone of a detector model, we can set ``prefix='backbone.'``. - Defaults to None. - map_location (str): map tensors into proper locations. - """ - - def __init__(self, checkpoint, prefix=None, map_location=None): - self.checkpoint = checkpoint - self.prefix = prefix - self.map_location = map_location - - def __call__(self, module): - from mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, - load_state_dict) - logger = get_logger('mmcv') - if self.prefix is None: - print_log(f'load model from: {self.checkpoint}', logger=logger) - load_checkpoint( - module, - self.checkpoint, - map_location=self.map_location, - strict=False, - logger=logger) - else: - print_log( - f'load {self.prefix} in model from: {self.checkpoint}', - logger=logger) - state_dict = _load_checkpoint_with_prefix( - self.prefix, self.checkpoint, map_location=self.map_location) - load_state_dict(module, state_dict, strict=False, logger=logger) - - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: load from {self.checkpoint}' - return info - - -def _initialize(module, cfg, wholemodule=False): - func = build_from_cfg(cfg, INITIALIZERS) - # wholemodule flag is for override mode, there is no layer key in override - # and initializer will give init values for the whole module with the name - # in override. - func.wholemodule = wholemodule - func(module) - - -def _initialize_override(module, override, cfg): - if not isinstance(override, (dict, list)): - raise TypeError(f'override must be a dict or a list of dict, \ - but got {type(override)}') - - override = [override] if isinstance(override, dict) else override - - for override_ in override: - - cp_override = copy.deepcopy(override_) - name = cp_override.pop('name', None) - if name is None: - raise ValueError('`override` must contain the key "name",' - f'but got {cp_override}') - # if override only has name key, it means use args in init_cfg - if not cp_override: - cp_override.update(cfg) - # if override has name key and other args except type key, it will - # raise error - elif 'type' not in cp_override.keys(): - raise ValueError( - f'`override` need "type" key, but got {cp_override}') - - if hasattr(module, name): - _initialize(getattr(module, name), cp_override, wholemodule=True) - else: - raise RuntimeError(f'module did not have attribute {name}, ' - f'but init_cfg is {cp_override}.') - - -def initialize(module, init_cfg): - r"""Initialize a module. - - Args: - module (``torch.nn.Module``): the module will be initialized. - init_cfg (dict | list[dict]): initialization configuration dict to - define initializer. OpenMMLab has implemented 6 initializers - including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, - ``Kaiming``, and ``Pretrained``. - - Example: - >>> module = nn.Linear(2, 3, bias=True) - >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) - >>> initialize(module, init_cfg) - - >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) - >>> # define key ``'layer'`` for initializing layer with different - >>> # configuration - >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), - dict(type='Constant', layer='Linear', val=2)] - >>> initialize(module, init_cfg) - - >>> # define key``'override'`` to initialize some specific part in - >>> # module - >>> class FooNet(nn.Module): - >>> def __init__(self): - >>> super().__init__() - >>> self.feat = nn.Conv2d(3, 16, 3) - >>> self.reg = nn.Conv2d(16, 10, 3) - >>> self.cls = nn.Conv2d(16, 5, 3) - >>> model = FooNet() - >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', - >>> override=dict(type='Constant', name='reg', val=3, bias=4)) - >>> initialize(model, init_cfg) - - >>> model = ResNet(depth=50) - >>> # Initialize weights with the pretrained model. - >>> init_cfg = dict(type='Pretrained', - checkpoint='torchvision://resnet50') - >>> initialize(model, init_cfg) - - >>> # Initialize weights of a sub-module with the specific part of - >>> # a pretrained model by using "prefix". - >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ - >>> 'retinanet_r50_fpn_1x_coco/'\ - >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' - >>> init_cfg = dict(type='Pretrained', - checkpoint=url, prefix='backbone.') - """ - if not isinstance(init_cfg, (dict, list)): - raise TypeError(f'init_cfg must be a dict or a list of dict, \ - but got {type(init_cfg)}') - - if isinstance(init_cfg, dict): - init_cfg = [init_cfg] - - for cfg in init_cfg: - # should deeply copy the original config because cfg may be used by - # other modules, e.g., one init_cfg shared by multiple bottleneck - # blocks, the expected cfg will be changed after pop and will change - # the initialization behavior of other modules - cp_cfg = copy.deepcopy(cfg) - override = cp_cfg.pop('override', None) - _initialize(module, cp_cfg) - - if override is not None: - cp_cfg.pop('layer', None) - _initialize_override(module, override, cp_cfg) - else: - # All attributes in module have same initialization. - pass - - -def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, - b: float) -> Tensor: - # Method based on - # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf - # Modified from - # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - def norm_cdf(x): - # Computes standard normal cumulative distribution function - return (1. + math.erf(x / math.sqrt(2.))) / 2. - - if (mean < a - 2 * std) or (mean > b + 2 * std): - warnings.warn( - 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' - 'The distribution of values may be incorrect.', - stacklevel=2) - - with torch.no_grad(): - # Values are generated by using a truncated uniform distribution and - # then using the inverse CDF for the normal distribution. - # Get upper and lower cdf values - lower = norm_cdf((a - mean) / std) - upper = norm_cdf((b - mean) / std) - - # Uniformly fill tensor with values from [lower, upper], then translate - # to [2lower-1, 2upper-1]. - tensor.uniform_(2 * lower - 1, 2 * upper - 1) - - # Use inverse cdf transform for normal distribution to get truncated - # standard normal - tensor.erfinv_() - - # Transform to proper mean, std - tensor.mul_(std * math.sqrt(2.)) - tensor.add_(mean) - - # Clamp to ensure it's in the proper range - tensor.clamp_(min=a, max=b) - return tensor - - -def trunc_normal_(tensor: Tensor, - mean: float = 0., - std: float = 1., - a: float = -2., - b: float = 2.) -> Tensor: - r"""Fills the input Tensor with values drawn from a truncated - normal distribution. The values are effectively drawn from the - normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` - with values outside :math:`[a, b]` redrawn until they are within - the bounds. The method used for generating the random values works - best when :math:`a \leq \text{mean} \leq b`. - - Modified from - https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - - Args: - tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. - mean (float): the mean of the normal distribution. - std (float): the standard deviation of the normal distribution. - a (float): the minimum cutoff value. - b (float): the maximum cutoff value. - """ - return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/__init__.py deleted file mode 100644 index 6ac55e63b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from . import ipu, mlu - -__all__ = ['mlu', 'ipu'] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/__init__.py deleted file mode 100755 index d550865ad..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import IPUFp16OptimizerHook - from .model_wrapper import ipu_model_wrapper - from .runner import IPUBaseRunner, IPUEpochBasedRunner, IPUIterBasedRunner - from .utils import cfg2options - __all__ = [ - 'cfg2options', 'ipu_model_wrapper', 'IPUFp16OptimizerHook', - 'IPUDataLoader', 'IPUBaseRunner', 'IPUEpochBasedRunner', - 'IPUIterBasedRunner' - ] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/dataloader.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/dataloader.py deleted file mode 100755 index 1485df2f3..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/dataloader.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence -from functools import partial - -import poptorch -from torch.utils.data.dataloader import default_collate - -from mmcv.parallel import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Put each data field into a tensor/DataContainer with outer dimension - batch size. - - TODO support for - :type:`~mmcv.parallel.DataContainer`. Currently, it will be ignored. - There are 3 cases. - - 1. cpu_only = True, e.g., meta data. - 2. cpu_only = False, stack = True, e.g., images tensors. - 3. cpu_only = False, stack = False, e.g., gt bboxes. - """ - - if not isinstance(batch, Sequence): - raise TypeError( - f'`batch` should be a sequence, but got {type(batch)}.') - - if isinstance(batch[0], DataContainer): - # TODO `DataContainer` will be supported in the future. - raise TypeError('DataContainer is not supported in ipu data loader.') - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - collated_batch = [] - for samples in transposed: - if not isinstance(samples[0], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch.append(collate(samples, samples_per_gpu)) - return collated_batch - elif isinstance(batch[0], Mapping): - collated_batch = {} - for key in batch[0]: - if not isinstance(batch[0][key], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch[key] = collate([d[key] for d in batch]) - return collated_batch - else: - return default_collate(batch) - - -class IPUDataLoader(poptorch.DataLoader): - """Thin wrapper of `torch.utils.data.DataLoader`. - - Compared with the pytorch DataLoder, this DataLoder changes the way of - calculation of batch size and adds the AsynchronousDataAccessor to - load and release data faster in cpu mode. - - If this data loader is used in a distributed execution environment, it will - ensure that each process uses a different subset of the dataset, providing - you first call ``options.randomSeed(N)`` with an integer N which is the - same across all hosts. - - Args: - dataset (torch.utils.data.Dataset): The dataset to get the data from. - options (poptorch.Options): Options that will be used to compile - and run the model. - batch_size (int, optional): This is the batch size in the conventional - sense of being the size that runs through an operation in the model - at any given time. - shuffle (bool, optional): set to ``True`` to have the data reshuffled - at every epoch (default: ``False``). - num_workers (int, optional): how many subprocesses to use for data - loading. ``0`` means that the data will be loaded in the main - process. (default: ``0``) - drop_last (bool, optional): If True and the number of elements in the - dataset is not a multiple of the combined batch size then the - incomplete batch at the end will be dropped. - persistent_workers (bool, optional): Re-use workers between - iterations if True. - auto_distributed_partitioning (bool, optional): If True, partitions the - dataset for distributed execution automatically. Otherwise, it is - assumed that partitioning has been handled manually. - mode (poptorch.DataLoaderMode, optional): If `DataLoaderMode.Async`, - uses an :py:class:`~poptorch.AsynchronousDataAccessor` to access - the dataset. If `DataLoaderMode.Sync`, accesses the dataset - synchronously. - async_options (Dict[str, Any], optional): Options to pass to - :py:class:`~poptorch.AsynchronousDataAccessor`. - rebatched_worker_size (int, optional): When using AsyncRebatched: batch - size of the tensors loaded by the workers. - Default to the combined batch size. - If specified the ``rebatched_worker_size`` must be less than - or equal to the combined batch size. - kwargs (Dict[str, Any], optional): Other options to pass to PyTorch's - ``DataLoader`` constructor. - """ - - def __init__(self, - dataset, - options, - batch_size=1, - shuffle=False, - num_workers=0, - drop_last=True, - persistent_workers=True, - auto_distributed_partitioning=True, - mode='sync', - async_options=None, - rebatched_worker_size=None, - **kwargs): - """Lazy init: - - In many frameworks, the dataloader will be constructed before the - initialization of the ipu options, so the lazy init method is used - here, and the real initialization will not be done until the dataloader - needs to be used and the options are input. - """ - # lazy init: sometimes, we cannot get IPU options when build data - # loader - self.kwargs = { - 'dataset': dataset, - 'batch_size': batch_size, - 'shuffle': shuffle, - 'num_workers': num_workers, - 'drop_last': drop_last, - 'persistent_workers': persistent_workers, - 'auto_distributed_partitioning': auto_distributed_partitioning, - 'mode': mode, - 'collate_fn': partial(collate, samples_per_gpu=batch_size), - 'async_options': async_options, - 'rebatched_worker_size': rebatched_worker_size, - **kwargs - } - self.dataset = dataset - self.initialized = False - if options: - self.init(options=options) - - def init(self, options, **kwargs): - if not self.initialized: - kwargs = {**self.kwargs, **kwargs, 'options': options} - if kwargs['mode'] == 'sync': - kwargs['mode'] = poptorch.DataLoaderMode.Sync - elif kwargs['mode'] == 'async': - kwargs['mode'] = poptorch.DataLoaderMode.AsyncRebatched - if kwargs['async_options'] is None: - kwargs['async_options'] = { - 'load_indefinitely': True, - 'buffer_size': 8 - } - if kwargs['rebatched_worker_size'] is None: - kwargs['rebatched_worker_size'] = 128 - super().__init__(**kwargs) - self.initialized = True - - return self diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py deleted file mode 100755 index a6f3b3cd2..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import numpy as np -import torch - -from mmcv.parallel import DataContainer - -# A customized None type for HierarchicalDataManager -HierarchicalDataNone = object() - - -class HierarchicalDataManager: - """A class manage all the tensors in the hierarchical data. - - At present, the input data structure accepted by IPU is limited, - when the input data structure of mmcv varies. - Here, an intermediate class is needed to get and update tensors - from the original data. - - HierarchicalDataManager will record a hierarchical input/output data in - self._hierarchical_data. For example, we have an input data: - {'img': tensorA, 'label': tensorB, 'img_metas': [tensorC, tensorD]} - To enable IPU to use the input, HierarchicalDataManager will collect - the torch tensors from self._hierarchical_data into a tuple like: - (tensorA, tensorB, tensorC, tensorD). - Meanwhile, the return of IPU is a tuple of tensors, HierarchicalDataManager - also have a function named update_all_tensors to update tensors in - self._hierarchical_data which is the output for upper calls. - - Args: - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - """ - - def __init__(self, logger=None): - self.atomic_types = (int, str, float, np.ndarray, type(None)) - self.warning = warnings.warn if logger is None else logger.warning - # enable or disable input data's shape and value check - self.quick_mode = False - self._hierarchical_data = None - - def quick(self): - self.quick_mode = True - - def compare_atomic_type(self, a, b): - """Compare data, supported datatypes are numpy array and python basic - types.""" - if isinstance(a, np.ndarray): - return np.all(a == b) - else: - return a == b - - def record_hierarchical_data(self, data): - """Record a hierarchical data.""" - if self._hierarchical_data is not None: - if isinstance(data, torch.Tensor): - assert isinstance(self._hierarchical_data, torch.Tensor), \ - 'original hierarchical data is not torch.tensor' - self._hierarchical_data = data - else: - self.update_hierarchical_data(data) - else: - self._hierarchical_data = data - - @property - def hierarchical_data(self): - return self._hierarchical_data - - def update_hierarchical_data(self, - dataA, - dataB=HierarchicalDataNone, - strict=True, - address='data'): - """Update dataB with dataA in-place. - - Args: - dataA (list or dict or tuple): New hierarchical data. - dataB (list or dict or tuple): hierarchical data to update. - if not specified, self.hierarchical_data will be updated then. - strict (bool, optional): If true, an error will be reported - when the following conditions occur: - 1. Non-torch.Tensor data changed. - 2. Torch.Tensor data shape changed. - address (str): Record the address of current data to be updated. - Default: 'data'. - """ - if dataB is HierarchicalDataNone: - dataB = self.hierarchical_data - - # Update with a da ta with the same structure - # but different values(tensors and basic python data types) - if isinstance(dataA, (tuple, list)): - for idx, node in enumerate(dataA): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(idx)}]' - assert isinstance(node, type(dataB[idx])),\ - f'data structure changed: {new_address}' - if isinstance(node, torch.Tensor): - dataB[idx] = node - else: - self.update_hierarchical_data( - node, dataB[idx], strict, address=new_address) - elif isinstance(dataA, dict): - for k, v in dataA.items(): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(k)}]' - assert isinstance(v, type(dataB[k])),\ - f'data structure changed: {new_address}' - if isinstance(v, torch.Tensor): - dataB[k] = v - else: - self.update_hierarchical_data( - v, dataB[k], strict, address=new_address) - elif isinstance(dataA, self.atomic_types): - if not self.quick_mode: - is_equal = self.compare_atomic_type(dataA, dataB) - if not is_equal: - if strict: - raise ValueError( - 'all data except torch.Tensor should be same, ' - f'but data({address}) is changed.') - else: - self.warning( - f'find a non-torch.Tensor data({type(dataA)}) ' - f'changed, and the address is {address}') - elif isinstance(dataA, DataContainer): - if not self.quick_mode: - assert isinstance(dataB, DataContainer) - new_address = address + '.data' - self.update_hierarchical_data( - dataA.data, dataB.data, False, address=new_address) - else: - raise NotImplementedError( - f'not supported datatype:{type(dataA)}, address is {address}') - - def collect_all_tensors(self, hierarchical_data=None): - """Collect torch.Tensor data from self.hierarchical_data to a list and - return.""" - # get a list of tensor from self._hierarchical_data - if hierarchical_data is None: - hierarchical_data = self._hierarchical_data - tensors = [] - if isinstance(hierarchical_data, torch.Tensor): - tensors = [hierarchical_data] - else: - self._collect_tensors(hierarchical_data, tensors) - return tensors - - def _collect_tensors(self, data, tensors): - if isinstance(data, (tuple, list)): - for node in data: - if isinstance(node, torch.Tensor): - tensors.append(node) - else: - self._collect_tensors(node, tensors) - elif isinstance(data, dict): - for v in data.values(): - if isinstance(v, torch.Tensor): - tensors.append(v) - else: - self._collect_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._collect_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def update_all_tensors(self, tensors): - """Put tensors from tuple back to self.hierarchical_data.""" - if isinstance(self._hierarchical_data, torch.Tensor): - print(tensors, len(tensors)) - assert len(tensors) == 1 - assert isinstance(tensors[0], torch.Tensor) - self._hierarchical_data = tensors[0] - else: - # convert to list if tensors is tuple - tensors = list(tensors) - self._set_tensors(self._hierarchical_data, tensors) - return self.hierarchical_data - - def _set_tensors(self, data, tensors): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = tensors.pop(0) - else: - self._set_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._set_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def clean_all_tensors(self): - """Delete tensors from self.hierarchical_data.""" - self._clean_tensors(self._hierarchical_data) - - def _clean_tensors(self, data): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = None - else: - self._clean_tensors(v) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._clean_tensors(data.data) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/hook_wrapper.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/hook_wrapper.py deleted file mode 100755 index 141afb86d..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/hook_wrapper.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import HOOKS, LrUpdaterHook, OptimizerHook -from mmcv.utils import TORCH_VERSION, digit_version - - -def wrap_lr_updater_hook(lr_hook_class): - """A wrapper function to wrap any subclass of LrUpdaterHook. - - IPU needs extra operations to upload optimizer settings. This wrapper will - override function(_set_lr) of a subclass of LrUpdaterHook. - """ - assert issubclass(lr_hook_class, LrUpdaterHook) - - class ipu_lr_hook_class(lr_hook_class): - - def _set_lr(self, runner, *args, **kwargs): - super()._set_lr(runner, *args, **kwargs) - # convert torch optimizer to poptorch optimizer - runner.model.setOptimizer(runner.optimizer) - - return ipu_lr_hook_class - - -def wrap_optimizer_hook(optimizer_hook_class): - """A wrapper function to wrap OptimizerHook. - - This is an non-intrusive implementation of wrapping optimizer hook (or you - need to change every config file to use IPU optimizer hook) IPU's clip-norm - implementation is different from pytorch, so there should be an error - raised when using clip-norm. - """ - - class ipu_optimizer_hook_class(OptimizerHook): - - def __init__(self, **kwargs): - super().__init__(**kwargs) - if self.grad_clip is not None: - raise NotImplementedError('IPU does not support gradient clip') - - return ipu_optimizer_hook_class - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class IPUFp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - assert grad_clip is None,\ - 'IPU mode does not support `grad_clip` currently' - assert coalesce,\ - 'implemented all reduce in distributed training currently' - assert bucket_size_mb == -1,\ - '`bucket_size_mb` should not be set in IPU mode' - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - raise NotImplementedError( - 'IPU mode does not support dynamic loss scale currently') - elif isinstance(loss_scale, float): - self.loss_scale = loss_scale - elif isinstance(loss_scale, dict): - raise NotImplementedError( - 'IPU mode supports single scale currently') - else: - raise ValueError( - f'loss_scale should be float, but got {loss_scale} ') - - def after_train_iter(self, runner): - pass - -else: - raise RuntimeError('The IPU mode only supports torch 1.6 and above') diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/model_wrapper.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/model_wrapper.py deleted file mode 100755 index c345537e2..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/model_wrapper.py +++ /dev/null @@ -1,721 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect -from collections import OrderedDict -from typing import Optional, Union - -import poptorch -import torch -import torch.nn as nn -from poptorch import PoplarExecutor, __version__, identity_loss -from poptorch._args_parser import ArgsParser - -from mmcv.runner import auto_fp16 -from .hierarchical_data_manager import HierarchicalDataManager -from .utils import compare_ndarray, model_sharding, recomputation_checkpoint - - -class DictArgsParser(ArgsParser): - """A helper class for handling model input. - - Args: - inputs (list): Inputs of model. - """ - - def __init__(self, inputs): - # Combine args and kwargs: - self._has_variadic_arguments = True - self._varnames = list(inputs.keys()) - self._defaults = [inspect.Parameter.empty for _ in self._varnames] - self._warned_not_contiguous_input = False - - -class WrappedNet(nn.Module): - """A net wrapper for model conversion. - - This wrapper will make some changes and add some extra functions to - training/inference model. - - Args: - model (:obj:`nn.Module`): The model to run. - inputs_manager (:obj:`HierarchicalDataManager`): A parser - converting inputs from tuple to dictionary. - outputs_manager (:obj:`HierarchicalDataManager`): A parser - converting outputs from dictionary to tuple. - inter_outputs_in_cpu (dict): Specify the features to be - recorded. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - model, - inputs_manager, - outputs_manager, - inter_outputs_in_cpu, - modules_to_record=None): - super().__init__() - self.model = model - self.inputs_manager = inputs_manager - self.outputs_manager = outputs_manager - self.training = model.training - # Register a hook function to capture the intermediate features - # generated by the network to align the outputs between ipu and cpu - # Used to confirm whether the implementation of CPU is consistent - # with the implementation of IPU - self.inter_outputs_in_cpu = inter_outputs_in_cpu - if modules_to_record is None: - modules_to_record = [] - - for idx, (name, module) in enumerate(model.named_modules()): - if name in modules_to_record or idx in modules_to_record: - features_hook = self.get_input_output_hook( - name, idx, self.inter_outputs_in_cpu) - module.register_forward_hook(hook=features_hook) - - def get_input_output_hook(self, name, idx, save_dict): - - def input_output_hook(module, fea_in, fea_out): - if isinstance(fea_in, tuple): - fea_in = list(fea_in) - if isinstance(fea_out, tuple): - fea_out = list(fea_out) - save_dict[name] = { - 'fea_in': fea_in, - 'fea_out': fea_out, - 'idx': idx - } - return None - - return input_output_hook - - def forward(self, inputs_tuple): - """This function is used to be compiled to ipu, the inputs and outputs - need to be tuples, so here we need to restore the input back to a - dictionary and convert the output to a tuple.""" - self.inputs_manager.update_all_tensors(inputs_tuple) - kwargs = {**(self.inputs_manager.hierarchical_data)} - if self.training: - outputs = self.forward_train(kwargs) - # tell poptorch which loss will be used finally - identity_loss(outputs['loss'], reduction='none') - else: - outputs = self.forward_eval(kwargs) - - if isinstance(outputs, torch.Tensor): - # currently not support single tensor output, - # need to wrap it with a dictionary, - # use a keyword to identify this case - outputs = {'output of WrappedNet: single tensor': outputs} - - # if there are some features need to be record, add extra outputs - for name in self.inter_outputs_in_cpu: - outputs[name] = self.inter_outputs_in_cpu[name] - - # record all the places of return tensors in the converting stage - # while in the real run stage, all the tensor are changed in-place - # that means the output can be obtained directly outside this function - self.outputs_manager.record_hierarchical_data(outputs) - plain_outputs = self.outputs_manager.collect_all_tensors() - return plain_outputs - - def forward_train(self, kwargs): - optimizer = kwargs.pop('optimizer') - outputs = self.train_step(kwargs, optimizer) - return outputs - - def train_step(self, data, optimizer=None, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating are also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer`, optional): The - optimizer of runner is passed to ``train_step()``. This - argument is unused and reserved. - - Returns: - dict: Dict of outputs. The following fields are contained. - - loss (torch.Tensor): A tensor for back propagation, which \ - can be a weighted sum of multiple losses. - - log_vars (dict): Dict contains all the variables to be sent \ - to the logger. - - num_samples (int): Indicates the batch size (when the model \ - is DDP, it means the batch size on each GPU), which is \ - used for averaging the logs. - """ - losses = self.model(**data) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, log_vars=log_vars, num_samples=len(data['img'].data)) - - return outputs - - def _parse_losses(self, losses): - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(loss.mean() for loss in loss_value) - elif isinstance(loss_value, dict): - for name, value in loss_value.items(): - log_vars[name] = value - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(value for key, value in log_vars.items() if 'loss' in key) - log_vars['loss'] = loss - - return loss, log_vars - - def forward_eval(self, kwargs): - img = kwargs.pop('img') - img_metas = kwargs.pop('img_metas', None) - return_loss = kwargs.pop('return_loss') - assert not return_loss - # TODO Temporarily hard-code to close post_process, - # otherwise, in the third trace(_check_trace), - # post_process will convert output tensor to numpy array automatically, - # resulting in _check_trace failure - outputs = self.model( - img, - img_metas=img_metas, - return_loss=return_loss, - post_process=False) - return outputs - - -class MMPoplarExecutor(PoplarExecutor): - """An executor for inputs/outputs parsing, model compilation, data - alignment and IPU upload/download. - - Args: - model (:obj:`nn.Module`): The model to be compiled. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - training (bool): Model in training mode or eval mode. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - args (argument list): Arguments passed to the `__init__` - method of PoplarExecutor. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of PoplarExecutor. - """ - - def __init__(self, - model, - logger=None, - training=True, - modules_to_record=None, - *args, - **kwargs): - # self.model == self._user_model: input pytorch model - # self._model: wrapped model which is used to compile - # and update weights, these two models use same weights - # wrapped model only accept and output tuple, so - # HierarchicalDataManager will convert dictionary - # to tuple and convert them back - self.inputs_manager = HierarchicalDataManager(logger=logger) - self.outputs_manager = HierarchicalDataManager(logger=logger) - self.logger = logger - # the features calculated by CPU - self.inter_outputs_in_cpu = {} - # the features calculated by IPU - self.inter_outputs_in_ipu = {} - if modules_to_record is None: - # It is possible that the IPU implementation of some operators - # is inconsistent with the expected (CPU), here you can use - # this method to confirm whether there is a problem - self.compare_with_cpu = False - else: - self.compare_with_cpu = True - # move model.fp16_enabled to self.fp16_enabled, - # modify the position where the input is automatically casted to half - if getattr(model, 'fp16_enabled', False): - model.fp16_enabled = False - self.fp16_enabled = True - # make torch.jit.trace convert self._model - model = WrappedNet( - model, - self.inputs_manager, - self.outputs_manager, - self.inter_outputs_in_cpu, - modules_to_record=modules_to_record) - super().__init__(model, training=training, *args, **kwargs) - # overwrite self._args_parser in train_step or val_step - self._args_parser = None - if training: - assert self.training - else: - assert not self.training - - @property - def training(self): - # If trying to get the attribute(training) of self, - # since the class has no training attribute, - # it will automatically look for the training attribute of self.model. - # However, the real attribute we want to check is self._training, - # self.model.training and self._training are often inconsistent. - # It is not clear whether it is a Poptorch bug or a special design, - # temporarily use this function to fix the problem - return self._training # comes from self.model._training - - @auto_fp16(supported_types=(PoplarExecutor, )) - def run_model(self, data_dict): - # this function is used to parse input_dict - # and convert to output_dict - if self.isCompiled(): - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - else: - # get tensors out of data and put them in a tuple - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - # turn logger in data manager off after compilation - self.inputs_manager.quick() - self.outputs_manager.quick() - - # parser args in the first iter - if self._args_parser is None: - self._args_parser = DictArgsParser({'args': inputs_tuple}) - - # run or convert model - # the plain_outputs will be used in converting stage - plain_outputs = self(inputs_tuple) - - self.inputs_manager.clean_all_tensors() - - # put list of tensors back to the output dict - # according to the same order - self.outputs_manager.update_all_tensors(plain_outputs) - # get the real output dictionary from self.outputs_manager - output_dict = self.outputs_manager.hierarchical_data - - # split output_dict into inter_outputs_in_ipu - # and output of the torch model - torch_model_output = {} - for name in output_dict: - if name in self.inter_outputs_in_cpu: - self.inter_outputs_in_ipu[name] = output_dict[name] - else: - torch_model_output[name] = output_dict[name] - - if 'output of WrappedNet: single tensor' in output_dict: - assert len(torch_model_output) == 1 - assert isinstance( - torch_model_output['output of WrappedNet: single tensor'], - torch.Tensor) - torch_model_output = \ - torch_model_output['output of WrappedNet: single tensor'] - - return torch_model_output - - def train_step(self, data, optimizer=None, **kwargs): - # arguments from mmcls/models/classifiers/base.py: - # BaseClassifier.train_step - assert self.training - assert len(kwargs) == 0 # TODO, support later if necessary - - # TODO support datacontainer as input - # currently, auto_fp16 and HierarchicalDataManager take too much - # time on traversing datacontainer - data['img_metas'] = None - num_samples = len(data['img'].data) - - # TODO we will ignore optimizer because it will not be used in model, - # support later if necessary - data['optimizer'] = None - output_dict = self.run_model(data) - - # outputs contained loss, log_vars, num_samples, - # only loss(torch.tensor) has been updated - # remove all unchanged vars, left torch.tensor - neat_output_dict = {'loss': output_dict['loss']} - - # re-parse outputs, get back log_vars and num_samples - loss, log_vars = self.model._parse_losses(neat_output_dict) - final_output_dict = dict( - loss=loss, log_vars=log_vars, num_samples=num_samples) - return final_output_dict - - def eval_call(self, img, img_metas=None, return_loss=True, **kwargs): - # arguments from mmdet/models/detectors/base.py:BaseDetector.forward - # tmp usssage for eval mode - assert not self.training - assert len(kwargs) == 0 # TODO, support later if necessary - assert not return_loss - data = {'img': img, 'img_metas': img_metas, 'return_loss': return_loss} - - output_dict = self.run_model(data) - - return output_dict - - def detachFromDevice(self): - if self.isCompiled() and self._is_attached: - super().detachFromDevice() - - def attachToDevice(self): - if self.isCompiled() and not self._is_attached: - super().attachToDevice() - - -class TrainEvalModel: - """A class maintaining training MMPoplarExecutor and inference - MMPoplarExecutor. - - Args: - train_model (:obj:`nn.Module`): The training model to be compiled. - ``train_model`` can be None if only executing validation. - eval_model (:obj:`nn.Module`): The inference model to be compiled. - options (mmcv.Config, dict): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - train_model, - eval_model, - options, - optimizer, - modules_to_record=None, - logger=None): - if train_model is None: - self._train_executor = None - self.training = False - else: - self._train_executor = get_training_model( - train_model, - options=options['training'], - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - self.training = True - self._eval_executor = get_inference_model( - eval_model, options=options['inference'], logger=logger) - - @property - def executor(self): - if self.training: - return self._train_executor - else: - return self._eval_executor - - def train(self, mode: bool = True): - """Sets the module in training mode. - - This has any effect only on certain modules. See documentations of - particular modules for details of their behaviors in - training/evaluation mode, if they are affected, - e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - Args: - mode (bool): whether to set training mode (``True``) or evaluation - mode (``False``). Default: ``True``. - - Returns: - Module: self - """ - if not isinstance(mode, bool): - raise ValueError('training mode is expected to be boolean, ' - f'but got {type(mode)}') - if self._train_executor is None and mode: - raise RuntimeError( - 'The train_executor is not initialized.' - 'If you want to initialize train_executor,' - 'you need to input optimizer when converting pytorch model') - - if mode == self.training: - self.model.train(mode) - return self - else: - if self.isCompiled(): - # copy weights from IPU to cpu before off-load current session - self.copyWeightsToHost() - # detach the current session before change the mode, - # if is training mode and weights are updated, - # poptorch will copy weights from IPU to host - self.detachFromDevice() - - self.training = mode # session will changed with mode changing - self.model.train(mode) - - # after changing mode, attach the current new session, - # and this function will copy weights of model to device - self.attachToDevice() - return self - - def eval(self): - """Sets the module in evaluation mode. - - This has any effect only on certain modules. - See documentations of particular modules - for details of their behaviors in training/evaluation mode, - if they are affected, e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - This is equivalent with :meth:`self.train(False) - `. - - See :ref:`locally-disable-grad-doc` for a comparison between - `.eval()` and several similar mechanisms that may be confused with it. - - Returns: - Module: self - """ - return self.train(False) - - def compare_data_between_ipu_and_cpu(self, inter_outputs_in_cpu, - inter_outputs_in_ipu): - for key, val in inter_outputs_in_cpu.items(): - is_tensor = isinstance(val['fea_in'], torch.Tensor) - fea_in_cpu = val['fea_in'] - fea_in_cpu_list = [fea_in_cpu] if is_tensor else fea_in_cpu - fea_in_ipu = inter_outputs_in_ipu[key]['fea_in'] - fea_in_ipu_list = [fea_in_ipu] if is_tensor else fea_in_ipu - - is_tensor = isinstance(val['fea_out'], torch.Tensor) - fea_out_cpu = val['fea_out'] - fea_out_cpu_list = [fea_out_cpu] if is_tensor else fea_out_cpu - fea_out_ipu = inter_outputs_in_ipu[key]['fea_out'] - fea_out_ipu_list = [fea_out_ipu] if is_tensor else fea_out_ipu - - print('comparing layer:', key) - for idx, (featA, featB) in \ - enumerate(zip(fea_in_cpu_list, fea_in_ipu_list)): - print('fea_in, tensor ', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - for idx, (featA, featB) in \ - enumerate(zip(fea_out_cpu_list, fea_out_ipu_list)): - print('fea_out, tensor', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def train_step(self, data, optimizer=None, **kwargs): - assert self.training, 'not supported train_step on eval mode' - inter_outputs_in_cpu = {} - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu): - self.copyWeightsToHost() - # run in CPU mode - self._train_executor.model.train_step(data, optimizer, **kwargs) - inter_outputs_in_cpu = { - **(self._train_executor.inter_outputs_in_cpu) - } - # run in IPU mode - result = self._train_executor.train_step(data, optimizer, **kwargs) - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu - and len(inter_outputs_in_cpu) > 0): - self.compare_data_between_ipu_and_cpu( - inter_outputs_in_cpu, - self._train_executor.inter_outputs_in_ipu) - return result - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def __call__(self, *args, **kwargs): - if self.training: - raise NotImplementedError('use train_step rather than __call__') - else: - return self._eval_executor.eval_call(*args, **kwargs) - - def __getattr__(self, attr): - return getattr(self.executor, attr) - - -def get_training_model(model: nn.Module, - options: Optional[poptorch.Options] = None, - optimizer: Optional[torch.optim.Optimizer] = None, - logger=None, - modules_to_record=None) -> poptorch.PoplarExecutor: - """Create a PopTorch training model from a PyTorch model, running on IPU - hardware in training mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned training model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.train()`` on the original model, which - changes the ``training`` bool of the model instance, will not alter the - model returned by this function. You may need to call ``model.train()`` - on your model before you call this function for correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): The optimizers - to apply during training. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place - of ``model``. - """ - # Create a copy of the original model in case it needs to be wrapped - maybe_wrapped_model = copy.copy(model) - - return MMPoplarExecutor( - model=maybe_wrapped_model, - logger=logger, - options=options, - training=True, - optimizer=optimizer, - user_model=model, - modules_to_record=modules_to_record, - poptorch_version=__version__) - - -def get_inference_model(model: Union[nn.Module, poptorch.PoplarExecutor], - options: Optional[poptorch.Options] = None, - logger=None) -> poptorch.PoplarExecutor: - """Create a PopTorch inference model from a PyTorch model, running on IPU - hardware in inference mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned inference model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.eval()`` on the original model will not alter - the model returned by this function. You may need to call - ``model.eval()`` on your model before you call this function for - correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place of - ``model``. - """ - - return MMPoplarExecutor( - model=copy.copy(model), - logger=logger, - options=options, - training=False, - poptorch_version=__version__) - - -def ipu_model_wrapper(model, - options, - optimizer=None, - logger=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None): - """Convert torch model to IPU model. - - Args: - model (nn.Module): The target model to be converted. - options (dict[str, poptorch.Options]): IPU options, generated - by :func:`cfg2options`. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during training. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (dict): A dictionary contains train_split_edges and - train_ckpt_nodes, See details in :func:`model_sharding` and - :func:`recomputation_checkpoint` functions. - fp16_cfg (dict): Config for IPU fp16 training. Currently supports - configs: `loss_scale`, `velocity_accum_type` and `accum_type`. - See details in - https://docs.graphcore.ai/projects/poptorch-user-guide/en/latest/index.html - - Returns: - TrainEvalModel: IPU wrapped model. - """ - if ipu_model_cfg is None: - ipu_model_cfg = {} - training = model.training if optimizer is not None else False - # set mixed-precision - if fp16_cfg is not None: - from mmcv.runner import wrap_fp16_model - loss_scale = fp16_cfg['loss_scale'] - wrap_fp16_model(model) - model.half() - # TODO tmp ussage to set loss scaling for torch original optimizer - if optimizer is not None: - optimizer.loss_scaling = loss_scale - if fp16_cfg.get('velocity_accum_type', False): - if fp16_cfg['velocity_accum_type'] == 'half': - optimizer.velocity_accum_type = torch.half - else: - optimizer.velocity_accum_type = torch.float32 - if fp16_cfg.get('accum_type', False): - if fp16_cfg['accum_type'] == 'half': - optimizer.accum_type = torch.half - else: - optimizer.accum_type = torch.float32 - # TODO support feature alignment for fp16 - if modules_to_record is not None: - raise NotImplementedError( - 'Feature alignment for fp16 is not implemented') - - # set model partition - if optimizer is None: - train_model = None - else: - # split model into multi-IPUs if specified - train_model = model_sharding( - copy.copy(model).train(), - ipu_model_cfg.get('train_split_edges', [])) - - recomputation_checkpoint(train_model, - ipu_model_cfg.get('train_ckpt_nodes', [])) - - # TODO support feature alignment for gradient accumulation mode - gradient_accumulation = \ - getattr(options['training'].Training, 'gradient_accumulation', 1) - if gradient_accumulation > 1: - assert modules_to_record is None, \ - 'Feature alignment for grad-accumulation mode not implemented' - - # TODO support feature alignment for multi-replica mode - replication_factor = \ - getattr(options['training'], 'replication_factor', 1) - if replication_factor > 1: - assert modules_to_record is None, \ - 'Feature alignment for multi-replica mode not implemented' - - # TODO supports different model partitions between train and eval mode - assert len(ipu_model_cfg.get('eval_split_edges', [])) == 0,\ - 'Currently, BeginBlock can only be used once on the same model' - eval_model = copy.copy(model).eval() - - # wrap model for compilation - model = TrainEvalModel( - train_model, - eval_model, - options=options, - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - model.train(training) - return model diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/runner.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/runner.py deleted file mode 100755 index e2d492267..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/runner.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.runner import (HOOKS, RUNNERS, BaseRunner, EpochBasedRunner, - IterBasedRunner) -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import (IPUFp16OptimizerHook, wrap_lr_updater_hook, - wrap_optimizer_hook) - from .model_wrapper import ipu_model_wrapper - from .utils import build_from_cfg_with_wrapper, cfg2options - - -class IPUBaseRunner(BaseRunner): - """A base runner for IPU. - - This runner has some extra processes for IPU which are shown below: - - 1. Parse options for IPU - 2. wrap pytorch model for IPU - 3. Raise errors while encountering illegal usage - 4. Input IPU options and initialize dataloader if finding an instance - of IPUDataLoader - - Args: - model (:obj:`nn.Module`): The model to run. - options_cfg (mmcv.Config, dict): Options that will be used to compile - and run the model. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (mmcv.Config, dict): Config of model partition and - recomputing checkpoint - fp16_cfg (mmcv.Config): Config for fp16 training. - batch_processor (callable): A callable method that process a data - batch. Should be None for IPU runner - kwargs (Dict[str, Any], optional): Keyword arguments will be passed to - ``base_runner.BaseRunner``. - """ - - def __init__(self, - model, - options_cfg=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None, - batch_processor=None, - **kwargs): - assert hasattr(model, 'train_step') and batch_processor is None,\ - 'only support model with train_step' - - if options_cfg is None: - options_cfg = {} - # call BaseRunner.__init__() here - super().__init__(model, **kwargs) - - # process options of ipu - if IS_IPU_AVAILABLE: - self.options = cfg2options(options_cfg) - self.model = ipu_model_wrapper( - self.model, - self.options, - self.optimizer, - self.logger, - modules_to_record=modules_to_record, - ipu_model_cfg=ipu_model_cfg, - fp16_cfg=fp16_cfg) - else: - raise NotImplementedError('cpu mode on IPURunner is not supported') - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - assert isinstance(lr_config, dict) - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, - # e.g., 'cyclic', then its first letter will be capitalized, - # e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, the string will not be changed - # if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = build_from_cfg_with_wrapper(lr_config, HOOKS, - wrap_lr_updater_hook) - self.register_hook(hook, priority='VERY_HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - assert isinstance(optimizer_config, (dict, IPUFp16OptimizerHook)) - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = build_from_cfg_with_wrapper(optimizer_config, HOOKS, - wrap_optimizer_hook) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def run(self, data_loaders, workflow, *args, **kwargs): - for i, flow in enumerate(workflow): - mode, _ = flow - # initialize IPU dataloader if not initialized - assert isinstance(data_loaders[i], IPUDataLoader),\ - 'IPU runner can only work with `IPUDataLoader`' - data_loaders[i].init(options=self.get_options(mode)) - - super().run(data_loaders, workflow, *args, **kwargs) - - def get_options(self, mode): - if mode == 'train': - return self.options['training'] - elif mode == 'val': - return self.options['inference'] - else: - raise ValueError(f'mode should be train or val but got {mode}') - - -@RUNNERS.register_module() -class IPUEpochBasedRunner(IPUBaseRunner, EpochBasedRunner): - """Epoch-based Runner for IPU. - - The Inheritance order(MRO) is: IPUEpochBasedRunner -> IPUBaseRunner -> - EpochBasedRunner -> BaseRunner This runner train models epoch by epoch. - """ - pass - - -@RUNNERS.register_module() -class IPUIterBasedRunner(IPUBaseRunner, IterBasedRunner): - """Iteration-based Runner for IPU. - - The Inheritance order(MRO) is: IPUIterBasedRunner -> IPUBaseRunner -> - IterBasedRunner -> BaseRunner This runner train models iteration by - iteration. - """ - pass diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/utils.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/utils.py deleted file mode 100755 index 79709db1e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/ipu/utils.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import numpy as np -import popart -import poptorch -import torch -import torch.nn as nn - -from mmcv.utils import Registry - - -def _options_assigner(cfg, options_node): - # set popart.options by config - # cfg: dict, python data type - # options_node: python module or function - if isinstance(cfg, dict): - for key in cfg: - _options_assigner(cfg[key], getattr(options_node, key)) - elif isinstance(cfg, (int, float, str, list)): - if callable(options_node): - options_node(cfg) - else: - error_msg = f'options_node type {type(options_node)} not supported' - raise NotImplementedError(error_msg) - else: - error_msg = f'cfg type {type(cfg)} not supported' - raise NotImplementedError(error_msg) - - -def cfg2options(cfg): - """Parse dictionary to ipu options. - - Args: - cfg (dict): A dictionary of ipu settings. - - Returns: - dict[str, poptorch.Options]: Training options and inference options - of IPU. - """ - # set ipu options for inference and training by config - train_cfg = cfg.pop('train_cfg', {}) - eval_cfg = cfg.pop('eval_cfg', {}) - eval_cfg['replicationFactor'] = 1 # eval mode only use one replica - eval_cfg['executionStrategy'] = 'ShardedExecution' - # overwrite default ipu cfg with specified train cfgs - training_ipu_cfg = {**cfg, **train_cfg} - # overwrite default ipu cfg with specified eval cfgs - inference_ipu_cfg = {**cfg, **eval_cfg} - - ipu_options = { - 'training': _cast_to_options(training_ipu_cfg), - 'inference': _cast_to_options(inference_ipu_cfg) - } - - # TODO configure these codes - ipu_options['training']._Popart.set('disableGradAccumulationTensorStreams', - True) - ipu_options['training']._Popart.set( - 'accumulateOuterFragmentSettings.schedule', - int(popart.AccumulateOuterFragmentSchedule.OverlapMemoryOptimized)) - ipu_options['training'].Precision.enableStochasticRounding(True) - - return ipu_options - - -def _cast_to_options(cfg): - # If it cannot be directly assigned, use if statement to parse it, - # and if it can be directly assigned, use _options_assigner to assign - options = poptorch.Options() - - if 'availableMemoryProportion' in cfg: - available_memory_proportion = cfg.pop('availableMemoryProportion') - mem_props = {} - for i, mem_prop in enumerate(available_memory_proportion): - mem_props[f'IPU{i}'] = mem_prop - options.setAvailableMemoryProportion(mem_props) - - if 'executionStrategy' in cfg: - execution_strategy = cfg.pop('executionStrategy') - if execution_strategy == 'SameAsIpu': - options.setExecutionStrategy( - poptorch.PipelinedExecution( - getattr(poptorch.AutoStage, execution_strategy))) - elif execution_strategy == 'ShardedExecution': - options.setExecutionStrategy(poptorch.ShardedExecution()) - else: - raise NotImplementedError( - 'executionStrategy should be "SameAsIpu" or "ShardedExecution"' - f', but got {execution_strategy}') - - if 'partialsType' in cfg: - partials_type = cfg.pop('partialsType') - options.Precision.setPartialsType(getattr( - torch, partials_type)) # half or float - - _options_assigner(cfg, options) - return options - - -def model_sharding(model, split_edges): - """split models in-place into multi-IPUs. - - Args: - model (nn.Module): The target model to be split. - split_edges (list of dict): Model layer names or layer numbers - of split edge. Each item of ``split_edges`` is a dictionary, - which may contain the following key-pairs: - - - layer_to_call: PyTorch module to assign to the block - - user_id (optional): A user defined identifier for the block. - - ipu_id: The id of the IPU to run on. - - Examples: - >>> split_edges = [ - ... dict(layer_to_call='model.conv1', ipu_id=0), - ... dict(layer_to_call='model.conv3', ipu_id=1)] - >>> sharding_model = model_sharding(torch_model, split_edges) - - Returns: - nn.Module: Split model. - """ - if len(split_edges) == 0: - return model - assert isinstance(split_edges, list) - spilt_edges_dict = {edge['layer_to_call']: edge for edge in split_edges} - - for idx, (name, module) in enumerate(model.named_modules()): - if idx in spilt_edges_dict and name in spilt_edges_dict: - raise ValueError( - 'The same layer is referenced twice while doing model' - f' partition: idx is {idx} and name is {name}') - - edge = spilt_edges_dict.pop(name, None) - edge = spilt_edges_dict.pop(idx, edge) - if edge is not None: - poptorch.BeginBlock(module, edge.get('user_id', name), - edge['ipu_id']) - - # ensure all split_edges are used - if len(spilt_edges_dict) > 0: - split_edge_names = list(spilt_edges_dict.keys()) - raise RuntimeError( - f'split_edges: {split_edge_names} are not contained in the model') - return model - - -def recomputation_checkpoint(model: nn.Module, module_names: list): - """Annotates the output of a module to be checkpointed instead of - recomputed. - - If recomputation mode is enabled, ipu will release the activations of - the middle layers to save memory. During the backward of gradient, - the activation of the middle layer will be recalculated again. - This function is used to declare the activations of some intermediate - layers that need to be saved in order to skip the recomputation of - some layers. - - Args: - model (nn.Module): The target model to apply recomputation - checkpoint. - module_names (list): Layer names of module. - """ - - def recompute_outputs(module, inputs, outputs): - if isinstance(outputs, tuple): - return tuple(poptorch.recomputationCheckpoint(y) for y in outputs) - else: - return poptorch.recomputationCheckpoint(outputs) - - for name, module in model.named_modules(): - if name in module_names: - module.register_forward_hook(recompute_outputs) - module_names.remove(name) - - # check all module_names are used - assert len(module_names) == 0,\ - f'recomputed nodes: {module_names} are not contained in the model' - - -def compare_ndarray(featA, featB, rtol=1e-3, atol=1e-5): - """Align data between two activations or weights.""" - try: - np.testing.assert_allclose(featA, featB, rtol=rtol, atol=atol) - except AssertionError as e: - print(e) - - -def build_from_cfg_with_wrapper(cfg, - registry, - wrapper_func=None, - default_args=None): - """Build a module from config dict and wrap module with "wrapper_func". - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - wrapper_func (function): Used to wrap class - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - - if wrapper_func is None: - wrapped_obj_cls = obj_cls - else: - wrapped_obj_cls = wrapper_func(obj_cls) - try: - return wrapped_obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{wrapped_obj_cls.__name__}: {e}') diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/__init__.py deleted file mode 100644 index 572c4da7e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .data_parallel import MLUDataParallel -from .distributed import MLUDistributedDataParallel -from .scatter_gather import scatter, scatter_kwargs - -__all__ = [ - 'MLUDataParallel', 'MLUDistributedDataParallel', 'scatter', - 'scatter_kwargs' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/_functions.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/_functions.py deleted file mode 100644 index 7c35e65a2..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/_functions.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def scatter(input, devices): - """scatter copies tensor to MLU directly.""" - if isinstance(input, list): - outputs = [scatter(_input, devices) for _input in input] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - return output.to('mlu') if devices != [-1] else output - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_mlus, input): - outputs = scatter(input, target_mlus) - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/data_parallel.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/data_parallel.py deleted file mode 100644 index b2d09d0b0..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/data_parallel.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -import torch - -from mmcv.parallel import MMDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDataParallel(MMDataParallel): - """The MLUDataParallel module that supports DataContainer. - - MLUDataParallel is a class inherited from MMDataParall, which supports - MLU training and inference only. - - The main differences with MMDataParallel: - - - It only supports single-card of MLU, and only use first card to - run training and inference. - - - It uses direct host-to-device copy instead of stream-background - scatter. - - .. warning:: - MLUDataParallel only supports single MLU training, if you need to - train with multiple MLUs, please use MLUDistributedDataParallel - instead. If you have multiple MLUs, you can set the environment - variable ``MLU_VISIBLE_DEVICES=0`` (or any other card number(s)) - to specify the running device. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MLUDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.device_ids = [0] - self.src_device_obj = torch.device('mlu:0') - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/distributed.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/distributed.py deleted file mode 100644 index 3768c754c..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/distributed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.parallel import MMDistributedDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDistributedDataParallel(MMDistributedDataParallel): - """The DDP module supports DataContainer. - - MLUDDP has one difference from MMDDP which moves data to MLU with coping - instead of scattering. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/scatter_gather.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/scatter_gather.py deleted file mode 100644 index 0b0c9b96f..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/device/mlu/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmcv.parallel.data_container import DataContainer -from ._functions import Scatter - - -def scatter(inputs, target_mlus, dim=0): - """Scatter inputs to target mlu. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_mlus != [-1]: - obj = obj.to('mlu') - return [obj] - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_mlus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_mlus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_mlus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_mlus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_mlus, dim) if inputs else [] - kwargs = scatter(kwargs, target_mlus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/engine/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/engine/__init__.py deleted file mode 100644 index 3193b7f66..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, - single_gpu_test) - -__all__ = [ - 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', - 'single_gpu_test' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/engine/test.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/engine/test.py deleted file mode 100644 index f236b1cda..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/engine/test.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, data_loader): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - # Assume result has the same length of batch_size - # refer to https://github.com/open-mmlab/mmcv/issues/985 - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting - ``gpu_collect=True``, it encodes results to gpu tensors and use gpu - communication for results collection. On cpu mode it saves the results on - different gpus to ``tmpdir`` and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - if rank == 0: - batch_size = len(result) - batch_size_all = batch_size * world_size - if batch_size_all + prog_bar.completed > len(dataset): - batch_size_all = len(dataset) - prog_bar.completed - for _ in range(batch_size_all): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results under cpu mode. - - On cpu mode, this function will save the results on different gpus to - ``tmpdir`` and collect them by the rank 0 worker. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - tmpdir (str | None): temporal directory for collected results to - store. If set to None, it will create a random temporal directory - for it. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_result = mmcv.load(part_file) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results under gpu mode. - - On gpu mode, this function will encode results to gpu tensors and use gpu - communication for results collection. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/__init__.py deleted file mode 100644 index 2051b85f7..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .file_client import BaseStorageBackend, FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler -from .io import dump, load, register_handler -from .parse import dict_from_file, list_from_file - -__all__ = [ - 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', - 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', - 'list_from_file', 'dict_from_file' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/file_client.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/file_client.py deleted file mode 100644 index e7fd7cdfa..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/file_client.py +++ /dev/null @@ -1,1163 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import os -import os.path as osp -import re -import tempfile -import warnings -from abc import ABCMeta, abstractmethod -from contextlib import contextmanager -from pathlib import Path -from typing import Iterable, Iterator, Optional, Tuple, Union -from urllib.request import urlopen - -import mmcv -from mmcv.utils.misc import has_method -from mmcv.utils.path import is_filepath - - -class BaseStorageBackend(metaclass=ABCMeta): - """Abstract class of storage backends. - - All backends need to implement two apis: ``get()`` and ``get_text()``. - ``get()`` reads the file as a byte stream and ``get_text()`` reads the file - as texts. - """ - - # a flag to indicate whether the backend can create a symlink for a file - _allow_symlink = False - - @property - def name(self): - return self.__class__.__name__ - - @property - def allow_symlink(self): - return self._allow_symlink - - @abstractmethod - def get(self, filepath): - pass - - @abstractmethod - def get_text(self, filepath): - pass - - -class CephBackend(BaseStorageBackend): - """Ceph storage backend (for internal use). - - Args: - path_mapping (dict|None): path mapping dict from local path to Petrel - path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` - will be replaced by ``dst``. Default: None. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - """ - - def __init__(self, path_mapping=None): - try: - import ceph - except ImportError: - raise ImportError('Please install ceph to enable CephBackend.') - - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - self._client = ceph.S3Client() - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def get(self, filepath): - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class PetrelBackend(BaseStorageBackend): - """Petrel storage backend (for internal use). - - PetrelBackend supports reading and writing data to multiple clusters. - If the file path contains the cluster name, PetrelBackend will read data - from specified cluster or write data to it. Otherwise, PetrelBackend will - access the default cluster. - - Args: - path_mapping (dict, optional): Path mapping dict from local path to - Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in - ``filepath`` will be replaced by ``dst``. Default: None. - enable_mc (bool, optional): Whether to enable memcached support. - Default: True. - - Examples: - >>> filepath1 = 's3://path/of/file' - >>> filepath2 = 'cluster-name:s3://path/of/file' - >>> client = PetrelBackend() - >>> client.get(filepath1) # get data from default cluster - >>> client.get(filepath2) # get data from 'cluster-name' cluster - """ - - def __init__(self, - path_mapping: Optional[dict] = None, - enable_mc: bool = True): - try: - from petrel_client import client - except ImportError: - raise ImportError('Please install petrel_client to enable ' - 'PetrelBackend.') - - self._client = client.Client(enable_mc=enable_mc) - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def _map_path(self, filepath: Union[str, Path]) -> str: - """Map ``filepath`` to a string path whose prefix will be replaced by - :attr:`self.path_mapping`. - - Args: - filepath (str): Path to be mapped. - """ - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - return filepath - - def _format_path(self, filepath: str) -> str: - """Convert a ``filepath`` to standard format of petrel oss. - - If the ``filepath`` is concatenated by ``os.path.join``, in a Windows - environment, the ``filepath`` will be the format of - 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the - above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. - - Args: - filepath (str): Path to be formatted. - """ - return re.sub(r'\\+', '/', filepath) - - def get(self, filepath: Union[str, Path]) -> memoryview: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - memoryview: A memory view of expected bytes object to avoid - copying. The memoryview object can be converted to bytes by - ``value_buf.tobytes()``. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return str(self.get(filepath), encoding=encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Save data to a given ``filepath``. - - Args: - obj (bytes): Data to be saved. - filepath (str or Path): Path to write data. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.put(filepath, obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Save data to a given ``filepath``. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to encode the ``obj``. - Default: 'utf-8'. - """ - self.put(bytes(obj, encoding=encoding), filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - if not has_method(self._client, 'delete'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `delete` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.delete(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - if not (has_method(self._client, 'contains') - and has_method(self._client, 'isdir')): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` and `isdir` methods, please use a higher' - 'version or dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) or self._client.isdir(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - if not has_method(self._client, 'isdir'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `isdir` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - if not has_method(self._client, 'contains'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` method, please use a higher version or ' - 'dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result after concatenation. - """ - filepath = self._format_path(self._map_path(filepath)) - if filepath.endswith('/'): - filepath = filepath[:-1] - formatted_paths = [filepath] - for path in filepaths: - formatted_paths.append(self._format_path(self._map_path(path))) - return '/'.join(formatted_paths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download a file from ``filepath`` and return a temporary path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str | Path): Download a file from ``filepath``. - - Examples: - >>> client = PetrelBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('s3://path/of/your/file') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one temporary path. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - assert self.isfile(filepath) - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - Petrel has no concept of directories but it simulates the directory - hierarchy in the filesystem through public prefixes. In addition, - if the returned path ends with '/', it means the path is a public - prefix which is a logical directory. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - In addition, the returned path of directory will not contains the - suffix '/' which is consistent with other backends. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if not has_method(self._client, 'list'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `list` method, please use a higher version or dev' - ' branch instead.')) - - dir_path = self._map_path(dir_path) - dir_path = self._format_path(dir_path) - if list_dir and suffix is not None: - raise TypeError( - '`list_dir` should be False when `suffix` is not None') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - # Petrel's simulated directory hierarchy assumes that directory paths - # should end with `/` - if not dir_path.endswith('/'): - dir_path += '/' - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for path in self._client.list(dir_path): - # the `self.isdir` is not used here to determine whether path - # is a directory, because `self.isdir` relies on - # `self._client.list` - if path.endswith('/'): # a directory path - next_dir_path = self.join_path(dir_path, path) - if list_dir: - # get the relative path and exclude the last - # character '/' - rel_dir = next_dir_path[len(root):-1] - yield rel_dir - if recursive: - yield from _list_dir_or_file(next_dir_path, list_dir, - list_file, suffix, - recursive) - else: # a file path - absolute_path = self.join_path(dir_path, path) - rel_path = absolute_path[len(root):] - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class MemcachedBackend(BaseStorageBackend): - """Memcached storage backend. - - Attributes: - server_list_cfg (str): Config file for memcached server list. - client_cfg (str): Config file for memcached client. - sys_path (str | None): Additional path to be appended to `sys.path`. - Default: None. - """ - - def __init__(self, server_list_cfg, client_cfg, sys_path=None): - if sys_path is not None: - import sys - sys.path.append(sys_path) - try: - import mc - except ImportError: - raise ImportError( - 'Please install memcached to enable MemcachedBackend.') - - self.server_list_cfg = server_list_cfg - self.client_cfg = client_cfg - self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, - self.client_cfg) - # mc.pyvector servers as a point which points to a memory cache - self._mc_buffer = mc.pyvector() - - def get(self, filepath): - filepath = str(filepath) - import mc - self._client.Get(filepath, self._mc_buffer) - value_buf = mc.ConvertBuffer(self._mc_buffer) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class LmdbBackend(BaseStorageBackend): - """Lmdb storage backend. - - Args: - db_path (str): Lmdb database path. - readonly (bool, optional): Lmdb environment parameter. If True, - disallow any write operations. Default: True. - lock (bool, optional): Lmdb environment parameter. If False, when - concurrent access occurs, do not lock the database. Default: False. - readahead (bool, optional): Lmdb environment parameter. If False, - disable the OS filesystem readahead mechanism, which may improve - random read performance when a database is larger than RAM. - Default: False. - - Attributes: - db_path (str): Lmdb database path. - """ - - def __init__(self, - db_path, - readonly=True, - lock=False, - readahead=False, - **kwargs): - try: - import lmdb # NOQA - except ImportError: - raise ImportError('Please install lmdb to enable LmdbBackend.') - - self.db_path = str(db_path) - self.readonly = readonly - self.lock = lock - self.readahead = readahead - self.kwargs = kwargs - self._client = None - - def get(self, filepath): - """Get values according to the filepath. - - Args: - filepath (str | obj:`Path`): Here, filepath is the lmdb key. - """ - if self._client is None: - self._client = self._get_client() - - with self._client.begin(write=False) as txn: - value_buf = txn.get(str(filepath).encode('utf-8')) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - def _get_client(self): - import lmdb - - return lmdb.open( - self.db_path, - readonly=self.readonly, - lock=self.lock, - readahead=self.readahead, - **self.kwargs) - - def __del__(self): - self._client.close() - - -class HardDiskBackend(BaseStorageBackend): - """Raw hard disks storage backend.""" - - _allow_symlink = True - - def get(self, filepath: Union[str, Path]) -> bytes: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes: Expected bytes object. - """ - with open(filepath, 'rb') as f: - value_buf = f.read() - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - with open(filepath, 'r', encoding=encoding) as f: - value_buf = f.read() - return value_buf - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` will create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'wb') as f: - f.write(obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` will create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'w', encoding=encoding) as f: - f.write(obj) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - os.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return osp.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return osp.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return osp.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return osp.join(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, filepath: Union[str, Path]) -> Iterable[Union[str, Path]]: - """Only for unified API and do nothing.""" - yield filepath - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if list_dir and suffix is not None: - raise TypeError('`suffix` should be None when `list_dir` is True') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - elif osp.isdir(entry.path): - if list_dir: - rel_dir = osp.relpath(entry.path, root) - yield rel_dir - if recursive: - yield from _list_dir_or_file(entry.path, list_dir, - list_file, suffix, - recursive) - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class HTTPBackend(BaseStorageBackend): - """HTTP and HTTPS storage bachend.""" - - def get(self, filepath): - value_buf = urlopen(filepath).read() - return value_buf - - def get_text(self, filepath, encoding='utf-8'): - value_buf = urlopen(filepath).read() - return value_buf.decode(encoding) - - @contextmanager - def get_local_path(self, filepath: str) -> Iterable[str]: - """Download a file from ``filepath``. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str): Download a file from ``filepath``. - - Examples: - >>> client = HTTPBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('http://path/of/your/file') as path: - ... # do something here - """ - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - -class FileClient: - """A general file client to access files in different backends. - - The client loads a file or text in a specified backend from its path - and returns it as a binary or text file. There are two ways to choose a - backend, the name of backend and the prefix of path. Although both of them - can be used to choose a storage backend, ``backend`` has a higher priority - that is if they are all set, the storage backend will be chosen by the - backend argument. If they are all `None`, the disk backend will be chosen. - Note that It can also register other backend accessor with a given name, - prefixes, and backend class. In addition, We use the singleton pattern to - avoid repeated object creation. If the arguments are the same, the same - object will be returned. - - Args: - backend (str, optional): The storage backend type. Options are "disk", - "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. - prefix (str, optional): The prefix of the registered storage backend. - Options are "s3", "http", "https". Default: None. - - Examples: - >>> # only set backend - >>> file_client = FileClient(backend='petrel') - >>> # only set prefix - >>> file_client = FileClient(prefix='s3') - >>> # set both backend and prefix but use backend to choose client - >>> file_client = FileClient(backend='petrel', prefix='s3') - >>> # if the arguments are the same, the same object is returned - >>> file_client1 = FileClient(backend='petrel') - >>> file_client1 is file_client - True - - Attributes: - client (:obj:`BaseStorageBackend`): The backend object. - """ - - _backends = { - 'disk': HardDiskBackend, - 'ceph': CephBackend, - 'memcached': MemcachedBackend, - 'lmdb': LmdbBackend, - 'petrel': PetrelBackend, - 'http': HTTPBackend, - } - # This collection is used to record the overridden backends, and when a - # backend appears in the collection, the singleton pattern is disabled for - # that backend, because if the singleton pattern is used, then the object - # returned will be the backend before overwriting - _overridden_backends = set() - _prefix_to_backends = { - 's3': PetrelBackend, - 'http': HTTPBackend, - 'https': HTTPBackend, - } - _overridden_prefixes = set() - - _instances = {} - - def __new__(cls, backend=None, prefix=None, **kwargs): - if backend is None and prefix is None: - backend = 'disk' - if backend is not None and backend not in cls._backends: - raise ValueError( - f'Backend {backend} is not supported. Currently supported ones' - f' are {list(cls._backends.keys())}') - if prefix is not None and prefix not in cls._prefix_to_backends: - raise ValueError( - f'prefix {prefix} is not supported. Currently supported ones ' - f'are {list(cls._prefix_to_backends.keys())}') - - # concatenate the arguments to a unique key for determining whether - # objects with the same arguments were created - arg_key = f'{backend}:{prefix}' - for key, value in kwargs.items(): - arg_key += f':{key}:{value}' - - # if a backend was overridden, it will create a new object - if (arg_key in cls._instances - and backend not in cls._overridden_backends - and prefix not in cls._overridden_prefixes): - _instance = cls._instances[arg_key] - else: - # create a new object and put it to _instance - _instance = super().__new__(cls) - if backend is not None: - _instance.client = cls._backends[backend](**kwargs) - else: - _instance.client = cls._prefix_to_backends[prefix](**kwargs) - - cls._instances[arg_key] = _instance - - return _instance - - @property - def name(self): - return self.client.name - - @property - def allow_symlink(self): - return self.client.allow_symlink - - @staticmethod - def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: - """Parse the prefix of a uri. - - Args: - uri (str | Path): Uri to be parsed that contains the file prefix. - - Examples: - >>> FileClient.parse_uri_prefix('s3://path/of/your/file') - 's3' - - Returns: - str | None: Return the prefix of uri if the uri contains '://' else - ``None``. - """ - assert is_filepath(uri) - uri = str(uri) - if '://' not in uri: - return None - else: - prefix, _ = uri.split('://') - # In the case of PetrelBackend, the prefix may contains the cluster - # name like clusterName:s3 - if ':' in prefix: - _, prefix = prefix.split(':') - return prefix - - @classmethod - def infer_client(cls, - file_client_args: Optional[dict] = None, - uri: Optional[Union[str, Path]] = None) -> 'FileClient': - """Infer a suitable file client based on the URI and arguments. - - Args: - file_client_args (dict, optional): Arguments to instantiate a - FileClient. Default: None. - uri (str | Path, optional): Uri to be parsed that contains the file - prefix. Default: None. - - Examples: - >>> uri = 's3://path/of/your/file' - >>> file_client = FileClient.infer_client(uri=uri) - >>> file_client_args = {'backend': 'petrel'} - >>> file_client = FileClient.infer_client(file_client_args) - - Returns: - FileClient: Instantiated FileClient object. - """ - assert file_client_args is not None or uri is not None - if file_client_args is None: - file_prefix = cls.parse_uri_prefix(uri) # type: ignore - return cls(prefix=file_prefix) - else: - return cls(**file_client_args) - - @classmethod - def _register_backend(cls, name, backend, force=False, prefixes=None): - if not isinstance(name, str): - raise TypeError('the backend name should be a string, ' - f'but got {type(name)}') - if not inspect.isclass(backend): - raise TypeError( - f'backend should be a class but got {type(backend)}') - if not issubclass(backend, BaseStorageBackend): - raise TypeError( - f'backend {backend} is not a subclass of BaseStorageBackend') - if not force and name in cls._backends: - raise KeyError( - f'{name} is already registered as a storage backend, ' - 'add "force=True" if you want to override it') - - if name in cls._backends and force: - cls._overridden_backends.add(name) - cls._backends[name] = backend - - if prefixes is not None: - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if prefix not in cls._prefix_to_backends: - cls._prefix_to_backends[prefix] = backend - elif (prefix in cls._prefix_to_backends) and force: - cls._overridden_prefixes.add(prefix) - cls._prefix_to_backends[prefix] = backend - else: - raise KeyError( - f'{prefix} is already registered as a storage backend,' - ' add "force=True" if you want to override it') - - @classmethod - def register_backend(cls, name, backend=None, force=False, prefixes=None): - """Register a backend to FileClient. - - This method can be used as a normal class method or a decorator. - - .. code-block:: python - - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - FileClient.register_backend('new', NewBackend) - - or - - .. code-block:: python - - @FileClient.register_backend('new') - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - Args: - name (str): The name of the registered backend. - backend (class, optional): The backend class to be registered, - which must be a subclass of :class:`BaseStorageBackend`. - When this method is used as a decorator, backend is None. - Defaults to None. - force (bool, optional): Whether to override the backend if the name - has already been registered. Defaults to False. - prefixes (str or list[str] or tuple[str], optional): The prefixes - of the registered storage backend. Default: None. - `New in version 1.3.15.` - """ - if backend is not None: - cls._register_backend( - name, backend, force=force, prefixes=prefixes) - return - - def _register(backend_cls): - cls._register_backend( - name, backend_cls, force=force, prefixes=prefixes) - return backend_cls - - return _register - - def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: - """Read data from a given ``filepath`` with 'rb' mode. - - Note: - There are two types of return values for ``get``, one is ``bytes`` - and the other is ``memoryview``. The advantage of using memoryview - is that you can avoid copying, and if you want to convert it to - ``bytes``, you can use ``.tobytes()``. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes | memoryview: Expected bytes object or a memory view of the - bytes object. - """ - return self.client.get(filepath) - - def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return self.client.get_text(filepath, encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` should create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - self.client.put(obj, filepath) - - def put_text(self, obj: str, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` should create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str, optional): The encoding format used to open the - `filepath`. Default: 'utf-8'. - """ - self.client.put_text(obj, filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str, Path): Path to be removed. - """ - self.client.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return self.client.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return self.client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return self.client.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return self.client.join_path(filepath, *filepaths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download data from ``filepath`` and write the data to local path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Note: - If the ``filepath`` is a local path, just return itself. - - .. warning:: - ``get_local_path`` is an experimental interface that may change in - the future. - - Args: - filepath (str or Path): Path to be read data. - - Examples: - >>> file_client = FileClient(prefix='s3') - >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one path. - """ - with self.client.get_local_path(str(filepath)) as local_path: - yield local_path - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, - suffix, recursive) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/__init__.py deleted file mode 100644 index aa24d9197..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseFileHandler -from .json_handler import JsonHandler -from .pickle_handler import PickleHandler -from .yaml_handler import YamlHandler - -__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/base.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/base.py deleted file mode 100644 index 288878bc5..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseFileHandler(metaclass=ABCMeta): - # `str_like` is a flag to indicate whether the type of file object is - # str-like object or bytes-like object. Pickle only processes bytes-like - # objects but json only processes str-like object. If it is str-like - # object, `StringIO` will be used to process the buffer. - str_like = True - - @abstractmethod - def load_from_fileobj(self, file, **kwargs): - pass - - @abstractmethod - def dump_to_fileobj(self, obj, file, **kwargs): - pass - - @abstractmethod - def dump_to_str(self, obj, **kwargs): - pass - - def load_from_path(self, filepath, mode='r', **kwargs): - with open(filepath, mode) as f: - return self.load_from_fileobj(f, **kwargs) - - def dump_to_path(self, obj, filepath, mode='w', **kwargs): - with open(filepath, mode) as f: - self.dump_to_fileobj(obj, f, **kwargs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/json_handler.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/json_handler.py deleted file mode 100644 index 18d4f15f7..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/json_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -import numpy as np - -from .base import BaseFileHandler - - -def set_default(obj): - """Set default json values for non-serializable values. - - It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. - It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, - etc.) into plain numbers of plain python built-in types. - """ - if isinstance(obj, (set, range)): - return list(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, np.generic): - return obj.item() - raise TypeError(f'{type(obj)} is unsupported for json dump') - - -class JsonHandler(BaseFileHandler): - - def load_from_fileobj(self, file): - return json.load(file) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('default', set_default) - json.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('default', set_default) - return json.dumps(obj, **kwargs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/pickle_handler.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/pickle_handler.py deleted file mode 100644 index b37c79bed..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/pickle_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import pickle - -from .base import BaseFileHandler - - -class PickleHandler(BaseFileHandler): - - str_like = False - - def load_from_fileobj(self, file, **kwargs): - return pickle.load(file, **kwargs) - - def load_from_path(self, filepath, **kwargs): - return super(PickleHandler, self).load_from_path( - filepath, mode='rb', **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('protocol', 2) - return pickle.dumps(obj, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('protocol', 2) - pickle.dump(obj, file, **kwargs) - - def dump_to_path(self, obj, filepath, **kwargs): - super(PickleHandler, self).dump_to_path( - obj, filepath, mode='wb', **kwargs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/yaml_handler.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/yaml_handler.py deleted file mode 100644 index 60911e7e6..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/handlers/yaml_handler.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import yaml - -try: - from yaml import CDumper as Dumper - from yaml import CLoader as Loader -except ImportError: - from yaml import Loader, Dumper - -from .base import BaseFileHandler # isort:skip - - -class YamlHandler(BaseFileHandler): - - def load_from_fileobj(self, file, **kwargs): - kwargs.setdefault('Loader', Loader) - return yaml.load(file, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('Dumper', Dumper) - yaml.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('Dumper', Dumper) - return yaml.dump(obj, **kwargs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/io.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/io.py deleted file mode 100644 index aaefde58a..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/io.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from io import BytesIO, StringIO -from pathlib import Path - -from ..utils import is_list_of, is_str -from .file_client import FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler - -file_handlers = { - 'json': JsonHandler(), - 'yaml': YamlHandler(), - 'yml': YamlHandler(), - 'pickle': PickleHandler(), - 'pkl': PickleHandler() -} - - -def load(file, file_format=None, file_client_args=None, **kwargs): - """Load data from json/yaml/pickle files. - - This method provides a unified api for loading data from serialized files. - - Note: - In v1.3.16 and later, ``load`` supports loading data from serialized - files those can be storaged in different backends. - - Args: - file (str or :obj:`Path` or file-like object): Filename or a file-like - object. - file_format (str, optional): If not specified, the file format will be - inferred from the file extension, otherwise use the specified one. - Currently supported formats include "json", "yaml/yml" and - "pickle/pkl". - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> load('/path/of/your/file') # file is storaged in disk - >>> load('https://path/of/your/file') # file is storaged in Internet - >>> load('s3://path/of/your/file') # file is storaged in petrel - - Returns: - The content from the file. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None and is_str(file): - file_format = file.split('.')[-1] - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO(file_client.get_text(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - else: - with BytesIO(file_client.get(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - elif hasattr(file, 'read'): - obj = handler.load_from_fileobj(file, **kwargs) - else: - raise TypeError('"file" must be a filepath str or a file-object') - return obj - - -def dump(obj, file=None, file_format=None, file_client_args=None, **kwargs): - """Dump data to json/yaml/pickle strings or files. - - This method provides a unified api for dumping data as strings or to files, - and also supports custom arguments for each file format. - - Note: - In v1.3.16 and later, ``dump`` supports dumping data as strings or to - files which is saved to different backends. - - Args: - obj (any): The python object to be dumped. - file (str or :obj:`Path` or file-like object, optional): If not - specified, then the object is dumped to a str, otherwise to a file - specified by the filename or file-like object. - file_format (str, optional): Same as :func:`load`. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dump('hello world', '/path/of/your/file') # disk - >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel - - Returns: - bool: True for success, False otherwise. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None: - if is_str(file): - file_format = file.split('.')[-1] - elif file is None: - raise ValueError( - 'file_format must be specified since file is None') - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if file is None: - return handler.dump_to_str(obj, **kwargs) - elif is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put_text(f.getvalue(), file) - else: - with BytesIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put(f.getvalue(), file) - elif hasattr(file, 'write'): - handler.dump_to_fileobj(obj, file, **kwargs) - else: - raise TypeError('"file" must be a filename str or a file-object') - - -def _register_handler(handler, file_formats): - """Register a handler for some file extensions. - - Args: - handler (:obj:`BaseFileHandler`): Handler to be registered. - file_formats (str or list[str]): File formats to be handled by this - handler. - """ - if not isinstance(handler, BaseFileHandler): - raise TypeError( - f'handler must be a child of BaseFileHandler, not {type(handler)}') - if isinstance(file_formats, str): - file_formats = [file_formats] - if not is_list_of(file_formats, str): - raise TypeError('file_formats must be a str or a list of str') - for ext in file_formats: - file_handlers[ext] = handler - - -def register_handler(file_formats, **kwargs): - - def wrap(cls): - _register_handler(cls(**kwargs), file_formats) - return cls - - return wrap diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/parse.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/parse.py deleted file mode 100644 index f60f0d611..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/fileio/parse.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from io import StringIO - -from .file_client import FileClient - - -def list_from_file(filename, - prefix='', - offset=0, - max_num=0, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a list of strings. - - Note: - In v1.3.16 and later, ``list_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a list for strings. - - Args: - filename (str): Filename. - prefix (str): The prefix to be inserted to the beginning of each item. - offset (int): The offset of lines. - max_num (int): The maximum number of lines to be read, - zeros and negatives mean no limitation. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> list_from_file('/path/of/your/file') # disk - ['hello', 'world'] - >>> list_from_file('s3://path/of/your/file') # ceph or petrel - ['hello', 'world'] - - Returns: - list[str]: A list of strings. - """ - cnt = 0 - item_list = [] - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for _ in range(offset): - f.readline() - for line in f: - if 0 < max_num <= cnt: - break - item_list.append(prefix + line.rstrip('\n\r')) - cnt += 1 - return item_list - - -def dict_from_file(filename, - key_type=str, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a dict. - - Each line of the text file will be two or more columns split by - whitespaces or tabs. The first column will be parsed as dict keys, and - the following columns will be parsed as dict values. - - Note: - In v1.3.16 and later, ``dict_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a dict. - - Args: - filename(str): Filename. - key_type(type): Type of the dict keys. str is user by default and - type conversion will be performed if specified. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dict_from_file('/path/of/your/file') # disk - {'key1': 'value1', 'key2': 'value2'} - >>> dict_from_file('s3://path/of/your/file') # ceph or petrel - {'key1': 'value1', 'key2': 'value2'} - - Returns: - dict: The parsed contents. - """ - mapping = {} - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for line in f: - items = line.rstrip('\n').split() - assert len(items) >= 2 - key = key_type(items[0]) - val = items[1:] if len(items) > 2 else items[1] - mapping[key] = val - return mapping diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/__init__.py deleted file mode 100644 index d0051d609..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, - gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, - rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) -from .geometric import (cutout, imcrop, imflip, imflip_, impad, - impad_to_multiple, imrescale, imresize, imresize_like, - imresize_to_multiple, imrotate, imshear, imtranslate, - rescale_size) -from .io import imfrombytes, imread, imwrite, supported_backends, use_backend -from .misc import tensor2imgs -from .photometric import (adjust_brightness, adjust_color, adjust_contrast, - adjust_lighting, adjust_sharpness, auto_contrast, - clahe, imdenormalize, imequalize, iminvert, - imnormalize, imnormalize_, lut_transform, posterize, - solarize) - -__all__ = [ - 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', - 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', - 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', - 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', - 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', - 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', - 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', - 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', - 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', - 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/colorspace.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/colorspace.py deleted file mode 100644 index 4337720ea..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/colorspace.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def imconvert(img, src, dst): - """Convert an image from the src colorspace to dst colorspace. - - Args: - img (ndarray): The input image. - src (str): The source colorspace, e.g., 'rgb', 'hsv'. - dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. - - Returns: - ndarray: The converted image. - """ - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - out_img = cv2.cvtColor(img, code) - return out_img - - -def bgr2gray(img, keepdim=False): - """Convert a BGR image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def rgb2gray(img, keepdim=False): - """Convert a RGB image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def gray2bgr(img): - """Convert a grayscale image to BGR image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted BGR image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return out_img - - -def gray2rgb(img): - """Convert a grayscale image to RGB image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted RGB image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) - return out_img - - -def _convert_input_type_range(img): - """Convert the type and range of the input image. - - It converts the input image to np.float32 type and range of [0, 1]. - It is mainly used for pre-processing the input image in colorspace - conversion functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - (ndarray): The converted image with type of np.float32 and range of - [0, 1]. - """ - img_type = img.dtype - img = img.astype(np.float32) - if img_type == np.float32: - pass - elif img_type == np.uint8: - img /= 255. - else: - raise TypeError('The img type should be np.float32 or np.uint8, ' - f'but got {img_type}') - return img - - -def _convert_output_type_range(img, dst_type): - """Convert the type and range of the image according to dst_type. - - It converts the image to desired type and range. If `dst_type` is np.uint8, - images will be converted to np.uint8 type with range [0, 255]. If - `dst_type` is np.float32, it converts the image to np.float32 type with - range [0, 1]. - It is mainly used for post-processing images in colorspace conversion - functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The image to be converted with np.float32 type and - range [0, 255]. - dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it - converts the image to np.uint8 type with range [0, 255]. If - dst_type is np.float32, it converts the image to np.float32 type - with range [0, 1]. - - Returns: - (ndarray): The converted image with desired type and range. - """ - if dst_type not in (np.uint8, np.float32): - raise TypeError('The dst_type should be np.float32 or np.uint8, ' - f'but got {dst_type}') - if dst_type == np.uint8: - img = img.round() - else: - img /= 255. - return img.astype(dst_type) - - -def rgb2ycbcr(img, y_only=False): - """Convert a RGB image to YCbCr image. - - This function produces the same results as Matlab's `rgb2ycbcr` function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 - else: - out_img = np.matmul( - img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], - [24.966, 112.0, -18.214]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def bgr2ycbcr(img, y_only=False): - """Convert a BGR image to YCbCr image. - - The bgr version of rgb2ycbcr. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 - else: - out_img = np.matmul( - img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], - [65.481, -37.797, 112.0]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2rgb(img): - """Convert a YCbCr image to RGB image. - - This function produces the same results as Matlab's ycbcr2rgb function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted RGB image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0, -0.00153632, 0.00791071], - [0.00625893, -0.00318811, 0]]) * 255.0 + [ - -222.921, 135.576, -276.836 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2bgr(img): - """Convert a YCbCr image to BGR image. - - The bgr version of ycbcr2rgb. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted BGR image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0.00791071, -0.00153632, 0], - [0, -0.00318811, 0.00625893]]) * 255.0 + [ - -276.836, 135.576, -222.921 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def convert_color_factory(src, dst): - - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - - def convert_color(img): - out_img = cv2.cvtColor(img, code) - return out_img - - convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} - image. - - Args: - img (ndarray or str): The input image. - - Returns: - ndarray: The converted {dst.upper()} image. - """ - - return convert_color - - -bgr2rgb = convert_color_factory('bgr', 'rgb') - -rgb2bgr = convert_color_factory('rgb', 'bgr') - -bgr2hsv = convert_color_factory('bgr', 'hsv') - -hsv2bgr = convert_color_factory('hsv', 'bgr') - -bgr2hls = convert_color_factory('bgr', 'hls') - -hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/geometric.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/geometric.py deleted file mode 100644 index 4c423bf2a..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/geometric.py +++ /dev/null @@ -1,741 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers - -import cv2 -import numpy as np - -from ..utils import to_2tuple -from .io import imread_backend - -try: - from PIL import Image -except ImportError: - Image = None - - -def _scale_size(size, scale): - """Rescale a size by a ratio. - - Args: - size (tuple[int]): (w, h). - scale (float | tuple(float)): Scaling factor. - - Returns: - tuple[int]: scaled size. - """ - if isinstance(scale, (float, int)): - scale = (scale, scale) - w, h = size - return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) - - -cv2_interp_codes = { - 'nearest': cv2.INTER_NEAREST, - 'bilinear': cv2.INTER_LINEAR, - 'bicubic': cv2.INTER_CUBIC, - 'area': cv2.INTER_AREA, - 'lanczos': cv2.INTER_LANCZOS4 -} - -# Pillow >=v9.1.0 use a slightly different naming scheme for filters. -# Set pillow_interp_codes according to the naming scheme used. -if Image is not None: - if hasattr(Image, 'Resampling'): - pillow_interp_codes = { - 'nearest': Image.Resampling.NEAREST, - 'bilinear': Image.Resampling.BILINEAR, - 'bicubic': Image.Resampling.BICUBIC, - 'box': Image.Resampling.BOX, - 'lanczos': Image.Resampling.LANCZOS, - 'hamming': Image.Resampling.HAMMING - } - else: - pillow_interp_codes = { - 'nearest': Image.NEAREST, - 'bilinear': Image.BILINEAR, - 'bicubic': Image.BICUBIC, - 'box': Image.BOX, - 'lanczos': Image.LANCZOS, - 'hamming': Image.HAMMING - } - - -def imresize(img, - size, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image to a given size. - - Args: - img (ndarray): The input image. - size (tuple[int]): Target size (w, h). - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if backend is None: - backend = imread_backend - if backend not in ['cv2', 'pillow']: - raise ValueError(f'backend: {backend} is not supported for resize.' - f"Supported backends are 'cv2', 'pillow'") - - if backend == 'pillow': - assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' - pil_image = Image.fromarray(img) - pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) - resized_img = np.array(pil_image) - else: - resized_img = cv2.resize( - img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) - if not return_scale: - return resized_img - else: - w_scale = size[0] / w - h_scale = size[1] / h - return resized_img, w_scale, h_scale - - -def imresize_to_multiple(img, - divisor, - size=None, - scale_factor=None, - keep_ratio=False, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image according to a given size or scale factor and then rounds - up the the resized or rescaled image size to the nearest value that can be - divided by the divisor. - - Args: - img (ndarray): The input image. - divisor (int | tuple): Resized image size will be a multiple of - divisor. If divisor is a tuple, divisor should be - (w_divisor, h_divisor). - size (None | int | tuple[int]): Target size (w, h). Default: None. - scale_factor (None | float | tuple[float]): Multiplier for spatial - size. Should match input size if it is a tuple and the 2D style is - (w_scale_factor, h_scale_factor). Default: None. - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: False. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if size is not None and scale_factor is not None: - raise ValueError('only one of size or scale_factor should be defined') - elif size is None and scale_factor is None: - raise ValueError('one of size or scale_factor should be defined') - elif size is not None: - size = to_2tuple(size) - if keep_ratio: - size = rescale_size((w, h), size, return_scale=False) - else: - size = _scale_size((w, h), scale_factor) - - divisor = to_2tuple(divisor) - size = tuple([int(np.ceil(s / d)) * d for s, d in zip(size, divisor)]) - resized_img, w_scale, h_scale = imresize( - img, - size, - return_scale=True, - interpolation=interpolation, - out=out, - backend=backend) - if return_scale: - return resized_img, w_scale, h_scale - else: - return resized_img - - -def imresize_like(img, - dst_img, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image to the same size of a given image. - - Args: - img (ndarray): The input image. - dst_img (ndarray): The target image. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = dst_img.shape[:2] - return imresize(img, (w, h), return_scale, interpolation, backend=backend) - - -def rescale_size(old_size, scale, return_scale=False): - """Calculate the new size to be rescaled to. - - Args: - old_size (tuple[int]): The old size (w, h) of image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image size. - - Returns: - tuple[int]: The new rescaled image size. - """ - w, h = old_size - if isinstance(scale, (float, int)): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - scale_factor = scale - elif isinstance(scale, tuple): - max_long_edge = max(scale) - max_short_edge = min(scale) - scale_factor = min(max_long_edge / max(h, w), - max_short_edge / min(h, w)) - else: - raise TypeError( - f'Scale must be a number or tuple of int, but got {type(scale)}') - - new_size = _scale_size((w, h), scale_factor) - - if return_scale: - return new_size, scale_factor - else: - return new_size - - -def imrescale(img, - scale, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image while keeping the aspect ratio. - - Args: - img (ndarray): The input image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - ndarray: The rescaled image. - """ - h, w = img.shape[:2] - new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) - rescaled_img = imresize( - img, new_size, interpolation=interpolation, backend=backend) - if return_scale: - return rescaled_img, scale_factor - else: - return rescaled_img - - -def imflip(img, direction='horizontal'): - """Flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image. - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return np.flip(img, axis=1) - elif direction == 'vertical': - return np.flip(img, axis=0) - else: - return np.flip(img, axis=(0, 1)) - - -def imflip_(img, direction='horizontal'): - """Inplace flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image (inplace). - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return cv2.flip(img, 1, img) - elif direction == 'vertical': - return cv2.flip(img, 0, img) - else: - return cv2.flip(img, -1, img) - - -def imrotate(img, - angle, - center=None, - scale=1.0, - border_value=0, - interpolation='bilinear', - auto_bound=False): - """Rotate an image. - - Args: - img (ndarray): Image to be rotated. - angle (float): Rotation angle in degrees, positive values mean - clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. - scale (float): Isotropic scale factor. - border_value (int): Border value. - interpolation (str): Same as :func:`resize`. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. - - Returns: - ndarray: The rotated image. - """ - if center is not None and auto_bound: - raise ValueError('`auto_bound` conflicts with `center`') - h, w = img.shape[:2] - if center is None: - center = ((w - 1) * 0.5, (h - 1) * 0.5) - assert isinstance(center, tuple) - - matrix = cv2.getRotationMatrix2D(center, -angle, scale) - if auto_bound: - cos = np.abs(matrix[0, 0]) - sin = np.abs(matrix[0, 1]) - new_w = h * sin + w * cos - new_h = h * cos + w * sin - matrix[0, 2] += (new_w - w) * 0.5 - matrix[1, 2] += (new_h - h) * 0.5 - w = int(np.round(new_w)) - h = int(np.round(new_h)) - rotated = cv2.warpAffine( - img, - matrix, (w, h), - flags=cv2_interp_codes[interpolation], - borderValue=border_value) - return rotated - - -def bbox_clip(bboxes, img_shape): - """Clip bboxes to fit the image shape. - - Args: - bboxes (ndarray): Shape (..., 4*k) - img_shape (tuple[int]): (height, width) of the image. - - Returns: - ndarray: Clipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) - cmin[0::2] = img_shape[1] - 1 - cmin[1::2] = img_shape[0] - 1 - clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) - return clipped_bboxes - - -def bbox_scaling(bboxes, scale, clip_shape=None): - """Scaling bboxes w.r.t the box center. - - Args: - bboxes (ndarray): Shape(..., 4). - scale (float): Scaling factor. - clip_shape (tuple[int], optional): If specified, bboxes that exceed the - boundary will be clipped according to the given shape (h, w). - - Returns: - ndarray: Scaled bboxes. - """ - if float(scale) == 1.0: - scaled_bboxes = bboxes.copy() - else: - w = bboxes[..., 2] - bboxes[..., 0] + 1 - h = bboxes[..., 3] - bboxes[..., 1] + 1 - dw = (w * (scale - 1)) * 0.5 - dh = (h * (scale - 1)) * 0.5 - scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) - if clip_shape is not None: - return bbox_clip(scaled_bboxes, clip_shape) - else: - return scaled_bboxes - - -def imcrop(img, bboxes, scale=1.0, pad_fill=None): - """Crop image patches. - - 3 steps: scale the bboxes -> clip bboxes -> crop and pad. - - Args: - img (ndarray): Image to be cropped. - bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. - scale (float, optional): Scale ratio of bboxes, the default value - 1.0 means no padding. - pad_fill (Number | list[Number]): Value to be filled for padding. - Default: None, which means no padding. - - Returns: - list[ndarray] | ndarray: The cropped image patches. - """ - chn = 1 if img.ndim == 2 else img.shape[2] - if pad_fill is not None: - if isinstance(pad_fill, (int, float)): - pad_fill = [pad_fill for _ in range(chn)] - assert len(pad_fill) == chn - - _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes - scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) - clipped_bbox = bbox_clip(scaled_bboxes, img.shape) - - patches = [] - for i in range(clipped_bbox.shape[0]): - x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) - if pad_fill is None: - patch = img[y1:y2 + 1, x1:x2 + 1, ...] - else: - _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) - if chn == 1: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) - else: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) - patch = np.array( - pad_fill, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - x_start = 0 if _x1 >= 0 else -_x1 - y_start = 0 if _y1 >= 0 else -_y1 - w = x2 - x1 + 1 - h = y2 - y1 + 1 - patch[y_start:y_start + h, x_start:x_start + w, - ...] = img[y1:y1 + h, x1:x1 + w, ...] - patches.append(patch) - - if bboxes.ndim == 1: - return patches[0] - else: - return patches - - -def impad(img, - *, - shape=None, - padding=None, - pad_val=0, - padding_mode='constant'): - """Pad the given image to a certain shape or pad on all sides with - specified padding mode and padding value. - - Args: - img (ndarray): Image to be padded. - shape (tuple[int]): Expected padding shape (h, w). Default: None. - padding (int or tuple[int]): Padding on each border. If a single int is - provided this is used to pad all borders. If tuple of length 2 is - provided this is the padding on left/right and top/bottom - respectively. If a tuple of length 4 is provided this is the - padding for the left, top, right and bottom borders respectively. - Default: None. Note that `shape` and `padding` can not be both - set. - pad_val (Number | Sequence[Number]): Values to be filled in padding - areas when padding_mode is 'constant'. Default: 0. - padding_mode (str): Type of padding. Should be: constant, edge, - reflect or symmetric. Default: constant. - - constant: pads with a constant value, this value is specified - with pad_val. - - edge: pads with the last value at the edge of the image. - - reflect: pads with reflection of image without repeating the last - value on the edge. For example, padding [1, 2, 3, 4] with 2 - elements on both sides in reflect mode will result in - [3, 2, 1, 2, 3, 4, 3, 2]. - - symmetric: pads with reflection of image repeating the last value - on the edge. For example, padding [1, 2, 3, 4] with 2 elements on - both sides in symmetric mode will result in - [2, 1, 1, 2, 3, 4, 4, 3] - - Returns: - ndarray: The padded image. - """ - - assert (shape is not None) ^ (padding is not None) - if shape is not None: - width = max(shape[1] - img.shape[1], 0) - height = max(shape[0] - img.shape[0], 0) - padding = (0, 0, width, height) - - # check pad_val - if isinstance(pad_val, tuple): - assert len(pad_val) == img.shape[-1] - elif not isinstance(pad_val, numbers.Number): - raise TypeError('pad_val must be a int or a tuple. ' - f'But received {type(pad_val)}') - - # check padding - if isinstance(padding, tuple) and len(padding) in [2, 4]: - if len(padding) == 2: - padding = (padding[0], padding[1], padding[0], padding[1]) - elif isinstance(padding, numbers.Number): - padding = (padding, padding, padding, padding) - else: - raise ValueError('Padding must be a int or a 2, or 4 element tuple.' - f'But received {padding}') - - # check padding mode - assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] - - border_type = { - 'constant': cv2.BORDER_CONSTANT, - 'edge': cv2.BORDER_REPLICATE, - 'reflect': cv2.BORDER_REFLECT_101, - 'symmetric': cv2.BORDER_REFLECT - } - img = cv2.copyMakeBorder( - img, - padding[1], - padding[3], - padding[0], - padding[2], - border_type[padding_mode], - value=pad_val) - - return img - - -def impad_to_multiple(img, divisor, pad_val=0): - """Pad an image to ensure each edge to be multiple to some number. - - Args: - img (ndarray): Image to be padded. - divisor (int): Padded image edges will be multiple to divisor. - pad_val (Number | Sequence[Number]): Same as :func:`impad`. - - Returns: - ndarray: The padded image. - """ - pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor - pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor - return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) - - -def cutout(img, shape, pad_val=0): - """Randomly cut out a rectangle from the original img. - - Args: - img (ndarray): Image to be cutout. - shape (int | tuple[int]): Expected cutout shape (h, w). If given as a - int, the value will be used for both h and w. - pad_val (int | float | tuple[int | float]): Values to be filled in the - cut area. Defaults to 0. - - Returns: - ndarray: The cutout image. - """ - - channels = 1 if img.ndim == 2 else img.shape[2] - if isinstance(shape, int): - cut_h, cut_w = shape, shape - else: - assert isinstance(shape, tuple) and len(shape) == 2, \ - f'shape must be a int or a tuple with length 2, but got type ' \ - f'{type(shape)} instead.' - cut_h, cut_w = shape - if isinstance(pad_val, (int, float)): - pad_val = tuple([pad_val] * channels) - elif isinstance(pad_val, tuple): - assert len(pad_val) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(pad_val), channels) - else: - raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') - - img_h, img_w = img.shape[:2] - y0 = np.random.uniform(img_h) - x0 = np.random.uniform(img_w) - - y1 = int(max(0, y0 - cut_h / 2.)) - x1 = int(max(0, x0 - cut_w / 2.)) - y2 = min(img_h, y1 + cut_h) - x2 = min(img_w, x1 + cut_w) - - if img.ndim == 2: - patch_shape = (y2 - y1, x2 - x1) - else: - patch_shape = (y2 - y1, x2 - x1, channels) - - img_cutout = img.copy() - patch = np.array( - pad_val, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - img_cutout[y1:y2, x1:x2, ...] = patch - - return img_cutout - - -def _get_shear_matrix(magnitude, direction='horizontal'): - """Generate the shear matrix for transformation. - - Args: - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - - Returns: - ndarray: The shear matrix with dtype float32. - """ - if direction == 'horizontal': - shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) - elif direction == 'vertical': - shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) - return shear_matrix - - -def imshear(img, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear an image. - - Args: - img (ndarray): Image to be sheared with format (h, w) - or (h, w, c). - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The sheared image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`') - shear_matrix = _get_shear_matrix(magnitude, direction) - sheared = cv2.warpAffine( - img, - shear_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. shearing masks whose channels large - # than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return sheared - - -def _get_translate_matrix(offset, direction='horizontal'): - """Generate the translate matrix. - - Args: - offset (int | float): The offset used for translate. - direction (str): The translate direction, either - "horizontal" or "vertical". - - Returns: - ndarray: The translate matrix with dtype float32. - """ - if direction == 'horizontal': - translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) - elif direction == 'vertical': - translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) - return translate_matrix - - -def imtranslate(img, - offset, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Translate an image. - - Args: - img (ndarray): Image to be translated with format - (h, w) or (h, w, c). - offset (int | float): The offset used for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The translated image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`.') - translate_matrix = _get_translate_matrix(offset, direction) - translated = cv2.warpAffine( - img, - translate_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. translating masks whose channels - # large than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return translated diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/io.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/io.py deleted file mode 100644 index ae81b561a..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/io.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os.path as osp -import warnings -from pathlib import Path - -import cv2 -import numpy as np -from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, - IMREAD_UNCHANGED) - -from mmcv.fileio import FileClient -from mmcv.utils import is_filepath, is_str - -try: - from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG -except ImportError: - TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None - -try: - from PIL import Image, ImageOps -except ImportError: - Image = None - -try: - import tifffile -except ImportError: - tifffile = None - -jpeg = None -supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] - -imread_flags = { - 'color': IMREAD_COLOR, - 'grayscale': IMREAD_GRAYSCALE, - 'unchanged': IMREAD_UNCHANGED, - 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, - 'grayscale_ignore_orientation': - IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE -} - -imread_backend = 'cv2' - - -def use_backend(backend): - """Select a backend for image decoding. - - Args: - backend (str): The image decoding backend type. Options are `cv2`, - `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) - and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` - file format. - """ - assert backend in supported_backends - global imread_backend - imread_backend = backend - if imread_backend == 'turbojpeg': - if TurboJPEG is None: - raise ImportError('`PyTurboJPEG` is not installed') - global jpeg - if jpeg is None: - jpeg = TurboJPEG() - elif imread_backend == 'pillow': - if Image is None: - raise ImportError('`Pillow` is not installed') - elif imread_backend == 'tifffile': - if tifffile is None: - raise ImportError('`tifffile` is not installed') - - -def _jpegflag(flag='color', channel_order='bgr'): - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'color': - if channel_order == 'bgr': - return TJPF_BGR - elif channel_order == 'rgb': - return TJCS_RGB - elif flag == 'grayscale': - return TJPF_GRAY - else: - raise ValueError('flag must be "color" or "grayscale"') - - -def _pillow2array(img, flag='color', channel_order='bgr'): - """Convert a pillow image to numpy array. - - Args: - img (:obj:`PIL.Image.Image`): The image loaded using PIL - flag (str): Flags specifying the color type of a loaded image, - candidates are 'color', 'grayscale' and 'unchanged'. - Default to 'color'. - channel_order (str): The channel order of the output image array, - candidates are 'bgr' and 'rgb'. Default to 'bgr'. - - Returns: - np.ndarray: The converted numpy array - """ - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'unchanged': - array = np.array(img) - if array.ndim >= 3 and array.shape[2] >= 3: # color image - array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR - else: - # Handle exif orientation tag - if flag in ['color', 'grayscale']: - img = ImageOps.exif_transpose(img) - # If the image mode is not 'RGB', convert it to 'RGB' first. - if img.mode != 'RGB': - if img.mode != 'LA': - # Most formats except 'LA' can be directly converted to RGB - img = img.convert('RGB') - else: - # When the mode is 'LA', the default conversion will fill in - # the canvas with black, which sometimes shadows black objects - # in the foreground. - # - # Therefore, a random color (124, 117, 104) is used for canvas - img_rgba = img.convert('RGBA') - img = Image.new('RGB', img_rgba.size, (124, 117, 104)) - img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha - if flag in ['color', 'color_ignore_orientation']: - array = np.array(img) - if channel_order != 'rgb': - array = array[:, :, ::-1] # RGB to BGR - elif flag in ['grayscale', 'grayscale_ignore_orientation']: - img = img.convert('L') - array = np.array(img) - else: - raise ValueError( - 'flag must be "color", "grayscale", "unchanged", ' - f'"color_ignore_orientation" or "grayscale_ignore_orientation"' - f' but got {flag}') - return array - - -def imread(img_or_path, - flag='color', - channel_order='bgr', - backend=None, - file_client_args=None): - """Read an image. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Args: - img_or_path (ndarray or str or Path): Either a numpy array or str or - pathlib.Path. If it is a numpy array (loaded image), then - it will be returned as is. - flag (str): Flags specifying the color type of a loaded image, - candidates are `color`, `grayscale`, `unchanged`, - `color_ignore_orientation` and `grayscale_ignore_orientation`. - By default, `cv2` and `pillow` backend would rotate the image - according to its EXIF info unless called with `unchanged` or - `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend - always ignore image's EXIF info regardless of the flag. - The `turbojpeg` backend only supports `color` and `grayscale`. - channel_order (str): Order of channel, candidates are `bgr` and `rgb`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. - If backend is None, the global imread_backend specified by - ``mmcv.use_backend()`` will be used. Default: None. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> import mmcv - >>> img_path = '/path/to/img.jpg' - >>> img = mmcv.imread(img_path) - >>> img = mmcv.imread(img_path, flag='color', channel_order='rgb', - ... backend='cv2') - >>> img = mmcv.imread(img_path, flag='color', channel_order='bgr', - ... backend='pillow') - >>> s3_img_path = 's3://bucket/img.jpg' - >>> # infer the file backend by the prefix s3 - >>> img = mmcv.imread(s3_img_path) - >>> # manually set the file backend petrel - >>> img = mmcv.imread(s3_img_path, file_client_args={ - ... 'backend': 'petrel'}) - >>> http_img_path = 'http://path/to/img.jpg' - >>> img = mmcv.imread(http_img_path) - >>> img = mmcv.imread(http_img_path, file_client_args={ - ... 'backend': 'http'}) - """ - - if isinstance(img_or_path, Path): - img_or_path = str(img_or_path) - - if isinstance(img_or_path, np.ndarray): - return img_or_path - elif is_str(img_or_path): - file_client = FileClient.infer_client(file_client_args, img_or_path) - img_bytes = file_client.get(img_or_path) - return imfrombytes(img_bytes, flag, channel_order, backend) - else: - raise TypeError('"img" must be a numpy array or a str or ' - 'a pathlib.Path object') - - -def imfrombytes(content, flag='color', channel_order='bgr', backend=None): - """Read an image from bytes. - - Args: - content (bytes): Image bytes got from files or other streams. - flag (str): Same as :func:`imread`. - channel_order (str): The channel order of the output, candidates - are 'bgr' and 'rgb'. Default to 'bgr'. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. If backend is - None, the global imread_backend specified by ``mmcv.use_backend()`` - will be used. Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> img_path = '/path/to/img.jpg' - >>> with open(img_path, 'rb') as f: - >>> img_buff = f.read() - >>> img = mmcv.imfrombytes(img_buff) - >>> img = mmcv.imfrombytes(img_buff, flag='color', channel_order='rgb') - >>> img = mmcv.imfrombytes(img_buff, backend='pillow') - >>> img = mmcv.imfrombytes(img_buff, backend='cv2') - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError( - f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow', 'tifffile'") - if backend == 'turbojpeg': - img = jpeg.decode(content, _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - with io.BytesIO(content) as buff: - img = Image.open(buff) - img = _pillow2array(img, flag, channel_order) - return img - elif backend == 'tifffile': - with io.BytesIO(content) as buff: - img = tifffile.imread(buff) - return img - else: - img_np = np.frombuffer(content, np.uint8) - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imdecode(img_np, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - - -def imwrite(img, - file_path, - params=None, - auto_mkdir=None, - file_client_args=None): - """Write image to file. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Warning: - The parameter `auto_mkdir` will be deprecated in the future and every - file clients will make directory automatically. - - Args: - img (ndarray): Image array to be written. - file_path (str): Image file path. - params (None or list): Same as opencv :func:`imwrite` interface. - auto_mkdir (bool): If the parent folder of `file_path` does not exist, - whether to create it automatically. It will be deprecated. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - bool: Successful or not. - - Examples: - >>> # write to hard disk client - >>> ret = mmcv.imwrite(img, '/path/to/img.jpg') - >>> # infer the file backend by the prefix s3 - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg') - >>> # manually set the file backend petrel - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg', file_client_args={ - ... 'backend': 'petrel'}) - """ - assert is_filepath(file_path) - file_path = str(file_path) - if auto_mkdir is not None: - warnings.warn( - 'The parameter `auto_mkdir` will be deprecated in the future and ' - 'every file clients will make directory automatically.') - file_client = FileClient.infer_client(file_client_args, file_path) - img_ext = osp.splitext(file_path)[-1] - # Encode image according to image suffix. - # For example, if image path is '/path/your/img.jpg', the encode - # format is '.jpg'. - flag, img_buff = cv2.imencode(img_ext, img, params) - file_client.put(img_buff.tobytes(), file_path) - return flag diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/misc.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/misc.py deleted file mode 100644 index 43934a689..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/misc.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - -import mmcv - -try: - import torch -except ImportError: - torch = None - - -def tensor2imgs(tensor, mean=None, std=None, to_rgb=True): - """Convert tensor to 3-channel images or 1-channel gray images. - - Args: - tensor (torch.Tensor): Tensor that contains multiple images, shape ( - N, C, H, W). :math:`C` can be either 3 or 1. - mean (tuple[float], optional): Mean of images. If None, - (0, 0, 0) will be used for tensor with 3-channel, - while (0, ) for tensor with 1-channel. Defaults to None. - std (tuple[float], optional): Standard deviation of images. If None, - (1, 1, 1) will be used for tensor with 3-channel, - while (1, ) for tensor with 1-channel. Defaults to None. - to_rgb (bool, optional): Whether the tensor was converted to RGB - format in the first place. If so, convert it back to BGR. - For the tensor with 1 channel, it must be False. Defaults to True. - - Returns: - list[np.ndarray]: A list that contains multiple images. - """ - - if torch is None: - raise RuntimeError('pytorch is not installed') - assert torch.is_tensor(tensor) and tensor.ndim == 4 - channels = tensor.size(1) - assert channels in [1, 3] - if mean is None: - mean = (0, ) * channels - if std is None: - std = (1, ) * channels - assert (channels == len(mean) == len(std) == 3) or \ - (channels == len(mean) == len(std) == 1 and not to_rgb) - - num_imgs = tensor.size(0) - mean = np.array(mean, dtype=np.float32) - std = np.array(std, dtype=np.float32) - imgs = [] - for img_id in range(num_imgs): - img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) - img = mmcv.imdenormalize( - img, mean, std, to_bgr=to_rgb).astype(np.uint8) - imgs.append(np.ascontiguousarray(img)) - return imgs diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/photometric.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/photometric.py deleted file mode 100644 index 5085d0120..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/image/photometric.py +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - -from ..utils import is_tuple_of -from .colorspace import bgr2gray, gray2bgr - - -def imnormalize(img, mean, std, to_rgb=True): - """Normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - img = img.copy().astype(np.float32) - return imnormalize_(img, mean, std, to_rgb) - - -def imnormalize_(img, mean, std, to_rgb=True): - """Inplace normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - # cv2 inplace normalization does not accept uint8 - assert img.dtype != np.uint8 - mean = np.float64(mean.reshape(1, -1)) - stdinv = 1 / np.float64(std.reshape(1, -1)) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - cv2.subtract(img, mean, img) # inplace - cv2.multiply(img, stdinv, img) # inplace - return img - - -def imdenormalize(img, mean, std, to_bgr=True): - assert img.dtype != np.uint8 - mean = mean.reshape(1, -1).astype(np.float64) - std = std.reshape(1, -1).astype(np.float64) - img = cv2.multiply(img, std) # make a copy - cv2.add(img, mean, img) # inplace - if to_bgr: - cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace - return img - - -def iminvert(img): - """Invert (negate) an image. - - Args: - img (ndarray): Image to be inverted. - - Returns: - ndarray: The inverted image. - """ - return np.full_like(img, 255) - img - - -def solarize(img, thr=128): - """Solarize an image (invert all pixel values above a threshold) - - Args: - img (ndarray): Image to be solarized. - thr (int): Threshold for solarizing (0 - 255). - - Returns: - ndarray: The solarized image. - """ - img = np.where(img < thr, img, 255 - img) - return img - - -def posterize(img, bits): - """Posterize an image (reduce the number of bits for each color channel) - - Args: - img (ndarray): Image to be posterized. - bits (int): Number of bits (1 to 8) to use for posterizing. - - Returns: - ndarray: The posterized image. - """ - shift = 8 - bits - img = np.left_shift(np.right_shift(img, shift), shift) - return img - - -def adjust_color(img, alpha=1, beta=None, gamma=0): - r"""It blends the source image and its gray image: - - .. math:: - output = img * alpha + gray\_img * beta + gamma - - Args: - img (ndarray): The input source image. - alpha (int | float): Weight for the source image. Default 1. - beta (int | float): Weight for the converted gray image. - If None, it's assigned the value (1 - `alpha`). - gamma (int | float): Scalar added to each sum. - Same as :func:`cv2.addWeighted`. Default 0. - - Returns: - ndarray: Colored image which has the same size and dtype as input. - """ - gray_img = bgr2gray(img) - gray_img = np.tile(gray_img[..., None], [1, 1, 3]) - if beta is None: - beta = 1 - alpha - colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) - if not colored_img.dtype == np.uint8: - # Note when the dtype of `img` is not the default `np.uint8` - # (e.g. np.float32), the value in `colored_img` got from cv2 - # is not guaranteed to be in range [0, 255], so here clip - # is needed. - colored_img = np.clip(colored_img, 0, 255) - return colored_img - - -def imequalize(img): - """Equalize the image histogram. - - This function applies a non-linear mapping to the input image, - in order to create a uniform distribution of grayscale values - in the output image. - - Args: - img (ndarray): Image to be equalized. - - Returns: - ndarray: The equalized image. - """ - - def _scale_channel(im, c): - """Scale the data in the corresponding channel.""" - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # For computing the step, filter out the nonzeros. - nonzero_histo = histo[histo > 0] - step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 - if not step: - lut = np.array(range(256)) - else: - # Compute the cumulative sum, shifted by step // 2 - # and then normalized by step. - lut = (np.cumsum(histo) + (step // 2)) // step - # Shift lut, prepending with 0. - lut = np.concatenate([[0], lut[:-1]], 0) - # handle potential integer overflow - lut[lut > 255] = 255 - # If step is zero, return the original image. - # Otherwise, index from lut. - return np.where(np.equal(step, 0), im, lut[im]) - - # Scales each channel independently and then stacks - # the result. - s1 = _scale_channel(img, 0) - s2 = _scale_channel(img, 1) - s3 = _scale_channel(img, 2) - equalized_img = np.stack([s1, s2, s3], axis=-1) - return equalized_img.astype(img.dtype) - - -def adjust_brightness(img, factor=1.): - """Adjust image brightness. - - This function controls the brightness of an image. An - enhancement factor of 0.0 gives a black image. - A factor of 1.0 gives the original image. This function - blends the source image and the degenerated black image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be brightened. - factor (float): A value controls the enhancement. - Factor 1.0 returns the original image, lower - factors mean less color (brightness, contrast, - etc), and higher values more. Default 1. - - Returns: - ndarray: The brightened image. - """ - degenerated = np.zeros_like(img) - # Note manually convert the dtype to np.float32, to - # achieve as close results as PIL.ImageEnhance.Brightness. - # Set beta=1-factor, and gamma=0 - brightened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - brightened_img = np.clip(brightened_img, 0, 255) - return brightened_img.astype(img.dtype) - - -def adjust_contrast(img, factor=1.): - """Adjust image contrast. - - This function controls the contrast of an image. An - enhancement factor of 0.0 gives a solid grey - image. A factor of 1.0 gives the original image. It - blends the source image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be contrasted. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - - Returns: - ndarray: The contrasted image. - """ - gray_img = bgr2gray(img) - hist = np.histogram(gray_img, 256, (0, 255))[0] - mean = round(np.sum(gray_img) / np.sum(hist)) - degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) - degenerated = gray2bgr(degenerated) - contrasted_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - contrasted_img = np.clip(contrasted_img, 0, 255) - return contrasted_img.astype(img.dtype) - - -def auto_contrast(img, cutoff=0): - """Auto adjust image contrast. - - This function maximize (normalize) image contrast by first removing cutoff - percent of the lightest and darkest pixels from the histogram and remapping - the image so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - Args: - img (ndarray): Image to be contrasted. BGR order. - cutoff (int | float | tuple): The cutoff percent of the lightest and - darkest pixels to be removed. If given as tuple, it shall be - (low, high). Otherwise, the single value will be used for both. - Defaults to 0. - - Returns: - ndarray: The contrasted image. - """ - - def _auto_contrast_channel(im, c, cutoff): - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # Remove cut-off percent pixels from histo - histo_sum = np.cumsum(histo) - cut_low = histo_sum[-1] * cutoff[0] // 100 - cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 - histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low - histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) - - # Compute mapping - low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] - # If all the values have been cut off, return the origin img - if low >= high: - return im - scale = 255.0 / (high - low) - offset = -low * scale - lut = np.array(range(256)) - lut = lut * scale + offset - lut = np.clip(lut, 0, 255) - return lut[im] - - if isinstance(cutoff, (int, float)): - cutoff = (cutoff, cutoff) - else: - assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ - f'float or tuple, but got {type(cutoff)} instead.' - # Auto adjusts contrast for each channel independently and then stacks - # the result. - s1 = _auto_contrast_channel(img, 0, cutoff) - s2 = _auto_contrast_channel(img, 1, cutoff) - s3 = _auto_contrast_channel(img, 2, cutoff) - contrasted_img = np.stack([s1, s2, s3], axis=-1) - return contrasted_img.astype(img.dtype) - - -def adjust_sharpness(img, factor=1., kernel=None): - """Adjust image sharpness. - - This function controls the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image. A - factor of 1.0 gives the original image. And a factor - of 2.0 gives a sharpened image. It blends the source - image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be sharpened. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - kernel (np.ndarray, optional): Filter kernel to be applied on the img - to obtain the degenerated img. Defaults to None. - - Note: - No value sanity check is enforced on the kernel set by users. So with - an inappropriate kernel, the ``adjust_sharpness`` may fail to perform - the function its name indicates but end up performing whatever - transform determined by the kernel. - - Returns: - ndarray: The sharpened image. - """ - - if kernel is None: - # adopted from PIL.ImageFilter.SMOOTH - kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 - assert isinstance(kernel, np.ndarray), \ - f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' - assert kernel.ndim == 2, \ - f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' - - degenerated = cv2.filter2D(img, -1, kernel) - sharpened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - sharpened_img = np.clip(sharpened_img, 0, 255) - return sharpened_img.astype(img.dtype) - - -def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): - """AlexNet-style PCA jitter. - - This data augmentation is proposed in `ImageNet Classification with Deep - Convolutional Neural Networks - `_. - - Args: - img (ndarray): Image to be adjusted lighting. BGR order. - eigval (ndarray): the eigenvalue of the convariance matrix of pixel - values, respectively. - eigvec (ndarray): the eigenvector of the convariance matrix of pixel - values, respectively. - alphastd (float): The standard deviation for distribution of alpha. - Defaults to 0.1 - to_rgb (bool): Whether to convert img to rgb. - - Returns: - ndarray: The adjusted image. - """ - assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ - f'eigval and eigvec should both be of type np.ndarray, got ' \ - f'{type(eigval)} and {type(eigvec)} instead.' - - assert eigval.ndim == 1 and eigvec.ndim == 2 - assert eigvec.shape == (3, eigval.shape[0]) - n_eigval = eigval.shape[0] - assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ - f'got {type(alphastd)} instead.' - - img = img.copy().astype(np.float32) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - - alpha = np.random.normal(0, alphastd, n_eigval) - alter = eigvec \ - * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ - * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) - alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) - img_adjusted = img + alter - return img_adjusted - - -def lut_transform(img, lut_table): - """Transform array by look-up table. - - The function lut_transform fills the output array with values from the - look-up table. Indices of the entries are taken from the input array. - - Args: - img (ndarray): Image to be transformed. - lut_table (ndarray): look-up table of 256 elements; in case of - multi-channel input array, the table should either have a single - channel (in this case the same table is used for all channels) or - the same number of channels as in the input array. - - Returns: - ndarray: The transformed image. - """ - assert isinstance(img, np.ndarray) - assert 0 <= np.min(img) and np.max(img) <= 255 - assert isinstance(lut_table, np.ndarray) - assert lut_table.shape == (256, ) - - return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) - - -def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - img (ndarray): Image to be processed. - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - - Returns: - ndarray: The processed image. - """ - assert isinstance(img, np.ndarray) - assert img.ndim == 2 - assert isinstance(clip_limit, (float, int)) - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - - clahe = cv2.createCLAHE(clip_limit, tile_grid_size) - return clahe.apply(np.array(img, dtype=np.uint8)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/deprecated.json b/cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/deprecated.json deleted file mode 100644 index 25cf6f28c..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/deprecated.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "resnet50_caffe": "detectron/resnet50_caffe", - "resnet50_caffe_bgr": "detectron2/resnet50_caffe_bgr", - "resnet101_caffe": "detectron/resnet101_caffe", - "resnet101_caffe_bgr": "detectron2/resnet101_caffe_bgr" -} diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/mmcls.json b/cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/mmcls.json deleted file mode 100644 index c073a41d0..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/mmcls.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "vgg11": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_batch256_imagenet_20210208-4271cd6c.pth", - "vgg13": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_batch256_imagenet_20210208-4d1d6080.pth", - "vgg16": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_batch256_imagenet_20210208-db26f1a5.pth", - "vgg19": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_batch256_imagenet_20210208-e6920e4a.pth", - "vgg11_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_bn_batch256_imagenet_20210207-f244902c.pth", - "vgg13_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_bn_batch256_imagenet_20210207-1a8b7864.pth", - "vgg16_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_bn_batch256_imagenet_20210208-7e55cd29.pth", - "vgg19_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_bn_batch256_imagenet_20210208-da620c4f.pth", - "resnet18": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet18_8xb32_in1k_20210831-fbbb1da6.pth", - "resnet34": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet34_8xb32_in1k_20210831-f257d4e6.pth", - "resnet50": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb32_in1k_20210831-ea4938fc.pth", - "resnet101": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet101_8xb32_in1k_20210831-539c63f8.pth", - "resnet152": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet152_8xb32_in1k_20210901-4d7582fa.pth", - "resnet50_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d50_b32x8_imagenet_20210531-db14775a.pth", - "resnet101_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d101_b32x8_imagenet_20210531-6e13bcd3.pth", - "resnet152_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d152_b32x8_imagenet_20210531-278cf22a.pth", - "resnext50_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext50_32x4d_b32x8_imagenet_20210429-56066e27.pth", - "resnext101_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x4d_b32x8_imagenet_20210506-e0fa3dd5.pth", - "resnext101_32x8d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x8d_b32x8_imagenet_20210506-23a247d5.pth", - "resnext152_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext152_32x4d_b32x8_imagenet_20210524-927787be.pth", - "se-resnet50": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet50_batch256_imagenet_20200804-ae206104.pth", - "se-resnet101": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet101_batch256_imagenet_20200804-ba5b51d4.pth", - "resnest50": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest50_imagenet_converted-1ebf0afe.pth", - "resnest101": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest101_imagenet_converted-032caa52.pth", - "resnest200": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest200_imagenet_converted-581a60f2.pth", - "resnest269": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest269_imagenet_converted-59930960.pth", - "shufflenet_v1": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v1/shufflenet_v1_batch1024_imagenet_20200804-5d6cec73.pth", - "shufflenet_v2": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v2/shufflenet_v2_batch1024_imagenet_20200812-5bf4721e.pth", - "mobilenet_v2": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v2/mobilenet_v2_batch256_imagenet_20200708-3b2dc3af.pth", - "mobilenet_v3_small": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_small-8427ecf0.pth", - "mobilenet_v3_large": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_large-3ea3c186.pth", - "repvgg_A0": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A0_3rdparty_4xb64-coslr-120e_in1k_20210909-883ab98c.pth", - "repvgg_A1": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A1_3rdparty_4xb64-coslr-120e_in1k_20210909-24003a24.pth", - "repvgg_A2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A2_3rdparty_4xb64-coslr-120e_in1k_20210909-97d7695a.pth", - "repvgg_B0": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B0_3rdparty_4xb64-coslr-120e_in1k_20210909-446375f4.pth", - "repvgg_B1": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1_3rdparty_4xb64-coslr-120e_in1k_20210909-750cdf67.pth", - "repvgg_B1g2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1g2_3rdparty_4xb64-coslr-120e_in1k_20210909-344f6422.pth", - "repvgg_B1g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1g4_3rdparty_4xb64-coslr-120e_in1k_20210909-d4c1a642.pth", - "repvgg_B2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B2_3rdparty_4xb64-coslr-120e_in1k_20210909-bd6b937c.pth", - "repvgg_B2g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B2g4_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-7b7955f0.pth", - "repvgg_B3": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B3_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-dda968bf.pth", - "repvgg_B3g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B3g4_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-4e54846a.pth", - "repvgg_D2se": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-D2se_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-cf3139b7.pth", - "res2net101_w26": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net101-w26-s4_3rdparty_8xb32_in1k_20210927-870b6c36.pth", - "res2net50_w14": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net50-w14-s8_3rdparty_8xb32_in1k_20210927-bc967bf1.pth", - "res2net50_w26": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net50-w26-s8_3rdparty_8xb32_in1k_20210927-f547a94b.pth", - "swin_tiny": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_tiny_224_b16x64_300e_imagenet_20210616_090925-66df6be6.pth", - "swin_small": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_small_224_b16x64_300e_imagenet_20210615_110219-7f9d988b.pth", - "swin_base": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/convert/swin_base_patch4_window7_224_22kto1k-f967f799.pth", - "swin_large": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/convert/swin_large_patch4_window7_224_22kto1k-5f0996db.pth", - "t2t_vit_t_14": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-14_3rdparty_8xb64_in1k_20210928-b7c09b62.pth", - "t2t_vit_t_19": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-19_3rdparty_8xb64_in1k_20210928-7f1478d5.pth", - "t2t_vit_t_24": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-24_3rdparty_8xb64_in1k_20210928-fe95a61b.pth", - "tnt_small": "https://download.openmmlab.com/mmclassification/v0/tnt/tnt-small-p16_3rdparty_in1k_20210903-c56ee7df.pth", - "vit_base_p16": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-98e8652b.pth", - "vit_base_p32": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p32_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-9cea8599.pth", - "vit_large_p16": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-large-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-b20ba619.pth" -} diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/open_mmlab.json b/cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/open_mmlab.json deleted file mode 100644 index 8311db4fe..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/open_mmlab.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "vgg16_caffe": "https://download.openmmlab.com/pretrain/third_party/vgg16_caffe-292e1171.pth", - "detectron/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_caffe-788b5fa3.pth", - "detectron2/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_msra-5891d200.pth", - "detectron/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_caffe-3ad79236.pth", - "detectron2/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_msra-6cc46731.pth", - "detectron2/resnext101_32x8d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x8d-1516f1aa.pth", - "resnext50_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext50-32x4d-0ab1a123.pth", - "resnext101_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d-a5af3160.pth", - "resnext101_64x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_64x4d-ee2c6f71.pth", - "contrib/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_thangvubk-ad1730dd.pth", - "detectron/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn-9186a21c.pth", - "detectron/resnet101_gn": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn-cac0ab98.pth", - "jhu/resnet50_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_ws-15beedd8.pth", - "jhu/resnet101_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn_ws-3e3c308c.pth", - "jhu/resnext50_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn_ws-0d87ac85.pth", - "jhu/resnext101_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn_ws-34ac1a9e.pth", - "jhu/resnext50_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn-c7e8b754.pth", - "jhu/resnext101_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn-ac3bb84e.pth", - "msra/hrnetv2_w18_small": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18_small-b5a04e21.pth", - "msra/hrnetv2_w18": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18-00eb2006.pth", - "msra/hrnetv2_w32": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w32-dc9eeb4f.pth", - "msra/hrnetv2_w40": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w40-ed0b031c.pth", - "msra/hrnetv2_w48": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w48-d2186c55.pth", - "bninception_caffe": "https://download.openmmlab.com/pretrain/third_party/bn_inception_caffe-ed2e8665.pth", - "kin400/i3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/i3d_r50_f32s2_k400-2c57e077.pth", - "kin400/nl3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/nl3d_r50_f32s2_k400-fa7e7caa.pth", - "res2net101_v1d_26w_4s": "https://download.openmmlab.com/pretrain/third_party/res2net101_v1d_26w_4s_mmdetv2-f0a600f9.pth", - "regnetx_400mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_400mf-a5b10d96.pth", - "regnetx_800mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_800mf-1f4be4c7.pth", - "regnetx_1.6gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_1.6gf-5791c176.pth", - "regnetx_3.2gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_3.2gf-c2599b0f.pth", - "regnetx_4.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_4.0gf-a88f671e.pth", - "regnetx_6.4gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_6.4gf-006af45d.pth", - "regnetx_8.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_8.0gf-3c68abe7.pth", - "regnetx_12gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_12gf-4c2a3350.pth", - "resnet18_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet18_v1c-b5776b93.pth", - "resnet50_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet50_v1c-2cccc1ad.pth", - "resnet101_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet101_v1c-e67eebb6.pth", - "mmedit/vgg16": "https://download.openmmlab.com/mmediting/third_party/vgg_state_dict.pth", - "mmedit/res34_en_nomixup": "https://download.openmmlab.com/mmediting/third_party/model_best_resnet34_En_nomixup.pth", - "mmedit/mobilenet_v2": "https://download.openmmlab.com/mmediting/third_party/mobilenet_v2.pth", - "contrib/mobilenet_v3_large": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_large-bc2c3fd3.pth", - "contrib/mobilenet_v3_small": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_small-47085aa1.pth", - "resnest50": "https://download.openmmlab.com/pretrain/third_party/resnest50_d2-7497a55b.pth", - "resnest101": "https://download.openmmlab.com/pretrain/third_party/resnest101_d2-f3b931b2.pth", - "resnest200": "https://download.openmmlab.com/pretrain/third_party/resnest200_d2-ca88e41f.pth", - "darknet53": "https://download.openmmlab.com/pretrain/third_party/darknet53-a628ea1b.pth", - "mmdet/mobilenet_v2": "https://download.openmmlab.com/mmdetection/v2.0/third_party/mobilenet_v2_batch256_imagenet-ff34753d.pth" -} diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/torchvision_0.12.json b/cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/torchvision_0.12.json deleted file mode 100644 index 06defe674..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/model_zoo/torchvision_0.12.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "alexnet": "https://download.pytorch.org/models/alexnet-owt-7be5be79.pth", - "densenet121": "https://download.pytorch.org/models/densenet121-a639ec97.pth", - "densenet169": "https://download.pytorch.org/models/densenet169-b2777c0a.pth", - "densenet201": "https://download.pytorch.org/models/densenet201-c1103571.pth", - "densenet161": "https://download.pytorch.org/models/densenet161-8d451a50.pth", - "efficientnet_b0": "https://download.pytorch.org/models/efficientnet_b0_rwightman-3dd342df.pth", - "efficientnet_b1": "https://download.pytorch.org/models/efficientnet_b1_rwightman-533bc792.pth", - "efficientnet_b2": "https://download.pytorch.org/models/efficientnet_b2_rwightman-bcdf34b7.pth", - "efficientnet_b3": "https://download.pytorch.org/models/efficientnet_b3_rwightman-cf984f9c.pth", - "efficientnet_b4": "https://download.pytorch.org/models/efficientnet_b4_rwightman-7eb33cd5.pth", - "efficientnet_b5": "https://download.pytorch.org/models/efficientnet_b5_lukemelas-b6417697.pth", - "efficientnet_b6": "https://download.pytorch.org/models/efficientnet_b6_lukemelas-c76e70fd.pth", - "efficientnet_b7": "https://download.pytorch.org/models/efficientnet_b7_lukemelas-dcc49843.pth", - "googlenet": "https://download.pytorch.org/models/googlenet-1378be20.pth", - "inception_v3_google": "https://download.pytorch.org/models/inception_v3_google-0cc3c7bd.pth", - "mobilenet_v2": "https://download.pytorch.org/models/mobilenet_v2-b0353104.pth", - "mobilenet_v3_large": "https://download.pytorch.org/models/mobilenet_v3_large-8738ca79.pth", - "mobilenet_v3_small": "https://download.pytorch.org/models/mobilenet_v3_small-047dcff4.pth", - "regnet_y_400mf": "https://download.pytorch.org/models/regnet_y_400mf-c65dace8.pth", - "regnet_y_800mf": "https://download.pytorch.org/models/regnet_y_800mf-1b27b58c.pth", - "regnet_y_1_6gf": "https://download.pytorch.org/models/regnet_y_1_6gf-b11a554e.pth", - "regnet_y_3_2gf": "https://download.pytorch.org/models/regnet_y_3_2gf-b5a9779c.pth", - "regnet_y_8gf": "https://download.pytorch.org/models/regnet_y_8gf-d0d0e4a8.pth", - "regnet_y_16gf": "https://download.pytorch.org/models/regnet_y_16gf-9e6ed7dd.pth", - "regnet_y_32gf": "https://download.pytorch.org/models/regnet_y_32gf-4dee3f7a.pth", - "regnet_x_400mf": "https://download.pytorch.org/models/regnet_x_400mf-adf1edd5.pth", - "regnet_x_800mf": "https://download.pytorch.org/models/regnet_x_800mf-ad17e45c.pth", - "regnet_x_1_6gf": "https://download.pytorch.org/models/regnet_x_1_6gf-e3633e7f.pth", - "regnet_x_3_2gf": "https://download.pytorch.org/models/regnet_x_3_2gf-f342aeae.pth", - "regnet_x_8gf": "https://download.pytorch.org/models/regnet_x_8gf-03ceed89.pth", - "regnet_x_16gf": "https://download.pytorch.org/models/regnet_x_16gf-2007eb11.pth", - "regnet_x_32gf": "https://download.pytorch.org/models/regnet_x_32gf-9d47f8d0.pth", - "resnet18": "https://download.pytorch.org/models/resnet18-f37072fd.pth", - "resnet34": "https://download.pytorch.org/models/resnet34-b627a593.pth", - "resnet50": "https://download.pytorch.org/models/resnet50-0676ba61.pth", - "resnet101": "https://download.pytorch.org/models/resnet101-63fe2227.pth", - "resnet152": "https://download.pytorch.org/models/resnet152-394f9c45.pth", - "resnext50_32x4d": "https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth", - "resnext101_32x8d": "https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth", - "wide_resnet50_2": "https://download.pytorch.org/models/wide_resnet50_2-95faca4d.pth", - "wide_resnet101_2": "https://download.pytorch.org/models/wide_resnet101_2-32ee1156.pth", - "shufflenetv2_x0.5": "https://download.pytorch.org/models/shufflenetv2_x0.5-f707e7126e.pth", - "shufflenetv2_x1.0": "https://download.pytorch.org/models/shufflenetv2_x1-5666bf0f80.pth", - "shufflenetv2_x1.5": null, - "shufflenetv2_x2.0": null, - "squeezenet1_0": "https://download.pytorch.org/models/squeezenet1_0-b66bff10.pth", - "squeezenet1_1": "https://download.pytorch.org/models/squeezenet1_1-b8a52dc0.pth", - "vgg11": "https://download.pytorch.org/models/vgg11-8a719046.pth", - "vgg13": "https://download.pytorch.org/models/vgg13-19584684.pth", - "vgg16": "https://download.pytorch.org/models/vgg16-397923af.pth", - "vgg19": "https://download.pytorch.org/models/vgg19-dcbb9e9d.pth", - "vgg11_bn": "https://download.pytorch.org/models/vgg11_bn-6002323d.pth", - "vgg13_bn": "https://download.pytorch.org/models/vgg13_bn-abd245e5.pth", - "vgg16_bn": "https://download.pytorch.org/models/vgg16_bn-6c64b313.pth", - "vgg19_bn": "https://download.pytorch.org/models/vgg19_bn-c79401a0.pth" -} diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/__init__.py deleted file mode 100644 index a12b79cef..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .deprecated_wrappers import Conv2d_deprecated as Conv2d -from .deprecated_wrappers import ConvTranspose2d_deprecated as ConvTranspose2d -from .deprecated_wrappers import Linear_deprecated as Linear -from .deprecated_wrappers import MaxPool2d_deprecated as MaxPool2d -from .focal_loss import (SigmoidFocalLoss, SoftmaxFocalLoss, - sigmoid_focal_loss, softmax_focal_loss) -from .sync_bn import SyncBatchNorm -from .cc_attention import CrissCrossAttention -from .point_sample import * -from .psa_mask import PSAMask, PSAMaskFunction -from .info import * \ No newline at end of file diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/cc_attention.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/cc_attention.py deleted file mode 100644 index 3fd83fcb9..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/cc_attention.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.cnn import PLUGIN_LAYERS, Scale - - -def NEG_INF_DIAG(n, device): - """Returns a diagonal matrix of size [n, n]. - - The diagonal are all "-inf". This is for avoiding calculating the - overlapped element in the Criss-Cross twice. - """ - return torch.diag(torch.tensor(float('-inf')).to(device).repeat(n), 0) - - -@PLUGIN_LAYERS.register_module() -class CrissCrossAttention(nn.Module): - """Criss-Cross Attention Module. - - .. note:: - Before v1.3.13, we use a CUDA op. Since v1.3.13, we switch - to a pure PyTorch and equivalent implementation. For more - details, please refer to https://github.com/open-mmlab/mmcv/pull/1201. - - Speed comparison for one forward pass - - - Input size: [2,512,97,97] - - Device: 1 NVIDIA GeForce RTX 2080 Ti - - +-----------------------+---------------+------------+---------------+ - | |PyTorch version|CUDA version|Relative speed | - +=======================+===============+============+===============+ - |with torch.no_grad() |0.00554402 s |0.0299619 s |5.4x | - +-----------------------+---------------+------------+---------------+ - |no with torch.no_grad()|0.00562803 s |0.0301349 s |5.4x | - +-----------------------+---------------+------------+---------------+ - - Args: - in_channels (int): Channels of the input feature map. - """ - - def __init__(self, in_channels): - super().__init__() - self.query_conv = nn.Conv2d(in_channels, in_channels // 8, 1) - self.key_conv = nn.Conv2d(in_channels, in_channels // 8, 1) - self.value_conv = nn.Conv2d(in_channels, in_channels, 1) - self.gamma = Scale(0.) - self.in_channels = in_channels - - def forward(self, x): - """forward function of Criss-Cross Attention. - - Args: - x (torch.Tensor): Input feature with the shape of - (batch_size, in_channels, height, width). - - Returns: - torch.Tensor: Output of the layer, with the shape of - (batch_size, in_channels, height, width) - """ - B, C, H, W = x.size() - query = self.query_conv(x) - key = self.key_conv(x) - value = self.value_conv(x) - energy_H = torch.einsum('bchw,bciw->bwhi', query, key) + NEG_INF_DIAG( - H, query.device) - energy_H = energy_H.transpose(1, 2) - energy_W = torch.einsum('bchw,bchj->bhwj', query, key) - attn = F.softmax( - torch.cat([energy_H, energy_W], dim=-1), dim=-1) # [B,H,W,(H+W)] - out = torch.einsum('bciw,bhwi->bchw', value, attn[..., :H]) - out += torch.einsum('bchj,bhwj->bchw', value, attn[..., H:]) - - out = self.gamma(out) + x - out = out.contiguous() - - return out - - def __repr__(self): - s = self.__class__.__name__ - s += f'(in_channels={self.in_channels})' - return s diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/README.md b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/README.md deleted file mode 100644 index 3bc020040..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/README.md +++ /dev/null @@ -1,170 +0,0 @@ -# Code Structure of CUDA operators - -This folder contains all non-python code for MMCV custom ops. Please follow the same architecture if you want to add new ops. - -## Directories Tree - -```folder -. -├── common -│ ├── box_iou_rotated_utils.hpp -│ ├── parrots_cpp_helper.hpp -│ ├── parrots_cuda_helper.hpp -│ ├── pytorch_cpp_helper.hpp -│ ├── pytorch_cuda_helper.hpp -│ ├── pytorch_device_registry.hpp -│   └── cuda -│   ├── common_cuda_helper.hpp -│   ├── parrots_cudawarpfunction.cuh -│   ├── ... -│   └── ops_cuda_kernel.cuh -├── onnxruntime -│   ├── onnxruntime_register.h -│   ├── onnxruntime_session_options_config_keys.h -│   ├── ort_mmcv_utils.h -│   ├── ... -│   ├── onnx_ops.h -│   └── cpu -│ ├── onnxruntime_register.cpp -│      ├── ... -│      └── onnx_ops_impl.cpp -├── parrots -│   ├── ... -│   ├── ops.cpp -│   ├── ops_parrots.cpp -│   └── ops_pytorch.h -├── pytorch -│   ├── info.cpp -│   ├── pybind.cpp -│   ├── ... -│   ├── ops.cpp -│   ├── cuda -│   │   ├── ... -│   │   └── ops_cuda.cu -│   └── cpu -│      ├── ... -│      └── ops.cpp -└── tensorrt - ├── trt_cuda_helper.cuh - ├── trt_plugin_helper.hpp - ├── trt_plugin.hpp - ├── trt_serialize.hpp - ├── ... - ├── trt_ops.hpp - └── plugins -    ├── trt_cuda_helper.cu -    ├── trt_plugin.cpp -    ├── ... -    ├── trt_ops.cpp -    └── trt_ops_kernel.cu -``` - -## Components - -- `common`: This directory contains all tools and shared codes. - - `cuda`: The cuda kernels which can be shared by all backends. **HIP** kernel is also here since they have similar syntax. -- `onnxruntime`: **ONNX Runtime** support for custom ops. - - `cpu`: CPU implementation of supported ops. -- `parrots`: **Parrots** is a deep learning frame for model training and inference. Parrots custom ops are placed in this directory. -- `pytorch`: **PyTorch** custom ops are supported by binding C++ to Python with **pybind11**. The ops implementation and binding codes are placed in this directory. - - `cuda`: This directory contains cuda kernel launchers, which feed memory pointers of tensor to the cuda kernel in `common/cuda`. The launchers provide c++ interface of cuda implementation of corresponding custom ops. - - `cpu`: This directory contain cpu implementations of corresponding custom ops. -- `tensorrt`: **TensorRT** support for custom ops. - - `plugins`: This directory contains the implementation of the supported custom ops. Some ops might also use shared cuda kernel in `common/cuda`. - -## How to add new PyTorch ops? - -1. (Optional) Add shared kernel in `common` to support special hardware platform. - - ```c++ - // src/common/cuda/new_ops_cuda_kernel.cuh - - template - __global__ void new_ops_forward_cuda_kernel(const T* input, T* output, ...) { - // forward here - } - - ``` - - Add cuda kernel launcher in `pytorch/cuda`. - - ```c++ - // src/pytorch/cuda - #include - - void NewOpsForwardCUDAKernelLauncher(Tensor input, Tensor output, ...){ - // initialize - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - ... - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "new_ops_forward_cuda_kernel", ([&] { - new_ops_forward_cuda_kernel - <<>>( - input.data_ptr(), output.data_ptr(),...); - })); - AT_CUDA_CHECK(cudaGetLastError()); - } - ``` - -2. Register implementation for different devices. - - ```c++ - // src/pytorch/cuda/cudabind.cpp - ... - - Tensor new_ops_forward_cuda(Tensor input, Tensor output, ...){ - // implement cuda forward here - // use `NewOpsForwardCUDAKernelLauncher` here - } - // declare interface here. - Tensor new_ops_forward_impl(Tensor input, Tensor output, ...); - // register the implementation for given device (CUDA here). - REGISTER_DEVICE_IMPL(new_ops_forward_impl, CUDA, new_ops_forward_cuda); - ``` - -3. Add ops implementation in `pytorch` directory. Select different implementations according to device type. - - ```c++ - // src/pytorch/new_ops.cpp - Tensor new_ops_forward_impl(Tensor input, Tensor output, ...){ - // dispatch the implementation according to the device type of input. - DISPATCH_DEVICE_IMPL(new_ops_forward_impl, input, output, ...); - } - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...){ - return new_ops_forward_impl(input, output, ...); - } - ``` - -4. Binding the implementation in `pytorch/pybind.cpp` - - ```c++ - // src/pytorch/pybind.cpp - - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...); - - ... - - // bind with pybind11 - m.def("new_ops_forward", &new_ops_forward, "new_ops_forward", - py::arg("input"), py::arg("output"), ...); - - ... - - ``` - -5. Build MMCV again. Enjoy new ops in python - - ```python - from ..utils import ext_loader - ext_module = ext_loader.load_ext('_ext', ['new_ops_forward']) - - ... - - ext_module.new_ops_forward(input, output, ...) - - ``` diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp deleted file mode 100644 index e18036bac..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef COMMON_CUDA_HELPER -#define COMMON_CUDA_HELPER - -#include - -#define CUDA_1D_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) - -#define CUDA_2D_KERNEL_LOOP(i, n, j, m) \ - for (size_t i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) \ - for (size_t j = blockIdx.y * blockDim.y + threadIdx.y; j < (m); \ - j += blockDim.y * gridDim.y) - -#define CUDA_2D_KERNEL_BLOCK_LOOP(i, n, j, m) \ - for (size_t i = blockIdx.x; i < (n); i += gridDim.x) \ - for (size_t j = blockIdx.y; j < (m); j += gridDim.y) - -#define THREADS_PER_BLOCK 512 - -inline int GET_BLOCKS(const int N, const int num_threads = THREADS_PER_BLOCK) { - int optimal_block_num = (N + num_threads - 1) / num_threads; - int max_block_num = 4096; - return std::min(optimal_block_num, max_block_num); -} - -template -__device__ T bilinear_interpolate(const T* input, const int height, - const int width, T y, T x, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) return 0; - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - int y_low = (int)y; - int x_low = (int)x; - int y_high; - int x_high; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - // do bilinear interpolation - T v1 = input[y_low * width + x_low]; - T v2 = input[y_low * width + x_high]; - T v3 = input[y_high * width + x_low]; - T v4 = input[y_high * width + x_high]; - T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - return val; -} - -template -__device__ void bilinear_interpolate_gradient( - const int height, const int width, T y, T x, T& w1, T& w2, T& w3, T& w4, - int& x_low, int& x_high, int& y_low, int& y_high, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - w1 = w2 = w3 = w4 = 0.; - x_low = x_high = y_low = y_high = -1; - return; - } - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - y_low = (int)y; - x_low = (int)x; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - - // reference in forward - // T v1 = input[y_low * width + x_low]; - // T v2 = input[y_low * width + x_high]; - // T v3 = input[y_high * width + x_low]; - // T v4 = input[y_high * width + x_high]; - // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - return; -} -#endif // COMMON_CUDA_HELPER diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh deleted file mode 100644 index 5d946686b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef PSAMASK_CUDA_KERNEL_CUH -#define PSAMASK_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -// CUDA: grid stride looping -#ifndef CUDA_KERNEL_LOOP -#define CUDA_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) -#endif - -template -__global__ void psamask_collect_forward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* mask_data, T* buffer_data) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - buffer_data[(n * h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)) * - h_feature * w_feature + - h * w_feature + w] = mask_data - [((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + h) * - w_feature + - w]; - } - } - } -} - -template -__global__ void psamask_distribute_forward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* mask_data, T* buffer_data) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - buffer_data[(n * h_feature * w_feature + h * w_feature + w) * - h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)] = mask_data - [((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + h) * - w_feature + - w]; - } - } - } -} - -template -__global__ void psamask_collect_backward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* buffer_diff, T* mask_diff) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - mask_diff[((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + - h) * - w_feature + - w] = buffer_diff[(n * h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)) * - h_feature * w_feature + - h * w_feature + w]; - } - } - } -} - -template -__global__ void psamask_distribute_backward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* buffer_diff, T* mask_diff) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - mask_diff[((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + - h) * - w_feature + - w] = - buffer_diff[(n * h_feature * w_feature + h * w_feature + w) * - h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)]; - } - } - } -} - -#endif // PSAMASK_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 1eb5f8fcc..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sigmoid_focal_loss_forward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* output, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + expf(-input[index])); - - // (1 - p)**gamma * log(p) - T term_p = pow(((T)1. - p), gamma) * log(max(p, (T)FLT_MIN)); - // p**gamma * log(1 - p) - T term_n = pow(p, gamma) * log(max((T)1. - p, (T)FLT_MIN)); - - output[index] = (T)0.; - output[index] += -flag_p * alpha * term_p; - output[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - output[index] *= weight[t]; - } - } -} - -template -__global__ void sigmoid_focal_loss_backward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* grad_input, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + exp(-input[index])); - - // (1 - p)**gamma * (1 - p - gamma*p*log(p)) - T term_p = pow(((T)1. - p), gamma) * - ((T)1. - p - (gamma * p * log(max(p, (T)FLT_MIN)))); - // p**gamma * (gamma * (1 - p) * log(1 - p) - p) - T term_n = pow(p, gamma) * - (gamma * ((T)1. - p) * log(max((T)1. - p, (T)FLT_MIN)) - p); - - grad_input[index] = (T)0.; - grad_input[index] += -flag_p * alpha * term_p; - grad_input[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - grad_input[index] *= weight[t]; - } - } -} - -#endif // SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 631b2c617..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void softmax_focal_loss_forward_cuda_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* output, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - output[index] = - -alpha * pow((T)1. - pred, gamma) * log(max(pred, (T)FLT_MIN)); - } else { - output[index] = 0; - } - if (weight != NULL) { - output[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda1_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* buff, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - buff[index] = alpha * (-pow((T)1. - pred, gamma) + - gamma * pow((T)1. - pred, gamma - 1) * pred * - log(max(pred, (T)FLT_MIN))); - } else { - buff[index] = 0; - } - if (weight != NULL) { - buff[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda2_kernel( - const int nthreads, const T* softmax, const int64_t* target, const T* buff, - T* grad_input, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - int64_t label = target[n]; - - if (label >= 0) { - T flag = (label == c ? (T)1. : (T)0.); - grad_input[index] = buff[n] * (flag - softmax[index]); - } else { - grad_input[index] = 0; - } - } -} - -#endif // SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh deleted file mode 100644 index 4ec6a4668..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SYNCBN_CUDA_KERNEL_CUH -#define SYNCBN_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sync_bn_forward_mean_cuda_kernel(const T *input, float *mean, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += input[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_mean_cuda_kernel(const phalf *input, - float *mean, int num, - int channels, int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += static_cast(input[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_var_cuda_kernel(const T *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = input[index] - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_var_cuda_kernel(const phalf *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = static_cast(input[index]) - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_output_cuda_kernel( - const T *input, const float *mean, const float *var, float *running_mean, - float *running_var, const float *weight, const float *bias, float *norm, - float *std, T *output, int num, int channels, int spatial, float eps, - float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = (input[index] - mean_value) / std_value; - output[index] = norm[index] * weight_value + bias_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - (input[index] - mean_value) / std_value * weight_value + bias_value; - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = norm[index] = (input[index] - mean_value) / std_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = (input[index] - mean_value) / std_value; - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template <> -__global__ void sync_bn_forward_output_cuda_kernel( - const phalf *input, const float *mean, const float *var, - float *running_mean, float *running_var, const float *weight, - const float *bias, float *norm, float *std, phalf *output, int num, - int channels, int spatial, float eps, float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = - static_cast(norm[index] * weight_value + bias_value); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - static_cast((static_cast(input[index]) - mean_value) / - std_value * weight_value + - bias_value); - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = static_cast(norm[index]); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = static_cast( - (static_cast(input[index]) - mean_value) / std_value); - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template -__global__ void sync_bn_backward_param_cuda_kernel(const T *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += grad_output[index] * norm[index]; - buffer2[tid] += grad_output[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template <> -__global__ void sync_bn_backward_param_cuda_kernel(const phalf *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += static_cast(grad_output[index]) * norm[index]; - buffer2[tid] += static_cast(grad_output[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const T *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, T *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = - weight[c] * - (grad_output[index] - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]; - } -} - -template <> -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const phalf *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, phalf *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = static_cast( - weight[c] * - (static_cast(grad_output[index]) - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]); - } -} - -#endif // SYNCBN_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp deleted file mode 100644 index f68e87405..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef PYTORCH_CPP_HELPER -#define PYTORCH_CPP_HELPER -#include - -#include - -using namespace at; - -#define CHECK_CUDA(x) \ - TORCH_CHECK(x.device().is_cuda(), #x " must be a CUDA tensor") -#define CHECK_MLU(x) \ - TORCH_CHECK(x.device().type() == at::kMLU, #x " must be a MLU tensor") -#define CHECK_CPU(x) \ - TORCH_CHECK(x.device().type() == at::kCPU, #x " must be a CPU tensor") -#define CHECK_CONTIGUOUS(x) \ - TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") -#define CHECK_CUDA_INPUT(x) \ - CHECK_CUDA(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_MLU_INPUT(x) \ - CHECK_MLU(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_CPU_INPUT(x) \ - CHECK_CPU(x); \ - CHECK_CONTIGUOUS(x) - -#endif // PYTORCH_CPP_HELPER diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp deleted file mode 100644 index 9869b535f..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PYTORCH_CUDA_HELPER -#define PYTORCH_CUDA_HELPER - -#include -#include -#include - -#include -#include - -#include "common_cuda_helper.hpp" - -using at::Half; -using at::Tensor; -using phalf = at::Half; - -#define __PHALF(x) (x) - -#endif // PYTORCH_CUDA_HELPER diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp deleted file mode 100644 index 2a32b7270..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef PYTORCH_DEVICE_REGISTRY_H -#define PYTORCH_DEVICE_REGISTRY_H - -// Using is recommended in the official documentation in -// https://pytorch.org/tutorials/advanced/cpp_extension.html#writing-the-c-op. -// However, we use for compatibility with CUDA 9.0 -// Read https://github.com/pytorch/extension-cpp/issues/35 for more details. -#include - -#include -#include -#include -#include - -inline std::string GetDeviceStr(const at::Device& device) { - std::string str = DeviceTypeName(device.type(), true); - if (device.has_index()) { - str.push_back(':'); - str.append(std::to_string(device.index())); - } - return str; -} - -// Registry -template -class DeviceRegistry; - -template -class DeviceRegistry { - public: - using FunctionType = Ret (*)(Args...); - static const int MAX_DEVICE_TYPES = - int8_t(at::DeviceType::COMPILE_TIME_MAX_DEVICE_TYPES); - - void Register(at::DeviceType device, FunctionType function) { - funcs_[int8_t(device)] = function; - } - - FunctionType Find(at::DeviceType device) const { - return funcs_[int8_t(device)]; - } - - static DeviceRegistry& instance() { - static DeviceRegistry inst; - return inst; - } - - private: - DeviceRegistry() { - for (size_t i = 0; i < MAX_DEVICE_TYPES; ++i) { - funcs_[i] = nullptr; - } - }; - FunctionType funcs_[MAX_DEVICE_TYPES]; -}; - -// get device of first tensor param - -template , at::Tensor>::value, - bool> = true> -at::Device GetFirstTensorDevice(T&& t, Args&&... args) { - return std::forward(t).device(); -} -template , at::Tensor>::value, - bool> = true> -at::Device GetFirstTensorDevice(T&& t, Args&&... args) { - return GetFirstTensorDevice(std::forward(args)...); -} - -// check device consistency - -inline std::pair CheckDeviceConsistency( - const at::Device& device, int index) { - return {index, device}; -} - -template , at::Tensor>::value, - bool> = true> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args); - -template , at::Tensor>::value, - bool> = true> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args) { - auto new_device = std::forward(t).device(); - if (new_device.type() != device.type() || - new_device.index() != device.index()) { - return {index, new_device}; - } - return CheckDeviceConsistency(device, index + 1, std::forward(args)...); -} - -template < - typename T, typename... Args, - std::enable_if_t, at::Tensor>::value, bool>> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args) { - return CheckDeviceConsistency(device, index + 1, std::forward(args)...); -} - -// dispatch - -template -auto Dispatch(const R& registry, const char* name, Args&&... args) { - auto device = GetFirstTensorDevice(std::forward(args)...); - auto inconsist = - CheckDeviceConsistency(device, 0, std::forward(args)...); - TORCH_CHECK(inconsist.first >= int(sizeof...(Args)), name, ": at param ", - inconsist.first, - ", inconsistent device: ", GetDeviceStr(inconsist.second).c_str(), - " vs ", GetDeviceStr(device).c_str(), "\n") - auto f_ptr = registry.Find(device.type()); - TORCH_CHECK(f_ptr != nullptr, name, ": implementation for device ", - GetDeviceStr(device).c_str(), " not found.\n") - return f_ptr(std::forward(args)...); -} - -// helper macro - -#define DEVICE_REGISTRY(key) DeviceRegistry::instance() - -#define REGISTER_DEVICE_IMPL(key, device, value) \ - struct key##_##device##_registerer { \ - key##_##device##_registerer() { \ - DEVICE_REGISTRY(key).Register(at::k##device, value); \ - } \ - }; \ - static key##_##device##_registerer _##key##_##device##_registerer; - -#define DISPATCH_DEVICE_IMPL(key, ...) \ - Dispatch(DEVICE_REGISTRY(key), #key, __VA_ARGS__) - -#endif // PYTORCH_DEVICE_REGISTRY diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp deleted file mode 100644 index dc376bb51..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp +++ /dev/null @@ -1,210 +0,0 @@ -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha); - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha); - -void sigmoid_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SigmoidFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void sigmoid_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - SigmoidFocalLossBackwardCUDAKernelLauncher(input, target, weight, grad_input, - gamma, alpha); -} - -void softmax_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SoftmaxFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void softmax_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - SoftmaxFocalLossBackwardCUDAKernelLauncher(input, target, weight, buff, - grad_input, gamma, alpha); -} - -void sigmoid_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha); - -void softmax_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha); - -REGISTER_DEVICE_IMPL(sigmoid_focal_loss_forward_impl, CUDA, - sigmoid_focal_loss_forward_cuda); -REGISTER_DEVICE_IMPL(sigmoid_focal_loss_backward_impl, CUDA, - sigmoid_focal_loss_backward_cuda); -REGISTER_DEVICE_IMPL(softmax_focal_loss_forward_impl, CUDA, - softmax_focal_loss_forward_cuda); -REGISTER_DEVICE_IMPL(softmax_focal_loss_backward_impl, CUDA, - softmax_focal_loss_backward_cuda); - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean); - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var); - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size); - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias); - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -void sync_bn_forward_mean_cuda(const Tensor input, Tensor mean) { - SyncBNForwardMeanCUDAKernelLauncher(input, mean); -} - -void sync_bn_forward_var_cuda(const Tensor input, const Tensor mean, - Tensor var) { - SyncBNForwardVarCUDAKernelLauncher(input, mean, var); -} - -void sync_bn_forward_output_cuda(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - SyncBNForwardOutputCUDAKernelLauncher(input, mean, var, running_mean, - running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_cuda(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - SyncBNBackwardParamCUDAKernelLauncher(grad_output, norm, grad_weight, - grad_bias); -} - -void sync_bn_backward_data_cuda(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - SyncBNBackwardDataCUDAKernelLauncher(grad_output, weight, grad_weight, - grad_bias, norm, std, grad_input); -} - -void sync_bn_forward_mean_impl(const Tensor input, Tensor mean); - -void sync_bn_forward_var_impl(const Tensor input, const Tensor mean, - Tensor var); - -void sync_bn_forward_output_impl(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param_impl(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data_impl(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input); - -REGISTER_DEVICE_IMPL(sync_bn_forward_mean_impl, CUDA, - sync_bn_forward_mean_cuda); -REGISTER_DEVICE_IMPL(sync_bn_forward_var_impl, CUDA, sync_bn_forward_var_cuda); -REGISTER_DEVICE_IMPL(sync_bn_forward_output_impl, CUDA, - sync_bn_forward_output_cuda); -REGISTER_DEVICE_IMPL(sync_bn_backward_param_impl, CUDA, - sync_bn_backward_param_cuda); -REGISTER_DEVICE_IMPL(sync_bn_backward_data_impl, CUDA, - sync_bn_backward_data_cuda); - - - -void PSAMaskForwardCUDAKernelLauncher(const int psa_type, const Tensor input, - Tensor output, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, - const int half_w_mask); - -void PSAMaskBackwardCUDAKernelLauncher( - const int psa_type, const Tensor grad_output, Tensor grad_input, - const int num_, const int h_feature, const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, const int half_w_mask); - -void psamask_forward_cuda(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask) { - PSAMaskForwardCUDAKernelLauncher(psa_type, input, output, num_, h_feature, - w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_backward_cuda(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - PSAMaskBackwardCUDAKernelLauncher(psa_type, grad_output, grad_input, num_, - h_feature, w_feature, h_mask, w_mask, - half_h_mask, half_w_mask); -} - -void psamask_forward_impl(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask); - -void psamask_backward_impl(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask); -REGISTER_DEVICE_IMPL(psamask_forward_impl, CUDA, psamask_forward_cuda); -REGISTER_DEVICE_IMPL(psamask_backward_impl, CUDA, psamask_backward_cuda); diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu deleted file mode 100644 index cb899f954..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sigmoid_focal_loss_cuda_kernel.cuh" -#include "softmax_focal_loss_cuda_kernel.cuh" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = input.size(1); - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_forward_cuda_kernel", [&] { - sigmoid_focal_loss_forward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha) { - int output_size = grad_input.numel(); - int num_classes = input.size(1); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_backward_cuda_kernel", [&] { - sigmoid_focal_loss_backward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - grad_input.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = softmax.size(1); - - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(softmax.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - softmax.scalar_type(), "softmax_focal_loss_forward_cuda_kernel", [&] { - softmax_focal_loss_forward_cuda_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha) { - int num_classes = softmax.size(1); - - int output_size = buff.numel(); - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda1_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda1_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - buff.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); - - output_size = grad_input.numel(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda2_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda2_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), buff.data_ptr(), - grad_input.data_ptr(), num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu deleted file mode 100644 index a0bdfa60c..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// Modified from -// https://github.com/hszhao/semseg/blob/master/lib/psa/src - -#include - -#include "psamask_cuda_kernel.cuh" -#include "pytorch_cuda_helper.hpp" - -void PSAMaskForwardCUDAKernelLauncher(const int psa_type, const Tensor input, - Tensor output, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, - const int half_w_mask) { - int nthreads = num_ * h_feature * w_feature; - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - if (psa_type == 0) - AT_DISPATCH_FLOATING_TYPES( - input.scalar_type(), "psamask_collect_forward_cuda", [&] { - psamask_collect_forward_cuda<<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, input.data_ptr(), - output.data_ptr()); - }); - else - AT_DISPATCH_FLOATING_TYPES( - input.scalar_type(), "psamask_distribute_forward_cuda", [&] { - psamask_distribute_forward_cuda - <<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, input.data_ptr(), - output.data_ptr()); - }); -} - -void PSAMaskBackwardCUDAKernelLauncher( - const int psa_type, const Tensor grad_output, Tensor grad_input, - const int num_, const int h_feature, const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, const int half_w_mask) { - int nthreads = num_ * h_feature * w_feature; - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - if (psa_type == 0) - AT_DISPATCH_FLOATING_TYPES( - grad_input.scalar_type(), "psamask_collect_backward_cuda", [&] { - psamask_collect_backward_cuda<<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, grad_output.data_ptr(), - grad_input.data_ptr()); - }); - else - AT_DISPATCH_FLOATING_TYPES( - grad_input.scalar_type(), "psamask_distribute_backward_cuda", [&] { - psamask_distribute_backward_cuda - <<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, grad_output.data_ptr(), - grad_input.data_ptr()); - }); -} diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu deleted file mode 100644 index 657c81701..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sync_bn_cuda_kernel.cuh" - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_mean_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_var_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), num, channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_output_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), running_mean.data_ptr(), - running_var.data_ptr(), weight.data_ptr(), - bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), output.data_ptr(), num, - channels, spatial, eps, momentum, group_size); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias) { - int num = grad_output.size(0); - int channels = grad_output.size(1); - int spatial = grad_output.size(2); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_param_cuda_kernel", [&] { - sync_bn_backward_param_cuda_kernel - <<>>( - grad_output.data_ptr(), norm.data_ptr(), - grad_weight.data_ptr(), grad_bias.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - int output_size = grad_input.numel(); - int num = grad_input.size(0); - int channels = grad_input.size(1); - int spatial = grad_input.size(2); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_data_cuda_kernel", [&] { - sync_bn_backward_data_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - weight.data_ptr(), grad_weight.data_ptr(), - grad_bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), grad_input.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp deleted file mode 100644 index ed0e21865..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void sigmoid_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(sigmoid_focal_loss_forward_impl, input, target, weight, - output, gamma, alpha); -} - -void sigmoid_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(sigmoid_focal_loss_backward_impl, input, target, weight, - grad_input, gamma, alpha); -} - -void softmax_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(softmax_focal_loss_forward_impl, input, target, weight, - output, gamma, alpha); -} - -void softmax_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - DISPATCH_DEVICE_IMPL(softmax_focal_loss_backward_impl, input, target, weight, - buff, grad_input, gamma, alpha); -} - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - sigmoid_focal_loss_forward_impl(input, target, weight, output, gamma, alpha); -} - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha) { - sigmoid_focal_loss_backward_impl(input, target, weight, grad_input, gamma, - alpha); -} - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - softmax_focal_loss_forward_impl(input, target, weight, output, gamma, alpha); -} - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha) { - softmax_focal_loss_backward_impl(input, target, weight, buff, grad_input, - gamma, alpha); -} diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp deleted file mode 100644 index a08d227d4..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// modified from -// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/vision.cpp -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF -#include -int get_cudart_version() { return CUDART_VERSION; } -#endif -#endif - -std::string get_compiling_cuda_version() { -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF - std::ostringstream oss; - // copied from - // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/cuda/detail/CUDAHooks.cpp#L231 - auto printCudaStyleVersion = [&](int v) { - oss << (v / 1000) << "." << (v / 10 % 100); - if (v % 10 != 0) { - oss << "." << (v % 10); - } - }; - printCudaStyleVersion(get_cudart_version()); - return oss.str(); -#else - return std::string("rocm not available"); -#endif -#else - return std::string("not available"); -#endif -} - -// similar to -// https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/Version.cpp -std::string get_compiler_version() { - std::ostringstream ss; -#if defined(__GNUC__) -#ifndef __clang__ - { ss << "GCC " << __GNUC__ << "." << __GNUC_MINOR__; } -#endif -#endif - -#if defined(__clang_major__) - { - ss << "clang " << __clang_major__ << "." << __clang_minor__ << "." - << __clang_patchlevel__; - } -#endif - -#if defined(_MSC_VER) - { ss << "MSVC " << _MSC_FULL_VER; } -#endif - return ss.str(); -} diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp deleted file mode 100644 index 6064c9ba5..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// Modified from -// https://github.com/hszhao/semseg/blob/master/lib/psa/src -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void psamask_forward_impl(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask) { - DISPATCH_DEVICE_IMPL(psamask_forward_impl, psa_type, input, output, num_, - h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_backward_impl(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - DISPATCH_DEVICE_IMPL(psamask_backward_impl, psa_type, grad_output, grad_input, - num_, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_forward(const Tensor input, Tensor output, const int psa_type, - const int num_, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask) { - psamask_forward_impl(psa_type, input, output, num_, h_feature, w_feature, - h_mask, w_mask, half_h_mask, half_w_mask); -} - -void psamask_backward(Tensor grad_output, const Tensor grad_input, - const int psa_type, const int num_, const int h_feature, - const int w_feature, const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - psamask_backward_impl(psa_type, grad_output, grad_input, num_, h_feature, - w_feature, h_mask, w_mask, half_h_mask, half_w_mask); -} diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp deleted file mode 100644 index 1e99174d8..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include - -#include "pytorch_cpp_helper.hpp" - -std::string get_compiler_version(); -std::string get_compiling_cuda_version(); - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha); - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha); - -void sync_bn_forward_mean(const Tensor input, Tensor mean); - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var); - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - - -void psamask_forward(const Tensor input, Tensor output, const int psa_type, - const int num_, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask); - -void psamask_backward(Tensor grad_output, const Tensor grad_input, - const int psa_type, const int num_, const int h_feature, - const int w_feature, const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask); - - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("get_compiler_version", &get_compiler_version, "get_compiler_version"); - m.def("get_compiling_cuda_version", &get_compiling_cuda_version, - "get_compiling_cuda_version"); - - - m.def("sigmoid_focal_loss_forward", &sigmoid_focal_loss_forward, - "sigmoid_focal_loss_forward ", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("sigmoid_focal_loss_backward", &sigmoid_focal_loss_backward, - "sigmoid_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("grad_input"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_forward", &softmax_focal_loss_forward, - "softmax_focal_loss_forward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_backward", &softmax_focal_loss_backward, - "softmax_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("buff"), py::arg("grad_input"), - py::arg("gamma"), py::arg("alpha")); - - // SyncBN - m.def("sync_bn_forward_mean", &sync_bn_forward_mean, "sync_bn forward_mean", - py::arg("input"), py::arg("mean")); - m.def("sync_bn_forward_var", &sync_bn_forward_var, "sync_bn forward_var", - py::arg("input"), py::arg("mean"), py::arg("var")); - m.def("sync_bn_forward_output", &sync_bn_forward_output, - "sync_bn forward_output", py::arg("input"), py::arg("mean"), - py::arg("var"), py::arg("weight"), py::arg("bias"), - py::arg("running_mean"), py::arg("running_var"), py::arg("norm"), - py::arg("std"), py::arg("output"), py::arg("eps"), py::arg("momentum"), - py::arg("group_size")); - m.def("sync_bn_backward_param", &sync_bn_backward_param, - "sync_bn backward_param", py::arg("grad_output"), py::arg("norm"), - py::arg("grad_weight"), py::arg("grad_bias")); - m.def("sync_bn_backward_data", &sync_bn_backward_data, - "sync_bn backward_data", py::arg("grad_output"), py::arg("weight"), - py::arg("grad_weight"), py::arg("grad_bias"), py::arg("norm"), - py::arg("std"), py::arg("grad_input")); - - // PASMask - m.def("psamask_forward", &psamask_forward, "PSAMASK forward (CPU/CUDA)", - py::arg("input"), py::arg("output"), py::arg("psa_type"), - py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), - py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), - py::arg("half_w_mask")); - m.def("psamask_backward", &psamask_backward, "PSAMASK backward (CPU/CUDA)", - py::arg("grad_output"), py::arg("grad_input"), py::arg("psa_type"), - py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), - py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), - py::arg("half_w_mask")); -} diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp deleted file mode 100644 index fd5a51327..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void sync_bn_forward_mean_impl(const Tensor input, Tensor mean) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_mean_impl, input, mean); -} - -void sync_bn_forward_var_impl(const Tensor input, const Tensor mean, - Tensor var) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_var_impl, input, mean, var); -} - -void sync_bn_forward_output_impl(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_output_impl, input, mean, var, - running_mean, running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_impl(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - DISPATCH_DEVICE_IMPL(sync_bn_backward_param_impl, grad_output, norm, - grad_weight, grad_bias); -} - -void sync_bn_backward_data_impl(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - DISPATCH_DEVICE_IMPL(sync_bn_backward_data_impl, grad_output, weight, - grad_weight, grad_bias, norm, std, grad_input); -} - -void sync_bn_forward_mean(const Tensor input, Tensor mean) { - sync_bn_forward_mean_impl(input, mean); -} - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var) { - sync_bn_forward_var_impl(input, mean, var); -} - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - sync_bn_forward_output_impl(input, mean, var, running_mean, running_var, - weight, bias, norm, std, output, eps, momentum, - group_size); -} - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - sync_bn_backward_param_impl(grad_output, norm, grad_weight, grad_bias); -} - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - sync_bn_backward_data_impl(grad_output, weight, grad_weight, grad_bias, norm, - std, grad_input); -} diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/deprecated_wrappers.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/deprecated_wrappers.py deleted file mode 100644 index 629a8033f..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/deprecated_wrappers.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# This file is for backward compatibility. -# Module wrappers for empty tensor have been moved to mmcv.cnn.bricks. -import warnings - -from ..cnn.bricks.wrappers import Conv2d, ConvTranspose2d, Linear, MaxPool2d - - -class Conv2d_deprecated(Conv2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Conv2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) - - -class ConvTranspose2d_deprecated(ConvTranspose2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing ConvTranspose2d wrapper from "mmcv.ops" will be ' - 'deprecated in the future. Please import them from "mmcv.cnn" ' - 'instead', DeprecationWarning) - - -class MaxPool2d_deprecated(MaxPool2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing MaxPool2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) - - -class Linear_deprecated(Linear): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Linear wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/focal_loss.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/focal_loss.py deleted file mode 100644 index 805860516..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/focal_loss.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sigmoid_focal_loss_forward', 'sigmoid_focal_loss_backward', - 'softmax_focal_loss_forward', 'softmax_focal_loss_backward' -]) - - -class SigmoidFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSigmoidFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance( - target, (torch.Tensor, torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - output = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_forward( - input, target, weight, output, gamma=ctx.gamma, alpha=ctx.alpha) - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input, target, weight) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - input, target, weight = ctx.saved_tensors - - grad_input = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_backward( - input, - target, - weight, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input.size(0) - return grad_input, None, None, None, None, None - - -sigmoid_focal_loss = SigmoidFocalLossFunction.apply - - -class SigmoidFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SigmoidFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return sigmoid_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s - - -class SoftmaxFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSoftmaxFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - channel_stats, _ = torch.max(input, dim=1) - input_softmax = input - channel_stats.unsqueeze(1).expand_as(input) - input_softmax.exp_() - - channel_stats = input_softmax.sum(dim=1) - input_softmax /= channel_stats.unsqueeze(1).expand_as(input) - - output = input.new_zeros(input.size(0)) - ext_module.softmax_focal_loss_forward( - input_softmax, - target, - weight, - output, - gamma=ctx.gamma, - alpha=ctx.alpha) - - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input_softmax, target, weight) - return output - - @staticmethod - def backward(ctx, grad_output): - input_softmax, target, weight = ctx.saved_tensors - buff = input_softmax.new_zeros(input_softmax.size(0)) - grad_input = input_softmax.new_zeros(input_softmax.size()) - - ext_module.softmax_focal_loss_backward( - input_softmax, - target, - weight, - buff, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input_softmax.size(0) - return grad_input, None, None, None, None, None - - -softmax_focal_loss = SoftmaxFocalLossFunction.apply - - -class SoftmaxFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SoftmaxFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return softmax_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/info.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/info.py deleted file mode 100644 index 29f2e5598..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/info.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os - -import torch - -if torch.__version__ == 'parrots': - import parrots - - def get_compiler_version(): - return 'GCC ' + parrots.version.compiler - - def get_compiling_cuda_version(): - return parrots.version.cuda -else: - from ..utils import ext_loader - ext_module = ext_loader.load_ext( - '_ext', ['get_compiler_version', 'get_compiling_cuda_version']) - - def get_compiler_version(): - return ext_module.get_compiler_version() - - def get_compiling_cuda_version(): - return ext_module.get_compiling_cuda_version() - - -def get_onnxruntime_op_path(): - wildcard = os.path.join( - os.path.abspath(os.path.dirname(os.path.dirname(__file__))), - '_ext_ort.*.so') - - paths = glob.glob(wildcard) - if len(paths) > 0: - return paths[0] - else: - return '' diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/point_sample.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/point_sample.py deleted file mode 100644 index 9a70b28e2..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/point_sample.py +++ /dev/null @@ -1,346 +0,0 @@ -# Modified from https://github.com/facebookresearch/detectron2/tree/master/projects/PointRend # noqa - -from os import path as osp - -import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.nn.modules.utils import _pair -from torch.onnx.operators import shape_as_tensor - - -def bilinear_grid_sample(im, grid, align_corners=False): - """Given an input and a flow-field grid, computes the output using input - values and pixel locations from grid. Supported only bilinear interpolation - method to sample the input pixels. - - Args: - im (torch.Tensor): Input feature map, shape (N, C, H, W) - grid (torch.Tensor): Point coordinates, shape (N, Hg, Wg, 2) - align_corners {bool}: If set to True, the extrema (-1 and 1) are - considered as referring to the center points of the input’s - corner pixels. If set to False, they are instead considered as - referring to the corner points of the input’s corner pixels, - making the sampling more resolution agnostic. - - Returns: - torch.Tensor: A tensor with sampled points, shape (N, C, Hg, Wg) - """ - n, c, h, w = im.shape - gn, gh, gw, _ = grid.shape - assert n == gn - - x = grid[:, :, :, 0] - y = grid[:, :, :, 1] - - if align_corners: - x = ((x + 1) / 2) * (w - 1) - y = ((y + 1) / 2) * (h - 1) - else: - x = ((x + 1) * w - 1) / 2 - y = ((y + 1) * h - 1) / 2 - - x = x.view(n, -1) - y = y.view(n, -1) - - x0 = torch.floor(x).long() - y0 = torch.floor(y).long() - x1 = x0 + 1 - y1 = y0 + 1 - - wa = ((x1 - x) * (y1 - y)).unsqueeze(1) - wb = ((x1 - x) * (y - y0)).unsqueeze(1) - wc = ((x - x0) * (y1 - y)).unsqueeze(1) - wd = ((x - x0) * (y - y0)).unsqueeze(1) - - # Apply default for grid_sample function zero padding - im_padded = F.pad(im, pad=[1, 1, 1, 1], mode='constant', value=0) - padded_h = h + 2 - padded_w = w + 2 - # save points positions after padding - x0, x1, y0, y1 = x0 + 1, x1 + 1, y0 + 1, y1 + 1 - - # Clip coordinates to padded image size - x0 = torch.where(x0 < 0, torch.tensor(0), x0) - x0 = torch.where(x0 > padded_w - 1, torch.tensor(padded_w - 1), x0) - x1 = torch.where(x1 < 0, torch.tensor(0), x1) - x1 = torch.where(x1 > padded_w - 1, torch.tensor(padded_w - 1), x1) - y0 = torch.where(y0 < 0, torch.tensor(0), y0) - y0 = torch.where(y0 > padded_h - 1, torch.tensor(padded_h - 1), y0) - y1 = torch.where(y1 < 0, torch.tensor(0), y1) - y1 = torch.where(y1 > padded_h - 1, torch.tensor(padded_h - 1), y1) - - im_padded = im_padded.view(n, c, -1) - - x0_y0 = (x0 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x0_y1 = (x0 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y0 = (x1 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y1 = (x1 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - - Ia = torch.gather(im_padded, 2, x0_y0) - Ib = torch.gather(im_padded, 2, x0_y1) - Ic = torch.gather(im_padded, 2, x1_y0) - Id = torch.gather(im_padded, 2, x1_y1) - - return (Ia * wa + Ib * wb + Ic * wc + Id * wd).reshape(n, c, gh, gw) - - -def is_in_onnx_export_without_custom_ops(): - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - return torch.onnx.is_in_onnx_export( - ) and not osp.exists(ort_custom_op_path) - - -def normalize(grid): - """Normalize input grid from [-1, 1] to [0, 1] - - Args: - grid (torch.Tensor): The grid to be normalize, range [-1, 1]. - - Returns: - torch.Tensor: Normalized grid, range [0, 1]. - """ - - return (grid + 1.0) / 2.0 - - -def denormalize(grid): - """Denormalize input grid from range [0, 1] to [-1, 1] - - Args: - grid (torch.Tensor): The grid to be denormalize, range [0, 1]. - - Returns: - torch.Tensor: Denormalized grid, range [-1, 1]. - """ - - return grid * 2.0 - 1.0 - - -def generate_grid(num_grid, size, device): - """Generate regular square grid of points in [0, 1] x [0, 1] coordinate - space. - - Args: - num_grid (int): The number of grids to sample, one for each region. - size (tuple[int, int]): The side size of the regular grid. - device (torch.device): Desired device of returned tensor. - - Returns: - torch.Tensor: A tensor of shape (num_grid, size[0]*size[1], 2) that - contains coordinates for the regular grids. - """ - - affine_trans = torch.tensor([[[1., 0., 0.], [0., 1., 0.]]], device=device) - grid = F.affine_grid( - affine_trans, torch.Size((1, 1, *size)), align_corners=False) - grid = normalize(grid) - return grid.view(1, -1, 2).expand(num_grid, -1, -1) - - -def rel_roi_point_to_abs_img_point(rois, rel_roi_points): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (torch.Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (torch.Tensor): Point coordinates inside RoI, relative - to RoI, location, range (0, 1), shape (N, P, 2) - Returns: - torch.Tensor: Image based absolute point coordinates, shape (N, P, 2) - """ - - with torch.no_grad(): - assert rel_roi_points.size(0) == rois.size(0) - assert rois.dim() == 2 - assert rel_roi_points.dim() == 3 - assert rel_roi_points.size(2) == 2 - # remove batch idx - if rois.size(1) == 5: - rois = rois[:, 1:] - abs_img_points = rel_roi_points.clone() - # To avoid an error during exporting to onnx use independent - # variables instead inplace computation - xs = abs_img_points[:, :, 0] * (rois[:, None, 2] - rois[:, None, 0]) - ys = abs_img_points[:, :, 1] * (rois[:, None, 3] - rois[:, None, 1]) - xs += rois[:, None, 0] - ys += rois[:, None, 1] - abs_img_points = torch.stack([xs, ys], dim=2) - return abs_img_points - - -def get_shape_from_feature_map(x): - """Get spatial resolution of input feature map considering exporting to - onnx mode. - - Args: - x (torch.Tensor): Input tensor, shape (N, C, H, W) - - Returns: - torch.Tensor: Spatial resolution (width, height), shape (1, 1, 2) - """ - if torch.onnx.is_in_onnx_export(): - img_shape = shape_as_tensor(x)[2:].flip(0).view(1, 1, 2).to( - x.device).float() - else: - img_shape = torch.tensor(x.shape[2:]).flip(0).view(1, 1, 2).to( - x.device).float() - return img_shape - - -def abs_img_point_to_rel_img_point(abs_img_points, img, spatial_scale=1.): - """Convert image based absolute point coordinates to image based relative - coordinates for sampling. - - Args: - abs_img_points (torch.Tensor): Image based absolute point coordinates, - shape (N, P, 2) - img (tuple or torch.Tensor): (height, width) of image or feature map. - spatial_scale (float, optional): Scale points by this factor. - Default: 1. - - Returns: - Tensor: Image based relative point coordinates for sampling, shape - (N, P, 2). - """ - - assert (isinstance(img, tuple) and len(img) == 2) or \ - (isinstance(img, torch.Tensor) and len(img.shape) == 4) - - if isinstance(img, tuple): - h, w = img - scale = torch.tensor([w, h], - dtype=torch.float, - device=abs_img_points.device) - scale = scale.view(1, 1, 2) - else: - scale = get_shape_from_feature_map(img) - - return abs_img_points / scale * spatial_scale - - -def rel_roi_point_to_rel_img_point(rois, - rel_roi_points, - img, - spatial_scale=1.): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (torch.Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (torch.Tensor): Point coordinates inside RoI, relative - to RoI, location, range (0, 1), shape (N, P, 2) - img (tuple or torch.Tensor): (height, width) of image or feature map. - spatial_scale (float, optional): Scale points by this factor. - Default: 1. - - Returns: - torch.Tensor: Image based relative point coordinates for sampling, - shape (N, P, 2). - """ - - abs_img_point = rel_roi_point_to_abs_img_point(rois, rel_roi_points) - rel_img_point = abs_img_point_to_rel_img_point(abs_img_point, img, - spatial_scale) - - return rel_img_point - - -def point_sample(input, points, align_corners=False, **kwargs): - """A wrapper around :func:`grid_sample` to support 3D point_coords tensors - Unlike :func:`torch.nn.functional.grid_sample` it assumes point_coords to - lie inside ``[0, 1] x [0, 1]`` square. - - Args: - input (torch.Tensor): Feature map, shape (N, C, H, W). - points (torch.Tensor): Image based absolute point coordinates - (normalized), range [0, 1] x [0, 1], shape (N, P, 2) or - (N, Hgrid, Wgrid, 2). - align_corners (bool, optional): Whether align_corners. - Default: False - - Returns: - torch.Tensor: Features of `point` on `input`, shape (N, C, P) or - (N, C, Hgrid, Wgrid). - """ - - add_dim = False - if points.dim() == 3: - add_dim = True - points = points.unsqueeze(2) - if is_in_onnx_export_without_custom_ops(): - # If custom ops for onnx runtime not compiled use python - # implementation of grid_sample function to make onnx graph - # with supported nodes - output = bilinear_grid_sample( - input, denormalize(points), align_corners=align_corners) - else: - output = F.grid_sample( - input, denormalize(points), align_corners=align_corners, **kwargs) - if add_dim: - output = output.squeeze(3) - return output - - -class SimpleRoIAlign(nn.Module): - - def __init__(self, output_size, spatial_scale, aligned=True): - """Simple RoI align in PointRend, faster than standard RoIAlign. - - Args: - output_size (tuple[int]): h, w - spatial_scale (float): scale the input boxes by this number - aligned (bool): if False, use the legacy implementation in - MMDetection, align_corners=True will be used in F.grid_sample. - If True, align the results more perfectly. - """ - - super(SimpleRoIAlign, self).__init__() - self.output_size = _pair(output_size) - self.spatial_scale = float(spatial_scale) - # to be consistent with other RoI ops - self.use_torchvision = False - self.aligned = aligned - - def forward(self, features, rois): - num_imgs = features.size(0) - num_rois = rois.size(0) - rel_roi_points = generate_grid( - num_rois, self.output_size, device=rois.device) - - if torch.onnx.is_in_onnx_export(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois, rel_roi_points, features, self.spatial_scale) - rel_img_points = rel_img_points.reshape(num_imgs, -1, - *rel_img_points.shape[1:]) - point_feats = point_sample( - features, rel_img_points, align_corners=not self.aligned) - point_feats = point_feats.transpose(1, 2) - else: - point_feats = [] - for batch_ind in range(num_imgs): - # unravel batch dim - feat = features[batch_ind].unsqueeze(0) - inds = (rois[:, 0].long() == batch_ind) - if inds.any(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois[inds], rel_roi_points[inds], feat, - self.spatial_scale).unsqueeze(0) - point_feat = point_sample( - feat, rel_img_points, align_corners=not self.aligned) - point_feat = point_feat.squeeze(0).transpose(0, 1) - point_feats.append(point_feat) - - point_feats = torch.cat(point_feats, dim=0) - - channels = features.size(1) - roi_feats = point_feats.reshape(num_rois, channels, *self.output_size) - - return roi_feats - - def __repr__(self): - format_str = self.__class__.__name__ - format_str += '(output_size={}, spatial_scale={}'.format( - self.output_size, self.spatial_scale) - return format_str diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/psa_mask.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/psa_mask.py deleted file mode 100644 index cdf14e62b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/psa_mask.py +++ /dev/null @@ -1,92 +0,0 @@ -# Modified from https://github.com/hszhao/semseg/blob/master/lib/psa -from torch import nn -from torch.autograd import Function -from torch.nn.modules.utils import _pair - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', - ['psamask_forward', 'psamask_backward']) - - -class PSAMaskFunction(Function): - - @staticmethod - def symbolic(g, input, psa_type, mask_size): - return g.op( - 'mmcv::MMCVPSAMask', - input, - psa_type_i=psa_type, - mask_size_i=mask_size) - - @staticmethod - def forward(ctx, input, psa_type, mask_size): - ctx.psa_type = psa_type - ctx.mask_size = _pair(mask_size) - ctx.save_for_backward(input) - - h_mask, w_mask = ctx.mask_size - batch_size, channels, h_feature, w_feature = input.size() - assert channels == h_mask * w_mask - output = input.new_zeros( - (batch_size, h_feature * w_feature, h_feature, w_feature)) - - ext_module.psamask_forward( - input, - output, - psa_type=psa_type, - num_=batch_size, - h_feature=h_feature, - w_feature=w_feature, - h_mask=h_mask, - w_mask=w_mask, - half_h_mask=(h_mask - 1) // 2, - half_w_mask=(w_mask - 1) // 2) - return output - - @staticmethod - def backward(ctx, grad_output): - input = ctx.saved_tensors[0] - psa_type = ctx.psa_type - h_mask, w_mask = ctx.mask_size - batch_size, channels, h_feature, w_feature = input.size() - grad_input = grad_output.new_zeros( - (batch_size, channels, h_feature, w_feature)) - ext_module.psamask_backward( - grad_output, - grad_input, - psa_type=psa_type, - num_=batch_size, - h_feature=h_feature, - w_feature=w_feature, - h_mask=h_mask, - w_mask=w_mask, - half_h_mask=(h_mask - 1) // 2, - half_w_mask=(w_mask - 1) // 2) - return grad_input, None, None, None - - -psa_mask = PSAMaskFunction.apply - - -class PSAMask(nn.Module): - - def __init__(self, psa_type, mask_size=None): - super(PSAMask, self).__init__() - assert psa_type in ['collect', 'distribute'] - if psa_type == 'collect': - psa_type_enum = 0 - else: - psa_type_enum = 1 - self.psa_type_enum = psa_type_enum - self.mask_size = mask_size - self.psa_type = psa_type - - def forward(self, input): - return psa_mask(input, self.psa_type_enum, self.mask_size) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(psa_type={self.psa_type}, ' - s += f'mask_size={self.mask_size})' - return s diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/sync_bn.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/sync_bn.py deleted file mode 100644 index 04302f031..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/ops/sync_bn.py +++ /dev/null @@ -1,279 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn.functional as F -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.module import Module -from torch.nn.parameter import Parameter - -from mmcv.cnn import NORM_LAYERS -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sync_bn_forward_mean', 'sync_bn_forward_var', 'sync_bn_forward_output', - 'sync_bn_backward_param', 'sync_bn_backward_data' -]) - - -class SyncBatchNormFunction(Function): - - @staticmethod - def symbolic(g, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - return g.op( - 'mmcv::MMCVSyncBatchNorm', - input, - running_mean, - running_var, - weight, - bias, - momentum_f=momentum, - eps_f=eps, - group_i=group, - group_size_i=group_size, - stats_mode=stats_mode) - - @staticmethod - def forward(self, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - self.momentum = momentum - self.eps = eps - self.group = group - self.group_size = group_size - self.stats_mode = stats_mode - - assert isinstance( - input, (torch.HalfTensor, torch.FloatTensor, - torch.cuda.HalfTensor, torch.cuda.FloatTensor)), \ - f'only support Half or Float Tensor, but {input.type()}' - output = torch.zeros_like(input) - input3d = input.flatten(start_dim=2) - output3d = output.view_as(input3d) - num_channels = input3d.size(1) - - # ensure mean/var/norm/std are initialized as zeros - # ``torch.empty()`` does not guarantee that - mean = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - var = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - norm = torch.zeros_like( - input3d, dtype=torch.float, device=input3d.device) - std = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - - batch_size = input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_forward_mean(input3d, mean) - batch_flag = torch.ones([1], device=mean.device, dtype=mean.dtype) - else: - # skip updating mean and leave it as zeros when the input is empty - batch_flag = torch.zeros([1], device=mean.device, dtype=mean.dtype) - - # synchronize mean and the batch flag - vec = torch.cat([mean, batch_flag]) - if self.stats_mode == 'N': - vec *= batch_size - if self.group_size > 1: - dist.all_reduce(vec, group=self.group) - total_batch = vec[-1].detach() - mean = vec[:num_channels] - - if self.stats_mode == 'default': - mean = mean / self.group_size - elif self.stats_mode == 'N': - mean = mean / total_batch.clamp(min=1) - else: - raise NotImplementedError - - # leave var as zeros when the input is empty - if batch_size > 0: - ext_module.sync_bn_forward_var(input3d, mean, var) - - if self.stats_mode == 'N': - var *= batch_size - if self.group_size > 1: - dist.all_reduce(var, group=self.group) - - if self.stats_mode == 'default': - var /= self.group_size - elif self.stats_mode == 'N': - var /= total_batch.clamp(min=1) - else: - raise NotImplementedError - - # if the total batch size over all the ranks is zero, - # we should not update the statistics in the current batch - update_flag = total_batch.clamp(max=1) - momentum = update_flag * self.momentum - ext_module.sync_bn_forward_output( - input3d, - mean, - var, - weight, - bias, - running_mean, - running_var, - norm, - std, - output3d, - eps=self.eps, - momentum=momentum, - group_size=self.group_size) - self.save_for_backward(norm, std, weight) - return output - - @staticmethod - @once_differentiable - def backward(self, grad_output): - norm, std, weight = self.saved_tensors - grad_weight = torch.zeros_like(weight) - grad_bias = torch.zeros_like(weight) - grad_input = torch.zeros_like(grad_output) - grad_output3d = grad_output.flatten(start_dim=2) - grad_input3d = grad_input.view_as(grad_output3d) - - batch_size = grad_input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_backward_param(grad_output3d, norm, grad_weight, - grad_bias) - - # all reduce - if self.group_size > 1: - dist.all_reduce(grad_weight, group=self.group) - dist.all_reduce(grad_bias, group=self.group) - grad_weight /= self.group_size - grad_bias /= self.group_size - - if batch_size > 0: - ext_module.sync_bn_backward_data(grad_output3d, weight, - grad_weight, grad_bias, norm, std, - grad_input3d) - - return grad_input, None, None, grad_weight, grad_bias, \ - None, None, None, None, None - - -@NORM_LAYERS.register_module(name='MMSyncBN') -class SyncBatchNorm(Module): - """Synchronized Batch Normalization. - - Args: - num_features (int): number of features/chennels in input tensor - eps (float, optional): a value added to the denominator for numerical - stability. Defaults to 1e-5. - momentum (float, optional): the value used for the running_mean and - running_var computation. Defaults to 0.1. - affine (bool, optional): whether to use learnable affine parameters. - Defaults to True. - track_running_stats (bool, optional): whether to track the running - mean and variance during training. When set to False, this - module does not track such statistics, and initializes statistics - buffers ``running_mean`` and ``running_var`` as ``None``. When - these buffers are ``None``, this module always uses batch - statistics in both training and eval modes. Defaults to True. - group (int, optional): synchronization of stats happen within - each process group individually. By default it is synchronization - across the whole world. Defaults to None. - stats_mode (str, optional): The statistical mode. Available options - includes ``'default'`` and ``'N'``. Defaults to 'default'. - When ``stats_mode=='default'``, it computes the overall statistics - using those from each worker with equal weight, i.e., the - statistics are synchronized and simply divied by ``group``. This - mode will produce inaccurate statistics when empty tensors occur. - When ``stats_mode=='N'``, it compute the overall statistics using - the total number of batches in each worker ignoring the number of - group, i.e., the statistics are synchronized and then divied by - the total batch ``N``. This mode is beneficial when empty tensors - occur during training, as it average the total mean by the real - number of batch. - """ - - def __init__(self, - num_features, - eps=1e-5, - momentum=0.1, - affine=True, - track_running_stats=True, - group=None, - stats_mode='default'): - super(SyncBatchNorm, self).__init__() - self.num_features = num_features - self.eps = eps - self.momentum = momentum - self.affine = affine - self.track_running_stats = track_running_stats - group = dist.group.WORLD if group is None else group - self.group = group - self.group_size = dist.get_world_size(group) - assert stats_mode in ['default', 'N'], \ - f'"stats_mode" only accepts "default" and "N", got "{stats_mode}"' - self.stats_mode = stats_mode - if self.affine: - self.weight = Parameter(torch.Tensor(num_features)) - self.bias = Parameter(torch.Tensor(num_features)) - else: - self.register_parameter('weight', None) - self.register_parameter('bias', None) - if self.track_running_stats: - self.register_buffer('running_mean', torch.zeros(num_features)) - self.register_buffer('running_var', torch.ones(num_features)) - self.register_buffer('num_batches_tracked', - torch.tensor(0, dtype=torch.long)) - else: - self.register_buffer('running_mean', None) - self.register_buffer('running_var', None) - self.register_buffer('num_batches_tracked', None) - self.reset_parameters() - - def reset_running_stats(self): - if self.track_running_stats: - self.running_mean.zero_() - self.running_var.fill_(1) - self.num_batches_tracked.zero_() - - def reset_parameters(self): - self.reset_running_stats() - if self.affine: - self.weight.data.uniform_() # pytorch use ones_() - self.bias.data.zero_() - - def forward(self, input): - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input, got {input.dim()}D input') - if self.momentum is None: - exponential_average_factor = 0.0 - else: - exponential_average_factor = self.momentum - - if self.training and self.track_running_stats: - if self.num_batches_tracked is not None: - self.num_batches_tracked += 1 - if self.momentum is None: # use cumulative moving average - exponential_average_factor = 1.0 / float( - self.num_batches_tracked) - else: # use exponential moving average - exponential_average_factor = self.momentum - - if self.training or not self.track_running_stats: - return SyncBatchNormFunction.apply( - input, self.running_mean, self.running_var, self.weight, - self.bias, exponential_average_factor, self.eps, self.group, - self.group_size, self.stats_mode) - else: - return F.batch_norm(input, self.running_mean, self.running_var, - self.weight, self.bias, False, - exponential_average_factor, self.eps) - - def __repr__(self): - s = self.__class__.__name__ - s += f'({self.num_features}, ' - s += f'eps={self.eps}, ' - s += f'momentum={self.momentum}, ' - s += f'affine={self.affine}, ' - s += f'track_running_stats={self.track_running_stats}, ' - s += f'group_size={self.group_size},' - s += f'stats_mode={self.stats_mode})' - return s diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/__init__.py deleted file mode 100644 index 2ed2c17ad..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collate import collate -from .data_container import DataContainer -from .data_parallel import MMDataParallel -from .distributed import MMDistributedDataParallel -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter, scatter_kwargs -from .utils import is_module_wrapper - -__all__ = [ - 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', - 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/_functions.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/_functions.py deleted file mode 100644 index 95c58bf1a..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/_functions.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import _get_stream - - -def scatter(input, devices, streams=None): - """Scatters tensor across multiple GPUs.""" - if streams is None: - streams = [None] * len(devices) - - if isinstance(input, list): - chunk_size = (len(input) - 1) // len(devices) + 1 - outputs = [ - scatter(input[i], [devices[i // chunk_size]], - [streams[i // chunk_size]]) for i in range(len(input)) - ] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - # TODO: copy to a pinned buffer first (if copying from CPU) - stream = streams[0] if output.numel() > 0 else None - if devices != [-1]: - with torch.cuda.device(devices[0]), torch.cuda.stream(stream): - output = output.cuda(devices[0], non_blocking=True) - - return output - else: - raise Exception(f'Unknown type {type(input)}.') - - -def synchronize_stream(output, devices, streams): - if isinstance(output, list): - chunk_size = len(output) // len(devices) - for i in range(len(devices)): - for j in range(chunk_size): - synchronize_stream(output[i * chunk_size + j], [devices[i]], - [streams[i]]) - elif isinstance(output, torch.Tensor): - if output.numel() != 0: - with torch.cuda.device(devices[0]): - main_stream = torch.cuda.current_stream() - main_stream.wait_stream(streams[0]) - output.record_stream(main_stream) - else: - raise Exception(f'Unknown type {type(output)}.') - - -def get_input_device(input): - if isinstance(input, list): - for item in input: - input_device = get_input_device(item) - if input_device != -1: - return input_device - return -1 - elif isinstance(input, torch.Tensor): - return input.get_device() if input.is_cuda else -1 - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_gpus, input): - input_device = get_input_device(input) - streams = None - if input_device == -1 and target_gpus != [-1]: - # Perform CPU to GPU copies in a background stream - streams = [_get_stream(device) for device in target_gpus] - - outputs = scatter(input, target_gpus, streams) - # Synchronize with the copy stream - if streams is not None: - synchronize_stream(outputs, target_gpus, streams) - - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/collate.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/collate.py deleted file mode 100644 index ad749197d..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/collate.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence - -import torch -import torch.nn.functional as F -from torch.utils.data.dataloader import default_collate - -from .data_container import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Puts each data field into a tensor/DataContainer with outer dimension - batch size. - - Extend default_collate to add support for - :type:`~mmcv.parallel.DataContainer`. There are 3 cases. - - 1. cpu_only = True, e.g., meta data - 2. cpu_only = False, stack = True, e.g., images tensors - 3. cpu_only = False, stack = False, e.g., gt bboxes - """ - - if not isinstance(batch, Sequence): - raise TypeError(f'{batch.dtype} is not supported.') - - if isinstance(batch[0], DataContainer): - stacked = [] - if batch[0].cpu_only: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer( - stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) - elif batch[0].stack: - for i in range(0, len(batch), samples_per_gpu): - assert isinstance(batch[i].data, torch.Tensor) - - if batch[i].pad_dims is not None: - ndim = batch[i].dim() - assert ndim > batch[i].pad_dims - max_shape = [0 for _ in range(batch[i].pad_dims)] - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = batch[i].size(-dim) - for sample in batch[i:i + samples_per_gpu]: - for dim in range(0, ndim - batch[i].pad_dims): - assert batch[i].size(dim) == sample.size(dim) - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = max(max_shape[dim - 1], - sample.size(-dim)) - padded_samples = [] - for sample in batch[i:i + samples_per_gpu]: - pad = [0 for _ in range(batch[i].pad_dims * 2)] - for dim in range(1, batch[i].pad_dims + 1): - pad[2 * dim - - 1] = max_shape[dim - 1] - sample.size(-dim) - padded_samples.append( - F.pad( - sample.data, pad, value=sample.padding_value)) - stacked.append(default_collate(padded_samples)) - elif batch[i].pad_dims is None: - stacked.append( - default_collate([ - sample.data - for sample in batch[i:i + samples_per_gpu] - ])) - else: - raise ValueError( - 'pad_dims should be either None or integers (1-3)') - - else: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer(stacked, batch[0].stack, batch[0].padding_value) - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - return [collate(samples, samples_per_gpu) for samples in transposed] - elif isinstance(batch[0], Mapping): - return { - key: collate([d[key] for d in batch], samples_per_gpu) - for key in batch[0] - } - else: - return default_collate(batch) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/data_container.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/data_container.py deleted file mode 100644 index cedb0d32a..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/data_container.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch - - -def assert_tensor_type(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not isinstance(args[0].data, torch.Tensor): - raise AttributeError( - f'{args[0].__class__.__name__} has no attribute ' - f'{func.__name__} for type {args[0].datatype}') - return func(*args, **kwargs) - - return wrapper - - -class DataContainer: - """A container for any type of objects. - - Typically tensors will be stacked in the collate function and sliced along - some dimension in the scatter function. This behavior has some limitations. - 1. All tensors have to be the same size. - 2. Types are limited (numpy array or Tensor). - - We design `DataContainer` and `MMDataParallel` to overcome these - limitations. The behavior can be either of the following. - - - copy to GPU, pad all tensors to the same size and stack them - - copy to GPU without stacking - - leave the objects as is and pass it to the model - - pad_dims specifies the number of last few dimensions to do padding - """ - - def __init__(self, - data, - stack=False, - padding_value=0, - cpu_only=False, - pad_dims=2): - self._data = data - self._cpu_only = cpu_only - self._stack = stack - self._padding_value = padding_value - assert pad_dims in [None, 1, 2, 3] - self._pad_dims = pad_dims - - def __repr__(self): - return f'{self.__class__.__name__}({repr(self.data)})' - - def __len__(self): - return len(self._data) - - @property - def data(self): - return self._data - - @property - def datatype(self): - if isinstance(self.data, torch.Tensor): - return self.data.type() - else: - return type(self.data) - - @property - def cpu_only(self): - return self._cpu_only - - @property - def stack(self): - return self._stack - - @property - def padding_value(self): - return self._padding_value - - @property - def pad_dims(self): - return self._pad_dims - - @assert_tensor_type - def size(self, *args, **kwargs): - return self.data.size(*args, **kwargs) - - @assert_tensor_type - def dim(self): - return self.data.dim() diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/data_parallel.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/data_parallel.py deleted file mode 100644 index 7a5abeb6e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/data_parallel.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from itertools import chain - -from torch.nn.parallel import DataParallel - -from .scatter_gather import scatter_kwargs - - -class MMDataParallel(DataParallel): - """The DataParallel module that supports DataContainer. - - MMDataParallel has two main differences with PyTorch DataParallel: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data during both GPU and CPU inference. - - It implement two more APIs ``train_step()`` and ``val_step()``. - - .. warning:: - MMDataParallel only supports single GPU training, if you need to - train with multiple GPUs, please use MMDistributedDataParallel - instead. If you have multiple GPUs and you just want to use - MMDataParallel, you can set the environment variable - ``CUDA_VISIBLE_DEVICES=0`` or instantiate ``MMDataParallel`` with - ``device_ids=[0]``. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - device_ids (list[int]): Device IDS of modules to be scattered to. - Defaults to None when GPU is not available. - output_device (str | int): Device ID for output. Defaults to None. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MMDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.dim = dim - - def forward(self, *inputs, **kwargs): - """Override the original forward function. - - The main difference lies in the CPU inference where the data in - :class:`DataContainers` will still be gathered. - """ - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module(*inputs[0], **kwargs[0]) - else: - return super().forward(*inputs, **kwargs) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.train_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.train_step(*inputs[0], **kwargs[0]) - - def val_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.val_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/distributed.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/distributed.py deleted file mode 100644 index 0188ca4ab..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/distributed.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel.distributed import (DistributedDataParallel, - _find_tensors) - -from mmcv import print_log -from mmcv.utils import TORCH_VERSION, digit_version -from .scatter_gather import scatter_kwargs - - -class MMDistributedDataParallel(DistributedDataParallel): - """The DDP module that supports DataContainer. - - MMDDP has two main differences with PyTorch DDP: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data. - - It implement two APIs ``train_step()`` and ``val_step()``. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - """train_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.train_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.train_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.train_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output - - def val_step(self, *inputs, **kwargs): - """val_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.val_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.val_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.val_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/distributed_deprecated.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/distributed_deprecated.py deleted file mode 100644 index b593d4a9e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/distributed_deprecated.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn as nn -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter_kwargs - - -@MODULE_WRAPPERS.register_module() -class MMDistributedDataParallel(nn.Module): - - def __init__(self, - module, - dim=0, - broadcast_buffers=True, - bucket_cap_mb=25): - super(MMDistributedDataParallel, self).__init__() - self.module = module - self.dim = dim - self.broadcast_buffers = broadcast_buffers - - self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 - self._sync_params() - - def _dist_broadcast_coalesced(self, tensors, buffer_size): - for tensors in _take_tensors(tensors, buffer_size): - flat_tensors = _flatten_dense_tensors(tensors) - dist.broadcast(flat_tensors, 0) - for tensor, synced in zip( - tensors, _unflatten_dense_tensors(flat_tensors, tensors)): - tensor.copy_(synced) - - def _sync_params(self): - module_states = list(self.module.state_dict().values()) - if len(module_states) > 0: - self._dist_broadcast_coalesced(module_states, - self.broadcast_bucket_size) - if self.broadcast_buffers: - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) < digit_version('1.0')): - buffers = [b.data for b in self.module._all_buffers()] - else: - buffers = [b.data for b in self.module.buffers()] - if len(buffers) > 0: - self._dist_broadcast_coalesced(buffers, - self.broadcast_bucket_size) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/registry.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/registry.py deleted file mode 100644 index 144f9fb16..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel, DistributedDataParallel - -from mmcv.utils import Registry - -MODULE_WRAPPERS = Registry('module wrapper') -MODULE_WRAPPERS.register_module(module=DataParallel) -MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/scatter_gather.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/scatter_gather.py deleted file mode 100644 index 900ff8856..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import Scatter as OrigScatter - -from ._functions import Scatter -from .data_container import DataContainer - - -def scatter(inputs, target_gpus, dim=0): - """Scatter inputs to target gpus. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_gpus != [-1]: - return OrigScatter.apply(target_gpus, None, dim, obj) - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_gpus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_gpus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_gpus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_gpus, dim) if inputs else [] - kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/utils.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/utils.py deleted file mode 100644 index 0f5712cb4..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/parallel/utils.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import MODULE_WRAPPERS - - -def is_module_wrapper(module): - """Check if a module is a module wrapper. - - The following 3 modules in MMCV (and their subclasses) are regarded as - module wrappers: DataParallel, DistributedDataParallel, - MMDistributedDataParallel (the deprecated version). You may add you own - module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: True if the input module is a module wrapper. - """ - module_wrappers = tuple(MODULE_WRAPPERS.module_dict.values()) - return isinstance(module, module_wrappers) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/__init__.py deleted file mode 100644 index 183d53672..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_module import BaseModule, ModuleDict, ModuleList, Sequential -from .base_runner import BaseRunner -from .builder import RUNNERS, build_runner -from .checkpoint import (CheckpointLoader, _load_checkpoint, - _load_checkpoint_with_prefix, load_checkpoint, - load_state_dict, save_checkpoint, weights_to_cpu) -from .default_constructor import DefaultRunnerConstructor -from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, - init_dist, master_only) -from .epoch_based_runner import EpochBasedRunner, Runner -from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model -from .hooks import (HOOKS, CheckpointHook, ClearMLLoggerHook, ClosureHook, - DistEvalHook, DistSamplerSeedHook, DvcliveLoggerHook, - EMAHook, EvalHook, Fp16OptimizerHook, - GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, Hook, IterTimerHook, - LoggerHook, MlflowLoggerHook, NeptuneLoggerHook, - OptimizerHook, PaviLoggerHook, SegmindLoggerHook, - SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .hooks.lr_updater import StepLrUpdaterHook # noqa -from .hooks.lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, - InvLrUpdaterHook, LinearAnnealingLrUpdaterHook, - LrUpdaterHook, OneCycleLrUpdaterHook, - PolyLrUpdaterHook) -from .hooks.momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .iter_based_runner import IterBasedRunner, IterLoader -from .log_buffer import LogBuffer -from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, - DefaultOptimizerConstructor, build_optimizer, - build_optimizer_constructor) -from .priority import Priority, get_priority -from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed - -# initialize ipu to registor ipu runner to RUNNERS -from mmcv.device import ipu # isort:skip # noqa - -__all__ = [ - 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', - 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', - 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', - 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', - 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', - 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', - 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', - 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', - 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', - 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', - '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', - 'ModuleDict', 'ModuleList', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor', - 'SegmindLoggerHook', 'LinearAnnealingMomentumUpdaterHook', - 'LinearAnnealingLrUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/base_module.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/base_module.py deleted file mode 100644 index 7937eca37..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/base_module.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings -from abc import ABCMeta -from collections import defaultdict -from logging import FileHandler - -import torch.nn as nn - -from mmcv.runner.dist_utils import master_only -from mmcv.utils.logging import get_logger, logger_initialized, print_log - - -class BaseModule(nn.Module, metaclass=ABCMeta): - """Base module for all modules in openmmlab. - - ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional - functionality of parameter initialization. Compared with - ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. - - - ``init_cfg``: the config to control the initialization. - - ``init_weights``: The function of parameter initialization and recording - initialization information. - - ``_params_init_info``: Used to track the parameter initialization - information. This attribute only exists during executing the - ``init_weights``. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, init_cfg=None): - """Initialize BaseModule, inherited from `torch.nn.Module`""" - - # NOTE init_cfg can be defined in different levels, but init_cfg - # in low levels has a higher priority. - - super(BaseModule, self).__init__() - # define default value of init_cfg instead of hard code - # in init_weights() function - self._is_init = False - - self.init_cfg = copy.deepcopy(init_cfg) - - # Backward compatibility in derived classes - # if pretrained is not None: - # warnings.warn('DeprecationWarning: pretrained is a deprecated \ - # key, please consider using init_cfg') - # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - - @property - def is_init(self): - return self._is_init - - def init_weights(self): - """Initialize the weights.""" - - is_top_level_module = False - # check if it is top-level module - if not hasattr(self, '_params_init_info'): - # The `_params_init_info` is used to record the initialization - # information of the parameters - # the key should be the obj:`nn.Parameter` of model and the value - # should be a dict containing - # - init_info (str): The string that describes the initialization. - # - tmp_mean_value (FloatTensor): The mean of the parameter, - # which indicates whether the parameter has been modified. - # this attribute would be deleted after all parameters - # is initialized. - self._params_init_info = defaultdict(dict) - is_top_level_module = True - - # Initialize the `_params_init_info`, - # When detecting the `tmp_mean_value` of - # the corresponding parameter is changed, update related - # initialization information - for name, param in self.named_parameters(): - self._params_init_info[param][ - 'init_info'] = f'The value is the same before and ' \ - f'after calling `init_weights` ' \ - f'of {self.__class__.__name__} ' - self._params_init_info[param][ - 'tmp_mean_value'] = param.data.mean() - - # pass `params_init_info` to all submodules - # All submodules share the same `params_init_info`, - # so it will be updated when parameters are - # modified at any level of the model. - for sub_module in self.modules(): - sub_module._params_init_info = self._params_init_info - - # Get the initialized logger, if not exist, - # create a logger named `mmcv` - logger_names = list(logger_initialized.keys()) - logger_name = logger_names[0] if logger_names else 'mmcv' - - from ..cnn import initialize - from ..cnn.utils.weight_init import update_init_info - module_name = self.__class__.__name__ - if not self._is_init: - if self.init_cfg: - print_log( - f'initialize {module_name} with init_cfg {self.init_cfg}', - logger=logger_name) - initialize(self, self.init_cfg) - if isinstance(self.init_cfg, dict): - # prevent the parameters of - # the pre-trained model - # from being overwritten by - # the `init_weights` - if self.init_cfg['type'] == 'Pretrained': - return - - for m in self.children(): - if hasattr(m, 'init_weights'): - m.init_weights() - # users may overload the `init_weights` - update_init_info( - m, - init_info=f'Initialized by ' - f'user-defined `init_weights`' - f' in {m.__class__.__name__} ') - - self._is_init = True - else: - warnings.warn(f'init_weights of {self.__class__.__name__} has ' - f'been called more than once.') - - if is_top_level_module: - self._dump_init_info(logger_name) - - for sub_module in self.modules(): - del sub_module._params_init_info - - @master_only - def _dump_init_info(self, logger_name): - """Dump the initialization information to a file named - `initialization.log.json` in workdir. - - Args: - logger_name (str): The name of logger. - """ - - logger = get_logger(logger_name) - - with_file_handler = False - # dump the information to the logger file if there is a `FileHandler` - for handler in logger.handlers: - if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True - if not with_file_handler: - for name, param in self.named_parameters(): - print_log( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n ", - logger=logger_name) - - def __repr__(self): - s = super().__repr__() - if self.init_cfg: - s += f'\ninit_cfg={self.init_cfg}' - return s - - -class Sequential(BaseModule, nn.Sequential): - """Sequential module in openmmlab. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, *args, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.Sequential.__init__(self, *args) - - -class ModuleList(BaseModule, nn.ModuleList): - """ModuleList in openmmlab. - - Args: - modules (iterable, optional): an iterable of modules to add. - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleList.__init__(self, modules) - - -class ModuleDict(BaseModule, nn.ModuleDict): - """ModuleDict in openmmlab. - - Args: - modules (dict, optional): a mapping (dictionary) of (string: module) - or an iterable of key-value pairs of type (string, module). - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleDict.__init__(self, modules) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/base_runner.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/base_runner.py deleted file mode 100644 index 12a0025f8..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/base_runner.py +++ /dev/null @@ -1,544 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -import os.path as osp -import warnings -from abc import ABCMeta, abstractmethod - -import torch -from torch.optim import Optimizer - -import mmcv -from ..parallel import is_module_wrapper -from .checkpoint import load_checkpoint -from .dist_utils import get_dist_info -from .hooks import HOOKS, Hook -from .log_buffer import LogBuffer -from .priority import Priority, get_priority -from .utils import get_time_str - - -class BaseRunner(metaclass=ABCMeta): - """The base class of Runner, a training helper for PyTorch. - - All subclasses should implement the following APIs: - - - ``run()`` - - ``train()`` - - ``val()`` - - ``save_checkpoint()`` - - Args: - model (:obj:`torch.nn.Module`): The model to be run. - batch_processor (callable): A callable method that process a data - batch. The interface of this method should be - `batch_processor(model, data, train_mode) -> dict` - optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an - optimizer (in most cases) or a dict of optimizers (in models that - requires more than one optimizer, e.g., GAN). - work_dir (str, optional): The working directory to save checkpoints - and logs. Defaults to None. - logger (:obj:`logging.Logger`): Logger used during training. - Defaults to None. (The default value is just for backward - compatibility) - meta (dict | None): A dict records some import information such as - environment info and seed, which will be logged in logger hook. - Defaults to None. - max_epochs (int, optional): Total training epochs. - max_iters (int, optional): Total training iterations. - """ - - def __init__(self, - model, - batch_processor=None, - optimizer=None, - work_dir=None, - logger=None, - meta=None, - max_iters=None, - max_epochs=None): - if batch_processor is not None: - if not callable(batch_processor): - raise TypeError('batch_processor must be callable, ' - f'but got {type(batch_processor)}') - warnings.warn( - 'batch_processor is deprecated, please implement ' - 'train_step() and val_step() in the model instead.', - DeprecationWarning) - # raise an error is `batch_processor` is not None and - # `model.train_step()` exists. - if is_module_wrapper(model): - _model = model.module - else: - _model = model - if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): - raise RuntimeError( - 'batch_processor and model.train_step()/model.val_step() ' - 'cannot be both available.') - else: - assert hasattr(model, 'train_step') - - # check the type of `optimizer` - if isinstance(optimizer, dict): - for name, optim in optimizer.items(): - if not isinstance(optim, Optimizer): - raise TypeError( - f'optimizer must be a dict of torch.optim.Optimizers, ' - f'but optimizer["{name}"] is a {type(optim)}') - elif not isinstance(optimizer, Optimizer) and optimizer is not None: - raise TypeError( - f'optimizer must be a torch.optim.Optimizer object ' - f'or dict or None, but got {type(optimizer)}') - - # check the type of `logger` - if not isinstance(logger, logging.Logger): - raise TypeError(f'logger must be a logging.Logger object, ' - f'but got {type(logger)}') - - # check the type of `meta` - if meta is not None and not isinstance(meta, dict): - raise TypeError( - f'meta must be a dict or None, but got {type(meta)}') - - self.model = model - self.batch_processor = batch_processor - self.optimizer = optimizer - self.logger = logger - self.meta = meta - # create work_dir - if mmcv.is_str(work_dir): - self.work_dir = osp.abspath(work_dir) - mmcv.mkdir_or_exist(self.work_dir) - elif work_dir is None: - self.work_dir = None - else: - raise TypeError('"work_dir" must be a str or None') - - # get model name from the model class - if hasattr(self.model, 'module'): - self._model_name = self.model.module.__class__.__name__ - else: - self._model_name = self.model.__class__.__name__ - - self._rank, self._world_size = get_dist_info() - self.timestamp = get_time_str() - self.mode = None - self._hooks = [] - self._epoch = 0 - self._iter = 0 - self._inner_iter = 0 - - if max_epochs is not None and max_iters is not None: - raise ValueError( - 'Only one of `max_epochs` or `max_iters` can be set.') - - self._max_epochs = max_epochs - self._max_iters = max_iters - # TODO: Redesign LogBuffer, it is not flexible and elegant enough - self.log_buffer = LogBuffer() - - @property - def model_name(self): - """str: Name of the model, usually the module class name.""" - return self._model_name - - @property - def rank(self): - """int: Rank of current process. (distributed training)""" - return self._rank - - @property - def world_size(self): - """int: Number of processes participating in the job. - (distributed training)""" - return self._world_size - - @property - def hooks(self): - """list[:obj:`Hook`]: A list of registered hooks.""" - return self._hooks - - @property - def epoch(self): - """int: Current epoch.""" - return self._epoch - - @property - def iter(self): - """int: Current iteration.""" - return self._iter - - @property - def inner_iter(self): - """int: Iteration in an epoch.""" - return self._inner_iter - - @property - def max_epochs(self): - """int: Maximum training epochs.""" - return self._max_epochs - - @property - def max_iters(self): - """int: Maximum training iterations.""" - return self._max_iters - - @abstractmethod - def train(self): - pass - - @abstractmethod - def val(self): - pass - - @abstractmethod - def run(self, data_loaders, workflow, **kwargs): - pass - - @abstractmethod - def save_checkpoint(self, - out_dir, - filename_tmpl, - save_optimizer=True, - meta=None, - create_symlink=True): - pass - - def current_lr(self): - """Get current learning rates. - - Returns: - list[float] | dict[str, list[float]]: Current learning rates of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - if isinstance(self.optimizer, torch.optim.Optimizer): - lr = [group['lr'] for group in self.optimizer.param_groups] - elif isinstance(self.optimizer, dict): - lr = dict() - for name, optim in self.optimizer.items(): - lr[name] = [group['lr'] for group in optim.param_groups] - else: - raise RuntimeError( - 'lr is not applicable because optimizer does not exist.') - return lr - - def current_momentum(self): - """Get current momentums. - - Returns: - list[float] | dict[str, list[float]]: Current momentums of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - - def _get_momentum(optimizer): - momentums = [] - for group in optimizer.param_groups: - if 'momentum' in group.keys(): - momentums.append(group['momentum']) - elif 'betas' in group.keys(): - momentums.append(group['betas'][0]) - else: - momentums.append(0) - return momentums - - if self.optimizer is None: - raise RuntimeError( - 'momentum is not applicable because optimizer does not exist.') - elif isinstance(self.optimizer, torch.optim.Optimizer): - momentums = _get_momentum(self.optimizer) - elif isinstance(self.optimizer, dict): - momentums = dict() - for name, optim in self.optimizer.items(): - momentums[name] = _get_momentum(optim) - return momentums - - def register_hook(self, hook, priority='NORMAL'): - """Register a hook into the hook list. - - The hook will be inserted into a priority queue, with the specified - priority (See :class:`Priority` for details of priorities). - For hooks with the same priority, they will be triggered in the same - order as they are registered. - - Args: - hook (:obj:`Hook`): The hook to be registered. - priority (int or str or :obj:`Priority`): Hook priority. - Lower value means higher priority. - """ - assert isinstance(hook, Hook) - if hasattr(hook, 'priority'): - raise ValueError('"priority" is a reserved attribute for hooks') - priority = get_priority(priority) - hook.priority = priority - # insert the hook to a sorted list - inserted = False - for i in range(len(self._hooks) - 1, -1, -1): - if priority >= self._hooks[i].priority: - self._hooks.insert(i + 1, hook) - inserted = True - break - if not inserted: - self._hooks.insert(0, hook) - - def register_hook_from_cfg(self, hook_cfg): - """Register a hook from its cfg. - - Args: - hook_cfg (dict): Hook config. It should have at least keys 'type' - and 'priority' indicating its type and priority. - - Note: - The specific hook class to register should not use 'type' and - 'priority' arguments during initialization. - """ - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = mmcv.build_from_cfg(hook_cfg, HOOKS) - self.register_hook(hook, priority=priority) - - def call_hook(self, fn_name): - """Call all hooks. - - Args: - fn_name (str): The function name in each hook to be called, such as - "before_train_epoch". - """ - for hook in self._hooks: - getattr(hook, fn_name)(self) - - def get_hook_info(self): - # Get hooks info in each stage - stage_hook_map = {stage: [] for stage in Hook.stages} - for hook in self.hooks: - try: - priority = Priority(hook.priority).name - except ValueError: - priority = hook.priority - classname = hook.__class__.__name__ - hook_info = f'({priority:<12}) {classname:<35}' - for trigger_stage in hook.get_triggered_stages(): - stage_hook_map[trigger_stage].append(hook_info) - - stage_hook_infos = [] - for stage in Hook.stages: - hook_infos = stage_hook_map[stage] - if len(hook_infos) > 0: - info = f'{stage}:\n' - info += '\n'.join(hook_infos) - info += '\n -------------------- ' - stage_hook_infos.append(info) - return '\n'.join(stage_hook_infos) - - def load_checkpoint(self, - filename, - map_location='cpu', - strict=False, - revise_keys=[(r'^module.', '')]): - return load_checkpoint( - self.model, - filename, - map_location, - strict, - self.logger, - revise_keys=revise_keys) - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - if map_location == 'default': - if torch.cuda.is_available(): - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint(checkpoint) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - if self.meta is None: - self.meta = {} - self.meta.setdefault('hook_msgs', {}) - # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages - self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) - - # Re-calculate the number of iterations when resuming - # models with different number of GPUs - if 'config' in checkpoint['meta']: - config = mmcv.Config.fromstring( - checkpoint['meta']['config'], file_format='.py') - previous_gpu_ids = config.get('gpu_ids', None) - if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( - previous_gpu_ids) != self.world_size: - self._iter = int(self._iter * len(previous_gpu_ids) / - self.world_size) - self.logger.info('the iteration number is changed due to ' - 'change of GPU number') - - # resume meta information meta - self.meta = checkpoint['meta'] - - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - elif isinstance(lr_config, dict): - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = mmcv.build_from_cfg(lr_config, HOOKS) - else: - hook = lr_config - self.register_hook(hook, priority='VERY_HIGH') - - def register_momentum_hook(self, momentum_config): - if momentum_config is None: - return - if isinstance(momentum_config, dict): - assert 'policy' in momentum_config - policy_type = momentum_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of momentum updater. - # Since this is not applicable for - # `CosineAnnealingMomentumUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'MomentumUpdaterHook' - momentum_config['type'] = hook_type - hook = mmcv.build_from_cfg(momentum_config, HOOKS) - else: - hook = momentum_config - self.register_hook(hook, priority='HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = mmcv.build_from_cfg(optimizer_config, HOOKS) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def register_checkpoint_hook(self, checkpoint_config): - if checkpoint_config is None: - return - if isinstance(checkpoint_config, dict): - checkpoint_config.setdefault('type', 'CheckpointHook') - hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) - else: - hook = checkpoint_config - self.register_hook(hook, priority='NORMAL') - - def register_logger_hooks(self, log_config): - if log_config is None: - return - log_interval = log_config['interval'] - for info in log_config['hooks']: - logger_hook = mmcv.build_from_cfg( - info, HOOKS, default_args=dict(interval=log_interval)) - self.register_hook(logger_hook, priority='VERY_LOW') - - def register_timer_hook(self, timer_config): - if timer_config is None: - return - if isinstance(timer_config, dict): - timer_config_ = copy.deepcopy(timer_config) - hook = mmcv.build_from_cfg(timer_config_, HOOKS) - else: - hook = timer_config - self.register_hook(hook, priority='LOW') - - def register_custom_hooks(self, custom_config): - if custom_config is None: - return - - if not isinstance(custom_config, list): - custom_config = [custom_config] - - for item in custom_config: - if isinstance(item, dict): - self.register_hook_from_cfg(item) - else: - self.register_hook(item, priority='NORMAL') - - def register_profiler_hook(self, profiler_config): - if profiler_config is None: - return - if isinstance(profiler_config, dict): - profiler_config.setdefault('type', 'ProfilerHook') - hook = mmcv.build_from_cfg(profiler_config, HOOKS) - else: - hook = profiler_config - self.register_hook(hook) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - timer_config=dict(type='IterTimerHook'), - custom_hooks_config=None): - """Register default and custom hooks for training. - - Default and custom hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - self.register_lr_hook(lr_config) - self.register_momentum_hook(momentum_config) - self.register_optimizer_hook(optimizer_config) - self.register_checkpoint_hook(checkpoint_config) - self.register_timer_hook(timer_config) - self.register_logger_hooks(log_config) - self.register_custom_hooks(custom_hooks_config) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/builder.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/builder.py deleted file mode 100644 index 77c96ba0b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/builder.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from ..utils import Registry - -RUNNERS = Registry('runner') -RUNNER_BUILDERS = Registry('runner builder') - - -def build_runner_constructor(cfg): - return RUNNER_BUILDERS.build(cfg) - - -def build_runner(cfg, default_args=None): - runner_cfg = copy.deepcopy(cfg) - constructor_type = runner_cfg.pop('constructor', - 'DefaultRunnerConstructor') - runner_constructor = build_runner_constructor( - dict( - type=constructor_type, - runner_cfg=runner_cfg, - default_args=default_args)) - runner = runner_constructor() - return runner diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/checkpoint.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/checkpoint.py deleted file mode 100644 index 835ee725a..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/checkpoint.py +++ /dev/null @@ -1,759 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os -import os.path as osp -import pkgutil -import re -import time -import warnings -from collections import OrderedDict -from importlib import import_module -from tempfile import TemporaryDirectory - -import torch -import torchvision -from torch.optim import Optimizer - -import mmcv -from ..fileio import FileClient -from ..fileio import load as load_file -from ..parallel import is_module_wrapper -from ..utils import digit_version, load_url, mkdir_or_exist -from .dist_utils import get_dist_info - -ENV_MMCV_HOME = 'MMCV_HOME' -ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' -DEFAULT_CACHE_DIR = '~/.cache' - - -def _get_mmcv_home(): - mmcv_home = os.path.expanduser( - os.getenv( - ENV_MMCV_HOME, - os.path.join( - os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) - - mkdir_or_exist(mmcv_home) - return mmcv_home - - -def load_state_dict(module, state_dict, strict=False, logger=None): - """Load state_dict to a module. - - This method is modified from :meth:`torch.nn.Module.load_state_dict`. - Default value for ``strict`` is set to ``False`` and the message for - param mismatch will be shown even if strict is False. - - Args: - module (Module): Module that receives the state_dict. - state_dict (OrderedDict): Weights. - strict (bool): whether to strictly enforce that the keys - in :attr:`state_dict` match the keys returned by this module's - :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. - logger (:obj:`logging.Logger`, optional): Logger to log the error - message. If not specified, print function will be used. - """ - unexpected_keys = [] - all_missing_keys = [] - err_msg = [] - - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata - - # use _load_from_state_dict to enable checkpoint version control - def load(module, prefix=''): - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - local_metadata = {} if metadata is None else metadata.get( - prefix[:-1], {}) - module._load_from_state_dict(state_dict, prefix, local_metadata, True, - all_missing_keys, unexpected_keys, - err_msg) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(module) - load = None # break load->load reference cycle - - # ignore "num_batches_tracked" of BN layers - missing_keys = [ - key for key in all_missing_keys if 'num_batches_tracked' not in key - ] - - if unexpected_keys: - err_msg.append('unexpected key in source ' - f'state_dict: {", ".join(unexpected_keys)}\n') - if missing_keys: - err_msg.append( - f'missing keys in source state_dict: {", ".join(missing_keys)}\n') - - rank, _ = get_dist_info() - if len(err_msg) > 0 and rank == 0: - err_msg.insert( - 0, 'The model and loaded state dict do not match exactly\n') - err_msg = '\n'.join(err_msg) - if strict: - raise RuntimeError(err_msg) - elif logger is not None: - logger.warning(err_msg) - else: - print(err_msg) - - -def get_torchvision_models(): - if digit_version(torchvision.__version__) < digit_version('0.13.0a0'): - model_urls = dict() - # When the version of torchvision is lower than 0.13, the model url is - # not declared in `torchvision.model.__init__.py`, so we need to - # iterate through `torchvision.models.__path__` to get the url for each - # model. - for _, name, ispkg in pkgutil.walk_packages( - torchvision.models.__path__): - if ispkg: - continue - _zoo = import_module(f'torchvision.models.{name}') - if hasattr(_zoo, 'model_urls'): - _urls = getattr(_zoo, 'model_urls') - model_urls.update(_urls) - else: - # Since torchvision bumps to v0.13, the weight loading logic, - # model keys and model urls have been changed. Here the URLs of old - # version is loaded to avoid breaking back compatibility. If the - # torchvision version>=0.13.0, new URLs will be added. Users can get - # the resnet50 checkpoint by setting 'resnet50.imagent1k_v1', - # 'resnet50' or 'ResNet50_Weights.IMAGENET1K_V1' in the config. - json_path = osp.join(mmcv.__path__[0], - 'model_zoo/torchvision_0.12.json') - model_urls = mmcv.load(json_path) - for cls_name, cls in torchvision.models.__dict__.items(): - # The name of torchvision model weights classes ends with - # `_Weights` such as `ResNet18_Weights`. However, some model weight - # classes, such as `MNASNet0_75_Weights` does not have any urls in - # torchvision 0.13.0 and cannot be iterated. Here we simply check - # `DEFAULT` attribute to ensure the class is not empty. - if (not cls_name.endswith('_Weights') - or not hasattr(cls, 'DEFAULT')): - continue - # Since `cls.DEFAULT` can not be accessed by iterating cls, we set - # default urls explicitly. - cls_key = cls_name.replace('_Weights', '').lower() - model_urls[f'{cls_key}.default'] = cls.DEFAULT.url - for weight_enum in cls: - cls_key = cls_name.replace('_Weights', '').lower() - cls_key = f'{cls_key}.{weight_enum.name.lower()}' - model_urls[cls_key] = weight_enum.url - - return model_urls - - -def get_external_models(): - mmcv_home = _get_mmcv_home() - default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') - default_urls = load_file(default_json_path) - assert isinstance(default_urls, dict) - external_json_path = osp.join(mmcv_home, 'open_mmlab.json') - if osp.exists(external_json_path): - external_urls = load_file(external_json_path) - assert isinstance(external_urls, dict) - default_urls.update(external_urls) - - return default_urls - - -def get_mmcls_models(): - mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') - mmcls_urls = load_file(mmcls_json_path) - - return mmcls_urls - - -def get_deprecated_model_names(): - deprecate_json_path = osp.join(mmcv.__path__[0], - 'model_zoo/deprecated.json') - deprecate_urls = load_file(deprecate_json_path) - assert isinstance(deprecate_urls, dict) - - return deprecate_urls - - -def _process_mmcls_checkpoint(checkpoint): - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - # Some checkpoints converted from 3rd-party repo don't - # have the "state_dict" key. - state_dict = checkpoint - new_state_dict = OrderedDict() - for k, v in state_dict.items(): - if k.startswith('backbone.'): - new_state_dict[k[9:]] = v - new_checkpoint = dict(state_dict=new_state_dict) - - return new_checkpoint - - -class CheckpointLoader: - """A general checkpoint loader to manage all schemes.""" - - _schemes = {} - - @classmethod - def _register_scheme(cls, prefixes, loader, force=False): - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if (prefix not in cls._schemes) or force: - cls._schemes[prefix] = loader - else: - raise KeyError( - f'{prefix} is already registered as a loader backend, ' - 'add "force=True" if you want to override it') - # sort, longer prefixes take priority - cls._schemes = OrderedDict( - sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) - - @classmethod - def register_scheme(cls, prefixes, loader=None, force=False): - """Register a loader to CheckpointLoader. - - This method can be used as a normal class method or a decorator. - - Args: - prefixes (str or list[str] or tuple[str]): - The prefix of the registered loader. - loader (function, optional): The loader function to be registered. - When this method is used as a decorator, loader is None. - Defaults to None. - force (bool, optional): Whether to override the loader - if the prefix has already been registered. Defaults to False. - """ - - if loader is not None: - cls._register_scheme(prefixes, loader, force=force) - return - - def _register(loader_cls): - cls._register_scheme(prefixes, loader_cls, force=force) - return loader_cls - - return _register - - @classmethod - def _get_checkpoint_loader(cls, path): - """Finds a loader that supports the given path. Falls back to the local - loader if no other loader is found. - - Args: - path (str): checkpoint path - - Returns: - callable: checkpoint loader - """ - for p in cls._schemes: - # use regular match to handle some cases that where the prefix of - # loader has a prefix. For example, both 's3://path' and - # 'open-mmlab:s3://path' should return `load_from_ceph` - if re.match(p, path) is not None: - return cls._schemes[p] - - @classmethod - def load_checkpoint(cls, filename, map_location=None, logger=None): - """load checkpoint through URL scheme path. - - Args: - filename (str): checkpoint file name with given prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - logger (:mod:`logging.Logger`, optional): The logger for message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint_loader = cls._get_checkpoint_loader(filename) - class_name = checkpoint_loader.__name__ - mmcv.print_log( - f'load checkpoint from {class_name[10:]} path: {filename}', logger) - return checkpoint_loader(filename, map_location) - - -@CheckpointLoader.register_scheme(prefixes='') -def load_from_local(filename, map_location): - """load checkpoint by local file path. - - Args: - filename (str): local checkpoint file path - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - filename = osp.expanduser(filename) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) -def load_from_http(filename, map_location=None, model_dir=None): - """load checkpoint through HTTP or HTTPS scheme path. In distributed - setting, this function only download checkpoint at local rank 0. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - model_dir (string, optional): directory in which to save the object, - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - rank, world_size = get_dist_info() - if rank == 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - if world_size > 1: - torch.distributed.barrier() - if rank > 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='pavi://') -def load_from_pavi(filename, map_location=None): - """load checkpoint through the file path prefixed with pavi. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with pavi prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - assert filename.startswith('pavi://'), \ - f'Expected filename startswith `pavi://`, but get {filename}' - model_path = filename[7:] - - try: - from pavi import modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - - model = modelcloud.get(model_path) - with TemporaryDirectory() as tmp_dir: - downloaded_file = osp.join(tmp_dir, model.name) - model.download(downloaded_file) - checkpoint = torch.load(downloaded_file, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=r'(\S+\:)?s3://') -def load_from_ceph(filename, map_location=None, backend='petrel'): - """load checkpoint through the file path prefixed with s3. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Note: - Since v1.4.1, the registered scheme prefixes have been enhanced to - support bucket names in the path prefix, e.g. 's3://xx.xx/xx.path', - 'bucket1:s3://xx.xx/xx.path'. - - Args: - filename (str): checkpoint file path with s3 prefix - map_location (str, optional): Same as :func:`torch.load`. - backend (str, optional): The storage backend type. Options are 'ceph', - 'petrel'. Default: 'petrel'. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - allowed_backends = ['ceph', 'petrel'] - if backend not in allowed_backends: - raise ValueError(f'Load from Backend {backend} is not supported.') - - if backend == 'ceph': - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - - # CephClient and PetrelBackend have the same prefix 's3://' and the latter - # will be chosen as default. If PetrelBackend can not be instantiated - # successfully, the CephClient will be chosen. - try: - file_client = FileClient(backend=backend) - except ImportError: - allowed_backends.remove(backend) - file_client = FileClient(backend=allowed_backends[0]) - - with io.BytesIO(file_client.get(filename)) as buffer: - checkpoint = torch.load(buffer, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) -def load_from_torchvision(filename, map_location=None): - """load checkpoint through the file path prefixed with modelzoo or - torchvision. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - model_urls = get_torchvision_models() - if filename.startswith('modelzoo://'): - warnings.warn( - 'The URL scheme of "modelzoo://" is deprecated, please ' - 'use "torchvision://" instead', DeprecationWarning) - model_name = filename[11:] - else: - model_name = filename[14:] - - # Support getting model urls in the same way as torchvision - # `ResNet50_Weights.IMAGENET1K_V1` will be mapped to - # resnet50.imagenet1k_v1. - model_name = model_name.lower().replace('_weights', '') - return load_from_http(model_urls[model_name], map_location=map_location) - - -@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) -def load_from_openmmlab(filename, map_location=None): - """load checkpoint through the file path prefixed with open-mmlab or - openmmlab. - - Args: - filename (str): checkpoint file path with open-mmlab or - openmmlab prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_external_models() - prefix_str = 'open-mmlab://' - if filename.startswith(prefix_str): - model_name = filename[13:] - else: - model_name = filename[12:] - prefix_str = 'openmmlab://' - - deprecated_urls = get_deprecated_model_names() - if model_name in deprecated_urls: - warnings.warn( - f'{prefix_str}{model_name} is deprecated in favor ' - f'of {prefix_str}{deprecated_urls[model_name]}', - DeprecationWarning) - model_name = deprecated_urls[model_name] - model_url = model_urls[model_name] - # check if is url - if model_url.startswith(('http://', 'https://')): - checkpoint = load_from_http(model_url, map_location=map_location) - else: - filename = osp.join(_get_mmcv_home(), model_url) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='mmcls://') -def load_from_mmcls(filename, map_location=None): - """load checkpoint through the file path prefixed with mmcls. - - Args: - filename (str): checkpoint file path with mmcls prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_mmcls_models() - model_name = filename[8:] - checkpoint = load_from_http( - model_urls[model_name], map_location=map_location) - checkpoint = _process_mmcls_checkpoint(checkpoint) - return checkpoint - - -def _load_checkpoint(filename, map_location=None, logger=None): - """Load checkpoint from somewhere (modelzoo, file, url). - - Args: - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str, optional): Same as :func:`torch.load`. - Default: None. - logger (:mod:`logging.Logger`, optional): The logger for error message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. It can be either an - OrderedDict storing model weights or a dict containing other - information, which depends on the checkpoint. - """ - return CheckpointLoader.load_checkpoint(filename, map_location, logger) - - -def _load_checkpoint_with_prefix(prefix, filename, map_location=None): - """Load partial pretrained model with specific prefix. - - Args: - prefix (str): The prefix of sub-module. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str | None): Same as :func:`torch.load`. Default: None. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint = _load_checkpoint(filename, map_location=map_location) - - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - if not prefix.endswith('.'): - prefix += '.' - prefix_len = len(prefix) - - state_dict = { - k[prefix_len:]: v - for k, v in state_dict.items() if k.startswith(prefix) - } - - assert state_dict, f'{prefix} is not in the pretrained model' - return state_dict - - -def load_checkpoint(model, - filename, - map_location=None, - strict=False, - logger=None, - revise_keys=[(r'^module\.', '')]): - """Load checkpoint from a file or URI. - - Args: - model (Module): Module to load checkpoint. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str): Same as :func:`torch.load`. - strict (bool): Whether to allow different params for the model and - checkpoint. - logger (:mod:`logging.Logger` or None): The logger for error message. - revise_keys (list): A list of customized keywords to modify the - state_dict in checkpoint. Each item is a (pattern, replacement) - pair of the regular expression operations. Default: strip - the prefix 'module.' by [(r'^module\\.', '')]. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - checkpoint = _load_checkpoint(filename, map_location, logger) - # OrderedDict is a subclass of dict - if not isinstance(checkpoint, dict): - raise RuntimeError( - f'No state_dict found in checkpoint file {filename}') - # get state_dict from checkpoint - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - - # strip prefix of state_dict - metadata = getattr(state_dict, '_metadata', OrderedDict()) - for p, r in revise_keys: - state_dict = OrderedDict( - {re.sub(p, r, k): v - for k, v in state_dict.items()}) - # Keep metadata in state_dict - state_dict._metadata = metadata - - # load state_dict - load_state_dict(model, state_dict, strict, logger) - return checkpoint - - -def weights_to_cpu(state_dict): - """Copy a model state_dict to cpu. - - Args: - state_dict (OrderedDict): Model weights on GPU. - - Returns: - OrderedDict: Model weights on GPU. - """ - state_dict_cpu = OrderedDict() - for key, val in state_dict.items(): - state_dict_cpu[key] = val.cpu() - # Keep metadata in state_dict - state_dict_cpu._metadata = getattr(state_dict, '_metadata', OrderedDict()) - return state_dict_cpu - - -def _save_to_state_dict(module, destination, prefix, keep_vars): - """Saves module state to `destination` dictionary. - - This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. - - Args: - module (nn.Module): The module to generate state_dict. - destination (dict): A dict where state will be stored. - prefix (str): The prefix for parameters and buffers used in this - module. - """ - for name, param in module._parameters.items(): - if param is not None: - destination[prefix + name] = param if keep_vars else param.detach() - for name, buf in module._buffers.items(): - # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d - if buf is not None: - destination[prefix + name] = buf if keep_vars else buf.detach() - - -def get_state_dict(module, destination=None, prefix='', keep_vars=False): - """Returns a dictionary containing a whole state of the module. - - Both parameters and persistent buffers (e.g. running averages) are - included. Keys are corresponding parameter and buffer names. - - This method is modified from :meth:`torch.nn.Module.state_dict` to - recursively check parallel module in case that the model has a complicated - structure, e.g., nn.Module(nn.Module(DDP)). - - Args: - module (nn.Module): The module to generate state_dict. - destination (OrderedDict): Returned dict for the state of the - module. - prefix (str): Prefix of the key. - keep_vars (bool): Whether to keep the variable property of the - parameters. Default: False. - - Returns: - dict: A dictionary containing a whole state of the module. - """ - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - - # below is the same as torch.nn.Module.state_dict() - if destination is None: - destination = OrderedDict() - destination._metadata = OrderedDict() - destination._metadata[prefix[:-1]] = local_metadata = dict( - version=module._version) - _save_to_state_dict(module, destination, prefix, keep_vars) - for name, child in module._modules.items(): - if child is not None: - get_state_dict( - child, destination, prefix + name + '.', keep_vars=keep_vars) - for hook in module._state_dict_hooks.values(): - hook_result = hook(module, destination, prefix, local_metadata) - if hook_result is not None: - destination = hook_result - return destination - - -def save_checkpoint(model, - filename, - optimizer=None, - meta=None, - file_client_args=None): - """Save checkpoint to file. - - The checkpoint will have 3 fields: ``meta``, ``state_dict`` and - ``optimizer``. By default ``meta`` will contain version and time info. - - Args: - model (Module): Module whose params are to be saved. - filename (str): Checkpoint filename. - optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. - meta (dict, optional): Metadata to be saved in checkpoint. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError(f'meta must be a dict or None, but got {type(meta)}') - meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) - - if is_module_wrapper(model): - model = model.module - - if hasattr(model, 'CLASSES') and model.CLASSES is not None: - # save class name to the meta - meta.update(CLASSES=model.CLASSES) - - checkpoint = { - 'meta': meta, - 'state_dict': weights_to_cpu(get_state_dict(model)) - } - # save optimizer state dict in the checkpoint - if isinstance(optimizer, Optimizer): - checkpoint['optimizer'] = optimizer.state_dict() - elif isinstance(optimizer, dict): - checkpoint['optimizer'] = {} - for name, optim in optimizer.items(): - checkpoint['optimizer'][name] = optim.state_dict() - - if filename.startswith('pavi://'): - if file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" if filename starts with' - f'"pavi://", but got {file_client_args}') - try: - from pavi import exception, modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - model_path = filename[7:] - root = modelcloud.Folder() - model_dir, model_name = osp.split(model_path) - try: - model = modelcloud.get(model_dir) - except exception.NodeNotFoundError: - model = root.create_training_model(model_dir) - with TemporaryDirectory() as tmp_dir: - checkpoint_file = osp.join(tmp_dir, model_name) - with open(checkpoint_file, 'wb') as f: - torch.save(checkpoint, f) - f.flush() - model.create_file(checkpoint_file, name=model_name) - else: - file_client = FileClient.infer_client(file_client_args, filename) - with io.BytesIO() as f: - torch.save(checkpoint, f) - file_client.put(f.getvalue(), filename) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/default_constructor.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/default_constructor.py deleted file mode 100644 index 4a4f2cc64..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/default_constructor.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import RUNNER_BUILDERS, RUNNERS - - -@RUNNER_BUILDERS.register_module() -class DefaultRunnerConstructor: - """Default constructor for runners. - - Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. - For example, We can inject some new properties and functions for `Runner`. - - Example: - >>> from mmcv.runner import RUNNER_BUILDERS, build_runner - >>> # Define a new RunnerReconstructor - >>> @RUNNER_BUILDERS.register_module() - >>> class MyRunnerConstructor: - ... def __init__(self, runner_cfg, default_args=None): - ... if not isinstance(runner_cfg, dict): - ... raise TypeError('runner_cfg should be a dict', - ... f'but got {type(runner_cfg)}') - ... self.runner_cfg = runner_cfg - ... self.default_args = default_args - ... - ... def __call__(self): - ... runner = RUNNERS.build(self.runner_cfg, - ... default_args=self.default_args) - ... # Add new properties for existing runner - ... runner.my_name = 'my_runner' - ... runner.my_function = lambda self: print(self.my_name) - ... ... - >>> # build your runner - >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, - ... constructor='MyRunnerConstructor') - >>> runner = build_runner(runner_cfg) - """ - - def __init__(self, runner_cfg, default_args=None): - if not isinstance(runner_cfg, dict): - raise TypeError('runner_cfg should be a dict', - f'but got {type(runner_cfg)}') - self.runner_cfg = runner_cfg - self.default_args = default_args - - def __call__(self): - return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/dist_utils.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/dist_utils.py deleted file mode 100644 index 26d77a8f9..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/dist_utils.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved -import functools -import os -import socket -import subprocess -from collections import OrderedDict - -import torch -import torch.multiprocessing as mp -from torch import distributed as dist -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import IS_MLU_AVAILABLE - - -def _find_free_port(): - # Copied from https://github.com/facebookresearch/detectron2/blob/main/detectron2/engine/launch.py # noqa: E501 - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - # Binding to port 0 will cause the OS to find an available port for us - sock.bind(('', 0)) - port = sock.getsockname()[1] - sock.close() - # NOTE: there is still a chance the port could be taken by other processes. - return port - - -def _is_free_port(port): - ips = socket.gethostbyname_ex(socket.gethostname())[-1] - ips.append('localhost') - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - return all(s.connect_ex((ip, port)) != 0 for ip in ips) - - -def init_dist(launcher, backend='nccl', **kwargs): - if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') - if launcher == 'pytorch': - _init_dist_pytorch(backend, **kwargs) - elif launcher == 'mpi': - _init_dist_mpi(backend, **kwargs) - elif launcher == 'slurm': - _init_dist_slurm(backend, **kwargs) - else: - raise ValueError(f'Invalid launcher type: {launcher}') - - -def _init_dist_pytorch(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['RANK']) - if IS_MLU_AVAILABLE: - import torch_mlu # noqa: F401 - torch.mlu.set_device(rank) - dist.init_process_group( - backend='cncl', - rank=rank, - world_size=int(os.environ['WORLD_SIZE']), - **kwargs) - else: - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_mpi(backend, **kwargs): - local_rank = int(os.environ['OMPI_COMM_WORLD_LOCAL_RANK']) - torch.cuda.set_device(local_rank) - if 'MASTER_PORT' not in os.environ: - # 29500 is torch.distributed default port - os.environ['MASTER_PORT'] = '29500' - if 'MASTER_ADDR' not in os.environ: - raise KeyError('The environment variable MASTER_ADDR is not set') - os.environ['WORLD_SIZE'] = os.environ['OMPI_COMM_WORLD_SIZE'] - os.environ['RANK'] = os.environ['OMPI_COMM_WORLD_RANK'] - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_slurm(backend, port=None): - """Initialize slurm distributed training environment. - - If argument ``port`` is not specified, then the master port will be system - environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system - environment variable, then a default port ``29500`` will be used. - - Args: - backend (str): Backend of torch.distributed. - port (int, optional): Master port. Defaults to None. - """ - proc_id = int(os.environ['SLURM_PROCID']) - ntasks = int(os.environ['SLURM_NTASKS']) - node_list = os.environ['SLURM_NODELIST'] - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(proc_id % num_gpus) - addr = subprocess.getoutput( - f'scontrol show hostname {node_list} | head -n1') - # specify master port - if port is not None: - os.environ['MASTER_PORT'] = str(port) - elif 'MASTER_PORT' in os.environ: - pass # use MASTER_PORT in the environment variable - else: - # if torch.distributed default port(29500) is available - # then use it, else find a free port - if _is_free_port(29500): - os.environ['MASTER_PORT'] = '29500' - else: - os.environ['MASTER_PORT'] = str(_find_free_port()) - # use MASTER_ADDR in the environment variable if it already exists - if 'MASTER_ADDR' not in os.environ: - os.environ['MASTER_ADDR'] = addr - os.environ['WORLD_SIZE'] = str(ntasks) - os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) - os.environ['RANK'] = str(proc_id) - dist.init_process_group(backend=backend) - - -def get_dist_info(): - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - world_size = dist.get_world_size() - else: - rank = 0 - world_size = 1 - return rank, world_size - - -def master_only(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - rank, _ = get_dist_info() - if rank == 0: - return func(*args, **kwargs) - - return wrapper - - -def allreduce_params(params, coalesce=True, bucket_size_mb=-1): - """Allreduce parameters. - - Args: - params (list[torch.Parameters]): List of parameters or buffers of a - model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - _, world_size = get_dist_info() - if world_size == 1: - return - params = [param.data for param in params] - if coalesce: - _allreduce_coalesced(params, world_size, bucket_size_mb) - else: - for tensor in params: - dist.all_reduce(tensor.div_(world_size)) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - _, world_size = get_dist_info() - if world_size == 1: - return - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/epoch_based_runner.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/epoch_based_runner.py deleted file mode 100644 index 078e91df3..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/epoch_based_runner.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .utils import get_host_info - - -@RUNNERS.register_module() -class EpochBasedRunner(BaseRunner): - """Epoch-based Runner. - - This runner train models epoch by epoch. - """ - - def run_iter(self, data_batch, train_mode, **kwargs): - if self.batch_processor is not None: - outputs = self.batch_processor( - self.model, data_batch, train_mode=train_mode, **kwargs) - elif train_mode: - outputs = self.model.train_step(data_batch, self.optimizer, - **kwargs) - else: - outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('"batch_processor()" or "model.train_step()"' - 'and "model.val_step()" must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - self._iter += 1 - - self.call_hook('after_train_epoch') - self._epoch += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - self.call_hook('before_val_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_val_iter') - self.run_iter(data_batch, train_mode=False) - self.call_hook('after_val_iter') - - self.call_hook('after_val_epoch') - - def run(self, data_loaders, workflow, max_epochs=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, epochs) to specify the - running order and epochs. E.g, [('train', 2), ('val', 1)] means - running 2 epochs for training and 1 epoch for validation, - iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_epochs is not None: - warnings.warn( - 'setting max_epochs in run is deprecated, ' - 'please set max_epochs in runner_config', DeprecationWarning) - self._max_epochs = max_epochs - - assert self._max_epochs is not None, ( - 'max_epochs must be specified during instantiation') - - for i, flow in enumerate(workflow): - mode, epochs = flow - if mode == 'train': - self._max_iters = self._max_epochs * len(data_loaders[i]) - break - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d epochs', workflow, - self._max_epochs) - self.call_hook('before_run') - - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - mode, epochs = flow - if isinstance(mode, str): # self.train() - if not hasattr(self, mode): - raise ValueError( - f'runner has no method named "{mode}" to run an ' - 'epoch') - epoch_runner = getattr(self, mode) - else: - raise TypeError( - 'mode in workflow must be a str, but got {}'.format( - type(mode))) - - for _ in range(epochs): - if mode == 'train' and self.epoch >= self._max_epochs: - break - epoch_runner(data_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_run') - - def save_checkpoint(self, - out_dir, - filename_tmpl='epoch_{}.pth', - save_optimizer=True, - meta=None, - create_symlink=True): - """Save the checkpoint. - - Args: - out_dir (str): The directory that checkpoints are saved. - filename_tmpl (str, optional): The checkpoint filename template, - which contains a placeholder for the epoch number. - Defaults to 'epoch_{}.pth'. - save_optimizer (bool, optional): Whether to save the optimizer to - the checkpoint. Defaults to True. - meta (dict, optional): The meta information to be saved in the - checkpoint. Defaults to None. - create_symlink (bool, optional): Whether to create a symlink - "latest.pth" to point to the latest checkpoint. - Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.epoch + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - -@RUNNERS.register_module() -class Runner(EpochBasedRunner): - """Deprecated name of EpochBasedRunner.""" - - def __init__(self, *args, **kwargs): - warnings.warn( - 'Runner was deprecated, please use EpochBasedRunner instead', - DeprecationWarning) - super().__init__(*args, **kwargs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/fp16_utils.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/fp16_utils.py deleted file mode 100644 index be3ac3a31..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/fp16_utils.py +++ /dev/null @@ -1,423 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import warnings -from collections import abc -from inspect import getfullargspec - -import numpy as np -import torch -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .dist_utils import allreduce_grads as _allreduce_grads - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 - # manually, so the behavior may not be consistent with real amp. - from torch.cuda.amp import autocast -except ImportError: - pass - - -def cast_tensor_type(inputs, src_type, dst_type): - """Recursively convert Tensor in inputs from src_type to dst_type. - - Note: - In v1.4.4 and later, ``cast_tersor_type`` will only convert the - torch.Tensor which is consistent with ``src_type`` to the ``dst_type``. - Before v1.4.4, it ignores the ``src_type`` argument, leading to some - potential problems. For example, - ``cast_tensor_type(inputs, torch.float, torch.half)`` will convert all - tensors in inputs to ``torch.half`` including those originally in - ``torch.Int`` or other types, which is not expected. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype): Source type.. - dst_type (torch.dtype): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - if isinstance(inputs, nn.Module): - return inputs - elif isinstance(inputs, torch.Tensor): - # we need to ensure that the type of inputs to be casted are the same - # as the argument `src_type`. - return inputs.to(dst_type) if inputs.dtype == src_type else inputs - elif isinstance(inputs, str): - return inputs - elif isinstance(inputs, np.ndarray): - return inputs - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type, dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type, dst_type) for item in inputs) - else: - return inputs - - -def auto_fp16(apply_to=None, out_fp32=False, supported_types=(nn.Module, )): - """Decorator to enable fp16 training automatically. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If inputs arguments are fp32 tensors, they will - be converted to fp16 automatically. Arguments other than fp32 tensors are - ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp32 (bool): Whether to convert the output back to fp32. - supported_types (tuple): Classes can be decorated by ``auto_fp16``. - `New in version 1.5.0.` - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp16 - >>> @auto_fp16() - >>> def forward(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp16 - >>> @auto_fp16(apply_to=('pred', )) - >>> def do_something(self, pred, others): - >>> pass - """ - - def auto_fp16_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], supported_types): - raise TypeError('@auto_fp16 can only be used to decorate the ' - f'method of those classes {supported_types}') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - # NOTE: default args are not taken into consideration - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.float, torch.half)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = {} - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.float, torch.half) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=True): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp32: - output = cast_tensor_type(output, torch.half, torch.float) - return output - - return new_func - - return auto_fp16_wrapper - - -def force_fp32(apply_to=None, out_fp16=False): - """Decorator to convert input arguments to fp32 in force. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If there are some inputs that must be processed - in fp32 mode, then this decorator can handle it. If inputs arguments are - fp16 tensors, they will be converted to fp32 automatically. Arguments other - than fp16 tensors are ignored. If you are using PyTorch >= 1.6, - torch.cuda.amp is used as the backend, otherwise, original mmcv - implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp16 (bool): Whether to convert the output back to fp16. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp32 - >>> @force_fp32() - >>> def loss(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp32 - >>> @force_fp32(apply_to=('pred', )) - >>> def post_process(self, pred, others): - >>> pass - """ - - def force_fp32_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@force_fp32 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.half, torch.float)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = dict() - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.half, torch.float) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=False): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp16: - output = cast_tensor_type(output, torch.float, torch.half) - return output - - return new_func - - return force_fp32_wrapper - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - warnings.warning( - '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' - 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads', - DeprecationWarning) - _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) - - -def wrap_fp16_model(model): - """Wrap the FP32 model to FP16. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - For PyTorch >= 1.6, this function will - 1. Set fp16 flag inside the model to True. - - Otherwise: - 1. Convert FP32 model to FP16. - 2. Remain some necessary layers to be FP32, e.g., normalization layers. - 3. Set `fp16_enabled` flag inside the model to True. - - Args: - model (nn.Module): Model in FP32. - """ - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.6.0')): - # convert model to fp16 - model.half() - # patch the normalization layers to make it work in fp32 mode - patch_norm_fp32(model) - # set `fp16_enabled` flag - for m in model.modules(): - if hasattr(m, 'fp16_enabled'): - m.fp16_enabled = True - - -def patch_norm_fp32(module): - """Recursively convert normalization layers from FP16 to FP32. - - Args: - module (nn.Module): The modules to be converted in FP16. - - Returns: - nn.Module: The converted module, the normalization layers have been - converted to FP32. - """ - if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): - module.float() - if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': - module.forward = patch_forward_method(module.forward, torch.half, - torch.float) - for child in module.children(): - patch_norm_fp32(child) - return module - - -def patch_forward_method(func, src_type, dst_type, convert_output=True): - """Patch the forward method of a module. - - Args: - func (callable): The original forward method. - src_type (torch.dtype): Type of input arguments to be converted from. - dst_type (torch.dtype): Type of input arguments to be converted to. - convert_output (bool): Whether to convert the output back to src_type. - - Returns: - callable: The patched forward method. - """ - - def new_forward(*args, **kwargs): - output = func(*cast_tensor_type(args, src_type, dst_type), - **cast_tensor_type(kwargs, src_type, dst_type)) - if convert_output: - output = cast_tensor_type(output, dst_type, src_type) - return output - - return new_forward - - -class LossScaler: - """Class that manages loss scaling in mixed precision training which - supports both dynamic or static mode. - - The implementation refers to - https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. - Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. - It's important to understand how :class:`LossScaler` operates. - Loss scaling is designed to combat the problem of underflowing - gradients encountered at long times when training fp16 networks. - Dynamic loss scaling begins by attempting a very high loss - scale. Ironically, this may result in OVERflowing gradients. - If overflowing gradients are encountered, :class:`FP16_Optimizer` then - skips the update step for this particular iteration/minibatch, - and :class:`LossScaler` adjusts the loss scale to a lower value. - If a certain number of iterations occur without overflowing gradients - detected,:class:`LossScaler` increases the loss scale once more. - In this way :class:`LossScaler` attempts to "ride the edge" of always - using the highest loss scale possible without incurring overflow. - - Args: - init_scale (float): Initial loss scale value, default: 2**32. - scale_factor (float): Factor used when adjusting the loss scale. - Default: 2. - mode (str): Loss scaling mode. 'dynamic' or 'static' - scale_window (int): Number of consecutive iterations without an - overflow to wait before increasing the loss scale. Default: 1000. - """ - - def __init__(self, - init_scale=2**32, - mode='dynamic', - scale_factor=2., - scale_window=1000): - self.cur_scale = init_scale - self.cur_iter = 0 - assert mode in ('dynamic', - 'static'), 'mode can only be dynamic or static' - self.mode = mode - self.last_overflow_iter = -1 - self.scale_factor = scale_factor - self.scale_window = scale_window - - def has_overflow(self, params): - """Check if params contain overflow.""" - if self.mode != 'dynamic': - return False - for p in params: - if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): - return True - return False - - def _has_inf_or_nan(x): - """Check if params contain NaN.""" - try: - cpu_sum = float(x.float().sum()) - except RuntimeError as instance: - if 'value cannot be converted' not in instance.args[0]: - raise - return True - else: - if cpu_sum == float('inf') or cpu_sum == -float('inf') \ - or cpu_sum != cpu_sum: - return True - return False - - def update_scale(self, overflow): - """update the current loss scale value when overflow happens.""" - if self.mode != 'dynamic': - return - if overflow: - self.cur_scale = max(self.cur_scale / self.scale_factor, 1) - self.last_overflow_iter = self.cur_iter - else: - if (self.cur_iter - self.last_overflow_iter) % \ - self.scale_window == 0: - self.cur_scale *= self.scale_factor - self.cur_iter += 1 - - def state_dict(self): - """Returns the state of the scaler as a :class:`dict`.""" - return dict( - cur_scale=self.cur_scale, - cur_iter=self.cur_iter, - mode=self.mode, - last_overflow_iter=self.last_overflow_iter, - scale_factor=self.scale_factor, - scale_window=self.scale_window) - - def load_state_dict(self, state_dict): - """Loads the loss_scaler state dict. - - Args: - state_dict (dict): scaler state. - """ - self.cur_scale = state_dict['cur_scale'] - self.cur_iter = state_dict['cur_iter'] - self.mode = state_dict['mode'] - self.last_overflow_iter = state_dict['last_overflow_iter'] - self.scale_factor = state_dict['scale_factor'] - self.scale_window = state_dict['scale_window'] - - @property - def loss_scale(self): - return self.cur_scale diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/__init__.py deleted file mode 100644 index 03e2a619e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkpoint import CheckpointHook -from .closure import ClosureHook -from .ema import EMAHook -from .evaluation import DistEvalHook, EvalHook -from .hook import HOOKS, Hook -from .iter_timer import IterTimerHook -from .logger import (ClearMLLoggerHook, DvcliveLoggerHook, LoggerHook, - MlflowLoggerHook, NeptuneLoggerHook, PaviLoggerHook, - SegmindLoggerHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, InvLrUpdaterHook, - LinearAnnealingLrUpdaterHook, LrUpdaterHook, - OneCycleLrUpdaterHook, PolyLrUpdaterHook, - StepLrUpdaterHook) -from .memory import EmptyCacheHook -from .momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, OptimizerHook) -from .profiler import ProfilerHook -from .sampler_seed import DistSamplerSeedHook -from .sync_buffer import SyncBuffersHook - -__all__ = [ - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'OptimizerHook', - 'Fp16OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', - 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TextLoggerHook', 'TensorboardLoggerHook', 'NeptuneLoggerHook', - 'WandbLoggerHook', 'DvcliveLoggerHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'SyncBuffersHook', 'EMAHook', 'EvalHook', 'DistEvalHook', 'ProfilerHook', - 'GradientCumulativeOptimizerHook', 'GradientCumulativeFp16OptimizerHook', - 'SegmindLoggerHook', 'LinearAnnealingLrUpdaterHook', - 'LinearAnnealingMomentumUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/checkpoint.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/checkpoint.py deleted file mode 100644 index 7bb75f402..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/checkpoint.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -from mmcv.fileio import FileClient -from ..dist_utils import allreduce_params, master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class CheckpointHook(Hook): - """Save checkpoints periodically. - - Args: - interval (int): The saving period. If ``by_epoch=True``, interval - indicates epochs, otherwise it indicates iterations. - Default: -1, which means "never". - by_epoch (bool): Saving checkpoints by epoch or by iteration. - Default: True. - save_optimizer (bool): Whether to save optimizer state_dict in the - checkpoint. It is usually used for resuming experiments. - Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, ``runner.work_dir`` will be used by default. If - specified, the ``out_dir`` will be the concatenation of ``out_dir`` - and the last level directory of ``runner.work_dir``. - `Changed in version 1.3.16.` - max_keep_ckpts (int, optional): The maximum checkpoints to keep. - In some cases we want only the latest few checkpoints and would - like to delete old ones to save the disk space. - Default: -1, which means unlimited. - save_last (bool, optional): Whether to force the last checkpoint to be - saved regardless of interval. Default: True. - sync_buffer (bool, optional): Whether to synchronize buffers in - different gpus. Default: False. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - - .. warning:: - Before v1.3.16, the ``out_dir`` argument indicates the path where the - checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the - root directory and the final path to save checkpoint is the - concatenation of ``out_dir`` and the last level directory of - ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" - and the value of ``runner.work_dir`` is "/path/of/B", then the final - path will be "/path/of/A/B". - """ - - def __init__(self, - interval=-1, - by_epoch=True, - save_optimizer=True, - out_dir=None, - max_keep_ckpts=-1, - save_last=True, - sync_buffer=False, - file_client_args=None, - **kwargs): - self.interval = interval - self.by_epoch = by_epoch - self.save_optimizer = save_optimizer - self.out_dir = out_dir - self.max_keep_ckpts = max_keep_ckpts - self.save_last = save_last - self.args = kwargs - self.sync_buffer = sync_buffer - self.file_client_args = file_client_args - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - - runner.logger.info((f'Checkpoints will be saved to {self.out_dir} by ' - f'{self.file_client.name}.')) - - # disable the create_symlink option because some file backends do not - # allow to create a symlink - if 'create_symlink' in self.args: - if self.args[ - 'create_symlink'] and not self.file_client.allow_symlink: - self.args['create_symlink'] = False - warnings.warn( - ('create_symlink is set as True by the user but is changed' - 'to be False because creating symbolic link is not ' - f'allowed in {self.file_client.name}')) - else: - self.args['create_symlink'] = self.file_client.allow_symlink - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` epochs - # 2. reach the last epoch of training - if self.every_n_epochs( - runner, self.interval) or (self.save_last - and self.is_last_epoch(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.epoch + 1} epochs') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) - - @master_only - def _save_checkpoint(self, runner): - """Save the current checkpoint and delete unwanted checkpoint.""" - runner.save_checkpoint( - self.out_dir, save_optimizer=self.save_optimizer, **self.args) - if runner.meta is not None: - if self.by_epoch: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) - else: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) - runner.meta.setdefault('hook_msgs', dict()) - runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( - self.out_dir, cur_ckpt_filename) - # remove other checkpoints - if self.max_keep_ckpts > 0: - if self.by_epoch: - name = 'epoch_{}.pth' - current_ckpt = runner.epoch + 1 - else: - name = 'iter_{}.pth' - current_ckpt = runner.iter + 1 - redundant_ckpts = range( - current_ckpt - self.max_keep_ckpts * self.interval, 0, - -self.interval) - filename_tmpl = self.args.get('filename_tmpl', name) - for _step in redundant_ckpts: - ckpt_path = self.file_client.join_path( - self.out_dir, filename_tmpl.format(_step)) - if self.file_client.isfile(ckpt_path): - self.file_client.remove(ckpt_path) - else: - break - - def after_train_iter(self, runner): - if self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` iterations - # 2. reach the last iteration of training - if self.every_n_iters( - runner, self.interval) or (self.save_last - and self.is_last_iter(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.iter + 1} iterations') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/closure.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/closure.py deleted file mode 100644 index b955f81f4..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/closure.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ClosureHook(Hook): - - def __init__(self, fn_name, fn): - assert hasattr(self, fn_name) - assert callable(fn) - setattr(self, fn_name, fn) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/ema.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/ema.py deleted file mode 100644 index 6ed77b84e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/ema.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...parallel import is_module_wrapper -from ..hooks.hook import HOOKS, Hook - - -@HOOKS.register_module() -class EMAHook(Hook): - r"""Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. - - .. math:: - - Xema\_{t+1} = (1 - \text{momentum}) \times - Xema\_{t} + \text{momentum} \times X_t - - Args: - momentum (float): The momentum used for updating ema parameter. - Defaults to 0.0002. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - warm_up (int): During first warm_up steps, we may use smaller momentum - to update ema parameters more slowly. Defaults to 100. - resume_from (str): The checkpoint path. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - warm_up=100, - resume_from=None): - assert isinstance(interval, int) and interval > 0 - self.warm_up = warm_up - self.interval = interval - assert momentum > 0 and momentum < 1 - self.momentum = momentum**interval - self.checkpoint = resume_from - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - self.model_parameters = dict(model.named_parameters(recurse=True)) - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers(recurse=True)) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - curr_step = runner.iter - # We warm up the momentum considering the instability at beginning - momentum = min(self.momentum, - (1 + curr_step) / (self.warm_up + curr_step)) - if curr_step % self.interval != 0: - return - for name, parameter in self.model_parameters.items(): - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/evaluation.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/evaluation.py deleted file mode 100644 index 2a57d535e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/evaluation.py +++ /dev/null @@ -1,511 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from math import inf - -import torch.distributed as dist -from torch.nn.modules.batchnorm import _BatchNorm -from torch.utils.data import DataLoader - -from mmcv.fileio import FileClient -from mmcv.utils import is_seq_of -from .hook import Hook -from .logger import LoggerHook - - -class EvalHook(Hook): - """Non-Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader, and return the test results. If ``None``, the default - test function ``mmcv.engine.single_gpu_test`` will be used. - (default: ``None``) - greater_keys (List[str] | None, optional): Metric keys that will be - inferred by 'greater' comparison rule. If ``None``, - _default_greater_keys will be used. (default: ``None``) - less_keys (List[str] | None, optional): Metric keys that will be - inferred by 'less' comparison rule. If ``None``, _default_less_keys - will be used. (default: ``None``) - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - `New in version 1.3.16.` - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - `New in version 1.3.16.` - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - - Note: - If new arguments are added for EvalHook, tools/test.py, - tools/eval_metric.py may be affected. - """ - - # Since the key for determine greater or less is related to the downstream - # tasks, downstream repos may need to overwrite the following inner - # variable accordingly. - - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - _default_greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - _default_less_keys = ['loss'] - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - out_dir=None, - file_client_args=None, - **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError(f'dataloader must be a pytorch DataLoader, ' - f'but got {type(dataloader)}') - - if interval <= 0: - raise ValueError(f'interval must be a positive number, ' - f'but got {interval}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' - - if start is not None and start < 0: - raise ValueError(f'The evaluation start epoch {start} is smaller ' - f'than 0') - - self.dataloader = dataloader - self.interval = interval - self.start = start - self.by_epoch = by_epoch - - assert isinstance(save_best, str) or save_best is None, \ - '""save_best"" should be a str or None ' \ - f'rather than {type(save_best)}' - self.save_best = save_best - self.eval_kwargs = eval_kwargs - self.initial_flag = True - - if test_fn is None: - from mmcv.engine import single_gpu_test - self.test_fn = single_gpu_test - else: - self.test_fn = test_fn - - if greater_keys is None: - self.greater_keys = self._default_greater_keys - else: - if not isinstance(greater_keys, (list, tuple)): - greater_keys = (greater_keys, ) - assert is_seq_of(greater_keys, str) - self.greater_keys = greater_keys - - if less_keys is None: - self.less_keys = self._default_less_keys - else: - if not isinstance(less_keys, (list, tuple)): - less_keys = (less_keys, ) - assert is_seq_of(less_keys, str) - self.less_keys = less_keys - - if self.save_best is not None: - self.best_ckpt_path = None - self._init_rule(rule, self.save_best) - - self.out_dir = out_dir - self.file_client_args = file_client_args - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific (note that the key indicator matching - is case-insensitive): - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator != 'auto': - # `_lc` here means we use the lower case of keys for - # case-insensitive matching - key_indicator_lc = key_indicator.lower() - greater_keys = [key.lower() for key in self.greater_keys] - less_keys = [key.lower() for key in self.less_keys] - - if key_indicator_lc in greater_keys: - rule = 'greater' - elif key_indicator_lc in less_keys: - rule = 'less' - elif any(key in key_indicator_lc for key in greater_keys): - rule = 'greater' - elif any(key in key_indicator_lc for key in less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - if self.rule is not None: - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'The best checkpoint will be saved to {self.out_dir} by ' - f'{self.file_client.name}')) - - if self.save_best is not None: - if runner.meta is None: - warnings.warn('runner.meta is None. Creating an empty one.') - runner.meta = dict() - runner.meta.setdefault('hook_msgs', dict()) - self.best_ckpt_path = runner.meta['hook_msgs'].get( - 'best_ckpt', None) - - def before_train_iter(self, runner): - """Evaluate the model only at the start of training by iteration.""" - if self.by_epoch or not self.initial_flag: - return - if self.start is not None and runner.iter >= self.start: - self.after_train_iter(runner) - self.initial_flag = False - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - if not (self.by_epoch and self.initial_flag): - return - if self.start is not None and runner.epoch >= self.start: - self.after_train_epoch(runner) - self.initial_flag = False - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch and self._should_evaluate(runner): - # Because the priority of EvalHook is higher than LoggerHook, the - # training log and the evaluating log are mixed. Therefore, - # we need to dump the training log and clear it before evaluating - # log is generated. In addition, this problem will only appear in - # `IterBasedRunner` whose `self.by_epoch` is False, because - # `EpochBasedRunner` whose `self.by_epoch` is True calls - # `_do_evaluate` in `after_train_epoch` stage, and at this stage - # the training log has been printed, so it will not cause any - # problem. more details at - # https://github.com/open-mmlab/mmsegmentation/issues/694 - for hook in runner._hooks: - if isinstance(hook, LoggerHook): - hook.after_train_iter(runner) - runner.log_buffer.clear() - - self._do_evaluate(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch and self._should_evaluate(runner): - self._do_evaluate(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - results = self.test_fn(runner.model, self.dataloader) - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - def _should_evaluate(self, runner): - """Judge whether to perform evaluation. - - Here is the rule to judge whether to perform evaluation: - 1. It will not perform evaluation during the epoch/iteration interval, - which is determined by ``self.interval``. - 2. It will not perform evaluation if the start time is larger than - current time. - 3. It will not perform evaluation when current time is larger than - the start time but during epoch/iteration interval. - - Returns: - bool: The flag indicating whether to perform evaluation. - """ - if self.by_epoch: - current = runner.epoch - check_time = self.every_n_epochs - else: - current = runner.iter - check_time = self.every_n_iters - - if self.start is None: - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - elif (current + 1) < self.start: - # No evaluation if start is larger than the current time. - return False - else: - # Evaluation only at epochs/iters 3, 5, 7... - # if start==3 and interval==2 - if (current + 1 - self.start) % self.interval: - return False - return True - - def _save_ckpt(self, runner, key_score): - """Save the best checkpoint. - - It will compare the score according to the compare function, write - related information (best score, best checkpoint path) and save the - best checkpoint into ``work_dir``. - """ - if self.by_epoch: - current = f'epoch_{runner.epoch + 1}' - cur_type, cur_time = 'epoch', runner.epoch + 1 - else: - current = f'iter_{runner.iter + 1}' - cur_type, cur_time = 'iter', runner.iter + 1 - - best_score = runner.meta['hook_msgs'].get( - 'best_score', self.init_value_map[self.rule]) - if self.compare_func(key_score, best_score): - best_score = key_score - runner.meta['hook_msgs']['best_score'] = best_score - - if self.best_ckpt_path and self.file_client.isfile( - self.best_ckpt_path): - self.file_client.remove(self.best_ckpt_path) - runner.logger.info( - (f'The previous best checkpoint {self.best_ckpt_path} was ' - 'removed')) - - best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' - self.best_ckpt_path = self.file_client.join_path( - self.out_dir, best_ckpt_name) - runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path - - runner.save_checkpoint( - self.out_dir, - filename_tmpl=best_ckpt_name, - create_symlink=False) - runner.logger.info( - f'Now best checkpoint is saved as {best_ckpt_name}.') - runner.logger.info( - f'Best {self.key_indicator} is {best_score:0.4f} ' - f'at {cur_time} {cur_type}.') - - def evaluate(self, runner, results): - """Evaluate the results. - - Args: - runner (:obj:`mmcv.Runner`): The underlined training runner. - results (list): Output results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - - if self.save_best is not None: - # If the performance of model is pool, the `eval_res` may be an - # empty dict and it will raise exception when `self.save_best` is - # not None. More details at - # https://github.com/open-mmlab/mmdetection/issues/6265. - if not eval_res: - warnings.warn( - 'Since `eval_res` is an empty dict, the behavior to save ' - 'the best checkpoint will be skipped in this evaluation.') - return None - - if self.key_indicator == 'auto': - # infer from eval_results - self._init_rule(self.rule, list(eval_res.keys())[0]) - return eval_res[self.key_indicator] - - return None - - -class DistEvalHook(EvalHook): - """Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader in a multi-gpu manner, and return the test results. If - ``None``, the default test function ``mmcv.engine.multi_gpu_test`` - will be used. (default: ``None``) - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - broadcast_bn_buffer (bool): Whether to broadcast the - buffer(running_mean and running_var) of rank 0 to other rank - before evaluation. Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - """ - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - broadcast_bn_buffer=True, - tmpdir=None, - gpu_collect=False, - out_dir=None, - file_client_args=None, - **eval_kwargs): - - if test_fn is None: - from mmcv.engine import multi_gpu_test - test_fn = multi_gpu_test - - super().__init__( - dataloader, - start=start, - interval=interval, - by_epoch=by_epoch, - save_best=save_best, - rule=rule, - test_fn=test_fn, - greater_keys=greater_keys, - less_keys=less_keys, - out_dir=out_dir, - file_client_args=file_client_args, - **eval_kwargs) - - self.broadcast_bn_buffer = broadcast_bn_buffer - self.tmpdir = tmpdir - self.gpu_collect = gpu_collect - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - results = self.test_fn( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to - # save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/hook.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/hook.py deleted file mode 100644 index f2d1c9865..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/hook.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, is_method_overridden - -HOOKS = Registry('hook') - - -class Hook: - stages = ('before_run', 'before_train_epoch', 'before_train_iter', - 'after_train_iter', 'after_train_epoch', 'before_val_epoch', - 'before_val_iter', 'after_val_iter', 'after_val_epoch', - 'after_run') - - def before_run(self, runner): - pass - - def after_run(self, runner): - pass - - def before_epoch(self, runner): - pass - - def after_epoch(self, runner): - pass - - def before_iter(self, runner): - pass - - def after_iter(self, runner): - pass - - def before_train_epoch(self, runner): - self.before_epoch(runner) - - def before_val_epoch(self, runner): - self.before_epoch(runner) - - def after_train_epoch(self, runner): - self.after_epoch(runner) - - def after_val_epoch(self, runner): - self.after_epoch(runner) - - def before_train_iter(self, runner): - self.before_iter(runner) - - def before_val_iter(self, runner): - self.before_iter(runner) - - def after_train_iter(self, runner): - self.after_iter(runner) - - def after_val_iter(self, runner): - self.after_iter(runner) - - def every_n_epochs(self, runner, n): - return (runner.epoch + 1) % n == 0 if n > 0 else False - - def every_n_inner_iters(self, runner, n): - return (runner.inner_iter + 1) % n == 0 if n > 0 else False - - def every_n_iters(self, runner, n): - return (runner.iter + 1) % n == 0 if n > 0 else False - - def end_of_epoch(self, runner): - return runner.inner_iter + 1 == len(runner.data_loader) - - def is_last_epoch(self, runner): - return runner.epoch + 1 == runner._max_epochs - - def is_last_iter(self, runner): - return runner.iter + 1 == runner._max_iters - - def get_triggered_stages(self): - trigger_stages = set() - for stage in Hook.stages: - if is_method_overridden(stage, Hook, self): - trigger_stages.add(stage) - - # some methods will be triggered in multi stages - # use this dict to map method to stages. - method_stages_map = { - 'before_epoch': ['before_train_epoch', 'before_val_epoch'], - 'after_epoch': ['after_train_epoch', 'after_val_epoch'], - 'before_iter': ['before_train_iter', 'before_val_iter'], - 'after_iter': ['after_train_iter', 'after_val_iter'], - } - - for method, map_stages in method_stages_map.items(): - if is_method_overridden(method, Hook, self): - trigger_stages.update(map_stages) - - return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/iter_timer.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/iter_timer.py deleted file mode 100644 index 16531be96..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/iter_timer.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import time -import torch.distributed as dist - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class IterTimerHook(Hook): - - def before_epoch(self, runner): - self.t = time.time() - - def before_iter(self, runner): - self.batch_size = runner.cfg.data.samples_per_gpu - runner.log_buffer.update({'data_time': time.time() - self.t}) - - def after_iter(self, runner): - iter_info = {'time': time.time() - self.t} - fps = self.batch_size / iter_info["time"] - - if dist.is_initialized(): - fps = fps * dist.get_world_size() - - iter_info["fps"] = fps - - runner.log_buffer.update(iter_info) - self.t = time.time() diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/__init__.py deleted file mode 100644 index 062709e70..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import LoggerHook -from .clearml import ClearMLLoggerHook -from .dvclive import DvcliveLoggerHook -from .mlflow import MlflowLoggerHook -from .neptune import NeptuneLoggerHook -from .pavi import PaviLoggerHook -from .segmind import SegmindLoggerHook -from .tensorboard import TensorboardLoggerHook -from .text import TextLoggerHook -from .wandb import WandbLoggerHook - -__all__ = [ - 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', - 'NeptuneLoggerHook', 'DvcliveLoggerHook', 'SegmindLoggerHook', - 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/base.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/base.py deleted file mode 100644 index 9f1a51b31..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/base.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from abc import ABCMeta, abstractmethod - -import numpy as np -import torch - -from ..hook import Hook - - -class LoggerHook(Hook): - """Base class for logger hooks. - - Args: - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - """ - - __metaclass__ = ABCMeta - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - self.interval = interval - self.ignore_last = ignore_last - self.reset_flag = reset_flag - self.by_epoch = by_epoch - - @abstractmethod - def log(self, runner): - pass - - @staticmethod - def is_scalar(val, include_np=True, include_torch=True): - """Tell the input variable is a scalar or not. - - Args: - val: Input variable. - include_np (bool): Whether include 0-d np.ndarray as a scalar. - include_torch (bool): Whether include 0-d torch.Tensor as a scalar. - - Returns: - bool: True or False. - """ - if isinstance(val, numbers.Number): - return True - elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: - return True - elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: - return True - else: - return False - - def get_mode(self, runner): - if runner.mode == 'train': - if 'time' in runner.log_buffer.output: - mode = 'train' - else: - mode = 'val' - elif runner.mode == 'val': - mode = 'val' - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return mode - - def get_epoch(self, runner): - if runner.mode == 'train': - epoch = runner.epoch + 1 - elif runner.mode == 'val': - # normal val mode - # runner.epoch += 1 has been done before val workflow - epoch = runner.epoch - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return epoch - - def get_iter(self, runner, inner_iter=False): - """Get the current training iteration step.""" - if self.by_epoch and inner_iter: - current_iter = runner.inner_iter + 1 - else: - current_iter = runner.iter + 1 - return current_iter - - def get_lr_tags(self, runner): - tags = {} - lrs = runner.current_lr() - if isinstance(lrs, dict): - for name, value in lrs.items(): - tags[f'learning_rate/{name}'] = value[0] - else: - tags['learning_rate'] = lrs[0] - return tags - - def get_momentum_tags(self, runner): - tags = {} - momentums = runner.current_momentum() - if isinstance(momentums, dict): - for name, value in momentums.items(): - tags[f'momentum/{name}'] = value[0] - else: - tags['momentum'] = momentums[0] - return tags - - def get_loggable_tags(self, - runner, - allow_scalar=True, - allow_text=False, - add_mode=True, - tags_to_skip=('time', 'data_time')): - tags = {} - for var, val in runner.log_buffer.output.items(): - if var in tags_to_skip: - continue - if self.is_scalar(val) and not allow_scalar: - continue - if isinstance(val, str) and not allow_text: - continue - if add_mode: - var = f'{self.get_mode(runner)}/{var}' - tags[var] = val - tags.update(self.get_lr_tags(runner)) - tags.update(self.get_momentum_tags(runner)) - return tags - - def before_run(self, runner): - for hook in runner.hooks[::-1]: - if isinstance(hook, LoggerHook): - hook.reset_flag = True - break - - def before_epoch(self, runner): - runner.log_buffer.clear() # clear logs of last epoch - - def after_train_iter(self, runner): - if self.by_epoch and self.every_n_inner_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif not self.by_epoch and self.every_n_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif self.end_of_epoch(runner) and not self.ignore_last: - # not precise but more stable - runner.log_buffer.average(self.interval) - - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_train_epoch(self, runner): - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_val_epoch(self, runner): - runner.log_buffer.average() - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/clearml.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/clearml.py deleted file mode 100644 index 1de71e152..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/clearml.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class ClearMLLoggerHook(LoggerHook): - """Class to log metrics with clearml. - - It requires `clearml`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the `clearml.Task.init` - initialization keys. See `taskinit`_ for more details. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _clearml: - https://clear.ml/docs/latest/docs/ - .. _taskinit: - https://clear.ml/docs/latest/docs/references/sdk/task/#taskinit - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(ClearMLLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_clearml() - self.init_kwargs = init_kwargs - - def import_clearml(self): - try: - import clearml - except ImportError: - raise ImportError( - 'Please run "pip install clearml" to install clearml') - self.clearml = clearml - - @master_only - def before_run(self, runner): - super(ClearMLLoggerHook, self).before_run(runner) - task_kwargs = self.init_kwargs if self.init_kwargs else {} - self.task = self.clearml.Task.init(**task_kwargs) - self.task_logger = self.task.get_logger() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - for tag, val in tags.items(): - self.task_logger.report_scalar(tag, tag, val, - self.get_iter(runner)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/dvclive.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/dvclive.py deleted file mode 100644 index c79eefa75..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/dvclive.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from pathlib import Path - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class DvcliveLoggerHook(LoggerHook): - """Class to log metrics with dvclive. - - It requires `dvclive`_ to be installed. - - Args: - model_file (str): Default None. If not None, after each epoch the - model will be saved to {model_file}. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - kwargs: Arguments for instantiating `Live`_. - - .. _dvclive: - https://dvc.org/doc/dvclive - - .. _Live: - https://dvc.org/doc/dvclive/api-reference/live#parameters - """ - - def __init__(self, - model_file=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - **kwargs): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.model_file = model_file - self.import_dvclive(**kwargs) - - def import_dvclive(self, **kwargs): - try: - from dvclive import Live - except ImportError: - raise ImportError( - 'Please run "pip install dvclive" to install dvclive') - self.dvclive = Live(**kwargs) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.dvclive.set_step(self.get_iter(runner)) - for k, v in tags.items(): - self.dvclive.log(k, v) - - @master_only - def after_train_epoch(self, runner): - super().after_train_epoch(runner) - if self.model_file is not None: - runner.save_checkpoint( - Path(self.model_file).parent, - filename_tmpl=Path(self.model_file).name, - create_symlink=False, - ) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/mlflow.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/mlflow.py deleted file mode 100644 index dcd87bcb5..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/mlflow.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import TORCH_VERSION -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class MlflowLoggerHook(LoggerHook): - """Class to log metrics and (optionally) a trained model to MLflow. - - It requires `MLflow`_ to be installed. - - Args: - exp_name (str, optional): Name of the experiment to be used. - Default None. If not None, set the active experiment. - If experiment does not exist, an experiment with provided name - will be created. - tags (Dict[str], optional): Tags for the current run. - Default None. If not None, set tags for the current run. - log_model (bool, optional): Whether to log an MLflow artifact. - Default True. If True, log runner.model as an MLflow artifact - for the current run. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _MLflow: - https://www.mlflow.org/docs/latest/index.html - """ - - def __init__(self, - exp_name=None, - tags=None, - log_model=True, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(MlflowLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_mlflow() - self.exp_name = exp_name - self.tags = tags - self.log_model = log_model - - def import_mlflow(self): - try: - import mlflow - import mlflow.pytorch as mlflow_pytorch - except ImportError: - raise ImportError( - 'Please run "pip install mlflow" to install mlflow') - self.mlflow = mlflow - self.mlflow_pytorch = mlflow_pytorch - - @master_only - def before_run(self, runner): - super(MlflowLoggerHook, self).before_run(runner) - if self.exp_name is not None: - self.mlflow.set_experiment(self.exp_name) - if self.tags is not None: - self.mlflow.set_tags(self.tags) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.mlflow.log_metrics(tags, step=self.get_iter(runner)) - - @master_only - def after_run(self, runner): - if self.log_model: - self.mlflow_pytorch.log_model( - runner.model, - 'models', - pip_requirements=[f'torch=={TORCH_VERSION}']) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/neptune.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/neptune.py deleted file mode 100644 index e0aafe91d..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/neptune.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class NeptuneLoggerHook(LoggerHook): - """Class to log metrics to NeptuneAI. - - It requires `Neptune`_ to be installed. - - Args: - init_kwargs (dict): a dict contains the initialization keys as below: - - - project (str): Name of a project in a form of - namespace/project_name. If None, the value of NEPTUNE_PROJECT - environment variable will be taken. - - api_token (str): User’s API token. If None, the value of - NEPTUNE_API_TOKEN environment variable will be taken. Note: It is - strongly recommended to use NEPTUNE_API_TOKEN environment - variable rather than placing your API token in plain text in your - source code. - - name (str, optional, default is 'Untitled'): Editable name of the - run. Name is displayed in the run's Details and in Runs table as - a column. - - Check https://docs.neptune.ai/api-reference/neptune#init for more - init arguments. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than ``interval``. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _Neptune: - https://docs.neptune.ai - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=True, - with_step=True, - by_epoch=True): - - super(NeptuneLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_neptune() - self.init_kwargs = init_kwargs - self.with_step = with_step - - def import_neptune(self): - try: - import neptune.new as neptune - except ImportError: - raise ImportError( - 'Please run "pip install neptune-client" to install neptune') - self.neptune = neptune - self.run = None - - @master_only - def before_run(self, runner): - if self.init_kwargs: - self.run = self.neptune.init(**self.init_kwargs) - else: - self.run = self.neptune.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for tag_name, tag_value in tags.items(): - if self.with_step: - self.run[tag_name].log( - tag_value, step=self.get_iter(runner)) - else: - tags['global_step'] = self.get_iter(runner) - self.run[tag_name].log(tags) - - @master_only - def after_run(self, runner): - self.run.stop() diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/pavi.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/pavi.py deleted file mode 100644 index d5d61f9e5..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/pavi.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp - -import torch -import yaml - -import mmcv -from ....parallel.utils import is_module_wrapper -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class PaviLoggerHook(LoggerHook): - """Class to visual model, log metrics (for internal use). - - Args: - init_kwargs (dict): A dict contains the initialization keys. - add_graph (bool): Whether to visual model. Default: False. - add_last_ckpt (bool): Whether to save checkpoint after run. - Default: False. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - img_key (string): Get image data from Dataset. Default: 'img_info'. - """ - - def __init__(self, - init_kwargs=None, - add_graph=False, - add_last_ckpt=False, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - img_key='img_info'): - super(PaviLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.init_kwargs = init_kwargs - self.add_graph = add_graph - self.add_last_ckpt = add_last_ckpt - self.img_key = img_key - - @master_only - def before_run(self, runner): - super(PaviLoggerHook, self).before_run(runner) - try: - from pavi import SummaryWriter - except ImportError: - raise ImportError('Please run "pip install pavi" to install pavi.') - - self.run_name = runner.work_dir.split('/')[-1] - - if not self.init_kwargs: - self.init_kwargs = dict() - self.init_kwargs['name'] = self.run_name - self.init_kwargs['model'] = runner._model_name - if runner.meta is not None: - if 'config_dict' in runner.meta: - config_dict = runner.meta['config_dict'] - assert isinstance( - config_dict, - dict), ('meta["config_dict"] has to be of a dict, ' - f'but got {type(config_dict)}') - elif 'config_file' in runner.meta: - config_file = runner.meta['config_file'] - config_dict = dict(mmcv.Config.fromfile(config_file)) - else: - config_dict = None - if config_dict is not None: - # 'max_.*iter' is parsed in pavi sdk as the maximum iterations - # to properly set up the progress bar. - config_dict = config_dict.copy() - config_dict.setdefault('max_iter', runner.max_iters) - # non-serializable values are first converted in - # mmcv.dump to json - config_dict = json.loads( - mmcv.dump(config_dict, file_format='json')) - session_text = yaml.dump(config_dict) - self.init_kwargs['session_text'] = session_text - self.writer = SummaryWriter(**self.init_kwargs) - - def get_step(self, runner): - """Get the total training step/epoch.""" - if self.get_mode(runner) == 'val' and self.by_epoch: - return self.get_epoch(runner) - else: - return self.get_iter(runner) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, add_mode=False) - if tags: - self.writer.add_scalars( - self.get_mode(runner), tags, self.get_step(runner)) - - @master_only - def after_run(self, runner): - if self.add_last_ckpt: - ckpt_path = osp.join(runner.work_dir, 'latest.pth') - if osp.islink(ckpt_path): - ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) - - if osp.isfile(ckpt_path): - # runner.epoch += 1 has been done before `after_run`. - iteration = runner.epoch if self.by_epoch else runner.iter - return self.writer.add_snapshot_file( - tag=self.run_name, - snapshot_file_path=ckpt_path, - iteration=iteration) - - # flush the buffer and send a task ending signal to Pavi - self.writer.close() - - @master_only - def before_epoch(self, runner): - if runner.epoch == 0 and self.add_graph: - if is_module_wrapper(runner.model): - _model = runner.model.module - else: - _model = runner.model - device = next(_model.parameters()).device - data = next(iter(runner.data_loader)) - image = data[self.img_key][0:1].to(device) - with torch.no_grad(): - self.writer.add_graph(_model, image) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/segmind.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/segmind.py deleted file mode 100644 index e262c7c1a..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/segmind.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class SegmindLoggerHook(LoggerHook): - """Class to log metrics to Segmind. - - It requires `Segmind`_ to be installed. - - Args: - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - - .. _Segmind: - https://docs.segmind.com/python-library - """ - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(SegmindLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_segmind() - - def import_segmind(self): - try: - import segmind - except ImportError: - raise ImportError( - "Please run 'pip install segmind' to install segmind") - self.log_metrics = segmind.tracking.fluent.log_metrics - self.mlflow_log = segmind.utils.logging_utils.try_mlflow_log - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - # logging metrics to segmind - self.mlflow_log( - self.log_metrics, tags, step=runner.epoch, epoch=runner.epoch) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py deleted file mode 100644 index bf00d5742..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import TORCH_VERSION, digit_version -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TensorboardLoggerHook(LoggerHook): - """Class to log metrics to Tensorboard. - - Args: - log_dir (string): Save directory location. Default: None. If default - values are used, directory location is ``runner.work_dir``/tf_logs. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - """ - - def __init__(self, - log_dir=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(TensorboardLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.log_dir = log_dir - - @master_only - def before_run(self, runner): - super(TensorboardLoggerHook, self).before_run(runner) - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.1')): - try: - from tensorboardX import SummaryWriter - except ImportError: - raise ImportError('Please install tensorboardX to use ' - 'TensorboardLoggerHook.') - else: - try: - from torch.utils.tensorboard import SummaryWriter - except ImportError: - raise ImportError( - 'Please run "pip install future tensorboard" to install ' - 'the dependencies to use torch.utils.tensorboard ' - '(applicable to PyTorch 1.1 or higher)') - - if self.log_dir is None: - self.log_dir = osp.join(runner.work_dir, 'tf_logs') - self.writer = SummaryWriter(self.log_dir) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, allow_text=True) - for tag, val in tags.items(): - if isinstance(val, str): - self.writer.add_text(tag, val, self.get_iter(runner)) - else: - self.writer.add_scalar(tag, val, self.get_iter(runner)) - - @master_only - def after_run(self, runner): - self.writer.close() diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/text.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/text.py deleted file mode 100644 index 644ced2c9..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/text.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import datetime -import os -import os.path as osp -from collections import OrderedDict - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.fileio.file_client import FileClient -from mmcv.utils import is_tuple_of, scandir -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TextLoggerHook(LoggerHook): - """Logger hook in text. - - In this logger hook, the information will be printed on terminal and - saved in json file. - - Args: - by_epoch (bool, optional): Whether EpochBasedRunner is used. - Default: True. - interval (int, optional): Logging interval (every k iterations). - Default: 10. - ignore_last (bool, optional): Ignore the log of last iterations in each - epoch if less than :attr:`interval`. Default: True. - reset_flag (bool, optional): Whether to clear the output buffer after - logging. Default: False. - interval_exp_name (int, optional): Logging interval for experiment - name. This feature is to help users conveniently get the experiment - information from screen or log file. Default: 1000. - out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. - If ``out_dir`` is specified, logs will be copied to a new directory - which is the concatenation of ``out_dir`` and the last level - directory of ``runner.work_dir``. Default: None. - `New in version 1.3.16.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be copied to ``out_dir``. - Default: ('.log.json', '.log', '.py'). - `New in version 1.3.16.` - keep_local (bool, optional): Whether to keep local log when - :attr:`out_dir` is specified. If False, the local log will be - removed. Default: True. - `New in version 1.3.16.` - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - - def __init__(self, - by_epoch=True, - interval=10, - ignore_last=True, - reset_flag=False, - interval_exp_name=1000, - out_dir=None, - out_suffix=('.log.json', '.log', '.py'), - keep_local=True, - file_client_args=None): - super(TextLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.by_epoch = by_epoch - self.time_sec_tot = 0 - self.interval_exp_name = interval_exp_name - - if out_dir is None and file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" when `out_dir` is not' - 'specified.') - self.out_dir = out_dir - - if not (out_dir is None or isinstance(out_dir, str) - or is_tuple_of(out_dir, str)): - raise TypeError('out_dir should be "None" or string or tuple of ' - 'string, but got {out_dir}') - self.out_suffix = out_suffix - - self.keep_local = keep_local - self.file_client_args = file_client_args - if self.out_dir is not None: - self.file_client = FileClient.infer_client(file_client_args, - self.out_dir) - - def before_run(self, runner): - super(TextLoggerHook, self).before_run(runner) - - if self.out_dir is not None: - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - # The final `self.out_dir` is the concatenation of `self.out_dir` - # and the last level directory of `runner.work_dir` - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'Text logs will be saved to {self.out_dir} by ' - f'{self.file_client.name} after the training process.')) - - self.start_iter = runner.iter - self.json_log_path = osp.join(runner.work_dir, - f'{runner.timestamp}.log.json') - if runner.meta is not None: - self._dump_log(runner.meta, runner) - - def _get_max_memory(self, runner): - device = getattr(runner.model, 'output_device', None) - mem = torch.cuda.max_memory_allocated(device=device) - mem_mb = torch.tensor([int(mem) // (1024 * 1024)], - dtype=torch.int, - device=device) - if runner.world_size > 1: - dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) - return mem_mb.item() - - def _log_info(self, log_dict, runner): - # print exp name for users to distinguish experiments - # at every ``interval_exp_name`` iterations and the end of each epoch - if runner.meta is not None and 'exp_name' in runner.meta: - if (self.every_n_iters(runner, self.interval_exp_name)) or ( - self.by_epoch and self.end_of_epoch(runner)): - exp_info = f'Exp name: {runner.meta["exp_name"]}' - runner.logger.info(exp_info) - - if log_dict['mode'] == 'train': - if isinstance(log_dict['lr'], dict): - lr_str = [] - for k, val in log_dict['lr'].items(): - lr_str.append(f'lr_{k}: {val:.3e}') - lr_str = ' '.join(lr_str) - else: - lr_str = f'lr: {log_dict["lr"]:.3e}' - - # by epoch: Epoch [4][100/1000] - # by iter: Iter [100/100000] - if self.by_epoch: - log_str = f'Epoch [{log_dict["epoch"]}]' \ - f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' - else: - log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' - log_str += f'{lr_str}, ' - - if 'time' in log_dict.keys(): - self.time_sec_tot += (log_dict['time'] * self.interval) - time_sec_avg = self.time_sec_tot / ( - runner.iter - self.start_iter + 1) - eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) - eta_str = str(datetime.timedelta(seconds=int(eta_sec))) - log_str += f'eta: {eta_str}, ' - log_str += f'time: {log_dict["time"]:.3f}, ' \ - f'data_time: {log_dict["data_time"]:.3f}, ' - # statistic memory - if torch.cuda.is_available(): - log_str += f'memory: {log_dict["memory"]}, ' - else: - # val/test time - # here 1000 is the length of the val dataloader - # by epoch: Epoch[val] [4][1000] - # by iter: Iter[val] [1000] - if self.by_epoch: - log_str = f'Epoch({log_dict["mode"]}) ' \ - f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' - else: - log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' - - log_items = [] - for name, val in log_dict.items(): - # TODO: resolve this hack - # these items have been in log_str - if name in [ - 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', - 'memory', 'epoch' - ]: - continue - if isinstance(val, float): - val = f'{val:.4f}' - log_items.append(f'{name}: {val}') - log_str += ', '.join(log_items) - - runner.logger.info(log_str) - - def _dump_log(self, log_dict, runner): - # dump log in json format - json_log = OrderedDict() - for k, v in log_dict.items(): - json_log[k] = self._round_float(v) - # only append log at last line - if runner.rank == 0: - with open(self.json_log_path, 'a+') as f: - mmcv.dump(json_log, f, file_format='json') - f.write('\n') - - def _round_float(self, items): - if isinstance(items, list): - return [self._round_float(item) for item in items] - elif isinstance(items, float): - return round(items, 5) - else: - return items - - def log(self, runner): - if 'eval_iter_num' in runner.log_buffer.output: - # this doesn't modify runner.iter and is regardless of by_epoch - cur_iter = runner.log_buffer.output.pop('eval_iter_num') - else: - cur_iter = self.get_iter(runner, inner_iter=True) - - log_dict = OrderedDict( - mode=self.get_mode(runner), - epoch=self.get_epoch(runner), - iter=cur_iter) - - # only record lr of the first param group - cur_lr = runner.current_lr() - if isinstance(cur_lr, list): - log_dict['lr'] = cur_lr[0] - else: - assert isinstance(cur_lr, dict) - log_dict['lr'] = {} - for k, lr_ in cur_lr.items(): - assert isinstance(lr_, list) - log_dict['lr'].update({k: lr_[0]}) - - if 'time' in runner.log_buffer.output: - # statistic memory - if torch.cuda.is_available(): - log_dict['memory'] = self._get_max_memory(runner) - - log_dict = dict(log_dict, **runner.log_buffer.output) - - self._log_info(log_dict, runner) - self._dump_log(log_dict, runner) - return log_dict - - def after_run(self, runner): - # copy or upload logs to self.out_dir - if self.out_dir is not None: - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - out_filepath = self.file_client.join_path( - self.out_dir, filename) - with open(local_filepath, 'r') as f: - self.file_client.put_text(f.read(), out_filepath) - - runner.logger.info( - (f'The file {local_filepath} has been uploaded to ' - f'{out_filepath}.')) - - if not self.keep_local: - os.remove(local_filepath) - runner.logger.info( - (f'{local_filepath} was removed due to the ' - '`self.keep_local=False`')) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/wandb.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/wandb.py deleted file mode 100644 index 78b890ee1..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/logger/wandb.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import scandir -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class WandbLoggerHook(LoggerHook): - """Class to log metrics with wandb. - - It requires `wandb`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the initialization keys. Check - https://docs.wandb.ai/ref/python/init for more init arguments. - interval (int): Logging interval (every k iterations). - Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - commit (bool): Save the metrics dict to the wandb server and increment - the step. If false ``wandb.log`` just updates the current metrics - dict with the row argument and metrics won't be saved until - ``wandb.log`` is called with ``commit=True``. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - log_artifact (bool): If True, artifacts in {work_dir} will be uploaded - to wandb after training ends. - Default: True - `New in version 1.4.3.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be uploaded to wandb. - Default: ('.log.json', '.log', '.py'). - `New in version 1.4.3.` - - .. _wandb: - https://docs.wandb.ai - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - commit=True, - by_epoch=True, - with_step=True, - log_artifact=True, - out_suffix=('.log.json', '.log', '.py')): - super(WandbLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_wandb() - self.init_kwargs = init_kwargs - self.commit = commit - self.with_step = with_step - self.log_artifact = log_artifact - self.out_suffix = out_suffix - - def import_wandb(self): - try: - import wandb - except ImportError: - raise ImportError( - 'Please run "pip install wandb" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(WandbLoggerHook, self).before_run(runner) - if self.wandb is None: - self.import_wandb() - if self.init_kwargs: - self.wandb.init(**self.init_kwargs) - else: - self.wandb.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - if self.with_step: - self.wandb.log( - tags, step=self.get_iter(runner), commit=self.commit) - else: - tags['global_step'] = self.get_iter(runner) - self.wandb.log(tags, commit=self.commit) - - @master_only - def after_run(self, runner): - if self.log_artifact: - wandb_artifact = self.wandb.Artifact( - name='artifacts', type='model') - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - wandb_artifact.add_file(local_filepath) - self.wandb.log_artifact(wandb_artifact) - self.wandb.join() diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/lr_updater.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/lr_updater.py deleted file mode 100644 index ee2a53a65..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/lr_updater.py +++ /dev/null @@ -1,730 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from math import cos, pi - -import mmcv -from .hook import HOOKS, Hook - - -class LrUpdaterHook(Hook): - """LR Scheduler in MMCV. - - Args: - by_epoch (bool): LR changes epoch by epoch - warmup (string): Type of warmup used. It can be None(use no warmup), - 'constant', 'linear' or 'exp' - warmup_iters (int): The number of iterations or epochs that warmup - lasts - warmup_ratio (float): LR used at the beginning of warmup equals to - warmup_ratio * initial_lr - warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters - means the number of epochs that warmup lasts, otherwise means the - number of iteration that warmup lasts - """ - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.1, - warmup_by_epoch=False): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_ratio" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - self.warmup_by_epoch = warmup_by_epoch - - if self.warmup_by_epoch: - self.warmup_epochs = self.warmup_iters - self.warmup_iters = None - else: - self.warmup_epochs = None - - self.base_lr = [] # initial lr for all param groups - self.regular_lr = [] # expected lr if no warming up is performed - - def _set_lr(self, runner, lr_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, lr in zip(optim.param_groups, lr_groups[k]): - param_group['lr'] = lr - else: - for param_group, lr in zip(runner.optimizer.param_groups, - lr_groups): - param_group['lr'] = lr - - def get_lr(self, runner, base_lr): - raise NotImplementedError - - def get_regular_lr(self, runner): - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(runner, _base_lr) - for _base_lr in self.base_lr[k] - ] - lr_groups.update({k: _lr_group}) - - return lr_groups - else: - return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - if self.warmup == 'constant': - warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_lr = [_lr * (1 - k) for _lr in regular_lr] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.regular_lr, dict): - lr_groups = {} - for key, regular_lr in self.regular_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.regular_lr) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - group.setdefault('initial_lr', group['lr']) - _base_lr = [ - group['initial_lr'] for group in optim.param_groups - ] - self.base_lr.update({k: _base_lr}) - else: - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['initial_lr'] for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if self.warmup_iters is None: - epoch_len = len(runner.data_loader) - self.warmup_iters = self.warmup_epochs * epoch_len - - if not self.by_epoch: - return - - self.regular_lr = self.get_regular_lr(runner) - self._set_lr(runner, self.regular_lr) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_lr = self.get_regular_lr(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - - -@HOOKS.register_module() -class FixedLrUpdaterHook(LrUpdaterHook): - - def __init__(self, **kwargs): - super(FixedLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - return base_lr - - -@HOOKS.register_module() -class StepLrUpdaterHook(LrUpdaterHook): - """Step LR scheduler with min_lr clipping. - - Args: - step (int | list[int]): Step to decay the LR. If an int value is given, - regard it as the decay interval. If a list is given, decay LR at - these steps. - gamma (float, optional): Decay LR ratio. Default: 0.1. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. If None - is given, we don't perform lr clipping. Default: None. - """ - - def __init__(self, step, gamma=0.1, min_lr=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_lr = min_lr - super(StepLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - lr = base_lr * (self.gamma**exp) - if self.min_lr is not None: - # clip to a minimum value - lr = max(lr, self.min_lr) - return lr - - -@HOOKS.register_module() -class ExpLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, **kwargs): - self.gamma = gamma - super(ExpLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * self.gamma**progress - - -@HOOKS.register_module() -class PolyLrUpdaterHook(LrUpdaterHook): - - def __init__(self, power=1., min_lr=0., **kwargs): - self.power = power - self.min_lr = min_lr - super(PolyLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - coeff = (1 - progress / max_progress)**self.power - return (base_lr - self.min_lr) * coeff + self.min_lr - - -@HOOKS.register_module() -class InvLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, power=1., **kwargs): - self.gamma = gamma - self.power = power - super(InvLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * (1 + self.gamma * progress)**(-self.power) - - -@HOOKS.register_module() -class CosineAnnealingLrUpdaterHook(LrUpdaterHook): - """CosineAnnealing LR scheduler. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(CosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): - """Flat + Cosine lr schedule. - - Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 - - Args: - start_percent (float): When to start annealing the learning rate - after the percentage of the total training steps. - The value should be in range [0, 1). - Default: 0.75 - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - start_percent=0.75, - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - if start_percent < 0 or start_percent > 1 or not isinstance( - start_percent, float): - raise ValueError( - 'expected float between 0 and 1 start_percent, but ' - f'got {start_percent}') - self.start_percent = start_percent - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(FlatCosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - start = round(runner.max_epochs * self.start_percent) - progress = runner.epoch - start - max_progress = runner.max_epochs - start - else: - start = round(runner.max_iters * self.start_percent) - progress = runner.iter - start - max_progress = runner.max_iters - start - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress < 0: - return base_lr - else: - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class CosineRestartLrUpdaterHook(LrUpdaterHook): - """Cosine annealing with restarts learning rate scheme. - - Args: - periods (list[int]): Periods for each cosine anneling cycle. - restart_weights (list[float], optional): Restart weights at each - restart iteration. Default: [1]. - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - periods, - restart_weights=[1], - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.periods = periods - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - self.restart_weights = restart_weights - assert (len(self.periods) == len(self.restart_weights) - ), 'periods and restart_weights should have the same length.' - super(CosineRestartLrUpdaterHook, self).__init__(**kwargs) - - self.cumulative_periods = [ - sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) - ] - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - else: - progress = runner.iter - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - idx = get_position_from_periods(progress, self.cumulative_periods) - current_weight = self.restart_weights[idx] - nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] - current_periods = self.periods[idx] - - alpha = min((progress - nearest_restart) / current_periods, 1) - return annealing_cos(base_lr, target_lr, alpha, current_weight) - - -def get_position_from_periods(iteration, cumulative_periods): - """Get the position from a period list. - - It will return the index of the right-closest number in the period list. - For example, the cumulative_periods = [100, 200, 300, 400], - if iteration == 50, return 0; - if iteration == 210, return 2; - if iteration == 300, return 3. - - Args: - iteration (int): Current iteration. - cumulative_periods (list[int]): Cumulative period list. - - Returns: - int: The position of the right-closest number in the period list. - """ - for i, period in enumerate(cumulative_periods): - if iteration < period: - return i - raise ValueError(f'Current iteration {iteration} exceeds ' - f'cumulative_periods {cumulative_periods}') - - -@HOOKS.register_module() -class CyclicLrUpdaterHook(LrUpdaterHook): - """Cyclic LR Scheduler. - - Implement the cyclical learning rate policy (CLR) described in - https://arxiv.org/pdf/1506.01186.pdf - - Different from the original paper, we use cosine annealing rather than - triangular policy inside a cycle. This improves the performance in the - 3D detection area. - - Args: - by_epoch (bool, optional): Whether to update LR by epoch. - target_ratio (tuple[float], optional): Relative ratio of the highest LR - and the lowest LR to the initial LR. - cyclic_times (int, optional): Number of cycles during training - step_ratio_up (float, optional): The ratio of the increasing process of - LR in the total cycle. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch=False, - target_ratio=(10, 1e-4), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - gamma=1, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - assert 0 < gamma <= 1, \ - '"gamma" must be in range (0, 1]' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.max_iter_per_phase = None - self.lr_phases = [] # init lr_phases - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicLrUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicLrUpdaterHook, self).before_run(runner) - # initiate lr_phases - # total lr_phases are separated as up and down - self.max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * self.max_iter_per_phase) - self.lr_phases.append([0, iter_up_phase, 1, self.target_ratio[0]]) - self.lr_phases.append([ - iter_up_phase, self.max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - # Update weight decay - scale = self.gamma**curr_cycle - - for (start_iter, end_iter, start_ratio, end_ratio) in self.lr_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_lr and base lr. The target end_ratio can be - # expressed as: - # end_ratio = (base_lr + scale * (max_lr - base_lr)) / base_lr - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_lr * start_ratio, - base_lr * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleLrUpdaterHook(LrUpdaterHook): - """One Cycle LR Scheduler. - - The 1cycle learning rate policy changes the learning rate after every - batch. The one cycle learning rate policy is described in - https://arxiv.org/pdf/1708.07120.pdf - - Args: - max_lr (float or list): Upper learning rate boundaries in the cycle - for each parameter group. - total_steps (int, optional): The total number of steps in the cycle. - Note that if a value is not provided here, it will be the max_iter - of runner. Default: None. - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - div_factor (float): Determines the initial learning rate via - initial_lr = max_lr/div_factor - Default: 25 - final_div_factor (float): Determines the minimum learning rate via - min_lr = initial_lr/final_div_factor - Default: 1e4 - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - max_lr, - total_steps=None, - pct_start=0.3, - anneal_strategy='cos', - div_factor=25, - final_div_factor=1e4, - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch = False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(max_lr, (numbers.Number, list, dict)): - raise ValueError('the type of max_lr must be the one of list or ' - f'dict, but got {type(max_lr)}') - self._max_lr = max_lr - if total_steps is not None: - if not isinstance(total_steps, int): - raise ValueError('the type of total_steps must be int, but' - f'got {type(total_steps)}') - self.total_steps = total_steps - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.div_factor = div_factor - self.final_div_factor = final_div_factor - self.three_phase = three_phase - self.lr_phases = [] # init lr_phases - super(OneCycleLrUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if hasattr(self, 'total_steps'): - total_steps = self.total_steps - else: - total_steps = runner.max_iters - if total_steps < runner.max_iters: - raise ValueError( - 'The total steps must be greater than or equal to max ' - f'iterations {runner.max_iters} of runner, but total steps ' - f'is {total_steps}.') - - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - _max_lr = format_param(k, optim, self._max_lr) - self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(optim.param_groups, self.base_lr[k]): - group.setdefault('initial_lr', lr) - else: - k = type(runner.optimizer).__name__ - _max_lr = format_param(k, runner.optimizer, self._max_lr) - self.base_lr = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(runner.optimizer.param_groups, self.base_lr): - group.setdefault('initial_lr', lr) - - if self.three_phase: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append([ - float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 - ]) - self.lr_phases.append( - [total_steps - 1, 1, 1 / self.final_div_factor]) - else: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append( - [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - start_iter = 0 - for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): - if curr_iter <= end_iter: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, - pct) - break - start_iter = end_iter - return lr - - -@HOOKS.register_module() -class LinearAnnealingLrUpdaterHook(LrUpdaterHook): - """Linear annealing LR Scheduler decays the learning rate of each parameter - group linearly. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(LinearAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_linear(base_lr, target_lr, progress / max_progress) - - -def annealing_cos(start, end, factor, weight=1): - """Calculate annealing cos learning rate. - - Cosine anneal from `weight * start + (1 - weight) * end` to `end` as - percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the cosine annealing. - end (float): The ending learing rate of the cosine annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - weight (float, optional): The combination factor of `start` and `end` - when calculating the actual starting learning rate. Default to 1. - """ - cos_out = cos(pi * factor) + 1 - return end + 0.5 * weight * (start - end) * cos_out - - -def annealing_linear(start, end, factor): - """Calculate annealing linear learning rate. - - Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the linear annealing. - end (float): The ending learing rate of the linear annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - """ - return start + (end - start) * factor - - -def format_param(name, optim, param): - if isinstance(param, numbers.Number): - return [param] * len(optim.param_groups) - elif isinstance(param, (list, tuple)): # multi param groups - if len(param) != len(optim.param_groups): - raise ValueError(f'expected {len(optim.param_groups)} ' - f'values for {name}, got {len(param)}') - return param - else: # multi optimizers - if name not in param: - raise KeyError(f'{name} is not found in {param.keys()}') - return param[name] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/memory.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/memory.py deleted file mode 100644 index 70cf9a838..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class EmptyCacheHook(Hook): - - def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): - self._before_epoch = before_epoch - self._after_epoch = after_epoch - self._after_iter = after_iter - - def after_iter(self, runner): - if self._after_iter: - torch.cuda.empty_cache() - - def before_epoch(self, runner): - if self._before_epoch: - torch.cuda.empty_cache() - - def after_epoch(self, runner): - if self._after_epoch: - torch.cuda.empty_cache() diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/momentum_updater.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/momentum_updater.py deleted file mode 100644 index aa15fe23c..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/momentum_updater.py +++ /dev/null @@ -1,566 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -from .hook import HOOKS, Hook -from .lr_updater import annealing_cos, annealing_linear, format_param - - -class MomentumUpdaterHook(Hook): - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.9): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_momentum" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - - self.base_momentum = [] # initial momentum for all param groups - self.regular_momentum = [ - ] # expected momentum if no warming up is performed - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, base_momentum): - raise NotImplementedError - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k in runner.optimizer.keys(): - _momentum_group = [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum[k] - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - return [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum - ] - - def get_warmup_momentum(self, cur_iters): - - def _get_warmup_momentum(cur_iters, regular_momentum): - if self.warmup == 'constant': - warmup_momentum = [ - _momentum / self.warmup_ratio - for _momentum in regular_momentum - ] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_momentum = [ - _momentum / (1 - k) for _momentum in regular_momentum - ] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_momentum = [ - _momentum / k for _momentum in regular_momentum - ] - return warmup_momentum - - if isinstance(self.regular_momentum, dict): - momentum_groups = {} - for key, regular_momentum in self.regular_momentum.items(): - momentum_groups[key] = _get_warmup_momentum( - cur_iters, regular_momentum) - return momentum_groups - else: - return _get_warmup_momentum(cur_iters, self.regular_momentum) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, - # if 'initial_momentum' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_momentum = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - _base_momentum = [ - group['initial_momentum'] for group in optim.param_groups - ] - self.base_momentum.update({k: _base_momentum}) - else: - for group in runner.optimizer.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - self.base_momentum = [ - group['initial_momentum'] - for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if not self.by_epoch: - return - self.regular_momentum = self.get_regular_momentum(runner) - self._set_momentum(runner, self.regular_momentum) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_momentum = self.get_regular_momentum(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - - -@HOOKS.register_module() -class StepMomentumUpdaterHook(MomentumUpdaterHook): - """Step momentum scheduler with min value clipping. - - Args: - step (int | list[int]): Step to decay the momentum. If an int value is - given, regard it as the decay interval. If a list is given, decay - momentum at these steps. - gamma (float, optional): Decay momentum ratio. Default: 0.5. - min_momentum (float, optional): Minimum momentum value to keep. If - momentum after decay is lower than this value, it will be clipped - accordingly. If None is given, we don't perform lr clipping. - Default: None. - """ - - def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_momentum = min_momentum - super(StepMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - momentum = base_momentum * (self.gamma**exp) - if self.min_momentum is not None: - # clip to a minimum value - momentum = max(momentum, self.min_momentum) - return momentum - - -@HOOKS.register_module() -class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Cosine annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(CosineAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_cos(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class LinearAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Linear annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(LinearAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_linear(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class CyclicMomentumUpdaterHook(MomentumUpdaterHook): - """Cyclic momentum Scheduler. - - Implement the cyclical momentum scheduler policy described in - https://arxiv.org/pdf/1708.07120.pdf - - This momentum scheduler usually used together with the CyclicLRUpdater - to improve the performance in the 3D detection area. - - Args: - target_ratio (tuple[float]): Relative ratio of the lowest momentum and - the highest momentum to the initial momentum. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of momentum - in the total cycle. - by_epoch (bool): Whether to update momentum by epoch. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch=False, - target_ratio=(0.85 / 0.95, 1), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - gamma=1, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.momentum_phases = [] # init momentum_phases - - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - # currently only support by_epoch=False - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicMomentumUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicMomentumUpdaterHook, self).before_run(runner) - # initiate momentum_phases - # total momentum_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.max_iter_per_phase = max_iter_per_phase - self.momentum_phases.append( - [0, iter_up_phase, 1, self.target_ratio[0]]) - self.momentum_phases.append([ - iter_up_phase, max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_momentum(self, runner, base_momentum): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - scale = self.gamma**curr_cycle - for (start_iter, end_iter, start_ratio, end_ratio) \ - in self.momentum_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_momentum and base momentum. The target end_ratio - # can be expressed as: - # end_ratio = (base_momentum + scale * \ - # (max_momentum - base_momentum)) / base_momentum - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_momentum * start_ratio, - base_momentum * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): - """OneCycle momentum Scheduler. - - This momentum scheduler usually used together with the OneCycleLrUpdater - to improve the performance. - - Args: - base_momentum (float or list): Lower momentum boundaries in the cycle - for each parameter group. Note that momentum is cycled inversely - to learning rate; at the peak of a cycle, momentum is - 'base_momentum' and learning rate is 'max_lr'. - Default: 0.85 - max_momentum (float or list): Upper momentum boundaries in the cycle - for each parameter group. Functionally, - it defines the cycle amplitude (max_momentum - base_momentum). - Note that momentum is cycled inversely - to learning rate; at the start of a cycle, momentum is - 'max_momentum' and learning rate is 'base_lr' - Default: 0.95 - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - base_momentum=0.85, - max_momentum=0.95, - pct_start=0.3, - anneal_strategy='cos', - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch=False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(base_momentum, (float, list, dict)): - raise ValueError('base_momentum must be the type among of float,' - 'list or dict.') - self._base_momentum = base_momentum - if not isinstance(max_momentum, (float, list, dict)): - raise ValueError('max_momentum must be the type among of float,' - 'list or dict.') - self._max_momentum = max_momentum - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('Expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must by one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.three_phase = three_phase - self.momentum_phases = [] # init momentum_phases - super(OneCycleMomentumUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip( - optim.param_groups, _base_momentum, _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - else: - optim = runner.optimizer - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - k = type(optim).__name__ - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip(optim.param_groups, - _base_momentum, - _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - - if self.three_phase: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': - float(2 * self.pct_start * runner.max_iters) - 2, - 'start_momentum': - 'base_momentum', - 'end_momentum': - 'max_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'max_momentum', - 'end_momentum': 'max_momentum' - }) - else: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'base_momentum', - 'end_momentum': 'max_momentum' - }) - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, param_group): - curr_iter = runner.iter - start_iter = 0 - for i, phase in enumerate(self.momentum_phases): - end_iter = phase['end_iter'] - if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - momentum = self.anneal_func( - param_group[phase['start_momentum']], - param_group[phase['end_momentum']], pct) - break - start_iter = end_iter - return momentum - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k, optim in runner.optimizer.items(): - _momentum_group = [ - self.get_momentum(runner, param_group) - for param_group in optim.param_groups - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - momentum_groups = [] - for param_group in runner.optimizer.param_groups: - momentum_groups.append(self.get_momentum(runner, param_group)) - return momentum_groups diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/optimizer.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/optimizer.py deleted file mode 100644 index 12c885183..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/optimizer.py +++ /dev/null @@ -1,556 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -from collections import defaultdict -from itertools import chain - -from torch.nn.utils import clip_grad - -from mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version -from ..dist_utils import allreduce_grads -from ..fp16_utils import LossScaler, wrap_fp16_model -from .hook import HOOKS, Hook - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - from torch.cuda.amp import GradScaler -except ImportError: - pass - - -@HOOKS.register_module() -class OptimizerHook(Hook): - """A hook contains custom operations for the optimizer. - - Args: - grad_clip (dict, optional): A config dict to control the clip_grad. - Default: None. - detect_anomalous_params (bool): This option is only used for - debugging which will slow down the training speed. - Detect anomalous parameters that are not included in - the computational graph with `loss` as the root. - There are two cases - - - Parameters were not used during - forward pass. - - Parameters were not used to produce - loss. - Default: False. - """ - - def __init__(self, grad_clip=None, detect_anomalous_params=False): - self.grad_clip = grad_clip - self.detect_anomalous_params = detect_anomalous_params - - def clip_grads(self, params): - params = list( - filter(lambda p: p.requires_grad and p.grad is not None, params)) - if len(params) > 0: - return clip_grad.clip_grad_norm_(params, **self.grad_clip) - - def after_train_iter(self, runner): - runner.optimizer.zero_grad() - if self.detect_anomalous_params: - self.detect_anomalous_parameters(runner.outputs['loss'], runner) - runner.outputs['loss'].backward() - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - - def detect_anomalous_parameters(self, loss, runner): - logger = runner.logger - parameters_in_graph = set() - visited = set() - - def traverse(grad_fn): - if grad_fn is None: - return - if grad_fn not in visited: - visited.add(grad_fn) - if hasattr(grad_fn, 'variable'): - parameters_in_graph.add(grad_fn.variable) - parents = grad_fn.next_functions - if parents is not None: - for parent in parents: - grad_fn = parent[0] - traverse(grad_fn) - - traverse(loss.grad_fn) - for n, p in runner.model.named_parameters(): - if p not in parameters_in_graph and p.requires_grad: - logger.log( - level=logging.ERROR, - msg=f'{n} with shape {p.size()} is not ' - f'in the computational graph \n') - - -@HOOKS.register_module() -class GradientCumulativeOptimizerHook(OptimizerHook): - """Optimizer Hook implements multi-iters gradient cumulating. - - Args: - cumulative_iters (int, optional): Num of gradient cumulative iters. - The optimizer will step every `cumulative_iters` iters. - Defaults to 1. - - Examples: - >>> # Use cumulative_iters to simulate a large batch size - >>> # It is helpful when the hardware cannot handle a large batch size. - >>> loader = DataLoader(data, batch_size=64) - >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) - >>> # almost equals to - >>> loader = DataLoader(data, batch_size=256) - >>> optim_hook = OptimizerHook() - """ - - def __init__(self, cumulative_iters=1, **kwargs): - super(GradientCumulativeOptimizerHook, self).__init__(**kwargs) - - assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ - f'cumulative_iters only accepts positive int, but got ' \ - f'{type(cumulative_iters)} instead.' - - self.cumulative_iters = cumulative_iters - self.divisible_iters = 0 - self.remainder_iters = 0 - self.initialized = False - - def has_batch_norm(self, module): - if isinstance(module, _BatchNorm): - return True - for m in module.children(): - if self.has_batch_norm(m): - return True - return False - - def _init(self, runner): - if runner.iter % self.cumulative_iters != 0: - runner.logger.warning( - 'Resume iter number is not divisible by cumulative_iters in ' - 'GradientCumulativeOptimizerHook, which means the gradient of ' - 'some iters is lost and the result may be influenced slightly.' - ) - - if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: - runner.logger.warning( - 'GradientCumulativeOptimizerHook may slightly decrease ' - 'performance if the model has BatchNorm layers.') - - residual_iters = runner.max_iters - runner.iter - - self.divisible_iters = ( - residual_iters // self.cumulative_iters * self.cumulative_iters) - self.remainder_iters = residual_iters - self.divisible_iters - - self.initialized = True - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - runner.optimizer.zero_grad() - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - self.loss_scaler = GradScaler() - elif isinstance(loss_scale, float): - self._scale_update_param = loss_scale - self.loss_scaler = GradScaler(init_scale=loss_scale) - elif isinstance(loss_scale, dict): - self.loss_scaler = GradScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training.""" - # wrap model mode to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer to - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients. - 3. Unscale the optimizer’s gradient tensors. - 4. Call optimizer.step() and update scale factor. - 5. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - - self.loss_scaler.scale(runner.outputs['loss']).backward() - self.loss_scaler.unscale_(runner.optimizer) - # grad clip - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using PyTorch's implementation) implements - multi-iters gradient cumulating. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - """ - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - - self.loss_scaler.scale(loss).backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - self.loss_scaler.unscale_(runner.optimizer) - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() - -else: - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (mmcv's implementation). - - The steps of fp16 optimizer is as follows. - 1. Scale the loss value. - 2. BP in the fp16 model. - 2. Copy gradients from fp16 model to fp32 weights. - 3. Update fp32 weights. - 4. Copy updated parameters from fp32 weights to fp16 model. - - Refer to https://arxiv.org/abs/1710.03740 for more details. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of LossScaler. - Defaults to 512. - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - if loss_scale == 'dynamic': - self.loss_scaler = LossScaler(mode='dynamic') - elif isinstance(loss_scale, float): - self.loss_scaler = LossScaler( - init_scale=loss_scale, mode='static') - elif isinstance(loss_scale, dict): - self.loss_scaler = LossScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training. - - 1. Make a master copy of fp32 weights for optimization. - 2. Convert the main model from fp32 to fp16. - """ - # keep a copy of fp32 weights - old_groups = runner.optimizer.param_groups - runner.optimizer.param_groups = copy.deepcopy( - runner.optimizer.param_groups) - state = defaultdict(dict) - p_map = { - old_p: p - for old_p, p in zip( - chain(*(g['params'] for g in old_groups)), - chain(*(g['params'] - for g in runner.optimizer.param_groups))) - } - for k, v in runner.optimizer.state.items(): - state[p_map[k]] = v - runner.optimizer.state = state - # convert model to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer `loss_scalar.py` - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients (fp16). - 3. Copy gradients from the model to the fp32 weight copy. - 4. Scale the gradients back and update the fp32 weight copy. - 5. Copy back the params from fp32 weight copy to the fp16 model. - 6. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - # scale the loss value - scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale - scaled_loss.backward() - # copy fp16 grads in the model to fp32 params in the optimizer - - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - self.loss_scaler.update_scale(has_overflow) - if has_overflow: - runner.logger.warning('Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using mmcv implementation) implements multi- - iters gradient cumulating.""" - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - - loss = runner.outputs['loss'] - loss = loss / loss_factor - - # scale the loss value - scaled_loss = loss * self.loss_scaler.loss_scale - scaled_loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - else: - runner.logger.warning( - 'Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - self.loss_scaler.update_scale(has_overflow) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/profiler.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/profiler.py deleted file mode 100644 index fef9adc13..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/profiler.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Callable, List, Optional, Union - -import torch - -from ..dist_utils import master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ProfilerHook(Hook): - """Profiler to analyze performance during training. - - PyTorch Profiler is a tool that allows the collection of the performance - metrics during the training. More details on Profiler can be found at - https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile - - Args: - by_epoch (bool): Profile performance by epoch or by iteration. - Default: True. - profile_iters (int): Number of iterations for profiling. - If ``by_epoch=True``, profile_iters indicates that they are the - first profile_iters epochs at the beginning of the - training, otherwise it indicates the first profile_iters - iterations. Default: 1. - activities (list[str]): List of activity groups (CPU, CUDA) to use in - profiling. Default: ['cpu', 'cuda']. - schedule (dict, optional): Config of generating the callable schedule. - if schedule is None, profiler will not add step markers into the - trace and table view. Default: None. - on_trace_ready (callable, dict): Either a handler or a dict of generate - handler. Default: None. - record_shapes (bool): Save information about operator's input shapes. - Default: False. - profile_memory (bool): Track tensor memory allocation/deallocation. - Default: False. - with_stack (bool): Record source information (file and line number) - for the ops. Default: False. - with_flops (bool): Use formula to estimate the FLOPS of specific - operators (matrix multiplication and 2D convolution). - Default: False. - json_trace_path (str, optional): Exports the collected trace in Chrome - JSON format. Default: None. - - Example: - >>> runner = ... # instantiate a Runner - >>> # tensorboard trace - >>> trace_config = dict(type='tb_trace', dir_name='work_dir') - >>> profiler_config = dict(on_trace_ready=trace_config) - >>> runner.register_profiler_hook(profiler_config) - >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) - """ - - def __init__(self, - by_epoch: bool = True, - profile_iters: int = 1, - activities: List[str] = ['cpu', 'cuda'], - schedule: Optional[dict] = None, - on_trace_ready: Optional[Union[Callable, dict]] = None, - record_shapes: bool = False, - profile_memory: bool = False, - with_stack: bool = False, - with_flops: bool = False, - json_trace_path: Optional[str] = None) -> None: - try: - from torch import profiler # torch version >= 1.8.1 - except ImportError: - raise ImportError('profiler is the new feature of torch1.8.1, ' - f'but your version is {torch.__version__}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' - self.by_epoch = by_epoch - - if profile_iters < 1: - raise ValueError('profile_iters should be greater than 0, but got ' - f'{profile_iters}') - self.profile_iters = profile_iters - - if not isinstance(activities, list): - raise ValueError( - f'activities should be list, but got {type(activities)}') - self.activities = [] - for activity in activities: - activity = activity.lower() - if activity == 'cpu': - self.activities.append(profiler.ProfilerActivity.CPU) - elif activity == 'cuda': - self.activities.append(profiler.ProfilerActivity.CUDA) - else: - raise ValueError( - f'activity should be "cpu" or "cuda", but got {activity}') - - if schedule is not None: - self.schedule = profiler.schedule(**schedule) - else: - self.schedule = None - - self.on_trace_ready = on_trace_ready - self.record_shapes = record_shapes - self.profile_memory = profile_memory - self.with_stack = with_stack - self.with_flops = with_flops - self.json_trace_path = json_trace_path - - @master_only - def before_run(self, runner): - if self.by_epoch and runner.max_epochs < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_epochs}') - - if not self.by_epoch and runner.max_iters < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_iters}') - - if callable(self.on_trace_ready): # handler - _on_trace_ready = self.on_trace_ready - elif isinstance(self.on_trace_ready, dict): # config of handler - trace_cfg = self.on_trace_ready.copy() - trace_type = trace_cfg.pop('type') # log_trace handler - if trace_type == 'log_trace': - - def _log_handler(prof): - print(prof.key_averages().table(**trace_cfg)) - - _on_trace_ready = _log_handler - elif trace_type == 'tb_trace': # tensorboard_trace handler - try: - import torch_tb_profiler # noqa: F401 - except ImportError: - raise ImportError('please run "pip install ' - 'torch-tb-profiler" to install ' - 'torch_tb_profiler') - _on_trace_ready = torch.profiler.tensorboard_trace_handler( - **trace_cfg) - else: - raise ValueError('trace_type should be "log_trace" or ' - f'"tb_trace", but got {trace_type}') - elif self.on_trace_ready is None: - _on_trace_ready = None # type: ignore - else: - raise ValueError('on_trace_ready should be handler, dict or None, ' - f'but got {type(self.on_trace_ready)}') - - if self.by_epoch and runner.max_epochs > 1: - warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' - 'instead of 1 epoch. Since profiler will slow down ' - 'the training, it is recommended to train 1 epoch ' - 'with ProfilerHook and adjust your setting according' - ' to the profiler summary. During normal training ' - '(epoch > 1), you may disable the ProfilerHook.') - - self.profiler = torch.profiler.profile( - activities=self.activities, - schedule=self.schedule, - on_trace_ready=_on_trace_ready, - record_shapes=self.record_shapes, - profile_memory=self.profile_memory, - with_stack=self.with_stack, - with_flops=self.with_flops) - - self.profiler.__enter__() - runner.logger.info('profiler is profiling...') - - @master_only - def after_train_epoch(self, runner): - if self.by_epoch and runner.epoch == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) - - @master_only - def after_train_iter(self, runner): - self.profiler.step() - if not self.by_epoch and runner.iter == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/sampler_seed.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/sampler_seed.py deleted file mode 100644 index ee0dc6bdd..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/sampler_seed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class DistSamplerSeedHook(Hook): - """Data-loading sampler for distributed training. - - When distributed training, it is only useful in conjunction with - :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same - purpose with :obj:`IterLoader`. - """ - - def before_epoch(self, runner): - if hasattr(runner.data_loader.sampler, 'set_epoch'): - # in case the data loader uses `SequentialSampler` in Pytorch - runner.data_loader.sampler.set_epoch(runner.epoch) - elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): - # batch sampler in pytorch warps the sampler as its attributes. - runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/sync_buffer.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/sync_buffer.py deleted file mode 100644 index 6376b7ff8..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/hooks/sync_buffer.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..dist_utils import allreduce_params -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class SyncBuffersHook(Hook): - """Synchronize model buffers such as running_mean and running_var in BN at - the end of each epoch. - - Args: - distributed (bool): Whether distributed training is used. It is - effective only for distributed training. Defaults to True. - """ - - def __init__(self, distributed=True): - self.distributed = distributed - - def after_epoch(self, runner): - """All-reduce model buffers at the end of each epoch.""" - if self.distributed: - allreduce_params(runner.model.buffers()) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/iter_based_runner.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/iter_based_runner.py deleted file mode 100644 index 9892b07a4..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/iter_based_runner.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch -from torch.optim import Optimizer - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .hooks import IterTimerHook -from .utils import get_host_info - - -class IterLoader: - - def __init__(self, dataloader): - self._dataloader = dataloader - self.iter_loader = iter(self._dataloader) - self._epoch = 0 - - @property - def epoch(self): - return self._epoch - - def __next__(self): - try: - data = next(self.iter_loader) - except StopIteration: - self._epoch += 1 - if hasattr(self._dataloader.sampler, 'set_epoch'): - self._dataloader.sampler.set_epoch(self._epoch) - time.sleep(2) # Prevent possible deadlock during epoch transition - self.iter_loader = iter(self._dataloader) - data = next(self.iter_loader) - - return data - - def __len__(self): - return len(self._dataloader) - - -@RUNNERS.register_module() -class IterBasedRunner(BaseRunner): - """Iteration-based Runner. - - This runner train models iteration by iteration. - """ - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._epoch = data_loader.epoch - data_batch = next(data_loader) - self.call_hook('before_train_iter') - outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.train_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_train_iter') - self._inner_iter += 1 - self._iter += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - data_batch = next(data_loader) - self.call_hook('before_val_iter') - outputs = self.model.val_step(data_batch, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.val_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_val_iter') - self._inner_iter += 1 - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, iters) to specify the - running order and iterations. E.g, [('train', 10000), - ('val', 1000)] means running 10000 iterations for training and - 1000 iterations for validation, iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - """Resume model from checkpoint. - - Args: - checkpoint (str): Checkpoint to resume from. - resume_optimizer (bool, optional): Whether resume the optimizer(s) - if the checkpoint file includes optimizer(s). Default to True. - map_location (str, optional): Same as :func:`torch.load`. - Default to 'default'. - """ - if map_location == 'default': - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - self._inner_iter = checkpoint['meta']['iter'] - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') - - def save_checkpoint(self, - out_dir, - filename_tmpl='iter_{}.pth', - meta=None, - save_optimizer=True, - create_symlink=True): - """Save checkpoint to file. - - Args: - out_dir (str): Directory to save checkpoint files. - filename_tmpl (str, optional): Checkpoint file template. - Defaults to 'iter_{}.pth'. - meta (dict, optional): Metadata to be saved in checkpoint. - Defaults to None. - save_optimizer (bool, optional): Whether save optimizer. - Defaults to True. - create_symlink (bool, optional): Whether create symlink to the - latest checkpoint file. Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.iter + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - custom_hooks_config=None): - """Register default hooks for iter-based training. - - Checkpoint hook, optimizer stepper hook and logger hooks will be set to - `by_epoch=False` by default. - - Default hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - if checkpoint_config is not None: - checkpoint_config.setdefault('by_epoch', False) - if lr_config is not None: - lr_config.setdefault('by_epoch', False) - if log_config is not None: - for info in log_config['hooks']: - info.setdefault('by_epoch', False) - super(IterBasedRunner, self).register_training_hooks( - lr_config=lr_config, - momentum_config=momentum_config, - optimizer_config=optimizer_config, - checkpoint_config=checkpoint_config, - log_config=log_config, - timer_config=IterTimerHook(), - custom_hooks_config=custom_hooks_config) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/log_buffer.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/log_buffer.py deleted file mode 100644 index d949e2941..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/log_buffer.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import numpy as np - - -class LogBuffer: - - def __init__(self): - self.val_history = OrderedDict() - self.n_history = OrderedDict() - self.output = OrderedDict() - self.ready = False - - def clear(self): - self.val_history.clear() - self.n_history.clear() - self.clear_output() - - def clear_output(self): - self.output.clear() - self.ready = False - - def update(self, vars, count=1): - assert isinstance(vars, dict) - for key, var in vars.items(): - if key not in self.val_history: - self.val_history[key] = [] - self.n_history[key] = [] - self.val_history[key].append(var) - self.n_history[key].append(count) - - def average(self, n=0): - """Average latest n values or all values.""" - assert n >= 0 - for key in self.val_history: - values = np.array(self.val_history[key][-n:]) - nums = np.array(self.n_history[key][-n:]) - avg = np.sum(values * nums) / np.sum(nums) - self.output[key] = avg - self.ready = True diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/optimizer/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/optimizer/__init__.py deleted file mode 100644 index 53c34d047..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/optimizer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, - build_optimizer_constructor) -from .default_constructor import DefaultOptimizerConstructor - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', - 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/optimizer/builder.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/optimizer/builder.py deleted file mode 100644 index f9234eed8..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/optimizer/builder.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect - -import torch - -from ...utils import Registry, build_from_cfg - -OPTIMIZERS = Registry('optimizer') -OPTIMIZER_BUILDERS = Registry('optimizer builder') - - -def register_torch_optimizers(): - torch_optimizers = [] - for module_name in dir(torch.optim): - if module_name.startswith('__'): - continue - _optim = getattr(torch.optim, module_name) - if inspect.isclass(_optim) and issubclass(_optim, - torch.optim.Optimizer): - OPTIMIZERS.register_module()(_optim) - torch_optimizers.append(module_name) - return torch_optimizers - - -TORCH_OPTIMIZERS = register_torch_optimizers() - - -def build_optimizer_constructor(cfg): - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/optimizer/default_constructor.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/optimizer/default_constructor.py deleted file mode 100644 index ae97db880..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/optimizer/default_constructor.py +++ /dev/null @@ -1,250 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -from torch.nn import GroupNorm, LayerNorm - -from mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of -from mmcv.utils.ext_loader import check_ops_exist -from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS - - -@OPTIMIZER_BUILDERS.register_module() -class DefaultOptimizerConstructor: - """Default constructor for optimizers. - - By default each parameter share the same optimizer settings, and we - provide an argument ``paramwise_cfg`` to specify parameter-wise settings. - It is a dict and may contain the following fields: - - - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If - one of the keys in ``custom_keys`` is a substring of the name of one - parameter, then the setting of the parameter will be specified by - ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will - be ignored. It should be noted that the aforementioned ``key`` is the - longest key that is a substring of the name of the parameter. If there - are multiple matched keys with the same length, then the key with lower - alphabet order will be chosen. - ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` - and ``decay_mult``. See Example 2 below. - - ``bias_lr_mult`` (float): It will be multiplied to the learning - rate for all bias parameters (except for those in normalization - layers and offset layers of DCN). - - ``bias_decay_mult`` (float): It will be multiplied to the weight - decay for all bias parameters (except for those in - normalization layers, depthwise conv layers, offset layers of DCN). - - ``norm_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of normalization - layers. - - ``dwconv_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of depthwise conv - layers. - - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning - rate for parameters of offset layer in the deformable convs - of a model. - - ``bypass_duplicate`` (bool): If true, the duplicate parameters - would not be added into optimizer. Default: False. - - Note: - - 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will - override the effect of ``bias_lr_mult`` in the bias of offset layer. - So be careful when using both ``bias_lr_mult`` and - ``dcn_offset_lr_mult``. If you wish to apply both of them to the offset - layer in deformable convs, set ``dcn_offset_lr_mult`` to the original - ``dcn_offset_lr_mult`` * ``bias_lr_mult``. - - 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will - apply it to all the DCN layers in the model. So be careful when the - model contains multiple DCN layers in places other than backbone. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - optimizer_cfg (dict): The config dict of the optimizer. - Positional fields are - - - `type`: class name of the optimizer. - - Optional fields are - - - any arguments of the corresponding optimizer type, e.g., - lr, weight_decay, momentum, etc. - paramwise_cfg (dict, optional): Parameter-wise options. - - Example 1: - >>> model = torch.nn.modules.Conv1d(1, 1, 1) - >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, - >>> weight_decay=0.0001) - >>> paramwise_cfg = dict(norm_decay_mult=0.) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - - Example 2: - >>> # assume model have attribute model.backbone and model.cls_head - >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) - >>> paramwise_cfg = dict(custom_keys={ - 'backbone': dict(lr_mult=0.1, decay_mult=0.9)}) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - >>> # Then the `lr` and `weight_decay` for model.backbone is - >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for - >>> # model.cls_head is (0.01, 0.95). - """ - - def __init__(self, optimizer_cfg, paramwise_cfg=None): - if not isinstance(optimizer_cfg, dict): - raise TypeError('optimizer_cfg should be a dict', - f'but got {type(optimizer_cfg)}') - self.optimizer_cfg = optimizer_cfg - self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg - self.base_lr = optimizer_cfg.get('lr', None) - self.base_wd = optimizer_cfg.get('weight_decay', None) - self._validate_cfg() - - def _validate_cfg(self): - if not isinstance(self.paramwise_cfg, dict): - raise TypeError('paramwise_cfg should be None or a dict, ' - f'but got {type(self.paramwise_cfg)}') - - if 'custom_keys' in self.paramwise_cfg: - if not isinstance(self.paramwise_cfg['custom_keys'], dict): - raise TypeError( - 'If specified, custom_keys must be a dict, ' - f'but got {type(self.paramwise_cfg["custom_keys"])}') - if self.base_wd is None: - for key in self.paramwise_cfg['custom_keys']: - if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: - raise ValueError('base_wd should not be None') - - # get base lr and weight decay - # weight_decay must be explicitly specified if mult is specified - if ('bias_decay_mult' in self.paramwise_cfg - or 'norm_decay_mult' in self.paramwise_cfg - or 'dwconv_decay_mult' in self.paramwise_cfg): - if self.base_wd is None: - raise ValueError('base_wd should not be None') - - def _is_in(self, param_group, param_group_list): - assert is_list_of(param_group_list, dict) - param = set(param_group['params']) - param_set = set() - for group in param_group_list: - param_set.update(set(group['params'])) - - return not param.isdisjoint(param_set) - - def add_params(self, params, module, prefix='', is_dcn_module=None): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - prefix (str): The prefix of the module - is_dcn_module (int|float|None): If the current module is a - submodule of DCN, `is_dcn_module` will be passed to - control conv_offset layer's learning rate. Defaults to None. - """ - # get param-wise options - custom_keys = self.paramwise_cfg.get('custom_keys', {}) - # first sort with alphabet order and then sort with reversed len of str - sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) - - bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) - bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) - norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) - dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) - bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) - dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) - - # special rules for norm layers and depth-wise conv layers - is_norm = isinstance(module, - (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) - is_dwconv = ( - isinstance(module, torch.nn.Conv2d) - and module.in_channels == module.groups) - - for name, param in module.named_parameters(recurse=False): - param_group = {'params': [param]} - if not param.requires_grad: - params.append(param_group) - continue - if bypass_duplicate and self._is_in(param_group, params): - warnings.warn(f'{prefix} is duplicate. It is skipped since ' - f'bypass_duplicate={bypass_duplicate}') - continue - # if the parameter match one of the custom keys, ignore other rules - is_custom = False - for key in sorted_keys: - if key in f'{prefix}.{name}': - is_custom = True - lr_mult = custom_keys[key].get('lr_mult', 1.) - param_group['lr'] = self.base_lr * lr_mult - if self.base_wd is not None: - decay_mult = custom_keys[key].get('decay_mult', 1.) - param_group['weight_decay'] = self.base_wd * decay_mult - break - - if not is_custom: - # bias_lr_mult affects all bias parameters - # except for norm.bias dcn.conv_offset.bias - if name == 'bias' and not (is_norm or is_dcn_module): - param_group['lr'] = self.base_lr * bias_lr_mult - - if (prefix.find('conv_offset') != -1 and is_dcn_module - and isinstance(module, torch.nn.Conv2d)): - # deal with both dcn_offset's bias & weight - param_group['lr'] = self.base_lr * dcn_offset_lr_mult - - # apply weight decay policies - if self.base_wd is not None: - # norm decay - if is_norm: - param_group[ - 'weight_decay'] = self.base_wd * norm_decay_mult - # depth-wise conv - elif is_dwconv: - param_group[ - 'weight_decay'] = self.base_wd * dwconv_decay_mult - # bias lr and decay - elif name == 'bias' and not is_dcn_module: - # TODO: current bias_decay_mult will have affect on DCN - param_group[ - 'weight_decay'] = self.base_wd * bias_decay_mult - params.append(param_group) - - if check_ops_exist(): - from mmcv.ops import DeformConv2d, ModulatedDeformConv2d - is_dcn_module = isinstance(module, - (DeformConv2d, ModulatedDeformConv2d)) - else: - is_dcn_module = False - for child_name, child_mod in module.named_children(): - child_prefix = f'{prefix}.{child_name}' if prefix else child_name - self.add_params( - params, - child_mod, - prefix=child_prefix, - is_dcn_module=is_dcn_module) - - def __call__(self, model): - if hasattr(model, 'module'): - model = model.module - - optimizer_cfg = self.optimizer_cfg.copy() - # if no paramwise option is specified, just use the global setting - if not self.paramwise_cfg: - optimizer_cfg['params'] = model.parameters() - return build_from_cfg(optimizer_cfg, OPTIMIZERS) - - # set param-wise lr and weight decay recursively - params = [] - self.add_params(params, model) - optimizer_cfg['params'] = params - - return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/priority.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/priority.py deleted file mode 100644 index 64cc4e3a0..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/priority.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from enum import Enum - - -class Priority(Enum): - """Hook priority levels. - - +--------------+------------+ - | Level | Value | - +==============+============+ - | HIGHEST | 0 | - +--------------+------------+ - | VERY_HIGH | 10 | - +--------------+------------+ - | HIGH | 30 | - +--------------+------------+ - | ABOVE_NORMAL | 40 | - +--------------+------------+ - | NORMAL | 50 | - +--------------+------------+ - | BELOW_NORMAL | 60 | - +--------------+------------+ - | LOW | 70 | - +--------------+------------+ - | VERY_LOW | 90 | - +--------------+------------+ - | LOWEST | 100 | - +--------------+------------+ - """ - - HIGHEST = 0 - VERY_HIGH = 10 - HIGH = 30 - ABOVE_NORMAL = 40 - NORMAL = 50 - BELOW_NORMAL = 60 - LOW = 70 - VERY_LOW = 90 - LOWEST = 100 - - -def get_priority(priority): - """Get priority value. - - Args: - priority (int or str or :obj:`Priority`): Priority. - - Returns: - int: The priority value. - """ - if isinstance(priority, int): - if priority < 0 or priority > 100: - raise ValueError('priority must be between 0 and 100') - return priority - elif isinstance(priority, Priority): - return priority.value - elif isinstance(priority, str): - return Priority[priority.upper()].value - else: - raise TypeError('priority must be an integer or Priority enum value') diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/utils.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/utils.py deleted file mode 100644 index 144d11e1a..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/runner/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random -import sys -import time -import warnings -from getpass import getuser -from socket import gethostname - -import numpy as np -import torch - -import mmcv - - -def get_host_info(): - """Get hostname and username. - - Return empty string if exception raised, e.g. ``getpass.getuser()`` will - lead to error in docker container - """ - host = '' - try: - host = f'{getuser()}@{gethostname()}' - except Exception as e: - warnings.warn(f'Host or user not found: {str(e)}') - finally: - return host - - -def get_time_str(): - return time.strftime('%Y%m%d_%H%M%S', time.localtime()) - - -def obj_from_dict(info, parent=None, default_args=None): - """Initialize an object from dict. - - The dict must contain the key "type", which indicates the object type, it - can be either a string or type, such as "list" or ``list``. Remaining - fields are treated as the arguments for constructing the object. - - Args: - info (dict): Object types and arguments. - parent (:class:`module`): Module which may containing expected object - classes. - default_args (dict, optional): Default arguments for initializing the - object. - - Returns: - any type: Object built from the dict. - """ - assert isinstance(info, dict) and 'type' in info - assert isinstance(default_args, dict) or default_args is None - args = info.copy() - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if parent is not None: - obj_type = getattr(parent, obj_type) - else: - obj_type = sys.modules[obj_type] - elif not isinstance(obj_type, type): - raise TypeError('type must be a str or valid type, but ' - f'got {type(obj_type)}') - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - return obj_type(**args) - - -def set_random_seed(seed, deterministic=False, use_rank_shift=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - rank_shift (bool): Whether to add rank number to the random seed to - have different random seed in different threads. Default: False. - """ - if use_rank_shift: - rank, _ = mmcv.runner.get_dist_info() - seed += rank - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/__init__.py deleted file mode 100644 index e0825ed67..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/__init__.py +++ /dev/null @@ -1,78 +0,0 @@ -# flake8: noqa -# Copyright (c) OpenMMLab. All rights reserved. -from .config import Config, ConfigDict, DictAction -from .misc import (check_prerequisites, concat_list, deprecated_api_warning, - has_method, import_modules_from_strings, is_list_of, - is_method_overridden, is_seq_of, is_str, is_tuple_of, - iter_cast, list_cast, requires_executable, requires_package, - slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, - to_ntuple, tuple_cast) -from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, - scandir, symlink) -from .progressbar import (ProgressBar, track_iter_progress, - track_parallel_progress, track_progress) -from .testing import (assert_attrs_equal, assert_dict_contains_subset, - assert_dict_has_keys, assert_is_norm_layer, - assert_keys_equal, assert_params_all_zeros, - check_python_script) -from .timer import Timer, TimerError, check_time -from .version_utils import digit_version, get_git_hash - -try: - import torch -except ImportError: - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', - 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', - 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', - 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', - 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', - 'track_progress', 'track_iter_progress', 'track_parallel_progress', - 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', - 'digit_version', 'get_git_hash', 'import_modules_from_strings', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', - 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', - 'is_method_overridden', 'has_method' - ] -else: - from .device_type import IS_IPU_AVAILABLE, IS_MLU_AVAILABLE - from .env import collect_env - from .hub import load_url - from .logging import get_logger, print_log - from .parrots_jit import jit, skip_no_elena - # yapf: disable - from .parrots_wrapper import (IS_CUDA_AVAILABLE, TORCH_VERSION, - BuildExtension, CppExtension, CUDAExtension, - DataLoader, PoolDataLoader, SyncBatchNorm, - _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, - _AvgPoolNd, _BatchNorm, _ConvNd, - _ConvTransposeMixin, _get_cuda_home, - _InstanceNorm, _MaxPoolNd, get_build_config, - is_rocm_pytorch) - # yapf: enable - from .registry import Registry, build_from_cfg - from .seed import worker_init_fn - from .trace import is_jit_tracing - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', - 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', - 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', - 'check_prerequisites', 'requires_package', 'requires_executable', - 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', - 'symlink', 'scandir', 'ProgressBar', 'track_progress', - 'track_iter_progress', 'track_parallel_progress', 'Registry', - 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', - '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', - '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', - 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', - 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', - 'deprecated_api_warning', 'digit_version', 'get_git_hash', - 'import_modules_from_strings', 'jit', 'skip_no_elena', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', - 'assert_params_all_zeros', 'check_python_script', - 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', - '_get_cuda_home', 'load_url', 'has_method', 'IS_CUDA_AVAILABLE', - 'worker_init_fn', 'IS_MLU_AVAILABLE', 'IS_IPU_AVAILABLE' - ] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/config.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/config.py deleted file mode 100644 index 8efbc24e2..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/config.py +++ /dev/null @@ -1,719 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import ast -import copy -import os -import os.path as osp -import platform -import shutil -import sys -import tempfile -import types -import uuid -import warnings -from argparse import Action, ArgumentParser -from collections import abc -from importlib import import_module -from pathlib import Path - -from addict import Dict -from yapf.yapflib.yapf_api import FormatCode - -from .misc import import_modules_from_strings -from .path import check_file_exist - -if platform.system() == 'Windows': - import regex as re -else: - import re - -BASE_KEY = '_base_' -DELETE_KEY = '_delete_' -DEPRECATION_KEY = '_deprecation_' -RESERVED_KEYS = ['filename', 'text', 'pretty_text'] - - -class ConfigDict(Dict): - - def __missing__(self, name): - raise KeyError(name) - - def __getattr__(self, name): - try: - value = super(ConfigDict, self).__getattr__(name) - except KeyError: - ex = AttributeError(f"'{self.__class__.__name__}' object has no " - f"attribute '{name}'") - except Exception as e: - ex = e - else: - return value - raise ex - - -def add_args(parser, cfg, prefix=''): - for k, v in cfg.items(): - if isinstance(v, str): - parser.add_argument('--' + prefix + k) - elif isinstance(v, int): - parser.add_argument('--' + prefix + k, type=int) - elif isinstance(v, float): - parser.add_argument('--' + prefix + k, type=float) - elif isinstance(v, bool): - parser.add_argument('--' + prefix + k, action='store_true') - elif isinstance(v, dict): - add_args(parser, v, prefix + k + '.') - elif isinstance(v, abc.Iterable): - parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') - else: - print(f'cannot parse key {prefix + k} of type {type(v)}') - return parser - - -class Config: - """A facility for config and config files. - - It supports common file formats as configs: python/json/yaml. The interface - is the same as a dict object and also allows access config values as - attributes. - - Example: - >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - >>> cfg.a - 1 - >>> cfg.b - {'b1': [0, 1]} - >>> cfg.b.b1 - [0, 1] - >>> cfg = Config.fromfile('tests/data/config/a.py') - >>> cfg.filename - "/home/kchen/projects/mmcv/tests/data/config/a.py" - >>> cfg.item4 - 'test' - >>> cfg - "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " - "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" - """ - - @staticmethod - def _validate_py_syntax(filename): - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - content = f.read() - try: - ast.parse(content) - except SyntaxError as e: - raise SyntaxError('There are syntax errors in config ' - f'file {filename}: {e}') - - @staticmethod - def _substitute_predefined_vars(filename, temp_config_name): - file_dirname = osp.dirname(filename) - file_basename = osp.basename(filename) - file_basename_no_extension = osp.splitext(file_basename)[0] - file_extname = osp.splitext(filename)[1] - support_templates = dict( - fileDirname=file_dirname, - fileBasename=file_basename, - fileBasenameNoExtension=file_basename_no_extension, - fileExtname=file_extname) - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - for key, value in support_templates.items(): - regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' - value = value.replace('\\', '/') - config_file = re.sub(regexp, value, config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - - @staticmethod - def _pre_substitute_base_vars(filename, temp_config_name): - """Substitute base variable placehoders to string, so that parsing - would work.""" - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - base_var_dict = {} - regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' - base_vars = set(re.findall(regexp, config_file)) - for base_var in base_vars: - randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' - base_var_dict[randstr] = base_var - regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' - config_file = re.sub(regexp, f'"{randstr}"', config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - return base_var_dict - - @staticmethod - def _substitute_base_vars(cfg, base_var_dict, base_cfg): - """Substitute variable strings to their actual values.""" - cfg = copy.deepcopy(cfg) - - if isinstance(cfg, dict): - for k, v in cfg.items(): - if isinstance(v, str) and v in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[v].split('.'): - new_v = new_v[new_k] - cfg[k] = new_v - elif isinstance(v, (list, tuple, dict)): - cfg[k] = Config._substitute_base_vars( - v, base_var_dict, base_cfg) - elif isinstance(cfg, tuple): - cfg = tuple( - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg) - elif isinstance(cfg, list): - cfg = [ - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg - ] - elif isinstance(cfg, str) and cfg in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[cfg].split('.'): - new_v = new_v[new_k] - cfg = new_v - - return cfg - - @staticmethod - def _file2dict(filename, use_predefined_variables=True): - filename = osp.abspath(osp.expanduser(filename)) - check_file_exist(filename) - fileExtname = osp.splitext(filename)[1] - if fileExtname not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - - with tempfile.TemporaryDirectory() as temp_config_dir: - temp_config_file = tempfile.NamedTemporaryFile( - dir=temp_config_dir, suffix=fileExtname) - if platform.system() == 'Windows': - temp_config_file.close() - temp_config_name = osp.basename(temp_config_file.name) - # Substitute predefined variables - if use_predefined_variables: - Config._substitute_predefined_vars(filename, - temp_config_file.name) - else: - shutil.copyfile(filename, temp_config_file.name) - # Substitute base variables from placeholders to strings - base_var_dict = Config._pre_substitute_base_vars( - temp_config_file.name, temp_config_file.name) - - if filename.endswith('.py'): - temp_module_name = osp.splitext(temp_config_name)[0] - sys.path.insert(0, temp_config_dir) - Config._validate_py_syntax(filename) - mod = import_module(temp_module_name) - sys.path.pop(0) - cfg_dict = { - name: value - for name, value in mod.__dict__.items() - if not name.startswith('__') - and not isinstance(value, types.ModuleType) - and not isinstance(value, types.FunctionType) - } - # delete imported module - del sys.modules[temp_module_name] - elif filename.endswith(('.yml', '.yaml', '.json')): - import mmcv - cfg_dict = mmcv.load(temp_config_file.name) - # close temp file - temp_config_file.close() - - # check deprecation information - if DEPRECATION_KEY in cfg_dict: - deprecation_info = cfg_dict.pop(DEPRECATION_KEY) - warning_msg = f'The config file {filename} will be deprecated ' \ - 'in the future.' - if 'expected' in deprecation_info: - warning_msg += f' Please use {deprecation_info["expected"]} ' \ - 'instead.' - if 'reference' in deprecation_info: - warning_msg += ' More information can be found at ' \ - f'{deprecation_info["reference"]}' - warnings.warn(warning_msg, DeprecationWarning) - - cfg_text = filename + '\n' - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - cfg_text += f.read() - - if BASE_KEY in cfg_dict: - cfg_dir = osp.dirname(filename) - base_filename = cfg_dict.pop(BASE_KEY) - base_filename = base_filename if isinstance( - base_filename, list) else [base_filename] - - cfg_dict_list = list() - cfg_text_list = list() - for f in base_filename: - _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) - cfg_dict_list.append(_cfg_dict) - cfg_text_list.append(_cfg_text) - - base_cfg_dict = dict() - for c in cfg_dict_list: - duplicate_keys = base_cfg_dict.keys() & c.keys() - if len(duplicate_keys) > 0: - raise KeyError('Duplicate key is not allowed among bases. ' - f'Duplicate keys: {duplicate_keys}') - base_cfg_dict.update(c) - - # Substitute base variables from strings to their actual values - cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, - base_cfg_dict) - - base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) - cfg_dict = base_cfg_dict - - # merge cfg_text - cfg_text_list.append(cfg_text) - cfg_text = '\n'.join(cfg_text_list) - - return cfg_dict, cfg_text - - @staticmethod - def _merge_a_into_b(a, b, allow_list_keys=False): - """merge dict ``a`` into dict ``b`` (non-inplace). - - Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid - in-place modifications. - - Args: - a (dict): The source dict to be merged into ``b``. - b (dict): The origin dict to be fetch keys from ``a``. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in source ``a`` and will replace the element of the - corresponding index in b if b is a list. Default: False. - - Returns: - dict: The modified dict of ``b`` using ``a``. - - Examples: - # Normally merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # Delete b first and merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # b is a list - >>> Config._merge_a_into_b( - ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) - [{'a': 2}, {'b': 2}] - """ - b = b.copy() - for k, v in a.items(): - if allow_list_keys and k.isdigit() and isinstance(b, list): - k = int(k) - if len(b) <= k: - raise KeyError(f'Index {k} exceeds the length of list {b}') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - elif isinstance(v, dict): - if k in b and not v.pop(DELETE_KEY, False): - allowed_types = (dict, list) if allow_list_keys else dict - if not isinstance(b[k], allowed_types): - raise TypeError( - f'{k}={v} in child config cannot inherit from ' - f'base because {k} is a dict in the child config ' - f'but is of type {type(b[k])} in base config. ' - f'You may set `{DELETE_KEY}=True` to ignore the ' - f'base config.') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - else: - b[k] = ConfigDict(v) - else: - b[k] = v - return b - - @staticmethod - def fromfile(filename, - use_predefined_variables=True, - import_custom_modules=True): - if isinstance(filename, Path): - filename = str(filename) - cfg_dict, cfg_text = Config._file2dict(filename, - use_predefined_variables) - if import_custom_modules and cfg_dict.get('custom_imports', None): - import_modules_from_strings(**cfg_dict['custom_imports']) - return Config(cfg_dict, cfg_text=cfg_text, filename=filename) - - @staticmethod - def fromstring(cfg_str, file_format): - """Generate config from config str. - - Args: - cfg_str (str): Config str. - file_format (str): Config file format corresponding to the - config str. Only py/yml/yaml/json type are supported now! - - Returns: - :obj:`Config`: Config obj. - """ - if file_format not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - if file_format != '.py' and 'dict(' in cfg_str: - # check if users specify a wrong suffix for python - warnings.warn( - 'Please check "file_format", the file format may be .py') - with tempfile.NamedTemporaryFile( - 'w', encoding='utf-8', suffix=file_format, - delete=False) as temp_file: - temp_file.write(cfg_str) - # on windows, previous implementation cause error - # see PR 1077 for details - cfg = Config.fromfile(temp_file.name) - os.remove(temp_file.name) - return cfg - - @staticmethod - def auto_argparser(description=None): - """Generate argparser from config file automatically (experimental)""" - partial_parser = ArgumentParser(description=description) - partial_parser.add_argument('config', help='config file path') - cfg_file = partial_parser.parse_known_args()[0].config - cfg = Config.fromfile(cfg_file) - parser = ArgumentParser(description=description) - parser.add_argument('config', help='config file path') - add_args(parser, cfg) - return parser, cfg - - def __init__(self, cfg_dict=None, cfg_text=None, filename=None): - if cfg_dict is None: - cfg_dict = dict() - elif not isinstance(cfg_dict, dict): - raise TypeError('cfg_dict must be a dict, but ' - f'got {type(cfg_dict)}') - for key in cfg_dict: - if key in RESERVED_KEYS: - raise KeyError(f'{key} is reserved for config file') - - if isinstance(filename, Path): - filename = str(filename) - - super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) - super(Config, self).__setattr__('_filename', filename) - if cfg_text: - text = cfg_text - elif filename: - with open(filename, 'r') as f: - text = f.read() - else: - text = '' - super(Config, self).__setattr__('_text', text) - - @property - def filename(self): - return self._filename - - @property - def text(self): - return self._text - - @property - def pretty_text(self): - - indent = 4 - - def _indent(s_, num_spaces): - s = s_.split('\n') - if len(s) == 1: - return s_ - first = s.pop(0) - s = [(num_spaces * ' ') + line for line in s] - s = '\n'.join(s) - s = first + '\n' + s - return s - - def _format_basic_types(k, v, use_mapping=False): - if isinstance(v, str): - v_str = f"'{v}'" - else: - v_str = str(v) - - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) - - return attr_str - - def _format_list(k, v, use_mapping=False): - # check if all items in the list are dict - if all(isinstance(_, dict) for _ in v): - v_str = '[\n' - v_str += '\n'.join( - f'dict({_indent(_format_dict(v_), indent)}),' - for v_ in v).rstrip(',') - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) + ']' - else: - attr_str = _format_basic_types(k, v, use_mapping) - return attr_str - - def _contain_invalid_identifier(dict_str): - contain_invalid_identifier = False - for key_name in dict_str: - contain_invalid_identifier |= \ - (not str(key_name).isidentifier()) - return contain_invalid_identifier - - def _format_dict(input_dict, outest_level=False): - r = '' - s = [] - - use_mapping = _contain_invalid_identifier(input_dict) - if use_mapping: - r += '{' - for idx, (k, v) in enumerate(input_dict.items()): - is_last = idx >= len(input_dict) - 1 - end = '' if outest_level or is_last else ',' - if isinstance(v, dict): - v_str = '\n' + _format_dict(v) - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: dict({v_str}' - else: - attr_str = f'{str(k)}=dict({v_str}' - attr_str = _indent(attr_str, indent) + ')' + end - elif isinstance(v, list): - attr_str = _format_list(k, v, use_mapping) + end - else: - attr_str = _format_basic_types(k, v, use_mapping) + end - - s.append(attr_str) - r += '\n'.join(s) - if use_mapping: - r += '}' - return r - - cfg_dict = self._cfg_dict.to_dict() - text = _format_dict(cfg_dict, outest_level=True) - # copied from setup.cfg - yapf_style = dict( - based_on_style='pep8', - blank_line_before_nested_class_or_def=True, - split_before_expression_after_opening_paren=True) - text, _ = FormatCode(text, style_config=yapf_style, verify=True) - - return text - - def __repr__(self): - return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' - - def __len__(self): - return len(self._cfg_dict) - - def __getattr__(self, name): - return getattr(self._cfg_dict, name) - - def __getitem__(self, name): - return self._cfg_dict.__getitem__(name) - - def __setattr__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setattr__(name, value) - - def __setitem__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setitem__(name, value) - - def __iter__(self): - return iter(self._cfg_dict) - - def __getstate__(self): - return (self._cfg_dict, self._filename, self._text) - - def __copy__(self): - cls = self.__class__ - other = cls.__new__(cls) - other.__dict__.update(self.__dict__) - - return other - - def __deepcopy__(self, memo): - cls = self.__class__ - other = cls.__new__(cls) - memo[id(self)] = other - - for key, value in self.__dict__.items(): - super(Config, other).__setattr__(key, copy.deepcopy(value, memo)) - - return other - - def __setstate__(self, state): - _cfg_dict, _filename, _text = state - super(Config, self).__setattr__('_cfg_dict', _cfg_dict) - super(Config, self).__setattr__('_filename', _filename) - super(Config, self).__setattr__('_text', _text) - - def dump(self, file=None): - cfg_dict = super(Config, self).__getattribute__('_cfg_dict').to_dict() - if self.filename.endswith('.py'): - if file is None: - return self.pretty_text - else: - with open(file, 'w', encoding='utf-8') as f: - f.write(self.pretty_text) - else: - import mmcv - if file is None: - file_format = self.filename.split('.')[-1] - return mmcv.dump(cfg_dict, file_format=file_format) - else: - mmcv.dump(cfg_dict, file) - - def merge_from_dict(self, options, allow_list_keys=True): - """Merge list into cfg_dict. - - Merge the dict parsed by MultipleKVAction into this cfg. - - Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} - >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) - >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - - >>> # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) - >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) - >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - - Args: - options (dict): dict of configs to merge from. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in ``options`` and will replace the element of the - corresponding index in the config if the config is a list. - Default: True. - """ - option_cfg_dict = {} - for full_key, v in options.items(): - d = option_cfg_dict - key_list = full_key.split('.') - for subkey in key_list[:-1]: - d.setdefault(subkey, ConfigDict()) - d = d[subkey] - subkey = key_list[-1] - d[subkey] = v - - cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - super(Config, self).__setattr__( - '_cfg_dict', - Config._merge_a_into_b( - option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) - - -class DictAction(Action): - """ - argparse action to split an argument into KEY=VALUE form - on the first = and append to a dictionary. List options can - be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit - brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build - list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' - """ - - @staticmethod - def _parse_int_float_bool(val): - try: - return int(val) - except ValueError: - pass - try: - return float(val) - except ValueError: - pass - if val.lower() in ['true', 'false']: - return True if val.lower() == 'true' else False - if val == 'None': - return None - return val - - @staticmethod - def _parse_iterable(val): - """Parse iterable values in the string. - - All elements inside '()' or '[]' are treated as iterable values. - - Args: - val (str): Value string. - - Returns: - list | tuple: The expanded list or tuple from the string. - - Examples: - >>> DictAction._parse_iterable('1,2,3') - [1, 2, 3] - >>> DictAction._parse_iterable('[a, b, c]') - ['a', 'b', 'c'] - >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') - [(1, 2, 3), ['a', 'b'], 'c'] - """ - - def find_next_comma(string): - """Find the position of next comma in the string. - - If no ',' is found in the string, return the string length. All - chars inside '()' and '[]' are treated as one element and thus ',' - inside these brackets are ignored. - """ - assert (string.count('(') == string.count(')')) and ( - string.count('[') == string.count(']')), \ - f'Imbalanced brackets exist in {string}' - end = len(string) - for idx, char in enumerate(string): - pre = string[:idx] - # The string before this ',' is balanced - if ((char == ',') and (pre.count('(') == pre.count(')')) - and (pre.count('[') == pre.count(']'))): - end = idx - break - return end - - # Strip ' and " characters and replace whitespace. - val = val.strip('\'\"').replace(' ', '') - is_tuple = False - if val.startswith('(') and val.endswith(')'): - is_tuple = True - val = val[1:-1] - elif val.startswith('[') and val.endswith(']'): - val = val[1:-1] - elif ',' not in val: - # val is a single value - return DictAction._parse_int_float_bool(val) - - values = [] - while len(val) > 0: - comma_idx = find_next_comma(val) - element = DictAction._parse_iterable(val[:comma_idx]) - values.append(element) - val = val[comma_idx + 1:] - if is_tuple: - values = tuple(values) - return values - - def __call__(self, parser, namespace, values, option_string=None): - options = {} - for kv in values: - key, val = kv.split('=', maxsplit=1) - options[key] = self._parse_iterable(val) - setattr(namespace, self.dest, options) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/device_type.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/device_type.py deleted file mode 100644 index c29d944ab..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/device_type.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - - -def is_ipu_available(): - try: - import poptorch - return poptorch.ipuHardwareIsAvailable() - except ImportError: - return False - - -IS_IPU_AVAILABLE = is_ipu_available() - - -def is_mlu_available(): - try: - import torch - return (hasattr(torch, 'is_mlu_available') - and torch.is_mlu_available()) - except Exception: - return False - - -IS_MLU_AVAILABLE = is_mlu_available() diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/env.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/env.py deleted file mode 100644 index 511332506..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/env.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This file holding some environment constant for sharing by other files.""" - -import os.path as osp -import subprocess -import sys -from collections import defaultdict - -import cv2 -import torch - -import mmcv -from .parrots_wrapper import get_build_config - - -def collect_env(): - """Collect the information of the running environments. - - Returns: - dict: The environment information. The following fields are contained. - - - sys.platform: The variable of ``sys.platform``. - - Python: Python version. - - CUDA available: Bool, indicating if CUDA is available. - - GPU devices: Device type of each GPU. - - CUDA_HOME (optional): The env var ``CUDA_HOME``. - - NVCC (optional): NVCC version. - - GCC: GCC version, "n/a" if GCC is not installed. - - MSVC: Microsoft Virtual C++ Compiler version, Windows only. - - PyTorch: PyTorch version. - - PyTorch compiling details: The output of \ - ``torch.__config__.show()``. - - TorchVision (optional): TorchVision version. - - OpenCV: OpenCV version. - - MMCV: MMCV version. - - MMCV Compiler: The GCC version for compiling MMCV ops. - - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. - """ - env_info = {} - env_info['sys.platform'] = sys.platform - env_info['Python'] = sys.version.replace('\n', '') - - cuda_available = torch.cuda.is_available() - env_info['CUDA available'] = cuda_available - - if cuda_available: - devices = defaultdict(list) - for k in range(torch.cuda.device_count()): - devices[torch.cuda.get_device_name(k)].append(str(k)) - for name, device_ids in devices.items(): - env_info['GPU ' + ','.join(device_ids)] = name - - from mmcv.utils.parrots_wrapper import _get_cuda_home - CUDA_HOME = _get_cuda_home() - env_info['CUDA_HOME'] = CUDA_HOME - - if CUDA_HOME is not None and osp.isdir(CUDA_HOME): - try: - nvcc = osp.join(CUDA_HOME, 'bin/nvcc') - nvcc = subprocess.check_output(f'"{nvcc}" -V', shell=True) - nvcc = nvcc.decode('utf-8').strip() - release = nvcc.rfind('Cuda compilation tools') - build = nvcc.rfind('Build ') - nvcc = nvcc[release:build].strip() - except subprocess.SubprocessError: - nvcc = 'Not Available' - env_info['NVCC'] = nvcc - - try: - # Check C++ Compiler. - # For Unix-like, sysconfig has 'CC' variable like 'gcc -pthread ...', - # indicating the compiler used, we use this to get the compiler name - import sysconfig - cc = sysconfig.get_config_var('CC') - if cc: - cc = osp.basename(cc.split()[0]) - cc_info = subprocess.check_output(f'{cc} --version', shell=True) - env_info['GCC'] = cc_info.decode('utf-8').partition( - '\n')[0].strip() - else: - # on Windows, cl.exe is not in PATH. We need to find the path. - # distutils.ccompiler.new_compiler() returns a msvccompiler - # object and after initialization, path to cl.exe is found. - import locale - import os - from distutils.ccompiler import new_compiler - ccompiler = new_compiler() - ccompiler.initialize() - cc = subprocess.check_output( - f'{ccompiler.cc}', stderr=subprocess.STDOUT, shell=True) - encoding = os.device_encoding( - sys.stdout.fileno()) or locale.getpreferredencoding() - env_info['MSVC'] = cc.decode(encoding).partition('\n')[0].strip() - env_info['GCC'] = 'n/a' - except subprocess.CalledProcessError: - env_info['GCC'] = 'n/a' - - env_info['PyTorch'] = torch.__version__ - env_info['PyTorch compiling details'] = get_build_config() - - try: - import torchvision - env_info['TorchVision'] = torchvision.__version__ - except ModuleNotFoundError: - pass - - env_info['OpenCV'] = cv2.__version__ - - env_info['MMCV'] = mmcv.__version__ - - try: - from mmcv.ops import get_compiler_version, get_compiling_cuda_version - except ModuleNotFoundError: - env_info['MMCV Compiler'] = 'n/a' - env_info['MMCV CUDA Compiler'] = 'n/a' - else: - env_info['MMCV Compiler'] = get_compiler_version() - env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() - - return env_info diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/ext_loader.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/ext_loader.py deleted file mode 100644 index f82c6d568..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/ext_loader.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os -import pkgutil -import warnings -from collections import namedtuple - -import torch - -if torch.__version__ != 'parrots': - - def load_ext(name, funcs): - ext = importlib.import_module('mmcv.' + name) - for fun in funcs: - assert hasattr(ext, fun), f'{fun} miss in module {name}' - return ext -else: - from parrots import extension - from parrots.base import ParrotsException - - has_return_value_ops = [ - 'nms', - 'softnms', - 'nms_match', - 'nms_rotated', - 'top_pool_forward', - 'top_pool_backward', - 'bottom_pool_forward', - 'bottom_pool_backward', - 'left_pool_forward', - 'left_pool_backward', - 'right_pool_forward', - 'right_pool_backward', - 'fused_bias_leakyrelu', - 'upfirdn2d', - 'ms_deform_attn_forward', - 'pixel_group', - 'contour_expand', - 'diff_iou_rotated_sort_vertices_forward', - ] - - def get_fake_func(name, e): - - def fake_func(*args, **kwargs): - warnings.warn(f'{name} is not supported in parrots now') - raise e - - return fake_func - - def load_ext(name, funcs): - ExtModule = namedtuple('ExtModule', funcs) - ext_list = [] - lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - for fun in funcs: - try: - ext_fun = extension.load(fun, name, lib_dir=lib_root) - except ParrotsException as e: - if 'No element registered' not in e.message: - warnings.warn(e.message) - ext_fun = get_fake_func(fun, e) - ext_list.append(ext_fun) - else: - if fun in has_return_value_ops: - ext_list.append(ext_fun.op) - else: - ext_list.append(ext_fun.op_) - return ExtModule(*ext_list) - - -def check_ops_exist(): - ext_loader = pkgutil.find_loader('mmcv._ext') - return ext_loader is not None diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/hub.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/hub.py deleted file mode 100644 index 12fbff2ee..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/hub.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# The 1.6 release of PyTorch switched torch.save to use a new zipfile-based -# file format. It will cause RuntimeError when a checkpoint was saved in -# torch >= 1.6.0 but loaded in torch < 1.7.0. -# More details at https://github.com/open-mmlab/mmpose/issues/904 -from .parrots_wrapper import TORCH_VERSION -from .path import mkdir_or_exist -from .version_utils import digit_version - -if TORCH_VERSION != 'parrots' and digit_version(TORCH_VERSION) < digit_version( - '1.7.0'): - # Modified from https://github.com/pytorch/pytorch/blob/master/torch/hub.py - import os - import sys - import warnings - import zipfile - from urllib.parse import urlparse - - import torch - from torch.hub import HASH_REGEX, _get_torch_home, download_url_to_file - - # Hub used to support automatically extracts from zipfile manually - # compressed by users. The legacy zip format expects only one file from - # torch.save() < 1.6 in the zip. We should remove this support since - # zipfile is now default zipfile format for torch.save(). - def _is_legacy_zip_format(filename): - if zipfile.is_zipfile(filename): - infolist = zipfile.ZipFile(filename).infolist() - return len(infolist) == 1 and not infolist[0].is_dir() - return False - - def _legacy_zip_load(filename, model_dir, map_location): - warnings.warn( - 'Falling back to the old format < 1.6. This support will' - ' be deprecated in favor of default zipfile format ' - 'introduced in 1.6. Please redo torch.save() to save it ' - 'in the new zipfile format.', DeprecationWarning) - # Note: extractall() defaults to overwrite file if exists. No need to - # clean up beforehand. We deliberately don't handle tarfile here - # since our legacy serialization format was in tar. - # E.g. resnet18-5c106cde.pth which is widely used. - with zipfile.ZipFile(filename) as f: - members = f.infolist() - if len(members) != 1: - raise RuntimeError( - 'Only one file(not dir) is allowed in the zipfile') - f.extractall(model_dir) - extraced_name = members[0].filename - extracted_file = os.path.join(model_dir, extraced_name) - return torch.load(extracted_file, map_location=map_location) - - def load_url(url, - model_dir=None, - map_location=None, - progress=True, - check_hash=False, - file_name=None): - r"""Loads the Torch serialized object at the given URL. - - If downloaded file is a zip file, it will be automatically decompressed - - If the object is already present in `model_dir`, it's deserialized and - returned. - The default value of ``model_dir`` is ``/checkpoints`` where - ``hub_dir`` is the directory returned by :func:`~torch.hub.get_dir`. - - Args: - url (str): URL of the object to download - model_dir (str, optional): directory in which to save the object - map_location (optional): a function or a dict specifying how to - remap storage locations (see torch.load) - progress (bool, optional): whether or not to display a progress bar - to stderr. Default: True - check_hash(bool, optional): If True, the filename part of the URL - should follow the naming convention ``filename-.ext`` - where ```` is the first eight or more digits of the - SHA256 hash of the contents of the file. The hash is used to - ensure unique names and to verify the contents of the file. - Default: False - file_name (str, optional): name for the downloaded file. Filename - from ``url`` will be used if not set. Default: None. - - Example: - >>> url = ('https://s3.amazonaws.com/pytorch/models/resnet18-5c106' - ... 'cde.pth') - >>> state_dict = torch.hub.load_state_dict_from_url(url) - """ - # Issue warning to move data if old env is set - if os.getenv('TORCH_MODEL_ZOO'): - warnings.warn( - 'TORCH_MODEL_ZOO is deprecated, please use env ' - 'TORCH_HOME instead', DeprecationWarning) - - if model_dir is None: - torch_home = _get_torch_home() - model_dir = os.path.join(torch_home, 'checkpoints') - - mkdir_or_exist(model_dir) - - parts = urlparse(url) - filename = os.path.basename(parts.path) - if file_name is not None: - filename = file_name - cached_file = os.path.join(model_dir, filename) - if not os.path.exists(cached_file): - sys.stderr.write('Downloading: "{}" to {}\n'.format( - url, cached_file)) - hash_prefix = None - if check_hash: - r = HASH_REGEX.search(filename) # r is Optional[Match[str]] - hash_prefix = r.group(1) if r else None - download_url_to_file( - url, cached_file, hash_prefix, progress=progress) - - if _is_legacy_zip_format(cached_file): - return _legacy_zip_load(cached_file, model_dir, map_location) - - try: - return torch.load(cached_file, map_location=map_location) - except RuntimeError as error: - if digit_version(TORCH_VERSION) < digit_version('1.5.0'): - warnings.warn( - f'If the error is the same as "{cached_file} is a zip ' - 'archive (did you mean to use torch.jit.load()?)", you can' - ' upgrade your torch to 1.5.0 or higher (current torch ' - f'version is {TORCH_VERSION}). The error was raised ' - ' because the checkpoint was saved in torch>=1.6.0 but ' - 'loaded in torch<1.5.') - raise error -else: - from torch.utils.model_zoo import load_url # noqa: F401 diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/logging.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/logging.py deleted file mode 100644 index c4c7025f0..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/logging.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.distributed as dist - -logger_initialized = {} - - -def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): - """Initialize and get a logger by name. - - If the logger has not been initialized, this method will initialize the - logger by adding one or two handlers, otherwise the initialized logger will - be directly returned. During initialization, a StreamHandler will always be - added. If `log_file` is specified and the process rank is 0, a FileHandler - will also be added. - - Args: - name (str): Logger name. - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the logger. - log_level (int): The logger level. Note that only the process of - rank 0 is affected, and other processes will set the level to - "Error" thus be silent most of the time. - file_mode (str): The file mode used in opening log file. - Defaults to 'w'. - - Returns: - logging.Logger: The expected logger. - """ - logger = logging.getLogger(name) - if name in logger_initialized: - return logger - # handle hierarchical names - # e.g., logger "a" is initialized, then logger "a.b" will skip the - # initialization since it is a child of "a". - for logger_name in logger_initialized: - if name.startswith(logger_name): - return logger - - # handle duplicate logs to the console - # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) - # to the root logger. As logger.propagate is True by default, this root - # level handler causes logging messages from rank>0 processes to - # unexpectedly show up on the console, creating much unwanted clutter. - # To fix this issue, we set the root logger's StreamHandler, if any, to log - # at the ERROR level. - for handler in logger.root.handlers: - if type(handler) is logging.StreamHandler: - handler.setLevel(logging.ERROR) - - stream_handler = logging.StreamHandler() - handlers = [stream_handler] - - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - else: - rank = 0 - - # only rank 0 will add a FileHandler - if rank == 0 and log_file is not None: - # Here, the default behaviour of the official logger is 'a'. Thus, we - # provide an interface to change the file mode to the default - # behaviour. - file_handler = logging.FileHandler(log_file, file_mode) - handlers.append(file_handler) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - for handler in handlers: - handler.setFormatter(formatter) - handler.setLevel(log_level) - logger.addHandler(handler) - - if rank == 0: - logger.setLevel(log_level) - else: - logger.setLevel(logging.ERROR) - - logger_initialized[name] = True - - return logger - - -def print_log(msg, logger=None, level=logging.INFO): - """Print a log message. - - Args: - msg (str): The message to be logged. - logger (logging.Logger | str | None): The logger to be used. - Some special loggers are: - - - "silent": no message will be printed. - - other str: the logger obtained with `get_root_logger(logger)`. - - None: The `print()` method will be used to print log messages. - level (int): Logging level. Only available when `logger` is a Logger - object or "root". - """ - if logger is None: - print(msg) - elif isinstance(logger, logging.Logger): - logger.log(level, msg) - elif logger == 'silent': - pass - elif isinstance(logger, str): - _logger = get_logger(logger) - _logger.log(level, msg) - else: - raise TypeError( - 'logger should be either a logging.Logger object, str, ' - f'"silent" or None, but got {type(logger)}') diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/misc.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/misc.py deleted file mode 100644 index 7957ea89b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/misc.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections.abc -import functools -import itertools -import subprocess -import warnings -from collections import abc -from importlib import import_module -from inspect import getfullargspec -from itertools import repeat - - -# From PyTorch internals -def _ntuple(n): - - def parse(x): - if isinstance(x, collections.abc.Iterable): - return x - return tuple(repeat(x, n)) - - return parse - - -to_1tuple = _ntuple(1) -to_2tuple = _ntuple(2) -to_3tuple = _ntuple(3) -to_4tuple = _ntuple(4) -to_ntuple = _ntuple - - -def is_str(x): - """Whether the input is an string instance. - - Note: This method is deprecated since python 2 is no longer supported. - """ - return isinstance(x, str) - - -def import_modules_from_strings(imports, allow_failed_imports=False): - """Import modules from the given list of strings. - - Args: - imports (list | str | None): The given module names to be imported. - allow_failed_imports (bool): If True, the failed imports will return - None. Otherwise, an ImportError is raise. Default: False. - - Returns: - list[module] | module | None: The imported modules. - - Examples: - >>> osp, sys = import_modules_from_strings( - ... ['os.path', 'sys']) - >>> import os.path as osp_ - >>> import sys as sys_ - >>> assert osp == osp_ - >>> assert sys == sys_ - """ - if not imports: - return - single_import = False - if isinstance(imports, str): - single_import = True - imports = [imports] - if not isinstance(imports, list): - raise TypeError( - f'custom_imports must be a list but got type {type(imports)}') - imported = [] - for imp in imports: - if not isinstance(imp, str): - raise TypeError( - f'{imp} is of type {type(imp)} and cannot be imported.') - try: - imported_tmp = import_module(imp) - except ImportError: - if allow_failed_imports: - warnings.warn(f'{imp} failed to import and is ignored.', - UserWarning) - imported_tmp = None - else: - raise ImportError - imported.append(imported_tmp) - if single_import: - imported = imported[0] - return imported - - -def iter_cast(inputs, dst_type, return_type=None): - """Cast elements of an iterable object into some type. - - Args: - inputs (Iterable): The input object. - dst_type (type): Destination type. - return_type (type, optional): If specified, the output object will be - converted to this type, otherwise an iterator. - - Returns: - iterator or specified type: The converted object. - """ - if not isinstance(inputs, abc.Iterable): - raise TypeError('inputs must be an iterable object') - if not isinstance(dst_type, type): - raise TypeError('"dst_type" must be a valid type') - - out_iterable = map(dst_type, inputs) - - if return_type is None: - return out_iterable - else: - return return_type(out_iterable) - - -def list_cast(inputs, dst_type): - """Cast elements of an iterable object into a list of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=list) - - -def tuple_cast(inputs, dst_type): - """Cast elements of an iterable object into a tuple of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=tuple) - - -def is_seq_of(seq, expected_type, seq_type=None): - """Check whether it is a sequence of some type. - - Args: - seq (Sequence): The sequence to be checked. - expected_type (type): Expected type of sequence items. - seq_type (type, optional): Expected sequence type. - - Returns: - bool: Whether the sequence is valid. - """ - if seq_type is None: - exp_seq_type = abc.Sequence - else: - assert isinstance(seq_type, type) - exp_seq_type = seq_type - if not isinstance(seq, exp_seq_type): - return False - for item in seq: - if not isinstance(item, expected_type): - return False - return True - - -def is_list_of(seq, expected_type): - """Check whether it is a list of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=list) - - -def is_tuple_of(seq, expected_type): - """Check whether it is a tuple of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=tuple) - - -def slice_list(in_list, lens): - """Slice a list into several sub lists by a list of given length. - - Args: - in_list (list): The list to be sliced. - lens(int or list): The expected length of each out list. - - Returns: - list: A list of sliced list. - """ - if isinstance(lens, int): - assert len(in_list) % lens == 0 - lens = [lens] * int(len(in_list) / lens) - if not isinstance(lens, list): - raise TypeError('"indices" must be an integer or a list of integers') - elif sum(lens) != len(in_list): - raise ValueError('sum of lens and list length does not ' - f'match: {sum(lens)} != {len(in_list)}') - out_list = [] - idx = 0 - for i in range(len(lens)): - out_list.append(in_list[idx:idx + lens[i]]) - idx += lens[i] - return out_list - - -def concat_list(in_list): - """Concatenate a list of list into a single list. - - Args: - in_list (list): The list of list to be merged. - - Returns: - list: The concatenated flat list. - """ - return list(itertools.chain(*in_list)) - - -def check_prerequisites( - prerequisites, - checker, - msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' - 'found, please install them first.'): # yapf: disable - """A decorator factory to check if prerequisites are satisfied. - - Args: - prerequisites (str of list[str]): Prerequisites to be checked. - checker (callable): The checker method that returns True if a - prerequisite is meet, False otherwise. - msg_tmpl (str): The message template with two variables. - - Returns: - decorator: A specific decorator. - """ - - def wrap(func): - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - requirements = [prerequisites] if isinstance( - prerequisites, str) else prerequisites - missing = [] - for item in requirements: - if not checker(item): - missing.append(item) - if missing: - print(msg_tmpl.format(', '.join(missing), func.__name__)) - raise RuntimeError('Prerequisites not meet.') - else: - return func(*args, **kwargs) - - return wrapped_func - - return wrap - - -def _check_py_package(package): - try: - import_module(package) - except ImportError: - return False - else: - return True - - -def _check_executable(cmd): - if subprocess.call(f'which {cmd}', shell=True) != 0: - return False - else: - return True - - -def requires_package(prerequisites): - """A decorator to check if some python packages are installed. - - Example: - >>> @requires_package('numpy') - >>> func(arg1, args): - >>> return numpy.zeros(1) - array([0.]) - >>> @requires_package(['numpy', 'non_package']) - >>> func(arg1, args): - >>> return numpy.zeros(1) - ImportError - """ - return check_prerequisites(prerequisites, checker=_check_py_package) - - -def requires_executable(prerequisites): - """A decorator to check if some executable files are installed. - - Example: - >>> @requires_executable('ffmpeg') - >>> func(arg1, args): - >>> print(1) - 1 - """ - return check_prerequisites(prerequisites, checker=_check_executable) - - -def deprecated_api_warning(name_dict, cls_name=None): - """A decorator to check if some arguments are deprecate and try to replace - deprecate src_arg_name to dst_arg_name. - - Args: - name_dict(dict): - key (str): Deprecate argument names. - val (str): Expected argument names. - - Returns: - func: New function. - """ - - def api_warning_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get name of the function - func_name = old_func.__name__ - if cls_name is not None: - func_name = f'{cls_name}.{func_name}' - if args: - arg_names = args_info.args[:len(args)] - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in arg_names: - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - arg_names[arg_names.index(src_arg_name)] = dst_arg_name - if kwargs: - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in kwargs: - - assert dst_arg_name not in kwargs, ( - f'The expected behavior is to replace ' - f'the deprecated key `{src_arg_name}` to ' - f'new key `{dst_arg_name}`, but got them ' - f'in the arguments at the same time, which ' - f'is confusing. `{src_arg_name} will be ' - f'deprecated in the future, please ' - f'use `{dst_arg_name}` instead.') - - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - kwargs[dst_arg_name] = kwargs.pop(src_arg_name) - - # apply converted arguments to the decorated method - output = old_func(*args, **kwargs) - return output - - return new_func - - return api_warning_wrapper - - -def is_method_overridden(method, base_class, derived_class): - """Check if a method of base class is overridden in derived class. - - Args: - method (str): the method name to check. - base_class (type): the class of the base class. - derived_class (type | Any): the class or instance of the derived class. - """ - assert isinstance(base_class, type), \ - "base_class doesn't accept instance, Please pass class instead." - - if not isinstance(derived_class, type): - derived_class = derived_class.__class__ - - base_method = getattr(base_class, method) - derived_method = getattr(derived_class, method) - return derived_method != base_method - - -def has_method(obj: object, method: str) -> bool: - """Check whether the object has a method. - - Args: - method (str): The method name to check. - obj (object): The object to check. - - Returns: - bool: True if the object has the method else False. - """ - return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/parrots_jit.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/parrots_jit.py deleted file mode 100644 index 61873f6db..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/parrots_jit.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -from .parrots_wrapper import TORCH_VERSION - -parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') - -if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': - from parrots.jit import pat as jit -else: - - def jit(func=None, - check_input=None, - full_shape=True, - derivate=False, - coderize=False, - optimize=False): - - def wrapper(func): - - def wrapper_inner(*args, **kargs): - return func(*args, **kargs) - - return wrapper_inner - - if func is None: - return wrapper - else: - return func - - -if TORCH_VERSION == 'parrots': - from parrots.utils.tester import skip_no_elena -else: - - def skip_no_elena(func): - - def wrapper(*args, **kargs): - return func(*args, **kargs) - - return wrapper diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/parrots_wrapper.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/parrots_wrapper.py deleted file mode 100644 index 7e657b561..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/parrots_wrapper.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch - -TORCH_VERSION = torch.__version__ - - -def is_cuda_available() -> bool: - return torch.cuda.is_available() - - -IS_CUDA_AVAILABLE = is_cuda_available() - - -def is_rocm_pytorch() -> bool: - is_rocm = False - if TORCH_VERSION != 'parrots': - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - return is_rocm - - -def _get_cuda_home(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import CUDA_HOME - else: - if is_rocm_pytorch(): - from torch.utils.cpp_extension import ROCM_HOME - CUDA_HOME = ROCM_HOME - else: - from torch.utils.cpp_extension import CUDA_HOME - return CUDA_HOME - - -def get_build_config(): - if TORCH_VERSION == 'parrots': - from parrots.config import get_build_info - return get_build_info() - else: - return torch.__config__.show() - - -def _get_conv(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin - else: - from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin - return _ConvNd, _ConvTransposeMixin - - -def _get_dataloader(): - if TORCH_VERSION == 'parrots': - from torch.utils.data import DataLoader, PoolDataLoader - else: - from torch.utils.data import DataLoader - PoolDataLoader = DataLoader - return DataLoader, PoolDataLoader - - -def _get_extension(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import BuildExtension, Extension - CppExtension = partial(Extension, cuda=False) - CUDAExtension = partial(Extension, cuda=True) - else: - from torch.utils.cpp_extension import (BuildExtension, CppExtension, - CUDAExtension) - return BuildExtension, CppExtension, CUDAExtension - - -def _get_pool(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - else: - from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd - - -def _get_norm(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm2d - else: - from torch.nn.modules.batchnorm import _BatchNorm - from torch.nn.modules.instancenorm import _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm - return _BatchNorm, _InstanceNorm, SyncBatchNorm_ - - -_ConvNd, _ConvTransposeMixin = _get_conv() -DataLoader, PoolDataLoader = _get_dataloader() -BuildExtension, CppExtension, CUDAExtension = _get_extension() -_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() -_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() - - -class SyncBatchNorm(SyncBatchNorm_): - - def _check_input_dim(self, input): - if TORCH_VERSION == 'parrots': - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input (got {input.dim()}D input)') - else: - super()._check_input_dim(input) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/path.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/path.py deleted file mode 100644 index 568081837..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/path.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -from pathlib import Path - -from .misc import is_str - - -def is_filepath(x): - return is_str(x) or isinstance(x, Path) - - -def fopen(filepath, *args, **kwargs): - if is_str(filepath): - return open(filepath, *args, **kwargs) - elif isinstance(filepath, Path): - return filepath.open(*args, **kwargs) - raise ValueError('`filepath` should be a string or a Path') - - -def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): - if not osp.isfile(filename): - raise FileNotFoundError(msg_tmpl.format(filename)) - - -def mkdir_or_exist(dir_name, mode=0o777): - if dir_name == '': - return - dir_name = osp.expanduser(dir_name) - os.makedirs(dir_name, mode=mode, exist_ok=True) - - -def symlink(src, dst, overwrite=True, **kwargs): - if os.path.lexists(dst) and overwrite: - os.remove(dst) - os.symlink(src, dst, **kwargs) - - -def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): - """Scan a directory to find the interested files. - - Args: - dir_path (str | :obj:`Path`): Path of the directory. - suffix (str | tuple(str), optional): File suffix that we are - interested in. Default: None. - recursive (bool, optional): If set to True, recursively scan the - directory. Default: False. - case_sensitive (bool, optional) : If set to False, ignore the case of - suffix. Default: True. - - Returns: - A generator for all the interested files with relative paths. - """ - if isinstance(dir_path, (str, Path)): - dir_path = str(dir_path) - else: - raise TypeError('"dir_path" must be a string or Path object') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('"suffix" must be a string or tuple of strings') - - if suffix is not None and not case_sensitive: - suffix = suffix.lower() if isinstance(suffix, str) else tuple( - item.lower() for item in suffix) - - root = dir_path - - def _scandir(dir_path, suffix, recursive, case_sensitive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - _rel_path = rel_path if case_sensitive else rel_path.lower() - if suffix is None or _rel_path.endswith(suffix): - yield rel_path - elif recursive and os.path.isdir(entry.path): - # scan recursively if entry.path is a directory - yield from _scandir(entry.path, suffix, recursive, - case_sensitive) - - return _scandir(dir_path, suffix, recursive, case_sensitive) - - -def find_vcs_root(path, markers=('.git', )): - """Finds the root directory (including itself) of specified markers. - - Args: - path (str): Path of directory or file. - markers (list[str], optional): List of file or directory names. - - Returns: - The directory contained one of the markers or None if not found. - """ - if osp.isfile(path): - path = osp.dirname(path) - - prev, cur = None, osp.abspath(osp.expanduser(path)) - while cur != prev: - if any(osp.exists(osp.join(cur, marker)) for marker in markers): - return cur - prev, cur = cur, osp.split(cur)[0] - return None diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/progressbar.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/progressbar.py deleted file mode 100644 index 0062f670d..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/progressbar.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from collections.abc import Iterable -from multiprocessing import Pool -from shutil import get_terminal_size - -from .timer import Timer - - -class ProgressBar: - """A progress bar which can print the progress.""" - - def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): - self.task_num = task_num - self.bar_width = bar_width - self.completed = 0 - self.file = file - if start: - self.start() - - @property - def terminal_width(self): - width, _ = get_terminal_size() - return width - - def start(self): - if self.task_num > 0: - self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' - 'elapsed: 0s, ETA:') - else: - self.file.write('completed: 0, elapsed: 0s') - self.file.flush() - self.timer = Timer() - - def update(self, num_tasks=1): - assert num_tasks > 0 - self.completed += num_tasks - elapsed = self.timer.since_start() - if elapsed > 0: - fps = self.completed / elapsed - else: - fps = float('inf') - if self.task_num > 0: - percentage = self.completed / float(self.task_num) - eta = int(elapsed * (1 - percentage) / percentage + 0.5) - msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ - f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ - f'ETA: {eta:5}s' - - bar_width = min(self.bar_width, - int(self.terminal_width - len(msg)) + 2, - int(self.terminal_width * 0.6)) - bar_width = max(2, bar_width) - mark_width = int(bar_width * percentage) - bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) - self.file.write(msg.format(bar_chars)) - else: - self.file.write( - f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' - f' {fps:.1f} tasks/s') - self.file.flush() - - -def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): - """Track the progress of tasks execution with a progress bar. - - Tasks are done with a simple for-loop. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - results = [] - for task in tasks: - results.append(func(task, **kwargs)) - prog_bar.update() - prog_bar.file.write('\n') - return results - - -def init_pool(process_num, initializer=None, initargs=None): - if initializer is None: - return Pool(process_num) - elif initargs is None: - return Pool(process_num, initializer) - else: - if not isinstance(initargs, tuple): - raise TypeError('"initargs" must be a tuple') - return Pool(process_num, initializer, initargs) - - -def track_parallel_progress(func, - tasks, - nproc, - initializer=None, - initargs=None, - bar_width=50, - chunksize=1, - skip_first=False, - keep_order=True, - file=sys.stdout): - """Track the progress of parallel task execution with a progress bar. - - The built-in :mod:`multiprocessing` module is used for process pools and - tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - nproc (int): Process (worker) number. - initializer (None or callable): Refer to :class:`multiprocessing.Pool` - for details. - initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for - details. - chunksize (int): Refer to :class:`multiprocessing.Pool` for details. - bar_width (int): Width of progress bar. - skip_first (bool): Whether to skip the first sample for each worker - when estimating fps, since the initialization step may takes - longer. - keep_order (bool): If True, :func:`Pool.imap` is used, otherwise - :func:`Pool.imap_unordered` is used. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - pool = init_pool(nproc, initializer, initargs) - start = not skip_first - task_num -= nproc * chunksize * int(skip_first) - prog_bar = ProgressBar(task_num, bar_width, start, file=file) - results = [] - if keep_order: - gen = pool.imap(func, tasks, chunksize) - else: - gen = pool.imap_unordered(func, tasks, chunksize) - for result in gen: - results.append(result) - if skip_first: - if len(results) < nproc * chunksize: - continue - elif len(results) == nproc * chunksize: - prog_bar.start() - continue - prog_bar.update() - prog_bar.file.write('\n') - pool.close() - pool.join() - return results - - -def track_iter_progress(tasks, bar_width=50, file=sys.stdout): - """Track the progress of tasks iteration or enumeration with a progress - bar. - - Tasks are yielded with a simple for-loop. - - Args: - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Yields: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - for task in tasks: - yield task - prog_bar.update() - prog_bar.file.write('\n') diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/registry.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/registry.py deleted file mode 100644 index a6d92b68b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/registry.py +++ /dev/null @@ -1,337 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import warnings -from functools import partial - -from .misc import deprecated_api_warning, is_seq_of - - -def build_from_cfg(cfg, registry, default_args=None): - """Build a module from config dict when it is a class configuration, or - call a function from config dict when it is a function configuration. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = build_from_cfg(dict(type='Resnet'), MODELS) - >>> # Returns an instantiated object - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = build_from_cfg(dict(type='resnet50'), MODELS) - >>> # Return a result of the calling function - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type) or inspect.isfunction(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - try: - return obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{obj_cls.__name__}: {e}') - - -class Registry: - """A registry to map strings to classes or functions. - - Registered object could be built from registry. Meanwhile, registered - functions could be called from registry. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = MODELS.build(dict(type='ResNet')) - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = MODELS.build(dict(type='resnet50')) - - Please refer to - https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for - advanced usage. - - Args: - name (str): Registry name. - build_func(func, optional): Build function to construct instance from - Registry, func:`build_from_cfg` is used if neither ``parent`` or - ``build_func`` is specified. If ``parent`` is specified and - ``build_func`` is not given, ``build_func`` will be inherited - from ``parent``. Default: None. - parent (Registry, optional): Parent registry. The class registered in - children registry could be built from parent. Default: None. - scope (str, optional): The scope of registry. It is the key to search - for children registry. If not specified, scope will be the name of - the package where class is defined, e.g. mmdet, mmcls, mmseg. - Default: None. - """ - - def __init__(self, name, build_func=None, parent=None, scope=None): - self._name = name - self._module_dict = dict() - self._children = dict() - self._scope = self.infer_scope() if scope is None else scope - - # self.build_func will be set with the following priority: - # 1. build_func - # 2. parent.build_func - # 3. build_from_cfg - if build_func is None: - if parent is not None: - self.build_func = parent.build_func - else: - self.build_func = build_from_cfg - else: - self.build_func = build_func - if parent is not None: - assert isinstance(parent, Registry) - parent._add_children(self) - self.parent = parent - else: - self.parent = None - - def __len__(self): - return len(self._module_dict) - - def __contains__(self, key): - return self.get(key) is not None - - def __repr__(self): - format_str = self.__class__.__name__ + \ - f'(name={self._name}, ' \ - f'items={self._module_dict})' - return format_str - - @staticmethod - def infer_scope(): - """Infer the scope of registry. - - The name of the package where registry is defined will be returned. - - Example: - >>> # in mmdet/models/backbone/resnet.py - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - The scope of ``ResNet`` will be ``mmdet``. - - Returns: - str: The inferred scope name. - """ - # We access the caller using inspect.currentframe() instead of - # inspect.stack() for performance reasons. See details in PR #1844 - frame = inspect.currentframe() - # get the frame where `infer_scope()` is called - infer_scope_caller = frame.f_back.f_back - filename = inspect.getmodule(infer_scope_caller).__name__ - split_filename = filename.split('.') - return split_filename[0] - - @staticmethod - def split_scope_key(key): - """Split scope and key. - - The first scope will be split from key. - - Examples: - >>> Registry.split_scope_key('mmdet.ResNet') - 'mmdet', 'ResNet' - >>> Registry.split_scope_key('ResNet') - None, 'ResNet' - - Return: - tuple[str | None, str]: The former element is the first scope of - the key, which can be ``None``. The latter is the remaining key. - """ - split_index = key.find('.') - if split_index != -1: - return key[:split_index], key[split_index + 1:] - else: - return None, key - - @property - def name(self): - return self._name - - @property - def scope(self): - return self._scope - - @property - def module_dict(self): - return self._module_dict - - @property - def children(self): - return self._children - - def get(self, key): - """Get the registry record. - - Args: - key (str): The class name in string format. - - Returns: - class: The corresponding class. - """ - scope, real_key = self.split_scope_key(key) - if scope is None or scope == self._scope: - # get from self - if real_key in self._module_dict: - return self._module_dict[real_key] - else: - # get from self._children - if scope in self._children: - return self._children[scope].get(real_key) - else: - # goto root - parent = self.parent - while parent.parent is not None: - parent = parent.parent - return parent.get(key) - - def build(self, *args, **kwargs): - return self.build_func(*args, **kwargs, registry=self) - - def _add_children(self, registry): - """Add children for a registry. - - The ``registry`` will be added as children based on its scope. - The parent registry could build objects from children registry. - - Example: - >>> models = Registry('models') - >>> mmdet_models = Registry('models', parent=models) - >>> @mmdet_models.register_module() - >>> class ResNet: - >>> pass - >>> resnet = models.build(dict(type='mmdet.ResNet')) - """ - - assert isinstance(registry, Registry) - assert registry.scope is not None - assert registry.scope not in self.children, \ - f'scope {registry.scope} exists in {self.name} registry' - self.children[registry.scope] = registry - - @deprecated_api_warning(name_dict=dict(module_class='module')) - def _register_module(self, module, module_name=None, force=False): - if not inspect.isclass(module) and not inspect.isfunction(module): - raise TypeError('module must be a class or a function, ' - f'but got {type(module)}') - - if module_name is None: - module_name = module.__name__ - if isinstance(module_name, str): - module_name = [module_name] - for name in module_name: - if not force and name in self._module_dict: - raise KeyError(f'{name} is already registered ' - f'in {self.name}') - self._module_dict[name] = module - - def deprecated_register_module(self, cls=None, force=False): - warnings.warn( - 'The old API of register_module(module, force=False) ' - 'is deprecated and will be removed, please use the new API ' - 'register_module(name=None, force=False, module=None) instead.', - DeprecationWarning) - if cls is None: - return partial(self.deprecated_register_module, force=force) - self._register_module(cls, force=force) - return cls - - def register_module(self, name=None, force=False, module=None): - """Register a module. - - A record will be added to `self._module_dict`, whose key is the class - name or the specified name, and value is the class itself. - It can be used as a decorator or a normal function. - - Example: - >>> backbones = Registry('backbone') - >>> @backbones.register_module() - >>> class ResNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> @backbones.register_module(name='mnet') - >>> class MobileNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> class ResNet: - >>> pass - >>> backbones.register_module(ResNet) - - Args: - name (str | None): The module name to be registered. If not - specified, the class name will be used. - force (bool, optional): Whether to override an existing class with - the same name. Default: False. - module (type): Module class or function to be registered. - """ - if not isinstance(force, bool): - raise TypeError(f'force must be a boolean, but got {type(force)}') - # NOTE: This is a walkaround to be compatible with the old api, - # while it may introduce unexpected bugs. - if isinstance(name, type): - return self.deprecated_register_module(name, force=force) - - # raise the error ahead of time - if not (name is None or isinstance(name, str) or is_seq_of(name, str)): - raise TypeError( - 'name must be either of None, an instance of str or a sequence' - f' of str, but got {type(name)}') - - # use it as a normal method: x.register_module(module=SomeClass) - if module is not None: - self._register_module(module=module, module_name=name, force=force) - return module - - # use it as a decorator: @x.register_module() - def _register(module): - self._register_module(module=module, module_name=name, force=force) - return module - - return _register diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/seed.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/seed.py deleted file mode 100644 index 003f92367..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/seed.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import random - -import numpy as np -import torch - - -def worker_init_fn(worker_id: int, num_workers: int, rank: int, seed: int): - """Function to initialize each worker. - - The seed of each worker equals to - ``num_worker * rank + worker_id + user_seed``. - - Args: - worker_id (int): Id for each worker. - num_workers (int): Number of workers. - rank (int): Rank in distributed training. - seed (int): Random seed. - """ - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/testing.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/testing.py deleted file mode 100644 index 7b64e8fae..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/testing.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (c) Open-MMLab. -import sys -from collections.abc import Iterable -from runpy import run_path -from shlex import split -from typing import Any, Dict, List -from unittest.mock import patch - - -def check_python_script(cmd): - """Run the python cmd script with `__main__`. The difference between - `os.system` is that, this function exectues code in the current process, so - that it can be tracked by coverage tools. Currently it supports two forms: - - - ./tests/data/scripts/hello.py zz - - python tests/data/scripts/hello.py zz - """ - args = split(cmd) - if args[0] == 'python': - args = args[1:] - with patch.object(sys, 'argv', args): - run_path(args[0], run_name='__main__') - - -def _any(judge_result): - """Since built-in ``any`` works only when the element of iterable is not - iterable, implement the function.""" - if not isinstance(judge_result, Iterable): - return judge_result - - try: - for element in judge_result: - if _any(element): - return True - except TypeError: - # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) - if judge_result: - return True - return False - - -def assert_dict_contains_subset(dict_obj: Dict[Any, Any], - expected_subset: Dict[Any, Any]) -> bool: - """Check if the dict_obj contains the expected_subset. - - Args: - dict_obj (Dict[Any, Any]): Dict object to be checked. - expected_subset (Dict[Any, Any]): Subset expected to be contained in - dict_obj. - - Returns: - bool: Whether the dict_obj contains the expected_subset. - """ - - for key, value in expected_subset.items(): - if key not in dict_obj.keys() or _any(dict_obj[key] != value): - return False - return True - - -def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: - """Check if attribute of class object is correct. - - Args: - obj (object): Class object to be checked. - expected_attrs (Dict[str, Any]): Dict of the expected attrs. - - Returns: - bool: Whether the attribute of class object is correct. - """ - for attr, value in expected_attrs.items(): - if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): - return False - return True - - -def assert_dict_has_keys(obj: Dict[str, Any], - expected_keys: List[str]) -> bool: - """Check if the obj has all the expected_keys. - - Args: - obj (Dict[str, Any]): Object to be checked. - expected_keys (List[str]): Keys expected to contained in the keys of - the obj. - - Returns: - bool: Whether the obj has the expected keys. - """ - return set(expected_keys).issubset(set(obj.keys())) - - -def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: - """Check if target_keys is equal to result_keys. - - Args: - result_keys (List[str]): Result keys to be checked. - target_keys (List[str]): Target keys to be checked. - - Returns: - bool: Whether target_keys is equal to result_keys. - """ - return set(result_keys) == set(target_keys) - - -def assert_is_norm_layer(module) -> bool: - """Check if the module is a norm layer. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the module is a norm layer. - """ - from torch.nn import GroupNorm, LayerNorm - - from .parrots_wrapper import _BatchNorm, _InstanceNorm - norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) - return isinstance(module, norm_layer_candidates) - - -def assert_params_all_zeros(module) -> bool: - """Check if the parameters of the module is all zeros. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the parameters of the module is all zeros. - """ - weight_data = module.weight.data - is_weight_zero = weight_data.allclose( - weight_data.new_zeros(weight_data.size())) - - if hasattr(module, 'bias') and module.bias is not None: - bias_data = module.bias.data - is_bias_zero = bias_data.allclose( - bias_data.new_zeros(bias_data.size())) - else: - is_bias_zero = True - - return is_weight_zero and is_bias_zero diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/timer.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/timer.py deleted file mode 100644 index 02e96e537..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/timer.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from time import time - - -class TimerError(Exception): - - def __init__(self, message): - self.message = message - super(TimerError, self).__init__(message) - - -class Timer: - """A flexible Timer class. - - Examples: - >>> import time - >>> import mmcv - >>> with mmcv.Timer(): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - 1.000 - >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - it takes 1.0 seconds - >>> timer = mmcv.Timer() - >>> time.sleep(0.5) - >>> print(timer.since_start()) - 0.500 - >>> time.sleep(0.5) - >>> print(timer.since_last_check()) - 0.500 - >>> print(timer.since_start()) - 1.000 - """ - - def __init__(self, start=True, print_tmpl=None): - self._is_running = False - self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' - if start: - self.start() - - @property - def is_running(self): - """bool: indicate whether the timer is running""" - return self._is_running - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - print(self.print_tmpl.format(self.since_last_check())) - self._is_running = False - - def start(self): - """Start the timer.""" - if not self._is_running: - self._t_start = time() - self._is_running = True - self._t_last = time() - - def since_start(self): - """Total time since the timer is started. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - self._t_last = time() - return self._t_last - self._t_start - - def since_last_check(self): - """Time since the last checking. - - Either :func:`since_start` or :func:`since_last_check` is a checking - operation. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - dur = time() - self._t_last - self._t_last = time() - return dur - - -_g_timers = {} # global timers - - -def check_time(timer_id): - """Add check points in a single line. - - This method is suitable for running a task on a list of items. A timer will - be registered when the method is called for the first time. - - Examples: - >>> import time - >>> import mmcv - >>> for i in range(1, 6): - >>> # simulate a code block - >>> time.sleep(i) - >>> mmcv.check_time('task1') - 2.000 - 3.000 - 4.000 - 5.000 - - Args: - str: Timer identifier. - """ - if timer_id not in _g_timers: - _g_timers[timer_id] = Timer() - return 0 - else: - return _g_timers[timer_id].since_last_check() diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/trace.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/trace.py deleted file mode 100644 index 45423bd05..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/trace.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch - -from mmcv.utils import digit_version - - -def is_jit_tracing() -> bool: - if (torch.__version__ != 'parrots' - and digit_version(torch.__version__) >= digit_version('1.6.0')): - on_trace = torch.jit.is_tracing() - # In PyTorch 1.6, torch.jit.is_tracing has a bug. - # Refers to https://github.com/pytorch/pytorch/issues/42448 - if isinstance(on_trace, bool): - return on_trace - else: - return torch._C._is_tracing() - else: - warnings.warn( - 'torch.jit.is_tracing is only supported after v1.6.0. ' - 'Therefore is_tracing returns False automatically. Please ' - 'set on_trace manually if you are using trace.', UserWarning) - return False diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/version_utils.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/version_utils.py deleted file mode 100644 index 963c45a2e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/utils/version_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import subprocess -import warnings - -from packaging.version import parse - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - assert 'parrots' not in version_str - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - -def get_git_hash(fallback='unknown', digits=None): - """Get the git hash of the current repo. - - Args: - fallback (str, optional): The fallback string when git hash is - unavailable. Defaults to 'unknown'. - digits (int, optional): kept digits of the hash. Defaults to None, - meaning all digits are kept. - - Returns: - str: Git commit hash. - """ - - if digits is not None and not isinstance(digits, int): - raise TypeError('digits must be None or an integer') - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - sha = out.strip().decode('ascii') - if digits is not None: - sha = sha[:digits] - except OSError: - sha = fallback - - return sha diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmcv/version.py b/cv/semantic_segmentation/apcnet/pytorch/mmcv/version.py deleted file mode 100644 index a97ffc2dd..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmcv/version.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -__version__ = '1.5.0' - - -def parse_version_info(version_str: str, length: int = 4) -> tuple: - """Parse a version string into a tuple. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int | str]: The version info, e.g., "1.3.0" is parsed into - (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into - (2, 0, 0, 0, 'rc', 1) (when length is set to 4). - """ - from packaging.version import parse - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - release.extend(list(version.pre)) - elif version.is_postrelease: - release.extend(list(version.post)) - else: - release.extend([0, 0]) - return tuple(release) - - -version_info = tuple(int(x) for x in __version__.split('.')[:3]) - -__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/__init__.py deleted file mode 100644 index 360abfc85..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv -from packaging.version import parse - -from .version import __version__, version_info - -MMCV_MIN = '1.3.13' -MMCV_MAX = '1.6.0' - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -mmcv_min_version = digit_version(MMCV_MIN) -mmcv_max_version = digit_version(MMCV_MAX) -mmcv_version = digit_version(mmcv.__version__) - - -assert (mmcv_min_version <= mmcv_version <= mmcv_max_version), \ - f'MMCV=={mmcv.__version__} is used but incompatible. ' \ - f'Please install mmcv>={mmcv_min_version}, <={mmcv_max_version}.' - -__all__ = ['__version__', 'version_info', 'digit_version'] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/__init__.py deleted file mode 100644 index c68818053..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .inference import inference_segmentor, init_segmentor, show_result_pyplot -from .test import multi_gpu_test, single_gpu_test -from .train import (get_root_logger, init_random_seed, set_random_seed, - train_segmentor) - -__all__ = [ - 'get_root_logger', 'set_random_seed', 'train_segmentor', 'init_segmentor', - 'inference_segmentor', 'multi_gpu_test', 'single_gpu_test', - 'show_result_pyplot', 'init_random_seed' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/inference.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/inference.py deleted file mode 100644 index 906943804..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/inference.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import matplotlib.pyplot as plt -import mmcv -import torch -from mmcv.parallel import collate, scatter -from mmcv.runner import load_checkpoint - -from mmseg.datasets.pipelines import Compose -from mmseg.models import build_segmentor - - -def init_segmentor(config, checkpoint=None, device='cuda:0'): - """Initialize a segmentor from config file. - - Args: - config (str or :obj:`mmcv.Config`): Config file path or the config - object. - checkpoint (str, optional): Checkpoint path. If left as None, the model - will not load any weights. - device (str, optional) CPU/CUDA device option. Default 'cuda:0'. - Use 'cpu' for loading model on CPU. - Returns: - nn.Module: The constructed segmentor. - """ - if isinstance(config, str): - config = mmcv.Config.fromfile(config) - elif not isinstance(config, mmcv.Config): - raise TypeError('config must be a filename or Config object, ' - 'but got {}'.format(type(config))) - config.model.pretrained = None - config.model.train_cfg = None - model = build_segmentor(config.model, test_cfg=config.get('test_cfg')) - if checkpoint is not None: - checkpoint = load_checkpoint(model, checkpoint, map_location='cpu') - model.CLASSES = checkpoint['meta']['CLASSES'] - model.PALETTE = checkpoint['meta']['PALETTE'] - model.cfg = config # save the config in the model for convenience - model.to(device) - model.eval() - return model - - -class LoadImage: - """A simple pipeline to load image.""" - - def __call__(self, results): - """Call function to load images into results. - - Args: - results (dict): A result dict contains the file name - of the image to be read. - - Returns: - dict: ``results`` will be returned containing loaded image. - """ - - if isinstance(results['img'], str): - results['filename'] = results['img'] - results['ori_filename'] = results['img'] - else: - results['filename'] = None - results['ori_filename'] = None - img = mmcv.imread(results['img']) - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - return results - - -def inference_segmentor(model, img): - """Inference image(s) with the segmentor. - - Args: - model (nn.Module): The loaded segmentor. - imgs (str/ndarray or list[str/ndarray]): Either image files or loaded - images. - - Returns: - (list[Tensor]): The segmentation result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # build the data pipeline - test_pipeline = [LoadImage()] + cfg.data.test.pipeline[1:] - test_pipeline = Compose(test_pipeline) - # prepare data - data = dict(img=img) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if next(model.parameters()).is_cuda: - # scatter to specified GPU - data = scatter(data, [device])[0] - else: - data['img_metas'] = [i.data[0] for i in data['img_metas']] - - # forward the model - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - return result - - -def show_result_pyplot(model, - img, - result, - palette=None, - fig_size=(15, 10), - opacity=0.5, - title='', - block=True): - """Visualize the segmentation results on the image. - - Args: - model (nn.Module): The loaded segmentor. - img (str or np.ndarray): Image filename or loaded image. - result (list): The segmentation result. - palette (list[list[int]]] | None): The palette of segmentation - map. If None is given, random palette will be generated. - Default: None - fig_size (tuple): Figure size of the pyplot figure. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - title (str): The title of pyplot figure. - Default is ''. - block (bool): Whether to block the pyplot figure. - Default is True. - """ - if hasattr(model, 'module'): - model = model.module - img = model.show_result( - img, result, palette=palette, show=False, opacity=opacity) - plt.figure(figsize=fig_size) - plt.imshow(mmcv.bgr2rgb(img)) - plt.title(title) - plt.tight_layout() - plt.show(block=block) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/test.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/test.py deleted file mode 100644 index cc4fcc979..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/test.py +++ /dev/null @@ -1,233 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import tempfile -import warnings - -import mmcv -import numpy as np -import torch -from mmcv.engine import collect_results_cpu, collect_results_gpu -from mmcv.image import tensor2imgs -from mmcv.runner import get_dist_info - - -def np2tmp(array, temp_file_name=None, tmpdir=None): - """Save ndarray to local numpy file. - - Args: - array (ndarray): Ndarray to save. - temp_file_name (str): Numpy file name. If 'temp_file_name=None', this - function will generate a file name with tempfile.NamedTemporaryFile - to save ndarray. Default: None. - tmpdir (str): Temporary directory to save Ndarray files. Default: None. - Returns: - str: The numpy file name. - """ - - if temp_file_name is None: - temp_file_name = tempfile.NamedTemporaryFile( - suffix='.npy', delete=False, dir=tmpdir).name - np.save(temp_file_name, array) - return temp_file_name - - -def single_gpu_test(model, - data_loader, - show=False, - out_dir=None, - efficient_test=False, - opacity=0.5, - pre_eval=False, - format_only=False, - format_args={}): - """Test with single GPU by progressive mode. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - show (bool): Whether show results during inference. Default: False. - out_dir (str, optional): If specified, the results will be dumped into - the directory to save output results. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - loader_indices = data_loader.batch_sampler - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - - if show or out_dir: - img_tensor = data['img'][0] - img_metas = data['img_metas'][0].data[0] - imgs = tensor2imgs(img_tensor, **img_metas[0]['img_norm_cfg']) - assert len(imgs) == len(img_metas) - - for img, img_meta in zip(imgs, img_metas): - h, w, _ = img_meta['img_shape'] - img_show = img[:h, :w, :] - - ori_h, ori_w = img_meta['ori_shape'][:-1] - img_show = mmcv.imresize(img_show, (ori_w, ori_h)) - - if out_dir: - out_file = osp.join(out_dir, img_meta['ori_filename']) - else: - out_file = None - - model.module.show_result( - img_show, - result, - palette=dataset.PALETTE, - show=show, - out_file=out_file, - opacity=opacity) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - results.extend(result) - else: - results.extend(result) - - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - - return results - - -def multi_gpu_test(model, - data_loader, - tmpdir=None, - gpu_collect=False, - efficient_test=False, - pre_eval=False, - format_only=False, - format_args={}): - """Test model with multiple gpus by progressive mode. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' - it encodes results to gpu tensors and use gpu communication for results - collection. On cpu mode it saves the results on different gpus to 'tmpdir' - and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. The same path is used for efficient - test. Default: None. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - - # batch_sampler based on DistributedSampler, the indices only point to data - # samples of related machine. - loader_indices = data_loader.batch_sampler - - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - - results.extend(result) - - if rank == 0: - batch_size = len(result) * world_size - for _ in range(batch_size): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/train.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/train.py deleted file mode 100644 index 9be063785..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/apis/train.py +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import random -import warnings - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (HOOKS, DistSamplerSeedHook, EpochBasedRunner, - build_runner, get_dist_info) -from mmcv.utils import build_from_cfg - -from mmseg import digit_version -from mmseg.core import DistEvalHook, EvalHook, build_optimizer -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.utils import find_latest_checkpoint, get_root_logger - - -def init_random_seed(seed=None, device='cuda'): - """Initialize random seed. - - If the seed is not set, the seed will be automatically randomized, - and then broadcast to all processes to prevent some potential bugs. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is not None: - return seed - - # Make sure all ranks share the same random seed to prevent - # some potential bugs. Please refer to - # https://github.com/open-mmlab/mmdetection/issues/6339 - rank, world_size = get_dist_info() - seed = np.random.randint(2**31) - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() - - -def set_random_seed(seed, deterministic=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - """ - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - - -def train_segmentor(model, - dataset, - cfg, - distributed=False, - validate=False, - timestamp=None, - meta=None): - """Launch segmentor training.""" - logger = get_root_logger(cfg.log_level) - - # prepare data loaders - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - # The default loader config - loader_cfg = dict( - # cfg.gpus will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - seed=cfg.seed, - drop_last=True) - # The overall dataloader settings - loader_cfg.update({ - k: v - for k, v in cfg.data.items() if k not in [ - 'train', 'val', 'test', 'train_dataloader', 'val_dataloader', - 'test_dataloader' - ] - }) - - # The specific dataloader settings - train_loader_cfg = {**loader_cfg, **cfg.data.get('train_dataloader', {})} - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus - if distributed: - find_unused_parameters = cfg.get('find_unused_parameters', False) - # Sets the `find_unused_parameters` parameter in - # torch.nn.parallel.DistributedDataParallel - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - else: - if not torch.cuda.is_available(): - assert digit_version(mmcv.__version__) >= digit_version('1.4.4'), \ - 'Please use MMCV >= 1.4.4 for CPU training!' - model = MMDataParallel(model, device_ids=cfg.gpu_ids) - # build runner - optimizer = build_optimizer(model, cfg.optimizer) - - if cfg.get('runner') is None: - cfg.runner = {'type': 'IterBasedRunner', 'max_iters': cfg.total_iters} - warnings.warn( - 'config is now expected to have a `runner` section, ' - 'please set `runner` in your config.', UserWarning) - - runner = build_runner( - cfg.runner, - default_args=dict( - model=model, - batch_processor=None, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta)) - - runner.cfg = cfg - - # register hooks - runner.register_training_hooks(cfg.lr_config, cfg.optimizer_config, - cfg.checkpoint_config, cfg.log_config, - cfg.get('momentum_config', None)) - if distributed: - # when distributed training by epoch, using`DistSamplerSeedHook` to set - # the different seed to distributed sampler for each epoch, it will - # shuffle dataset at each epoch and avoid overfitting. - if isinstance(runner, EpochBasedRunner): - runner.register_hook(DistSamplerSeedHook()) - - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register eval hooks - if validate: - val_dataset = build_dataset(cfg.data.val, dict(test_mode=True)) - # The specific dataloader settings - val_loader_cfg = { - **loader_cfg, - 'samples_per_gpu': 1, - 'shuffle': False, # Not shuffle by default - **cfg.data.get('val_dataloader', {}), - } - val_dataloader = build_dataloader(val_dataset, **val_loader_cfg) - eval_cfg = cfg.get('evaluation', {}) - eval_cfg['by_epoch'] = cfg.runner['type'] != 'IterBasedRunner' - eval_hook = DistEvalHook if distributed else EvalHook - # In this PR (https://github.com/open-mmlab/mmcv/pull/1193), the - # priority of IterTimerHook has been modified from 'NORMAL' to 'LOW'. - runner.register_hook( - eval_hook(val_dataloader, **eval_cfg), priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from is None and cfg.get('auto_resume'): - resume_from = find_latest_checkpoint(cfg.work_dir) - if resume_from is not None: - cfg.resume_from = resume_from - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/__init__.py deleted file mode 100644 index 1a077d2f1..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, build_optimizer, - build_optimizer_constructor) -from .evaluation import * # noqa: F401, F403 -from .optimizers import * # noqa: F401, F403 -from .seg import * # noqa: F401, F403 -from .utils import * # noqa: F401, F403 - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/builder.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/builder.py deleted file mode 100644 index 406dd9b4b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/builder.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from mmcv.runner.optimizer import OPTIMIZER_BUILDERS as MMCV_OPTIMIZER_BUILDERS -from mmcv.utils import Registry, build_from_cfg - -OPTIMIZER_BUILDERS = Registry( - 'optimizer builder', parent=MMCV_OPTIMIZER_BUILDERS) - - -def build_optimizer_constructor(cfg): - constructor_type = cfg.get('type') - if constructor_type in OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - elif constructor_type in MMCV_OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, MMCV_OPTIMIZER_BUILDERS) - else: - raise KeyError(f'{constructor_type} is not registered ' - 'in the optimizer builder registry.') - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/__init__.py deleted file mode 100644 index 3d16d17e5..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .class_names import get_classes, get_palette -from .eval_hooks import DistEvalHook, EvalHook -from .metrics import (eval_metrics, intersect_and_union, mean_dice, - mean_fscore, mean_iou, pre_eval_to_metrics) - -__all__ = [ - 'EvalHook', 'DistEvalHook', 'mean_dice', 'mean_iou', 'mean_fscore', - 'eval_metrics', 'get_classes', 'get_palette', 'pre_eval_to_metrics', - 'intersect_and_union' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/class_names.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/class_names.py deleted file mode 100644 index e3bff6231..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/class_names.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - - -def cityscapes_classes(): - """Cityscapes class names for external use.""" - return [ - 'road', 'sidewalk', 'building', 'wall', 'fence', 'pole', - 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle' - ] - - -def ade_classes(): - """ADE20K class names for external use.""" - return [ - 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', - 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', - 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', - 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', - 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', - 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', - 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', - 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', - 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', - 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', - 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', - 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', - 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', - 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', - 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', - 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', - 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', - 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', - 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', - 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', - 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', - 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', - 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', - 'clock', 'flag' - ] - - -def voc_classes(): - """Pascal VOC class names for external use.""" - return [ - 'background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', - 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', - 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', - 'tvmonitor' - ] - - -def cocostuff_classes(): - """CocoStuff class names for external use.""" - return [ - 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', - 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', - 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', - 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', - 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', - 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', - 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', - 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', - 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', - 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', - 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', - 'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner', - 'blanket', 'branch', 'bridge', 'building-other', 'bush', 'cabinet', - 'cage', 'cardboard', 'carpet', 'ceiling-other', 'ceiling-tile', - 'cloth', 'clothes', 'clouds', 'counter', 'cupboard', 'curtain', - 'desk-stuff', 'dirt', 'door-stuff', 'fence', 'floor-marble', - 'floor-other', 'floor-stone', 'floor-tile', 'floor-wood', 'flower', - 'fog', 'food-other', 'fruit', 'furniture-other', 'grass', 'gravel', - 'ground-other', 'hill', 'house', 'leaves', 'light', 'mat', 'metal', - 'mirror-stuff', 'moss', 'mountain', 'mud', 'napkin', 'net', 'paper', - 'pavement', 'pillow', 'plant-other', 'plastic', 'platform', - 'playingfield', 'railing', 'railroad', 'river', 'road', 'rock', 'roof', - 'rug', 'salad', 'sand', 'sea', 'shelf', 'sky-other', 'skyscraper', - 'snow', 'solid-other', 'stairs', 'stone', 'straw', 'structural-other', - 'table', 'tent', 'textile-other', 'towel', 'tree', 'vegetable', - 'wall-brick', 'wall-concrete', 'wall-other', 'wall-panel', - 'wall-stone', 'wall-tile', 'wall-wood', 'water-other', 'waterdrops', - 'window-blind', 'window-other', 'wood' - ] - - -def loveda_classes(): - """LoveDA class names for external use.""" - return [ - 'background', 'building', 'road', 'water', 'barren', 'forest', - 'agricultural' - ] - - -def potsdam_classes(): - """Potsdam class names for external use.""" - return [ - 'impervious_surface', 'building', 'low_vegetation', 'tree', 'car', - 'clutter' - ] - - -def vaihingen_classes(): - """Vaihingen class names for external use.""" - return [ - 'impervious_surface', 'building', 'low_vegetation', 'tree', 'car', - 'clutter' - ] - - -def isaid_classes(): - """iSAID class names for external use.""" - return [ - 'background', 'ship', 'store_tank', 'baseball_diamond', 'tennis_court', - 'basketball_court', 'Ground_Track_Field', 'Bridge', 'Large_Vehicle', - 'Small_Vehicle', 'Helicopter', 'Swimming_pool', 'Roundabout', - 'Soccer_ball_field', 'plane', 'Harbor' - ] - - -def stare_classes(): - """stare class names for external use.""" - return ['background', 'vessel'] - - -def cityscapes_palette(): - """Cityscapes palette for external use.""" - return [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], - [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], - [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], - [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], [0, 80, 100], - [0, 0, 230], [119, 11, 32]] - - -def ade_palette(): - """ADE20K palette for external use.""" - return [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], - [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], - [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], - [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], - [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], - [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], - [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], - [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], - [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], - [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], - [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], - [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], - [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], - [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], - [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], - [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], - [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], - [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], - [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], - [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], - [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], - [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], - [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], - [102, 255, 0], [92, 0, 255]] - - -def voc_palette(): - """Pascal VOC palette for external use.""" - return [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], - [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], - [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], - [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], - [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] - - -def cocostuff_palette(): - """CocoStuff palette for external use.""" - return [[0, 192, 64], [0, 192, 64], [0, 64, 96], [128, 192, 192], - [0, 64, 64], [0, 192, 224], [0, 192, 192], [128, 192, 64], - [0, 192, 96], [128, 192, 64], [128, 32, 192], [0, 0, 224], - [0, 0, 64], [0, 160, 192], [128, 0, 96], [128, 0, 192], - [0, 32, 192], [128, 128, 224], [0, 0, 192], [128, 160, 192], - [128, 128, 0], [128, 0, 32], [128, 32, 0], [128, 0, 128], - [64, 128, 32], [0, 160, 0], [0, 0, 0], [192, 128, 160], [0, 32, 0], - [0, 128, 128], [64, 128, 160], [128, 160, 0], [0, 128, 0], - [192, 128, 32], [128, 96, 128], [0, 0, 128], [64, 0, 32], - [0, 224, 128], [128, 0, 0], [192, 0, 160], [0, 96, 128], - [128, 128, 128], [64, 0, 160], [128, 224, 128], [128, 128, 64], - [192, 0, 32], [128, 96, 0], [128, 0, 192], [0, 128, 32], - [64, 224, 0], [0, 0, 64], [128, 128, 160], [64, 96, 0], - [0, 128, 192], [0, 128, 160], [192, 224, 0], [0, 128, 64], - [128, 128, 32], [192, 32, 128], [0, 64, 192], [0, 0, 32], - [64, 160, 128], [128, 64, 64], [128, 0, 160], [64, 32, 128], - [128, 192, 192], [0, 0, 160], [192, 160, 128], [128, 192, 0], - [128, 0, 96], [192, 32, 0], [128, 64, 128], [64, 128, 96], - [64, 160, 0], [0, 64, 0], [192, 128, 224], [64, 32, 0], - [0, 192, 128], [64, 128, 224], [192, 160, 0], [0, 192, 0], - [192, 128, 96], [192, 96, 128], [0, 64, 128], [64, 0, 96], - [64, 224, 128], [128, 64, 0], [192, 0, 224], [64, 96, 128], - [128, 192, 128], [64, 0, 224], [192, 224, 128], [128, 192, 64], - [192, 0, 96], [192, 96, 0], [128, 64, 192], [0, 128, 96], - [0, 224, 0], [64, 64, 64], [128, 128, 224], [0, 96, 0], - [64, 192, 192], [0, 128, 224], [128, 224, 0], [64, 192, 64], - [128, 128, 96], [128, 32, 128], [64, 0, 192], [0, 64, 96], - [0, 160, 128], [192, 0, 64], [128, 64, 224], [0, 32, 128], - [192, 128, 192], [0, 64, 224], [128, 160, 128], [192, 128, 0], - [128, 64, 32], [128, 32, 64], [192, 0, 128], [64, 192, 32], - [0, 160, 64], [64, 0, 0], [192, 192, 160], [0, 32, 64], - [64, 128, 128], [64, 192, 160], [128, 160, 64], [64, 128, 0], - [192, 192, 32], [128, 96, 192], [64, 0, 128], [64, 64, 32], - [0, 224, 192], [192, 0, 0], [192, 64, 160], [0, 96, 192], - [192, 128, 128], [64, 64, 160], [128, 224, 192], [192, 128, 64], - [192, 64, 32], [128, 96, 64], [192, 0, 192], [0, 192, 32], - [64, 224, 64], [64, 0, 64], [128, 192, 160], [64, 96, 64], - [64, 128, 192], [0, 192, 160], [192, 224, 64], [64, 128, 64], - [128, 192, 32], [192, 32, 192], [64, 64, 192], [0, 64, 32], - [64, 160, 192], [192, 64, 64], [128, 64, 160], [64, 32, 192], - [192, 192, 192], [0, 64, 160], [192, 160, 192], [192, 192, 0], - [128, 64, 96], [192, 32, 64], [192, 64, 128], [64, 192, 96], - [64, 160, 64], [64, 64, 0]] - - -def loveda_palette(): - """LoveDA palette for external use.""" - return [[255, 255, 255], [255, 0, 0], [255, 255, 0], [0, 0, 255], - [159, 129, 183], [0, 255, 0], [255, 195, 128]] - - -def potsdam_palette(): - """Potsdam palette for external use.""" - return [[255, 255, 255], [0, 0, 255], [0, 255, 255], [0, 255, 0], - [255, 255, 0], [255, 0, 0]] - - -def vaihingen_palette(): - """Vaihingen palette for external use.""" - return [[255, 255, 255], [0, 0, 255], [0, 255, 255], [0, 255, 0], - [255, 255, 0], [255, 0, 0]] - - -def isaid_palette(): - """iSAID palette for external use.""" - return [[0, 0, 0], [0, 0, 63], [0, 63, 63], [0, 63, 0], [0, 63, 127], - [0, 63, 191], [0, 63, 255], [0, 127, 63], [0, 127, - 127], [0, 0, 127], - [0, 0, 191], [0, 0, 255], [0, 191, 127], [0, 127, 191], - [0, 127, 255], [0, 100, 155]] - - -def stare_palette(): - """STARE palette for external use.""" - return [[120, 120, 120], [6, 230, 230]] - - -dataset_aliases = { - 'cityscapes': ['cityscapes'], - 'ade': ['ade', 'ade20k'], - 'voc': ['voc', 'pascal_voc', 'voc12', 'voc12aug'], - 'loveda': ['loveda'], - 'potsdam': ['potsdam'], - 'vaihingen': ['vaihingen'], - 'cocostuff': [ - 'cocostuff', 'cocostuff10k', 'cocostuff164k', 'coco-stuff', - 'coco-stuff10k', 'coco-stuff164k', 'coco_stuff', 'coco_stuff10k', - 'coco_stuff164k' - ], - 'isaid': ['isaid', 'iSAID'], - 'stare': ['stare', 'STARE'] -} - - -def get_classes(dataset): - """Get class names of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_classes()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels - - -def get_palette(dataset): - """Get class palette (RGB) of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_palette()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/eval_hooks.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/eval_hooks.py deleted file mode 100644 index 952db3b0b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/eval_hooks.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -import torch.distributed as dist -from mmcv.runner import DistEvalHook as _DistEvalHook -from mmcv.runner import EvalHook as _EvalHook -from torch.nn.modules.batchnorm import _BatchNorm - - -class EvalHook(_EvalHook): - """Single GPU EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``single_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - if not self._should_evaluate(runner): - return - - from mmseg.apis import single_gpu_test - results = single_gpu_test( - runner.model, self.dataloader, show=False, pre_eval=self.pre_eval) - runner.log_buffer.clear() - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - if self.save_best: - self._save_ckpt(runner, key_score) - - -class DistEvalHook(_DistEvalHook): - """Distributed EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``multi_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - if not self._should_evaluate(runner): - return - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - from mmseg.apis import multi_gpu_test - results = multi_gpu_test( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect, - pre_eval=self.pre_eval) - - runner.log_buffer.clear() - - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - - if self.save_best: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/metrics.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/metrics.py deleted file mode 100644 index a1c0908e1..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/evaluation/metrics.py +++ /dev/null @@ -1,395 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import mmcv -import numpy as np -import torch - - -def f_score(precision, recall, beta=1): - """calculate the f-score value. - - Args: - precision (float | torch.Tensor): The precision value. - recall (float | torch.Tensor): The recall value. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - Returns: - [torch.tensor]: The f-score value. - """ - score = (1 + beta**2) * (precision * recall) / ( - (beta**2 * precision) + recall) - return score - - -def intersect_and_union(pred_label, - label, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate intersection and Union. - - Args: - pred_label (ndarray | str): Prediction segmentation map - or predict result filename. - label (ndarray | str): Ground truth segmentation map - or label filename. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. The parameter will - work only when label is str. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. The parameter will - work only when label is str. Default: False. - - Returns: - torch.Tensor: The intersection of prediction and ground truth - histogram on all classes. - torch.Tensor: The union of prediction and ground truth histogram on - all classes. - torch.Tensor: The prediction histogram on all classes. - torch.Tensor: The ground truth histogram on all classes. - """ - - if isinstance(pred_label, str): - pred_label = torch.from_numpy(np.load(pred_label)) - else: - pred_label = torch.from_numpy((pred_label)) - - if isinstance(label, str): - label = torch.from_numpy( - mmcv.imread(label, flag='unchanged', backend='pillow')) - else: - label = torch.from_numpy(label) - - if label_map is not None: - for old_id, new_id in label_map.items(): - label[label == old_id] = new_id - if reduce_zero_label: - label[label == 0] = 255 - label = label - 1 - label[label == 254] = 255 - - mask = (label != ignore_index) - pred_label = pred_label[mask] - label = label[mask] - - intersect = pred_label[pred_label == label] - area_intersect = torch.histc( - intersect.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_pred_label = torch.histc( - pred_label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_label = torch.histc( - label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_union = area_pred_label + area_label - area_intersect - return area_intersect, area_union, area_pred_label, area_label - - -def total_intersect_and_union(results, - gt_seg_maps, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate Total Intersection and Union. - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - ndarray: The intersection of prediction and ground truth histogram - on all classes. - ndarray: The union of prediction and ground truth histogram on all - classes. - ndarray: The prediction histogram on all classes. - ndarray: The ground truth histogram on all classes. - """ - total_area_intersect = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_union = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_pred_label = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_label = torch.zeros((num_classes, ), dtype=torch.float64) - for result, gt_seg_map in zip(results, gt_seg_maps): - area_intersect, area_union, area_pred_label, area_label = \ - intersect_and_union( - result, gt_seg_map, num_classes, ignore_index, - label_map, reduce_zero_label) - total_area_intersect += area_intersect - total_area_union += area_union - total_area_pred_label += area_pred_label - total_area_label += area_label - return total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label - - -def mean_iou(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category IoU, shape (num_classes, ). - """ - iou_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mIoU'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return iou_result - - -def mean_dice(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Dice (mDice) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category dice, shape (num_classes, ). - """ - - dice_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mDice'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return dice_result - - -def mean_fscore(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category recall, shape (num_classes, ). - ndarray: Per category precision, shape (num_classes, ). - ndarray: Per category f-score, shape (num_classes, ). - """ - fscore_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mFscore'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label, - beta=beta) - return fscore_result - - -def eval_metrics(results, - gt_seg_maps, - num_classes, - ignore_index, - metrics=['mIoU'], - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate evaluation metrics - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label = total_intersect_and_union( - results, gt_seg_maps, num_classes, ignore_index, label_map, - reduce_zero_label) - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def pre_eval_to_metrics(pre_eval_results, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Convert pre-eval results to metrics. - - Args: - pre_eval_results (list[tuple[torch.Tensor]]): per image eval results - for computing evaluation metric - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - # convert list of tuples to tuple of lists, e.g. - # [(A_1, B_1, C_1, D_1), ..., (A_n, B_n, C_n, D_n)] to - # ([A_1, ..., A_n], ..., [D_1, ..., D_n]) - pre_eval_results = tuple(zip(*pre_eval_results)) - assert len(pre_eval_results) == 4 - - total_area_intersect = sum(pre_eval_results[0]) - total_area_union = sum(pre_eval_results[1]) - total_area_pred_label = sum(pre_eval_results[2]) - total_area_label = sum(pre_eval_results[3]) - - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def total_area_to_metrics(total_area_intersect, - total_area_union, - total_area_pred_label, - total_area_label, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Calculate evaluation metrics - Args: - total_area_intersect (ndarray): The intersection of prediction and - ground truth histogram on all classes. - total_area_union (ndarray): The union of prediction and ground truth - histogram on all classes. - total_area_pred_label (ndarray): The prediction histogram on all - classes. - total_area_label (ndarray): The ground truth histogram on all classes. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - if isinstance(metrics, str): - metrics = [metrics] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metrics).issubset(set(allowed_metrics)): - raise KeyError('metrics {} is not supported'.format(metrics)) - - all_acc = total_area_intersect.sum() / total_area_label.sum() - ret_metrics = OrderedDict({'aAcc': all_acc}) - for metric in metrics: - if metric == 'mIoU': - iou = total_area_intersect / total_area_union - acc = total_area_intersect / total_area_label - ret_metrics['IoU'] = iou - ret_metrics['Acc'] = acc - elif metric == 'mDice': - dice = 2 * total_area_intersect / ( - total_area_pred_label + total_area_label) - acc = total_area_intersect / total_area_label - ret_metrics['Dice'] = dice - ret_metrics['Acc'] = acc - elif metric == 'mFscore': - precision = total_area_intersect / total_area_pred_label - recall = total_area_intersect / total_area_label - f_value = torch.tensor( - [f_score(x[0], x[1], beta) for x in zip(precision, recall)]) - ret_metrics['Fscore'] = f_value - ret_metrics['Precision'] = precision - ret_metrics['Recall'] = recall - - ret_metrics = { - metric: value.numpy() - for metric, value in ret_metrics.items() - } - if nan_to_num is not None: - ret_metrics = OrderedDict({ - metric: np.nan_to_num(metric_value, nan=nan_to_num) - for metric, metric_value in ret_metrics.items() - }) - return ret_metrics diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/optimizers/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/optimizers/__init__.py deleted file mode 100644 index 4fbf4ecfc..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/optimizers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .layer_decay_optimizer_constructor import ( - LayerDecayOptimizerConstructor, LearningRateDecayOptimizerConstructor) - -__all__ = [ - 'LearningRateDecayOptimizerConstructor', 'LayerDecayOptimizerConstructor' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py deleted file mode 100644 index 2b6b8ff9c..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import warnings - -from mmcv.runner import DefaultOptimizerConstructor, get_dist_info - -from mmseg.utils import get_root_logger -from ..builder import OPTIMIZER_BUILDERS - - -def get_layer_id_for_convnext(var_name, max_layer_id): - """Get the layer id to set the different learning rates in ``layer_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_layer_id (int): Maximum number of backbone layers. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - stage_id = int(var_name.split('.')[2]) - if stage_id == 0: - layer_id = 0 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - block_id = int(var_name.split('.')[3]) - if stage_id == 0: - layer_id = 1 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 + block_id // 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - else: - return max_layer_id + 1 - - -def get_stage_id_for_convnext(var_name, max_stage_id): - """Get the stage id to set the different learning rates in ``stage_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_stage_id (int): Maximum number of backbone layers. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - return 0 - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - return stage_id + 1 - else: - return max_stage_id - 1 - - -def get_layer_id_for_vit(var_name, max_layer_id): - """Get the layer id to set the different learning rates. - - Args: - var_name (str): The key of the model. - num_max_layer (int): Maximum number of backbone layers. - - Returns: - int: Returns the layer id of the key. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.patch_embed'): - return 0 - elif var_name.startswith('backbone.layers'): - layer_id = int(var_name.split('.')[2]) - return layer_id + 1 - else: - return max_layer_id - 1 - - -@OPTIMIZER_BUILDERS.register_module() -class LearningRateDecayOptimizerConstructor(DefaultOptimizerConstructor): - """Different learning rates are set for different layers of backbone. - - Note: Currently, this optimizer constructor is built for ConvNeXt, - BEiT and MAE. - """ - - def add_params(self, params, module, **kwargs): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - """ - logger = get_root_logger() - - parameter_groups = {} - logger.info(f'self.paramwise_cfg is {self.paramwise_cfg}') - num_layers = self.paramwise_cfg.get('num_layers') + 2 - decay_rate = self.paramwise_cfg.get('decay_rate') - decay_type = self.paramwise_cfg.get('decay_type', 'layer_wise') - logger.info('Build LearningRateDecayOptimizerConstructor ' - f'{decay_type} {decay_rate} - {num_layers}') - weight_decay = self.base_wd - for name, param in module.named_parameters(): - if not param.requires_grad: - continue # frozen weights - if len(param.shape) == 1 or name.endswith('.bias') or name in ( - 'pos_embed', 'cls_token'): - group_name = 'no_decay' - this_weight_decay = 0. - else: - group_name = 'decay' - this_weight_decay = weight_decay - if 'layer_wise' in decay_type: - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_convnext( - name, self.paramwise_cfg.get('num_layers')) - logger.info(f'set param {name} as id {layer_id}') - elif 'BEiT' in module.backbone.__class__.__name__ or \ - 'MAE' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_vit(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - elif decay_type == 'stage_wise': - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_stage_id_for_convnext(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - group_name = f'layer_{layer_id}_{group_name}' - - if group_name not in parameter_groups: - scale = decay_rate**(num_layers - layer_id - 1) - - parameter_groups[group_name] = { - 'weight_decay': this_weight_decay, - 'params': [], - 'param_names': [], - 'lr_scale': scale, - 'group_name': group_name, - 'lr': scale * self.base_lr, - } - - parameter_groups[group_name]['params'].append(param) - parameter_groups[group_name]['param_names'].append(name) - rank, _ = get_dist_info() - if rank == 0: - to_display = {} - for key in parameter_groups: - to_display[key] = { - 'param_names': parameter_groups[key]['param_names'], - 'lr_scale': parameter_groups[key]['lr_scale'], - 'lr': parameter_groups[key]['lr'], - 'weight_decay': parameter_groups[key]['weight_decay'], - } - logger.info(f'Param groups = {json.dumps(to_display, indent=2)}') - params.extend(parameter_groups.values()) - - -@OPTIMIZER_BUILDERS.register_module() -class LayerDecayOptimizerConstructor(LearningRateDecayOptimizerConstructor): - """Different learning rates are set for different layers of backbone. - - Note: Currently, this optimizer constructor is built for BEiT, - and it will be deprecated. - Please use ``LearningRateDecayOptimizerConstructor`` instead. - """ - - def __init__(self, optimizer_cfg, paramwise_cfg): - warnings.warn('DeprecationWarning: Original ' - 'LayerDecayOptimizerConstructor of BEiT ' - 'will be deprecated. Please use ' - 'LearningRateDecayOptimizerConstructor instead, ' - 'and set decay_type = layer_wise_vit in paramwise_cfg.') - paramwise_cfg.update({'decay_type': 'layer_wise_vit'}) - warnings.warn('DeprecationWarning: Layer_decay_rate will ' - 'be deleted, please use decay_rate instead.') - paramwise_cfg['decay_rate'] = paramwise_cfg.pop('layer_decay_rate') - super(LayerDecayOptimizerConstructor, - self).__init__(optimizer_cfg, paramwise_cfg) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/__init__.py deleted file mode 100644 index 5206b96be..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_pixel_sampler -from .sampler import BasePixelSampler, OHEMPixelSampler - -__all__ = ['build_pixel_sampler', 'BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/builder.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/builder.py deleted file mode 100644 index 1cecd347b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/builder.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, build_from_cfg - -PIXEL_SAMPLERS = Registry('pixel sampler') - - -def build_pixel_sampler(cfg, **default_args): - """Build pixel sampler for segmentation map.""" - return build_from_cfg(cfg, PIXEL_SAMPLERS, default_args) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/sampler/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/sampler/__init__.py deleted file mode 100644 index 5a7648564..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/sampler/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_pixel_sampler import BasePixelSampler -from .ohem_pixel_sampler import OHEMPixelSampler - -__all__ = ['BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py deleted file mode 100644 index 03672cd47..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BasePixelSampler(metaclass=ABCMeta): - """Base class of pixel sampler.""" - - def __init__(self, **kwargs): - pass - - @abstractmethod - def sample(self, seg_logit, seg_label): - """Placeholder for sample function.""" diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py deleted file mode 100644 index 833a28768..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import PIXEL_SAMPLERS -from .base_pixel_sampler import BasePixelSampler - - -@PIXEL_SAMPLERS.register_module() -class OHEMPixelSampler(BasePixelSampler): - """Online Hard Example Mining Sampler for segmentation. - - Args: - context (nn.Module): The context of sampler, subclass of - :obj:`BaseDecodeHead`. - thresh (float, optional): The threshold for hard example selection. - Below which, are prediction with low confidence. If not - specified, the hard examples will be pixels of top ``min_kept`` - loss. Default: None. - min_kept (int, optional): The minimum number of predictions to keep. - Default: 100000. - """ - - def __init__(self, context, thresh=None, min_kept=100000): - super(OHEMPixelSampler, self).__init__() - self.context = context - assert min_kept > 1 - self.thresh = thresh - self.min_kept = min_kept - - def sample(self, seg_logit, seg_label): - """Sample pixels that have high loss or with low prediction confidence. - - Args: - seg_logit (torch.Tensor): segmentation logits, shape (N, C, H, W) - seg_label (torch.Tensor): segmentation label, shape (N, 1, H, W) - - Returns: - torch.Tensor: segmentation weight, shape (N, H, W) - """ - with torch.no_grad(): - assert seg_logit.shape[2:] == seg_label.shape[2:] - assert seg_label.shape[1] == 1 - seg_label = seg_label.squeeze(1).long() - batch_kept = self.min_kept * seg_label.size(0) - valid_mask = seg_label != self.context.ignore_index - seg_weight = seg_logit.new_zeros(size=seg_label.size()) - valid_seg_weight = seg_weight[valid_mask] - if self.thresh is not None: - seg_prob = F.softmax(seg_logit, dim=1) - - tmp_seg_label = seg_label.clone().unsqueeze(1) - tmp_seg_label[tmp_seg_label == self.context.ignore_index] = 0 - seg_prob = seg_prob.gather(1, tmp_seg_label).squeeze(1) - sort_prob, sort_indices = seg_prob[valid_mask].sort() - - if sort_prob.numel() > 0: - min_threshold = sort_prob[min(batch_kept, - sort_prob.numel() - 1)] - else: - min_threshold = 0.0 - threshold = max(min_threshold, self.thresh) - valid_seg_weight[seg_prob[valid_mask] < threshold] = 1. - else: - if not isinstance(self.context.loss_decode, nn.ModuleList): - losses_decode = [self.context.loss_decode] - else: - losses_decode = self.context.loss_decode - losses = 0.0 - for loss_module in losses_decode: - losses += loss_module( - seg_logit, - seg_label, - weight=None, - ignore_index=self.context.ignore_index, - reduction_override='none') - - # faster than topk according to https://github.com/pytorch/pytorch/issues/22812 # noqa - _, sort_indices = losses[valid_mask].sort(descending=True) - valid_seg_weight[sort_indices[:batch_kept]] = 1. - - seg_weight[valid_mask] = valid_seg_weight - - return seg_weight diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/utils/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/utils/__init__.py deleted file mode 100644 index 28882893a..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/utils/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .dist_util import check_dist_init, sync_random_seed -from .misc import add_prefix - -__all__ = ['add_prefix', 'check_dist_init', 'sync_random_seed'] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/utils/dist_util.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/utils/dist_util.py deleted file mode 100644 index b3288519d..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/utils/dist_util.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def check_dist_init(): - return dist.is_available() and dist.is_initialized() - - -def sync_random_seed(seed=None, device='cuda'): - """Make sure different ranks share the same seed. All workers must call - this function, otherwise it will deadlock. This method is generally used in - `DistributedSampler`, because the seed should be identical across all - processes in the distributed group. - - In distributed sampling, different ranks should sample non-overlapped - data in the dataset. Therefore, this function is used to make sure that - each rank shuffles the data indices in the same order based - on the same seed. Then different ranks could use different indices - to select non-overlapped data from the same data list. - - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - - if seed is None: - seed = np.random.randint(2**31) - assert isinstance(seed, int) - - rank, world_size = get_dist_info() - - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/utils/misc.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/utils/misc.py deleted file mode 100644 index 282bb8d96..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/core/utils/misc.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def add_prefix(inputs, prefix): - """Add prefix for dict. - - Args: - inputs (dict): The input dict with str keys. - prefix (str): The prefix to add. - - Returns: - - dict: The dict with keys updated with ``prefix``. - """ - - outputs = dict() - for name, value in inputs.items(): - outputs[f'{prefix}.{name}'] = value - - return outputs diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/__init__.py deleted file mode 100644 index 3366f0aec..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .ade import ADE20KDataset -from .builder import DATASETS, PIPELINES, build_dataloader, build_dataset -from .cityscapes import CityscapesDataset -from .coco_stuff import COCOStuffDataset -from .custom import CustomDataset -from .dataset_wrappers import (ConcatDataset, MultiImageMixDataset, - RepeatDataset) -from .pascal_context import PascalContextDataset, PascalContextDataset59 -from .voc import PascalVOCDataset diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/ade.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/ade.py deleted file mode 100644 index db94cebd3..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/ade.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np -from PIL import Image - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class ADE20KDataset(CustomDataset): - """ADE20K dataset. - - In segmentation map annotation for ADE20K, 0 stands for background, which - is not included in 150 categories. ``reduce_zero_label`` is fixed to True. - The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is fixed to - '.png'. - """ - CLASSES = ( - 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', - 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', - 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', - 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', - 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', - 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', - 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', - 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', - 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', - 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', - 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', - 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', - 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', - 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', - 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', - 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', - 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', - 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', - 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', - 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', - 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', - 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', - 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', - 'clock', 'flag') - - PALETTE = [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], - [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], - [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], - [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], - [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], - [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], - [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], - [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], - [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], - [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], - [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], - [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], - [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], - [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], - [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], - [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], - [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], - [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], - [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], - [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], - [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], - [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], - [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], - [102, 255, 0], [92, 0, 255]] - - def __init__(self, **kwargs): - super(ADE20KDataset, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - reduce_zero_label=True, - **kwargs) - - def results2img(self, results, imgfile_prefix, to_label_id, indices=None): - """Write the segmentation results to images. - - Args: - results (list[ndarray]): Testing results of the - dataset. - imgfile_prefix (str): The filename prefix of the png files. - If the prefix is "somepath/xxx", - the png files will be named "somepath/xxx.png". - to_label_id (bool): whether convert output to label_id for - submission. - indices (list[int], optional): Indices of input results, if not - set, all the indices of the dataset will be used. - Default: None. - - Returns: - list[str: str]: result txt files which contains corresponding - semantic segmentation images. - """ - if indices is None: - indices = list(range(len(self))) - - mmcv.mkdir_or_exist(imgfile_prefix) - result_files = [] - for result, idx in zip(results, indices): - - filename = self.img_infos[idx]['filename'] - basename = osp.splitext(osp.basename(filename))[0] - - png_filename = osp.join(imgfile_prefix, f'{basename}.png') - - # The index range of official requirement is from 0 to 150. - # But the index range of output is from 0 to 149. - # That is because we set reduce_zero_label=True. - result = result + 1 - - output = Image.fromarray(result.astype(np.uint8)) - output.save(png_filename) - result_files.append(png_filename) - - return result_files - - def format_results(self, - results, - imgfile_prefix, - to_label_id=True, - indices=None): - """Format the results into dir (standard format for ade20k evaluation). - - Args: - results (list): Testing results of the dataset. - imgfile_prefix (str | None): The prefix of images files. It - includes the file path and the prefix of filename, e.g., - "a/b/prefix". - to_label_id (bool): whether convert output to label_id for - submission. Default: False - indices (list[int], optional): Indices of input results, if not - set, all the indices of the dataset will be used. - Default: None. - - Returns: - tuple: (result_files, tmp_dir), result_files is a list containing - the image paths, tmp_dir is the temporal directory created - for saving json/png files when img_prefix is not specified. - """ - - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - result_files = self.results2img(results, imgfile_prefix, to_label_id, - indices) - return result_files diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/builder.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/builder.py deleted file mode 100644 index 4d852d365..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/builder.py +++ /dev/null @@ -1,191 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import platform -import random -from functools import partial - -import numpy as np -import torch -from mmcv.parallel import collate -from mmcv.runner import get_dist_info -from mmcv.utils import Registry, build_from_cfg, digit_version -from torch.utils.data import DataLoader - -from .samplers import DistributedSampler - -if platform.system() != 'Windows': - # https://github.com/pytorch/pytorch/issues/973 - import resource - rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) - base_soft_limit = rlimit[0] - hard_limit = rlimit[1] - soft_limit = min(max(4096, base_soft_limit), hard_limit) - resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) - -DATASETS = Registry('dataset') -PIPELINES = Registry('pipeline') - - -def _concat_dataset(cfg, default_args=None): - """Build :obj:`ConcatDataset by.""" - from .dataset_wrappers import ConcatDataset - img_dir = cfg['img_dir'] - ann_dir = cfg.get('ann_dir', None) - split = cfg.get('split', None) - # pop 'separate_eval' since it is not a valid key for common datasets. - separate_eval = cfg.pop('separate_eval', True) - num_img_dir = len(img_dir) if isinstance(img_dir, (list, tuple)) else 1 - if ann_dir is not None: - num_ann_dir = len(ann_dir) if isinstance(ann_dir, (list, tuple)) else 1 - else: - num_ann_dir = 0 - if split is not None: - num_split = len(split) if isinstance(split, (list, tuple)) else 1 - else: - num_split = 0 - if num_img_dir > 1: - assert num_img_dir == num_ann_dir or num_ann_dir == 0 - assert num_img_dir == num_split or num_split == 0 - else: - assert num_split == num_ann_dir or num_ann_dir <= 1 - num_dset = max(num_split, num_img_dir) - - datasets = [] - for i in range(num_dset): - data_cfg = copy.deepcopy(cfg) - if isinstance(img_dir, (list, tuple)): - data_cfg['img_dir'] = img_dir[i] - if isinstance(ann_dir, (list, tuple)): - data_cfg['ann_dir'] = ann_dir[i] - if isinstance(split, (list, tuple)): - data_cfg['split'] = split[i] - datasets.append(build_dataset(data_cfg, default_args)) - - return ConcatDataset(datasets, separate_eval) - - -def build_dataset(cfg, default_args=None): - """Build datasets.""" - from .dataset_wrappers import (ConcatDataset, MultiImageMixDataset, - RepeatDataset) - if isinstance(cfg, (list, tuple)): - dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) - elif cfg['type'] == 'RepeatDataset': - dataset = RepeatDataset( - build_dataset(cfg['dataset'], default_args), cfg['times']) - elif cfg['type'] == 'MultiImageMixDataset': - cp_cfg = copy.deepcopy(cfg) - cp_cfg['dataset'] = build_dataset(cp_cfg['dataset']) - cp_cfg.pop('type') - dataset = MultiImageMixDataset(**cp_cfg) - elif isinstance(cfg.get('img_dir'), (list, tuple)) or isinstance( - cfg.get('split', None), (list, tuple)): - dataset = _concat_dataset(cfg, default_args) - else: - dataset = build_from_cfg(cfg, DATASETS, default_args) - - return dataset - - -def build_dataloader(dataset, - samples_per_gpu, - workers_per_gpu, - num_gpus=1, - dist=True, - shuffle=True, - seed=None, - drop_last=False, - pin_memory=True, - persistent_workers=True, - **kwargs): - """Build PyTorch DataLoader. - - In distributed training, each GPU/process has a dataloader. - In non-distributed training, there is only one dataloader for all GPUs. - - Args: - dataset (Dataset): A PyTorch dataset. - samples_per_gpu (int): Number of training samples on each GPU, i.e., - batch size of each GPU. - workers_per_gpu (int): How many subprocesses to use for data loading - for each GPU. - num_gpus (int): Number of GPUs. Only used in non-distributed training. - dist (bool): Distributed training/test or not. Default: True. - shuffle (bool): Whether to shuffle the data at every epoch. - Default: True. - seed (int | None): Seed to be used. Default: None. - drop_last (bool): Whether to drop the last incomplete batch in epoch. - Default: False - pin_memory (bool): Whether to use pin_memory in DataLoader. - Default: True - persistent_workers (bool): If True, the data loader will not shutdown - the worker processes after a dataset has been consumed once. - This allows to maintain the workers Dataset instances alive. - The argument also has effect in PyTorch>=1.7.0. - Default: True - kwargs: any keyword argument to be used to initialize DataLoader - - Returns: - DataLoader: A PyTorch dataloader. - """ - rank, world_size = get_dist_info() - if dist: - sampler = DistributedSampler( - dataset, world_size, rank, shuffle=shuffle, seed=seed) - shuffle = False - batch_size = samples_per_gpu - num_workers = workers_per_gpu - else: - sampler = None - batch_size = num_gpus * samples_per_gpu - num_workers = num_gpus * workers_per_gpu - - init_fn = partial( - worker_init_fn, num_workers=num_workers, rank=rank, - seed=seed) if seed is not None else None - - if digit_version(torch.__version__) >= digit_version('1.8.0'): - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - persistent_workers=persistent_workers, - **kwargs) - else: - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - **kwargs) - - return data_loader - - -def worker_init_fn(worker_id, num_workers, rank, seed): - """Worker init func for dataloader. - - The seed of each worker equals to num_worker * rank + worker_id + user_seed - - Args: - worker_id (int): Worker id. - num_workers (int): Number of workers. - rank (int): The rank of current process. - seed (int): The random seed to use. - """ - - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/cityscapes.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/cityscapes.py deleted file mode 100644 index ed633d00d..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/cityscapes.py +++ /dev/null @@ -1,214 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np -from mmcv.utils import print_log -from PIL import Image - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class CityscapesDataset(CustomDataset): - """Cityscapes dataset. - - The ``img_suffix`` is fixed to '_leftImg8bit.png' and ``seg_map_suffix`` is - fixed to '_gtFine_labelTrainIds.png' for Cityscapes dataset. - """ - - CLASSES = ('road', 'sidewalk', 'building', 'wall', 'fence', 'pole', - 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle') - - PALETTE = [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], - [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], - [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], - [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], - [0, 80, 100], [0, 0, 230], [119, 11, 32]] - - def __init__(self, - img_suffix='_leftImg8bit.png', - seg_map_suffix='_gtFine_labelTrainIds.png', - **kwargs): - super(CityscapesDataset, self).__init__( - img_suffix=img_suffix, seg_map_suffix=seg_map_suffix, **kwargs) - - @staticmethod - def _convert_to_label_id(result): - """Convert trainId to id for cityscapes.""" - if isinstance(result, str): - result = np.load(result) - import cityscapesscripts.helpers.labels as CSLabels - result_copy = result.copy() - for trainId, label in CSLabels.trainId2label.items(): - result_copy[result == trainId] = label.id - - return result_copy - - def results2img(self, results, imgfile_prefix, to_label_id, indices=None): - """Write the segmentation results to images. - - Args: - results (list[ndarray]): Testing results of the - dataset. - imgfile_prefix (str): The filename prefix of the png files. - If the prefix is "somepath/xxx", - the png files will be named "somepath/xxx.png". - to_label_id (bool): whether convert output to label_id for - submission. - indices (list[int], optional): Indices of input results, - if not set, all the indices of the dataset will be used. - Default: None. - - Returns: - list[str: str]: result txt files which contains corresponding - semantic segmentation images. - """ - if indices is None: - indices = list(range(len(self))) - - mmcv.mkdir_or_exist(imgfile_prefix) - result_files = [] - for result, idx in zip(results, indices): - if to_label_id: - result = self._convert_to_label_id(result) - filename = self.img_infos[idx]['filename'] - basename = osp.splitext(osp.basename(filename))[0] - - png_filename = osp.join(imgfile_prefix, f'{basename}.png') - - output = Image.fromarray(result.astype(np.uint8)).convert('P') - import cityscapesscripts.helpers.labels as CSLabels - palette = np.zeros((len(CSLabels.id2label), 3), dtype=np.uint8) - for label_id, label in CSLabels.id2label.items(): - palette[label_id] = label.color - - output.putpalette(palette) - output.save(png_filename) - result_files.append(png_filename) - - return result_files - - def format_results(self, - results, - imgfile_prefix, - to_label_id=True, - indices=None): - """Format the results into dir (standard format for Cityscapes - evaluation). - - Args: - results (list): Testing results of the dataset. - imgfile_prefix (str): The prefix of images files. It - includes the file path and the prefix of filename, e.g., - "a/b/prefix". - to_label_id (bool): whether convert output to label_id for - submission. Default: False - indices (list[int], optional): Indices of input results, - if not set, all the indices of the dataset will be used. - Default: None. - - Returns: - tuple: (result_files, tmp_dir), result_files is a list containing - the image paths, tmp_dir is the temporal directory created - for saving json/png files when img_prefix is not specified. - """ - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - result_files = self.results2img(results, imgfile_prefix, to_label_id, - indices) - - return result_files - - def evaluate(self, - results, - metric='mIoU', - logger=None, - imgfile_prefix=None): - """Evaluation in Cityscapes/default protocol. - - Args: - results (list): Testing results of the dataset. - metric (str | list[str]): Metrics to be evaluated. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - imgfile_prefix (str | None): The prefix of output image file, - for cityscapes evaluation only. It includes the file path and - the prefix of filename, e.g., "a/b/prefix". - If results are evaluated with cityscapes protocol, it would be - the prefix of output png files. The output files would be - png images under folder "a/b/prefix/xxx.png", where "xxx" is - the image name of cityscapes. If not specified, a temp file - will be created for evaluation. - Default: None. - - Returns: - dict[str, float]: Cityscapes/default metrics. - """ - - eval_results = dict() - metrics = metric.copy() if isinstance(metric, list) else [metric] - if 'cityscapes' in metrics: - eval_results.update( - self._evaluate_cityscapes(results, logger, imgfile_prefix)) - metrics.remove('cityscapes') - if len(metrics) > 0: - eval_results.update( - super(CityscapesDataset, - self).evaluate(results, metrics, logger)) - - return eval_results - - def _evaluate_cityscapes(self, results, logger, imgfile_prefix): - """Evaluation in Cityscapes protocol. - - Args: - results (list): Testing results of the dataset. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - imgfile_prefix (str | None): The prefix of output image file - - Returns: - dict[str: float]: Cityscapes evaluation results. - """ - try: - import cityscapesscripts.evaluation.evalPixelLevelSemanticLabeling as CSEval # noqa - except ImportError: - raise ImportError('Please run "pip install cityscapesscripts" to ' - 'install cityscapesscripts first.') - msg = 'Evaluating in Cityscapes style' - if logger is None: - msg = '\n' + msg - print_log(msg, logger=logger) - - result_dir = imgfile_prefix - - eval_results = dict() - print_log(f'Evaluating results under {result_dir} ...', logger=logger) - - CSEval.args.evalInstLevelScore = True - CSEval.args.predictionPath = osp.abspath(result_dir) - CSEval.args.evalPixelAccuracy = True - CSEval.args.JSONOutput = False - - seg_map_list = [] - pred_list = [] - - # when evaluating with official cityscapesscripts, - # **_gtFine_labelIds.png is used - for seg_map in mmcv.scandir( - self.ann_dir, 'gtFine_labelIds.png', recursive=True): - seg_map_list.append(osp.join(self.ann_dir, seg_map)) - pred_list.append(CSEval.getPrediction(CSEval.args, seg_map)) - - eval_results.update( - CSEval.evaluateImgLists(pred_list, seg_map_list, CSEval.args)) - - return eval_results diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/coco_stuff.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/coco_stuff.py deleted file mode 100644 index 24d089556..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/coco_stuff.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class COCOStuffDataset(CustomDataset): - """COCO-Stuff dataset. - - In segmentation map annotation for COCO-Stuff, Train-IDs of the 10k version - are from 1 to 171, where 0 is the ignore index, and Train-ID of COCO Stuff - 164k is from 0 to 170, where 255 is the ignore index. So, they are all 171 - semantic categories. ``reduce_zero_label`` is set to True and False for the - 10k and 164k versions, respectively. The ``img_suffix`` is fixed to '.jpg', - and ``seg_map_suffix`` is fixed to '.png'. - """ - CLASSES = ( - 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', - 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', - 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', - 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', - 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', - 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', - 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', - 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', - 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', - 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', - 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', - 'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner', - 'blanket', 'branch', 'bridge', 'building-other', 'bush', 'cabinet', - 'cage', 'cardboard', 'carpet', 'ceiling-other', 'ceiling-tile', - 'cloth', 'clothes', 'clouds', 'counter', 'cupboard', 'curtain', - 'desk-stuff', 'dirt', 'door-stuff', 'fence', 'floor-marble', - 'floor-other', 'floor-stone', 'floor-tile', 'floor-wood', - 'flower', 'fog', 'food-other', 'fruit', 'furniture-other', 'grass', - 'gravel', 'ground-other', 'hill', 'house', 'leaves', 'light', 'mat', - 'metal', 'mirror-stuff', 'moss', 'mountain', 'mud', 'napkin', 'net', - 'paper', 'pavement', 'pillow', 'plant-other', 'plastic', 'platform', - 'playingfield', 'railing', 'railroad', 'river', 'road', 'rock', 'roof', - 'rug', 'salad', 'sand', 'sea', 'shelf', 'sky-other', 'skyscraper', - 'snow', 'solid-other', 'stairs', 'stone', 'straw', 'structural-other', - 'table', 'tent', 'textile-other', 'towel', 'tree', 'vegetable', - 'wall-brick', 'wall-concrete', 'wall-other', 'wall-panel', - 'wall-stone', 'wall-tile', 'wall-wood', 'water-other', 'waterdrops', - 'window-blind', 'window-other', 'wood') - - PALETTE = [[0, 192, 64], [0, 192, 64], [0, 64, 96], [128, 192, 192], - [0, 64, 64], [0, 192, 224], [0, 192, 192], [128, 192, 64], - [0, 192, 96], [128, 192, 64], [128, 32, 192], [0, 0, 224], - [0, 0, 64], [0, 160, 192], [128, 0, 96], [128, 0, 192], - [0, 32, 192], [128, 128, 224], [0, 0, 192], [128, 160, 192], - [128, 128, 0], [128, 0, 32], [128, 32, 0], [128, 0, 128], - [64, 128, 32], [0, 160, 0], [0, 0, 0], [192, 128, 160], - [0, 32, 0], [0, 128, 128], [64, 128, 160], [128, 160, 0], - [0, 128, 0], [192, 128, 32], [128, 96, 128], [0, 0, 128], - [64, 0, 32], [0, 224, 128], [128, 0, 0], [192, 0, 160], - [0, 96, 128], [128, 128, 128], [64, 0, 160], [128, 224, 128], - [128, 128, 64], [192, 0, 32], [128, 96, 0], [128, 0, 192], - [0, 128, 32], [64, 224, 0], [0, 0, 64], [128, 128, 160], - [64, 96, 0], [0, 128, 192], [0, 128, 160], [192, 224, 0], - [0, 128, 64], [128, 128, 32], [192, 32, 128], [0, 64, 192], - [0, 0, 32], [64, 160, 128], [128, 64, 64], [128, 0, 160], - [64, 32, 128], [128, 192, 192], [0, 0, 160], [192, 160, 128], - [128, 192, 0], [128, 0, 96], [192, 32, 0], [128, 64, 128], - [64, 128, 96], [64, 160, 0], [0, 64, 0], [192, 128, 224], - [64, 32, 0], [0, 192, 128], [64, 128, 224], [192, 160, 0], - [0, 192, 0], [192, 128, 96], [192, 96, 128], [0, 64, 128], - [64, 0, 96], [64, 224, 128], [128, 64, 0], [192, 0, 224], - [64, 96, 128], [128, 192, 128], [64, 0, 224], [192, 224, 128], - [128, 192, 64], [192, 0, 96], [192, 96, 0], [128, 64, 192], - [0, 128, 96], [0, 224, 0], [64, 64, 64], [128, 128, 224], - [0, 96, 0], [64, 192, 192], [0, 128, 224], [128, 224, 0], - [64, 192, 64], [128, 128, 96], [128, 32, 128], [64, 0, 192], - [0, 64, 96], [0, 160, 128], [192, 0, 64], [128, 64, 224], - [0, 32, 128], [192, 128, 192], [0, 64, 224], [128, 160, 128], - [192, 128, 0], [128, 64, 32], [128, 32, 64], [192, 0, 128], - [64, 192, 32], [0, 160, 64], [64, 0, 0], [192, 192, 160], - [0, 32, 64], [64, 128, 128], [64, 192, 160], [128, 160, 64], - [64, 128, 0], [192, 192, 32], [128, 96, 192], [64, 0, 128], - [64, 64, 32], [0, 224, 192], [192, 0, 0], [192, 64, 160], - [0, 96, 192], [192, 128, 128], [64, 64, 160], [128, 224, 192], - [192, 128, 64], [192, 64, 32], [128, 96, 64], [192, 0, 192], - [0, 192, 32], [64, 224, 64], [64, 0, 64], [128, 192, 160], - [64, 96, 64], [64, 128, 192], [0, 192, 160], [192, 224, 64], - [64, 128, 64], [128, 192, 32], [192, 32, 192], [64, 64, 192], - [0, 64, 32], [64, 160, 192], [192, 64, 64], [128, 64, 160], - [64, 32, 192], [192, 192, 192], [0, 64, 160], [192, 160, 192], - [192, 192, 0], [128, 64, 96], [192, 32, 64], [192, 64, 128], - [64, 192, 96], [64, 160, 64], [64, 64, 0]] - - def __init__(self, **kwargs): - super(COCOStuffDataset, self).__init__( - img_suffix='.jpg', seg_map_suffix='_labelTrainIds.png', **kwargs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/custom.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/custom.py deleted file mode 100644 index 4615d4114..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/custom.py +++ /dev/null @@ -1,487 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from collections import OrderedDict - -import mmcv -import numpy as np -from mmcv.utils import print_log -from prettytable import PrettyTable -from torch.utils.data import Dataset - -from mmseg.core import eval_metrics, intersect_and_union, pre_eval_to_metrics -from mmseg.utils import get_root_logger -from .builder import DATASETS -from .pipelines import Compose, LoadAnnotations - - -@DATASETS.register_module() -class CustomDataset(Dataset): - """Custom dataset for semantic segmentation. An example of file structure - is as followed. - - .. code-block:: none - - ├── data - │ ├── my_dataset - │ │ ├── img_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{img_suffix} - │ │ │ │ ├── yyy{img_suffix} - │ │ │ │ ├── zzz{img_suffix} - │ │ │ ├── val - │ │ ├── ann_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{seg_map_suffix} - │ │ │ │ ├── yyy{seg_map_suffix} - │ │ │ │ ├── zzz{seg_map_suffix} - │ │ │ ├── val - - The img/gt_semantic_seg pair of CustomDataset should be of the same - except suffix. A valid img/gt_semantic_seg filename pair should be like - ``xxx{img_suffix}`` and ``xxx{seg_map_suffix}`` (extension is also included - in the suffix). If split is given, then ``xxx`` is specified in txt file. - Otherwise, all files in ``img_dir/``and ``ann_dir`` will be loaded. - Please refer to ``docs/en/tutorials/new_dataset.md`` for more details. - - - Args: - pipeline (list[dict]): Processing pipeline - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. Default: '.jpg' - ann_dir (str, optional): Path to annotation directory. Default: None - seg_map_suffix (str): Suffix of segmentation maps. Default: '.png' - split (str, optional): Split txt file. If split is specified, only - file with suffix in the splits will be loaded. Otherwise, all - images in img_dir/ann_dir will be loaded. Default: None - data_root (str, optional): Data root for img_dir/ann_dir. Default: - None. - test_mode (bool): If test_mode=True, gt wouldn't be loaded. - ignore_index (int): The label index to be ignored. Default: 255 - reduce_zero_label (bool): Whether to mark label zero as ignored. - Default: False - classes (str | Sequence[str], optional): Specify classes to load. - If is None, ``cls.CLASSES`` will be used. Default: None. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, and - self.PALETTE is None, random palette will be generated. - Default: None - gt_seg_map_loader_cfg (dict, optional): build LoadAnnotations to - load gt for evaluation, load from disk by default. Default: None. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - CLASSES = None - - PALETTE = None - - def __init__(self, - pipeline, - img_dir, - img_suffix='.jpg', - ann_dir=None, - seg_map_suffix='.png', - split=None, - data_root=None, - test_mode=False, - ignore_index=255, - reduce_zero_label=False, - classes=None, - palette=None, - gt_seg_map_loader_cfg=None, - file_client_args=dict(backend='disk')): - self.pipeline = Compose(pipeline) - self.img_dir = img_dir - self.img_suffix = img_suffix - self.ann_dir = ann_dir - self.seg_map_suffix = seg_map_suffix - self.split = split - self.data_root = data_root - self.test_mode = test_mode - self.ignore_index = ignore_index - self.reduce_zero_label = reduce_zero_label - self.label_map = None - self.CLASSES, self.PALETTE = self.get_classes_and_palette( - classes, palette) - self.gt_seg_map_loader = LoadAnnotations( - ) if gt_seg_map_loader_cfg is None else LoadAnnotations( - **gt_seg_map_loader_cfg) - - self.file_client_args = file_client_args - self.file_client = mmcv.FileClient.infer_client(self.file_client_args) - - if test_mode: - assert self.CLASSES is not None, \ - '`cls.CLASSES` or `classes` should be specified when testing' - - # join paths if data_root is specified - if self.data_root is not None: - if not osp.isabs(self.img_dir): - self.img_dir = osp.join(self.data_root, self.img_dir) - if not (self.ann_dir is None or osp.isabs(self.ann_dir)): - self.ann_dir = osp.join(self.data_root, self.ann_dir) - if not (self.split is None or osp.isabs(self.split)): - self.split = osp.join(self.data_root, self.split) - - # load annotations - self.img_infos = self.load_annotations(self.img_dir, self.img_suffix, - self.ann_dir, - self.seg_map_suffix, self.split) - - def __len__(self): - """Total number of samples of data.""" - return len(self.img_infos) - - def load_annotations(self, img_dir, img_suffix, ann_dir, seg_map_suffix, - split): - """Load annotation from directory. - - Args: - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. - ann_dir (str|None): Path to annotation directory. - seg_map_suffix (str|None): Suffix of segmentation maps. - split (str|None): Split txt file. If split is specified, only file - with suffix in the splits will be loaded. Otherwise, all images - in img_dir/ann_dir will be loaded. Default: None - - Returns: - list[dict]: All image info of dataset. - """ - - img_infos = [] - if split is not None: - lines = mmcv.list_from_file( - split, file_client_args=self.file_client_args) - for line in lines: - img_name = line.strip() - img_info = dict(filename=img_name + img_suffix) - if ann_dir is not None: - seg_map = img_name + seg_map_suffix - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - else: - for img in self.file_client.list_dir_or_file( - dir_path=img_dir, - list_dir=False, - suffix=img_suffix, - recursive=True): - img_info = dict(filename=img) - if ann_dir is not None: - seg_map = img.replace(img_suffix, seg_map_suffix) - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - img_infos = sorted(img_infos, key=lambda x: x['filename']) - - print_log(f'Loaded {len(img_infos)} images', logger=get_root_logger()) - return img_infos - - def get_ann_info(self, idx): - """Get annotation by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - return self.img_infos[idx]['ann'] - - def pre_pipeline(self, results): - """Prepare results dict for pipeline.""" - results['seg_fields'] = [] - results['img_prefix'] = self.img_dir - results['seg_prefix'] = self.ann_dir - if self.custom_classes: - results['label_map'] = self.label_map - - def __getitem__(self, idx): - """Get training/test data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training/test data (with annotation if `test_mode` is set - False). - """ - - if self.test_mode: - return self.prepare_test_img(idx) - else: - return self.prepare_train_img(idx) - - def prepare_train_img(self, idx): - """Get training data and annotations after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training data and annotation after pipeline with new keys - introduced by pipeline. - """ - - img_info = self.img_infos[idx] - ann_info = self.get_ann_info(idx) - results = dict(img_info=img_info, ann_info=ann_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def prepare_test_img(self, idx): - """Get testing data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Testing data after pipeline with new keys introduced by - pipeline. - """ - - img_info = self.img_infos[idx] - results = dict(img_info=img_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """Place holder to format result to dataset specific output.""" - raise NotImplementedError - - def get_gt_seg_map_by_idx(self, index): - """Get one ground truth segmentation map for evaluation.""" - ann_info = self.get_ann_info(index) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - return results['gt_semantic_seg'] - - def get_gt_seg_maps(self, efficient_test=None): - """Get ground truth segmentation maps for evaluation.""" - if efficient_test is not None: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` has been deprecated ' - 'since MMSeg v0.16, the ``get_gt_seg_maps()`` is CPU memory ' - 'friendly by default. ') - - for idx in range(len(self)): - ann_info = self.get_ann_info(idx) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - yield results['gt_semantic_seg'] - - def pre_eval(self, preds, indices): - """Collect eval result from each iteration. - - Args: - preds (list[torch.Tensor] | torch.Tensor): the segmentation logit - after argmax, shape (N, H, W). - indices (list[int] | int): the prediction related ground truth - indices. - - Returns: - list[torch.Tensor]: (area_intersect, area_union, area_prediction, - area_ground_truth). - """ - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - - pre_eval_results = [] - - for pred, index in zip(preds, indices): - seg_map = self.get_gt_seg_map_by_idx(index) - pre_eval_results.append( - intersect_and_union( - pred, - seg_map, - len(self.CLASSES), - self.ignore_index, - # as the labels has been converted when dataset initialized - # in `get_palette_for_custom_classes ` this `label_map` - # should be `dict()`, see - # https://github.com/open-mmlab/mmsegmentation/issues/1415 - # for more ditails - label_map=dict(), - reduce_zero_label=self.reduce_zero_label)) - - return pre_eval_results - - def get_classes_and_palette(self, classes=None, palette=None): - """Get class names of current dataset. - - Args: - classes (Sequence[str] | str | None): If classes is None, use - default CLASSES defined by builtin dataset. If classes is a - string, take it as a file name. The file contains the name of - classes where each line contains one class name. If classes is - a tuple or list, override the CLASSES defined by the dataset. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, random - palette will be generated. Default: None - """ - if classes is None: - self.custom_classes = False - return self.CLASSES, self.PALETTE - - self.custom_classes = True - if isinstance(classes, str): - # take it as a file path - class_names = mmcv.list_from_file(classes) - elif isinstance(classes, (tuple, list)): - class_names = classes - else: - raise ValueError(f'Unsupported type {type(classes)} of classes.') - - if self.CLASSES: - if not set(class_names).issubset(self.CLASSES): - raise ValueError('classes is not a subset of CLASSES.') - - # dictionary, its keys are the old label ids and its values - # are the new label ids. - # used for changing pixel labels in load_annotations. - self.label_map = {} - for i, c in enumerate(self.CLASSES): - if c not in class_names: - self.label_map[i] = -1 - else: - self.label_map[i] = class_names.index(c) - - palette = self.get_palette_for_custom_classes(class_names, palette) - - return class_names, palette - - def get_palette_for_custom_classes(self, class_names, palette=None): - - if self.label_map is not None: - # return subset of palette - palette = [] - for old_id, new_id in sorted( - self.label_map.items(), key=lambda x: x[1]): - if new_id != -1: - palette.append(self.PALETTE[old_id]) - palette = type(self.PALETTE)(palette) - - elif palette is None: - if self.PALETTE is None: - # Get random state before set seed, and restore - # random state later. - # It will prevent loss of randomness, as the palette - # may be different in each iteration if not specified. - # See: https://github.com/open-mmlab/mmdetection/issues/5844 - state = np.random.get_state() - np.random.seed(42) - # random palette - palette = np.random.randint(0, 255, size=(len(class_names), 3)) - np.random.set_state(state) - else: - palette = self.PALETTE - - return palette - - def evaluate(self, - results, - metric='mIoU', - logger=None, - gt_seg_maps=None, - **kwargs): - """Evaluate the dataset. - - Args: - results (list[tuple[torch.Tensor]] | list[str]): per image pre_eval - results or predict segmentation map for computing evaluation - metric. - metric (str | list[str]): Metrics to be evaluated. 'mIoU', - 'mDice' and 'mFscore' are supported. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - gt_seg_maps (generator[ndarray]): Custom gt seg maps as input, - used in ConcatDataset - - Returns: - dict[str, float]: Default metrics. - """ - if isinstance(metric, str): - metric = [metric] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metric).issubset(set(allowed_metrics)): - raise KeyError('metric {} is not supported'.format(metric)) - - eval_results = {} - # test a list of files - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - if gt_seg_maps is None: - gt_seg_maps = self.get_gt_seg_maps() - num_classes = len(self.CLASSES) - ret_metrics = eval_metrics( - results, - gt_seg_maps, - num_classes, - self.ignore_index, - metric, - label_map=dict(), - reduce_zero_label=self.reduce_zero_label) - # test a list of pre_eval_results - else: - ret_metrics = pre_eval_to_metrics(results, metric) - - # Because dataset.CLASSES is required for per-eval. - if self.CLASSES is None: - class_names = tuple(range(num_classes)) - else: - class_names = self.CLASSES - - # summary table - ret_metrics_summary = OrderedDict({ - ret_metric: np.round(np.nanmean(ret_metric_value) * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - - # each class table - ret_metrics.pop('aAcc', None) - ret_metrics_class = OrderedDict({ - ret_metric: np.round(ret_metric_value * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - ret_metrics_class.update({'Class': class_names}) - ret_metrics_class.move_to_end('Class', last=False) - - # for logger - class_table_data = PrettyTable() - for key, val in ret_metrics_class.items(): - class_table_data.add_column(key, val) - - summary_table_data = PrettyTable() - for key, val in ret_metrics_summary.items(): - if key == 'aAcc': - summary_table_data.add_column(key, [val]) - else: - summary_table_data.add_column('m' + key, [val]) - - print_log('per class results:', logger) - print_log('\n' + class_table_data.get_string(), logger=logger) - print_log('Summary:', logger) - print_log('\n' + summary_table_data.get_string(), logger=logger) - - # each metric dict - for key, value in ret_metrics_summary.items(): - if key == 'aAcc': - eval_results[key] = value / 100.0 - else: - eval_results['m' + key] = value / 100.0 - - ret_metrics_class.pop('Class', None) - for key, value in ret_metrics_class.items(): - eval_results.update({ - key + '.' + str(name): value[idx] / 100.0 - for idx, name in enumerate(class_names) - }) - - return eval_results diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/dataset_wrappers.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/dataset_wrappers.py deleted file mode 100644 index 1fb089f9f..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/dataset_wrappers.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import bisect -import collections -import copy -from itertools import chain - -import mmcv -import numpy as np -from mmcv.utils import build_from_cfg, print_log -from torch.utils.data.dataset import ConcatDataset as _ConcatDataset - -from .builder import DATASETS, PIPELINES -from .cityscapes import CityscapesDataset - - -@DATASETS.register_module() -class ConcatDataset(_ConcatDataset): - """A wrapper of concatenated dataset. - - Same as :obj:`torch.utils.data.dataset.ConcatDataset`, but - support evaluation and formatting results - - Args: - datasets (list[:obj:`Dataset`]): A list of datasets. - separate_eval (bool): Whether to evaluate the concatenated - dataset results separately, Defaults to True. - """ - - def __init__(self, datasets, separate_eval=True): - super(ConcatDataset, self).__init__(datasets) - self.CLASSES = datasets[0].CLASSES - self.PALETTE = datasets[0].PALETTE - self.separate_eval = separate_eval - assert separate_eval in [True, False], \ - f'separate_eval can only be True or False,' \ - f'but get {separate_eval}' - if any([isinstance(ds, CityscapesDataset) for ds in datasets]): - raise NotImplementedError( - 'Evaluating ConcatDataset containing CityscapesDataset' - 'is not supported!') - - def evaluate(self, results, logger=None, **kwargs): - """Evaluate the results. - - Args: - results (list[tuple[torch.Tensor]] | list[str]]): per image - pre_eval results or predict segmentation map for - computing evaluation metric. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - - Returns: - dict[str: float]: evaluate results of the total dataset - or each separate - dataset if `self.separate_eval=True`. - """ - assert len(results) == self.cumulative_sizes[-1], \ - ('Dataset and results have different sizes: ' - f'{self.cumulative_sizes[-1]} v.s. {len(results)}') - - # Check whether all the datasets support evaluation - for dataset in self.datasets: - assert hasattr(dataset, 'evaluate'), \ - f'{type(dataset)} does not implement evaluate function' - - if self.separate_eval: - dataset_idx = -1 - total_eval_results = dict() - for size, dataset in zip(self.cumulative_sizes, self.datasets): - start_idx = 0 if dataset_idx == -1 else \ - self.cumulative_sizes[dataset_idx] - end_idx = self.cumulative_sizes[dataset_idx + 1] - - results_per_dataset = results[start_idx:end_idx] - print_log( - f'\nEvaluateing {dataset.img_dir} with ' - f'{len(results_per_dataset)} images now', - logger=logger) - - eval_results_per_dataset = dataset.evaluate( - results_per_dataset, logger=logger, **kwargs) - dataset_idx += 1 - for k, v in eval_results_per_dataset.items(): - total_eval_results.update({f'{dataset_idx}_{k}': v}) - - return total_eval_results - - if len(set([type(ds) for ds in self.datasets])) != 1: - raise NotImplementedError( - 'All the datasets should have same types when ' - 'self.separate_eval=False') - else: - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - # merge the generators of gt_seg_maps - gt_seg_maps = chain( - *[dataset.get_gt_seg_maps() for dataset in self.datasets]) - else: - # if the results are `pre_eval` results, - # we do not need gt_seg_maps to evaluate - gt_seg_maps = None - eval_results = self.datasets[0].evaluate( - results, gt_seg_maps=gt_seg_maps, logger=logger, **kwargs) - return eval_results - - def get_dataset_idx_and_sample_idx(self, indice): - """Return dataset and sample index when given an indice of - ConcatDataset. - - Args: - indice (int): indice of sample in ConcatDataset - - Returns: - int: the index of sub dataset the sample belong to - int: the index of sample in its corresponding subset - """ - if indice < 0: - if -indice > len(self): - raise ValueError( - 'absolute value of index should not exceed dataset length') - indice = len(self) + indice - dataset_idx = bisect.bisect_right(self.cumulative_sizes, indice) - if dataset_idx == 0: - sample_idx = indice - else: - sample_idx = indice - self.cumulative_sizes[dataset_idx - 1] - return dataset_idx, sample_idx - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """format result for every sample of ConcatDataset.""" - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].format_results( - [results[i]], - imgfile_prefix + f'/{dataset_idx}', - indices=[sample_idx], - **kwargs) - ret_res.append(res) - return sum(ret_res, []) - - def pre_eval(self, preds, indices): - """do pre eval for every sample of ConcatDataset.""" - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].pre_eval(preds[i], sample_idx) - ret_res.append(res) - return sum(ret_res, []) - - -@DATASETS.register_module() -class RepeatDataset(object): - """A wrapper of repeated dataset. - - The length of repeated dataset will be `times` larger than the original - dataset. This is useful when the data loading time is long but the dataset - is small. Using RepeatDataset can reduce the data loading time between - epochs. - - Args: - dataset (:obj:`Dataset`): The dataset to be repeated. - times (int): Repeat times. - """ - - def __init__(self, dataset, times): - self.dataset = dataset - self.times = times - self.CLASSES = dataset.CLASSES - self.PALETTE = dataset.PALETTE - self._ori_len = len(self.dataset) - - def __getitem__(self, idx): - """Get item from original dataset.""" - return self.dataset[idx % self._ori_len] - - def __len__(self): - """The length is multiplied by ``times``""" - return self.times * self._ori_len - - -@DATASETS.register_module() -class MultiImageMixDataset: - """A wrapper of multiple images mixed dataset. - - Suitable for training on multiple images mixed data augmentation like - mosaic and mixup. For the augmentation pipeline of mixed image data, - the `get_indexes` method needs to be provided to obtain the image - indexes, and you can set `skip_flags` to change the pipeline running - process. - - - Args: - dataset (:obj:`CustomDataset`): The dataset to be mixed. - pipeline (Sequence[dict]): Sequence of transform object or - config dict to be composed. - skip_type_keys (list[str], optional): Sequence of type string to - be skip pipeline. Default to None. - """ - - def __init__(self, dataset, pipeline, skip_type_keys=None): - assert isinstance(pipeline, collections.abc.Sequence) - if skip_type_keys is not None: - assert all([ - isinstance(skip_type_key, str) - for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys - - self.pipeline = [] - self.pipeline_types = [] - for transform in pipeline: - if isinstance(transform, dict): - self.pipeline_types.append(transform['type']) - transform = build_from_cfg(transform, PIPELINES) - self.pipeline.append(transform) - else: - raise TypeError('pipeline must be a dict') - - self.dataset = dataset - self.CLASSES = dataset.CLASSES - self.PALETTE = dataset.PALETTE - self.num_samples = len(dataset) - - def __len__(self): - return self.num_samples - - def __getitem__(self, idx): - results = copy.deepcopy(self.dataset[idx]) - for (transform, transform_type) in zip(self.pipeline, - self.pipeline_types): - if self._skip_type_keys is not None and \ - transform_type in self._skip_type_keys: - continue - - if hasattr(transform, 'get_indexes'): - indexes = transform.get_indexes(self.dataset) - if not isinstance(indexes, collections.abc.Sequence): - indexes = [indexes] - mix_results = [ - copy.deepcopy(self.dataset[index]) for index in indexes - ] - results['mix_results'] = mix_results - - results = transform(results) - - if 'mix_results' in results: - results.pop('mix_results') - - return results - - def update_skip_type_keys(self, skip_type_keys): - """Update skip_type_keys. - - It is called by an external hook. - - Args: - skip_type_keys (list[str], optional): Sequence of type - string to be skip pipeline. - """ - assert all([ - isinstance(skip_type_key, str) for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pascal_context.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pascal_context.py deleted file mode 100644 index efacee0f3..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pascal_context.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class PascalContextDataset(CustomDataset): - """PascalContext dataset. - - In segmentation map annotation for PascalContext, 0 stands for background, - which is included in 60 categories. ``reduce_zero_label`` is fixed to - False. The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is - fixed to '.png'. - - Args: - split (str): Split txt file for PascalContext. - """ - - CLASSES = ('background', 'aeroplane', 'bag', 'bed', 'bedclothes', 'bench', - 'bicycle', 'bird', 'boat', 'book', 'bottle', 'building', 'bus', - 'cabinet', 'car', 'cat', 'ceiling', 'chair', 'cloth', - 'computer', 'cow', 'cup', 'curtain', 'dog', 'door', 'fence', - 'floor', 'flower', 'food', 'grass', 'ground', 'horse', - 'keyboard', 'light', 'motorbike', 'mountain', 'mouse', 'person', - 'plate', 'platform', 'pottedplant', 'road', 'rock', 'sheep', - 'shelves', 'sidewalk', 'sign', 'sky', 'snow', 'sofa', 'table', - 'track', 'train', 'tree', 'truck', 'tvmonitor', 'wall', 'water', - 'window', 'wood') - - PALETTE = [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255]] - - def __init__(self, split, **kwargs): - super(PascalContextDataset, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - split=split, - reduce_zero_label=False, - **kwargs) - assert self.file_client.exists(self.img_dir) and self.split is not None - - -@DATASETS.register_module() -class PascalContextDataset59(CustomDataset): - """PascalContext dataset. - - In segmentation map annotation for PascalContext, 0 stands for background, - which is included in 60 categories. ``reduce_zero_label`` is fixed to - False. The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is - fixed to '.png'. - - Args: - split (str): Split txt file for PascalContext. - """ - - CLASSES = ('aeroplane', 'bag', 'bed', 'bedclothes', 'bench', 'bicycle', - 'bird', 'boat', 'book', 'bottle', 'building', 'bus', 'cabinet', - 'car', 'cat', 'ceiling', 'chair', 'cloth', 'computer', 'cow', - 'cup', 'curtain', 'dog', 'door', 'fence', 'floor', 'flower', - 'food', 'grass', 'ground', 'horse', 'keyboard', 'light', - 'motorbike', 'mountain', 'mouse', 'person', 'plate', 'platform', - 'pottedplant', 'road', 'rock', 'sheep', 'shelves', 'sidewalk', - 'sign', 'sky', 'snow', 'sofa', 'table', 'track', 'train', - 'tree', 'truck', 'tvmonitor', 'wall', 'water', 'window', 'wood') - - PALETTE = [[180, 120, 120], [6, 230, 230], [80, 50, 50], [4, 200, 3], - [120, 120, 80], [140, 140, 140], [204, 5, 255], [230, 230, 230], - [4, 250, 7], [224, 5, 255], [235, 255, 7], [150, 5, 61], - [120, 120, 70], [8, 255, 51], [255, 6, 82], [143, 255, 140], - [204, 255, 4], [255, 51, 7], [204, 70, 3], [0, 102, 200], - [61, 230, 250], [255, 6, 51], [11, 102, 255], [255, 7, 71], - [255, 9, 224], [9, 7, 230], [220, 220, 220], [255, 9, 92], - [112, 9, 255], [8, 255, 214], [7, 255, 224], [255, 184, 6], - [10, 255, 71], [255, 41, 10], [7, 255, 255], [224, 255, 8], - [102, 8, 255], [255, 61, 6], [255, 194, 7], [255, 122, 8], - [0, 255, 20], [255, 8, 41], [255, 5, 153], [6, 51, 255], - [235, 12, 255], [160, 150, 20], [0, 163, 255], [140, 140, 140], - [250, 10, 15], [20, 255, 0], [31, 255, 0], [255, 31, 0], - [255, 224, 0], [153, 255, 0], [0, 0, 255], [255, 71, 0], - [0, 235, 255], [0, 173, 255], [31, 0, 255]] - - def __init__(self, split, **kwargs): - super(PascalContextDataset59, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - split=split, - reduce_zero_label=True, - **kwargs) - assert self.file_client.exists(self.img_dir) and self.split is not None diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/__init__.py deleted file mode 100644 index 8256a6fe2..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .compose import Compose -from .formatting import (Collect, ImageToTensor, ToDataContainer, ToTensor, - Transpose, to_tensor) -from .loading import LoadAnnotations, LoadImageFromFile -from .test_time_aug import MultiScaleFlipAug -from .transforms import (CLAHE, AdjustGamma, Normalize, Pad, - PhotoMetricDistortion, RandomCrop, RandomCutOut, - RandomFlip, RandomMosaic, RandomRotate, Rerange, - Resize, RGB2Gray, SegRescale) - -__all__ = [ - 'Compose', 'to_tensor', 'ToTensor', 'ImageToTensor', 'ToDataContainer', - 'Transpose', 'Collect', 'LoadAnnotations', 'LoadImageFromFile', - 'MultiScaleFlipAug', 'Resize', 'RandomFlip', 'Pad', 'RandomCrop', - 'Normalize', 'SegRescale', 'PhotoMetricDistortion', 'RandomRotate', - 'AdjustGamma', 'CLAHE', 'Rerange', 'RGB2Gray', 'RandomCutOut', - 'RandomMosaic' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/compose.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/compose.py deleted file mode 100644 index 30280c133..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/compose.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections - -from mmcv.utils import build_from_cfg - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class Compose(object): - """Compose multiple transforms sequentially. - - Args: - transforms (Sequence[dict | callable]): Sequence of transform object or - config dict to be composed. - """ - - def __init__(self, transforms): - assert isinstance(transforms, collections.abc.Sequence) - self.transforms = [] - for transform in transforms: - if isinstance(transform, dict): - transform = build_from_cfg(transform, PIPELINES) - self.transforms.append(transform) - elif callable(transform): - self.transforms.append(transform) - else: - raise TypeError('transform must be callable or a dict') - - def __call__(self, data): - """Call function to apply transforms sequentially. - - Args: - data (dict): A result dict contains the data to transform. - - Returns: - dict: Transformed data. - """ - - for t in self.transforms: - data = t(data) - if data is None: - return None - return data - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - format_string += '\n' - format_string += f' {t}' - format_string += '\n)' - return format_string diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/formating.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/formating.py deleted file mode 100644 index f6e53bfeb..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/formating.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -import warnings - -from .formatting import * - -warnings.warn('DeprecationWarning: mmseg.datasets.pipelines.formating will be ' - 'deprecated in 2021, please replace it with ' - 'mmseg.datasets.pipelines.formatting.') diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/formatting.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/formatting.py deleted file mode 100644 index 4e057c1b8..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/formatting.py +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import mmcv -import numpy as np -import torch -from mmcv.parallel import DataContainer as DC - -from ..builder import PIPELINES - - -def to_tensor(data): - """Convert objects of various python types to :obj:`torch.Tensor`. - - Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, - :class:`Sequence`, :class:`int` and :class:`float`. - - Args: - data (torch.Tensor | numpy.ndarray | Sequence | int | float): Data to - be converted. - """ - - if isinstance(data, torch.Tensor): - return data - elif isinstance(data, np.ndarray): - return torch.from_numpy(data) - elif isinstance(data, Sequence) and not mmcv.is_str(data): - return torch.tensor(data) - elif isinstance(data, int): - return torch.LongTensor([data]) - elif isinstance(data, float): - return torch.FloatTensor([data]) - else: - raise TypeError(f'type {type(data)} cannot be converted to tensor.') - - -@PIPELINES.register_module() -class ToTensor(object): - """Convert some results to :obj:`torch.Tensor` by given keys. - - Args: - keys (Sequence[str]): Keys that need to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert data in results to :obj:`torch.Tensor`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted - to :obj:`torch.Tensor`. - """ - - for key in self.keys: - results[key] = to_tensor(results[key]) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class ImageToTensor(object): - """Convert image to :obj:`torch.Tensor` by given keys. - - The dimension order of input image is (H, W, C). The pipeline will convert - it to (C, H, W). If only 2 dimension (H, W) is given, the output would be - (1, H, W). - - Args: - keys (Sequence[str]): Key of images to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - img = results[key] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - results[key] = to_tensor(img.transpose(2, 0, 1)) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class Transpose(object): - """Transpose some results by given keys. - - Args: - keys (Sequence[str]): Keys of results to be transposed. - order (Sequence[int]): Order of transpose. - """ - - def __init__(self, keys, order): - self.keys = keys - self.order = order - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - results[key] = results[key].transpose(self.order) - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, order={self.order})' - - -@PIPELINES.register_module() -class ToDataContainer(object): - """Convert results to :obj:`mmcv.DataContainer` by given fields. - - Args: - fields (Sequence[dict]): Each field is a dict like - ``dict(key='xxx', **kwargs)``. The ``key`` in result will - be converted to :obj:`mmcv.DataContainer` with ``**kwargs``. - Default: ``(dict(key='img', stack=True), - dict(key='gt_semantic_seg'))``. - """ - - def __init__(self, - fields=(dict(key='img', - stack=True), dict(key='gt_semantic_seg'))): - self.fields = fields - - def __call__(self, results): - """Call function to convert data in results to - :obj:`mmcv.DataContainer`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted to - :obj:`mmcv.DataContainer`. - """ - - for field in self.fields: - field = field.copy() - key = field.pop('key') - results[key] = DC(results[key], **field) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(fields={self.fields})' - - -@PIPELINES.register_module() -class DefaultFormatBundle(object): - """Default formatting bundle. - - It simplifies the pipeline of formatting common fields, including "img" - and "gt_semantic_seg". These fields are formatted as follows. - - - img: (1)transpose, (2)to tensor, (3)to DataContainer (stack=True) - - gt_semantic_seg: (1)unsqueeze dim-0 (2)to tensor, - (3)to DataContainer (stack=True) - """ - - def __call__(self, results): - """Call function to transform and format common fields in results. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data that is formatted with - default bundle. - """ - - if 'img' in results: - img = results['img'] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - img = np.ascontiguousarray(img.transpose(2, 0, 1)) - results['img'] = DC(to_tensor(img), stack=True) - if 'gt_semantic_seg' in results: - # convert to long - results['gt_semantic_seg'] = DC( - to_tensor(results['gt_semantic_seg'][None, - ...].astype(np.int64)), - stack=True) - return results - - def __repr__(self): - return self.__class__.__name__ - - -@PIPELINES.register_module() -class Collect(object): - """Collect data from the loader relevant to the specific task. - - This is usually the last stage of the data loader pipeline. Typically keys - is set to some subset of "img", "gt_semantic_seg". - - The "img_meta" item is always populated. The contents of the "img_meta" - dictionary depends on "meta_keys". By default this includes: - - - "img_shape": shape of the image input to the network as a tuple - (h, w, c). Note that images may be zero padded on the bottom/right - if the batch tensor is larger than this shape. - - - "scale_factor": a float indicating the preprocessing scale - - - "flip": a boolean indicating if image flip transform was used - - - "filename": path to the image file - - - "ori_shape": original shape of the image as a tuple (h, w, c) - - - "pad_shape": image shape after padding - - - "img_norm_cfg": a dict of normalization information: - - mean - per channel mean subtraction - - std - per channel std divisor - - to_rgb - bool indicating if bgr was converted to rgb - - Args: - keys (Sequence[str]): Keys of results to be collected in ``data``. - meta_keys (Sequence[str], optional): Meta keys to be converted to - ``mmcv.DataContainer`` and collected in ``data[img_metas]``. - Default: (``filename``, ``ori_filename``, ``ori_shape``, - ``img_shape``, ``pad_shape``, ``scale_factor``, ``flip``, - ``flip_direction``, ``img_norm_cfg``) - """ - - def __init__(self, - keys, - meta_keys=('filename', 'ori_filename', 'ori_shape', - 'img_shape', 'pad_shape', 'scale_factor', 'flip', - 'flip_direction', 'img_norm_cfg')): - self.keys = keys - self.meta_keys = meta_keys - - def __call__(self, results): - """Call function to collect keys in results. The keys in ``meta_keys`` - will be converted to :obj:mmcv.DataContainer. - - Args: - results (dict): Result dict contains the data to collect. - - Returns: - dict: The result dict contains the following keys - - keys in``self.keys`` - - ``img_metas`` - """ - - data = {} - img_meta = {} - for key in self.meta_keys: - img_meta[key] = results[key] - data['img_metas'] = DC(img_meta, cpu_only=True) - for key in self.keys: - data[key] = results[key] - return data - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, meta_keys={self.meta_keys})' diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/loading.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/loading.py deleted file mode 100644 index 572e43431..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/loading.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class LoadImageFromFile(object): - """Load an image from file. - - Required keys are "img_prefix" and "img_info" (a dict that must contain the - key "filename"). Added or updated keys are "filename", "img", "img_shape", - "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`), - "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1). - - Args: - to_float32 (bool): Whether to convert the loaded image to a float32 - numpy array. If set to False, the loaded image is an uint8 array. - Defaults to False. - color_type (str): The flag argument for :func:`mmcv.imfrombytes`. - Defaults to 'color'. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'cv2' - """ - - def __init__(self, - to_float32=False, - color_type='color', - file_client_args=dict(backend='disk'), - imdecode_backend='cv2'): - self.to_float32 = to_float32 - self.color_type = color_type - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call functions to load image and get image meta information. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded image and meta information. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('img_prefix') is not None: - filename = osp.join(results['img_prefix'], - results['img_info']['filename']) - else: - filename = results['img_info']['filename'] - img_bytes = self.file_client.get(filename) - img = mmcv.imfrombytes( - img_bytes, flag=self.color_type, backend=self.imdecode_backend) - if self.to_float32: - img = img.astype(np.float32) - - results['filename'] = filename - results['ori_filename'] = results['img_info']['filename'] - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - # Set initial values for default meta_keys - results['pad_shape'] = img.shape - results['scale_factor'] = 1.0 - num_channels = 1 if len(img.shape) < 3 else img.shape[2] - results['img_norm_cfg'] = dict( - mean=np.zeros(num_channels, dtype=np.float32), - std=np.ones(num_channels, dtype=np.float32), - to_rgb=False) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(to_float32={self.to_float32},' - repr_str += f"color_type='{self.color_type}'," - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str - - -@PIPELINES.register_module() -class LoadAnnotations(object): - """Load annotations for semantic segmentation. - - Args: - reduce_zero_label (bool): Whether reduce all label value by 1. - Usually used for datasets where 0 is background label. - Default: False. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'pillow' - """ - - def __init__(self, - reduce_zero_label=False, - file_client_args=dict(backend='disk'), - imdecode_backend='pillow'): - self.reduce_zero_label = reduce_zero_label - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call function to load multiple types annotations. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded semantic segmentation annotations. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('seg_prefix', None) is not None: - filename = osp.join(results['seg_prefix'], - results['ann_info']['seg_map']) - else: - filename = results['ann_info']['seg_map'] - img_bytes = self.file_client.get(filename) - gt_semantic_seg = mmcv.imfrombytes( - img_bytes, flag='unchanged', - backend=self.imdecode_backend).squeeze().astype(np.uint8) - # modify if custom classes - if results.get('label_map', None) is not None: - # Add deep copy to solve bug of repeatedly - # replace `gt_semantic_seg`, which is reported in - # https://github.com/open-mmlab/mmsegmentation/pull/1445/ - gt_semantic_seg_copy = gt_semantic_seg.copy() - for old_id, new_id in results['label_map'].items(): - gt_semantic_seg[gt_semantic_seg_copy == old_id] = new_id - # reduce zero_label - if self.reduce_zero_label: - # avoid using underflow conversion - gt_semantic_seg[gt_semantic_seg == 0] = 255 - gt_semantic_seg = gt_semantic_seg - 1 - gt_semantic_seg[gt_semantic_seg == 254] = 255 - results['gt_semantic_seg'] = gt_semantic_seg - results['seg_fields'].append('gt_semantic_seg') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(reduce_zero_label={self.reduce_zero_label},' - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py deleted file mode 100644 index 5c17cbbba..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv - -from ..builder import PIPELINES -from .compose import Compose - - -@PIPELINES.register_module() -class MultiScaleFlipAug(object): - """Test-time augmentation with multiple scales and flipping. - - An example configuration is as followed: - - .. code-block:: - - img_scale=(2048, 1024), - img_ratios=[0.5, 1.0], - flip=True, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ] - - After MultiScaleFLipAug with above configuration, the results are wrapped - into lists of the same length as followed: - - .. code-block:: - - dict( - img=[...], - img_shape=[...], - scale=[(1024, 512), (1024, 512), (2048, 1024), (2048, 1024)] - flip=[False, True, False, True] - ... - ) - - Args: - transforms (list[dict]): Transforms to apply in each augmentation. - img_scale (None | tuple | list[tuple]): Images scales for resizing. - img_ratios (float | list[float]): Image ratios for resizing - flip (bool): Whether apply flip augmentation. Default: False. - flip_direction (str | list[str]): Flip augmentation directions, - options are "horizontal" and "vertical". If flip_direction is list, - multiple flip augmentations will be applied. - It has no effect when flip == False. Default: "horizontal". - """ - - def __init__(self, - transforms, - img_scale, - img_ratios=None, - flip=False, - flip_direction='horizontal'): - self.transforms = Compose(transforms) - if img_ratios is not None: - img_ratios = img_ratios if isinstance(img_ratios, - list) else [img_ratios] - assert mmcv.is_list_of(img_ratios, float) - if img_scale is None: - # mode 1: given img_scale=None and a range of image ratio - self.img_scale = None - assert mmcv.is_list_of(img_ratios, float) - elif isinstance(img_scale, tuple) and mmcv.is_list_of( - img_ratios, float): - assert len(img_scale) == 2 - # mode 2: given a scale and a range of image ratio - self.img_scale = [(int(img_scale[0] * ratio), - int(img_scale[1] * ratio)) - for ratio in img_ratios] - else: - # mode 3: given multiple scales - self.img_scale = img_scale if isinstance(img_scale, - list) else [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) or self.img_scale is None - self.flip = flip - self.img_ratios = img_ratios - self.flip_direction = flip_direction if isinstance( - flip_direction, list) else [flip_direction] - assert mmcv.is_list_of(self.flip_direction, str) - if not self.flip and self.flip_direction != ['horizontal']: - warnings.warn( - 'flip_direction has no effect when flip is set to False') - if (self.flip - and not any([t['type'] == 'RandomFlip' for t in transforms])): - warnings.warn( - 'flip has no effect when RandomFlip is not in transforms') - - def __call__(self, results): - """Call function to apply test time augment transforms on results. - - Args: - results (dict): Result dict contains the data to transform. - - Returns: - dict[str: list]: The augmented data, where each value is wrapped - into a list. - """ - - aug_data = [] - if self.img_scale is None and mmcv.is_list_of(self.img_ratios, float): - h, w = results['img'].shape[:2] - img_scale = [(int(w * ratio), int(h * ratio)) - for ratio in self.img_ratios] - else: - img_scale = self.img_scale - flip_aug = [False, True] if self.flip else [False] - for scale in img_scale: - for flip in flip_aug: - for direction in self.flip_direction: - _results = results.copy() - _results['scale'] = scale - _results['flip'] = flip - _results['flip_direction'] = direction - data = self.transforms(_results) - aug_data.append(data) - # list of dict to dict of list - aug_data_dict = {key: [] for key in aug_data[0]} - for data in aug_data: - for key, val in data.items(): - aug_data_dict[key].append(val) - return aug_data_dict - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(transforms={self.transforms}, ' - repr_str += f'img_scale={self.img_scale}, flip={self.flip})' - repr_str += f'flip_direction={self.flip_direction}' - return repr_str diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/transforms.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/transforms.py deleted file mode 100644 index 5673b646f..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/pipelines/transforms.py +++ /dev/null @@ -1,1335 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -import mmcv -import numpy as np -from mmcv.utils import deprecated_api_warning, is_tuple_of -from numpy import random - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class ResizeToMultiple(object): - """Resize images & seg to multiple of divisor. - - Args: - size_divisor (int): images and gt seg maps need to resize to multiple - of size_divisor. Default: 32. - interpolation (str, optional): The interpolation mode of image resize. - Default: None - """ - - def __init__(self, size_divisor=32, interpolation=None): - self.size_divisor = size_divisor - self.interpolation = interpolation - - def __call__(self, results): - """Call function to resize images, semantic segmentation map to - multiple of size divisor. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape' keys are updated. - """ - # Align image to multiple of size divisor. - img = results['img'] - img = mmcv.imresize_to_multiple( - img, - self.size_divisor, - scale_factor=1, - interpolation=self.interpolation - if self.interpolation else 'bilinear') - - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape - - # Align segmentation map to multiple of size divisor. - for key in results.get('seg_fields', []): - gt_seg = results[key] - gt_seg = mmcv.imresize_to_multiple( - gt_seg, - self.size_divisor, - scale_factor=1, - interpolation='nearest') - results[key] = gt_seg - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(size_divisor={self.size_divisor}, ' - f'interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class Resize(object): - """Resize images & seg. - - This transform resizes the input image to some scale. If the input dict - contains the key "scale", then the scale in the input dict is used, - otherwise the specified scale in the init method is used. - - ``img_scale`` can be None, a tuple (single-scale) or a list of tuple - (multi-scale). There are 4 multiscale modes: - - - ``ratio_range is not None``: - 1. When img_scale is None, img_scale is the shape of image in results - (img_scale = results['img'].shape[:2]) and the image is resized based - on the original size. (mode 1) - 2. When img_scale is a tuple (single-scale), randomly sample a ratio from - the ratio range and multiply it with the image scale. (mode 2) - - - ``ratio_range is None and multiscale_mode == "range"``: randomly sample a - scale from the a range. (mode 3) - - - ``ratio_range is None and multiscale_mode == "value"``: randomly sample a - scale from multiple scales. (mode 4) - - Args: - img_scale (tuple or list[tuple]): Images scales for resizing. - Default:None. - multiscale_mode (str): Either "range" or "value". - Default: 'range' - ratio_range (tuple[float]): (min_ratio, max_ratio). - Default: None - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: True - min_size (int, optional): The minimum size for input and the shape - of the image and seg map will not be less than ``min_size``. - As the shape of model input is fixed like 'SETR' and 'BEiT'. - Following the setting in these models, resized images must be - bigger than the crop size in ``slide_inference``. Default: None - """ - - def __init__(self, - img_scale=None, - multiscale_mode='range', - ratio_range=None, - keep_ratio=True, - min_size=None): - if img_scale is None: - self.img_scale = None - else: - if isinstance(img_scale, list): - self.img_scale = img_scale - else: - self.img_scale = [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) - - if ratio_range is not None: - # mode 1: given img_scale=None and a range of image ratio - # mode 2: given a scale and a range of image ratio - assert self.img_scale is None or len(self.img_scale) == 1 - else: - # mode 3 and 4: given multiple scales or a range of scales - assert multiscale_mode in ['value', 'range'] - - self.multiscale_mode = multiscale_mode - self.ratio_range = ratio_range - self.keep_ratio = keep_ratio - self.min_size = min_size - - @staticmethod - def random_select(img_scales): - """Randomly select an img_scale from given candidates. - - Args: - img_scales (list[tuple]): Images scales for selection. - - Returns: - (tuple, int): Returns a tuple ``(img_scale, scale_dix)``, - where ``img_scale`` is the selected image scale and - ``scale_idx`` is the selected index in the given candidates. - """ - - assert mmcv.is_list_of(img_scales, tuple) - scale_idx = np.random.randint(len(img_scales)) - img_scale = img_scales[scale_idx] - return img_scale, scale_idx - - @staticmethod - def random_sample(img_scales): - """Randomly sample an img_scale when ``multiscale_mode=='range'``. - - Args: - img_scales (list[tuple]): Images scale range for sampling. - There must be two tuples in img_scales, which specify the lower - and upper bound of image scales. - - Returns: - (tuple, None): Returns a tuple ``(img_scale, None)``, where - ``img_scale`` is sampled scale and None is just a placeholder - to be consistent with :func:`random_select`. - """ - - assert mmcv.is_list_of(img_scales, tuple) and len(img_scales) == 2 - img_scale_long = [max(s) for s in img_scales] - img_scale_short = [min(s) for s in img_scales] - long_edge = np.random.randint( - min(img_scale_long), - max(img_scale_long) + 1) - short_edge = np.random.randint( - min(img_scale_short), - max(img_scale_short) + 1) - img_scale = (long_edge, short_edge) - return img_scale, None - - @staticmethod - def random_sample_ratio(img_scale, ratio_range): - """Randomly sample an img_scale when ``ratio_range`` is specified. - - A ratio will be randomly sampled from the range specified by - ``ratio_range``. Then it would be multiplied with ``img_scale`` to - generate sampled scale. - - Args: - img_scale (tuple): Images scale base to multiply with ratio. - ratio_range (tuple[float]): The minimum and maximum ratio to scale - the ``img_scale``. - - Returns: - (tuple, None): Returns a tuple ``(scale, None)``, where - ``scale`` is sampled ratio multiplied with ``img_scale`` and - None is just a placeholder to be consistent with - :func:`random_select`. - """ - - assert isinstance(img_scale, tuple) and len(img_scale) == 2 - min_ratio, max_ratio = ratio_range - assert min_ratio <= max_ratio - ratio = np.random.random_sample() * (max_ratio - min_ratio) + min_ratio - scale = int(img_scale[0] * ratio), int(img_scale[1] * ratio) - return scale, None - - def _random_scale(self, results): - """Randomly sample an img_scale according to ``ratio_range`` and - ``multiscale_mode``. - - If ``ratio_range`` is specified, a ratio will be sampled and be - multiplied with ``img_scale``. - If multiple scales are specified by ``img_scale``, a scale will be - sampled according to ``multiscale_mode``. - Otherwise, single scale will be used. - - Args: - results (dict): Result dict from :obj:`dataset`. - - Returns: - dict: Two new keys 'scale` and 'scale_idx` are added into - ``results``, which would be used by subsequent pipelines. - """ - - if self.ratio_range is not None: - if self.img_scale is None: - h, w = results['img'].shape[:2] - scale, scale_idx = self.random_sample_ratio((w, h), - self.ratio_range) - else: - scale, scale_idx = self.random_sample_ratio( - self.img_scale[0], self.ratio_range) - elif len(self.img_scale) == 1: - scale, scale_idx = self.img_scale[0], 0 - elif self.multiscale_mode == 'range': - scale, scale_idx = self.random_sample(self.img_scale) - elif self.multiscale_mode == 'value': - scale, scale_idx = self.random_select(self.img_scale) - else: - raise NotImplementedError - - results['scale'] = scale - results['scale_idx'] = scale_idx - - def _resize_img(self, results): - """Resize images with ``results['scale']``.""" - if self.keep_ratio: - if self.min_size is not None: - # TODO: Now 'min_size' is an 'int' which means the minimum - # shape of images is (min_size, min_size, 3). 'min_size' - # with tuple type will be supported, i.e. the width and - # height are not equal. - if min(results['scale']) < self.min_size: - new_short = self.min_size - else: - new_short = min(results['scale']) - - h, w = results['img'].shape[:2] - if h > w: - new_h, new_w = new_short * h / w, new_short - else: - new_h, new_w = new_short, new_short * w / h - results['scale'] = (new_h, new_w) - - img, scale_factor = mmcv.imrescale( - results['img'], results['scale'], return_scale=True) - # the w_scale and h_scale has minor difference - # a real fix should be done in the mmcv.imrescale in the future - new_h, new_w = img.shape[:2] - h, w = results['img'].shape[:2] - w_scale = new_w / w - h_scale = new_h / h - else: - img, w_scale, h_scale = mmcv.imresize( - results['img'], results['scale'], return_scale=True) - scale_factor = np.array([w_scale, h_scale, w_scale, h_scale], - dtype=np.float32) - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape # in case that there is no padding - results['scale_factor'] = scale_factor - results['keep_ratio'] = self.keep_ratio - - def _resize_seg(self, results): - """Resize semantic segmentation map with ``results['scale']``.""" - for key in results.get('seg_fields', []): - if self.keep_ratio: - gt_seg = mmcv.imrescale( - results[key], results['scale'], interpolation='nearest') - else: - gt_seg = mmcv.imresize( - results[key], results['scale'], interpolation='nearest') - results[key] = gt_seg - - def __call__(self, results): - """Call function to resize images, bounding boxes, masks, semantic - segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape', 'scale_factor', - 'keep_ratio' keys are added into result dict. - """ - - if 'scale' not in results: - self._random_scale(results) - self._resize_img(results) - self._resize_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(img_scale={self.img_scale}, ' - f'multiscale_mode={self.multiscale_mode}, ' - f'ratio_range={self.ratio_range}, ' - f'keep_ratio={self.keep_ratio})') - return repr_str - - -@PIPELINES.register_module() -class RandomFlip(object): - """Flip the image & seg. - - If the input dict contains the key "flip", then the flag will be used, - otherwise it will be randomly decided by a ratio specified in the init - method. - - Args: - prob (float, optional): The flipping probability. Default: None. - direction(str, optional): The flipping direction. Options are - 'horizontal' and 'vertical'. Default: 'horizontal'. - """ - - @deprecated_api_warning({'flip_ratio': 'prob'}, cls_name='RandomFlip') - def __init__(self, prob=None, direction='horizontal'): - self.prob = prob - self.direction = direction - if prob is not None: - assert prob >= 0 and prob <= 1 - assert direction in ['horizontal', 'vertical'] - - def __call__(self, results): - """Call function to flip bounding boxes, masks, semantic segmentation - maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Flipped results, 'flip', 'flip_direction' keys are added into - result dict. - """ - - if 'flip' not in results: - flip = True if np.random.rand() < self.prob else False - results['flip'] = flip - if 'flip_direction' not in results: - results['flip_direction'] = self.direction - if results['flip']: - # flip image - results['img'] = mmcv.imflip( - results['img'], direction=results['flip_direction']) - - # flip segs - for key in results.get('seg_fields', []): - # use copy() to make numpy stride positive - results[key] = mmcv.imflip( - results[key], direction=results['flip_direction']).copy() - return results - - def __repr__(self): - return self.__class__.__name__ + f'(prob={self.prob})' - - -@PIPELINES.register_module() -class Pad(object): - """Pad the image & mask. - - There are two padding modes: (1) pad to a fixed size and (2) pad to the - minimum size that is divisible by some number. - Added keys are "pad_shape", "pad_fixed_size", "pad_size_divisor", - - Args: - size (tuple, optional): Fixed padding size. - size_divisor (int, optional): The divisor of padded size. - pad_val (float, optional): Padding value. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - """ - - def __init__(self, - size=None, - size_divisor=None, - pad_val=0, - seg_pad_val=255): - self.size = size - self.size_divisor = size_divisor - self.pad_val = pad_val - self.seg_pad_val = seg_pad_val - # only one of size and size_divisor should be valid - assert size is not None or size_divisor is not None - assert size is None or size_divisor is None - - def _pad_img(self, results): - """Pad images according to ``self.size``.""" - if self.size is not None: - padded_img = mmcv.impad( - results['img'], shape=self.size, pad_val=self.pad_val) - elif self.size_divisor is not None: - padded_img = mmcv.impad_to_multiple( - results['img'], self.size_divisor, pad_val=self.pad_val) - results['img'] = padded_img - results['pad_shape'] = padded_img.shape - results['pad_fixed_size'] = self.size - results['pad_size_divisor'] = self.size_divisor - - def _pad_seg(self, results): - """Pad masks according to ``results['pad_shape']``.""" - for key in results.get('seg_fields', []): - results[key] = mmcv.impad( - results[key], - shape=results['pad_shape'][:2], - pad_val=self.seg_pad_val) - - def __call__(self, results): - """Call function to pad images, masks, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Updated result dict. - """ - - self._pad_img(results) - self._pad_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(size={self.size}, size_divisor={self.size_divisor}, ' \ - f'pad_val={self.pad_val})' - return repr_str - - -@PIPELINES.register_module() -class Normalize(object): - """Normalize the image. - - Added key is "img_norm_cfg". - - Args: - mean (sequence): Mean values of 3 channels. - std (sequence): Std values of 3 channels. - to_rgb (bool): Whether to convert the image from BGR to RGB, - default is true. - """ - - def __init__(self, mean, std, to_rgb=True): - self.mean = np.array(mean, dtype=np.float32) - self.std = np.array(std, dtype=np.float32) - self.to_rgb = to_rgb - - def __call__(self, results): - """Call function to normalize images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Normalized results, 'img_norm_cfg' key is added into - result dict. - """ - - results['img'] = mmcv.imnormalize(results['img'], self.mean, self.std, - self.to_rgb) - results['img_norm_cfg'] = dict( - mean=self.mean, std=self.std, to_rgb=self.to_rgb) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(mean={self.mean}, std={self.std}, to_rgb=' \ - f'{self.to_rgb})' - return repr_str - - -@PIPELINES.register_module() -class Rerange(object): - """Rerange the image pixel value. - - Args: - min_value (float or int): Minimum value of the reranged image. - Default: 0. - max_value (float or int): Maximum value of the reranged image. - Default: 255. - """ - - def __init__(self, min_value=0, max_value=255): - assert isinstance(min_value, float) or isinstance(min_value, int) - assert isinstance(max_value, float) or isinstance(max_value, int) - assert min_value < max_value - self.min_value = min_value - self.max_value = max_value - - def __call__(self, results): - """Call function to rerange images. - - Args: - results (dict): Result dict from loading pipeline. - Returns: - dict: Reranged results. - """ - - img = results['img'] - img_min_value = np.min(img) - img_max_value = np.max(img) - - assert img_min_value < img_max_value - # rerange to [0, 1] - img = (img - img_min_value) / (img_max_value - img_min_value) - # rerange to [min_value, max_value] - img = img * (self.max_value - self.min_value) + self.min_value - results['img'] = img - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(min_value={self.min_value}, max_value={self.max_value})' - return repr_str - - -@PIPELINES.register_module() -class CLAHE(object): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - """ - - def __init__(self, clip_limit=40.0, tile_grid_size=(8, 8)): - assert isinstance(clip_limit, (float, int)) - self.clip_limit = clip_limit - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - self.tile_grid_size = tile_grid_size - - def __call__(self, results): - """Call function to Use CLAHE method process images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - for i in range(results['img'].shape[2]): - results['img'][:, :, i] = mmcv.clahe( - np.array(results['img'][:, :, i], dtype=np.uint8), - self.clip_limit, self.tile_grid_size) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(clip_limit={self.clip_limit}, '\ - f'tile_grid_size={self.tile_grid_size})' - return repr_str - - -@PIPELINES.register_module() -class RandomCrop(object): - """Random crop the image & seg. - - Args: - crop_size (tuple): Expected size after cropping, (h, w). - cat_max_ratio (float): The maximum ratio that single category could - occupy. - """ - - def __init__(self, crop_size, cat_max_ratio=1., ignore_index=255): - assert crop_size[0] > 0 and crop_size[1] > 0 - self.crop_size = crop_size - self.cat_max_ratio = cat_max_ratio - self.ignore_index = ignore_index - - def get_crop_bbox(self, img): - """Randomly get a crop bounding box.""" - margin_h = max(img.shape[0] - self.crop_size[0], 0) - margin_w = max(img.shape[1] - self.crop_size[1], 0) - offset_h = np.random.randint(0, margin_h + 1) - offset_w = np.random.randint(0, margin_w + 1) - crop_y1, crop_y2 = offset_h, offset_h + self.crop_size[0] - crop_x1, crop_x2 = offset_w, offset_w + self.crop_size[1] - - return crop_y1, crop_y2, crop_x1, crop_x2 - - def crop(self, img, crop_bbox): - """Crop from ``img``""" - crop_y1, crop_y2, crop_x1, crop_x2 = crop_bbox - img = img[crop_y1:crop_y2, crop_x1:crop_x2, ...] - return img - - def __call__(self, results): - """Call function to randomly crop images, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Randomly cropped results, 'img_shape' key in result dict is - updated according to crop size. - """ - - img = results['img'] - crop_bbox = self.get_crop_bbox(img) - if self.cat_max_ratio < 1.: - # Repeat 10 times - for _ in range(10): - seg_temp = self.crop(results['gt_semantic_seg'], crop_bbox) - labels, cnt = np.unique(seg_temp, return_counts=True) - cnt = cnt[labels != self.ignore_index] - if len(cnt) > 1 and np.max(cnt) / np.sum( - cnt) < self.cat_max_ratio: - break - crop_bbox = self.get_crop_bbox(img) - - # crop the image - img = self.crop(img, crop_bbox) - img_shape = img.shape - results['img'] = img - results['img_shape'] = img_shape - - # crop semantic seg - for key in results.get('seg_fields', []): - results[key] = self.crop(results[key], crop_bbox) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(crop_size={self.crop_size})' - - -@PIPELINES.register_module() -class RandomRotate(object): - """Rotate the image & seg. - - Args: - prob (float): The rotation probability. - degree (float, tuple[float]): Range of degrees to select from. If - degree is a number instead of tuple like (min, max), - the range of degree will be (``-degree``, ``+degree``) - pad_val (float, optional): Padding value of image. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. Default: None. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. Default: False - """ - - def __init__(self, - prob, - degree, - pad_val=0, - seg_pad_val=255, - center=None, - auto_bound=False): - self.prob = prob - assert prob >= 0 and prob <= 1 - if isinstance(degree, (float, int)): - assert degree > 0, f'degree {degree} should be positive' - self.degree = (-degree, degree) - else: - self.degree = degree - assert len(self.degree) == 2, f'degree {self.degree} should be a ' \ - f'tuple of (min, max)' - self.pal_val = pad_val - self.seg_pad_val = seg_pad_val - self.center = center - self.auto_bound = auto_bound - - def __call__(self, results): - """Call function to rotate image, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Rotated results. - """ - - rotate = True if np.random.rand() < self.prob else False - degree = np.random.uniform(min(*self.degree), max(*self.degree)) - if rotate: - # rotate image - results['img'] = mmcv.imrotate( - results['img'], - angle=degree, - border_value=self.pal_val, - center=self.center, - auto_bound=self.auto_bound) - - # rotate segs - for key in results.get('seg_fields', []): - results[key] = mmcv.imrotate( - results[key], - angle=degree, - border_value=self.seg_pad_val, - center=self.center, - auto_bound=self.auto_bound, - interpolation='nearest') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' \ - f'degree={self.degree}, ' \ - f'pad_val={self.pal_val}, ' \ - f'seg_pad_val={self.seg_pad_val}, ' \ - f'center={self.center}, ' \ - f'auto_bound={self.auto_bound})' - return repr_str - - -@PIPELINES.register_module() -class RGB2Gray(object): - """Convert RGB image to grayscale image. - - This transform calculate the weighted mean of input image channels with - ``weights`` and then expand the channels to ``out_channels``. When - ``out_channels`` is None, the number of output channels is the same as - input channels. - - Args: - out_channels (int): Expected number of output channels after - transforming. Default: None. - weights (tuple[float]): The weights to calculate the weighted mean. - Default: (0.299, 0.587, 0.114). - """ - - def __init__(self, out_channels=None, weights=(0.299, 0.587, 0.114)): - assert out_channels is None or out_channels > 0 - self.out_channels = out_channels - assert isinstance(weights, tuple) - for item in weights: - assert isinstance(item, (float, int)) - self.weights = weights - - def __call__(self, results): - """Call function to convert RGB image to grayscale image. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with grayscale image. - """ - img = results['img'] - assert len(img.shape) == 3 - assert img.shape[2] == len(self.weights) - weights = np.array(self.weights).reshape((1, 1, -1)) - img = (img * weights).sum(2, keepdims=True) - if self.out_channels is None: - img = img.repeat(weights.shape[2], axis=2) - else: - img = img.repeat(self.out_channels, axis=2) - - results['img'] = img - results['img_shape'] = img.shape - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(out_channels={self.out_channels}, ' \ - f'weights={self.weights})' - return repr_str - - -@PIPELINES.register_module() -class AdjustGamma(object): - """Using gamma correction to process the image. - - Args: - gamma (float or int): Gamma value used in gamma correction. - Default: 1.0. - """ - - def __init__(self, gamma=1.0): - assert isinstance(gamma, float) or isinstance(gamma, int) - assert gamma > 0 - self.gamma = gamma - inv_gamma = 1.0 / gamma - self.table = np.array([(i / 255.0)**inv_gamma * 255 - for i in np.arange(256)]).astype('uint8') - - def __call__(self, results): - """Call function to process the image with gamma correction. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - results['img'] = mmcv.lut_transform( - np.array(results['img'], dtype=np.uint8), self.table) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(gamma={self.gamma})' - - -@PIPELINES.register_module() -class SegRescale(object): - """Rescale semantic segmentation maps. - - Args: - scale_factor (float): The scale factor of the final output. - """ - - def __init__(self, scale_factor=1): - self.scale_factor = scale_factor - - def __call__(self, results): - """Call function to scale the semantic segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with semantic segmentation map scaled. - """ - for key in results.get('seg_fields', []): - if self.scale_factor != 1: - results[key] = mmcv.imrescale( - results[key], self.scale_factor, interpolation='nearest') - return results - - def __repr__(self): - return self.__class__.__name__ + f'(scale_factor={self.scale_factor})' - - -@PIPELINES.register_module() -class PhotoMetricDistortion(object): - """Apply photometric distortion to image sequentially, every transformation - is applied with a probability of 0.5. The position of random contrast is in - second or second to last. - - 1. random brightness - 2. random contrast (mode 0) - 3. convert color from BGR to HSV - 4. random saturation - 5. random hue - 6. convert color from HSV to BGR - 7. random contrast (mode 1) - - Args: - brightness_delta (int): delta of brightness. - contrast_range (tuple): range of contrast. - saturation_range (tuple): range of saturation. - hue_delta (int): delta of hue. - """ - - def __init__(self, - brightness_delta=32, - contrast_range=(0.5, 1.5), - saturation_range=(0.5, 1.5), - hue_delta=18): - self.brightness_delta = brightness_delta - self.contrast_lower, self.contrast_upper = contrast_range - self.saturation_lower, self.saturation_upper = saturation_range - self.hue_delta = hue_delta - - def convert(self, img, alpha=1, beta=0): - """Multiple with alpha and add beat with clip.""" - img = img.astype(np.float32) * alpha + beta - img = np.clip(img, 0, 255) - return img.astype(np.uint8) - - def brightness(self, img): - """Brightness distortion.""" - if random.randint(2): - return self.convert( - img, - beta=random.uniform(-self.brightness_delta, - self.brightness_delta)) - return img - - def contrast(self, img): - """Contrast distortion.""" - if random.randint(2): - return self.convert( - img, - alpha=random.uniform(self.contrast_lower, self.contrast_upper)) - return img - - def saturation(self, img): - """Saturation distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, 1] = self.convert( - img[:, :, 1], - alpha=random.uniform(self.saturation_lower, - self.saturation_upper)) - img = mmcv.hsv2bgr(img) - return img - - def hue(self, img): - """Hue distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, - 0] = (img[:, :, 0].astype(int) + - random.randint(-self.hue_delta, self.hue_delta)) % 180 - img = mmcv.hsv2bgr(img) - return img - - def __call__(self, results): - """Call function to perform photometric distortion on images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images distorted. - """ - - img = results['img'] - # random brightness - img = self.brightness(img) - - # mode == 0 --> do random contrast first - # mode == 1 --> do random contrast last - mode = random.randint(2) - if mode == 1: - img = self.contrast(img) - - # random saturation - img = self.saturation(img) - - # random hue - img = self.hue(img) - - # random contrast - if mode == 0: - img = self.contrast(img) - - results['img'] = img - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(brightness_delta={self.brightness_delta}, ' - f'contrast_range=({self.contrast_lower}, ' - f'{self.contrast_upper}), ' - f'saturation_range=({self.saturation_lower}, ' - f'{self.saturation_upper}), ' - f'hue_delta={self.hue_delta})') - return repr_str - - -@PIPELINES.register_module() -class RandomCutOut(object): - """CutOut operation. - - Randomly drop some regions of image used in - `Cutout `_. - Args: - prob (float): cutout probability. - n_holes (int | tuple[int, int]): Number of regions to be dropped. - If it is given as a list, number of holes will be randomly - selected from the closed interval [`n_holes[0]`, `n_holes[1]`]. - cutout_shape (tuple[int, int] | list[tuple[int, int]]): The candidate - shape of dropped regions. It can be `tuple[int, int]` to use a - fixed cutout shape, or `list[tuple[int, int]]` to randomly choose - shape from the list. - cutout_ratio (tuple[float, float] | list[tuple[float, float]]): The - candidate ratio of dropped regions. It can be `tuple[float, float]` - to use a fixed ratio or `list[tuple[float, float]]` to randomly - choose ratio from the list. Please note that `cutout_shape` - and `cutout_ratio` cannot be both given at the same time. - fill_in (tuple[float, float, float] | tuple[int, int, int]): The value - of pixel to fill in the dropped regions. Default: (0, 0, 0). - seg_fill_in (int): The labels of pixel to fill in the dropped regions. - If seg_fill_in is None, skip. Default: None. - """ - - def __init__(self, - prob, - n_holes, - cutout_shape=None, - cutout_ratio=None, - fill_in=(0, 0, 0), - seg_fill_in=None): - - assert 0 <= prob and prob <= 1 - assert (cutout_shape is None) ^ (cutout_ratio is None), \ - 'Either cutout_shape or cutout_ratio should be specified.' - assert (isinstance(cutout_shape, (list, tuple)) - or isinstance(cutout_ratio, (list, tuple))) - if isinstance(n_holes, tuple): - assert len(n_holes) == 2 and 0 <= n_holes[0] < n_holes[1] - else: - n_holes = (n_holes, n_holes) - if seg_fill_in is not None: - assert (isinstance(seg_fill_in, int) and 0 <= seg_fill_in - and seg_fill_in <= 255) - self.prob = prob - self.n_holes = n_holes - self.fill_in = fill_in - self.seg_fill_in = seg_fill_in - self.with_ratio = cutout_ratio is not None - self.candidates = cutout_ratio if self.with_ratio else cutout_shape - if not isinstance(self.candidates, list): - self.candidates = [self.candidates] - - def __call__(self, results): - """Call function to drop some regions of image.""" - cutout = True if np.random.rand() < self.prob else False - if cutout: - h, w, c = results['img'].shape - n_holes = np.random.randint(self.n_holes[0], self.n_holes[1] + 1) - for _ in range(n_holes): - x1 = np.random.randint(0, w) - y1 = np.random.randint(0, h) - index = np.random.randint(0, len(self.candidates)) - if not self.with_ratio: - cutout_w, cutout_h = self.candidates[index] - else: - cutout_w = int(self.candidates[index][0] * w) - cutout_h = int(self.candidates[index][1] * h) - - x2 = np.clip(x1 + cutout_w, 0, w) - y2 = np.clip(y1 + cutout_h, 0, h) - results['img'][y1:y2, x1:x2, :] = self.fill_in - - if self.seg_fill_in is not None: - for key in results.get('seg_fields', []): - results[key][y1:y2, x1:x2] = self.seg_fill_in - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' - repr_str += f'n_holes={self.n_holes}, ' - repr_str += (f'cutout_ratio={self.candidates}, ' if self.with_ratio - else f'cutout_shape={self.candidates}, ') - repr_str += f'fill_in={self.fill_in}, ' - repr_str += f'seg_fill_in={self.seg_fill_in})' - return repr_str - - -@PIPELINES.register_module() -class RandomMosaic(object): - """Mosaic augmentation. Given 4 images, mosaic transform combines them into - one output image. The output image is composed of the parts from each sub- - image. - - .. code:: text - - mosaic transform - center_x - +------------------------------+ - | pad | pad | - | +-----------+ | - | | | | - | | image1 |--------+ | - | | | | | - | | | image2 | | - center_y |----+-------------+-----------| - | | cropped | | - |pad | image3 | image4 | - | | | | - +----|-------------+-----------+ - | | - +-------------+ - - The mosaic transform steps are as follows: - 1. Choose the mosaic center as the intersections of 4 images - 2. Get the left top image according to the index, and randomly - sample another 3 images from the custom dataset. - 3. Sub image will be cropped if image is larger than mosaic patch - - Args: - prob (float): mosaic probability. - img_scale (Sequence[int]): Image size after mosaic pipeline of - a single image. The size of the output image is four times - that of a single image. The output image comprises 4 single images. - Default: (640, 640). - center_ratio_range (Sequence[float]): Center ratio range of mosaic - output. Default: (0.5, 1.5). - pad_val (int): Pad value. Default: 0. - seg_pad_val (int): Pad value of segmentation map. Default: 255. - """ - - def __init__(self, - prob, - img_scale=(640, 640), - center_ratio_range=(0.5, 1.5), - pad_val=0, - seg_pad_val=255): - assert 0 <= prob and prob <= 1 - assert isinstance(img_scale, tuple) - self.prob = prob - self.img_scale = img_scale - self.center_ratio_range = center_ratio_range - self.pad_val = pad_val - self.seg_pad_val = seg_pad_val - - def __call__(self, results): - """Call function to make a mosaic of image. - - Args: - results (dict): Result dict. - - Returns: - dict: Result dict with mosaic transformed. - """ - mosaic = True if np.random.rand() < self.prob else False - if mosaic: - results = self._mosaic_transform_img(results) - results = self._mosaic_transform_seg(results) - return results - - def get_indexes(self, dataset): - """Call function to collect indexes. - - Args: - dataset (:obj:`MultiImageMixDataset`): The dataset. - - Returns: - list: indexes. - """ - - indexes = [random.randint(0, len(dataset)) for _ in range(3)] - return indexes - - def _mosaic_transform_img(self, results): - """Mosaic transform function. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - if len(results['img'].shape) == 3: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2), 3), - self.pad_val, - dtype=results['img'].dtype) - else: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.pad_val, - dtype=results['img'].dtype) - - # mosaic center x, y - self.center_x = int( - random.uniform(*self.center_ratio_range) * self.img_scale[1]) - self.center_y = int( - random.uniform(*self.center_ratio_range) * self.img_scale[0]) - center_position = (self.center_x, self.center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - result_patch = copy.deepcopy(results) - else: - result_patch = copy.deepcopy(results['mix_results'][i - 1]) - - img_i = result_patch['img'] - h_i, w_i = img_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - img_i = mmcv.imresize( - img_i, (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i))) - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, img_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_img[y1_p:y2_p, x1_p:x2_p] = img_i[y1_c:y2_c, x1_c:x2_c] - - results['img'] = mosaic_img - results['img_shape'] = mosaic_img.shape - results['ori_shape'] = mosaic_img.shape - - return results - - def _mosaic_transform_seg(self, results): - """Mosaic transform function for label annotations. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - for key in results.get('seg_fields', []): - mosaic_seg = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.seg_pad_val, - dtype=results[key].dtype) - - # mosaic center x, y - center_position = (self.center_x, self.center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - result_patch = copy.deepcopy(results) - else: - result_patch = copy.deepcopy(results['mix_results'][i - 1]) - - gt_seg_i = result_patch[key] - h_i, w_i = gt_seg_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - gt_seg_i = mmcv.imresize( - gt_seg_i, - (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i)), - interpolation='nearest') - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, gt_seg_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_seg[y1_p:y2_p, x1_p:x2_p] = gt_seg_i[y1_c:y2_c, - x1_c:x2_c] - - results[key] = mosaic_seg - - return results - - def _mosaic_combine(self, loc, center_position_xy, img_shape_wh): - """Calculate global coordinate of mosaic image and local coordinate of - cropped sub-image. - - Args: - loc (str): Index for the sub-image, loc in ('top_left', - 'top_right', 'bottom_left', 'bottom_right'). - center_position_xy (Sequence[float]): Mixing center for 4 images, - (x, y). - img_shape_wh (Sequence[int]): Width and height of sub-image - - Returns: - tuple[tuple[float]]: Corresponding coordinate of pasting and - cropping - - paste_coord (tuple): paste corner coordinate in mosaic image. - - crop_coord (tuple): crop corner coordinate in mosaic image. - """ - - assert loc in ('top_left', 'top_right', 'bottom_left', 'bottom_right') - if loc == 'top_left': - # index0 to top left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - center_position_xy[0], \ - center_position_xy[1] - crop_coord = img_shape_wh[0] - (x2 - x1), img_shape_wh[1] - ( - y2 - y1), img_shape_wh[0], img_shape_wh[1] - - elif loc == 'top_right': - # index1 to top right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - center_position_xy[1] - crop_coord = 0, img_shape_wh[1] - (y2 - y1), min( - img_shape_wh[0], x2 - x1), img_shape_wh[1] - - elif loc == 'bottom_left': - # index2 to bottom left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - center_position_xy[1], \ - center_position_xy[0], \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = img_shape_wh[0] - (x2 - x1), 0, img_shape_wh[0], min( - y2 - y1, img_shape_wh[1]) - - else: - # index3 to bottom right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - center_position_xy[1], \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = 0, 0, min(img_shape_wh[0], - x2 - x1), min(y2 - y1, img_shape_wh[1]) - - paste_coord = x1, y1, x2, y2 - return paste_coord, crop_coord - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' - repr_str += f'img_scale={self.img_scale}, ' - repr_str += f'center_ratio_range={self.center_ratio_range}, ' - repr_str += f'pad_val={self.pad_val}, ' - repr_str += f'seg_pad_val={self.pad_val})' - return repr_str diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/samplers/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/samplers/__init__.py deleted file mode 100644 index da09effaf..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/samplers/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .distributed_sampler import DistributedSampler - -__all__ = ['DistributedSampler'] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/samplers/distributed_sampler.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/samplers/distributed_sampler.py deleted file mode 100644 index d1a13c716..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/samplers/distributed_sampler.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from __future__ import division -from typing import Iterator, Optional - -import torch -from torch.utils.data import Dataset -from torch.utils.data import DistributedSampler as _DistributedSampler - -from mmseg.core.utils import sync_random_seed - - -class DistributedSampler(_DistributedSampler): - """DistributedSampler inheriting from - `torch.utils.data.DistributedSampler`. - - Args: - datasets (Dataset): the dataset will be loaded. - num_replicas (int, optional): Number of processes participating in - distributed training. By default, world_size is retrieved from the - current distributed group. - rank (int, optional): Rank of the current process within num_replicas. - By default, rank is retrieved from the current distributed group. - shuffle (bool): If True (default), sampler will shuffle the indices. - seed (int): random seed used to shuffle the sampler if - :attr:`shuffle=True`. This number should be identical across all - processes in the distributed group. Default: ``0``. - """ - - def __init__(self, - dataset: Dataset, - num_replicas: Optional[int] = None, - rank: Optional[int] = None, - shuffle: bool = True, - seed=0) -> None: - super().__init__( - dataset, num_replicas=num_replicas, rank=rank, shuffle=shuffle) - - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - - def __iter__(self) -> Iterator: - """ - Yields: - Iterator: iterator of indices for rank. - """ - # deterministically shuffle based on epoch - if self.shuffle: - g = torch.Generator() - # When :attr:`shuffle=True`, this ensures all replicas - # use a different random ordering for each epoch. - # Otherwise, the next iteration of this sampler will - # yield the same ordering. - g.manual_seed(self.epoch + self.seed) - indices = torch.randperm(len(self.dataset), generator=g).tolist() - else: - indices = torch.arange(len(self.dataset)).tolist() - - # add extra samples to make it evenly divisible - indices += indices[:(self.total_size - len(indices))] - assert len(indices) == self.total_size - - # subsample - indices = indices[self.rank:self.total_size:self.num_replicas] - assert len(indices) == self.num_samples - - return iter(indices) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/voc.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/voc.py deleted file mode 100644 index 9eecc344f..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/datasets/voc.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class PascalVOCDataset(CustomDataset): - """Pascal VOC dataset. - - Args: - split (str): Split txt file for Pascal VOC. - """ - - CLASSES = ('background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', - 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', - 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', - 'train', 'tvmonitor') - - PALETTE = [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], - [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], - [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], - [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], - [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] - - def __init__(self, split, **kwargs): - - if "img_dir" in kwargs: - image_dir = kwargs["img_dir"] - if not osp.join(kwargs['data_root'], image_dir): - image_dir = "images" - if osp.join(kwargs['data_root'], image_dir): - kwargs["img_dir"] = image_dir - - super(PascalVOCDataset, self).__init__( - img_suffix='.jpg', seg_map_suffix='.png', split=split, **kwargs) - - assert osp.exists(self.img_dir) and self.split is not None diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/__init__.py deleted file mode 100644 index 87d8108e3..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .backbones import * # noqa: F401,F403 -from .builder import (BACKBONES, HEADS, LOSSES, SEGMENTORS, build_backbone, - build_head, build_loss, build_segmentor) -from .decode_heads import * # noqa: F401,F403 -from .losses import * # noqa: F401,F403 -from .necks import * # noqa: F401,F403 -from .segmentors import * # noqa: F401,F403 - -__all__ = [ - 'BACKBONES', 'HEADS', 'LOSSES', 'SEGMENTORS', 'build_backbone', - 'build_head', 'build_loss', 'build_segmentor' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/__init__.py deleted file mode 100644 index f842617d8..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .resnest import ResNeSt -from .resnet import ResNet, ResNetV1c, ResNetV1d -from .resnext import ResNeXt diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/resnest.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/resnest.py deleted file mode 100644 index 91952c2ca..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/resnest.py +++ /dev/null @@ -1,318 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import torch -import torch.nn as nn -import torch.nn.functional as F -import torch.utils.checkpoint as cp -from mmcv.cnn import build_conv_layer, build_norm_layer - -from ..builder import BACKBONES -from ..utils import ResLayer -from .resnet import Bottleneck as _Bottleneck -from .resnet import ResNetV1d - - -class RSoftmax(nn.Module): - """Radix Softmax module in ``SplitAttentionConv2d``. - - Args: - radix (int): Radix of input. - groups (int): Groups of input. - """ - - def __init__(self, radix, groups): - super().__init__() - self.radix = radix - self.groups = groups - - def forward(self, x): - batch = x.size(0) - if self.radix > 1: - x = x.view(batch, self.groups, self.radix, -1).transpose(1, 2) - x = F.softmax(x, dim=1) - x = x.reshape(batch, -1) - else: - x = torch.sigmoid(x) - return x - - -class SplitAttentionConv2d(nn.Module): - """Split-Attention Conv2d in ResNeSt. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int | tuple[int]): Same as nn.Conv2d. - stride (int | tuple[int]): Same as nn.Conv2d. - padding (int | tuple[int]): Same as nn.Conv2d. - dilation (int | tuple[int]): Same as nn.Conv2d. - groups (int): Same as nn.Conv2d. - radix (int): Radix of SpltAtConv2d. Default: 2 - reduction_factor (int): Reduction factor of inter_channels. Default: 4. - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - dcn (dict): Config dict for DCN. Default: None. - """ - - def __init__(self, - in_channels, - channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - radix=2, - reduction_factor=4, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None): - super(SplitAttentionConv2d, self).__init__() - inter_channels = max(in_channels * radix // reduction_factor, 32) - self.radix = radix - self.groups = groups - self.channels = channels - self.with_dcn = dcn is not None - self.dcn = dcn - fallback_on_stride = False - if self.with_dcn: - fallback_on_stride = self.dcn.pop('fallback_on_stride', False) - if self.with_dcn and not fallback_on_stride: - assert conv_cfg is None, 'conv_cfg must be None for DCN' - conv_cfg = dcn - self.conv = build_conv_layer( - conv_cfg, - in_channels, - channels * radix, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups * radix, - bias=False) - self.norm0_name, norm0 = build_norm_layer( - norm_cfg, channels * radix, postfix=0) - self.add_module(self.norm0_name, norm0) - self.relu = nn.ReLU(inplace=True) - self.fc1 = build_conv_layer( - None, channels, inter_channels, 1, groups=self.groups) - self.norm1_name, norm1 = build_norm_layer( - norm_cfg, inter_channels, postfix=1) - self.add_module(self.norm1_name, norm1) - self.fc2 = build_conv_layer( - None, inter_channels, channels * radix, 1, groups=self.groups) - self.rsoftmax = RSoftmax(radix, groups) - - @property - def norm0(self): - """nn.Module: the normalization layer named "norm0" """ - return getattr(self, self.norm0_name) - - @property - def norm1(self): - """nn.Module: the normalization layer named "norm1" """ - return getattr(self, self.norm1_name) - - def forward(self, x): - x = self.conv(x) - x = self.norm0(x) - x = self.relu(x) - - batch, rchannel = x.shape[:2] - batch = x.size(0) - if self.radix > 1: - splits = x.view(batch, self.radix, -1, *x.shape[2:]) - gap = splits.sum(dim=1) - else: - gap = x - gap = F.adaptive_avg_pool2d(gap, 1) - gap = self.fc1(gap) - - gap = self.norm1(gap) - gap = self.relu(gap) - - atten = self.fc2(gap) - atten = self.rsoftmax(atten).view(batch, -1, 1, 1) - - if self.radix > 1: - attens = atten.view(batch, self.radix, -1, *atten.shape[2:]) - out = torch.sum(attens * splits, dim=1) - else: - out = atten * x - return out.contiguous() - - -class Bottleneck(_Bottleneck): - """Bottleneck block for ResNeSt. - - Args: - inplane (int): Input planes of this block. - planes (int): Middle planes of this block. - groups (int): Groups of conv2. - width_per_group (int): Width per group of conv2. 64x4d indicates - ``groups=64, width_per_group=4`` and 32x8d indicates - ``groups=32, width_per_group=8``. - radix (int): Radix of SpltAtConv2d. Default: 2 - reduction_factor (int): Reduction factor of inter_channels in - SplitAttentionConv2d. Default: 4. - avg_down_stride (bool): Whether to use average pool for stride in - Bottleneck. Default: True. - kwargs (dict): Key word arguments for base class. - """ - expansion = 4 - - def __init__(self, - inplanes, - planes, - groups=1, - base_width=4, - base_channels=64, - radix=2, - reduction_factor=4, - avg_down_stride=True, - **kwargs): - """Bottleneck block for ResNeSt.""" - super(Bottleneck, self).__init__(inplanes, planes, **kwargs) - - if groups == 1: - width = self.planes - else: - width = math.floor(self.planes * - (base_width / base_channels)) * groups - - self.avg_down_stride = avg_down_stride and self.conv2_stride > 1 - - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, width, postfix=1) - self.norm3_name, norm3 = build_norm_layer( - self.norm_cfg, self.planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - self.conv_cfg, - self.inplanes, - width, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - self.with_modulated_dcn = False - self.conv2 = SplitAttentionConv2d( - width, - width, - kernel_size=3, - stride=1 if self.avg_down_stride else self.conv2_stride, - padding=self.dilation, - dilation=self.dilation, - groups=groups, - radix=radix, - reduction_factor=reduction_factor, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - dcn=self.dcn) - delattr(self, self.norm2_name) - - if self.avg_down_stride: - self.avd_layer = nn.AvgPool2d(3, self.conv2_stride, padding=1) - - self.conv3 = build_conv_layer( - self.conv_cfg, - width, - self.planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - def forward(self, x): - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv1_plugin_names) - - out = self.conv2(out) - - if self.avg_down_stride: - out = self.avd_layer(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv2_plugin_names) - - out = self.conv3(out) - out = self.norm3(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv3_plugin_names) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -@BACKBONES.register_module() -class ResNeSt(ResNetV1d): - """ResNeSt backbone. - - This backbone is the implementation of `ResNeSt: - Split-Attention Networks `_. - - Args: - groups (int): Number of groups of Bottleneck. Default: 1 - base_width (int): Base width of Bottleneck. Default: 4 - radix (int): Radix of SpltAtConv2d. Default: 2 - reduction_factor (int): Reduction factor of inter_channels in - SplitAttentionConv2d. Default: 4. - avg_down_stride (bool): Whether to use average pool for stride in - Bottleneck. Default: True. - kwargs (dict): Keyword arguments for ResNet. - """ - - arch_settings = { - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)), - 200: (Bottleneck, (3, 24, 36, 3)) - } - - def __init__(self, - groups=1, - base_width=4, - radix=2, - reduction_factor=4, - avg_down_stride=True, - **kwargs): - self.groups = groups - self.base_width = base_width - self.radix = radix - self.reduction_factor = reduction_factor - self.avg_down_stride = avg_down_stride - super(ResNeSt, self).__init__(**kwargs) - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``.""" - return ResLayer( - groups=self.groups, - base_width=self.base_width, - base_channels=self.base_channels, - radix=self.radix, - reduction_factor=self.reduction_factor, - avg_down_stride=self.avg_down_stride, - **kwargs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/resnet.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/resnet.py deleted file mode 100644 index e8b961d5f..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/resnet.py +++ /dev/null @@ -1,714 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.utils.checkpoint as cp -from mmcv.cnn import build_conv_layer, build_norm_layer, build_plugin_layer -from mmcv.runner import BaseModule -from mmcv.utils.parrots_wrapper import _BatchNorm - -from ..builder import BACKBONES -from ..utils import ResLayer - - -class BasicBlock(BaseModule): - """Basic block for ResNet.""" - - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(BasicBlock, self).__init__(init_cfg) - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - 3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - self.add_module(self.norm1_name, norm1) - self.conv2 = build_conv_layer( - conv_cfg, planes, planes, 3, padding=1, bias=False) - self.add_module(self.norm2_name, norm2) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.norm2(out) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -class Bottleneck(BaseModule): - """Bottleneck block for ResNet. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if it is - "caffe", the stride-two layer is the first 1x1 conv layer. - """ - - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(Bottleneck, self).__init__(init_cfg) - assert style in ['pytorch', 'caffe'] - assert dcn is None or isinstance(dcn, dict) - assert plugins is None or isinstance(plugins, list) - if plugins is not None: - allowed_position = ['after_conv1', 'after_conv2', 'after_conv3'] - assert all(p['position'] in allowed_position for p in plugins) - - self.inplanes = inplanes - self.planes = planes - self.stride = stride - self.dilation = dilation - self.style = style - self.with_cp = with_cp - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.dcn = dcn - self.with_dcn = dcn is not None - self.plugins = plugins - self.with_plugins = plugins is not None - - if self.with_plugins: - # collect plugins for conv1/conv2/conv3 - self.after_conv1_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv1' - ] - self.after_conv2_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv2' - ] - self.after_conv3_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv3' - ] - - if self.style == 'pytorch': - self.conv1_stride = 1 - self.conv2_stride = stride - else: - self.conv1_stride = stride - self.conv2_stride = 1 - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - self.norm3_name, norm3 = build_norm_layer( - norm_cfg, planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - fallback_on_stride = False - if self.with_dcn: - fallback_on_stride = dcn.pop('fallback_on_stride', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = build_conv_layer( - conv_cfg, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - else: - assert self.conv_cfg is None, 'conv_cfg must be None for DCN' - self.conv2 = build_conv_layer( - dcn, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.add_module(self.norm2_name, norm2) - self.conv3 = build_conv_layer( - conv_cfg, - planes, - planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - - if self.with_plugins: - self.after_conv1_plugin_names = self.make_block_plugins( - planes, self.after_conv1_plugins) - self.after_conv2_plugin_names = self.make_block_plugins( - planes, self.after_conv2_plugins) - self.after_conv3_plugin_names = self.make_block_plugins( - planes * self.expansion, self.after_conv3_plugins) - - def make_block_plugins(self, in_channels, plugins): - """make plugins for block. - - Args: - in_channels (int): Input channels of plugin. - plugins (list[dict]): List of plugins cfg to build. - - Returns: - list[str]: List of the names of plugin. - """ - assert isinstance(plugins, list) - plugin_names = [] - for plugin in plugins: - plugin = plugin.copy() - name, layer = build_plugin_layer( - plugin, - in_channels=in_channels, - postfix=plugin.pop('postfix', '')) - assert not hasattr(self, name), f'duplicate plugin {name}' - self.add_module(name, layer) - plugin_names.append(name) - return plugin_names - - def forward_plugin(self, x, plugin_names): - """Forward function for plugins.""" - out = x - for name in plugin_names: - out = getattr(self, name)(x) - return out - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - @property - def norm3(self): - """nn.Module: normalization layer after the third convolution layer""" - return getattr(self, self.norm3_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv1_plugin_names) - - out = self.conv2(out) - out = self.norm2(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv2_plugin_names) - - out = self.conv3(out) - out = self.norm3(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv3_plugin_names) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -@BACKBONES.register_module() -class ResNet(BaseModule): - """ResNet backbone. - - This backbone is the improved implementation of `Deep Residual Learning - for Image Recognition `_. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - in_channels (int): Number of input image channels. Default: 3. - stem_channels (int): Number of stem channels. Default: 64. - base_channels (int): Number of base channels of res layer. Default: 64. - num_stages (int): Resnet stages, normally 4. Default: 4. - strides (Sequence[int]): Strides of the first block of each stage. - Default: (1, 2, 2, 2). - dilations (Sequence[int]): Dilation of each stage. - Default: (1, 1, 1, 1). - out_indices (Sequence[int]): Output from which stages. - Default: (0, 1, 2, 3). - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. Default: 'pytorch'. - deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv. - Default: False. - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False. - frozen_stages (int): Stages to be frozen (stop grad and set eval mode). - -1 means not freezing any parameters. Default: -1. - conv_cfg (dict | None): Dictionary to construct and config conv layer. - When conv_cfg is None, cfg will be set to dict(type='Conv2d'). - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN', requires_grad=True). - norm_eval (bool): Whether to set norm layers to eval mode, namely, - freeze running stats (mean and var). Note: Effect on Batch Norm - and its variants only. Default: False. - dcn (dict | None): Dictionary to construct and config DCN conv layer. - When dcn is not None, conv_cfg must be None. Default: None. - stage_with_dcn (Sequence[bool]): Whether to set DCN conv for each - stage. The length of stage_with_dcn is equal to num_stages. - Default: (False, False, False, False). - plugins (list[dict]): List of plugins for stages, each dict contains: - - - cfg (dict, required): Cfg dict to build plugin. - - - position (str, required): Position inside block to insert plugin, - options: 'after_conv1', 'after_conv2', 'after_conv3'. - - - stages (tuple[bool], optional): Stages to apply plugin, length - should be same as 'num_stages'. - Default: None. - multi_grid (Sequence[int]|None): Multi grid dilation rates of last - stage. Default: None. - contract_dilation (bool): Whether contract first dilation of each layer - Default: False. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - zero_init_residual (bool): Whether to use zero init for last norm layer - in resblocks to let them behave as identity. Default: True. - pretrained (str, optional): model pretrained path. Default: None. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Example: - >>> from mmseg.models import ResNet - >>> import torch - >>> self = ResNet(depth=18) - >>> self.eval() - >>> inputs = torch.rand(1, 3, 32, 32) - >>> level_outputs = self.forward(inputs) - >>> for level_out in level_outputs: - ... print(tuple(level_out.shape)) - (1, 64, 8, 8) - (1, 128, 4, 4) - (1, 256, 2, 2) - (1, 512, 1, 1) - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - in_channels=3, - stem_channels=64, - base_channels=64, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - deep_stem=False, - avg_down=False, - frozen_stages=-1, - conv_cfg=None, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=False, - dcn=None, - stage_with_dcn=(False, False, False, False), - plugins=None, - multi_grid=None, - contract_dilation=False, - with_cp=False, - zero_init_residual=True, - pretrained=None, - init_cfg=None): - super(ResNet, self).__init__(init_cfg) - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - - self.pretrained = pretrained - self.zero_init_residual = zero_init_residual - block_init_cfg = None - assert not (init_cfg and pretrained), \ - 'init_cfg and pretrained cannot be setting at the same time' - if isinstance(pretrained, str): - warnings.warn('DeprecationWarning: pretrained is a deprecated, ' - 'please use "init_cfg" instead') - self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - elif pretrained is None: - if init_cfg is None: - self.init_cfg = [ - dict(type='Kaiming', layer='Conv2d'), - dict( - type='Constant', - val=1, - layer=['_BatchNorm', 'GroupNorm']) - ] - block = self.arch_settings[depth][0] - if self.zero_init_residual: - if block is BasicBlock: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm2')) - elif block is Bottleneck: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm3')) - else: - raise TypeError('pretrained must be a str or None') - - self.depth = depth - self.stem_channels = stem_channels - self.base_channels = base_channels - self.num_stages = num_stages - assert num_stages >= 1 and num_stages <= 4 - self.strides = strides - self.dilations = dilations - assert len(strides) == len(dilations) == num_stages - self.out_indices = out_indices - assert max(out_indices) < num_stages - self.style = style - self.deep_stem = deep_stem - self.avg_down = avg_down - self.frozen_stages = frozen_stages - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.with_cp = with_cp - self.norm_eval = norm_eval - self.dcn = dcn - self.stage_with_dcn = stage_with_dcn - if dcn is not None: - assert len(stage_with_dcn) == num_stages - self.plugins = plugins - self.multi_grid = multi_grid - self.contract_dilation = contract_dilation - self.block, stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - self.inplanes = stem_channels - - self._make_stem_layer(in_channels, stem_channels) - - self.res_layers = [] - for i, num_blocks in enumerate(self.stage_blocks): - stride = strides[i] - dilation = dilations[i] - dcn = self.dcn if self.stage_with_dcn[i] else None - if plugins is not None: - stage_plugins = self.make_stage_plugins(plugins, i) - else: - stage_plugins = None - # multi grid is applied to last layer only - stage_multi_grid = multi_grid if i == len( - self.stage_blocks) - 1 else None - planes = base_channels * 2**i - res_layer = self.make_res_layer( - block=self.block, - inplanes=self.inplanes, - planes=planes, - num_blocks=num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - avg_down=self.avg_down, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - dcn=dcn, - plugins=stage_plugins, - multi_grid=stage_multi_grid, - contract_dilation=contract_dilation, - init_cfg=block_init_cfg) - self.inplanes = planes * self.block.expansion - layer_name = f'layer{i+1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self._freeze_stages() - - self.feat_dim = self.block.expansion * base_channels * 2**( - len(self.stage_blocks) - 1) - - def make_stage_plugins(self, plugins, stage_idx): - """make plugins for ResNet 'stage_idx'th stage . - - Currently we support to insert 'context_block', - 'empirical_attention_block', 'nonlocal_block' into the backbone like - ResNet/ResNeXt. They could be inserted after conv1/conv2/conv3 of - Bottleneck. - - An example of plugins format could be : - >>> plugins=[ - ... dict(cfg=dict(type='xxx', arg1='xxx'), - ... stages=(False, True, True, True), - ... position='after_conv2'), - ... dict(cfg=dict(type='yyy'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='1'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='2'), - ... stages=(True, True, True, True), - ... position='after_conv3') - ... ] - >>> self = ResNet(depth=18) - >>> stage_plugins = self.make_stage_plugins(plugins, 0) - >>> assert len(stage_plugins) == 3 - - Suppose 'stage_idx=0', the structure of blocks in the stage would be: - conv1-> conv2->conv3->yyy->zzz1->zzz2 - Suppose 'stage_idx=1', the structure of blocks in the stage would be: - conv1-> conv2->xxx->conv3->yyy->zzz1->zzz2 - - If stages is missing, the plugin would be applied to all stages. - - Args: - plugins (list[dict]): List of plugins cfg to build. The postfix is - required if multiple same type plugins are inserted. - stage_idx (int): Index of stage to build - - Returns: - list[dict]: Plugins for current stage - """ - stage_plugins = [] - for plugin in plugins: - plugin = plugin.copy() - stages = plugin.pop('stages', None) - assert stages is None or len(stages) == self.num_stages - # whether to insert plugin into current stage - if stages is None or stages[stage_idx]: - stage_plugins.append(plugin) - - return stage_plugins - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``.""" - return ResLayer(**kwargs) - - @property - def norm1(self): - """nn.Module: the normalization layer named "norm1" """ - return getattr(self, self.norm1_name) - - def _make_stem_layer(self, in_channels, stem_channels): - """Make stem layer for ResNet.""" - if self.deep_stem: - self.stem = nn.Sequential( - build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels // 2, - kernel_size=3, - stride=2, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels // 2, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels)[1], - nn.ReLU(inplace=True)) - else: - self.conv1 = build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels, - kernel_size=7, - stride=2, - padding=3, - bias=False) - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, stem_channels, postfix=1) - self.add_module(self.norm1_name, norm1) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - def _freeze_stages(self): - """Freeze stages param and norm stats.""" - if self.frozen_stages >= 0: - if self.deep_stem: - self.stem.eval() - for param in self.stem.parameters(): - param.requires_grad = False - else: - self.norm1.eval() - for m in [self.conv1, self.norm1]: - for param in m.parameters(): - param.requires_grad = False - - for i in range(1, self.frozen_stages + 1): - m = getattr(self, f'layer{i}') - m.eval() - for param in m.parameters(): - param.requires_grad = False - - def forward(self, x): - """Forward function.""" - if self.deep_stem: - x = self.stem(x) - else: - x = self.conv1(x) - x = self.norm1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - return tuple(outs) - - def train(self, mode=True): - """Convert the model into training mode while keep normalization layer - freezed.""" - super(ResNet, self).train(mode) - self._freeze_stages() - if mode and self.norm_eval: - for m in self.modules(): - # trick: eval have effect on BatchNorm only - if isinstance(m, _BatchNorm): - m.eval() - - -@BACKBONES.register_module() -class ResNetV1c(ResNet): - """ResNetV1c variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1c replaces the 7x7 conv in - the input stem with three 3x3 convs. For more details please refer to `Bag - of Tricks for Image Classification with Convolutional Neural Networks - `_. - """ - - def __init__(self, **kwargs): - super(ResNetV1c, self).__init__( - deep_stem=True, avg_down=False, **kwargs) - - -@BACKBONES.register_module() -class ResNetV1d(ResNet): - """ResNetV1d variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1d replaces the 7x7 conv in - the input stem with three 3x3 convs. And in the downsampling block, a 2x2 - avg_pool with stride 2 is added before conv, whose stride is changed to 1. - """ - - def __init__(self, **kwargs): - super(ResNetV1d, self).__init__( - deep_stem=True, avg_down=True, **kwargs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/resnext.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/resnext.py deleted file mode 100644 index 805c27bf3..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/resnext.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from mmcv.cnn import build_conv_layer, build_norm_layer - -from ..builder import BACKBONES -from ..utils import ResLayer -from .resnet import Bottleneck as _Bottleneck -from .resnet import ResNet - - -class Bottleneck(_Bottleneck): - """Bottleneck block for ResNeXt. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if it is - "caffe", the stride-two layer is the first 1x1 conv layer. - """ - - def __init__(self, - inplanes, - planes, - groups=1, - base_width=4, - base_channels=64, - **kwargs): - super(Bottleneck, self).__init__(inplanes, planes, **kwargs) - - if groups == 1: - width = self.planes - else: - width = math.floor(self.planes * - (base_width / base_channels)) * groups - - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, width, postfix=1) - self.norm2_name, norm2 = build_norm_layer( - self.norm_cfg, width, postfix=2) - self.norm3_name, norm3 = build_norm_layer( - self.norm_cfg, self.planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - self.conv_cfg, - self.inplanes, - width, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - fallback_on_stride = False - self.with_modulated_dcn = False - if self.with_dcn: - fallback_on_stride = self.dcn.pop('fallback_on_stride', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = build_conv_layer( - self.conv_cfg, - width, - width, - kernel_size=3, - stride=self.conv2_stride, - padding=self.dilation, - dilation=self.dilation, - groups=groups, - bias=False) - else: - assert self.conv_cfg is None, 'conv_cfg must be None for DCN' - self.conv2 = build_conv_layer( - self.dcn, - width, - width, - kernel_size=3, - stride=self.conv2_stride, - padding=self.dilation, - dilation=self.dilation, - groups=groups, - bias=False) - - self.add_module(self.norm2_name, norm2) - self.conv3 = build_conv_layer( - self.conv_cfg, - width, - self.planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - -@BACKBONES.register_module() -class ResNeXt(ResNet): - """ResNeXt backbone. - - This backbone is the implementation of `Aggregated - Residual Transformations for Deep Neural - Networks `_. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - in_channels (int): Number of input image channels. Normally 3. - num_stages (int): Resnet stages, normally 4. - groups (int): Group of resnext. - base_width (int): Base width of resnext. - strides (Sequence[int]): Strides of the first block of each stage. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - norm_cfg (dict): dictionary to construct and config norm layer. - norm_eval (bool): Whether to set norm layers to eval mode, namely, - freeze running stats (mean and var). Note: Effect on Batch Norm - and its variants only. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. - zero_init_residual (bool): whether to use zero init for last norm layer - in resblocks to let them behave as identity. - - Example: - >>> from mmseg.models import ResNeXt - >>> import torch - >>> self = ResNeXt(depth=50) - >>> self.eval() - >>> inputs = torch.rand(1, 3, 32, 32) - >>> level_outputs = self.forward(inputs) - >>> for level_out in level_outputs: - ... print(tuple(level_out.shape)) - (1, 256, 8, 8) - (1, 512, 4, 4) - (1, 1024, 2, 2) - (1, 2048, 1, 1) - """ - - arch_settings = { - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, groups=1, base_width=4, **kwargs): - self.groups = groups - self.base_width = base_width - super(ResNeXt, self).__init__(**kwargs) - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``""" - return ResLayer( - groups=self.groups, - base_width=self.base_width, - base_channels=self.base_channels, - **kwargs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/timm_backbone.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/timm_backbone.py deleted file mode 100644 index 01b29fc5e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/backbones/timm_backbone.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -try: - import timm -except ImportError: - timm = None - -from mmcv.cnn.bricks.registry import NORM_LAYERS -from mmcv.runner import BaseModule - -from ..builder import BACKBONES - - -@BACKBONES.register_module() -class TIMMBackbone(BaseModule): - """Wrapper to use backbones from timm library. More details can be found in - `timm `_ . - - Args: - model_name (str): Name of timm model to instantiate. - pretrained (bool): Load pretrained weights if True. - checkpoint_path (str): Path of checkpoint to load after - model is initialized. - in_channels (int): Number of input image channels. Default: 3. - init_cfg (dict, optional): Initialization config dict - **kwargs: Other timm & model specific arguments. - """ - - def __init__( - self, - model_name, - features_only=True, - pretrained=True, - checkpoint_path='', - in_channels=3, - init_cfg=None, - **kwargs, - ): - if timm is None: - raise RuntimeError('timm is not installed') - super(TIMMBackbone, self).__init__(init_cfg) - if 'norm_layer' in kwargs: - kwargs['norm_layer'] = NORM_LAYERS.get(kwargs['norm_layer']) - self.timm_model = timm.create_model( - model_name=model_name, - features_only=features_only, - pretrained=pretrained, - in_chans=in_channels, - checkpoint_path=checkpoint_path, - **kwargs, - ) - - # Make unused parameters None - self.timm_model.global_pool = None - self.timm_model.fc = None - self.timm_model.classifier = None - - # Hack to use pretrained weights from timm - if pretrained or checkpoint_path: - self._is_init = True - - def forward(self, x): - features = self.timm_model(x) - return features diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/builder.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/builder.py deleted file mode 100644 index 5e18e4e64..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/builder.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -from mmcv.cnn import MODELS as MMCV_MODELS -from mmcv.cnn.bricks.registry import ATTENTION as MMCV_ATTENTION -from mmcv.utils import Registry - -MODELS = Registry('models', parent=MMCV_MODELS) -ATTENTION = Registry('attention', parent=MMCV_ATTENTION) - -BACKBONES = MODELS -NECKS = MODELS -HEADS = MODELS -LOSSES = MODELS -SEGMENTORS = MODELS - - -def build_backbone(cfg): - """Build backbone.""" - return BACKBONES.build(cfg) - - -def build_neck(cfg): - """Build neck.""" - return NECKS.build(cfg) - - -def build_head(cfg): - """Build head.""" - return HEADS.build(cfg) - - -def build_loss(cfg): - """Build loss.""" - return LOSSES.build(cfg) - - -def build_segmentor(cfg, train_cfg=None, test_cfg=None): - """Build segmentor.""" - if train_cfg is not None or test_cfg is not None: - warnings.warn( - 'train_cfg and test_cfg is deprecated, ' - 'please specify them in model', UserWarning) - assert cfg.get('train_cfg') is None or train_cfg is None, \ - 'train_cfg specified in both outer field and model field ' - assert cfg.get('test_cfg') is None or test_cfg is None, \ - 'test_cfg specified in both outer field and model field ' - return SEGMENTORS.build( - cfg, default_args=dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/__init__.py deleted file mode 100644 index b2ac10d17..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .apc_head import APCHead -from .aspp_head import ASPPHead -from .cc_head import CCHead -from .decode_head import BaseDecodeHead -from .fcn_head import FCNHead \ No newline at end of file diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/apc_head.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/apc_head.py deleted file mode 100644 index 3198fd188..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/apc_head.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule - -from mmseg.ops import resize -from ..builder import HEADS -from .decode_head import BaseDecodeHead - - -class ACM(nn.Module): - """Adaptive Context Module used in APCNet. - - Args: - pool_scale (int): Pooling scale used in Adaptive Context - Module to extract region features. - fusion (bool): Add one conv to fuse residual feature. - in_channels (int): Input channels. - channels (int): Channels after modules, before conv_seg. - conv_cfg (dict | None): Config of conv layers. - norm_cfg (dict | None): Config of norm layers. - act_cfg (dict): Config of activation layers. - """ - - def __init__(self, pool_scale, fusion, in_channels, channels, conv_cfg, - norm_cfg, act_cfg): - super(ACM, self).__init__() - self.pool_scale = pool_scale - self.fusion = fusion - self.in_channels = in_channels - self.channels = channels - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.pooled_redu_conv = ConvModule( - self.in_channels, - self.channels, - 1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - self.input_redu_conv = ConvModule( - self.in_channels, - self.channels, - 1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - self.global_info = ConvModule( - self.channels, - self.channels, - 1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - self.gla = nn.Conv2d(self.channels, self.pool_scale**2, 1, 1, 0) - - self.residual_conv = ConvModule( - self.channels, - self.channels, - 1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - if self.fusion: - self.fusion_conv = ConvModule( - self.channels, - self.channels, - 1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - def forward(self, x): - """Forward function.""" - pooled_x = F.adaptive_avg_pool2d(x, self.pool_scale) - # [batch_size, channels, h, w] - x = self.input_redu_conv(x) - # [batch_size, channels, pool_scale, pool_scale] - pooled_x = self.pooled_redu_conv(pooled_x) - batch_size = x.size(0) - # [batch_size, pool_scale * pool_scale, channels] - pooled_x = pooled_x.view(batch_size, self.channels, - -1).permute(0, 2, 1).contiguous() - # [batch_size, h * w, pool_scale * pool_scale] - affinity_matrix = self.gla(x + resize( - self.global_info(F.adaptive_avg_pool2d(x, 1)), size=x.shape[2:]) - ).permute(0, 2, 3, 1).reshape( - batch_size, -1, self.pool_scale**2) - affinity_matrix = F.sigmoid(affinity_matrix) - # [batch_size, h * w, channels] - z_out = torch.matmul(affinity_matrix, pooled_x) - # [batch_size, channels, h * w] - z_out = z_out.permute(0, 2, 1).contiguous() - # [batch_size, channels, h, w] - z_out = z_out.view(batch_size, self.channels, x.size(2), x.size(3)) - z_out = self.residual_conv(z_out) - z_out = F.relu(z_out + x) - if self.fusion: - z_out = self.fusion_conv(z_out) - - return z_out - - -@HEADS.register_module() -class APCHead(BaseDecodeHead): - """Adaptive Pyramid Context Network for Semantic Segmentation. - - This head is the implementation of - `APCNet `_. - - Args: - pool_scales (tuple[int]): Pooling scales used in Adaptive Context - Module. Default: (1, 2, 3, 6). - fusion (bool): Add one conv to fuse residual feature. - """ - - def __init__(self, pool_scales=(1, 2, 3, 6), fusion=True, **kwargs): - super(APCHead, self).__init__(**kwargs) - assert isinstance(pool_scales, (list, tuple)) - self.pool_scales = pool_scales - self.fusion = fusion - acm_modules = [] - for pool_scale in self.pool_scales: - acm_modules.append( - ACM(pool_scale, - self.fusion, - self.in_channels, - self.channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - self.acm_modules = nn.ModuleList(acm_modules) - self.bottleneck = ConvModule( - self.in_channels + len(pool_scales) * self.channels, - self.channels, - 3, - padding=1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - def forward(self, inputs): - """Forward function.""" - x = self._transform_inputs(inputs) - acm_outs = [x] - for acm_module in self.acm_modules: - acm_outs.append(acm_module(x)) - acm_outs = torch.cat(acm_outs, dim=1) - output = self.bottleneck(acm_outs) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/aspp_head.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/aspp_head.py deleted file mode 100644 index 7059aee96..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/aspp_head.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule - -from mmseg.ops import resize -from ..builder import HEADS -from .decode_head import BaseDecodeHead - - -class ASPPModule(nn.ModuleList): - """Atrous Spatial Pyramid Pooling (ASPP) Module. - - Args: - dilations (tuple[int]): Dilation rate of each layer. - in_channels (int): Input channels. - channels (int): Channels after modules, before conv_seg. - conv_cfg (dict|None): Config of conv layers. - norm_cfg (dict|None): Config of norm layers. - act_cfg (dict): Config of activation layers. - """ - - def __init__(self, dilations, in_channels, channels, conv_cfg, norm_cfg, - act_cfg): - super(ASPPModule, self).__init__() - self.dilations = dilations - self.in_channels = in_channels - self.channels = channels - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - for dilation in dilations: - self.append( - ConvModule( - self.in_channels, - self.channels, - 1 if dilation == 1 else 3, - dilation=dilation, - padding=0 if dilation == 1 else dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - - def forward(self, x): - """Forward function.""" - aspp_outs = [] - for aspp_module in self: - aspp_outs.append(aspp_module(x)) - - return aspp_outs - - -@HEADS.register_module() -class ASPPHead(BaseDecodeHead): - """Rethinking Atrous Convolution for Semantic Image Segmentation. - - This head is the implementation of `DeepLabV3 - `_. - - Args: - dilations (tuple[int]): Dilation rates for ASPP module. - Default: (1, 6, 12, 18). - """ - - def __init__(self, dilations=(1, 6, 12, 18), **kwargs): - super(ASPPHead, self).__init__(**kwargs) - assert isinstance(dilations, (list, tuple)) - self.dilations = dilations - self.image_pool = nn.Sequential( - nn.AdaptiveAvgPool2d(1), - ConvModule( - self.in_channels, - self.channels, - 1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - self.aspp_modules = ASPPModule( - dilations, - self.in_channels, - self.channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - self.bottleneck = ConvModule( - (len(dilations) + 1) * self.channels, - self.channels, - 3, - padding=1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - def _forward_feature(self, inputs): - """Forward function for feature maps before classifying each pixel with - ``self.cls_seg`` fc. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - feats (Tensor): A tensor of shape (batch_size, self.channels, - H, W) which is feature map for last layer of decoder head. - """ - x = self._transform_inputs(inputs) - aspp_outs = [ - resize( - self.image_pool(x), - size=x.size()[2:], - mode='bilinear', - align_corners=self.align_corners) - ] - aspp_outs.extend(self.aspp_modules(x)) - aspp_outs = torch.cat(aspp_outs, dim=1) - feats = self.bottleneck(aspp_outs) - return feats - - def forward(self, inputs): - """Forward function.""" - output = self._forward_feature(inputs) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/cc_head.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/cc_head.py deleted file mode 100644 index ed19eb46d..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/cc_head.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import HEADS -from .fcn_head import FCNHead - -try: - from mmcv.ops import CrissCrossAttention -except ModuleNotFoundError: - CrissCrossAttention = None - - -@HEADS.register_module() -class CCHead(FCNHead): - """CCNet: Criss-Cross Attention for Semantic Segmentation. - - This head is the implementation of `CCNet - `_. - - Args: - recurrence (int): Number of recurrence of Criss Cross Attention - module. Default: 2. - """ - - def __init__(self, recurrence=2, **kwargs): - if CrissCrossAttention is None: - raise RuntimeError('Please install mmcv-full for ' - 'CrissCrossAttention ops') - super(CCHead, self).__init__(num_convs=2, **kwargs) - self.recurrence = recurrence - self.cca = CrissCrossAttention(self.channels) - - def forward(self, inputs): - """Forward function.""" - x = self._transform_inputs(inputs) - output = self.convs[0](x) - for _ in range(self.recurrence): - output = self.cca(output) - output = self.convs[1](output) - if self.concat_input: - output = self.conv_cat(torch.cat([x, output], dim=1)) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/decode_head.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/decode_head.py deleted file mode 100644 index d08b1d0b6..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/decode_head.py +++ /dev/null @@ -1,266 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -import torch -import torch.nn as nn -from mmcv.runner import BaseModule, auto_fp16, force_fp32 - -from mmseg.core import build_pixel_sampler -from mmseg.ops import resize -from ..builder import build_loss -from ..losses import accuracy - - -class BaseDecodeHead(BaseModule, metaclass=ABCMeta): - """Base class for BaseDecodeHead. - - Args: - in_channels (int|Sequence[int]): Input channels. - channels (int): Channels after modules, before conv_seg. - num_classes (int): Number of classes. - dropout_ratio (float): Ratio of dropout layer. Default: 0.1. - conv_cfg (dict|None): Config of conv layers. Default: None. - norm_cfg (dict|None): Config of norm layers. Default: None. - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU') - in_index (int|Sequence[int]): Input feature index. Default: -1 - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - Default: None. - loss_decode (dict | Sequence[dict]): Config of decode loss. - The `loss_name` is property of corresponding loss function which - could be shown in training log. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - e.g. dict(type='CrossEntropyLoss'), - [dict(type='CrossEntropyLoss', loss_name='loss_ce'), - dict(type='DiceLoss', loss_name='loss_dice')] - Default: dict(type='CrossEntropyLoss'). - ignore_index (int | None): The label index to be ignored. When using - masked BCE loss, ignore_index should be set to None. Default: 255. - sampler (dict|None): The config of segmentation map sampler. - Default: None. - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - """ - - def __init__(self, - in_channels, - channels, - *, - num_classes, - dropout_ratio=0.1, - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - in_index=-1, - input_transform=None, - loss_decode=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0), - ignore_index=255, - sampler=None, - align_corners=False, - init_cfg=dict( - type='Normal', std=0.01, override=dict(name='conv_seg'))): - super(BaseDecodeHead, self).__init__(init_cfg) - self._init_inputs(in_channels, in_index, input_transform) - self.channels = channels - self.num_classes = num_classes - self.dropout_ratio = dropout_ratio - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.in_index = in_index - - self.ignore_index = ignore_index - self.align_corners = align_corners - - if isinstance(loss_decode, dict): - self.loss_decode = build_loss(loss_decode) - elif isinstance(loss_decode, (list, tuple)): - self.loss_decode = nn.ModuleList() - for loss in loss_decode: - self.loss_decode.append(build_loss(loss)) - else: - raise TypeError(f'loss_decode must be a dict or sequence of dict,\ - but got {type(loss_decode)}') - - if sampler is not None: - self.sampler = build_pixel_sampler(sampler, context=self) - else: - self.sampler = None - - self.conv_seg = nn.Conv2d(channels, num_classes, kernel_size=1) - if dropout_ratio > 0: - self.dropout = nn.Dropout2d(dropout_ratio) - else: - self.dropout = None - self.fp16_enabled = False - - def extra_repr(self): - """Extra repr.""" - s = f'input_transform={self.input_transform}, ' \ - f'ignore_index={self.ignore_index}, ' \ - f'align_corners={self.align_corners}' - return s - - def _init_inputs(self, in_channels, in_index, input_transform): - """Check and initialize input transforms. - - The in_channels, in_index and input_transform must match. - Specifically, when input_transform is None, only single feature map - will be selected. So in_channels and in_index must be of type int. - When input_transform - - Args: - in_channels (int|Sequence[int]): Input channels. - in_index (int|Sequence[int]): Input feature index. - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - """ - - if input_transform is not None: - assert input_transform in ['resize_concat', 'multiple_select'] - self.input_transform = input_transform - self.in_index = in_index - if input_transform is not None: - assert isinstance(in_channels, (list, tuple)) - assert isinstance(in_index, (list, tuple)) - assert len(in_channels) == len(in_index) - if input_transform == 'resize_concat': - self.in_channels = sum(in_channels) - else: - self.in_channels = in_channels - else: - assert isinstance(in_channels, int) - assert isinstance(in_index, int) - self.in_channels = in_channels - - def _transform_inputs(self, inputs): - """Transform inputs for decoder. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - Tensor: The transformed inputs - """ - - if self.input_transform == 'resize_concat': - inputs = [inputs[i] for i in self.in_index] - upsampled_inputs = [ - resize( - input=x, - size=inputs[0].shape[2:], - mode='bilinear', - align_corners=self.align_corners) for x in inputs - ] - inputs = torch.cat(upsampled_inputs, dim=1) - elif self.input_transform == 'multiple_select': - inputs = [inputs[i] for i in self.in_index] - else: - inputs = inputs[self.in_index] - - return inputs - - @auto_fp16() - @abstractmethod - def forward(self, inputs): - """Placeholder of forward function.""" - pass - - def forward_train(self, inputs, img_metas, gt_semantic_seg, train_cfg): - """Forward function for training. - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - train_cfg (dict): The training config. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - seg_logits = self.forward(inputs) - losses = self.losses(seg_logits, gt_semantic_seg) - return losses - - def forward_test(self, inputs, img_metas, test_cfg): - """Forward function for testing. - - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - test_cfg (dict): The testing config. - - Returns: - Tensor: Output segmentation map. - """ - return self.forward(inputs) - - def cls_seg(self, feat): - """Classify each pixel.""" - if self.dropout is not None: - feat = self.dropout(feat) - output = self.conv_seg(feat) - return output - - @force_fp32(apply_to=('seg_logit', )) - def losses(self, seg_logit, seg_label): - """Compute segmentation loss.""" - loss = dict() - seg_logit = resize( - input=seg_logit, - size=seg_label.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - if self.sampler is not None: - seg_weight = self.sampler.sample(seg_logit, seg_label) - else: - seg_weight = None - seg_label = seg_label.squeeze(1) - - if not isinstance(self.loss_decode, nn.ModuleList): - losses_decode = [self.loss_decode] - else: - losses_decode = self.loss_decode - for loss_decode in losses_decode: - if loss_decode.loss_name not in loss: - loss[loss_decode.loss_name] = loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - else: - loss[loss_decode.loss_name] += loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - - loss['acc_seg'] = accuracy( - seg_logit, seg_label, ignore_index=self.ignore_index) - return loss diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/fcn_head.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/fcn_head.py deleted file mode 100644 index fb79a0d7c..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/decode_heads/fcn_head.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule - -from ..builder import HEADS -from .decode_head import BaseDecodeHead - - -@HEADS.register_module() -class FCNHead(BaseDecodeHead): - """Fully Convolution Networks for Semantic Segmentation. - - This head is implemented of `FCNNet `_. - - Args: - num_convs (int): Number of convs in the head. Default: 2. - kernel_size (int): The kernel size for convs in the head. Default: 3. - concat_input (bool): Whether concat the input and output of convs - before classification layer. - dilation (int): The dilation rate for convs in the head. Default: 1. - """ - - def __init__(self, - num_convs=2, - kernel_size=3, - concat_input=True, - dilation=1, - **kwargs): - assert num_convs >= 0 and dilation > 0 and isinstance(dilation, int) - self.num_convs = num_convs - self.concat_input = concat_input - self.kernel_size = kernel_size - super(FCNHead, self).__init__(**kwargs) - if num_convs == 0: - assert self.in_channels == self.channels - - conv_padding = (kernel_size // 2) * dilation - convs = [] - convs.append( - ConvModule( - self.in_channels, - self.channels, - kernel_size=kernel_size, - padding=conv_padding, - dilation=dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - for i in range(num_convs - 1): - convs.append( - ConvModule( - self.channels, - self.channels, - kernel_size=kernel_size, - padding=conv_padding, - dilation=dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - if num_convs == 0: - self.convs = nn.Identity() - else: - self.convs = nn.Sequential(*convs) - if self.concat_input: - self.conv_cat = ConvModule( - self.in_channels + self.channels, - self.channels, - kernel_size=kernel_size, - padding=kernel_size // 2, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - def _forward_feature(self, inputs): - """Forward function for feature maps before classifying each pixel with - ``self.cls_seg`` fc. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - feats (Tensor): A tensor of shape (batch_size, self.channels, - H, W) which is feature map for last layer of decoder head. - """ - x = self._transform_inputs(inputs) - feats = self.convs(x) - if self.concat_input: - feats = self.conv_cat(torch.cat([x, feats], dim=1)) - return feats - - def forward(self, inputs): - """Forward function.""" - output = self._forward_feature(inputs) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/__init__.py deleted file mode 100644 index fbc5b2d1b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .accuracy import Accuracy, accuracy -from .cross_entropy_loss import (CrossEntropyLoss, binary_cross_entropy, - cross_entropy, mask_cross_entropy) -from .dice_loss import DiceLoss -from .focal_loss import FocalLoss -from .lovasz_loss import LovaszLoss -from .utils import reduce_loss, weight_reduce_loss, weighted_loss - -__all__ = [ - 'accuracy', 'Accuracy', 'cross_entropy', 'binary_cross_entropy', - 'mask_cross_entropy', 'CrossEntropyLoss', 'reduce_loss', - 'weight_reduce_loss', 'weighted_loss', 'LovaszLoss', 'DiceLoss', - 'FocalLoss' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/accuracy.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/accuracy.py deleted file mode 100644 index 1d9e2d770..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/accuracy.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def accuracy(pred, target, topk=1, thresh=None, ignore_index=None): - """Calculate accuracy according to the prediction and target. - - Args: - pred (torch.Tensor): The model prediction, shape (N, num_class, ...) - target (torch.Tensor): The target of each prediction, shape (N, , ...) - ignore_index (int | None): The label index to be ignored. Default: None - topk (int | tuple[int], optional): If the predictions in ``topk`` - matches the target, the predictions will be regarded as - correct ones. Defaults to 1. - thresh (float, optional): If not None, predictions with scores under - this threshold are considered incorrect. Default to None. - - Returns: - float | tuple[float]: If the input ``topk`` is a single integer, - the function will return a single float as accuracy. If - ``topk`` is a tuple containing multiple integers, the - function will return a tuple containing accuracies of - each ``topk`` number. - """ - assert isinstance(topk, (int, tuple)) - if isinstance(topk, int): - topk = (topk, ) - return_single = True - else: - return_single = False - - maxk = max(topk) - if pred.size(0) == 0: - accu = [pred.new_tensor(0.) for i in range(len(topk))] - return accu[0] if return_single else accu - assert pred.ndim == target.ndim + 1 - assert pred.size(0) == target.size(0) - assert maxk <= pred.size(1), \ - f'maxk {maxk} exceeds pred dimension {pred.size(1)}' - pred_value, pred_label = pred.topk(maxk, dim=1) - # transpose to shape (maxk, N, ...) - pred_label = pred_label.transpose(0, 1) - correct = pred_label.eq(target.unsqueeze(0).expand_as(pred_label)) - if thresh is not None: - # Only prediction values larger than thresh are counted as correct - correct = correct & (pred_value > thresh).t() - if ignore_index is not None: - correct = correct[:, target != ignore_index] - res = [] - eps = torch.finfo(torch.float32).eps - for k in topk: - # Avoid causing ZeroDivisionError when all pixels - # of an image are ignored - correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) + eps - if ignore_index is not None: - total_num = target[target != ignore_index].numel() + eps - else: - total_num = target.numel() + eps - res.append(correct_k.mul_(100.0 / total_num)) - return res[0] if return_single else res - - -class Accuracy(nn.Module): - """Accuracy calculation module.""" - - def __init__(self, topk=(1, ), thresh=None, ignore_index=None): - """Module to calculate the accuracy. - - Args: - topk (tuple, optional): The criterion used to calculate the - accuracy. Defaults to (1,). - thresh (float, optional): If not None, predictions with scores - under this threshold are considered incorrect. Default to None. - """ - super().__init__() - self.topk = topk - self.thresh = thresh - self.ignore_index = ignore_index - - def forward(self, pred, target): - """Forward function to calculate accuracy. - - Args: - pred (torch.Tensor): Prediction of models. - target (torch.Tensor): Target for each prediction. - - Returns: - tuple[float]: The accuracies under different topk criterions. - """ - return accuracy(pred, target, self.topk, self.thresh, - self.ignore_index) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/cross_entropy_loss.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/cross_entropy_loss.py deleted file mode 100644 index 623fd58db..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/cross_entropy_loss.py +++ /dev/null @@ -1,296 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weight_reduce_loss - - -def cross_entropy(pred, - label, - weight=None, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=-100, - avg_non_ignore=False): - """cross_entropy. The wrapper function for :func:`F.cross_entropy` - - Args: - pred (torch.Tensor): The prediction with shape (N, 1). - label (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - Default: None. - class_weight (list[float], optional): The weight for each class. - Default: None. - reduction (str, optional): The method used to reduce the loss. - Options are 'none', 'mean' and 'sum'. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Default: None. - ignore_index (int): Specifies a target value that is ignored and - does not contribute to the input gradients. When - ``avg_non_ignore `` is ``True``, and the ``reduction`` is - ``''mean''``, the loss is averaged over non-ignored targets. - Defaults: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - """ - - # class_weight is a manual rescaling weight given to each class. - # If given, has to be a Tensor of size C element-wise losses - loss = F.cross_entropy( - pred, - label, - weight=class_weight, - reduction='none', - ignore_index=ignore_index) - - # apply weights and do the reduction - # average loss over non-ignored elements - # pytorch's official cross_entropy average loss over non-ignored elements - # refer to https://github.com/pytorch/pytorch/blob/56b43f4fec1f76953f15a627694d4bba34588969/torch/nn/functional.py#L2660 # noqa - if (avg_factor is None) and avg_non_ignore and reduction == 'mean': - avg_factor = label.numel() - (label == ignore_index).sum().item() - if weight is not None: - weight = weight.float() - loss = weight_reduce_loss( - loss, weight=weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def _expand_onehot_labels(labels, label_weights, target_shape, ignore_index): - """Expand onehot labels to match the size of prediction.""" - bin_labels = labels.new_zeros(target_shape) - valid_mask = (labels >= 0) & (labels != ignore_index) - inds = torch.nonzero(valid_mask, as_tuple=True) - - if inds[0].numel() > 0: - if labels.dim() == 3: - bin_labels[inds[0], labels[valid_mask], inds[1], inds[2]] = 1 - else: - bin_labels[inds[0], labels[valid_mask]] = 1 - - valid_mask = valid_mask.unsqueeze(1).expand(target_shape).float() - - if label_weights is None: - bin_label_weights = valid_mask - else: - bin_label_weights = label_weights.unsqueeze(1).expand(target_shape) - bin_label_weights = bin_label_weights * valid_mask - - return bin_labels, bin_label_weights, valid_mask - - -def binary_cross_entropy(pred, - label, - weight=None, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=-100, - avg_non_ignore=False, - **kwargs): - """Calculate the binary CrossEntropy loss. - - Args: - pred (torch.Tensor): The prediction with shape (N, 1). - label (torch.Tensor): The learning label of the prediction. - Note: In bce loss, label < 0 is invalid. - weight (torch.Tensor, optional): Sample-wise loss weight. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (int): The label index to be ignored. Default: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - - Returns: - torch.Tensor: The calculated loss - """ - if pred.size(1) == 1: - # For binary class segmentation, the shape of pred is - # [N, 1, H, W] and that of label is [N, H, W]. - # As the ignore_index often set as 255, so the - # binary class label check should mask out - # ignore_index - assert label[label != ignore_index].max() <= 1, \ - 'For pred with shape [N, 1, H, W], its label must have at ' \ - 'most 2 classes' - pred = pred.squeeze() - if pred.dim() != label.dim(): - assert (pred.dim() == 2 and label.dim() == 1) or ( - pred.dim() == 4 and label.dim() == 3), \ - 'Only pred shape [N, C], label shape [N] or pred shape [N, C, ' \ - 'H, W], label shape [N, H, W] are supported' - # `weight` returned from `_expand_onehot_labels` - # has been treated for valid (non-ignore) pixels - label, weight, valid_mask = _expand_onehot_labels( - label, weight, pred.shape, ignore_index) - else: - # should mask out the ignored elements - valid_mask = ((label >= 0) & (label != ignore_index)).float() - if weight is not None: - weight = weight * valid_mask - else: - weight = valid_mask - # average loss over non-ignored and valid elements - if reduction == 'mean' and avg_factor is None and avg_non_ignore: - avg_factor = valid_mask.sum().item() - - loss = F.binary_cross_entropy_with_logits( - pred, label.float(), pos_weight=class_weight, reduction='none') - # do the reduction for the weighted loss - loss = weight_reduce_loss( - loss, weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def mask_cross_entropy(pred, - target, - label, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=None, - **kwargs): - """Calculate the CrossEntropy loss for masks. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. - label (torch.Tensor): ``label`` indicates the class label of the mask' - corresponding object. This will be used to select the mask in the - of the class which the object belongs to when the mask prediction - if not class-agnostic. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (None): Placeholder, to be consistent with other loss. - Default: None. - - Returns: - torch.Tensor: The calculated loss - """ - assert ignore_index is None, 'BCE loss does not support ignore_index' - # TODO: handle these two reserved arguments - assert reduction == 'mean' and avg_factor is None - num_rois = pred.size()[0] - inds = torch.arange(0, num_rois, dtype=torch.long, device=pred.device) - pred_slice = pred[inds, label].squeeze(1) - return F.binary_cross_entropy_with_logits( - pred_slice, target, weight=class_weight, reduction='mean')[None] - - -@LOSSES.register_module() -class CrossEntropyLoss(nn.Module): - """CrossEntropyLoss. - - Args: - use_sigmoid (bool, optional): Whether the prediction uses sigmoid - of softmax. Defaults to False. - use_mask (bool, optional): Whether to use mask cross entropy loss. - Defaults to False. - reduction (str, optional): . Defaults to 'mean'. - Options are "none", "mean" and "sum". - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - """ - - def __init__(self, - use_sigmoid=False, - use_mask=False, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_ce', - avg_non_ignore=False): - super(CrossEntropyLoss, self).__init__() - assert (use_sigmoid is False) or (use_mask is False) - self.use_sigmoid = use_sigmoid - self.use_mask = use_mask - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = get_class_weight(class_weight) - self.avg_non_ignore = avg_non_ignore - if not self.avg_non_ignore and self.reduction == 'mean': - warnings.warn( - 'Default ``avg_non_ignore`` is False, if you would like to ' - 'ignore the certain label and average loss over non-ignore ' - 'labels, which is the same with PyTorch official ' - 'cross_entropy, set ``avg_non_ignore=True``.') - - if self.use_sigmoid: - self.cls_criterion = binary_cross_entropy - elif self.use_mask: - self.cls_criterion = mask_cross_entropy - else: - self.cls_criterion = cross_entropy - self._loss_name = loss_name - - def extra_repr(self): - """Extra repr.""" - s = f'avg_non_ignore={self.avg_non_ignore}' - return s - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - ignore_index=-100, - **kwargs): - """Forward function.""" - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = cls_score.new_tensor(self.class_weight) - else: - class_weight = None - # Note: for BCE loss, label < 0 is invalid. - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - weight, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - avg_non_ignore=self.avg_non_ignore, - ignore_index=ignore_index, - **kwargs) - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/dice_loss.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/dice_loss.py deleted file mode 100644 index 79a3abfc2..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/dice_loss.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/LikeLy-Journey/SegmenTron/blob/master/ -segmentron/solver/loss.py (Apache-2.0 License)""" -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weighted_loss - - -@weighted_loss -def dice_loss(pred, - target, - valid_mask, - smooth=1, - exponent=2, - class_weight=None, - ignore_index=255): - assert pred.shape[0] == target.shape[0] - total_loss = 0 - num_classes = pred.shape[1] - for i in range(num_classes): - if i != ignore_index: - dice_loss = binary_dice_loss( - pred[:, i], - target[..., i], - valid_mask=valid_mask, - smooth=smooth, - exponent=exponent) - if class_weight is not None: - dice_loss *= class_weight[i] - total_loss += dice_loss - return total_loss / num_classes - - -@weighted_loss -def binary_dice_loss(pred, target, valid_mask, smooth=1, exponent=2, **kwards): - assert pred.shape[0] == target.shape[0] - pred = pred.reshape(pred.shape[0], -1) - target = target.reshape(target.shape[0], -1) - valid_mask = valid_mask.reshape(valid_mask.shape[0], -1) - - num = torch.sum(torch.mul(pred, target) * valid_mask, dim=1) * 2 + smooth - den = torch.sum(pred.pow(exponent) + target.pow(exponent), dim=1) + smooth - - return 1 - num / den - - -@LOSSES.register_module() -class DiceLoss(nn.Module): - """DiceLoss. - - This loss is proposed in `V-Net: Fully Convolutional Neural Networks for - Volumetric Medical Image Segmentation `_. - - Args: - smooth (float): A float number to smooth loss, and avoid NaN error. - Default: 1 - exponent (float): An float number to calculate denominator - value: \\sum{x^exponent} + \\sum{y^exponent}. Default: 2. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Default to 1.0. - ignore_index (int | None): The label index to be ignored. Default: 255. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_dice'. - """ - - def __init__(self, - smooth=1, - exponent=2, - reduction='mean', - class_weight=None, - loss_weight=1.0, - ignore_index=255, - loss_name='loss_dice', - **kwards): - super(DiceLoss, self).__init__() - self.smooth = smooth - self.exponent = exponent - self.reduction = reduction - self.class_weight = get_class_weight(class_weight) - self.loss_weight = loss_weight - self.ignore_index = ignore_index - self._loss_name = loss_name - - def forward(self, - pred, - target, - avg_factor=None, - reduction_override=None, - **kwards): - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = pred.new_tensor(self.class_weight) - else: - class_weight = None - - pred = F.softmax(pred, dim=1) - num_classes = pred.shape[1] - one_hot_target = F.one_hot( - torch.clamp(target.long(), 0, num_classes - 1), - num_classes=num_classes) - valid_mask = (target != self.ignore_index).long() - - loss = self.loss_weight * dice_loss( - pred, - one_hot_target, - valid_mask=valid_mask, - reduction=reduction, - avg_factor=avg_factor, - smooth=self.smooth, - exponent=self.exponent, - class_weight=class_weight, - ignore_index=self.ignore_index) - return loss - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/focal_loss.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/focal_loss.py deleted file mode 100644 index af1c711df..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/focal_loss.py +++ /dev/null @@ -1,327 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Modified from https://github.com/open-mmlab/mmdetection -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.ops import sigmoid_focal_loss as _sigmoid_focal_loss - -from ..builder import LOSSES -from .utils import weight_reduce_loss - - -# This method is used when cuda is not available -def py_sigmoid_focal_loss(pred, - target, - one_hot_target=None, - weight=None, - gamma=2.0, - alpha=0.5, - class_weight=None, - valid_mask=None, - reduction='mean', - avg_factor=None): - """PyTorch version of `Focal Loss `_. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the - number of classes - target (torch.Tensor): The learning label of the prediction with - shape (N, C) - one_hot_target (None): Placeholder. It should be None. - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal Loss. - Defaults to 0.5. - class_weight (list[float], optional): Weight of each class. - Defaults to None. - valid_mask (torch.Tensor, optional): A mask uses 1 to mark the valid - samples and uses 0 to mark the ignored samples. Default: None. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - if isinstance(alpha, list): - alpha = pred.new_tensor(alpha) - pred_sigmoid = pred.sigmoid() - target = target.type_as(pred) - one_minus_pt = (1 - pred_sigmoid) * target + pred_sigmoid * (1 - target) - focal_weight = (alpha * target + (1 - alpha) * - (1 - target)) * one_minus_pt.pow(gamma) - - loss = F.binary_cross_entropy_with_logits( - pred, target, reduction='none') * focal_weight - final_weight = torch.ones(1, pred.size(1)).type_as(loss) - if weight is not None: - if weight.shape != loss.shape and weight.size(0) == loss.size(0): - # For most cases, weight is of shape (N, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - assert weight.dim() == loss.dim() - final_weight = final_weight * weight - if class_weight is not None: - final_weight = final_weight * pred.new_tensor(class_weight) - if valid_mask is not None: - final_weight = final_weight * valid_mask - loss = weight_reduce_loss(loss, final_weight, reduction, avg_factor) - return loss - - -def sigmoid_focal_loss(pred, - target, - one_hot_target, - weight=None, - gamma=2.0, - alpha=0.5, - class_weight=None, - valid_mask=None, - reduction='mean', - avg_factor=None): - r"""A warpper of cuda version `Focal Loss - `_. - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. It's shape - should be (N, ) - one_hot_target (torch.Tensor): The learning label with shape (N, C) - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal Loss. - Defaults to 0.5. - class_weight (list[float], optional): Weight of each class. - Defaults to None. - valid_mask (torch.Tensor, optional): A mask uses 1 to mark the valid - samples and uses 0 to mark the ignored samples. Default: None. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - # Function.apply does not accept keyword arguments, so the decorator - # "weighted_loss" is not applicable - final_weight = torch.ones(1, pred.size(1)).type_as(pred) - if isinstance(alpha, list): - # _sigmoid_focal_loss doesn't accept alpha of list type. Therefore, if - # a list is given, we set the input alpha as 0.5. This means setting - # equal weight for foreground class and background class. By - # multiplying the loss by 2, the effect of setting alpha as 0.5 is - # undone. The alpha of type list is used to regulate the loss in the - # post-processing process. - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), - gamma, 0.5, None, 'none') * 2 - alpha = pred.new_tensor(alpha) - final_weight = final_weight * ( - alpha * one_hot_target + (1 - alpha) * (1 - one_hot_target)) - else: - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), - gamma, alpha, None, 'none') - if weight is not None: - if weight.shape != loss.shape and weight.size(0) == loss.size(0): - # For most cases, weight is of shape (N, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - assert weight.dim() == loss.dim() - final_weight = final_weight * weight - if class_weight is not None: - final_weight = final_weight * pred.new_tensor(class_weight) - if valid_mask is not None: - final_weight = final_weight * valid_mask - loss = weight_reduce_loss(loss, final_weight, reduction, avg_factor) - return loss - - -@LOSSES.register_module() -class FocalLoss(nn.Module): - - def __init__(self, - use_sigmoid=True, - gamma=2.0, - alpha=0.5, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_focal'): - """`Focal Loss `_ - Args: - use_sigmoid (bool, optional): Whether to the prediction is - used for sigmoid or softmax. Defaults to True. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal - Loss. Defaults to 0.5. When a list is provided, the length - of the list should be equal to the number of classes. - Please be careful that this parameter is not the - class-wise weight but the weight of a binary classification - problem. This binary classification problem regards the - pixels which belong to one class as the foreground - and the other pixels as the background, each element in - the list is the weight of the corresponding foreground class. - The value of alpha or each element of alpha should be a float - in the interval [0, 1]. If you want to specify the class-wise - weight, please use `class_weight` parameter. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and - "sum". - class_weight (list[float], optional): Weight of each class. - Defaults to None. - loss_weight (float, optional): Weight of loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this - loss item to be included into the backward graph, `loss_` must - be the prefix of the name. Defaults to 'loss_focal'. - """ - super(FocalLoss, self).__init__() - assert use_sigmoid is True, \ - 'AssertionError: Only sigmoid focal loss supported now.' - assert reduction in ('none', 'mean', 'sum'), \ - "AssertionError: reduction should be 'none', 'mean' or " \ - "'sum'" - assert isinstance(alpha, (float, list)), \ - 'AssertionError: alpha should be of type float' - assert isinstance(gamma, float), \ - 'AssertionError: gamma should be of type float' - assert isinstance(loss_weight, float), \ - 'AssertionError: loss_weight should be of type float' - assert isinstance(loss_name, str), \ - 'AssertionError: loss_name should be of type str' - assert isinstance(class_weight, list) or class_weight is None, \ - 'AssertionError: class_weight must be None or of type list' - self.use_sigmoid = use_sigmoid - self.gamma = gamma - self.alpha = alpha - self.reduction = reduction - self.class_weight = class_weight - self.loss_weight = loss_weight - self._loss_name = loss_name - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - ignore_index=255, - **kwargs): - """Forward function. - - Args: - pred (torch.Tensor): The prediction with shape - (N, C) where C = number of classes, or - (N, C, d_1, d_2, ..., d_K) with K≥1 in the - case of K-dimensional loss. - target (torch.Tensor): The ground truth. If containing class - indices, shape (N) where each value is 0≤targets[i]≤C−1, - or (N, d_1, d_2, ..., d_K) with K≥1 in the case of - K-dimensional loss. If containing class probabilities, - same shape as the input. - weight (torch.Tensor, optional): The weight of loss for each - prediction. Defaults to None. - avg_factor (int, optional): Average factor that is used to - average the loss. Defaults to None. - reduction_override (str, optional): The reduction method used - to override the original reduction method of the loss. - Options are "none", "mean" and "sum". - ignore_index (int, optional): The label index to be ignored. - Default: 255 - Returns: - torch.Tensor: The calculated loss - """ - assert isinstance(ignore_index, int), \ - 'ignore_index must be of type int' - assert reduction_override in (None, 'none', 'mean', 'sum'), \ - "AssertionError: reduction should be 'none', 'mean' or " \ - "'sum'" - assert pred.shape == target.shape or \ - (pred.size(0) == target.size(0) and - pred.shape[2:] == target.shape[1:]), \ - "The shape of pred doesn't match the shape of target" - - original_shape = pred.shape - - # [B, C, d_1, d_2, ..., d_k] -> [C, B, d_1, d_2, ..., d_k] - pred = pred.transpose(0, 1) - # [C, B, d_1, d_2, ..., d_k] -> [C, N] - pred = pred.reshape(pred.size(0), -1) - # [C, N] -> [N, C] - pred = pred.transpose(0, 1).contiguous() - - if original_shape == target.shape: - # target with shape [B, C, d_1, d_2, ...] - # transform it's shape into [N, C] - # [B, C, d_1, d_2, ...] -> [C, B, d_1, d_2, ..., d_k] - target = target.transpose(0, 1) - # [C, B, d_1, d_2, ..., d_k] -> [C, N] - target = target.reshape(target.size(0), -1) - # [C, N] -> [N, C] - target = target.transpose(0, 1).contiguous() - else: - # target with shape [B, d_1, d_2, ...] - # transform it's shape into [N, ] - target = target.view(-1).contiguous() - valid_mask = (target != ignore_index).view(-1, 1) - # avoid raising error when using F.one_hot() - target = torch.where(target == ignore_index, target.new_tensor(0), - target) - - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.use_sigmoid: - num_classes = pred.size(1) - if torch.cuda.is_available() and pred.is_cuda: - if target.dim() == 1: - one_hot_target = F.one_hot(target, num_classes=num_classes) - else: - one_hot_target = target - target = target.argmax(dim=1) - valid_mask = (target != ignore_index).view(-1, 1) - calculate_loss_func = sigmoid_focal_loss - else: - one_hot_target = None - if target.dim() == 1: - target = F.one_hot(target, num_classes=num_classes) - else: - valid_mask = (target.argmax(dim=1) != ignore_index).view( - -1, 1) - calculate_loss_func = py_sigmoid_focal_loss - - loss_cls = self.loss_weight * calculate_loss_func( - pred, - target, - one_hot_target, - weight, - gamma=self.gamma, - alpha=self.alpha, - class_weight=self.class_weight, - valid_mask=valid_mask, - reduction=reduction, - avg_factor=avg_factor) - - if reduction == 'none': - # [N, C] -> [C, N] - loss_cls = loss_cls.transpose(0, 1) - # [C, N] -> [C, B, d1, d2, ...] - # original_shape: [B, C, d1, d2, ...] - loss_cls = loss_cls.reshape(original_shape[1], - original_shape[0], - *original_shape[2:]) - # [C, B, d1, d2, ...] -> [B, C, d1, d2, ...] - loss_cls = loss_cls.transpose(0, 1).contiguous() - else: - raise NotImplementedError - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/lovasz_loss.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/lovasz_loss.py deleted file mode 100644 index 2bb0fad39..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/lovasz_loss.py +++ /dev/null @@ -1,323 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/bermanmaxim/LovaszSoftmax/blob/master/pytor -ch/lovasz_losses.py Lovasz-Softmax and Jaccard hinge loss in PyTorch Maxim -Berman 2018 ESAT-PSI KU Leuven (MIT License)""" - -import mmcv -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weight_reduce_loss - - -def lovasz_grad(gt_sorted): - """Computes gradient of the Lovasz extension w.r.t sorted errors. - - See Alg. 1 in paper. - """ - p = len(gt_sorted) - gts = gt_sorted.sum() - intersection = gts - gt_sorted.float().cumsum(0) - union = gts + (1 - gt_sorted).float().cumsum(0) - jaccard = 1. - intersection / union - if p > 1: # cover 1-pixel case - jaccard[1:p] = jaccard[1:p] - jaccard[0:-1] - return jaccard - - -def flatten_binary_logits(logits, labels, ignore_index=None): - """Flattens predictions in the batch (binary case) Remove labels equal to - 'ignore_index'.""" - logits = logits.view(-1) - labels = labels.view(-1) - if ignore_index is None: - return logits, labels - valid = (labels != ignore_index) - vlogits = logits[valid] - vlabels = labels[valid] - return vlogits, vlabels - - -def flatten_probs(probs, labels, ignore_index=None): - """Flattens predictions in the batch.""" - if probs.dim() == 3: - # assumes output of a sigmoid layer - B, H, W = probs.size() - probs = probs.view(B, 1, H, W) - B, C, H, W = probs.size() - probs = probs.permute(0, 2, 3, 1).contiguous().view(-1, C) # B*H*W, C=P,C - labels = labels.view(-1) - if ignore_index is None: - return probs, labels - valid = (labels != ignore_index) - vprobs = probs[valid.nonzero().squeeze()] - vlabels = labels[valid] - return vprobs, vlabels - - -def lovasz_hinge_flat(logits, labels): - """Binary Lovasz hinge loss. - - Args: - logits (torch.Tensor): [P], logits at each prediction - (between -infty and +infty). - labels (torch.Tensor): [P], binary ground truth labels (0 or 1). - - Returns: - torch.Tensor: The calculated loss. - """ - if len(labels) == 0: - # only void pixels, the gradients should be 0 - return logits.sum() * 0. - signs = 2. * labels.float() - 1. - errors = (1. - logits * signs) - errors_sorted, perm = torch.sort(errors, dim=0, descending=True) - perm = perm.data - gt_sorted = labels[perm] - grad = lovasz_grad(gt_sorted) - loss = torch.dot(F.relu(errors_sorted), grad) - return loss - - -def lovasz_hinge(logits, - labels, - classes='present', - per_image=False, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=255): - """Binary Lovasz hinge loss. - - Args: - logits (torch.Tensor): [B, H, W], logits at each pixel - (between -infty and +infty). - labels (torch.Tensor): [B, H, W], binary ground truth masks (0 or 1). - classes (str | list[int], optional): Placeholder, to be consistent with - other loss. Default: None. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - class_weight (list[float], optional): Placeholder, to be consistent - with other loss. Default: None. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. This parameter only works when per_image is True. - Default: None. - ignore_index (int | None): The label index to be ignored. Default: 255. - - Returns: - torch.Tensor: The calculated loss. - """ - if per_image: - loss = [ - lovasz_hinge_flat(*flatten_binary_logits( - logit.unsqueeze(0), label.unsqueeze(0), ignore_index)) - for logit, label in zip(logits, labels) - ] - loss = weight_reduce_loss( - torch.stack(loss), None, reduction, avg_factor) - else: - loss = lovasz_hinge_flat( - *flatten_binary_logits(logits, labels, ignore_index)) - return loss - - -def lovasz_softmax_flat(probs, labels, classes='present', class_weight=None): - """Multi-class Lovasz-Softmax loss. - - Args: - probs (torch.Tensor): [P, C], class probabilities at each prediction - (between 0 and 1). - labels (torch.Tensor): [P], ground truth labels (between 0 and C - 1). - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - class_weight (list[float], optional): The weight for each class. - Default: None. - - Returns: - torch.Tensor: The calculated loss. - """ - if probs.numel() == 0: - # only void pixels, the gradients should be 0 - return probs * 0. - C = probs.size(1) - losses = [] - class_to_sum = list(range(C)) if classes in ['all', 'present'] else classes - for c in class_to_sum: - fg = (labels == c).float() # foreground for class c - if (classes == 'present' and fg.sum() == 0): - continue - if C == 1: - if len(classes) > 1: - raise ValueError('Sigmoid output possible only with 1 class') - class_pred = probs[:, 0] - else: - class_pred = probs[:, c] - errors = (fg - class_pred).abs() - errors_sorted, perm = torch.sort(errors, 0, descending=True) - perm = perm.data - fg_sorted = fg[perm] - loss = torch.dot(errors_sorted, lovasz_grad(fg_sorted)) - if class_weight is not None: - loss *= class_weight[c] - losses.append(loss) - return torch.stack(losses).mean() - - -def lovasz_softmax(probs, - labels, - classes='present', - per_image=False, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=255): - """Multi-class Lovasz-Softmax loss. - - Args: - probs (torch.Tensor): [B, C, H, W], class probabilities at each - prediction (between 0 and 1). - labels (torch.Tensor): [B, H, W], ground truth labels (between 0 and - C - 1). - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - class_weight (list[float], optional): The weight for each class. - Default: None. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. This parameter only works when per_image is True. - Default: None. - ignore_index (int | None): The label index to be ignored. Default: 255. - - Returns: - torch.Tensor: The calculated loss. - """ - - if per_image: - loss = [ - lovasz_softmax_flat( - *flatten_probs( - prob.unsqueeze(0), label.unsqueeze(0), ignore_index), - classes=classes, - class_weight=class_weight) - for prob, label in zip(probs, labels) - ] - loss = weight_reduce_loss( - torch.stack(loss), None, reduction, avg_factor) - else: - loss = lovasz_softmax_flat( - *flatten_probs(probs, labels, ignore_index), - classes=classes, - class_weight=class_weight) - return loss - - -@LOSSES.register_module() -class LovaszLoss(nn.Module): - """LovaszLoss. - - This loss is proposed in `The Lovasz-Softmax loss: A tractable surrogate - for the optimization of the intersection-over-union measure in neural - networks `_. - - Args: - loss_type (str, optional): Binary or multi-class loss. - Default: 'multi_class'. Options are "binary" and "multi_class". - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_lovasz'. - """ - - def __init__(self, - loss_type='multi_class', - classes='present', - per_image=False, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_lovasz'): - super(LovaszLoss, self).__init__() - assert loss_type in ('binary', 'multi_class'), "loss_type should be \ - 'binary' or 'multi_class'." - - if loss_type == 'binary': - self.cls_criterion = lovasz_hinge - else: - self.cls_criterion = lovasz_softmax - assert classes in ('all', 'present') or mmcv.is_list_of(classes, int) - if not per_image: - assert reduction == 'none', "reduction should be 'none' when \ - per_image is False." - - self.classes = classes - self.per_image = per_image - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = get_class_weight(class_weight) - self._loss_name = loss_name - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - """Forward function.""" - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = cls_score.new_tensor(self.class_weight) - else: - class_weight = None - - # if multi-class loss, transform logits to probs - if self.cls_criterion == lovasz_softmax: - cls_score = F.softmax(cls_score, dim=1) - - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - self.classes, - self.per_image, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/utils.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/utils.py deleted file mode 100644 index 621f57c74..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/losses/utils.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import mmcv -import numpy as np -import torch -import torch.nn.functional as F - - -def get_class_weight(class_weight): - """Get class weight for loss function. - - Args: - class_weight (list[float] | str | None): If class_weight is a str, - take it as a file name and read from it. - """ - if isinstance(class_weight, str): - # take it as a file path - if class_weight.endswith('.npy'): - class_weight = np.load(class_weight) - else: - # pkl, json or yaml - class_weight = mmcv.load(class_weight) - - return class_weight - - -def reduce_loss(loss, reduction): - """Reduce loss as specified. - - Args: - loss (Tensor): Elementwise loss tensor. - reduction (str): Options are "none", "mean" and "sum". - - Return: - Tensor: Reduced loss tensor. - """ - reduction_enum = F._Reduction.get_enum(reduction) - # none: 0, elementwise_mean:1, sum: 2 - if reduction_enum == 0: - return loss - elif reduction_enum == 1: - return loss.mean() - elif reduction_enum == 2: - return loss.sum() - - -def weight_reduce_loss(loss, weight=None, reduction='mean', avg_factor=None): - """Apply element-wise weight and reduce loss. - - Args: - loss (Tensor): Element-wise loss. - weight (Tensor): Element-wise weights. - reduction (str): Same as built-in losses of PyTorch. - avg_factor (float): Average factor when computing the mean of losses. - - Returns: - Tensor: Processed loss values. - """ - # if weight is specified, apply element-wise weight - if weight is not None: - assert weight.dim() == loss.dim() - if weight.dim() > 1: - assert weight.size(1) == 1 or weight.size(1) == loss.size(1) - loss = loss * weight - - # if avg_factor is not specified, just reduce the loss - if avg_factor is None: - loss = reduce_loss(loss, reduction) - else: - # if reduction is mean, then average the loss by avg_factor - if reduction == 'mean': - # Avoid causing ZeroDivisionError when avg_factor is 0.0, - # i.e., all labels of an image belong to ignore index. - eps = torch.finfo(torch.float32).eps - loss = loss.sum() / (avg_factor + eps) - # if reduction is 'none', then do nothing, otherwise raise an error - elif reduction != 'none': - raise ValueError('avg_factor can not be used with reduction="sum"') - return loss - - -def weighted_loss(loss_func): - """Create a weighted version of a given loss function. - - To use this decorator, the loss function must have the signature like - `loss_func(pred, target, **kwargs)`. The function only needs to compute - element-wise loss without any reduction. This decorator will add weight - and reduction arguments to the function. The decorated function will have - the signature like `loss_func(pred, target, weight=None, reduction='mean', - avg_factor=None, **kwargs)`. - - :Example: - - >>> import torch - >>> @weighted_loss - >>> def l1_loss(pred, target): - >>> return (pred - target).abs() - - >>> pred = torch.Tensor([0, 2, 3]) - >>> target = torch.Tensor([1, 1, 1]) - >>> weight = torch.Tensor([1, 0, 1]) - - >>> l1_loss(pred, target) - tensor(1.3333) - >>> l1_loss(pred, target, weight) - tensor(1.) - >>> l1_loss(pred, target, reduction='none') - tensor([1., 1., 2.]) - >>> l1_loss(pred, target, weight, avg_factor=2) - tensor(1.5000) - """ - - @functools.wraps(loss_func) - def wrapper(pred, - target, - weight=None, - reduction='mean', - avg_factor=None, - **kwargs): - # get element-wise loss - loss = loss_func(pred, target, **kwargs) - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - return wrapper diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/__init__.py deleted file mode 100644 index ff03186a9..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .featurepyramid import Feature2Pyramid -from .fpn import FPN -from .ic_neck import ICNeck -from .jpu import JPU -from .mla_neck import MLANeck -from .multilevel_neck import MultiLevelNeck - -__all__ = [ - 'FPN', 'MultiLevelNeck', 'MLANeck', 'ICNeck', 'JPU', 'Feature2Pyramid' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/featurepyramid.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/featurepyramid.py deleted file mode 100644 index 82a00ceb1..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/featurepyramid.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import build_norm_layer - -from ..builder import NECKS - - -@NECKS.register_module() -class Feature2Pyramid(nn.Module): - """Feature2Pyramid. - - A neck structure connect ViT backbone and decoder_heads. - - Args: - embed_dims (int): Embedding dimension. - rescales (list[float]): Different sampling multiples were - used to obtain pyramid features. Default: [4, 2, 1, 0.5]. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='SyncBN', requires_grad=True). - """ - - def __init__(self, - embed_dim, - rescales=[4, 2, 1, 0.5], - norm_cfg=dict(type='SyncBN', requires_grad=True)): - super(Feature2Pyramid, self).__init__() - self.rescales = rescales - self.upsample_4x = None - for k in self.rescales: - if k == 4: - self.upsample_4x = nn.Sequential( - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2), - build_norm_layer(norm_cfg, embed_dim)[1], - nn.GELU(), - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2), - ) - elif k == 2: - self.upsample_2x = nn.Sequential( - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2)) - elif k == 1: - self.identity = nn.Identity() - elif k == 0.5: - self.downsample_2x = nn.MaxPool2d(kernel_size=2, stride=2) - elif k == 0.25: - self.downsample_4x = nn.MaxPool2d(kernel_size=4, stride=4) - else: - raise KeyError(f'invalid {k} for feature2pyramid') - - def forward(self, inputs): - assert len(inputs) == len(self.rescales) - outputs = [] - if self.upsample_4x is not None: - ops = [ - self.upsample_4x, self.upsample_2x, self.identity, - self.downsample_2x - ] - else: - ops = [ - self.upsample_2x, self.identity, self.downsample_2x, - self.downsample_4x - ] - for i in range(len(inputs)): - outputs.append(ops[i](inputs[i])) - return tuple(outputs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/fpn.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/fpn.py deleted file mode 100644 index 6997de9d4..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/fpn.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule, auto_fp16 - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class FPN(BaseModule): - """Feature Pyramid Network. - - This neck is the implementation of `Feature Pyramid Networks for Object - Detection `_. - - Args: - in_channels (list[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - num_outs (int): Number of output scales. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - add_extra_convs (bool | str): If bool, it decides whether to add conv - layers on top of the original feature maps. Default to False. - If True, its actual mode is specified by `extra_convs_on_inputs`. - If str, it specifies the source feature map of the extra convs. - Only the following options are allowed - - - 'on_input': Last feat map of neck inputs (i.e. backbone feature). - - 'on_lateral': Last feature map after lateral convs. - - 'on_output': The last output feature map after fpn convs. - extra_convs_on_inputs (bool, deprecated): Whether to apply extra convs - on the original feature from the backbone. If True, - it is equivalent to `add_extra_convs='on_input'`. If False, it is - equivalent to set `add_extra_convs='on_output'`. Default to True. - relu_before_extra_convs (bool): Whether to apply relu before the extra - conv. Default: False. - no_norm_on_lateral (bool): Whether to apply norm on lateral. - Default: False. - conv_cfg (dict): Config dict for convolution layer. Default: None. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - upsample_cfg (dict): Config dict for interpolate layer. - Default: dict(mode='nearest'). - init_cfg (dict or list[dict], optional): Initialization config dict. - - Example: - >>> import torch - >>> in_channels = [2, 3, 5, 7] - >>> scales = [340, 170, 84, 43] - >>> inputs = [torch.rand(1, c, s, s) - ... for c, s in zip(in_channels, scales)] - >>> self = FPN(in_channels, 11, len(in_channels)).eval() - >>> outputs = self.forward(inputs) - >>> for i in range(len(outputs)): - ... print(f'outputs[{i}].shape = {outputs[i].shape}') - outputs[0].shape = torch.Size([1, 11, 340, 340]) - outputs[1].shape = torch.Size([1, 11, 170, 170]) - outputs[2].shape = torch.Size([1, 11, 84, 84]) - outputs[3].shape = torch.Size([1, 11, 43, 43]) - """ - - def __init__(self, - in_channels, - out_channels, - num_outs, - start_level=0, - end_level=-1, - add_extra_convs=False, - extra_convs_on_inputs=False, - relu_before_extra_convs=False, - no_norm_on_lateral=False, - conv_cfg=None, - norm_cfg=None, - act_cfg=None, - upsample_cfg=dict(mode='nearest'), - init_cfg=dict( - type='Xavier', layer='Conv2d', distribution='uniform')): - super(FPN, self).__init__(init_cfg) - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.num_ins = len(in_channels) - self.num_outs = num_outs - self.relu_before_extra_convs = relu_before_extra_convs - self.no_norm_on_lateral = no_norm_on_lateral - self.fp16_enabled = False - self.upsample_cfg = upsample_cfg.copy() - - if end_level == -1: - self.backbone_end_level = self.num_ins - assert num_outs >= self.num_ins - start_level - else: - # if end_level < inputs, no extra level is allowed - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - assert num_outs == end_level - start_level - self.start_level = start_level - self.end_level = end_level - self.add_extra_convs = add_extra_convs - assert isinstance(add_extra_convs, (str, bool)) - if isinstance(add_extra_convs, str): - # Extra_convs_source choices: 'on_input', 'on_lateral', 'on_output' - assert add_extra_convs in ('on_input', 'on_lateral', 'on_output') - elif add_extra_convs: # True - if extra_convs_on_inputs: - # For compatibility with previous release - # TODO: deprecate `extra_convs_on_inputs` - self.add_extra_convs = 'on_input' - else: - self.add_extra_convs = 'on_output' - - self.lateral_convs = nn.ModuleList() - self.fpn_convs = nn.ModuleList() - - for i in range(self.start_level, self.backbone_end_level): - l_conv = ConvModule( - in_channels[i], - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg if not self.no_norm_on_lateral else None, - act_cfg=act_cfg, - inplace=False) - fpn_conv = ConvModule( - out_channels, - out_channels, - 3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - - self.lateral_convs.append(l_conv) - self.fpn_convs.append(fpn_conv) - - # add extra conv layers (e.g., RetinaNet) - extra_levels = num_outs - self.backbone_end_level + self.start_level - if self.add_extra_convs and extra_levels >= 1: - for i in range(extra_levels): - if i == 0 and self.add_extra_convs == 'on_input': - in_channels = self.in_channels[self.backbone_end_level - 1] - else: - in_channels = out_channels - extra_fpn_conv = ConvModule( - in_channels, - out_channels, - 3, - stride=2, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - self.fpn_convs.append(extra_fpn_conv) - - @auto_fp16() - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # build laterals - laterals = [ - lateral_conv(inputs[i + self.start_level]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - - # build top-down path - used_backbone_levels = len(laterals) - for i in range(used_backbone_levels - 1, 0, -1): - # In some cases, fixing `scale factor` (e.g. 2) is preferred, but - # it cannot co-exist with `size` in `F.interpolate`. - if 'scale_factor' in self.upsample_cfg: - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], **self.upsample_cfg) - else: - prev_shape = laterals[i - 1].shape[2:] - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], size=prev_shape, **self.upsample_cfg) - - # build outputs - # part 1: from original levels - outs = [ - self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels) - ] - # part 2: add extra levels - if self.num_outs > len(outs): - # use max pool to get more levels on top of outputs - # (e.g., Faster R-CNN, Mask R-CNN) - if not self.add_extra_convs: - for i in range(self.num_outs - used_backbone_levels): - outs.append(F.max_pool2d(outs[-1], 1, stride=2)) - # add conv layers on top of original feature maps (RetinaNet) - else: - if self.add_extra_convs == 'on_input': - extra_source = inputs[self.backbone_end_level - 1] - elif self.add_extra_convs == 'on_lateral': - extra_source = laterals[-1] - elif self.add_extra_convs == 'on_output': - extra_source = outs[-1] - else: - raise NotImplementedError - outs.append(self.fpn_convs[used_backbone_levels](extra_source)) - for i in range(used_backbone_levels + 1, self.num_outs): - if self.relu_before_extra_convs: - outs.append(self.fpn_convs[i](F.relu(outs[-1]))) - else: - outs.append(self.fpn_convs[i](outs[-1])) - return tuple(outs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/ic_neck.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/ic_neck.py deleted file mode 100644 index a5d81cef8..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/ic_neck.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -class CascadeFeatureFusion(BaseModule): - """Cascade Feature Fusion Unit in ICNet. - - Args: - low_channels (int): The number of input channels for - low resolution feature map. - high_channels (int): The number of input channels for - high resolution feature map. - out_channels (int): The number of output channels. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Returns: - x (Tensor): The output tensor of shape (N, out_channels, H, W). - x_low (Tensor): The output tensor of shape (N, out_channels, H, W) - for Cascade Label Guidance in auxiliary heads. - """ - - def __init__(self, - low_channels, - high_channels, - out_channels, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(CascadeFeatureFusion, self).__init__(init_cfg=init_cfg) - self.align_corners = align_corners - self.conv_low = ConvModule( - low_channels, - out_channels, - 3, - padding=2, - dilation=2, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.conv_high = ConvModule( - high_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, x_low, x_high): - x_low = resize( - x_low, - size=x_high.size()[2:], - mode='bilinear', - align_corners=self.align_corners) - # Note: Different from original paper, `x_low` is underwent - # `self.conv_low` rather than another 1x1 conv classifier - # before being used for auxiliary head. - x_low = self.conv_low(x_low) - x_high = self.conv_high(x_high) - x = x_low + x_high - x = F.relu(x, inplace=True) - return x, x_low - - -@NECKS.register_module() -class ICNeck(BaseModule): - """ICNet for Real-Time Semantic Segmentation on High-Resolution Images. - - This head is the implementation of `ICHead - `_. - - Args: - in_channels (int): The number of input image channels. Default: 3. - out_channels (int): The numbers of output feature channels. - Default: 128. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(64, 256, 256), - out_channels=128, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(ICNeck, self).__init__(init_cfg=init_cfg) - assert len(in_channels) == 3, 'Length of input channels \ - must be 3!' - - self.in_channels = in_channels - self.out_channels = out_channels - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.align_corners = align_corners - self.cff_24 = CascadeFeatureFusion( - self.in_channels[2], - self.in_channels[1], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - self.cff_12 = CascadeFeatureFusion( - self.out_channels, - self.in_channels[0], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - def forward(self, inputs): - assert len(inputs) == 3, 'Length of input feature \ - maps must be 3!' - - x_sub1, x_sub2, x_sub4 = inputs - x_cff_24, x_24 = self.cff_24(x_sub4, x_sub2) - x_cff_12, x_12 = self.cff_12(x_cff_24, x_sub1) - # Note: `x_cff_12` is used for decode_head, - # `x_24` and `x_12` are used for auxiliary head. - return x_24, x_12, x_cff_12 diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/jpu.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/jpu.py deleted file mode 100644 index 3cc6b9f42..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/jpu.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, DepthwiseSeparableConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class JPU(BaseModule): - """FastFCN: Rethinking Dilated Convolution in the Backbone - for Semantic Segmentation. - - This Joint Pyramid Upsampling (JPU) neck is the implementation of - `FastFCN `_. - - Args: - in_channels (Tuple[int], optional): The number of input channels - for each convolution operations before upsampling. - Default: (512, 1024, 2048). - mid_channels (int): The number of output channels of JPU. - Default: 512. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - dilations (tuple[int]): Dilation rate of each Depthwise - Separable ConvModule. Default: (1, 2, 4, 8). - align_corners (bool, optional): The align_corners argument of - resize operation. Default: False. - conv_cfg (dict | None): Config of conv layers. - Default: None. - norm_cfg (dict | None): Config of norm layers. - Default: dict(type='BN'). - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU'). - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(512, 1024, 2048), - mid_channels=512, - start_level=0, - end_level=-1, - dilations=(1, 2, 4, 8), - align_corners=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(JPU, self).__init__(init_cfg=init_cfg) - assert isinstance(in_channels, tuple) - assert isinstance(dilations, tuple) - self.in_channels = in_channels - self.mid_channels = mid_channels - self.start_level = start_level - self.num_ins = len(in_channels) - if end_level == -1: - self.backbone_end_level = self.num_ins - else: - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - - self.dilations = dilations - self.align_corners = align_corners - - self.conv_layers = nn.ModuleList() - self.dilation_layers = nn.ModuleList() - for i in range(self.start_level, self.backbone_end_level): - conv_layer = nn.Sequential( - ConvModule( - self.in_channels[i], - self.mid_channels, - kernel_size=3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.conv_layers.append(conv_layer) - for i in range(len(dilations)): - dilation_layer = nn.Sequential( - DepthwiseSeparableConvModule( - in_channels=(self.backbone_end_level - self.start_level) * - self.mid_channels, - out_channels=self.mid_channels, - kernel_size=3, - stride=1, - padding=dilations[i], - dilation=dilations[i], - dw_norm_cfg=norm_cfg, - dw_act_cfg=None, - pw_norm_cfg=norm_cfg, - pw_act_cfg=act_cfg)) - self.dilation_layers.append(dilation_layer) - - def forward(self, inputs): - """Forward function.""" - assert len(inputs) == len(self.in_channels), 'Length of inputs must \ - be the same with self.in_channels!' - - feats = [ - self.conv_layers[i - self.start_level](inputs[i]) - for i in range(self.start_level, self.backbone_end_level) - ] - - h, w = feats[0].shape[2:] - for i in range(1, len(feats)): - feats[i] = resize( - feats[i], - size=(h, w), - mode='bilinear', - align_corners=self.align_corners) - - feat = torch.cat(feats, dim=1) - concat_feat = torch.cat([ - self.dilation_layers[i](feat) for i in range(len(self.dilations)) - ], - dim=1) - - outs = [] - - # Default: outs[2] is the output of JPU for decoder head, outs[1] is - # the feature map from backbone for auxiliary head. Additionally, - # outs[0] can also be used for auxiliary head. - for i in range(self.start_level, self.backbone_end_level - 1): - outs.append(inputs[i]) - outs.append(concat_feat) - return tuple(outs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/mla_neck.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/mla_neck.py deleted file mode 100644 index 1513e296d..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/mla_neck.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, build_norm_layer - -from ..builder import NECKS - - -class MLAModule(nn.Module): - - def __init__(self, - in_channels=[1024, 1024, 1024, 1024], - out_channels=256, - norm_cfg=None, - act_cfg=None): - super(MLAModule, self).__init__() - self.channel_proj = nn.ModuleList() - for i in range(len(in_channels)): - self.channel_proj.append( - ConvModule( - in_channels=in_channels[i], - out_channels=out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.feat_extract = nn.ModuleList() - for i in range(len(in_channels)): - self.feat_extract.append( - ConvModule( - in_channels=out_channels, - out_channels=out_channels, - kernel_size=3, - padding=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, inputs): - - # feat_list -> [p2, p3, p4, p5] - feat_list = [] - for x, conv in zip(inputs, self.channel_proj): - feat_list.append(conv(x)) - - # feat_list -> [p5, p4, p3, p2] - # mid_list -> [m5, m4, m3, m2] - feat_list = feat_list[::-1] - mid_list = [] - for feat in feat_list: - if len(mid_list) == 0: - mid_list.append(feat) - else: - mid_list.append(mid_list[-1] + feat) - - # mid_list -> [m5, m4, m3, m2] - # out_list -> [o2, o3, o4, o5] - out_list = [] - for mid, conv in zip(mid_list, self.feat_extract): - out_list.append(conv(mid)) - - return tuple(out_list) - - -@NECKS.register_module() -class MLANeck(nn.Module): - """Multi-level Feature Aggregation. - - This neck is `The Multi-level Feature Aggregation construction of - SETR `_. - - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - norm_layer (dict): Config dict for input normalization. - Default: norm_layer=dict(type='LN', eps=1e-6, requires_grad=True). - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - norm_layer=dict(type='LN', eps=1e-6, requires_grad=True), - norm_cfg=None, - act_cfg=None): - super(MLANeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - - # In order to build general vision transformer backbone, we have to - # move MLA to neck. - self.norm = nn.ModuleList([ - build_norm_layer(norm_layer, in_channels[i])[1] - for i in range(len(in_channels)) - ]) - - self.mla = MLAModule( - in_channels=in_channels, - out_channels=out_channels, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # Convert from nchw to nlc - outs = [] - for i in range(len(inputs)): - x = inputs[i] - n, c, h, w = x.shape - x = x.reshape(n, c, h * w).transpose(2, 1).contiguous() - x = self.norm[i](x) - x = x.transpose(1, 2).reshape(n, c, h, w).contiguous() - outs.append(x) - - outs = self.mla(outs) - return tuple(outs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/multilevel_neck.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/multilevel_neck.py deleted file mode 100644 index 5151f8762..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/necks/multilevel_neck.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, xavier_init - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class MultiLevelNeck(nn.Module): - """MultiLevelNeck. - - A neck structure connect vit backbone and decoder_heads. - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - scales (List[float]): Scale factors for each input feature map. - Default: [0.5, 1, 2, 4] - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - scales=[0.5, 1, 2, 4], - norm_cfg=None, - act_cfg=None): - super(MultiLevelNeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.scales = scales - self.num_outs = len(scales) - self.lateral_convs = nn.ModuleList() - self.convs = nn.ModuleList() - for in_channel in in_channels: - self.lateral_convs.append( - ConvModule( - in_channel, - out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - for _ in range(self.num_outs): - self.convs.append( - ConvModule( - out_channels, - out_channels, - kernel_size=3, - padding=1, - stride=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - # default init_weights for conv(msra) and norm in ConvModule - def init_weights(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - xavier_init(m, distribution='uniform') - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - inputs = [ - lateral_conv(inputs[i]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - # for len(inputs) not equal to self.num_outs - if len(inputs) == 1: - inputs = [inputs[0] for _ in range(self.num_outs)] - outs = [] - for i in range(self.num_outs): - x_resize = resize( - inputs[i], scale_factor=self.scales[i], mode='bilinear') - outs.append(self.convs[i](x_resize)) - return tuple(outs) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/__init__.py deleted file mode 100644 index 387c858bd..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseSegmentor -from .cascade_encoder_decoder import CascadeEncoderDecoder -from .encoder_decoder import EncoderDecoder - -__all__ = ['BaseSegmentor', 'EncoderDecoder', 'CascadeEncoderDecoder'] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/base.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/base.py deleted file mode 100644 index 76dc8f075..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/base.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from abc import ABCMeta, abstractmethod -from collections import OrderedDict - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import BaseModule, auto_fp16 - - -class BaseSegmentor(BaseModule, metaclass=ABCMeta): - """Base class for segmentors.""" - - def __init__(self, init_cfg=None): - super(BaseSegmentor, self).__init__(init_cfg) - self.fp16_enabled = False - - @property - def with_neck(self): - """bool: whether the segmentor has neck""" - return hasattr(self, 'neck') and self.neck is not None - - @property - def with_auxiliary_head(self): - """bool: whether the segmentor has auxiliary head""" - return hasattr(self, - 'auxiliary_head') and self.auxiliary_head is not None - - @property - def with_decode_head(self): - """bool: whether the segmentor has decode head""" - return hasattr(self, 'decode_head') and self.decode_head is not None - - @abstractmethod - def extract_feat(self, imgs): - """Placeholder for extract features from images.""" - pass - - @abstractmethod - def encode_decode(self, img, img_metas): - """Placeholder for encode images with backbone and decode into a - semantic segmentation map of the same size as input.""" - pass - - @abstractmethod - def forward_train(self, imgs, img_metas, **kwargs): - """Placeholder for Forward function for training.""" - pass - - @abstractmethod - def simple_test(self, img, img_meta, **kwargs): - """Placeholder for single image test.""" - pass - - @abstractmethod - def aug_test(self, imgs, img_metas, **kwargs): - """Placeholder for augmentation test.""" - pass - - def forward_test(self, imgs, img_metas, **kwargs): - """ - Args: - imgs (List[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains all images in the batch. - img_metas (List[List[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. - """ - for var, name in [(imgs, 'imgs'), (img_metas, 'img_metas')]: - if not isinstance(var, list): - raise TypeError(f'{name} must be a list, but got ' - f'{type(var)}') - - num_augs = len(imgs) - if num_augs != len(img_metas): - raise ValueError(f'num of augmentations ({len(imgs)}) != ' - f'num of image meta ({len(img_metas)})') - # all images in the same aug batch all of the same ori_shape and pad - # shape - for img_meta in img_metas: - ori_shapes = [_['ori_shape'] for _ in img_meta] - assert all(shape == ori_shapes[0] for shape in ori_shapes) - img_shapes = [_['img_shape'] for _ in img_meta] - assert all(shape == img_shapes[0] for shape in img_shapes) - pad_shapes = [_['pad_shape'] for _ in img_meta] - assert all(shape == pad_shapes[0] for shape in pad_shapes) - - if num_augs == 1: - return self.simple_test(imgs[0], img_metas[0], **kwargs) - else: - return self.aug_test(imgs, img_metas, **kwargs) - - @auto_fp16(apply_to=('img', )) - def forward(self, img, img_metas, return_loss=True, **kwargs): - """Calls either :func:`forward_train` or :func:`forward_test` depending - on whether ``return_loss`` is ``True``. - - Note this setting will change the expected inputs. When - ``return_loss=True``, img and img_meta are single-nested (i.e. Tensor - and List[dict]), and when ``resturn_loss=False``, img and img_meta - should be double nested (i.e. List[Tensor], List[List[dict]]), with - the outer list indicating test time augmentations. - """ - if return_loss: - return self.forward_train(img, img_metas, **kwargs) - else: - return self.forward_test(img, img_metas, **kwargs) - - def train_step(self, data_batch, optimizer, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating is also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer` | dict): The optimizer of - runner is passed to ``train_step()``. This argument is unused - and reserved. - - Returns: - dict: It should contain at least 3 keys: ``loss``, ``log_vars``, - ``num_samples``. - ``loss`` is a tensor for back propagation, which can be a - weighted sum of multiple losses. - ``log_vars`` contains all the variables to be sent to the - logger. - ``num_samples`` indicates the batch size (when the model is - DDP, it means the batch size on each GPU), which is used for - averaging the logs. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, - log_vars=log_vars, - num_samples=len(data_batch['img_metas'])) - - return outputs - - def val_step(self, data_batch, optimizer=None, **kwargs): - """The iteration step during validation. - - This method shares the same signature as :func:`train_step`, but used - during val epochs. Note that the evaluation after training epochs is - not implemented with this method, but an evaluation hook. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - log_vars_ = dict() - for loss_name, loss_value in log_vars.items(): - k = loss_name + '_val' - log_vars_[k] = loss_value - - outputs = dict( - loss=loss, - log_vars=log_vars_, - num_samples=len(data_batch['img_metas'])) - - return outputs - - @staticmethod - def _parse_losses(losses): - """Parse the raw outputs (losses) of the network. - - Args: - losses (dict): Raw output of the network, which usually contain - losses and other necessary information. - - Returns: - tuple[Tensor, dict]: (loss, log_vars), loss is the loss tensor - which may be a weighted sum of all losses, log_vars contains - all the variables to be sent to the logger. - """ - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(_value for _key, _value in log_vars.items() - if 'loss' in _key) - - # If the loss_vars has different length, raise assertion error - # to prevent GPUs from infinite waiting. - if dist.is_available() and dist.is_initialized(): - log_var_length = torch.tensor(len(log_vars), device=loss.device) - dist.all_reduce(log_var_length) - message = (f'rank {dist.get_rank()}' + - f' len(log_vars): {len(log_vars)}' + ' keys: ' + - ','.join(log_vars.keys()) + '\n') - assert log_var_length == len(log_vars) * dist.get_world_size(), \ - 'loss log variables are different across GPUs!\n' + message - - log_vars['loss'] = loss - for loss_name, loss_value in log_vars.items(): - # reduce loss when distributed training - if dist.is_available() and dist.is_initialized(): - loss_value = loss_value.data.clone() - dist.all_reduce(loss_value.div_(dist.get_world_size())) - log_vars[loss_name] = loss_value.item() - - return loss, log_vars - - def show_result(self, - img, - result, - palette=None, - win_name='', - show=False, - wait_time=0, - out_file=None, - opacity=0.5): - """Draw `result` over `img`. - - Args: - img (str or Tensor): The image to be displayed. - result (Tensor): The semantic segmentation results to draw over - `img`. - palette (list[list[int]]] | np.ndarray | None): The palette of - segmentation map. If None is given, random palette will be - generated. Default: None - win_name (str): The window name. - wait_time (int): Value of waitKey param. - Default: 0. - show (bool): Whether to show the image. - Default: False. - out_file (str or None): The filename to write the image. - Default: None. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - Returns: - img (Tensor): Only if not `show` or `out_file` - """ - img = mmcv.imread(img) - img = img.copy() - seg = result[0] - if palette is None: - if self.PALETTE is None: - # Get random state before set seed, - # and restore random state later. - # It will prevent loss of randomness, as the palette - # may be different in each iteration if not specified. - # See: https://github.com/open-mmlab/mmdetection/issues/5844 - state = np.random.get_state() - np.random.seed(42) - # random palette - palette = np.random.randint( - 0, 255, size=(len(self.CLASSES), 3)) - np.random.set_state(state) - else: - palette = self.PALETTE - palette = np.array(palette) - assert palette.shape[0] == len(self.CLASSES) - assert palette.shape[1] == 3 - assert len(palette.shape) == 2 - assert 0 < opacity <= 1.0 - color_seg = np.zeros((seg.shape[0], seg.shape[1], 3), dtype=np.uint8) - for label, color in enumerate(palette): - color_seg[seg == label, :] = color - # convert to BGR - color_seg = color_seg[..., ::-1] - - img = img * (1 - opacity) + color_seg * opacity - img = img.astype(np.uint8) - # if out_file specified, do not show image in window - if out_file is not None: - show = False - - if show: - mmcv.imshow(img, win_name, wait_time) - if out_file is not None: - mmcv.imwrite(img, out_file) - - if not (show or out_file): - warnings.warn('show==False and out_file is not specified, only ' - 'result image will be returned') - return img diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py deleted file mode 100644 index 1913a22e2..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from mmseg.core import add_prefix -from mmseg.ops import resize -from .. import builder -from ..builder import SEGMENTORS -from .encoder_decoder import EncoderDecoder - - -@SEGMENTORS.register_module() -class CascadeEncoderDecoder(EncoderDecoder): - """Cascade Encoder Decoder segmentors. - - CascadeEncoderDecoder almost the same as EncoderDecoder, while decoders of - CascadeEncoderDecoder are cascaded. The output of previous decoder_head - will be the input of next decoder_head. - """ - - def __init__(self, - num_stages, - backbone, - decode_head, - neck=None, - auxiliary_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - self.num_stages = num_stages - super(CascadeEncoderDecoder, self).__init__( - backbone=backbone, - decode_head=decode_head, - neck=neck, - auxiliary_head=auxiliary_head, - train_cfg=train_cfg, - test_cfg=test_cfg, - pretrained=pretrained, - init_cfg=init_cfg) - - def _init_decode_head(self, decode_head): - """Initialize ``decode_head``""" - assert isinstance(decode_head, list) - assert len(decode_head) == self.num_stages - self.decode_head = nn.ModuleList() - for i in range(self.num_stages): - self.decode_head.append(builder.build_head(decode_head[i])) - self.align_corners = self.decode_head[-1].align_corners - self.num_classes = self.decode_head[-1].num_classes - - def encode_decode(self, img, img_metas): - """Encode images with backbone and decode into a semantic segmentation - map of the same size as input.""" - x = self.extract_feat(img) - out = self.decode_head[0].forward_test(x, img_metas, self.test_cfg) - for i in range(1, self.num_stages): - out = self.decode_head[i].forward_test(x, out, img_metas, - self.test_cfg) - out = resize( - input=out, - size=img.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - return out - - def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for decode head in - training.""" - losses = dict() - - loss_decode = self.decode_head[0].forward_train( - x, img_metas, gt_semantic_seg, self.train_cfg) - - losses.update(add_prefix(loss_decode, 'decode_0')) - - for i in range(1, self.num_stages): - # forward test again, maybe unnecessary for most methods. - if i == 1: - prev_outputs = self.decode_head[0].forward_test( - x, img_metas, self.test_cfg) - else: - prev_outputs = self.decode_head[i - 1].forward_test( - x, prev_outputs, img_metas, self.test_cfg) - loss_decode = self.decode_head[i].forward_train( - x, prev_outputs, img_metas, gt_semantic_seg, self.train_cfg) - losses.update(add_prefix(loss_decode, f'decode_{i}')) - - return losses diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/encoder_decoder.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/encoder_decoder.py deleted file mode 100644 index 72467b469..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/segmentors/encoder_decoder.py +++ /dev/null @@ -1,284 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmseg.core import add_prefix -from mmseg.ops import resize -from .. import builder -from ..builder import SEGMENTORS -from .base import BaseSegmentor - - -@SEGMENTORS.register_module() -class EncoderDecoder(BaseSegmentor): - """Encoder Decoder segmentors. - - EncoderDecoder typically consists of backbone, decode_head, auxiliary_head. - Note that auxiliary_head is only used for deep supervision during training, - which could be dumped during inference. - """ - - def __init__(self, - backbone, - decode_head, - neck=None, - auxiliary_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - super(EncoderDecoder, self).__init__(init_cfg) - if pretrained is not None: - assert backbone.get('pretrained') is None, \ - 'both backbone and segmentor set pretrained weight' - backbone.pretrained = pretrained - self.backbone = builder.build_backbone(backbone) - if neck is not None: - self.neck = builder.build_neck(neck) - self._init_decode_head(decode_head) - self._init_auxiliary_head(auxiliary_head) - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - assert self.with_decode_head - - def _init_decode_head(self, decode_head): - """Initialize ``decode_head``""" - self.decode_head = builder.build_head(decode_head) - self.align_corners = self.decode_head.align_corners - self.num_classes = self.decode_head.num_classes - - def _init_auxiliary_head(self, auxiliary_head): - """Initialize ``auxiliary_head``""" - if auxiliary_head is not None: - if isinstance(auxiliary_head, list): - self.auxiliary_head = nn.ModuleList() - for head_cfg in auxiliary_head: - self.auxiliary_head.append(builder.build_head(head_cfg)) - else: - self.auxiliary_head = builder.build_head(auxiliary_head) - - def extract_feat(self, img): - """Extract features from images.""" - x = self.backbone(img) - if self.with_neck: - x = self.neck(x) - return x - - def encode_decode(self, img, img_metas): - """Encode images with backbone and decode into a semantic segmentation - map of the same size as input.""" - x = self.extract_feat(img) - out = self._decode_head_forward_test(x, img_metas) - out = resize( - input=out, - size=img.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - return out - - def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for decode head in - training.""" - losses = dict() - loss_decode = self.decode_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - - losses.update(add_prefix(loss_decode, 'decode')) - return losses - - def _decode_head_forward_test(self, x, img_metas): - """Run forward function and calculate loss for decode head in - inference.""" - seg_logits = self.decode_head.forward_test(x, img_metas, self.test_cfg) - return seg_logits - - def _auxiliary_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for auxiliary head in - training.""" - losses = dict() - if isinstance(self.auxiliary_head, nn.ModuleList): - for idx, aux_head in enumerate(self.auxiliary_head): - loss_aux = aux_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - losses.update(add_prefix(loss_aux, f'aux_{idx}')) - else: - loss_aux = self.auxiliary_head.forward_train( - x, img_metas, gt_semantic_seg, self.train_cfg) - losses.update(add_prefix(loss_aux, 'aux')) - - return losses - - def forward_dummy(self, img): - """Dummy forward function.""" - seg_logit = self.encode_decode(img, None) - - return seg_logit - - def forward_train(self, img, img_metas, gt_semantic_seg): - """Forward function for training. - - Args: - img (Tensor): Input images. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - - x = self.extract_feat(img) - - losses = dict() - - loss_decode = self._decode_head_forward_train(x, img_metas, - gt_semantic_seg) - losses.update(loss_decode) - - if self.with_auxiliary_head: - loss_aux = self._auxiliary_head_forward_train( - x, img_metas, gt_semantic_seg) - losses.update(loss_aux) - - return losses - - # TODO refactor - def slide_inference(self, img, img_meta, rescale): - """Inference by sliding-window with overlap. - - If h_crop > h_img or w_crop > w_img, the small patch will be used to - decode without padding. - """ - - h_stride, w_stride = self.test_cfg.stride - h_crop, w_crop = self.test_cfg.crop_size - batch_size, _, h_img, w_img = img.size() - num_classes = self.num_classes - h_grids = max(h_img - h_crop + h_stride - 1, 0) // h_stride + 1 - w_grids = max(w_img - w_crop + w_stride - 1, 0) // w_stride + 1 - preds = img.new_zeros((batch_size, num_classes, h_img, w_img)) - count_mat = img.new_zeros((batch_size, 1, h_img, w_img)) - for h_idx in range(h_grids): - for w_idx in range(w_grids): - y1 = h_idx * h_stride - x1 = w_idx * w_stride - y2 = min(y1 + h_crop, h_img) - x2 = min(x1 + w_crop, w_img) - y1 = max(y2 - h_crop, 0) - x1 = max(x2 - w_crop, 0) - crop_img = img[:, :, y1:y2, x1:x2] - crop_seg_logit = self.encode_decode(crop_img, img_meta) - preds += F.pad(crop_seg_logit, - (int(x1), int(preds.shape[3] - x2), int(y1), - int(preds.shape[2] - y2))) - - count_mat[:, :, y1:y2, x1:x2] += 1 - assert (count_mat == 0).sum() == 0 - if torch.onnx.is_in_onnx_export(): - # cast count_mat to constant while exporting to ONNX - count_mat = torch.from_numpy( - count_mat.cpu().detach().numpy()).to(device=img.device) - preds = preds / count_mat - if rescale: - preds = resize( - preds, - size=img_meta[0]['ori_shape'][:2], - mode='bilinear', - align_corners=self.align_corners, - warning=False) - return preds - - def whole_inference(self, img, img_meta, rescale): - """Inference with full image.""" - - seg_logit = self.encode_decode(img, img_meta) - if rescale: - # support dynamic shape for onnx - if torch.onnx.is_in_onnx_export(): - size = img.shape[2:] - else: - size = img_meta[0]['ori_shape'][:2] - seg_logit = resize( - seg_logit, - size=size, - mode='bilinear', - align_corners=self.align_corners, - warning=False) - - return seg_logit - - def inference(self, img, img_meta, rescale): - """Inference with slide/whole style. - - Args: - img (Tensor): The input image of shape (N, 3, H, W). - img_meta (dict): Image info dict where each dict has: 'img_shape', - 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - rescale (bool): Whether rescale back to original shape. - - Returns: - Tensor: The output segmentation map. - """ - - assert self.test_cfg.mode in ['slide', 'whole'] - ori_shape = img_meta[0]['ori_shape'] - assert all(_['ori_shape'] == ori_shape for _ in img_meta) - if self.test_cfg.mode == 'slide': - seg_logit = self.slide_inference(img, img_meta, rescale) - else: - seg_logit = self.whole_inference(img, img_meta, rescale) - output = F.softmax(seg_logit, dim=1) - flip = img_meta[0]['flip'] - if flip: - flip_direction = img_meta[0]['flip_direction'] - assert flip_direction in ['horizontal', 'vertical'] - if flip_direction == 'horizontal': - output = output.flip(dims=(3, )) - elif flip_direction == 'vertical': - output = output.flip(dims=(2, )) - - return output - - def simple_test(self, img, img_meta, rescale=True): - """Simple test with single image.""" - seg_logit = self.inference(img, img_meta, rescale) - seg_pred = seg_logit.argmax(dim=1) - if torch.onnx.is_in_onnx_export(): - # our inference backend only support 4D output - seg_pred = seg_pred.unsqueeze(0) - return seg_pred - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred - - def aug_test(self, imgs, img_metas, rescale=True): - """Test with augmentations. - - Only rescale=True is supported. - """ - # aug_test rescale all imgs back to ori_shape for now - assert rescale - # to save memory, we get augmented seg logit inplace - seg_logit = self.inference(imgs[0], img_metas[0], rescale) - for i in range(1, len(imgs)): - cur_seg_logit = self.inference(imgs[i], img_metas[i], rescale) - seg_logit += cur_seg_logit - seg_logit /= len(imgs) - seg_pred = seg_logit.argmax(dim=1) - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/__init__.py deleted file mode 100644 index 6d8329021..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .embed import PatchEmbed -from .inverted_residual import InvertedResidual, InvertedResidualV3 -from .make_divisible import make_divisible -from .res_layer import ResLayer -from .se_layer import SELayer -from .self_attention_block import SelfAttentionBlock -from .shape_convert import (nchw2nlc2nchw, nchw_to_nlc, nlc2nchw2nlc, - nlc_to_nchw) -from .up_conv_block import UpConvBlock - -__all__ = [ - 'ResLayer', 'SelfAttentionBlock', 'make_divisible', 'InvertedResidual', - 'UpConvBlock', 'InvertedResidualV3', 'SELayer', 'PatchEmbed', - 'nchw_to_nlc', 'nlc_to_nchw', 'nchw2nlc2nchw', 'nlc2nchw2nlc' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/embed.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/embed.py deleted file mode 100644 index 1515675e1..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/embed.py +++ /dev/null @@ -1,330 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -from typing import Sequence - -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner.base_module import BaseModule -from mmcv.utils import to_2tuple - - -class AdaptivePadding(nn.Module): - """Applies padding to input (if needed) so that input can get fully covered - by filter you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad zero around - input. The "corner" mode would pad zero to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel: - stride (int | tuple): Stride of the filter. Default: 1: - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - - super(AdaptivePadding, self).__init__() - - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The config dict for embedding - conv layer type selection. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int, optional): The slide stride of embedding conv. - Default: None (Would be set as `kernel_size`). - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only work when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=None, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super(PatchEmbed, self).__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adap_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adap_padding: - pad_h, pad_w = self.adap_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adap_padding: - x = self.adap_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map. Our implementation uses `nn.Unfold` to - merge patch, which is about 25% faster than original implementation. - Instead, we need to modify pretrained models for compatibility. - - Args: - in_channels (int): The num of input channels. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adap_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - - if self.adap_padding: - x = self.adap_padding(x) - H, W = x.shape[-2:] - - x = self.sampler(x) - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/inverted_residual.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/inverted_residual.py deleted file mode 100644 index c9cda7682..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/inverted_residual.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import ConvModule -from torch import nn -from torch.utils import checkpoint as cp - -from .se_layer import SELayer - - -class InvertedResidual(nn.Module): - """InvertedResidual block for MobileNetV2. - - Args: - in_channels (int): The input channels of the InvertedResidual block. - out_channels (int): The output channels of the InvertedResidual block. - stride (int): Stride of the middle (first) 3x3 convolution. - expand_ratio (int): Adjusts number of channels of the hidden layer - in InvertedResidual by this amount. - dilation (int): Dilation rate of depthwise conv. Default: 1 - conv_cfg (dict): Config dict for convolution layer. - Default: None, which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU6'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - stride, - expand_ratio, - dilation=1, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU6'), - with_cp=False, - **kwargs): - super(InvertedResidual, self).__init__() - self.stride = stride - assert stride in [1, 2], f'stride must in [1, 2]. ' \ - f'But received {stride}.' - self.with_cp = with_cp - self.use_res_connect = self.stride == 1 and in_channels == out_channels - hidden_dim = int(round(in_channels * expand_ratio)) - - layers = [] - if expand_ratio != 1: - layers.append( - ConvModule( - in_channels=in_channels, - out_channels=hidden_dim, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs)) - layers.extend([ - ConvModule( - in_channels=hidden_dim, - out_channels=hidden_dim, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - groups=hidden_dim, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs), - ConvModule( - in_channels=hidden_dim, - out_channels=out_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None, - **kwargs) - ]) - self.conv = nn.Sequential(*layers) - - def forward(self, x): - - def _inner_forward(x): - if self.use_res_connect: - return x + self.conv(x) - else: - return self.conv(x) - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out - - -class InvertedResidualV3(nn.Module): - """Inverted Residual Block for MobileNetV3. - - Args: - in_channels (int): The input channels of this Module. - out_channels (int): The output channels of this Module. - mid_channels (int): The input channels of the depthwise convolution. - kernel_size (int): The kernel size of the depthwise convolution. - Default: 3. - stride (int): The stride of the depthwise convolution. Default: 1. - se_cfg (dict): Config dict for se layer. Default: None, which means no - se layer. - with_expand_conv (bool): Use expand conv or not. If set False, - mid_channels must be the same with in_channels. Default: True. - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - mid_channels, - kernel_size=3, - stride=1, - se_cfg=None, - with_expand_conv=True, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - with_cp=False): - super(InvertedResidualV3, self).__init__() - self.with_res_shortcut = (stride == 1 and in_channels == out_channels) - assert stride in [1, 2] - self.with_cp = with_cp - self.with_se = se_cfg is not None - self.with_expand_conv = with_expand_conv - - if self.with_se: - assert isinstance(se_cfg, dict) - if not self.with_expand_conv: - assert mid_channels == in_channels - - if self.with_expand_conv: - self.expand_conv = ConvModule( - in_channels=in_channels, - out_channels=mid_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.depthwise_conv = ConvModule( - in_channels=mid_channels, - out_channels=mid_channels, - kernel_size=kernel_size, - stride=stride, - padding=kernel_size // 2, - groups=mid_channels, - conv_cfg=dict( - type='Conv2dAdaptivePadding') if stride == 2 else conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - if self.with_se: - self.se = SELayer(**se_cfg) - - self.linear_conv = ConvModule( - in_channels=mid_channels, - out_channels=out_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - def forward(self, x): - - def _inner_forward(x): - out = x - - if self.with_expand_conv: - out = self.expand_conv(out) - - out = self.depthwise_conv(out) - - if self.with_se: - out = self.se(out) - - out = self.linear_conv(out) - - if self.with_res_shortcut: - return x + out - else: - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/make_divisible.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/make_divisible.py deleted file mode 100644 index ed42c2eee..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/make_divisible.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def make_divisible(value, divisor, min_value=None, min_ratio=0.9): - """Make divisible function. - - This function rounds the channel number to the nearest value that can be - divisible by the divisor. It is taken from the original tf repo. It ensures - that all layers have a channel number that is divisible by divisor. It can - be seen here: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py # noqa - - Args: - value (int): The original channel number. - divisor (int): The divisor to fully divide the channel number. - min_value (int): The minimum value of the output channel. - Default: None, means that the minimum value equal to the divisor. - min_ratio (float): The minimum ratio of the rounded channel number to - the original channel number. Default: 0.9. - - Returns: - int: The modified output channel number. - """ - - if min_value is None: - min_value = divisor - new_value = max(min_value, int(value + divisor / 2) // divisor * divisor) - # Make sure that round down does not go down by more than (1-min_ratio). - if new_value < min_ratio * value: - new_value += divisor - return new_value diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/res_layer.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/res_layer.py deleted file mode 100644 index 190a0c5d5..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/res_layer.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner import Sequential -from torch import nn as nn - - -class ResLayer(Sequential): - """ResLayer to build ResNet style backbone. - - Args: - block (nn.Module): block used to build ResLayer. - inplanes (int): inplanes of block. - planes (int): planes of block. - num_blocks (int): number of blocks. - stride (int): stride of the first block. Default: 1 - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False - conv_cfg (dict): dictionary to construct and config conv layer. - Default: None - norm_cfg (dict): dictionary to construct and config norm layer. - Default: dict(type='BN') - multi_grid (int | None): Multi grid dilation rates of last - stage. Default: None - contract_dilation (bool): Whether contract first dilation of each layer - Default: False - """ - - def __init__(self, - block, - inplanes, - planes, - num_blocks, - stride=1, - dilation=1, - avg_down=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - multi_grid=None, - contract_dilation=False, - **kwargs): - self.block = block - - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = [] - conv_stride = stride - if avg_down: - conv_stride = 1 - downsample.append( - nn.AvgPool2d( - kernel_size=stride, - stride=stride, - ceil_mode=True, - count_include_pad=False)) - downsample.extend([ - build_conv_layer( - conv_cfg, - inplanes, - planes * block.expansion, - kernel_size=1, - stride=conv_stride, - bias=False), - build_norm_layer(norm_cfg, planes * block.expansion)[1] - ]) - downsample = nn.Sequential(*downsample) - - layers = [] - if multi_grid is None: - if dilation > 1 and contract_dilation: - first_dilation = dilation // 2 - else: - first_dilation = dilation - else: - first_dilation = multi_grid[0] - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=stride, - dilation=first_dilation, - downsample=downsample, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - inplanes = planes * block.expansion - for i in range(1, num_blocks): - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=1, - dilation=dilation if multi_grid is None else multi_grid[i], - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - super(ResLayer, self).__init__(*layers) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/se_layer.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/se_layer.py deleted file mode 100644 index 16f52aa5c..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/se_layer.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch.nn as nn -from mmcv.cnn import ConvModule - -from .make_divisible import make_divisible - - -class SELayer(nn.Module): - """Squeeze-and-Excitation Module. - - Args: - channels (int): The input (and output) channels of the SE layer. - ratio (int): Squeeze ratio in SELayer, the intermediate channel will be - ``int(channels/ratio)``. Default: 16. - conv_cfg (None or dict): Config dict for convolution layer. - Default: None, which means using conv2d. - act_cfg (dict or Sequence[dict]): Config dict for activation layer. - If act_cfg is a dict, two activation layers will be configured - by this dict. If act_cfg is a sequence of dicts, the first - activation layer will be configured by the first dict and the - second activation layer will be configured by the second dict. - Default: (dict(type='ReLU'), dict(type='HSigmoid', bias=3.0, - divisor=6.0)). - """ - - def __init__(self, - channels, - ratio=16, - conv_cfg=None, - act_cfg=(dict(type='ReLU'), - dict(type='HSigmoid', bias=3.0, divisor=6.0))): - super(SELayer, self).__init__() - if isinstance(act_cfg, dict): - act_cfg = (act_cfg, act_cfg) - assert len(act_cfg) == 2 - assert mmcv.is_tuple_of(act_cfg, dict) - self.global_avgpool = nn.AdaptiveAvgPool2d(1) - self.conv1 = ConvModule( - in_channels=channels, - out_channels=make_divisible(channels // ratio, 8), - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[0]) - self.conv2 = ConvModule( - in_channels=make_divisible(channels // ratio, 8), - out_channels=channels, - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[1]) - - def forward(self, x): - out = self.global_avgpool(x) - out = self.conv1(out) - out = self.conv2(out) - return x * out diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/self_attention_block.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/self_attention_block.py deleted file mode 100644 index c945fa716..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/self_attention_block.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.cnn import ConvModule, constant_init -from torch import nn as nn -from torch.nn import functional as F - - -class SelfAttentionBlock(nn.Module): - """General self-attention block/non-local block. - - Please refer to https://arxiv.org/abs/1706.03762 for details about key, - query and value. - - Args: - key_in_channels (int): Input channels of key feature. - query_in_channels (int): Input channels of query feature. - channels (int): Output channels of key/query transform. - out_channels (int): Output channels. - share_key_query (bool): Whether share projection weight between key - and query projection. - query_downsample (nn.Module): Query downsample module. - key_downsample (nn.Module): Key downsample module. - key_query_num_convs (int): Number of convs for key/query projection. - value_num_convs (int): Number of convs for value projection. - matmul_norm (bool): Whether normalize attention map with sqrt of - channels - with_out (bool): Whether use out projection. - conv_cfg (dict|None): Config of conv layers. - norm_cfg (dict|None): Config of norm layers. - act_cfg (dict|None): Config of activation layers. - """ - - def __init__(self, key_in_channels, query_in_channels, channels, - out_channels, share_key_query, query_downsample, - key_downsample, key_query_num_convs, value_out_num_convs, - key_query_norm, value_out_norm, matmul_norm, with_out, - conv_cfg, norm_cfg, act_cfg): - super(SelfAttentionBlock, self).__init__() - if share_key_query: - assert key_in_channels == query_in_channels - self.key_in_channels = key_in_channels - self.query_in_channels = query_in_channels - self.out_channels = out_channels - self.channels = channels - self.share_key_query = share_key_query - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.key_project = self.build_project( - key_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if share_key_query: - self.query_project = self.key_project - else: - self.query_project = self.build_project( - query_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.value_project = self.build_project( - key_in_channels, - channels if with_out else out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if with_out: - self.out_project = self.build_project( - channels, - out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.out_project = None - - self.query_downsample = query_downsample - self.key_downsample = key_downsample - self.matmul_norm = matmul_norm - - self.init_weights() - - def init_weights(self): - """Initialize weight of later layer.""" - if self.out_project is not None: - if not isinstance(self.out_project, ConvModule): - constant_init(self.out_project, 0) - - def build_project(self, in_channels, channels, num_convs, use_conv_module, - conv_cfg, norm_cfg, act_cfg): - """Build projection layer for key/query/value/out.""" - if use_conv_module: - convs = [ - ConvModule( - in_channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - ] - for _ in range(num_convs - 1): - convs.append( - ConvModule( - channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - else: - convs = [nn.Conv2d(in_channels, channels, 1)] - for _ in range(num_convs - 1): - convs.append(nn.Conv2d(channels, channels, 1)) - if len(convs) > 1: - convs = nn.Sequential(*convs) - else: - convs = convs[0] - return convs - - def forward(self, query_feats, key_feats): - """Forward function.""" - batch_size = query_feats.size(0) - query = self.query_project(query_feats) - if self.query_downsample is not None: - query = self.query_downsample(query) - query = query.reshape(*query.shape[:2], -1) - query = query.permute(0, 2, 1).contiguous() - - key = self.key_project(key_feats) - value = self.value_project(key_feats) - if self.key_downsample is not None: - key = self.key_downsample(key) - value = self.key_downsample(value) - key = key.reshape(*key.shape[:2], -1) - value = value.reshape(*value.shape[:2], -1) - value = value.permute(0, 2, 1).contiguous() - - sim_map = torch.matmul(query, key) - if self.matmul_norm: - sim_map = (self.channels**-.5) * sim_map - sim_map = F.softmax(sim_map, dim=-1) - - context = torch.matmul(sim_map, value) - context = context.permute(0, 2, 1).contiguous() - context = context.reshape(batch_size, -1, *query_feats.shape[2:]) - if self.out_project is not None: - context = self.out_project(context) - return context diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/shape_convert.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/shape_convert.py deleted file mode 100644 index cce1e220b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/shape_convert.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def nlc_to_nchw(x, hw_shape): - """Convert [N, L, C] shape tensor to [N, C, H, W] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, L, C] before conversion. - hw_shape (Sequence[int]): The height and width of output feature map. - - Returns: - Tensor: The output tensor of shape [N, C, H, W] after conversion. - """ - H, W = hw_shape - assert len(x.shape) == 3 - B, L, C = x.shape - assert L == H * W, 'The seq_len doesn\'t match H, W' - return x.transpose(1, 2).reshape(B, C, H, W) - - -def nchw_to_nlc(x): - """Flatten [N, C, H, W] shape tensor to [N, L, C] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, C, H, W] before conversion. - - Returns: - Tensor: The output tensor of shape [N, L, C] after conversion. - """ - assert len(x.shape) == 4 - return x.flatten(2).transpose(1, 2).contiguous() - - -def nchw2nlc2nchw(module, x, contiguous=False, **kwargs): - """Flatten [N, C, H, W] shape tensor `x` to [N, L, C] shape tensor. Use the - reshaped tensor as the input of `module`, and the convert the output of - `module`, whose shape is. - - [N, L, C], to [N, C, H, W]. - - Args: - module (Callable): A callable object the takes a tensor - with shape [N, L, C] as input. - x (Tensor): The input tensor of shape [N, C, H, W]. - contiguous: - contiguous (Bool): Whether to make the tensor contiguous - after each shape transform. - - Returns: - Tensor: The output tensor of shape [N, C, H, W]. - - Example: - >>> import torch - >>> import torch.nn as nn - >>> norm = nn.LayerNorm(4) - >>> feature_map = torch.rand(4, 4, 5, 5) - >>> output = nchw2nlc2nchw(norm, feature_map) - """ - B, C, H, W = x.shape - if not contiguous: - x = x.flatten(2).transpose(1, 2) - x = module(x, **kwargs) - x = x.transpose(1, 2).reshape(B, C, H, W) - else: - x = x.flatten(2).transpose(1, 2).contiguous() - x = module(x, **kwargs) - x = x.transpose(1, 2).reshape(B, C, H, W).contiguous() - return x - - -def nlc2nchw2nlc(module, x, hw_shape, contiguous=False, **kwargs): - """Convert [N, L, C] shape tensor `x` to [N, C, H, W] shape tensor. Use the - reshaped tensor as the input of `module`, and convert the output of - `module`, whose shape is. - - [N, C, H, W], to [N, L, C]. - - Args: - module (Callable): A callable object the takes a tensor - with shape [N, C, H, W] as input. - x (Tensor): The input tensor of shape [N, L, C]. - hw_shape: (Sequence[int]): The height and width of the - feature map with shape [N, C, H, W]. - contiguous (Bool): Whether to make the tensor contiguous - after each shape transform. - - Returns: - Tensor: The output tensor of shape [N, L, C]. - - Example: - >>> import torch - >>> import torch.nn as nn - >>> conv = nn.Conv2d(16, 16, 3, 1, 1) - >>> feature_map = torch.rand(4, 25, 16) - >>> output = nlc2nchw2nlc(conv, feature_map, (5, 5)) - """ - H, W = hw_shape - assert len(x.shape) == 3 - B, L, C = x.shape - assert L == H * W, 'The seq_len doesn\'t match H, W' - if not contiguous: - x = x.transpose(1, 2).reshape(B, C, H, W) - x = module(x, **kwargs) - x = x.flatten(2).transpose(1, 2) - else: - x = x.transpose(1, 2).reshape(B, C, H, W).contiguous() - x = module(x, **kwargs) - x = x.flatten(2).transpose(1, 2).contiguous() - return x diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/up_conv_block.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/up_conv_block.py deleted file mode 100644 index d8396d9c2..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/models/utils/up_conv_block.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, build_upsample_layer - - -class UpConvBlock(nn.Module): - """Upsample convolution block in decoder for UNet. - - This upsample convolution block consists of one upsample module - followed by one convolution block. The upsample module expands the - high-level low-resolution feature map and the convolution block fuses - the upsampled high-level low-resolution feature map and the low-level - high-resolution feature map from encoder. - - Args: - conv_block (nn.Sequential): Sequential of convolutional layers. - in_channels (int): Number of input channels of the high-level - skip_channels (int): Number of input channels of the low-level - high-resolution feature map from encoder. - out_channels (int): Number of output channels. - num_convs (int): Number of convolutional layers in the conv_block. - Default: 2. - stride (int): Stride of convolutional layer in conv_block. Default: 1. - dilation (int): Dilation rate of convolutional layer in conv_block. - Default: 1. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - conv_cfg (dict | None): Config dict for convolution layer. - Default: None. - norm_cfg (dict | None): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict | None): Config dict for activation layer in ConvModule. - Default: dict(type='ReLU'). - upsample_cfg (dict): The upsample config of the upsample module in - decoder. Default: dict(type='InterpConv'). If the size of - high-level feature map is the same as that of skip feature map - (low-level feature map from encoder), it does not need upsample the - high-level feature map and the upsample_cfg is None. - dcn (bool): Use deformable convolution in convolutional layer or not. - Default: None. - plugins (dict): plugins for convolutional layers. Default: None. - """ - - def __init__(self, - conv_block, - in_channels, - skip_channels, - out_channels, - num_convs=2, - stride=1, - dilation=1, - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - upsample_cfg=dict(type='InterpConv'), - dcn=None, - plugins=None): - super(UpConvBlock, self).__init__() - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.conv_block = conv_block( - in_channels=2 * skip_channels, - out_channels=out_channels, - num_convs=num_convs, - stride=stride, - dilation=dilation, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - dcn=None, - plugins=None) - if upsample_cfg is not None: - self.upsample = build_upsample_layer( - cfg=upsample_cfg, - in_channels=in_channels, - out_channels=skip_channels, - with_cp=with_cp, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.upsample = ConvModule( - in_channels, - skip_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, skip, x): - """Forward function.""" - - x = self.upsample(x) - out = torch.cat([skip, x], dim=1) - out = self.conv_block(out) - - return out diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/ops/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/ops/__init__.py deleted file mode 100644 index bc075cd4e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/ops/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .encoding import Encoding -from .wrappers import Upsample, resize - -__all__ = ['Upsample', 'resize', 'Encoding'] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/ops/encoding.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/ops/encoding.py deleted file mode 100644 index f397cc54e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/ops/encoding.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn -from torch.nn import functional as F - - -class Encoding(nn.Module): - """Encoding Layer: a learnable residual encoder. - - Input is of shape (batch_size, channels, height, width). - Output is of shape (batch_size, num_codes, channels). - - Args: - channels: dimension of the features or feature channels - num_codes: number of code words - """ - - def __init__(self, channels, num_codes): - super(Encoding, self).__init__() - # init codewords and smoothing factor - self.channels, self.num_codes = channels, num_codes - std = 1. / ((num_codes * channels)**0.5) - # [num_codes, channels] - self.codewords = nn.Parameter( - torch.empty(num_codes, channels, - dtype=torch.float).uniform_(-std, std), - requires_grad=True) - # [num_codes] - self.scale = nn.Parameter( - torch.empty(num_codes, dtype=torch.float).uniform_(-1, 0), - requires_grad=True) - - @staticmethod - def scaled_l2(x, codewords, scale): - num_codes, channels = codewords.size() - batch_size = x.size(0) - reshaped_scale = scale.view((1, 1, num_codes)) - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - - scaled_l2_norm = reshaped_scale * ( - expanded_x - reshaped_codewords).pow(2).sum(dim=3) - return scaled_l2_norm - - @staticmethod - def aggregate(assignment_weights, x, codewords): - num_codes, channels = codewords.size() - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - batch_size = x.size(0) - - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - encoded_feat = (assignment_weights.unsqueeze(3) * - (expanded_x - reshaped_codewords)).sum(dim=1) - return encoded_feat - - def forward(self, x): - assert x.dim() == 4 and x.size(1) == self.channels - # [batch_size, channels, height, width] - batch_size = x.size(0) - # [batch_size, height x width, channels] - x = x.view(batch_size, self.channels, -1).transpose(1, 2).contiguous() - # assignment_weights: [batch_size, channels, num_codes] - assignment_weights = F.softmax( - self.scaled_l2(x, self.codewords, self.scale), dim=2) - # aggregate - encoded_feat = self.aggregate(assignment_weights, x, self.codewords) - return encoded_feat - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(Nx{self.channels}xHxW =>Nx{self.num_codes}' \ - f'x{self.channels})' - return repr_str diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/ops/wrappers.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/ops/wrappers.py deleted file mode 100644 index ce67e4beb..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/ops/wrappers.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.nn.functional as F - - -def resize(input, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None, - warning=True): - if warning: - if size is not None and align_corners: - input_h, input_w = tuple(int(x) for x in input.shape[2:]) - output_h, output_w = tuple(int(x) for x in size) - if output_h > input_h or output_w > output_h: - if ((output_h > 1 and output_w > 1 and input_h > 1 - and input_w > 1) and (output_h - 1) % (input_h - 1) - and (output_w - 1) % (input_w - 1)): - warnings.warn( - f'When align_corners={align_corners}, ' - 'the output would more aligned if ' - f'input size {(input_h, input_w)} is `x+1` and ' - f'out size {(output_h, output_w)} is `nx+1`') - return F.interpolate(input, size, scale_factor, mode, align_corners) - - -class Upsample(nn.Module): - - def __init__(self, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None): - super(Upsample, self).__init__() - self.size = size - if isinstance(scale_factor, tuple): - self.scale_factor = tuple(float(factor) for factor in scale_factor) - else: - self.scale_factor = float(scale_factor) if scale_factor else None - self.mode = mode - self.align_corners = align_corners - - def forward(self, x): - if not self.size: - size = [int(t * self.scale_factor) for t in x.shape[-2:]] - else: - size = self.size - return resize(x, size, None, self.mode, self.align_corners) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/__init__.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/__init__.py deleted file mode 100644 index ed002c7de..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collect_env import collect_env -from .logger import get_root_logger -from .misc import find_latest_checkpoint -from .set_env import setup_multi_processes - -__all__ = [ - 'get_root_logger', 'collect_env', 'find_latest_checkpoint', - 'setup_multi_processes' -] diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/collect_env.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/collect_env.py deleted file mode 100644 index 3379ecb06..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/collect_env.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash - -import mmseg - - -def collect_env(): - """Collect the information of the running environments.""" - env_info = collect_base_env() - env_info['MMSegmentation'] = f'{mmseg.__version__}+{get_git_hash()[:7]}' - - return env_info - - -if __name__ == '__main__': - for name, val in collect_env().items(): - print('{}: {}'.format(name, val)) diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/logger.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/logger.py deleted file mode 100644 index 0cb3c78d6..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/logger.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -from mmcv.utils import get_logger - - -def get_root_logger(log_file=None, log_level=logging.INFO): - """Get the root logger. - - The logger will be initialized if it has not been initialized. By default a - StreamHandler will be added. If `log_file` is specified, a FileHandler will - also be added. The name of the root logger is the top-level package name, - e.g., "mmseg". - - Args: - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the root logger. - log_level (int): The root logger level. Note that only the process of - rank 0 is affected, while other processes will set the level to - "Error" and be silent most of the time. - - Returns: - logging.Logger: The root logger. - """ - - logger = get_logger(name='mmseg', log_file=log_file, log_level=log_level) - - return logger diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/misc.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/misc.py deleted file mode 100644 index bd1b6b163..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/misc.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os.path as osp -import warnings - - -def find_latest_checkpoint(path, suffix='pth'): - """This function is for finding the latest checkpoint. - - It will be used when automatically resume, modified from - https://github.com/open-mmlab/mmdetection/blob/dev-v2.20.0/mmdet/utils/misc.py - - Args: - path (str): The path to find checkpoints. - suffix (str): File extension for the checkpoint. Defaults to pth. - - Returns: - latest_path(str | None): File path of the latest checkpoint. - """ - if not osp.exists(path): - warnings.warn("The path of the checkpoints doesn't exist.") - return None - if osp.exists(osp.join(path, f'latest.{suffix}')): - return osp.join(path, f'latest.{suffix}') - - checkpoints = glob.glob(osp.join(path, f'*.{suffix}')) - if len(checkpoints) == 0: - warnings.warn('The are no checkpoints in the path') - return None - latest = -1 - latest_path = '' - for checkpoint in checkpoints: - if len(checkpoint) < len(latest_path): - continue - # `count` is iteration number, as checkpoints are saved as - # 'iter_xx.pth' or 'epoch_xx.pth' and xx is iteration number. - count = int(osp.basename(checkpoint).split('_')[-1].split('.')[0]) - if count > latest: - latest = count - latest_path = checkpoint - return latest_path diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/set_env.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/set_env.py deleted file mode 100644 index b2d3aaf14..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/utils/set_env.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import platform - -import cv2 -import torch.multiprocessing as mp - -from ..utils import get_root_logger - - -def setup_multi_processes(cfg): - """Setup multi-processing environment variables.""" - logger = get_root_logger() - - # set multi-process start method - if platform.system() != 'Windows': - mp_start_method = cfg.get('mp_start_method', None) - current_method = mp.get_start_method(allow_none=True) - if mp_start_method in ('fork', 'spawn', 'forkserver'): - logger.info( - f'Multi-processing start method `{mp_start_method}` is ' - f'different from the previous setting `{current_method}`.' - f'It will be force set to `{mp_start_method}`.') - mp.set_start_method(mp_start_method, force=True) - else: - logger.info( - f'Multi-processing start method is `{mp_start_method}`') - - # disable opencv multithreading to avoid system being overloaded - opencv_num_threads = cfg.get('opencv_num_threads', None) - if isinstance(opencv_num_threads, int): - logger.info(f'OpenCV num_threads is `{opencv_num_threads}`') - cv2.setNumThreads(opencv_num_threads) - else: - logger.info(f'OpenCV num_threads is `{cv2.getNumThreads}') - - if cfg.data.workers_per_gpu > 1: - # setup OMP threads - # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa - omp_num_threads = cfg.get('omp_num_threads', None) - if 'OMP_NUM_THREADS' not in os.environ: - if isinstance(omp_num_threads, int): - logger.info(f'OMP num threads is {omp_num_threads}') - os.environ['OMP_NUM_THREADS'] = str(omp_num_threads) - else: - logger.info(f'OMP num threads is {os.environ["OMP_NUM_THREADS"] }') - - # setup MKL threads - if 'MKL_NUM_THREADS' not in os.environ: - mkl_num_threads = cfg.get('mkl_num_threads', None) - if isinstance(mkl_num_threads, int): - logger.info(f'MKL num threads is {mkl_num_threads}') - os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads) - else: - logger.info(f'MKL num threads is {os.environ["MKL_NUM_THREADS"]}') diff --git a/cv/semantic_segmentation/apcnet/pytorch/mmseg/version.py b/cv/semantic_segmentation/apcnet/pytorch/mmseg/version.py deleted file mode 100644 index e05146f0a..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/mmseg/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) Open-MMLab. All rights reserved. - -__version__ = '0.24.1' - - -def parse_version_info(version_str): - version_info = [] - for x in version_str.split('.'): - if x.isdigit(): - version_info.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - version_info.append(int(patch_version[0])) - version_info.append(f'rc{patch_version[1]}') - return tuple(version_info) - - -version_info = parse_version_info(__version__) diff --git a/cv/semantic_segmentation/apcnet/pytorch/requirements.txt b/cv/semantic_segmentation/apcnet/pytorch/requirements.txt deleted file mode 100644 index 40cc187d9..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ --r requirements/mmcv/runtime.txt --r requirements/apcnet.txt -cityscapesscripts \ No newline at end of file diff --git a/cv/semantic_segmentation/apcnet/pytorch/requirements/apcnet.txt b/cv/semantic_segmentation/apcnet/pytorch/requirements/apcnet.txt deleted file mode 100644 index 2712f504c..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/requirements/apcnet.txt +++ /dev/null @@ -1,4 +0,0 @@ -matplotlib -numpy -packaging -prettytable diff --git a/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/build.txt b/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/build.txt deleted file mode 100644 index abf514853..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/build.txt +++ /dev/null @@ -1 +0,0 @@ -pytest-runner diff --git a/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/docs.txt b/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/docs.txt deleted file mode 100644 index 6a5319af3..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/docs.txt +++ /dev/null @@ -1,8 +0,0 @@ -docutils==0.16.0 -myst-parser -opencv-python --e git+https://github.com/open-mmlab/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme -sphinx==4.0.2 -sphinx-copybutton -sphinx_markdown_tables -torch diff --git a/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/optional.txt b/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/optional.txt deleted file mode 100644 index 63730036f..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/optional.txt +++ /dev/null @@ -1 +0,0 @@ -ninja diff --git a/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/requirements.txt b/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/requirements.txt deleted file mode 100644 index 6f00e6303..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ --r requirements/mmcv/build.txt --r requirements/mmcv/optional.txt --r requirements/mmcv/runtime.txt --r requirements/mmcv/test.txt diff --git a/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/runtime.txt b/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/runtime.txt deleted file mode 100644 index 66e90d674..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/runtime.txt +++ /dev/null @@ -1,7 +0,0 @@ -addict -numpy -packaging -Pillow -pyyaml -regex;sys_platform=='win32' -yapf diff --git a/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/test.txt b/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/test.txt deleted file mode 100644 index 6d9d17b98..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/requirements/mmcv/test.txt +++ /dev/null @@ -1,9 +0,0 @@ -coverage -lmdb -onnx==1.7.0; python_version < '3.10' -onnxoptimizer; python_version < '3.10' -onnxruntime>=1.8.0; python_version < '3.10' -pytest -PyTurboJPEG -scipy -tifffile diff --git a/cv/semantic_segmentation/apcnet/pytorch/requirements/mminstall.txt b/cv/semantic_segmentation/apcnet/pytorch/requirements/mminstall.txt deleted file mode 100644 index bd43faf87..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/requirements/mminstall.txt +++ /dev/null @@ -1,2 +0,0 @@ -mmcls>=0.20.1 -mmcv-full>=1.4.4,<=1.6.0 diff --git a/cv/semantic_segmentation/apcnet/pytorch/requirements/optional.txt b/cv/semantic_segmentation/apcnet/pytorch/requirements/optional.txt deleted file mode 100644 index 47fa59331..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/requirements/optional.txt +++ /dev/null @@ -1 +0,0 @@ -cityscapesscripts diff --git a/cv/semantic_segmentation/apcnet/pytorch/requirements/runtime.txt b/cv/semantic_segmentation/apcnet/pytorch/requirements/runtime.txt deleted file mode 100644 index 520408fe8..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/requirements/runtime.txt +++ /dev/null @@ -1,5 +0,0 @@ -matplotlib -mmcls>=0.20.1 -numpy -packaging -prettytable diff --git a/cv/semantic_segmentation/apcnet/pytorch/setup.py b/cv/semantic_segmentation/apcnet/pytorch/setup.py deleted file mode 100644 index d409997db..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/setup.py +++ /dev/null @@ -1,258 +0,0 @@ -import glob -import os -import platform -import re -import warnings -from pkg_resources import DistributionNotFound, get_distribution -from setuptools import find_packages, setup - -EXT_TYPE = '' -try: - import torch - if torch.__version__ == 'parrots': - from parrots.utils.build_extension import BuildExtension - EXT_TYPE = 'parrots' - elif (hasattr(torch, 'is_mlu_available') and torch.is_mlu_available()) or \ - os.getenv('FORCE_MLU', '0') == '1': - from torch_mlu.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - else: - from torch.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - cmd_class = {'build_ext': BuildExtension} -except ModuleNotFoundError: - cmd_class = {} - print('Skip building ext ops due to the absence of torch.') - - -def choose_requirement(primary, secondary): - """If some version of primary requirement installed, return primary, else - return secondary.""" - try: - name = re.split(r'[!<>=]', primary)[0] - get_distribution(name) - except DistributionNotFound: - return secondary - - return str(primary) - - -def get_version(): - version_file = 'mmcv/version.py' - with open(version_file, 'r', encoding='utf-8') as f: - exec(compile(f.read(), version_file, 'exec')) - return locals()['__version__'] - - -def parse_requirements(fname='requirements/mmcv/runtime.txt', with_version=True): - """Parse the package dependencies listed in a requirements file but strips - specific versioning information. - - Args: - fname (str): path to requirements file - with_version (bool, default=False): if True include version specs - - Returns: - List[str]: list of requirements items - - CommandLine: - python -c "import setup; print(setup.parse_requirements())" - """ - import sys - from os.path import exists - require_fpath = fname - - def parse_line(line): - """Parse information from a line in a requirements text file.""" - if line.startswith('-r '): - # Allow specifying requirements in other files - target = line.split(' ')[1] - for info in parse_require_file(target): - yield info - else: - info = {'line': line} - if line.startswith('-e '): - info['package'] = line.split('#egg=')[1] - else: - # Remove versioning from the package - pat = '(' + '|'.join(['>=', '==', '>']) + ')' - parts = re.split(pat, line, maxsplit=1) - parts = [p.strip() for p in parts] - - info['package'] = parts[0] - if len(parts) > 1: - op, rest = parts[1:] - if ';' in rest: - # Handle platform specific dependencies - # http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies - version, platform_deps = map(str.strip, - rest.split(';')) - info['platform_deps'] = platform_deps - else: - version = rest # NOQA - info['version'] = (op, version) - yield info - - def parse_require_file(fpath): - with open(fpath, 'r') as f: - for line in f.readlines(): - line = line.strip() - if line and not line.startswith('#'): - for info in parse_line(line): - yield info - - def gen_packages_items(): - if exists(require_fpath): - for info in parse_require_file(require_fpath): - parts = [info['package']] - if with_version and 'version' in info: - parts.extend(info['version']) - if not sys.version.startswith('3.4'): - # apparently package_deps are broken in 3.4 - platform_deps = info.get('platform_deps') - if platform_deps is not None: - parts.append(';' + platform_deps) - item = ''.join(parts) - yield item - - packages = list(gen_packages_items()) - return packages - - -install_requires = parse_requirements() - -try: - # OpenCV installed via conda. - import cv2 # NOQA: F401 - major, minor, *rest = cv2.__version__.split('.') - if int(major) < 3: - raise RuntimeError( - f'OpenCV >=3 is required but {cv2.__version__} is installed') -except ImportError: - # If first not installed install second package - CHOOSE_INSTALL_REQUIRES = [('opencv-python-headless>=3', - 'opencv-python>=3')] - for main, secondary in CHOOSE_INSTALL_REQUIRES: - install_requires.append(choose_requirement(main, secondary)) - - -def get_extensions(): - extensions = [] - - if EXT_TYPE == 'pytorch': - ext_name = 'mmcv._ext' - from torch.utils.cpp_extension import CppExtension, CUDAExtension - - # prevent ninja from using too many resources - try: - import psutil - num_cpu = len(psutil.Process().cpu_affinity()) - cpu_use = max(4, num_cpu - 1) - except (ModuleNotFoundError, AttributeError): - cpu_use = 4 - - os.environ.setdefault('MAX_JOBS', str(cpu_use)) - define_macros = [] - - # Before PyTorch1.8.0, when compiling CUDA code, `cxx` is a - # required key passed to PyTorch. Even if there is no flag passed - # to cxx, users also need to pass an empty list to PyTorch. - # Since PyTorch1.8.0, it has a default value so users do not need - # to pass an empty list anymore. - # More details at https://github.com/pytorch/pytorch/pull/45956 - extra_compile_args = {'cxx': []} - - # Since the PR (https://github.com/open-mmlab/mmcv/pull/1463) uses - # c++14 features, the argument ['std=c++14'] must be added here. - # However, in the windows environment, some standard libraries - # will depend on c++17 or higher. In fact, for the windows - # environment, the compiler will choose the appropriate compiler - # to compile those cpp files, so there is no need to add the - # argument - if platform.system() != 'Windows': - extra_compile_args['cxx'] = ['-std=c++14'] - - include_dirs = [] - - is_rocm_pytorch = False - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm_pytorch = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - - if is_rocm_pytorch or torch.cuda.is_available() or os.getenv( - 'FORCE_CUDA', '0') == '1': - if is_rocm_pytorch: - define_macros += [('HIP_DIFF', None)] - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cpp') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - else: - print(f'Compiling {ext_name} only with CPU') - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') - extension = CppExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - - # Since the PR (https://github.com/open-mmlab/mmcv/pull/1463) uses - # c++14 features, the argument ['std=c++14'] must be added here. - # However, in the windows environment, some standard libraries - # will depend on c++17 or higher. In fact, for the windows - # environment, the compiler will choose the appropriate compiler - # to compile those cpp files, so there is no need to add the - # argument - if 'nvcc' in extra_compile_args and platform.system() != 'Windows': - extra_compile_args['nvcc'] += ['-std=c++14'] - - ext_ops = extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args) - extensions.append(ext_ops) - - return extensions - - -setup( - name='mmcv' if os.getenv('MMCV_WITH_OPS', '0') == '0' else 'mmcv-full', - version=get_version(), - description='OpenMMLab Computer Vision Foundation', - keywords='computer vision', - packages=find_packages(), - include_package_data=True, - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Topic :: Utilities', - ], - url='https://github.com/open-mmlab/mmcv', - author='MMCV Contributors', - author_email='openmmlab@gmail.com', - install_requires=install_requires, - extras_require={ - 'all': parse_requirements('requirements/mmcv/requirements.txt'), - 'tests': parse_requirements('requirements/mmcv/test.txt'), - 'build': parse_requirements('requirements/mmcv/build.txt'), - 'optional': parse_requirements('requirements/mmcv/optional.txt'), - }, - ext_modules=get_extensions(), - cmdclass=cmd_class, - zip_safe=False) diff --git a/cv/semantic_segmentation/apcnet/pytorch/tools/analyze_logs.py b/cv/semantic_segmentation/apcnet/pytorch/tools/analyze_logs.py deleted file mode 100644 index e2127d4d6..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/tools/analyze_logs.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/open- -mmlab/mmdetection/blob/master/tools/analysis_tools/analyze_logs.py.""" -import argparse -import json -from collections import defaultdict - -import matplotlib.pyplot as plt -import seaborn as sns - - -def plot_curve(log_dicts, args): - if args.backend is not None: - plt.switch_backend(args.backend) - sns.set_style(args.style) - # if legend is None, use {filename}_{key} as legend - legend = args.legend - if legend is None: - legend = [] - for json_log in args.json_logs: - for metric in args.keys: - legend.append(f'{json_log}_{metric}') - assert len(legend) == (len(args.json_logs) * len(args.keys)) - metrics = args.keys - - num_metrics = len(metrics) - for i, log_dict in enumerate(log_dicts): - epochs = list(log_dict.keys()) - for j, metric in enumerate(metrics): - print(f'plot curve of {args.json_logs[i]}, metric is {metric}') - plot_epochs = [] - plot_iters = [] - plot_values = [] - # In some log files exist lines of validation, - # `mode` list is used to only collect iter number - # of training line. - for epoch in epochs: - epoch_logs = log_dict[epoch] - if metric not in epoch_logs.keys(): - continue - if metric in ['mIoU', 'mAcc', 'aAcc']: - plot_epochs.append(epoch) - plot_values.append(epoch_logs[metric][0]) - else: - for idx in range(len(epoch_logs[metric])): - if epoch_logs['mode'][idx] == 'train': - plot_iters.append(epoch_logs['iter'][idx]) - plot_values.append(epoch_logs[metric][idx]) - ax = plt.gca() - label = legend[i * num_metrics + j] - if metric in ['mIoU', 'mAcc', 'aAcc']: - ax.set_xticks(plot_epochs) - plt.xlabel('epoch') - plt.plot(plot_epochs, plot_values, label=label, marker='o') - else: - plt.xlabel('iter') - plt.plot(plot_iters, plot_values, label=label, linewidth=0.5) - plt.legend() - if args.title is not None: - plt.title(args.title) - if args.out is None: - plt.show() - else: - print(f'save curve to: {args.out}') - plt.savefig(args.out) - plt.cla() - - -def parse_args(): - parser = argparse.ArgumentParser(description='Analyze Json Log') - parser.add_argument( - 'json_logs', - type=str, - nargs='+', - help='path of train log in json format') - parser.add_argument( - '--keys', - type=str, - nargs='+', - default=['mIoU'], - help='the metric that you want to plot') - parser.add_argument('--title', type=str, help='title of figure') - parser.add_argument( - '--legend', - type=str, - nargs='+', - default=None, - help='legend of each plot') - parser.add_argument( - '--backend', type=str, default=None, help='backend of plt') - parser.add_argument( - '--style', type=str, default='dark', help='style of plt') - parser.add_argument('--out', type=str, default=None) - args = parser.parse_args() - return args - - -def load_json_logs(json_logs): - # load and convert json_logs to log_dict, key is epoch, value is a sub dict - # keys of sub dict is different metrics - # value of sub dict is a list of corresponding values of all iterations - log_dicts = [dict() for _ in json_logs] - for json_log, log_dict in zip(json_logs, log_dicts): - with open(json_log, 'r') as log_file: - for line in log_file: - log = json.loads(line.strip()) - # skip lines without `epoch` field - if 'epoch' not in log: - continue - epoch = log.pop('epoch') - if epoch not in log_dict: - log_dict[epoch] = defaultdict(list) - for k, v in log.items(): - log_dict[epoch][k].append(v) - return log_dicts - - -def main(): - args = parse_args() - json_logs = args.json_logs - for json_log in json_logs: - assert json_log.endswith('.json') - log_dicts = load_json_logs(json_logs) - plot_curve(log_dicts, args) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/apcnet/pytorch/tools/benchmark.py b/cv/semantic_segmentation/apcnet/pytorch/tools/benchmark.py deleted file mode 100644 index f6d688848..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/tools/benchmark.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import time - -import mmcv -import numpy as np -import torch -from mmcv import Config -from mmcv.parallel import MMDataParallel -from mmcv.runner import load_checkpoint, wrap_fp16_model - -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='MMSeg benchmark a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--log-interval', type=int, default=50, help='interval of logging') - parser.add_argument( - '--work-dir', - help=('if specified, the results will be dumped ' - 'into the directory as json')) - parser.add_argument('--repeat-times', type=int, default=1) - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.work_dir is not None: - mmcv.mkdir_or_exist(osp.abspath(args.work_dir)) - json_file = osp.join(args.work_dir, f'fps_{timestamp}.json') - else: - # use config filename as default work_dir if cfg.work_dir is None - work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - mmcv.mkdir_or_exist(osp.abspath(work_dir)) - json_file = osp.join(work_dir, f'fps_{timestamp}.json') - - repeat_times = args.repeat_times - # set cudnn_benchmark - torch.backends.cudnn.benchmark = False - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - benchmark_dict = dict(config=args.config, unit='img / s') - overall_fps_list = [] - for time_index in range(repeat_times): - print(f'Run {time_index + 1}:') - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - data_loader = build_dataloader( - dataset, - samples_per_gpu=1, - workers_per_gpu=cfg.data.workers_per_gpu, - dist=False, - shuffle=False) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - if 'checkpoint' in args and osp.exists(args.checkpoint): - load_checkpoint(model, args.checkpoint, map_location='cpu') - - model = MMDataParallel(model, device_ids=[0]) - - model.eval() - - # the first several iterations may be very slow so skip them - num_warmup = 5 - pure_inf_time = 0 - total_iters = 200 - - # benchmark with 200 image and take the average - for i, data in enumerate(data_loader): - - torch.cuda.synchronize() - start_time = time.perf_counter() - - with torch.no_grad(): - model(return_loss=False, rescale=True, **data) - - torch.cuda.synchronize() - elapsed = time.perf_counter() - start_time - - if i >= num_warmup: - pure_inf_time += elapsed - if (i + 1) % args.log_interval == 0: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Done image [{i + 1:<3}/ {total_iters}], ' - f'fps: {fps:.2f} img / s') - - if (i + 1) == total_iters: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Overall fps: {fps:.2f} img / s\n') - benchmark_dict[f'overall_fps_{time_index + 1}'] = round(fps, 2) - overall_fps_list.append(fps) - break - benchmark_dict['average_fps'] = round(np.mean(overall_fps_list), 2) - benchmark_dict['fps_variance'] = round(np.var(overall_fps_list), 4) - print(f'Average fps of {repeat_times} evaluations: ' - f'{benchmark_dict["average_fps"]}') - print(f'The variance of {repeat_times} evaluations: ' - f'{benchmark_dict["fps_variance"]}') - mmcv.dump(benchmark_dict, json_file, indent=4) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/apcnet/pytorch/tools/confusion_matrix.py b/cv/semantic_segmentation/apcnet/pytorch/tools/confusion_matrix.py deleted file mode 100644 index 2c5b64cf4..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/tools/confusion_matrix.py +++ /dev/null @@ -1,184 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os - -import matplotlib.pyplot as plt -import mmcv -import numpy as np -from matplotlib.ticker import MultipleLocator -from mmcv import Config, DictAction - -from mmseg.datasets import build_dataset - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Generate confusion matrix from segmentation results') - parser.add_argument('config', help='test config file path') - parser.add_argument( - 'prediction_path', help='prediction path where test .pkl result') - parser.add_argument( - 'save_dir', help='directory where confusion matrix will be saved') - parser.add_argument( - '--show', action='store_true', help='show confusion matrix') - parser.add_argument( - '--color-theme', - default='winter', - help='theme of the matrix color map') - parser.add_argument( - '--title', - default='Normalized Confusion Matrix', - help='title of the matrix color map') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - args = parser.parse_args() - return args - - -def calculate_confusion_matrix(dataset, results): - """Calculate the confusion matrix. - - Args: - dataset (Dataset): Test or val dataset. - results (list[ndarray]): A list of segmentation results in each image. - """ - n = len(dataset.CLASSES) - confusion_matrix = np.zeros(shape=[n, n]) - assert len(dataset) == len(results) - prog_bar = mmcv.ProgressBar(len(results)) - for idx, per_img_res in enumerate(results): - res_segm = per_img_res - gt_segm = dataset.get_gt_seg_map_by_idx(idx) - inds = n * gt_segm + res_segm - inds = inds.flatten() - mat = np.bincount(inds, minlength=n**2).reshape(n, n) - confusion_matrix += mat - prog_bar.update() - return confusion_matrix - - -def plot_confusion_matrix(confusion_matrix, - labels, - save_dir=None, - show=True, - title='Normalized Confusion Matrix', - color_theme='winter'): - """Draw confusion matrix with matplotlib. - - Args: - confusion_matrix (ndarray): The confusion matrix. - labels (list[str]): List of class names. - save_dir (str|optional): If set, save the confusion matrix plot to the - given path. Default: None. - show (bool): Whether to show the plot. Default: True. - title (str): Title of the plot. Default: `Normalized Confusion Matrix`. - color_theme (str): Theme of the matrix color map. Default: `winter`. - """ - # normalize the confusion matrix - per_label_sums = confusion_matrix.sum(axis=1)[:, np.newaxis] - confusion_matrix = \ - confusion_matrix.astype(np.float32) / per_label_sums * 100 - - num_classes = len(labels) - fig, ax = plt.subplots( - figsize=(2 * num_classes, 2 * num_classes * 0.8), dpi=180) - cmap = plt.get_cmap(color_theme) - im = ax.imshow(confusion_matrix, cmap=cmap) - plt.colorbar(mappable=im, ax=ax) - - title_font = {'weight': 'bold', 'size': 12} - ax.set_title(title, fontdict=title_font) - label_font = {'size': 10} - plt.ylabel('Ground Truth Label', fontdict=label_font) - plt.xlabel('Prediction Label', fontdict=label_font) - - # draw locator - xmajor_locator = MultipleLocator(1) - xminor_locator = MultipleLocator(0.5) - ax.xaxis.set_major_locator(xmajor_locator) - ax.xaxis.set_minor_locator(xminor_locator) - ymajor_locator = MultipleLocator(1) - yminor_locator = MultipleLocator(0.5) - ax.yaxis.set_major_locator(ymajor_locator) - ax.yaxis.set_minor_locator(yminor_locator) - - # draw grid - ax.grid(True, which='minor', linestyle='-') - - # draw label - ax.set_xticks(np.arange(num_classes)) - ax.set_yticks(np.arange(num_classes)) - ax.set_xticklabels(labels) - ax.set_yticklabels(labels) - - ax.tick_params( - axis='x', bottom=False, top=True, labelbottom=False, labeltop=True) - plt.setp( - ax.get_xticklabels(), rotation=45, ha='left', rotation_mode='anchor') - - # draw confusion matrix value - for i in range(num_classes): - for j in range(num_classes): - ax.text( - j, - i, - '{}%'.format( - round(confusion_matrix[i, j], 2 - ) if not np.isnan(confusion_matrix[i, j]) else -1), - ha='center', - va='center', - color='w', - size=7) - - ax.set_ylim(len(confusion_matrix) - 0.5, -0.5) # matplotlib>3.1.1 - - fig.tight_layout() - if save_dir is not None: - plt.savefig( - os.path.join(save_dir, 'confusion_matrix.png'), format='png') - if show: - plt.show() - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - results = mmcv.load(args.prediction_path) - - assert isinstance(results, list) - if isinstance(results[0], np.ndarray): - pass - else: - raise TypeError('invalid type of prediction results') - - if isinstance(cfg.data.test, dict): - cfg.data.test.test_mode = True - elif isinstance(cfg.data.test, list): - for ds_cfg in cfg.data.test: - ds_cfg.test_mode = True - - dataset = build_dataset(cfg.data.test) - confusion_matrix = calculate_confusion_matrix(dataset, results) - plot_confusion_matrix( - confusion_matrix, - dataset.CLASSES, - save_dir=args.save_dir, - show=args.show, - title=args.title, - color_theme=args.color_theme) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/cityscapes.py b/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/cityscapes.py deleted file mode 100644 index 17b616847..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/cityscapes.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp - -import mmcv -from cityscapesscripts.preparation.json2labelImg import json2labelImg - - -def convert_json_to_label(json_file): - label_file = json_file.replace('_polygons.json', '_labelTrainIds.png') - json2labelImg(json_file, label_file, 'trainIds') - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert Cityscapes annotations to TrainIds') - parser.add_argument('cityscapes_path', help='cityscapes data path') - parser.add_argument('--gt-dir', default='gtFine', type=str) - parser.add_argument('-o', '--out-dir', help='output path') - parser.add_argument( - '--nproc', default=1, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - cityscapes_path = args.cityscapes_path - out_dir = args.out_dir if args.out_dir else cityscapes_path - mmcv.mkdir_or_exist(out_dir) - - gt_dir = osp.join(cityscapes_path, args.gt_dir) - - poly_files = [] - for poly in mmcv.scandir(gt_dir, '_polygons.json', recursive=True): - poly_file = osp.join(gt_dir, poly) - poly_files.append(poly_file) - if args.nproc > 1: - mmcv.track_parallel_progress(convert_json_to_label, poly_files, - args.nproc) - else: - mmcv.track_progress(convert_json_to_label, poly_files) - - split_names = ['train', 'val', 'test'] - - for split in split_names: - filenames = [] - for poly in mmcv.scandir( - osp.join(gt_dir, split), '_polygons.json', recursive=True): - filenames.append(poly.replace('_gtFine_polygons.json', '')) - with open(osp.join(out_dir, f'{split}.txt'), 'w') as f: - f.writelines(f + '\n' for f in filenames) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/coco_stuff10k.py b/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/coco_stuff10k.py deleted file mode 100644 index 374f81970..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/coco_stuff10k.py +++ /dev/null @@ -1,307 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import shutil -from functools import partial - -import mmcv -import numpy as np -from PIL import Image -from scipy.io import loadmat - -COCO_LEN = 10000 - -clsID_to_trID = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 13: 12, - 14: 13, - 15: 14, - 16: 15, - 17: 16, - 18: 17, - 19: 18, - 20: 19, - 21: 20, - 22: 21, - 23: 22, - 24: 23, - 25: 24, - 27: 25, - 28: 26, - 31: 27, - 32: 28, - 33: 29, - 34: 30, - 35: 31, - 36: 32, - 37: 33, - 38: 34, - 39: 35, - 40: 36, - 41: 37, - 42: 38, - 43: 39, - 44: 40, - 46: 41, - 47: 42, - 48: 43, - 49: 44, - 50: 45, - 51: 46, - 52: 47, - 53: 48, - 54: 49, - 55: 50, - 56: 51, - 57: 52, - 58: 53, - 59: 54, - 60: 55, - 61: 56, - 62: 57, - 63: 58, - 64: 59, - 65: 60, - 67: 61, - 70: 62, - 72: 63, - 73: 64, - 74: 65, - 75: 66, - 76: 67, - 77: 68, - 78: 69, - 79: 70, - 80: 71, - 81: 72, - 82: 73, - 84: 74, - 85: 75, - 86: 76, - 87: 77, - 88: 78, - 89: 79, - 90: 80, - 92: 81, - 93: 82, - 94: 83, - 95: 84, - 96: 85, - 97: 86, - 98: 87, - 99: 88, - 100: 89, - 101: 90, - 102: 91, - 103: 92, - 104: 93, - 105: 94, - 106: 95, - 107: 96, - 108: 97, - 109: 98, - 110: 99, - 111: 100, - 112: 101, - 113: 102, - 114: 103, - 115: 104, - 116: 105, - 117: 106, - 118: 107, - 119: 108, - 120: 109, - 121: 110, - 122: 111, - 123: 112, - 124: 113, - 125: 114, - 126: 115, - 127: 116, - 128: 117, - 129: 118, - 130: 119, - 131: 120, - 132: 121, - 133: 122, - 134: 123, - 135: 124, - 136: 125, - 137: 126, - 138: 127, - 139: 128, - 140: 129, - 141: 130, - 142: 131, - 143: 132, - 144: 133, - 145: 134, - 146: 135, - 147: 136, - 148: 137, - 149: 138, - 150: 139, - 151: 140, - 152: 141, - 153: 142, - 154: 143, - 155: 144, - 156: 145, - 157: 146, - 158: 147, - 159: 148, - 160: 149, - 161: 150, - 162: 151, - 163: 152, - 164: 153, - 165: 154, - 166: 155, - 167: 156, - 168: 157, - 169: 158, - 170: 159, - 171: 160, - 172: 161, - 173: 162, - 174: 163, - 175: 164, - 176: 165, - 177: 166, - 178: 167, - 179: 168, - 180: 169, - 181: 170, - 182: 171 -} - - -def convert_to_trainID(tuple_path, in_img_dir, in_ann_dir, out_img_dir, - out_mask_dir, is_train): - imgpath, maskpath = tuple_path - shutil.copyfile( - osp.join(in_img_dir, imgpath), - osp.join(out_img_dir, 'train2014', imgpath) if is_train else osp.join( - out_img_dir, 'test2014', imgpath)) - annotate = loadmat(osp.join(in_ann_dir, maskpath)) - mask = annotate['S'].astype(np.uint8) - mask_copy = mask.copy() - for clsID, trID in clsID_to_trID.items(): - mask_copy[mask == clsID] = trID - seg_filename = osp.join(out_mask_dir, 'train2014', - maskpath.split('.')[0] + - '_labelTrainIds.png') if is_train else osp.join( - out_mask_dir, 'test2014', - maskpath.split('.')[0] + '_labelTrainIds.png') - Image.fromarray(mask_copy).save(seg_filename, 'PNG') - - -def generate_coco_list(folder): - train_list = osp.join(folder, 'imageLists', 'train.txt') - test_list = osp.join(folder, 'imageLists', 'test.txt') - train_paths = [] - test_paths = [] - - with open(train_list) as f: - for filename in f: - basename = filename.strip() - imgpath = basename + '.jpg' - maskpath = basename + '.mat' - train_paths.append((imgpath, maskpath)) - - with open(test_list) as f: - for filename in f: - basename = filename.strip() - imgpath = basename + '.jpg' - maskpath = basename + '.mat' - test_paths.append((imgpath, maskpath)) - - return train_paths, test_paths - - -def parse_args(): - parser = argparse.ArgumentParser( - description=\ - 'Convert COCO Stuff 10k annotations to mmsegmentation format') # noqa - parser.add_argument('coco_path', help='coco stuff path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=16, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - coco_path = args.coco_path - nproc = args.nproc - - out_dir = args.out_dir or coco_path - out_img_dir = osp.join(out_dir, 'images') - out_mask_dir = osp.join(out_dir, 'annotations') - - mmcv.mkdir_or_exist(osp.join(out_img_dir, 'train2014')) - mmcv.mkdir_or_exist(osp.join(out_img_dir, 'test2014')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'train2014')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'test2014')) - - train_list, test_list = generate_coco_list(coco_path) - assert (len(train_list) + - len(test_list)) == COCO_LEN, 'Wrong length of list {} & {}'.format( - len(train_list), len(test_list)) - - if args.nproc > 1: - mmcv.track_parallel_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=True), - train_list, - nproc=nproc) - mmcv.track_parallel_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=False), - test_list, - nproc=nproc) - else: - mmcv.track_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=True), train_list) - mmcv.track_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=False), test_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/coco_stuff164k.py b/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/coco_stuff164k.py deleted file mode 100644 index 6d8e2f2a3..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/coco_stuff164k.py +++ /dev/null @@ -1,264 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import shutil -from functools import partial -from glob import glob - -import mmcv -import numpy as np -from PIL import Image - -COCO_LEN = 123287 - -clsID_to_trID = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 12: 11, - 13: 12, - 14: 13, - 15: 14, - 16: 15, - 17: 16, - 18: 17, - 19: 18, - 20: 19, - 21: 20, - 22: 21, - 23: 22, - 24: 23, - 26: 24, - 27: 25, - 30: 26, - 31: 27, - 32: 28, - 33: 29, - 34: 30, - 35: 31, - 36: 32, - 37: 33, - 38: 34, - 39: 35, - 40: 36, - 41: 37, - 42: 38, - 43: 39, - 45: 40, - 46: 41, - 47: 42, - 48: 43, - 49: 44, - 50: 45, - 51: 46, - 52: 47, - 53: 48, - 54: 49, - 55: 50, - 56: 51, - 57: 52, - 58: 53, - 59: 54, - 60: 55, - 61: 56, - 62: 57, - 63: 58, - 64: 59, - 66: 60, - 69: 61, - 71: 62, - 72: 63, - 73: 64, - 74: 65, - 75: 66, - 76: 67, - 77: 68, - 78: 69, - 79: 70, - 80: 71, - 81: 72, - 83: 73, - 84: 74, - 85: 75, - 86: 76, - 87: 77, - 88: 78, - 89: 79, - 91: 80, - 92: 81, - 93: 82, - 94: 83, - 95: 84, - 96: 85, - 97: 86, - 98: 87, - 99: 88, - 100: 89, - 101: 90, - 102: 91, - 103: 92, - 104: 93, - 105: 94, - 106: 95, - 107: 96, - 108: 97, - 109: 98, - 110: 99, - 111: 100, - 112: 101, - 113: 102, - 114: 103, - 115: 104, - 116: 105, - 117: 106, - 118: 107, - 119: 108, - 120: 109, - 121: 110, - 122: 111, - 123: 112, - 124: 113, - 125: 114, - 126: 115, - 127: 116, - 128: 117, - 129: 118, - 130: 119, - 131: 120, - 132: 121, - 133: 122, - 134: 123, - 135: 124, - 136: 125, - 137: 126, - 138: 127, - 139: 128, - 140: 129, - 141: 130, - 142: 131, - 143: 132, - 144: 133, - 145: 134, - 146: 135, - 147: 136, - 148: 137, - 149: 138, - 150: 139, - 151: 140, - 152: 141, - 153: 142, - 154: 143, - 155: 144, - 156: 145, - 157: 146, - 158: 147, - 159: 148, - 160: 149, - 161: 150, - 162: 151, - 163: 152, - 164: 153, - 165: 154, - 166: 155, - 167: 156, - 168: 157, - 169: 158, - 170: 159, - 171: 160, - 172: 161, - 173: 162, - 174: 163, - 175: 164, - 176: 165, - 177: 166, - 178: 167, - 179: 168, - 180: 169, - 181: 170, - 255: 255 -} - - -def convert_to_trainID(maskpath, out_mask_dir, is_train): - mask = np.array(Image.open(maskpath)) - mask_copy = mask.copy() - for clsID, trID in clsID_to_trID.items(): - mask_copy[mask == clsID] = trID - seg_filename = osp.join( - out_mask_dir, 'train2017', - osp.basename(maskpath).split('.')[0] + - '_labelTrainIds.png') if is_train else osp.join( - out_mask_dir, 'val2017', - osp.basename(maskpath).split('.')[0] + '_labelTrainIds.png') - Image.fromarray(mask_copy).save(seg_filename, 'PNG') - - -def parse_args(): - parser = argparse.ArgumentParser( - description=\ - 'Convert COCO Stuff 164k annotations to mmsegmentation format') # noqa - parser.add_argument('coco_path', help='coco stuff path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=16, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - coco_path = args.coco_path - nproc = args.nproc - - out_dir = args.out_dir or coco_path - out_img_dir = osp.join(out_dir, 'images') - out_mask_dir = osp.join(out_dir, 'annotations') - - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'train2017')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'val2017')) - - if out_dir != coco_path: - shutil.copytree(osp.join(coco_path, 'images'), out_img_dir) - - train_list = glob(osp.join(coco_path, 'annotations', 'train2017', '*.png')) - train_list = [file for file in train_list if '_labelTrainIds' not in file] - test_list = glob(osp.join(coco_path, 'annotations', 'val2017', '*.png')) - test_list = [file for file in test_list if '_labelTrainIds' not in file] - assert (len(train_list) + - len(test_list)) == COCO_LEN, 'Wrong length of list {} & {}'.format( - len(train_list), len(test_list)) - - if args.nproc > 1: - mmcv.track_parallel_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=True), - train_list, - nproc=nproc) - mmcv.track_parallel_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=False), - test_list, - nproc=nproc) - else: - mmcv.track_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=True), - train_list) - mmcv.track_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=False), - test_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/pascal_context.py b/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/pascal_context.py deleted file mode 100644 index 03b79d518..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/pascal_context.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -from functools import partial - -import mmcv -import numpy as np -from detail import Detail -from PIL import Image - -_mapping = np.sort( - np.array([ - 0, 2, 259, 260, 415, 324, 9, 258, 144, 18, 19, 22, 23, 397, 25, 284, - 158, 159, 416, 33, 162, 420, 454, 295, 296, 427, 44, 45, 46, 308, 59, - 440, 445, 31, 232, 65, 354, 424, 68, 326, 72, 458, 34, 207, 80, 355, - 85, 347, 220, 349, 360, 98, 187, 104, 105, 366, 189, 368, 113, 115 - ])) -_key = np.array(range(len(_mapping))).astype('uint8') - - -def generate_labels(img_id, detail, out_dir): - - def _class_to_index(mask, _mapping, _key): - # assert the values - values = np.unique(mask) - for i in range(len(values)): - assert (values[i] in _mapping) - index = np.digitize(mask.ravel(), _mapping, right=True) - return _key[index].reshape(mask.shape) - - mask = Image.fromarray( - _class_to_index(detail.getMask(img_id), _mapping=_mapping, _key=_key)) - filename = img_id['file_name'] - mask.save(osp.join(out_dir, filename.replace('jpg', 'png'))) - return osp.splitext(osp.basename(filename))[0] - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert PASCAL VOC annotations to mmsegmentation format') - parser.add_argument('devkit_path', help='pascal voc devkit path') - parser.add_argument('json_path', help='annoation json filepath') - parser.add_argument('-o', '--out_dir', help='output path') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - devkit_path = args.devkit_path - if args.out_dir is None: - out_dir = osp.join(devkit_path, 'VOC2010', 'SegmentationClassContext') - else: - out_dir = args.out_dir - json_path = args.json_path - mmcv.mkdir_or_exist(out_dir) - img_dir = osp.join(devkit_path, 'VOC2010', 'JPEGImages') - - train_detail = Detail(json_path, img_dir, 'train') - train_ids = train_detail.getImgs() - - val_detail = Detail(json_path, img_dir, 'val') - val_ids = val_detail.getImgs() - - mmcv.mkdir_or_exist( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext')) - - train_list = mmcv.track_progress( - partial(generate_labels, detail=train_detail, out_dir=out_dir), - train_ids) - with open( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext', - 'train.txt'), 'w') as f: - f.writelines(line + '\n' for line in sorted(train_list)) - - val_list = mmcv.track_progress( - partial(generate_labels, detail=val_detail, out_dir=out_dir), val_ids) - with open( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext', - 'val.txt'), 'w') as f: - f.writelines(line + '\n' for line in sorted(val_list)) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/voc_aug.py b/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/voc_aug.py deleted file mode 100644 index 1d42c2704..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/tools/convert_datasets/voc_aug.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -from functools import partial - -import mmcv -import numpy as np -from PIL import Image -from scipy.io import loadmat - -AUG_LEN = 10582 - - -def convert_mat(mat_file, in_dir, out_dir): - data = loadmat(osp.join(in_dir, mat_file)) - mask = data['GTcls'][0]['Segmentation'][0].astype(np.uint8) - seg_filename = osp.join(out_dir, mat_file.replace('.mat', '.png')) - Image.fromarray(mask).save(seg_filename, 'PNG') - - -def generate_aug_list(merged_list, excluded_list): - return list(set(merged_list) - set(excluded_list)) - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert PASCAL VOC annotations to mmsegmentation format') - parser.add_argument('devkit_path', help='pascal voc devkit path') - parser.add_argument('aug_path', help='pascal voc aug path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=1, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - devkit_path = args.devkit_path - aug_path = args.aug_path - nproc = args.nproc - if args.out_dir is None: - out_dir = osp.join(devkit_path, 'VOC2012', 'SegmentationClassAug') - else: - out_dir = args.out_dir - mmcv.mkdir_or_exist(out_dir) - in_dir = osp.join(aug_path, 'dataset', 'cls') - - mmcv.track_parallel_progress( - partial(convert_mat, in_dir=in_dir, out_dir=out_dir), - list(mmcv.scandir(in_dir, suffix='.mat')), - nproc=nproc) - - full_aug_list = [] - with open(osp.join(aug_path, 'dataset', 'train.txt')) as f: - full_aug_list += [line.strip() for line in f] - with open(osp.join(aug_path, 'dataset', 'val.txt')) as f: - full_aug_list += [line.strip() for line in f] - - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'train.txt')) as f: - ori_train_list = [line.strip() for line in f] - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'val.txt')) as f: - val_list = [line.strip() for line in f] - - aug_train_list = generate_aug_list(ori_train_list + full_aug_list, - val_list) - assert len(aug_train_list) == AUG_LEN, 'len(aug_train_list) != {}'.format( - AUG_LEN) - - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'trainaug.txt'), 'w') as f: - f.writelines(line + '\n' for line in aug_train_list) - - aug_list = generate_aug_list(full_aug_list, ori_train_list + val_list) - assert len(aug_list) == AUG_LEN - len( - ori_train_list), 'len(aug_list) != {}'.format(AUG_LEN - - len(ori_train_list)) - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', 'aug.txt'), - 'w') as f: - f.writelines(line + '\n' for line in aug_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/apcnet/pytorch/tools/get_flops.py b/cv/semantic_segmentation/apcnet/pytorch/tools/get_flops.py deleted file mode 100644 index e30c36fdf..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/tools/get_flops.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse - -from mmcv import Config -from mmcv.cnn import get_model_complexity_info - -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Get the FLOPs of a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument( - '--shape', - type=int, - nargs='+', - default=[2048, 1024], - help='input image size') - args = parser.parse_args() - return args - - -def main(): - - args = parse_args() - - if len(args.shape) == 1: - input_shape = (3, args.shape[0], args.shape[0]) - elif len(args.shape) == 2: - input_shape = (3, ) + tuple(args.shape) - else: - raise ValueError('invalid input shape') - - cfg = Config.fromfile(args.config) - cfg.model.pretrained = None - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')).cuda() - model.eval() - - if hasattr(model, 'forward_dummy'): - model.forward = model.forward_dummy - else: - raise NotImplementedError( - 'FLOPs counter is currently not currently supported with {}'. - format(model.__class__.__name__)) - - flops, params = get_model_complexity_info(model, input_shape) - split_line = '=' * 30 - print('{0}\nInput shape: {1}\nFlops: {2}\nParams: {3}\n{0}'.format( - split_line, input_shape, flops, params)) - print('!!!Please be cautious if you use the results in papers. ' - 'You may need to check if all ops are supported and verify that the ' - 'flops computation is correct.') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/apcnet/pytorch/tools/print_config.py b/cv/semantic_segmentation/apcnet/pytorch/tools/print_config.py deleted file mode 100644 index 3f9c08dd9..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/tools/print_config.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import warnings - -from mmcv import Config, DictAction - -from mmseg.apis import init_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='Print the whole config') - parser.add_argument('config', help='config file path') - parser.add_argument( - '--graph', action='store_true', help='print the models graph') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - args = parser.parse_args() - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options, ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - print(f'Config:\n{cfg.pretty_text}') - # dump config - cfg.dump('example.py') - # dump models graph - if args.graph: - model = init_segmentor(args.config, device='cpu') - print(f'Model graph:\n{str(model)}') - with open('example-graph.txt', 'w') as f: - f.writelines(str(model)) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/apcnet/pytorch/tools/slurm_test.sh b/cv/semantic_segmentation/apcnet/pytorch/tools/slurm_test.sh deleted file mode 100755 index 4e6f7bf4e..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/tools/slurm_test.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -set -x - -PARTITION=$1 -JOB_NAME=$2 -CONFIG=$3 -CHECKPOINT=$4 -GPUS=${GPUS:-4} -GPUS_PER_NODE=${GPUS_PER_NODE:-4} -CPUS_PER_TASK=${CPUS_PER_TASK:-5} -PY_ARGS=${@:5} -SRUN_ARGS=${SRUN_ARGS:-""} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -srun -p ${PARTITION} \ - --job-name=${JOB_NAME} \ - --gres=gpu:${GPUS_PER_NODE} \ - --ntasks=${GPUS} \ - --ntasks-per-node=${GPUS_PER_NODE} \ - --cpus-per-task=${CPUS_PER_TASK} \ - --kill-on-bad-exit=1 \ - ${SRUN_ARGS} \ - python -u tools/test.py ${CONFIG} ${CHECKPOINT} --launcher="slurm" ${PY_ARGS} diff --git a/cv/semantic_segmentation/apcnet/pytorch/tools/slurm_train.sh b/cv/semantic_segmentation/apcnet/pytorch/tools/slurm_train.sh deleted file mode 100755 index ab232105f..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/tools/slurm_train.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -set -x - -PARTITION=$1 -JOB_NAME=$2 -CONFIG=$3 -GPUS=${GPUS:-4} -GPUS_PER_NODE=${GPUS_PER_NODE:-4} -CPUS_PER_TASK=${CPUS_PER_TASK:-5} -SRUN_ARGS=${SRUN_ARGS:-""} -PY_ARGS=${@:4} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -srun -p ${PARTITION} \ - --job-name=${JOB_NAME} \ - --gres=gpu:${GPUS_PER_NODE} \ - --ntasks=${GPUS} \ - --ntasks-per-node=${GPUS_PER_NODE} \ - --cpus-per-task=${CPUS_PER_TASK} \ - --kill-on-bad-exit=1 \ - ${SRUN_ARGS} \ - python -u tools/train.py ${CONFIG} --launcher="slurm" ${PY_ARGS} diff --git a/cv/semantic_segmentation/apcnet/pytorch/tools/test.py b/cv/semantic_segmentation/apcnet/pytorch/tools/test.py deleted file mode 100644 index 12892ec9b..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/tools/test.py +++ /dev/null @@ -1,319 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os -import os.path as osp -import shutil -import time -import warnings - -import mmcv -import torch -from mmcv.cnn.utils import revert_sync_batchnorm -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (get_dist_info, init_dist, load_checkpoint, - wrap_fp16_model) -from mmcv.utils import DictAction - -from mmseg import digit_version -from mmseg.apis import multi_gpu_test, single_gpu_test -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor -from mmseg.utils import setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser( - description='mmseg test (and eval) a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--work-dir', - help=('if specified, the evaluation metric results will be dumped' - 'into the directory as json')) - parser.add_argument( - '--aug-test', action='store_true', help='Use Flip and Multi scale aug') - parser.add_argument('--out', help='output result file in pickle format') - parser.add_argument( - '--format-only', - action='store_true', - help='Format the output results without perform evaluation. It is' - 'useful when you want to format the result to a specific format and ' - 'submit it to the test server') - parser.add_argument( - '--eval', - type=str, - nargs='+', - help='evaluation metrics, which depends on the dataset, e.g., "mIoU"' - ' for generic datasets, and "cityscapes" for Cityscapes') - parser.add_argument('--show', action='store_true', help='show results') - parser.add_argument( - '--show-dir', help='directory where painted images will be saved') - parser.add_argument( - '--gpu-collect', - action='store_true', - help='whether to use gpu to collect results.') - parser.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed testing)') - parser.add_argument( - '--tmpdir', - help='tmp directory used for collecting results from multiple ' - 'workers, available when gpu_collect is not specified') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--eval-options', - nargs='+', - action=DictAction, - help='custom options for evaluation') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument( - '--opacity', - type=float, - default=0.5, - help='Opacity of painted segmentation map. In (0, 1] range.') - parser.add_argument('--local_rank', type=int, default=0) - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - assert args.out or args.eval or args.format_only or args.show \ - or args.show_dir, \ - ('Please specify at least one operation (save/eval/format/show the ' - 'results / save the results) with the argument "--out", "--eval"' - ', "--format-only", "--show" or "--show-dir"') - - if args.eval and args.format_only: - raise ValueError('--eval and --format_only cannot be both specified') - - if args.out is not None and not args.out.endswith(('.pkl', '.pickle')): - raise ValueError('The output file must be a pkl file.') - - cfg = mmcv.Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - if args.aug_test: - # hard code index - cfg.data.test.pipeline[1].img_ratios = [ - 0.5, 0.75, 1.0, 1.25, 1.5, 1.75 - ] - cfg.data.test.pipeline[1].flip = True - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - if args.gpu_id is not None: - cfg.gpu_ids = [args.gpu_id] - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - cfg.gpu_ids = [args.gpu_id] - distributed = False - if len(cfg.gpu_ids) > 1: - warnings.warn(f'The gpu-ids is reset from {cfg.gpu_ids} to ' - f'{cfg.gpu_ids[0:1]} to avoid potential error in ' - 'non-distribute testing time.') - cfg.gpu_ids = cfg.gpu_ids[0:1] - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - - rank, _ = get_dist_info() - # allows not to create - if args.work_dir is not None and rank == 0: - mmcv.mkdir_or_exist(osp.abspath(args.work_dir)) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.aug_test: - json_file = osp.join(args.work_dir, - f'eval_multi_scale_{timestamp}.json') - else: - json_file = osp.join(args.work_dir, - f'eval_single_scale_{timestamp}.json') - elif rank == 0: - work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - mmcv.mkdir_or_exist(osp.abspath(work_dir)) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.aug_test: - json_file = osp.join(work_dir, - f'eval_multi_scale_{timestamp}.json') - else: - json_file = osp.join(work_dir, - f'eval_single_scale_{timestamp}.json') - - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - # The default loader config - loader_cfg = dict( - # cfg.gpus will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - shuffle=False) - # The overall dataloader settings - loader_cfg.update({ - k: v - for k, v in cfg.data.items() if k not in [ - 'train', 'val', 'test', 'train_dataloader', 'val_dataloader', - 'test_dataloader' - ] - }) - test_loader_cfg = { - **loader_cfg, - 'samples_per_gpu': 1, - 'shuffle': False, # Not shuffle by default - **cfg.data.get('test_dataloader', {}) - } - # build the dataloader - data_loader = build_dataloader(dataset, **test_loader_cfg) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - checkpoint = load_checkpoint(model, args.checkpoint, map_location='cpu') - if 'CLASSES' in checkpoint.get('meta', {}): - model.CLASSES = checkpoint['meta']['CLASSES'] - else: - print('"CLASSES" not found in meta, use dataset.CLASSES instead') - model.CLASSES = dataset.CLASSES - if 'PALETTE' in checkpoint.get('meta', {}): - model.PALETTE = checkpoint['meta']['PALETTE'] - else: - print('"PALETTE" not found in meta, use dataset.PALETTE instead') - model.PALETTE = dataset.PALETTE - - # clean gpu memory when starting a new evaluation. - torch.cuda.empty_cache() - eval_kwargs = {} if args.eval_options is None else args.eval_options - - # Deprecated - efficient_test = eval_kwargs.get('efficient_test', False) - if efficient_test: - warnings.warn( - '``efficient_test=True`` does not have effect in tools/test.py, ' - 'the evaluation and format results are CPU memory efficient by ' - 'default') - - eval_on_format_results = ( - args.eval is not None and 'cityscapes' in args.eval) - if eval_on_format_results: - assert len(args.eval) == 1, 'eval on format results is not ' \ - 'applicable for metrics other than ' \ - 'cityscapes' - if args.format_only or eval_on_format_results: - if 'imgfile_prefix' in eval_kwargs: - tmpdir = eval_kwargs['imgfile_prefix'] - else: - tmpdir = '.format_cityscapes' - eval_kwargs.setdefault('imgfile_prefix', tmpdir) - mmcv.mkdir_or_exist(tmpdir) - else: - tmpdir = None - - if not distributed: - warnings.warn( - 'SyncBN is only supported with DDP. To be compatible with DP, ' - 'we convert SyncBN to BN. Please use dist_train.sh which can ' - 'avoid this error.') - if not torch.cuda.is_available(): - assert digit_version(mmcv.__version__) >= digit_version('1.4.4'), \ - 'Please use MMCV >= 1.4.4 for CPU training!' - model = revert_sync_batchnorm(model) - model = MMDataParallel(model, device_ids=cfg.gpu_ids) - results = single_gpu_test( - model, - data_loader, - args.show, - args.show_dir, - False, - args.opacity, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - else: - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False) - results = multi_gpu_test( - model, - data_loader, - args.tmpdir, - args.gpu_collect, - False, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - - rank, _ = get_dist_info() - if rank == 0: - if args.out: - warnings.warn( - 'The behavior of ``args.out`` has been changed since MMSeg ' - 'v0.16, the pickled outputs could be seg map as type of ' - 'np.array, pre-eval results or file paths for ' - '``dataset.format_results()``.') - print(f'\nwriting results to {args.out}') - mmcv.dump(results, args.out) - if args.eval: - eval_kwargs.update(metric=args.eval) - metric = dataset.evaluate(results, **eval_kwargs) - metric_dict = dict(config=args.config, metric=metric) - mmcv.dump(metric_dict, json_file, indent=4) - if tmpdir is not None and eval_on_format_results: - # remove tmp dir when cityscapes evaluation - shutil.rmtree(tmpdir) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/apcnet/pytorch/train.py b/cv/semantic_segmentation/apcnet/pytorch/train.py deleted file mode 100644 index e198dd60f..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/train.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import copy -import os -import os.path as osp -import time -import warnings - -import mmcv -import torch -import torch.distributed as dist -from mmcv.cnn.utils import revert_sync_batchnorm -from mmcv.runner import get_dist_info, init_dist -from mmcv.utils import Config, DictAction, get_git_hash - -from mmseg import __version__ -from mmseg.apis import init_random_seed, set_random_seed, train_segmentor -from mmseg.datasets import build_dataset -from mmseg.models import build_segmentor -from mmseg.utils import collect_env, get_root_logger, setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument('--work-dir', help='the dir to save logs and models') - parser.add_argument( - '--load-from', help='the checkpoint file to load weights from') - parser.add_argument( - '--resume-from', help='the checkpoint file to resume from') - parser.add_argument( - '--no-validate', - action='store_true', - help='whether not to evaluate the checkpoint during training') - group_gpus = parser.add_mutually_exclusive_group() - group_gpus.add_argument( - '--gpus', - type=int, - help='(Deprecated, please use --gpu-id) number of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-ids', - type=int, - nargs='+', - help='(Deprecated, please use --gpu-id) ids of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed training)') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--diff_seed', - action='store_true', - help='Whether or not set different seeds for different ranks') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - parser.add_argument('--dist_backend', type=str, default=None) - parser.add_argument( - '--auto-resume', - action='store_true', - help='resume from the latest checkpoint automatically.') - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - - # work_dir is determined in this priority: CLI > segment in file > filename - if args.work_dir is not None: - # update configs according to CLI args if args.work_dir is not None - cfg.work_dir = args.work_dir - elif cfg.get('work_dir', None) is None: - # use config filename as default work_dir if cfg.work_dir is None - cfg.work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - if args.load_from is not None: - cfg.load_from = args.load_from - if args.resume_from is not None: - cfg.resume_from = args.resume_from - if args.gpus is not None: - cfg.gpu_ids = range(1) - warnings.warn('`--gpus` is deprecated because we only support ' - 'single GPU mode in non-distributed training. ' - 'Use `gpus=1` now.') - if args.gpu_ids is not None: - cfg.gpu_ids = args.gpu_ids[0:1] - warnings.warn('`--gpu-ids` is deprecated, please use `--gpu-id`. ' - 'Because we only support single GPU mode in ' - 'non-distributed training. Use the first GPU ' - 'in `gpu_ids` now.') - if args.gpus is None and args.gpu_ids is None: - cfg.gpu_ids = [args.gpu_id] - - cfg.auto_resume = args.auto_resume - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - if args.dist_backend is not None: - cfg.dist_params.backend = args.dist_backend - init_dist(args.launcher, **cfg.dist_params) - # gpu_ids is used to calculate iter when resuming checkpoint - _, world_size = get_dist_info() - cfg.gpu_ids = range(world_size) - - # create work_dir - mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) - # dump config - cfg.dump(osp.join(cfg.work_dir, osp.basename(args.config))) - # init the logger before other steps - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - log_file = osp.join(cfg.work_dir, f'{timestamp}.log') - logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) - - # set multi-process settings - setup_multi_processes(cfg) - - # init the meta dict to record some important information such as - # environment info and seed, which will be logged - meta = dict() - # log env info - env_info_dict = collect_env() - env_info = '\n'.join([f'{k}: {v}' for k, v in env_info_dict.items()]) - dash_line = '-' * 60 + '\n' - logger.info('Environment info:\n' + dash_line + env_info + '\n' + - dash_line) - meta['env_info'] = env_info - - # log some basic info - logger.info(f'Distributed training: {distributed}') - logger.info(f'Config:\n{cfg.pretty_text}') - - # set random seeds - seed = init_random_seed(args.seed) - seed = seed + dist.get_rank() if args.diff_seed else seed - logger.info(f'Set random seed to {seed}, ' - f'deterministic: {args.deterministic}') - set_random_seed(seed, deterministic=args.deterministic) - cfg.seed = seed - meta['seed'] = seed - meta['exp_name'] = osp.basename(args.config) - - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')) - model.init_weights() - - # SyncBN is not support for DP - if not distributed: - warnings.warn( - 'SyncBN is only supported with DDP. To be compatible with DP, ' - 'we convert SyncBN to BN. Please use dist_train.sh which can ' - 'avoid this error.') - model = revert_sync_batchnorm(model) - - logger.info(model) - - datasets = [build_dataset(cfg.data.train)] - if len(cfg.workflow) == 2: - val_dataset = copy.deepcopy(cfg.data.val) - val_dataset.pipeline = cfg.data.train.pipeline - datasets.append(build_dataset(val_dataset)) - if cfg.checkpoint_config is not None: - # save mmseg version, config file content and class names in - # checkpoints as meta data - cfg.checkpoint_config.meta = dict( - mmseg_version=f'{__version__}+{get_git_hash()[:7]}', - config=cfg.pretty_text, - CLASSES=datasets[0].CLASSES, - PALETTE=datasets[0].PALETTE) - # add an attribute for visualization convenience - model.CLASSES = datasets[0].CLASSES - # passing checkpoint meta for saving best checkpoint - meta.update(cfg.checkpoint_config.meta) - train_segmentor( - model, - datasets, - cfg, - distributed=distributed, - validate=(not args.no_validate), - timestamp=timestamp, - meta=meta) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/apcnet/pytorch/train.sh b/cv/semantic_segmentation/apcnet/pytorch/train.sh deleted file mode 100755 index 88edd482c..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/train.sh +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG=$1 - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 $(dirname "$0")/train.py \ - $CONFIG \ - --launcher pytorch ${@:2} diff --git a/cv/semantic_segmentation/apcnet/pytorch/train_dist.sh b/cv/semantic_segmentation/apcnet/pytorch/train_dist.sh deleted file mode 100755 index b2b398a23..000000000 --- a/cv/semantic_segmentation/apcnet/pytorch/train_dist.sh +++ /dev/null @@ -1,17 +0,0 @@ -CONFIG=$1 -GPUS=$2 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/train.py \ - $CONFIG \ - --launcher pytorch ${@:3} -- Gitee From e3423dbec7b6515d1d6a3c9a5f29e807eb559002 Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Wed, 5 Mar 2025 17:16:02 +0800 Subject: [PATCH 02/15] update unet++ --- .../unet++/pytorch/.gitignore | 120 -- .../unet++/pytorch/CITATION.cff | 8 - .../unet++/pytorch/LICENSE | 203 --- .../unet++/pytorch/README.md | 104 +- .../pytorch/configs/_base_/datasets/drive.py | 59 - .../pytorch/configs/_base_/default_runtime.py | 14 - .../configs/_base_/models/unet++_r34.py | 57 - .../configs/_base_/schedules/schedule_160k.py | 9 - .../configs/_base_/schedules/schedule_1k.py | 9 - .../configs/_base_/schedules/schedule_20k.py | 9 - .../configs/_base_/schedules/schedule_320k.py | 9 - .../configs/_base_/schedules/schedule_40k.py | 9 - .../configs/_base_/schedules/schedule_80k.py | 9 - .../configs/unet++/unet++_r34_40k_drive.py | 9 - .../unet++/pytorch/docker/Dockerfile | 32 - .../unet++/pytorch/docker/serve/Dockerfile | 49 - .../pytorch/docker/serve/config.properties | 5 - .../unet++/pytorch/docker/serve/entrypoint.sh | 12 - .../unet++/pytorch/mmcv/__init__.py | 13 - .../unet++/pytorch/mmcv/cnn/__init__.py | 21 - .../pytorch/mmcv/cnn/bricks/__init__.py | 35 - .../pytorch/mmcv/cnn/bricks/activation.py | 93 -- .../pytorch/mmcv/cnn/bricks/context_block.py | 125 -- .../unet++/pytorch/mmcv/cnn/bricks/conv.py | 44 - .../cnn/bricks/conv2d_adaptive_padding.py | 62 - .../pytorch/mmcv/cnn/bricks/conv_module.py | 206 --- .../unet++/pytorch/mmcv/cnn/bricks/conv_ws.py | 148 -- .../bricks/depthwise_separable_conv_module.py | 96 -- .../unet++/pytorch/mmcv/cnn/bricks/drop.py | 65 - .../mmcv/cnn/bricks/generalized_attention.py | 412 ----- .../pytorch/mmcv/cnn/bricks/hsigmoid.py | 46 - .../unet++/pytorch/mmcv/cnn/bricks/hswish.py | 38 - .../pytorch/mmcv/cnn/bricks/non_local.py | 306 ---- .../unet++/pytorch/mmcv/cnn/bricks/norm.py | 144 -- .../unet++/pytorch/mmcv/cnn/bricks/padding.py | 36 - .../unet++/pytorch/mmcv/cnn/bricks/plugin.py | 89 -- .../pytorch/mmcv/cnn/bricks/registry.py | 16 - .../unet++/pytorch/mmcv/cnn/bricks/scale.py | 21 - .../unet++/pytorch/mmcv/cnn/bricks/swish.py | 25 - .../pytorch/mmcv/cnn/bricks/transformer.py | 944 ----------- .../pytorch/mmcv/cnn/bricks/upsample.py | 84 - .../pytorch/mmcv/cnn/bricks/wrappers.py | 180 --- .../unet++/pytorch/mmcv/cnn/builder.py | 30 - .../unet++/pytorch/mmcv/cnn/utils/__init__.py | 19 - .../pytorch/mmcv/cnn/utils/flops_counter.py | 599 ------- .../pytorch/mmcv/cnn/utils/fuse_conv_bn.py | 59 - .../unet++/pytorch/mmcv/cnn/utils/sync_bn.py | 60 - .../pytorch/mmcv/cnn/utils/weight_init.py | 685 -------- .../unet++/pytorch/mmcv/device/__init__.py | 4 - .../pytorch/mmcv/device/ipu/__init__.py | 14 - .../pytorch/mmcv/device/ipu/dataloader.py | 157 -- .../device/ipu/hierarchical_data_manager.py | 243 --- .../pytorch/mmcv/device/ipu/hook_wrapper.py | 105 -- .../pytorch/mmcv/device/ipu/model_wrapper.py | 721 --------- .../unet++/pytorch/mmcv/device/ipu/runner.py | 142 -- .../unet++/pytorch/mmcv/device/ipu/utils.py | 244 --- .../pytorch/mmcv/device/mlu/__init__.py | 9 - .../pytorch/mmcv/device/mlu/_functions.py | 22 - .../pytorch/mmcv/device/mlu/data_parallel.py | 41 - .../pytorch/mmcv/device/mlu/distributed.py | 20 - .../pytorch/mmcv/device/mlu/scatter_gather.py | 59 - .../unet++/pytorch/mmcv/engine/__init__.py | 8 - .../unet++/pytorch/mmcv/engine/test.py | 202 --- .../unet++/pytorch/mmcv/fileio/__init__.py | 11 - .../unet++/pytorch/mmcv/fileio/file_client.py | 1163 -------------- .../pytorch/mmcv/fileio/handlers/__init__.py | 7 - .../pytorch/mmcv/fileio/handlers/base.py | 30 - .../mmcv/fileio/handlers/json_handler.py | 36 - .../mmcv/fileio/handlers/pickle_handler.py | 28 - .../mmcv/fileio/handlers/yaml_handler.py | 25 - .../unet++/pytorch/mmcv/fileio/io.py | 151 -- .../unet++/pytorch/mmcv/fileio/parse.py | 97 -- .../unet++/pytorch/mmcv/image/__init__.py | 28 - .../unet++/pytorch/mmcv/image/colorspace.py | 306 ---- .../unet++/pytorch/mmcv/image/geometric.py | 741 --------- .../unet++/pytorch/mmcv/image/io.py | 314 ---- .../unet++/pytorch/mmcv/image/misc.py | 53 - .../unet++/pytorch/mmcv/image/photometric.py | 428 ----- .../pytorch/mmcv/model_zoo/deprecated.json | 6 - .../unet++/pytorch/mmcv/model_zoo/mmcls.json | 59 - .../pytorch/mmcv/model_zoo/open_mmlab.json | 50 - .../mmcv/model_zoo/torchvision_0.12.json | 57 - .../unet++/pytorch/mmcv/ops/__init__.py | 12 - .../unet++/pytorch/mmcv/ops/cc_attention.py | 84 - .../unet++/pytorch/mmcv/ops/csrc/README.md | 170 -- .../csrc/common/cuda/common_cuda_helper.hpp | 120 -- .../csrc/common/cuda/psamask_cuda_kernel.cuh | 141 -- .../cuda/sigmoid_focal_loss_cuda_kernel.cuh | 71 - .../cuda/softmax_focal_loss_cuda_kernel.cuh | 72 - .../csrc/common/cuda/sync_bn_cuda_kernel.cuh | 331 ---- .../ops/csrc/common/pytorch_cpp_helper.hpp | 27 - .../ops/csrc/common/pytorch_cuda_helper.hpp | 19 - .../csrc/common/pytorch_device_registry.hpp | 141 -- .../mmcv/ops/csrc/pytorch/cuda/cudabind.cpp | 210 --- .../ops/csrc/pytorch/cuda/focal_loss_cuda.cu | 111 -- .../ops/csrc/pytorch/cuda/psamask_cuda.cu | 60 - .../ops/csrc/pytorch/cuda/sync_bn_cuda.cu | 110 -- .../mmcv/ops/csrc/pytorch/focal_loss.cpp | 53 - .../pytorch/mmcv/ops/csrc/pytorch/info.cpp | 56 - .../pytorch/mmcv/ops/csrc/pytorch/psamask.cpp | 41 - .../pytorch/mmcv/ops/csrc/pytorch/pybind.cpp | 106 -- .../pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp | 69 - .../pytorch/mmcv/ops/deprecated_wrappers.py | 46 - .../unet++/pytorch/mmcv/ops/focal_loss.py | 213 --- .../unet++/pytorch/mmcv/ops/info.py | 36 - .../unet++/pytorch/mmcv/ops/point_sample.py | 346 ---- .../unet++/pytorch/mmcv/ops/psa_mask.py | 92 -- .../unet++/pytorch/mmcv/ops/sync_bn.py | 279 ---- .../unet++/pytorch/mmcv/parallel/__init__.py | 13 - .../pytorch/mmcv/parallel/_functions.py | 76 - .../unet++/pytorch/mmcv/parallel/collate.py | 84 - .../pytorch/mmcv/parallel/data_container.py | 89 -- .../pytorch/mmcv/parallel/data_parallel.py | 97 -- .../pytorch/mmcv/parallel/distributed.py | 138 -- .../mmcv/parallel/distributed_deprecated.py | 70 - .../unet++/pytorch/mmcv/parallel/registry.py | 8 - .../pytorch/mmcv/parallel/scatter_gather.py | 59 - .../unet++/pytorch/mmcv/parallel/utils.py | 20 - .../unet++/pytorch/mmcv/runner/__init__.py | 73 - .../unet++/pytorch/mmcv/runner/base_module.py | 208 --- .../unet++/pytorch/mmcv/runner/base_runner.py | 544 ------- .../unet++/pytorch/mmcv/runner/builder.py | 24 - .../unet++/pytorch/mmcv/runner/checkpoint.py | 759 --------- .../mmcv/runner/default_constructor.py | 45 - .../unet++/pytorch/mmcv/runner/dist_utils.py | 204 --- .../pytorch/mmcv/runner/epoch_based_runner.py | 188 --- .../unet++/pytorch/mmcv/runner/fp16_utils.py | 423 ----- .../pytorch/mmcv/runner/hooks/__init__.py | 48 - .../pytorch/mmcv/runner/hooks/checkpoint.py | 167 -- .../pytorch/mmcv/runner/hooks/closure.py | 11 - .../unet++/pytorch/mmcv/runner/hooks/ema.py | 89 -- .../pytorch/mmcv/runner/hooks/evaluation.py | 511 ------ .../unet++/pytorch/mmcv/runner/hooks/hook.py | 92 -- .../pytorch/mmcv/runner/hooks/iter_timer.py | 31 - .../mmcv/runner/hooks/logger/__init__.py | 18 - .../pytorch/mmcv/runner/hooks/logger/base.py | 167 -- .../mmcv/runner/hooks/logger/clearml.py | 62 - .../mmcv/runner/hooks/logger/dvclive.py | 68 - .../mmcv/runner/hooks/logger/mlflow.py | 80 - .../mmcv/runner/hooks/logger/neptune.py | 88 -- .../pytorch/mmcv/runner/hooks/logger/pavi.py | 132 -- .../mmcv/runner/hooks/logger/segmind.py | 49 - .../mmcv/runner/hooks/logger/tensorboard.py | 69 - .../pytorch/mmcv/runner/hooks/logger/text.py | 256 --- .../pytorch/mmcv/runner/hooks/logger/wandb.py | 107 -- .../pytorch/mmcv/runner/hooks/lr_updater.py | 730 --------- .../pytorch/mmcv/runner/hooks/memory.py | 25 - .../mmcv/runner/hooks/momentum_updater.py | 566 ------- .../pytorch/mmcv/runner/hooks/optimizer.py | 556 ------- .../pytorch/mmcv/runner/hooks/profiler.py | 180 --- .../pytorch/mmcv/runner/hooks/sampler_seed.py | 20 - .../pytorch/mmcv/runner/hooks/sync_buffer.py | 22 - .../pytorch/mmcv/runner/iter_based_runner.py | 273 ---- .../unet++/pytorch/mmcv/runner/log_buffer.py | 41 - .../pytorch/mmcv/runner/optimizer/__init__.py | 9 - .../pytorch/mmcv/runner/optimizer/builder.py | 44 - .../runner/optimizer/default_constructor.py | 250 --- .../unet++/pytorch/mmcv/runner/priority.py | 60 - .../unet++/pytorch/mmcv/runner/utils.py | 93 -- .../unet++/pytorch/mmcv/utils/__init__.py | 78 - .../unet++/pytorch/mmcv/utils/config.py | 719 --------- .../unet++/pytorch/mmcv/utils/device_type.py | 24 - .../unet++/pytorch/mmcv/utils/env.py | 120 -- .../unet++/pytorch/mmcv/utils/ext_loader.py | 72 - .../unet++/pytorch/mmcv/utils/hub.py | 131 -- .../unet++/pytorch/mmcv/utils/logging.py | 111 -- .../unet++/pytorch/mmcv/utils/misc.py | 377 ----- .../unet++/pytorch/mmcv/utils/parrots_jit.py | 41 - .../pytorch/mmcv/utils/parrots_wrapper.py | 114 -- .../unet++/pytorch/mmcv/utils/path.py | 101 -- .../unet++/pytorch/mmcv/utils/progressbar.py | 208 --- .../unet++/pytorch/mmcv/utils/registry.py | 337 ---- .../unet++/pytorch/mmcv/utils/seed.py | 23 - .../unet++/pytorch/mmcv/utils/testing.py | 141 -- .../unet++/pytorch/mmcv/utils/timer.py | 118 -- .../unet++/pytorch/mmcv/utils/trace.py | 24 - .../pytorch/mmcv/utils/version_utils.py | 90 -- .../unet++/pytorch/mmcv/version.py | 35 - .../unet++/pytorch/mmseg/__init__.py | 62 - .../unet++/pytorch/mmseg/apis/__init__.py | 11 - .../unet++/pytorch/mmseg/apis/inference.py | 136 -- .../unet++/pytorch/mmseg/apis/test.py | 233 --- .../unet++/pytorch/mmseg/apis/train.py | 196 --- .../unet++/pytorch/mmseg/core/__init__.py | 11 - .../unet++/pytorch/mmseg/core/builder.py | 33 - .../pytorch/mmseg/core/evaluation/__init__.py | 11 - .../mmseg/core/evaluation/class_names.py | 316 ---- .../mmseg/core/evaluation/eval_hooks.py | 128 -- .../pytorch/mmseg/core/evaluation/metrics.py | 395 ----- .../pytorch/mmseg/core/optimizers/__init__.py | 7 - .../layer_decay_optimizer_constructor.py | 208 --- .../unet++/pytorch/mmseg/core/seg/__init__.py | 5 - .../unet++/pytorch/mmseg/core/seg/builder.py | 9 - .../mmseg/core/seg/sampler/__init__.py | 5 - .../core/seg/sampler/base_pixel_sampler.py | 13 - .../core/seg/sampler/ohem_pixel_sampler.py | 85 - .../pytorch/mmseg/core/utils/__init__.py | 5 - .../pytorch/mmseg/core/utils/dist_util.py | 46 - .../unet++/pytorch/mmseg/core/utils/misc.py | 18 - .../unet++/pytorch/mmseg/datasets/__init__.py | 6 - .../unet++/pytorch/mmseg/datasets/builder.py | 191 --- .../unet++/pytorch/mmseg/datasets/custom.py | 487 ------ .../mmseg/datasets/dataset_wrappers.py | 276 ---- .../unet++/pytorch/mmseg/datasets/drive.py | 26 - .../mmseg/datasets/pipelines/__init__.py | 19 - .../mmseg/datasets/pipelines/compose.py | 52 - .../mmseg/datasets/pipelines/formating.py | 9 - .../mmseg/datasets/pipelines/formatting.py | 289 ---- .../mmseg/datasets/pipelines/loading.py | 158 -- .../mmseg/datasets/pipelines/test_time_aug.py | 134 -- .../mmseg/datasets/pipelines/transforms.py | 1385 ----------------- .../mmseg/datasets/samplers/__init__.py | 4 - .../datasets/samplers/distributed_sampler.py | 71 - .../unet++/pytorch/mmseg/models/__init__.py | 13 - .../mmseg/models/backbones/__init__.py | 3 - .../pytorch/mmseg/models/backbones/resnet.py | 714 --------- .../unet++/pytorch/mmseg/models/builder.py | 49 - .../mmseg/models/decode_heads/__init__.py | 5 - .../mmseg/models/decode_heads/decode_head.py | 266 ---- .../mmseg/models/decode_heads/fcn_head.py | 96 -- .../models/decode_heads/unetplusplus_head.py | 142 -- .../pytorch/mmseg/models/losses/__init__.py | 15 - .../pytorch/mmseg/models/losses/accuracy.py | 92 -- .../mmseg/models/losses/cross_entropy_loss.py | 296 ---- .../pytorch/mmseg/models/losses/dice_loss.py | 137 -- .../pytorch/mmseg/models/losses/focal_loss.py | 327 ---- .../mmseg/models/losses/lovasz_loss.py | 323 ---- .../pytorch/mmseg/models/losses/utils.py | 126 -- .../pytorch/mmseg/models/necks/__init__.py | 11 - .../mmseg/models/necks/featurepyramid.py | 67 - .../unet++/pytorch/mmseg/models/necks/fpn.py | 213 --- .../pytorch/mmseg/models/necks/ic_neck.py | 148 -- .../unet++/pytorch/mmseg/models/necks/jpu.py | 131 -- .../pytorch/mmseg/models/necks/mla_neck.py | 118 -- .../mmseg/models/necks/multilevel_neck.py | 78 - .../mmseg/models/segmentors/__init__.py | 6 - .../pytorch/mmseg/models/segmentors/base.py | 291 ---- .../segmentors/cascade_encoder_decoder.py | 88 -- .../models/segmentors/encoder_decoder.py | 284 ---- .../pytorch/mmseg/models/utils/__init__.py | 18 - .../pytorch/mmseg/models/utils/embed.py | 330 ---- .../mmseg/models/utils/inverted_residual.py | 213 --- .../mmseg/models/utils/make_divisible.py | 28 - .../pytorch/mmseg/models/utils/res_layer.py | 96 -- .../pytorch/mmseg/models/utils/se_layer.py | 58 - .../models/utils/self_attention_block.py | 160 -- .../mmseg/models/utils/shape_convert.py | 107 -- .../mmseg/models/utils/up_conv_block.py | 102 -- .../pytorch/mmseg/models/utils/wrappers.py | 51 - .../unet++/pytorch/mmseg/ops/__init__.py | 5 - .../unet++/pytorch/mmseg/ops/encoding.py | 75 - .../unet++/pytorch/mmseg/ops/wrappers.py | 51 - .../unet++/pytorch/mmseg/registry/__init__.py | 15 - .../unet++/pytorch/mmseg/registry/registry.py | 116 -- .../unet++/pytorch/mmseg/utils/__init__.py | 10 - .../unet++/pytorch/mmseg/utils/collect_env.py | 18 - .../unet++/pytorch/mmseg/utils/logger.py | 28 - .../unet++/pytorch/mmseg/utils/misc.py | 41 - .../unet++/pytorch/mmseg/utils/set_env.py | 55 - .../unet++/pytorch/mmseg/version.py | 18 - .../unet++/pytorch/requirements.txt | 4 - .../unet++/pytorch/requirements/apcnet.txt | 4 - .../pytorch/requirements/mmcv/build.txt | 1 - .../unet++/pytorch/requirements/mmcv/docs.txt | 8 - .../pytorch/requirements/mmcv/optional.txt | 1 - .../requirements/mmcv/requirements.txt | 4 - .../pytorch/requirements/mmcv/runtime.txt | 7 - .../unet++/pytorch/requirements/mmcv/test.txt | 9 - .../unet++/pytorch/requirements/mminstall.txt | 2 - .../unet++/pytorch/requirements/optional.txt | 1 - .../unet++/pytorch/requirements/runtime.txt | 5 - .../unet++/pytorch/setup.py | 258 --- .../unet++/pytorch/tools/analyze_logs.py | 128 -- .../unet++/pytorch/tools/benchmark.py | 120 -- .../unet++/pytorch/tools/confusion_matrix.py | 184 --- .../pytorch/tools/convert_datasets/drive.py | 113 -- .../unet++/pytorch/tools/get_flops.py | 60 - .../unet++/pytorch/tools/print_config.py | 69 - .../unet++/pytorch/tools/slurm_test.sh | 24 - .../unet++/pytorch/tools/slurm_train.sh | 23 - .../unet++/pytorch/tools/test.py | 319 ---- .../unet++/pytorch/train.py | 243 --- .../unet++/pytorch/train_dist.sh | 19 - 283 files changed, 17 insertions(+), 37187 deletions(-) delete mode 100644 cv/semantic_segmentation/unet++/pytorch/.gitignore delete mode 100644 cv/semantic_segmentation/unet++/pytorch/CITATION.cff delete mode 100644 cv/semantic_segmentation/unet++/pytorch/LICENSE delete mode 100644 cv/semantic_segmentation/unet++/pytorch/configs/_base_/datasets/drive.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/configs/_base_/default_runtime.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/configs/_base_/models/unet++_r34.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_160k.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_1k.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_20k.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_320k.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_40k.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_80k.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/configs/unet++/unet++_r34_40k_drive.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/docker/Dockerfile delete mode 100644 cv/semantic_segmentation/unet++/pytorch/docker/serve/Dockerfile delete mode 100644 cv/semantic_segmentation/unet++/pytorch/docker/serve/config.properties delete mode 100644 cv/semantic_segmentation/unet++/pytorch/docker/serve/entrypoint.sh delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/activation.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/context_block.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv_module.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv_ws.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/drop.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/generalized_attention.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/hsigmoid.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/hswish.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/non_local.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/norm.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/padding.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/plugin.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/registry.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/scale.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/swish.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/transformer.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/upsample.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/wrappers.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/builder.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/flops_counter.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/fuse_conv_bn.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/sync_bn.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/weight_init.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/device/__init__.py delete mode 100755 cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/__init__.py delete mode 100755 cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/dataloader.py delete mode 100755 cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/hierarchical_data_manager.py delete mode 100755 cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/hook_wrapper.py delete mode 100755 cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/model_wrapper.py delete mode 100755 cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/runner.py delete mode 100755 cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/utils.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/_functions.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/data_parallel.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/distributed.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/scatter_gather.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/engine/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/engine/test.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/file_client.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/base.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/json_handler.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/pickle_handler.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/yaml_handler.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/io.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/parse.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/image/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/image/colorspace.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/image/geometric.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/image/io.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/image/misc.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/image/photometric.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/deprecated.json delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/mmcls.json delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/open_mmlab.json delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/torchvision_0.12.json delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/cc_attention.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/README.md delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/info.cpp delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/deprecated_wrappers.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/focal_loss.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/info.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/point_sample.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/psa_mask.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/ops/sync_bn.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/_functions.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/collate.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/data_container.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/data_parallel.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/distributed.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/distributed_deprecated.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/registry.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/scatter_gather.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/utils.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/base_module.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/base_runner.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/builder.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/checkpoint.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/default_constructor.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/dist_utils.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/epoch_based_runner.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/fp16_utils.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/checkpoint.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/closure.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/ema.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/evaluation.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/hook.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/iter_timer.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/base.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/clearml.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/dvclive.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/mlflow.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/neptune.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/pavi.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/segmind.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/tensorboard.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/text.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/wandb.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/lr_updater.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/memory.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/momentum_updater.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/optimizer.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/profiler.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/sampler_seed.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/sync_buffer.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/iter_based_runner.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/log_buffer.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/optimizer/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/optimizer/builder.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/optimizer/default_constructor.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/priority.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/runner/utils.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/config.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/device_type.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/env.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/ext_loader.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/hub.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/logging.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/misc.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/parrots_jit.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/parrots_wrapper.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/path.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/progressbar.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/registry.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/seed.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/testing.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/timer.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/trace.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/utils/version_utils.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmcv/version.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/apis/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/apis/inference.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/apis/test.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/apis/train.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/builder.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/class_names.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/eval_hooks.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/metrics.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/optimizers/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/builder.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/sampler/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/utils/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/utils/dist_util.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/core/utils/misc.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/builder.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/custom.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/dataset_wrappers.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/drive.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/compose.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/formating.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/formatting.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/loading.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/test_time_aug.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/transforms.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/samplers/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/samplers/distributed_sampler.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/backbones/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/backbones/resnet.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/builder.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/decode_head.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/fcn_head.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/unetplusplus_head.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/accuracy.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/cross_entropy_loss.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/dice_loss.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/focal_loss.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/lovasz_loss.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/utils.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/featurepyramid.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/fpn.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/ic_neck.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/jpu.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/mla_neck.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/multilevel_neck.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/base.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/encoder_decoder.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/embed.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/inverted_residual.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/make_divisible.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/res_layer.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/se_layer.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/self_attention_block.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/shape_convert.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/up_conv_block.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/wrappers.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/ops/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/ops/encoding.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/ops/wrappers.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/registry/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/registry/registry.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/utils/__init__.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/utils/collect_env.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/utils/logger.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/utils/misc.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/utils/set_env.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/mmseg/version.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/requirements.txt delete mode 100644 cv/semantic_segmentation/unet++/pytorch/requirements/apcnet.txt delete mode 100644 cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/build.txt delete mode 100644 cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/docs.txt delete mode 100644 cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/optional.txt delete mode 100644 cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/requirements.txt delete mode 100644 cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/runtime.txt delete mode 100644 cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/test.txt delete mode 100644 cv/semantic_segmentation/unet++/pytorch/requirements/mminstall.txt delete mode 100644 cv/semantic_segmentation/unet++/pytorch/requirements/optional.txt delete mode 100644 cv/semantic_segmentation/unet++/pytorch/requirements/runtime.txt delete mode 100644 cv/semantic_segmentation/unet++/pytorch/setup.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/tools/analyze_logs.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/tools/benchmark.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/tools/confusion_matrix.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/tools/convert_datasets/drive.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/tools/get_flops.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/tools/print_config.py delete mode 100755 cv/semantic_segmentation/unet++/pytorch/tools/slurm_test.sh delete mode 100755 cv/semantic_segmentation/unet++/pytorch/tools/slurm_train.sh delete mode 100644 cv/semantic_segmentation/unet++/pytorch/tools/test.py delete mode 100644 cv/semantic_segmentation/unet++/pytorch/train.py delete mode 100755 cv/semantic_segmentation/unet++/pytorch/train_dist.sh diff --git a/cv/semantic_segmentation/unet++/pytorch/.gitignore b/cv/semantic_segmentation/unet++/pytorch/.gitignore deleted file mode 100644 index 787d13ec6..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/.gitignore +++ /dev/null @@ -1,120 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/en/_build/ -docs/zh_cn/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ -.DS_Store - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -data -.vscode -.idea - -# custom -*.pkl -*.pkl.json -*.log.json -work_dirs/ -mmseg/.mim - -# Pytorch -*.pth diff --git a/cv/semantic_segmentation/unet++/pytorch/CITATION.cff b/cv/semantic_segmentation/unet++/pytorch/CITATION.cff deleted file mode 100644 index cfd7cab05..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/CITATION.cff +++ /dev/null @@ -1,8 +0,0 @@ -cff-version: 1.2.0 -message: "If you use this software, please cite it as below." -authors: - - name: "MMSegmentation Contributors" -title: "OpenMMLab Semantic Segmentation Toolbox and Benchmark" -date-released: 2020-07-10 -url: "https://github.com/open-mmlab/mmsegmentation" -license: Apache-2.0 diff --git a/cv/semantic_segmentation/unet++/pytorch/LICENSE b/cv/semantic_segmentation/unet++/pytorch/LICENSE deleted file mode 100644 index 38e625bf5..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Copyright 2020 The MMSegmentation Authors. All rights reserved. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020 The MMSegmentation Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cv/semantic_segmentation/unet++/pytorch/README.md b/cv/semantic_segmentation/unet++/pytorch/README.md index 32dc23ec3..4cdfeae15 100644 --- a/cv/semantic_segmentation/unet++/pytorch/README.md +++ b/cv/semantic_segmentation/unet++/pytorch/README.md @@ -15,20 +15,18 @@ similar. ### Install packages ```bash +# Install libGL +## CentOS yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx -pip3 install -r requirements.txt +# install mmsegmentation +git clone -b v1.2.2 https://github.com/open-mmlab/mmsegmentation.git --depth=1 +cd mmsegmentation/ +pip install -v -e . -wget http://www.zlib.net/fossils/zlib-1.2.9.tar.gz -tar xvf zlib-1.2.9.tar.gz -cd zlib-1.2.9/ -./configure && make install -``` - -### Build extension - -```bash -python3 setup.py build && cp build/lib.linux*/mmcv/_ext.cpython* mmcv +pip install ftfy ``` ## Step 2: Prepare datasets @@ -48,82 +46,15 @@ python3 tools/convert_datasets/drive.py /path/to/training.zip /path/to/test.zip ``` ## Step 3: Training - - - -```bash -# Training on multiple cards -## "config" files can be found in the configs directory -bash train_dist.sh [training args] - -# Example -bash train_dist.sh configs/unet++/unet++_r34_40k_drive.py 8 +### Training on single card +```shell +python3 tools/train.py configs/unet/unet-s5-d16_pspnet_4xb4-40k_drive-64x64.py ``` -**Training arguments are as follows:** - -```python -# the dir to save logs and models -work-dir: str = None - -# the checkpoint file to load weights from -load-from: str = None - -# the checkpoint file to resume from -resume-from: str = None - -# whether not to evaluate the checkpoint during training -no-validate: bool = False - -# (Deprecated, please use --gpu-id) number of gpus to -# use (only applicable to non-distributed training) -gpus: int = None - -# (Deprecated, please use --gpu-id) ids of gpus to use -# (only applicable to non-distributed training) -gpu-ids: int = None - -# id of gpu to use (only applicable to non-distributed training) -gpu-id: int = 0 - -# random seed -seed: int = None - -# Whether or not set different seeds for different ranks -diff_seed: bool = False - -# whether to set deterministic options for CUDNN backend. -deterministic: bool = False - -# --options is deprecated in favor of --cfg_options' and it -# will not be supported in version v0.22.0. Override some -# settings in the used config, the key-value pair in xxx=yyy -# format will be merged into config file. If the value to be -# overwritten is a list, it should be like key="[a,b]" or key=a,b -# It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" -# Note that the quotation marks are necessary and that no white space -# is allowed. -options: str = None - -# override some settings in the used config, the key-value pair -# in xxx=yyy format will be merged into config file. If the value -# to be overwritten is a list, it should be like key="[a,b]" or key=a,b -# It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" -# Note that the quotation marks are necessary and that no white -# space is allowed. -cfg-options: str = None - -# job launcher -launcher: str = "none" - -# local rank -local_rank: int = 0 - -# distributed backend -dist_backend: str = None - -# resume from the latest checkpoint automatically. -auto-resume: bool = False +### Training on mutil-cards +```shell +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/unet/unet-s5-d16_pspnet_4xb4-40k_drive-64x64.py 8 ``` ## Results @@ -133,5 +64,4 @@ auto-resume: bool = False | BI-V100 x8 | 64x64 | 40000 | 238.9 | 87.52 | ## Reference -- [dataset_prepare](https://mmsegmentation.readthedocs.io/en/latest/dataset_prepare.html#cityscapes) -- [mmsegmentation](https://github.com/open-mmlab/mmsegmentation) +[mmsegmentation](https://github.com/open-mmlab/mmsegmentation) diff --git a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/datasets/drive.py b/cv/semantic_segmentation/unet++/pytorch/configs/_base_/datasets/drive.py deleted file mode 100644 index a6fd25f15..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/datasets/drive.py +++ /dev/null @@ -1,59 +0,0 @@ -# dataset settings -dataset_type = 'DRIVEDataset' -data_root = 'data/DRIVE' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -img_scale = (584, 565) -crop_size = (64, 64) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']) -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']) - ]) -] - -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type='RepeatDataset', - times=40000, - dataset=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline)), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) \ No newline at end of file diff --git a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/default_runtime.py b/cv/semantic_segmentation/unet++/pytorch/configs/_base_/default_runtime.py deleted file mode 100644 index b564cc4e7..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/default_runtime.py +++ /dev/null @@ -1,14 +0,0 @@ -# yapf:disable -log_config = dict( - interval=50, - hooks=[ - dict(type='TextLoggerHook', by_epoch=False), - # dict(type='TensorboardLoggerHook') - ]) -# yapf:enable -dist_params = dict(backend='nccl') -log_level = 'INFO' -load_from = None -resume_from = None -workflow = [('train', 1)] -cudnn_benchmark = True diff --git a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/models/unet++_r34.py b/cv/semantic_segmentation/unet++/pytorch/configs/_base_/models/unet++_r34.py deleted file mode 100644 index d16550367..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/models/unet++_r34.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -# model settings -norm_cfg = dict(type='SyncBN', requires_grad=True) -model = dict( - type='EncoderDecoder', - pretrained=None, - backbone=dict(type='ResNet', - depth=34), - decode_head=dict( - type='UNetPlusPlusHead', - in_channels=[64, 128, 256, 512], - channels=64, - input_transform=None, - dropout_ratio=0.1, - num_classes=19, - norm_cfg=norm_cfg, - align_corners=False, - loss_decode=[ - dict( - type='CrossEntropyLoss', - loss_name='loss_ce', - use_sigmoid=False, - loss_weight=1.0), - dict(type='DiceLoss', loss_name='loss_dice', loss_weight=3.0), - ]), - auxiliary_head=dict( - type='FCNHead', - in_channels=128, - in_index=1, - channels=64, - num_convs=1, - concat_input=False, - dropout_ratio=0.1, - num_classes=2, - norm_cfg=norm_cfg, - align_corners=False, - loss_decode=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), - - # model training and testing settings - train_cfg=dict(), - test_cfg=dict(mode='slide', crop_size=(256, 256), stride=(170, 170)) -) \ No newline at end of file diff --git a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_160k.py b/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_160k.py deleted file mode 100644 index eae05127a..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_160k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=160000) -checkpoint_config = dict(by_epoch=False, interval=16000) -evaluation = dict(interval=4000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_1k.py b/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_1k.py deleted file mode 100644 index 04cf41030..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_1k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=1000) -checkpoint_config = dict(by_epoch=False, interval=1000) -evaluation = dict(interval=1000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_20k.py b/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_20k.py deleted file mode 100644 index 73c702197..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_20k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=20000) -checkpoint_config = dict(by_epoch=False, interval=2000) -evaluation = dict(interval=2000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_320k.py b/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_320k.py deleted file mode 100644 index a0b230626..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_320k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=320000) -checkpoint_config = dict(by_epoch=False, interval=32000) -evaluation = dict(interval=32000, metric='mIoU') diff --git a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_40k.py b/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_40k.py deleted file mode 100644 index d2c502325..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_40k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=40000) -checkpoint_config = dict(by_epoch=False, interval=4000) -evaluation = dict(interval=4000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_80k.py b/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_80k.py deleted file mode 100644 index 8365a878e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/configs/_base_/schedules/schedule_80k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=80000) -checkpoint_config = dict(by_epoch=False, interval=8000) -evaluation = dict(interval=8000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/unet++/pytorch/configs/unet++/unet++_r34_40k_drive.py b/cv/semantic_segmentation/unet++/pytorch/configs/unet++/unet++_r34_40k_drive.py deleted file mode 100644 index afa750868..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/configs/unet++/unet++_r34_40k_drive.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -_base_ = [ - '../_base_/models/unet++_r34.py', '../_base_/datasets/drive.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_40k.py' -] -model = dict(test_cfg=dict(crop_size=(64, 64), stride=(42, 42))) -evaluation = dict(metric='mDice') \ No newline at end of file diff --git a/cv/semantic_segmentation/unet++/pytorch/docker/Dockerfile b/cv/semantic_segmentation/unet++/pytorch/docker/Dockerfile deleted file mode 100644 index 64482b472..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/docker/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -ARG PYTORCH="1.11.0" -ARG CUDA="11.3" -ARG CUDNN="8" - -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ENV TORCH_CUDA_ARCH_LIST="6.0 6.1 7.0+PTX" -ENV TORCH_NVCC_FLAGS="-Xfatbin -compress-all" -ENV CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" - -# To fix GPG key error when running apt-get update -RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub -RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/7fa2af80.pub - -RUN apt-get update && apt-get install -y git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -RUN conda clean --all - -# Install MMCV -ARG PYTORCH -ARG CUDA -ARG MMCV -RUN ["/bin/bash", "-c", "pip install --no-cache-dir mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] - -# Install MMSegmentation -RUN git clone https://github.com/open-mmlab/mmsegmentation.git /mmsegmentation -WORKDIR /mmsegmentation -ENV FORCE_CUDA="1" -RUN pip install -r requirements.txt -RUN pip install --no-cache-dir -e . diff --git a/cv/semantic_segmentation/unet++/pytorch/docker/serve/Dockerfile b/cv/semantic_segmentation/unet++/pytorch/docker/serve/Dockerfile deleted file mode 100644 index c1d154528..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/docker/serve/Dockerfile +++ /dev/null @@ -1,49 +0,0 @@ -ARG PYTORCH="1.11.0" -ARG CUDA="11.3" -ARG CUDNN="8" -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ARG MMCV="1.4.8" -ARG MMSEG="0.24.1" - -ENV PYTHONUNBUFFERED TRUE - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ - ca-certificates \ - g++ \ - openjdk-11-jre-headless \ - # MMDet Requirements - ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && rm -rf /var/lib/apt/lists/* - -ENV PATH="/opt/conda/bin:$PATH" -RUN export FORCE_CUDA=1 - -# TORCHSEVER -RUN pip install torchserve torch-model-archiver - -# MMLAB -ARG PYTORCH -ARG CUDA -RUN ["/bin/bash", "-c", "pip install mmcv-full==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] -RUN pip install mmsegmentation==${MMSEG} - -RUN useradd -m model-server \ - && mkdir -p /home/model-server/tmp - -COPY entrypoint.sh /usr/local/bin/entrypoint.sh - -RUN chmod +x /usr/local/bin/entrypoint.sh \ - && chown -R model-server /home/model-server - -COPY config.properties /home/model-server/config.properties -RUN mkdir /home/model-server/model-store && chown -R model-server /home/model-server/model-store - -EXPOSE 8080 8081 8082 - -USER model-server -WORKDIR /home/model-server -ENV TEMP=/home/model-server/tmp -ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] -CMD ["serve"] diff --git a/cv/semantic_segmentation/unet++/pytorch/docker/serve/config.properties b/cv/semantic_segmentation/unet++/pytorch/docker/serve/config.properties deleted file mode 100644 index efb9c47e4..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/docker/serve/config.properties +++ /dev/null @@ -1,5 +0,0 @@ -inference_address=http://0.0.0.0:8080 -management_address=http://0.0.0.0:8081 -metrics_address=http://0.0.0.0:8082 -model_store=/home/model-server/model-store -load_models=all diff --git a/cv/semantic_segmentation/unet++/pytorch/docker/serve/entrypoint.sh b/cv/semantic_segmentation/unet++/pytorch/docker/serve/entrypoint.sh deleted file mode 100644 index 41ba00b04..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/docker/serve/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - -if [[ "$1" = "serve" ]]; then - shift 1 - torchserve --start --ts-config /home/model-server/config.properties -else - eval "$@" -fi - -# prevent docker exit -tail -f /dev/null diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/__init__.py deleted file mode 100644 index 435429d48..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -from .fileio import * -from .image import * -from .utils import * -from .version import * - -# The following modules are not imported to this level, so mmcv may be used -# without PyTorch. -# - runner -# - parallel -# - op -# - device diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/__init__.py deleted file mode 100644 index 3d5599d9a..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# yapf: disable -from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, - ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, - ConvTranspose2d, ConvTranspose3d, ConvWS2d, - DepthwiseSeparableConvModule, GeneralizedAttention, - HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, - NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, - build_activation_layer, build_conv_layer, - build_norm_layer, build_padding_layer, build_plugin_layer, - build_upsample_layer, conv_ws_2d, is_norm) -from .builder import MODELS, build_model_from_cfg -# yapf: enable -from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, - NormalInit, PretrainedInit, TruncNormalInit, UniformInit, - XavierInit, bias_init_with_prob, caffe2_xavier_init, - constant_init, fuse_conv_bn, get_model_complexity_info, - initialize, kaiming_init, normal_init, trunc_normal_init, - uniform_init, xavier_init) - diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/__init__.py deleted file mode 100644 index 0f33124ed..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .activation import build_activation_layer -from .context_block import ContextBlock -from .conv import build_conv_layer -from .conv2d_adaptive_padding import Conv2dAdaptivePadding -from .conv_module import ConvModule -from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d -from .depthwise_separable_conv_module import DepthwiseSeparableConvModule -from .drop import Dropout, DropPath -from .generalized_attention import GeneralizedAttention -from .hsigmoid import HSigmoid -from .hswish import HSwish -from .non_local import NonLocal1d, NonLocal2d, NonLocal3d -from .norm import build_norm_layer, is_norm -from .padding import build_padding_layer -from .plugin import build_plugin_layer -from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) -from .scale import Scale -from .swish import Swish -from .upsample import build_upsample_layer -from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, - Linear, MaxPool2d, MaxPool3d) - -__all__ = [ - 'ConvModule', 'build_activation_layer', 'build_conv_layer', - 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', - 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', - 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', - 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', - 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', - 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', - 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/activation.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/activation.py deleted file mode 100644 index 26be59581..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/activation.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version -from .registry import ACTIVATION_LAYERS - -for module in [ - nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, - nn.Sigmoid, nn.Tanh -]: - ACTIVATION_LAYERS.register_module(module=module) - - -@ACTIVATION_LAYERS.register_module(name='Clip') -@ACTIVATION_LAYERS.register_module() -class Clamp(nn.Module): - """Clamp activation layer. - - This activation function is to clamp the feature map value within - :math:`[min, max]`. More details can be found in ``torch.clamp()``. - - Args: - min (Number | optional): Lower-bound of the range to be clamped to. - Default to -1. - max (Number | optional): Upper-bound of the range to be clamped to. - Default to 1. - """ - - def __init__(self, min=-1., max=1.): - super(Clamp, self).__init__() - self.min = min - self.max = max - - def forward(self, x): - """Forward function. - - Args: - x (torch.Tensor): The input tensor. - - Returns: - torch.Tensor: Clamped tensor. - """ - return torch.clamp(x, min=self.min, max=self.max) - - -class GELU(nn.Module): - r"""Applies the Gaussian Error Linear Units function: - - .. math:: - \text{GELU}(x) = x * \Phi(x) - where :math:`\Phi(x)` is the Cumulative Distribution Function for - Gaussian Distribution. - - Shape: - - Input: :math:`(N, *)` where `*` means, any number of additional - dimensions - - Output: :math:`(N, *)`, same shape as the input - - .. image:: scripts/activation_images/GELU.png - - Examples:: - - >>> m = nn.GELU() - >>> input = torch.randn(2) - >>> output = m(input) - """ - - def forward(self, input): - return F.gelu(input) - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.4')): - ACTIVATION_LAYERS.register_module(module=GELU) -else: - ACTIVATION_LAYERS.register_module(module=nn.GELU) - - -def build_activation_layer(cfg): - """Build activation layer. - - Args: - cfg (dict): The activation layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate an activation layer. - - Returns: - nn.Module: Created activation layer. - """ - return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/context_block.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/context_block.py deleted file mode 100644 index d60fdb904..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/context_block.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn - -from ..utils import constant_init, kaiming_init -from .registry import PLUGIN_LAYERS - - -def last_zero_init(m): - if isinstance(m, nn.Sequential): - constant_init(m[-1], val=0) - else: - constant_init(m, val=0) - - -@PLUGIN_LAYERS.register_module() -class ContextBlock(nn.Module): - """ContextBlock module in GCNet. - - See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' - (https://arxiv.org/abs/1904.11492) for details. - - Args: - in_channels (int): Channels of the input feature map. - ratio (float): Ratio of channels of transform bottleneck - pooling_type (str): Pooling method for context modeling. - Options are 'att' and 'avg', stand for attention pooling and - average pooling respectively. Default: 'att'. - fusion_types (Sequence[str]): Fusion method for feature fusion, - Options are 'channels_add', 'channel_mul', stand for channelwise - addition and multiplication respectively. Default: ('channel_add',) - """ - - _abbr_ = 'context_block' - - def __init__(self, - in_channels, - ratio, - pooling_type='att', - fusion_types=('channel_add', )): - super(ContextBlock, self).__init__() - assert pooling_type in ['avg', 'att'] - assert isinstance(fusion_types, (list, tuple)) - valid_fusion_types = ['channel_add', 'channel_mul'] - assert all([f in valid_fusion_types for f in fusion_types]) - assert len(fusion_types) > 0, 'at least one fusion should be used' - self.in_channels = in_channels - self.ratio = ratio - self.planes = int(in_channels * ratio) - self.pooling_type = pooling_type - self.fusion_types = fusion_types - if pooling_type == 'att': - self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) - self.softmax = nn.Softmax(dim=2) - else: - self.avg_pool = nn.AdaptiveAvgPool2d(1) - if 'channel_add' in fusion_types: - self.channel_add_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_add_conv = None - if 'channel_mul' in fusion_types: - self.channel_mul_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_mul_conv = None - self.reset_parameters() - - def reset_parameters(self): - if self.pooling_type == 'att': - kaiming_init(self.conv_mask, mode='fan_in') - self.conv_mask.inited = True - - if self.channel_add_conv is not None: - last_zero_init(self.channel_add_conv) - if self.channel_mul_conv is not None: - last_zero_init(self.channel_mul_conv) - - def spatial_pool(self, x): - batch, channel, height, width = x.size() - if self.pooling_type == 'att': - input_x = x - # [N, C, H * W] - input_x = input_x.view(batch, channel, height * width) - # [N, 1, C, H * W] - input_x = input_x.unsqueeze(1) - # [N, 1, H, W] - context_mask = self.conv_mask(x) - # [N, 1, H * W] - context_mask = context_mask.view(batch, 1, height * width) - # [N, 1, H * W] - context_mask = self.softmax(context_mask) - # [N, 1, H * W, 1] - context_mask = context_mask.unsqueeze(-1) - # [N, 1, C, 1] - context = torch.matmul(input_x, context_mask) - # [N, C, 1, 1] - context = context.view(batch, channel, 1, 1) - else: - # [N, C, 1, 1] - context = self.avg_pool(x) - - return context - - def forward(self, x): - # [N, C, 1, 1] - context = self.spatial_pool(x) - - out = x - if self.channel_mul_conv is not None: - # [N, C, 1, 1] - channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) - out = out * channel_mul_term - if self.channel_add_conv is not None: - # [N, C, 1, 1] - channel_add_term = self.channel_add_conv(context) - out = out + channel_add_term - - return out diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv.py deleted file mode 100644 index f6c35fd70..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from .registry import CONV_LAYERS - -CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) -CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) -CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) -CONV_LAYERS.register_module('Conv', module=nn.Conv2d) - - -def build_conv_layer(cfg, *args, **kwargs): - """Build convolution layer. - - Args: - cfg (None or dict): The conv layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an conv layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding conv layer. - - Returns: - nn.Module: Created conv layer. - """ - if cfg is None: - cfg_ = dict(type='Conv2d') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in CONV_LAYERS: - raise KeyError(f'Unrecognized layer type {layer_type}') - else: - conv_layer = CONV_LAYERS.get(layer_type) - - layer = conv_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py deleted file mode 100644 index b45e758ac..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from torch import nn -from torch.nn import functional as F - -from .registry import CONV_LAYERS - - -@CONV_LAYERS.register_module() -class Conv2dAdaptivePadding(nn.Conv2d): - """Implementation of 2D convolution in tensorflow with `padding` as "same", - which applies padding to input (if needed) so that input image gets fully - covered by filter and stride you specified. For stride 1, this will ensure - that output image size is same as input. For stride of 2, output dimensions - will be half, for example. - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the convolving kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If ``True``, adds a learnable bias to the - output. Default: ``True`` - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__(in_channels, out_channels, kernel_size, stride, 0, - dilation, groups, bias) - - def forward(self, x): - img_h, img_w = x.size()[-2:] - kernel_h, kernel_w = self.weight.size()[-2:] - stride_h, stride_w = self.stride - output_h = math.ceil(img_h / stride_h) - output_w = math.ceil(img_w / stride_w) - pad_h = ( - max((output_h - 1) * self.stride[0] + - (kernel_h - 1) * self.dilation[0] + 1 - img_h, 0)) - pad_w = ( - max((output_w - 1) * self.stride[1] + - (kernel_w - 1) * self.dilation[1] + 1 - img_w, 0)) - if pad_h > 0 or pad_w > 0: - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2 - ]) - return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv_module.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv_module.py deleted file mode 100644 index 0078647a1..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv_module.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from mmcv.utils import _BatchNorm, _InstanceNorm -from ..utils import constant_init, kaiming_init -from .activation import build_activation_layer -from .conv import build_conv_layer -from .norm import build_norm_layer -from .padding import build_padding_layer -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class ConvModule(nn.Module): - """A conv block that bundles conv/norm/activation layers. - - This block simplifies the usage of convolution layers, which are commonly - used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). - It is based upon three build methods: `build_conv_layer()`, - `build_norm_layer()` and `build_activation_layer()`. - - Besides, we add some additional features in this module. - 1. Automatically set `bias` of the conv layer. - 2. Spectral norm is supported. - 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only - supports zero and circular padding, and we add "reflect" padding mode. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. - groups (int): Number of blocked connections from input channels to - output channels. Same as that in ``nn._ConvNd``. - bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - inplace (bool): Whether to use inplace mode for activation. - Default: True. - with_spectral_norm (bool): Whether use spectral norm in conv module. - Default: False. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in PyTorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Common examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - Default: ('conv', 'norm', 'act'). - """ - - _abbr_ = 'conv_block' - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias='auto', - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - padding_mode='zeros', - order=('conv', 'norm', 'act')): - super(ConvModule, self).__init__() - assert conv_cfg is None or isinstance(conv_cfg, dict) - assert norm_cfg is None or isinstance(norm_cfg, dict) - assert act_cfg is None or isinstance(act_cfg, dict) - official_padding_mode = ['zeros', 'circular'] - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.with_explicit_padding = padding_mode not in official_padding_mode - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 3 - assert set(order) == set(['conv', 'norm', 'act']) - - self.with_norm = norm_cfg is not None - self.with_activation = act_cfg is not None - # if the conv layer is before a norm layer, bias is unnecessary. - if bias == 'auto': - bias = not self.with_norm - self.with_bias = bias - - if self.with_explicit_padding: - pad_cfg = dict(type=padding_mode) - self.padding_layer = build_padding_layer(pad_cfg, padding) - - # reset padding to 0 for conv module - conv_padding = 0 if self.with_explicit_padding else padding - # build convolution layer - self.conv = build_conv_layer( - conv_cfg, - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=conv_padding, - dilation=dilation, - groups=groups, - bias=bias) - # export the attributes of self.conv to a higher level for convenience - self.in_channels = self.conv.in_channels - self.out_channels = self.conv.out_channels - self.kernel_size = self.conv.kernel_size - self.stride = self.conv.stride - self.padding = padding - self.dilation = self.conv.dilation - self.transposed = self.conv.transposed - self.output_padding = self.conv.output_padding - self.groups = self.conv.groups - - if self.with_spectral_norm: - self.conv = nn.utils.spectral_norm(self.conv) - - # build normalization layers - if self.with_norm: - # norm layer is after conv layer - if order.index('norm') > order.index('conv'): - norm_channels = out_channels - else: - norm_channels = in_channels - self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) - self.add_module(self.norm_name, norm) - if self.with_bias: - if isinstance(norm, (_BatchNorm, _InstanceNorm)): - warnings.warn( - 'Unnecessary conv bias before batch/instance norm') - else: - self.norm_name = None - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - # nn.Tanh has no 'inplace' argument - if act_cfg_['type'] not in [ - 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish', 'GELU' - ]: - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - @property - def norm(self): - if self.norm_name: - return getattr(self, self.norm_name) - else: - return None - - def init_weights(self): - # 1. It is mainly for customized conv layers with their own - # initialization manners by calling their own ``init_weights()``, - # and we do not want ConvModule to override the initialization. - # 2. For customized conv layers without their own initialization - # manners (that is, they don't have their own ``init_weights()``) - # and PyTorch's conv layers, they will be initialized by - # this method with default ``kaiming_init``. - # Note: For PyTorch's conv layers, they will be overwritten by our - # initialization implementation using default ``kaiming_init``. - if not hasattr(self.conv, 'init_weights'): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) - if self.with_norm: - constant_init(self.norm, 1, bias=0) - - def forward(self, x, activate=True, norm=True): - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - x = self.conv(x) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv_ws.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv_ws.py deleted file mode 100644 index a3941e278..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/conv_ws.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from .registry import CONV_LAYERS - - -def conv_ws_2d(input, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - eps=1e-5): - c_in = weight.size(0) - weight_flat = weight.view(c_in, -1) - mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1) - std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1) - weight = (weight - mean) / (std + eps) - return F.conv2d(input, weight, bias, stride, padding, dilation, groups) - - -@CONV_LAYERS.register_module('ConvWS') -class ConvWS2d(nn.Conv2d): - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True, - eps=1e-5): - super(ConvWS2d, self).__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.eps = eps - - def forward(self, x): - return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups, self.eps) - - -@CONV_LAYERS.register_module(name='ConvAWS') -class ConvAWS2d(nn.Conv2d): - """AWS (Adaptive Weight Standardization) - - This is a variant of Weight Standardization - (https://arxiv.org/pdf/1903.10520.pdf) - It is used in DetectoRS to avoid NaN - (https://arxiv.org/pdf/2006.02334.pdf) - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the conv kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If set True, adds a learnable bias to the - output. Default: True - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.register_buffer('weight_gamma', - torch.ones(self.out_channels, 1, 1, 1)) - self.register_buffer('weight_beta', - torch.zeros(self.out_channels, 1, 1, 1)) - - def _get_weight(self, weight): - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - weight = (weight - mean) / std - weight = self.weight_gamma * weight + self.weight_beta - return weight - - def forward(self, x): - weight = self._get_weight(self.weight) - return F.conv2d(x, weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - """Override default load function. - - AWS overrides the function _load_from_state_dict to recover - weight_gamma and weight_beta if they are missing. If weight_gamma and - weight_beta are found in the checkpoint, this function will return - after super()._load_from_state_dict. Otherwise, it will compute the - mean and std of the pretrained weights and store them in weight_beta - and weight_gamma. - """ - - self.weight_gamma.data.fill_(-1) - local_missing_keys = [] - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, local_missing_keys, - unexpected_keys, error_msgs) - if self.weight_gamma.data.mean() > 0: - for k in local_missing_keys: - missing_keys.append(k) - return - weight = self.weight.data - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - self.weight_beta.data.copy_(mean) - self.weight_gamma.data.copy_(std) - missing_gamma_beta = [ - k for k in local_missing_keys - if k.endswith('weight_gamma') or k.endswith('weight_beta') - ] - for k in missing_gamma_beta: - local_missing_keys.remove(k) - for k in local_missing_keys: - missing_keys.append(k) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py deleted file mode 100644 index 722d5d8d7..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .conv_module import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if `norm_cfg` and `act_cfg` are specified. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. Default: 1. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. Default: 0. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super(DepthwiseSeparableConvModule, self).__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/drop.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/drop.py deleted file mode 100644 index b0a026654..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/drop.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from mmcv import build_from_cfg -from .registry import DROPOUT_LAYERS - - -def drop_path(x, drop_prob=0., training=False): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - """ - if drop_prob == 0. or not training: - return x - keep_prob = 1 - drop_prob - # handle tensors with different dimensions, not just 4D tensors. - shape = (x.shape[0], ) + (1, ) * (x.ndim - 1) - random_tensor = keep_prob + torch.rand( - shape, dtype=x.dtype, device=x.device) - output = x.div(keep_prob) * random_tensor.floor() - return output - - -@DROPOUT_LAYERS.register_module() -class DropPath(nn.Module): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - - Args: - drop_prob (float): Probability of the path to be zeroed. Default: 0.1 - """ - - def __init__(self, drop_prob=0.1): - super(DropPath, self).__init__() - self.drop_prob = drop_prob - - def forward(self, x): - return drop_path(x, self.drop_prob, self.training) - - -@DROPOUT_LAYERS.register_module() -class Dropout(nn.Dropout): - """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of - ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with - ``DropPath`` - - Args: - drop_prob (float): Probability of the elements to be - zeroed. Default: 0.5. - inplace (bool): Do the operation inplace or not. Default: False. - """ - - def __init__(self, drop_prob=0.5, inplace=False): - super().__init__(p=drop_prob, inplace=inplace) - - -def build_dropout(cfg, default_args=None): - """Builder for drop out layers.""" - return build_from_cfg(cfg, DROPOUT_LAYERS, default_args) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/generalized_attention.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/generalized_attention.py deleted file mode 100644 index c8a74d268..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/generalized_attention.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import kaiming_init -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class GeneralizedAttention(nn.Module): - """GeneralizedAttention module. - - See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' - (https://arxiv.org/abs/1711.07971) for details. - - Args: - in_channels (int): Channels of the input feature map. - spatial_range (int): The spatial range. -1 indicates no spatial range - constraint. Default: -1. - num_heads (int): The head number of empirical_attention module. - Default: 9. - position_embedding_dim (int): The position embedding dimension. - Default: -1. - position_magnitude (int): A multiplier acting on coord difference. - Default: 1. - kv_stride (int): The feature stride acting on key/value feature map. - Default: 2. - q_stride (int): The feature stride acting on query feature map. - Default: 1. - attention_type (str): A binary indicator string for indicating which - items in generalized empirical_attention module are used. - Default: '1111'. - - - '1000' indicates 'query and key content' (appr - appr) item, - - '0100' indicates 'query content and relative position' - (appr - position) item, - - '0010' indicates 'key content only' (bias - appr) item, - - '0001' indicates 'relative position only' (bias - position) item. - """ - - _abbr_ = 'gen_attention_block' - - def __init__(self, - in_channels, - spatial_range=-1, - num_heads=9, - position_embedding_dim=-1, - position_magnitude=1, - kv_stride=2, - q_stride=1, - attention_type='1111'): - - super(GeneralizedAttention, self).__init__() - - # hard range means local range for non-local operation - self.position_embedding_dim = ( - position_embedding_dim - if position_embedding_dim > 0 else in_channels) - - self.position_magnitude = position_magnitude - self.num_heads = num_heads - self.in_channels = in_channels - self.spatial_range = spatial_range - self.kv_stride = kv_stride - self.q_stride = q_stride - self.attention_type = [bool(int(_)) for _ in attention_type] - self.qk_embed_dim = in_channels // num_heads - out_c = self.qk_embed_dim * num_heads - - if self.attention_type[0] or self.attention_type[1]: - self.query_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.query_conv.kaiming_init = True - - if self.attention_type[0] or self.attention_type[2]: - self.key_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.key_conv.kaiming_init = True - - self.v_dim = in_channels // num_heads - self.value_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=self.v_dim * num_heads, - kernel_size=1, - bias=False) - self.value_conv.kaiming_init = True - - if self.attention_type[1] or self.attention_type[3]: - self.appr_geom_fc_x = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_x.kaiming_init = True - - self.appr_geom_fc_y = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_y.kaiming_init = True - - if self.attention_type[2]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.appr_bias = nn.Parameter(appr_bias_value) - - if self.attention_type[3]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.geom_bias = nn.Parameter(geom_bias_value) - - self.proj_conv = nn.Conv2d( - in_channels=self.v_dim * num_heads, - out_channels=in_channels, - kernel_size=1, - bias=True) - self.proj_conv.kaiming_init = True - self.gamma = nn.Parameter(torch.zeros(1)) - - if self.spatial_range >= 0: - # only works when non local is after 3*3 conv - if in_channels == 256: - max_len = 84 - elif in_channels == 512: - max_len = 42 - - max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) - local_constraint_map = np.ones( - (max_len, max_len, max_len_kv, max_len_kv), dtype=int) - for iy in range(max_len): - for ix in range(max_len): - local_constraint_map[ - iy, ix, - max((iy - self.spatial_range) // - self.kv_stride, 0):min((iy + self.spatial_range + - 1) // self.kv_stride + - 1, max_len), - max((ix - self.spatial_range) // - self.kv_stride, 0):min((ix + self.spatial_range + - 1) // self.kv_stride + - 1, max_len)] = 0 - - self.local_constraint_map = nn.Parameter( - torch.from_numpy(local_constraint_map).byte(), - requires_grad=False) - - if self.q_stride > 1: - self.q_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.q_stride) - else: - self.q_downsample = None - - if self.kv_stride > 1: - self.kv_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.kv_stride) - else: - self.kv_downsample = None - - self.init_weights() - - def get_position_embedding(self, - h, - w, - h_kv, - w_kv, - q_stride, - kv_stride, - device, - dtype, - feat_dim, - wave_length=1000): - # the default type of Tensor is float32, leading to type mismatch - # in fp16 mode. Cast it to support fp16 mode. - h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) - h_idxs = h_idxs.view((h, 1)) * q_stride - - w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) - w_idxs = w_idxs.view((w, 1)) * q_stride - - h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( - device=device, dtype=dtype) - h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride - - w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( - device=device, dtype=dtype) - w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride - - # (h, h_kv, 1) - h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) - h_diff *= self.position_magnitude - - # (w, w_kv, 1) - w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) - w_diff *= self.position_magnitude - - feat_range = torch.arange(0, feat_dim / 4).to( - device=device, dtype=dtype) - - dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) - dim_mat = dim_mat**((4. / feat_dim) * feat_range) - dim_mat = dim_mat.view((1, 1, -1)) - - embedding_x = torch.cat( - ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) - - embedding_y = torch.cat( - ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) - - return embedding_x, embedding_y - - def forward(self, x_input): - num_heads = self.num_heads - - # use empirical_attention - if self.q_downsample is not None: - x_q = self.q_downsample(x_input) - else: - x_q = x_input - n, _, h, w = x_q.shape - - if self.kv_downsample is not None: - x_kv = self.kv_downsample(x_input) - else: - x_kv = x_input - _, _, h_kv, w_kv = x_kv.shape - - if self.attention_type[0] or self.attention_type[1]: - proj_query = self.query_conv(x_q).view( - (n, num_heads, self.qk_embed_dim, h * w)) - proj_query = proj_query.permute(0, 1, 3, 2) - - if self.attention_type[0] or self.attention_type[2]: - proj_key = self.key_conv(x_kv).view( - (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) - - if self.attention_type[1] or self.attention_type[3]: - position_embed_x, position_embed_y = self.get_position_embedding( - h, w, h_kv, w_kv, self.q_stride, self.kv_stride, - x_input.device, x_input.dtype, self.position_embedding_dim) - # (n, num_heads, w, w_kv, dim) - position_feat_x = self.appr_geom_fc_x(position_embed_x).\ - view(1, w, w_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - # (n, num_heads, h, h_kv, dim) - position_feat_y = self.appr_geom_fc_y(position_embed_y).\ - view(1, h, h_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - position_feat_x /= math.sqrt(2) - position_feat_y /= math.sqrt(2) - - # accelerate for saliency only - if (np.sum(self.attention_type) == 1) and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy = torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, h_kv * w_kv) - - h = 1 - w = 1 - else: - # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for - if not self.attention_type[0]: - energy = torch.zeros( - n, - num_heads, - h, - w, - h_kv, - w_kv, - dtype=x_input.dtype, - device=x_input.device) - - # attention_type[0]: appr - appr - # attention_type[1]: appr - position - # attention_type[2]: bias - appr - # attention_type[3]: bias - position - if self.attention_type[0] or self.attention_type[2]: - if self.attention_type[0] and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - energy = torch.matmul(proj_query + appr_bias, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[0]: - energy = torch.matmul(proj_query, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy += torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, 1, h_kv, w_kv) - - if self.attention_type[1] or self.attention_type[3]: - if self.attention_type[1] and self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - - proj_query_reshape = (proj_query + geom_bias).\ - view(n, num_heads, h, w, self.qk_embed_dim) - - energy_x = torch.matmul( - proj_query_reshape.permute(0, 1, 3, 2, 4), - position_feat_x.permute(0, 1, 2, 4, 3)) - energy_x = energy_x.\ - permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul( - proj_query_reshape, - position_feat_y.permute(0, 1, 2, 4, 3)) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[1]: - proj_query_reshape = proj_query.\ - view(n, num_heads, h, w, self.qk_embed_dim) - proj_query_reshape = proj_query_reshape.\ - permute(0, 1, 3, 2, 4) - position_feat_x_reshape = position_feat_x.\ - permute(0, 1, 2, 4, 3) - position_feat_y_reshape = position_feat_y.\ - permute(0, 1, 2, 4, 3) - - energy_x = torch.matmul(proj_query_reshape, - position_feat_x_reshape) - energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul(proj_query_reshape, - position_feat_y_reshape) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, self.qk_embed_dim, 1).\ - repeat(n, 1, 1, 1) - - position_feat_x_reshape = position_feat_x.\ - view(n, num_heads, w * w_kv, self.qk_embed_dim) - - position_feat_y_reshape = position_feat_y.\ - view(n, num_heads, h * h_kv, self.qk_embed_dim) - - energy_x = torch.matmul(position_feat_x_reshape, geom_bias) - energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) - - energy_y = torch.matmul(position_feat_y_reshape, geom_bias) - energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) - - energy += energy_x + energy_y - - energy = energy.view(n, num_heads, h * w, h_kv * w_kv) - - if self.spatial_range >= 0: - cur_local_constraint_map = \ - self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ - contiguous().\ - view(1, 1, h*w, h_kv*w_kv) - - energy = energy.masked_fill_(cur_local_constraint_map, - float('-inf')) - - attention = F.softmax(energy, 3) - - proj_value = self.value_conv(x_kv) - proj_value_reshape = proj_value.\ - view((n, num_heads, self.v_dim, h_kv * w_kv)).\ - permute(0, 1, 3, 2) - - out = torch.matmul(attention, proj_value_reshape).\ - permute(0, 1, 3, 2).\ - contiguous().\ - view(n, self.v_dim * self.num_heads, h, w) - - out = self.proj_conv(out) - - # output is downsampled, upsample back to input size - if self.q_downsample is not None: - out = F.interpolate( - out, - size=x_input.shape[2:], - mode='bilinear', - align_corners=False) - - out = self.gamma * out + x_input - return out - - def init_weights(self): - for m in self.modules(): - if hasattr(m, 'kaiming_init') and m.kaiming_init: - kaiming_init( - m, - mode='fan_in', - nonlinearity='leaky_relu', - bias=0, - distribution='uniform', - a=1) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/hsigmoid.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/hsigmoid.py deleted file mode 100644 index e013d739e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/hsigmoid.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSigmoid(nn.Module): - """Hard Sigmoid Module. Apply the hard sigmoid function: - Hsigmoid(x) = min(max((x + bias) / divisor, min_value), max_value) - Default: Hsigmoid(x) = min(max((x + 3) / 6, 0), 1) - - Note: - In MMCV v1.4.4, we modified the default value of args to align with - PyTorch official. - - Args: - bias (float): Bias of the input feature map. Default: 3.0. - divisor (float): Divisor of the input feature map. Default: 6.0. - min_value (float): Lower bound value. Default: 0.0. - max_value (float): Upper bound value. Default: 1.0. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, bias=3.0, divisor=6.0, min_value=0.0, max_value=1.0): - super(HSigmoid, self).__init__() - warnings.warn( - 'In MMCV v1.4.4, we modified the default value of args to align ' - 'with PyTorch official. Previous Implementation: ' - 'Hsigmoid(x) = min(max((x + 1) / 2, 0), 1). ' - 'Current Implementation: ' - 'Hsigmoid(x) = min(max((x + 3) / 6, 0), 1).') - self.bias = bias - self.divisor = divisor - assert self.divisor != 0 - self.min_value = min_value - self.max_value = max_value - - def forward(self, x): - x = (x + self.bias) / self.divisor - - return x.clamp_(self.min_value, self.max_value) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/hswish.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/hswish.py deleted file mode 100644 index 27096832f..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/hswish.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import ACTIVATION_LAYERS - - -class HSwish(nn.Module): - """Hard Swish Module. - - This module applies the hard swish function: - - .. math:: - Hswish(x) = x * ReLU6(x + 3) / 6 - - Args: - inplace (bool): can optionally do the operation in-place. - Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.act = nn.ReLU6(inplace) - - def forward(self, x): - return x * self.act(x + 3) / 6 - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.7')): - # Hardswish is not supported when PyTorch version < 1.6. - # And Hardswish in PyTorch 1.6 does not support inplace. - ACTIVATION_LAYERS.register_module(module=HSwish) -else: - ACTIVATION_LAYERS.register_module(module=nn.Hardswish, name='HSwish') diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/non_local.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/non_local.py deleted file mode 100644 index 92d00155e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/non_local.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta - -import torch -import torch.nn as nn - -from ..utils import constant_init, normal_init -from .conv_module import ConvModule -from .registry import PLUGIN_LAYERS - - -class _NonLocalNd(nn.Module, metaclass=ABCMeta): - """Basic Non-local module. - - This module is proposed in - "Non-local Neural Networks" - Paper reference: https://arxiv.org/abs/1711.07971 - Code reference: https://github.com/AlexHex7/Non-local_pytorch - - Args: - in_channels (int): Channels of the input feature map. - reduction (int): Channel reduction ratio. Default: 2. - use_scale (bool): Whether to scale pairwise_weight by - `1/sqrt(inter_channels)` when the mode is `embedded_gaussian`. - Default: True. - conv_cfg (None | dict): The config dict for convolution layers. - If not specified, it will use `nn.Conv2d` for convolution layers. - Default: None. - norm_cfg (None | dict): The config dict for normalization layers. - Default: None. (This parameter is only applicable to conv_out.) - mode (str): Options are `gaussian`, `concatenation`, - `embedded_gaussian` and `dot_product`. Default: embedded_gaussian. - """ - - def __init__(self, - in_channels, - reduction=2, - use_scale=True, - conv_cfg=None, - norm_cfg=None, - mode='embedded_gaussian', - **kwargs): - super(_NonLocalNd, self).__init__() - self.in_channels = in_channels - self.reduction = reduction - self.use_scale = use_scale - self.inter_channels = max(in_channels // reduction, 1) - self.mode = mode - - if mode not in [ - 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation' - ]: - raise ValueError("Mode should be in 'gaussian', 'concatenation', " - f"'embedded_gaussian' or 'dot_product', but got " - f'{mode} instead.') - - # g, theta, phi are defaulted as `nn.ConvNd`. - # Here we use ConvModule for potential usage. - self.g = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.conv_out = ConvModule( - self.inter_channels, - self.in_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - if self.mode != 'gaussian': - self.theta = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.phi = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - - if self.mode == 'concatenation': - self.concat_project = ConvModule( - self.inter_channels * 2, - 1, - kernel_size=1, - stride=1, - padding=0, - bias=False, - act_cfg=dict(type='ReLU')) - - self.init_weights(**kwargs) - - def init_weights(self, std=0.01, zeros_init=True): - if self.mode != 'gaussian': - for m in [self.g, self.theta, self.phi]: - normal_init(m.conv, std=std) - else: - normal_init(self.g.conv, std=std) - if zeros_init: - if self.conv_out.norm_cfg is None: - constant_init(self.conv_out.conv, 0) - else: - constant_init(self.conv_out.norm, 0) - else: - if self.conv_out.norm_cfg is None: - normal_init(self.conv_out.conv, std=std) - else: - normal_init(self.conv_out.norm, std=std) - - def gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def embedded_gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - if self.use_scale: - # theta_x.shape[-1] is `self.inter_channels` - pairwise_weight /= theta_x.shape[-1]**0.5 - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def dot_product(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight /= pairwise_weight.shape[-1] - return pairwise_weight - - def concatenation(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - h = theta_x.size(2) - w = phi_x.size(3) - theta_x = theta_x.repeat(1, 1, 1, w) - phi_x = phi_x.repeat(1, 1, h, 1) - - concat_feature = torch.cat([theta_x, phi_x], dim=1) - pairwise_weight = self.concat_project(concat_feature) - n, _, h, w = pairwise_weight.size() - pairwise_weight = pairwise_weight.view(n, h, w) - pairwise_weight /= pairwise_weight.shape[-1] - - return pairwise_weight - - def forward(self, x): - # Assume `reduction = 1`, then `inter_channels = C` - # or `inter_channels = C` when `mode="gaussian"` - - # NonLocal1d x: [N, C, H] - # NonLocal2d x: [N, C, H, W] - # NonLocal3d x: [N, C, T, H, W] - n = x.size(0) - - # NonLocal1d g_x: [N, H, C] - # NonLocal2d g_x: [N, HxW, C] - # NonLocal3d g_x: [N, TxHxW, C] - g_x = self.g(x).view(n, self.inter_channels, -1) - g_x = g_x.permute(0, 2, 1) - - # NonLocal1d theta_x: [N, H, C], phi_x: [N, C, H] - # NonLocal2d theta_x: [N, HxW, C], phi_x: [N, C, HxW] - # NonLocal3d theta_x: [N, TxHxW, C], phi_x: [N, C, TxHxW] - if self.mode == 'gaussian': - theta_x = x.view(n, self.in_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - if self.sub_sample: - phi_x = self.phi(x).view(n, self.in_channels, -1) - else: - phi_x = x.view(n, self.in_channels, -1) - elif self.mode == 'concatenation': - theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) - phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) - else: - theta_x = self.theta(x).view(n, self.inter_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - phi_x = self.phi(x).view(n, self.inter_channels, -1) - - pairwise_func = getattr(self, self.mode) - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = pairwise_func(theta_x, phi_x) - - # NonLocal1d y: [N, H, C] - # NonLocal2d y: [N, HxW, C] - # NonLocal3d y: [N, TxHxW, C] - y = torch.matmul(pairwise_weight, g_x) - # NonLocal1d y: [N, C, H] - # NonLocal2d y: [N, C, H, W] - # NonLocal3d y: [N, C, T, H, W] - y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, - *x.size()[2:]) - - output = x + self.conv_out(y) - - return output - - -class NonLocal1d(_NonLocalNd): - """1D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv1d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv1d'), - **kwargs): - super(NonLocal1d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool1d(kernel_size=2) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -@PLUGIN_LAYERS.register_module() -class NonLocal2d(_NonLocalNd): - """2D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv2d'). - """ - - _abbr_ = 'nonlocal_block' - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv2d'), - **kwargs): - super(NonLocal2d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -class NonLocal3d(_NonLocalNd): - """3D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv3d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv3d'), - **kwargs): - super(NonLocal3d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/norm.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/norm.py deleted file mode 100644 index 51efdc184..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/norm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import torch.nn as nn - -from mmcv.utils import is_tuple_of -from mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm -from .registry import NORM_LAYERS - -NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) -NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) -NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) -NORM_LAYERS.register_module('GN', module=nn.GroupNorm) -NORM_LAYERS.register_module('LN', module=nn.LayerNorm) -NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) -NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - When we build a norm layer with `build_norm_layer()`, we want to preserve - the norm type in variable names, e.g, self.bn1, self.gn. This method will - infer the abbreviation to map class types to abbreviations. - - Rule 1: If the class has the property "_abbr_", return the property. - Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or - InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and - "in" respectively. - Rule 3: If the class name contains "batch", "group", "layer" or "instance", - the abbreviation of this layer will be "bn", "gn", "ln" and "in" - respectively. - Rule 4: Otherwise, the abbreviation falls back to "norm". - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN - return 'in' - elif issubclass(class_type, _BatchNorm): - return 'bn' - elif issubclass(class_type, nn.GroupNorm): - return 'gn' - elif issubclass(class_type, nn.LayerNorm): - return 'ln' - else: - class_name = class_type.__name__.lower() - if 'batch' in class_name: - return 'bn' - elif 'group' in class_name: - return 'gn' - elif 'layer' in class_name: - return 'ln' - elif 'instance' in class_name: - return 'in' - else: - return 'norm_layer' - - -def build_norm_layer(cfg, num_features, postfix=''): - """Build normalization layer. - - Args: - cfg (dict): The norm layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate a norm layer. - - requires_grad (bool, optional): Whether stop gradient updates. - num_features (int): Number of input channels. - postfix (int | str): The postfix to be appended into norm abbreviation - to create named layer. - - Returns: - tuple[str, nn.Module]: The first element is the layer name consisting - of abbreviation and postfix, e.g., bn1, gn. The second element is the - created norm layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in NORM_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - - norm_layer = NORM_LAYERS.get(layer_type) - abbr = infer_abbr(norm_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - requires_grad = cfg_.pop('requires_grad', True) - cfg_.setdefault('eps', 1e-5) - if layer_type != 'GN': - layer = norm_layer(num_features, **cfg_) - if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): - layer._specify_ddp_gpu_num(1) - else: - assert 'num_groups' in cfg_ - layer = norm_layer(num_channels=num_features, **cfg_) - - for param in layer.parameters(): - param.requires_grad = requires_grad - - return name, layer - - -def is_norm(layer, exclude=None): - """Check if a layer is a normalization layer. - - Args: - layer (nn.Module): The layer to be checked. - exclude (type | tuple[type]): Types to be excluded. - - Returns: - bool: Whether the layer is a norm layer. - """ - if exclude is not None: - if not isinstance(exclude, tuple): - exclude = (exclude, ) - if not is_tuple_of(exclude, type): - raise TypeError( - f'"exclude" must be either None or type or a tuple of types, ' - f'but got {type(exclude)}: {exclude}') - - if exclude and isinstance(layer, exclude): - return False - - all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) - return isinstance(layer, all_norm_bases) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/padding.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/padding.py deleted file mode 100644 index e4ac6b28a..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/padding.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import PADDING_LAYERS - -PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) -PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) -PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) - - -def build_padding_layer(cfg, *args, **kwargs): - """Build padding layer. - - Args: - cfg (None or dict): The padding layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate a padding layer. - - Returns: - nn.Module: Created padding layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - - cfg_ = cfg.copy() - padding_type = cfg_.pop('type') - if padding_type not in PADDING_LAYERS: - raise KeyError(f'Unrecognized padding type {padding_type}.') - else: - padding_layer = PADDING_LAYERS.get(padding_type) - - layer = padding_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/plugin.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/plugin.py deleted file mode 100644 index 009f7529b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/plugin.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import platform - -from .registry import PLUGIN_LAYERS - -if platform.system() == 'Windows': - import regex as re -else: - import re - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - This method will infer the abbreviation to map class types to - abbreviations. - - Rule 1: If the class has the property "abbr", return the property. - Rule 2: Otherwise, the abbreviation falls back to snake case of class - name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - - def camel2snack(word): - """Convert camel case word into snack case. - - Modified from `inflection lib - `_. - - Example:: - - >>> camel2snack("FancyBlock") - 'fancy_block' - """ - - word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) - word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) - word = word.replace('-', '_') - return word.lower() - - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - else: - return camel2snack(class_type.__name__) - - -def build_plugin_layer(cfg, postfix='', **kwargs): - """Build plugin layer. - - Args: - cfg (None or dict): cfg should contain: - - - type (str): identify plugin layer type. - - layer args: args needed to instantiate a plugin layer. - postfix (int, str): appended into norm abbreviation to - create named layer. Default: ''. - - Returns: - tuple[str, nn.Module]: The first one is the concatenation of - abbreviation and postfix. The second is the created plugin layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in PLUGIN_LAYERS: - raise KeyError(f'Unrecognized plugin type {layer_type}') - - plugin_layer = PLUGIN_LAYERS.get(layer_type) - abbr = infer_abbr(plugin_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - layer = plugin_layer(**kwargs, **cfg_) - - return name, layer diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/registry.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/registry.py deleted file mode 100644 index c29279776..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/registry.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -CONV_LAYERS = Registry('conv layer') -NORM_LAYERS = Registry('norm layer') -ACTIVATION_LAYERS = Registry('activation layer') -PADDING_LAYERS = Registry('padding layer') -UPSAMPLE_LAYERS = Registry('upsample layer') -PLUGIN_LAYERS = Registry('plugin layer') - -DROPOUT_LAYERS = Registry('drop out layers') -POSITIONAL_ENCODING = Registry('position encoding') -ATTENTION = Registry('attention') -FEEDFORWARD_NETWORK = Registry('feed-forward Network') -TRANSFORMER_LAYER = Registry('transformerLayer') -TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/scale.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/scale.py deleted file mode 100644 index c905fffcc..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/scale.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class Scale(nn.Module): - """A learnable scale parameter. - - This layer scales the input by a learnable factor. It multiplies a - learnable scale parameter of shape (1,) with input of any shape. - - Args: - scale (float): Initial value of scale factor. Default: 1.0 - """ - - def __init__(self, scale=1.0): - super(Scale, self).__init__() - self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) - - def forward(self, x): - return x * self.scale diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/swish.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/swish.py deleted file mode 100644 index e2ca8ed7b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/swish.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class Swish(nn.Module): - """Swish Module. - - This module applies the swish function: - - .. math:: - Swish(x) = x * Sigmoid(x) - - Returns: - Tensor: The output tensor. - """ - - def __init__(self): - super(Swish, self).__init__() - - def forward(self, x): - return x * torch.sigmoid(x) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/transformer.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/transformer.py deleted file mode 100644 index 70c6623c7..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/transformer.py +++ /dev/null @@ -1,944 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings -from typing import Sequence - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.cnn import (Linear, build_activation_layer, build_conv_layer, - build_norm_layer) -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential -from mmcv.utils import (ConfigDict, build_from_cfg, deprecated_api_warning, - to_2tuple) -from .drop import build_dropout -from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, - TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) - -# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file -try: - from mmcv.ops.multi_scale_deform_attn import \ - MultiScaleDeformableAttention # noqa F401 - warnings.warn( - ImportWarning( - '``MultiScaleDeformableAttention`` has been moved to ' - '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 - '``from mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 - 'to ``from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 - )) - -except ImportError: - warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' - '``mmcv.ops.multi_scale_deform_attn``, ' - 'You should install ``mmcv-full`` if you need this module. ') - - -def build_positional_encoding(cfg, default_args=None): - """Builder for Position Encoding.""" - return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) - - -def build_attention(cfg, default_args=None): - """Builder for attention.""" - return build_from_cfg(cfg, ATTENTION, default_args) - - -def build_feedforward_network(cfg, default_args=None): - """Builder for feed-forward network (FFN).""" - return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) - - -def build_transformer_layer(cfg, default_args=None): - """Builder for transformer layer.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) - - -def build_transformer_layer_sequence(cfg, default_args=None): - """Builder for transformer encoder and transformer decoder.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) - - -class AdaptivePadding(nn.Module): - """Applies padding adaptively to the input. - - This module can make input get fully covered by filter - you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad - zero around input. The "corner" mode would pad zero - to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel. Default: 1. - stride (int | tuple): Stride of the filter. Default: 1. - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - super(AdaptivePadding, self).__init__() - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - """Calculate the padding size of input. - - Args: - input_shape (:obj:`torch.Size`): arrange as (H, W). - - Returns: - Tuple[int]: The padding size along the - original H and W directions - """ - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - """Add padding to `x` - - Args: - x (Tensor): Input tensor has shape (B, C, H, W). - - Returns: - Tensor: The tensor with adaptive padding - """ - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The type of convolution - to generate patch embedding. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int): The slide stride of embedding conv. - Default: 16. - padding (int | tuple | string): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only works when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=16, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super(PatchEmbed, self).__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adaptive_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # e.g. when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adaptive_padding: - pad_h, pad_w = self.adaptive_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adaptive_padding: - x = self.adaptive_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map ((used in Swin Transformer)). - Our implementation uses `nn.Unfold` to - merge patches, which is about 25% faster than the original - implementation. However, we need to modify pretrained - models for compatibility. - - Args: - in_channels (int): The num of input channels. - to gets fully covered by filter and stride you specified. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adaptive_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - - if self.adaptive_padding: - x = self.adaptive_padding(x) - H, W = x.shape[-2:] - - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - x = self.sampler(x) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size - - -@ATTENTION.register_module() -class MultiheadAttention(BaseModule): - """A wrapper for ``torch.nn.MultiheadAttention``. - - This module implements MultiheadAttention with identity connection, - and positional encoding is also passed as input. - - Args: - embed_dims (int): The embedding dimension. - num_heads (int): Parallel attention heads. - attn_drop (float): A Dropout layer on attn_output_weights. - Default: 0.0. - proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. - Default: 0.0. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): When it is True, Key, Query and Value are shape of - (batch, n, embed_dim), otherwise (n, batch, embed_dim). - Default to False. - """ - - def __init__(self, - embed_dims, - num_heads, - attn_drop=0., - proj_drop=0., - dropout_layer=dict(type='Dropout', drop_prob=0.), - init_cfg=None, - batch_first=False, - **kwargs): - super(MultiheadAttention, self).__init__(init_cfg) - if 'dropout' in kwargs: - warnings.warn( - 'The arguments `dropout` in MultiheadAttention ' - 'has been deprecated, now you can separately ' - 'set `attn_drop`(float), proj_drop(float), ' - 'and `dropout_layer`(dict) ', DeprecationWarning) - attn_drop = kwargs['dropout'] - dropout_layer['drop_prob'] = kwargs.pop('dropout') - - self.embed_dims = embed_dims - self.num_heads = num_heads - self.batch_first = batch_first - - self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, - **kwargs) - - self.proj_drop = nn.Dropout(proj_drop) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else nn.Identity() - - @deprecated_api_warning({'residual': 'identity'}, - cls_name='MultiheadAttention') - def forward(self, - query, - key=None, - value=None, - identity=None, - query_pos=None, - key_pos=None, - attn_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `MultiheadAttention`. - - **kwargs allow passing a more general data flow when combining - with other operations in `transformerlayer`. - - Args: - query (Tensor): The input query with shape [num_queries, bs, - embed_dims] if self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - If None, the ``query`` will be used. Defaults to None. - value (Tensor): The value tensor with same shape as `key`. - Same in `nn.MultiheadAttention.forward`. Defaults to None. - If None, the `key` will be used. - identity (Tensor): This tensor, with the same shape as x, - will be used for the identity link. - If None, `x` will be used. Defaults to None. - query_pos (Tensor): The positional encoding for query, with - the same shape as `x`. If not None, it will - be added to `x` before forward function. Defaults to None. - key_pos (Tensor): The positional encoding for `key`, with the - same shape as `key`. Defaults to None. If not None, it will - be added to `key` before forward function. If None, and - `query_pos` has the same shape as `key`, then `query_pos` - will be used for `key_pos`. Defaults to None. - attn_mask (Tensor): ByteTensor mask with shape [num_queries, - num_keys]. Same in `nn.MultiheadAttention.forward`. - Defaults to None. - key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. - Defaults to None. - - Returns: - Tensor: forwarded results with shape - [num_queries, bs, embed_dims] - if self.batch_first is False, else - [bs, num_queries embed_dims]. - """ - - if key is None: - key = query - if value is None: - value = key - if identity is None: - identity = query - if key_pos is None: - if query_pos is not None: - # use query_pos if key_pos is not available - if query_pos.shape == key.shape: - key_pos = query_pos - else: - warnings.warn(f'position encoding of key is' - f'missing in {self.__class__.__name__}.') - if query_pos is not None: - query = query + query_pos - if key_pos is not None: - key = key + key_pos - - # Because the dataflow('key', 'query', 'value') of - # ``torch.nn.MultiheadAttention`` is (num_query, batch, - # embed_dims), We should adjust the shape of dataflow from - # batch_first (batch, num_query, embed_dims) to num_query_first - # (num_query ,batch, embed_dims), and recover ``attn_output`` - # from num_query_first to batch_first. - if self.batch_first: - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - out = self.attn( - query=query, - key=key, - value=value, - attn_mask=attn_mask, - key_padding_mask=key_padding_mask)[0] - - if self.batch_first: - out = out.transpose(0, 1) - - return identity + self.dropout_layer(self.proj_drop(out)) - - -@FEEDFORWARD_NETWORK.register_module() -class FFN(BaseModule): - """Implements feed-forward networks (FFNs) with identity connection. - - Args: - embed_dims (int): The feature dimension. Same as - `MultiheadAttention`. Defaults: 256. - feedforward_channels (int): The hidden dimension of FFNs. - Defaults: 1024. - num_fcs (int, optional): The number of fully-connected layers in - FFNs. Default: 2. - act_cfg (dict, optional): The activation config for FFNs. - Default: dict(type='ReLU') - ffn_drop (float, optional): Probability of an element to be - zeroed in FFN. Default 0.0. - add_identity (bool, optional): Whether to add the - identity connection. Default: `True`. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - @deprecated_api_warning( - { - 'dropout': 'ffn_drop', - 'add_residual': 'add_identity' - }, - cls_name='FFN') - def __init__(self, - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - act_cfg=dict(type='ReLU', inplace=True), - ffn_drop=0., - dropout_layer=None, - add_identity=True, - init_cfg=None, - **kwargs): - super(FFN, self).__init__(init_cfg) - assert num_fcs >= 2, 'num_fcs should be no less ' \ - f'than 2. got {num_fcs}.' - self.embed_dims = embed_dims - self.feedforward_channels = feedforward_channels - self.num_fcs = num_fcs - self.act_cfg = act_cfg - self.activate = build_activation_layer(act_cfg) - - layers = [] - in_channels = embed_dims - for _ in range(num_fcs - 1): - layers.append( - Sequential( - Linear(in_channels, feedforward_channels), self.activate, - nn.Dropout(ffn_drop))) - in_channels = feedforward_channels - layers.append(Linear(feedforward_channels, embed_dims)) - layers.append(nn.Dropout(ffn_drop)) - self.layers = Sequential(*layers) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else torch.nn.Identity() - self.add_identity = add_identity - - @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') - def forward(self, x, identity=None): - """Forward function for `FFN`. - - The function would add x to the output tensor if residue is None. - """ - out = self.layers(x) - if not self.add_identity: - return self.dropout_layer(out) - if identity is None: - identity = x - return identity + self.dropout_layer(out) - - -@TRANSFORMER_LAYER.register_module() -class BaseTransformerLayer(BaseModule): - """Base `TransformerLayer` for vision transformer. - - It can be built from `mmcv.ConfigDict` and support more flexible - customization, for example, using any number of `FFN or LN ` and - use different kinds of `attention` by specifying a list of `ConfigDict` - named `attn_cfgs`. It is worth mentioning that it supports `prenorm` - when you specifying `norm` as the first element of `operation_order`. - More details about the `prenorm`: `On Layer Normalization in the - Transformer Architecture `_ . - - Args: - attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for `self_attention` or `cross_attention` modules, - The order of the configs in the list should be consistent with - corresponding attentions in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. Default: None. - ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for FFN, The order of the configs in the list should be - consistent with corresponding ffn in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. - operation_order (tuple[str]): The execution order of operation - in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). - Support `prenorm` when you specifying first element as `norm`. - Default:None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): Key, Query and Value are shape - of (batch, n, embed_dim) - or (n, batch, embed_dim). Default to False. - """ - - def __init__(self, - attn_cfgs=None, - ffn_cfgs=dict( - type='FFN', - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - ffn_drop=0., - act_cfg=dict(type='ReLU', inplace=True), - ), - operation_order=None, - norm_cfg=dict(type='LN'), - init_cfg=None, - batch_first=False, - **kwargs): - - deprecated_args = dict( - feedforward_channels='feedforward_channels', - ffn_dropout='ffn_drop', - ffn_num_fcs='num_fcs') - for ori_name, new_name in deprecated_args.items(): - if ori_name in kwargs: - warnings.warn( - f'The arguments `{ori_name}` in BaseTransformerLayer ' - f'has been deprecated, now you should set `{new_name}` ' - f'and other FFN related arguments ' - f'to a dict named `ffn_cfgs`. ', DeprecationWarning) - ffn_cfgs[new_name] = kwargs[ori_name] - - super(BaseTransformerLayer, self).__init__(init_cfg) - - self.batch_first = batch_first - - assert set(operation_order) & set( - ['self_attn', 'norm', 'ffn', 'cross_attn']) == \ - set(operation_order), f'The operation_order of' \ - f' {self.__class__.__name__} should ' \ - f'contains all four operation type ' \ - f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" - - num_attn = operation_order.count('self_attn') + operation_order.count( - 'cross_attn') - if isinstance(attn_cfgs, dict): - attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] - else: - assert num_attn == len(attn_cfgs), f'The length ' \ - f'of attn_cfg {num_attn} is ' \ - f'not consistent with the number of attention' \ - f'in operation_order {operation_order}.' - - self.num_attn = num_attn - self.operation_order = operation_order - self.norm_cfg = norm_cfg - self.pre_norm = operation_order[0] == 'norm' - self.attentions = ModuleList() - - index = 0 - for operation_name in operation_order: - if operation_name in ['self_attn', 'cross_attn']: - if 'batch_first' in attn_cfgs[index]: - assert self.batch_first == attn_cfgs[index]['batch_first'] - else: - attn_cfgs[index]['batch_first'] = self.batch_first - attention = build_attention(attn_cfgs[index]) - # Some custom attentions used as `self_attn` - # or `cross_attn` can have different behavior. - attention.operation_name = operation_name - self.attentions.append(attention) - index += 1 - - self.embed_dims = self.attentions[0].embed_dims - - self.ffns = ModuleList() - num_ffns = operation_order.count('ffn') - if isinstance(ffn_cfgs, dict): - ffn_cfgs = ConfigDict(ffn_cfgs) - if isinstance(ffn_cfgs, dict): - ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] - assert len(ffn_cfgs) == num_ffns - for ffn_index in range(num_ffns): - if 'embed_dims' not in ffn_cfgs[ffn_index]: - ffn_cfgs[ffn_index]['embed_dims'] = self.embed_dims - else: - assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims - self.ffns.append( - build_feedforward_network(ffn_cfgs[ffn_index], - dict(type='FFN'))) - - self.norms = ModuleList() - num_norms = operation_order.count('norm') - for _ in range(num_norms): - self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) - - def forward(self, - query, - key=None, - value=None, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerDecoderLayer`. - - **kwargs contains some specific arguments of attentions. - - Args: - query (Tensor): The input query with shape - [num_queries, bs, embed_dims] if - self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - value (Tensor): The value tensor with same shape as `key`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor] | None): 2D Tensor used in - calculation of corresponding attention. The length of - it should equal to the number of `attention` in - `operation_order`. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in `self_attn` layer. - Defaults to None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: forwarded results with shape [num_queries, bs, embed_dims]. - """ - - norm_index = 0 - attn_index = 0 - ffn_index = 0 - identity = query - if attn_masks is None: - attn_masks = [None for _ in range(self.num_attn)] - elif isinstance(attn_masks, torch.Tensor): - attn_masks = [ - copy.deepcopy(attn_masks) for _ in range(self.num_attn) - ] - warnings.warn(f'Use same attn_mask in all attentions in ' - f'{self.__class__.__name__} ') - else: - assert len(attn_masks) == self.num_attn, f'The length of ' \ - f'attn_masks {len(attn_masks)} must be equal ' \ - f'to the number of attention in ' \ - f'operation_order {self.num_attn}' - - for layer in self.operation_order: - if layer == 'self_attn': - temp_key = temp_value = query - query = self.attentions[attn_index]( - query, - temp_key, - temp_value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=query_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=query_key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'norm': - query = self.norms[norm_index](query) - norm_index += 1 - - elif layer == 'cross_attn': - query = self.attentions[attn_index]( - query, - key, - value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=key_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'ffn': - query = self.ffns[ffn_index]( - query, identity if self.pre_norm else None) - ffn_index += 1 - - return query - - -@TRANSFORMER_LAYER_SEQUENCE.register_module() -class TransformerLayerSequence(BaseModule): - """Base class for TransformerEncoder and TransformerDecoder in vision - transformer. - - As base-class of Encoder and Decoder in vision transformer. - Support customization such as specifying different kind - of `transformer_layer` in `transformer_coder`. - - Args: - transformerlayer (list[obj:`mmcv.ConfigDict`] | - obj:`mmcv.ConfigDict`): Config of transformerlayer - in TransformerCoder. If it is obj:`mmcv.ConfigDict`, - it would be repeated `num_layer` times to a - list[`mmcv.ConfigDict`]. Default: None. - num_layers (int): The number of `TransformerLayer`. Default: None. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): - super(TransformerLayerSequence, self).__init__(init_cfg) - if isinstance(transformerlayers, dict): - transformerlayers = [ - copy.deepcopy(transformerlayers) for _ in range(num_layers) - ] - else: - assert isinstance(transformerlayers, list) and \ - len(transformerlayers) == num_layers - self.num_layers = num_layers - self.layers = ModuleList() - for i in range(num_layers): - self.layers.append(build_transformer_layer(transformerlayers[i])) - self.embed_dims = self.layers[0].embed_dims - self.pre_norm = self.layers[0].pre_norm - - def forward(self, - query, - key, - value, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerCoder`. - - Args: - query (Tensor): Input query with shape - `(num_queries, bs, embed_dims)`. - key (Tensor): The key tensor with shape - `(num_keys, bs, embed_dims)`. - value (Tensor): The value tensor with shape - `(num_keys, bs, embed_dims)`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor], optional): Each element is 2D Tensor - which is used in calculation of corresponding attention in - operation_order. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in self-attention - Default: None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: results with shape [num_queries, bs, embed_dims]. - """ - for layer in self.layers: - query = layer( - query, - key, - value, - query_pos=query_pos, - key_pos=key_pos, - attn_masks=attn_masks, - query_key_padding_mask=query_key_padding_mask, - key_padding_mask=key_padding_mask, - **kwargs) - return query diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/upsample.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/upsample.py deleted file mode 100644 index 0fd21fbf9..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/upsample.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import xavier_init -from .registry import UPSAMPLE_LAYERS - -UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) -UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) - - -@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') -class PixelShufflePack(nn.Module): - """Pixel Shuffle upsample layer. - - This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to - achieve a simple upsampling with pixel shuffle. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of the conv layer to expand the - channels. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super(PixelShufflePack, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - xavier_init(self.upsample_conv, distribution='uniform') - - def forward(self, x): - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x - - -def build_upsample_layer(cfg, *args, **kwargs): - """Build upsample layer. - - Args: - cfg (dict): The upsample layer config, which should contain: - - - type (str): Layer type. - - scale_factor (int): Upsample ratio, which is not applicable to - deconv. - - layer args: Args needed to instantiate a upsample layer. - args (argument list): Arguments passed to the ``__init__`` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the - ``__init__`` method of the corresponding conv layer. - - Returns: - nn.Module: Created upsample layer. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - raise KeyError( - f'the cfg dict must contain the key "type", but got {cfg}') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in UPSAMPLE_LAYERS: - raise KeyError(f'Unrecognized upsample type {layer_type}') - else: - upsample = UPSAMPLE_LAYERS.get(layer_type) - - if upsample is nn.Upsample: - cfg_['mode'] = layer_type - layer = upsample(*args, **kwargs, **cfg_) - return layer diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/wrappers.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/wrappers.py deleted file mode 100644 index 8aebf67bf..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/bricks/wrappers.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 - -Wrap some nn modules to support empty tensor input. Currently, these wrappers -are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask -heads are trained on only positive RoIs. -""" -import math - -import torch -import torch.nn as nn -from torch.nn.modules.utils import _pair, _triple - -from .registry import CONV_LAYERS, UPSAMPLE_LAYERS - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def obsolete_torch_version(torch_version, version_threshold): - return torch_version == 'parrots' or torch_version <= version_threshold - - -class NewEmptyTensorOp(torch.autograd.Function): - - @staticmethod - def forward(ctx, x, new_shape): - ctx.shape = x.shape - return x.new_empty(new_shape) - - @staticmethod - def backward(ctx, grad): - shape = ctx.shape - return NewEmptyTensorOp.apply(grad, shape), None - - -@CONV_LAYERS.register_module('Conv', force=True) -class Conv2d(nn.Conv2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module('Conv3d', force=True) -class Conv3d(nn.Conv3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv') -@UPSAMPLE_LAYERS.register_module('deconv', force=True) -class ConvTranspose2d(nn.ConvTranspose2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv3d') -@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) -class ConvTranspose3d(nn.ConvTranspose3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -class MaxPool2d(nn.MaxPool2d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), - _pair(self.padding), _pair(self.stride), - _pair(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class MaxPool3d(nn.MaxPool3d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), - _triple(self.padding), - _triple(self.stride), - _triple(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class Linear(torch.nn.Linear): - - def forward(self, x): - # empty tensor forward of Linear layer is supported in Pytorch 1.6 - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): - out_shape = [x.shape[0], self.out_features] - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/builder.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/builder.py deleted file mode 100644 index 7567316c5..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/builder.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..runner import Sequential -from ..utils import Registry, build_from_cfg - - -def build_model_from_cfg(cfg, registry, default_args=None): - """Build a PyTorch model from config dict(s). Different from - ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. - - Args: - cfg (dict, list[dict]): The config of modules, is is either a config - dict or a list of config dicts. If cfg is a list, a - the built modules will be wrapped with ``nn.Sequential``. - registry (:obj:`Registry`): A registry the module belongs to. - default_args (dict, optional): Default arguments to build the module. - Defaults to None. - - Returns: - nn.Module: A built nn module. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return Sequential(*modules) - else: - return build_from_cfg(cfg, registry, default_args) - - -MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/__init__.py deleted file mode 100644 index a263e31c1..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .flops_counter import get_model_complexity_info -from .fuse_conv_bn import fuse_conv_bn -from .sync_bn import revert_sync_batchnorm -from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, - KaimingInit, NormalInit, PretrainedInit, - TruncNormalInit, UniformInit, XavierInit, - bias_init_with_prob, caffe2_xavier_init, - constant_init, initialize, kaiming_init, normal_init, - trunc_normal_init, uniform_init, xavier_init) - -__all__ = [ - 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', - 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', - 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'revert_sync_batchnorm' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/flops_counter.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/flops_counter.py deleted file mode 100644 index a6045db84..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/flops_counter.py +++ /dev/null @@ -1,599 +0,0 @@ -# Modified from flops-counter.pytorch by Vladislav Sovrasov -# original repo: https://github.com/sovrasov/flops-counter.pytorch - -# MIT License - -# Copyright (c) 2018 Vladislav Sovrasov - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -import warnings -from functools import partial - -import numpy as np -import torch -import torch.nn as nn - -import mmcv - - -def get_model_complexity_info(model, - input_shape, - print_per_layer_stat=True, - as_strings=True, - input_constructor=None, - flush=False, - ost=sys.stdout): - """Get complexity information of a model. - - This method can calculate FLOPs and parameter counts of a model with - corresponding input shape. It can also print complexity information for - each layer in a model. - - Supported layers are listed as below: - - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. - - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, - ``nn.LeakyReLU``, ``nn.ReLU6``. - - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, - ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, - ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, - ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, - ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. - - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, - ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, - ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. - - Linear: ``nn.Linear``. - - Deconvolution: ``nn.ConvTranspose2d``. - - Upsample: ``nn.Upsample``. - - Args: - model (nn.Module): The model for complexity calculation. - input_shape (tuple): Input shape used for calculation. - print_per_layer_stat (bool): Whether to print complexity information - for each layer in a model. Default: True. - as_strings (bool): Output FLOPs and params counts in a string form. - Default: True. - input_constructor (None | callable): If specified, it takes a callable - method that generates input. otherwise, it will generate a random - tensor with input shape to calculate FLOPs. Default: None. - flush (bool): same as that in :func:`print`. Default: False. - ost (stream): same as ``file`` param in :func:`print`. - Default: sys.stdout. - - Returns: - tuple[float | str]: If ``as_strings`` is set to True, it will return - FLOPs and parameter counts in a string format. otherwise, it will - return those in a float number format. - """ - assert type(input_shape) is tuple - assert len(input_shape) >= 1 - assert isinstance(model, nn.Module) - flops_model = add_flops_counting_methods(model) - flops_model.eval() - flops_model.start_flops_count() - if input_constructor: - input = input_constructor(input_shape) - _ = flops_model(**input) - else: - try: - batch = torch.ones(()).new_empty( - (1, *input_shape), - dtype=next(flops_model.parameters()).dtype, - device=next(flops_model.parameters()).device) - except StopIteration: - # Avoid StopIteration for models which have no parameters, - # like `nn.Relu()`, `nn.AvgPool2d`, etc. - batch = torch.ones(()).new_empty((1, *input_shape)) - - _ = flops_model(batch) - - flops_count, params_count = flops_model.compute_average_flops_cost() - if print_per_layer_stat: - print_model_with_flops( - flops_model, flops_count, params_count, ost=ost, flush=flush) - flops_model.stop_flops_count() - - if as_strings: - return flops_to_string(flops_count), params_to_string(params_count) - - return flops_count, params_count - - -def flops_to_string(flops, units='GFLOPs', precision=2): - """Convert FLOPs number into a string. - - Note that Here we take a multiply-add counts as one FLOP. - - Args: - flops (float): FLOPs number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', - 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically - choose the most suitable unit for FLOPs. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted FLOPs number with units. - - Examples: - >>> flops_to_string(1e9) - '1.0 GFLOPs' - >>> flops_to_string(2e5, 'MFLOPs') - '0.2 MFLOPs' - >>> flops_to_string(3e-9, None) - '3e-09 FLOPs' - """ - if units is None: - if flops // 10**9 > 0: - return str(round(flops / 10.**9, precision)) + ' GFLOPs' - elif flops // 10**6 > 0: - return str(round(flops / 10.**6, precision)) + ' MFLOPs' - elif flops // 10**3 > 0: - return str(round(flops / 10.**3, precision)) + ' KFLOPs' - else: - return str(flops) + ' FLOPs' - else: - if units == 'GFLOPs': - return str(round(flops / 10.**9, precision)) + ' ' + units - elif units == 'MFLOPs': - return str(round(flops / 10.**6, precision)) + ' ' + units - elif units == 'KFLOPs': - return str(round(flops / 10.**3, precision)) + ' ' + units - else: - return str(flops) + ' FLOPs' - - -def params_to_string(num_params, units=None, precision=2): - """Convert parameter number into a string. - - Args: - num_params (float): Parameter number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'M', - 'K' and ''. If set to None, it will automatically choose the most - suitable unit for Parameter number. Default: None. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted parameter number with units. - - Examples: - >>> params_to_string(1e9) - '1000.0 M' - >>> params_to_string(2e5) - '200.0 k' - >>> params_to_string(3e-9) - '3e-09' - """ - if units is None: - if num_params // 10**6 > 0: - return str(round(num_params / 10**6, precision)) + ' M' - elif num_params // 10**3: - return str(round(num_params / 10**3, precision)) + ' k' - else: - return str(num_params) - else: - if units == 'M': - return str(round(num_params / 10.**6, precision)) + ' ' + units - elif units == 'K': - return str(round(num_params / 10.**3, precision)) + ' ' + units - else: - return str(num_params) - - -def print_model_with_flops(model, - total_flops, - total_params, - units='GFLOPs', - precision=3, - ost=sys.stdout, - flush=False): - """Print a model with FLOPs for each layer. - - Args: - model (nn.Module): The model to be printed. - total_flops (float): Total FLOPs of the model. - total_params (float): Total parameter counts of the model. - units (str | None): Converted FLOPs units. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 3. - ost (stream): same as `file` param in :func:`print`. - Default: sys.stdout. - flush (bool): same as that in :func:`print`. Default: False. - - Example: - >>> class ExampleModel(nn.Module): - - >>> def __init__(self): - >>> super().__init__() - >>> self.conv1 = nn.Conv2d(3, 8, 3) - >>> self.conv2 = nn.Conv2d(8, 256, 3) - >>> self.conv3 = nn.Conv2d(256, 8, 3) - >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) - >>> self.flatten = nn.Flatten() - >>> self.fc = nn.Linear(8, 1) - - >>> def forward(self, x): - >>> x = self.conv1(x) - >>> x = self.conv2(x) - >>> x = self.conv3(x) - >>> x = self.avg_pool(x) - >>> x = self.flatten(x) - >>> x = self.fc(x) - >>> return x - - >>> model = ExampleModel() - >>> x = (3, 16, 16) - to print the complexity information state for each layer, you can use - >>> get_model_complexity_info(model, x) - or directly use - >>> print_model_with_flops(model, 4579784.0, 37361) - ExampleModel( - 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, - (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 - (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) - (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) - (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) - (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) - (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) - ) - """ - - def accumulate_params(self): - if is_supported_instance(self): - return self.__params__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_params() - return sum - - def accumulate_flops(self): - if is_supported_instance(self): - return self.__flops__ / model.__batch_counter__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_flops() - return sum - - def flops_repr(self): - accumulated_num_params = self.accumulate_params() - accumulated_flops_cost = self.accumulate_flops() - return ', '.join([ - params_to_string( - accumulated_num_params, units='M', precision=precision), - '{:.3%} Params'.format(accumulated_num_params / total_params), - flops_to_string( - accumulated_flops_cost, units=units, precision=precision), - '{:.3%} FLOPs'.format(accumulated_flops_cost / total_flops), - self.original_extra_repr() - ]) - - def add_extra_repr(m): - m.accumulate_flops = accumulate_flops.__get__(m) - m.accumulate_params = accumulate_params.__get__(m) - flops_extra_repr = flops_repr.__get__(m) - if m.extra_repr != flops_extra_repr: - m.original_extra_repr = m.extra_repr - m.extra_repr = flops_extra_repr - assert m.extra_repr != m.original_extra_repr - - def del_extra_repr(m): - if hasattr(m, 'original_extra_repr'): - m.extra_repr = m.original_extra_repr - del m.original_extra_repr - if hasattr(m, 'accumulate_flops'): - del m.accumulate_flops - - model.apply(add_extra_repr) - print(model, file=ost, flush=flush) - model.apply(del_extra_repr) - - -def get_model_parameters_number(model): - """Calculate parameter number of a model. - - Args: - model (nn.module): The model for parameter number calculation. - - Returns: - float: Parameter number of the model. - """ - num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - return num_params - - -def add_flops_counting_methods(net_main_module): - # adding additional methods to the existing module object, - # this is done this way so that each function has access to self object - net_main_module.start_flops_count = start_flops_count.__get__( - net_main_module) - net_main_module.stop_flops_count = stop_flops_count.__get__( - net_main_module) - net_main_module.reset_flops_count = reset_flops_count.__get__( - net_main_module) - net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # noqa: E501 - net_main_module) - - net_main_module.reset_flops_count() - - return net_main_module - - -def compute_average_flops_cost(self): - """Compute average FLOPs cost. - - A method to compute average FLOPs cost, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - - Returns: - float: Current mean flops consumption per image. - """ - batches_count = self.__batch_counter__ - flops_sum = 0 - for module in self.modules(): - if is_supported_instance(module): - flops_sum += module.__flops__ - params_sum = get_model_parameters_number(self) - return flops_sum / batches_count, params_sum - - -def start_flops_count(self): - """Activate the computation of mean flops consumption per image. - - A method to activate the computation of mean flops consumption per image. - which will be available after ``add_flops_counting_methods()`` is called on - a desired net object. It should be called before running the network. - """ - add_batch_counter_hook_function(self) - - def add_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - return - - else: - handle = module.register_forward_hook( - get_modules_mapping()[type(module)]) - - module.__flops_handle__ = handle - - self.apply(partial(add_flops_counter_hook_function)) - - -def stop_flops_count(self): - """Stop computing the mean flops consumption per image. - - A method to stop computing the mean flops consumption per image, which will - be available after ``add_flops_counting_methods()`` is called on a desired - net object. It can be called to pause the computation whenever. - """ - remove_batch_counter_hook_function(self) - self.apply(remove_flops_counter_hook_function) - - -def reset_flops_count(self): - """Reset statistics computed so far. - - A method to Reset computed statistics, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - """ - add_batch_counter_variables_or_reset(self) - self.apply(add_flops_counter_variable_or_reset) - - -# ---- Internal functions -def empty_flops_counter_hook(module, input, output): - module.__flops__ += 0 - - -def upsample_flops_counter_hook(module, input, output): - output_size = output[0] - batch_size = output_size.shape[0] - output_elements_count = batch_size - for val in output_size.shape[1:]: - output_elements_count *= val - module.__flops__ += int(output_elements_count) - - -def relu_flops_counter_hook(module, input, output): - active_elements_count = output.numel() - module.__flops__ += int(active_elements_count) - - -def linear_flops_counter_hook(module, input, output): - input = input[0] - output_last_dim = output.shape[ - -1] # pytorch checks dimensions, so here we don't care much - module.__flops__ += int(np.prod(input.shape) * output_last_dim) - - -def pool_flops_counter_hook(module, input, output): - input = input[0] - module.__flops__ += int(np.prod(input.shape)) - - -def norm_flops_counter_hook(module, input, output): - input = input[0] - - batch_flops = np.prod(input.shape) - if (getattr(module, 'affine', False) - or getattr(module, 'elementwise_affine', False)): - batch_flops *= 2 - module.__flops__ += int(batch_flops) - - -def deconv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - input_height, input_width = input.shape[2:] - - kernel_height, kernel_width = conv_module.kernel_size - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = ( - kernel_height * kernel_width * in_channels * filters_per_channel) - - active_elements_count = batch_size * input_height * input_width - overall_conv_flops = conv_per_position_flops * active_elements_count - bias_flops = 0 - if conv_module.bias is not None: - output_height, output_width = output.shape[2:] - bias_flops = out_channels * batch_size * output_height * output_width - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def conv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - output_dims = list(output.shape[2:]) - - kernel_dims = list(conv_module.kernel_size) - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = int( - np.prod(kernel_dims)) * in_channels * filters_per_channel - - active_elements_count = batch_size * int(np.prod(output_dims)) - - overall_conv_flops = conv_per_position_flops * active_elements_count - - bias_flops = 0 - - if conv_module.bias is not None: - - bias_flops = out_channels * active_elements_count - - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def batch_counter_hook(module, input, output): - batch_size = 1 - if len(input) > 0: - # Can have multiple inputs, getting the first one - input = input[0] - batch_size = len(input) - else: - warnings.warn('No positional inputs found for a module, ' - 'assuming batch size is 1.') - module.__batch_counter__ += batch_size - - -def add_batch_counter_variables_or_reset(module): - - module.__batch_counter__ = 0 - - -def add_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - return - - handle = module.register_forward_hook(batch_counter_hook) - module.__batch_counter_handle__ = handle - - -def remove_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - module.__batch_counter_handle__.remove() - del module.__batch_counter_handle__ - - -def add_flops_counter_variable_or_reset(module): - if is_supported_instance(module): - if hasattr(module, '__flops__') or hasattr(module, '__params__'): - warnings.warn('variables __flops__ or __params__ are already ' - 'defined for the module' + type(module).__name__ + - ' ptflops can affect your code!') - module.__flops__ = 0 - module.__params__ = get_model_parameters_number(module) - - -def is_supported_instance(module): - if type(module) in get_modules_mapping(): - return True - return False - - -def remove_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - module.__flops_handle__.remove() - del module.__flops_handle__ - - -def get_modules_mapping(): - return { - # convolutions - nn.Conv1d: conv_flops_counter_hook, - nn.Conv2d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, - nn.Conv3d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, - # activations - nn.ReLU: relu_flops_counter_hook, - nn.PReLU: relu_flops_counter_hook, - nn.ELU: relu_flops_counter_hook, - nn.LeakyReLU: relu_flops_counter_hook, - nn.ReLU6: relu_flops_counter_hook, - # poolings - nn.MaxPool1d: pool_flops_counter_hook, - nn.AvgPool1d: pool_flops_counter_hook, - nn.AvgPool2d: pool_flops_counter_hook, - nn.MaxPool2d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, - nn.MaxPool3d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, - nn.AvgPool3d: pool_flops_counter_hook, - nn.AdaptiveMaxPool1d: pool_flops_counter_hook, - nn.AdaptiveAvgPool1d: pool_flops_counter_hook, - nn.AdaptiveMaxPool2d: pool_flops_counter_hook, - nn.AdaptiveAvgPool2d: pool_flops_counter_hook, - nn.AdaptiveMaxPool3d: pool_flops_counter_hook, - nn.AdaptiveAvgPool3d: pool_flops_counter_hook, - # normalizations - nn.BatchNorm1d: norm_flops_counter_hook, - nn.BatchNorm2d: norm_flops_counter_hook, - nn.BatchNorm3d: norm_flops_counter_hook, - nn.GroupNorm: norm_flops_counter_hook, - nn.InstanceNorm1d: norm_flops_counter_hook, - nn.InstanceNorm2d: norm_flops_counter_hook, - nn.InstanceNorm3d: norm_flops_counter_hook, - nn.LayerNorm: norm_flops_counter_hook, - # FC - nn.Linear: linear_flops_counter_hook, - mmcv.cnn.bricks.Linear: linear_flops_counter_hook, - # Upscale - nn.Upsample: upsample_flops_counter_hook, - # Deconvolution - nn.ConvTranspose2d: deconv_flops_counter_hook, - mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, - } diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/fuse_conv_bn.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/fuse_conv_bn.py deleted file mode 100644 index cb7076f80..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/fuse_conv_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def _fuse_conv_bn(conv, bn): - """Fuse conv and bn into one module. - - Args: - conv (nn.Module): Conv to be fused. - bn (nn.Module): BN to be fused. - - Returns: - nn.Module: Fused module. - """ - conv_w = conv.weight - conv_b = conv.bias if conv.bias is not None else torch.zeros_like( - bn.running_mean) - - factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) - conv.weight = nn.Parameter(conv_w * - factor.reshape([conv.out_channels, 1, 1, 1])) - conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) - return conv - - -def fuse_conv_bn(module): - """Recursively fuse conv and bn in a module. - - During inference, the functionary of batch norm layers is turned off - but only the mean and var alone channels are used, which exposes the - chance to fuse it with the preceding conv layers to save computations and - simplify network structures. - - Args: - module (nn.Module): Module to be fused. - - Returns: - nn.Module: Fused module. - """ - last_conv = None - last_conv_name = None - - for name, child in module.named_children(): - if isinstance(child, - (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): - if last_conv is None: # only fuse BN that is after Conv - continue - fused_conv = _fuse_conv_bn(last_conv, child) - module._modules[last_conv_name] = fused_conv - # To reduce changes, set BN as Identity instead of deleting it. - module._modules[name] = nn.Identity() - last_conv = None - elif isinstance(child, nn.Conv2d): - last_conv = child - last_conv_name = name - else: - fuse_conv_bn(child) - return module diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/sync_bn.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/sync_bn.py deleted file mode 100644 index 0c52526e9..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/sync_bn.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -import mmcv - - -class _BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): - """A general BatchNorm layer without input dimension check. - - Reproduced from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc - is `_check_input_dim` that is designed for tensor sanity checks. - The check has been bypassed in this class for the convenience of converting - SyncBatchNorm. - """ - - def _check_input_dim(self, input): - return - - -def revert_sync_batchnorm(module): - """Helper function to convert all `SyncBatchNorm` (SyncBN) and - `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to - `BatchNormXd` layers. - - Adapted from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - - Args: - module (nn.Module): The module containing `SyncBatchNorm` layers. - - Returns: - module_output: The converted module with `BatchNormXd` layers. - """ - module_output = module - module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] - if hasattr(mmcv, 'ops'): - module_checklist.append(mmcv.ops.SyncBatchNorm) - if isinstance(module, tuple(module_checklist)): - module_output = _BatchNormXd(module.num_features, module.eps, - module.momentum, module.affine, - module.track_running_stats) - if module.affine: - # no_grad() may not be needed here but - # just to be consistent with `convert_sync_batchnorm()` - with torch.no_grad(): - module_output.weight = module.weight - module_output.bias = module.bias - module_output.running_mean = module.running_mean - module_output.running_var = module.running_var - module_output.num_batches_tracked = module.num_batches_tracked - module_output.training = module.training - # qconfig exists in quantized models - if hasattr(module, 'qconfig'): - module_output.qconfig = module.qconfig - for name, child in module.named_children(): - module_output.add_module(name, revert_sync_batchnorm(child)) - del module - return module_output diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/weight_init.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/weight_init.py deleted file mode 100644 index 0ac08c87f..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/cnn/utils/weight_init.py +++ /dev/null @@ -1,685 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings - -import numpy as np -import torch -import torch.nn as nn -from torch import Tensor - -from mmcv.utils import Registry, build_from_cfg, get_logger, print_log - -INITIALIZERS = Registry('initializer') - - -def update_init_info(module, init_info): - """Update the `_params_init_info` in the module if the value of parameters - are changed. - - Args: - module (obj:`nn.Module`): The module of PyTorch with a user-defined - attribute `_params_init_info` which records the initialization - information. - init_info (str): The string that describes the initialization. - """ - assert hasattr( - module, - '_params_init_info'), f'Can not find `_params_init_info` in {module}' - for name, param in module.named_parameters(): - - assert param in module._params_init_info, ( - f'Find a new :obj:`Parameter` ' - f'named `{name}` during executing the ' - f'`init_weights` of ' - f'`{module.__class__.__name__}`. ' - f'Please do not add or ' - f'replace parameters during executing ' - f'the `init_weights`. ') - - # The parameter has been changed during executing the - # `init_weights` of module - mean_value = param.data.mean() - if module._params_init_info[param]['tmp_mean_value'] != mean_value: - module._params_init_info[param]['init_info'] = init_info - module._params_init_info[param]['tmp_mean_value'] = mean_value - - -def constant_init(module, val, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.constant_(module.weight, val) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def xavier_init(module, gain=1, bias=0, distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.xavier_uniform_(module.weight, gain=gain) - else: - nn.init.xavier_normal_(module.weight, gain=gain) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def normal_init(module, mean=0, std=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.normal_(module.weight, mean, std) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def trunc_normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - trunc_normal_(module.weight, mean, std, a, b) # type: ignore - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) # type: ignore - - -def uniform_init(module, a=0, b=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.uniform_(module.weight, a, b) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def kaiming_init(module, - a=0, - mode='fan_out', - nonlinearity='relu', - bias=0, - distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.kaiming_uniform_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - else: - nn.init.kaiming_normal_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def caffe2_xavier_init(module, bias=0): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - kaiming_init( - module, - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - bias=bias, - distribution='uniform') - - -def bias_init_with_prob(prior_prob): - """initialize conv/fc bias value according to a given probability value.""" - bias_init = float(-np.log((1 - prior_prob) / prior_prob)) - return bias_init - - -def _get_bases_name(m): - return [b.__name__ for b in m.__class__.__bases__] - - -class BaseInit(object): - - def __init__(self, *, bias=0, bias_prob=None, layer=None): - self.wholemodule = False - if not isinstance(bias, (int, float)): - raise TypeError(f'bias must be a number, but got a {type(bias)}') - - if bias_prob is not None: - if not isinstance(bias_prob, float): - raise TypeError(f'bias_prob type must be float, \ - but got {type(bias_prob)}') - - if layer is not None: - if not isinstance(layer, (str, list)): - raise TypeError(f'layer must be a str or a list of str, \ - but got a {type(layer)}') - else: - layer = [] - - if bias_prob is not None: - self.bias = bias_init_with_prob(bias_prob) - else: - self.bias = bias - self.layer = [layer] if isinstance(layer, str) else layer - - def _get_init_info(self): - info = f'{self.__class__.__name__}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Constant') -class ConstantInit(BaseInit): - """Initialize module parameters with constant values. - - Args: - val (int | float): the value to fill the weights in the module with - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, val, **kwargs): - super().__init__(**kwargs) - self.val = val - - def __call__(self, module): - - def init(m): - if self.wholemodule: - constant_init(m, self.val, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - constant_init(m, self.val, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Xavier') -class XavierInit(BaseInit): - r"""Initialize module parameters with values according to the method - described in `Understanding the difficulty of training deep feedforward - neural networks - Glorot, X. & Bengio, Y. (2010). - `_ - - Args: - gain (int | float): an optional scaling factor. Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` - or ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, gain=1, distribution='normal', **kwargs): - super().__init__(**kwargs) - self.gain = gain - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - xavier_init(m, self.gain, self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - xavier_init(m, self.gain, self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: gain={self.gain}, ' \ - f'distribution={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Normal') -class NormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. - - Args: - mean (int | float):the mean of the normal distribution. Defaults to 0. - std (int | float): the standard deviation of the normal distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, mean=0, std=1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.std = std - - def __call__(self, module): - - def init(m): - if self.wholemodule: - normal_init(m, self.mean, self.std, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - normal_init(m, self.mean, self.std, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: mean={self.mean},' \ - f' std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='TruncNormal') -class TruncNormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values - outside :math:`[a, b]`. - - Args: - mean (float): the mean of the normal distribution. Defaults to 0. - std (float): the standard deviation of the normal distribution. - Defaults to 1. - a (float): The minimum cutoff value. - b ( float): The maximum cutoff value. - bias (float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - **kwargs) -> None: - super().__init__(**kwargs) - self.mean = mean - self.std = std - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ - f' mean={self.mean}, std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Uniform') -class UniformInit(BaseInit): - r"""Initialize module parameters with values drawn from the uniform - distribution :math:`\mathcal{U}(a, b)`. - - Args: - a (int | float): the lower bound of the uniform distribution. - Defaults to 0. - b (int | float): the upper bound of the uniform distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, a=0, b=1, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def __call__(self, module): - - def init(m): - if self.wholemodule: - uniform_init(m, self.a, self.b, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - uniform_init(m, self.a, self.b, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a},' \ - f' b={self.b}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Kaiming') -class KaimingInit(BaseInit): - r"""Initialize module parameters with the values according to the method - described in `Delving deep into rectifiers: Surpassing human-level - performance on ImageNet classification - He, K. et al. (2015). - `_ - - Args: - a (int | float): the negative slope of the rectifier used after this - layer (only used with ``'leaky_relu'``). Defaults to 0. - mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing - ``'fan_in'`` preserves the magnitude of the variance of the weights - in the forward pass. Choosing ``'fan_out'`` preserves the - magnitudes in the backwards pass. Defaults to ``'fan_out'``. - nonlinearity (str): the non-linear function (`nn.functional` name), - recommended to use only with ``'relu'`` or ``'leaky_relu'`` . - Defaults to 'relu'. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` or - ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - a=0, - mode='fan_out', - nonlinearity='relu', - distribution='normal', - **kwargs): - super().__init__(**kwargs) - self.a = a - self.mode = mode - self.nonlinearity = nonlinearity - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ - f'nonlinearity={self.nonlinearity}, ' \ - f'distribution ={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Caffe2Xavier') -class Caffe2XavierInit(KaimingInit): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - def __init__(self, **kwargs): - super().__init__( - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='uniform', - **kwargs) - - def __call__(self, module): - super().__call__(module) - - -@INITIALIZERS.register_module(name='Pretrained') -class PretrainedInit(object): - """Initialize module by loading a pretrained model. - - Args: - checkpoint (str): the checkpoint file of the pretrained model should - be load. - prefix (str, optional): the prefix of a sub-module in the pretrained - model. it is for loading a part of the pretrained model to - initialize. For example, if we would like to only load the - backbone of a detector model, we can set ``prefix='backbone.'``. - Defaults to None. - map_location (str): map tensors into proper locations. - """ - - def __init__(self, checkpoint, prefix=None, map_location=None): - self.checkpoint = checkpoint - self.prefix = prefix - self.map_location = map_location - - def __call__(self, module): - from mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, - load_state_dict) - logger = get_logger('mmcv') - if self.prefix is None: - print_log(f'load model from: {self.checkpoint}', logger=logger) - load_checkpoint( - module, - self.checkpoint, - map_location=self.map_location, - strict=False, - logger=logger) - else: - print_log( - f'load {self.prefix} in model from: {self.checkpoint}', - logger=logger) - state_dict = _load_checkpoint_with_prefix( - self.prefix, self.checkpoint, map_location=self.map_location) - load_state_dict(module, state_dict, strict=False, logger=logger) - - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: load from {self.checkpoint}' - return info - - -def _initialize(module, cfg, wholemodule=False): - func = build_from_cfg(cfg, INITIALIZERS) - # wholemodule flag is for override mode, there is no layer key in override - # and initializer will give init values for the whole module with the name - # in override. - func.wholemodule = wholemodule - func(module) - - -def _initialize_override(module, override, cfg): - if not isinstance(override, (dict, list)): - raise TypeError(f'override must be a dict or a list of dict, \ - but got {type(override)}') - - override = [override] if isinstance(override, dict) else override - - for override_ in override: - - cp_override = copy.deepcopy(override_) - name = cp_override.pop('name', None) - if name is None: - raise ValueError('`override` must contain the key "name",' - f'but got {cp_override}') - # if override only has name key, it means use args in init_cfg - if not cp_override: - cp_override.update(cfg) - # if override has name key and other args except type key, it will - # raise error - elif 'type' not in cp_override.keys(): - raise ValueError( - f'`override` need "type" key, but got {cp_override}') - - if hasattr(module, name): - _initialize(getattr(module, name), cp_override, wholemodule=True) - else: - raise RuntimeError(f'module did not have attribute {name}, ' - f'but init_cfg is {cp_override}.') - - -def initialize(module, init_cfg): - r"""Initialize a module. - - Args: - module (``torch.nn.Module``): the module will be initialized. - init_cfg (dict | list[dict]): initialization configuration dict to - define initializer. OpenMMLab has implemented 6 initializers - including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, - ``Kaiming``, and ``Pretrained``. - - Example: - >>> module = nn.Linear(2, 3, bias=True) - >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) - >>> initialize(module, init_cfg) - - >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) - >>> # define key ``'layer'`` for initializing layer with different - >>> # configuration - >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), - dict(type='Constant', layer='Linear', val=2)] - >>> initialize(module, init_cfg) - - >>> # define key``'override'`` to initialize some specific part in - >>> # module - >>> class FooNet(nn.Module): - >>> def __init__(self): - >>> super().__init__() - >>> self.feat = nn.Conv2d(3, 16, 3) - >>> self.reg = nn.Conv2d(16, 10, 3) - >>> self.cls = nn.Conv2d(16, 5, 3) - >>> model = FooNet() - >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', - >>> override=dict(type='Constant', name='reg', val=3, bias=4)) - >>> initialize(model, init_cfg) - - >>> model = ResNet(depth=50) - >>> # Initialize weights with the pretrained model. - >>> init_cfg = dict(type='Pretrained', - checkpoint='torchvision://resnet50') - >>> initialize(model, init_cfg) - - >>> # Initialize weights of a sub-module with the specific part of - >>> # a pretrained model by using "prefix". - >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ - >>> 'retinanet_r50_fpn_1x_coco/'\ - >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' - >>> init_cfg = dict(type='Pretrained', - checkpoint=url, prefix='backbone.') - """ - if not isinstance(init_cfg, (dict, list)): - raise TypeError(f'init_cfg must be a dict or a list of dict, \ - but got {type(init_cfg)}') - - if isinstance(init_cfg, dict): - init_cfg = [init_cfg] - - for cfg in init_cfg: - # should deeply copy the original config because cfg may be used by - # other modules, e.g., one init_cfg shared by multiple bottleneck - # blocks, the expected cfg will be changed after pop and will change - # the initialization behavior of other modules - cp_cfg = copy.deepcopy(cfg) - override = cp_cfg.pop('override', None) - _initialize(module, cp_cfg) - - if override is not None: - cp_cfg.pop('layer', None) - _initialize_override(module, override, cp_cfg) - else: - # All attributes in module have same initialization. - pass - - -def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, - b: float) -> Tensor: - # Method based on - # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf - # Modified from - # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - def norm_cdf(x): - # Computes standard normal cumulative distribution function - return (1. + math.erf(x / math.sqrt(2.))) / 2. - - if (mean < a - 2 * std) or (mean > b + 2 * std): - warnings.warn( - 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' - 'The distribution of values may be incorrect.', - stacklevel=2) - - with torch.no_grad(): - # Values are generated by using a truncated uniform distribution and - # then using the inverse CDF for the normal distribution. - # Get upper and lower cdf values - lower = norm_cdf((a - mean) / std) - upper = norm_cdf((b - mean) / std) - - # Uniformly fill tensor with values from [lower, upper], then translate - # to [2lower-1, 2upper-1]. - tensor.uniform_(2 * lower - 1, 2 * upper - 1) - - # Use inverse cdf transform for normal distribution to get truncated - # standard normal - tensor.erfinv_() - - # Transform to proper mean, std - tensor.mul_(std * math.sqrt(2.)) - tensor.add_(mean) - - # Clamp to ensure it's in the proper range - tensor.clamp_(min=a, max=b) - return tensor - - -def trunc_normal_(tensor: Tensor, - mean: float = 0., - std: float = 1., - a: float = -2., - b: float = 2.) -> Tensor: - r"""Fills the input Tensor with values drawn from a truncated - normal distribution. The values are effectively drawn from the - normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` - with values outside :math:`[a, b]` redrawn until they are within - the bounds. The method used for generating the random values works - best when :math:`a \leq \text{mean} \leq b`. - - Modified from - https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - - Args: - tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. - mean (float): the mean of the normal distribution. - std (float): the standard deviation of the normal distribution. - a (float): the minimum cutoff value. - b (float): the maximum cutoff value. - """ - return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/device/__init__.py deleted file mode 100644 index 6ac55e63b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from . import ipu, mlu - -__all__ = ['mlu', 'ipu'] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/__init__.py deleted file mode 100755 index d550865ad..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import IPUFp16OptimizerHook - from .model_wrapper import ipu_model_wrapper - from .runner import IPUBaseRunner, IPUEpochBasedRunner, IPUIterBasedRunner - from .utils import cfg2options - __all__ = [ - 'cfg2options', 'ipu_model_wrapper', 'IPUFp16OptimizerHook', - 'IPUDataLoader', 'IPUBaseRunner', 'IPUEpochBasedRunner', - 'IPUIterBasedRunner' - ] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/dataloader.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/dataloader.py deleted file mode 100755 index 1485df2f3..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/dataloader.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence -from functools import partial - -import poptorch -from torch.utils.data.dataloader import default_collate - -from mmcv.parallel import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Put each data field into a tensor/DataContainer with outer dimension - batch size. - - TODO support for - :type:`~mmcv.parallel.DataContainer`. Currently, it will be ignored. - There are 3 cases. - - 1. cpu_only = True, e.g., meta data. - 2. cpu_only = False, stack = True, e.g., images tensors. - 3. cpu_only = False, stack = False, e.g., gt bboxes. - """ - - if not isinstance(batch, Sequence): - raise TypeError( - f'`batch` should be a sequence, but got {type(batch)}.') - - if isinstance(batch[0], DataContainer): - # TODO `DataContainer` will be supported in the future. - raise TypeError('DataContainer is not supported in ipu data loader.') - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - collated_batch = [] - for samples in transposed: - if not isinstance(samples[0], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch.append(collate(samples, samples_per_gpu)) - return collated_batch - elif isinstance(batch[0], Mapping): - collated_batch = {} - for key in batch[0]: - if not isinstance(batch[0][key], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch[key] = collate([d[key] for d in batch]) - return collated_batch - else: - return default_collate(batch) - - -class IPUDataLoader(poptorch.DataLoader): - """Thin wrapper of `torch.utils.data.DataLoader`. - - Compared with the pytorch DataLoder, this DataLoder changes the way of - calculation of batch size and adds the AsynchronousDataAccessor to - load and release data faster in cpu mode. - - If this data loader is used in a distributed execution environment, it will - ensure that each process uses a different subset of the dataset, providing - you first call ``options.randomSeed(N)`` with an integer N which is the - same across all hosts. - - Args: - dataset (torch.utils.data.Dataset): The dataset to get the data from. - options (poptorch.Options): Options that will be used to compile - and run the model. - batch_size (int, optional): This is the batch size in the conventional - sense of being the size that runs through an operation in the model - at any given time. - shuffle (bool, optional): set to ``True`` to have the data reshuffled - at every epoch (default: ``False``). - num_workers (int, optional): how many subprocesses to use for data - loading. ``0`` means that the data will be loaded in the main - process. (default: ``0``) - drop_last (bool, optional): If True and the number of elements in the - dataset is not a multiple of the combined batch size then the - incomplete batch at the end will be dropped. - persistent_workers (bool, optional): Re-use workers between - iterations if True. - auto_distributed_partitioning (bool, optional): If True, partitions the - dataset for distributed execution automatically. Otherwise, it is - assumed that partitioning has been handled manually. - mode (poptorch.DataLoaderMode, optional): If `DataLoaderMode.Async`, - uses an :py:class:`~poptorch.AsynchronousDataAccessor` to access - the dataset. If `DataLoaderMode.Sync`, accesses the dataset - synchronously. - async_options (Dict[str, Any], optional): Options to pass to - :py:class:`~poptorch.AsynchronousDataAccessor`. - rebatched_worker_size (int, optional): When using AsyncRebatched: batch - size of the tensors loaded by the workers. - Default to the combined batch size. - If specified the ``rebatched_worker_size`` must be less than - or equal to the combined batch size. - kwargs (Dict[str, Any], optional): Other options to pass to PyTorch's - ``DataLoader`` constructor. - """ - - def __init__(self, - dataset, - options, - batch_size=1, - shuffle=False, - num_workers=0, - drop_last=True, - persistent_workers=True, - auto_distributed_partitioning=True, - mode='sync', - async_options=None, - rebatched_worker_size=None, - **kwargs): - """Lazy init: - - In many frameworks, the dataloader will be constructed before the - initialization of the ipu options, so the lazy init method is used - here, and the real initialization will not be done until the dataloader - needs to be used and the options are input. - """ - # lazy init: sometimes, we cannot get IPU options when build data - # loader - self.kwargs = { - 'dataset': dataset, - 'batch_size': batch_size, - 'shuffle': shuffle, - 'num_workers': num_workers, - 'drop_last': drop_last, - 'persistent_workers': persistent_workers, - 'auto_distributed_partitioning': auto_distributed_partitioning, - 'mode': mode, - 'collate_fn': partial(collate, samples_per_gpu=batch_size), - 'async_options': async_options, - 'rebatched_worker_size': rebatched_worker_size, - **kwargs - } - self.dataset = dataset - self.initialized = False - if options: - self.init(options=options) - - def init(self, options, **kwargs): - if not self.initialized: - kwargs = {**self.kwargs, **kwargs, 'options': options} - if kwargs['mode'] == 'sync': - kwargs['mode'] = poptorch.DataLoaderMode.Sync - elif kwargs['mode'] == 'async': - kwargs['mode'] = poptorch.DataLoaderMode.AsyncRebatched - if kwargs['async_options'] is None: - kwargs['async_options'] = { - 'load_indefinitely': True, - 'buffer_size': 8 - } - if kwargs['rebatched_worker_size'] is None: - kwargs['rebatched_worker_size'] = 128 - super().__init__(**kwargs) - self.initialized = True - - return self diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/hierarchical_data_manager.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/hierarchical_data_manager.py deleted file mode 100755 index a6f3b3cd2..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/hierarchical_data_manager.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import numpy as np -import torch - -from mmcv.parallel import DataContainer - -# A customized None type for HierarchicalDataManager -HierarchicalDataNone = object() - - -class HierarchicalDataManager: - """A class manage all the tensors in the hierarchical data. - - At present, the input data structure accepted by IPU is limited, - when the input data structure of mmcv varies. - Here, an intermediate class is needed to get and update tensors - from the original data. - - HierarchicalDataManager will record a hierarchical input/output data in - self._hierarchical_data. For example, we have an input data: - {'img': tensorA, 'label': tensorB, 'img_metas': [tensorC, tensorD]} - To enable IPU to use the input, HierarchicalDataManager will collect - the torch tensors from self._hierarchical_data into a tuple like: - (tensorA, tensorB, tensorC, tensorD). - Meanwhile, the return of IPU is a tuple of tensors, HierarchicalDataManager - also have a function named update_all_tensors to update tensors in - self._hierarchical_data which is the output for upper calls. - - Args: - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - """ - - def __init__(self, logger=None): - self.atomic_types = (int, str, float, np.ndarray, type(None)) - self.warning = warnings.warn if logger is None else logger.warning - # enable or disable input data's shape and value check - self.quick_mode = False - self._hierarchical_data = None - - def quick(self): - self.quick_mode = True - - def compare_atomic_type(self, a, b): - """Compare data, supported datatypes are numpy array and python basic - types.""" - if isinstance(a, np.ndarray): - return np.all(a == b) - else: - return a == b - - def record_hierarchical_data(self, data): - """Record a hierarchical data.""" - if self._hierarchical_data is not None: - if isinstance(data, torch.Tensor): - assert isinstance(self._hierarchical_data, torch.Tensor), \ - 'original hierarchical data is not torch.tensor' - self._hierarchical_data = data - else: - self.update_hierarchical_data(data) - else: - self._hierarchical_data = data - - @property - def hierarchical_data(self): - return self._hierarchical_data - - def update_hierarchical_data(self, - dataA, - dataB=HierarchicalDataNone, - strict=True, - address='data'): - """Update dataB with dataA in-place. - - Args: - dataA (list or dict or tuple): New hierarchical data. - dataB (list or dict or tuple): hierarchical data to update. - if not specified, self.hierarchical_data will be updated then. - strict (bool, optional): If true, an error will be reported - when the following conditions occur: - 1. Non-torch.Tensor data changed. - 2. Torch.Tensor data shape changed. - address (str): Record the address of current data to be updated. - Default: 'data'. - """ - if dataB is HierarchicalDataNone: - dataB = self.hierarchical_data - - # Update with a da ta with the same structure - # but different values(tensors and basic python data types) - if isinstance(dataA, (tuple, list)): - for idx, node in enumerate(dataA): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(idx)}]' - assert isinstance(node, type(dataB[idx])),\ - f'data structure changed: {new_address}' - if isinstance(node, torch.Tensor): - dataB[idx] = node - else: - self.update_hierarchical_data( - node, dataB[idx], strict, address=new_address) - elif isinstance(dataA, dict): - for k, v in dataA.items(): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(k)}]' - assert isinstance(v, type(dataB[k])),\ - f'data structure changed: {new_address}' - if isinstance(v, torch.Tensor): - dataB[k] = v - else: - self.update_hierarchical_data( - v, dataB[k], strict, address=new_address) - elif isinstance(dataA, self.atomic_types): - if not self.quick_mode: - is_equal = self.compare_atomic_type(dataA, dataB) - if not is_equal: - if strict: - raise ValueError( - 'all data except torch.Tensor should be same, ' - f'but data({address}) is changed.') - else: - self.warning( - f'find a non-torch.Tensor data({type(dataA)}) ' - f'changed, and the address is {address}') - elif isinstance(dataA, DataContainer): - if not self.quick_mode: - assert isinstance(dataB, DataContainer) - new_address = address + '.data' - self.update_hierarchical_data( - dataA.data, dataB.data, False, address=new_address) - else: - raise NotImplementedError( - f'not supported datatype:{type(dataA)}, address is {address}') - - def collect_all_tensors(self, hierarchical_data=None): - """Collect torch.Tensor data from self.hierarchical_data to a list and - return.""" - # get a list of tensor from self._hierarchical_data - if hierarchical_data is None: - hierarchical_data = self._hierarchical_data - tensors = [] - if isinstance(hierarchical_data, torch.Tensor): - tensors = [hierarchical_data] - else: - self._collect_tensors(hierarchical_data, tensors) - return tensors - - def _collect_tensors(self, data, tensors): - if isinstance(data, (tuple, list)): - for node in data: - if isinstance(node, torch.Tensor): - tensors.append(node) - else: - self._collect_tensors(node, tensors) - elif isinstance(data, dict): - for v in data.values(): - if isinstance(v, torch.Tensor): - tensors.append(v) - else: - self._collect_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._collect_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def update_all_tensors(self, tensors): - """Put tensors from tuple back to self.hierarchical_data.""" - if isinstance(self._hierarchical_data, torch.Tensor): - print(tensors, len(tensors)) - assert len(tensors) == 1 - assert isinstance(tensors[0], torch.Tensor) - self._hierarchical_data = tensors[0] - else: - # convert to list if tensors is tuple - tensors = list(tensors) - self._set_tensors(self._hierarchical_data, tensors) - return self.hierarchical_data - - def _set_tensors(self, data, tensors): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = tensors.pop(0) - else: - self._set_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._set_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def clean_all_tensors(self): - """Delete tensors from self.hierarchical_data.""" - self._clean_tensors(self._hierarchical_data) - - def _clean_tensors(self, data): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = None - else: - self._clean_tensors(v) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._clean_tensors(data.data) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/hook_wrapper.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/hook_wrapper.py deleted file mode 100755 index 141afb86d..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/hook_wrapper.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import HOOKS, LrUpdaterHook, OptimizerHook -from mmcv.utils import TORCH_VERSION, digit_version - - -def wrap_lr_updater_hook(lr_hook_class): - """A wrapper function to wrap any subclass of LrUpdaterHook. - - IPU needs extra operations to upload optimizer settings. This wrapper will - override function(_set_lr) of a subclass of LrUpdaterHook. - """ - assert issubclass(lr_hook_class, LrUpdaterHook) - - class ipu_lr_hook_class(lr_hook_class): - - def _set_lr(self, runner, *args, **kwargs): - super()._set_lr(runner, *args, **kwargs) - # convert torch optimizer to poptorch optimizer - runner.model.setOptimizer(runner.optimizer) - - return ipu_lr_hook_class - - -def wrap_optimizer_hook(optimizer_hook_class): - """A wrapper function to wrap OptimizerHook. - - This is an non-intrusive implementation of wrapping optimizer hook (or you - need to change every config file to use IPU optimizer hook) IPU's clip-norm - implementation is different from pytorch, so there should be an error - raised when using clip-norm. - """ - - class ipu_optimizer_hook_class(OptimizerHook): - - def __init__(self, **kwargs): - super().__init__(**kwargs) - if self.grad_clip is not None: - raise NotImplementedError('IPU does not support gradient clip') - - return ipu_optimizer_hook_class - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class IPUFp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - assert grad_clip is None,\ - 'IPU mode does not support `grad_clip` currently' - assert coalesce,\ - 'implemented all reduce in distributed training currently' - assert bucket_size_mb == -1,\ - '`bucket_size_mb` should not be set in IPU mode' - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - raise NotImplementedError( - 'IPU mode does not support dynamic loss scale currently') - elif isinstance(loss_scale, float): - self.loss_scale = loss_scale - elif isinstance(loss_scale, dict): - raise NotImplementedError( - 'IPU mode supports single scale currently') - else: - raise ValueError( - f'loss_scale should be float, but got {loss_scale} ') - - def after_train_iter(self, runner): - pass - -else: - raise RuntimeError('The IPU mode only supports torch 1.6 and above') diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/model_wrapper.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/model_wrapper.py deleted file mode 100755 index c345537e2..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/model_wrapper.py +++ /dev/null @@ -1,721 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect -from collections import OrderedDict -from typing import Optional, Union - -import poptorch -import torch -import torch.nn as nn -from poptorch import PoplarExecutor, __version__, identity_loss -from poptorch._args_parser import ArgsParser - -from mmcv.runner import auto_fp16 -from .hierarchical_data_manager import HierarchicalDataManager -from .utils import compare_ndarray, model_sharding, recomputation_checkpoint - - -class DictArgsParser(ArgsParser): - """A helper class for handling model input. - - Args: - inputs (list): Inputs of model. - """ - - def __init__(self, inputs): - # Combine args and kwargs: - self._has_variadic_arguments = True - self._varnames = list(inputs.keys()) - self._defaults = [inspect.Parameter.empty for _ in self._varnames] - self._warned_not_contiguous_input = False - - -class WrappedNet(nn.Module): - """A net wrapper for model conversion. - - This wrapper will make some changes and add some extra functions to - training/inference model. - - Args: - model (:obj:`nn.Module`): The model to run. - inputs_manager (:obj:`HierarchicalDataManager`): A parser - converting inputs from tuple to dictionary. - outputs_manager (:obj:`HierarchicalDataManager`): A parser - converting outputs from dictionary to tuple. - inter_outputs_in_cpu (dict): Specify the features to be - recorded. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - model, - inputs_manager, - outputs_manager, - inter_outputs_in_cpu, - modules_to_record=None): - super().__init__() - self.model = model - self.inputs_manager = inputs_manager - self.outputs_manager = outputs_manager - self.training = model.training - # Register a hook function to capture the intermediate features - # generated by the network to align the outputs between ipu and cpu - # Used to confirm whether the implementation of CPU is consistent - # with the implementation of IPU - self.inter_outputs_in_cpu = inter_outputs_in_cpu - if modules_to_record is None: - modules_to_record = [] - - for idx, (name, module) in enumerate(model.named_modules()): - if name in modules_to_record or idx in modules_to_record: - features_hook = self.get_input_output_hook( - name, idx, self.inter_outputs_in_cpu) - module.register_forward_hook(hook=features_hook) - - def get_input_output_hook(self, name, idx, save_dict): - - def input_output_hook(module, fea_in, fea_out): - if isinstance(fea_in, tuple): - fea_in = list(fea_in) - if isinstance(fea_out, tuple): - fea_out = list(fea_out) - save_dict[name] = { - 'fea_in': fea_in, - 'fea_out': fea_out, - 'idx': idx - } - return None - - return input_output_hook - - def forward(self, inputs_tuple): - """This function is used to be compiled to ipu, the inputs and outputs - need to be tuples, so here we need to restore the input back to a - dictionary and convert the output to a tuple.""" - self.inputs_manager.update_all_tensors(inputs_tuple) - kwargs = {**(self.inputs_manager.hierarchical_data)} - if self.training: - outputs = self.forward_train(kwargs) - # tell poptorch which loss will be used finally - identity_loss(outputs['loss'], reduction='none') - else: - outputs = self.forward_eval(kwargs) - - if isinstance(outputs, torch.Tensor): - # currently not support single tensor output, - # need to wrap it with a dictionary, - # use a keyword to identify this case - outputs = {'output of WrappedNet: single tensor': outputs} - - # if there are some features need to be record, add extra outputs - for name in self.inter_outputs_in_cpu: - outputs[name] = self.inter_outputs_in_cpu[name] - - # record all the places of return tensors in the converting stage - # while in the real run stage, all the tensor are changed in-place - # that means the output can be obtained directly outside this function - self.outputs_manager.record_hierarchical_data(outputs) - plain_outputs = self.outputs_manager.collect_all_tensors() - return plain_outputs - - def forward_train(self, kwargs): - optimizer = kwargs.pop('optimizer') - outputs = self.train_step(kwargs, optimizer) - return outputs - - def train_step(self, data, optimizer=None, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating are also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer`, optional): The - optimizer of runner is passed to ``train_step()``. This - argument is unused and reserved. - - Returns: - dict: Dict of outputs. The following fields are contained. - - loss (torch.Tensor): A tensor for back propagation, which \ - can be a weighted sum of multiple losses. - - log_vars (dict): Dict contains all the variables to be sent \ - to the logger. - - num_samples (int): Indicates the batch size (when the model \ - is DDP, it means the batch size on each GPU), which is \ - used for averaging the logs. - """ - losses = self.model(**data) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, log_vars=log_vars, num_samples=len(data['img'].data)) - - return outputs - - def _parse_losses(self, losses): - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(loss.mean() for loss in loss_value) - elif isinstance(loss_value, dict): - for name, value in loss_value.items(): - log_vars[name] = value - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(value for key, value in log_vars.items() if 'loss' in key) - log_vars['loss'] = loss - - return loss, log_vars - - def forward_eval(self, kwargs): - img = kwargs.pop('img') - img_metas = kwargs.pop('img_metas', None) - return_loss = kwargs.pop('return_loss') - assert not return_loss - # TODO Temporarily hard-code to close post_process, - # otherwise, in the third trace(_check_trace), - # post_process will convert output tensor to numpy array automatically, - # resulting in _check_trace failure - outputs = self.model( - img, - img_metas=img_metas, - return_loss=return_loss, - post_process=False) - return outputs - - -class MMPoplarExecutor(PoplarExecutor): - """An executor for inputs/outputs parsing, model compilation, data - alignment and IPU upload/download. - - Args: - model (:obj:`nn.Module`): The model to be compiled. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - training (bool): Model in training mode or eval mode. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - args (argument list): Arguments passed to the `__init__` - method of PoplarExecutor. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of PoplarExecutor. - """ - - def __init__(self, - model, - logger=None, - training=True, - modules_to_record=None, - *args, - **kwargs): - # self.model == self._user_model: input pytorch model - # self._model: wrapped model which is used to compile - # and update weights, these two models use same weights - # wrapped model only accept and output tuple, so - # HierarchicalDataManager will convert dictionary - # to tuple and convert them back - self.inputs_manager = HierarchicalDataManager(logger=logger) - self.outputs_manager = HierarchicalDataManager(logger=logger) - self.logger = logger - # the features calculated by CPU - self.inter_outputs_in_cpu = {} - # the features calculated by IPU - self.inter_outputs_in_ipu = {} - if modules_to_record is None: - # It is possible that the IPU implementation of some operators - # is inconsistent with the expected (CPU), here you can use - # this method to confirm whether there is a problem - self.compare_with_cpu = False - else: - self.compare_with_cpu = True - # move model.fp16_enabled to self.fp16_enabled, - # modify the position where the input is automatically casted to half - if getattr(model, 'fp16_enabled', False): - model.fp16_enabled = False - self.fp16_enabled = True - # make torch.jit.trace convert self._model - model = WrappedNet( - model, - self.inputs_manager, - self.outputs_manager, - self.inter_outputs_in_cpu, - modules_to_record=modules_to_record) - super().__init__(model, training=training, *args, **kwargs) - # overwrite self._args_parser in train_step or val_step - self._args_parser = None - if training: - assert self.training - else: - assert not self.training - - @property - def training(self): - # If trying to get the attribute(training) of self, - # since the class has no training attribute, - # it will automatically look for the training attribute of self.model. - # However, the real attribute we want to check is self._training, - # self.model.training and self._training are often inconsistent. - # It is not clear whether it is a Poptorch bug or a special design, - # temporarily use this function to fix the problem - return self._training # comes from self.model._training - - @auto_fp16(supported_types=(PoplarExecutor, )) - def run_model(self, data_dict): - # this function is used to parse input_dict - # and convert to output_dict - if self.isCompiled(): - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - else: - # get tensors out of data and put them in a tuple - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - # turn logger in data manager off after compilation - self.inputs_manager.quick() - self.outputs_manager.quick() - - # parser args in the first iter - if self._args_parser is None: - self._args_parser = DictArgsParser({'args': inputs_tuple}) - - # run or convert model - # the plain_outputs will be used in converting stage - plain_outputs = self(inputs_tuple) - - self.inputs_manager.clean_all_tensors() - - # put list of tensors back to the output dict - # according to the same order - self.outputs_manager.update_all_tensors(plain_outputs) - # get the real output dictionary from self.outputs_manager - output_dict = self.outputs_manager.hierarchical_data - - # split output_dict into inter_outputs_in_ipu - # and output of the torch model - torch_model_output = {} - for name in output_dict: - if name in self.inter_outputs_in_cpu: - self.inter_outputs_in_ipu[name] = output_dict[name] - else: - torch_model_output[name] = output_dict[name] - - if 'output of WrappedNet: single tensor' in output_dict: - assert len(torch_model_output) == 1 - assert isinstance( - torch_model_output['output of WrappedNet: single tensor'], - torch.Tensor) - torch_model_output = \ - torch_model_output['output of WrappedNet: single tensor'] - - return torch_model_output - - def train_step(self, data, optimizer=None, **kwargs): - # arguments from mmcls/models/classifiers/base.py: - # BaseClassifier.train_step - assert self.training - assert len(kwargs) == 0 # TODO, support later if necessary - - # TODO support datacontainer as input - # currently, auto_fp16 and HierarchicalDataManager take too much - # time on traversing datacontainer - data['img_metas'] = None - num_samples = len(data['img'].data) - - # TODO we will ignore optimizer because it will not be used in model, - # support later if necessary - data['optimizer'] = None - output_dict = self.run_model(data) - - # outputs contained loss, log_vars, num_samples, - # only loss(torch.tensor) has been updated - # remove all unchanged vars, left torch.tensor - neat_output_dict = {'loss': output_dict['loss']} - - # re-parse outputs, get back log_vars and num_samples - loss, log_vars = self.model._parse_losses(neat_output_dict) - final_output_dict = dict( - loss=loss, log_vars=log_vars, num_samples=num_samples) - return final_output_dict - - def eval_call(self, img, img_metas=None, return_loss=True, **kwargs): - # arguments from mmdet/models/detectors/base.py:BaseDetector.forward - # tmp usssage for eval mode - assert not self.training - assert len(kwargs) == 0 # TODO, support later if necessary - assert not return_loss - data = {'img': img, 'img_metas': img_metas, 'return_loss': return_loss} - - output_dict = self.run_model(data) - - return output_dict - - def detachFromDevice(self): - if self.isCompiled() and self._is_attached: - super().detachFromDevice() - - def attachToDevice(self): - if self.isCompiled() and not self._is_attached: - super().attachToDevice() - - -class TrainEvalModel: - """A class maintaining training MMPoplarExecutor and inference - MMPoplarExecutor. - - Args: - train_model (:obj:`nn.Module`): The training model to be compiled. - ``train_model`` can be None if only executing validation. - eval_model (:obj:`nn.Module`): The inference model to be compiled. - options (mmcv.Config, dict): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - train_model, - eval_model, - options, - optimizer, - modules_to_record=None, - logger=None): - if train_model is None: - self._train_executor = None - self.training = False - else: - self._train_executor = get_training_model( - train_model, - options=options['training'], - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - self.training = True - self._eval_executor = get_inference_model( - eval_model, options=options['inference'], logger=logger) - - @property - def executor(self): - if self.training: - return self._train_executor - else: - return self._eval_executor - - def train(self, mode: bool = True): - """Sets the module in training mode. - - This has any effect only on certain modules. See documentations of - particular modules for details of their behaviors in - training/evaluation mode, if they are affected, - e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - Args: - mode (bool): whether to set training mode (``True``) or evaluation - mode (``False``). Default: ``True``. - - Returns: - Module: self - """ - if not isinstance(mode, bool): - raise ValueError('training mode is expected to be boolean, ' - f'but got {type(mode)}') - if self._train_executor is None and mode: - raise RuntimeError( - 'The train_executor is not initialized.' - 'If you want to initialize train_executor,' - 'you need to input optimizer when converting pytorch model') - - if mode == self.training: - self.model.train(mode) - return self - else: - if self.isCompiled(): - # copy weights from IPU to cpu before off-load current session - self.copyWeightsToHost() - # detach the current session before change the mode, - # if is training mode and weights are updated, - # poptorch will copy weights from IPU to host - self.detachFromDevice() - - self.training = mode # session will changed with mode changing - self.model.train(mode) - - # after changing mode, attach the current new session, - # and this function will copy weights of model to device - self.attachToDevice() - return self - - def eval(self): - """Sets the module in evaluation mode. - - This has any effect only on certain modules. - See documentations of particular modules - for details of their behaviors in training/evaluation mode, - if they are affected, e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - This is equivalent with :meth:`self.train(False) - `. - - See :ref:`locally-disable-grad-doc` for a comparison between - `.eval()` and several similar mechanisms that may be confused with it. - - Returns: - Module: self - """ - return self.train(False) - - def compare_data_between_ipu_and_cpu(self, inter_outputs_in_cpu, - inter_outputs_in_ipu): - for key, val in inter_outputs_in_cpu.items(): - is_tensor = isinstance(val['fea_in'], torch.Tensor) - fea_in_cpu = val['fea_in'] - fea_in_cpu_list = [fea_in_cpu] if is_tensor else fea_in_cpu - fea_in_ipu = inter_outputs_in_ipu[key]['fea_in'] - fea_in_ipu_list = [fea_in_ipu] if is_tensor else fea_in_ipu - - is_tensor = isinstance(val['fea_out'], torch.Tensor) - fea_out_cpu = val['fea_out'] - fea_out_cpu_list = [fea_out_cpu] if is_tensor else fea_out_cpu - fea_out_ipu = inter_outputs_in_ipu[key]['fea_out'] - fea_out_ipu_list = [fea_out_ipu] if is_tensor else fea_out_ipu - - print('comparing layer:', key) - for idx, (featA, featB) in \ - enumerate(zip(fea_in_cpu_list, fea_in_ipu_list)): - print('fea_in, tensor ', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - for idx, (featA, featB) in \ - enumerate(zip(fea_out_cpu_list, fea_out_ipu_list)): - print('fea_out, tensor', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def train_step(self, data, optimizer=None, **kwargs): - assert self.training, 'not supported train_step on eval mode' - inter_outputs_in_cpu = {} - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu): - self.copyWeightsToHost() - # run in CPU mode - self._train_executor.model.train_step(data, optimizer, **kwargs) - inter_outputs_in_cpu = { - **(self._train_executor.inter_outputs_in_cpu) - } - # run in IPU mode - result = self._train_executor.train_step(data, optimizer, **kwargs) - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu - and len(inter_outputs_in_cpu) > 0): - self.compare_data_between_ipu_and_cpu( - inter_outputs_in_cpu, - self._train_executor.inter_outputs_in_ipu) - return result - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def __call__(self, *args, **kwargs): - if self.training: - raise NotImplementedError('use train_step rather than __call__') - else: - return self._eval_executor.eval_call(*args, **kwargs) - - def __getattr__(self, attr): - return getattr(self.executor, attr) - - -def get_training_model(model: nn.Module, - options: Optional[poptorch.Options] = None, - optimizer: Optional[torch.optim.Optimizer] = None, - logger=None, - modules_to_record=None) -> poptorch.PoplarExecutor: - """Create a PopTorch training model from a PyTorch model, running on IPU - hardware in training mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned training model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.train()`` on the original model, which - changes the ``training`` bool of the model instance, will not alter the - model returned by this function. You may need to call ``model.train()`` - on your model before you call this function for correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): The optimizers - to apply during training. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place - of ``model``. - """ - # Create a copy of the original model in case it needs to be wrapped - maybe_wrapped_model = copy.copy(model) - - return MMPoplarExecutor( - model=maybe_wrapped_model, - logger=logger, - options=options, - training=True, - optimizer=optimizer, - user_model=model, - modules_to_record=modules_to_record, - poptorch_version=__version__) - - -def get_inference_model(model: Union[nn.Module, poptorch.PoplarExecutor], - options: Optional[poptorch.Options] = None, - logger=None) -> poptorch.PoplarExecutor: - """Create a PopTorch inference model from a PyTorch model, running on IPU - hardware in inference mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned inference model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.eval()`` on the original model will not alter - the model returned by this function. You may need to call - ``model.eval()`` on your model before you call this function for - correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place of - ``model``. - """ - - return MMPoplarExecutor( - model=copy.copy(model), - logger=logger, - options=options, - training=False, - poptorch_version=__version__) - - -def ipu_model_wrapper(model, - options, - optimizer=None, - logger=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None): - """Convert torch model to IPU model. - - Args: - model (nn.Module): The target model to be converted. - options (dict[str, poptorch.Options]): IPU options, generated - by :func:`cfg2options`. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during training. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (dict): A dictionary contains train_split_edges and - train_ckpt_nodes, See details in :func:`model_sharding` and - :func:`recomputation_checkpoint` functions. - fp16_cfg (dict): Config for IPU fp16 training. Currently supports - configs: `loss_scale`, `velocity_accum_type` and `accum_type`. - See details in - https://docs.graphcore.ai/projects/poptorch-user-guide/en/latest/index.html - - Returns: - TrainEvalModel: IPU wrapped model. - """ - if ipu_model_cfg is None: - ipu_model_cfg = {} - training = model.training if optimizer is not None else False - # set mixed-precision - if fp16_cfg is not None: - from mmcv.runner import wrap_fp16_model - loss_scale = fp16_cfg['loss_scale'] - wrap_fp16_model(model) - model.half() - # TODO tmp ussage to set loss scaling for torch original optimizer - if optimizer is not None: - optimizer.loss_scaling = loss_scale - if fp16_cfg.get('velocity_accum_type', False): - if fp16_cfg['velocity_accum_type'] == 'half': - optimizer.velocity_accum_type = torch.half - else: - optimizer.velocity_accum_type = torch.float32 - if fp16_cfg.get('accum_type', False): - if fp16_cfg['accum_type'] == 'half': - optimizer.accum_type = torch.half - else: - optimizer.accum_type = torch.float32 - # TODO support feature alignment for fp16 - if modules_to_record is not None: - raise NotImplementedError( - 'Feature alignment for fp16 is not implemented') - - # set model partition - if optimizer is None: - train_model = None - else: - # split model into multi-IPUs if specified - train_model = model_sharding( - copy.copy(model).train(), - ipu_model_cfg.get('train_split_edges', [])) - - recomputation_checkpoint(train_model, - ipu_model_cfg.get('train_ckpt_nodes', [])) - - # TODO support feature alignment for gradient accumulation mode - gradient_accumulation = \ - getattr(options['training'].Training, 'gradient_accumulation', 1) - if gradient_accumulation > 1: - assert modules_to_record is None, \ - 'Feature alignment for grad-accumulation mode not implemented' - - # TODO support feature alignment for multi-replica mode - replication_factor = \ - getattr(options['training'], 'replication_factor', 1) - if replication_factor > 1: - assert modules_to_record is None, \ - 'Feature alignment for multi-replica mode not implemented' - - # TODO supports different model partitions between train and eval mode - assert len(ipu_model_cfg.get('eval_split_edges', [])) == 0,\ - 'Currently, BeginBlock can only be used once on the same model' - eval_model = copy.copy(model).eval() - - # wrap model for compilation - model = TrainEvalModel( - train_model, - eval_model, - options=options, - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - model.train(training) - return model diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/runner.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/runner.py deleted file mode 100755 index e2d492267..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/runner.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.runner import (HOOKS, RUNNERS, BaseRunner, EpochBasedRunner, - IterBasedRunner) -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import (IPUFp16OptimizerHook, wrap_lr_updater_hook, - wrap_optimizer_hook) - from .model_wrapper import ipu_model_wrapper - from .utils import build_from_cfg_with_wrapper, cfg2options - - -class IPUBaseRunner(BaseRunner): - """A base runner for IPU. - - This runner has some extra processes for IPU which are shown below: - - 1. Parse options for IPU - 2. wrap pytorch model for IPU - 3. Raise errors while encountering illegal usage - 4. Input IPU options and initialize dataloader if finding an instance - of IPUDataLoader - - Args: - model (:obj:`nn.Module`): The model to run. - options_cfg (mmcv.Config, dict): Options that will be used to compile - and run the model. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (mmcv.Config, dict): Config of model partition and - recomputing checkpoint - fp16_cfg (mmcv.Config): Config for fp16 training. - batch_processor (callable): A callable method that process a data - batch. Should be None for IPU runner - kwargs (Dict[str, Any], optional): Keyword arguments will be passed to - ``base_runner.BaseRunner``. - """ - - def __init__(self, - model, - options_cfg=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None, - batch_processor=None, - **kwargs): - assert hasattr(model, 'train_step') and batch_processor is None,\ - 'only support model with train_step' - - if options_cfg is None: - options_cfg = {} - # call BaseRunner.__init__() here - super().__init__(model, **kwargs) - - # process options of ipu - if IS_IPU_AVAILABLE: - self.options = cfg2options(options_cfg) - self.model = ipu_model_wrapper( - self.model, - self.options, - self.optimizer, - self.logger, - modules_to_record=modules_to_record, - ipu_model_cfg=ipu_model_cfg, - fp16_cfg=fp16_cfg) - else: - raise NotImplementedError('cpu mode on IPURunner is not supported') - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - assert isinstance(lr_config, dict) - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, - # e.g., 'cyclic', then its first letter will be capitalized, - # e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, the string will not be changed - # if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = build_from_cfg_with_wrapper(lr_config, HOOKS, - wrap_lr_updater_hook) - self.register_hook(hook, priority='VERY_HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - assert isinstance(optimizer_config, (dict, IPUFp16OptimizerHook)) - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = build_from_cfg_with_wrapper(optimizer_config, HOOKS, - wrap_optimizer_hook) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def run(self, data_loaders, workflow, *args, **kwargs): - for i, flow in enumerate(workflow): - mode, _ = flow - # initialize IPU dataloader if not initialized - assert isinstance(data_loaders[i], IPUDataLoader),\ - 'IPU runner can only work with `IPUDataLoader`' - data_loaders[i].init(options=self.get_options(mode)) - - super().run(data_loaders, workflow, *args, **kwargs) - - def get_options(self, mode): - if mode == 'train': - return self.options['training'] - elif mode == 'val': - return self.options['inference'] - else: - raise ValueError(f'mode should be train or val but got {mode}') - - -@RUNNERS.register_module() -class IPUEpochBasedRunner(IPUBaseRunner, EpochBasedRunner): - """Epoch-based Runner for IPU. - - The Inheritance order(MRO) is: IPUEpochBasedRunner -> IPUBaseRunner -> - EpochBasedRunner -> BaseRunner This runner train models epoch by epoch. - """ - pass - - -@RUNNERS.register_module() -class IPUIterBasedRunner(IPUBaseRunner, IterBasedRunner): - """Iteration-based Runner for IPU. - - The Inheritance order(MRO) is: IPUIterBasedRunner -> IPUBaseRunner -> - IterBasedRunner -> BaseRunner This runner train models iteration by - iteration. - """ - pass diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/utils.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/utils.py deleted file mode 100755 index 79709db1e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/ipu/utils.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import numpy as np -import popart -import poptorch -import torch -import torch.nn as nn - -from mmcv.utils import Registry - - -def _options_assigner(cfg, options_node): - # set popart.options by config - # cfg: dict, python data type - # options_node: python module or function - if isinstance(cfg, dict): - for key in cfg: - _options_assigner(cfg[key], getattr(options_node, key)) - elif isinstance(cfg, (int, float, str, list)): - if callable(options_node): - options_node(cfg) - else: - error_msg = f'options_node type {type(options_node)} not supported' - raise NotImplementedError(error_msg) - else: - error_msg = f'cfg type {type(cfg)} not supported' - raise NotImplementedError(error_msg) - - -def cfg2options(cfg): - """Parse dictionary to ipu options. - - Args: - cfg (dict): A dictionary of ipu settings. - - Returns: - dict[str, poptorch.Options]: Training options and inference options - of IPU. - """ - # set ipu options for inference and training by config - train_cfg = cfg.pop('train_cfg', {}) - eval_cfg = cfg.pop('eval_cfg', {}) - eval_cfg['replicationFactor'] = 1 # eval mode only use one replica - eval_cfg['executionStrategy'] = 'ShardedExecution' - # overwrite default ipu cfg with specified train cfgs - training_ipu_cfg = {**cfg, **train_cfg} - # overwrite default ipu cfg with specified eval cfgs - inference_ipu_cfg = {**cfg, **eval_cfg} - - ipu_options = { - 'training': _cast_to_options(training_ipu_cfg), - 'inference': _cast_to_options(inference_ipu_cfg) - } - - # TODO configure these codes - ipu_options['training']._Popart.set('disableGradAccumulationTensorStreams', - True) - ipu_options['training']._Popart.set( - 'accumulateOuterFragmentSettings.schedule', - int(popart.AccumulateOuterFragmentSchedule.OverlapMemoryOptimized)) - ipu_options['training'].Precision.enableStochasticRounding(True) - - return ipu_options - - -def _cast_to_options(cfg): - # If it cannot be directly assigned, use if statement to parse it, - # and if it can be directly assigned, use _options_assigner to assign - options = poptorch.Options() - - if 'availableMemoryProportion' in cfg: - available_memory_proportion = cfg.pop('availableMemoryProportion') - mem_props = {} - for i, mem_prop in enumerate(available_memory_proportion): - mem_props[f'IPU{i}'] = mem_prop - options.setAvailableMemoryProportion(mem_props) - - if 'executionStrategy' in cfg: - execution_strategy = cfg.pop('executionStrategy') - if execution_strategy == 'SameAsIpu': - options.setExecutionStrategy( - poptorch.PipelinedExecution( - getattr(poptorch.AutoStage, execution_strategy))) - elif execution_strategy == 'ShardedExecution': - options.setExecutionStrategy(poptorch.ShardedExecution()) - else: - raise NotImplementedError( - 'executionStrategy should be "SameAsIpu" or "ShardedExecution"' - f', but got {execution_strategy}') - - if 'partialsType' in cfg: - partials_type = cfg.pop('partialsType') - options.Precision.setPartialsType(getattr( - torch, partials_type)) # half or float - - _options_assigner(cfg, options) - return options - - -def model_sharding(model, split_edges): - """split models in-place into multi-IPUs. - - Args: - model (nn.Module): The target model to be split. - split_edges (list of dict): Model layer names or layer numbers - of split edge. Each item of ``split_edges`` is a dictionary, - which may contain the following key-pairs: - - - layer_to_call: PyTorch module to assign to the block - - user_id (optional): A user defined identifier for the block. - - ipu_id: The id of the IPU to run on. - - Examples: - >>> split_edges = [ - ... dict(layer_to_call='model.conv1', ipu_id=0), - ... dict(layer_to_call='model.conv3', ipu_id=1)] - >>> sharding_model = model_sharding(torch_model, split_edges) - - Returns: - nn.Module: Split model. - """ - if len(split_edges) == 0: - return model - assert isinstance(split_edges, list) - spilt_edges_dict = {edge['layer_to_call']: edge for edge in split_edges} - - for idx, (name, module) in enumerate(model.named_modules()): - if idx in spilt_edges_dict and name in spilt_edges_dict: - raise ValueError( - 'The same layer is referenced twice while doing model' - f' partition: idx is {idx} and name is {name}') - - edge = spilt_edges_dict.pop(name, None) - edge = spilt_edges_dict.pop(idx, edge) - if edge is not None: - poptorch.BeginBlock(module, edge.get('user_id', name), - edge['ipu_id']) - - # ensure all split_edges are used - if len(spilt_edges_dict) > 0: - split_edge_names = list(spilt_edges_dict.keys()) - raise RuntimeError( - f'split_edges: {split_edge_names} are not contained in the model') - return model - - -def recomputation_checkpoint(model: nn.Module, module_names: list): - """Annotates the output of a module to be checkpointed instead of - recomputed. - - If recomputation mode is enabled, ipu will release the activations of - the middle layers to save memory. During the backward of gradient, - the activation of the middle layer will be recalculated again. - This function is used to declare the activations of some intermediate - layers that need to be saved in order to skip the recomputation of - some layers. - - Args: - model (nn.Module): The target model to apply recomputation - checkpoint. - module_names (list): Layer names of module. - """ - - def recompute_outputs(module, inputs, outputs): - if isinstance(outputs, tuple): - return tuple(poptorch.recomputationCheckpoint(y) for y in outputs) - else: - return poptorch.recomputationCheckpoint(outputs) - - for name, module in model.named_modules(): - if name in module_names: - module.register_forward_hook(recompute_outputs) - module_names.remove(name) - - # check all module_names are used - assert len(module_names) == 0,\ - f'recomputed nodes: {module_names} are not contained in the model' - - -def compare_ndarray(featA, featB, rtol=1e-3, atol=1e-5): - """Align data between two activations or weights.""" - try: - np.testing.assert_allclose(featA, featB, rtol=rtol, atol=atol) - except AssertionError as e: - print(e) - - -def build_from_cfg_with_wrapper(cfg, - registry, - wrapper_func=None, - default_args=None): - """Build a module from config dict and wrap module with "wrapper_func". - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - wrapper_func (function): Used to wrap class - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - - if wrapper_func is None: - wrapped_obj_cls = obj_cls - else: - wrapped_obj_cls = wrapper_func(obj_cls) - try: - return wrapped_obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{wrapped_obj_cls.__name__}: {e}') diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/__init__.py deleted file mode 100644 index 572c4da7e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .data_parallel import MLUDataParallel -from .distributed import MLUDistributedDataParallel -from .scatter_gather import scatter, scatter_kwargs - -__all__ = [ - 'MLUDataParallel', 'MLUDistributedDataParallel', 'scatter', - 'scatter_kwargs' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/_functions.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/_functions.py deleted file mode 100644 index 7c35e65a2..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/_functions.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def scatter(input, devices): - """scatter copies tensor to MLU directly.""" - if isinstance(input, list): - outputs = [scatter(_input, devices) for _input in input] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - return output.to('mlu') if devices != [-1] else output - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_mlus, input): - outputs = scatter(input, target_mlus) - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/data_parallel.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/data_parallel.py deleted file mode 100644 index b2d09d0b0..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/data_parallel.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -import torch - -from mmcv.parallel import MMDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDataParallel(MMDataParallel): - """The MLUDataParallel module that supports DataContainer. - - MLUDataParallel is a class inherited from MMDataParall, which supports - MLU training and inference only. - - The main differences with MMDataParallel: - - - It only supports single-card of MLU, and only use first card to - run training and inference. - - - It uses direct host-to-device copy instead of stream-background - scatter. - - .. warning:: - MLUDataParallel only supports single MLU training, if you need to - train with multiple MLUs, please use MLUDistributedDataParallel - instead. If you have multiple MLUs, you can set the environment - variable ``MLU_VISIBLE_DEVICES=0`` (or any other card number(s)) - to specify the running device. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MLUDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.device_ids = [0] - self.src_device_obj = torch.device('mlu:0') - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/distributed.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/distributed.py deleted file mode 100644 index 3768c754c..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/distributed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.parallel import MMDistributedDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDistributedDataParallel(MMDistributedDataParallel): - """The DDP module supports DataContainer. - - MLUDDP has one difference from MMDDP which moves data to MLU with coping - instead of scattering. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/scatter_gather.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/scatter_gather.py deleted file mode 100644 index 0b0c9b96f..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/device/mlu/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmcv.parallel.data_container import DataContainer -from ._functions import Scatter - - -def scatter(inputs, target_mlus, dim=0): - """Scatter inputs to target mlu. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_mlus != [-1]: - obj = obj.to('mlu') - return [obj] - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_mlus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_mlus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_mlus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_mlus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_mlus, dim) if inputs else [] - kwargs = scatter(kwargs, target_mlus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/engine/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/engine/__init__.py deleted file mode 100644 index 3193b7f66..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, - single_gpu_test) - -__all__ = [ - 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', - 'single_gpu_test' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/engine/test.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/engine/test.py deleted file mode 100644 index f236b1cda..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/engine/test.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, data_loader): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - # Assume result has the same length of batch_size - # refer to https://github.com/open-mmlab/mmcv/issues/985 - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting - ``gpu_collect=True``, it encodes results to gpu tensors and use gpu - communication for results collection. On cpu mode it saves the results on - different gpus to ``tmpdir`` and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - if rank == 0: - batch_size = len(result) - batch_size_all = batch_size * world_size - if batch_size_all + prog_bar.completed > len(dataset): - batch_size_all = len(dataset) - prog_bar.completed - for _ in range(batch_size_all): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results under cpu mode. - - On cpu mode, this function will save the results on different gpus to - ``tmpdir`` and collect them by the rank 0 worker. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - tmpdir (str | None): temporal directory for collected results to - store. If set to None, it will create a random temporal directory - for it. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_result = mmcv.load(part_file) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results under gpu mode. - - On gpu mode, this function will encode results to gpu tensors and use gpu - communication for results collection. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/__init__.py deleted file mode 100644 index 2051b85f7..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .file_client import BaseStorageBackend, FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler -from .io import dump, load, register_handler -from .parse import dict_from_file, list_from_file - -__all__ = [ - 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', - 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', - 'list_from_file', 'dict_from_file' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/file_client.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/file_client.py deleted file mode 100644 index e7fd7cdfa..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/file_client.py +++ /dev/null @@ -1,1163 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import os -import os.path as osp -import re -import tempfile -import warnings -from abc import ABCMeta, abstractmethod -from contextlib import contextmanager -from pathlib import Path -from typing import Iterable, Iterator, Optional, Tuple, Union -from urllib.request import urlopen - -import mmcv -from mmcv.utils.misc import has_method -from mmcv.utils.path import is_filepath - - -class BaseStorageBackend(metaclass=ABCMeta): - """Abstract class of storage backends. - - All backends need to implement two apis: ``get()`` and ``get_text()``. - ``get()`` reads the file as a byte stream and ``get_text()`` reads the file - as texts. - """ - - # a flag to indicate whether the backend can create a symlink for a file - _allow_symlink = False - - @property - def name(self): - return self.__class__.__name__ - - @property - def allow_symlink(self): - return self._allow_symlink - - @abstractmethod - def get(self, filepath): - pass - - @abstractmethod - def get_text(self, filepath): - pass - - -class CephBackend(BaseStorageBackend): - """Ceph storage backend (for internal use). - - Args: - path_mapping (dict|None): path mapping dict from local path to Petrel - path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` - will be replaced by ``dst``. Default: None. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - """ - - def __init__(self, path_mapping=None): - try: - import ceph - except ImportError: - raise ImportError('Please install ceph to enable CephBackend.') - - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - self._client = ceph.S3Client() - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def get(self, filepath): - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class PetrelBackend(BaseStorageBackend): - """Petrel storage backend (for internal use). - - PetrelBackend supports reading and writing data to multiple clusters. - If the file path contains the cluster name, PetrelBackend will read data - from specified cluster or write data to it. Otherwise, PetrelBackend will - access the default cluster. - - Args: - path_mapping (dict, optional): Path mapping dict from local path to - Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in - ``filepath`` will be replaced by ``dst``. Default: None. - enable_mc (bool, optional): Whether to enable memcached support. - Default: True. - - Examples: - >>> filepath1 = 's3://path/of/file' - >>> filepath2 = 'cluster-name:s3://path/of/file' - >>> client = PetrelBackend() - >>> client.get(filepath1) # get data from default cluster - >>> client.get(filepath2) # get data from 'cluster-name' cluster - """ - - def __init__(self, - path_mapping: Optional[dict] = None, - enable_mc: bool = True): - try: - from petrel_client import client - except ImportError: - raise ImportError('Please install petrel_client to enable ' - 'PetrelBackend.') - - self._client = client.Client(enable_mc=enable_mc) - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def _map_path(self, filepath: Union[str, Path]) -> str: - """Map ``filepath`` to a string path whose prefix will be replaced by - :attr:`self.path_mapping`. - - Args: - filepath (str): Path to be mapped. - """ - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - return filepath - - def _format_path(self, filepath: str) -> str: - """Convert a ``filepath`` to standard format of petrel oss. - - If the ``filepath`` is concatenated by ``os.path.join``, in a Windows - environment, the ``filepath`` will be the format of - 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the - above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. - - Args: - filepath (str): Path to be formatted. - """ - return re.sub(r'\\+', '/', filepath) - - def get(self, filepath: Union[str, Path]) -> memoryview: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - memoryview: A memory view of expected bytes object to avoid - copying. The memoryview object can be converted to bytes by - ``value_buf.tobytes()``. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return str(self.get(filepath), encoding=encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Save data to a given ``filepath``. - - Args: - obj (bytes): Data to be saved. - filepath (str or Path): Path to write data. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.put(filepath, obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Save data to a given ``filepath``. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to encode the ``obj``. - Default: 'utf-8'. - """ - self.put(bytes(obj, encoding=encoding), filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - if not has_method(self._client, 'delete'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `delete` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.delete(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - if not (has_method(self._client, 'contains') - and has_method(self._client, 'isdir')): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` and `isdir` methods, please use a higher' - 'version or dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) or self._client.isdir(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - if not has_method(self._client, 'isdir'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `isdir` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - if not has_method(self._client, 'contains'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` method, please use a higher version or ' - 'dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result after concatenation. - """ - filepath = self._format_path(self._map_path(filepath)) - if filepath.endswith('/'): - filepath = filepath[:-1] - formatted_paths = [filepath] - for path in filepaths: - formatted_paths.append(self._format_path(self._map_path(path))) - return '/'.join(formatted_paths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download a file from ``filepath`` and return a temporary path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str | Path): Download a file from ``filepath``. - - Examples: - >>> client = PetrelBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('s3://path/of/your/file') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one temporary path. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - assert self.isfile(filepath) - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - Petrel has no concept of directories but it simulates the directory - hierarchy in the filesystem through public prefixes. In addition, - if the returned path ends with '/', it means the path is a public - prefix which is a logical directory. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - In addition, the returned path of directory will not contains the - suffix '/' which is consistent with other backends. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if not has_method(self._client, 'list'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `list` method, please use a higher version or dev' - ' branch instead.')) - - dir_path = self._map_path(dir_path) - dir_path = self._format_path(dir_path) - if list_dir and suffix is not None: - raise TypeError( - '`list_dir` should be False when `suffix` is not None') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - # Petrel's simulated directory hierarchy assumes that directory paths - # should end with `/` - if not dir_path.endswith('/'): - dir_path += '/' - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for path in self._client.list(dir_path): - # the `self.isdir` is not used here to determine whether path - # is a directory, because `self.isdir` relies on - # `self._client.list` - if path.endswith('/'): # a directory path - next_dir_path = self.join_path(dir_path, path) - if list_dir: - # get the relative path and exclude the last - # character '/' - rel_dir = next_dir_path[len(root):-1] - yield rel_dir - if recursive: - yield from _list_dir_or_file(next_dir_path, list_dir, - list_file, suffix, - recursive) - else: # a file path - absolute_path = self.join_path(dir_path, path) - rel_path = absolute_path[len(root):] - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class MemcachedBackend(BaseStorageBackend): - """Memcached storage backend. - - Attributes: - server_list_cfg (str): Config file for memcached server list. - client_cfg (str): Config file for memcached client. - sys_path (str | None): Additional path to be appended to `sys.path`. - Default: None. - """ - - def __init__(self, server_list_cfg, client_cfg, sys_path=None): - if sys_path is not None: - import sys - sys.path.append(sys_path) - try: - import mc - except ImportError: - raise ImportError( - 'Please install memcached to enable MemcachedBackend.') - - self.server_list_cfg = server_list_cfg - self.client_cfg = client_cfg - self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, - self.client_cfg) - # mc.pyvector servers as a point which points to a memory cache - self._mc_buffer = mc.pyvector() - - def get(self, filepath): - filepath = str(filepath) - import mc - self._client.Get(filepath, self._mc_buffer) - value_buf = mc.ConvertBuffer(self._mc_buffer) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class LmdbBackend(BaseStorageBackend): - """Lmdb storage backend. - - Args: - db_path (str): Lmdb database path. - readonly (bool, optional): Lmdb environment parameter. If True, - disallow any write operations. Default: True. - lock (bool, optional): Lmdb environment parameter. If False, when - concurrent access occurs, do not lock the database. Default: False. - readahead (bool, optional): Lmdb environment parameter. If False, - disable the OS filesystem readahead mechanism, which may improve - random read performance when a database is larger than RAM. - Default: False. - - Attributes: - db_path (str): Lmdb database path. - """ - - def __init__(self, - db_path, - readonly=True, - lock=False, - readahead=False, - **kwargs): - try: - import lmdb # NOQA - except ImportError: - raise ImportError('Please install lmdb to enable LmdbBackend.') - - self.db_path = str(db_path) - self.readonly = readonly - self.lock = lock - self.readahead = readahead - self.kwargs = kwargs - self._client = None - - def get(self, filepath): - """Get values according to the filepath. - - Args: - filepath (str | obj:`Path`): Here, filepath is the lmdb key. - """ - if self._client is None: - self._client = self._get_client() - - with self._client.begin(write=False) as txn: - value_buf = txn.get(str(filepath).encode('utf-8')) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - def _get_client(self): - import lmdb - - return lmdb.open( - self.db_path, - readonly=self.readonly, - lock=self.lock, - readahead=self.readahead, - **self.kwargs) - - def __del__(self): - self._client.close() - - -class HardDiskBackend(BaseStorageBackend): - """Raw hard disks storage backend.""" - - _allow_symlink = True - - def get(self, filepath: Union[str, Path]) -> bytes: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes: Expected bytes object. - """ - with open(filepath, 'rb') as f: - value_buf = f.read() - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - with open(filepath, 'r', encoding=encoding) as f: - value_buf = f.read() - return value_buf - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` will create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'wb') as f: - f.write(obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` will create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'w', encoding=encoding) as f: - f.write(obj) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - os.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return osp.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return osp.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return osp.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return osp.join(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, filepath: Union[str, Path]) -> Iterable[Union[str, Path]]: - """Only for unified API and do nothing.""" - yield filepath - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if list_dir and suffix is not None: - raise TypeError('`suffix` should be None when `list_dir` is True') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - elif osp.isdir(entry.path): - if list_dir: - rel_dir = osp.relpath(entry.path, root) - yield rel_dir - if recursive: - yield from _list_dir_or_file(entry.path, list_dir, - list_file, suffix, - recursive) - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class HTTPBackend(BaseStorageBackend): - """HTTP and HTTPS storage bachend.""" - - def get(self, filepath): - value_buf = urlopen(filepath).read() - return value_buf - - def get_text(self, filepath, encoding='utf-8'): - value_buf = urlopen(filepath).read() - return value_buf.decode(encoding) - - @contextmanager - def get_local_path(self, filepath: str) -> Iterable[str]: - """Download a file from ``filepath``. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str): Download a file from ``filepath``. - - Examples: - >>> client = HTTPBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('http://path/of/your/file') as path: - ... # do something here - """ - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - -class FileClient: - """A general file client to access files in different backends. - - The client loads a file or text in a specified backend from its path - and returns it as a binary or text file. There are two ways to choose a - backend, the name of backend and the prefix of path. Although both of them - can be used to choose a storage backend, ``backend`` has a higher priority - that is if they are all set, the storage backend will be chosen by the - backend argument. If they are all `None`, the disk backend will be chosen. - Note that It can also register other backend accessor with a given name, - prefixes, and backend class. In addition, We use the singleton pattern to - avoid repeated object creation. If the arguments are the same, the same - object will be returned. - - Args: - backend (str, optional): The storage backend type. Options are "disk", - "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. - prefix (str, optional): The prefix of the registered storage backend. - Options are "s3", "http", "https". Default: None. - - Examples: - >>> # only set backend - >>> file_client = FileClient(backend='petrel') - >>> # only set prefix - >>> file_client = FileClient(prefix='s3') - >>> # set both backend and prefix but use backend to choose client - >>> file_client = FileClient(backend='petrel', prefix='s3') - >>> # if the arguments are the same, the same object is returned - >>> file_client1 = FileClient(backend='petrel') - >>> file_client1 is file_client - True - - Attributes: - client (:obj:`BaseStorageBackend`): The backend object. - """ - - _backends = { - 'disk': HardDiskBackend, - 'ceph': CephBackend, - 'memcached': MemcachedBackend, - 'lmdb': LmdbBackend, - 'petrel': PetrelBackend, - 'http': HTTPBackend, - } - # This collection is used to record the overridden backends, and when a - # backend appears in the collection, the singleton pattern is disabled for - # that backend, because if the singleton pattern is used, then the object - # returned will be the backend before overwriting - _overridden_backends = set() - _prefix_to_backends = { - 's3': PetrelBackend, - 'http': HTTPBackend, - 'https': HTTPBackend, - } - _overridden_prefixes = set() - - _instances = {} - - def __new__(cls, backend=None, prefix=None, **kwargs): - if backend is None and prefix is None: - backend = 'disk' - if backend is not None and backend not in cls._backends: - raise ValueError( - f'Backend {backend} is not supported. Currently supported ones' - f' are {list(cls._backends.keys())}') - if prefix is not None and prefix not in cls._prefix_to_backends: - raise ValueError( - f'prefix {prefix} is not supported. Currently supported ones ' - f'are {list(cls._prefix_to_backends.keys())}') - - # concatenate the arguments to a unique key for determining whether - # objects with the same arguments were created - arg_key = f'{backend}:{prefix}' - for key, value in kwargs.items(): - arg_key += f':{key}:{value}' - - # if a backend was overridden, it will create a new object - if (arg_key in cls._instances - and backend not in cls._overridden_backends - and prefix not in cls._overridden_prefixes): - _instance = cls._instances[arg_key] - else: - # create a new object and put it to _instance - _instance = super().__new__(cls) - if backend is not None: - _instance.client = cls._backends[backend](**kwargs) - else: - _instance.client = cls._prefix_to_backends[prefix](**kwargs) - - cls._instances[arg_key] = _instance - - return _instance - - @property - def name(self): - return self.client.name - - @property - def allow_symlink(self): - return self.client.allow_symlink - - @staticmethod - def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: - """Parse the prefix of a uri. - - Args: - uri (str | Path): Uri to be parsed that contains the file prefix. - - Examples: - >>> FileClient.parse_uri_prefix('s3://path/of/your/file') - 's3' - - Returns: - str | None: Return the prefix of uri if the uri contains '://' else - ``None``. - """ - assert is_filepath(uri) - uri = str(uri) - if '://' not in uri: - return None - else: - prefix, _ = uri.split('://') - # In the case of PetrelBackend, the prefix may contains the cluster - # name like clusterName:s3 - if ':' in prefix: - _, prefix = prefix.split(':') - return prefix - - @classmethod - def infer_client(cls, - file_client_args: Optional[dict] = None, - uri: Optional[Union[str, Path]] = None) -> 'FileClient': - """Infer a suitable file client based on the URI and arguments. - - Args: - file_client_args (dict, optional): Arguments to instantiate a - FileClient. Default: None. - uri (str | Path, optional): Uri to be parsed that contains the file - prefix. Default: None. - - Examples: - >>> uri = 's3://path/of/your/file' - >>> file_client = FileClient.infer_client(uri=uri) - >>> file_client_args = {'backend': 'petrel'} - >>> file_client = FileClient.infer_client(file_client_args) - - Returns: - FileClient: Instantiated FileClient object. - """ - assert file_client_args is not None or uri is not None - if file_client_args is None: - file_prefix = cls.parse_uri_prefix(uri) # type: ignore - return cls(prefix=file_prefix) - else: - return cls(**file_client_args) - - @classmethod - def _register_backend(cls, name, backend, force=False, prefixes=None): - if not isinstance(name, str): - raise TypeError('the backend name should be a string, ' - f'but got {type(name)}') - if not inspect.isclass(backend): - raise TypeError( - f'backend should be a class but got {type(backend)}') - if not issubclass(backend, BaseStorageBackend): - raise TypeError( - f'backend {backend} is not a subclass of BaseStorageBackend') - if not force and name in cls._backends: - raise KeyError( - f'{name} is already registered as a storage backend, ' - 'add "force=True" if you want to override it') - - if name in cls._backends and force: - cls._overridden_backends.add(name) - cls._backends[name] = backend - - if prefixes is not None: - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if prefix not in cls._prefix_to_backends: - cls._prefix_to_backends[prefix] = backend - elif (prefix in cls._prefix_to_backends) and force: - cls._overridden_prefixes.add(prefix) - cls._prefix_to_backends[prefix] = backend - else: - raise KeyError( - f'{prefix} is already registered as a storage backend,' - ' add "force=True" if you want to override it') - - @classmethod - def register_backend(cls, name, backend=None, force=False, prefixes=None): - """Register a backend to FileClient. - - This method can be used as a normal class method or a decorator. - - .. code-block:: python - - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - FileClient.register_backend('new', NewBackend) - - or - - .. code-block:: python - - @FileClient.register_backend('new') - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - Args: - name (str): The name of the registered backend. - backend (class, optional): The backend class to be registered, - which must be a subclass of :class:`BaseStorageBackend`. - When this method is used as a decorator, backend is None. - Defaults to None. - force (bool, optional): Whether to override the backend if the name - has already been registered. Defaults to False. - prefixes (str or list[str] or tuple[str], optional): The prefixes - of the registered storage backend. Default: None. - `New in version 1.3.15.` - """ - if backend is not None: - cls._register_backend( - name, backend, force=force, prefixes=prefixes) - return - - def _register(backend_cls): - cls._register_backend( - name, backend_cls, force=force, prefixes=prefixes) - return backend_cls - - return _register - - def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: - """Read data from a given ``filepath`` with 'rb' mode. - - Note: - There are two types of return values for ``get``, one is ``bytes`` - and the other is ``memoryview``. The advantage of using memoryview - is that you can avoid copying, and if you want to convert it to - ``bytes``, you can use ``.tobytes()``. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes | memoryview: Expected bytes object or a memory view of the - bytes object. - """ - return self.client.get(filepath) - - def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return self.client.get_text(filepath, encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` should create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - self.client.put(obj, filepath) - - def put_text(self, obj: str, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` should create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str, optional): The encoding format used to open the - `filepath`. Default: 'utf-8'. - """ - self.client.put_text(obj, filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str, Path): Path to be removed. - """ - self.client.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return self.client.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return self.client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return self.client.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return self.client.join_path(filepath, *filepaths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download data from ``filepath`` and write the data to local path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Note: - If the ``filepath`` is a local path, just return itself. - - .. warning:: - ``get_local_path`` is an experimental interface that may change in - the future. - - Args: - filepath (str or Path): Path to be read data. - - Examples: - >>> file_client = FileClient(prefix='s3') - >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one path. - """ - with self.client.get_local_path(str(filepath)) as local_path: - yield local_path - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, - suffix, recursive) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/__init__.py deleted file mode 100644 index aa24d9197..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseFileHandler -from .json_handler import JsonHandler -from .pickle_handler import PickleHandler -from .yaml_handler import YamlHandler - -__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/base.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/base.py deleted file mode 100644 index 288878bc5..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseFileHandler(metaclass=ABCMeta): - # `str_like` is a flag to indicate whether the type of file object is - # str-like object or bytes-like object. Pickle only processes bytes-like - # objects but json only processes str-like object. If it is str-like - # object, `StringIO` will be used to process the buffer. - str_like = True - - @abstractmethod - def load_from_fileobj(self, file, **kwargs): - pass - - @abstractmethod - def dump_to_fileobj(self, obj, file, **kwargs): - pass - - @abstractmethod - def dump_to_str(self, obj, **kwargs): - pass - - def load_from_path(self, filepath, mode='r', **kwargs): - with open(filepath, mode) as f: - return self.load_from_fileobj(f, **kwargs) - - def dump_to_path(self, obj, filepath, mode='w', **kwargs): - with open(filepath, mode) as f: - self.dump_to_fileobj(obj, f, **kwargs) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/json_handler.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/json_handler.py deleted file mode 100644 index 18d4f15f7..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/json_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -import numpy as np - -from .base import BaseFileHandler - - -def set_default(obj): - """Set default json values for non-serializable values. - - It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. - It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, - etc.) into plain numbers of plain python built-in types. - """ - if isinstance(obj, (set, range)): - return list(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, np.generic): - return obj.item() - raise TypeError(f'{type(obj)} is unsupported for json dump') - - -class JsonHandler(BaseFileHandler): - - def load_from_fileobj(self, file): - return json.load(file) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('default', set_default) - json.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('default', set_default) - return json.dumps(obj, **kwargs) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/pickle_handler.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/pickle_handler.py deleted file mode 100644 index b37c79bed..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/pickle_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import pickle - -from .base import BaseFileHandler - - -class PickleHandler(BaseFileHandler): - - str_like = False - - def load_from_fileobj(self, file, **kwargs): - return pickle.load(file, **kwargs) - - def load_from_path(self, filepath, **kwargs): - return super(PickleHandler, self).load_from_path( - filepath, mode='rb', **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('protocol', 2) - return pickle.dumps(obj, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('protocol', 2) - pickle.dump(obj, file, **kwargs) - - def dump_to_path(self, obj, filepath, **kwargs): - super(PickleHandler, self).dump_to_path( - obj, filepath, mode='wb', **kwargs) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/yaml_handler.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/yaml_handler.py deleted file mode 100644 index 60911e7e6..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/handlers/yaml_handler.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import yaml - -try: - from yaml import CDumper as Dumper - from yaml import CLoader as Loader -except ImportError: - from yaml import Loader, Dumper - -from .base import BaseFileHandler # isort:skip - - -class YamlHandler(BaseFileHandler): - - def load_from_fileobj(self, file, **kwargs): - kwargs.setdefault('Loader', Loader) - return yaml.load(file, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('Dumper', Dumper) - yaml.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('Dumper', Dumper) - return yaml.dump(obj, **kwargs) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/io.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/io.py deleted file mode 100644 index aaefde58a..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/io.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from io import BytesIO, StringIO -from pathlib import Path - -from ..utils import is_list_of, is_str -from .file_client import FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler - -file_handlers = { - 'json': JsonHandler(), - 'yaml': YamlHandler(), - 'yml': YamlHandler(), - 'pickle': PickleHandler(), - 'pkl': PickleHandler() -} - - -def load(file, file_format=None, file_client_args=None, **kwargs): - """Load data from json/yaml/pickle files. - - This method provides a unified api for loading data from serialized files. - - Note: - In v1.3.16 and later, ``load`` supports loading data from serialized - files those can be storaged in different backends. - - Args: - file (str or :obj:`Path` or file-like object): Filename or a file-like - object. - file_format (str, optional): If not specified, the file format will be - inferred from the file extension, otherwise use the specified one. - Currently supported formats include "json", "yaml/yml" and - "pickle/pkl". - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> load('/path/of/your/file') # file is storaged in disk - >>> load('https://path/of/your/file') # file is storaged in Internet - >>> load('s3://path/of/your/file') # file is storaged in petrel - - Returns: - The content from the file. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None and is_str(file): - file_format = file.split('.')[-1] - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO(file_client.get_text(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - else: - with BytesIO(file_client.get(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - elif hasattr(file, 'read'): - obj = handler.load_from_fileobj(file, **kwargs) - else: - raise TypeError('"file" must be a filepath str or a file-object') - return obj - - -def dump(obj, file=None, file_format=None, file_client_args=None, **kwargs): - """Dump data to json/yaml/pickle strings or files. - - This method provides a unified api for dumping data as strings or to files, - and also supports custom arguments for each file format. - - Note: - In v1.3.16 and later, ``dump`` supports dumping data as strings or to - files which is saved to different backends. - - Args: - obj (any): The python object to be dumped. - file (str or :obj:`Path` or file-like object, optional): If not - specified, then the object is dumped to a str, otherwise to a file - specified by the filename or file-like object. - file_format (str, optional): Same as :func:`load`. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dump('hello world', '/path/of/your/file') # disk - >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel - - Returns: - bool: True for success, False otherwise. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None: - if is_str(file): - file_format = file.split('.')[-1] - elif file is None: - raise ValueError( - 'file_format must be specified since file is None') - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if file is None: - return handler.dump_to_str(obj, **kwargs) - elif is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put_text(f.getvalue(), file) - else: - with BytesIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put(f.getvalue(), file) - elif hasattr(file, 'write'): - handler.dump_to_fileobj(obj, file, **kwargs) - else: - raise TypeError('"file" must be a filename str or a file-object') - - -def _register_handler(handler, file_formats): - """Register a handler for some file extensions. - - Args: - handler (:obj:`BaseFileHandler`): Handler to be registered. - file_formats (str or list[str]): File formats to be handled by this - handler. - """ - if not isinstance(handler, BaseFileHandler): - raise TypeError( - f'handler must be a child of BaseFileHandler, not {type(handler)}') - if isinstance(file_formats, str): - file_formats = [file_formats] - if not is_list_of(file_formats, str): - raise TypeError('file_formats must be a str or a list of str') - for ext in file_formats: - file_handlers[ext] = handler - - -def register_handler(file_formats, **kwargs): - - def wrap(cls): - _register_handler(cls(**kwargs), file_formats) - return cls - - return wrap diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/parse.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/parse.py deleted file mode 100644 index f60f0d611..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/fileio/parse.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from io import StringIO - -from .file_client import FileClient - - -def list_from_file(filename, - prefix='', - offset=0, - max_num=0, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a list of strings. - - Note: - In v1.3.16 and later, ``list_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a list for strings. - - Args: - filename (str): Filename. - prefix (str): The prefix to be inserted to the beginning of each item. - offset (int): The offset of lines. - max_num (int): The maximum number of lines to be read, - zeros and negatives mean no limitation. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> list_from_file('/path/of/your/file') # disk - ['hello', 'world'] - >>> list_from_file('s3://path/of/your/file') # ceph or petrel - ['hello', 'world'] - - Returns: - list[str]: A list of strings. - """ - cnt = 0 - item_list = [] - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for _ in range(offset): - f.readline() - for line in f: - if 0 < max_num <= cnt: - break - item_list.append(prefix + line.rstrip('\n\r')) - cnt += 1 - return item_list - - -def dict_from_file(filename, - key_type=str, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a dict. - - Each line of the text file will be two or more columns split by - whitespaces or tabs. The first column will be parsed as dict keys, and - the following columns will be parsed as dict values. - - Note: - In v1.3.16 and later, ``dict_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a dict. - - Args: - filename(str): Filename. - key_type(type): Type of the dict keys. str is user by default and - type conversion will be performed if specified. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dict_from_file('/path/of/your/file') # disk - {'key1': 'value1', 'key2': 'value2'} - >>> dict_from_file('s3://path/of/your/file') # ceph or petrel - {'key1': 'value1', 'key2': 'value2'} - - Returns: - dict: The parsed contents. - """ - mapping = {} - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for line in f: - items = line.rstrip('\n').split() - assert len(items) >= 2 - key = key_type(items[0]) - val = items[1:] if len(items) > 2 else items[1] - mapping[key] = val - return mapping diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/image/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/image/__init__.py deleted file mode 100644 index d0051d609..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/image/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, - gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, - rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) -from .geometric import (cutout, imcrop, imflip, imflip_, impad, - impad_to_multiple, imrescale, imresize, imresize_like, - imresize_to_multiple, imrotate, imshear, imtranslate, - rescale_size) -from .io import imfrombytes, imread, imwrite, supported_backends, use_backend -from .misc import tensor2imgs -from .photometric import (adjust_brightness, adjust_color, adjust_contrast, - adjust_lighting, adjust_sharpness, auto_contrast, - clahe, imdenormalize, imequalize, iminvert, - imnormalize, imnormalize_, lut_transform, posterize, - solarize) - -__all__ = [ - 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', - 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', - 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', - 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', - 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', - 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', - 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', - 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', - 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', - 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/image/colorspace.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/image/colorspace.py deleted file mode 100644 index 4337720ea..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/image/colorspace.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def imconvert(img, src, dst): - """Convert an image from the src colorspace to dst colorspace. - - Args: - img (ndarray): The input image. - src (str): The source colorspace, e.g., 'rgb', 'hsv'. - dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. - - Returns: - ndarray: The converted image. - """ - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - out_img = cv2.cvtColor(img, code) - return out_img - - -def bgr2gray(img, keepdim=False): - """Convert a BGR image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def rgb2gray(img, keepdim=False): - """Convert a RGB image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def gray2bgr(img): - """Convert a grayscale image to BGR image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted BGR image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return out_img - - -def gray2rgb(img): - """Convert a grayscale image to RGB image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted RGB image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) - return out_img - - -def _convert_input_type_range(img): - """Convert the type and range of the input image. - - It converts the input image to np.float32 type and range of [0, 1]. - It is mainly used for pre-processing the input image in colorspace - conversion functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - (ndarray): The converted image with type of np.float32 and range of - [0, 1]. - """ - img_type = img.dtype - img = img.astype(np.float32) - if img_type == np.float32: - pass - elif img_type == np.uint8: - img /= 255. - else: - raise TypeError('The img type should be np.float32 or np.uint8, ' - f'but got {img_type}') - return img - - -def _convert_output_type_range(img, dst_type): - """Convert the type and range of the image according to dst_type. - - It converts the image to desired type and range. If `dst_type` is np.uint8, - images will be converted to np.uint8 type with range [0, 255]. If - `dst_type` is np.float32, it converts the image to np.float32 type with - range [0, 1]. - It is mainly used for post-processing images in colorspace conversion - functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The image to be converted with np.float32 type and - range [0, 255]. - dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it - converts the image to np.uint8 type with range [0, 255]. If - dst_type is np.float32, it converts the image to np.float32 type - with range [0, 1]. - - Returns: - (ndarray): The converted image with desired type and range. - """ - if dst_type not in (np.uint8, np.float32): - raise TypeError('The dst_type should be np.float32 or np.uint8, ' - f'but got {dst_type}') - if dst_type == np.uint8: - img = img.round() - else: - img /= 255. - return img.astype(dst_type) - - -def rgb2ycbcr(img, y_only=False): - """Convert a RGB image to YCbCr image. - - This function produces the same results as Matlab's `rgb2ycbcr` function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 - else: - out_img = np.matmul( - img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], - [24.966, 112.0, -18.214]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def bgr2ycbcr(img, y_only=False): - """Convert a BGR image to YCbCr image. - - The bgr version of rgb2ycbcr. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 - else: - out_img = np.matmul( - img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], - [65.481, -37.797, 112.0]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2rgb(img): - """Convert a YCbCr image to RGB image. - - This function produces the same results as Matlab's ycbcr2rgb function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted RGB image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0, -0.00153632, 0.00791071], - [0.00625893, -0.00318811, 0]]) * 255.0 + [ - -222.921, 135.576, -276.836 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2bgr(img): - """Convert a YCbCr image to BGR image. - - The bgr version of ycbcr2rgb. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted BGR image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0.00791071, -0.00153632, 0], - [0, -0.00318811, 0.00625893]]) * 255.0 + [ - -276.836, 135.576, -222.921 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def convert_color_factory(src, dst): - - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - - def convert_color(img): - out_img = cv2.cvtColor(img, code) - return out_img - - convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} - image. - - Args: - img (ndarray or str): The input image. - - Returns: - ndarray: The converted {dst.upper()} image. - """ - - return convert_color - - -bgr2rgb = convert_color_factory('bgr', 'rgb') - -rgb2bgr = convert_color_factory('rgb', 'bgr') - -bgr2hsv = convert_color_factory('bgr', 'hsv') - -hsv2bgr = convert_color_factory('hsv', 'bgr') - -bgr2hls = convert_color_factory('bgr', 'hls') - -hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/image/geometric.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/image/geometric.py deleted file mode 100644 index 4c423bf2a..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/image/geometric.py +++ /dev/null @@ -1,741 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers - -import cv2 -import numpy as np - -from ..utils import to_2tuple -from .io import imread_backend - -try: - from PIL import Image -except ImportError: - Image = None - - -def _scale_size(size, scale): - """Rescale a size by a ratio. - - Args: - size (tuple[int]): (w, h). - scale (float | tuple(float)): Scaling factor. - - Returns: - tuple[int]: scaled size. - """ - if isinstance(scale, (float, int)): - scale = (scale, scale) - w, h = size - return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) - - -cv2_interp_codes = { - 'nearest': cv2.INTER_NEAREST, - 'bilinear': cv2.INTER_LINEAR, - 'bicubic': cv2.INTER_CUBIC, - 'area': cv2.INTER_AREA, - 'lanczos': cv2.INTER_LANCZOS4 -} - -# Pillow >=v9.1.0 use a slightly different naming scheme for filters. -# Set pillow_interp_codes according to the naming scheme used. -if Image is not None: - if hasattr(Image, 'Resampling'): - pillow_interp_codes = { - 'nearest': Image.Resampling.NEAREST, - 'bilinear': Image.Resampling.BILINEAR, - 'bicubic': Image.Resampling.BICUBIC, - 'box': Image.Resampling.BOX, - 'lanczos': Image.Resampling.LANCZOS, - 'hamming': Image.Resampling.HAMMING - } - else: - pillow_interp_codes = { - 'nearest': Image.NEAREST, - 'bilinear': Image.BILINEAR, - 'bicubic': Image.BICUBIC, - 'box': Image.BOX, - 'lanczos': Image.LANCZOS, - 'hamming': Image.HAMMING - } - - -def imresize(img, - size, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image to a given size. - - Args: - img (ndarray): The input image. - size (tuple[int]): Target size (w, h). - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if backend is None: - backend = imread_backend - if backend not in ['cv2', 'pillow']: - raise ValueError(f'backend: {backend} is not supported for resize.' - f"Supported backends are 'cv2', 'pillow'") - - if backend == 'pillow': - assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' - pil_image = Image.fromarray(img) - pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) - resized_img = np.array(pil_image) - else: - resized_img = cv2.resize( - img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) - if not return_scale: - return resized_img - else: - w_scale = size[0] / w - h_scale = size[1] / h - return resized_img, w_scale, h_scale - - -def imresize_to_multiple(img, - divisor, - size=None, - scale_factor=None, - keep_ratio=False, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image according to a given size or scale factor and then rounds - up the the resized or rescaled image size to the nearest value that can be - divided by the divisor. - - Args: - img (ndarray): The input image. - divisor (int | tuple): Resized image size will be a multiple of - divisor. If divisor is a tuple, divisor should be - (w_divisor, h_divisor). - size (None | int | tuple[int]): Target size (w, h). Default: None. - scale_factor (None | float | tuple[float]): Multiplier for spatial - size. Should match input size if it is a tuple and the 2D style is - (w_scale_factor, h_scale_factor). Default: None. - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: False. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if size is not None and scale_factor is not None: - raise ValueError('only one of size or scale_factor should be defined') - elif size is None and scale_factor is None: - raise ValueError('one of size or scale_factor should be defined') - elif size is not None: - size = to_2tuple(size) - if keep_ratio: - size = rescale_size((w, h), size, return_scale=False) - else: - size = _scale_size((w, h), scale_factor) - - divisor = to_2tuple(divisor) - size = tuple([int(np.ceil(s / d)) * d for s, d in zip(size, divisor)]) - resized_img, w_scale, h_scale = imresize( - img, - size, - return_scale=True, - interpolation=interpolation, - out=out, - backend=backend) - if return_scale: - return resized_img, w_scale, h_scale - else: - return resized_img - - -def imresize_like(img, - dst_img, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image to the same size of a given image. - - Args: - img (ndarray): The input image. - dst_img (ndarray): The target image. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = dst_img.shape[:2] - return imresize(img, (w, h), return_scale, interpolation, backend=backend) - - -def rescale_size(old_size, scale, return_scale=False): - """Calculate the new size to be rescaled to. - - Args: - old_size (tuple[int]): The old size (w, h) of image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image size. - - Returns: - tuple[int]: The new rescaled image size. - """ - w, h = old_size - if isinstance(scale, (float, int)): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - scale_factor = scale - elif isinstance(scale, tuple): - max_long_edge = max(scale) - max_short_edge = min(scale) - scale_factor = min(max_long_edge / max(h, w), - max_short_edge / min(h, w)) - else: - raise TypeError( - f'Scale must be a number or tuple of int, but got {type(scale)}') - - new_size = _scale_size((w, h), scale_factor) - - if return_scale: - return new_size, scale_factor - else: - return new_size - - -def imrescale(img, - scale, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image while keeping the aspect ratio. - - Args: - img (ndarray): The input image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - ndarray: The rescaled image. - """ - h, w = img.shape[:2] - new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) - rescaled_img = imresize( - img, new_size, interpolation=interpolation, backend=backend) - if return_scale: - return rescaled_img, scale_factor - else: - return rescaled_img - - -def imflip(img, direction='horizontal'): - """Flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image. - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return np.flip(img, axis=1) - elif direction == 'vertical': - return np.flip(img, axis=0) - else: - return np.flip(img, axis=(0, 1)) - - -def imflip_(img, direction='horizontal'): - """Inplace flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image (inplace). - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return cv2.flip(img, 1, img) - elif direction == 'vertical': - return cv2.flip(img, 0, img) - else: - return cv2.flip(img, -1, img) - - -def imrotate(img, - angle, - center=None, - scale=1.0, - border_value=0, - interpolation='bilinear', - auto_bound=False): - """Rotate an image. - - Args: - img (ndarray): Image to be rotated. - angle (float): Rotation angle in degrees, positive values mean - clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. - scale (float): Isotropic scale factor. - border_value (int): Border value. - interpolation (str): Same as :func:`resize`. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. - - Returns: - ndarray: The rotated image. - """ - if center is not None and auto_bound: - raise ValueError('`auto_bound` conflicts with `center`') - h, w = img.shape[:2] - if center is None: - center = ((w - 1) * 0.5, (h - 1) * 0.5) - assert isinstance(center, tuple) - - matrix = cv2.getRotationMatrix2D(center, -angle, scale) - if auto_bound: - cos = np.abs(matrix[0, 0]) - sin = np.abs(matrix[0, 1]) - new_w = h * sin + w * cos - new_h = h * cos + w * sin - matrix[0, 2] += (new_w - w) * 0.5 - matrix[1, 2] += (new_h - h) * 0.5 - w = int(np.round(new_w)) - h = int(np.round(new_h)) - rotated = cv2.warpAffine( - img, - matrix, (w, h), - flags=cv2_interp_codes[interpolation], - borderValue=border_value) - return rotated - - -def bbox_clip(bboxes, img_shape): - """Clip bboxes to fit the image shape. - - Args: - bboxes (ndarray): Shape (..., 4*k) - img_shape (tuple[int]): (height, width) of the image. - - Returns: - ndarray: Clipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) - cmin[0::2] = img_shape[1] - 1 - cmin[1::2] = img_shape[0] - 1 - clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) - return clipped_bboxes - - -def bbox_scaling(bboxes, scale, clip_shape=None): - """Scaling bboxes w.r.t the box center. - - Args: - bboxes (ndarray): Shape(..., 4). - scale (float): Scaling factor. - clip_shape (tuple[int], optional): If specified, bboxes that exceed the - boundary will be clipped according to the given shape (h, w). - - Returns: - ndarray: Scaled bboxes. - """ - if float(scale) == 1.0: - scaled_bboxes = bboxes.copy() - else: - w = bboxes[..., 2] - bboxes[..., 0] + 1 - h = bboxes[..., 3] - bboxes[..., 1] + 1 - dw = (w * (scale - 1)) * 0.5 - dh = (h * (scale - 1)) * 0.5 - scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) - if clip_shape is not None: - return bbox_clip(scaled_bboxes, clip_shape) - else: - return scaled_bboxes - - -def imcrop(img, bboxes, scale=1.0, pad_fill=None): - """Crop image patches. - - 3 steps: scale the bboxes -> clip bboxes -> crop and pad. - - Args: - img (ndarray): Image to be cropped. - bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. - scale (float, optional): Scale ratio of bboxes, the default value - 1.0 means no padding. - pad_fill (Number | list[Number]): Value to be filled for padding. - Default: None, which means no padding. - - Returns: - list[ndarray] | ndarray: The cropped image patches. - """ - chn = 1 if img.ndim == 2 else img.shape[2] - if pad_fill is not None: - if isinstance(pad_fill, (int, float)): - pad_fill = [pad_fill for _ in range(chn)] - assert len(pad_fill) == chn - - _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes - scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) - clipped_bbox = bbox_clip(scaled_bboxes, img.shape) - - patches = [] - for i in range(clipped_bbox.shape[0]): - x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) - if pad_fill is None: - patch = img[y1:y2 + 1, x1:x2 + 1, ...] - else: - _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) - if chn == 1: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) - else: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) - patch = np.array( - pad_fill, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - x_start = 0 if _x1 >= 0 else -_x1 - y_start = 0 if _y1 >= 0 else -_y1 - w = x2 - x1 + 1 - h = y2 - y1 + 1 - patch[y_start:y_start + h, x_start:x_start + w, - ...] = img[y1:y1 + h, x1:x1 + w, ...] - patches.append(patch) - - if bboxes.ndim == 1: - return patches[0] - else: - return patches - - -def impad(img, - *, - shape=None, - padding=None, - pad_val=0, - padding_mode='constant'): - """Pad the given image to a certain shape or pad on all sides with - specified padding mode and padding value. - - Args: - img (ndarray): Image to be padded. - shape (tuple[int]): Expected padding shape (h, w). Default: None. - padding (int or tuple[int]): Padding on each border. If a single int is - provided this is used to pad all borders. If tuple of length 2 is - provided this is the padding on left/right and top/bottom - respectively. If a tuple of length 4 is provided this is the - padding for the left, top, right and bottom borders respectively. - Default: None. Note that `shape` and `padding` can not be both - set. - pad_val (Number | Sequence[Number]): Values to be filled in padding - areas when padding_mode is 'constant'. Default: 0. - padding_mode (str): Type of padding. Should be: constant, edge, - reflect or symmetric. Default: constant. - - constant: pads with a constant value, this value is specified - with pad_val. - - edge: pads with the last value at the edge of the image. - - reflect: pads with reflection of image without repeating the last - value on the edge. For example, padding [1, 2, 3, 4] with 2 - elements on both sides in reflect mode will result in - [3, 2, 1, 2, 3, 4, 3, 2]. - - symmetric: pads with reflection of image repeating the last value - on the edge. For example, padding [1, 2, 3, 4] with 2 elements on - both sides in symmetric mode will result in - [2, 1, 1, 2, 3, 4, 4, 3] - - Returns: - ndarray: The padded image. - """ - - assert (shape is not None) ^ (padding is not None) - if shape is not None: - width = max(shape[1] - img.shape[1], 0) - height = max(shape[0] - img.shape[0], 0) - padding = (0, 0, width, height) - - # check pad_val - if isinstance(pad_val, tuple): - assert len(pad_val) == img.shape[-1] - elif not isinstance(pad_val, numbers.Number): - raise TypeError('pad_val must be a int or a tuple. ' - f'But received {type(pad_val)}') - - # check padding - if isinstance(padding, tuple) and len(padding) in [2, 4]: - if len(padding) == 2: - padding = (padding[0], padding[1], padding[0], padding[1]) - elif isinstance(padding, numbers.Number): - padding = (padding, padding, padding, padding) - else: - raise ValueError('Padding must be a int or a 2, or 4 element tuple.' - f'But received {padding}') - - # check padding mode - assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] - - border_type = { - 'constant': cv2.BORDER_CONSTANT, - 'edge': cv2.BORDER_REPLICATE, - 'reflect': cv2.BORDER_REFLECT_101, - 'symmetric': cv2.BORDER_REFLECT - } - img = cv2.copyMakeBorder( - img, - padding[1], - padding[3], - padding[0], - padding[2], - border_type[padding_mode], - value=pad_val) - - return img - - -def impad_to_multiple(img, divisor, pad_val=0): - """Pad an image to ensure each edge to be multiple to some number. - - Args: - img (ndarray): Image to be padded. - divisor (int): Padded image edges will be multiple to divisor. - pad_val (Number | Sequence[Number]): Same as :func:`impad`. - - Returns: - ndarray: The padded image. - """ - pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor - pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor - return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) - - -def cutout(img, shape, pad_val=0): - """Randomly cut out a rectangle from the original img. - - Args: - img (ndarray): Image to be cutout. - shape (int | tuple[int]): Expected cutout shape (h, w). If given as a - int, the value will be used for both h and w. - pad_val (int | float | tuple[int | float]): Values to be filled in the - cut area. Defaults to 0. - - Returns: - ndarray: The cutout image. - """ - - channels = 1 if img.ndim == 2 else img.shape[2] - if isinstance(shape, int): - cut_h, cut_w = shape, shape - else: - assert isinstance(shape, tuple) and len(shape) == 2, \ - f'shape must be a int or a tuple with length 2, but got type ' \ - f'{type(shape)} instead.' - cut_h, cut_w = shape - if isinstance(pad_val, (int, float)): - pad_val = tuple([pad_val] * channels) - elif isinstance(pad_val, tuple): - assert len(pad_val) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(pad_val), channels) - else: - raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') - - img_h, img_w = img.shape[:2] - y0 = np.random.uniform(img_h) - x0 = np.random.uniform(img_w) - - y1 = int(max(0, y0 - cut_h / 2.)) - x1 = int(max(0, x0 - cut_w / 2.)) - y2 = min(img_h, y1 + cut_h) - x2 = min(img_w, x1 + cut_w) - - if img.ndim == 2: - patch_shape = (y2 - y1, x2 - x1) - else: - patch_shape = (y2 - y1, x2 - x1, channels) - - img_cutout = img.copy() - patch = np.array( - pad_val, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - img_cutout[y1:y2, x1:x2, ...] = patch - - return img_cutout - - -def _get_shear_matrix(magnitude, direction='horizontal'): - """Generate the shear matrix for transformation. - - Args: - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - - Returns: - ndarray: The shear matrix with dtype float32. - """ - if direction == 'horizontal': - shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) - elif direction == 'vertical': - shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) - return shear_matrix - - -def imshear(img, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear an image. - - Args: - img (ndarray): Image to be sheared with format (h, w) - or (h, w, c). - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The sheared image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`') - shear_matrix = _get_shear_matrix(magnitude, direction) - sheared = cv2.warpAffine( - img, - shear_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. shearing masks whose channels large - # than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return sheared - - -def _get_translate_matrix(offset, direction='horizontal'): - """Generate the translate matrix. - - Args: - offset (int | float): The offset used for translate. - direction (str): The translate direction, either - "horizontal" or "vertical". - - Returns: - ndarray: The translate matrix with dtype float32. - """ - if direction == 'horizontal': - translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) - elif direction == 'vertical': - translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) - return translate_matrix - - -def imtranslate(img, - offset, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Translate an image. - - Args: - img (ndarray): Image to be translated with format - (h, w) or (h, w, c). - offset (int | float): The offset used for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The translated image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`.') - translate_matrix = _get_translate_matrix(offset, direction) - translated = cv2.warpAffine( - img, - translate_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. translating masks whose channels - # large than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return translated diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/image/io.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/image/io.py deleted file mode 100644 index ae81b561a..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/image/io.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os.path as osp -import warnings -from pathlib import Path - -import cv2 -import numpy as np -from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, - IMREAD_UNCHANGED) - -from mmcv.fileio import FileClient -from mmcv.utils import is_filepath, is_str - -try: - from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG -except ImportError: - TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None - -try: - from PIL import Image, ImageOps -except ImportError: - Image = None - -try: - import tifffile -except ImportError: - tifffile = None - -jpeg = None -supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] - -imread_flags = { - 'color': IMREAD_COLOR, - 'grayscale': IMREAD_GRAYSCALE, - 'unchanged': IMREAD_UNCHANGED, - 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, - 'grayscale_ignore_orientation': - IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE -} - -imread_backend = 'cv2' - - -def use_backend(backend): - """Select a backend for image decoding. - - Args: - backend (str): The image decoding backend type. Options are `cv2`, - `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) - and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` - file format. - """ - assert backend in supported_backends - global imread_backend - imread_backend = backend - if imread_backend == 'turbojpeg': - if TurboJPEG is None: - raise ImportError('`PyTurboJPEG` is not installed') - global jpeg - if jpeg is None: - jpeg = TurboJPEG() - elif imread_backend == 'pillow': - if Image is None: - raise ImportError('`Pillow` is not installed') - elif imread_backend == 'tifffile': - if tifffile is None: - raise ImportError('`tifffile` is not installed') - - -def _jpegflag(flag='color', channel_order='bgr'): - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'color': - if channel_order == 'bgr': - return TJPF_BGR - elif channel_order == 'rgb': - return TJCS_RGB - elif flag == 'grayscale': - return TJPF_GRAY - else: - raise ValueError('flag must be "color" or "grayscale"') - - -def _pillow2array(img, flag='color', channel_order='bgr'): - """Convert a pillow image to numpy array. - - Args: - img (:obj:`PIL.Image.Image`): The image loaded using PIL - flag (str): Flags specifying the color type of a loaded image, - candidates are 'color', 'grayscale' and 'unchanged'. - Default to 'color'. - channel_order (str): The channel order of the output image array, - candidates are 'bgr' and 'rgb'. Default to 'bgr'. - - Returns: - np.ndarray: The converted numpy array - """ - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'unchanged': - array = np.array(img) - if array.ndim >= 3 and array.shape[2] >= 3: # color image - array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR - else: - # Handle exif orientation tag - if flag in ['color', 'grayscale']: - img = ImageOps.exif_transpose(img) - # If the image mode is not 'RGB', convert it to 'RGB' first. - if img.mode != 'RGB': - if img.mode != 'LA': - # Most formats except 'LA' can be directly converted to RGB - img = img.convert('RGB') - else: - # When the mode is 'LA', the default conversion will fill in - # the canvas with black, which sometimes shadows black objects - # in the foreground. - # - # Therefore, a random color (124, 117, 104) is used for canvas - img_rgba = img.convert('RGBA') - img = Image.new('RGB', img_rgba.size, (124, 117, 104)) - img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha - if flag in ['color', 'color_ignore_orientation']: - array = np.array(img) - if channel_order != 'rgb': - array = array[:, :, ::-1] # RGB to BGR - elif flag in ['grayscale', 'grayscale_ignore_orientation']: - img = img.convert('L') - array = np.array(img) - else: - raise ValueError( - 'flag must be "color", "grayscale", "unchanged", ' - f'"color_ignore_orientation" or "grayscale_ignore_orientation"' - f' but got {flag}') - return array - - -def imread(img_or_path, - flag='color', - channel_order='bgr', - backend=None, - file_client_args=None): - """Read an image. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Args: - img_or_path (ndarray or str or Path): Either a numpy array or str or - pathlib.Path. If it is a numpy array (loaded image), then - it will be returned as is. - flag (str): Flags specifying the color type of a loaded image, - candidates are `color`, `grayscale`, `unchanged`, - `color_ignore_orientation` and `grayscale_ignore_orientation`. - By default, `cv2` and `pillow` backend would rotate the image - according to its EXIF info unless called with `unchanged` or - `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend - always ignore image's EXIF info regardless of the flag. - The `turbojpeg` backend only supports `color` and `grayscale`. - channel_order (str): Order of channel, candidates are `bgr` and `rgb`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. - If backend is None, the global imread_backend specified by - ``mmcv.use_backend()`` will be used. Default: None. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> import mmcv - >>> img_path = '/path/to/img.jpg' - >>> img = mmcv.imread(img_path) - >>> img = mmcv.imread(img_path, flag='color', channel_order='rgb', - ... backend='cv2') - >>> img = mmcv.imread(img_path, flag='color', channel_order='bgr', - ... backend='pillow') - >>> s3_img_path = 's3://bucket/img.jpg' - >>> # infer the file backend by the prefix s3 - >>> img = mmcv.imread(s3_img_path) - >>> # manually set the file backend petrel - >>> img = mmcv.imread(s3_img_path, file_client_args={ - ... 'backend': 'petrel'}) - >>> http_img_path = 'http://path/to/img.jpg' - >>> img = mmcv.imread(http_img_path) - >>> img = mmcv.imread(http_img_path, file_client_args={ - ... 'backend': 'http'}) - """ - - if isinstance(img_or_path, Path): - img_or_path = str(img_or_path) - - if isinstance(img_or_path, np.ndarray): - return img_or_path - elif is_str(img_or_path): - file_client = FileClient.infer_client(file_client_args, img_or_path) - img_bytes = file_client.get(img_or_path) - return imfrombytes(img_bytes, flag, channel_order, backend) - else: - raise TypeError('"img" must be a numpy array or a str or ' - 'a pathlib.Path object') - - -def imfrombytes(content, flag='color', channel_order='bgr', backend=None): - """Read an image from bytes. - - Args: - content (bytes): Image bytes got from files or other streams. - flag (str): Same as :func:`imread`. - channel_order (str): The channel order of the output, candidates - are 'bgr' and 'rgb'. Default to 'bgr'. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. If backend is - None, the global imread_backend specified by ``mmcv.use_backend()`` - will be used. Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> img_path = '/path/to/img.jpg' - >>> with open(img_path, 'rb') as f: - >>> img_buff = f.read() - >>> img = mmcv.imfrombytes(img_buff) - >>> img = mmcv.imfrombytes(img_buff, flag='color', channel_order='rgb') - >>> img = mmcv.imfrombytes(img_buff, backend='pillow') - >>> img = mmcv.imfrombytes(img_buff, backend='cv2') - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError( - f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow', 'tifffile'") - if backend == 'turbojpeg': - img = jpeg.decode(content, _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - with io.BytesIO(content) as buff: - img = Image.open(buff) - img = _pillow2array(img, flag, channel_order) - return img - elif backend == 'tifffile': - with io.BytesIO(content) as buff: - img = tifffile.imread(buff) - return img - else: - img_np = np.frombuffer(content, np.uint8) - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imdecode(img_np, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - - -def imwrite(img, - file_path, - params=None, - auto_mkdir=None, - file_client_args=None): - """Write image to file. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Warning: - The parameter `auto_mkdir` will be deprecated in the future and every - file clients will make directory automatically. - - Args: - img (ndarray): Image array to be written. - file_path (str): Image file path. - params (None or list): Same as opencv :func:`imwrite` interface. - auto_mkdir (bool): If the parent folder of `file_path` does not exist, - whether to create it automatically. It will be deprecated. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - bool: Successful or not. - - Examples: - >>> # write to hard disk client - >>> ret = mmcv.imwrite(img, '/path/to/img.jpg') - >>> # infer the file backend by the prefix s3 - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg') - >>> # manually set the file backend petrel - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg', file_client_args={ - ... 'backend': 'petrel'}) - """ - assert is_filepath(file_path) - file_path = str(file_path) - if auto_mkdir is not None: - warnings.warn( - 'The parameter `auto_mkdir` will be deprecated in the future and ' - 'every file clients will make directory automatically.') - file_client = FileClient.infer_client(file_client_args, file_path) - img_ext = osp.splitext(file_path)[-1] - # Encode image according to image suffix. - # For example, if image path is '/path/your/img.jpg', the encode - # format is '.jpg'. - flag, img_buff = cv2.imencode(img_ext, img, params) - file_client.put(img_buff.tobytes(), file_path) - return flag diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/image/misc.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/image/misc.py deleted file mode 100644 index 43934a689..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/image/misc.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - -import mmcv - -try: - import torch -except ImportError: - torch = None - - -def tensor2imgs(tensor, mean=None, std=None, to_rgb=True): - """Convert tensor to 3-channel images or 1-channel gray images. - - Args: - tensor (torch.Tensor): Tensor that contains multiple images, shape ( - N, C, H, W). :math:`C` can be either 3 or 1. - mean (tuple[float], optional): Mean of images. If None, - (0, 0, 0) will be used for tensor with 3-channel, - while (0, ) for tensor with 1-channel. Defaults to None. - std (tuple[float], optional): Standard deviation of images. If None, - (1, 1, 1) will be used for tensor with 3-channel, - while (1, ) for tensor with 1-channel. Defaults to None. - to_rgb (bool, optional): Whether the tensor was converted to RGB - format in the first place. If so, convert it back to BGR. - For the tensor with 1 channel, it must be False. Defaults to True. - - Returns: - list[np.ndarray]: A list that contains multiple images. - """ - - if torch is None: - raise RuntimeError('pytorch is not installed') - assert torch.is_tensor(tensor) and tensor.ndim == 4 - channels = tensor.size(1) - assert channels in [1, 3] - if mean is None: - mean = (0, ) * channels - if std is None: - std = (1, ) * channels - assert (channels == len(mean) == len(std) == 3) or \ - (channels == len(mean) == len(std) == 1 and not to_rgb) - - num_imgs = tensor.size(0) - mean = np.array(mean, dtype=np.float32) - std = np.array(std, dtype=np.float32) - imgs = [] - for img_id in range(num_imgs): - img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) - img = mmcv.imdenormalize( - img, mean, std, to_bgr=to_rgb).astype(np.uint8) - imgs.append(np.ascontiguousarray(img)) - return imgs diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/image/photometric.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/image/photometric.py deleted file mode 100644 index 5085d0120..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/image/photometric.py +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - -from ..utils import is_tuple_of -from .colorspace import bgr2gray, gray2bgr - - -def imnormalize(img, mean, std, to_rgb=True): - """Normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - img = img.copy().astype(np.float32) - return imnormalize_(img, mean, std, to_rgb) - - -def imnormalize_(img, mean, std, to_rgb=True): - """Inplace normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - # cv2 inplace normalization does not accept uint8 - assert img.dtype != np.uint8 - mean = np.float64(mean.reshape(1, -1)) - stdinv = 1 / np.float64(std.reshape(1, -1)) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - cv2.subtract(img, mean, img) # inplace - cv2.multiply(img, stdinv, img) # inplace - return img - - -def imdenormalize(img, mean, std, to_bgr=True): - assert img.dtype != np.uint8 - mean = mean.reshape(1, -1).astype(np.float64) - std = std.reshape(1, -1).astype(np.float64) - img = cv2.multiply(img, std) # make a copy - cv2.add(img, mean, img) # inplace - if to_bgr: - cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace - return img - - -def iminvert(img): - """Invert (negate) an image. - - Args: - img (ndarray): Image to be inverted. - - Returns: - ndarray: The inverted image. - """ - return np.full_like(img, 255) - img - - -def solarize(img, thr=128): - """Solarize an image (invert all pixel values above a threshold) - - Args: - img (ndarray): Image to be solarized. - thr (int): Threshold for solarizing (0 - 255). - - Returns: - ndarray: The solarized image. - """ - img = np.where(img < thr, img, 255 - img) - return img - - -def posterize(img, bits): - """Posterize an image (reduce the number of bits for each color channel) - - Args: - img (ndarray): Image to be posterized. - bits (int): Number of bits (1 to 8) to use for posterizing. - - Returns: - ndarray: The posterized image. - """ - shift = 8 - bits - img = np.left_shift(np.right_shift(img, shift), shift) - return img - - -def adjust_color(img, alpha=1, beta=None, gamma=0): - r"""It blends the source image and its gray image: - - .. math:: - output = img * alpha + gray\_img * beta + gamma - - Args: - img (ndarray): The input source image. - alpha (int | float): Weight for the source image. Default 1. - beta (int | float): Weight for the converted gray image. - If None, it's assigned the value (1 - `alpha`). - gamma (int | float): Scalar added to each sum. - Same as :func:`cv2.addWeighted`. Default 0. - - Returns: - ndarray: Colored image which has the same size and dtype as input. - """ - gray_img = bgr2gray(img) - gray_img = np.tile(gray_img[..., None], [1, 1, 3]) - if beta is None: - beta = 1 - alpha - colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) - if not colored_img.dtype == np.uint8: - # Note when the dtype of `img` is not the default `np.uint8` - # (e.g. np.float32), the value in `colored_img` got from cv2 - # is not guaranteed to be in range [0, 255], so here clip - # is needed. - colored_img = np.clip(colored_img, 0, 255) - return colored_img - - -def imequalize(img): - """Equalize the image histogram. - - This function applies a non-linear mapping to the input image, - in order to create a uniform distribution of grayscale values - in the output image. - - Args: - img (ndarray): Image to be equalized. - - Returns: - ndarray: The equalized image. - """ - - def _scale_channel(im, c): - """Scale the data in the corresponding channel.""" - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # For computing the step, filter out the nonzeros. - nonzero_histo = histo[histo > 0] - step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 - if not step: - lut = np.array(range(256)) - else: - # Compute the cumulative sum, shifted by step // 2 - # and then normalized by step. - lut = (np.cumsum(histo) + (step // 2)) // step - # Shift lut, prepending with 0. - lut = np.concatenate([[0], lut[:-1]], 0) - # handle potential integer overflow - lut[lut > 255] = 255 - # If step is zero, return the original image. - # Otherwise, index from lut. - return np.where(np.equal(step, 0), im, lut[im]) - - # Scales each channel independently and then stacks - # the result. - s1 = _scale_channel(img, 0) - s2 = _scale_channel(img, 1) - s3 = _scale_channel(img, 2) - equalized_img = np.stack([s1, s2, s3], axis=-1) - return equalized_img.astype(img.dtype) - - -def adjust_brightness(img, factor=1.): - """Adjust image brightness. - - This function controls the brightness of an image. An - enhancement factor of 0.0 gives a black image. - A factor of 1.0 gives the original image. This function - blends the source image and the degenerated black image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be brightened. - factor (float): A value controls the enhancement. - Factor 1.0 returns the original image, lower - factors mean less color (brightness, contrast, - etc), and higher values more. Default 1. - - Returns: - ndarray: The brightened image. - """ - degenerated = np.zeros_like(img) - # Note manually convert the dtype to np.float32, to - # achieve as close results as PIL.ImageEnhance.Brightness. - # Set beta=1-factor, and gamma=0 - brightened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - brightened_img = np.clip(brightened_img, 0, 255) - return brightened_img.astype(img.dtype) - - -def adjust_contrast(img, factor=1.): - """Adjust image contrast. - - This function controls the contrast of an image. An - enhancement factor of 0.0 gives a solid grey - image. A factor of 1.0 gives the original image. It - blends the source image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be contrasted. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - - Returns: - ndarray: The contrasted image. - """ - gray_img = bgr2gray(img) - hist = np.histogram(gray_img, 256, (0, 255))[0] - mean = round(np.sum(gray_img) / np.sum(hist)) - degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) - degenerated = gray2bgr(degenerated) - contrasted_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - contrasted_img = np.clip(contrasted_img, 0, 255) - return contrasted_img.astype(img.dtype) - - -def auto_contrast(img, cutoff=0): - """Auto adjust image contrast. - - This function maximize (normalize) image contrast by first removing cutoff - percent of the lightest and darkest pixels from the histogram and remapping - the image so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - Args: - img (ndarray): Image to be contrasted. BGR order. - cutoff (int | float | tuple): The cutoff percent of the lightest and - darkest pixels to be removed. If given as tuple, it shall be - (low, high). Otherwise, the single value will be used for both. - Defaults to 0. - - Returns: - ndarray: The contrasted image. - """ - - def _auto_contrast_channel(im, c, cutoff): - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # Remove cut-off percent pixels from histo - histo_sum = np.cumsum(histo) - cut_low = histo_sum[-1] * cutoff[0] // 100 - cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 - histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low - histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) - - # Compute mapping - low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] - # If all the values have been cut off, return the origin img - if low >= high: - return im - scale = 255.0 / (high - low) - offset = -low * scale - lut = np.array(range(256)) - lut = lut * scale + offset - lut = np.clip(lut, 0, 255) - return lut[im] - - if isinstance(cutoff, (int, float)): - cutoff = (cutoff, cutoff) - else: - assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ - f'float or tuple, but got {type(cutoff)} instead.' - # Auto adjusts contrast for each channel independently and then stacks - # the result. - s1 = _auto_contrast_channel(img, 0, cutoff) - s2 = _auto_contrast_channel(img, 1, cutoff) - s3 = _auto_contrast_channel(img, 2, cutoff) - contrasted_img = np.stack([s1, s2, s3], axis=-1) - return contrasted_img.astype(img.dtype) - - -def adjust_sharpness(img, factor=1., kernel=None): - """Adjust image sharpness. - - This function controls the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image. A - factor of 1.0 gives the original image. And a factor - of 2.0 gives a sharpened image. It blends the source - image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be sharpened. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - kernel (np.ndarray, optional): Filter kernel to be applied on the img - to obtain the degenerated img. Defaults to None. - - Note: - No value sanity check is enforced on the kernel set by users. So with - an inappropriate kernel, the ``adjust_sharpness`` may fail to perform - the function its name indicates but end up performing whatever - transform determined by the kernel. - - Returns: - ndarray: The sharpened image. - """ - - if kernel is None: - # adopted from PIL.ImageFilter.SMOOTH - kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 - assert isinstance(kernel, np.ndarray), \ - f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' - assert kernel.ndim == 2, \ - f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' - - degenerated = cv2.filter2D(img, -1, kernel) - sharpened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - sharpened_img = np.clip(sharpened_img, 0, 255) - return sharpened_img.astype(img.dtype) - - -def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): - """AlexNet-style PCA jitter. - - This data augmentation is proposed in `ImageNet Classification with Deep - Convolutional Neural Networks - `_. - - Args: - img (ndarray): Image to be adjusted lighting. BGR order. - eigval (ndarray): the eigenvalue of the convariance matrix of pixel - values, respectively. - eigvec (ndarray): the eigenvector of the convariance matrix of pixel - values, respectively. - alphastd (float): The standard deviation for distribution of alpha. - Defaults to 0.1 - to_rgb (bool): Whether to convert img to rgb. - - Returns: - ndarray: The adjusted image. - """ - assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ - f'eigval and eigvec should both be of type np.ndarray, got ' \ - f'{type(eigval)} and {type(eigvec)} instead.' - - assert eigval.ndim == 1 and eigvec.ndim == 2 - assert eigvec.shape == (3, eigval.shape[0]) - n_eigval = eigval.shape[0] - assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ - f'got {type(alphastd)} instead.' - - img = img.copy().astype(np.float32) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - - alpha = np.random.normal(0, alphastd, n_eigval) - alter = eigvec \ - * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ - * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) - alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) - img_adjusted = img + alter - return img_adjusted - - -def lut_transform(img, lut_table): - """Transform array by look-up table. - - The function lut_transform fills the output array with values from the - look-up table. Indices of the entries are taken from the input array. - - Args: - img (ndarray): Image to be transformed. - lut_table (ndarray): look-up table of 256 elements; in case of - multi-channel input array, the table should either have a single - channel (in this case the same table is used for all channels) or - the same number of channels as in the input array. - - Returns: - ndarray: The transformed image. - """ - assert isinstance(img, np.ndarray) - assert 0 <= np.min(img) and np.max(img) <= 255 - assert isinstance(lut_table, np.ndarray) - assert lut_table.shape == (256, ) - - return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) - - -def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - img (ndarray): Image to be processed. - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - - Returns: - ndarray: The processed image. - """ - assert isinstance(img, np.ndarray) - assert img.ndim == 2 - assert isinstance(clip_limit, (float, int)) - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - - clahe = cv2.createCLAHE(clip_limit, tile_grid_size) - return clahe.apply(np.array(img, dtype=np.uint8)) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/deprecated.json b/cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/deprecated.json deleted file mode 100644 index 25cf6f28c..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/deprecated.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "resnet50_caffe": "detectron/resnet50_caffe", - "resnet50_caffe_bgr": "detectron2/resnet50_caffe_bgr", - "resnet101_caffe": "detectron/resnet101_caffe", - "resnet101_caffe_bgr": "detectron2/resnet101_caffe_bgr" -} diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/mmcls.json b/cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/mmcls.json deleted file mode 100644 index c073a41d0..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/mmcls.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "vgg11": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_batch256_imagenet_20210208-4271cd6c.pth", - "vgg13": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_batch256_imagenet_20210208-4d1d6080.pth", - "vgg16": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_batch256_imagenet_20210208-db26f1a5.pth", - "vgg19": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_batch256_imagenet_20210208-e6920e4a.pth", - "vgg11_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_bn_batch256_imagenet_20210207-f244902c.pth", - "vgg13_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_bn_batch256_imagenet_20210207-1a8b7864.pth", - "vgg16_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_bn_batch256_imagenet_20210208-7e55cd29.pth", - "vgg19_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_bn_batch256_imagenet_20210208-da620c4f.pth", - "resnet18": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet18_8xb32_in1k_20210831-fbbb1da6.pth", - "resnet34": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet34_8xb32_in1k_20210831-f257d4e6.pth", - "resnet50": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb32_in1k_20210831-ea4938fc.pth", - "resnet101": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet101_8xb32_in1k_20210831-539c63f8.pth", - "resnet152": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet152_8xb32_in1k_20210901-4d7582fa.pth", - "resnet50_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d50_b32x8_imagenet_20210531-db14775a.pth", - "resnet101_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d101_b32x8_imagenet_20210531-6e13bcd3.pth", - "resnet152_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d152_b32x8_imagenet_20210531-278cf22a.pth", - "resnext50_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext50_32x4d_b32x8_imagenet_20210429-56066e27.pth", - "resnext101_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x4d_b32x8_imagenet_20210506-e0fa3dd5.pth", - "resnext101_32x8d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x8d_b32x8_imagenet_20210506-23a247d5.pth", - "resnext152_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext152_32x4d_b32x8_imagenet_20210524-927787be.pth", - "se-resnet50": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet50_batch256_imagenet_20200804-ae206104.pth", - "se-resnet101": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet101_batch256_imagenet_20200804-ba5b51d4.pth", - "resnest50": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest50_imagenet_converted-1ebf0afe.pth", - "resnest101": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest101_imagenet_converted-032caa52.pth", - "resnest200": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest200_imagenet_converted-581a60f2.pth", - "resnest269": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest269_imagenet_converted-59930960.pth", - "shufflenet_v1": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v1/shufflenet_v1_batch1024_imagenet_20200804-5d6cec73.pth", - "shufflenet_v2": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v2/shufflenet_v2_batch1024_imagenet_20200812-5bf4721e.pth", - "mobilenet_v2": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v2/mobilenet_v2_batch256_imagenet_20200708-3b2dc3af.pth", - "mobilenet_v3_small": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_small-8427ecf0.pth", - "mobilenet_v3_large": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_large-3ea3c186.pth", - "repvgg_A0": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A0_3rdparty_4xb64-coslr-120e_in1k_20210909-883ab98c.pth", - "repvgg_A1": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A1_3rdparty_4xb64-coslr-120e_in1k_20210909-24003a24.pth", - "repvgg_A2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A2_3rdparty_4xb64-coslr-120e_in1k_20210909-97d7695a.pth", - "repvgg_B0": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B0_3rdparty_4xb64-coslr-120e_in1k_20210909-446375f4.pth", - "repvgg_B1": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1_3rdparty_4xb64-coslr-120e_in1k_20210909-750cdf67.pth", - "repvgg_B1g2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1g2_3rdparty_4xb64-coslr-120e_in1k_20210909-344f6422.pth", - "repvgg_B1g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1g4_3rdparty_4xb64-coslr-120e_in1k_20210909-d4c1a642.pth", - "repvgg_B2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B2_3rdparty_4xb64-coslr-120e_in1k_20210909-bd6b937c.pth", - "repvgg_B2g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B2g4_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-7b7955f0.pth", - "repvgg_B3": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B3_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-dda968bf.pth", - "repvgg_B3g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B3g4_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-4e54846a.pth", - "repvgg_D2se": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-D2se_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-cf3139b7.pth", - "res2net101_w26": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net101-w26-s4_3rdparty_8xb32_in1k_20210927-870b6c36.pth", - "res2net50_w14": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net50-w14-s8_3rdparty_8xb32_in1k_20210927-bc967bf1.pth", - "res2net50_w26": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net50-w26-s8_3rdparty_8xb32_in1k_20210927-f547a94b.pth", - "swin_tiny": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_tiny_224_b16x64_300e_imagenet_20210616_090925-66df6be6.pth", - "swin_small": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_small_224_b16x64_300e_imagenet_20210615_110219-7f9d988b.pth", - "swin_base": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/convert/swin_base_patch4_window7_224_22kto1k-f967f799.pth", - "swin_large": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/convert/swin_large_patch4_window7_224_22kto1k-5f0996db.pth", - "t2t_vit_t_14": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-14_3rdparty_8xb64_in1k_20210928-b7c09b62.pth", - "t2t_vit_t_19": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-19_3rdparty_8xb64_in1k_20210928-7f1478d5.pth", - "t2t_vit_t_24": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-24_3rdparty_8xb64_in1k_20210928-fe95a61b.pth", - "tnt_small": "https://download.openmmlab.com/mmclassification/v0/tnt/tnt-small-p16_3rdparty_in1k_20210903-c56ee7df.pth", - "vit_base_p16": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-98e8652b.pth", - "vit_base_p32": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p32_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-9cea8599.pth", - "vit_large_p16": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-large-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-b20ba619.pth" -} diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/open_mmlab.json b/cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/open_mmlab.json deleted file mode 100644 index 8311db4fe..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/open_mmlab.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "vgg16_caffe": "https://download.openmmlab.com/pretrain/third_party/vgg16_caffe-292e1171.pth", - "detectron/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_caffe-788b5fa3.pth", - "detectron2/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_msra-5891d200.pth", - "detectron/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_caffe-3ad79236.pth", - "detectron2/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_msra-6cc46731.pth", - "detectron2/resnext101_32x8d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x8d-1516f1aa.pth", - "resnext50_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext50-32x4d-0ab1a123.pth", - "resnext101_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d-a5af3160.pth", - "resnext101_64x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_64x4d-ee2c6f71.pth", - "contrib/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_thangvubk-ad1730dd.pth", - "detectron/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn-9186a21c.pth", - "detectron/resnet101_gn": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn-cac0ab98.pth", - "jhu/resnet50_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_ws-15beedd8.pth", - "jhu/resnet101_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn_ws-3e3c308c.pth", - "jhu/resnext50_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn_ws-0d87ac85.pth", - "jhu/resnext101_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn_ws-34ac1a9e.pth", - "jhu/resnext50_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn-c7e8b754.pth", - "jhu/resnext101_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn-ac3bb84e.pth", - "msra/hrnetv2_w18_small": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18_small-b5a04e21.pth", - "msra/hrnetv2_w18": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18-00eb2006.pth", - "msra/hrnetv2_w32": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w32-dc9eeb4f.pth", - "msra/hrnetv2_w40": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w40-ed0b031c.pth", - "msra/hrnetv2_w48": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w48-d2186c55.pth", - "bninception_caffe": "https://download.openmmlab.com/pretrain/third_party/bn_inception_caffe-ed2e8665.pth", - "kin400/i3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/i3d_r50_f32s2_k400-2c57e077.pth", - "kin400/nl3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/nl3d_r50_f32s2_k400-fa7e7caa.pth", - "res2net101_v1d_26w_4s": "https://download.openmmlab.com/pretrain/third_party/res2net101_v1d_26w_4s_mmdetv2-f0a600f9.pth", - "regnetx_400mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_400mf-a5b10d96.pth", - "regnetx_800mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_800mf-1f4be4c7.pth", - "regnetx_1.6gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_1.6gf-5791c176.pth", - "regnetx_3.2gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_3.2gf-c2599b0f.pth", - "regnetx_4.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_4.0gf-a88f671e.pth", - "regnetx_6.4gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_6.4gf-006af45d.pth", - "regnetx_8.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_8.0gf-3c68abe7.pth", - "regnetx_12gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_12gf-4c2a3350.pth", - "resnet18_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet18_v1c-b5776b93.pth", - "resnet50_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet50_v1c-2cccc1ad.pth", - "resnet101_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet101_v1c-e67eebb6.pth", - "mmedit/vgg16": "https://download.openmmlab.com/mmediting/third_party/vgg_state_dict.pth", - "mmedit/res34_en_nomixup": "https://download.openmmlab.com/mmediting/third_party/model_best_resnet34_En_nomixup.pth", - "mmedit/mobilenet_v2": "https://download.openmmlab.com/mmediting/third_party/mobilenet_v2.pth", - "contrib/mobilenet_v3_large": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_large-bc2c3fd3.pth", - "contrib/mobilenet_v3_small": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_small-47085aa1.pth", - "resnest50": "https://download.openmmlab.com/pretrain/third_party/resnest50_d2-7497a55b.pth", - "resnest101": "https://download.openmmlab.com/pretrain/third_party/resnest101_d2-f3b931b2.pth", - "resnest200": "https://download.openmmlab.com/pretrain/third_party/resnest200_d2-ca88e41f.pth", - "darknet53": "https://download.openmmlab.com/pretrain/third_party/darknet53-a628ea1b.pth", - "mmdet/mobilenet_v2": "https://download.openmmlab.com/mmdetection/v2.0/third_party/mobilenet_v2_batch256_imagenet-ff34753d.pth" -} diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/torchvision_0.12.json b/cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/torchvision_0.12.json deleted file mode 100644 index 06defe674..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/model_zoo/torchvision_0.12.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "alexnet": "https://download.pytorch.org/models/alexnet-owt-7be5be79.pth", - "densenet121": "https://download.pytorch.org/models/densenet121-a639ec97.pth", - "densenet169": "https://download.pytorch.org/models/densenet169-b2777c0a.pth", - "densenet201": "https://download.pytorch.org/models/densenet201-c1103571.pth", - "densenet161": "https://download.pytorch.org/models/densenet161-8d451a50.pth", - "efficientnet_b0": "https://download.pytorch.org/models/efficientnet_b0_rwightman-3dd342df.pth", - "efficientnet_b1": "https://download.pytorch.org/models/efficientnet_b1_rwightman-533bc792.pth", - "efficientnet_b2": "https://download.pytorch.org/models/efficientnet_b2_rwightman-bcdf34b7.pth", - "efficientnet_b3": "https://download.pytorch.org/models/efficientnet_b3_rwightman-cf984f9c.pth", - "efficientnet_b4": "https://download.pytorch.org/models/efficientnet_b4_rwightman-7eb33cd5.pth", - "efficientnet_b5": "https://download.pytorch.org/models/efficientnet_b5_lukemelas-b6417697.pth", - "efficientnet_b6": "https://download.pytorch.org/models/efficientnet_b6_lukemelas-c76e70fd.pth", - "efficientnet_b7": "https://download.pytorch.org/models/efficientnet_b7_lukemelas-dcc49843.pth", - "googlenet": "https://download.pytorch.org/models/googlenet-1378be20.pth", - "inception_v3_google": "https://download.pytorch.org/models/inception_v3_google-0cc3c7bd.pth", - "mobilenet_v2": "https://download.pytorch.org/models/mobilenet_v2-b0353104.pth", - "mobilenet_v3_large": "https://download.pytorch.org/models/mobilenet_v3_large-8738ca79.pth", - "mobilenet_v3_small": "https://download.pytorch.org/models/mobilenet_v3_small-047dcff4.pth", - "regnet_y_400mf": "https://download.pytorch.org/models/regnet_y_400mf-c65dace8.pth", - "regnet_y_800mf": "https://download.pytorch.org/models/regnet_y_800mf-1b27b58c.pth", - "regnet_y_1_6gf": "https://download.pytorch.org/models/regnet_y_1_6gf-b11a554e.pth", - "regnet_y_3_2gf": "https://download.pytorch.org/models/regnet_y_3_2gf-b5a9779c.pth", - "regnet_y_8gf": "https://download.pytorch.org/models/regnet_y_8gf-d0d0e4a8.pth", - "regnet_y_16gf": "https://download.pytorch.org/models/regnet_y_16gf-9e6ed7dd.pth", - "regnet_y_32gf": "https://download.pytorch.org/models/regnet_y_32gf-4dee3f7a.pth", - "regnet_x_400mf": "https://download.pytorch.org/models/regnet_x_400mf-adf1edd5.pth", - "regnet_x_800mf": "https://download.pytorch.org/models/regnet_x_800mf-ad17e45c.pth", - "regnet_x_1_6gf": "https://download.pytorch.org/models/regnet_x_1_6gf-e3633e7f.pth", - "regnet_x_3_2gf": "https://download.pytorch.org/models/regnet_x_3_2gf-f342aeae.pth", - "regnet_x_8gf": "https://download.pytorch.org/models/regnet_x_8gf-03ceed89.pth", - "regnet_x_16gf": "https://download.pytorch.org/models/regnet_x_16gf-2007eb11.pth", - "regnet_x_32gf": "https://download.pytorch.org/models/regnet_x_32gf-9d47f8d0.pth", - "resnet18": "https://download.pytorch.org/models/resnet18-f37072fd.pth", - "resnet34": "https://download.pytorch.org/models/resnet34-b627a593.pth", - "resnet50": "https://download.pytorch.org/models/resnet50-0676ba61.pth", - "resnet101": "https://download.pytorch.org/models/resnet101-63fe2227.pth", - "resnet152": "https://download.pytorch.org/models/resnet152-394f9c45.pth", - "resnext50_32x4d": "https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth", - "resnext101_32x8d": "https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth", - "wide_resnet50_2": "https://download.pytorch.org/models/wide_resnet50_2-95faca4d.pth", - "wide_resnet101_2": "https://download.pytorch.org/models/wide_resnet101_2-32ee1156.pth", - "shufflenetv2_x0.5": "https://download.pytorch.org/models/shufflenetv2_x0.5-f707e7126e.pth", - "shufflenetv2_x1.0": "https://download.pytorch.org/models/shufflenetv2_x1-5666bf0f80.pth", - "shufflenetv2_x1.5": null, - "shufflenetv2_x2.0": null, - "squeezenet1_0": "https://download.pytorch.org/models/squeezenet1_0-b66bff10.pth", - "squeezenet1_1": "https://download.pytorch.org/models/squeezenet1_1-b8a52dc0.pth", - "vgg11": "https://download.pytorch.org/models/vgg11-8a719046.pth", - "vgg13": "https://download.pytorch.org/models/vgg13-19584684.pth", - "vgg16": "https://download.pytorch.org/models/vgg16-397923af.pth", - "vgg19": "https://download.pytorch.org/models/vgg19-dcbb9e9d.pth", - "vgg11_bn": "https://download.pytorch.org/models/vgg11_bn-6002323d.pth", - "vgg13_bn": "https://download.pytorch.org/models/vgg13_bn-abd245e5.pth", - "vgg16_bn": "https://download.pytorch.org/models/vgg16_bn-6c64b313.pth", - "vgg19_bn": "https://download.pytorch.org/models/vgg19_bn-c79401a0.pth" -} diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/__init__.py deleted file mode 100644 index a12b79cef..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .deprecated_wrappers import Conv2d_deprecated as Conv2d -from .deprecated_wrappers import ConvTranspose2d_deprecated as ConvTranspose2d -from .deprecated_wrappers import Linear_deprecated as Linear -from .deprecated_wrappers import MaxPool2d_deprecated as MaxPool2d -from .focal_loss import (SigmoidFocalLoss, SoftmaxFocalLoss, - sigmoid_focal_loss, softmax_focal_loss) -from .sync_bn import SyncBatchNorm -from .cc_attention import CrissCrossAttention -from .point_sample import * -from .psa_mask import PSAMask, PSAMaskFunction -from .info import * \ No newline at end of file diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/cc_attention.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/cc_attention.py deleted file mode 100644 index 3fd83fcb9..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/cc_attention.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.cnn import PLUGIN_LAYERS, Scale - - -def NEG_INF_DIAG(n, device): - """Returns a diagonal matrix of size [n, n]. - - The diagonal are all "-inf". This is for avoiding calculating the - overlapped element in the Criss-Cross twice. - """ - return torch.diag(torch.tensor(float('-inf')).to(device).repeat(n), 0) - - -@PLUGIN_LAYERS.register_module() -class CrissCrossAttention(nn.Module): - """Criss-Cross Attention Module. - - .. note:: - Before v1.3.13, we use a CUDA op. Since v1.3.13, we switch - to a pure PyTorch and equivalent implementation. For more - details, please refer to https://github.com/open-mmlab/mmcv/pull/1201. - - Speed comparison for one forward pass - - - Input size: [2,512,97,97] - - Device: 1 NVIDIA GeForce RTX 2080 Ti - - +-----------------------+---------------+------------+---------------+ - | |PyTorch version|CUDA version|Relative speed | - +=======================+===============+============+===============+ - |with torch.no_grad() |0.00554402 s |0.0299619 s |5.4x | - +-----------------------+---------------+------------+---------------+ - |no with torch.no_grad()|0.00562803 s |0.0301349 s |5.4x | - +-----------------------+---------------+------------+---------------+ - - Args: - in_channels (int): Channels of the input feature map. - """ - - def __init__(self, in_channels): - super().__init__() - self.query_conv = nn.Conv2d(in_channels, in_channels // 8, 1) - self.key_conv = nn.Conv2d(in_channels, in_channels // 8, 1) - self.value_conv = nn.Conv2d(in_channels, in_channels, 1) - self.gamma = Scale(0.) - self.in_channels = in_channels - - def forward(self, x): - """forward function of Criss-Cross Attention. - - Args: - x (torch.Tensor): Input feature with the shape of - (batch_size, in_channels, height, width). - - Returns: - torch.Tensor: Output of the layer, with the shape of - (batch_size, in_channels, height, width) - """ - B, C, H, W = x.size() - query = self.query_conv(x) - key = self.key_conv(x) - value = self.value_conv(x) - energy_H = torch.einsum('bchw,bciw->bwhi', query, key) + NEG_INF_DIAG( - H, query.device) - energy_H = energy_H.transpose(1, 2) - energy_W = torch.einsum('bchw,bchj->bhwj', query, key) - attn = F.softmax( - torch.cat([energy_H, energy_W], dim=-1), dim=-1) # [B,H,W,(H+W)] - out = torch.einsum('bciw,bhwi->bchw', value, attn[..., :H]) - out += torch.einsum('bchj,bhwj->bchw', value, attn[..., H:]) - - out = self.gamma(out) + x - out = out.contiguous() - - return out - - def __repr__(self): - s = self.__class__.__name__ - s += f'(in_channels={self.in_channels})' - return s diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/README.md b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/README.md deleted file mode 100644 index 3bc020040..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/README.md +++ /dev/null @@ -1,170 +0,0 @@ -# Code Structure of CUDA operators - -This folder contains all non-python code for MMCV custom ops. Please follow the same architecture if you want to add new ops. - -## Directories Tree - -```folder -. -├── common -│ ├── box_iou_rotated_utils.hpp -│ ├── parrots_cpp_helper.hpp -│ ├── parrots_cuda_helper.hpp -│ ├── pytorch_cpp_helper.hpp -│ ├── pytorch_cuda_helper.hpp -│ ├── pytorch_device_registry.hpp -│   └── cuda -│   ├── common_cuda_helper.hpp -│   ├── parrots_cudawarpfunction.cuh -│   ├── ... -│   └── ops_cuda_kernel.cuh -├── onnxruntime -│   ├── onnxruntime_register.h -│   ├── onnxruntime_session_options_config_keys.h -│   ├── ort_mmcv_utils.h -│   ├── ... -│   ├── onnx_ops.h -│   └── cpu -│ ├── onnxruntime_register.cpp -│      ├── ... -│      └── onnx_ops_impl.cpp -├── parrots -│   ├── ... -│   ├── ops.cpp -│   ├── ops_parrots.cpp -│   └── ops_pytorch.h -├── pytorch -│   ├── info.cpp -│   ├── pybind.cpp -│   ├── ... -│   ├── ops.cpp -│   ├── cuda -│   │   ├── ... -│   │   └── ops_cuda.cu -│   └── cpu -│      ├── ... -│      └── ops.cpp -└── tensorrt - ├── trt_cuda_helper.cuh - ├── trt_plugin_helper.hpp - ├── trt_plugin.hpp - ├── trt_serialize.hpp - ├── ... - ├── trt_ops.hpp - └── plugins -    ├── trt_cuda_helper.cu -    ├── trt_plugin.cpp -    ├── ... -    ├── trt_ops.cpp -    └── trt_ops_kernel.cu -``` - -## Components - -- `common`: This directory contains all tools and shared codes. - - `cuda`: The cuda kernels which can be shared by all backends. **HIP** kernel is also here since they have similar syntax. -- `onnxruntime`: **ONNX Runtime** support for custom ops. - - `cpu`: CPU implementation of supported ops. -- `parrots`: **Parrots** is a deep learning frame for model training and inference. Parrots custom ops are placed in this directory. -- `pytorch`: **PyTorch** custom ops are supported by binding C++ to Python with **pybind11**. The ops implementation and binding codes are placed in this directory. - - `cuda`: This directory contains cuda kernel launchers, which feed memory pointers of tensor to the cuda kernel in `common/cuda`. The launchers provide c++ interface of cuda implementation of corresponding custom ops. - - `cpu`: This directory contain cpu implementations of corresponding custom ops. -- `tensorrt`: **TensorRT** support for custom ops. - - `plugins`: This directory contains the implementation of the supported custom ops. Some ops might also use shared cuda kernel in `common/cuda`. - -## How to add new PyTorch ops? - -1. (Optional) Add shared kernel in `common` to support special hardware platform. - - ```c++ - // src/common/cuda/new_ops_cuda_kernel.cuh - - template - __global__ void new_ops_forward_cuda_kernel(const T* input, T* output, ...) { - // forward here - } - - ``` - - Add cuda kernel launcher in `pytorch/cuda`. - - ```c++ - // src/pytorch/cuda - #include - - void NewOpsForwardCUDAKernelLauncher(Tensor input, Tensor output, ...){ - // initialize - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - ... - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "new_ops_forward_cuda_kernel", ([&] { - new_ops_forward_cuda_kernel - <<>>( - input.data_ptr(), output.data_ptr(),...); - })); - AT_CUDA_CHECK(cudaGetLastError()); - } - ``` - -2. Register implementation for different devices. - - ```c++ - // src/pytorch/cuda/cudabind.cpp - ... - - Tensor new_ops_forward_cuda(Tensor input, Tensor output, ...){ - // implement cuda forward here - // use `NewOpsForwardCUDAKernelLauncher` here - } - // declare interface here. - Tensor new_ops_forward_impl(Tensor input, Tensor output, ...); - // register the implementation for given device (CUDA here). - REGISTER_DEVICE_IMPL(new_ops_forward_impl, CUDA, new_ops_forward_cuda); - ``` - -3. Add ops implementation in `pytorch` directory. Select different implementations according to device type. - - ```c++ - // src/pytorch/new_ops.cpp - Tensor new_ops_forward_impl(Tensor input, Tensor output, ...){ - // dispatch the implementation according to the device type of input. - DISPATCH_DEVICE_IMPL(new_ops_forward_impl, input, output, ...); - } - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...){ - return new_ops_forward_impl(input, output, ...); - } - ``` - -4. Binding the implementation in `pytorch/pybind.cpp` - - ```c++ - // src/pytorch/pybind.cpp - - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...); - - ... - - // bind with pybind11 - m.def("new_ops_forward", &new_ops_forward, "new_ops_forward", - py::arg("input"), py::arg("output"), ...); - - ... - - ``` - -5. Build MMCV again. Enjoy new ops in python - - ```python - from ..utils import ext_loader - ext_module = ext_loader.load_ext('_ext', ['new_ops_forward']) - - ... - - ext_module.new_ops_forward(input, output, ...) - - ``` diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp deleted file mode 100644 index e18036bac..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef COMMON_CUDA_HELPER -#define COMMON_CUDA_HELPER - -#include - -#define CUDA_1D_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) - -#define CUDA_2D_KERNEL_LOOP(i, n, j, m) \ - for (size_t i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) \ - for (size_t j = blockIdx.y * blockDim.y + threadIdx.y; j < (m); \ - j += blockDim.y * gridDim.y) - -#define CUDA_2D_KERNEL_BLOCK_LOOP(i, n, j, m) \ - for (size_t i = blockIdx.x; i < (n); i += gridDim.x) \ - for (size_t j = blockIdx.y; j < (m); j += gridDim.y) - -#define THREADS_PER_BLOCK 512 - -inline int GET_BLOCKS(const int N, const int num_threads = THREADS_PER_BLOCK) { - int optimal_block_num = (N + num_threads - 1) / num_threads; - int max_block_num = 4096; - return std::min(optimal_block_num, max_block_num); -} - -template -__device__ T bilinear_interpolate(const T* input, const int height, - const int width, T y, T x, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) return 0; - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - int y_low = (int)y; - int x_low = (int)x; - int y_high; - int x_high; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - // do bilinear interpolation - T v1 = input[y_low * width + x_low]; - T v2 = input[y_low * width + x_high]; - T v3 = input[y_high * width + x_low]; - T v4 = input[y_high * width + x_high]; - T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - return val; -} - -template -__device__ void bilinear_interpolate_gradient( - const int height, const int width, T y, T x, T& w1, T& w2, T& w3, T& w4, - int& x_low, int& x_high, int& y_low, int& y_high, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - w1 = w2 = w3 = w4 = 0.; - x_low = x_high = y_low = y_high = -1; - return; - } - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - y_low = (int)y; - x_low = (int)x; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - - // reference in forward - // T v1 = input[y_low * width + x_low]; - // T v2 = input[y_low * width + x_high]; - // T v3 = input[y_high * width + x_low]; - // T v4 = input[y_high * width + x_high]; - // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - return; -} -#endif // COMMON_CUDA_HELPER diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh deleted file mode 100644 index 5d946686b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef PSAMASK_CUDA_KERNEL_CUH -#define PSAMASK_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -// CUDA: grid stride looping -#ifndef CUDA_KERNEL_LOOP -#define CUDA_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) -#endif - -template -__global__ void psamask_collect_forward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* mask_data, T* buffer_data) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - buffer_data[(n * h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)) * - h_feature * w_feature + - h * w_feature + w] = mask_data - [((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + h) * - w_feature + - w]; - } - } - } -} - -template -__global__ void psamask_distribute_forward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* mask_data, T* buffer_data) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - buffer_data[(n * h_feature * w_feature + h * w_feature + w) * - h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)] = mask_data - [((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + h) * - w_feature + - w]; - } - } - } -} - -template -__global__ void psamask_collect_backward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* buffer_diff, T* mask_diff) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - mask_diff[((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + - h) * - w_feature + - w] = buffer_diff[(n * h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)) * - h_feature * w_feature + - h * w_feature + w]; - } - } - } -} - -template -__global__ void psamask_distribute_backward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* buffer_diff, T* mask_diff) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - mask_diff[((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + - h) * - w_feature + - w] = - buffer_diff[(n * h_feature * w_feature + h * w_feature + w) * - h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)]; - } - } - } -} - -#endif // PSAMASK_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 1eb5f8fcc..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sigmoid_focal_loss_forward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* output, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + expf(-input[index])); - - // (1 - p)**gamma * log(p) - T term_p = pow(((T)1. - p), gamma) * log(max(p, (T)FLT_MIN)); - // p**gamma * log(1 - p) - T term_n = pow(p, gamma) * log(max((T)1. - p, (T)FLT_MIN)); - - output[index] = (T)0.; - output[index] += -flag_p * alpha * term_p; - output[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - output[index] *= weight[t]; - } - } -} - -template -__global__ void sigmoid_focal_loss_backward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* grad_input, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + exp(-input[index])); - - // (1 - p)**gamma * (1 - p - gamma*p*log(p)) - T term_p = pow(((T)1. - p), gamma) * - ((T)1. - p - (gamma * p * log(max(p, (T)FLT_MIN)))); - // p**gamma * (gamma * (1 - p) * log(1 - p) - p) - T term_n = pow(p, gamma) * - (gamma * ((T)1. - p) * log(max((T)1. - p, (T)FLT_MIN)) - p); - - grad_input[index] = (T)0.; - grad_input[index] += -flag_p * alpha * term_p; - grad_input[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - grad_input[index] *= weight[t]; - } - } -} - -#endif // SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 631b2c617..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void softmax_focal_loss_forward_cuda_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* output, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - output[index] = - -alpha * pow((T)1. - pred, gamma) * log(max(pred, (T)FLT_MIN)); - } else { - output[index] = 0; - } - if (weight != NULL) { - output[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda1_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* buff, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - buff[index] = alpha * (-pow((T)1. - pred, gamma) + - gamma * pow((T)1. - pred, gamma - 1) * pred * - log(max(pred, (T)FLT_MIN))); - } else { - buff[index] = 0; - } - if (weight != NULL) { - buff[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda2_kernel( - const int nthreads, const T* softmax, const int64_t* target, const T* buff, - T* grad_input, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - int64_t label = target[n]; - - if (label >= 0) { - T flag = (label == c ? (T)1. : (T)0.); - grad_input[index] = buff[n] * (flag - softmax[index]); - } else { - grad_input[index] = 0; - } - } -} - -#endif // SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh deleted file mode 100644 index 4ec6a4668..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SYNCBN_CUDA_KERNEL_CUH -#define SYNCBN_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sync_bn_forward_mean_cuda_kernel(const T *input, float *mean, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += input[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_mean_cuda_kernel(const phalf *input, - float *mean, int num, - int channels, int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += static_cast(input[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_var_cuda_kernel(const T *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = input[index] - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_var_cuda_kernel(const phalf *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = static_cast(input[index]) - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_output_cuda_kernel( - const T *input, const float *mean, const float *var, float *running_mean, - float *running_var, const float *weight, const float *bias, float *norm, - float *std, T *output, int num, int channels, int spatial, float eps, - float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = (input[index] - mean_value) / std_value; - output[index] = norm[index] * weight_value + bias_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - (input[index] - mean_value) / std_value * weight_value + bias_value; - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = norm[index] = (input[index] - mean_value) / std_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = (input[index] - mean_value) / std_value; - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template <> -__global__ void sync_bn_forward_output_cuda_kernel( - const phalf *input, const float *mean, const float *var, - float *running_mean, float *running_var, const float *weight, - const float *bias, float *norm, float *std, phalf *output, int num, - int channels, int spatial, float eps, float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = - static_cast(norm[index] * weight_value + bias_value); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - static_cast((static_cast(input[index]) - mean_value) / - std_value * weight_value + - bias_value); - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = static_cast(norm[index]); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = static_cast( - (static_cast(input[index]) - mean_value) / std_value); - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template -__global__ void sync_bn_backward_param_cuda_kernel(const T *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += grad_output[index] * norm[index]; - buffer2[tid] += grad_output[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template <> -__global__ void sync_bn_backward_param_cuda_kernel(const phalf *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += static_cast(grad_output[index]) * norm[index]; - buffer2[tid] += static_cast(grad_output[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const T *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, T *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = - weight[c] * - (grad_output[index] - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]; - } -} - -template <> -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const phalf *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, phalf *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = static_cast( - weight[c] * - (static_cast(grad_output[index]) - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]); - } -} - -#endif // SYNCBN_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp deleted file mode 100644 index f68e87405..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef PYTORCH_CPP_HELPER -#define PYTORCH_CPP_HELPER -#include - -#include - -using namespace at; - -#define CHECK_CUDA(x) \ - TORCH_CHECK(x.device().is_cuda(), #x " must be a CUDA tensor") -#define CHECK_MLU(x) \ - TORCH_CHECK(x.device().type() == at::kMLU, #x " must be a MLU tensor") -#define CHECK_CPU(x) \ - TORCH_CHECK(x.device().type() == at::kCPU, #x " must be a CPU tensor") -#define CHECK_CONTIGUOUS(x) \ - TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") -#define CHECK_CUDA_INPUT(x) \ - CHECK_CUDA(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_MLU_INPUT(x) \ - CHECK_MLU(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_CPU_INPUT(x) \ - CHECK_CPU(x); \ - CHECK_CONTIGUOUS(x) - -#endif // PYTORCH_CPP_HELPER diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp deleted file mode 100644 index 9869b535f..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PYTORCH_CUDA_HELPER -#define PYTORCH_CUDA_HELPER - -#include -#include -#include - -#include -#include - -#include "common_cuda_helper.hpp" - -using at::Half; -using at::Tensor; -using phalf = at::Half; - -#define __PHALF(x) (x) - -#endif // PYTORCH_CUDA_HELPER diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp deleted file mode 100644 index 2a32b7270..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef PYTORCH_DEVICE_REGISTRY_H -#define PYTORCH_DEVICE_REGISTRY_H - -// Using is recommended in the official documentation in -// https://pytorch.org/tutorials/advanced/cpp_extension.html#writing-the-c-op. -// However, we use for compatibility with CUDA 9.0 -// Read https://github.com/pytorch/extension-cpp/issues/35 for more details. -#include - -#include -#include -#include -#include - -inline std::string GetDeviceStr(const at::Device& device) { - std::string str = DeviceTypeName(device.type(), true); - if (device.has_index()) { - str.push_back(':'); - str.append(std::to_string(device.index())); - } - return str; -} - -// Registry -template -class DeviceRegistry; - -template -class DeviceRegistry { - public: - using FunctionType = Ret (*)(Args...); - static const int MAX_DEVICE_TYPES = - int8_t(at::DeviceType::COMPILE_TIME_MAX_DEVICE_TYPES); - - void Register(at::DeviceType device, FunctionType function) { - funcs_[int8_t(device)] = function; - } - - FunctionType Find(at::DeviceType device) const { - return funcs_[int8_t(device)]; - } - - static DeviceRegistry& instance() { - static DeviceRegistry inst; - return inst; - } - - private: - DeviceRegistry() { - for (size_t i = 0; i < MAX_DEVICE_TYPES; ++i) { - funcs_[i] = nullptr; - } - }; - FunctionType funcs_[MAX_DEVICE_TYPES]; -}; - -// get device of first tensor param - -template , at::Tensor>::value, - bool> = true> -at::Device GetFirstTensorDevice(T&& t, Args&&... args) { - return std::forward(t).device(); -} -template , at::Tensor>::value, - bool> = true> -at::Device GetFirstTensorDevice(T&& t, Args&&... args) { - return GetFirstTensorDevice(std::forward(args)...); -} - -// check device consistency - -inline std::pair CheckDeviceConsistency( - const at::Device& device, int index) { - return {index, device}; -} - -template , at::Tensor>::value, - bool> = true> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args); - -template , at::Tensor>::value, - bool> = true> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args) { - auto new_device = std::forward(t).device(); - if (new_device.type() != device.type() || - new_device.index() != device.index()) { - return {index, new_device}; - } - return CheckDeviceConsistency(device, index + 1, std::forward(args)...); -} - -template < - typename T, typename... Args, - std::enable_if_t, at::Tensor>::value, bool>> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args) { - return CheckDeviceConsistency(device, index + 1, std::forward(args)...); -} - -// dispatch - -template -auto Dispatch(const R& registry, const char* name, Args&&... args) { - auto device = GetFirstTensorDevice(std::forward(args)...); - auto inconsist = - CheckDeviceConsistency(device, 0, std::forward(args)...); - TORCH_CHECK(inconsist.first >= int(sizeof...(Args)), name, ": at param ", - inconsist.first, - ", inconsistent device: ", GetDeviceStr(inconsist.second).c_str(), - " vs ", GetDeviceStr(device).c_str(), "\n") - auto f_ptr = registry.Find(device.type()); - TORCH_CHECK(f_ptr != nullptr, name, ": implementation for device ", - GetDeviceStr(device).c_str(), " not found.\n") - return f_ptr(std::forward(args)...); -} - -// helper macro - -#define DEVICE_REGISTRY(key) DeviceRegistry::instance() - -#define REGISTER_DEVICE_IMPL(key, device, value) \ - struct key##_##device##_registerer { \ - key##_##device##_registerer() { \ - DEVICE_REGISTRY(key).Register(at::k##device, value); \ - } \ - }; \ - static key##_##device##_registerer _##key##_##device##_registerer; - -#define DISPATCH_DEVICE_IMPL(key, ...) \ - Dispatch(DEVICE_REGISTRY(key), #key, __VA_ARGS__) - -#endif // PYTORCH_DEVICE_REGISTRY diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp deleted file mode 100644 index dc376bb51..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp +++ /dev/null @@ -1,210 +0,0 @@ -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha); - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha); - -void sigmoid_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SigmoidFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void sigmoid_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - SigmoidFocalLossBackwardCUDAKernelLauncher(input, target, weight, grad_input, - gamma, alpha); -} - -void softmax_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SoftmaxFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void softmax_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - SoftmaxFocalLossBackwardCUDAKernelLauncher(input, target, weight, buff, - grad_input, gamma, alpha); -} - -void sigmoid_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha); - -void softmax_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha); - -REGISTER_DEVICE_IMPL(sigmoid_focal_loss_forward_impl, CUDA, - sigmoid_focal_loss_forward_cuda); -REGISTER_DEVICE_IMPL(sigmoid_focal_loss_backward_impl, CUDA, - sigmoid_focal_loss_backward_cuda); -REGISTER_DEVICE_IMPL(softmax_focal_loss_forward_impl, CUDA, - softmax_focal_loss_forward_cuda); -REGISTER_DEVICE_IMPL(softmax_focal_loss_backward_impl, CUDA, - softmax_focal_loss_backward_cuda); - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean); - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var); - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size); - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias); - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -void sync_bn_forward_mean_cuda(const Tensor input, Tensor mean) { - SyncBNForwardMeanCUDAKernelLauncher(input, mean); -} - -void sync_bn_forward_var_cuda(const Tensor input, const Tensor mean, - Tensor var) { - SyncBNForwardVarCUDAKernelLauncher(input, mean, var); -} - -void sync_bn_forward_output_cuda(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - SyncBNForwardOutputCUDAKernelLauncher(input, mean, var, running_mean, - running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_cuda(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - SyncBNBackwardParamCUDAKernelLauncher(grad_output, norm, grad_weight, - grad_bias); -} - -void sync_bn_backward_data_cuda(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - SyncBNBackwardDataCUDAKernelLauncher(grad_output, weight, grad_weight, - grad_bias, norm, std, grad_input); -} - -void sync_bn_forward_mean_impl(const Tensor input, Tensor mean); - -void sync_bn_forward_var_impl(const Tensor input, const Tensor mean, - Tensor var); - -void sync_bn_forward_output_impl(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param_impl(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data_impl(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input); - -REGISTER_DEVICE_IMPL(sync_bn_forward_mean_impl, CUDA, - sync_bn_forward_mean_cuda); -REGISTER_DEVICE_IMPL(sync_bn_forward_var_impl, CUDA, sync_bn_forward_var_cuda); -REGISTER_DEVICE_IMPL(sync_bn_forward_output_impl, CUDA, - sync_bn_forward_output_cuda); -REGISTER_DEVICE_IMPL(sync_bn_backward_param_impl, CUDA, - sync_bn_backward_param_cuda); -REGISTER_DEVICE_IMPL(sync_bn_backward_data_impl, CUDA, - sync_bn_backward_data_cuda); - - - -void PSAMaskForwardCUDAKernelLauncher(const int psa_type, const Tensor input, - Tensor output, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, - const int half_w_mask); - -void PSAMaskBackwardCUDAKernelLauncher( - const int psa_type, const Tensor grad_output, Tensor grad_input, - const int num_, const int h_feature, const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, const int half_w_mask); - -void psamask_forward_cuda(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask) { - PSAMaskForwardCUDAKernelLauncher(psa_type, input, output, num_, h_feature, - w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_backward_cuda(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - PSAMaskBackwardCUDAKernelLauncher(psa_type, grad_output, grad_input, num_, - h_feature, w_feature, h_mask, w_mask, - half_h_mask, half_w_mask); -} - -void psamask_forward_impl(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask); - -void psamask_backward_impl(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask); -REGISTER_DEVICE_IMPL(psamask_forward_impl, CUDA, psamask_forward_cuda); -REGISTER_DEVICE_IMPL(psamask_backward_impl, CUDA, psamask_backward_cuda); diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu deleted file mode 100644 index cb899f954..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sigmoid_focal_loss_cuda_kernel.cuh" -#include "softmax_focal_loss_cuda_kernel.cuh" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = input.size(1); - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_forward_cuda_kernel", [&] { - sigmoid_focal_loss_forward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha) { - int output_size = grad_input.numel(); - int num_classes = input.size(1); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_backward_cuda_kernel", [&] { - sigmoid_focal_loss_backward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - grad_input.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = softmax.size(1); - - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(softmax.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - softmax.scalar_type(), "softmax_focal_loss_forward_cuda_kernel", [&] { - softmax_focal_loss_forward_cuda_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha) { - int num_classes = softmax.size(1); - - int output_size = buff.numel(); - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda1_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda1_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - buff.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); - - output_size = grad_input.numel(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda2_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda2_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), buff.data_ptr(), - grad_input.data_ptr(), num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu deleted file mode 100644 index a0bdfa60c..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// Modified from -// https://github.com/hszhao/semseg/blob/master/lib/psa/src - -#include - -#include "psamask_cuda_kernel.cuh" -#include "pytorch_cuda_helper.hpp" - -void PSAMaskForwardCUDAKernelLauncher(const int psa_type, const Tensor input, - Tensor output, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, - const int half_w_mask) { - int nthreads = num_ * h_feature * w_feature; - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - if (psa_type == 0) - AT_DISPATCH_FLOATING_TYPES( - input.scalar_type(), "psamask_collect_forward_cuda", [&] { - psamask_collect_forward_cuda<<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, input.data_ptr(), - output.data_ptr()); - }); - else - AT_DISPATCH_FLOATING_TYPES( - input.scalar_type(), "psamask_distribute_forward_cuda", [&] { - psamask_distribute_forward_cuda - <<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, input.data_ptr(), - output.data_ptr()); - }); -} - -void PSAMaskBackwardCUDAKernelLauncher( - const int psa_type, const Tensor grad_output, Tensor grad_input, - const int num_, const int h_feature, const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, const int half_w_mask) { - int nthreads = num_ * h_feature * w_feature; - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - if (psa_type == 0) - AT_DISPATCH_FLOATING_TYPES( - grad_input.scalar_type(), "psamask_collect_backward_cuda", [&] { - psamask_collect_backward_cuda<<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, grad_output.data_ptr(), - grad_input.data_ptr()); - }); - else - AT_DISPATCH_FLOATING_TYPES( - grad_input.scalar_type(), "psamask_distribute_backward_cuda", [&] { - psamask_distribute_backward_cuda - <<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, grad_output.data_ptr(), - grad_input.data_ptr()); - }); -} diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu deleted file mode 100644 index 657c81701..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sync_bn_cuda_kernel.cuh" - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_mean_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_var_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), num, channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_output_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), running_mean.data_ptr(), - running_var.data_ptr(), weight.data_ptr(), - bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), output.data_ptr(), num, - channels, spatial, eps, momentum, group_size); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias) { - int num = grad_output.size(0); - int channels = grad_output.size(1); - int spatial = grad_output.size(2); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_param_cuda_kernel", [&] { - sync_bn_backward_param_cuda_kernel - <<>>( - grad_output.data_ptr(), norm.data_ptr(), - grad_weight.data_ptr(), grad_bias.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - int output_size = grad_input.numel(); - int num = grad_input.size(0); - int channels = grad_input.size(1); - int spatial = grad_input.size(2); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_data_cuda_kernel", [&] { - sync_bn_backward_data_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - weight.data_ptr(), grad_weight.data_ptr(), - grad_bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), grad_input.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp deleted file mode 100644 index ed0e21865..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void sigmoid_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(sigmoid_focal_loss_forward_impl, input, target, weight, - output, gamma, alpha); -} - -void sigmoid_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(sigmoid_focal_loss_backward_impl, input, target, weight, - grad_input, gamma, alpha); -} - -void softmax_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(softmax_focal_loss_forward_impl, input, target, weight, - output, gamma, alpha); -} - -void softmax_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - DISPATCH_DEVICE_IMPL(softmax_focal_loss_backward_impl, input, target, weight, - buff, grad_input, gamma, alpha); -} - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - sigmoid_focal_loss_forward_impl(input, target, weight, output, gamma, alpha); -} - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha) { - sigmoid_focal_loss_backward_impl(input, target, weight, grad_input, gamma, - alpha); -} - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - softmax_focal_loss_forward_impl(input, target, weight, output, gamma, alpha); -} - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha) { - softmax_focal_loss_backward_impl(input, target, weight, buff, grad_input, - gamma, alpha); -} diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/info.cpp b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/info.cpp deleted file mode 100644 index a08d227d4..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/info.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// modified from -// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/vision.cpp -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF -#include -int get_cudart_version() { return CUDART_VERSION; } -#endif -#endif - -std::string get_compiling_cuda_version() { -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF - std::ostringstream oss; - // copied from - // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/cuda/detail/CUDAHooks.cpp#L231 - auto printCudaStyleVersion = [&](int v) { - oss << (v / 1000) << "." << (v / 10 % 100); - if (v % 10 != 0) { - oss << "." << (v % 10); - } - }; - printCudaStyleVersion(get_cudart_version()); - return oss.str(); -#else - return std::string("rocm not available"); -#endif -#else - return std::string("not available"); -#endif -} - -// similar to -// https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/Version.cpp -std::string get_compiler_version() { - std::ostringstream ss; -#if defined(__GNUC__) -#ifndef __clang__ - { ss << "GCC " << __GNUC__ << "." << __GNUC_MINOR__; } -#endif -#endif - -#if defined(__clang_major__) - { - ss << "clang " << __clang_major__ << "." << __clang_minor__ << "." - << __clang_patchlevel__; - } -#endif - -#if defined(_MSC_VER) - { ss << "MSVC " << _MSC_FULL_VER; } -#endif - return ss.str(); -} diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp deleted file mode 100644 index 6064c9ba5..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// Modified from -// https://github.com/hszhao/semseg/blob/master/lib/psa/src -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void psamask_forward_impl(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask) { - DISPATCH_DEVICE_IMPL(psamask_forward_impl, psa_type, input, output, num_, - h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_backward_impl(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - DISPATCH_DEVICE_IMPL(psamask_backward_impl, psa_type, grad_output, grad_input, - num_, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_forward(const Tensor input, Tensor output, const int psa_type, - const int num_, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask) { - psamask_forward_impl(psa_type, input, output, num_, h_feature, w_feature, - h_mask, w_mask, half_h_mask, half_w_mask); -} - -void psamask_backward(Tensor grad_output, const Tensor grad_input, - const int psa_type, const int num_, const int h_feature, - const int w_feature, const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - psamask_backward_impl(psa_type, grad_output, grad_input, num_, h_feature, - w_feature, h_mask, w_mask, half_h_mask, half_w_mask); -} diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp deleted file mode 100644 index 1e99174d8..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include - -#include "pytorch_cpp_helper.hpp" - -std::string get_compiler_version(); -std::string get_compiling_cuda_version(); - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha); - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha); - -void sync_bn_forward_mean(const Tensor input, Tensor mean); - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var); - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - - -void psamask_forward(const Tensor input, Tensor output, const int psa_type, - const int num_, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask); - -void psamask_backward(Tensor grad_output, const Tensor grad_input, - const int psa_type, const int num_, const int h_feature, - const int w_feature, const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask); - - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("get_compiler_version", &get_compiler_version, "get_compiler_version"); - m.def("get_compiling_cuda_version", &get_compiling_cuda_version, - "get_compiling_cuda_version"); - - - m.def("sigmoid_focal_loss_forward", &sigmoid_focal_loss_forward, - "sigmoid_focal_loss_forward ", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("sigmoid_focal_loss_backward", &sigmoid_focal_loss_backward, - "sigmoid_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("grad_input"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_forward", &softmax_focal_loss_forward, - "softmax_focal_loss_forward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_backward", &softmax_focal_loss_backward, - "softmax_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("buff"), py::arg("grad_input"), - py::arg("gamma"), py::arg("alpha")); - - // SyncBN - m.def("sync_bn_forward_mean", &sync_bn_forward_mean, "sync_bn forward_mean", - py::arg("input"), py::arg("mean")); - m.def("sync_bn_forward_var", &sync_bn_forward_var, "sync_bn forward_var", - py::arg("input"), py::arg("mean"), py::arg("var")); - m.def("sync_bn_forward_output", &sync_bn_forward_output, - "sync_bn forward_output", py::arg("input"), py::arg("mean"), - py::arg("var"), py::arg("weight"), py::arg("bias"), - py::arg("running_mean"), py::arg("running_var"), py::arg("norm"), - py::arg("std"), py::arg("output"), py::arg("eps"), py::arg("momentum"), - py::arg("group_size")); - m.def("sync_bn_backward_param", &sync_bn_backward_param, - "sync_bn backward_param", py::arg("grad_output"), py::arg("norm"), - py::arg("grad_weight"), py::arg("grad_bias")); - m.def("sync_bn_backward_data", &sync_bn_backward_data, - "sync_bn backward_data", py::arg("grad_output"), py::arg("weight"), - py::arg("grad_weight"), py::arg("grad_bias"), py::arg("norm"), - py::arg("std"), py::arg("grad_input")); - - // PASMask - m.def("psamask_forward", &psamask_forward, "PSAMASK forward (CPU/CUDA)", - py::arg("input"), py::arg("output"), py::arg("psa_type"), - py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), - py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), - py::arg("half_w_mask")); - m.def("psamask_backward", &psamask_backward, "PSAMASK backward (CPU/CUDA)", - py::arg("grad_output"), py::arg("grad_input"), py::arg("psa_type"), - py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), - py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), - py::arg("half_w_mask")); -} diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp deleted file mode 100644 index fd5a51327..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void sync_bn_forward_mean_impl(const Tensor input, Tensor mean) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_mean_impl, input, mean); -} - -void sync_bn_forward_var_impl(const Tensor input, const Tensor mean, - Tensor var) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_var_impl, input, mean, var); -} - -void sync_bn_forward_output_impl(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_output_impl, input, mean, var, - running_mean, running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_impl(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - DISPATCH_DEVICE_IMPL(sync_bn_backward_param_impl, grad_output, norm, - grad_weight, grad_bias); -} - -void sync_bn_backward_data_impl(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - DISPATCH_DEVICE_IMPL(sync_bn_backward_data_impl, grad_output, weight, - grad_weight, grad_bias, norm, std, grad_input); -} - -void sync_bn_forward_mean(const Tensor input, Tensor mean) { - sync_bn_forward_mean_impl(input, mean); -} - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var) { - sync_bn_forward_var_impl(input, mean, var); -} - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - sync_bn_forward_output_impl(input, mean, var, running_mean, running_var, - weight, bias, norm, std, output, eps, momentum, - group_size); -} - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - sync_bn_backward_param_impl(grad_output, norm, grad_weight, grad_bias); -} - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - sync_bn_backward_data_impl(grad_output, weight, grad_weight, grad_bias, norm, - std, grad_input); -} diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/deprecated_wrappers.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/deprecated_wrappers.py deleted file mode 100644 index 629a8033f..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/deprecated_wrappers.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# This file is for backward compatibility. -# Module wrappers for empty tensor have been moved to mmcv.cnn.bricks. -import warnings - -from ..cnn.bricks.wrappers import Conv2d, ConvTranspose2d, Linear, MaxPool2d - - -class Conv2d_deprecated(Conv2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Conv2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) - - -class ConvTranspose2d_deprecated(ConvTranspose2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing ConvTranspose2d wrapper from "mmcv.ops" will be ' - 'deprecated in the future. Please import them from "mmcv.cnn" ' - 'instead', DeprecationWarning) - - -class MaxPool2d_deprecated(MaxPool2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing MaxPool2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) - - -class Linear_deprecated(Linear): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Linear wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/focal_loss.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/focal_loss.py deleted file mode 100644 index 805860516..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/focal_loss.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sigmoid_focal_loss_forward', 'sigmoid_focal_loss_backward', - 'softmax_focal_loss_forward', 'softmax_focal_loss_backward' -]) - - -class SigmoidFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSigmoidFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance( - target, (torch.Tensor, torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - output = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_forward( - input, target, weight, output, gamma=ctx.gamma, alpha=ctx.alpha) - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input, target, weight) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - input, target, weight = ctx.saved_tensors - - grad_input = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_backward( - input, - target, - weight, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input.size(0) - return grad_input, None, None, None, None, None - - -sigmoid_focal_loss = SigmoidFocalLossFunction.apply - - -class SigmoidFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SigmoidFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return sigmoid_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s - - -class SoftmaxFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSoftmaxFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - channel_stats, _ = torch.max(input, dim=1) - input_softmax = input - channel_stats.unsqueeze(1).expand_as(input) - input_softmax.exp_() - - channel_stats = input_softmax.sum(dim=1) - input_softmax /= channel_stats.unsqueeze(1).expand_as(input) - - output = input.new_zeros(input.size(0)) - ext_module.softmax_focal_loss_forward( - input_softmax, - target, - weight, - output, - gamma=ctx.gamma, - alpha=ctx.alpha) - - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input_softmax, target, weight) - return output - - @staticmethod - def backward(ctx, grad_output): - input_softmax, target, weight = ctx.saved_tensors - buff = input_softmax.new_zeros(input_softmax.size(0)) - grad_input = input_softmax.new_zeros(input_softmax.size()) - - ext_module.softmax_focal_loss_backward( - input_softmax, - target, - weight, - buff, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input_softmax.size(0) - return grad_input, None, None, None, None, None - - -softmax_focal_loss = SoftmaxFocalLossFunction.apply - - -class SoftmaxFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SoftmaxFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return softmax_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/info.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/info.py deleted file mode 100644 index 29f2e5598..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/info.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os - -import torch - -if torch.__version__ == 'parrots': - import parrots - - def get_compiler_version(): - return 'GCC ' + parrots.version.compiler - - def get_compiling_cuda_version(): - return parrots.version.cuda -else: - from ..utils import ext_loader - ext_module = ext_loader.load_ext( - '_ext', ['get_compiler_version', 'get_compiling_cuda_version']) - - def get_compiler_version(): - return ext_module.get_compiler_version() - - def get_compiling_cuda_version(): - return ext_module.get_compiling_cuda_version() - - -def get_onnxruntime_op_path(): - wildcard = os.path.join( - os.path.abspath(os.path.dirname(os.path.dirname(__file__))), - '_ext_ort.*.so') - - paths = glob.glob(wildcard) - if len(paths) > 0: - return paths[0] - else: - return '' diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/point_sample.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/point_sample.py deleted file mode 100644 index 9a70b28e2..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/point_sample.py +++ /dev/null @@ -1,346 +0,0 @@ -# Modified from https://github.com/facebookresearch/detectron2/tree/master/projects/PointRend # noqa - -from os import path as osp - -import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.nn.modules.utils import _pair -from torch.onnx.operators import shape_as_tensor - - -def bilinear_grid_sample(im, grid, align_corners=False): - """Given an input and a flow-field grid, computes the output using input - values and pixel locations from grid. Supported only bilinear interpolation - method to sample the input pixels. - - Args: - im (torch.Tensor): Input feature map, shape (N, C, H, W) - grid (torch.Tensor): Point coordinates, shape (N, Hg, Wg, 2) - align_corners {bool}: If set to True, the extrema (-1 and 1) are - considered as referring to the center points of the input’s - corner pixels. If set to False, they are instead considered as - referring to the corner points of the input’s corner pixels, - making the sampling more resolution agnostic. - - Returns: - torch.Tensor: A tensor with sampled points, shape (N, C, Hg, Wg) - """ - n, c, h, w = im.shape - gn, gh, gw, _ = grid.shape - assert n == gn - - x = grid[:, :, :, 0] - y = grid[:, :, :, 1] - - if align_corners: - x = ((x + 1) / 2) * (w - 1) - y = ((y + 1) / 2) * (h - 1) - else: - x = ((x + 1) * w - 1) / 2 - y = ((y + 1) * h - 1) / 2 - - x = x.view(n, -1) - y = y.view(n, -1) - - x0 = torch.floor(x).long() - y0 = torch.floor(y).long() - x1 = x0 + 1 - y1 = y0 + 1 - - wa = ((x1 - x) * (y1 - y)).unsqueeze(1) - wb = ((x1 - x) * (y - y0)).unsqueeze(1) - wc = ((x - x0) * (y1 - y)).unsqueeze(1) - wd = ((x - x0) * (y - y0)).unsqueeze(1) - - # Apply default for grid_sample function zero padding - im_padded = F.pad(im, pad=[1, 1, 1, 1], mode='constant', value=0) - padded_h = h + 2 - padded_w = w + 2 - # save points positions after padding - x0, x1, y0, y1 = x0 + 1, x1 + 1, y0 + 1, y1 + 1 - - # Clip coordinates to padded image size - x0 = torch.where(x0 < 0, torch.tensor(0), x0) - x0 = torch.where(x0 > padded_w - 1, torch.tensor(padded_w - 1), x0) - x1 = torch.where(x1 < 0, torch.tensor(0), x1) - x1 = torch.where(x1 > padded_w - 1, torch.tensor(padded_w - 1), x1) - y0 = torch.where(y0 < 0, torch.tensor(0), y0) - y0 = torch.where(y0 > padded_h - 1, torch.tensor(padded_h - 1), y0) - y1 = torch.where(y1 < 0, torch.tensor(0), y1) - y1 = torch.where(y1 > padded_h - 1, torch.tensor(padded_h - 1), y1) - - im_padded = im_padded.view(n, c, -1) - - x0_y0 = (x0 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x0_y1 = (x0 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y0 = (x1 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y1 = (x1 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - - Ia = torch.gather(im_padded, 2, x0_y0) - Ib = torch.gather(im_padded, 2, x0_y1) - Ic = torch.gather(im_padded, 2, x1_y0) - Id = torch.gather(im_padded, 2, x1_y1) - - return (Ia * wa + Ib * wb + Ic * wc + Id * wd).reshape(n, c, gh, gw) - - -def is_in_onnx_export_without_custom_ops(): - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - return torch.onnx.is_in_onnx_export( - ) and not osp.exists(ort_custom_op_path) - - -def normalize(grid): - """Normalize input grid from [-1, 1] to [0, 1] - - Args: - grid (torch.Tensor): The grid to be normalize, range [-1, 1]. - - Returns: - torch.Tensor: Normalized grid, range [0, 1]. - """ - - return (grid + 1.0) / 2.0 - - -def denormalize(grid): - """Denormalize input grid from range [0, 1] to [-1, 1] - - Args: - grid (torch.Tensor): The grid to be denormalize, range [0, 1]. - - Returns: - torch.Tensor: Denormalized grid, range [-1, 1]. - """ - - return grid * 2.0 - 1.0 - - -def generate_grid(num_grid, size, device): - """Generate regular square grid of points in [0, 1] x [0, 1] coordinate - space. - - Args: - num_grid (int): The number of grids to sample, one for each region. - size (tuple[int, int]): The side size of the regular grid. - device (torch.device): Desired device of returned tensor. - - Returns: - torch.Tensor: A tensor of shape (num_grid, size[0]*size[1], 2) that - contains coordinates for the regular grids. - """ - - affine_trans = torch.tensor([[[1., 0., 0.], [0., 1., 0.]]], device=device) - grid = F.affine_grid( - affine_trans, torch.Size((1, 1, *size)), align_corners=False) - grid = normalize(grid) - return grid.view(1, -1, 2).expand(num_grid, -1, -1) - - -def rel_roi_point_to_abs_img_point(rois, rel_roi_points): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (torch.Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (torch.Tensor): Point coordinates inside RoI, relative - to RoI, location, range (0, 1), shape (N, P, 2) - Returns: - torch.Tensor: Image based absolute point coordinates, shape (N, P, 2) - """ - - with torch.no_grad(): - assert rel_roi_points.size(0) == rois.size(0) - assert rois.dim() == 2 - assert rel_roi_points.dim() == 3 - assert rel_roi_points.size(2) == 2 - # remove batch idx - if rois.size(1) == 5: - rois = rois[:, 1:] - abs_img_points = rel_roi_points.clone() - # To avoid an error during exporting to onnx use independent - # variables instead inplace computation - xs = abs_img_points[:, :, 0] * (rois[:, None, 2] - rois[:, None, 0]) - ys = abs_img_points[:, :, 1] * (rois[:, None, 3] - rois[:, None, 1]) - xs += rois[:, None, 0] - ys += rois[:, None, 1] - abs_img_points = torch.stack([xs, ys], dim=2) - return abs_img_points - - -def get_shape_from_feature_map(x): - """Get spatial resolution of input feature map considering exporting to - onnx mode. - - Args: - x (torch.Tensor): Input tensor, shape (N, C, H, W) - - Returns: - torch.Tensor: Spatial resolution (width, height), shape (1, 1, 2) - """ - if torch.onnx.is_in_onnx_export(): - img_shape = shape_as_tensor(x)[2:].flip(0).view(1, 1, 2).to( - x.device).float() - else: - img_shape = torch.tensor(x.shape[2:]).flip(0).view(1, 1, 2).to( - x.device).float() - return img_shape - - -def abs_img_point_to_rel_img_point(abs_img_points, img, spatial_scale=1.): - """Convert image based absolute point coordinates to image based relative - coordinates for sampling. - - Args: - abs_img_points (torch.Tensor): Image based absolute point coordinates, - shape (N, P, 2) - img (tuple or torch.Tensor): (height, width) of image or feature map. - spatial_scale (float, optional): Scale points by this factor. - Default: 1. - - Returns: - Tensor: Image based relative point coordinates for sampling, shape - (N, P, 2). - """ - - assert (isinstance(img, tuple) and len(img) == 2) or \ - (isinstance(img, torch.Tensor) and len(img.shape) == 4) - - if isinstance(img, tuple): - h, w = img - scale = torch.tensor([w, h], - dtype=torch.float, - device=abs_img_points.device) - scale = scale.view(1, 1, 2) - else: - scale = get_shape_from_feature_map(img) - - return abs_img_points / scale * spatial_scale - - -def rel_roi_point_to_rel_img_point(rois, - rel_roi_points, - img, - spatial_scale=1.): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (torch.Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (torch.Tensor): Point coordinates inside RoI, relative - to RoI, location, range (0, 1), shape (N, P, 2) - img (tuple or torch.Tensor): (height, width) of image or feature map. - spatial_scale (float, optional): Scale points by this factor. - Default: 1. - - Returns: - torch.Tensor: Image based relative point coordinates for sampling, - shape (N, P, 2). - """ - - abs_img_point = rel_roi_point_to_abs_img_point(rois, rel_roi_points) - rel_img_point = abs_img_point_to_rel_img_point(abs_img_point, img, - spatial_scale) - - return rel_img_point - - -def point_sample(input, points, align_corners=False, **kwargs): - """A wrapper around :func:`grid_sample` to support 3D point_coords tensors - Unlike :func:`torch.nn.functional.grid_sample` it assumes point_coords to - lie inside ``[0, 1] x [0, 1]`` square. - - Args: - input (torch.Tensor): Feature map, shape (N, C, H, W). - points (torch.Tensor): Image based absolute point coordinates - (normalized), range [0, 1] x [0, 1], shape (N, P, 2) or - (N, Hgrid, Wgrid, 2). - align_corners (bool, optional): Whether align_corners. - Default: False - - Returns: - torch.Tensor: Features of `point` on `input`, shape (N, C, P) or - (N, C, Hgrid, Wgrid). - """ - - add_dim = False - if points.dim() == 3: - add_dim = True - points = points.unsqueeze(2) - if is_in_onnx_export_without_custom_ops(): - # If custom ops for onnx runtime not compiled use python - # implementation of grid_sample function to make onnx graph - # with supported nodes - output = bilinear_grid_sample( - input, denormalize(points), align_corners=align_corners) - else: - output = F.grid_sample( - input, denormalize(points), align_corners=align_corners, **kwargs) - if add_dim: - output = output.squeeze(3) - return output - - -class SimpleRoIAlign(nn.Module): - - def __init__(self, output_size, spatial_scale, aligned=True): - """Simple RoI align in PointRend, faster than standard RoIAlign. - - Args: - output_size (tuple[int]): h, w - spatial_scale (float): scale the input boxes by this number - aligned (bool): if False, use the legacy implementation in - MMDetection, align_corners=True will be used in F.grid_sample. - If True, align the results more perfectly. - """ - - super(SimpleRoIAlign, self).__init__() - self.output_size = _pair(output_size) - self.spatial_scale = float(spatial_scale) - # to be consistent with other RoI ops - self.use_torchvision = False - self.aligned = aligned - - def forward(self, features, rois): - num_imgs = features.size(0) - num_rois = rois.size(0) - rel_roi_points = generate_grid( - num_rois, self.output_size, device=rois.device) - - if torch.onnx.is_in_onnx_export(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois, rel_roi_points, features, self.spatial_scale) - rel_img_points = rel_img_points.reshape(num_imgs, -1, - *rel_img_points.shape[1:]) - point_feats = point_sample( - features, rel_img_points, align_corners=not self.aligned) - point_feats = point_feats.transpose(1, 2) - else: - point_feats = [] - for batch_ind in range(num_imgs): - # unravel batch dim - feat = features[batch_ind].unsqueeze(0) - inds = (rois[:, 0].long() == batch_ind) - if inds.any(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois[inds], rel_roi_points[inds], feat, - self.spatial_scale).unsqueeze(0) - point_feat = point_sample( - feat, rel_img_points, align_corners=not self.aligned) - point_feat = point_feat.squeeze(0).transpose(0, 1) - point_feats.append(point_feat) - - point_feats = torch.cat(point_feats, dim=0) - - channels = features.size(1) - roi_feats = point_feats.reshape(num_rois, channels, *self.output_size) - - return roi_feats - - def __repr__(self): - format_str = self.__class__.__name__ - format_str += '(output_size={}, spatial_scale={}'.format( - self.output_size, self.spatial_scale) - return format_str diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/psa_mask.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/psa_mask.py deleted file mode 100644 index cdf14e62b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/psa_mask.py +++ /dev/null @@ -1,92 +0,0 @@ -# Modified from https://github.com/hszhao/semseg/blob/master/lib/psa -from torch import nn -from torch.autograd import Function -from torch.nn.modules.utils import _pair - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', - ['psamask_forward', 'psamask_backward']) - - -class PSAMaskFunction(Function): - - @staticmethod - def symbolic(g, input, psa_type, mask_size): - return g.op( - 'mmcv::MMCVPSAMask', - input, - psa_type_i=psa_type, - mask_size_i=mask_size) - - @staticmethod - def forward(ctx, input, psa_type, mask_size): - ctx.psa_type = psa_type - ctx.mask_size = _pair(mask_size) - ctx.save_for_backward(input) - - h_mask, w_mask = ctx.mask_size - batch_size, channels, h_feature, w_feature = input.size() - assert channels == h_mask * w_mask - output = input.new_zeros( - (batch_size, h_feature * w_feature, h_feature, w_feature)) - - ext_module.psamask_forward( - input, - output, - psa_type=psa_type, - num_=batch_size, - h_feature=h_feature, - w_feature=w_feature, - h_mask=h_mask, - w_mask=w_mask, - half_h_mask=(h_mask - 1) // 2, - half_w_mask=(w_mask - 1) // 2) - return output - - @staticmethod - def backward(ctx, grad_output): - input = ctx.saved_tensors[0] - psa_type = ctx.psa_type - h_mask, w_mask = ctx.mask_size - batch_size, channels, h_feature, w_feature = input.size() - grad_input = grad_output.new_zeros( - (batch_size, channels, h_feature, w_feature)) - ext_module.psamask_backward( - grad_output, - grad_input, - psa_type=psa_type, - num_=batch_size, - h_feature=h_feature, - w_feature=w_feature, - h_mask=h_mask, - w_mask=w_mask, - half_h_mask=(h_mask - 1) // 2, - half_w_mask=(w_mask - 1) // 2) - return grad_input, None, None, None - - -psa_mask = PSAMaskFunction.apply - - -class PSAMask(nn.Module): - - def __init__(self, psa_type, mask_size=None): - super(PSAMask, self).__init__() - assert psa_type in ['collect', 'distribute'] - if psa_type == 'collect': - psa_type_enum = 0 - else: - psa_type_enum = 1 - self.psa_type_enum = psa_type_enum - self.mask_size = mask_size - self.psa_type = psa_type - - def forward(self, input): - return psa_mask(input, self.psa_type_enum, self.mask_size) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(psa_type={self.psa_type}, ' - s += f'mask_size={self.mask_size})' - return s diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/sync_bn.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/sync_bn.py deleted file mode 100644 index 04302f031..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/ops/sync_bn.py +++ /dev/null @@ -1,279 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn.functional as F -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.module import Module -from torch.nn.parameter import Parameter - -from mmcv.cnn import NORM_LAYERS -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sync_bn_forward_mean', 'sync_bn_forward_var', 'sync_bn_forward_output', - 'sync_bn_backward_param', 'sync_bn_backward_data' -]) - - -class SyncBatchNormFunction(Function): - - @staticmethod - def symbolic(g, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - return g.op( - 'mmcv::MMCVSyncBatchNorm', - input, - running_mean, - running_var, - weight, - bias, - momentum_f=momentum, - eps_f=eps, - group_i=group, - group_size_i=group_size, - stats_mode=stats_mode) - - @staticmethod - def forward(self, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - self.momentum = momentum - self.eps = eps - self.group = group - self.group_size = group_size - self.stats_mode = stats_mode - - assert isinstance( - input, (torch.HalfTensor, torch.FloatTensor, - torch.cuda.HalfTensor, torch.cuda.FloatTensor)), \ - f'only support Half or Float Tensor, but {input.type()}' - output = torch.zeros_like(input) - input3d = input.flatten(start_dim=2) - output3d = output.view_as(input3d) - num_channels = input3d.size(1) - - # ensure mean/var/norm/std are initialized as zeros - # ``torch.empty()`` does not guarantee that - mean = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - var = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - norm = torch.zeros_like( - input3d, dtype=torch.float, device=input3d.device) - std = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - - batch_size = input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_forward_mean(input3d, mean) - batch_flag = torch.ones([1], device=mean.device, dtype=mean.dtype) - else: - # skip updating mean and leave it as zeros when the input is empty - batch_flag = torch.zeros([1], device=mean.device, dtype=mean.dtype) - - # synchronize mean and the batch flag - vec = torch.cat([mean, batch_flag]) - if self.stats_mode == 'N': - vec *= batch_size - if self.group_size > 1: - dist.all_reduce(vec, group=self.group) - total_batch = vec[-1].detach() - mean = vec[:num_channels] - - if self.stats_mode == 'default': - mean = mean / self.group_size - elif self.stats_mode == 'N': - mean = mean / total_batch.clamp(min=1) - else: - raise NotImplementedError - - # leave var as zeros when the input is empty - if batch_size > 0: - ext_module.sync_bn_forward_var(input3d, mean, var) - - if self.stats_mode == 'N': - var *= batch_size - if self.group_size > 1: - dist.all_reduce(var, group=self.group) - - if self.stats_mode == 'default': - var /= self.group_size - elif self.stats_mode == 'N': - var /= total_batch.clamp(min=1) - else: - raise NotImplementedError - - # if the total batch size over all the ranks is zero, - # we should not update the statistics in the current batch - update_flag = total_batch.clamp(max=1) - momentum = update_flag * self.momentum - ext_module.sync_bn_forward_output( - input3d, - mean, - var, - weight, - bias, - running_mean, - running_var, - norm, - std, - output3d, - eps=self.eps, - momentum=momentum, - group_size=self.group_size) - self.save_for_backward(norm, std, weight) - return output - - @staticmethod - @once_differentiable - def backward(self, grad_output): - norm, std, weight = self.saved_tensors - grad_weight = torch.zeros_like(weight) - grad_bias = torch.zeros_like(weight) - grad_input = torch.zeros_like(grad_output) - grad_output3d = grad_output.flatten(start_dim=2) - grad_input3d = grad_input.view_as(grad_output3d) - - batch_size = grad_input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_backward_param(grad_output3d, norm, grad_weight, - grad_bias) - - # all reduce - if self.group_size > 1: - dist.all_reduce(grad_weight, group=self.group) - dist.all_reduce(grad_bias, group=self.group) - grad_weight /= self.group_size - grad_bias /= self.group_size - - if batch_size > 0: - ext_module.sync_bn_backward_data(grad_output3d, weight, - grad_weight, grad_bias, norm, std, - grad_input3d) - - return grad_input, None, None, grad_weight, grad_bias, \ - None, None, None, None, None - - -@NORM_LAYERS.register_module(name='MMSyncBN') -class SyncBatchNorm(Module): - """Synchronized Batch Normalization. - - Args: - num_features (int): number of features/chennels in input tensor - eps (float, optional): a value added to the denominator for numerical - stability. Defaults to 1e-5. - momentum (float, optional): the value used for the running_mean and - running_var computation. Defaults to 0.1. - affine (bool, optional): whether to use learnable affine parameters. - Defaults to True. - track_running_stats (bool, optional): whether to track the running - mean and variance during training. When set to False, this - module does not track such statistics, and initializes statistics - buffers ``running_mean`` and ``running_var`` as ``None``. When - these buffers are ``None``, this module always uses batch - statistics in both training and eval modes. Defaults to True. - group (int, optional): synchronization of stats happen within - each process group individually. By default it is synchronization - across the whole world. Defaults to None. - stats_mode (str, optional): The statistical mode. Available options - includes ``'default'`` and ``'N'``. Defaults to 'default'. - When ``stats_mode=='default'``, it computes the overall statistics - using those from each worker with equal weight, i.e., the - statistics are synchronized and simply divied by ``group``. This - mode will produce inaccurate statistics when empty tensors occur. - When ``stats_mode=='N'``, it compute the overall statistics using - the total number of batches in each worker ignoring the number of - group, i.e., the statistics are synchronized and then divied by - the total batch ``N``. This mode is beneficial when empty tensors - occur during training, as it average the total mean by the real - number of batch. - """ - - def __init__(self, - num_features, - eps=1e-5, - momentum=0.1, - affine=True, - track_running_stats=True, - group=None, - stats_mode='default'): - super(SyncBatchNorm, self).__init__() - self.num_features = num_features - self.eps = eps - self.momentum = momentum - self.affine = affine - self.track_running_stats = track_running_stats - group = dist.group.WORLD if group is None else group - self.group = group - self.group_size = dist.get_world_size(group) - assert stats_mode in ['default', 'N'], \ - f'"stats_mode" only accepts "default" and "N", got "{stats_mode}"' - self.stats_mode = stats_mode - if self.affine: - self.weight = Parameter(torch.Tensor(num_features)) - self.bias = Parameter(torch.Tensor(num_features)) - else: - self.register_parameter('weight', None) - self.register_parameter('bias', None) - if self.track_running_stats: - self.register_buffer('running_mean', torch.zeros(num_features)) - self.register_buffer('running_var', torch.ones(num_features)) - self.register_buffer('num_batches_tracked', - torch.tensor(0, dtype=torch.long)) - else: - self.register_buffer('running_mean', None) - self.register_buffer('running_var', None) - self.register_buffer('num_batches_tracked', None) - self.reset_parameters() - - def reset_running_stats(self): - if self.track_running_stats: - self.running_mean.zero_() - self.running_var.fill_(1) - self.num_batches_tracked.zero_() - - def reset_parameters(self): - self.reset_running_stats() - if self.affine: - self.weight.data.uniform_() # pytorch use ones_() - self.bias.data.zero_() - - def forward(self, input): - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input, got {input.dim()}D input') - if self.momentum is None: - exponential_average_factor = 0.0 - else: - exponential_average_factor = self.momentum - - if self.training and self.track_running_stats: - if self.num_batches_tracked is not None: - self.num_batches_tracked += 1 - if self.momentum is None: # use cumulative moving average - exponential_average_factor = 1.0 / float( - self.num_batches_tracked) - else: # use exponential moving average - exponential_average_factor = self.momentum - - if self.training or not self.track_running_stats: - return SyncBatchNormFunction.apply( - input, self.running_mean, self.running_var, self.weight, - self.bias, exponential_average_factor, self.eps, self.group, - self.group_size, self.stats_mode) - else: - return F.batch_norm(input, self.running_mean, self.running_var, - self.weight, self.bias, False, - exponential_average_factor, self.eps) - - def __repr__(self): - s = self.__class__.__name__ - s += f'({self.num_features}, ' - s += f'eps={self.eps}, ' - s += f'momentum={self.momentum}, ' - s += f'affine={self.affine}, ' - s += f'track_running_stats={self.track_running_stats}, ' - s += f'group_size={self.group_size},' - s += f'stats_mode={self.stats_mode})' - return s diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/__init__.py deleted file mode 100644 index 2ed2c17ad..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collate import collate -from .data_container import DataContainer -from .data_parallel import MMDataParallel -from .distributed import MMDistributedDataParallel -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter, scatter_kwargs -from .utils import is_module_wrapper - -__all__ = [ - 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', - 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/_functions.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/_functions.py deleted file mode 100644 index 95c58bf1a..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/_functions.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import _get_stream - - -def scatter(input, devices, streams=None): - """Scatters tensor across multiple GPUs.""" - if streams is None: - streams = [None] * len(devices) - - if isinstance(input, list): - chunk_size = (len(input) - 1) // len(devices) + 1 - outputs = [ - scatter(input[i], [devices[i // chunk_size]], - [streams[i // chunk_size]]) for i in range(len(input)) - ] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - # TODO: copy to a pinned buffer first (if copying from CPU) - stream = streams[0] if output.numel() > 0 else None - if devices != [-1]: - with torch.cuda.device(devices[0]), torch.cuda.stream(stream): - output = output.cuda(devices[0], non_blocking=True) - - return output - else: - raise Exception(f'Unknown type {type(input)}.') - - -def synchronize_stream(output, devices, streams): - if isinstance(output, list): - chunk_size = len(output) // len(devices) - for i in range(len(devices)): - for j in range(chunk_size): - synchronize_stream(output[i * chunk_size + j], [devices[i]], - [streams[i]]) - elif isinstance(output, torch.Tensor): - if output.numel() != 0: - with torch.cuda.device(devices[0]): - main_stream = torch.cuda.current_stream() - main_stream.wait_stream(streams[0]) - output.record_stream(main_stream) - else: - raise Exception(f'Unknown type {type(output)}.') - - -def get_input_device(input): - if isinstance(input, list): - for item in input: - input_device = get_input_device(item) - if input_device != -1: - return input_device - return -1 - elif isinstance(input, torch.Tensor): - return input.get_device() if input.is_cuda else -1 - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_gpus, input): - input_device = get_input_device(input) - streams = None - if input_device == -1 and target_gpus != [-1]: - # Perform CPU to GPU copies in a background stream - streams = [_get_stream(device) for device in target_gpus] - - outputs = scatter(input, target_gpus, streams) - # Synchronize with the copy stream - if streams is not None: - synchronize_stream(outputs, target_gpus, streams) - - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/collate.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/collate.py deleted file mode 100644 index ad749197d..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/collate.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence - -import torch -import torch.nn.functional as F -from torch.utils.data.dataloader import default_collate - -from .data_container import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Puts each data field into a tensor/DataContainer with outer dimension - batch size. - - Extend default_collate to add support for - :type:`~mmcv.parallel.DataContainer`. There are 3 cases. - - 1. cpu_only = True, e.g., meta data - 2. cpu_only = False, stack = True, e.g., images tensors - 3. cpu_only = False, stack = False, e.g., gt bboxes - """ - - if not isinstance(batch, Sequence): - raise TypeError(f'{batch.dtype} is not supported.') - - if isinstance(batch[0], DataContainer): - stacked = [] - if batch[0].cpu_only: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer( - stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) - elif batch[0].stack: - for i in range(0, len(batch), samples_per_gpu): - assert isinstance(batch[i].data, torch.Tensor) - - if batch[i].pad_dims is not None: - ndim = batch[i].dim() - assert ndim > batch[i].pad_dims - max_shape = [0 for _ in range(batch[i].pad_dims)] - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = batch[i].size(-dim) - for sample in batch[i:i + samples_per_gpu]: - for dim in range(0, ndim - batch[i].pad_dims): - assert batch[i].size(dim) == sample.size(dim) - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = max(max_shape[dim - 1], - sample.size(-dim)) - padded_samples = [] - for sample in batch[i:i + samples_per_gpu]: - pad = [0 for _ in range(batch[i].pad_dims * 2)] - for dim in range(1, batch[i].pad_dims + 1): - pad[2 * dim - - 1] = max_shape[dim - 1] - sample.size(-dim) - padded_samples.append( - F.pad( - sample.data, pad, value=sample.padding_value)) - stacked.append(default_collate(padded_samples)) - elif batch[i].pad_dims is None: - stacked.append( - default_collate([ - sample.data - for sample in batch[i:i + samples_per_gpu] - ])) - else: - raise ValueError( - 'pad_dims should be either None or integers (1-3)') - - else: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer(stacked, batch[0].stack, batch[0].padding_value) - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - return [collate(samples, samples_per_gpu) for samples in transposed] - elif isinstance(batch[0], Mapping): - return { - key: collate([d[key] for d in batch], samples_per_gpu) - for key in batch[0] - } - else: - return default_collate(batch) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/data_container.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/data_container.py deleted file mode 100644 index cedb0d32a..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/data_container.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch - - -def assert_tensor_type(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not isinstance(args[0].data, torch.Tensor): - raise AttributeError( - f'{args[0].__class__.__name__} has no attribute ' - f'{func.__name__} for type {args[0].datatype}') - return func(*args, **kwargs) - - return wrapper - - -class DataContainer: - """A container for any type of objects. - - Typically tensors will be stacked in the collate function and sliced along - some dimension in the scatter function. This behavior has some limitations. - 1. All tensors have to be the same size. - 2. Types are limited (numpy array or Tensor). - - We design `DataContainer` and `MMDataParallel` to overcome these - limitations. The behavior can be either of the following. - - - copy to GPU, pad all tensors to the same size and stack them - - copy to GPU without stacking - - leave the objects as is and pass it to the model - - pad_dims specifies the number of last few dimensions to do padding - """ - - def __init__(self, - data, - stack=False, - padding_value=0, - cpu_only=False, - pad_dims=2): - self._data = data - self._cpu_only = cpu_only - self._stack = stack - self._padding_value = padding_value - assert pad_dims in [None, 1, 2, 3] - self._pad_dims = pad_dims - - def __repr__(self): - return f'{self.__class__.__name__}({repr(self.data)})' - - def __len__(self): - return len(self._data) - - @property - def data(self): - return self._data - - @property - def datatype(self): - if isinstance(self.data, torch.Tensor): - return self.data.type() - else: - return type(self.data) - - @property - def cpu_only(self): - return self._cpu_only - - @property - def stack(self): - return self._stack - - @property - def padding_value(self): - return self._padding_value - - @property - def pad_dims(self): - return self._pad_dims - - @assert_tensor_type - def size(self, *args, **kwargs): - return self.data.size(*args, **kwargs) - - @assert_tensor_type - def dim(self): - return self.data.dim() diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/data_parallel.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/data_parallel.py deleted file mode 100644 index 7a5abeb6e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/data_parallel.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from itertools import chain - -from torch.nn.parallel import DataParallel - -from .scatter_gather import scatter_kwargs - - -class MMDataParallel(DataParallel): - """The DataParallel module that supports DataContainer. - - MMDataParallel has two main differences with PyTorch DataParallel: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data during both GPU and CPU inference. - - It implement two more APIs ``train_step()`` and ``val_step()``. - - .. warning:: - MMDataParallel only supports single GPU training, if you need to - train with multiple GPUs, please use MMDistributedDataParallel - instead. If you have multiple GPUs and you just want to use - MMDataParallel, you can set the environment variable - ``CUDA_VISIBLE_DEVICES=0`` or instantiate ``MMDataParallel`` with - ``device_ids=[0]``. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - device_ids (list[int]): Device IDS of modules to be scattered to. - Defaults to None when GPU is not available. - output_device (str | int): Device ID for output. Defaults to None. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MMDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.dim = dim - - def forward(self, *inputs, **kwargs): - """Override the original forward function. - - The main difference lies in the CPU inference where the data in - :class:`DataContainers` will still be gathered. - """ - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module(*inputs[0], **kwargs[0]) - else: - return super().forward(*inputs, **kwargs) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.train_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.train_step(*inputs[0], **kwargs[0]) - - def val_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.val_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/distributed.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/distributed.py deleted file mode 100644 index 0188ca4ab..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/distributed.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel.distributed import (DistributedDataParallel, - _find_tensors) - -from mmcv import print_log -from mmcv.utils import TORCH_VERSION, digit_version -from .scatter_gather import scatter_kwargs - - -class MMDistributedDataParallel(DistributedDataParallel): - """The DDP module that supports DataContainer. - - MMDDP has two main differences with PyTorch DDP: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data. - - It implement two APIs ``train_step()`` and ``val_step()``. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - """train_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.train_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.train_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.train_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output - - def val_step(self, *inputs, **kwargs): - """val_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.val_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.val_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.val_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/distributed_deprecated.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/distributed_deprecated.py deleted file mode 100644 index b593d4a9e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/distributed_deprecated.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn as nn -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter_kwargs - - -@MODULE_WRAPPERS.register_module() -class MMDistributedDataParallel(nn.Module): - - def __init__(self, - module, - dim=0, - broadcast_buffers=True, - bucket_cap_mb=25): - super(MMDistributedDataParallel, self).__init__() - self.module = module - self.dim = dim - self.broadcast_buffers = broadcast_buffers - - self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 - self._sync_params() - - def _dist_broadcast_coalesced(self, tensors, buffer_size): - for tensors in _take_tensors(tensors, buffer_size): - flat_tensors = _flatten_dense_tensors(tensors) - dist.broadcast(flat_tensors, 0) - for tensor, synced in zip( - tensors, _unflatten_dense_tensors(flat_tensors, tensors)): - tensor.copy_(synced) - - def _sync_params(self): - module_states = list(self.module.state_dict().values()) - if len(module_states) > 0: - self._dist_broadcast_coalesced(module_states, - self.broadcast_bucket_size) - if self.broadcast_buffers: - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) < digit_version('1.0')): - buffers = [b.data for b in self.module._all_buffers()] - else: - buffers = [b.data for b in self.module.buffers()] - if len(buffers) > 0: - self._dist_broadcast_coalesced(buffers, - self.broadcast_bucket_size) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/registry.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/registry.py deleted file mode 100644 index 144f9fb16..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel, DistributedDataParallel - -from mmcv.utils import Registry - -MODULE_WRAPPERS = Registry('module wrapper') -MODULE_WRAPPERS.register_module(module=DataParallel) -MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/scatter_gather.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/scatter_gather.py deleted file mode 100644 index 900ff8856..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import Scatter as OrigScatter - -from ._functions import Scatter -from .data_container import DataContainer - - -def scatter(inputs, target_gpus, dim=0): - """Scatter inputs to target gpus. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_gpus != [-1]: - return OrigScatter.apply(target_gpus, None, dim, obj) - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_gpus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_gpus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_gpus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_gpus, dim) if inputs else [] - kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/utils.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/utils.py deleted file mode 100644 index 0f5712cb4..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/parallel/utils.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import MODULE_WRAPPERS - - -def is_module_wrapper(module): - """Check if a module is a module wrapper. - - The following 3 modules in MMCV (and their subclasses) are regarded as - module wrappers: DataParallel, DistributedDataParallel, - MMDistributedDataParallel (the deprecated version). You may add you own - module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: True if the input module is a module wrapper. - """ - module_wrappers = tuple(MODULE_WRAPPERS.module_dict.values()) - return isinstance(module, module_wrappers) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/__init__.py deleted file mode 100644 index 183d53672..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_module import BaseModule, ModuleDict, ModuleList, Sequential -from .base_runner import BaseRunner -from .builder import RUNNERS, build_runner -from .checkpoint import (CheckpointLoader, _load_checkpoint, - _load_checkpoint_with_prefix, load_checkpoint, - load_state_dict, save_checkpoint, weights_to_cpu) -from .default_constructor import DefaultRunnerConstructor -from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, - init_dist, master_only) -from .epoch_based_runner import EpochBasedRunner, Runner -from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model -from .hooks import (HOOKS, CheckpointHook, ClearMLLoggerHook, ClosureHook, - DistEvalHook, DistSamplerSeedHook, DvcliveLoggerHook, - EMAHook, EvalHook, Fp16OptimizerHook, - GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, Hook, IterTimerHook, - LoggerHook, MlflowLoggerHook, NeptuneLoggerHook, - OptimizerHook, PaviLoggerHook, SegmindLoggerHook, - SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .hooks.lr_updater import StepLrUpdaterHook # noqa -from .hooks.lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, - InvLrUpdaterHook, LinearAnnealingLrUpdaterHook, - LrUpdaterHook, OneCycleLrUpdaterHook, - PolyLrUpdaterHook) -from .hooks.momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .iter_based_runner import IterBasedRunner, IterLoader -from .log_buffer import LogBuffer -from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, - DefaultOptimizerConstructor, build_optimizer, - build_optimizer_constructor) -from .priority import Priority, get_priority -from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed - -# initialize ipu to registor ipu runner to RUNNERS -from mmcv.device import ipu # isort:skip # noqa - -__all__ = [ - 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', - 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', - 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', - 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', - 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', - 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', - 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', - 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', - 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', - 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', - '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', - 'ModuleDict', 'ModuleList', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor', - 'SegmindLoggerHook', 'LinearAnnealingMomentumUpdaterHook', - 'LinearAnnealingLrUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/base_module.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/base_module.py deleted file mode 100644 index 7937eca37..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/base_module.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings -from abc import ABCMeta -from collections import defaultdict -from logging import FileHandler - -import torch.nn as nn - -from mmcv.runner.dist_utils import master_only -from mmcv.utils.logging import get_logger, logger_initialized, print_log - - -class BaseModule(nn.Module, metaclass=ABCMeta): - """Base module for all modules in openmmlab. - - ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional - functionality of parameter initialization. Compared with - ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. - - - ``init_cfg``: the config to control the initialization. - - ``init_weights``: The function of parameter initialization and recording - initialization information. - - ``_params_init_info``: Used to track the parameter initialization - information. This attribute only exists during executing the - ``init_weights``. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, init_cfg=None): - """Initialize BaseModule, inherited from `torch.nn.Module`""" - - # NOTE init_cfg can be defined in different levels, but init_cfg - # in low levels has a higher priority. - - super(BaseModule, self).__init__() - # define default value of init_cfg instead of hard code - # in init_weights() function - self._is_init = False - - self.init_cfg = copy.deepcopy(init_cfg) - - # Backward compatibility in derived classes - # if pretrained is not None: - # warnings.warn('DeprecationWarning: pretrained is a deprecated \ - # key, please consider using init_cfg') - # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - - @property - def is_init(self): - return self._is_init - - def init_weights(self): - """Initialize the weights.""" - - is_top_level_module = False - # check if it is top-level module - if not hasattr(self, '_params_init_info'): - # The `_params_init_info` is used to record the initialization - # information of the parameters - # the key should be the obj:`nn.Parameter` of model and the value - # should be a dict containing - # - init_info (str): The string that describes the initialization. - # - tmp_mean_value (FloatTensor): The mean of the parameter, - # which indicates whether the parameter has been modified. - # this attribute would be deleted after all parameters - # is initialized. - self._params_init_info = defaultdict(dict) - is_top_level_module = True - - # Initialize the `_params_init_info`, - # When detecting the `tmp_mean_value` of - # the corresponding parameter is changed, update related - # initialization information - for name, param in self.named_parameters(): - self._params_init_info[param][ - 'init_info'] = f'The value is the same before and ' \ - f'after calling `init_weights` ' \ - f'of {self.__class__.__name__} ' - self._params_init_info[param][ - 'tmp_mean_value'] = param.data.mean() - - # pass `params_init_info` to all submodules - # All submodules share the same `params_init_info`, - # so it will be updated when parameters are - # modified at any level of the model. - for sub_module in self.modules(): - sub_module._params_init_info = self._params_init_info - - # Get the initialized logger, if not exist, - # create a logger named `mmcv` - logger_names = list(logger_initialized.keys()) - logger_name = logger_names[0] if logger_names else 'mmcv' - - from ..cnn import initialize - from ..cnn.utils.weight_init import update_init_info - module_name = self.__class__.__name__ - if not self._is_init: - if self.init_cfg: - print_log( - f'initialize {module_name} with init_cfg {self.init_cfg}', - logger=logger_name) - initialize(self, self.init_cfg) - if isinstance(self.init_cfg, dict): - # prevent the parameters of - # the pre-trained model - # from being overwritten by - # the `init_weights` - if self.init_cfg['type'] == 'Pretrained': - return - - for m in self.children(): - if hasattr(m, 'init_weights'): - m.init_weights() - # users may overload the `init_weights` - update_init_info( - m, - init_info=f'Initialized by ' - f'user-defined `init_weights`' - f' in {m.__class__.__name__} ') - - self._is_init = True - else: - warnings.warn(f'init_weights of {self.__class__.__name__} has ' - f'been called more than once.') - - if is_top_level_module: - self._dump_init_info(logger_name) - - for sub_module in self.modules(): - del sub_module._params_init_info - - @master_only - def _dump_init_info(self, logger_name): - """Dump the initialization information to a file named - `initialization.log.json` in workdir. - - Args: - logger_name (str): The name of logger. - """ - - logger = get_logger(logger_name) - - with_file_handler = False - # dump the information to the logger file if there is a `FileHandler` - for handler in logger.handlers: - if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True - if not with_file_handler: - for name, param in self.named_parameters(): - print_log( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n ", - logger=logger_name) - - def __repr__(self): - s = super().__repr__() - if self.init_cfg: - s += f'\ninit_cfg={self.init_cfg}' - return s - - -class Sequential(BaseModule, nn.Sequential): - """Sequential module in openmmlab. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, *args, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.Sequential.__init__(self, *args) - - -class ModuleList(BaseModule, nn.ModuleList): - """ModuleList in openmmlab. - - Args: - modules (iterable, optional): an iterable of modules to add. - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleList.__init__(self, modules) - - -class ModuleDict(BaseModule, nn.ModuleDict): - """ModuleDict in openmmlab. - - Args: - modules (dict, optional): a mapping (dictionary) of (string: module) - or an iterable of key-value pairs of type (string, module). - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleDict.__init__(self, modules) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/base_runner.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/base_runner.py deleted file mode 100644 index 12a0025f8..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/base_runner.py +++ /dev/null @@ -1,544 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -import os.path as osp -import warnings -from abc import ABCMeta, abstractmethod - -import torch -from torch.optim import Optimizer - -import mmcv -from ..parallel import is_module_wrapper -from .checkpoint import load_checkpoint -from .dist_utils import get_dist_info -from .hooks import HOOKS, Hook -from .log_buffer import LogBuffer -from .priority import Priority, get_priority -from .utils import get_time_str - - -class BaseRunner(metaclass=ABCMeta): - """The base class of Runner, a training helper for PyTorch. - - All subclasses should implement the following APIs: - - - ``run()`` - - ``train()`` - - ``val()`` - - ``save_checkpoint()`` - - Args: - model (:obj:`torch.nn.Module`): The model to be run. - batch_processor (callable): A callable method that process a data - batch. The interface of this method should be - `batch_processor(model, data, train_mode) -> dict` - optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an - optimizer (in most cases) or a dict of optimizers (in models that - requires more than one optimizer, e.g., GAN). - work_dir (str, optional): The working directory to save checkpoints - and logs. Defaults to None. - logger (:obj:`logging.Logger`): Logger used during training. - Defaults to None. (The default value is just for backward - compatibility) - meta (dict | None): A dict records some import information such as - environment info and seed, which will be logged in logger hook. - Defaults to None. - max_epochs (int, optional): Total training epochs. - max_iters (int, optional): Total training iterations. - """ - - def __init__(self, - model, - batch_processor=None, - optimizer=None, - work_dir=None, - logger=None, - meta=None, - max_iters=None, - max_epochs=None): - if batch_processor is not None: - if not callable(batch_processor): - raise TypeError('batch_processor must be callable, ' - f'but got {type(batch_processor)}') - warnings.warn( - 'batch_processor is deprecated, please implement ' - 'train_step() and val_step() in the model instead.', - DeprecationWarning) - # raise an error is `batch_processor` is not None and - # `model.train_step()` exists. - if is_module_wrapper(model): - _model = model.module - else: - _model = model - if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): - raise RuntimeError( - 'batch_processor and model.train_step()/model.val_step() ' - 'cannot be both available.') - else: - assert hasattr(model, 'train_step') - - # check the type of `optimizer` - if isinstance(optimizer, dict): - for name, optim in optimizer.items(): - if not isinstance(optim, Optimizer): - raise TypeError( - f'optimizer must be a dict of torch.optim.Optimizers, ' - f'but optimizer["{name}"] is a {type(optim)}') - elif not isinstance(optimizer, Optimizer) and optimizer is not None: - raise TypeError( - f'optimizer must be a torch.optim.Optimizer object ' - f'or dict or None, but got {type(optimizer)}') - - # check the type of `logger` - if not isinstance(logger, logging.Logger): - raise TypeError(f'logger must be a logging.Logger object, ' - f'but got {type(logger)}') - - # check the type of `meta` - if meta is not None and not isinstance(meta, dict): - raise TypeError( - f'meta must be a dict or None, but got {type(meta)}') - - self.model = model - self.batch_processor = batch_processor - self.optimizer = optimizer - self.logger = logger - self.meta = meta - # create work_dir - if mmcv.is_str(work_dir): - self.work_dir = osp.abspath(work_dir) - mmcv.mkdir_or_exist(self.work_dir) - elif work_dir is None: - self.work_dir = None - else: - raise TypeError('"work_dir" must be a str or None') - - # get model name from the model class - if hasattr(self.model, 'module'): - self._model_name = self.model.module.__class__.__name__ - else: - self._model_name = self.model.__class__.__name__ - - self._rank, self._world_size = get_dist_info() - self.timestamp = get_time_str() - self.mode = None - self._hooks = [] - self._epoch = 0 - self._iter = 0 - self._inner_iter = 0 - - if max_epochs is not None and max_iters is not None: - raise ValueError( - 'Only one of `max_epochs` or `max_iters` can be set.') - - self._max_epochs = max_epochs - self._max_iters = max_iters - # TODO: Redesign LogBuffer, it is not flexible and elegant enough - self.log_buffer = LogBuffer() - - @property - def model_name(self): - """str: Name of the model, usually the module class name.""" - return self._model_name - - @property - def rank(self): - """int: Rank of current process. (distributed training)""" - return self._rank - - @property - def world_size(self): - """int: Number of processes participating in the job. - (distributed training)""" - return self._world_size - - @property - def hooks(self): - """list[:obj:`Hook`]: A list of registered hooks.""" - return self._hooks - - @property - def epoch(self): - """int: Current epoch.""" - return self._epoch - - @property - def iter(self): - """int: Current iteration.""" - return self._iter - - @property - def inner_iter(self): - """int: Iteration in an epoch.""" - return self._inner_iter - - @property - def max_epochs(self): - """int: Maximum training epochs.""" - return self._max_epochs - - @property - def max_iters(self): - """int: Maximum training iterations.""" - return self._max_iters - - @abstractmethod - def train(self): - pass - - @abstractmethod - def val(self): - pass - - @abstractmethod - def run(self, data_loaders, workflow, **kwargs): - pass - - @abstractmethod - def save_checkpoint(self, - out_dir, - filename_tmpl, - save_optimizer=True, - meta=None, - create_symlink=True): - pass - - def current_lr(self): - """Get current learning rates. - - Returns: - list[float] | dict[str, list[float]]: Current learning rates of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - if isinstance(self.optimizer, torch.optim.Optimizer): - lr = [group['lr'] for group in self.optimizer.param_groups] - elif isinstance(self.optimizer, dict): - lr = dict() - for name, optim in self.optimizer.items(): - lr[name] = [group['lr'] for group in optim.param_groups] - else: - raise RuntimeError( - 'lr is not applicable because optimizer does not exist.') - return lr - - def current_momentum(self): - """Get current momentums. - - Returns: - list[float] | dict[str, list[float]]: Current momentums of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - - def _get_momentum(optimizer): - momentums = [] - for group in optimizer.param_groups: - if 'momentum' in group.keys(): - momentums.append(group['momentum']) - elif 'betas' in group.keys(): - momentums.append(group['betas'][0]) - else: - momentums.append(0) - return momentums - - if self.optimizer is None: - raise RuntimeError( - 'momentum is not applicable because optimizer does not exist.') - elif isinstance(self.optimizer, torch.optim.Optimizer): - momentums = _get_momentum(self.optimizer) - elif isinstance(self.optimizer, dict): - momentums = dict() - for name, optim in self.optimizer.items(): - momentums[name] = _get_momentum(optim) - return momentums - - def register_hook(self, hook, priority='NORMAL'): - """Register a hook into the hook list. - - The hook will be inserted into a priority queue, with the specified - priority (See :class:`Priority` for details of priorities). - For hooks with the same priority, they will be triggered in the same - order as they are registered. - - Args: - hook (:obj:`Hook`): The hook to be registered. - priority (int or str or :obj:`Priority`): Hook priority. - Lower value means higher priority. - """ - assert isinstance(hook, Hook) - if hasattr(hook, 'priority'): - raise ValueError('"priority" is a reserved attribute for hooks') - priority = get_priority(priority) - hook.priority = priority - # insert the hook to a sorted list - inserted = False - for i in range(len(self._hooks) - 1, -1, -1): - if priority >= self._hooks[i].priority: - self._hooks.insert(i + 1, hook) - inserted = True - break - if not inserted: - self._hooks.insert(0, hook) - - def register_hook_from_cfg(self, hook_cfg): - """Register a hook from its cfg. - - Args: - hook_cfg (dict): Hook config. It should have at least keys 'type' - and 'priority' indicating its type and priority. - - Note: - The specific hook class to register should not use 'type' and - 'priority' arguments during initialization. - """ - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = mmcv.build_from_cfg(hook_cfg, HOOKS) - self.register_hook(hook, priority=priority) - - def call_hook(self, fn_name): - """Call all hooks. - - Args: - fn_name (str): The function name in each hook to be called, such as - "before_train_epoch". - """ - for hook in self._hooks: - getattr(hook, fn_name)(self) - - def get_hook_info(self): - # Get hooks info in each stage - stage_hook_map = {stage: [] for stage in Hook.stages} - for hook in self.hooks: - try: - priority = Priority(hook.priority).name - except ValueError: - priority = hook.priority - classname = hook.__class__.__name__ - hook_info = f'({priority:<12}) {classname:<35}' - for trigger_stage in hook.get_triggered_stages(): - stage_hook_map[trigger_stage].append(hook_info) - - stage_hook_infos = [] - for stage in Hook.stages: - hook_infos = stage_hook_map[stage] - if len(hook_infos) > 0: - info = f'{stage}:\n' - info += '\n'.join(hook_infos) - info += '\n -------------------- ' - stage_hook_infos.append(info) - return '\n'.join(stage_hook_infos) - - def load_checkpoint(self, - filename, - map_location='cpu', - strict=False, - revise_keys=[(r'^module.', '')]): - return load_checkpoint( - self.model, - filename, - map_location, - strict, - self.logger, - revise_keys=revise_keys) - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - if map_location == 'default': - if torch.cuda.is_available(): - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint(checkpoint) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - if self.meta is None: - self.meta = {} - self.meta.setdefault('hook_msgs', {}) - # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages - self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) - - # Re-calculate the number of iterations when resuming - # models with different number of GPUs - if 'config' in checkpoint['meta']: - config = mmcv.Config.fromstring( - checkpoint['meta']['config'], file_format='.py') - previous_gpu_ids = config.get('gpu_ids', None) - if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( - previous_gpu_ids) != self.world_size: - self._iter = int(self._iter * len(previous_gpu_ids) / - self.world_size) - self.logger.info('the iteration number is changed due to ' - 'change of GPU number') - - # resume meta information meta - self.meta = checkpoint['meta'] - - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - elif isinstance(lr_config, dict): - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = mmcv.build_from_cfg(lr_config, HOOKS) - else: - hook = lr_config - self.register_hook(hook, priority='VERY_HIGH') - - def register_momentum_hook(self, momentum_config): - if momentum_config is None: - return - if isinstance(momentum_config, dict): - assert 'policy' in momentum_config - policy_type = momentum_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of momentum updater. - # Since this is not applicable for - # `CosineAnnealingMomentumUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'MomentumUpdaterHook' - momentum_config['type'] = hook_type - hook = mmcv.build_from_cfg(momentum_config, HOOKS) - else: - hook = momentum_config - self.register_hook(hook, priority='HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = mmcv.build_from_cfg(optimizer_config, HOOKS) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def register_checkpoint_hook(self, checkpoint_config): - if checkpoint_config is None: - return - if isinstance(checkpoint_config, dict): - checkpoint_config.setdefault('type', 'CheckpointHook') - hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) - else: - hook = checkpoint_config - self.register_hook(hook, priority='NORMAL') - - def register_logger_hooks(self, log_config): - if log_config is None: - return - log_interval = log_config['interval'] - for info in log_config['hooks']: - logger_hook = mmcv.build_from_cfg( - info, HOOKS, default_args=dict(interval=log_interval)) - self.register_hook(logger_hook, priority='VERY_LOW') - - def register_timer_hook(self, timer_config): - if timer_config is None: - return - if isinstance(timer_config, dict): - timer_config_ = copy.deepcopy(timer_config) - hook = mmcv.build_from_cfg(timer_config_, HOOKS) - else: - hook = timer_config - self.register_hook(hook, priority='LOW') - - def register_custom_hooks(self, custom_config): - if custom_config is None: - return - - if not isinstance(custom_config, list): - custom_config = [custom_config] - - for item in custom_config: - if isinstance(item, dict): - self.register_hook_from_cfg(item) - else: - self.register_hook(item, priority='NORMAL') - - def register_profiler_hook(self, profiler_config): - if profiler_config is None: - return - if isinstance(profiler_config, dict): - profiler_config.setdefault('type', 'ProfilerHook') - hook = mmcv.build_from_cfg(profiler_config, HOOKS) - else: - hook = profiler_config - self.register_hook(hook) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - timer_config=dict(type='IterTimerHook'), - custom_hooks_config=None): - """Register default and custom hooks for training. - - Default and custom hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - self.register_lr_hook(lr_config) - self.register_momentum_hook(momentum_config) - self.register_optimizer_hook(optimizer_config) - self.register_checkpoint_hook(checkpoint_config) - self.register_timer_hook(timer_config) - self.register_logger_hooks(log_config) - self.register_custom_hooks(custom_hooks_config) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/builder.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/builder.py deleted file mode 100644 index 77c96ba0b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/builder.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from ..utils import Registry - -RUNNERS = Registry('runner') -RUNNER_BUILDERS = Registry('runner builder') - - -def build_runner_constructor(cfg): - return RUNNER_BUILDERS.build(cfg) - - -def build_runner(cfg, default_args=None): - runner_cfg = copy.deepcopy(cfg) - constructor_type = runner_cfg.pop('constructor', - 'DefaultRunnerConstructor') - runner_constructor = build_runner_constructor( - dict( - type=constructor_type, - runner_cfg=runner_cfg, - default_args=default_args)) - runner = runner_constructor() - return runner diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/checkpoint.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/checkpoint.py deleted file mode 100644 index 835ee725a..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/checkpoint.py +++ /dev/null @@ -1,759 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os -import os.path as osp -import pkgutil -import re -import time -import warnings -from collections import OrderedDict -from importlib import import_module -from tempfile import TemporaryDirectory - -import torch -import torchvision -from torch.optim import Optimizer - -import mmcv -from ..fileio import FileClient -from ..fileio import load as load_file -from ..parallel import is_module_wrapper -from ..utils import digit_version, load_url, mkdir_or_exist -from .dist_utils import get_dist_info - -ENV_MMCV_HOME = 'MMCV_HOME' -ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' -DEFAULT_CACHE_DIR = '~/.cache' - - -def _get_mmcv_home(): - mmcv_home = os.path.expanduser( - os.getenv( - ENV_MMCV_HOME, - os.path.join( - os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) - - mkdir_or_exist(mmcv_home) - return mmcv_home - - -def load_state_dict(module, state_dict, strict=False, logger=None): - """Load state_dict to a module. - - This method is modified from :meth:`torch.nn.Module.load_state_dict`. - Default value for ``strict`` is set to ``False`` and the message for - param mismatch will be shown even if strict is False. - - Args: - module (Module): Module that receives the state_dict. - state_dict (OrderedDict): Weights. - strict (bool): whether to strictly enforce that the keys - in :attr:`state_dict` match the keys returned by this module's - :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. - logger (:obj:`logging.Logger`, optional): Logger to log the error - message. If not specified, print function will be used. - """ - unexpected_keys = [] - all_missing_keys = [] - err_msg = [] - - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata - - # use _load_from_state_dict to enable checkpoint version control - def load(module, prefix=''): - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - local_metadata = {} if metadata is None else metadata.get( - prefix[:-1], {}) - module._load_from_state_dict(state_dict, prefix, local_metadata, True, - all_missing_keys, unexpected_keys, - err_msg) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(module) - load = None # break load->load reference cycle - - # ignore "num_batches_tracked" of BN layers - missing_keys = [ - key for key in all_missing_keys if 'num_batches_tracked' not in key - ] - - if unexpected_keys: - err_msg.append('unexpected key in source ' - f'state_dict: {", ".join(unexpected_keys)}\n') - if missing_keys: - err_msg.append( - f'missing keys in source state_dict: {", ".join(missing_keys)}\n') - - rank, _ = get_dist_info() - if len(err_msg) > 0 and rank == 0: - err_msg.insert( - 0, 'The model and loaded state dict do not match exactly\n') - err_msg = '\n'.join(err_msg) - if strict: - raise RuntimeError(err_msg) - elif logger is not None: - logger.warning(err_msg) - else: - print(err_msg) - - -def get_torchvision_models(): - if digit_version(torchvision.__version__) < digit_version('0.13.0a0'): - model_urls = dict() - # When the version of torchvision is lower than 0.13, the model url is - # not declared in `torchvision.model.__init__.py`, so we need to - # iterate through `torchvision.models.__path__` to get the url for each - # model. - for _, name, ispkg in pkgutil.walk_packages( - torchvision.models.__path__): - if ispkg: - continue - _zoo = import_module(f'torchvision.models.{name}') - if hasattr(_zoo, 'model_urls'): - _urls = getattr(_zoo, 'model_urls') - model_urls.update(_urls) - else: - # Since torchvision bumps to v0.13, the weight loading logic, - # model keys and model urls have been changed. Here the URLs of old - # version is loaded to avoid breaking back compatibility. If the - # torchvision version>=0.13.0, new URLs will be added. Users can get - # the resnet50 checkpoint by setting 'resnet50.imagent1k_v1', - # 'resnet50' or 'ResNet50_Weights.IMAGENET1K_V1' in the config. - json_path = osp.join(mmcv.__path__[0], - 'model_zoo/torchvision_0.12.json') - model_urls = mmcv.load(json_path) - for cls_name, cls in torchvision.models.__dict__.items(): - # The name of torchvision model weights classes ends with - # `_Weights` such as `ResNet18_Weights`. However, some model weight - # classes, such as `MNASNet0_75_Weights` does not have any urls in - # torchvision 0.13.0 and cannot be iterated. Here we simply check - # `DEFAULT` attribute to ensure the class is not empty. - if (not cls_name.endswith('_Weights') - or not hasattr(cls, 'DEFAULT')): - continue - # Since `cls.DEFAULT` can not be accessed by iterating cls, we set - # default urls explicitly. - cls_key = cls_name.replace('_Weights', '').lower() - model_urls[f'{cls_key}.default'] = cls.DEFAULT.url - for weight_enum in cls: - cls_key = cls_name.replace('_Weights', '').lower() - cls_key = f'{cls_key}.{weight_enum.name.lower()}' - model_urls[cls_key] = weight_enum.url - - return model_urls - - -def get_external_models(): - mmcv_home = _get_mmcv_home() - default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') - default_urls = load_file(default_json_path) - assert isinstance(default_urls, dict) - external_json_path = osp.join(mmcv_home, 'open_mmlab.json') - if osp.exists(external_json_path): - external_urls = load_file(external_json_path) - assert isinstance(external_urls, dict) - default_urls.update(external_urls) - - return default_urls - - -def get_mmcls_models(): - mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') - mmcls_urls = load_file(mmcls_json_path) - - return mmcls_urls - - -def get_deprecated_model_names(): - deprecate_json_path = osp.join(mmcv.__path__[0], - 'model_zoo/deprecated.json') - deprecate_urls = load_file(deprecate_json_path) - assert isinstance(deprecate_urls, dict) - - return deprecate_urls - - -def _process_mmcls_checkpoint(checkpoint): - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - # Some checkpoints converted from 3rd-party repo don't - # have the "state_dict" key. - state_dict = checkpoint - new_state_dict = OrderedDict() - for k, v in state_dict.items(): - if k.startswith('backbone.'): - new_state_dict[k[9:]] = v - new_checkpoint = dict(state_dict=new_state_dict) - - return new_checkpoint - - -class CheckpointLoader: - """A general checkpoint loader to manage all schemes.""" - - _schemes = {} - - @classmethod - def _register_scheme(cls, prefixes, loader, force=False): - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if (prefix not in cls._schemes) or force: - cls._schemes[prefix] = loader - else: - raise KeyError( - f'{prefix} is already registered as a loader backend, ' - 'add "force=True" if you want to override it') - # sort, longer prefixes take priority - cls._schemes = OrderedDict( - sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) - - @classmethod - def register_scheme(cls, prefixes, loader=None, force=False): - """Register a loader to CheckpointLoader. - - This method can be used as a normal class method or a decorator. - - Args: - prefixes (str or list[str] or tuple[str]): - The prefix of the registered loader. - loader (function, optional): The loader function to be registered. - When this method is used as a decorator, loader is None. - Defaults to None. - force (bool, optional): Whether to override the loader - if the prefix has already been registered. Defaults to False. - """ - - if loader is not None: - cls._register_scheme(prefixes, loader, force=force) - return - - def _register(loader_cls): - cls._register_scheme(prefixes, loader_cls, force=force) - return loader_cls - - return _register - - @classmethod - def _get_checkpoint_loader(cls, path): - """Finds a loader that supports the given path. Falls back to the local - loader if no other loader is found. - - Args: - path (str): checkpoint path - - Returns: - callable: checkpoint loader - """ - for p in cls._schemes: - # use regular match to handle some cases that where the prefix of - # loader has a prefix. For example, both 's3://path' and - # 'open-mmlab:s3://path' should return `load_from_ceph` - if re.match(p, path) is not None: - return cls._schemes[p] - - @classmethod - def load_checkpoint(cls, filename, map_location=None, logger=None): - """load checkpoint through URL scheme path. - - Args: - filename (str): checkpoint file name with given prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - logger (:mod:`logging.Logger`, optional): The logger for message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint_loader = cls._get_checkpoint_loader(filename) - class_name = checkpoint_loader.__name__ - mmcv.print_log( - f'load checkpoint from {class_name[10:]} path: {filename}', logger) - return checkpoint_loader(filename, map_location) - - -@CheckpointLoader.register_scheme(prefixes='') -def load_from_local(filename, map_location): - """load checkpoint by local file path. - - Args: - filename (str): local checkpoint file path - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - filename = osp.expanduser(filename) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) -def load_from_http(filename, map_location=None, model_dir=None): - """load checkpoint through HTTP or HTTPS scheme path. In distributed - setting, this function only download checkpoint at local rank 0. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - model_dir (string, optional): directory in which to save the object, - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - rank, world_size = get_dist_info() - if rank == 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - if world_size > 1: - torch.distributed.barrier() - if rank > 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='pavi://') -def load_from_pavi(filename, map_location=None): - """load checkpoint through the file path prefixed with pavi. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with pavi prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - assert filename.startswith('pavi://'), \ - f'Expected filename startswith `pavi://`, but get {filename}' - model_path = filename[7:] - - try: - from pavi import modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - - model = modelcloud.get(model_path) - with TemporaryDirectory() as tmp_dir: - downloaded_file = osp.join(tmp_dir, model.name) - model.download(downloaded_file) - checkpoint = torch.load(downloaded_file, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=r'(\S+\:)?s3://') -def load_from_ceph(filename, map_location=None, backend='petrel'): - """load checkpoint through the file path prefixed with s3. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Note: - Since v1.4.1, the registered scheme prefixes have been enhanced to - support bucket names in the path prefix, e.g. 's3://xx.xx/xx.path', - 'bucket1:s3://xx.xx/xx.path'. - - Args: - filename (str): checkpoint file path with s3 prefix - map_location (str, optional): Same as :func:`torch.load`. - backend (str, optional): The storage backend type. Options are 'ceph', - 'petrel'. Default: 'petrel'. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - allowed_backends = ['ceph', 'petrel'] - if backend not in allowed_backends: - raise ValueError(f'Load from Backend {backend} is not supported.') - - if backend == 'ceph': - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - - # CephClient and PetrelBackend have the same prefix 's3://' and the latter - # will be chosen as default. If PetrelBackend can not be instantiated - # successfully, the CephClient will be chosen. - try: - file_client = FileClient(backend=backend) - except ImportError: - allowed_backends.remove(backend) - file_client = FileClient(backend=allowed_backends[0]) - - with io.BytesIO(file_client.get(filename)) as buffer: - checkpoint = torch.load(buffer, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) -def load_from_torchvision(filename, map_location=None): - """load checkpoint through the file path prefixed with modelzoo or - torchvision. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - model_urls = get_torchvision_models() - if filename.startswith('modelzoo://'): - warnings.warn( - 'The URL scheme of "modelzoo://" is deprecated, please ' - 'use "torchvision://" instead', DeprecationWarning) - model_name = filename[11:] - else: - model_name = filename[14:] - - # Support getting model urls in the same way as torchvision - # `ResNet50_Weights.IMAGENET1K_V1` will be mapped to - # resnet50.imagenet1k_v1. - model_name = model_name.lower().replace('_weights', '') - return load_from_http(model_urls[model_name], map_location=map_location) - - -@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) -def load_from_openmmlab(filename, map_location=None): - """load checkpoint through the file path prefixed with open-mmlab or - openmmlab. - - Args: - filename (str): checkpoint file path with open-mmlab or - openmmlab prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_external_models() - prefix_str = 'open-mmlab://' - if filename.startswith(prefix_str): - model_name = filename[13:] - else: - model_name = filename[12:] - prefix_str = 'openmmlab://' - - deprecated_urls = get_deprecated_model_names() - if model_name in deprecated_urls: - warnings.warn( - f'{prefix_str}{model_name} is deprecated in favor ' - f'of {prefix_str}{deprecated_urls[model_name]}', - DeprecationWarning) - model_name = deprecated_urls[model_name] - model_url = model_urls[model_name] - # check if is url - if model_url.startswith(('http://', 'https://')): - checkpoint = load_from_http(model_url, map_location=map_location) - else: - filename = osp.join(_get_mmcv_home(), model_url) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='mmcls://') -def load_from_mmcls(filename, map_location=None): - """load checkpoint through the file path prefixed with mmcls. - - Args: - filename (str): checkpoint file path with mmcls prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_mmcls_models() - model_name = filename[8:] - checkpoint = load_from_http( - model_urls[model_name], map_location=map_location) - checkpoint = _process_mmcls_checkpoint(checkpoint) - return checkpoint - - -def _load_checkpoint(filename, map_location=None, logger=None): - """Load checkpoint from somewhere (modelzoo, file, url). - - Args: - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str, optional): Same as :func:`torch.load`. - Default: None. - logger (:mod:`logging.Logger`, optional): The logger for error message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. It can be either an - OrderedDict storing model weights or a dict containing other - information, which depends on the checkpoint. - """ - return CheckpointLoader.load_checkpoint(filename, map_location, logger) - - -def _load_checkpoint_with_prefix(prefix, filename, map_location=None): - """Load partial pretrained model with specific prefix. - - Args: - prefix (str): The prefix of sub-module. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str | None): Same as :func:`torch.load`. Default: None. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint = _load_checkpoint(filename, map_location=map_location) - - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - if not prefix.endswith('.'): - prefix += '.' - prefix_len = len(prefix) - - state_dict = { - k[prefix_len:]: v - for k, v in state_dict.items() if k.startswith(prefix) - } - - assert state_dict, f'{prefix} is not in the pretrained model' - return state_dict - - -def load_checkpoint(model, - filename, - map_location=None, - strict=False, - logger=None, - revise_keys=[(r'^module\.', '')]): - """Load checkpoint from a file or URI. - - Args: - model (Module): Module to load checkpoint. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str): Same as :func:`torch.load`. - strict (bool): Whether to allow different params for the model and - checkpoint. - logger (:mod:`logging.Logger` or None): The logger for error message. - revise_keys (list): A list of customized keywords to modify the - state_dict in checkpoint. Each item is a (pattern, replacement) - pair of the regular expression operations. Default: strip - the prefix 'module.' by [(r'^module\\.', '')]. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - checkpoint = _load_checkpoint(filename, map_location, logger) - # OrderedDict is a subclass of dict - if not isinstance(checkpoint, dict): - raise RuntimeError( - f'No state_dict found in checkpoint file {filename}') - # get state_dict from checkpoint - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - - # strip prefix of state_dict - metadata = getattr(state_dict, '_metadata', OrderedDict()) - for p, r in revise_keys: - state_dict = OrderedDict( - {re.sub(p, r, k): v - for k, v in state_dict.items()}) - # Keep metadata in state_dict - state_dict._metadata = metadata - - # load state_dict - load_state_dict(model, state_dict, strict, logger) - return checkpoint - - -def weights_to_cpu(state_dict): - """Copy a model state_dict to cpu. - - Args: - state_dict (OrderedDict): Model weights on GPU. - - Returns: - OrderedDict: Model weights on GPU. - """ - state_dict_cpu = OrderedDict() - for key, val in state_dict.items(): - state_dict_cpu[key] = val.cpu() - # Keep metadata in state_dict - state_dict_cpu._metadata = getattr(state_dict, '_metadata', OrderedDict()) - return state_dict_cpu - - -def _save_to_state_dict(module, destination, prefix, keep_vars): - """Saves module state to `destination` dictionary. - - This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. - - Args: - module (nn.Module): The module to generate state_dict. - destination (dict): A dict where state will be stored. - prefix (str): The prefix for parameters and buffers used in this - module. - """ - for name, param in module._parameters.items(): - if param is not None: - destination[prefix + name] = param if keep_vars else param.detach() - for name, buf in module._buffers.items(): - # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d - if buf is not None: - destination[prefix + name] = buf if keep_vars else buf.detach() - - -def get_state_dict(module, destination=None, prefix='', keep_vars=False): - """Returns a dictionary containing a whole state of the module. - - Both parameters and persistent buffers (e.g. running averages) are - included. Keys are corresponding parameter and buffer names. - - This method is modified from :meth:`torch.nn.Module.state_dict` to - recursively check parallel module in case that the model has a complicated - structure, e.g., nn.Module(nn.Module(DDP)). - - Args: - module (nn.Module): The module to generate state_dict. - destination (OrderedDict): Returned dict for the state of the - module. - prefix (str): Prefix of the key. - keep_vars (bool): Whether to keep the variable property of the - parameters. Default: False. - - Returns: - dict: A dictionary containing a whole state of the module. - """ - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - - # below is the same as torch.nn.Module.state_dict() - if destination is None: - destination = OrderedDict() - destination._metadata = OrderedDict() - destination._metadata[prefix[:-1]] = local_metadata = dict( - version=module._version) - _save_to_state_dict(module, destination, prefix, keep_vars) - for name, child in module._modules.items(): - if child is not None: - get_state_dict( - child, destination, prefix + name + '.', keep_vars=keep_vars) - for hook in module._state_dict_hooks.values(): - hook_result = hook(module, destination, prefix, local_metadata) - if hook_result is not None: - destination = hook_result - return destination - - -def save_checkpoint(model, - filename, - optimizer=None, - meta=None, - file_client_args=None): - """Save checkpoint to file. - - The checkpoint will have 3 fields: ``meta``, ``state_dict`` and - ``optimizer``. By default ``meta`` will contain version and time info. - - Args: - model (Module): Module whose params are to be saved. - filename (str): Checkpoint filename. - optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. - meta (dict, optional): Metadata to be saved in checkpoint. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError(f'meta must be a dict or None, but got {type(meta)}') - meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) - - if is_module_wrapper(model): - model = model.module - - if hasattr(model, 'CLASSES') and model.CLASSES is not None: - # save class name to the meta - meta.update(CLASSES=model.CLASSES) - - checkpoint = { - 'meta': meta, - 'state_dict': weights_to_cpu(get_state_dict(model)) - } - # save optimizer state dict in the checkpoint - if isinstance(optimizer, Optimizer): - checkpoint['optimizer'] = optimizer.state_dict() - elif isinstance(optimizer, dict): - checkpoint['optimizer'] = {} - for name, optim in optimizer.items(): - checkpoint['optimizer'][name] = optim.state_dict() - - if filename.startswith('pavi://'): - if file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" if filename starts with' - f'"pavi://", but got {file_client_args}') - try: - from pavi import exception, modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - model_path = filename[7:] - root = modelcloud.Folder() - model_dir, model_name = osp.split(model_path) - try: - model = modelcloud.get(model_dir) - except exception.NodeNotFoundError: - model = root.create_training_model(model_dir) - with TemporaryDirectory() as tmp_dir: - checkpoint_file = osp.join(tmp_dir, model_name) - with open(checkpoint_file, 'wb') as f: - torch.save(checkpoint, f) - f.flush() - model.create_file(checkpoint_file, name=model_name) - else: - file_client = FileClient.infer_client(file_client_args, filename) - with io.BytesIO() as f: - torch.save(checkpoint, f) - file_client.put(f.getvalue(), filename) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/default_constructor.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/default_constructor.py deleted file mode 100644 index 4a4f2cc64..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/default_constructor.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import RUNNER_BUILDERS, RUNNERS - - -@RUNNER_BUILDERS.register_module() -class DefaultRunnerConstructor: - """Default constructor for runners. - - Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. - For example, We can inject some new properties and functions for `Runner`. - - Example: - >>> from mmcv.runner import RUNNER_BUILDERS, build_runner - >>> # Define a new RunnerReconstructor - >>> @RUNNER_BUILDERS.register_module() - >>> class MyRunnerConstructor: - ... def __init__(self, runner_cfg, default_args=None): - ... if not isinstance(runner_cfg, dict): - ... raise TypeError('runner_cfg should be a dict', - ... f'but got {type(runner_cfg)}') - ... self.runner_cfg = runner_cfg - ... self.default_args = default_args - ... - ... def __call__(self): - ... runner = RUNNERS.build(self.runner_cfg, - ... default_args=self.default_args) - ... # Add new properties for existing runner - ... runner.my_name = 'my_runner' - ... runner.my_function = lambda self: print(self.my_name) - ... ... - >>> # build your runner - >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, - ... constructor='MyRunnerConstructor') - >>> runner = build_runner(runner_cfg) - """ - - def __init__(self, runner_cfg, default_args=None): - if not isinstance(runner_cfg, dict): - raise TypeError('runner_cfg should be a dict', - f'but got {type(runner_cfg)}') - self.runner_cfg = runner_cfg - self.default_args = default_args - - def __call__(self): - return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/dist_utils.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/dist_utils.py deleted file mode 100644 index 26d77a8f9..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/dist_utils.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved -import functools -import os -import socket -import subprocess -from collections import OrderedDict - -import torch -import torch.multiprocessing as mp -from torch import distributed as dist -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import IS_MLU_AVAILABLE - - -def _find_free_port(): - # Copied from https://github.com/facebookresearch/detectron2/blob/main/detectron2/engine/launch.py # noqa: E501 - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - # Binding to port 0 will cause the OS to find an available port for us - sock.bind(('', 0)) - port = sock.getsockname()[1] - sock.close() - # NOTE: there is still a chance the port could be taken by other processes. - return port - - -def _is_free_port(port): - ips = socket.gethostbyname_ex(socket.gethostname())[-1] - ips.append('localhost') - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - return all(s.connect_ex((ip, port)) != 0 for ip in ips) - - -def init_dist(launcher, backend='nccl', **kwargs): - if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') - if launcher == 'pytorch': - _init_dist_pytorch(backend, **kwargs) - elif launcher == 'mpi': - _init_dist_mpi(backend, **kwargs) - elif launcher == 'slurm': - _init_dist_slurm(backend, **kwargs) - else: - raise ValueError(f'Invalid launcher type: {launcher}') - - -def _init_dist_pytorch(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['RANK']) - if IS_MLU_AVAILABLE: - import torch_mlu # noqa: F401 - torch.mlu.set_device(rank) - dist.init_process_group( - backend='cncl', - rank=rank, - world_size=int(os.environ['WORLD_SIZE']), - **kwargs) - else: - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_mpi(backend, **kwargs): - local_rank = int(os.environ['OMPI_COMM_WORLD_LOCAL_RANK']) - torch.cuda.set_device(local_rank) - if 'MASTER_PORT' not in os.environ: - # 29500 is torch.distributed default port - os.environ['MASTER_PORT'] = '29500' - if 'MASTER_ADDR' not in os.environ: - raise KeyError('The environment variable MASTER_ADDR is not set') - os.environ['WORLD_SIZE'] = os.environ['OMPI_COMM_WORLD_SIZE'] - os.environ['RANK'] = os.environ['OMPI_COMM_WORLD_RANK'] - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_slurm(backend, port=None): - """Initialize slurm distributed training environment. - - If argument ``port`` is not specified, then the master port will be system - environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system - environment variable, then a default port ``29500`` will be used. - - Args: - backend (str): Backend of torch.distributed. - port (int, optional): Master port. Defaults to None. - """ - proc_id = int(os.environ['SLURM_PROCID']) - ntasks = int(os.environ['SLURM_NTASKS']) - node_list = os.environ['SLURM_NODELIST'] - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(proc_id % num_gpus) - addr = subprocess.getoutput( - f'scontrol show hostname {node_list} | head -n1') - # specify master port - if port is not None: - os.environ['MASTER_PORT'] = str(port) - elif 'MASTER_PORT' in os.environ: - pass # use MASTER_PORT in the environment variable - else: - # if torch.distributed default port(29500) is available - # then use it, else find a free port - if _is_free_port(29500): - os.environ['MASTER_PORT'] = '29500' - else: - os.environ['MASTER_PORT'] = str(_find_free_port()) - # use MASTER_ADDR in the environment variable if it already exists - if 'MASTER_ADDR' not in os.environ: - os.environ['MASTER_ADDR'] = addr - os.environ['WORLD_SIZE'] = str(ntasks) - os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) - os.environ['RANK'] = str(proc_id) - dist.init_process_group(backend=backend) - - -def get_dist_info(): - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - world_size = dist.get_world_size() - else: - rank = 0 - world_size = 1 - return rank, world_size - - -def master_only(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - rank, _ = get_dist_info() - if rank == 0: - return func(*args, **kwargs) - - return wrapper - - -def allreduce_params(params, coalesce=True, bucket_size_mb=-1): - """Allreduce parameters. - - Args: - params (list[torch.Parameters]): List of parameters or buffers of a - model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - _, world_size = get_dist_info() - if world_size == 1: - return - params = [param.data for param in params] - if coalesce: - _allreduce_coalesced(params, world_size, bucket_size_mb) - else: - for tensor in params: - dist.all_reduce(tensor.div_(world_size)) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - _, world_size = get_dist_info() - if world_size == 1: - return - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/epoch_based_runner.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/epoch_based_runner.py deleted file mode 100644 index 078e91df3..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/epoch_based_runner.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .utils import get_host_info - - -@RUNNERS.register_module() -class EpochBasedRunner(BaseRunner): - """Epoch-based Runner. - - This runner train models epoch by epoch. - """ - - def run_iter(self, data_batch, train_mode, **kwargs): - if self.batch_processor is not None: - outputs = self.batch_processor( - self.model, data_batch, train_mode=train_mode, **kwargs) - elif train_mode: - outputs = self.model.train_step(data_batch, self.optimizer, - **kwargs) - else: - outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('"batch_processor()" or "model.train_step()"' - 'and "model.val_step()" must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - self._iter += 1 - - self.call_hook('after_train_epoch') - self._epoch += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - self.call_hook('before_val_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_val_iter') - self.run_iter(data_batch, train_mode=False) - self.call_hook('after_val_iter') - - self.call_hook('after_val_epoch') - - def run(self, data_loaders, workflow, max_epochs=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, epochs) to specify the - running order and epochs. E.g, [('train', 2), ('val', 1)] means - running 2 epochs for training and 1 epoch for validation, - iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_epochs is not None: - warnings.warn( - 'setting max_epochs in run is deprecated, ' - 'please set max_epochs in runner_config', DeprecationWarning) - self._max_epochs = max_epochs - - assert self._max_epochs is not None, ( - 'max_epochs must be specified during instantiation') - - for i, flow in enumerate(workflow): - mode, epochs = flow - if mode == 'train': - self._max_iters = self._max_epochs * len(data_loaders[i]) - break - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d epochs', workflow, - self._max_epochs) - self.call_hook('before_run') - - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - mode, epochs = flow - if isinstance(mode, str): # self.train() - if not hasattr(self, mode): - raise ValueError( - f'runner has no method named "{mode}" to run an ' - 'epoch') - epoch_runner = getattr(self, mode) - else: - raise TypeError( - 'mode in workflow must be a str, but got {}'.format( - type(mode))) - - for _ in range(epochs): - if mode == 'train' and self.epoch >= self._max_epochs: - break - epoch_runner(data_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_run') - - def save_checkpoint(self, - out_dir, - filename_tmpl='epoch_{}.pth', - save_optimizer=True, - meta=None, - create_symlink=True): - """Save the checkpoint. - - Args: - out_dir (str): The directory that checkpoints are saved. - filename_tmpl (str, optional): The checkpoint filename template, - which contains a placeholder for the epoch number. - Defaults to 'epoch_{}.pth'. - save_optimizer (bool, optional): Whether to save the optimizer to - the checkpoint. Defaults to True. - meta (dict, optional): The meta information to be saved in the - checkpoint. Defaults to None. - create_symlink (bool, optional): Whether to create a symlink - "latest.pth" to point to the latest checkpoint. - Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.epoch + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - -@RUNNERS.register_module() -class Runner(EpochBasedRunner): - """Deprecated name of EpochBasedRunner.""" - - def __init__(self, *args, **kwargs): - warnings.warn( - 'Runner was deprecated, please use EpochBasedRunner instead', - DeprecationWarning) - super().__init__(*args, **kwargs) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/fp16_utils.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/fp16_utils.py deleted file mode 100644 index be3ac3a31..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/fp16_utils.py +++ /dev/null @@ -1,423 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import warnings -from collections import abc -from inspect import getfullargspec - -import numpy as np -import torch -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .dist_utils import allreduce_grads as _allreduce_grads - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 - # manually, so the behavior may not be consistent with real amp. - from torch.cuda.amp import autocast -except ImportError: - pass - - -def cast_tensor_type(inputs, src_type, dst_type): - """Recursively convert Tensor in inputs from src_type to dst_type. - - Note: - In v1.4.4 and later, ``cast_tersor_type`` will only convert the - torch.Tensor which is consistent with ``src_type`` to the ``dst_type``. - Before v1.4.4, it ignores the ``src_type`` argument, leading to some - potential problems. For example, - ``cast_tensor_type(inputs, torch.float, torch.half)`` will convert all - tensors in inputs to ``torch.half`` including those originally in - ``torch.Int`` or other types, which is not expected. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype): Source type.. - dst_type (torch.dtype): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - if isinstance(inputs, nn.Module): - return inputs - elif isinstance(inputs, torch.Tensor): - # we need to ensure that the type of inputs to be casted are the same - # as the argument `src_type`. - return inputs.to(dst_type) if inputs.dtype == src_type else inputs - elif isinstance(inputs, str): - return inputs - elif isinstance(inputs, np.ndarray): - return inputs - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type, dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type, dst_type) for item in inputs) - else: - return inputs - - -def auto_fp16(apply_to=None, out_fp32=False, supported_types=(nn.Module, )): - """Decorator to enable fp16 training automatically. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If inputs arguments are fp32 tensors, they will - be converted to fp16 automatically. Arguments other than fp32 tensors are - ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp32 (bool): Whether to convert the output back to fp32. - supported_types (tuple): Classes can be decorated by ``auto_fp16``. - `New in version 1.5.0.` - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp16 - >>> @auto_fp16() - >>> def forward(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp16 - >>> @auto_fp16(apply_to=('pred', )) - >>> def do_something(self, pred, others): - >>> pass - """ - - def auto_fp16_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], supported_types): - raise TypeError('@auto_fp16 can only be used to decorate the ' - f'method of those classes {supported_types}') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - # NOTE: default args are not taken into consideration - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.float, torch.half)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = {} - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.float, torch.half) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=True): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp32: - output = cast_tensor_type(output, torch.half, torch.float) - return output - - return new_func - - return auto_fp16_wrapper - - -def force_fp32(apply_to=None, out_fp16=False): - """Decorator to convert input arguments to fp32 in force. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If there are some inputs that must be processed - in fp32 mode, then this decorator can handle it. If inputs arguments are - fp16 tensors, they will be converted to fp32 automatically. Arguments other - than fp16 tensors are ignored. If you are using PyTorch >= 1.6, - torch.cuda.amp is used as the backend, otherwise, original mmcv - implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp16 (bool): Whether to convert the output back to fp16. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp32 - >>> @force_fp32() - >>> def loss(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp32 - >>> @force_fp32(apply_to=('pred', )) - >>> def post_process(self, pred, others): - >>> pass - """ - - def force_fp32_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@force_fp32 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.half, torch.float)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = dict() - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.half, torch.float) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=False): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp16: - output = cast_tensor_type(output, torch.float, torch.half) - return output - - return new_func - - return force_fp32_wrapper - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - warnings.warning( - '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' - 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads', - DeprecationWarning) - _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) - - -def wrap_fp16_model(model): - """Wrap the FP32 model to FP16. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - For PyTorch >= 1.6, this function will - 1. Set fp16 flag inside the model to True. - - Otherwise: - 1. Convert FP32 model to FP16. - 2. Remain some necessary layers to be FP32, e.g., normalization layers. - 3. Set `fp16_enabled` flag inside the model to True. - - Args: - model (nn.Module): Model in FP32. - """ - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.6.0')): - # convert model to fp16 - model.half() - # patch the normalization layers to make it work in fp32 mode - patch_norm_fp32(model) - # set `fp16_enabled` flag - for m in model.modules(): - if hasattr(m, 'fp16_enabled'): - m.fp16_enabled = True - - -def patch_norm_fp32(module): - """Recursively convert normalization layers from FP16 to FP32. - - Args: - module (nn.Module): The modules to be converted in FP16. - - Returns: - nn.Module: The converted module, the normalization layers have been - converted to FP32. - """ - if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): - module.float() - if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': - module.forward = patch_forward_method(module.forward, torch.half, - torch.float) - for child in module.children(): - patch_norm_fp32(child) - return module - - -def patch_forward_method(func, src_type, dst_type, convert_output=True): - """Patch the forward method of a module. - - Args: - func (callable): The original forward method. - src_type (torch.dtype): Type of input arguments to be converted from. - dst_type (torch.dtype): Type of input arguments to be converted to. - convert_output (bool): Whether to convert the output back to src_type. - - Returns: - callable: The patched forward method. - """ - - def new_forward(*args, **kwargs): - output = func(*cast_tensor_type(args, src_type, dst_type), - **cast_tensor_type(kwargs, src_type, dst_type)) - if convert_output: - output = cast_tensor_type(output, dst_type, src_type) - return output - - return new_forward - - -class LossScaler: - """Class that manages loss scaling in mixed precision training which - supports both dynamic or static mode. - - The implementation refers to - https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. - Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. - It's important to understand how :class:`LossScaler` operates. - Loss scaling is designed to combat the problem of underflowing - gradients encountered at long times when training fp16 networks. - Dynamic loss scaling begins by attempting a very high loss - scale. Ironically, this may result in OVERflowing gradients. - If overflowing gradients are encountered, :class:`FP16_Optimizer` then - skips the update step for this particular iteration/minibatch, - and :class:`LossScaler` adjusts the loss scale to a lower value. - If a certain number of iterations occur without overflowing gradients - detected,:class:`LossScaler` increases the loss scale once more. - In this way :class:`LossScaler` attempts to "ride the edge" of always - using the highest loss scale possible without incurring overflow. - - Args: - init_scale (float): Initial loss scale value, default: 2**32. - scale_factor (float): Factor used when adjusting the loss scale. - Default: 2. - mode (str): Loss scaling mode. 'dynamic' or 'static' - scale_window (int): Number of consecutive iterations without an - overflow to wait before increasing the loss scale. Default: 1000. - """ - - def __init__(self, - init_scale=2**32, - mode='dynamic', - scale_factor=2., - scale_window=1000): - self.cur_scale = init_scale - self.cur_iter = 0 - assert mode in ('dynamic', - 'static'), 'mode can only be dynamic or static' - self.mode = mode - self.last_overflow_iter = -1 - self.scale_factor = scale_factor - self.scale_window = scale_window - - def has_overflow(self, params): - """Check if params contain overflow.""" - if self.mode != 'dynamic': - return False - for p in params: - if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): - return True - return False - - def _has_inf_or_nan(x): - """Check if params contain NaN.""" - try: - cpu_sum = float(x.float().sum()) - except RuntimeError as instance: - if 'value cannot be converted' not in instance.args[0]: - raise - return True - else: - if cpu_sum == float('inf') or cpu_sum == -float('inf') \ - or cpu_sum != cpu_sum: - return True - return False - - def update_scale(self, overflow): - """update the current loss scale value when overflow happens.""" - if self.mode != 'dynamic': - return - if overflow: - self.cur_scale = max(self.cur_scale / self.scale_factor, 1) - self.last_overflow_iter = self.cur_iter - else: - if (self.cur_iter - self.last_overflow_iter) % \ - self.scale_window == 0: - self.cur_scale *= self.scale_factor - self.cur_iter += 1 - - def state_dict(self): - """Returns the state of the scaler as a :class:`dict`.""" - return dict( - cur_scale=self.cur_scale, - cur_iter=self.cur_iter, - mode=self.mode, - last_overflow_iter=self.last_overflow_iter, - scale_factor=self.scale_factor, - scale_window=self.scale_window) - - def load_state_dict(self, state_dict): - """Loads the loss_scaler state dict. - - Args: - state_dict (dict): scaler state. - """ - self.cur_scale = state_dict['cur_scale'] - self.cur_iter = state_dict['cur_iter'] - self.mode = state_dict['mode'] - self.last_overflow_iter = state_dict['last_overflow_iter'] - self.scale_factor = state_dict['scale_factor'] - self.scale_window = state_dict['scale_window'] - - @property - def loss_scale(self): - return self.cur_scale diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/__init__.py deleted file mode 100644 index 03e2a619e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkpoint import CheckpointHook -from .closure import ClosureHook -from .ema import EMAHook -from .evaluation import DistEvalHook, EvalHook -from .hook import HOOKS, Hook -from .iter_timer import IterTimerHook -from .logger import (ClearMLLoggerHook, DvcliveLoggerHook, LoggerHook, - MlflowLoggerHook, NeptuneLoggerHook, PaviLoggerHook, - SegmindLoggerHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, InvLrUpdaterHook, - LinearAnnealingLrUpdaterHook, LrUpdaterHook, - OneCycleLrUpdaterHook, PolyLrUpdaterHook, - StepLrUpdaterHook) -from .memory import EmptyCacheHook -from .momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, OptimizerHook) -from .profiler import ProfilerHook -from .sampler_seed import DistSamplerSeedHook -from .sync_buffer import SyncBuffersHook - -__all__ = [ - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'OptimizerHook', - 'Fp16OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', - 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TextLoggerHook', 'TensorboardLoggerHook', 'NeptuneLoggerHook', - 'WandbLoggerHook', 'DvcliveLoggerHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'SyncBuffersHook', 'EMAHook', 'EvalHook', 'DistEvalHook', 'ProfilerHook', - 'GradientCumulativeOptimizerHook', 'GradientCumulativeFp16OptimizerHook', - 'SegmindLoggerHook', 'LinearAnnealingLrUpdaterHook', - 'LinearAnnealingMomentumUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/checkpoint.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/checkpoint.py deleted file mode 100644 index 7bb75f402..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/checkpoint.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -from mmcv.fileio import FileClient -from ..dist_utils import allreduce_params, master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class CheckpointHook(Hook): - """Save checkpoints periodically. - - Args: - interval (int): The saving period. If ``by_epoch=True``, interval - indicates epochs, otherwise it indicates iterations. - Default: -1, which means "never". - by_epoch (bool): Saving checkpoints by epoch or by iteration. - Default: True. - save_optimizer (bool): Whether to save optimizer state_dict in the - checkpoint. It is usually used for resuming experiments. - Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, ``runner.work_dir`` will be used by default. If - specified, the ``out_dir`` will be the concatenation of ``out_dir`` - and the last level directory of ``runner.work_dir``. - `Changed in version 1.3.16.` - max_keep_ckpts (int, optional): The maximum checkpoints to keep. - In some cases we want only the latest few checkpoints and would - like to delete old ones to save the disk space. - Default: -1, which means unlimited. - save_last (bool, optional): Whether to force the last checkpoint to be - saved regardless of interval. Default: True. - sync_buffer (bool, optional): Whether to synchronize buffers in - different gpus. Default: False. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - - .. warning:: - Before v1.3.16, the ``out_dir`` argument indicates the path where the - checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the - root directory and the final path to save checkpoint is the - concatenation of ``out_dir`` and the last level directory of - ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" - and the value of ``runner.work_dir`` is "/path/of/B", then the final - path will be "/path/of/A/B". - """ - - def __init__(self, - interval=-1, - by_epoch=True, - save_optimizer=True, - out_dir=None, - max_keep_ckpts=-1, - save_last=True, - sync_buffer=False, - file_client_args=None, - **kwargs): - self.interval = interval - self.by_epoch = by_epoch - self.save_optimizer = save_optimizer - self.out_dir = out_dir - self.max_keep_ckpts = max_keep_ckpts - self.save_last = save_last - self.args = kwargs - self.sync_buffer = sync_buffer - self.file_client_args = file_client_args - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - - runner.logger.info((f'Checkpoints will be saved to {self.out_dir} by ' - f'{self.file_client.name}.')) - - # disable the create_symlink option because some file backends do not - # allow to create a symlink - if 'create_symlink' in self.args: - if self.args[ - 'create_symlink'] and not self.file_client.allow_symlink: - self.args['create_symlink'] = False - warnings.warn( - ('create_symlink is set as True by the user but is changed' - 'to be False because creating symbolic link is not ' - f'allowed in {self.file_client.name}')) - else: - self.args['create_symlink'] = self.file_client.allow_symlink - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` epochs - # 2. reach the last epoch of training - if self.every_n_epochs( - runner, self.interval) or (self.save_last - and self.is_last_epoch(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.epoch + 1} epochs') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) - - @master_only - def _save_checkpoint(self, runner): - """Save the current checkpoint and delete unwanted checkpoint.""" - runner.save_checkpoint( - self.out_dir, save_optimizer=self.save_optimizer, **self.args) - if runner.meta is not None: - if self.by_epoch: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) - else: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) - runner.meta.setdefault('hook_msgs', dict()) - runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( - self.out_dir, cur_ckpt_filename) - # remove other checkpoints - if self.max_keep_ckpts > 0: - if self.by_epoch: - name = 'epoch_{}.pth' - current_ckpt = runner.epoch + 1 - else: - name = 'iter_{}.pth' - current_ckpt = runner.iter + 1 - redundant_ckpts = range( - current_ckpt - self.max_keep_ckpts * self.interval, 0, - -self.interval) - filename_tmpl = self.args.get('filename_tmpl', name) - for _step in redundant_ckpts: - ckpt_path = self.file_client.join_path( - self.out_dir, filename_tmpl.format(_step)) - if self.file_client.isfile(ckpt_path): - self.file_client.remove(ckpt_path) - else: - break - - def after_train_iter(self, runner): - if self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` iterations - # 2. reach the last iteration of training - if self.every_n_iters( - runner, self.interval) or (self.save_last - and self.is_last_iter(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.iter + 1} iterations') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/closure.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/closure.py deleted file mode 100644 index b955f81f4..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/closure.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ClosureHook(Hook): - - def __init__(self, fn_name, fn): - assert hasattr(self, fn_name) - assert callable(fn) - setattr(self, fn_name, fn) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/ema.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/ema.py deleted file mode 100644 index 6ed77b84e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/ema.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...parallel import is_module_wrapper -from ..hooks.hook import HOOKS, Hook - - -@HOOKS.register_module() -class EMAHook(Hook): - r"""Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. - - .. math:: - - Xema\_{t+1} = (1 - \text{momentum}) \times - Xema\_{t} + \text{momentum} \times X_t - - Args: - momentum (float): The momentum used for updating ema parameter. - Defaults to 0.0002. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - warm_up (int): During first warm_up steps, we may use smaller momentum - to update ema parameters more slowly. Defaults to 100. - resume_from (str): The checkpoint path. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - warm_up=100, - resume_from=None): - assert isinstance(interval, int) and interval > 0 - self.warm_up = warm_up - self.interval = interval - assert momentum > 0 and momentum < 1 - self.momentum = momentum**interval - self.checkpoint = resume_from - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - self.model_parameters = dict(model.named_parameters(recurse=True)) - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers(recurse=True)) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - curr_step = runner.iter - # We warm up the momentum considering the instability at beginning - momentum = min(self.momentum, - (1 + curr_step) / (self.warm_up + curr_step)) - if curr_step % self.interval != 0: - return - for name, parameter in self.model_parameters.items(): - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/evaluation.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/evaluation.py deleted file mode 100644 index 2a57d535e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/evaluation.py +++ /dev/null @@ -1,511 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from math import inf - -import torch.distributed as dist -from torch.nn.modules.batchnorm import _BatchNorm -from torch.utils.data import DataLoader - -from mmcv.fileio import FileClient -from mmcv.utils import is_seq_of -from .hook import Hook -from .logger import LoggerHook - - -class EvalHook(Hook): - """Non-Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader, and return the test results. If ``None``, the default - test function ``mmcv.engine.single_gpu_test`` will be used. - (default: ``None``) - greater_keys (List[str] | None, optional): Metric keys that will be - inferred by 'greater' comparison rule. If ``None``, - _default_greater_keys will be used. (default: ``None``) - less_keys (List[str] | None, optional): Metric keys that will be - inferred by 'less' comparison rule. If ``None``, _default_less_keys - will be used. (default: ``None``) - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - `New in version 1.3.16.` - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - `New in version 1.3.16.` - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - - Note: - If new arguments are added for EvalHook, tools/test.py, - tools/eval_metric.py may be affected. - """ - - # Since the key for determine greater or less is related to the downstream - # tasks, downstream repos may need to overwrite the following inner - # variable accordingly. - - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - _default_greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - _default_less_keys = ['loss'] - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - out_dir=None, - file_client_args=None, - **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError(f'dataloader must be a pytorch DataLoader, ' - f'but got {type(dataloader)}') - - if interval <= 0: - raise ValueError(f'interval must be a positive number, ' - f'but got {interval}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' - - if start is not None and start < 0: - raise ValueError(f'The evaluation start epoch {start} is smaller ' - f'than 0') - - self.dataloader = dataloader - self.interval = interval - self.start = start - self.by_epoch = by_epoch - - assert isinstance(save_best, str) or save_best is None, \ - '""save_best"" should be a str or None ' \ - f'rather than {type(save_best)}' - self.save_best = save_best - self.eval_kwargs = eval_kwargs - self.initial_flag = True - - if test_fn is None: - from mmcv.engine import single_gpu_test - self.test_fn = single_gpu_test - else: - self.test_fn = test_fn - - if greater_keys is None: - self.greater_keys = self._default_greater_keys - else: - if not isinstance(greater_keys, (list, tuple)): - greater_keys = (greater_keys, ) - assert is_seq_of(greater_keys, str) - self.greater_keys = greater_keys - - if less_keys is None: - self.less_keys = self._default_less_keys - else: - if not isinstance(less_keys, (list, tuple)): - less_keys = (less_keys, ) - assert is_seq_of(less_keys, str) - self.less_keys = less_keys - - if self.save_best is not None: - self.best_ckpt_path = None - self._init_rule(rule, self.save_best) - - self.out_dir = out_dir - self.file_client_args = file_client_args - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific (note that the key indicator matching - is case-insensitive): - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator != 'auto': - # `_lc` here means we use the lower case of keys for - # case-insensitive matching - key_indicator_lc = key_indicator.lower() - greater_keys = [key.lower() for key in self.greater_keys] - less_keys = [key.lower() for key in self.less_keys] - - if key_indicator_lc in greater_keys: - rule = 'greater' - elif key_indicator_lc in less_keys: - rule = 'less' - elif any(key in key_indicator_lc for key in greater_keys): - rule = 'greater' - elif any(key in key_indicator_lc for key in less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - if self.rule is not None: - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'The best checkpoint will be saved to {self.out_dir} by ' - f'{self.file_client.name}')) - - if self.save_best is not None: - if runner.meta is None: - warnings.warn('runner.meta is None. Creating an empty one.') - runner.meta = dict() - runner.meta.setdefault('hook_msgs', dict()) - self.best_ckpt_path = runner.meta['hook_msgs'].get( - 'best_ckpt', None) - - def before_train_iter(self, runner): - """Evaluate the model only at the start of training by iteration.""" - if self.by_epoch or not self.initial_flag: - return - if self.start is not None and runner.iter >= self.start: - self.after_train_iter(runner) - self.initial_flag = False - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - if not (self.by_epoch and self.initial_flag): - return - if self.start is not None and runner.epoch >= self.start: - self.after_train_epoch(runner) - self.initial_flag = False - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch and self._should_evaluate(runner): - # Because the priority of EvalHook is higher than LoggerHook, the - # training log and the evaluating log are mixed. Therefore, - # we need to dump the training log and clear it before evaluating - # log is generated. In addition, this problem will only appear in - # `IterBasedRunner` whose `self.by_epoch` is False, because - # `EpochBasedRunner` whose `self.by_epoch` is True calls - # `_do_evaluate` in `after_train_epoch` stage, and at this stage - # the training log has been printed, so it will not cause any - # problem. more details at - # https://github.com/open-mmlab/mmsegmentation/issues/694 - for hook in runner._hooks: - if isinstance(hook, LoggerHook): - hook.after_train_iter(runner) - runner.log_buffer.clear() - - self._do_evaluate(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch and self._should_evaluate(runner): - self._do_evaluate(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - results = self.test_fn(runner.model, self.dataloader) - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - def _should_evaluate(self, runner): - """Judge whether to perform evaluation. - - Here is the rule to judge whether to perform evaluation: - 1. It will not perform evaluation during the epoch/iteration interval, - which is determined by ``self.interval``. - 2. It will not perform evaluation if the start time is larger than - current time. - 3. It will not perform evaluation when current time is larger than - the start time but during epoch/iteration interval. - - Returns: - bool: The flag indicating whether to perform evaluation. - """ - if self.by_epoch: - current = runner.epoch - check_time = self.every_n_epochs - else: - current = runner.iter - check_time = self.every_n_iters - - if self.start is None: - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - elif (current + 1) < self.start: - # No evaluation if start is larger than the current time. - return False - else: - # Evaluation only at epochs/iters 3, 5, 7... - # if start==3 and interval==2 - if (current + 1 - self.start) % self.interval: - return False - return True - - def _save_ckpt(self, runner, key_score): - """Save the best checkpoint. - - It will compare the score according to the compare function, write - related information (best score, best checkpoint path) and save the - best checkpoint into ``work_dir``. - """ - if self.by_epoch: - current = f'epoch_{runner.epoch + 1}' - cur_type, cur_time = 'epoch', runner.epoch + 1 - else: - current = f'iter_{runner.iter + 1}' - cur_type, cur_time = 'iter', runner.iter + 1 - - best_score = runner.meta['hook_msgs'].get( - 'best_score', self.init_value_map[self.rule]) - if self.compare_func(key_score, best_score): - best_score = key_score - runner.meta['hook_msgs']['best_score'] = best_score - - if self.best_ckpt_path and self.file_client.isfile( - self.best_ckpt_path): - self.file_client.remove(self.best_ckpt_path) - runner.logger.info( - (f'The previous best checkpoint {self.best_ckpt_path} was ' - 'removed')) - - best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' - self.best_ckpt_path = self.file_client.join_path( - self.out_dir, best_ckpt_name) - runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path - - runner.save_checkpoint( - self.out_dir, - filename_tmpl=best_ckpt_name, - create_symlink=False) - runner.logger.info( - f'Now best checkpoint is saved as {best_ckpt_name}.') - runner.logger.info( - f'Best {self.key_indicator} is {best_score:0.4f} ' - f'at {cur_time} {cur_type}.') - - def evaluate(self, runner, results): - """Evaluate the results. - - Args: - runner (:obj:`mmcv.Runner`): The underlined training runner. - results (list): Output results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - - if self.save_best is not None: - # If the performance of model is pool, the `eval_res` may be an - # empty dict and it will raise exception when `self.save_best` is - # not None. More details at - # https://github.com/open-mmlab/mmdetection/issues/6265. - if not eval_res: - warnings.warn( - 'Since `eval_res` is an empty dict, the behavior to save ' - 'the best checkpoint will be skipped in this evaluation.') - return None - - if self.key_indicator == 'auto': - # infer from eval_results - self._init_rule(self.rule, list(eval_res.keys())[0]) - return eval_res[self.key_indicator] - - return None - - -class DistEvalHook(EvalHook): - """Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader in a multi-gpu manner, and return the test results. If - ``None``, the default test function ``mmcv.engine.multi_gpu_test`` - will be used. (default: ``None``) - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - broadcast_bn_buffer (bool): Whether to broadcast the - buffer(running_mean and running_var) of rank 0 to other rank - before evaluation. Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - """ - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - broadcast_bn_buffer=True, - tmpdir=None, - gpu_collect=False, - out_dir=None, - file_client_args=None, - **eval_kwargs): - - if test_fn is None: - from mmcv.engine import multi_gpu_test - test_fn = multi_gpu_test - - super().__init__( - dataloader, - start=start, - interval=interval, - by_epoch=by_epoch, - save_best=save_best, - rule=rule, - test_fn=test_fn, - greater_keys=greater_keys, - less_keys=less_keys, - out_dir=out_dir, - file_client_args=file_client_args, - **eval_kwargs) - - self.broadcast_bn_buffer = broadcast_bn_buffer - self.tmpdir = tmpdir - self.gpu_collect = gpu_collect - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - results = self.test_fn( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to - # save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/hook.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/hook.py deleted file mode 100644 index f2d1c9865..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/hook.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, is_method_overridden - -HOOKS = Registry('hook') - - -class Hook: - stages = ('before_run', 'before_train_epoch', 'before_train_iter', - 'after_train_iter', 'after_train_epoch', 'before_val_epoch', - 'before_val_iter', 'after_val_iter', 'after_val_epoch', - 'after_run') - - def before_run(self, runner): - pass - - def after_run(self, runner): - pass - - def before_epoch(self, runner): - pass - - def after_epoch(self, runner): - pass - - def before_iter(self, runner): - pass - - def after_iter(self, runner): - pass - - def before_train_epoch(self, runner): - self.before_epoch(runner) - - def before_val_epoch(self, runner): - self.before_epoch(runner) - - def after_train_epoch(self, runner): - self.after_epoch(runner) - - def after_val_epoch(self, runner): - self.after_epoch(runner) - - def before_train_iter(self, runner): - self.before_iter(runner) - - def before_val_iter(self, runner): - self.before_iter(runner) - - def after_train_iter(self, runner): - self.after_iter(runner) - - def after_val_iter(self, runner): - self.after_iter(runner) - - def every_n_epochs(self, runner, n): - return (runner.epoch + 1) % n == 0 if n > 0 else False - - def every_n_inner_iters(self, runner, n): - return (runner.inner_iter + 1) % n == 0 if n > 0 else False - - def every_n_iters(self, runner, n): - return (runner.iter + 1) % n == 0 if n > 0 else False - - def end_of_epoch(self, runner): - return runner.inner_iter + 1 == len(runner.data_loader) - - def is_last_epoch(self, runner): - return runner.epoch + 1 == runner._max_epochs - - def is_last_iter(self, runner): - return runner.iter + 1 == runner._max_iters - - def get_triggered_stages(self): - trigger_stages = set() - for stage in Hook.stages: - if is_method_overridden(stage, Hook, self): - trigger_stages.add(stage) - - # some methods will be triggered in multi stages - # use this dict to map method to stages. - method_stages_map = { - 'before_epoch': ['before_train_epoch', 'before_val_epoch'], - 'after_epoch': ['after_train_epoch', 'after_val_epoch'], - 'before_iter': ['before_train_iter', 'before_val_iter'], - 'after_iter': ['after_train_iter', 'after_val_iter'], - } - - for method, map_stages in method_stages_map.items(): - if is_method_overridden(method, Hook, self): - trigger_stages.update(map_stages) - - return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/iter_timer.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/iter_timer.py deleted file mode 100644 index 16531be96..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/iter_timer.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import time -import torch.distributed as dist - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class IterTimerHook(Hook): - - def before_epoch(self, runner): - self.t = time.time() - - def before_iter(self, runner): - self.batch_size = runner.cfg.data.samples_per_gpu - runner.log_buffer.update({'data_time': time.time() - self.t}) - - def after_iter(self, runner): - iter_info = {'time': time.time() - self.t} - fps = self.batch_size / iter_info["time"] - - if dist.is_initialized(): - fps = fps * dist.get_world_size() - - iter_info["fps"] = fps - - runner.log_buffer.update(iter_info) - self.t = time.time() diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/__init__.py deleted file mode 100644 index 062709e70..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import LoggerHook -from .clearml import ClearMLLoggerHook -from .dvclive import DvcliveLoggerHook -from .mlflow import MlflowLoggerHook -from .neptune import NeptuneLoggerHook -from .pavi import PaviLoggerHook -from .segmind import SegmindLoggerHook -from .tensorboard import TensorboardLoggerHook -from .text import TextLoggerHook -from .wandb import WandbLoggerHook - -__all__ = [ - 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', - 'NeptuneLoggerHook', 'DvcliveLoggerHook', 'SegmindLoggerHook', - 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/base.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/base.py deleted file mode 100644 index 9f1a51b31..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/base.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from abc import ABCMeta, abstractmethod - -import numpy as np -import torch - -from ..hook import Hook - - -class LoggerHook(Hook): - """Base class for logger hooks. - - Args: - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - """ - - __metaclass__ = ABCMeta - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - self.interval = interval - self.ignore_last = ignore_last - self.reset_flag = reset_flag - self.by_epoch = by_epoch - - @abstractmethod - def log(self, runner): - pass - - @staticmethod - def is_scalar(val, include_np=True, include_torch=True): - """Tell the input variable is a scalar or not. - - Args: - val: Input variable. - include_np (bool): Whether include 0-d np.ndarray as a scalar. - include_torch (bool): Whether include 0-d torch.Tensor as a scalar. - - Returns: - bool: True or False. - """ - if isinstance(val, numbers.Number): - return True - elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: - return True - elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: - return True - else: - return False - - def get_mode(self, runner): - if runner.mode == 'train': - if 'time' in runner.log_buffer.output: - mode = 'train' - else: - mode = 'val' - elif runner.mode == 'val': - mode = 'val' - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return mode - - def get_epoch(self, runner): - if runner.mode == 'train': - epoch = runner.epoch + 1 - elif runner.mode == 'val': - # normal val mode - # runner.epoch += 1 has been done before val workflow - epoch = runner.epoch - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return epoch - - def get_iter(self, runner, inner_iter=False): - """Get the current training iteration step.""" - if self.by_epoch and inner_iter: - current_iter = runner.inner_iter + 1 - else: - current_iter = runner.iter + 1 - return current_iter - - def get_lr_tags(self, runner): - tags = {} - lrs = runner.current_lr() - if isinstance(lrs, dict): - for name, value in lrs.items(): - tags[f'learning_rate/{name}'] = value[0] - else: - tags['learning_rate'] = lrs[0] - return tags - - def get_momentum_tags(self, runner): - tags = {} - momentums = runner.current_momentum() - if isinstance(momentums, dict): - for name, value in momentums.items(): - tags[f'momentum/{name}'] = value[0] - else: - tags['momentum'] = momentums[0] - return tags - - def get_loggable_tags(self, - runner, - allow_scalar=True, - allow_text=False, - add_mode=True, - tags_to_skip=('time', 'data_time')): - tags = {} - for var, val in runner.log_buffer.output.items(): - if var in tags_to_skip: - continue - if self.is_scalar(val) and not allow_scalar: - continue - if isinstance(val, str) and not allow_text: - continue - if add_mode: - var = f'{self.get_mode(runner)}/{var}' - tags[var] = val - tags.update(self.get_lr_tags(runner)) - tags.update(self.get_momentum_tags(runner)) - return tags - - def before_run(self, runner): - for hook in runner.hooks[::-1]: - if isinstance(hook, LoggerHook): - hook.reset_flag = True - break - - def before_epoch(self, runner): - runner.log_buffer.clear() # clear logs of last epoch - - def after_train_iter(self, runner): - if self.by_epoch and self.every_n_inner_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif not self.by_epoch and self.every_n_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif self.end_of_epoch(runner) and not self.ignore_last: - # not precise but more stable - runner.log_buffer.average(self.interval) - - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_train_epoch(self, runner): - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_val_epoch(self, runner): - runner.log_buffer.average() - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/clearml.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/clearml.py deleted file mode 100644 index 1de71e152..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/clearml.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class ClearMLLoggerHook(LoggerHook): - """Class to log metrics with clearml. - - It requires `clearml`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the `clearml.Task.init` - initialization keys. See `taskinit`_ for more details. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _clearml: - https://clear.ml/docs/latest/docs/ - .. _taskinit: - https://clear.ml/docs/latest/docs/references/sdk/task/#taskinit - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(ClearMLLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_clearml() - self.init_kwargs = init_kwargs - - def import_clearml(self): - try: - import clearml - except ImportError: - raise ImportError( - 'Please run "pip install clearml" to install clearml') - self.clearml = clearml - - @master_only - def before_run(self, runner): - super(ClearMLLoggerHook, self).before_run(runner) - task_kwargs = self.init_kwargs if self.init_kwargs else {} - self.task = self.clearml.Task.init(**task_kwargs) - self.task_logger = self.task.get_logger() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - for tag, val in tags.items(): - self.task_logger.report_scalar(tag, tag, val, - self.get_iter(runner)) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/dvclive.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/dvclive.py deleted file mode 100644 index c79eefa75..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/dvclive.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from pathlib import Path - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class DvcliveLoggerHook(LoggerHook): - """Class to log metrics with dvclive. - - It requires `dvclive`_ to be installed. - - Args: - model_file (str): Default None. If not None, after each epoch the - model will be saved to {model_file}. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - kwargs: Arguments for instantiating `Live`_. - - .. _dvclive: - https://dvc.org/doc/dvclive - - .. _Live: - https://dvc.org/doc/dvclive/api-reference/live#parameters - """ - - def __init__(self, - model_file=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - **kwargs): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.model_file = model_file - self.import_dvclive(**kwargs) - - def import_dvclive(self, **kwargs): - try: - from dvclive import Live - except ImportError: - raise ImportError( - 'Please run "pip install dvclive" to install dvclive') - self.dvclive = Live(**kwargs) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.dvclive.set_step(self.get_iter(runner)) - for k, v in tags.items(): - self.dvclive.log(k, v) - - @master_only - def after_train_epoch(self, runner): - super().after_train_epoch(runner) - if self.model_file is not None: - runner.save_checkpoint( - Path(self.model_file).parent, - filename_tmpl=Path(self.model_file).name, - create_symlink=False, - ) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/mlflow.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/mlflow.py deleted file mode 100644 index dcd87bcb5..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/mlflow.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import TORCH_VERSION -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class MlflowLoggerHook(LoggerHook): - """Class to log metrics and (optionally) a trained model to MLflow. - - It requires `MLflow`_ to be installed. - - Args: - exp_name (str, optional): Name of the experiment to be used. - Default None. If not None, set the active experiment. - If experiment does not exist, an experiment with provided name - will be created. - tags (Dict[str], optional): Tags for the current run. - Default None. If not None, set tags for the current run. - log_model (bool, optional): Whether to log an MLflow artifact. - Default True. If True, log runner.model as an MLflow artifact - for the current run. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _MLflow: - https://www.mlflow.org/docs/latest/index.html - """ - - def __init__(self, - exp_name=None, - tags=None, - log_model=True, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(MlflowLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_mlflow() - self.exp_name = exp_name - self.tags = tags - self.log_model = log_model - - def import_mlflow(self): - try: - import mlflow - import mlflow.pytorch as mlflow_pytorch - except ImportError: - raise ImportError( - 'Please run "pip install mlflow" to install mlflow') - self.mlflow = mlflow - self.mlflow_pytorch = mlflow_pytorch - - @master_only - def before_run(self, runner): - super(MlflowLoggerHook, self).before_run(runner) - if self.exp_name is not None: - self.mlflow.set_experiment(self.exp_name) - if self.tags is not None: - self.mlflow.set_tags(self.tags) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.mlflow.log_metrics(tags, step=self.get_iter(runner)) - - @master_only - def after_run(self, runner): - if self.log_model: - self.mlflow_pytorch.log_model( - runner.model, - 'models', - pip_requirements=[f'torch=={TORCH_VERSION}']) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/neptune.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/neptune.py deleted file mode 100644 index e0aafe91d..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/neptune.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class NeptuneLoggerHook(LoggerHook): - """Class to log metrics to NeptuneAI. - - It requires `Neptune`_ to be installed. - - Args: - init_kwargs (dict): a dict contains the initialization keys as below: - - - project (str): Name of a project in a form of - namespace/project_name. If None, the value of NEPTUNE_PROJECT - environment variable will be taken. - - api_token (str): User’s API token. If None, the value of - NEPTUNE_API_TOKEN environment variable will be taken. Note: It is - strongly recommended to use NEPTUNE_API_TOKEN environment - variable rather than placing your API token in plain text in your - source code. - - name (str, optional, default is 'Untitled'): Editable name of the - run. Name is displayed in the run's Details and in Runs table as - a column. - - Check https://docs.neptune.ai/api-reference/neptune#init for more - init arguments. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than ``interval``. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _Neptune: - https://docs.neptune.ai - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=True, - with_step=True, - by_epoch=True): - - super(NeptuneLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_neptune() - self.init_kwargs = init_kwargs - self.with_step = with_step - - def import_neptune(self): - try: - import neptune.new as neptune - except ImportError: - raise ImportError( - 'Please run "pip install neptune-client" to install neptune') - self.neptune = neptune - self.run = None - - @master_only - def before_run(self, runner): - if self.init_kwargs: - self.run = self.neptune.init(**self.init_kwargs) - else: - self.run = self.neptune.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for tag_name, tag_value in tags.items(): - if self.with_step: - self.run[tag_name].log( - tag_value, step=self.get_iter(runner)) - else: - tags['global_step'] = self.get_iter(runner) - self.run[tag_name].log(tags) - - @master_only - def after_run(self, runner): - self.run.stop() diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/pavi.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/pavi.py deleted file mode 100644 index d5d61f9e5..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/pavi.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp - -import torch -import yaml - -import mmcv -from ....parallel.utils import is_module_wrapper -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class PaviLoggerHook(LoggerHook): - """Class to visual model, log metrics (for internal use). - - Args: - init_kwargs (dict): A dict contains the initialization keys. - add_graph (bool): Whether to visual model. Default: False. - add_last_ckpt (bool): Whether to save checkpoint after run. - Default: False. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - img_key (string): Get image data from Dataset. Default: 'img_info'. - """ - - def __init__(self, - init_kwargs=None, - add_graph=False, - add_last_ckpt=False, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - img_key='img_info'): - super(PaviLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.init_kwargs = init_kwargs - self.add_graph = add_graph - self.add_last_ckpt = add_last_ckpt - self.img_key = img_key - - @master_only - def before_run(self, runner): - super(PaviLoggerHook, self).before_run(runner) - try: - from pavi import SummaryWriter - except ImportError: - raise ImportError('Please run "pip install pavi" to install pavi.') - - self.run_name = runner.work_dir.split('/')[-1] - - if not self.init_kwargs: - self.init_kwargs = dict() - self.init_kwargs['name'] = self.run_name - self.init_kwargs['model'] = runner._model_name - if runner.meta is not None: - if 'config_dict' in runner.meta: - config_dict = runner.meta['config_dict'] - assert isinstance( - config_dict, - dict), ('meta["config_dict"] has to be of a dict, ' - f'but got {type(config_dict)}') - elif 'config_file' in runner.meta: - config_file = runner.meta['config_file'] - config_dict = dict(mmcv.Config.fromfile(config_file)) - else: - config_dict = None - if config_dict is not None: - # 'max_.*iter' is parsed in pavi sdk as the maximum iterations - # to properly set up the progress bar. - config_dict = config_dict.copy() - config_dict.setdefault('max_iter', runner.max_iters) - # non-serializable values are first converted in - # mmcv.dump to json - config_dict = json.loads( - mmcv.dump(config_dict, file_format='json')) - session_text = yaml.dump(config_dict) - self.init_kwargs['session_text'] = session_text - self.writer = SummaryWriter(**self.init_kwargs) - - def get_step(self, runner): - """Get the total training step/epoch.""" - if self.get_mode(runner) == 'val' and self.by_epoch: - return self.get_epoch(runner) - else: - return self.get_iter(runner) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, add_mode=False) - if tags: - self.writer.add_scalars( - self.get_mode(runner), tags, self.get_step(runner)) - - @master_only - def after_run(self, runner): - if self.add_last_ckpt: - ckpt_path = osp.join(runner.work_dir, 'latest.pth') - if osp.islink(ckpt_path): - ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) - - if osp.isfile(ckpt_path): - # runner.epoch += 1 has been done before `after_run`. - iteration = runner.epoch if self.by_epoch else runner.iter - return self.writer.add_snapshot_file( - tag=self.run_name, - snapshot_file_path=ckpt_path, - iteration=iteration) - - # flush the buffer and send a task ending signal to Pavi - self.writer.close() - - @master_only - def before_epoch(self, runner): - if runner.epoch == 0 and self.add_graph: - if is_module_wrapper(runner.model): - _model = runner.model.module - else: - _model = runner.model - device = next(_model.parameters()).device - data = next(iter(runner.data_loader)) - image = data[self.img_key][0:1].to(device) - with torch.no_grad(): - self.writer.add_graph(_model, image) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/segmind.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/segmind.py deleted file mode 100644 index e262c7c1a..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/segmind.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class SegmindLoggerHook(LoggerHook): - """Class to log metrics to Segmind. - - It requires `Segmind`_ to be installed. - - Args: - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - - .. _Segmind: - https://docs.segmind.com/python-library - """ - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(SegmindLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_segmind() - - def import_segmind(self): - try: - import segmind - except ImportError: - raise ImportError( - "Please run 'pip install segmind' to install segmind") - self.log_metrics = segmind.tracking.fluent.log_metrics - self.mlflow_log = segmind.utils.logging_utils.try_mlflow_log - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - # logging metrics to segmind - self.mlflow_log( - self.log_metrics, tags, step=runner.epoch, epoch=runner.epoch) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/tensorboard.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/tensorboard.py deleted file mode 100644 index bf00d5742..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/tensorboard.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import TORCH_VERSION, digit_version -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TensorboardLoggerHook(LoggerHook): - """Class to log metrics to Tensorboard. - - Args: - log_dir (string): Save directory location. Default: None. If default - values are used, directory location is ``runner.work_dir``/tf_logs. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - """ - - def __init__(self, - log_dir=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(TensorboardLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.log_dir = log_dir - - @master_only - def before_run(self, runner): - super(TensorboardLoggerHook, self).before_run(runner) - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.1')): - try: - from tensorboardX import SummaryWriter - except ImportError: - raise ImportError('Please install tensorboardX to use ' - 'TensorboardLoggerHook.') - else: - try: - from torch.utils.tensorboard import SummaryWriter - except ImportError: - raise ImportError( - 'Please run "pip install future tensorboard" to install ' - 'the dependencies to use torch.utils.tensorboard ' - '(applicable to PyTorch 1.1 or higher)') - - if self.log_dir is None: - self.log_dir = osp.join(runner.work_dir, 'tf_logs') - self.writer = SummaryWriter(self.log_dir) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, allow_text=True) - for tag, val in tags.items(): - if isinstance(val, str): - self.writer.add_text(tag, val, self.get_iter(runner)) - else: - self.writer.add_scalar(tag, val, self.get_iter(runner)) - - @master_only - def after_run(self, runner): - self.writer.close() diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/text.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/text.py deleted file mode 100644 index 644ced2c9..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/text.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import datetime -import os -import os.path as osp -from collections import OrderedDict - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.fileio.file_client import FileClient -from mmcv.utils import is_tuple_of, scandir -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TextLoggerHook(LoggerHook): - """Logger hook in text. - - In this logger hook, the information will be printed on terminal and - saved in json file. - - Args: - by_epoch (bool, optional): Whether EpochBasedRunner is used. - Default: True. - interval (int, optional): Logging interval (every k iterations). - Default: 10. - ignore_last (bool, optional): Ignore the log of last iterations in each - epoch if less than :attr:`interval`. Default: True. - reset_flag (bool, optional): Whether to clear the output buffer after - logging. Default: False. - interval_exp_name (int, optional): Logging interval for experiment - name. This feature is to help users conveniently get the experiment - information from screen or log file. Default: 1000. - out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. - If ``out_dir`` is specified, logs will be copied to a new directory - which is the concatenation of ``out_dir`` and the last level - directory of ``runner.work_dir``. Default: None. - `New in version 1.3.16.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be copied to ``out_dir``. - Default: ('.log.json', '.log', '.py'). - `New in version 1.3.16.` - keep_local (bool, optional): Whether to keep local log when - :attr:`out_dir` is specified. If False, the local log will be - removed. Default: True. - `New in version 1.3.16.` - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - - def __init__(self, - by_epoch=True, - interval=10, - ignore_last=True, - reset_flag=False, - interval_exp_name=1000, - out_dir=None, - out_suffix=('.log.json', '.log', '.py'), - keep_local=True, - file_client_args=None): - super(TextLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.by_epoch = by_epoch - self.time_sec_tot = 0 - self.interval_exp_name = interval_exp_name - - if out_dir is None and file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" when `out_dir` is not' - 'specified.') - self.out_dir = out_dir - - if not (out_dir is None or isinstance(out_dir, str) - or is_tuple_of(out_dir, str)): - raise TypeError('out_dir should be "None" or string or tuple of ' - 'string, but got {out_dir}') - self.out_suffix = out_suffix - - self.keep_local = keep_local - self.file_client_args = file_client_args - if self.out_dir is not None: - self.file_client = FileClient.infer_client(file_client_args, - self.out_dir) - - def before_run(self, runner): - super(TextLoggerHook, self).before_run(runner) - - if self.out_dir is not None: - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - # The final `self.out_dir` is the concatenation of `self.out_dir` - # and the last level directory of `runner.work_dir` - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'Text logs will be saved to {self.out_dir} by ' - f'{self.file_client.name} after the training process.')) - - self.start_iter = runner.iter - self.json_log_path = osp.join(runner.work_dir, - f'{runner.timestamp}.log.json') - if runner.meta is not None: - self._dump_log(runner.meta, runner) - - def _get_max_memory(self, runner): - device = getattr(runner.model, 'output_device', None) - mem = torch.cuda.max_memory_allocated(device=device) - mem_mb = torch.tensor([int(mem) // (1024 * 1024)], - dtype=torch.int, - device=device) - if runner.world_size > 1: - dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) - return mem_mb.item() - - def _log_info(self, log_dict, runner): - # print exp name for users to distinguish experiments - # at every ``interval_exp_name`` iterations and the end of each epoch - if runner.meta is not None and 'exp_name' in runner.meta: - if (self.every_n_iters(runner, self.interval_exp_name)) or ( - self.by_epoch and self.end_of_epoch(runner)): - exp_info = f'Exp name: {runner.meta["exp_name"]}' - runner.logger.info(exp_info) - - if log_dict['mode'] == 'train': - if isinstance(log_dict['lr'], dict): - lr_str = [] - for k, val in log_dict['lr'].items(): - lr_str.append(f'lr_{k}: {val:.3e}') - lr_str = ' '.join(lr_str) - else: - lr_str = f'lr: {log_dict["lr"]:.3e}' - - # by epoch: Epoch [4][100/1000] - # by iter: Iter [100/100000] - if self.by_epoch: - log_str = f'Epoch [{log_dict["epoch"]}]' \ - f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' - else: - log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' - log_str += f'{lr_str}, ' - - if 'time' in log_dict.keys(): - self.time_sec_tot += (log_dict['time'] * self.interval) - time_sec_avg = self.time_sec_tot / ( - runner.iter - self.start_iter + 1) - eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) - eta_str = str(datetime.timedelta(seconds=int(eta_sec))) - log_str += f'eta: {eta_str}, ' - log_str += f'time: {log_dict["time"]:.3f}, ' \ - f'data_time: {log_dict["data_time"]:.3f}, ' - # statistic memory - if torch.cuda.is_available(): - log_str += f'memory: {log_dict["memory"]}, ' - else: - # val/test time - # here 1000 is the length of the val dataloader - # by epoch: Epoch[val] [4][1000] - # by iter: Iter[val] [1000] - if self.by_epoch: - log_str = f'Epoch({log_dict["mode"]}) ' \ - f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' - else: - log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' - - log_items = [] - for name, val in log_dict.items(): - # TODO: resolve this hack - # these items have been in log_str - if name in [ - 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', - 'memory', 'epoch' - ]: - continue - if isinstance(val, float): - val = f'{val:.4f}' - log_items.append(f'{name}: {val}') - log_str += ', '.join(log_items) - - runner.logger.info(log_str) - - def _dump_log(self, log_dict, runner): - # dump log in json format - json_log = OrderedDict() - for k, v in log_dict.items(): - json_log[k] = self._round_float(v) - # only append log at last line - if runner.rank == 0: - with open(self.json_log_path, 'a+') as f: - mmcv.dump(json_log, f, file_format='json') - f.write('\n') - - def _round_float(self, items): - if isinstance(items, list): - return [self._round_float(item) for item in items] - elif isinstance(items, float): - return round(items, 5) - else: - return items - - def log(self, runner): - if 'eval_iter_num' in runner.log_buffer.output: - # this doesn't modify runner.iter and is regardless of by_epoch - cur_iter = runner.log_buffer.output.pop('eval_iter_num') - else: - cur_iter = self.get_iter(runner, inner_iter=True) - - log_dict = OrderedDict( - mode=self.get_mode(runner), - epoch=self.get_epoch(runner), - iter=cur_iter) - - # only record lr of the first param group - cur_lr = runner.current_lr() - if isinstance(cur_lr, list): - log_dict['lr'] = cur_lr[0] - else: - assert isinstance(cur_lr, dict) - log_dict['lr'] = {} - for k, lr_ in cur_lr.items(): - assert isinstance(lr_, list) - log_dict['lr'].update({k: lr_[0]}) - - if 'time' in runner.log_buffer.output: - # statistic memory - if torch.cuda.is_available(): - log_dict['memory'] = self._get_max_memory(runner) - - log_dict = dict(log_dict, **runner.log_buffer.output) - - self._log_info(log_dict, runner) - self._dump_log(log_dict, runner) - return log_dict - - def after_run(self, runner): - # copy or upload logs to self.out_dir - if self.out_dir is not None: - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - out_filepath = self.file_client.join_path( - self.out_dir, filename) - with open(local_filepath, 'r') as f: - self.file_client.put_text(f.read(), out_filepath) - - runner.logger.info( - (f'The file {local_filepath} has been uploaded to ' - f'{out_filepath}.')) - - if not self.keep_local: - os.remove(local_filepath) - runner.logger.info( - (f'{local_filepath} was removed due to the ' - '`self.keep_local=False`')) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/wandb.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/wandb.py deleted file mode 100644 index 78b890ee1..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/logger/wandb.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import scandir -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class WandbLoggerHook(LoggerHook): - """Class to log metrics with wandb. - - It requires `wandb`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the initialization keys. Check - https://docs.wandb.ai/ref/python/init for more init arguments. - interval (int): Logging interval (every k iterations). - Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - commit (bool): Save the metrics dict to the wandb server and increment - the step. If false ``wandb.log`` just updates the current metrics - dict with the row argument and metrics won't be saved until - ``wandb.log`` is called with ``commit=True``. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - log_artifact (bool): If True, artifacts in {work_dir} will be uploaded - to wandb after training ends. - Default: True - `New in version 1.4.3.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be uploaded to wandb. - Default: ('.log.json', '.log', '.py'). - `New in version 1.4.3.` - - .. _wandb: - https://docs.wandb.ai - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - commit=True, - by_epoch=True, - with_step=True, - log_artifact=True, - out_suffix=('.log.json', '.log', '.py')): - super(WandbLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_wandb() - self.init_kwargs = init_kwargs - self.commit = commit - self.with_step = with_step - self.log_artifact = log_artifact - self.out_suffix = out_suffix - - def import_wandb(self): - try: - import wandb - except ImportError: - raise ImportError( - 'Please run "pip install wandb" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(WandbLoggerHook, self).before_run(runner) - if self.wandb is None: - self.import_wandb() - if self.init_kwargs: - self.wandb.init(**self.init_kwargs) - else: - self.wandb.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - if self.with_step: - self.wandb.log( - tags, step=self.get_iter(runner), commit=self.commit) - else: - tags['global_step'] = self.get_iter(runner) - self.wandb.log(tags, commit=self.commit) - - @master_only - def after_run(self, runner): - if self.log_artifact: - wandb_artifact = self.wandb.Artifact( - name='artifacts', type='model') - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - wandb_artifact.add_file(local_filepath) - self.wandb.log_artifact(wandb_artifact) - self.wandb.join() diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/lr_updater.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/lr_updater.py deleted file mode 100644 index ee2a53a65..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/lr_updater.py +++ /dev/null @@ -1,730 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from math import cos, pi - -import mmcv -from .hook import HOOKS, Hook - - -class LrUpdaterHook(Hook): - """LR Scheduler in MMCV. - - Args: - by_epoch (bool): LR changes epoch by epoch - warmup (string): Type of warmup used. It can be None(use no warmup), - 'constant', 'linear' or 'exp' - warmup_iters (int): The number of iterations or epochs that warmup - lasts - warmup_ratio (float): LR used at the beginning of warmup equals to - warmup_ratio * initial_lr - warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters - means the number of epochs that warmup lasts, otherwise means the - number of iteration that warmup lasts - """ - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.1, - warmup_by_epoch=False): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_ratio" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - self.warmup_by_epoch = warmup_by_epoch - - if self.warmup_by_epoch: - self.warmup_epochs = self.warmup_iters - self.warmup_iters = None - else: - self.warmup_epochs = None - - self.base_lr = [] # initial lr for all param groups - self.regular_lr = [] # expected lr if no warming up is performed - - def _set_lr(self, runner, lr_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, lr in zip(optim.param_groups, lr_groups[k]): - param_group['lr'] = lr - else: - for param_group, lr in zip(runner.optimizer.param_groups, - lr_groups): - param_group['lr'] = lr - - def get_lr(self, runner, base_lr): - raise NotImplementedError - - def get_regular_lr(self, runner): - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(runner, _base_lr) - for _base_lr in self.base_lr[k] - ] - lr_groups.update({k: _lr_group}) - - return lr_groups - else: - return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - if self.warmup == 'constant': - warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_lr = [_lr * (1 - k) for _lr in regular_lr] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.regular_lr, dict): - lr_groups = {} - for key, regular_lr in self.regular_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.regular_lr) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - group.setdefault('initial_lr', group['lr']) - _base_lr = [ - group['initial_lr'] for group in optim.param_groups - ] - self.base_lr.update({k: _base_lr}) - else: - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['initial_lr'] for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if self.warmup_iters is None: - epoch_len = len(runner.data_loader) - self.warmup_iters = self.warmup_epochs * epoch_len - - if not self.by_epoch: - return - - self.regular_lr = self.get_regular_lr(runner) - self._set_lr(runner, self.regular_lr) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_lr = self.get_regular_lr(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - - -@HOOKS.register_module() -class FixedLrUpdaterHook(LrUpdaterHook): - - def __init__(self, **kwargs): - super(FixedLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - return base_lr - - -@HOOKS.register_module() -class StepLrUpdaterHook(LrUpdaterHook): - """Step LR scheduler with min_lr clipping. - - Args: - step (int | list[int]): Step to decay the LR. If an int value is given, - regard it as the decay interval. If a list is given, decay LR at - these steps. - gamma (float, optional): Decay LR ratio. Default: 0.1. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. If None - is given, we don't perform lr clipping. Default: None. - """ - - def __init__(self, step, gamma=0.1, min_lr=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_lr = min_lr - super(StepLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - lr = base_lr * (self.gamma**exp) - if self.min_lr is not None: - # clip to a minimum value - lr = max(lr, self.min_lr) - return lr - - -@HOOKS.register_module() -class ExpLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, **kwargs): - self.gamma = gamma - super(ExpLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * self.gamma**progress - - -@HOOKS.register_module() -class PolyLrUpdaterHook(LrUpdaterHook): - - def __init__(self, power=1., min_lr=0., **kwargs): - self.power = power - self.min_lr = min_lr - super(PolyLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - coeff = (1 - progress / max_progress)**self.power - return (base_lr - self.min_lr) * coeff + self.min_lr - - -@HOOKS.register_module() -class InvLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, power=1., **kwargs): - self.gamma = gamma - self.power = power - super(InvLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * (1 + self.gamma * progress)**(-self.power) - - -@HOOKS.register_module() -class CosineAnnealingLrUpdaterHook(LrUpdaterHook): - """CosineAnnealing LR scheduler. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(CosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): - """Flat + Cosine lr schedule. - - Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 - - Args: - start_percent (float): When to start annealing the learning rate - after the percentage of the total training steps. - The value should be in range [0, 1). - Default: 0.75 - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - start_percent=0.75, - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - if start_percent < 0 or start_percent > 1 or not isinstance( - start_percent, float): - raise ValueError( - 'expected float between 0 and 1 start_percent, but ' - f'got {start_percent}') - self.start_percent = start_percent - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(FlatCosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - start = round(runner.max_epochs * self.start_percent) - progress = runner.epoch - start - max_progress = runner.max_epochs - start - else: - start = round(runner.max_iters * self.start_percent) - progress = runner.iter - start - max_progress = runner.max_iters - start - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress < 0: - return base_lr - else: - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class CosineRestartLrUpdaterHook(LrUpdaterHook): - """Cosine annealing with restarts learning rate scheme. - - Args: - periods (list[int]): Periods for each cosine anneling cycle. - restart_weights (list[float], optional): Restart weights at each - restart iteration. Default: [1]. - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - periods, - restart_weights=[1], - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.periods = periods - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - self.restart_weights = restart_weights - assert (len(self.periods) == len(self.restart_weights) - ), 'periods and restart_weights should have the same length.' - super(CosineRestartLrUpdaterHook, self).__init__(**kwargs) - - self.cumulative_periods = [ - sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) - ] - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - else: - progress = runner.iter - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - idx = get_position_from_periods(progress, self.cumulative_periods) - current_weight = self.restart_weights[idx] - nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] - current_periods = self.periods[idx] - - alpha = min((progress - nearest_restart) / current_periods, 1) - return annealing_cos(base_lr, target_lr, alpha, current_weight) - - -def get_position_from_periods(iteration, cumulative_periods): - """Get the position from a period list. - - It will return the index of the right-closest number in the period list. - For example, the cumulative_periods = [100, 200, 300, 400], - if iteration == 50, return 0; - if iteration == 210, return 2; - if iteration == 300, return 3. - - Args: - iteration (int): Current iteration. - cumulative_periods (list[int]): Cumulative period list. - - Returns: - int: The position of the right-closest number in the period list. - """ - for i, period in enumerate(cumulative_periods): - if iteration < period: - return i - raise ValueError(f'Current iteration {iteration} exceeds ' - f'cumulative_periods {cumulative_periods}') - - -@HOOKS.register_module() -class CyclicLrUpdaterHook(LrUpdaterHook): - """Cyclic LR Scheduler. - - Implement the cyclical learning rate policy (CLR) described in - https://arxiv.org/pdf/1506.01186.pdf - - Different from the original paper, we use cosine annealing rather than - triangular policy inside a cycle. This improves the performance in the - 3D detection area. - - Args: - by_epoch (bool, optional): Whether to update LR by epoch. - target_ratio (tuple[float], optional): Relative ratio of the highest LR - and the lowest LR to the initial LR. - cyclic_times (int, optional): Number of cycles during training - step_ratio_up (float, optional): The ratio of the increasing process of - LR in the total cycle. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch=False, - target_ratio=(10, 1e-4), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - gamma=1, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - assert 0 < gamma <= 1, \ - '"gamma" must be in range (0, 1]' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.max_iter_per_phase = None - self.lr_phases = [] # init lr_phases - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicLrUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicLrUpdaterHook, self).before_run(runner) - # initiate lr_phases - # total lr_phases are separated as up and down - self.max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * self.max_iter_per_phase) - self.lr_phases.append([0, iter_up_phase, 1, self.target_ratio[0]]) - self.lr_phases.append([ - iter_up_phase, self.max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - # Update weight decay - scale = self.gamma**curr_cycle - - for (start_iter, end_iter, start_ratio, end_ratio) in self.lr_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_lr and base lr. The target end_ratio can be - # expressed as: - # end_ratio = (base_lr + scale * (max_lr - base_lr)) / base_lr - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_lr * start_ratio, - base_lr * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleLrUpdaterHook(LrUpdaterHook): - """One Cycle LR Scheduler. - - The 1cycle learning rate policy changes the learning rate after every - batch. The one cycle learning rate policy is described in - https://arxiv.org/pdf/1708.07120.pdf - - Args: - max_lr (float or list): Upper learning rate boundaries in the cycle - for each parameter group. - total_steps (int, optional): The total number of steps in the cycle. - Note that if a value is not provided here, it will be the max_iter - of runner. Default: None. - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - div_factor (float): Determines the initial learning rate via - initial_lr = max_lr/div_factor - Default: 25 - final_div_factor (float): Determines the minimum learning rate via - min_lr = initial_lr/final_div_factor - Default: 1e4 - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - max_lr, - total_steps=None, - pct_start=0.3, - anneal_strategy='cos', - div_factor=25, - final_div_factor=1e4, - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch = False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(max_lr, (numbers.Number, list, dict)): - raise ValueError('the type of max_lr must be the one of list or ' - f'dict, but got {type(max_lr)}') - self._max_lr = max_lr - if total_steps is not None: - if not isinstance(total_steps, int): - raise ValueError('the type of total_steps must be int, but' - f'got {type(total_steps)}') - self.total_steps = total_steps - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.div_factor = div_factor - self.final_div_factor = final_div_factor - self.three_phase = three_phase - self.lr_phases = [] # init lr_phases - super(OneCycleLrUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if hasattr(self, 'total_steps'): - total_steps = self.total_steps - else: - total_steps = runner.max_iters - if total_steps < runner.max_iters: - raise ValueError( - 'The total steps must be greater than or equal to max ' - f'iterations {runner.max_iters} of runner, but total steps ' - f'is {total_steps}.') - - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - _max_lr = format_param(k, optim, self._max_lr) - self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(optim.param_groups, self.base_lr[k]): - group.setdefault('initial_lr', lr) - else: - k = type(runner.optimizer).__name__ - _max_lr = format_param(k, runner.optimizer, self._max_lr) - self.base_lr = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(runner.optimizer.param_groups, self.base_lr): - group.setdefault('initial_lr', lr) - - if self.three_phase: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append([ - float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 - ]) - self.lr_phases.append( - [total_steps - 1, 1, 1 / self.final_div_factor]) - else: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append( - [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - start_iter = 0 - for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): - if curr_iter <= end_iter: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, - pct) - break - start_iter = end_iter - return lr - - -@HOOKS.register_module() -class LinearAnnealingLrUpdaterHook(LrUpdaterHook): - """Linear annealing LR Scheduler decays the learning rate of each parameter - group linearly. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(LinearAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_linear(base_lr, target_lr, progress / max_progress) - - -def annealing_cos(start, end, factor, weight=1): - """Calculate annealing cos learning rate. - - Cosine anneal from `weight * start + (1 - weight) * end` to `end` as - percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the cosine annealing. - end (float): The ending learing rate of the cosine annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - weight (float, optional): The combination factor of `start` and `end` - when calculating the actual starting learning rate. Default to 1. - """ - cos_out = cos(pi * factor) + 1 - return end + 0.5 * weight * (start - end) * cos_out - - -def annealing_linear(start, end, factor): - """Calculate annealing linear learning rate. - - Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the linear annealing. - end (float): The ending learing rate of the linear annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - """ - return start + (end - start) * factor - - -def format_param(name, optim, param): - if isinstance(param, numbers.Number): - return [param] * len(optim.param_groups) - elif isinstance(param, (list, tuple)): # multi param groups - if len(param) != len(optim.param_groups): - raise ValueError(f'expected {len(optim.param_groups)} ' - f'values for {name}, got {len(param)}') - return param - else: # multi optimizers - if name not in param: - raise KeyError(f'{name} is not found in {param.keys()}') - return param[name] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/memory.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/memory.py deleted file mode 100644 index 70cf9a838..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class EmptyCacheHook(Hook): - - def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): - self._before_epoch = before_epoch - self._after_epoch = after_epoch - self._after_iter = after_iter - - def after_iter(self, runner): - if self._after_iter: - torch.cuda.empty_cache() - - def before_epoch(self, runner): - if self._before_epoch: - torch.cuda.empty_cache() - - def after_epoch(self, runner): - if self._after_epoch: - torch.cuda.empty_cache() diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/momentum_updater.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/momentum_updater.py deleted file mode 100644 index aa15fe23c..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/momentum_updater.py +++ /dev/null @@ -1,566 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -from .hook import HOOKS, Hook -from .lr_updater import annealing_cos, annealing_linear, format_param - - -class MomentumUpdaterHook(Hook): - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.9): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_momentum" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - - self.base_momentum = [] # initial momentum for all param groups - self.regular_momentum = [ - ] # expected momentum if no warming up is performed - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, base_momentum): - raise NotImplementedError - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k in runner.optimizer.keys(): - _momentum_group = [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum[k] - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - return [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum - ] - - def get_warmup_momentum(self, cur_iters): - - def _get_warmup_momentum(cur_iters, regular_momentum): - if self.warmup == 'constant': - warmup_momentum = [ - _momentum / self.warmup_ratio - for _momentum in regular_momentum - ] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_momentum = [ - _momentum / (1 - k) for _momentum in regular_momentum - ] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_momentum = [ - _momentum / k for _momentum in regular_momentum - ] - return warmup_momentum - - if isinstance(self.regular_momentum, dict): - momentum_groups = {} - for key, regular_momentum in self.regular_momentum.items(): - momentum_groups[key] = _get_warmup_momentum( - cur_iters, regular_momentum) - return momentum_groups - else: - return _get_warmup_momentum(cur_iters, self.regular_momentum) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, - # if 'initial_momentum' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_momentum = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - _base_momentum = [ - group['initial_momentum'] for group in optim.param_groups - ] - self.base_momentum.update({k: _base_momentum}) - else: - for group in runner.optimizer.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - self.base_momentum = [ - group['initial_momentum'] - for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if not self.by_epoch: - return - self.regular_momentum = self.get_regular_momentum(runner) - self._set_momentum(runner, self.regular_momentum) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_momentum = self.get_regular_momentum(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - - -@HOOKS.register_module() -class StepMomentumUpdaterHook(MomentumUpdaterHook): - """Step momentum scheduler with min value clipping. - - Args: - step (int | list[int]): Step to decay the momentum. If an int value is - given, regard it as the decay interval. If a list is given, decay - momentum at these steps. - gamma (float, optional): Decay momentum ratio. Default: 0.5. - min_momentum (float, optional): Minimum momentum value to keep. If - momentum after decay is lower than this value, it will be clipped - accordingly. If None is given, we don't perform lr clipping. - Default: None. - """ - - def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_momentum = min_momentum - super(StepMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - momentum = base_momentum * (self.gamma**exp) - if self.min_momentum is not None: - # clip to a minimum value - momentum = max(momentum, self.min_momentum) - return momentum - - -@HOOKS.register_module() -class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Cosine annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(CosineAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_cos(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class LinearAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Linear annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(LinearAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_linear(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class CyclicMomentumUpdaterHook(MomentumUpdaterHook): - """Cyclic momentum Scheduler. - - Implement the cyclical momentum scheduler policy described in - https://arxiv.org/pdf/1708.07120.pdf - - This momentum scheduler usually used together with the CyclicLRUpdater - to improve the performance in the 3D detection area. - - Args: - target_ratio (tuple[float]): Relative ratio of the lowest momentum and - the highest momentum to the initial momentum. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of momentum - in the total cycle. - by_epoch (bool): Whether to update momentum by epoch. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch=False, - target_ratio=(0.85 / 0.95, 1), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - gamma=1, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.momentum_phases = [] # init momentum_phases - - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - # currently only support by_epoch=False - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicMomentumUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicMomentumUpdaterHook, self).before_run(runner) - # initiate momentum_phases - # total momentum_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.max_iter_per_phase = max_iter_per_phase - self.momentum_phases.append( - [0, iter_up_phase, 1, self.target_ratio[0]]) - self.momentum_phases.append([ - iter_up_phase, max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_momentum(self, runner, base_momentum): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - scale = self.gamma**curr_cycle - for (start_iter, end_iter, start_ratio, end_ratio) \ - in self.momentum_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_momentum and base momentum. The target end_ratio - # can be expressed as: - # end_ratio = (base_momentum + scale * \ - # (max_momentum - base_momentum)) / base_momentum - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_momentum * start_ratio, - base_momentum * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): - """OneCycle momentum Scheduler. - - This momentum scheduler usually used together with the OneCycleLrUpdater - to improve the performance. - - Args: - base_momentum (float or list): Lower momentum boundaries in the cycle - for each parameter group. Note that momentum is cycled inversely - to learning rate; at the peak of a cycle, momentum is - 'base_momentum' and learning rate is 'max_lr'. - Default: 0.85 - max_momentum (float or list): Upper momentum boundaries in the cycle - for each parameter group. Functionally, - it defines the cycle amplitude (max_momentum - base_momentum). - Note that momentum is cycled inversely - to learning rate; at the start of a cycle, momentum is - 'max_momentum' and learning rate is 'base_lr' - Default: 0.95 - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - base_momentum=0.85, - max_momentum=0.95, - pct_start=0.3, - anneal_strategy='cos', - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch=False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(base_momentum, (float, list, dict)): - raise ValueError('base_momentum must be the type among of float,' - 'list or dict.') - self._base_momentum = base_momentum - if not isinstance(max_momentum, (float, list, dict)): - raise ValueError('max_momentum must be the type among of float,' - 'list or dict.') - self._max_momentum = max_momentum - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('Expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must by one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.three_phase = three_phase - self.momentum_phases = [] # init momentum_phases - super(OneCycleMomentumUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip( - optim.param_groups, _base_momentum, _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - else: - optim = runner.optimizer - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - k = type(optim).__name__ - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip(optim.param_groups, - _base_momentum, - _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - - if self.three_phase: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': - float(2 * self.pct_start * runner.max_iters) - 2, - 'start_momentum': - 'base_momentum', - 'end_momentum': - 'max_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'max_momentum', - 'end_momentum': 'max_momentum' - }) - else: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'base_momentum', - 'end_momentum': 'max_momentum' - }) - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, param_group): - curr_iter = runner.iter - start_iter = 0 - for i, phase in enumerate(self.momentum_phases): - end_iter = phase['end_iter'] - if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - momentum = self.anneal_func( - param_group[phase['start_momentum']], - param_group[phase['end_momentum']], pct) - break - start_iter = end_iter - return momentum - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k, optim in runner.optimizer.items(): - _momentum_group = [ - self.get_momentum(runner, param_group) - for param_group in optim.param_groups - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - momentum_groups = [] - for param_group in runner.optimizer.param_groups: - momentum_groups.append(self.get_momentum(runner, param_group)) - return momentum_groups diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/optimizer.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/optimizer.py deleted file mode 100644 index 12c885183..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/optimizer.py +++ /dev/null @@ -1,556 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -from collections import defaultdict -from itertools import chain - -from torch.nn.utils import clip_grad - -from mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version -from ..dist_utils import allreduce_grads -from ..fp16_utils import LossScaler, wrap_fp16_model -from .hook import HOOKS, Hook - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - from torch.cuda.amp import GradScaler -except ImportError: - pass - - -@HOOKS.register_module() -class OptimizerHook(Hook): - """A hook contains custom operations for the optimizer. - - Args: - grad_clip (dict, optional): A config dict to control the clip_grad. - Default: None. - detect_anomalous_params (bool): This option is only used for - debugging which will slow down the training speed. - Detect anomalous parameters that are not included in - the computational graph with `loss` as the root. - There are two cases - - - Parameters were not used during - forward pass. - - Parameters were not used to produce - loss. - Default: False. - """ - - def __init__(self, grad_clip=None, detect_anomalous_params=False): - self.grad_clip = grad_clip - self.detect_anomalous_params = detect_anomalous_params - - def clip_grads(self, params): - params = list( - filter(lambda p: p.requires_grad and p.grad is not None, params)) - if len(params) > 0: - return clip_grad.clip_grad_norm_(params, **self.grad_clip) - - def after_train_iter(self, runner): - runner.optimizer.zero_grad() - if self.detect_anomalous_params: - self.detect_anomalous_parameters(runner.outputs['loss'], runner) - runner.outputs['loss'].backward() - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - - def detect_anomalous_parameters(self, loss, runner): - logger = runner.logger - parameters_in_graph = set() - visited = set() - - def traverse(grad_fn): - if grad_fn is None: - return - if grad_fn not in visited: - visited.add(grad_fn) - if hasattr(grad_fn, 'variable'): - parameters_in_graph.add(grad_fn.variable) - parents = grad_fn.next_functions - if parents is not None: - for parent in parents: - grad_fn = parent[0] - traverse(grad_fn) - - traverse(loss.grad_fn) - for n, p in runner.model.named_parameters(): - if p not in parameters_in_graph and p.requires_grad: - logger.log( - level=logging.ERROR, - msg=f'{n} with shape {p.size()} is not ' - f'in the computational graph \n') - - -@HOOKS.register_module() -class GradientCumulativeOptimizerHook(OptimizerHook): - """Optimizer Hook implements multi-iters gradient cumulating. - - Args: - cumulative_iters (int, optional): Num of gradient cumulative iters. - The optimizer will step every `cumulative_iters` iters. - Defaults to 1. - - Examples: - >>> # Use cumulative_iters to simulate a large batch size - >>> # It is helpful when the hardware cannot handle a large batch size. - >>> loader = DataLoader(data, batch_size=64) - >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) - >>> # almost equals to - >>> loader = DataLoader(data, batch_size=256) - >>> optim_hook = OptimizerHook() - """ - - def __init__(self, cumulative_iters=1, **kwargs): - super(GradientCumulativeOptimizerHook, self).__init__(**kwargs) - - assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ - f'cumulative_iters only accepts positive int, but got ' \ - f'{type(cumulative_iters)} instead.' - - self.cumulative_iters = cumulative_iters - self.divisible_iters = 0 - self.remainder_iters = 0 - self.initialized = False - - def has_batch_norm(self, module): - if isinstance(module, _BatchNorm): - return True - for m in module.children(): - if self.has_batch_norm(m): - return True - return False - - def _init(self, runner): - if runner.iter % self.cumulative_iters != 0: - runner.logger.warning( - 'Resume iter number is not divisible by cumulative_iters in ' - 'GradientCumulativeOptimizerHook, which means the gradient of ' - 'some iters is lost and the result may be influenced slightly.' - ) - - if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: - runner.logger.warning( - 'GradientCumulativeOptimizerHook may slightly decrease ' - 'performance if the model has BatchNorm layers.') - - residual_iters = runner.max_iters - runner.iter - - self.divisible_iters = ( - residual_iters // self.cumulative_iters * self.cumulative_iters) - self.remainder_iters = residual_iters - self.divisible_iters - - self.initialized = True - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - runner.optimizer.zero_grad() - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - self.loss_scaler = GradScaler() - elif isinstance(loss_scale, float): - self._scale_update_param = loss_scale - self.loss_scaler = GradScaler(init_scale=loss_scale) - elif isinstance(loss_scale, dict): - self.loss_scaler = GradScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training.""" - # wrap model mode to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer to - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients. - 3. Unscale the optimizer’s gradient tensors. - 4. Call optimizer.step() and update scale factor. - 5. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - - self.loss_scaler.scale(runner.outputs['loss']).backward() - self.loss_scaler.unscale_(runner.optimizer) - # grad clip - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using PyTorch's implementation) implements - multi-iters gradient cumulating. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - """ - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - - self.loss_scaler.scale(loss).backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - self.loss_scaler.unscale_(runner.optimizer) - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() - -else: - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (mmcv's implementation). - - The steps of fp16 optimizer is as follows. - 1. Scale the loss value. - 2. BP in the fp16 model. - 2. Copy gradients from fp16 model to fp32 weights. - 3. Update fp32 weights. - 4. Copy updated parameters from fp32 weights to fp16 model. - - Refer to https://arxiv.org/abs/1710.03740 for more details. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of LossScaler. - Defaults to 512. - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - if loss_scale == 'dynamic': - self.loss_scaler = LossScaler(mode='dynamic') - elif isinstance(loss_scale, float): - self.loss_scaler = LossScaler( - init_scale=loss_scale, mode='static') - elif isinstance(loss_scale, dict): - self.loss_scaler = LossScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training. - - 1. Make a master copy of fp32 weights for optimization. - 2. Convert the main model from fp32 to fp16. - """ - # keep a copy of fp32 weights - old_groups = runner.optimizer.param_groups - runner.optimizer.param_groups = copy.deepcopy( - runner.optimizer.param_groups) - state = defaultdict(dict) - p_map = { - old_p: p - for old_p, p in zip( - chain(*(g['params'] for g in old_groups)), - chain(*(g['params'] - for g in runner.optimizer.param_groups))) - } - for k, v in runner.optimizer.state.items(): - state[p_map[k]] = v - runner.optimizer.state = state - # convert model to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer `loss_scalar.py` - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients (fp16). - 3. Copy gradients from the model to the fp32 weight copy. - 4. Scale the gradients back and update the fp32 weight copy. - 5. Copy back the params from fp32 weight copy to the fp16 model. - 6. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - # scale the loss value - scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale - scaled_loss.backward() - # copy fp16 grads in the model to fp32 params in the optimizer - - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - self.loss_scaler.update_scale(has_overflow) - if has_overflow: - runner.logger.warning('Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using mmcv implementation) implements multi- - iters gradient cumulating.""" - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - - loss = runner.outputs['loss'] - loss = loss / loss_factor - - # scale the loss value - scaled_loss = loss * self.loss_scaler.loss_scale - scaled_loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - else: - runner.logger.warning( - 'Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - self.loss_scaler.update_scale(has_overflow) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/profiler.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/profiler.py deleted file mode 100644 index fef9adc13..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/profiler.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Callable, List, Optional, Union - -import torch - -from ..dist_utils import master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ProfilerHook(Hook): - """Profiler to analyze performance during training. - - PyTorch Profiler is a tool that allows the collection of the performance - metrics during the training. More details on Profiler can be found at - https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile - - Args: - by_epoch (bool): Profile performance by epoch or by iteration. - Default: True. - profile_iters (int): Number of iterations for profiling. - If ``by_epoch=True``, profile_iters indicates that they are the - first profile_iters epochs at the beginning of the - training, otherwise it indicates the first profile_iters - iterations. Default: 1. - activities (list[str]): List of activity groups (CPU, CUDA) to use in - profiling. Default: ['cpu', 'cuda']. - schedule (dict, optional): Config of generating the callable schedule. - if schedule is None, profiler will not add step markers into the - trace and table view. Default: None. - on_trace_ready (callable, dict): Either a handler or a dict of generate - handler. Default: None. - record_shapes (bool): Save information about operator's input shapes. - Default: False. - profile_memory (bool): Track tensor memory allocation/deallocation. - Default: False. - with_stack (bool): Record source information (file and line number) - for the ops. Default: False. - with_flops (bool): Use formula to estimate the FLOPS of specific - operators (matrix multiplication and 2D convolution). - Default: False. - json_trace_path (str, optional): Exports the collected trace in Chrome - JSON format. Default: None. - - Example: - >>> runner = ... # instantiate a Runner - >>> # tensorboard trace - >>> trace_config = dict(type='tb_trace', dir_name='work_dir') - >>> profiler_config = dict(on_trace_ready=trace_config) - >>> runner.register_profiler_hook(profiler_config) - >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) - """ - - def __init__(self, - by_epoch: bool = True, - profile_iters: int = 1, - activities: List[str] = ['cpu', 'cuda'], - schedule: Optional[dict] = None, - on_trace_ready: Optional[Union[Callable, dict]] = None, - record_shapes: bool = False, - profile_memory: bool = False, - with_stack: bool = False, - with_flops: bool = False, - json_trace_path: Optional[str] = None) -> None: - try: - from torch import profiler # torch version >= 1.8.1 - except ImportError: - raise ImportError('profiler is the new feature of torch1.8.1, ' - f'but your version is {torch.__version__}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' - self.by_epoch = by_epoch - - if profile_iters < 1: - raise ValueError('profile_iters should be greater than 0, but got ' - f'{profile_iters}') - self.profile_iters = profile_iters - - if not isinstance(activities, list): - raise ValueError( - f'activities should be list, but got {type(activities)}') - self.activities = [] - for activity in activities: - activity = activity.lower() - if activity == 'cpu': - self.activities.append(profiler.ProfilerActivity.CPU) - elif activity == 'cuda': - self.activities.append(profiler.ProfilerActivity.CUDA) - else: - raise ValueError( - f'activity should be "cpu" or "cuda", but got {activity}') - - if schedule is not None: - self.schedule = profiler.schedule(**schedule) - else: - self.schedule = None - - self.on_trace_ready = on_trace_ready - self.record_shapes = record_shapes - self.profile_memory = profile_memory - self.with_stack = with_stack - self.with_flops = with_flops - self.json_trace_path = json_trace_path - - @master_only - def before_run(self, runner): - if self.by_epoch and runner.max_epochs < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_epochs}') - - if not self.by_epoch and runner.max_iters < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_iters}') - - if callable(self.on_trace_ready): # handler - _on_trace_ready = self.on_trace_ready - elif isinstance(self.on_trace_ready, dict): # config of handler - trace_cfg = self.on_trace_ready.copy() - trace_type = trace_cfg.pop('type') # log_trace handler - if trace_type == 'log_trace': - - def _log_handler(prof): - print(prof.key_averages().table(**trace_cfg)) - - _on_trace_ready = _log_handler - elif trace_type == 'tb_trace': # tensorboard_trace handler - try: - import torch_tb_profiler # noqa: F401 - except ImportError: - raise ImportError('please run "pip install ' - 'torch-tb-profiler" to install ' - 'torch_tb_profiler') - _on_trace_ready = torch.profiler.tensorboard_trace_handler( - **trace_cfg) - else: - raise ValueError('trace_type should be "log_trace" or ' - f'"tb_trace", but got {trace_type}') - elif self.on_trace_ready is None: - _on_trace_ready = None # type: ignore - else: - raise ValueError('on_trace_ready should be handler, dict or None, ' - f'but got {type(self.on_trace_ready)}') - - if self.by_epoch and runner.max_epochs > 1: - warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' - 'instead of 1 epoch. Since profiler will slow down ' - 'the training, it is recommended to train 1 epoch ' - 'with ProfilerHook and adjust your setting according' - ' to the profiler summary. During normal training ' - '(epoch > 1), you may disable the ProfilerHook.') - - self.profiler = torch.profiler.profile( - activities=self.activities, - schedule=self.schedule, - on_trace_ready=_on_trace_ready, - record_shapes=self.record_shapes, - profile_memory=self.profile_memory, - with_stack=self.with_stack, - with_flops=self.with_flops) - - self.profiler.__enter__() - runner.logger.info('profiler is profiling...') - - @master_only - def after_train_epoch(self, runner): - if self.by_epoch and runner.epoch == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) - - @master_only - def after_train_iter(self, runner): - self.profiler.step() - if not self.by_epoch and runner.iter == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/sampler_seed.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/sampler_seed.py deleted file mode 100644 index ee0dc6bdd..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/sampler_seed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class DistSamplerSeedHook(Hook): - """Data-loading sampler for distributed training. - - When distributed training, it is only useful in conjunction with - :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same - purpose with :obj:`IterLoader`. - """ - - def before_epoch(self, runner): - if hasattr(runner.data_loader.sampler, 'set_epoch'): - # in case the data loader uses `SequentialSampler` in Pytorch - runner.data_loader.sampler.set_epoch(runner.epoch) - elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): - # batch sampler in pytorch warps the sampler as its attributes. - runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/sync_buffer.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/sync_buffer.py deleted file mode 100644 index 6376b7ff8..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/hooks/sync_buffer.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..dist_utils import allreduce_params -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class SyncBuffersHook(Hook): - """Synchronize model buffers such as running_mean and running_var in BN at - the end of each epoch. - - Args: - distributed (bool): Whether distributed training is used. It is - effective only for distributed training. Defaults to True. - """ - - def __init__(self, distributed=True): - self.distributed = distributed - - def after_epoch(self, runner): - """All-reduce model buffers at the end of each epoch.""" - if self.distributed: - allreduce_params(runner.model.buffers()) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/iter_based_runner.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/iter_based_runner.py deleted file mode 100644 index 9892b07a4..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/iter_based_runner.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch -from torch.optim import Optimizer - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .hooks import IterTimerHook -from .utils import get_host_info - - -class IterLoader: - - def __init__(self, dataloader): - self._dataloader = dataloader - self.iter_loader = iter(self._dataloader) - self._epoch = 0 - - @property - def epoch(self): - return self._epoch - - def __next__(self): - try: - data = next(self.iter_loader) - except StopIteration: - self._epoch += 1 - if hasattr(self._dataloader.sampler, 'set_epoch'): - self._dataloader.sampler.set_epoch(self._epoch) - time.sleep(2) # Prevent possible deadlock during epoch transition - self.iter_loader = iter(self._dataloader) - data = next(self.iter_loader) - - return data - - def __len__(self): - return len(self._dataloader) - - -@RUNNERS.register_module() -class IterBasedRunner(BaseRunner): - """Iteration-based Runner. - - This runner train models iteration by iteration. - """ - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._epoch = data_loader.epoch - data_batch = next(data_loader) - self.call_hook('before_train_iter') - outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.train_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_train_iter') - self._inner_iter += 1 - self._iter += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - data_batch = next(data_loader) - self.call_hook('before_val_iter') - outputs = self.model.val_step(data_batch, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.val_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_val_iter') - self._inner_iter += 1 - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, iters) to specify the - running order and iterations. E.g, [('train', 10000), - ('val', 1000)] means running 10000 iterations for training and - 1000 iterations for validation, iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - """Resume model from checkpoint. - - Args: - checkpoint (str): Checkpoint to resume from. - resume_optimizer (bool, optional): Whether resume the optimizer(s) - if the checkpoint file includes optimizer(s). Default to True. - map_location (str, optional): Same as :func:`torch.load`. - Default to 'default'. - """ - if map_location == 'default': - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - self._inner_iter = checkpoint['meta']['iter'] - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') - - def save_checkpoint(self, - out_dir, - filename_tmpl='iter_{}.pth', - meta=None, - save_optimizer=True, - create_symlink=True): - """Save checkpoint to file. - - Args: - out_dir (str): Directory to save checkpoint files. - filename_tmpl (str, optional): Checkpoint file template. - Defaults to 'iter_{}.pth'. - meta (dict, optional): Metadata to be saved in checkpoint. - Defaults to None. - save_optimizer (bool, optional): Whether save optimizer. - Defaults to True. - create_symlink (bool, optional): Whether create symlink to the - latest checkpoint file. Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.iter + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - custom_hooks_config=None): - """Register default hooks for iter-based training. - - Checkpoint hook, optimizer stepper hook and logger hooks will be set to - `by_epoch=False` by default. - - Default hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - if checkpoint_config is not None: - checkpoint_config.setdefault('by_epoch', False) - if lr_config is not None: - lr_config.setdefault('by_epoch', False) - if log_config is not None: - for info in log_config['hooks']: - info.setdefault('by_epoch', False) - super(IterBasedRunner, self).register_training_hooks( - lr_config=lr_config, - momentum_config=momentum_config, - optimizer_config=optimizer_config, - checkpoint_config=checkpoint_config, - log_config=log_config, - timer_config=IterTimerHook(), - custom_hooks_config=custom_hooks_config) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/log_buffer.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/log_buffer.py deleted file mode 100644 index d949e2941..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/log_buffer.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import numpy as np - - -class LogBuffer: - - def __init__(self): - self.val_history = OrderedDict() - self.n_history = OrderedDict() - self.output = OrderedDict() - self.ready = False - - def clear(self): - self.val_history.clear() - self.n_history.clear() - self.clear_output() - - def clear_output(self): - self.output.clear() - self.ready = False - - def update(self, vars, count=1): - assert isinstance(vars, dict) - for key, var in vars.items(): - if key not in self.val_history: - self.val_history[key] = [] - self.n_history[key] = [] - self.val_history[key].append(var) - self.n_history[key].append(count) - - def average(self, n=0): - """Average latest n values or all values.""" - assert n >= 0 - for key in self.val_history: - values = np.array(self.val_history[key][-n:]) - nums = np.array(self.n_history[key][-n:]) - avg = np.sum(values * nums) / np.sum(nums) - self.output[key] = avg - self.ready = True diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/optimizer/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/optimizer/__init__.py deleted file mode 100644 index 53c34d047..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/optimizer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, - build_optimizer_constructor) -from .default_constructor import DefaultOptimizerConstructor - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', - 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/optimizer/builder.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/optimizer/builder.py deleted file mode 100644 index f9234eed8..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/optimizer/builder.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect - -import torch - -from ...utils import Registry, build_from_cfg - -OPTIMIZERS = Registry('optimizer') -OPTIMIZER_BUILDERS = Registry('optimizer builder') - - -def register_torch_optimizers(): - torch_optimizers = [] - for module_name in dir(torch.optim): - if module_name.startswith('__'): - continue - _optim = getattr(torch.optim, module_name) - if inspect.isclass(_optim) and issubclass(_optim, - torch.optim.Optimizer): - OPTIMIZERS.register_module()(_optim) - torch_optimizers.append(module_name) - return torch_optimizers - - -TORCH_OPTIMIZERS = register_torch_optimizers() - - -def build_optimizer_constructor(cfg): - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/optimizer/default_constructor.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/optimizer/default_constructor.py deleted file mode 100644 index ae97db880..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/optimizer/default_constructor.py +++ /dev/null @@ -1,250 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -from torch.nn import GroupNorm, LayerNorm - -from mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of -from mmcv.utils.ext_loader import check_ops_exist -from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS - - -@OPTIMIZER_BUILDERS.register_module() -class DefaultOptimizerConstructor: - """Default constructor for optimizers. - - By default each parameter share the same optimizer settings, and we - provide an argument ``paramwise_cfg`` to specify parameter-wise settings. - It is a dict and may contain the following fields: - - - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If - one of the keys in ``custom_keys`` is a substring of the name of one - parameter, then the setting of the parameter will be specified by - ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will - be ignored. It should be noted that the aforementioned ``key`` is the - longest key that is a substring of the name of the parameter. If there - are multiple matched keys with the same length, then the key with lower - alphabet order will be chosen. - ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` - and ``decay_mult``. See Example 2 below. - - ``bias_lr_mult`` (float): It will be multiplied to the learning - rate for all bias parameters (except for those in normalization - layers and offset layers of DCN). - - ``bias_decay_mult`` (float): It will be multiplied to the weight - decay for all bias parameters (except for those in - normalization layers, depthwise conv layers, offset layers of DCN). - - ``norm_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of normalization - layers. - - ``dwconv_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of depthwise conv - layers. - - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning - rate for parameters of offset layer in the deformable convs - of a model. - - ``bypass_duplicate`` (bool): If true, the duplicate parameters - would not be added into optimizer. Default: False. - - Note: - - 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will - override the effect of ``bias_lr_mult`` in the bias of offset layer. - So be careful when using both ``bias_lr_mult`` and - ``dcn_offset_lr_mult``. If you wish to apply both of them to the offset - layer in deformable convs, set ``dcn_offset_lr_mult`` to the original - ``dcn_offset_lr_mult`` * ``bias_lr_mult``. - - 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will - apply it to all the DCN layers in the model. So be careful when the - model contains multiple DCN layers in places other than backbone. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - optimizer_cfg (dict): The config dict of the optimizer. - Positional fields are - - - `type`: class name of the optimizer. - - Optional fields are - - - any arguments of the corresponding optimizer type, e.g., - lr, weight_decay, momentum, etc. - paramwise_cfg (dict, optional): Parameter-wise options. - - Example 1: - >>> model = torch.nn.modules.Conv1d(1, 1, 1) - >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, - >>> weight_decay=0.0001) - >>> paramwise_cfg = dict(norm_decay_mult=0.) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - - Example 2: - >>> # assume model have attribute model.backbone and model.cls_head - >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) - >>> paramwise_cfg = dict(custom_keys={ - 'backbone': dict(lr_mult=0.1, decay_mult=0.9)}) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - >>> # Then the `lr` and `weight_decay` for model.backbone is - >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for - >>> # model.cls_head is (0.01, 0.95). - """ - - def __init__(self, optimizer_cfg, paramwise_cfg=None): - if not isinstance(optimizer_cfg, dict): - raise TypeError('optimizer_cfg should be a dict', - f'but got {type(optimizer_cfg)}') - self.optimizer_cfg = optimizer_cfg - self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg - self.base_lr = optimizer_cfg.get('lr', None) - self.base_wd = optimizer_cfg.get('weight_decay', None) - self._validate_cfg() - - def _validate_cfg(self): - if not isinstance(self.paramwise_cfg, dict): - raise TypeError('paramwise_cfg should be None or a dict, ' - f'but got {type(self.paramwise_cfg)}') - - if 'custom_keys' in self.paramwise_cfg: - if not isinstance(self.paramwise_cfg['custom_keys'], dict): - raise TypeError( - 'If specified, custom_keys must be a dict, ' - f'but got {type(self.paramwise_cfg["custom_keys"])}') - if self.base_wd is None: - for key in self.paramwise_cfg['custom_keys']: - if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: - raise ValueError('base_wd should not be None') - - # get base lr and weight decay - # weight_decay must be explicitly specified if mult is specified - if ('bias_decay_mult' in self.paramwise_cfg - or 'norm_decay_mult' in self.paramwise_cfg - or 'dwconv_decay_mult' in self.paramwise_cfg): - if self.base_wd is None: - raise ValueError('base_wd should not be None') - - def _is_in(self, param_group, param_group_list): - assert is_list_of(param_group_list, dict) - param = set(param_group['params']) - param_set = set() - for group in param_group_list: - param_set.update(set(group['params'])) - - return not param.isdisjoint(param_set) - - def add_params(self, params, module, prefix='', is_dcn_module=None): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - prefix (str): The prefix of the module - is_dcn_module (int|float|None): If the current module is a - submodule of DCN, `is_dcn_module` will be passed to - control conv_offset layer's learning rate. Defaults to None. - """ - # get param-wise options - custom_keys = self.paramwise_cfg.get('custom_keys', {}) - # first sort with alphabet order and then sort with reversed len of str - sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) - - bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) - bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) - norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) - dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) - bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) - dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) - - # special rules for norm layers and depth-wise conv layers - is_norm = isinstance(module, - (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) - is_dwconv = ( - isinstance(module, torch.nn.Conv2d) - and module.in_channels == module.groups) - - for name, param in module.named_parameters(recurse=False): - param_group = {'params': [param]} - if not param.requires_grad: - params.append(param_group) - continue - if bypass_duplicate and self._is_in(param_group, params): - warnings.warn(f'{prefix} is duplicate. It is skipped since ' - f'bypass_duplicate={bypass_duplicate}') - continue - # if the parameter match one of the custom keys, ignore other rules - is_custom = False - for key in sorted_keys: - if key in f'{prefix}.{name}': - is_custom = True - lr_mult = custom_keys[key].get('lr_mult', 1.) - param_group['lr'] = self.base_lr * lr_mult - if self.base_wd is not None: - decay_mult = custom_keys[key].get('decay_mult', 1.) - param_group['weight_decay'] = self.base_wd * decay_mult - break - - if not is_custom: - # bias_lr_mult affects all bias parameters - # except for norm.bias dcn.conv_offset.bias - if name == 'bias' and not (is_norm or is_dcn_module): - param_group['lr'] = self.base_lr * bias_lr_mult - - if (prefix.find('conv_offset') != -1 and is_dcn_module - and isinstance(module, torch.nn.Conv2d)): - # deal with both dcn_offset's bias & weight - param_group['lr'] = self.base_lr * dcn_offset_lr_mult - - # apply weight decay policies - if self.base_wd is not None: - # norm decay - if is_norm: - param_group[ - 'weight_decay'] = self.base_wd * norm_decay_mult - # depth-wise conv - elif is_dwconv: - param_group[ - 'weight_decay'] = self.base_wd * dwconv_decay_mult - # bias lr and decay - elif name == 'bias' and not is_dcn_module: - # TODO: current bias_decay_mult will have affect on DCN - param_group[ - 'weight_decay'] = self.base_wd * bias_decay_mult - params.append(param_group) - - if check_ops_exist(): - from mmcv.ops import DeformConv2d, ModulatedDeformConv2d - is_dcn_module = isinstance(module, - (DeformConv2d, ModulatedDeformConv2d)) - else: - is_dcn_module = False - for child_name, child_mod in module.named_children(): - child_prefix = f'{prefix}.{child_name}' if prefix else child_name - self.add_params( - params, - child_mod, - prefix=child_prefix, - is_dcn_module=is_dcn_module) - - def __call__(self, model): - if hasattr(model, 'module'): - model = model.module - - optimizer_cfg = self.optimizer_cfg.copy() - # if no paramwise option is specified, just use the global setting - if not self.paramwise_cfg: - optimizer_cfg['params'] = model.parameters() - return build_from_cfg(optimizer_cfg, OPTIMIZERS) - - # set param-wise lr and weight decay recursively - params = [] - self.add_params(params, model) - optimizer_cfg['params'] = params - - return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/priority.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/priority.py deleted file mode 100644 index 64cc4e3a0..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/priority.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from enum import Enum - - -class Priority(Enum): - """Hook priority levels. - - +--------------+------------+ - | Level | Value | - +==============+============+ - | HIGHEST | 0 | - +--------------+------------+ - | VERY_HIGH | 10 | - +--------------+------------+ - | HIGH | 30 | - +--------------+------------+ - | ABOVE_NORMAL | 40 | - +--------------+------------+ - | NORMAL | 50 | - +--------------+------------+ - | BELOW_NORMAL | 60 | - +--------------+------------+ - | LOW | 70 | - +--------------+------------+ - | VERY_LOW | 90 | - +--------------+------------+ - | LOWEST | 100 | - +--------------+------------+ - """ - - HIGHEST = 0 - VERY_HIGH = 10 - HIGH = 30 - ABOVE_NORMAL = 40 - NORMAL = 50 - BELOW_NORMAL = 60 - LOW = 70 - VERY_LOW = 90 - LOWEST = 100 - - -def get_priority(priority): - """Get priority value. - - Args: - priority (int or str or :obj:`Priority`): Priority. - - Returns: - int: The priority value. - """ - if isinstance(priority, int): - if priority < 0 or priority > 100: - raise ValueError('priority must be between 0 and 100') - return priority - elif isinstance(priority, Priority): - return priority.value - elif isinstance(priority, str): - return Priority[priority.upper()].value - else: - raise TypeError('priority must be an integer or Priority enum value') diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/utils.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/utils.py deleted file mode 100644 index 144d11e1a..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/runner/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random -import sys -import time -import warnings -from getpass import getuser -from socket import gethostname - -import numpy as np -import torch - -import mmcv - - -def get_host_info(): - """Get hostname and username. - - Return empty string if exception raised, e.g. ``getpass.getuser()`` will - lead to error in docker container - """ - host = '' - try: - host = f'{getuser()}@{gethostname()}' - except Exception as e: - warnings.warn(f'Host or user not found: {str(e)}') - finally: - return host - - -def get_time_str(): - return time.strftime('%Y%m%d_%H%M%S', time.localtime()) - - -def obj_from_dict(info, parent=None, default_args=None): - """Initialize an object from dict. - - The dict must contain the key "type", which indicates the object type, it - can be either a string or type, such as "list" or ``list``. Remaining - fields are treated as the arguments for constructing the object. - - Args: - info (dict): Object types and arguments. - parent (:class:`module`): Module which may containing expected object - classes. - default_args (dict, optional): Default arguments for initializing the - object. - - Returns: - any type: Object built from the dict. - """ - assert isinstance(info, dict) and 'type' in info - assert isinstance(default_args, dict) or default_args is None - args = info.copy() - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if parent is not None: - obj_type = getattr(parent, obj_type) - else: - obj_type = sys.modules[obj_type] - elif not isinstance(obj_type, type): - raise TypeError('type must be a str or valid type, but ' - f'got {type(obj_type)}') - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - return obj_type(**args) - - -def set_random_seed(seed, deterministic=False, use_rank_shift=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - rank_shift (bool): Whether to add rank number to the random seed to - have different random seed in different threads. Default: False. - """ - if use_rank_shift: - rank, _ = mmcv.runner.get_dist_info() - seed += rank - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/__init__.py deleted file mode 100644 index e0825ed67..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/__init__.py +++ /dev/null @@ -1,78 +0,0 @@ -# flake8: noqa -# Copyright (c) OpenMMLab. All rights reserved. -from .config import Config, ConfigDict, DictAction -from .misc import (check_prerequisites, concat_list, deprecated_api_warning, - has_method, import_modules_from_strings, is_list_of, - is_method_overridden, is_seq_of, is_str, is_tuple_of, - iter_cast, list_cast, requires_executable, requires_package, - slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, - to_ntuple, tuple_cast) -from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, - scandir, symlink) -from .progressbar import (ProgressBar, track_iter_progress, - track_parallel_progress, track_progress) -from .testing import (assert_attrs_equal, assert_dict_contains_subset, - assert_dict_has_keys, assert_is_norm_layer, - assert_keys_equal, assert_params_all_zeros, - check_python_script) -from .timer import Timer, TimerError, check_time -from .version_utils import digit_version, get_git_hash - -try: - import torch -except ImportError: - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', - 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', - 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', - 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', - 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', - 'track_progress', 'track_iter_progress', 'track_parallel_progress', - 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', - 'digit_version', 'get_git_hash', 'import_modules_from_strings', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', - 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', - 'is_method_overridden', 'has_method' - ] -else: - from .device_type import IS_IPU_AVAILABLE, IS_MLU_AVAILABLE - from .env import collect_env - from .hub import load_url - from .logging import get_logger, print_log - from .parrots_jit import jit, skip_no_elena - # yapf: disable - from .parrots_wrapper import (IS_CUDA_AVAILABLE, TORCH_VERSION, - BuildExtension, CppExtension, CUDAExtension, - DataLoader, PoolDataLoader, SyncBatchNorm, - _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, - _AvgPoolNd, _BatchNorm, _ConvNd, - _ConvTransposeMixin, _get_cuda_home, - _InstanceNorm, _MaxPoolNd, get_build_config, - is_rocm_pytorch) - # yapf: enable - from .registry import Registry, build_from_cfg - from .seed import worker_init_fn - from .trace import is_jit_tracing - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', - 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', - 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', - 'check_prerequisites', 'requires_package', 'requires_executable', - 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', - 'symlink', 'scandir', 'ProgressBar', 'track_progress', - 'track_iter_progress', 'track_parallel_progress', 'Registry', - 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', - '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', - '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', - 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', - 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', - 'deprecated_api_warning', 'digit_version', 'get_git_hash', - 'import_modules_from_strings', 'jit', 'skip_no_elena', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', - 'assert_params_all_zeros', 'check_python_script', - 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', - '_get_cuda_home', 'load_url', 'has_method', 'IS_CUDA_AVAILABLE', - 'worker_init_fn', 'IS_MLU_AVAILABLE', 'IS_IPU_AVAILABLE' - ] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/config.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/config.py deleted file mode 100644 index 8efbc24e2..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/config.py +++ /dev/null @@ -1,719 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import ast -import copy -import os -import os.path as osp -import platform -import shutil -import sys -import tempfile -import types -import uuid -import warnings -from argparse import Action, ArgumentParser -from collections import abc -from importlib import import_module -from pathlib import Path - -from addict import Dict -from yapf.yapflib.yapf_api import FormatCode - -from .misc import import_modules_from_strings -from .path import check_file_exist - -if platform.system() == 'Windows': - import regex as re -else: - import re - -BASE_KEY = '_base_' -DELETE_KEY = '_delete_' -DEPRECATION_KEY = '_deprecation_' -RESERVED_KEYS = ['filename', 'text', 'pretty_text'] - - -class ConfigDict(Dict): - - def __missing__(self, name): - raise KeyError(name) - - def __getattr__(self, name): - try: - value = super(ConfigDict, self).__getattr__(name) - except KeyError: - ex = AttributeError(f"'{self.__class__.__name__}' object has no " - f"attribute '{name}'") - except Exception as e: - ex = e - else: - return value - raise ex - - -def add_args(parser, cfg, prefix=''): - for k, v in cfg.items(): - if isinstance(v, str): - parser.add_argument('--' + prefix + k) - elif isinstance(v, int): - parser.add_argument('--' + prefix + k, type=int) - elif isinstance(v, float): - parser.add_argument('--' + prefix + k, type=float) - elif isinstance(v, bool): - parser.add_argument('--' + prefix + k, action='store_true') - elif isinstance(v, dict): - add_args(parser, v, prefix + k + '.') - elif isinstance(v, abc.Iterable): - parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') - else: - print(f'cannot parse key {prefix + k} of type {type(v)}') - return parser - - -class Config: - """A facility for config and config files. - - It supports common file formats as configs: python/json/yaml. The interface - is the same as a dict object and also allows access config values as - attributes. - - Example: - >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - >>> cfg.a - 1 - >>> cfg.b - {'b1': [0, 1]} - >>> cfg.b.b1 - [0, 1] - >>> cfg = Config.fromfile('tests/data/config/a.py') - >>> cfg.filename - "/home/kchen/projects/mmcv/tests/data/config/a.py" - >>> cfg.item4 - 'test' - >>> cfg - "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " - "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" - """ - - @staticmethod - def _validate_py_syntax(filename): - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - content = f.read() - try: - ast.parse(content) - except SyntaxError as e: - raise SyntaxError('There are syntax errors in config ' - f'file {filename}: {e}') - - @staticmethod - def _substitute_predefined_vars(filename, temp_config_name): - file_dirname = osp.dirname(filename) - file_basename = osp.basename(filename) - file_basename_no_extension = osp.splitext(file_basename)[0] - file_extname = osp.splitext(filename)[1] - support_templates = dict( - fileDirname=file_dirname, - fileBasename=file_basename, - fileBasenameNoExtension=file_basename_no_extension, - fileExtname=file_extname) - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - for key, value in support_templates.items(): - regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' - value = value.replace('\\', '/') - config_file = re.sub(regexp, value, config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - - @staticmethod - def _pre_substitute_base_vars(filename, temp_config_name): - """Substitute base variable placehoders to string, so that parsing - would work.""" - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - base_var_dict = {} - regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' - base_vars = set(re.findall(regexp, config_file)) - for base_var in base_vars: - randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' - base_var_dict[randstr] = base_var - regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' - config_file = re.sub(regexp, f'"{randstr}"', config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - return base_var_dict - - @staticmethod - def _substitute_base_vars(cfg, base_var_dict, base_cfg): - """Substitute variable strings to their actual values.""" - cfg = copy.deepcopy(cfg) - - if isinstance(cfg, dict): - for k, v in cfg.items(): - if isinstance(v, str) and v in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[v].split('.'): - new_v = new_v[new_k] - cfg[k] = new_v - elif isinstance(v, (list, tuple, dict)): - cfg[k] = Config._substitute_base_vars( - v, base_var_dict, base_cfg) - elif isinstance(cfg, tuple): - cfg = tuple( - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg) - elif isinstance(cfg, list): - cfg = [ - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg - ] - elif isinstance(cfg, str) and cfg in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[cfg].split('.'): - new_v = new_v[new_k] - cfg = new_v - - return cfg - - @staticmethod - def _file2dict(filename, use_predefined_variables=True): - filename = osp.abspath(osp.expanduser(filename)) - check_file_exist(filename) - fileExtname = osp.splitext(filename)[1] - if fileExtname not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - - with tempfile.TemporaryDirectory() as temp_config_dir: - temp_config_file = tempfile.NamedTemporaryFile( - dir=temp_config_dir, suffix=fileExtname) - if platform.system() == 'Windows': - temp_config_file.close() - temp_config_name = osp.basename(temp_config_file.name) - # Substitute predefined variables - if use_predefined_variables: - Config._substitute_predefined_vars(filename, - temp_config_file.name) - else: - shutil.copyfile(filename, temp_config_file.name) - # Substitute base variables from placeholders to strings - base_var_dict = Config._pre_substitute_base_vars( - temp_config_file.name, temp_config_file.name) - - if filename.endswith('.py'): - temp_module_name = osp.splitext(temp_config_name)[0] - sys.path.insert(0, temp_config_dir) - Config._validate_py_syntax(filename) - mod = import_module(temp_module_name) - sys.path.pop(0) - cfg_dict = { - name: value - for name, value in mod.__dict__.items() - if not name.startswith('__') - and not isinstance(value, types.ModuleType) - and not isinstance(value, types.FunctionType) - } - # delete imported module - del sys.modules[temp_module_name] - elif filename.endswith(('.yml', '.yaml', '.json')): - import mmcv - cfg_dict = mmcv.load(temp_config_file.name) - # close temp file - temp_config_file.close() - - # check deprecation information - if DEPRECATION_KEY in cfg_dict: - deprecation_info = cfg_dict.pop(DEPRECATION_KEY) - warning_msg = f'The config file {filename} will be deprecated ' \ - 'in the future.' - if 'expected' in deprecation_info: - warning_msg += f' Please use {deprecation_info["expected"]} ' \ - 'instead.' - if 'reference' in deprecation_info: - warning_msg += ' More information can be found at ' \ - f'{deprecation_info["reference"]}' - warnings.warn(warning_msg, DeprecationWarning) - - cfg_text = filename + '\n' - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - cfg_text += f.read() - - if BASE_KEY in cfg_dict: - cfg_dir = osp.dirname(filename) - base_filename = cfg_dict.pop(BASE_KEY) - base_filename = base_filename if isinstance( - base_filename, list) else [base_filename] - - cfg_dict_list = list() - cfg_text_list = list() - for f in base_filename: - _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) - cfg_dict_list.append(_cfg_dict) - cfg_text_list.append(_cfg_text) - - base_cfg_dict = dict() - for c in cfg_dict_list: - duplicate_keys = base_cfg_dict.keys() & c.keys() - if len(duplicate_keys) > 0: - raise KeyError('Duplicate key is not allowed among bases. ' - f'Duplicate keys: {duplicate_keys}') - base_cfg_dict.update(c) - - # Substitute base variables from strings to their actual values - cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, - base_cfg_dict) - - base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) - cfg_dict = base_cfg_dict - - # merge cfg_text - cfg_text_list.append(cfg_text) - cfg_text = '\n'.join(cfg_text_list) - - return cfg_dict, cfg_text - - @staticmethod - def _merge_a_into_b(a, b, allow_list_keys=False): - """merge dict ``a`` into dict ``b`` (non-inplace). - - Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid - in-place modifications. - - Args: - a (dict): The source dict to be merged into ``b``. - b (dict): The origin dict to be fetch keys from ``a``. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in source ``a`` and will replace the element of the - corresponding index in b if b is a list. Default: False. - - Returns: - dict: The modified dict of ``b`` using ``a``. - - Examples: - # Normally merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # Delete b first and merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # b is a list - >>> Config._merge_a_into_b( - ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) - [{'a': 2}, {'b': 2}] - """ - b = b.copy() - for k, v in a.items(): - if allow_list_keys and k.isdigit() and isinstance(b, list): - k = int(k) - if len(b) <= k: - raise KeyError(f'Index {k} exceeds the length of list {b}') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - elif isinstance(v, dict): - if k in b and not v.pop(DELETE_KEY, False): - allowed_types = (dict, list) if allow_list_keys else dict - if not isinstance(b[k], allowed_types): - raise TypeError( - f'{k}={v} in child config cannot inherit from ' - f'base because {k} is a dict in the child config ' - f'but is of type {type(b[k])} in base config. ' - f'You may set `{DELETE_KEY}=True` to ignore the ' - f'base config.') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - else: - b[k] = ConfigDict(v) - else: - b[k] = v - return b - - @staticmethod - def fromfile(filename, - use_predefined_variables=True, - import_custom_modules=True): - if isinstance(filename, Path): - filename = str(filename) - cfg_dict, cfg_text = Config._file2dict(filename, - use_predefined_variables) - if import_custom_modules and cfg_dict.get('custom_imports', None): - import_modules_from_strings(**cfg_dict['custom_imports']) - return Config(cfg_dict, cfg_text=cfg_text, filename=filename) - - @staticmethod - def fromstring(cfg_str, file_format): - """Generate config from config str. - - Args: - cfg_str (str): Config str. - file_format (str): Config file format corresponding to the - config str. Only py/yml/yaml/json type are supported now! - - Returns: - :obj:`Config`: Config obj. - """ - if file_format not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - if file_format != '.py' and 'dict(' in cfg_str: - # check if users specify a wrong suffix for python - warnings.warn( - 'Please check "file_format", the file format may be .py') - with tempfile.NamedTemporaryFile( - 'w', encoding='utf-8', suffix=file_format, - delete=False) as temp_file: - temp_file.write(cfg_str) - # on windows, previous implementation cause error - # see PR 1077 for details - cfg = Config.fromfile(temp_file.name) - os.remove(temp_file.name) - return cfg - - @staticmethod - def auto_argparser(description=None): - """Generate argparser from config file automatically (experimental)""" - partial_parser = ArgumentParser(description=description) - partial_parser.add_argument('config', help='config file path') - cfg_file = partial_parser.parse_known_args()[0].config - cfg = Config.fromfile(cfg_file) - parser = ArgumentParser(description=description) - parser.add_argument('config', help='config file path') - add_args(parser, cfg) - return parser, cfg - - def __init__(self, cfg_dict=None, cfg_text=None, filename=None): - if cfg_dict is None: - cfg_dict = dict() - elif not isinstance(cfg_dict, dict): - raise TypeError('cfg_dict must be a dict, but ' - f'got {type(cfg_dict)}') - for key in cfg_dict: - if key in RESERVED_KEYS: - raise KeyError(f'{key} is reserved for config file') - - if isinstance(filename, Path): - filename = str(filename) - - super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) - super(Config, self).__setattr__('_filename', filename) - if cfg_text: - text = cfg_text - elif filename: - with open(filename, 'r') as f: - text = f.read() - else: - text = '' - super(Config, self).__setattr__('_text', text) - - @property - def filename(self): - return self._filename - - @property - def text(self): - return self._text - - @property - def pretty_text(self): - - indent = 4 - - def _indent(s_, num_spaces): - s = s_.split('\n') - if len(s) == 1: - return s_ - first = s.pop(0) - s = [(num_spaces * ' ') + line for line in s] - s = '\n'.join(s) - s = first + '\n' + s - return s - - def _format_basic_types(k, v, use_mapping=False): - if isinstance(v, str): - v_str = f"'{v}'" - else: - v_str = str(v) - - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) - - return attr_str - - def _format_list(k, v, use_mapping=False): - # check if all items in the list are dict - if all(isinstance(_, dict) for _ in v): - v_str = '[\n' - v_str += '\n'.join( - f'dict({_indent(_format_dict(v_), indent)}),' - for v_ in v).rstrip(',') - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) + ']' - else: - attr_str = _format_basic_types(k, v, use_mapping) - return attr_str - - def _contain_invalid_identifier(dict_str): - contain_invalid_identifier = False - for key_name in dict_str: - contain_invalid_identifier |= \ - (not str(key_name).isidentifier()) - return contain_invalid_identifier - - def _format_dict(input_dict, outest_level=False): - r = '' - s = [] - - use_mapping = _contain_invalid_identifier(input_dict) - if use_mapping: - r += '{' - for idx, (k, v) in enumerate(input_dict.items()): - is_last = idx >= len(input_dict) - 1 - end = '' if outest_level or is_last else ',' - if isinstance(v, dict): - v_str = '\n' + _format_dict(v) - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: dict({v_str}' - else: - attr_str = f'{str(k)}=dict({v_str}' - attr_str = _indent(attr_str, indent) + ')' + end - elif isinstance(v, list): - attr_str = _format_list(k, v, use_mapping) + end - else: - attr_str = _format_basic_types(k, v, use_mapping) + end - - s.append(attr_str) - r += '\n'.join(s) - if use_mapping: - r += '}' - return r - - cfg_dict = self._cfg_dict.to_dict() - text = _format_dict(cfg_dict, outest_level=True) - # copied from setup.cfg - yapf_style = dict( - based_on_style='pep8', - blank_line_before_nested_class_or_def=True, - split_before_expression_after_opening_paren=True) - text, _ = FormatCode(text, style_config=yapf_style, verify=True) - - return text - - def __repr__(self): - return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' - - def __len__(self): - return len(self._cfg_dict) - - def __getattr__(self, name): - return getattr(self._cfg_dict, name) - - def __getitem__(self, name): - return self._cfg_dict.__getitem__(name) - - def __setattr__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setattr__(name, value) - - def __setitem__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setitem__(name, value) - - def __iter__(self): - return iter(self._cfg_dict) - - def __getstate__(self): - return (self._cfg_dict, self._filename, self._text) - - def __copy__(self): - cls = self.__class__ - other = cls.__new__(cls) - other.__dict__.update(self.__dict__) - - return other - - def __deepcopy__(self, memo): - cls = self.__class__ - other = cls.__new__(cls) - memo[id(self)] = other - - for key, value in self.__dict__.items(): - super(Config, other).__setattr__(key, copy.deepcopy(value, memo)) - - return other - - def __setstate__(self, state): - _cfg_dict, _filename, _text = state - super(Config, self).__setattr__('_cfg_dict', _cfg_dict) - super(Config, self).__setattr__('_filename', _filename) - super(Config, self).__setattr__('_text', _text) - - def dump(self, file=None): - cfg_dict = super(Config, self).__getattribute__('_cfg_dict').to_dict() - if self.filename.endswith('.py'): - if file is None: - return self.pretty_text - else: - with open(file, 'w', encoding='utf-8') as f: - f.write(self.pretty_text) - else: - import mmcv - if file is None: - file_format = self.filename.split('.')[-1] - return mmcv.dump(cfg_dict, file_format=file_format) - else: - mmcv.dump(cfg_dict, file) - - def merge_from_dict(self, options, allow_list_keys=True): - """Merge list into cfg_dict. - - Merge the dict parsed by MultipleKVAction into this cfg. - - Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} - >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) - >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - - >>> # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) - >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) - >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - - Args: - options (dict): dict of configs to merge from. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in ``options`` and will replace the element of the - corresponding index in the config if the config is a list. - Default: True. - """ - option_cfg_dict = {} - for full_key, v in options.items(): - d = option_cfg_dict - key_list = full_key.split('.') - for subkey in key_list[:-1]: - d.setdefault(subkey, ConfigDict()) - d = d[subkey] - subkey = key_list[-1] - d[subkey] = v - - cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - super(Config, self).__setattr__( - '_cfg_dict', - Config._merge_a_into_b( - option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) - - -class DictAction(Action): - """ - argparse action to split an argument into KEY=VALUE form - on the first = and append to a dictionary. List options can - be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit - brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build - list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' - """ - - @staticmethod - def _parse_int_float_bool(val): - try: - return int(val) - except ValueError: - pass - try: - return float(val) - except ValueError: - pass - if val.lower() in ['true', 'false']: - return True if val.lower() == 'true' else False - if val == 'None': - return None - return val - - @staticmethod - def _parse_iterable(val): - """Parse iterable values in the string. - - All elements inside '()' or '[]' are treated as iterable values. - - Args: - val (str): Value string. - - Returns: - list | tuple: The expanded list or tuple from the string. - - Examples: - >>> DictAction._parse_iterable('1,2,3') - [1, 2, 3] - >>> DictAction._parse_iterable('[a, b, c]') - ['a', 'b', 'c'] - >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') - [(1, 2, 3), ['a', 'b'], 'c'] - """ - - def find_next_comma(string): - """Find the position of next comma in the string. - - If no ',' is found in the string, return the string length. All - chars inside '()' and '[]' are treated as one element and thus ',' - inside these brackets are ignored. - """ - assert (string.count('(') == string.count(')')) and ( - string.count('[') == string.count(']')), \ - f'Imbalanced brackets exist in {string}' - end = len(string) - for idx, char in enumerate(string): - pre = string[:idx] - # The string before this ',' is balanced - if ((char == ',') and (pre.count('(') == pre.count(')')) - and (pre.count('[') == pre.count(']'))): - end = idx - break - return end - - # Strip ' and " characters and replace whitespace. - val = val.strip('\'\"').replace(' ', '') - is_tuple = False - if val.startswith('(') and val.endswith(')'): - is_tuple = True - val = val[1:-1] - elif val.startswith('[') and val.endswith(']'): - val = val[1:-1] - elif ',' not in val: - # val is a single value - return DictAction._parse_int_float_bool(val) - - values = [] - while len(val) > 0: - comma_idx = find_next_comma(val) - element = DictAction._parse_iterable(val[:comma_idx]) - values.append(element) - val = val[comma_idx + 1:] - if is_tuple: - values = tuple(values) - return values - - def __call__(self, parser, namespace, values, option_string=None): - options = {} - for kv in values: - key, val = kv.split('=', maxsplit=1) - options[key] = self._parse_iterable(val) - setattr(namespace, self.dest, options) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/device_type.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/device_type.py deleted file mode 100644 index c29d944ab..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/device_type.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - - -def is_ipu_available(): - try: - import poptorch - return poptorch.ipuHardwareIsAvailable() - except ImportError: - return False - - -IS_IPU_AVAILABLE = is_ipu_available() - - -def is_mlu_available(): - try: - import torch - return (hasattr(torch, 'is_mlu_available') - and torch.is_mlu_available()) - except Exception: - return False - - -IS_MLU_AVAILABLE = is_mlu_available() diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/env.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/env.py deleted file mode 100644 index 511332506..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/env.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This file holding some environment constant for sharing by other files.""" - -import os.path as osp -import subprocess -import sys -from collections import defaultdict - -import cv2 -import torch - -import mmcv -from .parrots_wrapper import get_build_config - - -def collect_env(): - """Collect the information of the running environments. - - Returns: - dict: The environment information. The following fields are contained. - - - sys.platform: The variable of ``sys.platform``. - - Python: Python version. - - CUDA available: Bool, indicating if CUDA is available. - - GPU devices: Device type of each GPU. - - CUDA_HOME (optional): The env var ``CUDA_HOME``. - - NVCC (optional): NVCC version. - - GCC: GCC version, "n/a" if GCC is not installed. - - MSVC: Microsoft Virtual C++ Compiler version, Windows only. - - PyTorch: PyTorch version. - - PyTorch compiling details: The output of \ - ``torch.__config__.show()``. - - TorchVision (optional): TorchVision version. - - OpenCV: OpenCV version. - - MMCV: MMCV version. - - MMCV Compiler: The GCC version for compiling MMCV ops. - - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. - """ - env_info = {} - env_info['sys.platform'] = sys.platform - env_info['Python'] = sys.version.replace('\n', '') - - cuda_available = torch.cuda.is_available() - env_info['CUDA available'] = cuda_available - - if cuda_available: - devices = defaultdict(list) - for k in range(torch.cuda.device_count()): - devices[torch.cuda.get_device_name(k)].append(str(k)) - for name, device_ids in devices.items(): - env_info['GPU ' + ','.join(device_ids)] = name - - from mmcv.utils.parrots_wrapper import _get_cuda_home - CUDA_HOME = _get_cuda_home() - env_info['CUDA_HOME'] = CUDA_HOME - - if CUDA_HOME is not None and osp.isdir(CUDA_HOME): - try: - nvcc = osp.join(CUDA_HOME, 'bin/nvcc') - nvcc = subprocess.check_output(f'"{nvcc}" -V', shell=True) - nvcc = nvcc.decode('utf-8').strip() - release = nvcc.rfind('Cuda compilation tools') - build = nvcc.rfind('Build ') - nvcc = nvcc[release:build].strip() - except subprocess.SubprocessError: - nvcc = 'Not Available' - env_info['NVCC'] = nvcc - - try: - # Check C++ Compiler. - # For Unix-like, sysconfig has 'CC' variable like 'gcc -pthread ...', - # indicating the compiler used, we use this to get the compiler name - import sysconfig - cc = sysconfig.get_config_var('CC') - if cc: - cc = osp.basename(cc.split()[0]) - cc_info = subprocess.check_output(f'{cc} --version', shell=True) - env_info['GCC'] = cc_info.decode('utf-8').partition( - '\n')[0].strip() - else: - # on Windows, cl.exe is not in PATH. We need to find the path. - # distutils.ccompiler.new_compiler() returns a msvccompiler - # object and after initialization, path to cl.exe is found. - import locale - import os - from distutils.ccompiler import new_compiler - ccompiler = new_compiler() - ccompiler.initialize() - cc = subprocess.check_output( - f'{ccompiler.cc}', stderr=subprocess.STDOUT, shell=True) - encoding = os.device_encoding( - sys.stdout.fileno()) or locale.getpreferredencoding() - env_info['MSVC'] = cc.decode(encoding).partition('\n')[0].strip() - env_info['GCC'] = 'n/a' - except subprocess.CalledProcessError: - env_info['GCC'] = 'n/a' - - env_info['PyTorch'] = torch.__version__ - env_info['PyTorch compiling details'] = get_build_config() - - try: - import torchvision - env_info['TorchVision'] = torchvision.__version__ - except ModuleNotFoundError: - pass - - env_info['OpenCV'] = cv2.__version__ - - env_info['MMCV'] = mmcv.__version__ - - try: - from mmcv.ops import get_compiler_version, get_compiling_cuda_version - except ModuleNotFoundError: - env_info['MMCV Compiler'] = 'n/a' - env_info['MMCV CUDA Compiler'] = 'n/a' - else: - env_info['MMCV Compiler'] = get_compiler_version() - env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() - - return env_info diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/ext_loader.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/ext_loader.py deleted file mode 100644 index f82c6d568..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/ext_loader.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os -import pkgutil -import warnings -from collections import namedtuple - -import torch - -if torch.__version__ != 'parrots': - - def load_ext(name, funcs): - ext = importlib.import_module('mmcv.' + name) - for fun in funcs: - assert hasattr(ext, fun), f'{fun} miss in module {name}' - return ext -else: - from parrots import extension - from parrots.base import ParrotsException - - has_return_value_ops = [ - 'nms', - 'softnms', - 'nms_match', - 'nms_rotated', - 'top_pool_forward', - 'top_pool_backward', - 'bottom_pool_forward', - 'bottom_pool_backward', - 'left_pool_forward', - 'left_pool_backward', - 'right_pool_forward', - 'right_pool_backward', - 'fused_bias_leakyrelu', - 'upfirdn2d', - 'ms_deform_attn_forward', - 'pixel_group', - 'contour_expand', - 'diff_iou_rotated_sort_vertices_forward', - ] - - def get_fake_func(name, e): - - def fake_func(*args, **kwargs): - warnings.warn(f'{name} is not supported in parrots now') - raise e - - return fake_func - - def load_ext(name, funcs): - ExtModule = namedtuple('ExtModule', funcs) - ext_list = [] - lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - for fun in funcs: - try: - ext_fun = extension.load(fun, name, lib_dir=lib_root) - except ParrotsException as e: - if 'No element registered' not in e.message: - warnings.warn(e.message) - ext_fun = get_fake_func(fun, e) - ext_list.append(ext_fun) - else: - if fun in has_return_value_ops: - ext_list.append(ext_fun.op) - else: - ext_list.append(ext_fun.op_) - return ExtModule(*ext_list) - - -def check_ops_exist(): - ext_loader = pkgutil.find_loader('mmcv._ext') - return ext_loader is not None diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/hub.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/hub.py deleted file mode 100644 index 12fbff2ee..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/hub.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# The 1.6 release of PyTorch switched torch.save to use a new zipfile-based -# file format. It will cause RuntimeError when a checkpoint was saved in -# torch >= 1.6.0 but loaded in torch < 1.7.0. -# More details at https://github.com/open-mmlab/mmpose/issues/904 -from .parrots_wrapper import TORCH_VERSION -from .path import mkdir_or_exist -from .version_utils import digit_version - -if TORCH_VERSION != 'parrots' and digit_version(TORCH_VERSION) < digit_version( - '1.7.0'): - # Modified from https://github.com/pytorch/pytorch/blob/master/torch/hub.py - import os - import sys - import warnings - import zipfile - from urllib.parse import urlparse - - import torch - from torch.hub import HASH_REGEX, _get_torch_home, download_url_to_file - - # Hub used to support automatically extracts from zipfile manually - # compressed by users. The legacy zip format expects only one file from - # torch.save() < 1.6 in the zip. We should remove this support since - # zipfile is now default zipfile format for torch.save(). - def _is_legacy_zip_format(filename): - if zipfile.is_zipfile(filename): - infolist = zipfile.ZipFile(filename).infolist() - return len(infolist) == 1 and not infolist[0].is_dir() - return False - - def _legacy_zip_load(filename, model_dir, map_location): - warnings.warn( - 'Falling back to the old format < 1.6. This support will' - ' be deprecated in favor of default zipfile format ' - 'introduced in 1.6. Please redo torch.save() to save it ' - 'in the new zipfile format.', DeprecationWarning) - # Note: extractall() defaults to overwrite file if exists. No need to - # clean up beforehand. We deliberately don't handle tarfile here - # since our legacy serialization format was in tar. - # E.g. resnet18-5c106cde.pth which is widely used. - with zipfile.ZipFile(filename) as f: - members = f.infolist() - if len(members) != 1: - raise RuntimeError( - 'Only one file(not dir) is allowed in the zipfile') - f.extractall(model_dir) - extraced_name = members[0].filename - extracted_file = os.path.join(model_dir, extraced_name) - return torch.load(extracted_file, map_location=map_location) - - def load_url(url, - model_dir=None, - map_location=None, - progress=True, - check_hash=False, - file_name=None): - r"""Loads the Torch serialized object at the given URL. - - If downloaded file is a zip file, it will be automatically decompressed - - If the object is already present in `model_dir`, it's deserialized and - returned. - The default value of ``model_dir`` is ``/checkpoints`` where - ``hub_dir`` is the directory returned by :func:`~torch.hub.get_dir`. - - Args: - url (str): URL of the object to download - model_dir (str, optional): directory in which to save the object - map_location (optional): a function or a dict specifying how to - remap storage locations (see torch.load) - progress (bool, optional): whether or not to display a progress bar - to stderr. Default: True - check_hash(bool, optional): If True, the filename part of the URL - should follow the naming convention ``filename-.ext`` - where ```` is the first eight or more digits of the - SHA256 hash of the contents of the file. The hash is used to - ensure unique names and to verify the contents of the file. - Default: False - file_name (str, optional): name for the downloaded file. Filename - from ``url`` will be used if not set. Default: None. - - Example: - >>> url = ('https://s3.amazonaws.com/pytorch/models/resnet18-5c106' - ... 'cde.pth') - >>> state_dict = torch.hub.load_state_dict_from_url(url) - """ - # Issue warning to move data if old env is set - if os.getenv('TORCH_MODEL_ZOO'): - warnings.warn( - 'TORCH_MODEL_ZOO is deprecated, please use env ' - 'TORCH_HOME instead', DeprecationWarning) - - if model_dir is None: - torch_home = _get_torch_home() - model_dir = os.path.join(torch_home, 'checkpoints') - - mkdir_or_exist(model_dir) - - parts = urlparse(url) - filename = os.path.basename(parts.path) - if file_name is not None: - filename = file_name - cached_file = os.path.join(model_dir, filename) - if not os.path.exists(cached_file): - sys.stderr.write('Downloading: "{}" to {}\n'.format( - url, cached_file)) - hash_prefix = None - if check_hash: - r = HASH_REGEX.search(filename) # r is Optional[Match[str]] - hash_prefix = r.group(1) if r else None - download_url_to_file( - url, cached_file, hash_prefix, progress=progress) - - if _is_legacy_zip_format(cached_file): - return _legacy_zip_load(cached_file, model_dir, map_location) - - try: - return torch.load(cached_file, map_location=map_location) - except RuntimeError as error: - if digit_version(TORCH_VERSION) < digit_version('1.5.0'): - warnings.warn( - f'If the error is the same as "{cached_file} is a zip ' - 'archive (did you mean to use torch.jit.load()?)", you can' - ' upgrade your torch to 1.5.0 or higher (current torch ' - f'version is {TORCH_VERSION}). The error was raised ' - ' because the checkpoint was saved in torch>=1.6.0 but ' - 'loaded in torch<1.5.') - raise error -else: - from torch.utils.model_zoo import load_url # noqa: F401 diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/logging.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/logging.py deleted file mode 100644 index c4c7025f0..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/logging.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.distributed as dist - -logger_initialized = {} - - -def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): - """Initialize and get a logger by name. - - If the logger has not been initialized, this method will initialize the - logger by adding one or two handlers, otherwise the initialized logger will - be directly returned. During initialization, a StreamHandler will always be - added. If `log_file` is specified and the process rank is 0, a FileHandler - will also be added. - - Args: - name (str): Logger name. - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the logger. - log_level (int): The logger level. Note that only the process of - rank 0 is affected, and other processes will set the level to - "Error" thus be silent most of the time. - file_mode (str): The file mode used in opening log file. - Defaults to 'w'. - - Returns: - logging.Logger: The expected logger. - """ - logger = logging.getLogger(name) - if name in logger_initialized: - return logger - # handle hierarchical names - # e.g., logger "a" is initialized, then logger "a.b" will skip the - # initialization since it is a child of "a". - for logger_name in logger_initialized: - if name.startswith(logger_name): - return logger - - # handle duplicate logs to the console - # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) - # to the root logger. As logger.propagate is True by default, this root - # level handler causes logging messages from rank>0 processes to - # unexpectedly show up on the console, creating much unwanted clutter. - # To fix this issue, we set the root logger's StreamHandler, if any, to log - # at the ERROR level. - for handler in logger.root.handlers: - if type(handler) is logging.StreamHandler: - handler.setLevel(logging.ERROR) - - stream_handler = logging.StreamHandler() - handlers = [stream_handler] - - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - else: - rank = 0 - - # only rank 0 will add a FileHandler - if rank == 0 and log_file is not None: - # Here, the default behaviour of the official logger is 'a'. Thus, we - # provide an interface to change the file mode to the default - # behaviour. - file_handler = logging.FileHandler(log_file, file_mode) - handlers.append(file_handler) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - for handler in handlers: - handler.setFormatter(formatter) - handler.setLevel(log_level) - logger.addHandler(handler) - - if rank == 0: - logger.setLevel(log_level) - else: - logger.setLevel(logging.ERROR) - - logger_initialized[name] = True - - return logger - - -def print_log(msg, logger=None, level=logging.INFO): - """Print a log message. - - Args: - msg (str): The message to be logged. - logger (logging.Logger | str | None): The logger to be used. - Some special loggers are: - - - "silent": no message will be printed. - - other str: the logger obtained with `get_root_logger(logger)`. - - None: The `print()` method will be used to print log messages. - level (int): Logging level. Only available when `logger` is a Logger - object or "root". - """ - if logger is None: - print(msg) - elif isinstance(logger, logging.Logger): - logger.log(level, msg) - elif logger == 'silent': - pass - elif isinstance(logger, str): - _logger = get_logger(logger) - _logger.log(level, msg) - else: - raise TypeError( - 'logger should be either a logging.Logger object, str, ' - f'"silent" or None, but got {type(logger)}') diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/misc.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/misc.py deleted file mode 100644 index 7957ea89b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/misc.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections.abc -import functools -import itertools -import subprocess -import warnings -from collections import abc -from importlib import import_module -from inspect import getfullargspec -from itertools import repeat - - -# From PyTorch internals -def _ntuple(n): - - def parse(x): - if isinstance(x, collections.abc.Iterable): - return x - return tuple(repeat(x, n)) - - return parse - - -to_1tuple = _ntuple(1) -to_2tuple = _ntuple(2) -to_3tuple = _ntuple(3) -to_4tuple = _ntuple(4) -to_ntuple = _ntuple - - -def is_str(x): - """Whether the input is an string instance. - - Note: This method is deprecated since python 2 is no longer supported. - """ - return isinstance(x, str) - - -def import_modules_from_strings(imports, allow_failed_imports=False): - """Import modules from the given list of strings. - - Args: - imports (list | str | None): The given module names to be imported. - allow_failed_imports (bool): If True, the failed imports will return - None. Otherwise, an ImportError is raise. Default: False. - - Returns: - list[module] | module | None: The imported modules. - - Examples: - >>> osp, sys = import_modules_from_strings( - ... ['os.path', 'sys']) - >>> import os.path as osp_ - >>> import sys as sys_ - >>> assert osp == osp_ - >>> assert sys == sys_ - """ - if not imports: - return - single_import = False - if isinstance(imports, str): - single_import = True - imports = [imports] - if not isinstance(imports, list): - raise TypeError( - f'custom_imports must be a list but got type {type(imports)}') - imported = [] - for imp in imports: - if not isinstance(imp, str): - raise TypeError( - f'{imp} is of type {type(imp)} and cannot be imported.') - try: - imported_tmp = import_module(imp) - except ImportError: - if allow_failed_imports: - warnings.warn(f'{imp} failed to import and is ignored.', - UserWarning) - imported_tmp = None - else: - raise ImportError - imported.append(imported_tmp) - if single_import: - imported = imported[0] - return imported - - -def iter_cast(inputs, dst_type, return_type=None): - """Cast elements of an iterable object into some type. - - Args: - inputs (Iterable): The input object. - dst_type (type): Destination type. - return_type (type, optional): If specified, the output object will be - converted to this type, otherwise an iterator. - - Returns: - iterator or specified type: The converted object. - """ - if not isinstance(inputs, abc.Iterable): - raise TypeError('inputs must be an iterable object') - if not isinstance(dst_type, type): - raise TypeError('"dst_type" must be a valid type') - - out_iterable = map(dst_type, inputs) - - if return_type is None: - return out_iterable - else: - return return_type(out_iterable) - - -def list_cast(inputs, dst_type): - """Cast elements of an iterable object into a list of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=list) - - -def tuple_cast(inputs, dst_type): - """Cast elements of an iterable object into a tuple of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=tuple) - - -def is_seq_of(seq, expected_type, seq_type=None): - """Check whether it is a sequence of some type. - - Args: - seq (Sequence): The sequence to be checked. - expected_type (type): Expected type of sequence items. - seq_type (type, optional): Expected sequence type. - - Returns: - bool: Whether the sequence is valid. - """ - if seq_type is None: - exp_seq_type = abc.Sequence - else: - assert isinstance(seq_type, type) - exp_seq_type = seq_type - if not isinstance(seq, exp_seq_type): - return False - for item in seq: - if not isinstance(item, expected_type): - return False - return True - - -def is_list_of(seq, expected_type): - """Check whether it is a list of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=list) - - -def is_tuple_of(seq, expected_type): - """Check whether it is a tuple of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=tuple) - - -def slice_list(in_list, lens): - """Slice a list into several sub lists by a list of given length. - - Args: - in_list (list): The list to be sliced. - lens(int or list): The expected length of each out list. - - Returns: - list: A list of sliced list. - """ - if isinstance(lens, int): - assert len(in_list) % lens == 0 - lens = [lens] * int(len(in_list) / lens) - if not isinstance(lens, list): - raise TypeError('"indices" must be an integer or a list of integers') - elif sum(lens) != len(in_list): - raise ValueError('sum of lens and list length does not ' - f'match: {sum(lens)} != {len(in_list)}') - out_list = [] - idx = 0 - for i in range(len(lens)): - out_list.append(in_list[idx:idx + lens[i]]) - idx += lens[i] - return out_list - - -def concat_list(in_list): - """Concatenate a list of list into a single list. - - Args: - in_list (list): The list of list to be merged. - - Returns: - list: The concatenated flat list. - """ - return list(itertools.chain(*in_list)) - - -def check_prerequisites( - prerequisites, - checker, - msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' - 'found, please install them first.'): # yapf: disable - """A decorator factory to check if prerequisites are satisfied. - - Args: - prerequisites (str of list[str]): Prerequisites to be checked. - checker (callable): The checker method that returns True if a - prerequisite is meet, False otherwise. - msg_tmpl (str): The message template with two variables. - - Returns: - decorator: A specific decorator. - """ - - def wrap(func): - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - requirements = [prerequisites] if isinstance( - prerequisites, str) else prerequisites - missing = [] - for item in requirements: - if not checker(item): - missing.append(item) - if missing: - print(msg_tmpl.format(', '.join(missing), func.__name__)) - raise RuntimeError('Prerequisites not meet.') - else: - return func(*args, **kwargs) - - return wrapped_func - - return wrap - - -def _check_py_package(package): - try: - import_module(package) - except ImportError: - return False - else: - return True - - -def _check_executable(cmd): - if subprocess.call(f'which {cmd}', shell=True) != 0: - return False - else: - return True - - -def requires_package(prerequisites): - """A decorator to check if some python packages are installed. - - Example: - >>> @requires_package('numpy') - >>> func(arg1, args): - >>> return numpy.zeros(1) - array([0.]) - >>> @requires_package(['numpy', 'non_package']) - >>> func(arg1, args): - >>> return numpy.zeros(1) - ImportError - """ - return check_prerequisites(prerequisites, checker=_check_py_package) - - -def requires_executable(prerequisites): - """A decorator to check if some executable files are installed. - - Example: - >>> @requires_executable('ffmpeg') - >>> func(arg1, args): - >>> print(1) - 1 - """ - return check_prerequisites(prerequisites, checker=_check_executable) - - -def deprecated_api_warning(name_dict, cls_name=None): - """A decorator to check if some arguments are deprecate and try to replace - deprecate src_arg_name to dst_arg_name. - - Args: - name_dict(dict): - key (str): Deprecate argument names. - val (str): Expected argument names. - - Returns: - func: New function. - """ - - def api_warning_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get name of the function - func_name = old_func.__name__ - if cls_name is not None: - func_name = f'{cls_name}.{func_name}' - if args: - arg_names = args_info.args[:len(args)] - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in arg_names: - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - arg_names[arg_names.index(src_arg_name)] = dst_arg_name - if kwargs: - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in kwargs: - - assert dst_arg_name not in kwargs, ( - f'The expected behavior is to replace ' - f'the deprecated key `{src_arg_name}` to ' - f'new key `{dst_arg_name}`, but got them ' - f'in the arguments at the same time, which ' - f'is confusing. `{src_arg_name} will be ' - f'deprecated in the future, please ' - f'use `{dst_arg_name}` instead.') - - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - kwargs[dst_arg_name] = kwargs.pop(src_arg_name) - - # apply converted arguments to the decorated method - output = old_func(*args, **kwargs) - return output - - return new_func - - return api_warning_wrapper - - -def is_method_overridden(method, base_class, derived_class): - """Check if a method of base class is overridden in derived class. - - Args: - method (str): the method name to check. - base_class (type): the class of the base class. - derived_class (type | Any): the class or instance of the derived class. - """ - assert isinstance(base_class, type), \ - "base_class doesn't accept instance, Please pass class instead." - - if not isinstance(derived_class, type): - derived_class = derived_class.__class__ - - base_method = getattr(base_class, method) - derived_method = getattr(derived_class, method) - return derived_method != base_method - - -def has_method(obj: object, method: str) -> bool: - """Check whether the object has a method. - - Args: - method (str): The method name to check. - obj (object): The object to check. - - Returns: - bool: True if the object has the method else False. - """ - return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/parrots_jit.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/parrots_jit.py deleted file mode 100644 index 61873f6db..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/parrots_jit.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -from .parrots_wrapper import TORCH_VERSION - -parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') - -if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': - from parrots.jit import pat as jit -else: - - def jit(func=None, - check_input=None, - full_shape=True, - derivate=False, - coderize=False, - optimize=False): - - def wrapper(func): - - def wrapper_inner(*args, **kargs): - return func(*args, **kargs) - - return wrapper_inner - - if func is None: - return wrapper - else: - return func - - -if TORCH_VERSION == 'parrots': - from parrots.utils.tester import skip_no_elena -else: - - def skip_no_elena(func): - - def wrapper(*args, **kargs): - return func(*args, **kargs) - - return wrapper diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/parrots_wrapper.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/parrots_wrapper.py deleted file mode 100644 index 7e657b561..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/parrots_wrapper.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch - -TORCH_VERSION = torch.__version__ - - -def is_cuda_available() -> bool: - return torch.cuda.is_available() - - -IS_CUDA_AVAILABLE = is_cuda_available() - - -def is_rocm_pytorch() -> bool: - is_rocm = False - if TORCH_VERSION != 'parrots': - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - return is_rocm - - -def _get_cuda_home(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import CUDA_HOME - else: - if is_rocm_pytorch(): - from torch.utils.cpp_extension import ROCM_HOME - CUDA_HOME = ROCM_HOME - else: - from torch.utils.cpp_extension import CUDA_HOME - return CUDA_HOME - - -def get_build_config(): - if TORCH_VERSION == 'parrots': - from parrots.config import get_build_info - return get_build_info() - else: - return torch.__config__.show() - - -def _get_conv(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin - else: - from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin - return _ConvNd, _ConvTransposeMixin - - -def _get_dataloader(): - if TORCH_VERSION == 'parrots': - from torch.utils.data import DataLoader, PoolDataLoader - else: - from torch.utils.data import DataLoader - PoolDataLoader = DataLoader - return DataLoader, PoolDataLoader - - -def _get_extension(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import BuildExtension, Extension - CppExtension = partial(Extension, cuda=False) - CUDAExtension = partial(Extension, cuda=True) - else: - from torch.utils.cpp_extension import (BuildExtension, CppExtension, - CUDAExtension) - return BuildExtension, CppExtension, CUDAExtension - - -def _get_pool(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - else: - from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd - - -def _get_norm(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm2d - else: - from torch.nn.modules.batchnorm import _BatchNorm - from torch.nn.modules.instancenorm import _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm - return _BatchNorm, _InstanceNorm, SyncBatchNorm_ - - -_ConvNd, _ConvTransposeMixin = _get_conv() -DataLoader, PoolDataLoader = _get_dataloader() -BuildExtension, CppExtension, CUDAExtension = _get_extension() -_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() -_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() - - -class SyncBatchNorm(SyncBatchNorm_): - - def _check_input_dim(self, input): - if TORCH_VERSION == 'parrots': - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input (got {input.dim()}D input)') - else: - super()._check_input_dim(input) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/path.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/path.py deleted file mode 100644 index 568081837..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/path.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -from pathlib import Path - -from .misc import is_str - - -def is_filepath(x): - return is_str(x) or isinstance(x, Path) - - -def fopen(filepath, *args, **kwargs): - if is_str(filepath): - return open(filepath, *args, **kwargs) - elif isinstance(filepath, Path): - return filepath.open(*args, **kwargs) - raise ValueError('`filepath` should be a string or a Path') - - -def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): - if not osp.isfile(filename): - raise FileNotFoundError(msg_tmpl.format(filename)) - - -def mkdir_or_exist(dir_name, mode=0o777): - if dir_name == '': - return - dir_name = osp.expanduser(dir_name) - os.makedirs(dir_name, mode=mode, exist_ok=True) - - -def symlink(src, dst, overwrite=True, **kwargs): - if os.path.lexists(dst) and overwrite: - os.remove(dst) - os.symlink(src, dst, **kwargs) - - -def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): - """Scan a directory to find the interested files. - - Args: - dir_path (str | :obj:`Path`): Path of the directory. - suffix (str | tuple(str), optional): File suffix that we are - interested in. Default: None. - recursive (bool, optional): If set to True, recursively scan the - directory. Default: False. - case_sensitive (bool, optional) : If set to False, ignore the case of - suffix. Default: True. - - Returns: - A generator for all the interested files with relative paths. - """ - if isinstance(dir_path, (str, Path)): - dir_path = str(dir_path) - else: - raise TypeError('"dir_path" must be a string or Path object') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('"suffix" must be a string or tuple of strings') - - if suffix is not None and not case_sensitive: - suffix = suffix.lower() if isinstance(suffix, str) else tuple( - item.lower() for item in suffix) - - root = dir_path - - def _scandir(dir_path, suffix, recursive, case_sensitive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - _rel_path = rel_path if case_sensitive else rel_path.lower() - if suffix is None or _rel_path.endswith(suffix): - yield rel_path - elif recursive and os.path.isdir(entry.path): - # scan recursively if entry.path is a directory - yield from _scandir(entry.path, suffix, recursive, - case_sensitive) - - return _scandir(dir_path, suffix, recursive, case_sensitive) - - -def find_vcs_root(path, markers=('.git', )): - """Finds the root directory (including itself) of specified markers. - - Args: - path (str): Path of directory or file. - markers (list[str], optional): List of file or directory names. - - Returns: - The directory contained one of the markers or None if not found. - """ - if osp.isfile(path): - path = osp.dirname(path) - - prev, cur = None, osp.abspath(osp.expanduser(path)) - while cur != prev: - if any(osp.exists(osp.join(cur, marker)) for marker in markers): - return cur - prev, cur = cur, osp.split(cur)[0] - return None diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/progressbar.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/progressbar.py deleted file mode 100644 index 0062f670d..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/progressbar.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from collections.abc import Iterable -from multiprocessing import Pool -from shutil import get_terminal_size - -from .timer import Timer - - -class ProgressBar: - """A progress bar which can print the progress.""" - - def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): - self.task_num = task_num - self.bar_width = bar_width - self.completed = 0 - self.file = file - if start: - self.start() - - @property - def terminal_width(self): - width, _ = get_terminal_size() - return width - - def start(self): - if self.task_num > 0: - self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' - 'elapsed: 0s, ETA:') - else: - self.file.write('completed: 0, elapsed: 0s') - self.file.flush() - self.timer = Timer() - - def update(self, num_tasks=1): - assert num_tasks > 0 - self.completed += num_tasks - elapsed = self.timer.since_start() - if elapsed > 0: - fps = self.completed / elapsed - else: - fps = float('inf') - if self.task_num > 0: - percentage = self.completed / float(self.task_num) - eta = int(elapsed * (1 - percentage) / percentage + 0.5) - msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ - f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ - f'ETA: {eta:5}s' - - bar_width = min(self.bar_width, - int(self.terminal_width - len(msg)) + 2, - int(self.terminal_width * 0.6)) - bar_width = max(2, bar_width) - mark_width = int(bar_width * percentage) - bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) - self.file.write(msg.format(bar_chars)) - else: - self.file.write( - f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' - f' {fps:.1f} tasks/s') - self.file.flush() - - -def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): - """Track the progress of tasks execution with a progress bar. - - Tasks are done with a simple for-loop. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - results = [] - for task in tasks: - results.append(func(task, **kwargs)) - prog_bar.update() - prog_bar.file.write('\n') - return results - - -def init_pool(process_num, initializer=None, initargs=None): - if initializer is None: - return Pool(process_num) - elif initargs is None: - return Pool(process_num, initializer) - else: - if not isinstance(initargs, tuple): - raise TypeError('"initargs" must be a tuple') - return Pool(process_num, initializer, initargs) - - -def track_parallel_progress(func, - tasks, - nproc, - initializer=None, - initargs=None, - bar_width=50, - chunksize=1, - skip_first=False, - keep_order=True, - file=sys.stdout): - """Track the progress of parallel task execution with a progress bar. - - The built-in :mod:`multiprocessing` module is used for process pools and - tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - nproc (int): Process (worker) number. - initializer (None or callable): Refer to :class:`multiprocessing.Pool` - for details. - initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for - details. - chunksize (int): Refer to :class:`multiprocessing.Pool` for details. - bar_width (int): Width of progress bar. - skip_first (bool): Whether to skip the first sample for each worker - when estimating fps, since the initialization step may takes - longer. - keep_order (bool): If True, :func:`Pool.imap` is used, otherwise - :func:`Pool.imap_unordered` is used. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - pool = init_pool(nproc, initializer, initargs) - start = not skip_first - task_num -= nproc * chunksize * int(skip_first) - prog_bar = ProgressBar(task_num, bar_width, start, file=file) - results = [] - if keep_order: - gen = pool.imap(func, tasks, chunksize) - else: - gen = pool.imap_unordered(func, tasks, chunksize) - for result in gen: - results.append(result) - if skip_first: - if len(results) < nproc * chunksize: - continue - elif len(results) == nproc * chunksize: - prog_bar.start() - continue - prog_bar.update() - prog_bar.file.write('\n') - pool.close() - pool.join() - return results - - -def track_iter_progress(tasks, bar_width=50, file=sys.stdout): - """Track the progress of tasks iteration or enumeration with a progress - bar. - - Tasks are yielded with a simple for-loop. - - Args: - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Yields: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - for task in tasks: - yield task - prog_bar.update() - prog_bar.file.write('\n') diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/registry.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/registry.py deleted file mode 100644 index a6d92b68b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/registry.py +++ /dev/null @@ -1,337 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import warnings -from functools import partial - -from .misc import deprecated_api_warning, is_seq_of - - -def build_from_cfg(cfg, registry, default_args=None): - """Build a module from config dict when it is a class configuration, or - call a function from config dict when it is a function configuration. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = build_from_cfg(dict(type='Resnet'), MODELS) - >>> # Returns an instantiated object - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = build_from_cfg(dict(type='resnet50'), MODELS) - >>> # Return a result of the calling function - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type) or inspect.isfunction(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - try: - return obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{obj_cls.__name__}: {e}') - - -class Registry: - """A registry to map strings to classes or functions. - - Registered object could be built from registry. Meanwhile, registered - functions could be called from registry. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = MODELS.build(dict(type='ResNet')) - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = MODELS.build(dict(type='resnet50')) - - Please refer to - https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for - advanced usage. - - Args: - name (str): Registry name. - build_func(func, optional): Build function to construct instance from - Registry, func:`build_from_cfg` is used if neither ``parent`` or - ``build_func`` is specified. If ``parent`` is specified and - ``build_func`` is not given, ``build_func`` will be inherited - from ``parent``. Default: None. - parent (Registry, optional): Parent registry. The class registered in - children registry could be built from parent. Default: None. - scope (str, optional): The scope of registry. It is the key to search - for children registry. If not specified, scope will be the name of - the package where class is defined, e.g. mmdet, mmcls, mmseg. - Default: None. - """ - - def __init__(self, name, build_func=None, parent=None, scope=None): - self._name = name - self._module_dict = dict() - self._children = dict() - self._scope = self.infer_scope() if scope is None else scope - - # self.build_func will be set with the following priority: - # 1. build_func - # 2. parent.build_func - # 3. build_from_cfg - if build_func is None: - if parent is not None: - self.build_func = parent.build_func - else: - self.build_func = build_from_cfg - else: - self.build_func = build_func - if parent is not None: - assert isinstance(parent, Registry) - parent._add_children(self) - self.parent = parent - else: - self.parent = None - - def __len__(self): - return len(self._module_dict) - - def __contains__(self, key): - return self.get(key) is not None - - def __repr__(self): - format_str = self.__class__.__name__ + \ - f'(name={self._name}, ' \ - f'items={self._module_dict})' - return format_str - - @staticmethod - def infer_scope(): - """Infer the scope of registry. - - The name of the package where registry is defined will be returned. - - Example: - >>> # in mmdet/models/backbone/resnet.py - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - The scope of ``ResNet`` will be ``mmdet``. - - Returns: - str: The inferred scope name. - """ - # We access the caller using inspect.currentframe() instead of - # inspect.stack() for performance reasons. See details in PR #1844 - frame = inspect.currentframe() - # get the frame where `infer_scope()` is called - infer_scope_caller = frame.f_back.f_back - filename = inspect.getmodule(infer_scope_caller).__name__ - split_filename = filename.split('.') - return split_filename[0] - - @staticmethod - def split_scope_key(key): - """Split scope and key. - - The first scope will be split from key. - - Examples: - >>> Registry.split_scope_key('mmdet.ResNet') - 'mmdet', 'ResNet' - >>> Registry.split_scope_key('ResNet') - None, 'ResNet' - - Return: - tuple[str | None, str]: The former element is the first scope of - the key, which can be ``None``. The latter is the remaining key. - """ - split_index = key.find('.') - if split_index != -1: - return key[:split_index], key[split_index + 1:] - else: - return None, key - - @property - def name(self): - return self._name - - @property - def scope(self): - return self._scope - - @property - def module_dict(self): - return self._module_dict - - @property - def children(self): - return self._children - - def get(self, key): - """Get the registry record. - - Args: - key (str): The class name in string format. - - Returns: - class: The corresponding class. - """ - scope, real_key = self.split_scope_key(key) - if scope is None or scope == self._scope: - # get from self - if real_key in self._module_dict: - return self._module_dict[real_key] - else: - # get from self._children - if scope in self._children: - return self._children[scope].get(real_key) - else: - # goto root - parent = self.parent - while parent.parent is not None: - parent = parent.parent - return parent.get(key) - - def build(self, *args, **kwargs): - return self.build_func(*args, **kwargs, registry=self) - - def _add_children(self, registry): - """Add children for a registry. - - The ``registry`` will be added as children based on its scope. - The parent registry could build objects from children registry. - - Example: - >>> models = Registry('models') - >>> mmdet_models = Registry('models', parent=models) - >>> @mmdet_models.register_module() - >>> class ResNet: - >>> pass - >>> resnet = models.build(dict(type='mmdet.ResNet')) - """ - - assert isinstance(registry, Registry) - assert registry.scope is not None - assert registry.scope not in self.children, \ - f'scope {registry.scope} exists in {self.name} registry' - self.children[registry.scope] = registry - - @deprecated_api_warning(name_dict=dict(module_class='module')) - def _register_module(self, module, module_name=None, force=False): - if not inspect.isclass(module) and not inspect.isfunction(module): - raise TypeError('module must be a class or a function, ' - f'but got {type(module)}') - - if module_name is None: - module_name = module.__name__ - if isinstance(module_name, str): - module_name = [module_name] - for name in module_name: - if not force and name in self._module_dict: - raise KeyError(f'{name} is already registered ' - f'in {self.name}') - self._module_dict[name] = module - - def deprecated_register_module(self, cls=None, force=False): - warnings.warn( - 'The old API of register_module(module, force=False) ' - 'is deprecated and will be removed, please use the new API ' - 'register_module(name=None, force=False, module=None) instead.', - DeprecationWarning) - if cls is None: - return partial(self.deprecated_register_module, force=force) - self._register_module(cls, force=force) - return cls - - def register_module(self, name=None, force=False, module=None): - """Register a module. - - A record will be added to `self._module_dict`, whose key is the class - name or the specified name, and value is the class itself. - It can be used as a decorator or a normal function. - - Example: - >>> backbones = Registry('backbone') - >>> @backbones.register_module() - >>> class ResNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> @backbones.register_module(name='mnet') - >>> class MobileNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> class ResNet: - >>> pass - >>> backbones.register_module(ResNet) - - Args: - name (str | None): The module name to be registered. If not - specified, the class name will be used. - force (bool, optional): Whether to override an existing class with - the same name. Default: False. - module (type): Module class or function to be registered. - """ - if not isinstance(force, bool): - raise TypeError(f'force must be a boolean, but got {type(force)}') - # NOTE: This is a walkaround to be compatible with the old api, - # while it may introduce unexpected bugs. - if isinstance(name, type): - return self.deprecated_register_module(name, force=force) - - # raise the error ahead of time - if not (name is None or isinstance(name, str) or is_seq_of(name, str)): - raise TypeError( - 'name must be either of None, an instance of str or a sequence' - f' of str, but got {type(name)}') - - # use it as a normal method: x.register_module(module=SomeClass) - if module is not None: - self._register_module(module=module, module_name=name, force=force) - return module - - # use it as a decorator: @x.register_module() - def _register(module): - self._register_module(module=module, module_name=name, force=force) - return module - - return _register diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/seed.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/seed.py deleted file mode 100644 index 003f92367..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/seed.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import random - -import numpy as np -import torch - - -def worker_init_fn(worker_id: int, num_workers: int, rank: int, seed: int): - """Function to initialize each worker. - - The seed of each worker equals to - ``num_worker * rank + worker_id + user_seed``. - - Args: - worker_id (int): Id for each worker. - num_workers (int): Number of workers. - rank (int): Rank in distributed training. - seed (int): Random seed. - """ - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/testing.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/testing.py deleted file mode 100644 index 7b64e8fae..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/testing.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (c) Open-MMLab. -import sys -from collections.abc import Iterable -from runpy import run_path -from shlex import split -from typing import Any, Dict, List -from unittest.mock import patch - - -def check_python_script(cmd): - """Run the python cmd script with `__main__`. The difference between - `os.system` is that, this function exectues code in the current process, so - that it can be tracked by coverage tools. Currently it supports two forms: - - - ./tests/data/scripts/hello.py zz - - python tests/data/scripts/hello.py zz - """ - args = split(cmd) - if args[0] == 'python': - args = args[1:] - with patch.object(sys, 'argv', args): - run_path(args[0], run_name='__main__') - - -def _any(judge_result): - """Since built-in ``any`` works only when the element of iterable is not - iterable, implement the function.""" - if not isinstance(judge_result, Iterable): - return judge_result - - try: - for element in judge_result: - if _any(element): - return True - except TypeError: - # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) - if judge_result: - return True - return False - - -def assert_dict_contains_subset(dict_obj: Dict[Any, Any], - expected_subset: Dict[Any, Any]) -> bool: - """Check if the dict_obj contains the expected_subset. - - Args: - dict_obj (Dict[Any, Any]): Dict object to be checked. - expected_subset (Dict[Any, Any]): Subset expected to be contained in - dict_obj. - - Returns: - bool: Whether the dict_obj contains the expected_subset. - """ - - for key, value in expected_subset.items(): - if key not in dict_obj.keys() or _any(dict_obj[key] != value): - return False - return True - - -def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: - """Check if attribute of class object is correct. - - Args: - obj (object): Class object to be checked. - expected_attrs (Dict[str, Any]): Dict of the expected attrs. - - Returns: - bool: Whether the attribute of class object is correct. - """ - for attr, value in expected_attrs.items(): - if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): - return False - return True - - -def assert_dict_has_keys(obj: Dict[str, Any], - expected_keys: List[str]) -> bool: - """Check if the obj has all the expected_keys. - - Args: - obj (Dict[str, Any]): Object to be checked. - expected_keys (List[str]): Keys expected to contained in the keys of - the obj. - - Returns: - bool: Whether the obj has the expected keys. - """ - return set(expected_keys).issubset(set(obj.keys())) - - -def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: - """Check if target_keys is equal to result_keys. - - Args: - result_keys (List[str]): Result keys to be checked. - target_keys (List[str]): Target keys to be checked. - - Returns: - bool: Whether target_keys is equal to result_keys. - """ - return set(result_keys) == set(target_keys) - - -def assert_is_norm_layer(module) -> bool: - """Check if the module is a norm layer. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the module is a norm layer. - """ - from torch.nn import GroupNorm, LayerNorm - - from .parrots_wrapper import _BatchNorm, _InstanceNorm - norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) - return isinstance(module, norm_layer_candidates) - - -def assert_params_all_zeros(module) -> bool: - """Check if the parameters of the module is all zeros. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the parameters of the module is all zeros. - """ - weight_data = module.weight.data - is_weight_zero = weight_data.allclose( - weight_data.new_zeros(weight_data.size())) - - if hasattr(module, 'bias') and module.bias is not None: - bias_data = module.bias.data - is_bias_zero = bias_data.allclose( - bias_data.new_zeros(bias_data.size())) - else: - is_bias_zero = True - - return is_weight_zero and is_bias_zero diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/timer.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/timer.py deleted file mode 100644 index 02e96e537..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/timer.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from time import time - - -class TimerError(Exception): - - def __init__(self, message): - self.message = message - super(TimerError, self).__init__(message) - - -class Timer: - """A flexible Timer class. - - Examples: - >>> import time - >>> import mmcv - >>> with mmcv.Timer(): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - 1.000 - >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - it takes 1.0 seconds - >>> timer = mmcv.Timer() - >>> time.sleep(0.5) - >>> print(timer.since_start()) - 0.500 - >>> time.sleep(0.5) - >>> print(timer.since_last_check()) - 0.500 - >>> print(timer.since_start()) - 1.000 - """ - - def __init__(self, start=True, print_tmpl=None): - self._is_running = False - self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' - if start: - self.start() - - @property - def is_running(self): - """bool: indicate whether the timer is running""" - return self._is_running - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - print(self.print_tmpl.format(self.since_last_check())) - self._is_running = False - - def start(self): - """Start the timer.""" - if not self._is_running: - self._t_start = time() - self._is_running = True - self._t_last = time() - - def since_start(self): - """Total time since the timer is started. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - self._t_last = time() - return self._t_last - self._t_start - - def since_last_check(self): - """Time since the last checking. - - Either :func:`since_start` or :func:`since_last_check` is a checking - operation. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - dur = time() - self._t_last - self._t_last = time() - return dur - - -_g_timers = {} # global timers - - -def check_time(timer_id): - """Add check points in a single line. - - This method is suitable for running a task on a list of items. A timer will - be registered when the method is called for the first time. - - Examples: - >>> import time - >>> import mmcv - >>> for i in range(1, 6): - >>> # simulate a code block - >>> time.sleep(i) - >>> mmcv.check_time('task1') - 2.000 - 3.000 - 4.000 - 5.000 - - Args: - str: Timer identifier. - """ - if timer_id not in _g_timers: - _g_timers[timer_id] = Timer() - return 0 - else: - return _g_timers[timer_id].since_last_check() diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/trace.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/trace.py deleted file mode 100644 index 45423bd05..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/trace.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch - -from mmcv.utils import digit_version - - -def is_jit_tracing() -> bool: - if (torch.__version__ != 'parrots' - and digit_version(torch.__version__) >= digit_version('1.6.0')): - on_trace = torch.jit.is_tracing() - # In PyTorch 1.6, torch.jit.is_tracing has a bug. - # Refers to https://github.com/pytorch/pytorch/issues/42448 - if isinstance(on_trace, bool): - return on_trace - else: - return torch._C._is_tracing() - else: - warnings.warn( - 'torch.jit.is_tracing is only supported after v1.6.0. ' - 'Therefore is_tracing returns False automatically. Please ' - 'set on_trace manually if you are using trace.', UserWarning) - return False diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/version_utils.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/version_utils.py deleted file mode 100644 index 963c45a2e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/utils/version_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import subprocess -import warnings - -from packaging.version import parse - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - assert 'parrots' not in version_str - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - -def get_git_hash(fallback='unknown', digits=None): - """Get the git hash of the current repo. - - Args: - fallback (str, optional): The fallback string when git hash is - unavailable. Defaults to 'unknown'. - digits (int, optional): kept digits of the hash. Defaults to None, - meaning all digits are kept. - - Returns: - str: Git commit hash. - """ - - if digits is not None and not isinstance(digits, int): - raise TypeError('digits must be None or an integer') - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - sha = out.strip().decode('ascii') - if digits is not None: - sha = sha[:digits] - except OSError: - sha = fallback - - return sha diff --git a/cv/semantic_segmentation/unet++/pytorch/mmcv/version.py b/cv/semantic_segmentation/unet++/pytorch/mmcv/version.py deleted file mode 100644 index a97ffc2dd..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmcv/version.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -__version__ = '1.5.0' - - -def parse_version_info(version_str: str, length: int = 4) -> tuple: - """Parse a version string into a tuple. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int | str]: The version info, e.g., "1.3.0" is parsed into - (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into - (2, 0, 0, 0, 'rc', 1) (when length is set to 4). - """ - from packaging.version import parse - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - release.extend(list(version.pre)) - elif version.is_postrelease: - release.extend(list(version.post)) - else: - release.extend([0, 0]) - return tuple(release) - - -version_info = tuple(int(x) for x in __version__.split('.')[:3]) - -__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/__init__.py deleted file mode 100644 index 360abfc85..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv -from packaging.version import parse - -from .version import __version__, version_info - -MMCV_MIN = '1.3.13' -MMCV_MAX = '1.6.0' - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -mmcv_min_version = digit_version(MMCV_MIN) -mmcv_max_version = digit_version(MMCV_MAX) -mmcv_version = digit_version(mmcv.__version__) - - -assert (mmcv_min_version <= mmcv_version <= mmcv_max_version), \ - f'MMCV=={mmcv.__version__} is used but incompatible. ' \ - f'Please install mmcv>={mmcv_min_version}, <={mmcv_max_version}.' - -__all__ = ['__version__', 'version_info', 'digit_version'] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/apis/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/apis/__init__.py deleted file mode 100644 index c68818053..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/apis/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .inference import inference_segmentor, init_segmentor, show_result_pyplot -from .test import multi_gpu_test, single_gpu_test -from .train import (get_root_logger, init_random_seed, set_random_seed, - train_segmentor) - -__all__ = [ - 'get_root_logger', 'set_random_seed', 'train_segmentor', 'init_segmentor', - 'inference_segmentor', 'multi_gpu_test', 'single_gpu_test', - 'show_result_pyplot', 'init_random_seed' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/apis/inference.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/apis/inference.py deleted file mode 100644 index 906943804..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/apis/inference.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import matplotlib.pyplot as plt -import mmcv -import torch -from mmcv.parallel import collate, scatter -from mmcv.runner import load_checkpoint - -from mmseg.datasets.pipelines import Compose -from mmseg.models import build_segmentor - - -def init_segmentor(config, checkpoint=None, device='cuda:0'): - """Initialize a segmentor from config file. - - Args: - config (str or :obj:`mmcv.Config`): Config file path or the config - object. - checkpoint (str, optional): Checkpoint path. If left as None, the model - will not load any weights. - device (str, optional) CPU/CUDA device option. Default 'cuda:0'. - Use 'cpu' for loading model on CPU. - Returns: - nn.Module: The constructed segmentor. - """ - if isinstance(config, str): - config = mmcv.Config.fromfile(config) - elif not isinstance(config, mmcv.Config): - raise TypeError('config must be a filename or Config object, ' - 'but got {}'.format(type(config))) - config.model.pretrained = None - config.model.train_cfg = None - model = build_segmentor(config.model, test_cfg=config.get('test_cfg')) - if checkpoint is not None: - checkpoint = load_checkpoint(model, checkpoint, map_location='cpu') - model.CLASSES = checkpoint['meta']['CLASSES'] - model.PALETTE = checkpoint['meta']['PALETTE'] - model.cfg = config # save the config in the model for convenience - model.to(device) - model.eval() - return model - - -class LoadImage: - """A simple pipeline to load image.""" - - def __call__(self, results): - """Call function to load images into results. - - Args: - results (dict): A result dict contains the file name - of the image to be read. - - Returns: - dict: ``results`` will be returned containing loaded image. - """ - - if isinstance(results['img'], str): - results['filename'] = results['img'] - results['ori_filename'] = results['img'] - else: - results['filename'] = None - results['ori_filename'] = None - img = mmcv.imread(results['img']) - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - return results - - -def inference_segmentor(model, img): - """Inference image(s) with the segmentor. - - Args: - model (nn.Module): The loaded segmentor. - imgs (str/ndarray or list[str/ndarray]): Either image files or loaded - images. - - Returns: - (list[Tensor]): The segmentation result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # build the data pipeline - test_pipeline = [LoadImage()] + cfg.data.test.pipeline[1:] - test_pipeline = Compose(test_pipeline) - # prepare data - data = dict(img=img) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if next(model.parameters()).is_cuda: - # scatter to specified GPU - data = scatter(data, [device])[0] - else: - data['img_metas'] = [i.data[0] for i in data['img_metas']] - - # forward the model - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - return result - - -def show_result_pyplot(model, - img, - result, - palette=None, - fig_size=(15, 10), - opacity=0.5, - title='', - block=True): - """Visualize the segmentation results on the image. - - Args: - model (nn.Module): The loaded segmentor. - img (str or np.ndarray): Image filename or loaded image. - result (list): The segmentation result. - palette (list[list[int]]] | None): The palette of segmentation - map. If None is given, random palette will be generated. - Default: None - fig_size (tuple): Figure size of the pyplot figure. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - title (str): The title of pyplot figure. - Default is ''. - block (bool): Whether to block the pyplot figure. - Default is True. - """ - if hasattr(model, 'module'): - model = model.module - img = model.show_result( - img, result, palette=palette, show=False, opacity=opacity) - plt.figure(figsize=fig_size) - plt.imshow(mmcv.bgr2rgb(img)) - plt.title(title) - plt.tight_layout() - plt.show(block=block) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/apis/test.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/apis/test.py deleted file mode 100644 index cc4fcc979..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/apis/test.py +++ /dev/null @@ -1,233 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import tempfile -import warnings - -import mmcv -import numpy as np -import torch -from mmcv.engine import collect_results_cpu, collect_results_gpu -from mmcv.image import tensor2imgs -from mmcv.runner import get_dist_info - - -def np2tmp(array, temp_file_name=None, tmpdir=None): - """Save ndarray to local numpy file. - - Args: - array (ndarray): Ndarray to save. - temp_file_name (str): Numpy file name. If 'temp_file_name=None', this - function will generate a file name with tempfile.NamedTemporaryFile - to save ndarray. Default: None. - tmpdir (str): Temporary directory to save Ndarray files. Default: None. - Returns: - str: The numpy file name. - """ - - if temp_file_name is None: - temp_file_name = tempfile.NamedTemporaryFile( - suffix='.npy', delete=False, dir=tmpdir).name - np.save(temp_file_name, array) - return temp_file_name - - -def single_gpu_test(model, - data_loader, - show=False, - out_dir=None, - efficient_test=False, - opacity=0.5, - pre_eval=False, - format_only=False, - format_args={}): - """Test with single GPU by progressive mode. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - show (bool): Whether show results during inference. Default: False. - out_dir (str, optional): If specified, the results will be dumped into - the directory to save output results. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - loader_indices = data_loader.batch_sampler - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - - if show or out_dir: - img_tensor = data['img'][0] - img_metas = data['img_metas'][0].data[0] - imgs = tensor2imgs(img_tensor, **img_metas[0]['img_norm_cfg']) - assert len(imgs) == len(img_metas) - - for img, img_meta in zip(imgs, img_metas): - h, w, _ = img_meta['img_shape'] - img_show = img[:h, :w, :] - - ori_h, ori_w = img_meta['ori_shape'][:-1] - img_show = mmcv.imresize(img_show, (ori_w, ori_h)) - - if out_dir: - out_file = osp.join(out_dir, img_meta['ori_filename']) - else: - out_file = None - - model.module.show_result( - img_show, - result, - palette=dataset.PALETTE, - show=show, - out_file=out_file, - opacity=opacity) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - results.extend(result) - else: - results.extend(result) - - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - - return results - - -def multi_gpu_test(model, - data_loader, - tmpdir=None, - gpu_collect=False, - efficient_test=False, - pre_eval=False, - format_only=False, - format_args={}): - """Test model with multiple gpus by progressive mode. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' - it encodes results to gpu tensors and use gpu communication for results - collection. On cpu mode it saves the results on different gpus to 'tmpdir' - and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. The same path is used for efficient - test. Default: None. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - - # batch_sampler based on DistributedSampler, the indices only point to data - # samples of related machine. - loader_indices = data_loader.batch_sampler - - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - - results.extend(result) - - if rank == 0: - batch_size = len(result) * world_size - for _ in range(batch_size): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/apis/train.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/apis/train.py deleted file mode 100644 index 9be063785..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/apis/train.py +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import random -import warnings - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (HOOKS, DistSamplerSeedHook, EpochBasedRunner, - build_runner, get_dist_info) -from mmcv.utils import build_from_cfg - -from mmseg import digit_version -from mmseg.core import DistEvalHook, EvalHook, build_optimizer -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.utils import find_latest_checkpoint, get_root_logger - - -def init_random_seed(seed=None, device='cuda'): - """Initialize random seed. - - If the seed is not set, the seed will be automatically randomized, - and then broadcast to all processes to prevent some potential bugs. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is not None: - return seed - - # Make sure all ranks share the same random seed to prevent - # some potential bugs. Please refer to - # https://github.com/open-mmlab/mmdetection/issues/6339 - rank, world_size = get_dist_info() - seed = np.random.randint(2**31) - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() - - -def set_random_seed(seed, deterministic=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - """ - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - - -def train_segmentor(model, - dataset, - cfg, - distributed=False, - validate=False, - timestamp=None, - meta=None): - """Launch segmentor training.""" - logger = get_root_logger(cfg.log_level) - - # prepare data loaders - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - # The default loader config - loader_cfg = dict( - # cfg.gpus will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - seed=cfg.seed, - drop_last=True) - # The overall dataloader settings - loader_cfg.update({ - k: v - for k, v in cfg.data.items() if k not in [ - 'train', 'val', 'test', 'train_dataloader', 'val_dataloader', - 'test_dataloader' - ] - }) - - # The specific dataloader settings - train_loader_cfg = {**loader_cfg, **cfg.data.get('train_dataloader', {})} - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus - if distributed: - find_unused_parameters = cfg.get('find_unused_parameters', False) - # Sets the `find_unused_parameters` parameter in - # torch.nn.parallel.DistributedDataParallel - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - else: - if not torch.cuda.is_available(): - assert digit_version(mmcv.__version__) >= digit_version('1.4.4'), \ - 'Please use MMCV >= 1.4.4 for CPU training!' - model = MMDataParallel(model, device_ids=cfg.gpu_ids) - # build runner - optimizer = build_optimizer(model, cfg.optimizer) - - if cfg.get('runner') is None: - cfg.runner = {'type': 'IterBasedRunner', 'max_iters': cfg.total_iters} - warnings.warn( - 'config is now expected to have a `runner` section, ' - 'please set `runner` in your config.', UserWarning) - - runner = build_runner( - cfg.runner, - default_args=dict( - model=model, - batch_processor=None, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta)) - - runner.cfg = cfg - - # register hooks - runner.register_training_hooks(cfg.lr_config, cfg.optimizer_config, - cfg.checkpoint_config, cfg.log_config, - cfg.get('momentum_config', None)) - if distributed: - # when distributed training by epoch, using`DistSamplerSeedHook` to set - # the different seed to distributed sampler for each epoch, it will - # shuffle dataset at each epoch and avoid overfitting. - if isinstance(runner, EpochBasedRunner): - runner.register_hook(DistSamplerSeedHook()) - - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register eval hooks - if validate: - val_dataset = build_dataset(cfg.data.val, dict(test_mode=True)) - # The specific dataloader settings - val_loader_cfg = { - **loader_cfg, - 'samples_per_gpu': 1, - 'shuffle': False, # Not shuffle by default - **cfg.data.get('val_dataloader', {}), - } - val_dataloader = build_dataloader(val_dataset, **val_loader_cfg) - eval_cfg = cfg.get('evaluation', {}) - eval_cfg['by_epoch'] = cfg.runner['type'] != 'IterBasedRunner' - eval_hook = DistEvalHook if distributed else EvalHook - # In this PR (https://github.com/open-mmlab/mmcv/pull/1193), the - # priority of IterTimerHook has been modified from 'NORMAL' to 'LOW'. - runner.register_hook( - eval_hook(val_dataloader, **eval_cfg), priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from is None and cfg.get('auto_resume'): - resume_from = find_latest_checkpoint(cfg.work_dir) - if resume_from is not None: - cfg.resume_from = resume_from - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/__init__.py deleted file mode 100644 index 1a077d2f1..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, build_optimizer, - build_optimizer_constructor) -from .evaluation import * # noqa: F401, F403 -from .optimizers import * # noqa: F401, F403 -from .seg import * # noqa: F401, F403 -from .utils import * # noqa: F401, F403 - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/builder.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/builder.py deleted file mode 100644 index 406dd9b4b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/builder.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from mmcv.runner.optimizer import OPTIMIZER_BUILDERS as MMCV_OPTIMIZER_BUILDERS -from mmcv.utils import Registry, build_from_cfg - -OPTIMIZER_BUILDERS = Registry( - 'optimizer builder', parent=MMCV_OPTIMIZER_BUILDERS) - - -def build_optimizer_constructor(cfg): - constructor_type = cfg.get('type') - if constructor_type in OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - elif constructor_type in MMCV_OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, MMCV_OPTIMIZER_BUILDERS) - else: - raise KeyError(f'{constructor_type} is not registered ' - 'in the optimizer builder registry.') - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/__init__.py deleted file mode 100644 index 3d16d17e5..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .class_names import get_classes, get_palette -from .eval_hooks import DistEvalHook, EvalHook -from .metrics import (eval_metrics, intersect_and_union, mean_dice, - mean_fscore, mean_iou, pre_eval_to_metrics) - -__all__ = [ - 'EvalHook', 'DistEvalHook', 'mean_dice', 'mean_iou', 'mean_fscore', - 'eval_metrics', 'get_classes', 'get_palette', 'pre_eval_to_metrics', - 'intersect_and_union' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/class_names.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/class_names.py deleted file mode 100644 index e3bff6231..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/class_names.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - - -def cityscapes_classes(): - """Cityscapes class names for external use.""" - return [ - 'road', 'sidewalk', 'building', 'wall', 'fence', 'pole', - 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle' - ] - - -def ade_classes(): - """ADE20K class names for external use.""" - return [ - 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', - 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', - 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', - 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', - 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', - 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', - 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', - 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', - 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', - 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', - 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', - 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', - 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', - 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', - 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', - 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', - 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', - 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', - 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', - 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', - 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', - 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', - 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', - 'clock', 'flag' - ] - - -def voc_classes(): - """Pascal VOC class names for external use.""" - return [ - 'background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', - 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', - 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', - 'tvmonitor' - ] - - -def cocostuff_classes(): - """CocoStuff class names for external use.""" - return [ - 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', - 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', - 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', - 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', - 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', - 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', - 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', - 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', - 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', - 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', - 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', - 'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner', - 'blanket', 'branch', 'bridge', 'building-other', 'bush', 'cabinet', - 'cage', 'cardboard', 'carpet', 'ceiling-other', 'ceiling-tile', - 'cloth', 'clothes', 'clouds', 'counter', 'cupboard', 'curtain', - 'desk-stuff', 'dirt', 'door-stuff', 'fence', 'floor-marble', - 'floor-other', 'floor-stone', 'floor-tile', 'floor-wood', 'flower', - 'fog', 'food-other', 'fruit', 'furniture-other', 'grass', 'gravel', - 'ground-other', 'hill', 'house', 'leaves', 'light', 'mat', 'metal', - 'mirror-stuff', 'moss', 'mountain', 'mud', 'napkin', 'net', 'paper', - 'pavement', 'pillow', 'plant-other', 'plastic', 'platform', - 'playingfield', 'railing', 'railroad', 'river', 'road', 'rock', 'roof', - 'rug', 'salad', 'sand', 'sea', 'shelf', 'sky-other', 'skyscraper', - 'snow', 'solid-other', 'stairs', 'stone', 'straw', 'structural-other', - 'table', 'tent', 'textile-other', 'towel', 'tree', 'vegetable', - 'wall-brick', 'wall-concrete', 'wall-other', 'wall-panel', - 'wall-stone', 'wall-tile', 'wall-wood', 'water-other', 'waterdrops', - 'window-blind', 'window-other', 'wood' - ] - - -def loveda_classes(): - """LoveDA class names for external use.""" - return [ - 'background', 'building', 'road', 'water', 'barren', 'forest', - 'agricultural' - ] - - -def potsdam_classes(): - """Potsdam class names for external use.""" - return [ - 'impervious_surface', 'building', 'low_vegetation', 'tree', 'car', - 'clutter' - ] - - -def vaihingen_classes(): - """Vaihingen class names for external use.""" - return [ - 'impervious_surface', 'building', 'low_vegetation', 'tree', 'car', - 'clutter' - ] - - -def isaid_classes(): - """iSAID class names for external use.""" - return [ - 'background', 'ship', 'store_tank', 'baseball_diamond', 'tennis_court', - 'basketball_court', 'Ground_Track_Field', 'Bridge', 'Large_Vehicle', - 'Small_Vehicle', 'Helicopter', 'Swimming_pool', 'Roundabout', - 'Soccer_ball_field', 'plane', 'Harbor' - ] - - -def stare_classes(): - """stare class names for external use.""" - return ['background', 'vessel'] - - -def cityscapes_palette(): - """Cityscapes palette for external use.""" - return [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], - [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], - [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], - [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], [0, 80, 100], - [0, 0, 230], [119, 11, 32]] - - -def ade_palette(): - """ADE20K palette for external use.""" - return [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], - [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], - [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], - [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], - [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], - [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], - [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], - [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], - [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], - [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], - [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], - [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], - [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], - [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], - [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], - [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], - [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], - [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], - [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], - [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], - [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], - [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], - [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], - [102, 255, 0], [92, 0, 255]] - - -def voc_palette(): - """Pascal VOC palette for external use.""" - return [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], - [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], - [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], - [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], - [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] - - -def cocostuff_palette(): - """CocoStuff palette for external use.""" - return [[0, 192, 64], [0, 192, 64], [0, 64, 96], [128, 192, 192], - [0, 64, 64], [0, 192, 224], [0, 192, 192], [128, 192, 64], - [0, 192, 96], [128, 192, 64], [128, 32, 192], [0, 0, 224], - [0, 0, 64], [0, 160, 192], [128, 0, 96], [128, 0, 192], - [0, 32, 192], [128, 128, 224], [0, 0, 192], [128, 160, 192], - [128, 128, 0], [128, 0, 32], [128, 32, 0], [128, 0, 128], - [64, 128, 32], [0, 160, 0], [0, 0, 0], [192, 128, 160], [0, 32, 0], - [0, 128, 128], [64, 128, 160], [128, 160, 0], [0, 128, 0], - [192, 128, 32], [128, 96, 128], [0, 0, 128], [64, 0, 32], - [0, 224, 128], [128, 0, 0], [192, 0, 160], [0, 96, 128], - [128, 128, 128], [64, 0, 160], [128, 224, 128], [128, 128, 64], - [192, 0, 32], [128, 96, 0], [128, 0, 192], [0, 128, 32], - [64, 224, 0], [0, 0, 64], [128, 128, 160], [64, 96, 0], - [0, 128, 192], [0, 128, 160], [192, 224, 0], [0, 128, 64], - [128, 128, 32], [192, 32, 128], [0, 64, 192], [0, 0, 32], - [64, 160, 128], [128, 64, 64], [128, 0, 160], [64, 32, 128], - [128, 192, 192], [0, 0, 160], [192, 160, 128], [128, 192, 0], - [128, 0, 96], [192, 32, 0], [128, 64, 128], [64, 128, 96], - [64, 160, 0], [0, 64, 0], [192, 128, 224], [64, 32, 0], - [0, 192, 128], [64, 128, 224], [192, 160, 0], [0, 192, 0], - [192, 128, 96], [192, 96, 128], [0, 64, 128], [64, 0, 96], - [64, 224, 128], [128, 64, 0], [192, 0, 224], [64, 96, 128], - [128, 192, 128], [64, 0, 224], [192, 224, 128], [128, 192, 64], - [192, 0, 96], [192, 96, 0], [128, 64, 192], [0, 128, 96], - [0, 224, 0], [64, 64, 64], [128, 128, 224], [0, 96, 0], - [64, 192, 192], [0, 128, 224], [128, 224, 0], [64, 192, 64], - [128, 128, 96], [128, 32, 128], [64, 0, 192], [0, 64, 96], - [0, 160, 128], [192, 0, 64], [128, 64, 224], [0, 32, 128], - [192, 128, 192], [0, 64, 224], [128, 160, 128], [192, 128, 0], - [128, 64, 32], [128, 32, 64], [192, 0, 128], [64, 192, 32], - [0, 160, 64], [64, 0, 0], [192, 192, 160], [0, 32, 64], - [64, 128, 128], [64, 192, 160], [128, 160, 64], [64, 128, 0], - [192, 192, 32], [128, 96, 192], [64, 0, 128], [64, 64, 32], - [0, 224, 192], [192, 0, 0], [192, 64, 160], [0, 96, 192], - [192, 128, 128], [64, 64, 160], [128, 224, 192], [192, 128, 64], - [192, 64, 32], [128, 96, 64], [192, 0, 192], [0, 192, 32], - [64, 224, 64], [64, 0, 64], [128, 192, 160], [64, 96, 64], - [64, 128, 192], [0, 192, 160], [192, 224, 64], [64, 128, 64], - [128, 192, 32], [192, 32, 192], [64, 64, 192], [0, 64, 32], - [64, 160, 192], [192, 64, 64], [128, 64, 160], [64, 32, 192], - [192, 192, 192], [0, 64, 160], [192, 160, 192], [192, 192, 0], - [128, 64, 96], [192, 32, 64], [192, 64, 128], [64, 192, 96], - [64, 160, 64], [64, 64, 0]] - - -def loveda_palette(): - """LoveDA palette for external use.""" - return [[255, 255, 255], [255, 0, 0], [255, 255, 0], [0, 0, 255], - [159, 129, 183], [0, 255, 0], [255, 195, 128]] - - -def potsdam_palette(): - """Potsdam palette for external use.""" - return [[255, 255, 255], [0, 0, 255], [0, 255, 255], [0, 255, 0], - [255, 255, 0], [255, 0, 0]] - - -def vaihingen_palette(): - """Vaihingen palette for external use.""" - return [[255, 255, 255], [0, 0, 255], [0, 255, 255], [0, 255, 0], - [255, 255, 0], [255, 0, 0]] - - -def isaid_palette(): - """iSAID palette for external use.""" - return [[0, 0, 0], [0, 0, 63], [0, 63, 63], [0, 63, 0], [0, 63, 127], - [0, 63, 191], [0, 63, 255], [0, 127, 63], [0, 127, - 127], [0, 0, 127], - [0, 0, 191], [0, 0, 255], [0, 191, 127], [0, 127, 191], - [0, 127, 255], [0, 100, 155]] - - -def stare_palette(): - """STARE palette for external use.""" - return [[120, 120, 120], [6, 230, 230]] - - -dataset_aliases = { - 'cityscapes': ['cityscapes'], - 'ade': ['ade', 'ade20k'], - 'voc': ['voc', 'pascal_voc', 'voc12', 'voc12aug'], - 'loveda': ['loveda'], - 'potsdam': ['potsdam'], - 'vaihingen': ['vaihingen'], - 'cocostuff': [ - 'cocostuff', 'cocostuff10k', 'cocostuff164k', 'coco-stuff', - 'coco-stuff10k', 'coco-stuff164k', 'coco_stuff', 'coco_stuff10k', - 'coco_stuff164k' - ], - 'isaid': ['isaid', 'iSAID'], - 'stare': ['stare', 'STARE'] -} - - -def get_classes(dataset): - """Get class names of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_classes()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels - - -def get_palette(dataset): - """Get class palette (RGB) of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_palette()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/eval_hooks.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/eval_hooks.py deleted file mode 100644 index 952db3b0b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/eval_hooks.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -import torch.distributed as dist -from mmcv.runner import DistEvalHook as _DistEvalHook -from mmcv.runner import EvalHook as _EvalHook -from torch.nn.modules.batchnorm import _BatchNorm - - -class EvalHook(_EvalHook): - """Single GPU EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``single_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - if not self._should_evaluate(runner): - return - - from mmseg.apis import single_gpu_test - results = single_gpu_test( - runner.model, self.dataloader, show=False, pre_eval=self.pre_eval) - runner.log_buffer.clear() - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - if self.save_best: - self._save_ckpt(runner, key_score) - - -class DistEvalHook(_DistEvalHook): - """Distributed EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``multi_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - if not self._should_evaluate(runner): - return - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - from mmseg.apis import multi_gpu_test - results = multi_gpu_test( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect, - pre_eval=self.pre_eval) - - runner.log_buffer.clear() - - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - - if self.save_best: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/metrics.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/metrics.py deleted file mode 100644 index a1c0908e1..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/evaluation/metrics.py +++ /dev/null @@ -1,395 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import mmcv -import numpy as np -import torch - - -def f_score(precision, recall, beta=1): - """calculate the f-score value. - - Args: - precision (float | torch.Tensor): The precision value. - recall (float | torch.Tensor): The recall value. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - Returns: - [torch.tensor]: The f-score value. - """ - score = (1 + beta**2) * (precision * recall) / ( - (beta**2 * precision) + recall) - return score - - -def intersect_and_union(pred_label, - label, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate intersection and Union. - - Args: - pred_label (ndarray | str): Prediction segmentation map - or predict result filename. - label (ndarray | str): Ground truth segmentation map - or label filename. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. The parameter will - work only when label is str. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. The parameter will - work only when label is str. Default: False. - - Returns: - torch.Tensor: The intersection of prediction and ground truth - histogram on all classes. - torch.Tensor: The union of prediction and ground truth histogram on - all classes. - torch.Tensor: The prediction histogram on all classes. - torch.Tensor: The ground truth histogram on all classes. - """ - - if isinstance(pred_label, str): - pred_label = torch.from_numpy(np.load(pred_label)) - else: - pred_label = torch.from_numpy((pred_label)) - - if isinstance(label, str): - label = torch.from_numpy( - mmcv.imread(label, flag='unchanged', backend='pillow')) - else: - label = torch.from_numpy(label) - - if label_map is not None: - for old_id, new_id in label_map.items(): - label[label == old_id] = new_id - if reduce_zero_label: - label[label == 0] = 255 - label = label - 1 - label[label == 254] = 255 - - mask = (label != ignore_index) - pred_label = pred_label[mask] - label = label[mask] - - intersect = pred_label[pred_label == label] - area_intersect = torch.histc( - intersect.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_pred_label = torch.histc( - pred_label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_label = torch.histc( - label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_union = area_pred_label + area_label - area_intersect - return area_intersect, area_union, area_pred_label, area_label - - -def total_intersect_and_union(results, - gt_seg_maps, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate Total Intersection and Union. - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - ndarray: The intersection of prediction and ground truth histogram - on all classes. - ndarray: The union of prediction and ground truth histogram on all - classes. - ndarray: The prediction histogram on all classes. - ndarray: The ground truth histogram on all classes. - """ - total_area_intersect = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_union = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_pred_label = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_label = torch.zeros((num_classes, ), dtype=torch.float64) - for result, gt_seg_map in zip(results, gt_seg_maps): - area_intersect, area_union, area_pred_label, area_label = \ - intersect_and_union( - result, gt_seg_map, num_classes, ignore_index, - label_map, reduce_zero_label) - total_area_intersect += area_intersect - total_area_union += area_union - total_area_pred_label += area_pred_label - total_area_label += area_label - return total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label - - -def mean_iou(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category IoU, shape (num_classes, ). - """ - iou_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mIoU'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return iou_result - - -def mean_dice(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Dice (mDice) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category dice, shape (num_classes, ). - """ - - dice_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mDice'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return dice_result - - -def mean_fscore(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category recall, shape (num_classes, ). - ndarray: Per category precision, shape (num_classes, ). - ndarray: Per category f-score, shape (num_classes, ). - """ - fscore_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mFscore'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label, - beta=beta) - return fscore_result - - -def eval_metrics(results, - gt_seg_maps, - num_classes, - ignore_index, - metrics=['mIoU'], - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate evaluation metrics - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label = total_intersect_and_union( - results, gt_seg_maps, num_classes, ignore_index, label_map, - reduce_zero_label) - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def pre_eval_to_metrics(pre_eval_results, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Convert pre-eval results to metrics. - - Args: - pre_eval_results (list[tuple[torch.Tensor]]): per image eval results - for computing evaluation metric - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - # convert list of tuples to tuple of lists, e.g. - # [(A_1, B_1, C_1, D_1), ..., (A_n, B_n, C_n, D_n)] to - # ([A_1, ..., A_n], ..., [D_1, ..., D_n]) - pre_eval_results = tuple(zip(*pre_eval_results)) - assert len(pre_eval_results) == 4 - - total_area_intersect = sum(pre_eval_results[0]) - total_area_union = sum(pre_eval_results[1]) - total_area_pred_label = sum(pre_eval_results[2]) - total_area_label = sum(pre_eval_results[3]) - - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def total_area_to_metrics(total_area_intersect, - total_area_union, - total_area_pred_label, - total_area_label, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Calculate evaluation metrics - Args: - total_area_intersect (ndarray): The intersection of prediction and - ground truth histogram on all classes. - total_area_union (ndarray): The union of prediction and ground truth - histogram on all classes. - total_area_pred_label (ndarray): The prediction histogram on all - classes. - total_area_label (ndarray): The ground truth histogram on all classes. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - if isinstance(metrics, str): - metrics = [metrics] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metrics).issubset(set(allowed_metrics)): - raise KeyError('metrics {} is not supported'.format(metrics)) - - all_acc = total_area_intersect.sum() / total_area_label.sum() - ret_metrics = OrderedDict({'aAcc': all_acc}) - for metric in metrics: - if metric == 'mIoU': - iou = total_area_intersect / total_area_union - acc = total_area_intersect / total_area_label - ret_metrics['IoU'] = iou - ret_metrics['Acc'] = acc - elif metric == 'mDice': - dice = 2 * total_area_intersect / ( - total_area_pred_label + total_area_label) - acc = total_area_intersect / total_area_label - ret_metrics['Dice'] = dice - ret_metrics['Acc'] = acc - elif metric == 'mFscore': - precision = total_area_intersect / total_area_pred_label - recall = total_area_intersect / total_area_label - f_value = torch.tensor( - [f_score(x[0], x[1], beta) for x in zip(precision, recall)]) - ret_metrics['Fscore'] = f_value - ret_metrics['Precision'] = precision - ret_metrics['Recall'] = recall - - ret_metrics = { - metric: value.numpy() - for metric, value in ret_metrics.items() - } - if nan_to_num is not None: - ret_metrics = OrderedDict({ - metric: np.nan_to_num(metric_value, nan=nan_to_num) - for metric, metric_value in ret_metrics.items() - }) - return ret_metrics diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/optimizers/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/optimizers/__init__.py deleted file mode 100644 index 4fbf4ecfc..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/optimizers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .layer_decay_optimizer_constructor import ( - LayerDecayOptimizerConstructor, LearningRateDecayOptimizerConstructor) - -__all__ = [ - 'LearningRateDecayOptimizerConstructor', 'LayerDecayOptimizerConstructor' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py deleted file mode 100644 index 2b6b8ff9c..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import warnings - -from mmcv.runner import DefaultOptimizerConstructor, get_dist_info - -from mmseg.utils import get_root_logger -from ..builder import OPTIMIZER_BUILDERS - - -def get_layer_id_for_convnext(var_name, max_layer_id): - """Get the layer id to set the different learning rates in ``layer_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_layer_id (int): Maximum number of backbone layers. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - stage_id = int(var_name.split('.')[2]) - if stage_id == 0: - layer_id = 0 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - block_id = int(var_name.split('.')[3]) - if stage_id == 0: - layer_id = 1 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 + block_id // 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - else: - return max_layer_id + 1 - - -def get_stage_id_for_convnext(var_name, max_stage_id): - """Get the stage id to set the different learning rates in ``stage_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_stage_id (int): Maximum number of backbone layers. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - return 0 - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - return stage_id + 1 - else: - return max_stage_id - 1 - - -def get_layer_id_for_vit(var_name, max_layer_id): - """Get the layer id to set the different learning rates. - - Args: - var_name (str): The key of the model. - num_max_layer (int): Maximum number of backbone layers. - - Returns: - int: Returns the layer id of the key. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.patch_embed'): - return 0 - elif var_name.startswith('backbone.layers'): - layer_id = int(var_name.split('.')[2]) - return layer_id + 1 - else: - return max_layer_id - 1 - - -@OPTIMIZER_BUILDERS.register_module() -class LearningRateDecayOptimizerConstructor(DefaultOptimizerConstructor): - """Different learning rates are set for different layers of backbone. - - Note: Currently, this optimizer constructor is built for ConvNeXt, - BEiT and MAE. - """ - - def add_params(self, params, module, **kwargs): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - """ - logger = get_root_logger() - - parameter_groups = {} - logger.info(f'self.paramwise_cfg is {self.paramwise_cfg}') - num_layers = self.paramwise_cfg.get('num_layers') + 2 - decay_rate = self.paramwise_cfg.get('decay_rate') - decay_type = self.paramwise_cfg.get('decay_type', 'layer_wise') - logger.info('Build LearningRateDecayOptimizerConstructor ' - f'{decay_type} {decay_rate} - {num_layers}') - weight_decay = self.base_wd - for name, param in module.named_parameters(): - if not param.requires_grad: - continue # frozen weights - if len(param.shape) == 1 or name.endswith('.bias') or name in ( - 'pos_embed', 'cls_token'): - group_name = 'no_decay' - this_weight_decay = 0. - else: - group_name = 'decay' - this_weight_decay = weight_decay - if 'layer_wise' in decay_type: - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_convnext( - name, self.paramwise_cfg.get('num_layers')) - logger.info(f'set param {name} as id {layer_id}') - elif 'BEiT' in module.backbone.__class__.__name__ or \ - 'MAE' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_vit(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - elif decay_type == 'stage_wise': - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_stage_id_for_convnext(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - group_name = f'layer_{layer_id}_{group_name}' - - if group_name not in parameter_groups: - scale = decay_rate**(num_layers - layer_id - 1) - - parameter_groups[group_name] = { - 'weight_decay': this_weight_decay, - 'params': [], - 'param_names': [], - 'lr_scale': scale, - 'group_name': group_name, - 'lr': scale * self.base_lr, - } - - parameter_groups[group_name]['params'].append(param) - parameter_groups[group_name]['param_names'].append(name) - rank, _ = get_dist_info() - if rank == 0: - to_display = {} - for key in parameter_groups: - to_display[key] = { - 'param_names': parameter_groups[key]['param_names'], - 'lr_scale': parameter_groups[key]['lr_scale'], - 'lr': parameter_groups[key]['lr'], - 'weight_decay': parameter_groups[key]['weight_decay'], - } - logger.info(f'Param groups = {json.dumps(to_display, indent=2)}') - params.extend(parameter_groups.values()) - - -@OPTIMIZER_BUILDERS.register_module() -class LayerDecayOptimizerConstructor(LearningRateDecayOptimizerConstructor): - """Different learning rates are set for different layers of backbone. - - Note: Currently, this optimizer constructor is built for BEiT, - and it will be deprecated. - Please use ``LearningRateDecayOptimizerConstructor`` instead. - """ - - def __init__(self, optimizer_cfg, paramwise_cfg): - warnings.warn('DeprecationWarning: Original ' - 'LayerDecayOptimizerConstructor of BEiT ' - 'will be deprecated. Please use ' - 'LearningRateDecayOptimizerConstructor instead, ' - 'and set decay_type = layer_wise_vit in paramwise_cfg.') - paramwise_cfg.update({'decay_type': 'layer_wise_vit'}) - warnings.warn('DeprecationWarning: Layer_decay_rate will ' - 'be deleted, please use decay_rate instead.') - paramwise_cfg['decay_rate'] = paramwise_cfg.pop('layer_decay_rate') - super(LayerDecayOptimizerConstructor, - self).__init__(optimizer_cfg, paramwise_cfg) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/__init__.py deleted file mode 100644 index 5206b96be..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_pixel_sampler -from .sampler import BasePixelSampler, OHEMPixelSampler - -__all__ = ['build_pixel_sampler', 'BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/builder.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/builder.py deleted file mode 100644 index 1cecd347b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/builder.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, build_from_cfg - -PIXEL_SAMPLERS = Registry('pixel sampler') - - -def build_pixel_sampler(cfg, **default_args): - """Build pixel sampler for segmentation map.""" - return build_from_cfg(cfg, PIXEL_SAMPLERS, default_args) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/sampler/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/sampler/__init__.py deleted file mode 100644 index 5a7648564..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/sampler/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_pixel_sampler import BasePixelSampler -from .ohem_pixel_sampler import OHEMPixelSampler - -__all__ = ['BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py deleted file mode 100644 index 03672cd47..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BasePixelSampler(metaclass=ABCMeta): - """Base class of pixel sampler.""" - - def __init__(self, **kwargs): - pass - - @abstractmethod - def sample(self, seg_logit, seg_label): - """Placeholder for sample function.""" diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py deleted file mode 100644 index 833a28768..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import PIXEL_SAMPLERS -from .base_pixel_sampler import BasePixelSampler - - -@PIXEL_SAMPLERS.register_module() -class OHEMPixelSampler(BasePixelSampler): - """Online Hard Example Mining Sampler for segmentation. - - Args: - context (nn.Module): The context of sampler, subclass of - :obj:`BaseDecodeHead`. - thresh (float, optional): The threshold for hard example selection. - Below which, are prediction with low confidence. If not - specified, the hard examples will be pixels of top ``min_kept`` - loss. Default: None. - min_kept (int, optional): The minimum number of predictions to keep. - Default: 100000. - """ - - def __init__(self, context, thresh=None, min_kept=100000): - super(OHEMPixelSampler, self).__init__() - self.context = context - assert min_kept > 1 - self.thresh = thresh - self.min_kept = min_kept - - def sample(self, seg_logit, seg_label): - """Sample pixels that have high loss or with low prediction confidence. - - Args: - seg_logit (torch.Tensor): segmentation logits, shape (N, C, H, W) - seg_label (torch.Tensor): segmentation label, shape (N, 1, H, W) - - Returns: - torch.Tensor: segmentation weight, shape (N, H, W) - """ - with torch.no_grad(): - assert seg_logit.shape[2:] == seg_label.shape[2:] - assert seg_label.shape[1] == 1 - seg_label = seg_label.squeeze(1).long() - batch_kept = self.min_kept * seg_label.size(0) - valid_mask = seg_label != self.context.ignore_index - seg_weight = seg_logit.new_zeros(size=seg_label.size()) - valid_seg_weight = seg_weight[valid_mask] - if self.thresh is not None: - seg_prob = F.softmax(seg_logit, dim=1) - - tmp_seg_label = seg_label.clone().unsqueeze(1) - tmp_seg_label[tmp_seg_label == self.context.ignore_index] = 0 - seg_prob = seg_prob.gather(1, tmp_seg_label).squeeze(1) - sort_prob, sort_indices = seg_prob[valid_mask].sort() - - if sort_prob.numel() > 0: - min_threshold = sort_prob[min(batch_kept, - sort_prob.numel() - 1)] - else: - min_threshold = 0.0 - threshold = max(min_threshold, self.thresh) - valid_seg_weight[seg_prob[valid_mask] < threshold] = 1. - else: - if not isinstance(self.context.loss_decode, nn.ModuleList): - losses_decode = [self.context.loss_decode] - else: - losses_decode = self.context.loss_decode - losses = 0.0 - for loss_module in losses_decode: - losses += loss_module( - seg_logit, - seg_label, - weight=None, - ignore_index=self.context.ignore_index, - reduction_override='none') - - # faster than topk according to https://github.com/pytorch/pytorch/issues/22812 # noqa - _, sort_indices = losses[valid_mask].sort(descending=True) - valid_seg_weight[sort_indices[:batch_kept]] = 1. - - seg_weight[valid_mask] = valid_seg_weight - - return seg_weight diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/utils/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/utils/__init__.py deleted file mode 100644 index 28882893a..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/utils/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .dist_util import check_dist_init, sync_random_seed -from .misc import add_prefix - -__all__ = ['add_prefix', 'check_dist_init', 'sync_random_seed'] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/utils/dist_util.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/utils/dist_util.py deleted file mode 100644 index b3288519d..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/utils/dist_util.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def check_dist_init(): - return dist.is_available() and dist.is_initialized() - - -def sync_random_seed(seed=None, device='cuda'): - """Make sure different ranks share the same seed. All workers must call - this function, otherwise it will deadlock. This method is generally used in - `DistributedSampler`, because the seed should be identical across all - processes in the distributed group. - - In distributed sampling, different ranks should sample non-overlapped - data in the dataset. Therefore, this function is used to make sure that - each rank shuffles the data indices in the same order based - on the same seed. Then different ranks could use different indices - to select non-overlapped data from the same data list. - - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - - if seed is None: - seed = np.random.randint(2**31) - assert isinstance(seed, int) - - rank, world_size = get_dist_info() - - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/utils/misc.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/core/utils/misc.py deleted file mode 100644 index 282bb8d96..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/core/utils/misc.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def add_prefix(inputs, prefix): - """Add prefix for dict. - - Args: - inputs (dict): The input dict with str keys. - prefix (str): The prefix to add. - - Returns: - - dict: The dict with keys updated with ``prefix``. - """ - - outputs = dict() - for name, value in inputs.items(): - outputs[f'{prefix}.{name}'] = value - - return outputs diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/__init__.py deleted file mode 100644 index e3b6a4300..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import DATASETS, PIPELINES, build_dataloader, build_dataset -from .custom import CustomDataset -from .dataset_wrappers import (ConcatDataset, MultiImageMixDataset, - RepeatDataset) -from .drive import DRIVEDataset diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/builder.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/builder.py deleted file mode 100644 index 4d852d365..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/builder.py +++ /dev/null @@ -1,191 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import platform -import random -from functools import partial - -import numpy as np -import torch -from mmcv.parallel import collate -from mmcv.runner import get_dist_info -from mmcv.utils import Registry, build_from_cfg, digit_version -from torch.utils.data import DataLoader - -from .samplers import DistributedSampler - -if platform.system() != 'Windows': - # https://github.com/pytorch/pytorch/issues/973 - import resource - rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) - base_soft_limit = rlimit[0] - hard_limit = rlimit[1] - soft_limit = min(max(4096, base_soft_limit), hard_limit) - resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) - -DATASETS = Registry('dataset') -PIPELINES = Registry('pipeline') - - -def _concat_dataset(cfg, default_args=None): - """Build :obj:`ConcatDataset by.""" - from .dataset_wrappers import ConcatDataset - img_dir = cfg['img_dir'] - ann_dir = cfg.get('ann_dir', None) - split = cfg.get('split', None) - # pop 'separate_eval' since it is not a valid key for common datasets. - separate_eval = cfg.pop('separate_eval', True) - num_img_dir = len(img_dir) if isinstance(img_dir, (list, tuple)) else 1 - if ann_dir is not None: - num_ann_dir = len(ann_dir) if isinstance(ann_dir, (list, tuple)) else 1 - else: - num_ann_dir = 0 - if split is not None: - num_split = len(split) if isinstance(split, (list, tuple)) else 1 - else: - num_split = 0 - if num_img_dir > 1: - assert num_img_dir == num_ann_dir or num_ann_dir == 0 - assert num_img_dir == num_split or num_split == 0 - else: - assert num_split == num_ann_dir or num_ann_dir <= 1 - num_dset = max(num_split, num_img_dir) - - datasets = [] - for i in range(num_dset): - data_cfg = copy.deepcopy(cfg) - if isinstance(img_dir, (list, tuple)): - data_cfg['img_dir'] = img_dir[i] - if isinstance(ann_dir, (list, tuple)): - data_cfg['ann_dir'] = ann_dir[i] - if isinstance(split, (list, tuple)): - data_cfg['split'] = split[i] - datasets.append(build_dataset(data_cfg, default_args)) - - return ConcatDataset(datasets, separate_eval) - - -def build_dataset(cfg, default_args=None): - """Build datasets.""" - from .dataset_wrappers import (ConcatDataset, MultiImageMixDataset, - RepeatDataset) - if isinstance(cfg, (list, tuple)): - dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) - elif cfg['type'] == 'RepeatDataset': - dataset = RepeatDataset( - build_dataset(cfg['dataset'], default_args), cfg['times']) - elif cfg['type'] == 'MultiImageMixDataset': - cp_cfg = copy.deepcopy(cfg) - cp_cfg['dataset'] = build_dataset(cp_cfg['dataset']) - cp_cfg.pop('type') - dataset = MultiImageMixDataset(**cp_cfg) - elif isinstance(cfg.get('img_dir'), (list, tuple)) or isinstance( - cfg.get('split', None), (list, tuple)): - dataset = _concat_dataset(cfg, default_args) - else: - dataset = build_from_cfg(cfg, DATASETS, default_args) - - return dataset - - -def build_dataloader(dataset, - samples_per_gpu, - workers_per_gpu, - num_gpus=1, - dist=True, - shuffle=True, - seed=None, - drop_last=False, - pin_memory=True, - persistent_workers=True, - **kwargs): - """Build PyTorch DataLoader. - - In distributed training, each GPU/process has a dataloader. - In non-distributed training, there is only one dataloader for all GPUs. - - Args: - dataset (Dataset): A PyTorch dataset. - samples_per_gpu (int): Number of training samples on each GPU, i.e., - batch size of each GPU. - workers_per_gpu (int): How many subprocesses to use for data loading - for each GPU. - num_gpus (int): Number of GPUs. Only used in non-distributed training. - dist (bool): Distributed training/test or not. Default: True. - shuffle (bool): Whether to shuffle the data at every epoch. - Default: True. - seed (int | None): Seed to be used. Default: None. - drop_last (bool): Whether to drop the last incomplete batch in epoch. - Default: False - pin_memory (bool): Whether to use pin_memory in DataLoader. - Default: True - persistent_workers (bool): If True, the data loader will not shutdown - the worker processes after a dataset has been consumed once. - This allows to maintain the workers Dataset instances alive. - The argument also has effect in PyTorch>=1.7.0. - Default: True - kwargs: any keyword argument to be used to initialize DataLoader - - Returns: - DataLoader: A PyTorch dataloader. - """ - rank, world_size = get_dist_info() - if dist: - sampler = DistributedSampler( - dataset, world_size, rank, shuffle=shuffle, seed=seed) - shuffle = False - batch_size = samples_per_gpu - num_workers = workers_per_gpu - else: - sampler = None - batch_size = num_gpus * samples_per_gpu - num_workers = num_gpus * workers_per_gpu - - init_fn = partial( - worker_init_fn, num_workers=num_workers, rank=rank, - seed=seed) if seed is not None else None - - if digit_version(torch.__version__) >= digit_version('1.8.0'): - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - persistent_workers=persistent_workers, - **kwargs) - else: - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - **kwargs) - - return data_loader - - -def worker_init_fn(worker_id, num_workers, rank, seed): - """Worker init func for dataloader. - - The seed of each worker equals to num_worker * rank + worker_id + user_seed - - Args: - worker_id (int): Worker id. - num_workers (int): Number of workers. - rank (int): The rank of current process. - seed (int): The random seed to use. - """ - - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/custom.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/custom.py deleted file mode 100644 index 4615d4114..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/custom.py +++ /dev/null @@ -1,487 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from collections import OrderedDict - -import mmcv -import numpy as np -from mmcv.utils import print_log -from prettytable import PrettyTable -from torch.utils.data import Dataset - -from mmseg.core import eval_metrics, intersect_and_union, pre_eval_to_metrics -from mmseg.utils import get_root_logger -from .builder import DATASETS -from .pipelines import Compose, LoadAnnotations - - -@DATASETS.register_module() -class CustomDataset(Dataset): - """Custom dataset for semantic segmentation. An example of file structure - is as followed. - - .. code-block:: none - - ├── data - │ ├── my_dataset - │ │ ├── img_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{img_suffix} - │ │ │ │ ├── yyy{img_suffix} - │ │ │ │ ├── zzz{img_suffix} - │ │ │ ├── val - │ │ ├── ann_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{seg_map_suffix} - │ │ │ │ ├── yyy{seg_map_suffix} - │ │ │ │ ├── zzz{seg_map_suffix} - │ │ │ ├── val - - The img/gt_semantic_seg pair of CustomDataset should be of the same - except suffix. A valid img/gt_semantic_seg filename pair should be like - ``xxx{img_suffix}`` and ``xxx{seg_map_suffix}`` (extension is also included - in the suffix). If split is given, then ``xxx`` is specified in txt file. - Otherwise, all files in ``img_dir/``and ``ann_dir`` will be loaded. - Please refer to ``docs/en/tutorials/new_dataset.md`` for more details. - - - Args: - pipeline (list[dict]): Processing pipeline - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. Default: '.jpg' - ann_dir (str, optional): Path to annotation directory. Default: None - seg_map_suffix (str): Suffix of segmentation maps. Default: '.png' - split (str, optional): Split txt file. If split is specified, only - file with suffix in the splits will be loaded. Otherwise, all - images in img_dir/ann_dir will be loaded. Default: None - data_root (str, optional): Data root for img_dir/ann_dir. Default: - None. - test_mode (bool): If test_mode=True, gt wouldn't be loaded. - ignore_index (int): The label index to be ignored. Default: 255 - reduce_zero_label (bool): Whether to mark label zero as ignored. - Default: False - classes (str | Sequence[str], optional): Specify classes to load. - If is None, ``cls.CLASSES`` will be used. Default: None. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, and - self.PALETTE is None, random palette will be generated. - Default: None - gt_seg_map_loader_cfg (dict, optional): build LoadAnnotations to - load gt for evaluation, load from disk by default. Default: None. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - CLASSES = None - - PALETTE = None - - def __init__(self, - pipeline, - img_dir, - img_suffix='.jpg', - ann_dir=None, - seg_map_suffix='.png', - split=None, - data_root=None, - test_mode=False, - ignore_index=255, - reduce_zero_label=False, - classes=None, - palette=None, - gt_seg_map_loader_cfg=None, - file_client_args=dict(backend='disk')): - self.pipeline = Compose(pipeline) - self.img_dir = img_dir - self.img_suffix = img_suffix - self.ann_dir = ann_dir - self.seg_map_suffix = seg_map_suffix - self.split = split - self.data_root = data_root - self.test_mode = test_mode - self.ignore_index = ignore_index - self.reduce_zero_label = reduce_zero_label - self.label_map = None - self.CLASSES, self.PALETTE = self.get_classes_and_palette( - classes, palette) - self.gt_seg_map_loader = LoadAnnotations( - ) if gt_seg_map_loader_cfg is None else LoadAnnotations( - **gt_seg_map_loader_cfg) - - self.file_client_args = file_client_args - self.file_client = mmcv.FileClient.infer_client(self.file_client_args) - - if test_mode: - assert self.CLASSES is not None, \ - '`cls.CLASSES` or `classes` should be specified when testing' - - # join paths if data_root is specified - if self.data_root is not None: - if not osp.isabs(self.img_dir): - self.img_dir = osp.join(self.data_root, self.img_dir) - if not (self.ann_dir is None or osp.isabs(self.ann_dir)): - self.ann_dir = osp.join(self.data_root, self.ann_dir) - if not (self.split is None or osp.isabs(self.split)): - self.split = osp.join(self.data_root, self.split) - - # load annotations - self.img_infos = self.load_annotations(self.img_dir, self.img_suffix, - self.ann_dir, - self.seg_map_suffix, self.split) - - def __len__(self): - """Total number of samples of data.""" - return len(self.img_infos) - - def load_annotations(self, img_dir, img_suffix, ann_dir, seg_map_suffix, - split): - """Load annotation from directory. - - Args: - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. - ann_dir (str|None): Path to annotation directory. - seg_map_suffix (str|None): Suffix of segmentation maps. - split (str|None): Split txt file. If split is specified, only file - with suffix in the splits will be loaded. Otherwise, all images - in img_dir/ann_dir will be loaded. Default: None - - Returns: - list[dict]: All image info of dataset. - """ - - img_infos = [] - if split is not None: - lines = mmcv.list_from_file( - split, file_client_args=self.file_client_args) - for line in lines: - img_name = line.strip() - img_info = dict(filename=img_name + img_suffix) - if ann_dir is not None: - seg_map = img_name + seg_map_suffix - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - else: - for img in self.file_client.list_dir_or_file( - dir_path=img_dir, - list_dir=False, - suffix=img_suffix, - recursive=True): - img_info = dict(filename=img) - if ann_dir is not None: - seg_map = img.replace(img_suffix, seg_map_suffix) - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - img_infos = sorted(img_infos, key=lambda x: x['filename']) - - print_log(f'Loaded {len(img_infos)} images', logger=get_root_logger()) - return img_infos - - def get_ann_info(self, idx): - """Get annotation by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - return self.img_infos[idx]['ann'] - - def pre_pipeline(self, results): - """Prepare results dict for pipeline.""" - results['seg_fields'] = [] - results['img_prefix'] = self.img_dir - results['seg_prefix'] = self.ann_dir - if self.custom_classes: - results['label_map'] = self.label_map - - def __getitem__(self, idx): - """Get training/test data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training/test data (with annotation if `test_mode` is set - False). - """ - - if self.test_mode: - return self.prepare_test_img(idx) - else: - return self.prepare_train_img(idx) - - def prepare_train_img(self, idx): - """Get training data and annotations after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training data and annotation after pipeline with new keys - introduced by pipeline. - """ - - img_info = self.img_infos[idx] - ann_info = self.get_ann_info(idx) - results = dict(img_info=img_info, ann_info=ann_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def prepare_test_img(self, idx): - """Get testing data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Testing data after pipeline with new keys introduced by - pipeline. - """ - - img_info = self.img_infos[idx] - results = dict(img_info=img_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """Place holder to format result to dataset specific output.""" - raise NotImplementedError - - def get_gt_seg_map_by_idx(self, index): - """Get one ground truth segmentation map for evaluation.""" - ann_info = self.get_ann_info(index) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - return results['gt_semantic_seg'] - - def get_gt_seg_maps(self, efficient_test=None): - """Get ground truth segmentation maps for evaluation.""" - if efficient_test is not None: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` has been deprecated ' - 'since MMSeg v0.16, the ``get_gt_seg_maps()`` is CPU memory ' - 'friendly by default. ') - - for idx in range(len(self)): - ann_info = self.get_ann_info(idx) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - yield results['gt_semantic_seg'] - - def pre_eval(self, preds, indices): - """Collect eval result from each iteration. - - Args: - preds (list[torch.Tensor] | torch.Tensor): the segmentation logit - after argmax, shape (N, H, W). - indices (list[int] | int): the prediction related ground truth - indices. - - Returns: - list[torch.Tensor]: (area_intersect, area_union, area_prediction, - area_ground_truth). - """ - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - - pre_eval_results = [] - - for pred, index in zip(preds, indices): - seg_map = self.get_gt_seg_map_by_idx(index) - pre_eval_results.append( - intersect_and_union( - pred, - seg_map, - len(self.CLASSES), - self.ignore_index, - # as the labels has been converted when dataset initialized - # in `get_palette_for_custom_classes ` this `label_map` - # should be `dict()`, see - # https://github.com/open-mmlab/mmsegmentation/issues/1415 - # for more ditails - label_map=dict(), - reduce_zero_label=self.reduce_zero_label)) - - return pre_eval_results - - def get_classes_and_palette(self, classes=None, palette=None): - """Get class names of current dataset. - - Args: - classes (Sequence[str] | str | None): If classes is None, use - default CLASSES defined by builtin dataset. If classes is a - string, take it as a file name. The file contains the name of - classes where each line contains one class name. If classes is - a tuple or list, override the CLASSES defined by the dataset. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, random - palette will be generated. Default: None - """ - if classes is None: - self.custom_classes = False - return self.CLASSES, self.PALETTE - - self.custom_classes = True - if isinstance(classes, str): - # take it as a file path - class_names = mmcv.list_from_file(classes) - elif isinstance(classes, (tuple, list)): - class_names = classes - else: - raise ValueError(f'Unsupported type {type(classes)} of classes.') - - if self.CLASSES: - if not set(class_names).issubset(self.CLASSES): - raise ValueError('classes is not a subset of CLASSES.') - - # dictionary, its keys are the old label ids and its values - # are the new label ids. - # used for changing pixel labels in load_annotations. - self.label_map = {} - for i, c in enumerate(self.CLASSES): - if c not in class_names: - self.label_map[i] = -1 - else: - self.label_map[i] = class_names.index(c) - - palette = self.get_palette_for_custom_classes(class_names, palette) - - return class_names, palette - - def get_palette_for_custom_classes(self, class_names, palette=None): - - if self.label_map is not None: - # return subset of palette - palette = [] - for old_id, new_id in sorted( - self.label_map.items(), key=lambda x: x[1]): - if new_id != -1: - palette.append(self.PALETTE[old_id]) - palette = type(self.PALETTE)(palette) - - elif palette is None: - if self.PALETTE is None: - # Get random state before set seed, and restore - # random state later. - # It will prevent loss of randomness, as the palette - # may be different in each iteration if not specified. - # See: https://github.com/open-mmlab/mmdetection/issues/5844 - state = np.random.get_state() - np.random.seed(42) - # random palette - palette = np.random.randint(0, 255, size=(len(class_names), 3)) - np.random.set_state(state) - else: - palette = self.PALETTE - - return palette - - def evaluate(self, - results, - metric='mIoU', - logger=None, - gt_seg_maps=None, - **kwargs): - """Evaluate the dataset. - - Args: - results (list[tuple[torch.Tensor]] | list[str]): per image pre_eval - results or predict segmentation map for computing evaluation - metric. - metric (str | list[str]): Metrics to be evaluated. 'mIoU', - 'mDice' and 'mFscore' are supported. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - gt_seg_maps (generator[ndarray]): Custom gt seg maps as input, - used in ConcatDataset - - Returns: - dict[str, float]: Default metrics. - """ - if isinstance(metric, str): - metric = [metric] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metric).issubset(set(allowed_metrics)): - raise KeyError('metric {} is not supported'.format(metric)) - - eval_results = {} - # test a list of files - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - if gt_seg_maps is None: - gt_seg_maps = self.get_gt_seg_maps() - num_classes = len(self.CLASSES) - ret_metrics = eval_metrics( - results, - gt_seg_maps, - num_classes, - self.ignore_index, - metric, - label_map=dict(), - reduce_zero_label=self.reduce_zero_label) - # test a list of pre_eval_results - else: - ret_metrics = pre_eval_to_metrics(results, metric) - - # Because dataset.CLASSES is required for per-eval. - if self.CLASSES is None: - class_names = tuple(range(num_classes)) - else: - class_names = self.CLASSES - - # summary table - ret_metrics_summary = OrderedDict({ - ret_metric: np.round(np.nanmean(ret_metric_value) * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - - # each class table - ret_metrics.pop('aAcc', None) - ret_metrics_class = OrderedDict({ - ret_metric: np.round(ret_metric_value * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - ret_metrics_class.update({'Class': class_names}) - ret_metrics_class.move_to_end('Class', last=False) - - # for logger - class_table_data = PrettyTable() - for key, val in ret_metrics_class.items(): - class_table_data.add_column(key, val) - - summary_table_data = PrettyTable() - for key, val in ret_metrics_summary.items(): - if key == 'aAcc': - summary_table_data.add_column(key, [val]) - else: - summary_table_data.add_column('m' + key, [val]) - - print_log('per class results:', logger) - print_log('\n' + class_table_data.get_string(), logger=logger) - print_log('Summary:', logger) - print_log('\n' + summary_table_data.get_string(), logger=logger) - - # each metric dict - for key, value in ret_metrics_summary.items(): - if key == 'aAcc': - eval_results[key] = value / 100.0 - else: - eval_results['m' + key] = value / 100.0 - - ret_metrics_class.pop('Class', None) - for key, value in ret_metrics_class.items(): - eval_results.update({ - key + '.' + str(name): value[idx] / 100.0 - for idx, name in enumerate(class_names) - }) - - return eval_results diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/dataset_wrappers.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/dataset_wrappers.py deleted file mode 100644 index d226c3eaa..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/dataset_wrappers.py +++ /dev/null @@ -1,276 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import bisect -import collections -import copy -from itertools import chain - -import mmcv -import numpy as np -from mmcv.utils import build_from_cfg, print_log -from torch.utils.data.dataset import ConcatDataset as _ConcatDataset - -from .builder import DATASETS, PIPELINES - - -@DATASETS.register_module() -class ConcatDataset(_ConcatDataset): - """A wrapper of concatenated dataset. - - Same as :obj:`torch.utils.data.dataset.ConcatDataset`, but - support evaluation and formatting results - - Args: - datasets (list[:obj:`Dataset`]): A list of datasets. - separate_eval (bool): Whether to evaluate the concatenated - dataset results separately, Defaults to True. - """ - - def __init__(self, datasets, separate_eval=True): - super(ConcatDataset, self).__init__(datasets) - self.CLASSES = datasets[0].CLASSES - self.PALETTE = datasets[0].PALETTE - self.separate_eval = separate_eval - assert separate_eval in [True, False], \ - f'separate_eval can only be True or False,' \ - f'but get {separate_eval}' - if any([isinstance(ds, CityscapesDataset) for ds in datasets]): - raise NotImplementedError( - 'Evaluating ConcatDataset containing CityscapesDataset' - 'is not supported!') - - def evaluate(self, results, logger=None, **kwargs): - """Evaluate the results. - - Args: - results (list[tuple[torch.Tensor]] | list[str]]): per image - pre_eval results or predict segmentation map for - computing evaluation metric. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - - Returns: - dict[str: float]: evaluate results of the total dataset - or each separate - dataset if `self.separate_eval=True`. - """ - assert len(results) == self.cumulative_sizes[-1], \ - ('Dataset and results have different sizes: ' - f'{self.cumulative_sizes[-1]} v.s. {len(results)}') - - # Check whether all the datasets support evaluation - for dataset in self.datasets: - assert hasattr(dataset, 'evaluate'), \ - f'{type(dataset)} does not implement evaluate function' - - if self.separate_eval: - dataset_idx = -1 - total_eval_results = dict() - for size, dataset in zip(self.cumulative_sizes, self.datasets): - start_idx = 0 if dataset_idx == -1 else \ - self.cumulative_sizes[dataset_idx] - end_idx = self.cumulative_sizes[dataset_idx + 1] - - results_per_dataset = results[start_idx:end_idx] - print_log( - f'\nEvaluateing {dataset.img_dir} with ' - f'{len(results_per_dataset)} images now', - logger=logger) - - eval_results_per_dataset = dataset.evaluate( - results_per_dataset, logger=logger, **kwargs) - dataset_idx += 1 - for k, v in eval_results_per_dataset.items(): - total_eval_results.update({f'{dataset_idx}_{k}': v}) - - return total_eval_results - - if len(set([type(ds) for ds in self.datasets])) != 1: - raise NotImplementedError( - 'All the datasets should have same types when ' - 'self.separate_eval=False') - else: - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - # merge the generators of gt_seg_maps - gt_seg_maps = chain( - *[dataset.get_gt_seg_maps() for dataset in self.datasets]) - else: - # if the results are `pre_eval` results, - # we do not need gt_seg_maps to evaluate - gt_seg_maps = None - eval_results = self.datasets[0].evaluate( - results, gt_seg_maps=gt_seg_maps, logger=logger, **kwargs) - return eval_results - - def get_dataset_idx_and_sample_idx(self, indice): - """Return dataset and sample index when given an indice of - ConcatDataset. - - Args: - indice (int): indice of sample in ConcatDataset - - Returns: - int: the index of sub dataset the sample belong to - int: the index of sample in its corresponding subset - """ - if indice < 0: - if -indice > len(self): - raise ValueError( - 'absolute value of index should not exceed dataset length') - indice = len(self) + indice - dataset_idx = bisect.bisect_right(self.cumulative_sizes, indice) - if dataset_idx == 0: - sample_idx = indice - else: - sample_idx = indice - self.cumulative_sizes[dataset_idx - 1] - return dataset_idx, sample_idx - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """format result for every sample of ConcatDataset.""" - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].format_results( - [results[i]], - imgfile_prefix + f'/{dataset_idx}', - indices=[sample_idx], - **kwargs) - ret_res.append(res) - return sum(ret_res, []) - - def pre_eval(self, preds, indices): - """do pre eval for every sample of ConcatDataset.""" - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].pre_eval(preds[i], sample_idx) - ret_res.append(res) - return sum(ret_res, []) - - -@DATASETS.register_module() -class RepeatDataset(object): - """A wrapper of repeated dataset. - - The length of repeated dataset will be `times` larger than the original - dataset. This is useful when the data loading time is long but the dataset - is small. Using RepeatDataset can reduce the data loading time between - epochs. - - Args: - dataset (:obj:`Dataset`): The dataset to be repeated. - times (int): Repeat times. - """ - - def __init__(self, dataset, times): - self.dataset = dataset - self.times = times - self.CLASSES = dataset.CLASSES - self.PALETTE = dataset.PALETTE - self._ori_len = len(self.dataset) - - def __getitem__(self, idx): - """Get item from original dataset.""" - return self.dataset[idx % self._ori_len] - - def __len__(self): - """The length is multiplied by ``times``""" - return self.times * self._ori_len - - -@DATASETS.register_module() -class MultiImageMixDataset: - """A wrapper of multiple images mixed dataset. - - Suitable for training on multiple images mixed data augmentation like - mosaic and mixup. For the augmentation pipeline of mixed image data, - the `get_indexes` method needs to be provided to obtain the image - indexes, and you can set `skip_flags` to change the pipeline running - process. - - - Args: - dataset (:obj:`CustomDataset`): The dataset to be mixed. - pipeline (Sequence[dict]): Sequence of transform object or - config dict to be composed. - skip_type_keys (list[str], optional): Sequence of type string to - be skip pipeline. Default to None. - """ - - def __init__(self, dataset, pipeline, skip_type_keys=None): - assert isinstance(pipeline, collections.abc.Sequence) - if skip_type_keys is not None: - assert all([ - isinstance(skip_type_key, str) - for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys - - self.pipeline = [] - self.pipeline_types = [] - for transform in pipeline: - if isinstance(transform, dict): - self.pipeline_types.append(transform['type']) - transform = build_from_cfg(transform, PIPELINES) - self.pipeline.append(transform) - else: - raise TypeError('pipeline must be a dict') - - self.dataset = dataset - self.CLASSES = dataset.CLASSES - self.PALETTE = dataset.PALETTE - self.num_samples = len(dataset) - - def __len__(self): - return self.num_samples - - def __getitem__(self, idx): - results = copy.deepcopy(self.dataset[idx]) - for (transform, transform_type) in zip(self.pipeline, - self.pipeline_types): - if self._skip_type_keys is not None and \ - transform_type in self._skip_type_keys: - continue - - if hasattr(transform, 'get_indexes'): - indexes = transform.get_indexes(self.dataset) - if not isinstance(indexes, collections.abc.Sequence): - indexes = [indexes] - mix_results = [ - copy.deepcopy(self.dataset[index]) for index in indexes - ] - results['mix_results'] = mix_results - - results = transform(results) - - if 'mix_results' in results: - results.pop('mix_results') - - return results - - def update_skip_type_keys(self, skip_type_keys): - """Update skip_type_keys. - - It is called by an external hook. - - Args: - skip_type_keys (list[str], optional): Sequence of type - string to be skip pipeline. - """ - assert all([ - isinstance(skip_type_key, str) for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/drive.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/drive.py deleted file mode 100644 index c4ced7426..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/drive.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class DRIVEDataset(CustomDataset): - """DRIVE dataset. - - In segmentation map annotation for DRIVE, 0 stands for background, which is - included in 2 categories. ``reduce_zero_label`` is fixed to False. The - ``img_suffix`` is fixed to '.png' and ``seg_map_suffix`` is fixed to - '_manual1.png'. - """ - - CLASSES = ('background', 'vessel') - - PALETTE = [[120, 120, 120], [6, 230, 230]] - - def __init__(self, **kwargs): - super(DRIVEDataset, self).__init__( - img_suffix='.png', - seg_map_suffix='_manual1.png', - reduce_zero_label=False, - **kwargs) - assert self.file_client.exists(self.img_dir) \ No newline at end of file diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/__init__.py deleted file mode 100644 index 5ffd71ba1..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .compose import Compose -from .formatting import (Collect, ImageToTensor, ToDataContainer, ToTensor, - Transpose, to_tensor) -from .loading import LoadAnnotations, LoadImageFromFile -from .test_time_aug import MultiScaleFlipAug -from .transforms import (CLAHE, AdjustGamma, Normalize, Pad, - PhotoMetricDistortion, RandomCrop, RandomCutOut, - RandomFlip, RandomMosaic, RandomRotate, Rerange, - Resize, RGB2Gray, SegRescale, CenterCrop) - -__all__ = [ - 'Compose', 'to_tensor', 'ToTensor', 'ImageToTensor', 'ToDataContainer', - 'Transpose', 'Collect', 'LoadAnnotations', 'LoadImageFromFile', - 'MultiScaleFlipAug', 'Resize', 'RandomFlip', 'Pad', 'RandomCrop', - 'Normalize', 'SegRescale', 'PhotoMetricDistortion', 'RandomRotate', - 'AdjustGamma', 'CLAHE', 'Rerange', 'RGB2Gray', 'RandomCutOut', - 'RandomMosaic', 'CenterCrop' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/compose.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/compose.py deleted file mode 100644 index 30280c133..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/compose.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections - -from mmcv.utils import build_from_cfg - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class Compose(object): - """Compose multiple transforms sequentially. - - Args: - transforms (Sequence[dict | callable]): Sequence of transform object or - config dict to be composed. - """ - - def __init__(self, transforms): - assert isinstance(transforms, collections.abc.Sequence) - self.transforms = [] - for transform in transforms: - if isinstance(transform, dict): - transform = build_from_cfg(transform, PIPELINES) - self.transforms.append(transform) - elif callable(transform): - self.transforms.append(transform) - else: - raise TypeError('transform must be callable or a dict') - - def __call__(self, data): - """Call function to apply transforms sequentially. - - Args: - data (dict): A result dict contains the data to transform. - - Returns: - dict: Transformed data. - """ - - for t in self.transforms: - data = t(data) - if data is None: - return None - return data - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - format_string += '\n' - format_string += f' {t}' - format_string += '\n)' - return format_string diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/formating.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/formating.py deleted file mode 100644 index f6e53bfeb..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/formating.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -import warnings - -from .formatting import * - -warnings.warn('DeprecationWarning: mmseg.datasets.pipelines.formating will be ' - 'deprecated in 2021, please replace it with ' - 'mmseg.datasets.pipelines.formatting.') diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/formatting.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/formatting.py deleted file mode 100644 index 4e057c1b8..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/formatting.py +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import mmcv -import numpy as np -import torch -from mmcv.parallel import DataContainer as DC - -from ..builder import PIPELINES - - -def to_tensor(data): - """Convert objects of various python types to :obj:`torch.Tensor`. - - Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, - :class:`Sequence`, :class:`int` and :class:`float`. - - Args: - data (torch.Tensor | numpy.ndarray | Sequence | int | float): Data to - be converted. - """ - - if isinstance(data, torch.Tensor): - return data - elif isinstance(data, np.ndarray): - return torch.from_numpy(data) - elif isinstance(data, Sequence) and not mmcv.is_str(data): - return torch.tensor(data) - elif isinstance(data, int): - return torch.LongTensor([data]) - elif isinstance(data, float): - return torch.FloatTensor([data]) - else: - raise TypeError(f'type {type(data)} cannot be converted to tensor.') - - -@PIPELINES.register_module() -class ToTensor(object): - """Convert some results to :obj:`torch.Tensor` by given keys. - - Args: - keys (Sequence[str]): Keys that need to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert data in results to :obj:`torch.Tensor`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted - to :obj:`torch.Tensor`. - """ - - for key in self.keys: - results[key] = to_tensor(results[key]) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class ImageToTensor(object): - """Convert image to :obj:`torch.Tensor` by given keys. - - The dimension order of input image is (H, W, C). The pipeline will convert - it to (C, H, W). If only 2 dimension (H, W) is given, the output would be - (1, H, W). - - Args: - keys (Sequence[str]): Key of images to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - img = results[key] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - results[key] = to_tensor(img.transpose(2, 0, 1)) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class Transpose(object): - """Transpose some results by given keys. - - Args: - keys (Sequence[str]): Keys of results to be transposed. - order (Sequence[int]): Order of transpose. - """ - - def __init__(self, keys, order): - self.keys = keys - self.order = order - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - results[key] = results[key].transpose(self.order) - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, order={self.order})' - - -@PIPELINES.register_module() -class ToDataContainer(object): - """Convert results to :obj:`mmcv.DataContainer` by given fields. - - Args: - fields (Sequence[dict]): Each field is a dict like - ``dict(key='xxx', **kwargs)``. The ``key`` in result will - be converted to :obj:`mmcv.DataContainer` with ``**kwargs``. - Default: ``(dict(key='img', stack=True), - dict(key='gt_semantic_seg'))``. - """ - - def __init__(self, - fields=(dict(key='img', - stack=True), dict(key='gt_semantic_seg'))): - self.fields = fields - - def __call__(self, results): - """Call function to convert data in results to - :obj:`mmcv.DataContainer`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted to - :obj:`mmcv.DataContainer`. - """ - - for field in self.fields: - field = field.copy() - key = field.pop('key') - results[key] = DC(results[key], **field) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(fields={self.fields})' - - -@PIPELINES.register_module() -class DefaultFormatBundle(object): - """Default formatting bundle. - - It simplifies the pipeline of formatting common fields, including "img" - and "gt_semantic_seg". These fields are formatted as follows. - - - img: (1)transpose, (2)to tensor, (3)to DataContainer (stack=True) - - gt_semantic_seg: (1)unsqueeze dim-0 (2)to tensor, - (3)to DataContainer (stack=True) - """ - - def __call__(self, results): - """Call function to transform and format common fields in results. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data that is formatted with - default bundle. - """ - - if 'img' in results: - img = results['img'] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - img = np.ascontiguousarray(img.transpose(2, 0, 1)) - results['img'] = DC(to_tensor(img), stack=True) - if 'gt_semantic_seg' in results: - # convert to long - results['gt_semantic_seg'] = DC( - to_tensor(results['gt_semantic_seg'][None, - ...].astype(np.int64)), - stack=True) - return results - - def __repr__(self): - return self.__class__.__name__ - - -@PIPELINES.register_module() -class Collect(object): - """Collect data from the loader relevant to the specific task. - - This is usually the last stage of the data loader pipeline. Typically keys - is set to some subset of "img", "gt_semantic_seg". - - The "img_meta" item is always populated. The contents of the "img_meta" - dictionary depends on "meta_keys". By default this includes: - - - "img_shape": shape of the image input to the network as a tuple - (h, w, c). Note that images may be zero padded on the bottom/right - if the batch tensor is larger than this shape. - - - "scale_factor": a float indicating the preprocessing scale - - - "flip": a boolean indicating if image flip transform was used - - - "filename": path to the image file - - - "ori_shape": original shape of the image as a tuple (h, w, c) - - - "pad_shape": image shape after padding - - - "img_norm_cfg": a dict of normalization information: - - mean - per channel mean subtraction - - std - per channel std divisor - - to_rgb - bool indicating if bgr was converted to rgb - - Args: - keys (Sequence[str]): Keys of results to be collected in ``data``. - meta_keys (Sequence[str], optional): Meta keys to be converted to - ``mmcv.DataContainer`` and collected in ``data[img_metas]``. - Default: (``filename``, ``ori_filename``, ``ori_shape``, - ``img_shape``, ``pad_shape``, ``scale_factor``, ``flip``, - ``flip_direction``, ``img_norm_cfg``) - """ - - def __init__(self, - keys, - meta_keys=('filename', 'ori_filename', 'ori_shape', - 'img_shape', 'pad_shape', 'scale_factor', 'flip', - 'flip_direction', 'img_norm_cfg')): - self.keys = keys - self.meta_keys = meta_keys - - def __call__(self, results): - """Call function to collect keys in results. The keys in ``meta_keys`` - will be converted to :obj:mmcv.DataContainer. - - Args: - results (dict): Result dict contains the data to collect. - - Returns: - dict: The result dict contains the following keys - - keys in``self.keys`` - - ``img_metas`` - """ - - data = {} - img_meta = {} - for key in self.meta_keys: - img_meta[key] = results[key] - data['img_metas'] = DC(img_meta, cpu_only=True) - for key in self.keys: - data[key] = results[key] - return data - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, meta_keys={self.meta_keys})' diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/loading.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/loading.py deleted file mode 100644 index 572e43431..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/loading.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class LoadImageFromFile(object): - """Load an image from file. - - Required keys are "img_prefix" and "img_info" (a dict that must contain the - key "filename"). Added or updated keys are "filename", "img", "img_shape", - "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`), - "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1). - - Args: - to_float32 (bool): Whether to convert the loaded image to a float32 - numpy array. If set to False, the loaded image is an uint8 array. - Defaults to False. - color_type (str): The flag argument for :func:`mmcv.imfrombytes`. - Defaults to 'color'. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'cv2' - """ - - def __init__(self, - to_float32=False, - color_type='color', - file_client_args=dict(backend='disk'), - imdecode_backend='cv2'): - self.to_float32 = to_float32 - self.color_type = color_type - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call functions to load image and get image meta information. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded image and meta information. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('img_prefix') is not None: - filename = osp.join(results['img_prefix'], - results['img_info']['filename']) - else: - filename = results['img_info']['filename'] - img_bytes = self.file_client.get(filename) - img = mmcv.imfrombytes( - img_bytes, flag=self.color_type, backend=self.imdecode_backend) - if self.to_float32: - img = img.astype(np.float32) - - results['filename'] = filename - results['ori_filename'] = results['img_info']['filename'] - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - # Set initial values for default meta_keys - results['pad_shape'] = img.shape - results['scale_factor'] = 1.0 - num_channels = 1 if len(img.shape) < 3 else img.shape[2] - results['img_norm_cfg'] = dict( - mean=np.zeros(num_channels, dtype=np.float32), - std=np.ones(num_channels, dtype=np.float32), - to_rgb=False) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(to_float32={self.to_float32},' - repr_str += f"color_type='{self.color_type}'," - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str - - -@PIPELINES.register_module() -class LoadAnnotations(object): - """Load annotations for semantic segmentation. - - Args: - reduce_zero_label (bool): Whether reduce all label value by 1. - Usually used for datasets where 0 is background label. - Default: False. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'pillow' - """ - - def __init__(self, - reduce_zero_label=False, - file_client_args=dict(backend='disk'), - imdecode_backend='pillow'): - self.reduce_zero_label = reduce_zero_label - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call function to load multiple types annotations. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded semantic segmentation annotations. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('seg_prefix', None) is not None: - filename = osp.join(results['seg_prefix'], - results['ann_info']['seg_map']) - else: - filename = results['ann_info']['seg_map'] - img_bytes = self.file_client.get(filename) - gt_semantic_seg = mmcv.imfrombytes( - img_bytes, flag='unchanged', - backend=self.imdecode_backend).squeeze().astype(np.uint8) - # modify if custom classes - if results.get('label_map', None) is not None: - # Add deep copy to solve bug of repeatedly - # replace `gt_semantic_seg`, which is reported in - # https://github.com/open-mmlab/mmsegmentation/pull/1445/ - gt_semantic_seg_copy = gt_semantic_seg.copy() - for old_id, new_id in results['label_map'].items(): - gt_semantic_seg[gt_semantic_seg_copy == old_id] = new_id - # reduce zero_label - if self.reduce_zero_label: - # avoid using underflow conversion - gt_semantic_seg[gt_semantic_seg == 0] = 255 - gt_semantic_seg = gt_semantic_seg - 1 - gt_semantic_seg[gt_semantic_seg == 254] = 255 - results['gt_semantic_seg'] = gt_semantic_seg - results['seg_fields'].append('gt_semantic_seg') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(reduce_zero_label={self.reduce_zero_label},' - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/test_time_aug.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/test_time_aug.py deleted file mode 100644 index 5c17cbbba..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/test_time_aug.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv - -from ..builder import PIPELINES -from .compose import Compose - - -@PIPELINES.register_module() -class MultiScaleFlipAug(object): - """Test-time augmentation with multiple scales and flipping. - - An example configuration is as followed: - - .. code-block:: - - img_scale=(2048, 1024), - img_ratios=[0.5, 1.0], - flip=True, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ] - - After MultiScaleFLipAug with above configuration, the results are wrapped - into lists of the same length as followed: - - .. code-block:: - - dict( - img=[...], - img_shape=[...], - scale=[(1024, 512), (1024, 512), (2048, 1024), (2048, 1024)] - flip=[False, True, False, True] - ... - ) - - Args: - transforms (list[dict]): Transforms to apply in each augmentation. - img_scale (None | tuple | list[tuple]): Images scales for resizing. - img_ratios (float | list[float]): Image ratios for resizing - flip (bool): Whether apply flip augmentation. Default: False. - flip_direction (str | list[str]): Flip augmentation directions, - options are "horizontal" and "vertical". If flip_direction is list, - multiple flip augmentations will be applied. - It has no effect when flip == False. Default: "horizontal". - """ - - def __init__(self, - transforms, - img_scale, - img_ratios=None, - flip=False, - flip_direction='horizontal'): - self.transforms = Compose(transforms) - if img_ratios is not None: - img_ratios = img_ratios if isinstance(img_ratios, - list) else [img_ratios] - assert mmcv.is_list_of(img_ratios, float) - if img_scale is None: - # mode 1: given img_scale=None and a range of image ratio - self.img_scale = None - assert mmcv.is_list_of(img_ratios, float) - elif isinstance(img_scale, tuple) and mmcv.is_list_of( - img_ratios, float): - assert len(img_scale) == 2 - # mode 2: given a scale and a range of image ratio - self.img_scale = [(int(img_scale[0] * ratio), - int(img_scale[1] * ratio)) - for ratio in img_ratios] - else: - # mode 3: given multiple scales - self.img_scale = img_scale if isinstance(img_scale, - list) else [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) or self.img_scale is None - self.flip = flip - self.img_ratios = img_ratios - self.flip_direction = flip_direction if isinstance( - flip_direction, list) else [flip_direction] - assert mmcv.is_list_of(self.flip_direction, str) - if not self.flip and self.flip_direction != ['horizontal']: - warnings.warn( - 'flip_direction has no effect when flip is set to False') - if (self.flip - and not any([t['type'] == 'RandomFlip' for t in transforms])): - warnings.warn( - 'flip has no effect when RandomFlip is not in transforms') - - def __call__(self, results): - """Call function to apply test time augment transforms on results. - - Args: - results (dict): Result dict contains the data to transform. - - Returns: - dict[str: list]: The augmented data, where each value is wrapped - into a list. - """ - - aug_data = [] - if self.img_scale is None and mmcv.is_list_of(self.img_ratios, float): - h, w = results['img'].shape[:2] - img_scale = [(int(w * ratio), int(h * ratio)) - for ratio in self.img_ratios] - else: - img_scale = self.img_scale - flip_aug = [False, True] if self.flip else [False] - for scale in img_scale: - for flip in flip_aug: - for direction in self.flip_direction: - _results = results.copy() - _results['scale'] = scale - _results['flip'] = flip - _results['flip_direction'] = direction - data = self.transforms(_results) - aug_data.append(data) - # list of dict to dict of list - aug_data_dict = {key: [] for key in aug_data[0]} - for data in aug_data: - for key, val in data.items(): - aug_data_dict[key].append(val) - return aug_data_dict - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(transforms={self.transforms}, ' - repr_str += f'img_scale={self.img_scale}, flip={self.flip})' - repr_str += f'flip_direction={self.flip_direction}' - return repr_str diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/transforms.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/transforms.py deleted file mode 100644 index 56b50f337..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/pipelines/transforms.py +++ /dev/null @@ -1,1385 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import copy - -import mmcv -import numpy as np -from mmcv.utils import deprecated_api_warning, is_tuple_of -from numpy import random - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class ResizeToMultiple(object): - """Resize images & seg to multiple of divisor. - - Args: - size_divisor (int): images and gt seg maps need to resize to multiple - of size_divisor. Default: 32. - interpolation (str, optional): The interpolation mode of image resize. - Default: None - """ - - def __init__(self, size_divisor=32, interpolation=None): - self.size_divisor = size_divisor - self.interpolation = interpolation - - def __call__(self, results): - """Call function to resize images, semantic segmentation map to - multiple of size divisor. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape' keys are updated. - """ - # Align image to multiple of size divisor. - img = results['img'] - img = mmcv.imresize_to_multiple( - img, - self.size_divisor, - scale_factor=1, - interpolation=self.interpolation - if self.interpolation else 'bilinear') - - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape - - # Align segmentation map to multiple of size divisor. - for key in results.get('seg_fields', []): - gt_seg = results[key] - gt_seg = mmcv.imresize_to_multiple( - gt_seg, - self.size_divisor, - scale_factor=1, - interpolation='nearest') - results[key] = gt_seg - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(size_divisor={self.size_divisor}, ' - f'interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class Resize(object): - """Resize images & seg. - - This transform resizes the input image to some scale. If the input dict - contains the key "scale", then the scale in the input dict is used, - otherwise the specified scale in the init method is used. - - ``img_scale`` can be None, a tuple (single-scale) or a list of tuple - (multi-scale). There are 4 multiscale modes: - - - ``ratio_range is not None``: - 1. When img_scale is None, img_scale is the shape of image in results - (img_scale = results['img'].shape[:2]) and the image is resized based - on the original size. (mode 1) - 2. When img_scale is a tuple (single-scale), randomly sample a ratio from - the ratio range and multiply it with the image scale. (mode 2) - - - ``ratio_range is None and multiscale_mode == "range"``: randomly sample a - scale from the a range. (mode 3) - - - ``ratio_range is None and multiscale_mode == "value"``: randomly sample a - scale from multiple scales. (mode 4) - - Args: - img_scale (tuple or list[tuple]): Images scales for resizing. - Default:None. - multiscale_mode (str): Either "range" or "value". - Default: 'range' - ratio_range (tuple[float]): (min_ratio, max_ratio). - Default: None - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: True - min_size (int, optional): The minimum size for input and the shape - of the image and seg map will not be less than ``min_size``. - As the shape of model input is fixed like 'SETR' and 'BEiT'. - Following the setting in these models, resized images must be - bigger than the crop size in ``slide_inference``. Default: None - """ - - def __init__(self, - img_scale=None, - multiscale_mode='range', - ratio_range=None, - keep_ratio=True, - min_size=None): - if img_scale is None: - self.img_scale = None - else: - if isinstance(img_scale, list): - self.img_scale = img_scale - else: - self.img_scale = [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) - - if ratio_range is not None: - # mode 1: given img_scale=None and a range of image ratio - # mode 2: given a scale and a range of image ratio - assert self.img_scale is None or len(self.img_scale) == 1 - else: - # mode 3 and 4: given multiple scales or a range of scales - assert multiscale_mode in ['value', 'range'] - - self.multiscale_mode = multiscale_mode - self.ratio_range = ratio_range - self.keep_ratio = keep_ratio - self.min_size = min_size - - @staticmethod - def random_select(img_scales): - """Randomly select an img_scale from given candidates. - - Args: - img_scales (list[tuple]): Images scales for selection. - - Returns: - (tuple, int): Returns a tuple ``(img_scale, scale_dix)``, - where ``img_scale`` is the selected image scale and - ``scale_idx`` is the selected index in the given candidates. - """ - - assert mmcv.is_list_of(img_scales, tuple) - scale_idx = np.random.randint(len(img_scales)) - img_scale = img_scales[scale_idx] - return img_scale, scale_idx - - @staticmethod - def random_sample(img_scales): - """Randomly sample an img_scale when ``multiscale_mode=='range'``. - - Args: - img_scales (list[tuple]): Images scale range for sampling. - There must be two tuples in img_scales, which specify the lower - and upper bound of image scales. - - Returns: - (tuple, None): Returns a tuple ``(img_scale, None)``, where - ``img_scale`` is sampled scale and None is just a placeholder - to be consistent with :func:`random_select`. - """ - - assert mmcv.is_list_of(img_scales, tuple) and len(img_scales) == 2 - img_scale_long = [max(s) for s in img_scales] - img_scale_short = [min(s) for s in img_scales] - long_edge = np.random.randint( - min(img_scale_long), - max(img_scale_long) + 1) - short_edge = np.random.randint( - min(img_scale_short), - max(img_scale_short) + 1) - img_scale = (long_edge, short_edge) - return img_scale, None - - @staticmethod - def random_sample_ratio(img_scale, ratio_range): - """Randomly sample an img_scale when ``ratio_range`` is specified. - - A ratio will be randomly sampled from the range specified by - ``ratio_range``. Then it would be multiplied with ``img_scale`` to - generate sampled scale. - - Args: - img_scale (tuple): Images scale base to multiply with ratio. - ratio_range (tuple[float]): The minimum and maximum ratio to scale - the ``img_scale``. - - Returns: - (tuple, None): Returns a tuple ``(scale, None)``, where - ``scale`` is sampled ratio multiplied with ``img_scale`` and - None is just a placeholder to be consistent with - :func:`random_select`. - """ - - assert isinstance(img_scale, tuple) and len(img_scale) == 2 - min_ratio, max_ratio = ratio_range - assert min_ratio <= max_ratio - ratio = np.random.random_sample() * (max_ratio - min_ratio) + min_ratio - scale = int(img_scale[0] * ratio), int(img_scale[1] * ratio) - return scale, None - - def _random_scale(self, results): - """Randomly sample an img_scale according to ``ratio_range`` and - ``multiscale_mode``. - - If ``ratio_range`` is specified, a ratio will be sampled and be - multiplied with ``img_scale``. - If multiple scales are specified by ``img_scale``, a scale will be - sampled according to ``multiscale_mode``. - Otherwise, single scale will be used. - - Args: - results (dict): Result dict from :obj:`dataset`. - - Returns: - dict: Two new keys 'scale` and 'scale_idx` are added into - ``results``, which would be used by subsequent pipelines. - """ - - if self.ratio_range is not None: - if self.img_scale is None: - h, w = results['img'].shape[:2] - scale, scale_idx = self.random_sample_ratio((w, h), - self.ratio_range) - else: - scale, scale_idx = self.random_sample_ratio( - self.img_scale[0], self.ratio_range) - elif len(self.img_scale) == 1: - scale, scale_idx = self.img_scale[0], 0 - elif self.multiscale_mode == 'range': - scale, scale_idx = self.random_sample(self.img_scale) - elif self.multiscale_mode == 'value': - scale, scale_idx = self.random_select(self.img_scale) - else: - raise NotImplementedError - - results['scale'] = scale - results['scale_idx'] = scale_idx - - def _resize_img(self, results): - """Resize images with ``results['scale']``.""" - if self.keep_ratio: - if self.min_size is not None: - # TODO: Now 'min_size' is an 'int' which means the minimum - # shape of images is (min_size, min_size, 3). 'min_size' - # with tuple type will be supported, i.e. the width and - # height are not equal. - if min(results['scale']) < self.min_size: - new_short = self.min_size - else: - new_short = min(results['scale']) - - h, w = results['img'].shape[:2] - if h > w: - new_h, new_w = new_short * h / w, new_short - else: - new_h, new_w = new_short, new_short * w / h - results['scale'] = (new_h, new_w) - - img, scale_factor = mmcv.imrescale( - results['img'], results['scale'], return_scale=True) - # the w_scale and h_scale has minor difference - # a real fix should be done in the mmcv.imrescale in the future - new_h, new_w = img.shape[:2] - h, w = results['img'].shape[:2] - w_scale = new_w / w - h_scale = new_h / h - else: - img, w_scale, h_scale = mmcv.imresize( - results['img'], results['scale'], return_scale=True) - scale_factor = np.array([w_scale, h_scale, w_scale, h_scale], - dtype=np.float32) - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape # in case that there is no padding - results['scale_factor'] = scale_factor - results['keep_ratio'] = self.keep_ratio - - def _resize_seg(self, results): - """Resize semantic segmentation map with ``results['scale']``.""" - for key in results.get('seg_fields', []): - if self.keep_ratio: - gt_seg = mmcv.imrescale( - results[key], results['scale'], interpolation='nearest') - else: - gt_seg = mmcv.imresize( - results[key], results['scale'], interpolation='nearest') - results[key] = gt_seg - - def __call__(self, results): - """Call function to resize images, bounding boxes, masks, semantic - segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape', 'scale_factor', - 'keep_ratio' keys are added into result dict. - """ - - if 'scale' not in results: - self._random_scale(results) - self._resize_img(results) - self._resize_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(img_scale={self.img_scale}, ' - f'multiscale_mode={self.multiscale_mode}, ' - f'ratio_range={self.ratio_range}, ' - f'keep_ratio={self.keep_ratio})') - return repr_str - - -@PIPELINES.register_module() -class RandomFlip(object): - """Flip the image & seg. - - If the input dict contains the key "flip", then the flag will be used, - otherwise it will be randomly decided by a ratio specified in the init - method. - - Args: - prob (float, optional): The flipping probability. Default: None. - direction(str, optional): The flipping direction. Options are - 'horizontal' and 'vertical'. Default: 'horizontal'. - """ - - @deprecated_api_warning({'flip_ratio': 'prob'}, cls_name='RandomFlip') - def __init__(self, prob=None, direction='horizontal'): - self.prob = prob - self.direction = direction - if prob is not None: - assert prob >= 0 and prob <= 1 - assert direction in ['horizontal', 'vertical'] - - def __call__(self, results): - """Call function to flip bounding boxes, masks, semantic segmentation - maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Flipped results, 'flip', 'flip_direction' keys are added into - result dict. - """ - - if 'flip' not in results: - flip = True if np.random.rand() < self.prob else False - results['flip'] = flip - if 'flip_direction' not in results: - results['flip_direction'] = self.direction - if results['flip']: - # flip image - results['img'] = mmcv.imflip( - results['img'], direction=results['flip_direction']) - - # flip segs - for key in results.get('seg_fields', []): - # use copy() to make numpy stride positive - results[key] = mmcv.imflip( - results[key], direction=results['flip_direction']).copy() - return results - - def __repr__(self): - return self.__class__.__name__ + f'(prob={self.prob})' - - -@PIPELINES.register_module() -class Pad(object): - """Pad the image & mask. - - There are two padding modes: (1) pad to a fixed size and (2) pad to the - minimum size that is divisible by some number. - Added keys are "pad_shape", "pad_fixed_size", "pad_size_divisor", - - Args: - size (tuple, optional): Fixed padding size. - size_divisor (int, optional): The divisor of padded size. - pad_val (float, optional): Padding value. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - """ - - def __init__(self, - size=None, - size_divisor=None, - pad_val=0, - seg_pad_val=255): - self.size = size - self.size_divisor = size_divisor - self.pad_val = pad_val - self.seg_pad_val = seg_pad_val - # only one of size and size_divisor should be valid - assert size is not None or size_divisor is not None - assert size is None or size_divisor is None - - def _pad_img(self, results): - """Pad images according to ``self.size``.""" - if self.size is not None: - padded_img = mmcv.impad( - results['img'], shape=self.size, pad_val=self.pad_val) - elif self.size_divisor is not None: - padded_img = mmcv.impad_to_multiple( - results['img'], self.size_divisor, pad_val=self.pad_val) - results['img'] = padded_img - results['pad_shape'] = padded_img.shape - results['pad_fixed_size'] = self.size - results['pad_size_divisor'] = self.size_divisor - - def _pad_seg(self, results): - """Pad masks according to ``results['pad_shape']``.""" - for key in results.get('seg_fields', []): - results[key] = mmcv.impad( - results[key], - shape=results['pad_shape'][:2], - pad_val=self.seg_pad_val) - - def __call__(self, results): - """Call function to pad images, masks, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Updated result dict. - """ - - self._pad_img(results) - self._pad_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(size={self.size}, size_divisor={self.size_divisor}, ' \ - f'pad_val={self.pad_val})' - return repr_str - - -@PIPELINES.register_module() -class Normalize(object): - """Normalize the image. - - Added key is "img_norm_cfg". - - Args: - mean (sequence): Mean values of 3 channels. - std (sequence): Std values of 3 channels. - to_rgb (bool): Whether to convert the image from BGR to RGB, - default is true. - """ - - def __init__(self, mean, std, to_rgb=True): - self.mean = np.array(mean, dtype=np.float32) - self.std = np.array(std, dtype=np.float32) - self.to_rgb = to_rgb - - def __call__(self, results): - """Call function to normalize images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Normalized results, 'img_norm_cfg' key is added into - result dict. - """ - - results['img'] = mmcv.imnormalize(results['img'], self.mean, self.std, - self.to_rgb) - results['img_norm_cfg'] = dict( - mean=self.mean, std=self.std, to_rgb=self.to_rgb) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(mean={self.mean}, std={self.std}, to_rgb=' \ - f'{self.to_rgb})' - return repr_str - - -@PIPELINES.register_module() -class Rerange(object): - """Rerange the image pixel value. - - Args: - min_value (float or int): Minimum value of the reranged image. - Default: 0. - max_value (float or int): Maximum value of the reranged image. - Default: 255. - """ - - def __init__(self, min_value=0, max_value=255): - assert isinstance(min_value, float) or isinstance(min_value, int) - assert isinstance(max_value, float) or isinstance(max_value, int) - assert min_value < max_value - self.min_value = min_value - self.max_value = max_value - - def __call__(self, results): - """Call function to rerange images. - - Args: - results (dict): Result dict from loading pipeline. - Returns: - dict: Reranged results. - """ - - img = results['img'] - img_min_value = np.min(img) - img_max_value = np.max(img) - - assert img_min_value < img_max_value - # rerange to [0, 1] - img = (img - img_min_value) / (img_max_value - img_min_value) - # rerange to [min_value, max_value] - img = img * (self.max_value - self.min_value) + self.min_value - results['img'] = img - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(min_value={self.min_value}, max_value={self.max_value})' - return repr_str - - -@PIPELINES.register_module() -class CLAHE(object): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - """ - - def __init__(self, clip_limit=40.0, tile_grid_size=(8, 8)): - assert isinstance(clip_limit, (float, int)) - self.clip_limit = clip_limit - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - self.tile_grid_size = tile_grid_size - - def __call__(self, results): - """Call function to Use CLAHE method process images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - for i in range(results['img'].shape[2]): - results['img'][:, :, i] = mmcv.clahe( - np.array(results['img'][:, :, i], dtype=np.uint8), - self.clip_limit, self.tile_grid_size) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(clip_limit={self.clip_limit}, '\ - f'tile_grid_size={self.tile_grid_size})' - return repr_str - - -@PIPELINES.register_module() -class RandomCrop(object): - """Random crop the image & seg. - - Args: - crop_size (tuple): Expected size after cropping, (h, w). - cat_max_ratio (float): The maximum ratio that single category could - occupy. - """ - - def __init__(self, crop_size, cat_max_ratio=1., ignore_index=255): - assert crop_size[0] > 0 and crop_size[1] > 0 - self.crop_size = crop_size - self.cat_max_ratio = cat_max_ratio - self.ignore_index = ignore_index - - def get_crop_bbox(self, img): - """Randomly get a crop bounding box.""" - margin_h = max(img.shape[0] - self.crop_size[0], 0) - margin_w = max(img.shape[1] - self.crop_size[1], 0) - offset_h = np.random.randint(0, margin_h + 1) - offset_w = np.random.randint(0, margin_w + 1) - crop_y1, crop_y2 = offset_h, offset_h + self.crop_size[0] - crop_x1, crop_x2 = offset_w, offset_w + self.crop_size[1] - - return crop_y1, crop_y2, crop_x1, crop_x2 - - def crop(self, img, crop_bbox): - """Crop from ``img``""" - crop_y1, crop_y2, crop_x1, crop_x2 = crop_bbox - img = img[crop_y1:crop_y2, crop_x1:crop_x2, ...] - return img - - def __call__(self, results): - """Call function to randomly crop images, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Randomly cropped results, 'img_shape' key in result dict is - updated according to crop size. - """ - - img = results['img'] - crop_bbox = self.get_crop_bbox(img) - if self.cat_max_ratio < 1.: - # Repeat 10 times - for _ in range(10): - seg_temp = self.crop(results['gt_semantic_seg'], crop_bbox) - labels, cnt = np.unique(seg_temp, return_counts=True) - cnt = cnt[labels != self.ignore_index] - if len(cnt) > 1 and np.max(cnt) / np.sum( - cnt) < self.cat_max_ratio: - break - crop_bbox = self.get_crop_bbox(img) - - # crop the image - img = self.crop(img, crop_bbox) - img_shape = img.shape - results['img'] = img - results['img_shape'] = img_shape - - # crop semantic seg - for key in results.get('seg_fields', []): - results[key] = self.crop(results[key], crop_bbox) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(crop_size={self.crop_size})' - -@PIPELINES.register_module() -class CenterCrop(object): - """Center crop the image & seg. - - Args: - crop_size (tuple): Expected size after cropping, (h, w). - """ - - def __init__(self, crop_size, ignore_index=255): - assert crop_size[0] > 0 and crop_size[1] > 0 - self.crop_size = crop_size - self.ignore_index = ignore_index - - def get_crop_bbox(self, img): - offset_h = max((img.shape[0] - self.crop_size[0]) // 2, 0) - offset_w = max((img.shape[1] - self.crop_size[1]) // 2, 0) - crop_y1, crop_y2 = offset_h, offset_h + self.crop_size[0] - crop_x1, crop_x2 = offset_w, offset_w + self.crop_size[1] - - return crop_y1, crop_y2, crop_x1, crop_x2 - - def crop(self, img, crop_bbox): - """Crop from ``img``""" - crop_y1, crop_y2, crop_x1, crop_x2 = crop_bbox - img = img[crop_y1:crop_y2, crop_x1:crop_x2, ...] - return img - - def __call__(self, results): - - img = results['img'] - crop_bbox = self.get_crop_bbox(img) - - # crop the image - img = self.crop(img, crop_bbox) - img_shape = img.shape - results['img'] = img - results['img_shape'] = img_shape - - # crop semantic seg - for key in results.get('seg_fields', []): - results[key] = self.crop(results[key], crop_bbox) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(crop_size={self.crop_size})' - - -@PIPELINES.register_module() -class RandomRotate(object): - """Rotate the image & seg. - - Args: - prob (float): The rotation probability. - degree (float, tuple[float]): Range of degrees to select from. If - degree is a number instead of tuple like (min, max), - the range of degree will be (``-degree``, ``+degree``) - pad_val (float, optional): Padding value of image. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. Default: None. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. Default: False - """ - - def __init__(self, - prob, - degree, - pad_val=0, - seg_pad_val=255, - center=None, - auto_bound=False): - self.prob = prob - assert prob >= 0 and prob <= 1 - if isinstance(degree, (float, int)): - assert degree > 0, f'degree {degree} should be positive' - self.degree = (-degree, degree) - else: - self.degree = degree - assert len(self.degree) == 2, f'degree {self.degree} should be a ' \ - f'tuple of (min, max)' - self.pal_val = pad_val - self.seg_pad_val = seg_pad_val - self.center = center - self.auto_bound = auto_bound - - def __call__(self, results): - """Call function to rotate image, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Rotated results. - """ - - rotate = True if np.random.rand() < self.prob else False - degree = np.random.uniform(min(*self.degree), max(*self.degree)) - if rotate: - # rotate image - results['img'] = mmcv.imrotate( - results['img'], - angle=degree, - border_value=self.pal_val, - center=self.center, - auto_bound=self.auto_bound) - - # rotate segs - for key in results.get('seg_fields', []): - results[key] = mmcv.imrotate( - results[key], - angle=degree, - border_value=self.seg_pad_val, - center=self.center, - auto_bound=self.auto_bound, - interpolation='nearest') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' \ - f'degree={self.degree}, ' \ - f'pad_val={self.pal_val}, ' \ - f'seg_pad_val={self.seg_pad_val}, ' \ - f'center={self.center}, ' \ - f'auto_bound={self.auto_bound})' - return repr_str - - -@PIPELINES.register_module() -class RGB2Gray(object): - """Convert RGB image to grayscale image. - - This transform calculate the weighted mean of input image channels with - ``weights`` and then expand the channels to ``out_channels``. When - ``out_channels`` is None, the number of output channels is the same as - input channels. - - Args: - out_channels (int): Expected number of output channels after - transforming. Default: None. - weights (tuple[float]): The weights to calculate the weighted mean. - Default: (0.299, 0.587, 0.114). - """ - - def __init__(self, out_channels=None, weights=(0.299, 0.587, 0.114)): - assert out_channels is None or out_channels > 0 - self.out_channels = out_channels - assert isinstance(weights, tuple) - for item in weights: - assert isinstance(item, (float, int)) - self.weights = weights - - def __call__(self, results): - """Call function to convert RGB image to grayscale image. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with grayscale image. - """ - img = results['img'] - assert len(img.shape) == 3 - assert img.shape[2] == len(self.weights) - weights = np.array(self.weights).reshape((1, 1, -1)) - img = (img * weights).sum(2, keepdims=True) - if self.out_channels is None: - img = img.repeat(weights.shape[2], axis=2) - else: - img = img.repeat(self.out_channels, axis=2) - - results['img'] = img - results['img_shape'] = img.shape - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(out_channels={self.out_channels}, ' \ - f'weights={self.weights})' - return repr_str - - -@PIPELINES.register_module() -class AdjustGamma(object): - """Using gamma correction to process the image. - - Args: - gamma (float or int): Gamma value used in gamma correction. - Default: 1.0. - """ - - def __init__(self, gamma=1.0): - assert isinstance(gamma, float) or isinstance(gamma, int) - assert gamma > 0 - self.gamma = gamma - inv_gamma = 1.0 / gamma - self.table = np.array([(i / 255.0)**inv_gamma * 255 - for i in np.arange(256)]).astype('uint8') - - def __call__(self, results): - """Call function to process the image with gamma correction. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - results['img'] = mmcv.lut_transform( - np.array(results['img'], dtype=np.uint8), self.table) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(gamma={self.gamma})' - - -@PIPELINES.register_module() -class SegRescale(object): - """Rescale semantic segmentation maps. - - Args: - scale_factor (float): The scale factor of the final output. - """ - - def __init__(self, scale_factor=1): - self.scale_factor = scale_factor - - def __call__(self, results): - """Call function to scale the semantic segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with semantic segmentation map scaled. - """ - for key in results.get('seg_fields', []): - if self.scale_factor != 1: - results[key] = mmcv.imrescale( - results[key], self.scale_factor, interpolation='nearest') - return results - - def __repr__(self): - return self.__class__.__name__ + f'(scale_factor={self.scale_factor})' - - -@PIPELINES.register_module() -class PhotoMetricDistortion(object): - """Apply photometric distortion to image sequentially, every transformation - is applied with a probability of 0.5. The position of random contrast is in - second or second to last. - - 1. random brightness - 2. random contrast (mode 0) - 3. convert color from BGR to HSV - 4. random saturation - 5. random hue - 6. convert color from HSV to BGR - 7. random contrast (mode 1) - - Args: - brightness_delta (int): delta of brightness. - contrast_range (tuple): range of contrast. - saturation_range (tuple): range of saturation. - hue_delta (int): delta of hue. - """ - - def __init__(self, - brightness_delta=32, - contrast_range=(0.5, 1.5), - saturation_range=(0.5, 1.5), - hue_delta=18): - self.brightness_delta = brightness_delta - self.contrast_lower, self.contrast_upper = contrast_range - self.saturation_lower, self.saturation_upper = saturation_range - self.hue_delta = hue_delta - - def convert(self, img, alpha=1, beta=0): - """Multiple with alpha and add beat with clip.""" - img = img.astype(np.float32) * alpha + beta - img = np.clip(img, 0, 255) - return img.astype(np.uint8) - - def brightness(self, img): - """Brightness distortion.""" - if random.randint(2): - return self.convert( - img, - beta=random.uniform(-self.brightness_delta, - self.brightness_delta)) - return img - - def contrast(self, img): - """Contrast distortion.""" - if random.randint(2): - return self.convert( - img, - alpha=random.uniform(self.contrast_lower, self.contrast_upper)) - return img - - def saturation(self, img): - """Saturation distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, 1] = self.convert( - img[:, :, 1], - alpha=random.uniform(self.saturation_lower, - self.saturation_upper)) - img = mmcv.hsv2bgr(img) - return img - - def hue(self, img): - """Hue distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, - 0] = (img[:, :, 0].astype(int) + - random.randint(-self.hue_delta, self.hue_delta)) % 180 - img = mmcv.hsv2bgr(img) - return img - - def __call__(self, results): - """Call function to perform photometric distortion on images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images distorted. - """ - - img = results['img'] - # random brightness - img = self.brightness(img) - - # mode == 0 --> do random contrast first - # mode == 1 --> do random contrast last - mode = random.randint(2) - if mode == 1: - img = self.contrast(img) - - # random saturation - img = self.saturation(img) - - # random hue - img = self.hue(img) - - # random contrast - if mode == 0: - img = self.contrast(img) - - results['img'] = img - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(brightness_delta={self.brightness_delta}, ' - f'contrast_range=({self.contrast_lower}, ' - f'{self.contrast_upper}), ' - f'saturation_range=({self.saturation_lower}, ' - f'{self.saturation_upper}), ' - f'hue_delta={self.hue_delta})') - return repr_str - - -@PIPELINES.register_module() -class RandomCutOut(object): - """CutOut operation. - - Randomly drop some regions of image used in - `Cutout `_. - Args: - prob (float): cutout probability. - n_holes (int | tuple[int, int]): Number of regions to be dropped. - If it is given as a list, number of holes will be randomly - selected from the closed interval [`n_holes[0]`, `n_holes[1]`]. - cutout_shape (tuple[int, int] | list[tuple[int, int]]): The candidate - shape of dropped regions. It can be `tuple[int, int]` to use a - fixed cutout shape, or `list[tuple[int, int]]` to randomly choose - shape from the list. - cutout_ratio (tuple[float, float] | list[tuple[float, float]]): The - candidate ratio of dropped regions. It can be `tuple[float, float]` - to use a fixed ratio or `list[tuple[float, float]]` to randomly - choose ratio from the list. Please note that `cutout_shape` - and `cutout_ratio` cannot be both given at the same time. - fill_in (tuple[float, float, float] | tuple[int, int, int]): The value - of pixel to fill in the dropped regions. Default: (0, 0, 0). - seg_fill_in (int): The labels of pixel to fill in the dropped regions. - If seg_fill_in is None, skip. Default: None. - """ - - def __init__(self, - prob, - n_holes, - cutout_shape=None, - cutout_ratio=None, - fill_in=(0, 0, 0), - seg_fill_in=None): - - assert 0 <= prob and prob <= 1 - assert (cutout_shape is None) ^ (cutout_ratio is None), \ - 'Either cutout_shape or cutout_ratio should be specified.' - assert (isinstance(cutout_shape, (list, tuple)) - or isinstance(cutout_ratio, (list, tuple))) - if isinstance(n_holes, tuple): - assert len(n_holes) == 2 and 0 <= n_holes[0] < n_holes[1] - else: - n_holes = (n_holes, n_holes) - if seg_fill_in is not None: - assert (isinstance(seg_fill_in, int) and 0 <= seg_fill_in - and seg_fill_in <= 255) - self.prob = prob - self.n_holes = n_holes - self.fill_in = fill_in - self.seg_fill_in = seg_fill_in - self.with_ratio = cutout_ratio is not None - self.candidates = cutout_ratio if self.with_ratio else cutout_shape - if not isinstance(self.candidates, list): - self.candidates = [self.candidates] - - def __call__(self, results): - """Call function to drop some regions of image.""" - cutout = True if np.random.rand() < self.prob else False - if cutout: - h, w, c = results['img'].shape - n_holes = np.random.randint(self.n_holes[0], self.n_holes[1] + 1) - for _ in range(n_holes): - x1 = np.random.randint(0, w) - y1 = np.random.randint(0, h) - index = np.random.randint(0, len(self.candidates)) - if not self.with_ratio: - cutout_w, cutout_h = self.candidates[index] - else: - cutout_w = int(self.candidates[index][0] * w) - cutout_h = int(self.candidates[index][1] * h) - - x2 = np.clip(x1 + cutout_w, 0, w) - y2 = np.clip(y1 + cutout_h, 0, h) - results['img'][y1:y2, x1:x2, :] = self.fill_in - - if self.seg_fill_in is not None: - for key in results.get('seg_fields', []): - results[key][y1:y2, x1:x2] = self.seg_fill_in - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' - repr_str += f'n_holes={self.n_holes}, ' - repr_str += (f'cutout_ratio={self.candidates}, ' if self.with_ratio - else f'cutout_shape={self.candidates}, ') - repr_str += f'fill_in={self.fill_in}, ' - repr_str += f'seg_fill_in={self.seg_fill_in})' - return repr_str - - -@PIPELINES.register_module() -class RandomMosaic(object): - """Mosaic augmentation. Given 4 images, mosaic transform combines them into - one output image. The output image is composed of the parts from each sub- - image. - - .. code:: text - - mosaic transform - center_x - +------------------------------+ - | pad | pad | - | +-----------+ | - | | | | - | | image1 |--------+ | - | | | | | - | | | image2 | | - center_y |----+-------------+-----------| - | | cropped | | - |pad | image3 | image4 | - | | | | - +----|-------------+-----------+ - | | - +-------------+ - - The mosaic transform steps are as follows: - 1. Choose the mosaic center as the intersections of 4 images - 2. Get the left top image according to the index, and randomly - sample another 3 images from the custom dataset. - 3. Sub image will be cropped if image is larger than mosaic patch - - Args: - prob (float): mosaic probability. - img_scale (Sequence[int]): Image size after mosaic pipeline of - a single image. The size of the output image is four times - that of a single image. The output image comprises 4 single images. - Default: (640, 640). - center_ratio_range (Sequence[float]): Center ratio range of mosaic - output. Default: (0.5, 1.5). - pad_val (int): Pad value. Default: 0. - seg_pad_val (int): Pad value of segmentation map. Default: 255. - """ - - def __init__(self, - prob, - img_scale=(640, 640), - center_ratio_range=(0.5, 1.5), - pad_val=0, - seg_pad_val=255): - assert 0 <= prob and prob <= 1 - assert isinstance(img_scale, tuple) - self.prob = prob - self.img_scale = img_scale - self.center_ratio_range = center_ratio_range - self.pad_val = pad_val - self.seg_pad_val = seg_pad_val - - def __call__(self, results): - """Call function to make a mosaic of image. - - Args: - results (dict): Result dict. - - Returns: - dict: Result dict with mosaic transformed. - """ - mosaic = True if np.random.rand() < self.prob else False - if mosaic: - results = self._mosaic_transform_img(results) - results = self._mosaic_transform_seg(results) - return results - - def get_indexes(self, dataset): - """Call function to collect indexes. - - Args: - dataset (:obj:`MultiImageMixDataset`): The dataset. - - Returns: - list: indexes. - """ - - indexes = [random.randint(0, len(dataset)) for _ in range(3)] - return indexes - - def _mosaic_transform_img(self, results): - """Mosaic transform function. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - if len(results['img'].shape) == 3: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2), 3), - self.pad_val, - dtype=results['img'].dtype) - else: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.pad_val, - dtype=results['img'].dtype) - - # mosaic center x, y - self.center_x = int( - random.uniform(*self.center_ratio_range) * self.img_scale[1]) - self.center_y = int( - random.uniform(*self.center_ratio_range) * self.img_scale[0]) - center_position = (self.center_x, self.center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - result_patch = copy.deepcopy(results) - else: - result_patch = copy.deepcopy(results['mix_results'][i - 1]) - - img_i = result_patch['img'] - h_i, w_i = img_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - img_i = mmcv.imresize( - img_i, (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i))) - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, img_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_img[y1_p:y2_p, x1_p:x2_p] = img_i[y1_c:y2_c, x1_c:x2_c] - - results['img'] = mosaic_img - results['img_shape'] = mosaic_img.shape - results['ori_shape'] = mosaic_img.shape - - return results - - def _mosaic_transform_seg(self, results): - """Mosaic transform function for label annotations. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - for key in results.get('seg_fields', []): - mosaic_seg = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.seg_pad_val, - dtype=results[key].dtype) - - # mosaic center x, y - center_position = (self.center_x, self.center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - result_patch = copy.deepcopy(results) - else: - result_patch = copy.deepcopy(results['mix_results'][i - 1]) - - gt_seg_i = result_patch[key] - h_i, w_i = gt_seg_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - gt_seg_i = mmcv.imresize( - gt_seg_i, - (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i)), - interpolation='nearest') - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, gt_seg_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_seg[y1_p:y2_p, x1_p:x2_p] = gt_seg_i[y1_c:y2_c, - x1_c:x2_c] - - results[key] = mosaic_seg - - return results - - def _mosaic_combine(self, loc, center_position_xy, img_shape_wh): - """Calculate global coordinate of mosaic image and local coordinate of - cropped sub-image. - - Args: - loc (str): Index for the sub-image, loc in ('top_left', - 'top_right', 'bottom_left', 'bottom_right'). - center_position_xy (Sequence[float]): Mixing center for 4 images, - (x, y). - img_shape_wh (Sequence[int]): Width and height of sub-image - - Returns: - tuple[tuple[float]]: Corresponding coordinate of pasting and - cropping - - paste_coord (tuple): paste corner coordinate in mosaic image. - - crop_coord (tuple): crop corner coordinate in mosaic image. - """ - - assert loc in ('top_left', 'top_right', 'bottom_left', 'bottom_right') - if loc == 'top_left': - # index0 to top left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - center_position_xy[0], \ - center_position_xy[1] - crop_coord = img_shape_wh[0] - (x2 - x1), img_shape_wh[1] - ( - y2 - y1), img_shape_wh[0], img_shape_wh[1] - - elif loc == 'top_right': - # index1 to top right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - center_position_xy[1] - crop_coord = 0, img_shape_wh[1] - (y2 - y1), min( - img_shape_wh[0], x2 - x1), img_shape_wh[1] - - elif loc == 'bottom_left': - # index2 to bottom left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - center_position_xy[1], \ - center_position_xy[0], \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = img_shape_wh[0] - (x2 - x1), 0, img_shape_wh[0], min( - y2 - y1, img_shape_wh[1]) - - else: - # index3 to bottom right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - center_position_xy[1], \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = 0, 0, min(img_shape_wh[0], - x2 - x1), min(y2 - y1, img_shape_wh[1]) - - paste_coord = x1, y1, x2, y2 - return paste_coord, crop_coord - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' - repr_str += f'img_scale={self.img_scale}, ' - repr_str += f'center_ratio_range={self.center_ratio_range}, ' - repr_str += f'pad_val={self.pad_val}, ' - repr_str += f'seg_pad_val={self.pad_val})' - return repr_str diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/samplers/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/samplers/__init__.py deleted file mode 100644 index da09effaf..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/samplers/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .distributed_sampler import DistributedSampler - -__all__ = ['DistributedSampler'] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/samplers/distributed_sampler.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/samplers/distributed_sampler.py deleted file mode 100644 index d1a13c716..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/datasets/samplers/distributed_sampler.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from __future__ import division -from typing import Iterator, Optional - -import torch -from torch.utils.data import Dataset -from torch.utils.data import DistributedSampler as _DistributedSampler - -from mmseg.core.utils import sync_random_seed - - -class DistributedSampler(_DistributedSampler): - """DistributedSampler inheriting from - `torch.utils.data.DistributedSampler`. - - Args: - datasets (Dataset): the dataset will be loaded. - num_replicas (int, optional): Number of processes participating in - distributed training. By default, world_size is retrieved from the - current distributed group. - rank (int, optional): Rank of the current process within num_replicas. - By default, rank is retrieved from the current distributed group. - shuffle (bool): If True (default), sampler will shuffle the indices. - seed (int): random seed used to shuffle the sampler if - :attr:`shuffle=True`. This number should be identical across all - processes in the distributed group. Default: ``0``. - """ - - def __init__(self, - dataset: Dataset, - num_replicas: Optional[int] = None, - rank: Optional[int] = None, - shuffle: bool = True, - seed=0) -> None: - super().__init__( - dataset, num_replicas=num_replicas, rank=rank, shuffle=shuffle) - - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - - def __iter__(self) -> Iterator: - """ - Yields: - Iterator: iterator of indices for rank. - """ - # deterministically shuffle based on epoch - if self.shuffle: - g = torch.Generator() - # When :attr:`shuffle=True`, this ensures all replicas - # use a different random ordering for each epoch. - # Otherwise, the next iteration of this sampler will - # yield the same ordering. - g.manual_seed(self.epoch + self.seed) - indices = torch.randperm(len(self.dataset), generator=g).tolist() - else: - indices = torch.arange(len(self.dataset)).tolist() - - # add extra samples to make it evenly divisible - indices += indices[:(self.total_size - len(indices))] - assert len(indices) == self.total_size - - # subsample - indices = indices[self.rank:self.total_size:self.num_replicas] - assert len(indices) == self.num_samples - - return iter(indices) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/__init__.py deleted file mode 100644 index 87d8108e3..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .backbones import * # noqa: F401,F403 -from .builder import (BACKBONES, HEADS, LOSSES, SEGMENTORS, build_backbone, - build_head, build_loss, build_segmentor) -from .decode_heads import * # noqa: F401,F403 -from .losses import * # noqa: F401,F403 -from .necks import * # noqa: F401,F403 -from .segmentors import * # noqa: F401,F403 - -__all__ = [ - 'BACKBONES', 'HEADS', 'LOSSES', 'SEGMENTORS', 'build_backbone', - 'build_head', 'build_loss', 'build_segmentor' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/backbones/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/backbones/__init__.py deleted file mode 100644 index 464bfa9f4..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/backbones/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .resnet import ResNet, ResNetV1c, ResNetV1d - diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/backbones/resnet.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/backbones/resnet.py deleted file mode 100644 index e8b961d5f..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/backbones/resnet.py +++ /dev/null @@ -1,714 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.utils.checkpoint as cp -from mmcv.cnn import build_conv_layer, build_norm_layer, build_plugin_layer -from mmcv.runner import BaseModule -from mmcv.utils.parrots_wrapper import _BatchNorm - -from ..builder import BACKBONES -from ..utils import ResLayer - - -class BasicBlock(BaseModule): - """Basic block for ResNet.""" - - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(BasicBlock, self).__init__(init_cfg) - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - 3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - self.add_module(self.norm1_name, norm1) - self.conv2 = build_conv_layer( - conv_cfg, planes, planes, 3, padding=1, bias=False) - self.add_module(self.norm2_name, norm2) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.norm2(out) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -class Bottleneck(BaseModule): - """Bottleneck block for ResNet. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if it is - "caffe", the stride-two layer is the first 1x1 conv layer. - """ - - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(Bottleneck, self).__init__(init_cfg) - assert style in ['pytorch', 'caffe'] - assert dcn is None or isinstance(dcn, dict) - assert plugins is None or isinstance(plugins, list) - if plugins is not None: - allowed_position = ['after_conv1', 'after_conv2', 'after_conv3'] - assert all(p['position'] in allowed_position for p in plugins) - - self.inplanes = inplanes - self.planes = planes - self.stride = stride - self.dilation = dilation - self.style = style - self.with_cp = with_cp - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.dcn = dcn - self.with_dcn = dcn is not None - self.plugins = plugins - self.with_plugins = plugins is not None - - if self.with_plugins: - # collect plugins for conv1/conv2/conv3 - self.after_conv1_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv1' - ] - self.after_conv2_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv2' - ] - self.after_conv3_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv3' - ] - - if self.style == 'pytorch': - self.conv1_stride = 1 - self.conv2_stride = stride - else: - self.conv1_stride = stride - self.conv2_stride = 1 - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - self.norm3_name, norm3 = build_norm_layer( - norm_cfg, planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - fallback_on_stride = False - if self.with_dcn: - fallback_on_stride = dcn.pop('fallback_on_stride', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = build_conv_layer( - conv_cfg, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - else: - assert self.conv_cfg is None, 'conv_cfg must be None for DCN' - self.conv2 = build_conv_layer( - dcn, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.add_module(self.norm2_name, norm2) - self.conv3 = build_conv_layer( - conv_cfg, - planes, - planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - - if self.with_plugins: - self.after_conv1_plugin_names = self.make_block_plugins( - planes, self.after_conv1_plugins) - self.after_conv2_plugin_names = self.make_block_plugins( - planes, self.after_conv2_plugins) - self.after_conv3_plugin_names = self.make_block_plugins( - planes * self.expansion, self.after_conv3_plugins) - - def make_block_plugins(self, in_channels, plugins): - """make plugins for block. - - Args: - in_channels (int): Input channels of plugin. - plugins (list[dict]): List of plugins cfg to build. - - Returns: - list[str]: List of the names of plugin. - """ - assert isinstance(plugins, list) - plugin_names = [] - for plugin in plugins: - plugin = plugin.copy() - name, layer = build_plugin_layer( - plugin, - in_channels=in_channels, - postfix=plugin.pop('postfix', '')) - assert not hasattr(self, name), f'duplicate plugin {name}' - self.add_module(name, layer) - plugin_names.append(name) - return plugin_names - - def forward_plugin(self, x, plugin_names): - """Forward function for plugins.""" - out = x - for name in plugin_names: - out = getattr(self, name)(x) - return out - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - @property - def norm3(self): - """nn.Module: normalization layer after the third convolution layer""" - return getattr(self, self.norm3_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv1_plugin_names) - - out = self.conv2(out) - out = self.norm2(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv2_plugin_names) - - out = self.conv3(out) - out = self.norm3(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv3_plugin_names) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -@BACKBONES.register_module() -class ResNet(BaseModule): - """ResNet backbone. - - This backbone is the improved implementation of `Deep Residual Learning - for Image Recognition `_. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - in_channels (int): Number of input image channels. Default: 3. - stem_channels (int): Number of stem channels. Default: 64. - base_channels (int): Number of base channels of res layer. Default: 64. - num_stages (int): Resnet stages, normally 4. Default: 4. - strides (Sequence[int]): Strides of the first block of each stage. - Default: (1, 2, 2, 2). - dilations (Sequence[int]): Dilation of each stage. - Default: (1, 1, 1, 1). - out_indices (Sequence[int]): Output from which stages. - Default: (0, 1, 2, 3). - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. Default: 'pytorch'. - deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv. - Default: False. - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False. - frozen_stages (int): Stages to be frozen (stop grad and set eval mode). - -1 means not freezing any parameters. Default: -1. - conv_cfg (dict | None): Dictionary to construct and config conv layer. - When conv_cfg is None, cfg will be set to dict(type='Conv2d'). - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN', requires_grad=True). - norm_eval (bool): Whether to set norm layers to eval mode, namely, - freeze running stats (mean and var). Note: Effect on Batch Norm - and its variants only. Default: False. - dcn (dict | None): Dictionary to construct and config DCN conv layer. - When dcn is not None, conv_cfg must be None. Default: None. - stage_with_dcn (Sequence[bool]): Whether to set DCN conv for each - stage. The length of stage_with_dcn is equal to num_stages. - Default: (False, False, False, False). - plugins (list[dict]): List of plugins for stages, each dict contains: - - - cfg (dict, required): Cfg dict to build plugin. - - - position (str, required): Position inside block to insert plugin, - options: 'after_conv1', 'after_conv2', 'after_conv3'. - - - stages (tuple[bool], optional): Stages to apply plugin, length - should be same as 'num_stages'. - Default: None. - multi_grid (Sequence[int]|None): Multi grid dilation rates of last - stage. Default: None. - contract_dilation (bool): Whether contract first dilation of each layer - Default: False. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - zero_init_residual (bool): Whether to use zero init for last norm layer - in resblocks to let them behave as identity. Default: True. - pretrained (str, optional): model pretrained path. Default: None. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Example: - >>> from mmseg.models import ResNet - >>> import torch - >>> self = ResNet(depth=18) - >>> self.eval() - >>> inputs = torch.rand(1, 3, 32, 32) - >>> level_outputs = self.forward(inputs) - >>> for level_out in level_outputs: - ... print(tuple(level_out.shape)) - (1, 64, 8, 8) - (1, 128, 4, 4) - (1, 256, 2, 2) - (1, 512, 1, 1) - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - in_channels=3, - stem_channels=64, - base_channels=64, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - deep_stem=False, - avg_down=False, - frozen_stages=-1, - conv_cfg=None, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=False, - dcn=None, - stage_with_dcn=(False, False, False, False), - plugins=None, - multi_grid=None, - contract_dilation=False, - with_cp=False, - zero_init_residual=True, - pretrained=None, - init_cfg=None): - super(ResNet, self).__init__(init_cfg) - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - - self.pretrained = pretrained - self.zero_init_residual = zero_init_residual - block_init_cfg = None - assert not (init_cfg and pretrained), \ - 'init_cfg and pretrained cannot be setting at the same time' - if isinstance(pretrained, str): - warnings.warn('DeprecationWarning: pretrained is a deprecated, ' - 'please use "init_cfg" instead') - self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - elif pretrained is None: - if init_cfg is None: - self.init_cfg = [ - dict(type='Kaiming', layer='Conv2d'), - dict( - type='Constant', - val=1, - layer=['_BatchNorm', 'GroupNorm']) - ] - block = self.arch_settings[depth][0] - if self.zero_init_residual: - if block is BasicBlock: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm2')) - elif block is Bottleneck: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm3')) - else: - raise TypeError('pretrained must be a str or None') - - self.depth = depth - self.stem_channels = stem_channels - self.base_channels = base_channels - self.num_stages = num_stages - assert num_stages >= 1 and num_stages <= 4 - self.strides = strides - self.dilations = dilations - assert len(strides) == len(dilations) == num_stages - self.out_indices = out_indices - assert max(out_indices) < num_stages - self.style = style - self.deep_stem = deep_stem - self.avg_down = avg_down - self.frozen_stages = frozen_stages - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.with_cp = with_cp - self.norm_eval = norm_eval - self.dcn = dcn - self.stage_with_dcn = stage_with_dcn - if dcn is not None: - assert len(stage_with_dcn) == num_stages - self.plugins = plugins - self.multi_grid = multi_grid - self.contract_dilation = contract_dilation - self.block, stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - self.inplanes = stem_channels - - self._make_stem_layer(in_channels, stem_channels) - - self.res_layers = [] - for i, num_blocks in enumerate(self.stage_blocks): - stride = strides[i] - dilation = dilations[i] - dcn = self.dcn if self.stage_with_dcn[i] else None - if plugins is not None: - stage_plugins = self.make_stage_plugins(plugins, i) - else: - stage_plugins = None - # multi grid is applied to last layer only - stage_multi_grid = multi_grid if i == len( - self.stage_blocks) - 1 else None - planes = base_channels * 2**i - res_layer = self.make_res_layer( - block=self.block, - inplanes=self.inplanes, - planes=planes, - num_blocks=num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - avg_down=self.avg_down, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - dcn=dcn, - plugins=stage_plugins, - multi_grid=stage_multi_grid, - contract_dilation=contract_dilation, - init_cfg=block_init_cfg) - self.inplanes = planes * self.block.expansion - layer_name = f'layer{i+1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self._freeze_stages() - - self.feat_dim = self.block.expansion * base_channels * 2**( - len(self.stage_blocks) - 1) - - def make_stage_plugins(self, plugins, stage_idx): - """make plugins for ResNet 'stage_idx'th stage . - - Currently we support to insert 'context_block', - 'empirical_attention_block', 'nonlocal_block' into the backbone like - ResNet/ResNeXt. They could be inserted after conv1/conv2/conv3 of - Bottleneck. - - An example of plugins format could be : - >>> plugins=[ - ... dict(cfg=dict(type='xxx', arg1='xxx'), - ... stages=(False, True, True, True), - ... position='after_conv2'), - ... dict(cfg=dict(type='yyy'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='1'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='2'), - ... stages=(True, True, True, True), - ... position='after_conv3') - ... ] - >>> self = ResNet(depth=18) - >>> stage_plugins = self.make_stage_plugins(plugins, 0) - >>> assert len(stage_plugins) == 3 - - Suppose 'stage_idx=0', the structure of blocks in the stage would be: - conv1-> conv2->conv3->yyy->zzz1->zzz2 - Suppose 'stage_idx=1', the structure of blocks in the stage would be: - conv1-> conv2->xxx->conv3->yyy->zzz1->zzz2 - - If stages is missing, the plugin would be applied to all stages. - - Args: - plugins (list[dict]): List of plugins cfg to build. The postfix is - required if multiple same type plugins are inserted. - stage_idx (int): Index of stage to build - - Returns: - list[dict]: Plugins for current stage - """ - stage_plugins = [] - for plugin in plugins: - plugin = plugin.copy() - stages = plugin.pop('stages', None) - assert stages is None or len(stages) == self.num_stages - # whether to insert plugin into current stage - if stages is None or stages[stage_idx]: - stage_plugins.append(plugin) - - return stage_plugins - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``.""" - return ResLayer(**kwargs) - - @property - def norm1(self): - """nn.Module: the normalization layer named "norm1" """ - return getattr(self, self.norm1_name) - - def _make_stem_layer(self, in_channels, stem_channels): - """Make stem layer for ResNet.""" - if self.deep_stem: - self.stem = nn.Sequential( - build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels // 2, - kernel_size=3, - stride=2, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels // 2, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels)[1], - nn.ReLU(inplace=True)) - else: - self.conv1 = build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels, - kernel_size=7, - stride=2, - padding=3, - bias=False) - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, stem_channels, postfix=1) - self.add_module(self.norm1_name, norm1) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - def _freeze_stages(self): - """Freeze stages param and norm stats.""" - if self.frozen_stages >= 0: - if self.deep_stem: - self.stem.eval() - for param in self.stem.parameters(): - param.requires_grad = False - else: - self.norm1.eval() - for m in [self.conv1, self.norm1]: - for param in m.parameters(): - param.requires_grad = False - - for i in range(1, self.frozen_stages + 1): - m = getattr(self, f'layer{i}') - m.eval() - for param in m.parameters(): - param.requires_grad = False - - def forward(self, x): - """Forward function.""" - if self.deep_stem: - x = self.stem(x) - else: - x = self.conv1(x) - x = self.norm1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - return tuple(outs) - - def train(self, mode=True): - """Convert the model into training mode while keep normalization layer - freezed.""" - super(ResNet, self).train(mode) - self._freeze_stages() - if mode and self.norm_eval: - for m in self.modules(): - # trick: eval have effect on BatchNorm only - if isinstance(m, _BatchNorm): - m.eval() - - -@BACKBONES.register_module() -class ResNetV1c(ResNet): - """ResNetV1c variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1c replaces the 7x7 conv in - the input stem with three 3x3 convs. For more details please refer to `Bag - of Tricks for Image Classification with Convolutional Neural Networks - `_. - """ - - def __init__(self, **kwargs): - super(ResNetV1c, self).__init__( - deep_stem=True, avg_down=False, **kwargs) - - -@BACKBONES.register_module() -class ResNetV1d(ResNet): - """ResNetV1d variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1d replaces the 7x7 conv in - the input stem with three 3x3 convs. And in the downsampling block, a 2x2 - avg_pool with stride 2 is added before conv, whose stride is changed to 1. - """ - - def __init__(self, **kwargs): - super(ResNetV1d, self).__init__( - deep_stem=True, avg_down=True, **kwargs) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/builder.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/builder.py deleted file mode 100644 index 5e18e4e64..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/builder.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -from mmcv.cnn import MODELS as MMCV_MODELS -from mmcv.cnn.bricks.registry import ATTENTION as MMCV_ATTENTION -from mmcv.utils import Registry - -MODELS = Registry('models', parent=MMCV_MODELS) -ATTENTION = Registry('attention', parent=MMCV_ATTENTION) - -BACKBONES = MODELS -NECKS = MODELS -HEADS = MODELS -LOSSES = MODELS -SEGMENTORS = MODELS - - -def build_backbone(cfg): - """Build backbone.""" - return BACKBONES.build(cfg) - - -def build_neck(cfg): - """Build neck.""" - return NECKS.build(cfg) - - -def build_head(cfg): - """Build head.""" - return HEADS.build(cfg) - - -def build_loss(cfg): - """Build loss.""" - return LOSSES.build(cfg) - - -def build_segmentor(cfg, train_cfg=None, test_cfg=None): - """Build segmentor.""" - if train_cfg is not None or test_cfg is not None: - warnings.warn( - 'train_cfg and test_cfg is deprecated, ' - 'please specify them in model', UserWarning) - assert cfg.get('train_cfg') is None or train_cfg is None, \ - 'train_cfg specified in both outer field and model field ' - assert cfg.get('test_cfg') is None or test_cfg is None, \ - 'test_cfg specified in both outer field and model field ' - return SEGMENTORS.build( - cfg, default_args=dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/__init__.py deleted file mode 100644 index 028d9accc..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from .decode_head import BaseDecodeHead -from .unetplusplus_head import UNetPlusPlusHead -from .fcn_head import FCNHead diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/decode_head.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/decode_head.py deleted file mode 100644 index 098e1b4e4..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/decode_head.py +++ /dev/null @@ -1,266 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -import torch -import torch.nn as nn -from mmcv.runner import BaseModule, auto_fp16, force_fp32 - -from mmseg.core import build_pixel_sampler -from mmseg.ops import resize -from ..builder import build_loss -from ..losses import accuracy - - -class BaseDecodeHead(BaseModule, metaclass=ABCMeta): - """Base class for BaseDecodeHead. - - Args: - in_channels (int|Sequence[int]): Input channels. - channels (int): Channels after modules, before conv_seg. - num_classes (int): Number of classes. - dropout_ratio (float): Ratio of dropout layer. Default: 0.1. - conv_cfg (dict|None): Config of conv layers. Default: None. - norm_cfg (dict|None): Config of norm layers. Default: None. - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU') - in_index (int|Sequence[int]): Input feature index. Default: -1 - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - Default: None. - loss_decode (dict | Sequence[dict]): Config of decode loss. - The `loss_name` is property of corresponding loss function which - could be shown in training log. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - e.g. dict(type='CrossEntropyLoss'), - [dict(type='CrossEntropyLoss', loss_name='loss_ce'), - dict(type='DiceLoss', loss_name='loss_dice')] - Default: dict(type='CrossEntropyLoss'). - ignore_index (int | None): The label index to be ignored. When using - masked BCE loss, ignore_index should be set to None. Default: 255. - sampler (dict|None): The config of segmentation map sampler. - Default: None. - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - """ - - def __init__(self, - in_channels, - channels, - *, - num_classes, - dropout_ratio=0.1, - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - in_index=-1, - input_transform=None, - loss_decode=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0), - ignore_index=255, - sampler=None, - align_corners=False, - init_cfg=dict( - type='Normal', std=0.01, override=dict(name='conv_seg'))): - super(BaseDecodeHead, self).__init__(init_cfg) - self._init_inputs(in_channels, in_index, input_transform) - self.channels = channels - self.num_classes = num_classes - self.dropout_ratio = dropout_ratio - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.in_index = in_index - - self.ignore_index = ignore_index - self.align_corners = align_corners - - if isinstance(loss_decode, dict): - self.loss_decode = build_loss(loss_decode) - elif isinstance(loss_decode, (list, tuple)): - self.loss_decode = nn.ModuleList() - for loss in loss_decode: - self.loss_decode.append(build_loss(loss)) - else: - raise TypeError(f'loss_decode must be a dict or sequence of dict,\ - but got {type(loss_decode)}') - - if sampler is not None: - self.sampler = build_pixel_sampler(sampler, context=self) - else: - self.sampler = None - - self.conv_seg = nn.Conv2d(channels, num_classes, kernel_size=1) - if dropout_ratio > 0: - self.dropout = nn.Dropout2d(dropout_ratio) - else: - self.dropout = None - self.fp16_enabled = False - - def extra_repr(self): - """Extra repr.""" - s = f'input_transform={self.input_transform}, ' \ - f'ignore_index={self.ignore_index}, ' \ - f'align_corners={self.align_corners}' - return s - - def _init_inputs(self, in_channels, in_index, input_transform): - """Check and initialize input transforms. - - The in_channels, in_index and input_transform must match. - Specifically, when input_transform is None, only single feature map - will be selected. So in_channels and in_index must be of type int. - When input_transform - - Args: - in_channels (int|Sequence[int]): Input channels. - in_index (int|Sequence[int]): Input feature index. - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - """ - - if input_transform is not None: - assert input_transform in ['resize_concat', 'multiple_select'] - self.input_transform = input_transform - self.in_index = in_index - if input_transform is not None: - assert isinstance(in_channels, (list, tuple)) - assert isinstance(in_index, (list, tuple)) - assert len(in_channels) == len(in_index) - if input_transform == 'resize_concat': - self.in_channels = sum(in_channels) - else: - self.in_channels = in_channels - else: - assert isinstance(in_channels, (int, list, tuple)) - assert isinstance(in_index, int) - self.in_channels = in_channels - - def _transform_inputs(self, inputs): - """Transform inputs for decoder. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - Tensor: The transformed inputs - """ - - if self.input_transform == 'resize_concat': - inputs = [inputs[i] for i in self.in_index] - upsampled_inputs = [ - resize( - input=x, - size=inputs[0].shape[2:], - mode='bilinear', - align_corners=self.align_corners) for x in inputs - ] - inputs = torch.cat(upsampled_inputs, dim=1) - elif self.input_transform == 'multiple_select': - inputs = [inputs[i] for i in self.in_index] - else: - inputs = inputs[self.in_index] - - return inputs - - @auto_fp16() - @abstractmethod - def forward(self, inputs): - """Placeholder of forward function.""" - pass - - def forward_train(self, inputs, img_metas, gt_semantic_seg, train_cfg): - """Forward function for training. - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - train_cfg (dict): The training config. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - seg_logits = self.forward(inputs) - losses = self.losses(seg_logits, gt_semantic_seg) - return losses - - def forward_test(self, inputs, img_metas, test_cfg): - """Forward function for testing. - - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - test_cfg (dict): The testing config. - - Returns: - Tensor: Output segmentation map. - """ - return self.forward(inputs) - - def cls_seg(self, feat): - """Classify each pixel.""" - if self.dropout is not None: - feat = self.dropout(feat) - output = self.conv_seg(feat) - return output - - @force_fp32(apply_to=('seg_logit', )) - def losses(self, seg_logit, seg_label): - """Compute segmentation loss.""" - loss = dict() - seg_logit = resize( - input=seg_logit, - size=seg_label.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - if self.sampler is not None: - seg_weight = self.sampler.sample(seg_logit, seg_label) - else: - seg_weight = None - seg_label = seg_label.squeeze(1) - - if not isinstance(self.loss_decode, nn.ModuleList): - losses_decode = [self.loss_decode] - else: - losses_decode = self.loss_decode - for loss_decode in losses_decode: - if loss_decode.loss_name not in loss: - loss[loss_decode.loss_name] = loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - else: - loss[loss_decode.loss_name] += loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - - loss['acc_seg'] = accuracy( - seg_logit, seg_label, ignore_index=self.ignore_index) - return loss diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/fcn_head.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/fcn_head.py deleted file mode 100644 index fb79a0d7c..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/fcn_head.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule - -from ..builder import HEADS -from .decode_head import BaseDecodeHead - - -@HEADS.register_module() -class FCNHead(BaseDecodeHead): - """Fully Convolution Networks for Semantic Segmentation. - - This head is implemented of `FCNNet `_. - - Args: - num_convs (int): Number of convs in the head. Default: 2. - kernel_size (int): The kernel size for convs in the head. Default: 3. - concat_input (bool): Whether concat the input and output of convs - before classification layer. - dilation (int): The dilation rate for convs in the head. Default: 1. - """ - - def __init__(self, - num_convs=2, - kernel_size=3, - concat_input=True, - dilation=1, - **kwargs): - assert num_convs >= 0 and dilation > 0 and isinstance(dilation, int) - self.num_convs = num_convs - self.concat_input = concat_input - self.kernel_size = kernel_size - super(FCNHead, self).__init__(**kwargs) - if num_convs == 0: - assert self.in_channels == self.channels - - conv_padding = (kernel_size // 2) * dilation - convs = [] - convs.append( - ConvModule( - self.in_channels, - self.channels, - kernel_size=kernel_size, - padding=conv_padding, - dilation=dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - for i in range(num_convs - 1): - convs.append( - ConvModule( - self.channels, - self.channels, - kernel_size=kernel_size, - padding=conv_padding, - dilation=dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - if num_convs == 0: - self.convs = nn.Identity() - else: - self.convs = nn.Sequential(*convs) - if self.concat_input: - self.conv_cat = ConvModule( - self.in_channels + self.channels, - self.channels, - kernel_size=kernel_size, - padding=kernel_size // 2, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - def _forward_feature(self, inputs): - """Forward function for feature maps before classifying each pixel with - ``self.cls_seg`` fc. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - feats (Tensor): A tensor of shape (batch_size, self.channels, - H, W) which is feature map for last layer of decoder head. - """ - x = self._transform_inputs(inputs) - feats = self.convs(x) - if self.concat_input: - feats = self.conv_cat(torch.cat([x, feats], dim=1)) - return feats - - def forward(self, inputs): - """Forward function.""" - output = self._forward_feature(inputs) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/unetplusplus_head.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/unetplusplus_head.py deleted file mode 100644 index cb6e1e4a5..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/decode_heads/unetplusplus_head.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import torch -import torch.nn as nn -import torch.nn.functional as F -from collections import OrderedDict - -from ..builder import HEADS -from .decode_head import BaseDecodeHead - -def conv_bn_relu(in_channels, out_channels, kernel_size=3, padding=1, stride=1): - return nn.Sequential(nn.Conv2d(in_channels, - out_channels, - kernel_size=kernel_size, - padding=padding, - stride=stride), - nn.BatchNorm2d(out_channels), - nn.ReLU(inplace=True)) - -# class AttBlock(nn.Module): -# def __init__(self, F_g, F_l, F_int): -# super(AttBlock, self).__init__() -# self.W_g = nn.Sequential( -# nn.Conv2d(in_channels=F_g, -# out_channels=F_int, -# kernel_size=1, -# stride=1, -# padding=0, -# bias=True), -# nn.BatchNorm2d(F_int) -# ) - -# self.W_x = nn.Sequential( -# nn.Conv2d(in_channels=F_l, -# out_channels=F_int, -# kernel_size=1, -# stride=1, -# padding=0, -# bias=True), -# nn.BatchNorm2d(F_int) -# ) - -# self.psi = nn.Sequential( -# nn.Conv2d(in_channels=F_int, -# out_channels=1, -# kernel_size=1, -# stride=1, -# padding=0, -# bias=True), -# nn.BatchNorm2d(1), -# nn.Sigmoid() -# ) - -# self.relu = nn.ReLU(inplace=True) - -# def forward(self, g, x): -# print(g.shape, x.shape) -# g1 = self.W_g(g) -# x1 = self.W_x(x) -# print(g1.shape, x1.shape) -# psi = self.relu(g1 + x1) -# psi = self.psi(psi) - -# return x * psi - - -class DecBlock(nn.Module): - def __init__( - self, - in_channels, - skip_channels, - out_channels - ): - super().__init__() - self.conv1 = conv_bn_relu(in_channels=in_channels + skip_channels, - out_channels=out_channels) - - self.conv2 = conv_bn_relu(in_channels=out_channels, - out_channels=out_channels) - - self.up = nn.Upsample(scale_factor=2, - mode='bilinear', - align_corners=True) - - # self.att = AttBlock(F_g=in_channels, F_l=skip_channels, F_int=in_channels) - - def forward(self, x, skip=None): - x = self.up(x) - if skip is not None: - x = torch.cat([x, skip], dim=1) - x = self.conv1(x) - x = self.conv2(x) - # print(x.shape) - return x - - -@HEADS.register_module() -class UNetPlusPlusHead(BaseDecodeHead): - def __init__( - self, **kwargs - ): - super(UNetPlusPlusHead, self).__init__(**kwargs) - - self.decoder_layers = nn.ModuleList() - skip_channels = self.in_channels[:-1] - - blocks = {} - for stage_idx in range(1, len(self.in_channels)): - for lvl_idx in range(len(self.in_channels) - stage_idx): - in_ch = self.in_channels[lvl_idx + 1] - skip_ch = skip_channels[lvl_idx] * (stage_idx) - out_ch = self.in_channels[lvl_idx] - blocks[f'x_{lvl_idx}_{stage_idx}'] = DecBlock(in_ch, skip_ch, out_ch) - - self.blocks = nn.ModuleDict(blocks) - - def forward(self, features): - dense_x = OrderedDict() - for idx, item in enumerate(features): - dense_x[f'x_{idx}_{0}'] = features[idx] - - for stage_idx in range(1, len(self.in_channels)): - for lvl_idx in range(len(self.in_channels) - stage_idx): - skip_features = [dense_x[f'x_{lvl_idx}_{idx}'] for idx in range(stage_idx)] - skip_features = torch.cat(skip_features, dim=1) - output = self.blocks[f'x_{lvl_idx}_{stage_idx}'](dense_x[f'x_{lvl_idx + 1}_{stage_idx - 1}'], - skip_features) - dense_x[f'x_{lvl_idx}_{stage_idx}'] = output - - - res = self.cls_seg(dense_x[next(reversed(dense_x))]) - return res - - - def init_weights(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') - elif isinstance(m, nn.BatchNorm2d): - nn.init.constant_(m.weight, 1) - nn.init.constant_(m.bias, 0) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/__init__.py deleted file mode 100644 index fbc5b2d1b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .accuracy import Accuracy, accuracy -from .cross_entropy_loss import (CrossEntropyLoss, binary_cross_entropy, - cross_entropy, mask_cross_entropy) -from .dice_loss import DiceLoss -from .focal_loss import FocalLoss -from .lovasz_loss import LovaszLoss -from .utils import reduce_loss, weight_reduce_loss, weighted_loss - -__all__ = [ - 'accuracy', 'Accuracy', 'cross_entropy', 'binary_cross_entropy', - 'mask_cross_entropy', 'CrossEntropyLoss', 'reduce_loss', - 'weight_reduce_loss', 'weighted_loss', 'LovaszLoss', 'DiceLoss', - 'FocalLoss' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/accuracy.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/accuracy.py deleted file mode 100644 index 1d9e2d770..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/accuracy.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def accuracy(pred, target, topk=1, thresh=None, ignore_index=None): - """Calculate accuracy according to the prediction and target. - - Args: - pred (torch.Tensor): The model prediction, shape (N, num_class, ...) - target (torch.Tensor): The target of each prediction, shape (N, , ...) - ignore_index (int | None): The label index to be ignored. Default: None - topk (int | tuple[int], optional): If the predictions in ``topk`` - matches the target, the predictions will be regarded as - correct ones. Defaults to 1. - thresh (float, optional): If not None, predictions with scores under - this threshold are considered incorrect. Default to None. - - Returns: - float | tuple[float]: If the input ``topk`` is a single integer, - the function will return a single float as accuracy. If - ``topk`` is a tuple containing multiple integers, the - function will return a tuple containing accuracies of - each ``topk`` number. - """ - assert isinstance(topk, (int, tuple)) - if isinstance(topk, int): - topk = (topk, ) - return_single = True - else: - return_single = False - - maxk = max(topk) - if pred.size(0) == 0: - accu = [pred.new_tensor(0.) for i in range(len(topk))] - return accu[0] if return_single else accu - assert pred.ndim == target.ndim + 1 - assert pred.size(0) == target.size(0) - assert maxk <= pred.size(1), \ - f'maxk {maxk} exceeds pred dimension {pred.size(1)}' - pred_value, pred_label = pred.topk(maxk, dim=1) - # transpose to shape (maxk, N, ...) - pred_label = pred_label.transpose(0, 1) - correct = pred_label.eq(target.unsqueeze(0).expand_as(pred_label)) - if thresh is not None: - # Only prediction values larger than thresh are counted as correct - correct = correct & (pred_value > thresh).t() - if ignore_index is not None: - correct = correct[:, target != ignore_index] - res = [] - eps = torch.finfo(torch.float32).eps - for k in topk: - # Avoid causing ZeroDivisionError when all pixels - # of an image are ignored - correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) + eps - if ignore_index is not None: - total_num = target[target != ignore_index].numel() + eps - else: - total_num = target.numel() + eps - res.append(correct_k.mul_(100.0 / total_num)) - return res[0] if return_single else res - - -class Accuracy(nn.Module): - """Accuracy calculation module.""" - - def __init__(self, topk=(1, ), thresh=None, ignore_index=None): - """Module to calculate the accuracy. - - Args: - topk (tuple, optional): The criterion used to calculate the - accuracy. Defaults to (1,). - thresh (float, optional): If not None, predictions with scores - under this threshold are considered incorrect. Default to None. - """ - super().__init__() - self.topk = topk - self.thresh = thresh - self.ignore_index = ignore_index - - def forward(self, pred, target): - """Forward function to calculate accuracy. - - Args: - pred (torch.Tensor): Prediction of models. - target (torch.Tensor): Target for each prediction. - - Returns: - tuple[float]: The accuracies under different topk criterions. - """ - return accuracy(pred, target, self.topk, self.thresh, - self.ignore_index) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/cross_entropy_loss.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/cross_entropy_loss.py deleted file mode 100644 index 623fd58db..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/cross_entropy_loss.py +++ /dev/null @@ -1,296 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weight_reduce_loss - - -def cross_entropy(pred, - label, - weight=None, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=-100, - avg_non_ignore=False): - """cross_entropy. The wrapper function for :func:`F.cross_entropy` - - Args: - pred (torch.Tensor): The prediction with shape (N, 1). - label (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - Default: None. - class_weight (list[float], optional): The weight for each class. - Default: None. - reduction (str, optional): The method used to reduce the loss. - Options are 'none', 'mean' and 'sum'. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Default: None. - ignore_index (int): Specifies a target value that is ignored and - does not contribute to the input gradients. When - ``avg_non_ignore `` is ``True``, and the ``reduction`` is - ``''mean''``, the loss is averaged over non-ignored targets. - Defaults: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - """ - - # class_weight is a manual rescaling weight given to each class. - # If given, has to be a Tensor of size C element-wise losses - loss = F.cross_entropy( - pred, - label, - weight=class_weight, - reduction='none', - ignore_index=ignore_index) - - # apply weights and do the reduction - # average loss over non-ignored elements - # pytorch's official cross_entropy average loss over non-ignored elements - # refer to https://github.com/pytorch/pytorch/blob/56b43f4fec1f76953f15a627694d4bba34588969/torch/nn/functional.py#L2660 # noqa - if (avg_factor is None) and avg_non_ignore and reduction == 'mean': - avg_factor = label.numel() - (label == ignore_index).sum().item() - if weight is not None: - weight = weight.float() - loss = weight_reduce_loss( - loss, weight=weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def _expand_onehot_labels(labels, label_weights, target_shape, ignore_index): - """Expand onehot labels to match the size of prediction.""" - bin_labels = labels.new_zeros(target_shape) - valid_mask = (labels >= 0) & (labels != ignore_index) - inds = torch.nonzero(valid_mask, as_tuple=True) - - if inds[0].numel() > 0: - if labels.dim() == 3: - bin_labels[inds[0], labels[valid_mask], inds[1], inds[2]] = 1 - else: - bin_labels[inds[0], labels[valid_mask]] = 1 - - valid_mask = valid_mask.unsqueeze(1).expand(target_shape).float() - - if label_weights is None: - bin_label_weights = valid_mask - else: - bin_label_weights = label_weights.unsqueeze(1).expand(target_shape) - bin_label_weights = bin_label_weights * valid_mask - - return bin_labels, bin_label_weights, valid_mask - - -def binary_cross_entropy(pred, - label, - weight=None, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=-100, - avg_non_ignore=False, - **kwargs): - """Calculate the binary CrossEntropy loss. - - Args: - pred (torch.Tensor): The prediction with shape (N, 1). - label (torch.Tensor): The learning label of the prediction. - Note: In bce loss, label < 0 is invalid. - weight (torch.Tensor, optional): Sample-wise loss weight. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (int): The label index to be ignored. Default: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - - Returns: - torch.Tensor: The calculated loss - """ - if pred.size(1) == 1: - # For binary class segmentation, the shape of pred is - # [N, 1, H, W] and that of label is [N, H, W]. - # As the ignore_index often set as 255, so the - # binary class label check should mask out - # ignore_index - assert label[label != ignore_index].max() <= 1, \ - 'For pred with shape [N, 1, H, W], its label must have at ' \ - 'most 2 classes' - pred = pred.squeeze() - if pred.dim() != label.dim(): - assert (pred.dim() == 2 and label.dim() == 1) or ( - pred.dim() == 4 and label.dim() == 3), \ - 'Only pred shape [N, C], label shape [N] or pred shape [N, C, ' \ - 'H, W], label shape [N, H, W] are supported' - # `weight` returned from `_expand_onehot_labels` - # has been treated for valid (non-ignore) pixels - label, weight, valid_mask = _expand_onehot_labels( - label, weight, pred.shape, ignore_index) - else: - # should mask out the ignored elements - valid_mask = ((label >= 0) & (label != ignore_index)).float() - if weight is not None: - weight = weight * valid_mask - else: - weight = valid_mask - # average loss over non-ignored and valid elements - if reduction == 'mean' and avg_factor is None and avg_non_ignore: - avg_factor = valid_mask.sum().item() - - loss = F.binary_cross_entropy_with_logits( - pred, label.float(), pos_weight=class_weight, reduction='none') - # do the reduction for the weighted loss - loss = weight_reduce_loss( - loss, weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def mask_cross_entropy(pred, - target, - label, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=None, - **kwargs): - """Calculate the CrossEntropy loss for masks. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. - label (torch.Tensor): ``label`` indicates the class label of the mask' - corresponding object. This will be used to select the mask in the - of the class which the object belongs to when the mask prediction - if not class-agnostic. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (None): Placeholder, to be consistent with other loss. - Default: None. - - Returns: - torch.Tensor: The calculated loss - """ - assert ignore_index is None, 'BCE loss does not support ignore_index' - # TODO: handle these two reserved arguments - assert reduction == 'mean' and avg_factor is None - num_rois = pred.size()[0] - inds = torch.arange(0, num_rois, dtype=torch.long, device=pred.device) - pred_slice = pred[inds, label].squeeze(1) - return F.binary_cross_entropy_with_logits( - pred_slice, target, weight=class_weight, reduction='mean')[None] - - -@LOSSES.register_module() -class CrossEntropyLoss(nn.Module): - """CrossEntropyLoss. - - Args: - use_sigmoid (bool, optional): Whether the prediction uses sigmoid - of softmax. Defaults to False. - use_mask (bool, optional): Whether to use mask cross entropy loss. - Defaults to False. - reduction (str, optional): . Defaults to 'mean'. - Options are "none", "mean" and "sum". - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - """ - - def __init__(self, - use_sigmoid=False, - use_mask=False, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_ce', - avg_non_ignore=False): - super(CrossEntropyLoss, self).__init__() - assert (use_sigmoid is False) or (use_mask is False) - self.use_sigmoid = use_sigmoid - self.use_mask = use_mask - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = get_class_weight(class_weight) - self.avg_non_ignore = avg_non_ignore - if not self.avg_non_ignore and self.reduction == 'mean': - warnings.warn( - 'Default ``avg_non_ignore`` is False, if you would like to ' - 'ignore the certain label and average loss over non-ignore ' - 'labels, which is the same with PyTorch official ' - 'cross_entropy, set ``avg_non_ignore=True``.') - - if self.use_sigmoid: - self.cls_criterion = binary_cross_entropy - elif self.use_mask: - self.cls_criterion = mask_cross_entropy - else: - self.cls_criterion = cross_entropy - self._loss_name = loss_name - - def extra_repr(self): - """Extra repr.""" - s = f'avg_non_ignore={self.avg_non_ignore}' - return s - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - ignore_index=-100, - **kwargs): - """Forward function.""" - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = cls_score.new_tensor(self.class_weight) - else: - class_weight = None - # Note: for BCE loss, label < 0 is invalid. - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - weight, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - avg_non_ignore=self.avg_non_ignore, - ignore_index=ignore_index, - **kwargs) - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/dice_loss.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/dice_loss.py deleted file mode 100644 index 79a3abfc2..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/dice_loss.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/LikeLy-Journey/SegmenTron/blob/master/ -segmentron/solver/loss.py (Apache-2.0 License)""" -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weighted_loss - - -@weighted_loss -def dice_loss(pred, - target, - valid_mask, - smooth=1, - exponent=2, - class_weight=None, - ignore_index=255): - assert pred.shape[0] == target.shape[0] - total_loss = 0 - num_classes = pred.shape[1] - for i in range(num_classes): - if i != ignore_index: - dice_loss = binary_dice_loss( - pred[:, i], - target[..., i], - valid_mask=valid_mask, - smooth=smooth, - exponent=exponent) - if class_weight is not None: - dice_loss *= class_weight[i] - total_loss += dice_loss - return total_loss / num_classes - - -@weighted_loss -def binary_dice_loss(pred, target, valid_mask, smooth=1, exponent=2, **kwards): - assert pred.shape[0] == target.shape[0] - pred = pred.reshape(pred.shape[0], -1) - target = target.reshape(target.shape[0], -1) - valid_mask = valid_mask.reshape(valid_mask.shape[0], -1) - - num = torch.sum(torch.mul(pred, target) * valid_mask, dim=1) * 2 + smooth - den = torch.sum(pred.pow(exponent) + target.pow(exponent), dim=1) + smooth - - return 1 - num / den - - -@LOSSES.register_module() -class DiceLoss(nn.Module): - """DiceLoss. - - This loss is proposed in `V-Net: Fully Convolutional Neural Networks for - Volumetric Medical Image Segmentation `_. - - Args: - smooth (float): A float number to smooth loss, and avoid NaN error. - Default: 1 - exponent (float): An float number to calculate denominator - value: \\sum{x^exponent} + \\sum{y^exponent}. Default: 2. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Default to 1.0. - ignore_index (int | None): The label index to be ignored. Default: 255. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_dice'. - """ - - def __init__(self, - smooth=1, - exponent=2, - reduction='mean', - class_weight=None, - loss_weight=1.0, - ignore_index=255, - loss_name='loss_dice', - **kwards): - super(DiceLoss, self).__init__() - self.smooth = smooth - self.exponent = exponent - self.reduction = reduction - self.class_weight = get_class_weight(class_weight) - self.loss_weight = loss_weight - self.ignore_index = ignore_index - self._loss_name = loss_name - - def forward(self, - pred, - target, - avg_factor=None, - reduction_override=None, - **kwards): - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = pred.new_tensor(self.class_weight) - else: - class_weight = None - - pred = F.softmax(pred, dim=1) - num_classes = pred.shape[1] - one_hot_target = F.one_hot( - torch.clamp(target.long(), 0, num_classes - 1), - num_classes=num_classes) - valid_mask = (target != self.ignore_index).long() - - loss = self.loss_weight * dice_loss( - pred, - one_hot_target, - valid_mask=valid_mask, - reduction=reduction, - avg_factor=avg_factor, - smooth=self.smooth, - exponent=self.exponent, - class_weight=class_weight, - ignore_index=self.ignore_index) - return loss - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/focal_loss.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/focal_loss.py deleted file mode 100644 index af1c711df..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/focal_loss.py +++ /dev/null @@ -1,327 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Modified from https://github.com/open-mmlab/mmdetection -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.ops import sigmoid_focal_loss as _sigmoid_focal_loss - -from ..builder import LOSSES -from .utils import weight_reduce_loss - - -# This method is used when cuda is not available -def py_sigmoid_focal_loss(pred, - target, - one_hot_target=None, - weight=None, - gamma=2.0, - alpha=0.5, - class_weight=None, - valid_mask=None, - reduction='mean', - avg_factor=None): - """PyTorch version of `Focal Loss `_. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the - number of classes - target (torch.Tensor): The learning label of the prediction with - shape (N, C) - one_hot_target (None): Placeholder. It should be None. - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal Loss. - Defaults to 0.5. - class_weight (list[float], optional): Weight of each class. - Defaults to None. - valid_mask (torch.Tensor, optional): A mask uses 1 to mark the valid - samples and uses 0 to mark the ignored samples. Default: None. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - if isinstance(alpha, list): - alpha = pred.new_tensor(alpha) - pred_sigmoid = pred.sigmoid() - target = target.type_as(pred) - one_minus_pt = (1 - pred_sigmoid) * target + pred_sigmoid * (1 - target) - focal_weight = (alpha * target + (1 - alpha) * - (1 - target)) * one_minus_pt.pow(gamma) - - loss = F.binary_cross_entropy_with_logits( - pred, target, reduction='none') * focal_weight - final_weight = torch.ones(1, pred.size(1)).type_as(loss) - if weight is not None: - if weight.shape != loss.shape and weight.size(0) == loss.size(0): - # For most cases, weight is of shape (N, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - assert weight.dim() == loss.dim() - final_weight = final_weight * weight - if class_weight is not None: - final_weight = final_weight * pred.new_tensor(class_weight) - if valid_mask is not None: - final_weight = final_weight * valid_mask - loss = weight_reduce_loss(loss, final_weight, reduction, avg_factor) - return loss - - -def sigmoid_focal_loss(pred, - target, - one_hot_target, - weight=None, - gamma=2.0, - alpha=0.5, - class_weight=None, - valid_mask=None, - reduction='mean', - avg_factor=None): - r"""A warpper of cuda version `Focal Loss - `_. - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. It's shape - should be (N, ) - one_hot_target (torch.Tensor): The learning label with shape (N, C) - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal Loss. - Defaults to 0.5. - class_weight (list[float], optional): Weight of each class. - Defaults to None. - valid_mask (torch.Tensor, optional): A mask uses 1 to mark the valid - samples and uses 0 to mark the ignored samples. Default: None. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - # Function.apply does not accept keyword arguments, so the decorator - # "weighted_loss" is not applicable - final_weight = torch.ones(1, pred.size(1)).type_as(pred) - if isinstance(alpha, list): - # _sigmoid_focal_loss doesn't accept alpha of list type. Therefore, if - # a list is given, we set the input alpha as 0.5. This means setting - # equal weight for foreground class and background class. By - # multiplying the loss by 2, the effect of setting alpha as 0.5 is - # undone. The alpha of type list is used to regulate the loss in the - # post-processing process. - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), - gamma, 0.5, None, 'none') * 2 - alpha = pred.new_tensor(alpha) - final_weight = final_weight * ( - alpha * one_hot_target + (1 - alpha) * (1 - one_hot_target)) - else: - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), - gamma, alpha, None, 'none') - if weight is not None: - if weight.shape != loss.shape and weight.size(0) == loss.size(0): - # For most cases, weight is of shape (N, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - assert weight.dim() == loss.dim() - final_weight = final_weight * weight - if class_weight is not None: - final_weight = final_weight * pred.new_tensor(class_weight) - if valid_mask is not None: - final_weight = final_weight * valid_mask - loss = weight_reduce_loss(loss, final_weight, reduction, avg_factor) - return loss - - -@LOSSES.register_module() -class FocalLoss(nn.Module): - - def __init__(self, - use_sigmoid=True, - gamma=2.0, - alpha=0.5, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_focal'): - """`Focal Loss `_ - Args: - use_sigmoid (bool, optional): Whether to the prediction is - used for sigmoid or softmax. Defaults to True. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal - Loss. Defaults to 0.5. When a list is provided, the length - of the list should be equal to the number of classes. - Please be careful that this parameter is not the - class-wise weight but the weight of a binary classification - problem. This binary classification problem regards the - pixels which belong to one class as the foreground - and the other pixels as the background, each element in - the list is the weight of the corresponding foreground class. - The value of alpha or each element of alpha should be a float - in the interval [0, 1]. If you want to specify the class-wise - weight, please use `class_weight` parameter. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and - "sum". - class_weight (list[float], optional): Weight of each class. - Defaults to None. - loss_weight (float, optional): Weight of loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this - loss item to be included into the backward graph, `loss_` must - be the prefix of the name. Defaults to 'loss_focal'. - """ - super(FocalLoss, self).__init__() - assert use_sigmoid is True, \ - 'AssertionError: Only sigmoid focal loss supported now.' - assert reduction in ('none', 'mean', 'sum'), \ - "AssertionError: reduction should be 'none', 'mean' or " \ - "'sum'" - assert isinstance(alpha, (float, list)), \ - 'AssertionError: alpha should be of type float' - assert isinstance(gamma, float), \ - 'AssertionError: gamma should be of type float' - assert isinstance(loss_weight, float), \ - 'AssertionError: loss_weight should be of type float' - assert isinstance(loss_name, str), \ - 'AssertionError: loss_name should be of type str' - assert isinstance(class_weight, list) or class_weight is None, \ - 'AssertionError: class_weight must be None or of type list' - self.use_sigmoid = use_sigmoid - self.gamma = gamma - self.alpha = alpha - self.reduction = reduction - self.class_weight = class_weight - self.loss_weight = loss_weight - self._loss_name = loss_name - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - ignore_index=255, - **kwargs): - """Forward function. - - Args: - pred (torch.Tensor): The prediction with shape - (N, C) where C = number of classes, or - (N, C, d_1, d_2, ..., d_K) with K≥1 in the - case of K-dimensional loss. - target (torch.Tensor): The ground truth. If containing class - indices, shape (N) where each value is 0≤targets[i]≤C−1, - or (N, d_1, d_2, ..., d_K) with K≥1 in the case of - K-dimensional loss. If containing class probabilities, - same shape as the input. - weight (torch.Tensor, optional): The weight of loss for each - prediction. Defaults to None. - avg_factor (int, optional): Average factor that is used to - average the loss. Defaults to None. - reduction_override (str, optional): The reduction method used - to override the original reduction method of the loss. - Options are "none", "mean" and "sum". - ignore_index (int, optional): The label index to be ignored. - Default: 255 - Returns: - torch.Tensor: The calculated loss - """ - assert isinstance(ignore_index, int), \ - 'ignore_index must be of type int' - assert reduction_override in (None, 'none', 'mean', 'sum'), \ - "AssertionError: reduction should be 'none', 'mean' or " \ - "'sum'" - assert pred.shape == target.shape or \ - (pred.size(0) == target.size(0) and - pred.shape[2:] == target.shape[1:]), \ - "The shape of pred doesn't match the shape of target" - - original_shape = pred.shape - - # [B, C, d_1, d_2, ..., d_k] -> [C, B, d_1, d_2, ..., d_k] - pred = pred.transpose(0, 1) - # [C, B, d_1, d_2, ..., d_k] -> [C, N] - pred = pred.reshape(pred.size(0), -1) - # [C, N] -> [N, C] - pred = pred.transpose(0, 1).contiguous() - - if original_shape == target.shape: - # target with shape [B, C, d_1, d_2, ...] - # transform it's shape into [N, C] - # [B, C, d_1, d_2, ...] -> [C, B, d_1, d_2, ..., d_k] - target = target.transpose(0, 1) - # [C, B, d_1, d_2, ..., d_k] -> [C, N] - target = target.reshape(target.size(0), -1) - # [C, N] -> [N, C] - target = target.transpose(0, 1).contiguous() - else: - # target with shape [B, d_1, d_2, ...] - # transform it's shape into [N, ] - target = target.view(-1).contiguous() - valid_mask = (target != ignore_index).view(-1, 1) - # avoid raising error when using F.one_hot() - target = torch.where(target == ignore_index, target.new_tensor(0), - target) - - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.use_sigmoid: - num_classes = pred.size(1) - if torch.cuda.is_available() and pred.is_cuda: - if target.dim() == 1: - one_hot_target = F.one_hot(target, num_classes=num_classes) - else: - one_hot_target = target - target = target.argmax(dim=1) - valid_mask = (target != ignore_index).view(-1, 1) - calculate_loss_func = sigmoid_focal_loss - else: - one_hot_target = None - if target.dim() == 1: - target = F.one_hot(target, num_classes=num_classes) - else: - valid_mask = (target.argmax(dim=1) != ignore_index).view( - -1, 1) - calculate_loss_func = py_sigmoid_focal_loss - - loss_cls = self.loss_weight * calculate_loss_func( - pred, - target, - one_hot_target, - weight, - gamma=self.gamma, - alpha=self.alpha, - class_weight=self.class_weight, - valid_mask=valid_mask, - reduction=reduction, - avg_factor=avg_factor) - - if reduction == 'none': - # [N, C] -> [C, N] - loss_cls = loss_cls.transpose(0, 1) - # [C, N] -> [C, B, d1, d2, ...] - # original_shape: [B, C, d1, d2, ...] - loss_cls = loss_cls.reshape(original_shape[1], - original_shape[0], - *original_shape[2:]) - # [C, B, d1, d2, ...] -> [B, C, d1, d2, ...] - loss_cls = loss_cls.transpose(0, 1).contiguous() - else: - raise NotImplementedError - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/lovasz_loss.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/lovasz_loss.py deleted file mode 100644 index 2bb0fad39..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/lovasz_loss.py +++ /dev/null @@ -1,323 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/bermanmaxim/LovaszSoftmax/blob/master/pytor -ch/lovasz_losses.py Lovasz-Softmax and Jaccard hinge loss in PyTorch Maxim -Berman 2018 ESAT-PSI KU Leuven (MIT License)""" - -import mmcv -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weight_reduce_loss - - -def lovasz_grad(gt_sorted): - """Computes gradient of the Lovasz extension w.r.t sorted errors. - - See Alg. 1 in paper. - """ - p = len(gt_sorted) - gts = gt_sorted.sum() - intersection = gts - gt_sorted.float().cumsum(0) - union = gts + (1 - gt_sorted).float().cumsum(0) - jaccard = 1. - intersection / union - if p > 1: # cover 1-pixel case - jaccard[1:p] = jaccard[1:p] - jaccard[0:-1] - return jaccard - - -def flatten_binary_logits(logits, labels, ignore_index=None): - """Flattens predictions in the batch (binary case) Remove labels equal to - 'ignore_index'.""" - logits = logits.view(-1) - labels = labels.view(-1) - if ignore_index is None: - return logits, labels - valid = (labels != ignore_index) - vlogits = logits[valid] - vlabels = labels[valid] - return vlogits, vlabels - - -def flatten_probs(probs, labels, ignore_index=None): - """Flattens predictions in the batch.""" - if probs.dim() == 3: - # assumes output of a sigmoid layer - B, H, W = probs.size() - probs = probs.view(B, 1, H, W) - B, C, H, W = probs.size() - probs = probs.permute(0, 2, 3, 1).contiguous().view(-1, C) # B*H*W, C=P,C - labels = labels.view(-1) - if ignore_index is None: - return probs, labels - valid = (labels != ignore_index) - vprobs = probs[valid.nonzero().squeeze()] - vlabels = labels[valid] - return vprobs, vlabels - - -def lovasz_hinge_flat(logits, labels): - """Binary Lovasz hinge loss. - - Args: - logits (torch.Tensor): [P], logits at each prediction - (between -infty and +infty). - labels (torch.Tensor): [P], binary ground truth labels (0 or 1). - - Returns: - torch.Tensor: The calculated loss. - """ - if len(labels) == 0: - # only void pixels, the gradients should be 0 - return logits.sum() * 0. - signs = 2. * labels.float() - 1. - errors = (1. - logits * signs) - errors_sorted, perm = torch.sort(errors, dim=0, descending=True) - perm = perm.data - gt_sorted = labels[perm] - grad = lovasz_grad(gt_sorted) - loss = torch.dot(F.relu(errors_sorted), grad) - return loss - - -def lovasz_hinge(logits, - labels, - classes='present', - per_image=False, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=255): - """Binary Lovasz hinge loss. - - Args: - logits (torch.Tensor): [B, H, W], logits at each pixel - (between -infty and +infty). - labels (torch.Tensor): [B, H, W], binary ground truth masks (0 or 1). - classes (str | list[int], optional): Placeholder, to be consistent with - other loss. Default: None. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - class_weight (list[float], optional): Placeholder, to be consistent - with other loss. Default: None. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. This parameter only works when per_image is True. - Default: None. - ignore_index (int | None): The label index to be ignored. Default: 255. - - Returns: - torch.Tensor: The calculated loss. - """ - if per_image: - loss = [ - lovasz_hinge_flat(*flatten_binary_logits( - logit.unsqueeze(0), label.unsqueeze(0), ignore_index)) - for logit, label in zip(logits, labels) - ] - loss = weight_reduce_loss( - torch.stack(loss), None, reduction, avg_factor) - else: - loss = lovasz_hinge_flat( - *flatten_binary_logits(logits, labels, ignore_index)) - return loss - - -def lovasz_softmax_flat(probs, labels, classes='present', class_weight=None): - """Multi-class Lovasz-Softmax loss. - - Args: - probs (torch.Tensor): [P, C], class probabilities at each prediction - (between 0 and 1). - labels (torch.Tensor): [P], ground truth labels (between 0 and C - 1). - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - class_weight (list[float], optional): The weight for each class. - Default: None. - - Returns: - torch.Tensor: The calculated loss. - """ - if probs.numel() == 0: - # only void pixels, the gradients should be 0 - return probs * 0. - C = probs.size(1) - losses = [] - class_to_sum = list(range(C)) if classes in ['all', 'present'] else classes - for c in class_to_sum: - fg = (labels == c).float() # foreground for class c - if (classes == 'present' and fg.sum() == 0): - continue - if C == 1: - if len(classes) > 1: - raise ValueError('Sigmoid output possible only with 1 class') - class_pred = probs[:, 0] - else: - class_pred = probs[:, c] - errors = (fg - class_pred).abs() - errors_sorted, perm = torch.sort(errors, 0, descending=True) - perm = perm.data - fg_sorted = fg[perm] - loss = torch.dot(errors_sorted, lovasz_grad(fg_sorted)) - if class_weight is not None: - loss *= class_weight[c] - losses.append(loss) - return torch.stack(losses).mean() - - -def lovasz_softmax(probs, - labels, - classes='present', - per_image=False, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=255): - """Multi-class Lovasz-Softmax loss. - - Args: - probs (torch.Tensor): [B, C, H, W], class probabilities at each - prediction (between 0 and 1). - labels (torch.Tensor): [B, H, W], ground truth labels (between 0 and - C - 1). - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - class_weight (list[float], optional): The weight for each class. - Default: None. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. This parameter only works when per_image is True. - Default: None. - ignore_index (int | None): The label index to be ignored. Default: 255. - - Returns: - torch.Tensor: The calculated loss. - """ - - if per_image: - loss = [ - lovasz_softmax_flat( - *flatten_probs( - prob.unsqueeze(0), label.unsqueeze(0), ignore_index), - classes=classes, - class_weight=class_weight) - for prob, label in zip(probs, labels) - ] - loss = weight_reduce_loss( - torch.stack(loss), None, reduction, avg_factor) - else: - loss = lovasz_softmax_flat( - *flatten_probs(probs, labels, ignore_index), - classes=classes, - class_weight=class_weight) - return loss - - -@LOSSES.register_module() -class LovaszLoss(nn.Module): - """LovaszLoss. - - This loss is proposed in `The Lovasz-Softmax loss: A tractable surrogate - for the optimization of the intersection-over-union measure in neural - networks `_. - - Args: - loss_type (str, optional): Binary or multi-class loss. - Default: 'multi_class'. Options are "binary" and "multi_class". - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_lovasz'. - """ - - def __init__(self, - loss_type='multi_class', - classes='present', - per_image=False, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_lovasz'): - super(LovaszLoss, self).__init__() - assert loss_type in ('binary', 'multi_class'), "loss_type should be \ - 'binary' or 'multi_class'." - - if loss_type == 'binary': - self.cls_criterion = lovasz_hinge - else: - self.cls_criterion = lovasz_softmax - assert classes in ('all', 'present') or mmcv.is_list_of(classes, int) - if not per_image: - assert reduction == 'none', "reduction should be 'none' when \ - per_image is False." - - self.classes = classes - self.per_image = per_image - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = get_class_weight(class_weight) - self._loss_name = loss_name - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - """Forward function.""" - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = cls_score.new_tensor(self.class_weight) - else: - class_weight = None - - # if multi-class loss, transform logits to probs - if self.cls_criterion == lovasz_softmax: - cls_score = F.softmax(cls_score, dim=1) - - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - self.classes, - self.per_image, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/utils.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/utils.py deleted file mode 100644 index 621f57c74..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/losses/utils.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import mmcv -import numpy as np -import torch -import torch.nn.functional as F - - -def get_class_weight(class_weight): - """Get class weight for loss function. - - Args: - class_weight (list[float] | str | None): If class_weight is a str, - take it as a file name and read from it. - """ - if isinstance(class_weight, str): - # take it as a file path - if class_weight.endswith('.npy'): - class_weight = np.load(class_weight) - else: - # pkl, json or yaml - class_weight = mmcv.load(class_weight) - - return class_weight - - -def reduce_loss(loss, reduction): - """Reduce loss as specified. - - Args: - loss (Tensor): Elementwise loss tensor. - reduction (str): Options are "none", "mean" and "sum". - - Return: - Tensor: Reduced loss tensor. - """ - reduction_enum = F._Reduction.get_enum(reduction) - # none: 0, elementwise_mean:1, sum: 2 - if reduction_enum == 0: - return loss - elif reduction_enum == 1: - return loss.mean() - elif reduction_enum == 2: - return loss.sum() - - -def weight_reduce_loss(loss, weight=None, reduction='mean', avg_factor=None): - """Apply element-wise weight and reduce loss. - - Args: - loss (Tensor): Element-wise loss. - weight (Tensor): Element-wise weights. - reduction (str): Same as built-in losses of PyTorch. - avg_factor (float): Average factor when computing the mean of losses. - - Returns: - Tensor: Processed loss values. - """ - # if weight is specified, apply element-wise weight - if weight is not None: - assert weight.dim() == loss.dim() - if weight.dim() > 1: - assert weight.size(1) == 1 or weight.size(1) == loss.size(1) - loss = loss * weight - - # if avg_factor is not specified, just reduce the loss - if avg_factor is None: - loss = reduce_loss(loss, reduction) - else: - # if reduction is mean, then average the loss by avg_factor - if reduction == 'mean': - # Avoid causing ZeroDivisionError when avg_factor is 0.0, - # i.e., all labels of an image belong to ignore index. - eps = torch.finfo(torch.float32).eps - loss = loss.sum() / (avg_factor + eps) - # if reduction is 'none', then do nothing, otherwise raise an error - elif reduction != 'none': - raise ValueError('avg_factor can not be used with reduction="sum"') - return loss - - -def weighted_loss(loss_func): - """Create a weighted version of a given loss function. - - To use this decorator, the loss function must have the signature like - `loss_func(pred, target, **kwargs)`. The function only needs to compute - element-wise loss without any reduction. This decorator will add weight - and reduction arguments to the function. The decorated function will have - the signature like `loss_func(pred, target, weight=None, reduction='mean', - avg_factor=None, **kwargs)`. - - :Example: - - >>> import torch - >>> @weighted_loss - >>> def l1_loss(pred, target): - >>> return (pred - target).abs() - - >>> pred = torch.Tensor([0, 2, 3]) - >>> target = torch.Tensor([1, 1, 1]) - >>> weight = torch.Tensor([1, 0, 1]) - - >>> l1_loss(pred, target) - tensor(1.3333) - >>> l1_loss(pred, target, weight) - tensor(1.) - >>> l1_loss(pred, target, reduction='none') - tensor([1., 1., 2.]) - >>> l1_loss(pred, target, weight, avg_factor=2) - tensor(1.5000) - """ - - @functools.wraps(loss_func) - def wrapper(pred, - target, - weight=None, - reduction='mean', - avg_factor=None, - **kwargs): - # get element-wise loss - loss = loss_func(pred, target, **kwargs) - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - return wrapper diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/__init__.py deleted file mode 100644 index ff03186a9..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .featurepyramid import Feature2Pyramid -from .fpn import FPN -from .ic_neck import ICNeck -from .jpu import JPU -from .mla_neck import MLANeck -from .multilevel_neck import MultiLevelNeck - -__all__ = [ - 'FPN', 'MultiLevelNeck', 'MLANeck', 'ICNeck', 'JPU', 'Feature2Pyramid' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/featurepyramid.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/featurepyramid.py deleted file mode 100644 index 82a00ceb1..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/featurepyramid.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import build_norm_layer - -from ..builder import NECKS - - -@NECKS.register_module() -class Feature2Pyramid(nn.Module): - """Feature2Pyramid. - - A neck structure connect ViT backbone and decoder_heads. - - Args: - embed_dims (int): Embedding dimension. - rescales (list[float]): Different sampling multiples were - used to obtain pyramid features. Default: [4, 2, 1, 0.5]. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='SyncBN', requires_grad=True). - """ - - def __init__(self, - embed_dim, - rescales=[4, 2, 1, 0.5], - norm_cfg=dict(type='SyncBN', requires_grad=True)): - super(Feature2Pyramid, self).__init__() - self.rescales = rescales - self.upsample_4x = None - for k in self.rescales: - if k == 4: - self.upsample_4x = nn.Sequential( - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2), - build_norm_layer(norm_cfg, embed_dim)[1], - nn.GELU(), - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2), - ) - elif k == 2: - self.upsample_2x = nn.Sequential( - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2)) - elif k == 1: - self.identity = nn.Identity() - elif k == 0.5: - self.downsample_2x = nn.MaxPool2d(kernel_size=2, stride=2) - elif k == 0.25: - self.downsample_4x = nn.MaxPool2d(kernel_size=4, stride=4) - else: - raise KeyError(f'invalid {k} for feature2pyramid') - - def forward(self, inputs): - assert len(inputs) == len(self.rescales) - outputs = [] - if self.upsample_4x is not None: - ops = [ - self.upsample_4x, self.upsample_2x, self.identity, - self.downsample_2x - ] - else: - ops = [ - self.upsample_2x, self.identity, self.downsample_2x, - self.downsample_4x - ] - for i in range(len(inputs)): - outputs.append(ops[i](inputs[i])) - return tuple(outputs) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/fpn.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/fpn.py deleted file mode 100644 index 6997de9d4..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/fpn.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule, auto_fp16 - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class FPN(BaseModule): - """Feature Pyramid Network. - - This neck is the implementation of `Feature Pyramid Networks for Object - Detection `_. - - Args: - in_channels (list[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - num_outs (int): Number of output scales. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - add_extra_convs (bool | str): If bool, it decides whether to add conv - layers on top of the original feature maps. Default to False. - If True, its actual mode is specified by `extra_convs_on_inputs`. - If str, it specifies the source feature map of the extra convs. - Only the following options are allowed - - - 'on_input': Last feat map of neck inputs (i.e. backbone feature). - - 'on_lateral': Last feature map after lateral convs. - - 'on_output': The last output feature map after fpn convs. - extra_convs_on_inputs (bool, deprecated): Whether to apply extra convs - on the original feature from the backbone. If True, - it is equivalent to `add_extra_convs='on_input'`. If False, it is - equivalent to set `add_extra_convs='on_output'`. Default to True. - relu_before_extra_convs (bool): Whether to apply relu before the extra - conv. Default: False. - no_norm_on_lateral (bool): Whether to apply norm on lateral. - Default: False. - conv_cfg (dict): Config dict for convolution layer. Default: None. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - upsample_cfg (dict): Config dict for interpolate layer. - Default: dict(mode='nearest'). - init_cfg (dict or list[dict], optional): Initialization config dict. - - Example: - >>> import torch - >>> in_channels = [2, 3, 5, 7] - >>> scales = [340, 170, 84, 43] - >>> inputs = [torch.rand(1, c, s, s) - ... for c, s in zip(in_channels, scales)] - >>> self = FPN(in_channels, 11, len(in_channels)).eval() - >>> outputs = self.forward(inputs) - >>> for i in range(len(outputs)): - ... print(f'outputs[{i}].shape = {outputs[i].shape}') - outputs[0].shape = torch.Size([1, 11, 340, 340]) - outputs[1].shape = torch.Size([1, 11, 170, 170]) - outputs[2].shape = torch.Size([1, 11, 84, 84]) - outputs[3].shape = torch.Size([1, 11, 43, 43]) - """ - - def __init__(self, - in_channels, - out_channels, - num_outs, - start_level=0, - end_level=-1, - add_extra_convs=False, - extra_convs_on_inputs=False, - relu_before_extra_convs=False, - no_norm_on_lateral=False, - conv_cfg=None, - norm_cfg=None, - act_cfg=None, - upsample_cfg=dict(mode='nearest'), - init_cfg=dict( - type='Xavier', layer='Conv2d', distribution='uniform')): - super(FPN, self).__init__(init_cfg) - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.num_ins = len(in_channels) - self.num_outs = num_outs - self.relu_before_extra_convs = relu_before_extra_convs - self.no_norm_on_lateral = no_norm_on_lateral - self.fp16_enabled = False - self.upsample_cfg = upsample_cfg.copy() - - if end_level == -1: - self.backbone_end_level = self.num_ins - assert num_outs >= self.num_ins - start_level - else: - # if end_level < inputs, no extra level is allowed - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - assert num_outs == end_level - start_level - self.start_level = start_level - self.end_level = end_level - self.add_extra_convs = add_extra_convs - assert isinstance(add_extra_convs, (str, bool)) - if isinstance(add_extra_convs, str): - # Extra_convs_source choices: 'on_input', 'on_lateral', 'on_output' - assert add_extra_convs in ('on_input', 'on_lateral', 'on_output') - elif add_extra_convs: # True - if extra_convs_on_inputs: - # For compatibility with previous release - # TODO: deprecate `extra_convs_on_inputs` - self.add_extra_convs = 'on_input' - else: - self.add_extra_convs = 'on_output' - - self.lateral_convs = nn.ModuleList() - self.fpn_convs = nn.ModuleList() - - for i in range(self.start_level, self.backbone_end_level): - l_conv = ConvModule( - in_channels[i], - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg if not self.no_norm_on_lateral else None, - act_cfg=act_cfg, - inplace=False) - fpn_conv = ConvModule( - out_channels, - out_channels, - 3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - - self.lateral_convs.append(l_conv) - self.fpn_convs.append(fpn_conv) - - # add extra conv layers (e.g., RetinaNet) - extra_levels = num_outs - self.backbone_end_level + self.start_level - if self.add_extra_convs and extra_levels >= 1: - for i in range(extra_levels): - if i == 0 and self.add_extra_convs == 'on_input': - in_channels = self.in_channels[self.backbone_end_level - 1] - else: - in_channels = out_channels - extra_fpn_conv = ConvModule( - in_channels, - out_channels, - 3, - stride=2, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - self.fpn_convs.append(extra_fpn_conv) - - @auto_fp16() - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # build laterals - laterals = [ - lateral_conv(inputs[i + self.start_level]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - - # build top-down path - used_backbone_levels = len(laterals) - for i in range(used_backbone_levels - 1, 0, -1): - # In some cases, fixing `scale factor` (e.g. 2) is preferred, but - # it cannot co-exist with `size` in `F.interpolate`. - if 'scale_factor' in self.upsample_cfg: - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], **self.upsample_cfg) - else: - prev_shape = laterals[i - 1].shape[2:] - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], size=prev_shape, **self.upsample_cfg) - - # build outputs - # part 1: from original levels - outs = [ - self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels) - ] - # part 2: add extra levels - if self.num_outs > len(outs): - # use max pool to get more levels on top of outputs - # (e.g., Faster R-CNN, Mask R-CNN) - if not self.add_extra_convs: - for i in range(self.num_outs - used_backbone_levels): - outs.append(F.max_pool2d(outs[-1], 1, stride=2)) - # add conv layers on top of original feature maps (RetinaNet) - else: - if self.add_extra_convs == 'on_input': - extra_source = inputs[self.backbone_end_level - 1] - elif self.add_extra_convs == 'on_lateral': - extra_source = laterals[-1] - elif self.add_extra_convs == 'on_output': - extra_source = outs[-1] - else: - raise NotImplementedError - outs.append(self.fpn_convs[used_backbone_levels](extra_source)) - for i in range(used_backbone_levels + 1, self.num_outs): - if self.relu_before_extra_convs: - outs.append(self.fpn_convs[i](F.relu(outs[-1]))) - else: - outs.append(self.fpn_convs[i](outs[-1])) - return tuple(outs) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/ic_neck.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/ic_neck.py deleted file mode 100644 index a5d81cef8..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/ic_neck.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -class CascadeFeatureFusion(BaseModule): - """Cascade Feature Fusion Unit in ICNet. - - Args: - low_channels (int): The number of input channels for - low resolution feature map. - high_channels (int): The number of input channels for - high resolution feature map. - out_channels (int): The number of output channels. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Returns: - x (Tensor): The output tensor of shape (N, out_channels, H, W). - x_low (Tensor): The output tensor of shape (N, out_channels, H, W) - for Cascade Label Guidance in auxiliary heads. - """ - - def __init__(self, - low_channels, - high_channels, - out_channels, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(CascadeFeatureFusion, self).__init__(init_cfg=init_cfg) - self.align_corners = align_corners - self.conv_low = ConvModule( - low_channels, - out_channels, - 3, - padding=2, - dilation=2, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.conv_high = ConvModule( - high_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, x_low, x_high): - x_low = resize( - x_low, - size=x_high.size()[2:], - mode='bilinear', - align_corners=self.align_corners) - # Note: Different from original paper, `x_low` is underwent - # `self.conv_low` rather than another 1x1 conv classifier - # before being used for auxiliary head. - x_low = self.conv_low(x_low) - x_high = self.conv_high(x_high) - x = x_low + x_high - x = F.relu(x, inplace=True) - return x, x_low - - -@NECKS.register_module() -class ICNeck(BaseModule): - """ICNet for Real-Time Semantic Segmentation on High-Resolution Images. - - This head is the implementation of `ICHead - `_. - - Args: - in_channels (int): The number of input image channels. Default: 3. - out_channels (int): The numbers of output feature channels. - Default: 128. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(64, 256, 256), - out_channels=128, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(ICNeck, self).__init__(init_cfg=init_cfg) - assert len(in_channels) == 3, 'Length of input channels \ - must be 3!' - - self.in_channels = in_channels - self.out_channels = out_channels - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.align_corners = align_corners - self.cff_24 = CascadeFeatureFusion( - self.in_channels[2], - self.in_channels[1], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - self.cff_12 = CascadeFeatureFusion( - self.out_channels, - self.in_channels[0], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - def forward(self, inputs): - assert len(inputs) == 3, 'Length of input feature \ - maps must be 3!' - - x_sub1, x_sub2, x_sub4 = inputs - x_cff_24, x_24 = self.cff_24(x_sub4, x_sub2) - x_cff_12, x_12 = self.cff_12(x_cff_24, x_sub1) - # Note: `x_cff_12` is used for decode_head, - # `x_24` and `x_12` are used for auxiliary head. - return x_24, x_12, x_cff_12 diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/jpu.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/jpu.py deleted file mode 100644 index 3cc6b9f42..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/jpu.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, DepthwiseSeparableConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class JPU(BaseModule): - """FastFCN: Rethinking Dilated Convolution in the Backbone - for Semantic Segmentation. - - This Joint Pyramid Upsampling (JPU) neck is the implementation of - `FastFCN `_. - - Args: - in_channels (Tuple[int], optional): The number of input channels - for each convolution operations before upsampling. - Default: (512, 1024, 2048). - mid_channels (int): The number of output channels of JPU. - Default: 512. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - dilations (tuple[int]): Dilation rate of each Depthwise - Separable ConvModule. Default: (1, 2, 4, 8). - align_corners (bool, optional): The align_corners argument of - resize operation. Default: False. - conv_cfg (dict | None): Config of conv layers. - Default: None. - norm_cfg (dict | None): Config of norm layers. - Default: dict(type='BN'). - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU'). - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(512, 1024, 2048), - mid_channels=512, - start_level=0, - end_level=-1, - dilations=(1, 2, 4, 8), - align_corners=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(JPU, self).__init__(init_cfg=init_cfg) - assert isinstance(in_channels, tuple) - assert isinstance(dilations, tuple) - self.in_channels = in_channels - self.mid_channels = mid_channels - self.start_level = start_level - self.num_ins = len(in_channels) - if end_level == -1: - self.backbone_end_level = self.num_ins - else: - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - - self.dilations = dilations - self.align_corners = align_corners - - self.conv_layers = nn.ModuleList() - self.dilation_layers = nn.ModuleList() - for i in range(self.start_level, self.backbone_end_level): - conv_layer = nn.Sequential( - ConvModule( - self.in_channels[i], - self.mid_channels, - kernel_size=3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.conv_layers.append(conv_layer) - for i in range(len(dilations)): - dilation_layer = nn.Sequential( - DepthwiseSeparableConvModule( - in_channels=(self.backbone_end_level - self.start_level) * - self.mid_channels, - out_channels=self.mid_channels, - kernel_size=3, - stride=1, - padding=dilations[i], - dilation=dilations[i], - dw_norm_cfg=norm_cfg, - dw_act_cfg=None, - pw_norm_cfg=norm_cfg, - pw_act_cfg=act_cfg)) - self.dilation_layers.append(dilation_layer) - - def forward(self, inputs): - """Forward function.""" - assert len(inputs) == len(self.in_channels), 'Length of inputs must \ - be the same with self.in_channels!' - - feats = [ - self.conv_layers[i - self.start_level](inputs[i]) - for i in range(self.start_level, self.backbone_end_level) - ] - - h, w = feats[0].shape[2:] - for i in range(1, len(feats)): - feats[i] = resize( - feats[i], - size=(h, w), - mode='bilinear', - align_corners=self.align_corners) - - feat = torch.cat(feats, dim=1) - concat_feat = torch.cat([ - self.dilation_layers[i](feat) for i in range(len(self.dilations)) - ], - dim=1) - - outs = [] - - # Default: outs[2] is the output of JPU for decoder head, outs[1] is - # the feature map from backbone for auxiliary head. Additionally, - # outs[0] can also be used for auxiliary head. - for i in range(self.start_level, self.backbone_end_level - 1): - outs.append(inputs[i]) - outs.append(concat_feat) - return tuple(outs) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/mla_neck.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/mla_neck.py deleted file mode 100644 index 1513e296d..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/mla_neck.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, build_norm_layer - -from ..builder import NECKS - - -class MLAModule(nn.Module): - - def __init__(self, - in_channels=[1024, 1024, 1024, 1024], - out_channels=256, - norm_cfg=None, - act_cfg=None): - super(MLAModule, self).__init__() - self.channel_proj = nn.ModuleList() - for i in range(len(in_channels)): - self.channel_proj.append( - ConvModule( - in_channels=in_channels[i], - out_channels=out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.feat_extract = nn.ModuleList() - for i in range(len(in_channels)): - self.feat_extract.append( - ConvModule( - in_channels=out_channels, - out_channels=out_channels, - kernel_size=3, - padding=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, inputs): - - # feat_list -> [p2, p3, p4, p5] - feat_list = [] - for x, conv in zip(inputs, self.channel_proj): - feat_list.append(conv(x)) - - # feat_list -> [p5, p4, p3, p2] - # mid_list -> [m5, m4, m3, m2] - feat_list = feat_list[::-1] - mid_list = [] - for feat in feat_list: - if len(mid_list) == 0: - mid_list.append(feat) - else: - mid_list.append(mid_list[-1] + feat) - - # mid_list -> [m5, m4, m3, m2] - # out_list -> [o2, o3, o4, o5] - out_list = [] - for mid, conv in zip(mid_list, self.feat_extract): - out_list.append(conv(mid)) - - return tuple(out_list) - - -@NECKS.register_module() -class MLANeck(nn.Module): - """Multi-level Feature Aggregation. - - This neck is `The Multi-level Feature Aggregation construction of - SETR `_. - - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - norm_layer (dict): Config dict for input normalization. - Default: norm_layer=dict(type='LN', eps=1e-6, requires_grad=True). - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - norm_layer=dict(type='LN', eps=1e-6, requires_grad=True), - norm_cfg=None, - act_cfg=None): - super(MLANeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - - # In order to build general vision transformer backbone, we have to - # move MLA to neck. - self.norm = nn.ModuleList([ - build_norm_layer(norm_layer, in_channels[i])[1] - for i in range(len(in_channels)) - ]) - - self.mla = MLAModule( - in_channels=in_channels, - out_channels=out_channels, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # Convert from nchw to nlc - outs = [] - for i in range(len(inputs)): - x = inputs[i] - n, c, h, w = x.shape - x = x.reshape(n, c, h * w).transpose(2, 1).contiguous() - x = self.norm[i](x) - x = x.transpose(1, 2).reshape(n, c, h, w).contiguous() - outs.append(x) - - outs = self.mla(outs) - return tuple(outs) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/multilevel_neck.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/multilevel_neck.py deleted file mode 100644 index 5151f8762..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/necks/multilevel_neck.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, xavier_init - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class MultiLevelNeck(nn.Module): - """MultiLevelNeck. - - A neck structure connect vit backbone and decoder_heads. - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - scales (List[float]): Scale factors for each input feature map. - Default: [0.5, 1, 2, 4] - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - scales=[0.5, 1, 2, 4], - norm_cfg=None, - act_cfg=None): - super(MultiLevelNeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.scales = scales - self.num_outs = len(scales) - self.lateral_convs = nn.ModuleList() - self.convs = nn.ModuleList() - for in_channel in in_channels: - self.lateral_convs.append( - ConvModule( - in_channel, - out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - for _ in range(self.num_outs): - self.convs.append( - ConvModule( - out_channels, - out_channels, - kernel_size=3, - padding=1, - stride=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - # default init_weights for conv(msra) and norm in ConvModule - def init_weights(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - xavier_init(m, distribution='uniform') - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - inputs = [ - lateral_conv(inputs[i]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - # for len(inputs) not equal to self.num_outs - if len(inputs) == 1: - inputs = [inputs[0] for _ in range(self.num_outs)] - outs = [] - for i in range(self.num_outs): - x_resize = resize( - inputs[i], scale_factor=self.scales[i], mode='bilinear') - outs.append(self.convs[i](x_resize)) - return tuple(outs) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/__init__.py deleted file mode 100644 index 387c858bd..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseSegmentor -from .cascade_encoder_decoder import CascadeEncoderDecoder -from .encoder_decoder import EncoderDecoder - -__all__ = ['BaseSegmentor', 'EncoderDecoder', 'CascadeEncoderDecoder'] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/base.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/base.py deleted file mode 100644 index 76dc8f075..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/base.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from abc import ABCMeta, abstractmethod -from collections import OrderedDict - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import BaseModule, auto_fp16 - - -class BaseSegmentor(BaseModule, metaclass=ABCMeta): - """Base class for segmentors.""" - - def __init__(self, init_cfg=None): - super(BaseSegmentor, self).__init__(init_cfg) - self.fp16_enabled = False - - @property - def with_neck(self): - """bool: whether the segmentor has neck""" - return hasattr(self, 'neck') and self.neck is not None - - @property - def with_auxiliary_head(self): - """bool: whether the segmentor has auxiliary head""" - return hasattr(self, - 'auxiliary_head') and self.auxiliary_head is not None - - @property - def with_decode_head(self): - """bool: whether the segmentor has decode head""" - return hasattr(self, 'decode_head') and self.decode_head is not None - - @abstractmethod - def extract_feat(self, imgs): - """Placeholder for extract features from images.""" - pass - - @abstractmethod - def encode_decode(self, img, img_metas): - """Placeholder for encode images with backbone and decode into a - semantic segmentation map of the same size as input.""" - pass - - @abstractmethod - def forward_train(self, imgs, img_metas, **kwargs): - """Placeholder for Forward function for training.""" - pass - - @abstractmethod - def simple_test(self, img, img_meta, **kwargs): - """Placeholder for single image test.""" - pass - - @abstractmethod - def aug_test(self, imgs, img_metas, **kwargs): - """Placeholder for augmentation test.""" - pass - - def forward_test(self, imgs, img_metas, **kwargs): - """ - Args: - imgs (List[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains all images in the batch. - img_metas (List[List[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. - """ - for var, name in [(imgs, 'imgs'), (img_metas, 'img_metas')]: - if not isinstance(var, list): - raise TypeError(f'{name} must be a list, but got ' - f'{type(var)}') - - num_augs = len(imgs) - if num_augs != len(img_metas): - raise ValueError(f'num of augmentations ({len(imgs)}) != ' - f'num of image meta ({len(img_metas)})') - # all images in the same aug batch all of the same ori_shape and pad - # shape - for img_meta in img_metas: - ori_shapes = [_['ori_shape'] for _ in img_meta] - assert all(shape == ori_shapes[0] for shape in ori_shapes) - img_shapes = [_['img_shape'] for _ in img_meta] - assert all(shape == img_shapes[0] for shape in img_shapes) - pad_shapes = [_['pad_shape'] for _ in img_meta] - assert all(shape == pad_shapes[0] for shape in pad_shapes) - - if num_augs == 1: - return self.simple_test(imgs[0], img_metas[0], **kwargs) - else: - return self.aug_test(imgs, img_metas, **kwargs) - - @auto_fp16(apply_to=('img', )) - def forward(self, img, img_metas, return_loss=True, **kwargs): - """Calls either :func:`forward_train` or :func:`forward_test` depending - on whether ``return_loss`` is ``True``. - - Note this setting will change the expected inputs. When - ``return_loss=True``, img and img_meta are single-nested (i.e. Tensor - and List[dict]), and when ``resturn_loss=False``, img and img_meta - should be double nested (i.e. List[Tensor], List[List[dict]]), with - the outer list indicating test time augmentations. - """ - if return_loss: - return self.forward_train(img, img_metas, **kwargs) - else: - return self.forward_test(img, img_metas, **kwargs) - - def train_step(self, data_batch, optimizer, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating is also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer` | dict): The optimizer of - runner is passed to ``train_step()``. This argument is unused - and reserved. - - Returns: - dict: It should contain at least 3 keys: ``loss``, ``log_vars``, - ``num_samples``. - ``loss`` is a tensor for back propagation, which can be a - weighted sum of multiple losses. - ``log_vars`` contains all the variables to be sent to the - logger. - ``num_samples`` indicates the batch size (when the model is - DDP, it means the batch size on each GPU), which is used for - averaging the logs. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, - log_vars=log_vars, - num_samples=len(data_batch['img_metas'])) - - return outputs - - def val_step(self, data_batch, optimizer=None, **kwargs): - """The iteration step during validation. - - This method shares the same signature as :func:`train_step`, but used - during val epochs. Note that the evaluation after training epochs is - not implemented with this method, but an evaluation hook. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - log_vars_ = dict() - for loss_name, loss_value in log_vars.items(): - k = loss_name + '_val' - log_vars_[k] = loss_value - - outputs = dict( - loss=loss, - log_vars=log_vars_, - num_samples=len(data_batch['img_metas'])) - - return outputs - - @staticmethod - def _parse_losses(losses): - """Parse the raw outputs (losses) of the network. - - Args: - losses (dict): Raw output of the network, which usually contain - losses and other necessary information. - - Returns: - tuple[Tensor, dict]: (loss, log_vars), loss is the loss tensor - which may be a weighted sum of all losses, log_vars contains - all the variables to be sent to the logger. - """ - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(_value for _key, _value in log_vars.items() - if 'loss' in _key) - - # If the loss_vars has different length, raise assertion error - # to prevent GPUs from infinite waiting. - if dist.is_available() and dist.is_initialized(): - log_var_length = torch.tensor(len(log_vars), device=loss.device) - dist.all_reduce(log_var_length) - message = (f'rank {dist.get_rank()}' + - f' len(log_vars): {len(log_vars)}' + ' keys: ' + - ','.join(log_vars.keys()) + '\n') - assert log_var_length == len(log_vars) * dist.get_world_size(), \ - 'loss log variables are different across GPUs!\n' + message - - log_vars['loss'] = loss - for loss_name, loss_value in log_vars.items(): - # reduce loss when distributed training - if dist.is_available() and dist.is_initialized(): - loss_value = loss_value.data.clone() - dist.all_reduce(loss_value.div_(dist.get_world_size())) - log_vars[loss_name] = loss_value.item() - - return loss, log_vars - - def show_result(self, - img, - result, - palette=None, - win_name='', - show=False, - wait_time=0, - out_file=None, - opacity=0.5): - """Draw `result` over `img`. - - Args: - img (str or Tensor): The image to be displayed. - result (Tensor): The semantic segmentation results to draw over - `img`. - palette (list[list[int]]] | np.ndarray | None): The palette of - segmentation map. If None is given, random palette will be - generated. Default: None - win_name (str): The window name. - wait_time (int): Value of waitKey param. - Default: 0. - show (bool): Whether to show the image. - Default: False. - out_file (str or None): The filename to write the image. - Default: None. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - Returns: - img (Tensor): Only if not `show` or `out_file` - """ - img = mmcv.imread(img) - img = img.copy() - seg = result[0] - if palette is None: - if self.PALETTE is None: - # Get random state before set seed, - # and restore random state later. - # It will prevent loss of randomness, as the palette - # may be different in each iteration if not specified. - # See: https://github.com/open-mmlab/mmdetection/issues/5844 - state = np.random.get_state() - np.random.seed(42) - # random palette - palette = np.random.randint( - 0, 255, size=(len(self.CLASSES), 3)) - np.random.set_state(state) - else: - palette = self.PALETTE - palette = np.array(palette) - assert palette.shape[0] == len(self.CLASSES) - assert palette.shape[1] == 3 - assert len(palette.shape) == 2 - assert 0 < opacity <= 1.0 - color_seg = np.zeros((seg.shape[0], seg.shape[1], 3), dtype=np.uint8) - for label, color in enumerate(palette): - color_seg[seg == label, :] = color - # convert to BGR - color_seg = color_seg[..., ::-1] - - img = img * (1 - opacity) + color_seg * opacity - img = img.astype(np.uint8) - # if out_file specified, do not show image in window - if out_file is not None: - show = False - - if show: - mmcv.imshow(img, win_name, wait_time) - if out_file is not None: - mmcv.imwrite(img, out_file) - - if not (show or out_file): - warnings.warn('show==False and out_file is not specified, only ' - 'result image will be returned') - return img diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py deleted file mode 100644 index 1913a22e2..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from mmseg.core import add_prefix -from mmseg.ops import resize -from .. import builder -from ..builder import SEGMENTORS -from .encoder_decoder import EncoderDecoder - - -@SEGMENTORS.register_module() -class CascadeEncoderDecoder(EncoderDecoder): - """Cascade Encoder Decoder segmentors. - - CascadeEncoderDecoder almost the same as EncoderDecoder, while decoders of - CascadeEncoderDecoder are cascaded. The output of previous decoder_head - will be the input of next decoder_head. - """ - - def __init__(self, - num_stages, - backbone, - decode_head, - neck=None, - auxiliary_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - self.num_stages = num_stages - super(CascadeEncoderDecoder, self).__init__( - backbone=backbone, - decode_head=decode_head, - neck=neck, - auxiliary_head=auxiliary_head, - train_cfg=train_cfg, - test_cfg=test_cfg, - pretrained=pretrained, - init_cfg=init_cfg) - - def _init_decode_head(self, decode_head): - """Initialize ``decode_head``""" - assert isinstance(decode_head, list) - assert len(decode_head) == self.num_stages - self.decode_head = nn.ModuleList() - for i in range(self.num_stages): - self.decode_head.append(builder.build_head(decode_head[i])) - self.align_corners = self.decode_head[-1].align_corners - self.num_classes = self.decode_head[-1].num_classes - - def encode_decode(self, img, img_metas): - """Encode images with backbone and decode into a semantic segmentation - map of the same size as input.""" - x = self.extract_feat(img) - out = self.decode_head[0].forward_test(x, img_metas, self.test_cfg) - for i in range(1, self.num_stages): - out = self.decode_head[i].forward_test(x, out, img_metas, - self.test_cfg) - out = resize( - input=out, - size=img.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - return out - - def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for decode head in - training.""" - losses = dict() - - loss_decode = self.decode_head[0].forward_train( - x, img_metas, gt_semantic_seg, self.train_cfg) - - losses.update(add_prefix(loss_decode, 'decode_0')) - - for i in range(1, self.num_stages): - # forward test again, maybe unnecessary for most methods. - if i == 1: - prev_outputs = self.decode_head[0].forward_test( - x, img_metas, self.test_cfg) - else: - prev_outputs = self.decode_head[i - 1].forward_test( - x, prev_outputs, img_metas, self.test_cfg) - loss_decode = self.decode_head[i].forward_train( - x, prev_outputs, img_metas, gt_semantic_seg, self.train_cfg) - losses.update(add_prefix(loss_decode, f'decode_{i}')) - - return losses diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/encoder_decoder.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/encoder_decoder.py deleted file mode 100644 index 72467b469..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/segmentors/encoder_decoder.py +++ /dev/null @@ -1,284 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmseg.core import add_prefix -from mmseg.ops import resize -from .. import builder -from ..builder import SEGMENTORS -from .base import BaseSegmentor - - -@SEGMENTORS.register_module() -class EncoderDecoder(BaseSegmentor): - """Encoder Decoder segmentors. - - EncoderDecoder typically consists of backbone, decode_head, auxiliary_head. - Note that auxiliary_head is only used for deep supervision during training, - which could be dumped during inference. - """ - - def __init__(self, - backbone, - decode_head, - neck=None, - auxiliary_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - super(EncoderDecoder, self).__init__(init_cfg) - if pretrained is not None: - assert backbone.get('pretrained') is None, \ - 'both backbone and segmentor set pretrained weight' - backbone.pretrained = pretrained - self.backbone = builder.build_backbone(backbone) - if neck is not None: - self.neck = builder.build_neck(neck) - self._init_decode_head(decode_head) - self._init_auxiliary_head(auxiliary_head) - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - assert self.with_decode_head - - def _init_decode_head(self, decode_head): - """Initialize ``decode_head``""" - self.decode_head = builder.build_head(decode_head) - self.align_corners = self.decode_head.align_corners - self.num_classes = self.decode_head.num_classes - - def _init_auxiliary_head(self, auxiliary_head): - """Initialize ``auxiliary_head``""" - if auxiliary_head is not None: - if isinstance(auxiliary_head, list): - self.auxiliary_head = nn.ModuleList() - for head_cfg in auxiliary_head: - self.auxiliary_head.append(builder.build_head(head_cfg)) - else: - self.auxiliary_head = builder.build_head(auxiliary_head) - - def extract_feat(self, img): - """Extract features from images.""" - x = self.backbone(img) - if self.with_neck: - x = self.neck(x) - return x - - def encode_decode(self, img, img_metas): - """Encode images with backbone and decode into a semantic segmentation - map of the same size as input.""" - x = self.extract_feat(img) - out = self._decode_head_forward_test(x, img_metas) - out = resize( - input=out, - size=img.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - return out - - def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for decode head in - training.""" - losses = dict() - loss_decode = self.decode_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - - losses.update(add_prefix(loss_decode, 'decode')) - return losses - - def _decode_head_forward_test(self, x, img_metas): - """Run forward function and calculate loss for decode head in - inference.""" - seg_logits = self.decode_head.forward_test(x, img_metas, self.test_cfg) - return seg_logits - - def _auxiliary_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for auxiliary head in - training.""" - losses = dict() - if isinstance(self.auxiliary_head, nn.ModuleList): - for idx, aux_head in enumerate(self.auxiliary_head): - loss_aux = aux_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - losses.update(add_prefix(loss_aux, f'aux_{idx}')) - else: - loss_aux = self.auxiliary_head.forward_train( - x, img_metas, gt_semantic_seg, self.train_cfg) - losses.update(add_prefix(loss_aux, 'aux')) - - return losses - - def forward_dummy(self, img): - """Dummy forward function.""" - seg_logit = self.encode_decode(img, None) - - return seg_logit - - def forward_train(self, img, img_metas, gt_semantic_seg): - """Forward function for training. - - Args: - img (Tensor): Input images. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - - x = self.extract_feat(img) - - losses = dict() - - loss_decode = self._decode_head_forward_train(x, img_metas, - gt_semantic_seg) - losses.update(loss_decode) - - if self.with_auxiliary_head: - loss_aux = self._auxiliary_head_forward_train( - x, img_metas, gt_semantic_seg) - losses.update(loss_aux) - - return losses - - # TODO refactor - def slide_inference(self, img, img_meta, rescale): - """Inference by sliding-window with overlap. - - If h_crop > h_img or w_crop > w_img, the small patch will be used to - decode without padding. - """ - - h_stride, w_stride = self.test_cfg.stride - h_crop, w_crop = self.test_cfg.crop_size - batch_size, _, h_img, w_img = img.size() - num_classes = self.num_classes - h_grids = max(h_img - h_crop + h_stride - 1, 0) // h_stride + 1 - w_grids = max(w_img - w_crop + w_stride - 1, 0) // w_stride + 1 - preds = img.new_zeros((batch_size, num_classes, h_img, w_img)) - count_mat = img.new_zeros((batch_size, 1, h_img, w_img)) - for h_idx in range(h_grids): - for w_idx in range(w_grids): - y1 = h_idx * h_stride - x1 = w_idx * w_stride - y2 = min(y1 + h_crop, h_img) - x2 = min(x1 + w_crop, w_img) - y1 = max(y2 - h_crop, 0) - x1 = max(x2 - w_crop, 0) - crop_img = img[:, :, y1:y2, x1:x2] - crop_seg_logit = self.encode_decode(crop_img, img_meta) - preds += F.pad(crop_seg_logit, - (int(x1), int(preds.shape[3] - x2), int(y1), - int(preds.shape[2] - y2))) - - count_mat[:, :, y1:y2, x1:x2] += 1 - assert (count_mat == 0).sum() == 0 - if torch.onnx.is_in_onnx_export(): - # cast count_mat to constant while exporting to ONNX - count_mat = torch.from_numpy( - count_mat.cpu().detach().numpy()).to(device=img.device) - preds = preds / count_mat - if rescale: - preds = resize( - preds, - size=img_meta[0]['ori_shape'][:2], - mode='bilinear', - align_corners=self.align_corners, - warning=False) - return preds - - def whole_inference(self, img, img_meta, rescale): - """Inference with full image.""" - - seg_logit = self.encode_decode(img, img_meta) - if rescale: - # support dynamic shape for onnx - if torch.onnx.is_in_onnx_export(): - size = img.shape[2:] - else: - size = img_meta[0]['ori_shape'][:2] - seg_logit = resize( - seg_logit, - size=size, - mode='bilinear', - align_corners=self.align_corners, - warning=False) - - return seg_logit - - def inference(self, img, img_meta, rescale): - """Inference with slide/whole style. - - Args: - img (Tensor): The input image of shape (N, 3, H, W). - img_meta (dict): Image info dict where each dict has: 'img_shape', - 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - rescale (bool): Whether rescale back to original shape. - - Returns: - Tensor: The output segmentation map. - """ - - assert self.test_cfg.mode in ['slide', 'whole'] - ori_shape = img_meta[0]['ori_shape'] - assert all(_['ori_shape'] == ori_shape for _ in img_meta) - if self.test_cfg.mode == 'slide': - seg_logit = self.slide_inference(img, img_meta, rescale) - else: - seg_logit = self.whole_inference(img, img_meta, rescale) - output = F.softmax(seg_logit, dim=1) - flip = img_meta[0]['flip'] - if flip: - flip_direction = img_meta[0]['flip_direction'] - assert flip_direction in ['horizontal', 'vertical'] - if flip_direction == 'horizontal': - output = output.flip(dims=(3, )) - elif flip_direction == 'vertical': - output = output.flip(dims=(2, )) - - return output - - def simple_test(self, img, img_meta, rescale=True): - """Simple test with single image.""" - seg_logit = self.inference(img, img_meta, rescale) - seg_pred = seg_logit.argmax(dim=1) - if torch.onnx.is_in_onnx_export(): - # our inference backend only support 4D output - seg_pred = seg_pred.unsqueeze(0) - return seg_pred - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred - - def aug_test(self, imgs, img_metas, rescale=True): - """Test with augmentations. - - Only rescale=True is supported. - """ - # aug_test rescale all imgs back to ori_shape for now - assert rescale - # to save memory, we get augmented seg logit inplace - seg_logit = self.inference(imgs[0], img_metas[0], rescale) - for i in range(1, len(imgs)): - cur_seg_logit = self.inference(imgs[i], img_metas[i], rescale) - seg_logit += cur_seg_logit - seg_logit /= len(imgs) - seg_pred = seg_logit.argmax(dim=1) - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/__init__.py deleted file mode 100644 index fc281e3d6..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .embed import PatchEmbed -from .inverted_residual import InvertedResidual, InvertedResidualV3 -from .make_divisible import make_divisible -from .res_layer import ResLayer -from .se_layer import SELayer -from .self_attention_block import SelfAttentionBlock -from .shape_convert import (nchw2nlc2nchw, nchw_to_nlc, nlc2nchw2nlc, - nlc_to_nchw) -from .up_conv_block import UpConvBlock -from .wrappers import resize, Upsample - -__all__ = [ - 'ResLayer', 'SelfAttentionBlock', 'make_divisible', 'InvertedResidual', - 'UpConvBlock', 'InvertedResidualV3', 'SELayer', 'PatchEmbed', - 'nchw_to_nlc', 'nlc_to_nchw', 'nchw2nlc2nchw', 'nlc2nchw2nlc', - 'resize', 'Upsample' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/embed.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/embed.py deleted file mode 100644 index 1515675e1..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/embed.py +++ /dev/null @@ -1,330 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -from typing import Sequence - -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner.base_module import BaseModule -from mmcv.utils import to_2tuple - - -class AdaptivePadding(nn.Module): - """Applies padding to input (if needed) so that input can get fully covered - by filter you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad zero around - input. The "corner" mode would pad zero to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel: - stride (int | tuple): Stride of the filter. Default: 1: - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - - super(AdaptivePadding, self).__init__() - - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The config dict for embedding - conv layer type selection. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int, optional): The slide stride of embedding conv. - Default: None (Would be set as `kernel_size`). - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only work when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=None, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super(PatchEmbed, self).__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adap_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adap_padding: - pad_h, pad_w = self.adap_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adap_padding: - x = self.adap_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map. Our implementation uses `nn.Unfold` to - merge patch, which is about 25% faster than original implementation. - Instead, we need to modify pretrained models for compatibility. - - Args: - in_channels (int): The num of input channels. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adap_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - - if self.adap_padding: - x = self.adap_padding(x) - H, W = x.shape[-2:] - - x = self.sampler(x) - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/inverted_residual.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/inverted_residual.py deleted file mode 100644 index c9cda7682..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/inverted_residual.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import ConvModule -from torch import nn -from torch.utils import checkpoint as cp - -from .se_layer import SELayer - - -class InvertedResidual(nn.Module): - """InvertedResidual block for MobileNetV2. - - Args: - in_channels (int): The input channels of the InvertedResidual block. - out_channels (int): The output channels of the InvertedResidual block. - stride (int): Stride of the middle (first) 3x3 convolution. - expand_ratio (int): Adjusts number of channels of the hidden layer - in InvertedResidual by this amount. - dilation (int): Dilation rate of depthwise conv. Default: 1 - conv_cfg (dict): Config dict for convolution layer. - Default: None, which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU6'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - stride, - expand_ratio, - dilation=1, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU6'), - with_cp=False, - **kwargs): - super(InvertedResidual, self).__init__() - self.stride = stride - assert stride in [1, 2], f'stride must in [1, 2]. ' \ - f'But received {stride}.' - self.with_cp = with_cp - self.use_res_connect = self.stride == 1 and in_channels == out_channels - hidden_dim = int(round(in_channels * expand_ratio)) - - layers = [] - if expand_ratio != 1: - layers.append( - ConvModule( - in_channels=in_channels, - out_channels=hidden_dim, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs)) - layers.extend([ - ConvModule( - in_channels=hidden_dim, - out_channels=hidden_dim, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - groups=hidden_dim, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs), - ConvModule( - in_channels=hidden_dim, - out_channels=out_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None, - **kwargs) - ]) - self.conv = nn.Sequential(*layers) - - def forward(self, x): - - def _inner_forward(x): - if self.use_res_connect: - return x + self.conv(x) - else: - return self.conv(x) - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out - - -class InvertedResidualV3(nn.Module): - """Inverted Residual Block for MobileNetV3. - - Args: - in_channels (int): The input channels of this Module. - out_channels (int): The output channels of this Module. - mid_channels (int): The input channels of the depthwise convolution. - kernel_size (int): The kernel size of the depthwise convolution. - Default: 3. - stride (int): The stride of the depthwise convolution. Default: 1. - se_cfg (dict): Config dict for se layer. Default: None, which means no - se layer. - with_expand_conv (bool): Use expand conv or not. If set False, - mid_channels must be the same with in_channels. Default: True. - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - mid_channels, - kernel_size=3, - stride=1, - se_cfg=None, - with_expand_conv=True, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - with_cp=False): - super(InvertedResidualV3, self).__init__() - self.with_res_shortcut = (stride == 1 and in_channels == out_channels) - assert stride in [1, 2] - self.with_cp = with_cp - self.with_se = se_cfg is not None - self.with_expand_conv = with_expand_conv - - if self.with_se: - assert isinstance(se_cfg, dict) - if not self.with_expand_conv: - assert mid_channels == in_channels - - if self.with_expand_conv: - self.expand_conv = ConvModule( - in_channels=in_channels, - out_channels=mid_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.depthwise_conv = ConvModule( - in_channels=mid_channels, - out_channels=mid_channels, - kernel_size=kernel_size, - stride=stride, - padding=kernel_size // 2, - groups=mid_channels, - conv_cfg=dict( - type='Conv2dAdaptivePadding') if stride == 2 else conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - if self.with_se: - self.se = SELayer(**se_cfg) - - self.linear_conv = ConvModule( - in_channels=mid_channels, - out_channels=out_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - def forward(self, x): - - def _inner_forward(x): - out = x - - if self.with_expand_conv: - out = self.expand_conv(out) - - out = self.depthwise_conv(out) - - if self.with_se: - out = self.se(out) - - out = self.linear_conv(out) - - if self.with_res_shortcut: - return x + out - else: - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/make_divisible.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/make_divisible.py deleted file mode 100644 index ed42c2eee..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/make_divisible.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def make_divisible(value, divisor, min_value=None, min_ratio=0.9): - """Make divisible function. - - This function rounds the channel number to the nearest value that can be - divisible by the divisor. It is taken from the original tf repo. It ensures - that all layers have a channel number that is divisible by divisor. It can - be seen here: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py # noqa - - Args: - value (int): The original channel number. - divisor (int): The divisor to fully divide the channel number. - min_value (int): The minimum value of the output channel. - Default: None, means that the minimum value equal to the divisor. - min_ratio (float): The minimum ratio of the rounded channel number to - the original channel number. Default: 0.9. - - Returns: - int: The modified output channel number. - """ - - if min_value is None: - min_value = divisor - new_value = max(min_value, int(value + divisor / 2) // divisor * divisor) - # Make sure that round down does not go down by more than (1-min_ratio). - if new_value < min_ratio * value: - new_value += divisor - return new_value diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/res_layer.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/res_layer.py deleted file mode 100644 index 190a0c5d5..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/res_layer.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner import Sequential -from torch import nn as nn - - -class ResLayer(Sequential): - """ResLayer to build ResNet style backbone. - - Args: - block (nn.Module): block used to build ResLayer. - inplanes (int): inplanes of block. - planes (int): planes of block. - num_blocks (int): number of blocks. - stride (int): stride of the first block. Default: 1 - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False - conv_cfg (dict): dictionary to construct and config conv layer. - Default: None - norm_cfg (dict): dictionary to construct and config norm layer. - Default: dict(type='BN') - multi_grid (int | None): Multi grid dilation rates of last - stage. Default: None - contract_dilation (bool): Whether contract first dilation of each layer - Default: False - """ - - def __init__(self, - block, - inplanes, - planes, - num_blocks, - stride=1, - dilation=1, - avg_down=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - multi_grid=None, - contract_dilation=False, - **kwargs): - self.block = block - - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = [] - conv_stride = stride - if avg_down: - conv_stride = 1 - downsample.append( - nn.AvgPool2d( - kernel_size=stride, - stride=stride, - ceil_mode=True, - count_include_pad=False)) - downsample.extend([ - build_conv_layer( - conv_cfg, - inplanes, - planes * block.expansion, - kernel_size=1, - stride=conv_stride, - bias=False), - build_norm_layer(norm_cfg, planes * block.expansion)[1] - ]) - downsample = nn.Sequential(*downsample) - - layers = [] - if multi_grid is None: - if dilation > 1 and contract_dilation: - first_dilation = dilation // 2 - else: - first_dilation = dilation - else: - first_dilation = multi_grid[0] - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=stride, - dilation=first_dilation, - downsample=downsample, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - inplanes = planes * block.expansion - for i in range(1, num_blocks): - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=1, - dilation=dilation if multi_grid is None else multi_grid[i], - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - super(ResLayer, self).__init__(*layers) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/se_layer.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/se_layer.py deleted file mode 100644 index 16f52aa5c..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/se_layer.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch.nn as nn -from mmcv.cnn import ConvModule - -from .make_divisible import make_divisible - - -class SELayer(nn.Module): - """Squeeze-and-Excitation Module. - - Args: - channels (int): The input (and output) channels of the SE layer. - ratio (int): Squeeze ratio in SELayer, the intermediate channel will be - ``int(channels/ratio)``. Default: 16. - conv_cfg (None or dict): Config dict for convolution layer. - Default: None, which means using conv2d. - act_cfg (dict or Sequence[dict]): Config dict for activation layer. - If act_cfg is a dict, two activation layers will be configured - by this dict. If act_cfg is a sequence of dicts, the first - activation layer will be configured by the first dict and the - second activation layer will be configured by the second dict. - Default: (dict(type='ReLU'), dict(type='HSigmoid', bias=3.0, - divisor=6.0)). - """ - - def __init__(self, - channels, - ratio=16, - conv_cfg=None, - act_cfg=(dict(type='ReLU'), - dict(type='HSigmoid', bias=3.0, divisor=6.0))): - super(SELayer, self).__init__() - if isinstance(act_cfg, dict): - act_cfg = (act_cfg, act_cfg) - assert len(act_cfg) == 2 - assert mmcv.is_tuple_of(act_cfg, dict) - self.global_avgpool = nn.AdaptiveAvgPool2d(1) - self.conv1 = ConvModule( - in_channels=channels, - out_channels=make_divisible(channels // ratio, 8), - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[0]) - self.conv2 = ConvModule( - in_channels=make_divisible(channels // ratio, 8), - out_channels=channels, - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[1]) - - def forward(self, x): - out = self.global_avgpool(x) - out = self.conv1(out) - out = self.conv2(out) - return x * out diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/self_attention_block.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/self_attention_block.py deleted file mode 100644 index c945fa716..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/self_attention_block.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.cnn import ConvModule, constant_init -from torch import nn as nn -from torch.nn import functional as F - - -class SelfAttentionBlock(nn.Module): - """General self-attention block/non-local block. - - Please refer to https://arxiv.org/abs/1706.03762 for details about key, - query and value. - - Args: - key_in_channels (int): Input channels of key feature. - query_in_channels (int): Input channels of query feature. - channels (int): Output channels of key/query transform. - out_channels (int): Output channels. - share_key_query (bool): Whether share projection weight between key - and query projection. - query_downsample (nn.Module): Query downsample module. - key_downsample (nn.Module): Key downsample module. - key_query_num_convs (int): Number of convs for key/query projection. - value_num_convs (int): Number of convs for value projection. - matmul_norm (bool): Whether normalize attention map with sqrt of - channels - with_out (bool): Whether use out projection. - conv_cfg (dict|None): Config of conv layers. - norm_cfg (dict|None): Config of norm layers. - act_cfg (dict|None): Config of activation layers. - """ - - def __init__(self, key_in_channels, query_in_channels, channels, - out_channels, share_key_query, query_downsample, - key_downsample, key_query_num_convs, value_out_num_convs, - key_query_norm, value_out_norm, matmul_norm, with_out, - conv_cfg, norm_cfg, act_cfg): - super(SelfAttentionBlock, self).__init__() - if share_key_query: - assert key_in_channels == query_in_channels - self.key_in_channels = key_in_channels - self.query_in_channels = query_in_channels - self.out_channels = out_channels - self.channels = channels - self.share_key_query = share_key_query - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.key_project = self.build_project( - key_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if share_key_query: - self.query_project = self.key_project - else: - self.query_project = self.build_project( - query_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.value_project = self.build_project( - key_in_channels, - channels if with_out else out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if with_out: - self.out_project = self.build_project( - channels, - out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.out_project = None - - self.query_downsample = query_downsample - self.key_downsample = key_downsample - self.matmul_norm = matmul_norm - - self.init_weights() - - def init_weights(self): - """Initialize weight of later layer.""" - if self.out_project is not None: - if not isinstance(self.out_project, ConvModule): - constant_init(self.out_project, 0) - - def build_project(self, in_channels, channels, num_convs, use_conv_module, - conv_cfg, norm_cfg, act_cfg): - """Build projection layer for key/query/value/out.""" - if use_conv_module: - convs = [ - ConvModule( - in_channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - ] - for _ in range(num_convs - 1): - convs.append( - ConvModule( - channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - else: - convs = [nn.Conv2d(in_channels, channels, 1)] - for _ in range(num_convs - 1): - convs.append(nn.Conv2d(channels, channels, 1)) - if len(convs) > 1: - convs = nn.Sequential(*convs) - else: - convs = convs[0] - return convs - - def forward(self, query_feats, key_feats): - """Forward function.""" - batch_size = query_feats.size(0) - query = self.query_project(query_feats) - if self.query_downsample is not None: - query = self.query_downsample(query) - query = query.reshape(*query.shape[:2], -1) - query = query.permute(0, 2, 1).contiguous() - - key = self.key_project(key_feats) - value = self.value_project(key_feats) - if self.key_downsample is not None: - key = self.key_downsample(key) - value = self.key_downsample(value) - key = key.reshape(*key.shape[:2], -1) - value = value.reshape(*value.shape[:2], -1) - value = value.permute(0, 2, 1).contiguous() - - sim_map = torch.matmul(query, key) - if self.matmul_norm: - sim_map = (self.channels**-.5) * sim_map - sim_map = F.softmax(sim_map, dim=-1) - - context = torch.matmul(sim_map, value) - context = context.permute(0, 2, 1).contiguous() - context = context.reshape(batch_size, -1, *query_feats.shape[2:]) - if self.out_project is not None: - context = self.out_project(context) - return context diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/shape_convert.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/shape_convert.py deleted file mode 100644 index cce1e220b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/shape_convert.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def nlc_to_nchw(x, hw_shape): - """Convert [N, L, C] shape tensor to [N, C, H, W] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, L, C] before conversion. - hw_shape (Sequence[int]): The height and width of output feature map. - - Returns: - Tensor: The output tensor of shape [N, C, H, W] after conversion. - """ - H, W = hw_shape - assert len(x.shape) == 3 - B, L, C = x.shape - assert L == H * W, 'The seq_len doesn\'t match H, W' - return x.transpose(1, 2).reshape(B, C, H, W) - - -def nchw_to_nlc(x): - """Flatten [N, C, H, W] shape tensor to [N, L, C] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, C, H, W] before conversion. - - Returns: - Tensor: The output tensor of shape [N, L, C] after conversion. - """ - assert len(x.shape) == 4 - return x.flatten(2).transpose(1, 2).contiguous() - - -def nchw2nlc2nchw(module, x, contiguous=False, **kwargs): - """Flatten [N, C, H, W] shape tensor `x` to [N, L, C] shape tensor. Use the - reshaped tensor as the input of `module`, and the convert the output of - `module`, whose shape is. - - [N, L, C], to [N, C, H, W]. - - Args: - module (Callable): A callable object the takes a tensor - with shape [N, L, C] as input. - x (Tensor): The input tensor of shape [N, C, H, W]. - contiguous: - contiguous (Bool): Whether to make the tensor contiguous - after each shape transform. - - Returns: - Tensor: The output tensor of shape [N, C, H, W]. - - Example: - >>> import torch - >>> import torch.nn as nn - >>> norm = nn.LayerNorm(4) - >>> feature_map = torch.rand(4, 4, 5, 5) - >>> output = nchw2nlc2nchw(norm, feature_map) - """ - B, C, H, W = x.shape - if not contiguous: - x = x.flatten(2).transpose(1, 2) - x = module(x, **kwargs) - x = x.transpose(1, 2).reshape(B, C, H, W) - else: - x = x.flatten(2).transpose(1, 2).contiguous() - x = module(x, **kwargs) - x = x.transpose(1, 2).reshape(B, C, H, W).contiguous() - return x - - -def nlc2nchw2nlc(module, x, hw_shape, contiguous=False, **kwargs): - """Convert [N, L, C] shape tensor `x` to [N, C, H, W] shape tensor. Use the - reshaped tensor as the input of `module`, and convert the output of - `module`, whose shape is. - - [N, C, H, W], to [N, L, C]. - - Args: - module (Callable): A callable object the takes a tensor - with shape [N, C, H, W] as input. - x (Tensor): The input tensor of shape [N, L, C]. - hw_shape: (Sequence[int]): The height and width of the - feature map with shape [N, C, H, W]. - contiguous (Bool): Whether to make the tensor contiguous - after each shape transform. - - Returns: - Tensor: The output tensor of shape [N, L, C]. - - Example: - >>> import torch - >>> import torch.nn as nn - >>> conv = nn.Conv2d(16, 16, 3, 1, 1) - >>> feature_map = torch.rand(4, 25, 16) - >>> output = nlc2nchw2nlc(conv, feature_map, (5, 5)) - """ - H, W = hw_shape - assert len(x.shape) == 3 - B, L, C = x.shape - assert L == H * W, 'The seq_len doesn\'t match H, W' - if not contiguous: - x = x.transpose(1, 2).reshape(B, C, H, W) - x = module(x, **kwargs) - x = x.flatten(2).transpose(1, 2) - else: - x = x.transpose(1, 2).reshape(B, C, H, W).contiguous() - x = module(x, **kwargs) - x = x.flatten(2).transpose(1, 2).contiguous() - return x diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/up_conv_block.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/up_conv_block.py deleted file mode 100644 index d8396d9c2..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/up_conv_block.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, build_upsample_layer - - -class UpConvBlock(nn.Module): - """Upsample convolution block in decoder for UNet. - - This upsample convolution block consists of one upsample module - followed by one convolution block. The upsample module expands the - high-level low-resolution feature map and the convolution block fuses - the upsampled high-level low-resolution feature map and the low-level - high-resolution feature map from encoder. - - Args: - conv_block (nn.Sequential): Sequential of convolutional layers. - in_channels (int): Number of input channels of the high-level - skip_channels (int): Number of input channels of the low-level - high-resolution feature map from encoder. - out_channels (int): Number of output channels. - num_convs (int): Number of convolutional layers in the conv_block. - Default: 2. - stride (int): Stride of convolutional layer in conv_block. Default: 1. - dilation (int): Dilation rate of convolutional layer in conv_block. - Default: 1. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - conv_cfg (dict | None): Config dict for convolution layer. - Default: None. - norm_cfg (dict | None): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict | None): Config dict for activation layer in ConvModule. - Default: dict(type='ReLU'). - upsample_cfg (dict): The upsample config of the upsample module in - decoder. Default: dict(type='InterpConv'). If the size of - high-level feature map is the same as that of skip feature map - (low-level feature map from encoder), it does not need upsample the - high-level feature map and the upsample_cfg is None. - dcn (bool): Use deformable convolution in convolutional layer or not. - Default: None. - plugins (dict): plugins for convolutional layers. Default: None. - """ - - def __init__(self, - conv_block, - in_channels, - skip_channels, - out_channels, - num_convs=2, - stride=1, - dilation=1, - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - upsample_cfg=dict(type='InterpConv'), - dcn=None, - plugins=None): - super(UpConvBlock, self).__init__() - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.conv_block = conv_block( - in_channels=2 * skip_channels, - out_channels=out_channels, - num_convs=num_convs, - stride=stride, - dilation=dilation, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - dcn=None, - plugins=None) - if upsample_cfg is not None: - self.upsample = build_upsample_layer( - cfg=upsample_cfg, - in_channels=in_channels, - out_channels=skip_channels, - with_cp=with_cp, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.upsample = ConvModule( - in_channels, - skip_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, skip, x): - """Forward function.""" - - x = self.upsample(x) - out = torch.cat([skip, x], dim=1) - out = self.conv_block(out) - - return out diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/wrappers.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/wrappers.py deleted file mode 100644 index abbd0c029..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/models/utils/wrappers.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.nn.functional as F - - -def resize(input, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None, - warning=True): - if warning: - if size is not None and align_corners: - input_h, input_w = tuple(int(x) for x in input.shape[2:]) - output_h, output_w = tuple(int(x) for x in size) - if output_h > input_h or output_w > output_h: - if ((output_h > 1 and output_w > 1 and input_h > 1 - and input_w > 1) and (output_h - 1) % (input_h - 1) - and (output_w - 1) % (input_w - 1)): - warnings.warn( - f'When align_corners={align_corners}, ' - 'the output would more aligned if ' - f'input size {(input_h, input_w)} is `x+1` and ' - f'out size {(output_h, output_w)} is `nx+1`') - return F.interpolate(input, size, scale_factor, mode, align_corners) - - -class Upsample(nn.Module): - - def __init__(self, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None): - super().__init__() - self.size = size - if isinstance(scale_factor, tuple): - self.scale_factor = tuple(float(factor) for factor in scale_factor) - else: - self.scale_factor = float(scale_factor) if scale_factor else None - self.mode = mode - self.align_corners = align_corners - - def forward(self, x): - if not self.size: - size = [int(t * self.scale_factor) for t in x.shape[-2:]] - else: - size = self.size - return resize(x, size, None, self.mode, self.align_corners) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/ops/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/ops/__init__.py deleted file mode 100644 index bc075cd4e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/ops/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .encoding import Encoding -from .wrappers import Upsample, resize - -__all__ = ['Upsample', 'resize', 'Encoding'] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/ops/encoding.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/ops/encoding.py deleted file mode 100644 index f397cc54e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/ops/encoding.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn -from torch.nn import functional as F - - -class Encoding(nn.Module): - """Encoding Layer: a learnable residual encoder. - - Input is of shape (batch_size, channels, height, width). - Output is of shape (batch_size, num_codes, channels). - - Args: - channels: dimension of the features or feature channels - num_codes: number of code words - """ - - def __init__(self, channels, num_codes): - super(Encoding, self).__init__() - # init codewords and smoothing factor - self.channels, self.num_codes = channels, num_codes - std = 1. / ((num_codes * channels)**0.5) - # [num_codes, channels] - self.codewords = nn.Parameter( - torch.empty(num_codes, channels, - dtype=torch.float).uniform_(-std, std), - requires_grad=True) - # [num_codes] - self.scale = nn.Parameter( - torch.empty(num_codes, dtype=torch.float).uniform_(-1, 0), - requires_grad=True) - - @staticmethod - def scaled_l2(x, codewords, scale): - num_codes, channels = codewords.size() - batch_size = x.size(0) - reshaped_scale = scale.view((1, 1, num_codes)) - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - - scaled_l2_norm = reshaped_scale * ( - expanded_x - reshaped_codewords).pow(2).sum(dim=3) - return scaled_l2_norm - - @staticmethod - def aggregate(assignment_weights, x, codewords): - num_codes, channels = codewords.size() - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - batch_size = x.size(0) - - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - encoded_feat = (assignment_weights.unsqueeze(3) * - (expanded_x - reshaped_codewords)).sum(dim=1) - return encoded_feat - - def forward(self, x): - assert x.dim() == 4 and x.size(1) == self.channels - # [batch_size, channels, height, width] - batch_size = x.size(0) - # [batch_size, height x width, channels] - x = x.view(batch_size, self.channels, -1).transpose(1, 2).contiguous() - # assignment_weights: [batch_size, channels, num_codes] - assignment_weights = F.softmax( - self.scaled_l2(x, self.codewords, self.scale), dim=2) - # aggregate - encoded_feat = self.aggregate(assignment_weights, x, self.codewords) - return encoded_feat - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(Nx{self.channels}xHxW =>Nx{self.num_codes}' \ - f'x{self.channels})' - return repr_str diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/ops/wrappers.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/ops/wrappers.py deleted file mode 100644 index ce67e4beb..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/ops/wrappers.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.nn.functional as F - - -def resize(input, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None, - warning=True): - if warning: - if size is not None and align_corners: - input_h, input_w = tuple(int(x) for x in input.shape[2:]) - output_h, output_w = tuple(int(x) for x in size) - if output_h > input_h or output_w > output_h: - if ((output_h > 1 and output_w > 1 and input_h > 1 - and input_w > 1) and (output_h - 1) % (input_h - 1) - and (output_w - 1) % (input_w - 1)): - warnings.warn( - f'When align_corners={align_corners}, ' - 'the output would more aligned if ' - f'input size {(input_h, input_w)} is `x+1` and ' - f'out size {(output_h, output_w)} is `nx+1`') - return F.interpolate(input, size, scale_factor, mode, align_corners) - - -class Upsample(nn.Module): - - def __init__(self, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None): - super(Upsample, self).__init__() - self.size = size - if isinstance(scale_factor, tuple): - self.scale_factor = tuple(float(factor) for factor in scale_factor) - else: - self.scale_factor = float(scale_factor) if scale_factor else None - self.mode = mode - self.align_corners = align_corners - - def forward(self, x): - if not self.size: - size = [int(t * self.scale_factor) for t in x.shape[-2:]] - else: - size = self.size - return resize(x, size, None, self.mode, self.align_corners) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/registry/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/registry/__init__.py deleted file mode 100644 index ee514d1a2..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/registry/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import (DATA_SAMPLERS, DATASETS, EVALUATOR, HOOKS, INFERENCERS, - LOG_PROCESSORS, LOOPS, METRICS, MODEL_WRAPPERS, MODELS, - OPTIM_WRAPPER_CONSTRUCTORS, OPTIM_WRAPPERS, OPTIMIZERS, - PARAM_SCHEDULERS, RUNNER_CONSTRUCTORS, RUNNERS, - TASK_UTILS, TRANSFORMS, VISBACKENDS, VISUALIZERS, - WEIGHT_INITIALIZERS) - -__all__ = [ - 'HOOKS', 'DATASETS', 'DATA_SAMPLERS', 'TRANSFORMS', 'MODELS', - 'WEIGHT_INITIALIZERS', 'OPTIMIZERS', 'OPTIM_WRAPPER_CONSTRUCTORS', - 'TASK_UTILS', 'PARAM_SCHEDULERS', 'METRICS', 'MODEL_WRAPPERS', - 'VISBACKENDS', 'VISUALIZERS', 'RUNNERS', 'RUNNER_CONSTRUCTORS', 'LOOPS', - 'EVALUATOR', 'LOG_PROCESSORS', 'OPTIM_WRAPPERS', 'INFERENCERS' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/registry/registry.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/registry/registry.py deleted file mode 100644 index 1e423980d..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/registry/registry.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""MMSegmentation provides 21 registry nodes to support using modules across -projects. Each node is a child of the root registry in MMEngine. - -More details can be found at -https://mmengine.readthedocs.io/en/latest/advanced_tutorials/registry.html. -""" - -from mmengine.registry import DATA_SAMPLERS as MMENGINE_DATA_SAMPLERS -from mmengine.registry import DATASETS as MMENGINE_DATASETS -from mmengine.registry import EVALUATOR as MMENGINE_EVALUATOR -from mmengine.registry import HOOKS as MMENGINE_HOOKS -from mmengine.registry import INFERENCERS as MMENGINE_INFERENCERS -from mmengine.registry import LOG_PROCESSORS as MMENGINE_LOG_PROCESSORS -from mmengine.registry import LOOPS as MMENGINE_LOOPS -from mmengine.registry import METRICS as MMENGINE_METRICS -from mmengine.registry import MODEL_WRAPPERS as MMENGINE_MODEL_WRAPPERS -from mmengine.registry import MODELS as MMENGINE_MODELS -from mmengine.registry import \ - OPTIM_WRAPPER_CONSTRUCTORS as MMENGINE_OPTIM_WRAPPER_CONSTRUCTORS -from mmengine.registry import OPTIM_WRAPPERS as MMENGINE_OPTIM_WRAPPERS -from mmengine.registry import OPTIMIZERS as MMENGINE_OPTIMIZERS -from mmengine.registry import PARAM_SCHEDULERS as MMENGINE_PARAM_SCHEDULERS -from mmengine.registry import \ - RUNNER_CONSTRUCTORS as MMENGINE_RUNNER_CONSTRUCTORS -from mmengine.registry import RUNNERS as MMENGINE_RUNNERS -from mmengine.registry import TASK_UTILS as MMENGINE_TASK_UTILS -from mmengine.registry import TRANSFORMS as MMENGINE_TRANSFORMS -from mmengine.registry import VISBACKENDS as MMENGINE_VISBACKENDS -from mmengine.registry import VISUALIZERS as MMENGINE_VISUALIZERS -from mmengine.registry import \ - WEIGHT_INITIALIZERS as MMENGINE_WEIGHT_INITIALIZERS -from mmengine.registry import Registry - -# manage all kinds of runners like `EpochBasedRunner` and `IterBasedRunner` -RUNNERS = Registry('runner', parent=MMENGINE_RUNNERS) -# manage runner constructors that define how to initialize runners -RUNNER_CONSTRUCTORS = Registry( - 'runner constructor', parent=MMENGINE_RUNNER_CONSTRUCTORS) -# manage all kinds of loops like `EpochBasedTrainLoop` -LOOPS = Registry('loop', parent=MMENGINE_LOOPS) -# manage all kinds of hooks like `CheckpointHook` -HOOKS = Registry( - 'hook', parent=MMENGINE_HOOKS, locations=['mmseg.engine.hooks']) - -# manage data-related modules -DATASETS = Registry( - 'dataset', parent=MMENGINE_DATASETS, locations=['mmseg.datasets']) -DATA_SAMPLERS = Registry('data sampler', parent=MMENGINE_DATA_SAMPLERS) -TRANSFORMS = Registry( - 'transform', - parent=MMENGINE_TRANSFORMS, - locations=['mmseg.datasets.transforms']) - -# mangage all kinds of modules inheriting `nn.Module` -MODELS = Registry('model', parent=MMENGINE_MODELS, locations=['mmseg.models']) -# mangage all kinds of model wrappers like 'MMDistributedDataParallel' -MODEL_WRAPPERS = Registry( - 'model_wrapper', - parent=MMENGINE_MODEL_WRAPPERS, - locations=['mmseg.models']) -# mangage all kinds of weight initialization modules like `Uniform` -WEIGHT_INITIALIZERS = Registry( - 'weight initializer', - parent=MMENGINE_WEIGHT_INITIALIZERS, - locations=['mmseg.models']) - -# mangage all kinds of optimizers like `SGD` and `Adam` -OPTIMIZERS = Registry( - 'optimizer', - parent=MMENGINE_OPTIMIZERS, - locations=['mmseg.engine.optimizers']) -# manage optimizer wrapper -OPTIM_WRAPPERS = Registry( - 'optim_wrapper', - parent=MMENGINE_OPTIM_WRAPPERS, - locations=['mmseg.engine.optimizers']) -# manage constructors that customize the optimization hyperparameters. -OPTIM_WRAPPER_CONSTRUCTORS = Registry( - 'optimizer wrapper constructor', - parent=MMENGINE_OPTIM_WRAPPER_CONSTRUCTORS, - locations=['mmseg.engine.optimizers']) -# mangage all kinds of parameter schedulers like `MultiStepLR` -PARAM_SCHEDULERS = Registry( - 'parameter scheduler', parent=MMENGINE_PARAM_SCHEDULERS) - -# manage all kinds of metrics -METRICS = Registry( - 'metric', parent=MMENGINE_METRICS, locations=['mmseg.evaluation']) -# manage evaluator -EVALUATOR = Registry( - 'evaluator', parent=MMENGINE_EVALUATOR, locations=['mmseg.evaluation']) - -# manage task-specific modules like ohem pixel sampler -TASK_UTILS = Registry( - 'task util', parent=MMENGINE_TASK_UTILS, locations=['mmseg.models']) - -# manage visualizer -VISUALIZERS = Registry( - 'visualizer', - parent=MMENGINE_VISUALIZERS, - locations=['mmseg.visualization']) -# manage visualizer backend -VISBACKENDS = Registry( - 'vis_backend', - parent=MMENGINE_VISBACKENDS, - locations=['mmseg.visualization']) - -# manage logprocessor -LOG_PROCESSORS = Registry( - 'log_processor', - parent=MMENGINE_LOG_PROCESSORS, - locations=['mmseg.visualization']) - -# manage inferencer -INFERENCERS = Registry('inferencer', parent=MMENGINE_INFERENCERS) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/__init__.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/__init__.py deleted file mode 100644 index ed002c7de..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collect_env import collect_env -from .logger import get_root_logger -from .misc import find_latest_checkpoint -from .set_env import setup_multi_processes - -__all__ = [ - 'get_root_logger', 'collect_env', 'find_latest_checkpoint', - 'setup_multi_processes' -] diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/collect_env.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/collect_env.py deleted file mode 100644 index 3379ecb06..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/collect_env.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash - -import mmseg - - -def collect_env(): - """Collect the information of the running environments.""" - env_info = collect_base_env() - env_info['MMSegmentation'] = f'{mmseg.__version__}+{get_git_hash()[:7]}' - - return env_info - - -if __name__ == '__main__': - for name, val in collect_env().items(): - print('{}: {}'.format(name, val)) diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/logger.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/logger.py deleted file mode 100644 index 0cb3c78d6..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/logger.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -from mmcv.utils import get_logger - - -def get_root_logger(log_file=None, log_level=logging.INFO): - """Get the root logger. - - The logger will be initialized if it has not been initialized. By default a - StreamHandler will be added. If `log_file` is specified, a FileHandler will - also be added. The name of the root logger is the top-level package name, - e.g., "mmseg". - - Args: - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the root logger. - log_level (int): The root logger level. Note that only the process of - rank 0 is affected, while other processes will set the level to - "Error" and be silent most of the time. - - Returns: - logging.Logger: The root logger. - """ - - logger = get_logger(name='mmseg', log_file=log_file, log_level=log_level) - - return logger diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/misc.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/misc.py deleted file mode 100644 index bd1b6b163..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/misc.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os.path as osp -import warnings - - -def find_latest_checkpoint(path, suffix='pth'): - """This function is for finding the latest checkpoint. - - It will be used when automatically resume, modified from - https://github.com/open-mmlab/mmdetection/blob/dev-v2.20.0/mmdet/utils/misc.py - - Args: - path (str): The path to find checkpoints. - suffix (str): File extension for the checkpoint. Defaults to pth. - - Returns: - latest_path(str | None): File path of the latest checkpoint. - """ - if not osp.exists(path): - warnings.warn("The path of the checkpoints doesn't exist.") - return None - if osp.exists(osp.join(path, f'latest.{suffix}')): - return osp.join(path, f'latest.{suffix}') - - checkpoints = glob.glob(osp.join(path, f'*.{suffix}')) - if len(checkpoints) == 0: - warnings.warn('The are no checkpoints in the path') - return None - latest = -1 - latest_path = '' - for checkpoint in checkpoints: - if len(checkpoint) < len(latest_path): - continue - # `count` is iteration number, as checkpoints are saved as - # 'iter_xx.pth' or 'epoch_xx.pth' and xx is iteration number. - count = int(osp.basename(checkpoint).split('_')[-1].split('.')[0]) - if count > latest: - latest = count - latest_path = checkpoint - return latest_path diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/set_env.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/set_env.py deleted file mode 100644 index b2d3aaf14..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/utils/set_env.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import platform - -import cv2 -import torch.multiprocessing as mp - -from ..utils import get_root_logger - - -def setup_multi_processes(cfg): - """Setup multi-processing environment variables.""" - logger = get_root_logger() - - # set multi-process start method - if platform.system() != 'Windows': - mp_start_method = cfg.get('mp_start_method', None) - current_method = mp.get_start_method(allow_none=True) - if mp_start_method in ('fork', 'spawn', 'forkserver'): - logger.info( - f'Multi-processing start method `{mp_start_method}` is ' - f'different from the previous setting `{current_method}`.' - f'It will be force set to `{mp_start_method}`.') - mp.set_start_method(mp_start_method, force=True) - else: - logger.info( - f'Multi-processing start method is `{mp_start_method}`') - - # disable opencv multithreading to avoid system being overloaded - opencv_num_threads = cfg.get('opencv_num_threads', None) - if isinstance(opencv_num_threads, int): - logger.info(f'OpenCV num_threads is `{opencv_num_threads}`') - cv2.setNumThreads(opencv_num_threads) - else: - logger.info(f'OpenCV num_threads is `{cv2.getNumThreads}') - - if cfg.data.workers_per_gpu > 1: - # setup OMP threads - # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa - omp_num_threads = cfg.get('omp_num_threads', None) - if 'OMP_NUM_THREADS' not in os.environ: - if isinstance(omp_num_threads, int): - logger.info(f'OMP num threads is {omp_num_threads}') - os.environ['OMP_NUM_THREADS'] = str(omp_num_threads) - else: - logger.info(f'OMP num threads is {os.environ["OMP_NUM_THREADS"] }') - - # setup MKL threads - if 'MKL_NUM_THREADS' not in os.environ: - mkl_num_threads = cfg.get('mkl_num_threads', None) - if isinstance(mkl_num_threads, int): - logger.info(f'MKL num threads is {mkl_num_threads}') - os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads) - else: - logger.info(f'MKL num threads is {os.environ["MKL_NUM_THREADS"]}') diff --git a/cv/semantic_segmentation/unet++/pytorch/mmseg/version.py b/cv/semantic_segmentation/unet++/pytorch/mmseg/version.py deleted file mode 100644 index e05146f0a..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/mmseg/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) Open-MMLab. All rights reserved. - -__version__ = '0.24.1' - - -def parse_version_info(version_str): - version_info = [] - for x in version_str.split('.'): - if x.isdigit(): - version_info.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - version_info.append(int(patch_version[0])) - version_info.append(f'rc{patch_version[1]}') - return tuple(version_info) - - -version_info = parse_version_info(__version__) diff --git a/cv/semantic_segmentation/unet++/pytorch/requirements.txt b/cv/semantic_segmentation/unet++/pytorch/requirements.txt deleted file mode 100644 index a50fc71db..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ --r requirements/mmcv/runtime.txt --r requirements/apcnet.txt -opencv-python -cityscapesscripts \ No newline at end of file diff --git a/cv/semantic_segmentation/unet++/pytorch/requirements/apcnet.txt b/cv/semantic_segmentation/unet++/pytorch/requirements/apcnet.txt deleted file mode 100644 index 2712f504c..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/requirements/apcnet.txt +++ /dev/null @@ -1,4 +0,0 @@ -matplotlib -numpy -packaging -prettytable diff --git a/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/build.txt b/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/build.txt deleted file mode 100644 index abf514853..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/build.txt +++ /dev/null @@ -1 +0,0 @@ -pytest-runner diff --git a/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/docs.txt b/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/docs.txt deleted file mode 100644 index 6a5319af3..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/docs.txt +++ /dev/null @@ -1,8 +0,0 @@ -docutils==0.16.0 -myst-parser -opencv-python --e git+https://github.com/open-mmlab/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme -sphinx==4.0.2 -sphinx-copybutton -sphinx_markdown_tables -torch diff --git a/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/optional.txt b/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/optional.txt deleted file mode 100644 index 63730036f..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/optional.txt +++ /dev/null @@ -1 +0,0 @@ -ninja diff --git a/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/requirements.txt b/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/requirements.txt deleted file mode 100644 index 6f00e6303..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ --r requirements/mmcv/build.txt --r requirements/mmcv/optional.txt --r requirements/mmcv/runtime.txt --r requirements/mmcv/test.txt diff --git a/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/runtime.txt b/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/runtime.txt deleted file mode 100644 index 66e90d674..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/runtime.txt +++ /dev/null @@ -1,7 +0,0 @@ -addict -numpy -packaging -Pillow -pyyaml -regex;sys_platform=='win32' -yapf diff --git a/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/test.txt b/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/test.txt deleted file mode 100644 index 6d9d17b98..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/requirements/mmcv/test.txt +++ /dev/null @@ -1,9 +0,0 @@ -coverage -lmdb -onnx==1.7.0; python_version < '3.10' -onnxoptimizer; python_version < '3.10' -onnxruntime>=1.8.0; python_version < '3.10' -pytest -PyTurboJPEG -scipy -tifffile diff --git a/cv/semantic_segmentation/unet++/pytorch/requirements/mminstall.txt b/cv/semantic_segmentation/unet++/pytorch/requirements/mminstall.txt deleted file mode 100644 index bd43faf87..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/requirements/mminstall.txt +++ /dev/null @@ -1,2 +0,0 @@ -mmcls>=0.20.1 -mmcv-full>=1.4.4,<=1.6.0 diff --git a/cv/semantic_segmentation/unet++/pytorch/requirements/optional.txt b/cv/semantic_segmentation/unet++/pytorch/requirements/optional.txt deleted file mode 100644 index 47fa59331..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/requirements/optional.txt +++ /dev/null @@ -1 +0,0 @@ -cityscapesscripts diff --git a/cv/semantic_segmentation/unet++/pytorch/requirements/runtime.txt b/cv/semantic_segmentation/unet++/pytorch/requirements/runtime.txt deleted file mode 100644 index 520408fe8..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/requirements/runtime.txt +++ /dev/null @@ -1,5 +0,0 @@ -matplotlib -mmcls>=0.20.1 -numpy -packaging -prettytable diff --git a/cv/semantic_segmentation/unet++/pytorch/setup.py b/cv/semantic_segmentation/unet++/pytorch/setup.py deleted file mode 100644 index d409997db..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/setup.py +++ /dev/null @@ -1,258 +0,0 @@ -import glob -import os -import platform -import re -import warnings -from pkg_resources import DistributionNotFound, get_distribution -from setuptools import find_packages, setup - -EXT_TYPE = '' -try: - import torch - if torch.__version__ == 'parrots': - from parrots.utils.build_extension import BuildExtension - EXT_TYPE = 'parrots' - elif (hasattr(torch, 'is_mlu_available') and torch.is_mlu_available()) or \ - os.getenv('FORCE_MLU', '0') == '1': - from torch_mlu.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - else: - from torch.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - cmd_class = {'build_ext': BuildExtension} -except ModuleNotFoundError: - cmd_class = {} - print('Skip building ext ops due to the absence of torch.') - - -def choose_requirement(primary, secondary): - """If some version of primary requirement installed, return primary, else - return secondary.""" - try: - name = re.split(r'[!<>=]', primary)[0] - get_distribution(name) - except DistributionNotFound: - return secondary - - return str(primary) - - -def get_version(): - version_file = 'mmcv/version.py' - with open(version_file, 'r', encoding='utf-8') as f: - exec(compile(f.read(), version_file, 'exec')) - return locals()['__version__'] - - -def parse_requirements(fname='requirements/mmcv/runtime.txt', with_version=True): - """Parse the package dependencies listed in a requirements file but strips - specific versioning information. - - Args: - fname (str): path to requirements file - with_version (bool, default=False): if True include version specs - - Returns: - List[str]: list of requirements items - - CommandLine: - python -c "import setup; print(setup.parse_requirements())" - """ - import sys - from os.path import exists - require_fpath = fname - - def parse_line(line): - """Parse information from a line in a requirements text file.""" - if line.startswith('-r '): - # Allow specifying requirements in other files - target = line.split(' ')[1] - for info in parse_require_file(target): - yield info - else: - info = {'line': line} - if line.startswith('-e '): - info['package'] = line.split('#egg=')[1] - else: - # Remove versioning from the package - pat = '(' + '|'.join(['>=', '==', '>']) + ')' - parts = re.split(pat, line, maxsplit=1) - parts = [p.strip() for p in parts] - - info['package'] = parts[0] - if len(parts) > 1: - op, rest = parts[1:] - if ';' in rest: - # Handle platform specific dependencies - # http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies - version, platform_deps = map(str.strip, - rest.split(';')) - info['platform_deps'] = platform_deps - else: - version = rest # NOQA - info['version'] = (op, version) - yield info - - def parse_require_file(fpath): - with open(fpath, 'r') as f: - for line in f.readlines(): - line = line.strip() - if line and not line.startswith('#'): - for info in parse_line(line): - yield info - - def gen_packages_items(): - if exists(require_fpath): - for info in parse_require_file(require_fpath): - parts = [info['package']] - if with_version and 'version' in info: - parts.extend(info['version']) - if not sys.version.startswith('3.4'): - # apparently package_deps are broken in 3.4 - platform_deps = info.get('platform_deps') - if platform_deps is not None: - parts.append(';' + platform_deps) - item = ''.join(parts) - yield item - - packages = list(gen_packages_items()) - return packages - - -install_requires = parse_requirements() - -try: - # OpenCV installed via conda. - import cv2 # NOQA: F401 - major, minor, *rest = cv2.__version__.split('.') - if int(major) < 3: - raise RuntimeError( - f'OpenCV >=3 is required but {cv2.__version__} is installed') -except ImportError: - # If first not installed install second package - CHOOSE_INSTALL_REQUIRES = [('opencv-python-headless>=3', - 'opencv-python>=3')] - for main, secondary in CHOOSE_INSTALL_REQUIRES: - install_requires.append(choose_requirement(main, secondary)) - - -def get_extensions(): - extensions = [] - - if EXT_TYPE == 'pytorch': - ext_name = 'mmcv._ext' - from torch.utils.cpp_extension import CppExtension, CUDAExtension - - # prevent ninja from using too many resources - try: - import psutil - num_cpu = len(psutil.Process().cpu_affinity()) - cpu_use = max(4, num_cpu - 1) - except (ModuleNotFoundError, AttributeError): - cpu_use = 4 - - os.environ.setdefault('MAX_JOBS', str(cpu_use)) - define_macros = [] - - # Before PyTorch1.8.0, when compiling CUDA code, `cxx` is a - # required key passed to PyTorch. Even if there is no flag passed - # to cxx, users also need to pass an empty list to PyTorch. - # Since PyTorch1.8.0, it has a default value so users do not need - # to pass an empty list anymore. - # More details at https://github.com/pytorch/pytorch/pull/45956 - extra_compile_args = {'cxx': []} - - # Since the PR (https://github.com/open-mmlab/mmcv/pull/1463) uses - # c++14 features, the argument ['std=c++14'] must be added here. - # However, in the windows environment, some standard libraries - # will depend on c++17 or higher. In fact, for the windows - # environment, the compiler will choose the appropriate compiler - # to compile those cpp files, so there is no need to add the - # argument - if platform.system() != 'Windows': - extra_compile_args['cxx'] = ['-std=c++14'] - - include_dirs = [] - - is_rocm_pytorch = False - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm_pytorch = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - - if is_rocm_pytorch or torch.cuda.is_available() or os.getenv( - 'FORCE_CUDA', '0') == '1': - if is_rocm_pytorch: - define_macros += [('HIP_DIFF', None)] - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cpp') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - else: - print(f'Compiling {ext_name} only with CPU') - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') - extension = CppExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - - # Since the PR (https://github.com/open-mmlab/mmcv/pull/1463) uses - # c++14 features, the argument ['std=c++14'] must be added here. - # However, in the windows environment, some standard libraries - # will depend on c++17 or higher. In fact, for the windows - # environment, the compiler will choose the appropriate compiler - # to compile those cpp files, so there is no need to add the - # argument - if 'nvcc' in extra_compile_args and platform.system() != 'Windows': - extra_compile_args['nvcc'] += ['-std=c++14'] - - ext_ops = extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args) - extensions.append(ext_ops) - - return extensions - - -setup( - name='mmcv' if os.getenv('MMCV_WITH_OPS', '0') == '0' else 'mmcv-full', - version=get_version(), - description='OpenMMLab Computer Vision Foundation', - keywords='computer vision', - packages=find_packages(), - include_package_data=True, - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Topic :: Utilities', - ], - url='https://github.com/open-mmlab/mmcv', - author='MMCV Contributors', - author_email='openmmlab@gmail.com', - install_requires=install_requires, - extras_require={ - 'all': parse_requirements('requirements/mmcv/requirements.txt'), - 'tests': parse_requirements('requirements/mmcv/test.txt'), - 'build': parse_requirements('requirements/mmcv/build.txt'), - 'optional': parse_requirements('requirements/mmcv/optional.txt'), - }, - ext_modules=get_extensions(), - cmdclass=cmd_class, - zip_safe=False) diff --git a/cv/semantic_segmentation/unet++/pytorch/tools/analyze_logs.py b/cv/semantic_segmentation/unet++/pytorch/tools/analyze_logs.py deleted file mode 100644 index e2127d4d6..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/tools/analyze_logs.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/open- -mmlab/mmdetection/blob/master/tools/analysis_tools/analyze_logs.py.""" -import argparse -import json -from collections import defaultdict - -import matplotlib.pyplot as plt -import seaborn as sns - - -def plot_curve(log_dicts, args): - if args.backend is not None: - plt.switch_backend(args.backend) - sns.set_style(args.style) - # if legend is None, use {filename}_{key} as legend - legend = args.legend - if legend is None: - legend = [] - for json_log in args.json_logs: - for metric in args.keys: - legend.append(f'{json_log}_{metric}') - assert len(legend) == (len(args.json_logs) * len(args.keys)) - metrics = args.keys - - num_metrics = len(metrics) - for i, log_dict in enumerate(log_dicts): - epochs = list(log_dict.keys()) - for j, metric in enumerate(metrics): - print(f'plot curve of {args.json_logs[i]}, metric is {metric}') - plot_epochs = [] - plot_iters = [] - plot_values = [] - # In some log files exist lines of validation, - # `mode` list is used to only collect iter number - # of training line. - for epoch in epochs: - epoch_logs = log_dict[epoch] - if metric not in epoch_logs.keys(): - continue - if metric in ['mIoU', 'mAcc', 'aAcc']: - plot_epochs.append(epoch) - plot_values.append(epoch_logs[metric][0]) - else: - for idx in range(len(epoch_logs[metric])): - if epoch_logs['mode'][idx] == 'train': - plot_iters.append(epoch_logs['iter'][idx]) - plot_values.append(epoch_logs[metric][idx]) - ax = plt.gca() - label = legend[i * num_metrics + j] - if metric in ['mIoU', 'mAcc', 'aAcc']: - ax.set_xticks(plot_epochs) - plt.xlabel('epoch') - plt.plot(plot_epochs, plot_values, label=label, marker='o') - else: - plt.xlabel('iter') - plt.plot(plot_iters, plot_values, label=label, linewidth=0.5) - plt.legend() - if args.title is not None: - plt.title(args.title) - if args.out is None: - plt.show() - else: - print(f'save curve to: {args.out}') - plt.savefig(args.out) - plt.cla() - - -def parse_args(): - parser = argparse.ArgumentParser(description='Analyze Json Log') - parser.add_argument( - 'json_logs', - type=str, - nargs='+', - help='path of train log in json format') - parser.add_argument( - '--keys', - type=str, - nargs='+', - default=['mIoU'], - help='the metric that you want to plot') - parser.add_argument('--title', type=str, help='title of figure') - parser.add_argument( - '--legend', - type=str, - nargs='+', - default=None, - help='legend of each plot') - parser.add_argument( - '--backend', type=str, default=None, help='backend of plt') - parser.add_argument( - '--style', type=str, default='dark', help='style of plt') - parser.add_argument('--out', type=str, default=None) - args = parser.parse_args() - return args - - -def load_json_logs(json_logs): - # load and convert json_logs to log_dict, key is epoch, value is a sub dict - # keys of sub dict is different metrics - # value of sub dict is a list of corresponding values of all iterations - log_dicts = [dict() for _ in json_logs] - for json_log, log_dict in zip(json_logs, log_dicts): - with open(json_log, 'r') as log_file: - for line in log_file: - log = json.loads(line.strip()) - # skip lines without `epoch` field - if 'epoch' not in log: - continue - epoch = log.pop('epoch') - if epoch not in log_dict: - log_dict[epoch] = defaultdict(list) - for k, v in log.items(): - log_dict[epoch][k].append(v) - return log_dicts - - -def main(): - args = parse_args() - json_logs = args.json_logs - for json_log in json_logs: - assert json_log.endswith('.json') - log_dicts = load_json_logs(json_logs) - plot_curve(log_dicts, args) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/unet++/pytorch/tools/benchmark.py b/cv/semantic_segmentation/unet++/pytorch/tools/benchmark.py deleted file mode 100644 index f6d688848..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/tools/benchmark.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import time - -import mmcv -import numpy as np -import torch -from mmcv import Config -from mmcv.parallel import MMDataParallel -from mmcv.runner import load_checkpoint, wrap_fp16_model - -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='MMSeg benchmark a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--log-interval', type=int, default=50, help='interval of logging') - parser.add_argument( - '--work-dir', - help=('if specified, the results will be dumped ' - 'into the directory as json')) - parser.add_argument('--repeat-times', type=int, default=1) - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.work_dir is not None: - mmcv.mkdir_or_exist(osp.abspath(args.work_dir)) - json_file = osp.join(args.work_dir, f'fps_{timestamp}.json') - else: - # use config filename as default work_dir if cfg.work_dir is None - work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - mmcv.mkdir_or_exist(osp.abspath(work_dir)) - json_file = osp.join(work_dir, f'fps_{timestamp}.json') - - repeat_times = args.repeat_times - # set cudnn_benchmark - torch.backends.cudnn.benchmark = False - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - benchmark_dict = dict(config=args.config, unit='img / s') - overall_fps_list = [] - for time_index in range(repeat_times): - print(f'Run {time_index + 1}:') - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - data_loader = build_dataloader( - dataset, - samples_per_gpu=1, - workers_per_gpu=cfg.data.workers_per_gpu, - dist=False, - shuffle=False) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - if 'checkpoint' in args and osp.exists(args.checkpoint): - load_checkpoint(model, args.checkpoint, map_location='cpu') - - model = MMDataParallel(model, device_ids=[0]) - - model.eval() - - # the first several iterations may be very slow so skip them - num_warmup = 5 - pure_inf_time = 0 - total_iters = 200 - - # benchmark with 200 image and take the average - for i, data in enumerate(data_loader): - - torch.cuda.synchronize() - start_time = time.perf_counter() - - with torch.no_grad(): - model(return_loss=False, rescale=True, **data) - - torch.cuda.synchronize() - elapsed = time.perf_counter() - start_time - - if i >= num_warmup: - pure_inf_time += elapsed - if (i + 1) % args.log_interval == 0: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Done image [{i + 1:<3}/ {total_iters}], ' - f'fps: {fps:.2f} img / s') - - if (i + 1) == total_iters: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Overall fps: {fps:.2f} img / s\n') - benchmark_dict[f'overall_fps_{time_index + 1}'] = round(fps, 2) - overall_fps_list.append(fps) - break - benchmark_dict['average_fps'] = round(np.mean(overall_fps_list), 2) - benchmark_dict['fps_variance'] = round(np.var(overall_fps_list), 4) - print(f'Average fps of {repeat_times} evaluations: ' - f'{benchmark_dict["average_fps"]}') - print(f'The variance of {repeat_times} evaluations: ' - f'{benchmark_dict["fps_variance"]}') - mmcv.dump(benchmark_dict, json_file, indent=4) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/unet++/pytorch/tools/confusion_matrix.py b/cv/semantic_segmentation/unet++/pytorch/tools/confusion_matrix.py deleted file mode 100644 index 2c5b64cf4..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/tools/confusion_matrix.py +++ /dev/null @@ -1,184 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os - -import matplotlib.pyplot as plt -import mmcv -import numpy as np -from matplotlib.ticker import MultipleLocator -from mmcv import Config, DictAction - -from mmseg.datasets import build_dataset - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Generate confusion matrix from segmentation results') - parser.add_argument('config', help='test config file path') - parser.add_argument( - 'prediction_path', help='prediction path where test .pkl result') - parser.add_argument( - 'save_dir', help='directory where confusion matrix will be saved') - parser.add_argument( - '--show', action='store_true', help='show confusion matrix') - parser.add_argument( - '--color-theme', - default='winter', - help='theme of the matrix color map') - parser.add_argument( - '--title', - default='Normalized Confusion Matrix', - help='title of the matrix color map') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - args = parser.parse_args() - return args - - -def calculate_confusion_matrix(dataset, results): - """Calculate the confusion matrix. - - Args: - dataset (Dataset): Test or val dataset. - results (list[ndarray]): A list of segmentation results in each image. - """ - n = len(dataset.CLASSES) - confusion_matrix = np.zeros(shape=[n, n]) - assert len(dataset) == len(results) - prog_bar = mmcv.ProgressBar(len(results)) - for idx, per_img_res in enumerate(results): - res_segm = per_img_res - gt_segm = dataset.get_gt_seg_map_by_idx(idx) - inds = n * gt_segm + res_segm - inds = inds.flatten() - mat = np.bincount(inds, minlength=n**2).reshape(n, n) - confusion_matrix += mat - prog_bar.update() - return confusion_matrix - - -def plot_confusion_matrix(confusion_matrix, - labels, - save_dir=None, - show=True, - title='Normalized Confusion Matrix', - color_theme='winter'): - """Draw confusion matrix with matplotlib. - - Args: - confusion_matrix (ndarray): The confusion matrix. - labels (list[str]): List of class names. - save_dir (str|optional): If set, save the confusion matrix plot to the - given path. Default: None. - show (bool): Whether to show the plot. Default: True. - title (str): Title of the plot. Default: `Normalized Confusion Matrix`. - color_theme (str): Theme of the matrix color map. Default: `winter`. - """ - # normalize the confusion matrix - per_label_sums = confusion_matrix.sum(axis=1)[:, np.newaxis] - confusion_matrix = \ - confusion_matrix.astype(np.float32) / per_label_sums * 100 - - num_classes = len(labels) - fig, ax = plt.subplots( - figsize=(2 * num_classes, 2 * num_classes * 0.8), dpi=180) - cmap = plt.get_cmap(color_theme) - im = ax.imshow(confusion_matrix, cmap=cmap) - plt.colorbar(mappable=im, ax=ax) - - title_font = {'weight': 'bold', 'size': 12} - ax.set_title(title, fontdict=title_font) - label_font = {'size': 10} - plt.ylabel('Ground Truth Label', fontdict=label_font) - plt.xlabel('Prediction Label', fontdict=label_font) - - # draw locator - xmajor_locator = MultipleLocator(1) - xminor_locator = MultipleLocator(0.5) - ax.xaxis.set_major_locator(xmajor_locator) - ax.xaxis.set_minor_locator(xminor_locator) - ymajor_locator = MultipleLocator(1) - yminor_locator = MultipleLocator(0.5) - ax.yaxis.set_major_locator(ymajor_locator) - ax.yaxis.set_minor_locator(yminor_locator) - - # draw grid - ax.grid(True, which='minor', linestyle='-') - - # draw label - ax.set_xticks(np.arange(num_classes)) - ax.set_yticks(np.arange(num_classes)) - ax.set_xticklabels(labels) - ax.set_yticklabels(labels) - - ax.tick_params( - axis='x', bottom=False, top=True, labelbottom=False, labeltop=True) - plt.setp( - ax.get_xticklabels(), rotation=45, ha='left', rotation_mode='anchor') - - # draw confusion matrix value - for i in range(num_classes): - for j in range(num_classes): - ax.text( - j, - i, - '{}%'.format( - round(confusion_matrix[i, j], 2 - ) if not np.isnan(confusion_matrix[i, j]) else -1), - ha='center', - va='center', - color='w', - size=7) - - ax.set_ylim(len(confusion_matrix) - 0.5, -0.5) # matplotlib>3.1.1 - - fig.tight_layout() - if save_dir is not None: - plt.savefig( - os.path.join(save_dir, 'confusion_matrix.png'), format='png') - if show: - plt.show() - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - results = mmcv.load(args.prediction_path) - - assert isinstance(results, list) - if isinstance(results[0], np.ndarray): - pass - else: - raise TypeError('invalid type of prediction results') - - if isinstance(cfg.data.test, dict): - cfg.data.test.test_mode = True - elif isinstance(cfg.data.test, list): - for ds_cfg in cfg.data.test: - ds_cfg.test_mode = True - - dataset = build_dataset(cfg.data.test) - confusion_matrix = calculate_confusion_matrix(dataset, results) - plot_confusion_matrix( - confusion_matrix, - dataset.CLASSES, - save_dir=args.save_dir, - show=args.show, - title=args.title, - color_theme=args.color_theme) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/unet++/pytorch/tools/convert_datasets/drive.py b/cv/semantic_segmentation/unet++/pytorch/tools/convert_datasets/drive.py deleted file mode 100644 index 4ede22c59..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/tools/convert_datasets/drive.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os -import os.path as osp -import tempfile -import zipfile - -import cv2 -import mmcv - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert DRIVE dataset to mmsegmentation format') - parser.add_argument( - 'training_path', help='the training part of DRIVE dataset') - parser.add_argument( - 'testing_path', help='the testing part of DRIVE dataset') - parser.add_argument('--tmp_dir', help='path of the temporary directory') - parser.add_argument('-o', '--out_dir', help='output path') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - training_path = args.training_path - testing_path = args.testing_path - if args.out_dir is None: - out_dir = osp.join('data', 'DRIVE') - else: - out_dir = args.out_dir - - print('Making directories...') - mmcv.mkdir_or_exist(out_dir) - mmcv.mkdir_or_exist(osp.join(out_dir, 'images')) - mmcv.mkdir_or_exist(osp.join(out_dir, 'images', 'training')) - mmcv.mkdir_or_exist(osp.join(out_dir, 'images', 'validation')) - mmcv.mkdir_or_exist(osp.join(out_dir, 'annotations')) - mmcv.mkdir_or_exist(osp.join(out_dir, 'annotations', 'training')) - mmcv.mkdir_or_exist(osp.join(out_dir, 'annotations', 'validation')) - - with tempfile.TemporaryDirectory(dir=args.tmp_dir) as tmp_dir: - print('Extracting training.zip...') - zip_file = zipfile.ZipFile(training_path) - zip_file.extractall(tmp_dir) - - print('Generating training dataset...') - now_dir = osp.join(tmp_dir, 'training', 'images') - for img_name in os.listdir(now_dir): - img = mmcv.imread(osp.join(now_dir, img_name)) - mmcv.imwrite( - img, - osp.join( - out_dir, 'images', 'training', - osp.splitext(img_name)[0].replace('_training', '') + - '.png')) - - now_dir = osp.join(tmp_dir, 'training', '1st_manual') - for img_name in os.listdir(now_dir): - cap = cv2.VideoCapture(osp.join(now_dir, img_name)) - ret, img = cap.read() - mmcv.imwrite( - img[:, :, 0] // 128, - osp.join(out_dir, 'annotations', 'training', - osp.splitext(img_name)[0] + '.png')) - - print('Extracting test.zip...') - zip_file = zipfile.ZipFile(testing_path) - zip_file.extractall(tmp_dir) - - print('Generating validation dataset...') - now_dir = osp.join(tmp_dir, 'test', 'images') - for img_name in os.listdir(now_dir): - img = mmcv.imread(osp.join(now_dir, img_name)) - mmcv.imwrite( - img, - osp.join( - out_dir, 'images', 'validation', - osp.splitext(img_name)[0].replace('_test', '') + '.png')) - - now_dir = osp.join(tmp_dir, 'test', '1st_manual') - if osp.exists(now_dir): - for img_name in os.listdir(now_dir): - cap = cv2.VideoCapture(osp.join(now_dir, img_name)) - ret, img = cap.read() - # The annotation img should be divided by 128, because some of - # the annotation imgs are not standard. We should set a - # threshold to convert the nonstandard annotation imgs. The - # value divided by 128 is equivalent to '1 if value >= 128 - # else 0' - mmcv.imwrite( - img[:, :, 0] // 128, - osp.join(out_dir, 'annotations', 'validation', - osp.splitext(img_name)[0] + '.png')) - - now_dir = osp.join(tmp_dir, 'test', '2nd_manual') - if osp.exists(now_dir): - for img_name in os.listdir(now_dir): - cap = cv2.VideoCapture(osp.join(now_dir, img_name)) - ret, img = cap.read() - mmcv.imwrite( - img[:, :, 0] // 128, - osp.join(out_dir, 'annotations', 'validation', - osp.splitext(img_name)[0] + '.png')) - - print('Removing the temporary files...') - - print('Done!') - - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/cv/semantic_segmentation/unet++/pytorch/tools/get_flops.py b/cv/semantic_segmentation/unet++/pytorch/tools/get_flops.py deleted file mode 100644 index e30c36fdf..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/tools/get_flops.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse - -from mmcv import Config -from mmcv.cnn import get_model_complexity_info - -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Get the FLOPs of a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument( - '--shape', - type=int, - nargs='+', - default=[2048, 1024], - help='input image size') - args = parser.parse_args() - return args - - -def main(): - - args = parse_args() - - if len(args.shape) == 1: - input_shape = (3, args.shape[0], args.shape[0]) - elif len(args.shape) == 2: - input_shape = (3, ) + tuple(args.shape) - else: - raise ValueError('invalid input shape') - - cfg = Config.fromfile(args.config) - cfg.model.pretrained = None - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')).cuda() - model.eval() - - if hasattr(model, 'forward_dummy'): - model.forward = model.forward_dummy - else: - raise NotImplementedError( - 'FLOPs counter is currently not currently supported with {}'. - format(model.__class__.__name__)) - - flops, params = get_model_complexity_info(model, input_shape) - split_line = '=' * 30 - print('{0}\nInput shape: {1}\nFlops: {2}\nParams: {3}\n{0}'.format( - split_line, input_shape, flops, params)) - print('!!!Please be cautious if you use the results in papers. ' - 'You may need to check if all ops are supported and verify that the ' - 'flops computation is correct.') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/unet++/pytorch/tools/print_config.py b/cv/semantic_segmentation/unet++/pytorch/tools/print_config.py deleted file mode 100644 index 3f9c08dd9..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/tools/print_config.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import warnings - -from mmcv import Config, DictAction - -from mmseg.apis import init_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='Print the whole config') - parser.add_argument('config', help='config file path') - parser.add_argument( - '--graph', action='store_true', help='print the models graph') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - args = parser.parse_args() - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options, ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - print(f'Config:\n{cfg.pretty_text}') - # dump config - cfg.dump('example.py') - # dump models graph - if args.graph: - model = init_segmentor(args.config, device='cpu') - print(f'Model graph:\n{str(model)}') - with open('example-graph.txt', 'w') as f: - f.writelines(str(model)) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/unet++/pytorch/tools/slurm_test.sh b/cv/semantic_segmentation/unet++/pytorch/tools/slurm_test.sh deleted file mode 100755 index 4e6f7bf4e..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/tools/slurm_test.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -set -x - -PARTITION=$1 -JOB_NAME=$2 -CONFIG=$3 -CHECKPOINT=$4 -GPUS=${GPUS:-4} -GPUS_PER_NODE=${GPUS_PER_NODE:-4} -CPUS_PER_TASK=${CPUS_PER_TASK:-5} -PY_ARGS=${@:5} -SRUN_ARGS=${SRUN_ARGS:-""} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -srun -p ${PARTITION} \ - --job-name=${JOB_NAME} \ - --gres=gpu:${GPUS_PER_NODE} \ - --ntasks=${GPUS} \ - --ntasks-per-node=${GPUS_PER_NODE} \ - --cpus-per-task=${CPUS_PER_TASK} \ - --kill-on-bad-exit=1 \ - ${SRUN_ARGS} \ - python -u tools/test.py ${CONFIG} ${CHECKPOINT} --launcher="slurm" ${PY_ARGS} diff --git a/cv/semantic_segmentation/unet++/pytorch/tools/slurm_train.sh b/cv/semantic_segmentation/unet++/pytorch/tools/slurm_train.sh deleted file mode 100755 index ab232105f..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/tools/slurm_train.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -set -x - -PARTITION=$1 -JOB_NAME=$2 -CONFIG=$3 -GPUS=${GPUS:-4} -GPUS_PER_NODE=${GPUS_PER_NODE:-4} -CPUS_PER_TASK=${CPUS_PER_TASK:-5} -SRUN_ARGS=${SRUN_ARGS:-""} -PY_ARGS=${@:4} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -srun -p ${PARTITION} \ - --job-name=${JOB_NAME} \ - --gres=gpu:${GPUS_PER_NODE} \ - --ntasks=${GPUS} \ - --ntasks-per-node=${GPUS_PER_NODE} \ - --cpus-per-task=${CPUS_PER_TASK} \ - --kill-on-bad-exit=1 \ - ${SRUN_ARGS} \ - python -u tools/train.py ${CONFIG} --launcher="slurm" ${PY_ARGS} diff --git a/cv/semantic_segmentation/unet++/pytorch/tools/test.py b/cv/semantic_segmentation/unet++/pytorch/tools/test.py deleted file mode 100644 index 12892ec9b..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/tools/test.py +++ /dev/null @@ -1,319 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os -import os.path as osp -import shutil -import time -import warnings - -import mmcv -import torch -from mmcv.cnn.utils import revert_sync_batchnorm -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (get_dist_info, init_dist, load_checkpoint, - wrap_fp16_model) -from mmcv.utils import DictAction - -from mmseg import digit_version -from mmseg.apis import multi_gpu_test, single_gpu_test -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor -from mmseg.utils import setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser( - description='mmseg test (and eval) a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--work-dir', - help=('if specified, the evaluation metric results will be dumped' - 'into the directory as json')) - parser.add_argument( - '--aug-test', action='store_true', help='Use Flip and Multi scale aug') - parser.add_argument('--out', help='output result file in pickle format') - parser.add_argument( - '--format-only', - action='store_true', - help='Format the output results without perform evaluation. It is' - 'useful when you want to format the result to a specific format and ' - 'submit it to the test server') - parser.add_argument( - '--eval', - type=str, - nargs='+', - help='evaluation metrics, which depends on the dataset, e.g., "mIoU"' - ' for generic datasets, and "cityscapes" for Cityscapes') - parser.add_argument('--show', action='store_true', help='show results') - parser.add_argument( - '--show-dir', help='directory where painted images will be saved') - parser.add_argument( - '--gpu-collect', - action='store_true', - help='whether to use gpu to collect results.') - parser.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed testing)') - parser.add_argument( - '--tmpdir', - help='tmp directory used for collecting results from multiple ' - 'workers, available when gpu_collect is not specified') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--eval-options', - nargs='+', - action=DictAction, - help='custom options for evaluation') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument( - '--opacity', - type=float, - default=0.5, - help='Opacity of painted segmentation map. In (0, 1] range.') - parser.add_argument('--local_rank', type=int, default=0) - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - assert args.out or args.eval or args.format_only or args.show \ - or args.show_dir, \ - ('Please specify at least one operation (save/eval/format/show the ' - 'results / save the results) with the argument "--out", "--eval"' - ', "--format-only", "--show" or "--show-dir"') - - if args.eval and args.format_only: - raise ValueError('--eval and --format_only cannot be both specified') - - if args.out is not None and not args.out.endswith(('.pkl', '.pickle')): - raise ValueError('The output file must be a pkl file.') - - cfg = mmcv.Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - if args.aug_test: - # hard code index - cfg.data.test.pipeline[1].img_ratios = [ - 0.5, 0.75, 1.0, 1.25, 1.5, 1.75 - ] - cfg.data.test.pipeline[1].flip = True - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - if args.gpu_id is not None: - cfg.gpu_ids = [args.gpu_id] - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - cfg.gpu_ids = [args.gpu_id] - distributed = False - if len(cfg.gpu_ids) > 1: - warnings.warn(f'The gpu-ids is reset from {cfg.gpu_ids} to ' - f'{cfg.gpu_ids[0:1]} to avoid potential error in ' - 'non-distribute testing time.') - cfg.gpu_ids = cfg.gpu_ids[0:1] - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - - rank, _ = get_dist_info() - # allows not to create - if args.work_dir is not None and rank == 0: - mmcv.mkdir_or_exist(osp.abspath(args.work_dir)) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.aug_test: - json_file = osp.join(args.work_dir, - f'eval_multi_scale_{timestamp}.json') - else: - json_file = osp.join(args.work_dir, - f'eval_single_scale_{timestamp}.json') - elif rank == 0: - work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - mmcv.mkdir_or_exist(osp.abspath(work_dir)) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.aug_test: - json_file = osp.join(work_dir, - f'eval_multi_scale_{timestamp}.json') - else: - json_file = osp.join(work_dir, - f'eval_single_scale_{timestamp}.json') - - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - # The default loader config - loader_cfg = dict( - # cfg.gpus will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - shuffle=False) - # The overall dataloader settings - loader_cfg.update({ - k: v - for k, v in cfg.data.items() if k not in [ - 'train', 'val', 'test', 'train_dataloader', 'val_dataloader', - 'test_dataloader' - ] - }) - test_loader_cfg = { - **loader_cfg, - 'samples_per_gpu': 1, - 'shuffle': False, # Not shuffle by default - **cfg.data.get('test_dataloader', {}) - } - # build the dataloader - data_loader = build_dataloader(dataset, **test_loader_cfg) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - checkpoint = load_checkpoint(model, args.checkpoint, map_location='cpu') - if 'CLASSES' in checkpoint.get('meta', {}): - model.CLASSES = checkpoint['meta']['CLASSES'] - else: - print('"CLASSES" not found in meta, use dataset.CLASSES instead') - model.CLASSES = dataset.CLASSES - if 'PALETTE' in checkpoint.get('meta', {}): - model.PALETTE = checkpoint['meta']['PALETTE'] - else: - print('"PALETTE" not found in meta, use dataset.PALETTE instead') - model.PALETTE = dataset.PALETTE - - # clean gpu memory when starting a new evaluation. - torch.cuda.empty_cache() - eval_kwargs = {} if args.eval_options is None else args.eval_options - - # Deprecated - efficient_test = eval_kwargs.get('efficient_test', False) - if efficient_test: - warnings.warn( - '``efficient_test=True`` does not have effect in tools/test.py, ' - 'the evaluation and format results are CPU memory efficient by ' - 'default') - - eval_on_format_results = ( - args.eval is not None and 'cityscapes' in args.eval) - if eval_on_format_results: - assert len(args.eval) == 1, 'eval on format results is not ' \ - 'applicable for metrics other than ' \ - 'cityscapes' - if args.format_only or eval_on_format_results: - if 'imgfile_prefix' in eval_kwargs: - tmpdir = eval_kwargs['imgfile_prefix'] - else: - tmpdir = '.format_cityscapes' - eval_kwargs.setdefault('imgfile_prefix', tmpdir) - mmcv.mkdir_or_exist(tmpdir) - else: - tmpdir = None - - if not distributed: - warnings.warn( - 'SyncBN is only supported with DDP. To be compatible with DP, ' - 'we convert SyncBN to BN. Please use dist_train.sh which can ' - 'avoid this error.') - if not torch.cuda.is_available(): - assert digit_version(mmcv.__version__) >= digit_version('1.4.4'), \ - 'Please use MMCV >= 1.4.4 for CPU training!' - model = revert_sync_batchnorm(model) - model = MMDataParallel(model, device_ids=cfg.gpu_ids) - results = single_gpu_test( - model, - data_loader, - args.show, - args.show_dir, - False, - args.opacity, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - else: - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False) - results = multi_gpu_test( - model, - data_loader, - args.tmpdir, - args.gpu_collect, - False, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - - rank, _ = get_dist_info() - if rank == 0: - if args.out: - warnings.warn( - 'The behavior of ``args.out`` has been changed since MMSeg ' - 'v0.16, the pickled outputs could be seg map as type of ' - 'np.array, pre-eval results or file paths for ' - '``dataset.format_results()``.') - print(f'\nwriting results to {args.out}') - mmcv.dump(results, args.out) - if args.eval: - eval_kwargs.update(metric=args.eval) - metric = dataset.evaluate(results, **eval_kwargs) - metric_dict = dict(config=args.config, metric=metric) - mmcv.dump(metric_dict, json_file, indent=4) - if tmpdir is not None and eval_on_format_results: - # remove tmp dir when cityscapes evaluation - shutil.rmtree(tmpdir) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/unet++/pytorch/train.py b/cv/semantic_segmentation/unet++/pytorch/train.py deleted file mode 100644 index e198dd60f..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/train.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import copy -import os -import os.path as osp -import time -import warnings - -import mmcv -import torch -import torch.distributed as dist -from mmcv.cnn.utils import revert_sync_batchnorm -from mmcv.runner import get_dist_info, init_dist -from mmcv.utils import Config, DictAction, get_git_hash - -from mmseg import __version__ -from mmseg.apis import init_random_seed, set_random_seed, train_segmentor -from mmseg.datasets import build_dataset -from mmseg.models import build_segmentor -from mmseg.utils import collect_env, get_root_logger, setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument('--work-dir', help='the dir to save logs and models') - parser.add_argument( - '--load-from', help='the checkpoint file to load weights from') - parser.add_argument( - '--resume-from', help='the checkpoint file to resume from') - parser.add_argument( - '--no-validate', - action='store_true', - help='whether not to evaluate the checkpoint during training') - group_gpus = parser.add_mutually_exclusive_group() - group_gpus.add_argument( - '--gpus', - type=int, - help='(Deprecated, please use --gpu-id) number of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-ids', - type=int, - nargs='+', - help='(Deprecated, please use --gpu-id) ids of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed training)') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--diff_seed', - action='store_true', - help='Whether or not set different seeds for different ranks') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - parser.add_argument('--dist_backend', type=str, default=None) - parser.add_argument( - '--auto-resume', - action='store_true', - help='resume from the latest checkpoint automatically.') - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - - # work_dir is determined in this priority: CLI > segment in file > filename - if args.work_dir is not None: - # update configs according to CLI args if args.work_dir is not None - cfg.work_dir = args.work_dir - elif cfg.get('work_dir', None) is None: - # use config filename as default work_dir if cfg.work_dir is None - cfg.work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - if args.load_from is not None: - cfg.load_from = args.load_from - if args.resume_from is not None: - cfg.resume_from = args.resume_from - if args.gpus is not None: - cfg.gpu_ids = range(1) - warnings.warn('`--gpus` is deprecated because we only support ' - 'single GPU mode in non-distributed training. ' - 'Use `gpus=1` now.') - if args.gpu_ids is not None: - cfg.gpu_ids = args.gpu_ids[0:1] - warnings.warn('`--gpu-ids` is deprecated, please use `--gpu-id`. ' - 'Because we only support single GPU mode in ' - 'non-distributed training. Use the first GPU ' - 'in `gpu_ids` now.') - if args.gpus is None and args.gpu_ids is None: - cfg.gpu_ids = [args.gpu_id] - - cfg.auto_resume = args.auto_resume - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - if args.dist_backend is not None: - cfg.dist_params.backend = args.dist_backend - init_dist(args.launcher, **cfg.dist_params) - # gpu_ids is used to calculate iter when resuming checkpoint - _, world_size = get_dist_info() - cfg.gpu_ids = range(world_size) - - # create work_dir - mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) - # dump config - cfg.dump(osp.join(cfg.work_dir, osp.basename(args.config))) - # init the logger before other steps - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - log_file = osp.join(cfg.work_dir, f'{timestamp}.log') - logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) - - # set multi-process settings - setup_multi_processes(cfg) - - # init the meta dict to record some important information such as - # environment info and seed, which will be logged - meta = dict() - # log env info - env_info_dict = collect_env() - env_info = '\n'.join([f'{k}: {v}' for k, v in env_info_dict.items()]) - dash_line = '-' * 60 + '\n' - logger.info('Environment info:\n' + dash_line + env_info + '\n' + - dash_line) - meta['env_info'] = env_info - - # log some basic info - logger.info(f'Distributed training: {distributed}') - logger.info(f'Config:\n{cfg.pretty_text}') - - # set random seeds - seed = init_random_seed(args.seed) - seed = seed + dist.get_rank() if args.diff_seed else seed - logger.info(f'Set random seed to {seed}, ' - f'deterministic: {args.deterministic}') - set_random_seed(seed, deterministic=args.deterministic) - cfg.seed = seed - meta['seed'] = seed - meta['exp_name'] = osp.basename(args.config) - - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')) - model.init_weights() - - # SyncBN is not support for DP - if not distributed: - warnings.warn( - 'SyncBN is only supported with DDP. To be compatible with DP, ' - 'we convert SyncBN to BN. Please use dist_train.sh which can ' - 'avoid this error.') - model = revert_sync_batchnorm(model) - - logger.info(model) - - datasets = [build_dataset(cfg.data.train)] - if len(cfg.workflow) == 2: - val_dataset = copy.deepcopy(cfg.data.val) - val_dataset.pipeline = cfg.data.train.pipeline - datasets.append(build_dataset(val_dataset)) - if cfg.checkpoint_config is not None: - # save mmseg version, config file content and class names in - # checkpoints as meta data - cfg.checkpoint_config.meta = dict( - mmseg_version=f'{__version__}+{get_git_hash()[:7]}', - config=cfg.pretty_text, - CLASSES=datasets[0].CLASSES, - PALETTE=datasets[0].PALETTE) - # add an attribute for visualization convenience - model.CLASSES = datasets[0].CLASSES - # passing checkpoint meta for saving best checkpoint - meta.update(cfg.checkpoint_config.meta) - train_segmentor( - model, - datasets, - cfg, - distributed=distributed, - validate=(not args.no_validate), - timestamp=timestamp, - meta=meta) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/unet++/pytorch/train_dist.sh b/cv/semantic_segmentation/unet++/pytorch/train_dist.sh deleted file mode 100755 index d09675129..000000000 --- a/cv/semantic_segmentation/unet++/pytorch/train_dist.sh +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - -CONFIG=$1 -GPUS=$2 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/train.py \ - $CONFIG \ - --launcher pytorch ${@:3} -- Gitee From 04d8d12c1d7a4b967145772f89879f0cec64ba6e Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Wed, 5 Mar 2025 17:26:10 +0800 Subject: [PATCH 03/15] update solo --- .../solo/pytorch/.gitignore | 126 - cv/instance_segmentation/solo/pytorch/LICENSE | 203 -- .../solo/pytorch/README.md | 29 +- .../configs/_base_/datasets/coco_instance.py | 49 - .../pytorch/configs/_base_/default_runtime.py | 27 - .../configs/_base_/schedules/schedule_1x.py | 11 - .../solo/pytorch/configs/solo/README.md | 54 - .../decoupled_solo_light_r50_fpn_3x_coco.py | 63 - .../solo/decoupled_solo_r50_fpn_1x_coco.py | 28 - .../solo/decoupled_solo_r50_fpn_3x_coco.py | 25 - .../solo/pytorch/configs/solo/metafile.yml | 115 - .../configs/solo/solo_r50_fpn_1x_coco.py | 53 - .../configs/solo/solo_r50_fpn_3x_coco.py | 28 - .../solo/pytorch/docker/Dockerfile | 25 - .../solo/pytorch/docker/serve/Dockerfile | 49 - .../pytorch/docker/serve/config.properties | 5 - .../solo/pytorch/docker/serve/entrypoint.sh | 12 - .../solo/pytorch/mmcv/__init__.py | 11 - .../solo/pytorch/mmcv/cnn/__init__.py | 41 - .../solo/pytorch/mmcv/cnn/alexnet.py | 61 - .../solo/pytorch/mmcv/cnn/bricks/__init__.py | 35 - .../pytorch/mmcv/cnn/bricks/activation.py | 92 - .../pytorch/mmcv/cnn/bricks/context_block.py | 125 - .../solo/pytorch/mmcv/cnn/bricks/conv.py | 44 - .../cnn/bricks/conv2d_adaptive_padding.py | 62 - .../pytorch/mmcv/cnn/bricks/conv_module.py | 206 -- .../solo/pytorch/mmcv/cnn/bricks/conv_ws.py | 148 - .../bricks/depthwise_separable_conv_module.py | 96 - .../solo/pytorch/mmcv/cnn/bricks/drop.py | 65 - .../mmcv/cnn/bricks/generalized_attention.py | 412 --- .../solo/pytorch/mmcv/cnn/bricks/hsigmoid.py | 34 - .../solo/pytorch/mmcv/cnn/bricks/hswish.py | 29 - .../solo/pytorch/mmcv/cnn/bricks/non_local.py | 306 -- .../solo/pytorch/mmcv/cnn/bricks/norm.py | 144 - .../solo/pytorch/mmcv/cnn/bricks/padding.py | 36 - .../solo/pytorch/mmcv/cnn/bricks/plugin.py | 88 - .../solo/pytorch/mmcv/cnn/bricks/registry.py | 16 - .../solo/pytorch/mmcv/cnn/bricks/scale.py | 21 - .../solo/pytorch/mmcv/cnn/bricks/swish.py | 25 - .../pytorch/mmcv/cnn/bricks/transformer.py | 595 ---- .../solo/pytorch/mmcv/cnn/bricks/upsample.py | 84 - .../solo/pytorch/mmcv/cnn/bricks/wrappers.py | 180 - .../solo/pytorch/mmcv/cnn/builder.py | 30 - .../solo/pytorch/mmcv/cnn/resnet.py | 316 -- .../solo/pytorch/mmcv/cnn/utils/__init__.py | 19 - .../pytorch/mmcv/cnn/utils/flops_counter.py | 599 ---- .../pytorch/mmcv/cnn/utils/fuse_conv_bn.py | 59 - .../solo/pytorch/mmcv/cnn/utils/sync_bn.py | 59 - .../pytorch/mmcv/cnn/utils/weight_init.py | 684 ---- .../solo/pytorch/mmcv/cnn/vgg.py | 175 - .../solo/pytorch/mmcv/engine/__init__.py | 8 - .../solo/pytorch/mmcv/engine/test.py | 202 -- .../solo/pytorch/mmcv/fileio/__init__.py | 11 - .../solo/pytorch/mmcv/fileio/file_client.py | 1148 ------- .../pytorch/mmcv/fileio/handlers/__init__.py | 7 - .../solo/pytorch/mmcv/fileio/handlers/base.py | 30 - .../mmcv/fileio/handlers/json_handler.py | 36 - .../mmcv/fileio/handlers/pickle_handler.py | 28 - .../mmcv/fileio/handlers/yaml_handler.py | 24 - .../solo/pytorch/mmcv/fileio/io.py | 151 - .../solo/pytorch/mmcv/fileio/parse.py | 97 - .../solo/pytorch/mmcv/image/__init__.py | 28 - .../solo/pytorch/mmcv/image/colorspace.py | 306 -- .../solo/pytorch/mmcv/image/geometric.py | 728 ---- .../solo/pytorch/mmcv/image/io.py | 258 -- .../solo/pytorch/mmcv/image/misc.py | 44 - .../solo/pytorch/mmcv/image/photometric.py | 428 --- .../pytorch/mmcv/model_zoo/deprecated.json | 6 - .../solo/pytorch/mmcv/model_zoo/mmcls.json | 31 - .../pytorch/mmcv/model_zoo/open_mmlab.json | 50 - .../solo/pytorch/mmcv/ops/__init__.py | 13 - .../solo/pytorch/mmcv/ops/csrc/README.md | 169 - .../csrc/common/cuda/common_cuda_helper.hpp | 112 - .../ops/csrc/common/cuda/nms_cuda_kernel.cuh | 74 - .../ops/csrc/common/cuda/nms_rotated_cuda.cuh | 135 - .../common/cuda/roi_align_cuda_kernel.cuh | 212 -- .../csrc/common/cuda/roi_pool_cuda_kernel.cuh | 93 - .../cuda/sigmoid_focal_loss_cuda_kernel.cuh | 71 - .../cuda/softmax_focal_loss_cuda_kernel.cuh | 72 - .../csrc/common/cuda/sync_bn_cuda_kernel.cuh | 331 -- .../ops/csrc/common/pytorch_cpp_helper.hpp | 24 - .../ops/csrc/common/pytorch_cuda_helper.hpp | 19 - .../ops/csrc/pytorch/cuda/focal_loss_cuda.cu | 111 - .../mmcv/ops/csrc/pytorch/cuda/nms_cuda.cu | 53 - .../ops/csrc/pytorch/cuda/roi_align_cuda.cu | 58 - .../ops/csrc/pytorch/cuda/roi_pool_cuda.cu | 50 - .../ops/csrc/pytorch/cuda/sync_bn_cuda.cu | 110 - .../mmcv/ops/csrc/pytorch/focal_loss.cpp | 131 - .../pytorch/mmcv/ops/csrc/pytorch/info.cpp | 56 - .../pytorch/mmcv/ops/csrc/pytorch/nms.cpp | 261 -- .../pytorch/mmcv/ops/csrc/pytorch/pybind.cpp | 131 - .../mmcv/ops/csrc/pytorch/roi_align.cpp | 130 - .../mmcv/ops/csrc/pytorch/roi_align_cpu.cpp | 431 --- .../mmcv/ops/csrc/pytorch/roi_pool.cpp | 67 - .../pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp | 159 - .../pytorch/mmcv/ops/deprecated_wrappers.py | 43 - .../solo/pytorch/mmcv/ops/focal_loss.py | 212 -- .../solo/pytorch/mmcv/ops/info.py | 36 - .../solo/pytorch/mmcv/ops/nms.py | 417 --- .../solo/pytorch/mmcv/ops/point_sample.py | 336 -- .../solo/pytorch/mmcv/ops/roi_align.py | 223 -- .../solo/pytorch/mmcv/ops/roi_pool.py | 86 - .../solo/pytorch/mmcv/ops/sync_bn.py | 279 -- .../solo/pytorch/mmcv/parallel/__init__.py | 13 - .../solo/pytorch/mmcv/parallel/_functions.py | 79 - .../solo/pytorch/mmcv/parallel/collate.py | 84 - .../pytorch/mmcv/parallel/data_container.py | 89 - .../pytorch/mmcv/parallel/data_parallel.py | 89 - .../solo/pytorch/mmcv/parallel/distributed.py | 112 - .../mmcv/parallel/distributed_deprecated.py | 70 - .../solo/pytorch/mmcv/parallel/registry.py | 8 - .../pytorch/mmcv/parallel/scatter_gather.py | 59 - .../solo/pytorch/mmcv/parallel/utils.py | 20 - .../solo/pytorch/mmcv/runner/__init__.py | 47 - .../solo/pytorch/mmcv/runner/base_module.py | 195 -- .../solo/pytorch/mmcv/runner/base_runner.py | 542 --- .../solo/pytorch/mmcv/runner/builder.py | 24 - .../solo/pytorch/mmcv/runner/checkpoint.py | 707 ---- .../mmcv/runner/default_constructor.py | 44 - .../solo/pytorch/mmcv/runner/dist_utils.py | 164 - .../pytorch/mmcv/runner/epoch_based_runner.py | 187 -- .../solo/pytorch/mmcv/runner/fp16_utils.py | 410 --- .../pytorch/mmcv/runner/hooks/__init__.py | 29 - .../pytorch/mmcv/runner/hooks/checkpoint.py | 167 - .../solo/pytorch/mmcv/runner/hooks/closure.py | 11 - .../solo/pytorch/mmcv/runner/hooks/ema.py | 89 - .../pytorch/mmcv/runner/hooks/evaluation.py | 509 --- .../solo/pytorch/mmcv/runner/hooks/hook.py | 92 - .../pytorch/mmcv/runner/hooks/iter_timer.py | 18 - .../mmcv/runner/hooks/logger/__init__.py | 15 - .../pytorch/mmcv/runner/hooks/logger/base.py | 166 - .../mmcv/runner/hooks/logger/dvclive.py | 58 - .../mmcv/runner/hooks/logger/mlflow.py | 78 - .../mmcv/runner/hooks/logger/neptune.py | 82 - .../pytorch/mmcv/runner/hooks/logger/pavi.py | 117 - .../mmcv/runner/hooks/logger/tensorboard.py | 57 - .../pytorch/mmcv/runner/hooks/logger/text.py | 256 -- .../pytorch/mmcv/runner/hooks/logger/wandb.py | 56 - .../pytorch/mmcv/runner/hooks/lr_updater.py | 670 ---- .../solo/pytorch/mmcv/runner/hooks/memory.py | 25 - .../mmcv/runner/hooks/momentum_updater.py | 493 --- .../pytorch/mmcv/runner/hooks/optimizer.py | 508 --- .../pytorch/mmcv/runner/hooks/profiler.py | 180 - .../pytorch/mmcv/runner/hooks/sampler_seed.py | 20 - .../pytorch/mmcv/runner/hooks/sync_buffer.py | 22 - .../pytorch/mmcv/runner/iter_based_runner.py | 273 -- .../solo/pytorch/mmcv/runner/log_buffer.py | 41 - .../pytorch/mmcv/runner/optimizer/__init__.py | 9 - .../pytorch/mmcv/runner/optimizer/builder.py | 44 - .../runner/optimizer/default_constructor.py | 247 -- .../solo/pytorch/mmcv/runner/priority.py | 60 - .../solo/pytorch/mmcv/runner/utils.py | 93 - .../solo/pytorch/mmcv/utils/__init__.py | 69 - .../solo/pytorch/mmcv/utils/config.py | 688 ---- .../solo/pytorch/mmcv/utils/env.py | 95 - .../solo/pytorch/mmcv/utils/ext_loader.py | 71 - .../solo/pytorch/mmcv/utils/logging.py | 110 - .../solo/pytorch/mmcv/utils/misc.py | 377 --- .../solo/pytorch/mmcv/utils/parrots_jit.py | 41 - .../pytorch/mmcv/utils/parrots_wrapper.py | 107 - .../solo/pytorch/mmcv/utils/path.py | 101 - .../solo/pytorch/mmcv/utils/progressbar.py | 208 -- .../solo/pytorch/mmcv/utils/registry.py | 315 -- .../solo/pytorch/mmcv/utils/testing.py | 140 - .../solo/pytorch/mmcv/utils/timer.py | 118 - .../solo/pytorch/mmcv/utils/trace.py | 23 - .../solo/pytorch/mmcv/utils/version_utils.py | 90 - .../solo/pytorch/mmcv/version.py | 35 - .../solo/pytorch/mmdet/__init__.py | 29 - .../solo/pytorch/mmdet/apis/__init__.py | 12 - .../solo/pytorch/mmdet/apis/inference.py | 251 -- .../solo/pytorch/mmdet/apis/test.py | 209 -- .../solo/pytorch/mmdet/apis/train.py | 244 -- .../solo/pytorch/mmdet/core/__init__.py | 10 - .../pytorch/mmdet/core/anchor/__init__.py | 14 - .../mmdet/core/anchor/anchor_generator.py | 866 ----- .../solo/pytorch/mmdet/core/anchor/builder.py | 19 - .../mmdet/core/anchor/point_generator.py | 263 -- .../solo/pytorch/mmdet/core/anchor/utils.py | 72 - .../solo/pytorch/mmdet/core/bbox/__init__.py | 28 - .../mmdet/core/bbox/assigners/__init__.py | 22 - .../bbox/assigners/approx_max_iou_assigner.py | 146 - .../core/bbox/assigners/assign_result.py | 206 -- .../core/bbox/assigners/atss_assigner.py | 234 -- .../core/bbox/assigners/base_assigner.py | 10 - .../bbox/assigners/center_region_assigner.py | 336 -- .../core/bbox/assigners/grid_assigner.py | 156 - .../core/bbox/assigners/hungarian_assigner.py | 146 - .../bbox/assigners/mask_hungarian_assigner.py | 132 - .../core/bbox/assigners/max_iou_assigner.py | 218 -- .../core/bbox/assigners/point_assigner.py | 134 - .../core/bbox/assigners/region_assigner.py | 222 -- .../core/bbox/assigners/sim_ota_assigner.py | 257 -- .../bbox/assigners/task_aligned_assigner.py | 151 - .../core/bbox/assigners/uniform_assigner.py | 135 - .../solo/pytorch/mmdet/core/bbox/builder.py | 21 - .../pytorch/mmdet/core/bbox/coder/__init__.py | 15 - .../mmdet/core/bbox/coder/base_bbox_coder.py | 18 - .../core/bbox/coder/bucketing_bbox_coder.py | 351 -- .../core/bbox/coder/delta_xywh_bbox_coder.py | 392 --- .../bbox/coder/distance_point_bbox_coder.py | 63 - .../coder/legacy_delta_xywh_bbox_coder.py | 216 -- .../core/bbox/coder/pseudo_bbox_coder.py | 19 - .../mmdet/core/bbox/coder/tblr_bbox_coder.py | 206 -- .../mmdet/core/bbox/coder/yolo_bbox_coder.py | 83 - .../solo/pytorch/mmdet/core/bbox/demodata.py | 42 - .../core/bbox/iou_calculators/__init__.py | 5 - .../core/bbox/iou_calculators/builder.py | 9 - .../bbox/iou_calculators/iou2d_calculator.py | 261 -- .../mmdet/core/bbox/match_costs/__init__.py | 9 - .../mmdet/core/bbox/match_costs/builder.py | 9 - .../mmdet/core/bbox/match_costs/match_cost.py | 359 -- .../mmdet/core/bbox/samplers/__init__.py | 19 - .../mmdet/core/bbox/samplers/base_sampler.py | 102 - .../core/bbox/samplers/combined_sampler.py | 21 - .../samplers/instance_balanced_pos_sampler.py | 56 - .../bbox/samplers/iou_balanced_neg_sampler.py | 158 - .../core/bbox/samplers/mask_pseudo_sampler.py | 44 - .../bbox/samplers/mask_sampling_result.py | 60 - .../mmdet/core/bbox/samplers/ohem_sampler.py | 111 - .../core/bbox/samplers/pseudo_sampler.py | 42 - .../core/bbox/samplers/random_sampler.py | 82 - .../core/bbox/samplers/sampling_result.py | 153 - .../core/bbox/samplers/score_hlr_sampler.py | 265 -- .../pytorch/mmdet/core/bbox/transforms.py | 270 -- .../mmdet/core/data_structures/__init__.py | 5 - .../core/data_structures/general_data.py | 326 -- .../core/data_structures/instance_data.py | 188 -- .../pytorch/mmdet/core/evaluation/__init__.py | 19 - .../mmdet/core/evaluation/bbox_overlaps.py | 65 - .../mmdet/core/evaluation/class_names.py | 332 -- .../mmdet/core/evaluation/eval_hooks.py | 140 - .../pytorch/mmdet/core/evaluation/mean_ap.py | 782 ----- .../mmdet/core/evaluation/panoptic_utils.py | 6 - .../pytorch/mmdet/core/evaluation/recall.py | 197 -- .../pytorch/mmdet/core/export/__init__.py | 12 - .../mmdet/core/export/model_wrappers.py | 183 -- .../pytorch/mmdet/core/export/onnx_helper.py | 223 -- .../pytorch/mmdet/core/export/pytorch2onnx.py | 159 - .../solo/pytorch/mmdet/core/hook/__init__.py | 17 - .../pytorch/mmdet/core/hook/checkloss_hook.py | 24 - .../solo/pytorch/mmdet/core/hook/ema.py | 130 - .../mmdet/core/hook/memory_profiler_hook.py | 55 - .../mmdet/core/hook/set_epoch_info_hook.py | 15 - .../pytorch/mmdet/core/hook/sync_norm_hook.py | 52 - .../mmdet/core/hook/sync_random_size_hook.py | 72 - .../mmdet/core/hook/wandblogger_hook.py | 587 ---- .../mmdet/core/hook/yolox_lrupdater_hook.py | 67 - .../mmdet/core/hook/yolox_mode_switch_hook.py | 52 - .../solo/pytorch/mmdet/core/mask/__init__.py | 9 - .../pytorch/mmdet/core/mask/mask_target.py | 127 - .../pytorch/mmdet/core/mask/structures.py | 1102 ------- .../solo/pytorch/mmdet/core/mask/utils.py | 89 - .../pytorch/mmdet/core/optimizers/__init__.py | 9 - .../pytorch/mmdet/core/optimizers/builder.py | 33 - .../layer_decay_optimizer_constructor.py | 154 - .../mmdet/core/post_processing/__init__.py | 10 - .../mmdet/core/post_processing/bbox_nms.py | 171 - .../mmdet/core/post_processing/matrix_nms.py | 121 - .../mmdet/core/post_processing/merge_augs.py | 154 - .../solo/pytorch/mmdet/core/utils/__init__.py | 13 - .../pytorch/mmdet/core/utils/dist_utils.py | 193 -- .../solo/pytorch/mmdet/core/utils/misc.py | 208 -- .../mmdet/core/visualization/__init__.py | 9 - .../pytorch/mmdet/core/visualization/image.py | 559 ---- .../mmdet/core/visualization/palette.py | 63 - .../solo/pytorch/mmdet/datasets/__init__.py | 12 - .../mmdet/datasets/api_wrappers/__init__.py | 7 - .../mmdet/datasets/api_wrappers/coco_api.py | 47 - .../api_wrappers/panoptic_evaluation.py | 228 -- .../solo/pytorch/mmdet/datasets/builder.py | 215 -- .../solo/pytorch/mmdet/datasets/coco.py | 649 ---- .../solo/pytorch/mmdet/datasets/custom.py | 410 --- .../mmdet/datasets/dataset_wrappers.py | 456 --- .../mmdet/datasets/pipelines/__init__.py | 17 - .../mmdet/datasets/pipelines/compose.py | 55 - .../mmdet/datasets/pipelines/formating.py | 9 - .../mmdet/datasets/pipelines/formatting.py | 392 --- .../mmdet/datasets/pipelines/loading.py | 643 ---- .../mmdet/datasets/pipelines/test_time_aug.py | 121 - .../mmdet/datasets/pipelines/transforms.py | 2919 ----------------- .../mmdet/datasets/samplers/__init__.py | 10 - .../datasets/samplers/class_aware_sampler.py | 176 - .../datasets/samplers/distributed_sampler.py | 54 - .../mmdet/datasets/samplers/group_sampler.py | 148 - .../datasets/samplers/infinite_sampler.py | 186 -- .../solo/pytorch/mmdet/datasets/utils.py | 164 - .../solo/pytorch/mmdet/models/__init__.py | 16 - .../mmdet/models/backbones/__init__.py | 6 - .../pytorch/mmdet/models/backbones/resnet.py | 672 ---- .../solo/pytorch/mmdet/models/builder.py | 59 - .../mmdet/models/dense_heads/__init__.py | 3 - .../mmdet/models/dense_heads/anchor_head.py | 542 --- .../models/dense_heads/base_dense_head.py | 526 --- .../models/dense_heads/base_mask_head.py | 116 - .../models/dense_heads/dense_test_mixins.py | 206 -- .../mmdet/models/dense_heads/solo_head.py | 1177 ------- .../mmdet/models/detectors/__init__.py | 6 - .../pytorch/mmdet/models/detectors/base.py | 360 -- .../detectors/single_stage_instance_seg.py | 343 -- .../pytorch/mmdet/models/detectors/solo.py | 30 - .../pytorch/mmdet/models/losses/__init__.py | 15 - .../pytorch/mmdet/models/losses/accuracy.py | 79 - .../pytorch/mmdet/models/losses/dice_loss.py | 146 - .../pytorch/mmdet/models/losses/focal_loss.py | 244 -- .../pytorch/mmdet/models/losses/iou_loss.py | 474 --- .../solo/pytorch/mmdet/models/losses/utils.py | 105 - .../pytorch/mmdet/models/necks/__init__.py | 7 - .../solo/pytorch/mmdet/models/necks/fpn.py | 204 -- .../pytorch/mmdet/models/utils/__init__.py | 5 - .../pytorch/mmdet/models/utils/res_layer.py | 190 -- .../solo/pytorch/mmdet/utils/__init__.py | 17 - .../solo/pytorch/mmdet/utils/collect_env.py | 17 - .../solo/pytorch/mmdet/utils/compat_config.py | 139 - .../pytorch/mmdet/utils/contextmanagers.py | 122 - .../solo/pytorch/mmdet/utils/logger.py | 65 - .../solo/pytorch/mmdet/utils/memory.py | 213 -- .../solo/pytorch/mmdet/utils/misc.py | 76 - .../solo/pytorch/mmdet/utils/profiling.py | 40 - .../pytorch/mmdet/utils/replace_cfg_vals.py | 70 - .../solo/pytorch/mmdet/utils/setup_env.py | 53 - .../solo/pytorch/mmdet/utils/split_batch.py | 45 - .../pytorch/mmdet/utils/util_distribution.py | 74 - .../solo/pytorch/mmdet/utils/util_mixins.py | 105 - .../solo/pytorch/mmdet/utils/util_random.py | 34 - .../solo/pytorch/mmdet/version.py | 19 - .../solo/pytorch/pytest.ini | 7 - .../solo/pytorch/requirements.txt | 7 - .../solo/pytorch/setup.py | 354 -- .../solo/pytorch/train.py | 243 -- .../solo/pytorch/train.sh | 17 - .../solo/pytorch/train_dist.sh | 33 - .../solov2/pytorch/README.md | 3 +- 333 files changed, 19 insertions(+), 53039 deletions(-) delete mode 100644 cv/instance_segmentation/solo/pytorch/.gitignore delete mode 100644 cv/instance_segmentation/solo/pytorch/LICENSE delete mode 100644 cv/instance_segmentation/solo/pytorch/configs/_base_/datasets/coco_instance.py delete mode 100644 cv/instance_segmentation/solo/pytorch/configs/_base_/default_runtime.py delete mode 100644 cv/instance_segmentation/solo/pytorch/configs/_base_/schedules/schedule_1x.py delete mode 100644 cv/instance_segmentation/solo/pytorch/configs/solo/README.md delete mode 100644 cv/instance_segmentation/solo/pytorch/configs/solo/decoupled_solo_light_r50_fpn_3x_coco.py delete mode 100644 cv/instance_segmentation/solo/pytorch/configs/solo/decoupled_solo_r50_fpn_1x_coco.py delete mode 100644 cv/instance_segmentation/solo/pytorch/configs/solo/decoupled_solo_r50_fpn_3x_coco.py delete mode 100644 cv/instance_segmentation/solo/pytorch/configs/solo/metafile.yml delete mode 100644 cv/instance_segmentation/solo/pytorch/configs/solo/solo_r50_fpn_1x_coco.py delete mode 100644 cv/instance_segmentation/solo/pytorch/configs/solo/solo_r50_fpn_3x_coco.py delete mode 100644 cv/instance_segmentation/solo/pytorch/docker/Dockerfile delete mode 100644 cv/instance_segmentation/solo/pytorch/docker/serve/Dockerfile delete mode 100644 cv/instance_segmentation/solo/pytorch/docker/serve/config.properties delete mode 100644 cv/instance_segmentation/solo/pytorch/docker/serve/entrypoint.sh delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/alexnet.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/activation.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/context_block.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv_module.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv_ws.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/drop.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/generalized_attention.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/hsigmoid.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/hswish.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/non_local.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/norm.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/padding.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/plugin.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/registry.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/scale.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/swish.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/transformer.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/upsample.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/wrappers.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/builder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/resnet.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/flops_counter.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/fuse_conv_bn.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/sync_bn.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/weight_init.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/cnn/vgg.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/engine/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/engine/test.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/fileio/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/fileio/file_client.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/base.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/json_handler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/pickle_handler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/yaml_handler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/fileio/io.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/fileio/parse.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/image/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/image/colorspace.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/image/geometric.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/image/io.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/image/misc.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/image/photometric.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/model_zoo/deprecated.json delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/model_zoo/mmcls.json delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/model_zoo/open_mmlab.json delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/README.md delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/nms_cuda_kernel.cuh delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/nms_rotated_cuda.cuh delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/roi_align_cuda_kernel.cuh delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/roi_pool_cuda_kernel.cuh delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/nms_cuda.cu delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_align_cuda.cu delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_pool_cuda.cu delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/info.cpp delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/nms.cpp delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/roi_align.cpp delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/roi_align_cpu.cpp delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/roi_pool.cpp delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/deprecated_wrappers.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/focal_loss.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/info.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/nms.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/point_sample.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/roi_align.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/roi_pool.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/ops/sync_bn.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/parallel/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/parallel/_functions.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/parallel/collate.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/parallel/data_container.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/parallel/data_parallel.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/parallel/distributed.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/parallel/distributed_deprecated.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/parallel/registry.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/parallel/scatter_gather.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/parallel/utils.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/base_module.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/base_runner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/builder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/checkpoint.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/default_constructor.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/dist_utils.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/epoch_based_runner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/fp16_utils.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/checkpoint.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/closure.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/ema.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/evaluation.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/hook.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/iter_timer.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/base.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/dvclive.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/mlflow.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/neptune.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/pavi.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/tensorboard.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/text.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/wandb.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/lr_updater.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/memory.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/momentum_updater.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/optimizer.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/profiler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/sampler_seed.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/sync_buffer.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/iter_based_runner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/log_buffer.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/optimizer/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/optimizer/builder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/optimizer/default_constructor.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/priority.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/runner/utils.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/config.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/env.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/ext_loader.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/logging.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/misc.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/parrots_jit.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/parrots_wrapper.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/path.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/progressbar.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/registry.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/testing.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/timer.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/trace.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/utils/version_utils.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmcv/version.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/apis/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/apis/inference.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/apis/test.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/apis/train.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/anchor_generator.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/builder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/point_generator.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/utils.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/approx_max_iou_assigner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/assign_result.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/atss_assigner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/base_assigner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/center_region_assigner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/grid_assigner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/hungarian_assigner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/mask_hungarian_assigner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/max_iou_assigner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/point_assigner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/region_assigner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/sim_ota_assigner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/task_aligned_assigner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/uniform_assigner.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/builder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/base_bbox_coder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/bucketing_bbox_coder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/delta_xywh_bbox_coder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/distance_point_bbox_coder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/legacy_delta_xywh_bbox_coder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/pseudo_bbox_coder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/tblr_bbox_coder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/yolo_bbox_coder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/demodata.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/iou_calculators/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/iou_calculators/builder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/iou_calculators/iou2d_calculator.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/match_costs/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/match_costs/builder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/match_costs/match_cost.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/base_sampler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/combined_sampler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/instance_balanced_pos_sampler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/iou_balanced_neg_sampler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/mask_pseudo_sampler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/mask_sampling_result.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/ohem_sampler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/pseudo_sampler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/random_sampler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/sampling_result.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/score_hlr_sampler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/transforms.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/data_structures/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/data_structures/general_data.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/data_structures/instance_data.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/bbox_overlaps.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/class_names.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/eval_hooks.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/mean_ap.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/panoptic_utils.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/recall.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/export/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/export/model_wrappers.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/export/onnx_helper.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/export/pytorch2onnx.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/hook/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/hook/checkloss_hook.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/hook/ema.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/hook/memory_profiler_hook.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/hook/set_epoch_info_hook.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/hook/sync_norm_hook.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/hook/sync_random_size_hook.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/hook/wandblogger_hook.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/hook/yolox_lrupdater_hook.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/hook/yolox_mode_switch_hook.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/mask/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/mask/mask_target.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/mask/structures.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/mask/utils.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/optimizers/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/optimizers/builder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/optimizers/layer_decay_optimizer_constructor.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/bbox_nms.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/matrix_nms.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/merge_augs.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/utils/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/utils/dist_utils.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/utils/misc.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/visualization/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/visualization/image.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/core/visualization/palette.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/api_wrappers/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/api_wrappers/coco_api.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/api_wrappers/panoptic_evaluation.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/builder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/coco.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/custom.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/dataset_wrappers.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/compose.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/formating.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/formatting.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/loading.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/test_time_aug.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/transforms.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/class_aware_sampler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/distributed_sampler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/group_sampler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/infinite_sampler.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/datasets/utils.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/backbones/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/backbones/resnet.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/builder.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/anchor_head.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/base_dense_head.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/base_mask_head.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/dense_test_mixins.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/solo_head.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/base.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/single_stage_instance_seg.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/solo.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/losses/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/losses/accuracy.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/losses/dice_loss.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/losses/focal_loss.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/losses/iou_loss.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/losses/utils.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/necks/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/necks/fpn.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/utils/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/models/utils/res_layer.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/__init__.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/collect_env.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/compat_config.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/contextmanagers.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/logger.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/memory.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/misc.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/profiling.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/replace_cfg_vals.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/setup_env.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/split_batch.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/util_distribution.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/util_mixins.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/utils/util_random.py delete mode 100644 cv/instance_segmentation/solo/pytorch/mmdet/version.py delete mode 100644 cv/instance_segmentation/solo/pytorch/pytest.ini delete mode 100644 cv/instance_segmentation/solo/pytorch/requirements.txt delete mode 100644 cv/instance_segmentation/solo/pytorch/setup.py delete mode 100644 cv/instance_segmentation/solo/pytorch/train.py delete mode 100644 cv/instance_segmentation/solo/pytorch/train.sh delete mode 100644 cv/instance_segmentation/solo/pytorch/train_dist.sh diff --git a/cv/instance_segmentation/solo/pytorch/.gitignore b/cv/instance_segmentation/solo/pytorch/.gitignore deleted file mode 100644 index a47ecb183..000000000 --- a/cv/instance_segmentation/solo/pytorch/.gitignore +++ /dev/null @@ -1,126 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/en/_build/ -docs/zh_cn/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -data/ -data -.vscode -.idea -.DS_Store - -# custom -*.pkl -*.pkl.json -*.log.json -docs/modelzoo_statistics.md -mmdet/.mim -work_dirs/ - -# Pytorch -*.pth -*.py~ -*.sh~ -demo/ -docs/ diff --git a/cv/instance_segmentation/solo/pytorch/LICENSE b/cv/instance_segmentation/solo/pytorch/LICENSE deleted file mode 100644 index 1bfc23e48..000000000 --- a/cv/instance_segmentation/solo/pytorch/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Copyright 2018-2023 OpenMMLab. All rights reserved. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2023 OpenMMLab. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cv/instance_segmentation/solo/pytorch/README.md b/cv/instance_segmentation/solo/pytorch/README.md index 5c39c1e61..e28f4b851 100644 --- a/cv/instance_segmentation/solo/pytorch/README.md +++ b/cv/instance_segmentation/solo/pytorch/README.md @@ -7,10 +7,20 @@ We present a new, embarrassingly simple approach to instance segmentation in ima ## Step 1: Installing packages ```bash -$ pip3 install -r requirements.txt - yum install mesa-libGL - pip3 install yapf==0.31.0 -$ MMCV_WITH_OPS=1 python3 setup.py build && cp build/lib.linux*/mmcv/_ext.cpython* mmcv +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +# install MMDetection +git clone https://github.com/open-mmlab/mmdetection.git -b v3.3.0 --depth=1 +cd mmdetection +pip install -v -e . + +# Prepare resnet50-0676ba61.pth, skip this if fast network +mkdir -p /root/.cache/torch/hub/checkpoints/ +wget https://download.pytorch.org/models/resnet50-0676ba61.pth -O /root/.cache/torch/hub/checkpoints/resnet50-0676ba61.pth ``` ## Step 2: Preparing datasets @@ -19,7 +29,6 @@ $ MMCV_WITH_OPS=1 python3 setup.py build && cp build/lib.linux*/mmcv/_ext.cpytho $ mkdir -p data/coco $ cd data/coco ``` - Go to visit [COCO official website](https://cocodataset.org/#download), then select the COCO dataset you want to download. Take coco2017 dataset as an example, specify `/path/to/coco2017` to your COCO path in later training process, the unzipped dataset path structure sholud look like: @@ -43,19 +52,18 @@ coco2017 └── ... ``` - ## Step 3: Training ### One single GPU - ```bash -bash train.sh +python3 tools/train.py configs/solo/solo_r50_fpn_1x_coco.py ``` ### Multiple GPUs on one machine ```bash -$ bash train_dist.sh [training args] # config file can be found in the configs directory +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/solo/solo_r50_fpn_1x_coco.py 8 ``` ## Results on BI-V100 @@ -63,5 +71,4 @@ $ bash train_dist.sh [training args] # config file c Average Precision (AP) @[ loU=0.50:0.95 | area= all | maxDets=1001 ] = 0.361 ## Reference - -Reference: https://github.com/WXinlong/SOLO +[mmdetection](https://github.com/open-mmlab/mmdetection) diff --git a/cv/instance_segmentation/solo/pytorch/configs/_base_/datasets/coco_instance.py b/cv/instance_segmentation/solo/pytorch/configs/_base_/datasets/coco_instance.py deleted file mode 100644 index 9901a8584..000000000 --- a/cv/instance_segmentation/solo/pytorch/configs/_base_/datasets/coco_instance.py +++ /dev/null @@ -1,49 +0,0 @@ -# dataset settings -dataset_type = 'CocoDataset' -data_root = 'data/coco/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', with_bbox=True, with_mask=True), - dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(1333, 800), - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - type=dataset_type, - ann_file=data_root + 'annotations/instances_train2017.json', - img_prefix=data_root + 'train2017/', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - ann_file=data_root + 'annotations/instances_val2017.json', - img_prefix=data_root + 'val2017/', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - ann_file=data_root + 'annotations/instances_val2017.json', - img_prefix=data_root + 'val2017/', - pipeline=test_pipeline)) -evaluation = dict(metric=['bbox', 'segm']) diff --git a/cv/instance_segmentation/solo/pytorch/configs/_base_/default_runtime.py b/cv/instance_segmentation/solo/pytorch/configs/_base_/default_runtime.py deleted file mode 100644 index 5b0b1452c..000000000 --- a/cv/instance_segmentation/solo/pytorch/configs/_base_/default_runtime.py +++ /dev/null @@ -1,27 +0,0 @@ -checkpoint_config = dict(interval=1) -# yapf:disable -log_config = dict( - interval=50, - hooks=[ - dict(type='TextLoggerHook'), - # dict(type='TensorboardLoggerHook') - ]) -# yapf:enable -custom_hooks = [dict(type='NumClassCheckHook')] - -dist_params = dict(backend='nccl') -log_level = 'INFO' -load_from = None -resume_from = None -workflow = [('train', 1)] - -# disable opencv multithreading to avoid system being overloaded -opencv_num_threads = 0 -# set multi-process start method as `fork` to speed up the training -mp_start_method = 'fork' - -# Default setting for scaling LR automatically -# - `enable` means enable scaling LR automatically -# or not by default. -# - `base_batch_size` = (8 GPUs) x (2 samples per GPU). -auto_scale_lr = dict(enable=False, base_batch_size=16) diff --git a/cv/instance_segmentation/solo/pytorch/configs/_base_/schedules/schedule_1x.py b/cv/instance_segmentation/solo/pytorch/configs/_base_/schedules/schedule_1x.py deleted file mode 100644 index 13b3783cb..000000000 --- a/cv/instance_segmentation/solo/pytorch/configs/_base_/schedules/schedule_1x.py +++ /dev/null @@ -1,11 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001) -optimizer_config = dict(grad_clip=None) -# learning policy -lr_config = dict( - policy='step', - warmup='linear', - warmup_iters=500, - warmup_ratio=0.001, - step=[8, 11]) -runner = dict(type='EpochBasedRunner', max_epochs=12) diff --git a/cv/instance_segmentation/solo/pytorch/configs/solo/README.md b/cv/instance_segmentation/solo/pytorch/configs/solo/README.md deleted file mode 100644 index 8bd043255..000000000 --- a/cv/instance_segmentation/solo/pytorch/configs/solo/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# SOLO - -> [SOLO: Segmenting Objects by Locations](https://arxiv.org/abs/1912.04488) - - - -## Abstract - -We present a new, embarrassingly simple approach to instance segmentation in images. Compared to many other dense prediction tasks, e.g., semantic segmentation, it is the arbitrary number of instances that have made instance segmentation much more challenging. In order to predict a mask for each instance, mainstream approaches either follow the 'detect-thensegment' strategy as used by Mask R-CNN, or predict category masks first then use clustering techniques to group pixels into individual instances. We view the task of instance segmentation from a completely new perspective by introducing the notion of "instance categories", which assigns categories to each pixel within an instance according to the instance's location and size, thus nicely converting instance mask segmentation into a classification-solvable problem. Now instance segmentation is decomposed into two classification tasks. We demonstrate a much simpler and flexible instance segmentation framework with strong performance, achieving on par accuracy with Mask R-CNN and outperforming recent singleshot instance segmenters in accuracy. We hope that this very simple and strong framework can serve as a baseline for many instance-level recognition tasks besides instance segmentation. - -
- -
- -## Results and Models - -### SOLO - -| Backbone | Style | MS train | Lr schd | Mem (GB) | Inf time (fps) | mask AP | Download | -|:---------:|:-------:|:--------:|:-------:|:--------:|:--------------:|:------:|:--------:| -| R-50 | pytorch | N | 1x | 8.0 | 14.0 | 33.1 | [model](https://download.openmmlab.com/mmdetection/v2.0/solo/solo_r50_fpn_1x_coco/solo_r50_fpn_1x_coco_20210821_035055-2290a6b8.pth) | [log](https://download.openmmlab.com/mmdetection/v2.0/solo/solo_r50_fpn_1x_coco/solo_r50_fpn_1x_coco_20210821_035055.log.json) | -| R-50 | pytorch | Y | 3x | 7.4 | 14.0 | 35.9 | [model](https://download.openmmlab.com/mmdetection/v2.0/solo/solo_r50_fpn_3x_coco/solo_r50_fpn_3x_coco_20210901_012353-11d224d7.pth) | [log](https://download.openmmlab.com/mmdetection/v2.0/solo/solo_r50_fpn_3x_coco/solo_r50_fpn_3x_coco_20210901_012353.log.json) | - -### Decoupled SOLO - -| Backbone | Style | MS train | Lr schd | Mem (GB) | Inf time (fps) | mask AP | Download | -|:---------:|:-------:|:--------:|:-------:|:--------:|:--------------:|:-------:|:--------:| -| R-50 | pytorch | N | 1x | 7.8 | 12.5 | 33.9 | [model](https://download.openmmlab.com/mmdetection/v2.0/solo/decoupled_solo_r50_fpn_1x_coco/decoupled_solo_r50_fpn_1x_coco_20210820_233348-6337c589.pth) | [log](https://download.openmmlab.com/mmdetection/v2.0/solo/decoupled_solo_r50_fpn_1x_coco/decoupled_solo_r50_fpn_1x_coco_20210820_233348.log.json) | -| R-50 | pytorch | Y | 3x | 7.9 | 12.5 | 36.7 | [model](https://download.openmmlab.com/mmdetection/v2.0/solo/decoupled_solo_r50_fpn_3x_coco/decoupled_solo_r50_fpn_3x_coco_20210821_042504-7b3301ec.pth) | [log](https://download.openmmlab.com/mmdetection/v2.0/solo/decoupled_solo_r50_fpn_3x_coco/decoupled_solo_r50_fpn_3x_coco_20210821_042504.log.json) | - -- Decoupled SOLO has a decoupled head which is different from SOLO head. -Decoupled SOLO serves as an efficient and equivalent variant in accuracy -of SOLO. Please refer to the corresponding config files for details. - -### Decoupled Light SOLO - -| Backbone | Style | MS train | Lr schd | Mem (GB) | Inf time (fps) | mask AP | Download | -|:---------:|:-------:|:--------:|:-------:|:--------:|:--------------:|:------:|:--------:| -| R-50 | pytorch | Y | 3x | 2.2 | 31.2 | 32.9 | [model](https://download.openmmlab.com/mmdetection/v2.0/solo/decoupled_solo_light_r50_fpn_3x_coco/decoupled_solo_light_r50_fpn_3x_coco_20210906_142703-e70e226f.pth) | [log](https://download.openmmlab.com/mmdetection/v2.0/solo/decoupled_solo_light_r50_fpn_3x_coco/decoupled_solo_light_r50_fpn_3x_coco_20210906_142703.log.json) | - -- Decoupled Light SOLO using decoupled structure similar to Decoupled -SOLO head, with light-weight head and smaller input size, Please refer -to the corresponding config files for details. - -## Citation - -```latex -@inproceedings{wang2020solo, - title = {{SOLO}: Segmenting Objects by Locations}, - author = {Wang, Xinlong and Kong, Tao and Shen, Chunhua and Jiang, Yuning and Li, Lei}, - booktitle = {Proc. Eur. Conf. Computer Vision (ECCV)}, - year = {2020} -} -``` diff --git a/cv/instance_segmentation/solo/pytorch/configs/solo/decoupled_solo_light_r50_fpn_3x_coco.py b/cv/instance_segmentation/solo/pytorch/configs/solo/decoupled_solo_light_r50_fpn_3x_coco.py deleted file mode 100644 index 101f8f1d3..000000000 --- a/cv/instance_segmentation/solo/pytorch/configs/solo/decoupled_solo_light_r50_fpn_3x_coco.py +++ /dev/null @@ -1,63 +0,0 @@ -_base_ = './decoupled_solo_r50_fpn_3x_coco.py' - -# model settings -model = dict( - mask_head=dict( - type='DecoupledSOLOLightHead', - num_classes=80, - in_channels=256, - stacked_convs=4, - feat_channels=256, - strides=[8, 8, 16, 32, 32], - scale_ranges=((1, 64), (32, 128), (64, 256), (128, 512), (256, 2048)), - pos_scale=0.2, - num_grids=[40, 36, 24, 16, 12], - cls_down_index=0, - loss_mask=dict( - type='DiceLoss', use_sigmoid=True, activate=False, - loss_weight=3.0), - loss_cls=dict( - type='FocalLoss', - use_sigmoid=True, - gamma=2.0, - alpha=0.25, - loss_weight=1.0), - norm_cfg=dict(type='GN', num_groups=32, requires_grad=True))) - -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', with_bbox=True, with_mask=True), - dict( - type='Resize', - img_scale=[(852, 512), (852, 480), (852, 448), (852, 416), (852, 384), - (852, 352)], - multiscale_mode='value', - keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(852, 512), - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] - -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/instance_segmentation/solo/pytorch/configs/solo/decoupled_solo_r50_fpn_1x_coco.py b/cv/instance_segmentation/solo/pytorch/configs/solo/decoupled_solo_r50_fpn_1x_coco.py deleted file mode 100644 index b611cdf4d..000000000 --- a/cv/instance_segmentation/solo/pytorch/configs/solo/decoupled_solo_r50_fpn_1x_coco.py +++ /dev/null @@ -1,28 +0,0 @@ -_base_ = [ - './solo_r50_fpn_1x_coco.py', -] -# model settings -model = dict( - mask_head=dict( - type='DecoupledSOLOHead', - num_classes=80, - in_channels=256, - stacked_convs=7, - feat_channels=256, - strides=[8, 8, 16, 32, 32], - scale_ranges=((1, 96), (48, 192), (96, 384), (192, 768), (384, 2048)), - pos_scale=0.2, - num_grids=[40, 36, 24, 16, 12], - cls_down_index=0, - loss_mask=dict( - type='DiceLoss', use_sigmoid=True, activate=False, - loss_weight=3.0), - loss_cls=dict( - type='FocalLoss', - use_sigmoid=True, - gamma=2.0, - alpha=0.25, - loss_weight=1.0), - norm_cfg=dict(type='GN', num_groups=32, requires_grad=True))) - -optimizer = dict(type='SGD', lr=0.01) diff --git a/cv/instance_segmentation/solo/pytorch/configs/solo/decoupled_solo_r50_fpn_3x_coco.py b/cv/instance_segmentation/solo/pytorch/configs/solo/decoupled_solo_r50_fpn_3x_coco.py deleted file mode 100644 index 4a8c19dec..000000000 --- a/cv/instance_segmentation/solo/pytorch/configs/solo/decoupled_solo_r50_fpn_3x_coco.py +++ /dev/null @@ -1,25 +0,0 @@ -_base_ = './solo_r50_fpn_3x_coco.py' - -# model settings -model = dict( - mask_head=dict( - type='DecoupledSOLOHead', - num_classes=80, - in_channels=256, - stacked_convs=7, - feat_channels=256, - strides=[8, 8, 16, 32, 32], - scale_ranges=((1, 96), (48, 192), (96, 384), (192, 768), (384, 2048)), - pos_scale=0.2, - num_grids=[40, 36, 24, 16, 12], - cls_down_index=0, - loss_mask=dict( - type='DiceLoss', use_sigmoid=True, activate=False, - loss_weight=3.0), - loss_cls=dict( - type='FocalLoss', - use_sigmoid=True, - gamma=2.0, - alpha=0.25, - loss_weight=1.0), - norm_cfg=dict(type='GN', num_groups=32, requires_grad=True))) diff --git a/cv/instance_segmentation/solo/pytorch/configs/solo/metafile.yml b/cv/instance_segmentation/solo/pytorch/configs/solo/metafile.yml deleted file mode 100644 index b6244e80f..000000000 --- a/cv/instance_segmentation/solo/pytorch/configs/solo/metafile.yml +++ /dev/null @@ -1,115 +0,0 @@ -Collections: - - Name: SOLO - Metadata: - Training Data: COCO - Training Techniques: - - SGD with Momentum - - Weight Decay - Training Resources: 8x V100 GPUs - Architecture: - - FPN - - Convolution - - ResNet - Paper: https://arxiv.org/abs/1912.04488 - README: configs/solo/README.md - -Models: - - Name: decoupled_solo_r50_fpn_1x_coco - In Collection: SOLO - Config: configs/solo/decoupled_solo_r50_fpn_1x_coco.py - Metadata: - Training Memory (GB): 7.8 - Epochs: 12 - inference time (ms/im): - - value: 116.4 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (1333, 800) - Results: - - Task: Instance Segmentation - Dataset: COCO - Metrics: - mask AP: 33.9 - Weights: https://download.openmmlab.com/mmdetection/v2.0/solo/decoupled_solo_r50_fpn_1x_coco/decoupled_solo_r50_fpn_1x_coco_20210820_233348-6337c589.pth - - - Name: decoupled_solo_r50_fpn_3x_coco - In Collection: SOLO - Config: configs/solo/decoupled_solo_r50_fpn_3x_coco.py - Metadata: - Training Memory (GB): 7.9 - Epochs: 36 - inference time (ms/im): - - value: 117.2 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (1333, 800) - Results: - - Task: Instance Segmentation - Dataset: COCO - Metrics: - mask AP: 36.7 - Weights: https://download.openmmlab.com/mmdetection/v2.0/solo/decoupled_solo_r50_fpn_3x_coco/decoupled_solo_r50_fpn_3x_coco_20210821_042504-7b3301ec.pth - - - Name: decoupled_solo_light_r50_fpn_3x_coco - In Collection: SOLO - Config: configs/solo/decoupled_solo_light_r50_fpn_3x_coco.py - Metadata: - Training Memory (GB): 2.2 - Epochs: 36 - inference time (ms/im): - - value: 35.0 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (852, 512) - Results: - - Task: Instance Segmentation - Dataset: COCO - Metrics: - mask AP: 32.9 - Weights: https://download.openmmlab.com/mmdetection/v2.0/solo/decoupled_solo_light_r50_fpn_3x_coco/decoupled_solo_light_r50_fpn_3x_coco_20210906_142703-e70e226f.pth - - - Name: solo_r50_fpn_3x_coco - In Collection: SOLO - Config: configs/solo/solo_r50_fpn_3x_coco.py - Metadata: - Training Memory (GB): 7.4 - Epochs: 36 - inference time (ms/im): - - value: 94.2 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (1333, 800) - Results: - - Task: Instance Segmentation - Dataset: COCO - Metrics: - mask AP: 35.9 - Weights: https://download.openmmlab.com/mmdetection/v2.0/solo/solo_r50_fpn_3x_coco/solo_r50_fpn_3x_coco_20210901_012353-11d224d7.pth - - - Name: solo_r50_fpn_1x_coco - In Collection: SOLO - Config: configs/solo/solo_r50_fpn_1x_coco.py - Metadata: - Training Memory (GB): 8.0 - Epochs: 12 - inference time (ms/im): - - value: 95.1 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (1333, 800) - Results: - - Task: Instance Segmentation - Dataset: COCO - Metrics: - mask AP: 33.1 - Weights: https://download.openmmlab.com/mmdetection/v2.0/solo/solo_r50_fpn_1x_coco/solo_r50_fpn_1x_coco_20210821_035055-2290a6b8.pth diff --git a/cv/instance_segmentation/solo/pytorch/configs/solo/solo_r50_fpn_1x_coco.py b/cv/instance_segmentation/solo/pytorch/configs/solo/solo_r50_fpn_1x_coco.py deleted file mode 100644 index 9093a5048..000000000 --- a/cv/instance_segmentation/solo/pytorch/configs/solo/solo_r50_fpn_1x_coco.py +++ /dev/null @@ -1,53 +0,0 @@ -_base_ = [ - '../_base_/datasets/coco_instance.py', - '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py' -] - -# model settings -model = dict( - type='SOLO', - backbone=dict( - type='ResNet', - depth=50, - num_stages=4, - out_indices=(0, 1, 2, 3), - frozen_stages=1, - init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50'), - style='pytorch'), - neck=dict( - type='FPN', - in_channels=[256, 512, 1024, 2048], - out_channels=256, - start_level=0, - num_outs=5), - mask_head=dict( - type='SOLOHead', - num_classes=80, - in_channels=256, - stacked_convs=7, - feat_channels=256, - strides=[8, 8, 16, 32, 32], - scale_ranges=((1, 96), (48, 192), (96, 384), (192, 768), (384, 2048)), - pos_scale=0.2, - num_grids=[40, 36, 24, 16, 12], - cls_down_index=0, - loss_mask=dict(type='DiceLoss', use_sigmoid=True, loss_weight=3.0), - loss_cls=dict( - type='FocalLoss', - use_sigmoid=True, - gamma=2.0, - alpha=0.25, - loss_weight=1.0), - norm_cfg=dict(type='GN', num_groups=32, requires_grad=True)), - # model training and testing settings - test_cfg=dict( - nms_pre=500, - score_thr=0.1, - mask_thr=0.5, - filter_thr=0.05, - kernel='gaussian', # gaussian/linear - sigma=2.0, - max_per_img=100)) - -# optimizer -optimizer = dict(type='SGD', lr=0.01) diff --git a/cv/instance_segmentation/solo/pytorch/configs/solo/solo_r50_fpn_3x_coco.py b/cv/instance_segmentation/solo/pytorch/configs/solo/solo_r50_fpn_3x_coco.py deleted file mode 100644 index 52302cdf9..000000000 --- a/cv/instance_segmentation/solo/pytorch/configs/solo/solo_r50_fpn_3x_coco.py +++ /dev/null @@ -1,28 +0,0 @@ -_base_ = './solo_r50_fpn_1x_coco.py' - -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', with_bbox=True, with_mask=True), - dict( - type='Resize', - img_scale=[(1333, 800), (1333, 768), (1333, 736), (1333, 704), - (1333, 672), (1333, 640)], - multiscale_mode='value', - keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']), -] -data = dict(train=dict(pipeline=train_pipeline)) - -lr_config = dict( - policy='step', - warmup='linear', - warmup_iters=500, - warmup_ratio=1.0 / 3, - step=[27, 33]) -runner = dict(type='EpochBasedRunner', max_epochs=36) diff --git a/cv/instance_segmentation/solo/pytorch/docker/Dockerfile b/cv/instance_segmentation/solo/pytorch/docker/Dockerfile deleted file mode 100644 index 5ee7a370e..000000000 --- a/cv/instance_segmentation/solo/pytorch/docker/Dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -ARG PYTORCH="1.6.0" -ARG CUDA="10.1" -ARG CUDNN="7" - -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ENV TORCH_CUDA_ARCH_LIST="6.0 6.1 7.0+PTX" -ENV TORCH_NVCC_FLAGS="-Xfatbin -compress-all" -ENV CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" - -RUN apt-get update && apt-get install -y ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Install MMCV -RUN pip install --no-cache-dir --upgrade pip wheel setuptools -RUN pip install --no-cache-dir mmcv-full==1.3.17 -f https://download.openmmlab.com/mmcv/dist/cu101/torch1.6.0/index.html - -# Install MMDetection -RUN conda clean --all -RUN git clone https://github.com/open-mmlab/mmdetection.git /mmdetection -WORKDIR /mmdetection -ENV FORCE_CUDA="1" -RUN pip install --no-cache-dir -r requirements/build.txt -RUN pip install --no-cache-dir -e . diff --git a/cv/instance_segmentation/solo/pytorch/docker/serve/Dockerfile b/cv/instance_segmentation/solo/pytorch/docker/serve/Dockerfile deleted file mode 100644 index 37d88f6e7..000000000 --- a/cv/instance_segmentation/solo/pytorch/docker/serve/Dockerfile +++ /dev/null @@ -1,49 +0,0 @@ -ARG PYTORCH="1.6.0" -ARG CUDA="10.1" -ARG CUDNN="7" -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ARG MMCV="1.3.17" -ARG MMDET="2.25.0" - -ENV PYTHONUNBUFFERED TRUE - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ - ca-certificates \ - g++ \ - openjdk-11-jre-headless \ - # MMDet Requirements - ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && rm -rf /var/lib/apt/lists/* - -ENV PATH="/opt/conda/bin:$PATH" -RUN export FORCE_CUDA=1 - -# TORCHSEVER -RUN pip install torchserve torch-model-archiver - -# MMLAB -ARG PYTORCH -ARG CUDA -RUN ["/bin/bash", "-c", "pip install mmcv-full==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] -RUN pip install mmdet==${MMDET} - -RUN useradd -m model-server \ - && mkdir -p /home/model-server/tmp - -COPY entrypoint.sh /usr/local/bin/entrypoint.sh - -RUN chmod +x /usr/local/bin/entrypoint.sh \ - && chown -R model-server /home/model-server - -COPY config.properties /home/model-server/config.properties -RUN mkdir /home/model-server/model-store && chown -R model-server /home/model-server/model-store - -EXPOSE 8080 8081 8082 - -USER model-server -WORKDIR /home/model-server -ENV TEMP=/home/model-server/tmp -ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] -CMD ["serve"] diff --git a/cv/instance_segmentation/solo/pytorch/docker/serve/config.properties b/cv/instance_segmentation/solo/pytorch/docker/serve/config.properties deleted file mode 100644 index efb9c47e4..000000000 --- a/cv/instance_segmentation/solo/pytorch/docker/serve/config.properties +++ /dev/null @@ -1,5 +0,0 @@ -inference_address=http://0.0.0.0:8080 -management_address=http://0.0.0.0:8081 -metrics_address=http://0.0.0.0:8082 -model_store=/home/model-server/model-store -load_models=all diff --git a/cv/instance_segmentation/solo/pytorch/docker/serve/entrypoint.sh b/cv/instance_segmentation/solo/pytorch/docker/serve/entrypoint.sh deleted file mode 100644 index 41ba00b04..000000000 --- a/cv/instance_segmentation/solo/pytorch/docker/serve/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - -if [[ "$1" = "serve" ]]; then - shift 1 - torchserve --start --ts-config /home/model-server/config.properties -else - eval "$@" -fi - -# prevent docker exit -tail -f /dev/null diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/__init__.py deleted file mode 100644 index 87c01b07c..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -from .fileio import * -from .image import * -from .utils import * -from .version import * -# The following modules are not imported to this level, so mmcv may be used -# without PyTorch. -# - runner -# - parallel -# - op diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/__init__.py deleted file mode 100644 index 7246c8974..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .alexnet import AlexNet -# yapf: disable -from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, - ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, - ConvTranspose2d, ConvTranspose3d, ConvWS2d, - DepthwiseSeparableConvModule, GeneralizedAttention, - HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, - NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, - build_activation_layer, build_conv_layer, - build_norm_layer, build_padding_layer, build_plugin_layer, - build_upsample_layer, conv_ws_2d, is_norm) -from .builder import MODELS, build_model_from_cfg -# yapf: enable -from .resnet import ResNet, make_res_layer -from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, - NormalInit, PretrainedInit, TruncNormalInit, UniformInit, - XavierInit, bias_init_with_prob, caffe2_xavier_init, - constant_init, fuse_conv_bn, get_model_complexity_info, - initialize, kaiming_init, normal_init, trunc_normal_init, - uniform_init, xavier_init) -from .vgg import VGG, make_vgg_layer - -__all__ = [ - 'AlexNet', 'VGG', 'make_vgg_layer', 'ResNet', 'make_res_layer', - 'constant_init', 'xavier_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'kaiming_init', 'caffe2_xavier_init', - 'bias_init_with_prob', 'ConvModule', 'build_activation_layer', - 'build_conv_layer', 'build_norm_layer', 'build_padding_layer', - 'build_upsample_layer', 'build_plugin_layer', 'is_norm', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'HSigmoid', 'Swish', 'HSwish', - 'GeneralizedAttention', 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', - 'PADDING_LAYERS', 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', - 'get_model_complexity_info', 'conv_ws_2d', 'ConvAWS2d', 'ConvWS2d', - 'fuse_conv_bn', 'DepthwiseSeparableConvModule', 'Linear', 'Conv2d', - 'ConvTranspose2d', 'MaxPool2d', 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', - 'initialize', 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'MODELS', 'build_model_from_cfg' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/alexnet.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/alexnet.py deleted file mode 100644 index 89e36b8c7..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/alexnet.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn - - -class AlexNet(nn.Module): - """AlexNet backbone. - - Args: - num_classes (int): number of classes for classification. - """ - - def __init__(self, num_classes=-1): - super(AlexNet, self).__init__() - self.num_classes = num_classes - self.features = nn.Sequential( - nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - nn.Conv2d(64, 192, kernel_size=5, padding=2), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - nn.Conv2d(192, 384, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.Conv2d(384, 256, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.Conv2d(256, 256, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - ) - if self.num_classes > 0: - self.classifier = nn.Sequential( - nn.Dropout(), - nn.Linear(256 * 6 * 6, 4096), - nn.ReLU(inplace=True), - nn.Dropout(), - nn.Linear(4096, 4096), - nn.ReLU(inplace=True), - nn.Linear(4096, num_classes), - ) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - # use default initializer - pass - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - - x = self.features(x) - if self.num_classes > 0: - x = x.view(x.size(0), 256 * 6 * 6) - x = self.classifier(x) - - return x diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/__init__.py deleted file mode 100644 index 0f33124ed..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .activation import build_activation_layer -from .context_block import ContextBlock -from .conv import build_conv_layer -from .conv2d_adaptive_padding import Conv2dAdaptivePadding -from .conv_module import ConvModule -from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d -from .depthwise_separable_conv_module import DepthwiseSeparableConvModule -from .drop import Dropout, DropPath -from .generalized_attention import GeneralizedAttention -from .hsigmoid import HSigmoid -from .hswish import HSwish -from .non_local import NonLocal1d, NonLocal2d, NonLocal3d -from .norm import build_norm_layer, is_norm -from .padding import build_padding_layer -from .plugin import build_plugin_layer -from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) -from .scale import Scale -from .swish import Swish -from .upsample import build_upsample_layer -from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, - Linear, MaxPool2d, MaxPool3d) - -__all__ = [ - 'ConvModule', 'build_activation_layer', 'build_conv_layer', - 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', - 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', - 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', - 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', - 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', - 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', - 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/activation.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/activation.py deleted file mode 100644 index 79f198838..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/activation.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version -from .registry import ACTIVATION_LAYERS - -for module in [ - nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, - nn.Sigmoid, nn.Tanh -]: - ACTIVATION_LAYERS.register_module(module=module) - - -@ACTIVATION_LAYERS.register_module(name='Clip') -@ACTIVATION_LAYERS.register_module() -class Clamp(nn.Module): - """Clamp activation layer. - - This activation function is to clamp the feature map value within - :math:`[min, max]`. More details can be found in ``torch.clamp()``. - - Args: - min (Number | optional): Lower-bound of the range to be clamped to. - Default to -1. - max (Number | optional): Upper-bound of the range to be clamped to. - Default to 1. - """ - - def __init__(self, min=-1., max=1.): - super(Clamp, self).__init__() - self.min = min - self.max = max - - def forward(self, x): - """Forward function. - - Args: - x (torch.Tensor): The input tensor. - - Returns: - torch.Tensor: Clamped tensor. - """ - return torch.clamp(x, min=self.min, max=self.max) - - -class GELU(nn.Module): - r"""Applies the Gaussian Error Linear Units function: - - .. math:: - \text{GELU}(x) = x * \Phi(x) - where :math:`\Phi(x)` is the Cumulative Distribution Function for - Gaussian Distribution. - - Shape: - - Input: :math:`(N, *)` where `*` means, any number of additional - dimensions - - Output: :math:`(N, *)`, same shape as the input - - .. image:: scripts/activation_images/GELU.png - - Examples:: - - >>> m = nn.GELU() - >>> input = torch.randn(2) - >>> output = m(input) - """ - - def forward(self, input): - return F.gelu(input) - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.4')): - ACTIVATION_LAYERS.register_module(module=GELU) -else: - ACTIVATION_LAYERS.register_module(module=nn.GELU) - - -def build_activation_layer(cfg): - """Build activation layer. - - Args: - cfg (dict): The activation layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an activation layer. - - Returns: - nn.Module: Created activation layer. - """ - return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/context_block.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/context_block.py deleted file mode 100644 index d60fdb904..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/context_block.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn - -from ..utils import constant_init, kaiming_init -from .registry import PLUGIN_LAYERS - - -def last_zero_init(m): - if isinstance(m, nn.Sequential): - constant_init(m[-1], val=0) - else: - constant_init(m, val=0) - - -@PLUGIN_LAYERS.register_module() -class ContextBlock(nn.Module): - """ContextBlock module in GCNet. - - See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' - (https://arxiv.org/abs/1904.11492) for details. - - Args: - in_channels (int): Channels of the input feature map. - ratio (float): Ratio of channels of transform bottleneck - pooling_type (str): Pooling method for context modeling. - Options are 'att' and 'avg', stand for attention pooling and - average pooling respectively. Default: 'att'. - fusion_types (Sequence[str]): Fusion method for feature fusion, - Options are 'channels_add', 'channel_mul', stand for channelwise - addition and multiplication respectively. Default: ('channel_add',) - """ - - _abbr_ = 'context_block' - - def __init__(self, - in_channels, - ratio, - pooling_type='att', - fusion_types=('channel_add', )): - super(ContextBlock, self).__init__() - assert pooling_type in ['avg', 'att'] - assert isinstance(fusion_types, (list, tuple)) - valid_fusion_types = ['channel_add', 'channel_mul'] - assert all([f in valid_fusion_types for f in fusion_types]) - assert len(fusion_types) > 0, 'at least one fusion should be used' - self.in_channels = in_channels - self.ratio = ratio - self.planes = int(in_channels * ratio) - self.pooling_type = pooling_type - self.fusion_types = fusion_types - if pooling_type == 'att': - self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) - self.softmax = nn.Softmax(dim=2) - else: - self.avg_pool = nn.AdaptiveAvgPool2d(1) - if 'channel_add' in fusion_types: - self.channel_add_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_add_conv = None - if 'channel_mul' in fusion_types: - self.channel_mul_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_mul_conv = None - self.reset_parameters() - - def reset_parameters(self): - if self.pooling_type == 'att': - kaiming_init(self.conv_mask, mode='fan_in') - self.conv_mask.inited = True - - if self.channel_add_conv is not None: - last_zero_init(self.channel_add_conv) - if self.channel_mul_conv is not None: - last_zero_init(self.channel_mul_conv) - - def spatial_pool(self, x): - batch, channel, height, width = x.size() - if self.pooling_type == 'att': - input_x = x - # [N, C, H * W] - input_x = input_x.view(batch, channel, height * width) - # [N, 1, C, H * W] - input_x = input_x.unsqueeze(1) - # [N, 1, H, W] - context_mask = self.conv_mask(x) - # [N, 1, H * W] - context_mask = context_mask.view(batch, 1, height * width) - # [N, 1, H * W] - context_mask = self.softmax(context_mask) - # [N, 1, H * W, 1] - context_mask = context_mask.unsqueeze(-1) - # [N, 1, C, 1] - context = torch.matmul(input_x, context_mask) - # [N, C, 1, 1] - context = context.view(batch, channel, 1, 1) - else: - # [N, C, 1, 1] - context = self.avg_pool(x) - - return context - - def forward(self, x): - # [N, C, 1, 1] - context = self.spatial_pool(x) - - out = x - if self.channel_mul_conv is not None: - # [N, C, 1, 1] - channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) - out = out * channel_mul_term - if self.channel_add_conv is not None: - # [N, C, 1, 1] - channel_add_term = self.channel_add_conv(context) - out = out + channel_add_term - - return out diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv.py deleted file mode 100644 index cf5449199..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from .registry import CONV_LAYERS - -CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) -CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) -CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) -CONV_LAYERS.register_module('Conv', module=nn.Conv2d) - - -def build_conv_layer(cfg, *args, **kwargs): - """Build convolution layer. - - Args: - cfg (None or dict): The conv layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an conv layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding conv layer. - - Returns: - nn.Module: Created conv layer. - """ - if cfg is None: - cfg_ = dict(type='Conv2d') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in CONV_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - else: - conv_layer = CONV_LAYERS.get(layer_type) - - layer = conv_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py deleted file mode 100644 index b45e758ac..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from torch import nn -from torch.nn import functional as F - -from .registry import CONV_LAYERS - - -@CONV_LAYERS.register_module() -class Conv2dAdaptivePadding(nn.Conv2d): - """Implementation of 2D convolution in tensorflow with `padding` as "same", - which applies padding to input (if needed) so that input image gets fully - covered by filter and stride you specified. For stride 1, this will ensure - that output image size is same as input. For stride of 2, output dimensions - will be half, for example. - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the convolving kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If ``True``, adds a learnable bias to the - output. Default: ``True`` - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__(in_channels, out_channels, kernel_size, stride, 0, - dilation, groups, bias) - - def forward(self, x): - img_h, img_w = x.size()[-2:] - kernel_h, kernel_w = self.weight.size()[-2:] - stride_h, stride_w = self.stride - output_h = math.ceil(img_h / stride_h) - output_w = math.ceil(img_w / stride_w) - pad_h = ( - max((output_h - 1) * self.stride[0] + - (kernel_h - 1) * self.dilation[0] + 1 - img_h, 0)) - pad_w = ( - max((output_w - 1) * self.stride[1] + - (kernel_w - 1) * self.dilation[1] + 1 - img_w, 0)) - if pad_h > 0 or pad_w > 0: - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2 - ]) - return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv_module.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv_module.py deleted file mode 100644 index 4f19f1d0c..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv_module.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from mmcv.utils import _BatchNorm, _InstanceNorm -from ..utils import constant_init, kaiming_init -from .activation import build_activation_layer -from .conv import build_conv_layer -from .norm import build_norm_layer -from .padding import build_padding_layer -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class ConvModule(nn.Module): - """A conv block that bundles conv/norm/activation layers. - - This block simplifies the usage of convolution layers, which are commonly - used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). - It is based upon three build methods: `build_conv_layer()`, - `build_norm_layer()` and `build_activation_layer()`. - - Besides, we add some additional features in this module. - 1. Automatically set `bias` of the conv layer. - 2. Spectral norm is supported. - 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only - supports zero and circular padding, and we add "reflect" padding mode. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. - groups (int): Number of blocked connections from input channels to - output channels. Same as that in ``nn._ConvNd``. - bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - inplace (bool): Whether to use inplace mode for activation. - Default: True. - with_spectral_norm (bool): Whether use spectral norm in conv module. - Default: False. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in PyTorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Common examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - Default: ('conv', 'norm', 'act'). - """ - - _abbr_ = 'conv_block' - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias='auto', - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - padding_mode='zeros', - order=('conv', 'norm', 'act')): - super(ConvModule, self).__init__() - assert conv_cfg is None or isinstance(conv_cfg, dict) - assert norm_cfg is None or isinstance(norm_cfg, dict) - assert act_cfg is None or isinstance(act_cfg, dict) - official_padding_mode = ['zeros', 'circular'] - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.with_explicit_padding = padding_mode not in official_padding_mode - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 3 - assert set(order) == set(['conv', 'norm', 'act']) - - self.with_norm = norm_cfg is not None - self.with_activation = act_cfg is not None - # if the conv layer is before a norm layer, bias is unnecessary. - if bias == 'auto': - bias = not self.with_norm - self.with_bias = bias - - if self.with_explicit_padding: - pad_cfg = dict(type=padding_mode) - self.padding_layer = build_padding_layer(pad_cfg, padding) - - # reset padding to 0 for conv module - conv_padding = 0 if self.with_explicit_padding else padding - # build convolution layer - self.conv = build_conv_layer( - conv_cfg, - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=conv_padding, - dilation=dilation, - groups=groups, - bias=bias) - # export the attributes of self.conv to a higher level for convenience - self.in_channels = self.conv.in_channels - self.out_channels = self.conv.out_channels - self.kernel_size = self.conv.kernel_size - self.stride = self.conv.stride - self.padding = padding - self.dilation = self.conv.dilation - self.transposed = self.conv.transposed - self.output_padding = self.conv.output_padding - self.groups = self.conv.groups - - if self.with_spectral_norm: - self.conv = nn.utils.spectral_norm(self.conv) - - # build normalization layers - if self.with_norm: - # norm layer is after conv layer - if order.index('norm') > order.index('conv'): - norm_channels = out_channels - else: - norm_channels = in_channels - self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) - self.add_module(self.norm_name, norm) - if self.with_bias: - if isinstance(norm, (_BatchNorm, _InstanceNorm)): - warnings.warn( - 'Unnecessary conv bias before batch/instance norm') - else: - self.norm_name = None - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - # nn.Tanh has no 'inplace' argument - if act_cfg_['type'] not in [ - 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish' - ]: - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - @property - def norm(self): - if self.norm_name: - return getattr(self, self.norm_name) - else: - return None - - def init_weights(self): - # 1. It is mainly for customized conv layers with their own - # initialization manners by calling their own ``init_weights()``, - # and we do not want ConvModule to override the initialization. - # 2. For customized conv layers without their own initialization - # manners (that is, they don't have their own ``init_weights()``) - # and PyTorch's conv layers, they will be initialized by - # this method with default ``kaiming_init``. - # Note: For PyTorch's conv layers, they will be overwritten by our - # initialization implementation using default ``kaiming_init``. - if not hasattr(self.conv, 'init_weights'): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) - if self.with_norm: - constant_init(self.norm, 1, bias=0) - - def forward(self, x, activate=True, norm=True): - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - x = self.conv(x) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv_ws.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv_ws.py deleted file mode 100644 index a3941e278..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/conv_ws.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from .registry import CONV_LAYERS - - -def conv_ws_2d(input, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - eps=1e-5): - c_in = weight.size(0) - weight_flat = weight.view(c_in, -1) - mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1) - std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1) - weight = (weight - mean) / (std + eps) - return F.conv2d(input, weight, bias, stride, padding, dilation, groups) - - -@CONV_LAYERS.register_module('ConvWS') -class ConvWS2d(nn.Conv2d): - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True, - eps=1e-5): - super(ConvWS2d, self).__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.eps = eps - - def forward(self, x): - return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups, self.eps) - - -@CONV_LAYERS.register_module(name='ConvAWS') -class ConvAWS2d(nn.Conv2d): - """AWS (Adaptive Weight Standardization) - - This is a variant of Weight Standardization - (https://arxiv.org/pdf/1903.10520.pdf) - It is used in DetectoRS to avoid NaN - (https://arxiv.org/pdf/2006.02334.pdf) - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the conv kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If set True, adds a learnable bias to the - output. Default: True - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.register_buffer('weight_gamma', - torch.ones(self.out_channels, 1, 1, 1)) - self.register_buffer('weight_beta', - torch.zeros(self.out_channels, 1, 1, 1)) - - def _get_weight(self, weight): - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - weight = (weight - mean) / std - weight = self.weight_gamma * weight + self.weight_beta - return weight - - def forward(self, x): - weight = self._get_weight(self.weight) - return F.conv2d(x, weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - """Override default load function. - - AWS overrides the function _load_from_state_dict to recover - weight_gamma and weight_beta if they are missing. If weight_gamma and - weight_beta are found in the checkpoint, this function will return - after super()._load_from_state_dict. Otherwise, it will compute the - mean and std of the pretrained weights and store them in weight_beta - and weight_gamma. - """ - - self.weight_gamma.data.fill_(-1) - local_missing_keys = [] - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, local_missing_keys, - unexpected_keys, error_msgs) - if self.weight_gamma.data.mean() > 0: - for k in local_missing_keys: - missing_keys.append(k) - return - weight = self.weight.data - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - self.weight_beta.data.copy_(mean) - self.weight_gamma.data.copy_(std) - missing_gamma_beta = [ - k for k in local_missing_keys - if k.endswith('weight_gamma') or k.endswith('weight_beta') - ] - for k in missing_gamma_beta: - local_missing_keys.remove(k) - for k in local_missing_keys: - missing_keys.append(k) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py deleted file mode 100644 index 722d5d8d7..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .conv_module import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if `norm_cfg` and `act_cfg` are specified. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. Default: 1. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. Default: 0. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super(DepthwiseSeparableConvModule, self).__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/drop.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/drop.py deleted file mode 100644 index b0a026654..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/drop.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from mmcv import build_from_cfg -from .registry import DROPOUT_LAYERS - - -def drop_path(x, drop_prob=0., training=False): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - """ - if drop_prob == 0. or not training: - return x - keep_prob = 1 - drop_prob - # handle tensors with different dimensions, not just 4D tensors. - shape = (x.shape[0], ) + (1, ) * (x.ndim - 1) - random_tensor = keep_prob + torch.rand( - shape, dtype=x.dtype, device=x.device) - output = x.div(keep_prob) * random_tensor.floor() - return output - - -@DROPOUT_LAYERS.register_module() -class DropPath(nn.Module): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - - Args: - drop_prob (float): Probability of the path to be zeroed. Default: 0.1 - """ - - def __init__(self, drop_prob=0.1): - super(DropPath, self).__init__() - self.drop_prob = drop_prob - - def forward(self, x): - return drop_path(x, self.drop_prob, self.training) - - -@DROPOUT_LAYERS.register_module() -class Dropout(nn.Dropout): - """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of - ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with - ``DropPath`` - - Args: - drop_prob (float): Probability of the elements to be - zeroed. Default: 0.5. - inplace (bool): Do the operation inplace or not. Default: False. - """ - - def __init__(self, drop_prob=0.5, inplace=False): - super().__init__(p=drop_prob, inplace=inplace) - - -def build_dropout(cfg, default_args=None): - """Builder for drop out layers.""" - return build_from_cfg(cfg, DROPOUT_LAYERS, default_args) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/generalized_attention.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/generalized_attention.py deleted file mode 100644 index 988d9adf2..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/generalized_attention.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import kaiming_init -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class GeneralizedAttention(nn.Module): - """GeneralizedAttention module. - - See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' - (https://arxiv.org/abs/1711.07971) for details. - - Args: - in_channels (int): Channels of the input feature map. - spatial_range (int): The spatial range. -1 indicates no spatial range - constraint. Default: -1. - num_heads (int): The head number of empirical_attention module. - Default: 9. - position_embedding_dim (int): The position embedding dimension. - Default: -1. - position_magnitude (int): A multiplier acting on coord difference. - Default: 1. - kv_stride (int): The feature stride acting on key/value feature map. - Default: 2. - q_stride (int): The feature stride acting on query feature map. - Default: 1. - attention_type (str): A binary indicator string for indicating which - items in generalized empirical_attention module are used. - Default: '1111'. - - - '1000' indicates 'query and key content' (appr - appr) item, - - '0100' indicates 'query content and relative position' - (appr - position) item, - - '0010' indicates 'key content only' (bias - appr) item, - - '0001' indicates 'relative position only' (bias - position) item. - """ - - _abbr_ = 'gen_attention_block' - - def __init__(self, - in_channels, - spatial_range=-1, - num_heads=9, - position_embedding_dim=-1, - position_magnitude=1, - kv_stride=2, - q_stride=1, - attention_type='1111'): - - super(GeneralizedAttention, self).__init__() - - # hard range means local range for non-local operation - self.position_embedding_dim = ( - position_embedding_dim - if position_embedding_dim > 0 else in_channels) - - self.position_magnitude = position_magnitude - self.num_heads = num_heads - self.in_channels = in_channels - self.spatial_range = spatial_range - self.kv_stride = kv_stride - self.q_stride = q_stride - self.attention_type = [bool(int(_)) for _ in attention_type] - self.qk_embed_dim = in_channels // num_heads - out_c = self.qk_embed_dim * num_heads - - if self.attention_type[0] or self.attention_type[1]: - self.query_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.query_conv.kaiming_init = True - - if self.attention_type[0] or self.attention_type[2]: - self.key_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.key_conv.kaiming_init = True - - self.v_dim = in_channels // num_heads - self.value_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=self.v_dim * num_heads, - kernel_size=1, - bias=False) - self.value_conv.kaiming_init = True - - if self.attention_type[1] or self.attention_type[3]: - self.appr_geom_fc_x = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_x.kaiming_init = True - - self.appr_geom_fc_y = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_y.kaiming_init = True - - if self.attention_type[2]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.appr_bias = nn.Parameter(appr_bias_value) - - if self.attention_type[3]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.geom_bias = nn.Parameter(geom_bias_value) - - self.proj_conv = nn.Conv2d( - in_channels=self.v_dim * num_heads, - out_channels=in_channels, - kernel_size=1, - bias=True) - self.proj_conv.kaiming_init = True - self.gamma = nn.Parameter(torch.zeros(1)) - - if self.spatial_range >= 0: - # only works when non local is after 3*3 conv - if in_channels == 256: - max_len = 84 - elif in_channels == 512: - max_len = 42 - - max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) - local_constraint_map = np.ones( - (max_len, max_len, max_len_kv, max_len_kv), dtype=np.int) - for iy in range(max_len): - for ix in range(max_len): - local_constraint_map[ - iy, ix, - max((iy - self.spatial_range) // - self.kv_stride, 0):min((iy + self.spatial_range + - 1) // self.kv_stride + - 1, max_len), - max((ix - self.spatial_range) // - self.kv_stride, 0):min((ix + self.spatial_range + - 1) // self.kv_stride + - 1, max_len)] = 0 - - self.local_constraint_map = nn.Parameter( - torch.from_numpy(local_constraint_map).byte(), - requires_grad=False) - - if self.q_stride > 1: - self.q_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.q_stride) - else: - self.q_downsample = None - - if self.kv_stride > 1: - self.kv_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.kv_stride) - else: - self.kv_downsample = None - - self.init_weights() - - def get_position_embedding(self, - h, - w, - h_kv, - w_kv, - q_stride, - kv_stride, - device, - dtype, - feat_dim, - wave_length=1000): - # the default type of Tensor is float32, leading to type mismatch - # in fp16 mode. Cast it to support fp16 mode. - h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) - h_idxs = h_idxs.view((h, 1)) * q_stride - - w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) - w_idxs = w_idxs.view((w, 1)) * q_stride - - h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( - device=device, dtype=dtype) - h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride - - w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( - device=device, dtype=dtype) - w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride - - # (h, h_kv, 1) - h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) - h_diff *= self.position_magnitude - - # (w, w_kv, 1) - w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) - w_diff *= self.position_magnitude - - feat_range = torch.arange(0, feat_dim / 4).to( - device=device, dtype=dtype) - - dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) - dim_mat = dim_mat**((4. / feat_dim) * feat_range) - dim_mat = dim_mat.view((1, 1, -1)) - - embedding_x = torch.cat( - ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) - - embedding_y = torch.cat( - ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) - - return embedding_x, embedding_y - - def forward(self, x_input): - num_heads = self.num_heads - - # use empirical_attention - if self.q_downsample is not None: - x_q = self.q_downsample(x_input) - else: - x_q = x_input - n, _, h, w = x_q.shape - - if self.kv_downsample is not None: - x_kv = self.kv_downsample(x_input) - else: - x_kv = x_input - _, _, h_kv, w_kv = x_kv.shape - - if self.attention_type[0] or self.attention_type[1]: - proj_query = self.query_conv(x_q).view( - (n, num_heads, self.qk_embed_dim, h * w)) - proj_query = proj_query.permute(0, 1, 3, 2) - - if self.attention_type[0] or self.attention_type[2]: - proj_key = self.key_conv(x_kv).view( - (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) - - if self.attention_type[1] or self.attention_type[3]: - position_embed_x, position_embed_y = self.get_position_embedding( - h, w, h_kv, w_kv, self.q_stride, self.kv_stride, - x_input.device, x_input.dtype, self.position_embedding_dim) - # (n, num_heads, w, w_kv, dim) - position_feat_x = self.appr_geom_fc_x(position_embed_x).\ - view(1, w, w_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - # (n, num_heads, h, h_kv, dim) - position_feat_y = self.appr_geom_fc_y(position_embed_y).\ - view(1, h, h_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - position_feat_x /= math.sqrt(2) - position_feat_y /= math.sqrt(2) - - # accelerate for saliency only - if (np.sum(self.attention_type) == 1) and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy = torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, h_kv * w_kv) - - h = 1 - w = 1 - else: - # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for - if not self.attention_type[0]: - energy = torch.zeros( - n, - num_heads, - h, - w, - h_kv, - w_kv, - dtype=x_input.dtype, - device=x_input.device) - - # attention_type[0]: appr - appr - # attention_type[1]: appr - position - # attention_type[2]: bias - appr - # attention_type[3]: bias - position - if self.attention_type[0] or self.attention_type[2]: - if self.attention_type[0] and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - energy = torch.matmul(proj_query + appr_bias, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[0]: - energy = torch.matmul(proj_query, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy += torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, 1, h_kv, w_kv) - - if self.attention_type[1] or self.attention_type[3]: - if self.attention_type[1] and self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - - proj_query_reshape = (proj_query + geom_bias).\ - view(n, num_heads, h, w, self.qk_embed_dim) - - energy_x = torch.matmul( - proj_query_reshape.permute(0, 1, 3, 2, 4), - position_feat_x.permute(0, 1, 2, 4, 3)) - energy_x = energy_x.\ - permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul( - proj_query_reshape, - position_feat_y.permute(0, 1, 2, 4, 3)) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[1]: - proj_query_reshape = proj_query.\ - view(n, num_heads, h, w, self.qk_embed_dim) - proj_query_reshape = proj_query_reshape.\ - permute(0, 1, 3, 2, 4) - position_feat_x_reshape = position_feat_x.\ - permute(0, 1, 2, 4, 3) - position_feat_y_reshape = position_feat_y.\ - permute(0, 1, 2, 4, 3) - - energy_x = torch.matmul(proj_query_reshape, - position_feat_x_reshape) - energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul(proj_query_reshape, - position_feat_y_reshape) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, self.qk_embed_dim, 1).\ - repeat(n, 1, 1, 1) - - position_feat_x_reshape = position_feat_x.\ - view(n, num_heads, w*w_kv, self.qk_embed_dim) - - position_feat_y_reshape = position_feat_y.\ - view(n, num_heads, h * h_kv, self.qk_embed_dim) - - energy_x = torch.matmul(position_feat_x_reshape, geom_bias) - energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) - - energy_y = torch.matmul(position_feat_y_reshape, geom_bias) - energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) - - energy += energy_x + energy_y - - energy = energy.view(n, num_heads, h * w, h_kv * w_kv) - - if self.spatial_range >= 0: - cur_local_constraint_map = \ - self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ - contiguous().\ - view(1, 1, h*w, h_kv*w_kv) - - energy = energy.masked_fill_(cur_local_constraint_map, - float('-inf')) - - attention = F.softmax(energy, 3) - - proj_value = self.value_conv(x_kv) - proj_value_reshape = proj_value.\ - view((n, num_heads, self.v_dim, h_kv * w_kv)).\ - permute(0, 1, 3, 2) - - out = torch.matmul(attention, proj_value_reshape).\ - permute(0, 1, 3, 2).\ - contiguous().\ - view(n, self.v_dim * self.num_heads, h, w) - - out = self.proj_conv(out) - - # output is downsampled, upsample back to input size - if self.q_downsample is not None: - out = F.interpolate( - out, - size=x_input.shape[2:], - mode='bilinear', - align_corners=False) - - out = self.gamma * out + x_input - return out - - def init_weights(self): - for m in self.modules(): - if hasattr(m, 'kaiming_init') and m.kaiming_init: - kaiming_init( - m, - mode='fan_in', - nonlinearity='leaky_relu', - bias=0, - distribution='uniform', - a=1) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/hsigmoid.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/hsigmoid.py deleted file mode 100644 index 30b1a3d65..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/hsigmoid.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSigmoid(nn.Module): - """Hard Sigmoid Module. Apply the hard sigmoid function: - Hsigmoid(x) = min(max((x + bias) / divisor, min_value), max_value) - Default: Hsigmoid(x) = min(max((x + 1) / 2, 0), 1) - - Args: - bias (float): Bias of the input feature map. Default: 1.0. - divisor (float): Divisor of the input feature map. Default: 2.0. - min_value (float): Lower bound value. Default: 0.0. - max_value (float): Upper bound value. Default: 1.0. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, bias=1.0, divisor=2.0, min_value=0.0, max_value=1.0): - super(HSigmoid, self).__init__() - self.bias = bias - self.divisor = divisor - assert self.divisor != 0 - self.min_value = min_value - self.max_value = max_value - - def forward(self, x): - x = (x + self.bias) / self.divisor - - return x.clamp_(self.min_value, self.max_value) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/hswish.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/hswish.py deleted file mode 100644 index 7e0c090ff..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/hswish.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSwish(nn.Module): - """Hard Swish Module. - - This module applies the hard swish function: - - .. math:: - Hswish(x) = x * ReLU6(x + 3) / 6 - - Args: - inplace (bool): can optionally do the operation in-place. - Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.act = nn.ReLU6(inplace) - - def forward(self, x): - return x * self.act(x + 3) / 6 diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/non_local.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/non_local.py deleted file mode 100644 index 92d00155e..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/non_local.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta - -import torch -import torch.nn as nn - -from ..utils import constant_init, normal_init -from .conv_module import ConvModule -from .registry import PLUGIN_LAYERS - - -class _NonLocalNd(nn.Module, metaclass=ABCMeta): - """Basic Non-local module. - - This module is proposed in - "Non-local Neural Networks" - Paper reference: https://arxiv.org/abs/1711.07971 - Code reference: https://github.com/AlexHex7/Non-local_pytorch - - Args: - in_channels (int): Channels of the input feature map. - reduction (int): Channel reduction ratio. Default: 2. - use_scale (bool): Whether to scale pairwise_weight by - `1/sqrt(inter_channels)` when the mode is `embedded_gaussian`. - Default: True. - conv_cfg (None | dict): The config dict for convolution layers. - If not specified, it will use `nn.Conv2d` for convolution layers. - Default: None. - norm_cfg (None | dict): The config dict for normalization layers. - Default: None. (This parameter is only applicable to conv_out.) - mode (str): Options are `gaussian`, `concatenation`, - `embedded_gaussian` and `dot_product`. Default: embedded_gaussian. - """ - - def __init__(self, - in_channels, - reduction=2, - use_scale=True, - conv_cfg=None, - norm_cfg=None, - mode='embedded_gaussian', - **kwargs): - super(_NonLocalNd, self).__init__() - self.in_channels = in_channels - self.reduction = reduction - self.use_scale = use_scale - self.inter_channels = max(in_channels // reduction, 1) - self.mode = mode - - if mode not in [ - 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation' - ]: - raise ValueError("Mode should be in 'gaussian', 'concatenation', " - f"'embedded_gaussian' or 'dot_product', but got " - f'{mode} instead.') - - # g, theta, phi are defaulted as `nn.ConvNd`. - # Here we use ConvModule for potential usage. - self.g = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.conv_out = ConvModule( - self.inter_channels, - self.in_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - if self.mode != 'gaussian': - self.theta = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.phi = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - - if self.mode == 'concatenation': - self.concat_project = ConvModule( - self.inter_channels * 2, - 1, - kernel_size=1, - stride=1, - padding=0, - bias=False, - act_cfg=dict(type='ReLU')) - - self.init_weights(**kwargs) - - def init_weights(self, std=0.01, zeros_init=True): - if self.mode != 'gaussian': - for m in [self.g, self.theta, self.phi]: - normal_init(m.conv, std=std) - else: - normal_init(self.g.conv, std=std) - if zeros_init: - if self.conv_out.norm_cfg is None: - constant_init(self.conv_out.conv, 0) - else: - constant_init(self.conv_out.norm, 0) - else: - if self.conv_out.norm_cfg is None: - normal_init(self.conv_out.conv, std=std) - else: - normal_init(self.conv_out.norm, std=std) - - def gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def embedded_gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - if self.use_scale: - # theta_x.shape[-1] is `self.inter_channels` - pairwise_weight /= theta_x.shape[-1]**0.5 - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def dot_product(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight /= pairwise_weight.shape[-1] - return pairwise_weight - - def concatenation(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - h = theta_x.size(2) - w = phi_x.size(3) - theta_x = theta_x.repeat(1, 1, 1, w) - phi_x = phi_x.repeat(1, 1, h, 1) - - concat_feature = torch.cat([theta_x, phi_x], dim=1) - pairwise_weight = self.concat_project(concat_feature) - n, _, h, w = pairwise_weight.size() - pairwise_weight = pairwise_weight.view(n, h, w) - pairwise_weight /= pairwise_weight.shape[-1] - - return pairwise_weight - - def forward(self, x): - # Assume `reduction = 1`, then `inter_channels = C` - # or `inter_channels = C` when `mode="gaussian"` - - # NonLocal1d x: [N, C, H] - # NonLocal2d x: [N, C, H, W] - # NonLocal3d x: [N, C, T, H, W] - n = x.size(0) - - # NonLocal1d g_x: [N, H, C] - # NonLocal2d g_x: [N, HxW, C] - # NonLocal3d g_x: [N, TxHxW, C] - g_x = self.g(x).view(n, self.inter_channels, -1) - g_x = g_x.permute(0, 2, 1) - - # NonLocal1d theta_x: [N, H, C], phi_x: [N, C, H] - # NonLocal2d theta_x: [N, HxW, C], phi_x: [N, C, HxW] - # NonLocal3d theta_x: [N, TxHxW, C], phi_x: [N, C, TxHxW] - if self.mode == 'gaussian': - theta_x = x.view(n, self.in_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - if self.sub_sample: - phi_x = self.phi(x).view(n, self.in_channels, -1) - else: - phi_x = x.view(n, self.in_channels, -1) - elif self.mode == 'concatenation': - theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) - phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) - else: - theta_x = self.theta(x).view(n, self.inter_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - phi_x = self.phi(x).view(n, self.inter_channels, -1) - - pairwise_func = getattr(self, self.mode) - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = pairwise_func(theta_x, phi_x) - - # NonLocal1d y: [N, H, C] - # NonLocal2d y: [N, HxW, C] - # NonLocal3d y: [N, TxHxW, C] - y = torch.matmul(pairwise_weight, g_x) - # NonLocal1d y: [N, C, H] - # NonLocal2d y: [N, C, H, W] - # NonLocal3d y: [N, C, T, H, W] - y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, - *x.size()[2:]) - - output = x + self.conv_out(y) - - return output - - -class NonLocal1d(_NonLocalNd): - """1D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv1d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv1d'), - **kwargs): - super(NonLocal1d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool1d(kernel_size=2) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -@PLUGIN_LAYERS.register_module() -class NonLocal2d(_NonLocalNd): - """2D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv2d'). - """ - - _abbr_ = 'nonlocal_block' - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv2d'), - **kwargs): - super(NonLocal2d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -class NonLocal3d(_NonLocalNd): - """3D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv3d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv3d'), - **kwargs): - super(NonLocal3d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/norm.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/norm.py deleted file mode 100644 index cfb326bdb..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/norm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import torch.nn as nn - -from mmcv.utils import is_tuple_of -from mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm -from .registry import NORM_LAYERS - -NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) -NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) -NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) -NORM_LAYERS.register_module('GN', module=nn.GroupNorm) -NORM_LAYERS.register_module('LN', module=nn.LayerNorm) -NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) -NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - When we build a norm layer with `build_norm_layer()`, we want to preserve - the norm type in variable names, e.g, self.bn1, self.gn. This method will - infer the abbreviation to map class types to abbreviations. - - Rule 1: If the class has the property "_abbr_", return the property. - Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or - InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and - "in" respectively. - Rule 3: If the class name contains "batch", "group", "layer" or "instance", - the abbreviation of this layer will be "bn", "gn", "ln" and "in" - respectively. - Rule 4: Otherwise, the abbreviation falls back to "norm". - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN - return 'in' - elif issubclass(class_type, _BatchNorm): - return 'bn' - elif issubclass(class_type, nn.GroupNorm): - return 'gn' - elif issubclass(class_type, nn.LayerNorm): - return 'ln' - else: - class_name = class_type.__name__.lower() - if 'batch' in class_name: - return 'bn' - elif 'group' in class_name: - return 'gn' - elif 'layer' in class_name: - return 'ln' - elif 'instance' in class_name: - return 'in' - else: - return 'norm_layer' - - -def build_norm_layer(cfg, num_features, postfix=''): - """Build normalization layer. - - Args: - cfg (dict): The norm layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate a norm layer. - - requires_grad (bool, optional): Whether stop gradient updates. - num_features (int): Number of input channels. - postfix (int | str): The postfix to be appended into norm abbreviation - to create named layer. - - Returns: - (str, nn.Module): The first element is the layer name consisting of - abbreviation and postfix, e.g., bn1, gn. The second element is the - created norm layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in NORM_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - - norm_layer = NORM_LAYERS.get(layer_type) - abbr = infer_abbr(norm_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - requires_grad = cfg_.pop('requires_grad', True) - cfg_.setdefault('eps', 1e-5) - if layer_type != 'GN': - layer = norm_layer(num_features, **cfg_) - if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): - layer._specify_ddp_gpu_num(1) - else: - assert 'num_groups' in cfg_ - layer = norm_layer(num_channels=num_features, **cfg_) - - for param in layer.parameters(): - param.requires_grad = requires_grad - - return name, layer - - -def is_norm(layer, exclude=None): - """Check if a layer is a normalization layer. - - Args: - layer (nn.Module): The layer to be checked. - exclude (type | tuple[type]): Types to be excluded. - - Returns: - bool: Whether the layer is a norm layer. - """ - if exclude is not None: - if not isinstance(exclude, tuple): - exclude = (exclude, ) - if not is_tuple_of(exclude, type): - raise TypeError( - f'"exclude" must be either None or type or a tuple of types, ' - f'but got {type(exclude)}: {exclude}') - - if exclude and isinstance(layer, exclude): - return False - - all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) - return isinstance(layer, all_norm_bases) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/padding.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/padding.py deleted file mode 100644 index e4ac6b28a..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/padding.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import PADDING_LAYERS - -PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) -PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) -PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) - - -def build_padding_layer(cfg, *args, **kwargs): - """Build padding layer. - - Args: - cfg (None or dict): The padding layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate a padding layer. - - Returns: - nn.Module: Created padding layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - - cfg_ = cfg.copy() - padding_type = cfg_.pop('type') - if padding_type not in PADDING_LAYERS: - raise KeyError(f'Unrecognized padding type {padding_type}.') - else: - padding_layer = PADDING_LAYERS.get(padding_type) - - layer = padding_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/plugin.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/plugin.py deleted file mode 100644 index 07c010d40..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/plugin.py +++ /dev/null @@ -1,88 +0,0 @@ -import inspect -import platform - -from .registry import PLUGIN_LAYERS - -if platform.system() == 'Windows': - import regex as re -else: - import re - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - This method will infer the abbreviation to map class types to - abbreviations. - - Rule 1: If the class has the property "abbr", return the property. - Rule 2: Otherwise, the abbreviation falls back to snake case of class - name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - - def camel2snack(word): - """Convert camel case word into snack case. - - Modified from `inflection lib - `_. - - Example:: - - >>> camel2snack("FancyBlock") - 'fancy_block' - """ - - word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) - word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) - word = word.replace('-', '_') - return word.lower() - - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - else: - return camel2snack(class_type.__name__) - - -def build_plugin_layer(cfg, postfix='', **kwargs): - """Build plugin layer. - - Args: - cfg (None or dict): cfg should contain: - type (str): identify plugin layer type. - layer args: args needed to instantiate a plugin layer. - postfix (int, str): appended into norm abbreviation to - create named layer. Default: ''. - - Returns: - tuple[str, nn.Module]: - name (str): abbreviation + postfix - layer (nn.Module): created plugin layer - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in PLUGIN_LAYERS: - raise KeyError(f'Unrecognized plugin type {layer_type}') - - plugin_layer = PLUGIN_LAYERS.get(layer_type) - abbr = infer_abbr(plugin_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - layer = plugin_layer(**kwargs, **cfg_) - - return name, layer diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/registry.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/registry.py deleted file mode 100644 index c29279776..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/registry.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -CONV_LAYERS = Registry('conv layer') -NORM_LAYERS = Registry('norm layer') -ACTIVATION_LAYERS = Registry('activation layer') -PADDING_LAYERS = Registry('padding layer') -UPSAMPLE_LAYERS = Registry('upsample layer') -PLUGIN_LAYERS = Registry('plugin layer') - -DROPOUT_LAYERS = Registry('drop out layers') -POSITIONAL_ENCODING = Registry('position encoding') -ATTENTION = Registry('attention') -FEEDFORWARD_NETWORK = Registry('feed-forward Network') -TRANSFORMER_LAYER = Registry('transformerLayer') -TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/scale.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/scale.py deleted file mode 100644 index c905fffcc..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/scale.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class Scale(nn.Module): - """A learnable scale parameter. - - This layer scales the input by a learnable factor. It multiplies a - learnable scale parameter of shape (1,) with input of any shape. - - Args: - scale (float): Initial value of scale factor. Default: 1.0 - """ - - def __init__(self, scale=1.0): - super(Scale, self).__init__() - self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) - - def forward(self, x): - return x * self.scale diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/swish.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/swish.py deleted file mode 100644 index e2ca8ed7b..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/swish.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class Swish(nn.Module): - """Swish Module. - - This module applies the swish function: - - .. math:: - Swish(x) = x * Sigmoid(x) - - Returns: - Tensor: The output tensor. - """ - - def __init__(self): - super(Swish, self).__init__() - - def forward(self, x): - return x * torch.sigmoid(x) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/transformer.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/transformer.py deleted file mode 100644 index ed32688af..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/transformer.py +++ /dev/null @@ -1,595 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings - -import torch -import torch.nn as nn - -from mmcv import ConfigDict, deprecated_api_warning -from mmcv.cnn import Linear, build_activation_layer, build_norm_layer -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential -from mmcv.utils import build_from_cfg -from .drop import build_dropout -from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, - TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) - -# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file -try: - from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention # noqa F401 - warnings.warn( - ImportWarning( - '``MultiScaleDeformableAttention`` has been moved to ' - '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 - '``from mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 - 'to ``from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 - )) - -except ImportError: - warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' - '``mmcv.ops.multi_scale_deform_attn``, ' - 'You should install ``mmcv-full`` if you need this module. ') - - -def build_positional_encoding(cfg, default_args=None): - """Builder for Position Encoding.""" - return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) - - -def build_attention(cfg, default_args=None): - """Builder for attention.""" - return build_from_cfg(cfg, ATTENTION, default_args) - - -def build_feedforward_network(cfg, default_args=None): - """Builder for feed-forward network (FFN).""" - return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) - - -def build_transformer_layer(cfg, default_args=None): - """Builder for transformer layer.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) - - -def build_transformer_layer_sequence(cfg, default_args=None): - """Builder for transformer encoder and transformer decoder.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) - - -@ATTENTION.register_module() -class MultiheadAttention(BaseModule): - """A wrapper for ``torch.nn.MultiheadAttention``. - - This module implements MultiheadAttention with identity connection, - and positional encoding is also passed as input. - - Args: - embed_dims (int): The embedding dimension. - num_heads (int): Parallel attention heads. - attn_drop (float): A Dropout layer on attn_output_weights. - Default: 0.0. - proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. - Default: 0.0. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): When it is True, Key, Query and Value are shape of - (batch, n, embed_dim), otherwise (n, batch, embed_dim). - Default to False. - """ - - def __init__(self, - embed_dims, - num_heads, - attn_drop=0., - proj_drop=0., - dropout_layer=dict(type='Dropout', drop_prob=0.), - init_cfg=None, - batch_first=False, - **kwargs): - super(MultiheadAttention, self).__init__(init_cfg) - if 'dropout' in kwargs: - warnings.warn('The arguments `dropout` in MultiheadAttention ' - 'has been deprecated, now you can separately ' - 'set `attn_drop`(float), proj_drop(float), ' - 'and `dropout_layer`(dict) ') - attn_drop = kwargs['dropout'] - dropout_layer['drop_prob'] = kwargs.pop('dropout') - - self.embed_dims = embed_dims - self.num_heads = num_heads - self.batch_first = batch_first - - self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, - **kwargs) - - self.proj_drop = nn.Dropout(proj_drop) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else nn.Identity() - - @deprecated_api_warning({'residual': 'identity'}, - cls_name='MultiheadAttention') - def forward(self, - query, - key=None, - value=None, - identity=None, - query_pos=None, - key_pos=None, - attn_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `MultiheadAttention`. - - **kwargs allow passing a more general data flow when combining - with other operations in `transformerlayer`. - - Args: - query (Tensor): The input query with shape [num_queries, bs, - embed_dims] if self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - If None, the ``query`` will be used. Defaults to None. - value (Tensor): The value tensor with same shape as `key`. - Same in `nn.MultiheadAttention.forward`. Defaults to None. - If None, the `key` will be used. - identity (Tensor): This tensor, with the same shape as x, - will be used for the identity link. - If None, `x` will be used. Defaults to None. - query_pos (Tensor): The positional encoding for query, with - the same shape as `x`. If not None, it will - be added to `x` before forward function. Defaults to None. - key_pos (Tensor): The positional encoding for `key`, with the - same shape as `key`. Defaults to None. If not None, it will - be added to `key` before forward function. If None, and - `query_pos` has the same shape as `key`, then `query_pos` - will be used for `key_pos`. Defaults to None. - attn_mask (Tensor): ByteTensor mask with shape [num_queries, - num_keys]. Same in `nn.MultiheadAttention.forward`. - Defaults to None. - key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. - Defaults to None. - - Returns: - Tensor: forwarded results with shape - [num_queries, bs, embed_dims] - if self.batch_first is False, else - [bs, num_queries embed_dims]. - """ - - if key is None: - key = query - if value is None: - value = key - if identity is None: - identity = query - if key_pos is None: - if query_pos is not None: - # use query_pos if key_pos is not available - if query_pos.shape == key.shape: - key_pos = query_pos - else: - warnings.warn(f'position encoding of key is' - f'missing in {self.__class__.__name__}.') - if query_pos is not None: - query = query + query_pos - if key_pos is not None: - key = key + key_pos - - # Because the dataflow('key', 'query', 'value') of - # ``torch.nn.MultiheadAttention`` is (num_query, batch, - # embed_dims), We should adjust the shape of dataflow from - # batch_first (batch, num_query, embed_dims) to num_query_first - # (num_query ,batch, embed_dims), and recover ``attn_output`` - # from num_query_first to batch_first. - if self.batch_first: - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - out = self.attn( - query=query, - key=key, - value=value, - attn_mask=attn_mask, - key_padding_mask=key_padding_mask)[0] - - if self.batch_first: - out = out.transpose(0, 1) - - return identity + self.dropout_layer(self.proj_drop(out)) - - -@FEEDFORWARD_NETWORK.register_module() -class FFN(BaseModule): - """Implements feed-forward networks (FFNs) with identity connection. - - Args: - embed_dims (int): The feature dimension. Same as - `MultiheadAttention`. Defaults: 256. - feedforward_channels (int): The hidden dimension of FFNs. - Defaults: 1024. - num_fcs (int, optional): The number of fully-connected layers in - FFNs. Default: 2. - act_cfg (dict, optional): The activation config for FFNs. - Default: dict(type='ReLU') - ffn_drop (float, optional): Probability of an element to be - zeroed in FFN. Default 0.0. - add_identity (bool, optional): Whether to add the - identity connection. Default: `True`. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - @deprecated_api_warning( - { - 'dropout': 'ffn_drop', - 'add_residual': 'add_identity' - }, - cls_name='FFN') - def __init__(self, - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - act_cfg=dict(type='ReLU', inplace=True), - ffn_drop=0., - dropout_layer=None, - add_identity=True, - init_cfg=None, - **kwargs): - super(FFN, self).__init__(init_cfg) - assert num_fcs >= 2, 'num_fcs should be no less ' \ - f'than 2. got {num_fcs}.' - self.embed_dims = embed_dims - self.feedforward_channels = feedforward_channels - self.num_fcs = num_fcs - self.act_cfg = act_cfg - self.activate = build_activation_layer(act_cfg) - - layers = [] - in_channels = embed_dims - for _ in range(num_fcs - 1): - layers.append( - Sequential( - Linear(in_channels, feedforward_channels), self.activate, - nn.Dropout(ffn_drop))) - in_channels = feedforward_channels - layers.append(Linear(feedforward_channels, embed_dims)) - layers.append(nn.Dropout(ffn_drop)) - self.layers = Sequential(*layers) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else torch.nn.Identity() - self.add_identity = add_identity - - @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') - def forward(self, x, identity=None): - """Forward function for `FFN`. - - The function would add x to the output tensor if residue is None. - """ - out = self.layers(x) - if not self.add_identity: - return self.dropout_layer(out) - if identity is None: - identity = x - return identity + self.dropout_layer(out) - - -@TRANSFORMER_LAYER.register_module() -class BaseTransformerLayer(BaseModule): - """Base `TransformerLayer` for vision transformer. - - It can be built from `mmcv.ConfigDict` and support more flexible - customization, for example, using any number of `FFN or LN ` and - use different kinds of `attention` by specifying a list of `ConfigDict` - named `attn_cfgs`. It is worth mentioning that it supports `prenorm` - when you specifying `norm` as the first element of `operation_order`. - More details about the `prenorm`: `On Layer Normalization in the - Transformer Architecture `_ . - - Args: - attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for `self_attention` or `cross_attention` modules, - The order of the configs in the list should be consistent with - corresponding attentions in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. Default: None. - ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for FFN, The order of the configs in the list should be - consistent with corresponding ffn in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. - operation_order (tuple[str]): The execution order of operation - in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). - Support `prenorm` when you specifying first element as `norm`. - Default:None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): Key, Query and Value are shape - of (batch, n, embed_dim) - or (n, batch, embed_dim). Default to False. - """ - - def __init__(self, - attn_cfgs=None, - ffn_cfgs=dict( - type='FFN', - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - ffn_drop=0., - act_cfg=dict(type='ReLU', inplace=True), - ), - operation_order=None, - norm_cfg=dict(type='LN'), - init_cfg=None, - batch_first=False, - **kwargs): - - deprecated_args = dict( - feedforward_channels='feedforward_channels', - ffn_dropout='ffn_drop', - ffn_num_fcs='num_fcs') - for ori_name, new_name in deprecated_args.items(): - if ori_name in kwargs: - warnings.warn( - f'The arguments `{ori_name}` in BaseTransformerLayer ' - f'has been deprecated, now you should set `{new_name}` ' - f'and other FFN related arguments ' - f'to a dict named `ffn_cfgs`. ') - ffn_cfgs[new_name] = kwargs[ori_name] - - super(BaseTransformerLayer, self).__init__(init_cfg) - - self.batch_first = batch_first - - assert set(operation_order) & set( - ['self_attn', 'norm', 'ffn', 'cross_attn']) == \ - set(operation_order), f'The operation_order of' \ - f' {self.__class__.__name__} should ' \ - f'contains all four operation type ' \ - f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" - - num_attn = operation_order.count('self_attn') + operation_order.count( - 'cross_attn') - if isinstance(attn_cfgs, dict): - attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] - else: - assert num_attn == len(attn_cfgs), f'The length ' \ - f'of attn_cfg {num_attn} is ' \ - f'not consistent with the number of attention' \ - f'in operation_order {operation_order}.' - - self.num_attn = num_attn - self.operation_order = operation_order - self.norm_cfg = norm_cfg - self.pre_norm = operation_order[0] == 'norm' - self.attentions = ModuleList() - - index = 0 - for operation_name in operation_order: - if operation_name in ['self_attn', 'cross_attn']: - if 'batch_first' in attn_cfgs[index]: - assert self.batch_first == attn_cfgs[index]['batch_first'] - else: - attn_cfgs[index]['batch_first'] = self.batch_first - attention = build_attention(attn_cfgs[index]) - # Some custom attentions used as `self_attn` - # or `cross_attn` can have different behavior. - attention.operation_name = operation_name - self.attentions.append(attention) - index += 1 - - self.embed_dims = self.attentions[0].embed_dims - - self.ffns = ModuleList() - num_ffns = operation_order.count('ffn') - if isinstance(ffn_cfgs, dict): - ffn_cfgs = ConfigDict(ffn_cfgs) - if isinstance(ffn_cfgs, dict): - ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] - assert len(ffn_cfgs) == num_ffns - for ffn_index in range(num_ffns): - if 'embed_dims' not in ffn_cfgs[ffn_index]: - ffn_cfgs['embed_dims'] = self.embed_dims - else: - assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims - self.ffns.append( - build_feedforward_network(ffn_cfgs[ffn_index], - dict(type='FFN'))) - - self.norms = ModuleList() - num_norms = operation_order.count('norm') - for _ in range(num_norms): - self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) - - def forward(self, - query, - key=None, - value=None, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerDecoderLayer`. - - **kwargs contains some specific arguments of attentions. - - Args: - query (Tensor): The input query with shape - [num_queries, bs, embed_dims] if - self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - value (Tensor): The value tensor with same shape as `key`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor] | None): 2D Tensor used in - calculation of corresponding attention. The length of - it should equal to the number of `attention` in - `operation_order`. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in `self_attn` layer. - Defaults to None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: forwarded results with shape [num_queries, bs, embed_dims]. - """ - - norm_index = 0 - attn_index = 0 - ffn_index = 0 - identity = query - if attn_masks is None: - attn_masks = [None for _ in range(self.num_attn)] - elif isinstance(attn_masks, torch.Tensor): - attn_masks = [ - copy.deepcopy(attn_masks) for _ in range(self.num_attn) - ] - warnings.warn(f'Use same attn_mask in all attentions in ' - f'{self.__class__.__name__} ') - else: - assert len(attn_masks) == self.num_attn, f'The length of ' \ - f'attn_masks {len(attn_masks)} must be equal ' \ - f'to the number of attention in ' \ - f'operation_order {self.num_attn}' - - for layer in self.operation_order: - if layer == 'self_attn': - temp_key = temp_value = query - query = self.attentions[attn_index]( - query, - temp_key, - temp_value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=query_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=query_key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'norm': - query = self.norms[norm_index](query) - norm_index += 1 - - elif layer == 'cross_attn': - query = self.attentions[attn_index]( - query, - key, - value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=key_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'ffn': - query = self.ffns[ffn_index]( - query, identity if self.pre_norm else None) - ffn_index += 1 - - return query - - -@TRANSFORMER_LAYER_SEQUENCE.register_module() -class TransformerLayerSequence(BaseModule): - """Base class for TransformerEncoder and TransformerDecoder in vision - transformer. - - As base-class of Encoder and Decoder in vision transformer. - Support customization such as specifying different kind - of `transformer_layer` in `transformer_coder`. - - Args: - transformerlayer (list[obj:`mmcv.ConfigDict`] | - obj:`mmcv.ConfigDict`): Config of transformerlayer - in TransformerCoder. If it is obj:`mmcv.ConfigDict`, - it would be repeated `num_layer` times to a - list[`mmcv.ConfigDict`]. Default: None. - num_layers (int): The number of `TransformerLayer`. Default: None. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): - super(TransformerLayerSequence, self).__init__(init_cfg) - if isinstance(transformerlayers, dict): - transformerlayers = [ - copy.deepcopy(transformerlayers) for _ in range(num_layers) - ] - else: - assert isinstance(transformerlayers, list) and \ - len(transformerlayers) == num_layers - self.num_layers = num_layers - self.layers = ModuleList() - for i in range(num_layers): - self.layers.append(build_transformer_layer(transformerlayers[i])) - self.embed_dims = self.layers[0].embed_dims - self.pre_norm = self.layers[0].pre_norm - - def forward(self, - query, - key, - value, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerCoder`. - - Args: - query (Tensor): Input query with shape - `(num_queries, bs, embed_dims)`. - key (Tensor): The key tensor with shape - `(num_keys, bs, embed_dims)`. - value (Tensor): The value tensor with shape - `(num_keys, bs, embed_dims)`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor], optional): Each element is 2D Tensor - which is used in calculation of corresponding attention in - operation_order. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in self-attention - Default: None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: results with shape [num_queries, bs, embed_dims]. - """ - for layer in self.layers: - query = layer( - query, - key, - value, - query_pos=query_pos, - key_pos=key_pos, - attn_masks=attn_masks, - query_key_padding_mask=query_key_padding_mask, - key_padding_mask=key_padding_mask, - **kwargs) - return query diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/upsample.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/upsample.py deleted file mode 100644 index a1a353767..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/upsample.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import xavier_init -from .registry import UPSAMPLE_LAYERS - -UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) -UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) - - -@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') -class PixelShufflePack(nn.Module): - """Pixel Shuffle upsample layer. - - This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to - achieve a simple upsampling with pixel shuffle. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of the conv layer to expand the - channels. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super(PixelShufflePack, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - xavier_init(self.upsample_conv, distribution='uniform') - - def forward(self, x): - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x - - -def build_upsample_layer(cfg, *args, **kwargs): - """Build upsample layer. - - Args: - cfg (dict): The upsample layer config, which should contain: - - - type (str): Layer type. - - scale_factor (int): Upsample ratio, which is not applicable to - deconv. - - layer args: Args needed to instantiate a upsample layer. - args (argument list): Arguments passed to the ``__init__`` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the - ``__init__`` method of the corresponding conv layer. - - Returns: - nn.Module: Created upsample layer. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - raise KeyError( - f'the cfg dict must contain the key "type", but got {cfg}') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in UPSAMPLE_LAYERS: - raise KeyError(f'Unrecognized upsample type {layer_type}') - else: - upsample = UPSAMPLE_LAYERS.get(layer_type) - - if upsample is nn.Upsample: - cfg_['mode'] = layer_type - layer = upsample(*args, **kwargs, **cfg_) - return layer diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/wrappers.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/wrappers.py deleted file mode 100644 index 8aebf67bf..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/bricks/wrappers.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 - -Wrap some nn modules to support empty tensor input. Currently, these wrappers -are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask -heads are trained on only positive RoIs. -""" -import math - -import torch -import torch.nn as nn -from torch.nn.modules.utils import _pair, _triple - -from .registry import CONV_LAYERS, UPSAMPLE_LAYERS - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def obsolete_torch_version(torch_version, version_threshold): - return torch_version == 'parrots' or torch_version <= version_threshold - - -class NewEmptyTensorOp(torch.autograd.Function): - - @staticmethod - def forward(ctx, x, new_shape): - ctx.shape = x.shape - return x.new_empty(new_shape) - - @staticmethod - def backward(ctx, grad): - shape = ctx.shape - return NewEmptyTensorOp.apply(grad, shape), None - - -@CONV_LAYERS.register_module('Conv', force=True) -class Conv2d(nn.Conv2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module('Conv3d', force=True) -class Conv3d(nn.Conv3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv') -@UPSAMPLE_LAYERS.register_module('deconv', force=True) -class ConvTranspose2d(nn.ConvTranspose2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv3d') -@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) -class ConvTranspose3d(nn.ConvTranspose3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -class MaxPool2d(nn.MaxPool2d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), - _pair(self.padding), _pair(self.stride), - _pair(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class MaxPool3d(nn.MaxPool3d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), - _triple(self.padding), - _triple(self.stride), - _triple(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class Linear(torch.nn.Linear): - - def forward(self, x): - # empty tensor forward of Linear layer is supported in Pytorch 1.6 - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): - out_shape = [x.shape[0], self.out_features] - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/builder.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/builder.py deleted file mode 100644 index 7567316c5..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/builder.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..runner import Sequential -from ..utils import Registry, build_from_cfg - - -def build_model_from_cfg(cfg, registry, default_args=None): - """Build a PyTorch model from config dict(s). Different from - ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. - - Args: - cfg (dict, list[dict]): The config of modules, is is either a config - dict or a list of config dicts. If cfg is a list, a - the built modules will be wrapped with ``nn.Sequential``. - registry (:obj:`Registry`): A registry the module belongs to. - default_args (dict, optional): Default arguments to build the module. - Defaults to None. - - Returns: - nn.Module: A built nn module. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return Sequential(*modules) - else: - return build_from_cfg(cfg, registry, default_args) - - -MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/resnet.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/resnet.py deleted file mode 100644 index 1cb3ac057..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/resnet.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn -import torch.utils.checkpoint as cp - -from .utils import constant_init, kaiming_init - - -def conv3x3(in_planes, out_planes, stride=1, dilation=1): - """3x3 convolution with padding.""" - return nn.Conv2d( - in_planes, - out_planes, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False): - super(BasicBlock, self).__init__() - assert style in ['pytorch', 'caffe'] - self.conv1 = conv3x3(inplanes, planes, stride, dilation) - self.bn1 = nn.BatchNorm2d(planes) - self.relu = nn.ReLU(inplace=True) - self.conv2 = conv3x3(planes, planes) - self.bn2 = nn.BatchNorm2d(planes) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - assert not with_cp - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Bottleneck(nn.Module): - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False): - """Bottleneck block. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if - it is "caffe", the stride-two layer is the first 1x1 conv layer. - """ - super(Bottleneck, self).__init__() - assert style in ['pytorch', 'caffe'] - if style == 'pytorch': - conv1_stride = 1 - conv2_stride = stride - else: - conv1_stride = stride - conv2_stride = 1 - self.conv1 = nn.Conv2d( - inplanes, planes, kernel_size=1, stride=conv1_stride, bias=False) - self.conv2 = nn.Conv2d( - planes, - planes, - kernel_size=3, - stride=conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.bn1 = nn.BatchNorm2d(planes) - self.bn2 = nn.BatchNorm2d(planes) - self.conv3 = nn.Conv2d( - planes, planes * self.expansion, kernel_size=1, bias=False) - self.bn3 = nn.BatchNorm2d(planes * self.expansion) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - def forward(self, x): - - def _inner_forward(x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -def make_res_layer(block, - inplanes, - planes, - blocks, - stride=1, - dilation=1, - style='pytorch', - with_cp=False): - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = nn.Sequential( - nn.Conv2d( - inplanes, - planes * block.expansion, - kernel_size=1, - stride=stride, - bias=False), - nn.BatchNorm2d(planes * block.expansion), - ) - - layers = [] - layers.append( - block( - inplanes, - planes, - stride, - dilation, - downsample, - style=style, - with_cp=with_cp)) - inplanes = planes * block.expansion - for _ in range(1, blocks): - layers.append( - block(inplanes, planes, 1, dilation, style=style, with_cp=with_cp)) - - return nn.Sequential(*layers) - - -class ResNet(nn.Module): - """ResNet backbone. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - num_stages (int): Resnet stages, normally 4. - strides (Sequence[int]): Strides of the first block of each stage. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze - running stats (mean and var). - bn_frozen (bool): Whether to freeze weight and bias of BN layers. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - frozen_stages=-1, - bn_eval=True, - bn_frozen=False, - with_cp=False): - super(ResNet, self).__init__() - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - assert num_stages >= 1 and num_stages <= 4 - block, stage_blocks = self.arch_settings[depth] - stage_blocks = stage_blocks[:num_stages] - assert len(strides) == len(dilations) == num_stages - assert max(out_indices) < num_stages - - self.out_indices = out_indices - self.style = style - self.frozen_stages = frozen_stages - self.bn_eval = bn_eval - self.bn_frozen = bn_frozen - self.with_cp = with_cp - - self.inplanes = 64 - self.conv1 = nn.Conv2d( - 3, 64, kernel_size=7, stride=2, padding=3, bias=False) - self.bn1 = nn.BatchNorm2d(64) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - self.res_layers = [] - for i, num_blocks in enumerate(stage_blocks): - stride = strides[i] - dilation = dilations[i] - planes = 64 * 2**i - res_layer = make_res_layer( - block, - self.inplanes, - planes, - num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - with_cp=with_cp) - self.inplanes = planes * block.expansion - layer_name = f'layer{i + 1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self.feat_dim = block.expansion * 64 * 2**(len(stage_blocks) - 1) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m) - elif isinstance(m, nn.BatchNorm2d): - constant_init(m, 1) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - if len(outs) == 1: - return outs[0] - else: - return tuple(outs) - - def train(self, mode=True): - super(ResNet, self).train(mode) - if self.bn_eval: - for m in self.modules(): - if isinstance(m, nn.BatchNorm2d): - m.eval() - if self.bn_frozen: - for params in m.parameters(): - params.requires_grad = False - if mode and self.frozen_stages >= 0: - for param in self.conv1.parameters(): - param.requires_grad = False - for param in self.bn1.parameters(): - param.requires_grad = False - self.bn1.eval() - self.bn1.weight.requires_grad = False - self.bn1.bias.requires_grad = False - for i in range(1, self.frozen_stages + 1): - mod = getattr(self, f'layer{i}') - mod.eval() - for param in mod.parameters(): - param.requires_grad = False diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/__init__.py deleted file mode 100644 index a263e31c1..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .flops_counter import get_model_complexity_info -from .fuse_conv_bn import fuse_conv_bn -from .sync_bn import revert_sync_batchnorm -from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, - KaimingInit, NormalInit, PretrainedInit, - TruncNormalInit, UniformInit, XavierInit, - bias_init_with_prob, caffe2_xavier_init, - constant_init, initialize, kaiming_init, normal_init, - trunc_normal_init, uniform_init, xavier_init) - -__all__ = [ - 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', - 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', - 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'revert_sync_batchnorm' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/flops_counter.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/flops_counter.py deleted file mode 100644 index dceeb398b..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/flops_counter.py +++ /dev/null @@ -1,599 +0,0 @@ -# Modified from flops-counter.pytorch by Vladislav Sovrasov -# original repo: https://github.com/sovrasov/flops-counter.pytorch - -# MIT License - -# Copyright (c) 2018 Vladislav Sovrasov - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -from functools import partial - -import numpy as np -import torch -import torch.nn as nn - -import mmcv - - -def get_model_complexity_info(model, - input_shape, - print_per_layer_stat=True, - as_strings=True, - input_constructor=None, - flush=False, - ost=sys.stdout): - """Get complexity information of a model. - - This method can calculate FLOPs and parameter counts of a model with - corresponding input shape. It can also print complexity information for - each layer in a model. - - Supported layers are listed as below: - - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. - - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, ``nn.LeakyReLU``, - ``nn.ReLU6``. - - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, - ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, - ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, - ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, - ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. - - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, - ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, - ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. - - Linear: ``nn.Linear``. - - Deconvolution: ``nn.ConvTranspose2d``. - - Upsample: ``nn.Upsample``. - - Args: - model (nn.Module): The model for complexity calculation. - input_shape (tuple): Input shape used for calculation. - print_per_layer_stat (bool): Whether to print complexity information - for each layer in a model. Default: True. - as_strings (bool): Output FLOPs and params counts in a string form. - Default: True. - input_constructor (None | callable): If specified, it takes a callable - method that generates input. otherwise, it will generate a random - tensor with input shape to calculate FLOPs. Default: None. - flush (bool): same as that in :func:`print`. Default: False. - ost (stream): same as ``file`` param in :func:`print`. - Default: sys.stdout. - - Returns: - tuple[float | str]: If ``as_strings`` is set to True, it will return - FLOPs and parameter counts in a string format. otherwise, it will - return those in a float number format. - """ - assert type(input_shape) is tuple - assert len(input_shape) >= 1 - assert isinstance(model, nn.Module) - flops_model = add_flops_counting_methods(model) - flops_model.eval() - flops_model.start_flops_count() - if input_constructor: - input = input_constructor(input_shape) - _ = flops_model(**input) - else: - try: - batch = torch.ones(()).new_empty( - (1, *input_shape), - dtype=next(flops_model.parameters()).dtype, - device=next(flops_model.parameters()).device) - except StopIteration: - # Avoid StopIteration for models which have no parameters, - # like `nn.Relu()`, `nn.AvgPool2d`, etc. - batch = torch.ones(()).new_empty((1, *input_shape)) - - _ = flops_model(batch) - - flops_count, params_count = flops_model.compute_average_flops_cost() - if print_per_layer_stat: - print_model_with_flops( - flops_model, flops_count, params_count, ost=ost, flush=flush) - flops_model.stop_flops_count() - - if as_strings: - return flops_to_string(flops_count), params_to_string(params_count) - - return flops_count, params_count - - -def flops_to_string(flops, units='GFLOPs', precision=2): - """Convert FLOPs number into a string. - - Note that Here we take a multiply-add counts as one FLOP. - - Args: - flops (float): FLOPs number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', - 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically - choose the most suitable unit for FLOPs. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted FLOPs number with units. - - Examples: - >>> flops_to_string(1e9) - '1.0 GFLOPs' - >>> flops_to_string(2e5, 'MFLOPs') - '0.2 MFLOPs' - >>> flops_to_string(3e-9, None) - '3e-09 FLOPs' - """ - if units is None: - if flops // 10**9 > 0: - return str(round(flops / 10.**9, precision)) + ' GFLOPs' - elif flops // 10**6 > 0: - return str(round(flops / 10.**6, precision)) + ' MFLOPs' - elif flops // 10**3 > 0: - return str(round(flops / 10.**3, precision)) + ' KFLOPs' - else: - return str(flops) + ' FLOPs' - else: - if units == 'GFLOPs': - return str(round(flops / 10.**9, precision)) + ' ' + units - elif units == 'MFLOPs': - return str(round(flops / 10.**6, precision)) + ' ' + units - elif units == 'KFLOPs': - return str(round(flops / 10.**3, precision)) + ' ' + units - else: - return str(flops) + ' FLOPs' - - -def params_to_string(num_params, units=None, precision=2): - """Convert parameter number into a string. - - Args: - num_params (float): Parameter number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'M', - 'K' and ''. If set to None, it will automatically choose the most - suitable unit for Parameter number. Default: None. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted parameter number with units. - - Examples: - >>> params_to_string(1e9) - '1000.0 M' - >>> params_to_string(2e5) - '200.0 k' - >>> params_to_string(3e-9) - '3e-09' - """ - if units is None: - if num_params // 10**6 > 0: - return str(round(num_params / 10**6, precision)) + ' M' - elif num_params // 10**3: - return str(round(num_params / 10**3, precision)) + ' k' - else: - return str(num_params) - else: - if units == 'M': - return str(round(num_params / 10.**6, precision)) + ' ' + units - elif units == 'K': - return str(round(num_params / 10.**3, precision)) + ' ' + units - else: - return str(num_params) - - -def print_model_with_flops(model, - total_flops, - total_params, - units='GFLOPs', - precision=3, - ost=sys.stdout, - flush=False): - """Print a model with FLOPs for each layer. - - Args: - model (nn.Module): The model to be printed. - total_flops (float): Total FLOPs of the model. - total_params (float): Total parameter counts of the model. - units (str | None): Converted FLOPs units. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 3. - ost (stream): same as `file` param in :func:`print`. - Default: sys.stdout. - flush (bool): same as that in :func:`print`. Default: False. - - Example: - >>> class ExampleModel(nn.Module): - - >>> def __init__(self): - >>> super().__init__() - >>> self.conv1 = nn.Conv2d(3, 8, 3) - >>> self.conv2 = nn.Conv2d(8, 256, 3) - >>> self.conv3 = nn.Conv2d(256, 8, 3) - >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) - >>> self.flatten = nn.Flatten() - >>> self.fc = nn.Linear(8, 1) - - >>> def forward(self, x): - >>> x = self.conv1(x) - >>> x = self.conv2(x) - >>> x = self.conv3(x) - >>> x = self.avg_pool(x) - >>> x = self.flatten(x) - >>> x = self.fc(x) - >>> return x - - >>> model = ExampleModel() - >>> x = (3, 16, 16) - to print the complexity information state for each layer, you can use - >>> get_model_complexity_info(model, x) - or directly use - >>> print_model_with_flops(model, 4579784.0, 37361) - ExampleModel( - 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, - (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 - (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) - (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) - (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) - (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) - (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) - ) - """ - - def accumulate_params(self): - if is_supported_instance(self): - return self.__params__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_params() - return sum - - def accumulate_flops(self): - if is_supported_instance(self): - return self.__flops__ / model.__batch_counter__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_flops() - return sum - - def flops_repr(self): - accumulated_num_params = self.accumulate_params() - accumulated_flops_cost = self.accumulate_flops() - return ', '.join([ - params_to_string( - accumulated_num_params, units='M', precision=precision), - '{:.3%} Params'.format(accumulated_num_params / total_params), - flops_to_string( - accumulated_flops_cost, units=units, precision=precision), - '{:.3%} FLOPs'.format(accumulated_flops_cost / total_flops), - self.original_extra_repr() - ]) - - def add_extra_repr(m): - m.accumulate_flops = accumulate_flops.__get__(m) - m.accumulate_params = accumulate_params.__get__(m) - flops_extra_repr = flops_repr.__get__(m) - if m.extra_repr != flops_extra_repr: - m.original_extra_repr = m.extra_repr - m.extra_repr = flops_extra_repr - assert m.extra_repr != m.original_extra_repr - - def del_extra_repr(m): - if hasattr(m, 'original_extra_repr'): - m.extra_repr = m.original_extra_repr - del m.original_extra_repr - if hasattr(m, 'accumulate_flops'): - del m.accumulate_flops - - model.apply(add_extra_repr) - print(model, file=ost, flush=flush) - model.apply(del_extra_repr) - - -def get_model_parameters_number(model): - """Calculate parameter number of a model. - - Args: - model (nn.module): The model for parameter number calculation. - - Returns: - float: Parameter number of the model. - """ - num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - return num_params - - -def add_flops_counting_methods(net_main_module): - # adding additional methods to the existing module object, - # this is done this way so that each function has access to self object - net_main_module.start_flops_count = start_flops_count.__get__( - net_main_module) - net_main_module.stop_flops_count = stop_flops_count.__get__( - net_main_module) - net_main_module.reset_flops_count = reset_flops_count.__get__( - net_main_module) - net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # noqa: E501 - net_main_module) - - net_main_module.reset_flops_count() - - return net_main_module - - -def compute_average_flops_cost(self): - """Compute average FLOPs cost. - - A method to compute average FLOPs cost, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - - Returns: - float: Current mean flops consumption per image. - """ - batches_count = self.__batch_counter__ - flops_sum = 0 - for module in self.modules(): - if is_supported_instance(module): - flops_sum += module.__flops__ - params_sum = get_model_parameters_number(self) - return flops_sum / batches_count, params_sum - - -def start_flops_count(self): - """Activate the computation of mean flops consumption per image. - - A method to activate the computation of mean flops consumption per image. - which will be available after ``add_flops_counting_methods()`` is called on - a desired net object. It should be called before running the network. - """ - add_batch_counter_hook_function(self) - - def add_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - return - - else: - handle = module.register_forward_hook( - get_modules_mapping()[type(module)]) - - module.__flops_handle__ = handle - - self.apply(partial(add_flops_counter_hook_function)) - - -def stop_flops_count(self): - """Stop computing the mean flops consumption per image. - - A method to stop computing the mean flops consumption per image, which will - be available after ``add_flops_counting_methods()`` is called on a desired - net object. It can be called to pause the computation whenever. - """ - remove_batch_counter_hook_function(self) - self.apply(remove_flops_counter_hook_function) - - -def reset_flops_count(self): - """Reset statistics computed so far. - - A method to Reset computed statistics, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - """ - add_batch_counter_variables_or_reset(self) - self.apply(add_flops_counter_variable_or_reset) - - -# ---- Internal functions -def empty_flops_counter_hook(module, input, output): - module.__flops__ += 0 - - -def upsample_flops_counter_hook(module, input, output): - output_size = output[0] - batch_size = output_size.shape[0] - output_elements_count = batch_size - for val in output_size.shape[1:]: - output_elements_count *= val - module.__flops__ += int(output_elements_count) - - -def relu_flops_counter_hook(module, input, output): - active_elements_count = output.numel() - module.__flops__ += int(active_elements_count) - - -def linear_flops_counter_hook(module, input, output): - input = input[0] - output_last_dim = output.shape[ - -1] # pytorch checks dimensions, so here we don't care much - module.__flops__ += int(np.prod(input.shape) * output_last_dim) - - -def pool_flops_counter_hook(module, input, output): - input = input[0] - module.__flops__ += int(np.prod(input.shape)) - - -def norm_flops_counter_hook(module, input, output): - input = input[0] - - batch_flops = np.prod(input.shape) - if (getattr(module, 'affine', False) - or getattr(module, 'elementwise_affine', False)): - batch_flops *= 2 - module.__flops__ += int(batch_flops) - - -def deconv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - input_height, input_width = input.shape[2:] - - kernel_height, kernel_width = conv_module.kernel_size - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = ( - kernel_height * kernel_width * in_channels * filters_per_channel) - - active_elements_count = batch_size * input_height * input_width - overall_conv_flops = conv_per_position_flops * active_elements_count - bias_flops = 0 - if conv_module.bias is not None: - output_height, output_width = output.shape[2:] - bias_flops = out_channels * batch_size * output_height * output_height - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def conv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - output_dims = list(output.shape[2:]) - - kernel_dims = list(conv_module.kernel_size) - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = int( - np.prod(kernel_dims)) * in_channels * filters_per_channel - - active_elements_count = batch_size * int(np.prod(output_dims)) - - overall_conv_flops = conv_per_position_flops * active_elements_count - - bias_flops = 0 - - if conv_module.bias is not None: - - bias_flops = out_channels * active_elements_count - - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def batch_counter_hook(module, input, output): - batch_size = 1 - if len(input) > 0: - # Can have multiple inputs, getting the first one - input = input[0] - batch_size = len(input) - else: - pass - print('Warning! No positional inputs found for a module, ' - 'assuming batch size is 1.') - module.__batch_counter__ += batch_size - - -def add_batch_counter_variables_or_reset(module): - - module.__batch_counter__ = 0 - - -def add_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - return - - handle = module.register_forward_hook(batch_counter_hook) - module.__batch_counter_handle__ = handle - - -def remove_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - module.__batch_counter_handle__.remove() - del module.__batch_counter_handle__ - - -def add_flops_counter_variable_or_reset(module): - if is_supported_instance(module): - if hasattr(module, '__flops__') or hasattr(module, '__params__'): - print('Warning: variables __flops__ or __params__ are already ' - 'defined for the module' + type(module).__name__ + - ' ptflops can affect your code!') - module.__flops__ = 0 - module.__params__ = get_model_parameters_number(module) - - -def is_supported_instance(module): - if type(module) in get_modules_mapping(): - return True - return False - - -def remove_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - module.__flops_handle__.remove() - del module.__flops_handle__ - - -def get_modules_mapping(): - return { - # convolutions - nn.Conv1d: conv_flops_counter_hook, - nn.Conv2d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, - nn.Conv3d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, - # activations - nn.ReLU: relu_flops_counter_hook, - nn.PReLU: relu_flops_counter_hook, - nn.ELU: relu_flops_counter_hook, - nn.LeakyReLU: relu_flops_counter_hook, - nn.ReLU6: relu_flops_counter_hook, - # poolings - nn.MaxPool1d: pool_flops_counter_hook, - nn.AvgPool1d: pool_flops_counter_hook, - nn.AvgPool2d: pool_flops_counter_hook, - nn.MaxPool2d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, - nn.MaxPool3d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, - nn.AvgPool3d: pool_flops_counter_hook, - nn.AdaptiveMaxPool1d: pool_flops_counter_hook, - nn.AdaptiveAvgPool1d: pool_flops_counter_hook, - nn.AdaptiveMaxPool2d: pool_flops_counter_hook, - nn.AdaptiveAvgPool2d: pool_flops_counter_hook, - nn.AdaptiveMaxPool3d: pool_flops_counter_hook, - nn.AdaptiveAvgPool3d: pool_flops_counter_hook, - # normalizations - nn.BatchNorm1d: norm_flops_counter_hook, - nn.BatchNorm2d: norm_flops_counter_hook, - nn.BatchNorm3d: norm_flops_counter_hook, - nn.GroupNorm: norm_flops_counter_hook, - nn.InstanceNorm1d: norm_flops_counter_hook, - nn.InstanceNorm2d: norm_flops_counter_hook, - nn.InstanceNorm3d: norm_flops_counter_hook, - nn.LayerNorm: norm_flops_counter_hook, - # FC - nn.Linear: linear_flops_counter_hook, - mmcv.cnn.bricks.Linear: linear_flops_counter_hook, - # Upscale - nn.Upsample: upsample_flops_counter_hook, - # Deconvolution - nn.ConvTranspose2d: deconv_flops_counter_hook, - mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, - } diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/fuse_conv_bn.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/fuse_conv_bn.py deleted file mode 100644 index cb7076f80..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/fuse_conv_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def _fuse_conv_bn(conv, bn): - """Fuse conv and bn into one module. - - Args: - conv (nn.Module): Conv to be fused. - bn (nn.Module): BN to be fused. - - Returns: - nn.Module: Fused module. - """ - conv_w = conv.weight - conv_b = conv.bias if conv.bias is not None else torch.zeros_like( - bn.running_mean) - - factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) - conv.weight = nn.Parameter(conv_w * - factor.reshape([conv.out_channels, 1, 1, 1])) - conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) - return conv - - -def fuse_conv_bn(module): - """Recursively fuse conv and bn in a module. - - During inference, the functionary of batch norm layers is turned off - but only the mean and var alone channels are used, which exposes the - chance to fuse it with the preceding conv layers to save computations and - simplify network structures. - - Args: - module (nn.Module): Module to be fused. - - Returns: - nn.Module: Fused module. - """ - last_conv = None - last_conv_name = None - - for name, child in module.named_children(): - if isinstance(child, - (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): - if last_conv is None: # only fuse BN that is after Conv - continue - fused_conv = _fuse_conv_bn(last_conv, child) - module._modules[last_conv_name] = fused_conv - # To reduce changes, set BN as Identity instead of deleting it. - module._modules[name] = nn.Identity() - last_conv = None - elif isinstance(child, nn.Conv2d): - last_conv = child - last_conv_name = name - else: - fuse_conv_bn(child) - return module diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/sync_bn.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/sync_bn.py deleted file mode 100644 index 8a79ff4a4..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/sync_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -import torch - -import mmcv - - -class _BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): - """A general BatchNorm layer without input dimension check. - - Reproduced from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc - is `_check_input_dim` that is designed for tensor sanity checks. - The check has been bypassed in this class for the convenience of converting - SyncBatchNorm. - """ - - def _check_input_dim(self, input): - return - - -def revert_sync_batchnorm(module): - """Helper function to convert all `SyncBatchNorm` (SyncBN) and - `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to - `BatchNormXd` layers. - - Adapted from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - - Args: - module (nn.Module): The module containing `SyncBatchNorm` layers. - - Returns: - module_output: The converted module with `BatchNormXd` layers. - """ - module_output = module - module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] - if hasattr(mmcv, 'ops'): - module_checklist.append(mmcv.ops.SyncBatchNorm) - if isinstance(module, tuple(module_checklist)): - module_output = _BatchNormXd(module.num_features, module.eps, - module.momentum, module.affine, - module.track_running_stats) - if module.affine: - # no_grad() may not be needed here but - # just to be consistent with `convert_sync_batchnorm()` - with torch.no_grad(): - module_output.weight = module.weight - module_output.bias = module.bias - module_output.running_mean = module.running_mean - module_output.running_var = module.running_var - module_output.num_batches_tracked = module.num_batches_tracked - module_output.training = module.training - # qconfig exists in quantized models - if hasattr(module, 'qconfig'): - module_output.qconfig = module.qconfig - for name, child in module.named_children(): - module_output.add_module(name, revert_sync_batchnorm(child)) - del module - return module_output diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/weight_init.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/weight_init.py deleted file mode 100644 index e1ac999e2..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/utils/weight_init.py +++ /dev/null @@ -1,684 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings - -import numpy as np -import torch -import torch.nn as nn -from torch import Tensor - -from mmcv.utils import Registry, build_from_cfg, get_logger, print_log - -INITIALIZERS = Registry('initializer') - - -def update_init_info(module, init_info): - """Update the `_params_init_info` in the module if the value of parameters - are changed. - - Args: - module (obj:`nn.Module`): The module of PyTorch with a user-defined - attribute `_params_init_info` which records the initialization - information. - init_info (str): The string that describes the initialization. - """ - assert hasattr( - module, - '_params_init_info'), f'Can not find `_params_init_info` in {module}' - for name, param in module.named_parameters(): - - assert param in module._params_init_info, ( - f'Find a new :obj:`Parameter` ' - f'named `{name}` during executing the ' - f'`init_weights` of ' - f'`{module.__class__.__name__}`. ' - f'Please do not add or ' - f'replace parameters during executing ' - f'the `init_weights`. ') - - # The parameter has been changed during executing the - # `init_weights` of module - mean_value = param.data.mean() - if module._params_init_info[param]['tmp_mean_value'] != mean_value: - module._params_init_info[param]['init_info'] = init_info - module._params_init_info[param]['tmp_mean_value'] = mean_value - - -def constant_init(module, val, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.constant_(module.weight, val) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def xavier_init(module, gain=1, bias=0, distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.xavier_uniform_(module.weight, gain=gain) - else: - nn.init.xavier_normal_(module.weight, gain=gain) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def normal_init(module, mean=0, std=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.normal_(module.weight, mean, std) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def trunc_normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - trunc_normal_(module.weight, mean, std, a, b) # type: ignore - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) # type: ignore - - -def uniform_init(module, a=0, b=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.uniform_(module.weight, a, b) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def kaiming_init(module, - a=0, - mode='fan_out', - nonlinearity='relu', - bias=0, - distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.kaiming_uniform_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - else: - nn.init.kaiming_normal_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def caffe2_xavier_init(module, bias=0): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - kaiming_init( - module, - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - bias=bias, - distribution='uniform') - - -def bias_init_with_prob(prior_prob): - """initialize conv/fc bias value according to a given probability value.""" - bias_init = float(-np.log((1 - prior_prob) / prior_prob)) - return bias_init - - -def _get_bases_name(m): - return [b.__name__ for b in m.__class__.__bases__] - - -class BaseInit(object): - - def __init__(self, *, bias=0, bias_prob=None, layer=None): - self.wholemodule = False - if not isinstance(bias, (int, float)): - raise TypeError(f'bias must be a number, but got a {type(bias)}') - - if bias_prob is not None: - if not isinstance(bias_prob, float): - raise TypeError(f'bias_prob type must be float, \ - but got {type(bias_prob)}') - - if layer is not None: - if not isinstance(layer, (str, list)): - raise TypeError(f'layer must be a str or a list of str, \ - but got a {type(layer)}') - else: - layer = [] - - if bias_prob is not None: - self.bias = bias_init_with_prob(bias_prob) - else: - self.bias = bias - self.layer = [layer] if isinstance(layer, str) else layer - - def _get_init_info(self): - info = f'{self.__class__.__name__}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Constant') -class ConstantInit(BaseInit): - """Initialize module parameters with constant values. - - Args: - val (int | float): the value to fill the weights in the module with - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, val, **kwargs): - super().__init__(**kwargs) - self.val = val - - def __call__(self, module): - - def init(m): - if self.wholemodule: - constant_init(m, self.val, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - constant_init(m, self.val, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Xavier') -class XavierInit(BaseInit): - r"""Initialize module parameters with values according to the method - described in `Understanding the difficulty of training deep feedforward - neural networks - Glorot, X. & Bengio, Y. (2010). - `_ - - Args: - gain (int | float): an optional scaling factor. Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` - or ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, gain=1, distribution='normal', **kwargs): - super().__init__(**kwargs) - self.gain = gain - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - xavier_init(m, self.gain, self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - xavier_init(m, self.gain, self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: gain={self.gain}, ' \ - f'distribution={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Normal') -class NormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. - - Args: - mean (int | float):the mean of the normal distribution. Defaults to 0. - std (int | float): the standard deviation of the normal distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, mean=0, std=1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.std = std - - def __call__(self, module): - - def init(m): - if self.wholemodule: - normal_init(m, self.mean, self.std, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - normal_init(m, self.mean, self.std, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: mean={self.mean},' \ - f' std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='TruncNormal') -class TruncNormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values - outside :math:`[a, b]`. - - Args: - mean (float): the mean of the normal distribution. Defaults to 0. - std (float): the standard deviation of the normal distribution. - Defaults to 1. - a (float): The minimum cutoff value. - b ( float): The maximum cutoff value. - bias (float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - **kwargs) -> None: - super().__init__(**kwargs) - self.mean = mean - self.std = std - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ - f' mean={self.mean}, std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Uniform') -class UniformInit(BaseInit): - r"""Initialize module parameters with values drawn from the uniform - distribution :math:`\mathcal{U}(a, b)`. - - Args: - a (int | float): the lower bound of the uniform distribution. - Defaults to 0. - b (int | float): the upper bound of the uniform distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, a=0, b=1, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def __call__(self, module): - - def init(m): - if self.wholemodule: - uniform_init(m, self.a, self.b, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - uniform_init(m, self.a, self.b, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a},' \ - f' b={self.b}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Kaiming') -class KaimingInit(BaseInit): - r"""Initialize module parameters with the values according to the method - described in `Delving deep into rectifiers: Surpassing human-level - performance on ImageNet classification - He, K. et al. (2015). - `_ - - Args: - a (int | float): the negative slope of the rectifier used after this - layer (only used with ``'leaky_relu'``). Defaults to 0. - mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing - ``'fan_in'`` preserves the magnitude of the variance of the weights - in the forward pass. Choosing ``'fan_out'`` preserves the - magnitudes in the backwards pass. Defaults to ``'fan_out'``. - nonlinearity (str): the non-linear function (`nn.functional` name), - recommended to use only with ``'relu'`` or ``'leaky_relu'`` . - Defaults to 'relu'. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` or - ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - a=0, - mode='fan_out', - nonlinearity='relu', - distribution='normal', - **kwargs): - super().__init__(**kwargs) - self.a = a - self.mode = mode - self.nonlinearity = nonlinearity - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ - f'nonlinearity={self.nonlinearity}, ' \ - f'distribution ={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Caffe2Xavier') -class Caffe2XavierInit(KaimingInit): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - def __init__(self, **kwargs): - super().__init__( - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='uniform', - **kwargs) - - def __call__(self, module): - super().__call__(module) - - -@INITIALIZERS.register_module(name='Pretrained') -class PretrainedInit(object): - """Initialize module by loading a pretrained model. - - Args: - checkpoint (str): the checkpoint file of the pretrained model should - be load. - prefix (str, optional): the prefix of a sub-module in the pretrained - model. it is for loading a part of the pretrained model to - initialize. For example, if we would like to only load the - backbone of a detector model, we can set ``prefix='backbone.'``. - Defaults to None. - map_location (str): map tensors into proper locations. - """ - - def __init__(self, checkpoint, prefix=None, map_location=None): - self.checkpoint = checkpoint - self.prefix = prefix - self.map_location = map_location - - def __call__(self, module): - from mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, - load_state_dict) - logger = get_logger('mmcv') - if self.prefix is None: - print_log(f'load model from: {self.checkpoint}', logger=logger) - load_checkpoint( - module, - self.checkpoint, - map_location=self.map_location, - strict=False, - logger=logger) - else: - print_log( - f'load {self.prefix} in model from: {self.checkpoint}', - logger=logger) - state_dict = _load_checkpoint_with_prefix( - self.prefix, self.checkpoint, map_location=self.map_location) - load_state_dict(module, state_dict, strict=False, logger=logger) - - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: load from {self.checkpoint}' - return info - - -def _initialize(module, cfg, wholemodule=False): - func = build_from_cfg(cfg, INITIALIZERS) - # wholemodule flag is for override mode, there is no layer key in override - # and initializer will give init values for the whole module with the name - # in override. - func.wholemodule = wholemodule - func(module) - - -def _initialize_override(module, override, cfg): - if not isinstance(override, (dict, list)): - raise TypeError(f'override must be a dict or a list of dict, \ - but got {type(override)}') - - override = [override] if isinstance(override, dict) else override - - for override_ in override: - - cp_override = copy.deepcopy(override_) - name = cp_override.pop('name', None) - if name is None: - raise ValueError('`override` must contain the key "name",' - f'but got {cp_override}') - # if override only has name key, it means use args in init_cfg - if not cp_override: - cp_override.update(cfg) - # if override has name key and other args except type key, it will - # raise error - elif 'type' not in cp_override.keys(): - raise ValueError( - f'`override` need "type" key, but got {cp_override}') - - if hasattr(module, name): - _initialize(getattr(module, name), cp_override, wholemodule=True) - else: - raise RuntimeError(f'module did not have attribute {name}, ' - f'but init_cfg is {cp_override}.') - - -def initialize(module, init_cfg): - """Initialize a module. - - Args: - module (``torch.nn.Module``): the module will be initialized. - init_cfg (dict | list[dict]): initialization configuration dict to - define initializer. OpenMMLab has implemented 6 initializers - including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, - ``Kaiming``, and ``Pretrained``. - Example: - >>> module = nn.Linear(2, 3, bias=True) - >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) - >>> initialize(module, init_cfg) - - >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) - >>> # define key ``'layer'`` for initializing layer with different - >>> # configuration - >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), - dict(type='Constant', layer='Linear', val=2)] - >>> initialize(module, init_cfg) - - >>> # define key``'override'`` to initialize some specific part in - >>> # module - >>> class FooNet(nn.Module): - >>> def __init__(self): - >>> super().__init__() - >>> self.feat = nn.Conv2d(3, 16, 3) - >>> self.reg = nn.Conv2d(16, 10, 3) - >>> self.cls = nn.Conv2d(16, 5, 3) - >>> model = FooNet() - >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', - >>> override=dict(type='Constant', name='reg', val=3, bias=4)) - >>> initialize(model, init_cfg) - - >>> model = ResNet(depth=50) - >>> # Initialize weights with the pretrained model. - >>> init_cfg = dict(type='Pretrained', - checkpoint='torchvision://resnet50') - >>> initialize(model, init_cfg) - - >>> # Initialize weights of a sub-module with the specific part of - >>> # a pretrained model by using "prefix". - >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ - >>> 'retinanet_r50_fpn_1x_coco/'\ - >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' - >>> init_cfg = dict(type='Pretrained', - checkpoint=url, prefix='backbone.') - """ - if not isinstance(init_cfg, (dict, list)): - raise TypeError(f'init_cfg must be a dict or a list of dict, \ - but got {type(init_cfg)}') - - if isinstance(init_cfg, dict): - init_cfg = [init_cfg] - - for cfg in init_cfg: - # should deeply copy the original config because cfg may be used by - # other modules, e.g., one init_cfg shared by multiple bottleneck - # blocks, the expected cfg will be changed after pop and will change - # the initialization behavior of other modules - cp_cfg = copy.deepcopy(cfg) - override = cp_cfg.pop('override', None) - _initialize(module, cp_cfg) - - if override is not None: - cp_cfg.pop('layer', None) - _initialize_override(module, override, cp_cfg) - else: - # All attributes in module have same initialization. - pass - - -def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, - b: float) -> Tensor: - # Method based on - # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf - # Modified from - # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - def norm_cdf(x): - # Computes standard normal cumulative distribution function - return (1. + math.erf(x / math.sqrt(2.))) / 2. - - if (mean < a - 2 * std) or (mean > b + 2 * std): - warnings.warn( - 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' - 'The distribution of values may be incorrect.', - stacklevel=2) - - with torch.no_grad(): - # Values are generated by using a truncated uniform distribution and - # then using the inverse CDF for the normal distribution. - # Get upper and lower cdf values - lower = norm_cdf((a - mean) / std) - upper = norm_cdf((b - mean) / std) - - # Uniformly fill tensor with values from [lower, upper], then translate - # to [2lower-1, 2upper-1]. - tensor.uniform_(2 * lower - 1, 2 * upper - 1) - - # Use inverse cdf transform for normal distribution to get truncated - # standard normal - tensor.erfinv_() - - # Transform to proper mean, std - tensor.mul_(std * math.sqrt(2.)) - tensor.add_(mean) - - # Clamp to ensure it's in the proper range - tensor.clamp_(min=a, max=b) - return tensor - - -def trunc_normal_(tensor: Tensor, - mean: float = 0., - std: float = 1., - a: float = -2., - b: float = 2.) -> Tensor: - r"""Fills the input Tensor with values drawn from a truncated - normal distribution. The values are effectively drawn from the - normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` - with values outside :math:`[a, b]` redrawn until they are within - the bounds. The method used for generating the random values works - best when :math:`a \leq \text{mean} \leq b`. - - Modified from - https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - - Args: - tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. - mean (float): the mean of the normal distribution. - std (float): the standard deviation of the normal distribution. - a (float): the minimum cutoff value. - b (float): the maximum cutoff value. - """ - return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/vgg.py b/cv/instance_segmentation/solo/pytorch/mmcv/cnn/vgg.py deleted file mode 100644 index 8778b6495..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/cnn/vgg.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn - -from .utils import constant_init, kaiming_init, normal_init - - -def conv3x3(in_planes, out_planes, dilation=1): - """3x3 convolution with padding.""" - return nn.Conv2d( - in_planes, - out_planes, - kernel_size=3, - padding=dilation, - dilation=dilation) - - -def make_vgg_layer(inplanes, - planes, - num_blocks, - dilation=1, - with_bn=False, - ceil_mode=False): - layers = [] - for _ in range(num_blocks): - layers.append(conv3x3(inplanes, planes, dilation)) - if with_bn: - layers.append(nn.BatchNorm2d(planes)) - layers.append(nn.ReLU(inplace=True)) - inplanes = planes - layers.append(nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=ceil_mode)) - - return layers - - -class VGG(nn.Module): - """VGG backbone. - - Args: - depth (int): Depth of vgg, from {11, 13, 16, 19}. - with_bn (bool): Use BatchNorm or not. - num_classes (int): number of classes for classification. - num_stages (int): VGG stages, normally 5. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze - running stats (mean and var). - bn_frozen (bool): Whether to freeze weight and bias of BN layers. - """ - - arch_settings = { - 11: (1, 1, 2, 2, 2), - 13: (2, 2, 2, 2, 2), - 16: (2, 2, 3, 3, 3), - 19: (2, 2, 4, 4, 4) - } - - def __init__(self, - depth, - with_bn=False, - num_classes=-1, - num_stages=5, - dilations=(1, 1, 1, 1, 1), - out_indices=(0, 1, 2, 3, 4), - frozen_stages=-1, - bn_eval=True, - bn_frozen=False, - ceil_mode=False, - with_last_pool=True): - super(VGG, self).__init__() - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for vgg') - assert num_stages >= 1 and num_stages <= 5 - stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - assert len(dilations) == num_stages - assert max(out_indices) <= num_stages - - self.num_classes = num_classes - self.out_indices = out_indices - self.frozen_stages = frozen_stages - self.bn_eval = bn_eval - self.bn_frozen = bn_frozen - - self.inplanes = 3 - start_idx = 0 - vgg_layers = [] - self.range_sub_modules = [] - for i, num_blocks in enumerate(self.stage_blocks): - num_modules = num_blocks * (2 + with_bn) + 1 - end_idx = start_idx + num_modules - dilation = dilations[i] - planes = 64 * 2**i if i < 4 else 512 - vgg_layer = make_vgg_layer( - self.inplanes, - planes, - num_blocks, - dilation=dilation, - with_bn=with_bn, - ceil_mode=ceil_mode) - vgg_layers.extend(vgg_layer) - self.inplanes = planes - self.range_sub_modules.append([start_idx, end_idx]) - start_idx = end_idx - if not with_last_pool: - vgg_layers.pop(-1) - self.range_sub_modules[-1][1] -= 1 - self.module_name = 'features' - self.add_module(self.module_name, nn.Sequential(*vgg_layers)) - - if self.num_classes > 0: - self.classifier = nn.Sequential( - nn.Linear(512 * 7 * 7, 4096), - nn.ReLU(True), - nn.Dropout(), - nn.Linear(4096, 4096), - nn.ReLU(True), - nn.Dropout(), - nn.Linear(4096, num_classes), - ) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m) - elif isinstance(m, nn.BatchNorm2d): - constant_init(m, 1) - elif isinstance(m, nn.Linear): - normal_init(m, std=0.01) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - outs = [] - vgg_layers = getattr(self, self.module_name) - for i in range(len(self.stage_blocks)): - for j in range(*self.range_sub_modules[i]): - vgg_layer = vgg_layers[j] - x = vgg_layer(x) - if i in self.out_indices: - outs.append(x) - if self.num_classes > 0: - x = x.view(x.size(0), -1) - x = self.classifier(x) - outs.append(x) - if len(outs) == 1: - return outs[0] - else: - return tuple(outs) - - def train(self, mode=True): - super(VGG, self).train(mode) - if self.bn_eval: - for m in self.modules(): - if isinstance(m, nn.BatchNorm2d): - m.eval() - if self.bn_frozen: - for params in m.parameters(): - params.requires_grad = False - vgg_layers = getattr(self, self.module_name) - if mode and self.frozen_stages >= 0: - for i in range(self.frozen_stages): - for j in range(*self.range_sub_modules[i]): - mod = vgg_layers[j] - mod.eval() - for param in mod.parameters(): - param.requires_grad = False diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/engine/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/engine/__init__.py deleted file mode 100644 index 3193b7f66..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, - single_gpu_test) - -__all__ = [ - 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', - 'single_gpu_test' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/engine/test.py b/cv/instance_segmentation/solo/pytorch/mmcv/engine/test.py deleted file mode 100644 index f236b1cda..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/engine/test.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, data_loader): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - # Assume result has the same length of batch_size - # refer to https://github.com/open-mmlab/mmcv/issues/985 - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting - ``gpu_collect=True``, it encodes results to gpu tensors and use gpu - communication for results collection. On cpu mode it saves the results on - different gpus to ``tmpdir`` and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - if rank == 0: - batch_size = len(result) - batch_size_all = batch_size * world_size - if batch_size_all + prog_bar.completed > len(dataset): - batch_size_all = len(dataset) - prog_bar.completed - for _ in range(batch_size_all): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results under cpu mode. - - On cpu mode, this function will save the results on different gpus to - ``tmpdir`` and collect them by the rank 0 worker. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - tmpdir (str | None): temporal directory for collected results to - store. If set to None, it will create a random temporal directory - for it. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_result = mmcv.load(part_file) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results under gpu mode. - - On gpu mode, this function will encode results to gpu tensors and use gpu - communication for results collection. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/fileio/__init__.py deleted file mode 100644 index 2051b85f7..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .file_client import BaseStorageBackend, FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler -from .io import dump, load, register_handler -from .parse import dict_from_file, list_from_file - -__all__ = [ - 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', - 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', - 'list_from_file', 'dict_from_file' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/file_client.py b/cv/instance_segmentation/solo/pytorch/mmcv/fileio/file_client.py deleted file mode 100644 index b2d622868..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/file_client.py +++ /dev/null @@ -1,1148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import os -import os.path as osp -import re -import tempfile -import warnings -from abc import ABCMeta, abstractmethod -from contextlib import contextmanager -from pathlib import Path -from typing import Iterable, Iterator, Optional, Tuple, Union -from urllib.request import urlopen - -import mmcv -from mmcv.utils.misc import has_method -from mmcv.utils.path import is_filepath - - -class BaseStorageBackend(metaclass=ABCMeta): - """Abstract class of storage backends. - - All backends need to implement two apis: ``get()`` and ``get_text()``. - ``get()`` reads the file as a byte stream and ``get_text()`` reads the file - as texts. - """ - - # a flag to indicate whether the backend can create a symlink for a file - _allow_symlink = False - - @property - def name(self): - return self.__class__.__name__ - - @property - def allow_symlink(self): - return self._allow_symlink - - @abstractmethod - def get(self, filepath): - pass - - @abstractmethod - def get_text(self, filepath): - pass - - -class CephBackend(BaseStorageBackend): - """Ceph storage backend (for internal use). - - Args: - path_mapping (dict|None): path mapping dict from local path to Petrel - path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` - will be replaced by ``dst``. Default: None. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - """ - - def __init__(self, path_mapping=None): - try: - import ceph - except ImportError: - raise ImportError('Please install ceph to enable CephBackend.') - - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead') - self._client = ceph.S3Client() - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def get(self, filepath): - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class PetrelBackend(BaseStorageBackend): - """Petrel storage backend (for internal use). - - PetrelBackend supports reading and writing data to multiple clusters. - If the file path contains the cluster name, PetrelBackend will read data - from specified cluster or write data to it. Otherwise, PetrelBackend will - access the default cluster. - - Args: - path_mapping (dict, optional): Path mapping dict from local path to - Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in - ``filepath`` will be replaced by ``dst``. Default: None. - enable_mc (bool, optional): Whether to enable memcached support. - Default: True. - - Examples: - >>> filepath1 = 's3://path/of/file' - >>> filepath2 = 'cluster-name:s3://path/of/file' - >>> client = PetrelBackend() - >>> client.get(filepath1) # get data from default cluster - >>> client.get(filepath2) # get data from 'cluster-name' cluster - """ - - def __init__(self, - path_mapping: Optional[dict] = None, - enable_mc: bool = True): - try: - from petrel_client import client - except ImportError: - raise ImportError('Please install petrel_client to enable ' - 'PetrelBackend.') - - self._client = client.Client(enable_mc=enable_mc) - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def _map_path(self, filepath: Union[str, Path]) -> str: - """Map ``filepath`` to a string path whose prefix will be replaced by - :attr:`self.path_mapping`. - - Args: - filepath (str): Path to be mapped. - """ - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - return filepath - - def _format_path(self, filepath: str) -> str: - """Convert a ``filepath`` to standard format of petrel oss. - - If the ``filepath`` is concatenated by ``os.path.join``, in a Windows - environment, the ``filepath`` will be the format of - 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the - above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. - - Args: - filepath (str): Path to be formatted. - """ - return re.sub(r'\\+', '/', filepath) - - def get(self, filepath: Union[str, Path]) -> memoryview: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - memoryview: A memory view of expected bytes object to avoid - copying. The memoryview object can be converted to bytes by - ``value_buf.tobytes()``. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return str(self.get(filepath), encoding=encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Save data to a given ``filepath``. - - Args: - obj (bytes): Data to be saved. - filepath (str or Path): Path to write data. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.put(filepath, obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Save data to a given ``filepath``. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to encode the ``obj``. - Default: 'utf-8'. - """ - self.put(bytes(obj, encoding=encoding), filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - if not has_method(self._client, 'delete'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `delete` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.delete(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - if not (has_method(self._client, 'contains') - and has_method(self._client, 'isdir')): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` and `isdir` methods, please use a higher' - 'version or dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) or self._client.isdir(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - if not has_method(self._client, 'isdir'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `isdir` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - if not has_method(self._client, 'contains'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` method, please use a higher version or ' - 'dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result after concatenation. - """ - filepath = self._format_path(self._map_path(filepath)) - if filepath.endswith('/'): - filepath = filepath[:-1] - formatted_paths = [filepath] - for path in filepaths: - formatted_paths.append(self._format_path(self._map_path(path))) - return '/'.join(formatted_paths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download a file from ``filepath`` and return a temporary path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str | Path): Download a file from ``filepath``. - - Examples: - >>> client = PetrelBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('s3://path/of/your/file') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one temporary path. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - assert self.isfile(filepath) - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - Petrel has no concept of directories but it simulates the directory - hierarchy in the filesystem through public prefixes. In addition, - if the returned path ends with '/', it means the path is a public - prefix which is a logical directory. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - In addition, the returned path of directory will not contains the - suffix '/' which is consistent with other backends. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if not has_method(self._client, 'list'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `list` method, please use a higher version or dev' - ' branch instead.')) - - dir_path = self._map_path(dir_path) - dir_path = self._format_path(dir_path) - if list_dir and suffix is not None: - raise TypeError( - '`list_dir` should be False when `suffix` is not None') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - # Petrel's simulated directory hierarchy assumes that directory paths - # should end with `/` - if not dir_path.endswith('/'): - dir_path += '/' - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for path in self._client.list(dir_path): - # the `self.isdir` is not used here to determine whether path - # is a directory, because `self.isdir` relies on - # `self._client.list` - if path.endswith('/'): # a directory path - next_dir_path = self.join_path(dir_path, path) - if list_dir: - # get the relative path and exclude the last - # character '/' - rel_dir = next_dir_path[len(root):-1] - yield rel_dir - if recursive: - yield from _list_dir_or_file(next_dir_path, list_dir, - list_file, suffix, - recursive) - else: # a file path - absolute_path = self.join_path(dir_path, path) - rel_path = absolute_path[len(root):] - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class MemcachedBackend(BaseStorageBackend): - """Memcached storage backend. - - Attributes: - server_list_cfg (str): Config file for memcached server list. - client_cfg (str): Config file for memcached client. - sys_path (str | None): Additional path to be appended to `sys.path`. - Default: None. - """ - - def __init__(self, server_list_cfg, client_cfg, sys_path=None): - if sys_path is not None: - import sys - sys.path.append(sys_path) - try: - import mc - except ImportError: - raise ImportError( - 'Please install memcached to enable MemcachedBackend.') - - self.server_list_cfg = server_list_cfg - self.client_cfg = client_cfg - self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, - self.client_cfg) - # mc.pyvector servers as a point which points to a memory cache - self._mc_buffer = mc.pyvector() - - def get(self, filepath): - filepath = str(filepath) - import mc - self._client.Get(filepath, self._mc_buffer) - value_buf = mc.ConvertBuffer(self._mc_buffer) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class LmdbBackend(BaseStorageBackend): - """Lmdb storage backend. - - Args: - db_path (str): Lmdb database path. - readonly (bool, optional): Lmdb environment parameter. If True, - disallow any write operations. Default: True. - lock (bool, optional): Lmdb environment parameter. If False, when - concurrent access occurs, do not lock the database. Default: False. - readahead (bool, optional): Lmdb environment parameter. If False, - disable the OS filesystem readahead mechanism, which may improve - random read performance when a database is larger than RAM. - Default: False. - - Attributes: - db_path (str): Lmdb database path. - """ - - def __init__(self, - db_path, - readonly=True, - lock=False, - readahead=False, - **kwargs): - try: - import lmdb - except ImportError: - raise ImportError('Please install lmdb to enable LmdbBackend.') - - self.db_path = str(db_path) - self._client = lmdb.open( - self.db_path, - readonly=readonly, - lock=lock, - readahead=readahead, - **kwargs) - - def get(self, filepath): - """Get values according to the filepath. - - Args: - filepath (str | obj:`Path`): Here, filepath is the lmdb key. - """ - filepath = str(filepath) - with self._client.begin(write=False) as txn: - value_buf = txn.get(filepath.encode('ascii')) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class HardDiskBackend(BaseStorageBackend): - """Raw hard disks storage backend.""" - - _allow_symlink = True - - def get(self, filepath: Union[str, Path]) -> bytes: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes: Expected bytes object. - """ - with open(filepath, 'rb') as f: - value_buf = f.read() - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - with open(filepath, 'r', encoding=encoding) as f: - value_buf = f.read() - return value_buf - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` will create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'wb') as f: - f.write(obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` will create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'w', encoding=encoding) as f: - f.write(obj) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - os.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return osp.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return osp.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return osp.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return osp.join(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, filepath: Union[str, Path]) -> Iterable[Union[str, Path]]: - """Only for unified API and do nothing.""" - yield filepath - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if list_dir and suffix is not None: - raise TypeError('`suffix` should be None when `list_dir` is True') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - elif osp.isdir(entry.path): - if list_dir: - rel_dir = osp.relpath(entry.path, root) - yield rel_dir - if recursive: - yield from _list_dir_or_file(entry.path, list_dir, - list_file, suffix, - recursive) - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class HTTPBackend(BaseStorageBackend): - """HTTP and HTTPS storage bachend.""" - - def get(self, filepath): - value_buf = urlopen(filepath).read() - return value_buf - - def get_text(self, filepath, encoding='utf-8'): - value_buf = urlopen(filepath).read() - return value_buf.decode(encoding) - - @contextmanager - def get_local_path(self, filepath: str) -> Iterable[str]: - """Download a file from ``filepath``. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str): Download a file from ``filepath``. - - Examples: - >>> client = HTTPBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('http://path/of/your/file') as path: - ... # do something here - """ - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - -class FileClient: - """A general file client to access files in different backends. - - The client loads a file or text in a specified backend from its path - and returns it as a binary or text file. There are two ways to choose a - backend, the name of backend and the prefix of path. Although both of them - can be used to choose a storage backend, ``backend`` has a higher priority - that is if they are all set, the storage backend will be chosen by the - backend argument. If they are all `None`, the disk backend will be chosen. - Note that It can also register other backend accessor with a given name, - prefixes, and backend class. In addition, We use the singleton pattern to - avoid repeated object creation. If the arguments are the same, the same - object will be returned. - - Args: - backend (str, optional): The storage backend type. Options are "disk", - "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. - prefix (str, optional): The prefix of the registered storage backend. - Options are "s3", "http", "https". Default: None. - - Examples: - >>> # only set backend - >>> file_client = FileClient(backend='petrel') - >>> # only set prefix - >>> file_client = FileClient(prefix='s3') - >>> # set both backend and prefix but use backend to choose client - >>> file_client = FileClient(backend='petrel', prefix='s3') - >>> # if the arguments are the same, the same object is returned - >>> file_client1 = FileClient(backend='petrel') - >>> file_client1 is file_client - True - - Attributes: - client (:obj:`BaseStorageBackend`): The backend object. - """ - - _backends = { - 'disk': HardDiskBackend, - 'ceph': CephBackend, - 'memcached': MemcachedBackend, - 'lmdb': LmdbBackend, - 'petrel': PetrelBackend, - 'http': HTTPBackend, - } - # This collection is used to record the overridden backends, and when a - # backend appears in the collection, the singleton pattern is disabled for - # that backend, because if the singleton pattern is used, then the object - # returned will be the backend before overwriting - _overridden_backends = set() - _prefix_to_backends = { - 's3': PetrelBackend, - 'http': HTTPBackend, - 'https': HTTPBackend, - } - _overridden_prefixes = set() - - _instances = {} - - def __new__(cls, backend=None, prefix=None, **kwargs): - if backend is None and prefix is None: - backend = 'disk' - if backend is not None and backend not in cls._backends: - raise ValueError( - f'Backend {backend} is not supported. Currently supported ones' - f' are {list(cls._backends.keys())}') - if prefix is not None and prefix not in cls._prefix_to_backends: - raise ValueError( - f'prefix {prefix} is not supported. Currently supported ones ' - f'are {list(cls._prefix_to_backends.keys())}') - - # concatenate the arguments to a unique key for determining whether - # objects with the same arguments were created - arg_key = f'{backend}:{prefix}' - for key, value in kwargs.items(): - arg_key += f':{key}:{value}' - - # if a backend was overridden, it will create a new object - if (arg_key in cls._instances - and backend not in cls._overridden_backends - and prefix not in cls._overridden_prefixes): - _instance = cls._instances[arg_key] - else: - # create a new object and put it to _instance - _instance = super().__new__(cls) - if backend is not None: - _instance.client = cls._backends[backend](**kwargs) - else: - _instance.client = cls._prefix_to_backends[prefix](**kwargs) - - cls._instances[arg_key] = _instance - - return _instance - - @property - def name(self): - return self.client.name - - @property - def allow_symlink(self): - return self.client.allow_symlink - - @staticmethod - def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: - """Parse the prefix of a uri. - - Args: - uri (str | Path): Uri to be parsed that contains the file prefix. - - Examples: - >>> FileClient.parse_uri_prefix('s3://path/of/your/file') - 's3' - - Returns: - str | None: Return the prefix of uri if the uri contains '://' - else ``None``. - """ - assert is_filepath(uri) - uri = str(uri) - if '://' not in uri: - return None - else: - prefix, _ = uri.split('://') - # In the case of PetrelBackend, the prefix may contains the cluster - # name like clusterName:s3 - if ':' in prefix: - _, prefix = prefix.split(':') - return prefix - - @classmethod - def infer_client(cls, - file_client_args: Optional[dict] = None, - uri: Optional[Union[str, Path]] = None) -> 'FileClient': - """Infer a suitable file client based on the URI and arguments. - - Args: - file_client_args (dict, optional): Arguments to instantiate a - FileClient. Default: None. - uri (str | Path, optional): Uri to be parsed that contains the file - prefix. Default: None. - - Examples: - >>> uri = 's3://path/of/your/file' - >>> file_client = FileClient.infer_client(uri=uri) - >>> file_client_args = {'backend': 'petrel'} - >>> file_client = FileClient.infer_client(file_client_args) - - Returns: - FileClient: Instantiated FileClient object. - """ - assert file_client_args is not None or uri is not None - if file_client_args is None: - file_prefix = cls.parse_uri_prefix(uri) # type: ignore - return cls(prefix=file_prefix) - else: - return cls(**file_client_args) - - @classmethod - def _register_backend(cls, name, backend, force=False, prefixes=None): - if not isinstance(name, str): - raise TypeError('the backend name should be a string, ' - f'but got {type(name)}') - if not inspect.isclass(backend): - raise TypeError( - f'backend should be a class but got {type(backend)}') - if not issubclass(backend, BaseStorageBackend): - raise TypeError( - f'backend {backend} is not a subclass of BaseStorageBackend') - if not force and name in cls._backends: - raise KeyError( - f'{name} is already registered as a storage backend, ' - 'add "force=True" if you want to override it') - - if name in cls._backends and force: - cls._overridden_backends.add(name) - cls._backends[name] = backend - - if prefixes is not None: - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if prefix not in cls._prefix_to_backends: - cls._prefix_to_backends[prefix] = backend - elif (prefix in cls._prefix_to_backends) and force: - cls._overridden_prefixes.add(prefix) - cls._prefix_to_backends[prefix] = backend - else: - raise KeyError( - f'{prefix} is already registered as a storage backend,' - ' add "force=True" if you want to override it') - - @classmethod - def register_backend(cls, name, backend=None, force=False, prefixes=None): - """Register a backend to FileClient. - - This method can be used as a normal class method or a decorator. - - .. code-block:: python - - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - FileClient.register_backend('new', NewBackend) - - or - - .. code-block:: python - - @FileClient.register_backend('new') - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - Args: - name (str): The name of the registered backend. - backend (class, optional): The backend class to be registered, - which must be a subclass of :class:`BaseStorageBackend`. - When this method is used as a decorator, backend is None. - Defaults to None. - force (bool, optional): Whether to override the backend if the name - has already been registered. Defaults to False. - prefixes (str or list[str] or tuple[str], optional): The prefixes - of the registered storage backend. Default: None. - `New in version 1.3.15.` - """ - if backend is not None: - cls._register_backend( - name, backend, force=force, prefixes=prefixes) - return - - def _register(backend_cls): - cls._register_backend( - name, backend_cls, force=force, prefixes=prefixes) - return backend_cls - - return _register - - def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: - """Read data from a given ``filepath`` with 'rb' mode. - - Note: - There are two types of return values for ``get``, one is ``bytes`` - and the other is ``memoryview``. The advantage of using memoryview - is that you can avoid copying, and if you want to convert it to - ``bytes``, you can use ``.tobytes()``. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes | memoryview: Expected bytes object or a memory view of the - bytes object. - """ - return self.client.get(filepath) - - def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return self.client.get_text(filepath, encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` should create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - self.client.put(obj, filepath) - - def put_text(self, obj: str, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` should create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str, optional): The encoding format used to open the - `filepath`. Default: 'utf-8'. - """ - self.client.put_text(obj, filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str, Path): Path to be removed. - """ - self.client.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return self.client.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return self.client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return self.client.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return self.client.join_path(filepath, *filepaths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download data from ``filepath`` and write the data to local path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Note: - If the ``filepath`` is a local path, just return itself. - - .. warning:: - ``get_local_path`` is an experimental interface that may change in - the future. - - Args: - filepath (str or Path): Path to be read data. - - Examples: - >>> file_client = FileClient(prefix='s3') - >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one path. - """ - with self.client.get_local_path(str(filepath)) as local_path: - yield local_path - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, - suffix, recursive) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/__init__.py deleted file mode 100644 index aa24d9197..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseFileHandler -from .json_handler import JsonHandler -from .pickle_handler import PickleHandler -from .yaml_handler import YamlHandler - -__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/base.py b/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/base.py deleted file mode 100644 index 288878bc5..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseFileHandler(metaclass=ABCMeta): - # `str_like` is a flag to indicate whether the type of file object is - # str-like object or bytes-like object. Pickle only processes bytes-like - # objects but json only processes str-like object. If it is str-like - # object, `StringIO` will be used to process the buffer. - str_like = True - - @abstractmethod - def load_from_fileobj(self, file, **kwargs): - pass - - @abstractmethod - def dump_to_fileobj(self, obj, file, **kwargs): - pass - - @abstractmethod - def dump_to_str(self, obj, **kwargs): - pass - - def load_from_path(self, filepath, mode='r', **kwargs): - with open(filepath, mode) as f: - return self.load_from_fileobj(f, **kwargs) - - def dump_to_path(self, obj, filepath, mode='w', **kwargs): - with open(filepath, mode) as f: - self.dump_to_fileobj(obj, f, **kwargs) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/json_handler.py b/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/json_handler.py deleted file mode 100644 index 18d4f15f7..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/json_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -import numpy as np - -from .base import BaseFileHandler - - -def set_default(obj): - """Set default json values for non-serializable values. - - It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. - It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, - etc.) into plain numbers of plain python built-in types. - """ - if isinstance(obj, (set, range)): - return list(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, np.generic): - return obj.item() - raise TypeError(f'{type(obj)} is unsupported for json dump') - - -class JsonHandler(BaseFileHandler): - - def load_from_fileobj(self, file): - return json.load(file) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('default', set_default) - json.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('default', set_default) - return json.dumps(obj, **kwargs) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/pickle_handler.py b/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/pickle_handler.py deleted file mode 100644 index b37c79bed..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/pickle_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import pickle - -from .base import BaseFileHandler - - -class PickleHandler(BaseFileHandler): - - str_like = False - - def load_from_fileobj(self, file, **kwargs): - return pickle.load(file, **kwargs) - - def load_from_path(self, filepath, **kwargs): - return super(PickleHandler, self).load_from_path( - filepath, mode='rb', **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('protocol', 2) - return pickle.dumps(obj, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('protocol', 2) - pickle.dump(obj, file, **kwargs) - - def dump_to_path(self, obj, filepath, **kwargs): - super(PickleHandler, self).dump_to_path( - obj, filepath, mode='wb', **kwargs) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/yaml_handler.py b/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/yaml_handler.py deleted file mode 100644 index c5aa2eea1..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/handlers/yaml_handler.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import yaml - -try: - from yaml import CLoader as Loader, CDumper as Dumper -except ImportError: - from yaml import Loader, Dumper - -from .base import BaseFileHandler # isort:skip - - -class YamlHandler(BaseFileHandler): - - def load_from_fileobj(self, file, **kwargs): - kwargs.setdefault('Loader', Loader) - return yaml.load(file, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('Dumper', Dumper) - yaml.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('Dumper', Dumper) - return yaml.dump(obj, **kwargs) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/io.py b/cv/instance_segmentation/solo/pytorch/mmcv/fileio/io.py deleted file mode 100644 index aaefde58a..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/io.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from io import BytesIO, StringIO -from pathlib import Path - -from ..utils import is_list_of, is_str -from .file_client import FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler - -file_handlers = { - 'json': JsonHandler(), - 'yaml': YamlHandler(), - 'yml': YamlHandler(), - 'pickle': PickleHandler(), - 'pkl': PickleHandler() -} - - -def load(file, file_format=None, file_client_args=None, **kwargs): - """Load data from json/yaml/pickle files. - - This method provides a unified api for loading data from serialized files. - - Note: - In v1.3.16 and later, ``load`` supports loading data from serialized - files those can be storaged in different backends. - - Args: - file (str or :obj:`Path` or file-like object): Filename or a file-like - object. - file_format (str, optional): If not specified, the file format will be - inferred from the file extension, otherwise use the specified one. - Currently supported formats include "json", "yaml/yml" and - "pickle/pkl". - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> load('/path/of/your/file') # file is storaged in disk - >>> load('https://path/of/your/file') # file is storaged in Internet - >>> load('s3://path/of/your/file') # file is storaged in petrel - - Returns: - The content from the file. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None and is_str(file): - file_format = file.split('.')[-1] - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO(file_client.get_text(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - else: - with BytesIO(file_client.get(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - elif hasattr(file, 'read'): - obj = handler.load_from_fileobj(file, **kwargs) - else: - raise TypeError('"file" must be a filepath str or a file-object') - return obj - - -def dump(obj, file=None, file_format=None, file_client_args=None, **kwargs): - """Dump data to json/yaml/pickle strings or files. - - This method provides a unified api for dumping data as strings or to files, - and also supports custom arguments for each file format. - - Note: - In v1.3.16 and later, ``dump`` supports dumping data as strings or to - files which is saved to different backends. - - Args: - obj (any): The python object to be dumped. - file (str or :obj:`Path` or file-like object, optional): If not - specified, then the object is dumped to a str, otherwise to a file - specified by the filename or file-like object. - file_format (str, optional): Same as :func:`load`. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dump('hello world', '/path/of/your/file') # disk - >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel - - Returns: - bool: True for success, False otherwise. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None: - if is_str(file): - file_format = file.split('.')[-1] - elif file is None: - raise ValueError( - 'file_format must be specified since file is None') - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if file is None: - return handler.dump_to_str(obj, **kwargs) - elif is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put_text(f.getvalue(), file) - else: - with BytesIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put(f.getvalue(), file) - elif hasattr(file, 'write'): - handler.dump_to_fileobj(obj, file, **kwargs) - else: - raise TypeError('"file" must be a filename str or a file-object') - - -def _register_handler(handler, file_formats): - """Register a handler for some file extensions. - - Args: - handler (:obj:`BaseFileHandler`): Handler to be registered. - file_formats (str or list[str]): File formats to be handled by this - handler. - """ - if not isinstance(handler, BaseFileHandler): - raise TypeError( - f'handler must be a child of BaseFileHandler, not {type(handler)}') - if isinstance(file_formats, str): - file_formats = [file_formats] - if not is_list_of(file_formats, str): - raise TypeError('file_formats must be a str or a list of str') - for ext in file_formats: - file_handlers[ext] = handler - - -def register_handler(file_formats, **kwargs): - - def wrap(cls): - _register_handler(cls(**kwargs), file_formats) - return cls - - return wrap diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/parse.py b/cv/instance_segmentation/solo/pytorch/mmcv/fileio/parse.py deleted file mode 100644 index f60f0d611..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/fileio/parse.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from io import StringIO - -from .file_client import FileClient - - -def list_from_file(filename, - prefix='', - offset=0, - max_num=0, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a list of strings. - - Note: - In v1.3.16 and later, ``list_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a list for strings. - - Args: - filename (str): Filename. - prefix (str): The prefix to be inserted to the beginning of each item. - offset (int): The offset of lines. - max_num (int): The maximum number of lines to be read, - zeros and negatives mean no limitation. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> list_from_file('/path/of/your/file') # disk - ['hello', 'world'] - >>> list_from_file('s3://path/of/your/file') # ceph or petrel - ['hello', 'world'] - - Returns: - list[str]: A list of strings. - """ - cnt = 0 - item_list = [] - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for _ in range(offset): - f.readline() - for line in f: - if 0 < max_num <= cnt: - break - item_list.append(prefix + line.rstrip('\n\r')) - cnt += 1 - return item_list - - -def dict_from_file(filename, - key_type=str, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a dict. - - Each line of the text file will be two or more columns split by - whitespaces or tabs. The first column will be parsed as dict keys, and - the following columns will be parsed as dict values. - - Note: - In v1.3.16 and later, ``dict_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a dict. - - Args: - filename(str): Filename. - key_type(type): Type of the dict keys. str is user by default and - type conversion will be performed if specified. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dict_from_file('/path/of/your/file') # disk - {'key1': 'value1', 'key2': 'value2'} - >>> dict_from_file('s3://path/of/your/file') # ceph or petrel - {'key1': 'value1', 'key2': 'value2'} - - Returns: - dict: The parsed contents. - """ - mapping = {} - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for line in f: - items = line.rstrip('\n').split() - assert len(items) >= 2 - key = key_type(items[0]) - val = items[1:] if len(items) > 2 else items[1] - mapping[key] = val - return mapping diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/image/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/image/__init__.py deleted file mode 100644 index d0051d609..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/image/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, - gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, - rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) -from .geometric import (cutout, imcrop, imflip, imflip_, impad, - impad_to_multiple, imrescale, imresize, imresize_like, - imresize_to_multiple, imrotate, imshear, imtranslate, - rescale_size) -from .io import imfrombytes, imread, imwrite, supported_backends, use_backend -from .misc import tensor2imgs -from .photometric import (adjust_brightness, adjust_color, adjust_contrast, - adjust_lighting, adjust_sharpness, auto_contrast, - clahe, imdenormalize, imequalize, iminvert, - imnormalize, imnormalize_, lut_transform, posterize, - solarize) - -__all__ = [ - 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', - 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', - 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', - 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', - 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', - 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', - 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', - 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', - 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', - 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/image/colorspace.py b/cv/instance_segmentation/solo/pytorch/mmcv/image/colorspace.py deleted file mode 100644 index 814533952..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/image/colorspace.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def imconvert(img, src, dst): - """Convert an image from the src colorspace to dst colorspace. - - Args: - img (ndarray): The input image. - src (str): The source colorspace, e.g., 'rgb', 'hsv'. - dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. - - Returns: - ndarray: The converted image. - """ - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - out_img = cv2.cvtColor(img, code) - return out_img - - -def bgr2gray(img, keepdim=False): - """Convert a BGR image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def rgb2gray(img, keepdim=False): - """Convert a RGB image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def gray2bgr(img): - """Convert a grayscale image to BGR image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted BGR image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return out_img - - -def gray2rgb(img): - """Convert a grayscale image to RGB image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted RGB image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) - return out_img - - -def _convert_input_type_range(img): - """Convert the type and range of the input image. - - It converts the input image to np.float32 type and range of [0, 1]. - It is mainly used for pre-processing the input image in colorspace - conversion functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - (ndarray): The converted image with type of np.float32 and range of - [0, 1]. - """ - img_type = img.dtype - img = img.astype(np.float32) - if img_type == np.float32: - pass - elif img_type == np.uint8: - img /= 255. - else: - raise TypeError('The img type should be np.float32 or np.uint8, ' - f'but got {img_type}') - return img - - -def _convert_output_type_range(img, dst_type): - """Convert the type and range of the image according to dst_type. - - It converts the image to desired type and range. If `dst_type` is np.uint8, - images will be converted to np.uint8 type with range [0, 255]. If - `dst_type` is np.float32, it converts the image to np.float32 type with - range [0, 1]. - It is mainly used for post-processing images in colorspace conversion - functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The image to be converted with np.float32 type and - range [0, 255]. - dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it - converts the image to np.uint8 type with range [0, 255]. If - dst_type is np.float32, it converts the image to np.float32 type - with range [0, 1]. - - Returns: - (ndarray): The converted image with desired type and range. - """ - if dst_type not in (np.uint8, np.float32): - raise TypeError('The dst_type should be np.float32 or np.uint8, ' - f'but got {dst_type}') - if dst_type == np.uint8: - img = img.round() - else: - img /= 255. - return img.astype(dst_type) - - -def rgb2ycbcr(img, y_only=False): - """Convert a RGB image to YCbCr image. - - This function produces the same results as Matlab's `rgb2ycbcr` function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 - else: - out_img = np.matmul( - img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], - [24.966, 112.0, -18.214]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def bgr2ycbcr(img, y_only=False): - """Convert a BGR image to YCbCr image. - - The bgr version of rgb2ycbcr. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 - else: - out_img = np.matmul( - img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], - [65.481, -37.797, 112.0]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2rgb(img): - """Convert a YCbCr image to RGB image. - - This function produces the same results as Matlab's ycbcr2rgb function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted RGB image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0, -0.00153632, 0.00791071], - [0.00625893, -0.00318811, 0]]) * 255.0 + [ - -222.921, 135.576, -276.836 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2bgr(img): - """Convert a YCbCr image to BGR image. - - The bgr version of ycbcr2rgb. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted BGR image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0.00791071, -0.00153632, 0], - [0, -0.00318811, 0.00625893]]) * 255.0 + [ - -276.836, 135.576, -222.921 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def convert_color_factory(src, dst): - - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - - def convert_color(img): - out_img = cv2.cvtColor(img, code) - return out_img - - convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} - image. - - Args: - img (ndarray or str): The input image. - - Returns: - ndarray: The converted {dst.upper()} image. - """ - - return convert_color - - -bgr2rgb = convert_color_factory('bgr', 'rgb') - -rgb2bgr = convert_color_factory('rgb', 'bgr') - -bgr2hsv = convert_color_factory('bgr', 'hsv') - -hsv2bgr = convert_color_factory('hsv', 'bgr') - -bgr2hls = convert_color_factory('bgr', 'hls') - -hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/image/geometric.py b/cv/instance_segmentation/solo/pytorch/mmcv/image/geometric.py deleted file mode 100644 index cf97c201c..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/image/geometric.py +++ /dev/null @@ -1,728 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers - -import cv2 -import numpy as np - -from ..utils import to_2tuple -from .io import imread_backend - -try: - from PIL import Image -except ImportError: - Image = None - - -def _scale_size(size, scale): - """Rescale a size by a ratio. - - Args: - size (tuple[int]): (w, h). - scale (float | tuple(float)): Scaling factor. - - Returns: - tuple[int]: scaled size. - """ - if isinstance(scale, (float, int)): - scale = (scale, scale) - w, h = size - return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) - - -cv2_interp_codes = { - 'nearest': cv2.INTER_NEAREST, - 'bilinear': cv2.INTER_LINEAR, - 'bicubic': cv2.INTER_CUBIC, - 'area': cv2.INTER_AREA, - 'lanczos': cv2.INTER_LANCZOS4 -} - -if Image is not None: - pillow_interp_codes = { - 'nearest': Image.NEAREST, - 'bilinear': Image.BILINEAR, - 'bicubic': Image.BICUBIC, - 'box': Image.BOX, - 'lanczos': Image.LANCZOS, - 'hamming': Image.HAMMING - } - - -def imresize(img, - size, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image to a given size. - - Args: - img (ndarray): The input image. - size (tuple[int]): Target size (w, h). - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if backend is None: - backend = imread_backend - if backend not in ['cv2', 'pillow']: - raise ValueError(f'backend: {backend} is not supported for resize.' - f"Supported backends are 'cv2', 'pillow'") - - if backend == 'pillow': - assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' - pil_image = Image.fromarray(img) - pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) - resized_img = np.array(pil_image) - else: - resized_img = cv2.resize( - img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) - if not return_scale: - return resized_img - else: - w_scale = size[0] / w - h_scale = size[1] / h - return resized_img, w_scale, h_scale - - -def imresize_to_multiple(img, - divisor, - size=None, - scale_factor=None, - keep_ratio=False, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image according to a given size or scale factor and then rounds - up the the resized or rescaled image size to the nearest value that can be - divided by the divisor. - - Args: - img (ndarray): The input image. - divisor (int | tuple): Resized image size will be a multiple of - divisor. If divisor is a tuple, divisor should be - (w_divisor, h_divisor). - size (None | int | tuple[int]): Target size (w, h). Default: None. - scale_factor (None | float | tuple[float]): Multiplier for spatial - size. Should match input size if it is a tuple and the 2D style is - (w_scale_factor, h_scale_factor). Default: None. - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: False. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if size is not None and scale_factor is not None: - raise ValueError('only one of size or scale_factor should be defined') - elif size is None and scale_factor is None: - raise ValueError('one of size or scale_factor should be defined') - elif size is not None: - size = to_2tuple(size) - if keep_ratio: - size = rescale_size((w, h), size, return_scale=False) - else: - size = _scale_size((w, h), scale_factor) - - divisor = to_2tuple(divisor) - size = tuple([int(np.ceil(s / d)) * d for s, d in zip(size, divisor)]) - resized_img, w_scale, h_scale = imresize( - img, - size, - return_scale=True, - interpolation=interpolation, - out=out, - backend=backend) - if return_scale: - return resized_img, w_scale, h_scale - else: - return resized_img - - -def imresize_like(img, - dst_img, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image to the same size of a given image. - - Args: - img (ndarray): The input image. - dst_img (ndarray): The target image. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = dst_img.shape[:2] - return imresize(img, (w, h), return_scale, interpolation, backend=backend) - - -def rescale_size(old_size, scale, return_scale=False): - """Calculate the new size to be rescaled to. - - Args: - old_size (tuple[int]): The old size (w, h) of image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image size. - - Returns: - tuple[int]: The new rescaled image size. - """ - w, h = old_size - if isinstance(scale, (float, int)): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - scale_factor = scale - elif isinstance(scale, tuple): - max_long_edge = max(scale) - max_short_edge = min(scale) - scale_factor = min(max_long_edge / max(h, w), - max_short_edge / min(h, w)) - else: - raise TypeError( - f'Scale must be a number or tuple of int, but got {type(scale)}') - - new_size = _scale_size((w, h), scale_factor) - - if return_scale: - return new_size, scale_factor - else: - return new_size - - -def imrescale(img, - scale, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image while keeping the aspect ratio. - - Args: - img (ndarray): The input image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - ndarray: The rescaled image. - """ - h, w = img.shape[:2] - new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) - rescaled_img = imresize( - img, new_size, interpolation=interpolation, backend=backend) - if return_scale: - return rescaled_img, scale_factor - else: - return rescaled_img - - -def imflip(img, direction='horizontal'): - """Flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image. - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return np.flip(img, axis=1) - elif direction == 'vertical': - return np.flip(img, axis=0) - else: - return np.flip(img, axis=(0, 1)) - - -def imflip_(img, direction='horizontal'): - """Inplace flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image (inplace). - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return cv2.flip(img, 1, img) - elif direction == 'vertical': - return cv2.flip(img, 0, img) - else: - return cv2.flip(img, -1, img) - - -def imrotate(img, - angle, - center=None, - scale=1.0, - border_value=0, - interpolation='bilinear', - auto_bound=False): - """Rotate an image. - - Args: - img (ndarray): Image to be rotated. - angle (float): Rotation angle in degrees, positive values mean - clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. - scale (float): Isotropic scale factor. - border_value (int): Border value. - interpolation (str): Same as :func:`resize`. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. - - Returns: - ndarray: The rotated image. - """ - if center is not None and auto_bound: - raise ValueError('`auto_bound` conflicts with `center`') - h, w = img.shape[:2] - if center is None: - center = ((w - 1) * 0.5, (h - 1) * 0.5) - assert isinstance(center, tuple) - - matrix = cv2.getRotationMatrix2D(center, -angle, scale) - if auto_bound: - cos = np.abs(matrix[0, 0]) - sin = np.abs(matrix[0, 1]) - new_w = h * sin + w * cos - new_h = h * cos + w * sin - matrix[0, 2] += (new_w - w) * 0.5 - matrix[1, 2] += (new_h - h) * 0.5 - w = int(np.round(new_w)) - h = int(np.round(new_h)) - rotated = cv2.warpAffine( - img, - matrix, (w, h), - flags=cv2_interp_codes[interpolation], - borderValue=border_value) - return rotated - - -def bbox_clip(bboxes, img_shape): - """Clip bboxes to fit the image shape. - - Args: - bboxes (ndarray): Shape (..., 4*k) - img_shape (tuple[int]): (height, width) of the image. - - Returns: - ndarray: Clipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) - cmin[0::2] = img_shape[1] - 1 - cmin[1::2] = img_shape[0] - 1 - clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) - return clipped_bboxes - - -def bbox_scaling(bboxes, scale, clip_shape=None): - """Scaling bboxes w.r.t the box center. - - Args: - bboxes (ndarray): Shape(..., 4). - scale (float): Scaling factor. - clip_shape (tuple[int], optional): If specified, bboxes that exceed the - boundary will be clipped according to the given shape (h, w). - - Returns: - ndarray: Scaled bboxes. - """ - if float(scale) == 1.0: - scaled_bboxes = bboxes.copy() - else: - w = bboxes[..., 2] - bboxes[..., 0] + 1 - h = bboxes[..., 3] - bboxes[..., 1] + 1 - dw = (w * (scale - 1)) * 0.5 - dh = (h * (scale - 1)) * 0.5 - scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) - if clip_shape is not None: - return bbox_clip(scaled_bboxes, clip_shape) - else: - return scaled_bboxes - - -def imcrop(img, bboxes, scale=1.0, pad_fill=None): - """Crop image patches. - - 3 steps: scale the bboxes -> clip bboxes -> crop and pad. - - Args: - img (ndarray): Image to be cropped. - bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. - scale (float, optional): Scale ratio of bboxes, the default value - 1.0 means no padding. - pad_fill (Number | list[Number]): Value to be filled for padding. - Default: None, which means no padding. - - Returns: - list[ndarray] | ndarray: The cropped image patches. - """ - chn = 1 if img.ndim == 2 else img.shape[2] - if pad_fill is not None: - if isinstance(pad_fill, (int, float)): - pad_fill = [pad_fill for _ in range(chn)] - assert len(pad_fill) == chn - - _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes - scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) - clipped_bbox = bbox_clip(scaled_bboxes, img.shape) - - patches = [] - for i in range(clipped_bbox.shape[0]): - x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) - if pad_fill is None: - patch = img[y1:y2 + 1, x1:x2 + 1, ...] - else: - _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) - if chn == 1: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) - else: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) - patch = np.array( - pad_fill, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - x_start = 0 if _x1 >= 0 else -_x1 - y_start = 0 if _y1 >= 0 else -_y1 - w = x2 - x1 + 1 - h = y2 - y1 + 1 - patch[y_start:y_start + h, x_start:x_start + w, - ...] = img[y1:y1 + h, x1:x1 + w, ...] - patches.append(patch) - - if bboxes.ndim == 1: - return patches[0] - else: - return patches - - -def impad(img, - *, - shape=None, - padding=None, - pad_val=0, - padding_mode='constant'): - """Pad the given image to a certain shape or pad on all sides with - specified padding mode and padding value. - - Args: - img (ndarray): Image to be padded. - shape (tuple[int]): Expected padding shape (h, w). Default: None. - padding (int or tuple[int]): Padding on each border. If a single int is - provided this is used to pad all borders. If tuple of length 2 is - provided this is the padding on left/right and top/bottom - respectively. If a tuple of length 4 is provided this is the - padding for the left, top, right and bottom borders respectively. - Default: None. Note that `shape` and `padding` can not be both - set. - pad_val (Number | Sequence[Number]): Values to be filled in padding - areas when padding_mode is 'constant'. Default: 0. - padding_mode (str): Type of padding. Should be: constant, edge, - reflect or symmetric. Default: constant. - - - constant: pads with a constant value, this value is specified - with pad_val. - - edge: pads with the last value at the edge of the image. - - reflect: pads with reflection of image without repeating the - last value on the edge. For example, padding [1, 2, 3, 4] - with 2 elements on both sides in reflect mode will result - in [3, 2, 1, 2, 3, 4, 3, 2]. - - symmetric: pads with reflection of image repeating the last - value on the edge. For example, padding [1, 2, 3, 4] with - 2 elements on both sides in symmetric mode will result in - [2, 1, 1, 2, 3, 4, 4, 3] - - Returns: - ndarray: The padded image. - """ - - assert (shape is not None) ^ (padding is not None) - if shape is not None: - padding = (0, 0, shape[1] - img.shape[1], shape[0] - img.shape[0]) - - # check pad_val - if isinstance(pad_val, tuple): - assert len(pad_val) == img.shape[-1] - elif not isinstance(pad_val, numbers.Number): - raise TypeError('pad_val must be a int or a tuple. ' - f'But received {type(pad_val)}') - - # check padding - if isinstance(padding, tuple) and len(padding) in [2, 4]: - if len(padding) == 2: - padding = (padding[0], padding[1], padding[0], padding[1]) - elif isinstance(padding, numbers.Number): - padding = (padding, padding, padding, padding) - else: - raise ValueError('Padding must be a int or a 2, or 4 element tuple.' - f'But received {padding}') - - # check padding mode - assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] - - border_type = { - 'constant': cv2.BORDER_CONSTANT, - 'edge': cv2.BORDER_REPLICATE, - 'reflect': cv2.BORDER_REFLECT_101, - 'symmetric': cv2.BORDER_REFLECT - } - img = cv2.copyMakeBorder( - img, - padding[1], - padding[3], - padding[0], - padding[2], - border_type[padding_mode], - value=pad_val) - - return img - - -def impad_to_multiple(img, divisor, pad_val=0): - """Pad an image to ensure each edge to be multiple to some number. - - Args: - img (ndarray): Image to be padded. - divisor (int): Padded image edges will be multiple to divisor. - pad_val (Number | Sequence[Number]): Same as :func:`impad`. - - Returns: - ndarray: The padded image. - """ - pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor - pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor - return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) - - -def cutout(img, shape, pad_val=0): - """Randomly cut out a rectangle from the original img. - - Args: - img (ndarray): Image to be cutout. - shape (int | tuple[int]): Expected cutout shape (h, w). If given as a - int, the value will be used for both h and w. - pad_val (int | float | tuple[int | float]): Values to be filled in the - cut area. Defaults to 0. - - Returns: - ndarray: The cutout image. - """ - - channels = 1 if img.ndim == 2 else img.shape[2] - if isinstance(shape, int): - cut_h, cut_w = shape, shape - else: - assert isinstance(shape, tuple) and len(shape) == 2, \ - f'shape must be a int or a tuple with length 2, but got type ' \ - f'{type(shape)} instead.' - cut_h, cut_w = shape - if isinstance(pad_val, (int, float)): - pad_val = tuple([pad_val] * channels) - elif isinstance(pad_val, tuple): - assert len(pad_val) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(pad_val), channels) - else: - raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') - - img_h, img_w = img.shape[:2] - y0 = np.random.uniform(img_h) - x0 = np.random.uniform(img_w) - - y1 = int(max(0, y0 - cut_h / 2.)) - x1 = int(max(0, x0 - cut_w / 2.)) - y2 = min(img_h, y1 + cut_h) - x2 = min(img_w, x1 + cut_w) - - if img.ndim == 2: - patch_shape = (y2 - y1, x2 - x1) - else: - patch_shape = (y2 - y1, x2 - x1, channels) - - img_cutout = img.copy() - patch = np.array( - pad_val, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - img_cutout[y1:y2, x1:x2, ...] = patch - - return img_cutout - - -def _get_shear_matrix(magnitude, direction='horizontal'): - """Generate the shear matrix for transformation. - - Args: - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - - Returns: - ndarray: The shear matrix with dtype float32. - """ - if direction == 'horizontal': - shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) - elif direction == 'vertical': - shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) - return shear_matrix - - -def imshear(img, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear an image. - - Args: - img (ndarray): Image to be sheared with format (h, w) - or (h, w, c). - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The sheared image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`') - shear_matrix = _get_shear_matrix(magnitude, direction) - sheared = cv2.warpAffine( - img, - shear_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. shearing masks whose channels large - # than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return sheared - - -def _get_translate_matrix(offset, direction='horizontal'): - """Generate the translate matrix. - - Args: - offset (int | float): The offset used for translate. - direction (str): The translate direction, either - "horizontal" or "vertical". - - Returns: - ndarray: The translate matrix with dtype float32. - """ - if direction == 'horizontal': - translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) - elif direction == 'vertical': - translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) - return translate_matrix - - -def imtranslate(img, - offset, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Translate an image. - - Args: - img (ndarray): Image to be translated with format - (h, w) or (h, w, c). - offset (int | float): The offset used for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The translated image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`.') - translate_matrix = _get_translate_matrix(offset, direction) - translated = cv2.warpAffine( - img, - translate_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. translating masks whose channels - # large than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return translated diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/image/io.py b/cv/instance_segmentation/solo/pytorch/mmcv/image/io.py deleted file mode 100644 index d47aaa845..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/image/io.py +++ /dev/null @@ -1,258 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os.path as osp -from pathlib import Path - -import cv2 -import numpy as np -from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, - IMREAD_UNCHANGED) - -from mmcv.utils import check_file_exist, is_str, mkdir_or_exist - -try: - from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG -except ImportError: - TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None - -try: - from PIL import Image, ImageOps -except ImportError: - Image = None - -try: - import tifffile -except ImportError: - tifffile = None - -jpeg = None -supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] - -imread_flags = { - 'color': IMREAD_COLOR, - 'grayscale': IMREAD_GRAYSCALE, - 'unchanged': IMREAD_UNCHANGED, - 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, - 'grayscale_ignore_orientation': - IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE -} - -imread_backend = 'cv2' - - -def use_backend(backend): - """Select a backend for image decoding. - - Args: - backend (str): The image decoding backend type. Options are `cv2`, - `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) - and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` - file format. - """ - assert backend in supported_backends - global imread_backend - imread_backend = backend - if imread_backend == 'turbojpeg': - if TurboJPEG is None: - raise ImportError('`PyTurboJPEG` is not installed') - global jpeg - if jpeg is None: - jpeg = TurboJPEG() - elif imread_backend == 'pillow': - if Image is None: - raise ImportError('`Pillow` is not installed') - elif imread_backend == 'tifffile': - if tifffile is None: - raise ImportError('`tifffile` is not installed') - - -def _jpegflag(flag='color', channel_order='bgr'): - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'color': - if channel_order == 'bgr': - return TJPF_BGR - elif channel_order == 'rgb': - return TJCS_RGB - elif flag == 'grayscale': - return TJPF_GRAY - else: - raise ValueError('flag must be "color" or "grayscale"') - - -def _pillow2array(img, flag='color', channel_order='bgr'): - """Convert a pillow image to numpy array. - - Args: - img (:obj:`PIL.Image.Image`): The image loaded using PIL - flag (str): Flags specifying the color type of a loaded image, - candidates are 'color', 'grayscale' and 'unchanged'. - Default to 'color'. - channel_order (str): The channel order of the output image array, - candidates are 'bgr' and 'rgb'. Default to 'bgr'. - - Returns: - np.ndarray: The converted numpy array - """ - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'unchanged': - array = np.array(img) - if array.ndim >= 3 and array.shape[2] >= 3: # color image - array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR - else: - # Handle exif orientation tag - if flag in ['color', 'grayscale']: - img = ImageOps.exif_transpose(img) - # If the image mode is not 'RGB', convert it to 'RGB' first. - if img.mode != 'RGB': - if img.mode != 'LA': - # Most formats except 'LA' can be directly converted to RGB - img = img.convert('RGB') - else: - # When the mode is 'LA', the default conversion will fill in - # the canvas with black, which sometimes shadows black objects - # in the foreground. - # - # Therefore, a random color (124, 117, 104) is used for canvas - img_rgba = img.convert('RGBA') - img = Image.new('RGB', img_rgba.size, (124, 117, 104)) - img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha - if flag in ['color', 'color_ignore_orientation']: - array = np.array(img) - if channel_order != 'rgb': - array = array[:, :, ::-1] # RGB to BGR - elif flag in ['grayscale', 'grayscale_ignore_orientation']: - img = img.convert('L') - array = np.array(img) - else: - raise ValueError( - 'flag must be "color", "grayscale", "unchanged", ' - f'"color_ignore_orientation" or "grayscale_ignore_orientation"' - f' but got {flag}') - return array - - -def imread(img_or_path, flag='color', channel_order='bgr', backend=None): - """Read an image. - - Args: - img_or_path (ndarray or str or Path): Either a numpy array or str or - pathlib.Path. If it is a numpy array (loaded image), then - it will be returned as is. - flag (str): Flags specifying the color type of a loaded image, - candidates are `color`, `grayscale`, `unchanged`, - `color_ignore_orientation` and `grayscale_ignore_orientation`. - By default, `cv2` and `pillow` backend would rotate the image - according to its EXIF info unless called with `unchanged` or - `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend - always ignore image's EXIF info regardless of the flag. - The `turbojpeg` backend only supports `color` and `grayscale`. - channel_order (str): Order of channel, candidates are `bgr` and `rgb`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. - If backend is None, the global imread_backend specified by - ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - ndarray: Loaded image array. - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError(f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow'") - if isinstance(img_or_path, Path): - img_or_path = str(img_or_path) - - if isinstance(img_or_path, np.ndarray): - return img_or_path - elif is_str(img_or_path): - check_file_exist(img_or_path, - f'img file does not exist: {img_or_path}') - if backend == 'turbojpeg': - with open(img_or_path, 'rb') as in_file: - img = jpeg.decode(in_file.read(), - _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - img = Image.open(img_or_path) - img = _pillow2array(img, flag, channel_order) - return img - elif backend == 'tifffile': - img = tifffile.imread(img_or_path) - return img - else: - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imread(img_or_path, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - else: - raise TypeError('"img" must be a numpy array or a str or ' - 'a pathlib.Path object') - - -def imfrombytes(content, flag='color', channel_order='bgr', backend=None): - """Read an image from bytes. - - Args: - content (bytes): Image bytes got from files or other streams. - flag (str): Same as :func:`imread`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `None`. If backend is None, the - global imread_backend specified by ``mmcv.use_backend()`` will be - used. Default: None. - - Returns: - ndarray: Loaded image array. - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError(f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow'") - if backend == 'turbojpeg': - img = jpeg.decode(content, _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - buff = io.BytesIO(content) - img = Image.open(buff) - img = _pillow2array(img, flag, channel_order) - return img - else: - img_np = np.frombuffer(content, np.uint8) - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imdecode(img_np, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - - -def imwrite(img, file_path, params=None, auto_mkdir=True): - """Write image to file. - - Args: - img (ndarray): Image array to be written. - file_path (str): Image file path. - params (None or list): Same as opencv :func:`imwrite` interface. - auto_mkdir (bool): If the parent folder of `file_path` does not exist, - whether to create it automatically. - - Returns: - bool: Successful or not. - """ - if auto_mkdir: - dir_name = osp.abspath(osp.dirname(file_path)) - mkdir_or_exist(dir_name) - return cv2.imwrite(file_path, img, params) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/image/misc.py b/cv/instance_segmentation/solo/pytorch/mmcv/image/misc.py deleted file mode 100644 index dfc4a9c6e..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/image/misc.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - -import mmcv - -try: - import torch -except ImportError: - torch = None - - -def tensor2imgs(tensor, mean=(0, 0, 0), std=(1, 1, 1), to_rgb=True): - """Convert tensor to 3-channel images. - - Args: - tensor (torch.Tensor): Tensor that contains multiple images, shape ( - N, C, H, W). - mean (tuple[float], optional): Mean of images. Defaults to (0, 0, 0). - std (tuple[float], optional): Standard deviation of images. - Defaults to (1, 1, 1). - to_rgb (bool, optional): Whether the tensor was converted to RGB - format in the first place. If so, convert it back to BGR. - Defaults to True. - - Returns: - list[np.ndarray]: A list that contains multiple images. - """ - - if torch is None: - raise RuntimeError('pytorch is not installed') - assert torch.is_tensor(tensor) and tensor.ndim == 4 - assert len(mean) == 3 - assert len(std) == 3 - - num_imgs = tensor.size(0) - mean = np.array(mean, dtype=np.float32) - std = np.array(std, dtype=np.float32) - imgs = [] - for img_id in range(num_imgs): - img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) - img = mmcv.imdenormalize( - img, mean, std, to_bgr=to_rgb).astype(np.uint8) - imgs.append(np.ascontiguousarray(img)) - return imgs diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/image/photometric.py b/cv/instance_segmentation/solo/pytorch/mmcv/image/photometric.py deleted file mode 100644 index 5085d0120..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/image/photometric.py +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - -from ..utils import is_tuple_of -from .colorspace import bgr2gray, gray2bgr - - -def imnormalize(img, mean, std, to_rgb=True): - """Normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - img = img.copy().astype(np.float32) - return imnormalize_(img, mean, std, to_rgb) - - -def imnormalize_(img, mean, std, to_rgb=True): - """Inplace normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - # cv2 inplace normalization does not accept uint8 - assert img.dtype != np.uint8 - mean = np.float64(mean.reshape(1, -1)) - stdinv = 1 / np.float64(std.reshape(1, -1)) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - cv2.subtract(img, mean, img) # inplace - cv2.multiply(img, stdinv, img) # inplace - return img - - -def imdenormalize(img, mean, std, to_bgr=True): - assert img.dtype != np.uint8 - mean = mean.reshape(1, -1).astype(np.float64) - std = std.reshape(1, -1).astype(np.float64) - img = cv2.multiply(img, std) # make a copy - cv2.add(img, mean, img) # inplace - if to_bgr: - cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace - return img - - -def iminvert(img): - """Invert (negate) an image. - - Args: - img (ndarray): Image to be inverted. - - Returns: - ndarray: The inverted image. - """ - return np.full_like(img, 255) - img - - -def solarize(img, thr=128): - """Solarize an image (invert all pixel values above a threshold) - - Args: - img (ndarray): Image to be solarized. - thr (int): Threshold for solarizing (0 - 255). - - Returns: - ndarray: The solarized image. - """ - img = np.where(img < thr, img, 255 - img) - return img - - -def posterize(img, bits): - """Posterize an image (reduce the number of bits for each color channel) - - Args: - img (ndarray): Image to be posterized. - bits (int): Number of bits (1 to 8) to use for posterizing. - - Returns: - ndarray: The posterized image. - """ - shift = 8 - bits - img = np.left_shift(np.right_shift(img, shift), shift) - return img - - -def adjust_color(img, alpha=1, beta=None, gamma=0): - r"""It blends the source image and its gray image: - - .. math:: - output = img * alpha + gray\_img * beta + gamma - - Args: - img (ndarray): The input source image. - alpha (int | float): Weight for the source image. Default 1. - beta (int | float): Weight for the converted gray image. - If None, it's assigned the value (1 - `alpha`). - gamma (int | float): Scalar added to each sum. - Same as :func:`cv2.addWeighted`. Default 0. - - Returns: - ndarray: Colored image which has the same size and dtype as input. - """ - gray_img = bgr2gray(img) - gray_img = np.tile(gray_img[..., None], [1, 1, 3]) - if beta is None: - beta = 1 - alpha - colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) - if not colored_img.dtype == np.uint8: - # Note when the dtype of `img` is not the default `np.uint8` - # (e.g. np.float32), the value in `colored_img` got from cv2 - # is not guaranteed to be in range [0, 255], so here clip - # is needed. - colored_img = np.clip(colored_img, 0, 255) - return colored_img - - -def imequalize(img): - """Equalize the image histogram. - - This function applies a non-linear mapping to the input image, - in order to create a uniform distribution of grayscale values - in the output image. - - Args: - img (ndarray): Image to be equalized. - - Returns: - ndarray: The equalized image. - """ - - def _scale_channel(im, c): - """Scale the data in the corresponding channel.""" - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # For computing the step, filter out the nonzeros. - nonzero_histo = histo[histo > 0] - step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 - if not step: - lut = np.array(range(256)) - else: - # Compute the cumulative sum, shifted by step // 2 - # and then normalized by step. - lut = (np.cumsum(histo) + (step // 2)) // step - # Shift lut, prepending with 0. - lut = np.concatenate([[0], lut[:-1]], 0) - # handle potential integer overflow - lut[lut > 255] = 255 - # If step is zero, return the original image. - # Otherwise, index from lut. - return np.where(np.equal(step, 0), im, lut[im]) - - # Scales each channel independently and then stacks - # the result. - s1 = _scale_channel(img, 0) - s2 = _scale_channel(img, 1) - s3 = _scale_channel(img, 2) - equalized_img = np.stack([s1, s2, s3], axis=-1) - return equalized_img.astype(img.dtype) - - -def adjust_brightness(img, factor=1.): - """Adjust image brightness. - - This function controls the brightness of an image. An - enhancement factor of 0.0 gives a black image. - A factor of 1.0 gives the original image. This function - blends the source image and the degenerated black image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be brightened. - factor (float): A value controls the enhancement. - Factor 1.0 returns the original image, lower - factors mean less color (brightness, contrast, - etc), and higher values more. Default 1. - - Returns: - ndarray: The brightened image. - """ - degenerated = np.zeros_like(img) - # Note manually convert the dtype to np.float32, to - # achieve as close results as PIL.ImageEnhance.Brightness. - # Set beta=1-factor, and gamma=0 - brightened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - brightened_img = np.clip(brightened_img, 0, 255) - return brightened_img.astype(img.dtype) - - -def adjust_contrast(img, factor=1.): - """Adjust image contrast. - - This function controls the contrast of an image. An - enhancement factor of 0.0 gives a solid grey - image. A factor of 1.0 gives the original image. It - blends the source image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be contrasted. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - - Returns: - ndarray: The contrasted image. - """ - gray_img = bgr2gray(img) - hist = np.histogram(gray_img, 256, (0, 255))[0] - mean = round(np.sum(gray_img) / np.sum(hist)) - degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) - degenerated = gray2bgr(degenerated) - contrasted_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - contrasted_img = np.clip(contrasted_img, 0, 255) - return contrasted_img.astype(img.dtype) - - -def auto_contrast(img, cutoff=0): - """Auto adjust image contrast. - - This function maximize (normalize) image contrast by first removing cutoff - percent of the lightest and darkest pixels from the histogram and remapping - the image so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - Args: - img (ndarray): Image to be contrasted. BGR order. - cutoff (int | float | tuple): The cutoff percent of the lightest and - darkest pixels to be removed. If given as tuple, it shall be - (low, high). Otherwise, the single value will be used for both. - Defaults to 0. - - Returns: - ndarray: The contrasted image. - """ - - def _auto_contrast_channel(im, c, cutoff): - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # Remove cut-off percent pixels from histo - histo_sum = np.cumsum(histo) - cut_low = histo_sum[-1] * cutoff[0] // 100 - cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 - histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low - histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) - - # Compute mapping - low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] - # If all the values have been cut off, return the origin img - if low >= high: - return im - scale = 255.0 / (high - low) - offset = -low * scale - lut = np.array(range(256)) - lut = lut * scale + offset - lut = np.clip(lut, 0, 255) - return lut[im] - - if isinstance(cutoff, (int, float)): - cutoff = (cutoff, cutoff) - else: - assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ - f'float or tuple, but got {type(cutoff)} instead.' - # Auto adjusts contrast for each channel independently and then stacks - # the result. - s1 = _auto_contrast_channel(img, 0, cutoff) - s2 = _auto_contrast_channel(img, 1, cutoff) - s3 = _auto_contrast_channel(img, 2, cutoff) - contrasted_img = np.stack([s1, s2, s3], axis=-1) - return contrasted_img.astype(img.dtype) - - -def adjust_sharpness(img, factor=1., kernel=None): - """Adjust image sharpness. - - This function controls the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image. A - factor of 1.0 gives the original image. And a factor - of 2.0 gives a sharpened image. It blends the source - image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be sharpened. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - kernel (np.ndarray, optional): Filter kernel to be applied on the img - to obtain the degenerated img. Defaults to None. - - Note: - No value sanity check is enforced on the kernel set by users. So with - an inappropriate kernel, the ``adjust_sharpness`` may fail to perform - the function its name indicates but end up performing whatever - transform determined by the kernel. - - Returns: - ndarray: The sharpened image. - """ - - if kernel is None: - # adopted from PIL.ImageFilter.SMOOTH - kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 - assert isinstance(kernel, np.ndarray), \ - f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' - assert kernel.ndim == 2, \ - f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' - - degenerated = cv2.filter2D(img, -1, kernel) - sharpened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - sharpened_img = np.clip(sharpened_img, 0, 255) - return sharpened_img.astype(img.dtype) - - -def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): - """AlexNet-style PCA jitter. - - This data augmentation is proposed in `ImageNet Classification with Deep - Convolutional Neural Networks - `_. - - Args: - img (ndarray): Image to be adjusted lighting. BGR order. - eigval (ndarray): the eigenvalue of the convariance matrix of pixel - values, respectively. - eigvec (ndarray): the eigenvector of the convariance matrix of pixel - values, respectively. - alphastd (float): The standard deviation for distribution of alpha. - Defaults to 0.1 - to_rgb (bool): Whether to convert img to rgb. - - Returns: - ndarray: The adjusted image. - """ - assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ - f'eigval and eigvec should both be of type np.ndarray, got ' \ - f'{type(eigval)} and {type(eigvec)} instead.' - - assert eigval.ndim == 1 and eigvec.ndim == 2 - assert eigvec.shape == (3, eigval.shape[0]) - n_eigval = eigval.shape[0] - assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ - f'got {type(alphastd)} instead.' - - img = img.copy().astype(np.float32) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - - alpha = np.random.normal(0, alphastd, n_eigval) - alter = eigvec \ - * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ - * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) - alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) - img_adjusted = img + alter - return img_adjusted - - -def lut_transform(img, lut_table): - """Transform array by look-up table. - - The function lut_transform fills the output array with values from the - look-up table. Indices of the entries are taken from the input array. - - Args: - img (ndarray): Image to be transformed. - lut_table (ndarray): look-up table of 256 elements; in case of - multi-channel input array, the table should either have a single - channel (in this case the same table is used for all channels) or - the same number of channels as in the input array. - - Returns: - ndarray: The transformed image. - """ - assert isinstance(img, np.ndarray) - assert 0 <= np.min(img) and np.max(img) <= 255 - assert isinstance(lut_table, np.ndarray) - assert lut_table.shape == (256, ) - - return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) - - -def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - img (ndarray): Image to be processed. - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - - Returns: - ndarray: The processed image. - """ - assert isinstance(img, np.ndarray) - assert img.ndim == 2 - assert isinstance(clip_limit, (float, int)) - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - - clahe = cv2.createCLAHE(clip_limit, tile_grid_size) - return clahe.apply(np.array(img, dtype=np.uint8)) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/model_zoo/deprecated.json b/cv/instance_segmentation/solo/pytorch/mmcv/model_zoo/deprecated.json deleted file mode 100644 index 25cf6f28c..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/model_zoo/deprecated.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "resnet50_caffe": "detectron/resnet50_caffe", - "resnet50_caffe_bgr": "detectron2/resnet50_caffe_bgr", - "resnet101_caffe": "detectron/resnet101_caffe", - "resnet101_caffe_bgr": "detectron2/resnet101_caffe_bgr" -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/model_zoo/mmcls.json b/cv/instance_segmentation/solo/pytorch/mmcv/model_zoo/mmcls.json deleted file mode 100644 index bdb311d9f..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/model_zoo/mmcls.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "vgg11": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_batch256_imagenet_20210208-4271cd6c.pth", - "vgg13": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_batch256_imagenet_20210208-4d1d6080.pth", - "vgg16": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_batch256_imagenet_20210208-db26f1a5.pth", - "vgg19": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_batch256_imagenet_20210208-e6920e4a.pth", - "vgg11_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_bn_batch256_imagenet_20210207-f244902c.pth", - "vgg13_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_bn_batch256_imagenet_20210207-1a8b7864.pth", - "vgg16_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_bn_batch256_imagenet_20210208-7e55cd29.pth", - "vgg19_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_bn_batch256_imagenet_20210208-da620c4f.pth", - "resnet18": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet18_batch256_imagenet_20200708-34ab8f90.pth", - "resnet34": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet34_batch256_imagenet_20200708-32ffb4f7.pth", - "resnet50": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_batch256_imagenet_20200708-cfb998bf.pth", - "resnet101": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet101_batch256_imagenet_20200708-753f3608.pth", - "resnet152": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet152_batch256_imagenet_20200708-ec25b1f9.pth", - "resnet50_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d50_batch256_imagenet_20200708-1ad0ce94.pth", - "resnet101_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d101_batch256_imagenet_20200708-9cb302ef.pth", - "resnet152_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d152_batch256_imagenet_20200708-e79cb6a2.pth", - "resnext50_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext50_32x4d_b32x8_imagenet_20210429-56066e27.pth", - "resnext101_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x4d_b32x8_imagenet_20210506-e0fa3dd5.pth", - "resnext101_32x8d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x8d_b32x8_imagenet_20210506-23a247d5.pth", - "resnext152_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext152_32x4d_b32x8_imagenet_20210524-927787be.pth", - "se-resnet50": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet50_batch256_imagenet_20200804-ae206104.pth", - "se-resnet101": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet101_batch256_imagenet_20200804-ba5b51d4.pth", - "resnest50": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest50_imagenet_converted-1ebf0afe.pth", - "resnest101": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest101_imagenet_converted-032caa52.pth", - "resnest200": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest200_imagenet_converted-581a60f2.pth", - "resnest269": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest269_imagenet_converted-59930960.pth", - "shufflenet_v1": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v1/shufflenet_v1_batch1024_imagenet_20200804-5d6cec73.pth", - "shufflenet_v2": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v2/shufflenet_v2_batch1024_imagenet_20200812-5bf4721e.pth", - "mobilenet_v2": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v2/mobilenet_v2_batch256_imagenet_20200708-3b2dc3af.pth" -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/model_zoo/open_mmlab.json b/cv/instance_segmentation/solo/pytorch/mmcv/model_zoo/open_mmlab.json deleted file mode 100644 index 8311db4fe..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/model_zoo/open_mmlab.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "vgg16_caffe": "https://download.openmmlab.com/pretrain/third_party/vgg16_caffe-292e1171.pth", - "detectron/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_caffe-788b5fa3.pth", - "detectron2/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_msra-5891d200.pth", - "detectron/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_caffe-3ad79236.pth", - "detectron2/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_msra-6cc46731.pth", - "detectron2/resnext101_32x8d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x8d-1516f1aa.pth", - "resnext50_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext50-32x4d-0ab1a123.pth", - "resnext101_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d-a5af3160.pth", - "resnext101_64x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_64x4d-ee2c6f71.pth", - "contrib/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_thangvubk-ad1730dd.pth", - "detectron/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn-9186a21c.pth", - "detectron/resnet101_gn": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn-cac0ab98.pth", - "jhu/resnet50_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_ws-15beedd8.pth", - "jhu/resnet101_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn_ws-3e3c308c.pth", - "jhu/resnext50_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn_ws-0d87ac85.pth", - "jhu/resnext101_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn_ws-34ac1a9e.pth", - "jhu/resnext50_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn-c7e8b754.pth", - "jhu/resnext101_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn-ac3bb84e.pth", - "msra/hrnetv2_w18_small": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18_small-b5a04e21.pth", - "msra/hrnetv2_w18": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18-00eb2006.pth", - "msra/hrnetv2_w32": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w32-dc9eeb4f.pth", - "msra/hrnetv2_w40": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w40-ed0b031c.pth", - "msra/hrnetv2_w48": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w48-d2186c55.pth", - "bninception_caffe": "https://download.openmmlab.com/pretrain/third_party/bn_inception_caffe-ed2e8665.pth", - "kin400/i3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/i3d_r50_f32s2_k400-2c57e077.pth", - "kin400/nl3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/nl3d_r50_f32s2_k400-fa7e7caa.pth", - "res2net101_v1d_26w_4s": "https://download.openmmlab.com/pretrain/third_party/res2net101_v1d_26w_4s_mmdetv2-f0a600f9.pth", - "regnetx_400mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_400mf-a5b10d96.pth", - "regnetx_800mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_800mf-1f4be4c7.pth", - "regnetx_1.6gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_1.6gf-5791c176.pth", - "regnetx_3.2gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_3.2gf-c2599b0f.pth", - "regnetx_4.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_4.0gf-a88f671e.pth", - "regnetx_6.4gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_6.4gf-006af45d.pth", - "regnetx_8.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_8.0gf-3c68abe7.pth", - "regnetx_12gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_12gf-4c2a3350.pth", - "resnet18_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet18_v1c-b5776b93.pth", - "resnet50_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet50_v1c-2cccc1ad.pth", - "resnet101_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet101_v1c-e67eebb6.pth", - "mmedit/vgg16": "https://download.openmmlab.com/mmediting/third_party/vgg_state_dict.pth", - "mmedit/res34_en_nomixup": "https://download.openmmlab.com/mmediting/third_party/model_best_resnet34_En_nomixup.pth", - "mmedit/mobilenet_v2": "https://download.openmmlab.com/mmediting/third_party/mobilenet_v2.pth", - "contrib/mobilenet_v3_large": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_large-bc2c3fd3.pth", - "contrib/mobilenet_v3_small": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_small-47085aa1.pth", - "resnest50": "https://download.openmmlab.com/pretrain/third_party/resnest50_d2-7497a55b.pth", - "resnest101": "https://download.openmmlab.com/pretrain/third_party/resnest101_d2-f3b931b2.pth", - "resnest200": "https://download.openmmlab.com/pretrain/third_party/resnest200_d2-ca88e41f.pth", - "darknet53": "https://download.openmmlab.com/pretrain/third_party/darknet53-a628ea1b.pth", - "mmdet/mobilenet_v2": "https://download.openmmlab.com/mmdetection/v2.0/third_party/mobilenet_v2_batch256_imagenet-ff34753d.pth" -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/ops/__init__.py deleted file mode 100644 index 7a100d849..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .deprecated_wrappers import Conv2d_deprecated as Conv2d -from .deprecated_wrappers import ConvTranspose2d_deprecated as ConvTranspose2d -from .deprecated_wrappers import Linear_deprecated as Linear -from .deprecated_wrappers import MaxPool2d_deprecated as MaxPool2d -from .focal_loss import (SigmoidFocalLoss, SoftmaxFocalLoss, - sigmoid_focal_loss, softmax_focal_loss) -from .info import (get_compiler_version, get_compiling_cuda_version, - get_onnxruntime_op_path) -from .nms import batched_nms, nms, nms_match, soft_nms -from .roi_align import RoIAlign, roi_align -from .roi_pool import RoIPool, roi_pool -from .sync_bn import SyncBatchNorm \ No newline at end of file diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/README.md b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/README.md deleted file mode 100644 index 91c237f3d..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/README.md +++ /dev/null @@ -1,169 +0,0 @@ -# Code Structure of CUDA operators - -This folder contains all non-python code for MMCV custom ops. Please follow the same architecture if you want to add new ops. - -## Directories Tree - -```folder -. -├── common -│ ├── box_iou_rotated_utils.hpp -│ ├── parrots_cpp_helper.hpp -│ ├── parrots_cuda_helper.hpp -│ ├── pytorch_cpp_helper.hpp -│ ├── pytorch_cuda_helper.hpp -│   └── cuda -│   ├── common_cuda_helper.hpp -│   ├── parrots_cudawarpfunction.cuh -│   ├── ... -│   └── ops_cuda_kernel.cuh -├── onnxruntime -│   ├── onnxruntime_register.h -│   ├── onnxruntime_session_options_config_keys.h -│   ├── ort_mmcv_utils.h -│   ├── ... -│   ├── onnx_ops.h -│   └── cpu -│ ├── onnxruntime_register.cpp -│      ├── ... -│      └── onnx_ops_impl.cpp -├── parrots -│   ├── ... -│   ├── ops.cpp -│   ├── ops_parrots.cpp -│   └── ops_pytorch.h -├── pytorch -│   ├── info.cpp -│   ├── pybind.cpp -│   ├── ... -│   ├── ops.cpp -│   └── cuda -│      ├── ... -│      └── ops_cuda.cu -└── tensorrt - ├── trt_cuda_helper.cuh - ├── trt_plugin_helper.hpp - ├── trt_plugin.hpp - ├── trt_serialize.hpp - ├── ... - ├── trt_ops.hpp - └── plugins -    ├── trt_cuda_helper.cu -    ├── trt_plugin.cpp -    ├── ... -    ├── trt_ops.cpp -    └── trt_ops_kernel.cu -``` - -## Components - -- `common`: This directory contains all tools and shared codes. - - `cuda`: The cuda kernels which can be shared by all backends. **HIP** kernel is also here since they have similar syntax. -- `onnxruntime`: **ONNX Runtime** support for custom ops. - - `cpu`: CPU implementation of supported ops. -- `parrots`: **Parrots** is a deep learning frame for model training and inference. Parrots custom ops are placed in this directory. -- `pytorch`: **PyTorch** custom ops are supported by binding C++ to Python with **pybind11**. The ops implementation and binding codes are placed in this directory. - - `cuda`: This directory contains cuda kernel launchers, which feed memory pointers of tensor to the cuda kernel in `common/cuda`. The launchers provide c++ interface of cuda implementation of corresponding custom ops. -- `tensorrt`: **TensorRT** support for custom ops. - - `plugins`: This directory contains the implementation of the supported custom ops. Some ops might also use shared cuda kernel in `common/cuda`. - -## How to add new PyTorch ops? - -1. (Optional) Add shared kernel in `common` to support special hardware platform. - - ```c++ - // src/common/cuda/new_ops_cuda_kernel.cuh - - template - __global__ void new_ops_forward_cuda_kernel(const T* input, T* output, ...) { - // forward here - } - - ``` - - Add cuda kernel launcher in `pytorch/cuda`. - - ```c++ - // src/pytorch/cuda - #include - - void NewOpsForwardCUDAKernelLauncher(Tensor input, Tensor output, ...){ - // initialize - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - ... - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "new_ops_forward_cuda_kernel", ([&] { - new_ops_forward_cuda_kernel - <<>>( - input.data_ptr(), output.data_ptr(),...); - })); - AT_CUDA_CHECK(cudaGetLastError()); - } - ``` - -2. Add ops implementation in `pytorch` directory. Select different implementations according to device type. - - ```c++ - // src/pytorch/new_ops.cpp - #ifdef MMCV_WITH_CUDA - Tensor new_ops_forward_cuda(Tensor input, Tensor output, ...){ - // implement cuda forward here - // use `NewOpsForwardCUDAKernelLauncher` here - } - #else - - Tensor new_ops_forward_cpu(Tensor input, Tensor output, ...){ - // implement cpu forward here - } - - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...){ - // select implementation by input device type - if (boxes.device().is_cuda()) { - #ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(output); - return new_ops_forward_cuda(input, output, ...); - #else - AT_ERROR("new ops is not compiled with GPU support"); - #endif - } else { - CHECK_CPU_INPUT(input); - CHECK_CPU_INPUT(output); - return new_ops_forward_cpu(input, output, ...); - } - } - ``` - -3. Binding the implementation in `pytorch/pybind.cpp` - - ```c++ - // src/pytorch/pybind.cpp - - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...); - - ... - - // bind with pybind11 - m.def("new_ops_forward", &new_ops_forward, "new_ops_forward", - py::arg("input"), py::arg("output"), ...); - - ... - - ``` - -4. Build MMCV again. Enjoy new ops in python - - ```python - from ..utils import ext_loader - ext_module = ext_loader.load_ext('_ext', ['new_ops_forward']) - - ... - - ext_module.new_ops_forward(input, output, ...) - - ``` diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp deleted file mode 100644 index a1e926adb..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef COMMON_CUDA_HELPER -#define COMMON_CUDA_HELPER - -#include - -#define CUDA_1D_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) - -#define THREADS_PER_BLOCK 512 - -#define DIVUP(m, n) ((m) / (n) + ((m) % (n) > 0)) - -inline int GET_BLOCKS(const int N) { - int optimal_block_num = (N + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; - int max_block_num = 4096; - return std::min(optimal_block_num, max_block_num); -} - -template -__device__ T bilinear_interpolate(const T* input, const int height, - const int width, T y, T x, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) return 0; - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - int y_low = (int)y; - int x_low = (int)x; - int y_high; - int x_high; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - // do bilinear interpolation - T v1 = input[y_low * width + x_low]; - T v2 = input[y_low * width + x_high]; - T v3 = input[y_high * width + x_low]; - T v4 = input[y_high * width + x_high]; - T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - return val; -} - -template -__device__ void bilinear_interpolate_gradient( - const int height, const int width, T y, T x, T& w1, T& w2, T& w3, T& w4, - int& x_low, int& x_high, int& y_low, int& y_high, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - w1 = w2 = w3 = w4 = 0.; - x_low = x_high = y_low = y_high = -1; - return; - } - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - y_low = (int)y; - x_low = (int)x; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - - // reference in forward - // T v1 = input[y_low * width + x_low]; - // T v2 = input[y_low * width + x_high]; - // T v3 = input[y_high * width + x_low]; - // T v4 = input[y_high * width + x_high]; - // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - return; -} -#endif // COMMON_CUDA_HELPER diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/nms_cuda_kernel.cuh b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/nms_cuda_kernel.cuh deleted file mode 100644 index 40a2f4622..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/nms_cuda_kernel.cuh +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef NMS_CUDA_KERNEL_CUH -#define NMS_CUDA_KERNEL_CUH - -#include -#ifdef MMCV_WITH_TRT -#include "common_cuda_helper.hpp" -#else // MMCV_WITH_TRT -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else // MMCV_USE_PARROTS -#include "pytorch_cuda_helper.hpp" -#endif // MMCV_USE_PARROTS -#endif // MMCV_WITH_TRT - -int const threadsPerBlock = sizeof(unsigned long long int) * 8; - -__device__ inline bool devIoU(float const *const a, float const *const b, - const int offset, const float threshold) { - float left = fmaxf(a[0], b[0]), right = fminf(a[2], b[2]); - float top = fmaxf(a[1], b[1]), bottom = fminf(a[3], b[3]); - float width = fmaxf(right - left + offset, 0.f), - height = fmaxf(bottom - top + offset, 0.f); - float interS = width * height; - float Sa = (a[2] - a[0] + offset) * (a[3] - a[1] + offset); - float Sb = (b[2] - b[0] + offset) * (b[3] - b[1] + offset); - return interS > threshold * (Sa + Sb - interS); -} - -__global__ void nms_cuda(const int n_boxes, const float iou_threshold, - const int offset, const float *dev_boxes, - unsigned long long *dev_mask) { - const int row_start = blockIdx.y; - const int col_start = blockIdx.x; - const int tid = threadIdx.x; - - if (row_start > col_start) return; - - const int row_size = - fminf(n_boxes - row_start * threadsPerBlock, threadsPerBlock); - const int col_size = - fminf(n_boxes - col_start * threadsPerBlock, threadsPerBlock); - - __shared__ float block_boxes[threadsPerBlock * 4]; - if (tid < col_size) { - block_boxes[tid * 4 + 0] = - dev_boxes[(threadsPerBlock * col_start + tid) * 4 + 0]; - block_boxes[tid * 4 + 1] = - dev_boxes[(threadsPerBlock * col_start + tid) * 4 + 1]; - block_boxes[tid * 4 + 2] = - dev_boxes[(threadsPerBlock * col_start + tid) * 4 + 2]; - block_boxes[tid * 4 + 3] = - dev_boxes[(threadsPerBlock * col_start + tid) * 4 + 3]; - } - __syncthreads(); - - if (tid < row_size) { - const int cur_box_idx = threadsPerBlock * row_start + tid; - const float *cur_box = dev_boxes + cur_box_idx * 4; - int i = 0; - unsigned long long int t = 0; - int start = 0; - if (row_start == col_start) { - start = tid + 1; - } - for (i = start; i < col_size; i++) { - if (devIoU(cur_box, block_boxes + i * 4, offset, iou_threshold)) { - t |= 1ULL << i; - } - } - dev_mask[cur_box_idx * gridDim.y + col_start] = t; - } -} -#endif // NMS_CUDA_KERNEL_CUH diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/nms_rotated_cuda.cuh b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/nms_rotated_cuda.cuh deleted file mode 100644 index 80bed9681..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/nms_rotated_cuda.cuh +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved -// modified from -// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/nms_rotated/nms_rotated_cuda.cu -#ifndef NMS_ROTATED_CUDA_CUH -#define NMS_ROTATED_CUDA_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif -#include "box_iou_rotated_utils.hpp" - -__host__ __device__ inline int divideUP(const int x, const int y) { - return (((x) + (y)-1) / (y)); -} - -namespace { -int const threadsPerBlock = sizeof(unsigned long long) * 8; -} - -template -__global__ void nms_rotated_cuda_kernel(const int n_boxes, - const float iou_threshold, - const T* dev_boxes, - unsigned long long* dev_mask, - const int multi_label) { - // nms_rotated_cuda_kernel is modified from torchvision's nms_cuda_kernel - - if (multi_label == 1) { - const int row_start = blockIdx.y; - const int col_start = blockIdx.x; - - // if (row_start > col_start) return; - - const int row_size = - min(n_boxes - row_start * threadsPerBlock, threadsPerBlock); - const int col_size = - min(n_boxes - col_start * threadsPerBlock, threadsPerBlock); - - // Compared to nms_cuda_kernel, where each box is represented with 4 values - // (x1, y1, x2, y2), each rotated box is represented with 5 values - // (x_center, y_center, width, height, angle_degrees) here. - __shared__ T block_boxes[threadsPerBlock * 5]; - if (threadIdx.x < col_size) { - block_boxes[threadIdx.x * 6 + 0] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 6 + 0]; - block_boxes[threadIdx.x * 6 + 1] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 6 + 1]; - block_boxes[threadIdx.x * 6 + 2] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 6 + 2]; - block_boxes[threadIdx.x * 6 + 3] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 6 + 3]; - block_boxes[threadIdx.x * 6 + 4] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 6 + 4]; - block_boxes[threadIdx.x * 6 + 5] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 6 + 5]; - } - __syncthreads(); - - if (threadIdx.x < row_size) { - const int cur_box_idx = threadsPerBlock * row_start + threadIdx.x; - const T* cur_box = dev_boxes + cur_box_idx * 6; - int i = 0; - unsigned long long t = 0; - int start = 0; - if (row_start == col_start) { - start = threadIdx.x + 1; - } - for (i = start; i < col_size; i++) { - // Instead of devIoU used by original horizontal nms, here - // we use the single_box_iou_rotated function from - // box_iou_rotated_utils.h - if (single_box_iou_rotated(cur_box, block_boxes + i * 6, 0) > - iou_threshold) { - t |= 1ULL << i; - } - } - const int col_blocks = divideUP(n_boxes, threadsPerBlock); - dev_mask[cur_box_idx * col_blocks + col_start] = t; - } - } else { - const int row_start = blockIdx.y; - const int col_start = blockIdx.x; - - // if (row_start > col_start) return; - - const int row_size = - min(n_boxes - row_start * threadsPerBlock, threadsPerBlock); - const int col_size = - min(n_boxes - col_start * threadsPerBlock, threadsPerBlock); - - // Compared to nms_cuda_kernel, where each box is represented with 4 values - // (x1, y1, x2, y2), each rotated box is represented with 5 values - // (x_center, y_center, width, height, angle_degrees) here. - __shared__ T block_boxes[threadsPerBlock * 5]; - if (threadIdx.x < col_size) { - block_boxes[threadIdx.x * 5 + 0] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 0]; - block_boxes[threadIdx.x * 5 + 1] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 1]; - block_boxes[threadIdx.x * 5 + 2] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 2]; - block_boxes[threadIdx.x * 5 + 3] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 3]; - block_boxes[threadIdx.x * 5 + 4] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 4]; - } - __syncthreads(); - - if (threadIdx.x < row_size) { - const int cur_box_idx = threadsPerBlock * row_start + threadIdx.x; - const T* cur_box = dev_boxes + cur_box_idx * 5; - int i = 0; - unsigned long long t = 0; - int start = 0; - if (row_start == col_start) { - start = threadIdx.x + 1; - } - for (i = start; i < col_size; i++) { - // Instead of devIoU used by original horizontal nms, here - // we use the single_box_iou_rotated function from - // box_iou_rotated_utils.h - if (single_box_iou_rotated(cur_box, block_boxes + i * 5, 0) > - iou_threshold) { - t |= 1ULL << i; - } - } - const int col_blocks = divideUP(n_boxes, threadsPerBlock); - dev_mask[cur_box_idx * col_blocks + col_start] = t; - } - } -} - -#endif diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/roi_align_cuda_kernel.cuh b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/roi_align_cuda_kernel.cuh deleted file mode 100644 index 4541462af..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/roi_align_cuda_kernel.cuh +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef ROI_ALIGN_CUDA_KERNEL_CUH -#define ROI_ALIGN_CUDA_KERNEL_CUH - -#include -#ifdef MMCV_WITH_TRT -#include "common_cuda_helper.hpp" -#else // MMCV_WITH_TRT -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else // MMCV_USE_PARROTS -#include "pytorch_cuda_helper.hpp" -#endif // MMCV_USE_PARROTS -#endif // MMCV_WITH_TRT - -/*** Forward ***/ -template -__global__ void roi_align_forward_cuda_kernel( - const int nthreads, const T* input, const T* rois, T* output, T* argmax_y, - T* argmax_x, const int pooled_height, const int pooled_width, - const T spatial_scale, const int sampling_ratio, - const int pool_mode, // 0 - max pool, 1 - avg pool - const bool aligned, const int channels, const int height, const int width) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - // (n, c, ph, pw) is an element in the pooled output - int pw = index % pooled_width; - int ph = (index / pooled_width) % pooled_height; - int c = (index / pooled_width / pooled_height) % channels; - int n = index / pooled_width / pooled_height / channels; - - const T* offset_rois = rois + n * 5; - int roi_batch_ind = offset_rois[0]; - - // Do not using rounding; this implementation detail is critical - T offset = aligned ? (T)0.5 : (T)0.0; - T roi_start_w = offset_rois[1] * spatial_scale - offset; - T roi_start_h = offset_rois[2] * spatial_scale - offset; - T roi_end_w = offset_rois[3] * spatial_scale - offset; - T roi_end_h = offset_rois[4] * spatial_scale - offset; - - T roi_width = roi_end_w - roi_start_w; - T roi_height = roi_end_h - roi_start_h; - if (!aligned) { // for backward-compatibility only - roi_width = max(roi_width, (T)1.); - roi_height = max(roi_height, (T)1.); - } - - T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); - T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); - - const T* offset_input = - input + (roi_batch_ind * channels + c) * height * width; - - // We use roi_bin_grid to sample the grid and mimic integral - int roi_bin_grid_h = - (sampling_ratio > 0) - ? sampling_ratio - : static_cast(ceilf(roi_height / pooled_height)); - int roi_bin_grid_w = - (sampling_ratio > 0) - ? sampling_ratio - : static_cast(ceilf(roi_width / pooled_width)); - - if (pool_mode == 0) { - // We do max pooling inside a bin - T maxval = -FLT_MAX; - T maxidx_y = -1.f, maxidx_x = -1.f; - for (int iy = 0; iy < roi_bin_grid_h; iy++) { - const T y = roi_start_h + ph * bin_size_h + - static_cast(iy + .5f) * bin_size_h / - static_cast(roi_bin_grid_h); - for (int ix = 0; ix < roi_bin_grid_w; ix++) { - const T x = roi_start_w + pw * bin_size_w + - static_cast(ix + .5f) * bin_size_w / - static_cast(roi_bin_grid_w); - T val = - bilinear_interpolate(offset_input, height, width, y, x, index); - if (val > maxval) { - maxval = val; - maxidx_y = y; - maxidx_x = x; - } - } - } - output[index] = maxval; - argmax_y[index] = maxidx_y; - argmax_x[index] = maxidx_x; - } else if (pool_mode == 1) { - // We do average pooling inside a bin - const T count = max(roi_bin_grid_h * roi_bin_grid_w, 1); - T output_val = 0.; - for (int iy = 0; iy < roi_bin_grid_h; iy++) { - const T y = roi_start_h + ph * bin_size_h + - static_cast(iy + .5f) * bin_size_h / - static_cast(roi_bin_grid_h); - for (int ix = 0; ix < roi_bin_grid_w; ix++) { - const T x = roi_start_w + pw * bin_size_w + - static_cast(ix + .5f) * bin_size_w / - static_cast(roi_bin_grid_w); - T val = - bilinear_interpolate(offset_input, height, width, y, x, index); - output_val += val; - } - } - output[index] = output_val / count; - } - } -} - -/*** Backward ***/ -template -__global__ void roi_align_backward_cuda_kernel( - const int nthreads, const T* grad_output, const T* rois, const T* argmax_y, - const T* argmax_x, T* grad_input, const int pooled_height, - const int pooled_width, const T spatial_scale, const int sampling_ratio, - const int pool_mode, // 0 - max pool, 1 - avg pool - const bool aligned, const int channels, const int height, const int width) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - // (n, c, ph, pw) is an element in the pooled output - int pw = index % pooled_width; - int ph = (index / pooled_width) % pooled_height; - int c = (index / pooled_width / pooled_height) % channels; - int n = index / pooled_width / pooled_height / channels; - - const T grad_output_this_bin = grad_output[index]; - - const T* offset_rois = rois + n * 5; - int roi_batch_ind = offset_rois[0]; - T* offset_grad_input = - grad_input + ((roi_batch_ind * channels + c) * height * width); - - if (pool_mode == 0) { - T y = argmax_y[index], x = argmax_x[index]; - if (y != -1.f) { - T w1, w2, w3, w4; - int x_low, x_high, y_low, y_high; - bilinear_interpolate_gradient(height, width, y, x, w1, w2, w3, w4, - x_low, x_high, y_low, y_high, index); - - if (x_low >= 0 && x_high >= 0 && y_low >= 0 && y_high >= 0) { - atomicAdd(offset_grad_input + y_low * width + x_low, - grad_output_this_bin * w1); - atomicAdd(offset_grad_input + y_low * width + x_high, - grad_output_this_bin * w2); - atomicAdd(offset_grad_input + y_high * width + x_low, - grad_output_this_bin * w3); - atomicAdd(offset_grad_input + y_high * width + x_high, - grad_output_this_bin * w4); - } - } - } else if (pool_mode == 1) { - // Do not using rounding; this implementation detail is critical - T offset = aligned ? (T)0.5 : (T)0.0; - T roi_start_w = offset_rois[1] * spatial_scale - offset; - T roi_start_h = offset_rois[2] * spatial_scale - offset; - T roi_end_w = offset_rois[3] * spatial_scale - offset; - T roi_end_h = offset_rois[4] * spatial_scale - offset; - - T roi_width = roi_end_w - roi_start_w; - T roi_height = roi_end_h - roi_start_h; - if (!aligned) { // for backward-compatibility only - roi_width = max(roi_width, (T)1.); - roi_height = max(roi_height, (T)1.); - } - - T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); - T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); - - // We use roi_bin_grid to sample the grid and mimic integral - int roi_bin_grid_h = - (sampling_ratio > 0) - ? sampling_ratio - : static_cast(ceilf(roi_height / pooled_height)); - int roi_bin_grid_w = - (sampling_ratio > 0) - ? sampling_ratio - : static_cast(ceilf(roi_width / pooled_width)); - - // We do average (integral) pooling inside a bin - const T count = roi_bin_grid_h * roi_bin_grid_w; // e.g. = 4 - - for (int iy = 0; iy < roi_bin_grid_h; iy++) { - const T y = roi_start_h + ph * bin_size_h + - static_cast(iy + .5f) * bin_size_h / - static_cast(roi_bin_grid_h); - for (int ix = 0; ix < roi_bin_grid_w; ix++) { - const T x = roi_start_w + pw * bin_size_w + - static_cast(ix + .5f) * bin_size_w / - static_cast(roi_bin_grid_w); - - T w1, w2, w3, w4; - int x_low, x_high, y_low, y_high; - bilinear_interpolate_gradient(height, width, y, x, w1, w2, w3, w4, - x_low, x_high, y_low, y_high, index); - - if (x_low >= 0 && x_high >= 0 && y_low >= 0 && y_high >= 0) { - atomicAdd(offset_grad_input + y_low * width + x_low, - grad_output_this_bin * w1 / count); - atomicAdd(offset_grad_input + y_low * width + x_high, - grad_output_this_bin * w2 / count); - atomicAdd(offset_grad_input + y_high * width + x_low, - grad_output_this_bin * w3 / count); - atomicAdd(offset_grad_input + y_high * width + x_high, - grad_output_this_bin * w4 / count); - } - } - } - } - } -} - -#endif // ROI_ALIGN_CUDA_KERNEL_CUH diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/roi_pool_cuda_kernel.cuh b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/roi_pool_cuda_kernel.cuh deleted file mode 100644 index 3d7eae66b..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/roi_pool_cuda_kernel.cuh +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef ROI_POOL_CUDA_KERNEL_CUH -#define ROI_POOL_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void roi_pool_forward_cuda_kernel( - const int nthreads, const T* input, const T* rois, T* output, int* argmax, - const int pooled_height, const int pooled_width, const T spatial_scale, - const int channels, const int height, const int width) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - // (n, c, ph, pw) is an element in the pooled output - int pw = index % pooled_width; - int ph = (index / pooled_width) % pooled_height; - int c = (index / pooled_width / pooled_height) % channels; - int n = index / pooled_width / pooled_height / channels; - - const T* offset_rois = rois + n * 5; - int roi_batch_ind = offset_rois[0]; - // calculate the roi region on feature maps - T roi_x1 = offset_rois[1] * spatial_scale; - T roi_y1 = offset_rois[2] * spatial_scale; - T roi_x2 = (offset_rois[3] + 1) * spatial_scale; - T roi_y2 = (offset_rois[4] + 1) * spatial_scale; - - // force malformed rois to be 1x1 - T roi_w = roi_x2 - roi_x1; - T roi_h = roi_y2 - roi_y1; - if (roi_w <= 0 || roi_h <= 0) continue; - - T bin_size_w = roi_w / static_cast(pooled_width); - T bin_size_h = roi_h / static_cast(pooled_height); - - // the corresponding bin region - int bin_x1 = floorf(static_cast(pw) * bin_size_w + roi_x1); - int bin_y1 = floorf(static_cast(ph) * bin_size_h + roi_y1); - int bin_x2 = ceilf(static_cast(pw + 1) * bin_size_w + roi_x1); - int bin_y2 = ceilf(static_cast(ph + 1) * bin_size_h + roi_y1); - - // add roi offsets and clip to input boundaries - bin_x1 = min(max(bin_x1, 0), width); - bin_y1 = min(max(bin_y1, 0), height); - bin_x2 = min(max(bin_x2, 0), width); - bin_y2 = min(max(bin_y2, 0), height); - bool is_empty = (bin_y2 <= bin_y1) || (bin_x2 <= bin_x1); - - const T* offset_input = - input + (roi_batch_ind * channels + c) * height * width; - // Define an empty pooling region to be zero - // If nothing is pooled, argmax = -1 causes nothing to be backprop'd - T max_val = is_empty ? 0 : -FLT_MAX; - int max_idx = -1; - for (int h = bin_y1; h < bin_y2; ++h) { - for (int w = bin_x1; w < bin_x2; ++w) { - int offset = h * width + w; - if (offset_input[offset] > max_val) { - max_val = offset_input[offset]; - max_idx = offset; - } - } - } - output[index] = max_val; - if (argmax != NULL) argmax[index] = max_idx; - } -} - -template -__global__ void roi_pool_backward_cuda_kernel( - const int nthreads, const T* grad_output, const T* rois, const int* argmax, - T* grad_input, const int pooled_height, const int pooled_width, - const int channels, const int height, const int width) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - // (n, c) is an element in the pooled output - int c = (index / pooled_width / pooled_height) % channels; - int n = index / pooled_width / pooled_height / channels; - - int roi_batch_ind = rois[n * 5]; - T* grad_input_offset = - grad_input + ((roi_batch_ind * channels + c) * height * width); - int argmax_index = argmax[index]; - - if (argmax_index != -1) { - atomicAdd(grad_input_offset + argmax_index, grad_output[index]); - } - } -} - -#endif // ROI_POOL_CUDA_KERNEL_CUH diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 1eb5f8fcc..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sigmoid_focal_loss_forward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* output, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + expf(-input[index])); - - // (1 - p)**gamma * log(p) - T term_p = pow(((T)1. - p), gamma) * log(max(p, (T)FLT_MIN)); - // p**gamma * log(1 - p) - T term_n = pow(p, gamma) * log(max((T)1. - p, (T)FLT_MIN)); - - output[index] = (T)0.; - output[index] += -flag_p * alpha * term_p; - output[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - output[index] *= weight[t]; - } - } -} - -template -__global__ void sigmoid_focal_loss_backward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* grad_input, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + exp(-input[index])); - - // (1 - p)**gamma * (1 - p - gamma*p*log(p)) - T term_p = pow(((T)1. - p), gamma) * - ((T)1. - p - (gamma * p * log(max(p, (T)FLT_MIN)))); - // p**gamma * (gamma * (1 - p) * log(1 - p) - p) - T term_n = pow(p, gamma) * - (gamma * ((T)1. - p) * log(max((T)1. - p, (T)FLT_MIN)) - p); - - grad_input[index] = (T)0.; - grad_input[index] += -flag_p * alpha * term_p; - grad_input[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - grad_input[index] *= weight[t]; - } - } -} - -#endif // SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 631b2c617..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void softmax_focal_loss_forward_cuda_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* output, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - output[index] = - -alpha * pow((T)1. - pred, gamma) * log(max(pred, (T)FLT_MIN)); - } else { - output[index] = 0; - } - if (weight != NULL) { - output[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda1_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* buff, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - buff[index] = alpha * (-pow((T)1. - pred, gamma) + - gamma * pow((T)1. - pred, gamma - 1) * pred * - log(max(pred, (T)FLT_MIN))); - } else { - buff[index] = 0; - } - if (weight != NULL) { - buff[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda2_kernel( - const int nthreads, const T* softmax, const int64_t* target, const T* buff, - T* grad_input, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - int64_t label = target[n]; - - if (label >= 0) { - T flag = (label == c ? (T)1. : (T)0.); - grad_input[index] = buff[n] * (flag - softmax[index]); - } else { - grad_input[index] = 0; - } - } -} - -#endif // SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh deleted file mode 100644 index 4ec6a4668..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SYNCBN_CUDA_KERNEL_CUH -#define SYNCBN_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sync_bn_forward_mean_cuda_kernel(const T *input, float *mean, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += input[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_mean_cuda_kernel(const phalf *input, - float *mean, int num, - int channels, int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += static_cast(input[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_var_cuda_kernel(const T *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = input[index] - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_var_cuda_kernel(const phalf *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = static_cast(input[index]) - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_output_cuda_kernel( - const T *input, const float *mean, const float *var, float *running_mean, - float *running_var, const float *weight, const float *bias, float *norm, - float *std, T *output, int num, int channels, int spatial, float eps, - float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = (input[index] - mean_value) / std_value; - output[index] = norm[index] * weight_value + bias_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - (input[index] - mean_value) / std_value * weight_value + bias_value; - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = norm[index] = (input[index] - mean_value) / std_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = (input[index] - mean_value) / std_value; - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template <> -__global__ void sync_bn_forward_output_cuda_kernel( - const phalf *input, const float *mean, const float *var, - float *running_mean, float *running_var, const float *weight, - const float *bias, float *norm, float *std, phalf *output, int num, - int channels, int spatial, float eps, float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = - static_cast(norm[index] * weight_value + bias_value); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - static_cast((static_cast(input[index]) - mean_value) / - std_value * weight_value + - bias_value); - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = static_cast(norm[index]); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = static_cast( - (static_cast(input[index]) - mean_value) / std_value); - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template -__global__ void sync_bn_backward_param_cuda_kernel(const T *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += grad_output[index] * norm[index]; - buffer2[tid] += grad_output[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template <> -__global__ void sync_bn_backward_param_cuda_kernel(const phalf *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += static_cast(grad_output[index]) * norm[index]; - buffer2[tid] += static_cast(grad_output[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const T *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, T *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = - weight[c] * - (grad_output[index] - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]; - } -} - -template <> -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const phalf *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, phalf *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = static_cast( - weight[c] * - (static_cast(grad_output[index]) - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]); - } -} - -#endif // SYNCBN_CUDA_KERNEL_CUH diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp deleted file mode 100644 index c7f9f35b7..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef PYTORCH_CPP_HELPER -#define PYTORCH_CPP_HELPER -#include - -#include - -using namespace at; - -#define DIVUP(m, n) ((m) / (n) + ((m) % (n) > 0)) - -#define CHECK_CUDA(x) \ - TORCH_CHECK(x.device().is_cuda(), #x " must be a CUDA tensor") -#define CHECK_CPU(x) \ - TORCH_CHECK(!x.device().is_cuda(), #x " must be a CPU tensor") -#define CHECK_CONTIGUOUS(x) \ - TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") -#define CHECK_CUDA_INPUT(x) \ - CHECK_CUDA(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_CPU_INPUT(x) \ - CHECK_CPU(x); \ - CHECK_CONTIGUOUS(x) - -#endif // PYTORCH_CPP_HELPER diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp deleted file mode 100644 index 9869b535f..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PYTORCH_CUDA_HELPER -#define PYTORCH_CUDA_HELPER - -#include -#include -#include - -#include -#include - -#include "common_cuda_helper.hpp" - -using at::Half; -using at::Tensor; -using phalf = at::Half; - -#define __PHALF(x) (x) - -#endif // PYTORCH_CUDA_HELPER diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu deleted file mode 100644 index cb899f954..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sigmoid_focal_loss_cuda_kernel.cuh" -#include "softmax_focal_loss_cuda_kernel.cuh" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = input.size(1); - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_forward_cuda_kernel", [&] { - sigmoid_focal_loss_forward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha) { - int output_size = grad_input.numel(); - int num_classes = input.size(1); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_backward_cuda_kernel", [&] { - sigmoid_focal_loss_backward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - grad_input.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = softmax.size(1); - - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(softmax.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - softmax.scalar_type(), "softmax_focal_loss_forward_cuda_kernel", [&] { - softmax_focal_loss_forward_cuda_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha) { - int num_classes = softmax.size(1); - - int output_size = buff.numel(); - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda1_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda1_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - buff.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); - - output_size = grad_input.numel(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda2_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda2_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), buff.data_ptr(), - grad_input.data_ptr(), num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/nms_cuda.cu b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/nms_cuda.cu deleted file mode 100644 index 16cf64683..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/nms_cuda.cu +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "nms_cuda_kernel.cuh" -#include "pytorch_cuda_helper.hpp" - -Tensor NMSCUDAKernelLauncher(Tensor boxes, Tensor scores, float iou_threshold, - int offset) { - at::cuda::CUDAGuard device_guard(boxes.device()); - - if (boxes.numel() == 0) { - return at::empty({0}, boxes.options().dtype(at::kLong)); - } - auto order_t = std::get<1>(scores.sort(0, /*descending=*/true)); - auto boxes_sorted = boxes.index_select(0, order_t); - - int boxes_num = boxes.size(0); - const int col_blocks = DIVUP(boxes_num, threadsPerBlock); - Tensor mask = - at::empty({boxes_num, col_blocks}, boxes.options().dtype(at::kLong)); - dim3 blocks(col_blocks, col_blocks); - dim3 threads(threadsPerBlock); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - nms_cuda<<>>( - boxes_num, iou_threshold, offset, boxes_sorted.data_ptr(), - (unsigned long long*)mask.data_ptr()); - - at::Tensor mask_cpu = mask.to(at::kCPU); - unsigned long long* mask_host = - (unsigned long long*)mask_cpu.data_ptr(); - - std::vector remv(col_blocks); - memset(&remv[0], 0, sizeof(unsigned long long) * col_blocks); - - at::Tensor keep_t = - at::zeros({boxes_num}, boxes.options().dtype(at::kBool).device(at::kCPU)); - bool* keep = keep_t.data_ptr(); - - for (int i = 0; i < boxes_num; i++) { - int nblock = i / threadsPerBlock; - int inblock = i % threadsPerBlock; - - if (!(remv[nblock] & (1ULL << inblock))) { - keep[i] = true; - // set every overlap box with bit 1 in remv - unsigned long long* p = mask_host + i * col_blocks; - for (int j = nblock; j < col_blocks; j++) { - remv[j] |= p[j]; - } - } - } - - AT_CUDA_CHECK(cudaGetLastError()); - return order_t.masked_select(keep_t.to(at::kCUDA)); -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_align_cuda.cu b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_align_cuda.cu deleted file mode 100644 index 3d4f7614e..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_align_cuda.cu +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "roi_align_cuda_kernel.cuh" - -void ROIAlignForwardCUDAKernelLauncher(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned) { - int output_size = output.numel(); - int channels = input.size(1); - int height = input.size(2); - int width = input.size(3); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "roi_align_forward_cuda_kernel", [&] { - roi_align_forward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - rois.data_ptr(), output.data_ptr(), - argmax_y.data_ptr(), argmax_x.data_ptr(), - aligned_height, aligned_width, - static_cast(spatial_scale), sampling_ratio, pool_mode, - aligned, channels, height, width); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void ROIAlignBackwardCUDAKernelLauncher(Tensor grad_output, Tensor rois, - Tensor argmax_y, Tensor argmax_x, - Tensor grad_input, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, - bool aligned) { - int output_size = grad_output.numel(); - int channels = grad_input.size(1); - int height = grad_input.size(2); - int width = grad_input.size(3); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "roi_align_backward_cuda_kernel", [&] { - roi_align_backward_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - rois.data_ptr(), argmax_y.data_ptr(), - argmax_x.data_ptr(), grad_input.data_ptr(), - aligned_height, aligned_width, - static_cast(spatial_scale), sampling_ratio, pool_mode, - aligned, channels, height, width); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_pool_cuda.cu b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_pool_cuda.cu deleted file mode 100644 index d9cdf3050..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_pool_cuda.cu +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "roi_pool_cuda_kernel.cuh" - -void ROIPoolForwardCUDAKernelLauncher(Tensor input, Tensor rois, Tensor output, - Tensor argmax, int pooled_height, - int pooled_width, float spatial_scale) { - int output_size = output.numel(); - int channels = input.size(1); - int height = input.size(2); - int width = input.size(3); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "roi_pool_forward_cuda_kernel", [&] { - roi_pool_forward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - rois.data_ptr(), output.data_ptr(), - argmax.data_ptr(), pooled_height, pooled_width, - static_cast(spatial_scale), channels, height, width); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void ROIPoolBackwardCUDAKernelLauncher(Tensor grad_output, Tensor rois, - Tensor argmax, Tensor grad_input, - int pooled_height, int pooled_width, - float spatial_scale) { - int output_size = grad_output.numel(); - int channels = grad_input.size(1); - int height = grad_input.size(2); - int width = grad_input.size(3); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "roi_pool_backward_cuda_kernel", [&] { - roi_pool_backward_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - rois.data_ptr(), argmax.data_ptr(), - grad_input.data_ptr(), pooled_height, pooled_width, - channels, height, width); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu deleted file mode 100644 index 657c81701..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sync_bn_cuda_kernel.cuh" - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_mean_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_var_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), num, channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_output_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), running_mean.data_ptr(), - running_var.data_ptr(), weight.data_ptr(), - bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), output.data_ptr(), num, - channels, spatial, eps, momentum, group_size); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias) { - int num = grad_output.size(0); - int channels = grad_output.size(1); - int spatial = grad_output.size(2); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_param_cuda_kernel", [&] { - sync_bn_backward_param_cuda_kernel - <<>>( - grad_output.data_ptr(), norm.data_ptr(), - grad_weight.data_ptr(), grad_bias.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - int output_size = grad_input.numel(); - int num = grad_input.size(0); - int channels = grad_input.size(1); - int spatial = grad_input.size(2); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_data_cuda_kernel", [&] { - sync_bn_backward_data_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - weight.data_ptr(), grad_weight.data_ptr(), - grad_bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), grad_input.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp deleted file mode 100644 index 3e2c92b27..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha); - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha); - -void sigmoid_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SigmoidFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void sigmoid_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - SigmoidFocalLossBackwardCUDAKernelLauncher(input, target, weight, grad_input, - gamma, alpha); -} - -void softmax_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SoftmaxFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void softmax_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - SoftmaxFocalLossBackwardCUDAKernelLauncher(input, target, weight, buff, - grad_input, gamma, alpha); -} -#endif - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(target); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(output); - - sigmoid_focal_loss_forward_cuda(input, target, weight, output, gamma, - alpha); -#else - AT_ERROR("SigmoidFocalLoss is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SigmoidFocalLoss is not implemented on CPU"); - } -} - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(target); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(grad_input); - - sigmoid_focal_loss_backward_cuda(input, target, weight, grad_input, gamma, - alpha); -#else - AT_ERROR("SigmoidFocalLoss is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SigmoidFocalLoss is not implemented on CPU"); - } -} - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(target); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(output); - - softmax_focal_loss_forward_cuda(input, target, weight, output, gamma, - alpha); -#else - AT_ERROR("SoftmaxFocalLoss is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SoftmaxFocalLoss is not implemented on CPU"); - } -} - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(target); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(buff); - CHECK_CUDA_INPUT(grad_input); - - softmax_focal_loss_backward_cuda(input, target, weight, buff, grad_input, - gamma, alpha); -#else - AT_ERROR("SoftmaxFocalLoss is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SoftmaxFocalLoss is not implemented on CPU"); - } -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/info.cpp b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/info.cpp deleted file mode 100644 index a08d227d4..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/info.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// modified from -// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/vision.cpp -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF -#include -int get_cudart_version() { return CUDART_VERSION; } -#endif -#endif - -std::string get_compiling_cuda_version() { -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF - std::ostringstream oss; - // copied from - // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/cuda/detail/CUDAHooks.cpp#L231 - auto printCudaStyleVersion = [&](int v) { - oss << (v / 1000) << "." << (v / 10 % 100); - if (v % 10 != 0) { - oss << "." << (v % 10); - } - }; - printCudaStyleVersion(get_cudart_version()); - return oss.str(); -#else - return std::string("rocm not available"); -#endif -#else - return std::string("not available"); -#endif -} - -// similar to -// https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/Version.cpp -std::string get_compiler_version() { - std::ostringstream ss; -#if defined(__GNUC__) -#ifndef __clang__ - { ss << "GCC " << __GNUC__ << "." << __GNUC_MINOR__; } -#endif -#endif - -#if defined(__clang_major__) - { - ss << "clang " << __clang_major__ << "." << __clang_minor__ << "." - << __clang_patchlevel__; - } -#endif - -#if defined(_MSC_VER) - { ss << "MSVC " << _MSC_FULL_VER; } -#endif - return ss.str(); -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/nms.cpp b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/nms.cpp deleted file mode 100644 index e88208dc9..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/nms.cpp +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -Tensor NMSCUDAKernelLauncher(Tensor boxes, Tensor scores, float iou_threshold, - int offset); - -Tensor nms_cuda(Tensor boxes, Tensor scores, float iou_threshold, int offset) { - return NMSCUDAKernelLauncher(boxes, scores, iou_threshold, offset); -} -#endif - -Tensor nms_cpu(Tensor boxes, Tensor scores, float iou_threshold, int offset) { - if (boxes.numel() == 0) { - return at::empty({0}, boxes.options().dtype(at::kLong)); - } - auto x1_t = boxes.select(1, 0).contiguous(); - auto y1_t = boxes.select(1, 1).contiguous(); - auto x2_t = boxes.select(1, 2).contiguous(); - auto y2_t = boxes.select(1, 3).contiguous(); - - Tensor areas_t = (x2_t - x1_t + offset) * (y2_t - y1_t + offset); - - auto order_t = std::get<1>(scores.sort(0, /* descending=*/true)); - - auto nboxes = boxes.size(0); - Tensor select_t = at::ones({nboxes}, boxes.options().dtype(at::kBool)); - - auto select = select_t.data_ptr(); - auto order = order_t.data_ptr(); - auto x1 = x1_t.data_ptr(); - auto y1 = y1_t.data_ptr(); - auto x2 = x2_t.data_ptr(); - auto y2 = y2_t.data_ptr(); - auto areas = areas_t.data_ptr(); - - for (int64_t _i = 0; _i < nboxes; _i++) { - if (select[_i] == false) continue; - auto i = order[_i]; - auto ix1 = x1[i]; - auto iy1 = y1[i]; - auto ix2 = x2[i]; - auto iy2 = y2[i]; - auto iarea = areas[i]; - - for (int64_t _j = _i + 1; _j < nboxes; _j++) { - if (select[_j] == false) continue; - auto j = order[_j]; - auto xx1 = std::max(ix1, x1[j]); - auto yy1 = std::max(iy1, y1[j]); - auto xx2 = std::min(ix2, x2[j]); - auto yy2 = std::min(iy2, y2[j]); - - auto w = std::max(0.f, xx2 - xx1 + offset); - auto h = std::max(0.f, yy2 - yy1 + offset); - auto inter = w * h; - auto ovr = inter / (iarea + areas[j] - inter); - if (ovr > iou_threshold) select[_j] = false; - } - } - return order_t.masked_select(select_t); -} - -Tensor nms(Tensor boxes, Tensor scores, float iou_threshold, int offset) { - if (boxes.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(boxes); - CHECK_CUDA_INPUT(scores); - return nms_cuda(boxes, scores, iou_threshold, offset); -#else - AT_ERROR("nms is not compiled with GPU support"); -#endif - } else { - CHECK_CPU_INPUT(boxes); - CHECK_CPU_INPUT(scores); - return nms_cpu(boxes, scores, iou_threshold, offset); - } -} - -Tensor softnms_cpu(Tensor boxes, Tensor scores, Tensor dets, - float iou_threshold, float sigma, float min_score, - int method, int offset) { - if (boxes.numel() == 0) { - return at::empty({0}, boxes.options().dtype(at::kLong)); - } - - auto x1_t = boxes.select(1, 0).contiguous(); - auto y1_t = boxes.select(1, 1).contiguous(); - auto x2_t = boxes.select(1, 2).contiguous(); - auto y2_t = boxes.select(1, 3).contiguous(); - auto scores_t = scores.clone(); - - Tensor areas_t = (x2_t - x1_t + offset) * (y2_t - y1_t + offset); - - auto nboxes = boxes.size(0); - auto x1 = x1_t.data_ptr(); - auto y1 = y1_t.data_ptr(); - auto x2 = x2_t.data_ptr(); - auto y2 = y2_t.data_ptr(); - auto sc = scores_t.data_ptr(); - auto areas = areas_t.data_ptr(); - auto de = dets.data_ptr(); - - int64_t pos = 0; - Tensor inds_t = at::arange(nboxes, boxes.options().dtype(at::kLong)); - auto inds = inds_t.data_ptr(); - - for (int64_t i = 0; i < nboxes; i++) { - auto max_score = sc[i]; - auto max_pos = i; - - pos = i + 1; - // get max box - while (pos < nboxes) { - if (max_score < sc[pos]) { - max_score = sc[pos]; - max_pos = pos; - } - pos = pos + 1; - } - // swap - auto ix1 = de[i * 5 + 0] = x1[max_pos]; - auto iy1 = de[i * 5 + 1] = y1[max_pos]; - auto ix2 = de[i * 5 + 2] = x2[max_pos]; - auto iy2 = de[i * 5 + 3] = y2[max_pos]; - auto iscore = de[i * 5 + 4] = sc[max_pos]; - auto iarea = areas[max_pos]; - auto iind = inds[max_pos]; - x1[max_pos] = x1[i]; - y1[max_pos] = y1[i]; - x2[max_pos] = x2[i]; - y2[max_pos] = y2[i]; - sc[max_pos] = sc[i]; - areas[max_pos] = areas[i]; - inds[max_pos] = inds[i]; - x1[i] = ix1; - y1[i] = iy1; - x2[i] = ix2; - y2[i] = iy2; - sc[i] = iscore; - areas[i] = iarea; - inds[i] = iind; - - pos = i + 1; - while (pos < nboxes) { - auto xx1 = std::max(ix1, x1[pos]); - auto yy1 = std::max(iy1, y1[pos]); - auto xx2 = std::min(ix2, x2[pos]); - auto yy2 = std::min(iy2, y2[pos]); - - auto w = std::max(0.f, xx2 - xx1 + offset); - auto h = std::max(0.f, yy2 - yy1 + offset); - auto inter = w * h; - auto ovr = inter / (iarea + areas[pos] - inter); - - float weight = 1.; - if (method == 0) { - if (ovr >= iou_threshold) weight = 0; - } else if (method == 1) { - if (ovr >= iou_threshold) weight = 1 - ovr; - } else if (method == 2) { - weight = std::exp(-(ovr * ovr) / sigma); - } - sc[pos] *= weight; - // if box score falls below threshold, discard the box by - // swapping with last box update N - if (sc[pos] < min_score) { - x1[pos] = x1[nboxes - 1]; - y1[pos] = y1[nboxes - 1]; - x2[pos] = x2[nboxes - 1]; - y2[pos] = y2[nboxes - 1]; - sc[pos] = sc[nboxes - 1]; - areas[pos] = areas[nboxes - 1]; - inds[pos] = inds[nboxes - 1]; - nboxes = nboxes - 1; - pos = pos - 1; - } - pos = pos + 1; - } - } - return inds_t.slice(0, 0, nboxes); -} - -Tensor softnms(Tensor boxes, Tensor scores, Tensor dets, float iou_threshold, - float sigma, float min_score, int method, int offset) { - if (boxes.device().is_cuda()) { - AT_ERROR("softnms is not implemented on GPU"); - } else { - return softnms_cpu(boxes, scores, dets, iou_threshold, sigma, min_score, - method, offset); - } -} - -std::vector > nms_match_cpu(Tensor dets, float iou_threshold) { - auto x1_t = dets.select(1, 0).contiguous(); - auto y1_t = dets.select(1, 1).contiguous(); - auto x2_t = dets.select(1, 2).contiguous(); - auto y2_t = dets.select(1, 3).contiguous(); - auto scores = dets.select(1, 4).contiguous(); - - at::Tensor areas_t = (x2_t - x1_t) * (y2_t - y1_t); - - auto order_t = std::get<1>(scores.sort(0, /* descending=*/true)); - - auto ndets = dets.size(0); - at::Tensor suppressed_t = - at::zeros({ndets}, dets.options().dtype(at::kByte).device(at::kCPU)); - - auto suppressed = suppressed_t.data_ptr(); - auto order = order_t.data_ptr(); - auto x1 = x1_t.data_ptr(); - auto y1 = y1_t.data_ptr(); - auto x2 = x2_t.data_ptr(); - auto y2 = y2_t.data_ptr(); - auto areas = areas_t.data_ptr(); - - std::vector keep; - std::vector > matched; - - for (int64_t _i = 0; _i < ndets; _i++) { - auto i = order[_i]; - if (suppressed[i] == 1) continue; - keep.push_back(i); - std::vector v_i; - auto ix1 = x1[i]; - auto iy1 = y1[i]; - auto ix2 = x2[i]; - auto iy2 = y2[i]; - auto iarea = areas[i]; - - for (int64_t _j = _i + 1; _j < ndets; _j++) { - auto j = order[_j]; - if (suppressed[j] == 1) continue; - auto xx1 = std::max(ix1, x1[j]); - auto yy1 = std::max(iy1, y1[j]); - auto xx2 = std::min(ix2, x2[j]); - auto yy2 = std::min(iy2, y2[j]); - - auto w = std::max(static_cast(0), xx2 - xx1); - auto h = std::max(static_cast(0), yy2 - yy1); - auto inter = w * h; - auto ovr = inter / (iarea + areas[j] - inter); - if (ovr >= iou_threshold) { - suppressed[j] = 1; - v_i.push_back(j); - } - } - matched.push_back(v_i); - } - for (int i = 0; i < keep.size(); i++) - matched[i].insert(matched[i].begin(), keep[i]); - return matched; -} - -std::vector > nms_match(Tensor dets, float iou_threshold) { - if (dets.device().is_cuda()) { - AT_ERROR("nms_match is not implemented on GPU"); - } else { - return nms_match_cpu(dets, iou_threshold); - } -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp deleted file mode 100644 index de8e18c97..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -std::string get_compiler_version(); -std::string get_compiling_cuda_version(); - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha); - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha); - -Tensor nms(Tensor boxes, Tensor scores, float iou_threshold, int offset); - -Tensor softnms(Tensor boxes, Tensor scores, Tensor dets, float iou_threshold, - float sigma, float min_score, int method, int offset); - -std::vector> nms_match(Tensor dets, float iou_threshold); - - - -void roi_align_forward(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, bool aligned); - -void roi_align_backward(Tensor grad_output, Tensor rois, Tensor argmax_y, - Tensor argmax_x, Tensor grad_input, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, bool aligned); - -void roi_pool_forward(Tensor input, Tensor rois, Tensor output, Tensor argmax, - int pooled_height, int pooled_width, float spatial_scale); - -void roi_pool_backward(Tensor grad_output, Tensor rois, Tensor argmax, - Tensor grad_input, int pooled_height, int pooled_width, - float spatial_scale); - -void sync_bn_forward_mean(const Tensor input, Tensor mean); - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var); - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - - m.def("get_compiler_version", &get_compiler_version, "get_compiler_version"); - m.def("get_compiling_cuda_version", &get_compiling_cuda_version, - "get_compiling_cuda_version"); - - m.def("sigmoid_focal_loss_forward", &sigmoid_focal_loss_forward, - "sigmoid_focal_loss_forward ", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("sigmoid_focal_loss_backward", &sigmoid_focal_loss_backward, - "sigmoid_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("grad_input"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_forward", &softmax_focal_loss_forward, - "softmax_focal_loss_forward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_backward", &softmax_focal_loss_backward, - "softmax_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("buff"), py::arg("grad_input"), - py::arg("gamma"), py::arg("alpha")); - m.def("nms", &nms, "nms (CPU/CUDA) ", py::arg("boxes"), py::arg("scores"), - py::arg("iou_threshold"), py::arg("offset")); - m.def("softnms", &softnms, "softnms (CPU) ", py::arg("boxes"), - py::arg("scores"), py::arg("dets"), py::arg("iou_threshold"), - py::arg("sigma"), py::arg("min_score"), py::arg("method"), - py::arg("offset")); - m.def("nms_match", &nms_match, "nms_match (CPU) ", py::arg("dets"), - py::arg("iou_threshold")); - m.def("roi_align_forward", &roi_align_forward, "roi_align forward", - py::arg("input"), py::arg("rois"), py::arg("output"), - py::arg("argmax_y"), py::arg("argmax_x"), py::arg("aligned_height"), - py::arg("aligned_width"), py::arg("spatial_scale"), - py::arg("sampling_ratio"), py::arg("pool_mode"), py::arg("aligned")); - m.def("roi_align_backward", &roi_align_backward, "roi_align backward", - py::arg("grad_output"), py::arg("rois"), py::arg("argmax_y"), - py::arg("argmax_x"), py::arg("grad_input"), py::arg("aligned_height"), - py::arg("aligned_width"), py::arg("spatial_scale"), - py::arg("sampling_ratio"), py::arg("pool_mode"), py::arg("aligned")); - m.def("roi_pool_forward", &roi_pool_forward, "roi_pool forward", - py::arg("input"), py::arg("rois"), py::arg("output"), py::arg("argmax"), - py::arg("pooled_height"), py::arg("pooled_width"), - py::arg("spatial_scale")); - m.def("roi_pool_backward", &roi_pool_backward, "roi_pool backward", - py::arg("grad_output"), py::arg("rois"), py::arg("argmax"), - py::arg("grad_input"), py::arg("pooled_height"), - py::arg("pooled_width"), py::arg("spatial_scale")); - m.def("sync_bn_forward_mean", &sync_bn_forward_mean, "sync_bn forward_mean", - py::arg("input"), py::arg("mean")); - m.def("sync_bn_forward_var", &sync_bn_forward_var, "sync_bn forward_var", - py::arg("input"), py::arg("mean"), py::arg("var")); - m.def("sync_bn_forward_output", &sync_bn_forward_output, - "sync_bn forward_output", py::arg("input"), py::arg("mean"), - py::arg("var"), py::arg("weight"), py::arg("bias"), - py::arg("running_mean"), py::arg("running_var"), py::arg("norm"), - py::arg("std"), py::arg("output"), py::arg("eps"), py::arg("momentum"), - py::arg("group_size")); - m.def("sync_bn_backward_param", &sync_bn_backward_param, - "sync_bn backward_param", py::arg("grad_output"), py::arg("norm"), - py::arg("grad_weight"), py::arg("grad_bias")); - m.def("sync_bn_backward_data", &sync_bn_backward_data, - "sync_bn backward_data", py::arg("grad_output"), py::arg("weight"), - py::arg("grad_weight"), py::arg("grad_bias"), py::arg("norm"), - py::arg("std"), py::arg("grad_input")); -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/roi_align.cpp b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/roi_align.cpp deleted file mode 100644 index b44a742ce..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/roi_align.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -void ROIAlignForwardCUDAKernelLauncher(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned); - -void ROIAlignBackwardCUDAKernelLauncher(Tensor grad_output, Tensor rois, - Tensor argmax_y, Tensor argmax_x, - Tensor grad_input, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, - bool aligned); - -void roi_align_forward_cuda(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned) { - ROIAlignForwardCUDAKernelLauncher( - input, rois, output, argmax_y, argmax_x, aligned_height, aligned_width, - spatial_scale, sampling_ratio, pool_mode, aligned); -} - -void roi_align_backward_cuda(Tensor grad_output, Tensor rois, Tensor argmax_y, - Tensor argmax_x, Tensor grad_input, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned) { - ROIAlignBackwardCUDAKernelLauncher( - grad_output, rois, argmax_y, argmax_x, grad_input, aligned_height, - aligned_width, spatial_scale, sampling_ratio, pool_mode, aligned); -} -#endif - -void ROIAlignForwardCPULauncher(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned); - -void ROIAlignBackwardCPULauncher(Tensor grad_output, Tensor rois, - Tensor argmax_y, Tensor argmax_x, - Tensor grad_input, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, - bool aligned); - -void roi_align_forward_cpu(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, bool aligned) { - ROIAlignForwardCPULauncher(input, rois, output, argmax_y, argmax_x, - aligned_height, aligned_width, spatial_scale, - sampling_ratio, pool_mode, aligned); -} - -void roi_align_backward_cpu(Tensor grad_output, Tensor rois, Tensor argmax_y, - Tensor argmax_x, Tensor grad_input, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned) { - ROIAlignBackwardCPULauncher(grad_output, rois, argmax_y, argmax_x, grad_input, - aligned_height, aligned_width, spatial_scale, - sampling_ratio, pool_mode, aligned); -} - -void roi_align_forward(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, bool aligned) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(rois); - CHECK_CUDA_INPUT(output); - CHECK_CUDA_INPUT(argmax_y); - CHECK_CUDA_INPUT(argmax_x); - - roi_align_forward_cuda(input, rois, output, argmax_y, argmax_x, - aligned_height, aligned_width, spatial_scale, - sampling_ratio, pool_mode, aligned); -#else - AT_ERROR("RoIAlign is not compiled with GPU support"); -#endif - } else { - CHECK_CPU_INPUT(input); - CHECK_CPU_INPUT(rois); - CHECK_CPU_INPUT(output); - CHECK_CPU_INPUT(argmax_y); - CHECK_CPU_INPUT(argmax_x); - roi_align_forward_cpu(input, rois, output, argmax_y, argmax_x, - aligned_height, aligned_width, spatial_scale, - sampling_ratio, pool_mode, aligned); - } -} - -void roi_align_backward(Tensor grad_output, Tensor rois, Tensor argmax_y, - Tensor argmax_x, Tensor grad_input, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, bool aligned) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(rois); - CHECK_CUDA_INPUT(argmax_y); - CHECK_CUDA_INPUT(argmax_x); - CHECK_CUDA_INPUT(grad_input); - - roi_align_backward_cuda(grad_output, rois, argmax_y, argmax_x, grad_input, - aligned_height, aligned_width, spatial_scale, - sampling_ratio, pool_mode, aligned); -#else - AT_ERROR("RoIAlign is not compiled with GPU support"); -#endif - } else { - CHECK_CPU_INPUT(grad_output); - CHECK_CPU_INPUT(rois); - CHECK_CPU_INPUT(argmax_y); - CHECK_CPU_INPUT(argmax_x); - CHECK_CPU_INPUT(grad_input); - - roi_align_backward_cpu(grad_output, rois, argmax_y, argmax_x, grad_input, - aligned_height, aligned_width, spatial_scale, - sampling_ratio, pool_mode, aligned); - } -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/roi_align_cpu.cpp b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/roi_align_cpu.cpp deleted file mode 100644 index 3f797cb63..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/roi_align_cpu.cpp +++ /dev/null @@ -1,431 +0,0 @@ -// Modified from -// https://github.com/facebookresearch/detectron2/tree/master/detectron2/layers/csrc/ROIAlign -// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved -#include -#include - -#include "pytorch_cpp_helper.hpp" - -// implementation taken from Caffe2 -template -struct PreCalc { - int pos1; - int pos2; - int pos3; - int pos4; - T w1; - T w2; - T w3; - T w4; -}; - -template -void pre_calc_for_bilinear_interpolate( - const int height, const int width, const int pooled_height, - const int pooled_width, const int iy_upper, const int ix_upper, - T roi_start_h, T roi_start_w, T bin_size_h, T bin_size_w, - int roi_bin_grid_h, int roi_bin_grid_w, std::vector>& pre_calc) { - int pre_calc_index = 0; - for (int ph = 0; ph < pooled_height; ph++) { - for (int pw = 0; pw < pooled_width; pw++) { - for (int iy = 0; iy < iy_upper; iy++) { - const T yy = roi_start_h + ph * bin_size_h + - static_cast(iy + .5f) * bin_size_h / - static_cast(roi_bin_grid_h); // e.g., 0.5, 1.5 - for (int ix = 0; ix < ix_upper; ix++) { - const T xx = roi_start_w + pw * bin_size_w + - static_cast(ix + .5f) * bin_size_w / - static_cast(roi_bin_grid_w); - - T x = xx; - T y = yy; - // deal with: inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - PreCalc pc; - pc.pos1 = 0; - pc.pos2 = 0; - pc.pos3 = 0; - pc.pos4 = 0; - pc.w1 = 0; - pc.w2 = 0; - pc.w3 = 0; - pc.w4 = 0; - pre_calc[pre_calc_index] = pc; - pre_calc_index += 1; - continue; - } - - if (y <= 0) { - y = 0; - } - if (x <= 0) { - x = 0; - } - - int y_low = (int)y; - int x_low = (int)x; - int y_high; - int x_high; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - // save weights and indices - PreCalc pc; - pc.pos1 = y_low * width + x_low; - pc.pos2 = y_low * width + x_high; - pc.pos3 = y_high * width + x_low; - pc.pos4 = y_high * width + x_high; - pc.w1 = w1; - pc.w2 = w2; - pc.w3 = w3; - pc.w4 = w4; - pre_calc[pre_calc_index] = pc; - - pre_calc_index += 1; - } - } - } - } -} - -template -void ROIAlignForward(const int nthreads, const T* input, const T* rois, - T* output, T* argmax_y, T* argmax_x, - const int pooled_height, const int pooled_width, - const T spatial_scale, const int sampling_ratio, - const int pool_mode, // 0 - max pool, 1 - avg pool - const bool aligned, const int channels, const int height, - const int width) { - int n_rois = nthreads / channels / pooled_width / pooled_height; - // (n, c, ph, pw) is an element in the pooled output - // can be parallelized using omp - // #pragma omp parallel for num_threads(32) - for (int n = 0; n < n_rois; n++) { - int index_n = n * channels * pooled_width * pooled_height; - - const T* offset_rois = rois + n * 5; - int roi_batch_ind = offset_rois[0]; - - // Do not use rounding; this implementation detail is critical - T offset = aligned ? (T)0.5 : (T)0.0; - T roi_start_w = offset_rois[1] * spatial_scale - offset; - T roi_start_h = offset_rois[2] * spatial_scale - offset; - T roi_end_w = offset_rois[3] * spatial_scale - offset; - T roi_end_h = offset_rois[4] * spatial_scale - offset; - - T roi_width = roi_end_w - roi_start_w; - T roi_height = roi_end_h - roi_start_h; - if (aligned) { - AT_ASSERTM(roi_width >= 0 && roi_height >= 0, - "ROIs in ROIAlign cannot have non-negative size!"); - } else { // for backward-compatibility only - roi_width = std::max(roi_width, (T)1.); - roi_height = std::max(roi_height, (T)1.); - } - T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); - T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); - - // We use roi_bin_grid to sample the grid and mimic integral - int roi_bin_grid_h = (sampling_ratio > 0) - ? sampling_ratio - : ceilf(roi_height / pooled_height); // e.g., = 2 - int roi_bin_grid_w = - (sampling_ratio > 0) ? sampling_ratio : ceilf(roi_width / pooled_width); - - // When the grid is empty, output zeros == 0/1, instead of NaN. - const T count = std::max(roi_bin_grid_h * roi_bin_grid_w, 1); // e.g. = 4 - - // we want to precalculate indices and weights shared by all channels, - // this is the key point of optimization - std::vector> pre_calc(roi_bin_grid_h * roi_bin_grid_w * - pooled_width * pooled_height); - pre_calc_for_bilinear_interpolate( - height, width, pooled_height, pooled_width, roi_bin_grid_h, - roi_bin_grid_w, roi_start_h, roi_start_w, bin_size_h, bin_size_w, - roi_bin_grid_h, roi_bin_grid_w, pre_calc); - - for (int c = 0; c < channels; c++) { - int index_n_c = index_n + c * pooled_width * pooled_height; - const T* offset_input = - input + (roi_batch_ind * channels + c) * height * width; - int pre_calc_index = 0; - - for (int ph = 0; ph < pooled_height; ph++) { - for (int pw = 0; pw < pooled_width; pw++) { - int index = index_n_c + ph * pooled_width + pw; - - T output_val = 0.; - T maxval = -10000; - T maxidx_y = -1.f, maxidx_x = -1.f; - for (int iy = 0; iy < roi_bin_grid_h; iy++) { - const T y = roi_start_h + ph * bin_size_h + - static_cast(iy + .5f) * bin_size_h / - static_cast(roi_bin_grid_h); - for (int ix = 0; ix < roi_bin_grid_w; ix++) { - const T x = roi_start_w + pw * bin_size_w + - static_cast(ix + .5f) * bin_size_w / - static_cast(roi_bin_grid_w); - PreCalc pc = pre_calc[pre_calc_index]; - T val = pc.w1 * offset_input[pc.pos1] + - pc.w2 * offset_input[pc.pos2] + - pc.w3 * offset_input[pc.pos3] + - pc.w4 * offset_input[pc.pos4]; - if (val > maxval) { - maxval = val; - maxidx_y = y; - maxidx_x = x; - } - output_val += val; - pre_calc_index += 1; - } - } - if (pool_mode == 0) { - // We do max pooling inside a bin - output[index] = maxval; - argmax_y[index] = maxidx_y; - argmax_x[index] = maxidx_x; - } else if (pool_mode == 1) { - // We do average (integral) pooling inside a bin - output[index] = output_val / count; - } // if - } // for pw - } // for ph - } // for c - } // for n -} - -template -void bilinear_interpolate_gradient(const int height, const int width, T y, T x, - T& w1, T& w2, T& w3, T& w4, int& x_low, - int& x_high, int& y_low, int& y_high, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - w1 = w2 = w3 = w4 = 0.; - x_low = x_high = y_low = y_high = -1; - return; - } - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - y_low = (int)y; - x_low = (int)x; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - - // reference in forward - // T v1 = input[y_low * width + x_low]; - // T v2 = input[y_low * width + x_high]; - // T v3 = input[y_high * width + x_low]; - // T v4 = input[y_high * width + x_high]; - // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - return; -} - -template -inline void add(T* address, const T& val) { - *address += val; -} - -template -void ROIAlignBackward(const int nthreads, const T* grad_output, const T* rois, - const T* argmax_y, const T* argmax_x, T* grad_input, - const int pooled_height, const int pooled_width, - const T spatial_scale, const int sampling_ratio, - const int pool_mode, // 0 - max pool, 1 - avg pool - const bool aligned, const int channels, const int height, - const int width, const int n_stride, const int c_stride, - const int h_stride, const int w_stride) { - for (int index = 0; index < nthreads; index++) { - // (n, c, ph, pw) is an element in the pooled output - int pw = index % pooled_width; - int ph = (index / pooled_width) % pooled_height; - int c = (index / pooled_width / pooled_height) % channels; - int n = index / pooled_width / pooled_height / channels; - - const T* offset_rois = rois + n * 5; - int roi_batch_ind = offset_rois[0]; - - // Do not use rounding; this implementation detail is critical - T offset = aligned ? (T)0.5 : (T)0.0; - T roi_start_w = offset_rois[1] * spatial_scale - offset; - T roi_start_h = offset_rois[2] * spatial_scale - offset; - T roi_end_w = offset_rois[3] * spatial_scale - offset; - T roi_end_h = offset_rois[4] * spatial_scale - offset; - - T roi_width = roi_end_w - roi_start_w; - T roi_height = roi_end_h - roi_start_h; - if (aligned) { - AT_ASSERTM(roi_width >= 0 && roi_height >= 0, - "ROIs in ROIAlign do not have non-negative size!"); - } else { // for backward-compatibility only - roi_width = std::max(roi_width, (T)1.); - roi_height = std::max(roi_height, (T)1.); - } - T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); - T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); - - T* offset_grad_input = - grad_input + ((roi_batch_ind * channels + c) * height * width); - - int output_offset = n * n_stride + c * c_stride; - const T* offset_grad_output = grad_output + output_offset; - const T grad_output_this_bin = - offset_grad_output[ph * h_stride + pw * w_stride]; - - if (pool_mode == 0) { - // We do max pooling inside a bin - T y = argmax_y[index], x = argmax_x[index]; - if (y != -1.f) { - T w1, w2, w3, w4; - int x_low, x_high, y_low, y_high; - bilinear_interpolate_gradient(height, width, y, x, w1, w2, w3, w4, - x_low, x_high, y_low, y_high, index); - - T g1 = grad_output_this_bin * w1; - T g2 = grad_output_this_bin * w2; - T g3 = grad_output_this_bin * w3; - T g4 = grad_output_this_bin * w4; - - if (x_low >= 0 && x_high >= 0 && y_low >= 0 && y_high >= 0) { - // atomic add is not needed for now since it is single threaded - add(offset_grad_input + y_low * width + x_low, static_cast(g1)); - add(offset_grad_input + y_low * width + x_high, static_cast(g2)); - add(offset_grad_input + y_high * width + x_low, static_cast(g3)); - add(offset_grad_input + y_high * width + x_high, static_cast(g4)); - } // if - } // mode - } else if (pool_mode == 1) { - // We do average (integral) pooling inside a bin - // We use roi_bin_grid to sample the grid and mimic integral - int roi_bin_grid_h = - (sampling_ratio > 0) - ? sampling_ratio - : ceilf(roi_height / pooled_height); // e.g., = 2 - int roi_bin_grid_w = (sampling_ratio > 0) - ? sampling_ratio - : ceilf(roi_width / pooled_width); - - const T count = roi_bin_grid_h * roi_bin_grid_w; // e.g. = 4 - for (int iy = 0; iy < roi_bin_grid_h; iy++) { - const T y = roi_start_h + ph * bin_size_h + - static_cast(iy + .5f) * bin_size_h / - static_cast(roi_bin_grid_h); // e.g., 0.5, 1.5 - for (int ix = 0; ix < roi_bin_grid_w; ix++) { - const T x = roi_start_w + pw * bin_size_w + - static_cast(ix + .5f) * bin_size_w / - static_cast(roi_bin_grid_w); - - T w1, w2, w3, w4; - int x_low, x_high, y_low, y_high; - - bilinear_interpolate_gradient(height, width, y, x, w1, w2, w3, w4, - x_low, x_high, y_low, y_high, index); - - T g1 = grad_output_this_bin * w1 / count; - T g2 = grad_output_this_bin * w2 / count; - T g3 = grad_output_this_bin * w3 / count; - T g4 = grad_output_this_bin * w4 / count; - - if (x_low >= 0 && x_high >= 0 && y_low >= 0 && y_high >= 0) { - // atomic add is not needed for now since it is single threaded - add(offset_grad_input + y_low * width + x_low, static_cast(g1)); - add(offset_grad_input + y_low * width + x_high, static_cast(g2)); - add(offset_grad_input + y_high * width + x_low, static_cast(g3)); - add(offset_grad_input + y_high * width + x_high, - static_cast(g4)); - } // if - } // ix - } // iy - } // mode - } // for -} // ROIAlignBackward - -void ROIAlignForwardCPULauncher(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned) { - int output_size = output.numel(); - int channels = input.size(1); - int height = input.size(2); - int width = input.size(3); - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "ROIAlign_forward", [&] { - ROIAlignForward( - output_size, input.data_ptr(), rois.data_ptr(), - output.data_ptr(), argmax_y.data_ptr(), - argmax_x.data_ptr(), aligned_height, aligned_width, - static_cast(spatial_scale), sampling_ratio, pool_mode, - aligned, channels, height, width); - }); -} - -void ROIAlignBackwardCPULauncher(Tensor grad_output, Tensor rois, - Tensor argmax_y, Tensor argmax_x, - Tensor grad_input, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, - bool aligned) { - int output_size = grad_output.numel(); - int channels = grad_input.size(1); - int height = grad_input.size(2); - int width = grad_input.size(3); - - // get stride values to ensure indexing into gradients is correct. - int n_stride = grad_output.stride(0); - int c_stride = grad_output.stride(1); - int h_stride = grad_output.stride(2); - int w_stride = grad_output.stride(3); - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "ROIAlign_backward", [&] { - ROIAlignBackward( - output_size, grad_output.data_ptr(), - rois.data_ptr(), argmax_y.data_ptr(), - argmax_x.data_ptr(), grad_input.data_ptr(), - aligned_height, aligned_width, static_cast(spatial_scale), - sampling_ratio, pool_mode, aligned, channels, height, width, - n_stride, c_stride, h_stride, w_stride); - }); -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/roi_pool.cpp b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/roi_pool.cpp deleted file mode 100644 index 34c4b996b..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/roi_pool.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -void ROIPoolForwardCUDAKernelLauncher(Tensor input, Tensor rois, Tensor output, - Tensor argmax, int pooled_height, - int pooled_width, float spatial_scale); - -void ROIPoolBackwardCUDAKernelLauncher(Tensor grad_output, Tensor rois, - Tensor argmax, Tensor grad_input, - int pooled_height, int pooled_width, - float spatial_scale); - -void roi_pool_forward_cuda(Tensor input, Tensor rois, Tensor output, - Tensor argmax, int pooled_height, int pooled_width, - float spatial_scale) { - ROIPoolForwardCUDAKernelLauncher(input, rois, output, argmax, pooled_height, - pooled_width, spatial_scale); -} - -void roi_pool_backward_cuda(Tensor grad_output, Tensor rois, Tensor argmax, - Tensor grad_input, int pooled_height, - int pooled_width, float spatial_scale) { - ROIPoolBackwardCUDAKernelLauncher(grad_output, rois, argmax, grad_input, - pooled_height, pooled_width, spatial_scale); -} -#endif - -void roi_pool_forward(Tensor input, Tensor rois, Tensor output, Tensor argmax, - int pooled_height, int pooled_width, - float spatial_scale) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(rois); - CHECK_CUDA_INPUT(output); - CHECK_CUDA_INPUT(argmax); - - roi_pool_forward_cuda(input, rois, output, argmax, pooled_height, - pooled_width, spatial_scale); -#else - AT_ERROR("RoIPool is not compiled with GPU support"); -#endif - } else { - AT_ERROR("RoIPool is not implemented on CPU"); - } -} - -void roi_pool_backward(Tensor grad_output, Tensor rois, Tensor argmax, - Tensor grad_input, int pooled_height, int pooled_width, - float spatial_scale) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(rois); - CHECK_CUDA_INPUT(argmax); - CHECK_CUDA_INPUT(grad_input); - - roi_pool_backward_cuda(grad_output, rois, argmax, grad_input, pooled_height, - pooled_width, spatial_scale); -#else - AT_ERROR("RoIPool is not compiled with GPU support"); -#endif - } else { - AT_ERROR("RoIPool is not implemented on CPU"); - } -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp b/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp deleted file mode 100644 index 2e023a859..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean); - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var); - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size); - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias); - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -void sync_bn_forward_mean_cuda(const Tensor input, Tensor mean) { - SyncBNForwardMeanCUDAKernelLauncher(input, mean); -} - -void sync_bn_forward_var_cuda(const Tensor input, const Tensor mean, - Tensor var) { - SyncBNForwardVarCUDAKernelLauncher(input, mean, var); -} - -void sync_bn_forward_output_cuda(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - SyncBNForwardOutputCUDAKernelLauncher(input, mean, var, running_mean, - running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_cuda(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - SyncBNBackwardParamCUDAKernelLauncher(grad_output, norm, grad_weight, - grad_bias); -} - -void sync_bn_backward_data_cuda(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - SyncBNBackwardDataCUDAKernelLauncher(grad_output, weight, grad_weight, - grad_bias, norm, std, grad_input); -} -#endif - -void sync_bn_forward_mean(const Tensor input, Tensor mean) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - sync_bn_forward_mean_cuda(input, mean); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - CHECK_CUDA_INPUT(var); - sync_bn_forward_var_cuda(input, mean, var); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - CHECK_CUDA_INPUT(var); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(bias); - CHECK_CUDA_INPUT(running_mean); - CHECK_CUDA_INPUT(running_var); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(std); - CHECK_CUDA_INPUT(output); - sync_bn_forward_output_cuda(input, mean, var, running_mean, running_var, - weight, bias, norm, std, output, eps, momentum, - group_size); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(grad_weight); - CHECK_CUDA_INPUT(grad_bias); - sync_bn_backward_param_cuda(grad_output, norm, grad_weight, grad_bias); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(grad_weight); - CHECK_CUDA_INPUT(grad_bias); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(std); - CHECK_CUDA_INPUT(grad_input); - sync_bn_backward_data_cuda(grad_output, weight, grad_weight, grad_bias, - norm, std, grad_input); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/deprecated_wrappers.py b/cv/instance_segmentation/solo/pytorch/mmcv/ops/deprecated_wrappers.py deleted file mode 100644 index a2e593df9..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/deprecated_wrappers.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# This file is for backward compatibility. -# Module wrappers for empty tensor have been moved to mmcv.cnn.bricks. -import warnings - -from ..cnn.bricks.wrappers import Conv2d, ConvTranspose2d, Linear, MaxPool2d - - -class Conv2d_deprecated(Conv2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Conv2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') - - -class ConvTranspose2d_deprecated(ConvTranspose2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing ConvTranspose2d wrapper from "mmcv.ops" will be ' - 'deprecated in the future. Please import them from "mmcv.cnn" ' - 'instead') - - -class MaxPool2d_deprecated(MaxPool2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing MaxPool2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') - - -class Linear_deprecated(Linear): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Linear wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/focal_loss.py b/cv/instance_segmentation/solo/pytorch/mmcv/ops/focal_loss.py deleted file mode 100644 index 763bc93bd..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/focal_loss.py +++ /dev/null @@ -1,212 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sigmoid_focal_loss_forward', 'sigmoid_focal_loss_backward', - 'softmax_focal_loss_forward', 'softmax_focal_loss_backward' -]) - - -class SigmoidFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSigmoidFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - output = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_forward( - input, target, weight, output, gamma=ctx.gamma, alpha=ctx.alpha) - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input, target, weight) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - input, target, weight = ctx.saved_tensors - - grad_input = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_backward( - input, - target, - weight, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input.size(0) - return grad_input, None, None, None, None, None - - -sigmoid_focal_loss = SigmoidFocalLossFunction.apply - - -class SigmoidFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SigmoidFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return sigmoid_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s - - -class SoftmaxFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSoftmaxFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - channel_stats, _ = torch.max(input, dim=1) - input_softmax = input - channel_stats.unsqueeze(1).expand_as(input) - input_softmax.exp_() - - channel_stats = input_softmax.sum(dim=1) - input_softmax /= channel_stats.unsqueeze(1).expand_as(input) - - output = input.new_zeros(input.size(0)) - ext_module.softmax_focal_loss_forward( - input_softmax, - target, - weight, - output, - gamma=ctx.gamma, - alpha=ctx.alpha) - - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input_softmax, target, weight) - return output - - @staticmethod - def backward(ctx, grad_output): - input_softmax, target, weight = ctx.saved_tensors - buff = input_softmax.new_zeros(input_softmax.size(0)) - grad_input = input_softmax.new_zeros(input_softmax.size()) - - ext_module.softmax_focal_loss_backward( - input_softmax, - target, - weight, - buff, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input_softmax.size(0) - return grad_input, None, None, None, None, None - - -softmax_focal_loss = SoftmaxFocalLossFunction.apply - - -class SoftmaxFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SoftmaxFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return softmax_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/info.py b/cv/instance_segmentation/solo/pytorch/mmcv/ops/info.py deleted file mode 100644 index 29f2e5598..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/info.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os - -import torch - -if torch.__version__ == 'parrots': - import parrots - - def get_compiler_version(): - return 'GCC ' + parrots.version.compiler - - def get_compiling_cuda_version(): - return parrots.version.cuda -else: - from ..utils import ext_loader - ext_module = ext_loader.load_ext( - '_ext', ['get_compiler_version', 'get_compiling_cuda_version']) - - def get_compiler_version(): - return ext_module.get_compiler_version() - - def get_compiling_cuda_version(): - return ext_module.get_compiling_cuda_version() - - -def get_onnxruntime_op_path(): - wildcard = os.path.join( - os.path.abspath(os.path.dirname(os.path.dirname(__file__))), - '_ext_ort.*.so') - - paths = glob.glob(wildcard) - if len(paths) > 0: - return paths[0] - else: - return '' diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/nms.py b/cv/instance_segmentation/solo/pytorch/mmcv/ops/nms.py deleted file mode 100644 index 40ff7bee7..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/nms.py +++ /dev/null @@ -1,417 +0,0 @@ -import os - -import numpy as np -import torch - -from mmcv.utils import deprecated_api_warning -from ..utils import ext_loader - -ext_module = ext_loader.load_ext( - '_ext', ['nms', 'softnms', 'nms_match']) - - -# This function is modified from: https://github.com/pytorch/vision/ -class NMSop(torch.autograd.Function): - - @staticmethod - def forward(ctx, bboxes, scores, iou_threshold, offset, score_threshold, - max_num): - is_filtering_by_score = score_threshold > 0 - if is_filtering_by_score: - valid_mask = scores > score_threshold - bboxes, scores = bboxes[valid_mask], scores[valid_mask] - valid_inds = torch.nonzero( - valid_mask, as_tuple=False).squeeze(dim=1) - - inds = ext_module.nms( - bboxes, scores, iou_threshold=float(iou_threshold), offset=offset) - - if max_num > 0: - inds = inds[:max_num] - if is_filtering_by_score: - inds = valid_inds[inds] - return inds - - @staticmethod - def symbolic(g, bboxes, scores, iou_threshold, offset, score_threshold, - max_num): - from ..onnx import is_custom_op_loaded - has_custom_op = is_custom_op_loaded() - # TensorRT nms plugin is aligned with original nms in ONNXRuntime - is_trt_backend = os.environ.get('ONNX_BACKEND') == 'MMCVTensorRT' - if has_custom_op and (not is_trt_backend): - return g.op( - 'mmcv::NonMaxSuppression', - bboxes, - scores, - iou_threshold_f=float(iou_threshold), - offset_i=int(offset)) - else: - from torch.onnx.symbolic_opset9 import select, squeeze, unsqueeze - from ..onnx.onnx_utils.symbolic_helper import _size_helper - - boxes = unsqueeze(g, bboxes, 0) - scores = unsqueeze(g, unsqueeze(g, scores, 0), 0) - - if max_num > 0: - max_num = g.op( - 'Constant', - value_t=torch.tensor(max_num, dtype=torch.long)) - else: - dim = g.op('Constant', value_t=torch.tensor(0)) - max_num = _size_helper(g, bboxes, dim) - max_output_per_class = max_num - iou_threshold = g.op( - 'Constant', - value_t=torch.tensor([iou_threshold], dtype=torch.float)) - score_threshold = g.op( - 'Constant', - value_t=torch.tensor([score_threshold], dtype=torch.float)) - nms_out = g.op('NonMaxSuppression', boxes, scores, - max_output_per_class, iou_threshold, - score_threshold) - return squeeze( - g, - select( - g, nms_out, 1, - g.op( - 'Constant', - value_t=torch.tensor([2], dtype=torch.long))), 1) - - -class SoftNMSop(torch.autograd.Function): - - @staticmethod - def forward(ctx, boxes, scores, iou_threshold, sigma, min_score, method, - offset): - dets = boxes.new_empty((boxes.size(0), 5), device='cpu') - inds = ext_module.softnms( - boxes.cpu(), - scores.cpu(), - dets.cpu(), - iou_threshold=float(iou_threshold), - sigma=float(sigma), - min_score=float(min_score), - method=int(method), - offset=int(offset)) - return dets, inds - - @staticmethod - def symbolic(g, boxes, scores, iou_threshold, sigma, min_score, method, - offset): - from packaging import version - assert version.parse(torch.__version__) >= version.parse('1.7.0') - nms_out = g.op( - 'mmcv::SoftNonMaxSuppression', - boxes, - scores, - iou_threshold_f=float(iou_threshold), - sigma_f=float(sigma), - min_score_f=float(min_score), - method_i=int(method), - offset_i=int(offset), - outputs=2) - return nms_out - - -@deprecated_api_warning({'iou_thr': 'iou_threshold'}) -def nms(boxes, scores, iou_threshold, offset=0, score_threshold=0, max_num=-1): - """Dispatch to either CPU or GPU NMS implementations. - - The input can be either torch tensor or numpy array. GPU NMS will be used - if the input is gpu tensor, otherwise CPU NMS - will be used. The returned type will always be the same as inputs. - - Arguments: - boxes (torch.Tensor or np.ndarray): boxes in shape (N, 4). - scores (torch.Tensor or np.ndarray): scores in shape (N, ). - iou_threshold (float): IoU threshold for NMS. - offset (int, 0 or 1): boxes' width or height is (x2 - x1 + offset). - score_threshold (float): score threshold for NMS. - max_num (int): maximum number of boxes after NMS. - - Returns: - tuple: kept dets(boxes and scores) and indice, which is always the \ - same data type as the input. - - Example: - >>> boxes = np.array([[49.1, 32.4, 51.0, 35.9], - >>> [49.3, 32.9, 51.0, 35.3], - >>> [49.2, 31.8, 51.0, 35.4], - >>> [35.1, 11.5, 39.1, 15.7], - >>> [35.6, 11.8, 39.3, 14.2], - >>> [35.3, 11.5, 39.9, 14.5], - >>> [35.2, 11.7, 39.7, 15.7]], dtype=np.float32) - >>> scores = np.array([0.9, 0.9, 0.5, 0.5, 0.5, 0.4, 0.3],\ - dtype=np.float32) - >>> iou_threshold = 0.6 - >>> dets, inds = nms(boxes, scores, iou_threshold) - >>> assert len(inds) == len(dets) == 3 - """ - assert isinstance(boxes, (torch.Tensor, np.ndarray)) - assert isinstance(scores, (torch.Tensor, np.ndarray)) - is_numpy = False - if isinstance(boxes, np.ndarray): - is_numpy = True - boxes = torch.from_numpy(boxes) - if isinstance(scores, np.ndarray): - scores = torch.from_numpy(scores) - assert boxes.size(1) == 4 - assert boxes.size(0) == scores.size(0) - assert offset in (0, 1) - - if torch.__version__ == 'parrots': - indata_list = [boxes, scores] - indata_dict = { - 'iou_threshold': float(iou_threshold), - 'offset': int(offset) - } - inds = ext_module.nms(*indata_list, **indata_dict) - else: - inds = NMSop.apply(boxes, scores, iou_threshold, offset, - score_threshold, max_num) - dets = torch.cat((boxes[inds], scores[inds].reshape(-1, 1)), dim=1) - if is_numpy: - dets = dets.cpu().numpy() - inds = inds.cpu().numpy() - return dets, inds - - -@deprecated_api_warning({'iou_thr': 'iou_threshold'}) -def soft_nms(boxes, - scores, - iou_threshold=0.3, - sigma=0.5, - min_score=1e-3, - method='linear', - offset=0): - """Dispatch to only CPU Soft NMS implementations. - - The input can be either a torch tensor or numpy array. - The returned type will always be the same as inputs. - - Arguments: - boxes (torch.Tensor or np.ndarray): boxes in shape (N, 4). - scores (torch.Tensor or np.ndarray): scores in shape (N, ). - iou_threshold (float): IoU threshold for NMS. - sigma (float): hyperparameter for gaussian method - min_score (float): score filter threshold - method (str): either 'linear' or 'gaussian' - offset (int, 0 or 1): boxes' width or height is (x2 - x1 + offset). - - Returns: - tuple: kept dets(boxes and scores) and indice, which is always the \ - same data type as the input. - - Example: - >>> boxes = np.array([[4., 3., 5., 3.], - >>> [4., 3., 5., 4.], - >>> [3., 1., 3., 1.], - >>> [3., 1., 3., 1.], - >>> [3., 1., 3., 1.], - >>> [3., 1., 3., 1.]], dtype=np.float32) - >>> scores = np.array([0.9, 0.9, 0.5, 0.5, 0.4, 0.0], dtype=np.float32) - >>> iou_threshold = 0.6 - >>> dets, inds = soft_nms(boxes, scores, iou_threshold, sigma=0.5) - >>> assert len(inds) == len(dets) == 5 - """ - - assert isinstance(boxes, (torch.Tensor, np.ndarray)) - assert isinstance(scores, (torch.Tensor, np.ndarray)) - is_numpy = False - if isinstance(boxes, np.ndarray): - is_numpy = True - boxes = torch.from_numpy(boxes) - if isinstance(scores, np.ndarray): - scores = torch.from_numpy(scores) - assert boxes.size(1) == 4 - assert boxes.size(0) == scores.size(0) - assert offset in (0, 1) - method_dict = {'naive': 0, 'linear': 1, 'gaussian': 2} - assert method in method_dict.keys() - - if torch.__version__ == 'parrots': - dets = boxes.new_empty((boxes.size(0), 5), device='cpu') - indata_list = [boxes.cpu(), scores.cpu(), dets.cpu()] - indata_dict = { - 'iou_threshold': float(iou_threshold), - 'sigma': float(sigma), - 'min_score': min_score, - 'method': method_dict[method], - 'offset': int(offset) - } - inds = ext_module.softnms(*indata_list, **indata_dict) - else: - dets, inds = SoftNMSop.apply(boxes.cpu(), scores.cpu(), - float(iou_threshold), float(sigma), - float(min_score), method_dict[method], - int(offset)) - - dets = dets[:inds.size(0)] - - if is_numpy: - dets = dets.cpu().numpy() - inds = inds.cpu().numpy() - return dets, inds - else: - return dets.to(device=boxes.device), inds.to(device=boxes.device) - - -def batched_nms(boxes, scores, idxs, nms_cfg, class_agnostic=False): - """Performs non-maximum suppression in a batched fashion. - - Modified from https://github.com/pytorch/vision/blob - /505cd6957711af790211896d32b40291bea1bc21/torchvision/ops/boxes.py#L39. - In order to perform NMS independently per class, we add an offset to all - the boxes. The offset is dependent only on the class idx, and is large - enough so that boxes from different classes do not overlap. - - Arguments: - boxes (torch.Tensor): boxes in shape (N, 4). - scores (torch.Tensor): scores in shape (N, ). - idxs (torch.Tensor): each index value correspond to a bbox cluster, - and NMS will not be applied between elements of different idxs, - shape (N, ). - nms_cfg (dict): specify nms type and other parameters like iou_thr. - Possible keys includes the following. - - - iou_thr (float): IoU threshold used for NMS. - - split_thr (float): threshold number of boxes. In some cases the - number of boxes is large (e.g., 200k). To avoid OOM during - training, the users could set `split_thr` to a small value. - If the number of boxes is greater than the threshold, it will - perform NMS on each group of boxes separately and sequentially. - Defaults to 10000. - class_agnostic (bool): if true, nms is class agnostic, - i.e. IoU thresholding happens over all boxes, - regardless of the predicted class. - - Returns: - tuple: kept dets and indice. - """ - nms_cfg_ = nms_cfg.copy() - class_agnostic = nms_cfg_.pop('class_agnostic', class_agnostic) - if class_agnostic: - boxes_for_nms = boxes - else: - max_coordinate = boxes.max() - offsets = idxs.to(boxes) * (max_coordinate + torch.tensor(1).to(boxes)) - boxes_for_nms = boxes + offsets[:, None] - - nms_type = nms_cfg_.pop('type', 'nms') - nms_op = eval(nms_type) - - split_thr = nms_cfg_.pop('split_thr', 10000) - # Won't split to multiple nms nodes when exporting to onnx - if boxes_for_nms.shape[0] < split_thr or torch.onnx.is_in_onnx_export(): - dets, keep = nms_op(boxes_for_nms, scores, **nms_cfg_) - boxes = boxes[keep] - # -1 indexing works abnormal in TensorRT - # This assumes `dets` has 5 dimensions where - # the last dimension is score. - # TODO: more elegant way to handle the dimension issue. - # Some type of nms would reweight the score, such as SoftNMS - scores = dets[:, 4] - else: - max_num = nms_cfg_.pop('max_num', -1) - total_mask = scores.new_zeros(scores.size(), dtype=torch.bool) - # Some type of nms would reweight the score, such as SoftNMS - scores_after_nms = scores.new_zeros(scores.size()) - for id in torch.unique(idxs): - mask = (idxs == id).nonzero(as_tuple=False).view(-1) - dets, keep = nms_op(boxes_for_nms[mask], scores[mask], **nms_cfg_) - total_mask[mask[keep]] = True - scores_after_nms[mask[keep]] = dets[:, -1] - keep = total_mask.nonzero(as_tuple=False).view(-1) - - scores, inds = scores_after_nms[keep].sort(descending=True) - keep = keep[inds] - boxes = boxes[keep] - - if max_num > 0: - keep = keep[:max_num] - boxes = boxes[:max_num] - scores = scores[:max_num] - - return torch.cat([boxes, scores[:, None]], -1), keep - - -def nms_match(dets, iou_threshold): - """Matched dets into different groups by NMS. - - NMS match is Similar to NMS but when a bbox is suppressed, nms match will - record the indice of suppressed bbox and form a group with the indice of - kept bbox. In each group, indice is sorted as score order. - - Arguments: - dets (torch.Tensor | np.ndarray): Det boxes with scores, shape (N, 5). - iou_thr (float): IoU thresh for NMS. - - Returns: - List[torch.Tensor | np.ndarray]: The outer list corresponds different - matched group, the inner Tensor corresponds the indices for a group - in score order. - """ - if dets.shape[0] == 0: - matched = [] - else: - assert dets.shape[-1] == 5, 'inputs dets.shape should be (N, 5), ' \ - f'but get {dets.shape}' - if isinstance(dets, torch.Tensor): - dets_t = dets.detach().cpu() - else: - dets_t = torch.from_numpy(dets) - indata_list = [dets_t] - indata_dict = {'iou_threshold': float(iou_threshold)} - matched = ext_module.nms_match(*indata_list, **indata_dict) - if torch.__version__ == 'parrots': - matched = matched.tolist() - - if isinstance(dets, torch.Tensor): - return [dets.new_tensor(m, dtype=torch.long) for m in matched] - else: - return [np.array(m, dtype=np.int) for m in matched] - - -def nms_rotated(dets, scores, iou_threshold, labels=None): - """Performs non-maximum suppression (NMS) on the rotated boxes according to - their intersection-over-union (IoU). - - Rotated NMS iteratively removes lower scoring rotated boxes which have an - IoU greater than iou_threshold with another (higher scoring) rotated box. - - Args: - boxes (Tensor): Rotated boxes in shape (N, 5). They are expected to \ - be in (x_ctr, y_ctr, width, height, angle_radian) format. - scores (Tensor): scores in shape (N, ). - iou_threshold (float): IoU thresh for NMS. - labels (Tensor): boxes' label in shape (N,). - - Returns: - tuple: kept dets(boxes and scores) and indice, which is always the \ - same data type as the input. - """ - if dets.shape[0] == 0: - return dets, None - multi_label = labels is not None - if multi_label: - dets_wl = torch.cat((dets, labels.unsqueeze(1)), 1) - else: - dets_wl = dets - _, order = scores.sort(0, descending=True) - dets_sorted = dets_wl.index_select(0, order) - - if torch.__version__ == 'parrots': - keep_inds = ext_module.nms_rotated( - dets_wl, - scores, - order, - dets_sorted, - iou_threshold=iou_threshold, - multi_label=multi_label) - else: - keep_inds = ext_module.nms_rotated(dets_wl, scores, order, dets_sorted, - iou_threshold, multi_label) - dets = torch.cat((dets[keep_inds], scores[keep_inds].reshape(-1, 1)), - dim=1) - return dets, keep_inds diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/point_sample.py b/cv/instance_segmentation/solo/pytorch/mmcv/ops/point_sample.py deleted file mode 100644 index c084a8c22..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/point_sample.py +++ /dev/null @@ -1,336 +0,0 @@ -# Modified from https://github.com/facebookresearch/detectron2/tree/master/projects/PointRend # noqa - -from os import path as osp - -import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.nn.modules.utils import _pair -from torch.onnx.operators import shape_as_tensor - - -def bilinear_grid_sample(im, grid, align_corners=False): - """Given an input and a flow-field grid, computes the output using input - values and pixel locations from grid. Supported only bilinear interpolation - method to sample the input pixels. - - Args: - im (torch.Tensor): Input feature map, shape (N, C, H, W) - grid (torch.Tensor): Point coordinates, shape (N, Hg, Wg, 2) - align_corners {bool}: If set to True, the extrema (-1 and 1) are - considered as referring to the center points of the input’s - corner pixels. If set to False, they are instead considered as - referring to the corner points of the input’s corner pixels, - making the sampling more resolution agnostic. - Returns: - torch.Tensor: A tensor with sampled points, shape (N, C, Hg, Wg) - """ - n, c, h, w = im.shape - gn, gh, gw, _ = grid.shape - assert n == gn - - x = grid[:, :, :, 0] - y = grid[:, :, :, 1] - - if align_corners: - x = ((x + 1) / 2) * (w - 1) - y = ((y + 1) / 2) * (h - 1) - else: - x = ((x + 1) * w - 1) / 2 - y = ((y + 1) * h - 1) / 2 - - x = x.view(n, -1) - y = y.view(n, -1) - - x0 = torch.floor(x).long() - y0 = torch.floor(y).long() - x1 = x0 + 1 - y1 = y0 + 1 - - wa = ((x1 - x) * (y1 - y)).unsqueeze(1) - wb = ((x1 - x) * (y - y0)).unsqueeze(1) - wc = ((x - x0) * (y1 - y)).unsqueeze(1) - wd = ((x - x0) * (y - y0)).unsqueeze(1) - - # Apply default for grid_sample function zero padding - im_padded = F.pad(im, pad=[1, 1, 1, 1], mode='constant', value=0) - padded_h = h + 2 - padded_w = w + 2 - # save points positions after padding - x0, x1, y0, y1 = x0 + 1, x1 + 1, y0 + 1, y1 + 1 - - # Clip coordinates to padded image size - x0 = torch.where(x0 < 0, torch.tensor(0), x0) - x0 = torch.where(x0 > padded_w - 1, torch.tensor(padded_w - 1), x0) - x1 = torch.where(x1 < 0, torch.tensor(0), x1) - x1 = torch.where(x1 > padded_w - 1, torch.tensor(padded_w - 1), x1) - y0 = torch.where(y0 < 0, torch.tensor(0), y0) - y0 = torch.where(y0 > padded_h - 1, torch.tensor(padded_h - 1), y0) - y1 = torch.where(y1 < 0, torch.tensor(0), y1) - y1 = torch.where(y1 > padded_h - 1, torch.tensor(padded_h - 1), y1) - - im_padded = im_padded.view(n, c, -1) - - x0_y0 = (x0 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x0_y1 = (x0 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y0 = (x1 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y1 = (x1 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - - Ia = torch.gather(im_padded, 2, x0_y0) - Ib = torch.gather(im_padded, 2, x0_y1) - Ic = torch.gather(im_padded, 2, x1_y0) - Id = torch.gather(im_padded, 2, x1_y1) - - return (Ia * wa + Ib * wb + Ic * wc + Id * wd).reshape(n, c, gh, gw) - - -def is_in_onnx_export_without_custom_ops(): - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - return torch.onnx.is_in_onnx_export( - ) and not osp.exists(ort_custom_op_path) - - -def normalize(grid): - """Normalize input grid from [-1, 1] to [0, 1] - Args: - grid (Tensor): The grid to be normalize, range [-1, 1]. - Returns: - Tensor: Normalized grid, range [0, 1]. - """ - - return (grid + 1.0) / 2.0 - - -def denormalize(grid): - """Denormalize input grid from range [0, 1] to [-1, 1] - Args: - grid (Tensor): The grid to be denormalize, range [0, 1]. - Returns: - Tensor: Denormalized grid, range [-1, 1]. - """ - - return grid * 2.0 - 1.0 - - -def generate_grid(num_grid, size, device): - """Generate regular square grid of points in [0, 1] x [0, 1] coordinate - space. - - Args: - num_grid (int): The number of grids to sample, one for each region. - size (tuple(int, int)): The side size of the regular grid. - device (torch.device): Desired device of returned tensor. - - Returns: - (torch.Tensor): A tensor of shape (num_grid, size[0]*size[1], 2) that - contains coordinates for the regular grids. - """ - - affine_trans = torch.tensor([[[1., 0., 0.], [0., 1., 0.]]], device=device) - grid = F.affine_grid( - affine_trans, torch.Size((1, 1, *size)), align_corners=False) - grid = normalize(grid) - return grid.view(1, -1, 2).expand(num_grid, -1, -1) - - -def rel_roi_point_to_abs_img_point(rois, rel_roi_points): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (Tensor): Point coordinates inside RoI, relative to - RoI, location, range (0, 1), shape (N, P, 2) - Returns: - Tensor: Image based absolute point coordinates, shape (N, P, 2) - """ - - with torch.no_grad(): - assert rel_roi_points.size(0) == rois.size(0) - assert rois.dim() == 2 - assert rel_roi_points.dim() == 3 - assert rel_roi_points.size(2) == 2 - # remove batch idx - if rois.size(1) == 5: - rois = rois[:, 1:] - abs_img_points = rel_roi_points.clone() - # To avoid an error during exporting to onnx use independent - # variables instead inplace computation - xs = abs_img_points[:, :, 0] * (rois[:, None, 2] - rois[:, None, 0]) - ys = abs_img_points[:, :, 1] * (rois[:, None, 3] - rois[:, None, 1]) - xs += rois[:, None, 0] - ys += rois[:, None, 1] - abs_img_points = torch.stack([xs, ys], dim=2) - return abs_img_points - - -def get_shape_from_feature_map(x): - """Get spatial resolution of input feature map considering exporting to - onnx mode. - - Args: - x (torch.Tensor): Input tensor, shape (N, C, H, W) - Returns: - torch.Tensor: Spatial resolution (width, height), shape (1, 1, 2) - """ - if torch.onnx.is_in_onnx_export(): - img_shape = shape_as_tensor(x)[2:].flip(0).view(1, 1, 2).to( - x.device).float() - else: - img_shape = torch.tensor(x.shape[2:]).flip(0).view(1, 1, 2).to( - x.device).float() - return img_shape - - -def abs_img_point_to_rel_img_point(abs_img_points, img, spatial_scale=1.): - """Convert image based absolute point coordinates to image based relative - coordinates for sampling. - - Args: - abs_img_points (Tensor): Image based absolute point coordinates, - shape (N, P, 2) - img (tuple/Tensor): (height, width) of image or feature map. - spatial_scale (float): Scale points by this factor. Default: 1. - - Returns: - Tensor: Image based relative point coordinates for sampling, - shape (N, P, 2) - """ - - assert (isinstance(img, tuple) and len(img) == 2) or \ - (isinstance(img, torch.Tensor) and len(img.shape) == 4) - - if isinstance(img, tuple): - h, w = img - scale = torch.tensor([w, h], - dtype=torch.float, - device=abs_img_points.device) - scale = scale.view(1, 1, 2) - else: - scale = get_shape_from_feature_map(img) - - return abs_img_points / scale * spatial_scale - - -def rel_roi_point_to_rel_img_point(rois, - rel_roi_points, - img, - spatial_scale=1.): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (Tensor): Point coordinates inside RoI, relative to - RoI, location, range (0, 1), shape (N, P, 2) - img (tuple/Tensor): (height, width) of image or feature map. - spatial_scale (float): Scale points by this factor. Default: 1. - - Returns: - Tensor: Image based relative point coordinates for sampling, - shape (N, P, 2) - """ - - abs_img_point = rel_roi_point_to_abs_img_point(rois, rel_roi_points) - rel_img_point = abs_img_point_to_rel_img_point(abs_img_point, img, - spatial_scale) - - return rel_img_point - - -def point_sample(input, points, align_corners=False, **kwargs): - """A wrapper around :func:`grid_sample` to support 3D point_coords tensors - Unlike :func:`torch.nn.functional.grid_sample` it assumes point_coords to - lie inside ``[0, 1] x [0, 1]`` square. - - Args: - input (Tensor): Feature map, shape (N, C, H, W). - points (Tensor): Image based absolute point coordinates (normalized), - range [0, 1] x [0, 1], shape (N, P, 2) or (N, Hgrid, Wgrid, 2). - align_corners (bool): Whether align_corners. Default: False - - Returns: - Tensor: Features of `point` on `input`, shape (N, C, P) or - (N, C, Hgrid, Wgrid). - """ - - add_dim = False - if points.dim() == 3: - add_dim = True - points = points.unsqueeze(2) - if is_in_onnx_export_without_custom_ops(): - # If custom ops for onnx runtime not compiled use python - # implementation of grid_sample function to make onnx graph - # with supported nodes - output = bilinear_grid_sample( - input, denormalize(points), align_corners=align_corners) - else: - output = F.grid_sample( - input, denormalize(points), align_corners=align_corners, **kwargs) - if add_dim: - output = output.squeeze(3) - return output - - -class SimpleRoIAlign(nn.Module): - - def __init__(self, output_size, spatial_scale, aligned=True): - """Simple RoI align in PointRend, faster than standard RoIAlign. - - Args: - output_size (tuple[int]): h, w - spatial_scale (float): scale the input boxes by this number - aligned (bool): if False, use the legacy implementation in - MMDetection, align_corners=True will be used in F.grid_sample. - If True, align the results more perfectly. - """ - - super(SimpleRoIAlign, self).__init__() - self.output_size = _pair(output_size) - self.spatial_scale = float(spatial_scale) - # to be consistent with other RoI ops - self.use_torchvision = False - self.aligned = aligned - - def forward(self, features, rois): - num_imgs = features.size(0) - num_rois = rois.size(0) - rel_roi_points = generate_grid( - num_rois, self.output_size, device=rois.device) - - if torch.onnx.is_in_onnx_export(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois, rel_roi_points, features, self.spatial_scale) - rel_img_points = rel_img_points.reshape(num_imgs, -1, - *rel_img_points.shape[1:]) - point_feats = point_sample( - features, rel_img_points, align_corners=not self.aligned) - point_feats = point_feats.transpose(1, 2) - else: - point_feats = [] - for batch_ind in range(num_imgs): - # unravel batch dim - feat = features[batch_ind].unsqueeze(0) - inds = (rois[:, 0].long() == batch_ind) - if inds.any(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois[inds], rel_roi_points[inds], feat, - self.spatial_scale).unsqueeze(0) - point_feat = point_sample( - feat, rel_img_points, align_corners=not self.aligned) - point_feat = point_feat.squeeze(0).transpose(0, 1) - point_feats.append(point_feat) - - point_feats = torch.cat(point_feats, dim=0) - - channels = features.size(1) - roi_feats = point_feats.reshape(num_rois, channels, *self.output_size) - - return roi_feats - - def __repr__(self): - format_str = self.__class__.__name__ - format_str += '(output_size={}, spatial_scale={}'.format( - self.output_size, self.spatial_scale) - return format_str diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/roi_align.py b/cv/instance_segmentation/solo/pytorch/mmcv/ops/roi_align.py deleted file mode 100644 index 0755aefc6..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/roi_align.py +++ /dev/null @@ -1,223 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.utils import _pair - -from ..utils import deprecated_api_warning, ext_loader - -ext_module = ext_loader.load_ext('_ext', - ['roi_align_forward', 'roi_align_backward']) - - -class RoIAlignFunction(Function): - - @staticmethod - def symbolic(g, input, rois, output_size, spatial_scale, sampling_ratio, - pool_mode, aligned): - from ..onnx import is_custom_op_loaded - has_custom_op = is_custom_op_loaded() - if has_custom_op: - return g.op( - 'mmcv::MMCVRoiAlign', - input, - rois, - output_height_i=output_size[0], - output_width_i=output_size[1], - spatial_scale_f=spatial_scale, - sampling_ratio_i=sampling_ratio, - mode_s=pool_mode, - aligned_i=aligned) - else: - from torch.onnx.symbolic_opset9 import sub, squeeze - from torch.onnx.symbolic_helper import _slice_helper - from torch.onnx import TensorProtoDataType - # batch_indices = rois[:, 0].long() - batch_indices = _slice_helper( - g, rois, axes=[1], starts=[0], ends=[1]) - batch_indices = squeeze(g, batch_indices, 1) - batch_indices = g.op( - 'Cast', batch_indices, to_i=TensorProtoDataType.INT64) - # rois = rois[:, 1:] - rois = _slice_helper(g, rois, axes=[1], starts=[1], ends=[5]) - if aligned: - # rois -= 0.5/spatial_scale - aligned_offset = g.op( - 'Constant', - value_t=torch.tensor([0.5 / spatial_scale], - dtype=torch.float32)) - rois = sub(g, rois, aligned_offset) - # roi align - return g.op( - 'RoiAlign', - input, - rois, - batch_indices, - output_height_i=output_size[0], - output_width_i=output_size[1], - spatial_scale_f=spatial_scale, - sampling_ratio_i=max(0, sampling_ratio), - mode_s=pool_mode) - - @staticmethod - def forward(ctx, - input, - rois, - output_size, - spatial_scale=1.0, - sampling_ratio=0, - pool_mode='avg', - aligned=True): - ctx.output_size = _pair(output_size) - ctx.spatial_scale = spatial_scale - ctx.sampling_ratio = sampling_ratio - assert pool_mode in ('max', 'avg') - ctx.pool_mode = 0 if pool_mode == 'max' else 1 - ctx.aligned = aligned - ctx.input_shape = input.size() - - assert rois.size(1) == 5, 'RoI must be (idx, x1, y1, x2, y2)!' - - output_shape = (rois.size(0), input.size(1), ctx.output_size[0], - ctx.output_size[1]) - output = input.new_zeros(output_shape) - if ctx.pool_mode == 0: - argmax_y = input.new_zeros(output_shape) - argmax_x = input.new_zeros(output_shape) - else: - argmax_y = input.new_zeros(0) - argmax_x = input.new_zeros(0) - - ext_module.roi_align_forward( - input, - rois, - output, - argmax_y, - argmax_x, - aligned_height=ctx.output_size[0], - aligned_width=ctx.output_size[1], - spatial_scale=ctx.spatial_scale, - sampling_ratio=ctx.sampling_ratio, - pool_mode=ctx.pool_mode, - aligned=ctx.aligned) - - ctx.save_for_backward(rois, argmax_y, argmax_x) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - rois, argmax_y, argmax_x = ctx.saved_tensors - grad_input = grad_output.new_zeros(ctx.input_shape) - # complex head architecture may cause grad_output uncontiguous. - grad_output = grad_output.contiguous() - ext_module.roi_align_backward( - grad_output, - rois, - argmax_y, - argmax_x, - grad_input, - aligned_height=ctx.output_size[0], - aligned_width=ctx.output_size[1], - spatial_scale=ctx.spatial_scale, - sampling_ratio=ctx.sampling_ratio, - pool_mode=ctx.pool_mode, - aligned=ctx.aligned) - return grad_input, None, None, None, None, None, None - - -roi_align = RoIAlignFunction.apply - - -class RoIAlign(nn.Module): - """RoI align pooling layer. - - Args: - output_size (tuple): h, w - spatial_scale (float): scale the input boxes by this number - sampling_ratio (int): number of inputs samples to take for each - output sample. 0 to take samples densely for current models. - pool_mode (str, 'avg' or 'max'): pooling mode in each bin. - aligned (bool): if False, use the legacy implementation in - MMDetection. If True, align the results more perfectly. - use_torchvision (bool): whether to use roi_align from torchvision. - - Note: - The implementation of RoIAlign when aligned=True is modified from - https://github.com/facebookresearch/detectron2/ - - The meaning of aligned=True: - - Given a continuous coordinate c, its two neighboring pixel - indices (in our pixel model) are computed by floor(c - 0.5) and - ceil(c - 0.5). For example, c=1.3 has pixel neighbors with discrete - indices [0] and [1] (which are sampled from the underlying signal - at continuous coordinates 0.5 and 1.5). But the original roi_align - (aligned=False) does not subtract the 0.5 when computing - neighboring pixel indices and therefore it uses pixels with a - slightly incorrect alignment (relative to our pixel model) when - performing bilinear interpolation. - - With `aligned=True`, - we first appropriately scale the ROI and then shift it by -0.5 - prior to calling roi_align. This produces the correct neighbors; - - The difference does not make a difference to the model's - performance if ROIAlign is used together with conv layers. - """ - - @deprecated_api_warning( - { - 'out_size': 'output_size', - 'sample_num': 'sampling_ratio' - }, - cls_name='RoIAlign') - def __init__(self, - output_size, - spatial_scale=1.0, - sampling_ratio=0, - pool_mode='avg', - aligned=True, - use_torchvision=False): - super(RoIAlign, self).__init__() - - self.output_size = _pair(output_size) - self.spatial_scale = float(spatial_scale) - self.sampling_ratio = int(sampling_ratio) - self.pool_mode = pool_mode - self.aligned = aligned - self.use_torchvision = use_torchvision - - def forward(self, input, rois): - """ - Args: - input: NCHW images - rois: Bx5 boxes. First column is the index into N.\ - The other 4 columns are xyxy. - """ - if self.use_torchvision: - from torchvision.ops import roi_align as tv_roi_align - if 'aligned' in tv_roi_align.__code__.co_varnames: - return tv_roi_align(input, rois, self.output_size, - self.spatial_scale, self.sampling_ratio, - self.aligned) - else: - if self.aligned: - rois -= rois.new_tensor([0.] + - [0.5 / self.spatial_scale] * 4) - return tv_roi_align(input, rois, self.output_size, - self.spatial_scale, self.sampling_ratio) - else: - return roi_align(input, rois, self.output_size, self.spatial_scale, - self.sampling_ratio, self.pool_mode, self.aligned) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(output_size={self.output_size}, ' - s += f'spatial_scale={self.spatial_scale}, ' - s += f'sampling_ratio={self.sampling_ratio}, ' - s += f'pool_mode={self.pool_mode}, ' - s += f'aligned={self.aligned}, ' - s += f'use_torchvision={self.use_torchvision})' - return s diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/roi_pool.py b/cv/instance_segmentation/solo/pytorch/mmcv/ops/roi_pool.py deleted file mode 100644 index d339d8f29..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/roi_pool.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.utils import _pair - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', - ['roi_pool_forward', 'roi_pool_backward']) - - -class RoIPoolFunction(Function): - - @staticmethod - def symbolic(g, input, rois, output_size, spatial_scale): - return g.op( - 'MaxRoiPool', - input, - rois, - pooled_shape_i=output_size, - spatial_scale_f=spatial_scale) - - @staticmethod - def forward(ctx, input, rois, output_size, spatial_scale=1.0): - ctx.output_size = _pair(output_size) - ctx.spatial_scale = spatial_scale - ctx.input_shape = input.size() - - assert rois.size(1) == 5, 'RoI must be (idx, x1, y1, x2, y2)!' - - output_shape = (rois.size(0), input.size(1), ctx.output_size[0], - ctx.output_size[1]) - output = input.new_zeros(output_shape) - argmax = input.new_zeros(output_shape, dtype=torch.int) - - ext_module.roi_pool_forward( - input, - rois, - output, - argmax, - pooled_height=ctx.output_size[0], - pooled_width=ctx.output_size[1], - spatial_scale=ctx.spatial_scale) - - ctx.save_for_backward(rois, argmax) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - rois, argmax = ctx.saved_tensors - grad_input = grad_output.new_zeros(ctx.input_shape) - - ext_module.roi_pool_backward( - grad_output, - rois, - argmax, - grad_input, - pooled_height=ctx.output_size[0], - pooled_width=ctx.output_size[1], - spatial_scale=ctx.spatial_scale) - - return grad_input, None, None, None - - -roi_pool = RoIPoolFunction.apply - - -class RoIPool(nn.Module): - - def __init__(self, output_size, spatial_scale=1.0): - super(RoIPool, self).__init__() - - self.output_size = _pair(output_size) - self.spatial_scale = float(spatial_scale) - - def forward(self, input, rois): - return roi_pool(input, rois, self.output_size, self.spatial_scale) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(output_size={self.output_size}, ' - s += f'spatial_scale={self.spatial_scale})' - return s diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/ops/sync_bn.py b/cv/instance_segmentation/solo/pytorch/mmcv/ops/sync_bn.py deleted file mode 100644 index 04302f031..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/ops/sync_bn.py +++ /dev/null @@ -1,279 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn.functional as F -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.module import Module -from torch.nn.parameter import Parameter - -from mmcv.cnn import NORM_LAYERS -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sync_bn_forward_mean', 'sync_bn_forward_var', 'sync_bn_forward_output', - 'sync_bn_backward_param', 'sync_bn_backward_data' -]) - - -class SyncBatchNormFunction(Function): - - @staticmethod - def symbolic(g, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - return g.op( - 'mmcv::MMCVSyncBatchNorm', - input, - running_mean, - running_var, - weight, - bias, - momentum_f=momentum, - eps_f=eps, - group_i=group, - group_size_i=group_size, - stats_mode=stats_mode) - - @staticmethod - def forward(self, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - self.momentum = momentum - self.eps = eps - self.group = group - self.group_size = group_size - self.stats_mode = stats_mode - - assert isinstance( - input, (torch.HalfTensor, torch.FloatTensor, - torch.cuda.HalfTensor, torch.cuda.FloatTensor)), \ - f'only support Half or Float Tensor, but {input.type()}' - output = torch.zeros_like(input) - input3d = input.flatten(start_dim=2) - output3d = output.view_as(input3d) - num_channels = input3d.size(1) - - # ensure mean/var/norm/std are initialized as zeros - # ``torch.empty()`` does not guarantee that - mean = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - var = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - norm = torch.zeros_like( - input3d, dtype=torch.float, device=input3d.device) - std = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - - batch_size = input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_forward_mean(input3d, mean) - batch_flag = torch.ones([1], device=mean.device, dtype=mean.dtype) - else: - # skip updating mean and leave it as zeros when the input is empty - batch_flag = torch.zeros([1], device=mean.device, dtype=mean.dtype) - - # synchronize mean and the batch flag - vec = torch.cat([mean, batch_flag]) - if self.stats_mode == 'N': - vec *= batch_size - if self.group_size > 1: - dist.all_reduce(vec, group=self.group) - total_batch = vec[-1].detach() - mean = vec[:num_channels] - - if self.stats_mode == 'default': - mean = mean / self.group_size - elif self.stats_mode == 'N': - mean = mean / total_batch.clamp(min=1) - else: - raise NotImplementedError - - # leave var as zeros when the input is empty - if batch_size > 0: - ext_module.sync_bn_forward_var(input3d, mean, var) - - if self.stats_mode == 'N': - var *= batch_size - if self.group_size > 1: - dist.all_reduce(var, group=self.group) - - if self.stats_mode == 'default': - var /= self.group_size - elif self.stats_mode == 'N': - var /= total_batch.clamp(min=1) - else: - raise NotImplementedError - - # if the total batch size over all the ranks is zero, - # we should not update the statistics in the current batch - update_flag = total_batch.clamp(max=1) - momentum = update_flag * self.momentum - ext_module.sync_bn_forward_output( - input3d, - mean, - var, - weight, - bias, - running_mean, - running_var, - norm, - std, - output3d, - eps=self.eps, - momentum=momentum, - group_size=self.group_size) - self.save_for_backward(norm, std, weight) - return output - - @staticmethod - @once_differentiable - def backward(self, grad_output): - norm, std, weight = self.saved_tensors - grad_weight = torch.zeros_like(weight) - grad_bias = torch.zeros_like(weight) - grad_input = torch.zeros_like(grad_output) - grad_output3d = grad_output.flatten(start_dim=2) - grad_input3d = grad_input.view_as(grad_output3d) - - batch_size = grad_input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_backward_param(grad_output3d, norm, grad_weight, - grad_bias) - - # all reduce - if self.group_size > 1: - dist.all_reduce(grad_weight, group=self.group) - dist.all_reduce(grad_bias, group=self.group) - grad_weight /= self.group_size - grad_bias /= self.group_size - - if batch_size > 0: - ext_module.sync_bn_backward_data(grad_output3d, weight, - grad_weight, grad_bias, norm, std, - grad_input3d) - - return grad_input, None, None, grad_weight, grad_bias, \ - None, None, None, None, None - - -@NORM_LAYERS.register_module(name='MMSyncBN') -class SyncBatchNorm(Module): - """Synchronized Batch Normalization. - - Args: - num_features (int): number of features/chennels in input tensor - eps (float, optional): a value added to the denominator for numerical - stability. Defaults to 1e-5. - momentum (float, optional): the value used for the running_mean and - running_var computation. Defaults to 0.1. - affine (bool, optional): whether to use learnable affine parameters. - Defaults to True. - track_running_stats (bool, optional): whether to track the running - mean and variance during training. When set to False, this - module does not track such statistics, and initializes statistics - buffers ``running_mean`` and ``running_var`` as ``None``. When - these buffers are ``None``, this module always uses batch - statistics in both training and eval modes. Defaults to True. - group (int, optional): synchronization of stats happen within - each process group individually. By default it is synchronization - across the whole world. Defaults to None. - stats_mode (str, optional): The statistical mode. Available options - includes ``'default'`` and ``'N'``. Defaults to 'default'. - When ``stats_mode=='default'``, it computes the overall statistics - using those from each worker with equal weight, i.e., the - statistics are synchronized and simply divied by ``group``. This - mode will produce inaccurate statistics when empty tensors occur. - When ``stats_mode=='N'``, it compute the overall statistics using - the total number of batches in each worker ignoring the number of - group, i.e., the statistics are synchronized and then divied by - the total batch ``N``. This mode is beneficial when empty tensors - occur during training, as it average the total mean by the real - number of batch. - """ - - def __init__(self, - num_features, - eps=1e-5, - momentum=0.1, - affine=True, - track_running_stats=True, - group=None, - stats_mode='default'): - super(SyncBatchNorm, self).__init__() - self.num_features = num_features - self.eps = eps - self.momentum = momentum - self.affine = affine - self.track_running_stats = track_running_stats - group = dist.group.WORLD if group is None else group - self.group = group - self.group_size = dist.get_world_size(group) - assert stats_mode in ['default', 'N'], \ - f'"stats_mode" only accepts "default" and "N", got "{stats_mode}"' - self.stats_mode = stats_mode - if self.affine: - self.weight = Parameter(torch.Tensor(num_features)) - self.bias = Parameter(torch.Tensor(num_features)) - else: - self.register_parameter('weight', None) - self.register_parameter('bias', None) - if self.track_running_stats: - self.register_buffer('running_mean', torch.zeros(num_features)) - self.register_buffer('running_var', torch.ones(num_features)) - self.register_buffer('num_batches_tracked', - torch.tensor(0, dtype=torch.long)) - else: - self.register_buffer('running_mean', None) - self.register_buffer('running_var', None) - self.register_buffer('num_batches_tracked', None) - self.reset_parameters() - - def reset_running_stats(self): - if self.track_running_stats: - self.running_mean.zero_() - self.running_var.fill_(1) - self.num_batches_tracked.zero_() - - def reset_parameters(self): - self.reset_running_stats() - if self.affine: - self.weight.data.uniform_() # pytorch use ones_() - self.bias.data.zero_() - - def forward(self, input): - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input, got {input.dim()}D input') - if self.momentum is None: - exponential_average_factor = 0.0 - else: - exponential_average_factor = self.momentum - - if self.training and self.track_running_stats: - if self.num_batches_tracked is not None: - self.num_batches_tracked += 1 - if self.momentum is None: # use cumulative moving average - exponential_average_factor = 1.0 / float( - self.num_batches_tracked) - else: # use exponential moving average - exponential_average_factor = self.momentum - - if self.training or not self.track_running_stats: - return SyncBatchNormFunction.apply( - input, self.running_mean, self.running_var, self.weight, - self.bias, exponential_average_factor, self.eps, self.group, - self.group_size, self.stats_mode) - else: - return F.batch_norm(input, self.running_mean, self.running_var, - self.weight, self.bias, False, - exponential_average_factor, self.eps) - - def __repr__(self): - s = self.__class__.__name__ - s += f'({self.num_features}, ' - s += f'eps={self.eps}, ' - s += f'momentum={self.momentum}, ' - s += f'affine={self.affine}, ' - s += f'track_running_stats={self.track_running_stats}, ' - s += f'group_size={self.group_size},' - s += f'stats_mode={self.stats_mode})' - return s diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/parallel/__init__.py deleted file mode 100644 index 2ed2c17ad..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collate import collate -from .data_container import DataContainer -from .data_parallel import MMDataParallel -from .distributed import MMDistributedDataParallel -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter, scatter_kwargs -from .utils import is_module_wrapper - -__all__ = [ - 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', - 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/_functions.py b/cv/instance_segmentation/solo/pytorch/mmcv/parallel/_functions.py deleted file mode 100644 index 9b5a8a444..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/_functions.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import _get_stream - - -def scatter(input, devices, streams=None): - """Scatters tensor across multiple GPUs.""" - if streams is None: - streams = [None] * len(devices) - - if isinstance(input, list): - chunk_size = (len(input) - 1) // len(devices) + 1 - outputs = [ - scatter(input[i], [devices[i // chunk_size]], - [streams[i // chunk_size]]) for i in range(len(input)) - ] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - # TODO: copy to a pinned buffer first (if copying from CPU) - stream = streams[0] if output.numel() > 0 else None - if devices != [-1]: - with torch.cuda.device(devices[0]), torch.cuda.stream(stream): - output = output.cuda(devices[0], non_blocking=True) - else: - # unsqueeze the first dimension thus the tensor's shape is the - # same as those scattered with GPU. - output = output.unsqueeze(0) - return output - else: - raise Exception(f'Unknown type {type(input)}.') - - -def synchronize_stream(output, devices, streams): - if isinstance(output, list): - chunk_size = len(output) // len(devices) - for i in range(len(devices)): - for j in range(chunk_size): - synchronize_stream(output[i * chunk_size + j], [devices[i]], - [streams[i]]) - elif isinstance(output, torch.Tensor): - if output.numel() != 0: - with torch.cuda.device(devices[0]): - main_stream = torch.cuda.current_stream() - main_stream.wait_stream(streams[0]) - output.record_stream(main_stream) - else: - raise Exception(f'Unknown type {type(output)}.') - - -def get_input_device(input): - if isinstance(input, list): - for item in input: - input_device = get_input_device(item) - if input_device != -1: - return input_device - return -1 - elif isinstance(input, torch.Tensor): - return input.get_device() if input.is_cuda else -1 - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_gpus, input): - input_device = get_input_device(input) - streams = None - if input_device == -1 and target_gpus != [-1]: - # Perform CPU to GPU copies in a background stream - streams = [_get_stream(device) for device in target_gpus] - - outputs = scatter(input, target_gpus, streams) - # Synchronize with the copy stream - if streams is not None: - synchronize_stream(outputs, target_gpus, streams) - - return tuple(outputs) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/collate.py b/cv/instance_segmentation/solo/pytorch/mmcv/parallel/collate.py deleted file mode 100644 index ad749197d..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/collate.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence - -import torch -import torch.nn.functional as F -from torch.utils.data.dataloader import default_collate - -from .data_container import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Puts each data field into a tensor/DataContainer with outer dimension - batch size. - - Extend default_collate to add support for - :type:`~mmcv.parallel.DataContainer`. There are 3 cases. - - 1. cpu_only = True, e.g., meta data - 2. cpu_only = False, stack = True, e.g., images tensors - 3. cpu_only = False, stack = False, e.g., gt bboxes - """ - - if not isinstance(batch, Sequence): - raise TypeError(f'{batch.dtype} is not supported.') - - if isinstance(batch[0], DataContainer): - stacked = [] - if batch[0].cpu_only: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer( - stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) - elif batch[0].stack: - for i in range(0, len(batch), samples_per_gpu): - assert isinstance(batch[i].data, torch.Tensor) - - if batch[i].pad_dims is not None: - ndim = batch[i].dim() - assert ndim > batch[i].pad_dims - max_shape = [0 for _ in range(batch[i].pad_dims)] - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = batch[i].size(-dim) - for sample in batch[i:i + samples_per_gpu]: - for dim in range(0, ndim - batch[i].pad_dims): - assert batch[i].size(dim) == sample.size(dim) - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = max(max_shape[dim - 1], - sample.size(-dim)) - padded_samples = [] - for sample in batch[i:i + samples_per_gpu]: - pad = [0 for _ in range(batch[i].pad_dims * 2)] - for dim in range(1, batch[i].pad_dims + 1): - pad[2 * dim - - 1] = max_shape[dim - 1] - sample.size(-dim) - padded_samples.append( - F.pad( - sample.data, pad, value=sample.padding_value)) - stacked.append(default_collate(padded_samples)) - elif batch[i].pad_dims is None: - stacked.append( - default_collate([ - sample.data - for sample in batch[i:i + samples_per_gpu] - ])) - else: - raise ValueError( - 'pad_dims should be either None or integers (1-3)') - - else: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer(stacked, batch[0].stack, batch[0].padding_value) - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - return [collate(samples, samples_per_gpu) for samples in transposed] - elif isinstance(batch[0], Mapping): - return { - key: collate([d[key] for d in batch], samples_per_gpu) - for key in batch[0] - } - else: - return default_collate(batch) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/data_container.py b/cv/instance_segmentation/solo/pytorch/mmcv/parallel/data_container.py deleted file mode 100644 index cedb0d32a..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/data_container.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch - - -def assert_tensor_type(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not isinstance(args[0].data, torch.Tensor): - raise AttributeError( - f'{args[0].__class__.__name__} has no attribute ' - f'{func.__name__} for type {args[0].datatype}') - return func(*args, **kwargs) - - return wrapper - - -class DataContainer: - """A container for any type of objects. - - Typically tensors will be stacked in the collate function and sliced along - some dimension in the scatter function. This behavior has some limitations. - 1. All tensors have to be the same size. - 2. Types are limited (numpy array or Tensor). - - We design `DataContainer` and `MMDataParallel` to overcome these - limitations. The behavior can be either of the following. - - - copy to GPU, pad all tensors to the same size and stack them - - copy to GPU without stacking - - leave the objects as is and pass it to the model - - pad_dims specifies the number of last few dimensions to do padding - """ - - def __init__(self, - data, - stack=False, - padding_value=0, - cpu_only=False, - pad_dims=2): - self._data = data - self._cpu_only = cpu_only - self._stack = stack - self._padding_value = padding_value - assert pad_dims in [None, 1, 2, 3] - self._pad_dims = pad_dims - - def __repr__(self): - return f'{self.__class__.__name__}({repr(self.data)})' - - def __len__(self): - return len(self._data) - - @property - def data(self): - return self._data - - @property - def datatype(self): - if isinstance(self.data, torch.Tensor): - return self.data.type() - else: - return type(self.data) - - @property - def cpu_only(self): - return self._cpu_only - - @property - def stack(self): - return self._stack - - @property - def padding_value(self): - return self._padding_value - - @property - def pad_dims(self): - return self._pad_dims - - @assert_tensor_type - def size(self, *args, **kwargs): - return self.data.size(*args, **kwargs) - - @assert_tensor_type - def dim(self): - return self.data.dim() diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/data_parallel.py b/cv/instance_segmentation/solo/pytorch/mmcv/parallel/data_parallel.py deleted file mode 100644 index 79b5f69b6..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/data_parallel.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from itertools import chain - -from torch.nn.parallel import DataParallel - -from .scatter_gather import scatter_kwargs - - -class MMDataParallel(DataParallel): - """The DataParallel module that supports DataContainer. - - MMDataParallel has two main differences with PyTorch DataParallel: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data during both GPU and CPU inference. - - It implement two more APIs ``train_step()`` and ``val_step()``. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - device_ids (list[int]): Device IDS of modules to be scattered to. - Defaults to None when GPU is not available. - output_device (str | int): Device ID for output. Defaults to None. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MMDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.dim = dim - - def forward(self, *inputs, **kwargs): - """Override the original forward function. - - The main difference lies in the CPU inference where the data in - :class:`DataContainers` will still be gathered. - """ - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module(*inputs[0], **kwargs[0]) - else: - return super().forward(*inputs, **kwargs) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.train_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - 'instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.train_step(*inputs[0], **kwargs[0]) - - def val_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.val_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/distributed.py b/cv/instance_segmentation/solo/pytorch/mmcv/parallel/distributed.py deleted file mode 100644 index b799a213d..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/distributed.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel.distributed import (DistributedDataParallel, - _find_tensors) - -from mmcv import print_log -from mmcv.utils import TORCH_VERSION, digit_version -from .scatter_gather import scatter_kwargs - - -class MMDistributedDataParallel(DistributedDataParallel): - """The DDP module that supports DataContainer. - - MMDDP has two main differences with PyTorch DDP: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data. - - It implement two APIs ``train_step()`` and ``val_step()``. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - """train_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.train_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if getattr(self, 'require_forward_param_sync', True): - self._sync_params() - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.train_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.train_step(*inputs, **kwargs) - - if torch.is_grad_enabled() and getattr( - self, 'require_backward_grad_sync', True): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output - - def val_step(self, *inputs, **kwargs): - """val_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.val_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if getattr(self, 'require_forward_param_sync', True): - self._sync_params() - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.val_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.val_step(*inputs, **kwargs) - - if torch.is_grad_enabled() and getattr( - self, 'require_backward_grad_sync', True): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/distributed_deprecated.py b/cv/instance_segmentation/solo/pytorch/mmcv/parallel/distributed_deprecated.py deleted file mode 100644 index b593d4a9e..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/distributed_deprecated.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn as nn -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter_kwargs - - -@MODULE_WRAPPERS.register_module() -class MMDistributedDataParallel(nn.Module): - - def __init__(self, - module, - dim=0, - broadcast_buffers=True, - bucket_cap_mb=25): - super(MMDistributedDataParallel, self).__init__() - self.module = module - self.dim = dim - self.broadcast_buffers = broadcast_buffers - - self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 - self._sync_params() - - def _dist_broadcast_coalesced(self, tensors, buffer_size): - for tensors in _take_tensors(tensors, buffer_size): - flat_tensors = _flatten_dense_tensors(tensors) - dist.broadcast(flat_tensors, 0) - for tensor, synced in zip( - tensors, _unflatten_dense_tensors(flat_tensors, tensors)): - tensor.copy_(synced) - - def _sync_params(self): - module_states = list(self.module.state_dict().values()) - if len(module_states) > 0: - self._dist_broadcast_coalesced(module_states, - self.broadcast_bucket_size) - if self.broadcast_buffers: - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) < digit_version('1.0')): - buffers = [b.data for b in self.module._all_buffers()] - else: - buffers = [b.data for b in self.module.buffers()] - if len(buffers) > 0: - self._dist_broadcast_coalesced(buffers, - self.broadcast_bucket_size) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/registry.py b/cv/instance_segmentation/solo/pytorch/mmcv/parallel/registry.py deleted file mode 100644 index 144f9fb16..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel, DistributedDataParallel - -from mmcv.utils import Registry - -MODULE_WRAPPERS = Registry('module wrapper') -MODULE_WRAPPERS.register_module(module=DataParallel) -MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/scatter_gather.py b/cv/instance_segmentation/solo/pytorch/mmcv/parallel/scatter_gather.py deleted file mode 100644 index 900ff8856..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import Scatter as OrigScatter - -from ._functions import Scatter -from .data_container import DataContainer - - -def scatter(inputs, target_gpus, dim=0): - """Scatter inputs to target gpus. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_gpus != [-1]: - return OrigScatter.apply(target_gpus, None, dim, obj) - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_gpus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_gpus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_gpus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_gpus, dim) if inputs else [] - kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/utils.py b/cv/instance_segmentation/solo/pytorch/mmcv/parallel/utils.py deleted file mode 100644 index 0f5712cb4..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/parallel/utils.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import MODULE_WRAPPERS - - -def is_module_wrapper(module): - """Check if a module is a module wrapper. - - The following 3 modules in MMCV (and their subclasses) are regarded as - module wrappers: DataParallel, DistributedDataParallel, - MMDistributedDataParallel (the deprecated version). You may add you own - module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: True if the input module is a module wrapper. - """ - module_wrappers = tuple(MODULE_WRAPPERS.module_dict.values()) - return isinstance(module, module_wrappers) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/__init__.py deleted file mode 100644 index 52e4b48d3..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_module import BaseModule, ModuleList, Sequential -from .base_runner import BaseRunner -from .builder import RUNNERS, build_runner -from .checkpoint import (CheckpointLoader, _load_checkpoint, - _load_checkpoint_with_prefix, load_checkpoint, - load_state_dict, save_checkpoint, weights_to_cpu) -from .default_constructor import DefaultRunnerConstructor -from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, - init_dist, master_only) -from .epoch_based_runner import EpochBasedRunner, Runner -from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model -from .hooks import (HOOKS, CheckpointHook, ClosureHook, DistEvalHook, - DistSamplerSeedHook, DvcliveLoggerHook, EMAHook, EvalHook, - Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, Hook, IterTimerHook, - LoggerHook, LrUpdaterHook, MlflowLoggerHook, - NeptuneLoggerHook, OptimizerHook, PaviLoggerHook, - SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .iter_based_runner import IterBasedRunner, IterLoader -from .log_buffer import LogBuffer -from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, - DefaultOptimizerConstructor, build_optimizer, - build_optimizer_constructor) -from .priority import Priority, get_priority -from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed - -__all__ = [ - 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', - 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', - 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', - 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', - 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', - 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', - 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', - 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', - 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', - 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', - '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', - 'ModuleList', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/base_module.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/base_module.py deleted file mode 100644 index 529575b81..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/base_module.py +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings -from abc import ABCMeta -from collections import defaultdict -from logging import FileHandler - -import torch.nn as nn - -from mmcv.runner.dist_utils import master_only -from mmcv.utils.logging import get_logger, logger_initialized, print_log - - -class BaseModule(nn.Module, metaclass=ABCMeta): - """Base module for all modules in openmmlab. - - ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional - functionality of parameter initialization. Compared with - ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. - - - ``init_cfg``: the config to control the initialization. - - ``init_weights``: The function of parameter - initialization and recording initialization - information. - - ``_params_init_info``: Used to track the parameter - initialization information. This attribute only - exists during executing the ``init_weights``. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, init_cfg=None): - """Initialize BaseModule, inherited from `torch.nn.Module`""" - - # NOTE init_cfg can be defined in different levels, but init_cfg - # in low levels has a higher priority. - - super(BaseModule, self).__init__() - # define default value of init_cfg instead of hard code - # in init_weights() function - self._is_init = False - - self.init_cfg = copy.deepcopy(init_cfg) - - # Backward compatibility in derived classes - # if pretrained is not None: - # warnings.warn('DeprecationWarning: pretrained is a deprecated \ - # key, please consider using init_cfg') - # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - - @property - def is_init(self): - return self._is_init - - def init_weights(self): - """Initialize the weights.""" - - is_top_level_module = False - # check if it is top-level module - if not hasattr(self, '_params_init_info'): - # The `_params_init_info` is used to record the initialization - # information of the parameters - # the key should be the obj:`nn.Parameter` of model and the value - # should be a dict containing - # - init_info (str): The string that describes the initialization. - # - tmp_mean_value (FloatTensor): The mean of the parameter, - # which indicates whether the parameter has been modified. - # this attribute would be deleted after all parameters - # is initialized. - self._params_init_info = defaultdict(dict) - is_top_level_module = True - - # Initialize the `_params_init_info`, - # When detecting the `tmp_mean_value` of - # the corresponding parameter is changed, update related - # initialization information - for name, param in self.named_parameters(): - self._params_init_info[param][ - 'init_info'] = f'The value is the same before and ' \ - f'after calling `init_weights` ' \ - f'of {self.__class__.__name__} ' - self._params_init_info[param][ - 'tmp_mean_value'] = param.data.mean() - - # pass `params_init_info` to all submodules - # All submodules share the same `params_init_info`, - # so it will be updated when parameters are - # modified at any level of the model. - for sub_module in self.modules(): - sub_module._params_init_info = self._params_init_info - - # Get the initialized logger, if not exist, - # create a logger named `mmcv` - logger_names = list(logger_initialized.keys()) - logger_name = logger_names[0] if logger_names else 'mmcv' - - from ..cnn import initialize - from ..cnn.utils.weight_init import update_init_info - module_name = self.__class__.__name__ - if not self._is_init: - if self.init_cfg: - print_log( - f'initialize {module_name} with init_cfg {self.init_cfg}', - logger=logger_name) - initialize(self, self.init_cfg) - if isinstance(self.init_cfg, dict): - # prevent the parameters of - # the pre-trained model - # from being overwritten by - # the `init_weights` - if self.init_cfg['type'] == 'Pretrained': - return - - for m in self.children(): - if hasattr(m, 'init_weights'): - m.init_weights() - # users may overload the `init_weights` - update_init_info( - m, - init_info=f'Initialized by ' - f'user-defined `init_weights`' - f' in {m.__class__.__name__} ') - - self._is_init = True - else: - warnings.warn(f'init_weights of {self.__class__.__name__} has ' - f'been called more than once.') - - if is_top_level_module: - self._dump_init_info(logger_name) - - for sub_module in self.modules(): - del sub_module._params_init_info - - @master_only - def _dump_init_info(self, logger_name): - """Dump the initialization information to a file named - `initialization.log.json` in workdir. - - Args: - logger_name (str): The name of logger. - """ - - logger = get_logger(logger_name) - - with_file_handler = False - # dump the information to the logger file if there is a `FileHandler` - for handler in logger.handlers: - if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True - if not with_file_handler: - for name, param in self.named_parameters(): - print_log( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n ", - logger=logger_name) - - def __repr__(self): - s = super().__repr__() - if self.init_cfg: - s += f'\ninit_cfg={self.init_cfg}' - return s - - -class Sequential(BaseModule, nn.Sequential): - """Sequential module in openmmlab. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, *args, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.Sequential.__init__(self, *args) - - -class ModuleList(BaseModule, nn.ModuleList): - """ModuleList in openmmlab. - - Args: - modules (iterable, optional): an iterable of modules to add. - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleList.__init__(self, modules) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/base_runner.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/base_runner.py deleted file mode 100644 index 25cd98f51..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/base_runner.py +++ /dev/null @@ -1,542 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -import os.path as osp -import warnings -from abc import ABCMeta, abstractmethod - -import torch -from torch.optim import Optimizer - -import mmcv -from ..parallel import is_module_wrapper -from .checkpoint import load_checkpoint -from .dist_utils import get_dist_info -from .hooks import HOOKS, Hook -from .log_buffer import LogBuffer -from .priority import Priority, get_priority -from .utils import get_time_str - - -class BaseRunner(metaclass=ABCMeta): - """The base class of Runner, a training helper for PyTorch. - - All subclasses should implement the following APIs: - - - ``run()`` - - ``train()`` - - ``val()`` - - ``save_checkpoint()`` - - Args: - model (:obj:`torch.nn.Module`): The model to be run. - batch_processor (callable): A callable method that process a data - batch. The interface of this method should be - `batch_processor(model, data, train_mode) -> dict` - optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an - optimizer (in most cases) or a dict of optimizers (in models that - requires more than one optimizer, e.g., GAN). - work_dir (str, optional): The working directory to save checkpoints - and logs. Defaults to None. - logger (:obj:`logging.Logger`): Logger used during training. - Defaults to None. (The default value is just for backward - compatibility) - meta (dict | None): A dict records some import information such as - environment info and seed, which will be logged in logger hook. - Defaults to None. - max_epochs (int, optional): Total training epochs. - max_iters (int, optional): Total training iterations. - """ - - def __init__(self, - model, - batch_processor=None, - optimizer=None, - work_dir=None, - logger=None, - meta=None, - max_iters=None, - max_epochs=None): - if batch_processor is not None: - if not callable(batch_processor): - raise TypeError('batch_processor must be callable, ' - f'but got {type(batch_processor)}') - warnings.warn('batch_processor is deprecated, please implement ' - 'train_step() and val_step() in the model instead.') - # raise an error is `batch_processor` is not None and - # `model.train_step()` exists. - if is_module_wrapper(model): - _model = model.module - else: - _model = model - if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): - raise RuntimeError( - 'batch_processor and model.train_step()/model.val_step() ' - 'cannot be both available.') - else: - assert hasattr(model, 'train_step') - - # check the type of `optimizer` - if isinstance(optimizer, dict): - for name, optim in optimizer.items(): - if not isinstance(optim, Optimizer): - raise TypeError( - f'optimizer must be a dict of torch.optim.Optimizers, ' - f'but optimizer["{name}"] is a {type(optim)}') - elif not isinstance(optimizer, Optimizer) and optimizer is not None: - raise TypeError( - f'optimizer must be a torch.optim.Optimizer object ' - f'or dict or None, but got {type(optimizer)}') - - # check the type of `logger` - if not isinstance(logger, logging.Logger): - raise TypeError(f'logger must be a logging.Logger object, ' - f'but got {type(logger)}') - - # check the type of `meta` - if meta is not None and not isinstance(meta, dict): - raise TypeError( - f'meta must be a dict or None, but got {type(meta)}') - - self.model = model - self.batch_processor = batch_processor - self.optimizer = optimizer - self.logger = logger - self.meta = meta - # create work_dir - if mmcv.is_str(work_dir): - self.work_dir = osp.abspath(work_dir) - mmcv.mkdir_or_exist(self.work_dir) - elif work_dir is None: - self.work_dir = None - else: - raise TypeError('"work_dir" must be a str or None') - - # get model name from the model class - if hasattr(self.model, 'module'): - self._model_name = self.model.module.__class__.__name__ - else: - self._model_name = self.model.__class__.__name__ - - self._rank, self._world_size = get_dist_info() - self.timestamp = get_time_str() - self.mode = None - self._hooks = [] - self._epoch = 0 - self._iter = 0 - self._inner_iter = 0 - - if max_epochs is not None and max_iters is not None: - raise ValueError( - 'Only one of `max_epochs` or `max_iters` can be set.') - - self._max_epochs = max_epochs - self._max_iters = max_iters - # TODO: Redesign LogBuffer, it is not flexible and elegant enough - self.log_buffer = LogBuffer() - - @property - def model_name(self): - """str: Name of the model, usually the module class name.""" - return self._model_name - - @property - def rank(self): - """int: Rank of current process. (distributed training)""" - return self._rank - - @property - def world_size(self): - """int: Number of processes participating in the job. - (distributed training)""" - return self._world_size - - @property - def hooks(self): - """list[:obj:`Hook`]: A list of registered hooks.""" - return self._hooks - - @property - def epoch(self): - """int: Current epoch.""" - return self._epoch - - @property - def iter(self): - """int: Current iteration.""" - return self._iter - - @property - def inner_iter(self): - """int: Iteration in an epoch.""" - return self._inner_iter - - @property - def max_epochs(self): - """int: Maximum training epochs.""" - return self._max_epochs - - @property - def max_iters(self): - """int: Maximum training iterations.""" - return self._max_iters - - @abstractmethod - def train(self): - pass - - @abstractmethod - def val(self): - pass - - @abstractmethod - def run(self, data_loaders, workflow, **kwargs): - pass - - @abstractmethod - def save_checkpoint(self, - out_dir, - filename_tmpl, - save_optimizer=True, - meta=None, - create_symlink=True): - pass - - def current_lr(self): - """Get current learning rates. - - Returns: - list[float] | dict[str, list[float]]: Current learning rates of all - param groups. If the runner has a dict of optimizers, this - method will return a dict. - """ - if isinstance(self.optimizer, torch.optim.Optimizer): - lr = [group['lr'] for group in self.optimizer.param_groups] - elif isinstance(self.optimizer, dict): - lr = dict() - for name, optim in self.optimizer.items(): - lr[name] = [group['lr'] for group in optim.param_groups] - else: - raise RuntimeError( - 'lr is not applicable because optimizer does not exist.') - return lr - - def current_momentum(self): - """Get current momentums. - - Returns: - list[float] | dict[str, list[float]]: Current momentums of all - param groups. If the runner has a dict of optimizers, this - method will return a dict. - """ - - def _get_momentum(optimizer): - momentums = [] - for group in optimizer.param_groups: - if 'momentum' in group.keys(): - momentums.append(group['momentum']) - elif 'betas' in group.keys(): - momentums.append(group['betas'][0]) - else: - momentums.append(0) - return momentums - - if self.optimizer is None: - raise RuntimeError( - 'momentum is not applicable because optimizer does not exist.') - elif isinstance(self.optimizer, torch.optim.Optimizer): - momentums = _get_momentum(self.optimizer) - elif isinstance(self.optimizer, dict): - momentums = dict() - for name, optim in self.optimizer.items(): - momentums[name] = _get_momentum(optim) - return momentums - - def register_hook(self, hook, priority='NORMAL'): - """Register a hook into the hook list. - - The hook will be inserted into a priority queue, with the specified - priority (See :class:`Priority` for details of priorities). - For hooks with the same priority, they will be triggered in the same - order as they are registered. - - Args: - hook (:obj:`Hook`): The hook to be registered. - priority (int or str or :obj:`Priority`): Hook priority. - Lower value means higher priority. - """ - assert isinstance(hook, Hook) - if hasattr(hook, 'priority'): - raise ValueError('"priority" is a reserved attribute for hooks') - priority = get_priority(priority) - hook.priority = priority - # insert the hook to a sorted list - inserted = False - for i in range(len(self._hooks) - 1, -1, -1): - if priority >= self._hooks[i].priority: - self._hooks.insert(i + 1, hook) - inserted = True - break - if not inserted: - self._hooks.insert(0, hook) - - def register_hook_from_cfg(self, hook_cfg): - """Register a hook from its cfg. - - Args: - hook_cfg (dict): Hook config. It should have at least keys 'type' - and 'priority' indicating its type and priority. - - Notes: - The specific hook class to register should not use 'type' and - 'priority' arguments during initialization. - """ - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = mmcv.build_from_cfg(hook_cfg, HOOKS) - self.register_hook(hook, priority=priority) - - def call_hook(self, fn_name): - """Call all hooks. - - Args: - fn_name (str): The function name in each hook to be called, such as - "before_train_epoch". - """ - for hook in self._hooks: - getattr(hook, fn_name)(self) - - def get_hook_info(self): - # Get hooks info in each stage - stage_hook_map = {stage: [] for stage in Hook.stages} - for hook in self.hooks: - try: - priority = Priority(hook.priority).name - except ValueError: - priority = hook.priority - classname = hook.__class__.__name__ - hook_info = f'({priority:<12}) {classname:<35}' - for trigger_stage in hook.get_triggered_stages(): - stage_hook_map[trigger_stage].append(hook_info) - - stage_hook_infos = [] - for stage in Hook.stages: - hook_infos = stage_hook_map[stage] - if len(hook_infos) > 0: - info = f'{stage}:\n' - info += '\n'.join(hook_infos) - info += '\n -------------------- ' - stage_hook_infos.append(info) - return '\n'.join(stage_hook_infos) - - def load_checkpoint(self, - filename, - map_location='cpu', - strict=False, - revise_keys=[(r'^module.', '')]): - return load_checkpoint( - self.model, - filename, - map_location, - strict, - self.logger, - revise_keys=revise_keys) - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - if map_location == 'default': - if torch.cuda.is_available(): - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint(checkpoint) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - if self.meta is None: - self.meta = {} - self.meta.setdefault('hook_msgs', {}) - # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages - self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) - - # Re-calculate the number of iterations when resuming - # models with different number of GPUs - if 'config' in checkpoint['meta']: - config = mmcv.Config.fromstring( - checkpoint['meta']['config'], file_format='.py') - previous_gpu_ids = config.get('gpu_ids', None) - if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( - previous_gpu_ids) != self.world_size: - self._iter = int(self._iter * len(previous_gpu_ids) / - self.world_size) - self.logger.info('the iteration number is changed due to ' - 'change of GPU number') - - # resume meta information meta - self.meta = checkpoint['meta'] - - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - elif isinstance(lr_config, dict): - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = mmcv.build_from_cfg(lr_config, HOOKS) - else: - hook = lr_config - self.register_hook(hook, priority='VERY_HIGH') - - def register_momentum_hook(self, momentum_config): - if momentum_config is None: - return - if isinstance(momentum_config, dict): - assert 'policy' in momentum_config - policy_type = momentum_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of momentum updater. - # Since this is not applicable for - # `CosineAnnealingMomentumUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'MomentumUpdaterHook' - momentum_config['type'] = hook_type - hook = mmcv.build_from_cfg(momentum_config, HOOKS) - else: - hook = momentum_config - self.register_hook(hook, priority='HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = mmcv.build_from_cfg(optimizer_config, HOOKS) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def register_checkpoint_hook(self, checkpoint_config): - if checkpoint_config is None: - return - if isinstance(checkpoint_config, dict): - checkpoint_config.setdefault('type', 'CheckpointHook') - hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) - else: - hook = checkpoint_config - self.register_hook(hook, priority='NORMAL') - - def register_logger_hooks(self, log_config): - if log_config is None: - return - log_interval = log_config['interval'] - for info in log_config['hooks']: - logger_hook = mmcv.build_from_cfg( - info, HOOKS, default_args=dict(interval=log_interval)) - self.register_hook(logger_hook, priority='VERY_LOW') - - def register_timer_hook(self, timer_config): - if timer_config is None: - return - if isinstance(timer_config, dict): - timer_config_ = copy.deepcopy(timer_config) - hook = mmcv.build_from_cfg(timer_config_, HOOKS) - else: - hook = timer_config - self.register_hook(hook, priority='LOW') - - def register_custom_hooks(self, custom_config): - if custom_config is None: - return - - if not isinstance(custom_config, list): - custom_config = [custom_config] - - for item in custom_config: - if isinstance(item, dict): - self.register_hook_from_cfg(item) - else: - self.register_hook(item, priority='NORMAL') - - def register_profiler_hook(self, profiler_config): - if profiler_config is None: - return - if isinstance(profiler_config, dict): - profiler_config.setdefault('type', 'ProfilerHook') - hook = mmcv.build_from_cfg(profiler_config, HOOKS) - else: - hook = profiler_config - self.register_hook(hook) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - timer_config=dict(type='IterTimerHook'), - custom_hooks_config=None): - """Register default and custom hooks for training. - - Default and custom hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - self.register_lr_hook(lr_config) - self.register_momentum_hook(momentum_config) - self.register_optimizer_hook(optimizer_config) - self.register_checkpoint_hook(checkpoint_config) - self.register_timer_hook(timer_config) - self.register_logger_hooks(log_config) - self.register_custom_hooks(custom_hooks_config) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/builder.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/builder.py deleted file mode 100644 index 77c96ba0b..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/builder.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from ..utils import Registry - -RUNNERS = Registry('runner') -RUNNER_BUILDERS = Registry('runner builder') - - -def build_runner_constructor(cfg): - return RUNNER_BUILDERS.build(cfg) - - -def build_runner(cfg, default_args=None): - runner_cfg = copy.deepcopy(cfg) - constructor_type = runner_cfg.pop('constructor', - 'DefaultRunnerConstructor') - runner_constructor = build_runner_constructor( - dict( - type=constructor_type, - runner_cfg=runner_cfg, - default_args=default_args)) - runner = runner_constructor() - return runner diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/checkpoint.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/checkpoint.py deleted file mode 100644 index 6ad605b85..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/checkpoint.py +++ /dev/null @@ -1,707 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os -import os.path as osp -import pkgutil -import re -import time -import warnings -from collections import OrderedDict -from importlib import import_module -from tempfile import TemporaryDirectory - -import torch -import torchvision -from torch.optim import Optimizer -from torch.utils import model_zoo - -import mmcv -from ..fileio import FileClient -from ..fileio import load as load_file -from ..parallel import is_module_wrapper -from ..utils import mkdir_or_exist -from .dist_utils import get_dist_info - -ENV_MMCV_HOME = 'MMCV_HOME' -ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' -DEFAULT_CACHE_DIR = '~/.cache' - - -def _get_mmcv_home(): - mmcv_home = os.path.expanduser( - os.getenv( - ENV_MMCV_HOME, - os.path.join( - os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) - - mkdir_or_exist(mmcv_home) - return mmcv_home - - -def load_state_dict(module, state_dict, strict=False, logger=None): - """Load state_dict to a module. - - This method is modified from :meth:`torch.nn.Module.load_state_dict`. - Default value for ``strict`` is set to ``False`` and the message for - param mismatch will be shown even if strict is False. - - Args: - module (Module): Module that receives the state_dict. - state_dict (OrderedDict): Weights. - strict (bool): whether to strictly enforce that the keys - in :attr:`state_dict` match the keys returned by this module's - :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. - logger (:obj:`logging.Logger`, optional): Logger to log the error - message. If not specified, print function will be used. - """ - unexpected_keys = [] - all_missing_keys = [] - err_msg = [] - - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata - - # use _load_from_state_dict to enable checkpoint version control - def load(module, prefix=''): - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - local_metadata = {} if metadata is None else metadata.get( - prefix[:-1], {}) - module._load_from_state_dict(state_dict, prefix, local_metadata, True, - all_missing_keys, unexpected_keys, - err_msg) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(module) - load = None # break load->load reference cycle - - # ignore "num_batches_tracked" of BN layers - missing_keys = [ - key for key in all_missing_keys if 'num_batches_tracked' not in key - ] - - if unexpected_keys: - err_msg.append('unexpected key in source ' - f'state_dict: {", ".join(unexpected_keys)}\n') - if missing_keys: - err_msg.append( - f'missing keys in source state_dict: {", ".join(missing_keys)}\n') - - rank, _ = get_dist_info() - if len(err_msg) > 0 and rank == 0: - err_msg.insert( - 0, 'The model and loaded state dict do not match exactly\n') - err_msg = '\n'.join(err_msg) - if strict: - raise RuntimeError(err_msg) - elif logger is not None: - logger.warning(err_msg) - else: - print(err_msg) - - -def get_torchvision_models(): - model_urls = dict() - for _, name, ispkg in pkgutil.walk_packages(torchvision.models.__path__): - if ispkg: - continue - _zoo = import_module(f'torchvision.models.{name}') - if hasattr(_zoo, 'model_urls'): - _urls = getattr(_zoo, 'model_urls') - model_urls.update(_urls) - return model_urls - - -def get_external_models(): - mmcv_home = _get_mmcv_home() - default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') - default_urls = load_file(default_json_path) - assert isinstance(default_urls, dict) - external_json_path = osp.join(mmcv_home, 'open_mmlab.json') - if osp.exists(external_json_path): - external_urls = load_file(external_json_path) - assert isinstance(external_urls, dict) - default_urls.update(external_urls) - - return default_urls - - -def get_mmcls_models(): - mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') - mmcls_urls = load_file(mmcls_json_path) - - return mmcls_urls - - -def get_deprecated_model_names(): - deprecate_json_path = osp.join(mmcv.__path__[0], - 'model_zoo/deprecated.json') - deprecate_urls = load_file(deprecate_json_path) - assert isinstance(deprecate_urls, dict) - - return deprecate_urls - - -def _process_mmcls_checkpoint(checkpoint): - state_dict = checkpoint['state_dict'] - new_state_dict = OrderedDict() - for k, v in state_dict.items(): - if k.startswith('backbone.'): - new_state_dict[k[9:]] = v - new_checkpoint = dict(state_dict=new_state_dict) - - return new_checkpoint - - -class CheckpointLoader: - """A general checkpoint loader to manage all schemes.""" - - _schemes = {} - - @classmethod - def _register_scheme(cls, prefixes, loader, force=False): - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if (prefix not in cls._schemes) or force: - cls._schemes[prefix] = loader - else: - raise KeyError( - f'{prefix} is already registered as a loader backend, ' - 'add "force=True" if you want to override it') - # sort, longer prefixes take priority - cls._schemes = OrderedDict( - sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) - - @classmethod - def register_scheme(cls, prefixes, loader=None, force=False): - """Register a loader to CheckpointLoader. - - This method can be used as a normal class method or a decorator. - - Args: - prefixes (str or list[str] or tuple[str]): - The prefix of the registered loader. - loader (function, optional): The loader function to be registered. - When this method is used as a decorator, loader is None. - Defaults to None. - force (bool, optional): Whether to override the loader - if the prefix has already been registered. Defaults to False. - """ - - if loader is not None: - cls._register_scheme(prefixes, loader, force=force) - return - - def _register(loader_cls): - cls._register_scheme(prefixes, loader_cls, force=force) - return loader_cls - - return _register - - @classmethod - def _get_checkpoint_loader(cls, path): - """Finds a loader that supports the given path. Falls back to the local - loader if no other loader is found. - - Args: - path (str): checkpoint path - - Returns: - loader (function): checkpoint loader - """ - - for p in cls._schemes: - if path.startswith(p): - return cls._schemes[p] - - @classmethod - def load_checkpoint(cls, filename, map_location=None, logger=None): - """load checkpoint through URL scheme path. - - Args: - filename (str): checkpoint file name with given prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - logger (:mod:`logging.Logger`, optional): The logger for message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint_loader = cls._get_checkpoint_loader(filename) - class_name = checkpoint_loader.__name__ - mmcv.print_log( - f'load checkpoint from {class_name[10:]} path: {filename}', logger) - return checkpoint_loader(filename, map_location) - - -@CheckpointLoader.register_scheme(prefixes='') -def load_from_local(filename, map_location): - """load checkpoint by local file path. - - Args: - filename (str): local checkpoint file path - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - if not osp.isfile(filename): - raise IOError(f'{filename} is not a checkpoint file') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) -def load_from_http(filename, map_location=None, model_dir=None): - """load checkpoint through HTTP or HTTPS scheme path. In distributed - setting, this function only download checkpoint at local rank 0. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - model_dir (string, optional): directory in which to save the object, - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - rank, world_size = get_dist_info() - rank = int(os.environ.get('LOCAL_RANK', rank)) - if rank == 0: - checkpoint = model_zoo.load_url( - filename, model_dir=model_dir, map_location=map_location) - if world_size > 1: - torch.distributed.barrier() - if rank > 0: - checkpoint = model_zoo.load_url( - filename, model_dir=model_dir, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='pavi://') -def load_from_pavi(filename, map_location=None): - """load checkpoint through the file path prefixed with pavi. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with pavi prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - assert filename.startswith('pavi://'), \ - f'Expected filename startswith `pavi://`, but get {filename}' - model_path = filename[7:] - - try: - from pavi import modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - - model = modelcloud.get(model_path) - with TemporaryDirectory() as tmp_dir: - downloaded_file = osp.join(tmp_dir, model.name) - model.download(downloaded_file) - checkpoint = torch.load(downloaded_file, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='s3://') -def load_from_ceph(filename, map_location=None, backend='petrel'): - """load checkpoint through the file path prefixed with s3. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with s3 prefix - map_location (str, optional): Same as :func:`torch.load`. - backend (str, optional): The storage backend type. Options are 'ceph', - 'petrel'. Default: 'petrel'. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - allowed_backends = ['ceph', 'petrel'] - if backend not in allowed_backends: - raise ValueError(f'Load from Backend {backend} is not supported.') - - if backend == 'ceph': - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead') - - # CephClient and PetrelBackend have the same prefix 's3://' and the latter - # will be chosen as default. If PetrelBackend can not be instantiated - # successfully, the CephClient will be chosen. - try: - file_client = FileClient(backend=backend) - except ImportError: - allowed_backends.remove(backend) - file_client = FileClient(backend=allowed_backends[0]) - - with io.BytesIO(file_client.get(filename)) as buffer: - checkpoint = torch.load(buffer, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) -def load_from_torchvision(filename, map_location=None): - """load checkpoint through the file path prefixed with modelzoo or - torchvision. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - model_urls = get_torchvision_models() - if filename.startswith('modelzoo://'): - warnings.warn('The URL scheme of "modelzoo://" is deprecated, please ' - 'use "torchvision://" instead') - model_name = filename[11:] - else: - model_name = filename[14:] - return load_from_http(model_urls[model_name], map_location=map_location) - - -@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) -def load_from_openmmlab(filename, map_location=None): - """load checkpoint through the file path prefixed with open-mmlab or - openmmlab. - - Args: - filename (str): checkpoint file path with open-mmlab or - openmmlab prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_external_models() - prefix_str = 'open-mmlab://' - if filename.startswith(prefix_str): - model_name = filename[13:] - else: - model_name = filename[12:] - prefix_str = 'openmmlab://' - - deprecated_urls = get_deprecated_model_names() - if model_name in deprecated_urls: - warnings.warn(f'{prefix_str}{model_name} is deprecated in favor ' - f'of {prefix_str}{deprecated_urls[model_name]}') - model_name = deprecated_urls[model_name] - model_url = model_urls[model_name] - # check if is url - if model_url.startswith(('http://', 'https://')): - checkpoint = load_from_http(model_url, map_location=map_location) - else: - filename = osp.join(_get_mmcv_home(), model_url) - if not osp.isfile(filename): - raise IOError(f'{filename} is not a checkpoint file') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='mmcls://') -def load_from_mmcls(filename, map_location=None): - """load checkpoint through the file path prefixed with mmcls. - - Args: - filename (str): checkpoint file path with mmcls prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_mmcls_models() - model_name = filename[8:] - checkpoint = load_from_http( - model_urls[model_name], map_location=map_location) - checkpoint = _process_mmcls_checkpoint(checkpoint) - return checkpoint - - -def _load_checkpoint(filename, map_location=None, logger=None): - """Load checkpoint from somewhere (modelzoo, file, url). - - Args: - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str, optional): Same as :func:`torch.load`. - Default: None. - logger (:mod:`logging.Logger`, optional): The logger for error message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. It can be either an - OrderedDict storing model weights or a dict containing other - information, which depends on the checkpoint. - """ - return CheckpointLoader.load_checkpoint(filename, map_location, logger) - - -def _load_checkpoint_with_prefix(prefix, filename, map_location=None): - """Load partial pretrained model with specific prefix. - - Args: - prefix (str): The prefix of sub-module. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str | None): Same as :func:`torch.load`. Default: None. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint = _load_checkpoint(filename, map_location=map_location) - - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - if not prefix.endswith('.'): - prefix += '.' - prefix_len = len(prefix) - - state_dict = { - k[prefix_len:]: v - for k, v in state_dict.items() if k.startswith(prefix) - } - - assert state_dict, f'{prefix} is not in the pretrained model' - return state_dict - - -def load_checkpoint(model, - filename, - map_location=None, - strict=False, - logger=None, - revise_keys=[(r'^module\.', '')]): - """Load checkpoint from a file or URI. - - Args: - model (Module): Module to load checkpoint. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str): Same as :func:`torch.load`. - strict (bool): Whether to allow different params for the model and - checkpoint. - logger (:mod:`logging.Logger` or None): The logger for error message. - revise_keys (list): A list of customized keywords to modify the - state_dict in checkpoint. Each item is a (pattern, replacement) - pair of the regular expression operations. Default: strip - the prefix 'module.' by [(r'^module\\.', '')]. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - checkpoint = _load_checkpoint(filename, map_location, logger) - # OrderedDict is a subclass of dict - if not isinstance(checkpoint, dict): - raise RuntimeError( - f'No state_dict found in checkpoint file {filename}') - # get state_dict from checkpoint - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - - # strip prefix of state_dict - metadata = getattr(state_dict, '_metadata', OrderedDict()) - for p, r in revise_keys: - state_dict = OrderedDict( - {re.sub(p, r, k): v - for k, v in state_dict.items()}) - # Keep metadata in state_dict - state_dict._metadata = metadata - - # load state_dict - load_state_dict(model, state_dict, strict, logger) - return checkpoint - - -def weights_to_cpu(state_dict): - """Copy a model state_dict to cpu. - - Args: - state_dict (OrderedDict): Model weights on GPU. - - Returns: - OrderedDict: Model weights on GPU. - """ - state_dict_cpu = OrderedDict() - for key, val in state_dict.items(): - state_dict_cpu[key] = val.cpu() - # Keep metadata in state_dict - state_dict_cpu._metadata = getattr(state_dict, '_metadata', OrderedDict()) - return state_dict_cpu - - -def _save_to_state_dict(module, destination, prefix, keep_vars): - """Saves module state to `destination` dictionary. - - This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. - - Args: - module (nn.Module): The module to generate state_dict. - destination (dict): A dict where state will be stored. - prefix (str): The prefix for parameters and buffers used in this - module. - """ - for name, param in module._parameters.items(): - if param is not None: - destination[prefix + name] = param if keep_vars else param.detach() - for name, buf in module._buffers.items(): - # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d - if buf is not None: - destination[prefix + name] = buf if keep_vars else buf.detach() - - -def get_state_dict(module, destination=None, prefix='', keep_vars=False): - """Returns a dictionary containing a whole state of the module. - - Both parameters and persistent buffers (e.g. running averages) are - included. Keys are corresponding parameter and buffer names. - - This method is modified from :meth:`torch.nn.Module.state_dict` to - recursively check parallel module in case that the model has a complicated - structure, e.g., nn.Module(nn.Module(DDP)). - - Args: - module (nn.Module): The module to generate state_dict. - destination (OrderedDict): Returned dict for the state of the - module. - prefix (str): Prefix of the key. - keep_vars (bool): Whether to keep the variable property of the - parameters. Default: False. - - Returns: - dict: A dictionary containing a whole state of the module. - """ - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - - # below is the same as torch.nn.Module.state_dict() - if destination is None: - destination = OrderedDict() - destination._metadata = OrderedDict() - destination._metadata[prefix[:-1]] = local_metadata = dict( - version=module._version) - _save_to_state_dict(module, destination, prefix, keep_vars) - for name, child in module._modules.items(): - if child is not None: - get_state_dict( - child, destination, prefix + name + '.', keep_vars=keep_vars) - for hook in module._state_dict_hooks.values(): - hook_result = hook(module, destination, prefix, local_metadata) - if hook_result is not None: - destination = hook_result - return destination - - -def save_checkpoint(model, - filename, - optimizer=None, - meta=None, - file_client_args=None): - """Save checkpoint to file. - - The checkpoint will have 3 fields: ``meta``, ``state_dict`` and - ``optimizer``. By default ``meta`` will contain version and time info. - - Args: - model (Module): Module whose params are to be saved. - filename (str): Checkpoint filename. - optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. - meta (dict, optional): Metadata to be saved in checkpoint. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError(f'meta must be a dict or None, but got {type(meta)}') - meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) - - if is_module_wrapper(model): - model = model.module - - if hasattr(model, 'CLASSES') and model.CLASSES is not None: - # save class name to the meta - meta.update(CLASSES=model.CLASSES) - - checkpoint = { - 'meta': meta, - 'state_dict': weights_to_cpu(get_state_dict(model)) - } - # save optimizer state dict in the checkpoint - if isinstance(optimizer, Optimizer): - checkpoint['optimizer'] = optimizer.state_dict() - elif isinstance(optimizer, dict): - checkpoint['optimizer'] = {} - for name, optim in optimizer.items(): - checkpoint['optimizer'][name] = optim.state_dict() - - if filename.startswith('pavi://'): - if file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" if filename starts with' - f'"pavi://", but got {file_client_args}') - try: - from pavi import modelcloud - from pavi import exception - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - model_path = filename[7:] - root = modelcloud.Folder() - model_dir, model_name = osp.split(model_path) - try: - model = modelcloud.get(model_dir) - except exception.NodeNotFoundError: - model = root.create_training_model(model_dir) - with TemporaryDirectory() as tmp_dir: - checkpoint_file = osp.join(tmp_dir, model_name) - with open(checkpoint_file, 'wb') as f: - torch.save(checkpoint, f) - f.flush() - model.create_file(checkpoint_file, name=model_name) - else: - file_client = FileClient.infer_client(file_client_args, filename) - with io.BytesIO() as f: - torch.save(checkpoint, f) - file_client.put(f.getvalue(), filename) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/default_constructor.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/default_constructor.py deleted file mode 100644 index 0bad847f2..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/default_constructor.py +++ /dev/null @@ -1,44 +0,0 @@ -from .builder import RUNNER_BUILDERS, RUNNERS - - -@RUNNER_BUILDERS.register_module() -class DefaultRunnerConstructor: - """Default constructor for runners. - - Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. - For example, We can inject some new properties and functions for `Runner`. - - Example: - >>> from mmcv.runner import RUNNER_BUILDERS, build_runner - >>> # Define a new RunnerReconstructor - >>> @RUNNER_BUILDERS.register_module() - >>> class MyRunnerConstructor: - ... def __init__(self, runner_cfg, default_args=None): - ... if not isinstance(runner_cfg, dict): - ... raise TypeError('runner_cfg should be a dict', - ... f'but got {type(runner_cfg)}') - ... self.runner_cfg = runner_cfg - ... self.default_args = default_args - ... - ... def __call__(self): - ... runner = RUNNERS.build(self.runner_cfg, - ... default_args=self.default_args) - ... # Add new properties for existing runner - ... runner.my_name = 'my_runner' - ... runner.my_function = lambda self: print(self.my_name) - ... ... - >>> # build your runner - >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, - ... constructor='MyRunnerConstructor') - >>> runner = build_runner(runner_cfg) - """ - - def __init__(self, runner_cfg, default_args=None): - if not isinstance(runner_cfg, dict): - raise TypeError('runner_cfg should be a dict', - f'but got {type(runner_cfg)}') - self.runner_cfg = runner_cfg - self.default_args = default_args - - def __call__(self): - return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/dist_utils.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/dist_utils.py deleted file mode 100644 index d3a1ef3fd..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/dist_utils.py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import os -import subprocess -from collections import OrderedDict - -import torch -import torch.multiprocessing as mp -from torch import distributed as dist -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - - -def init_dist(launcher, backend='nccl', **kwargs): - if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') - if launcher == 'pytorch': - _init_dist_pytorch(backend, **kwargs) - elif launcher == 'mpi': - _init_dist_mpi(backend, **kwargs) - elif launcher == 'slurm': - _init_dist_slurm(backend, **kwargs) - else: - raise ValueError(f'Invalid launcher type: {launcher}') - - -def _init_dist_pytorch(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['RANK']) - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_mpi(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['OMPI_COMM_WORLD_RANK']) - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_slurm(backend, port=None): - """Initialize slurm distributed training environment. - - If argument ``port`` is not specified, then the master port will be system - environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system - environment variable, then a default port ``29500`` will be used. - - Args: - backend (str): Backend of torch.distributed. - port (int, optional): Master port. Defaults to None. - """ - proc_id = int(os.environ['SLURM_PROCID']) - ntasks = int(os.environ['SLURM_NTASKS']) - node_list = os.environ['SLURM_NODELIST'] - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(proc_id % num_gpus) - addr = subprocess.getoutput( - f'scontrol show hostname {node_list} | head -n1') - # specify master port - if port is not None: - os.environ['MASTER_PORT'] = str(port) - elif 'MASTER_PORT' in os.environ: - pass # use MASTER_PORT in the environment variable - else: - # 29500 is torch.distributed default port - os.environ['MASTER_PORT'] = '29500' - # use MASTER_ADDR in the environment variable if it already exists - if 'MASTER_ADDR' not in os.environ: - os.environ['MASTER_ADDR'] = addr - os.environ['WORLD_SIZE'] = str(ntasks) - os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) - os.environ['RANK'] = str(proc_id) - dist.init_process_group(backend=backend) - - -def get_dist_info(): - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - world_size = dist.get_world_size() - else: - rank = 0 - world_size = 1 - return rank, world_size - - -def master_only(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - rank, _ = get_dist_info() - if rank == 0: - return func(*args, **kwargs) - - return wrapper - - -def allreduce_params(params, coalesce=True, bucket_size_mb=-1): - """Allreduce parameters. - - Args: - params (list[torch.Parameters]): List of parameters or buffers of a - model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - _, world_size = get_dist_info() - if world_size == 1: - return - params = [param.data for param in params] - if coalesce: - _allreduce_coalesced(params, world_size, bucket_size_mb) - else: - for tensor in params: - dist.all_reduce(tensor.div_(world_size)) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - _, world_size = get_dist_info() - if world_size == 1: - return - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/epoch_based_runner.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/epoch_based_runner.py deleted file mode 100644 index 2dd29357a..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/epoch_based_runner.py +++ /dev/null @@ -1,187 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .utils import get_host_info - - -@RUNNERS.register_module() -class EpochBasedRunner(BaseRunner): - """Epoch-based Runner. - - This runner train models epoch by epoch. - """ - - def run_iter(self, data_batch, train_mode, **kwargs): - if self.batch_processor is not None: - outputs = self.batch_processor( - self.model, data_batch, train_mode=train_mode, **kwargs) - elif train_mode: - outputs = self.model.train_step(data_batch, self.optimizer, - **kwargs) - else: - outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('"batch_processor()" or "model.train_step()"' - 'and "model.val_step()" must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - self._iter += 1 - - self.call_hook('after_train_epoch') - self._epoch += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - self.call_hook('before_val_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_val_iter') - self.run_iter(data_batch, train_mode=False) - self.call_hook('after_val_iter') - - self.call_hook('after_val_epoch') - - def run(self, data_loaders, workflow, max_epochs=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, epochs) to specify the - running order and epochs. E.g, [('train', 2), ('val', 1)] means - running 2 epochs for training and 1 epoch for validation, - iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_epochs is not None: - warnings.warn( - 'setting max_epochs in run is deprecated, ' - 'please set max_epochs in runner_config', DeprecationWarning) - self._max_epochs = max_epochs - - assert self._max_epochs is not None, ( - 'max_epochs must be specified during instantiation') - - for i, flow in enumerate(workflow): - mode, epochs = flow - if mode == 'train': - self._max_iters = self._max_epochs * len(data_loaders[i]) - break - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d epochs', workflow, - self._max_epochs) - self.call_hook('before_run') - - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - mode, epochs = flow - if isinstance(mode, str): # self.train() - if not hasattr(self, mode): - raise ValueError( - f'runner has no method named "{mode}" to run an ' - 'epoch') - epoch_runner = getattr(self, mode) - else: - raise TypeError( - 'mode in workflow must be a str, but got {}'.format( - type(mode))) - - for _ in range(epochs): - if mode == 'train' and self.epoch >= self._max_epochs: - break - epoch_runner(data_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_run') - - def save_checkpoint(self, - out_dir, - filename_tmpl='epoch_{}.pth', - save_optimizer=True, - meta=None, - create_symlink=True): - """Save the checkpoint. - - Args: - out_dir (str): The directory that checkpoints are saved. - filename_tmpl (str, optional): The checkpoint filename template, - which contains a placeholder for the epoch number. - Defaults to 'epoch_{}.pth'. - save_optimizer (bool, optional): Whether to save the optimizer to - the checkpoint. Defaults to True. - meta (dict, optional): The meta information to be saved in the - checkpoint. Defaults to None. - create_symlink (bool, optional): Whether to create a symlink - "latest.pth" to point to the latest checkpoint. - Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.epoch + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - -@RUNNERS.register_module() -class Runner(EpochBasedRunner): - """Deprecated name of EpochBasedRunner.""" - - def __init__(self, *args, **kwargs): - warnings.warn( - 'Runner was deprecated, please use EpochBasedRunner instead') - super().__init__(*args, **kwargs) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/fp16_utils.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/fp16_utils.py deleted file mode 100644 index 4baab939a..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/fp16_utils.py +++ /dev/null @@ -1,410 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import warnings -from collections import abc -from inspect import getfullargspec - -import numpy as np -import torch -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .dist_utils import allreduce_grads as _allreduce_grads - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 - # manually, so the behavior may not be consistent with real amp. - from torch.cuda.amp import autocast -except ImportError: - pass - - -def cast_tensor_type(inputs, src_type, dst_type): - """Recursively convert Tensor in inputs from src_type to dst_type. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype): Source type.. - dst_type (torch.dtype): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - if isinstance(inputs, nn.Module): - return inputs - elif isinstance(inputs, torch.Tensor): - return inputs.to(dst_type) - elif isinstance(inputs, str): - return inputs - elif isinstance(inputs, np.ndarray): - return inputs - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type, dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type, dst_type) for item in inputs) - else: - return inputs - - -def auto_fp16(apply_to=None, out_fp32=False): - """Decorator to enable fp16 training automatically. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If inputs arguments are fp32 tensors, they will - be converted to fp16 automatically. Arguments other than fp32 tensors are - ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp32 (bool): Whether to convert the output back to fp32. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp16 - >>> @auto_fp16() - >>> def forward(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp16 - >>> @auto_fp16(apply_to=('pred', )) - >>> def do_something(self, pred, others): - >>> pass - """ - - def auto_fp16_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@auto_fp16 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - # NOTE: default args are not taken into consideration - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.float, torch.half)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = {} - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.float, torch.half) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=True): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp32: - output = cast_tensor_type(output, torch.half, torch.float) - return output - - return new_func - - return auto_fp16_wrapper - - -def force_fp32(apply_to=None, out_fp16=False): - """Decorator to convert input arguments to fp32 in force. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If there are some inputs that must be processed - in fp32 mode, then this decorator can handle it. If inputs arguments are - fp16 tensors, they will be converted to fp32 automatically. Arguments other - than fp16 tensors are ignored. If you are using PyTorch >= 1.6, - torch.cuda.amp is used as the backend, otherwise, original mmcv - implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp16 (bool): Whether to convert the output back to fp16. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp32 - >>> @force_fp32() - >>> def loss(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp32 - >>> @force_fp32(apply_to=('pred', )) - >>> def post_process(self, pred, others): - >>> pass - """ - - def force_fp32_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@force_fp32 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.half, torch.float)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = dict() - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.half, torch.float) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=False): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp16: - output = cast_tensor_type(output, torch.float, torch.half) - return output - - return new_func - - return force_fp32_wrapper - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - warnings.warning( - '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' - 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads') - _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) - - -def wrap_fp16_model(model): - """Wrap the FP32 model to FP16. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - For PyTorch >= 1.6, this function will - 1. Set fp16 flag inside the model to True. - - Otherwise: - 1. Convert FP32 model to FP16. - 2. Remain some necessary layers to be FP32, e.g., normalization layers. - 3. Set `fp16_enabled` flag inside the model to True. - - Args: - model (nn.Module): Model in FP32. - """ - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.6.0')): - # convert model to fp16 - model.half() - # patch the normalization layers to make it work in fp32 mode - patch_norm_fp32(model) - # set `fp16_enabled` flag - for m in model.modules(): - if hasattr(m, 'fp16_enabled'): - m.fp16_enabled = True - - -def patch_norm_fp32(module): - """Recursively convert normalization layers from FP16 to FP32. - - Args: - module (nn.Module): The modules to be converted in FP16. - - Returns: - nn.Module: The converted module, the normalization layers have been - converted to FP32. - """ - if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): - module.float() - if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': - module.forward = patch_forward_method(module.forward, torch.half, - torch.float) - for child in module.children(): - patch_norm_fp32(child) - return module - - -def patch_forward_method(func, src_type, dst_type, convert_output=True): - """Patch the forward method of a module. - - Args: - func (callable): The original forward method. - src_type (torch.dtype): Type of input arguments to be converted from. - dst_type (torch.dtype): Type of input arguments to be converted to. - convert_output (bool): Whether to convert the output back to src_type. - - Returns: - callable: The patched forward method. - """ - - def new_forward(*args, **kwargs): - output = func(*cast_tensor_type(args, src_type, dst_type), - **cast_tensor_type(kwargs, src_type, dst_type)) - if convert_output: - output = cast_tensor_type(output, dst_type, src_type) - return output - - return new_forward - - -class LossScaler: - """Class that manages loss scaling in mixed precision training which - supports both dynamic or static mode. - - The implementation refers to - https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. - Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. - It's important to understand how :class:`LossScaler` operates. - Loss scaling is designed to combat the problem of underflowing - gradients encountered at long times when training fp16 networks. - Dynamic loss scaling begins by attempting a very high loss - scale. Ironically, this may result in OVERflowing gradients. - If overflowing gradients are encountered, :class:`FP16_Optimizer` then - skips the update step for this particular iteration/minibatch, - and :class:`LossScaler` adjusts the loss scale to a lower value. - If a certain number of iterations occur without overflowing gradients - detected,:class:`LossScaler` increases the loss scale once more. - In this way :class:`LossScaler` attempts to "ride the edge" of always - using the highest loss scale possible without incurring overflow. - - Args: - init_scale (float): Initial loss scale value, default: 2**32. - scale_factor (float): Factor used when adjusting the loss scale. - Default: 2. - mode (str): Loss scaling mode. 'dynamic' or 'static' - scale_window (int): Number of consecutive iterations without an - overflow to wait before increasing the loss scale. Default: 1000. - """ - - def __init__(self, - init_scale=2**32, - mode='dynamic', - scale_factor=2., - scale_window=1000): - self.cur_scale = init_scale - self.cur_iter = 0 - assert mode in ('dynamic', - 'static'), 'mode can only be dynamic or static' - self.mode = mode - self.last_overflow_iter = -1 - self.scale_factor = scale_factor - self.scale_window = scale_window - - def has_overflow(self, params): - """Check if params contain overflow.""" - if self.mode != 'dynamic': - return False - for p in params: - if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): - return True - return False - - def _has_inf_or_nan(x): - """Check if params contain NaN.""" - try: - cpu_sum = float(x.float().sum()) - except RuntimeError as instance: - if 'value cannot be converted' not in instance.args[0]: - raise - return True - else: - if cpu_sum == float('inf') or cpu_sum == -float('inf') \ - or cpu_sum != cpu_sum: - return True - return False - - def update_scale(self, overflow): - """update the current loss scale value when overflow happens.""" - if self.mode != 'dynamic': - return - if overflow: - self.cur_scale = max(self.cur_scale / self.scale_factor, 1) - self.last_overflow_iter = self.cur_iter - else: - if (self.cur_iter - self.last_overflow_iter) % \ - self.scale_window == 0: - self.cur_scale *= self.scale_factor - self.cur_iter += 1 - - def state_dict(self): - """Returns the state of the scaler as a :class:`dict`.""" - return dict( - cur_scale=self.cur_scale, - cur_iter=self.cur_iter, - mode=self.mode, - last_overflow_iter=self.last_overflow_iter, - scale_factor=self.scale_factor, - scale_window=self.scale_window) - - def load_state_dict(self, state_dict): - """Loads the loss_scaler state dict. - - Args: - state_dict (dict): scaler state. - """ - self.cur_scale = state_dict['cur_scale'] - self.cur_iter = state_dict['cur_iter'] - self.mode = state_dict['mode'] - self.last_overflow_iter = state_dict['last_overflow_iter'] - self.scale_factor = state_dict['scale_factor'] - self.scale_window = state_dict['scale_window'] - - @property - def loss_scale(self): - return self.cur_scale diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/__init__.py deleted file mode 100644 index 915af28ce..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkpoint import CheckpointHook -from .closure import ClosureHook -from .ema import EMAHook -from .evaluation import DistEvalHook, EvalHook -from .hook import HOOKS, Hook -from .iter_timer import IterTimerHook -from .logger import (DvcliveLoggerHook, LoggerHook, MlflowLoggerHook, - NeptuneLoggerHook, PaviLoggerHook, TensorboardLoggerHook, - TextLoggerHook, WandbLoggerHook) -from .lr_updater import LrUpdaterHook -from .memory import EmptyCacheHook -from .momentum_updater import MomentumUpdaterHook -from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, OptimizerHook) -from .profiler import ProfilerHook -from .sampler_seed import DistSamplerSeedHook -from .sync_buffer import SyncBuffersHook - -__all__ = [ - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'OptimizerHook', 'Fp16OptimizerHook', 'IterTimerHook', - 'DistSamplerSeedHook', 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'DvcliveLoggerHook', - 'MomentumUpdaterHook', 'SyncBuffersHook', 'EMAHook', 'EvalHook', - 'DistEvalHook', 'ProfilerHook', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/checkpoint.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/checkpoint.py deleted file mode 100644 index 7bb75f402..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/checkpoint.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -from mmcv.fileio import FileClient -from ..dist_utils import allreduce_params, master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class CheckpointHook(Hook): - """Save checkpoints periodically. - - Args: - interval (int): The saving period. If ``by_epoch=True``, interval - indicates epochs, otherwise it indicates iterations. - Default: -1, which means "never". - by_epoch (bool): Saving checkpoints by epoch or by iteration. - Default: True. - save_optimizer (bool): Whether to save optimizer state_dict in the - checkpoint. It is usually used for resuming experiments. - Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, ``runner.work_dir`` will be used by default. If - specified, the ``out_dir`` will be the concatenation of ``out_dir`` - and the last level directory of ``runner.work_dir``. - `Changed in version 1.3.16.` - max_keep_ckpts (int, optional): The maximum checkpoints to keep. - In some cases we want only the latest few checkpoints and would - like to delete old ones to save the disk space. - Default: -1, which means unlimited. - save_last (bool, optional): Whether to force the last checkpoint to be - saved regardless of interval. Default: True. - sync_buffer (bool, optional): Whether to synchronize buffers in - different gpus. Default: False. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - - .. warning:: - Before v1.3.16, the ``out_dir`` argument indicates the path where the - checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the - root directory and the final path to save checkpoint is the - concatenation of ``out_dir`` and the last level directory of - ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" - and the value of ``runner.work_dir`` is "/path/of/B", then the final - path will be "/path/of/A/B". - """ - - def __init__(self, - interval=-1, - by_epoch=True, - save_optimizer=True, - out_dir=None, - max_keep_ckpts=-1, - save_last=True, - sync_buffer=False, - file_client_args=None, - **kwargs): - self.interval = interval - self.by_epoch = by_epoch - self.save_optimizer = save_optimizer - self.out_dir = out_dir - self.max_keep_ckpts = max_keep_ckpts - self.save_last = save_last - self.args = kwargs - self.sync_buffer = sync_buffer - self.file_client_args = file_client_args - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - - runner.logger.info((f'Checkpoints will be saved to {self.out_dir} by ' - f'{self.file_client.name}.')) - - # disable the create_symlink option because some file backends do not - # allow to create a symlink - if 'create_symlink' in self.args: - if self.args[ - 'create_symlink'] and not self.file_client.allow_symlink: - self.args['create_symlink'] = False - warnings.warn( - ('create_symlink is set as True by the user but is changed' - 'to be False because creating symbolic link is not ' - f'allowed in {self.file_client.name}')) - else: - self.args['create_symlink'] = self.file_client.allow_symlink - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` epochs - # 2. reach the last epoch of training - if self.every_n_epochs( - runner, self.interval) or (self.save_last - and self.is_last_epoch(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.epoch + 1} epochs') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) - - @master_only - def _save_checkpoint(self, runner): - """Save the current checkpoint and delete unwanted checkpoint.""" - runner.save_checkpoint( - self.out_dir, save_optimizer=self.save_optimizer, **self.args) - if runner.meta is not None: - if self.by_epoch: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) - else: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) - runner.meta.setdefault('hook_msgs', dict()) - runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( - self.out_dir, cur_ckpt_filename) - # remove other checkpoints - if self.max_keep_ckpts > 0: - if self.by_epoch: - name = 'epoch_{}.pth' - current_ckpt = runner.epoch + 1 - else: - name = 'iter_{}.pth' - current_ckpt = runner.iter + 1 - redundant_ckpts = range( - current_ckpt - self.max_keep_ckpts * self.interval, 0, - -self.interval) - filename_tmpl = self.args.get('filename_tmpl', name) - for _step in redundant_ckpts: - ckpt_path = self.file_client.join_path( - self.out_dir, filename_tmpl.format(_step)) - if self.file_client.isfile(ckpt_path): - self.file_client.remove(ckpt_path) - else: - break - - def after_train_iter(self, runner): - if self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` iterations - # 2. reach the last iteration of training - if self.every_n_iters( - runner, self.interval) or (self.save_last - and self.is_last_iter(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.iter + 1} iterations') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/closure.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/closure.py deleted file mode 100644 index b955f81f4..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/closure.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ClosureHook(Hook): - - def __init__(self, fn_name, fn): - assert hasattr(self, fn_name) - assert callable(fn) - setattr(self, fn_name, fn) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/ema.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/ema.py deleted file mode 100644 index 15c7e6808..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/ema.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...parallel import is_module_wrapper -from ..hooks.hook import HOOKS, Hook - - -@HOOKS.register_module() -class EMAHook(Hook): - r"""Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. - - .. math:: - - \text{Xema\_{t+1}} = (1 - \text{momentum}) \times - \text{Xema\_{t}} + \text{momentum} \times X_t - - Args: - momentum (float): The momentum used for updating ema parameter. - Defaults to 0.0002. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - warm_up (int): During first warm_up steps, we may use smaller momentum - to update ema parameters more slowly. Defaults to 100. - resume_from (str): The checkpoint path. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - warm_up=100, - resume_from=None): - assert isinstance(interval, int) and interval > 0 - self.warm_up = warm_up - self.interval = interval - assert momentum > 0 and momentum < 1 - self.momentum = momentum**interval - self.checkpoint = resume_from - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - self.model_parameters = dict(model.named_parameters(recurse=True)) - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers(recurse=True)) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - curr_step = runner.iter - # We warm up the momentum considering the instability at beginning - momentum = min(self.momentum, - (1 + curr_step) / (self.warm_up + curr_step)) - if curr_step % self.interval != 0: - return - for name, parameter in self.model_parameters.items(): - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/evaluation.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/evaluation.py deleted file mode 100644 index 1eeb44650..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/evaluation.py +++ /dev/null @@ -1,509 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from math import inf - -import torch.distributed as dist -from torch.nn.modules.batchnorm import _BatchNorm -from torch.utils.data import DataLoader - -from mmcv.fileio import FileClient -from mmcv.utils import is_seq_of -from .hook import Hook -from .logger import LoggerHook - - -class EvalHook(Hook): - """Non-Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader, and return the test results. If ``None``, the default - test function ``mmcv.engine.single_gpu_test`` will be used. - (default: ``None``) - greater_keys (List[str] | None, optional): Metric keys that will be - inferred by 'greater' comparison rule. If ``None``, - _default_greater_keys will be used. (default: ``None``) - less_keys (List[str] | None, optional): Metric keys that will be - inferred by 'less' comparison rule. If ``None``, _default_less_keys - will be used. (default: ``None``) - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - `New in version 1.3.16.` - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - `New in version 1.3.16.` - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - - Notes: - If new arguments are added for EvalHook, tools/test.py, - tools/eval_metric.py may be affected. - """ - - # Since the key for determine greater or less is related to the downstream - # tasks, downstream repos may need to overwrite the following inner - # variable accordingly. - - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - _default_greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - _default_less_keys = ['loss'] - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - out_dir=None, - file_client_args=None, - **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError(f'dataloader must be a pytorch DataLoader, ' - f'but got {type(dataloader)}') - - if interval <= 0: - raise ValueError(f'interval must be a positive number, ' - f'but got {interval}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' - - if start is not None and start < 0: - raise ValueError(f'The evaluation start epoch {start} is smaller ' - f'than 0') - - self.dataloader = dataloader - self.interval = interval - self.start = start - self.by_epoch = by_epoch - - assert isinstance(save_best, str) or save_best is None, \ - '""save_best"" should be a str or None ' \ - f'rather than {type(save_best)}' - self.save_best = save_best - self.eval_kwargs = eval_kwargs - self.initial_flag = True - - if test_fn is None: - from mmcv.engine import single_gpu_test - self.test_fn = single_gpu_test - else: - self.test_fn = test_fn - - if greater_keys is None: - self.greater_keys = self._default_greater_keys - else: - if not isinstance(greater_keys, (list, tuple)): - greater_keys = (greater_keys, ) - assert is_seq_of(greater_keys, str) - self.greater_keys = greater_keys - - if less_keys is None: - self.less_keys = self._default_less_keys - else: - if not isinstance(less_keys, (list, tuple)): - less_keys = (less_keys, ) - assert is_seq_of(less_keys, str) - self.less_keys = less_keys - - if self.save_best is not None: - self.best_ckpt_path = None - self._init_rule(rule, self.save_best) - - self.out_dir = out_dir - self.file_client_args = file_client_args - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific (note that the key indicator matching - is case-insensitive): - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator != 'auto': - # `_lc` here means we use the lower case of keys for - # case-insensitive matching - key_indicator_lc = key_indicator.lower() - greater_keys = [key.lower() for key in self.greater_keys] - less_keys = [key.lower() for key in self.less_keys] - - if key_indicator_lc in greater_keys: - rule = 'greater' - elif key_indicator_lc in less_keys: - rule = 'less' - elif any(key in key_indicator_lc for key in greater_keys): - rule = 'greater' - elif any(key in key_indicator_lc for key in less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - if self.rule is not None: - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'The best checkpoint will be saved to {self.out_dir} by ' - f'{self.file_client.name}')) - - if self.save_best is not None: - if runner.meta is None: - warnings.warn('runner.meta is None. Creating an empty one.') - runner.meta = dict() - runner.meta.setdefault('hook_msgs', dict()) - self.best_ckpt_path = runner.meta['hook_msgs'].get( - 'best_ckpt', None) - - def before_train_iter(self, runner): - """Evaluate the model only at the start of training by iteration.""" - if self.by_epoch or not self.initial_flag: - return - if self.start is not None and runner.iter >= self.start: - self.after_train_iter(runner) - self.initial_flag = False - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - if not (self.by_epoch and self.initial_flag): - return - if self.start is not None and runner.epoch >= self.start: - self.after_train_epoch(runner) - self.initial_flag = False - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch and self._should_evaluate(runner): - # Because the priority of EvalHook is higher than LoggerHook, the - # training log and the evaluating log are mixed. Therefore, - # we need to dump the training log and clear it before evaluating - # log is generated. In addition, this problem will only appear in - # `IterBasedRunner` whose `self.by_epoch` is False, because - # `EpochBasedRunner` whose `self.by_epoch` is True calls - # `_do_evaluate` in `after_train_epoch` stage, and at this stage - # the training log has been printed, so it will not cause any - # problem. more details at - # https://github.com/open-mmlab/mmsegmentation/issues/694 - for hook in runner._hooks: - if isinstance(hook, LoggerHook): - hook.after_train_iter(runner) - runner.log_buffer.clear() - - self._do_evaluate(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch and self._should_evaluate(runner): - self._do_evaluate(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - results = self.test_fn(runner.model, self.dataloader) - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - def _should_evaluate(self, runner): - """Judge whether to perform evaluation. - - Here is the rule to judge whether to perform evaluation: - 1. It will not perform evaluation during the epoch/iteration interval, - which is determined by ``self.interval``. - 2. It will not perform evaluation if the start time is larger than - current time. - 3. It will not perform evaluation when current time is larger than - the start time but during epoch/iteration interval. - - Returns: - bool: The flag indicating whether to perform evaluation. - """ - if self.by_epoch: - current = runner.epoch - check_time = self.every_n_epochs - else: - current = runner.iter - check_time = self.every_n_iters - - if self.start is None: - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - elif (current + 1) < self.start: - # No evaluation if start is larger than the current time. - return False - else: - # Evaluation only at epochs/iters 3, 5, 7... - # if start==3 and interval==2 - if (current + 1 - self.start) % self.interval: - return False - return True - - def _save_ckpt(self, runner, key_score): - """Save the best checkpoint. - - It will compare the score according to the compare function, write - related information (best score, best checkpoint path) and save the - best checkpoint into ``work_dir``. - """ - if self.by_epoch: - current = f'epoch_{runner.epoch + 1}' - cur_type, cur_time = 'epoch', runner.epoch + 1 - else: - current = f'iter_{runner.iter + 1}' - cur_type, cur_time = 'iter', runner.iter + 1 - - best_score = runner.meta['hook_msgs'].get( - 'best_score', self.init_value_map[self.rule]) - if self.compare_func(key_score, best_score): - best_score = key_score - runner.meta['hook_msgs']['best_score'] = best_score - - if self.best_ckpt_path and self.file_client.isfile( - self.best_ckpt_path): - self.file_client.remove(self.best_ckpt_path) - runner.logger.info( - (f'The previous best checkpoint {self.best_ckpt_path} was ' - 'removed')) - - best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' - self.best_ckpt_path = self.file_client.join_path( - self.out_dir, best_ckpt_name) - runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path - - runner.save_checkpoint( - self.out_dir, best_ckpt_name, create_symlink=False) - runner.logger.info( - f'Now best checkpoint is saved as {best_ckpt_name}.') - runner.logger.info( - f'Best {self.key_indicator} is {best_score:0.4f} ' - f'at {cur_time} {cur_type}.') - - def evaluate(self, runner, results): - """Evaluate the results. - - Args: - runner (:obj:`mmcv.Runner`): The underlined training runner. - results (list): Output results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - - if self.save_best is not None: - # If the performance of model is pool, the `eval_res` may be an - # empty dict and it will raise exception when `self.save_best` is - # not None. More details at - # https://github.com/open-mmlab/mmdetection/issues/6265. - if not eval_res: - warnings.warn( - 'Since `eval_res` is an empty dict, the behavior to save ' - 'the best checkpoint will be skipped in this evaluation.') - return None - - if self.key_indicator == 'auto': - # infer from eval_results - self._init_rule(self.rule, list(eval_res.keys())[0]) - return eval_res[self.key_indicator] - - return None - - -class DistEvalHook(EvalHook): - """Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader in a multi-gpu manner, and return the test results. If - ``None``, the default test function ``mmcv.engine.multi_gpu_test`` - will be used. (default: ``None``) - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - broadcast_bn_buffer (bool): Whether to broadcast the - buffer(running_mean and running_var) of rank 0 to other rank - before evaluation. Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - """ - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - broadcast_bn_buffer=True, - tmpdir=None, - gpu_collect=False, - out_dir=None, - file_client_args=None, - **eval_kwargs): - - if test_fn is None: - from mmcv.engine import multi_gpu_test - test_fn = multi_gpu_test - - super().__init__( - dataloader, - start=start, - interval=interval, - by_epoch=by_epoch, - save_best=save_best, - rule=rule, - test_fn=test_fn, - greater_keys=greater_keys, - less_keys=less_keys, - out_dir=out_dir, - file_client_args=file_client_args, - **eval_kwargs) - - self.broadcast_bn_buffer = broadcast_bn_buffer - self.tmpdir = tmpdir - self.gpu_collect = gpu_collect - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - results = self.test_fn( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to - # save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/hook.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/hook.py deleted file mode 100644 index f2d1c9865..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/hook.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, is_method_overridden - -HOOKS = Registry('hook') - - -class Hook: - stages = ('before_run', 'before_train_epoch', 'before_train_iter', - 'after_train_iter', 'after_train_epoch', 'before_val_epoch', - 'before_val_iter', 'after_val_iter', 'after_val_epoch', - 'after_run') - - def before_run(self, runner): - pass - - def after_run(self, runner): - pass - - def before_epoch(self, runner): - pass - - def after_epoch(self, runner): - pass - - def before_iter(self, runner): - pass - - def after_iter(self, runner): - pass - - def before_train_epoch(self, runner): - self.before_epoch(runner) - - def before_val_epoch(self, runner): - self.before_epoch(runner) - - def after_train_epoch(self, runner): - self.after_epoch(runner) - - def after_val_epoch(self, runner): - self.after_epoch(runner) - - def before_train_iter(self, runner): - self.before_iter(runner) - - def before_val_iter(self, runner): - self.before_iter(runner) - - def after_train_iter(self, runner): - self.after_iter(runner) - - def after_val_iter(self, runner): - self.after_iter(runner) - - def every_n_epochs(self, runner, n): - return (runner.epoch + 1) % n == 0 if n > 0 else False - - def every_n_inner_iters(self, runner, n): - return (runner.inner_iter + 1) % n == 0 if n > 0 else False - - def every_n_iters(self, runner, n): - return (runner.iter + 1) % n == 0 if n > 0 else False - - def end_of_epoch(self, runner): - return runner.inner_iter + 1 == len(runner.data_loader) - - def is_last_epoch(self, runner): - return runner.epoch + 1 == runner._max_epochs - - def is_last_iter(self, runner): - return runner.iter + 1 == runner._max_iters - - def get_triggered_stages(self): - trigger_stages = set() - for stage in Hook.stages: - if is_method_overridden(stage, Hook, self): - trigger_stages.add(stage) - - # some methods will be triggered in multi stages - # use this dict to map method to stages. - method_stages_map = { - 'before_epoch': ['before_train_epoch', 'before_val_epoch'], - 'after_epoch': ['after_train_epoch', 'after_val_epoch'], - 'before_iter': ['before_train_iter', 'before_val_iter'], - 'after_iter': ['after_train_iter', 'after_val_iter'], - } - - for method, map_stages in method_stages_map.items(): - if is_method_overridden(method, Hook, self): - trigger_stages.update(map_stages) - - return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/iter_timer.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/iter_timer.py deleted file mode 100644 index cfd5002fe..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/iter_timer.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import time - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class IterTimerHook(Hook): - - def before_epoch(self, runner): - self.t = time.time() - - def before_iter(self, runner): - runner.log_buffer.update({'data_time': time.time() - self.t}) - - def after_iter(self, runner): - runner.log_buffer.update({'time': time.time() - self.t}) - self.t = time.time() diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/__init__.py deleted file mode 100644 index a0b6b3456..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import LoggerHook -from .dvclive import DvcliveLoggerHook -from .mlflow import MlflowLoggerHook -from .neptune import NeptuneLoggerHook -from .pavi import PaviLoggerHook -from .tensorboard import TensorboardLoggerHook -from .text import TextLoggerHook -from .wandb import WandbLoggerHook - -__all__ = [ - 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', - 'NeptuneLoggerHook', 'DvcliveLoggerHook' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/base.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/base.py deleted file mode 100644 index f84525672..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/base.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from abc import ABCMeta, abstractmethod - -import numpy as np -import torch - -from ..hook import Hook - - -class LoggerHook(Hook): - """Base class for logger hooks. - - Args: - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging. - by_epoch (bool): Whether EpochBasedRunner is used. - """ - - __metaclass__ = ABCMeta - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - self.interval = interval - self.ignore_last = ignore_last - self.reset_flag = reset_flag - self.by_epoch = by_epoch - - @abstractmethod - def log(self, runner): - pass - - @staticmethod - def is_scalar(val, include_np=True, include_torch=True): - """Tell the input variable is a scalar or not. - - Args: - val: Input variable. - include_np (bool): Whether include 0-d np.ndarray as a scalar. - include_torch (bool): Whether include 0-d torch.Tensor as a scalar. - - Returns: - bool: True or False. - """ - if isinstance(val, numbers.Number): - return True - elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: - return True - elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: - return True - else: - return False - - def get_mode(self, runner): - if runner.mode == 'train': - if 'time' in runner.log_buffer.output: - mode = 'train' - else: - mode = 'val' - elif runner.mode == 'val': - mode = 'val' - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return mode - - def get_epoch(self, runner): - if runner.mode == 'train': - epoch = runner.epoch + 1 - elif runner.mode == 'val': - # normal val mode - # runner.epoch += 1 has been done before val workflow - epoch = runner.epoch - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return epoch - - def get_iter(self, runner, inner_iter=False): - """Get the current training iteration step.""" - if self.by_epoch and inner_iter: - current_iter = runner.inner_iter + 1 - else: - current_iter = runner.iter + 1 - return current_iter - - def get_lr_tags(self, runner): - tags = {} - lrs = runner.current_lr() - if isinstance(lrs, dict): - for name, value in lrs.items(): - tags[f'learning_rate/{name}'] = value[0] - else: - tags['learning_rate'] = lrs[0] - return tags - - def get_momentum_tags(self, runner): - tags = {} - momentums = runner.current_momentum() - if isinstance(momentums, dict): - for name, value in momentums.items(): - tags[f'momentum/{name}'] = value[0] - else: - tags['momentum'] = momentums[0] - return tags - - def get_loggable_tags(self, - runner, - allow_scalar=True, - allow_text=False, - add_mode=True, - tags_to_skip=('time', 'data_time')): - tags = {} - for var, val in runner.log_buffer.output.items(): - if var in tags_to_skip: - continue - if self.is_scalar(val) and not allow_scalar: - continue - if isinstance(val, str) and not allow_text: - continue - if add_mode: - var = f'{self.get_mode(runner)}/{var}' - tags[var] = val - tags.update(self.get_lr_tags(runner)) - tags.update(self.get_momentum_tags(runner)) - return tags - - def before_run(self, runner): - for hook in runner.hooks[::-1]: - if isinstance(hook, LoggerHook): - hook.reset_flag = True - break - - def before_epoch(self, runner): - runner.log_buffer.clear() # clear logs of last epoch - - def after_train_iter(self, runner): - if self.by_epoch and self.every_n_inner_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif not self.by_epoch and self.every_n_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif self.end_of_epoch(runner) and not self.ignore_last: - # not precise but more stable - runner.log_buffer.average(self.interval) - - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_train_epoch(self, runner): - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_val_epoch(self, runner): - runner.log_buffer.average() - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/dvclive.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/dvclive.py deleted file mode 100644 index 687cdc58c..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/dvclive.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class DvcliveLoggerHook(LoggerHook): - """Class to log metrics with dvclive. - - It requires `dvclive`_ to be installed. - - Args: - path (str): Directory where dvclive will write TSV log files. - interval (int): Logging interval (every k iterations). - Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. - Default: True. - - .. _dvclive: - https://dvc.org/doc/dvclive - """ - - def __init__(self, - path, - interval=10, - ignore_last=True, - reset_flag=True, - by_epoch=True): - - super(DvcliveLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.path = path - self.import_dvclive() - - def import_dvclive(self): - try: - import dvclive - except ImportError: - raise ImportError( - 'Please run "pip install dvclive" to install dvclive') - self.dvclive = dvclive - - @master_only - def before_run(self, runner): - self.dvclive.init(self.path) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for k, v in tags.items(): - self.dvclive.log(k, v, step=self.get_iter(runner)) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/mlflow.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/mlflow.py deleted file mode 100644 index f9a72592b..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/mlflow.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class MlflowLoggerHook(LoggerHook): - - def __init__(self, - exp_name=None, - tags=None, - log_model=True, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - """Class to log metrics and (optionally) a trained model to MLflow. - - It requires `MLflow`_ to be installed. - - Args: - exp_name (str, optional): Name of the experiment to be used. - Default None. - If not None, set the active experiment. - If experiment does not exist, an experiment with provided name - will be created. - tags (dict of str: str, optional): Tags for the current run. - Default None. - If not None, set tags for the current run. - log_model (bool, optional): Whether to log an MLflow artifact. - Default True. - If True, log runner.model as an MLflow artifact - for the current run. - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging - by_epoch (bool): Whether EpochBasedRunner is used. - - .. _MLflow: - https://www.mlflow.org/docs/latest/index.html - """ - super(MlflowLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_mlflow() - self.exp_name = exp_name - self.tags = tags - self.log_model = log_model - - def import_mlflow(self): - try: - import mlflow - import mlflow.pytorch as mlflow_pytorch - except ImportError: - raise ImportError( - 'Please run "pip install mlflow" to install mlflow') - self.mlflow = mlflow - self.mlflow_pytorch = mlflow_pytorch - - @master_only - def before_run(self, runner): - super(MlflowLoggerHook, self).before_run(runner) - if self.exp_name is not None: - self.mlflow.set_experiment(self.exp_name) - if self.tags is not None: - self.mlflow.set_tags(self.tags) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.mlflow.log_metrics(tags, step=self.get_iter(runner)) - - @master_only - def after_run(self, runner): - if self.log_model: - self.mlflow_pytorch.log_model(runner.model, 'models') diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/neptune.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/neptune.py deleted file mode 100644 index 7a38772b0..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/neptune.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class NeptuneLoggerHook(LoggerHook): - """Class to log metrics to NeptuneAI. - - It requires `neptune-client` to be installed. - - Args: - init_kwargs (dict): a dict contains the initialization keys as below: - - project (str): Name of a project in a form of - namespace/project_name. If None, the value of - NEPTUNE_PROJECT environment variable will be taken. - - api_token (str): User’s API token. - If None, the value of NEPTUNE_API_TOKEN environment - variable will be taken. Note: It is strongly recommended - to use NEPTUNE_API_TOKEN environment variable rather than - placing your API token in plain text in your source code. - - name (str, optional, default is 'Untitled'): Editable name of - the run. Name is displayed in the run's Details and in - Runs table as a column. - Check https://docs.neptune.ai/api-reference/neptune#init for - more init arguments. - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging - by_epoch (bool): Whether EpochBasedRunner is used. - - .. _NeptuneAI: - https://docs.neptune.ai/you-should-know/logging-metadata - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=True, - with_step=True, - by_epoch=True): - - super(NeptuneLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_neptune() - self.init_kwargs = init_kwargs - self.with_step = with_step - - def import_neptune(self): - try: - import neptune.new as neptune - except ImportError: - raise ImportError( - 'Please run "pip install neptune-client" to install neptune') - self.neptune = neptune - self.run = None - - @master_only - def before_run(self, runner): - if self.init_kwargs: - self.run = self.neptune.init(**self.init_kwargs) - else: - self.run = self.neptune.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for tag_name, tag_value in tags.items(): - if self.with_step: - self.run[tag_name].log( - tag_value, step=self.get_iter(runner)) - else: - tags['global_step'] = self.get_iter(runner) - self.run[tag_name].log(tags) - - @master_only - def after_run(self, runner): - self.run.stop() diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/pavi.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/pavi.py deleted file mode 100644 index ba2f6e8df..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/pavi.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp - -import torch -import yaml - -import mmcv -from ....parallel.utils import is_module_wrapper -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class PaviLoggerHook(LoggerHook): - - def __init__(self, - init_kwargs=None, - add_graph=False, - add_last_ckpt=False, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - img_key='img_info'): - super(PaviLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.init_kwargs = init_kwargs - self.add_graph = add_graph - self.add_last_ckpt = add_last_ckpt - self.img_key = img_key - - @master_only - def before_run(self, runner): - super(PaviLoggerHook, self).before_run(runner) - try: - from pavi import SummaryWriter - except ImportError: - raise ImportError('Please run "pip install pavi" to install pavi.') - - self.run_name = runner.work_dir.split('/')[-1] - - if not self.init_kwargs: - self.init_kwargs = dict() - self.init_kwargs['name'] = self.run_name - self.init_kwargs['model'] = runner._model_name - if runner.meta is not None: - if 'config_dict' in runner.meta: - config_dict = runner.meta['config_dict'] - assert isinstance( - config_dict, - dict), ('meta["config_dict"] has to be of a dict, ' - f'but got {type(config_dict)}') - elif 'config_file' in runner.meta: - config_file = runner.meta['config_file'] - config_dict = dict(mmcv.Config.fromfile(config_file)) - else: - config_dict = None - if config_dict is not None: - # 'max_.*iter' is parsed in pavi sdk as the maximum iterations - # to properly set up the progress bar. - config_dict = config_dict.copy() - config_dict.setdefault('max_iter', runner.max_iters) - # non-serializable values are first converted in - # mmcv.dump to json - config_dict = json.loads( - mmcv.dump(config_dict, file_format='json')) - session_text = yaml.dump(config_dict) - self.init_kwargs['session_text'] = session_text - self.writer = SummaryWriter(**self.init_kwargs) - - def get_step(self, runner): - """Get the total training step/epoch.""" - if self.get_mode(runner) == 'val' and self.by_epoch: - return self.get_epoch(runner) - else: - return self.get_iter(runner) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, add_mode=False) - if tags: - self.writer.add_scalars( - self.get_mode(runner), tags, self.get_step(runner)) - - @master_only - def after_run(self, runner): - if self.add_last_ckpt: - ckpt_path = osp.join(runner.work_dir, 'latest.pth') - if osp.islink(ckpt_path): - ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) - - if osp.isfile(ckpt_path): - # runner.epoch += 1 has been done before `after_run`. - iteration = runner.epoch if self.by_epoch else runner.iter - return self.writer.add_snapshot_file( - tag=self.run_name, - snapshot_file_path=ckpt_path, - iteration=iteration) - - # flush the buffer and send a task ending signal to Pavi - self.writer.close() - - @master_only - def before_epoch(self, runner): - if runner.epoch == 0 and self.add_graph: - if is_module_wrapper(runner.model): - _model = runner.model.module - else: - _model = runner.model - device = next(_model.parameters()).device - data = next(iter(runner.data_loader)) - image = data[self.img_key][0:1].to(device) - with torch.no_grad(): - self.writer.add_graph(_model, image) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/tensorboard.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/tensorboard.py deleted file mode 100644 index a8d50366f..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/tensorboard.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import TORCH_VERSION, digit_version -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TensorboardLoggerHook(LoggerHook): - - def __init__(self, - log_dir=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(TensorboardLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.log_dir = log_dir - - @master_only - def before_run(self, runner): - super(TensorboardLoggerHook, self).before_run(runner) - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.1')): - try: - from tensorboardX import SummaryWriter - except ImportError: - raise ImportError('Please install tensorboardX to use ' - 'TensorboardLoggerHook.') - else: - try: - from torch.utils.tensorboard import SummaryWriter - except ImportError: - raise ImportError( - 'Please run "pip install future tensorboard" to install ' - 'the dependencies to use torch.utils.tensorboard ' - '(applicable to PyTorch 1.1 or higher)') - - if self.log_dir is None: - self.log_dir = osp.join(runner.work_dir, 'tf_logs') - self.writer = SummaryWriter(self.log_dir) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, allow_text=True) - for tag, val in tags.items(): - if isinstance(val, str): - self.writer.add_text(tag, val, self.get_iter(runner)) - else: - self.writer.add_scalar(tag, val, self.get_iter(runner)) - - @master_only - def after_run(self, runner): - self.writer.close() diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/text.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/text.py deleted file mode 100644 index 043c7bf20..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/text.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import datetime -import os -import os.path as osp -from collections import OrderedDict - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.fileio.file_client import FileClient -from mmcv.utils import is_tuple_of, scandir -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TextLoggerHook(LoggerHook): - """Logger hook in text. - - In this logger hook, the information will be printed on terminal and - saved in json file. - - Args: - by_epoch (bool, optional): Whether EpochBasedRunner is used. - Default: True. - interval (int, optional): Logging interval (every k iterations). - Default: 10. - ignore_last (bool, optional): Ignore the log of last iterations in each - epoch if less than :attr:`interval`. Default: True. - reset_flag (bool, optional): Whether to clear the output buffer after - logging. Default: False. - interval_exp_name (int, optional): Logging interval for experiment - name. This feature is to help users conveniently get the experiment - information from screen or log file. Default: 1000. - out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. - If ``out_dir`` is specified, logs will be copied to a new directory - which is the concatenation of ``out_dir`` and the last level - directory of ``runner.work_dir``. Default: None. - `New in version 1.3.16.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be copied to ``out_dir``. - Default: ('.log.json', '.log', '.py'). - `New in version 1.3.16.` - keep_local (bool, optional): Whether to keep local log when - :attr:`out_dir` is specified. If False, the local log will be - removed. Default: True. - `New in version 1.3.16.` - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - - def __init__(self, - by_epoch=True, - interval=10, - ignore_last=True, - reset_flag=False, - interval_exp_name=1000, - out_dir=None, - out_suffix=('.log.json', '.log', '.py'), - keep_local=True, - file_client_args=None): - super(TextLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.by_epoch = by_epoch - self.time_sec_tot = 0 - self.interval_exp_name = interval_exp_name - - if out_dir is None and file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" when `out_dir` is not' - 'specified.') - self.out_dir = out_dir - - if not (out_dir is None or isinstance(out_dir, str) - or is_tuple_of(out_dir, str)): - raise TypeError('out_dir should be "None" or string or tuple of ' - 'string, but got {out_dir}') - self.out_suffix = out_suffix - - self.keep_local = keep_local - self.file_client_args = file_client_args - if self.out_dir is not None: - self.file_client = FileClient.infer_client(file_client_args, - self.out_dir) - - def before_run(self, runner): - super(TextLoggerHook, self).before_run(runner) - - if self.out_dir is not None: - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - # The final `self.out_dir` is the concatenation of `self.out_dir` - # and the last level directory of `runner.work_dir` - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'Text logs will be saved to {self.out_dir} by ' - f'{self.file_client.name} after the training process.')) - - self.start_iter = runner.iter - self.json_log_path = osp.join(runner.work_dir, - f'{runner.timestamp}.log.json') - if runner.meta is not None: - self._dump_log(runner.meta, runner) - - def _get_max_memory(self, runner): - device = getattr(runner.model, 'output_device', None) - mem = torch.cuda.max_memory_allocated(device=device) - mem_mb = torch.tensor([mem / (1024 * 1024)], - dtype=torch.int, - device=device) - if runner.world_size > 1: - dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) - return mem_mb.item() - - def _log_info(self, log_dict, runner): - # print exp name for users to distinguish experiments - # at every ``interval_exp_name`` iterations and the end of each epoch - if runner.meta is not None and 'exp_name' in runner.meta: - if (self.every_n_iters(runner, self.interval_exp_name)) or ( - self.by_epoch and self.end_of_epoch(runner)): - exp_info = f'Exp name: {runner.meta["exp_name"]}' - runner.logger.info(exp_info) - - if log_dict['mode'] == 'train': - if isinstance(log_dict['lr'], dict): - lr_str = [] - for k, val in log_dict['lr'].items(): - lr_str.append(f'lr_{k}: {val:.3e}') - lr_str = ' '.join(lr_str) - else: - lr_str = f'lr: {log_dict["lr"]:.3e}' - - # by epoch: Epoch [4][100/1000] - # by iter: Iter [100/100000] - if self.by_epoch: - log_str = f'Epoch [{log_dict["epoch"]}]' \ - f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' - else: - log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' - log_str += f'{lr_str}, ' - - if 'time' in log_dict.keys(): - self.time_sec_tot += (log_dict['time'] * self.interval) - time_sec_avg = self.time_sec_tot / ( - runner.iter - self.start_iter + 1) - eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) - eta_str = str(datetime.timedelta(seconds=int(eta_sec))) - log_str += f'eta: {eta_str}, ' - log_str += f'time: {log_dict["time"]:.3f}, ' \ - f'data_time: {log_dict["data_time"]:.3f}, ' - # statistic memory - if torch.cuda.is_available(): - log_str += f'memory: {log_dict["memory"]}, ' - else: - # val/test time - # here 1000 is the length of the val dataloader - # by epoch: Epoch[val] [4][1000] - # by iter: Iter[val] [1000] - if self.by_epoch: - log_str = f'Epoch({log_dict["mode"]}) ' \ - f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' - else: - log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' - - log_items = [] - for name, val in log_dict.items(): - # TODO: resolve this hack - # these items have been in log_str - if name in [ - 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', - 'memory', 'epoch' - ]: - continue - if isinstance(val, float): - val = f'{val:.4f}' - log_items.append(f'{name}: {val}') - log_str += ', '.join(log_items) - - runner.logger.info(log_str) - - def _dump_log(self, log_dict, runner): - # dump log in json format - json_log = OrderedDict() - for k, v in log_dict.items(): - json_log[k] = self._round_float(v) - # only append log at last line - if runner.rank == 0: - with open(self.json_log_path, 'a+') as f: - mmcv.dump(json_log, f, file_format='json') - f.write('\n') - - def _round_float(self, items): - if isinstance(items, list): - return [self._round_float(item) for item in items] - elif isinstance(items, float): - return round(items, 5) - else: - return items - - def log(self, runner): - if 'eval_iter_num' in runner.log_buffer.output: - # this doesn't modify runner.iter and is regardless of by_epoch - cur_iter = runner.log_buffer.output.pop('eval_iter_num') - else: - cur_iter = self.get_iter(runner, inner_iter=True) - - log_dict = OrderedDict( - mode=self.get_mode(runner), - epoch=self.get_epoch(runner), - iter=cur_iter) - - # only record lr of the first param group - cur_lr = runner.current_lr() - if isinstance(cur_lr, list): - log_dict['lr'] = cur_lr[0] - else: - assert isinstance(cur_lr, dict) - log_dict['lr'] = {} - for k, lr_ in cur_lr.items(): - assert isinstance(lr_, list) - log_dict['lr'].update({k: lr_[0]}) - - if 'time' in runner.log_buffer.output: - # statistic memory - if torch.cuda.is_available(): - log_dict['memory'] = self._get_max_memory(runner) - - log_dict = dict(log_dict, **runner.log_buffer.output) - - self._log_info(log_dict, runner) - self._dump_log(log_dict, runner) - return log_dict - - def after_run(self, runner): - # copy or upload logs to self.out_dir - if self.out_dir is not None: - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - out_filepath = self.file_client.join_path( - self.out_dir, filename) - with open(local_filepath, 'r') as f: - self.file_client.put_text(f.read(), out_filepath) - - runner.logger.info( - (f'The file {local_filepath} has been uploaded to ' - f'{out_filepath}.')) - - if not self.keep_local: - os.remove(local_filepath) - runner.logger.info( - (f'{local_filepath} was removed due to the ' - '`self.keep_local=False`')) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/wandb.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/wandb.py deleted file mode 100644 index 9f6808462..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/logger/wandb.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class WandbLoggerHook(LoggerHook): - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - commit=True, - by_epoch=True, - with_step=True): - super(WandbLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_wandb() - self.init_kwargs = init_kwargs - self.commit = commit - self.with_step = with_step - - def import_wandb(self): - try: - import wandb - except ImportError: - raise ImportError( - 'Please run "pip install wandb" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(WandbLoggerHook, self).before_run(runner) - if self.wandb is None: - self.import_wandb() - if self.init_kwargs: - self.wandb.init(**self.init_kwargs) - else: - self.wandb.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - if self.with_step: - self.wandb.log( - tags, step=self.get_iter(runner), commit=self.commit) - else: - tags['global_step'] = self.get_iter(runner) - self.wandb.log(tags, commit=self.commit) - - @master_only - def after_run(self, runner): - self.wandb.join() diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/lr_updater.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/lr_updater.py deleted file mode 100644 index e5a124157..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/lr_updater.py +++ /dev/null @@ -1,670 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from math import cos, pi - -import mmcv -from .hook import HOOKS, Hook - - -class LrUpdaterHook(Hook): - """LR Scheduler in MMCV. - - Args: - by_epoch (bool): LR changes epoch by epoch - warmup (string): Type of warmup used. It can be None(use no warmup), - 'constant', 'linear' or 'exp' - warmup_iters (int): The number of iterations or epochs that warmup - lasts - warmup_ratio (float): LR used at the beginning of warmup equals to - warmup_ratio * initial_lr - warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters - means the number of epochs that warmup lasts, otherwise means the - number of iteration that warmup lasts - """ - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.1, - warmup_by_epoch=False): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_ratio" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - self.warmup_by_epoch = warmup_by_epoch - - if self.warmup_by_epoch: - self.warmup_epochs = self.warmup_iters - self.warmup_iters = None - else: - self.warmup_epochs = None - - self.base_lr = [] # initial lr for all param groups - self.regular_lr = [] # expected lr if no warming up is performed - - def _set_lr(self, runner, lr_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, lr in zip(optim.param_groups, lr_groups[k]): - param_group['lr'] = lr - else: - for param_group, lr in zip(runner.optimizer.param_groups, - lr_groups): - param_group['lr'] = lr - - def get_lr(self, runner, base_lr): - raise NotImplementedError - - def get_regular_lr(self, runner): - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(runner, _base_lr) - for _base_lr in self.base_lr[k] - ] - lr_groups.update({k: _lr_group}) - - return lr_groups - else: - return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - if self.warmup == 'constant': - warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_lr = [_lr * (1 - k) for _lr in regular_lr] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.regular_lr, dict): - lr_groups = {} - for key, regular_lr in self.regular_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.regular_lr) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - group.setdefault('initial_lr', group['lr']) - _base_lr = [ - group['initial_lr'] for group in optim.param_groups - ] - self.base_lr.update({k: _base_lr}) - else: - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['initial_lr'] for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if self.warmup_iters is None: - epoch_len = len(runner.data_loader) - self.warmup_iters = self.warmup_epochs * epoch_len - - if not self.by_epoch: - return - - self.regular_lr = self.get_regular_lr(runner) - self._set_lr(runner, self.regular_lr) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_lr = self.get_regular_lr(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - - -@HOOKS.register_module() -class FixedLrUpdaterHook(LrUpdaterHook): - - def __init__(self, **kwargs): - super(FixedLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - return base_lr - - -@HOOKS.register_module() -class StepLrUpdaterHook(LrUpdaterHook): - """Step LR scheduler with min_lr clipping. - - Args: - step (int | list[int]): Step to decay the LR. If an int value is given, - regard it as the decay interval. If a list is given, decay LR at - these steps. - gamma (float, optional): Decay LR ratio. Default: 0.1. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. If None - is given, we don't perform lr clipping. Default: None. - """ - - def __init__(self, step, gamma=0.1, min_lr=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_lr = min_lr - super(StepLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - lr = base_lr * (self.gamma**exp) - if self.min_lr is not None: - # clip to a minimum value - lr = max(lr, self.min_lr) - return lr - - -@HOOKS.register_module() -class ExpLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, **kwargs): - self.gamma = gamma - super(ExpLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * self.gamma**progress - - -@HOOKS.register_module() -class PolyLrUpdaterHook(LrUpdaterHook): - - def __init__(self, power=1., min_lr=0., **kwargs): - self.power = power - self.min_lr = min_lr - super(PolyLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - coeff = (1 - progress / max_progress)**self.power - return (base_lr - self.min_lr) * coeff + self.min_lr - - -@HOOKS.register_module() -class InvLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, power=1., **kwargs): - self.gamma = gamma - self.power = power - super(InvLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * (1 + self.gamma * progress)**(-self.power) - - -@HOOKS.register_module() -class CosineAnnealingLrUpdaterHook(LrUpdaterHook): - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(CosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): - """Flat + Cosine lr schedule. - - Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 - - Args: - start_percent (float): When to start annealing the learning rate - after the percentage of the total training steps. - The value should be in range [0, 1). - Default: 0.75 - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - start_percent=0.75, - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - if start_percent < 0 or start_percent > 1 or not isinstance( - start_percent, float): - raise ValueError( - 'expected float between 0 and 1 start_percent, but ' - f'got {start_percent}') - self.start_percent = start_percent - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(FlatCosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - start = round(runner.max_epochs * self.start_percent) - progress = runner.epoch - start - max_progress = runner.max_epochs - start - else: - start = round(runner.max_iters * self.start_percent) - progress = runner.iter - start - max_progress = runner.max_iters - start - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress < 0: - return base_lr - else: - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class CosineRestartLrUpdaterHook(LrUpdaterHook): - """Cosine annealing with restarts learning rate scheme. - - Args: - periods (list[int]): Periods for each cosine anneling cycle. - restart_weights (list[float], optional): Restart weights at each - restart iteration. Default: [1]. - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - periods, - restart_weights=[1], - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.periods = periods - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - self.restart_weights = restart_weights - assert (len(self.periods) == len(self.restart_weights) - ), 'periods and restart_weights should have the same length.' - super(CosineRestartLrUpdaterHook, self).__init__(**kwargs) - - self.cumulative_periods = [ - sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) - ] - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - else: - progress = runner.iter - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - idx = get_position_from_periods(progress, self.cumulative_periods) - current_weight = self.restart_weights[idx] - nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] - current_periods = self.periods[idx] - - alpha = min((progress - nearest_restart) / current_periods, 1) - return annealing_cos(base_lr, target_lr, alpha, current_weight) - - -def get_position_from_periods(iteration, cumulative_periods): - """Get the position from a period list. - - It will return the index of the right-closest number in the period list. - For example, the cumulative_periods = [100, 200, 300, 400], - if iteration == 50, return 0; - if iteration == 210, return 2; - if iteration == 300, return 3. - - Args: - iteration (int): Current iteration. - cumulative_periods (list[int]): Cumulative period list. - - Returns: - int: The position of the right-closest number in the period list. - """ - for i, period in enumerate(cumulative_periods): - if iteration < period: - return i - raise ValueError(f'Current iteration {iteration} exceeds ' - f'cumulative_periods {cumulative_periods}') - - -@HOOKS.register_module() -class CyclicLrUpdaterHook(LrUpdaterHook): - """Cyclic LR Scheduler. - - Implement the cyclical learning rate policy (CLR) described in - https://arxiv.org/pdf/1506.01186.pdf - - Different from the original paper, we use cosine annealing rather than - triangular policy inside a cycle. This improves the performance in the - 3D detection area. - - Args: - by_epoch (bool): Whether to update LR by epoch. - target_ratio (tuple[float]): Relative ratio of the highest LR and the - lowest LR to the initial LR. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of LR in - the total cycle. - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - """ - - def __init__(self, - by_epoch=False, - target_ratio=(10, 1e-4), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.lr_phases = [] # init lr_phases - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicLrUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicLrUpdaterHook, self).before_run(runner) - # initiate lr_phases - # total lr_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.lr_phases.append( - [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) - self.lr_phases.append([ - iter_up_phase, max_iter_per_phase, max_iter_per_phase, - self.target_ratio[0], self.target_ratio[1] - ]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - for (start_iter, end_iter, max_iter_per_phase, start_ratio, - end_ratio) in self.lr_phases: - curr_iter %= max_iter_per_phase - if start_iter <= curr_iter < end_iter: - progress = curr_iter - start_iter - return self.anneal_func(base_lr * start_ratio, - base_lr * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleLrUpdaterHook(LrUpdaterHook): - """One Cycle LR Scheduler. - - The 1cycle learning rate policy changes the learning rate after every - batch. The one cycle learning rate policy is described in - https://arxiv.org/pdf/1708.07120.pdf - - Args: - max_lr (float or list): Upper learning rate boundaries in the cycle - for each parameter group. - total_steps (int, optional): The total number of steps in the cycle. - Note that if a value is not provided here, it will be the max_iter - of runner. Default: None. - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - div_factor (float): Determines the initial learning rate via - initial_lr = max_lr/div_factor - Default: 25 - final_div_factor (float): Determines the minimum learning rate via - min_lr = initial_lr/final_div_factor - Default: 1e4 - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - max_lr, - total_steps=None, - pct_start=0.3, - anneal_strategy='cos', - div_factor=25, - final_div_factor=1e4, - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch = False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(max_lr, (numbers.Number, list, dict)): - raise ValueError('the type of max_lr must be the one of list or ' - f'dict, but got {type(max_lr)}') - self._max_lr = max_lr - if total_steps is not None: - if not isinstance(total_steps, int): - raise ValueError('the type of total_steps must be int, but' - f'got {type(total_steps)}') - self.total_steps = total_steps - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.div_factor = div_factor - self.final_div_factor = final_div_factor - self.three_phase = three_phase - self.lr_phases = [] # init lr_phases - super(OneCycleLrUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if hasattr(self, 'total_steps'): - total_steps = self.total_steps - else: - total_steps = runner.max_iters - if total_steps < runner.max_iters: - raise ValueError( - 'The total steps must be greater than or equal to max ' - f'iterations {runner.max_iters} of runner, but total steps ' - f'is {total_steps}.') - - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - _max_lr = format_param(k, optim, self._max_lr) - self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(optim.param_groups, self.base_lr[k]): - group.setdefault('initial_lr', lr) - else: - k = type(runner.optimizer).__name__ - _max_lr = format_param(k, runner.optimizer, self._max_lr) - self.base_lr = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(runner.optimizer.param_groups, self.base_lr): - group.setdefault('initial_lr', lr) - - if self.three_phase: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append([ - float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 - ]) - self.lr_phases.append( - [total_steps - 1, 1, 1 / self.final_div_factor]) - else: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append( - [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - start_iter = 0 - for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): - if curr_iter <= end_iter: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, - pct) - break - start_iter = end_iter - return lr - - -def annealing_cos(start, end, factor, weight=1): - """Calculate annealing cos learning rate. - - Cosine anneal from `weight * start + (1 - weight) * end` to `end` as - percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the cosine annealing. - end (float): The ending learing rate of the cosine annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - weight (float, optional): The combination factor of `start` and `end` - when calculating the actual starting learning rate. Default to 1. - """ - cos_out = cos(pi * factor) + 1 - return end + 0.5 * weight * (start - end) * cos_out - - -def annealing_linear(start, end, factor): - """Calculate annealing linear learning rate. - - Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the linear annealing. - end (float): The ending learing rate of the linear annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - """ - return start + (end - start) * factor - - -def format_param(name, optim, param): - if isinstance(param, numbers.Number): - return [param] * len(optim.param_groups) - elif isinstance(param, (list, tuple)): # multi param groups - if len(param) != len(optim.param_groups): - raise ValueError(f'expected {len(optim.param_groups)} ' - f'values for {name}, got {len(param)}') - return param - else: # multi optimizers - if name not in param: - raise KeyError(f'{name} is not found in {param.keys()}') - return param[name] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/memory.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/memory.py deleted file mode 100644 index 70cf9a838..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class EmptyCacheHook(Hook): - - def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): - self._before_epoch = before_epoch - self._after_epoch = after_epoch - self._after_iter = after_iter - - def after_iter(self, runner): - if self._after_iter: - torch.cuda.empty_cache() - - def before_epoch(self, runner): - if self._before_epoch: - torch.cuda.empty_cache() - - def after_epoch(self, runner): - if self._after_epoch: - torch.cuda.empty_cache() diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/momentum_updater.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/momentum_updater.py deleted file mode 100644 index 13d0e2fab..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/momentum_updater.py +++ /dev/null @@ -1,493 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -from .hook import HOOKS, Hook -from .lr_updater import annealing_cos, annealing_linear, format_param - - -class MomentumUpdaterHook(Hook): - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.9): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_momentum" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - - self.base_momentum = [] # initial momentum for all param groups - self.regular_momentum = [ - ] # expected momentum if no warming up is performed - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, base_momentum): - raise NotImplementedError - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k in runner.optimizer.keys(): - _momentum_group = [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum[k] - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - return [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum - ] - - def get_warmup_momentum(self, cur_iters): - - def _get_warmup_momentum(cur_iters, regular_momentum): - if self.warmup == 'constant': - warmup_momentum = [ - _momentum / self.warmup_ratio - for _momentum in self.regular_momentum - ] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_momentum = [ - _momentum / (1 - k) for _momentum in self.regular_mom - ] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_momentum = [ - _momentum / k for _momentum in self.regular_mom - ] - return warmup_momentum - - if isinstance(self.regular_momentum, dict): - momentum_groups = {} - for key, regular_momentum in self.regular_momentum.items(): - momentum_groups[key] = _get_warmup_momentum( - cur_iters, regular_momentum) - return momentum_groups - else: - return _get_warmup_momentum(cur_iters, self.regular_momentum) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, - # if 'initial_momentum' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_momentum = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - _base_momentum = [ - group['initial_momentum'] for group in optim.param_groups - ] - self.base_momentum.update({k: _base_momentum}) - else: - for group in runner.optimizer.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - self.base_momentum = [ - group['initial_momentum'] - for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if not self.by_epoch: - return - self.regular_mom = self.get_regular_momentum(runner) - self._set_momentum(runner, self.regular_mom) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_mom = self.get_regular_momentum(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_momentum(runner, self.regular_mom) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_momentum(runner, self.regular_mom) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - - -@HOOKS.register_module() -class StepMomentumUpdaterHook(MomentumUpdaterHook): - """Step momentum scheduler with min value clipping. - - Args: - step (int | list[int]): Step to decay the momentum. If an int value is - given, regard it as the decay interval. If a list is given, decay - momentum at these steps. - gamma (float, optional): Decay momentum ratio. Default: 0.5. - min_momentum (float, optional): Minimum momentum value to keep. If - momentum after decay is lower than this value, it will be clipped - accordingly. If None is given, we don't perform lr clipping. - Default: None. - """ - - def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_momentum = min_momentum - super(StepMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - momentum = base_momentum * (self.gamma**exp) - if self.min_momentum is not None: - # clip to a minimum value - momentum = max(momentum, self.min_momentum) - return momentum - - -@HOOKS.register_module() -class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(CosineAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_cos(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class CyclicMomentumUpdaterHook(MomentumUpdaterHook): - """Cyclic momentum Scheduler. - - Implement the cyclical momentum scheduler policy described in - https://arxiv.org/pdf/1708.07120.pdf - - This momentum scheduler usually used together with the CyclicLRUpdater - to improve the performance in the 3D detection area. - - Attributes: - target_ratio (tuple[float]): Relative ratio of the lowest momentum and - the highest momentum to the initial momentum. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of momentum - in the total cycle. - by_epoch (bool): Whether to update momentum by epoch. - """ - - def __init__(self, - by_epoch=False, - target_ratio=(0.85 / 0.95, 1), - cyclic_times=1, - step_ratio_up=0.4, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.momentum_phases = [] # init momentum_phases - # currently only support by_epoch=False - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicMomentumUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicMomentumUpdaterHook, self).before_run(runner) - # initiate momentum_phases - # total momentum_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.momentum_phases.append( - [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) - self.momentum_phases.append([ - iter_up_phase, max_iter_per_phase, max_iter_per_phase, - self.target_ratio[0], self.target_ratio[1] - ]) - - def get_momentum(self, runner, base_momentum): - curr_iter = runner.iter - for (start_iter, end_iter, max_iter_per_phase, start_ratio, - end_ratio) in self.momentum_phases: - curr_iter %= max_iter_per_phase - if start_iter <= curr_iter < end_iter: - progress = curr_iter - start_iter - return annealing_cos(base_momentum * start_ratio, - base_momentum * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): - """OneCycle momentum Scheduler. - - This momentum scheduler usually used together with the OneCycleLrUpdater - to improve the performance. - - Args: - base_momentum (float or list): Lower momentum boundaries in the cycle - for each parameter group. Note that momentum is cycled inversely - to learning rate; at the peak of a cycle, momentum is - 'base_momentum' and learning rate is 'max_lr'. - Default: 0.85 - max_momentum (float or list): Upper momentum boundaries in the cycle - for each parameter group. Functionally, - it defines the cycle amplitude (max_momentum - base_momentum). - Note that momentum is cycled inversely - to learning rate; at the start of a cycle, momentum is - 'max_momentum' and learning rate is 'base_lr' - Default: 0.95 - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - base_momentum=0.85, - max_momentum=0.95, - pct_start=0.3, - anneal_strategy='cos', - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch=False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(base_momentum, (float, list, dict)): - raise ValueError('base_momentum must be the type among of float,' - 'list or dict.') - self._base_momentum = base_momentum - if not isinstance(max_momentum, (float, list, dict)): - raise ValueError('max_momentum must be the type among of float,' - 'list or dict.') - self._max_momentum = max_momentum - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('Expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must by one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.three_phase = three_phase - self.momentum_phases = [] # init momentum_phases - super(OneCycleMomentumUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip( - optim.param_groups, _base_momentum, _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - else: - optim = runner.optimizer - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - k = type(optim).__name__ - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip(optim.param_groups, - _base_momentum, - _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - - if self.three_phase: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': - float(2 * self.pct_start * runner.max_iters) - 2, - 'start_momentum': - 'base_momentum', - 'end_momentum': - 'max_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'max_momentum', - 'end_momentum': 'max_momentum' - }) - else: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'base_momentum', - 'end_momentum': 'max_momentum' - }) - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, param_group): - curr_iter = runner.iter - start_iter = 0 - for i, phase in enumerate(self.momentum_phases): - end_iter = phase['end_iter'] - if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - momentum = self.anneal_func( - param_group[phase['start_momentum']], - param_group[phase['end_momentum']], pct) - break - start_iter = end_iter - return momentum - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k, optim in runner.optimizer.items(): - _momentum_group = [ - self.get_momentum(runner, param_group) - for param_group in optim.param_groups - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - momentum_groups = [] - for param_group in runner.optimizer.param_groups: - momentum_groups.append(self.get_momentum(runner, param_group)) - return momentum_groups diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/optimizer.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/optimizer.py deleted file mode 100644 index f575ceda0..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/optimizer.py +++ /dev/null @@ -1,508 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -from collections import defaultdict -from itertools import chain - -from torch.nn.utils import clip_grad - -from mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version -from ..dist_utils import allreduce_grads -from ..fp16_utils import LossScaler, wrap_fp16_model -from .hook import HOOKS, Hook - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - from torch.cuda.amp import GradScaler -except ImportError: - pass - - -@HOOKS.register_module() -class OptimizerHook(Hook): - - def __init__(self, grad_clip=None): - self.grad_clip = grad_clip - - def clip_grads(self, params): - params = list( - filter(lambda p: p.requires_grad and p.grad is not None, params)) - if len(params) > 0: - return clip_grad.clip_grad_norm_(params, **self.grad_clip) - - def after_train_iter(self, runner): - runner.optimizer.zero_grad() - runner.outputs['loss'].backward() - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - - -@HOOKS.register_module() -class GradientCumulativeOptimizerHook(OptimizerHook): - """Optimizer Hook implements multi-iters gradient cumulating. - - Args: - cumulative_iters (int, optional): Num of gradient cumulative iters. - The optimizer will step every `cumulative_iters` iters. - Defaults to 1. - - Examples: - >>> # Use cumulative_iters to simulate a large batch size - >>> # It is helpful when the hardware cannot handle a large batch size. - >>> loader = DataLoader(data, batch_size=64) - >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) - >>> # almost equals to - >>> loader = DataLoader(data, batch_size=256) - >>> optim_hook = OptimizerHook() - """ - - def __init__(self, cumulative_iters=1, **kwargs): - super(GradientCumulativeOptimizerHook, self).__init__(**kwargs) - - assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ - f'cumulative_iters only accepts positive int, but got ' \ - f'{type(cumulative_iters)} instead.' - - self.cumulative_iters = cumulative_iters - self.divisible_iters = 0 - self.remainder_iters = 0 - self.initialized = False - - def has_batch_norm(self, module): - if isinstance(module, _BatchNorm): - return True - for m in module.children(): - if self.has_batch_norm(m): - return True - return False - - def _init(self, runner): - if runner.iter % self.cumulative_iters != 0: - runner.logger.warning( - 'Resume iter number is not divisible by cumulative_iters in ' - 'GradientCumulativeOptimizerHook, which means the gradient of ' - 'some iters is lost and the result may be influenced slightly.' - ) - - if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: - runner.logger.warning( - 'GradientCumulativeOptimizerHook may slightly decrease ' - 'performance if the model has BatchNorm layers.') - - residual_iters = runner.max_iters - runner.iter - - self.divisible_iters = ( - residual_iters // self.cumulative_iters * self.cumulative_iters) - self.remainder_iters = residual_iters - self.divisible_iters - - self.initialized = True - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - runner.optimizer.zero_grad() - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - self.loss_scaler = GradScaler() - elif isinstance(loss_scale, float): - self._scale_update_param = loss_scale - self.loss_scaler = GradScaler(init_scale=loss_scale) - elif isinstance(loss_scale, dict): - self.loss_scaler = GradScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training.""" - # wrap model mode to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer to - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients. - 3. Unscale the optimizer’s gradient tensors. - 4. Call optimizer.step() and update scale factor. - 5. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - - self.loss_scaler.scale(runner.outputs['loss']).backward() - self.loss_scaler.unscale_(runner.optimizer) - # grad clip - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using PyTorch's implementation) implements - multi-iters gradient cumulating. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - """ - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - - self.loss_scaler.scale(loss).backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - self.loss_scaler.unscale_(runner.optimizer) - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() - -else: - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (mmcv's implementation). - - The steps of fp16 optimizer is as follows. - 1. Scale the loss value. - 2. BP in the fp16 model. - 2. Copy gradients from fp16 model to fp32 weights. - 3. Update fp32 weights. - 4. Copy updated parameters from fp32 weights to fp16 model. - - Refer to https://arxiv.org/abs/1710.03740 for more details. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of LossScaler. - Defaults to 512. - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - if loss_scale == 'dynamic': - self.loss_scaler = LossScaler(mode='dynamic') - elif isinstance(loss_scale, float): - self.loss_scaler = LossScaler( - init_scale=loss_scale, mode='static') - elif isinstance(loss_scale, dict): - self.loss_scaler = LossScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training. - - 1. Make a master copy of fp32 weights for optimization. - 2. Convert the main model from fp32 to fp16. - """ - # keep a copy of fp32 weights - old_groups = runner.optimizer.param_groups - runner.optimizer.param_groups = copy.deepcopy( - runner.optimizer.param_groups) - state = defaultdict(dict) - p_map = { - old_p: p - for old_p, p in zip( - chain(*(g['params'] for g in old_groups)), - chain(*(g['params'] - for g in runner.optimizer.param_groups))) - } - for k, v in runner.optimizer.state.items(): - state[p_map[k]] = v - runner.optimizer.state = state - # convert model to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer `loss_scalar.py` - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients (fp16). - 3. Copy gradients from the model to the fp32 weight copy. - 4. Scale the gradients back and update the fp32 weight copy. - 5. Copy back the params from fp32 weight copy to the fp16 model. - 6. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - # scale the loss value - scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale - scaled_loss.backward() - # copy fp16 grads in the model to fp32 params in the optimizer - - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - self.loss_scaler.update_scale(has_overflow) - if has_overflow: - runner.logger.warning('Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using mmcv implementation) implements multi- - iters gradient cumulating.""" - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - - loss = runner.outputs['loss'] - loss = loss / loss_factor - - # scale the loss value - scaled_loss = loss * self.loss_scaler.loss_scale - scaled_loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - else: - runner.logger.warning( - 'Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - self.loss_scaler.update_scale(has_overflow) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/profiler.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/profiler.py deleted file mode 100644 index b70236997..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/profiler.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Callable, List, Optional, Union - -import torch - -from ..dist_utils import master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ProfilerHook(Hook): - """Profiler to analyze performance during training. - - PyTorch Profiler is a tool that allows the collection of the performance - metrics during the training. More details on Profiler can be found at - https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile - - Args: - by_epoch (bool): Profile performance by epoch or by iteration. - Default: True. - profile_iters (int): Number of iterations for profiling. - If ``by_epoch=True``, profile_iters indicates that they are the - first profile_iters epochs at the beginning of the - training, otherwise it indicates the first profile_iters - iterations. Default: 1. - activities (list[str]): List of activity groups (CPU, CUDA) to use in - profiling. Default: ['cpu', 'cuda']. - schedule (dict, optional): Config of generating the callable schedule. - if schedule is None, profiler will not add step markers into the - trace and table view. Default: None. - on_trace_ready (callable, dict): Either a handler or a dict of generate - handler. Default: None. - record_shapes (bool): Save information about operator's input shapes. - Default: False. - profile_memory (bool): Track tensor memory allocation/deallocation. - Default: False. - with_stack (bool): Record source information (file and line number) - for the ops. Default: False. - with_flops (bool): Use formula to estimate the FLOPS of specific - operators (matrix multiplication and 2D convolution). - Default: False. - json_trace_path (str, optional): Exports the collected trace in Chrome - JSON format. Default: None. - - Example: - >>> runner = ... # instantiate a Runner - >>> # tensorboard trace - >>> trace_config = dict(type='tb_trace', dir_name='work_dir') - >>> profiler_config = dict(on_trace_ready=trace_config) - >>> runner.register_profiler_hook(profiler_config) - >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) - """ - - def __init__(self, - by_epoch: bool = True, - profile_iters: int = 1, - activities: List[str] = ['cpu', 'cuda'], - schedule: Optional[dict] = None, - on_trace_ready: Optional[Union[Callable, dict]] = None, - record_shapes: bool = False, - profile_memory: bool = False, - with_stack: bool = False, - with_flops: bool = False, - json_trace_path: Optional[str] = None) -> None: - try: - from torch import profiler # torch version >= 1.8.1 - except ImportError: - raise ImportError('profiler is the new feature of torch1.8.1, ' - f'but your version is {torch.__version__}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' - self.by_epoch = by_epoch - - if profile_iters < 1: - raise ValueError('profile_iters should be greater than 0, but got ' - f'{profile_iters}') - self.profile_iters = profile_iters - - if not isinstance(activities, list): - raise ValueError( - f'activities should be list, but got {type(activities)}') - self.activities = [] - for activity in activities: - activity = activity.lower() - if activity == 'cpu': - self.activities.append(profiler.ProfilerActivity.CPU) - elif activity == 'cuda': - self.activities.append(profiler.ProfilerActivity.CUDA) - else: - raise ValueError( - f'activity should be "cpu" or "cuda", but got {activity}') - - if schedule is not None: - self.schedule = profiler.schedule(**schedule) - else: - self.schedule = None - - self.on_trace_ready = on_trace_ready - self.record_shapes = record_shapes - self.profile_memory = profile_memory - self.with_stack = with_stack - self.with_flops = with_flops - self.json_trace_path = json_trace_path - - @master_only - def before_run(self, runner): - if self.by_epoch and runner.max_epochs < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_epochs}') - - if not self.by_epoch and runner.max_iters < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_iters}') - - if callable(self.on_trace_ready): # handler - _on_trace_ready = self.on_trace_ready - elif isinstance(self.on_trace_ready, dict): # config of handler - trace_cfg = self.on_trace_ready.copy() - trace_type = trace_cfg.pop('type') # log_trace handler - if trace_type == 'log_trace': - - def _log_handler(prof): - print(prof.key_averages().table(**trace_cfg)) - - _on_trace_ready = _log_handler - elif trace_type == 'tb_trace': # tensorboard_trace handler - try: - import torch_tb_profiler # noqa: F401 - except ImportError: - raise ImportError('please run "pip install ' - 'torch-tb-profiler" to install ' - 'torch_tb_profiler') - _on_trace_ready = torch.profiler.tensorboard_trace_handler( - **trace_cfg) - else: - raise ValueError('trace_type should be "log_trace" or ' - f'"tb_trace", but got {trace_type}') - elif self.on_trace_ready is None: - _on_trace_ready = None # type: ignore - else: - raise ValueError('on_trace_ready should be handler, dict or None, ' - f'but got {type(self.on_trace_ready)}') - - if runner.max_epochs > 1: - warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' - 'instead of 1 epoch. Since profiler will slow down ' - 'the training, it is recommended to train 1 epoch ' - 'with ProfilerHook and adjust your setting according' - ' to the profiler summary. During normal training ' - '(epoch > 1), you may disable the ProfilerHook.') - - self.profiler = torch.profiler.profile( - activities=self.activities, - schedule=self.schedule, - on_trace_ready=_on_trace_ready, - record_shapes=self.record_shapes, - profile_memory=self.profile_memory, - with_stack=self.with_stack, - with_flops=self.with_flops) - - self.profiler.__enter__() - runner.logger.info('profiler is profiling...') - - @master_only - def after_train_epoch(self, runner): - if self.by_epoch and runner.epoch == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) - - @master_only - def after_train_iter(self, runner): - self.profiler.step() - if not self.by_epoch and runner.iter == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/sampler_seed.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/sampler_seed.py deleted file mode 100644 index ee0dc6bdd..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/sampler_seed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class DistSamplerSeedHook(Hook): - """Data-loading sampler for distributed training. - - When distributed training, it is only useful in conjunction with - :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same - purpose with :obj:`IterLoader`. - """ - - def before_epoch(self, runner): - if hasattr(runner.data_loader.sampler, 'set_epoch'): - # in case the data loader uses `SequentialSampler` in Pytorch - runner.data_loader.sampler.set_epoch(runner.epoch) - elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): - # batch sampler in pytorch warps the sampler as its attributes. - runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/sync_buffer.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/sync_buffer.py deleted file mode 100644 index 6376b7ff8..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/hooks/sync_buffer.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..dist_utils import allreduce_params -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class SyncBuffersHook(Hook): - """Synchronize model buffers such as running_mean and running_var in BN at - the end of each epoch. - - Args: - distributed (bool): Whether distributed training is used. It is - effective only for distributed training. Defaults to True. - """ - - def __init__(self, distributed=True): - self.distributed = distributed - - def after_epoch(self, runner): - """All-reduce model buffers at the end of each epoch.""" - if self.distributed: - allreduce_params(runner.model.buffers()) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/iter_based_runner.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/iter_based_runner.py deleted file mode 100644 index 9892b07a4..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/iter_based_runner.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch -from torch.optim import Optimizer - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .hooks import IterTimerHook -from .utils import get_host_info - - -class IterLoader: - - def __init__(self, dataloader): - self._dataloader = dataloader - self.iter_loader = iter(self._dataloader) - self._epoch = 0 - - @property - def epoch(self): - return self._epoch - - def __next__(self): - try: - data = next(self.iter_loader) - except StopIteration: - self._epoch += 1 - if hasattr(self._dataloader.sampler, 'set_epoch'): - self._dataloader.sampler.set_epoch(self._epoch) - time.sleep(2) # Prevent possible deadlock during epoch transition - self.iter_loader = iter(self._dataloader) - data = next(self.iter_loader) - - return data - - def __len__(self): - return len(self._dataloader) - - -@RUNNERS.register_module() -class IterBasedRunner(BaseRunner): - """Iteration-based Runner. - - This runner train models iteration by iteration. - """ - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._epoch = data_loader.epoch - data_batch = next(data_loader) - self.call_hook('before_train_iter') - outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.train_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_train_iter') - self._inner_iter += 1 - self._iter += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - data_batch = next(data_loader) - self.call_hook('before_val_iter') - outputs = self.model.val_step(data_batch, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.val_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_val_iter') - self._inner_iter += 1 - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, iters) to specify the - running order and iterations. E.g, [('train', 10000), - ('val', 1000)] means running 10000 iterations for training and - 1000 iterations for validation, iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - """Resume model from checkpoint. - - Args: - checkpoint (str): Checkpoint to resume from. - resume_optimizer (bool, optional): Whether resume the optimizer(s) - if the checkpoint file includes optimizer(s). Default to True. - map_location (str, optional): Same as :func:`torch.load`. - Default to 'default'. - """ - if map_location == 'default': - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - self._inner_iter = checkpoint['meta']['iter'] - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') - - def save_checkpoint(self, - out_dir, - filename_tmpl='iter_{}.pth', - meta=None, - save_optimizer=True, - create_symlink=True): - """Save checkpoint to file. - - Args: - out_dir (str): Directory to save checkpoint files. - filename_tmpl (str, optional): Checkpoint file template. - Defaults to 'iter_{}.pth'. - meta (dict, optional): Metadata to be saved in checkpoint. - Defaults to None. - save_optimizer (bool, optional): Whether save optimizer. - Defaults to True. - create_symlink (bool, optional): Whether create symlink to the - latest checkpoint file. Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.iter + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - custom_hooks_config=None): - """Register default hooks for iter-based training. - - Checkpoint hook, optimizer stepper hook and logger hooks will be set to - `by_epoch=False` by default. - - Default hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - if checkpoint_config is not None: - checkpoint_config.setdefault('by_epoch', False) - if lr_config is not None: - lr_config.setdefault('by_epoch', False) - if log_config is not None: - for info in log_config['hooks']: - info.setdefault('by_epoch', False) - super(IterBasedRunner, self).register_training_hooks( - lr_config=lr_config, - momentum_config=momentum_config, - optimizer_config=optimizer_config, - checkpoint_config=checkpoint_config, - log_config=log_config, - timer_config=IterTimerHook(), - custom_hooks_config=custom_hooks_config) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/log_buffer.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/log_buffer.py deleted file mode 100644 index d949e2941..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/log_buffer.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import numpy as np - - -class LogBuffer: - - def __init__(self): - self.val_history = OrderedDict() - self.n_history = OrderedDict() - self.output = OrderedDict() - self.ready = False - - def clear(self): - self.val_history.clear() - self.n_history.clear() - self.clear_output() - - def clear_output(self): - self.output.clear() - self.ready = False - - def update(self, vars, count=1): - assert isinstance(vars, dict) - for key, var in vars.items(): - if key not in self.val_history: - self.val_history[key] = [] - self.n_history[key] = [] - self.val_history[key].append(var) - self.n_history[key].append(count) - - def average(self, n=0): - """Average latest n values or all values.""" - assert n >= 0 - for key in self.val_history: - values = np.array(self.val_history[key][-n:]) - nums = np.array(self.n_history[key][-n:]) - avg = np.sum(values * nums) / np.sum(nums) - self.output[key] = avg - self.ready = True diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/optimizer/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/optimizer/__init__.py deleted file mode 100644 index 53c34d047..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/optimizer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, - build_optimizer_constructor) -from .default_constructor import DefaultOptimizerConstructor - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', - 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/optimizer/builder.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/optimizer/builder.py deleted file mode 100644 index f9234eed8..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/optimizer/builder.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect - -import torch - -from ...utils import Registry, build_from_cfg - -OPTIMIZERS = Registry('optimizer') -OPTIMIZER_BUILDERS = Registry('optimizer builder') - - -def register_torch_optimizers(): - torch_optimizers = [] - for module_name in dir(torch.optim): - if module_name.startswith('__'): - continue - _optim = getattr(torch.optim, module_name) - if inspect.isclass(_optim) and issubclass(_optim, - torch.optim.Optimizer): - OPTIMIZERS.register_module()(_optim) - torch_optimizers.append(module_name) - return torch_optimizers - - -TORCH_OPTIMIZERS = register_torch_optimizers() - - -def build_optimizer_constructor(cfg): - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/optimizer/default_constructor.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/optimizer/default_constructor.py deleted file mode 100644 index e5f5db271..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/optimizer/default_constructor.py +++ /dev/null @@ -1,247 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -from torch.nn import GroupNorm, LayerNorm - -from mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of -from mmcv.utils.ext_loader import check_ops_exist -from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS - - -@OPTIMIZER_BUILDERS.register_module() -class DefaultOptimizerConstructor: - """Default constructor for optimizers. - - By default each parameter share the same optimizer settings, and we - provide an argument ``paramwise_cfg`` to specify parameter-wise settings. - It is a dict and may contain the following fields: - - - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If - one of the keys in ``custom_keys`` is a substring of the name of one - parameter, then the setting of the parameter will be specified by - ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will - be ignored. It should be noted that the aforementioned ``key`` is the - longest key that is a substring of the name of the parameter. If there - are multiple matched keys with the same length, then the key with lower - alphabet order will be chosen. - ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` - and ``decay_mult``. See Example 2 below. - - ``bias_lr_mult`` (float): It will be multiplied to the learning - rate for all bias parameters (except for those in normalization - layers and offset layers of DCN). - - ``bias_decay_mult`` (float): It will be multiplied to the weight - decay for all bias parameters (except for those in - normalization layers, depthwise conv layers, offset layers of DCN). - - ``norm_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of normalization - layers. - - ``dwconv_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of depthwise conv - layers. - - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning - rate for parameters of offset layer in the deformable convs - of a model. - - ``bypass_duplicate`` (bool): If true, the duplicate parameters - would not be added into optimizer. Default: False. - - Note: - 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will - override the effect of ``bias_lr_mult`` in the bias of offset - layer. So be careful when using both ``bias_lr_mult`` and - ``dcn_offset_lr_mult``. If you wish to apply both of them to the - offset layer in deformable convs, set ``dcn_offset_lr_mult`` - to the original ``dcn_offset_lr_mult`` * ``bias_lr_mult``. - 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will - apply it to all the DCN layers in the model. So be careful when - the model contains multiple DCN layers in places other than - backbone. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - optimizer_cfg (dict): The config dict of the optimizer. - Positional fields are - - - `type`: class name of the optimizer. - - Optional fields are - - - any arguments of the corresponding optimizer type, e.g., - lr, weight_decay, momentum, etc. - paramwise_cfg (dict, optional): Parameter-wise options. - - Example 1: - >>> model = torch.nn.modules.Conv1d(1, 1, 1) - >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, - >>> weight_decay=0.0001) - >>> paramwise_cfg = dict(norm_decay_mult=0.) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - - Example 2: - >>> # assume model have attribute model.backbone and model.cls_head - >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) - >>> paramwise_cfg = dict(custom_keys={ - '.backbone': dict(lr_mult=0.1, decay_mult=0.9)}) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - >>> # Then the `lr` and `weight_decay` for model.backbone is - >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for - >>> # model.cls_head is (0.01, 0.95). - """ - - def __init__(self, optimizer_cfg, paramwise_cfg=None): - if not isinstance(optimizer_cfg, dict): - raise TypeError('optimizer_cfg should be a dict', - f'but got {type(optimizer_cfg)}') - self.optimizer_cfg = optimizer_cfg - self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg - self.base_lr = optimizer_cfg.get('lr', None) - self.base_wd = optimizer_cfg.get('weight_decay', None) - self._validate_cfg() - - def _validate_cfg(self): - if not isinstance(self.paramwise_cfg, dict): - raise TypeError('paramwise_cfg should be None or a dict, ' - f'but got {type(self.paramwise_cfg)}') - - if 'custom_keys' in self.paramwise_cfg: - if not isinstance(self.paramwise_cfg['custom_keys'], dict): - raise TypeError( - 'If specified, custom_keys must be a dict, ' - f'but got {type(self.paramwise_cfg["custom_keys"])}') - if self.base_wd is None: - for key in self.paramwise_cfg['custom_keys']: - if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: - raise ValueError('base_wd should not be None') - - # get base lr and weight decay - # weight_decay must be explicitly specified if mult is specified - if ('bias_decay_mult' in self.paramwise_cfg - or 'norm_decay_mult' in self.paramwise_cfg - or 'dwconv_decay_mult' in self.paramwise_cfg): - if self.base_wd is None: - raise ValueError('base_wd should not be None') - - def _is_in(self, param_group, param_group_list): - assert is_list_of(param_group_list, dict) - param = set(param_group['params']) - param_set = set() - for group in param_group_list: - param_set.update(set(group['params'])) - - return not param.isdisjoint(param_set) - - def add_params(self, params, module, prefix='', is_dcn_module=None): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - prefix (str): The prefix of the module - is_dcn_module (int|float|None): If the current module is a - submodule of DCN, `is_dcn_module` will be passed to - control conv_offset layer's learning rate. Defaults to None. - """ - # get param-wise options - custom_keys = self.paramwise_cfg.get('custom_keys', {}) - # first sort with alphabet order and then sort with reversed len of str - sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) - - bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) - bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) - norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) - dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) - bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) - dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) - - # special rules for norm layers and depth-wise conv layers - is_norm = isinstance(module, - (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) - is_dwconv = ( - isinstance(module, torch.nn.Conv2d) - and module.in_channels == module.groups) - - for name, param in module.named_parameters(recurse=False): - param_group = {'params': [param]} - if not param.requires_grad: - params.append(param_group) - continue - if bypass_duplicate and self._is_in(param_group, params): - warnings.warn(f'{prefix} is duplicate. It is skipped since ' - f'bypass_duplicate={bypass_duplicate}') - continue - # if the parameter match one of the custom keys, ignore other rules - is_custom = False - for key in sorted_keys: - if key in f'{prefix}.{name}': - is_custom = True - lr_mult = custom_keys[key].get('lr_mult', 1.) - param_group['lr'] = self.base_lr * lr_mult - if self.base_wd is not None: - decay_mult = custom_keys[key].get('decay_mult', 1.) - param_group['weight_decay'] = self.base_wd * decay_mult - break - - if not is_custom: - # bias_lr_mult affects all bias parameters - # except for norm.bias dcn.conv_offset.bias - if name == 'bias' and not (is_norm or is_dcn_module): - param_group['lr'] = self.base_lr * bias_lr_mult - - if (prefix.find('conv_offset') != -1 and is_dcn_module - and isinstance(module, torch.nn.Conv2d)): - # deal with both dcn_offset's bias & weight - param_group['lr'] = self.base_lr * dcn_offset_lr_mult - - # apply weight decay policies - if self.base_wd is not None: - # norm decay - if is_norm: - param_group[ - 'weight_decay'] = self.base_wd * norm_decay_mult - # depth-wise conv - elif is_dwconv: - param_group[ - 'weight_decay'] = self.base_wd * dwconv_decay_mult - # bias lr and decay - elif name == 'bias' and not is_dcn_module: - # TODO: current bias_decay_mult will have affect on DCN - param_group[ - 'weight_decay'] = self.base_wd * bias_decay_mult - params.append(param_group) - - if check_ops_exist(): - is_dcn_module = False - else: - is_dcn_module = False - for child_name, child_mod in module.named_children(): - child_prefix = f'{prefix}.{child_name}' if prefix else child_name - self.add_params( - params, - child_mod, - prefix=child_prefix, - is_dcn_module=is_dcn_module) - - def __call__(self, model): - if hasattr(model, 'module'): - model = model.module - - optimizer_cfg = self.optimizer_cfg.copy() - # if no paramwise option is specified, just use the global setting - if not self.paramwise_cfg: - optimizer_cfg['params'] = model.parameters() - return build_from_cfg(optimizer_cfg, OPTIMIZERS) - - # set param-wise lr and weight decay recursively - params = [] - self.add_params(params, model) - optimizer_cfg['params'] = params - - return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/priority.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/priority.py deleted file mode 100644 index 64cc4e3a0..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/priority.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from enum import Enum - - -class Priority(Enum): - """Hook priority levels. - - +--------------+------------+ - | Level | Value | - +==============+============+ - | HIGHEST | 0 | - +--------------+------------+ - | VERY_HIGH | 10 | - +--------------+------------+ - | HIGH | 30 | - +--------------+------------+ - | ABOVE_NORMAL | 40 | - +--------------+------------+ - | NORMAL | 50 | - +--------------+------------+ - | BELOW_NORMAL | 60 | - +--------------+------------+ - | LOW | 70 | - +--------------+------------+ - | VERY_LOW | 90 | - +--------------+------------+ - | LOWEST | 100 | - +--------------+------------+ - """ - - HIGHEST = 0 - VERY_HIGH = 10 - HIGH = 30 - ABOVE_NORMAL = 40 - NORMAL = 50 - BELOW_NORMAL = 60 - LOW = 70 - VERY_LOW = 90 - LOWEST = 100 - - -def get_priority(priority): - """Get priority value. - - Args: - priority (int or str or :obj:`Priority`): Priority. - - Returns: - int: The priority value. - """ - if isinstance(priority, int): - if priority < 0 or priority > 100: - raise ValueError('priority must be between 0 and 100') - return priority - elif isinstance(priority, Priority): - return priority.value - elif isinstance(priority, str): - return Priority[priority.upper()].value - else: - raise TypeError('priority must be an integer or Priority enum value') diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/runner/utils.py b/cv/instance_segmentation/solo/pytorch/mmcv/runner/utils.py deleted file mode 100644 index 144d11e1a..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/runner/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random -import sys -import time -import warnings -from getpass import getuser -from socket import gethostname - -import numpy as np -import torch - -import mmcv - - -def get_host_info(): - """Get hostname and username. - - Return empty string if exception raised, e.g. ``getpass.getuser()`` will - lead to error in docker container - """ - host = '' - try: - host = f'{getuser()}@{gethostname()}' - except Exception as e: - warnings.warn(f'Host or user not found: {str(e)}') - finally: - return host - - -def get_time_str(): - return time.strftime('%Y%m%d_%H%M%S', time.localtime()) - - -def obj_from_dict(info, parent=None, default_args=None): - """Initialize an object from dict. - - The dict must contain the key "type", which indicates the object type, it - can be either a string or type, such as "list" or ``list``. Remaining - fields are treated as the arguments for constructing the object. - - Args: - info (dict): Object types and arguments. - parent (:class:`module`): Module which may containing expected object - classes. - default_args (dict, optional): Default arguments for initializing the - object. - - Returns: - any type: Object built from the dict. - """ - assert isinstance(info, dict) and 'type' in info - assert isinstance(default_args, dict) or default_args is None - args = info.copy() - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if parent is not None: - obj_type = getattr(parent, obj_type) - else: - obj_type = sys.modules[obj_type] - elif not isinstance(obj_type, type): - raise TypeError('type must be a str or valid type, but ' - f'got {type(obj_type)}') - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - return obj_type(**args) - - -def set_random_seed(seed, deterministic=False, use_rank_shift=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - rank_shift (bool): Whether to add rank number to the random seed to - have different random seed in different threads. Default: False. - """ - if use_rank_shift: - rank, _ = mmcv.runner.get_dist_info() - seed += rank - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/__init__.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/__init__.py deleted file mode 100644 index 378a00684..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/__init__.py +++ /dev/null @@ -1,69 +0,0 @@ -# flake8: noqa -# Copyright (c) OpenMMLab. All rights reserved. -from .config import Config, ConfigDict, DictAction -from .misc import (check_prerequisites, concat_list, deprecated_api_warning, - has_method, import_modules_from_strings, is_list_of, - is_method_overridden, is_seq_of, is_str, is_tuple_of, - iter_cast, list_cast, requires_executable, requires_package, - slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, - to_ntuple, tuple_cast) -from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, - scandir, symlink) -from .progressbar import (ProgressBar, track_iter_progress, - track_parallel_progress, track_progress) -from .testing import (assert_attrs_equal, assert_dict_contains_subset, - assert_dict_has_keys, assert_is_norm_layer, - assert_keys_equal, assert_params_all_zeros, - check_python_script) -from .timer import Timer, TimerError, check_time -from .version_utils import digit_version, get_git_hash - -try: - import torch -except ImportError: - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', - 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', - 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', - 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', - 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', - 'track_progress', 'track_iter_progress', 'track_parallel_progress', - 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', - 'digit_version', 'get_git_hash', 'import_modules_from_strings', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', - 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', - 'is_method_overridden', 'has_method' - ] -else: - from .env import collect_env - from .logging import get_logger, print_log - from .parrots_jit import jit, skip_no_elena - from .parrots_wrapper import ( - TORCH_VERSION, BuildExtension, CppExtension, CUDAExtension, DataLoader, - PoolDataLoader, SyncBatchNorm, _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, - _AvgPoolNd, _BatchNorm, _ConvNd, _ConvTransposeMixin, _InstanceNorm, - _MaxPoolNd, get_build_config, is_rocm_pytorch, _get_cuda_home) - from .registry import Registry, build_from_cfg - from .trace import is_jit_tracing - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', - 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', - 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', - 'check_prerequisites', 'requires_package', 'requires_executable', - 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', - 'symlink', 'scandir', 'ProgressBar', 'track_progress', - 'track_iter_progress', 'track_parallel_progress', 'Registry', - 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', - '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', - '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', - 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', - 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', - 'deprecated_api_warning', 'digit_version', 'get_git_hash', - 'import_modules_from_strings', 'jit', 'skip_no_elena', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', - 'assert_params_all_zeros', 'check_python_script', - 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', - '_get_cuda_home', 'has_method' - ] diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/config.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/config.py deleted file mode 100644 index c71377c07..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/config.py +++ /dev/null @@ -1,688 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import ast -import copy -import os -import os.path as osp -import platform -import shutil -import sys -import tempfile -import uuid -import warnings -from argparse import Action, ArgumentParser -from collections import abc -from importlib import import_module - -from addict import Dict -from yapf.yapflib.yapf_api import FormatCode - -from .misc import import_modules_from_strings -from .path import check_file_exist - -if platform.system() == 'Windows': - import regex as re -else: - import re - -BASE_KEY = '_base_' -DELETE_KEY = '_delete_' -DEPRECATION_KEY = '_deprecation_' -RESERVED_KEYS = ['filename', 'text', 'pretty_text'] - - -class ConfigDict(Dict): - - def __missing__(self, name): - raise KeyError(name) - - def __getattr__(self, name): - try: - value = super(ConfigDict, self).__getattr__(name) - except KeyError: - ex = AttributeError(f"'{self.__class__.__name__}' object has no " - f"attribute '{name}'") - except Exception as e: - ex = e - else: - return value - raise ex - - -def add_args(parser, cfg, prefix=''): - for k, v in cfg.items(): - if isinstance(v, str): - parser.add_argument('--' + prefix + k) - elif isinstance(v, int): - parser.add_argument('--' + prefix + k, type=int) - elif isinstance(v, float): - parser.add_argument('--' + prefix + k, type=float) - elif isinstance(v, bool): - parser.add_argument('--' + prefix + k, action='store_true') - elif isinstance(v, dict): - add_args(parser, v, prefix + k + '.') - elif isinstance(v, abc.Iterable): - parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') - else: - print(f'cannot parse key {prefix + k} of type {type(v)}') - return parser - - -class Config: - """A facility for config and config files. - - It supports common file formats as configs: python/json/yaml. The interface - is the same as a dict object and also allows access config values as - attributes. - - Example: - >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - >>> cfg.a - 1 - >>> cfg.b - {'b1': [0, 1]} - >>> cfg.b.b1 - [0, 1] - >>> cfg = Config.fromfile('tests/data/config/a.py') - >>> cfg.filename - "/home/kchen/projects/mmcv/tests/data/config/a.py" - >>> cfg.item4 - 'test' - >>> cfg - "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " - "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" - """ - - @staticmethod - def _validate_py_syntax(filename): - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - content = f.read() - try: - ast.parse(content) - except SyntaxError as e: - raise SyntaxError('There are syntax errors in config ' - f'file {filename}: {e}') - - @staticmethod - def _substitute_predefined_vars(filename, temp_config_name): - file_dirname = osp.dirname(filename) - file_basename = osp.basename(filename) - file_basename_no_extension = osp.splitext(file_basename)[0] - file_extname = osp.splitext(filename)[1] - support_templates = dict( - fileDirname=file_dirname, - fileBasename=file_basename, - fileBasenameNoExtension=file_basename_no_extension, - fileExtname=file_extname) - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - for key, value in support_templates.items(): - regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' - value = value.replace('\\', '/') - config_file = re.sub(regexp, value, config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - - @staticmethod - def _pre_substitute_base_vars(filename, temp_config_name): - """Substitute base variable placehoders to string, so that parsing - would work.""" - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - base_var_dict = {} - regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' - base_vars = set(re.findall(regexp, config_file)) - for base_var in base_vars: - randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' - base_var_dict[randstr] = base_var - regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' - config_file = re.sub(regexp, f'"{randstr}"', config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - return base_var_dict - - @staticmethod - def _substitute_base_vars(cfg, base_var_dict, base_cfg): - """Substitute variable strings to their actual values.""" - cfg = copy.deepcopy(cfg) - - if isinstance(cfg, dict): - for k, v in cfg.items(): - if isinstance(v, str) and v in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[v].split('.'): - new_v = new_v[new_k] - cfg[k] = new_v - elif isinstance(v, (list, tuple, dict)): - cfg[k] = Config._substitute_base_vars( - v, base_var_dict, base_cfg) - elif isinstance(cfg, tuple): - cfg = tuple( - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg) - elif isinstance(cfg, list): - cfg = [ - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg - ] - elif isinstance(cfg, str) and cfg in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[cfg].split('.'): - new_v = new_v[new_k] - cfg = new_v - - return cfg - - @staticmethod - def _file2dict(filename, use_predefined_variables=True): - filename = osp.abspath(osp.expanduser(filename)) - check_file_exist(filename) - fileExtname = osp.splitext(filename)[1] - if fileExtname not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - - with tempfile.TemporaryDirectory() as temp_config_dir: - temp_config_file = tempfile.NamedTemporaryFile( - dir=temp_config_dir, suffix=fileExtname) - if platform.system() == 'Windows': - temp_config_file.close() - temp_config_name = osp.basename(temp_config_file.name) - # Substitute predefined variables - if use_predefined_variables: - Config._substitute_predefined_vars(filename, - temp_config_file.name) - else: - shutil.copyfile(filename, temp_config_file.name) - # Substitute base variables from placeholders to strings - base_var_dict = Config._pre_substitute_base_vars( - temp_config_file.name, temp_config_file.name) - - if filename.endswith('.py'): - temp_module_name = osp.splitext(temp_config_name)[0] - sys.path.insert(0, temp_config_dir) - Config._validate_py_syntax(filename) - mod = import_module(temp_module_name) - sys.path.pop(0) - cfg_dict = { - name: value - for name, value in mod.__dict__.items() - if not name.startswith('__') - } - # delete imported module - del sys.modules[temp_module_name] - elif filename.endswith(('.yml', '.yaml', '.json')): - import mmcv - cfg_dict = mmcv.load(temp_config_file.name) - # close temp file - temp_config_file.close() - - # check deprecation information - if DEPRECATION_KEY in cfg_dict: - deprecation_info = cfg_dict.pop(DEPRECATION_KEY) - warning_msg = f'The config file {filename} will be deprecated ' \ - 'in the future.' - if 'expected' in deprecation_info: - warning_msg += f' Please use {deprecation_info["expected"]} ' \ - 'instead.' - if 'reference' in deprecation_info: - warning_msg += ' More information can be found at ' \ - f'{deprecation_info["reference"]}' - warnings.warn(warning_msg) - - cfg_text = filename + '\n' - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - cfg_text += f.read() - - if BASE_KEY in cfg_dict: - cfg_dir = osp.dirname(filename) - base_filename = cfg_dict.pop(BASE_KEY) - base_filename = base_filename if isinstance( - base_filename, list) else [base_filename] - - cfg_dict_list = list() - cfg_text_list = list() - for f in base_filename: - _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) - cfg_dict_list.append(_cfg_dict) - cfg_text_list.append(_cfg_text) - - base_cfg_dict = dict() - for c in cfg_dict_list: - duplicate_keys = base_cfg_dict.keys() & c.keys() - if len(duplicate_keys) > 0: - raise KeyError('Duplicate key is not allowed among bases. ' - f'Duplicate keys: {duplicate_keys}') - base_cfg_dict.update(c) - - # Substitute base variables from strings to their actual values - cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, - base_cfg_dict) - - base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) - cfg_dict = base_cfg_dict - - # merge cfg_text - cfg_text_list.append(cfg_text) - cfg_text = '\n'.join(cfg_text_list) - - return cfg_dict, cfg_text - - @staticmethod - def _merge_a_into_b(a, b, allow_list_keys=False): - """merge dict ``a`` into dict ``b`` (non-inplace). - - Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid - in-place modifications. - - Args: - a (dict): The source dict to be merged into ``b``. - b (dict): The origin dict to be fetch keys from ``a``. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in source ``a`` and will replace the element of the - corresponding index in b if b is a list. Default: False. - - Returns: - dict: The modified dict of ``b`` using ``a``. - - Examples: - # Normally merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # Delete b first and merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # b is a list - >>> Config._merge_a_into_b( - ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) - [{'a': 2}, {'b': 2}] - """ - b = b.copy() - for k, v in a.items(): - if allow_list_keys and k.isdigit() and isinstance(b, list): - k = int(k) - if len(b) <= k: - raise KeyError(f'Index {k} exceeds the length of list {b}') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - elif isinstance(v, - dict) and k in b and not v.pop(DELETE_KEY, False): - allowed_types = (dict, list) if allow_list_keys else dict - if not isinstance(b[k], allowed_types): - raise TypeError( - f'{k}={v} in child config cannot inherit from base ' - f'because {k} is a dict in the child config but is of ' - f'type {type(b[k])} in base config. You may set ' - f'`{DELETE_KEY}=True` to ignore the base config') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - else: - b[k] = v - return b - - @staticmethod - def fromfile(filename, - use_predefined_variables=True, - import_custom_modules=True): - cfg_dict, cfg_text = Config._file2dict(filename, - use_predefined_variables) - if import_custom_modules and cfg_dict.get('custom_imports', None): - import_modules_from_strings(**cfg_dict['custom_imports']) - return Config(cfg_dict, cfg_text=cfg_text, filename=filename) - - @staticmethod - def fromstring(cfg_str, file_format): - """Generate config from config str. - - Args: - cfg_str (str): Config str. - file_format (str): Config file format corresponding to the - config str. Only py/yml/yaml/json type are supported now! - - Returns: - obj:`Config`: Config obj. - """ - if file_format not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - if file_format != '.py' and 'dict(' in cfg_str: - # check if users specify a wrong suffix for python - warnings.warn( - 'Please check "file_format", the file format may be .py') - with tempfile.NamedTemporaryFile( - 'w', encoding='utf-8', suffix=file_format, - delete=False) as temp_file: - temp_file.write(cfg_str) - # on windows, previous implementation cause error - # see PR 1077 for details - cfg = Config.fromfile(temp_file.name) - os.remove(temp_file.name) - return cfg - - @staticmethod - def auto_argparser(description=None): - """Generate argparser from config file automatically (experimental)""" - partial_parser = ArgumentParser(description=description) - partial_parser.add_argument('config', help='config file path') - cfg_file = partial_parser.parse_known_args()[0].config - cfg = Config.fromfile(cfg_file) - parser = ArgumentParser(description=description) - parser.add_argument('config', help='config file path') - add_args(parser, cfg) - return parser, cfg - - def __init__(self, cfg_dict=None, cfg_text=None, filename=None): - if cfg_dict is None: - cfg_dict = dict() - elif not isinstance(cfg_dict, dict): - raise TypeError('cfg_dict must be a dict, but ' - f'got {type(cfg_dict)}') - for key in cfg_dict: - if key in RESERVED_KEYS: - raise KeyError(f'{key} is reserved for config file') - - super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) - super(Config, self).__setattr__('_filename', filename) - if cfg_text: - text = cfg_text - elif filename: - with open(filename, 'r') as f: - text = f.read() - else: - text = '' - super(Config, self).__setattr__('_text', text) - - @property - def filename(self): - return self._filename - - @property - def text(self): - return self._text - - @property - def pretty_text(self): - - indent = 4 - - def _indent(s_, num_spaces): - s = s_.split('\n') - if len(s) == 1: - return s_ - first = s.pop(0) - s = [(num_spaces * ' ') + line for line in s] - s = '\n'.join(s) - s = first + '\n' + s - return s - - def _format_basic_types(k, v, use_mapping=False): - if isinstance(v, str): - v_str = f"'{v}'" - else: - v_str = str(v) - - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) - - return attr_str - - def _format_list(k, v, use_mapping=False): - # check if all items in the list are dict - if all(isinstance(_, dict) for _ in v): - v_str = '[\n' - v_str += '\n'.join( - f'dict({_indent(_format_dict(v_), indent)}),' - for v_ in v).rstrip(',') - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) + ']' - else: - attr_str = _format_basic_types(k, v, use_mapping) - return attr_str - - def _contain_invalid_identifier(dict_str): - contain_invalid_identifier = False - for key_name in dict_str: - contain_invalid_identifier |= \ - (not str(key_name).isidentifier()) - return contain_invalid_identifier - - def _format_dict(input_dict, outest_level=False): - r = '' - s = [] - - use_mapping = _contain_invalid_identifier(input_dict) - if use_mapping: - r += '{' - for idx, (k, v) in enumerate(input_dict.items()): - is_last = idx >= len(input_dict) - 1 - end = '' if outest_level or is_last else ',' - if isinstance(v, dict): - v_str = '\n' + _format_dict(v) - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: dict({v_str}' - else: - attr_str = f'{str(k)}=dict({v_str}' - attr_str = _indent(attr_str, indent) + ')' + end - elif isinstance(v, list): - attr_str = _format_list(k, v, use_mapping) + end - else: - attr_str = _format_basic_types(k, v, use_mapping) + end - - s.append(attr_str) - r += '\n'.join(s) - if use_mapping: - r += '}' - return r - - cfg_dict = self._cfg_dict.to_dict() - text = _format_dict(cfg_dict, outest_level=True) - # copied from setup.cfg - yapf_style = dict( - based_on_style='pep8', - blank_line_before_nested_class_or_def=True, - split_before_expression_after_opening_paren=True) - text, _ = FormatCode(text, style_config=yapf_style, verify=True) - - return text - - def __repr__(self): - return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' - - def __len__(self): - return len(self._cfg_dict) - - def __getattr__(self, name): - return getattr(self._cfg_dict, name) - - def __getitem__(self, name): - return self._cfg_dict.__getitem__(name) - - def __setattr__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setattr__(name, value) - - def __setitem__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setitem__(name, value) - - def __iter__(self): - return iter(self._cfg_dict) - - def __getstate__(self): - return (self._cfg_dict, self._filename, self._text) - - def __setstate__(self, state): - _cfg_dict, _filename, _text = state - super(Config, self).__setattr__('_cfg_dict', _cfg_dict) - super(Config, self).__setattr__('_filename', _filename) - super(Config, self).__setattr__('_text', _text) - - def dump(self, file=None): - cfg_dict = super(Config, self).__getattribute__('_cfg_dict').to_dict() - if self.filename.endswith('.py'): - if file is None: - return self.pretty_text - else: - with open(file, 'w', encoding='utf-8') as f: - f.write(self.pretty_text) - else: - import mmcv - if file is None: - file_format = self.filename.split('.')[-1] - return mmcv.dump(cfg_dict, file_format=file_format) - else: - mmcv.dump(cfg_dict, file) - - def merge_from_dict(self, options, allow_list_keys=True): - """Merge list into cfg_dict. - - Merge the dict parsed by MultipleKVAction into this cfg. - - Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} - >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) - >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - - # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) - >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) - >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - - Args: - options (dict): dict of configs to merge from. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in ``options`` and will replace the element of the - corresponding index in the config if the config is a list. - Default: True. - """ - option_cfg_dict = {} - for full_key, v in options.items(): - d = option_cfg_dict - key_list = full_key.split('.') - for subkey in key_list[:-1]: - d.setdefault(subkey, ConfigDict()) - d = d[subkey] - subkey = key_list[-1] - d[subkey] = v - - cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - super(Config, self).__setattr__( - '_cfg_dict', - Config._merge_a_into_b( - option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) - - -class DictAction(Action): - """ - argparse action to split an argument into KEY=VALUE form - on the first = and append to a dictionary. List options can - be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit - brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build - list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' - """ - - @staticmethod - def _parse_int_float_bool(val): - try: - return int(val) - except ValueError: - pass - try: - return float(val) - except ValueError: - pass - if val.lower() in ['true', 'false']: - return True if val.lower() == 'true' else False - return val - - @staticmethod - def _parse_iterable(val): - """Parse iterable values in the string. - - All elements inside '()' or '[]' are treated as iterable values. - - Args: - val (str): Value string. - - Returns: - list | tuple: The expanded list or tuple from the string. - - Examples: - >>> DictAction._parse_iterable('1,2,3') - [1, 2, 3] - >>> DictAction._parse_iterable('[a, b, c]') - ['a', 'b', 'c'] - >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') - [(1, 2, 3), ['a', 'b'], 'c'] - """ - - def find_next_comma(string): - """Find the position of next comma in the string. - - If no ',' is found in the string, return the string length. All - chars inside '()' and '[]' are treated as one element and thus ',' - inside these brackets are ignored. - """ - assert (string.count('(') == string.count(')')) and ( - string.count('[') == string.count(']')), \ - f'Imbalanced brackets exist in {string}' - end = len(string) - for idx, char in enumerate(string): - pre = string[:idx] - # The string before this ',' is balanced - if ((char == ',') and (pre.count('(') == pre.count(')')) - and (pre.count('[') == pre.count(']'))): - end = idx - break - return end - - # Strip ' and " characters and replace whitespace. - val = val.strip('\'\"').replace(' ', '') - is_tuple = False - if val.startswith('(') and val.endswith(')'): - is_tuple = True - val = val[1:-1] - elif val.startswith('[') and val.endswith(']'): - val = val[1:-1] - elif ',' not in val: - # val is a single value - return DictAction._parse_int_float_bool(val) - - values = [] - while len(val) > 0: - comma_idx = find_next_comma(val) - element = DictAction._parse_iterable(val[:comma_idx]) - values.append(element) - val = val[comma_idx + 1:] - if is_tuple: - values = tuple(values) - return values - - def __call__(self, parser, namespace, values, option_string=None): - options = {} - for kv in values: - key, val = kv.split('=', maxsplit=1) - options[key] = self._parse_iterable(val) - setattr(namespace, self.dest, options) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/env.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/env.py deleted file mode 100644 index e46a1094f..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/env.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This file holding some environment constant for sharing by other files.""" - -import os.path as osp -import subprocess -import sys -from collections import defaultdict - -import cv2 -import torch - -import mmcv -from .parrots_wrapper import get_build_config - - -def collect_env(): - """Collect the information of the running environments. - - Returns: - dict: The environment information. The following fields are contained. - - - sys.platform: The variable of ``sys.platform``. - - Python: Python version. - - CUDA available: Bool, indicating if CUDA is available. - - GPU devices: Device type of each GPU. - - CUDA_HOME (optional): The env var ``CUDA_HOME``. - - NVCC (optional): NVCC version. - - GCC: GCC version, "n/a" if GCC is not installed. - - PyTorch: PyTorch version. - - PyTorch compiling details: The output of \ - ``torch.__config__.show()``. - - TorchVision (optional): TorchVision version. - - OpenCV: OpenCV version. - - MMCV: MMCV version. - - MMCV Compiler: The GCC version for compiling MMCV ops. - - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. - """ - env_info = {} - env_info['sys.platform'] = sys.platform - env_info['Python'] = sys.version.replace('\n', '') - - cuda_available = torch.cuda.is_available() - env_info['CUDA available'] = cuda_available - - if cuda_available: - devices = defaultdict(list) - for k in range(torch.cuda.device_count()): - devices[torch.cuda.get_device_name(k)].append(str(k)) - for name, device_ids in devices.items(): - env_info['GPU ' + ','.join(device_ids)] = name - - from mmcv.utils.parrots_wrapper import _get_cuda_home - CUDA_HOME = _get_cuda_home() - env_info['CUDA_HOME'] = CUDA_HOME - - if CUDA_HOME is not None and osp.isdir(CUDA_HOME): - try: - nvcc = osp.join(CUDA_HOME, 'bin/nvcc') - nvcc = subprocess.check_output( - f'"{nvcc}" -V | tail -n1', shell=True) - nvcc = nvcc.decode('utf-8').strip() - except subprocess.SubprocessError: - nvcc = 'Not Available' - env_info['NVCC'] = nvcc - - try: - gcc = subprocess.check_output('gcc --version | head -n1', shell=True) - gcc = gcc.decode('utf-8').strip() - env_info['GCC'] = gcc - except subprocess.CalledProcessError: # gcc is unavailable - env_info['GCC'] = 'n/a' - - env_info['PyTorch'] = torch.__version__ - env_info['PyTorch compiling details'] = get_build_config() - - try: - import torchvision - env_info['TorchVision'] = torchvision.__version__ - except ModuleNotFoundError: - pass - - env_info['OpenCV'] = cv2.__version__ - - env_info['MMCV'] = mmcv.__version__ - - try: - from mmcv.ops import get_compiler_version, get_compiling_cuda_version - except ModuleNotFoundError: - env_info['MMCV Compiler'] = 'n/a' - env_info['MMCV CUDA Compiler'] = 'n/a' - else: - env_info['MMCV Compiler'] = get_compiler_version() - env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() - - return env_info diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/ext_loader.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/ext_loader.py deleted file mode 100644 index 08132d2c1..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/ext_loader.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os -import pkgutil -import warnings -from collections import namedtuple - -import torch - -if torch.__version__ != 'parrots': - - def load_ext(name, funcs): - ext = importlib.import_module('mmcv.' + name) - for fun in funcs: - assert hasattr(ext, fun), f'{fun} miss in module {name}' - return ext -else: - from parrots import extension - from parrots.base import ParrotsException - - has_return_value_ops = [ - 'nms', - 'softnms', - 'nms_match', - 'nms_rotated', - 'top_pool_forward', - 'top_pool_backward', - 'bottom_pool_forward', - 'bottom_pool_backward', - 'left_pool_forward', - 'left_pool_backward', - 'right_pool_forward', - 'right_pool_backward', - 'fused_bias_leakyrelu', - 'upfirdn2d', - 'ms_deform_attn_forward', - 'pixel_group', - 'contour_expand', - ] - - def get_fake_func(name, e): - - def fake_func(*args, **kwargs): - warnings.warn(f'{name} is not supported in parrots now') - raise e - - return fake_func - - def load_ext(name, funcs): - ExtModule = namedtuple('ExtModule', funcs) - ext_list = [] - lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - for fun in funcs: - try: - ext_fun = extension.load(fun, name, lib_dir=lib_root) - except ParrotsException as e: - if 'No element registered' not in e.message: - warnings.warn(e.message) - ext_fun = get_fake_func(fun, e) - ext_list.append(ext_fun) - else: - if fun in has_return_value_ops: - ext_list.append(ext_fun.op) - else: - ext_list.append(ext_fun.op_) - return ExtModule(*ext_list) - - -def check_ops_exist(): - ext_loader = pkgutil.find_loader('mmcv._ext') - return ext_loader is not None diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/logging.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/logging.py deleted file mode 100644 index 4aa0e04bb..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/logging.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.distributed as dist - -logger_initialized = {} - - -def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): - """Initialize and get a logger by name. - - If the logger has not been initialized, this method will initialize the - logger by adding one or two handlers, otherwise the initialized logger will - be directly returned. During initialization, a StreamHandler will always be - added. If `log_file` is specified and the process rank is 0, a FileHandler - will also be added. - - Args: - name (str): Logger name. - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the logger. - log_level (int): The logger level. Note that only the process of - rank 0 is affected, and other processes will set the level to - "Error" thus be silent most of the time. - file_mode (str): The file mode used in opening log file. - Defaults to 'w'. - - Returns: - logging.Logger: The expected logger. - """ - logger = logging.getLogger(name) - if name in logger_initialized: - return logger - # handle hierarchical names - # e.g., logger "a" is initialized, then logger "a.b" will skip the - # initialization since it is a child of "a". - for logger_name in logger_initialized: - if name.startswith(logger_name): - return logger - - # handle duplicate logs to the console - # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) - # to the root logger. As logger.propagate is True by default, this root - # level handler causes logging messages from rank>0 processes to - # unexpectedly show up on the console, creating much unwanted clutter. - # To fix this issue, we set the root logger's StreamHandler, if any, to log - # at the ERROR level. - for handler in logger.root.handlers: - if type(handler) is logging.StreamHandler: - handler.setLevel(logging.ERROR) - - stream_handler = logging.StreamHandler() - handlers = [stream_handler] - - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - else: - rank = 0 - - # only rank 0 will add a FileHandler - if rank == 0 and log_file is not None: - # Here, the default behaviour of the official logger is 'a'. Thus, we - # provide an interface to change the file mode to the default - # behaviour. - file_handler = logging.FileHandler(log_file, file_mode) - handlers.append(file_handler) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - for handler in handlers: - handler.setFormatter(formatter) - handler.setLevel(log_level) - logger.addHandler(handler) - - if rank == 0: - logger.setLevel(log_level) - else: - logger.setLevel(logging.ERROR) - - logger_initialized[name] = True - - return logger - - -def print_log(msg, logger=None, level=logging.INFO): - """Print a log message. - - Args: - msg (str): The message to be logged. - logger (logging.Logger | str | None): The logger to be used. - Some special loggers are: - - "silent": no message will be printed. - - other str: the logger obtained with `get_root_logger(logger)`. - - None: The `print()` method will be used to print log messages. - level (int): Logging level. Only available when `logger` is a Logger - object or "root". - """ - if logger is None: - print(msg) - elif isinstance(logger, logging.Logger): - logger.log(level, msg) - elif logger == 'silent': - pass - elif isinstance(logger, str): - _logger = get_logger(logger) - _logger.log(level, msg) - else: - raise TypeError( - 'logger should be either a logging.Logger object, str, ' - f'"silent" or None, but got {type(logger)}') diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/misc.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/misc.py deleted file mode 100644 index 2c58d0d7f..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/misc.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections.abc -import functools -import itertools -import subprocess -import warnings -from collections import abc -from importlib import import_module -from inspect import getfullargspec -from itertools import repeat - - -# From PyTorch internals -def _ntuple(n): - - def parse(x): - if isinstance(x, collections.abc.Iterable): - return x - return tuple(repeat(x, n)) - - return parse - - -to_1tuple = _ntuple(1) -to_2tuple = _ntuple(2) -to_3tuple = _ntuple(3) -to_4tuple = _ntuple(4) -to_ntuple = _ntuple - - -def is_str(x): - """Whether the input is an string instance. - - Note: This method is deprecated since python 2 is no longer supported. - """ - return isinstance(x, str) - - -def import_modules_from_strings(imports, allow_failed_imports=False): - """Import modules from the given list of strings. - - Args: - imports (list | str | None): The given module names to be imported. - allow_failed_imports (bool): If True, the failed imports will return - None. Otherwise, an ImportError is raise. Default: False. - - Returns: - list[module] | module | None: The imported modules. - - Examples: - >>> osp, sys = import_modules_from_strings( - ... ['os.path', 'sys']) - >>> import os.path as osp_ - >>> import sys as sys_ - >>> assert osp == osp_ - >>> assert sys == sys_ - """ - if not imports: - return - single_import = False - if isinstance(imports, str): - single_import = True - imports = [imports] - if not isinstance(imports, list): - raise TypeError( - f'custom_imports must be a list but got type {type(imports)}') - imported = [] - for imp in imports: - if not isinstance(imp, str): - raise TypeError( - f'{imp} is of type {type(imp)} and cannot be imported.') - try: - imported_tmp = import_module(imp) - except ImportError: - if allow_failed_imports: - warnings.warn(f'{imp} failed to import and is ignored.', - UserWarning) - imported_tmp = None - else: - raise ImportError - imported.append(imported_tmp) - if single_import: - imported = imported[0] - return imported - - -def iter_cast(inputs, dst_type, return_type=None): - """Cast elements of an iterable object into some type. - - Args: - inputs (Iterable): The input object. - dst_type (type): Destination type. - return_type (type, optional): If specified, the output object will be - converted to this type, otherwise an iterator. - - Returns: - iterator or specified type: The converted object. - """ - if not isinstance(inputs, abc.Iterable): - raise TypeError('inputs must be an iterable object') - if not isinstance(dst_type, type): - raise TypeError('"dst_type" must be a valid type') - - out_iterable = map(dst_type, inputs) - - if return_type is None: - return out_iterable - else: - return return_type(out_iterable) - - -def list_cast(inputs, dst_type): - """Cast elements of an iterable object into a list of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=list) - - -def tuple_cast(inputs, dst_type): - """Cast elements of an iterable object into a tuple of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=tuple) - - -def is_seq_of(seq, expected_type, seq_type=None): - """Check whether it is a sequence of some type. - - Args: - seq (Sequence): The sequence to be checked. - expected_type (type): Expected type of sequence items. - seq_type (type, optional): Expected sequence type. - - Returns: - bool: Whether the sequence is valid. - """ - if seq_type is None: - exp_seq_type = abc.Sequence - else: - assert isinstance(seq_type, type) - exp_seq_type = seq_type - if not isinstance(seq, exp_seq_type): - return False - for item in seq: - if not isinstance(item, expected_type): - return False - return True - - -def is_list_of(seq, expected_type): - """Check whether it is a list of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=list) - - -def is_tuple_of(seq, expected_type): - """Check whether it is a tuple of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=tuple) - - -def slice_list(in_list, lens): - """Slice a list into several sub lists by a list of given length. - - Args: - in_list (list): The list to be sliced. - lens(int or list): The expected length of each out list. - - Returns: - list: A list of sliced list. - """ - if isinstance(lens, int): - assert len(in_list) % lens == 0 - lens = [lens] * int(len(in_list) / lens) - if not isinstance(lens, list): - raise TypeError('"indices" must be an integer or a list of integers') - elif sum(lens) != len(in_list): - raise ValueError('sum of lens and list length does not ' - f'match: {sum(lens)} != {len(in_list)}') - out_list = [] - idx = 0 - for i in range(len(lens)): - out_list.append(in_list[idx:idx + lens[i]]) - idx += lens[i] - return out_list - - -def concat_list(in_list): - """Concatenate a list of list into a single list. - - Args: - in_list (list): The list of list to be merged. - - Returns: - list: The concatenated flat list. - """ - return list(itertools.chain(*in_list)) - - -def check_prerequisites( - prerequisites, - checker, - msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' - 'found, please install them first.'): # yapf: disable - """A decorator factory to check if prerequisites are satisfied. - - Args: - prerequisites (str of list[str]): Prerequisites to be checked. - checker (callable): The checker method that returns True if a - prerequisite is meet, False otherwise. - msg_tmpl (str): The message template with two variables. - - Returns: - decorator: A specific decorator. - """ - - def wrap(func): - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - requirements = [prerequisites] if isinstance( - prerequisites, str) else prerequisites - missing = [] - for item in requirements: - if not checker(item): - missing.append(item) - if missing: - print(msg_tmpl.format(', '.join(missing), func.__name__)) - raise RuntimeError('Prerequisites not meet.') - else: - return func(*args, **kwargs) - - return wrapped_func - - return wrap - - -def _check_py_package(package): - try: - import_module(package) - except ImportError: - return False - else: - return True - - -def _check_executable(cmd): - if subprocess.call(f'which {cmd}', shell=True) != 0: - return False - else: - return True - - -def requires_package(prerequisites): - """A decorator to check if some python packages are installed. - - Example: - >>> @requires_package('numpy') - >>> func(arg1, args): - >>> return numpy.zeros(1) - array([0.]) - >>> @requires_package(['numpy', 'non_package']) - >>> func(arg1, args): - >>> return numpy.zeros(1) - ImportError - """ - return check_prerequisites(prerequisites, checker=_check_py_package) - - -def requires_executable(prerequisites): - """A decorator to check if some executable files are installed. - - Example: - >>> @requires_executable('ffmpeg') - >>> func(arg1, args): - >>> print(1) - 1 - """ - return check_prerequisites(prerequisites, checker=_check_executable) - - -def deprecated_api_warning(name_dict, cls_name=None): - """A decorator to check if some arguments are deprecate and try to replace - deprecate src_arg_name to dst_arg_name. - - Args: - name_dict(dict): - key (str): Deprecate argument names. - val (str): Expected argument names. - - Returns: - func: New function. - """ - - def api_warning_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get name of the function - func_name = old_func.__name__ - if cls_name is not None: - func_name = f'{cls_name}.{func_name}' - if args: - arg_names = args_info.args[:len(args)] - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in arg_names: - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead') - arg_names[arg_names.index(src_arg_name)] = dst_arg_name - if kwargs: - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in kwargs: - - assert dst_arg_name not in kwargs, ( - f'The expected behavior is to replace ' - f'the deprecated key `{src_arg_name}` to ' - f'new key `{dst_arg_name}`, but got them ' - f'in the arguments at the same time, which ' - f'is confusing. `{src_arg_name} will be ' - f'deprecated in the future, please ' - f'use `{dst_arg_name}` instead.') - - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead') - kwargs[dst_arg_name] = kwargs.pop(src_arg_name) - - # apply converted arguments to the decorated method - output = old_func(*args, **kwargs) - return output - - return new_func - - return api_warning_wrapper - - -def is_method_overridden(method, base_class, derived_class): - """Check if a method of base class is overridden in derived class. - - Args: - method (str): the method name to check. - base_class (type): the class of the base class. - derived_class (type | Any): the class or instance of the derived class. - """ - assert isinstance(base_class, type), \ - "base_class doesn't accept instance, Please pass class instead." - - if not isinstance(derived_class, type): - derived_class = derived_class.__class__ - - base_method = getattr(base_class, method) - derived_method = getattr(derived_class, method) - return derived_method != base_method - - -def has_method(obj: object, method: str) -> bool: - """Check whether the object has a method. - - Args: - method (str): The method name to check. - obj (object): The object to check. - - Returns: - bool: True if the object has the method else False. - """ - return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/parrots_jit.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/parrots_jit.py deleted file mode 100644 index 61873f6db..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/parrots_jit.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -from .parrots_wrapper import TORCH_VERSION - -parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') - -if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': - from parrots.jit import pat as jit -else: - - def jit(func=None, - check_input=None, - full_shape=True, - derivate=False, - coderize=False, - optimize=False): - - def wrapper(func): - - def wrapper_inner(*args, **kargs): - return func(*args, **kargs) - - return wrapper_inner - - if func is None: - return wrapper - else: - return func - - -if TORCH_VERSION == 'parrots': - from parrots.utils.tester import skip_no_elena -else: - - def skip_no_elena(func): - - def wrapper(*args, **kargs): - return func(*args, **kargs) - - return wrapper diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/parrots_wrapper.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/parrots_wrapper.py deleted file mode 100644 index 93c97640d..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/parrots_wrapper.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch - -TORCH_VERSION = torch.__version__ - - -def is_rocm_pytorch() -> bool: - is_rocm = False - if TORCH_VERSION != 'parrots': - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - return is_rocm - - -def _get_cuda_home(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import CUDA_HOME - else: - if is_rocm_pytorch(): - from torch.utils.cpp_extension import ROCM_HOME - CUDA_HOME = ROCM_HOME - else: - from torch.utils.cpp_extension import CUDA_HOME - return CUDA_HOME - - -def get_build_config(): - if TORCH_VERSION == 'parrots': - from parrots.config import get_build_info - return get_build_info() - else: - return torch.__config__.show() - - -def _get_conv(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin - else: - from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin - return _ConvNd, _ConvTransposeMixin - - -def _get_dataloader(): - if TORCH_VERSION == 'parrots': - from torch.utils.data import DataLoader, PoolDataLoader - else: - from torch.utils.data import DataLoader - PoolDataLoader = DataLoader - return DataLoader, PoolDataLoader - - -def _get_extension(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import BuildExtension, Extension - CppExtension = partial(Extension, cuda=False) - CUDAExtension = partial(Extension, cuda=True) - else: - from torch.utils.cpp_extension import (BuildExtension, CppExtension, - CUDAExtension) - return BuildExtension, CppExtension, CUDAExtension - - -def _get_pool(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - else: - from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd - - -def _get_norm(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm2d - else: - from torch.nn.modules.instancenorm import _InstanceNorm - from torch.nn.modules.batchnorm import _BatchNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm - return _BatchNorm, _InstanceNorm, SyncBatchNorm_ - - -_ConvNd, _ConvTransposeMixin = _get_conv() -DataLoader, PoolDataLoader = _get_dataloader() -BuildExtension, CppExtension, CUDAExtension = _get_extension() -_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() -_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() - - -class SyncBatchNorm(SyncBatchNorm_): - - def _check_input_dim(self, input): - if TORCH_VERSION == 'parrots': - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input (got {input.dim()}D input)') - else: - super()._check_input_dim(input) diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/path.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/path.py deleted file mode 100644 index 7dab4b304..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/path.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -from pathlib import Path - -from .misc import is_str - - -def is_filepath(x): - return is_str(x) or isinstance(x, Path) - - -def fopen(filepath, *args, **kwargs): - if is_str(filepath): - return open(filepath, *args, **kwargs) - elif isinstance(filepath, Path): - return filepath.open(*args, **kwargs) - raise ValueError('`filepath` should be a string or a Path') - - -def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): - if not osp.isfile(filename): - raise FileNotFoundError(msg_tmpl.format(filename)) - - -def mkdir_or_exist(dir_name, mode=0o777): - if dir_name == '': - return - dir_name = osp.expanduser(dir_name) - os.makedirs(dir_name, mode=mode, exist_ok=True) - - -def symlink(src, dst, overwrite=True, **kwargs): - if os.path.lexists(dst) and overwrite: - os.remove(dst) - os.symlink(src, dst, **kwargs) - - -def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): - """Scan a directory to find the interested files. - - Args: - dir_path (str | obj:`Path`): Path of the directory. - suffix (str | tuple(str), optional): File suffix that we are - interested in. Default: None. - recursive (bool, optional): If set to True, recursively scan the - directory. Default: False. - case_sensitive (bool, optional) : If set to False, ignore the case of - suffix. Default: True. - - Returns: - A generator for all the interested files with relative paths. - """ - if isinstance(dir_path, (str, Path)): - dir_path = str(dir_path) - else: - raise TypeError('"dir_path" must be a string or Path object') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('"suffix" must be a string or tuple of strings') - - if suffix is not None and not case_sensitive: - suffix = suffix.lower() if isinstance(suffix, str) else tuple( - item.lower() for item in suffix) - - root = dir_path - - def _scandir(dir_path, suffix, recursive, case_sensitive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - _rel_path = rel_path if case_sensitive else rel_path.lower() - if suffix is None or _rel_path.endswith(suffix): - yield rel_path - elif recursive and os.path.isdir(entry.path): - # scan recursively if entry.path is a directory - yield from _scandir(entry.path, suffix, recursive, - case_sensitive) - - return _scandir(dir_path, suffix, recursive, case_sensitive) - - -def find_vcs_root(path, markers=('.git', )): - """Finds the root directory (including itself) of specified markers. - - Args: - path (str): Path of directory or file. - markers (list[str], optional): List of file or directory names. - - Returns: - The directory contained one of the markers or None if not found. - """ - if osp.isfile(path): - path = osp.dirname(path) - - prev, cur = None, osp.abspath(osp.expanduser(path)) - while cur != prev: - if any(osp.exists(osp.join(cur, marker)) for marker in markers): - return cur - prev, cur = cur, osp.split(cur)[0] - return None diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/progressbar.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/progressbar.py deleted file mode 100644 index 0062f670d..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/progressbar.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from collections.abc import Iterable -from multiprocessing import Pool -from shutil import get_terminal_size - -from .timer import Timer - - -class ProgressBar: - """A progress bar which can print the progress.""" - - def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): - self.task_num = task_num - self.bar_width = bar_width - self.completed = 0 - self.file = file - if start: - self.start() - - @property - def terminal_width(self): - width, _ = get_terminal_size() - return width - - def start(self): - if self.task_num > 0: - self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' - 'elapsed: 0s, ETA:') - else: - self.file.write('completed: 0, elapsed: 0s') - self.file.flush() - self.timer = Timer() - - def update(self, num_tasks=1): - assert num_tasks > 0 - self.completed += num_tasks - elapsed = self.timer.since_start() - if elapsed > 0: - fps = self.completed / elapsed - else: - fps = float('inf') - if self.task_num > 0: - percentage = self.completed / float(self.task_num) - eta = int(elapsed * (1 - percentage) / percentage + 0.5) - msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ - f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ - f'ETA: {eta:5}s' - - bar_width = min(self.bar_width, - int(self.terminal_width - len(msg)) + 2, - int(self.terminal_width * 0.6)) - bar_width = max(2, bar_width) - mark_width = int(bar_width * percentage) - bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) - self.file.write(msg.format(bar_chars)) - else: - self.file.write( - f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' - f' {fps:.1f} tasks/s') - self.file.flush() - - -def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): - """Track the progress of tasks execution with a progress bar. - - Tasks are done with a simple for-loop. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - results = [] - for task in tasks: - results.append(func(task, **kwargs)) - prog_bar.update() - prog_bar.file.write('\n') - return results - - -def init_pool(process_num, initializer=None, initargs=None): - if initializer is None: - return Pool(process_num) - elif initargs is None: - return Pool(process_num, initializer) - else: - if not isinstance(initargs, tuple): - raise TypeError('"initargs" must be a tuple') - return Pool(process_num, initializer, initargs) - - -def track_parallel_progress(func, - tasks, - nproc, - initializer=None, - initargs=None, - bar_width=50, - chunksize=1, - skip_first=False, - keep_order=True, - file=sys.stdout): - """Track the progress of parallel task execution with a progress bar. - - The built-in :mod:`multiprocessing` module is used for process pools and - tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - nproc (int): Process (worker) number. - initializer (None or callable): Refer to :class:`multiprocessing.Pool` - for details. - initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for - details. - chunksize (int): Refer to :class:`multiprocessing.Pool` for details. - bar_width (int): Width of progress bar. - skip_first (bool): Whether to skip the first sample for each worker - when estimating fps, since the initialization step may takes - longer. - keep_order (bool): If True, :func:`Pool.imap` is used, otherwise - :func:`Pool.imap_unordered` is used. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - pool = init_pool(nproc, initializer, initargs) - start = not skip_first - task_num -= nproc * chunksize * int(skip_first) - prog_bar = ProgressBar(task_num, bar_width, start, file=file) - results = [] - if keep_order: - gen = pool.imap(func, tasks, chunksize) - else: - gen = pool.imap_unordered(func, tasks, chunksize) - for result in gen: - results.append(result) - if skip_first: - if len(results) < nproc * chunksize: - continue - elif len(results) == nproc * chunksize: - prog_bar.start() - continue - prog_bar.update() - prog_bar.file.write('\n') - pool.close() - pool.join() - return results - - -def track_iter_progress(tasks, bar_width=50, file=sys.stdout): - """Track the progress of tasks iteration or enumeration with a progress - bar. - - Tasks are yielded with a simple for-loop. - - Args: - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Yields: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - for task in tasks: - yield task - prog_bar.update() - prog_bar.file.write('\n') diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/registry.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/registry.py deleted file mode 100644 index fa9df39bc..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/registry.py +++ /dev/null @@ -1,315 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import warnings -from functools import partial - -from .misc import is_seq_of - - -def build_from_cfg(cfg, registry, default_args=None): - """Build a module from config dict. - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - try: - return obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{obj_cls.__name__}: {e}') - - -class Registry: - """A registry to map strings to classes. - - Registered object could be built from registry. - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = MODELS.build(dict(type='ResNet')) - - Please refer to - https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for - advanced usage. - - Args: - name (str): Registry name. - build_func(func, optional): Build function to construct instance from - Registry, func:`build_from_cfg` is used if neither ``parent`` or - ``build_func`` is specified. If ``parent`` is specified and - ``build_func`` is not given, ``build_func`` will be inherited - from ``parent``. Default: None. - parent (Registry, optional): Parent registry. The class registered in - children registry could be built from parent. Default: None. - scope (str, optional): The scope of registry. It is the key to search - for children registry. If not specified, scope will be the name of - the package where class is defined, e.g. mmdet, mmcls, mmseg. - Default: None. - """ - - def __init__(self, name, build_func=None, parent=None, scope=None): - self._name = name - self._module_dict = dict() - self._children = dict() - self._scope = self.infer_scope() if scope is None else scope - - # self.build_func will be set with the following priority: - # 1. build_func - # 2. parent.build_func - # 3. build_from_cfg - if build_func is None: - if parent is not None: - self.build_func = parent.build_func - else: - self.build_func = build_from_cfg - else: - self.build_func = build_func - if parent is not None: - assert isinstance(parent, Registry) - parent._add_children(self) - self.parent = parent - else: - self.parent = None - - def __len__(self): - return len(self._module_dict) - - def __contains__(self, key): - return self.get(key) is not None - - def __repr__(self): - format_str = self.__class__.__name__ + \ - f'(name={self._name}, ' \ - f'items={self._module_dict})' - return format_str - - @staticmethod - def infer_scope(): - """Infer the scope of registry. - - The name of the package where registry is defined will be returned. - - Example: - # in mmdet/models/backbone/resnet.py - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - The scope of ``ResNet`` will be ``mmdet``. - - - Returns: - scope (str): The inferred scope name. - """ - # inspect.stack() trace where this function is called, the index-2 - # indicates the frame where `infer_scope()` is called - filename = inspect.getmodule(inspect.stack()[2][0]).__name__ - split_filename = filename.split('.') - return split_filename[0] - - @staticmethod - def split_scope_key(key): - """Split scope and key. - - The first scope will be split from key. - - Examples: - >>> Registry.split_scope_key('mmdet.ResNet') - 'mmdet', 'ResNet' - >>> Registry.split_scope_key('ResNet') - None, 'ResNet' - - Return: - scope (str, None): The first scope. - key (str): The remaining key. - """ - split_index = key.find('.') - if split_index != -1: - return key[:split_index], key[split_index + 1:] - else: - return None, key - - @property - def name(self): - return self._name - - @property - def scope(self): - return self._scope - - @property - def module_dict(self): - return self._module_dict - - @property - def children(self): - return self._children - - def get(self, key): - """Get the registry record. - - Args: - key (str): The class name in string format. - - Returns: - class: The corresponding class. - """ - scope, real_key = self.split_scope_key(key) - if scope is None or scope == self._scope: - # get from self - if real_key in self._module_dict: - return self._module_dict[real_key] - else: - # get from self._children - if scope in self._children: - return self._children[scope].get(real_key) - else: - # goto root - parent = self.parent - while parent.parent is not None: - parent = parent.parent - return parent.get(key) - - def build(self, *args, **kwargs): - return self.build_func(*args, **kwargs, registry=self) - - def _add_children(self, registry): - """Add children for a registry. - - The ``registry`` will be added as children based on its scope. - The parent registry could build objects from children registry. - - Example: - >>> models = Registry('models') - >>> mmdet_models = Registry('models', parent=models) - >>> @mmdet_models.register_module() - >>> class ResNet: - >>> pass - >>> resnet = models.build(dict(type='mmdet.ResNet')) - """ - - assert isinstance(registry, Registry) - assert registry.scope is not None - assert registry.scope not in self.children, \ - f'scope {registry.scope} exists in {self.name} registry' - self.children[registry.scope] = registry - - def _register_module(self, module_class, module_name=None, force=False): - if not inspect.isclass(module_class): - raise TypeError('module must be a class, ' - f'but got {type(module_class)}') - - if module_name is None: - module_name = module_class.__name__ - if isinstance(module_name, str): - module_name = [module_name] - for name in module_name: - if not force and name in self._module_dict: - raise KeyError(f'{name} is already registered ' - f'in {self.name}') - self._module_dict[name] = module_class - - def deprecated_register_module(self, cls=None, force=False): - warnings.warn( - 'The old API of register_module(module, force=False) ' - 'is deprecated and will be removed, please use the new API ' - 'register_module(name=None, force=False, module=None) instead.') - if cls is None: - return partial(self.deprecated_register_module, force=force) - self._register_module(cls, force=force) - return cls - - def register_module(self, name=None, force=False, module=None): - """Register a module. - - A record will be added to `self._module_dict`, whose key is the class - name or the specified name, and value is the class itself. - It can be used as a decorator or a normal function. - - Example: - >>> backbones = Registry('backbone') - >>> @backbones.register_module() - >>> class ResNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> @backbones.register_module(name='mnet') - >>> class MobileNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> class ResNet: - >>> pass - >>> backbones.register_module(ResNet) - - Args: - name (str | None): The module name to be registered. If not - specified, the class name will be used. - force (bool, optional): Whether to override an existing class with - the same name. Default: False. - module (type): Module class to be registered. - """ - if not isinstance(force, bool): - raise TypeError(f'force must be a boolean, but got {type(force)}') - # NOTE: This is a walkaround to be compatible with the old api, - # while it may introduce unexpected bugs. - if isinstance(name, type): - return self.deprecated_register_module(name, force=force) - - # raise the error ahead of time - if not (name is None or isinstance(name, str) or is_seq_of(name, str)): - raise TypeError( - 'name must be either of None, an instance of str or a sequence' - f' of str, but got {type(name)}') - - # use it as a normal method: x.register_module(module=SomeClass) - if module is not None: - self._register_module( - module_class=module, module_name=name, force=force) - return module - - # use it as a decorator: @x.register_module() - def _register(cls): - self._register_module( - module_class=cls, module_name=name, force=force) - return cls - - return _register diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/testing.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/testing.py deleted file mode 100644 index a27f936da..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/testing.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) Open-MMLab. -import sys -from collections.abc import Iterable -from runpy import run_path -from shlex import split -from typing import Any, Dict, List -from unittest.mock import patch - - -def check_python_script(cmd): - """Run the python cmd script with `__main__`. The difference between - `os.system` is that, this function exectues code in the current process, so - that it can be tracked by coverage tools. Currently it supports two forms: - - - ./tests/data/scripts/hello.py zz - - python tests/data/scripts/hello.py zz - """ - args = split(cmd) - if args[0] == 'python': - args = args[1:] - with patch.object(sys, 'argv', args): - run_path(args[0], run_name='__main__') - - -def _any(judge_result): - """Since built-in ``any`` works only when the element of iterable is not - iterable, implement the function.""" - if not isinstance(judge_result, Iterable): - return judge_result - - try: - for element in judge_result: - if _any(element): - return True - except TypeError: - # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) - if judge_result: - return True - return False - - -def assert_dict_contains_subset(dict_obj: Dict[Any, Any], - expected_subset: Dict[Any, Any]) -> bool: - """Check if the dict_obj contains the expected_subset. - - Args: - dict_obj (Dict[Any, Any]): Dict object to be checked. - expected_subset (Dict[Any, Any]): Subset expected to be contained in - dict_obj. - - Returns: - bool: Whether the dict_obj contains the expected_subset. - """ - - for key, value in expected_subset.items(): - if key not in dict_obj.keys() or _any(dict_obj[key] != value): - return False - return True - - -def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: - """Check if attribute of class object is correct. - - Args: - obj (object): Class object to be checked. - expected_attrs (Dict[str, Any]): Dict of the expected attrs. - - Returns: - bool: Whether the attribute of class object is correct. - """ - for attr, value in expected_attrs.items(): - if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): - return False - return True - - -def assert_dict_has_keys(obj: Dict[str, Any], - expected_keys: List[str]) -> bool: - """Check if the obj has all the expected_keys. - - Args: - obj (Dict[str, Any]): Object to be checked. - expected_keys (List[str]): Keys expected to contained in the keys of - the obj. - - Returns: - bool: Whether the obj has the expected keys. - """ - return set(expected_keys).issubset(set(obj.keys())) - - -def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: - """Check if target_keys is equal to result_keys. - - Args: - result_keys (List[str]): Result keys to be checked. - target_keys (List[str]): Target keys to be checked. - - Returns: - bool: Whether target_keys is equal to result_keys. - """ - return set(result_keys) == set(target_keys) - - -def assert_is_norm_layer(module) -> bool: - """Check if the module is a norm layer. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the module is a norm layer. - """ - from .parrots_wrapper import _BatchNorm, _InstanceNorm - from torch.nn import GroupNorm, LayerNorm - norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) - return isinstance(module, norm_layer_candidates) - - -def assert_params_all_zeros(module) -> bool: - """Check if the parameters of the module is all zeros. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the parameters of the module is all zeros. - """ - weight_data = module.weight.data - is_weight_zero = weight_data.allclose( - weight_data.new_zeros(weight_data.size())) - - if hasattr(module, 'bias') and module.bias is not None: - bias_data = module.bias.data - is_bias_zero = bias_data.allclose( - bias_data.new_zeros(bias_data.size())) - else: - is_bias_zero = True - - return is_weight_zero and is_bias_zero diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/timer.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/timer.py deleted file mode 100644 index 66d4a78a8..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/timer.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from time import time - - -class TimerError(Exception): - - def __init__(self, message): - self.message = message - super(TimerError, self).__init__(message) - - -class Timer: - """A flexible Timer class. - - :Example: - - >>> import time - >>> import mmcv - >>> with mmcv.Timer(): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - 1.000 - >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - it takes 1.0 seconds - >>> timer = mmcv.Timer() - >>> time.sleep(0.5) - >>> print(timer.since_start()) - 0.500 - >>> time.sleep(0.5) - >>> print(timer.since_last_check()) - 0.500 - >>> print(timer.since_start()) - 1.000 - """ - - def __init__(self, start=True, print_tmpl=None): - self._is_running = False - self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' - if start: - self.start() - - @property - def is_running(self): - """bool: indicate whether the timer is running""" - return self._is_running - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - print(self.print_tmpl.format(self.since_last_check())) - self._is_running = False - - def start(self): - """Start the timer.""" - if not self._is_running: - self._t_start = time() - self._is_running = True - self._t_last = time() - - def since_start(self): - """Total time since the timer is started. - - Returns (float): Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - self._t_last = time() - return self._t_last - self._t_start - - def since_last_check(self): - """Time since the last checking. - - Either :func:`since_start` or :func:`since_last_check` is a checking - operation. - - Returns (float): Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - dur = time() - self._t_last - self._t_last = time() - return dur - - -_g_timers = {} # global timers - - -def check_time(timer_id): - """Add check points in a single line. - - This method is suitable for running a task on a list of items. A timer will - be registered when the method is called for the first time. - - :Example: - - >>> import time - >>> import mmcv - >>> for i in range(1, 6): - >>> # simulate a code block - >>> time.sleep(i) - >>> mmcv.check_time('task1') - 2.000 - 3.000 - 4.000 - 5.000 - - Args: - timer_id (str): Timer identifier. - """ - if timer_id not in _g_timers: - _g_timers[timer_id] = Timer() - return 0 - else: - return _g_timers[timer_id].since_last_check() diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/trace.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/trace.py deleted file mode 100644 index 8e49bfd38..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/trace.py +++ /dev/null @@ -1,23 +0,0 @@ -import warnings - -import torch - -from mmcv.utils import digit_version - - -def is_jit_tracing() -> bool: - if (torch.__version__ != 'parrots' - and digit_version(torch.__version__) >= digit_version('1.6.0')): - on_trace = torch.jit.is_tracing() - # In PyTorch 1.6, torch.jit.is_tracing has a bug. - # Refers to https://github.com/pytorch/pytorch/issues/42448 - if isinstance(on_trace, bool): - return on_trace - else: - return torch._C._is_tracing() - else: - warnings.warn( - 'torch.jit.is_tracing is only supported after v1.6.0. ' - 'Therefore is_tracing returns False automatically. Please ' - 'set on_trace manually if you are using trace.', UserWarning) - return False diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/utils/version_utils.py b/cv/instance_segmentation/solo/pytorch/mmcv/utils/version_utils.py deleted file mode 100644 index 963c45a2e..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/utils/version_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import subprocess -import warnings - -from packaging.version import parse - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - assert 'parrots' not in version_str - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - -def get_git_hash(fallback='unknown', digits=None): - """Get the git hash of the current repo. - - Args: - fallback (str, optional): The fallback string when git hash is - unavailable. Defaults to 'unknown'. - digits (int, optional): kept digits of the hash. Defaults to None, - meaning all digits are kept. - - Returns: - str: Git commit hash. - """ - - if digits is not None and not isinstance(digits, int): - raise TypeError('digits must be None or an integer') - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - sha = out.strip().decode('ascii') - if digits is not None: - sha = sha[:digits] - except OSError: - sha = fallback - - return sha diff --git a/cv/instance_segmentation/solo/pytorch/mmcv/version.py b/cv/instance_segmentation/solo/pytorch/mmcv/version.py deleted file mode 100644 index 1cce4e50b..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmcv/version.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -__version__ = '1.3.17' - - -def parse_version_info(version_str: str, length: int = 4) -> tuple: - """Parse a version string into a tuple. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int | str]: The version info, e.g., "1.3.0" is parsed into - (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into - (2, 0, 0, 0, 'rc', 1) (when length is set to 4). - """ - from packaging.version import parse - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - release.extend(list(version.pre)) - elif version.is_postrelease: - release.extend(list(version.post)) - else: - release.extend([0, 0]) - return tuple(release) - - -version_info = tuple(int(x) for x in __version__.split('.')[:3]) - -__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/__init__.py deleted file mode 100644 index 1f8ee169b..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - -from .version import __version__, short_version - - -def digit_version(version_str): - digit_version = [] - for x in version_str.split('.'): - if x.isdigit(): - digit_version.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - digit_version.append(int(patch_version[0]) - 1) - digit_version.append(int(patch_version[1])) - return digit_version - - -mmcv_minimum_version = '1.3.17' -mmcv_maximum_version = '1.6.0' -mmcv_version = digit_version(mmcv.__version__) - - -assert (mmcv_version >= digit_version(mmcv_minimum_version) - and mmcv_version <= digit_version(mmcv_maximum_version)), \ - f'MMCV=={mmcv.__version__} is used but incompatible. ' \ - f'Please install mmcv>={mmcv_minimum_version}, <={mmcv_maximum_version}.' - -__all__ = ['__version__', 'short_version'] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/apis/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/apis/__init__.py deleted file mode 100644 index a865e942a..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/apis/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .inference import (async_inference_detector, inference_detector, - init_detector, show_result_pyplot) -from .test import multi_gpu_test, single_gpu_test -from .train import (get_root_logger, init_random_seed, set_random_seed, - train_detector) - -__all__ = [ - 'get_root_logger', 'set_random_seed', 'train_detector', 'init_detector', - 'async_inference_detector', 'inference_detector', 'show_result_pyplot', - 'multi_gpu_test', 'single_gpu_test', 'init_random_seed' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/apis/inference.py b/cv/instance_segmentation/solo/pytorch/mmdet/apis/inference.py deleted file mode 100644 index 795fce518..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/apis/inference.py +++ /dev/null @@ -1,251 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from pathlib import Path - -import mmcv -import numpy as np -import torch -from mmcv.ops import RoIPool -from mmcv.parallel import collate, scatter -from mmcv.runner import load_checkpoint - -from mmdet.core import get_classes -from mmdet.datasets import replace_ImageToTensor -from mmdet.datasets.pipelines import Compose -from mmdet.models import build_detector - - -def init_detector(config, checkpoint=None, device='cuda:0', cfg_options=None): - """Initialize a detector from config file. - - Args: - config (str, :obj:`Path`, or :obj:`mmcv.Config`): Config file path, - :obj:`Path`, or the config object. - checkpoint (str, optional): Checkpoint path. If left as None, the model - will not load any weights. - cfg_options (dict): Options to override some settings in the used - config. - - Returns: - nn.Module: The constructed detector. - """ - if isinstance(config, (str, Path)): - config = mmcv.Config.fromfile(config) - elif not isinstance(config, mmcv.Config): - raise TypeError('config must be a filename or Config object, ' - f'but got {type(config)}') - if cfg_options is not None: - config.merge_from_dict(cfg_options) - if 'pretrained' in config.model: - config.model.pretrained = None - elif 'init_cfg' in config.model.backbone: - config.model.backbone.init_cfg = None - config.model.train_cfg = None - model = build_detector(config.model, test_cfg=config.get('test_cfg')) - if checkpoint is not None: - checkpoint = load_checkpoint(model, checkpoint, map_location='cpu') - if 'CLASSES' in checkpoint.get('meta', {}): - model.CLASSES = checkpoint['meta']['CLASSES'] - else: - warnings.simplefilter('once') - warnings.warn('Class names are not saved in the checkpoint\'s ' - 'meta data, use COCO classes by default.') - model.CLASSES = get_classes('coco') - model.cfg = config # save the config in the model for convenience - model.to(device) - model.eval() - return model - - -class LoadImage: - """Deprecated. - - A simple pipeline to load image. - """ - - def __call__(self, results): - """Call function to load images into results. - - Args: - results (dict): A result dict contains the file name - of the image to be read. - Returns: - dict: ``results`` will be returned containing loaded image. - """ - warnings.simplefilter('once') - warnings.warn('`LoadImage` is deprecated and will be removed in ' - 'future releases. You may use `LoadImageFromWebcam` ' - 'from `mmdet.datasets.pipelines.` instead.') - if isinstance(results['img'], str): - results['filename'] = results['img'] - results['ori_filename'] = results['img'] - else: - results['filename'] = None - results['ori_filename'] = None - img = mmcv.imread(results['img']) - results['img'] = img - results['img_fields'] = ['img'] - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - return results - - -def inference_detector(model, imgs): - """Inference image(s) with the detector. - - Args: - model (nn.Module): The loaded detector. - imgs (str/ndarray or list[str/ndarray] or tuple[str/ndarray]): - Either image files or loaded images. - - Returns: - If imgs is a list or tuple, the same length list type results - will be returned, otherwise return the detection results directly. - """ - - if isinstance(imgs, (list, tuple)): - is_batch = True - else: - imgs = [imgs] - is_batch = False - - cfg = model.cfg - device = next(model.parameters()).device # model device - - if isinstance(imgs[0], np.ndarray): - cfg = cfg.copy() - # set loading pipeline type - cfg.data.test.pipeline[0].type = 'LoadImageFromWebcam' - - cfg.data.test.pipeline = replace_ImageToTensor(cfg.data.test.pipeline) - test_pipeline = Compose(cfg.data.test.pipeline) - - datas = [] - for img in imgs: - # prepare data - if isinstance(img, np.ndarray): - # directly add img - data = dict(img=img) - else: - # add information into dict - data = dict(img_info=dict(filename=img), img_prefix=None) - # build the data pipeline - data = test_pipeline(data) - datas.append(data) - - data = collate(datas, samples_per_gpu=len(imgs)) - # just get the actual data from DataContainer - data['img_metas'] = [img_metas.data[0] for img_metas in data['img_metas']] - data['img'] = [img.data[0] for img in data['img']] - if next(model.parameters()).is_cuda: - # scatter to specified GPU - data = scatter(data, [device])[0] - else: - for m in model.modules(): - assert not isinstance( - m, RoIPool - ), 'CPU inference with RoIPool is not supported currently.' - - # forward the model - with torch.no_grad(): - results = model(return_loss=False, rescale=True, **data) - - if not is_batch: - return results[0] - else: - return results - - -async def async_inference_detector(model, imgs): - """Async inference image(s) with the detector. - - Args: - model (nn.Module): The loaded detector. - img (str | ndarray): Either image files or loaded images. - - Returns: - Awaitable detection results. - """ - if not isinstance(imgs, (list, tuple)): - imgs = [imgs] - - cfg = model.cfg - device = next(model.parameters()).device # model device - - if isinstance(imgs[0], np.ndarray): - cfg = cfg.copy() - # set loading pipeline type - cfg.data.test.pipeline[0].type = 'LoadImageFromWebcam' - - cfg.data.test.pipeline = replace_ImageToTensor(cfg.data.test.pipeline) - test_pipeline = Compose(cfg.data.test.pipeline) - - datas = [] - for img in imgs: - # prepare data - if isinstance(img, np.ndarray): - # directly add img - data = dict(img=img) - else: - # add information into dict - data = dict(img_info=dict(filename=img), img_prefix=None) - # build the data pipeline - data = test_pipeline(data) - datas.append(data) - - data = collate(datas, samples_per_gpu=len(imgs)) - # just get the actual data from DataContainer - data['img_metas'] = [img_metas.data[0] for img_metas in data['img_metas']] - data['img'] = [img.data[0] for img in data['img']] - if next(model.parameters()).is_cuda: - # scatter to specified GPU - data = scatter(data, [device])[0] - else: - for m in model.modules(): - assert not isinstance( - m, RoIPool - ), 'CPU inference with RoIPool is not supported currently.' - - # We don't restore `torch.is_grad_enabled()` value during concurrent - # inference since execution can overlap - torch.set_grad_enabled(False) - results = await model.aforward_test(rescale=True, **data) - return results - - -def show_result_pyplot(model, - img, - result, - score_thr=0.3, - title='result', - wait_time=0, - palette=None, - out_file=None): - """Visualize the detection results on the image. - - Args: - model (nn.Module): The loaded detector. - img (str or np.ndarray): Image filename or loaded image. - result (tuple[list] or list): The detection result, can be either - (bbox, segm) or just bbox. - score_thr (float): The threshold to visualize the bboxes and masks. - title (str): Title of the pyplot figure. - wait_time (float): Value of waitKey param. Default: 0. - palette (str or tuple(int) or :obj:`Color`): Color. - The tuple of color should be in BGR order. - out_file (str or None): The path to write the image. - Default: None. - """ - if hasattr(model, 'module'): - model = model.module - model.show_result( - img, - result, - score_thr=score_thr, - show=True, - wait_time=wait_time, - win_name=title, - bbox_color=palette, - text_color=(200, 200, 200), - mask_color=palette, - out_file=out_file) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/apis/test.py b/cv/instance_segmentation/solo/pytorch/mmdet/apis/test.py deleted file mode 100644 index 973d3623d..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/apis/test.py +++ /dev/null @@ -1,209 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import mmcv -import torch -import torch.distributed as dist -from mmcv.image import tensor2imgs -from mmcv.runner import get_dist_info - -from mmdet.core import encode_mask_results - - -def single_gpu_test(model, - data_loader, - show=False, - out_dir=None, - show_score_thr=0.3): - model.eval() - results = [] - dataset = data_loader.dataset - PALETTE = getattr(dataset, 'PALETTE', None) - prog_bar = mmcv.ProgressBar(len(dataset)) - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - - batch_size = len(result) - if show or out_dir: - if batch_size == 1 and isinstance(data['img'][0], torch.Tensor): - img_tensor = data['img'][0] - else: - img_tensor = data['img'][0].data[0] - img_metas = data['img_metas'][0].data[0] - imgs = tensor2imgs(img_tensor, **img_metas[0]['img_norm_cfg']) - assert len(imgs) == len(img_metas) - - for i, (img, img_meta) in enumerate(zip(imgs, img_metas)): - h, w, _ = img_meta['img_shape'] - img_show = img[:h, :w, :] - - ori_h, ori_w = img_meta['ori_shape'][:-1] - img_show = mmcv.imresize(img_show, (ori_w, ori_h)) - - if out_dir: - out_file = osp.join(out_dir, img_meta['ori_filename']) - else: - out_file = None - - model.module.show_result( - img_show, - result[i], - bbox_color=PALETTE, - text_color=PALETTE, - mask_color=PALETTE, - show=show, - out_file=out_file, - score_thr=show_score_thr) - - # encode mask results - if isinstance(result[0], tuple): - result = [(bbox_results, encode_mask_results(mask_results)) - for bbox_results, mask_results in result] - # This logic is only used in panoptic segmentation test. - elif isinstance(result[0], dict) and 'ins_results' in result[0]: - for j in range(len(result)): - bbox_results, mask_results = result[j]['ins_results'] - result[j]['ins_results'] = (bbox_results, - encode_mask_results(mask_results)) - - results.extend(result) - - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' - it encodes results to gpu tensors and use gpu communication for results - collection. On cpu mode it saves the results on different gpus to 'tmpdir' - and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - # encode mask results - if isinstance(result[0], tuple): - result = [(bbox_results, encode_mask_results(mask_results)) - for bbox_results, mask_results in result] - # This logic is only used in panoptic segmentation test. - elif isinstance(result[0], dict) and 'ins_results' in result[0]: - for j in range(len(result)): - bbox_results, mask_results = result[j]['ins_results'] - result[j]['ins_results'] = ( - bbox_results, encode_mask_results(mask_results)) - - results.extend(result) - - if rank == 0: - batch_size = len(result) - for _ in range(batch_size * world_size): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_list.append(mmcv.load(part_file)) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_list.append( - pickle.loads(recv[:shape[0]].cpu().numpy().tobytes())) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/apis/train.py b/cv/instance_segmentation/solo/pytorch/mmdet/apis/train.py deleted file mode 100644 index ca7633180..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/apis/train.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random - -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import (DistSamplerSeedHook, EpochBasedRunner, - Fp16OptimizerHook, OptimizerHook, build_runner, - get_dist_info) - -from mmdet.core import DistEvalHook, EvalHook, build_optimizer -from mmdet.datasets import (build_dataloader, build_dataset, - replace_ImageToTensor) -from mmdet.utils import (build_ddp, build_dp, compat_cfg, - find_latest_checkpoint, get_root_logger) - - -def init_random_seed(seed=None, device='cuda'): - """Initialize random seed. - - If the seed is not set, the seed will be automatically randomized, - and then broadcast to all processes to prevent some potential bugs. - - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - - Returns: - int: Seed to be used. - """ - if seed is not None: - return seed - - # Make sure all ranks share the same random seed to prevent - # some potential bugs. Please refer to - # https://github.com/open-mmlab/mmdetection/issues/6339 - rank, world_size = get_dist_info() - seed = np.random.randint(2**31) - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() - - -def set_random_seed(seed, deterministic=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - """ - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - - -def auto_scale_lr(cfg, distributed, logger): - """Automatically scaling LR according to GPU number and sample per GPU. - - Args: - cfg (config): Training config. - distributed (bool): Using distributed or not. - logger (logging.Logger): Logger. - """ - # Get flag from config - if ('auto_scale_lr' not in cfg) or \ - (not cfg.auto_scale_lr.get('enable', False)): - logger.info('Automatic scaling of learning rate (LR)' - ' has been disabled.') - return - - # Get base batch size from config - base_batch_size = cfg.auto_scale_lr.get('base_batch_size', None) - if base_batch_size is None: - return - - # Get gpu number - if distributed: - _, world_size = get_dist_info() - num_gpus = len(range(world_size)) - else: - num_gpus = len(cfg.gpu_ids) - - # calculate the batch size - samples_per_gpu = cfg.data.train_dataloader.samples_per_gpu - batch_size = num_gpus * samples_per_gpu - logger.info(f'Training with {num_gpus} GPU(s) with {samples_per_gpu} ' - f'samples per GPU. The total batch size is {batch_size}.') - - if batch_size != base_batch_size: - # scale LR with - # [linear scaling rule](https://arxiv.org/abs/1706.02677) - scaled_lr = (batch_size / base_batch_size) * cfg.optimizer.lr - logger.info('LR has been automatically scaled ' - f'from {cfg.optimizer.lr} to {scaled_lr}') - cfg.optimizer.lr = scaled_lr - else: - logger.info('The batch size match the ' - f'base batch size: {base_batch_size}, ' - f'will not scaling the LR ({cfg.optimizer.lr}).') - - -def train_detector(model, - dataset, - cfg, - distributed=False, - validate=False, - timestamp=None, - meta=None): - - cfg = compat_cfg(cfg) - logger = get_root_logger(log_level=cfg.log_level) - - # prepare data loaders - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - - runner_type = 'EpochBasedRunner' if 'runner' not in cfg else cfg.runner[ - 'type'] - - train_dataloader_default_args = dict( - samples_per_gpu=2, - workers_per_gpu=2, - # `num_gpus` will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - seed=cfg.seed, - runner_type=runner_type, - persistent_workers=False) - - train_loader_cfg = { - **train_dataloader_default_args, - **cfg.data.get('train_dataloader', {}) - } - - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus - if distributed: - find_unused_parameters = cfg.get('find_unused_parameters', False) - # Sets the `find_unused_parameters` parameter in - # torch.nn.parallel.DistributedDataParallel - model = build_ddp( - model, - cfg.device, - device_ids=[int(os.environ['LOCAL_RANK'])], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - else: - model = build_dp(model, cfg.device, device_ids=cfg.gpu_ids) - - # build optimizer - auto_scale_lr(cfg, distributed, logger) - optimizer = build_optimizer(model, cfg.optimizer) - - runner = build_runner( - cfg.runner, - default_args=dict( - model=model, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta)) - - # an ugly workaround to make .log and .log.json filenames the same - runner.timestamp = timestamp - - # fp16 setting - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - optimizer_config = Fp16OptimizerHook( - **cfg.optimizer_config, **fp16_cfg, distributed=distributed) - elif distributed and 'type' not in cfg.optimizer_config: - optimizer_config = OptimizerHook(**cfg.optimizer_config) - else: - optimizer_config = cfg.optimizer_config - - # register hooks - runner.register_training_hooks( - cfg.lr_config, - optimizer_config, - cfg.checkpoint_config, - cfg.log_config, - cfg.get('momentum_config', None), - custom_hooks_config=cfg.get('custom_hooks', None)) - - if distributed: - if isinstance(runner, EpochBasedRunner): - runner.register_hook(DistSamplerSeedHook()) - - # register eval hooks - if validate: - val_dataloader_default_args = dict( - samples_per_gpu=1, - workers_per_gpu=2, - dist=distributed, - shuffle=False, - persistent_workers=False) - - val_dataloader_args = { - **val_dataloader_default_args, - **cfg.data.get('val_dataloader', {}) - } - # Support batch_size > 1 in validation - - if val_dataloader_args['samples_per_gpu'] > 1: - # Replace 'ImageToTensor' to 'DefaultFormatBundle' - cfg.data.val.pipeline = replace_ImageToTensor( - cfg.data.val.pipeline) - val_dataset = build_dataset(cfg.data.val, dict(test_mode=True)) - - val_dataloader = build_dataloader(val_dataset, **val_dataloader_args) - eval_cfg = cfg.get('evaluation', {}) - eval_cfg['by_epoch'] = cfg.runner['type'] != 'IterBasedRunner' - eval_hook = DistEvalHook if distributed else EvalHook - # In this PR (https://github.com/open-mmlab/mmcv/pull/1193), the - # priority of IterTimerHook has been modified from 'NORMAL' to 'LOW'. - runner.register_hook( - eval_hook(val_dataloader, **eval_cfg), priority='LOW') - - resume_from = None - if cfg.resume_from is None and cfg.get('auto_resume'): - resume_from = find_latest_checkpoint(cfg.work_dir) - if resume_from is not None: - cfg.resume_from = resume_from - - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/__init__.py deleted file mode 100644 index 2a6203879..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .anchor import * # noqa: F401, F403 -from .bbox import * # noqa: F401, F403 -from .data_structures import * # noqa: F401, F403 -from .evaluation import * # noqa: F401, F403 -from .hook import * # noqa: F401, F403 -from .mask import * # noqa: F401, F403 -from .optimizers import * # noqa: F401, F403 -from .post_processing import * # noqa: F401, F403 -from .utils import * # noqa: F401, F403 diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/__init__.py deleted file mode 100644 index fcc7e4af3..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .anchor_generator import (AnchorGenerator, LegacyAnchorGenerator, - YOLOAnchorGenerator) -from .builder import (ANCHOR_GENERATORS, PRIOR_GENERATORS, - build_anchor_generator, build_prior_generator) -from .point_generator import MlvlPointGenerator, PointGenerator -from .utils import anchor_inside_flags, calc_region, images_to_levels - -__all__ = [ - 'AnchorGenerator', 'LegacyAnchorGenerator', 'anchor_inside_flags', - 'PointGenerator', 'images_to_levels', 'calc_region', - 'build_anchor_generator', 'ANCHOR_GENERATORS', 'YOLOAnchorGenerator', - 'build_prior_generator', 'PRIOR_GENERATORS', 'MlvlPointGenerator' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/anchor_generator.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/anchor_generator.py deleted file mode 100644 index 20886fbda..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/anchor_generator.py +++ /dev/null @@ -1,866 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv -import numpy as np -import torch -from torch.nn.modules.utils import _pair - -from .builder import PRIOR_GENERATORS - - -@PRIOR_GENERATORS.register_module() -class AnchorGenerator: - """Standard anchor generator for 2D anchor-based detectors. - - Args: - strides (list[int] | list[tuple[int, int]]): Strides of anchors - in multiple feature levels in order (w, h). - ratios (list[float]): The list of ratios between the height and width - of anchors in a single level. - scales (list[int] | None): Anchor scales for anchors in a single level. - It cannot be set at the same time if `octave_base_scale` and - `scales_per_octave` are set. - base_sizes (list[int] | None): The basic sizes - of anchors in multiple levels. - If None is given, strides will be used as base_sizes. - (If strides are non square, the shortest stride is taken.) - scale_major (bool): Whether to multiply scales first when generating - base anchors. If true, the anchors in the same row will have the - same scales. By default it is True in V2.0 - octave_base_scale (int): The base scale of octave. - scales_per_octave (int): Number of scales for each octave. - `octave_base_scale` and `scales_per_octave` are usually used in - retinanet and the `scales` should be None when they are set. - centers (list[tuple[float, float]] | None): The centers of the anchor - relative to the feature grid center in multiple feature levels. - By default it is set to be None and not used. If a list of tuple of - float is given, they will be used to shift the centers of anchors. - center_offset (float): The offset of center in proportion to anchors' - width and height. By default it is 0 in V2.0. - - Examples: - >>> from mmdet.core import AnchorGenerator - >>> self = AnchorGenerator([16], [1.], [1.], [9]) - >>> all_anchors = self.grid_priors([(2, 2)], device='cpu') - >>> print(all_anchors) - [tensor([[-4.5000, -4.5000, 4.5000, 4.5000], - [11.5000, -4.5000, 20.5000, 4.5000], - [-4.5000, 11.5000, 4.5000, 20.5000], - [11.5000, 11.5000, 20.5000, 20.5000]])] - >>> self = AnchorGenerator([16, 32], [1.], [1.], [9, 18]) - >>> all_anchors = self.grid_priors([(2, 2), (1, 1)], device='cpu') - >>> print(all_anchors) - [tensor([[-4.5000, -4.5000, 4.5000, 4.5000], - [11.5000, -4.5000, 20.5000, 4.5000], - [-4.5000, 11.5000, 4.5000, 20.5000], - [11.5000, 11.5000, 20.5000, 20.5000]]), \ - tensor([[-9., -9., 9., 9.]])] - """ - - def __init__(self, - strides, - ratios, - scales=None, - base_sizes=None, - scale_major=True, - octave_base_scale=None, - scales_per_octave=None, - centers=None, - center_offset=0.): - # check center and center_offset - if center_offset != 0: - assert centers is None, 'center cannot be set when center_offset' \ - f'!=0, {centers} is given.' - if not (0 <= center_offset <= 1): - raise ValueError('center_offset should be in range [0, 1], ' - f'{center_offset} is given.') - if centers is not None: - assert len(centers) == len(strides), \ - 'The number of strides should be the same as centers, got ' \ - f'{strides} and {centers}' - - # calculate base sizes of anchors - self.strides = [_pair(stride) for stride in strides] - self.base_sizes = [min(stride) for stride in self.strides - ] if base_sizes is None else base_sizes - assert len(self.base_sizes) == len(self.strides), \ - 'The number of strides should be the same as base sizes, got ' \ - f'{self.strides} and {self.base_sizes}' - - # calculate scales of anchors - assert ((octave_base_scale is not None - and scales_per_octave is not None) ^ (scales is not None)), \ - 'scales and octave_base_scale with scales_per_octave cannot' \ - ' be set at the same time' - if scales is not None: - self.scales = torch.Tensor(scales) - elif octave_base_scale is not None and scales_per_octave is not None: - octave_scales = np.array( - [2**(i / scales_per_octave) for i in range(scales_per_octave)]) - scales = octave_scales * octave_base_scale - self.scales = torch.Tensor(scales) - else: - raise ValueError('Either scales or octave_base_scale with ' - 'scales_per_octave should be set') - - self.octave_base_scale = octave_base_scale - self.scales_per_octave = scales_per_octave - self.ratios = torch.Tensor(ratios) - self.scale_major = scale_major - self.centers = centers - self.center_offset = center_offset - self.base_anchors = self.gen_base_anchors() - - @property - def num_base_anchors(self): - """list[int]: total number of base anchors in a feature grid""" - return self.num_base_priors - - @property - def num_base_priors(self): - """list[int]: The number of priors (anchors) at a point - on the feature grid""" - return [base_anchors.size(0) for base_anchors in self.base_anchors] - - @property - def num_levels(self): - """int: number of feature levels that the generator will be applied""" - return len(self.strides) - - def gen_base_anchors(self): - """Generate base anchors. - - Returns: - list(torch.Tensor): Base anchors of a feature grid in multiple \ - feature levels. - """ - multi_level_base_anchors = [] - for i, base_size in enumerate(self.base_sizes): - center = None - if self.centers is not None: - center = self.centers[i] - multi_level_base_anchors.append( - self.gen_single_level_base_anchors( - base_size, - scales=self.scales, - ratios=self.ratios, - center=center)) - return multi_level_base_anchors - - def gen_single_level_base_anchors(self, - base_size, - scales, - ratios, - center=None): - """Generate base anchors of a single level. - - Args: - base_size (int | float): Basic size of an anchor. - scales (torch.Tensor): Scales of the anchor. - ratios (torch.Tensor): The ratio between between the height - and width of anchors in a single level. - center (tuple[float], optional): The center of the base anchor - related to a single feature grid. Defaults to None. - - Returns: - torch.Tensor: Anchors in a single-level feature maps. - """ - w = base_size - h = base_size - if center is None: - x_center = self.center_offset * w - y_center = self.center_offset * h - else: - x_center, y_center = center - - h_ratios = torch.sqrt(ratios) - w_ratios = 1 / h_ratios - if self.scale_major: - ws = (w * w_ratios[:, None] * scales[None, :]).view(-1) - hs = (h * h_ratios[:, None] * scales[None, :]).view(-1) - else: - ws = (w * scales[:, None] * w_ratios[None, :]).view(-1) - hs = (h * scales[:, None] * h_ratios[None, :]).view(-1) - - # use float anchor and the anchor's center is aligned with the - # pixel center - base_anchors = [ - x_center - 0.5 * ws, y_center - 0.5 * hs, x_center + 0.5 * ws, - y_center + 0.5 * hs - ] - base_anchors = torch.stack(base_anchors, dim=-1) - - return base_anchors - - def _meshgrid(self, x, y, row_major=True): - """Generate mesh grid of x and y. - - Args: - x (torch.Tensor): Grids of x dimension. - y (torch.Tensor): Grids of y dimension. - row_major (bool, optional): Whether to return y grids first. - Defaults to True. - - Returns: - tuple[torch.Tensor]: The mesh grids of x and y. - """ - # use shape instead of len to keep tracing while exporting to onnx - xx = x.repeat(y.shape[0]) - yy = y.view(-1, 1).repeat(1, x.shape[0]).view(-1) - if row_major: - return xx, yy - else: - return yy, xx - - def grid_priors(self, featmap_sizes, dtype=torch.float32, device='cuda'): - """Generate grid anchors in multiple feature levels. - - Args: - featmap_sizes (list[tuple]): List of feature map sizes in - multiple feature levels. - dtype (:obj:`torch.dtype`): Dtype of priors. - Default: torch.float32. - device (str): The device where the anchors will be put on. - - Return: - list[torch.Tensor]: Anchors in multiple feature levels. \ - The sizes of each tensor should be [N, 4], where \ - N = width * height * num_base_anchors, width and height \ - are the sizes of the corresponding feature level, \ - num_base_anchors is the number of anchors for that level. - """ - assert self.num_levels == len(featmap_sizes) - multi_level_anchors = [] - for i in range(self.num_levels): - anchors = self.single_level_grid_priors( - featmap_sizes[i], level_idx=i, dtype=dtype, device=device) - multi_level_anchors.append(anchors) - return multi_level_anchors - - def single_level_grid_priors(self, - featmap_size, - level_idx, - dtype=torch.float32, - device='cuda'): - """Generate grid anchors of a single level. - - Note: - This function is usually called by method ``self.grid_priors``. - - Args: - featmap_size (tuple[int]): Size of the feature maps. - level_idx (int): The index of corresponding feature map level. - dtype (obj:`torch.dtype`): Date type of points.Defaults to - ``torch.float32``. - device (str, optional): The device the tensor will be put on. - Defaults to 'cuda'. - - Returns: - torch.Tensor: Anchors in the overall feature maps. - """ - - base_anchors = self.base_anchors[level_idx].to(device).to(dtype) - feat_h, feat_w = featmap_size - stride_w, stride_h = self.strides[level_idx] - # First create Range with the default dtype, than convert to - # target `dtype` for onnx exporting. - shift_x = torch.arange(0, feat_w, device=device).to(dtype) * stride_w - shift_y = torch.arange(0, feat_h, device=device).to(dtype) * stride_h - - shift_xx, shift_yy = self._meshgrid(shift_x, shift_y) - shifts = torch.stack([shift_xx, shift_yy, shift_xx, shift_yy], dim=-1) - # first feat_w elements correspond to the first row of shifts - # add A anchors (1, A, 4) to K shifts (K, 1, 4) to get - # shifted anchors (K, A, 4), reshape to (K*A, 4) - - all_anchors = base_anchors[None, :, :] + shifts[:, None, :] - all_anchors = all_anchors.view(-1, 4) - # first A rows correspond to A anchors of (0, 0) in feature map, - # then (0, 1), (0, 2), ... - return all_anchors - - def sparse_priors(self, - prior_idxs, - featmap_size, - level_idx, - dtype=torch.float32, - device='cuda'): - """Generate sparse anchors according to the ``prior_idxs``. - - Args: - prior_idxs (Tensor): The index of corresponding anchors - in the feature map. - featmap_size (tuple[int]): feature map size arrange as (h, w). - level_idx (int): The level index of corresponding feature - map. - dtype (obj:`torch.dtype`): Date type of points.Defaults to - ``torch.float32``. - device (obj:`torch.device`): The device where the points is - located. - Returns: - Tensor: Anchor with shape (N, 4), N should be equal to - the length of ``prior_idxs``. - """ - - height, width = featmap_size - num_base_anchors = self.num_base_anchors[level_idx] - base_anchor_id = prior_idxs % num_base_anchors - x = (prior_idxs // - num_base_anchors) % width * self.strides[level_idx][0] - y = (prior_idxs // width // - num_base_anchors) % height * self.strides[level_idx][1] - priors = torch.stack([x, y, x, y], 1).to(dtype).to(device) + \ - self.base_anchors[level_idx][base_anchor_id, :].to(device) - - return priors - - def grid_anchors(self, featmap_sizes, device='cuda'): - """Generate grid anchors in multiple feature levels. - - Args: - featmap_sizes (list[tuple]): List of feature map sizes in - multiple feature levels. - device (str): Device where the anchors will be put on. - - Return: - list[torch.Tensor]: Anchors in multiple feature levels. \ - The sizes of each tensor should be [N, 4], where \ - N = width * height * num_base_anchors, width and height \ - are the sizes of the corresponding feature level, \ - num_base_anchors is the number of anchors for that level. - """ - warnings.warn('``grid_anchors`` would be deprecated soon. ' - 'Please use ``grid_priors`` ') - - assert self.num_levels == len(featmap_sizes) - multi_level_anchors = [] - for i in range(self.num_levels): - anchors = self.single_level_grid_anchors( - self.base_anchors[i].to(device), - featmap_sizes[i], - self.strides[i], - device=device) - multi_level_anchors.append(anchors) - return multi_level_anchors - - def single_level_grid_anchors(self, - base_anchors, - featmap_size, - stride=(16, 16), - device='cuda'): - """Generate grid anchors of a single level. - - Note: - This function is usually called by method ``self.grid_anchors``. - - Args: - base_anchors (torch.Tensor): The base anchors of a feature grid. - featmap_size (tuple[int]): Size of the feature maps. - stride (tuple[int], optional): Stride of the feature map in order - (w, h). Defaults to (16, 16). - device (str, optional): Device the tensor will be put on. - Defaults to 'cuda'. - - Returns: - torch.Tensor: Anchors in the overall feature maps. - """ - - warnings.warn( - '``single_level_grid_anchors`` would be deprecated soon. ' - 'Please use ``single_level_grid_priors`` ') - - # keep featmap_size as Tensor instead of int, so that we - # can convert to ONNX correctly - feat_h, feat_w = featmap_size - shift_x = torch.arange(0, feat_w, device=device) * stride[0] - shift_y = torch.arange(0, feat_h, device=device) * stride[1] - - shift_xx, shift_yy = self._meshgrid(shift_x, shift_y) - shifts = torch.stack([shift_xx, shift_yy, shift_xx, shift_yy], dim=-1) - shifts = shifts.type_as(base_anchors) - # first feat_w elements correspond to the first row of shifts - # add A anchors (1, A, 4) to K shifts (K, 1, 4) to get - # shifted anchors (K, A, 4), reshape to (K*A, 4) - - all_anchors = base_anchors[None, :, :] + shifts[:, None, :] - all_anchors = all_anchors.view(-1, 4) - # first A rows correspond to A anchors of (0, 0) in feature map, - # then (0, 1), (0, 2), ... - return all_anchors - - def valid_flags(self, featmap_sizes, pad_shape, device='cuda'): - """Generate valid flags of anchors in multiple feature levels. - - Args: - featmap_sizes (list(tuple)): List of feature map sizes in - multiple feature levels. - pad_shape (tuple): The padded shape of the image. - device (str): Device where the anchors will be put on. - - Return: - list(torch.Tensor): Valid flags of anchors in multiple levels. - """ - assert self.num_levels == len(featmap_sizes) - multi_level_flags = [] - for i in range(self.num_levels): - anchor_stride = self.strides[i] - feat_h, feat_w = featmap_sizes[i] - h, w = pad_shape[:2] - valid_feat_h = min(int(np.ceil(h / anchor_stride[1])), feat_h) - valid_feat_w = min(int(np.ceil(w / anchor_stride[0])), feat_w) - flags = self.single_level_valid_flags((feat_h, feat_w), - (valid_feat_h, valid_feat_w), - self.num_base_anchors[i], - device=device) - multi_level_flags.append(flags) - return multi_level_flags - - def single_level_valid_flags(self, - featmap_size, - valid_size, - num_base_anchors, - device='cuda'): - """Generate the valid flags of anchor in a single feature map. - - Args: - featmap_size (tuple[int]): The size of feature maps, arrange - as (h, w). - valid_size (tuple[int]): The valid size of the feature maps. - num_base_anchors (int): The number of base anchors. - device (str, optional): Device where the flags will be put on. - Defaults to 'cuda'. - - Returns: - torch.Tensor: The valid flags of each anchor in a single level \ - feature map. - """ - feat_h, feat_w = featmap_size - valid_h, valid_w = valid_size - assert valid_h <= feat_h and valid_w <= feat_w - valid_x = torch.zeros(feat_w, dtype=torch.bool, device=device) - valid_y = torch.zeros(feat_h, dtype=torch.bool, device=device) - valid_x[:valid_w] = 1 - valid_y[:valid_h] = 1 - valid_xx, valid_yy = self._meshgrid(valid_x, valid_y) - valid = valid_xx & valid_yy - valid = valid[:, None].expand(valid.size(0), - num_base_anchors).contiguous().view(-1) - return valid - - def __repr__(self): - """str: a string that describes the module""" - indent_str = ' ' - repr_str = self.__class__.__name__ + '(\n' - repr_str += f'{indent_str}strides={self.strides},\n' - repr_str += f'{indent_str}ratios={self.ratios},\n' - repr_str += f'{indent_str}scales={self.scales},\n' - repr_str += f'{indent_str}base_sizes={self.base_sizes},\n' - repr_str += f'{indent_str}scale_major={self.scale_major},\n' - repr_str += f'{indent_str}octave_base_scale=' - repr_str += f'{self.octave_base_scale},\n' - repr_str += f'{indent_str}scales_per_octave=' - repr_str += f'{self.scales_per_octave},\n' - repr_str += f'{indent_str}num_levels={self.num_levels}\n' - repr_str += f'{indent_str}centers={self.centers},\n' - repr_str += f'{indent_str}center_offset={self.center_offset})' - return repr_str - - -@PRIOR_GENERATORS.register_module() -class SSDAnchorGenerator(AnchorGenerator): - """Anchor generator for SSD. - - Args: - strides (list[int] | list[tuple[int, int]]): Strides of anchors - in multiple feature levels. - ratios (list[float]): The list of ratios between the height and width - of anchors in a single level. - min_sizes (list[float]): The list of minimum anchor sizes on each - level. - max_sizes (list[float]): The list of maximum anchor sizes on each - level. - basesize_ratio_range (tuple(float)): Ratio range of anchors. Being - used when not setting min_sizes and max_sizes. - input_size (int): Size of feature map, 300 for SSD300, 512 for - SSD512. Being used when not setting min_sizes and max_sizes. - scale_major (bool): Whether to multiply scales first when generating - base anchors. If true, the anchors in the same row will have the - same scales. It is always set to be False in SSD. - """ - - def __init__(self, - strides, - ratios, - min_sizes=None, - max_sizes=None, - basesize_ratio_range=(0.15, 0.9), - input_size=300, - scale_major=True): - assert len(strides) == len(ratios) - assert not (min_sizes is None) ^ (max_sizes is None) - self.strides = [_pair(stride) for stride in strides] - self.centers = [(stride[0] / 2., stride[1] / 2.) - for stride in self.strides] - - if min_sizes is None and max_sizes is None: - # use hard code to generate SSD anchors - self.input_size = input_size - assert mmcv.is_tuple_of(basesize_ratio_range, float) - self.basesize_ratio_range = basesize_ratio_range - # calculate anchor ratios and sizes - min_ratio, max_ratio = basesize_ratio_range - min_ratio = int(min_ratio * 100) - max_ratio = int(max_ratio * 100) - step = int(np.floor(max_ratio - min_ratio) / (self.num_levels - 2)) - min_sizes = [] - max_sizes = [] - for ratio in range(int(min_ratio), int(max_ratio) + 1, step): - min_sizes.append(int(self.input_size * ratio / 100)) - max_sizes.append(int(self.input_size * (ratio + step) / 100)) - if self.input_size == 300: - if basesize_ratio_range[0] == 0.15: # SSD300 COCO - min_sizes.insert(0, int(self.input_size * 7 / 100)) - max_sizes.insert(0, int(self.input_size * 15 / 100)) - elif basesize_ratio_range[0] == 0.2: # SSD300 VOC - min_sizes.insert(0, int(self.input_size * 10 / 100)) - max_sizes.insert(0, int(self.input_size * 20 / 100)) - else: - raise ValueError( - 'basesize_ratio_range[0] should be either 0.15' - 'or 0.2 when input_size is 300, got ' - f'{basesize_ratio_range[0]}.') - elif self.input_size == 512: - if basesize_ratio_range[0] == 0.1: # SSD512 COCO - min_sizes.insert(0, int(self.input_size * 4 / 100)) - max_sizes.insert(0, int(self.input_size * 10 / 100)) - elif basesize_ratio_range[0] == 0.15: # SSD512 VOC - min_sizes.insert(0, int(self.input_size * 7 / 100)) - max_sizes.insert(0, int(self.input_size * 15 / 100)) - else: - raise ValueError( - 'When not setting min_sizes and max_sizes,' - 'basesize_ratio_range[0] should be either 0.1' - 'or 0.15 when input_size is 512, got' - f' {basesize_ratio_range[0]}.') - else: - raise ValueError( - 'Only support 300 or 512 in SSDAnchorGenerator when ' - 'not setting min_sizes and max_sizes, ' - f'got {self.input_size}.') - - assert len(min_sizes) == len(max_sizes) == len(strides) - - anchor_ratios = [] - anchor_scales = [] - for k in range(len(self.strides)): - scales = [1., np.sqrt(max_sizes[k] / min_sizes[k])] - anchor_ratio = [1.] - for r in ratios[k]: - anchor_ratio += [1 / r, r] # 4 or 6 ratio - anchor_ratios.append(torch.Tensor(anchor_ratio)) - anchor_scales.append(torch.Tensor(scales)) - - self.base_sizes = min_sizes - self.scales = anchor_scales - self.ratios = anchor_ratios - self.scale_major = scale_major - self.center_offset = 0 - self.base_anchors = self.gen_base_anchors() - - def gen_base_anchors(self): - """Generate base anchors. - - Returns: - list(torch.Tensor): Base anchors of a feature grid in multiple \ - feature levels. - """ - multi_level_base_anchors = [] - for i, base_size in enumerate(self.base_sizes): - base_anchors = self.gen_single_level_base_anchors( - base_size, - scales=self.scales[i], - ratios=self.ratios[i], - center=self.centers[i]) - indices = list(range(len(self.ratios[i]))) - indices.insert(1, len(indices)) - base_anchors = torch.index_select(base_anchors, 0, - torch.LongTensor(indices)) - multi_level_base_anchors.append(base_anchors) - return multi_level_base_anchors - - def __repr__(self): - """str: a string that describes the module""" - indent_str = ' ' - repr_str = self.__class__.__name__ + '(\n' - repr_str += f'{indent_str}strides={self.strides},\n' - repr_str += f'{indent_str}scales={self.scales},\n' - repr_str += f'{indent_str}scale_major={self.scale_major},\n' - repr_str += f'{indent_str}input_size={self.input_size},\n' - repr_str += f'{indent_str}scales={self.scales},\n' - repr_str += f'{indent_str}ratios={self.ratios},\n' - repr_str += f'{indent_str}num_levels={self.num_levels},\n' - repr_str += f'{indent_str}base_sizes={self.base_sizes},\n' - repr_str += f'{indent_str}basesize_ratio_range=' - repr_str += f'{self.basesize_ratio_range})' - return repr_str - - -@PRIOR_GENERATORS.register_module() -class LegacyAnchorGenerator(AnchorGenerator): - """Legacy anchor generator used in MMDetection V1.x. - - Note: - Difference to the V2.0 anchor generator: - - 1. The center offset of V1.x anchors are set to be 0.5 rather than 0. - 2. The width/height are minused by 1 when calculating the anchors' \ - centers and corners to meet the V1.x coordinate system. - 3. The anchors' corners are quantized. - - Args: - strides (list[int] | list[tuple[int]]): Strides of anchors - in multiple feature levels. - ratios (list[float]): The list of ratios between the height and width - of anchors in a single level. - scales (list[int] | None): Anchor scales for anchors in a single level. - It cannot be set at the same time if `octave_base_scale` and - `scales_per_octave` are set. - base_sizes (list[int]): The basic sizes of anchors in multiple levels. - If None is given, strides will be used to generate base_sizes. - scale_major (bool): Whether to multiply scales first when generating - base anchors. If true, the anchors in the same row will have the - same scales. By default it is True in V2.0 - octave_base_scale (int): The base scale of octave. - scales_per_octave (int): Number of scales for each octave. - `octave_base_scale` and `scales_per_octave` are usually used in - retinanet and the `scales` should be None when they are set. - centers (list[tuple[float, float]] | None): The centers of the anchor - relative to the feature grid center in multiple feature levels. - By default it is set to be None and not used. It a list of float - is given, this list will be used to shift the centers of anchors. - center_offset (float): The offset of center in proportion to anchors' - width and height. By default it is 0.5 in V2.0 but it should be 0.5 - in v1.x models. - - Examples: - >>> from mmdet.core import LegacyAnchorGenerator - >>> self = LegacyAnchorGenerator( - >>> [16], [1.], [1.], [9], center_offset=0.5) - >>> all_anchors = self.grid_anchors(((2, 2),), device='cpu') - >>> print(all_anchors) - [tensor([[ 0., 0., 8., 8.], - [16., 0., 24., 8.], - [ 0., 16., 8., 24.], - [16., 16., 24., 24.]])] - """ - - def gen_single_level_base_anchors(self, - base_size, - scales, - ratios, - center=None): - """Generate base anchors of a single level. - - Note: - The width/height of anchors are minused by 1 when calculating \ - the centers and corners to meet the V1.x coordinate system. - - Args: - base_size (int | float): Basic size of an anchor. - scales (torch.Tensor): Scales of the anchor. - ratios (torch.Tensor): The ratio between between the height. - and width of anchors in a single level. - center (tuple[float], optional): The center of the base anchor - related to a single feature grid. Defaults to None. - - Returns: - torch.Tensor: Anchors in a single-level feature map. - """ - w = base_size - h = base_size - if center is None: - x_center = self.center_offset * (w - 1) - y_center = self.center_offset * (h - 1) - else: - x_center, y_center = center - - h_ratios = torch.sqrt(ratios) - w_ratios = 1 / h_ratios - if self.scale_major: - ws = (w * w_ratios[:, None] * scales[None, :]).view(-1) - hs = (h * h_ratios[:, None] * scales[None, :]).view(-1) - else: - ws = (w * scales[:, None] * w_ratios[None, :]).view(-1) - hs = (h * scales[:, None] * h_ratios[None, :]).view(-1) - - # use float anchor and the anchor's center is aligned with the - # pixel center - base_anchors = [ - x_center - 0.5 * (ws - 1), y_center - 0.5 * (hs - 1), - x_center + 0.5 * (ws - 1), y_center + 0.5 * (hs - 1) - ] - base_anchors = torch.stack(base_anchors, dim=-1).round() - - return base_anchors - - -@PRIOR_GENERATORS.register_module() -class LegacySSDAnchorGenerator(SSDAnchorGenerator, LegacyAnchorGenerator): - """Legacy anchor generator used in MMDetection V1.x. - - The difference between `LegacySSDAnchorGenerator` and `SSDAnchorGenerator` - can be found in `LegacyAnchorGenerator`. - """ - - def __init__(self, - strides, - ratios, - basesize_ratio_range, - input_size=300, - scale_major=True): - super(LegacySSDAnchorGenerator, self).__init__( - strides=strides, - ratios=ratios, - basesize_ratio_range=basesize_ratio_range, - input_size=input_size, - scale_major=scale_major) - self.centers = [((stride - 1) / 2., (stride - 1) / 2.) - for stride in strides] - self.base_anchors = self.gen_base_anchors() - - -@PRIOR_GENERATORS.register_module() -class YOLOAnchorGenerator(AnchorGenerator): - """Anchor generator for YOLO. - - Args: - strides (list[int] | list[tuple[int, int]]): Strides of anchors - in multiple feature levels. - base_sizes (list[list[tuple[int, int]]]): The basic sizes - of anchors in multiple levels. - """ - - def __init__(self, strides, base_sizes): - self.strides = [_pair(stride) for stride in strides] - self.centers = [(stride[0] / 2., stride[1] / 2.) - for stride in self.strides] - self.base_sizes = [] - num_anchor_per_level = len(base_sizes[0]) - for base_sizes_per_level in base_sizes: - assert num_anchor_per_level == len(base_sizes_per_level) - self.base_sizes.append( - [_pair(base_size) for base_size in base_sizes_per_level]) - self.base_anchors = self.gen_base_anchors() - - @property - def num_levels(self): - """int: number of feature levels that the generator will be applied""" - return len(self.base_sizes) - - def gen_base_anchors(self): - """Generate base anchors. - - Returns: - list(torch.Tensor): Base anchors of a feature grid in multiple \ - feature levels. - """ - multi_level_base_anchors = [] - for i, base_sizes_per_level in enumerate(self.base_sizes): - center = None - if self.centers is not None: - center = self.centers[i] - multi_level_base_anchors.append( - self.gen_single_level_base_anchors(base_sizes_per_level, - center)) - return multi_level_base_anchors - - def gen_single_level_base_anchors(self, base_sizes_per_level, center=None): - """Generate base anchors of a single level. - - Args: - base_sizes_per_level (list[tuple[int, int]]): Basic sizes of - anchors. - center (tuple[float], optional): The center of the base anchor - related to a single feature grid. Defaults to None. - - Returns: - torch.Tensor: Anchors in a single-level feature maps. - """ - x_center, y_center = center - base_anchors = [] - for base_size in base_sizes_per_level: - w, h = base_size - - # use float anchor and the anchor's center is aligned with the - # pixel center - base_anchor = torch.Tensor([ - x_center - 0.5 * w, y_center - 0.5 * h, x_center + 0.5 * w, - y_center + 0.5 * h - ]) - base_anchors.append(base_anchor) - base_anchors = torch.stack(base_anchors, dim=0) - - return base_anchors - - def responsible_flags(self, featmap_sizes, gt_bboxes, device='cuda'): - """Generate responsible anchor flags of grid cells in multiple scales. - - Args: - featmap_sizes (list(tuple)): List of feature map sizes in multiple - feature levels. - gt_bboxes (Tensor): Ground truth boxes, shape (n, 4). - device (str): Device where the anchors will be put on. - - Return: - list(torch.Tensor): responsible flags of anchors in multiple level - """ - assert self.num_levels == len(featmap_sizes) - multi_level_responsible_flags = [] - for i in range(self.num_levels): - anchor_stride = self.strides[i] - flags = self.single_level_responsible_flags( - featmap_sizes[i], - gt_bboxes, - anchor_stride, - self.num_base_anchors[i], - device=device) - multi_level_responsible_flags.append(flags) - return multi_level_responsible_flags - - def single_level_responsible_flags(self, - featmap_size, - gt_bboxes, - stride, - num_base_anchors, - device='cuda'): - """Generate the responsible flags of anchor in a single feature map. - - Args: - featmap_size (tuple[int]): The size of feature maps. - gt_bboxes (Tensor): Ground truth boxes, shape (n, 4). - stride (tuple(int)): stride of current level - num_base_anchors (int): The number of base anchors. - device (str, optional): Device where the flags will be put on. - Defaults to 'cuda'. - - Returns: - torch.Tensor: The valid flags of each anchor in a single level \ - feature map. - """ - feat_h, feat_w = featmap_size - gt_bboxes_cx = ((gt_bboxes[:, 0] + gt_bboxes[:, 2]) * 0.5).to(device) - gt_bboxes_cy = ((gt_bboxes[:, 1] + gt_bboxes[:, 3]) * 0.5).to(device) - gt_bboxes_grid_x = torch.floor(gt_bboxes_cx / stride[0]).long() - gt_bboxes_grid_y = torch.floor(gt_bboxes_cy / stride[1]).long() - - # row major indexing - gt_bboxes_grid_idx = gt_bboxes_grid_y * feat_w + gt_bboxes_grid_x - - responsible_grid = torch.zeros( - feat_h * feat_w, dtype=torch.uint8, device=device) - responsible_grid[gt_bboxes_grid_idx] = 1 - - responsible_grid = responsible_grid[:, None].expand( - responsible_grid.size(0), num_base_anchors).contiguous().view(-1) - return responsible_grid diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/builder.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/builder.py deleted file mode 100644 index ddb25ad37..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/builder.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -from mmcv.utils import Registry, build_from_cfg - -PRIOR_GENERATORS = Registry('Generator for anchors and points') - -ANCHOR_GENERATORS = PRIOR_GENERATORS - - -def build_prior_generator(cfg, default_args=None): - return build_from_cfg(cfg, PRIOR_GENERATORS, default_args) - - -def build_anchor_generator(cfg, default_args=None): - warnings.warn( - '``build_anchor_generator`` would be deprecated soon, please use ' - '``build_prior_generator`` ') - return build_prior_generator(cfg, default_args=default_args) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/point_generator.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/point_generator.py deleted file mode 100644 index cc9c3887d..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/point_generator.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -from torch.nn.modules.utils import _pair - -from .builder import PRIOR_GENERATORS - - -@PRIOR_GENERATORS.register_module() -class PointGenerator: - - def _meshgrid(self, x, y, row_major=True): - xx = x.repeat(len(y)) - yy = y.view(-1, 1).repeat(1, len(x)).view(-1) - if row_major: - return xx, yy - else: - return yy, xx - - def grid_points(self, featmap_size, stride=16, device='cuda'): - feat_h, feat_w = featmap_size - shift_x = torch.arange(0., feat_w, device=device) * stride - shift_y = torch.arange(0., feat_h, device=device) * stride - shift_xx, shift_yy = self._meshgrid(shift_x, shift_y) - stride = shift_x.new_full((shift_xx.shape[0], ), stride) - shifts = torch.stack([shift_xx, shift_yy, stride], dim=-1) - all_points = shifts.to(device) - return all_points - - def valid_flags(self, featmap_size, valid_size, device='cuda'): - feat_h, feat_w = featmap_size - valid_h, valid_w = valid_size - assert valid_h <= feat_h and valid_w <= feat_w - valid_x = torch.zeros(feat_w, dtype=torch.bool, device=device) - valid_y = torch.zeros(feat_h, dtype=torch.bool, device=device) - valid_x[:valid_w] = 1 - valid_y[:valid_h] = 1 - valid_xx, valid_yy = self._meshgrid(valid_x, valid_y) - valid = valid_xx & valid_yy - return valid - - -@PRIOR_GENERATORS.register_module() -class MlvlPointGenerator: - """Standard points generator for multi-level (Mlvl) feature maps in 2D - points-based detectors. - - Args: - strides (list[int] | list[tuple[int, int]]): Strides of anchors - in multiple feature levels in order (w, h). - offset (float): The offset of points, the value is normalized with - corresponding stride. Defaults to 0.5. - """ - - def __init__(self, strides, offset=0.5): - self.strides = [_pair(stride) for stride in strides] - self.offset = offset - - @property - def num_levels(self): - """int: number of feature levels that the generator will be applied""" - return len(self.strides) - - @property - def num_base_priors(self): - """list[int]: The number of priors (points) at a point - on the feature grid""" - return [1 for _ in range(len(self.strides))] - - def _meshgrid(self, x, y, row_major=True): - yy, xx = torch.meshgrid(y, x) - if row_major: - # warning .flatten() would cause error in ONNX exporting - # have to use reshape here - return xx.reshape(-1), yy.reshape(-1) - - else: - return yy.reshape(-1), xx.reshape(-1) - - def grid_priors(self, - featmap_sizes, - dtype=torch.float32, - device='cuda', - with_stride=False): - """Generate grid points of multiple feature levels. - - Args: - featmap_sizes (list[tuple]): List of feature map sizes in - multiple feature levels, each size arrange as - as (h, w). - dtype (:obj:`dtype`): Dtype of priors. Default: torch.float32. - device (str): The device where the anchors will be put on. - with_stride (bool): Whether to concatenate the stride to - the last dimension of points. - - Return: - list[torch.Tensor]: Points of multiple feature levels. - The sizes of each tensor should be (N, 2) when with stride is - ``False``, where N = width * height, width and height - are the sizes of the corresponding feature level, - and the last dimension 2 represent (coord_x, coord_y), - otherwise the shape should be (N, 4), - and the last dimension 4 represent - (coord_x, coord_y, stride_w, stride_h). - """ - - assert self.num_levels == len(featmap_sizes) - multi_level_priors = [] - for i in range(self.num_levels): - priors = self.single_level_grid_priors( - featmap_sizes[i], - level_idx=i, - dtype=dtype, - device=device, - with_stride=with_stride) - multi_level_priors.append(priors) - return multi_level_priors - - def single_level_grid_priors(self, - featmap_size, - level_idx, - dtype=torch.float32, - device='cuda', - with_stride=False): - """Generate grid Points of a single level. - - Note: - This function is usually called by method ``self.grid_priors``. - - Args: - featmap_size (tuple[int]): Size of the feature maps, arrange as - (h, w). - level_idx (int): The index of corresponding feature map level. - dtype (:obj:`dtype`): Dtype of priors. Default: torch.float32. - device (str, optional): The device the tensor will be put on. - Defaults to 'cuda'. - with_stride (bool): Concatenate the stride to the last dimension - of points. - - Return: - Tensor: Points of single feature levels. - The shape of tensor should be (N, 2) when with stride is - ``False``, where N = width * height, width and height - are the sizes of the corresponding feature level, - and the last dimension 2 represent (coord_x, coord_y), - otherwise the shape should be (N, 4), - and the last dimension 4 represent - (coord_x, coord_y, stride_w, stride_h). - """ - feat_h, feat_w = featmap_size - stride_w, stride_h = self.strides[level_idx] - shift_x = (torch.arange(0, feat_w, device=device) + - self.offset) * stride_w - # keep featmap_size as Tensor instead of int, so that we - # can convert to ONNX correctly - shift_x = shift_x.to(dtype) - - shift_y = (torch.arange(0, feat_h, device=device) + - self.offset) * stride_h - # keep featmap_size as Tensor instead of int, so that we - # can convert to ONNX correctly - shift_y = shift_y.to(dtype) - shift_xx, shift_yy = self._meshgrid(shift_x, shift_y) - if not with_stride: - shifts = torch.stack([shift_xx, shift_yy], dim=-1) - else: - # use `shape[0]` instead of `len(shift_xx)` for ONNX export - stride_w = shift_xx.new_full((shift_xx.shape[0], ), - stride_w).to(dtype) - stride_h = shift_xx.new_full((shift_yy.shape[0], ), - stride_h).to(dtype) - shifts = torch.stack([shift_xx, shift_yy, stride_w, stride_h], - dim=-1) - all_points = shifts.to(device) - return all_points - - def valid_flags(self, featmap_sizes, pad_shape, device='cuda'): - """Generate valid flags of points of multiple feature levels. - - Args: - featmap_sizes (list(tuple)): List of feature map sizes in - multiple feature levels, each size arrange as - as (h, w). - pad_shape (tuple(int)): The padded shape of the image, - arrange as (h, w). - device (str): The device where the anchors will be put on. - - Return: - list(torch.Tensor): Valid flags of points of multiple levels. - """ - assert self.num_levels == len(featmap_sizes) - multi_level_flags = [] - for i in range(self.num_levels): - point_stride = self.strides[i] - feat_h, feat_w = featmap_sizes[i] - h, w = pad_shape[:2] - valid_feat_h = min(int(np.ceil(h / point_stride[1])), feat_h) - valid_feat_w = min(int(np.ceil(w / point_stride[0])), feat_w) - flags = self.single_level_valid_flags((feat_h, feat_w), - (valid_feat_h, valid_feat_w), - device=device) - multi_level_flags.append(flags) - return multi_level_flags - - def single_level_valid_flags(self, - featmap_size, - valid_size, - device='cuda'): - """Generate the valid flags of points of a single feature map. - - Args: - featmap_size (tuple[int]): The size of feature maps, arrange as - as (h, w). - valid_size (tuple[int]): The valid size of the feature maps. - The size arrange as as (h, w). - device (str, optional): The device where the flags will be put on. - Defaults to 'cuda'. - - Returns: - torch.Tensor: The valid flags of each points in a single level \ - feature map. - """ - feat_h, feat_w = featmap_size - valid_h, valid_w = valid_size - assert valid_h <= feat_h and valid_w <= feat_w - valid_x = torch.zeros(feat_w, dtype=torch.bool, device=device) - valid_y = torch.zeros(feat_h, dtype=torch.bool, device=device) - valid_x[:valid_w] = 1 - valid_y[:valid_h] = 1 - valid_xx, valid_yy = self._meshgrid(valid_x, valid_y) - valid = valid_xx & valid_yy - return valid - - def sparse_priors(self, - prior_idxs, - featmap_size, - level_idx, - dtype=torch.float32, - device='cuda'): - """Generate sparse points according to the ``prior_idxs``. - - Args: - prior_idxs (Tensor): The index of corresponding anchors - in the feature map. - featmap_size (tuple[int]): feature map size arrange as (w, h). - level_idx (int): The level index of corresponding feature - map. - dtype (obj:`torch.dtype`): Date type of points. Defaults to - ``torch.float32``. - device (obj:`torch.device`): The device where the points is - located. - Returns: - Tensor: Anchor with shape (N, 2), N should be equal to - the length of ``prior_idxs``. And last dimension - 2 represent (coord_x, coord_y). - """ - height, width = featmap_size - x = (prior_idxs % width + self.offset) * self.strides[level_idx][0] - y = ((prior_idxs // width) % height + - self.offset) * self.strides[level_idx][1] - prioris = torch.stack([x, y], 1).to(dtype) - prioris = prioris.to(device) - return prioris diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/utils.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/utils.py deleted file mode 100644 index c2f202476..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/anchor/utils.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def images_to_levels(target, num_levels): - """Convert targets by image to targets by feature level. - - [target_img0, target_img1] -> [target_level0, target_level1, ...] - """ - target = torch.stack(target, 0) - level_targets = [] - start = 0 - for n in num_levels: - end = start + n - # level_targets.append(target[:, start:end].squeeze(0)) - level_targets.append(target[:, start:end]) - start = end - return level_targets - - -def anchor_inside_flags(flat_anchors, - valid_flags, - img_shape, - allowed_border=0): - """Check whether the anchors are inside the border. - - Args: - flat_anchors (torch.Tensor): Flatten anchors, shape (n, 4). - valid_flags (torch.Tensor): An existing valid flags of anchors. - img_shape (tuple(int)): Shape of current image. - allowed_border (int, optional): The border to allow the valid anchor. - Defaults to 0. - - Returns: - torch.Tensor: Flags indicating whether the anchors are inside a \ - valid range. - """ - img_h, img_w = img_shape[:2] - if allowed_border >= 0: - inside_flags = valid_flags & \ - (flat_anchors[:, 0] >= -allowed_border) & \ - (flat_anchors[:, 1] >= -allowed_border) & \ - (flat_anchors[:, 2] < img_w + allowed_border) & \ - (flat_anchors[:, 3] < img_h + allowed_border) - else: - inside_flags = valid_flags - return inside_flags - - -def calc_region(bbox, ratio, featmap_size=None): - """Calculate a proportional bbox region. - - The bbox center are fixed and the new h' and w' is h * ratio and w * ratio. - - Args: - bbox (Tensor): Bboxes to calculate regions, shape (n, 4). - ratio (float): Ratio of the output region. - featmap_size (tuple): Feature map size used for clipping the boundary. - - Returns: - tuple: x1, y1, x2, y2 - """ - x1 = torch.round((1 - ratio) * bbox[0] + ratio * bbox[2]).long() - y1 = torch.round((1 - ratio) * bbox[1] + ratio * bbox[3]).long() - x2 = torch.round(ratio * bbox[0] + (1 - ratio) * bbox[2]).long() - y2 = torch.round(ratio * bbox[1] + (1 - ratio) * bbox[3]).long() - if featmap_size is not None: - x1 = x1.clamp(min=0, max=featmap_size[1]) - y1 = y1.clamp(min=0, max=featmap_size[0]) - x2 = x2.clamp(min=0, max=featmap_size[1]) - y2 = y2.clamp(min=0, max=featmap_size[0]) - return (x1, y1, x2, y2) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/__init__.py deleted file mode 100644 index 371eba198..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .assigners import (AssignResult, BaseAssigner, CenterRegionAssigner, - MaxIoUAssigner, RegionAssigner) -from .builder import build_assigner, build_bbox_coder, build_sampler -from .coder import (BaseBBoxCoder, DeltaXYWHBBoxCoder, DistancePointBBoxCoder, - PseudoBBoxCoder, TBLRBBoxCoder) -from .iou_calculators import BboxOverlaps2D, bbox_overlaps -from .samplers import (BaseSampler, CombinedSampler, - InstanceBalancedPosSampler, IoUBalancedNegSampler, - OHEMSampler, PseudoSampler, RandomSampler, - SamplingResult, ScoreHLRSampler) -from .transforms import (bbox2distance, bbox2result, bbox2roi, - bbox_cxcywh_to_xyxy, bbox_flip, bbox_mapping, - bbox_mapping_back, bbox_rescale, bbox_xyxy_to_cxcywh, - distance2bbox, find_inside_bboxes, roi2bbox) - -__all__ = [ - 'bbox_overlaps', 'BboxOverlaps2D', 'BaseAssigner', 'MaxIoUAssigner', - 'AssignResult', 'BaseSampler', 'PseudoSampler', 'RandomSampler', - 'InstanceBalancedPosSampler', 'IoUBalancedNegSampler', 'CombinedSampler', - 'OHEMSampler', 'SamplingResult', 'ScoreHLRSampler', 'build_assigner', - 'build_sampler', 'bbox_flip', 'bbox_mapping', 'bbox_mapping_back', - 'bbox2roi', 'roi2bbox', 'bbox2result', 'distance2bbox', 'bbox2distance', - 'build_bbox_coder', 'BaseBBoxCoder', 'PseudoBBoxCoder', - 'DeltaXYWHBBoxCoder', 'TBLRBBoxCoder', 'DistancePointBBoxCoder', - 'CenterRegionAssigner', 'bbox_rescale', 'bbox_cxcywh_to_xyxy', - 'bbox_xyxy_to_cxcywh', 'RegionAssigner', 'find_inside_bboxes' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/__init__.py deleted file mode 100644 index 5eaf7fa3a..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .approx_max_iou_assigner import ApproxMaxIoUAssigner -from .assign_result import AssignResult -from .atss_assigner import ATSSAssigner -from .base_assigner import BaseAssigner -from .center_region_assigner import CenterRegionAssigner -from .grid_assigner import GridAssigner -from .hungarian_assigner import HungarianAssigner -from .mask_hungarian_assigner import MaskHungarianAssigner -from .max_iou_assigner import MaxIoUAssigner -from .point_assigner import PointAssigner -from .region_assigner import RegionAssigner -from .sim_ota_assigner import SimOTAAssigner -from .task_aligned_assigner import TaskAlignedAssigner -from .uniform_assigner import UniformAssigner - -__all__ = [ - 'BaseAssigner', 'MaxIoUAssigner', 'ApproxMaxIoUAssigner', 'AssignResult', - 'PointAssigner', 'ATSSAssigner', 'CenterRegionAssigner', 'GridAssigner', - 'HungarianAssigner', 'RegionAssigner', 'UniformAssigner', 'SimOTAAssigner', - 'TaskAlignedAssigner', 'MaskHungarianAssigner' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/approx_max_iou_assigner.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/approx_max_iou_assigner.py deleted file mode 100644 index 304d09c3f..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/approx_max_iou_assigner.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from .max_iou_assigner import MaxIoUAssigner - - -@BBOX_ASSIGNERS.register_module() -class ApproxMaxIoUAssigner(MaxIoUAssigner): - """Assign a corresponding gt bbox or background to each bbox. - - Each proposals will be assigned with an integer indicating the ground truth - index. (semi-positive index: gt label (0-based), -1: background) - - - -1: negative sample, no assigned gt - - semi-positive integer: positive sample, index (0-based) of assigned gt - - Args: - pos_iou_thr (float): IoU threshold for positive bboxes. - neg_iou_thr (float or tuple): IoU threshold for negative bboxes. - min_pos_iou (float): Minimum iou for a bbox to be considered as a - positive bbox. Positive samples can have smaller IoU than - pos_iou_thr due to the 4th step (assign max IoU sample to each gt). - gt_max_assign_all (bool): Whether to assign all bboxes with the same - highest overlap with some gt to that gt. - ignore_iof_thr (float): IoF threshold for ignoring bboxes (if - `gt_bboxes_ignore` is specified). Negative values mean not - ignoring any bboxes. - ignore_wrt_candidates (bool): Whether to compute the iof between - `bboxes` and `gt_bboxes_ignore`, or the contrary. - match_low_quality (bool): Whether to allow quality matches. This is - usually allowed for RPN and single stage detectors, but not allowed - in the second stage. - gpu_assign_thr (int): The upper bound of the number of GT for GPU - assign. When the number of gt is above this threshold, will assign - on CPU device. Negative values mean not assign on CPU. - """ - - def __init__(self, - pos_iou_thr, - neg_iou_thr, - min_pos_iou=.0, - gt_max_assign_all=True, - ignore_iof_thr=-1, - ignore_wrt_candidates=True, - match_low_quality=True, - gpu_assign_thr=-1, - iou_calculator=dict(type='BboxOverlaps2D')): - self.pos_iou_thr = pos_iou_thr - self.neg_iou_thr = neg_iou_thr - self.min_pos_iou = min_pos_iou - self.gt_max_assign_all = gt_max_assign_all - self.ignore_iof_thr = ignore_iof_thr - self.ignore_wrt_candidates = ignore_wrt_candidates - self.gpu_assign_thr = gpu_assign_thr - self.match_low_quality = match_low_quality - self.iou_calculator = build_iou_calculator(iou_calculator) - - def assign(self, - approxs, - squares, - approxs_per_octave, - gt_bboxes, - gt_bboxes_ignore=None, - gt_labels=None): - """Assign gt to approxs. - - This method assign a gt bbox to each group of approxs (bboxes), - each group of approxs is represent by a base approx (bbox) and - will be assigned with -1, or a semi-positive number. - background_label (-1) means negative sample, - semi-positive number is the index (0-based) of assigned gt. - The assignment is done in following steps, the order matters. - - 1. assign every bbox to background_label (-1) - 2. use the max IoU of each group of approxs to assign - 2. assign proposals whose iou with all gts < neg_iou_thr to background - 3. for each bbox, if the iou with its nearest gt >= pos_iou_thr, - assign it to that bbox - 4. for each gt bbox, assign its nearest proposals (may be more than - one) to itself - - Args: - approxs (Tensor): Bounding boxes to be assigned, - shape(approxs_per_octave*n, 4). - squares (Tensor): Base Bounding boxes to be assigned, - shape(n, 4). - approxs_per_octave (int): number of approxs per octave - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - - Returns: - :obj:`AssignResult`: The assign result. - """ - num_squares = squares.size(0) - num_gts = gt_bboxes.size(0) - - if num_squares == 0 or num_gts == 0: - # No predictions and/or truth, return empty assignment - overlaps = approxs.new(num_gts, num_squares) - assign_result = self.assign_wrt_overlaps(overlaps, gt_labels) - return assign_result - - # re-organize anchors by approxs_per_octave x num_squares - approxs = torch.transpose( - approxs.view(num_squares, approxs_per_octave, 4), 0, - 1).contiguous().view(-1, 4) - assign_on_cpu = True if (self.gpu_assign_thr > 0) and ( - num_gts > self.gpu_assign_thr) else False - # compute overlap and assign gt on CPU when number of GT is large - if assign_on_cpu: - device = approxs.device - approxs = approxs.cpu() - gt_bboxes = gt_bboxes.cpu() - if gt_bboxes_ignore is not None: - gt_bboxes_ignore = gt_bboxes_ignore.cpu() - if gt_labels is not None: - gt_labels = gt_labels.cpu() - all_overlaps = self.iou_calculator(approxs, gt_bboxes) - - overlaps, _ = all_overlaps.view(approxs_per_octave, num_squares, - num_gts).max(dim=0) - overlaps = torch.transpose(overlaps, 0, 1) - - if (self.ignore_iof_thr > 0 and gt_bboxes_ignore is not None - and gt_bboxes_ignore.numel() > 0 and squares.numel() > 0): - if self.ignore_wrt_candidates: - ignore_overlaps = self.iou_calculator( - squares, gt_bboxes_ignore, mode='iof') - ignore_max_overlaps, _ = ignore_overlaps.max(dim=1) - else: - ignore_overlaps = self.iou_calculator( - gt_bboxes_ignore, squares, mode='iof') - ignore_max_overlaps, _ = ignore_overlaps.max(dim=0) - overlaps[:, ignore_max_overlaps > self.ignore_iof_thr] = -1 - - assign_result = self.assign_wrt_overlaps(overlaps, gt_labels) - if assign_on_cpu: - assign_result.gt_inds = assign_result.gt_inds.to(device) - assign_result.max_overlaps = assign_result.max_overlaps.to(device) - if assign_result.labels is not None: - assign_result.labels = assign_result.labels.to(device) - return assign_result diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/assign_result.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/assign_result.py deleted file mode 100644 index 488010b5d..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/assign_result.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmdet.utils import util_mixins - - -class AssignResult(util_mixins.NiceRepr): - """Stores assignments between predicted and truth boxes. - - Attributes: - num_gts (int): the number of truth boxes considered when computing this - assignment - - gt_inds (LongTensor): for each predicted box indicates the 1-based - index of the assigned truth box. 0 means unassigned and -1 means - ignore. - - max_overlaps (FloatTensor): the iou between the predicted box and its - assigned truth box. - - labels (None | LongTensor): If specified, for each predicted box - indicates the category label of the assigned truth box. - - Example: - >>> # An assign result between 4 predicted boxes and 9 true boxes - >>> # where only two boxes were assigned. - >>> num_gts = 9 - >>> max_overlaps = torch.LongTensor([0, .5, .9, 0]) - >>> gt_inds = torch.LongTensor([-1, 1, 2, 0]) - >>> labels = torch.LongTensor([0, 3, 4, 0]) - >>> self = AssignResult(num_gts, gt_inds, max_overlaps, labels) - >>> print(str(self)) # xdoctest: +IGNORE_WANT - - >>> # Force addition of gt labels (when adding gt as proposals) - >>> new_labels = torch.LongTensor([3, 4, 5]) - >>> self.add_gt_(new_labels) - >>> print(str(self)) # xdoctest: +IGNORE_WANT - - """ - - def __init__(self, num_gts, gt_inds, max_overlaps, labels=None): - self.num_gts = num_gts - self.gt_inds = gt_inds - self.max_overlaps = max_overlaps - self.labels = labels - # Interface for possible user-defined properties - self._extra_properties = {} - - @property - def num_preds(self): - """int: the number of predictions in this assignment""" - return len(self.gt_inds) - - def set_extra_property(self, key, value): - """Set user-defined new property.""" - assert key not in self.info - self._extra_properties[key] = value - - def get_extra_property(self, key): - """Get user-defined property.""" - return self._extra_properties.get(key, None) - - @property - def info(self): - """dict: a dictionary of info about the object""" - basic_info = { - 'num_gts': self.num_gts, - 'num_preds': self.num_preds, - 'gt_inds': self.gt_inds, - 'max_overlaps': self.max_overlaps, - 'labels': self.labels, - } - basic_info.update(self._extra_properties) - return basic_info - - def __nice__(self): - """str: a "nice" summary string describing this assign result""" - parts = [] - parts.append(f'num_gts={self.num_gts!r}') - if self.gt_inds is None: - parts.append(f'gt_inds={self.gt_inds!r}') - else: - parts.append(f'gt_inds.shape={tuple(self.gt_inds.shape)!r}') - if self.max_overlaps is None: - parts.append(f'max_overlaps={self.max_overlaps!r}') - else: - parts.append('max_overlaps.shape=' - f'{tuple(self.max_overlaps.shape)!r}') - if self.labels is None: - parts.append(f'labels={self.labels!r}') - else: - parts.append(f'labels.shape={tuple(self.labels.shape)!r}') - return ', '.join(parts) - - @classmethod - def random(cls, **kwargs): - """Create random AssignResult for tests or debugging. - - Args: - num_preds: number of predicted boxes - num_gts: number of true boxes - p_ignore (float): probability of a predicted box assigned to an - ignored truth - p_assigned (float): probability of a predicted box not being - assigned - p_use_label (float | bool): with labels or not - rng (None | int | numpy.random.RandomState): seed or state - - Returns: - :obj:`AssignResult`: Randomly generated assign results. - - Example: - >>> from mmdet.core.bbox.assigners.assign_result import * # NOQA - >>> self = AssignResult.random() - >>> print(self.info) - """ - from mmdet.core.bbox import demodata - rng = demodata.ensure_rng(kwargs.get('rng', None)) - - num_gts = kwargs.get('num_gts', None) - num_preds = kwargs.get('num_preds', None) - p_ignore = kwargs.get('p_ignore', 0.3) - p_assigned = kwargs.get('p_assigned', 0.7) - p_use_label = kwargs.get('p_use_label', 0.5) - num_classes = kwargs.get('p_use_label', 3) - - if num_gts is None: - num_gts = rng.randint(0, 8) - if num_preds is None: - num_preds = rng.randint(0, 16) - - if num_gts == 0: - max_overlaps = torch.zeros(num_preds, dtype=torch.float32) - gt_inds = torch.zeros(num_preds, dtype=torch.int64) - if p_use_label is True or p_use_label < rng.rand(): - labels = torch.zeros(num_preds, dtype=torch.int64) - else: - labels = None - else: - import numpy as np - - # Create an overlap for each predicted box - max_overlaps = torch.from_numpy(rng.rand(num_preds)) - - # Construct gt_inds for each predicted box - is_assigned = torch.from_numpy(rng.rand(num_preds) < p_assigned) - # maximum number of assignments constraints - n_assigned = min(num_preds, min(num_gts, is_assigned.sum())) - - assigned_idxs = np.where(is_assigned)[0] - rng.shuffle(assigned_idxs) - assigned_idxs = assigned_idxs[0:n_assigned] - assigned_idxs.sort() - - is_assigned[:] = 0 - is_assigned[assigned_idxs] = True - - is_ignore = torch.from_numpy( - rng.rand(num_preds) < p_ignore) & is_assigned - - gt_inds = torch.zeros(num_preds, dtype=torch.int64) - - true_idxs = np.arange(num_gts) - rng.shuffle(true_idxs) - true_idxs = torch.from_numpy(true_idxs) - gt_inds[is_assigned] = true_idxs[:n_assigned].long() - - gt_inds = torch.from_numpy( - rng.randint(1, num_gts + 1, size=num_preds)) - gt_inds[is_ignore] = -1 - gt_inds[~is_assigned] = 0 - max_overlaps[~is_assigned] = 0 - - if p_use_label is True or p_use_label < rng.rand(): - if num_classes == 0: - labels = torch.zeros(num_preds, dtype=torch.int64) - else: - labels = torch.from_numpy( - # remind that we set FG labels to [0, num_class-1] - # since mmdet v2.0 - # BG cat_id: num_class - rng.randint(0, num_classes, size=num_preds)) - labels[~is_assigned] = 0 - else: - labels = None - - self = cls(num_gts, gt_inds, max_overlaps, labels) - return self - - def add_gt_(self, gt_labels): - """Add ground truth as assigned results. - - Args: - gt_labels (torch.Tensor): Labels of gt boxes - """ - self_inds = torch.arange( - 1, len(gt_labels) + 1, dtype=torch.long, device=gt_labels.device) - self.gt_inds = torch.cat([self_inds, self.gt_inds]) - - self.max_overlaps = torch.cat( - [self.max_overlaps.new_ones(len(gt_labels)), self.max_overlaps]) - - if self.labels is not None: - self.labels = torch.cat([gt_labels, self.labels]) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/atss_assigner.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/atss_assigner.py deleted file mode 100644 index 79c8281e5..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/atss_assigner.py +++ /dev/null @@ -1,234 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -@BBOX_ASSIGNERS.register_module() -class ATSSAssigner(BaseAssigner): - """Assign a corresponding gt bbox or background to each bbox. - - Each proposals will be assigned with `0` or a positive integer - indicating the ground truth index. - - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - - If ``alpha`` is not None, it means that the dynamic cost - ATSSAssigner is adopted, which is currently only used in the DDOD. - - Args: - topk (float): number of bbox selected in each level - """ - - def __init__(self, - topk, - alpha=None, - iou_calculator=dict(type='BboxOverlaps2D'), - ignore_iof_thr=-1): - self.topk = topk - self.alpha = alpha - self.iou_calculator = build_iou_calculator(iou_calculator) - self.ignore_iof_thr = ignore_iof_thr - - """Assign a corresponding gt bbox or background to each bbox. - - Args: - topk (int): number of bbox selected in each level. - alpha (float): param of cost rate for each proposal only in DDOD. - Default None. - iou_calculator (dict): builder of IoU calculator. - Default dict(type='BboxOverlaps2D'). - ignore_iof_thr (int): whether ignore max overlaps or not. - Default -1 (1 or -1). - """ - - # https://github.com/sfzhang15/ATSS/blob/master/atss_core/modeling/rpn/atss/loss.py - def assign(self, - bboxes, - num_level_bboxes, - gt_bboxes, - gt_bboxes_ignore=None, - gt_labels=None, - cls_scores=None, - bbox_preds=None): - """Assign gt to bboxes. - - The assignment is done in following steps - - 1. compute iou between all bbox (bbox of all pyramid levels) and gt - 2. compute center distance between all bbox and gt - 3. on each pyramid level, for each gt, select k bbox whose center - are closest to the gt center, so we total select k*l bbox as - candidates for each gt - 4. get corresponding iou for the these candidates, and compute the - mean and std, set mean + std as the iou threshold - 5. select these candidates whose iou are greater than or equal to - the threshold as positive - 6. limit the positive sample's center in gt - - If ``alpha`` is not None, and ``cls_scores`` and `bbox_preds` - are not None, the overlaps calculation in the first step - will also include dynamic cost, which is currently only used in - the DDOD. - - Args: - bboxes (Tensor): Bounding boxes to be assigned, shape(n, 4). - num_level_bboxes (List): num of bboxes in each level - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. Default None. - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - cls_scores (list[Tensor]): Classification scores for all scale - levels, each is a 4D-tensor, the channels number is - num_base_priors * num_classes. Default None. - bbox_preds (list[Tensor]): Box energies / deltas for all scale - levels, each is a 4D-tensor, the channels number is - num_base_priors * 4. Default None. - - Returns: - :obj:`AssignResult`: The assign result. - """ - INF = 100000000 - bboxes = bboxes[:, :4] - num_gt, num_bboxes = gt_bboxes.size(0), bboxes.size(0) - - message = 'Invalid alpha parameter because cls_scores or ' \ - 'bbox_preds are None. If you want to use the ' \ - 'cost-based ATSSAssigner, please set cls_scores, ' \ - 'bbox_preds and self.alpha at the same time. ' - - if self.alpha is None: - # ATSSAssigner - overlaps = self.iou_calculator(bboxes, gt_bboxes) - if cls_scores is not None or bbox_preds is not None: - warnings.warn(message) - else: - # Dynamic cost ATSSAssigner in DDOD - assert cls_scores is not None and bbox_preds is not None, message - - # compute cls cost for bbox and GT - cls_cost = torch.sigmoid(cls_scores[:, gt_labels]) - - # compute iou between all bbox and gt - overlaps = self.iou_calculator(bbox_preds, gt_bboxes) - - # make sure that we are in element-wise multiplication - assert cls_cost.shape == overlaps.shape - - # overlaps is actually a cost matrix - overlaps = cls_cost**(1 - self.alpha) * overlaps**self.alpha - - # assign 0 by default - assigned_gt_inds = overlaps.new_full((num_bboxes, ), - 0, - dtype=torch.long) - - if num_gt == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - max_overlaps = overlaps.new_zeros((num_bboxes, )) - if num_gt == 0: - # No truth, assign everything to background - assigned_gt_inds[:] = 0 - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = overlaps.new_full((num_bboxes, ), - -1, - dtype=torch.long) - return AssignResult( - num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels) - - # compute center distance between all bbox and gt - gt_cx = (gt_bboxes[:, 0] + gt_bboxes[:, 2]) / 2.0 - gt_cy = (gt_bboxes[:, 1] + gt_bboxes[:, 3]) / 2.0 - gt_points = torch.stack((gt_cx, gt_cy), dim=1) - - bboxes_cx = (bboxes[:, 0] + bboxes[:, 2]) / 2.0 - bboxes_cy = (bboxes[:, 1] + bboxes[:, 3]) / 2.0 - bboxes_points = torch.stack((bboxes_cx, bboxes_cy), dim=1) - - distances = (bboxes_points[:, None, :] - - gt_points[None, :, :]).pow(2).sum(-1).sqrt() - - if (self.ignore_iof_thr > 0 and gt_bboxes_ignore is not None - and gt_bboxes_ignore.numel() > 0 and bboxes.numel() > 0): - ignore_overlaps = self.iou_calculator( - bboxes, gt_bboxes_ignore, mode='iof') - ignore_max_overlaps, _ = ignore_overlaps.max(dim=1) - ignore_idxs = ignore_max_overlaps > self.ignore_iof_thr - distances[ignore_idxs, :] = INF - assigned_gt_inds[ignore_idxs] = -1 - - # Selecting candidates based on the center distance - candidate_idxs = [] - start_idx = 0 - for level, bboxes_per_level in enumerate(num_level_bboxes): - # on each pyramid level, for each gt, - # select k bbox whose center are closest to the gt center - end_idx = start_idx + bboxes_per_level - distances_per_level = distances[start_idx:end_idx, :] - selectable_k = min(self.topk, bboxes_per_level) - - _, topk_idxs_per_level = distances_per_level.topk( - selectable_k, dim=0, largest=False) - candidate_idxs.append(topk_idxs_per_level + start_idx) - start_idx = end_idx - candidate_idxs = torch.cat(candidate_idxs, dim=0) - - # get corresponding iou for the these candidates, and compute the - # mean and std, set mean + std as the iou threshold - candidate_overlaps = overlaps[candidate_idxs, torch.arange(num_gt)] - overlaps_mean_per_gt = candidate_overlaps.mean(0) - overlaps_std_per_gt = candidate_overlaps.std(0) - overlaps_thr_per_gt = overlaps_mean_per_gt + overlaps_std_per_gt - - is_pos = candidate_overlaps >= overlaps_thr_per_gt[None, :] - - # limit the positive sample's center in gt - for gt_idx in range(num_gt): - candidate_idxs[:, gt_idx] += gt_idx * num_bboxes - ep_bboxes_cx = bboxes_cx.view(1, -1).expand( - num_gt, num_bboxes).contiguous().view(-1) - ep_bboxes_cy = bboxes_cy.view(1, -1).expand( - num_gt, num_bboxes).contiguous().view(-1) - candidate_idxs = candidate_idxs.view(-1) - - # calculate the left, top, right, bottom distance between positive - # bbox center and gt side - l_ = ep_bboxes_cx[candidate_idxs].view(-1, num_gt) - gt_bboxes[:, 0] - t_ = ep_bboxes_cy[candidate_idxs].view(-1, num_gt) - gt_bboxes[:, 1] - r_ = gt_bboxes[:, 2] - ep_bboxes_cx[candidate_idxs].view(-1, num_gt) - b_ = gt_bboxes[:, 3] - ep_bboxes_cy[candidate_idxs].view(-1, num_gt) - is_in_gts = torch.stack([l_, t_, r_, b_], dim=1).min(dim=1)[0] > 0.01 - - is_pos = is_pos & is_in_gts - - # if an anchor box is assigned to multiple gts, - # the one with the highest IoU will be selected. - overlaps_inf = torch.full_like(overlaps, - -INF).t().contiguous().view(-1) - index = candidate_idxs.view(-1)[is_pos.view(-1)] - overlaps_inf[index] = overlaps.t().contiguous().view(-1)[index] - overlaps_inf = overlaps_inf.view(num_gt, -1).t() - - max_overlaps, argmax_overlaps = overlaps_inf.max(dim=1) - assigned_gt_inds[ - max_overlaps != -INF] = argmax_overlaps[max_overlaps != -INF] + 1 - - if gt_labels is not None: - assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1) - pos_inds = torch.nonzero( - assigned_gt_inds > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[ - assigned_gt_inds[pos_inds] - 1] - else: - assigned_labels = None - return AssignResult( - num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/base_assigner.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/base_assigner.py deleted file mode 100644 index 3c2d597a5..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/base_assigner.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseAssigner(metaclass=ABCMeta): - """Base assigner that assigns boxes to ground truth boxes.""" - - @abstractmethod - def assign(self, bboxes, gt_bboxes, gt_bboxes_ignore=None, gt_labels=None): - """Assign boxes to either a ground truth boxes or a negative boxes.""" diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/center_region_assigner.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/center_region_assigner.py deleted file mode 100644 index 86e78597d..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/center_region_assigner.py +++ /dev/null @@ -1,336 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -def scale_boxes(bboxes, scale): - """Expand an array of boxes by a given scale. - - Args: - bboxes (Tensor): Shape (m, 4) - scale (float): The scale factor of bboxes - - Returns: - (Tensor): Shape (m, 4). Scaled bboxes - """ - assert bboxes.size(1) == 4 - w_half = (bboxes[:, 2] - bboxes[:, 0]) * .5 - h_half = (bboxes[:, 3] - bboxes[:, 1]) * .5 - x_c = (bboxes[:, 2] + bboxes[:, 0]) * .5 - y_c = (bboxes[:, 3] + bboxes[:, 1]) * .5 - - w_half *= scale - h_half *= scale - - boxes_scaled = torch.zeros_like(bboxes) - boxes_scaled[:, 0] = x_c - w_half - boxes_scaled[:, 2] = x_c + w_half - boxes_scaled[:, 1] = y_c - h_half - boxes_scaled[:, 3] = y_c + h_half - return boxes_scaled - - -def is_located_in(points, bboxes): - """Are points located in bboxes. - - Args: - points (Tensor): Points, shape: (m, 2). - bboxes (Tensor): Bounding boxes, shape: (n, 4). - - Return: - Tensor: Flags indicating if points are located in bboxes, shape: (m, n). - """ - assert points.size(1) == 2 - assert bboxes.size(1) == 4 - return (points[:, 0].unsqueeze(1) > bboxes[:, 0].unsqueeze(0)) & \ - (points[:, 0].unsqueeze(1) < bboxes[:, 2].unsqueeze(0)) & \ - (points[:, 1].unsqueeze(1) > bboxes[:, 1].unsqueeze(0)) & \ - (points[:, 1].unsqueeze(1) < bboxes[:, 3].unsqueeze(0)) - - -def bboxes_area(bboxes): - """Compute the area of an array of bboxes. - - Args: - bboxes (Tensor): The coordinates ox bboxes. Shape: (m, 4) - - Returns: - Tensor: Area of the bboxes. Shape: (m, ) - """ - assert bboxes.size(1) == 4 - w = (bboxes[:, 2] - bboxes[:, 0]) - h = (bboxes[:, 3] - bboxes[:, 1]) - areas = w * h - return areas - - -@BBOX_ASSIGNERS.register_module() -class CenterRegionAssigner(BaseAssigner): - """Assign pixels at the center region of a bbox as positive. - - Each proposals will be assigned with `-1`, `0`, or a positive integer - indicating the ground truth index. - - -1: negative samples - - semi-positive numbers: positive sample, index (0-based) of assigned gt - - Args: - pos_scale (float): Threshold within which pixels are - labelled as positive. - neg_scale (float): Threshold above which pixels are - labelled as positive. - min_pos_iof (float): Minimum iof of a pixel with a gt to be - labelled as positive. Default: 1e-2 - ignore_gt_scale (float): Threshold within which the pixels - are ignored when the gt is labelled as shadowed. Default: 0.5 - foreground_dominate (bool): If True, the bbox will be assigned as - positive when a gt's kernel region overlaps with another's shadowed - (ignored) region, otherwise it is set as ignored. Default to False. - """ - - def __init__(self, - pos_scale, - neg_scale, - min_pos_iof=1e-2, - ignore_gt_scale=0.5, - foreground_dominate=False, - iou_calculator=dict(type='BboxOverlaps2D')): - self.pos_scale = pos_scale - self.neg_scale = neg_scale - self.min_pos_iof = min_pos_iof - self.ignore_gt_scale = ignore_gt_scale - self.foreground_dominate = foreground_dominate - self.iou_calculator = build_iou_calculator(iou_calculator) - - def get_gt_priorities(self, gt_bboxes): - """Get gt priorities according to their areas. - - Smaller gt has higher priority. - - Args: - gt_bboxes (Tensor): Ground truth boxes, shape (k, 4). - - Returns: - Tensor: The priority of gts so that gts with larger priority is \ - more likely to be assigned. Shape (k, ) - """ - gt_areas = bboxes_area(gt_bboxes) - # Rank all gt bbox areas. Smaller objects has larger priority - _, sort_idx = gt_areas.sort(descending=True) - sort_idx = sort_idx.argsort() - return sort_idx - - def assign(self, bboxes, gt_bboxes, gt_bboxes_ignore=None, gt_labels=None): - """Assign gt to bboxes. - - This method assigns gts to every bbox (proposal/anchor), each bbox \ - will be assigned with -1, or a semi-positive number. -1 means \ - negative sample, semi-positive number is the index (0-based) of \ - assigned gt. - - Args: - bboxes (Tensor): Bounding boxes to be assigned, shape(n, 4). - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - gt_labels (tensor, optional): Label of gt_bboxes, shape (num_gts,). - - Returns: - :obj:`AssignResult`: The assigned result. Note that \ - shadowed_labels of shape (N, 2) is also added as an \ - `assign_result` attribute. `shadowed_labels` is a tensor \ - composed of N pairs of anchor_ind, class_label], where N \ - is the number of anchors that lie in the outer region of a \ - gt, anchor_ind is the shadowed anchor index and class_label \ - is the shadowed class label. - - Example: - >>> self = CenterRegionAssigner(0.2, 0.2) - >>> bboxes = torch.Tensor([[0, 0, 10, 10], [10, 10, 20, 20]]) - >>> gt_bboxes = torch.Tensor([[0, 0, 10, 10]]) - >>> assign_result = self.assign(bboxes, gt_bboxes) - >>> expected_gt_inds = torch.LongTensor([1, 0]) - >>> assert torch.all(assign_result.gt_inds == expected_gt_inds) - """ - # There are in total 5 steps in the pixel assignment - # 1. Find core (the center region, say inner 0.2) - # and shadow (the relatively ourter part, say inner 0.2-0.5) - # regions of every gt. - # 2. Find all prior bboxes that lie in gt_core and gt_shadow regions - # 3. Assign prior bboxes in gt_core with a one-hot id of the gt in - # the image. - # 3.1. For overlapping objects, the prior bboxes in gt_core is - # assigned with the object with smallest area - # 4. Assign prior bboxes with class label according to its gt id. - # 4.1. Assign -1 to prior bboxes lying in shadowed gts - # 4.2. Assign positive prior boxes with the corresponding label - # 5. Find pixels lying in the shadow of an object and assign them with - # background label, but set the loss weight of its corresponding - # gt to zero. - assert bboxes.size(1) == 4, 'bboxes must have size of 4' - # 1. Find core positive and shadow region of every gt - gt_core = scale_boxes(gt_bboxes, self.pos_scale) - gt_shadow = scale_boxes(gt_bboxes, self.neg_scale) - - # 2. Find prior bboxes that lie in gt_core and gt_shadow regions - bbox_centers = (bboxes[:, 2:4] + bboxes[:, 0:2]) / 2 - # The center points lie within the gt boxes - is_bbox_in_gt = is_located_in(bbox_centers, gt_bboxes) - # Only calculate bbox and gt_core IoF. This enables small prior bboxes - # to match large gts - bbox_and_gt_core_overlaps = self.iou_calculator( - bboxes, gt_core, mode='iof') - # The center point of effective priors should be within the gt box - is_bbox_in_gt_core = is_bbox_in_gt & ( - bbox_and_gt_core_overlaps > self.min_pos_iof) # shape (n, k) - - is_bbox_in_gt_shadow = ( - self.iou_calculator(bboxes, gt_shadow, mode='iof') > - self.min_pos_iof) - # Rule out center effective positive pixels - is_bbox_in_gt_shadow &= (~is_bbox_in_gt_core) - - num_gts, num_bboxes = gt_bboxes.size(0), bboxes.size(0) - if num_gts == 0 or num_bboxes == 0: - # If no gts exist, assign all pixels to negative - assigned_gt_ids = \ - is_bbox_in_gt_core.new_zeros((num_bboxes,), - dtype=torch.long) - pixels_in_gt_shadow = assigned_gt_ids.new_empty((0, 2)) - else: - # Step 3: assign a one-hot gt id to each pixel, and smaller objects - # have high priority to assign the pixel. - sort_idx = self.get_gt_priorities(gt_bboxes) - assigned_gt_ids, pixels_in_gt_shadow = \ - self.assign_one_hot_gt_indices(is_bbox_in_gt_core, - is_bbox_in_gt_shadow, - gt_priority=sort_idx) - - if gt_bboxes_ignore is not None and gt_bboxes_ignore.numel() > 0: - # No ground truth or boxes, return empty assignment - gt_bboxes_ignore = scale_boxes( - gt_bboxes_ignore, scale=self.ignore_gt_scale) - is_bbox_in_ignored_gts = is_located_in(bbox_centers, - gt_bboxes_ignore) - is_bbox_in_ignored_gts = is_bbox_in_ignored_gts.any(dim=1) - assigned_gt_ids[is_bbox_in_ignored_gts] = -1 - - # 4. Assign prior bboxes with class label according to its gt id. - assigned_labels = None - shadowed_pixel_labels = None - if gt_labels is not None: - # Default assigned label is the background (-1) - assigned_labels = assigned_gt_ids.new_full((num_bboxes, ), -1) - pos_inds = torch.nonzero( - assigned_gt_ids > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[assigned_gt_ids[pos_inds] - - 1] - # 5. Find pixels lying in the shadow of an object - shadowed_pixel_labels = pixels_in_gt_shadow.clone() - if pixels_in_gt_shadow.numel() > 0: - pixel_idx, gt_idx =\ - pixels_in_gt_shadow[:, 0], pixels_in_gt_shadow[:, 1] - assert (assigned_gt_ids[pixel_idx] != gt_idx).all(), \ - 'Some pixels are dually assigned to ignore and gt!' - shadowed_pixel_labels[:, 1] = gt_labels[gt_idx - 1] - override = ( - assigned_labels[pixel_idx] == shadowed_pixel_labels[:, 1]) - if self.foreground_dominate: - # When a pixel is both positive and shadowed, set it as pos - shadowed_pixel_labels = shadowed_pixel_labels[~override] - else: - # When a pixel is both pos and shadowed, set it as shadowed - assigned_labels[pixel_idx[override]] = -1 - assigned_gt_ids[pixel_idx[override]] = 0 - - assign_result = AssignResult( - num_gts, assigned_gt_ids, None, labels=assigned_labels) - # Add shadowed_labels as assign_result property. Shape: (num_shadow, 2) - assign_result.set_extra_property('shadowed_labels', - shadowed_pixel_labels) - return assign_result - - def assign_one_hot_gt_indices(self, - is_bbox_in_gt_core, - is_bbox_in_gt_shadow, - gt_priority=None): - """Assign only one gt index to each prior box. - - Gts with large gt_priority are more likely to be assigned. - - Args: - is_bbox_in_gt_core (Tensor): Bool tensor indicating the bbox center - is in the core area of a gt (e.g. 0-0.2). - Shape: (num_prior, num_gt). - is_bbox_in_gt_shadow (Tensor): Bool tensor indicating the bbox - center is in the shadowed area of a gt (e.g. 0.2-0.5). - Shape: (num_prior, num_gt). - gt_priority (Tensor): Priorities of gts. The gt with a higher - priority is more likely to be assigned to the bbox when the bbox - match with multiple gts. Shape: (num_gt, ). - - Returns: - tuple: Returns (assigned_gt_inds, shadowed_gt_inds). - - - assigned_gt_inds: The assigned gt index of each prior bbox \ - (i.e. index from 1 to num_gts). Shape: (num_prior, ). - - shadowed_gt_inds: shadowed gt indices. It is a tensor of \ - shape (num_ignore, 2) with first column being the \ - shadowed prior bbox indices and the second column the \ - shadowed gt indices (1-based). - """ - num_bboxes, num_gts = is_bbox_in_gt_core.shape - - if gt_priority is None: - gt_priority = torch.arange( - num_gts, device=is_bbox_in_gt_core.device) - assert gt_priority.size(0) == num_gts - # The bigger gt_priority, the more preferable to be assigned - # The assigned inds are by default 0 (background) - assigned_gt_inds = is_bbox_in_gt_core.new_zeros((num_bboxes, ), - dtype=torch.long) - # Shadowed bboxes are assigned to be background. But the corresponding - # label is ignored during loss calculation, which is done through - # shadowed_gt_inds - shadowed_gt_inds = torch.nonzero(is_bbox_in_gt_shadow, as_tuple=False) - if is_bbox_in_gt_core.sum() == 0: # No gt match - shadowed_gt_inds[:, 1] += 1 # 1-based. For consistency issue - return assigned_gt_inds, shadowed_gt_inds - - # The priority of each prior box and gt pair. If one prior box is - # matched bo multiple gts. Only the pair with the highest priority - # is saved - pair_priority = is_bbox_in_gt_core.new_full((num_bboxes, num_gts), - -1, - dtype=torch.long) - - # Each bbox could match with multiple gts. - # The following codes deal with this situation - # Matched bboxes (to any gt). Shape: (num_pos_anchor, ) - inds_of_match = torch.any(is_bbox_in_gt_core, dim=1) - # The matched gt index of each positive bbox. Length >= num_pos_anchor - # , since one bbox could match multiple gts - matched_bbox_gt_inds = torch.nonzero( - is_bbox_in_gt_core, as_tuple=False)[:, 1] - # Assign priority to each bbox-gt pair. - pair_priority[is_bbox_in_gt_core] = gt_priority[matched_bbox_gt_inds] - _, argmax_priority = pair_priority[inds_of_match].max(dim=1) - assigned_gt_inds[inds_of_match] = argmax_priority + 1 # 1-based - # Zero-out the assigned anchor box to filter the shadowed gt indices - is_bbox_in_gt_core[inds_of_match, argmax_priority] = 0 - # Concat the shadowed indices due to overlapping with that out side of - # effective scale. shape: (total_num_ignore, 2) - shadowed_gt_inds = torch.cat( - (shadowed_gt_inds, torch.nonzero( - is_bbox_in_gt_core, as_tuple=False)), - dim=0) - # `is_bbox_in_gt_core` should be changed back to keep arguments intact. - is_bbox_in_gt_core[inds_of_match, argmax_priority] = 1 - # 1-based shadowed gt indices, to be consistent with `assigned_gt_inds` - if shadowed_gt_inds.numel() > 0: - shadowed_gt_inds[:, 1] += 1 - return assigned_gt_inds, shadowed_gt_inds diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/grid_assigner.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/grid_assigner.py deleted file mode 100644 index a0c814e78..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/grid_assigner.py +++ /dev/null @@ -1,156 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -@BBOX_ASSIGNERS.register_module() -class GridAssigner(BaseAssigner): - """Assign a corresponding gt bbox or background to each bbox. - - Each proposals will be assigned with `-1`, `0`, or a positive integer - indicating the ground truth index. - - - -1: don't care - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - - Args: - pos_iou_thr (float): IoU threshold for positive bboxes. - neg_iou_thr (float or tuple): IoU threshold for negative bboxes. - min_pos_iou (float): Minimum iou for a bbox to be considered as a - positive bbox. Positive samples can have smaller IoU than - pos_iou_thr due to the 4th step (assign max IoU sample to each gt). - gt_max_assign_all (bool): Whether to assign all bboxes with the same - highest overlap with some gt to that gt. - """ - - def __init__(self, - pos_iou_thr, - neg_iou_thr, - min_pos_iou=.0, - gt_max_assign_all=True, - iou_calculator=dict(type='BboxOverlaps2D')): - self.pos_iou_thr = pos_iou_thr - self.neg_iou_thr = neg_iou_thr - self.min_pos_iou = min_pos_iou - self.gt_max_assign_all = gt_max_assign_all - self.iou_calculator = build_iou_calculator(iou_calculator) - - def assign(self, bboxes, box_responsible_flags, gt_bboxes, gt_labels=None): - """Assign gt to bboxes. The process is very much like the max iou - assigner, except that positive samples are constrained within the cell - that the gt boxes fell in. - - This method assign a gt bbox to every bbox (proposal/anchor), each bbox - will be assigned with -1, 0, or a positive number. -1 means don't care, - 0 means negative sample, positive number is the index (1-based) of - assigned gt. - The assignment is done in following steps, the order matters. - - 1. assign every bbox to -1 - 2. assign proposals whose iou with all gts <= neg_iou_thr to 0 - 3. for each bbox within a cell, if the iou with its nearest gt > - pos_iou_thr and the center of that gt falls inside the cell, - assign it to that bbox - 4. for each gt bbox, assign its nearest proposals within the cell the - gt bbox falls in to itself. - - Args: - bboxes (Tensor): Bounding boxes to be assigned, shape(n, 4). - box_responsible_flags (Tensor): flag to indicate whether box is - responsible for prediction, shape(n, ) - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - - Returns: - :obj:`AssignResult`: The assign result. - """ - num_gts, num_bboxes = gt_bboxes.size(0), bboxes.size(0) - - # compute iou between all gt and bboxes - overlaps = self.iou_calculator(gt_bboxes, bboxes) - - # 1. assign -1 by default - assigned_gt_inds = overlaps.new_full((num_bboxes, ), - -1, - dtype=torch.long) - - if num_gts == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - max_overlaps = overlaps.new_zeros((num_bboxes, )) - if num_gts == 0: - # No truth, assign everything to background - assigned_gt_inds[:] = 0 - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = overlaps.new_full((num_bboxes, ), - -1, - dtype=torch.long) - return AssignResult( - num_gts, - assigned_gt_inds, - max_overlaps, - labels=assigned_labels) - - # 2. assign negative: below - # for each anchor, which gt best overlaps with it - # for each anchor, the max iou of all gts - # shape of max_overlaps == argmax_overlaps == num_bboxes - max_overlaps, argmax_overlaps = overlaps.max(dim=0) - - if isinstance(self.neg_iou_thr, float): - assigned_gt_inds[(max_overlaps >= 0) - & (max_overlaps <= self.neg_iou_thr)] = 0 - elif isinstance(self.neg_iou_thr, (tuple, list)): - assert len(self.neg_iou_thr) == 2 - assigned_gt_inds[(max_overlaps > self.neg_iou_thr[0]) - & (max_overlaps <= self.neg_iou_thr[1])] = 0 - - # 3. assign positive: falls into responsible cell and above - # positive IOU threshold, the order matters. - # the prior condition of comparison is to filter out all - # unrelated anchors, i.e. not box_responsible_flags - overlaps[:, ~box_responsible_flags.type(torch.bool)] = -1. - - # calculate max_overlaps again, but this time we only consider IOUs - # for anchors responsible for prediction - max_overlaps, argmax_overlaps = overlaps.max(dim=0) - - # for each gt, which anchor best overlaps with it - # for each gt, the max iou of all proposals - # shape of gt_max_overlaps == gt_argmax_overlaps == num_gts - gt_max_overlaps, gt_argmax_overlaps = overlaps.max(dim=1) - - pos_inds = (max_overlaps > - self.pos_iou_thr) & box_responsible_flags.type(torch.bool) - assigned_gt_inds[pos_inds] = argmax_overlaps[pos_inds] + 1 - - # 4. assign positive to max overlapped anchors within responsible cell - for i in range(num_gts): - if gt_max_overlaps[i] > self.min_pos_iou: - if self.gt_max_assign_all: - max_iou_inds = (overlaps[i, :] == gt_max_overlaps[i]) & \ - box_responsible_flags.type(torch.bool) - assigned_gt_inds[max_iou_inds] = i + 1 - elif box_responsible_flags[gt_argmax_overlaps[i]]: - assigned_gt_inds[gt_argmax_overlaps[i]] = i + 1 - - # assign labels of positive anchors - if gt_labels is not None: - assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1) - pos_inds = torch.nonzero( - assigned_gt_inds > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[ - assigned_gt_inds[pos_inds] - 1] - - else: - assigned_labels = None - - return AssignResult( - num_gts, assigned_gt_inds, max_overlaps, labels=assigned_labels) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/hungarian_assigner.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/hungarian_assigner.py deleted file mode 100644 index 4105fb5c4..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/hungarian_assigner.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..match_costs import build_match_cost -from ..transforms import bbox_cxcywh_to_xyxy -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - -try: - from scipy.optimize import linear_sum_assignment -except ImportError: - linear_sum_assignment = None - - -@BBOX_ASSIGNERS.register_module() -class HungarianAssigner(BaseAssigner): - """Computes one-to-one matching between predictions and ground truth. - - This class computes an assignment between the targets and the predictions - based on the costs. The costs are weighted sum of three components: - classification cost, regression L1 cost and regression iou cost. The - targets don't include the no_object, so generally there are more - predictions than targets. After the one-to-one matching, the un-matched - are treated as backgrounds. Thus each query prediction will be assigned - with `0` or a positive integer indicating the ground truth index: - - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - - Args: - cls_weight (int | float, optional): The scale factor for classification - cost. Default 1.0. - bbox_weight (int | float, optional): The scale factor for regression - L1 cost. Default 1.0. - iou_weight (int | float, optional): The scale factor for regression - iou cost. Default 1.0. - iou_calculator (dict | optional): The config for the iou calculation. - Default type `BboxOverlaps2D`. - iou_mode (str | optional): "iou" (intersection over union), "iof" - (intersection over foreground), or "giou" (generalized - intersection over union). Default "giou". - """ - - def __init__(self, - cls_cost=dict(type='ClassificationCost', weight=1.), - reg_cost=dict(type='BBoxL1Cost', weight=1.0), - iou_cost=dict(type='IoUCost', iou_mode='giou', weight=1.0)): - self.cls_cost = build_match_cost(cls_cost) - self.reg_cost = build_match_cost(reg_cost) - self.iou_cost = build_match_cost(iou_cost) - - def assign(self, - bbox_pred, - cls_pred, - gt_bboxes, - gt_labels, - img_meta, - gt_bboxes_ignore=None, - eps=1e-7): - """Computes one-to-one matching based on the weighted costs. - - This method assign each query prediction to a ground truth or - background. The `assigned_gt_inds` with -1 means don't care, - 0 means negative sample, and positive number is the index (1-based) - of assigned gt. - The assignment is done in the following steps, the order matters. - - 1. assign every prediction to -1 - 2. compute the weighted costs - 3. do Hungarian matching on CPU based on the costs - 4. assign all to 0 (background) first, then for each matched pair - between predictions and gts, treat this prediction as foreground - and assign the corresponding gt index (plus 1) to it. - - Args: - bbox_pred (Tensor): Predicted boxes with normalized coordinates - (cx, cy, w, h), which are all in range [0, 1]. Shape - [num_query, 4]. - cls_pred (Tensor): Predicted classification logits, shape - [num_query, num_class]. - gt_bboxes (Tensor): Ground truth boxes with unnormalized - coordinates (x1, y1, x2, y2). Shape [num_gt, 4]. - gt_labels (Tensor): Label of `gt_bboxes`, shape (num_gt,). - img_meta (dict): Meta information for current image. - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`. Default None. - eps (int | float, optional): A value added to the denominator for - numerical stability. Default 1e-7. - - Returns: - :obj:`AssignResult`: The assigned result. - """ - assert gt_bboxes_ignore is None, \ - 'Only case when gt_bboxes_ignore is None is supported.' - num_gts, num_bboxes = gt_bboxes.size(0), bbox_pred.size(0) - - # 1. assign -1 by default - assigned_gt_inds = bbox_pred.new_full((num_bboxes, ), - -1, - dtype=torch.long) - assigned_labels = bbox_pred.new_full((num_bboxes, ), - -1, - dtype=torch.long) - if num_gts == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - if num_gts == 0: - # No ground truth, assign all to background - assigned_gt_inds[:] = 0 - return AssignResult( - num_gts, assigned_gt_inds, None, labels=assigned_labels) - img_h, img_w, _ = img_meta['img_shape'] - factor = gt_bboxes.new_tensor([img_w, img_h, img_w, - img_h]).unsqueeze(0) - - # 2. compute the weighted costs - # classification and bboxcost. - cls_cost = self.cls_cost(cls_pred, gt_labels) - # regression L1 cost - normalize_gt_bboxes = gt_bboxes / factor - reg_cost = self.reg_cost(bbox_pred, normalize_gt_bboxes) - # regression iou cost, defaultly giou is used in official DETR. - bboxes = bbox_cxcywh_to_xyxy(bbox_pred) * factor - iou_cost = self.iou_cost(bboxes, gt_bboxes) - # weighted sum of above three costs - cost = cls_cost + reg_cost + iou_cost - - # 3. do Hungarian matching on CPU using linear_sum_assignment - cost = cost.detach().cpu() - if linear_sum_assignment is None: - raise ImportError('Please run "pip install scipy" ' - 'to install scipy first.') - matched_row_inds, matched_col_inds = linear_sum_assignment(cost) - matched_row_inds = torch.from_numpy(matched_row_inds).to( - bbox_pred.device) - matched_col_inds = torch.from_numpy(matched_col_inds).to( - bbox_pred.device) - - # 4. assign backgrounds and foregrounds - # assign all indices to backgrounds first - assigned_gt_inds[:] = 0 - # assign foregrounds based on matching results - assigned_gt_inds[matched_row_inds] = matched_col_inds + 1 - assigned_labels[matched_row_inds] = gt_labels[matched_col_inds] - return AssignResult( - num_gts, assigned_gt_inds, None, labels=assigned_labels) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/mask_hungarian_assigner.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/mask_hungarian_assigner.py deleted file mode 100644 index f5f27f3f5..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/mask_hungarian_assigner.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmdet.core.bbox.builder import BBOX_ASSIGNERS -from mmdet.core.bbox.match_costs.builder import build_match_cost -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - -try: - from scipy.optimize import linear_sum_assignment -except ImportError: - linear_sum_assignment = None - - -@BBOX_ASSIGNERS.register_module() -class MaskHungarianAssigner(BaseAssigner): - """Computes one-to-one matching between predictions and ground truth for - mask. - - This class computes an assignment between the targets and the predictions - based on the costs. The costs are weighted sum of three components: - classification cost, mask focal cost and mask dice cost. The - targets don't include the no_object, so generally there are more - predictions than targets. After the one-to-one matching, the un-matched - are treated as backgrounds. Thus each query prediction will be assigned - with `0` or a positive integer indicating the ground truth index: - - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - - Args: - cls_cost (:obj:`mmcv.ConfigDict` | dict): Classification cost config. - mask_cost (:obj:`mmcv.ConfigDict` | dict): Mask cost config. - dice_cost (:obj:`mmcv.ConfigDict` | dict): Dice cost config. - """ - - def __init__(self, - cls_cost=dict(type='ClassificationCost', weight=1.0), - mask_cost=dict( - type='FocalLossCost', weight=1.0, binary_input=True), - dice_cost=dict(type='DiceCost', weight=1.0)): - self.cls_cost = build_match_cost(cls_cost) - self.mask_cost = build_match_cost(mask_cost) - self.dice_cost = build_match_cost(dice_cost) - - def assign(self, - cls_pred, - mask_pred, - gt_labels, - gt_mask, - img_meta, - gt_bboxes_ignore=None, - eps=1e-7): - """Computes one-to-one matching based on the weighted costs. - - Args: - cls_pred (Tensor | None): Class prediction in shape - (num_query, cls_out_channels). - mask_pred (Tensor): Mask prediction in shape (num_query, H, W). - gt_labels (Tensor): Label of 'gt_mask'in shape = (num_gt, ). - gt_mask (Tensor): Ground truth mask in shape = (num_gt, H, W). - img_meta (dict): Meta information for current image. - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`. Default None. - eps (int | float, optional): A value added to the denominator for - numerical stability. Default 1e-7. - - Returns: - :obj:`AssignResult`: The assigned result. - """ - assert gt_bboxes_ignore is None, \ - 'Only case when gt_bboxes_ignore is None is supported.' - # K-Net sometimes passes cls_pred=None to this assigner. - # So we should use the shape of mask_pred - num_gt, num_query = gt_labels.shape[0], mask_pred.shape[0] - - # 1. assign -1 by default - assigned_gt_inds = mask_pred.new_full((num_query, ), - -1, - dtype=torch.long) - assigned_labels = mask_pred.new_full((num_query, ), - -1, - dtype=torch.long) - if num_gt == 0 or num_query == 0: - # No ground truth or boxes, return empty assignment - if num_gt == 0: - # No ground truth, assign all to background - assigned_gt_inds[:] = 0 - return AssignResult( - num_gt, assigned_gt_inds, None, labels=assigned_labels) - - # 2. compute the weighted costs - # classification and maskcost. - if self.cls_cost.weight != 0 and cls_pred is not None: - cls_cost = self.cls_cost(cls_pred, gt_labels) - else: - cls_cost = 0 - - if self.mask_cost.weight != 0: - # mask_pred shape = [num_query, h, w] - # gt_mask shape = [num_gt, h, w] - # mask_cost shape = [num_query, num_gt] - mask_cost = self.mask_cost(mask_pred, gt_mask) - else: - mask_cost = 0 - - if self.dice_cost.weight != 0: - dice_cost = self.dice_cost(mask_pred, gt_mask) - else: - dice_cost = 0 - cost = cls_cost + mask_cost + dice_cost - - # 3. do Hungarian matching on CPU using linear_sum_assignment - cost = cost.detach().cpu() - if linear_sum_assignment is None: - raise ImportError('Please run "pip install scipy" ' - 'to install scipy first.') - - matched_row_inds, matched_col_inds = linear_sum_assignment(cost) - matched_row_inds = torch.from_numpy(matched_row_inds).to( - mask_pred.device) - matched_col_inds = torch.from_numpy(matched_col_inds).to( - mask_pred.device) - - # 4. assign backgrounds and foregrounds - # assign all indices to backgrounds first - assigned_gt_inds[:] = 0 - # assign foregrounds based on matching results - assigned_gt_inds[matched_row_inds] = matched_col_inds + 1 - assigned_labels[matched_row_inds] = gt_labels[matched_col_inds] - return AssignResult( - num_gt, assigned_gt_inds, None, labels=assigned_labels) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/max_iou_assigner.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/max_iou_assigner.py deleted file mode 100644 index 676421f76..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/max_iou_assigner.py +++ /dev/null @@ -1,218 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -@BBOX_ASSIGNERS.register_module() -class MaxIoUAssigner(BaseAssigner): - """Assign a corresponding gt bbox or background to each bbox. - - Each proposals will be assigned with `-1`, or a semi-positive integer - indicating the ground truth index. - - - -1: negative sample, no assigned gt - - semi-positive integer: positive sample, index (0-based) of assigned gt - - Args: - pos_iou_thr (float): IoU threshold for positive bboxes. - neg_iou_thr (float or tuple): IoU threshold for negative bboxes. - min_pos_iou (float): Minimum iou for a bbox to be considered as a - positive bbox. Positive samples can have smaller IoU than - pos_iou_thr due to the 4th step (assign max IoU sample to each gt). - `min_pos_iou` is set to avoid assigning bboxes that have extremely - small iou with GT as positive samples. It brings about 0.3 mAP - improvements in 1x schedule but does not affect the performance of - 3x schedule. More comparisons can be found in - `PR #7464 `_. - gt_max_assign_all (bool): Whether to assign all bboxes with the same - highest overlap with some gt to that gt. - ignore_iof_thr (float): IoF threshold for ignoring bboxes (if - `gt_bboxes_ignore` is specified). Negative values mean not - ignoring any bboxes. - ignore_wrt_candidates (bool): Whether to compute the iof between - `bboxes` and `gt_bboxes_ignore`, or the contrary. - match_low_quality (bool): Whether to allow low quality matches. This is - usually allowed for RPN and single stage detectors, but not allowed - in the second stage. Details are demonstrated in Step 4. - gpu_assign_thr (int): The upper bound of the number of GT for GPU - assign. When the number of gt is above this threshold, will assign - on CPU device. Negative values mean not assign on CPU. - """ - - def __init__(self, - pos_iou_thr, - neg_iou_thr, - min_pos_iou=.0, - gt_max_assign_all=True, - ignore_iof_thr=-1, - ignore_wrt_candidates=True, - match_low_quality=True, - gpu_assign_thr=-1, - iou_calculator=dict(type='BboxOverlaps2D')): - self.pos_iou_thr = pos_iou_thr - self.neg_iou_thr = neg_iou_thr - self.min_pos_iou = min_pos_iou - self.gt_max_assign_all = gt_max_assign_all - self.ignore_iof_thr = ignore_iof_thr - self.ignore_wrt_candidates = ignore_wrt_candidates - self.gpu_assign_thr = gpu_assign_thr - self.match_low_quality = match_low_quality - self.iou_calculator = build_iou_calculator(iou_calculator) - - def assign(self, bboxes, gt_bboxes, gt_bboxes_ignore=None, gt_labels=None): - """Assign gt to bboxes. - - This method assign a gt bbox to every bbox (proposal/anchor), each bbox - will be assigned with -1, or a semi-positive number. -1 means negative - sample, semi-positive number is the index (0-based) of assigned gt. - The assignment is done in following steps, the order matters. - - 1. assign every bbox to the background - 2. assign proposals whose iou with all gts < neg_iou_thr to 0 - 3. for each bbox, if the iou with its nearest gt >= pos_iou_thr, - assign it to that bbox - 4. for each gt bbox, assign its nearest proposals (may be more than - one) to itself - - Args: - bboxes (Tensor): Bounding boxes to be assigned, shape(n, 4). - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - - Returns: - :obj:`AssignResult`: The assign result. - - Example: - >>> self = MaxIoUAssigner(0.5, 0.5) - >>> bboxes = torch.Tensor([[0, 0, 10, 10], [10, 10, 20, 20]]) - >>> gt_bboxes = torch.Tensor([[0, 0, 10, 9]]) - >>> assign_result = self.assign(bboxes, gt_bboxes) - >>> expected_gt_inds = torch.LongTensor([1, 0]) - >>> assert torch.all(assign_result.gt_inds == expected_gt_inds) - """ - assign_on_cpu = True if (self.gpu_assign_thr > 0) and ( - gt_bboxes.shape[0] > self.gpu_assign_thr) else False - # compute overlap and assign gt on CPU when number of GT is large - if assign_on_cpu: - device = bboxes.device - bboxes = bboxes.cpu() - gt_bboxes = gt_bboxes.cpu() - if gt_bboxes_ignore is not None: - gt_bboxes_ignore = gt_bboxes_ignore.cpu() - if gt_labels is not None: - gt_labels = gt_labels.cpu() - - overlaps = self.iou_calculator(gt_bboxes, bboxes) - - if (self.ignore_iof_thr > 0 and gt_bboxes_ignore is not None - and gt_bboxes_ignore.numel() > 0 and bboxes.numel() > 0): - if self.ignore_wrt_candidates: - ignore_overlaps = self.iou_calculator( - bboxes, gt_bboxes_ignore, mode='iof') - ignore_max_overlaps, _ = ignore_overlaps.max(dim=1) - else: - ignore_overlaps = self.iou_calculator( - gt_bboxes_ignore, bboxes, mode='iof') - ignore_max_overlaps, _ = ignore_overlaps.max(dim=0) - overlaps[:, ignore_max_overlaps > self.ignore_iof_thr] = -1 - - assign_result = self.assign_wrt_overlaps(overlaps, gt_labels) - if assign_on_cpu: - assign_result.gt_inds = assign_result.gt_inds.to(device) - assign_result.max_overlaps = assign_result.max_overlaps.to(device) - if assign_result.labels is not None: - assign_result.labels = assign_result.labels.to(device) - return assign_result - - def assign_wrt_overlaps(self, overlaps, gt_labels=None): - """Assign w.r.t. the overlaps of bboxes with gts. - - Args: - overlaps (Tensor): Overlaps between k gt_bboxes and n bboxes, - shape(k, n). - gt_labels (Tensor, optional): Labels of k gt_bboxes, shape (k, ). - - Returns: - :obj:`AssignResult`: The assign result. - """ - num_gts, num_bboxes = overlaps.size(0), overlaps.size(1) - - # 1. assign -1 by default - assigned_gt_inds = overlaps.new_full((num_bboxes, ), - -1, - dtype=torch.long) - - if num_gts == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - max_overlaps = overlaps.new_zeros((num_bboxes, )) - if num_gts == 0: - # No truth, assign everything to background - assigned_gt_inds[:] = 0 - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = overlaps.new_full((num_bboxes, ), - -1, - dtype=torch.long) - return AssignResult( - num_gts, - assigned_gt_inds, - max_overlaps, - labels=assigned_labels) - - # for each anchor, which gt best overlaps with it - # for each anchor, the max iou of all gts - max_overlaps, argmax_overlaps = overlaps.max(dim=0) - # for each gt, which anchor best overlaps with it - # for each gt, the max iou of all proposals - gt_max_overlaps, gt_argmax_overlaps = overlaps.max(dim=1) - - # 2. assign negative: below - # the negative inds are set to be 0 - if isinstance(self.neg_iou_thr, float): - assigned_gt_inds[(max_overlaps >= 0) - & (max_overlaps < self.neg_iou_thr)] = 0 - elif isinstance(self.neg_iou_thr, tuple): - assert len(self.neg_iou_thr) == 2 - assigned_gt_inds[(max_overlaps >= self.neg_iou_thr[0]) - & (max_overlaps < self.neg_iou_thr[1])] = 0 - - # 3. assign positive: above positive IoU threshold - pos_inds = max_overlaps >= self.pos_iou_thr - assigned_gt_inds[pos_inds] = argmax_overlaps[pos_inds] + 1 - - if self.match_low_quality: - # Low-quality matching will overwrite the assigned_gt_inds assigned - # in Step 3. Thus, the assigned gt might not be the best one for - # prediction. - # For example, if bbox A has 0.9 and 0.8 iou with GT bbox 1 & 2, - # bbox 1 will be assigned as the best target for bbox A in step 3. - # However, if GT bbox 2's gt_argmax_overlaps = A, bbox A's - # assigned_gt_inds will be overwritten to be bbox 2. - # This might be the reason that it is not used in ROI Heads. - for i in range(num_gts): - if gt_max_overlaps[i] >= self.min_pos_iou: - if self.gt_max_assign_all: - max_iou_inds = overlaps[i, :] == gt_max_overlaps[i] - assigned_gt_inds[max_iou_inds] = i + 1 - else: - assigned_gt_inds[gt_argmax_overlaps[i]] = i + 1 - - if gt_labels is not None: - assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1) - pos_inds = torch.nonzero( - assigned_gt_inds > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[ - assigned_gt_inds[pos_inds] - 1] - else: - assigned_labels = None - - return AssignResult( - num_gts, assigned_gt_inds, max_overlaps, labels=assigned_labels) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/point_assigner.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/point_assigner.py deleted file mode 100644 index b0dc22463..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/point_assigner.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -@BBOX_ASSIGNERS.register_module() -class PointAssigner(BaseAssigner): - """Assign a corresponding gt bbox or background to each point. - - Each proposals will be assigned with `0`, or a positive integer - indicating the ground truth index. - - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - """ - - def __init__(self, scale=4, pos_num=3): - self.scale = scale - self.pos_num = pos_num - - def assign(self, points, gt_bboxes, gt_bboxes_ignore=None, gt_labels=None): - """Assign gt to points. - - This method assign a gt bbox to every points set, each points set - will be assigned with the background_label (-1), or a label number. - -1 is background, and semi-positive number is the index (0-based) of - assigned gt. - The assignment is done in following steps, the order matters. - - 1. assign every points to the background_label (-1) - 2. A point is assigned to some gt bbox if - (i) the point is within the k closest points to the gt bbox - (ii) the distance between this point and the gt is smaller than - other gt bboxes - - Args: - points (Tensor): points to be assigned, shape(n, 3) while last - dimension stands for (x, y, stride). - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - NOTE: currently unused. - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - - Returns: - :obj:`AssignResult`: The assign result. - """ - num_points = points.shape[0] - num_gts = gt_bboxes.shape[0] - - if num_gts == 0 or num_points == 0: - # If no truth assign everything to the background - assigned_gt_inds = points.new_full((num_points, ), - 0, - dtype=torch.long) - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = points.new_full((num_points, ), - -1, - dtype=torch.long) - return AssignResult( - num_gts, assigned_gt_inds, None, labels=assigned_labels) - - points_xy = points[:, :2] - points_stride = points[:, 2] - points_lvl = torch.log2( - points_stride).int() # [3...,4...,5...,6...,7...] - lvl_min, lvl_max = points_lvl.min(), points_lvl.max() - - # assign gt box - gt_bboxes_xy = (gt_bboxes[:, :2] + gt_bboxes[:, 2:]) / 2 - gt_bboxes_wh = (gt_bboxes[:, 2:] - gt_bboxes[:, :2]).clamp(min=1e-6) - scale = self.scale - gt_bboxes_lvl = ((torch.log2(gt_bboxes_wh[:, 0] / scale) + - torch.log2(gt_bboxes_wh[:, 1] / scale)) / 2).int() - gt_bboxes_lvl = torch.clamp(gt_bboxes_lvl, min=lvl_min, max=lvl_max) - - # stores the assigned gt index of each point - assigned_gt_inds = points.new_zeros((num_points, ), dtype=torch.long) - # stores the assigned gt dist (to this point) of each point - assigned_gt_dist = points.new_full((num_points, ), float('inf')) - points_range = torch.arange(points.shape[0]) - - for idx in range(num_gts): - gt_lvl = gt_bboxes_lvl[idx] - # get the index of points in this level - lvl_idx = gt_lvl == points_lvl - points_index = points_range[lvl_idx] - # get the points in this level - lvl_points = points_xy[lvl_idx, :] - # get the center point of gt - gt_point = gt_bboxes_xy[[idx], :] - # get width and height of gt - gt_wh = gt_bboxes_wh[[idx], :] - # compute the distance between gt center and - # all points in this level - points_gt_dist = ((lvl_points - gt_point) / gt_wh).norm(dim=1) - # find the nearest k points to gt center in this level - min_dist, min_dist_index = torch.topk( - points_gt_dist, self.pos_num, largest=False) - # the index of nearest k points to gt center in this level - min_dist_points_index = points_index[min_dist_index] - # The less_than_recorded_index stores the index - # of min_dist that is less then the assigned_gt_dist. Where - # assigned_gt_dist stores the dist from previous assigned gt - # (if exist) to each point. - less_than_recorded_index = min_dist < assigned_gt_dist[ - min_dist_points_index] - # The min_dist_points_index stores the index of points satisfy: - # (1) it is k nearest to current gt center in this level. - # (2) it is closer to current gt center than other gt center. - min_dist_points_index = min_dist_points_index[ - less_than_recorded_index] - # assign the result - assigned_gt_inds[min_dist_points_index] = idx + 1 - assigned_gt_dist[min_dist_points_index] = min_dist[ - less_than_recorded_index] - - if gt_labels is not None: - assigned_labels = assigned_gt_inds.new_full((num_points, ), -1) - pos_inds = torch.nonzero( - assigned_gt_inds > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[ - assigned_gt_inds[pos_inds] - 1] - else: - assigned_labels = None - - return AssignResult( - num_gts, assigned_gt_inds, None, labels=assigned_labels) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/region_assigner.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/region_assigner.py deleted file mode 100644 index 1833b8941..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/region_assigner.py +++ /dev/null @@ -1,222 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmdet.core import anchor_inside_flags -from ..builder import BBOX_ASSIGNERS -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -def calc_region(bbox, ratio, stride, featmap_size=None): - """Calculate region of the box defined by the ratio, the ratio is from the - center of the box to every edge.""" - # project bbox on the feature - f_bbox = bbox / stride - x1 = torch.round((1 - ratio) * f_bbox[0] + ratio * f_bbox[2]) - y1 = torch.round((1 - ratio) * f_bbox[1] + ratio * f_bbox[3]) - x2 = torch.round(ratio * f_bbox[0] + (1 - ratio) * f_bbox[2]) - y2 = torch.round(ratio * f_bbox[1] + (1 - ratio) * f_bbox[3]) - if featmap_size is not None: - x1 = x1.clamp(min=0, max=featmap_size[1]) - y1 = y1.clamp(min=0, max=featmap_size[0]) - x2 = x2.clamp(min=0, max=featmap_size[1]) - y2 = y2.clamp(min=0, max=featmap_size[0]) - return (x1, y1, x2, y2) - - -def anchor_ctr_inside_region_flags(anchors, stride, region): - """Get the flag indicate whether anchor centers are inside regions.""" - x1, y1, x2, y2 = region - f_anchors = anchors / stride - x = (f_anchors[:, 0] + f_anchors[:, 2]) * 0.5 - y = (f_anchors[:, 1] + f_anchors[:, 3]) * 0.5 - flags = (x >= x1) & (x <= x2) & (y >= y1) & (y <= y2) - return flags - - -@BBOX_ASSIGNERS.register_module() -class RegionAssigner(BaseAssigner): - """Assign a corresponding gt bbox or background to each bbox. - - Each proposals will be assigned with `-1`, `0`, or a positive integer - indicating the ground truth index. - - - -1: don't care - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - - Args: - center_ratio: ratio of the region in the center of the bbox to - define positive sample. - ignore_ratio: ratio of the region to define ignore samples. - """ - - def __init__(self, center_ratio=0.2, ignore_ratio=0.5): - self.center_ratio = center_ratio - self.ignore_ratio = ignore_ratio - - def assign(self, - mlvl_anchors, - mlvl_valid_flags, - gt_bboxes, - img_meta, - featmap_sizes, - anchor_scale, - anchor_strides, - gt_bboxes_ignore=None, - gt_labels=None, - allowed_border=0): - """Assign gt to anchors. - - This method assign a gt bbox to every bbox (proposal/anchor), each bbox - will be assigned with -1, 0, or a positive number. -1 means don't care, - 0 means negative sample, positive number is the index (1-based) of - assigned gt. - - The assignment is done in following steps, and the order matters. - - 1. Assign every anchor to 0 (negative) - 2. (For each gt_bboxes) Compute ignore flags based on ignore_region - then assign -1 to anchors w.r.t. ignore flags - 3. (For each gt_bboxes) Compute pos flags based on center_region then - assign gt_bboxes to anchors w.r.t. pos flags - 4. (For each gt_bboxes) Compute ignore flags based on adjacent anchor - level then assign -1 to anchors w.r.t. ignore flags - 5. Assign anchor outside of image to -1 - - Args: - mlvl_anchors (list[Tensor]): Multi level anchors. - mlvl_valid_flags (list[Tensor]): Multi level valid flags. - gt_bboxes (Tensor): Ground truth bboxes of image - img_meta (dict): Meta info of image. - featmap_sizes (list[Tensor]): Feature mapsize each level - anchor_scale (int): Scale of the anchor. - anchor_strides (list[int]): Stride of the anchor. - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - allowed_border (int, optional): The border to allow the valid - anchor. Defaults to 0. - - Returns: - :obj:`AssignResult`: The assign result. - """ - if gt_bboxes_ignore is not None: - raise NotImplementedError - - num_gts = gt_bboxes.shape[0] - num_bboxes = sum(x.shape[0] for x in mlvl_anchors) - - if num_gts == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - max_overlaps = gt_bboxes.new_zeros((num_bboxes, )) - assigned_gt_inds = gt_bboxes.new_zeros((num_bboxes, ), - dtype=torch.long) - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = gt_bboxes.new_full((num_bboxes, ), - -1, - dtype=torch.long) - return AssignResult( - num_gts, - assigned_gt_inds, - max_overlaps, - labels=assigned_labels) - - num_lvls = len(mlvl_anchors) - r1 = (1 - self.center_ratio) / 2 - r2 = (1 - self.ignore_ratio) / 2 - - scale = torch.sqrt((gt_bboxes[:, 2] - gt_bboxes[:, 0]) * - (gt_bboxes[:, 3] - gt_bboxes[:, 1])) - min_anchor_size = scale.new_full( - (1, ), float(anchor_scale * anchor_strides[0])) - target_lvls = torch.floor( - torch.log2(scale) - torch.log2(min_anchor_size) + 0.5) - target_lvls = target_lvls.clamp(min=0, max=num_lvls - 1).long() - - # 1. assign 0 (negative) by default - mlvl_assigned_gt_inds = [] - mlvl_ignore_flags = [] - for lvl in range(num_lvls): - h, w = featmap_sizes[lvl] - assert h * w == mlvl_anchors[lvl].shape[0] - assigned_gt_inds = gt_bboxes.new_full((h * w, ), - 0, - dtype=torch.long) - ignore_flags = torch.zeros_like(assigned_gt_inds) - mlvl_assigned_gt_inds.append(assigned_gt_inds) - mlvl_ignore_flags.append(ignore_flags) - - for gt_id in range(num_gts): - lvl = target_lvls[gt_id].item() - featmap_size = featmap_sizes[lvl] - stride = anchor_strides[lvl] - anchors = mlvl_anchors[lvl] - gt_bbox = gt_bboxes[gt_id, :4] - - # Compute regions - ignore_region = calc_region(gt_bbox, r2, stride, featmap_size) - ctr_region = calc_region(gt_bbox, r1, stride, featmap_size) - - # 2. Assign -1 to ignore flags - ignore_flags = anchor_ctr_inside_region_flags( - anchors, stride, ignore_region) - mlvl_assigned_gt_inds[lvl][ignore_flags] = -1 - - # 3. Assign gt_bboxes to pos flags - pos_flags = anchor_ctr_inside_region_flags(anchors, stride, - ctr_region) - mlvl_assigned_gt_inds[lvl][pos_flags] = gt_id + 1 - - # 4. Assign -1 to ignore adjacent lvl - if lvl > 0: - d_lvl = lvl - 1 - d_anchors = mlvl_anchors[d_lvl] - d_featmap_size = featmap_sizes[d_lvl] - d_stride = anchor_strides[d_lvl] - d_ignore_region = calc_region(gt_bbox, r2, d_stride, - d_featmap_size) - ignore_flags = anchor_ctr_inside_region_flags( - d_anchors, d_stride, d_ignore_region) - mlvl_ignore_flags[d_lvl][ignore_flags] = 1 - if lvl < num_lvls - 1: - u_lvl = lvl + 1 - u_anchors = mlvl_anchors[u_lvl] - u_featmap_size = featmap_sizes[u_lvl] - u_stride = anchor_strides[u_lvl] - u_ignore_region = calc_region(gt_bbox, r2, u_stride, - u_featmap_size) - ignore_flags = anchor_ctr_inside_region_flags( - u_anchors, u_stride, u_ignore_region) - mlvl_ignore_flags[u_lvl][ignore_flags] = 1 - - # 4. (cont.) Assign -1 to ignore adjacent lvl - for lvl in range(num_lvls): - ignore_flags = mlvl_ignore_flags[lvl] - mlvl_assigned_gt_inds[lvl][ignore_flags] = -1 - - # 5. Assign -1 to anchor outside of image - flat_assigned_gt_inds = torch.cat(mlvl_assigned_gt_inds) - flat_anchors = torch.cat(mlvl_anchors) - flat_valid_flags = torch.cat(mlvl_valid_flags) - assert (flat_assigned_gt_inds.shape[0] == flat_anchors.shape[0] == - flat_valid_flags.shape[0]) - inside_flags = anchor_inside_flags(flat_anchors, flat_valid_flags, - img_meta['img_shape'], - allowed_border) - outside_flags = ~inside_flags - flat_assigned_gt_inds[outside_flags] = -1 - - if gt_labels is not None: - assigned_labels = torch.zeros_like(flat_assigned_gt_inds) - pos_flags = assigned_gt_inds > 0 - assigned_labels[pos_flags] = gt_labels[ - flat_assigned_gt_inds[pos_flags] - 1] - else: - assigned_labels = None - - return AssignResult( - num_gts, flat_assigned_gt_inds, None, labels=assigned_labels) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/sim_ota_assigner.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/sim_ota_assigner.py deleted file mode 100644 index 58bfef433..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/sim_ota_assigner.py +++ /dev/null @@ -1,257 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -import torch.nn.functional as F - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import bbox_overlaps -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -@BBOX_ASSIGNERS.register_module() -class SimOTAAssigner(BaseAssigner): - """Computes matching between predictions and ground truth. - - Args: - center_radius (int | float, optional): Ground truth center size - to judge whether a prior is in center. Default 2.5. - candidate_topk (int, optional): The candidate top-k which used to - get top-k ious to calculate dynamic-k. Default 10. - iou_weight (int | float, optional): The scale factor for regression - iou cost. Default 3.0. - cls_weight (int | float, optional): The scale factor for classification - cost. Default 1.0. - """ - - def __init__(self, - center_radius=2.5, - candidate_topk=10, - iou_weight=3.0, - cls_weight=1.0): - self.center_radius = center_radius - self.candidate_topk = candidate_topk - self.iou_weight = iou_weight - self.cls_weight = cls_weight - - def assign(self, - pred_scores, - priors, - decoded_bboxes, - gt_bboxes, - gt_labels, - gt_bboxes_ignore=None, - eps=1e-7): - """Assign gt to priors using SimOTA. It will switch to CPU mode when - GPU is out of memory. - Args: - pred_scores (Tensor): Classification scores of one image, - a 2D-Tensor with shape [num_priors, num_classes] - priors (Tensor): All priors of one image, a 2D-Tensor with shape - [num_priors, 4] in [cx, xy, stride_w, stride_y] format. - decoded_bboxes (Tensor): Predicted bboxes, a 2D-Tensor with shape - [num_priors, 4] in [tl_x, tl_y, br_x, br_y] format. - gt_bboxes (Tensor): Ground truth bboxes of one image, a 2D-Tensor - with shape [num_gts, 4] in [tl_x, tl_y, br_x, br_y] format. - gt_labels (Tensor): Ground truth labels of one image, a Tensor - with shape [num_gts]. - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - eps (float): A value added to the denominator for numerical - stability. Default 1e-7. - Returns: - assign_result (obj:`AssignResult`): The assigned result. - """ - try: - assign_result = self._assign(pred_scores, priors, decoded_bboxes, - gt_bboxes, gt_labels, - gt_bboxes_ignore, eps) - return assign_result - except RuntimeError: - origin_device = pred_scores.device - warnings.warn('OOM RuntimeError is raised due to the huge memory ' - 'cost during label assignment. CPU mode is applied ' - 'in this batch. If you want to avoid this issue, ' - 'try to reduce the batch size or image size.') - torch.cuda.empty_cache() - - pred_scores = pred_scores.cpu() - priors = priors.cpu() - decoded_bboxes = decoded_bboxes.cpu() - gt_bboxes = gt_bboxes.cpu().float() - gt_labels = gt_labels.cpu() - - assign_result = self._assign(pred_scores, priors, decoded_bboxes, - gt_bboxes, gt_labels, - gt_bboxes_ignore, eps) - assign_result.gt_inds = assign_result.gt_inds.to(origin_device) - assign_result.max_overlaps = assign_result.max_overlaps.to( - origin_device) - assign_result.labels = assign_result.labels.to(origin_device) - - return assign_result - - def _assign(self, - pred_scores, - priors, - decoded_bboxes, - gt_bboxes, - gt_labels, - gt_bboxes_ignore=None, - eps=1e-7): - """Assign gt to priors using SimOTA. - Args: - pred_scores (Tensor): Classification scores of one image, - a 2D-Tensor with shape [num_priors, num_classes] - priors (Tensor): All priors of one image, a 2D-Tensor with shape - [num_priors, 4] in [cx, xy, stride_w, stride_y] format. - decoded_bboxes (Tensor): Predicted bboxes, a 2D-Tensor with shape - [num_priors, 4] in [tl_x, tl_y, br_x, br_y] format. - gt_bboxes (Tensor): Ground truth bboxes of one image, a 2D-Tensor - with shape [num_gts, 4] in [tl_x, tl_y, br_x, br_y] format. - gt_labels (Tensor): Ground truth labels of one image, a Tensor - with shape [num_gts]. - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - eps (float): A value added to the denominator for numerical - stability. Default 1e-7. - Returns: - :obj:`AssignResult`: The assigned result. - """ - INF = 100000.0 - num_gt = gt_bboxes.size(0) - num_bboxes = decoded_bboxes.size(0) - - # assign 0 by default - assigned_gt_inds = decoded_bboxes.new_full((num_bboxes, ), - 0, - dtype=torch.long) - valid_mask, is_in_boxes_and_center = self.get_in_gt_and_in_center_info( - priors, gt_bboxes) - valid_decoded_bbox = decoded_bboxes[valid_mask] - valid_pred_scores = pred_scores[valid_mask] - num_valid = valid_decoded_bbox.size(0) - - if num_gt == 0 or num_bboxes == 0 or num_valid == 0: - # No ground truth or boxes, return empty assignment - max_overlaps = decoded_bboxes.new_zeros((num_bboxes, )) - if num_gt == 0: - # No truth, assign everything to background - assigned_gt_inds[:] = 0 - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = decoded_bboxes.new_full((num_bboxes, ), - -1, - dtype=torch.long) - return AssignResult( - num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels) - - pairwise_ious = bbox_overlaps(valid_decoded_bbox, gt_bboxes) - iou_cost = -torch.log(pairwise_ious + eps) - - gt_onehot_label = ( - F.one_hot(gt_labels.to(torch.int64), - pred_scores.shape[-1]).float().unsqueeze(0).repeat( - num_valid, 1, 1)) - - valid_pred_scores = valid_pred_scores.unsqueeze(1).repeat(1, num_gt, 1) - cls_cost = ( - F.binary_cross_entropy( - valid_pred_scores.to(dtype=torch.float32).sqrt_(), - gt_onehot_label, - reduction='none', - ).sum(-1).to(dtype=valid_pred_scores.dtype)) - - cost_matrix = ( - cls_cost * self.cls_weight + iou_cost * self.iou_weight + - (~is_in_boxes_and_center) * INF) - - matched_pred_ious, matched_gt_inds = \ - self.dynamic_k_matching( - cost_matrix, pairwise_ious, num_gt, valid_mask) - - # convert to AssignResult format - assigned_gt_inds[valid_mask] = matched_gt_inds + 1 - assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1) - assigned_labels[valid_mask] = gt_labels[matched_gt_inds].long() - max_overlaps = assigned_gt_inds.new_full((num_bboxes, ), - -INF, - dtype=torch.float32) - max_overlaps[valid_mask] = matched_pred_ious - return AssignResult( - num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels) - - def get_in_gt_and_in_center_info(self, priors, gt_bboxes): - num_gt = gt_bboxes.size(0) - - repeated_x = priors[:, 0].unsqueeze(1).repeat(1, num_gt) - repeated_y = priors[:, 1].unsqueeze(1).repeat(1, num_gt) - repeated_stride_x = priors[:, 2].unsqueeze(1).repeat(1, num_gt) - repeated_stride_y = priors[:, 3].unsqueeze(1).repeat(1, num_gt) - - # is prior centers in gt bboxes, shape: [n_prior, n_gt] - l_ = repeated_x - gt_bboxes[:, 0] - t_ = repeated_y - gt_bboxes[:, 1] - r_ = gt_bboxes[:, 2] - repeated_x - b_ = gt_bboxes[:, 3] - repeated_y - - deltas = torch.stack([l_, t_, r_, b_], dim=1) - is_in_gts = deltas.min(dim=1).values > 0 - is_in_gts_all = is_in_gts.sum(dim=1) > 0 - - # is prior centers in gt centers - gt_cxs = (gt_bboxes[:, 0] + gt_bboxes[:, 2]) / 2.0 - gt_cys = (gt_bboxes[:, 1] + gt_bboxes[:, 3]) / 2.0 - ct_box_l = gt_cxs - self.center_radius * repeated_stride_x - ct_box_t = gt_cys - self.center_radius * repeated_stride_y - ct_box_r = gt_cxs + self.center_radius * repeated_stride_x - ct_box_b = gt_cys + self.center_radius * repeated_stride_y - - cl_ = repeated_x - ct_box_l - ct_ = repeated_y - ct_box_t - cr_ = ct_box_r - repeated_x - cb_ = ct_box_b - repeated_y - - ct_deltas = torch.stack([cl_, ct_, cr_, cb_], dim=1) - is_in_cts = ct_deltas.min(dim=1).values > 0 - is_in_cts_all = is_in_cts.sum(dim=1) > 0 - - # in boxes or in centers, shape: [num_priors] - is_in_gts_or_centers = is_in_gts_all | is_in_cts_all - - # both in boxes and centers, shape: [num_fg, num_gt] - is_in_boxes_and_centers = ( - is_in_gts[is_in_gts_or_centers, :] - & is_in_cts[is_in_gts_or_centers, :]) - return is_in_gts_or_centers, is_in_boxes_and_centers - - def dynamic_k_matching(self, cost, pairwise_ious, num_gt, valid_mask): - matching_matrix = torch.zeros_like(cost, dtype=torch.uint8) - # select candidate topk ious for dynamic-k calculation - candidate_topk = min(self.candidate_topk, pairwise_ious.size(0)) - topk_ious, _ = torch.topk(pairwise_ious, candidate_topk, dim=0) - # calculate dynamic k for each gt - dynamic_ks = torch.clamp(topk_ious.sum(0).int(), min=1) - for gt_idx in range(num_gt): - _, pos_idx = torch.topk( - cost[:, gt_idx], k=dynamic_ks[gt_idx], largest=False) - matching_matrix[:, gt_idx][pos_idx] = 1 - - del topk_ious, dynamic_ks, pos_idx - - prior_match_gt_mask = matching_matrix.sum(1) > 1 - if prior_match_gt_mask.sum() > 0: - cost_min, cost_argmin = torch.min( - cost[prior_match_gt_mask, :], dim=1) - matching_matrix[prior_match_gt_mask, :] *= 0 - matching_matrix[prior_match_gt_mask, cost_argmin] = 1 - # get foreground mask inside box and center prior - fg_mask_inboxes = matching_matrix.sum(1) > 0 - valid_mask[valid_mask.clone()] = fg_mask_inboxes - - matched_gt_inds = matching_matrix[fg_mask_inboxes, :].argmax(1) - matched_pred_ious = (matching_matrix * - pairwise_ious).sum(1)[fg_mask_inboxes] - return matched_pred_ious, matched_gt_inds diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/task_aligned_assigner.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/task_aligned_assigner.py deleted file mode 100644 index 1872de4a7..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/task_aligned_assigner.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - -INF = 100000000 - - -@BBOX_ASSIGNERS.register_module() -class TaskAlignedAssigner(BaseAssigner): - """Task aligned assigner used in the paper: - `TOOD: Task-aligned One-stage Object Detection. - `_. - - Assign a corresponding gt bbox or background to each predicted bbox. - Each bbox will be assigned with `0` or a positive integer - indicating the ground truth index. - - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - - Args: - topk (int): number of bbox selected in each level - iou_calculator (dict): Config dict for iou calculator. - Default: dict(type='BboxOverlaps2D') - """ - - def __init__(self, topk, iou_calculator=dict(type='BboxOverlaps2D')): - assert topk >= 1 - self.topk = topk - self.iou_calculator = build_iou_calculator(iou_calculator) - - def assign(self, - pred_scores, - decode_bboxes, - anchors, - gt_bboxes, - gt_bboxes_ignore=None, - gt_labels=None, - alpha=1, - beta=6): - """Assign gt to bboxes. - - The assignment is done in following steps - - 1. compute alignment metric between all bbox (bbox of all pyramid - levels) and gt - 2. select top-k bbox as candidates for each gt - 3. limit the positive sample's center in gt (because the anchor-free - detector only can predict positive distance) - - - Args: - pred_scores (Tensor): predicted class probability, - shape(n, num_classes) - decode_bboxes (Tensor): predicted bounding boxes, shape(n, 4) - anchors (Tensor): pre-defined anchors, shape(n, 4). - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - - Returns: - :obj:`TaskAlignedAssignResult`: The assign result. - """ - anchors = anchors[:, :4] - num_gt, num_bboxes = gt_bboxes.size(0), anchors.size(0) - # compute alignment metric between all bbox and gt - overlaps = self.iou_calculator(decode_bboxes, gt_bboxes).detach() - bbox_scores = pred_scores[:, gt_labels].detach() - # assign 0 by default - assigned_gt_inds = anchors.new_full((num_bboxes, ), - 0, - dtype=torch.long) - assign_metrics = anchors.new_zeros((num_bboxes, )) - - if num_gt == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - max_overlaps = anchors.new_zeros((num_bboxes, )) - if num_gt == 0: - # No gt boxes, assign everything to background - assigned_gt_inds[:] = 0 - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = anchors.new_full((num_bboxes, ), - -1, - dtype=torch.long) - assign_result = AssignResult( - num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels) - assign_result.assign_metrics = assign_metrics - return assign_result - - # select top-k bboxes as candidates for each gt - alignment_metrics = bbox_scores**alpha * overlaps**beta - topk = min(self.topk, alignment_metrics.size(0)) - _, candidate_idxs = alignment_metrics.topk(topk, dim=0, largest=True) - candidate_metrics = alignment_metrics[candidate_idxs, - torch.arange(num_gt)] - is_pos = candidate_metrics > 0 - - # limit the positive sample's center in gt - anchors_cx = (anchors[:, 0] + anchors[:, 2]) / 2.0 - anchors_cy = (anchors[:, 1] + anchors[:, 3]) / 2.0 - for gt_idx in range(num_gt): - candidate_idxs[:, gt_idx] += gt_idx * num_bboxes - ep_anchors_cx = anchors_cx.view(1, -1).expand( - num_gt, num_bboxes).contiguous().view(-1) - ep_anchors_cy = anchors_cy.view(1, -1).expand( - num_gt, num_bboxes).contiguous().view(-1) - candidate_idxs = candidate_idxs.view(-1) - - # calculate the left, top, right, bottom distance between positive - # bbox center and gt side - l_ = ep_anchors_cx[candidate_idxs].view(-1, num_gt) - gt_bboxes[:, 0] - t_ = ep_anchors_cy[candidate_idxs].view(-1, num_gt) - gt_bboxes[:, 1] - r_ = gt_bboxes[:, 2] - ep_anchors_cx[candidate_idxs].view(-1, num_gt) - b_ = gt_bboxes[:, 3] - ep_anchors_cy[candidate_idxs].view(-1, num_gt) - is_in_gts = torch.stack([l_, t_, r_, b_], dim=1).min(dim=1)[0] > 0.01 - is_pos = is_pos & is_in_gts - - # if an anchor box is assigned to multiple gts, - # the one with the highest iou will be selected. - overlaps_inf = torch.full_like(overlaps, - -INF).t().contiguous().view(-1) - index = candidate_idxs.view(-1)[is_pos.view(-1)] - overlaps_inf[index] = overlaps.t().contiguous().view(-1)[index] - overlaps_inf = overlaps_inf.view(num_gt, -1).t() - - max_overlaps, argmax_overlaps = overlaps_inf.max(dim=1) - assigned_gt_inds[ - max_overlaps != -INF] = argmax_overlaps[max_overlaps != -INF] + 1 - assign_metrics[max_overlaps != -INF] = alignment_metrics[ - max_overlaps != -INF, argmax_overlaps[max_overlaps != -INF]] - - if gt_labels is not None: - assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1) - pos_inds = torch.nonzero( - assigned_gt_inds > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[ - assigned_gt_inds[pos_inds] - 1] - else: - assigned_labels = None - assign_result = AssignResult( - num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels) - assign_result.assign_metrics = assign_metrics - return assign_result diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/uniform_assigner.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/uniform_assigner.py deleted file mode 100644 index 70294fc45..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/assigners/uniform_assigner.py +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from ..transforms import bbox_xyxy_to_cxcywh -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -@BBOX_ASSIGNERS.register_module() -class UniformAssigner(BaseAssigner): - """Uniform Matching between the anchors and gt boxes, which can achieve - balance in positive anchors, and gt_bboxes_ignore was not considered for - now. - - Args: - pos_ignore_thr (float): the threshold to ignore positive anchors - neg_ignore_thr (float): the threshold to ignore negative anchors - match_times(int): Number of positive anchors for each gt box. - Default 4. - iou_calculator (dict): iou_calculator config - """ - - def __init__(self, - pos_ignore_thr, - neg_ignore_thr, - match_times=4, - iou_calculator=dict(type='BboxOverlaps2D')): - self.match_times = match_times - self.pos_ignore_thr = pos_ignore_thr - self.neg_ignore_thr = neg_ignore_thr - self.iou_calculator = build_iou_calculator(iou_calculator) - - def assign(self, - bbox_pred, - anchor, - gt_bboxes, - gt_bboxes_ignore=None, - gt_labels=None): - num_gts, num_bboxes = gt_bboxes.size(0), bbox_pred.size(0) - - # 1. assign -1 by default - assigned_gt_inds = bbox_pred.new_full((num_bboxes, ), - 0, - dtype=torch.long) - assigned_labels = bbox_pred.new_full((num_bboxes, ), - -1, - dtype=torch.long) - if num_gts == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - if num_gts == 0: - # No ground truth, assign all to background - assigned_gt_inds[:] = 0 - assign_result = AssignResult( - num_gts, assigned_gt_inds, None, labels=assigned_labels) - assign_result.set_extra_property( - 'pos_idx', bbox_pred.new_empty(0, dtype=torch.bool)) - assign_result.set_extra_property('pos_predicted_boxes', - bbox_pred.new_empty((0, 4))) - assign_result.set_extra_property('target_boxes', - bbox_pred.new_empty((0, 4))) - return assign_result - - # 2. Compute the L1 cost between boxes - # Note that we use anchors and predict boxes both - cost_bbox = torch.cdist( - bbox_xyxy_to_cxcywh(bbox_pred), - bbox_xyxy_to_cxcywh(gt_bboxes), - p=1) - cost_bbox_anchors = torch.cdist( - bbox_xyxy_to_cxcywh(anchor), bbox_xyxy_to_cxcywh(gt_bboxes), p=1) - - # We found that topk function has different results in cpu and - # cuda mode. In order to ensure consistency with the source code, - # we also use cpu mode. - # TODO: Check whether the performance of cpu and cuda are the same. - C = cost_bbox.cpu() - C1 = cost_bbox_anchors.cpu() - - # self.match_times x n - index = torch.topk( - C, # c=b,n,x c[i]=n,x - k=self.match_times, - dim=0, - largest=False)[1] - - # self.match_times x n - index1 = torch.topk(C1, k=self.match_times, dim=0, largest=False)[1] - # (self.match_times*2) x n - indexes = torch.cat((index, index1), - dim=1).reshape(-1).to(bbox_pred.device) - - pred_overlaps = self.iou_calculator(bbox_pred, gt_bboxes) - anchor_overlaps = self.iou_calculator(anchor, gt_bboxes) - pred_max_overlaps, _ = pred_overlaps.max(dim=1) - anchor_max_overlaps, _ = anchor_overlaps.max(dim=0) - - # 3. Compute the ignore indexes use gt_bboxes and predict boxes - ignore_idx = pred_max_overlaps > self.neg_ignore_thr - assigned_gt_inds[ignore_idx] = -1 - - # 4. Compute the ignore indexes of positive sample use anchors - # and predict boxes - pos_gt_index = torch.arange( - 0, C1.size(1), - device=bbox_pred.device).repeat(self.match_times * 2) - pos_ious = anchor_overlaps[indexes, pos_gt_index] - pos_ignore_idx = pos_ious < self.pos_ignore_thr - - pos_gt_index_with_ignore = pos_gt_index + 1 - pos_gt_index_with_ignore[pos_ignore_idx] = -1 - assigned_gt_inds[indexes] = pos_gt_index_with_ignore - - if gt_labels is not None: - assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1) - pos_inds = torch.nonzero( - assigned_gt_inds > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[ - assigned_gt_inds[pos_inds] - 1] - else: - assigned_labels = None - - assign_result = AssignResult( - num_gts, - assigned_gt_inds, - anchor_max_overlaps, - labels=assigned_labels) - assign_result.set_extra_property('pos_idx', ~pos_ignore_idx) - assign_result.set_extra_property('pos_predicted_boxes', - bbox_pred[indexes]) - assign_result.set_extra_property('target_boxes', - gt_bboxes[pos_gt_index]) - return assign_result diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/builder.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/builder.py deleted file mode 100644 index 9cfa055b5..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/builder.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, build_from_cfg - -BBOX_ASSIGNERS = Registry('bbox_assigner') -BBOX_SAMPLERS = Registry('bbox_sampler') -BBOX_CODERS = Registry('bbox_coder') - - -def build_assigner(cfg, **default_args): - """Builder of box assigner.""" - return build_from_cfg(cfg, BBOX_ASSIGNERS, default_args) - - -def build_sampler(cfg, **default_args): - """Builder of box sampler.""" - return build_from_cfg(cfg, BBOX_SAMPLERS, default_args) - - -def build_bbox_coder(cfg, **default_args): - """Builder of box coder.""" - return build_from_cfg(cfg, BBOX_CODERS, default_args) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/__init__.py deleted file mode 100644 index e12fd64e1..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_bbox_coder import BaseBBoxCoder -from .bucketing_bbox_coder import BucketingBBoxCoder -from .delta_xywh_bbox_coder import DeltaXYWHBBoxCoder -from .distance_point_bbox_coder import DistancePointBBoxCoder -from .legacy_delta_xywh_bbox_coder import LegacyDeltaXYWHBBoxCoder -from .pseudo_bbox_coder import PseudoBBoxCoder -from .tblr_bbox_coder import TBLRBBoxCoder -from .yolo_bbox_coder import YOLOBBoxCoder - -__all__ = [ - 'BaseBBoxCoder', 'PseudoBBoxCoder', 'DeltaXYWHBBoxCoder', - 'LegacyDeltaXYWHBBoxCoder', 'TBLRBBoxCoder', 'YOLOBBoxCoder', - 'BucketingBBoxCoder', 'DistancePointBBoxCoder' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/base_bbox_coder.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/base_bbox_coder.py deleted file mode 100644 index a7ed041a4..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/base_bbox_coder.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseBBoxCoder(metaclass=ABCMeta): - """Base bounding box coder.""" - - def __init__(self, **kwargs): - pass - - @abstractmethod - def encode(self, bboxes, gt_bboxes): - """Encode deltas between bboxes and ground truth boxes.""" - - @abstractmethod - def decode(self, bboxes, bboxes_pred): - """Decode the predicted bboxes according to prediction and base - boxes.""" diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/bucketing_bbox_coder.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/bucketing_bbox_coder.py deleted file mode 100644 index 4be0ada04..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/bucketing_bbox_coder.py +++ /dev/null @@ -1,351 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np -import torch -import torch.nn.functional as F - -from ..builder import BBOX_CODERS -from ..transforms import bbox_rescale -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class BucketingBBoxCoder(BaseBBoxCoder): - """Bucketing BBox Coder for Side-Aware Boundary Localization (SABL). - - Boundary Localization with Bucketing and Bucketing Guided Rescoring - are implemented here. - - Please refer to https://arxiv.org/abs/1912.04260 for more details. - - Args: - num_buckets (int): Number of buckets. - scale_factor (int): Scale factor of proposals to generate buckets. - offset_topk (int): Topk buckets are used to generate - bucket fine regression targets. Defaults to 2. - offset_upperbound (float): Offset upperbound to generate - bucket fine regression targets. - To avoid too large offset displacements. Defaults to 1.0. - cls_ignore_neighbor (bool): Ignore second nearest bucket or Not. - Defaults to True. - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Defaults to True. - """ - - def __init__(self, - num_buckets, - scale_factor, - offset_topk=2, - offset_upperbound=1.0, - cls_ignore_neighbor=True, - clip_border=True): - super(BucketingBBoxCoder, self).__init__() - self.num_buckets = num_buckets - self.scale_factor = scale_factor - self.offset_topk = offset_topk - self.offset_upperbound = offset_upperbound - self.cls_ignore_neighbor = cls_ignore_neighbor - self.clip_border = clip_border - - def encode(self, bboxes, gt_bboxes): - """Get bucketing estimation and fine regression targets during - training. - - Args: - bboxes (torch.Tensor): source boxes, e.g., object proposals. - gt_bboxes (torch.Tensor): target of the transformation, e.g., - ground truth boxes. - - Returns: - encoded_bboxes(tuple[Tensor]): bucketing estimation - and fine regression targets and weights - """ - - assert bboxes.size(0) == gt_bboxes.size(0) - assert bboxes.size(-1) == gt_bboxes.size(-1) == 4 - encoded_bboxes = bbox2bucket(bboxes, gt_bboxes, self.num_buckets, - self.scale_factor, self.offset_topk, - self.offset_upperbound, - self.cls_ignore_neighbor) - return encoded_bboxes - - def decode(self, bboxes, pred_bboxes, max_shape=None): - """Apply transformation `pred_bboxes` to `boxes`. - Args: - boxes (torch.Tensor): Basic boxes. - pred_bboxes (torch.Tensor): Predictions for bucketing estimation - and fine regression - max_shape (tuple[int], optional): Maximum shape of boxes. - Defaults to None. - - Returns: - torch.Tensor: Decoded boxes. - """ - assert len(pred_bboxes) == 2 - cls_preds, offset_preds = pred_bboxes - assert cls_preds.size(0) == bboxes.size(0) and offset_preds.size( - 0) == bboxes.size(0) - decoded_bboxes = bucket2bbox(bboxes, cls_preds, offset_preds, - self.num_buckets, self.scale_factor, - max_shape, self.clip_border) - - return decoded_bboxes - - -@mmcv.jit(coderize=True) -def generat_buckets(proposals, num_buckets, scale_factor=1.0): - """Generate buckets w.r.t bucket number and scale factor of proposals. - - Args: - proposals (Tensor): Shape (n, 4) - num_buckets (int): Number of buckets. - scale_factor (float): Scale factor to rescale proposals. - - Returns: - tuple[Tensor]: (bucket_w, bucket_h, l_buckets, r_buckets, - t_buckets, d_buckets) - - - bucket_w: Width of buckets on x-axis. Shape (n, ). - - bucket_h: Height of buckets on y-axis. Shape (n, ). - - l_buckets: Left buckets. Shape (n, ceil(side_num/2)). - - r_buckets: Right buckets. Shape (n, ceil(side_num/2)). - - t_buckets: Top buckets. Shape (n, ceil(side_num/2)). - - d_buckets: Down buckets. Shape (n, ceil(side_num/2)). - """ - proposals = bbox_rescale(proposals, scale_factor) - - # number of buckets in each side - side_num = int(np.ceil(num_buckets / 2.0)) - pw = proposals[..., 2] - proposals[..., 0] - ph = proposals[..., 3] - proposals[..., 1] - px1 = proposals[..., 0] - py1 = proposals[..., 1] - px2 = proposals[..., 2] - py2 = proposals[..., 3] - - bucket_w = pw / num_buckets - bucket_h = ph / num_buckets - - # left buckets - l_buckets = px1[:, None] + (0.5 + torch.arange( - 0, side_num).to(proposals).float())[None, :] * bucket_w[:, None] - # right buckets - r_buckets = px2[:, None] - (0.5 + torch.arange( - 0, side_num).to(proposals).float())[None, :] * bucket_w[:, None] - # top buckets - t_buckets = py1[:, None] + (0.5 + torch.arange( - 0, side_num).to(proposals).float())[None, :] * bucket_h[:, None] - # down buckets - d_buckets = py2[:, None] - (0.5 + torch.arange( - 0, side_num).to(proposals).float())[None, :] * bucket_h[:, None] - return bucket_w, bucket_h, l_buckets, r_buckets, t_buckets, d_buckets - - -@mmcv.jit(coderize=True) -def bbox2bucket(proposals, - gt, - num_buckets, - scale_factor, - offset_topk=2, - offset_upperbound=1.0, - cls_ignore_neighbor=True): - """Generate buckets estimation and fine regression targets. - - Args: - proposals (Tensor): Shape (n, 4) - gt (Tensor): Shape (n, 4) - num_buckets (int): Number of buckets. - scale_factor (float): Scale factor to rescale proposals. - offset_topk (int): Topk buckets are used to generate - bucket fine regression targets. Defaults to 2. - offset_upperbound (float): Offset allowance to generate - bucket fine regression targets. - To avoid too large offset displacements. Defaults to 1.0. - cls_ignore_neighbor (bool): Ignore second nearest bucket or Not. - Defaults to True. - - Returns: - tuple[Tensor]: (offsets, offsets_weights, bucket_labels, cls_weights). - - - offsets: Fine regression targets. \ - Shape (n, num_buckets*2). - - offsets_weights: Fine regression weights. \ - Shape (n, num_buckets*2). - - bucket_labels: Bucketing estimation labels. \ - Shape (n, num_buckets*2). - - cls_weights: Bucketing estimation weights. \ - Shape (n, num_buckets*2). - """ - assert proposals.size() == gt.size() - - # generate buckets - proposals = proposals.float() - gt = gt.float() - (bucket_w, bucket_h, l_buckets, r_buckets, t_buckets, - d_buckets) = generat_buckets(proposals, num_buckets, scale_factor) - - gx1 = gt[..., 0] - gy1 = gt[..., 1] - gx2 = gt[..., 2] - gy2 = gt[..., 3] - - # generate offset targets and weights - # offsets from buckets to gts - l_offsets = (l_buckets - gx1[:, None]) / bucket_w[:, None] - r_offsets = (r_buckets - gx2[:, None]) / bucket_w[:, None] - t_offsets = (t_buckets - gy1[:, None]) / bucket_h[:, None] - d_offsets = (d_buckets - gy2[:, None]) / bucket_h[:, None] - - # select top-k nearest buckets - l_topk, l_label = l_offsets.abs().topk( - offset_topk, dim=1, largest=False, sorted=True) - r_topk, r_label = r_offsets.abs().topk( - offset_topk, dim=1, largest=False, sorted=True) - t_topk, t_label = t_offsets.abs().topk( - offset_topk, dim=1, largest=False, sorted=True) - d_topk, d_label = d_offsets.abs().topk( - offset_topk, dim=1, largest=False, sorted=True) - - offset_l_weights = l_offsets.new_zeros(l_offsets.size()) - offset_r_weights = r_offsets.new_zeros(r_offsets.size()) - offset_t_weights = t_offsets.new_zeros(t_offsets.size()) - offset_d_weights = d_offsets.new_zeros(d_offsets.size()) - inds = torch.arange(0, proposals.size(0)).to(proposals).long() - - # generate offset weights of top-k nearest buckets - for k in range(offset_topk): - if k >= 1: - offset_l_weights[inds, l_label[:, - k]] = (l_topk[:, k] < - offset_upperbound).float() - offset_r_weights[inds, r_label[:, - k]] = (r_topk[:, k] < - offset_upperbound).float() - offset_t_weights[inds, t_label[:, - k]] = (t_topk[:, k] < - offset_upperbound).float() - offset_d_weights[inds, d_label[:, - k]] = (d_topk[:, k] < - offset_upperbound).float() - else: - offset_l_weights[inds, l_label[:, k]] = 1.0 - offset_r_weights[inds, r_label[:, k]] = 1.0 - offset_t_weights[inds, t_label[:, k]] = 1.0 - offset_d_weights[inds, d_label[:, k]] = 1.0 - - offsets = torch.cat([l_offsets, r_offsets, t_offsets, d_offsets], dim=-1) - offsets_weights = torch.cat([ - offset_l_weights, offset_r_weights, offset_t_weights, offset_d_weights - ], - dim=-1) - - # generate bucket labels and weight - side_num = int(np.ceil(num_buckets / 2.0)) - labels = torch.stack( - [l_label[:, 0], r_label[:, 0], t_label[:, 0], d_label[:, 0]], dim=-1) - - batch_size = labels.size(0) - bucket_labels = F.one_hot(labels.view(-1), side_num).view(batch_size, - -1).float() - bucket_cls_l_weights = (l_offsets.abs() < 1).float() - bucket_cls_r_weights = (r_offsets.abs() < 1).float() - bucket_cls_t_weights = (t_offsets.abs() < 1).float() - bucket_cls_d_weights = (d_offsets.abs() < 1).float() - bucket_cls_weights = torch.cat([ - bucket_cls_l_weights, bucket_cls_r_weights, bucket_cls_t_weights, - bucket_cls_d_weights - ], - dim=-1) - # ignore second nearest buckets for cls if necessary - if cls_ignore_neighbor: - bucket_cls_weights = (~((bucket_cls_weights == 1) & - (bucket_labels == 0))).float() - else: - bucket_cls_weights[:] = 1.0 - return offsets, offsets_weights, bucket_labels, bucket_cls_weights - - -@mmcv.jit(coderize=True) -def bucket2bbox(proposals, - cls_preds, - offset_preds, - num_buckets, - scale_factor=1.0, - max_shape=None, - clip_border=True): - """Apply bucketing estimation (cls preds) and fine regression (offset - preds) to generate det bboxes. - - Args: - proposals (Tensor): Boxes to be transformed. Shape (n, 4) - cls_preds (Tensor): bucketing estimation. Shape (n, num_buckets*2). - offset_preds (Tensor): fine regression. Shape (n, num_buckets*2). - num_buckets (int): Number of buckets. - scale_factor (float): Scale factor to rescale proposals. - max_shape (tuple[int, int]): Maximum bounds for boxes. specifies (H, W) - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Defaults to True. - - Returns: - tuple[Tensor]: (bboxes, loc_confidence). - - - bboxes: predicted bboxes. Shape (n, 4) - - loc_confidence: localization confidence of predicted bboxes. - Shape (n,). - """ - - side_num = int(np.ceil(num_buckets / 2.0)) - cls_preds = cls_preds.view(-1, side_num) - offset_preds = offset_preds.view(-1, side_num) - - scores = F.softmax(cls_preds, dim=1) - score_topk, score_label = scores.topk(2, dim=1, largest=True, sorted=True) - - rescaled_proposals = bbox_rescale(proposals, scale_factor) - - pw = rescaled_proposals[..., 2] - rescaled_proposals[..., 0] - ph = rescaled_proposals[..., 3] - rescaled_proposals[..., 1] - px1 = rescaled_proposals[..., 0] - py1 = rescaled_proposals[..., 1] - px2 = rescaled_proposals[..., 2] - py2 = rescaled_proposals[..., 3] - - bucket_w = pw / num_buckets - bucket_h = ph / num_buckets - - score_inds_l = score_label[0::4, 0] - score_inds_r = score_label[1::4, 0] - score_inds_t = score_label[2::4, 0] - score_inds_d = score_label[3::4, 0] - l_buckets = px1 + (0.5 + score_inds_l.float()) * bucket_w - r_buckets = px2 - (0.5 + score_inds_r.float()) * bucket_w - t_buckets = py1 + (0.5 + score_inds_t.float()) * bucket_h - d_buckets = py2 - (0.5 + score_inds_d.float()) * bucket_h - - offsets = offset_preds.view(-1, 4, side_num) - inds = torch.arange(proposals.size(0)).to(proposals).long() - l_offsets = offsets[:, 0, :][inds, score_inds_l] - r_offsets = offsets[:, 1, :][inds, score_inds_r] - t_offsets = offsets[:, 2, :][inds, score_inds_t] - d_offsets = offsets[:, 3, :][inds, score_inds_d] - - x1 = l_buckets - l_offsets * bucket_w - x2 = r_buckets - r_offsets * bucket_w - y1 = t_buckets - t_offsets * bucket_h - y2 = d_buckets - d_offsets * bucket_h - - if clip_border and max_shape is not None: - x1 = x1.clamp(min=0, max=max_shape[1] - 1) - y1 = y1.clamp(min=0, max=max_shape[0] - 1) - x2 = x2.clamp(min=0, max=max_shape[1] - 1) - y2 = y2.clamp(min=0, max=max_shape[0] - 1) - bboxes = torch.cat([x1[:, None], y1[:, None], x2[:, None], y2[:, None]], - dim=-1) - - # bucketing guided rescoring - loc_confidence = score_topk[:, 0] - top2_neighbor_inds = (score_label[:, 0] - score_label[:, 1]).abs() == 1 - loc_confidence += score_topk[:, 1] * top2_neighbor_inds.float() - loc_confidence = loc_confidence.view(-1, 4).mean(dim=1) - - return bboxes, loc_confidence diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/delta_xywh_bbox_coder.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/delta_xywh_bbox_coder.py deleted file mode 100644 index a7f1c62fa..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/delta_xywh_bbox_coder.py +++ /dev/null @@ -1,392 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv -import numpy as np -import torch - -from ..builder import BBOX_CODERS -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class DeltaXYWHBBoxCoder(BaseBBoxCoder): - """Delta XYWH BBox coder. - - Following the practice in `R-CNN `_, - this coder encodes bbox (x1, y1, x2, y2) into delta (dx, dy, dw, dh) and - decodes delta (dx, dy, dw, dh) back to original bbox (x1, y1, x2, y2). - - Args: - target_means (Sequence[float]): Denormalizing means of target for - delta coordinates - target_stds (Sequence[float]): Denormalizing standard deviation of - target for delta coordinates - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Defaults to True. - add_ctr_clamp (bool): Whether to add center clamp, when added, the - predicted box is clamped is its center is too far away from - the original anchor's center. Only used by YOLOF. Default False. - ctr_clamp (int): the maximum pixel shift to clamp. Only used by YOLOF. - Default 32. - """ - - def __init__(self, - target_means=(0., 0., 0., 0.), - target_stds=(1., 1., 1., 1.), - clip_border=True, - add_ctr_clamp=False, - ctr_clamp=32): - super(BaseBBoxCoder, self).__init__() - self.means = target_means - self.stds = target_stds - self.clip_border = clip_border - self.add_ctr_clamp = add_ctr_clamp - self.ctr_clamp = ctr_clamp - - def encode(self, bboxes, gt_bboxes): - """Get box regression transformation deltas that can be used to - transform the ``bboxes`` into the ``gt_bboxes``. - - Args: - bboxes (torch.Tensor): Source boxes, e.g., object proposals. - gt_bboxes (torch.Tensor): Target of the transformation, e.g., - ground-truth boxes. - - Returns: - torch.Tensor: Box transformation deltas - """ - - assert bboxes.size(0) == gt_bboxes.size(0) - assert bboxes.size(-1) == gt_bboxes.size(-1) == 4 - encoded_bboxes = bbox2delta(bboxes, gt_bboxes, self.means, self.stds) - return encoded_bboxes - - def decode(self, - bboxes, - pred_bboxes, - max_shape=None, - wh_ratio_clip=16 / 1000): - """Apply transformation `pred_bboxes` to `boxes`. - - Args: - bboxes (torch.Tensor): Basic boxes. Shape (B, N, 4) or (N, 4) - pred_bboxes (Tensor): Encoded offsets with respect to each roi. - Has shape (B, N, num_classes * 4) or (B, N, 4) or - (N, num_classes * 4) or (N, 4). Note N = num_anchors * W * H - when rois is a grid of anchors.Offset encoding follows [1]_. - max_shape (Sequence[int] or torch.Tensor or Sequence[ - Sequence[int]],optional): Maximum bounds for boxes, specifies - (H, W, C) or (H, W). If bboxes shape is (B, N, 4), then - the max_shape should be a Sequence[Sequence[int]] - and the length of max_shape should also be B. - wh_ratio_clip (float, optional): The allowed ratio between - width and height. - - Returns: - torch.Tensor: Decoded boxes. - """ - - assert pred_bboxes.size(0) == bboxes.size(0) - if pred_bboxes.ndim == 3: - assert pred_bboxes.size(1) == bboxes.size(1) - - if pred_bboxes.ndim == 2 and not torch.onnx.is_in_onnx_export(): - # single image decode - decoded_bboxes = delta2bbox(bboxes, pred_bboxes, self.means, - self.stds, max_shape, wh_ratio_clip, - self.clip_border, self.add_ctr_clamp, - self.ctr_clamp) - else: - if pred_bboxes.ndim == 3 and not torch.onnx.is_in_onnx_export(): - warnings.warn( - 'DeprecationWarning: onnx_delta2bbox is deprecated ' - 'in the case of batch decoding and non-ONNX, ' - 'please use “delta2bbox” instead. In order to improve ' - 'the decoding speed, the batch function will no ' - 'longer be supported. ') - decoded_bboxes = onnx_delta2bbox(bboxes, pred_bboxes, self.means, - self.stds, max_shape, - wh_ratio_clip, self.clip_border, - self.add_ctr_clamp, - self.ctr_clamp) - - return decoded_bboxes - - -@mmcv.jit(coderize=True) -def bbox2delta(proposals, gt, means=(0., 0., 0., 0.), stds=(1., 1., 1., 1.)): - """Compute deltas of proposals w.r.t. gt. - - We usually compute the deltas of x, y, w, h of proposals w.r.t ground - truth bboxes to get regression target. - This is the inverse function of :func:`delta2bbox`. - - Args: - proposals (Tensor): Boxes to be transformed, shape (N, ..., 4) - gt (Tensor): Gt bboxes to be used as base, shape (N, ..., 4) - means (Sequence[float]): Denormalizing means for delta coordinates - stds (Sequence[float]): Denormalizing standard deviation for delta - coordinates - - Returns: - Tensor: deltas with shape (N, 4), where columns represent dx, dy, - dw, dh. - """ - assert proposals.size() == gt.size() - - proposals = proposals.float() - gt = gt.float() - px = (proposals[..., 0] + proposals[..., 2]) * 0.5 - py = (proposals[..., 1] + proposals[..., 3]) * 0.5 - pw = proposals[..., 2] - proposals[..., 0] - ph = proposals[..., 3] - proposals[..., 1] - - gx = (gt[..., 0] + gt[..., 2]) * 0.5 - gy = (gt[..., 1] + gt[..., 3]) * 0.5 - gw = gt[..., 2] - gt[..., 0] - gh = gt[..., 3] - gt[..., 1] - - dx = (gx - px) / pw - dy = (gy - py) / ph - dw = torch.log(gw / pw) - dh = torch.log(gh / ph) - deltas = torch.stack([dx, dy, dw, dh], dim=-1) - - means = deltas.new_tensor(means).unsqueeze(0) - stds = deltas.new_tensor(stds).unsqueeze(0) - deltas = deltas.sub_(means).div_(stds) - - return deltas - - -@mmcv.jit(coderize=True) -def delta2bbox(rois, - deltas, - means=(0., 0., 0., 0.), - stds=(1., 1., 1., 1.), - max_shape=None, - wh_ratio_clip=16 / 1000, - clip_border=True, - add_ctr_clamp=False, - ctr_clamp=32): - """Apply deltas to shift/scale base boxes. - - Typically the rois are anchor or proposed bounding boxes and the deltas are - network outputs used to shift/scale those boxes. - This is the inverse function of :func:`bbox2delta`. - - Args: - rois (Tensor): Boxes to be transformed. Has shape (N, 4). - deltas (Tensor): Encoded offsets relative to each roi. - Has shape (N, num_classes * 4) or (N, 4). Note - N = num_base_anchors * W * H, when rois is a grid of - anchors. Offset encoding follows [1]_. - means (Sequence[float]): Denormalizing means for delta coordinates. - Default (0., 0., 0., 0.). - stds (Sequence[float]): Denormalizing standard deviation for delta - coordinates. Default (1., 1., 1., 1.). - max_shape (tuple[int, int]): Maximum bounds for boxes, specifies - (H, W). Default None. - wh_ratio_clip (float): Maximum aspect ratio for boxes. Default - 16 / 1000. - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Default True. - add_ctr_clamp (bool): Whether to add center clamp. When set to True, - the center of the prediction bounding box will be clamped to - avoid being too far away from the center of the anchor. - Only used by YOLOF. Default False. - ctr_clamp (int): the maximum pixel shift to clamp. Only used by YOLOF. - Default 32. - - Returns: - Tensor: Boxes with shape (N, num_classes * 4) or (N, 4), where 4 - represent tl_x, tl_y, br_x, br_y. - - References: - .. [1] https://arxiv.org/abs/1311.2524 - - Example: - >>> rois = torch.Tensor([[ 0., 0., 1., 1.], - >>> [ 0., 0., 1., 1.], - >>> [ 0., 0., 1., 1.], - >>> [ 5., 5., 5., 5.]]) - >>> deltas = torch.Tensor([[ 0., 0., 0., 0.], - >>> [ 1., 1., 1., 1.], - >>> [ 0., 0., 2., -1.], - >>> [ 0.7, -1.9, -0.5, 0.3]]) - >>> delta2bbox(rois, deltas, max_shape=(32, 32, 3)) - tensor([[0.0000, 0.0000, 1.0000, 1.0000], - [0.1409, 0.1409, 2.8591, 2.8591], - [0.0000, 0.3161, 4.1945, 0.6839], - [5.0000, 5.0000, 5.0000, 5.0000]]) - """ - num_bboxes, num_classes = deltas.size(0), deltas.size(1) // 4 - if num_bboxes == 0: - return deltas - - deltas = deltas.reshape(-1, 4) - - means = deltas.new_tensor(means).view(1, -1) - stds = deltas.new_tensor(stds).view(1, -1) - denorm_deltas = deltas * stds + means - - dxy = denorm_deltas[:, :2] - dwh = denorm_deltas[:, 2:] - - # Compute width/height of each roi - rois_ = rois.repeat(1, num_classes).reshape(-1, 4) - pxy = ((rois_[:, :2] + rois_[:, 2:]) * 0.5) - pwh = (rois_[:, 2:] - rois_[:, :2]) - - dxy_wh = pwh * dxy - - max_ratio = np.abs(np.log(wh_ratio_clip)) - if add_ctr_clamp: - dxy_wh = torch.clamp(dxy_wh, max=ctr_clamp, min=-ctr_clamp) - dwh = torch.clamp(dwh, max=max_ratio) - else: - dwh = dwh.clamp(min=-max_ratio, max=max_ratio) - - gxy = pxy + dxy_wh - gwh = pwh * dwh.exp() - x1y1 = gxy - (gwh * 0.5) - x2y2 = gxy + (gwh * 0.5) - bboxes = torch.cat([x1y1, x2y2], dim=-1) - if clip_border and max_shape is not None: - bboxes[..., 0::2].clamp_(min=0, max=max_shape[1]) - bboxes[..., 1::2].clamp_(min=0, max=max_shape[0]) - bboxes = bboxes.reshape(num_bboxes, -1) - return bboxes - - -def onnx_delta2bbox(rois, - deltas, - means=(0., 0., 0., 0.), - stds=(1., 1., 1., 1.), - max_shape=None, - wh_ratio_clip=16 / 1000, - clip_border=True, - add_ctr_clamp=False, - ctr_clamp=32): - """Apply deltas to shift/scale base boxes. - - Typically the rois are anchor or proposed bounding boxes and the deltas are - network outputs used to shift/scale those boxes. - This is the inverse function of :func:`bbox2delta`. - - Args: - rois (Tensor): Boxes to be transformed. Has shape (N, 4) or (B, N, 4) - deltas (Tensor): Encoded offsets with respect to each roi. - Has shape (B, N, num_classes * 4) or (B, N, 4) or - (N, num_classes * 4) or (N, 4). Note N = num_anchors * W * H - when rois is a grid of anchors.Offset encoding follows [1]_. - means (Sequence[float]): Denormalizing means for delta coordinates. - Default (0., 0., 0., 0.). - stds (Sequence[float]): Denormalizing standard deviation for delta - coordinates. Default (1., 1., 1., 1.). - max_shape (Sequence[int] or torch.Tensor or Sequence[ - Sequence[int]],optional): Maximum bounds for boxes, specifies - (H, W, C) or (H, W). If rois shape is (B, N, 4), then - the max_shape should be a Sequence[Sequence[int]] - and the length of max_shape should also be B. Default None. - wh_ratio_clip (float): Maximum aspect ratio for boxes. - Default 16 / 1000. - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Default True. - add_ctr_clamp (bool): Whether to add center clamp, when added, the - predicted box is clamped is its center is too far away from - the original anchor's center. Only used by YOLOF. Default False. - ctr_clamp (int): the maximum pixel shift to clamp. Only used by YOLOF. - Default 32. - - Returns: - Tensor: Boxes with shape (B, N, num_classes * 4) or (B, N, 4) or - (N, num_classes * 4) or (N, 4), where 4 represent - tl_x, tl_y, br_x, br_y. - - References: - .. [1] https://arxiv.org/abs/1311.2524 - - Example: - >>> rois = torch.Tensor([[ 0., 0., 1., 1.], - >>> [ 0., 0., 1., 1.], - >>> [ 0., 0., 1., 1.], - >>> [ 5., 5., 5., 5.]]) - >>> deltas = torch.Tensor([[ 0., 0., 0., 0.], - >>> [ 1., 1., 1., 1.], - >>> [ 0., 0., 2., -1.], - >>> [ 0.7, -1.9, -0.5, 0.3]]) - >>> delta2bbox(rois, deltas, max_shape=(32, 32, 3)) - tensor([[0.0000, 0.0000, 1.0000, 1.0000], - [0.1409, 0.1409, 2.8591, 2.8591], - [0.0000, 0.3161, 4.1945, 0.6839], - [5.0000, 5.0000, 5.0000, 5.0000]]) - """ - means = deltas.new_tensor(means).view(1, - -1).repeat(1, - deltas.size(-1) // 4) - stds = deltas.new_tensor(stds).view(1, -1).repeat(1, deltas.size(-1) // 4) - denorm_deltas = deltas * stds + means - dx = denorm_deltas[..., 0::4] - dy = denorm_deltas[..., 1::4] - dw = denorm_deltas[..., 2::4] - dh = denorm_deltas[..., 3::4] - - x1, y1 = rois[..., 0], rois[..., 1] - x2, y2 = rois[..., 2], rois[..., 3] - # Compute center of each roi - px = ((x1 + x2) * 0.5).unsqueeze(-1).expand_as(dx) - py = ((y1 + y2) * 0.5).unsqueeze(-1).expand_as(dy) - # Compute width/height of each roi - pw = (x2 - x1).unsqueeze(-1).expand_as(dw) - ph = (y2 - y1).unsqueeze(-1).expand_as(dh) - - dx_width = pw * dx - dy_height = ph * dy - - max_ratio = np.abs(np.log(wh_ratio_clip)) - if add_ctr_clamp: - dx_width = torch.clamp(dx_width, max=ctr_clamp, min=-ctr_clamp) - dy_height = torch.clamp(dy_height, max=ctr_clamp, min=-ctr_clamp) - dw = torch.clamp(dw, max=max_ratio) - dh = torch.clamp(dh, max=max_ratio) - else: - dw = dw.clamp(min=-max_ratio, max=max_ratio) - dh = dh.clamp(min=-max_ratio, max=max_ratio) - # Use exp(network energy) to enlarge/shrink each roi - gw = pw * dw.exp() - gh = ph * dh.exp() - # Use network energy to shift the center of each roi - gx = px + dx_width - gy = py + dy_height - # Convert center-xy/width/height to top-left, bottom-right - x1 = gx - gw * 0.5 - y1 = gy - gh * 0.5 - x2 = gx + gw * 0.5 - y2 = gy + gh * 0.5 - - bboxes = torch.stack([x1, y1, x2, y2], dim=-1).view(deltas.size()) - - if clip_border and max_shape is not None: - # clip bboxes with dynamic `min` and `max` for onnx - if torch.onnx.is_in_onnx_export(): - from mmdet.core.export import dynamic_clip_for_onnx - x1, y1, x2, y2 = dynamic_clip_for_onnx(x1, y1, x2, y2, max_shape) - bboxes = torch.stack([x1, y1, x2, y2], dim=-1).view(deltas.size()) - return bboxes - if not isinstance(max_shape, torch.Tensor): - max_shape = x1.new_tensor(max_shape) - max_shape = max_shape[..., :2].type_as(x1) - if max_shape.ndim == 2: - assert bboxes.ndim == 3 - assert max_shape.size(0) == bboxes.size(0) - - min_xy = x1.new_tensor(0) - max_xy = torch.cat( - [max_shape] * (deltas.size(-1) // 2), - dim=-1).flip(-1).unsqueeze(-2) - bboxes = torch.where(bboxes < min_xy, min_xy, bboxes) - bboxes = torch.where(bboxes > max_xy, max_xy, bboxes) - - return bboxes diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/distance_point_bbox_coder.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/distance_point_bbox_coder.py deleted file mode 100644 index 9f308a841..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/distance_point_bbox_coder.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..builder import BBOX_CODERS -from ..transforms import bbox2distance, distance2bbox -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class DistancePointBBoxCoder(BaseBBoxCoder): - """Distance Point BBox coder. - - This coder encodes gt bboxes (x1, y1, x2, y2) into (top, bottom, left, - right) and decode it back to the original. - - Args: - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Defaults to True. - """ - - def __init__(self, clip_border=True): - super(BaseBBoxCoder, self).__init__() - self.clip_border = clip_border - - def encode(self, points, gt_bboxes, max_dis=None, eps=0.1): - """Encode bounding box to distances. - - Args: - points (Tensor): Shape (N, 2), The format is [x, y]. - gt_bboxes (Tensor): Shape (N, 4), The format is "xyxy" - max_dis (float): Upper bound of the distance. Default None. - eps (float): a small value to ensure target < max_dis, instead <=. - Default 0.1. - - Returns: - Tensor: Box transformation deltas. The shape is (N, 4). - """ - assert points.size(0) == gt_bboxes.size(0) - assert points.size(-1) == 2 - assert gt_bboxes.size(-1) == 4 - return bbox2distance(points, gt_bboxes, max_dis, eps) - - def decode(self, points, pred_bboxes, max_shape=None): - """Decode distance prediction to bounding box. - - Args: - points (Tensor): Shape (B, N, 2) or (N, 2). - pred_bboxes (Tensor): Distance from the given point to 4 - boundaries (left, top, right, bottom). Shape (B, N, 4) - or (N, 4) - max_shape (Sequence[int] or torch.Tensor or Sequence[ - Sequence[int]],optional): Maximum bounds for boxes, specifies - (H, W, C) or (H, W). If priors shape is (B, N, 4), then - the max_shape should be a Sequence[Sequence[int]], - and the length of max_shape should also be B. - Default None. - Returns: - Tensor: Boxes with shape (N, 4) or (B, N, 4) - """ - assert points.size(0) == pred_bboxes.size(0) - assert points.size(-1) == 2 - assert pred_bboxes.size(-1) == 4 - if self.clip_border is False: - max_shape = None - return distance2bbox(points, pred_bboxes, max_shape) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/legacy_delta_xywh_bbox_coder.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/legacy_delta_xywh_bbox_coder.py deleted file mode 100644 index 7fa348b2d..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/legacy_delta_xywh_bbox_coder.py +++ /dev/null @@ -1,216 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np -import torch - -from ..builder import BBOX_CODERS -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class LegacyDeltaXYWHBBoxCoder(BaseBBoxCoder): - """Legacy Delta XYWH BBox coder used in MMDet V1.x. - - Following the practice in R-CNN [1]_, this coder encodes bbox (x1, y1, x2, - y2) into delta (dx, dy, dw, dh) and decodes delta (dx, dy, dw, dh) - back to original bbox (x1, y1, x2, y2). - - Note: - The main difference between :class`LegacyDeltaXYWHBBoxCoder` and - :class:`DeltaXYWHBBoxCoder` is whether ``+ 1`` is used during width and - height calculation. We suggest to only use this coder when testing with - MMDet V1.x models. - - References: - .. [1] https://arxiv.org/abs/1311.2524 - - Args: - target_means (Sequence[float]): denormalizing means of target for - delta coordinates - target_stds (Sequence[float]): denormalizing standard deviation of - target for delta coordinates - """ - - def __init__(self, - target_means=(0., 0., 0., 0.), - target_stds=(1., 1., 1., 1.)): - super(BaseBBoxCoder, self).__init__() - self.means = target_means - self.stds = target_stds - - def encode(self, bboxes, gt_bboxes): - """Get box regression transformation deltas that can be used to - transform the ``bboxes`` into the ``gt_bboxes``. - - Args: - bboxes (torch.Tensor): source boxes, e.g., object proposals. - gt_bboxes (torch.Tensor): target of the transformation, e.g., - ground-truth boxes. - - Returns: - torch.Tensor: Box transformation deltas - """ - assert bboxes.size(0) == gt_bboxes.size(0) - assert bboxes.size(-1) == gt_bboxes.size(-1) == 4 - encoded_bboxes = legacy_bbox2delta(bboxes, gt_bboxes, self.means, - self.stds) - return encoded_bboxes - - def decode(self, - bboxes, - pred_bboxes, - max_shape=None, - wh_ratio_clip=16 / 1000): - """Apply transformation `pred_bboxes` to `boxes`. - - Args: - boxes (torch.Tensor): Basic boxes. - pred_bboxes (torch.Tensor): Encoded boxes with shape - max_shape (tuple[int], optional): Maximum shape of boxes. - Defaults to None. - wh_ratio_clip (float, optional): The allowed ratio between - width and height. - - Returns: - torch.Tensor: Decoded boxes. - """ - assert pred_bboxes.size(0) == bboxes.size(0) - decoded_bboxes = legacy_delta2bbox(bboxes, pred_bboxes, self.means, - self.stds, max_shape, wh_ratio_clip) - - return decoded_bboxes - - -@mmcv.jit(coderize=True) -def legacy_bbox2delta(proposals, - gt, - means=(0., 0., 0., 0.), - stds=(1., 1., 1., 1.)): - """Compute deltas of proposals w.r.t. gt in the MMDet V1.x manner. - - We usually compute the deltas of x, y, w, h of proposals w.r.t ground - truth bboxes to get regression target. - This is the inverse function of `delta2bbox()` - - Args: - proposals (Tensor): Boxes to be transformed, shape (N, ..., 4) - gt (Tensor): Gt bboxes to be used as base, shape (N, ..., 4) - means (Sequence[float]): Denormalizing means for delta coordinates - stds (Sequence[float]): Denormalizing standard deviation for delta - coordinates - - Returns: - Tensor: deltas with shape (N, 4), where columns represent dx, dy, - dw, dh. - """ - assert proposals.size() == gt.size() - - proposals = proposals.float() - gt = gt.float() - px = (proposals[..., 0] + proposals[..., 2]) * 0.5 - py = (proposals[..., 1] + proposals[..., 3]) * 0.5 - pw = proposals[..., 2] - proposals[..., 0] + 1.0 - ph = proposals[..., 3] - proposals[..., 1] + 1.0 - - gx = (gt[..., 0] + gt[..., 2]) * 0.5 - gy = (gt[..., 1] + gt[..., 3]) * 0.5 - gw = gt[..., 2] - gt[..., 0] + 1.0 - gh = gt[..., 3] - gt[..., 1] + 1.0 - - dx = (gx - px) / pw - dy = (gy - py) / ph - dw = torch.log(gw / pw) - dh = torch.log(gh / ph) - deltas = torch.stack([dx, dy, dw, dh], dim=-1) - - means = deltas.new_tensor(means).unsqueeze(0) - stds = deltas.new_tensor(stds).unsqueeze(0) - deltas = deltas.sub_(means).div_(stds) - - return deltas - - -@mmcv.jit(coderize=True) -def legacy_delta2bbox(rois, - deltas, - means=(0., 0., 0., 0.), - stds=(1., 1., 1., 1.), - max_shape=None, - wh_ratio_clip=16 / 1000): - """Apply deltas to shift/scale base boxes in the MMDet V1.x manner. - - Typically the rois are anchor or proposed bounding boxes and the deltas are - network outputs used to shift/scale those boxes. - This is the inverse function of `bbox2delta()` - - Args: - rois (Tensor): Boxes to be transformed. Has shape (N, 4) - deltas (Tensor): Encoded offsets with respect to each roi. - Has shape (N, 4 * num_classes). Note N = num_anchors * W * H when - rois is a grid of anchors. Offset encoding follows [1]_. - means (Sequence[float]): Denormalizing means for delta coordinates - stds (Sequence[float]): Denormalizing standard deviation for delta - coordinates - max_shape (tuple[int, int]): Maximum bounds for boxes. specifies (H, W) - wh_ratio_clip (float): Maximum aspect ratio for boxes. - - Returns: - Tensor: Boxes with shape (N, 4), where columns represent - tl_x, tl_y, br_x, br_y. - - References: - .. [1] https://arxiv.org/abs/1311.2524 - - Example: - >>> rois = torch.Tensor([[ 0., 0., 1., 1.], - >>> [ 0., 0., 1., 1.], - >>> [ 0., 0., 1., 1.], - >>> [ 5., 5., 5., 5.]]) - >>> deltas = torch.Tensor([[ 0., 0., 0., 0.], - >>> [ 1., 1., 1., 1.], - >>> [ 0., 0., 2., -1.], - >>> [ 0.7, -1.9, -0.5, 0.3]]) - >>> legacy_delta2bbox(rois, deltas, max_shape=(32, 32)) - tensor([[0.0000, 0.0000, 1.5000, 1.5000], - [0.0000, 0.0000, 5.2183, 5.2183], - [0.0000, 0.1321, 7.8891, 0.8679], - [5.3967, 2.4251, 6.0033, 3.7749]]) - """ - means = deltas.new_tensor(means).repeat(1, deltas.size(1) // 4) - stds = deltas.new_tensor(stds).repeat(1, deltas.size(1) // 4) - denorm_deltas = deltas * stds + means - dx = denorm_deltas[:, 0::4] - dy = denorm_deltas[:, 1::4] - dw = denorm_deltas[:, 2::4] - dh = denorm_deltas[:, 3::4] - max_ratio = np.abs(np.log(wh_ratio_clip)) - dw = dw.clamp(min=-max_ratio, max=max_ratio) - dh = dh.clamp(min=-max_ratio, max=max_ratio) - # Compute center of each roi - px = ((rois[:, 0] + rois[:, 2]) * 0.5).unsqueeze(1).expand_as(dx) - py = ((rois[:, 1] + rois[:, 3]) * 0.5).unsqueeze(1).expand_as(dy) - # Compute width/height of each roi - pw = (rois[:, 2] - rois[:, 0] + 1.0).unsqueeze(1).expand_as(dw) - ph = (rois[:, 3] - rois[:, 1] + 1.0).unsqueeze(1).expand_as(dh) - # Use exp(network energy) to enlarge/shrink each roi - gw = pw * dw.exp() - gh = ph * dh.exp() - # Use network energy to shift the center of each roi - gx = px + pw * dx - gy = py + ph * dy - # Convert center-xy/width/height to top-left, bottom-right - - # The true legacy box coder should +- 0.5 here. - # However, current implementation improves the performance when testing - # the models trained in MMDetection 1.X (~0.5 bbox AP, 0.2 mask AP) - x1 = gx - gw * 0.5 - y1 = gy - gh * 0.5 - x2 = gx + gw * 0.5 - y2 = gy + gh * 0.5 - if max_shape is not None: - x1 = x1.clamp(min=0, max=max_shape[1] - 1) - y1 = y1.clamp(min=0, max=max_shape[0] - 1) - x2 = x2.clamp(min=0, max=max_shape[1] - 1) - y2 = y2.clamp(min=0, max=max_shape[0] - 1) - bboxes = torch.stack([x1, y1, x2, y2], dim=-1).view_as(deltas) - return bboxes diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/pseudo_bbox_coder.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/pseudo_bbox_coder.py deleted file mode 100644 index fe71f369c..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/pseudo_bbox_coder.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..builder import BBOX_CODERS -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class PseudoBBoxCoder(BaseBBoxCoder): - """Pseudo bounding box coder.""" - - def __init__(self, **kwargs): - super(BaseBBoxCoder, self).__init__(**kwargs) - - def encode(self, bboxes, gt_bboxes): - """torch.Tensor: return the given ``bboxes``""" - return gt_bboxes - - def decode(self, bboxes, pred_bboxes): - """torch.Tensor: return the given ``pred_bboxes``""" - return pred_bboxes diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/tblr_bbox_coder.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/tblr_bbox_coder.py deleted file mode 100644 index cb4206636..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/tblr_bbox_coder.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch - -from ..builder import BBOX_CODERS -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class TBLRBBoxCoder(BaseBBoxCoder): - """TBLR BBox coder. - - Following the practice in `FSAF `_, - this coder encodes gt bboxes (x1, y1, x2, y2) into (top, bottom, left, - right) and decode it back to the original. - - Args: - normalizer (list | float): Normalization factor to be - divided with when coding the coordinates. If it is a list, it should - have length of 4 indicating normalization factor in tblr dims. - Otherwise it is a unified float factor for all dims. Default: 4.0 - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Defaults to True. - """ - - def __init__(self, normalizer=4.0, clip_border=True): - super(BaseBBoxCoder, self).__init__() - self.normalizer = normalizer - self.clip_border = clip_border - - def encode(self, bboxes, gt_bboxes): - """Get box regression transformation deltas that can be used to - transform the ``bboxes`` into the ``gt_bboxes`` in the (top, left, - bottom, right) order. - - Args: - bboxes (torch.Tensor): source boxes, e.g., object proposals. - gt_bboxes (torch.Tensor): target of the transformation, e.g., - ground truth boxes. - - Returns: - torch.Tensor: Box transformation deltas - """ - assert bboxes.size(0) == gt_bboxes.size(0) - assert bboxes.size(-1) == gt_bboxes.size(-1) == 4 - encoded_bboxes = bboxes2tblr( - bboxes, gt_bboxes, normalizer=self.normalizer) - return encoded_bboxes - - def decode(self, bboxes, pred_bboxes, max_shape=None): - """Apply transformation `pred_bboxes` to `boxes`. - - Args: - bboxes (torch.Tensor): Basic boxes.Shape (B, N, 4) or (N, 4) - pred_bboxes (torch.Tensor): Encoded boxes with shape - (B, N, 4) or (N, 4) - max_shape (Sequence[int] or torch.Tensor or Sequence[ - Sequence[int]],optional): Maximum bounds for boxes, specifies - (H, W, C) or (H, W). If bboxes shape is (B, N, 4), then - the max_shape should be a Sequence[Sequence[int]] - and the length of max_shape should also be B. - - Returns: - torch.Tensor: Decoded boxes. - """ - decoded_bboxes = tblr2bboxes( - bboxes, - pred_bboxes, - normalizer=self.normalizer, - max_shape=max_shape, - clip_border=self.clip_border) - - return decoded_bboxes - - -@mmcv.jit(coderize=True) -def bboxes2tblr(priors, gts, normalizer=4.0, normalize_by_wh=True): - """Encode ground truth boxes to tblr coordinate. - - It first convert the gt coordinate to tblr format, - (top, bottom, left, right), relative to prior box centers. - The tblr coordinate may be normalized by the side length of prior bboxes - if `normalize_by_wh` is specified as True, and it is then normalized by - the `normalizer` factor. - - Args: - priors (Tensor): Prior boxes in point form - Shape: (num_proposals,4). - gts (Tensor): Coords of ground truth for each prior in point-form - Shape: (num_proposals, 4). - normalizer (Sequence[float] | float): normalization parameter of - encoded boxes. If it is a list, it has to have length = 4. - Default: 4.0 - normalize_by_wh (bool): Whether to normalize tblr coordinate by the - side length (wh) of prior bboxes. - - Return: - encoded boxes (Tensor), Shape: (num_proposals, 4) - """ - - # dist b/t match center and prior's center - if not isinstance(normalizer, float): - normalizer = torch.tensor(normalizer, device=priors.device) - assert len(normalizer) == 4, 'Normalizer must have length = 4' - assert priors.size(0) == gts.size(0) - prior_centers = (priors[:, 0:2] + priors[:, 2:4]) / 2 - xmin, ymin, xmax, ymax = gts.split(1, dim=1) - top = prior_centers[:, 1].unsqueeze(1) - ymin - bottom = ymax - prior_centers[:, 1].unsqueeze(1) - left = prior_centers[:, 0].unsqueeze(1) - xmin - right = xmax - prior_centers[:, 0].unsqueeze(1) - loc = torch.cat((top, bottom, left, right), dim=1) - if normalize_by_wh: - # Normalize tblr by anchor width and height - wh = priors[:, 2:4] - priors[:, 0:2] - w, h = torch.split(wh, 1, dim=1) - loc[:, :2] /= h # tb is normalized by h - loc[:, 2:] /= w # lr is normalized by w - # Normalize tblr by the given normalization factor - return loc / normalizer - - -@mmcv.jit(coderize=True) -def tblr2bboxes(priors, - tblr, - normalizer=4.0, - normalize_by_wh=True, - max_shape=None, - clip_border=True): - """Decode tblr outputs to prediction boxes. - - The process includes 3 steps: 1) De-normalize tblr coordinates by - multiplying it with `normalizer`; 2) De-normalize tblr coordinates by the - prior bbox width and height if `normalize_by_wh` is `True`; 3) Convert - tblr (top, bottom, left, right) pair relative to the center of priors back - to (xmin, ymin, xmax, ymax) coordinate. - - Args: - priors (Tensor): Prior boxes in point form (x0, y0, x1, y1) - Shape: (N,4) or (B, N, 4). - tblr (Tensor): Coords of network output in tblr form - Shape: (N, 4) or (B, N, 4). - normalizer (Sequence[float] | float): Normalization parameter of - encoded boxes. By list, it represents the normalization factors at - tblr dims. By float, it is the unified normalization factor at all - dims. Default: 4.0 - normalize_by_wh (bool): Whether the tblr coordinates have been - normalized by the side length (wh) of prior bboxes. - max_shape (Sequence[int] or torch.Tensor or Sequence[ - Sequence[int]],optional): Maximum bounds for boxes, specifies - (H, W, C) or (H, W). If priors shape is (B, N, 4), then - the max_shape should be a Sequence[Sequence[int]] - and the length of max_shape should also be B. - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Defaults to True. - - Return: - encoded boxes (Tensor): Boxes with shape (N, 4) or (B, N, 4) - """ - if not isinstance(normalizer, float): - normalizer = torch.tensor(normalizer, device=priors.device) - assert len(normalizer) == 4, 'Normalizer must have length = 4' - assert priors.size(0) == tblr.size(0) - if priors.ndim == 3: - assert priors.size(1) == tblr.size(1) - - loc_decode = tblr * normalizer - prior_centers = (priors[..., 0:2] + priors[..., 2:4]) / 2 - if normalize_by_wh: - wh = priors[..., 2:4] - priors[..., 0:2] - w, h = torch.split(wh, 1, dim=-1) - # Inplace operation with slice would failed for exporting to ONNX - th = h * loc_decode[..., :2] # tb - tw = w * loc_decode[..., 2:] # lr - loc_decode = torch.cat([th, tw], dim=-1) - # Cannot be exported using onnx when loc_decode.split(1, dim=-1) - top, bottom, left, right = loc_decode.split((1, 1, 1, 1), dim=-1) - xmin = prior_centers[..., 0].unsqueeze(-1) - left - xmax = prior_centers[..., 0].unsqueeze(-1) + right - ymin = prior_centers[..., 1].unsqueeze(-1) - top - ymax = prior_centers[..., 1].unsqueeze(-1) + bottom - - bboxes = torch.cat((xmin, ymin, xmax, ymax), dim=-1) - - if clip_border and max_shape is not None: - # clip bboxes with dynamic `min` and `max` for onnx - if torch.onnx.is_in_onnx_export(): - from mmdet.core.export import dynamic_clip_for_onnx - xmin, ymin, xmax, ymax = dynamic_clip_for_onnx( - xmin, ymin, xmax, ymax, max_shape) - bboxes = torch.cat([xmin, ymin, xmax, ymax], dim=-1) - return bboxes - if not isinstance(max_shape, torch.Tensor): - max_shape = priors.new_tensor(max_shape) - max_shape = max_shape[..., :2].type_as(priors) - if max_shape.ndim == 2: - assert bboxes.ndim == 3 - assert max_shape.size(0) == bboxes.size(0) - - min_xy = priors.new_tensor(0) - max_xy = torch.cat([max_shape, max_shape], - dim=-1).flip(-1).unsqueeze(-2) - bboxes = torch.where(bboxes < min_xy, min_xy, bboxes) - bboxes = torch.where(bboxes > max_xy, max_xy, bboxes) - - return bboxes diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/yolo_bbox_coder.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/yolo_bbox_coder.py deleted file mode 100644 index 2852eca75..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/coder/yolo_bbox_coder.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch - -from ..builder import BBOX_CODERS -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class YOLOBBoxCoder(BaseBBoxCoder): - """YOLO BBox coder. - - Following `YOLO `_, this coder divide - image into grids, and encode bbox (x1, y1, x2, y2) into (cx, cy, dw, dh). - cx, cy in [0., 1.], denotes relative center position w.r.t the center of - bboxes. dw, dh are the same as :obj:`DeltaXYWHBBoxCoder`. - - Args: - eps (float): Min value of cx, cy when encoding. - """ - - def __init__(self, eps=1e-6): - super(BaseBBoxCoder, self).__init__() - self.eps = eps - - @mmcv.jit(coderize=True) - def encode(self, bboxes, gt_bboxes, stride): - """Get box regression transformation deltas that can be used to - transform the ``bboxes`` into the ``gt_bboxes``. - - Args: - bboxes (torch.Tensor): Source boxes, e.g., anchors. - gt_bboxes (torch.Tensor): Target of the transformation, e.g., - ground-truth boxes. - stride (torch.Tensor | int): Stride of bboxes. - - Returns: - torch.Tensor: Box transformation deltas - """ - - assert bboxes.size(0) == gt_bboxes.size(0) - assert bboxes.size(-1) == gt_bboxes.size(-1) == 4 - x_center_gt = (gt_bboxes[..., 0] + gt_bboxes[..., 2]) * 0.5 - y_center_gt = (gt_bboxes[..., 1] + gt_bboxes[..., 3]) * 0.5 - w_gt = gt_bboxes[..., 2] - gt_bboxes[..., 0] - h_gt = gt_bboxes[..., 3] - gt_bboxes[..., 1] - x_center = (bboxes[..., 0] + bboxes[..., 2]) * 0.5 - y_center = (bboxes[..., 1] + bboxes[..., 3]) * 0.5 - w = bboxes[..., 2] - bboxes[..., 0] - h = bboxes[..., 3] - bboxes[..., 1] - w_target = torch.log((w_gt / w).clamp(min=self.eps)) - h_target = torch.log((h_gt / h).clamp(min=self.eps)) - x_center_target = ((x_center_gt - x_center) / stride + 0.5).clamp( - self.eps, 1 - self.eps) - y_center_target = ((y_center_gt - y_center) / stride + 0.5).clamp( - self.eps, 1 - self.eps) - encoded_bboxes = torch.stack( - [x_center_target, y_center_target, w_target, h_target], dim=-1) - return encoded_bboxes - - @mmcv.jit(coderize=True) - def decode(self, bboxes, pred_bboxes, stride): - """Apply transformation `pred_bboxes` to `boxes`. - - Args: - boxes (torch.Tensor): Basic boxes, e.g. anchors. - pred_bboxes (torch.Tensor): Encoded boxes with shape - stride (torch.Tensor | int): Strides of bboxes. - - Returns: - torch.Tensor: Decoded boxes. - """ - assert pred_bboxes.size(-1) == bboxes.size(-1) == 4 - xy_centers = (bboxes[..., :2] + bboxes[..., 2:]) * 0.5 + ( - pred_bboxes[..., :2] - 0.5) * stride - whs = (bboxes[..., 2:] - - bboxes[..., :2]) * 0.5 * pred_bboxes[..., 2:].exp() - decoded_bboxes = torch.stack( - (xy_centers[..., 0] - whs[..., 0], xy_centers[..., 1] - - whs[..., 1], xy_centers[..., 0] + whs[..., 0], - xy_centers[..., 1] + whs[..., 1]), - dim=-1) - return decoded_bboxes diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/demodata.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/demodata.py deleted file mode 100644 index eb24b34b6..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/demodata.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch - -from mmdet.utils.util_random import ensure_rng - - -def random_boxes(num=1, scale=1, rng=None): - """Simple version of ``kwimage.Boxes.random`` - - Returns: - Tensor: shape (n, 4) in x1, y1, x2, y2 format. - - References: - https://gitlab.kitware.com/computer-vision/kwimage/blob/master/kwimage/structs/boxes.py#L1390 - - Example: - >>> num = 3 - >>> scale = 512 - >>> rng = 0 - >>> boxes = random_boxes(num, scale, rng) - >>> print(boxes) - tensor([[280.9925, 278.9802, 308.6148, 366.1769], - [216.9113, 330.6978, 224.0446, 456.5878], - [405.3632, 196.3221, 493.3953, 270.7942]]) - """ - rng = ensure_rng(rng) - - tlbr = rng.rand(num, 4).astype(np.float32) - - tl_x = np.minimum(tlbr[:, 0], tlbr[:, 2]) - tl_y = np.minimum(tlbr[:, 1], tlbr[:, 3]) - br_x = np.maximum(tlbr[:, 0], tlbr[:, 2]) - br_y = np.maximum(tlbr[:, 1], tlbr[:, 3]) - - tlbr[:, 0] = tl_x * scale - tlbr[:, 1] = tl_y * scale - tlbr[:, 2] = br_x * scale - tlbr[:, 3] = br_y * scale - - boxes = torch.from_numpy(tlbr) - return boxes diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/iou_calculators/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/iou_calculators/__init__.py deleted file mode 100644 index 04ba925b4..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/iou_calculators/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_iou_calculator -from .iou2d_calculator import BboxOverlaps2D, bbox_overlaps - -__all__ = ['build_iou_calculator', 'BboxOverlaps2D', 'bbox_overlaps'] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/iou_calculators/builder.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/iou_calculators/builder.py deleted file mode 100644 index 378ee269f..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/iou_calculators/builder.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, build_from_cfg - -IOU_CALCULATORS = Registry('IoU calculator') - - -def build_iou_calculator(cfg, default_args=None): - """Builder of IoU calculator.""" - return build_from_cfg(cfg, IOU_CALCULATORS, default_args) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/iou_calculators/iou2d_calculator.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/iou_calculators/iou2d_calculator.py deleted file mode 100644 index 4656d6198..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/iou_calculators/iou2d_calculator.py +++ /dev/null @@ -1,261 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .builder import IOU_CALCULATORS - - -def cast_tensor_type(x, scale=1., dtype=None): - if dtype == 'fp16': - # scale is for preventing overflows - x = (x / scale).half() - return x - - -def fp16_clamp(x, min=None, max=None): - if not x.is_cuda and x.dtype == torch.float16: - # clamp for cpu float16, tensor fp16 has no clamp implementation - return x.float().clamp(min, max).half() - - return x.clamp(min, max) - - -@IOU_CALCULATORS.register_module() -class BboxOverlaps2D: - """2D Overlaps (e.g. IoUs, GIoUs) Calculator.""" - - def __init__(self, scale=1., dtype=None): - self.scale = scale - self.dtype = dtype - - def __call__(self, bboxes1, bboxes2, mode='iou', is_aligned=False): - """Calculate IoU between 2D bboxes. - - Args: - bboxes1 (Tensor): bboxes have shape (m, 4) in - format, or shape (m, 5) in format. - bboxes2 (Tensor): bboxes have shape (m, 4) in - format, shape (m, 5) in format, or be - empty. If ``is_aligned `` is ``True``, then m and n must be - equal. - mode (str): "iou" (intersection over union), "iof" (intersection - over foreground), or "giou" (generalized intersection over - union). - is_aligned (bool, optional): If True, then m and n must be equal. - Default False. - - Returns: - Tensor: shape (m, n) if ``is_aligned `` is False else shape (m,) - """ - assert bboxes1.size(-1) in [0, 4, 5] - assert bboxes2.size(-1) in [0, 4, 5] - if bboxes2.size(-1) == 5: - bboxes2 = bboxes2[..., :4] - if bboxes1.size(-1) == 5: - bboxes1 = bboxes1[..., :4] - - if self.dtype == 'fp16': - # change tensor type to save cpu and cuda memory and keep speed - bboxes1 = cast_tensor_type(bboxes1, self.scale, self.dtype) - bboxes2 = cast_tensor_type(bboxes2, self.scale, self.dtype) - overlaps = bbox_overlaps(bboxes1, bboxes2, mode, is_aligned) - if not overlaps.is_cuda and overlaps.dtype == torch.float16: - # resume cpu float32 - overlaps = overlaps.float() - return overlaps - - return bbox_overlaps(bboxes1, bboxes2, mode, is_aligned) - - def __repr__(self): - """str: a string describing the module""" - repr_str = self.__class__.__name__ + f'(' \ - f'scale={self.scale}, dtype={self.dtype})' - return repr_str - - -def bbox_overlaps(bboxes1, bboxes2, mode='iou', is_aligned=False, eps=1e-6): - """Calculate overlap between two set of bboxes. - - FP16 Contributed by https://github.com/open-mmlab/mmdetection/pull/4889 - Note: - Assume bboxes1 is M x 4, bboxes2 is N x 4, when mode is 'iou', - there are some new generated variable when calculating IOU - using bbox_overlaps function: - - 1) is_aligned is False - area1: M x 1 - area2: N x 1 - lt: M x N x 2 - rb: M x N x 2 - wh: M x N x 2 - overlap: M x N x 1 - union: M x N x 1 - ious: M x N x 1 - - Total memory: - S = (9 x N x M + N + M) * 4 Byte, - - When using FP16, we can reduce: - R = (9 x N x M + N + M) * 4 / 2 Byte - R large than (N + M) * 4 * 2 is always true when N and M >= 1. - Obviously, N + M <= N * M < 3 * N * M, when N >=2 and M >=2, - N + 1 < 3 * N, when N or M is 1. - - Given M = 40 (ground truth), N = 400000 (three anchor boxes - in per grid, FPN, R-CNNs), - R = 275 MB (one times) - - A special case (dense detection), M = 512 (ground truth), - R = 3516 MB = 3.43 GB - - When the batch size is B, reduce: - B x R - - Therefore, CUDA memory runs out frequently. - - Experiments on GeForce RTX 2080Ti (11019 MiB): - - | dtype | M | N | Use | Real | Ideal | - |:----:|:----:|:----:|:----:|:----:|:----:| - | FP32 | 512 | 400000 | 8020 MiB | -- | -- | - | FP16 | 512 | 400000 | 4504 MiB | 3516 MiB | 3516 MiB | - | FP32 | 40 | 400000 | 1540 MiB | -- | -- | - | FP16 | 40 | 400000 | 1264 MiB | 276MiB | 275 MiB | - - 2) is_aligned is True - area1: N x 1 - area2: N x 1 - lt: N x 2 - rb: N x 2 - wh: N x 2 - overlap: N x 1 - union: N x 1 - ious: N x 1 - - Total memory: - S = 11 x N * 4 Byte - - When using FP16, we can reduce: - R = 11 x N * 4 / 2 Byte - - So do the 'giou' (large than 'iou'). - - Time-wise, FP16 is generally faster than FP32. - - When gpu_assign_thr is not -1, it takes more time on cpu - but not reduce memory. - There, we can reduce half the memory and keep the speed. - - If ``is_aligned`` is ``False``, then calculate the overlaps between each - bbox of bboxes1 and bboxes2, otherwise the overlaps between each aligned - pair of bboxes1 and bboxes2. - - Args: - bboxes1 (Tensor): shape (B, m, 4) in format or empty. - bboxes2 (Tensor): shape (B, n, 4) in format or empty. - B indicates the batch dim, in shape (B1, B2, ..., Bn). - If ``is_aligned`` is ``True``, then m and n must be equal. - mode (str): "iou" (intersection over union), "iof" (intersection over - foreground) or "giou" (generalized intersection over union). - Default "iou". - is_aligned (bool, optional): If True, then m and n must be equal. - Default False. - eps (float, optional): A value added to the denominator for numerical - stability. Default 1e-6. - - Returns: - Tensor: shape (m, n) if ``is_aligned`` is False else shape (m,) - - Example: - >>> bboxes1 = torch.FloatTensor([ - >>> [0, 0, 10, 10], - >>> [10, 10, 20, 20], - >>> [32, 32, 38, 42], - >>> ]) - >>> bboxes2 = torch.FloatTensor([ - >>> [0, 0, 10, 20], - >>> [0, 10, 10, 19], - >>> [10, 10, 20, 20], - >>> ]) - >>> overlaps = bbox_overlaps(bboxes1, bboxes2) - >>> assert overlaps.shape == (3, 3) - >>> overlaps = bbox_overlaps(bboxes1, bboxes2, is_aligned=True) - >>> assert overlaps.shape == (3, ) - - Example: - >>> empty = torch.empty(0, 4) - >>> nonempty = torch.FloatTensor([[0, 0, 10, 9]]) - >>> assert tuple(bbox_overlaps(empty, nonempty).shape) == (0, 1) - >>> assert tuple(bbox_overlaps(nonempty, empty).shape) == (1, 0) - >>> assert tuple(bbox_overlaps(empty, empty).shape) == (0, 0) - """ - - assert mode in ['iou', 'iof', 'giou'], f'Unsupported mode {mode}' - # Either the boxes are empty or the length of boxes' last dimension is 4 - assert (bboxes1.size(-1) == 4 or bboxes1.size(0) == 0) - assert (bboxes2.size(-1) == 4 or bboxes2.size(0) == 0) - - # Batch dim must be the same - # Batch dim: (B1, B2, ... Bn) - assert bboxes1.shape[:-2] == bboxes2.shape[:-2] - batch_shape = bboxes1.shape[:-2] - - rows = bboxes1.size(-2) - cols = bboxes2.size(-2) - if is_aligned: - assert rows == cols - - if rows * cols == 0: - if is_aligned: - return bboxes1.new(batch_shape + (rows, )) - else: - return bboxes1.new(batch_shape + (rows, cols)) - - area1 = (bboxes1[..., 2] - bboxes1[..., 0]) * ( - bboxes1[..., 3] - bboxes1[..., 1]) - area2 = (bboxes2[..., 2] - bboxes2[..., 0]) * ( - bboxes2[..., 3] - bboxes2[..., 1]) - - if is_aligned: - lt = torch.max(bboxes1[..., :2], bboxes2[..., :2]) # [B, rows, 2] - rb = torch.min(bboxes1[..., 2:], bboxes2[..., 2:]) # [B, rows, 2] - - wh = fp16_clamp(rb - lt, min=0) - overlap = wh[..., 0] * wh[..., 1] - - if mode in ['iou', 'giou']: - union = area1 + area2 - overlap - else: - union = area1 - if mode == 'giou': - enclosed_lt = torch.min(bboxes1[..., :2], bboxes2[..., :2]) - enclosed_rb = torch.max(bboxes1[..., 2:], bboxes2[..., 2:]) - else: - lt = torch.max(bboxes1[..., :, None, :2], - bboxes2[..., None, :, :2]) # [B, rows, cols, 2] - rb = torch.min(bboxes1[..., :, None, 2:], - bboxes2[..., None, :, 2:]) # [B, rows, cols, 2] - - wh = fp16_clamp(rb - lt, min=0) - overlap = wh[..., 0] * wh[..., 1] - - if mode in ['iou', 'giou']: - union = area1[..., None] + area2[..., None, :] - overlap - else: - union = area1[..., None] - if mode == 'giou': - enclosed_lt = torch.min(bboxes1[..., :, None, :2], - bboxes2[..., None, :, :2]) - enclosed_rb = torch.max(bboxes1[..., :, None, 2:], - bboxes2[..., None, :, 2:]) - - eps = union.new_tensor([eps]) - union = torch.max(union, eps) - ious = overlap / union - if mode in ['iou', 'iof']: - return ious - # calculate gious - enclose_wh = fp16_clamp(enclosed_rb - enclosed_lt, min=0) - enclose_area = enclose_wh[..., 0] * enclose_wh[..., 1] - enclose_area = torch.max(enclose_area, eps) - gious = ious - (enclose_area - union) / enclose_area - return gious diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/match_costs/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/match_costs/__init__.py deleted file mode 100644 index 1b6367950..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/match_costs/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_match_cost -from .match_cost import (BBoxL1Cost, ClassificationCost, CrossEntropyLossCost, - DiceCost, FocalLossCost, IoUCost) - -__all__ = [ - 'build_match_cost', 'ClassificationCost', 'BBoxL1Cost', 'IoUCost', - 'FocalLossCost', 'DiceCost', 'CrossEntropyLossCost' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/match_costs/builder.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/match_costs/builder.py deleted file mode 100644 index ea086adff..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/match_costs/builder.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, build_from_cfg - -MATCH_COST = Registry('Match Cost') - - -def build_match_cost(cfg, default_args=None): - """Builder of IoU calculator.""" - return build_from_cfg(cfg, MATCH_COST, default_args) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/match_costs/match_cost.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/match_costs/match_cost.py deleted file mode 100644 index 4342b0245..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/match_costs/match_cost.py +++ /dev/null @@ -1,359 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn.functional as F - -from mmdet.core.bbox.iou_calculators import bbox_overlaps -from mmdet.core.bbox.transforms import bbox_cxcywh_to_xyxy, bbox_xyxy_to_cxcywh -from .builder import MATCH_COST - - -@MATCH_COST.register_module() -class BBoxL1Cost: - """BBoxL1Cost. - - Args: - weight (int | float, optional): loss_weight - box_format (str, optional): 'xyxy' for DETR, 'xywh' for Sparse_RCNN - - Examples: - >>> from mmdet.core.bbox.match_costs.match_cost import BBoxL1Cost - >>> import torch - >>> self = BBoxL1Cost() - >>> bbox_pred = torch.rand(1, 4) - >>> gt_bboxes= torch.FloatTensor([[0, 0, 2, 4], [1, 2, 3, 4]]) - >>> factor = torch.tensor([10, 8, 10, 8]) - >>> self(bbox_pred, gt_bboxes, factor) - tensor([[1.6172, 1.6422]]) - """ - - def __init__(self, weight=1., box_format='xyxy'): - self.weight = weight - assert box_format in ['xyxy', 'xywh'] - self.box_format = box_format - - def __call__(self, bbox_pred, gt_bboxes): - """ - Args: - bbox_pred (Tensor): Predicted boxes with normalized coordinates - (cx, cy, w, h), which are all in range [0, 1]. Shape - (num_query, 4). - gt_bboxes (Tensor): Ground truth boxes with normalized - coordinates (x1, y1, x2, y2). Shape (num_gt, 4). - - Returns: - torch.Tensor: bbox_cost value with weight - """ - if self.box_format == 'xywh': - gt_bboxes = bbox_xyxy_to_cxcywh(gt_bboxes) - elif self.box_format == 'xyxy': - bbox_pred = bbox_cxcywh_to_xyxy(bbox_pred) - bbox_cost = torch.cdist(bbox_pred, gt_bboxes, p=1) - return bbox_cost * self.weight - - -@MATCH_COST.register_module() -class FocalLossCost: - """FocalLossCost. - - Args: - weight (int | float, optional): loss_weight - alpha (int | float, optional): focal_loss alpha - gamma (int | float, optional): focal_loss gamma - eps (float, optional): default 1e-12 - binary_input (bool, optional): Whether the input is binary, - default False. - - Examples: - >>> from mmdet.core.bbox.match_costs.match_cost import FocalLossCost - >>> import torch - >>> self = FocalLossCost() - >>> cls_pred = torch.rand(4, 3) - >>> gt_labels = torch.tensor([0, 1, 2]) - >>> factor = torch.tensor([10, 8, 10, 8]) - >>> self(cls_pred, gt_labels) - tensor([[-0.3236, -0.3364, -0.2699], - [-0.3439, -0.3209, -0.4807], - [-0.4099, -0.3795, -0.2929], - [-0.1950, -0.1207, -0.2626]]) - """ - - def __init__(self, - weight=1., - alpha=0.25, - gamma=2, - eps=1e-12, - binary_input=False): - self.weight = weight - self.alpha = alpha - self.gamma = gamma - self.eps = eps - self.binary_input = binary_input - - def _focal_loss_cost(self, cls_pred, gt_labels): - """ - Args: - cls_pred (Tensor): Predicted classification logits, shape - (num_query, num_class). - gt_labels (Tensor): Label of `gt_bboxes`, shape (num_gt,). - - Returns: - torch.Tensor: cls_cost value with weight - """ - cls_pred = cls_pred.sigmoid() - neg_cost = -(1 - cls_pred + self.eps).log() * ( - 1 - self.alpha) * cls_pred.pow(self.gamma) - pos_cost = -(cls_pred + self.eps).log() * self.alpha * ( - 1 - cls_pred).pow(self.gamma) - - cls_cost = pos_cost[:, gt_labels] - neg_cost[:, gt_labels] - return cls_cost * self.weight - - def _mask_focal_loss_cost(self, cls_pred, gt_labels): - """ - Args: - cls_pred (Tensor): Predicted classfication logits - in shape (num_query, d1, ..., dn), dtype=torch.float32. - gt_labels (Tensor): Ground truth in shape (num_gt, d1, ..., dn), - dtype=torch.long. Labels should be binary. - - Returns: - Tensor: Focal cost matrix with weight in shape\ - (num_query, num_gt). - """ - cls_pred = cls_pred.flatten(1) - gt_labels = gt_labels.flatten(1).float() - n = cls_pred.shape[1] - cls_pred = cls_pred.sigmoid() - neg_cost = -(1 - cls_pred + self.eps).log() * ( - 1 - self.alpha) * cls_pred.pow(self.gamma) - pos_cost = -(cls_pred + self.eps).log() * self.alpha * ( - 1 - cls_pred).pow(self.gamma) - - cls_cost = torch.einsum('nc,mc->nm', pos_cost, gt_labels) + \ - torch.einsum('nc,mc->nm', neg_cost, (1 - gt_labels)) - return cls_cost / n * self.weight - - def __call__(self, cls_pred, gt_labels): - """ - Args: - cls_pred (Tensor): Predicted classfication logits. - gt_labels (Tensor)): Labels. - - Returns: - Tensor: Focal cost matrix with weight in shape\ - (num_query, num_gt). - """ - if self.binary_input: - return self._mask_focal_loss_cost(cls_pred, gt_labels) - else: - return self._focal_loss_cost(cls_pred, gt_labels) - - -@MATCH_COST.register_module() -class ClassificationCost: - """ClsSoftmaxCost. - - Args: - weight (int | float, optional): loss_weight - - Examples: - >>> from mmdet.core.bbox.match_costs.match_cost import \ - ... ClassificationCost - >>> import torch - >>> self = ClassificationCost() - >>> cls_pred = torch.rand(4, 3) - >>> gt_labels = torch.tensor([0, 1, 2]) - >>> factor = torch.tensor([10, 8, 10, 8]) - >>> self(cls_pred, gt_labels) - tensor([[-0.3430, -0.3525, -0.3045], - [-0.3077, -0.2931, -0.3992], - [-0.3664, -0.3455, -0.2881], - [-0.3343, -0.2701, -0.3956]]) - """ - - def __init__(self, weight=1.): - self.weight = weight - - def __call__(self, cls_pred, gt_labels): - """ - Args: - cls_pred (Tensor): Predicted classification logits, shape - (num_query, num_class). - gt_labels (Tensor): Label of `gt_bboxes`, shape (num_gt,). - - Returns: - torch.Tensor: cls_cost value with weight - """ - # Following the official DETR repo, contrary to the loss that - # NLL is used, we approximate it in 1 - cls_score[gt_label]. - # The 1 is a constant that doesn't change the matching, - # so it can be omitted. - cls_score = cls_pred.softmax(-1) - cls_cost = -cls_score[:, gt_labels] - return cls_cost * self.weight - - -@MATCH_COST.register_module() -class IoUCost: - """IoUCost. - - Args: - iou_mode (str, optional): iou mode such as 'iou' | 'giou' - weight (int | float, optional): loss weight - - Examples: - >>> from mmdet.core.bbox.match_costs.match_cost import IoUCost - >>> import torch - >>> self = IoUCost() - >>> bboxes = torch.FloatTensor([[1,1, 2, 2], [2, 2, 3, 4]]) - >>> gt_bboxes = torch.FloatTensor([[0, 0, 2, 4], [1, 2, 3, 4]]) - >>> self(bboxes, gt_bboxes) - tensor([[-0.1250, 0.1667], - [ 0.1667, -0.5000]]) - """ - - def __init__(self, iou_mode='giou', weight=1.): - self.weight = weight - self.iou_mode = iou_mode - - def __call__(self, bboxes, gt_bboxes): - """ - Args: - bboxes (Tensor): Predicted boxes with unnormalized coordinates - (x1, y1, x2, y2). Shape (num_query, 4). - gt_bboxes (Tensor): Ground truth boxes with unnormalized - coordinates (x1, y1, x2, y2). Shape (num_gt, 4). - - Returns: - torch.Tensor: iou_cost value with weight - """ - # overlaps: [num_bboxes, num_gt] - overlaps = bbox_overlaps( - bboxes, gt_bboxes, mode=self.iou_mode, is_aligned=False) - # The 1 is a constant that doesn't change the matching, so omitted. - iou_cost = -overlaps - return iou_cost * self.weight - - -@MATCH_COST.register_module() -class DiceCost: - """Cost of mask assignments based on dice losses. - - Args: - weight (int | float, optional): loss_weight. Defaults to 1. - pred_act (bool, optional): Whether to apply sigmoid to mask_pred. - Defaults to False. - eps (float, optional): default 1e-12. - naive_dice (bool, optional): If True, use the naive dice loss - in which the power of the number in the denominator is - the first power. If Flase, use the second power that - is adopted by K-Net and SOLO. - Defaults to True. - """ - - def __init__(self, weight=1., pred_act=False, eps=1e-3, naive_dice=True): - self.weight = weight - self.pred_act = pred_act - self.eps = eps - self.naive_dice = naive_dice - - def binary_mask_dice_loss(self, mask_preds, gt_masks): - """ - Args: - mask_preds (Tensor): Mask prediction in shape (num_query, *). - gt_masks (Tensor): Ground truth in shape (num_gt, *) - store 0 or 1, 0 for negative class and 1 for - positive class. - - Returns: - Tensor: Dice cost matrix in shape (num_query, num_gt). - """ - mask_preds = mask_preds.flatten(1) - gt_masks = gt_masks.flatten(1).float() - numerator = 2 * torch.einsum('nc,mc->nm', mask_preds, gt_masks) - if self.naive_dice: - denominator = mask_preds.sum(-1)[:, None] + \ - gt_masks.sum(-1)[None, :] - else: - denominator = mask_preds.pow(2).sum(1)[:, None] + \ - gt_masks.pow(2).sum(1)[None, :] - loss = 1 - (numerator + self.eps) / (denominator + self.eps) - return loss - - def __call__(self, mask_preds, gt_masks): - """ - Args: - mask_preds (Tensor): Mask prediction logits in shape (num_query, *) - gt_masks (Tensor): Ground truth in shape (num_gt, *) - - Returns: - Tensor: Dice cost matrix with weight in shape (num_query, num_gt). - """ - if self.pred_act: - mask_preds = mask_preds.sigmoid() - dice_cost = self.binary_mask_dice_loss(mask_preds, gt_masks) - return dice_cost * self.weight - - -@MATCH_COST.register_module() -class CrossEntropyLossCost: - """CrossEntropyLossCost. - - Args: - weight (int | float, optional): loss weight. Defaults to 1. - use_sigmoid (bool, optional): Whether the prediction uses sigmoid - of softmax. Defaults to True. - Examples: - >>> from mmdet.core.bbox.match_costs import CrossEntropyLossCost - >>> import torch - >>> bce = CrossEntropyLossCost(use_sigmoid=True) - >>> cls_pred = torch.tensor([[7.6, 1.2], [-1.3, 10]]) - >>> gt_labels = torch.tensor([[1, 1], [1, 0]]) - >>> print(bce(cls_pred, gt_labels)) - """ - - def __init__(self, weight=1., use_sigmoid=True): - assert use_sigmoid, 'use_sigmoid = False is not supported yet.' - self.weight = weight - self.use_sigmoid = use_sigmoid - - def _binary_cross_entropy(self, cls_pred, gt_labels): - """ - Args: - cls_pred (Tensor): The prediction with shape (num_query, 1, *) or - (num_query, *). - gt_labels (Tensor): The learning label of prediction with - shape (num_gt, *). - - Returns: - Tensor: Cross entropy cost matrix in shape (num_query, num_gt). - """ - cls_pred = cls_pred.flatten(1).float() - gt_labels = gt_labels.flatten(1).float() - n = cls_pred.shape[1] - pos = F.binary_cross_entropy_with_logits( - cls_pred, torch.ones_like(cls_pred), reduction='none') - neg = F.binary_cross_entropy_with_logits( - cls_pred, torch.zeros_like(cls_pred), reduction='none') - cls_cost = torch.einsum('nc,mc->nm', pos, gt_labels) + \ - torch.einsum('nc,mc->nm', neg, 1 - gt_labels) - cls_cost = cls_cost / n - - return cls_cost - - def __call__(self, cls_pred, gt_labels): - """ - Args: - cls_pred (Tensor): Predicted classification logits. - gt_labels (Tensor): Labels. - - Returns: - Tensor: Cross entropy cost matrix with weight in - shape (num_query, num_gt). - """ - if self.use_sigmoid: - cls_cost = self._binary_cross_entropy(cls_pred, gt_labels) - else: - raise NotImplementedError - - return cls_cost * self.weight diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/__init__.py deleted file mode 100644 index f58505b59..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_sampler import BaseSampler -from .combined_sampler import CombinedSampler -from .instance_balanced_pos_sampler import InstanceBalancedPosSampler -from .iou_balanced_neg_sampler import IoUBalancedNegSampler -from .mask_pseudo_sampler import MaskPseudoSampler -from .mask_sampling_result import MaskSamplingResult -from .ohem_sampler import OHEMSampler -from .pseudo_sampler import PseudoSampler -from .random_sampler import RandomSampler -from .sampling_result import SamplingResult -from .score_hlr_sampler import ScoreHLRSampler - -__all__ = [ - 'BaseSampler', 'PseudoSampler', 'RandomSampler', - 'InstanceBalancedPosSampler', 'IoUBalancedNegSampler', 'CombinedSampler', - 'OHEMSampler', 'SamplingResult', 'ScoreHLRSampler', 'MaskPseudoSampler', - 'MaskSamplingResult' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/base_sampler.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/base_sampler.py deleted file mode 100644 index bd15c7c64..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/base_sampler.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -import torch - -from .sampling_result import SamplingResult - - -class BaseSampler(metaclass=ABCMeta): - """Base class of samplers.""" - - def __init__(self, - num, - pos_fraction, - neg_pos_ub=-1, - add_gt_as_proposals=True, - **kwargs): - self.num = num - self.pos_fraction = pos_fraction - self.neg_pos_ub = neg_pos_ub - self.add_gt_as_proposals = add_gt_as_proposals - self.pos_sampler = self - self.neg_sampler = self - - @abstractmethod - def _sample_pos(self, assign_result, num_expected, **kwargs): - """Sample positive samples.""" - pass - - @abstractmethod - def _sample_neg(self, assign_result, num_expected, **kwargs): - """Sample negative samples.""" - pass - - def sample(self, - assign_result, - bboxes, - gt_bboxes, - gt_labels=None, - **kwargs): - """Sample positive and negative bboxes. - - This is a simple implementation of bbox sampling given candidates, - assigning results and ground truth bboxes. - - Args: - assign_result (:obj:`AssignResult`): Bbox assigning results. - bboxes (Tensor): Boxes to be sampled from. - gt_bboxes (Tensor): Ground truth bboxes. - gt_labels (Tensor, optional): Class labels of ground truth bboxes. - - Returns: - :obj:`SamplingResult`: Sampling result. - - Example: - >>> from mmdet.core.bbox import RandomSampler - >>> from mmdet.core.bbox import AssignResult - >>> from mmdet.core.bbox.demodata import ensure_rng, random_boxes - >>> rng = ensure_rng(None) - >>> assign_result = AssignResult.random(rng=rng) - >>> bboxes = random_boxes(assign_result.num_preds, rng=rng) - >>> gt_bboxes = random_boxes(assign_result.num_gts, rng=rng) - >>> gt_labels = None - >>> self = RandomSampler(num=32, pos_fraction=0.5, neg_pos_ub=-1, - >>> add_gt_as_proposals=False) - >>> self = self.sample(assign_result, bboxes, gt_bboxes, gt_labels) - """ - if len(bboxes.shape) < 2: - bboxes = bboxes[None, :] - - bboxes = bboxes[:, :4] - - gt_flags = bboxes.new_zeros((bboxes.shape[0], ), dtype=torch.uint8) - if self.add_gt_as_proposals and len(gt_bboxes) > 0: - if gt_labels is None: - raise ValueError( - 'gt_labels must be given when add_gt_as_proposals is True') - bboxes = torch.cat([gt_bboxes, bboxes], dim=0) - assign_result.add_gt_(gt_labels) - gt_ones = bboxes.new_ones(gt_bboxes.shape[0], dtype=torch.uint8) - gt_flags = torch.cat([gt_ones, gt_flags]) - - num_expected_pos = int(self.num * self.pos_fraction) - pos_inds = self.pos_sampler._sample_pos( - assign_result, num_expected_pos, bboxes=bboxes, **kwargs) - # We found that sampled indices have duplicated items occasionally. - # (may be a bug of PyTorch) - pos_inds = pos_inds.unique() - num_sampled_pos = pos_inds.numel() - num_expected_neg = self.num - num_sampled_pos - if self.neg_pos_ub >= 0: - _pos = max(1, num_sampled_pos) - neg_upper_bound = int(self.neg_pos_ub * _pos) - if num_expected_neg > neg_upper_bound: - num_expected_neg = neg_upper_bound - neg_inds = self.neg_sampler._sample_neg( - assign_result, num_expected_neg, bboxes=bboxes, **kwargs) - neg_inds = neg_inds.unique() - - sampling_result = SamplingResult(pos_inds, neg_inds, bboxes, gt_bboxes, - assign_result, gt_flags) - return sampling_result diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/combined_sampler.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/combined_sampler.py deleted file mode 100644 index 4f6d86ff2..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/combined_sampler.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..builder import BBOX_SAMPLERS, build_sampler -from .base_sampler import BaseSampler - - -@BBOX_SAMPLERS.register_module() -class CombinedSampler(BaseSampler): - """A sampler that combines positive sampler and negative sampler.""" - - def __init__(self, pos_sampler, neg_sampler, **kwargs): - super(CombinedSampler, self).__init__(**kwargs) - self.pos_sampler = build_sampler(pos_sampler, **kwargs) - self.neg_sampler = build_sampler(neg_sampler, **kwargs) - - def _sample_pos(self, **kwargs): - """Sample positive samples.""" - raise NotImplementedError - - def _sample_neg(self, **kwargs): - """Sample negative samples.""" - raise NotImplementedError diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/instance_balanced_pos_sampler.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/instance_balanced_pos_sampler.py deleted file mode 100644 index 5e0d9cc0e..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/instance_balanced_pos_sampler.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch - -from ..builder import BBOX_SAMPLERS -from .random_sampler import RandomSampler - - -@BBOX_SAMPLERS.register_module() -class InstanceBalancedPosSampler(RandomSampler): - """Instance balanced sampler that samples equal number of positive samples - for each instance.""" - - def _sample_pos(self, assign_result, num_expected, **kwargs): - """Sample positive boxes. - - Args: - assign_result (:obj:`AssignResult`): The assigned results of boxes. - num_expected (int): The number of expected positive samples - - Returns: - Tensor or ndarray: sampled indices. - """ - pos_inds = torch.nonzero(assign_result.gt_inds > 0, as_tuple=False) - if pos_inds.numel() != 0: - pos_inds = pos_inds.squeeze(1) - if pos_inds.numel() <= num_expected: - return pos_inds - else: - unique_gt_inds = assign_result.gt_inds[pos_inds].unique() - num_gts = len(unique_gt_inds) - num_per_gt = int(round(num_expected / float(num_gts)) + 1) - sampled_inds = [] - for i in unique_gt_inds: - inds = torch.nonzero( - assign_result.gt_inds == i.item(), as_tuple=False) - if inds.numel() != 0: - inds = inds.squeeze(1) - else: - continue - if len(inds) > num_per_gt: - inds = self.random_choice(inds, num_per_gt) - sampled_inds.append(inds) - sampled_inds = torch.cat(sampled_inds) - if len(sampled_inds) < num_expected: - num_extra = num_expected - len(sampled_inds) - extra_inds = np.array( - list(set(pos_inds.cpu()) - set(sampled_inds.cpu()))) - if len(extra_inds) > num_extra: - extra_inds = self.random_choice(extra_inds, num_extra) - extra_inds = torch.from_numpy(extra_inds).to( - assign_result.gt_inds.device).long() - sampled_inds = torch.cat([sampled_inds, extra_inds]) - elif len(sampled_inds) > num_expected: - sampled_inds = self.random_choice(sampled_inds, num_expected) - return sampled_inds diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/iou_balanced_neg_sampler.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/iou_balanced_neg_sampler.py deleted file mode 100644 index 56e2874a4..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/iou_balanced_neg_sampler.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch - -from ..builder import BBOX_SAMPLERS -from .random_sampler import RandomSampler - - -@BBOX_SAMPLERS.register_module() -class IoUBalancedNegSampler(RandomSampler): - """IoU Balanced Sampling. - - arXiv: https://arxiv.org/pdf/1904.02701.pdf (CVPR 2019) - - Sampling proposals according to their IoU. `floor_fraction` of needed RoIs - are sampled from proposals whose IoU are lower than `floor_thr` randomly. - The others are sampled from proposals whose IoU are higher than - `floor_thr`. These proposals are sampled from some bins evenly, which are - split by `num_bins` via IoU evenly. - - Args: - num (int): number of proposals. - pos_fraction (float): fraction of positive proposals. - floor_thr (float): threshold (minimum) IoU for IoU balanced sampling, - set to -1 if all using IoU balanced sampling. - floor_fraction (float): sampling fraction of proposals under floor_thr. - num_bins (int): number of bins in IoU balanced sampling. - """ - - def __init__(self, - num, - pos_fraction, - floor_thr=-1, - floor_fraction=0, - num_bins=3, - **kwargs): - super(IoUBalancedNegSampler, self).__init__(num, pos_fraction, - **kwargs) - assert floor_thr >= 0 or floor_thr == -1 - assert 0 <= floor_fraction <= 1 - assert num_bins >= 1 - - self.floor_thr = floor_thr - self.floor_fraction = floor_fraction - self.num_bins = num_bins - - def sample_via_interval(self, max_overlaps, full_set, num_expected): - """Sample according to the iou interval. - - Args: - max_overlaps (torch.Tensor): IoU between bounding boxes and ground - truth boxes. - full_set (set(int)): A full set of indices of boxes。 - num_expected (int): Number of expected samples。 - - Returns: - np.ndarray: Indices of samples - """ - max_iou = max_overlaps.max() - iou_interval = (max_iou - self.floor_thr) / self.num_bins - per_num_expected = int(num_expected / self.num_bins) - - sampled_inds = [] - for i in range(self.num_bins): - start_iou = self.floor_thr + i * iou_interval - end_iou = self.floor_thr + (i + 1) * iou_interval - tmp_set = set( - np.where( - np.logical_and(max_overlaps >= start_iou, - max_overlaps < end_iou))[0]) - tmp_inds = list(tmp_set & full_set) - if len(tmp_inds) > per_num_expected: - tmp_sampled_set = self.random_choice(tmp_inds, - per_num_expected) - else: - tmp_sampled_set = np.array(tmp_inds, dtype=np.int) - sampled_inds.append(tmp_sampled_set) - - sampled_inds = np.concatenate(sampled_inds) - if len(sampled_inds) < num_expected: - num_extra = num_expected - len(sampled_inds) - extra_inds = np.array(list(full_set - set(sampled_inds))) - if len(extra_inds) > num_extra: - extra_inds = self.random_choice(extra_inds, num_extra) - sampled_inds = np.concatenate([sampled_inds, extra_inds]) - - return sampled_inds - - def _sample_neg(self, assign_result, num_expected, **kwargs): - """Sample negative boxes. - - Args: - assign_result (:obj:`AssignResult`): The assigned results of boxes. - num_expected (int): The number of expected negative samples - - Returns: - Tensor or ndarray: sampled indices. - """ - neg_inds = torch.nonzero(assign_result.gt_inds == 0, as_tuple=False) - if neg_inds.numel() != 0: - neg_inds = neg_inds.squeeze(1) - if len(neg_inds) <= num_expected: - return neg_inds - else: - max_overlaps = assign_result.max_overlaps.cpu().numpy() - # balance sampling for negative samples - neg_set = set(neg_inds.cpu().numpy()) - - if self.floor_thr > 0: - floor_set = set( - np.where( - np.logical_and(max_overlaps >= 0, - max_overlaps < self.floor_thr))[0]) - iou_sampling_set = set( - np.where(max_overlaps >= self.floor_thr)[0]) - elif self.floor_thr == 0: - floor_set = set(np.where(max_overlaps == 0)[0]) - iou_sampling_set = set( - np.where(max_overlaps > self.floor_thr)[0]) - else: - floor_set = set() - iou_sampling_set = set( - np.where(max_overlaps > self.floor_thr)[0]) - # for sampling interval calculation - self.floor_thr = 0 - - floor_neg_inds = list(floor_set & neg_set) - iou_sampling_neg_inds = list(iou_sampling_set & neg_set) - num_expected_iou_sampling = int(num_expected * - (1 - self.floor_fraction)) - if len(iou_sampling_neg_inds) > num_expected_iou_sampling: - if self.num_bins >= 2: - iou_sampled_inds = self.sample_via_interval( - max_overlaps, set(iou_sampling_neg_inds), - num_expected_iou_sampling) - else: - iou_sampled_inds = self.random_choice( - iou_sampling_neg_inds, num_expected_iou_sampling) - else: - iou_sampled_inds = np.array( - iou_sampling_neg_inds, dtype=np.int) - num_expected_floor = num_expected - len(iou_sampled_inds) - if len(floor_neg_inds) > num_expected_floor: - sampled_floor_inds = self.random_choice( - floor_neg_inds, num_expected_floor) - else: - sampled_floor_inds = np.array(floor_neg_inds, dtype=np.int) - sampled_inds = np.concatenate( - (sampled_floor_inds, iou_sampled_inds)) - if len(sampled_inds) < num_expected: - num_extra = num_expected - len(sampled_inds) - extra_inds = np.array(list(neg_set - set(sampled_inds))) - if len(extra_inds) > num_extra: - extra_inds = self.random_choice(extra_inds, num_extra) - sampled_inds = np.concatenate((sampled_inds, extra_inds)) - sampled_inds = torch.from_numpy(sampled_inds).long().to( - assign_result.gt_inds.device) - return sampled_inds diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/mask_pseudo_sampler.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/mask_pseudo_sampler.py deleted file mode 100644 index b5f69658d..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/mask_pseudo_sampler.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""copy from -https://github.com/ZwwWayne/K-Net/blob/main/knet/det/mask_pseudo_sampler.py.""" - -import torch - -from mmdet.core.bbox.builder import BBOX_SAMPLERS -from .base_sampler import BaseSampler -from .mask_sampling_result import MaskSamplingResult - - -@BBOX_SAMPLERS.register_module() -class MaskPseudoSampler(BaseSampler): - """A pseudo sampler that does not do sampling actually.""" - - def __init__(self, **kwargs): - pass - - def _sample_pos(self, **kwargs): - """Sample positive samples.""" - raise NotImplementedError - - def _sample_neg(self, **kwargs): - """Sample negative samples.""" - raise NotImplementedError - - def sample(self, assign_result, masks, gt_masks, **kwargs): - """Directly returns the positive and negative indices of samples. - - Args: - assign_result (:obj:`AssignResult`): Assigned results - masks (torch.Tensor): Bounding boxes - gt_masks (torch.Tensor): Ground truth boxes - Returns: - :obj:`SamplingResult`: sampler results - """ - pos_inds = torch.nonzero( - assign_result.gt_inds > 0, as_tuple=False).squeeze(-1).unique() - neg_inds = torch.nonzero( - assign_result.gt_inds == 0, as_tuple=False).squeeze(-1).unique() - gt_flags = masks.new_zeros(masks.shape[0], dtype=torch.uint8) - sampling_result = MaskSamplingResult(pos_inds, neg_inds, masks, - gt_masks, assign_result, gt_flags) - return sampling_result diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/mask_sampling_result.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/mask_sampling_result.py deleted file mode 100644 index 3d1094322..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/mask_sampling_result.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""copy from -https://github.com/ZwwWayne/K-Net/blob/main/knet/det/mask_pseudo_sampler.py.""" - -import torch - -from .sampling_result import SamplingResult - - -class MaskSamplingResult(SamplingResult): - """Mask sampling result.""" - - def __init__(self, pos_inds, neg_inds, masks, gt_masks, assign_result, - gt_flags): - self.pos_inds = pos_inds - self.neg_inds = neg_inds - self.pos_masks = masks[pos_inds] - self.neg_masks = masks[neg_inds] - self.pos_is_gt = gt_flags[pos_inds] - - self.num_gts = gt_masks.shape[0] - self.pos_assigned_gt_inds = assign_result.gt_inds[pos_inds] - 1 - - if gt_masks.numel() == 0: - # hack for index error case - assert self.pos_assigned_gt_inds.numel() == 0 - self.pos_gt_masks = torch.empty_like(gt_masks) - else: - self.pos_gt_masks = gt_masks[self.pos_assigned_gt_inds, :] - - if assign_result.labels is not None: - self.pos_gt_labels = assign_result.labels[pos_inds] - else: - self.pos_gt_labels = None - - @property - def masks(self): - """torch.Tensor: concatenated positive and negative boxes""" - return torch.cat([self.pos_masks, self.neg_masks]) - - def __nice__(self): - data = self.info.copy() - data['pos_masks'] = data.pop('pos_masks').shape - data['neg_masks'] = data.pop('neg_masks').shape - parts = [f"'{k}': {v!r}" for k, v in sorted(data.items())] - body = ' ' + ',\n '.join(parts) - return '{\n' + body + '\n}' - - @property - def info(self): - """Returns a dictionary of info about the object.""" - return { - 'pos_inds': self.pos_inds, - 'neg_inds': self.neg_inds, - 'pos_masks': self.pos_masks, - 'neg_masks': self.neg_masks, - 'pos_is_gt': self.pos_is_gt, - 'num_gts': self.num_gts, - 'pos_assigned_gt_inds': self.pos_assigned_gt_inds, - } diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/ohem_sampler.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/ohem_sampler.py deleted file mode 100644 index 7eb066633..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/ohem_sampler.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_SAMPLERS -from ..transforms import bbox2roi -from .base_sampler import BaseSampler - - -@BBOX_SAMPLERS.register_module() -class OHEMSampler(BaseSampler): - r"""Online Hard Example Mining Sampler described in `Training Region-based - Object Detectors with Online Hard Example Mining - `_. - """ - - def __init__(self, - num, - pos_fraction, - context, - neg_pos_ub=-1, - add_gt_as_proposals=True, - loss_key='loss_cls', - **kwargs): - super(OHEMSampler, self).__init__(num, pos_fraction, neg_pos_ub, - add_gt_as_proposals) - self.context = context - if not hasattr(self.context, 'num_stages'): - self.bbox_head = self.context.bbox_head - else: - self.bbox_head = self.context.bbox_head[self.context.current_stage] - - self.loss_key = loss_key - - def hard_mining(self, inds, num_expected, bboxes, labels, feats): - with torch.no_grad(): - rois = bbox2roi([bboxes]) - if not hasattr(self.context, 'num_stages'): - bbox_results = self.context._bbox_forward(feats, rois) - else: - bbox_results = self.context._bbox_forward( - self.context.current_stage, feats, rois) - cls_score = bbox_results['cls_score'] - loss = self.bbox_head.loss( - cls_score=cls_score, - bbox_pred=None, - rois=rois, - labels=labels, - label_weights=cls_score.new_ones(cls_score.size(0)), - bbox_targets=None, - bbox_weights=None, - reduction_override='none')[self.loss_key] - _, topk_loss_inds = loss.topk(num_expected) - return inds[topk_loss_inds] - - def _sample_pos(self, - assign_result, - num_expected, - bboxes=None, - feats=None, - **kwargs): - """Sample positive boxes. - - Args: - assign_result (:obj:`AssignResult`): Assigned results - num_expected (int): Number of expected positive samples - bboxes (torch.Tensor, optional): Boxes. Defaults to None. - feats (list[torch.Tensor], optional): Multi-level features. - Defaults to None. - - Returns: - torch.Tensor: Indices of positive samples - """ - # Sample some hard positive samples - pos_inds = torch.nonzero(assign_result.gt_inds > 0, as_tuple=False) - if pos_inds.numel() != 0: - pos_inds = pos_inds.squeeze(1) - if pos_inds.numel() <= num_expected: - return pos_inds - else: - return self.hard_mining(pos_inds, num_expected, bboxes[pos_inds], - assign_result.labels[pos_inds], feats) - - def _sample_neg(self, - assign_result, - num_expected, - bboxes=None, - feats=None, - **kwargs): - """Sample negative boxes. - - Args: - assign_result (:obj:`AssignResult`): Assigned results - num_expected (int): Number of expected negative samples - bboxes (torch.Tensor, optional): Boxes. Defaults to None. - feats (list[torch.Tensor], optional): Multi-level features. - Defaults to None. - - Returns: - torch.Tensor: Indices of negative samples - """ - # Sample some hard negative samples - neg_inds = torch.nonzero(assign_result.gt_inds == 0, as_tuple=False) - if neg_inds.numel() != 0: - neg_inds = neg_inds.squeeze(1) - if len(neg_inds) <= num_expected: - return neg_inds - else: - neg_labels = assign_result.labels.new_empty( - neg_inds.size(0)).fill_(self.bbox_head.num_classes) - return self.hard_mining(neg_inds, num_expected, bboxes[neg_inds], - neg_labels, feats) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/pseudo_sampler.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/pseudo_sampler.py deleted file mode 100644 index b5ce298ed..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/pseudo_sampler.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_SAMPLERS -from .base_sampler import BaseSampler -from .sampling_result import SamplingResult - - -@BBOX_SAMPLERS.register_module() -class PseudoSampler(BaseSampler): - """A pseudo sampler that does not do sampling actually.""" - - def __init__(self, **kwargs): - pass - - def _sample_pos(self, **kwargs): - """Sample positive samples.""" - raise NotImplementedError - - def _sample_neg(self, **kwargs): - """Sample negative samples.""" - raise NotImplementedError - - def sample(self, assign_result, bboxes, gt_bboxes, *args, **kwargs): - """Directly returns the positive and negative indices of samples. - - Args: - assign_result (:obj:`AssignResult`): Assigned results - bboxes (torch.Tensor): Bounding boxes - gt_bboxes (torch.Tensor): Ground truth boxes - - Returns: - :obj:`SamplingResult`: sampler results - """ - pos_inds = torch.nonzero( - assign_result.gt_inds > 0, as_tuple=False).squeeze(-1).unique() - neg_inds = torch.nonzero( - assign_result.gt_inds == 0, as_tuple=False).squeeze(-1).unique() - gt_flags = bboxes.new_zeros(bboxes.shape[0], dtype=torch.uint8) - sampling_result = SamplingResult(pos_inds, neg_inds, bboxes, gt_bboxes, - assign_result, gt_flags) - return sampling_result diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/random_sampler.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/random_sampler.py deleted file mode 100644 index d09207e7f..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/random_sampler.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_SAMPLERS -from .base_sampler import BaseSampler - - -@BBOX_SAMPLERS.register_module() -class RandomSampler(BaseSampler): - """Random sampler. - - Args: - num (int): Number of samples - pos_fraction (float): Fraction of positive samples - neg_pos_up (int, optional): Upper bound number of negative and - positive samples. Defaults to -1. - add_gt_as_proposals (bool, optional): Whether to add ground truth - boxes as proposals. Defaults to True. - """ - - def __init__(self, - num, - pos_fraction, - neg_pos_ub=-1, - add_gt_as_proposals=True, - **kwargs): - from mmdet.core.bbox import demodata - super(RandomSampler, self).__init__(num, pos_fraction, neg_pos_ub, - add_gt_as_proposals) - self.rng = demodata.ensure_rng(kwargs.get('rng', None)) - - def random_choice(self, gallery, num): - """Random select some elements from the gallery. - - If `gallery` is a Tensor, the returned indices will be a Tensor; - If `gallery` is a ndarray or list, the returned indices will be a - ndarray. - - Args: - gallery (Tensor | ndarray | list): indices pool. - num (int): expected sample num. - - Returns: - Tensor or ndarray: sampled indices. - """ - assert len(gallery) >= num - - is_tensor = isinstance(gallery, torch.Tensor) - if not is_tensor: - if torch.cuda.is_available(): - device = torch.cuda.current_device() - else: - device = 'cpu' - gallery = torch.tensor(gallery, dtype=torch.long, device=device) - # This is a temporary fix. We can revert the following code - # when PyTorch fixes the abnormal return of torch.randperm. - # See: https://github.com/open-mmlab/mmdetection/pull/5014 - perm = torch.randperm(gallery.numel())[:num].to(device=gallery.device) - rand_inds = gallery[perm] - if not is_tensor: - rand_inds = rand_inds.cpu().numpy() - return rand_inds - - def _sample_pos(self, assign_result, num_expected, **kwargs): - """Randomly sample some positive samples.""" - pos_inds = torch.nonzero(assign_result.gt_inds > 0, as_tuple=False) - if pos_inds.numel() != 0: - pos_inds = pos_inds.squeeze(1) - if pos_inds.numel() <= num_expected: - return pos_inds - else: - return self.random_choice(pos_inds, num_expected) - - def _sample_neg(self, assign_result, num_expected, **kwargs): - """Randomly sample some negative samples.""" - neg_inds = torch.nonzero(assign_result.gt_inds == 0, as_tuple=False) - if neg_inds.numel() != 0: - neg_inds = neg_inds.squeeze(1) - if len(neg_inds) <= num_expected: - return neg_inds - else: - return self.random_choice(neg_inds, num_expected) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/sampling_result.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/sampling_result.py deleted file mode 100644 index 50676d041..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/sampling_result.py +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmdet.utils import util_mixins - - -class SamplingResult(util_mixins.NiceRepr): - """Bbox sampling result. - - Example: - >>> # xdoctest: +IGNORE_WANT - >>> from mmdet.core.bbox.samplers.sampling_result import * # NOQA - >>> self = SamplingResult.random(rng=10) - >>> print(f'self = {self}') - self = - """ - - def __init__(self, pos_inds, neg_inds, bboxes, gt_bboxes, assign_result, - gt_flags): - self.pos_inds = pos_inds - self.neg_inds = neg_inds - self.pos_bboxes = bboxes[pos_inds] - self.neg_bboxes = bboxes[neg_inds] - self.pos_is_gt = gt_flags[pos_inds] - - self.num_gts = gt_bboxes.shape[0] - self.pos_assigned_gt_inds = assign_result.gt_inds[pos_inds] - 1 - - if gt_bboxes.numel() == 0: - # hack for index error case - assert self.pos_assigned_gt_inds.numel() == 0 - self.pos_gt_bboxes = torch.empty_like(gt_bboxes).view(-1, 4) - else: - if len(gt_bboxes.shape) < 2: - gt_bboxes = gt_bboxes.view(-1, 4) - - self.pos_gt_bboxes = gt_bboxes[self.pos_assigned_gt_inds.long(), :] - - if assign_result.labels is not None: - self.pos_gt_labels = assign_result.labels[pos_inds] - else: - self.pos_gt_labels = None - - @property - def bboxes(self): - """torch.Tensor: concatenated positive and negative boxes""" - return torch.cat([self.pos_bboxes, self.neg_bboxes]) - - def to(self, device): - """Change the device of the data inplace. - - Example: - >>> self = SamplingResult.random() - >>> print(f'self = {self.to(None)}') - >>> # xdoctest: +REQUIRES(--gpu) - >>> print(f'self = {self.to(0)}') - """ - _dict = self.__dict__ - for key, value in _dict.items(): - if isinstance(value, torch.Tensor): - _dict[key] = value.to(device) - return self - - def __nice__(self): - data = self.info.copy() - data['pos_bboxes'] = data.pop('pos_bboxes').shape - data['neg_bboxes'] = data.pop('neg_bboxes').shape - parts = [f"'{k}': {v!r}" for k, v in sorted(data.items())] - body = ' ' + ',\n '.join(parts) - return '{\n' + body + '\n}' - - @property - def info(self): - """Returns a dictionary of info about the object.""" - return { - 'pos_inds': self.pos_inds, - 'neg_inds': self.neg_inds, - 'pos_bboxes': self.pos_bboxes, - 'neg_bboxes': self.neg_bboxes, - 'pos_is_gt': self.pos_is_gt, - 'num_gts': self.num_gts, - 'pos_assigned_gt_inds': self.pos_assigned_gt_inds, - } - - @classmethod - def random(cls, rng=None, **kwargs): - """ - Args: - rng (None | int | numpy.random.RandomState): seed or state. - kwargs (keyword arguments): - - num_preds: number of predicted boxes - - num_gts: number of true boxes - - p_ignore (float): probability of a predicted box assigned to \ - an ignored truth. - - p_assigned (float): probability of a predicted box not being \ - assigned. - - p_use_label (float | bool): with labels or not. - - Returns: - :obj:`SamplingResult`: Randomly generated sampling result. - - Example: - >>> from mmdet.core.bbox.samplers.sampling_result import * # NOQA - >>> self = SamplingResult.random() - >>> print(self.__dict__) - """ - from mmdet.core.bbox import demodata - from mmdet.core.bbox.assigners.assign_result import AssignResult - from mmdet.core.bbox.samplers.random_sampler import RandomSampler - rng = demodata.ensure_rng(rng) - - # make probabalistic? - num = 32 - pos_fraction = 0.5 - neg_pos_ub = -1 - - assign_result = AssignResult.random(rng=rng, **kwargs) - - # Note we could just compute an assignment - bboxes = demodata.random_boxes(assign_result.num_preds, rng=rng) - gt_bboxes = demodata.random_boxes(assign_result.num_gts, rng=rng) - - if rng.rand() > 0.2: - # sometimes algorithms squeeze their data, be robust to that - gt_bboxes = gt_bboxes.squeeze() - bboxes = bboxes.squeeze() - - if assign_result.labels is None: - gt_labels = None - else: - gt_labels = None # todo - - if gt_labels is None: - add_gt_as_proposals = False - else: - add_gt_as_proposals = True # make probabalistic? - - sampler = RandomSampler( - num, - pos_fraction, - neg_pos_ub=neg_pos_ub, - add_gt_as_proposals=add_gt_as_proposals, - rng=rng) - self = sampler.sample(assign_result, bboxes, gt_bboxes, gt_labels) - return self diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/score_hlr_sampler.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/score_hlr_sampler.py deleted file mode 100644 index f4be9b8cf..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/samplers/score_hlr_sampler.py +++ /dev/null @@ -1,265 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.ops import nms_match - -from ..builder import BBOX_SAMPLERS -from ..transforms import bbox2roi -from .base_sampler import BaseSampler -from .sampling_result import SamplingResult - - -@BBOX_SAMPLERS.register_module() -class ScoreHLRSampler(BaseSampler): - r"""Importance-based Sample Reweighting (ISR_N), described in `Prime Sample - Attention in Object Detection `_. - - Score hierarchical local rank (HLR) differentiates with RandomSampler in - negative part. It firstly computes Score-HLR in a two-step way, - then linearly maps score hlr to the loss weights. - - Args: - num (int): Total number of sampled RoIs. - pos_fraction (float): Fraction of positive samples. - context (:class:`BaseRoIHead`): RoI head that the sampler belongs to. - neg_pos_ub (int): Upper bound of the ratio of num negative to num - positive, -1 means no upper bound. - add_gt_as_proposals (bool): Whether to add ground truth as proposals. - k (float): Power of the non-linear mapping. - bias (float): Shift of the non-linear mapping. - score_thr (float): Minimum score that a negative sample is to be - considered as valid bbox. - """ - - def __init__(self, - num, - pos_fraction, - context, - neg_pos_ub=-1, - add_gt_as_proposals=True, - k=0.5, - bias=0, - score_thr=0.05, - iou_thr=0.5, - **kwargs): - super().__init__(num, pos_fraction, neg_pos_ub, add_gt_as_proposals) - self.k = k - self.bias = bias - self.score_thr = score_thr - self.iou_thr = iou_thr - self.context = context - # context of cascade detectors is a list, so distinguish them here. - if not hasattr(context, 'num_stages'): - self.bbox_roi_extractor = context.bbox_roi_extractor - self.bbox_head = context.bbox_head - self.with_shared_head = context.with_shared_head - if self.with_shared_head: - self.shared_head = context.shared_head - else: - self.bbox_roi_extractor = context.bbox_roi_extractor[ - context.current_stage] - self.bbox_head = context.bbox_head[context.current_stage] - - @staticmethod - def random_choice(gallery, num): - """Randomly select some elements from the gallery. - - If `gallery` is a Tensor, the returned indices will be a Tensor; - If `gallery` is a ndarray or list, the returned indices will be a - ndarray. - - Args: - gallery (Tensor | ndarray | list): indices pool. - num (int): expected sample num. - - Returns: - Tensor or ndarray: sampled indices. - """ - assert len(gallery) >= num - - is_tensor = isinstance(gallery, torch.Tensor) - if not is_tensor: - if torch.cuda.is_available(): - device = torch.cuda.current_device() - else: - device = 'cpu' - gallery = torch.tensor(gallery, dtype=torch.long, device=device) - perm = torch.randperm(gallery.numel(), device=gallery.device)[:num] - rand_inds = gallery[perm] - if not is_tensor: - rand_inds = rand_inds.cpu().numpy() - return rand_inds - - def _sample_pos(self, assign_result, num_expected, **kwargs): - """Randomly sample some positive samples.""" - pos_inds = torch.nonzero(assign_result.gt_inds > 0).flatten() - if pos_inds.numel() <= num_expected: - return pos_inds - else: - return self.random_choice(pos_inds, num_expected) - - def _sample_neg(self, - assign_result, - num_expected, - bboxes, - feats=None, - img_meta=None, - **kwargs): - """Sample negative samples. - - Score-HLR sampler is done in the following steps: - 1. Take the maximum positive score prediction of each negative samples - as s_i. - 2. Filter out negative samples whose s_i <= score_thr, the left samples - are called valid samples. - 3. Use NMS-Match to divide valid samples into different groups, - samples in the same group will greatly overlap with each other - 4. Rank the matched samples in two-steps to get Score-HLR. - (1) In the same group, rank samples with their scores. - (2) In the same score rank across different groups, - rank samples with their scores again. - 5. Linearly map Score-HLR to the final label weights. - - Args: - assign_result (:obj:`AssignResult`): result of assigner. - num_expected (int): Expected number of samples. - bboxes (Tensor): bbox to be sampled. - feats (Tensor): Features come from FPN. - img_meta (dict): Meta information dictionary. - """ - neg_inds = torch.nonzero(assign_result.gt_inds == 0).flatten() - num_neg = neg_inds.size(0) - if num_neg == 0: - return neg_inds, None - with torch.no_grad(): - neg_bboxes = bboxes[neg_inds] - neg_rois = bbox2roi([neg_bboxes]) - bbox_result = self.context._bbox_forward(feats, neg_rois) - cls_score, bbox_pred = bbox_result['cls_score'], bbox_result[ - 'bbox_pred'] - - ori_loss = self.bbox_head.loss( - cls_score=cls_score, - bbox_pred=None, - rois=None, - labels=neg_inds.new_full((num_neg, ), - self.bbox_head.num_classes), - label_weights=cls_score.new_ones(num_neg), - bbox_targets=None, - bbox_weights=None, - reduction_override='none')['loss_cls'] - - # filter out samples with the max score lower than score_thr - max_score, argmax_score = cls_score.softmax(-1)[:, :-1].max(-1) - valid_inds = (max_score > self.score_thr).nonzero().view(-1) - invalid_inds = (max_score <= self.score_thr).nonzero().view(-1) - num_valid = valid_inds.size(0) - num_invalid = invalid_inds.size(0) - - num_expected = min(num_neg, num_expected) - num_hlr = min(num_valid, num_expected) - num_rand = num_expected - num_hlr - if num_valid > 0: - valid_rois = neg_rois[valid_inds] - valid_max_score = max_score[valid_inds] - valid_argmax_score = argmax_score[valid_inds] - valid_bbox_pred = bbox_pred[valid_inds] - - # valid_bbox_pred shape: [num_valid, #num_classes, 4] - valid_bbox_pred = valid_bbox_pred.view( - valid_bbox_pred.size(0), -1, 4) - selected_bbox_pred = valid_bbox_pred[range(num_valid), - valid_argmax_score] - pred_bboxes = self.bbox_head.bbox_coder.decode( - valid_rois[:, 1:], selected_bbox_pred) - pred_bboxes_with_score = torch.cat( - [pred_bboxes, valid_max_score[:, None]], -1) - group = nms_match(pred_bboxes_with_score, self.iou_thr) - - # imp: importance - imp = cls_score.new_zeros(num_valid) - for g in group: - g_score = valid_max_score[g] - # g_score has already sorted - rank = g_score.new_tensor(range(g_score.size(0))) - imp[g] = num_valid - rank + g_score - _, imp_rank_inds = imp.sort(descending=True) - _, imp_rank = imp_rank_inds.sort() - hlr_inds = imp_rank_inds[:num_expected] - - if num_rand > 0: - rand_inds = torch.randperm(num_invalid)[:num_rand] - select_inds = torch.cat( - [valid_inds[hlr_inds], invalid_inds[rand_inds]]) - else: - select_inds = valid_inds[hlr_inds] - - neg_label_weights = cls_score.new_ones(num_expected) - - up_bound = max(num_expected, num_valid) - imp_weights = (up_bound - - imp_rank[hlr_inds].float()) / up_bound - neg_label_weights[:num_hlr] = imp_weights - neg_label_weights[num_hlr:] = imp_weights.min() - neg_label_weights = (self.bias + - (1 - self.bias) * neg_label_weights).pow( - self.k) - ori_selected_loss = ori_loss[select_inds] - new_loss = ori_selected_loss * neg_label_weights - norm_ratio = ori_selected_loss.sum() / new_loss.sum() - neg_label_weights *= norm_ratio - else: - neg_label_weights = cls_score.new_ones(num_expected) - select_inds = torch.randperm(num_neg)[:num_expected] - - return neg_inds[select_inds], neg_label_weights - - def sample(self, - assign_result, - bboxes, - gt_bboxes, - gt_labels=None, - img_meta=None, - **kwargs): - """Sample positive and negative bboxes. - - This is a simple implementation of bbox sampling given candidates, - assigning results and ground truth bboxes. - - Args: - assign_result (:obj:`AssignResult`): Bbox assigning results. - bboxes (Tensor): Boxes to be sampled from. - gt_bboxes (Tensor): Ground truth bboxes. - gt_labels (Tensor, optional): Class labels of ground truth bboxes. - - Returns: - tuple[:obj:`SamplingResult`, Tensor]: Sampling result and negative - label weights. - """ - bboxes = bboxes[:, :4] - - gt_flags = bboxes.new_zeros((bboxes.shape[0], ), dtype=torch.uint8) - if self.add_gt_as_proposals: - bboxes = torch.cat([gt_bboxes, bboxes], dim=0) - assign_result.add_gt_(gt_labels) - gt_ones = bboxes.new_ones(gt_bboxes.shape[0], dtype=torch.uint8) - gt_flags = torch.cat([gt_ones, gt_flags]) - - num_expected_pos = int(self.num * self.pos_fraction) - pos_inds = self.pos_sampler._sample_pos( - assign_result, num_expected_pos, bboxes=bboxes, **kwargs) - num_sampled_pos = pos_inds.numel() - num_expected_neg = self.num - num_sampled_pos - if self.neg_pos_ub >= 0: - _pos = max(1, num_sampled_pos) - neg_upper_bound = int(self.neg_pos_ub * _pos) - if num_expected_neg > neg_upper_bound: - num_expected_neg = neg_upper_bound - neg_inds, neg_label_weights = self.neg_sampler._sample_neg( - assign_result, - num_expected_neg, - bboxes, - img_meta=img_meta, - **kwargs) - - return SamplingResult(pos_inds, neg_inds, bboxes, gt_bboxes, - assign_result, gt_flags), neg_label_weights diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/transforms.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/transforms.py deleted file mode 100644 index 6d72076a5..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/bbox/transforms.py +++ /dev/null @@ -1,270 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch - - -def find_inside_bboxes(bboxes, img_h, img_w): - """Find bboxes as long as a part of bboxes is inside the image. - - Args: - bboxes (Tensor): Shape (N, 4). - img_h (int): Image height. - img_w (int): Image width. - - Returns: - Tensor: Index of the remaining bboxes. - """ - inside_inds = (bboxes[:, 0] < img_w) & (bboxes[:, 2] > 0) \ - & (bboxes[:, 1] < img_h) & (bboxes[:, 3] > 0) - return inside_inds - - -def bbox_flip(bboxes, img_shape, direction='horizontal'): - """Flip bboxes horizontally or vertically. - - Args: - bboxes (Tensor): Shape (..., 4*k) - img_shape (tuple): Image shape. - direction (str): Flip direction, options are "horizontal", "vertical", - "diagonal". Default: "horizontal" - - Returns: - Tensor: Flipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - assert direction in ['horizontal', 'vertical', 'diagonal'] - flipped = bboxes.clone() - if direction == 'horizontal': - flipped[..., 0::4] = img_shape[1] - bboxes[..., 2::4] - flipped[..., 2::4] = img_shape[1] - bboxes[..., 0::4] - elif direction == 'vertical': - flipped[..., 1::4] = img_shape[0] - bboxes[..., 3::4] - flipped[..., 3::4] = img_shape[0] - bboxes[..., 1::4] - else: - flipped[..., 0::4] = img_shape[1] - bboxes[..., 2::4] - flipped[..., 1::4] = img_shape[0] - bboxes[..., 3::4] - flipped[..., 2::4] = img_shape[1] - bboxes[..., 0::4] - flipped[..., 3::4] = img_shape[0] - bboxes[..., 1::4] - return flipped - - -def bbox_mapping(bboxes, - img_shape, - scale_factor, - flip, - flip_direction='horizontal'): - """Map bboxes from the original image scale to testing scale.""" - new_bboxes = bboxes * bboxes.new_tensor(scale_factor) - if flip: - new_bboxes = bbox_flip(new_bboxes, img_shape, flip_direction) - return new_bboxes - - -def bbox_mapping_back(bboxes, - img_shape, - scale_factor, - flip, - flip_direction='horizontal'): - """Map bboxes from testing scale to original image scale.""" - new_bboxes = bbox_flip(bboxes, img_shape, - flip_direction) if flip else bboxes - new_bboxes = new_bboxes.view(-1, 4) / new_bboxes.new_tensor(scale_factor) - return new_bboxes.view(bboxes.shape) - - -def bbox2roi(bbox_list): - """Convert a list of bboxes to roi format. - - Args: - bbox_list (list[Tensor]): a list of bboxes corresponding to a batch - of images. - - Returns: - Tensor: shape (n, 5), [batch_ind, x1, y1, x2, y2] - """ - rois_list = [] - for img_id, bboxes in enumerate(bbox_list): - if bboxes.size(0) > 0: - img_inds = bboxes.new_full((bboxes.size(0), 1), img_id) - rois = torch.cat([img_inds, bboxes[:, :4]], dim=-1) - else: - rois = bboxes.new_zeros((0, 5)) - rois_list.append(rois) - rois = torch.cat(rois_list, 0) - return rois - - -def roi2bbox(rois): - """Convert rois to bounding box format. - - Args: - rois (torch.Tensor): RoIs with the shape (n, 5) where the first - column indicates batch id of each RoI. - - Returns: - list[torch.Tensor]: Converted boxes of corresponding rois. - """ - bbox_list = [] - img_ids = torch.unique(rois[:, 0].cpu(), sorted=True) - for img_id in img_ids: - inds = (rois[:, 0] == img_id.item()) - bbox = rois[inds, 1:] - bbox_list.append(bbox) - return bbox_list - - -def bbox2result(bboxes, labels, num_classes): - """Convert detection results to a list of numpy arrays. - - Args: - bboxes (torch.Tensor | np.ndarray): shape (n, 5) - labels (torch.Tensor | np.ndarray): shape (n, ) - num_classes (int): class number, including background class - - Returns: - list(ndarray): bbox results of each class - """ - if bboxes.shape[0] == 0: - return [np.zeros((0, 5), dtype=np.float32) for i in range(num_classes)] - else: - if isinstance(bboxes, torch.Tensor): - bboxes = bboxes.detach().cpu().numpy() - labels = labels.detach().cpu().numpy() - return [bboxes[labels == i, :] for i in range(num_classes)] - - -def distance2bbox(points, distance, max_shape=None): - """Decode distance prediction to bounding box. - - Args: - points (Tensor): Shape (B, N, 2) or (N, 2). - distance (Tensor): Distance from the given point to 4 - boundaries (left, top, right, bottom). Shape (B, N, 4) or (N, 4) - max_shape (Sequence[int] or torch.Tensor or Sequence[ - Sequence[int]],optional): Maximum bounds for boxes, specifies - (H, W, C) or (H, W). If priors shape is (B, N, 4), then - the max_shape should be a Sequence[Sequence[int]] - and the length of max_shape should also be B. - - Returns: - Tensor: Boxes with shape (N, 4) or (B, N, 4) - """ - - x1 = points[..., 0] - distance[..., 0] - y1 = points[..., 1] - distance[..., 1] - x2 = points[..., 0] + distance[..., 2] - y2 = points[..., 1] + distance[..., 3] - - bboxes = torch.stack([x1, y1, x2, y2], -1) - - if max_shape is not None: - if bboxes.dim() == 2 and not torch.onnx.is_in_onnx_export(): - # speed up - bboxes[:, 0::2].clamp_(min=0, max=max_shape[1]) - bboxes[:, 1::2].clamp_(min=0, max=max_shape[0]) - return bboxes - - # clip bboxes with dynamic `min` and `max` for onnx - if torch.onnx.is_in_onnx_export(): - from mmdet.core.export import dynamic_clip_for_onnx - x1, y1, x2, y2 = dynamic_clip_for_onnx(x1, y1, x2, y2, max_shape) - bboxes = torch.stack([x1, y1, x2, y2], dim=-1) - return bboxes - if not isinstance(max_shape, torch.Tensor): - max_shape = x1.new_tensor(max_shape) - max_shape = max_shape[..., :2].type_as(x1) - if max_shape.ndim == 2: - assert bboxes.ndim == 3 - assert max_shape.size(0) == bboxes.size(0) - - min_xy = x1.new_tensor(0) - max_xy = torch.cat([max_shape, max_shape], - dim=-1).flip(-1).unsqueeze(-2) - bboxes = torch.where(bboxes < min_xy, min_xy, bboxes) - bboxes = torch.where(bboxes > max_xy, max_xy, bboxes) - - return bboxes - - -def bbox2distance(points, bbox, max_dis=None, eps=0.1): - """Decode bounding box based on distances. - - Args: - points (Tensor): Shape (n, 2), [x, y]. - bbox (Tensor): Shape (n, 4), "xyxy" format - max_dis (float): Upper bound of the distance. - eps (float): a small value to ensure target < max_dis, instead <= - - Returns: - Tensor: Decoded distances. - """ - left = points[:, 0] - bbox[:, 0] - top = points[:, 1] - bbox[:, 1] - right = bbox[:, 2] - points[:, 0] - bottom = bbox[:, 3] - points[:, 1] - if max_dis is not None: - left = left.clamp(min=0, max=max_dis - eps) - top = top.clamp(min=0, max=max_dis - eps) - right = right.clamp(min=0, max=max_dis - eps) - bottom = bottom.clamp(min=0, max=max_dis - eps) - return torch.stack([left, top, right, bottom], -1) - - -def bbox_rescale(bboxes, scale_factor=1.0): - """Rescale bounding box w.r.t. scale_factor. - - Args: - bboxes (Tensor): Shape (n, 4) for bboxes or (n, 5) for rois - scale_factor (float): rescale factor - - Returns: - Tensor: Rescaled bboxes. - """ - if bboxes.size(1) == 5: - bboxes_ = bboxes[:, 1:] - inds_ = bboxes[:, 0] - else: - bboxes_ = bboxes - cx = (bboxes_[:, 0] + bboxes_[:, 2]) * 0.5 - cy = (bboxes_[:, 1] + bboxes_[:, 3]) * 0.5 - w = bboxes_[:, 2] - bboxes_[:, 0] - h = bboxes_[:, 3] - bboxes_[:, 1] - w = w * scale_factor - h = h * scale_factor - x1 = cx - 0.5 * w - x2 = cx + 0.5 * w - y1 = cy - 0.5 * h - y2 = cy + 0.5 * h - if bboxes.size(1) == 5: - rescaled_bboxes = torch.stack([inds_, x1, y1, x2, y2], dim=-1) - else: - rescaled_bboxes = torch.stack([x1, y1, x2, y2], dim=-1) - return rescaled_bboxes - - -def bbox_cxcywh_to_xyxy(bbox): - """Convert bbox coordinates from (cx, cy, w, h) to (x1, y1, x2, y2). - - Args: - bbox (Tensor): Shape (n, 4) for bboxes. - - Returns: - Tensor: Converted bboxes. - """ - cx, cy, w, h = bbox.split((1, 1, 1, 1), dim=-1) - bbox_new = [(cx - 0.5 * w), (cy - 0.5 * h), (cx + 0.5 * w), (cy + 0.5 * h)] - return torch.cat(bbox_new, dim=-1) - - -def bbox_xyxy_to_cxcywh(bbox): - """Convert bbox coordinates from (x1, y1, x2, y2) to (cx, cy, w, h). - - Args: - bbox (Tensor): Shape (n, 4) for bboxes. - - Returns: - Tensor: Converted bboxes. - """ - x1, y1, x2, y2 = bbox.split((1, 1, 1, 1), dim=-1) - bbox_new = [(x1 + x2) / 2, (y1 + y2) / 2, (x2 - x1), (y2 - y1)] - return torch.cat(bbox_new, dim=-1) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/data_structures/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/data_structures/__init__.py deleted file mode 100644 index 11ab96c56..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/data_structures/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .general_data import GeneralData -from .instance_data import InstanceData - -__all__ = ['GeneralData', 'InstanceData'] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/data_structures/general_data.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/data_structures/general_data.py deleted file mode 100644 index 99316e41b..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/data_structures/general_data.py +++ /dev/null @@ -1,326 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -import numpy as np -import torch - -from mmdet.utils.util_mixins import NiceRepr - - -class GeneralData(NiceRepr): - """A general data structure of OpenMMlab. - - A data structure that stores the meta information, - the annotations of the images or the model predictions, - which can be used in communication between components. - - The attributes in `GeneralData` are divided into two parts, - the `meta_info_fields` and the `data_fields` respectively. - - - `meta_info_fields`: Usually contains the - information about the image such as filename, - image_shape, pad_shape, etc. All attributes in - it are immutable once set, - but the user can add new meta information with - `set_meta_info` function, all information can be accessed - with methods `meta_info_keys`, `meta_info_values`, - `meta_info_items`. - - - `data_fields`: Annotations or model predictions are - stored. The attributes can be accessed or modified by - dict-like or object-like operations, such as - `.` , `[]`, `in`, `del`, `pop(str)` `get(str)`, `keys()`, - `values()`, `items()`. Users can also apply tensor-like methods - to all obj:`torch.Tensor` in the `data_fileds`, - such as `.cuda()`, `.cpu()`, `.numpy()`, `device`, `.to()` - `.detach()`, `.numpy()` - - Args: - meta_info (dict, optional): A dict contains the meta information - of single image. such as `img_shape`, `scale_factor`, etc. - Default: None. - data (dict, optional): A dict contains annotations of single image or - model predictions. Default: None. - - Examples: - >>> from mmdet.core import GeneralData - >>> img_meta = dict(img_shape=(800, 1196, 3), pad_shape=(800, 1216, 3)) - >>> instance_data = GeneralData(meta_info=img_meta) - >>> img_shape in instance_data - True - >>> instance_data.det_labels = torch.LongTensor([0, 1, 2, 3]) - >>> instance_data["det_scores"] = torch.Tensor([0.01, 0.1, 0.2, 0.3]) - >>> print(results) - - >>> instance_data.det_scores - tensor([0.0100, 0.1000, 0.2000, 0.3000]) - >>> instance_data.det_labels - tensor([0, 1, 2, 3]) - >>> instance_data['det_labels'] - tensor([0, 1, 2, 3]) - >>> 'det_labels' in instance_data - True - >>> instance_data.img_shape - (800, 1196, 3) - >>> 'det_scores' in instance_data - True - >>> del instance_data.det_scores - >>> 'det_scores' in instance_data - False - >>> det_labels = instance_data.pop('det_labels', None) - >>> det_labels - tensor([0, 1, 2, 3]) - >>> 'det_labels' in instance_data - >>> False - """ - - def __init__(self, meta_info=None, data=None): - - self._meta_info_fields = set() - self._data_fields = set() - - if meta_info is not None: - self.set_meta_info(meta_info=meta_info) - if data is not None: - self.set_data(data) - - def set_meta_info(self, meta_info): - """Add meta information. - - Args: - meta_info (dict): A dict contains the meta information - of image. such as `img_shape`, `scale_factor`, etc. - Default: None. - """ - assert isinstance(meta_info, - dict), f'meta should be a `dict` but get {meta_info}' - meta = copy.deepcopy(meta_info) - for k, v in meta.items(): - # should be consistent with original meta_info - if k in self._meta_info_fields: - ori_value = getattr(self, k) - if isinstance(ori_value, (torch.Tensor, np.ndarray)): - if (ori_value == v).all(): - continue - else: - raise KeyError( - f'img_meta_info {k} has been set as ' - f'{getattr(self, k)} before, which is immutable ') - elif ori_value == v: - continue - else: - raise KeyError( - f'img_meta_info {k} has been set as ' - f'{getattr(self, k)} before, which is immutable ') - else: - self._meta_info_fields.add(k) - self.__dict__[k] = v - - def set_data(self, data): - """Update a dict to `data_fields`. - - Args: - data (dict): A dict contains annotations of image or - model predictions. Default: None. - """ - assert isinstance(data, - dict), f'meta should be a `dict` but get {data}' - for k, v in data.items(): - self.__setattr__(k, v) - - def new(self, meta_info=None, data=None): - """Return a new results with same image meta information. - - Args: - meta_info (dict, optional): A dict contains the meta information - of image. such as `img_shape`, `scale_factor`, etc. - Default: None. - data (dict, optional): A dict contains annotations of image or - model predictions. Default: None. - """ - new_data = self.__class__() - new_data.set_meta_info(dict(self.meta_info_items())) - if meta_info is not None: - new_data.set_meta_info(meta_info) - if data is not None: - new_data.set_data(data) - return new_data - - def keys(self): - """ - Returns: - list: Contains all keys in data_fields. - """ - return [key for key in self._data_fields] - - def meta_info_keys(self): - """ - Returns: - list: Contains all keys in meta_info_fields. - """ - return [key for key in self._meta_info_fields] - - def values(self): - """ - Returns: - list: Contains all values in data_fields. - """ - return [getattr(self, k) for k in self.keys()] - - def meta_info_values(self): - """ - Returns: - list: Contains all values in meta_info_fields. - """ - return [getattr(self, k) for k in self.meta_info_keys()] - - def items(self): - for k in self.keys(): - yield (k, getattr(self, k)) - - def meta_info_items(self): - for k in self.meta_info_keys(): - yield (k, getattr(self, k)) - - def __setattr__(self, name, val): - if name in ('_meta_info_fields', '_data_fields'): - if not hasattr(self, name): - super().__setattr__(name, val) - else: - raise AttributeError( - f'{name} has been used as a ' - f'private attribute, which is immutable. ') - else: - if name in self._meta_info_fields: - raise AttributeError(f'`{name}` is used in meta information,' - f'which is immutable') - - self._data_fields.add(name) - super().__setattr__(name, val) - - def __delattr__(self, item): - - if item in ('_meta_info_fields', '_data_fields'): - raise AttributeError(f'{item} has been used as a ' - f'private attribute, which is immutable. ') - - if item in self._meta_info_fields: - raise KeyError(f'{item} is used in meta information, ' - f'which is immutable.') - super().__delattr__(item) - if item in self._data_fields: - self._data_fields.remove(item) - - # dict-like methods - __setitem__ = __setattr__ - __delitem__ = __delattr__ - - def __getitem__(self, name): - return getattr(self, name) - - def get(self, *args): - assert len(args) < 3, '`get` get more than 2 arguments' - return self.__dict__.get(*args) - - def pop(self, *args): - assert len(args) < 3, '`pop` get more than 2 arguments' - name = args[0] - if name in self._meta_info_fields: - raise KeyError(f'{name} is a key in meta information, ' - f'which is immutable') - - if args[0] in self._data_fields: - self._data_fields.remove(args[0]) - return self.__dict__.pop(*args) - - # with default value - elif len(args) == 2: - return args[1] - else: - raise KeyError(f'{args[0]}') - - def __contains__(self, item): - return item in self._data_fields or \ - item in self._meta_info_fields - - # Tensor-like methods - def to(self, *args, **kwargs): - """Apply same name function to all tensors in data_fields.""" - new_data = self.new() - for k, v in self.items(): - if hasattr(v, 'to'): - v = v.to(*args, **kwargs) - new_data[k] = v - return new_data - - # Tensor-like methods - def cpu(self): - """Apply same name function to all tensors in data_fields.""" - new_data = self.new() - for k, v in self.items(): - if isinstance(v, torch.Tensor): - v = v.cpu() - new_data[k] = v - return new_data - - # Tensor-like methods - def mlu(self): - """Apply same name function to all tensors in data_fields.""" - new_data = self.new() - for k, v in self.items(): - if isinstance(v, torch.Tensor): - v = v.mlu() - new_data[k] = v - return new_data - - # Tensor-like methods - def cuda(self): - """Apply same name function to all tensors in data_fields.""" - new_data = self.new() - for k, v in self.items(): - if isinstance(v, torch.Tensor): - v = v.cuda() - new_data[k] = v - return new_data - - # Tensor-like methods - def detach(self): - """Apply same name function to all tensors in data_fields.""" - new_data = self.new() - for k, v in self.items(): - if isinstance(v, torch.Tensor): - v = v.detach() - new_data[k] = v - return new_data - - # Tensor-like methods - def numpy(self): - """Apply same name function to all tensors in data_fields.""" - new_data = self.new() - for k, v in self.items(): - if isinstance(v, torch.Tensor): - v = v.detach().cpu().numpy() - new_data[k] = v - return new_data - - def __nice__(self): - repr = '\n \n META INFORMATION \n' - for k, v in self.meta_info_items(): - repr += f'{k}: {v} \n' - repr += '\n DATA FIELDS \n' - for k, v in self.items(): - if isinstance(v, (torch.Tensor, np.ndarray)): - repr += f'shape of {k}: {v.shape} \n' - else: - repr += f'{k}: {v} \n' - return repr + '\n' diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/data_structures/instance_data.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/data_structures/instance_data.py deleted file mode 100644 index eef2065c8..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/data_structures/instance_data.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import itertools - -import numpy as np -import torch - -from .general_data import GeneralData - - -class InstanceData(GeneralData): - """Data structure for instance-level annnotations or predictions. - - Subclass of :class:`GeneralData`. All value in `data_fields` - should have the same length. This design refer to - https://github.com/facebookresearch/detectron2/blob/master/detectron2/structures/instances.py # noqa E501 - - Examples: - >>> from mmdet.core import InstanceData - >>> import numpy as np - >>> img_meta = dict(img_shape=(800, 1196, 3), pad_shape=(800, 1216, 3)) - >>> results = InstanceData(img_meta) - >>> img_shape in results - True - >>> results.det_labels = torch.LongTensor([0, 1, 2, 3]) - >>> results["det_scores"] = torch.Tensor([0.01, 0.7, 0.6, 0.3]) - >>> results["det_masks"] = np.ndarray(4, 2, 2) - >>> len(results) - 4 - >>> print(resutls) - - >>> sorted_results = results[results.det_scores.sort().indices] - >>> sorted_results.det_scores - tensor([0.0100, 0.3000, 0.6000, 0.7000]) - >>> sorted_results.det_labels - tensor([0, 3, 2, 1]) - >>> print(results[results.scores > 0.5]) - - >>> results[results.det_scores > 0.5].det_labels - tensor([1, 2]) - >>> results[results.det_scores > 0.5].det_scores - tensor([0.7000, 0.6000]) - """ - - def __setattr__(self, name, value): - - if name in ('_meta_info_fields', '_data_fields'): - if not hasattr(self, name): - super().__setattr__(name, value) - else: - raise AttributeError( - f'{name} has been used as a ' - f'private attribute, which is immutable. ') - - else: - assert isinstance(value, (torch.Tensor, np.ndarray, list)), \ - f'Can set {type(value)}, only support' \ - f' {(torch.Tensor, np.ndarray, list)}' - - if self._data_fields: - assert len(value) == len(self), f'the length of ' \ - f'values {len(value)} is ' \ - f'not consistent with' \ - f' the length ' \ - f'of this :obj:`InstanceData` ' \ - f'{len(self)} ' - super().__setattr__(name, value) - - def __getitem__(self, item): - """ - Args: - item (str, obj:`slice`, - obj`torch.LongTensor`, obj:`torch.BoolTensor`): - get the corresponding values according to item. - - Returns: - obj:`InstanceData`: Corresponding values. - """ - assert len(self), ' This is a empty instance' - - assert isinstance( - item, (str, slice, int, torch.LongTensor, torch.BoolTensor)) - - if isinstance(item, str): - return getattr(self, item) - - if type(item) == int: - if item >= len(self) or item < -len(self): - raise IndexError(f'Index {item} out of range!') - else: - # keep the dimension - item = slice(item, None, len(self)) - - new_data = self.new() - if isinstance(item, (torch.Tensor)): - assert item.dim() == 1, 'Only support to get the' \ - ' values along the first dimension.' - if isinstance(item, torch.BoolTensor): - assert len(item) == len(self), f'The shape of the' \ - f' input(BoolTensor)) ' \ - f'{len(item)} ' \ - f' does not match the shape ' \ - f'of the indexed tensor ' \ - f'in results_filed ' \ - f'{len(self)} at ' \ - f'first dimension. ' - - for k, v in self.items(): - if isinstance(v, torch.Tensor): - new_data[k] = v[item] - elif isinstance(v, np.ndarray): - new_data[k] = v[item.cpu().numpy()] - elif isinstance(v, list): - r_list = [] - # convert to indexes from boolTensor - if isinstance(item, torch.BoolTensor): - indexes = torch.nonzero(item).view(-1) - else: - indexes = item - for index in indexes: - r_list.append(v[index]) - new_data[k] = r_list - else: - # item is a slice - for k, v in self.items(): - new_data[k] = v[item] - return new_data - - @staticmethod - def cat(instances_list): - """Concat the predictions of all :obj:`InstanceData` in the list. - - Args: - instances_list (list[:obj:`InstanceData`]): A list - of :obj:`InstanceData`. - - Returns: - obj:`InstanceData` - """ - assert all( - isinstance(results, InstanceData) for results in instances_list) - assert len(instances_list) > 0 - if len(instances_list) == 1: - return instances_list[0] - - new_data = instances_list[0].new() - for k in instances_list[0]._data_fields: - values = [results[k] for results in instances_list] - v0 = values[0] - if isinstance(v0, torch.Tensor): - values = torch.cat(values, dim=0) - elif isinstance(v0, np.ndarray): - values = np.concatenate(values, axis=0) - elif isinstance(v0, list): - values = list(itertools.chain(*values)) - else: - raise ValueError( - f'Can not concat the {k} which is a {type(v0)}') - new_data[k] = values - return new_data - - def __len__(self): - if len(self._data_fields): - for v in self.values(): - return len(v) - else: - raise AssertionError('This is an empty `InstanceData`.') diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/__init__.py deleted file mode 100644 index 67e7c55b3..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .class_names import (cityscapes_classes, coco_classes, dataset_aliases, - get_classes, imagenet_det_classes, - imagenet_vid_classes, oid_challenge_classes, - oid_v6_classes, voc_classes) -from .eval_hooks import DistEvalHook, EvalHook -from .mean_ap import average_precision, eval_map, print_map_summary -from .panoptic_utils import INSTANCE_OFFSET -from .recall import (eval_recalls, plot_iou_recall, plot_num_recall, - print_recall_summary) - -__all__ = [ - 'voc_classes', 'imagenet_det_classes', 'imagenet_vid_classes', - 'coco_classes', 'cityscapes_classes', 'dataset_aliases', 'get_classes', - 'DistEvalHook', 'EvalHook', 'average_precision', 'eval_map', - 'print_map_summary', 'eval_recalls', 'print_recall_summary', - 'plot_num_recall', 'plot_iou_recall', 'oid_v6_classes', - 'oid_challenge_classes', 'INSTANCE_OFFSET' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/bbox_overlaps.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/bbox_overlaps.py deleted file mode 100644 index 5d6eb82fc..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/bbox_overlaps.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - - -def bbox_overlaps(bboxes1, - bboxes2, - mode='iou', - eps=1e-6, - use_legacy_coordinate=False): - """Calculate the ious between each bbox of bboxes1 and bboxes2. - - Args: - bboxes1 (ndarray): Shape (n, 4) - bboxes2 (ndarray): Shape (k, 4) - mode (str): IOU (intersection over union) or IOF (intersection - over foreground) - use_legacy_coordinate (bool): Whether to use coordinate system in - mmdet v1.x. which means width, height should be - calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively. - Note when function is used in `VOCDataset`, it should be - True to align with the official implementation - `http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCdevkit_18-May-2011.tar` - Default: False. - - Returns: - ious (ndarray): Shape (n, k) - """ - - assert mode in ['iou', 'iof'] - if not use_legacy_coordinate: - extra_length = 0. - else: - extra_length = 1. - bboxes1 = bboxes1.astype(np.float32) - bboxes2 = bboxes2.astype(np.float32) - rows = bboxes1.shape[0] - cols = bboxes2.shape[0] - ious = np.zeros((rows, cols), dtype=np.float32) - if rows * cols == 0: - return ious - exchange = False - if bboxes1.shape[0] > bboxes2.shape[0]: - bboxes1, bboxes2 = bboxes2, bboxes1 - ious = np.zeros((cols, rows), dtype=np.float32) - exchange = True - area1 = (bboxes1[:, 2] - bboxes1[:, 0] + extra_length) * ( - bboxes1[:, 3] - bboxes1[:, 1] + extra_length) - area2 = (bboxes2[:, 2] - bboxes2[:, 0] + extra_length) * ( - bboxes2[:, 3] - bboxes2[:, 1] + extra_length) - for i in range(bboxes1.shape[0]): - x_start = np.maximum(bboxes1[i, 0], bboxes2[:, 0]) - y_start = np.maximum(bboxes1[i, 1], bboxes2[:, 1]) - x_end = np.minimum(bboxes1[i, 2], bboxes2[:, 2]) - y_end = np.minimum(bboxes1[i, 3], bboxes2[:, 3]) - overlap = np.maximum(x_end - x_start + extra_length, 0) * np.maximum( - y_end - y_start + extra_length, 0) - if mode == 'iou': - union = area1[i] + area2 - overlap - else: - union = area1[i] if not exchange else area2 - union = np.maximum(union, eps) - ious[i, :] = overlap / union - if exchange: - ious = ious.T - return ious diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/class_names.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/class_names.py deleted file mode 100644 index 737971182..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/class_names.py +++ /dev/null @@ -1,332 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - - -def wider_face_classes(): - return ['face'] - - -def voc_classes(): - return [ - 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', - 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', - 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor' - ] - - -def imagenet_det_classes(): - return [ - 'accordion', 'airplane', 'ant', 'antelope', 'apple', 'armadillo', - 'artichoke', 'axe', 'baby_bed', 'backpack', 'bagel', 'balance_beam', - 'banana', 'band_aid', 'banjo', 'baseball', 'basketball', 'bathing_cap', - 'beaker', 'bear', 'bee', 'bell_pepper', 'bench', 'bicycle', 'binder', - 'bird', 'bookshelf', 'bow_tie', 'bow', 'bowl', 'brassiere', 'burrito', - 'bus', 'butterfly', 'camel', 'can_opener', 'car', 'cart', 'cattle', - 'cello', 'centipede', 'chain_saw', 'chair', 'chime', 'cocktail_shaker', - 'coffee_maker', 'computer_keyboard', 'computer_mouse', 'corkscrew', - 'cream', 'croquet_ball', 'crutch', 'cucumber', 'cup_or_mug', 'diaper', - 'digital_clock', 'dishwasher', 'dog', 'domestic_cat', 'dragonfly', - 'drum', 'dumbbell', 'electric_fan', 'elephant', 'face_powder', 'fig', - 'filing_cabinet', 'flower_pot', 'flute', 'fox', 'french_horn', 'frog', - 'frying_pan', 'giant_panda', 'goldfish', 'golf_ball', 'golfcart', - 'guacamole', 'guitar', 'hair_dryer', 'hair_spray', 'hamburger', - 'hammer', 'hamster', 'harmonica', 'harp', 'hat_with_a_wide_brim', - 'head_cabbage', 'helmet', 'hippopotamus', 'horizontal_bar', 'horse', - 'hotdog', 'iPod', 'isopod', 'jellyfish', 'koala_bear', 'ladle', - 'ladybug', 'lamp', 'laptop', 'lemon', 'lion', 'lipstick', 'lizard', - 'lobster', 'maillot', 'maraca', 'microphone', 'microwave', 'milk_can', - 'miniskirt', 'monkey', 'motorcycle', 'mushroom', 'nail', 'neck_brace', - 'oboe', 'orange', 'otter', 'pencil_box', 'pencil_sharpener', 'perfume', - 'person', 'piano', 'pineapple', 'ping-pong_ball', 'pitcher', 'pizza', - 'plastic_bag', 'plate_rack', 'pomegranate', 'popsicle', 'porcupine', - 'power_drill', 'pretzel', 'printer', 'puck', 'punching_bag', 'purse', - 'rabbit', 'racket', 'ray', 'red_panda', 'refrigerator', - 'remote_control', 'rubber_eraser', 'rugby_ball', 'ruler', - 'salt_or_pepper_shaker', 'saxophone', 'scorpion', 'screwdriver', - 'seal', 'sheep', 'ski', 'skunk', 'snail', 'snake', 'snowmobile', - 'snowplow', 'soap_dispenser', 'soccer_ball', 'sofa', 'spatula', - 'squirrel', 'starfish', 'stethoscope', 'stove', 'strainer', - 'strawberry', 'stretcher', 'sunglasses', 'swimming_trunks', 'swine', - 'syringe', 'table', 'tape_player', 'tennis_ball', 'tick', 'tie', - 'tiger', 'toaster', 'traffic_light', 'train', 'trombone', 'trumpet', - 'turtle', 'tv_or_monitor', 'unicycle', 'vacuum', 'violin', - 'volleyball', 'waffle_iron', 'washer', 'water_bottle', 'watercraft', - 'whale', 'wine_bottle', 'zebra' - ] - - -def imagenet_vid_classes(): - return [ - 'airplane', 'antelope', 'bear', 'bicycle', 'bird', 'bus', 'car', - 'cattle', 'dog', 'domestic_cat', 'elephant', 'fox', 'giant_panda', - 'hamster', 'horse', 'lion', 'lizard', 'monkey', 'motorcycle', 'rabbit', - 'red_panda', 'sheep', 'snake', 'squirrel', 'tiger', 'train', 'turtle', - 'watercraft', 'whale', 'zebra' - ] - - -def coco_classes(): - return [ - 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', - 'truck', 'boat', 'traffic_light', 'fire_hydrant', 'stop_sign', - 'parking_meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', - 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', - 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', - 'sports_ball', 'kite', 'baseball_bat', 'baseball_glove', 'skateboard', - 'surfboard', 'tennis_racket', 'bottle', 'wine_glass', 'cup', 'fork', - 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', - 'broccoli', 'carrot', 'hot_dog', 'pizza', 'donut', 'cake', 'chair', - 'couch', 'potted_plant', 'bed', 'dining_table', 'toilet', 'tv', - 'laptop', 'mouse', 'remote', 'keyboard', 'cell_phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', - 'scissors', 'teddy_bear', 'hair_drier', 'toothbrush' - ] - - -def cityscapes_classes(): - return [ - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle' - ] - - -def oid_challenge_classes(): - return [ - 'Footwear', 'Jeans', 'House', 'Tree', 'Woman', 'Man', 'Land vehicle', - 'Person', 'Wheel', 'Bus', 'Human face', 'Bird', 'Dress', 'Girl', - 'Vehicle', 'Building', 'Cat', 'Car', 'Belt', 'Elephant', 'Dessert', - 'Butterfly', 'Train', 'Guitar', 'Poster', 'Book', 'Boy', 'Bee', - 'Flower', 'Window', 'Hat', 'Human head', 'Dog', 'Human arm', 'Drink', - 'Human mouth', 'Human hair', 'Human nose', 'Human hand', 'Table', - 'Marine invertebrates', 'Fish', 'Sculpture', 'Rose', 'Street light', - 'Glasses', 'Fountain', 'Skyscraper', 'Swimwear', 'Brassiere', 'Drum', - 'Duck', 'Countertop', 'Furniture', 'Ball', 'Human leg', 'Boat', - 'Balloon', 'Bicycle helmet', 'Goggles', 'Door', 'Human eye', 'Shirt', - 'Toy', 'Teddy bear', 'Pasta', 'Tomato', 'Human ear', - 'Vehicle registration plate', 'Microphone', 'Musical keyboard', - 'Tower', 'Houseplant', 'Flowerpot', 'Fruit', 'Vegetable', - 'Musical instrument', 'Suit', 'Motorcycle', 'Bagel', 'French fries', - 'Hamburger', 'Chair', 'Salt and pepper shakers', 'Snail', 'Airplane', - 'Horse', 'Laptop', 'Computer keyboard', 'Football helmet', 'Cocktail', - 'Juice', 'Tie', 'Computer monitor', 'Human beard', 'Bottle', - 'Saxophone', 'Lemon', 'Mouse', 'Sock', 'Cowboy hat', 'Sun hat', - 'Football', 'Porch', 'Sunglasses', 'Lobster', 'Crab', 'Picture frame', - 'Van', 'Crocodile', 'Surfboard', 'Shorts', 'Helicopter', 'Helmet', - 'Sports uniform', 'Taxi', 'Swan', 'Goose', 'Coat', 'Jacket', 'Handbag', - 'Flag', 'Skateboard', 'Television', 'Tire', 'Spoon', 'Palm tree', - 'Stairs', 'Salad', 'Castle', 'Oven', 'Microwave oven', 'Wine', - 'Ceiling fan', 'Mechanical fan', 'Cattle', 'Truck', 'Box', 'Ambulance', - 'Desk', 'Wine glass', 'Reptile', 'Tank', 'Traffic light', 'Billboard', - 'Tent', 'Insect', 'Spider', 'Treadmill', 'Cupboard', 'Shelf', - 'Seat belt', 'Human foot', 'Bicycle', 'Bicycle wheel', 'Couch', - 'Bookcase', 'Fedora', 'Backpack', 'Bench', 'Oyster', - 'Moths and butterflies', 'Lavender', 'Waffle', 'Fork', 'Animal', - 'Accordion', 'Mobile phone', 'Plate', 'Coffee cup', 'Saucer', - 'Platter', 'Dagger', 'Knife', 'Bull', 'Tortoise', 'Sea turtle', 'Deer', - 'Weapon', 'Apple', 'Ski', 'Taco', 'Traffic sign', 'Beer', 'Necklace', - 'Sunflower', 'Piano', 'Organ', 'Harpsichord', 'Bed', 'Cabinetry', - 'Nightstand', 'Curtain', 'Chest of drawers', 'Drawer', 'Parrot', - 'Sandal', 'High heels', 'Tableware', 'Cart', 'Mushroom', 'Kite', - 'Missile', 'Seafood', 'Camera', 'Paper towel', 'Toilet paper', - 'Sombrero', 'Radish', 'Lighthouse', 'Segway', 'Pig', 'Watercraft', - 'Golf cart', 'studio couch', 'Dolphin', 'Whale', 'Earrings', 'Otter', - 'Sea lion', 'Whiteboard', 'Monkey', 'Gondola', 'Zebra', - 'Baseball glove', 'Scarf', 'Adhesive tape', 'Trousers', 'Scoreboard', - 'Lily', 'Carnivore', 'Power plugs and sockets', 'Office building', - 'Sandwich', 'Swimming pool', 'Headphones', 'Tin can', 'Crown', 'Doll', - 'Cake', 'Frog', 'Beetle', 'Ant', 'Gas stove', 'Canoe', 'Falcon', - 'Blue jay', 'Egg', 'Fire hydrant', 'Raccoon', 'Muffin', 'Wall clock', - 'Coffee', 'Mug', 'Tea', 'Bear', 'Waste container', 'Home appliance', - 'Candle', 'Lion', 'Mirror', 'Starfish', 'Marine mammal', 'Wheelchair', - 'Umbrella', 'Alpaca', 'Violin', 'Cello', 'Brown bear', 'Canary', 'Bat', - 'Ruler', 'Plastic bag', 'Penguin', 'Watermelon', 'Harbor seal', 'Pen', - 'Pumpkin', 'Harp', 'Kitchen appliance', 'Roller skates', 'Bust', - 'Coffee table', 'Tennis ball', 'Tennis racket', 'Ladder', 'Boot', - 'Bowl', 'Stop sign', 'Volleyball', 'Eagle', 'Paddle', 'Chicken', - 'Skull', 'Lamp', 'Beehive', 'Maple', 'Sink', 'Goldfish', 'Tripod', - 'Coconut', 'Bidet', 'Tap', 'Bathroom cabinet', 'Toilet', - 'Filing cabinet', 'Pretzel', 'Table tennis racket', 'Bronze sculpture', - 'Rocket', 'Mouse', 'Hamster', 'Lizard', 'Lifejacket', 'Goat', - 'Washing machine', 'Trumpet', 'Horn', 'Trombone', 'Sheep', - 'Tablet computer', 'Pillow', 'Kitchen & dining room table', - 'Parachute', 'Raven', 'Glove', 'Loveseat', 'Christmas tree', - 'Shellfish', 'Rifle', 'Shotgun', 'Sushi', 'Sparrow', 'Bread', - 'Toaster', 'Watch', 'Asparagus', 'Artichoke', 'Suitcase', 'Antelope', - 'Broccoli', 'Ice cream', 'Racket', 'Banana', 'Cookie', 'Cucumber', - 'Dragonfly', 'Lynx', 'Caterpillar', 'Light bulb', 'Office supplies', - 'Miniskirt', 'Skirt', 'Fireplace', 'Potato', 'Light switch', - 'Croissant', 'Cabbage', 'Ladybug', 'Handgun', 'Luggage and bags', - 'Window blind', 'Snowboard', 'Baseball bat', 'Digital clock', - 'Serving tray', 'Infant bed', 'Sofa bed', 'Guacamole', 'Fox', 'Pizza', - 'Snowplow', 'Jet ski', 'Refrigerator', 'Lantern', 'Convenience store', - 'Sword', 'Rugby ball', 'Owl', 'Ostrich', 'Pancake', 'Strawberry', - 'Carrot', 'Tart', 'Dice', 'Turkey', 'Rabbit', 'Invertebrate', 'Vase', - 'Stool', 'Swim cap', 'Shower', 'Clock', 'Jellyfish', 'Aircraft', - 'Chopsticks', 'Orange', 'Snake', 'Sewing machine', 'Kangaroo', 'Mixer', - 'Food processor', 'Shrimp', 'Towel', 'Porcupine', 'Jaguar', 'Cannon', - 'Limousine', 'Mule', 'Squirrel', 'Kitchen knife', 'Tiara', 'Tiger', - 'Bow and arrow', 'Candy', 'Rhinoceros', 'Shark', 'Cricket ball', - 'Doughnut', 'Plumbing fixture', 'Camel', 'Polar bear', 'Coin', - 'Printer', 'Blender', 'Giraffe', 'Billiard table', 'Kettle', - 'Dinosaur', 'Pineapple', 'Zucchini', 'Jug', 'Barge', 'Teapot', - 'Golf ball', 'Binoculars', 'Scissors', 'Hot dog', 'Door handle', - 'Seahorse', 'Bathtub', 'Leopard', 'Centipede', 'Grapefruit', 'Snowman', - 'Cheetah', 'Alarm clock', 'Grape', 'Wrench', 'Wok', 'Bell pepper', - 'Cake stand', 'Barrel', 'Woodpecker', 'Flute', 'Corded phone', - 'Willow', 'Punching bag', 'Pomegranate', 'Telephone', 'Pear', - 'Common fig', 'Bench', 'Wood-burning stove', 'Burrito', 'Nail', - 'Turtle', 'Submarine sandwich', 'Drinking straw', 'Peach', 'Popcorn', - 'Frying pan', 'Picnic basket', 'Honeycomb', 'Envelope', 'Mango', - 'Cutting board', 'Pitcher', 'Stationary bicycle', 'Dumbbell', - 'Personal care', 'Dog bed', 'Snowmobile', 'Oboe', 'Briefcase', - 'Squash', 'Tick', 'Slow cooker', 'Coffeemaker', 'Measuring cup', - 'Crutch', 'Stretcher', 'Screwdriver', 'Flashlight', 'Spatula', - 'Pressure cooker', 'Ring binder', 'Beaker', 'Torch', 'Winter melon' - ] - - -def oid_v6_classes(): - return [ - 'Tortoise', 'Container', 'Magpie', 'Sea turtle', 'Football', - 'Ambulance', 'Ladder', 'Toothbrush', 'Syringe', 'Sink', 'Toy', - 'Organ (Musical Instrument)', 'Cassette deck', 'Apple', 'Human eye', - 'Cosmetics', 'Paddle', 'Snowman', 'Beer', 'Chopsticks', 'Human beard', - 'Bird', 'Parking meter', 'Traffic light', 'Croissant', 'Cucumber', - 'Radish', 'Towel', 'Doll', 'Skull', 'Washing machine', 'Glove', 'Tick', - 'Belt', 'Sunglasses', 'Banjo', 'Cart', 'Ball', 'Backpack', 'Bicycle', - 'Home appliance', 'Centipede', 'Boat', 'Surfboard', 'Boot', - 'Headphones', 'Hot dog', 'Shorts', 'Fast food', 'Bus', 'Boy', - 'Screwdriver', 'Bicycle wheel', 'Barge', 'Laptop', 'Miniskirt', - 'Drill (Tool)', 'Dress', 'Bear', 'Waffle', 'Pancake', 'Brown bear', - 'Woodpecker', 'Blue jay', 'Pretzel', 'Bagel', 'Tower', 'Teapot', - 'Person', 'Bow and arrow', 'Swimwear', 'Beehive', 'Brassiere', 'Bee', - 'Bat (Animal)', 'Starfish', 'Popcorn', 'Burrito', 'Chainsaw', - 'Balloon', 'Wrench', 'Tent', 'Vehicle registration plate', 'Lantern', - 'Toaster', 'Flashlight', 'Billboard', 'Tiara', 'Limousine', 'Necklace', - 'Carnivore', 'Scissors', 'Stairs', 'Computer keyboard', 'Printer', - 'Traffic sign', 'Chair', 'Shirt', 'Poster', 'Cheese', 'Sock', - 'Fire hydrant', 'Land vehicle', 'Earrings', 'Tie', 'Watercraft', - 'Cabinetry', 'Suitcase', 'Muffin', 'Bidet', 'Snack', 'Snowmobile', - 'Clock', 'Medical equipment', 'Cattle', 'Cello', 'Jet ski', 'Camel', - 'Coat', 'Suit', 'Desk', 'Cat', 'Bronze sculpture', 'Juice', 'Gondola', - 'Beetle', 'Cannon', 'Computer mouse', 'Cookie', 'Office building', - 'Fountain', 'Coin', 'Calculator', 'Cocktail', 'Computer monitor', - 'Box', 'Stapler', 'Christmas tree', 'Cowboy hat', 'Hiking equipment', - 'Studio couch', 'Drum', 'Dessert', 'Wine rack', 'Drink', 'Zucchini', - 'Ladle', 'Human mouth', 'Dairy Product', 'Dice', 'Oven', 'Dinosaur', - 'Ratchet (Device)', 'Couch', 'Cricket ball', 'Winter melon', 'Spatula', - 'Whiteboard', 'Pencil sharpener', 'Door', 'Hat', 'Shower', 'Eraser', - 'Fedora', 'Guacamole', 'Dagger', 'Scarf', 'Dolphin', 'Sombrero', - 'Tin can', 'Mug', 'Tap', 'Harbor seal', 'Stretcher', 'Can opener', - 'Goggles', 'Human body', 'Roller skates', 'Coffee cup', - 'Cutting board', 'Blender', 'Plumbing fixture', 'Stop sign', - 'Office supplies', 'Volleyball (Ball)', 'Vase', 'Slow cooker', - 'Wardrobe', 'Coffee', 'Whisk', 'Paper towel', 'Personal care', 'Food', - 'Sun hat', 'Tree house', 'Flying disc', 'Skirt', 'Gas stove', - 'Salt and pepper shakers', 'Mechanical fan', 'Face powder', 'Fax', - 'Fruit', 'French fries', 'Nightstand', 'Barrel', 'Kite', 'Tart', - 'Treadmill', 'Fox', 'Flag', 'French horn', 'Window blind', - 'Human foot', 'Golf cart', 'Jacket', 'Egg (Food)', 'Street light', - 'Guitar', 'Pillow', 'Human leg', 'Isopod', 'Grape', 'Human ear', - 'Power plugs and sockets', 'Panda', 'Giraffe', 'Woman', 'Door handle', - 'Rhinoceros', 'Bathtub', 'Goldfish', 'Houseplant', 'Goat', - 'Baseball bat', 'Baseball glove', 'Mixing bowl', - 'Marine invertebrates', 'Kitchen utensil', 'Light switch', 'House', - 'Horse', 'Stationary bicycle', 'Hammer', 'Ceiling fan', 'Sofa bed', - 'Adhesive tape', 'Harp', 'Sandal', 'Bicycle helmet', 'Saucer', - 'Harpsichord', 'Human hair', 'Heater', 'Harmonica', 'Hamster', - 'Curtain', 'Bed', 'Kettle', 'Fireplace', 'Scale', 'Drinking straw', - 'Insect', 'Hair dryer', 'Kitchenware', 'Indoor rower', 'Invertebrate', - 'Food processor', 'Bookcase', 'Refrigerator', 'Wood-burning stove', - 'Punching bag', 'Common fig', 'Cocktail shaker', 'Jaguar (Animal)', - 'Golf ball', 'Fashion accessory', 'Alarm clock', 'Filing cabinet', - 'Artichoke', 'Table', 'Tableware', 'Kangaroo', 'Koala', 'Knife', - 'Bottle', 'Bottle opener', 'Lynx', 'Lavender (Plant)', 'Lighthouse', - 'Dumbbell', 'Human head', 'Bowl', 'Humidifier', 'Porch', 'Lizard', - 'Billiard table', 'Mammal', 'Mouse', 'Motorcycle', - 'Musical instrument', 'Swim cap', 'Frying pan', 'Snowplow', - 'Bathroom cabinet', 'Missile', 'Bust', 'Man', 'Waffle iron', 'Milk', - 'Ring binder', 'Plate', 'Mobile phone', 'Baked goods', 'Mushroom', - 'Crutch', 'Pitcher (Container)', 'Mirror', 'Personal flotation device', - 'Table tennis racket', 'Pencil case', 'Musical keyboard', 'Scoreboard', - 'Briefcase', 'Kitchen knife', 'Nail (Construction)', 'Tennis ball', - 'Plastic bag', 'Oboe', 'Chest of drawers', 'Ostrich', 'Piano', 'Girl', - 'Plant', 'Potato', 'Hair spray', 'Sports equipment', 'Pasta', - 'Penguin', 'Pumpkin', 'Pear', 'Infant bed', 'Polar bear', 'Mixer', - 'Cupboard', 'Jacuzzi', 'Pizza', 'Digital clock', 'Pig', 'Reptile', - 'Rifle', 'Lipstick', 'Skateboard', 'Raven', 'High heels', 'Red panda', - 'Rose', 'Rabbit', 'Sculpture', 'Saxophone', 'Shotgun', 'Seafood', - 'Submarine sandwich', 'Snowboard', 'Sword', 'Picture frame', 'Sushi', - 'Loveseat', 'Ski', 'Squirrel', 'Tripod', 'Stethoscope', 'Submarine', - 'Scorpion', 'Segway', 'Training bench', 'Snake', 'Coffee table', - 'Skyscraper', 'Sheep', 'Television', 'Trombone', 'Tea', 'Tank', 'Taco', - 'Telephone', 'Torch', 'Tiger', 'Strawberry', 'Trumpet', 'Tree', - 'Tomato', 'Train', 'Tool', 'Picnic basket', 'Cooking spray', - 'Trousers', 'Bowling equipment', 'Football helmet', 'Truck', - 'Measuring cup', 'Coffeemaker', 'Violin', 'Vehicle', 'Handbag', - 'Paper cutter', 'Wine', 'Weapon', 'Wheel', 'Worm', 'Wok', 'Whale', - 'Zebra', 'Auto part', 'Jug', 'Pizza cutter', 'Cream', 'Monkey', 'Lion', - 'Bread', 'Platter', 'Chicken', 'Eagle', 'Helicopter', 'Owl', 'Duck', - 'Turtle', 'Hippopotamus', 'Crocodile', 'Toilet', 'Toilet paper', - 'Squid', 'Clothing', 'Footwear', 'Lemon', 'Spider', 'Deer', 'Frog', - 'Banana', 'Rocket', 'Wine glass', 'Countertop', 'Tablet computer', - 'Waste container', 'Swimming pool', 'Dog', 'Book', 'Elephant', 'Shark', - 'Candle', 'Leopard', 'Axe', 'Hand dryer', 'Soap dispenser', - 'Porcupine', 'Flower', 'Canary', 'Cheetah', 'Palm tree', 'Hamburger', - 'Maple', 'Building', 'Fish', 'Lobster', 'Garden Asparagus', - 'Furniture', 'Hedgehog', 'Airplane', 'Spoon', 'Otter', 'Bull', - 'Oyster', 'Horizontal bar', 'Convenience store', 'Bomb', 'Bench', - 'Ice cream', 'Caterpillar', 'Butterfly', 'Parachute', 'Orange', - 'Antelope', 'Beaker', 'Moths and butterflies', 'Window', 'Closet', - 'Castle', 'Jellyfish', 'Goose', 'Mule', 'Swan', 'Peach', 'Coconut', - 'Seat belt', 'Raccoon', 'Chisel', 'Fork', 'Lamp', 'Camera', - 'Squash (Plant)', 'Racket', 'Human face', 'Human arm', 'Vegetable', - 'Diaper', 'Unicycle', 'Falcon', 'Chime', 'Snail', 'Shellfish', - 'Cabbage', 'Carrot', 'Mango', 'Jeans', 'Flowerpot', 'Pineapple', - 'Drawer', 'Stool', 'Envelope', 'Cake', 'Dragonfly', 'Common sunflower', - 'Microwave oven', 'Honeycomb', 'Marine mammal', 'Sea lion', 'Ladybug', - 'Shelf', 'Watch', 'Candy', 'Salad', 'Parrot', 'Handgun', 'Sparrow', - 'Van', 'Grinder', 'Spice rack', 'Light bulb', 'Corded phone', - 'Sports uniform', 'Tennis racket', 'Wall clock', 'Serving tray', - 'Kitchen & dining room table', 'Dog bed', 'Cake stand', - 'Cat furniture', 'Bathroom accessory', 'Facial tissue holder', - 'Pressure cooker', 'Kitchen appliance', 'Tire', 'Ruler', - 'Luggage and bags', 'Microphone', 'Broccoli', 'Umbrella', 'Pastry', - 'Grapefruit', 'Band-aid', 'Animal', 'Bell pepper', 'Turkey', 'Lily', - 'Pomegranate', 'Doughnut', 'Glasses', 'Human nose', 'Pen', 'Ant', - 'Car', 'Aircraft', 'Human hand', 'Skunk', 'Teddy bear', 'Watermelon', - 'Cantaloupe', 'Dishwasher', 'Flute', 'Balance beam', 'Sandwich', - 'Shrimp', 'Sewing machine', 'Binoculars', 'Rays and skates', 'Ipod', - 'Accordion', 'Willow', 'Crab', 'Crown', 'Seahorse', 'Perfume', - 'Alpaca', 'Taxi', 'Canoe', 'Remote control', 'Wheelchair', - 'Rugby ball', 'Armadillo', 'Maracas', 'Helmet' - ] - - -dataset_aliases = { - 'voc': ['voc', 'pascal_voc', 'voc07', 'voc12'], - 'imagenet_det': ['det', 'imagenet_det', 'ilsvrc_det'], - 'imagenet_vid': ['vid', 'imagenet_vid', 'ilsvrc_vid'], - 'coco': ['coco', 'mscoco', 'ms_coco'], - 'wider_face': ['WIDERFaceDataset', 'wider_face', 'WIDERFace'], - 'cityscapes': ['cityscapes'], - 'oid_challenge': ['oid_challenge', 'openimages_challenge'], - 'oid_v6': ['oid_v6', 'openimages_v6'] -} - - -def get_classes(dataset): - """Get class names of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_classes()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/eval_hooks.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/eval_hooks.py deleted file mode 100644 index 98856c18c..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/eval_hooks.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import bisect -import os.path as osp - -import mmcv -import torch.distributed as dist -from mmcv.runner import DistEvalHook as BaseDistEvalHook -from mmcv.runner import EvalHook as BaseEvalHook -from torch.nn.modules.batchnorm import _BatchNorm - - -def _calc_dynamic_intervals(start_interval, dynamic_interval_list): - assert mmcv.is_list_of(dynamic_interval_list, tuple) - - dynamic_milestones = [0] - dynamic_milestones.extend( - [dynamic_interval[0] for dynamic_interval in dynamic_interval_list]) - dynamic_intervals = [start_interval] - dynamic_intervals.extend( - [dynamic_interval[1] for dynamic_interval in dynamic_interval_list]) - return dynamic_milestones, dynamic_intervals - - -class EvalHook(BaseEvalHook): - - def __init__(self, *args, dynamic_intervals=None, **kwargs): - super(EvalHook, self).__init__(*args, **kwargs) - self.latest_results = None - - self.use_dynamic_intervals = dynamic_intervals is not None - if self.use_dynamic_intervals: - self.dynamic_milestones, self.dynamic_intervals = \ - _calc_dynamic_intervals(self.interval, dynamic_intervals) - - def _decide_interval(self, runner): - if self.use_dynamic_intervals: - progress = runner.epoch if self.by_epoch else runner.iter - step = bisect.bisect(self.dynamic_milestones, (progress + 1)) - # Dynamically modify the evaluation interval - self.interval = self.dynamic_intervals[step - 1] - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - self._decide_interval(runner) - super().before_train_epoch(runner) - - def before_train_iter(self, runner): - self._decide_interval(runner) - super().before_train_iter(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - if not self._should_evaluate(runner): - return - - from mmdet.apis import single_gpu_test - - # Changed results to self.results so that MMDetWandbHook can access - # the evaluation results and log them to wandb. - results = single_gpu_test(runner.model, self.dataloader, show=False) - self.latest_results = results - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - -# Note: Considering that MMCV's EvalHook updated its interface in V1.3.16, -# in order to avoid strong version dependency, we did not directly -# inherit EvalHook but BaseDistEvalHook. -class DistEvalHook(BaseDistEvalHook): - - def __init__(self, *args, dynamic_intervals=None, **kwargs): - super(DistEvalHook, self).__init__(*args, **kwargs) - self.latest_results = None - - self.use_dynamic_intervals = dynamic_intervals is not None - if self.use_dynamic_intervals: - self.dynamic_milestones, self.dynamic_intervals = \ - _calc_dynamic_intervals(self.interval, dynamic_intervals) - - def _decide_interval(self, runner): - if self.use_dynamic_intervals: - progress = runner.epoch if self.by_epoch else runner.iter - step = bisect.bisect(self.dynamic_milestones, (progress + 1)) - # Dynamically modify the evaluation interval - self.interval = self.dynamic_intervals[step - 1] - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - self._decide_interval(runner) - super().before_train_epoch(runner) - - def before_train_iter(self, runner): - self._decide_interval(runner) - super().before_train_iter(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - if not self._should_evaluate(runner): - return - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - from mmdet.apis import multi_gpu_test - - # Changed results to self.results so that MMDetWandbHook can access - # the evaluation results and log them to wandb. - results = multi_gpu_test( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - self.latest_results = results - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - - # the key_score may be `None` so it needs to skip - # the action to save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/mean_ap.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/mean_ap.py deleted file mode 100644 index a293b80f0..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/mean_ap.py +++ /dev/null @@ -1,782 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from multiprocessing import Pool - -import mmcv -import numpy as np -from mmcv.utils import print_log -from terminaltables import AsciiTable - -from .bbox_overlaps import bbox_overlaps -from .class_names import get_classes - - -def average_precision(recalls, precisions, mode='area'): - """Calculate average precision (for single or multiple scales). - - Args: - recalls (ndarray): shape (num_scales, num_dets) or (num_dets, ) - precisions (ndarray): shape (num_scales, num_dets) or (num_dets, ) - mode (str): 'area' or '11points', 'area' means calculating the area - under precision-recall curve, '11points' means calculating - the average precision of recalls at [0, 0.1, ..., 1] - - Returns: - float or ndarray: calculated average precision - """ - no_scale = False - if recalls.ndim == 1: - no_scale = True - recalls = recalls[np.newaxis, :] - precisions = precisions[np.newaxis, :] - assert recalls.shape == precisions.shape and recalls.ndim == 2 - num_scales = recalls.shape[0] - ap = np.zeros(num_scales, dtype=np.float32) - if mode == 'area': - zeros = np.zeros((num_scales, 1), dtype=recalls.dtype) - ones = np.ones((num_scales, 1), dtype=recalls.dtype) - mrec = np.hstack((zeros, recalls, ones)) - mpre = np.hstack((zeros, precisions, zeros)) - for i in range(mpre.shape[1] - 1, 0, -1): - mpre[:, i - 1] = np.maximum(mpre[:, i - 1], mpre[:, i]) - for i in range(num_scales): - ind = np.where(mrec[i, 1:] != mrec[i, :-1])[0] - ap[i] = np.sum( - (mrec[i, ind + 1] - mrec[i, ind]) * mpre[i, ind + 1]) - elif mode == '11points': - for i in range(num_scales): - for thr in np.arange(0, 1 + 1e-3, 0.1): - precs = precisions[i, recalls[i, :] >= thr] - prec = precs.max() if precs.size > 0 else 0 - ap[i] += prec - ap /= 11 - else: - raise ValueError( - 'Unrecognized mode, only "area" and "11points" are supported') - if no_scale: - ap = ap[0] - return ap - - -def tpfp_imagenet(det_bboxes, - gt_bboxes, - gt_bboxes_ignore=None, - default_iou_thr=0.5, - area_ranges=None, - use_legacy_coordinate=False, - **kwargs): - """Check if detected bboxes are true positive or false positive. - - Args: - det_bbox (ndarray): Detected bboxes of this image, of shape (m, 5). - gt_bboxes (ndarray): GT bboxes of this image, of shape (n, 4). - gt_bboxes_ignore (ndarray): Ignored gt bboxes of this image, - of shape (k, 4). Default: None - default_iou_thr (float): IoU threshold to be considered as matched for - medium and large bboxes (small ones have special rules). - Default: 0.5. - area_ranges (list[tuple] | None): Range of bbox areas to be evaluated, - in the format [(min1, max1), (min2, max2), ...]. Default: None. - use_legacy_coordinate (bool): Whether to use coordinate system in - mmdet v1.x. which means width, height should be - calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively. - Default: False. - - Returns: - tuple[np.ndarray]: (tp, fp) whose elements are 0 and 1. The shape of - each array is (num_scales, m). - """ - - if not use_legacy_coordinate: - extra_length = 0. - else: - extra_length = 1. - - # an indicator of ignored gts - gt_ignore_inds = np.concatenate( - (np.zeros(gt_bboxes.shape[0], dtype=np.bool), - np.ones(gt_bboxes_ignore.shape[0], dtype=np.bool))) - # stack gt_bboxes and gt_bboxes_ignore for convenience - gt_bboxes = np.vstack((gt_bboxes, gt_bboxes_ignore)) - - num_dets = det_bboxes.shape[0] - num_gts = gt_bboxes.shape[0] - if area_ranges is None: - area_ranges = [(None, None)] - num_scales = len(area_ranges) - # tp and fp are of shape (num_scales, num_gts), each row is tp or fp - # of a certain scale. - tp = np.zeros((num_scales, num_dets), dtype=np.float32) - fp = np.zeros((num_scales, num_dets), dtype=np.float32) - if gt_bboxes.shape[0] == 0: - if area_ranges == [(None, None)]: - fp[...] = 1 - else: - det_areas = ( - det_bboxes[:, 2] - det_bboxes[:, 0] + extra_length) * ( - det_bboxes[:, 3] - det_bboxes[:, 1] + extra_length) - for i, (min_area, max_area) in enumerate(area_ranges): - fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1 - return tp, fp - ious = bbox_overlaps( - det_bboxes, gt_bboxes - 1, use_legacy_coordinate=use_legacy_coordinate) - gt_w = gt_bboxes[:, 2] - gt_bboxes[:, 0] + extra_length - gt_h = gt_bboxes[:, 3] - gt_bboxes[:, 1] + extra_length - iou_thrs = np.minimum((gt_w * gt_h) / ((gt_w + 10.0) * (gt_h + 10.0)), - default_iou_thr) - # sort all detections by scores in descending order - sort_inds = np.argsort(-det_bboxes[:, -1]) - for k, (min_area, max_area) in enumerate(area_ranges): - gt_covered = np.zeros(num_gts, dtype=bool) - # if no area range is specified, gt_area_ignore is all False - if min_area is None: - gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool) - else: - gt_areas = gt_w * gt_h - gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area) - for i in sort_inds: - max_iou = -1 - matched_gt = -1 - # find best overlapped available gt - for j in range(num_gts): - # different from PASCAL VOC: allow finding other gts if the - # best overlapped ones are already matched by other det bboxes - if gt_covered[j]: - continue - elif ious[i, j] >= iou_thrs[j] and ious[i, j] > max_iou: - max_iou = ious[i, j] - matched_gt = j - # there are 4 cases for a det bbox: - # 1. it matches a gt, tp = 1, fp = 0 - # 2. it matches an ignored gt, tp = 0, fp = 0 - # 3. it matches no gt and within area range, tp = 0, fp = 1 - # 4. it matches no gt but is beyond area range, tp = 0, fp = 0 - if matched_gt >= 0: - gt_covered[matched_gt] = 1 - if not (gt_ignore_inds[matched_gt] - or gt_area_ignore[matched_gt]): - tp[k, i] = 1 - elif min_area is None: - fp[k, i] = 1 - else: - bbox = det_bboxes[i, :4] - area = (bbox[2] - bbox[0] + extra_length) * ( - bbox[3] - bbox[1] + extra_length) - if area >= min_area and area < max_area: - fp[k, i] = 1 - return tp, fp - - -def tpfp_default(det_bboxes, - gt_bboxes, - gt_bboxes_ignore=None, - iou_thr=0.5, - area_ranges=None, - use_legacy_coordinate=False, - **kwargs): - """Check if detected bboxes are true positive or false positive. - - Args: - det_bbox (ndarray): Detected bboxes of this image, of shape (m, 5). - gt_bboxes (ndarray): GT bboxes of this image, of shape (n, 4). - gt_bboxes_ignore (ndarray): Ignored gt bboxes of this image, - of shape (k, 4). Default: None - iou_thr (float): IoU threshold to be considered as matched. - Default: 0.5. - area_ranges (list[tuple] | None): Range of bbox areas to be - evaluated, in the format [(min1, max1), (min2, max2), ...]. - Default: None. - use_legacy_coordinate (bool): Whether to use coordinate system in - mmdet v1.x. which means width, height should be - calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively. - Default: False. - - Returns: - tuple[np.ndarray]: (tp, fp) whose elements are 0 and 1. The shape of - each array is (num_scales, m). - """ - - if not use_legacy_coordinate: - extra_length = 0. - else: - extra_length = 1. - - # an indicator of ignored gts - gt_ignore_inds = np.concatenate( - (np.zeros(gt_bboxes.shape[0], dtype=np.bool), - np.ones(gt_bboxes_ignore.shape[0], dtype=np.bool))) - # stack gt_bboxes and gt_bboxes_ignore for convenience - gt_bboxes = np.vstack((gt_bboxes, gt_bboxes_ignore)) - - num_dets = det_bboxes.shape[0] - num_gts = gt_bboxes.shape[0] - if area_ranges is None: - area_ranges = [(None, None)] - num_scales = len(area_ranges) - # tp and fp are of shape (num_scales, num_gts), each row is tp or fp of - # a certain scale - tp = np.zeros((num_scales, num_dets), dtype=np.float32) - fp = np.zeros((num_scales, num_dets), dtype=np.float32) - - # if there is no gt bboxes in this image, then all det bboxes - # within area range are false positives - if gt_bboxes.shape[0] == 0: - if area_ranges == [(None, None)]: - fp[...] = 1 - else: - det_areas = ( - det_bboxes[:, 2] - det_bboxes[:, 0] + extra_length) * ( - det_bboxes[:, 3] - det_bboxes[:, 1] + extra_length) - for i, (min_area, max_area) in enumerate(area_ranges): - fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1 - return tp, fp - - ious = bbox_overlaps( - det_bboxes, gt_bboxes, use_legacy_coordinate=use_legacy_coordinate) - # for each det, the max iou with all gts - ious_max = ious.max(axis=1) - # for each det, which gt overlaps most with it - ious_argmax = ious.argmax(axis=1) - # sort all dets in descending order by scores - sort_inds = np.argsort(-det_bboxes[:, -1]) - for k, (min_area, max_area) in enumerate(area_ranges): - gt_covered = np.zeros(num_gts, dtype=bool) - # if no area range is specified, gt_area_ignore is all False - if min_area is None: - gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool) - else: - gt_areas = (gt_bboxes[:, 2] - gt_bboxes[:, 0] + extra_length) * ( - gt_bboxes[:, 3] - gt_bboxes[:, 1] + extra_length) - gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area) - for i in sort_inds: - if ious_max[i] >= iou_thr: - matched_gt = ious_argmax[i] - if not (gt_ignore_inds[matched_gt] - or gt_area_ignore[matched_gt]): - if not gt_covered[matched_gt]: - gt_covered[matched_gt] = True - tp[k, i] = 1 - else: - fp[k, i] = 1 - # otherwise ignore this detected bbox, tp = 0, fp = 0 - elif min_area is None: - fp[k, i] = 1 - else: - bbox = det_bboxes[i, :4] - area = (bbox[2] - bbox[0] + extra_length) * ( - bbox[3] - bbox[1] + extra_length) - if area >= min_area and area < max_area: - fp[k, i] = 1 - return tp, fp - - -def tpfp_openimages(det_bboxes, - gt_bboxes, - gt_bboxes_ignore=None, - iou_thr=0.5, - area_ranges=None, - use_legacy_coordinate=False, - gt_bboxes_group_of=None, - use_group_of=True, - ioa_thr=0.5, - **kwargs): - """Check if detected bboxes are true positive or false positive. - - Args: - det_bbox (ndarray): Detected bboxes of this image, of shape (m, 5). - gt_bboxes (ndarray): GT bboxes of this image, of shape (n, 4). - gt_bboxes_ignore (ndarray): Ignored gt bboxes of this image, - of shape (k, 4). Default: None - iou_thr (float): IoU threshold to be considered as matched. - Default: 0.5. - area_ranges (list[tuple] | None): Range of bbox areas to be - evaluated, in the format [(min1, max1), (min2, max2), ...]. - Default: None. - use_legacy_coordinate (bool): Whether to use coordinate system in - mmdet v1.x. which means width, height should be - calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively. - Default: False. - gt_bboxes_group_of (ndarray): GT group_of of this image, of shape - (k, 1). Default: None - use_group_of (bool): Whether to use group of when calculate TP and FP, - which only used in OpenImages evaluation. Default: True. - ioa_thr (float | None): IoA threshold to be considered as matched, - which only used in OpenImages evaluation. Default: 0.5. - - Returns: - tuple[np.ndarray]: Returns a tuple (tp, fp, det_bboxes), where - (tp, fp) whose elements are 0 and 1. The shape of each array is - (num_scales, m). (det_bboxes) whose will filter those are not - matched by group of gts when processing Open Images evaluation. - The shape is (num_scales, m). - """ - - if not use_legacy_coordinate: - extra_length = 0. - else: - extra_length = 1. - - # an indicator of ignored gts - gt_ignore_inds = np.concatenate( - (np.zeros(gt_bboxes.shape[0], dtype=np.bool), - np.ones(gt_bboxes_ignore.shape[0], dtype=np.bool))) - # stack gt_bboxes and gt_bboxes_ignore for convenience - gt_bboxes = np.vstack((gt_bboxes, gt_bboxes_ignore)) - - num_dets = det_bboxes.shape[0] - num_gts = gt_bboxes.shape[0] - if area_ranges is None: - area_ranges = [(None, None)] - num_scales = len(area_ranges) - # tp and fp are of shape (num_scales, num_gts), each row is tp or fp of - # a certain scale - tp = np.zeros((num_scales, num_dets), dtype=np.float32) - fp = np.zeros((num_scales, num_dets), dtype=np.float32) - - # if there is no gt bboxes in this image, then all det bboxes - # within area range are false positives - if gt_bboxes.shape[0] == 0: - if area_ranges == [(None, None)]: - fp[...] = 1 - else: - det_areas = ( - det_bboxes[:, 2] - det_bboxes[:, 0] + extra_length) * ( - det_bboxes[:, 3] - det_bboxes[:, 1] + extra_length) - for i, (min_area, max_area) in enumerate(area_ranges): - fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1 - return tp, fp, det_bboxes - - if gt_bboxes_group_of is not None and use_group_of: - # if handle group-of boxes, divided gt boxes into two parts: - # non-group-of and group-of.Then calculate ious and ioas through - # non-group-of group-of gts respectively. This only used in - # OpenImages evaluation. - assert gt_bboxes_group_of.shape[0] == gt_bboxes.shape[0] - non_group_gt_bboxes = gt_bboxes[~gt_bboxes_group_of] - group_gt_bboxes = gt_bboxes[gt_bboxes_group_of] - num_gts_group = group_gt_bboxes.shape[0] - ious = bbox_overlaps(det_bboxes, non_group_gt_bboxes) - ioas = bbox_overlaps(det_bboxes, group_gt_bboxes, mode='iof') - else: - # if not consider group-of boxes, only calculate ious through gt boxes - ious = bbox_overlaps( - det_bboxes, gt_bboxes, use_legacy_coordinate=use_legacy_coordinate) - ioas = None - - if ious.shape[1] > 0: - # for each det, the max iou with all gts - ious_max = ious.max(axis=1) - # for each det, which gt overlaps most with it - ious_argmax = ious.argmax(axis=1) - # sort all dets in descending order by scores - sort_inds = np.argsort(-det_bboxes[:, -1]) - for k, (min_area, max_area) in enumerate(area_ranges): - gt_covered = np.zeros(num_gts, dtype=bool) - # if no area range is specified, gt_area_ignore is all False - if min_area is None: - gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool) - else: - gt_areas = ( - gt_bboxes[:, 2] - gt_bboxes[:, 0] + extra_length) * ( - gt_bboxes[:, 3] - gt_bboxes[:, 1] + extra_length) - gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area) - for i in sort_inds: - if ious_max[i] >= iou_thr: - matched_gt = ious_argmax[i] - if not (gt_ignore_inds[matched_gt] - or gt_area_ignore[matched_gt]): - if not gt_covered[matched_gt]: - gt_covered[matched_gt] = True - tp[k, i] = 1 - else: - fp[k, i] = 1 - # otherwise ignore this detected bbox, tp = 0, fp = 0 - elif min_area is None: - fp[k, i] = 1 - else: - bbox = det_bboxes[i, :4] - area = (bbox[2] - bbox[0] + extra_length) * ( - bbox[3] - bbox[1] + extra_length) - if area >= min_area and area < max_area: - fp[k, i] = 1 - else: - # if there is no no-group-of gt bboxes in this image, - # then all det bboxes within area range are false positives. - # Only used in OpenImages evaluation. - if area_ranges == [(None, None)]: - fp[...] = 1 - else: - det_areas = ( - det_bboxes[:, 2] - det_bboxes[:, 0] + extra_length) * ( - det_bboxes[:, 3] - det_bboxes[:, 1] + extra_length) - for i, (min_area, max_area) in enumerate(area_ranges): - fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1 - - if ioas is None or ioas.shape[1] <= 0: - return tp, fp, det_bboxes - else: - # The evaluation of group-of TP and FP are done in two stages: - # 1. All detections are first matched to non group-of boxes; true - # positives are determined. - # 2. Detections that are determined as false positives are matched - # against group-of boxes and calculated group-of TP and FP. - # Only used in OpenImages evaluation. - det_bboxes_group = np.zeros( - (num_scales, ioas.shape[1], det_bboxes.shape[1]), dtype=float) - match_group_of = np.zeros((num_scales, num_dets), dtype=bool) - tp_group = np.zeros((num_scales, num_gts_group), dtype=np.float32) - ioas_max = ioas.max(axis=1) - # for each det, which gt overlaps most with it - ioas_argmax = ioas.argmax(axis=1) - # sort all dets in descending order by scores - sort_inds = np.argsort(-det_bboxes[:, -1]) - for k, (min_area, max_area) in enumerate(area_ranges): - box_is_covered = tp[k] - # if no area range is specified, gt_area_ignore is all False - if min_area is None: - gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool) - else: - gt_areas = (gt_bboxes[:, 2] - gt_bboxes[:, 0]) * ( - gt_bboxes[:, 3] - gt_bboxes[:, 1]) - gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area) - for i in sort_inds: - matched_gt = ioas_argmax[i] - if not box_is_covered[i]: - if ioas_max[i] >= ioa_thr: - if not (gt_ignore_inds[matched_gt] - or gt_area_ignore[matched_gt]): - if not tp_group[k, matched_gt]: - tp_group[k, matched_gt] = 1 - match_group_of[k, i] = True - else: - match_group_of[k, i] = True - - if det_bboxes_group[k, matched_gt, -1] < \ - det_bboxes[i, -1]: - det_bboxes_group[k, matched_gt] = \ - det_bboxes[i] - - fp_group = (tp_group <= 0).astype(float) - tps = [] - fps = [] - # concatenate tp, fp, and det-boxes which not matched group of - # gt boxes and tp_group, fp_group, and det_bboxes_group which - # matched group of boxes respectively. - for i in range(num_scales): - tps.append( - np.concatenate((tp[i][~match_group_of[i]], tp_group[i]))) - fps.append( - np.concatenate((fp[i][~match_group_of[i]], fp_group[i]))) - det_bboxes = np.concatenate( - (det_bboxes[~match_group_of[i]], det_bboxes_group[i])) - - tp = np.vstack(tps) - fp = np.vstack(fps) - return tp, fp, det_bboxes - - -def get_cls_results(det_results, annotations, class_id): - """Get det results and gt information of a certain class. - - Args: - det_results (list[list]): Same as `eval_map()`. - annotations (list[dict]): Same as `eval_map()`. - class_id (int): ID of a specific class. - - Returns: - tuple[list[np.ndarray]]: detected bboxes, gt bboxes, ignored gt bboxes - """ - cls_dets = [img_res[class_id] for img_res in det_results] - cls_gts = [] - cls_gts_ignore = [] - for ann in annotations: - gt_inds = ann['labels'] == class_id - cls_gts.append(ann['bboxes'][gt_inds, :]) - - if ann.get('labels_ignore', None) is not None: - ignore_inds = ann['labels_ignore'] == class_id - cls_gts_ignore.append(ann['bboxes_ignore'][ignore_inds, :]) - else: - cls_gts_ignore.append(np.empty((0, 4), dtype=np.float32)) - - return cls_dets, cls_gts, cls_gts_ignore - - -def get_cls_group_ofs(annotations, class_id): - """Get `gt_group_of` of a certain class, which is used in Open Images. - - Args: - annotations (list[dict]): Same as `eval_map()`. - class_id (int): ID of a specific class. - - Returns: - list[np.ndarray]: `gt_group_of` of a certain class. - """ - gt_group_ofs = [] - for ann in annotations: - gt_inds = ann['labels'] == class_id - if ann.get('gt_is_group_ofs', None) is not None: - gt_group_ofs.append(ann['gt_is_group_ofs'][gt_inds]) - else: - gt_group_ofs.append(np.empty((0, 1), dtype=np.bool)) - - return gt_group_ofs - - -def eval_map(det_results, - annotations, - scale_ranges=None, - iou_thr=0.5, - ioa_thr=None, - dataset=None, - logger=None, - tpfp_fn=None, - nproc=4, - use_legacy_coordinate=False, - use_group_of=False): - """Evaluate mAP of a dataset. - - Args: - det_results (list[list]): [[cls1_det, cls2_det, ...], ...]. - The outer list indicates images, and the inner list indicates - per-class detected bboxes. - annotations (list[dict]): Ground truth annotations where each item of - the list indicates an image. Keys of annotations are: - - - `bboxes`: numpy array of shape (n, 4) - - `labels`: numpy array of shape (n, ) - - `bboxes_ignore` (optional): numpy array of shape (k, 4) - - `labels_ignore` (optional): numpy array of shape (k, ) - scale_ranges (list[tuple] | None): Range of scales to be evaluated, - in the format [(min1, max1), (min2, max2), ...]. A range of - (32, 64) means the area range between (32**2, 64**2). - Default: None. - iou_thr (float): IoU threshold to be considered as matched. - Default: 0.5. - ioa_thr (float | None): IoA threshold to be considered as matched, - which only used in OpenImages evaluation. Default: None. - dataset (list[str] | str | None): Dataset name or dataset classes, - there are minor differences in metrics for different datasets, e.g. - "voc07", "imagenet_det", etc. Default: None. - logger (logging.Logger | str | None): The way to print the mAP - summary. See `mmcv.utils.print_log()` for details. Default: None. - tpfp_fn (callable | None): The function used to determine true/ - false positives. If None, :func:`tpfp_default` is used as default - unless dataset is 'det' or 'vid' (:func:`tpfp_imagenet` in this - case). If it is given as a function, then this function is used - to evaluate tp & fp. Default None. - nproc (int): Processes used for computing TP and FP. - Default: 4. - use_legacy_coordinate (bool): Whether to use coordinate system in - mmdet v1.x. which means width, height should be - calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively. - Default: False. - use_group_of (bool): Whether to use group of when calculate TP and FP, - which only used in OpenImages evaluation. Default: False. - - Returns: - tuple: (mAP, [dict, dict, ...]) - """ - assert len(det_results) == len(annotations) - if not use_legacy_coordinate: - extra_length = 0. - else: - extra_length = 1. - - num_imgs = len(det_results) - num_scales = len(scale_ranges) if scale_ranges is not None else 1 - num_classes = len(det_results[0]) # positive class num - area_ranges = ([(rg[0]**2, rg[1]**2) for rg in scale_ranges] - if scale_ranges is not None else None) - - # There is no need to use multi processes to process - # when num_imgs = 1 . - if num_imgs > 1: - assert nproc > 0, 'nproc must be at least one.' - nproc = min(nproc, num_imgs) - pool = Pool(nproc) - - eval_results = [] - for i in range(num_classes): - # get gt and det bboxes of this class - cls_dets, cls_gts, cls_gts_ignore = get_cls_results( - det_results, annotations, i) - # choose proper function according to datasets to compute tp and fp - if tpfp_fn is None: - if dataset in ['det', 'vid']: - tpfp_fn = tpfp_imagenet - elif dataset in ['oid_challenge', 'oid_v6'] \ - or use_group_of is True: - tpfp_fn = tpfp_openimages - else: - tpfp_fn = tpfp_default - if not callable(tpfp_fn): - raise ValueError( - f'tpfp_fn has to be a function or None, but got {tpfp_fn}') - - if num_imgs > 1: - # compute tp and fp for each image with multiple processes - args = [] - if use_group_of: - # used in Open Images Dataset evaluation - gt_group_ofs = get_cls_group_ofs(annotations, i) - args.append(gt_group_ofs) - args.append([use_group_of for _ in range(num_imgs)]) - if ioa_thr is not None: - args.append([ioa_thr for _ in range(num_imgs)]) - - tpfp = pool.starmap( - tpfp_fn, - zip(cls_dets, cls_gts, cls_gts_ignore, - [iou_thr for _ in range(num_imgs)], - [area_ranges for _ in range(num_imgs)], - [use_legacy_coordinate for _ in range(num_imgs)], *args)) - else: - tpfp = tpfp_fn( - cls_dets[0], - cls_gts[0], - cls_gts_ignore[0], - iou_thr, - area_ranges, - use_legacy_coordinate, - gt_bboxes_group_of=(get_cls_group_ofs(annotations, i)[0] - if use_group_of else None), - use_group_of=use_group_of, - ioa_thr=ioa_thr) - tpfp = [tpfp] - - if use_group_of: - tp, fp, cls_dets = tuple(zip(*tpfp)) - else: - tp, fp = tuple(zip(*tpfp)) - # calculate gt number of each scale - # ignored gts or gts beyond the specific scale are not counted - num_gts = np.zeros(num_scales, dtype=int) - for j, bbox in enumerate(cls_gts): - if area_ranges is None: - num_gts[0] += bbox.shape[0] - else: - gt_areas = (bbox[:, 2] - bbox[:, 0] + extra_length) * ( - bbox[:, 3] - bbox[:, 1] + extra_length) - for k, (min_area, max_area) in enumerate(area_ranges): - num_gts[k] += np.sum((gt_areas >= min_area) - & (gt_areas < max_area)) - # sort all det bboxes by score, also sort tp and fp - cls_dets = np.vstack(cls_dets) - num_dets = cls_dets.shape[0] - sort_inds = np.argsort(-cls_dets[:, -1]) - tp = np.hstack(tp)[:, sort_inds] - fp = np.hstack(fp)[:, sort_inds] - # calculate recall and precision with tp and fp - tp = np.cumsum(tp, axis=1) - fp = np.cumsum(fp, axis=1) - eps = np.finfo(np.float32).eps - recalls = tp / np.maximum(num_gts[:, np.newaxis], eps) - precisions = tp / np.maximum((tp + fp), eps) - # calculate AP - if scale_ranges is None: - recalls = recalls[0, :] - precisions = precisions[0, :] - num_gts = num_gts.item() - mode = 'area' if dataset != 'voc07' else '11points' - ap = average_precision(recalls, precisions, mode) - eval_results.append({ - 'num_gts': num_gts, - 'num_dets': num_dets, - 'recall': recalls, - 'precision': precisions, - 'ap': ap - }) - - if num_imgs > 1: - pool.close() - - if scale_ranges is not None: - # shape (num_classes, num_scales) - all_ap = np.vstack([cls_result['ap'] for cls_result in eval_results]) - all_num_gts = np.vstack( - [cls_result['num_gts'] for cls_result in eval_results]) - mean_ap = [] - for i in range(num_scales): - if np.any(all_num_gts[:, i] > 0): - mean_ap.append(all_ap[all_num_gts[:, i] > 0, i].mean()) - else: - mean_ap.append(0.0) - else: - aps = [] - for cls_result in eval_results: - if cls_result['num_gts'] > 0: - aps.append(cls_result['ap']) - mean_ap = np.array(aps).mean().item() if aps else 0.0 - - print_map_summary( - mean_ap, eval_results, dataset, area_ranges, logger=logger) - - return mean_ap, eval_results - - -def print_map_summary(mean_ap, - results, - dataset=None, - scale_ranges=None, - logger=None): - """Print mAP and results of each class. - - A table will be printed to show the gts/dets/recall/AP of each class and - the mAP. - - Args: - mean_ap (float): Calculated from `eval_map()`. - results (list[dict]): Calculated from `eval_map()`. - dataset (list[str] | str | None): Dataset name or dataset classes. - scale_ranges (list[tuple] | None): Range of scales to be evaluated. - logger (logging.Logger | str | None): The way to print the mAP - summary. See `mmcv.utils.print_log()` for details. Default: None. - """ - - if logger == 'silent': - return - - if isinstance(results[0]['ap'], np.ndarray): - num_scales = len(results[0]['ap']) - else: - num_scales = 1 - - if scale_ranges is not None: - assert len(scale_ranges) == num_scales - - num_classes = len(results) - - recalls = np.zeros((num_scales, num_classes), dtype=np.float32) - aps = np.zeros((num_scales, num_classes), dtype=np.float32) - num_gts = np.zeros((num_scales, num_classes), dtype=int) - for i, cls_result in enumerate(results): - if cls_result['recall'].size > 0: - recalls[:, i] = np.array(cls_result['recall'], ndmin=2)[:, -1] - aps[:, i] = cls_result['ap'] - num_gts[:, i] = cls_result['num_gts'] - - if dataset is None: - label_names = [str(i) for i in range(num_classes)] - elif mmcv.is_str(dataset): - label_names = get_classes(dataset) - else: - label_names = dataset - - if not isinstance(mean_ap, list): - mean_ap = [mean_ap] - - header = ['class', 'gts', 'dets', 'recall', 'ap'] - for i in range(num_scales): - if scale_ranges is not None: - print_log(f'Scale range {scale_ranges[i]}', logger=logger) - table_data = [header] - for j in range(num_classes): - row_data = [ - label_names[j], num_gts[i, j], results[j]['num_dets'], - f'{recalls[i, j]:.3f}', f'{aps[i, j]:.3f}' - ] - table_data.append(row_data) - table_data.append(['mAP', '', '', '', f'{mean_ap[i]:.3f}']) - table = AsciiTable(table_data) - table.inner_footing_row_border = True - print_log('\n' + table.table, logger=logger) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/panoptic_utils.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/panoptic_utils.py deleted file mode 100644 index 10c9ad934..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/panoptic_utils.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# A custom value to distinguish instance ID and category ID; need to -# be greater than the number of categories. -# For a pixel in the panoptic result map: -# pan_id = ins_id * INSTANCE_OFFSET + cat_id -INSTANCE_OFFSET = 1000 diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/recall.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/recall.py deleted file mode 100644 index 82b3c909b..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/evaluation/recall.py +++ /dev/null @@ -1,197 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import numpy as np -from mmcv.utils import print_log -from terminaltables import AsciiTable - -from .bbox_overlaps import bbox_overlaps - - -def _recalls(all_ious, proposal_nums, thrs): - - img_num = all_ious.shape[0] - total_gt_num = sum([ious.shape[0] for ious in all_ious]) - - _ious = np.zeros((proposal_nums.size, total_gt_num), dtype=np.float32) - for k, proposal_num in enumerate(proposal_nums): - tmp_ious = np.zeros(0) - for i in range(img_num): - ious = all_ious[i][:, :proposal_num].copy() - gt_ious = np.zeros((ious.shape[0])) - if ious.size == 0: - tmp_ious = np.hstack((tmp_ious, gt_ious)) - continue - for j in range(ious.shape[0]): - gt_max_overlaps = ious.argmax(axis=1) - max_ious = ious[np.arange(0, ious.shape[0]), gt_max_overlaps] - gt_idx = max_ious.argmax() - gt_ious[j] = max_ious[gt_idx] - box_idx = gt_max_overlaps[gt_idx] - ious[gt_idx, :] = -1 - ious[:, box_idx] = -1 - tmp_ious = np.hstack((tmp_ious, gt_ious)) - _ious[k, :] = tmp_ious - - _ious = np.fliplr(np.sort(_ious, axis=1)) - recalls = np.zeros((proposal_nums.size, thrs.size)) - for i, thr in enumerate(thrs): - recalls[:, i] = (_ious >= thr).sum(axis=1) / float(total_gt_num) - - return recalls - - -def set_recall_param(proposal_nums, iou_thrs): - """Check proposal_nums and iou_thrs and set correct format.""" - if isinstance(proposal_nums, Sequence): - _proposal_nums = np.array(proposal_nums) - elif isinstance(proposal_nums, int): - _proposal_nums = np.array([proposal_nums]) - else: - _proposal_nums = proposal_nums - - if iou_thrs is None: - _iou_thrs = np.array([0.5]) - elif isinstance(iou_thrs, Sequence): - _iou_thrs = np.array(iou_thrs) - elif isinstance(iou_thrs, float): - _iou_thrs = np.array([iou_thrs]) - else: - _iou_thrs = iou_thrs - - return _proposal_nums, _iou_thrs - - -def eval_recalls(gts, - proposals, - proposal_nums=None, - iou_thrs=0.5, - logger=None, - use_legacy_coordinate=False): - """Calculate recalls. - - Args: - gts (list[ndarray]): a list of arrays of shape (n, 4) - proposals (list[ndarray]): a list of arrays of shape (k, 4) or (k, 5) - proposal_nums (int | Sequence[int]): Top N proposals to be evaluated. - iou_thrs (float | Sequence[float]): IoU thresholds. Default: 0.5. - logger (logging.Logger | str | None): The way to print the recall - summary. See `mmcv.utils.print_log()` for details. Default: None. - use_legacy_coordinate (bool): Whether use coordinate system - in mmdet v1.x. "1" was added to both height and width - which means w, h should be - computed as 'x2 - x1 + 1` and 'y2 - y1 + 1'. Default: False. - - - Returns: - ndarray: recalls of different ious and proposal nums - """ - - img_num = len(gts) - assert img_num == len(proposals) - proposal_nums, iou_thrs = set_recall_param(proposal_nums, iou_thrs) - all_ious = [] - for i in range(img_num): - if proposals[i].ndim == 2 and proposals[i].shape[1] == 5: - scores = proposals[i][:, 4] - sort_idx = np.argsort(scores)[::-1] - img_proposal = proposals[i][sort_idx, :] - else: - img_proposal = proposals[i] - prop_num = min(img_proposal.shape[0], proposal_nums[-1]) - if gts[i] is None or gts[i].shape[0] == 0: - ious = np.zeros((0, img_proposal.shape[0]), dtype=np.float32) - else: - ious = bbox_overlaps( - gts[i], - img_proposal[:prop_num, :4], - use_legacy_coordinate=use_legacy_coordinate) - all_ious.append(ious) - all_ious = np.array(all_ious) - recalls = _recalls(all_ious, proposal_nums, iou_thrs) - - print_recall_summary(recalls, proposal_nums, iou_thrs, logger=logger) - return recalls - - -def print_recall_summary(recalls, - proposal_nums, - iou_thrs, - row_idxs=None, - col_idxs=None, - logger=None): - """Print recalls in a table. - - Args: - recalls (ndarray): calculated from `bbox_recalls` - proposal_nums (ndarray or list): top N proposals - iou_thrs (ndarray or list): iou thresholds - row_idxs (ndarray): which rows(proposal nums) to print - col_idxs (ndarray): which cols(iou thresholds) to print - logger (logging.Logger | str | None): The way to print the recall - summary. See `mmcv.utils.print_log()` for details. Default: None. - """ - proposal_nums = np.array(proposal_nums, dtype=np.int32) - iou_thrs = np.array(iou_thrs) - if row_idxs is None: - row_idxs = np.arange(proposal_nums.size) - if col_idxs is None: - col_idxs = np.arange(iou_thrs.size) - row_header = [''] + iou_thrs[col_idxs].tolist() - table_data = [row_header] - for i, num in enumerate(proposal_nums[row_idxs]): - row = [f'{val:.3f}' for val in recalls[row_idxs[i], col_idxs].tolist()] - row.insert(0, num) - table_data.append(row) - table = AsciiTable(table_data) - print_log('\n' + table.table, logger=logger) - - -def plot_num_recall(recalls, proposal_nums): - """Plot Proposal_num-Recalls curve. - - Args: - recalls(ndarray or list): shape (k,) - proposal_nums(ndarray or list): same shape as `recalls` - """ - if isinstance(proposal_nums, np.ndarray): - _proposal_nums = proposal_nums.tolist() - else: - _proposal_nums = proposal_nums - if isinstance(recalls, np.ndarray): - _recalls = recalls.tolist() - else: - _recalls = recalls - - import matplotlib.pyplot as plt - f = plt.figure() - plt.plot([0] + _proposal_nums, [0] + _recalls) - plt.xlabel('Proposal num') - plt.ylabel('Recall') - plt.axis([0, proposal_nums.max(), 0, 1]) - f.show() - - -def plot_iou_recall(recalls, iou_thrs): - """Plot IoU-Recalls curve. - - Args: - recalls(ndarray or list): shape (k,) - iou_thrs(ndarray or list): same shape as `recalls` - """ - if isinstance(iou_thrs, np.ndarray): - _iou_thrs = iou_thrs.tolist() - else: - _iou_thrs = iou_thrs - if isinstance(recalls, np.ndarray): - _recalls = recalls.tolist() - else: - _recalls = recalls - - import matplotlib.pyplot as plt - f = plt.figure() - plt.plot(_iou_thrs + [1.0], _recalls + [0.]) - plt.xlabel('IoU') - plt.ylabel('Recall') - plt.axis([iou_thrs.min(), 1, 0, 1]) - f.show() diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/export/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/export/__init__.py deleted file mode 100644 index a8179c936..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/export/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .onnx_helper import (add_dummy_nms_for_onnx, dynamic_clip_for_onnx, - get_k_for_topk) -from .pytorch2onnx import (build_model_from_cfg, - generate_inputs_and_wrap_model, - preprocess_example_input) - -__all__ = [ - 'build_model_from_cfg', 'generate_inputs_and_wrap_model', - 'preprocess_example_input', 'get_k_for_topk', 'add_dummy_nms_for_onnx', - 'dynamic_clip_for_onnx' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/export/model_wrappers.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/export/model_wrappers.py deleted file mode 100644 index 2f62bb031..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/export/model_wrappers.py +++ /dev/null @@ -1,183 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -import numpy as np -import torch - -from mmdet.core import bbox2result -from mmdet.models import BaseDetector - - -class DeployBaseDetector(BaseDetector): - """DeployBaseDetector.""" - - def __init__(self, class_names, device_id): - super(DeployBaseDetector, self).__init__() - self.CLASSES = class_names - self.device_id = device_id - - def simple_test(self, img, img_metas, **kwargs): - raise NotImplementedError('This method is not implemented.') - - def aug_test(self, imgs, img_metas, **kwargs): - raise NotImplementedError('This method is not implemented.') - - def extract_feat(self, imgs): - raise NotImplementedError('This method is not implemented.') - - def forward_train(self, imgs, img_metas, **kwargs): - raise NotImplementedError('This method is not implemented.') - - def val_step(self, data, optimizer): - raise NotImplementedError('This method is not implemented.') - - def train_step(self, data, optimizer): - raise NotImplementedError('This method is not implemented.') - - def forward_test(self, *, img, img_metas, **kwargs): - raise NotImplementedError('This method is not implemented.') - - def async_simple_test(self, img, img_metas, **kwargs): - raise NotImplementedError('This method is not implemented.') - - def forward(self, img, img_metas, return_loss=True, **kwargs): - outputs = self.forward_test(img, img_metas, **kwargs) - batch_dets, batch_labels = outputs[:2] - batch_masks = outputs[2] if len(outputs) == 3 else None - batch_size = img[0].shape[0] - img_metas = img_metas[0] - results = [] - rescale = kwargs.get('rescale', True) - for i in range(batch_size): - dets, labels = batch_dets[i], batch_labels[i] - if rescale: - scale_factor = img_metas[i]['scale_factor'] - - if isinstance(scale_factor, (list, tuple, np.ndarray)): - assert len(scale_factor) == 4 - scale_factor = np.array(scale_factor)[None, :] # [1,4] - dets[:, :4] /= scale_factor - - if 'border' in img_metas[i]: - # offset pixel of the top-left corners between original image - # and padded/enlarged image, 'border' is used when exporting - # CornerNet and CentripetalNet to onnx - x_off = img_metas[i]['border'][2] - y_off = img_metas[i]['border'][0] - dets[:, [0, 2]] -= x_off - dets[:, [1, 3]] -= y_off - dets[:, :4] *= (dets[:, :4] > 0).astype(dets.dtype) - - dets_results = bbox2result(dets, labels, len(self.CLASSES)) - - if batch_masks is not None: - masks = batch_masks[i] - img_h, img_w = img_metas[i]['img_shape'][:2] - ori_h, ori_w = img_metas[i]['ori_shape'][:2] - masks = masks[:, :img_h, :img_w] - if rescale: - masks = masks.astype(np.float32) - masks = torch.from_numpy(masks) - masks = torch.nn.functional.interpolate( - masks.unsqueeze(0), size=(ori_h, ori_w)) - masks = masks.squeeze(0).detach().numpy() - if masks.dtype != np.bool: - masks = masks >= 0.5 - segms_results = [[] for _ in range(len(self.CLASSES))] - for j in range(len(dets)): - segms_results[labels[j]].append(masks[j]) - results.append((dets_results, segms_results)) - else: - results.append(dets_results) - return results - - -class ONNXRuntimeDetector(DeployBaseDetector): - """Wrapper for detector's inference with ONNXRuntime.""" - - def __init__(self, onnx_file, class_names, device_id): - super(ONNXRuntimeDetector, self).__init__(class_names, device_id) - import onnxruntime as ort - - # get the custom op path - ort_custom_op_path = '' - try: - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - except (ImportError, ModuleNotFoundError): - warnings.warn('If input model has custom op from mmcv, \ - you may have to build mmcv with ONNXRuntime from source.') - session_options = ort.SessionOptions() - # register custom op for onnxruntime - if osp.exists(ort_custom_op_path): - session_options.register_custom_ops_library(ort_custom_op_path) - sess = ort.InferenceSession(onnx_file, session_options) - providers = ['CPUExecutionProvider'] - options = [{}] - is_cuda_available = ort.get_device() == 'GPU' - if is_cuda_available: - providers.insert(0, 'CUDAExecutionProvider') - options.insert(0, {'device_id': device_id}) - - sess.set_providers(providers, options) - - self.sess = sess - self.io_binding = sess.io_binding() - self.output_names = [_.name for _ in sess.get_outputs()] - self.is_cuda_available = is_cuda_available - - def forward_test(self, imgs, img_metas, **kwargs): - input_data = imgs[0] - # set io binding for inputs/outputs - device_type = 'cuda' if self.is_cuda_available else 'cpu' - if not self.is_cuda_available: - input_data = input_data.cpu() - self.io_binding.bind_input( - name='input', - device_type=device_type, - device_id=self.device_id, - element_type=np.float32, - shape=input_data.shape, - buffer_ptr=input_data.data_ptr()) - - for name in self.output_names: - self.io_binding.bind_output(name) - # run session to get outputs - self.sess.run_with_iobinding(self.io_binding) - ort_outputs = self.io_binding.copy_outputs_to_cpu() - return ort_outputs - - -class TensorRTDetector(DeployBaseDetector): - """Wrapper for detector's inference with TensorRT.""" - - def __init__(self, engine_file, class_names, device_id, output_names=None): - super(TensorRTDetector, self).__init__(class_names, device_id) - warnings.warn('`output_names` is deprecated and will be removed in ' - 'future releases.') - from mmcv.tensorrt import TRTWraper, load_tensorrt_plugin - try: - load_tensorrt_plugin() - except (ImportError, ModuleNotFoundError): - warnings.warn('If input model has custom op from mmcv, \ - you may have to build mmcv with TensorRT from source.') - - output_names = ['dets', 'labels'] - model = TRTWraper(engine_file, ['input'], output_names) - with_masks = False - # if TensorRT has totally 4 inputs/outputs, then - # the detector should have `mask` output. - if len(model.engine) == 4: - model.output_names = output_names + ['masks'] - with_masks = True - self.model = model - self.with_masks = with_masks - - def forward_test(self, imgs, img_metas, **kwargs): - input_data = imgs[0].contiguous() - with torch.cuda.device(self.device_id), torch.no_grad(): - outputs = self.model({'input': input_data}) - outputs = [outputs[name] for name in self.model.output_names] - outputs = [out.detach().cpu().numpy() for out in outputs] - return outputs diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/export/onnx_helper.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/export/onnx_helper.py deleted file mode 100644 index 9f6b9a012..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/export/onnx_helper.py +++ /dev/null @@ -1,223 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -import torch - - -def dynamic_clip_for_onnx(x1, y1, x2, y2, max_shape): - """Clip boxes dynamically for onnx. - - Since torch.clamp cannot have dynamic `min` and `max`, we scale the - boxes by 1/max_shape and clamp in the range [0, 1]. - - Args: - x1 (Tensor): The x1 for bounding boxes. - y1 (Tensor): The y1 for bounding boxes. - x2 (Tensor): The x2 for bounding boxes. - y2 (Tensor): The y2 for bounding boxes. - max_shape (Tensor or torch.Size): The (H,W) of original image. - Returns: - tuple(Tensor): The clipped x1, y1, x2, y2. - """ - assert isinstance( - max_shape, - torch.Tensor), '`max_shape` should be tensor of (h,w) for onnx' - - # scale by 1/max_shape - x1 = x1 / max_shape[1] - y1 = y1 / max_shape[0] - x2 = x2 / max_shape[1] - y2 = y2 / max_shape[0] - - # clamp [0, 1] - x1 = torch.clamp(x1, 0, 1) - y1 = torch.clamp(y1, 0, 1) - x2 = torch.clamp(x2, 0, 1) - y2 = torch.clamp(y2, 0, 1) - - # scale back - x1 = x1 * max_shape[1] - y1 = y1 * max_shape[0] - x2 = x2 * max_shape[1] - y2 = y2 * max_shape[0] - return x1, y1, x2, y2 - - -def get_k_for_topk(k, size): - """Get k of TopK for onnx exporting. - - The K of TopK in TensorRT should not be a Tensor, while in ONNX Runtime - it could be a Tensor.Due to dynamic shape feature, we have to decide - whether to do TopK and what K it should be while exporting to ONNX. - If returned K is less than zero, it means we do not have to do - TopK operation. - - Args: - k (int or Tensor): The set k value for nms from config file. - size (Tensor or torch.Size): The number of elements of \ - TopK's input tensor - Returns: - tuple: (int or Tensor): The final K for TopK. - """ - ret_k = -1 - if k <= 0 or size <= 0: - return ret_k - if torch.onnx.is_in_onnx_export(): - is_trt_backend = os.environ.get('ONNX_BACKEND') == 'MMCVTensorRT' - if is_trt_backend: - # TensorRT does not support dynamic K with TopK op - if 0 < k < size: - ret_k = k - else: - # Always keep topk op for dynamic input in onnx for ONNX Runtime - ret_k = torch.where(k < size, k, size) - elif k < size: - ret_k = k - else: - # ret_k is -1 - pass - return ret_k - - -def add_dummy_nms_for_onnx(boxes, - scores, - max_output_boxes_per_class=1000, - iou_threshold=0.5, - score_threshold=0.05, - pre_top_k=-1, - after_top_k=-1, - labels=None): - """Create a dummy onnx::NonMaxSuppression op while exporting to ONNX. - - This function helps exporting to onnx with batch and multiclass NMS op. - It only supports class-agnostic detection results. That is, the scores - is of shape (N, num_bboxes, num_classes) and the boxes is of shape - (N, num_boxes, 4). - - Args: - boxes (Tensor): The bounding boxes of shape [N, num_boxes, 4] - scores (Tensor): The detection scores of shape - [N, num_boxes, num_classes] - max_output_boxes_per_class (int): Maximum number of output - boxes per class of nms. Defaults to 1000. - iou_threshold (float): IOU threshold of nms. Defaults to 0.5 - score_threshold (float): score threshold of nms. - Defaults to 0.05. - pre_top_k (bool): Number of top K boxes to keep before nms. - Defaults to -1. - after_top_k (int): Number of top K boxes to keep after nms. - Defaults to -1. - labels (Tensor, optional): It not None, explicit labels would be used. - Otherwise, labels would be automatically generated using - num_classed. Defaults to None. - - Returns: - tuple[Tensor, Tensor]: dets of shape [N, num_det, 5] - and class labels of shape [N, num_det]. - """ - max_output_boxes_per_class = torch.LongTensor([max_output_boxes_per_class]) - iou_threshold = torch.tensor([iou_threshold], dtype=torch.float32) - score_threshold = torch.tensor([score_threshold], dtype=torch.float32) - batch_size = scores.shape[0] - num_class = scores.shape[2] - - nms_pre = torch.tensor(pre_top_k, device=scores.device, dtype=torch.long) - nms_pre = get_k_for_topk(nms_pre, boxes.shape[1]) - - if nms_pre > 0: - max_scores, _ = scores.max(-1) - _, topk_inds = max_scores.topk(nms_pre) - batch_inds = torch.arange(batch_size).view( - -1, 1).expand_as(topk_inds).long() - # Avoid onnx2tensorrt issue in https://github.com/NVIDIA/TensorRT/issues/1134 # noqa: E501 - transformed_inds = boxes.shape[1] * batch_inds + topk_inds - boxes = boxes.reshape(-1, 4)[transformed_inds, :].reshape( - batch_size, -1, 4) - scores = scores.reshape(-1, num_class)[transformed_inds, :].reshape( - batch_size, -1, num_class) - if labels is not None: - labels = labels.reshape(-1, 1)[transformed_inds].reshape( - batch_size, -1) - - scores = scores.permute(0, 2, 1) - num_box = boxes.shape[1] - # turn off tracing to create a dummy output of nms - state = torch._C._get_tracing_state() - # dummy indices of nms's output - num_fake_det = 2 - batch_inds = torch.randint(batch_size, (num_fake_det, 1)) - cls_inds = torch.randint(num_class, (num_fake_det, 1)) - box_inds = torch.randint(num_box, (num_fake_det, 1)) - indices = torch.cat([batch_inds, cls_inds, box_inds], dim=1) - output = indices - setattr(DummyONNXNMSop, 'output', output) - - # open tracing - torch._C._set_tracing_state(state) - selected_indices = DummyONNXNMSop.apply(boxes, scores, - max_output_boxes_per_class, - iou_threshold, score_threshold) - - batch_inds, cls_inds = selected_indices[:, 0], selected_indices[:, 1] - box_inds = selected_indices[:, 2] - if labels is None: - labels = torch.arange(num_class, dtype=torch.long).to(scores.device) - labels = labels.view(1, num_class, 1).expand_as(scores) - scores = scores.reshape(-1, 1) - boxes = boxes.reshape(batch_size, -1).repeat(1, num_class).reshape(-1, 4) - pos_inds = (num_class * batch_inds + cls_inds) * num_box + box_inds - mask = scores.new_zeros(scores.shape) - # Avoid onnx2tensorrt issue in https://github.com/NVIDIA/TensorRT/issues/1134 # noqa: E501 - # PyTorch style code: mask[batch_inds, box_inds] += 1 - mask[pos_inds, :] += 1 - scores = scores * mask - boxes = boxes * mask - - scores = scores.reshape(batch_size, -1) - boxes = boxes.reshape(batch_size, -1, 4) - labels = labels.reshape(batch_size, -1) - - nms_after = torch.tensor( - after_top_k, device=scores.device, dtype=torch.long) - nms_after = get_k_for_topk(nms_after, num_box * num_class) - - if nms_after > 0: - _, topk_inds = scores.topk(nms_after) - batch_inds = torch.arange(batch_size).view(-1, 1).expand_as(topk_inds) - # Avoid onnx2tensorrt issue in https://github.com/NVIDIA/TensorRT/issues/1134 # noqa: E501 - transformed_inds = scores.shape[1] * batch_inds + topk_inds - scores = scores.reshape(-1, 1)[transformed_inds, :].reshape( - batch_size, -1) - boxes = boxes.reshape(-1, 4)[transformed_inds, :].reshape( - batch_size, -1, 4) - labels = labels.reshape(-1, 1)[transformed_inds, :].reshape( - batch_size, -1) - - scores = scores.unsqueeze(2) - dets = torch.cat([boxes, scores], dim=2) - return dets, labels - - -class DummyONNXNMSop(torch.autograd.Function): - """DummyONNXNMSop. - - This class is only for creating onnx::NonMaxSuppression. - """ - - @staticmethod - def forward(ctx, boxes, scores, max_output_boxes_per_class, iou_threshold, - score_threshold): - - return DummyONNXNMSop.output - - @staticmethod - def symbolic(g, boxes, scores, max_output_boxes_per_class, iou_threshold, - score_threshold): - return g.op( - 'NonMaxSuppression', - boxes, - scores, - max_output_boxes_per_class, - iou_threshold, - score_threshold, - outputs=1) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/export/pytorch2onnx.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/export/pytorch2onnx.py deleted file mode 100644 index b8261eed9..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/export/pytorch2onnx.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import mmcv -import numpy as np -import torch -from mmcv.runner import load_checkpoint - - -def generate_inputs_and_wrap_model(config_path, - checkpoint_path, - input_config, - cfg_options=None): - """Prepare sample input and wrap model for ONNX export. - - The ONNX export API only accept args, and all inputs should be - torch.Tensor or corresponding types (such as tuple of tensor). - So we should call this function before exporting. This function will: - - 1. generate corresponding inputs which are used to execute the model. - 2. Wrap the model's forward function. - - For example, the MMDet models' forward function has a parameter - ``return_loss:bool``. As we want to set it as False while export API - supports neither bool type or kwargs. So we have to replace the forward - method like ``model.forward = partial(model.forward, return_loss=False)``. - - Args: - config_path (str): the OpenMMLab config for the model we want to - export to ONNX - checkpoint_path (str): Path to the corresponding checkpoint - input_config (dict): the exactly data in this dict depends on the - framework. For MMSeg, we can just declare the input shape, - and generate the dummy data accordingly. However, for MMDet, - we may pass the real img path, or the NMS will return None - as there is no legal bbox. - - Returns: - tuple: (model, tensor_data) wrapped model which can be called by - ``model(*tensor_data)`` and a list of inputs which are used to - execute the model while exporting. - """ - - model = build_model_from_cfg( - config_path, checkpoint_path, cfg_options=cfg_options) - one_img, one_meta = preprocess_example_input(input_config) - tensor_data = [one_img] - model.forward = partial( - model.forward, img_metas=[[one_meta]], return_loss=False) - - # pytorch has some bug in pytorch1.3, we have to fix it - # by replacing these existing op - opset_version = 11 - # put the import within the function thus it will not cause import error - # when not using this function - try: - from mmcv.onnx.symbolic import register_extra_symbolics - except ModuleNotFoundError: - raise NotImplementedError('please update mmcv to version>=v1.0.4') - register_extra_symbolics(opset_version) - - return model, tensor_data - - -def build_model_from_cfg(config_path, checkpoint_path, cfg_options=None): - """Build a model from config and load the given checkpoint. - - Args: - config_path (str): the OpenMMLab config for the model we want to - export to ONNX - checkpoint_path (str): Path to the corresponding checkpoint - - Returns: - torch.nn.Module: the built model - """ - from mmdet.models import build_detector - - cfg = mmcv.Config.fromfile(config_path) - if cfg_options is not None: - cfg.merge_from_dict(cfg_options) - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - # build the model - cfg.model.train_cfg = None - model = build_detector(cfg.model, test_cfg=cfg.get('test_cfg')) - checkpoint = load_checkpoint(model, checkpoint_path, map_location='cpu') - if 'CLASSES' in checkpoint.get('meta', {}): - model.CLASSES = checkpoint['meta']['CLASSES'] - else: - from mmdet.datasets import DATASETS - dataset = DATASETS.get(cfg.data.test['type']) - assert (dataset is not None) - model.CLASSES = dataset.CLASSES - model.cpu().eval() - return model - - -def preprocess_example_input(input_config): - """Prepare an example input image for ``generate_inputs_and_wrap_model``. - - Args: - input_config (dict): customized config describing the example input. - - Returns: - tuple: (one_img, one_meta), tensor of the example input image and \ - meta information for the example input image. - - Examples: - >>> from mmdet.core.export import preprocess_example_input - >>> input_config = { - >>> 'input_shape': (1,3,224,224), - >>> 'input_path': 'demo/demo.jpg', - >>> 'normalize_cfg': { - >>> 'mean': (123.675, 116.28, 103.53), - >>> 'std': (58.395, 57.12, 57.375) - >>> } - >>> } - >>> one_img, one_meta = preprocess_example_input(input_config) - >>> print(one_img.shape) - torch.Size([1, 3, 224, 224]) - >>> print(one_meta) - {'img_shape': (224, 224, 3), - 'ori_shape': (224, 224, 3), - 'pad_shape': (224, 224, 3), - 'filename': '.png', - 'scale_factor': 1.0, - 'flip': False} - """ - input_path = input_config['input_path'] - input_shape = input_config['input_shape'] - one_img = mmcv.imread(input_path) - one_img = mmcv.imresize(one_img, input_shape[2:][::-1]) - show_img = one_img.copy() - if 'normalize_cfg' in input_config.keys(): - normalize_cfg = input_config['normalize_cfg'] - mean = np.array(normalize_cfg['mean'], dtype=np.float32) - std = np.array(normalize_cfg['std'], dtype=np.float32) - to_rgb = normalize_cfg.get('to_rgb', True) - one_img = mmcv.imnormalize(one_img, mean, std, to_rgb=to_rgb) - one_img = one_img.transpose(2, 0, 1) - one_img = torch.from_numpy(one_img).unsqueeze(0).float().requires_grad_( - True) - (_, C, H, W) = input_shape - one_meta = { - 'img_shape': (H, W, C), - 'ori_shape': (H, W, C), - 'pad_shape': (H, W, C), - 'filename': '.png', - 'scale_factor': np.ones(4, dtype=np.float32), - 'flip': False, - 'show_img': show_img, - 'flip_direction': None - } - - return one_img, one_meta diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/__init__.py deleted file mode 100644 index 7b9ac9ff3..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkloss_hook import CheckInvalidLossHook -from .ema import ExpMomentumEMAHook, LinearMomentumEMAHook -from .memory_profiler_hook import MemoryProfilerHook -from .set_epoch_info_hook import SetEpochInfoHook -from .sync_norm_hook import SyncNormHook -from .sync_random_size_hook import SyncRandomSizeHook -from .wandblogger_hook import MMDetWandbHook -from .yolox_lrupdater_hook import YOLOXLrUpdaterHook -from .yolox_mode_switch_hook import YOLOXModeSwitchHook - -__all__ = [ - 'SyncRandomSizeHook', 'YOLOXModeSwitchHook', 'SyncNormHook', - 'ExpMomentumEMAHook', 'LinearMomentumEMAHook', 'YOLOXLrUpdaterHook', - 'CheckInvalidLossHook', 'SetEpochInfoHook', 'MemoryProfilerHook', - 'MMDetWandbHook' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/checkloss_hook.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/checkloss_hook.py deleted file mode 100644 index 754e61bef..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/checkloss_hook.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.runner.hooks import HOOKS, Hook - - -@HOOKS.register_module() -class CheckInvalidLossHook(Hook): - """Check invalid loss hook. - - This hook will regularly check whether the loss is valid - during training. - - Args: - interval (int): Checking interval (every k iterations). - Default: 50. - """ - - def __init__(self, interval=50): - self.interval = interval - - def after_train_iter(self, runner): - if self.every_n_iters(runner, self.interval): - assert torch.isfinite(runner.outputs['loss']), \ - runner.logger.info('loss become infinite or NaN!') diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/ema.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/ema.py deleted file mode 100644 index ff7bfbabe..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/ema.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from mmcv.parallel import is_module_wrapper -from mmcv.runner.hooks import HOOKS, Hook - - -class BaseEMAHook(Hook): - """Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointHook. Note, - the original model parameters are actually saved in ema field after train. - - Args: - momentum (float): The momentum used for updating ema parameter. - Ema's parameter are updated with the formula: - `ema_param = (1-momentum) * ema_param + momentum * cur_param`. - Defaults to 0.0002. - skip_buffers (bool): Whether to skip the model buffers, such as - batchnorm running stats (running_mean, running_var), it does not - perform the ema operation. Default to False. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - resume_from (str, optional): The checkpoint path. Defaults to None. - momentum_fun (func, optional): The function to change momentum - during early iteration (also warmup) to help early training. - It uses `momentum` as a constant. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - skip_buffers=False, - resume_from=None, - momentum_fun=None): - assert 0 < momentum < 1 - self.momentum = momentum - self.skip_buffers = skip_buffers - self.interval = interval - self.checkpoint = resume_from - self.momentum_fun = momentum_fun - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model. - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - if self.skip_buffers: - self.model_parameters = dict(model.named_parameters()) - else: - self.model_parameters = model.state_dict() - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers()) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def get_momentum(self, runner): - return self.momentum_fun(runner.iter) if self.momentum_fun else \ - self.momentum - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - if (runner.iter + 1) % self.interval != 0: - return - momentum = self.get_momentum(runner) - for name, parameter in self.model_parameters.items(): - # exclude num_tracking - if parameter.dtype.is_floating_point: - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_( - parameter.data, alpha=momentum) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) - - -@HOOKS.register_module() -class ExpMomentumEMAHook(BaseEMAHook): - """EMAHook using exponential momentum strategy. - - Args: - total_iter (int): The total number of iterations of EMA momentum. - Defaults to 2000. - """ - - def __init__(self, total_iter=2000, **kwargs): - super(ExpMomentumEMAHook, self).__init__(**kwargs) - self.momentum_fun = lambda x: (1 - self.momentum) * math.exp(-( - 1 + x) / total_iter) + self.momentum - - -@HOOKS.register_module() -class LinearMomentumEMAHook(BaseEMAHook): - """EMAHook using linear momentum strategy. - - Args: - warm_up (int): During first warm_up steps, we may use smaller decay - to update ema parameters more slowly. Defaults to 100. - """ - - def __init__(self, warm_up=100, **kwargs): - super(LinearMomentumEMAHook, self).__init__(**kwargs) - self.momentum_fun = lambda x: min(self.momentum**self.interval, - (1 + x) / (warm_up + x)) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/memory_profiler_hook.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/memory_profiler_hook.py deleted file mode 100644 index a473061b5..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/memory_profiler_hook.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner.hooks import HOOKS, Hook - - -@HOOKS.register_module() -class MemoryProfilerHook(Hook): - """Memory profiler hook recording memory information including virtual - memory, swap memory, and the memory of the current process. - - Args: - interval (int): Checking interval (every k iterations). - Default: 50. - """ - - def __init__(self, interval=50): - try: - from psutil import swap_memory, virtual_memory - self._swap_memory = swap_memory - self._virtual_memory = virtual_memory - except ImportError: - raise ImportError('psutil is not installed, please install it by: ' - 'pip install psutil') - - try: - from memory_profiler import memory_usage - self._memory_usage = memory_usage - except ImportError: - raise ImportError( - 'memory_profiler is not installed, please install it by: ' - 'pip install memory_profiler') - - self.interval = interval - - def after_iter(self, runner): - if self.every_n_iters(runner, self.interval): - # in Byte - virtual_memory = self._virtual_memory() - swap_memory = self._swap_memory() - # in MB - process_memory = self._memory_usage()[0] - factor = 1024 * 1024 - runner.logger.info( - 'Memory information ' - 'available_memory: ' - f'{round(virtual_memory.available / factor)} MB, ' - 'used_memory: ' - f'{round(virtual_memory.used / factor)} MB, ' - f'memory_utilization: {virtual_memory.percent} %, ' - 'available_swap_memory: ' - f'{round((swap_memory.total - swap_memory.used) / factor)}' - ' MB, ' - f'used_swap_memory: {round(swap_memory.used / factor)} MB, ' - f'swap_memory_utilization: {swap_memory.percent} %, ' - 'current_process_memory: ' - f'{round(process_memory)} MB') diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/set_epoch_info_hook.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/set_epoch_info_hook.py deleted file mode 100644 index c2b134ceb..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/set_epoch_info_hook.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.parallel import is_module_wrapper -from mmcv.runner import HOOKS, Hook - - -@HOOKS.register_module() -class SetEpochInfoHook(Hook): - """Set runner's epoch information to the model.""" - - def before_train_epoch(self, runner): - epoch = runner.epoch - model = runner.model - if is_module_wrapper(model): - model = model.module - model.set_epoch(epoch) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/sync_norm_hook.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/sync_norm_hook.py deleted file mode 100644 index 82931cef3..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/sync_norm_hook.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -from mmcv.runner import get_dist_info -from mmcv.runner.hooks import HOOKS, Hook -from torch import nn - -from ..utils.dist_utils import all_reduce_dict - - -def get_norm_states(module): - async_norm_states = OrderedDict() - for name, child in module.named_modules(): - if isinstance(child, nn.modules.batchnorm._NormBase): - for k, v in child.state_dict().items(): - async_norm_states['.'.join([name, k])] = v - return async_norm_states - - -@HOOKS.register_module() -class SyncNormHook(Hook): - """Synchronize Norm states after training epoch, currently used in YOLOX. - - Args: - num_last_epochs (int): The number of latter epochs in the end of the - training to switch to synchronizing norm interval. Default: 15. - interval (int): Synchronizing norm interval. Default: 1. - """ - - def __init__(self, num_last_epochs=15, interval=1): - self.interval = interval - self.num_last_epochs = num_last_epochs - - def before_train_epoch(self, runner): - epoch = runner.epoch - if (epoch + 1) == runner.max_epochs - self.num_last_epochs: - # Synchronize norm every epoch. - self.interval = 1 - - def after_train_epoch(self, runner): - """Synchronizing norm.""" - epoch = runner.epoch - module = runner.model - if (epoch + 1) % self.interval == 0: - _, world_size = get_dist_info() - if world_size == 1: - return - norm_states = get_norm_states(module) - if len(norm_states) == 0: - return - norm_states = all_reduce_dict(norm_states, op='mean') - module.load_state_dict(norm_states, strict=False) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/sync_random_size_hook.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/sync_random_size_hook.py deleted file mode 100644 index 6d7e96c6a..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/sync_random_size_hook.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import random -import warnings - -import torch -from mmcv.runner import get_dist_info -from mmcv.runner.hooks import HOOKS, Hook -from torch import distributed as dist - - -@HOOKS.register_module() -class SyncRandomSizeHook(Hook): - """Change and synchronize the random image size across ranks. - SyncRandomSizeHook is deprecated, please use Resize pipeline to achieve - similar functions. Such as `dict(type='Resize', img_scale=[(448, 448), - (832, 832)], multiscale_mode='range', keep_ratio=True)`. - - Note: Due to the multi-process dataloader, its behavior is different - from YOLOX's official implementation, the official is to change the - size every fixed iteration interval and what we achieved is a fixed - epoch interval. - - Args: - ratio_range (tuple[int]): Random ratio range. It will be multiplied - by 32, and then change the dataset output image size. - Default: (14, 26). - img_scale (tuple[int]): Size of input image. Default: (640, 640). - interval (int): The epoch interval of change image size. Default: 1. - device (torch.device | str): device for returned tensors. - Default: 'cuda'. - """ - - def __init__(self, - ratio_range=(14, 26), - img_scale=(640, 640), - interval=1, - device='cuda'): - warnings.warn('DeprecationWarning: SyncRandomSizeHook is deprecated. ' - 'Please use Resize pipeline to achieve similar ' - 'functions. Due to the multi-process dataloader, ' - 'its behavior is different from YOLOX\'s official ' - 'implementation, the official is to change the size ' - 'every fixed iteration interval and what we achieved ' - 'is a fixed epoch interval.') - self.rank, world_size = get_dist_info() - self.is_distributed = world_size > 1 - self.ratio_range = ratio_range - self.img_scale = img_scale - self.interval = interval - self.device = device - - def after_train_epoch(self, runner): - """Change the dataset output image size.""" - if self.ratio_range is not None and (runner.epoch + - 1) % self.interval == 0: - # Due to DDP and DP get the device behavior inconsistent, - # so we did not get the device from runner.model. - tensor = torch.LongTensor(2).to(self.device) - - if self.rank == 0: - size_factor = self.img_scale[1] * 1. / self.img_scale[0] - size = random.randint(*self.ratio_range) - size = (int(32 * size), 32 * int(size * size_factor)) - tensor[0] = size[0] - tensor[1] = size[1] - - if self.is_distributed: - dist.barrier() - dist.broadcast(tensor, 0) - - runner.data_loader.dataset.update_dynamic_scale( - (tensor[0].item(), tensor[1].item())) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/wandblogger_hook.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/wandblogger_hook.py deleted file mode 100644 index 01c22bf50..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/wandblogger_hook.py +++ /dev/null @@ -1,587 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os.path as osp -import sys -import warnings - -import mmcv -import numpy as np -import pycocotools.mask as mask_util -from mmcv.runner import HOOKS -from mmcv.runner.dist_utils import master_only -from mmcv.runner.hooks.checkpoint import CheckpointHook -from mmcv.runner.hooks.logger.wandb import WandbLoggerHook -from mmcv.utils import digit_version - -from mmdet.core import DistEvalHook, EvalHook -from mmdet.core.mask.structures import polygon_to_bitmap - - -@HOOKS.register_module() -class MMDetWandbHook(WandbLoggerHook): - """Enhanced Wandb logger hook for MMDetection. - - Comparing with the :cls:`mmcv.runner.WandbLoggerHook`, this hook can not - only automatically log all the metrics but also log the following extra - information - saves model checkpoints as W&B Artifact, and - logs model prediction as interactive W&B Tables. - - - Metrics: The MMDetWandbHook will automatically log training - and validation metrics along with system metrics (CPU/GPU). - - - Checkpointing: If `log_checkpoint` is True, the checkpoint saved at - every checkpoint interval will be saved as W&B Artifacts. - This depends on the : class:`mmcv.runner.CheckpointHook` whose priority - is higher than this hook. Please refer to - https://docs.wandb.ai/guides/artifacts/model-versioning - to learn more about model versioning with W&B Artifacts. - - - Checkpoint Metadata: If evaluation results are available for a given - checkpoint artifact, it will have a metadata associated with it. - The metadata contains the evaluation metrics computed on validation - data with that checkpoint along with the current epoch. It depends - on `EvalHook` whose priority is more than MMDetWandbHook. - - - Evaluation: At every evaluation interval, the `MMDetWandbHook` logs the - model prediction as interactive W&B Tables. The number of samples - logged is given by `num_eval_images`. Currently, the `MMDetWandbHook` - logs the predicted bounding boxes along with the ground truth at every - evaluation interval. This depends on the `EvalHook` whose priority is - more than `MMDetWandbHook`. Also note that the data is just logged once - and subsequent evaluation tables uses reference to the logged data - to save memory usage. Please refer to - https://docs.wandb.ai/guides/data-vis to learn more about W&B Tables. - - For more details check out W&B's MMDetection docs: - https://docs.wandb.ai/guides/integrations/mmdetection - - ``` - Example: - log_config = dict( - ... - hooks=[ - ..., - dict(type='MMDetWandbHook', - init_kwargs={ - 'entity': "YOUR_ENTITY", - 'project': "YOUR_PROJECT_NAME" - }, - interval=50, - log_checkpoint=True, - log_checkpoint_metadata=True, - num_eval_images=100, - bbox_score_thr=0.3) - ]) - ``` - - Args: - init_kwargs (dict): A dict passed to wandb.init to initialize - a W&B run. Please refer to https://docs.wandb.ai/ref/python/init - for possible key-value pairs. - interval (int): Logging interval (every k iterations). Defaults to 50. - log_checkpoint (bool): Save the checkpoint at every checkpoint interval - as W&B Artifacts. Use this for model versioning where each version - is a checkpoint. Defaults to False. - log_checkpoint_metadata (bool): Log the evaluation metrics computed - on the validation data with the checkpoint, along with current - epoch as a metadata to that checkpoint. - Defaults to True. - num_eval_images (int): The number of validation images to be logged. - If zero, the evaluation won't be logged. Defaults to 100. - bbox_score_thr (float): Threshold for bounding box scores. - Defaults to 0.3. - """ - - def __init__(self, - init_kwargs=None, - interval=50, - log_checkpoint=False, - log_checkpoint_metadata=False, - num_eval_images=100, - bbox_score_thr=0.3, - **kwargs): - super(MMDetWandbHook, self).__init__(init_kwargs, interval, **kwargs) - - self.log_checkpoint = log_checkpoint - self.log_checkpoint_metadata = ( - log_checkpoint and log_checkpoint_metadata) - self.num_eval_images = num_eval_images - self.bbox_score_thr = bbox_score_thr - self.log_evaluation = (num_eval_images > 0) - self.ckpt_hook: CheckpointHook = None - self.eval_hook: EvalHook = None - - def import_wandb(self): - try: - import wandb - from wandb import init # noqa - - # Fix ResourceWarning when calling wandb.log in wandb v0.12.10. - # https://github.com/wandb/client/issues/2837 - if digit_version(wandb.__version__) < digit_version('0.12.10'): - warnings.warn( - f'The current wandb {wandb.__version__} is ' - f'lower than v0.12.10 will cause ResourceWarning ' - f'when calling wandb.log, Please run ' - f'"pip install --upgrade wandb"') - - except ImportError: - raise ImportError( - 'Please run "pip install "wandb>=0.12.10"" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(MMDetWandbHook, self).before_run(runner) - - # Save and Log config. - if runner.meta is not None and runner.meta.get('exp_name', - None) is not None: - src_cfg_path = osp.join(runner.work_dir, - runner.meta.get('exp_name', None)) - if osp.exists(src_cfg_path): - self.wandb.save(src_cfg_path, base_path=runner.work_dir) - self._update_wandb_config(runner) - else: - runner.logger.warning('No meta information found in the runner. ') - - # Inspect CheckpointHook and EvalHook - for hook in runner.hooks: - if isinstance(hook, CheckpointHook): - self.ckpt_hook = hook - if isinstance(hook, (EvalHook, DistEvalHook)): - self.eval_hook = hook - - # Check conditions to log checkpoint - if self.log_checkpoint: - if self.ckpt_hook is None: - self.log_checkpoint = False - self.log_checkpoint_metadata = False - runner.logger.warning( - 'To log checkpoint in MMDetWandbHook, `CheckpointHook` is' - 'required, please check hooks in the runner.') - else: - self.ckpt_interval = self.ckpt_hook.interval - - # Check conditions to log evaluation - if self.log_evaluation or self.log_checkpoint_metadata: - if self.eval_hook is None: - self.log_evaluation = False - self.log_checkpoint_metadata = False - runner.logger.warning( - 'To log evaluation or checkpoint metadata in ' - 'MMDetWandbHook, `EvalHook` or `DistEvalHook` in mmdet ' - 'is required, please check whether the validation ' - 'is enabled.') - else: - self.eval_interval = self.eval_hook.interval - self.val_dataset = self.eval_hook.dataloader.dataset - # Determine the number of samples to be logged. - if self.num_eval_images > len(self.val_dataset): - self.num_eval_images = len(self.val_dataset) - runner.logger.warning( - f'The num_eval_images ({self.num_eval_images}) is ' - 'greater than the total number of validation samples ' - f'({len(self.val_dataset)}). The complete validation ' - 'dataset will be logged.') - - # Check conditions to log checkpoint metadata - if self.log_checkpoint_metadata: - assert self.ckpt_interval % self.eval_interval == 0, \ - 'To log checkpoint metadata in MMDetWandbHook, the interval ' \ - f'of checkpoint saving ({self.ckpt_interval}) should be ' \ - 'divisible by the interval of evaluation ' \ - f'({self.eval_interval}).' - - # Initialize evaluation table - if self.log_evaluation: - # Initialize data table - self._init_data_table() - # Add data to the data table - self._add_ground_truth(runner) - # Log ground truth data - self._log_data_table() - - @master_only - def after_train_epoch(self, runner): - super(MMDetWandbHook, self).after_train_epoch(runner) - - if not self.by_epoch: - return - - # Log checkpoint and metadata. - if (self.log_checkpoint - and self.every_n_epochs(runner, self.ckpt_interval) - or (self.ckpt_hook.save_last and self.is_last_epoch(runner))): - if self.log_checkpoint_metadata and self.eval_hook: - metadata = { - 'epoch': runner.epoch + 1, - **self._get_eval_results() - } - else: - metadata = None - aliases = [f'epoch_{runner.epoch + 1}', 'latest'] - model_path = osp.join(self.ckpt_hook.out_dir, - f'epoch_{runner.epoch + 1}.pth') - self._log_ckpt_as_artifact(model_path, aliases, metadata) - - # Save prediction table - if self.log_evaluation and self.eval_hook._should_evaluate(runner): - results = self.eval_hook.latest_results - # Initialize evaluation table - self._init_pred_table() - # Log predictions - self._log_predictions(results) - # Log the table - self._log_eval_table(runner.epoch + 1) - - @master_only - def after_train_iter(self, runner): - if self.get_mode(runner) == 'train': - # An ugly patch. The iter-based eval hook will call the - # `after_train_iter` method of all logger hooks before evaluation. - # Use this trick to skip that call. - # Don't call super method at first, it will clear the log_buffer - return super(MMDetWandbHook, self).after_train_iter(runner) - else: - super(MMDetWandbHook, self).after_train_iter(runner) - - if self.by_epoch: - return - - # Save checkpoint and metadata - if (self.log_checkpoint - and self.every_n_iters(runner, self.ckpt_interval) - or (self.ckpt_hook.save_last and self.is_last_iter(runner))): - if self.log_checkpoint_metadata and self.eval_hook: - metadata = { - 'iter': runner.iter + 1, - **self._get_eval_results() - } - else: - metadata = None - aliases = [f'iter_{runner.iter + 1}', 'latest'] - model_path = osp.join(self.ckpt_hook.out_dir, - f'iter_{runner.iter + 1}.pth') - self._log_ckpt_as_artifact(model_path, aliases, metadata) - - # Save prediction table - if self.log_evaluation and self.eval_hook._should_evaluate(runner): - results = self.eval_hook.latest_results - # Initialize evaluation table - self._init_pred_table() - # Log predictions - self._log_predictions(results) - # Log the table - self._log_eval_table(runner.iter + 1) - - @master_only - def after_run(self, runner): - self.wandb.finish() - - def _update_wandb_config(self, runner): - """Update wandb config.""" - # Import the config file. - sys.path.append(runner.work_dir) - config_filename = runner.meta['exp_name'][:-3] - configs = importlib.import_module(config_filename) - # Prepare a nested dict of config variables. - config_keys = [key for key in dir(configs) if not key.startswith('__')] - config_dict = {key: getattr(configs, key) for key in config_keys} - # Update the W&B config. - self.wandb.config.update(config_dict) - - def _log_ckpt_as_artifact(self, model_path, aliases, metadata=None): - """Log model checkpoint as W&B Artifact. - - Args: - model_path (str): Path of the checkpoint to log. - aliases (list): List of the aliases associated with this artifact. - metadata (dict, optional): Metadata associated with this artifact. - """ - model_artifact = self.wandb.Artifact( - f'run_{self.wandb.run.id}_model', type='model', metadata=metadata) - model_artifact.add_file(model_path) - self.wandb.log_artifact(model_artifact, aliases=aliases) - - def _get_eval_results(self): - """Get model evaluation results.""" - results = self.eval_hook.latest_results - eval_results = self.val_dataset.evaluate( - results, logger='silent', **self.eval_hook.eval_kwargs) - return eval_results - - def _init_data_table(self): - """Initialize the W&B Tables for validation data.""" - columns = ['image_name', 'image'] - self.data_table = self.wandb.Table(columns=columns) - - def _init_pred_table(self): - """Initialize the W&B Tables for model evaluation.""" - columns = ['image_name', 'ground_truth', 'prediction'] - self.eval_table = self.wandb.Table(columns=columns) - - def _add_ground_truth(self, runner): - # Get image loading pipeline - from mmdet.datasets.pipelines import LoadImageFromFile - img_loader = None - for t in self.val_dataset.pipeline.transforms: - if isinstance(t, LoadImageFromFile): - img_loader = t - - if img_loader is None: - self.log_evaluation = False - runner.logger.warning( - 'LoadImageFromFile is required to add images ' - 'to W&B Tables.') - return - - # Select the images to be logged. - self.eval_image_indexs = np.arange(len(self.val_dataset)) - # Set seed so that same validation set is logged each time. - np.random.seed(42) - np.random.shuffle(self.eval_image_indexs) - self.eval_image_indexs = self.eval_image_indexs[:self.num_eval_images] - - CLASSES = self.val_dataset.CLASSES - self.class_id_to_label = { - id + 1: name - for id, name in enumerate(CLASSES) - } - self.class_set = self.wandb.Classes([{ - 'id': id, - 'name': name - } for id, name in self.class_id_to_label.items()]) - - img_prefix = self.val_dataset.img_prefix - - for idx in self.eval_image_indexs: - img_info = self.val_dataset.data_infos[idx] - image_name = img_info.get('filename', f'img_{idx}') - img_height, img_width = img_info['height'], img_info['width'] - - img_meta = img_loader( - dict(img_info=img_info, img_prefix=img_prefix)) - - # Get image and convert from BGR to RGB - image = mmcv.bgr2rgb(img_meta['img']) - - data_ann = self.val_dataset.get_ann_info(idx) - bboxes = data_ann['bboxes'] - labels = data_ann['labels'] - masks = data_ann.get('masks', None) - - # Get dict of bounding boxes to be logged. - assert len(bboxes) == len(labels) - wandb_boxes = self._get_wandb_bboxes(bboxes, labels) - - # Get dict of masks to be logged. - if masks is not None: - wandb_masks = self._get_wandb_masks( - masks, - labels, - is_poly_mask=True, - height=img_height, - width=img_width) - else: - wandb_masks = None - # TODO: Panoramic segmentation visualization. - - # Log a row to the data table. - self.data_table.add_data( - image_name, - self.wandb.Image( - image, - boxes=wandb_boxes, - masks=wandb_masks, - classes=self.class_set)) - - def _log_predictions(self, results): - table_idxs = self.data_table_ref.get_index() - assert len(table_idxs) == len(self.eval_image_indexs) - - for ndx, eval_image_index in enumerate(self.eval_image_indexs): - # Get the result - result = results[eval_image_index] - if isinstance(result, tuple): - bbox_result, segm_result = result - if isinstance(segm_result, tuple): - segm_result = segm_result[0] # ms rcnn - else: - bbox_result, segm_result = result, None - assert len(bbox_result) == len(self.class_id_to_label) - - # Get labels - bboxes = np.vstack(bbox_result) - labels = [ - np.full(bbox.shape[0], i, dtype=np.int32) - for i, bbox in enumerate(bbox_result) - ] - labels = np.concatenate(labels) - - # Get segmentation mask if available. - segms = None - if segm_result is not None and len(labels) > 0: - segms = mmcv.concat_list(segm_result) - segms = mask_util.decode(segms) - segms = segms.transpose(2, 0, 1) - assert len(segms) == len(labels) - # TODO: Panoramic segmentation visualization. - - # Remove bounding boxes and masks with score lower than threshold. - if self.bbox_score_thr > 0: - assert bboxes is not None and bboxes.shape[1] == 5 - scores = bboxes[:, -1] - inds = scores > self.bbox_score_thr - bboxes = bboxes[inds, :] - labels = labels[inds] - if segms is not None: - segms = segms[inds, ...] - - # Get dict of bounding boxes to be logged. - wandb_boxes = self._get_wandb_bboxes(bboxes, labels, log_gt=False) - # Get dict of masks to be logged. - if segms is not None: - wandb_masks = self._get_wandb_masks(segms, labels) - else: - wandb_masks = None - - # Log a row to the eval table. - self.eval_table.add_data( - self.data_table_ref.data[ndx][0], - self.data_table_ref.data[ndx][1], - self.wandb.Image( - self.data_table_ref.data[ndx][1], - boxes=wandb_boxes, - masks=wandb_masks, - classes=self.class_set)) - - def _get_wandb_bboxes(self, bboxes, labels, log_gt=True): - """Get list of structured dict for logging bounding boxes to W&B. - - Args: - bboxes (list): List of bounding box coordinates in - (minX, minY, maxX, maxY) format. - labels (int): List of label ids. - log_gt (bool): Whether to log ground truth or prediction boxes. - - Returns: - Dictionary of bounding boxes to be logged. - """ - wandb_boxes = {} - - box_data = [] - for bbox, label in zip(bboxes, labels): - if not isinstance(label, int): - label = int(label) - label = label + 1 - - if len(bbox) == 5: - confidence = float(bbox[4]) - class_name = self.class_id_to_label[label] - box_caption = f'{class_name} {confidence:.2f}' - else: - box_caption = str(self.class_id_to_label[label]) - - position = dict( - minX=int(bbox[0]), - minY=int(bbox[1]), - maxX=int(bbox[2]), - maxY=int(bbox[3])) - - box_data.append({ - 'position': position, - 'class_id': label, - 'box_caption': box_caption, - 'domain': 'pixel' - }) - - wandb_bbox_dict = { - 'box_data': box_data, - 'class_labels': self.class_id_to_label - } - - if log_gt: - wandb_boxes['ground_truth'] = wandb_bbox_dict - else: - wandb_boxes['predictions'] = wandb_bbox_dict - - return wandb_boxes - - def _get_wandb_masks(self, - masks, - labels, - is_poly_mask=False, - height=None, - width=None): - """Get list of structured dict for logging masks to W&B. - - Args: - masks (list): List of masks. - labels (int): List of label ids. - is_poly_mask (bool): Whether the mask is polygonal or not. - This is true for CocoDataset. - height (int): Height of the image. - width (int): Width of the image. - - Returns: - Dictionary of masks to be logged. - """ - mask_label_dict = dict() - for mask, label in zip(masks, labels): - label = label + 1 - # Get bitmap mask from polygon. - if is_poly_mask: - if height is not None and width is not None: - mask = polygon_to_bitmap(mask, height, width) - # Create composite masks for each class. - if label not in mask_label_dict.keys(): - mask_label_dict[label] = mask - else: - mask_label_dict[label] = np.logical_or(mask_label_dict[label], - mask) - - wandb_masks = dict() - for key, value in mask_label_dict.items(): - # Create mask for that class. - value = value.astype(np.uint8) - value[value > 0] = key - - # Create dict of masks for logging. - class_name = self.class_id_to_label[key] - wandb_masks[class_name] = { - 'mask_data': value, - 'class_labels': self.class_id_to_label - } - - return wandb_masks - - def _log_data_table(self): - """Log the W&B Tables for validation data as artifact and calls - `use_artifact` on it so that the evaluation table can use the reference - of already uploaded images. - - This allows the data to be uploaded just once. - """ - data_artifact = self.wandb.Artifact('val', type='dataset') - data_artifact.add(self.data_table, 'val_data') - - self.wandb.run.use_artifact(data_artifact) - data_artifact.wait() - - self.data_table_ref = data_artifact.get('val_data') - - def _log_eval_table(self, idx): - """Log the W&B Tables for model evaluation. - - The table will be logged multiple times creating new version. Use this - to compare models at different intervals interactively. - """ - pred_artifact = self.wandb.Artifact( - f'run_{self.wandb.run.id}_pred', type='evaluation') - pred_artifact.add(self.eval_table, 'eval_data') - if self.by_epoch: - aliases = ['latest', f'epoch_{idx}'] - else: - aliases = ['latest', f'iter_{idx}'] - self.wandb.run.log_artifact(pred_artifact, aliases=aliases) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/yolox_lrupdater_hook.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/yolox_lrupdater_hook.py deleted file mode 100644 index ecb028ed2..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/yolox_lrupdater_hook.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner.hooks import HOOKS -from mmcv.runner.hooks.lr_updater import (CosineAnnealingLrUpdaterHook, - annealing_cos) - - -@HOOKS.register_module() -class YOLOXLrUpdaterHook(CosineAnnealingLrUpdaterHook): - """YOLOX learning rate scheme. - - There are two main differences between YOLOXLrUpdaterHook - and CosineAnnealingLrUpdaterHook. - - 1. When the current running epoch is greater than - `max_epoch-last_epoch`, a fixed learning rate will be used - 2. The exp warmup scheme is different with LrUpdaterHook in MMCV - - Args: - num_last_epochs (int): The number of epochs with a fixed learning rate - before the end of the training. - """ - - def __init__(self, num_last_epochs, **kwargs): - self.num_last_epochs = num_last_epochs - super(YOLOXLrUpdaterHook, self).__init__(**kwargs) - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - # exp warmup scheme - k = self.warmup_ratio * pow( - (cur_iters + 1) / float(self.warmup_iters), 2) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.base_lr, dict): - lr_groups = {} - for key, base_lr in self.base_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, base_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.base_lr) - - def get_lr(self, runner, base_lr): - last_iter = len(runner.data_loader) * self.num_last_epochs - - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - progress += 1 - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress >= max_progress - last_iter: - # fixed learning rate - return target_lr - else: - return annealing_cos( - base_lr, target_lr, (progress - self.warmup_iters) / - (max_progress - self.warmup_iters - last_iter)) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/yolox_mode_switch_hook.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/yolox_mode_switch_hook.py deleted file mode 100644 index 10834e686..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/hook/yolox_mode_switch_hook.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.parallel import is_module_wrapper -from mmcv.runner.hooks import HOOKS, Hook - - -@HOOKS.register_module() -class YOLOXModeSwitchHook(Hook): - """Switch the mode of YOLOX during training. - - This hook turns off the mosaic and mixup data augmentation and switches - to use L1 loss in bbox_head. - - Args: - num_last_epochs (int): The number of latter epochs in the end of the - training to close the data augmentation and switch to L1 loss. - Default: 15. - skip_type_keys (list[str], optional): Sequence of type string to be - skip pipeline. Default: ('Mosaic', 'RandomAffine', 'MixUp') - """ - - def __init__(self, - num_last_epochs=15, - skip_type_keys=('Mosaic', 'RandomAffine', 'MixUp')): - self.num_last_epochs = num_last_epochs - self.skip_type_keys = skip_type_keys - self._restart_dataloader = False - - def before_train_epoch(self, runner): - """Close mosaic and mixup augmentation and switches to use L1 loss.""" - epoch = runner.epoch - train_loader = runner.data_loader - model = runner.model - if is_module_wrapper(model): - model = model.module - if (epoch + 1) == runner.max_epochs - self.num_last_epochs: - runner.logger.info('No mosaic and mixup aug now!') - # The dataset pipeline cannot be updated when persistent_workers - # is True, so we need to force the dataloader's multi-process - # restart. This is a very hacky approach. - train_loader.dataset.update_skip_type_keys(self.skip_type_keys) - if hasattr(train_loader, 'persistent_workers' - ) and train_loader.persistent_workers is True: - train_loader._DataLoader__initialized = False - train_loader._iterator = None - self._restart_dataloader = True - runner.logger.info('Add additional L1 loss now!') - model.bbox_head.use_l1 = True - else: - # Once the restart is complete, we need to restore - # the initialization flag. - if self._restart_dataloader: - train_loader._DataLoader__initialized = True diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/mask/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/mask/__init__.py deleted file mode 100644 index 644a9b1d9..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/mask/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .mask_target import mask_target -from .structures import BaseInstanceMasks, BitmapMasks, PolygonMasks -from .utils import encode_mask_results, mask2bbox, split_combined_polys - -__all__ = [ - 'split_combined_polys', 'mask_target', 'BaseInstanceMasks', 'BitmapMasks', - 'PolygonMasks', 'encode_mask_results', 'mask2bbox' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/mask/mask_target.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/mask/mask_target.py deleted file mode 100644 index 273e7678f..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/mask/mask_target.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -from torch.nn.modules.utils import _pair - - -def mask_target(pos_proposals_list, pos_assigned_gt_inds_list, gt_masks_list, - cfg): - """Compute mask target for positive proposals in multiple images. - - Args: - pos_proposals_list (list[Tensor]): Positive proposals in multiple - images. - pos_assigned_gt_inds_list (list[Tensor]): Assigned GT indices for each - positive proposals. - gt_masks_list (list[:obj:`BaseInstanceMasks`]): Ground truth masks of - each image. - cfg (dict): Config dict that specifies the mask size. - - Returns: - list[Tensor]: Mask target of each image. - - Example: - >>> import mmcv - >>> import mmdet - >>> from mmdet.core.mask import BitmapMasks - >>> from mmdet.core.mask.mask_target import * - >>> H, W = 17, 18 - >>> cfg = mmcv.Config({'mask_size': (13, 14)}) - >>> rng = np.random.RandomState(0) - >>> # Positive proposals (tl_x, tl_y, br_x, br_y) for each image - >>> pos_proposals_list = [ - >>> torch.Tensor([ - >>> [ 7.2425, 5.5929, 13.9414, 14.9541], - >>> [ 7.3241, 3.6170, 16.3850, 15.3102], - >>> ]), - >>> torch.Tensor([ - >>> [ 4.8448, 6.4010, 7.0314, 9.7681], - >>> [ 5.9790, 2.6989, 7.4416, 4.8580], - >>> [ 0.0000, 0.0000, 0.1398, 9.8232], - >>> ]), - >>> ] - >>> # Corresponding class index for each proposal for each image - >>> pos_assigned_gt_inds_list = [ - >>> torch.LongTensor([7, 0]), - >>> torch.LongTensor([5, 4, 1]), - >>> ] - >>> # Ground truth mask for each true object for each image - >>> gt_masks_list = [ - >>> BitmapMasks(rng.rand(8, H, W), height=H, width=W), - >>> BitmapMasks(rng.rand(6, H, W), height=H, width=W), - >>> ] - >>> mask_targets = mask_target( - >>> pos_proposals_list, pos_assigned_gt_inds_list, - >>> gt_masks_list, cfg) - >>> assert mask_targets.shape == (5,) + cfg['mask_size'] - """ - cfg_list = [cfg for _ in range(len(pos_proposals_list))] - mask_targets = map(mask_target_single, pos_proposals_list, - pos_assigned_gt_inds_list, gt_masks_list, cfg_list) - mask_targets = list(mask_targets) - if len(mask_targets) > 0: - mask_targets = torch.cat(mask_targets) - return mask_targets - - -def mask_target_single(pos_proposals, pos_assigned_gt_inds, gt_masks, cfg): - """Compute mask target for each positive proposal in the image. - - Args: - pos_proposals (Tensor): Positive proposals. - pos_assigned_gt_inds (Tensor): Assigned GT inds of positive proposals. - gt_masks (:obj:`BaseInstanceMasks`): GT masks in the format of Bitmap - or Polygon. - cfg (dict): Config dict that indicate the mask size. - - Returns: - Tensor: Mask target of each positive proposals in the image. - - Example: - >>> import mmcv - >>> import mmdet - >>> from mmdet.core.mask import BitmapMasks - >>> from mmdet.core.mask.mask_target import * # NOQA - >>> H, W = 32, 32 - >>> cfg = mmcv.Config({'mask_size': (7, 11)}) - >>> rng = np.random.RandomState(0) - >>> # Masks for each ground truth box (relative to the image) - >>> gt_masks_data = rng.rand(3, H, W) - >>> gt_masks = BitmapMasks(gt_masks_data, height=H, width=W) - >>> # Predicted positive boxes in one image - >>> pos_proposals = torch.FloatTensor([ - >>> [ 16.2, 5.5, 19.9, 20.9], - >>> [ 17.3, 13.6, 19.3, 19.3], - >>> [ 14.8, 16.4, 17.0, 23.7], - >>> [ 0.0, 0.0, 16.0, 16.0], - >>> [ 4.0, 0.0, 20.0, 16.0], - >>> ]) - >>> # For each predicted proposal, its assignment to a gt mask - >>> pos_assigned_gt_inds = torch.LongTensor([0, 1, 2, 1, 1]) - >>> mask_targets = mask_target_single( - >>> pos_proposals, pos_assigned_gt_inds, gt_masks, cfg) - >>> assert mask_targets.shape == (5,) + cfg['mask_size'] - """ - device = pos_proposals.device - mask_size = _pair(cfg.mask_size) - binarize = not cfg.get('soft_mask_target', False) - num_pos = pos_proposals.size(0) - if num_pos > 0: - proposals_np = pos_proposals.cpu().numpy() - maxh, maxw = gt_masks.height, gt_masks.width - proposals_np[:, [0, 2]] = np.clip(proposals_np[:, [0, 2]], 0, maxw) - proposals_np[:, [1, 3]] = np.clip(proposals_np[:, [1, 3]], 0, maxh) - pos_assigned_gt_inds = pos_assigned_gt_inds.cpu().numpy() - - mask_targets = gt_masks.crop_and_resize( - proposals_np, - mask_size, - device=device, - inds=pos_assigned_gt_inds, - binarize=binarize).to_ndarray() - - mask_targets = torch.from_numpy(mask_targets).float().to(device) - else: - mask_targets = pos_proposals.new_zeros((0, ) + mask_size) - - return mask_targets diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/mask/structures.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/mask/structures.py deleted file mode 100644 index a9d0ebb4b..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/mask/structures.py +++ /dev/null @@ -1,1102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -import cv2 -import mmcv -import numpy as np -import pycocotools.mask as maskUtils -import torch -from mmcv.ops.roi_align import roi_align - - -class BaseInstanceMasks(metaclass=ABCMeta): - """Base class for instance masks.""" - - @abstractmethod - def rescale(self, scale, interpolation='nearest'): - """Rescale masks as large as possible while keeping the aspect ratio. - For details can refer to `mmcv.imrescale`. - - Args: - scale (tuple[int]): The maximum size (h, w) of rescaled mask. - interpolation (str): Same as :func:`mmcv.imrescale`. - - Returns: - BaseInstanceMasks: The rescaled masks. - """ - - @abstractmethod - def resize(self, out_shape, interpolation='nearest'): - """Resize masks to the given out_shape. - - Args: - out_shape: Target (h, w) of resized mask. - interpolation (str): See :func:`mmcv.imresize`. - - Returns: - BaseInstanceMasks: The resized masks. - """ - - @abstractmethod - def flip(self, flip_direction='horizontal'): - """Flip masks alone the given direction. - - Args: - flip_direction (str): Either 'horizontal' or 'vertical'. - - Returns: - BaseInstanceMasks: The flipped masks. - """ - - @abstractmethod - def pad(self, out_shape, pad_val): - """Pad masks to the given size of (h, w). - - Args: - out_shape (tuple[int]): Target (h, w) of padded mask. - pad_val (int): The padded value. - - Returns: - BaseInstanceMasks: The padded masks. - """ - - @abstractmethod - def crop(self, bbox): - """Crop each mask by the given bbox. - - Args: - bbox (ndarray): Bbox in format [x1, y1, x2, y2], shape (4, ). - - Return: - BaseInstanceMasks: The cropped masks. - """ - - @abstractmethod - def crop_and_resize(self, - bboxes, - out_shape, - inds, - device, - interpolation='bilinear', - binarize=True): - """Crop and resize masks by the given bboxes. - - This function is mainly used in mask targets computation. - It firstly align mask to bboxes by assigned_inds, then crop mask by the - assigned bbox and resize to the size of (mask_h, mask_w) - - Args: - bboxes (Tensor): Bboxes in format [x1, y1, x2, y2], shape (N, 4) - out_shape (tuple[int]): Target (h, w) of resized mask - inds (ndarray): Indexes to assign masks to each bbox, - shape (N,) and values should be between [0, num_masks - 1]. - device (str): Device of bboxes - interpolation (str): See `mmcv.imresize` - binarize (bool): if True fractional values are rounded to 0 or 1 - after the resize operation. if False and unsupported an error - will be raised. Defaults to True. - - Return: - BaseInstanceMasks: the cropped and resized masks. - """ - - @abstractmethod - def expand(self, expanded_h, expanded_w, top, left): - """see :class:`Expand`.""" - - @property - @abstractmethod - def areas(self): - """ndarray: areas of each instance.""" - - @abstractmethod - def to_ndarray(self): - """Convert masks to the format of ndarray. - - Return: - ndarray: Converted masks in the format of ndarray. - """ - - @abstractmethod - def to_tensor(self, dtype, device): - """Convert masks to the format of Tensor. - - Args: - dtype (str): Dtype of converted mask. - device (torch.device): Device of converted masks. - - Returns: - Tensor: Converted masks in the format of Tensor. - """ - - @abstractmethod - def translate(self, - out_shape, - offset, - direction='horizontal', - fill_val=0, - interpolation='bilinear'): - """Translate the masks. - - Args: - out_shape (tuple[int]): Shape for output mask, format (h, w). - offset (int | float): The offset for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - fill_val (int | float): Border value. Default 0. - interpolation (str): Same as :func:`mmcv.imtranslate`. - - Returns: - Translated masks. - """ - - def shear(self, - out_shape, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear the masks. - - Args: - out_shape (tuple[int]): Shape for output mask, format (h, w). - magnitude (int | float): The magnitude used for shear. - direction (str): The shear direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. Default 0. - interpolation (str): Same as in :func:`mmcv.imshear`. - - Returns: - ndarray: Sheared masks. - """ - - @abstractmethod - def rotate(self, out_shape, angle, center=None, scale=1.0, fill_val=0): - """Rotate the masks. - - Args: - out_shape (tuple[int]): Shape for output mask, format (h, w). - angle (int | float): Rotation angle in degrees. Positive values - mean counter-clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the - rotation in source image. If not specified, the center of - the image will be used. - scale (int | float): Isotropic scale factor. - fill_val (int | float): Border value. Default 0 for masks. - - Returns: - Rotated masks. - """ - - -class BitmapMasks(BaseInstanceMasks): - """This class represents masks in the form of bitmaps. - - Args: - masks (ndarray): ndarray of masks in shape (N, H, W), where N is - the number of objects. - height (int): height of masks - width (int): width of masks - - Example: - >>> from mmdet.core.mask.structures import * # NOQA - >>> num_masks, H, W = 3, 32, 32 - >>> rng = np.random.RandomState(0) - >>> masks = (rng.rand(num_masks, H, W) > 0.1).astype(np.int) - >>> self = BitmapMasks(masks, height=H, width=W) - - >>> # demo crop_and_resize - >>> num_boxes = 5 - >>> bboxes = np.array([[0, 0, 30, 10.0]] * num_boxes) - >>> out_shape = (14, 14) - >>> inds = torch.randint(0, len(self), size=(num_boxes,)) - >>> device = 'cpu' - >>> interpolation = 'bilinear' - >>> new = self.crop_and_resize( - ... bboxes, out_shape, inds, device, interpolation) - >>> assert len(new) == num_boxes - >>> assert new.height, new.width == out_shape - """ - - def __init__(self, masks, height, width): - self.height = height - self.width = width - if len(masks) == 0: - self.masks = np.empty((0, self.height, self.width), dtype=np.uint8) - else: - assert isinstance(masks, (list, np.ndarray)) - if isinstance(masks, list): - assert isinstance(masks[0], np.ndarray) - assert masks[0].ndim == 2 # (H, W) - else: - assert masks.ndim == 3 # (N, H, W) - - self.masks = np.stack(masks).reshape(-1, height, width) - assert self.masks.shape[1] == self.height - assert self.masks.shape[2] == self.width - - def __getitem__(self, index): - """Index the BitmapMask. - - Args: - index (int | ndarray): Indices in the format of integer or ndarray. - - Returns: - :obj:`BitmapMasks`: Indexed bitmap masks. - """ - masks = self.masks[index].reshape(-1, self.height, self.width) - return BitmapMasks(masks, self.height, self.width) - - def __iter__(self): - return iter(self.masks) - - def __repr__(self): - s = self.__class__.__name__ + '(' - s += f'num_masks={len(self.masks)}, ' - s += f'height={self.height}, ' - s += f'width={self.width})' - return s - - def __len__(self): - """Number of masks.""" - return len(self.masks) - - def rescale(self, scale, interpolation='nearest'): - """See :func:`BaseInstanceMasks.rescale`.""" - if len(self.masks) == 0: - new_w, new_h = mmcv.rescale_size((self.width, self.height), scale) - rescaled_masks = np.empty((0, new_h, new_w), dtype=np.uint8) - else: - rescaled_masks = np.stack([ - mmcv.imrescale(mask, scale, interpolation=interpolation) - for mask in self.masks - ]) - height, width = rescaled_masks.shape[1:] - return BitmapMasks(rescaled_masks, height, width) - - def resize(self, out_shape, interpolation='nearest'): - """See :func:`BaseInstanceMasks.resize`.""" - if len(self.masks) == 0: - resized_masks = np.empty((0, *out_shape), dtype=np.uint8) - else: - resized_masks = np.stack([ - mmcv.imresize( - mask, out_shape[::-1], interpolation=interpolation) - for mask in self.masks - ]) - return BitmapMasks(resized_masks, *out_shape) - - def flip(self, flip_direction='horizontal'): - """See :func:`BaseInstanceMasks.flip`.""" - assert flip_direction in ('horizontal', 'vertical', 'diagonal') - - if len(self.masks) == 0: - flipped_masks = self.masks - else: - flipped_masks = np.stack([ - mmcv.imflip(mask, direction=flip_direction) - for mask in self.masks - ]) - return BitmapMasks(flipped_masks, self.height, self.width) - - def pad(self, out_shape, pad_val=0): - """See :func:`BaseInstanceMasks.pad`.""" - if len(self.masks) == 0: - padded_masks = np.empty((0, *out_shape), dtype=np.uint8) - else: - padded_masks = np.stack([ - mmcv.impad(mask, shape=out_shape, pad_val=pad_val) - for mask in self.masks - ]) - return BitmapMasks(padded_masks, *out_shape) - - def crop(self, bbox): - """See :func:`BaseInstanceMasks.crop`.""" - assert isinstance(bbox, np.ndarray) - assert bbox.ndim == 1 - - # clip the boundary - bbox = bbox.copy() - bbox[0::2] = np.clip(bbox[0::2], 0, self.width) - bbox[1::2] = np.clip(bbox[1::2], 0, self.height) - x1, y1, x2, y2 = bbox - w = np.maximum(x2 - x1, 1) - h = np.maximum(y2 - y1, 1) - - if len(self.masks) == 0: - cropped_masks = np.empty((0, h, w), dtype=np.uint8) - else: - cropped_masks = self.masks[:, y1:y1 + h, x1:x1 + w] - return BitmapMasks(cropped_masks, h, w) - - def crop_and_resize(self, - bboxes, - out_shape, - inds, - device='cpu', - interpolation='bilinear', - binarize=True): - """See :func:`BaseInstanceMasks.crop_and_resize`.""" - if len(self.masks) == 0: - empty_masks = np.empty((0, *out_shape), dtype=np.uint8) - return BitmapMasks(empty_masks, *out_shape) - - # convert bboxes to tensor - if isinstance(bboxes, np.ndarray): - bboxes = torch.from_numpy(bboxes).to(device=device) - if isinstance(inds, np.ndarray): - inds = torch.from_numpy(inds).to(device=device) - - num_bbox = bboxes.shape[0] - fake_inds = torch.arange( - num_bbox, device=device).to(dtype=bboxes.dtype)[:, None] - rois = torch.cat([fake_inds, bboxes], dim=1) # Nx5 - rois = rois.to(device=device) - if num_bbox > 0: - gt_masks_th = torch.from_numpy(self.masks).to(device).index_select( - 0, inds).to(dtype=rois.dtype) - targets = roi_align(gt_masks_th[:, None, :, :], rois, out_shape, - 1.0, 0, 'avg', True).squeeze(1) - if binarize: - resized_masks = (targets >= 0.5).cpu().numpy() - else: - resized_masks = targets.cpu().numpy() - else: - resized_masks = [] - return BitmapMasks(resized_masks, *out_shape) - - def expand(self, expanded_h, expanded_w, top, left): - """See :func:`BaseInstanceMasks.expand`.""" - if len(self.masks) == 0: - expanded_mask = np.empty((0, expanded_h, expanded_w), - dtype=np.uint8) - else: - expanded_mask = np.zeros((len(self), expanded_h, expanded_w), - dtype=np.uint8) - expanded_mask[:, top:top + self.height, - left:left + self.width] = self.masks - return BitmapMasks(expanded_mask, expanded_h, expanded_w) - - def translate(self, - out_shape, - offset, - direction='horizontal', - fill_val=0, - interpolation='bilinear'): - """Translate the BitmapMasks. - - Args: - out_shape (tuple[int]): Shape for output mask, format (h, w). - offset (int | float): The offset for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - fill_val (int | float): Border value. Default 0 for masks. - interpolation (str): Same as :func:`mmcv.imtranslate`. - - Returns: - BitmapMasks: Translated BitmapMasks. - - Example: - >>> from mmdet.core.mask.structures import BitmapMasks - >>> self = BitmapMasks.random(dtype=np.uint8) - >>> out_shape = (32, 32) - >>> offset = 4 - >>> direction = 'horizontal' - >>> fill_val = 0 - >>> interpolation = 'bilinear' - >>> # Note, There seem to be issues when: - >>> # * out_shape is different than self's shape - >>> # * the mask dtype is not supported by cv2.AffineWarp - >>> new = self.translate(out_shape, offset, direction, fill_val, - >>> interpolation) - >>> assert len(new) == len(self) - >>> assert new.height, new.width == out_shape - """ - if len(self.masks) == 0: - translated_masks = np.empty((0, *out_shape), dtype=np.uint8) - else: - translated_masks = mmcv.imtranslate( - self.masks.transpose((1, 2, 0)), - offset, - direction, - border_value=fill_val, - interpolation=interpolation) - if translated_masks.ndim == 2: - translated_masks = translated_masks[:, :, None] - translated_masks = translated_masks.transpose( - (2, 0, 1)).astype(self.masks.dtype) - return BitmapMasks(translated_masks, *out_shape) - - def shear(self, - out_shape, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear the BitmapMasks. - - Args: - out_shape (tuple[int]): Shape for output mask, format (h, w). - magnitude (int | float): The magnitude used for shear. - direction (str): The shear direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as in :func:`mmcv.imshear`. - - Returns: - BitmapMasks: The sheared masks. - """ - if len(self.masks) == 0: - sheared_masks = np.empty((0, *out_shape), dtype=np.uint8) - else: - sheared_masks = mmcv.imshear( - self.masks.transpose((1, 2, 0)), - magnitude, - direction, - border_value=border_value, - interpolation=interpolation) - if sheared_masks.ndim == 2: - sheared_masks = sheared_masks[:, :, None] - sheared_masks = sheared_masks.transpose( - (2, 0, 1)).astype(self.masks.dtype) - return BitmapMasks(sheared_masks, *out_shape) - - def rotate(self, out_shape, angle, center=None, scale=1.0, fill_val=0): - """Rotate the BitmapMasks. - - Args: - out_shape (tuple[int]): Shape for output mask, format (h, w). - angle (int | float): Rotation angle in degrees. Positive values - mean counter-clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the - rotation in source image. If not specified, the center of - the image will be used. - scale (int | float): Isotropic scale factor. - fill_val (int | float): Border value. Default 0 for masks. - - Returns: - BitmapMasks: Rotated BitmapMasks. - """ - if len(self.masks) == 0: - rotated_masks = np.empty((0, *out_shape), dtype=self.masks.dtype) - else: - rotated_masks = mmcv.imrotate( - self.masks.transpose((1, 2, 0)), - angle, - center=center, - scale=scale, - border_value=fill_val) - if rotated_masks.ndim == 2: - # case when only one mask, (h, w) - rotated_masks = rotated_masks[:, :, None] # (h, w, 1) - rotated_masks = rotated_masks.transpose( - (2, 0, 1)).astype(self.masks.dtype) - return BitmapMasks(rotated_masks, *out_shape) - - @property - def areas(self): - """See :py:attr:`BaseInstanceMasks.areas`.""" - return self.masks.sum((1, 2)) - - def to_ndarray(self): - """See :func:`BaseInstanceMasks.to_ndarray`.""" - return self.masks - - def to_tensor(self, dtype, device): - """See :func:`BaseInstanceMasks.to_tensor`.""" - return torch.tensor(self.masks, dtype=dtype, device=device) - - @classmethod - def random(cls, - num_masks=3, - height=32, - width=32, - dtype=np.uint8, - rng=None): - """Generate random bitmap masks for demo / testing purposes. - - Example: - >>> from mmdet.core.mask.structures import BitmapMasks - >>> self = BitmapMasks.random() - >>> print('self = {}'.format(self)) - self = BitmapMasks(num_masks=3, height=32, width=32) - """ - from mmdet.utils.util_random import ensure_rng - rng = ensure_rng(rng) - masks = (rng.rand(num_masks, height, width) > 0.1).astype(dtype) - self = cls(masks, height=height, width=width) - return self - - def get_bboxes(self): - num_masks = len(self) - boxes = np.zeros((num_masks, 4), dtype=np.float32) - x_any = self.masks.any(axis=1) - y_any = self.masks.any(axis=2) - for idx in range(num_masks): - x = np.where(x_any[idx, :])[0] - y = np.where(y_any[idx, :])[0] - if len(x) > 0 and len(y) > 0: - # use +1 for x_max and y_max so that the right and bottom - # boundary of instance masks are fully included by the box - boxes[idx, :] = np.array([x[0], y[0], x[-1] + 1, y[-1] + 1], - dtype=np.float32) - return boxes - - -class PolygonMasks(BaseInstanceMasks): - """This class represents masks in the form of polygons. - - Polygons is a list of three levels. The first level of the list - corresponds to objects, the second level to the polys that compose the - object, the third level to the poly coordinates - - Args: - masks (list[list[ndarray]]): The first level of the list - corresponds to objects, the second level to the polys that - compose the object, the third level to the poly coordinates - height (int): height of masks - width (int): width of masks - - Example: - >>> from mmdet.core.mask.structures import * # NOQA - >>> masks = [ - >>> [ np.array([0, 0, 10, 0, 10, 10., 0, 10, 0, 0]) ] - >>> ] - >>> height, width = 16, 16 - >>> self = PolygonMasks(masks, height, width) - - >>> # demo translate - >>> new = self.translate((16, 16), 4., direction='horizontal') - >>> assert np.all(new.masks[0][0][1::2] == masks[0][0][1::2]) - >>> assert np.all(new.masks[0][0][0::2] == masks[0][0][0::2] + 4) - - >>> # demo crop_and_resize - >>> num_boxes = 3 - >>> bboxes = np.array([[0, 0, 30, 10.0]] * num_boxes) - >>> out_shape = (16, 16) - >>> inds = torch.randint(0, len(self), size=(num_boxes,)) - >>> device = 'cpu' - >>> interpolation = 'bilinear' - >>> new = self.crop_and_resize( - ... bboxes, out_shape, inds, device, interpolation) - >>> assert len(new) == num_boxes - >>> assert new.height, new.width == out_shape - """ - - def __init__(self, masks, height, width): - assert isinstance(masks, list) - if len(masks) > 0: - assert isinstance(masks[0], list) - assert isinstance(masks[0][0], np.ndarray) - - self.height = height - self.width = width - self.masks = masks - - def __getitem__(self, index): - """Index the polygon masks. - - Args: - index (ndarray | List): The indices. - - Returns: - :obj:`PolygonMasks`: The indexed polygon masks. - """ - if isinstance(index, np.ndarray): - index = index.tolist() - if isinstance(index, list): - masks = [self.masks[i] for i in index] - else: - try: - masks = self.masks[index] - except Exception: - raise ValueError( - f'Unsupported input of type {type(index)} for indexing!') - if len(masks) and isinstance(masks[0], np.ndarray): - masks = [masks] # ensure a list of three levels - return PolygonMasks(masks, self.height, self.width) - - def __iter__(self): - return iter(self.masks) - - def __repr__(self): - s = self.__class__.__name__ + '(' - s += f'num_masks={len(self.masks)}, ' - s += f'height={self.height}, ' - s += f'width={self.width})' - return s - - def __len__(self): - """Number of masks.""" - return len(self.masks) - - def rescale(self, scale, interpolation=None): - """see :func:`BaseInstanceMasks.rescale`""" - new_w, new_h = mmcv.rescale_size((self.width, self.height), scale) - if len(self.masks) == 0: - rescaled_masks = PolygonMasks([], new_h, new_w) - else: - rescaled_masks = self.resize((new_h, new_w)) - return rescaled_masks - - def resize(self, out_shape, interpolation=None): - """see :func:`BaseInstanceMasks.resize`""" - if len(self.masks) == 0: - resized_masks = PolygonMasks([], *out_shape) - else: - h_scale = out_shape[0] / self.height - w_scale = out_shape[1] / self.width - resized_masks = [] - for poly_per_obj in self.masks: - resized_poly = [] - for p in poly_per_obj: - p = p.copy() - p[0::2] = p[0::2] * w_scale - p[1::2] = p[1::2] * h_scale - resized_poly.append(p) - resized_masks.append(resized_poly) - resized_masks = PolygonMasks(resized_masks, *out_shape) - return resized_masks - - def flip(self, flip_direction='horizontal'): - """see :func:`BaseInstanceMasks.flip`""" - assert flip_direction in ('horizontal', 'vertical', 'diagonal') - if len(self.masks) == 0: - flipped_masks = PolygonMasks([], self.height, self.width) - else: - flipped_masks = [] - for poly_per_obj in self.masks: - flipped_poly_per_obj = [] - for p in poly_per_obj: - p = p.copy() - if flip_direction == 'horizontal': - p[0::2] = self.width - p[0::2] - elif flip_direction == 'vertical': - p[1::2] = self.height - p[1::2] - else: - p[0::2] = self.width - p[0::2] - p[1::2] = self.height - p[1::2] - flipped_poly_per_obj.append(p) - flipped_masks.append(flipped_poly_per_obj) - flipped_masks = PolygonMasks(flipped_masks, self.height, - self.width) - return flipped_masks - - def crop(self, bbox): - """see :func:`BaseInstanceMasks.crop`""" - assert isinstance(bbox, np.ndarray) - assert bbox.ndim == 1 - - # clip the boundary - bbox = bbox.copy() - bbox[0::2] = np.clip(bbox[0::2], 0, self.width) - bbox[1::2] = np.clip(bbox[1::2], 0, self.height) - x1, y1, x2, y2 = bbox - w = np.maximum(x2 - x1, 1) - h = np.maximum(y2 - y1, 1) - - if len(self.masks) == 0: - cropped_masks = PolygonMasks([], h, w) - else: - cropped_masks = [] - for poly_per_obj in self.masks: - cropped_poly_per_obj = [] - for p in poly_per_obj: - # pycocotools will clip the boundary - p = p.copy() - p[0::2] = p[0::2] - bbox[0] - p[1::2] = p[1::2] - bbox[1] - cropped_poly_per_obj.append(p) - cropped_masks.append(cropped_poly_per_obj) - cropped_masks = PolygonMasks(cropped_masks, h, w) - return cropped_masks - - def pad(self, out_shape, pad_val=0): - """padding has no effect on polygons`""" - return PolygonMasks(self.masks, *out_shape) - - def expand(self, *args, **kwargs): - """TODO: Add expand for polygon""" - raise NotImplementedError - - def crop_and_resize(self, - bboxes, - out_shape, - inds, - device='cpu', - interpolation='bilinear', - binarize=True): - """see :func:`BaseInstanceMasks.crop_and_resize`""" - out_h, out_w = out_shape - if len(self.masks) == 0: - return PolygonMasks([], out_h, out_w) - - if not binarize: - raise ValueError('Polygons are always binary, ' - 'setting binarize=False is unsupported') - - resized_masks = [] - for i in range(len(bboxes)): - mask = self.masks[inds[i]] - bbox = bboxes[i, :] - x1, y1, x2, y2 = bbox - w = np.maximum(x2 - x1, 1) - h = np.maximum(y2 - y1, 1) - h_scale = out_h / max(h, 0.1) # avoid too large scale - w_scale = out_w / max(w, 0.1) - - resized_mask = [] - for p in mask: - p = p.copy() - # crop - # pycocotools will clip the boundary - p[0::2] = p[0::2] - bbox[0] - p[1::2] = p[1::2] - bbox[1] - - # resize - p[0::2] = p[0::2] * w_scale - p[1::2] = p[1::2] * h_scale - resized_mask.append(p) - resized_masks.append(resized_mask) - return PolygonMasks(resized_masks, *out_shape) - - def translate(self, - out_shape, - offset, - direction='horizontal', - fill_val=None, - interpolation=None): - """Translate the PolygonMasks. - - Example: - >>> self = PolygonMasks.random(dtype=np.int) - >>> out_shape = (self.height, self.width) - >>> new = self.translate(out_shape, 4., direction='horizontal') - >>> assert np.all(new.masks[0][0][1::2] == self.masks[0][0][1::2]) - >>> assert np.all(new.masks[0][0][0::2] == self.masks[0][0][0::2] + 4) # noqa: E501 - """ - assert fill_val is None or fill_val == 0, 'Here fill_val is not '\ - f'used, and defaultly should be None or 0. got {fill_val}.' - if len(self.masks) == 0: - translated_masks = PolygonMasks([], *out_shape) - else: - translated_masks = [] - for poly_per_obj in self.masks: - translated_poly_per_obj = [] - for p in poly_per_obj: - p = p.copy() - if direction == 'horizontal': - p[0::2] = np.clip(p[0::2] + offset, 0, out_shape[1]) - elif direction == 'vertical': - p[1::2] = np.clip(p[1::2] + offset, 0, out_shape[0]) - translated_poly_per_obj.append(p) - translated_masks.append(translated_poly_per_obj) - translated_masks = PolygonMasks(translated_masks, *out_shape) - return translated_masks - - def shear(self, - out_shape, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """See :func:`BaseInstanceMasks.shear`.""" - if len(self.masks) == 0: - sheared_masks = PolygonMasks([], *out_shape) - else: - sheared_masks = [] - if direction == 'horizontal': - shear_matrix = np.stack([[1, magnitude], - [0, 1]]).astype(np.float32) - elif direction == 'vertical': - shear_matrix = np.stack([[1, 0], [magnitude, - 1]]).astype(np.float32) - for poly_per_obj in self.masks: - sheared_poly = [] - for p in poly_per_obj: - p = np.stack([p[0::2], p[1::2]], axis=0) # [2, n] - new_coords = np.matmul(shear_matrix, p) # [2, n] - new_coords[0, :] = np.clip(new_coords[0, :], 0, - out_shape[1]) - new_coords[1, :] = np.clip(new_coords[1, :], 0, - out_shape[0]) - sheared_poly.append( - new_coords.transpose((1, 0)).reshape(-1)) - sheared_masks.append(sheared_poly) - sheared_masks = PolygonMasks(sheared_masks, *out_shape) - return sheared_masks - - def rotate(self, out_shape, angle, center=None, scale=1.0, fill_val=0): - """See :func:`BaseInstanceMasks.rotate`.""" - if len(self.masks) == 0: - rotated_masks = PolygonMasks([], *out_shape) - else: - rotated_masks = [] - rotate_matrix = cv2.getRotationMatrix2D(center, -angle, scale) - for poly_per_obj in self.masks: - rotated_poly = [] - for p in poly_per_obj: - p = p.copy() - coords = np.stack([p[0::2], p[1::2]], axis=1) # [n, 2] - # pad 1 to convert from format [x, y] to homogeneous - # coordinates format [x, y, 1] - coords = np.concatenate( - (coords, np.ones((coords.shape[0], 1), coords.dtype)), - axis=1) # [n, 3] - rotated_coords = np.matmul( - rotate_matrix[None, :, :], - coords[:, :, None])[..., 0] # [n, 2, 1] -> [n, 2] - rotated_coords[:, 0] = np.clip(rotated_coords[:, 0], 0, - out_shape[1]) - rotated_coords[:, 1] = np.clip(rotated_coords[:, 1], 0, - out_shape[0]) - rotated_poly.append(rotated_coords.reshape(-1)) - rotated_masks.append(rotated_poly) - rotated_masks = PolygonMasks(rotated_masks, *out_shape) - return rotated_masks - - def to_bitmap(self): - """convert polygon masks to bitmap masks.""" - bitmap_masks = self.to_ndarray() - return BitmapMasks(bitmap_masks, self.height, self.width) - - @property - def areas(self): - """Compute areas of masks. - - This func is modified from `detectron2 - `_. - The function only works with Polygons using the shoelace formula. - - Return: - ndarray: areas of each instance - """ # noqa: W501 - area = [] - for polygons_per_obj in self.masks: - area_per_obj = 0 - for p in polygons_per_obj: - area_per_obj += self._polygon_area(p[0::2], p[1::2]) - area.append(area_per_obj) - return np.asarray(area) - - def _polygon_area(self, x, y): - """Compute the area of a component of a polygon. - - Using the shoelace formula: - https://stackoverflow.com/questions/24467972/calculate-area-of-polygon-given-x-y-coordinates - - Args: - x (ndarray): x coordinates of the component - y (ndarray): y coordinates of the component - - Return: - float: the are of the component - """ # noqa: 501 - return 0.5 * np.abs( - np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1))) - - def to_ndarray(self): - """Convert masks to the format of ndarray.""" - if len(self.masks) == 0: - return np.empty((0, self.height, self.width), dtype=np.uint8) - bitmap_masks = [] - for poly_per_obj in self.masks: - bitmap_masks.append( - polygon_to_bitmap(poly_per_obj, self.height, self.width)) - return np.stack(bitmap_masks) - - def to_tensor(self, dtype, device): - """See :func:`BaseInstanceMasks.to_tensor`.""" - if len(self.masks) == 0: - return torch.empty((0, self.height, self.width), - dtype=dtype, - device=device) - ndarray_masks = self.to_ndarray() - return torch.tensor(ndarray_masks, dtype=dtype, device=device) - - @classmethod - def random(cls, - num_masks=3, - height=32, - width=32, - n_verts=5, - dtype=np.float32, - rng=None): - """Generate random polygon masks for demo / testing purposes. - - Adapted from [1]_ - - References: - .. [1] https://gitlab.kitware.com/computer-vision/kwimage/-/blob/928cae35ca8/kwimage/structs/polygon.py#L379 # noqa: E501 - - Example: - >>> from mmdet.core.mask.structures import PolygonMasks - >>> self = PolygonMasks.random() - >>> print('self = {}'.format(self)) - """ - from mmdet.utils.util_random import ensure_rng - rng = ensure_rng(rng) - - def _gen_polygon(n, irregularity, spikeyness): - """Creates the polygon by sampling points on a circle around the - centre. Random noise is added by varying the angular spacing - between sequential points, and by varying the radial distance of - each point from the centre. - - Based on original code by Mike Ounsworth - - Args: - n (int): number of vertices - irregularity (float): [0,1] indicating how much variance there - is in the angular spacing of vertices. [0,1] will map to - [0, 2pi/numberOfVerts] - spikeyness (float): [0,1] indicating how much variance there is - in each vertex from the circle of radius aveRadius. [0,1] - will map to [0, aveRadius] - - Returns: - a list of vertices, in CCW order. - """ - from scipy.stats import truncnorm - - # Generate around the unit circle - cx, cy = (0.0, 0.0) - radius = 1 - - tau = np.pi * 2 - - irregularity = np.clip(irregularity, 0, 1) * 2 * np.pi / n - spikeyness = np.clip(spikeyness, 1e-9, 1) - - # generate n angle steps - lower = (tau / n) - irregularity - upper = (tau / n) + irregularity - angle_steps = rng.uniform(lower, upper, n) - - # normalize the steps so that point 0 and point n+1 are the same - k = angle_steps.sum() / (2 * np.pi) - angles = (angle_steps / k).cumsum() + rng.uniform(0, tau) - - # Convert high and low values to be wrt the standard normal range - # https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.truncnorm.html - low = 0 - high = 2 * radius - mean = radius - std = spikeyness - a = (low - mean) / std - b = (high - mean) / std - tnorm = truncnorm(a=a, b=b, loc=mean, scale=std) - - # now generate the points - radii = tnorm.rvs(n, random_state=rng) - x_pts = cx + radii * np.cos(angles) - y_pts = cy + radii * np.sin(angles) - - points = np.hstack([x_pts[:, None], y_pts[:, None]]) - - # Scale to 0-1 space - points = points - points.min(axis=0) - points = points / points.max(axis=0) - - # Randomly place within 0-1 space - points = points * (rng.rand() * .8 + .2) - min_pt = points.min(axis=0) - max_pt = points.max(axis=0) - - high = (1 - max_pt) - low = (0 - min_pt) - offset = (rng.rand(2) * (high - low)) + low - points = points + offset - return points - - def _order_vertices(verts): - """ - References: - https://stackoverflow.com/questions/1709283/how-can-i-sort-a-coordinate-list-for-a-rectangle-counterclockwise - """ - mlat = verts.T[0].sum() / len(verts) - mlng = verts.T[1].sum() / len(verts) - - tau = np.pi * 2 - angle = (np.arctan2(mlat - verts.T[0], verts.T[1] - mlng) + - tau) % tau - sortx = angle.argsort() - verts = verts.take(sortx, axis=0) - return verts - - # Generate a random exterior for each requested mask - masks = [] - for _ in range(num_masks): - exterior = _order_vertices(_gen_polygon(n_verts, 0.9, 0.9)) - exterior = (exterior * [(width, height)]).astype(dtype) - masks.append([exterior.ravel()]) - - self = cls(masks, height, width) - return self - - def get_bboxes(self): - num_masks = len(self) - boxes = np.zeros((num_masks, 4), dtype=np.float32) - for idx, poly_per_obj in enumerate(self.masks): - # simply use a number that is big enough for comparison with - # coordinates - xy_min = np.array([self.width * 2, self.height * 2], - dtype=np.float32) - xy_max = np.zeros(2, dtype=np.float32) - for p in poly_per_obj: - xy = np.array(p).reshape(-1, 2).astype(np.float32) - xy_min = np.minimum(xy_min, np.min(xy, axis=0)) - xy_max = np.maximum(xy_max, np.max(xy, axis=0)) - boxes[idx, :2] = xy_min - boxes[idx, 2:] = xy_max - - return boxes - - -def polygon_to_bitmap(polygons, height, width): - """Convert masks from the form of polygons to bitmaps. - - Args: - polygons (list[ndarray]): masks in polygon representation - height (int): mask height - width (int): mask width - - Return: - ndarray: the converted masks in bitmap representation - """ - rles = maskUtils.frPyObjects(polygons, height, width) - rle = maskUtils.merge(rles) - bitmap_mask = maskUtils.decode(rle).astype(np.bool) - return bitmap_mask - - -def bitmap_to_polygon(bitmap): - """Convert masks from the form of bitmaps to polygons. - - Args: - bitmap (ndarray): masks in bitmap representation. - - Return: - list[ndarray]: the converted mask in polygon representation. - bool: whether the mask has holes. - """ - bitmap = np.ascontiguousarray(bitmap).astype(np.uint8) - # cv2.RETR_CCOMP: retrieves all of the contours and organizes them - # into a two-level hierarchy. At the top level, there are external - # boundaries of the components. At the second level, there are - # boundaries of the holes. If there is another contour inside a hole - # of a connected component, it is still put at the top level. - # cv2.CHAIN_APPROX_NONE: stores absolutely all the contour points. - outs = cv2.findContours(bitmap, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE) - contours = outs[-2] - hierarchy = outs[-1] - if hierarchy is None: - return [], False - # hierarchy[i]: 4 elements, for the indexes of next, previous, - # parent, or nested contours. If there is no corresponding contour, - # it will be -1. - with_hole = (hierarchy.reshape(-1, 4)[:, 3] >= 0).any() - contours = [c.reshape(-1, 2) for c in contours] - return contours, with_hole diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/mask/utils.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/mask/utils.py deleted file mode 100644 index 90544b34f..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/mask/utils.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np -import pycocotools.mask as mask_util -import torch - - -def split_combined_polys(polys, poly_lens, polys_per_mask): - """Split the combined 1-D polys into masks. - - A mask is represented as a list of polys, and a poly is represented as - a 1-D array. In dataset, all masks are concatenated into a single 1-D - tensor. Here we need to split the tensor into original representations. - - Args: - polys (list): a list (length = image num) of 1-D tensors - poly_lens (list): a list (length = image num) of poly length - polys_per_mask (list): a list (length = image num) of poly number - of each mask - - Returns: - list: a list (length = image num) of list (length = mask num) of \ - list (length = poly num) of numpy array. - """ - mask_polys_list = [] - for img_id in range(len(polys)): - polys_single = polys[img_id] - polys_lens_single = poly_lens[img_id].tolist() - polys_per_mask_single = polys_per_mask[img_id].tolist() - - split_polys = mmcv.slice_list(polys_single, polys_lens_single) - mask_polys = mmcv.slice_list(split_polys, polys_per_mask_single) - mask_polys_list.append(mask_polys) - return mask_polys_list - - -# TODO: move this function to more proper place -def encode_mask_results(mask_results): - """Encode bitmap mask to RLE code. - - Args: - mask_results (list | tuple[list]): bitmap mask results. - In mask scoring rcnn, mask_results is a tuple of (segm_results, - segm_cls_score). - - Returns: - list | tuple: RLE encoded mask. - """ - if isinstance(mask_results, tuple): # mask scoring - cls_segms, cls_mask_scores = mask_results - else: - cls_segms = mask_results - num_classes = len(cls_segms) - encoded_mask_results = [[] for _ in range(num_classes)] - for i in range(len(cls_segms)): - for cls_segm in cls_segms[i]: - encoded_mask_results[i].append( - mask_util.encode( - np.array( - cls_segm[:, :, np.newaxis], order='F', - dtype='uint8'))[0]) # encoded with RLE - if isinstance(mask_results, tuple): - return encoded_mask_results, cls_mask_scores - else: - return encoded_mask_results - - -def mask2bbox(masks): - """Obtain tight bounding boxes of binary masks. - - Args: - masks (Tensor): Binary mask of shape (n, h, w). - - Returns: - Tensor: Bboxe with shape (n, 4) of \ - positive region in binary mask. - """ - N = masks.shape[0] - bboxes = masks.new_zeros((N, 4), dtype=torch.float32) - x_any = torch.any(masks, dim=1) - y_any = torch.any(masks, dim=2) - for i in range(N): - x = torch.where(x_any[i, :])[0] - y = torch.where(y_any[i, :])[0] - if len(x) > 0 and len(y) > 0: - bboxes[i, :] = bboxes.new_tensor( - [x[0], y[0], x[-1] + 1, y[-1] + 1]) - - return bboxes diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/optimizers/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/optimizers/__init__.py deleted file mode 100644 index e867d0761..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/optimizers/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import OPTIMIZER_BUILDERS, build_optimizer -from .layer_decay_optimizer_constructor import \ - LearningRateDecayOptimizerConstructor - -__all__ = [ - 'LearningRateDecayOptimizerConstructor', 'OPTIMIZER_BUILDERS', - 'build_optimizer' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/optimizers/builder.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/optimizers/builder.py deleted file mode 100644 index 406dd9b4b..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/optimizers/builder.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from mmcv.runner.optimizer import OPTIMIZER_BUILDERS as MMCV_OPTIMIZER_BUILDERS -from mmcv.utils import Registry, build_from_cfg - -OPTIMIZER_BUILDERS = Registry( - 'optimizer builder', parent=MMCV_OPTIMIZER_BUILDERS) - - -def build_optimizer_constructor(cfg): - constructor_type = cfg.get('type') - if constructor_type in OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - elif constructor_type in MMCV_OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, MMCV_OPTIMIZER_BUILDERS) - else: - raise KeyError(f'{constructor_type} is not registered ' - 'in the optimizer builder registry.') - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/optimizers/layer_decay_optimizer_constructor.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/optimizers/layer_decay_optimizer_constructor.py deleted file mode 100644 index 1bc3469e8..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/optimizers/layer_decay_optimizer_constructor.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -from mmcv.runner import DefaultOptimizerConstructor, get_dist_info - -from mmdet.utils import get_root_logger -from .builder import OPTIMIZER_BUILDERS - - -def get_layer_id_for_convnext(var_name, max_layer_id): - """Get the layer id to set the different learning rates in ``layer_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_layer_id (int): Maximum layer id. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - stage_id = int(var_name.split('.')[2]) - if stage_id == 0: - layer_id = 0 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - block_id = int(var_name.split('.')[3]) - if stage_id == 0: - layer_id = 1 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 + block_id // 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - else: - return max_layer_id + 1 - - -def get_stage_id_for_convnext(var_name, max_stage_id): - """Get the stage id to set the different learning rates in ``stage_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_stage_id (int): Maximum stage id. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - return 0 - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - return stage_id + 1 - else: - return max_stage_id - 1 - - -@OPTIMIZER_BUILDERS.register_module() -class LearningRateDecayOptimizerConstructor(DefaultOptimizerConstructor): - # Different learning rates are set for different layers of backbone. - # Note: Currently, this optimizer constructor is built for ConvNeXt. - - def add_params(self, params, module, **kwargs): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - """ - logger = get_root_logger() - - parameter_groups = {} - logger.info(f'self.paramwise_cfg is {self.paramwise_cfg}') - num_layers = self.paramwise_cfg.get('num_layers') + 2 - decay_rate = self.paramwise_cfg.get('decay_rate') - decay_type = self.paramwise_cfg.get('decay_type', 'layer_wise') - logger.info('Build LearningRateDecayOptimizerConstructor ' - f'{decay_type} {decay_rate} - {num_layers}') - weight_decay = self.base_wd - for name, param in module.named_parameters(): - if not param.requires_grad: - continue # frozen weights - if len(param.shape) == 1 or name.endswith('.bias') or name in ( - 'pos_embed', 'cls_token'): - group_name = 'no_decay' - this_weight_decay = 0. - else: - group_name = 'decay' - this_weight_decay = weight_decay - if 'layer_wise' in decay_type: - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_convnext( - name, self.paramwise_cfg.get('num_layers')) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - elif decay_type == 'stage_wise': - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_stage_id_for_convnext(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - group_name = f'layer_{layer_id}_{group_name}' - - if group_name not in parameter_groups: - scale = decay_rate**(num_layers - layer_id - 1) - - parameter_groups[group_name] = { - 'weight_decay': this_weight_decay, - 'params': [], - 'param_names': [], - 'lr_scale': scale, - 'group_name': group_name, - 'lr': scale * self.base_lr, - } - - parameter_groups[group_name]['params'].append(param) - parameter_groups[group_name]['param_names'].append(name) - rank, _ = get_dist_info() - if rank == 0: - to_display = {} - for key in parameter_groups: - to_display[key] = { - 'param_names': parameter_groups[key]['param_names'], - 'lr_scale': parameter_groups[key]['lr_scale'], - 'lr': parameter_groups[key]['lr'], - 'weight_decay': parameter_groups[key]['weight_decay'], - } - logger.info(f'Param groups = {json.dumps(to_display, indent=2)}') - params.extend(parameter_groups.values()) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/__init__.py deleted file mode 100644 index 00376bd49..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .bbox_nms import fast_nms, multiclass_nms -from .matrix_nms import mask_matrix_nms -from .merge_augs import (merge_aug_bboxes, merge_aug_masks, - merge_aug_proposals, merge_aug_scores) - -__all__ = [ - 'multiclass_nms', 'merge_aug_proposals', 'merge_aug_bboxes', - 'merge_aug_scores', 'merge_aug_masks', 'mask_matrix_nms', 'fast_nms' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/bbox_nms.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/bbox_nms.py deleted file mode 100644 index 4fcf57bb5..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/bbox_nms.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.ops.nms import batched_nms - -from mmdet.core.bbox.iou_calculators import bbox_overlaps - - -def multiclass_nms(multi_bboxes, - multi_scores, - score_thr, - nms_cfg, - max_num=-1, - score_factors=None, - return_inds=False): - """NMS for multi-class bboxes. - - Args: - multi_bboxes (Tensor): shape (n, #class*4) or (n, 4) - multi_scores (Tensor): shape (n, #class), where the last column - contains scores of the background class, but this will be ignored. - score_thr (float): bbox threshold, bboxes with scores lower than it - will not be considered. - nms_cfg (dict): a dict that contains the arguments of nms operations - max_num (int, optional): if there are more than max_num bboxes after - NMS, only top max_num will be kept. Default to -1. - score_factors (Tensor, optional): The factors multiplied to scores - before applying NMS. Default to None. - return_inds (bool, optional): Whether return the indices of kept - bboxes. Default to False. - - Returns: - tuple: (dets, labels, indices (optional)), tensors of shape (k, 5), - (k), and (k). Dets are boxes with scores. Labels are 0-based. - """ - num_classes = multi_scores.size(1) - 1 - # exclude background category - if multi_bboxes.shape[1] > 4: - bboxes = multi_bboxes.view(multi_scores.size(0), -1, 4) - else: - bboxes = multi_bboxes[:, None].expand( - multi_scores.size(0), num_classes, 4) - - scores = multi_scores[:, :-1] - - labels = torch.arange(num_classes, dtype=torch.long, device=scores.device) - labels = labels.view(1, -1).expand_as(scores) - - bboxes = bboxes.reshape(-1, 4) - scores = scores.reshape(-1) - labels = labels.reshape(-1) - - if not torch.onnx.is_in_onnx_export(): - # NonZero not supported in TensorRT - # remove low scoring boxes - valid_mask = scores > score_thr - # multiply score_factor after threshold to preserve more bboxes, improve - # mAP by 1% for YOLOv3 - if score_factors is not None: - # expand the shape to match original shape of score - score_factors = score_factors.view(-1, 1).expand( - multi_scores.size(0), num_classes) - score_factors = score_factors.reshape(-1) - scores = scores * score_factors - - if not torch.onnx.is_in_onnx_export(): - # NonZero not supported in TensorRT - inds = valid_mask.nonzero(as_tuple=False).squeeze(1) - bboxes, scores, labels = bboxes[inds], scores[inds], labels[inds] - else: - # TensorRT NMS plugin has invalid output filled with -1 - # add dummy data to make detection output correct. - bboxes = torch.cat([bboxes, bboxes.new_zeros(1, 4)], dim=0) - scores = torch.cat([scores, scores.new_zeros(1)], dim=0) - labels = torch.cat([labels, labels.new_zeros(1)], dim=0) - - if bboxes.numel() == 0: - if torch.onnx.is_in_onnx_export(): - raise RuntimeError('[ONNX Error] Can not record NMS ' - 'as it has not been executed this time') - dets = torch.cat([bboxes, scores[:, None]], -1) - if return_inds: - return dets, labels, inds - else: - return dets, labels - - dets, keep = batched_nms(bboxes, scores, labels, nms_cfg) - - if max_num > 0: - dets = dets[:max_num] - keep = keep[:max_num] - - if return_inds: - return dets, labels[keep], inds[keep] - else: - return dets, labels[keep] - - -def fast_nms(multi_bboxes, - multi_scores, - multi_coeffs, - score_thr, - iou_thr, - top_k, - max_num=-1): - """Fast NMS in `YOLACT `_. - - Fast NMS allows already-removed detections to suppress other detections so - that every instance can be decided to be kept or discarded in parallel, - which is not possible in traditional NMS. This relaxation allows us to - implement Fast NMS entirely in standard GPU-accelerated matrix operations. - - Args: - multi_bboxes (Tensor): shape (n, #class*4) or (n, 4) - multi_scores (Tensor): shape (n, #class+1), where the last column - contains scores of the background class, but this will be ignored. - multi_coeffs (Tensor): shape (n, #class*coeffs_dim). - score_thr (float): bbox threshold, bboxes with scores lower than it - will not be considered. - iou_thr (float): IoU threshold to be considered as conflicted. - top_k (int): if there are more than top_k bboxes before NMS, - only top top_k will be kept. - max_num (int): if there are more than max_num bboxes after NMS, - only top max_num will be kept. If -1, keep all the bboxes. - Default: -1. - - Returns: - tuple: (dets, labels, coefficients), tensors of shape (k, 5), (k, 1), - and (k, coeffs_dim). Dets are boxes with scores. - Labels are 0-based. - """ - - scores = multi_scores[:, :-1].t() # [#class, n] - scores, idx = scores.sort(1, descending=True) - - idx = idx[:, :top_k].contiguous() - scores = scores[:, :top_k] # [#class, topk] - num_classes, num_dets = idx.size() - boxes = multi_bboxes[idx.view(-1), :].view(num_classes, num_dets, 4) - coeffs = multi_coeffs[idx.view(-1), :].view(num_classes, num_dets, -1) - - iou = bbox_overlaps(boxes, boxes) # [#class, topk, topk] - iou.triu_(diagonal=1) - iou_max, _ = iou.max(dim=1) - - # Now just filter out the ones higher than the threshold - keep = iou_max <= iou_thr - - # Second thresholding introduces 0.2 mAP gain at negligible time cost - keep *= scores > score_thr - - # Assign each kept detection to its corresponding class - classes = torch.arange( - num_classes, device=boxes.device)[:, None].expand_as(keep) - classes = classes[keep] - - boxes = boxes[keep] - coeffs = coeffs[keep] - scores = scores[keep] - - # Only keep the top max_num highest scores across all classes - scores, idx = scores.sort(0, descending=True) - if max_num > 0: - idx = idx[:max_num] - scores = scores[:max_num] - - classes = classes[idx] - boxes = boxes[idx] - coeffs = coeffs[idx] - - cls_dets = torch.cat([boxes, scores[:, None]], dim=1) - return cls_dets, classes, coeffs diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/matrix_nms.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/matrix_nms.py deleted file mode 100644 index 9dc8c4f74..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/matrix_nms.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def mask_matrix_nms(masks, - labels, - scores, - filter_thr=-1, - nms_pre=-1, - max_num=-1, - kernel='gaussian', - sigma=2.0, - mask_area=None): - """Matrix NMS for multi-class masks. - - Args: - masks (Tensor): Has shape (num_instances, h, w) - labels (Tensor): Labels of corresponding masks, - has shape (num_instances,). - scores (Tensor): Mask scores of corresponding masks, - has shape (num_instances). - filter_thr (float): Score threshold to filter the masks - after matrix nms. Default: -1, which means do not - use filter_thr. - nms_pre (int): The max number of instances to do the matrix nms. - Default: -1, which means do not use nms_pre. - max_num (int, optional): If there are more than max_num masks after - matrix, only top max_num will be kept. Default: -1, which means - do not use max_num. - kernel (str): 'linear' or 'gaussian'. - sigma (float): std in gaussian method. - mask_area (Tensor): The sum of seg_masks. - - Returns: - tuple(Tensor): Processed mask results. - - - scores (Tensor): Updated scores, has shape (n,). - - labels (Tensor): Remained labels, has shape (n,). - - masks (Tensor): Remained masks, has shape (n, w, h). - - keep_inds (Tensor): The indices number of - the remaining mask in the input mask, has shape (n,). - """ - assert len(labels) == len(masks) == len(scores) - if len(labels) == 0: - return scores.new_zeros(0), labels.new_zeros(0), masks.new_zeros( - 0, *masks.shape[-2:]), labels.new_zeros(0) - if mask_area is None: - mask_area = masks.sum((1, 2)).float() - else: - assert len(masks) == len(mask_area) - - # sort and keep top nms_pre - scores, sort_inds = torch.sort(scores, descending=True) - - keep_inds = sort_inds - if nms_pre > 0 and len(sort_inds) > nms_pre: - sort_inds = sort_inds[:nms_pre] - keep_inds = keep_inds[:nms_pre] - scores = scores[:nms_pre] - masks = masks[sort_inds] - mask_area = mask_area[sort_inds] - labels = labels[sort_inds] - - num_masks = len(labels) - flatten_masks = masks.reshape(num_masks, -1).float() - # inter. - inter_matrix = torch.mm(flatten_masks, flatten_masks.transpose(1, 0)) - expanded_mask_area = mask_area.expand(num_masks, num_masks) - # Upper triangle iou matrix. - iou_matrix = (inter_matrix / - (expanded_mask_area + expanded_mask_area.transpose(1, 0) - - inter_matrix)).triu(diagonal=1) - # label_specific matrix. - expanded_labels = labels.expand(num_masks, num_masks) - # Upper triangle label matrix. - label_matrix = (expanded_labels == expanded_labels.transpose( - 1, 0)).triu(diagonal=1) - - # IoU compensation - compensate_iou, _ = (iou_matrix * label_matrix).max(0) - compensate_iou = compensate_iou.expand(num_masks, - num_masks).transpose(1, 0) - - # IoU decay - decay_iou = iou_matrix * label_matrix - - # Calculate the decay_coefficient - if kernel == 'gaussian': - decay_matrix = torch.exp(-1 * sigma * (decay_iou**2)) - compensate_matrix = torch.exp(-1 * sigma * (compensate_iou**2)) - decay_coefficient, _ = (decay_matrix / compensate_matrix).min(0) - elif kernel == 'linear': - decay_matrix = (1 - decay_iou) / (1 - compensate_iou) - decay_coefficient, _ = decay_matrix.min(0) - else: - raise NotImplementedError( - f'{kernel} kernel is not supported in matrix nms!') - # update the score. - scores = scores * decay_coefficient - - if filter_thr > 0: - keep = scores >= filter_thr - keep_inds = keep_inds[keep] - if not keep.any(): - return scores.new_zeros(0), labels.new_zeros(0), masks.new_zeros( - 0, *masks.shape[-2:]), labels.new_zeros(0) - masks = masks[keep] - scores = scores[keep] - labels = labels[keep] - - # sort and keep top max_num - scores, sort_inds = torch.sort(scores, descending=True) - keep_inds = keep_inds[sort_inds] - if max_num > 0 and len(sort_inds) > max_num: - sort_inds = sort_inds[:max_num] - keep_inds = keep_inds[:max_num] - scores = scores[:max_num] - masks = masks[sort_inds] - labels = labels[sort_inds] - - return scores, labels, masks, keep_inds diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/merge_augs.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/merge_augs.py deleted file mode 100644 index 2ac4603a1..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/post_processing/merge_augs.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings - -import numpy as np -import torch -from mmcv import ConfigDict -from mmcv.ops import nms - -from ..bbox import bbox_mapping_back - - -def merge_aug_proposals(aug_proposals, img_metas, cfg): - """Merge augmented proposals (multiscale, flip, etc.) - - Args: - aug_proposals (list[Tensor]): proposals from different testing - schemes, shape (n, 5). Note that they are not rescaled to the - original image size. - - img_metas (list[dict]): list of image info dict where each dict has: - 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmdet/datasets/pipelines/formatting.py:Collect`. - - cfg (dict): rpn test config. - - Returns: - Tensor: shape (n, 4), proposals corresponding to original image scale. - """ - - cfg = copy.deepcopy(cfg) - - # deprecate arguments warning - if 'nms' not in cfg or 'max_num' in cfg or 'nms_thr' in cfg: - warnings.warn( - 'In rpn_proposal or test_cfg, ' - 'nms_thr has been moved to a dict named nms as ' - 'iou_threshold, max_num has been renamed as max_per_img, ' - 'name of original arguments and the way to specify ' - 'iou_threshold of NMS will be deprecated.') - if 'nms' not in cfg: - cfg.nms = ConfigDict(dict(type='nms', iou_threshold=cfg.nms_thr)) - if 'max_num' in cfg: - if 'max_per_img' in cfg: - assert cfg.max_num == cfg.max_per_img, f'You set max_num and ' \ - f'max_per_img at the same time, but get {cfg.max_num} ' \ - f'and {cfg.max_per_img} respectively' \ - f'Please delete max_num which will be deprecated.' - else: - cfg.max_per_img = cfg.max_num - if 'nms_thr' in cfg: - assert cfg.nms.iou_threshold == cfg.nms_thr, f'You set ' \ - f'iou_threshold in nms and ' \ - f'nms_thr at the same time, but get ' \ - f'{cfg.nms.iou_threshold} and {cfg.nms_thr}' \ - f' respectively. Please delete the nms_thr ' \ - f'which will be deprecated.' - - recovered_proposals = [] - for proposals, img_info in zip(aug_proposals, img_metas): - img_shape = img_info['img_shape'] - scale_factor = img_info['scale_factor'] - flip = img_info['flip'] - flip_direction = img_info['flip_direction'] - _proposals = proposals.clone() - _proposals[:, :4] = bbox_mapping_back(_proposals[:, :4], img_shape, - scale_factor, flip, - flip_direction) - recovered_proposals.append(_proposals) - aug_proposals = torch.cat(recovered_proposals, dim=0) - merged_proposals, _ = nms(aug_proposals[:, :4].contiguous(), - aug_proposals[:, -1].contiguous(), - cfg.nms.iou_threshold) - scores = merged_proposals[:, 4] - _, order = scores.sort(0, descending=True) - num = min(cfg.max_per_img, merged_proposals.shape[0]) - order = order[:num] - merged_proposals = merged_proposals[order, :] - return merged_proposals - - -def merge_aug_bboxes(aug_bboxes, aug_scores, img_metas, rcnn_test_cfg): - """Merge augmented detection bboxes and scores. - - Args: - aug_bboxes (list[Tensor]): shape (n, 4*#class) - aug_scores (list[Tensor] or None): shape (n, #class) - img_shapes (list[Tensor]): shape (3, ). - rcnn_test_cfg (dict): rcnn test config. - - Returns: - tuple: (bboxes, scores) - """ - recovered_bboxes = [] - for bboxes, img_info in zip(aug_bboxes, img_metas): - img_shape = img_info[0]['img_shape'] - scale_factor = img_info[0]['scale_factor'] - flip = img_info[0]['flip'] - flip_direction = img_info[0]['flip_direction'] - bboxes = bbox_mapping_back(bboxes, img_shape, scale_factor, flip, - flip_direction) - recovered_bboxes.append(bboxes) - bboxes = torch.stack(recovered_bboxes).mean(dim=0) - if aug_scores is None: - return bboxes - else: - scores = torch.stack(aug_scores).mean(dim=0) - return bboxes, scores - - -def merge_aug_scores(aug_scores): - """Merge augmented bbox scores.""" - if isinstance(aug_scores[0], torch.Tensor): - return torch.mean(torch.stack(aug_scores), dim=0) - else: - return np.mean(aug_scores, axis=0) - - -def merge_aug_masks(aug_masks, img_metas, rcnn_test_cfg, weights=None): - """Merge augmented mask prediction. - - Args: - aug_masks (list[ndarray]): shape (n, #class, h, w) - img_shapes (list[ndarray]): shape (3, ). - rcnn_test_cfg (dict): rcnn test config. - - Returns: - tuple: (bboxes, scores) - """ - recovered_masks = [] - for mask, img_info in zip(aug_masks, img_metas): - flip = img_info[0]['flip'] - if flip: - flip_direction = img_info[0]['flip_direction'] - if flip_direction == 'horizontal': - mask = mask[:, :, :, ::-1] - elif flip_direction == 'vertical': - mask = mask[:, :, ::-1, :] - elif flip_direction == 'diagonal': - mask = mask[:, :, :, ::-1] - mask = mask[:, :, ::-1, :] - else: - raise ValueError( - f"Invalid flipping direction '{flip_direction}'") - recovered_masks.append(mask) - - if weights is None: - merged_masks = np.mean(recovered_masks, axis=0) - else: - merged_masks = np.average( - np.array(recovered_masks), axis=0, weights=np.array(weights)) - return merged_masks diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/utils/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/utils/__init__.py deleted file mode 100644 index 3f0d07081..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/utils/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .dist_utils import (DistOptimizerHook, all_reduce_dict, allreduce_grads, - reduce_mean, sync_random_seed) -from .misc import (center_of_mass, filter_scores_and_topk, flip_tensor, - generate_coordinate, mask2ndarray, multi_apply, - select_single_mlvl, unmap) - -__all__ = [ - 'allreduce_grads', 'DistOptimizerHook', 'reduce_mean', 'multi_apply', - 'unmap', 'mask2ndarray', 'flip_tensor', 'all_reduce_dict', - 'center_of_mass', 'generate_coordinate', 'select_single_mlvl', - 'filter_scores_and_topk', 'sync_random_seed' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/utils/dist_utils.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/utils/dist_utils.py deleted file mode 100644 index 8760774fd..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/utils/dist_utils.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import pickle -import warnings -from collections import OrderedDict - -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import OptimizerHook, get_dist_info -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - world_size = dist.get_world_size() - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -class DistOptimizerHook(OptimizerHook): - """Deprecated optimizer hook for distributed training.""" - - def __init__(self, *args, **kwargs): - warnings.warn('"DistOptimizerHook" is deprecated, please switch to' - '"mmcv.runner.OptimizerHook".') - super().__init__(*args, **kwargs) - - -def reduce_mean(tensor): - """"Obtain the mean of tensor on different GPUs.""" - if not (dist.is_available() and dist.is_initialized()): - return tensor - tensor = tensor.clone() - dist.all_reduce(tensor.div_(dist.get_world_size()), op=dist.ReduceOp.SUM) - return tensor - - -def obj2tensor(pyobj, device='cuda'): - """Serialize picklable python object to tensor.""" - storage = torch.ByteStorage.from_buffer(pickle.dumps(pyobj)) - return torch.ByteTensor(storage).to(device=device) - - -def tensor2obj(tensor): - """Deserialize tensor to picklable python object.""" - return pickle.loads(tensor.cpu().numpy().tobytes()) - - -@functools.lru_cache() -def _get_global_gloo_group(): - """Return a process group based on gloo backend, containing all the ranks - The result is cached.""" - if dist.get_backend() == 'nccl': - return dist.new_group(backend='gloo') - else: - return dist.group.WORLD - - -def all_reduce_dict(py_dict, op='sum', group=None, to_float=True): - """Apply all reduce function for python dict object. - - The code is modified from https://github.com/Megvii- - BaseDetection/YOLOX/blob/main/yolox/utils/allreduce_norm.py. - - NOTE: make sure that py_dict in different ranks has the same keys and - the values should be in the same shape. Currently only supports - nccl backend. - - Args: - py_dict (dict): Dict to be applied all reduce op. - op (str): Operator, could be 'sum' or 'mean'. Default: 'sum' - group (:obj:`torch.distributed.group`, optional): Distributed group, - Default: None. - to_float (bool): Whether to convert all values of dict to float. - Default: True. - - Returns: - OrderedDict: reduced python dict object. - """ - warnings.warn( - 'group` is deprecated. Currently only supports NCCL backend.') - _, world_size = get_dist_info() - if world_size == 1: - return py_dict - - # all reduce logic across different devices. - py_key = list(py_dict.keys()) - if not isinstance(py_dict, OrderedDict): - py_key_tensor = obj2tensor(py_key) - dist.broadcast(py_key_tensor, src=0) - py_key = tensor2obj(py_key_tensor) - - tensor_shapes = [py_dict[k].shape for k in py_key] - tensor_numels = [py_dict[k].numel() for k in py_key] - - if to_float: - warnings.warn('Note: the "to_float" is True, you need to ' - 'ensure that the behavior is reasonable.') - flatten_tensor = torch.cat( - [py_dict[k].flatten().float() for k in py_key]) - else: - flatten_tensor = torch.cat([py_dict[k].flatten() for k in py_key]) - - dist.all_reduce(flatten_tensor, op=dist.ReduceOp.SUM) - if op == 'mean': - flatten_tensor /= world_size - - split_tensors = [ - x.reshape(shape) for x, shape in zip( - torch.split(flatten_tensor, tensor_numels), tensor_shapes) - ] - out_dict = {k: v for k, v in zip(py_key, split_tensors)} - if isinstance(py_dict, OrderedDict): - out_dict = OrderedDict(out_dict) - return out_dict - - -def sync_random_seed(seed=None, device='cuda'): - """Make sure different ranks share the same seed. - - All workers must call this function, otherwise it will deadlock. - This method is generally used in `DistributedSampler`, - because the seed should be identical across all processes - in the distributed group. - - In distributed sampling, different ranks should sample non-overlapped - data in the dataset. Therefore, this function is used to make sure that - each rank shuffles the data indices in the same order based - on the same seed. Then different ranks could use different indices - to select non-overlapped data from the same data list. - - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - - Returns: - int: Seed to be used. - """ - if seed is None: - seed = np.random.randint(2**31) - assert isinstance(seed, int) - - rank, world_size = get_dist_info() - - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/utils/misc.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/utils/misc.py deleted file mode 100644 index 14cb745e3..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/utils/misc.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import numpy as np -import torch -from six.moves import map, zip - -from ..mask.structures import BitmapMasks, PolygonMasks - - -def multi_apply(func, *args, **kwargs): - """Apply function to a list of arguments. - - Note: - This function applies the ``func`` to multiple inputs and - map the multiple outputs of the ``func`` into different - list. Each list contains the same type of outputs corresponding - to different inputs. - - Args: - func (Function): A function that will be applied to a list of - arguments - - Returns: - tuple(list): A tuple containing multiple list, each list contains \ - a kind of returned results by the function - """ - pfunc = partial(func, **kwargs) if kwargs else func - map_results = map(pfunc, *args) - return tuple(map(list, zip(*map_results))) - - -def unmap(data, count, inds, fill=0): - """Unmap a subset of item (data) back to the original set of items (of size - count)""" - if data.dim() == 1: - ret = data.new_full((count, ), fill) - ret[inds.type(torch.bool)] = data - else: - new_size = (count, ) + data.size()[1:] - ret = data.new_full(new_size, fill) - ret[inds.type(torch.bool), :] = data - return ret - - -def mask2ndarray(mask): - """Convert Mask to ndarray.. - - Args: - mask (:obj:`BitmapMasks` or :obj:`PolygonMasks` or - torch.Tensor or np.ndarray): The mask to be converted. - - Returns: - np.ndarray: Ndarray mask of shape (n, h, w) that has been converted - """ - if isinstance(mask, (BitmapMasks, PolygonMasks)): - mask = mask.to_ndarray() - elif isinstance(mask, torch.Tensor): - mask = mask.detach().cpu().numpy() - elif not isinstance(mask, np.ndarray): - raise TypeError(f'Unsupported {type(mask)} data type') - return mask - - -def flip_tensor(src_tensor, flip_direction): - """flip tensor base on flip_direction. - - Args: - src_tensor (Tensor): input feature map, shape (B, C, H, W). - flip_direction (str): The flipping direction. Options are - 'horizontal', 'vertical', 'diagonal'. - - Returns: - out_tensor (Tensor): Flipped tensor. - """ - assert src_tensor.ndim == 4 - valid_directions = ['horizontal', 'vertical', 'diagonal'] - assert flip_direction in valid_directions - if flip_direction == 'horizontal': - out_tensor = torch.flip(src_tensor, [3]) - elif flip_direction == 'vertical': - out_tensor = torch.flip(src_tensor, [2]) - else: - out_tensor = torch.flip(src_tensor, [2, 3]) - return out_tensor - - -def select_single_mlvl(mlvl_tensors, batch_id, detach=True): - """Extract a multi-scale single image tensor from a multi-scale batch - tensor based on batch index. - - Note: The default value of detach is True, because the proposal gradient - needs to be detached during the training of the two-stage model. E.g - Cascade Mask R-CNN. - - Args: - mlvl_tensors (list[Tensor]): Batch tensor for all scale levels, - each is a 4D-tensor. - batch_id (int): Batch index. - detach (bool): Whether detach gradient. Default True. - - Returns: - list[Tensor]: Multi-scale single image tensor. - """ - assert isinstance(mlvl_tensors, (list, tuple)) - num_levels = len(mlvl_tensors) - - if detach: - mlvl_tensor_list = [ - mlvl_tensors[i][batch_id].detach() for i in range(num_levels) - ] - else: - mlvl_tensor_list = [ - mlvl_tensors[i][batch_id] for i in range(num_levels) - ] - return mlvl_tensor_list - - -def filter_scores_and_topk(scores, score_thr, topk, results=None): - """Filter results using score threshold and topk candidates. - - Args: - scores (Tensor): The scores, shape (num_bboxes, K). - score_thr (float): The score filter threshold. - topk (int): The number of topk candidates. - results (dict or list or Tensor, Optional): The results to - which the filtering rule is to be applied. The shape - of each item is (num_bboxes, N). - - Returns: - tuple: Filtered results - - - scores (Tensor): The scores after being filtered, \ - shape (num_bboxes_filtered, ). - - labels (Tensor): The class labels, shape \ - (num_bboxes_filtered, ). - - anchor_idxs (Tensor): The anchor indexes, shape \ - (num_bboxes_filtered, ). - - filtered_results (dict or list or Tensor, Optional): \ - The filtered results. The shape of each item is \ - (num_bboxes_filtered, N). - """ - valid_mask = scores > score_thr - scores = scores[valid_mask] - valid_idxs = torch.nonzero(valid_mask) - - num_topk = min(topk, valid_idxs.size(0)) - # torch.sort is actually faster than .topk (at least on GPUs) - scores, idxs = scores.sort(descending=True) - scores = scores[:num_topk] - topk_idxs = valid_idxs[idxs[:num_topk]] - keep_idxs, labels = topk_idxs.unbind(dim=1) - - filtered_results = None - if results is not None: - if isinstance(results, dict): - filtered_results = {k: v[keep_idxs] for k, v in results.items()} - elif isinstance(results, list): - filtered_results = [result[keep_idxs] for result in results] - elif isinstance(results, torch.Tensor): - filtered_results = results[keep_idxs] - else: - raise NotImplementedError(f'Only supports dict or list or Tensor, ' - f'but get {type(results)}.') - return scores, labels, keep_idxs, filtered_results - - -def center_of_mass(mask, esp=1e-6): - """Calculate the centroid coordinates of the mask. - - Args: - mask (Tensor): The mask to be calculated, shape (h, w). - esp (float): Avoid dividing by zero. Default: 1e-6. - - Returns: - tuple[Tensor]: the coordinates of the center point of the mask. - - - center_h (Tensor): the center point of the height. - - center_w (Tensor): the center point of the width. - """ - h, w = mask.shape - grid_h = torch.arange(h, device=mask.device)[:, None] - grid_w = torch.arange(w, device=mask.device) - normalizer = mask.sum().float().clamp(min=esp) - center_h = (mask * grid_h).sum() / normalizer - center_w = (mask * grid_w).sum() / normalizer - return center_h, center_w - - -def generate_coordinate(featmap_sizes, device='cuda'): - """Generate the coordinate. - - Args: - featmap_sizes (tuple): The feature to be calculated, - of shape (N, C, W, H). - device (str): The device where the feature will be put on. - Returns: - coord_feat (Tensor): The coordinate feature, of shape (N, 2, W, H). - """ - - x_range = torch.linspace(-1, 1, featmap_sizes[-1], device=device) - y_range = torch.linspace(-1, 1, featmap_sizes[-2], device=device) - y, x = torch.meshgrid(y_range, x_range) - y = y.expand([featmap_sizes[0], 1, -1, -1]) - x = x.expand([featmap_sizes[0], 1, -1, -1]) - coord_feat = torch.cat([x, y], 1) - - return coord_feat diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/visualization/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/visualization/__init__.py deleted file mode 100644 index 2eb17c4b3..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/visualization/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .image import (color_val_matplotlib, imshow_det_bboxes, - imshow_gt_det_bboxes) -from .palette import get_palette, palette_val - -__all__ = [ - 'imshow_det_bboxes', 'imshow_gt_det_bboxes', 'color_val_matplotlib', - 'palette_val', 'get_palette' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/visualization/image.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/visualization/image.py deleted file mode 100644 index 43bebf97f..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/visualization/image.py +++ /dev/null @@ -1,559 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import matplotlib.pyplot as plt -import mmcv -import numpy as np -import pycocotools.mask as mask_util -from matplotlib.collections import PatchCollection -from matplotlib.patches import Polygon - -from mmdet.core.evaluation.panoptic_utils import INSTANCE_OFFSET -from ..mask.structures import bitmap_to_polygon -from ..utils import mask2ndarray -from .palette import get_palette, palette_val - -__all__ = [ - 'color_val_matplotlib', 'draw_masks', 'draw_bboxes', 'draw_labels', - 'imshow_det_bboxes', 'imshow_gt_det_bboxes' -] - -EPS = 1e-2 - - -def color_val_matplotlib(color): - """Convert various input in BGR order to normalized RGB matplotlib color - tuples. - - Args: - color (:obj`Color` | str | tuple | int | ndarray): Color inputs. - - Returns: - tuple[float]: A tuple of 3 normalized floats indicating RGB channels. - """ - color = mmcv.color_val(color) - color = [color / 255 for color in color[::-1]] - return tuple(color) - - -def _get_adaptive_scales(areas, min_area=800, max_area=30000): - """Get adaptive scales according to areas. - - The scale range is [0.5, 1.0]. When the area is less than - ``'min_area'``, the scale is 0.5 while the area is larger than - ``'max_area'``, the scale is 1.0. - - Args: - areas (ndarray): The areas of bboxes or masks with the - shape of (n, ). - min_area (int): Lower bound areas for adaptive scales. - Default: 800. - max_area (int): Upper bound areas for adaptive scales. - Default: 30000. - - Returns: - ndarray: The adaotive scales with the shape of (n, ). - """ - scales = 0.5 + (areas - min_area) / (max_area - min_area) - scales = np.clip(scales, 0.5, 1.0) - return scales - - -def _get_bias_color(base, max_dist=30): - """Get different colors for each masks. - - Get different colors for each masks by adding a bias - color to the base category color. - Args: - base (ndarray): The base category color with the shape - of (3, ). - max_dist (int): The max distance of bias. Default: 30. - - Returns: - ndarray: The new color for a mask with the shape of (3, ). - """ - new_color = base + np.random.randint( - low=-max_dist, high=max_dist + 1, size=3) - return np.clip(new_color, 0, 255, new_color) - - -def draw_bboxes(ax, bboxes, color='g', alpha=0.8, thickness=2): - """Draw bounding boxes on the axes. - - Args: - ax (matplotlib.Axes): The input axes. - bboxes (ndarray): The input bounding boxes with the shape - of (n, 4). - color (list[tuple] | matplotlib.color): the colors for each - bounding boxes. - alpha (float): Transparency of bounding boxes. Default: 0.8. - thickness (int): Thickness of lines. Default: 2. - - Returns: - matplotlib.Axes: The result axes. - """ - polygons = [] - for i, bbox in enumerate(bboxes): - bbox_int = bbox.astype(np.int32) - poly = [[bbox_int[0], bbox_int[1]], [bbox_int[0], bbox_int[3]], - [bbox_int[2], bbox_int[3]], [bbox_int[2], bbox_int[1]]] - np_poly = np.array(poly).reshape((4, 2)) - polygons.append(Polygon(np_poly)) - p = PatchCollection( - polygons, - facecolor='none', - edgecolors=color, - linewidths=thickness, - alpha=alpha) - ax.add_collection(p) - - return ax - - -def draw_labels(ax, - labels, - positions, - scores=None, - class_names=None, - color='w', - font_size=8, - scales=None, - horizontal_alignment='left'): - """Draw labels on the axes. - - Args: - ax (matplotlib.Axes): The input axes. - labels (ndarray): The labels with the shape of (n, ). - positions (ndarray): The positions to draw each labels. - scores (ndarray): The scores for each labels. - class_names (list[str]): The class names. - color (list[tuple] | matplotlib.color): The colors for labels. - font_size (int): Font size of texts. Default: 8. - scales (list[float]): Scales of texts. Default: None. - horizontal_alignment (str): The horizontal alignment method of - texts. Default: 'left'. - - Returns: - matplotlib.Axes: The result axes. - """ - for i, (pos, label) in enumerate(zip(positions, labels)): - label_text = class_names[ - label] if class_names is not None else f'class {label}' - if scores is not None: - label_text += f'|{scores[i]:.02f}' - text_color = color[i] if isinstance(color, list) else color - - font_size_mask = font_size if scales is None else font_size * scales[i] - ax.text( - pos[0], - pos[1], - f'{label_text}', - bbox={ - 'facecolor': 'black', - 'alpha': 0.8, - 'pad': 0.7, - 'edgecolor': 'none' - }, - color=text_color, - fontsize=font_size_mask, - verticalalignment='top', - horizontalalignment=horizontal_alignment) - - return ax - - -def draw_masks(ax, img, masks, color=None, with_edge=True, alpha=0.8): - """Draw masks on the image and their edges on the axes. - - Args: - ax (matplotlib.Axes): The input axes. - img (ndarray): The image with the shape of (3, h, w). - masks (ndarray): The masks with the shape of (n, h, w). - color (ndarray): The colors for each masks with the shape - of (n, 3). - with_edge (bool): Whether to draw edges. Default: True. - alpha (float): Transparency of bounding boxes. Default: 0.8. - - Returns: - matplotlib.Axes: The result axes. - ndarray: The result image. - """ - taken_colors = set([0, 0, 0]) - if color is None: - random_colors = np.random.randint(0, 255, (masks.size(0), 3)) - color = [tuple(c) for c in random_colors] - color = np.array(color, dtype=np.uint8) - polygons = [] - for i, mask in enumerate(masks): - if with_edge: - contours, _ = bitmap_to_polygon(mask) - polygons += [Polygon(c) for c in contours] - - color_mask = color[i] - while tuple(color_mask) in taken_colors: - color_mask = _get_bias_color(color_mask) - taken_colors.add(tuple(color_mask)) - - mask = mask.astype(bool) - img[mask] = img[mask] * (1 - alpha) + color_mask * alpha - - p = PatchCollection( - polygons, facecolor='none', edgecolors='w', linewidths=1, alpha=0.8) - ax.add_collection(p) - - return ax, img - - -def imshow_det_bboxes(img, - bboxes=None, - labels=None, - segms=None, - class_names=None, - score_thr=0, - bbox_color='green', - text_color='green', - mask_color=None, - thickness=2, - font_size=8, - win_name='', - show=True, - wait_time=0, - out_file=None): - """Draw bboxes and class labels (with scores) on an image. - - Args: - img (str | ndarray): The image to be displayed. - bboxes (ndarray): Bounding boxes (with scores), shaped (n, 4) or - (n, 5). - labels (ndarray): Labels of bboxes. - segms (ndarray | None): Masks, shaped (n,h,w) or None. - class_names (list[str]): Names of each classes. - score_thr (float): Minimum score of bboxes to be shown. Default: 0. - bbox_color (list[tuple] | tuple | str | None): Colors of bbox lines. - If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: 'green'. - text_color (list[tuple] | tuple | str | None): Colors of texts. - If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: 'green'. - mask_color (list[tuple] | tuple | str | None, optional): Colors of - masks. If a single color is given, it will be applied to all - classes. The tuple of color should be in RGB order. - Default: None. - thickness (int): Thickness of lines. Default: 2. - font_size (int): Font size of texts. Default: 13. - show (bool): Whether to show the image. Default: True. - win_name (str): The window name. Default: ''. - wait_time (float): Value of waitKey param. Default: 0. - out_file (str, optional): The filename to write the image. - Default: None. - - Returns: - ndarray: The image with bboxes drawn on it. - """ - assert bboxes is None or bboxes.ndim == 2, \ - f' bboxes ndim should be 2, but its ndim is {bboxes.ndim}.' - assert labels.ndim == 1, \ - f' labels ndim should be 1, but its ndim is {labels.ndim}.' - assert bboxes is None or bboxes.shape[1] == 4 or bboxes.shape[1] == 5, \ - f' bboxes.shape[1] should be 4 or 5, but its {bboxes.shape[1]}.' - assert bboxes is None or bboxes.shape[0] <= labels.shape[0], \ - 'labels.shape[0] should not be less than bboxes.shape[0].' - assert segms is None or segms.shape[0] == labels.shape[0], \ - 'segms.shape[0] and labels.shape[0] should have the same length.' - assert segms is not None or bboxes is not None, \ - 'segms and bboxes should not be None at the same time.' - - img = mmcv.imread(img).astype(np.uint8) - - if score_thr > 0: - assert bboxes is not None and bboxes.shape[1] == 5 - scores = bboxes[:, -1] - inds = scores > score_thr - bboxes = bboxes[inds, :] - labels = labels[inds] - if segms is not None: - segms = segms[inds, ...] - - img = mmcv.bgr2rgb(img) - width, height = img.shape[1], img.shape[0] - img = np.ascontiguousarray(img) - - fig = plt.figure(win_name, frameon=False) - plt.title(win_name) - canvas = fig.canvas - dpi = fig.get_dpi() - # add a small EPS to avoid precision lost due to matplotlib's truncation - # (https://github.com/matplotlib/matplotlib/issues/15363) - fig.set_size_inches((width + EPS) / dpi, (height + EPS) / dpi) - - # remove white edges by set subplot margin - plt.subplots_adjust(left=0, right=1, bottom=0, top=1) - ax = plt.gca() - ax.axis('off') - - max_label = int(max(labels) if len(labels) > 0 else 0) - text_palette = palette_val(get_palette(text_color, max_label + 1)) - text_colors = [text_palette[label] for label in labels] - - num_bboxes = 0 - if bboxes is not None: - num_bboxes = bboxes.shape[0] - bbox_palette = palette_val(get_palette(bbox_color, max_label + 1)) - colors = [bbox_palette[label] for label in labels[:num_bboxes]] - draw_bboxes(ax, bboxes, colors, alpha=0.8, thickness=thickness) - - horizontal_alignment = 'left' - positions = bboxes[:, :2].astype(np.int32) + thickness - areas = (bboxes[:, 3] - bboxes[:, 1]) * (bboxes[:, 2] - bboxes[:, 0]) - scales = _get_adaptive_scales(areas) - scores = bboxes[:, 4] if bboxes.shape[1] == 5 else None - draw_labels( - ax, - labels[:num_bboxes], - positions, - scores=scores, - class_names=class_names, - color=text_colors, - font_size=font_size, - scales=scales, - horizontal_alignment=horizontal_alignment) - - if segms is not None: - mask_palette = get_palette(mask_color, max_label + 1) - colors = [mask_palette[label] for label in labels] - colors = np.array(colors, dtype=np.uint8) - draw_masks(ax, img, segms, colors, with_edge=True) - - if num_bboxes < segms.shape[0]: - segms = segms[num_bboxes:] - horizontal_alignment = 'center' - areas = [] - positions = [] - for mask in segms: - _, _, stats, centroids = cv2.connectedComponentsWithStats( - mask.astype(np.uint8), connectivity=8) - largest_id = np.argmax(stats[1:, -1]) + 1 - positions.append(centroids[largest_id]) - areas.append(stats[largest_id, -1]) - areas = np.stack(areas, axis=0) - scales = _get_adaptive_scales(areas) - draw_labels( - ax, - labels[num_bboxes:], - positions, - class_names=class_names, - color=text_colors, - font_size=font_size, - scales=scales, - horizontal_alignment=horizontal_alignment) - - plt.imshow(img) - - stream, _ = canvas.print_to_buffer() - buffer = np.frombuffer(stream, dtype='uint8') - img_rgba = buffer.reshape(height, width, 4) - rgb, alpha = np.split(img_rgba, [3], axis=2) - img = rgb.astype('uint8') - img = mmcv.rgb2bgr(img) - - if show: - # We do not use cv2 for display because in some cases, opencv will - # conflict with Qt, it will output a warning: Current thread - # is not the object's thread. You can refer to - # https://github.com/opencv/opencv-python/issues/46 for details - if wait_time == 0: - plt.show() - else: - plt.show(block=False) - plt.pause(wait_time) - if out_file is not None: - mmcv.imwrite(img, out_file) - - plt.close() - - return img - - -def imshow_gt_det_bboxes(img, - annotation, - result, - class_names=None, - score_thr=0, - gt_bbox_color=(61, 102, 255), - gt_text_color=(200, 200, 200), - gt_mask_color=(61, 102, 255), - det_bbox_color=(241, 101, 72), - det_text_color=(200, 200, 200), - det_mask_color=(241, 101, 72), - thickness=2, - font_size=13, - win_name='', - show=True, - wait_time=0, - out_file=None, - overlay_gt_pred=True): - """General visualization GT and result function. - - Args: - img (str | ndarray): The image to be displayed. - annotation (dict): Ground truth annotations where contain keys of - 'gt_bboxes' and 'gt_labels' or 'gt_masks'. - result (tuple[list] | list): The detection result, can be either - (bbox, segm) or just bbox. - class_names (list[str]): Names of each classes. - score_thr (float): Minimum score of bboxes to be shown. Default: 0. - gt_bbox_color (list[tuple] | tuple | str | None): Colors of bbox lines. - If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: (61, 102, 255). - gt_text_color (list[tuple] | tuple | str | None): Colors of texts. - If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: (200, 200, 200). - gt_mask_color (list[tuple] | tuple | str | None, optional): Colors of - masks. If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: (61, 102, 255). - det_bbox_color (list[tuple] | tuple | str | None):Colors of bbox lines. - If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: (241, 101, 72). - det_text_color (list[tuple] | tuple | str | None):Colors of texts. - If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: (200, 200, 200). - det_mask_color (list[tuple] | tuple | str | None, optional): Color of - masks. If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: (241, 101, 72). - thickness (int): Thickness of lines. Default: 2. - font_size (int): Font size of texts. Default: 13. - win_name (str): The window name. Default: ''. - show (bool): Whether to show the image. Default: True. - wait_time (float): Value of waitKey param. Default: 0. - out_file (str, optional): The filename to write the image. - Default: None. - overlay_gt_pred (bool): Whether to plot gts and predictions on the - same image. If False, predictions and gts will be plotted on two same - image which will be concatenated in vertical direction. The image - above is drawn with gt, and the image below is drawn with the - prediction result. Default: True. - - Returns: - ndarray: The image with bboxes or masks drawn on it. - """ - assert 'gt_bboxes' in annotation - assert 'gt_labels' in annotation - assert isinstance(result, (tuple, list, dict)), 'Expected ' \ - f'tuple or list or dict, but get {type(result)}' - - gt_bboxes = annotation['gt_bboxes'] - gt_labels = annotation['gt_labels'] - gt_masks = annotation.get('gt_masks', None) - if gt_masks is not None: - gt_masks = mask2ndarray(gt_masks) - - gt_seg = annotation.get('gt_semantic_seg', None) - if gt_seg is not None: - pad_value = 255 # the padding value of gt_seg - sem_labels = np.unique(gt_seg) - all_labels = np.concatenate((gt_labels, sem_labels), axis=0) - all_labels, counts = np.unique(all_labels, return_counts=True) - stuff_labels = all_labels[np.logical_and(counts < 2, - all_labels != pad_value)] - stuff_masks = gt_seg[None] == stuff_labels[:, None, None] - gt_labels = np.concatenate((gt_labels, stuff_labels), axis=0) - gt_masks = np.concatenate((gt_masks, stuff_masks.astype(np.uint8)), - axis=0) - # If you need to show the bounding boxes, - # please comment the following line - # gt_bboxes = None - - img = mmcv.imread(img) - - img_with_gt = imshow_det_bboxes( - img, - gt_bboxes, - gt_labels, - gt_masks, - class_names=class_names, - bbox_color=gt_bbox_color, - text_color=gt_text_color, - mask_color=gt_mask_color, - thickness=thickness, - font_size=font_size, - win_name=win_name, - show=False) - - if not isinstance(result, dict): - if isinstance(result, tuple): - bbox_result, segm_result = result - if isinstance(segm_result, tuple): - segm_result = segm_result[0] # ms rcnn - else: - bbox_result, segm_result = result, None - - bboxes = np.vstack(bbox_result) - labels = [ - np.full(bbox.shape[0], i, dtype=np.int32) - for i, bbox in enumerate(bbox_result) - ] - labels = np.concatenate(labels) - - segms = None - if segm_result is not None and len(labels) > 0: # non empty - segms = mmcv.concat_list(segm_result) - segms = mask_util.decode(segms) - segms = segms.transpose(2, 0, 1) - else: - assert class_names is not None, 'We need to know the number ' \ - 'of classes.' - VOID = len(class_names) - bboxes = None - pan_results = result['pan_results'] - # keep objects ahead - ids = np.unique(pan_results)[::-1] - legal_indices = ids != VOID - ids = ids[legal_indices] - labels = np.array([id % INSTANCE_OFFSET for id in ids], dtype=np.int64) - segms = (pan_results[None] == ids[:, None, None]) - - if overlay_gt_pred: - img = imshow_det_bboxes( - img_with_gt, - bboxes, - labels, - segms=segms, - class_names=class_names, - score_thr=score_thr, - bbox_color=det_bbox_color, - text_color=det_text_color, - mask_color=det_mask_color, - thickness=thickness, - font_size=font_size, - win_name=win_name, - show=show, - wait_time=wait_time, - out_file=out_file) - else: - img_with_det = imshow_det_bboxes( - img, - bboxes, - labels, - segms=segms, - class_names=class_names, - score_thr=score_thr, - bbox_color=det_bbox_color, - text_color=det_text_color, - mask_color=det_mask_color, - thickness=thickness, - font_size=font_size, - win_name=win_name, - show=False) - img = np.concatenate([img_with_gt, img_with_det], axis=0) - - plt.imshow(img) - if show: - if wait_time == 0: - plt.show() - else: - plt.show(block=False) - plt.pause(wait_time) - if out_file is not None: - mmcv.imwrite(img, out_file) - plt.close() - - return img diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/core/visualization/palette.py b/cv/instance_segmentation/solo/pytorch/mmdet/core/visualization/palette.py deleted file mode 100644 index 11692cdd0..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/core/visualization/palette.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np - - -def palette_val(palette): - """Convert palette to matplotlib palette. - - Args: - palette List[tuple]: A list of color tuples. - - Returns: - List[tuple[float]]: A list of RGB matplotlib color tuples. - """ - new_palette = [] - for color in palette: - color = [c / 255 for c in color] - new_palette.append(tuple(color)) - return new_palette - - -def get_palette(palette, num_classes): - """Get palette from various inputs. - - Args: - palette (list[tuple] | str | tuple | :obj:`Color`): palette inputs. - num_classes (int): the number of classes. - - Returns: - list[tuple[int]]: A list of color tuples. - """ - assert isinstance(num_classes, int) - - if isinstance(palette, list): - dataset_palette = palette - elif isinstance(palette, tuple): - dataset_palette = [palette] * num_classes - elif palette == 'random' or palette is None: - state = np.random.get_state() - # random color - np.random.seed(42) - palette = np.random.randint(0, 256, size=(num_classes, 3)) - np.random.set_state(state) - dataset_palette = [tuple(c) for c in palette] - elif palette == 'coco': - from mmdet.datasets import CocoDataset, CocoPanopticDataset - dataset_palette = CocoDataset.PALETTE - if len(dataset_palette) < num_classes: - dataset_palette = CocoPanopticDataset.PALETTE - elif palette == 'citys': - from mmdet.datasets import CityscapesDataset - dataset_palette = CityscapesDataset.PALETTE - elif palette == 'voc': - from mmdet.datasets import VOCDataset - dataset_palette = VOCDataset.PALETTE - elif mmcv.is_str(palette): - dataset_palette = [mmcv.color_val(palette)[::-1]] * num_classes - else: - raise TypeError(f'Invalid type for palette: {type(palette)}') - - assert len(dataset_palette) >= num_classes, \ - 'The length of palette should not be less than `num_classes`.' - return dataset_palette diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/__init__.py deleted file mode 100644 index 198519d50..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .builder import DATASETS, PIPELINES, build_dataloader, build_dataset -from .coco import CocoDataset -from .custom import CustomDataset -from .dataset_wrappers import (ClassBalancedDataset, ConcatDataset, - MultiImageMixDataset, RepeatDataset) -from .samplers import DistributedGroupSampler, DistributedSampler, GroupSampler -from .utils import (NumClassCheckHook, get_loading_pipeline, - replace_ImageToTensor) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/api_wrappers/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/api_wrappers/__init__.py deleted file mode 100644 index af8557593..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/api_wrappers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .coco_api import COCO, COCOeval -from .panoptic_evaluation import pq_compute_multi_core, pq_compute_single_core - -__all__ = [ - 'COCO', 'COCOeval', 'pq_compute_multi_core', 'pq_compute_single_core' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/api_wrappers/coco_api.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/api_wrappers/coco_api.py deleted file mode 100644 index eef6341eb..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/api_wrappers/coco_api.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# This file add snake case alias for coco api - -import warnings - -import pycocotools -from pycocotools.coco import COCO as _COCO -from pycocotools.cocoeval import COCOeval as _COCOeval - - -class COCO(_COCO): - """This class is almost the same as official pycocotools package. - - It implements some snake case function aliases. So that the COCO class has - the same interface as LVIS class. - """ - - def __init__(self, annotation_file=None): - if getattr(pycocotools, '__version__', '0') >= '12.0.2': - warnings.warn( - 'mmpycocotools is deprecated. Please install official pycocotools by "pip install pycocotools"', # noqa: E501 - UserWarning) - super().__init__(annotation_file=annotation_file) - self.img_ann_map = self.imgToAnns - self.cat_img_map = self.catToImgs - - def get_ann_ids(self, img_ids=[], cat_ids=[], area_rng=[], iscrowd=None): - return self.getAnnIds(img_ids, cat_ids, area_rng, iscrowd) - - def get_cat_ids(self, cat_names=[], sup_names=[], cat_ids=[]): - return self.getCatIds(cat_names, sup_names, cat_ids) - - def get_img_ids(self, img_ids=[], cat_ids=[]): - return self.getImgIds(img_ids, cat_ids) - - def load_anns(self, ids): - return self.loadAnns(ids) - - def load_cats(self, ids): - return self.loadCats(ids) - - def load_imgs(self, ids): - return self.loadImgs(ids) - - -# just for the ease of import -COCOeval = _COCOeval diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/api_wrappers/panoptic_evaluation.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/api_wrappers/panoptic_evaluation.py deleted file mode 100644 index 55f57bf4a..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/api_wrappers/panoptic_evaluation.py +++ /dev/null @@ -1,228 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -# Copyright (c) 2018, Alexander Kirillov -# This file supports `file_client` for `panopticapi`, -# the source code is copied from `panopticapi`, -# only the way to load the gt images is modified. -import multiprocessing -import os - -import mmcv -import numpy as np - -try: - from panopticapi.evaluation import OFFSET, VOID, PQStat - from panopticapi.utils import rgb2id -except ImportError: - PQStat = None - rgb2id = None - VOID = 0 - OFFSET = 256 * 256 * 256 - - -def pq_compute_single_core(proc_id, - annotation_set, - gt_folder, - pred_folder, - categories, - file_client=None, - print_log=False): - """The single core function to evaluate the metric of Panoptic - Segmentation. - - Same as the function with the same name in `panopticapi`. Only the function - to load the images is changed to use the file client. - - Args: - proc_id (int): The id of the mini process. - gt_folder (str): The path of the ground truth images. - pred_folder (str): The path of the prediction images. - categories (str): The categories of the dataset. - file_client (object): The file client of the dataset. If None, - the backend will be set to `disk`. - print_log (bool): Whether to print the log. Defaults to False. - """ - if PQStat is None: - raise RuntimeError( - 'panopticapi is not installed, please install it by: ' - 'pip install git+https://github.com/cocodataset/' - 'panopticapi.git.') - - if file_client is None: - file_client_args = dict(backend='disk') - file_client = mmcv.FileClient(**file_client_args) - - pq_stat = PQStat() - - idx = 0 - for gt_ann, pred_ann in annotation_set: - if print_log and idx % 100 == 0: - print('Core: {}, {} from {} images processed'.format( - proc_id, idx, len(annotation_set))) - idx += 1 - # The gt images can be on the local disk or `ceph`, so we use - # file_client here. - img_bytes = file_client.get( - os.path.join(gt_folder, gt_ann['file_name'])) - pan_gt = mmcv.imfrombytes(img_bytes, flag='color', channel_order='rgb') - pan_gt = rgb2id(pan_gt) - - # The predictions can only be on the local dist now. - pan_pred = mmcv.imread( - os.path.join(pred_folder, pred_ann['file_name']), - flag='color', - channel_order='rgb') - pan_pred = rgb2id(pan_pred) - - gt_segms = {el['id']: el for el in gt_ann['segments_info']} - pred_segms = {el['id']: el for el in pred_ann['segments_info']} - - # predicted segments area calculation + prediction sanity checks - pred_labels_set = set(el['id'] for el in pred_ann['segments_info']) - labels, labels_cnt = np.unique(pan_pred, return_counts=True) - for label, label_cnt in zip(labels, labels_cnt): - if label not in pred_segms: - if label == VOID: - continue - raise KeyError( - 'In the image with ID {} segment with ID {} is ' - 'presented in PNG and not presented in JSON.'.format( - gt_ann['image_id'], label)) - pred_segms[label]['area'] = label_cnt - pred_labels_set.remove(label) - if pred_segms[label]['category_id'] not in categories: - raise KeyError( - 'In the image with ID {} segment with ID {} has ' - 'unknown category_id {}.'.format( - gt_ann['image_id'], label, - pred_segms[label]['category_id'])) - if len(pred_labels_set) != 0: - raise KeyError( - 'In the image with ID {} the following segment IDs {} ' - 'are presented in JSON and not presented in PNG.'.format( - gt_ann['image_id'], list(pred_labels_set))) - - # confusion matrix calculation - pan_gt_pred = pan_gt.astype(np.uint64) * OFFSET + pan_pred.astype( - np.uint64) - gt_pred_map = {} - labels, labels_cnt = np.unique(pan_gt_pred, return_counts=True) - for label, intersection in zip(labels, labels_cnt): - gt_id = label // OFFSET - pred_id = label % OFFSET - gt_pred_map[(gt_id, pred_id)] = intersection - - # count all matched pairs - gt_matched = set() - pred_matched = set() - for label_tuple, intersection in gt_pred_map.items(): - gt_label, pred_label = label_tuple - if gt_label not in gt_segms: - continue - if pred_label not in pred_segms: - continue - if gt_segms[gt_label]['iscrowd'] == 1: - continue - if gt_segms[gt_label]['category_id'] != pred_segms[pred_label][ - 'category_id']: - continue - - union = pred_segms[pred_label]['area'] + gt_segms[gt_label][ - 'area'] - intersection - gt_pred_map.get((VOID, pred_label), 0) - iou = intersection / union - if iou > 0.5: - pq_stat[gt_segms[gt_label]['category_id']].tp += 1 - pq_stat[gt_segms[gt_label]['category_id']].iou += iou - gt_matched.add(gt_label) - pred_matched.add(pred_label) - - # count false positives - crowd_labels_dict = {} - for gt_label, gt_info in gt_segms.items(): - if gt_label in gt_matched: - continue - # crowd segments are ignored - if gt_info['iscrowd'] == 1: - crowd_labels_dict[gt_info['category_id']] = gt_label - continue - pq_stat[gt_info['category_id']].fn += 1 - - # count false positives - for pred_label, pred_info in pred_segms.items(): - if pred_label in pred_matched: - continue - # intersection of the segment with VOID - intersection = gt_pred_map.get((VOID, pred_label), 0) - # plus intersection with corresponding CROWD region if it exists - if pred_info['category_id'] in crowd_labels_dict: - intersection += gt_pred_map.get( - (crowd_labels_dict[pred_info['category_id']], pred_label), - 0) - # predicted segment is ignored if more than half of - # the segment correspond to VOID and CROWD regions - if intersection / pred_info['area'] > 0.5: - continue - pq_stat[pred_info['category_id']].fp += 1 - - if print_log: - print('Core: {}, all {} images processed'.format( - proc_id, len(annotation_set))) - return pq_stat - - -def pq_compute_multi_core(matched_annotations_list, - gt_folder, - pred_folder, - categories, - file_client=None, - nproc=32): - """Evaluate the metrics of Panoptic Segmentation with multithreading. - - Same as the function with the same name in `panopticapi`. - - Args: - matched_annotations_list (list): The matched annotation list. Each - element is a tuple of annotations of the same image with the - format (gt_anns, pred_anns). - gt_folder (str): The path of the ground truth images. - pred_folder (str): The path of the prediction images. - categories (str): The categories of the dataset. - file_client (object): The file client of the dataset. If None, - the backend will be set to `disk`. - nproc (int): Number of processes for panoptic quality computing. - Defaults to 32. When `nproc` exceeds the number of cpu cores, - the number of cpu cores is used. - """ - if PQStat is None: - raise RuntimeError( - 'panopticapi is not installed, please install it by: ' - 'pip install git+https://github.com/cocodataset/' - 'panopticapi.git.') - - if file_client is None: - file_client_args = dict(backend='disk') - file_client = mmcv.FileClient(**file_client_args) - - cpu_num = min(nproc, multiprocessing.cpu_count()) - - annotations_split = np.array_split(matched_annotations_list, cpu_num) - print('Number of cores: {}, images per core: {}'.format( - cpu_num, len(annotations_split[0]))) - workers = multiprocessing.Pool(processes=cpu_num) - processes = [] - for proc_id, annotation_set in enumerate(annotations_split): - p = workers.apply_async(pq_compute_single_core, - (proc_id, annotation_set, gt_folder, - pred_folder, categories, file_client)) - processes.append(p) - - # Close the process pool, otherwise it will lead to memory - # leaking problems. - workers.close() - workers.join() - - pq_stat = PQStat() - for p in processes: - pq_stat += p.get() - - return pq_stat diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/builder.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/builder.py deleted file mode 100644 index 1936296a5..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/builder.py +++ /dev/null @@ -1,215 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import platform -import random -import warnings -from functools import partial - -import numpy as np -import torch -from mmcv.parallel import collate -from mmcv.runner import get_dist_info -from mmcv.utils import TORCH_VERSION, Registry, build_from_cfg, digit_version -from torch.utils.data import DataLoader - -from .samplers import (ClassAwareSampler, DistributedGroupSampler, - DistributedSampler, GroupSampler, InfiniteBatchSampler, - InfiniteGroupBatchSampler) - -if platform.system() != 'Windows': - # https://github.com/pytorch/pytorch/issues/973 - import resource - rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) - base_soft_limit = rlimit[0] - hard_limit = rlimit[1] - soft_limit = min(max(4096, base_soft_limit), hard_limit) - resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) - -DATASETS = Registry('dataset') -PIPELINES = Registry('pipeline') - - -def _concat_dataset(cfg, default_args=None): - from .dataset_wrappers import ConcatDataset - ann_files = cfg['ann_file'] - img_prefixes = cfg.get('img_prefix', None) - seg_prefixes = cfg.get('seg_prefix', None) - proposal_files = cfg.get('proposal_file', None) - separate_eval = cfg.get('separate_eval', True) - - datasets = [] - num_dset = len(ann_files) - for i in range(num_dset): - data_cfg = copy.deepcopy(cfg) - # pop 'separate_eval' since it is not a valid key for common datasets. - if 'separate_eval' in data_cfg: - data_cfg.pop('separate_eval') - data_cfg['ann_file'] = ann_files[i] - if isinstance(img_prefixes, (list, tuple)): - data_cfg['img_prefix'] = img_prefixes[i] - if isinstance(seg_prefixes, (list, tuple)): - data_cfg['seg_prefix'] = seg_prefixes[i] - if isinstance(proposal_files, (list, tuple)): - data_cfg['proposal_file'] = proposal_files[i] - datasets.append(build_dataset(data_cfg, default_args)) - - return ConcatDataset(datasets, separate_eval) - - -def build_dataset(cfg, default_args=None): - from .dataset_wrappers import (ClassBalancedDataset, ConcatDataset, - MultiImageMixDataset, RepeatDataset) - if isinstance(cfg, (list, tuple)): - dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) - elif cfg['type'] == 'ConcatDataset': - dataset = ConcatDataset( - [build_dataset(c, default_args) for c in cfg['datasets']], - cfg.get('separate_eval', True)) - elif cfg['type'] == 'RepeatDataset': - dataset = RepeatDataset( - build_dataset(cfg['dataset'], default_args), cfg['times']) - elif cfg['type'] == 'ClassBalancedDataset': - dataset = ClassBalancedDataset( - build_dataset(cfg['dataset'], default_args), cfg['oversample_thr']) - elif cfg['type'] == 'MultiImageMixDataset': - cp_cfg = copy.deepcopy(cfg) - cp_cfg['dataset'] = build_dataset(cp_cfg['dataset']) - cp_cfg.pop('type') - dataset = MultiImageMixDataset(**cp_cfg) - elif isinstance(cfg.get('ann_file'), (list, tuple)): - dataset = _concat_dataset(cfg, default_args) - else: - dataset = build_from_cfg(cfg, DATASETS, default_args) - - return dataset - - -def build_dataloader(dataset, - samples_per_gpu, - workers_per_gpu, - num_gpus=1, - dist=True, - shuffle=True, - seed=None, - runner_type='EpochBasedRunner', - persistent_workers=False, - class_aware_sampler=None, - **kwargs): - """Build PyTorch DataLoader. - - In distributed training, each GPU/process has a dataloader. - In non-distributed training, there is only one dataloader for all GPUs. - - Args: - dataset (Dataset): A PyTorch dataset. - samples_per_gpu (int): Number of training samples on each GPU, i.e., - batch size of each GPU. - workers_per_gpu (int): How many subprocesses to use for data loading - for each GPU. - num_gpus (int): Number of GPUs. Only used in non-distributed training. - dist (bool): Distributed training/test or not. Default: True. - shuffle (bool): Whether to shuffle the data at every epoch. - Default: True. - seed (int, Optional): Seed to be used. Default: None. - runner_type (str): Type of runner. Default: `EpochBasedRunner` - persistent_workers (bool): If True, the data loader will not shutdown - the worker processes after a dataset has been consumed once. - This allows to maintain the workers `Dataset` instances alive. - This argument is only valid when PyTorch>=1.7.0. Default: False. - class_aware_sampler (dict): Whether to use `ClassAwareSampler` - during training. Default: None. - kwargs: any keyword argument to be used to initialize DataLoader - - Returns: - DataLoader: A PyTorch dataloader. - """ - rank, world_size = get_dist_info() - - if dist: - # When model is :obj:`DistributedDataParallel`, - # `batch_size` of :obj:`dataloader` is the - # number of training samples on each GPU. - batch_size = samples_per_gpu - num_workers = workers_per_gpu - else: - # When model is obj:`DataParallel` - # the batch size is samples on all the GPUS - batch_size = num_gpus * samples_per_gpu - num_workers = num_gpus * workers_per_gpu - - if runner_type == 'IterBasedRunner': - # this is a batch sampler, which can yield - # a mini-batch indices each time. - # it can be used in both `DataParallel` and - # `DistributedDataParallel` - if shuffle: - batch_sampler = InfiniteGroupBatchSampler( - dataset, batch_size, world_size, rank, seed=seed) - else: - batch_sampler = InfiniteBatchSampler( - dataset, - batch_size, - world_size, - rank, - seed=seed, - shuffle=False) - batch_size = 1 - sampler = None - else: - if class_aware_sampler is not None: - # ClassAwareSampler can be used in both distributed and - # non-distributed training. - num_sample_class = class_aware_sampler.get('num_sample_class', 1) - sampler = ClassAwareSampler( - dataset, - samples_per_gpu, - world_size, - rank, - seed=seed, - num_sample_class=num_sample_class) - elif dist: - # DistributedGroupSampler will definitely shuffle the data to - # satisfy that images on each GPU are in the same group - if shuffle: - sampler = DistributedGroupSampler( - dataset, samples_per_gpu, world_size, rank, seed=seed) - else: - sampler = DistributedSampler( - dataset, world_size, rank, shuffle=False, seed=seed) - else: - sampler = GroupSampler(dataset, - samples_per_gpu) if shuffle else None - batch_sampler = None - - init_fn = partial( - worker_init_fn, num_workers=num_workers, rank=rank, - seed=seed) if seed is not None else None - - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.7.0')): - kwargs['persistent_workers'] = persistent_workers - elif persistent_workers is True: - warnings.warn('persistent_workers is invalid because your pytorch ' - 'version is lower than 1.7.0') - - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - batch_sampler=batch_sampler, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=kwargs.pop('pin_memory', False), - worker_init_fn=init_fn, - **kwargs) - - return data_loader - - -def worker_init_fn(worker_id, num_workers, rank, seed): - # The seed of each worker equals to - # num_worker * rank + worker_id + user_seed - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/coco.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/coco.py deleted file mode 100644 index bcdd4df39..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/coco.py +++ /dev/null @@ -1,649 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import contextlib -import io -import itertools -import logging -import os.path as osp -import tempfile -import warnings -from collections import OrderedDict - -import mmcv -import numpy as np -from mmcv.utils import print_log -from terminaltables import AsciiTable - -from mmdet.core import eval_recalls -from .api_wrappers import COCO, COCOeval -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class CocoDataset(CustomDataset): - - CLASSES = ('person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', - 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', - 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', - 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', - 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', - 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', - 'baseball glove', 'skateboard', 'surfboard', 'tennis racket', - 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', - 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', - 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', - 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', - 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', - 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush') - - PALETTE = [(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230), - (106, 0, 228), (0, 60, 100), (0, 80, 100), (0, 0, 70), - (0, 0, 192), (250, 170, 30), (100, 170, 30), (220, 220, 0), - (175, 116, 175), (250, 0, 30), (165, 42, 42), (255, 77, 255), - (0, 226, 252), (182, 182, 255), (0, 82, 0), (120, 166, 157), - (110, 76, 0), (174, 57, 255), (199, 100, 0), (72, 0, 118), - (255, 179, 240), (0, 125, 92), (209, 0, 151), (188, 208, 182), - (0, 220, 176), (255, 99, 164), (92, 0, 73), (133, 129, 255), - (78, 180, 255), (0, 228, 0), (174, 255, 243), (45, 89, 255), - (134, 134, 103), (145, 148, 174), (255, 208, 186), - (197, 226, 255), (171, 134, 1), (109, 63, 54), (207, 138, 255), - (151, 0, 95), (9, 80, 61), (84, 105, 51), (74, 65, 105), - (166, 196, 102), (208, 195, 210), (255, 109, 65), (0, 143, 149), - (179, 0, 194), (209, 99, 106), (5, 121, 0), (227, 255, 205), - (147, 186, 208), (153, 69, 1), (3, 95, 161), (163, 255, 0), - (119, 0, 170), (0, 182, 199), (0, 165, 120), (183, 130, 88), - (95, 32, 0), (130, 114, 135), (110, 129, 133), (166, 74, 118), - (219, 142, 185), (79, 210, 114), (178, 90, 62), (65, 70, 15), - (127, 167, 115), (59, 105, 106), (142, 108, 45), (196, 172, 0), - (95, 54, 80), (128, 76, 255), (201, 57, 1), (246, 0, 122), - (191, 162, 208)] - - def load_annotations(self, ann_file): - """Load annotation from COCO style annotation file. - - Args: - ann_file (str): Path of annotation file. - - Returns: - list[dict]: Annotation info from COCO api. - """ - - self.coco = COCO(ann_file) - # The order of returned `cat_ids` will not - # change with the order of the CLASSES - self.cat_ids = self.coco.get_cat_ids(cat_names=self.CLASSES) - - self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)} - self.img_ids = self.coco.get_img_ids() - data_infos = [] - total_ann_ids = [] - for i in self.img_ids: - info = self.coco.load_imgs([i])[0] - info['filename'] = info['file_name'] - data_infos.append(info) - ann_ids = self.coco.get_ann_ids(img_ids=[i]) - total_ann_ids.extend(ann_ids) - assert len(set(total_ann_ids)) == len( - total_ann_ids), f"Annotation ids in '{ann_file}' are not unique!" - return data_infos - - def get_ann_info(self, idx): - """Get COCO annotation by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - img_id = self.data_infos[idx]['id'] - ann_ids = self.coco.get_ann_ids(img_ids=[img_id]) - ann_info = self.coco.load_anns(ann_ids) - return self._parse_ann_info(self.data_infos[idx], ann_info) - - def get_cat_ids(self, idx): - """Get COCO category ids by index. - - Args: - idx (int): Index of data. - - Returns: - list[int]: All categories in the image of specified index. - """ - - img_id = self.data_infos[idx]['id'] - ann_ids = self.coco.get_ann_ids(img_ids=[img_id]) - ann_info = self.coco.load_anns(ann_ids) - return [ann['category_id'] for ann in ann_info] - - def _filter_imgs(self, min_size=32): - """Filter images too small or without ground truths.""" - valid_inds = [] - # obtain images that contain annotation - ids_with_ann = set(_['image_id'] for _ in self.coco.anns.values()) - # obtain images that contain annotations of the required categories - ids_in_cat = set() - for i, class_id in enumerate(self.cat_ids): - ids_in_cat |= set(self.coco.cat_img_map[class_id]) - # merge the image id sets of the two conditions and use the merged set - # to filter out images if self.filter_empty_gt=True - ids_in_cat &= ids_with_ann - - valid_img_ids = [] - for i, img_info in enumerate(self.data_infos): - img_id = self.img_ids[i] - if self.filter_empty_gt and img_id not in ids_in_cat: - continue - if min(img_info['width'], img_info['height']) >= min_size: - valid_inds.append(i) - valid_img_ids.append(img_id) - self.img_ids = valid_img_ids - return valid_inds - - def _parse_ann_info(self, img_info, ann_info): - """Parse bbox and mask annotation. - - Args: - ann_info (list[dict]): Annotation info of an image. - with_mask (bool): Whether to parse mask annotations. - - Returns: - dict: A dict containing the following keys: bboxes, bboxes_ignore,\ - labels, masks, seg_map. "masks" are raw annotations and not \ - decoded into binary masks. - """ - gt_bboxes = [] - gt_labels = [] - gt_bboxes_ignore = [] - gt_masks_ann = [] - for i, ann in enumerate(ann_info): - if ann.get('ignore', False): - continue - x1, y1, w, h = ann['bbox'] - inter_w = max(0, min(x1 + w, img_info['width']) - max(x1, 0)) - inter_h = max(0, min(y1 + h, img_info['height']) - max(y1, 0)) - if inter_w * inter_h == 0: - continue - if ann['area'] <= 0 or w < 1 or h < 1: - continue - if ann['category_id'] not in self.cat_ids: - continue - bbox = [x1, y1, x1 + w, y1 + h] - if ann.get('iscrowd', False): - gt_bboxes_ignore.append(bbox) - else: - gt_bboxes.append(bbox) - gt_labels.append(self.cat2label[ann['category_id']]) - gt_masks_ann.append(ann.get('segmentation', None)) - - if gt_bboxes: - gt_bboxes = np.array(gt_bboxes, dtype=np.float32) - gt_labels = np.array(gt_labels, dtype=np.int64) - else: - gt_bboxes = np.zeros((0, 4), dtype=np.float32) - gt_labels = np.array([], dtype=np.int64) - - if gt_bboxes_ignore: - gt_bboxes_ignore = np.array(gt_bboxes_ignore, dtype=np.float32) - else: - gt_bboxes_ignore = np.zeros((0, 4), dtype=np.float32) - - seg_map = img_info['filename'].replace('jpg', 'png') - - ann = dict( - bboxes=gt_bboxes, - labels=gt_labels, - bboxes_ignore=gt_bboxes_ignore, - masks=gt_masks_ann, - seg_map=seg_map) - - return ann - - def xyxy2xywh(self, bbox): - """Convert ``xyxy`` style bounding boxes to ``xywh`` style for COCO - evaluation. - - Args: - bbox (numpy.ndarray): The bounding boxes, shape (4, ), in - ``xyxy`` order. - - Returns: - list[float]: The converted bounding boxes, in ``xywh`` order. - """ - - _bbox = bbox.tolist() - return [ - _bbox[0], - _bbox[1], - _bbox[2] - _bbox[0], - _bbox[3] - _bbox[1], - ] - - def _proposal2json(self, results): - """Convert proposal results to COCO json style.""" - json_results = [] - for idx in range(len(self)): - img_id = self.img_ids[idx] - bboxes = results[idx] - for i in range(bboxes.shape[0]): - data = dict() - data['image_id'] = img_id - data['bbox'] = self.xyxy2xywh(bboxes[i]) - data['score'] = float(bboxes[i][4]) - data['category_id'] = 1 - json_results.append(data) - return json_results - - def _det2json(self, results): - """Convert detection results to COCO json style.""" - json_results = [] - for idx in range(len(self)): - img_id = self.img_ids[idx] - result = results[idx] - for label in range(len(result)): - bboxes = result[label] - for i in range(bboxes.shape[0]): - data = dict() - data['image_id'] = img_id - data['bbox'] = self.xyxy2xywh(bboxes[i]) - data['score'] = float(bboxes[i][4]) - data['category_id'] = self.cat_ids[label] - json_results.append(data) - return json_results - - def _segm2json(self, results): - """Convert instance segmentation results to COCO json style.""" - bbox_json_results = [] - segm_json_results = [] - for idx in range(len(self)): - img_id = self.img_ids[idx] - det, seg = results[idx] - for label in range(len(det)): - # bbox results - bboxes = det[label] - for i in range(bboxes.shape[0]): - data = dict() - data['image_id'] = img_id - data['bbox'] = self.xyxy2xywh(bboxes[i]) - data['score'] = float(bboxes[i][4]) - data['category_id'] = self.cat_ids[label] - bbox_json_results.append(data) - - # segm results - # some detectors use different scores for bbox and mask - if isinstance(seg, tuple): - segms = seg[0][label] - mask_score = seg[1][label] - else: - segms = seg[label] - mask_score = [bbox[4] for bbox in bboxes] - for i in range(bboxes.shape[0]): - data = dict() - data['image_id'] = img_id - data['bbox'] = self.xyxy2xywh(bboxes[i]) - data['score'] = float(mask_score[i]) - data['category_id'] = self.cat_ids[label] - if isinstance(segms[i]['counts'], bytes): - segms[i]['counts'] = segms[i]['counts'].decode() - data['segmentation'] = segms[i] - segm_json_results.append(data) - return bbox_json_results, segm_json_results - - def results2json(self, results, outfile_prefix): - """Dump the detection results to a COCO style json file. - - There are 3 types of results: proposals, bbox predictions, mask - predictions, and they have different data types. This method will - automatically recognize the type, and dump them to json files. - - Args: - results (list[list | tuple | ndarray]): Testing results of the - dataset. - outfile_prefix (str): The filename prefix of the json files. If the - prefix is "somepath/xxx", the json files will be named - "somepath/xxx.bbox.json", "somepath/xxx.segm.json", - "somepath/xxx.proposal.json". - - Returns: - dict[str: str]: Possible keys are "bbox", "segm", "proposal", and \ - values are corresponding filenames. - """ - result_files = dict() - if isinstance(results[0], list): - json_results = self._det2json(results) - result_files['bbox'] = f'{outfile_prefix}.bbox.json' - result_files['proposal'] = f'{outfile_prefix}.bbox.json' - mmcv.dump(json_results, result_files['bbox']) - elif isinstance(results[0], tuple): - json_results = self._segm2json(results) - result_files['bbox'] = f'{outfile_prefix}.bbox.json' - result_files['proposal'] = f'{outfile_prefix}.bbox.json' - result_files['segm'] = f'{outfile_prefix}.segm.json' - mmcv.dump(json_results[0], result_files['bbox']) - mmcv.dump(json_results[1], result_files['segm']) - elif isinstance(results[0], np.ndarray): - json_results = self._proposal2json(results) - result_files['proposal'] = f'{outfile_prefix}.proposal.json' - mmcv.dump(json_results, result_files['proposal']) - else: - raise TypeError('invalid type of results') - return result_files - - def fast_eval_recall(self, results, proposal_nums, iou_thrs, logger=None): - gt_bboxes = [] - for i in range(len(self.img_ids)): - ann_ids = self.coco.get_ann_ids(img_ids=self.img_ids[i]) - ann_info = self.coco.load_anns(ann_ids) - if len(ann_info) == 0: - gt_bboxes.append(np.zeros((0, 4))) - continue - bboxes = [] - for ann in ann_info: - if ann.get('ignore', False) or ann['iscrowd']: - continue - x1, y1, w, h = ann['bbox'] - bboxes.append([x1, y1, x1 + w, y1 + h]) - bboxes = np.array(bboxes, dtype=np.float32) - if bboxes.shape[0] == 0: - bboxes = np.zeros((0, 4)) - gt_bboxes.append(bboxes) - - recalls = eval_recalls( - gt_bboxes, results, proposal_nums, iou_thrs, logger=logger) - ar = recalls.mean(axis=1) - return ar - - def format_results(self, results, jsonfile_prefix=None, **kwargs): - """Format the results to json (standard format for COCO evaluation). - - Args: - results (list[tuple | numpy.ndarray]): Testing results of the - dataset. - jsonfile_prefix (str | None): The prefix of json files. It includes - the file path and the prefix of filename, e.g., "a/b/prefix". - If not specified, a temp file will be created. Default: None. - - Returns: - tuple: (result_files, tmp_dir), result_files is a dict containing \ - the json filepaths, tmp_dir is the temporal directory created \ - for saving json files when jsonfile_prefix is not specified. - """ - assert isinstance(results, list), 'results must be a list' - assert len(results) == len(self), ( - 'The length of results is not equal to the dataset len: {} != {}'. - format(len(results), len(self))) - - if jsonfile_prefix is None: - tmp_dir = tempfile.TemporaryDirectory() - jsonfile_prefix = osp.join(tmp_dir.name, 'results') - else: - tmp_dir = None - result_files = self.results2json(results, jsonfile_prefix) - return result_files, tmp_dir - - def evaluate_det_segm(self, - results, - result_files, - coco_gt, - metrics, - logger=None, - classwise=False, - proposal_nums=(100, 300, 1000), - iou_thrs=None, - metric_items=None): - """Instance segmentation and object detection evaluation in COCO - protocol. - - Args: - results (list[list | tuple | dict]): Testing results of the - dataset. - result_files (dict[str, str]): a dict contains json file path. - coco_gt (COCO): COCO API object with ground truth annotation. - metric (str | list[str]): Metrics to be evaluated. Options are - 'bbox', 'segm', 'proposal', 'proposal_fast'. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - classwise (bool): Whether to evaluating the AP for each class. - proposal_nums (Sequence[int]): Proposal number used for evaluating - recalls, such as recall@100, recall@1000. - Default: (100, 300, 1000). - iou_thrs (Sequence[float], optional): IoU threshold used for - evaluating recalls/mAPs. If set to a list, the average of all - IoUs will also be computed. If not specified, [0.50, 0.55, - 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95] will be used. - Default: None. - metric_items (list[str] | str, optional): Metric items that will - be returned. If not specified, ``['AR@100', 'AR@300', - 'AR@1000', 'AR_s@1000', 'AR_m@1000', 'AR_l@1000' ]`` will be - used when ``metric=='proposal'``, ``['mAP', 'mAP_50', 'mAP_75', - 'mAP_s', 'mAP_m', 'mAP_l']`` will be used when - ``metric=='bbox' or metric=='segm'``. - - Returns: - dict[str, float]: COCO style evaluation metric. - """ - if iou_thrs is None: - iou_thrs = np.linspace( - .5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True) - if metric_items is not None: - if not isinstance(metric_items, list): - metric_items = [metric_items] - - eval_results = OrderedDict() - for metric in metrics: - msg = f'Evaluating {metric}...' - if logger is None: - msg = '\n' + msg - print_log(msg, logger=logger) - - if metric == 'proposal_fast': - if isinstance(results[0], tuple): - raise KeyError('proposal_fast is not supported for ' - 'instance segmentation result.') - ar = self.fast_eval_recall( - results, proposal_nums, iou_thrs, logger='silent') - log_msg = [] - for i, num in enumerate(proposal_nums): - eval_results[f'AR@{num}'] = ar[i] - log_msg.append(f'\nAR@{num}\t{ar[i]:.4f}') - log_msg = ''.join(log_msg) - print_log(log_msg, logger=logger) - continue - - iou_type = 'bbox' if metric == 'proposal' else metric - if metric not in result_files: - raise KeyError(f'{metric} is not in results') - try: - predictions = mmcv.load(result_files[metric]) - if iou_type == 'segm': - # Refer to https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/coco.py#L331 # noqa - # When evaluating mask AP, if the results contain bbox, - # cocoapi will use the box area instead of the mask area - # for calculating the instance area. Though the overall AP - # is not affected, this leads to different - # small/medium/large mask AP results. - for x in predictions: - x.pop('bbox') - warnings.simplefilter('once') - warnings.warn( - 'The key "bbox" is deleted for more accurate mask AP ' - 'of small/medium/large instances since v2.12.0. This ' - 'does not change the overall mAP calculation.', - UserWarning) - coco_det = coco_gt.loadRes(predictions) - except IndexError: - print_log( - 'The testing results of the whole dataset is empty.', - logger=logger, - level=logging.ERROR) - break - - cocoEval = COCOeval(coco_gt, coco_det, iou_type) - cocoEval.params.catIds = self.cat_ids - cocoEval.params.imgIds = self.img_ids - cocoEval.params.maxDets = list(proposal_nums) - cocoEval.params.iouThrs = iou_thrs - # mapping of cocoEval.stats - coco_metric_names = { - 'mAP': 0, - 'mAP_50': 1, - 'mAP_75': 2, - 'mAP_s': 3, - 'mAP_m': 4, - 'mAP_l': 5, - 'AR@100': 6, - 'AR@300': 7, - 'AR@1000': 8, - 'AR_s@1000': 9, - 'AR_m@1000': 10, - 'AR_l@1000': 11 - } - if metric_items is not None: - for metric_item in metric_items: - if metric_item not in coco_metric_names: - raise KeyError( - f'metric item {metric_item} is not supported') - - if metric == 'proposal': - cocoEval.params.useCats = 0 - cocoEval.evaluate() - cocoEval.accumulate() - - # Save coco summarize print information to logger - redirect_string = io.StringIO() - with contextlib.redirect_stdout(redirect_string): - cocoEval.summarize() - print_log('\n' + redirect_string.getvalue(), logger=logger) - - if metric_items is None: - metric_items = [ - 'AR@100', 'AR@300', 'AR@1000', 'AR_s@1000', - 'AR_m@1000', 'AR_l@1000' - ] - - for item in metric_items: - val = float( - f'{cocoEval.stats[coco_metric_names[item]]:.3f}') - eval_results[item] = val - else: - cocoEval.evaluate() - cocoEval.accumulate() - - # Save coco summarize print information to logger - redirect_string = io.StringIO() - with contextlib.redirect_stdout(redirect_string): - cocoEval.summarize() - print_log('\n' + redirect_string.getvalue(), logger=logger) - - if classwise: # Compute per-category AP - # Compute per-category AP - # from https://github.com/facebookresearch/detectron2/ - precisions = cocoEval.eval['precision'] - # precision: (iou, recall, cls, area range, max dets) - assert len(self.cat_ids) == precisions.shape[2] - - results_per_category = [] - for idx, catId in enumerate(self.cat_ids): - # area range index 0: all area ranges - # max dets index -1: typically 100 per image - nm = self.coco.loadCats(catId)[0] - precision = precisions[:, :, idx, 0, -1] - precision = precision[precision > -1] - if precision.size: - ap = np.mean(precision) - else: - ap = float('nan') - results_per_category.append( - (f'{nm["name"]}', f'{float(ap):0.3f}')) - - num_columns = min(6, len(results_per_category) * 2) - results_flatten = list( - itertools.chain(*results_per_category)) - headers = ['category', 'AP'] * (num_columns // 2) - results_2d = itertools.zip_longest(*[ - results_flatten[i::num_columns] - for i in range(num_columns) - ]) - table_data = [headers] - table_data += [result for result in results_2d] - table = AsciiTable(table_data) - print_log('\n' + table.table, logger=logger) - - if metric_items is None: - metric_items = [ - 'mAP', 'mAP_50', 'mAP_75', 'mAP_s', 'mAP_m', 'mAP_l' - ] - - for metric_item in metric_items: - key = f'{metric}_{metric_item}' - val = float( - f'{cocoEval.stats[coco_metric_names[metric_item]]:.3f}' - ) - eval_results[key] = val - ap = cocoEval.stats[:6] - eval_results[f'{metric}_mAP_copypaste'] = ( - f'{ap[0]:.3f} {ap[1]:.3f} {ap[2]:.3f} {ap[3]:.3f} ' - f'{ap[4]:.3f} {ap[5]:.3f}') - - return eval_results - - def evaluate(self, - results, - metric='bbox', - logger=None, - jsonfile_prefix=None, - classwise=False, - proposal_nums=(100, 300, 1000), - iou_thrs=None, - metric_items=None): - """Evaluation in COCO protocol. - - Args: - results (list[list | tuple]): Testing results of the dataset. - metric (str | list[str]): Metrics to be evaluated. Options are - 'bbox', 'segm', 'proposal', 'proposal_fast'. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - jsonfile_prefix (str | None): The prefix of json files. It includes - the file path and the prefix of filename, e.g., "a/b/prefix". - If not specified, a temp file will be created. Default: None. - classwise (bool): Whether to evaluating the AP for each class. - proposal_nums (Sequence[int]): Proposal number used for evaluating - recalls, such as recall@100, recall@1000. - Default: (100, 300, 1000). - iou_thrs (Sequence[float], optional): IoU threshold used for - evaluating recalls/mAPs. If set to a list, the average of all - IoUs will also be computed. If not specified, [0.50, 0.55, - 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95] will be used. - Default: None. - metric_items (list[str] | str, optional): Metric items that will - be returned. If not specified, ``['AR@100', 'AR@300', - 'AR@1000', 'AR_s@1000', 'AR_m@1000', 'AR_l@1000' ]`` will be - used when ``metric=='proposal'``, ``['mAP', 'mAP_50', 'mAP_75', - 'mAP_s', 'mAP_m', 'mAP_l']`` will be used when - ``metric=='bbox' or metric=='segm'``. - - Returns: - dict[str, float]: COCO style evaluation metric. - """ - - metrics = metric if isinstance(metric, list) else [metric] - allowed_metrics = ['bbox', 'segm', 'proposal', 'proposal_fast'] - for metric in metrics: - if metric not in allowed_metrics: - raise KeyError(f'metric {metric} is not supported') - - coco_gt = self.coco - self.cat_ids = coco_gt.get_cat_ids(cat_names=self.CLASSES) - - result_files, tmp_dir = self.format_results(results, jsonfile_prefix) - eval_results = self.evaluate_det_segm(results, result_files, coco_gt, - metrics, logger, classwise, - proposal_nums, iou_thrs, - metric_items) - - if tmp_dir is not None: - tmp_dir.cleanup() - return eval_results diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/custom.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/custom.py deleted file mode 100644 index a4d825898..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/custom.py +++ /dev/null @@ -1,410 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from collections import OrderedDict - -import mmcv -import numpy as np -from mmcv.utils import print_log -from terminaltables import AsciiTable -from torch.utils.data import Dataset - -from mmdet.core import eval_map, eval_recalls -from .builder import DATASETS -from .pipelines import Compose - - -@DATASETS.register_module() -class CustomDataset(Dataset): - """Custom dataset for detection. - - The annotation format is shown as follows. The `ann` field is optional for - testing. - - .. code-block:: none - - [ - { - 'filename': 'a.jpg', - 'width': 1280, - 'height': 720, - 'ann': { - 'bboxes': (n, 4) in (x1, y1, x2, y2) order. - 'labels': (n, ), - 'bboxes_ignore': (k, 4), (optional field) - 'labels_ignore': (k, 4) (optional field) - } - }, - ... - ] - - Args: - ann_file (str): Annotation file path. - pipeline (list[dict]): Processing pipeline. - classes (str | Sequence[str], optional): Specify classes to load. - If is None, ``cls.CLASSES`` will be used. Default: None. - data_root (str, optional): Data root for ``ann_file``, - ``img_prefix``, ``seg_prefix``, ``proposal_file`` if specified. - test_mode (bool, optional): If set True, annotation will not be loaded. - filter_empty_gt (bool, optional): If set true, images without bounding - boxes of the dataset's classes will be filtered out. This option - only works when `test_mode=False`, i.e., we never filter images - during tests. - """ - - CLASSES = None - - PALETTE = None - - def __init__(self, - ann_file, - pipeline, - classes=None, - data_root=None, - img_prefix='', - seg_prefix=None, - proposal_file=None, - test_mode=False, - filter_empty_gt=True, - file_client_args=dict(backend='disk')): - self.ann_file = ann_file - self.data_root = data_root - self.img_prefix = img_prefix - self.seg_prefix = seg_prefix - self.proposal_file = proposal_file - self.test_mode = test_mode - self.filter_empty_gt = filter_empty_gt - self.file_client = mmcv.FileClient(**file_client_args) - self.CLASSES = self.get_classes(classes) - - # join paths if data_root is specified - if self.data_root is not None: - if not osp.isabs(self.ann_file): - self.ann_file = osp.join(self.data_root, self.ann_file) - if not (self.img_prefix is None or osp.isabs(self.img_prefix)): - self.img_prefix = osp.join(self.data_root, self.img_prefix) - if not (self.seg_prefix is None or osp.isabs(self.seg_prefix)): - self.seg_prefix = osp.join(self.data_root, self.seg_prefix) - if not (self.proposal_file is None - or osp.isabs(self.proposal_file)): - self.proposal_file = osp.join(self.data_root, - self.proposal_file) - # load annotations (and proposals) - if hasattr(self.file_client, 'get_local_path'): - with self.file_client.get_local_path(self.ann_file) as local_path: - self.data_infos = self.load_annotations(local_path) - else: - warnings.warn( - 'The used MMCV version does not have get_local_path. ' - f'We treat the {self.ann_file} as local paths and it ' - 'might cause errors if the path is not a local path. ' - 'Please use MMCV>= 1.3.16 if you meet errors.') - self.data_infos = self.load_annotations(self.ann_file) - - if self.proposal_file is not None: - if hasattr(self.file_client, 'get_local_path'): - with self.file_client.get_local_path( - self.proposal_file) as local_path: - self.proposals = self.load_proposals(local_path) - else: - warnings.warn( - 'The used MMCV version does not have get_local_path. ' - f'We treat the {self.ann_file} as local paths and it ' - 'might cause errors if the path is not a local path. ' - 'Please use MMCV>= 1.3.16 if you meet errors.') - self.proposals = self.load_proposals(self.proposal_file) - else: - self.proposals = None - - # filter images too small and containing no annotations - if not test_mode: - valid_inds = self._filter_imgs() - self.data_infos = [self.data_infos[i] for i in valid_inds] - if self.proposals is not None: - self.proposals = [self.proposals[i] for i in valid_inds] - # set group flag for the sampler - self._set_group_flag() - - # processing pipeline - self.pipeline = Compose(pipeline) - - def __len__(self): - """Total number of samples of data.""" - return len(self.data_infos) - - def load_annotations(self, ann_file): - """Load annotation from annotation file.""" - return mmcv.load(ann_file) - - def load_proposals(self, proposal_file): - """Load proposal from proposal file.""" - return mmcv.load(proposal_file) - - def get_ann_info(self, idx): - """Get annotation by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - return self.data_infos[idx]['ann'] - - def get_cat_ids(self, idx): - """Get category ids by index. - - Args: - idx (int): Index of data. - - Returns: - list[int]: All categories in the image of specified index. - """ - - return self.data_infos[idx]['ann']['labels'].astype(np.int).tolist() - - def pre_pipeline(self, results): - """Prepare results dict for pipeline.""" - results['img_prefix'] = self.img_prefix - results['seg_prefix'] = self.seg_prefix - results['proposal_file'] = self.proposal_file - results['bbox_fields'] = [] - results['mask_fields'] = [] - results['seg_fields'] = [] - - def _filter_imgs(self, min_size=32): - """Filter images too small.""" - if self.filter_empty_gt: - warnings.warn( - 'CustomDataset does not support filtering empty gt images.') - valid_inds = [] - for i, img_info in enumerate(self.data_infos): - if min(img_info['width'], img_info['height']) >= min_size: - valid_inds.append(i) - return valid_inds - - def _set_group_flag(self): - """Set flag according to image aspect ratio. - - Images with aspect ratio greater than 1 will be set as group 1, - otherwise group 0. - """ - self.flag = np.zeros(len(self), dtype=np.uint8) - for i in range(len(self)): - img_info = self.data_infos[i] - if img_info['width'] / img_info['height'] > 1: - self.flag[i] = 1 - - def _rand_another(self, idx): - """Get another random index from the same group as the given index.""" - pool = np.where(self.flag == self.flag[idx])[0] - return np.random.choice(pool) - - def __getitem__(self, idx): - """Get training/test data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training/test data (with annotation if `test_mode` is set \ - True). - """ - - if self.test_mode: - return self.prepare_test_img(idx) - while True: - data = self.prepare_train_img(idx) - if data is None: - idx = self._rand_another(idx) - continue - return data - - def prepare_train_img(self, idx): - """Get training data and annotations after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training data and annotation after pipeline with new keys \ - introduced by pipeline. - """ - - img_info = self.data_infos[idx] - ann_info = self.get_ann_info(idx) - results = dict(img_info=img_info, ann_info=ann_info) - if self.proposals is not None: - results['proposals'] = self.proposals[idx] - self.pre_pipeline(results) - return self.pipeline(results) - - def prepare_test_img(self, idx): - """Get testing data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Testing data after pipeline with new keys introduced by \ - pipeline. - """ - - img_info = self.data_infos[idx] - results = dict(img_info=img_info) - if self.proposals is not None: - results['proposals'] = self.proposals[idx] - self.pre_pipeline(results) - return self.pipeline(results) - - @classmethod - def get_classes(cls, classes=None): - """Get class names of current dataset. - - Args: - classes (Sequence[str] | str | None): If classes is None, use - default CLASSES defined by builtin dataset. If classes is a - string, take it as a file name. The file contains the name of - classes where each line contains one class name. If classes is - a tuple or list, override the CLASSES defined by the dataset. - - Returns: - tuple[str] or list[str]: Names of categories of the dataset. - """ - if classes is None: - return cls.CLASSES - - if isinstance(classes, str): - # take it as a file path - class_names = mmcv.list_from_file(classes) - elif isinstance(classes, (tuple, list)): - class_names = classes - else: - raise ValueError(f'Unsupported type {type(classes)} of classes.') - - return class_names - - def get_cat2imgs(self): - """Get a dict with class as key and img_ids as values, which will be - used in :class:`ClassAwareSampler`. - - Returns: - dict[list]: A dict of per-label image list, - the item of the dict indicates a label index, - corresponds to the image index that contains the label. - """ - if self.CLASSES is None: - raise ValueError('self.CLASSES can not be None') - # sort the label index - cat2imgs = {i: [] for i in range(len(self.CLASSES))} - for i in range(len(self)): - cat_ids = set(self.get_cat_ids(i)) - for cat in cat_ids: - cat2imgs[cat].append(i) - return cat2imgs - - def format_results(self, results, **kwargs): - """Place holder to format result to dataset specific output.""" - - def evaluate(self, - results, - metric='mAP', - logger=None, - proposal_nums=(100, 300, 1000), - iou_thr=0.5, - scale_ranges=None): - """Evaluate the dataset. - - Args: - results (list): Testing results of the dataset. - metric (str | list[str]): Metrics to be evaluated. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - proposal_nums (Sequence[int]): Proposal number used for evaluating - recalls, such as recall@100, recall@1000. - Default: (100, 300, 1000). - iou_thr (float | list[float]): IoU threshold. Default: 0.5. - scale_ranges (list[tuple] | None): Scale ranges for evaluating mAP. - Default: None. - """ - - if not isinstance(metric, str): - assert len(metric) == 1 - metric = metric[0] - allowed_metrics = ['mAP', 'recall'] - if metric not in allowed_metrics: - raise KeyError(f'metric {metric} is not supported') - annotations = [self.get_ann_info(i) for i in range(len(self))] - eval_results = OrderedDict() - iou_thrs = [iou_thr] if isinstance(iou_thr, float) else iou_thr - if metric == 'mAP': - assert isinstance(iou_thrs, list) - mean_aps = [] - for iou_thr in iou_thrs: - print_log(f'\n{"-" * 15}iou_thr: {iou_thr}{"-" * 15}') - mean_ap, _ = eval_map( - results, - annotations, - scale_ranges=scale_ranges, - iou_thr=iou_thr, - dataset=self.CLASSES, - logger=logger) - mean_aps.append(mean_ap) - eval_results[f'AP{int(iou_thr * 100):02d}'] = round(mean_ap, 3) - eval_results['mAP'] = sum(mean_aps) / len(mean_aps) - elif metric == 'recall': - gt_bboxes = [ann['bboxes'] for ann in annotations] - recalls = eval_recalls( - gt_bboxes, results, proposal_nums, iou_thr, logger=logger) - for i, num in enumerate(proposal_nums): - for j, iou in enumerate(iou_thrs): - eval_results[f'recall@{num}@{iou}'] = recalls[i, j] - if recalls.shape[1] > 1: - ar = recalls.mean(axis=1) - for i, num in enumerate(proposal_nums): - eval_results[f'AR@{num}'] = ar[i] - return eval_results - - def __repr__(self): - """Print the number of instance number.""" - dataset_type = 'Test' if self.test_mode else 'Train' - result = (f'\n{self.__class__.__name__} {dataset_type} dataset ' - f'with number of images {len(self)}, ' - f'and instance counts: \n') - if self.CLASSES is None: - result += 'Category names are not provided. \n' - return result - instance_count = np.zeros(len(self.CLASSES) + 1).astype(int) - # count the instance number in each image - for idx in range(len(self)): - label = self.get_ann_info(idx)['labels'] - unique, counts = np.unique(label, return_counts=True) - if len(unique) > 0: - # add the occurrence number to each class - instance_count[unique] += counts - else: - # background is the last index - instance_count[-1] += 1 - # create a table with category count - table_data = [['category', 'count'] * 5] - row_data = [] - for cls, count in enumerate(instance_count): - if cls < len(self.CLASSES): - row_data += [f'{cls} [{self.CLASSES[cls]}]', f'{count}'] - else: - # add the background number - row_data += ['-1 background', f'{count}'] - if len(row_data) == 10: - table_data.append(row_data) - row_data = [] - if len(row_data) >= 2: - if row_data[-1] == '0': - row_data = row_data[:-2] - if len(row_data) >= 2: - table_data.append([]) - table_data.append(row_data) - - table = AsciiTable(table_data) - result += table.table - return result diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/dataset_wrappers.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/dataset_wrappers.py deleted file mode 100644 index e62b88eb6..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/dataset_wrappers.py +++ /dev/null @@ -1,456 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import bisect -import collections -import copy -import math -from collections import defaultdict - -import numpy as np -from mmcv.utils import build_from_cfg, print_log -from torch.utils.data.dataset import ConcatDataset as _ConcatDataset - -from .builder import DATASETS, PIPELINES -from .coco import CocoDataset - - -@DATASETS.register_module() -class ConcatDataset(_ConcatDataset): - """A wrapper of concatenated dataset. - - Same as :obj:`torch.utils.data.dataset.ConcatDataset`, but - concat the group flag for image aspect ratio. - - Args: - datasets (list[:obj:`Dataset`]): A list of datasets. - separate_eval (bool): Whether to evaluate the results - separately if it is used as validation dataset. - Defaults to True. - """ - - def __init__(self, datasets, separate_eval=True): - super(ConcatDataset, self).__init__(datasets) - self.CLASSES = datasets[0].CLASSES - self.PALETTE = getattr(datasets[0], 'PALETTE', None) - self.separate_eval = separate_eval - if not separate_eval: - if any([isinstance(ds, CocoDataset) for ds in datasets]): - raise NotImplementedError( - 'Evaluating concatenated CocoDataset as a whole is not' - ' supported! Please set "separate_eval=True"') - elif len(set([type(ds) for ds in datasets])) != 1: - raise NotImplementedError( - 'All the datasets should have same types') - - if hasattr(datasets[0], 'flag'): - flags = [] - for i in range(0, len(datasets)): - flags.append(datasets[i].flag) - self.flag = np.concatenate(flags) - - def get_cat_ids(self, idx): - """Get category ids of concatenated dataset by index. - - Args: - idx (int): Index of data. - - Returns: - list[int]: All categories in the image of specified index. - """ - - if idx < 0: - if -idx > len(self): - raise ValueError( - 'absolute value of index should not exceed dataset length') - idx = len(self) + idx - dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx) - if dataset_idx == 0: - sample_idx = idx - else: - sample_idx = idx - self.cumulative_sizes[dataset_idx - 1] - return self.datasets[dataset_idx].get_cat_ids(sample_idx) - - def get_ann_info(self, idx): - """Get annotation of concatenated dataset by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - if idx < 0: - if -idx > len(self): - raise ValueError( - 'absolute value of index should not exceed dataset length') - idx = len(self) + idx - dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx) - if dataset_idx == 0: - sample_idx = idx - else: - sample_idx = idx - self.cumulative_sizes[dataset_idx - 1] - return self.datasets[dataset_idx].get_ann_info(sample_idx) - - def evaluate(self, results, logger=None, **kwargs): - """Evaluate the results. - - Args: - results (list[list | tuple]): Testing results of the dataset. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - - Returns: - dict[str: float]: AP results of the total dataset or each separate - dataset if `self.separate_eval=True`. - """ - assert len(results) == self.cumulative_sizes[-1], \ - ('Dataset and results have different sizes: ' - f'{self.cumulative_sizes[-1]} v.s. {len(results)}') - - # Check whether all the datasets support evaluation - for dataset in self.datasets: - assert hasattr(dataset, 'evaluate'), \ - f'{type(dataset)} does not implement evaluate function' - - if self.separate_eval: - dataset_idx = -1 - total_eval_results = dict() - for size, dataset in zip(self.cumulative_sizes, self.datasets): - start_idx = 0 if dataset_idx == -1 else \ - self.cumulative_sizes[dataset_idx] - end_idx = self.cumulative_sizes[dataset_idx + 1] - - results_per_dataset = results[start_idx:end_idx] - print_log( - f'\nEvaluateing {dataset.ann_file} with ' - f'{len(results_per_dataset)} images now', - logger=logger) - - eval_results_per_dataset = dataset.evaluate( - results_per_dataset, logger=logger, **kwargs) - dataset_idx += 1 - for k, v in eval_results_per_dataset.items(): - total_eval_results.update({f'{dataset_idx}_{k}': v}) - - return total_eval_results - elif any([isinstance(ds, CocoDataset) for ds in self.datasets]): - raise NotImplementedError( - 'Evaluating concatenated CocoDataset as a whole is not' - ' supported! Please set "separate_eval=True"') - elif len(set([type(ds) for ds in self.datasets])) != 1: - raise NotImplementedError( - 'All the datasets should have same types') - else: - original_data_infos = self.datasets[0].data_infos - self.datasets[0].data_infos = sum( - [dataset.data_infos for dataset in self.datasets], []) - eval_results = self.datasets[0].evaluate( - results, logger=logger, **kwargs) - self.datasets[0].data_infos = original_data_infos - return eval_results - - -@DATASETS.register_module() -class RepeatDataset: - """A wrapper of repeated dataset. - - The length of repeated dataset will be `times` larger than the original - dataset. This is useful when the data loading time is long but the dataset - is small. Using RepeatDataset can reduce the data loading time between - epochs. - - Args: - dataset (:obj:`Dataset`): The dataset to be repeated. - times (int): Repeat times. - """ - - def __init__(self, dataset, times): - self.dataset = dataset - self.times = times - self.CLASSES = dataset.CLASSES - self.PALETTE = getattr(dataset, 'PALETTE', None) - if hasattr(self.dataset, 'flag'): - self.flag = np.tile(self.dataset.flag, times) - - self._ori_len = len(self.dataset) - - def __getitem__(self, idx): - return self.dataset[idx % self._ori_len] - - def get_cat_ids(self, idx): - """Get category ids of repeat dataset by index. - - Args: - idx (int): Index of data. - - Returns: - list[int]: All categories in the image of specified index. - """ - - return self.dataset.get_cat_ids(idx % self._ori_len) - - def get_ann_info(self, idx): - """Get annotation of repeat dataset by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - return self.dataset.get_ann_info(idx % self._ori_len) - - def __len__(self): - """Length after repetition.""" - return self.times * self._ori_len - - -# Modified from https://github.com/facebookresearch/detectron2/blob/41d475b75a230221e21d9cac5d69655e3415e3a4/detectron2/data/samplers/distributed_sampler.py#L57 # noqa -@DATASETS.register_module() -class ClassBalancedDataset: - """A wrapper of repeated dataset with repeat factor. - - Suitable for training on class imbalanced datasets like LVIS. Following - the sampling strategy in the `paper `_, - in each epoch, an image may appear multiple times based on its - "repeat factor". - The repeat factor for an image is a function of the frequency the rarest - category labeled in that image. The "frequency of category c" in [0, 1] - is defined by the fraction of images in the training set (without repeats) - in which category c appears. - The dataset needs to instantiate :func:`self.get_cat_ids` to support - ClassBalancedDataset. - - The repeat factor is computed as followed. - - 1. For each category c, compute the fraction # of images - that contain it: :math:`f(c)` - 2. For each category c, compute the category-level repeat factor: - :math:`r(c) = max(1, sqrt(t/f(c)))` - 3. For each image I, compute the image-level repeat factor: - :math:`r(I) = max_{c in I} r(c)` - - Args: - dataset (:obj:`CustomDataset`): The dataset to be repeated. - oversample_thr (float): frequency threshold below which data is - repeated. For categories with ``f_c >= oversample_thr``, there is - no oversampling. For categories with ``f_c < oversample_thr``, the - degree of oversampling following the square-root inverse frequency - heuristic above. - filter_empty_gt (bool, optional): If set true, images without bounding - boxes will not be oversampled. Otherwise, they will be categorized - as the pure background class and involved into the oversampling. - Default: True. - """ - - def __init__(self, dataset, oversample_thr, filter_empty_gt=True): - self.dataset = dataset - self.oversample_thr = oversample_thr - self.filter_empty_gt = filter_empty_gt - self.CLASSES = dataset.CLASSES - self.PALETTE = getattr(dataset, 'PALETTE', None) - - repeat_factors = self._get_repeat_factors(dataset, oversample_thr) - repeat_indices = [] - for dataset_idx, repeat_factor in enumerate(repeat_factors): - repeat_indices.extend([dataset_idx] * math.ceil(repeat_factor)) - self.repeat_indices = repeat_indices - - flags = [] - if hasattr(self.dataset, 'flag'): - for flag, repeat_factor in zip(self.dataset.flag, repeat_factors): - flags.extend([flag] * int(math.ceil(repeat_factor))) - assert len(flags) == len(repeat_indices) - self.flag = np.asarray(flags, dtype=np.uint8) - - def _get_repeat_factors(self, dataset, repeat_thr): - """Get repeat factor for each images in the dataset. - - Args: - dataset (:obj:`CustomDataset`): The dataset - repeat_thr (float): The threshold of frequency. If an image - contains the categories whose frequency below the threshold, - it would be repeated. - - Returns: - list[float]: The repeat factors for each images in the dataset. - """ - - # 1. For each category c, compute the fraction # of images - # that contain it: f(c) - category_freq = defaultdict(int) - num_images = len(dataset) - for idx in range(num_images): - cat_ids = set(self.dataset.get_cat_ids(idx)) - if len(cat_ids) == 0 and not self.filter_empty_gt: - cat_ids = set([len(self.CLASSES)]) - for cat_id in cat_ids: - category_freq[cat_id] += 1 - for k, v in category_freq.items(): - category_freq[k] = v / num_images - - # 2. For each category c, compute the category-level repeat factor: - # r(c) = max(1, sqrt(t/f(c))) - category_repeat = { - cat_id: max(1.0, math.sqrt(repeat_thr / cat_freq)) - for cat_id, cat_freq in category_freq.items() - } - - # 3. For each image I, compute the image-level repeat factor: - # r(I) = max_{c in I} r(c) - repeat_factors = [] - for idx in range(num_images): - cat_ids = set(self.dataset.get_cat_ids(idx)) - if len(cat_ids) == 0 and not self.filter_empty_gt: - cat_ids = set([len(self.CLASSES)]) - repeat_factor = 1 - if len(cat_ids) > 0: - repeat_factor = max( - {category_repeat[cat_id] - for cat_id in cat_ids}) - repeat_factors.append(repeat_factor) - - return repeat_factors - - def __getitem__(self, idx): - ori_index = self.repeat_indices[idx] - return self.dataset[ori_index] - - def get_ann_info(self, idx): - """Get annotation of dataset by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - ori_index = self.repeat_indices[idx] - return self.dataset.get_ann_info(ori_index) - - def __len__(self): - """Length after repetition.""" - return len(self.repeat_indices) - - -@DATASETS.register_module() -class MultiImageMixDataset: - """A wrapper of multiple images mixed dataset. - - Suitable for training on multiple images mixed data augmentation like - mosaic and mixup. For the augmentation pipeline of mixed image data, - the `get_indexes` method needs to be provided to obtain the image - indexes, and you can set `skip_flags` to change the pipeline running - process. At the same time, we provide the `dynamic_scale` parameter - to dynamically change the output image size. - - Args: - dataset (:obj:`CustomDataset`): The dataset to be mixed. - pipeline (Sequence[dict]): Sequence of transform object or - config dict to be composed. - dynamic_scale (tuple[int], optional): The image scale can be changed - dynamically. Default to None. It is deprecated. - skip_type_keys (list[str], optional): Sequence of type string to - be skip pipeline. Default to None. - max_refetch (int): The maximum number of retry iterations for getting - valid results from the pipeline. If the number of iterations is - greater than `max_refetch`, but results is still None, then the - iteration is terminated and raise the error. Default: 15. - """ - - def __init__(self, - dataset, - pipeline, - dynamic_scale=None, - skip_type_keys=None, - max_refetch=15): - if dynamic_scale is not None: - raise RuntimeError( - 'dynamic_scale is deprecated. Please use Resize pipeline ' - 'to achieve similar functions') - assert isinstance(pipeline, collections.abc.Sequence) - if skip_type_keys is not None: - assert all([ - isinstance(skip_type_key, str) - for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys - - self.pipeline = [] - self.pipeline_types = [] - for transform in pipeline: - if isinstance(transform, dict): - self.pipeline_types.append(transform['type']) - transform = build_from_cfg(transform, PIPELINES) - self.pipeline.append(transform) - else: - raise TypeError('pipeline must be a dict') - - self.dataset = dataset - self.CLASSES = dataset.CLASSES - self.PALETTE = getattr(dataset, 'PALETTE', None) - if hasattr(self.dataset, 'flag'): - self.flag = dataset.flag - self.num_samples = len(dataset) - self.max_refetch = max_refetch - - def __len__(self): - return self.num_samples - - def __getitem__(self, idx): - results = copy.deepcopy(self.dataset[idx]) - for (transform, transform_type) in zip(self.pipeline, - self.pipeline_types): - if self._skip_type_keys is not None and \ - transform_type in self._skip_type_keys: - continue - - if hasattr(transform, 'get_indexes'): - for i in range(self.max_refetch): - # Make sure the results passed the loading pipeline - # of the original dataset is not None. - indexes = transform.get_indexes(self.dataset) - if not isinstance(indexes, collections.abc.Sequence): - indexes = [indexes] - mix_results = [ - copy.deepcopy(self.dataset[index]) for index in indexes - ] - if None not in mix_results: - results['mix_results'] = mix_results - break - else: - raise RuntimeError( - 'The loading pipeline of the original dataset' - ' always return None. Please check the correctness ' - 'of the dataset and its pipeline.') - - for i in range(self.max_refetch): - # To confirm the results passed the training pipeline - # of the wrapper is not None. - updated_results = transform(copy.deepcopy(results)) - if updated_results is not None: - results = updated_results - break - else: - raise RuntimeError( - 'The training pipeline of the dataset wrapper' - ' always return None.Please check the correctness ' - 'of the dataset and its pipeline.') - - if 'mix_results' in results: - results.pop('mix_results') - - return results - - def update_skip_type_keys(self, skip_type_keys): - """Update skip_type_keys. It is called by an external hook. - - Args: - skip_type_keys (list[str], optional): Sequence of type - string to be skip pipeline. - """ - assert all([ - isinstance(skip_type_key, str) for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/__init__.py deleted file mode 100644 index e9048278d..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .compose import Compose -from .formatting import (Collect, DefaultFormatBundle, ImageToTensor, - ToDataContainer, ToTensor, Transpose, to_tensor) -from .loading import (FilterAnnotations, LoadAnnotations, LoadImageFromFile, - LoadImageFromWebcam, LoadMultiChannelImageFromFiles, - LoadPanopticAnnotations, LoadProposals) -from .test_time_aug import MultiScaleFlipAug -from .transforms import (Albu, CopyPaste, CutOut, Expand, MinIoURandomCrop, - MixUp, Mosaic, Normalize, Pad, PhotoMetricDistortion, - RandomAffine, RandomCenterCropPad, RandomCrop, - RandomFlip, RandomShift, Resize, SegRescale, - YOLOXHSVRandomAug) - diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/compose.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/compose.py deleted file mode 100644 index d75922009..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/compose.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections - -from mmcv.utils import build_from_cfg - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class Compose: - """Compose multiple transforms sequentially. - - Args: - transforms (Sequence[dict | callable]): Sequence of transform object or - config dict to be composed. - """ - - def __init__(self, transforms): - assert isinstance(transforms, collections.abc.Sequence) - self.transforms = [] - for transform in transforms: - if isinstance(transform, dict): - transform = build_from_cfg(transform, PIPELINES) - self.transforms.append(transform) - elif callable(transform): - self.transforms.append(transform) - else: - raise TypeError('transform must be callable or a dict') - - def __call__(self, data): - """Call function to apply transforms sequentially. - - Args: - data (dict): A result dict contains the data to transform. - - Returns: - dict: Transformed data. - """ - - for t in self.transforms: - data = t(data) - if data is None: - return None - return data - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - str_ = t.__repr__() - if 'Compose(' in str_: - str_ = str_.replace('\n', '\n ') - format_string += '\n' - format_string += f' {str_}' - format_string += '\n)' - return format_string diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/formating.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/formating.py deleted file mode 100644 index 3b3e45abb..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/formating.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -import warnings - -from .formatting import * - -warnings.warn('DeprecationWarning: mmdet.datasets.pipelines.formating will be ' - 'deprecated, please replace it with ' - 'mmdet.datasets.pipelines.formatting.') diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/formatting.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/formatting.py deleted file mode 100644 index 45ca69cfc..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/formatting.py +++ /dev/null @@ -1,392 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import mmcv -import numpy as np -import torch -from mmcv.parallel import DataContainer as DC - -from ..builder import PIPELINES - - -def to_tensor(data): - """Convert objects of various python types to :obj:`torch.Tensor`. - - Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, - :class:`Sequence`, :class:`int` and :class:`float`. - - Args: - data (torch.Tensor | numpy.ndarray | Sequence | int | float): Data to - be converted. - """ - - if isinstance(data, torch.Tensor): - return data - elif isinstance(data, np.ndarray): - return torch.from_numpy(data) - elif isinstance(data, Sequence) and not mmcv.is_str(data): - return torch.tensor(data) - elif isinstance(data, int): - return torch.LongTensor([data]) - elif isinstance(data, float): - return torch.FloatTensor([data]) - else: - raise TypeError(f'type {type(data)} cannot be converted to tensor.') - - -@PIPELINES.register_module() -class ToTensor: - """Convert some results to :obj:`torch.Tensor` by given keys. - - Args: - keys (Sequence[str]): Keys that need to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert data in results to :obj:`torch.Tensor`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted - to :obj:`torch.Tensor`. - """ - for key in self.keys: - results[key] = to_tensor(results[key]) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class ImageToTensor: - """Convert image to :obj:`torch.Tensor` by given keys. - - The dimension order of input image is (H, W, C). The pipeline will convert - it to (C, H, W). If only 2 dimension (H, W) is given, the output would be - (1, H, W). - - Args: - keys (Sequence[str]): Key of images to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - for key in self.keys: - img = results[key] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - results[key] = (to_tensor(img.transpose(2, 0, 1))).contiguous() - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class Transpose: - """Transpose some results by given keys. - - Args: - keys (Sequence[str]): Keys of results to be transposed. - order (Sequence[int]): Order of transpose. - """ - - def __init__(self, keys, order): - self.keys = keys - self.order = order - - def __call__(self, results): - """Call function to transpose the channel order of data in results. - - Args: - results (dict): Result dict contains the data to transpose. - - Returns: - dict: The result dict contains the data transposed to \ - ``self.order``. - """ - for key in self.keys: - results[key] = results[key].transpose(self.order) - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, order={self.order})' - - -@PIPELINES.register_module() -class ToDataContainer: - """Convert results to :obj:`mmcv.DataContainer` by given fields. - - Args: - fields (Sequence[dict]): Each field is a dict like - ``dict(key='xxx', **kwargs)``. The ``key`` in result will - be converted to :obj:`mmcv.DataContainer` with ``**kwargs``. - Default: ``(dict(key='img', stack=True), dict(key='gt_bboxes'), - dict(key='gt_labels'))``. - """ - - def __init__(self, - fields=(dict(key='img', stack=True), dict(key='gt_bboxes'), - dict(key='gt_labels'))): - self.fields = fields - - def __call__(self, results): - """Call function to convert data in results to - :obj:`mmcv.DataContainer`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted to \ - :obj:`mmcv.DataContainer`. - """ - - for field in self.fields: - field = field.copy() - key = field.pop('key') - results[key] = DC(results[key], **field) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(fields={self.fields})' - - -@PIPELINES.register_module() -class DefaultFormatBundle: - """Default formatting bundle. - - It simplifies the pipeline of formatting common fields, including "img", - "proposals", "gt_bboxes", "gt_labels", "gt_masks" and "gt_semantic_seg". - These fields are formatted as follows. - - - img: (1)transpose, (2)to tensor, (3)to DataContainer (stack=True) - - proposals: (1)to tensor, (2)to DataContainer - - gt_bboxes: (1)to tensor, (2)to DataContainer - - gt_bboxes_ignore: (1)to tensor, (2)to DataContainer - - gt_labels: (1)to tensor, (2)to DataContainer - - gt_masks: (1)to tensor, (2)to DataContainer (cpu_only=True) - - gt_semantic_seg: (1)unsqueeze dim-0 (2)to tensor, \ - (3)to DataContainer (stack=True) - - Args: - img_to_float (bool): Whether to force the image to be converted to - float type. Default: True. - pad_val (dict): A dict for padding value in batch collating, - the default value is `dict(img=0, masks=0, seg=255)`. - Without this argument, the padding value of "gt_semantic_seg" - will be set to 0 by default, which should be 255. - """ - - def __init__(self, - img_to_float=True, - pad_val=dict(img=0, masks=0, seg=255)): - self.img_to_float = img_to_float - self.pad_val = pad_val - - def __call__(self, results): - """Call function to transform and format common fields in results. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data that is formatted with \ - default bundle. - """ - - if 'img' in results: - img = results['img'] - if self.img_to_float is True and img.dtype == np.uint8: - # Normally, image is of uint8 type without normalization. - # At this time, it needs to be forced to be converted to - # flot32, otherwise the model training and inference - # will be wrong. Only used for YOLOX currently . - img = img.astype(np.float32) - # add default meta keys - results = self._add_default_meta_keys(results) - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - img = np.ascontiguousarray(img.transpose(2, 0, 1)) - results['img'] = DC( - to_tensor(img), padding_value=self.pad_val['img'], stack=True) - for key in ['proposals', 'gt_bboxes', 'gt_bboxes_ignore', 'gt_labels']: - if key not in results: - continue - results[key] = DC(to_tensor(results[key])) - if 'gt_masks' in results: - results['gt_masks'] = DC( - results['gt_masks'], - padding_value=self.pad_val['masks'], - cpu_only=True) - if 'gt_semantic_seg' in results: - results['gt_semantic_seg'] = DC( - to_tensor(results['gt_semantic_seg'][None, ...]), - padding_value=self.pad_val['seg'], - stack=True) - return results - - def _add_default_meta_keys(self, results): - """Add default meta keys. - - We set default meta keys including `pad_shape`, `scale_factor` and - `img_norm_cfg` to avoid the case where no `Resize`, `Normalize` and - `Pad` are implemented during the whole pipeline. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - results (dict): Updated result dict contains the data to convert. - """ - img = results['img'] - results.setdefault('pad_shape', img.shape) - results.setdefault('scale_factor', 1.0) - num_channels = 1 if len(img.shape) < 3 else img.shape[2] - results.setdefault( - 'img_norm_cfg', - dict( - mean=np.zeros(num_channels, dtype=np.float32), - std=np.ones(num_channels, dtype=np.float32), - to_rgb=False)) - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(img_to_float={self.img_to_float})' - - -@PIPELINES.register_module() -class Collect: - """Collect data from the loader relevant to the specific task. - - This is usually the last stage of the data loader pipeline. Typically keys - is set to some subset of "img", "proposals", "gt_bboxes", - "gt_bboxes_ignore", "gt_labels", and/or "gt_masks". - - The "img_meta" item is always populated. The contents of the "img_meta" - dictionary depends on "meta_keys". By default this includes: - - - "img_shape": shape of the image input to the network as a tuple \ - (h, w, c). Note that images may be zero padded on the \ - bottom/right if the batch tensor is larger than this shape. - - - "scale_factor": a float indicating the preprocessing scale - - - "flip": a boolean indicating if image flip transform was used - - - "filename": path to the image file - - - "ori_shape": original shape of the image as a tuple (h, w, c) - - - "pad_shape": image shape after padding - - - "img_norm_cfg": a dict of normalization information: - - - mean - per channel mean subtraction - - std - per channel std divisor - - to_rgb - bool indicating if bgr was converted to rgb - - Args: - keys (Sequence[str]): Keys of results to be collected in ``data``. - meta_keys (Sequence[str], optional): Meta keys to be converted to - ``mmcv.DataContainer`` and collected in ``data[img_metas]``. - Default: ``('filename', 'ori_filename', 'ori_shape', 'img_shape', - 'pad_shape', 'scale_factor', 'flip', 'flip_direction', - 'img_norm_cfg')`` - """ - - def __init__(self, - keys, - meta_keys=('filename', 'ori_filename', 'ori_shape', - 'img_shape', 'pad_shape', 'scale_factor', 'flip', - 'flip_direction', 'img_norm_cfg')): - self.keys = keys - self.meta_keys = meta_keys - - def __call__(self, results): - """Call function to collect keys in results. The keys in ``meta_keys`` - will be converted to :obj:mmcv.DataContainer. - - Args: - results (dict): Result dict contains the data to collect. - - Returns: - dict: The result dict contains the following keys - - - keys in``self.keys`` - - ``img_metas`` - """ - - data = {} - img_meta = {} - for key in self.meta_keys: - img_meta[key] = results[key] - data['img_metas'] = DC(img_meta, cpu_only=True) - for key in self.keys: - data[key] = results[key] - return data - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, meta_keys={self.meta_keys})' - - -@PIPELINES.register_module() -class WrapFieldsToLists: - """Wrap fields of the data dictionary into lists for evaluation. - - This class can be used as a last step of a test or validation - pipeline for single image evaluation or inference. - - Example: - >>> test_pipeline = [ - >>> dict(type='LoadImageFromFile'), - >>> dict(type='Normalize', - mean=[123.675, 116.28, 103.53], - std=[58.395, 57.12, 57.375], - to_rgb=True), - >>> dict(type='Pad', size_divisor=32), - >>> dict(type='ImageToTensor', keys=['img']), - >>> dict(type='Collect', keys=['img']), - >>> dict(type='WrapFieldsToLists') - >>> ] - """ - - def __call__(self, results): - """Call function to wrap fields into lists. - - Args: - results (dict): Result dict contains the data to wrap. - - Returns: - dict: The result dict where value of ``self.keys`` are wrapped \ - into list. - """ - - # Wrap dict fields into lists - for key, val in results.items(): - results[key] = [val] - return results - - def __repr__(self): - return f'{self.__class__.__name__}()' diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/loading.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/loading.py deleted file mode 100644 index 79bbf8099..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/loading.py +++ /dev/null @@ -1,643 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np -import pycocotools.mask as maskUtils - -from mmdet.core import BitmapMasks, PolygonMasks -from ..builder import PIPELINES - -try: - from panopticapi.utils import rgb2id -except ImportError: - rgb2id = None - - -@PIPELINES.register_module() -class LoadImageFromFile: - """Load an image from file. - - Required keys are "img_prefix" and "img_info" (a dict that must contain the - key "filename"). Added or updated keys are "filename", "img", "img_shape", - "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`), - "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1). - - Args: - to_float32 (bool): Whether to convert the loaded image to a float32 - numpy array. If set to False, the loaded image is an uint8 array. - Defaults to False. - color_type (str): The flag argument for :func:`mmcv.imfrombytes`. - Defaults to 'color'. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - def __init__(self, - to_float32=False, - color_type='color', - channel_order='bgr', - file_client_args=dict(backend='disk')): - self.to_float32 = to_float32 - self.color_type = color_type - self.channel_order = channel_order - self.file_client_args = file_client_args.copy() - self.file_client = None - - def __call__(self, results): - """Call functions to load image and get image meta information. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded image and meta information. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results['img_prefix'] is not None: - filename = osp.join(results['img_prefix'], - results['img_info']['filename']) - else: - filename = results['img_info']['filename'] - - img_bytes = self.file_client.get(filename) - img = mmcv.imfrombytes( - img_bytes, flag=self.color_type, channel_order=self.channel_order) - if self.to_float32: - img = img.astype(np.float32) - - results['filename'] = filename - results['ori_filename'] = results['img_info']['filename'] - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - results['img_fields'] = ['img'] - return results - - def __repr__(self): - repr_str = (f'{self.__class__.__name__}(' - f'to_float32={self.to_float32}, ' - f"color_type='{self.color_type}', " - f"channel_order='{self.channel_order}', " - f'file_client_args={self.file_client_args})') - return repr_str - - -@PIPELINES.register_module() -class LoadImageFromWebcam(LoadImageFromFile): - """Load an image from webcam. - - Similar with :obj:`LoadImageFromFile`, but the image read from webcam is in - ``results['img']``. - """ - - def __call__(self, results): - """Call functions to add image meta information. - - Args: - results (dict): Result dict with Webcam read image in - ``results['img']``. - - Returns: - dict: The dict contains loaded image and meta information. - """ - - img = results['img'] - if self.to_float32: - img = img.astype(np.float32) - - results['filename'] = None - results['ori_filename'] = None - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - results['img_fields'] = ['img'] - return results - - -@PIPELINES.register_module() -class LoadMultiChannelImageFromFiles: - """Load multi-channel images from a list of separate channel files. - - Required keys are "img_prefix" and "img_info" (a dict that must contain the - key "filename", which is expected to be a list of filenames). - Added or updated keys are "filename", "img", "img_shape", - "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`), - "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1). - - Args: - to_float32 (bool): Whether to convert the loaded image to a float32 - numpy array. If set to False, the loaded image is an uint8 array. - Defaults to False. - color_type (str): The flag argument for :func:`mmcv.imfrombytes`. - Defaults to 'color'. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - def __init__(self, - to_float32=False, - color_type='unchanged', - file_client_args=dict(backend='disk')): - self.to_float32 = to_float32 - self.color_type = color_type - self.file_client_args = file_client_args.copy() - self.file_client = None - - def __call__(self, results): - """Call functions to load multiple images and get images meta - information. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded images and meta information. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results['img_prefix'] is not None: - filename = [ - osp.join(results['img_prefix'], fname) - for fname in results['img_info']['filename'] - ] - else: - filename = results['img_info']['filename'] - - img = [] - for name in filename: - img_bytes = self.file_client.get(name) - img.append(mmcv.imfrombytes(img_bytes, flag=self.color_type)) - img = np.stack(img, axis=-1) - if self.to_float32: - img = img.astype(np.float32) - - results['filename'] = filename - results['ori_filename'] = results['img_info']['filename'] - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - # Set initial values for default meta_keys - results['pad_shape'] = img.shape - results['scale_factor'] = 1.0 - num_channels = 1 if len(img.shape) < 3 else img.shape[2] - results['img_norm_cfg'] = dict( - mean=np.zeros(num_channels, dtype=np.float32), - std=np.ones(num_channels, dtype=np.float32), - to_rgb=False) - return results - - def __repr__(self): - repr_str = (f'{self.__class__.__name__}(' - f'to_float32={self.to_float32}, ' - f"color_type='{self.color_type}', " - f'file_client_args={self.file_client_args})') - return repr_str - - -@PIPELINES.register_module() -class LoadAnnotations: - """Load multiple types of annotations. - - Args: - with_bbox (bool): Whether to parse and load the bbox annotation. - Default: True. - with_label (bool): Whether to parse and load the label annotation. - Default: True. - with_mask (bool): Whether to parse and load the mask annotation. - Default: False. - with_seg (bool): Whether to parse and load the semantic segmentation - annotation. Default: False. - poly2mask (bool): Whether to convert the instance masks from polygons - to bitmaps. Default: True. - denorm_bbox (bool): Whether to convert bbox from relative value to - absolute value. Only used in OpenImage Dataset. - Default: False. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - def __init__(self, - with_bbox=True, - with_label=True, - with_mask=False, - with_seg=False, - poly2mask=True, - denorm_bbox=False, - file_client_args=dict(backend='disk')): - self.with_bbox = with_bbox - self.with_label = with_label - self.with_mask = with_mask - self.with_seg = with_seg - self.poly2mask = poly2mask - self.denorm_bbox = denorm_bbox - self.file_client_args = file_client_args.copy() - self.file_client = None - - def _load_bboxes(self, results): - """Private function to load bounding box annotations. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded bounding box annotations. - """ - - ann_info = results['ann_info'] - results['gt_bboxes'] = ann_info['bboxes'].copy() - - if self.denorm_bbox: - bbox_num = results['gt_bboxes'].shape[0] - if bbox_num != 0: - h, w = results['img_shape'][:2] - results['gt_bboxes'][:, 0::2] *= w - results['gt_bboxes'][:, 1::2] *= h - - gt_bboxes_ignore = ann_info.get('bboxes_ignore', None) - if gt_bboxes_ignore is not None: - results['gt_bboxes_ignore'] = gt_bboxes_ignore.copy() - results['bbox_fields'].append('gt_bboxes_ignore') - results['bbox_fields'].append('gt_bboxes') - - gt_is_group_ofs = ann_info.get('gt_is_group_ofs', None) - if gt_is_group_ofs is not None: - results['gt_is_group_ofs'] = gt_is_group_ofs.copy() - - return results - - def _load_labels(self, results): - """Private function to load label annotations. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded label annotations. - """ - - results['gt_labels'] = results['ann_info']['labels'].copy() - return results - - def _poly2mask(self, mask_ann, img_h, img_w): - """Private function to convert masks represented with polygon to - bitmaps. - - Args: - mask_ann (list | dict): Polygon mask annotation input. - img_h (int): The height of output mask. - img_w (int): The width of output mask. - - Returns: - numpy.ndarray: The decode bitmap mask of shape (img_h, img_w). - """ - - if isinstance(mask_ann, list): - # polygon -- a single object might consist of multiple parts - # we merge all parts into one mask rle code - rles = maskUtils.frPyObjects(mask_ann, img_h, img_w) - rle = maskUtils.merge(rles) - elif isinstance(mask_ann['counts'], list): - # uncompressed RLE - rle = maskUtils.frPyObjects(mask_ann, img_h, img_w) - else: - # rle - rle = mask_ann - mask = maskUtils.decode(rle) - return mask - - def process_polygons(self, polygons): - """Convert polygons to list of ndarray and filter invalid polygons. - - Args: - polygons (list[list]): Polygons of one instance. - - Returns: - list[numpy.ndarray]: Processed polygons. - """ - - polygons = [np.array(p) for p in polygons] - valid_polygons = [] - for polygon in polygons: - if len(polygon) % 2 == 0 and len(polygon) >= 6: - valid_polygons.append(polygon) - return valid_polygons - - def _load_masks(self, results): - """Private function to load mask annotations. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded mask annotations. - If ``self.poly2mask`` is set ``True``, `gt_mask` will contain - :obj:`PolygonMasks`. Otherwise, :obj:`BitmapMasks` is used. - """ - - h, w = results['img_info']['height'], results['img_info']['width'] - gt_masks = results['ann_info']['masks'] - if self.poly2mask: - gt_masks = BitmapMasks( - [self._poly2mask(mask, h, w) for mask in gt_masks], h, w) - else: - gt_masks = PolygonMasks( - [self.process_polygons(polygons) for polygons in gt_masks], h, - w) - results['gt_masks'] = gt_masks - results['mask_fields'].append('gt_masks') - return results - - def _load_semantic_seg(self, results): - """Private function to load semantic segmentation annotations. - - Args: - results (dict): Result dict from :obj:`dataset`. - - Returns: - dict: The dict contains loaded semantic segmentation annotations. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - filename = osp.join(results['seg_prefix'], - results['ann_info']['seg_map']) - img_bytes = self.file_client.get(filename) - results['gt_semantic_seg'] = mmcv.imfrombytes( - img_bytes, flag='unchanged').squeeze() - results['seg_fields'].append('gt_semantic_seg') - return results - - def __call__(self, results): - """Call function to load multiple types annotations. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded bounding box, label, mask and - semantic segmentation annotations. - """ - - if self.with_bbox: - results = self._load_bboxes(results) - if results is None: - return None - if self.with_label: - results = self._load_labels(results) - if self.with_mask: - results = self._load_masks(results) - if self.with_seg: - results = self._load_semantic_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(with_bbox={self.with_bbox}, ' - repr_str += f'with_label={self.with_label}, ' - repr_str += f'with_mask={self.with_mask}, ' - repr_str += f'with_seg={self.with_seg}, ' - repr_str += f'poly2mask={self.poly2mask}, ' - repr_str += f'poly2mask={self.file_client_args})' - return repr_str - - -@PIPELINES.register_module() -class LoadPanopticAnnotations(LoadAnnotations): - """Load multiple types of panoptic annotations. - - Args: - with_bbox (bool): Whether to parse and load the bbox annotation. - Default: True. - with_label (bool): Whether to parse and load the label annotation. - Default: True. - with_mask (bool): Whether to parse and load the mask annotation. - Default: True. - with_seg (bool): Whether to parse and load the semantic segmentation - annotation. Default: True. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - def __init__(self, - with_bbox=True, - with_label=True, - with_mask=True, - with_seg=True, - file_client_args=dict(backend='disk')): - if rgb2id is None: - raise RuntimeError( - 'panopticapi is not installed, please install it by: ' - 'pip install git+https://github.com/cocodataset/' - 'panopticapi.git.') - - super(LoadPanopticAnnotations, self).__init__( - with_bbox=with_bbox, - with_label=with_label, - with_mask=with_mask, - with_seg=with_seg, - poly2mask=True, - denorm_bbox=False, - file_client_args=file_client_args) - - def _load_masks_and_semantic_segs(self, results): - """Private function to load mask and semantic segmentation annotations. - - In gt_semantic_seg, the foreground label is from `0` to - `num_things - 1`, the background label is from `num_things` to - `num_things + num_stuff - 1`, 255 means the ignored label (`VOID`). - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded mask and semantic segmentation - annotations. `BitmapMasks` is used for mask annotations. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - filename = osp.join(results['seg_prefix'], - results['ann_info']['seg_map']) - img_bytes = self.file_client.get(filename) - pan_png = mmcv.imfrombytes( - img_bytes, flag='color', channel_order='rgb').squeeze() - pan_png = rgb2id(pan_png) - - gt_masks = [] - gt_seg = np.zeros_like(pan_png) + 255 # 255 as ignore - - for mask_info in results['ann_info']['masks']: - mask = (pan_png == mask_info['id']) - gt_seg = np.where(mask, mask_info['category'], gt_seg) - - # The legal thing masks - if mask_info.get('is_thing'): - gt_masks.append(mask.astype(np.uint8)) - - if self.with_mask: - h, w = results['img_info']['height'], results['img_info']['width'] - gt_masks = BitmapMasks(gt_masks, h, w) - results['gt_masks'] = gt_masks - results['mask_fields'].append('gt_masks') - - if self.with_seg: - results['gt_semantic_seg'] = gt_seg - results['seg_fields'].append('gt_semantic_seg') - return results - - def __call__(self, results): - """Call function to load multiple types panoptic annotations. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded bounding box, label, mask and - semantic segmentation annotations. - """ - - if self.with_bbox: - results = self._load_bboxes(results) - if results is None: - return None - if self.with_label: - results = self._load_labels(results) - if self.with_mask or self.with_seg: - # The tasks completed by '_load_masks' and '_load_semantic_segs' - # in LoadAnnotations are merged to one function. - results = self._load_masks_and_semantic_segs(results) - - return results - - -@PIPELINES.register_module() -class LoadProposals: - """Load proposal pipeline. - - Required key is "proposals". Updated keys are "proposals", "bbox_fields". - - Args: - num_max_proposals (int, optional): Maximum number of proposals to load. - If not specified, all proposals will be loaded. - """ - - def __init__(self, num_max_proposals=None): - self.num_max_proposals = num_max_proposals - - def __call__(self, results): - """Call function to load proposals from file. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded proposal annotations. - """ - - proposals = results['proposals'] - if proposals.shape[1] not in (4, 5): - raise AssertionError( - 'proposals should have shapes (n, 4) or (n, 5), ' - f'but found {proposals.shape}') - proposals = proposals[:, :4] - - if self.num_max_proposals is not None: - proposals = proposals[:self.num_max_proposals] - - if len(proposals) == 0: - proposals = np.array([[0, 0, 0, 0]], dtype=np.float32) - results['proposals'] = proposals - results['bbox_fields'].append('proposals') - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(num_max_proposals={self.num_max_proposals})' - - -@PIPELINES.register_module() -class FilterAnnotations: - """Filter invalid annotations. - - Args: - min_gt_bbox_wh (tuple[float]): Minimum width and height of ground truth - boxes. Default: (1., 1.) - min_gt_mask_area (int): Minimum foreground area of ground truth masks. - Default: 1 - by_box (bool): Filter instances with bounding boxes not meeting the - min_gt_bbox_wh threshold. Default: True - by_mask (bool): Filter instances with masks not meeting - min_gt_mask_area threshold. Default: False - keep_empty (bool): Whether to return None when it - becomes an empty bbox after filtering. Default: True - """ - - def __init__(self, - min_gt_bbox_wh=(1., 1.), - min_gt_mask_area=1, - by_box=True, - by_mask=False, - keep_empty=True): - # TODO: add more filter options - assert by_box or by_mask - self.min_gt_bbox_wh = min_gt_bbox_wh - self.min_gt_mask_area = min_gt_mask_area - self.by_box = by_box - self.by_mask = by_mask - self.keep_empty = keep_empty - - def __call__(self, results): - if self.by_box: - assert 'gt_bboxes' in results - gt_bboxes = results['gt_bboxes'] - instance_num = gt_bboxes.shape[0] - if self.by_mask: - assert 'gt_masks' in results - gt_masks = results['gt_masks'] - instance_num = len(gt_masks) - - if instance_num == 0: - return results - - tests = [] - if self.by_box: - w = gt_bboxes[:, 2] - gt_bboxes[:, 0] - h = gt_bboxes[:, 3] - gt_bboxes[:, 1] - tests.append((w > self.min_gt_bbox_wh[0]) - & (h > self.min_gt_bbox_wh[1])) - if self.by_mask: - gt_masks = results['gt_masks'] - tests.append(gt_masks.areas >= self.min_gt_mask_area) - - keep = tests[0] - for t in tests[1:]: - keep = keep & t - - keys = ('gt_bboxes', 'gt_labels', 'gt_masks') - for key in keys: - if key in results: - results[key] = results[key][keep] - if not keep.any(): - if self.keep_empty: - return None - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(min_gt_bbox_wh={self.min_gt_bbox_wh},' \ - f'(min_gt_mask_area={self.min_gt_mask_area},' \ - f'(by_box={self.by_box},' \ - f'(by_mask={self.by_mask},' \ - f'always_keep={self.always_keep})' diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/test_time_aug.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/test_time_aug.py deleted file mode 100644 index 5f1ab7b7c..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/test_time_aug.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv - -from ..builder import PIPELINES -from .compose import Compose - - -@PIPELINES.register_module() -class MultiScaleFlipAug: - """Test-time augmentation with multiple scales and flipping. - - An example configuration is as followed: - - .. code-block:: - - img_scale=[(1333, 400), (1333, 800)], - flip=True, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ] - - After MultiScaleFLipAug with above configuration, the results are wrapped - into lists of the same length as followed: - - .. code-block:: - - dict( - img=[...], - img_shape=[...], - scale=[(1333, 400), (1333, 400), (1333, 800), (1333, 800)] - flip=[False, True, False, True] - ... - ) - - Args: - transforms (list[dict]): Transforms to apply in each augmentation. - img_scale (tuple | list[tuple] | None): Images scales for resizing. - scale_factor (float | list[float] | None): Scale factors for resizing. - flip (bool): Whether apply flip augmentation. Default: False. - flip_direction (str | list[str]): Flip augmentation directions, - options are "horizontal", "vertical" and "diagonal". If - flip_direction is a list, multiple flip augmentations will be - applied. It has no effect when flip == False. Default: - "horizontal". - """ - - def __init__(self, - transforms, - img_scale=None, - scale_factor=None, - flip=False, - flip_direction='horizontal'): - self.transforms = Compose(transforms) - assert (img_scale is None) ^ (scale_factor is None), ( - 'Must have but only one variable can be set') - if img_scale is not None: - self.img_scale = img_scale if isinstance(img_scale, - list) else [img_scale] - self.scale_key = 'scale' - assert mmcv.is_list_of(self.img_scale, tuple) - else: - self.img_scale = scale_factor if isinstance( - scale_factor, list) else [scale_factor] - self.scale_key = 'scale_factor' - - self.flip = flip - self.flip_direction = flip_direction if isinstance( - flip_direction, list) else [flip_direction] - assert mmcv.is_list_of(self.flip_direction, str) - if not self.flip and self.flip_direction != ['horizontal']: - warnings.warn( - 'flip_direction has no effect when flip is set to False') - if (self.flip - and not any([t['type'] == 'RandomFlip' for t in transforms])): - warnings.warn( - 'flip has no effect when RandomFlip is not in transforms') - - def __call__(self, results): - """Call function to apply test time augment transforms on results. - - Args: - results (dict): Result dict contains the data to transform. - - Returns: - dict[str: list]: The augmented data, where each value is wrapped - into a list. - """ - - aug_data = [] - flip_args = [(False, None)] - if self.flip: - flip_args += [(True, direction) - for direction in self.flip_direction] - for scale in self.img_scale: - for flip, direction in flip_args: - _results = results.copy() - _results[self.scale_key] = scale - _results['flip'] = flip - _results['flip_direction'] = direction - data = self.transforms(_results) - aug_data.append(data) - # list of dict to dict of list - aug_data_dict = {key: [] for key in aug_data[0]} - for data in aug_data: - for key, val in data.items(): - aug_data_dict[key].append(val) - return aug_data_dict - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(transforms={self.transforms}, ' - repr_str += f'img_scale={self.img_scale}, flip={self.flip}, ' - repr_str += f'flip_direction={self.flip_direction})' - return repr_str diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/transforms.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/transforms.py deleted file mode 100644 index 0a1b38911..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/pipelines/transforms.py +++ /dev/null @@ -1,2919 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect -import math -import warnings - -import cv2 -import mmcv -import numpy as np -from numpy import random - -from mmdet.core import BitmapMasks, PolygonMasks, find_inside_bboxes -from mmdet.core.evaluation.bbox_overlaps import bbox_overlaps -from mmdet.utils import log_img_scale -from ..builder import PIPELINES - -try: - from imagecorruptions import corrupt -except ImportError: - corrupt = None - -try: - import albumentations - from albumentations import Compose -except ImportError: - albumentations = None - Compose = None - - -@PIPELINES.register_module() -class Resize: - """Resize images & bbox & mask. - - This transform resizes the input image to some scale. Bboxes and masks are - then resized with the same scale factor. If the input dict contains the key - "scale", then the scale in the input dict is used, otherwise the specified - scale in the init method is used. If the input dict contains the key - "scale_factor" (if MultiScaleFlipAug does not give img_scale but - scale_factor), the actual scale will be computed by image shape and - scale_factor. - - `img_scale` can either be a tuple (single-scale) or a list of tuple - (multi-scale). There are 3 multiscale modes: - - - ``ratio_range is not None``: randomly sample a ratio from the ratio \ - range and multiply it with the image scale. - - ``ratio_range is None`` and ``multiscale_mode == "range"``: randomly \ - sample a scale from the multiscale range. - - ``ratio_range is None`` and ``multiscale_mode == "value"``: randomly \ - sample a scale from multiple scales. - - Args: - img_scale (tuple or list[tuple]): Images scales for resizing. - multiscale_mode (str): Either "range" or "value". - ratio_range (tuple[float]): (min_ratio, max_ratio) - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. - bbox_clip_border (bool, optional): Whether to clip the objects outside - the border of the image. In some dataset like MOT17, the gt bboxes - are allowed to cross the border of images. Therefore, we don't - need to clip the gt bboxes in these cases. Defaults to True. - backend (str): Image resize backend, choices are 'cv2' and 'pillow'. - These two backends generates slightly different results. Defaults - to 'cv2'. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - override (bool, optional): Whether to override `scale` and - `scale_factor` so as to call resize twice. Default False. If True, - after the first resizing, the existed `scale` and `scale_factor` - will be ignored so the second resizing can be allowed. - This option is a work-around for multiple times of resize in DETR. - Defaults to False. - """ - - def __init__(self, - img_scale=None, - multiscale_mode='range', - ratio_range=None, - keep_ratio=True, - bbox_clip_border=True, - backend='cv2', - interpolation='bilinear', - override=False): - if img_scale is None: - self.img_scale = None - else: - if isinstance(img_scale, list): - self.img_scale = img_scale - else: - self.img_scale = [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) - - if ratio_range is not None: - # mode 1: given a scale and a range of image ratio - assert len(self.img_scale) == 1 - else: - # mode 2: given multiple scales or a range of scales - assert multiscale_mode in ['value', 'range'] - - self.backend = backend - self.multiscale_mode = multiscale_mode - self.ratio_range = ratio_range - self.keep_ratio = keep_ratio - # TODO: refactor the override option in Resize - self.interpolation = interpolation - self.override = override - self.bbox_clip_border = bbox_clip_border - - @staticmethod - def random_select(img_scales): - """Randomly select an img_scale from given candidates. - - Args: - img_scales (list[tuple]): Images scales for selection. - - Returns: - (tuple, int): Returns a tuple ``(img_scale, scale_dix)``, \ - where ``img_scale`` is the selected image scale and \ - ``scale_idx`` is the selected index in the given candidates. - """ - - assert mmcv.is_list_of(img_scales, tuple) - scale_idx = np.random.randint(len(img_scales)) - img_scale = img_scales[scale_idx] - return img_scale, scale_idx - - @staticmethod - def random_sample(img_scales): - """Randomly sample an img_scale when ``multiscale_mode=='range'``. - - Args: - img_scales (list[tuple]): Images scale range for sampling. - There must be two tuples in img_scales, which specify the lower - and upper bound of image scales. - - Returns: - (tuple, None): Returns a tuple ``(img_scale, None)``, where \ - ``img_scale`` is sampled scale and None is just a placeholder \ - to be consistent with :func:`random_select`. - """ - - assert mmcv.is_list_of(img_scales, tuple) and len(img_scales) == 2 - img_scale_long = [max(s) for s in img_scales] - img_scale_short = [min(s) for s in img_scales] - long_edge = np.random.randint( - min(img_scale_long), - max(img_scale_long) + 1) - short_edge = np.random.randint( - min(img_scale_short), - max(img_scale_short) + 1) - img_scale = (long_edge, short_edge) - return img_scale, None - - @staticmethod - def random_sample_ratio(img_scale, ratio_range): - """Randomly sample an img_scale when ``ratio_range`` is specified. - - A ratio will be randomly sampled from the range specified by - ``ratio_range``. Then it would be multiplied with ``img_scale`` to - generate sampled scale. - - Args: - img_scale (tuple): Images scale base to multiply with ratio. - ratio_range (tuple[float]): The minimum and maximum ratio to scale - the ``img_scale``. - - Returns: - (tuple, None): Returns a tuple ``(scale, None)``, where \ - ``scale`` is sampled ratio multiplied with ``img_scale`` and \ - None is just a placeholder to be consistent with \ - :func:`random_select`. - """ - - assert isinstance(img_scale, tuple) and len(img_scale) == 2 - min_ratio, max_ratio = ratio_range - assert min_ratio <= max_ratio - ratio = np.random.random_sample() * (max_ratio - min_ratio) + min_ratio - scale = int(img_scale[0] * ratio), int(img_scale[1] * ratio) - return scale, None - - def _random_scale(self, results): - """Randomly sample an img_scale according to ``ratio_range`` and - ``multiscale_mode``. - - If ``ratio_range`` is specified, a ratio will be sampled and be - multiplied with ``img_scale``. - If multiple scales are specified by ``img_scale``, a scale will be - sampled according to ``multiscale_mode``. - Otherwise, single scale will be used. - - Args: - results (dict): Result dict from :obj:`dataset`. - - Returns: - dict: Two new keys 'scale` and 'scale_idx` are added into \ - ``results``, which would be used by subsequent pipelines. - """ - - if self.ratio_range is not None: - scale, scale_idx = self.random_sample_ratio( - self.img_scale[0], self.ratio_range) - elif len(self.img_scale) == 1: - scale, scale_idx = self.img_scale[0], 0 - elif self.multiscale_mode == 'range': - scale, scale_idx = self.random_sample(self.img_scale) - elif self.multiscale_mode == 'value': - scale, scale_idx = self.random_select(self.img_scale) - else: - raise NotImplementedError - - results['scale'] = scale - results['scale_idx'] = scale_idx - - def _resize_img(self, results): - """Resize images with ``results['scale']``.""" - for key in results.get('img_fields', ['img']): - if self.keep_ratio: - img, scale_factor = mmcv.imrescale( - results[key], - results['scale'], - return_scale=True, - interpolation=self.interpolation, - backend=self.backend) - # the w_scale and h_scale has minor difference - # a real fix should be done in the mmcv.imrescale in the future - new_h, new_w = img.shape[:2] - h, w = results[key].shape[:2] - w_scale = new_w / w - h_scale = new_h / h - else: - img, w_scale, h_scale = mmcv.imresize( - results[key], - results['scale'], - return_scale=True, - interpolation=self.interpolation, - backend=self.backend) - results[key] = img - - scale_factor = np.array([w_scale, h_scale, w_scale, h_scale], - dtype=np.float32) - results['img_shape'] = img.shape - # in case that there is no padding - results['pad_shape'] = img.shape - results['scale_factor'] = scale_factor - results['keep_ratio'] = self.keep_ratio - - def _resize_bboxes(self, results): - """Resize bounding boxes with ``results['scale_factor']``.""" - for key in results.get('bbox_fields', []): - bboxes = results[key] * results['scale_factor'] - if self.bbox_clip_border: - img_shape = results['img_shape'] - bboxes[:, 0::2] = np.clip(bboxes[:, 0::2], 0, img_shape[1]) - bboxes[:, 1::2] = np.clip(bboxes[:, 1::2], 0, img_shape[0]) - results[key] = bboxes - - def _resize_masks(self, results): - """Resize masks with ``results['scale']``""" - for key in results.get('mask_fields', []): - if results[key] is None: - continue - if self.keep_ratio: - results[key] = results[key].rescale(results['scale']) - else: - results[key] = results[key].resize(results['img_shape'][:2]) - - def _resize_seg(self, results): - """Resize semantic segmentation map with ``results['scale']``.""" - for key in results.get('seg_fields', []): - if self.keep_ratio: - gt_seg = mmcv.imrescale( - results[key], - results['scale'], - interpolation='nearest', - backend=self.backend) - else: - gt_seg = mmcv.imresize( - results[key], - results['scale'], - interpolation='nearest', - backend=self.backend) - results[key] = gt_seg - - def __call__(self, results): - """Call function to resize images, bounding boxes, masks, semantic - segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape', 'scale_factor', \ - 'keep_ratio' keys are added into result dict. - """ - - if 'scale' not in results: - if 'scale_factor' in results: - img_shape = results['img'].shape[:2] - scale_factor = results['scale_factor'] - assert isinstance(scale_factor, float) - results['scale'] = tuple( - [int(x * scale_factor) for x in img_shape][::-1]) - else: - self._random_scale(results) - else: - if not self.override: - assert 'scale_factor' not in results, ( - 'scale and scale_factor cannot be both set.') - else: - results.pop('scale') - if 'scale_factor' in results: - results.pop('scale_factor') - self._random_scale(results) - - self._resize_img(results) - self._resize_bboxes(results) - self._resize_masks(results) - self._resize_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(img_scale={self.img_scale}, ' - repr_str += f'multiscale_mode={self.multiscale_mode}, ' - repr_str += f'ratio_range={self.ratio_range}, ' - repr_str += f'keep_ratio={self.keep_ratio}, ' - repr_str += f'bbox_clip_border={self.bbox_clip_border})' - return repr_str - - -@PIPELINES.register_module() -class RandomFlip: - """Flip the image & bbox & mask. - - If the input dict contains the key "flip", then the flag will be used, - otherwise it will be randomly decided by a ratio specified in the init - method. - - When random flip is enabled, ``flip_ratio``/``direction`` can either be a - float/string or tuple of float/string. There are 3 flip modes: - - - ``flip_ratio`` is float, ``direction`` is string: the image will be - ``direction``ly flipped with probability of ``flip_ratio`` . - E.g., ``flip_ratio=0.5``, ``direction='horizontal'``, - then image will be horizontally flipped with probability of 0.5. - - ``flip_ratio`` is float, ``direction`` is list of string: the image will - be ``direction[i]``ly flipped with probability of - ``flip_ratio/len(direction)``. - E.g., ``flip_ratio=0.5``, ``direction=['horizontal', 'vertical']``, - then image will be horizontally flipped with probability of 0.25, - vertically with probability of 0.25. - - ``flip_ratio`` is list of float, ``direction`` is list of string: - given ``len(flip_ratio) == len(direction)``, the image will - be ``direction[i]``ly flipped with probability of ``flip_ratio[i]``. - E.g., ``flip_ratio=[0.3, 0.5]``, ``direction=['horizontal', - 'vertical']``, then image will be horizontally flipped with probability - of 0.3, vertically with probability of 0.5. - - Args: - flip_ratio (float | list[float], optional): The flipping probability. - Default: None. - direction(str | list[str], optional): The flipping direction. Options - are 'horizontal', 'vertical', 'diagonal'. Default: 'horizontal'. - If input is a list, the length must equal ``flip_ratio``. Each - element in ``flip_ratio`` indicates the flip probability of - corresponding direction. - """ - - def __init__(self, flip_ratio=None, direction='horizontal'): - if isinstance(flip_ratio, list): - assert mmcv.is_list_of(flip_ratio, float) - assert 0 <= sum(flip_ratio) <= 1 - elif isinstance(flip_ratio, float): - assert 0 <= flip_ratio <= 1 - elif flip_ratio is None: - pass - else: - raise ValueError('flip_ratios must be None, float, ' - 'or list of float') - self.flip_ratio = flip_ratio - - valid_directions = ['horizontal', 'vertical', 'diagonal'] - if isinstance(direction, str): - assert direction in valid_directions - elif isinstance(direction, list): - assert mmcv.is_list_of(direction, str) - assert set(direction).issubset(set(valid_directions)) - else: - raise ValueError('direction must be either str or list of str') - self.direction = direction - - if isinstance(flip_ratio, list): - assert len(self.flip_ratio) == len(self.direction) - - def bbox_flip(self, bboxes, img_shape, direction): - """Flip bboxes horizontally. - - Args: - bboxes (numpy.ndarray): Bounding boxes, shape (..., 4*k) - img_shape (tuple[int]): Image shape (height, width) - direction (str): Flip direction. Options are 'horizontal', - 'vertical'. - - Returns: - numpy.ndarray: Flipped bounding boxes. - """ - - assert bboxes.shape[-1] % 4 == 0 - flipped = bboxes.copy() - if direction == 'horizontal': - w = img_shape[1] - flipped[..., 0::4] = w - bboxes[..., 2::4] - flipped[..., 2::4] = w - bboxes[..., 0::4] - elif direction == 'vertical': - h = img_shape[0] - flipped[..., 1::4] = h - bboxes[..., 3::4] - flipped[..., 3::4] = h - bboxes[..., 1::4] - elif direction == 'diagonal': - w = img_shape[1] - h = img_shape[0] - flipped[..., 0::4] = w - bboxes[..., 2::4] - flipped[..., 1::4] = h - bboxes[..., 3::4] - flipped[..., 2::4] = w - bboxes[..., 0::4] - flipped[..., 3::4] = h - bboxes[..., 1::4] - else: - raise ValueError(f"Invalid flipping direction '{direction}'") - return flipped - - def __call__(self, results): - """Call function to flip bounding boxes, masks, semantic segmentation - maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Flipped results, 'flip', 'flip_direction' keys are added \ - into result dict. - """ - - if 'flip' not in results: - if isinstance(self.direction, list): - # None means non-flip - direction_list = self.direction + [None] - else: - # None means non-flip - direction_list = [self.direction, None] - - if isinstance(self.flip_ratio, list): - non_flip_ratio = 1 - sum(self.flip_ratio) - flip_ratio_list = self.flip_ratio + [non_flip_ratio] - else: - non_flip_ratio = 1 - self.flip_ratio - # exclude non-flip - single_ratio = self.flip_ratio / (len(direction_list) - 1) - flip_ratio_list = [single_ratio] * (len(direction_list) - - 1) + [non_flip_ratio] - - cur_dir = np.random.choice(direction_list, p=flip_ratio_list) - - results['flip'] = cur_dir is not None - if 'flip_direction' not in results: - results['flip_direction'] = cur_dir - if results['flip']: - # flip image - for key in results.get('img_fields', ['img']): - results[key] = mmcv.imflip( - results[key], direction=results['flip_direction']) - # flip bboxes - for key in results.get('bbox_fields', []): - results[key] = self.bbox_flip(results[key], - results['img_shape'], - results['flip_direction']) - # flip masks - for key in results.get('mask_fields', []): - results[key] = results[key].flip(results['flip_direction']) - - # flip segs - for key in results.get('seg_fields', []): - results[key] = mmcv.imflip( - results[key], direction=results['flip_direction']) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(flip_ratio={self.flip_ratio})' - - -@PIPELINES.register_module() -class RandomShift: - """Shift the image and box given shift pixels and probability. - - Args: - shift_ratio (float): Probability of shifts. Default 0.5. - max_shift_px (int): The max pixels for shifting. Default 32. - filter_thr_px (int): The width and height threshold for filtering. - The bbox and the rest of the targets below the width and - height threshold will be filtered. Default 1. - """ - - def __init__(self, shift_ratio=0.5, max_shift_px=32, filter_thr_px=1): - assert 0 <= shift_ratio <= 1 - assert max_shift_px >= 0 - self.shift_ratio = shift_ratio - self.max_shift_px = max_shift_px - self.filter_thr_px = int(filter_thr_px) - # The key correspondence from bboxes to labels. - self.bbox2label = { - 'gt_bboxes': 'gt_labels', - 'gt_bboxes_ignore': 'gt_labels_ignore' - } - - def __call__(self, results): - """Call function to random shift images, bounding boxes. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Shift results. - """ - if random.random() < self.shift_ratio: - img_shape = results['img'].shape[:2] - - random_shift_x = random.randint(-self.max_shift_px, - self.max_shift_px) - random_shift_y = random.randint(-self.max_shift_px, - self.max_shift_px) - new_x = max(0, random_shift_x) - ori_x = max(0, -random_shift_x) - new_y = max(0, random_shift_y) - ori_y = max(0, -random_shift_y) - - # TODO: support mask and semantic segmentation maps. - for key in results.get('bbox_fields', []): - bboxes = results[key].copy() - bboxes[..., 0::2] += random_shift_x - bboxes[..., 1::2] += random_shift_y - - # clip border - bboxes[..., 0::2] = np.clip(bboxes[..., 0::2], 0, img_shape[1]) - bboxes[..., 1::2] = np.clip(bboxes[..., 1::2], 0, img_shape[0]) - - # remove invalid bboxes - bbox_w = bboxes[..., 2] - bboxes[..., 0] - bbox_h = bboxes[..., 3] - bboxes[..., 1] - valid_inds = (bbox_w > self.filter_thr_px) & ( - bbox_h > self.filter_thr_px) - # If the shift does not contain any gt-bbox area, skip this - # image. - if key == 'gt_bboxes' and not valid_inds.any(): - return results - bboxes = bboxes[valid_inds] - results[key] = bboxes - - # label fields. e.g. gt_labels and gt_labels_ignore - label_key = self.bbox2label.get(key) - if label_key in results: - results[label_key] = results[label_key][valid_inds] - - for key in results.get('img_fields', ['img']): - img = results[key] - new_img = np.zeros_like(img) - img_h, img_w = img.shape[:2] - new_h = img_h - np.abs(random_shift_y) - new_w = img_w - np.abs(random_shift_x) - new_img[new_y:new_y + new_h, new_x:new_x + new_w] \ - = img[ori_y:ori_y + new_h, ori_x:ori_x + new_w] - results[key] = new_img - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(max_shift_px={self.max_shift_px}, ' - return repr_str - - -@PIPELINES.register_module() -class Pad: - """Pad the image & masks & segmentation map. - - There are two padding modes: (1) pad to a fixed size and (2) pad to the - minimum size that is divisible by some number. - Added keys are "pad_shape", "pad_fixed_size", "pad_size_divisor", - - Args: - size (tuple, optional): Fixed padding size. - size_divisor (int, optional): The divisor of padded size. - pad_to_square (bool): Whether to pad the image into a square. - Currently only used for YOLOX. Default: False. - pad_val (dict, optional): A dict for padding value, the default - value is `dict(img=0, masks=0, seg=255)`. - """ - - def __init__(self, - size=None, - size_divisor=None, - pad_to_square=False, - pad_val=dict(img=0, masks=0, seg=255)): - self.size = size - self.size_divisor = size_divisor - if isinstance(pad_val, float) or isinstance(pad_val, int): - warnings.warn( - 'pad_val of float type is deprecated now, ' - f'please use pad_val=dict(img={pad_val}, ' - f'masks={pad_val}, seg=255) instead.', DeprecationWarning) - pad_val = dict(img=pad_val, masks=pad_val, seg=255) - assert isinstance(pad_val, dict) - self.pad_val = pad_val - self.pad_to_square = pad_to_square - - if pad_to_square: - assert size is None and size_divisor is None, \ - 'The size and size_divisor must be None ' \ - 'when pad2square is True' - else: - assert size is not None or size_divisor is not None, \ - 'only one of size and size_divisor should be valid' - assert size is None or size_divisor is None - - def _pad_img(self, results): - """Pad images according to ``self.size``.""" - pad_val = self.pad_val.get('img', 0) - for key in results.get('img_fields', ['img']): - if self.pad_to_square: - max_size = max(results[key].shape[:2]) - self.size = (max_size, max_size) - if self.size is not None: - padded_img = mmcv.impad( - results[key], shape=self.size, pad_val=pad_val) - elif self.size_divisor is not None: - padded_img = mmcv.impad_to_multiple( - results[key], self.size_divisor, pad_val=pad_val) - results[key] = padded_img - results['pad_shape'] = padded_img.shape - results['pad_fixed_size'] = self.size - results['pad_size_divisor'] = self.size_divisor - - def _pad_masks(self, results): - """Pad masks according to ``results['pad_shape']``.""" - pad_shape = results['pad_shape'][:2] - pad_val = self.pad_val.get('masks', 0) - for key in results.get('mask_fields', []): - results[key] = results[key].pad(pad_shape, pad_val=pad_val) - - def _pad_seg(self, results): - """Pad semantic segmentation map according to - ``results['pad_shape']``.""" - pad_val = self.pad_val.get('seg', 255) - for key in results.get('seg_fields', []): - results[key] = mmcv.impad( - results[key], shape=results['pad_shape'][:2], pad_val=pad_val) - - def __call__(self, results): - """Call function to pad images, masks, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Updated result dict. - """ - self._pad_img(results) - self._pad_masks(results) - self._pad_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(size={self.size}, ' - repr_str += f'size_divisor={self.size_divisor}, ' - repr_str += f'pad_to_square={self.pad_to_square}, ' - repr_str += f'pad_val={self.pad_val})' - return repr_str - - -@PIPELINES.register_module() -class Normalize: - """Normalize the image. - - Added key is "img_norm_cfg". - - Args: - mean (sequence): Mean values of 3 channels. - std (sequence): Std values of 3 channels. - to_rgb (bool): Whether to convert the image from BGR to RGB, - default is true. - """ - - def __init__(self, mean, std, to_rgb=True): - self.mean = np.array(mean, dtype=np.float32) - self.std = np.array(std, dtype=np.float32) - self.to_rgb = to_rgb - - def __call__(self, results): - """Call function to normalize images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Normalized results, 'img_norm_cfg' key is added into - result dict. - """ - for key in results.get('img_fields', ['img']): - results[key] = mmcv.imnormalize(results[key], self.mean, self.std, - self.to_rgb) - results['img_norm_cfg'] = dict( - mean=self.mean, std=self.std, to_rgb=self.to_rgb) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(mean={self.mean}, std={self.std}, to_rgb={self.to_rgb})' - return repr_str - - -@PIPELINES.register_module() -class RandomCrop: - """Random crop the image & bboxes & masks. - - The absolute `crop_size` is sampled based on `crop_type` and `image_size`, - then the cropped results are generated. - - Args: - crop_size (tuple): The relative ratio or absolute pixels of - height and width. - crop_type (str, optional): one of "relative_range", "relative", - "absolute", "absolute_range". "relative" randomly crops - (h * crop_size[0], w * crop_size[1]) part from an input of size - (h, w). "relative_range" uniformly samples relative crop size from - range [crop_size[0], 1] and [crop_size[1], 1] for height and width - respectively. "absolute" crops from an input with absolute size - (crop_size[0], crop_size[1]). "absolute_range" uniformly samples - crop_h in range [crop_size[0], min(h, crop_size[1])] and crop_w - in range [crop_size[0], min(w, crop_size[1])]. Default "absolute". - allow_negative_crop (bool, optional): Whether to allow a crop that does - not contain any bbox area. Default False. - recompute_bbox (bool, optional): Whether to re-compute the boxes based - on cropped instance masks. Default False. - bbox_clip_border (bool, optional): Whether clip the objects outside - the border of the image. Defaults to True. - - Note: - - If the image is smaller than the absolute crop size, return the - original image. - - The keys for bboxes, labels and masks must be aligned. That is, - `gt_bboxes` corresponds to `gt_labels` and `gt_masks`, and - `gt_bboxes_ignore` corresponds to `gt_labels_ignore` and - `gt_masks_ignore`. - - If the crop does not contain any gt-bbox region and - `allow_negative_crop` is set to False, skip this image. - """ - - def __init__(self, - crop_size, - crop_type='absolute', - allow_negative_crop=False, - recompute_bbox=False, - bbox_clip_border=True): - if crop_type not in [ - 'relative_range', 'relative', 'absolute', 'absolute_range' - ]: - raise ValueError(f'Invalid crop_type {crop_type}.') - if crop_type in ['absolute', 'absolute_range']: - assert crop_size[0] > 0 and crop_size[1] > 0 - assert isinstance(crop_size[0], int) and isinstance( - crop_size[1], int) - else: - assert 0 < crop_size[0] <= 1 and 0 < crop_size[1] <= 1 - self.crop_size = crop_size - self.crop_type = crop_type - self.allow_negative_crop = allow_negative_crop - self.bbox_clip_border = bbox_clip_border - self.recompute_bbox = recompute_bbox - # The key correspondence from bboxes to labels and masks. - self.bbox2label = { - 'gt_bboxes': 'gt_labels', - 'gt_bboxes_ignore': 'gt_labels_ignore' - } - self.bbox2mask = { - 'gt_bboxes': 'gt_masks', - 'gt_bboxes_ignore': 'gt_masks_ignore' - } - - def _crop_data(self, results, crop_size, allow_negative_crop): - """Function to randomly crop images, bounding boxes, masks, semantic - segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - crop_size (tuple): Expected absolute size after cropping, (h, w). - allow_negative_crop (bool): Whether to allow a crop that does not - contain any bbox area. Default to False. - - Returns: - dict: Randomly cropped results, 'img_shape' key in result dict is - updated according to crop size. - """ - assert crop_size[0] > 0 and crop_size[1] > 0 - for key in results.get('img_fields', ['img']): - img = results[key] - margin_h = max(img.shape[0] - crop_size[0], 0) - margin_w = max(img.shape[1] - crop_size[1], 0) - offset_h = np.random.randint(0, margin_h + 1) - offset_w = np.random.randint(0, margin_w + 1) - crop_y1, crop_y2 = offset_h, offset_h + crop_size[0] - crop_x1, crop_x2 = offset_w, offset_w + crop_size[1] - - # crop the image - img = img[crop_y1:crop_y2, crop_x1:crop_x2, ...] - img_shape = img.shape - results[key] = img - results['img_shape'] = img_shape - - # crop bboxes accordingly and clip to the image boundary - for key in results.get('bbox_fields', []): - # e.g. gt_bboxes and gt_bboxes_ignore - bbox_offset = np.array([offset_w, offset_h, offset_w, offset_h], - dtype=np.float32) - bboxes = results[key] - bbox_offset - if self.bbox_clip_border: - bboxes[:, 0::2] = np.clip(bboxes[:, 0::2], 0, img_shape[1]) - bboxes[:, 1::2] = np.clip(bboxes[:, 1::2], 0, img_shape[0]) - valid_inds = (bboxes[:, 2] > bboxes[:, 0]) & ( - bboxes[:, 3] > bboxes[:, 1]) - # If the crop does not contain any gt-bbox area and - # allow_negative_crop is False, skip this image. - if (key == 'gt_bboxes' and not valid_inds.any() - and not allow_negative_crop): - return None - results[key] = bboxes[valid_inds, :] - # label fields. e.g. gt_labels and gt_labels_ignore - label_key = self.bbox2label.get(key) - if label_key in results: - results[label_key] = results[label_key][valid_inds] - - # mask fields, e.g. gt_masks and gt_masks_ignore - mask_key = self.bbox2mask.get(key) - if mask_key in results: - results[mask_key] = results[mask_key][ - valid_inds.nonzero()[0]].crop( - np.asarray([crop_x1, crop_y1, crop_x2, crop_y2])) - if self.recompute_bbox: - results[key] = results[mask_key].get_bboxes() - - # crop semantic seg - for key in results.get('seg_fields', []): - results[key] = results[key][crop_y1:crop_y2, crop_x1:crop_x2] - - return results - - def _get_crop_size(self, image_size): - """Randomly generates the absolute crop size based on `crop_type` and - `image_size`. - - Args: - image_size (tuple): (h, w). - - Returns: - crop_size (tuple): (crop_h, crop_w) in absolute pixels. - """ - h, w = image_size - if self.crop_type == 'absolute': - return (min(self.crop_size[0], h), min(self.crop_size[1], w)) - elif self.crop_type == 'absolute_range': - assert self.crop_size[0] <= self.crop_size[1] - crop_h = np.random.randint( - min(h, self.crop_size[0]), - min(h, self.crop_size[1]) + 1) - crop_w = np.random.randint( - min(w, self.crop_size[0]), - min(w, self.crop_size[1]) + 1) - return crop_h, crop_w - elif self.crop_type == 'relative': - crop_h, crop_w = self.crop_size - return int(h * crop_h + 0.5), int(w * crop_w + 0.5) - elif self.crop_type == 'relative_range': - crop_size = np.asarray(self.crop_size, dtype=np.float32) - crop_h, crop_w = crop_size + np.random.rand(2) * (1 - crop_size) - return int(h * crop_h + 0.5), int(w * crop_w + 0.5) - - def __call__(self, results): - """Call function to randomly crop images, bounding boxes, masks, - semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Randomly cropped results, 'img_shape' key in result dict is - updated according to crop size. - """ - image_size = results['img'].shape[:2] - crop_size = self._get_crop_size(image_size) - results = self._crop_data(results, crop_size, self.allow_negative_crop) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(crop_size={self.crop_size}, ' - repr_str += f'crop_type={self.crop_type}, ' - repr_str += f'allow_negative_crop={self.allow_negative_crop}, ' - repr_str += f'bbox_clip_border={self.bbox_clip_border})' - return repr_str - - -@PIPELINES.register_module() -class SegRescale: - """Rescale semantic segmentation maps. - - Args: - scale_factor (float): The scale factor of the final output. - backend (str): Image rescale backend, choices are 'cv2' and 'pillow'. - These two backends generates slightly different results. Defaults - to 'cv2'. - """ - - def __init__(self, scale_factor=1, backend='cv2'): - self.scale_factor = scale_factor - self.backend = backend - - def __call__(self, results): - """Call function to scale the semantic segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with semantic segmentation map scaled. - """ - - for key in results.get('seg_fields', []): - if self.scale_factor != 1: - results[key] = mmcv.imrescale( - results[key], - self.scale_factor, - interpolation='nearest', - backend=self.backend) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(scale_factor={self.scale_factor})' - - -@PIPELINES.register_module() -class PhotoMetricDistortion: - """Apply photometric distortion to image sequentially, every transformation - is applied with a probability of 0.5. The position of random contrast is in - second or second to last. - - 1. random brightness - 2. random contrast (mode 0) - 3. convert color from BGR to HSV - 4. random saturation - 5. random hue - 6. convert color from HSV to BGR - 7. random contrast (mode 1) - 8. randomly swap channels - - Args: - brightness_delta (int): delta of brightness. - contrast_range (tuple): range of contrast. - saturation_range (tuple): range of saturation. - hue_delta (int): delta of hue. - """ - - def __init__(self, - brightness_delta=32, - contrast_range=(0.5, 1.5), - saturation_range=(0.5, 1.5), - hue_delta=18): - self.brightness_delta = brightness_delta - self.contrast_lower, self.contrast_upper = contrast_range - self.saturation_lower, self.saturation_upper = saturation_range - self.hue_delta = hue_delta - - def __call__(self, results): - """Call function to perform photometric distortion on images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images distorted. - """ - - if 'img_fields' in results: - assert results['img_fields'] == ['img'], \ - 'Only single img_fields is allowed' - img = results['img'] - img = img.astype(np.float32) - # random brightness - if random.randint(2): - delta = random.uniform(-self.brightness_delta, - self.brightness_delta) - img += delta - - # mode == 0 --> do random contrast first - # mode == 1 --> do random contrast last - mode = random.randint(2) - if mode == 1: - if random.randint(2): - alpha = random.uniform(self.contrast_lower, - self.contrast_upper) - img *= alpha - - # convert color from BGR to HSV - img = mmcv.bgr2hsv(img) - - # random saturation - if random.randint(2): - img[..., 1] *= random.uniform(self.saturation_lower, - self.saturation_upper) - - # random hue - if random.randint(2): - img[..., 0] += random.uniform(-self.hue_delta, self.hue_delta) - img[..., 0][img[..., 0] > 360] -= 360 - img[..., 0][img[..., 0] < 0] += 360 - - # convert color from HSV to BGR - img = mmcv.hsv2bgr(img) - - # random contrast - if mode == 0: - if random.randint(2): - alpha = random.uniform(self.contrast_lower, - self.contrast_upper) - img *= alpha - - # randomly swap channels - if random.randint(2): - img = img[..., random.permutation(3)] - - results['img'] = img - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(\nbrightness_delta={self.brightness_delta},\n' - repr_str += 'contrast_range=' - repr_str += f'{(self.contrast_lower, self.contrast_upper)},\n' - repr_str += 'saturation_range=' - repr_str += f'{(self.saturation_lower, self.saturation_upper)},\n' - repr_str += f'hue_delta={self.hue_delta})' - return repr_str - - -@PIPELINES.register_module() -class Expand: - """Random expand the image & bboxes. - - Randomly place the original image on a canvas of 'ratio' x original image - size filled with mean values. The ratio is in the range of ratio_range. - - Args: - mean (tuple): mean value of dataset. - to_rgb (bool): if need to convert the order of mean to align with RGB. - ratio_range (tuple): range of expand ratio. - prob (float): probability of applying this transformation - """ - - def __init__(self, - mean=(0, 0, 0), - to_rgb=True, - ratio_range=(1, 4), - seg_ignore_label=None, - prob=0.5): - self.to_rgb = to_rgb - self.ratio_range = ratio_range - if to_rgb: - self.mean = mean[::-1] - else: - self.mean = mean - self.min_ratio, self.max_ratio = ratio_range - self.seg_ignore_label = seg_ignore_label - self.prob = prob - - def __call__(self, results): - """Call function to expand images, bounding boxes. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images, bounding boxes expanded - """ - - if random.uniform(0, 1) > self.prob: - return results - - if 'img_fields' in results: - assert results['img_fields'] == ['img'], \ - 'Only single img_fields is allowed' - img = results['img'] - - h, w, c = img.shape - ratio = random.uniform(self.min_ratio, self.max_ratio) - # speedup expand when meets large image - if np.all(self.mean == self.mean[0]): - expand_img = np.empty((int(h * ratio), int(w * ratio), c), - img.dtype) - expand_img.fill(self.mean[0]) - else: - expand_img = np.full((int(h * ratio), int(w * ratio), c), - self.mean, - dtype=img.dtype) - left = int(random.uniform(0, w * ratio - w)) - top = int(random.uniform(0, h * ratio - h)) - expand_img[top:top + h, left:left + w] = img - - results['img'] = expand_img - # expand bboxes - for key in results.get('bbox_fields', []): - results[key] = results[key] + np.tile( - (left, top), 2).astype(results[key].dtype) - - # expand masks - for key in results.get('mask_fields', []): - results[key] = results[key].expand( - int(h * ratio), int(w * ratio), top, left) - - # expand segs - for key in results.get('seg_fields', []): - gt_seg = results[key] - expand_gt_seg = np.full((int(h * ratio), int(w * ratio)), - self.seg_ignore_label, - dtype=gt_seg.dtype) - expand_gt_seg[top:top + h, left:left + w] = gt_seg - results[key] = expand_gt_seg - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(mean={self.mean}, to_rgb={self.to_rgb}, ' - repr_str += f'ratio_range={self.ratio_range}, ' - repr_str += f'seg_ignore_label={self.seg_ignore_label})' - return repr_str - - -@PIPELINES.register_module() -class MinIoURandomCrop: - """Random crop the image & bboxes, the cropped patches have minimum IoU - requirement with original image & bboxes, the IoU threshold is randomly - selected from min_ious. - - Args: - min_ious (tuple): minimum IoU threshold for all intersections with - bounding boxes - min_crop_size (float): minimum crop's size (i.e. h,w := a*h, a*w, - where a >= min_crop_size). - bbox_clip_border (bool, optional): Whether clip the objects outside - the border of the image. Defaults to True. - - Note: - The keys for bboxes, labels and masks should be paired. That is, \ - `gt_bboxes` corresponds to `gt_labels` and `gt_masks`, and \ - `gt_bboxes_ignore` to `gt_labels_ignore` and `gt_masks_ignore`. - """ - - def __init__(self, - min_ious=(0.1, 0.3, 0.5, 0.7, 0.9), - min_crop_size=0.3, - bbox_clip_border=True): - # 1: return ori img - self.min_ious = min_ious - self.sample_mode = (1, *min_ious, 0) - self.min_crop_size = min_crop_size - self.bbox_clip_border = bbox_clip_border - self.bbox2label = { - 'gt_bboxes': 'gt_labels', - 'gt_bboxes_ignore': 'gt_labels_ignore' - } - self.bbox2mask = { - 'gt_bboxes': 'gt_masks', - 'gt_bboxes_ignore': 'gt_masks_ignore' - } - - def __call__(self, results): - """Call function to crop images and bounding boxes with minimum IoU - constraint. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images and bounding boxes cropped, \ - 'img_shape' key is updated. - """ - - if 'img_fields' in results: - assert results['img_fields'] == ['img'], \ - 'Only single img_fields is allowed' - img = results['img'] - assert 'bbox_fields' in results - boxes = [results[key] for key in results['bbox_fields']] - boxes = np.concatenate(boxes, 0) - h, w, c = img.shape - while True: - mode = random.choice(self.sample_mode) - self.mode = mode - if mode == 1: - return results - - min_iou = mode - for i in range(50): - new_w = random.uniform(self.min_crop_size * w, w) - new_h = random.uniform(self.min_crop_size * h, h) - - # h / w in [0.5, 2] - if new_h / new_w < 0.5 or new_h / new_w > 2: - continue - - left = random.uniform(w - new_w) - top = random.uniform(h - new_h) - - patch = np.array( - (int(left), int(top), int(left + new_w), int(top + new_h))) - # Line or point crop is not allowed - if patch[2] == patch[0] or patch[3] == patch[1]: - continue - overlaps = bbox_overlaps( - patch.reshape(-1, 4), boxes.reshape(-1, 4)).reshape(-1) - if len(overlaps) > 0 and overlaps.min() < min_iou: - continue - - # center of boxes should inside the crop img - # only adjust boxes and instance masks when the gt is not empty - if len(overlaps) > 0: - # adjust boxes - def is_center_of_bboxes_in_patch(boxes, patch): - center = (boxes[:, :2] + boxes[:, 2:]) / 2 - mask = ((center[:, 0] > patch[0]) * - (center[:, 1] > patch[1]) * - (center[:, 0] < patch[2]) * - (center[:, 1] < patch[3])) - return mask - - mask = is_center_of_bboxes_in_patch(boxes, patch) - if not mask.any(): - continue - for key in results.get('bbox_fields', []): - boxes = results[key].copy() - mask = is_center_of_bboxes_in_patch(boxes, patch) - boxes = boxes[mask] - if self.bbox_clip_border: - boxes[:, 2:] = boxes[:, 2:].clip(max=patch[2:]) - boxes[:, :2] = boxes[:, :2].clip(min=patch[:2]) - boxes -= np.tile(patch[:2], 2) - - results[key] = boxes - # labels - label_key = self.bbox2label.get(key) - if label_key in results: - results[label_key] = results[label_key][mask] - - # mask fields - mask_key = self.bbox2mask.get(key) - if mask_key in results: - results[mask_key] = results[mask_key][ - mask.nonzero()[0]].crop(patch) - # adjust the img no matter whether the gt is empty before crop - img = img[patch[1]:patch[3], patch[0]:patch[2]] - results['img'] = img - results['img_shape'] = img.shape - - # seg fields - for key in results.get('seg_fields', []): - results[key] = results[key][patch[1]:patch[3], - patch[0]:patch[2]] - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(min_ious={self.min_ious}, ' - repr_str += f'min_crop_size={self.min_crop_size}, ' - repr_str += f'bbox_clip_border={self.bbox_clip_border})' - return repr_str - - -@PIPELINES.register_module() -class Corrupt: - """Corruption augmentation. - - Corruption transforms implemented based on - `imagecorruptions `_. - - Args: - corruption (str): Corruption name. - severity (int, optional): The severity of corruption. Default: 1. - """ - - def __init__(self, corruption, severity=1): - self.corruption = corruption - self.severity = severity - - def __call__(self, results): - """Call function to corrupt image. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images corrupted. - """ - - if corrupt is None: - raise RuntimeError('imagecorruptions is not installed') - if 'img_fields' in results: - assert results['img_fields'] == ['img'], \ - 'Only single img_fields is allowed' - results['img'] = corrupt( - results['img'].astype(np.uint8), - corruption_name=self.corruption, - severity=self.severity) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(corruption={self.corruption}, ' - repr_str += f'severity={self.severity})' - return repr_str - - -@PIPELINES.register_module() -class Albu: - """Albumentation augmentation. - - Adds custom transformations from Albumentations library. - Please, visit `https://albumentations.readthedocs.io` - to get more information. - - An example of ``transforms`` is as followed: - - .. code-block:: - - [ - dict( - type='ShiftScaleRotate', - shift_limit=0.0625, - scale_limit=0.0, - rotate_limit=0, - interpolation=1, - p=0.5), - dict( - type='RandomBrightnessContrast', - brightness_limit=[0.1, 0.3], - contrast_limit=[0.1, 0.3], - p=0.2), - dict(type='ChannelShuffle', p=0.1), - dict( - type='OneOf', - transforms=[ - dict(type='Blur', blur_limit=3, p=1.0), - dict(type='MedianBlur', blur_limit=3, p=1.0) - ], - p=0.1), - ] - - Args: - transforms (list[dict]): A list of albu transformations - bbox_params (dict): Bbox_params for albumentation `Compose` - keymap (dict): Contains {'input key':'albumentation-style key'} - skip_img_without_anno (bool): Whether to skip the image if no ann left - after aug - """ - - def __init__(self, - transforms, - bbox_params=None, - keymap=None, - update_pad_shape=False, - skip_img_without_anno=False): - if Compose is None: - raise RuntimeError('albumentations is not installed') - - # Args will be modified later, copying it will be safer - transforms = copy.deepcopy(transforms) - if bbox_params is not None: - bbox_params = copy.deepcopy(bbox_params) - if keymap is not None: - keymap = copy.deepcopy(keymap) - self.transforms = transforms - self.filter_lost_elements = False - self.update_pad_shape = update_pad_shape - self.skip_img_without_anno = skip_img_without_anno - - # A simple workaround to remove masks without boxes - if (isinstance(bbox_params, dict) and 'label_fields' in bbox_params - and 'filter_lost_elements' in bbox_params): - self.filter_lost_elements = True - self.origin_label_fields = bbox_params['label_fields'] - bbox_params['label_fields'] = ['idx_mapper'] - del bbox_params['filter_lost_elements'] - - self.bbox_params = ( - self.albu_builder(bbox_params) if bbox_params else None) - self.aug = Compose([self.albu_builder(t) for t in self.transforms], - bbox_params=self.bbox_params) - - if not keymap: - self.keymap_to_albu = { - 'img': 'image', - 'gt_masks': 'masks', - 'gt_bboxes': 'bboxes' - } - else: - self.keymap_to_albu = keymap - self.keymap_back = {v: k for k, v in self.keymap_to_albu.items()} - - def albu_builder(self, cfg): - """Import a module from albumentations. - - It inherits some of :func:`build_from_cfg` logic. - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - - Returns: - obj: The constructed object. - """ - - assert isinstance(cfg, dict) and 'type' in cfg - args = cfg.copy() - - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if albumentations is None: - raise RuntimeError('albumentations is not installed') - obj_cls = getattr(albumentations, obj_type) - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - - if 'transforms' in args: - args['transforms'] = [ - self.albu_builder(transform) - for transform in args['transforms'] - ] - - return obj_cls(**args) - - @staticmethod - def mapper(d, keymap): - """Dictionary mapper. Renames keys according to keymap provided. - - Args: - d (dict): old dict - keymap (dict): {'old_key':'new_key'} - Returns: - dict: new dict. - """ - - updated_dict = {} - for k, v in zip(d.keys(), d.values()): - new_k = keymap.get(k, k) - updated_dict[new_k] = d[k] - return updated_dict - - def __call__(self, results): - # dict to albumentations format - results = self.mapper(results, self.keymap_to_albu) - # TODO: add bbox_fields - if 'bboxes' in results: - # to list of boxes - if isinstance(results['bboxes'], np.ndarray): - results['bboxes'] = [x for x in results['bboxes']] - # add pseudo-field for filtration - if self.filter_lost_elements: - results['idx_mapper'] = np.arange(len(results['bboxes'])) - - # TODO: Support mask structure in albu - if 'masks' in results: - if isinstance(results['masks'], PolygonMasks): - raise NotImplementedError( - 'Albu only supports BitMap masks now') - ori_masks = results['masks'] - if albumentations.__version__ < '0.5': - results['masks'] = results['masks'].masks - else: - results['masks'] = [mask for mask in results['masks'].masks] - - results = self.aug(**results) - - if 'bboxes' in results: - if isinstance(results['bboxes'], list): - results['bboxes'] = np.array( - results['bboxes'], dtype=np.float32) - results['bboxes'] = results['bboxes'].reshape(-1, 4) - - # filter label_fields - if self.filter_lost_elements: - - for label in self.origin_label_fields: - results[label] = np.array( - [results[label][i] for i in results['idx_mapper']]) - if 'masks' in results: - results['masks'] = np.array( - [results['masks'][i] for i in results['idx_mapper']]) - results['masks'] = ori_masks.__class__( - results['masks'], results['image'].shape[0], - results['image'].shape[1]) - - if (not len(results['idx_mapper']) - and self.skip_img_without_anno): - return None - - if 'gt_labels' in results: - if isinstance(results['gt_labels'], list): - results['gt_labels'] = np.array(results['gt_labels']) - results['gt_labels'] = results['gt_labels'].astype(np.int64) - - # back to the original format - results = self.mapper(results, self.keymap_back) - - # update final shape - if self.update_pad_shape: - results['pad_shape'] = results['img'].shape - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ + f'(transforms={self.transforms})' - return repr_str - - -@PIPELINES.register_module() -class RandomCenterCropPad: - """Random center crop and random around padding for CornerNet. - - This operation generates randomly cropped image from the original image and - pads it simultaneously. Different from :class:`RandomCrop`, the output - shape may not equal to ``crop_size`` strictly. We choose a random value - from ``ratios`` and the output shape could be larger or smaller than - ``crop_size``. The padding operation is also different from :class:`Pad`, - here we use around padding instead of right-bottom padding. - - The relation between output image (padding image) and original image: - - .. code:: text - - output image - - +----------------------------+ - | padded area | - +------|----------------------------|----------+ - | | cropped area | | - | | +---------------+ | | - | | | . center | | | original image - | | | range | | | - | | +---------------+ | | - +------|----------------------------|----------+ - | padded area | - +----------------------------+ - - There are 5 main areas in the figure: - - - output image: output image of this operation, also called padding - image in following instruction. - - original image: input image of this operation. - - padded area: non-intersect area of output image and original image. - - cropped area: the overlap of output image and original image. - - center range: a smaller area where random center chosen from. - center range is computed by ``border`` and original image's shape - to avoid our random center is too close to original image's border. - - Also this operation act differently in train and test mode, the summary - pipeline is listed below. - - Train pipeline: - - 1. Choose a ``random_ratio`` from ``ratios``, the shape of padding image - will be ``random_ratio * crop_size``. - 2. Choose a ``random_center`` in center range. - 3. Generate padding image with center matches the ``random_center``. - 4. Initialize the padding image with pixel value equals to ``mean``. - 5. Copy the cropped area to padding image. - 6. Refine annotations. - - Test pipeline: - - 1. Compute output shape according to ``test_pad_mode``. - 2. Generate padding image with center matches the original image - center. - 3. Initialize the padding image with pixel value equals to ``mean``. - 4. Copy the ``cropped area`` to padding image. - - Args: - crop_size (tuple | None): expected size after crop, final size will - computed according to ratio. Requires (h, w) in train mode, and - None in test mode. - ratios (tuple): random select a ratio from tuple and crop image to - (crop_size[0] * ratio) * (crop_size[1] * ratio). - Only available in train mode. - border (int): max distance from center select area to image border. - Only available in train mode. - mean (sequence): Mean values of 3 channels. - std (sequence): Std values of 3 channels. - to_rgb (bool): Whether to convert the image from BGR to RGB. - test_mode (bool): whether involve random variables in transform. - In train mode, crop_size is fixed, center coords and ratio is - random selected from predefined lists. In test mode, crop_size - is image's original shape, center coords and ratio is fixed. - test_pad_mode (tuple): padding method and padding shape value, only - available in test mode. Default is using 'logical_or' with - 127 as padding shape value. - - - 'logical_or': final_shape = input_shape | padding_shape_value - - 'size_divisor': final_shape = int( - ceil(input_shape / padding_shape_value) * padding_shape_value) - test_pad_add_pix (int): Extra padding pixel in test mode. Default 0. - bbox_clip_border (bool, optional): Whether clip the objects outside - the border of the image. Defaults to True. - """ - - def __init__(self, - crop_size=None, - ratios=(0.9, 1.0, 1.1), - border=128, - mean=None, - std=None, - to_rgb=None, - test_mode=False, - test_pad_mode=('logical_or', 127), - test_pad_add_pix=0, - bbox_clip_border=True): - if test_mode: - assert crop_size is None, 'crop_size must be None in test mode' - assert ratios is None, 'ratios must be None in test mode' - assert border is None, 'border must be None in test mode' - assert isinstance(test_pad_mode, (list, tuple)) - assert test_pad_mode[0] in ['logical_or', 'size_divisor'] - else: - assert isinstance(crop_size, (list, tuple)) - assert crop_size[0] > 0 and crop_size[1] > 0, ( - 'crop_size must > 0 in train mode') - assert isinstance(ratios, (list, tuple)) - assert test_pad_mode is None, ( - 'test_pad_mode must be None in train mode') - - self.crop_size = crop_size - self.ratios = ratios - self.border = border - # We do not set default value to mean, std and to_rgb because these - # hyper-parameters are easy to forget but could affect the performance. - # Please use the same setting as Normalize for performance assurance. - assert mean is not None and std is not None and to_rgb is not None - self.to_rgb = to_rgb - self.input_mean = mean - self.input_std = std - if to_rgb: - self.mean = mean[::-1] - self.std = std[::-1] - else: - self.mean = mean - self.std = std - self.test_mode = test_mode - self.test_pad_mode = test_pad_mode - self.test_pad_add_pix = test_pad_add_pix - self.bbox_clip_border = bbox_clip_border - - def _get_border(self, border, size): - """Get final border for the target size. - - This function generates a ``final_border`` according to image's shape. - The area between ``final_border`` and ``size - final_border`` is the - ``center range``. We randomly choose center from the ``center range`` - to avoid our random center is too close to original image's border. - Also ``center range`` should be larger than 0. - - Args: - border (int): The initial border, default is 128. - size (int): The width or height of original image. - Returns: - int: The final border. - """ - k = 2 * border / size - i = pow(2, np.ceil(np.log2(np.ceil(k))) + (k == int(k))) - return border // i - - def _filter_boxes(self, patch, boxes): - """Check whether the center of each box is in the patch. - - Args: - patch (list[int]): The cropped area, [left, top, right, bottom]. - boxes (numpy array, (N x 4)): Ground truth boxes. - - Returns: - mask (numpy array, (N,)): Each box is inside or outside the patch. - """ - center = (boxes[:, :2] + boxes[:, 2:]) / 2 - mask = (center[:, 0] > patch[0]) * (center[:, 1] > patch[1]) * ( - center[:, 0] < patch[2]) * ( - center[:, 1] < patch[3]) - return mask - - def _crop_image_and_paste(self, image, center, size): - """Crop image with a given center and size, then paste the cropped - image to a blank image with two centers align. - - This function is equivalent to generating a blank image with ``size`` - as its shape. Then cover it on the original image with two centers ( - the center of blank image and the random center of original image) - aligned. The overlap area is paste from the original image and the - outside area is filled with ``mean pixel``. - - Args: - image (np array, H x W x C): Original image. - center (list[int]): Target crop center coord. - size (list[int]): Target crop size. [target_h, target_w] - - Returns: - cropped_img (np array, target_h x target_w x C): Cropped image. - border (np array, 4): The distance of four border of - ``cropped_img`` to the original image area, [top, bottom, - left, right] - patch (list[int]): The cropped area, [left, top, right, bottom]. - """ - center_y, center_x = center - target_h, target_w = size - img_h, img_w, img_c = image.shape - - x0 = max(0, center_x - target_w // 2) - x1 = min(center_x + target_w // 2, img_w) - y0 = max(0, center_y - target_h // 2) - y1 = min(center_y + target_h // 2, img_h) - patch = np.array((int(x0), int(y0), int(x1), int(y1))) - - left, right = center_x - x0, x1 - center_x - top, bottom = center_y - y0, y1 - center_y - - cropped_center_y, cropped_center_x = target_h // 2, target_w // 2 - cropped_img = np.zeros((target_h, target_w, img_c), dtype=image.dtype) - for i in range(img_c): - cropped_img[:, :, i] += self.mean[i] - y_slice = slice(cropped_center_y - top, cropped_center_y + bottom) - x_slice = slice(cropped_center_x - left, cropped_center_x + right) - cropped_img[y_slice, x_slice, :] = image[y0:y1, x0:x1, :] - - border = np.array([ - cropped_center_y - top, cropped_center_y + bottom, - cropped_center_x - left, cropped_center_x + right - ], - dtype=np.float32) - - return cropped_img, border, patch - - def _train_aug(self, results): - """Random crop and around padding the original image. - - Args: - results (dict): Image infomations in the augment pipeline. - - Returns: - results (dict): The updated dict. - """ - img = results['img'] - h, w, c = img.shape - boxes = results['gt_bboxes'] - while True: - scale = random.choice(self.ratios) - new_h = int(self.crop_size[0] * scale) - new_w = int(self.crop_size[1] * scale) - h_border = self._get_border(self.border, h) - w_border = self._get_border(self.border, w) - - for i in range(50): - center_x = random.randint(low=w_border, high=w - w_border) - center_y = random.randint(low=h_border, high=h - h_border) - - cropped_img, border, patch = self._crop_image_and_paste( - img, [center_y, center_x], [new_h, new_w]) - - mask = self._filter_boxes(patch, boxes) - # if image do not have valid bbox, any crop patch is valid. - if not mask.any() and len(boxes) > 0: - continue - - results['img'] = cropped_img - results['img_shape'] = cropped_img.shape - results['pad_shape'] = cropped_img.shape - - x0, y0, x1, y1 = patch - - left_w, top_h = center_x - x0, center_y - y0 - cropped_center_x, cropped_center_y = new_w // 2, new_h // 2 - - # crop bboxes accordingly and clip to the image boundary - for key in results.get('bbox_fields', []): - mask = self._filter_boxes(patch, results[key]) - bboxes = results[key][mask] - bboxes[:, 0:4:2] += cropped_center_x - left_w - x0 - bboxes[:, 1:4:2] += cropped_center_y - top_h - y0 - if self.bbox_clip_border: - bboxes[:, 0:4:2] = np.clip(bboxes[:, 0:4:2], 0, new_w) - bboxes[:, 1:4:2] = np.clip(bboxes[:, 1:4:2], 0, new_h) - keep = (bboxes[:, 2] > bboxes[:, 0]) & ( - bboxes[:, 3] > bboxes[:, 1]) - bboxes = bboxes[keep] - results[key] = bboxes - if key in ['gt_bboxes']: - if 'gt_labels' in results: - labels = results['gt_labels'][mask] - labels = labels[keep] - results['gt_labels'] = labels - if 'gt_masks' in results: - raise NotImplementedError( - 'RandomCenterCropPad only supports bbox.') - - # crop semantic seg - for key in results.get('seg_fields', []): - raise NotImplementedError( - 'RandomCenterCropPad only supports bbox.') - return results - - def _test_aug(self, results): - """Around padding the original image without cropping. - - The padding mode and value are from ``test_pad_mode``. - - Args: - results (dict): Image infomations in the augment pipeline. - - Returns: - results (dict): The updated dict. - """ - img = results['img'] - h, w, c = img.shape - results['img_shape'] = img.shape - if self.test_pad_mode[0] in ['logical_or']: - # self.test_pad_add_pix is only used for centernet - target_h = (h | self.test_pad_mode[1]) + self.test_pad_add_pix - target_w = (w | self.test_pad_mode[1]) + self.test_pad_add_pix - elif self.test_pad_mode[0] in ['size_divisor']: - divisor = self.test_pad_mode[1] - target_h = int(np.ceil(h / divisor)) * divisor - target_w = int(np.ceil(w / divisor)) * divisor - else: - raise NotImplementedError( - 'RandomCenterCropPad only support two testing pad mode:' - 'logical-or and size_divisor.') - - cropped_img, border, _ = self._crop_image_and_paste( - img, [h // 2, w // 2], [target_h, target_w]) - results['img'] = cropped_img - results['pad_shape'] = cropped_img.shape - results['border'] = border - return results - - def __call__(self, results): - img = results['img'] - assert img.dtype == np.float32, ( - 'RandomCenterCropPad needs the input image of dtype np.float32,' - ' please set "to_float32=True" in "LoadImageFromFile" pipeline') - h, w, c = img.shape - assert c == len(self.mean) - if self.test_mode: - return self._test_aug(results) - else: - return self._train_aug(results) - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(crop_size={self.crop_size}, ' - repr_str += f'ratios={self.ratios}, ' - repr_str += f'border={self.border}, ' - repr_str += f'mean={self.input_mean}, ' - repr_str += f'std={self.input_std}, ' - repr_str += f'to_rgb={self.to_rgb}, ' - repr_str += f'test_mode={self.test_mode}, ' - repr_str += f'test_pad_mode={self.test_pad_mode}, ' - repr_str += f'bbox_clip_border={self.bbox_clip_border})' - return repr_str - - -@PIPELINES.register_module() -class CutOut: - """CutOut operation. - - Randomly drop some regions of image used in - `Cutout `_. - - Args: - n_holes (int | tuple[int, int]): Number of regions to be dropped. - If it is given as a list, number of holes will be randomly - selected from the closed interval [`n_holes[0]`, `n_holes[1]`]. - cutout_shape (tuple[int, int] | list[tuple[int, int]]): The candidate - shape of dropped regions. It can be `tuple[int, int]` to use a - fixed cutout shape, or `list[tuple[int, int]]` to randomly choose - shape from the list. - cutout_ratio (tuple[float, float] | list[tuple[float, float]]): The - candidate ratio of dropped regions. It can be `tuple[float, float]` - to use a fixed ratio or `list[tuple[float, float]]` to randomly - choose ratio from the list. Please note that `cutout_shape` - and `cutout_ratio` cannot be both given at the same time. - fill_in (tuple[float, float, float] | tuple[int, int, int]): The value - of pixel to fill in the dropped regions. Default: (0, 0, 0). - """ - - def __init__(self, - n_holes, - cutout_shape=None, - cutout_ratio=None, - fill_in=(0, 0, 0)): - - assert (cutout_shape is None) ^ (cutout_ratio is None), \ - 'Either cutout_shape or cutout_ratio should be specified.' - assert (isinstance(cutout_shape, (list, tuple)) - or isinstance(cutout_ratio, (list, tuple))) - if isinstance(n_holes, tuple): - assert len(n_holes) == 2 and 0 <= n_holes[0] < n_holes[1] - else: - n_holes = (n_holes, n_holes) - self.n_holes = n_holes - self.fill_in = fill_in - self.with_ratio = cutout_ratio is not None - self.candidates = cutout_ratio if self.with_ratio else cutout_shape - if not isinstance(self.candidates, list): - self.candidates = [self.candidates] - - def __call__(self, results): - """Call function to drop some regions of image.""" - h, w, c = results['img'].shape - n_holes = np.random.randint(self.n_holes[0], self.n_holes[1] + 1) - for _ in range(n_holes): - x1 = np.random.randint(0, w) - y1 = np.random.randint(0, h) - index = np.random.randint(0, len(self.candidates)) - if not self.with_ratio: - cutout_w, cutout_h = self.candidates[index] - else: - cutout_w = int(self.candidates[index][0] * w) - cutout_h = int(self.candidates[index][1] * h) - - x2 = np.clip(x1 + cutout_w, 0, w) - y2 = np.clip(y1 + cutout_h, 0, h) - results['img'][y1:y2, x1:x2, :] = self.fill_in - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(n_holes={self.n_holes}, ' - repr_str += (f'cutout_ratio={self.candidates}, ' if self.with_ratio - else f'cutout_shape={self.candidates}, ') - repr_str += f'fill_in={self.fill_in})' - return repr_str - - -@PIPELINES.register_module() -class Mosaic: - """Mosaic augmentation. - - Given 4 images, mosaic transform combines them into - one output image. The output image is composed of the parts from each sub- - image. - - .. code:: text - - mosaic transform - center_x - +------------------------------+ - | pad | pad | - | +-----------+ | - | | | | - | | image1 |--------+ | - | | | | | - | | | image2 | | - center_y |----+-------------+-----------| - | | cropped | | - |pad | image3 | image4 | - | | | | - +----|-------------+-----------+ - | | - +-------------+ - - The mosaic transform steps are as follows: - - 1. Choose the mosaic center as the intersections of 4 images - 2. Get the left top image according to the index, and randomly - sample another 3 images from the custom dataset. - 3. Sub image will be cropped if image is larger than mosaic patch - - Args: - img_scale (Sequence[int]): Image size after mosaic pipeline of single - image. The shape order should be (height, width). - Default to (640, 640). - center_ratio_range (Sequence[float]): Center ratio range of mosaic - output. Default to (0.5, 1.5). - min_bbox_size (int | float): The minimum pixel for filtering - invalid bboxes after the mosaic pipeline. Default to 0. - bbox_clip_border (bool, optional): Whether to clip the objects outside - the border of the image. In some dataset like MOT17, the gt bboxes - are allowed to cross the border of images. Therefore, we don't - need to clip the gt bboxes in these cases. Defaults to True. - skip_filter (bool): Whether to skip filtering rules. If it - is True, the filter rule will not be applied, and the - `min_bbox_size` is invalid. Default to True. - pad_val (int): Pad value. Default to 114. - prob (float): Probability of applying this transformation. - Default to 1.0. - """ - - def __init__(self, - img_scale=(640, 640), - center_ratio_range=(0.5, 1.5), - min_bbox_size=0, - bbox_clip_border=True, - skip_filter=True, - pad_val=114, - prob=1.0): - assert isinstance(img_scale, tuple) - assert 0 <= prob <= 1.0, 'The probability should be in range [0,1]. '\ - f'got {prob}.' - - log_img_scale(img_scale, skip_square=True) - self.img_scale = img_scale - self.center_ratio_range = center_ratio_range - self.min_bbox_size = min_bbox_size - self.bbox_clip_border = bbox_clip_border - self.skip_filter = skip_filter - self.pad_val = pad_val - self.prob = prob - - def __call__(self, results): - """Call function to make a mosaic of image. - - Args: - results (dict): Result dict. - - Returns: - dict: Result dict with mosaic transformed. - """ - - if random.uniform(0, 1) > self.prob: - return results - - results = self._mosaic_transform(results) - return results - - def get_indexes(self, dataset): - """Call function to collect indexes. - - Args: - dataset (:obj:`MultiImageMixDataset`): The dataset. - - Returns: - list: indexes. - """ - - indexes = [random.randint(0, len(dataset)) for _ in range(3)] - return indexes - - def _mosaic_transform(self, results): - """Mosaic transform function. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - mosaic_labels = [] - mosaic_bboxes = [] - if len(results['img'].shape) == 3: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2), 3), - self.pad_val, - dtype=results['img'].dtype) - else: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.pad_val, - dtype=results['img'].dtype) - - # mosaic center x, y - center_x = int( - random.uniform(*self.center_ratio_range) * self.img_scale[1]) - center_y = int( - random.uniform(*self.center_ratio_range) * self.img_scale[0]) - center_position = (center_x, center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - results_patch = copy.deepcopy(results) - else: - results_patch = copy.deepcopy(results['mix_results'][i - 1]) - - img_i = results_patch['img'] - h_i, w_i = img_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - img_i = mmcv.imresize( - img_i, (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i))) - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, img_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_img[y1_p:y2_p, x1_p:x2_p] = img_i[y1_c:y2_c, x1_c:x2_c] - - # adjust coordinate - gt_bboxes_i = results_patch['gt_bboxes'] - gt_labels_i = results_patch['gt_labels'] - - if gt_bboxes_i.shape[0] > 0: - padw = x1_p - x1_c - padh = y1_p - y1_c - gt_bboxes_i[:, 0::2] = \ - scale_ratio_i * gt_bboxes_i[:, 0::2] + padw - gt_bboxes_i[:, 1::2] = \ - scale_ratio_i * gt_bboxes_i[:, 1::2] + padh - - mosaic_bboxes.append(gt_bboxes_i) - mosaic_labels.append(gt_labels_i) - - if len(mosaic_labels) > 0: - mosaic_bboxes = np.concatenate(mosaic_bboxes, 0) - mosaic_labels = np.concatenate(mosaic_labels, 0) - - if self.bbox_clip_border: - mosaic_bboxes[:, 0::2] = np.clip(mosaic_bboxes[:, 0::2], 0, - 2 * self.img_scale[1]) - mosaic_bboxes[:, 1::2] = np.clip(mosaic_bboxes[:, 1::2], 0, - 2 * self.img_scale[0]) - - if not self.skip_filter: - mosaic_bboxes, mosaic_labels = \ - self._filter_box_candidates(mosaic_bboxes, mosaic_labels) - - # remove outside bboxes - inside_inds = find_inside_bboxes(mosaic_bboxes, 2 * self.img_scale[0], - 2 * self.img_scale[1]) - mosaic_bboxes = mosaic_bboxes[inside_inds] - mosaic_labels = mosaic_labels[inside_inds] - - results['img'] = mosaic_img - results['img_shape'] = mosaic_img.shape - results['gt_bboxes'] = mosaic_bboxes - results['gt_labels'] = mosaic_labels - - return results - - def _mosaic_combine(self, loc, center_position_xy, img_shape_wh): - """Calculate global coordinate of mosaic image and local coordinate of - cropped sub-image. - - Args: - loc (str): Index for the sub-image, loc in ('top_left', - 'top_right', 'bottom_left', 'bottom_right'). - center_position_xy (Sequence[float]): Mixing center for 4 images, - (x, y). - img_shape_wh (Sequence[int]): Width and height of sub-image - - Returns: - tuple[tuple[float]]: Corresponding coordinate of pasting and - cropping - - paste_coord (tuple): paste corner coordinate in mosaic image. - - crop_coord (tuple): crop corner coordinate in mosaic image. - """ - assert loc in ('top_left', 'top_right', 'bottom_left', 'bottom_right') - if loc == 'top_left': - # index0 to top left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - center_position_xy[0], \ - center_position_xy[1] - crop_coord = img_shape_wh[0] - (x2 - x1), img_shape_wh[1] - ( - y2 - y1), img_shape_wh[0], img_shape_wh[1] - - elif loc == 'top_right': - # index1 to top right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - center_position_xy[1] - crop_coord = 0, img_shape_wh[1] - (y2 - y1), min( - img_shape_wh[0], x2 - x1), img_shape_wh[1] - - elif loc == 'bottom_left': - # index2 to bottom left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - center_position_xy[1], \ - center_position_xy[0], \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = img_shape_wh[0] - (x2 - x1), 0, img_shape_wh[0], min( - y2 - y1, img_shape_wh[1]) - - else: - # index3 to bottom right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - center_position_xy[1], \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = 0, 0, min(img_shape_wh[0], - x2 - x1), min(y2 - y1, img_shape_wh[1]) - - paste_coord = x1, y1, x2, y2 - return paste_coord, crop_coord - - def _filter_box_candidates(self, bboxes, labels): - """Filter out bboxes too small after Mosaic.""" - bbox_w = bboxes[:, 2] - bboxes[:, 0] - bbox_h = bboxes[:, 3] - bboxes[:, 1] - valid_inds = (bbox_w > self.min_bbox_size) & \ - (bbox_h > self.min_bbox_size) - valid_inds = np.nonzero(valid_inds)[0] - return bboxes[valid_inds], labels[valid_inds] - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'img_scale={self.img_scale}, ' - repr_str += f'center_ratio_range={self.center_ratio_range}, ' - repr_str += f'pad_val={self.pad_val}, ' - repr_str += f'min_bbox_size={self.min_bbox_size}, ' - repr_str += f'skip_filter={self.skip_filter})' - return repr_str - - -@PIPELINES.register_module() -class MixUp: - """MixUp data augmentation. - - .. code:: text - - mixup transform - +------------------------------+ - | mixup image | | - | +--------|--------+ | - | | | | | - |---------------+ | | - | | | | - | | image | | - | | | | - | | | | - | |-----------------+ | - | pad | - +------------------------------+ - - The mixup transform steps are as follows: - - 1. Another random image is picked by dataset and embedded in - the top left patch(after padding and resizing) - 2. The target of mixup transform is the weighted average of mixup - image and origin image. - - Args: - img_scale (Sequence[int]): Image output size after mixup pipeline. - The shape order should be (height, width). Default: (640, 640). - ratio_range (Sequence[float]): Scale ratio of mixup image. - Default: (0.5, 1.5). - flip_ratio (float): Horizontal flip ratio of mixup image. - Default: 0.5. - pad_val (int): Pad value. Default: 114. - max_iters (int): The maximum number of iterations. If the number of - iterations is greater than `max_iters`, but gt_bbox is still - empty, then the iteration is terminated. Default: 15. - min_bbox_size (float): Width and height threshold to filter bboxes. - If the height or width of a box is smaller than this value, it - will be removed. Default: 5. - min_area_ratio (float): Threshold of area ratio between - original bboxes and wrapped bboxes. If smaller than this value, - the box will be removed. Default: 0.2. - max_aspect_ratio (float): Aspect ratio of width and height - threshold to filter bboxes. If max(h/w, w/h) larger than this - value, the box will be removed. Default: 20. - bbox_clip_border (bool, optional): Whether to clip the objects outside - the border of the image. In some dataset like MOT17, the gt bboxes - are allowed to cross the border of images. Therefore, we don't - need to clip the gt bboxes in these cases. Defaults to True. - skip_filter (bool): Whether to skip filtering rules. If it - is True, the filter rule will not be applied, and the - `min_bbox_size` and `min_area_ratio` and `max_aspect_ratio` - is invalid. Default to True. - """ - - def __init__(self, - img_scale=(640, 640), - ratio_range=(0.5, 1.5), - flip_ratio=0.5, - pad_val=114, - max_iters=15, - min_bbox_size=5, - min_area_ratio=0.2, - max_aspect_ratio=20, - bbox_clip_border=True, - skip_filter=True): - assert isinstance(img_scale, tuple) - log_img_scale(img_scale, skip_square=True) - self.dynamic_scale = img_scale - self.ratio_range = ratio_range - self.flip_ratio = flip_ratio - self.pad_val = pad_val - self.max_iters = max_iters - self.min_bbox_size = min_bbox_size - self.min_area_ratio = min_area_ratio - self.max_aspect_ratio = max_aspect_ratio - self.bbox_clip_border = bbox_clip_border - self.skip_filter = skip_filter - - def __call__(self, results): - """Call function to make a mixup of image. - - Args: - results (dict): Result dict. - - Returns: - dict: Result dict with mixup transformed. - """ - - results = self._mixup_transform(results) - return results - - def get_indexes(self, dataset): - """Call function to collect indexes. - - Args: - dataset (:obj:`MultiImageMixDataset`): The dataset. - - Returns: - list: indexes. - """ - - for i in range(self.max_iters): - index = random.randint(0, len(dataset)) - gt_bboxes_i = dataset.get_ann_info(index)['bboxes'] - if len(gt_bboxes_i) != 0: - break - - return index - - def _mixup_transform(self, results): - """MixUp transform function. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - assert len( - results['mix_results']) == 1, 'MixUp only support 2 images now !' - - if results['mix_results'][0]['gt_bboxes'].shape[0] == 0: - # empty bbox - return results - - retrieve_results = results['mix_results'][0] - retrieve_img = retrieve_results['img'] - - jit_factor = random.uniform(*self.ratio_range) - is_filp = random.uniform(0, 1) > self.flip_ratio - - if len(retrieve_img.shape) == 3: - out_img = np.ones( - (self.dynamic_scale[0], self.dynamic_scale[1], 3), - dtype=retrieve_img.dtype) * self.pad_val - else: - out_img = np.ones( - self.dynamic_scale, dtype=retrieve_img.dtype) * self.pad_val - - # 1. keep_ratio resize - scale_ratio = min(self.dynamic_scale[0] / retrieve_img.shape[0], - self.dynamic_scale[1] / retrieve_img.shape[1]) - retrieve_img = mmcv.imresize( - retrieve_img, (int(retrieve_img.shape[1] * scale_ratio), - int(retrieve_img.shape[0] * scale_ratio))) - - # 2. paste - out_img[:retrieve_img.shape[0], :retrieve_img.shape[1]] = retrieve_img - - # 3. scale jit - scale_ratio *= jit_factor - out_img = mmcv.imresize(out_img, (int(out_img.shape[1] * jit_factor), - int(out_img.shape[0] * jit_factor))) - - # 4. flip - if is_filp: - out_img = out_img[:, ::-1, :] - - # 5. random crop - ori_img = results['img'] - origin_h, origin_w = out_img.shape[:2] - target_h, target_w = ori_img.shape[:2] - padded_img = np.zeros( - (max(origin_h, target_h), max(origin_w, - target_w), 3)).astype(np.uint8) - padded_img[:origin_h, :origin_w] = out_img - - x_offset, y_offset = 0, 0 - if padded_img.shape[0] > target_h: - y_offset = random.randint(0, padded_img.shape[0] - target_h) - if padded_img.shape[1] > target_w: - x_offset = random.randint(0, padded_img.shape[1] - target_w) - padded_cropped_img = padded_img[y_offset:y_offset + target_h, - x_offset:x_offset + target_w] - - # 6. adjust bbox - retrieve_gt_bboxes = retrieve_results['gt_bboxes'] - retrieve_gt_bboxes[:, 0::2] = retrieve_gt_bboxes[:, 0::2] * scale_ratio - retrieve_gt_bboxes[:, 1::2] = retrieve_gt_bboxes[:, 1::2] * scale_ratio - if self.bbox_clip_border: - retrieve_gt_bboxes[:, 0::2] = np.clip(retrieve_gt_bboxes[:, 0::2], - 0, origin_w) - retrieve_gt_bboxes[:, 1::2] = np.clip(retrieve_gt_bboxes[:, 1::2], - 0, origin_h) - - if is_filp: - retrieve_gt_bboxes[:, 0::2] = ( - origin_w - retrieve_gt_bboxes[:, 0::2][:, ::-1]) - - # 7. filter - cp_retrieve_gt_bboxes = retrieve_gt_bboxes.copy() - cp_retrieve_gt_bboxes[:, 0::2] = \ - cp_retrieve_gt_bboxes[:, 0::2] - x_offset - cp_retrieve_gt_bboxes[:, 1::2] = \ - cp_retrieve_gt_bboxes[:, 1::2] - y_offset - if self.bbox_clip_border: - cp_retrieve_gt_bboxes[:, 0::2] = np.clip( - cp_retrieve_gt_bboxes[:, 0::2], 0, target_w) - cp_retrieve_gt_bboxes[:, 1::2] = np.clip( - cp_retrieve_gt_bboxes[:, 1::2], 0, target_h) - - # 8. mix up - ori_img = ori_img.astype(np.float32) - mixup_img = 0.5 * ori_img + 0.5 * padded_cropped_img.astype(np.float32) - - retrieve_gt_labels = retrieve_results['gt_labels'] - if not self.skip_filter: - keep_list = self._filter_box_candidates(retrieve_gt_bboxes.T, - cp_retrieve_gt_bboxes.T) - - retrieve_gt_labels = retrieve_gt_labels[keep_list] - cp_retrieve_gt_bboxes = cp_retrieve_gt_bboxes[keep_list] - - mixup_gt_bboxes = np.concatenate( - (results['gt_bboxes'], cp_retrieve_gt_bboxes), axis=0) - mixup_gt_labels = np.concatenate( - (results['gt_labels'], retrieve_gt_labels), axis=0) - - # remove outside bbox - inside_inds = find_inside_bboxes(mixup_gt_bboxes, target_h, target_w) - mixup_gt_bboxes = mixup_gt_bboxes[inside_inds] - mixup_gt_labels = mixup_gt_labels[inside_inds] - - results['img'] = mixup_img.astype(np.uint8) - results['img_shape'] = mixup_img.shape - results['gt_bboxes'] = mixup_gt_bboxes - results['gt_labels'] = mixup_gt_labels - - return results - - def _filter_box_candidates(self, bbox1, bbox2): - """Compute candidate boxes which include following 5 things: - - bbox1 before augment, bbox2 after augment, min_bbox_size (pixels), - min_area_ratio, max_aspect_ratio. - """ - - w1, h1 = bbox1[2] - bbox1[0], bbox1[3] - bbox1[1] - w2, h2 = bbox2[2] - bbox2[0], bbox2[3] - bbox2[1] - ar = np.maximum(w2 / (h2 + 1e-16), h2 / (w2 + 1e-16)) - return ((w2 > self.min_bbox_size) - & (h2 > self.min_bbox_size) - & (w2 * h2 / (w1 * h1 + 1e-16) > self.min_area_ratio) - & (ar < self.max_aspect_ratio)) - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'dynamic_scale={self.dynamic_scale}, ' - repr_str += f'ratio_range={self.ratio_range}, ' - repr_str += f'flip_ratio={self.flip_ratio}, ' - repr_str += f'pad_val={self.pad_val}, ' - repr_str += f'max_iters={self.max_iters}, ' - repr_str += f'min_bbox_size={self.min_bbox_size}, ' - repr_str += f'min_area_ratio={self.min_area_ratio}, ' - repr_str += f'max_aspect_ratio={self.max_aspect_ratio}, ' - repr_str += f'skip_filter={self.skip_filter})' - return repr_str - - -@PIPELINES.register_module() -class RandomAffine: - """Random affine transform data augmentation. - - This operation randomly generates affine transform matrix which including - rotation, translation, shear and scaling transforms. - - Args: - max_rotate_degree (float): Maximum degrees of rotation transform. - Default: 10. - max_translate_ratio (float): Maximum ratio of translation. - Default: 0.1. - scaling_ratio_range (tuple[float]): Min and max ratio of - scaling transform. Default: (0.5, 1.5). - max_shear_degree (float): Maximum degrees of shear - transform. Default: 2. - border (tuple[int]): Distance from height and width sides of input - image to adjust output shape. Only used in mosaic dataset. - Default: (0, 0). - border_val (tuple[int]): Border padding values of 3 channels. - Default: (114, 114, 114). - min_bbox_size (float): Width and height threshold to filter bboxes. - If the height or width of a box is smaller than this value, it - will be removed. Default: 2. - min_area_ratio (float): Threshold of area ratio between - original bboxes and wrapped bboxes. If smaller than this value, - the box will be removed. Default: 0.2. - max_aspect_ratio (float): Aspect ratio of width and height - threshold to filter bboxes. If max(h/w, w/h) larger than this - value, the box will be removed. - bbox_clip_border (bool, optional): Whether to clip the objects outside - the border of the image. In some dataset like MOT17, the gt bboxes - are allowed to cross the border of images. Therefore, we don't - need to clip the gt bboxes in these cases. Defaults to True. - skip_filter (bool): Whether to skip filtering rules. If it - is True, the filter rule will not be applied, and the - `min_bbox_size` and `min_area_ratio` and `max_aspect_ratio` - is invalid. Default to True. - """ - - def __init__(self, - max_rotate_degree=10.0, - max_translate_ratio=0.1, - scaling_ratio_range=(0.5, 1.5), - max_shear_degree=2.0, - border=(0, 0), - border_val=(114, 114, 114), - min_bbox_size=2, - min_area_ratio=0.2, - max_aspect_ratio=20, - bbox_clip_border=True, - skip_filter=True): - assert 0 <= max_translate_ratio <= 1 - assert scaling_ratio_range[0] <= scaling_ratio_range[1] - assert scaling_ratio_range[0] > 0 - self.max_rotate_degree = max_rotate_degree - self.max_translate_ratio = max_translate_ratio - self.scaling_ratio_range = scaling_ratio_range - self.max_shear_degree = max_shear_degree - self.border = border - self.border_val = border_val - self.min_bbox_size = min_bbox_size - self.min_area_ratio = min_area_ratio - self.max_aspect_ratio = max_aspect_ratio - self.bbox_clip_border = bbox_clip_border - self.skip_filter = skip_filter - - def __call__(self, results): - img = results['img'] - height = img.shape[0] + self.border[0] * 2 - width = img.shape[1] + self.border[1] * 2 - - # Rotation - rotation_degree = random.uniform(-self.max_rotate_degree, - self.max_rotate_degree) - rotation_matrix = self._get_rotation_matrix(rotation_degree) - - # Scaling - scaling_ratio = random.uniform(self.scaling_ratio_range[0], - self.scaling_ratio_range[1]) - scaling_matrix = self._get_scaling_matrix(scaling_ratio) - - # Shear - x_degree = random.uniform(-self.max_shear_degree, - self.max_shear_degree) - y_degree = random.uniform(-self.max_shear_degree, - self.max_shear_degree) - shear_matrix = self._get_shear_matrix(x_degree, y_degree) - - # Translation - trans_x = random.uniform(-self.max_translate_ratio, - self.max_translate_ratio) * width - trans_y = random.uniform(-self.max_translate_ratio, - self.max_translate_ratio) * height - translate_matrix = self._get_translation_matrix(trans_x, trans_y) - - warp_matrix = ( - translate_matrix @ shear_matrix @ rotation_matrix @ scaling_matrix) - - img = cv2.warpPerspective( - img, - warp_matrix, - dsize=(width, height), - borderValue=self.border_val) - results['img'] = img - results['img_shape'] = img.shape - - for key in results.get('bbox_fields', []): - bboxes = results[key] - num_bboxes = len(bboxes) - if num_bboxes: - # homogeneous coordinates - xs = bboxes[:, [0, 0, 2, 2]].reshape(num_bboxes * 4) - ys = bboxes[:, [1, 3, 3, 1]].reshape(num_bboxes * 4) - ones = np.ones_like(xs) - points = np.vstack([xs, ys, ones]) - - warp_points = warp_matrix @ points - warp_points = warp_points[:2] / warp_points[2] - xs = warp_points[0].reshape(num_bboxes, 4) - ys = warp_points[1].reshape(num_bboxes, 4) - - warp_bboxes = np.vstack( - (xs.min(1), ys.min(1), xs.max(1), ys.max(1))).T - - if self.bbox_clip_border: - warp_bboxes[:, [0, 2]] = \ - warp_bboxes[:, [0, 2]].clip(0, width) - warp_bboxes[:, [1, 3]] = \ - warp_bboxes[:, [1, 3]].clip(0, height) - - # remove outside bbox - valid_index = find_inside_bboxes(warp_bboxes, height, width) - if not self.skip_filter: - # filter bboxes - filter_index = self.filter_gt_bboxes( - bboxes * scaling_ratio, warp_bboxes) - valid_index = valid_index & filter_index - - results[key] = warp_bboxes[valid_index] - if key in ['gt_bboxes']: - if 'gt_labels' in results: - results['gt_labels'] = results['gt_labels'][ - valid_index] - - if 'gt_masks' in results: - raise NotImplementedError( - 'RandomAffine only supports bbox.') - return results - - def filter_gt_bboxes(self, origin_bboxes, wrapped_bboxes): - origin_w = origin_bboxes[:, 2] - origin_bboxes[:, 0] - origin_h = origin_bboxes[:, 3] - origin_bboxes[:, 1] - wrapped_w = wrapped_bboxes[:, 2] - wrapped_bboxes[:, 0] - wrapped_h = wrapped_bboxes[:, 3] - wrapped_bboxes[:, 1] - aspect_ratio = np.maximum(wrapped_w / (wrapped_h + 1e-16), - wrapped_h / (wrapped_w + 1e-16)) - - wh_valid_idx = (wrapped_w > self.min_bbox_size) & \ - (wrapped_h > self.min_bbox_size) - area_valid_idx = wrapped_w * wrapped_h / (origin_w * origin_h + - 1e-16) > self.min_area_ratio - aspect_ratio_valid_idx = aspect_ratio < self.max_aspect_ratio - return wh_valid_idx & area_valid_idx & aspect_ratio_valid_idx - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(max_rotate_degree={self.max_rotate_degree}, ' - repr_str += f'max_translate_ratio={self.max_translate_ratio}, ' - repr_str += f'scaling_ratio={self.scaling_ratio_range}, ' - repr_str += f'max_shear_degree={self.max_shear_degree}, ' - repr_str += f'border={self.border}, ' - repr_str += f'border_val={self.border_val}, ' - repr_str += f'min_bbox_size={self.min_bbox_size}, ' - repr_str += f'min_area_ratio={self.min_area_ratio}, ' - repr_str += f'max_aspect_ratio={self.max_aspect_ratio}, ' - repr_str += f'skip_filter={self.skip_filter})' - return repr_str - - @staticmethod - def _get_rotation_matrix(rotate_degrees): - radian = math.radians(rotate_degrees) - rotation_matrix = np.array( - [[np.cos(radian), -np.sin(radian), 0.], - [np.sin(radian), np.cos(radian), 0.], [0., 0., 1.]], - dtype=np.float32) - return rotation_matrix - - @staticmethod - def _get_scaling_matrix(scale_ratio): - scaling_matrix = np.array( - [[scale_ratio, 0., 0.], [0., scale_ratio, 0.], [0., 0., 1.]], - dtype=np.float32) - return scaling_matrix - - @staticmethod - def _get_share_matrix(scale_ratio): - scaling_matrix = np.array( - [[scale_ratio, 0., 0.], [0., scale_ratio, 0.], [0., 0., 1.]], - dtype=np.float32) - return scaling_matrix - - @staticmethod - def _get_shear_matrix(x_shear_degrees, y_shear_degrees): - x_radian = math.radians(x_shear_degrees) - y_radian = math.radians(y_shear_degrees) - shear_matrix = np.array([[1, np.tan(x_radian), 0.], - [np.tan(y_radian), 1, 0.], [0., 0., 1.]], - dtype=np.float32) - return shear_matrix - - @staticmethod - def _get_translation_matrix(x, y): - translation_matrix = np.array([[1, 0., x], [0., 1, y], [0., 0., 1.]], - dtype=np.float32) - return translation_matrix - - -@PIPELINES.register_module() -class YOLOXHSVRandomAug: - """Apply HSV augmentation to image sequentially. It is referenced from - https://github.com/Megvii- - BaseDetection/YOLOX/blob/main/yolox/data/data_augment.py#L21. - - Args: - hue_delta (int): delta of hue. Default: 5. - saturation_delta (int): delta of saturation. Default: 30. - value_delta (int): delat of value. Default: 30. - """ - - def __init__(self, hue_delta=5, saturation_delta=30, value_delta=30): - self.hue_delta = hue_delta - self.saturation_delta = saturation_delta - self.value_delta = value_delta - - def __call__(self, results): - img = results['img'] - hsv_gains = np.random.uniform(-1, 1, 3) * [ - self.hue_delta, self.saturation_delta, self.value_delta - ] - # random selection of h, s, v - hsv_gains *= np.random.randint(0, 2, 3) - # prevent overflow - hsv_gains = hsv_gains.astype(np.int16) - img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV).astype(np.int16) - - img_hsv[..., 0] = (img_hsv[..., 0] + hsv_gains[0]) % 180 - img_hsv[..., 1] = np.clip(img_hsv[..., 1] + hsv_gains[1], 0, 255) - img_hsv[..., 2] = np.clip(img_hsv[..., 2] + hsv_gains[2], 0, 255) - cv2.cvtColor(img_hsv.astype(img.dtype), cv2.COLOR_HSV2BGR, dst=img) - - results['img'] = img - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(hue_delta={self.hue_delta}, ' - repr_str += f'saturation_delta={self.saturation_delta}, ' - repr_str += f'value_delta={self.value_delta})' - return repr_str - - -@PIPELINES.register_module() -class CopyPaste: - """Simple Copy-Paste is a Strong Data Augmentation Method for Instance - Segmentation The simple copy-paste transform steps are as follows: - - 1. The destination image is already resized with aspect ratio kept, - cropped and padded. - 2. Randomly select a source image, which is also already resized - with aspect ratio kept, cropped and padded in a similar way - as the destination image. - 3. Randomly select some objects from the source image. - 4. Paste these source objects to the destination image directly, - due to the source and destination image have the same size. - 5. Update object masks of the destination image, for some origin objects - may be occluded. - 6. Generate bboxes from the updated destination masks and - filter some objects which are totally occluded, and adjust bboxes - which are partly occluded. - 7. Append selected source bboxes, masks, and labels. - - Args: - max_num_pasted (int): The maximum number of pasted objects. - Default: 100. - bbox_occluded_thr (int): The threshold of occluded bbox. - Default: 10. - mask_occluded_thr (int): The threshold of occluded mask. - Default: 300. - selected (bool): Whether select objects or not. If select is False, - all objects of the source image will be pasted to the - destination image. - Default: True. - """ - - def __init__( - self, - max_num_pasted=100, - bbox_occluded_thr=10, - mask_occluded_thr=300, - selected=True, - ): - self.max_num_pasted = max_num_pasted - self.bbox_occluded_thr = bbox_occluded_thr - self.mask_occluded_thr = mask_occluded_thr - self.selected = selected - - def get_indexes(self, dataset): - """Call function to collect indexes.s. - - Args: - dataset (:obj:`MultiImageMixDataset`): The dataset. - Returns: - list: Indexes. - """ - return random.randint(0, len(dataset)) - - def __call__(self, results): - """Call function to make a copy-paste of image. - - Args: - results (dict): Result dict. - Returns: - dict: Result dict with copy-paste transformed. - """ - - assert 'mix_results' in results - num_images = len(results['mix_results']) - assert num_images == 1, \ - f'CopyPaste only supports processing 2 images, got {num_images}' - if self.selected: - selected_results = self._select_object(results['mix_results'][0]) - else: - selected_results = results['mix_results'][0] - return self._copy_paste(results, selected_results) - - def _select_object(self, results): - """Select some objects from the source results.""" - bboxes = results['gt_bboxes'] - labels = results['gt_labels'] - masks = results['gt_masks'] - max_num_pasted = min(bboxes.shape[0] + 1, self.max_num_pasted) - num_pasted = np.random.randint(0, max_num_pasted) - selected_inds = np.random.choice( - bboxes.shape[0], size=num_pasted, replace=False) - - selected_bboxes = bboxes[selected_inds] - selected_labels = labels[selected_inds] - selected_masks = masks[selected_inds] - - results['gt_bboxes'] = selected_bboxes - results['gt_labels'] = selected_labels - results['gt_masks'] = selected_masks - return results - - def _copy_paste(self, dst_results, src_results): - """CopyPaste transform function. - - Args: - dst_results (dict): Result dict of the destination image. - src_results (dict): Result dict of the source image. - Returns: - dict: Updated result dict. - """ - dst_img = dst_results['img'] - dst_bboxes = dst_results['gt_bboxes'] - dst_labels = dst_results['gt_labels'] - dst_masks = dst_results['gt_masks'] - - src_img = src_results['img'] - src_bboxes = src_results['gt_bboxes'] - src_labels = src_results['gt_labels'] - src_masks = src_results['gt_masks'] - - if len(src_bboxes) == 0: - return dst_results - - # update masks and generate bboxes from updated masks - composed_mask = np.where(np.any(src_masks.masks, axis=0), 1, 0) - updated_dst_masks = self.get_updated_masks(dst_masks, composed_mask) - updated_dst_bboxes = updated_dst_masks.get_bboxes() - assert len(updated_dst_bboxes) == len(updated_dst_masks) - - # filter totally occluded objects - bboxes_inds = np.all( - np.abs( - (updated_dst_bboxes - dst_bboxes)) <= self.bbox_occluded_thr, - axis=-1) - masks_inds = updated_dst_masks.masks.sum( - axis=(1, 2)) > self.mask_occluded_thr - valid_inds = bboxes_inds | masks_inds - - # Paste source objects to destination image directly - img = dst_img * (1 - composed_mask[..., np.newaxis] - ) + src_img * composed_mask[..., np.newaxis] - bboxes = np.concatenate([updated_dst_bboxes[valid_inds], src_bboxes]) - labels = np.concatenate([dst_labels[valid_inds], src_labels]) - masks = np.concatenate( - [updated_dst_masks.masks[valid_inds], src_masks.masks]) - - dst_results['img'] = img - dst_results['gt_bboxes'] = bboxes - dst_results['gt_labels'] = labels - dst_results['gt_masks'] = BitmapMasks(masks, masks.shape[1], - masks.shape[2]) - - return dst_results - - def get_updated_masks(self, masks, composed_mask): - assert masks.masks.shape[-2:] == composed_mask.shape[-2:], \ - 'Cannot compare two arrays of different size' - masks.masks = np.where(composed_mask, 0, masks.masks) - return masks - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'max_num_pasted={self.max_num_pasted}, ' - repr_str += f'bbox_occluded_thr={self.bbox_occluded_thr}, ' - repr_str += f'mask_occluded_thr={self.mask_occluded_thr}, ' - repr_str += f'selected={self.selected}, ' - return repr_str diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/__init__.py deleted file mode 100644 index a4c7ea135..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .class_aware_sampler import ClassAwareSampler -from .distributed_sampler import DistributedSampler -from .group_sampler import DistributedGroupSampler, GroupSampler -from .infinite_sampler import InfiniteBatchSampler, InfiniteGroupBatchSampler - -__all__ = [ - 'DistributedSampler', 'DistributedGroupSampler', 'GroupSampler', - 'InfiniteGroupBatchSampler', 'InfiniteBatchSampler', 'ClassAwareSampler' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/class_aware_sampler.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/class_aware_sampler.py deleted file mode 100644 index c52708eb8..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/class_aware_sampler.py +++ /dev/null @@ -1,176 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import torch -from mmcv.runner import get_dist_info -from torch.utils.data import Sampler - -from mmdet.core.utils import sync_random_seed - - -class ClassAwareSampler(Sampler): - r"""Sampler that restricts data loading to the label of the dataset. - - A class-aware sampling strategy to effectively tackle the - non-uniform class distribution. The length of the training data is - consistent with source data. Simple improvements based on `Relay - Backpropagation for Effective Learning of Deep Convolutional - Neural Networks `_ - - The implementation logic is referred to - https://github.com/Sense-X/TSD/blob/master/mmdet/datasets/samplers/distributed_classaware_sampler.py - - Args: - dataset: Dataset used for sampling. - samples_per_gpu (int): When model is :obj:`DistributedDataParallel`, - it is the number of training samples on each GPU. - When model is :obj:`DataParallel`, it is - `num_gpus * samples_per_gpu`. - Default : 1. - num_replicas (optional): Number of processes participating in - distributed training. - rank (optional): Rank of the current process within num_replicas. - seed (int, optional): random seed used to shuffle the sampler if - ``shuffle=True``. This number should be identical across all - processes in the distributed group. Default: 0. - num_sample_class (int): The number of samples taken from each - per-label list. Default: 1 - """ - - def __init__(self, - dataset, - samples_per_gpu=1, - num_replicas=None, - rank=None, - seed=0, - num_sample_class=1): - _rank, _num_replicas = get_dist_info() - if num_replicas is None: - num_replicas = _num_replicas - if rank is None: - rank = _rank - - self.dataset = dataset - self.num_replicas = num_replicas - self.samples_per_gpu = samples_per_gpu - self.rank = rank - self.epoch = 0 - # Must be the same across all workers. If None, will use a - # random seed shared among workers - # (require synchronization among all workers) - self.seed = sync_random_seed(seed) - - # The number of samples taken from each per-label list - assert num_sample_class > 0 and isinstance(num_sample_class, int) - self.num_sample_class = num_sample_class - # Get per-label image list from dataset - assert hasattr(dataset, 'get_cat2imgs'), \ - 'dataset must have `get_cat2imgs` function' - self.cat_dict = dataset.get_cat2imgs() - - self.num_samples = int( - math.ceil( - len(self.dataset) * 1.0 / self.num_replicas / - self.samples_per_gpu)) * self.samples_per_gpu - self.total_size = self.num_samples * self.num_replicas - - # get number of images containing each category - self.num_cat_imgs = [len(x) for x in self.cat_dict.values()] - # filter labels without images - self.valid_cat_inds = [ - i for i, length in enumerate(self.num_cat_imgs) if length != 0 - ] - self.num_classes = len(self.valid_cat_inds) - - def __iter__(self): - # deterministically shuffle based on epoch - g = torch.Generator() - g.manual_seed(self.epoch + self.seed) - - # initialize label list - label_iter_list = RandomCycleIter(self.valid_cat_inds, generator=g) - # initialize each per-label image list - data_iter_dict = dict() - for i in self.valid_cat_inds: - data_iter_dict[i] = RandomCycleIter(self.cat_dict[i], generator=g) - - def gen_cat_img_inds(cls_list, data_dict, num_sample_cls): - """Traverse the categories and extract `num_sample_cls` image - indexes of the corresponding categories one by one.""" - id_indices = [] - for _ in range(len(cls_list)): - cls_idx = next(cls_list) - for _ in range(num_sample_cls): - id = next(data_dict[cls_idx]) - id_indices.append(id) - return id_indices - - # deterministically shuffle based on epoch - num_bins = int( - math.ceil(self.total_size * 1.0 / self.num_classes / - self.num_sample_class)) - indices = [] - for i in range(num_bins): - indices += gen_cat_img_inds(label_iter_list, data_iter_dict, - self.num_sample_class) - - # fix extra samples to make it evenly divisible - if len(indices) >= self.total_size: - indices = indices[:self.total_size] - else: - indices += indices[:(self.total_size - len(indices))] - assert len(indices) == self.total_size - - # subsample - offset = self.num_samples * self.rank - indices = indices[offset:offset + self.num_samples] - assert len(indices) == self.num_samples - - return iter(indices) - - def __len__(self): - return self.num_samples - - def set_epoch(self, epoch): - self.epoch = epoch - - -class RandomCycleIter: - """Shuffle the list and do it again after the list have traversed. - - The implementation logic is referred to - https://github.com/wutong16/DistributionBalancedLoss/blob/master/mllt/datasets/loader/sampler.py - - Example: - >>> label_list = [0, 1, 2, 4, 5] - >>> g = torch.Generator() - >>> g.manual_seed(0) - >>> label_iter_list = RandomCycleIter(label_list, generator=g) - >>> index = next(label_iter_list) - Args: - data (list or ndarray): The data that needs to be shuffled. - generator: An torch.Generator object, which is used in setting the seed - for generating random numbers. - """ # noqa: W605 - - def __init__(self, data, generator=None): - self.data = data - self.length = len(data) - self.index = torch.randperm(self.length, generator=generator).numpy() - self.i = 0 - self.generator = generator - - def __iter__(self): - return self - - def __len__(self): - return len(self.data) - - def __next__(self): - if self.i == self.length: - self.index = torch.randperm( - self.length, generator=self.generator).numpy() - self.i = 0 - idx = self.data[self.index[self.i]] - self.i += 1 - return idx diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/distributed_sampler.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/distributed_sampler.py deleted file mode 100644 index 1bc8b7c36..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/distributed_sampler.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import torch -from torch.utils.data import DistributedSampler as _DistributedSampler - -from mmdet.core.utils import sync_random_seed -from mmdet.utils import get_device - - -class DistributedSampler(_DistributedSampler): - - def __init__(self, - dataset, - num_replicas=None, - rank=None, - shuffle=True, - seed=0): - super().__init__( - dataset, num_replicas=num_replicas, rank=rank, shuffle=shuffle) - - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - device = get_device() - self.seed = sync_random_seed(seed, device) - - def __iter__(self): - # deterministically shuffle based on epoch - if self.shuffle: - g = torch.Generator() - # When :attr:`shuffle=True`, this ensures all replicas - # use a different random ordering for each epoch. - # Otherwise, the next iteration of this sampler will - # yield the same ordering. - g.manual_seed(self.epoch + self.seed) - indices = torch.randperm(len(self.dataset), generator=g).tolist() - else: - indices = torch.arange(len(self.dataset)).tolist() - - # add extra samples to make it evenly divisible - # in case that indices is shorter than half of total_size - indices = (indices * - math.ceil(self.total_size / len(indices)))[:self.total_size] - assert len(indices) == self.total_size - - # subsample - indices = indices[self.rank:self.total_size:self.num_replicas] - assert len(indices) == self.num_samples - - return iter(indices) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/group_sampler.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/group_sampler.py deleted file mode 100644 index 783d2b21c..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/group_sampler.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -from mmcv.runner import get_dist_info -from torch.utils.data import Sampler - - -class GroupSampler(Sampler): - - def __init__(self, dataset, samples_per_gpu=1): - assert hasattr(dataset, 'flag') - self.dataset = dataset - self.samples_per_gpu = samples_per_gpu - self.flag = dataset.flag.astype(np.int64) - self.group_sizes = np.bincount(self.flag) - self.num_samples = 0 - for i, size in enumerate(self.group_sizes): - self.num_samples += int(np.ceil( - size / self.samples_per_gpu)) * self.samples_per_gpu - - def __iter__(self): - indices = [] - for i, size in enumerate(self.group_sizes): - if size == 0: - continue - indice = np.where(self.flag == i)[0] - assert len(indice) == size - np.random.shuffle(indice) - num_extra = int(np.ceil(size / self.samples_per_gpu) - ) * self.samples_per_gpu - len(indice) - indice = np.concatenate( - [indice, np.random.choice(indice, num_extra)]) - indices.append(indice) - indices = np.concatenate(indices) - indices = [ - indices[i * self.samples_per_gpu:(i + 1) * self.samples_per_gpu] - for i in np.random.permutation( - range(len(indices) // self.samples_per_gpu)) - ] - indices = np.concatenate(indices) - indices = indices.astype(np.int64).tolist() - assert len(indices) == self.num_samples - return iter(indices) - - def __len__(self): - return self.num_samples - - -class DistributedGroupSampler(Sampler): - """Sampler that restricts data loading to a subset of the dataset. - - It is especially useful in conjunction with - :class:`torch.nn.parallel.DistributedDataParallel`. In such case, each - process can pass a DistributedSampler instance as a DataLoader sampler, - and load a subset of the original dataset that is exclusive to it. - - .. note:: - Dataset is assumed to be of constant size. - - Arguments: - dataset: Dataset used for sampling. - num_replicas (optional): Number of processes participating in - distributed training. - rank (optional): Rank of the current process within num_replicas. - seed (int, optional): random seed used to shuffle the sampler if - ``shuffle=True``. This number should be identical across all - processes in the distributed group. Default: 0. - """ - - def __init__(self, - dataset, - samples_per_gpu=1, - num_replicas=None, - rank=None, - seed=0): - _rank, _num_replicas = get_dist_info() - if num_replicas is None: - num_replicas = _num_replicas - if rank is None: - rank = _rank - self.dataset = dataset - self.samples_per_gpu = samples_per_gpu - self.num_replicas = num_replicas - self.rank = rank - self.epoch = 0 - self.seed = seed if seed is not None else 0 - - assert hasattr(self.dataset, 'flag') - self.flag = self.dataset.flag - self.group_sizes = np.bincount(self.flag) - - self.num_samples = 0 - for i, j in enumerate(self.group_sizes): - self.num_samples += int( - math.ceil(self.group_sizes[i] * 1.0 / self.samples_per_gpu / - self.num_replicas)) * self.samples_per_gpu - self.total_size = self.num_samples * self.num_replicas - - def __iter__(self): - # deterministically shuffle based on epoch - g = torch.Generator() - g.manual_seed(self.epoch + self.seed) - - indices = [] - for i, size in enumerate(self.group_sizes): - if size > 0: - indice = np.where(self.flag == i)[0] - assert len(indice) == size - # add .numpy() to avoid bug when selecting indice in parrots. - # TODO: check whether torch.randperm() can be replaced by - # numpy.random.permutation(). - indice = indice[list( - torch.randperm(int(size), generator=g).numpy())].tolist() - extra = int( - math.ceil( - size * 1.0 / self.samples_per_gpu / self.num_replicas) - ) * self.samples_per_gpu * self.num_replicas - len(indice) - # pad indice - tmp = indice.copy() - for _ in range(extra // size): - indice.extend(tmp) - indice.extend(tmp[:extra % size]) - indices.extend(indice) - - assert len(indices) == self.total_size - - indices = [ - indices[j] for i in list( - torch.randperm( - len(indices) // self.samples_per_gpu, generator=g)) - for j in range(i * self.samples_per_gpu, (i + 1) * - self.samples_per_gpu) - ] - - # subsample - offset = self.num_samples * self.rank - indices = indices[offset:offset + self.num_samples] - assert len(indices) == self.num_samples - - return iter(indices) - - def __len__(self): - return self.num_samples - - def set_epoch(self, epoch): - self.epoch = epoch diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/infinite_sampler.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/infinite_sampler.py deleted file mode 100644 index d42487e6a..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/samplers/infinite_sampler.py +++ /dev/null @@ -1,186 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import itertools - -import numpy as np -import torch -from mmcv.runner import get_dist_info -from torch.utils.data.sampler import Sampler - -from mmdet.core.utils import sync_random_seed - - -class InfiniteGroupBatchSampler(Sampler): - """Similar to `BatchSampler` warping a `GroupSampler. It is designed for - iteration-based runners like `IterBasedRunner` and yields a mini-batch - indices each time, all indices in a batch should be in the same group. - - The implementation logic is referred to - https://github.com/facebookresearch/detectron2/blob/main/detectron2/data/samplers/grouped_batch_sampler.py - - Args: - dataset (object): The dataset. - batch_size (int): When model is :obj:`DistributedDataParallel`, - it is the number of training samples on each GPU. - When model is :obj:`DataParallel`, it is - `num_gpus * samples_per_gpu`. - Default : 1. - world_size (int, optional): Number of processes participating in - distributed training. Default: None. - rank (int, optional): Rank of current process. Default: None. - seed (int): Random seed. Default: 0. - shuffle (bool): Whether shuffle the indices of a dummy `epoch`, it - should be noted that `shuffle` can not guarantee that you can - generate sequential indices because it need to ensure - that all indices in a batch is in a group. Default: True. - """ # noqa: W605 - - def __init__(self, - dataset, - batch_size=1, - world_size=None, - rank=None, - seed=0, - shuffle=True): - _rank, _world_size = get_dist_info() - if world_size is None: - world_size = _world_size - if rank is None: - rank = _rank - self.rank = rank - self.world_size = world_size - self.dataset = dataset - self.batch_size = batch_size - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - self.shuffle = shuffle - - assert hasattr(self.dataset, 'flag') - self.flag = self.dataset.flag - self.group_sizes = np.bincount(self.flag) - # buffer used to save indices of each group - self.buffer_per_group = {k: [] for k in range(len(self.group_sizes))} - - self.size = len(dataset) - self.indices = self._indices_of_rank() - - def _infinite_indices(self): - """Infinitely yield a sequence of indices.""" - g = torch.Generator() - g.manual_seed(self.seed) - while True: - if self.shuffle: - yield from torch.randperm(self.size, generator=g).tolist() - - else: - yield from torch.arange(self.size).tolist() - - def _indices_of_rank(self): - """Slice the infinite indices by rank.""" - yield from itertools.islice(self._infinite_indices(), self.rank, None, - self.world_size) - - def __iter__(self): - # once batch size is reached, yield the indices - for idx in self.indices: - flag = self.flag[idx] - group_buffer = self.buffer_per_group[flag] - group_buffer.append(idx) - if len(group_buffer) == self.batch_size: - yield group_buffer[:] - del group_buffer[:] - - def __len__(self): - """Length of base dataset.""" - return self.size - - def set_epoch(self, epoch): - """Not supported in `IterationBased` runner.""" - raise NotImplementedError - - -class InfiniteBatchSampler(Sampler): - """Similar to `BatchSampler` warping a `DistributedSampler. It is designed - iteration-based runners like `IterBasedRunner` and yields a mini-batch - indices each time. - - The implementation logic is referred to - https://github.com/facebookresearch/detectron2/blob/main/detectron2/data/samplers/grouped_batch_sampler.py - - Args: - dataset (object): The dataset. - batch_size (int): When model is :obj:`DistributedDataParallel`, - it is the number of training samples on each GPU, - When model is :obj:`DataParallel`, it is - `num_gpus * samples_per_gpu`. - Default : 1. - world_size (int, optional): Number of processes participating in - distributed training. Default: None. - rank (int, optional): Rank of current process. Default: None. - seed (int): Random seed. Default: 0. - shuffle (bool): Whether shuffle the dataset or not. Default: True. - """ # noqa: W605 - - def __init__(self, - dataset, - batch_size=1, - world_size=None, - rank=None, - seed=0, - shuffle=True): - _rank, _world_size = get_dist_info() - if world_size is None: - world_size = _world_size - if rank is None: - rank = _rank - self.rank = rank - self.world_size = world_size - self.dataset = dataset - self.batch_size = batch_size - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - self.shuffle = shuffle - self.size = len(dataset) - self.indices = self._indices_of_rank() - - def _infinite_indices(self): - """Infinitely yield a sequence of indices.""" - g = torch.Generator() - g.manual_seed(self.seed) - while True: - if self.shuffle: - yield from torch.randperm(self.size, generator=g).tolist() - - else: - yield from torch.arange(self.size).tolist() - - def _indices_of_rank(self): - """Slice the infinite indices by rank.""" - yield from itertools.islice(self._infinite_indices(), self.rank, None, - self.world_size) - - def __iter__(self): - # once batch size is reached, yield the indices - batch_buffer = [] - for idx in self.indices: - batch_buffer.append(idx) - if len(batch_buffer) == self.batch_size: - yield batch_buffer - batch_buffer = [] - - def __len__(self): - """Length of base dataset.""" - return self.size - - def set_epoch(self, epoch): - """Not supported in `IterationBased` runner.""" - raise NotImplementedError diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/utils.py b/cv/instance_segmentation/solo/pytorch/mmdet/datasets/utils.py deleted file mode 100644 index be911b57c..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/datasets/utils.py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings - -from mmcv.cnn import VGG -from mmcv.runner.hooks import HOOKS, Hook - -from mmdet.datasets.builder import PIPELINES -from mmdet.datasets.pipelines import (LoadAnnotations, LoadImageFromFile, - LoadPanopticAnnotations) - - -def replace_ImageToTensor(pipelines): - """Replace the ImageToTensor transform in a data pipeline to - DefaultFormatBundle, which is normally useful in batch inference. - - Args: - pipelines (list[dict]): Data pipeline configs. - - Returns: - list: The new pipeline list with all ImageToTensor replaced by - DefaultFormatBundle. - - Examples: - >>> pipelines = [ - ... dict(type='LoadImageFromFile'), - ... dict( - ... type='MultiScaleFlipAug', - ... img_scale=(1333, 800), - ... flip=False, - ... transforms=[ - ... dict(type='Resize', keep_ratio=True), - ... dict(type='RandomFlip'), - ... dict(type='Normalize', mean=[0, 0, 0], std=[1, 1, 1]), - ... dict(type='Pad', size_divisor=32), - ... dict(type='ImageToTensor', keys=['img']), - ... dict(type='Collect', keys=['img']), - ... ]) - ... ] - >>> expected_pipelines = [ - ... dict(type='LoadImageFromFile'), - ... dict( - ... type='MultiScaleFlipAug', - ... img_scale=(1333, 800), - ... flip=False, - ... transforms=[ - ... dict(type='Resize', keep_ratio=True), - ... dict(type='RandomFlip'), - ... dict(type='Normalize', mean=[0, 0, 0], std=[1, 1, 1]), - ... dict(type='Pad', size_divisor=32), - ... dict(type='DefaultFormatBundle'), - ... dict(type='Collect', keys=['img']), - ... ]) - ... ] - >>> assert expected_pipelines == replace_ImageToTensor(pipelines) - """ - pipelines = copy.deepcopy(pipelines) - for i, pipeline in enumerate(pipelines): - if pipeline['type'] == 'MultiScaleFlipAug': - assert 'transforms' in pipeline - pipeline['transforms'] = replace_ImageToTensor( - pipeline['transforms']) - elif pipeline['type'] == 'ImageToTensor': - warnings.warn( - '"ImageToTensor" pipeline is replaced by ' - '"DefaultFormatBundle" for batch inference. It is ' - 'recommended to manually replace it in the test ' - 'data pipeline in your config file.', UserWarning) - pipelines[i] = {'type': 'DefaultFormatBundle'} - return pipelines - - -def get_loading_pipeline(pipeline): - """Only keep loading image and annotations related configuration. - - Args: - pipeline (list[dict]): Data pipeline configs. - - Returns: - list[dict]: The new pipeline list with only keep - loading image and annotations related configuration. - - Examples: - >>> pipelines = [ - ... dict(type='LoadImageFromFile'), - ... dict(type='LoadAnnotations', with_bbox=True), - ... dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), - ... dict(type='RandomFlip', flip_ratio=0.5), - ... dict(type='Normalize', **img_norm_cfg), - ... dict(type='Pad', size_divisor=32), - ... dict(type='DefaultFormatBundle'), - ... dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']) - ... ] - >>> expected_pipelines = [ - ... dict(type='LoadImageFromFile'), - ... dict(type='LoadAnnotations', with_bbox=True) - ... ] - >>> assert expected_pipelines ==\ - ... get_loading_pipeline(pipelines) - """ - loading_pipeline_cfg = [] - for cfg in pipeline: - obj_cls = PIPELINES.get(cfg['type']) - # TODO:use more elegant way to distinguish loading modules - if obj_cls is not None and obj_cls in (LoadImageFromFile, - LoadAnnotations, - LoadPanopticAnnotations): - loading_pipeline_cfg.append(cfg) - assert len(loading_pipeline_cfg) == 2, \ - 'The data pipeline in your config file must include ' \ - 'loading image and annotations related pipeline.' - return loading_pipeline_cfg - - -@HOOKS.register_module() -class NumClassCheckHook(Hook): - - def _check_head(self, runner): - """Check whether the `num_classes` in head matches the length of - `CLASSES` in `dataset`. - - Args: - runner (obj:`EpochBasedRunner`): Epoch based Runner. - """ - model = runner.model - dataset = runner.data_loader.dataset - if dataset.CLASSES is None: - runner.logger.warning( - f'Please set `CLASSES` ' - f'in the {dataset.__class__.__name__} and' - f'check if it is consistent with the `num_classes` ' - f'of head') - else: - assert type(dataset.CLASSES) is not str, \ - (f'`CLASSES` in {dataset.__class__.__name__}' - f'should be a tuple of str.' - f'Add comma if number of classes is 1 as ' - f'CLASSES = ({dataset.CLASSES},)') - for name, module in model.named_modules(): - if hasattr(module, 'num_classes') and not isinstance( - module, VGG): - assert module.num_classes == len(dataset.CLASSES), \ - (f'The `num_classes` ({module.num_classes}) in ' - f'{module.__class__.__name__} of ' - f'{model.__class__.__name__} does not matches ' - f'the length of `CLASSES` ' - f'{len(dataset.CLASSES)}) in ' - f'{dataset.__class__.__name__}') - - def before_train_epoch(self, runner): - """Check whether the training dataset is compatible with head. - - Args: - runner (obj:`EpochBasedRunner`): Epoch based Runner. - """ - self._check_head(runner) - - def before_val_epoch(self, runner): - """Check whether the dataset in val epoch is compatible with head. - - Args: - runner (obj:`EpochBasedRunner`): Epoch based Runner. - """ - self._check_head(runner) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/__init__.py deleted file mode 100644 index eb114dde7..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .backbones import * # noqa: F401,F403 -from .builder import (BACKBONES, DETECTORS, HEADS, LOSSES, NECKS, - ROI_EXTRACTORS, SHARED_HEADS, build_backbone, - build_detector, build_head, build_loss, build_neck, - build_roi_extractor, build_shared_head) -from .dense_heads import * # noqa: F401,F403 -from .detectors import * # noqa: F401,F403 -from .losses import * # noqa: F401,F403 -from .necks import * # noqa: F401,F403 - -__all__ = [ - 'BACKBONES', 'NECKS', 'ROI_EXTRACTORS', 'SHARED_HEADS', 'HEADS', 'LOSSES', - 'DETECTORS', 'build_backbone', 'build_neck', 'build_roi_extractor', - 'build_shared_head', 'build_head', 'build_loss', 'build_detector' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/backbones/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/backbones/__init__.py deleted file mode 100644 index c6f2540a8..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/backbones/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .resnet import ResNet, ResNetV1d - -__all__ = [ - 'ResNet', 'ResNetV1d' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/backbones/resnet.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/backbones/resnet.py deleted file mode 100644 index 1eaaae67c..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/backbones/resnet.py +++ /dev/null @@ -1,672 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.utils.checkpoint as cp -from mmcv.cnn import build_conv_layer, build_norm_layer, build_plugin_layer -from mmcv.runner import BaseModule -from torch.nn.modules.batchnorm import _BatchNorm - -from ..builder import BACKBONES -from ..utils import ResLayer - - -class BasicBlock(BaseModule): - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(BasicBlock, self).__init__(init_cfg) - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - 3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - self.add_module(self.norm1_name, norm1) - self.conv2 = build_conv_layer( - conv_cfg, planes, planes, 3, padding=1, bias=False) - self.add_module(self.norm2_name, norm2) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.norm2(out) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -class Bottleneck(BaseModule): - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - """Bottleneck block for ResNet. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if - it is "caffe", the stride-two layer is the first 1x1 conv layer. - """ - super(Bottleneck, self).__init__(init_cfg) - assert style in ['pytorch', 'caffe'] - assert dcn is None or isinstance(dcn, dict) - assert plugins is None or isinstance(plugins, list) - if plugins is not None: - allowed_position = ['after_conv1', 'after_conv2', 'after_conv3'] - assert all(p['position'] in allowed_position for p in plugins) - - self.inplanes = inplanes - self.planes = planes - self.stride = stride - self.dilation = dilation - self.style = style - self.with_cp = with_cp - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.dcn = dcn - self.with_dcn = dcn is not None - self.plugins = plugins - self.with_plugins = plugins is not None - - if self.with_plugins: - # collect plugins for conv1/conv2/conv3 - self.after_conv1_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv1' - ] - self.after_conv2_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv2' - ] - self.after_conv3_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv3' - ] - - if self.style == 'pytorch': - self.conv1_stride = 1 - self.conv2_stride = stride - else: - self.conv1_stride = stride - self.conv2_stride = 1 - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - self.norm3_name, norm3 = build_norm_layer( - norm_cfg, planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - fallback_on_stride = False - if self.with_dcn: - fallback_on_stride = dcn.pop('fallback_on_stride', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = build_conv_layer( - conv_cfg, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - else: - assert self.conv_cfg is None, 'conv_cfg must be None for DCN' - self.conv2 = build_conv_layer( - dcn, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.add_module(self.norm2_name, norm2) - self.conv3 = build_conv_layer( - conv_cfg, - planes, - planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - - if self.with_plugins: - self.after_conv1_plugin_names = self.make_block_plugins( - planes, self.after_conv1_plugins) - self.after_conv2_plugin_names = self.make_block_plugins( - planes, self.after_conv2_plugins) - self.after_conv3_plugin_names = self.make_block_plugins( - planes * self.expansion, self.after_conv3_plugins) - - def make_block_plugins(self, in_channels, plugins): - """make plugins for block. - - Args: - in_channels (int): Input channels of plugin. - plugins (list[dict]): List of plugins cfg to build. - - Returns: - list[str]: List of the names of plugin. - """ - assert isinstance(plugins, list) - plugin_names = [] - for plugin in plugins: - plugin = plugin.copy() - name, layer = build_plugin_layer( - plugin, - in_channels=in_channels, - postfix=plugin.pop('postfix', '')) - assert not hasattr(self, name), f'duplicate plugin {name}' - self.add_module(name, layer) - plugin_names.append(name) - return plugin_names - - def forward_plugin(self, x, plugin_names): - out = x - for name in plugin_names: - out = getattr(self, name)(out) - return out - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - @property - def norm3(self): - """nn.Module: normalization layer after the third convolution layer""" - return getattr(self, self.norm3_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv1_plugin_names) - - out = self.conv2(out) - out = self.norm2(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv2_plugin_names) - - out = self.conv3(out) - out = self.norm3(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv3_plugin_names) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -@BACKBONES.register_module() -class ResNet(BaseModule): - """ResNet backbone. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - stem_channels (int | None): Number of stem channels. If not specified, - it will be the same as `base_channels`. Default: None. - base_channels (int): Number of base channels of res layer. Default: 64. - in_channels (int): Number of input image channels. Default: 3. - num_stages (int): Resnet stages. Default: 4. - strides (Sequence[int]): Strides of the first block of each stage. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. - deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. - frozen_stages (int): Stages to be frozen (stop grad and set eval mode). - -1 means not freezing any parameters. - norm_cfg (dict): Dictionary to construct and config norm layer. - norm_eval (bool): Whether to set norm layers to eval mode, namely, - freeze running stats (mean and var). Note: Effect on Batch Norm - and its variants only. - plugins (list[dict]): List of plugins for stages, each dict contains: - - - cfg (dict, required): Cfg dict to build plugin. - - position (str, required): Position inside block to insert - plugin, options are 'after_conv1', 'after_conv2', 'after_conv3'. - - stages (tuple[bool], optional): Stages to apply plugin, length - should be same as 'num_stages'. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. - zero_init_residual (bool): Whether to use zero init for last norm layer - in resblocks to let them behave as identity. - pretrained (str, optional): model pretrained path. Default: None - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None - - Example: - >>> from mmdet.models import ResNet - >>> import torch - >>> self = ResNet(depth=18) - >>> self.eval() - >>> inputs = torch.rand(1, 3, 32, 32) - >>> level_outputs = self.forward(inputs) - >>> for level_out in level_outputs: - ... print(tuple(level_out.shape)) - (1, 64, 8, 8) - (1, 128, 4, 4) - (1, 256, 2, 2) - (1, 512, 1, 1) - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - in_channels=3, - stem_channels=None, - base_channels=64, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - deep_stem=False, - avg_down=False, - frozen_stages=-1, - conv_cfg=None, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=True, - dcn=None, - stage_with_dcn=(False, False, False, False), - plugins=None, - with_cp=False, - zero_init_residual=True, - pretrained=None, - init_cfg=None): - super(ResNet, self).__init__(init_cfg) - self.zero_init_residual = zero_init_residual - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - - block_init_cfg = None - assert not (init_cfg and pretrained), \ - 'init_cfg and pretrained cannot be specified at the same time' - if isinstance(pretrained, str): - warnings.warn('DeprecationWarning: pretrained is deprecated, ' - 'please use "init_cfg" instead') - self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - elif pretrained is None: - if init_cfg is None: - self.init_cfg = [ - dict(type='Kaiming', layer='Conv2d'), - dict( - type='Constant', - val=1, - layer=['_BatchNorm', 'GroupNorm']) - ] - block = self.arch_settings[depth][0] - if self.zero_init_residual: - if block is BasicBlock: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm2')) - elif block is Bottleneck: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm3')) - else: - raise TypeError('pretrained must be a str or None') - - self.depth = depth - if stem_channels is None: - stem_channels = base_channels - self.stem_channels = stem_channels - self.base_channels = base_channels - self.num_stages = num_stages - assert num_stages >= 1 and num_stages <= 4 - self.strides = strides - self.dilations = dilations - assert len(strides) == len(dilations) == num_stages - self.out_indices = out_indices - assert max(out_indices) < num_stages - self.style = style - self.deep_stem = deep_stem - self.avg_down = avg_down - self.frozen_stages = frozen_stages - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.with_cp = with_cp - self.norm_eval = norm_eval - self.dcn = dcn - self.stage_with_dcn = stage_with_dcn - if dcn is not None: - assert len(stage_with_dcn) == num_stages - self.plugins = plugins - self.block, stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - self.inplanes = stem_channels - - self._make_stem_layer(in_channels, stem_channels) - - self.res_layers = [] - for i, num_blocks in enumerate(self.stage_blocks): - stride = strides[i] - dilation = dilations[i] - dcn = self.dcn if self.stage_with_dcn[i] else None - if plugins is not None: - stage_plugins = self.make_stage_plugins(plugins, i) - else: - stage_plugins = None - planes = base_channels * 2**i - res_layer = self.make_res_layer( - block=self.block, - inplanes=self.inplanes, - planes=planes, - num_blocks=num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - avg_down=self.avg_down, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - dcn=dcn, - plugins=stage_plugins, - init_cfg=block_init_cfg) - self.inplanes = planes * self.block.expansion - layer_name = f'layer{i + 1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self._freeze_stages() - - self.feat_dim = self.block.expansion * base_channels * 2**( - len(self.stage_blocks) - 1) - - def make_stage_plugins(self, plugins, stage_idx): - """Make plugins for ResNet ``stage_idx`` th stage. - - Currently we support to insert ``context_block``, - ``empirical_attention_block``, ``nonlocal_block`` into the backbone - like ResNet/ResNeXt. They could be inserted after conv1/conv2/conv3 of - Bottleneck. - - An example of plugins format could be: - - Examples: - >>> plugins=[ - ... dict(cfg=dict(type='xxx', arg1='xxx'), - ... stages=(False, True, True, True), - ... position='after_conv2'), - ... dict(cfg=dict(type='yyy'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='1'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='2'), - ... stages=(True, True, True, True), - ... position='after_conv3') - ... ] - >>> self = ResNet(depth=18) - >>> stage_plugins = self.make_stage_plugins(plugins, 0) - >>> assert len(stage_plugins) == 3 - - Suppose ``stage_idx=0``, the structure of blocks in the stage would be: - - .. code-block:: none - - conv1-> conv2->conv3->yyy->zzz1->zzz2 - - Suppose 'stage_idx=1', the structure of blocks in the stage would be: - - .. code-block:: none - - conv1-> conv2->xxx->conv3->yyy->zzz1->zzz2 - - If stages is missing, the plugin would be applied to all stages. - - Args: - plugins (list[dict]): List of plugins cfg to build. The postfix is - required if multiple same type plugins are inserted. - stage_idx (int): Index of stage to build - - Returns: - list[dict]: Plugins for current stage - """ - stage_plugins = [] - for plugin in plugins: - plugin = plugin.copy() - stages = plugin.pop('stages', None) - assert stages is None or len(stages) == self.num_stages - # whether to insert plugin into current stage - if stages is None or stages[stage_idx]: - stage_plugins.append(plugin) - - return stage_plugins - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``.""" - return ResLayer(**kwargs) - - @property - def norm1(self): - """nn.Module: the normalization layer named "norm1" """ - return getattr(self, self.norm1_name) - - def _make_stem_layer(self, in_channels, stem_channels): - if self.deep_stem: - self.stem = nn.Sequential( - build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels // 2, - kernel_size=3, - stride=2, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels // 2, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels)[1], - nn.ReLU(inplace=True)) - else: - self.conv1 = build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels, - kernel_size=7, - stride=2, - padding=3, - bias=False) - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, stem_channels, postfix=1) - self.add_module(self.norm1_name, norm1) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - def _freeze_stages(self): - if self.frozen_stages >= 0: - if self.deep_stem: - self.stem.eval() - for param in self.stem.parameters(): - param.requires_grad = False - else: - self.norm1.eval() - for m in [self.conv1, self.norm1]: - for param in m.parameters(): - param.requires_grad = False - - for i in range(1, self.frozen_stages + 1): - m = getattr(self, f'layer{i}') - m.eval() - for param in m.parameters(): - param.requires_grad = False - - def forward(self, x): - """Forward function.""" - if self.deep_stem: - x = self.stem(x) - else: - x = self.conv1(x) - x = self.norm1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - return tuple(outs) - - def train(self, mode=True): - """Convert the model into training mode while keep normalization layer - freezed.""" - super(ResNet, self).train(mode) - self._freeze_stages() - if mode and self.norm_eval: - for m in self.modules(): - # trick: eval have effect on BatchNorm only - if isinstance(m, _BatchNorm): - m.eval() - - -@BACKBONES.register_module() -class ResNetV1d(ResNet): - r"""ResNetV1d variant described in `Bag of Tricks - `_. - - Compared with default ResNet(ResNetV1b), ResNetV1d replaces the 7x7 conv in - the input stem with three 3x3 convs. And in the downsampling block, a 2x2 - avg_pool with stride 2 is added before conv, whose stride is changed to 1. - """ - - def __init__(self, **kwargs): - super(ResNetV1d, self).__init__( - deep_stem=True, avg_down=True, **kwargs) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/builder.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/builder.py deleted file mode 100644 index ace6209f7..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/builder.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -from mmcv.cnn import MODELS as MMCV_MODELS -from mmcv.utils import Registry - -MODELS = Registry('models', parent=MMCV_MODELS) - -BACKBONES = MODELS -NECKS = MODELS -ROI_EXTRACTORS = MODELS -SHARED_HEADS = MODELS -HEADS = MODELS -LOSSES = MODELS -DETECTORS = MODELS - - -def build_backbone(cfg): - """Build backbone.""" - return BACKBONES.build(cfg) - - -def build_neck(cfg): - """Build neck.""" - return NECKS.build(cfg) - - -def build_roi_extractor(cfg): - """Build roi extractor.""" - return ROI_EXTRACTORS.build(cfg) - - -def build_shared_head(cfg): - """Build shared head.""" - return SHARED_HEADS.build(cfg) - - -def build_head(cfg): - """Build head.""" - return HEADS.build(cfg) - - -def build_loss(cfg): - """Build loss.""" - return LOSSES.build(cfg) - - -def build_detector(cfg, train_cfg=None, test_cfg=None): - """Build detector.""" - if train_cfg is not None or test_cfg is not None: - warnings.warn( - 'train_cfg and test_cfg is deprecated, ' - 'please specify them in model', UserWarning) - assert cfg.get('train_cfg') is None or train_cfg is None, \ - 'train_cfg specified in both outer field and model field ' - assert cfg.get('test_cfg') is None or test_cfg is None, \ - 'test_cfg specified in both outer field and model field ' - return DETECTORS.build( - cfg, default_args=dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/__init__.py deleted file mode 100644 index 19aecea61..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .solo_head import SOLOHead - -__all__ = ['SOLOHead'] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/anchor_head.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/anchor_head.py deleted file mode 100644 index d1bfab62d..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/anchor_head.py +++ /dev/null @@ -1,542 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -import torch.nn as nn -from mmcv.runner import force_fp32 - -from mmdet.core import (anchor_inside_flags, build_assigner, build_bbox_coder, - build_prior_generator, build_sampler, images_to_levels, - multi_apply, unmap) -from ..builder import HEADS, build_loss -from .base_dense_head import BaseDenseHead -from .dense_test_mixins import BBoxTestMixin - - -@HEADS.register_module() -class AnchorHead(BaseDenseHead, BBoxTestMixin): - """Anchor-based head (RPN, RetinaNet, SSD, etc.). - - Args: - num_classes (int): Number of categories excluding the background - category. - in_channels (int): Number of channels in the input feature map. - feat_channels (int): Number of hidden channels. Used in child classes. - anchor_generator (dict): Config dict for anchor generator - bbox_coder (dict): Config of bounding box coder. - reg_decoded_bbox (bool): If true, the regression loss would be - applied directly on decoded bounding boxes, converting both - the predicted boxes and regression targets to absolute - coordinates format. Default False. It should be `True` when - using `IoULoss`, `GIoULoss`, or `DIoULoss` in the bbox head. - loss_cls (dict): Config of classification loss. - loss_bbox (dict): Config of localization loss. - train_cfg (dict): Training config of anchor head. - test_cfg (dict): Testing config of anchor head. - init_cfg (dict or list[dict], optional): Initialization config dict. - """ # noqa: W605 - - def __init__(self, - num_classes, - in_channels, - feat_channels=256, - anchor_generator=dict( - type='AnchorGenerator', - scales=[8, 16, 32], - ratios=[0.5, 1.0, 2.0], - strides=[4, 8, 16, 32, 64]), - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - clip_border=True, - target_means=(.0, .0, .0, .0), - target_stds=(1.0, 1.0, 1.0, 1.0)), - reg_decoded_bbox=False, - loss_cls=dict( - type='CrossEntropyLoss', - use_sigmoid=True, - loss_weight=1.0), - loss_bbox=dict( - type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0), - train_cfg=None, - test_cfg=None, - init_cfg=dict(type='Normal', layer='Conv2d', std=0.01)): - super(AnchorHead, self).__init__(init_cfg) - self.in_channels = in_channels - self.num_classes = num_classes - self.feat_channels = feat_channels - self.use_sigmoid_cls = loss_cls.get('use_sigmoid', False) - if self.use_sigmoid_cls: - self.cls_out_channels = num_classes - else: - self.cls_out_channels = num_classes + 1 - - if self.cls_out_channels <= 0: - raise ValueError(f'num_classes={num_classes} is too small') - self.reg_decoded_bbox = reg_decoded_bbox - - self.bbox_coder = build_bbox_coder(bbox_coder) - self.loss_cls = build_loss(loss_cls) - self.loss_bbox = build_loss(loss_bbox) - self.train_cfg = train_cfg - self.test_cfg = test_cfg - if self.train_cfg: - self.assigner = build_assigner(self.train_cfg.assigner) - if hasattr(self.train_cfg, - 'sampler') and self.train_cfg.sampler.type.split( - '.')[-1] != 'PseudoSampler': - self.sampling = True - sampler_cfg = self.train_cfg.sampler - # avoid BC-breaking - if loss_cls['type'] in [ - 'FocalLoss', 'GHMC', 'QualityFocalLoss' - ]: - warnings.warn( - 'DeprecationWarning: Determining whether to sampling' - 'by loss type is deprecated, please delete sampler in' - 'your config when using `FocalLoss`, `GHMC`, ' - '`QualityFocalLoss` or other FocalLoss variant.') - self.sampling = False - sampler_cfg = dict(type='PseudoSampler') - else: - self.sampling = False - sampler_cfg = dict(type='PseudoSampler') - self.sampler = build_sampler(sampler_cfg, context=self) - self.fp16_enabled = False - - self.prior_generator = build_prior_generator(anchor_generator) - - # Usually the numbers of anchors for each level are the same - # except SSD detectors. So it is an int in the most dense - # heads but a list of int in SSDHead - self.num_base_priors = self.prior_generator.num_base_priors[0] - self._init_layers() - - @property - def num_anchors(self): - warnings.warn('DeprecationWarning: `num_anchors` is deprecated, ' - 'for consistency or also use ' - '`num_base_priors` instead') - return self.prior_generator.num_base_priors[0] - - @property - def anchor_generator(self): - warnings.warn('DeprecationWarning: anchor_generator is deprecated, ' - 'please use "prior_generator" instead') - return self.prior_generator - - def _init_layers(self): - """Initialize layers of the head.""" - self.conv_cls = nn.Conv2d(self.in_channels, - self.num_base_priors * self.cls_out_channels, - 1) - self.conv_reg = nn.Conv2d(self.in_channels, self.num_base_priors * 4, - 1) - - def forward_single(self, x): - """Forward feature of a single scale level. - - Args: - x (Tensor): Features of a single scale level. - - Returns: - tuple: - cls_score (Tensor): Cls scores for a single scale level \ - the channels number is num_base_priors * num_classes. - bbox_pred (Tensor): Box energies / deltas for a single scale \ - level, the channels number is num_base_priors * 4. - """ - cls_score = self.conv_cls(x) - bbox_pred = self.conv_reg(x) - return cls_score, bbox_pred - - def forward(self, feats): - """Forward features from the upstream network. - - Args: - feats (tuple[Tensor]): Features from the upstream network, each is - a 4D-tensor. - - Returns: - tuple: A tuple of classification scores and bbox prediction. - - - cls_scores (list[Tensor]): Classification scores for all \ - scale levels, each is a 4D-tensor, the channels number \ - is num_base_priors * num_classes. - - bbox_preds (list[Tensor]): Box energies / deltas for all \ - scale levels, each is a 4D-tensor, the channels number \ - is num_base_priors * 4. - """ - return multi_apply(self.forward_single, feats) - - def get_anchors(self, featmap_sizes, img_metas, device='cuda'): - """Get anchors according to feature map sizes. - - Args: - featmap_sizes (list[tuple]): Multi-level feature map sizes. - img_metas (list[dict]): Image meta info. - device (torch.device | str): Device for returned tensors - - Returns: - tuple: - anchor_list (list[Tensor]): Anchors of each image. - valid_flag_list (list[Tensor]): Valid flags of each image. - """ - num_imgs = len(img_metas) - - # since feature map sizes of all images are the same, we only compute - # anchors for one time - multi_level_anchors = self.prior_generator.grid_priors( - featmap_sizes, device=device) - anchor_list = [multi_level_anchors for _ in range(num_imgs)] - - # for each image, we compute valid flags of multi level anchors - valid_flag_list = [] - for img_id, img_meta in enumerate(img_metas): - multi_level_flags = self.prior_generator.valid_flags( - featmap_sizes, img_meta['pad_shape'], device) - valid_flag_list.append(multi_level_flags) - - return anchor_list, valid_flag_list - - def _get_targets_single(self, - flat_anchors, - valid_flags, - gt_bboxes, - gt_bboxes_ignore, - gt_labels, - img_meta, - label_channels=1, - unmap_outputs=True): - """Compute regression and classification targets for anchors in a - single image. - - Args: - flat_anchors (Tensor): Multi-level anchors of the image, which are - concatenated into a single tensor of shape (num_anchors ,4) - valid_flags (Tensor): Multi level valid flags of the image, - which are concatenated into a single tensor of - shape (num_anchors,). - gt_bboxes (Tensor): Ground truth bboxes of the image, - shape (num_gts, 4). - gt_bboxes_ignore (Tensor): Ground truth bboxes to be - ignored, shape (num_ignored_gts, 4). - img_meta (dict): Meta info of the image. - gt_labels (Tensor): Ground truth labels of each box, - shape (num_gts,). - label_channels (int): Channel of label. - unmap_outputs (bool): Whether to map outputs back to the original - set of anchors. - - Returns: - tuple: - labels_list (list[Tensor]): Labels of each level - label_weights_list (list[Tensor]): Label weights of each level - bbox_targets_list (list[Tensor]): BBox targets of each level - bbox_weights_list (list[Tensor]): BBox weights of each level - num_total_pos (int): Number of positive samples in all images - num_total_neg (int): Number of negative samples in all images - """ - inside_flags = anchor_inside_flags(flat_anchors, valid_flags, - img_meta['img_shape'][:2], - self.train_cfg.allowed_border) - if not inside_flags.any(): - return (None, ) * 7 - # assign gt and sample anchors - anchors = flat_anchors[inside_flags, :] - - assign_result = self.assigner.assign( - anchors, gt_bboxes, gt_bboxes_ignore, - None if self.sampling else gt_labels) - sampling_result = self.sampler.sample(assign_result, anchors, - gt_bboxes) - - num_valid_anchors = anchors.shape[0] - bbox_targets = torch.zeros_like(anchors) - bbox_weights = torch.zeros_like(anchors) - labels = anchors.new_full((num_valid_anchors, ), - self.num_classes, - dtype=torch.long) - label_weights = anchors.new_zeros(num_valid_anchors, dtype=torch.float) - - pos_inds = sampling_result.pos_inds - neg_inds = sampling_result.neg_inds - if len(pos_inds) > 0: - if not self.reg_decoded_bbox: - pos_bbox_targets = self.bbox_coder.encode( - sampling_result.pos_bboxes, sampling_result.pos_gt_bboxes) - else: - pos_bbox_targets = sampling_result.pos_gt_bboxes - bbox_targets[pos_inds, :] = pos_bbox_targets - bbox_weights[pos_inds, :] = 1.0 - if gt_labels is None: - # Only rpn gives gt_labels as None - # Foreground is the first class since v2.5.0 - labels[pos_inds] = 0 - else: - labels[pos_inds] = gt_labels[ - sampling_result.pos_assigned_gt_inds] - if self.train_cfg.pos_weight <= 0: - label_weights[pos_inds] = 1.0 - else: - label_weights[pos_inds] = self.train_cfg.pos_weight - if len(neg_inds) > 0: - label_weights[neg_inds] = 1.0 - - # map up to original set of anchors - if unmap_outputs: - num_total_anchors = flat_anchors.size(0) - labels = unmap( - labels, num_total_anchors, inside_flags, - fill=self.num_classes) # fill bg label - label_weights = unmap(label_weights, num_total_anchors, - inside_flags) - bbox_targets = unmap(bbox_targets, num_total_anchors, inside_flags) - bbox_weights = unmap(bbox_weights, num_total_anchors, inside_flags) - - return (labels, label_weights, bbox_targets, bbox_weights, pos_inds, - neg_inds, sampling_result) - - def get_targets(self, - anchor_list, - valid_flag_list, - gt_bboxes_list, - img_metas, - gt_bboxes_ignore_list=None, - gt_labels_list=None, - label_channels=1, - unmap_outputs=True, - return_sampling_results=False): - """Compute regression and classification targets for anchors in - multiple images. - - Args: - anchor_list (list[list[Tensor]]): Multi level anchors of each - image. The outer list indicates images, and the inner list - corresponds to feature levels of the image. Each element of - the inner list is a tensor of shape (num_anchors, 4). - valid_flag_list (list[list[Tensor]]): Multi level valid flags of - each image. The outer list indicates images, and the inner list - corresponds to feature levels of the image. Each element of - the inner list is a tensor of shape (num_anchors, ) - gt_bboxes_list (list[Tensor]): Ground truth bboxes of each image. - img_metas (list[dict]): Meta info of each image. - gt_bboxes_ignore_list (list[Tensor]): Ground truth bboxes to be - ignored. - gt_labels_list (list[Tensor]): Ground truth labels of each box. - label_channels (int): Channel of label. - unmap_outputs (bool): Whether to map outputs back to the original - set of anchors. - - Returns: - tuple: Usually returns a tuple containing learning targets. - - - labels_list (list[Tensor]): Labels of each level. - - label_weights_list (list[Tensor]): Label weights of each - level. - - bbox_targets_list (list[Tensor]): BBox targets of each level. - - bbox_weights_list (list[Tensor]): BBox weights of each level. - - num_total_pos (int): Number of positive samples in all - images. - - num_total_neg (int): Number of negative samples in all - images. - - additional_returns: This function enables user-defined returns from - `self._get_targets_single`. These returns are currently refined - to properties at each feature map (i.e. having HxW dimension). - The results will be concatenated after the end - """ - num_imgs = len(img_metas) - assert len(anchor_list) == len(valid_flag_list) == num_imgs - - # anchor number of multi levels - num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]] - # concat all level anchors to a single tensor - concat_anchor_list = [] - concat_valid_flag_list = [] - for i in range(num_imgs): - assert len(anchor_list[i]) == len(valid_flag_list[i]) - concat_anchor_list.append(torch.cat(anchor_list[i])) - concat_valid_flag_list.append(torch.cat(valid_flag_list[i])) - - # compute targets for each image - if gt_bboxes_ignore_list is None: - gt_bboxes_ignore_list = [None for _ in range(num_imgs)] - if gt_labels_list is None: - gt_labels_list = [None for _ in range(num_imgs)] - results = multi_apply( - self._get_targets_single, - concat_anchor_list, - concat_valid_flag_list, - gt_bboxes_list, - gt_bboxes_ignore_list, - gt_labels_list, - img_metas, - label_channels=label_channels, - unmap_outputs=unmap_outputs) - (all_labels, all_label_weights, all_bbox_targets, all_bbox_weights, - pos_inds_list, neg_inds_list, sampling_results_list) = results[:7] - rest_results = list(results[7:]) # user-added return values - # no valid anchors - if any([labels is None for labels in all_labels]): - return None - # sampled anchors of all images - num_total_pos = sum([max(inds.numel(), 1) for inds in pos_inds_list]) - num_total_neg = sum([max(inds.numel(), 1) for inds in neg_inds_list]) - # split targets to a list w.r.t. multiple levels - labels_list = images_to_levels(all_labels, num_level_anchors) - label_weights_list = images_to_levels(all_label_weights, - num_level_anchors) - bbox_targets_list = images_to_levels(all_bbox_targets, - num_level_anchors) - bbox_weights_list = images_to_levels(all_bbox_weights, - num_level_anchors) - res = (labels_list, label_weights_list, bbox_targets_list, - bbox_weights_list, num_total_pos, num_total_neg) - if return_sampling_results: - res = res + (sampling_results_list, ) - for i, r in enumerate(rest_results): # user-added return values - rest_results[i] = images_to_levels(r, num_level_anchors) - - return res + tuple(rest_results) - - def loss_single(self, cls_score, bbox_pred, anchors, labels, label_weights, - bbox_targets, bbox_weights, num_total_samples): - """Compute loss of a single scale level. - - Args: - cls_score (Tensor): Box scores for each scale level - Has shape (N, num_anchors * num_classes, H, W). - bbox_pred (Tensor): Box energies / deltas for each scale - level with shape (N, num_anchors * 4, H, W). - anchors (Tensor): Box reference for each scale level with shape - (N, num_total_anchors, 4). - labels (Tensor): Labels of each anchors with shape - (N, num_total_anchors). - label_weights (Tensor): Label weights of each anchor with shape - (N, num_total_anchors) - bbox_targets (Tensor): BBox regression targets of each anchor - weight shape (N, num_total_anchors, 4). - bbox_weights (Tensor): BBox regression loss weights of each anchor - with shape (N, num_total_anchors, 4). - num_total_samples (int): If sampling, num total samples equal to - the number of total anchors; Otherwise, it is the number of - positive anchors. - - Returns: - dict[str, Tensor]: A dictionary of loss components. - """ - # classification loss - labels = labels.reshape(-1) - label_weights = label_weights.reshape(-1) - cls_score = cls_score.permute(0, 2, 3, - 1).reshape(-1, self.cls_out_channels) - loss_cls = self.loss_cls( - cls_score, labels, label_weights, avg_factor=num_total_samples) - # regression loss - bbox_targets = bbox_targets.reshape(-1, 4) - bbox_weights = bbox_weights.reshape(-1, 4) - bbox_pred = bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4) - if self.reg_decoded_bbox: - # When the regression loss (e.g. `IouLoss`, `GIouLoss`) - # is applied directly on the decoded bounding boxes, it - # decodes the already encoded coordinates to absolute format. - anchors = anchors.reshape(-1, 4) - bbox_pred = self.bbox_coder.decode(anchors, bbox_pred) - loss_bbox = self.loss_bbox( - bbox_pred, - bbox_targets, - bbox_weights, - avg_factor=num_total_samples) - return loss_cls, loss_bbox - - @force_fp32(apply_to=('cls_scores', 'bbox_preds')) - def loss(self, - cls_scores, - bbox_preds, - gt_bboxes, - gt_labels, - img_metas, - gt_bboxes_ignore=None): - """Compute losses of the head. - - Args: - cls_scores (list[Tensor]): Box scores for each scale level - Has shape (N, num_anchors * num_classes, H, W) - bbox_preds (list[Tensor]): Box energies / deltas for each scale - level with shape (N, num_anchors * 4, H, W) - gt_bboxes (list[Tensor]): Ground truth bboxes for each image with - shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format. - gt_labels (list[Tensor]): class indices corresponding to each box - img_metas (list[dict]): Meta information of each image, e.g., - image size, scaling factor, etc. - gt_bboxes_ignore (None | list[Tensor]): specify which bounding - boxes can be ignored when computing the loss. Default: None - - Returns: - dict[str, Tensor]: A dictionary of loss components. - """ - featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores] - assert len(featmap_sizes) == self.prior_generator.num_levels - - device = cls_scores[0].device - - anchor_list, valid_flag_list = self.get_anchors( - featmap_sizes, img_metas, device=device) - label_channels = self.cls_out_channels if self.use_sigmoid_cls else 1 - cls_reg_targets = self.get_targets( - anchor_list, - valid_flag_list, - gt_bboxes, - img_metas, - gt_bboxes_ignore_list=gt_bboxes_ignore, - gt_labels_list=gt_labels, - label_channels=label_channels) - if cls_reg_targets is None: - return None - (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list, - num_total_pos, num_total_neg) = cls_reg_targets - num_total_samples = ( - num_total_pos + num_total_neg if self.sampling else num_total_pos) - - # anchor number of multi levels - num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]] - # concat all level anchors and flags to a single tensor - concat_anchor_list = [] - for i in range(len(anchor_list)): - concat_anchor_list.append(torch.cat(anchor_list[i])) - all_anchor_list = images_to_levels(concat_anchor_list, - num_level_anchors) - - losses_cls, losses_bbox = multi_apply( - self.loss_single, - cls_scores, - bbox_preds, - all_anchor_list, - labels_list, - label_weights_list, - bbox_targets_list, - bbox_weights_list, - num_total_samples=num_total_samples) - return dict(loss_cls=losses_cls, loss_bbox=losses_bbox) - - def aug_test(self, feats, img_metas, rescale=False): - """Test function with test time augmentation. - - Args: - feats (list[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains features for all images in the batch. - img_metas (list[list[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. each dict has image information. - rescale (bool, optional): Whether to rescale the results. - Defaults to False. - - Returns: - list[tuple[Tensor, Tensor]]: Each item in result_list is 2-tuple. - The first item is ``bboxes`` with shape (n, 5), where - 5 represent (tl_x, tl_y, br_x, br_y, score). - The shape of the second tensor in the tuple is ``labels`` - with shape (n,), The length of list should always be 1. - """ - return self.aug_test_bboxes(feats, img_metas, rescale=rescale) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/base_dense_head.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/base_dense_head.py deleted file mode 100644 index 0c7abb7b9..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/base_dense_head.py +++ /dev/null @@ -1,526 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -import torch -from mmcv.cnn.utils.weight_init import constant_init -from mmcv.ops import batched_nms -from mmcv.runner import BaseModule, force_fp32 - -from mmdet.core.utils import filter_scores_and_topk, select_single_mlvl - - -class BaseDenseHead(BaseModule, metaclass=ABCMeta): - """Base class for DenseHeads.""" - - def __init__(self, init_cfg=None): - super(BaseDenseHead, self).__init__(init_cfg) - - def init_weights(self): - super(BaseDenseHead, self).init_weights() - # avoid init_cfg overwrite the initialization of `conv_offset` - for m in self.modules(): - # DeformConv2dPack, ModulatedDeformConv2dPack - if hasattr(m, 'conv_offset'): - constant_init(m.conv_offset, 0) - - @abstractmethod - def loss(self, **kwargs): - """Compute losses of the head.""" - pass - - @force_fp32(apply_to=('cls_scores', 'bbox_preds')) - def get_bboxes(self, - cls_scores, - bbox_preds, - score_factors=None, - img_metas=None, - cfg=None, - rescale=False, - with_nms=True, - **kwargs): - """Transform network outputs of a batch into bbox results. - - Note: When score_factors is not None, the cls_scores are - usually multiplied by it then obtain the real score used in NMS, - such as CenterNess in FCOS, IoU branch in ATSS. - - Args: - cls_scores (list[Tensor]): Classification scores for all - scale levels, each is a 4D-tensor, has shape - (batch_size, num_priors * num_classes, H, W). - bbox_preds (list[Tensor]): Box energies / deltas for all - scale levels, each is a 4D-tensor, has shape - (batch_size, num_priors * 4, H, W). - score_factors (list[Tensor], Optional): Score factor for - all scale level, each is a 4D-tensor, has shape - (batch_size, num_priors * 1, H, W). Default None. - img_metas (list[dict], Optional): Image meta info. Default None. - cfg (mmcv.Config, Optional): Test / postprocessing configuration, - if None, test_cfg would be used. Default None. - rescale (bool): If True, return boxes in original image space. - Default False. - with_nms (bool): If True, do nms before return boxes. - Default True. - - Returns: - list[list[Tensor, Tensor]]: Each item in result_list is 2-tuple. - The first item is an (n, 5) tensor, where the first 4 columns - are bounding box positions (tl_x, tl_y, br_x, br_y) and the - 5-th column is a score between 0 and 1. The second item is a - (n,) tensor where each item is the predicted class label of - the corresponding box. - """ - assert len(cls_scores) == len(bbox_preds) - - if score_factors is None: - # e.g. Retina, FreeAnchor, Foveabox, etc. - with_score_factors = False - else: - # e.g. FCOS, PAA, ATSS, AutoAssign, etc. - with_score_factors = True - assert len(cls_scores) == len(score_factors) - - num_levels = len(cls_scores) - - featmap_sizes = [cls_scores[i].shape[-2:] for i in range(num_levels)] - mlvl_priors = self.prior_generator.grid_priors( - featmap_sizes, - dtype=cls_scores[0].dtype, - device=cls_scores[0].device) - - result_list = [] - - for img_id in range(len(img_metas)): - img_meta = img_metas[img_id] - cls_score_list = select_single_mlvl(cls_scores, img_id) - bbox_pred_list = select_single_mlvl(bbox_preds, img_id) - if with_score_factors: - score_factor_list = select_single_mlvl(score_factors, img_id) - else: - score_factor_list = [None for _ in range(num_levels)] - - results = self._get_bboxes_single(cls_score_list, bbox_pred_list, - score_factor_list, mlvl_priors, - img_meta, cfg, rescale, with_nms, - **kwargs) - result_list.append(results) - return result_list - - def _get_bboxes_single(self, - cls_score_list, - bbox_pred_list, - score_factor_list, - mlvl_priors, - img_meta, - cfg, - rescale=False, - with_nms=True, - **kwargs): - """Transform outputs of a single image into bbox predictions. - - Args: - cls_score_list (list[Tensor]): Box scores from all scale - levels of a single image, each item has shape - (num_priors * num_classes, H, W). - bbox_pred_list (list[Tensor]): Box energies / deltas from - all scale levels of a single image, each item has shape - (num_priors * 4, H, W). - score_factor_list (list[Tensor]): Score factor from all scale - levels of a single image, each item has shape - (num_priors * 1, H, W). - mlvl_priors (list[Tensor]): Each element in the list is - the priors of a single level in feature pyramid. In all - anchor-based methods, it has shape (num_priors, 4). In - all anchor-free methods, it has shape (num_priors, 2) - when `with_stride=True`, otherwise it still has shape - (num_priors, 4). - img_meta (dict): Image meta info. - cfg (mmcv.Config): Test / postprocessing configuration, - if None, test_cfg would be used. - rescale (bool): If True, return boxes in original image space. - Default: False. - with_nms (bool): If True, do nms before return boxes. - Default: True. - - Returns: - tuple[Tensor]: Results of detected bboxes and labels. If with_nms - is False and mlvl_score_factor is None, return mlvl_bboxes and - mlvl_scores, else return mlvl_bboxes, mlvl_scores and - mlvl_score_factor. Usually with_nms is False is used for aug - test. If with_nms is True, then return the following format - - - det_bboxes (Tensor): Predicted bboxes with shape \ - [num_bboxes, 5], where the first 4 columns are bounding \ - box positions (tl_x, tl_y, br_x, br_y) and the 5-th \ - column are scores between 0 and 1. - - det_labels (Tensor): Predicted labels of the corresponding \ - box with shape [num_bboxes]. - """ - if score_factor_list[0] is None: - # e.g. Retina, FreeAnchor, etc. - with_score_factors = False - else: - # e.g. FCOS, PAA, ATSS, etc. - with_score_factors = True - - cfg = self.test_cfg if cfg is None else cfg - img_shape = img_meta['img_shape'] - nms_pre = cfg.get('nms_pre', -1) - - mlvl_bboxes = [] - mlvl_scores = [] - mlvl_labels = [] - if with_score_factors: - mlvl_score_factors = [] - else: - mlvl_score_factors = None - for level_idx, (cls_score, bbox_pred, score_factor, priors) in \ - enumerate(zip(cls_score_list, bbox_pred_list, - score_factor_list, mlvl_priors)): - - assert cls_score.size()[-2:] == bbox_pred.size()[-2:] - - bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, 4) - if with_score_factors: - score_factor = score_factor.permute(1, 2, - 0).reshape(-1).sigmoid() - cls_score = cls_score.permute(1, 2, - 0).reshape(-1, self.cls_out_channels) - if self.use_sigmoid_cls: - scores = cls_score.sigmoid() - else: - # remind that we set FG labels to [0, num_class-1] - # since mmdet v2.0 - # BG cat_id: num_class - scores = cls_score.softmax(-1)[:, :-1] - - # After https://github.com/open-mmlab/mmdetection/pull/6268/, - # this operation keeps fewer bboxes under the same `nms_pre`. - # There is no difference in performance for most models. If you - # find a slight drop in performance, you can set a larger - # `nms_pre` than before. - results = filter_scores_and_topk( - scores, cfg.score_thr, nms_pre, - dict(bbox_pred=bbox_pred, priors=priors)) - scores, labels, keep_idxs, filtered_results = results - - bbox_pred = filtered_results['bbox_pred'] - priors = filtered_results['priors'] - - if with_score_factors: - score_factor = score_factor[keep_idxs] - - bboxes = self.bbox_coder.decode( - priors, bbox_pred, max_shape=img_shape) - - mlvl_bboxes.append(bboxes) - mlvl_scores.append(scores) - mlvl_labels.append(labels) - if with_score_factors: - mlvl_score_factors.append(score_factor) - - return self._bbox_post_process(mlvl_scores, mlvl_labels, mlvl_bboxes, - img_meta['scale_factor'], cfg, rescale, - with_nms, mlvl_score_factors, **kwargs) - - def _bbox_post_process(self, - mlvl_scores, - mlvl_labels, - mlvl_bboxes, - scale_factor, - cfg, - rescale=False, - with_nms=True, - mlvl_score_factors=None, - **kwargs): - """bbox post-processing method. - - The boxes would be rescaled to the original image scale and do - the nms operation. Usually `with_nms` is False is used for aug test. - - Args: - mlvl_scores (list[Tensor]): Box scores from all scale - levels of a single image, each item has shape - (num_bboxes, ). - mlvl_labels (list[Tensor]): Box class labels from all scale - levels of a single image, each item has shape - (num_bboxes, ). - mlvl_bboxes (list[Tensor]): Decoded bboxes from all scale - levels of a single image, each item has shape (num_bboxes, 4). - scale_factor (ndarray, optional): Scale factor of the image arange - as (w_scale, h_scale, w_scale, h_scale). - cfg (mmcv.Config): Test / postprocessing configuration, - if None, test_cfg would be used. - rescale (bool): If True, return boxes in original image space. - Default: False. - with_nms (bool): If True, do nms before return boxes. - Default: True. - mlvl_score_factors (list[Tensor], optional): Score factor from - all scale levels of a single image, each item has shape - (num_bboxes, ). Default: None. - - Returns: - tuple[Tensor]: Results of detected bboxes and labels. If with_nms - is False and mlvl_score_factor is None, return mlvl_bboxes and - mlvl_scores, else return mlvl_bboxes, mlvl_scores and - mlvl_score_factor. Usually with_nms is False is used for aug - test. If with_nms is True, then return the following format - - - det_bboxes (Tensor): Predicted bboxes with shape \ - [num_bboxes, 5], where the first 4 columns are bounding \ - box positions (tl_x, tl_y, br_x, br_y) and the 5-th \ - column are scores between 0 and 1. - - det_labels (Tensor): Predicted labels of the corresponding \ - box with shape [num_bboxes]. - """ - assert len(mlvl_scores) == len(mlvl_bboxes) == len(mlvl_labels) - - mlvl_bboxes = torch.cat(mlvl_bboxes) - if rescale: - mlvl_bboxes /= mlvl_bboxes.new_tensor(scale_factor) - mlvl_scores = torch.cat(mlvl_scores) - mlvl_labels = torch.cat(mlvl_labels) - - if mlvl_score_factors is not None: - # TODO: Add sqrt operation in order to be consistent with - # the paper. - mlvl_score_factors = torch.cat(mlvl_score_factors) - mlvl_scores = mlvl_scores * mlvl_score_factors - - if with_nms: - if mlvl_bboxes.numel() == 0: - det_bboxes = torch.cat([mlvl_bboxes, mlvl_scores[:, None]], -1) - return det_bboxes, mlvl_labels - - det_bboxes, keep_idxs = batched_nms(mlvl_bboxes, mlvl_scores, - mlvl_labels, cfg.nms) - det_bboxes = det_bboxes[:cfg.max_per_img] - det_labels = mlvl_labels[keep_idxs][:cfg.max_per_img] - return det_bboxes, det_labels - else: - return mlvl_bboxes, mlvl_scores, mlvl_labels - - def forward_train(self, - x, - img_metas, - gt_bboxes, - gt_labels=None, - gt_bboxes_ignore=None, - proposal_cfg=None, - **kwargs): - """ - Args: - x (list[Tensor]): Features from FPN. - img_metas (list[dict]): Meta information of each image, e.g., - image size, scaling factor, etc. - gt_bboxes (Tensor): Ground truth bboxes of the image, - shape (num_gts, 4). - gt_labels (Tensor): Ground truth labels of each box, - shape (num_gts,). - gt_bboxes_ignore (Tensor): Ground truth bboxes to be - ignored, shape (num_ignored_gts, 4). - proposal_cfg (mmcv.Config): Test / postprocessing configuration, - if None, test_cfg would be used - - Returns: - tuple: - losses: (dict[str, Tensor]): A dictionary of loss components. - proposal_list (list[Tensor]): Proposals of each image. - """ - outs = self(x) - if gt_labels is None: - loss_inputs = outs + (gt_bboxes, img_metas) - else: - loss_inputs = outs + (gt_bboxes, gt_labels, img_metas) - losses = self.loss(*loss_inputs, gt_bboxes_ignore=gt_bboxes_ignore) - if proposal_cfg is None: - return losses - else: - proposal_list = self.get_bboxes( - *outs, img_metas=img_metas, cfg=proposal_cfg) - return losses, proposal_list - - def simple_test(self, feats, img_metas, rescale=False): - """Test function without test-time augmentation. - - Args: - feats (tuple[torch.Tensor]): Multi-level features from the - upstream network, each is a 4D-tensor. - img_metas (list[dict]): List of image information. - rescale (bool, optional): Whether to rescale the results. - Defaults to False. - - Returns: - list[tuple[Tensor, Tensor]]: Each item in result_list is 2-tuple. - The first item is ``bboxes`` with shape (n, 5), - where 5 represent (tl_x, tl_y, br_x, br_y, score). - The shape of the second tensor in the tuple is ``labels`` - with shape (n, ). - """ - return self.simple_test_bboxes(feats, img_metas, rescale=rescale) - - @force_fp32(apply_to=('cls_scores', 'bbox_preds')) - def onnx_export(self, - cls_scores, - bbox_preds, - score_factors=None, - img_metas=None, - with_nms=True): - """Transform network output for a batch into bbox predictions. - - Args: - cls_scores (list[Tensor]): Box scores for each scale level - with shape (N, num_points * num_classes, H, W). - bbox_preds (list[Tensor]): Box energies / deltas for each scale - level with shape (N, num_points * 4, H, W). - score_factors (list[Tensor]): score_factors for each s - cale level with shape (N, num_points * 1, H, W). - Default: None. - img_metas (list[dict]): Meta information of each image, e.g., - image size, scaling factor, etc. Default: None. - with_nms (bool): Whether apply nms to the bboxes. Default: True. - - Returns: - tuple[Tensor, Tensor] | list[tuple]: When `with_nms` is True, - it is tuple[Tensor, Tensor], first tensor bboxes with shape - [N, num_det, 5], 5 arrange as (x1, y1, x2, y2, score) - and second element is class labels of shape [N, num_det]. - When `with_nms` is False, first tensor is bboxes with - shape [N, num_det, 4], second tensor is raw score has - shape [N, num_det, num_classes]. - """ - assert len(cls_scores) == len(bbox_preds) - - num_levels = len(cls_scores) - - featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores] - mlvl_priors = self.prior_generator.grid_priors( - featmap_sizes, - dtype=bbox_preds[0].dtype, - device=bbox_preds[0].device) - - mlvl_cls_scores = [cls_scores[i].detach() for i in range(num_levels)] - mlvl_bbox_preds = [bbox_preds[i].detach() for i in range(num_levels)] - - assert len( - img_metas - ) == 1, 'Only support one input image while in exporting to ONNX' - img_shape = img_metas[0]['img_shape_for_onnx'] - - cfg = self.test_cfg - assert len(cls_scores) == len(bbox_preds) == len(mlvl_priors) - device = cls_scores[0].device - batch_size = cls_scores[0].shape[0] - # convert to tensor to keep tracing - nms_pre_tensor = torch.tensor( - cfg.get('nms_pre', -1), device=device, dtype=torch.long) - - # e.g. Retina, FreeAnchor, etc. - if score_factors is None: - with_score_factors = False - mlvl_score_factor = [None for _ in range(num_levels)] - else: - # e.g. FCOS, PAA, ATSS, etc. - with_score_factors = True - mlvl_score_factor = [ - score_factors[i].detach() for i in range(num_levels) - ] - mlvl_score_factors = [] - - mlvl_batch_bboxes = [] - mlvl_scores = [] - - for cls_score, bbox_pred, score_factors, priors in zip( - mlvl_cls_scores, mlvl_bbox_preds, mlvl_score_factor, - mlvl_priors): - assert cls_score.size()[-2:] == bbox_pred.size()[-2:] - - scores = cls_score.permute(0, 2, 3, - 1).reshape(batch_size, -1, - self.cls_out_channels) - if self.use_sigmoid_cls: - scores = scores.sigmoid() - nms_pre_score = scores - else: - scores = scores.softmax(-1) - nms_pre_score = scores - - if with_score_factors: - score_factors = score_factors.permute(0, 2, 3, 1).reshape( - batch_size, -1).sigmoid() - bbox_pred = bbox_pred.permute(0, 2, 3, - 1).reshape(batch_size, -1, 4) - priors = priors.expand(batch_size, -1, priors.size(-1)) - # Get top-k predictions - from mmdet.core.export import get_k_for_topk - nms_pre = get_k_for_topk(nms_pre_tensor, bbox_pred.shape[1]) - if nms_pre > 0: - - if with_score_factors: - nms_pre_score = (nms_pre_score * score_factors[..., None]) - else: - nms_pre_score = nms_pre_score - - # Get maximum scores for foreground classes. - if self.use_sigmoid_cls: - max_scores, _ = nms_pre_score.max(-1) - else: - # remind that we set FG labels to [0, num_class-1] - # since mmdet v2.0 - # BG cat_id: num_class - max_scores, _ = nms_pre_score[..., :-1].max(-1) - _, topk_inds = max_scores.topk(nms_pre) - - batch_inds = torch.arange( - batch_size, device=bbox_pred.device).view( - -1, 1).expand_as(topk_inds).long() - # Avoid onnx2tensorrt issue in https://github.com/NVIDIA/TensorRT/issues/1134 # noqa: E501 - transformed_inds = bbox_pred.shape[1] * batch_inds + topk_inds - priors = priors.reshape( - -1, priors.size(-1))[transformed_inds, :].reshape( - batch_size, -1, priors.size(-1)) - bbox_pred = bbox_pred.reshape(-1, - 4)[transformed_inds, :].reshape( - batch_size, -1, 4) - scores = scores.reshape( - -1, self.cls_out_channels)[transformed_inds, :].reshape( - batch_size, -1, self.cls_out_channels) - if with_score_factors: - score_factors = score_factors.reshape( - -1, 1)[transformed_inds].reshape(batch_size, -1) - - bboxes = self.bbox_coder.decode( - priors, bbox_pred, max_shape=img_shape) - - mlvl_batch_bboxes.append(bboxes) - mlvl_scores.append(scores) - if with_score_factors: - mlvl_score_factors.append(score_factors) - - batch_bboxes = torch.cat(mlvl_batch_bboxes, dim=1) - batch_scores = torch.cat(mlvl_scores, dim=1) - if with_score_factors: - batch_score_factors = torch.cat(mlvl_score_factors, dim=1) - - # Replace multiclass_nms with ONNX::NonMaxSuppression in deployment - - from mmdet.core.export import add_dummy_nms_for_onnx - - if not self.use_sigmoid_cls: - batch_scores = batch_scores[..., :self.num_classes] - - if with_score_factors: - batch_scores = batch_scores * (batch_score_factors.unsqueeze(2)) - - if with_nms: - max_output_boxes_per_class = cfg.nms.get( - 'max_output_boxes_per_class', 200) - iou_threshold = cfg.nms.get('iou_threshold', 0.5) - score_threshold = cfg.score_thr - nms_pre = cfg.get('deploy_nms_pre', -1) - return add_dummy_nms_for_onnx(batch_bboxes, batch_scores, - max_output_boxes_per_class, - iou_threshold, score_threshold, - nms_pre, cfg.max_per_img) - else: - return batch_bboxes, batch_scores diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/base_mask_head.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/base_mask_head.py deleted file mode 100644 index 5eb94fb28..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/base_mask_head.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -from mmcv.runner import BaseModule - - -class BaseMaskHead(BaseModule, metaclass=ABCMeta): - """Base class for mask heads used in One-Stage Instance Segmentation.""" - - def __init__(self, init_cfg): - super(BaseMaskHead, self).__init__(init_cfg) - - @abstractmethod - def loss(self, **kwargs): - pass - - @abstractmethod - def get_results(self, **kwargs): - """Get precessed :obj:`InstanceData` of multiple images.""" - pass - - def forward_train(self, - x, - gt_labels, - gt_masks, - img_metas, - gt_bboxes=None, - gt_bboxes_ignore=None, - positive_infos=None, - **kwargs): - """ - Args: - x (list[Tensor] | tuple[Tensor]): Features from FPN. - Each has a shape (B, C, H, W). - gt_labels (list[Tensor]): Ground truth labels of all images. - each has a shape (num_gts,). - gt_masks (list[Tensor]) : Masks for each bbox, has a shape - (num_gts, h , w). - img_metas (list[dict]): Meta information of each image, e.g., - image size, scaling factor, etc. - gt_bboxes (list[Tensor]): Ground truth bboxes of the image, - each item has a shape (num_gts, 4). - gt_bboxes_ignore (list[Tensor], None): Ground truth bboxes to be - ignored, each item has a shape (num_ignored_gts, 4). - positive_infos (list[:obj:`InstanceData`], optional): Information - of positive samples. Used when the label assignment is - done outside the MaskHead, e.g., in BboxHead in - YOLACT or CondInst, etc. When the label assignment is done in - MaskHead, it would be None, like SOLO. All values - in it should have shape (num_positive_samples, *). - - Returns: - dict[str, Tensor]: A dictionary of loss components. - """ - if positive_infos is None: - outs = self(x) - else: - outs = self(x, positive_infos) - - assert isinstance(outs, tuple), 'Forward results should be a tuple, ' \ - 'even if only one item is returned' - loss = self.loss( - *outs, - gt_labels=gt_labels, - gt_masks=gt_masks, - img_metas=img_metas, - gt_bboxes=gt_bboxes, - gt_bboxes_ignore=gt_bboxes_ignore, - positive_infos=positive_infos, - **kwargs) - return loss - - def simple_test(self, - feats, - img_metas, - rescale=False, - instances_list=None, - **kwargs): - """Test function without test-time augmentation. - - Args: - feats (tuple[torch.Tensor]): Multi-level features from the - upstream network, each is a 4D-tensor. - img_metas (list[dict]): List of image information. - rescale (bool, optional): Whether to rescale the results. - Defaults to False. - instances_list (list[obj:`InstanceData`], optional): Detection - results of each image after the post process. Only exist - if there is a `bbox_head`, like `YOLACT`, `CondInst`, etc. - - Returns: - list[obj:`InstanceData`]: Instance segmentation \ - results of each image after the post process. \ - Each item usually contains following keys. \ - - - scores (Tensor): Classification scores, has a shape - (num_instance,) - - labels (Tensor): Has a shape (num_instances,). - - masks (Tensor): Processed mask results, has a - shape (num_instances, h, w). - """ - if instances_list is None: - outs = self(feats) - else: - outs = self(feats, instances_list=instances_list) - mask_inputs = outs + (img_metas, ) - results_list = self.get_results( - *mask_inputs, - rescale=rescale, - instances_list=instances_list, - **kwargs) - return results_list - - def onnx_export(self, img, img_metas): - raise NotImplementedError(f'{self.__class__.__name__} does ' - f'not support ONNX EXPORT') diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/dense_test_mixins.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/dense_test_mixins.py deleted file mode 100644 index 342154895..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/dense_test_mixins.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from inspect import signature - -import torch -from mmcv.ops import batched_nms - -from mmdet.core import bbox_mapping_back, merge_aug_proposals - -if sys.version_info >= (3, 7): - from mmdet.utils.contextmanagers import completed - - -class BBoxTestMixin(object): - """Mixin class for testing det bboxes via DenseHead.""" - - def simple_test_bboxes(self, feats, img_metas, rescale=False): - """Test det bboxes without test-time augmentation, can be applied in - DenseHead except for ``RPNHead`` and its variants, e.g., ``GARPNHead``, - etc. - - Args: - feats (tuple[torch.Tensor]): Multi-level features from the - upstream network, each is a 4D-tensor. - img_metas (list[dict]): List of image information. - rescale (bool, optional): Whether to rescale the results. - Defaults to False. - - Returns: - list[tuple[Tensor, Tensor]]: Each item in result_list is 2-tuple. - The first item is ``bboxes`` with shape (n, 5), - where 5 represent (tl_x, tl_y, br_x, br_y, score). - The shape of the second tensor in the tuple is ``labels`` - with shape (n,) - """ - outs = self.forward(feats) - results_list = self.get_bboxes( - *outs, img_metas=img_metas, rescale=rescale) - return results_list - - def aug_test_bboxes(self, feats, img_metas, rescale=False): - """Test det bboxes with test time augmentation, can be applied in - DenseHead except for ``RPNHead`` and its variants, e.g., ``GARPNHead``, - etc. - - Args: - feats (list[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains features for all images in the batch. - img_metas (list[list[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. each dict has image information. - rescale (bool, optional): Whether to rescale the results. - Defaults to False. - - Returns: - list[tuple[Tensor, Tensor]]: Each item in result_list is 2-tuple. - The first item is ``bboxes`` with shape (n, 5), - where 5 represent (tl_x, tl_y, br_x, br_y, score). - The shape of the second tensor in the tuple is ``labels`` - with shape (n,). The length of list should always be 1. - """ - # check with_nms argument - gb_sig = signature(self.get_bboxes) - gb_args = [p.name for p in gb_sig.parameters.values()] - gbs_sig = signature(self._get_bboxes_single) - gbs_args = [p.name for p in gbs_sig.parameters.values()] - assert ('with_nms' in gb_args) and ('with_nms' in gbs_args), \ - f'{self.__class__.__name__}' \ - ' does not support test-time augmentation' - - aug_bboxes = [] - aug_scores = [] - aug_labels = [] - for x, img_meta in zip(feats, img_metas): - # only one image in the batch - outs = self.forward(x) - bbox_outputs = self.get_bboxes( - *outs, - img_metas=img_meta, - cfg=self.test_cfg, - rescale=False, - with_nms=False)[0] - aug_bboxes.append(bbox_outputs[0]) - aug_scores.append(bbox_outputs[1]) - if len(bbox_outputs) >= 3: - aug_labels.append(bbox_outputs[2]) - - # after merging, bboxes will be rescaled to the original image size - merged_bboxes, merged_scores = self.merge_aug_bboxes( - aug_bboxes, aug_scores, img_metas) - merged_labels = torch.cat(aug_labels, dim=0) if aug_labels else None - - if merged_bboxes.numel() == 0: - det_bboxes = torch.cat([merged_bboxes, merged_scores[:, None]], -1) - return [ - (det_bboxes, merged_labels), - ] - - det_bboxes, keep_idxs = batched_nms(merged_bboxes, merged_scores, - merged_labels, self.test_cfg.nms) - det_bboxes = det_bboxes[:self.test_cfg.max_per_img] - det_labels = merged_labels[keep_idxs][:self.test_cfg.max_per_img] - - if rescale: - _det_bboxes = det_bboxes - else: - _det_bboxes = det_bboxes.clone() - _det_bboxes[:, :4] *= det_bboxes.new_tensor( - img_metas[0][0]['scale_factor']) - - return [ - (_det_bboxes, det_labels), - ] - - def simple_test_rpn(self, x, img_metas): - """Test without augmentation, only for ``RPNHead`` and its variants, - e.g., ``GARPNHead``, etc. - - Args: - x (tuple[Tensor]): Features from the upstream network, each is - a 4D-tensor. - img_metas (list[dict]): Meta info of each image. - - Returns: - list[Tensor]: Proposals of each image, each item has shape (n, 5), - where 5 represent (tl_x, tl_y, br_x, br_y, score). - """ - rpn_outs = self(x) - proposal_list = self.get_bboxes(*rpn_outs, img_metas=img_metas) - return proposal_list - - def aug_test_rpn(self, feats, img_metas): - """Test with augmentation for only for ``RPNHead`` and its variants, - e.g., ``GARPNHead``, etc. - - Args: - feats (tuple[Tensor]): Features from the upstream network, each is - a 4D-tensor. - img_metas (list[dict]): Meta info of each image. - - Returns: - list[Tensor]: Proposals of each image, each item has shape (n, 5), - where 5 represent (tl_x, tl_y, br_x, br_y, score). - """ - samples_per_gpu = len(img_metas[0]) - aug_proposals = [[] for _ in range(samples_per_gpu)] - for x, img_meta in zip(feats, img_metas): - proposal_list = self.simple_test_rpn(x, img_meta) - for i, proposals in enumerate(proposal_list): - aug_proposals[i].append(proposals) - # reorganize the order of 'img_metas' to match the dimensions - # of 'aug_proposals' - aug_img_metas = [] - for i in range(samples_per_gpu): - aug_img_meta = [] - for j in range(len(img_metas)): - aug_img_meta.append(img_metas[j][i]) - aug_img_metas.append(aug_img_meta) - # after merging, proposals will be rescaled to the original image size - merged_proposals = [ - merge_aug_proposals(proposals, aug_img_meta, self.test_cfg) - for proposals, aug_img_meta in zip(aug_proposals, aug_img_metas) - ] - return merged_proposals - - if sys.version_info >= (3, 7): - - async def async_simple_test_rpn(self, x, img_metas): - sleep_interval = self.test_cfg.pop('async_sleep_interval', 0.025) - async with completed( - __name__, 'rpn_head_forward', - sleep_interval=sleep_interval): - rpn_outs = self(x) - - proposal_list = self.get_bboxes(*rpn_outs, img_metas=img_metas) - return proposal_list - - def merge_aug_bboxes(self, aug_bboxes, aug_scores, img_metas): - """Merge augmented detection bboxes and scores. - - Args: - aug_bboxes (list[Tensor]): shape (n, 4*#class) - aug_scores (list[Tensor] or None): shape (n, #class) - img_shapes (list[Tensor]): shape (3, ). - - Returns: - tuple[Tensor]: ``bboxes`` with shape (n,4), where - 4 represent (tl_x, tl_y, br_x, br_y) - and ``scores`` with shape (n,). - """ - recovered_bboxes = [] - for bboxes, img_info in zip(aug_bboxes, img_metas): - img_shape = img_info[0]['img_shape'] - scale_factor = img_info[0]['scale_factor'] - flip = img_info[0]['flip'] - flip_direction = img_info[0]['flip_direction'] - bboxes = bbox_mapping_back(bboxes, img_shape, scale_factor, flip, - flip_direction) - recovered_bboxes.append(bboxes) - bboxes = torch.cat(recovered_bboxes, dim=0) - if aug_scores is None: - return bboxes - else: - scores = torch.cat(aug_scores, dim=0) - return bboxes, scores diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/solo_head.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/solo_head.py deleted file mode 100644 index 148f819fa..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/dense_heads/solo_head.py +++ /dev/null @@ -1,1177 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule - -from mmdet.core import InstanceData, mask_matrix_nms, multi_apply -from mmdet.core.utils import center_of_mass, generate_coordinate -from mmdet.models.builder import HEADS, build_loss -from .base_mask_head import BaseMaskHead - - -@HEADS.register_module() -class SOLOHead(BaseMaskHead): - """SOLO mask head used in `SOLO: Segmenting Objects by Locations. - - `_ - - Args: - num_classes (int): Number of categories excluding the background - category. - in_channels (int): Number of channels in the input feature map. - feat_channels (int): Number of hidden channels. Used in child classes. - Default: 256. - stacked_convs (int): Number of stacking convs of the head. - Default: 4. - strides (tuple): Downsample factor of each feature map. - scale_ranges (tuple[tuple[int, int]]): Area range of multiple - level masks, in the format [(min1, max1), (min2, max2), ...]. - A range of (16, 64) means the area range between (16, 64). - pos_scale (float): Constant scale factor to control the center region. - num_grids (list[int]): Divided image into a uniform grids, each - feature map has a different grid value. The number of output - channels is grid ** 2. Default: [40, 36, 24, 16, 12]. - cls_down_index (int): The index of downsample operation in - classification branch. Default: 0. - loss_mask (dict): Config of mask loss. - loss_cls (dict): Config of classification loss. - norm_cfg (dict): dictionary to construct and config norm layer. - Default: norm_cfg=dict(type='GN', num_groups=32, - requires_grad=True). - train_cfg (dict): Training config of head. - test_cfg (dict): Testing config of head. - init_cfg (dict or list[dict], optional): Initialization config dict. - """ - - def __init__( - self, - num_classes, - in_channels, - feat_channels=256, - stacked_convs=4, - strides=(4, 8, 16, 32, 64), - scale_ranges=((8, 32), (16, 64), (32, 128), (64, 256), (128, 512)), - pos_scale=0.2, - num_grids=[40, 36, 24, 16, 12], - cls_down_index=0, - loss_mask=None, - loss_cls=None, - norm_cfg=dict(type='GN', num_groups=32, requires_grad=True), - train_cfg=None, - test_cfg=None, - init_cfg=[ - dict(type='Normal', layer='Conv2d', std=0.01), - dict( - type='Normal', - std=0.01, - bias_prob=0.01, - override=dict(name='conv_mask_list')), - dict( - type='Normal', - std=0.01, - bias_prob=0.01, - override=dict(name='conv_cls')) - ], - ): - super(SOLOHead, self).__init__(init_cfg) - self.num_classes = num_classes - self.cls_out_channels = self.num_classes - self.in_channels = in_channels - self.feat_channels = feat_channels - self.stacked_convs = stacked_convs - self.strides = strides - self.num_grids = num_grids - # number of FPN feats - self.num_levels = len(strides) - assert self.num_levels == len(scale_ranges) == len(num_grids) - self.scale_ranges = scale_ranges - self.pos_scale = pos_scale - - self.cls_down_index = cls_down_index - self.loss_cls = build_loss(loss_cls) - self.loss_mask = build_loss(loss_mask) - self.norm_cfg = norm_cfg - self.init_cfg = init_cfg - self.train_cfg = train_cfg - self.test_cfg = test_cfg - self._init_layers() - - def _init_layers(self): - self.mask_convs = nn.ModuleList() - self.cls_convs = nn.ModuleList() - for i in range(self.stacked_convs): - chn = self.in_channels + 2 if i == 0 else self.feat_channels - self.mask_convs.append( - ConvModule( - chn, - self.feat_channels, - 3, - stride=1, - padding=1, - norm_cfg=self.norm_cfg)) - chn = self.in_channels if i == 0 else self.feat_channels - self.cls_convs.append( - ConvModule( - chn, - self.feat_channels, - 3, - stride=1, - padding=1, - norm_cfg=self.norm_cfg)) - self.conv_mask_list = nn.ModuleList() - for num_grid in self.num_grids: - self.conv_mask_list.append( - nn.Conv2d(self.feat_channels, num_grid**2, 1)) - - self.conv_cls = nn.Conv2d( - self.feat_channels, self.cls_out_channels, 3, padding=1) - - def resize_feats(self, feats): - """Downsample the first feat and upsample last feat in feats.""" - out = [] - for i in range(len(feats)): - if i == 0: - out.append( - F.interpolate(feats[0], scale_factor=0.5, mode='bilinear')) - elif i == len(feats) - 1: - out.append( - F.interpolate( - feats[i], - size=feats[i - 1].shape[-2:], - mode='bilinear')) - else: - out.append(feats[i]) - return out - - def forward(self, feats): - assert len(feats) == self.num_levels - feats = self.resize_feats(feats) - mlvl_mask_preds = [] - mlvl_cls_preds = [] - for i in range(self.num_levels): - x = feats[i] - mask_feat = x - cls_feat = x - # generate and concat the coordinate - coord_feat = generate_coordinate(mask_feat.size(), - mask_feat.device) - mask_feat = torch.cat([mask_feat, coord_feat], 1) - - for mask_layer in (self.mask_convs): - mask_feat = mask_layer(mask_feat) - - mask_feat = F.interpolate( - mask_feat, scale_factor=2, mode='bilinear') - mask_pred = self.conv_mask_list[i](mask_feat) - - # cls branch - for j, cls_layer in enumerate(self.cls_convs): - if j == self.cls_down_index: - num_grid = self.num_grids[i] - cls_feat = F.interpolate( - cls_feat, size=num_grid, mode='bilinear') - cls_feat = cls_layer(cls_feat) - - cls_pred = self.conv_cls(cls_feat) - - if not self.training: - feat_wh = feats[0].size()[-2:] - upsampled_size = (feat_wh[0] * 2, feat_wh[1] * 2) - mask_pred = F.interpolate( - mask_pred.sigmoid(), size=upsampled_size, mode='bilinear') - cls_pred = cls_pred.sigmoid() - # get local maximum - local_max = F.max_pool2d(cls_pred, 2, stride=1, padding=1) - keep_mask = local_max[:, :, :-1, :-1] == cls_pred - cls_pred = cls_pred * keep_mask - - mlvl_mask_preds.append(mask_pred) - mlvl_cls_preds.append(cls_pred) - return mlvl_mask_preds, mlvl_cls_preds - - def loss(self, - mlvl_mask_preds, - mlvl_cls_preds, - gt_labels, - gt_masks, - img_metas, - gt_bboxes=None, - **kwargs): - """Calculate the loss of total batch. - - Args: - mlvl_mask_preds (list[Tensor]): Multi-level mask prediction. - Each element in the list has shape - (batch_size, num_grids**2 ,h ,w). - mlvl_cls_preds (list[Tensor]): Multi-level scores. Each element - in the list has shape - (batch_size, num_classes, num_grids ,num_grids). - gt_labels (list[Tensor]): Labels of multiple images. - gt_masks (list[Tensor]): Ground truth masks of multiple images. - Each has shape (num_instances, h, w). - img_metas (list[dict]): Meta information of multiple images. - gt_bboxes (list[Tensor]): Ground truth bboxes of multiple - images. Default: None. - - Returns: - dict[str, Tensor]: A dictionary of loss components. - """ - num_levels = self.num_levels - num_imgs = len(gt_labels) - - featmap_sizes = [featmap.size()[-2:] for featmap in mlvl_mask_preds] - - # `BoolTensor` in `pos_masks` represent - # whether the corresponding point is - # positive - pos_mask_targets, labels, pos_masks = multi_apply( - self._get_targets_single, - gt_bboxes, - gt_labels, - gt_masks, - featmap_sizes=featmap_sizes) - - # change from the outside list meaning multi images - # to the outside list meaning multi levels - mlvl_pos_mask_targets = [[] for _ in range(num_levels)] - mlvl_pos_mask_preds = [[] for _ in range(num_levels)] - mlvl_pos_masks = [[] for _ in range(num_levels)] - mlvl_labels = [[] for _ in range(num_levels)] - for img_id in range(num_imgs): - assert num_levels == len(pos_mask_targets[img_id]) - for lvl in range(num_levels): - mlvl_pos_mask_targets[lvl].append( - pos_mask_targets[img_id][lvl]) - mlvl_pos_mask_preds[lvl].append( - mlvl_mask_preds[lvl][img_id, pos_masks[img_id][lvl], ...]) - mlvl_pos_masks[lvl].append(pos_masks[img_id][lvl].flatten()) - mlvl_labels[lvl].append(labels[img_id][lvl].flatten()) - - # cat multiple image - temp_mlvl_cls_preds = [] - for lvl in range(num_levels): - mlvl_pos_mask_targets[lvl] = torch.cat( - mlvl_pos_mask_targets[lvl], dim=0) - mlvl_pos_mask_preds[lvl] = torch.cat( - mlvl_pos_mask_preds[lvl], dim=0) - mlvl_pos_masks[lvl] = torch.cat(mlvl_pos_masks[lvl], dim=0) - mlvl_labels[lvl] = torch.cat(mlvl_labels[lvl], dim=0) - temp_mlvl_cls_preds.append(mlvl_cls_preds[lvl].permute( - 0, 2, 3, 1).reshape(-1, self.cls_out_channels)) - - num_pos = sum(item.sum() for item in mlvl_pos_masks) - # dice loss - loss_mask = [] - for pred, target in zip(mlvl_pos_mask_preds, mlvl_pos_mask_targets): - if pred.size()[0] == 0: - loss_mask.append(pred.sum().unsqueeze(0)) - continue - loss_mask.append( - self.loss_mask(pred, target, reduction_override='none')) - if num_pos > 0: - loss_mask = torch.cat(loss_mask).sum() / num_pos - else: - loss_mask = torch.cat(loss_mask).mean() - - flatten_labels = torch.cat(mlvl_labels) - flatten_cls_preds = torch.cat(temp_mlvl_cls_preds) - loss_cls = self.loss_cls( - flatten_cls_preds, flatten_labels, avg_factor=num_pos + 1) - return dict(loss_mask=loss_mask, loss_cls=loss_cls) - - def _get_targets_single(self, - gt_bboxes, - gt_labels, - gt_masks, - featmap_sizes=None): - """Compute targets for predictions of single image. - - Args: - gt_bboxes (Tensor): Ground truth bbox of each instance, - shape (num_gts, 4). - gt_labels (Tensor): Ground truth label of each instance, - shape (num_gts,). - gt_masks (Tensor): Ground truth mask of each instance, - shape (num_gts, h, w). - featmap_sizes (list[:obj:`torch.size`]): Size of each - feature map from feature pyramid, each element - means (feat_h, feat_w). Default: None. - - Returns: - Tuple: Usually returns a tuple containing targets for predictions. - - - mlvl_pos_mask_targets (list[Tensor]): Each element represent - the binary mask targets for positive points in this - level, has shape (num_pos, out_h, out_w). - - mlvl_labels (list[Tensor]): Each element is - classification labels for all - points in this level, has shape - (num_grid, num_grid). - - mlvl_pos_masks (list[Tensor]): Each element is - a `BoolTensor` to represent whether the - corresponding point in single level - is positive, has shape (num_grid **2). - """ - device = gt_labels.device - gt_areas = torch.sqrt((gt_bboxes[:, 2] - gt_bboxes[:, 0]) * - (gt_bboxes[:, 3] - gt_bboxes[:, 1])) - - mlvl_pos_mask_targets = [] - mlvl_labels = [] - mlvl_pos_masks = [] - for (lower_bound, upper_bound), stride, featmap_size, num_grid \ - in zip(self.scale_ranges, self.strides, - featmap_sizes, self.num_grids): - - mask_target = torch.zeros( - [num_grid**2, featmap_size[0], featmap_size[1]], - dtype=torch.uint8, - device=device) - # FG cat_id: [0, num_classes -1], BG cat_id: num_classes - labels = torch.zeros([num_grid, num_grid], - dtype=torch.int64, - device=device) + self.num_classes - pos_mask = torch.zeros([num_grid**2], - dtype=torch.bool, - device=device) - - gt_inds = ((gt_areas >= lower_bound) & - (gt_areas <= upper_bound)).nonzero().flatten() - if len(gt_inds) == 0: - mlvl_pos_mask_targets.append( - mask_target.new_zeros(0, featmap_size[0], featmap_size[1])) - mlvl_labels.append(labels) - mlvl_pos_masks.append(pos_mask) - continue - hit_gt_bboxes = gt_bboxes[gt_inds] - hit_gt_labels = gt_labels[gt_inds] - hit_gt_masks = gt_masks[gt_inds, ...] - - pos_w_ranges = 0.5 * (hit_gt_bboxes[:, 2] - - hit_gt_bboxes[:, 0]) * self.pos_scale - pos_h_ranges = 0.5 * (hit_gt_bboxes[:, 3] - - hit_gt_bboxes[:, 1]) * self.pos_scale - - # Make sure hit_gt_masks has a value - valid_mask_flags = hit_gt_masks.sum(dim=-1).sum(dim=-1) > 0 - output_stride = stride / 2 - - for gt_mask, gt_label, pos_h_range, pos_w_range, \ - valid_mask_flag in \ - zip(hit_gt_masks, hit_gt_labels, pos_h_ranges, - pos_w_ranges, valid_mask_flags): - if not valid_mask_flag: - continue - upsampled_size = (featmap_sizes[0][0] * 4, - featmap_sizes[0][1] * 4) - center_h, center_w = center_of_mass(gt_mask) - - coord_w = int( - (center_w / upsampled_size[1]) // (1. / num_grid)) - coord_h = int( - (center_h / upsampled_size[0]) // (1. / num_grid)) - - # left, top, right, down - top_box = max( - 0, - int(((center_h - pos_h_range) / upsampled_size[0]) // - (1. / num_grid))) - down_box = min( - num_grid - 1, - int(((center_h + pos_h_range) / upsampled_size[0]) // - (1. / num_grid))) - left_box = max( - 0, - int(((center_w - pos_w_range) / upsampled_size[1]) // - (1. / num_grid))) - right_box = min( - num_grid - 1, - int(((center_w + pos_w_range) / upsampled_size[1]) // - (1. / num_grid))) - - top = max(top_box, coord_h - 1) - down = min(down_box, coord_h + 1) - left = max(coord_w - 1, left_box) - right = min(right_box, coord_w + 1) - - labels[top:(down + 1), left:(right + 1)] = gt_label - # ins - gt_mask = np.uint8(gt_mask.cpu().numpy()) - # Follow the original implementation, F.interpolate is - # different from cv2 and opencv - gt_mask = mmcv.imrescale(gt_mask, scale=1. / output_stride) - gt_mask = torch.from_numpy(gt_mask).to(device=device) - - for i in range(top, down + 1): - for j in range(left, right + 1): - index = int(i * num_grid + j) - mask_target[index, :gt_mask.shape[0], :gt_mask. - shape[1]] = gt_mask - pos_mask[index] = True - mlvl_pos_mask_targets.append(mask_target[pos_mask]) - mlvl_labels.append(labels) - mlvl_pos_masks.append(pos_mask) - return mlvl_pos_mask_targets, mlvl_labels, mlvl_pos_masks - - def get_results(self, mlvl_mask_preds, mlvl_cls_scores, img_metas, - **kwargs): - """Get multi-image mask results. - - Args: - mlvl_mask_preds (list[Tensor]): Multi-level mask prediction. - Each element in the list has shape - (batch_size, num_grids**2 ,h ,w). - mlvl_cls_scores (list[Tensor]): Multi-level scores. Each element - in the list has shape - (batch_size, num_classes, num_grids ,num_grids). - img_metas (list[dict]): Meta information of all images. - - Returns: - list[:obj:`InstanceData`]: Processed results of multiple - images.Each :obj:`InstanceData` usually contains - following keys. - - - scores (Tensor): Classification scores, has shape - (num_instance,). - - labels (Tensor): Has shape (num_instances,). - - masks (Tensor): Processed mask results, has - shape (num_instances, h, w). - """ - mlvl_cls_scores = [ - item.permute(0, 2, 3, 1) for item in mlvl_cls_scores - ] - assert len(mlvl_mask_preds) == len(mlvl_cls_scores) - num_levels = len(mlvl_cls_scores) - - results_list = [] - for img_id in range(len(img_metas)): - cls_pred_list = [ - mlvl_cls_scores[lvl][img_id].view(-1, self.cls_out_channels) - for lvl in range(num_levels) - ] - mask_pred_list = [ - mlvl_mask_preds[lvl][img_id] for lvl in range(num_levels) - ] - - cls_pred_list = torch.cat(cls_pred_list, dim=0) - mask_pred_list = torch.cat(mask_pred_list, dim=0) - - results = self._get_results_single( - cls_pred_list, mask_pred_list, img_meta=img_metas[img_id]) - results_list.append(results) - - return results_list - - def _get_results_single(self, cls_scores, mask_preds, img_meta, cfg=None): - """Get processed mask related results of single image. - - Args: - cls_scores (Tensor): Classification score of all points - in single image, has shape (num_points, num_classes). - mask_preds (Tensor): Mask prediction of all points in - single image, has shape (num_points, feat_h, feat_w). - img_meta (dict): Meta information of corresponding image. - cfg (dict, optional): Config used in test phase. - Default: None. - - Returns: - :obj:`InstanceData`: Processed results of single image. - it usually contains following keys. - - - scores (Tensor): Classification scores, has shape - (num_instance,). - - labels (Tensor): Has shape (num_instances,). - - masks (Tensor): Processed mask results, has - shape (num_instances, h, w). - """ - - def empty_results(results, cls_scores): - """Generate a empty results.""" - results.scores = cls_scores.new_ones(0) - results.masks = cls_scores.new_zeros(0, *results.ori_shape[:2]) - results.labels = cls_scores.new_ones(0) - return results - - cfg = self.test_cfg if cfg is None else cfg - assert len(cls_scores) == len(mask_preds) - results = InstanceData(img_meta) - - featmap_size = mask_preds.size()[-2:] - - img_shape = results.img_shape - ori_shape = results.ori_shape - - h, w, _ = img_shape - upsampled_size = (featmap_size[0] * 4, featmap_size[1] * 4) - - score_mask = (cls_scores > cfg.score_thr) - cls_scores = cls_scores[score_mask] - if len(cls_scores) == 0: - return empty_results(results, cls_scores) - - inds = score_mask.nonzero() - cls_labels = inds[:, 1] - - # Filter the mask mask with an area is smaller than - # stride of corresponding feature level - lvl_interval = cls_labels.new_tensor(self.num_grids).pow(2).cumsum(0) - strides = cls_scores.new_ones(lvl_interval[-1]) - strides[:lvl_interval[0]] *= self.strides[0] - for lvl in range(1, self.num_levels): - strides[lvl_interval[lvl - - 1]:lvl_interval[lvl]] *= self.strides[lvl] - strides = strides[inds[:, 0]] - mask_preds = mask_preds[inds[:, 0]] - - masks = mask_preds > cfg.mask_thr - sum_masks = masks.sum((1, 2)).float() - keep = sum_masks > strides - if keep.sum() == 0: - return empty_results(results, cls_scores) - masks = masks[keep] - mask_preds = mask_preds[keep] - sum_masks = sum_masks[keep] - cls_scores = cls_scores[keep] - cls_labels = cls_labels[keep] - - # maskness. - mask_scores = (mask_preds * masks).sum((1, 2)) / sum_masks - cls_scores *= mask_scores - - scores, labels, _, keep_inds = mask_matrix_nms( - masks, - cls_labels, - cls_scores, - mask_area=sum_masks, - nms_pre=cfg.nms_pre, - max_num=cfg.max_per_img, - kernel=cfg.kernel, - sigma=cfg.sigma, - filter_thr=cfg.filter_thr) - mask_preds = mask_preds[keep_inds] - mask_preds = F.interpolate( - mask_preds.unsqueeze(0), size=upsampled_size, - mode='bilinear')[:, :, :h, :w] - mask_preds = F.interpolate( - mask_preds, size=ori_shape[:2], mode='bilinear').squeeze(0) - masks = mask_preds > cfg.mask_thr - - results.masks = masks - results.labels = labels - results.scores = scores - - return results - - -@HEADS.register_module() -class DecoupledSOLOHead(SOLOHead): - """Decoupled SOLO mask head used in `SOLO: Segmenting Objects by Locations. - - `_ - - Args: - init_cfg (dict or list[dict], optional): Initialization config dict. - """ - - def __init__(self, - *args, - init_cfg=[ - dict(type='Normal', layer='Conv2d', std=0.01), - dict( - type='Normal', - std=0.01, - bias_prob=0.01, - override=dict(name='conv_mask_list_x')), - dict( - type='Normal', - std=0.01, - bias_prob=0.01, - override=dict(name='conv_mask_list_y')), - dict( - type='Normal', - std=0.01, - bias_prob=0.01, - override=dict(name='conv_cls')) - ], - **kwargs): - super(DecoupledSOLOHead, self).__init__( - *args, init_cfg=init_cfg, **kwargs) - - def _init_layers(self): - self.mask_convs_x = nn.ModuleList() - self.mask_convs_y = nn.ModuleList() - self.cls_convs = nn.ModuleList() - - for i in range(self.stacked_convs): - chn = self.in_channels + 1 if i == 0 else self.feat_channels - self.mask_convs_x.append( - ConvModule( - chn, - self.feat_channels, - 3, - stride=1, - padding=1, - norm_cfg=self.norm_cfg)) - self.mask_convs_y.append( - ConvModule( - chn, - self.feat_channels, - 3, - stride=1, - padding=1, - norm_cfg=self.norm_cfg)) - - chn = self.in_channels if i == 0 else self.feat_channels - self.cls_convs.append( - ConvModule( - chn, - self.feat_channels, - 3, - stride=1, - padding=1, - norm_cfg=self.norm_cfg)) - - self.conv_mask_list_x = nn.ModuleList() - self.conv_mask_list_y = nn.ModuleList() - for num_grid in self.num_grids: - self.conv_mask_list_x.append( - nn.Conv2d(self.feat_channels, num_grid, 3, padding=1)) - self.conv_mask_list_y.append( - nn.Conv2d(self.feat_channels, num_grid, 3, padding=1)) - self.conv_cls = nn.Conv2d( - self.feat_channels, self.cls_out_channels, 3, padding=1) - - def forward(self, feats): - assert len(feats) == self.num_levels - feats = self.resize_feats(feats) - mask_preds_x = [] - mask_preds_y = [] - cls_preds = [] - for i in range(self.num_levels): - x = feats[i] - mask_feat = x - cls_feat = x - # generate and concat the coordinate - coord_feat = generate_coordinate(mask_feat.size(), - mask_feat.device) - mask_feat_x = torch.cat([mask_feat, coord_feat[:, 0:1, ...]], 1) - mask_feat_y = torch.cat([mask_feat, coord_feat[:, 1:2, ...]], 1) - - for mask_layer_x, mask_layer_y in \ - zip(self.mask_convs_x, self.mask_convs_y): - mask_feat_x = mask_layer_x(mask_feat_x) - mask_feat_y = mask_layer_y(mask_feat_y) - - mask_feat_x = F.interpolate( - mask_feat_x, scale_factor=2, mode='bilinear') - mask_feat_y = F.interpolate( - mask_feat_y, scale_factor=2, mode='bilinear') - - mask_pred_x = self.conv_mask_list_x[i](mask_feat_x) - mask_pred_y = self.conv_mask_list_y[i](mask_feat_y) - - # cls branch - for j, cls_layer in enumerate(self.cls_convs): - if j == self.cls_down_index: - num_grid = self.num_grids[i] - cls_feat = F.interpolate( - cls_feat, size=num_grid, mode='bilinear') - cls_feat = cls_layer(cls_feat) - - cls_pred = self.conv_cls(cls_feat) - - if not self.training: - feat_wh = feats[0].size()[-2:] - upsampled_size = (feat_wh[0] * 2, feat_wh[1] * 2) - mask_pred_x = F.interpolate( - mask_pred_x.sigmoid(), - size=upsampled_size, - mode='bilinear') - mask_pred_y = F.interpolate( - mask_pred_y.sigmoid(), - size=upsampled_size, - mode='bilinear') - cls_pred = cls_pred.sigmoid() - # get local maximum - local_max = F.max_pool2d(cls_pred, 2, stride=1, padding=1) - keep_mask = local_max[:, :, :-1, :-1] == cls_pred - cls_pred = cls_pred * keep_mask - - mask_preds_x.append(mask_pred_x) - mask_preds_y.append(mask_pred_y) - cls_preds.append(cls_pred) - return mask_preds_x, mask_preds_y, cls_preds - - def loss(self, - mlvl_mask_preds_x, - mlvl_mask_preds_y, - mlvl_cls_preds, - gt_labels, - gt_masks, - img_metas, - gt_bboxes=None, - **kwargs): - """Calculate the loss of total batch. - - Args: - mlvl_mask_preds_x (list[Tensor]): Multi-level mask prediction - from x branch. Each element in the list has shape - (batch_size, num_grids ,h ,w). - mlvl_mask_preds_x (list[Tensor]): Multi-level mask prediction - from y branch. Each element in the list has shape - (batch_size, num_grids ,h ,w). - mlvl_cls_preds (list[Tensor]): Multi-level scores. Each element - in the list has shape - (batch_size, num_classes, num_grids ,num_grids). - gt_labels (list[Tensor]): Labels of multiple images. - gt_masks (list[Tensor]): Ground truth masks of multiple images. - Each has shape (num_instances, h, w). - img_metas (list[dict]): Meta information of multiple images. - gt_bboxes (list[Tensor]): Ground truth bboxes of multiple - images. Default: None. - - Returns: - dict[str, Tensor]: A dictionary of loss components. - """ - num_levels = self.num_levels - num_imgs = len(gt_labels) - featmap_sizes = [featmap.size()[-2:] for featmap in mlvl_mask_preds_x] - - pos_mask_targets, labels, \ - xy_pos_indexes = \ - multi_apply(self._get_targets_single, - gt_bboxes, - gt_labels, - gt_masks, - featmap_sizes=featmap_sizes) - - # change from the outside list meaning multi images - # to the outside list meaning multi levels - mlvl_pos_mask_targets = [[] for _ in range(num_levels)] - mlvl_pos_mask_preds_x = [[] for _ in range(num_levels)] - mlvl_pos_mask_preds_y = [[] for _ in range(num_levels)] - mlvl_labels = [[] for _ in range(num_levels)] - for img_id in range(num_imgs): - - for lvl in range(num_levels): - mlvl_pos_mask_targets[lvl].append( - pos_mask_targets[img_id][lvl]) - mlvl_pos_mask_preds_x[lvl].append( - mlvl_mask_preds_x[lvl][img_id, - xy_pos_indexes[img_id][lvl][:, 1]]) - mlvl_pos_mask_preds_y[lvl].append( - mlvl_mask_preds_y[lvl][img_id, - xy_pos_indexes[img_id][lvl][:, 0]]) - mlvl_labels[lvl].append(labels[img_id][lvl].flatten()) - - # cat multiple image - temp_mlvl_cls_preds = [] - for lvl in range(num_levels): - mlvl_pos_mask_targets[lvl] = torch.cat( - mlvl_pos_mask_targets[lvl], dim=0) - mlvl_pos_mask_preds_x[lvl] = torch.cat( - mlvl_pos_mask_preds_x[lvl], dim=0) - mlvl_pos_mask_preds_y[lvl] = torch.cat( - mlvl_pos_mask_preds_y[lvl], dim=0) - mlvl_labels[lvl] = torch.cat(mlvl_labels[lvl], dim=0) - temp_mlvl_cls_preds.append(mlvl_cls_preds[lvl].permute( - 0, 2, 3, 1).reshape(-1, self.cls_out_channels)) - - num_pos = 0. - # dice loss - loss_mask = [] - for pred_x, pred_y, target in \ - zip(mlvl_pos_mask_preds_x, - mlvl_pos_mask_preds_y, mlvl_pos_mask_targets): - num_masks = pred_x.size(0) - if num_masks == 0: - # make sure can get grad - loss_mask.append((pred_x.sum() + pred_y.sum()).unsqueeze(0)) - continue - num_pos += num_masks - pred_mask = pred_y.sigmoid() * pred_x.sigmoid() - loss_mask.append( - self.loss_mask(pred_mask, target, reduction_override='none')) - if num_pos > 0: - loss_mask = torch.cat(loss_mask).sum() / num_pos - else: - loss_mask = torch.cat(loss_mask).mean() - - # cate - flatten_labels = torch.cat(mlvl_labels) - flatten_cls_preds = torch.cat(temp_mlvl_cls_preds) - - loss_cls = self.loss_cls( - flatten_cls_preds, flatten_labels, avg_factor=num_pos + 1) - return dict(loss_mask=loss_mask, loss_cls=loss_cls) - - def _get_targets_single(self, - gt_bboxes, - gt_labels, - gt_masks, - featmap_sizes=None): - """Compute targets for predictions of single image. - - Args: - gt_bboxes (Tensor): Ground truth bbox of each instance, - shape (num_gts, 4). - gt_labels (Tensor): Ground truth label of each instance, - shape (num_gts,). - gt_masks (Tensor): Ground truth mask of each instance, - shape (num_gts, h, w). - featmap_sizes (list[:obj:`torch.size`]): Size of each - feature map from feature pyramid, each element - means (feat_h, feat_w). Default: None. - - Returns: - Tuple: Usually returns a tuple containing targets for predictions. - - - mlvl_pos_mask_targets (list[Tensor]): Each element represent - the binary mask targets for positive points in this - level, has shape (num_pos, out_h, out_w). - - mlvl_labels (list[Tensor]): Each element is - classification labels for all - points in this level, has shape - (num_grid, num_grid). - - mlvl_xy_pos_indexes (list[Tensor]): Each element - in the list contains the index of positive samples in - corresponding level, has shape (num_pos, 2), last - dimension 2 present (index_x, index_y). - """ - mlvl_pos_mask_targets, mlvl_labels, \ - mlvl_pos_masks = \ - super()._get_targets_single(gt_bboxes, gt_labels, gt_masks, - featmap_sizes=featmap_sizes) - - mlvl_xy_pos_indexes = [(item - self.num_classes).nonzero() - for item in mlvl_labels] - - return mlvl_pos_mask_targets, mlvl_labels, mlvl_xy_pos_indexes - - def get_results(self, - mlvl_mask_preds_x, - mlvl_mask_preds_y, - mlvl_cls_scores, - img_metas, - rescale=None, - **kwargs): - """Get multi-image mask results. - - Args: - mlvl_mask_preds_x (list[Tensor]): Multi-level mask prediction - from x branch. Each element in the list has shape - (batch_size, num_grids ,h ,w). - mlvl_mask_preds_y (list[Tensor]): Multi-level mask prediction - from y branch. Each element in the list has shape - (batch_size, num_grids ,h ,w). - mlvl_cls_scores (list[Tensor]): Multi-level scores. Each element - in the list has shape - (batch_size, num_classes ,num_grids ,num_grids). - img_metas (list[dict]): Meta information of all images. - - Returns: - list[:obj:`InstanceData`]: Processed results of multiple - images.Each :obj:`InstanceData` usually contains - following keys. - - - scores (Tensor): Classification scores, has shape - (num_instance,). - - labels (Tensor): Has shape (num_instances,). - - masks (Tensor): Processed mask results, has - shape (num_instances, h, w). - """ - mlvl_cls_scores = [ - item.permute(0, 2, 3, 1) for item in mlvl_cls_scores - ] - assert len(mlvl_mask_preds_x) == len(mlvl_cls_scores) - num_levels = len(mlvl_cls_scores) - - results_list = [] - for img_id in range(len(img_metas)): - cls_pred_list = [ - mlvl_cls_scores[i][img_id].view( - -1, self.cls_out_channels).detach() - for i in range(num_levels) - ] - mask_pred_list_x = [ - mlvl_mask_preds_x[i][img_id] for i in range(num_levels) - ] - mask_pred_list_y = [ - mlvl_mask_preds_y[i][img_id] for i in range(num_levels) - ] - - cls_pred_list = torch.cat(cls_pred_list, dim=0) - mask_pred_list_x = torch.cat(mask_pred_list_x, dim=0) - mask_pred_list_y = torch.cat(mask_pred_list_y, dim=0) - - results = self._get_results_single( - cls_pred_list, - mask_pred_list_x, - mask_pred_list_y, - img_meta=img_metas[img_id], - cfg=self.test_cfg) - results_list.append(results) - return results_list - - def _get_results_single(self, cls_scores, mask_preds_x, mask_preds_y, - img_meta, cfg): - """Get processed mask related results of single image. - - Args: - cls_scores (Tensor): Classification score of all points - in single image, has shape (num_points, num_classes). - mask_preds_x (Tensor): Mask prediction of x branch of - all points in single image, has shape - (sum_num_grids, feat_h, feat_w). - mask_preds_y (Tensor): Mask prediction of y branch of - all points in single image, has shape - (sum_num_grids, feat_h, feat_w). - img_meta (dict): Meta information of corresponding image. - cfg (dict): Config used in test phase. - - Returns: - :obj:`InstanceData`: Processed results of single image. - it usually contains following keys. - - - scores (Tensor): Classification scores, has shape - (num_instance,). - - labels (Tensor): Has shape (num_instances,). - - masks (Tensor): Processed mask results, has - shape (num_instances, h, w). - """ - - def empty_results(results, cls_scores): - """Generate a empty results.""" - results.scores = cls_scores.new_ones(0) - results.masks = cls_scores.new_zeros(0, *results.ori_shape[:2]) - results.labels = cls_scores.new_ones(0) - return results - - cfg = self.test_cfg if cfg is None else cfg - - results = InstanceData(img_meta) - img_shape = results.img_shape - ori_shape = results.ori_shape - h, w, _ = img_shape - featmap_size = mask_preds_x.size()[-2:] - upsampled_size = (featmap_size[0] * 4, featmap_size[1] * 4) - - score_mask = (cls_scores > cfg.score_thr) - cls_scores = cls_scores[score_mask] - inds = score_mask.nonzero() - lvl_interval = inds.new_tensor(self.num_grids).pow(2).cumsum(0) - num_all_points = lvl_interval[-1] - lvl_start_index = inds.new_ones(num_all_points) - num_grids = inds.new_ones(num_all_points) - seg_size = inds.new_tensor(self.num_grids).cumsum(0) - mask_lvl_start_index = inds.new_ones(num_all_points) - strides = inds.new_ones(num_all_points) - - lvl_start_index[:lvl_interval[0]] *= 0 - mask_lvl_start_index[:lvl_interval[0]] *= 0 - num_grids[:lvl_interval[0]] *= self.num_grids[0] - strides[:lvl_interval[0]] *= self.strides[0] - - for lvl in range(1, self.num_levels): - lvl_start_index[lvl_interval[lvl - 1]:lvl_interval[lvl]] *= \ - lvl_interval[lvl - 1] - mask_lvl_start_index[lvl_interval[lvl - 1]:lvl_interval[lvl]] *= \ - seg_size[lvl - 1] - num_grids[lvl_interval[lvl - 1]:lvl_interval[lvl]] *= \ - self.num_grids[lvl] - strides[lvl_interval[lvl - 1]:lvl_interval[lvl]] *= \ - self.strides[lvl] - - lvl_start_index = lvl_start_index[inds[:, 0]] - mask_lvl_start_index = mask_lvl_start_index[inds[:, 0]] - num_grids = num_grids[inds[:, 0]] - strides = strides[inds[:, 0]] - - y_lvl_offset = (inds[:, 0] - lvl_start_index) // num_grids - x_lvl_offset = (inds[:, 0] - lvl_start_index) % num_grids - y_inds = mask_lvl_start_index + y_lvl_offset - x_inds = mask_lvl_start_index + x_lvl_offset - - cls_labels = inds[:, 1] - mask_preds = mask_preds_x[x_inds, ...] * mask_preds_y[y_inds, ...] - - masks = mask_preds > cfg.mask_thr - sum_masks = masks.sum((1, 2)).float() - keep = sum_masks > strides - if keep.sum() == 0: - return empty_results(results, cls_scores) - - masks = masks[keep] - mask_preds = mask_preds[keep] - sum_masks = sum_masks[keep] - cls_scores = cls_scores[keep] - cls_labels = cls_labels[keep] - - # maskness. - mask_scores = (mask_preds * masks).sum((1, 2)) / sum_masks - cls_scores *= mask_scores - - scores, labels, _, keep_inds = mask_matrix_nms( - masks, - cls_labels, - cls_scores, - mask_area=sum_masks, - nms_pre=cfg.nms_pre, - max_num=cfg.max_per_img, - kernel=cfg.kernel, - sigma=cfg.sigma, - filter_thr=cfg.filter_thr) - mask_preds = mask_preds[keep_inds] - mask_preds = F.interpolate( - mask_preds.unsqueeze(0), size=upsampled_size, - mode='bilinear')[:, :, :h, :w] - mask_preds = F.interpolate( - mask_preds, size=ori_shape[:2], mode='bilinear').squeeze(0) - masks = mask_preds > cfg.mask_thr - - results.masks = masks - results.labels = labels - results.scores = scores - - return results - - -@HEADS.register_module() -class DecoupledSOLOLightHead(DecoupledSOLOHead): - """Decoupled Light SOLO mask head used in `SOLO: Segmenting Objects by - Locations `_ - - Args: - with_dcn (bool): Whether use dcn in mask_convs and cls_convs, - default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - """ - - def __init__(self, - *args, - dcn_cfg=None, - init_cfg=[ - dict(type='Normal', layer='Conv2d', std=0.01), - dict( - type='Normal', - std=0.01, - bias_prob=0.01, - override=dict(name='conv_mask_list_x')), - dict( - type='Normal', - std=0.01, - bias_prob=0.01, - override=dict(name='conv_mask_list_y')), - dict( - type='Normal', - std=0.01, - bias_prob=0.01, - override=dict(name='conv_cls')) - ], - **kwargs): - assert dcn_cfg is None or isinstance(dcn_cfg, dict) - self.dcn_cfg = dcn_cfg - super(DecoupledSOLOLightHead, self).__init__( - *args, init_cfg=init_cfg, **kwargs) - - def _init_layers(self): - self.mask_convs = nn.ModuleList() - self.cls_convs = nn.ModuleList() - - for i in range(self.stacked_convs): - if self.dcn_cfg is not None\ - and i == self.stacked_convs - 1: - conv_cfg = self.dcn_cfg - else: - conv_cfg = None - - chn = self.in_channels + 2 if i == 0 else self.feat_channels - self.mask_convs.append( - ConvModule( - chn, - self.feat_channels, - 3, - stride=1, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=self.norm_cfg)) - - chn = self.in_channels if i == 0 else self.feat_channels - self.cls_convs.append( - ConvModule( - chn, - self.feat_channels, - 3, - stride=1, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=self.norm_cfg)) - - self.conv_mask_list_x = nn.ModuleList() - self.conv_mask_list_y = nn.ModuleList() - for num_grid in self.num_grids: - self.conv_mask_list_x.append( - nn.Conv2d(self.feat_channels, num_grid, 3, padding=1)) - self.conv_mask_list_y.append( - nn.Conv2d(self.feat_channels, num_grid, 3, padding=1)) - self.conv_cls = nn.Conv2d( - self.feat_channels, self.cls_out_channels, 3, padding=1) - - def forward(self, feats): - assert len(feats) == self.num_levels - feats = self.resize_feats(feats) - mask_preds_x = [] - mask_preds_y = [] - cls_preds = [] - for i in range(self.num_levels): - x = feats[i] - mask_feat = x - cls_feat = x - # generate and concat the coordinate - coord_feat = generate_coordinate(mask_feat.size(), - mask_feat.device) - mask_feat = torch.cat([mask_feat, coord_feat], 1) - - for mask_layer in self.mask_convs: - mask_feat = mask_layer(mask_feat) - - mask_feat = F.interpolate( - mask_feat, scale_factor=2, mode='bilinear') - - mask_pred_x = self.conv_mask_list_x[i](mask_feat) - mask_pred_y = self.conv_mask_list_y[i](mask_feat) - - # cls branch - for j, cls_layer in enumerate(self.cls_convs): - if j == self.cls_down_index: - num_grid = self.num_grids[i] - cls_feat = F.interpolate( - cls_feat, size=num_grid, mode='bilinear') - cls_feat = cls_layer(cls_feat) - - cls_pred = self.conv_cls(cls_feat) - - if not self.training: - feat_wh = feats[0].size()[-2:] - upsampled_size = (feat_wh[0] * 2, feat_wh[1] * 2) - mask_pred_x = F.interpolate( - mask_pred_x.sigmoid(), - size=upsampled_size, - mode='bilinear') - mask_pred_y = F.interpolate( - mask_pred_y.sigmoid(), - size=upsampled_size, - mode='bilinear') - cls_pred = cls_pred.sigmoid() - # get local maximum - local_max = F.max_pool2d(cls_pred, 2, stride=1, padding=1) - keep_mask = local_max[:, :, :-1, :-1] == cls_pred - cls_pred = cls_pred * keep_mask - - mask_preds_x.append(mask_pred_x) - mask_preds_y.append(mask_pred_y) - cls_preds.append(cls_pred) - return mask_preds_x, mask_preds_y, cls_preds diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/__init__.py deleted file mode 100644 index 45b2597e3..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .solo import SOLO - -__all__ = ['SOLO'] - - - diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/base.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/base.py deleted file mode 100644 index 691a0c316..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/base.py +++ /dev/null @@ -1,360 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod -from collections import OrderedDict - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import BaseModule, auto_fp16 - -# from mmdet.core.visualization import imshow_det_bboxes - - -class BaseDetector(BaseModule, metaclass=ABCMeta): - """Base class for detectors.""" - - def __init__(self, init_cfg=None): - super(BaseDetector, self).__init__(init_cfg) - self.fp16_enabled = False - - @property - def with_neck(self): - """bool: whether the detector has a neck""" - return hasattr(self, 'neck') and self.neck is not None - - # TODO: these properties need to be carefully handled - # for both single stage & two stage detectors - @property - def with_shared_head(self): - """bool: whether the detector has a shared head in the RoI Head""" - return hasattr(self, 'roi_head') and self.roi_head.with_shared_head - - @property - def with_bbox(self): - """bool: whether the detector has a bbox head""" - return ((hasattr(self, 'roi_head') and self.roi_head.with_bbox) - or (hasattr(self, 'bbox_head') and self.bbox_head is not None)) - - @property - def with_mask(self): - """bool: whether the detector has a mask head""" - return ((hasattr(self, 'roi_head') and self.roi_head.with_mask) - or (hasattr(self, 'mask_head') and self.mask_head is not None)) - - @abstractmethod - def extract_feat(self, imgs): - """Extract features from images.""" - pass - - def extract_feats(self, imgs): - """Extract features from multiple images. - - Args: - imgs (list[torch.Tensor]): A list of images. The images are - augmented from the same image but in different ways. - - Returns: - list[torch.Tensor]: Features of different images - """ - assert isinstance(imgs, list) - return [self.extract_feat(img) for img in imgs] - - def forward_train(self, imgs, img_metas, **kwargs): - """ - Args: - img (Tensor): of shape (N, C, H, W) encoding input images. - Typically these should be mean centered and std scaled. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys, see - :class:`mmdet.datasets.pipelines.Collect`. - kwargs (keyword arguments): Specific to concrete implementation. - """ - # NOTE the batched image size information may be useful, e.g. - # in DETR, this is needed for the construction of masks, which is - # then used for the transformer_head. - batch_input_shape = tuple(imgs[0].size()[-2:]) - for img_meta in img_metas: - img_meta['batch_input_shape'] = batch_input_shape - - async def async_simple_test(self, img, img_metas, **kwargs): - raise NotImplementedError - - @abstractmethod - def simple_test(self, img, img_metas, **kwargs): - pass - - @abstractmethod - def aug_test(self, imgs, img_metas, **kwargs): - """Test function with test time augmentation.""" - pass - - async def aforward_test(self, *, img, img_metas, **kwargs): - for var, name in [(img, 'img'), (img_metas, 'img_metas')]: - if not isinstance(var, list): - raise TypeError(f'{name} must be a list, but got {type(var)}') - - num_augs = len(img) - if num_augs != len(img_metas): - raise ValueError(f'num of augmentations ({len(img)}) ' - f'!= num of image metas ({len(img_metas)})') - # TODO: remove the restriction of samples_per_gpu == 1 when prepared - samples_per_gpu = img[0].size(0) - assert samples_per_gpu == 1 - - if num_augs == 1: - return await self.async_simple_test(img[0], img_metas[0], **kwargs) - else: - raise NotImplementedError - - def forward_test(self, imgs, img_metas, **kwargs): - """ - Args: - imgs (List[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains all images in the batch. - img_metas (List[List[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. - """ - for var, name in [(imgs, 'imgs'), (img_metas, 'img_metas')]: - if not isinstance(var, list): - raise TypeError(f'{name} must be a list, but got {type(var)}') - - num_augs = len(imgs) - if num_augs != len(img_metas): - raise ValueError(f'num of augmentations ({len(imgs)}) ' - f'!= num of image meta ({len(img_metas)})') - - # NOTE the batched image size information may be useful, e.g. - # in DETR, this is needed for the construction of masks, which is - # then used for the transformer_head. - for img, img_meta in zip(imgs, img_metas): - batch_size = len(img_meta) - for img_id in range(batch_size): - img_meta[img_id]['batch_input_shape'] = tuple(img.size()[-2:]) - - if num_augs == 1: - # proposals (List[List[Tensor]]): the outer list indicates - # test-time augs (multiscale, flip, etc.) and the inner list - # indicates images in a batch. - # The Tensor should have a shape Px4, where P is the number of - # proposals. - if 'proposals' in kwargs: - kwargs['proposals'] = kwargs['proposals'][0] - return self.simple_test(imgs[0], img_metas[0], **kwargs) - else: - assert imgs[0].size(0) == 1, 'aug test does not support ' \ - 'inference with batch size ' \ - f'{imgs[0].size(0)}' - # TODO: support test augmentation for predefined proposals - assert 'proposals' not in kwargs - return self.aug_test(imgs, img_metas, **kwargs) - - @auto_fp16(apply_to=('img', )) - def forward(self, img, img_metas, return_loss=True, **kwargs): - """Calls either :func:`forward_train` or :func:`forward_test` depending - on whether ``return_loss`` is ``True``. - - Note this setting will change the expected inputs. When - ``return_loss=True``, img and img_meta are single-nested (i.e. Tensor - and List[dict]), and when ``resturn_loss=False``, img and img_meta - should be double nested (i.e. List[Tensor], List[List[dict]]), with - the outer list indicating test time augmentations. - """ - if torch.onnx.is_in_onnx_export(): - assert len(img_metas) == 1 - return self.onnx_export(img[0], img_metas[0]) - - if return_loss: - return self.forward_train(img, img_metas, **kwargs) - else: - return self.forward_test(img, img_metas, **kwargs) - - def _parse_losses(self, losses): - """Parse the raw outputs (losses) of the network. - - Args: - losses (dict): Raw output of the network, which usually contain - losses and other necessary information. - - Returns: - tuple[Tensor, dict]: (loss, log_vars), loss is the loss tensor \ - which may be a weighted sum of all losses, log_vars contains \ - all the variables to be sent to the logger. - """ - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(_value for _key, _value in log_vars.items() - if 'loss' in _key) - - # If the loss_vars has different length, GPUs will wait infinitely - if dist.is_available() and dist.is_initialized(): - log_var_length = torch.tensor(len(log_vars), device=loss.device) - dist.all_reduce(log_var_length) - message = (f'rank {dist.get_rank()}' + - f' len(log_vars): {len(log_vars)}' + ' keys: ' + - ','.join(log_vars.keys())) - assert log_var_length == len(log_vars) * dist.get_world_size(), \ - 'loss log variables are different across GPUs!\n' + message - - log_vars['loss'] = loss - for loss_name, loss_value in log_vars.items(): - # reduce loss when distributed training - if dist.is_available() and dist.is_initialized(): - loss_value = loss_value.data.clone() - dist.all_reduce(loss_value.div_(dist.get_world_size())) - log_vars[loss_name] = loss_value.item() - - return loss, log_vars - - def train_step(self, data, optimizer): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating is also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer` | dict): The optimizer of - runner is passed to ``train_step()``. This argument is unused - and reserved. - - Returns: - dict: It should contain at least 3 keys: ``loss``, ``log_vars``, \ - ``num_samples``. - - - ``loss`` is a tensor for back propagation, which can be a - weighted sum of multiple losses. - - ``log_vars`` contains all the variables to be sent to the - logger. - - ``num_samples`` indicates the batch size (when the model is - DDP, it means the batch size on each GPU), which is used for - averaging the logs. - """ - losses = self(**data) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, log_vars=log_vars, num_samples=len(data['img_metas'])) - - return outputs - - def val_step(self, data, optimizer=None): - """The iteration step during validation. - - This method shares the same signature as :func:`train_step`, but used - during val epochs. Note that the evaluation after training epochs is - not implemented with this method, but an evaluation hook. - """ - losses = self(**data) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, log_vars=log_vars, num_samples=len(data['img_metas'])) - - return outputs - - def show_result(self, - img, - result, - score_thr=0.3, - bbox_color=(72, 101, 241), - text_color=(72, 101, 241), - mask_color=None, - thickness=2, - font_size=13, - win_name='', - show=False, - wait_time=0, - out_file=None): - """Draw `result` over `img`. - - Args: - img (str or Tensor): The image to be displayed. - result (Tensor or tuple): The results to draw over `img` - bbox_result or (bbox_result, segm_result). - score_thr (float, optional): Minimum score of bboxes to be shown. - Default: 0.3. - bbox_color (str or tuple(int) or :obj:`Color`):Color of bbox lines. - The tuple of color should be in BGR order. Default: 'green' - text_color (str or tuple(int) or :obj:`Color`):Color of texts. - The tuple of color should be in BGR order. Default: 'green' - mask_color (None or str or tuple(int) or :obj:`Color`): - Color of masks. The tuple of color should be in BGR order. - Default: None - thickness (int): Thickness of lines. Default: 2 - font_size (int): Font size of texts. Default: 13 - win_name (str): The window name. Default: '' - wait_time (float): Value of waitKey param. - Default: 0. - show (bool): Whether to show the image. - Default: False. - out_file (str or None): The filename to write the image. - Default: None. - - Returns: - img (Tensor): Only if not `show` or `out_file` - """ - img = mmcv.imread(img) - img = img.copy() - if isinstance(result, tuple): - bbox_result, segm_result = result - if isinstance(segm_result, tuple): - segm_result = segm_result[0] # ms rcnn - else: - bbox_result, segm_result = result, None - bboxes = np.vstack(bbox_result) - labels = [ - np.full(bbox.shape[0], i, dtype=np.int32) - for i, bbox in enumerate(bbox_result) - ] - labels = np.concatenate(labels) - # draw segmentation masks - segms = None - if segm_result is not None and len(labels) > 0: # non empty - segms = mmcv.concat_list(segm_result) - if isinstance(segms[0], torch.Tensor): - segms = torch.stack(segms, dim=0).detach().cpu().numpy() - else: - segms = np.stack(segms, axis=0) - # if out_file specified, do not show image in window - if out_file is not None: - show = False - # draw bounding boxes - # img = imshow_det_bboxes( - # img, - # bboxes, - # labels, - # segms, - # class_names=self.CLASSES, - # score_thr=score_thr, - # bbox_color=bbox_color, - # text_color=text_color, - # mask_color=mask_color, - # thickness=thickness, - # font_size=font_size, - # win_name=win_name, - # show=show, - # wait_time=wait_time, - # out_file=out_file) - - # if not (show or out_file): - # return img - - def onnx_export(self, img, img_metas): - raise NotImplementedError(f'{self.__class__.__name__} does ' - f'not support ONNX EXPORT') diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/single_stage_instance_seg.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/single_stage_instance_seg.py deleted file mode 100644 index 98d5328b0..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/single_stage_instance_seg.py +++ /dev/null @@ -1,343 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings - -import mmcv -import numpy as np -import torch - -from ..builder import DETECTORS, build_backbone, build_head, build_neck -from .base import BaseDetector - -INF = 1e8 - - -@DETECTORS.register_module() -class SingleStageInstanceSegmentor(BaseDetector): - """Base class for single-stage instance segmentors.""" - - def __init__(self, - backbone, - neck=None, - bbox_head=None, - mask_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - - if pretrained: - warnings.warn('DeprecationWarning: pretrained is deprecated, ' - 'please use "init_cfg" instead') - backbone.pretrained = pretrained - super(SingleStageInstanceSegmentor, self).__init__(init_cfg=init_cfg) - self.backbone = build_backbone(backbone) - if neck is not None: - self.neck = build_neck(neck) - else: - self.neck = None - if bbox_head is not None: - bbox_head.update(train_cfg=copy.deepcopy(train_cfg)) - bbox_head.update(test_cfg=copy.deepcopy(test_cfg)) - self.bbox_head = build_head(bbox_head) - else: - self.bbox_head = None - - assert mask_head, f'`mask_head` must ' \ - f'be implemented in {self.__class__.__name__}' - mask_head.update(train_cfg=copy.deepcopy(train_cfg)) - mask_head.update(test_cfg=copy.deepcopy(test_cfg)) - self.mask_head = build_head(mask_head) - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - def extract_feat(self, img): - """Directly extract features from the backbone and neck.""" - x = self.backbone(img) - if self.with_neck: - x = self.neck(x) - return x - - def forward_dummy(self, img): - """Used for computing network flops. - - See `mmdetection/tools/analysis_tools/get_flops.py` - """ - raise NotImplementedError( - f'`forward_dummy` is not implemented in {self.__class__.__name__}') - - def forward_train(self, - img, - img_metas, - gt_masks, - gt_labels, - gt_bboxes=None, - gt_bboxes_ignore=None, - **kwargs): - """ - Args: - img (Tensor): Input images of shape (B, C, H, W). - Typically these should be mean centered and std scaled. - img_metas (list[dict]): A List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - :class:`mmdet.datasets.pipelines.Collect`. - gt_masks (list[:obj:`BitmapMasks`] | None) : The segmentation - masks for each box. - gt_labels (list[Tensor]): Class indices corresponding to each box - gt_bboxes (list[Tensor]): Each item is the truth boxes - of each image in [tl_x, tl_y, br_x, br_y] format. - Default: None. - gt_bboxes_ignore (list[Tensor] | None): Specify which bounding - boxes can be ignored when computing the loss. - - Returns: - dict[str, Tensor]: A dictionary of loss components. - """ - - gt_masks = [ - gt_mask.to_tensor(dtype=torch.bool, device=img.device) - for gt_mask in gt_masks - ] - x = self.extract_feat(img) - losses = dict() - - # CondInst and YOLACT have bbox_head - if self.bbox_head: - # bbox_head_preds is a tuple - bbox_head_preds = self.bbox_head(x) - # positive_infos is a list of obj:`InstanceData` - # It contains the information about the positive samples - # CondInst, YOLACT - det_losses, positive_infos = self.bbox_head.loss( - *bbox_head_preds, - gt_bboxes=gt_bboxes, - gt_labels=gt_labels, - gt_masks=gt_masks, - img_metas=img_metas, - gt_bboxes_ignore=gt_bboxes_ignore, - **kwargs) - losses.update(det_losses) - else: - positive_infos = None - - mask_loss = self.mask_head.forward_train( - x, - gt_labels, - gt_masks, - img_metas, - positive_infos=positive_infos, - gt_bboxes=gt_bboxes, - gt_bboxes_ignore=gt_bboxes_ignore, - **kwargs) - # avoid loss override - assert not set(mask_loss.keys()) & set(losses.keys()) - - losses.update(mask_loss) - return losses - - def simple_test(self, img, img_metas, rescale=False): - """Test function without test-time augmentation. - - Args: - img (torch.Tensor): Images with shape (B, C, H, W). - img_metas (list[dict]): List of image information. - rescale (bool, optional): Whether to rescale the results. - Defaults to False. - - Returns: - list(tuple): Formatted bbox and mask results of multiple \ - images. The outer list corresponds to each image. \ - Each tuple contains two type of results of single image: - - - bbox_results (list[np.ndarray]): BBox results of - single image. The list corresponds to each class. - each ndarray has a shape (N, 5), N is the number of - bboxes with this category, and last dimension - 5 arrange as (x1, y1, x2, y2, scores). - - mask_results (list[np.ndarray]): Mask results of - single image. The list corresponds to each class. - each ndarray has a shape (N, img_h, img_w), N - is the number of masks with this category. - """ - feat = self.extract_feat(img) - if self.bbox_head: - outs = self.bbox_head(feat) - # results_list is list[obj:`InstanceData`] - results_list = self.bbox_head.get_results( - *outs, img_metas=img_metas, cfg=self.test_cfg, rescale=rescale) - else: - results_list = None - - results_list = self.mask_head.simple_test( - feat, img_metas, rescale=rescale, instances_list=results_list) - - format_results_list = [] - for results in results_list: - format_results_list.append(self.format_results(results)) - - return format_results_list - - def format_results(self, results): - """Format the model predictions according to the interface with - dataset. - - Args: - results (:obj:`InstanceData`): Processed - results of single images. Usually contains - following keys. - - - scores (Tensor): Classification scores, has shape - (num_instance,) - - labels (Tensor): Has shape (num_instances,). - - masks (Tensor): Processed mask results, has - shape (num_instances, h, w). - - Returns: - tuple: Formatted bbox and mask results.. It contains two items: - - - bbox_results (list[np.ndarray]): BBox results of - single image. The list corresponds to each class. - each ndarray has a shape (N, 5), N is the number of - bboxes with this category, and last dimension - 5 arrange as (x1, y1, x2, y2, scores). - - mask_results (list[np.ndarray]): Mask results of - single image. The list corresponds to each class. - each ndarray has shape (N, img_h, img_w), N - is the number of masks with this category. - """ - data_keys = results.keys() - assert 'scores' in data_keys - assert 'labels' in data_keys - - assert 'masks' in data_keys, \ - 'results should contain ' \ - 'masks when format the results ' - mask_results = [[] for _ in range(self.mask_head.num_classes)] - - num_masks = len(results) - - if num_masks == 0: - bbox_results = [ - np.zeros((0, 5), dtype=np.float32) - for _ in range(self.mask_head.num_classes) - ] - return bbox_results, mask_results - - labels = results.labels.detach().cpu().numpy() - - if 'bboxes' not in results: - # create dummy bbox results to store the scores - results.bboxes = results.scores.new_zeros(len(results), 4) - - det_bboxes = torch.cat([results.bboxes, results.scores[:, None]], - dim=-1) - det_bboxes = det_bboxes.detach().cpu().numpy() - bbox_results = [ - det_bboxes[labels == i, :] - for i in range(self.mask_head.num_classes) - ] - - masks = results.masks.detach().cpu().numpy() - - for idx in range(num_masks): - mask = masks[idx] - mask_results[labels[idx]].append(mask) - - return bbox_results, mask_results - - def aug_test(self, imgs, img_metas, rescale=False): - raise NotImplementedError - - def show_result(self, - img, - result, - score_thr=0.3, - bbox_color=(72, 101, 241), - text_color=(72, 101, 241), - mask_color=None, - thickness=2, - font_size=13, - win_name='', - show=False, - wait_time=0, - out_file=None): - """Draw `result` over `img`. - - Args: - img (str or Tensor): The image to be displayed. - result (tuple): Format bbox and mask results. - It contains two items: - - - bbox_results (list[np.ndarray]): BBox results of - single image. The list corresponds to each class. - each ndarray has a shape (N, 5), N is the number of - bboxes with this category, and last dimension - 5 arrange as (x1, y1, x2, y2, scores). - - mask_results (list[np.ndarray]): Mask results of - single image. The list corresponds to each class. - each ndarray has shape (N, img_h, img_w), N - is the number of masks with this category. - - score_thr (float, optional): Minimum score of bboxes to be shown. - Default: 0.3. - bbox_color (str or tuple(int) or :obj:`Color`):Color of bbox lines. - The tuple of color should be in BGR order. Default: 'green' - text_color (str or tuple(int) or :obj:`Color`):Color of texts. - The tuple of color should be in BGR order. Default: 'green' - mask_color (None or str or tuple(int) or :obj:`Color`): - Color of masks. The tuple of color should be in BGR order. - Default: None - thickness (int): Thickness of lines. Default: 2 - font_size (int): Font size of texts. Default: 13 - win_name (str): The window name. Default: '' - wait_time (float): Value of waitKey param. - Default: 0. - show (bool): Whether to show the image. - Default: False. - out_file (str or None): The filename to write the image. - Default: None. - - Returns: - img (Tensor): Only if not `show` or `out_file` - """ - - assert isinstance(result, tuple) - bbox_result, mask_result = result - bboxes = np.vstack(bbox_result) - img = mmcv.imread(img) - img = img.copy() - labels = [ - np.full(bbox.shape[0], i, dtype=np.int32) - for i, bbox in enumerate(bbox_result) - ] - labels = np.concatenate(labels) - if len(labels) == 0: - bboxes = np.zeros([0, 5]) - masks = np.zeros([0, 0, 0]) - # draw segmentation masks - else: - masks = mmcv.concat_list(mask_result) - - if isinstance(masks[0], torch.Tensor): - masks = torch.stack(masks, dim=0).detach().cpu().numpy() - else: - masks = np.stack(masks, axis=0) - # dummy bboxes - if bboxes[:, :4].sum() == 0: - num_masks = len(bboxes) - x_any = masks.any(axis=1) - y_any = masks.any(axis=2) - for idx in range(num_masks): - x = np.where(x_any[idx, :])[0] - y = np.where(y_any[idx, :])[0] - if len(x) > 0 and len(y) > 0: - bboxes[idx, :4] = np.array( - [x[0], y[0], x[-1] + 1, y[-1] + 1], - dtype=np.float32) - - - if not (show or out_file): - return img diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/solo.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/solo.py deleted file mode 100644 index df6f6de01..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/detectors/solo.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..builder import DETECTORS -from .single_stage_instance_seg import SingleStageInstanceSegmentor - - -@DETECTORS.register_module() -class SOLO(SingleStageInstanceSegmentor): - """`SOLO: Segmenting Objects by Locations - `_ - - """ - - def __init__(self, - backbone, - neck=None, - bbox_head=None, - mask_head=None, - train_cfg=None, - test_cfg=None, - init_cfg=None, - pretrained=None): - super().__init__( - backbone=backbone, - neck=neck, - bbox_head=bbox_head, - mask_head=mask_head, - train_cfg=train_cfg, - test_cfg=test_cfg, - init_cfg=init_cfg, - pretrained=pretrained) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/__init__.py deleted file mode 100644 index 30016bb9c..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .accuracy import Accuracy, accuracy -from .focal_loss import FocalLoss, sigmoid_focal_loss -from .iou_loss import (BoundedIoULoss, CIoULoss, DIoULoss, GIoULoss, IoULoss, - bounded_iou_loss, iou_loss) -from .utils import reduce_loss, weight_reduce_loss, weighted_loss -from .dice_loss import DiceLoss - -__all__ = [ - 'accuracy', 'Accuracy', 'sigmoid_focal_loss', - 'FocalLoss', 'reduce_loss', 'weight_reduce_loss', 'weighted_loss', - 'iou_loss', 'bounded_iou_loss', - 'IoULoss', 'BoundedIoULoss', 'GIoULoss', 'DIoULoss', 'CIoULoss', - 'DiceLoss' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/accuracy.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/accuracy.py deleted file mode 100644 index fe765a39f..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/accuracy.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch.nn as nn - - -@mmcv.jit(coderize=True) -def accuracy(pred, target, topk=1, thresh=None): - """Calculate accuracy according to the prediction and target. - - Args: - pred (torch.Tensor): The model prediction, shape (N, num_class) - target (torch.Tensor): The target of each prediction, shape (N, ) - topk (int | tuple[int], optional): If the predictions in ``topk`` - matches the target, the predictions will be regarded as - correct ones. Defaults to 1. - thresh (float, optional): If not None, predictions with scores under - this threshold are considered incorrect. Default to None. - - Returns: - float | tuple[float]: If the input ``topk`` is a single integer, - the function will return a single float as accuracy. If - ``topk`` is a tuple containing multiple integers, the - function will return a tuple containing accuracies of - each ``topk`` number. - """ - assert isinstance(topk, (int, tuple)) - if isinstance(topk, int): - topk = (topk, ) - return_single = True - else: - return_single = False - - maxk = max(topk) - if pred.size(0) == 0: - accu = [pred.new_tensor(0.) for i in range(len(topk))] - return accu[0] if return_single else accu - assert pred.ndim == 2 and target.ndim == 1 - assert pred.size(0) == target.size(0) - assert maxk <= pred.size(1), \ - f'maxk {maxk} exceeds pred dimension {pred.size(1)}' - pred_value, pred_label = pred.topk(maxk, dim=1) - pred_label = pred_label.t() # transpose to shape (maxk, N) - correct = pred_label.eq(target.view(1, -1).expand_as(pred_label)) - if thresh is not None: - # Only prediction values larger than thresh are counted as correct - correct = correct & (pred_value > thresh).t() - res = [] - for k in topk: - correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) - res.append(correct_k.mul_(100.0 / pred.size(0))) - return res[0] if return_single else res - - -class Accuracy(nn.Module): - - def __init__(self, topk=(1, ), thresh=None): - """Module to calculate the accuracy. - - Args: - topk (tuple, optional): The criterion used to calculate the - accuracy. Defaults to (1,). - thresh (float, optional): If not None, predictions with scores - under this threshold are considered incorrect. Default to None. - """ - super().__init__() - self.topk = topk - self.thresh = thresh - - def forward(self, pred, target): - """Forward function to calculate accuracy. - - Args: - pred (torch.Tensor): Prediction of models. - target (torch.Tensor): Target for each prediction. - - Returns: - tuple[float]: The accuracies under different topk criterions. - """ - return accuracy(pred, target, self.topk, self.thresh) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/dice_loss.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/dice_loss.py deleted file mode 100644 index 585beeaf1..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/dice_loss.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from ..builder import LOSSES -from .utils import weight_reduce_loss - - -def dice_loss(pred, - target, - weight=None, - eps=1e-3, - reduction='mean', - naive_dice=False, - avg_factor=None): - """Calculate dice loss, there are two forms of dice loss is supported: - - - the one proposed in `V-Net: Fully Convolutional Neural - Networks for Volumetric Medical Image Segmentation - `_. - - the dice loss in which the power of the number in the - denominator is the first power instead of the second - power. - - Args: - pred (torch.Tensor): The prediction, has a shape (n, *) - target (torch.Tensor): The learning label of the prediction, - shape (n, *), same shape of pred. - weight (torch.Tensor, optional): The weight of loss for each - prediction, has a shape (n,). Defaults to None. - eps (float): Avoid dividing by zero. Default: 1e-3. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. - Options are "none", "mean" and "sum". - naive_dice (bool, optional): If false, use the dice - loss defined in the V-Net paper, otherwise, use the - naive dice loss in which the power of the number in the - denominator is the first power instead of the second - power.Defaults to False. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - - input = pred.flatten(1) - target = target.flatten(1).float() - - a = torch.sum(input * target, 1) - if naive_dice: - b = torch.sum(input, 1) - c = torch.sum(target, 1) - d = (2 * a + eps) / (b + c + eps) - else: - b = torch.sum(input * input, 1) + eps - c = torch.sum(target * target, 1) + eps - d = (2 * a) / (b + c) - - loss = 1 - d - if weight is not None: - assert weight.ndim == loss.ndim - assert len(weight) == len(pred) - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - -@LOSSES.register_module() -class DiceLoss(nn.Module): - - def __init__(self, - use_sigmoid=True, - activate=True, - reduction='mean', - naive_dice=False, - loss_weight=1.0, - eps=1e-3): - """Compute dice loss. - - Args: - use_sigmoid (bool, optional): Whether to the prediction is - used for sigmoid or softmax. Defaults to True. - activate (bool): Whether to activate the predictions inside, - this will disable the inside sigmoid operation. - Defaults to True. - reduction (str, optional): The method used - to reduce the loss. Options are "none", - "mean" and "sum". Defaults to 'mean'. - naive_dice (bool, optional): If false, use the dice - loss defined in the V-Net paper, otherwise, use the - naive dice loss in which the power of the number in the - denominator is the first power instead of the second - power. Defaults to False. - loss_weight (float, optional): Weight of loss. Defaults to 1.0. - eps (float): Avoid dividing by zero. Defaults to 1e-3. - """ - - super(DiceLoss, self).__init__() - self.use_sigmoid = use_sigmoid - self.reduction = reduction - self.naive_dice = naive_dice - self.loss_weight = loss_weight - self.eps = eps - self.activate = activate - - def forward(self, - pred, - target, - weight=None, - reduction_override=None, - avg_factor=None): - """Forward function. - - Args: - pred (torch.Tensor): The prediction, has a shape (n, *). - target (torch.Tensor): The label of the prediction, - shape (n, *), same shape of pred. - weight (torch.Tensor, optional): The weight of loss for each - prediction, has a shape (n,). Defaults to None. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - reduction_override (str, optional): The reduction method used to - override the original reduction method of the loss. - Options are "none", "mean" and "sum". - - Returns: - torch.Tensor: The calculated loss - """ - - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - - if self.activate: - if self.use_sigmoid: - pred = pred.sigmoid() - else: - raise NotImplementedError - - loss = self.loss_weight * dice_loss( - pred, - target, - weight, - eps=self.eps, - reduction=reduction, - naive_dice=self.naive_dice, - avg_factor=avg_factor) - - return loss diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/focal_loss.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/focal_loss.py deleted file mode 100644 index 6c20fddd5..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/focal_loss.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.ops import sigmoid_focal_loss as _sigmoid_focal_loss - -from ..builder import LOSSES -from .utils import weight_reduce_loss - - -# This method is only for debugging -def py_sigmoid_focal_loss(pred, - target, - weight=None, - gamma=2.0, - alpha=0.25, - reduction='mean', - avg_factor=None): - """PyTorch version of `Focal Loss `_. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the - number of classes - target (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float, optional): A balanced form for Focal Loss. - Defaults to 0.25. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - pred_sigmoid = pred.sigmoid() - target = target.type_as(pred) - pt = (1 - pred_sigmoid) * target + pred_sigmoid * (1 - target) - focal_weight = (alpha * target + (1 - alpha) * - (1 - target)) * pt.pow(gamma) - loss = F.binary_cross_entropy_with_logits( - pred, target, reduction='none') * focal_weight - if weight is not None: - if weight.shape != loss.shape: - if weight.size(0) == loss.size(0): - # For most cases, weight is of shape (num_priors, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - else: - # Sometimes, weight per anchor per class is also needed. e.g. - # in FSAF. But it may be flattened of shape - # (num_priors x num_class, ), while loss is still of shape - # (num_priors, num_class). - assert weight.numel() == loss.numel() - weight = weight.view(loss.size(0), -1) - assert weight.ndim == loss.ndim - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - -def py_focal_loss_with_prob(pred, - target, - weight=None, - gamma=2.0, - alpha=0.25, - reduction='mean', - avg_factor=None): - """PyTorch version of `Focal Loss `_. - Different from `py_sigmoid_focal_loss`, this function accepts probability - as input. - - Args: - pred (torch.Tensor): The prediction probability with shape (N, C), - C is the number of classes. - target (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float, optional): A balanced form for Focal Loss. - Defaults to 0.25. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - num_classes = pred.size(1) - target = F.one_hot(target, num_classes=num_classes + 1) - target = target[:, :num_classes] - - target = target.type_as(pred) - pt = (1 - pred) * target + pred * (1 - target) - focal_weight = (alpha * target + (1 - alpha) * - (1 - target)) * pt.pow(gamma) - loss = F.binary_cross_entropy( - pred, target, reduction='none') * focal_weight - if weight is not None: - if weight.shape != loss.shape: - if weight.size(0) == loss.size(0): - # For most cases, weight is of shape (num_priors, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - else: - # Sometimes, weight per anchor per class is also needed. e.g. - # in FSAF. But it may be flattened of shape - # (num_priors x num_class, ), while loss is still of shape - # (num_priors, num_class). - assert weight.numel() == loss.numel() - weight = weight.view(loss.size(0), -1) - assert weight.ndim == loss.ndim - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - -def sigmoid_focal_loss(pred, - target, - weight=None, - gamma=2.0, - alpha=0.25, - reduction='mean', - avg_factor=None): - r"""A warpper of cuda version `Focal Loss - `_. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float, optional): A balanced form for Focal Loss. - Defaults to 0.25. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - # Function.apply does not accept keyword arguments, so the decorator - # "weighted_loss" is not applicable - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), gamma, - alpha, None, 'none') - if weight is not None: - if weight.shape != loss.shape: - if weight.size(0) == loss.size(0): - # For most cases, weight is of shape (num_priors, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - else: - # Sometimes, weight per anchor per class is also needed. e.g. - # in FSAF. But it may be flattened of shape - # (num_priors x num_class, ), while loss is still of shape - # (num_priors, num_class). - assert weight.numel() == loss.numel() - weight = weight.view(loss.size(0), -1) - assert weight.ndim == loss.ndim - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - -@LOSSES.register_module() -class FocalLoss(nn.Module): - - def __init__(self, - use_sigmoid=True, - gamma=2.0, - alpha=0.25, - reduction='mean', - loss_weight=1.0, - activated=False): - """`Focal Loss `_ - - Args: - use_sigmoid (bool, optional): Whether to the prediction is - used for sigmoid or softmax. Defaults to True. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float, optional): A balanced form for Focal Loss. - Defaults to 0.25. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and - "sum". - loss_weight (float, optional): Weight of loss. Defaults to 1.0. - activated (bool, optional): Whether the input is activated. - If True, it means the input has been activated and can be - treated as probabilities. Else, it should be treated as logits. - Defaults to False. - """ - super(FocalLoss, self).__init__() - assert use_sigmoid is True, 'Only sigmoid focal loss supported now.' - self.use_sigmoid = use_sigmoid - self.gamma = gamma - self.alpha = alpha - self.reduction = reduction - self.loss_weight = loss_weight - self.activated = activated - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None): - """Forward function. - - Args: - pred (torch.Tensor): The prediction. - target (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): The weight of loss for each - prediction. Defaults to None. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - reduction_override (str, optional): The reduction method used to - override the original reduction method of the loss. - Options are "none", "mean" and "sum". - - Returns: - torch.Tensor: The calculated loss - """ - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.use_sigmoid: - if self.activated: - calculate_loss_func = py_focal_loss_with_prob - else: - if torch.cuda.is_available() and pred.is_cuda: - calculate_loss_func = sigmoid_focal_loss - else: - num_classes = pred.size(1) - target = F.one_hot(target, num_classes=num_classes + 1) - target = target[:, :num_classes] - calculate_loss_func = py_sigmoid_focal_loss - - loss_cls = self.loss_weight * calculate_loss_func( - pred, - target, - weight, - gamma=self.gamma, - alpha=self.alpha, - reduction=reduction, - avg_factor=avg_factor) - - else: - raise NotImplementedError - return loss_cls diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/iou_loss.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/iou_loss.py deleted file mode 100644 index bf1ed04e1..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/iou_loss.py +++ /dev/null @@ -1,474 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -import warnings - -import mmcv -import torch -import torch.nn as nn - -from mmdet.core import bbox_overlaps -from ..builder import LOSSES -from .utils import weighted_loss - - -@mmcv.jit(derivate=True, coderize=True) -@weighted_loss -def iou_loss(pred, target, linear=False, mode='log', eps=1e-6): - """IoU loss. - - Computing the IoU loss between a set of predicted bboxes and target bboxes. - The loss is calculated as negative log of IoU. - - Args: - pred (torch.Tensor): Predicted bboxes of format (x1, y1, x2, y2), - shape (n, 4). - target (torch.Tensor): Corresponding gt bboxes, shape (n, 4). - linear (bool, optional): If True, use linear scale of loss instead of - log scale. Default: False. - mode (str): Loss scaling mode, including "linear", "square", and "log". - Default: 'log' - eps (float): Eps to avoid log(0). - - Return: - torch.Tensor: Loss tensor. - """ - assert mode in ['linear', 'square', 'log'] - if linear: - mode = 'linear' - warnings.warn('DeprecationWarning: Setting "linear=True" in ' - 'iou_loss is deprecated, please use "mode=`linear`" ' - 'instead.') - ious = bbox_overlaps(pred, target, is_aligned=True).clamp(min=eps) - if mode == 'linear': - loss = 1 - ious - elif mode == 'square': - loss = 1 - ious**2 - elif mode == 'log': - loss = -ious.log() - else: - raise NotImplementedError - return loss - - -@mmcv.jit(derivate=True, coderize=True) -@weighted_loss -def bounded_iou_loss(pred, target, beta=0.2, eps=1e-3): - """BIoULoss. - - This is an implementation of paper - `Improving Object Localization with Fitness NMS and Bounded IoU Loss. - `_. - - Args: - pred (torch.Tensor): Predicted bboxes. - target (torch.Tensor): Target bboxes. - beta (float): beta parameter in smoothl1. - eps (float): eps to avoid NaN. - """ - pred_ctrx = (pred[:, 0] + pred[:, 2]) * 0.5 - pred_ctry = (pred[:, 1] + pred[:, 3]) * 0.5 - pred_w = pred[:, 2] - pred[:, 0] - pred_h = pred[:, 3] - pred[:, 1] - with torch.no_grad(): - target_ctrx = (target[:, 0] + target[:, 2]) * 0.5 - target_ctry = (target[:, 1] + target[:, 3]) * 0.5 - target_w = target[:, 2] - target[:, 0] - target_h = target[:, 3] - target[:, 1] - - dx = target_ctrx - pred_ctrx - dy = target_ctry - pred_ctry - - loss_dx = 1 - torch.max( - (target_w - 2 * dx.abs()) / - (target_w + 2 * dx.abs() + eps), torch.zeros_like(dx)) - loss_dy = 1 - torch.max( - (target_h - 2 * dy.abs()) / - (target_h + 2 * dy.abs() + eps), torch.zeros_like(dy)) - loss_dw = 1 - torch.min(target_w / (pred_w + eps), pred_w / - (target_w + eps)) - loss_dh = 1 - torch.min(target_h / (pred_h + eps), pred_h / - (target_h + eps)) - # view(..., -1) does not work for empty tensor - loss_comb = torch.stack([loss_dx, loss_dy, loss_dw, loss_dh], - dim=-1).flatten(1) - - loss = torch.where(loss_comb < beta, 0.5 * loss_comb * loss_comb / beta, - loss_comb - 0.5 * beta) - return loss - - -@mmcv.jit(derivate=True, coderize=True) -@weighted_loss -def giou_loss(pred, target, eps=1e-7): - r"""`Generalized Intersection over Union: A Metric and A Loss for Bounding - Box Regression `_. - - Args: - pred (torch.Tensor): Predicted bboxes of format (x1, y1, x2, y2), - shape (n, 4). - target (torch.Tensor): Corresponding gt bboxes, shape (n, 4). - eps (float): Eps to avoid log(0). - - Return: - Tensor: Loss tensor. - """ - gious = bbox_overlaps(pred, target, mode='giou', is_aligned=True, eps=eps) - loss = 1 - gious - return loss - - -@mmcv.jit(derivate=True, coderize=True) -@weighted_loss -def diou_loss(pred, target, eps=1e-7): - r"""`Implementation of Distance-IoU Loss: Faster and Better - Learning for Bounding Box Regression, https://arxiv.org/abs/1911.08287`_. - - Code is modified from https://github.com/Zzh-tju/DIoU. - - Args: - pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2), - shape (n, 4). - target (Tensor): Corresponding gt bboxes, shape (n, 4). - eps (float): Eps to avoid log(0). - Return: - Tensor: Loss tensor. - """ - # overlap - lt = torch.max(pred[:, :2], target[:, :2]) - rb = torch.min(pred[:, 2:], target[:, 2:]) - wh = (rb - lt).clamp(min=0) - overlap = wh[:, 0] * wh[:, 1] - - # union - ap = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) - ag = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) - union = ap + ag - overlap + eps - - # IoU - ious = overlap / union - - # enclose area - enclose_x1y1 = torch.min(pred[:, :2], target[:, :2]) - enclose_x2y2 = torch.max(pred[:, 2:], target[:, 2:]) - enclose_wh = (enclose_x2y2 - enclose_x1y1).clamp(min=0) - - cw = enclose_wh[:, 0] - ch = enclose_wh[:, 1] - - c2 = cw**2 + ch**2 + eps - - b1_x1, b1_y1 = pred[:, 0], pred[:, 1] - b1_x2, b1_y2 = pred[:, 2], pred[:, 3] - b2_x1, b2_y1 = target[:, 0], target[:, 1] - b2_x2, b2_y2 = target[:, 2], target[:, 3] - - left = ((b2_x1 + b2_x2) - (b1_x1 + b1_x2))**2 / 4 - right = ((b2_y1 + b2_y2) - (b1_y1 + b1_y2))**2 / 4 - rho2 = left + right - - # DIoU - dious = ious - rho2 / c2 - loss = 1 - dious - return loss - - -@mmcv.jit(derivate=True, coderize=True) -@weighted_loss -def ciou_loss(pred, target, eps=1e-7): - r"""`Implementation of paper `Enhancing Geometric Factors into - Model Learning and Inference for Object Detection and Instance - Segmentation `_. - - Code is modified from https://github.com/Zzh-tju/CIoU. - - Args: - pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2), - shape (n, 4). - target (Tensor): Corresponding gt bboxes, shape (n, 4). - eps (float): Eps to avoid log(0). - Return: - Tensor: Loss tensor. - """ - # overlap - lt = torch.max(pred[:, :2], target[:, :2]) - rb = torch.min(pred[:, 2:], target[:, 2:]) - wh = (rb - lt).clamp(min=0) - overlap = wh[:, 0] * wh[:, 1] - - # union - ap = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) - ag = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) - union = ap + ag - overlap + eps - - # IoU - ious = overlap / union - - # enclose area - enclose_x1y1 = torch.min(pred[:, :2], target[:, :2]) - enclose_x2y2 = torch.max(pred[:, 2:], target[:, 2:]) - enclose_wh = (enclose_x2y2 - enclose_x1y1).clamp(min=0) - - cw = enclose_wh[:, 0] - ch = enclose_wh[:, 1] - - c2 = cw**2 + ch**2 + eps - - b1_x1, b1_y1 = pred[:, 0], pred[:, 1] - b1_x2, b1_y2 = pred[:, 2], pred[:, 3] - b2_x1, b2_y1 = target[:, 0], target[:, 1] - b2_x2, b2_y2 = target[:, 2], target[:, 3] - - w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps - w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps - - left = ((b2_x1 + b2_x2) - (b1_x1 + b1_x2))**2 / 4 - right = ((b2_y1 + b2_y2) - (b1_y1 + b1_y2))**2 / 4 - rho2 = left + right - - factor = 4 / math.pi**2 - v = factor * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2) - - with torch.no_grad(): - alpha = (ious > 0.5).float() * v / (1 - ious + v) - - # CIoU - cious = ious - (rho2 / c2 + alpha * v) - loss = 1 - cious.clamp(min=-1.0, max=1.0) - return loss - - -@LOSSES.register_module() -class IoULoss(nn.Module): - """IoULoss. - - Computing the IoU loss between a set of predicted bboxes and target bboxes. - - Args: - linear (bool): If True, use linear scale of loss else determined - by mode. Default: False. - eps (float): Eps to avoid log(0). - reduction (str): Options are "none", "mean" and "sum". - loss_weight (float): Weight of loss. - mode (str): Loss scaling mode, including "linear", "square", and "log". - Default: 'log' - """ - - def __init__(self, - linear=False, - eps=1e-6, - reduction='mean', - loss_weight=1.0, - mode='log'): - super(IoULoss, self).__init__() - assert mode in ['linear', 'square', 'log'] - if linear: - mode = 'linear' - warnings.warn('DeprecationWarning: Setting "linear=True" in ' - 'IOULoss is deprecated, please use "mode=`linear`" ' - 'instead.') - self.mode = mode - self.linear = linear - self.eps = eps - self.reduction = reduction - self.loss_weight = loss_weight - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - """Forward function. - - Args: - pred (torch.Tensor): The prediction. - target (torch.Tensor): The learning target of the prediction. - weight (torch.Tensor, optional): The weight of loss for each - prediction. Defaults to None. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - reduction_override (str, optional): The reduction method used to - override the original reduction method of the loss. - Defaults to None. Options are "none", "mean" and "sum". - """ - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if (weight is not None) and (not torch.any(weight > 0)) and ( - reduction != 'none'): - if pred.dim() == weight.dim() + 1: - weight = weight.unsqueeze(1) - return (pred * weight).sum() # 0 - if weight is not None and weight.dim() > 1: - # TODO: remove this in the future - # reduce the weight of shape (n, 4) to (n,) to match the - # iou_loss of shape (n,) - assert weight.shape == pred.shape - weight = weight.mean(-1) - loss = self.loss_weight * iou_loss( - pred, - target, - weight, - mode=self.mode, - eps=self.eps, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss - - -@LOSSES.register_module() -class BoundedIoULoss(nn.Module): - - def __init__(self, beta=0.2, eps=1e-3, reduction='mean', loss_weight=1.0): - super(BoundedIoULoss, self).__init__() - self.beta = beta - self.eps = eps - self.reduction = reduction - self.loss_weight = loss_weight - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - if weight is not None and not torch.any(weight > 0): - if pred.dim() == weight.dim() + 1: - weight = weight.unsqueeze(1) - return (pred * weight).sum() # 0 - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - loss = self.loss_weight * bounded_iou_loss( - pred, - target, - weight, - beta=self.beta, - eps=self.eps, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss - - -@LOSSES.register_module() -class GIoULoss(nn.Module): - - def __init__(self, eps=1e-6, reduction='mean', loss_weight=1.0): - super(GIoULoss, self).__init__() - self.eps = eps - self.reduction = reduction - self.loss_weight = loss_weight - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - if weight is not None and not torch.any(weight > 0): - if pred.dim() == weight.dim() + 1: - weight = weight.unsqueeze(1) - return (pred * weight).sum() # 0 - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if weight is not None and weight.dim() > 1: - # TODO: remove this in the future - # reduce the weight of shape (n, 4) to (n,) to match the - # giou_loss of shape (n,) - assert weight.shape == pred.shape - weight = weight.mean(-1) - loss = self.loss_weight * giou_loss( - pred, - target, - weight, - eps=self.eps, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss - - -@LOSSES.register_module() -class DIoULoss(nn.Module): - - def __init__(self, eps=1e-6, reduction='mean', loss_weight=1.0): - super(DIoULoss, self).__init__() - self.eps = eps - self.reduction = reduction - self.loss_weight = loss_weight - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - if weight is not None and not torch.any(weight > 0): - if pred.dim() == weight.dim() + 1: - weight = weight.unsqueeze(1) - return (pred * weight).sum() # 0 - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if weight is not None and weight.dim() > 1: - # TODO: remove this in the future - # reduce the weight of shape (n, 4) to (n,) to match the - # giou_loss of shape (n,) - assert weight.shape == pred.shape - weight = weight.mean(-1) - loss = self.loss_weight * diou_loss( - pred, - target, - weight, - eps=self.eps, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss - - -@LOSSES.register_module() -class CIoULoss(nn.Module): - - def __init__(self, eps=1e-6, reduction='mean', loss_weight=1.0): - super(CIoULoss, self).__init__() - self.eps = eps - self.reduction = reduction - self.loss_weight = loss_weight - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - if weight is not None and not torch.any(weight > 0): - if pred.dim() == weight.dim() + 1: - weight = weight.unsqueeze(1) - return (pred * weight).sum() # 0 - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if weight is not None and weight.dim() > 1: - # TODO: remove this in the future - # reduce the weight of shape (n, 4) to (n,) to match the - # giou_loss of shape (n,) - assert weight.shape == pred.shape - weight = weight.mean(-1) - loss = self.loss_weight * ciou_loss( - pred, - target, - weight, - eps=self.eps, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/utils.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/utils.py deleted file mode 100644 index 778237ebf..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/losses/utils.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import mmcv -import torch -import torch.nn.functional as F - - -def reduce_loss(loss, reduction): - """Reduce loss as specified. - - Args: - loss (Tensor): Elementwise loss tensor. - reduction (str): Options are "none", "mean" and "sum". - - Return: - Tensor: Reduced loss tensor. - """ - reduction_enum = F._Reduction.get_enum(reduction) - # none: 0, elementwise_mean:1, sum: 2 - if reduction_enum == 0: - return loss - elif reduction_enum == 1: - return loss.mean() - elif reduction_enum == 2: - return loss.sum() - - -@mmcv.jit(derivate=True, coderize=True) -def weight_reduce_loss(loss, weight=None, reduction='mean', avg_factor=None): - """Apply element-wise weight and reduce loss. - - Args: - loss (Tensor): Element-wise loss. - weight (Tensor): Element-wise weights. - reduction (str): Same as built-in losses of PyTorch. - avg_factor (float): Average factor when computing the mean of losses. - - Returns: - Tensor: Processed loss values. - """ - # if weight is specified, apply element-wise weight - if weight is not None: - loss = loss * weight - - # if avg_factor is not specified, just reduce the loss - if avg_factor is None: - loss = reduce_loss(loss, reduction) - else: - # if reduction is mean, then average the loss by avg_factor - if reduction == 'mean': - # Avoid causing ZeroDivisionError when avg_factor is 0.0, - # i.e., all labels of an image belong to ignore index. - eps = torch.finfo(torch.float32).eps - loss = loss.sum() / (avg_factor + eps) - # if reduction is 'none', then do nothing, otherwise raise an error - elif reduction != 'none': - raise ValueError('avg_factor can not be used with reduction="sum"') - return loss - - -def weighted_loss(loss_func): - """Create a weighted version of a given loss function. - - To use this decorator, the loss function must have the signature like - `loss_func(pred, target, **kwargs)`. The function only needs to compute - element-wise loss without any reduction. This decorator will add weight - and reduction arguments to the function. The decorated function will have - the signature like `loss_func(pred, target, weight=None, reduction='mean', - avg_factor=None, **kwargs)`. - - :Example: - - >>> import torch - >>> @weighted_loss - >>> def l1_loss(pred, target): - >>> return (pred - target).abs() - - >>> pred = torch.Tensor([0, 2, 3]) - >>> target = torch.Tensor([1, 1, 1]) - >>> weight = torch.Tensor([1, 0, 1]) - - >>> l1_loss(pred, target) - tensor(1.3333) - >>> l1_loss(pred, target, weight) - tensor(1.) - >>> l1_loss(pred, target, reduction='none') - tensor([1., 1., 2.]) - >>> l1_loss(pred, target, weight, avg_factor=2) - tensor(1.5000) - """ - - @functools.wraps(loss_func) - def wrapper(pred, - target, - weight=None, - reduction='mean', - avg_factor=None, - **kwargs): - # get element-wise loss - loss = loss_func(pred, target, **kwargs) - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - return wrapper diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/necks/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/necks/__init__.py deleted file mode 100644 index e79a2a0d7..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/necks/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from .fpn import FPN - -__all__ = [ - 'FPN' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/necks/fpn.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/necks/fpn.py deleted file mode 100644 index 9f6013865..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/necks/fpn.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule, auto_fp16 - -from ..builder import NECKS - - -@NECKS.register_module() -class FPN(BaseModule): - r"""Feature Pyramid Network. - - This is an implementation of paper `Feature Pyramid Networks for Object - Detection `_. - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale) - num_outs (int): Number of output scales. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - add_extra_convs (bool | str): If bool, it decides whether to add conv - layers on top of the original feature maps. Default to False. - If True, it is equivalent to `add_extra_convs='on_input'`. - If str, it specifies the source feature map of the extra convs. - Only the following options are allowed - - - 'on_input': Last feat map of neck inputs (i.e. backbone feature). - - 'on_lateral': Last feature map after lateral convs. - - 'on_output': The last output feature map after fpn convs. - relu_before_extra_convs (bool): Whether to apply relu before the extra - conv. Default: False. - no_norm_on_lateral (bool): Whether to apply norm on lateral. - Default: False. - conv_cfg (dict): Config dict for convolution layer. Default: None. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (str): Config dict for activation layer in ConvModule. - Default: None. - upsample_cfg (dict): Config dict for interpolate layer. - Default: `dict(mode='nearest')` - init_cfg (dict or list[dict], optional): Initialization config dict. - - Example: - >>> import torch - >>> in_channels = [2, 3, 5, 7] - >>> scales = [340, 170, 84, 43] - >>> inputs = [torch.rand(1, c, s, s) - ... for c, s in zip(in_channels, scales)] - >>> self = FPN(in_channels, 11, len(in_channels)).eval() - >>> outputs = self.forward(inputs) - >>> for i in range(len(outputs)): - ... print(f'outputs[{i}].shape = {outputs[i].shape}') - outputs[0].shape = torch.Size([1, 11, 340, 340]) - outputs[1].shape = torch.Size([1, 11, 170, 170]) - outputs[2].shape = torch.Size([1, 11, 84, 84]) - outputs[3].shape = torch.Size([1, 11, 43, 43]) - """ - - def __init__(self, - in_channels, - out_channels, - num_outs, - start_level=0, - end_level=-1, - add_extra_convs=False, - relu_before_extra_convs=False, - no_norm_on_lateral=False, - conv_cfg=None, - norm_cfg=None, - act_cfg=None, - upsample_cfg=dict(mode='nearest'), - init_cfg=dict( - type='Xavier', layer='Conv2d', distribution='uniform')): - super(FPN, self).__init__(init_cfg) - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.num_ins = len(in_channels) - self.num_outs = num_outs - self.relu_before_extra_convs = relu_before_extra_convs - self.no_norm_on_lateral = no_norm_on_lateral - self.fp16_enabled = False - self.upsample_cfg = upsample_cfg.copy() - - if end_level == -1: - self.backbone_end_level = self.num_ins - assert num_outs >= self.num_ins - start_level - else: - # if end_level < inputs, no extra level is allowed - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - assert num_outs == end_level - start_level - self.start_level = start_level - self.end_level = end_level - self.add_extra_convs = add_extra_convs - assert isinstance(add_extra_convs, (str, bool)) - if isinstance(add_extra_convs, str): - # Extra_convs_source choices: 'on_input', 'on_lateral', 'on_output' - assert add_extra_convs in ('on_input', 'on_lateral', 'on_output') - elif add_extra_convs: # True - self.add_extra_convs = 'on_input' - - self.lateral_convs = nn.ModuleList() - self.fpn_convs = nn.ModuleList() - - for i in range(self.start_level, self.backbone_end_level): - l_conv = ConvModule( - in_channels[i], - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg if not self.no_norm_on_lateral else None, - act_cfg=act_cfg, - inplace=False) - fpn_conv = ConvModule( - out_channels, - out_channels, - 3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - - self.lateral_convs.append(l_conv) - self.fpn_convs.append(fpn_conv) - - # add extra conv layers (e.g., RetinaNet) - extra_levels = num_outs - self.backbone_end_level + self.start_level - if self.add_extra_convs and extra_levels >= 1: - for i in range(extra_levels): - if i == 0 and self.add_extra_convs == 'on_input': - in_channels = self.in_channels[self.backbone_end_level - 1] - else: - in_channels = out_channels - extra_fpn_conv = ConvModule( - in_channels, - out_channels, - 3, - stride=2, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - self.fpn_convs.append(extra_fpn_conv) - - @auto_fp16() - def forward(self, inputs): - """Forward function.""" - assert len(inputs) == len(self.in_channels) - - # build laterals - laterals = [ - lateral_conv(inputs[i + self.start_level]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - - # build top-down path - used_backbone_levels = len(laterals) - for i in range(used_backbone_levels - 1, 0, -1): - # In some cases, fixing `scale factor` (e.g. 2) is preferred, but - # it cannot co-exist with `size` in `F.interpolate`. - if 'scale_factor' in self.upsample_cfg: - # fix runtime error of "+=" inplace operation in PyTorch 1.10 - laterals[i - 1] = laterals[i - 1] + F.interpolate( - laterals[i], **self.upsample_cfg) - else: - prev_shape = laterals[i - 1].shape[2:] - laterals[i - 1] = laterals[i - 1] + F.interpolate( - laterals[i], size=prev_shape, **self.upsample_cfg) - - # build outputs - # part 1: from original levels - outs = [ - self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels) - ] - # part 2: add extra levels - if self.num_outs > len(outs): - # use max pool to get more levels on top of outputs - # (e.g., Faster R-CNN, Mask R-CNN) - if not self.add_extra_convs: - for i in range(self.num_outs - used_backbone_levels): - outs.append(F.max_pool2d(outs[-1], 1, stride=2)) - # add conv layers on top of original feature maps (RetinaNet) - else: - if self.add_extra_convs == 'on_input': - extra_source = inputs[self.backbone_end_level - 1] - elif self.add_extra_convs == 'on_lateral': - extra_source = laterals[-1] - elif self.add_extra_convs == 'on_output': - extra_source = outs[-1] - else: - raise NotImplementedError - outs.append(self.fpn_convs[used_backbone_levels](extra_source)) - for i in range(used_backbone_levels + 1, self.num_outs): - if self.relu_before_extra_convs: - outs.append(self.fpn_convs[i](F.relu(outs[-1]))) - else: - outs.append(self.fpn_convs[i](outs[-1])) - return tuple(outs) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/utils/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/utils/__init__.py deleted file mode 100644 index bacd833c0..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/utils/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .res_layer import ResLayer -__all__ = [ - 'ResLayer'] - diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/models/utils/res_layer.py b/cv/instance_segmentation/solo/pytorch/mmdet/models/utils/res_layer.py deleted file mode 100644 index 5c3e89fb0..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/models/utils/res_layer.py +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner import BaseModule, Sequential -from torch import nn as nn - - -class ResLayer(Sequential): - """ResLayer to build ResNet style backbone. - - Args: - block (nn.Module): block used to build ResLayer. - inplanes (int): inplanes of block. - planes (int): planes of block. - num_blocks (int): number of blocks. - stride (int): stride of the first block. Default: 1 - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False - conv_cfg (dict): dictionary to construct and config conv layer. - Default: None - norm_cfg (dict): dictionary to construct and config norm layer. - Default: dict(type='BN') - downsample_first (bool): Downsample at the first block or last block. - False for Hourglass, True for ResNet. Default: True - """ - - def __init__(self, - block, - inplanes, - planes, - num_blocks, - stride=1, - avg_down=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - downsample_first=True, - **kwargs): - self.block = block - - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = [] - conv_stride = stride - if avg_down: - conv_stride = 1 - downsample.append( - nn.AvgPool2d( - kernel_size=stride, - stride=stride, - ceil_mode=True, - count_include_pad=False)) - downsample.extend([ - build_conv_layer( - conv_cfg, - inplanes, - planes * block.expansion, - kernel_size=1, - stride=conv_stride, - bias=False), - build_norm_layer(norm_cfg, planes * block.expansion)[1] - ]) - downsample = nn.Sequential(*downsample) - - layers = [] - if downsample_first: - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=stride, - downsample=downsample, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - inplanes = planes * block.expansion - for _ in range(1, num_blocks): - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - - else: # downsample_first=False is for HourglassModule - for _ in range(num_blocks - 1): - layers.append( - block( - inplanes=inplanes, - planes=inplanes, - stride=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=stride, - downsample=downsample, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - super(ResLayer, self).__init__(*layers) - - -class SimplifiedBasicBlock(BaseModule): - """Simplified version of original basic residual block. This is used in - `SCNet `_. - - - Norm layer is now optional - - Last ReLU in forward function is removed - """ - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_fg=None): - super(SimplifiedBasicBlock, self).__init__(init_fg) - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - assert not with_cp, 'Not implemented yet.' - self.with_norm = norm_cfg is not None - with_bias = True if norm_cfg is None else False - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - 3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=with_bias) - if self.with_norm: - self.norm1_name, norm1 = build_norm_layer( - norm_cfg, planes, postfix=1) - self.add_module(self.norm1_name, norm1) - self.conv2 = build_conv_layer( - conv_cfg, planes, planes, 3, padding=1, bias=with_bias) - if self.with_norm: - self.norm2_name, norm2 = build_norm_layer( - norm_cfg, planes, postfix=2) - self.add_module(self.norm2_name, norm2) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) if self.with_norm else None - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) if self.with_norm else None - - def forward(self, x): - """Forward function.""" - - identity = x - - out = self.conv1(x) - if self.with_norm: - out = self.norm1(out) - out = self.relu(out) - - out = self.conv2(out) - if self.with_norm: - out = self.norm2(out) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/__init__.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/__init__.py deleted file mode 100644 index f57acb5f0..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collect_env import collect_env -from .compat_config import compat_cfg -from .logger import get_caller_name, get_root_logger, log_img_scale -from .memory import AvoidCUDAOOM, AvoidOOM -from .misc import find_latest_checkpoint, update_data_root -from .replace_cfg_vals import replace_cfg_vals -from .setup_env import setup_multi_processes -from .split_batch import split_batch -from .util_distribution import build_ddp, build_dp, get_device - -__all__ = [ - 'get_root_logger', 'collect_env', 'find_latest_checkpoint', - 'update_data_root', 'setup_multi_processes', 'get_caller_name', - 'log_img_scale', 'compat_cfg', 'split_batch', 'build_ddp', 'build_dp', - 'get_device', 'replace_cfg_vals', 'AvoidOOM', 'AvoidCUDAOOM' -] diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/collect_env.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/collect_env.py deleted file mode 100644 index 97e25c0e9..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/collect_env.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash - -import mmdet - - -def collect_env(): - """Collect the information of the running environments.""" - env_info = collect_base_env() - env_info['MMDetection'] = mmdet.__version__ + '+' + get_git_hash()[:7] - return env_info - - -if __name__ == '__main__': - for name, val in collect_env().items(): - print(f'{name}: {val}') diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/compat_config.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/compat_config.py deleted file mode 100644 index 05aa37dcd..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/compat_config.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings - -from mmcv import ConfigDict - - -def compat_cfg(cfg): - """This function would modify some filed to keep the compatibility of - config. - - For example, it will move some args which will be deprecated to the correct - fields. - """ - cfg = copy.deepcopy(cfg) - cfg = compat_imgs_per_gpu(cfg) - cfg = compat_loader_args(cfg) - cfg = compat_runner_args(cfg) - return cfg - - -def compat_runner_args(cfg): - if 'runner' not in cfg: - cfg.runner = ConfigDict({ - 'type': 'EpochBasedRunner', - 'max_epochs': cfg.total_epochs - }) - warnings.warn( - 'config is now expected to have a `runner` section, ' - 'please set `runner` in your config.', UserWarning) - else: - if 'total_epochs' in cfg: - assert cfg.total_epochs == cfg.runner.max_epochs - return cfg - - -def compat_imgs_per_gpu(cfg): - cfg = copy.deepcopy(cfg) - if 'imgs_per_gpu' in cfg.data: - warnings.warn('"imgs_per_gpu" is deprecated in MMDet V2.0. ' - 'Please use "samples_per_gpu" instead') - if 'samples_per_gpu' in cfg.data: - warnings.warn( - f'Got "imgs_per_gpu"={cfg.data.imgs_per_gpu} and ' - f'"samples_per_gpu"={cfg.data.samples_per_gpu}, "imgs_per_gpu"' - f'={cfg.data.imgs_per_gpu} is used in this experiments') - else: - warnings.warn('Automatically set "samples_per_gpu"="imgs_per_gpu"=' - f'{cfg.data.imgs_per_gpu} in this experiments') - cfg.data.samples_per_gpu = cfg.data.imgs_per_gpu - return cfg - - -def compat_loader_args(cfg): - """Deprecated sample_per_gpu in cfg.data.""" - - cfg = copy.deepcopy(cfg) - if 'train_dataloader' not in cfg.data: - cfg.data['train_dataloader'] = ConfigDict() - if 'val_dataloader' not in cfg.data: - cfg.data['val_dataloader'] = ConfigDict() - if 'test_dataloader' not in cfg.data: - cfg.data['test_dataloader'] = ConfigDict() - - # special process for train_dataloader - if 'samples_per_gpu' in cfg.data: - - samples_per_gpu = cfg.data.pop('samples_per_gpu') - assert 'samples_per_gpu' not in \ - cfg.data.train_dataloader, ('`samples_per_gpu` are set ' - 'in `data` field and ` ' - 'data.train_dataloader` ' - 'at the same time. ' - 'Please only set it in ' - '`data.train_dataloader`. ') - cfg.data.train_dataloader['samples_per_gpu'] = samples_per_gpu - - if 'persistent_workers' in cfg.data: - - persistent_workers = cfg.data.pop('persistent_workers') - assert 'persistent_workers' not in \ - cfg.data.train_dataloader, ('`persistent_workers` are set ' - 'in `data` field and ` ' - 'data.train_dataloader` ' - 'at the same time. ' - 'Please only set it in ' - '`data.train_dataloader`. ') - cfg.data.train_dataloader['persistent_workers'] = persistent_workers - - if 'workers_per_gpu' in cfg.data: - - workers_per_gpu = cfg.data.pop('workers_per_gpu') - cfg.data.train_dataloader['workers_per_gpu'] = workers_per_gpu - cfg.data.val_dataloader['workers_per_gpu'] = workers_per_gpu - cfg.data.test_dataloader['workers_per_gpu'] = workers_per_gpu - - # special process for val_dataloader - if 'samples_per_gpu' in cfg.data.val: - # keep default value of `sample_per_gpu` is 1 - assert 'samples_per_gpu' not in \ - cfg.data.val_dataloader, ('`samples_per_gpu` are set ' - 'in `data.val` field and ` ' - 'data.val_dataloader` at ' - 'the same time. ' - 'Please only set it in ' - '`data.val_dataloader`. ') - cfg.data.val_dataloader['samples_per_gpu'] = \ - cfg.data.val.pop('samples_per_gpu') - # special process for val_dataloader - - # in case the test dataset is concatenated - if isinstance(cfg.data.test, dict): - if 'samples_per_gpu' in cfg.data.test: - assert 'samples_per_gpu' not in \ - cfg.data.test_dataloader, ('`samples_per_gpu` are set ' - 'in `data.test` field and ` ' - 'data.test_dataloader` ' - 'at the same time. ' - 'Please only set it in ' - '`data.test_dataloader`. ') - - cfg.data.test_dataloader['samples_per_gpu'] = \ - cfg.data.test.pop('samples_per_gpu') - - elif isinstance(cfg.data.test, list): - for ds_cfg in cfg.data.test: - if 'samples_per_gpu' in ds_cfg: - assert 'samples_per_gpu' not in \ - cfg.data.test_dataloader, ('`samples_per_gpu` are set ' - 'in `data.test` field and ` ' - 'data.test_dataloader` at' - ' the same time. ' - 'Please only set it in ' - '`data.test_dataloader`. ') - samples_per_gpu = max( - [ds_cfg.pop('samples_per_gpu', 1) for ds_cfg in cfg.data.test]) - cfg.data.test_dataloader['samples_per_gpu'] = samples_per_gpu - - return cfg diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/contextmanagers.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/contextmanagers.py deleted file mode 100644 index fa12bfcaf..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/contextmanagers.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import asyncio -import contextlib -import logging -import os -import time -from typing import List - -import torch - -logger = logging.getLogger(__name__) - -DEBUG_COMPLETED_TIME = bool(os.environ.get('DEBUG_COMPLETED_TIME', False)) - - -@contextlib.asynccontextmanager -async def completed(trace_name='', - name='', - sleep_interval=0.05, - streams: List[torch.cuda.Stream] = None): - """Async context manager that waits for work to complete on given CUDA - streams.""" - if not torch.cuda.is_available(): - yield - return - - stream_before_context_switch = torch.cuda.current_stream() - if not streams: - streams = [stream_before_context_switch] - else: - streams = [s if s else stream_before_context_switch for s in streams] - - end_events = [ - torch.cuda.Event(enable_timing=DEBUG_COMPLETED_TIME) for _ in streams - ] - - if DEBUG_COMPLETED_TIME: - start = torch.cuda.Event(enable_timing=True) - stream_before_context_switch.record_event(start) - - cpu_start = time.monotonic() - logger.debug('%s %s starting, streams: %s', trace_name, name, streams) - grad_enabled_before = torch.is_grad_enabled() - try: - yield - finally: - current_stream = torch.cuda.current_stream() - assert current_stream == stream_before_context_switch - - if DEBUG_COMPLETED_TIME: - cpu_end = time.monotonic() - for i, stream in enumerate(streams): - event = end_events[i] - stream.record_event(event) - - grad_enabled_after = torch.is_grad_enabled() - - # observed change of torch.is_grad_enabled() during concurrent run of - # async_test_bboxes code - assert (grad_enabled_before == grad_enabled_after - ), 'Unexpected is_grad_enabled() value change' - - are_done = [e.query() for e in end_events] - logger.debug('%s %s completed: %s streams: %s', trace_name, name, - are_done, streams) - with torch.cuda.stream(stream_before_context_switch): - while not all(are_done): - await asyncio.sleep(sleep_interval) - are_done = [e.query() for e in end_events] - logger.debug( - '%s %s completed: %s streams: %s', - trace_name, - name, - are_done, - streams, - ) - - current_stream = torch.cuda.current_stream() - assert current_stream == stream_before_context_switch - - if DEBUG_COMPLETED_TIME: - cpu_time = (cpu_end - cpu_start) * 1000 - stream_times_ms = '' - for i, stream in enumerate(streams): - elapsed_time = start.elapsed_time(end_events[i]) - stream_times_ms += f' {stream} {elapsed_time:.2f} ms' - logger.info('%s %s %.2f ms %s', trace_name, name, cpu_time, - stream_times_ms) - - -@contextlib.asynccontextmanager -async def concurrent(streamqueue: asyncio.Queue, - trace_name='concurrent', - name='stream'): - """Run code concurrently in different streams. - - :param streamqueue: asyncio.Queue instance. - - Queue tasks define the pool of streams used for concurrent execution. - """ - if not torch.cuda.is_available(): - yield - return - - initial_stream = torch.cuda.current_stream() - - with torch.cuda.stream(initial_stream): - stream = await streamqueue.get() - assert isinstance(stream, torch.cuda.Stream) - - try: - with torch.cuda.stream(stream): - logger.debug('%s %s is starting, stream: %s', trace_name, name, - stream) - yield - current = torch.cuda.current_stream() - assert current == stream - logger.debug('%s %s has finished, stream: %s', trace_name, - name, stream) - finally: - streamqueue.task_done() - streamqueue.put_nowait(stream) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/logger.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/logger.py deleted file mode 100644 index 485f641b7..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/logger.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import logging - -from mmcv.utils import get_logger - - -def get_root_logger(log_file=None, log_level=logging.INFO): - """Get root logger. - - Args: - log_file (str, optional): File path of log. Defaults to None. - log_level (int, optional): The level of logger. - Defaults to logging.INFO. - - Returns: - :obj:`logging.Logger`: The obtained logger - """ - logger = get_logger(name='mmdet', log_file=log_file, log_level=log_level) - - return logger - - -def get_caller_name(): - """Get name of caller method.""" - # this_func_frame = inspect.stack()[0][0] # i.e., get_caller_name - # callee_frame = inspect.stack()[1][0] # e.g., log_img_scale - caller_frame = inspect.stack()[2][0] # e.g., caller of log_img_scale - caller_method = caller_frame.f_code.co_name - try: - caller_class = caller_frame.f_locals['self'].__class__.__name__ - return f'{caller_class}.{caller_method}' - except KeyError: # caller is a function - return caller_method - - -def log_img_scale(img_scale, shape_order='hw', skip_square=False): - """Log image size. - - Args: - img_scale (tuple): Image size to be logged. - shape_order (str, optional): The order of image shape. - 'hw' for (height, width) and 'wh' for (width, height). - Defaults to 'hw'. - skip_square (bool, optional): Whether to skip logging for square - img_scale. Defaults to False. - - Returns: - bool: Whether to have done logging. - """ - if shape_order == 'hw': - height, width = img_scale - elif shape_order == 'wh': - width, height = img_scale - else: - raise ValueError(f'Invalid shape_order {shape_order}.') - - if skip_square and (height == width): - return False - - logger = get_root_logger() - caller = get_caller_name() - logger.info(f'image shape: height={height}, width={width} in {caller}') - - return True diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/memory.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/memory.py deleted file mode 100644 index eb212bcae..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/memory.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from collections import abc -from contextlib import contextmanager -from functools import wraps - -import torch - -from mmdet.utils import get_root_logger - - -def cast_tensor_type(inputs, src_type=None, dst_type=None): - """Recursively convert Tensor in inputs from ``src_type`` to ``dst_type``. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype | torch.device): Source type. - src_type (torch.dtype | torch.device): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - assert dst_type is not None - if isinstance(inputs, torch.Tensor): - if isinstance(dst_type, torch.device): - # convert Tensor to dst_device - if hasattr(inputs, 'to') and \ - hasattr(inputs, 'device') and \ - (inputs.device == src_type or src_type is None): - return inputs.to(dst_type) - else: - return inputs - else: - # convert Tensor to dst_dtype - if hasattr(inputs, 'to') and \ - hasattr(inputs, 'dtype') and \ - (inputs.dtype == src_type or src_type is None): - return inputs.to(dst_type) - else: - return inputs - # we need to ensure that the type of inputs to be casted are the same - # as the argument `src_type`. - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type=src_type, dst_type=dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type=src_type, dst_type=dst_type) - for item in inputs) - # TODO: Currently not supported - # elif isinstance(inputs, InstanceData): - # for key, value in inputs.items(): - # inputs[key] = cast_tensor_type( - # value, src_type=src_type, dst_type=dst_type) - # return inputs - else: - return inputs - - -@contextmanager -def _ignore_torch_cuda_oom(): - """A context which ignores CUDA OOM exception from pytorch. - - Code is modified from - # noqa: E501 - """ - try: - yield - except RuntimeError as e: - # NOTE: the string may change? - if 'CUDA out of memory. ' in str(e): - pass - else: - raise - - -class AvoidOOM: - """Try to convert inputs to FP16 and CPU if got a PyTorch's CUDA Out of - Memory error. It will do the following steps: - - 1. First retry after calling `torch.cuda.empty_cache()`. - 2. If that still fails, it will then retry by converting inputs - to FP16. - 3. If that still fails trying to convert inputs to CPUs. - In this case, it expects the function to dispatch to - CPU implementation. - - Args: - to_cpu (bool): Whether to convert outputs to CPU if get an OOM - error. This will slow down the code significantly. - Defaults to True. - test (bool): Skip `_ignore_torch_cuda_oom` operate that can use - lightweight data in unit test, only used in - test unit. Defaults to False. - - Examples: - >>> from mmdet.utils.memory import AvoidOOM - >>> AvoidCUDAOOM = AvoidOOM() - >>> output = AvoidOOM.retry_if_cuda_oom( - >>> some_torch_function)(input1, input2) - >>> # To use as a decorator - >>> # from mmdet.utils import AvoidCUDAOOM - >>> @AvoidCUDAOOM.retry_if_cuda_oom - >>> def function(*args, **kwargs): - >>> return None - ``` - - Note: - 1. The output may be on CPU even if inputs are on GPU. Processing - on CPU will slow down the code significantly. - 2. When converting inputs to CPU, it will only look at each argument - and check if it has `.device` and `.to` for conversion. Nested - structures of tensors are not supported. - 3. Since the function might be called more than once, it has to be - stateless. - """ - - def __init__(self, to_cpu=True, test=False): - self.to_cpu = to_cpu - self.test = test - - def retry_if_cuda_oom(self, func): - """Makes a function retry itself after encountering pytorch's CUDA OOM - error. - - The implementation logic is referred to - https://github.com/facebookresearch/detectron2/blob/main/detectron2/utils/memory.py - - Args: - func: a stateless callable that takes tensor-like objects - as arguments. - Returns: - func: a callable which retries `func` if OOM is encountered. - """ # noqa: W605 - - @wraps(func) - def wrapped(*args, **kwargs): - - # raw function - if not self.test: - with _ignore_torch_cuda_oom(): - return func(*args, **kwargs) - - # Clear cache and retry - torch.cuda.empty_cache() - with _ignore_torch_cuda_oom(): - return func(*args, **kwargs) - - # get the type and device of first tensor - dtype, device = None, None - values = args + tuple(kwargs.values()) - for value in values: - if isinstance(value, torch.Tensor): - dtype = value.dtype - device = value.device - break - if dtype is None or device is None: - raise ValueError('There is no tensor in the inputs, ' - 'cannot get dtype and device.') - - # Convert to FP16 - fp16_args = cast_tensor_type(args, dst_type=torch.half) - fp16_kwargs = cast_tensor_type(kwargs, dst_type=torch.half) - logger = get_root_logger() - logger.warning(f'Attempting to copy inputs of {str(func)} ' - 'to FP16 due to CUDA OOM') - - # get input tensor type, the output type will same as - # the first parameter type. - with _ignore_torch_cuda_oom(): - output = func(*fp16_args, **fp16_kwargs) - output = cast_tensor_type( - output, src_type=torch.half, dst_type=dtype) - if not self.test: - return output - logger.warning('Using FP16 still meet CUDA OOM') - - # Try on CPU. This will slow down the code significantly, - # therefore print a notice. - if self.to_cpu: - logger.warning(f'Attempting to copy inputs of {str(func)} ' - 'to CPU due to CUDA OOM') - cpu_device = torch.empty(0).device - cpu_args = cast_tensor_type(args, dst_type=cpu_device) - cpu_kwargs = cast_tensor_type(kwargs, dst_type=cpu_device) - - # convert outputs to GPU - with _ignore_torch_cuda_oom(): - logger.warning(f'Convert outputs to GPU (device={device})') - output = func(*cpu_args, **cpu_kwargs) - output = cast_tensor_type( - output, src_type=cpu_device, dst_type=device) - return output - - warnings.warn('Cannot convert output to GPU due to CUDA OOM, ' - 'the output is now on CPU, which might cause ' - 'errors if the output need to interact with GPU ' - 'data in subsequent operations') - logger.warning('Cannot convert output to GPU due to ' - 'CUDA OOM, the output is on CPU now.') - - return func(*cpu_args, **cpu_kwargs) - else: - # may still get CUDA OOM error - return func(*args, **kwargs) - - return wrapped - - -# To use AvoidOOM as a decorator -AvoidCUDAOOM = AvoidOOM() diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/misc.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/misc.py deleted file mode 100644 index 4113672ac..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/misc.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os -import os.path as osp -import warnings - -import mmcv -from mmcv.utils import print_log - - -def find_latest_checkpoint(path, suffix='pth'): - """Find the latest checkpoint from the working directory. - - Args: - path(str): The path to find checkpoints. - suffix(str): File extension. - Defaults to pth. - - Returns: - latest_path(str | None): File path of the latest checkpoint. - References: - .. [1] https://github.com/microsoft/SoftTeacher - /blob/main/ssod/utils/patch.py - """ - if not osp.exists(path): - warnings.warn('The path of checkpoints does not exist.') - return None - if osp.exists(osp.join(path, f'latest.{suffix}')): - return osp.join(path, f'latest.{suffix}') - - checkpoints = glob.glob(osp.join(path, f'*.{suffix}')) - if len(checkpoints) == 0: - warnings.warn('There are no checkpoints in the path.') - return None - latest = -1 - latest_path = None - for checkpoint in checkpoints: - count = int(osp.basename(checkpoint).split('_')[-1].split('.')[0]) - if count > latest: - latest = count - latest_path = checkpoint - return latest_path - - -def update_data_root(cfg, logger=None): - """Update data root according to env MMDET_DATASETS. - - If set env MMDET_DATASETS, update cfg.data_root according to - MMDET_DATASETS. Otherwise, using cfg.data_root as default. - - Args: - cfg (mmcv.Config): The model config need to modify - logger (logging.Logger | str | None): the way to print msg - """ - assert isinstance(cfg, mmcv.Config), \ - f'cfg got wrong type: {type(cfg)}, expected mmcv.Config' - - if 'MMDET_DATASETS' in os.environ: - dst_root = os.environ['MMDET_DATASETS'] - print_log(f'MMDET_DATASETS has been set to be {dst_root}.' - f'Using {dst_root} as data root.') - else: - return - - assert isinstance(cfg, mmcv.Config), \ - f'cfg got wrong type: {type(cfg)}, expected mmcv.Config' - - def update(cfg, src_str, dst_str): - for k, v in cfg.items(): - if isinstance(v, mmcv.ConfigDict): - update(cfg[k], src_str, dst_str) - if isinstance(v, str) and src_str in v: - cfg[k] = v.replace(src_str, dst_str) - - update(cfg.data, cfg.data_root, dst_root) - cfg.data_root = dst_root diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/profiling.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/profiling.py deleted file mode 100644 index 2f53f456c..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/profiling.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import contextlib -import sys -import time - -import torch - -if sys.version_info >= (3, 7): - - @contextlib.contextmanager - def profile_time(trace_name, - name, - enabled=True, - stream=None, - end_stream=None): - """Print time spent by CPU and GPU. - - Useful as a temporary context manager to find sweet spots of code - suitable for async implementation. - """ - if (not enabled) or not torch.cuda.is_available(): - yield - return - stream = stream if stream else torch.cuda.current_stream() - end_stream = end_stream if end_stream else stream - start = torch.cuda.Event(enable_timing=True) - end = torch.cuda.Event(enable_timing=True) - stream.record_event(start) - try: - cpu_start = time.monotonic() - yield - finally: - cpu_end = time.monotonic() - end_stream.record_event(end) - end.synchronize() - cpu_time = (cpu_end - cpu_start) * 1000 - gpu_time = start.elapsed_time(end) - msg = f'{trace_name} {name} cpu_time {cpu_time:.2f} ms ' - msg += f'gpu_time {gpu_time:.2f} ms stream {stream}' - print(msg, end_stream) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/replace_cfg_vals.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/replace_cfg_vals.py deleted file mode 100644 index 6ca301dc9..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/replace_cfg_vals.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import re - -from mmcv.utils import Config - - -def replace_cfg_vals(ori_cfg): - """Replace the string "${key}" with the corresponding value. - - Replace the "${key}" with the value of ori_cfg.key in the config. And - support replacing the chained ${key}. Such as, replace "${key0.key1}" - with the value of cfg.key0.key1. Code is modified from `vars.py - < https://github.com/microsoft/SoftTeacher/blob/main/ssod/utils/vars.py>`_ # noqa: E501 - - Args: - ori_cfg (mmcv.utils.config.Config): - The origin config with "${key}" generated from a file. - - Returns: - updated_cfg [mmcv.utils.config.Config]: - The config with "${key}" replaced by the corresponding value. - """ - - def get_value(cfg, key): - for k in key.split('.'): - cfg = cfg[k] - return cfg - - def replace_value(cfg): - if isinstance(cfg, dict): - return {key: replace_value(value) for key, value in cfg.items()} - elif isinstance(cfg, list): - return [replace_value(item) for item in cfg] - elif isinstance(cfg, tuple): - return tuple([replace_value(item) for item in cfg]) - elif isinstance(cfg, str): - # the format of string cfg may be: - # 1) "${key}", which will be replaced with cfg.key directly - # 2) "xxx${key}xxx" or "xxx${key1}xxx${key2}xxx", - # which will be replaced with the string of the cfg.key - keys = pattern_key.findall(cfg) - values = [get_value(ori_cfg, key[2:-1]) for key in keys] - if len(keys) == 1 and keys[0] == cfg: - # the format of string cfg is "${key}" - cfg = values[0] - else: - for key, value in zip(keys, values): - # the format of string cfg is - # "xxx${key}xxx" or "xxx${key1}xxx${key2}xxx" - assert not isinstance(value, (dict, list, tuple)), \ - f'for the format of string cfg is ' \ - f"'xxxxx${key}xxxxx' or 'xxx${key}xxx${key}xxx', " \ - f"the type of the value of '${key}' " \ - f'can not be dict, list, or tuple' \ - f'but you input {type(value)} in {cfg}' - cfg = cfg.replace(key, str(value)) - return cfg - else: - return cfg - - # the pattern of string "${key}" - pattern_key = re.compile(r'\$\{[a-zA-Z\d_.]*\}') - # the type of ori_cfg._cfg_dict is mmcv.utils.config.ConfigDict - updated_cfg = Config( - replace_value(ori_cfg._cfg_dict), filename=ori_cfg.filename) - # replace the model with model_wrapper - if updated_cfg.get('model_wrapper', None) is not None: - updated_cfg.model = updated_cfg.model_wrapper - updated_cfg.pop('model_wrapper') - return updated_cfg diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/setup_env.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/setup_env.py deleted file mode 100644 index 6637cf878..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/setup_env.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import platform -import warnings - -import cv2 -import torch.multiprocessing as mp - - -def setup_multi_processes(cfg): - """Setup multi-processing environment variables.""" - # set multi-process start method as `fork` to speed up the training - if platform.system() != 'Windows': - mp_start_method = cfg.get('mp_start_method', 'fork') - current_method = mp.get_start_method(allow_none=True) - if current_method is not None and current_method != mp_start_method: - warnings.warn( - f'Multi-processing start method `{mp_start_method}` is ' - f'different from the previous setting `{current_method}`.' - f'It will be force set to `{mp_start_method}`. You can change ' - f'this behavior by changing `mp_start_method` in your config.') - mp.set_start_method(mp_start_method, force=True) - - # disable opencv multithreading to avoid system being overloaded - opencv_num_threads = cfg.get('opencv_num_threads', 0) - cv2.setNumThreads(opencv_num_threads) - - # setup OMP threads - # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa - workers_per_gpu = cfg.data.get('workers_per_gpu', 1) - if 'train_dataloader' in cfg.data: - workers_per_gpu = \ - max(cfg.data.train_dataloader.get('workers_per_gpu', 1), - workers_per_gpu) - - if 'OMP_NUM_THREADS' not in os.environ and workers_per_gpu > 1: - omp_num_threads = 1 - warnings.warn( - f'Setting OMP_NUM_THREADS environment variable for each process ' - f'to be {omp_num_threads} in default, to avoid your system being ' - f'overloaded, please further tune the variable for optimal ' - f'performance in your application as needed.') - os.environ['OMP_NUM_THREADS'] = str(omp_num_threads) - - # setup MKL threads - if 'MKL_NUM_THREADS' not in os.environ and workers_per_gpu > 1: - mkl_num_threads = 1 - warnings.warn( - f'Setting MKL_NUM_THREADS environment variable for each process ' - f'to be {mkl_num_threads} in default, to avoid your system being ' - f'overloaded, please further tune the variable for optimal ' - f'performance in your application as needed.') - os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/split_batch.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/split_batch.py deleted file mode 100644 index 0276fb331..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/split_batch.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def split_batch(img, img_metas, kwargs): - """Split data_batch by tags. - - Code is modified from - # noqa: E501 - - Args: - img (Tensor): of shape (N, C, H, W) encoding input images. - Typically these should be mean centered and std scaled. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys, see - :class:`mmdet.datasets.pipelines.Collect`. - kwargs (dict): Specific to concrete implementation. - - Returns: - data_groups (dict): a dict that data_batch splited by tags, - such as 'sup', 'unsup_teacher', and 'unsup_student'. - """ - - # only stack img in the batch - def fuse_list(obj_list, obj): - return torch.stack(obj_list) if isinstance(obj, - torch.Tensor) else obj_list - - # select data with tag from data_batch - def select_group(data_batch, current_tag): - group_flag = [tag == current_tag for tag in data_batch['tag']] - return { - k: fuse_list([vv for vv, gf in zip(v, group_flag) if gf], v) - for k, v in data_batch.items() - } - - kwargs.update({'img': img, 'img_metas': img_metas}) - kwargs.update({'tag': [meta['tag'] for meta in img_metas]}) - tags = list(set(kwargs['tag'])) - data_groups = {tag: select_group(kwargs, tag) for tag in tags} - for tag, group in data_groups.items(): - group.pop('tag') - return data_groups diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/util_distribution.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/util_distribution.py deleted file mode 100644 index a186bf6cb..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/util_distribution.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel - -dp_factory = {'cuda': MMDataParallel, 'cpu': MMDataParallel} - -ddp_factory = {'cuda': MMDistributedDataParallel} - - -def build_dp(model, device='cuda', dim=0, *args, **kwargs): - """build DataParallel module by device type. - - if device is cuda, return a MMDataParallel model; if device is mlu, - return a MLUDataParallel model. - - Args: - model (:class:`nn.Module`): model to be parallelized. - device (str): device type, cuda, cpu or mlu. Defaults to cuda. - dim (int): Dimension used to scatter the data. Defaults to 0. - - Returns: - nn.Module: the model to be parallelized. - """ - if device == 'cuda': - model = model.cuda() - elif device == 'mlu': - from mmcv.device.mlu import MLUDataParallel - dp_factory['mlu'] = MLUDataParallel - model = model.mlu() - - return dp_factory[device](model, dim=dim, *args, **kwargs) - - -def build_ddp(model, device='cuda', *args, **kwargs): - """Build DistributedDataParallel module by device type. - - If device is cuda, return a MMDistributedDataParallel model; - if device is mlu, return a MLUDistributedDataParallel model. - - Args: - model (:class:`nn.Module`): module to be parallelized. - device (str): device type, mlu or cuda. - - Returns: - :class:`nn.Module`: the module to be parallelized - - References: - .. [1] https://pytorch.org/docs/stable/generated/torch.nn.parallel. - DistributedDataParallel.html - """ - assert device in ['cuda', 'mlu'], 'Only available for cuda or mlu devices.' - if device == 'cuda': - model = model.cuda() - elif device == 'mlu': - from mmcv.device.mlu import MLUDistributedDataParallel - ddp_factory['mlu'] = MLUDistributedDataParallel - model = model.mlu() - - return ddp_factory[device](model, *args, **kwargs) - - -def is_mlu_available(): - """Returns a bool indicating if MLU is currently available.""" - return hasattr(torch, 'is_mlu_available') and torch.is_mlu_available() - - -def get_device(): - """Returns an available device, cpu, cuda or mlu.""" - is_device_available = { - 'cuda': torch.cuda.is_available(), - 'mlu': is_mlu_available() - } - device_list = [k for k, v in is_device_available.items() if v] - return device_list[0] if len(device_list) == 1 else 'cpu' diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/util_mixins.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/util_mixins.py deleted file mode 100644 index b83b6617f..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/util_mixins.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This module defines the :class:`NiceRepr` mixin class, which defines a -``__repr__`` and ``__str__`` method that only depend on a custom ``__nice__`` -method, which you must define. This means you only have to overload one -function instead of two. Furthermore, if the object defines a ``__len__`` -method, then the ``__nice__`` method defaults to something sensible, otherwise -it is treated as abstract and raises ``NotImplementedError``. - -To use simply have your object inherit from :class:`NiceRepr` -(multi-inheritance should be ok). - -This code was copied from the ubelt library: https://github.com/Erotemic/ubelt - -Example: - >>> # Objects that define __nice__ have a default __str__ and __repr__ - >>> class Student(NiceRepr): - ... def __init__(self, name): - ... self.name = name - ... def __nice__(self): - ... return self.name - >>> s1 = Student('Alice') - >>> s2 = Student('Bob') - >>> print(f's1 = {s1}') - >>> print(f's2 = {s2}') - s1 = - s2 = - -Example: - >>> # Objects that define __len__ have a default __nice__ - >>> class Group(NiceRepr): - ... def __init__(self, data): - ... self.data = data - ... def __len__(self): - ... return len(self.data) - >>> g = Group([1, 2, 3]) - >>> print(f'g = {g}') - g = -""" -import warnings - - -class NiceRepr: - """Inherit from this class and define ``__nice__`` to "nicely" print your - objects. - - Defines ``__str__`` and ``__repr__`` in terms of ``__nice__`` function - Classes that inherit from :class:`NiceRepr` should redefine ``__nice__``. - If the inheriting class has a ``__len__``, method then the default - ``__nice__`` method will return its length. - - Example: - >>> class Foo(NiceRepr): - ... def __nice__(self): - ... return 'info' - >>> foo = Foo() - >>> assert str(foo) == '' - >>> assert repr(foo).startswith('>> class Bar(NiceRepr): - ... pass - >>> bar = Bar() - >>> import pytest - >>> with pytest.warns(None) as record: - >>> assert 'object at' in str(bar) - >>> assert 'object at' in repr(bar) - - Example: - >>> class Baz(NiceRepr): - ... def __len__(self): - ... return 5 - >>> baz = Baz() - >>> assert str(baz) == '' - """ - - def __nice__(self): - """str: a "nice" summary string describing this module""" - if hasattr(self, '__len__'): - # It is a common pattern for objects to use __len__ in __nice__ - # As a convenience we define a default __nice__ for these objects - return str(len(self)) - else: - # In all other cases force the subclass to overload __nice__ - raise NotImplementedError( - f'Define the __nice__ method for {self.__class__!r}') - - def __repr__(self): - """str: the string of the module""" - try: - nice = self.__nice__() - classname = self.__class__.__name__ - return f'<{classname}({nice}) at {hex(id(self))}>' - except NotImplementedError as ex: - warnings.warn(str(ex), category=RuntimeWarning) - return object.__repr__(self) - - def __str__(self): - """str: the string of the module""" - try: - classname = self.__class__.__name__ - nice = self.__nice__() - return f'<{classname}({nice})>' - except NotImplementedError as ex: - warnings.warn(str(ex), category=RuntimeWarning) - return object.__repr__(self) diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/utils/util_random.py b/cv/instance_segmentation/solo/pytorch/mmdet/utils/util_random.py deleted file mode 100644 index dc1ecb6c0..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/utils/util_random.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Helpers for random number generators.""" -import numpy as np - - -def ensure_rng(rng=None): - """Coerces input into a random number generator. - - If the input is None, then a global random state is returned. - - If the input is a numeric value, then that is used as a seed to construct a - random state. Otherwise the input is returned as-is. - - Adapted from [1]_. - - Args: - rng (int | numpy.random.RandomState | None): - if None, then defaults to the global rng. Otherwise this can be an - integer or a RandomState class - Returns: - (numpy.random.RandomState) : rng - - a numpy random number generator - - References: - .. [1] https://gitlab.kitware.com/computer-vision/kwarray/blob/master/kwarray/util_random.py#L270 # noqa: E501 - """ - - if rng is None: - rng = np.random.mtrand._rand - elif isinstance(rng, int): - rng = np.random.RandomState(rng) - else: - rng = rng - return rng diff --git a/cv/instance_segmentation/solo/pytorch/mmdet/version.py b/cv/instance_segmentation/solo/pytorch/mmdet/version.py deleted file mode 100644 index 56e9b0757..000000000 --- a/cv/instance_segmentation/solo/pytorch/mmdet/version.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -__version__ = '2.25.0' -short_version = __version__ - - -def parse_version_info(version_str): - version_info = [] - for x in version_str.split('.'): - if x.isdigit(): - version_info.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - version_info.append(int(patch_version[0])) - version_info.append(f'rc{patch_version[1]}') - return tuple(version_info) - - -version_info = parse_version_info(__version__) diff --git a/cv/instance_segmentation/solo/pytorch/pytest.ini b/cv/instance_segmentation/solo/pytorch/pytest.ini deleted file mode 100644 index 9796e871e..000000000 --- a/cv/instance_segmentation/solo/pytorch/pytest.ini +++ /dev/null @@ -1,7 +0,0 @@ -[pytest] -addopts = --xdoctest --xdoctest-style=auto -norecursedirs = .git ignore build __pycache__ data docker docs .eggs - -filterwarnings= default - ignore:.*No cfgstr given in Cacher constructor or call.*:Warning - ignore:.*Define the __nice__ method for.*:Warning diff --git a/cv/instance_segmentation/solo/pytorch/requirements.txt b/cv/instance_segmentation/solo/pytorch/requirements.txt deleted file mode 100644 index 683611773..000000000 --- a/cv/instance_segmentation/solo/pytorch/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -addict -yapf -matplotlib -pycocotools -six -terminaltables -opencv-python diff --git a/cv/instance_segmentation/solo/pytorch/setup.py b/cv/instance_segmentation/solo/pytorch/setup.py deleted file mode 100644 index 3cc9f7a46..000000000 --- a/cv/instance_segmentation/solo/pytorch/setup.py +++ /dev/null @@ -1,354 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -import glob -import os -import re -from pkg_resources import DistributionNotFound, get_distribution -from setuptools import find_packages, setup - -EXT_TYPE = '' -try: - import torch - if torch.__version__ == 'parrots': - from parrots.utils.build_extension import BuildExtension - EXT_TYPE = 'parrots' - else: - from torch.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - cmd_class = {'build_ext': BuildExtension} -except ModuleNotFoundError: - cmd_class = {} - print('Skip building ext ops due to the absence of torch.') - - -def choose_requirement(primary, secondary): - """If some version of primary requirement installed, return primary, else - return secondary.""" - try: - name = re.split(r'[!<>=]', primary)[0] - get_distribution(name) - except DistributionNotFound: - return secondary - - return str(primary) - - -def get_version(): - version_file = 'mmcv/version.py' - with open(version_file, 'r', encoding='utf-8') as f: - exec(compile(f.read(), version_file, 'exec')) - version = locals()['__version__'] - local_version_identifier = os.environ.get('MMCV_LOCAL_VERSION_IDENTIFIER', '') - if local_version_identifier != '': - version += '+' + local_version_identifier - return version - - -def parse_requirements(fname='requirements/runtime.txt', with_version=True): - """Parse the package dependencies listed in a requirements file but strips - specific versioning information. - - Args: - fname (str): path to requirements file - with_version (bool, default=False): if True include version specs - - Returns: - List[str]: list of requirements items - - CommandLine: - python -c "import setup; print(setup.parse_requirements())" - """ - import sys - from os.path import exists - require_fpath = fname - - def parse_line(line): - """Parse information from a line in a requirements text file.""" - if line.startswith('-r '): - # Allow specifying requirements in other files - target = line.split(' ')[1] - for info in parse_require_file(target): - yield info - else: - info = {'line': line} - if line.startswith('-e '): - info['package'] = line.split('#egg=')[1] - else: - # Remove versioning from the package - pat = '(' + '|'.join(['>=', '==', '>']) + ')' - parts = re.split(pat, line, maxsplit=1) - parts = [p.strip() for p in parts] - - info['package'] = parts[0] - if len(parts) > 1: - op, rest = parts[1:] - if ';' in rest: - # Handle platform specific dependencies - # http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies - version, platform_deps = map(str.strip, - rest.split(';')) - info['platform_deps'] = platform_deps - else: - version = rest # NOQA - info['version'] = (op, version) - yield info - - def parse_require_file(fpath): - with open(fpath, 'r') as f: - for line in f.readlines(): - line = line.strip() - if line and not line.startswith('#'): - for info in parse_line(line): - yield info - - def gen_packages_items(): - if exists(require_fpath): - for info in parse_require_file(require_fpath): - parts = [info['package']] - if with_version and 'version' in info: - parts.extend(info['version']) - if not sys.version.startswith('3.4'): - # apparently package_deps are broken in 3.4 - platform_deps = info.get('platform_deps') - if platform_deps is not None: - parts.append(';' + platform_deps) - item = ''.join(parts) - yield item - - packages = list(gen_packages_items()) - return packages - - -install_requires = parse_requirements() - -try: - # OpenCV installed via conda. - import cv2 # NOQA: F401 - major, minor, *rest = cv2.__version__.split('.') - if int(major) < 3: - raise RuntimeError( - f'OpenCV >=3 is required but {cv2.__version__} is installed') -except ImportError: - # If first not installed install second package - CHOOSE_INSTALL_REQUIRES = [('opencv-python-headless>=3', - 'opencv-python>=3')] - for main, secondary in CHOOSE_INSTALL_REQUIRES: - install_requires.append(choose_requirement(main, secondary)) - - -def get_extensions(): - extensions = [] - - if os.getenv('MMCV_WITH_TRT', '0') != '0': - ext_name = 'mmcv._ext_trt' - from torch.utils.cpp_extension import include_paths, library_paths - library_dirs = [] - libraries = [] - include_dirs = [] - tensorrt_path = os.getenv('TENSORRT_DIR', '0') - tensorrt_lib_path = glob.glob( - os.path.join(tensorrt_path, 'targets', '*', 'lib'))[0] - library_dirs += [tensorrt_lib_path] - libraries += ['nvinfer', 'nvparsers', 'nvinfer_plugin'] - libraries += ['cudart'] - define_macros = [] - extra_compile_args = {'cxx': []} - - include_path = os.path.abspath('./mmcv/ops/csrc/common/cuda') - include_trt_path = os.path.abspath('./mmcv/ops/csrc/tensorrt') - include_dirs.append(include_path) - include_dirs.append(include_trt_path) - include_dirs.append(os.path.join(tensorrt_path, 'include')) - include_dirs += include_paths(cuda=True) - - op_files = glob.glob('./mmcv/ops/csrc/tensorrt/plugins/*') - define_macros += [('MMCV_WITH_CUDA', None)] - define_macros += [('MMCV_WITH_TRT', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - library_dirs += library_paths(cuda=True) - - from setuptools import Extension - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - language='c++', - library_dirs=library_dirs, - libraries=libraries) - extensions.append(ext_ops) - - if os.getenv('MMCV_WITH_OPS', '0') == '0': - return extensions - - if EXT_TYPE == 'parrots': - ext_name = 'mmcv._ext' - from parrots.utils.build_extension import Extension - # new parrots op impl do not use MMCV_USE_PARROTS - # define_macros = [('MMCV_USE_PARROTS', None)] - define_macros = [] - include_dirs = [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') +\ - glob.glob('./mmcv/ops/csrc/parrots/*.cpp') - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args = { - 'nvcc': [cuda_args] if cuda_args else [], - 'cxx': [], - } - if torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - extra_compile_args['nvcc'] += [ - '-D__CUDA_NO_HALF_OPERATORS__', - '-D__CUDA_NO_HALF_CONVERSIONS__', - '-D__CUDA_NO_HALF2_OPERATORS__', - ] - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - cuda=True, - pytorch=True) - extensions.append(ext_ops) - elif EXT_TYPE == 'pytorch': - ext_name = 'mmcv._ext' - from torch.utils.cpp_extension import CppExtension, CUDAExtension - - # prevent ninja from using too many resources - try: - import psutil - num_cpu = len(psutil.Process().cpu_affinity()) - cpu_use = max(4, num_cpu - 1) - except (ModuleNotFoundError, AttributeError): - cpu_use = 4 - - os.environ.setdefault('MAX_JOBS', str(cpu_use)) - define_macros = [] - extra_compile_args = {'cxx': []} - include_dirs = [] - - is_rocm_pytorch = False - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm_pytorch = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - - project_dir = 'mmcv/ops/csrc/' - if is_rocm_pytorch: - from torch.utils.hipify import hipify_python - - hipify_python.hipify( - project_directory=project_dir, - output_directory=project_dir, - includes='mmcv/ops/csrc/*', - show_detailed=True, - is_pytorch_extension=True, - ) - define_macros += [('MMCV_WITH_CUDA', None)] - define_macros += [('HIP_DIFF', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/hip/*') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/hip')) - elif torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - else: - print(f'Compiling {ext_name} without CUDA') - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') - extension = CppExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - - ext_ops = extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args) - extensions.append(ext_ops) - - if EXT_TYPE == 'pytorch' and os.getenv('MMCV_WITH_ORT', '0') != '0': - ext_name = 'mmcv._ext_ort' - from torch.utils.cpp_extension import library_paths, include_paths - import onnxruntime - library_dirs = [] - libraries = [] - include_dirs = [] - ort_path = os.getenv('ONNXRUNTIME_DIR', '0') - library_dirs += [os.path.join(ort_path, 'lib')] - libraries.append('onnxruntime') - define_macros = [] - extra_compile_args = {'cxx': []} - - include_path = os.path.abspath('./mmcv/ops/csrc/onnxruntime') - include_dirs.append(include_path) - include_dirs.append(os.path.join(ort_path, 'include')) - - op_files = glob.glob('./mmcv/ops/csrc/onnxruntime/cpu/*') - if onnxruntime.get_device() == 'GPU' or os.getenv('FORCE_CUDA', - '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files += glob.glob('./mmcv/ops/csrc/onnxruntime/gpu/*') - include_dirs += include_paths(cuda=True) - library_dirs += library_paths(cuda=True) - else: - include_dirs += include_paths(cuda=False) - library_dirs += library_paths(cuda=False) - - from setuptools import Extension - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - language='c++', - library_dirs=library_dirs, - libraries=libraries) - extensions.append(ext_ops) - - return extensions - - -setup( - name='mmcv' if os.getenv('MMCV_WITH_OPS', '0') == '0' else 'mmcv-full', - version=get_version(), - description='OpenMMLab Computer Vision Foundation', - keywords='computer vision', - packages=find_packages(), - include_package_data=True, - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Topic :: Utilities', - ], - url='https://github.com/open-mmlab/mmcv', - author='MMCV Contributors', - author_email='openmmlab@gmail.com', - setup_requires=[], - tests_require=['pytest'], - install_requires=install_requires, - ext_modules=get_extensions(), - cmdclass=cmd_class, - zip_safe=False) diff --git a/cv/instance_segmentation/solo/pytorch/train.py b/cv/instance_segmentation/solo/pytorch/train.py deleted file mode 100644 index ab3e60c62..000000000 --- a/cv/instance_segmentation/solo/pytorch/train.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import copy -import os -import os.path as osp -import time -import warnings - -import mmcv -import torch -import torch.distributed as dist -from mmcv import Config, DictAction -from mmcv.runner import get_dist_info, init_dist -from mmcv.utils import get_git_hash -import sys -sys.path.append('./') -from mmdet import __version__ -from mmdet.apis import init_random_seed, set_random_seed, train_detector -from mmdet.datasets import build_dataset -from mmdet.models import build_detector -from mmdet.utils import (collect_env, get_device, get_root_logger, - replace_cfg_vals, setup_multi_processes, - update_data_root) - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train a detector') - parser.add_argument('config', help='train config file path') - parser.add_argument('--work-dir', help='the dir to save logs and models') - parser.add_argument( - '--resume-from', help='the checkpoint file to resume from') - parser.add_argument( - '--auto-resume', - action='store_true', - help='resume from the latest checkpoint automatically') - parser.add_argument( - '--no-validate', - action='store_true', - help='whether not to evaluate the checkpoint during training') - group_gpus = parser.add_mutually_exclusive_group() - group_gpus.add_argument( - '--gpus', - type=int, - help='(Deprecated, please use --gpu-id) number of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-ids', - type=int, - nargs='+', - help='(Deprecated, please use --gpu-id) ids of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed training)') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--diff-seed', - action='store_true', - help='Whether or not set different seeds for different ranks') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file (deprecate), ' - 'change to --cfg-options instead.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - parser.add_argument( - '--auto-scale-lr', - action='store_true', - help='enable automatically scaling LR.') - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - - # replace the ${key} with the value of cfg.key - cfg = replace_cfg_vals(cfg) - - # update data root according to MMDET_DATASETS - update_data_root(cfg) - - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - if args.auto_scale_lr: - if 'auto_scale_lr' in cfg and \ - 'enable' in cfg.auto_scale_lr and \ - 'base_batch_size' in cfg.auto_scale_lr: - cfg.auto_scale_lr.enable = True - else: - warnings.warn('Can not find "auto_scale_lr" or ' - '"auto_scale_lr.enable" or ' - '"auto_scale_lr.base_batch_size" in your' - ' configuration file. Please update all the ' - 'configuration files to mmdet >= 2.24.1.') - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - - # work_dir is determined in this priority: CLI > segment in file > filename - if args.work_dir is not None: - # update configs according to CLI args if args.work_dir is not None - cfg.work_dir = args.work_dir - elif cfg.get('work_dir', None) is None: - # use config filename as default work_dir if cfg.work_dir is None - cfg.work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - - if args.resume_from is not None: - cfg.resume_from = args.resume_from - cfg.auto_resume = args.auto_resume - if args.gpus is not None: - cfg.gpu_ids = range(1) - warnings.warn('`--gpus` is deprecated because we only support ' - 'single GPU mode in non-distributed training. ' - 'Use `gpus=1` now.') - if args.gpu_ids is not None: - cfg.gpu_ids = args.gpu_ids[0:1] - warnings.warn('`--gpu-ids` is deprecated, please use `--gpu-id`. ' - 'Because we only support single GPU mode in ' - 'non-distributed training. Use the first GPU ' - 'in `gpu_ids` now.') - if args.gpus is None and args.gpu_ids is None: - cfg.gpu_ids = [args.gpu_id] - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - # re-set gpu_ids with distributed training mode - _, world_size = get_dist_info() - cfg.gpu_ids = range(world_size) - - # create work_dir - mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) - # dump config - cfg.dump(osp.join(cfg.work_dir, osp.basename(args.config))) - # init the logger before other steps - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - log_file = osp.join(cfg.work_dir, f'{timestamp}.log') - logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) - - # init the meta dict to record some important information such as - # environment info and seed, which will be logged - meta = dict() - # log env info - env_info_dict = collect_env() - env_info = '\n'.join([(f'{k}: {v}') for k, v in env_info_dict.items()]) - dash_line = '-' * 60 + '\n' - logger.info('Environment info:\n' + dash_line + env_info + '\n' + - dash_line) - meta['env_info'] = env_info - meta['config'] = cfg.pretty_text - # log some basic info - logger.info(f'Distributed training: {distributed}') - logger.info(f'Config:\n{cfg.pretty_text}') - - cfg.device = get_device() - # set random seeds - seed = init_random_seed(args.seed, device=cfg.device) - seed = seed + dist.get_rank() if args.diff_seed else seed - logger.info(f'Set random seed to {seed}, ' - f'deterministic: {args.deterministic}') - set_random_seed(seed, deterministic=args.deterministic) - cfg.seed = seed - meta['seed'] = seed - meta['exp_name'] = osp.basename(args.config) - - model = build_detector( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')) - model.init_weights() - - datasets = [build_dataset(cfg.data.train)] - if len(cfg.workflow) == 2: - val_dataset = copy.deepcopy(cfg.data.val) - val_dataset.pipeline = cfg.data.train.pipeline - datasets.append(build_dataset(val_dataset)) - if cfg.checkpoint_config is not None: - # save mmdet version, config file content and class names in - # checkpoints as meta data - cfg.checkpoint_config.meta = dict( - mmdet_version=__version__ + get_git_hash()[:7], - CLASSES=datasets[0].CLASSES) - # add an attribute for visualization convenience - model.CLASSES = datasets[0].CLASSES - train_detector( - model, - datasets, - cfg, - distributed=distributed, - validate=(not args.no_validate), - timestamp=timestamp, - meta=meta) - - -if __name__ == '__main__': - main() diff --git a/cv/instance_segmentation/solo/pytorch/train.sh b/cv/instance_segmentation/solo/pytorch/train.sh deleted file mode 100644 index 14957276d..000000000 --- a/cv/instance_segmentation/solo/pytorch/train.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -python3 train.py configs/solo/solo_r50_fpn_1x_coco.py \ No newline at end of file diff --git a/cv/instance_segmentation/solo/pytorch/train_dist.sh b/cv/instance_segmentation/solo/pytorch/train_dist.sh deleted file mode 100644 index fd762b216..000000000 --- a/cv/instance_segmentation/solo/pytorch/train_dist.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -CONFIG=$1 -GPUS=$2 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/train.py \ - $CONFIG \ - --seed 0 \ - --launcher pytorch ${@:3} diff --git a/cv/instance_segmentation/solov2/pytorch/README.md b/cv/instance_segmentation/solov2/pytorch/README.md index c396ebfab..ccc8e3a32 100644 --- a/cv/instance_segmentation/solov2/pytorch/README.md +++ b/cv/instance_segmentation/solov2/pytorch/README.md @@ -75,5 +75,4 @@ bash tools/dist_train.sh configs/solov2/solov2_r50_fpn_1x_coco.py 8 | BI-V100 x8 | 21.26 images/s | ## Reference - -- [mmdetection](https://github.com/open-mmlab/mmdetection/tree/v3.2.0/configs/solov2) +[mmdetection](https://github.com/open-mmlab/mmdetection) -- Gitee From eadc8727a5ca4b33ee0caa7d149a15f8566fcfbb Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Wed, 5 Mar 2025 17:43:04 +0800 Subject: [PATCH 04/15] update attunet --- .../att_unet/pytorch/.gitignore | 120 -- .../att_unet/pytorch/CITATION.cff | 8 - .../att_unet/pytorch/LICENSE | 203 --- .../att_unet/pytorch/README.md | 107 +- .../pytorch/configs/_base_/datasets/ade20k.py | 54 - .../configs/_base_/datasets/ade20k_640x640.py | 54 - .../configs/_base_/datasets/cityscapes.py | 54 - .../_base_/datasets/cityscapes_1024x1024.py | 35 - .../_base_/datasets/cityscapes_768x768.py | 35 - .../_base_/datasets/cityscapes_769x769.py | 35 - .../_base_/datasets/cityscapes_832x832.py | 35 - .../configs/_base_/datasets/coco-stuff10k.py | 57 - .../configs/_base_/datasets/coco-stuff164k.py | 54 - .../configs/_base_/datasets/pascal_context.py | 60 - .../_base_/datasets/pascal_context_59.py | 60 - .../configs/_base_/datasets/pascal_voc12.py | 58 - .../_base_/datasets/pascal_voc12_aug.py | 9 - .../pytorch/configs/_base_/default_runtime.py | 14 - .../configs/_base_/models/attunet_r34.py | 45 - .../configs/_base_/schedules/schedule_160k.py | 9 - .../configs/_base_/schedules/schedule_1k.py | 9 - .../configs/_base_/schedules/schedule_20k.py | 9 - .../configs/_base_/schedules/schedule_320k.py | 9 - .../configs/_base_/schedules/schedule_40k.py | 9 - .../configs/_base_/schedules/schedule_80k.py | 9 - .../attunet_r34_512x1024_160k_cityscapes.py | 6 - .../att_unet/pytorch/docker/Dockerfile | 32 - .../att_unet/pytorch/docker/serve/Dockerfile | 49 - .../pytorch/docker/serve/config.properties | 5 - .../pytorch/docker/serve/entrypoint.sh | 12 - .../att_unet/pytorch/mmcv/__init__.py | 13 - .../att_unet/pytorch/mmcv/cnn/__init__.py | 21 - .../pytorch/mmcv/cnn/bricks/__init__.py | 35 - .../pytorch/mmcv/cnn/bricks/activation.py | 93 -- .../pytorch/mmcv/cnn/bricks/context_block.py | 125 -- .../att_unet/pytorch/mmcv/cnn/bricks/conv.py | 44 - .../cnn/bricks/conv2d_adaptive_padding.py | 62 - .../pytorch/mmcv/cnn/bricks/conv_module.py | 206 --- .../pytorch/mmcv/cnn/bricks/conv_ws.py | 148 -- .../bricks/depthwise_separable_conv_module.py | 96 -- .../att_unet/pytorch/mmcv/cnn/bricks/drop.py | 65 - .../mmcv/cnn/bricks/generalized_attention.py | 412 ----- .../pytorch/mmcv/cnn/bricks/hsigmoid.py | 46 - .../pytorch/mmcv/cnn/bricks/hswish.py | 38 - .../pytorch/mmcv/cnn/bricks/non_local.py | 306 ---- .../att_unet/pytorch/mmcv/cnn/bricks/norm.py | 144 -- .../pytorch/mmcv/cnn/bricks/padding.py | 36 - .../pytorch/mmcv/cnn/bricks/plugin.py | 89 -- .../pytorch/mmcv/cnn/bricks/registry.py | 16 - .../att_unet/pytorch/mmcv/cnn/bricks/scale.py | 21 - .../att_unet/pytorch/mmcv/cnn/bricks/swish.py | 25 - .../pytorch/mmcv/cnn/bricks/transformer.py | 944 ------------ .../pytorch/mmcv/cnn/bricks/upsample.py | 84 -- .../pytorch/mmcv/cnn/bricks/wrappers.py | 180 --- .../att_unet/pytorch/mmcv/cnn/builder.py | 30 - .../pytorch/mmcv/cnn/utils/__init__.py | 19 - .../pytorch/mmcv/cnn/utils/flops_counter.py | 599 -------- .../pytorch/mmcv/cnn/utils/fuse_conv_bn.py | 59 - .../pytorch/mmcv/cnn/utils/sync_bn.py | 60 - .../pytorch/mmcv/cnn/utils/weight_init.py | 685 --------- .../att_unet/pytorch/mmcv/device/__init__.py | 4 - .../pytorch/mmcv/device/ipu/__init__.py | 14 - .../pytorch/mmcv/device/ipu/dataloader.py | 157 -- .../device/ipu/hierarchical_data_manager.py | 243 --- .../pytorch/mmcv/device/ipu/hook_wrapper.py | 105 -- .../pytorch/mmcv/device/ipu/model_wrapper.py | 721 --------- .../pytorch/mmcv/device/ipu/runner.py | 142 -- .../att_unet/pytorch/mmcv/device/ipu/utils.py | 244 --- .../pytorch/mmcv/device/mlu/__init__.py | 9 - .../pytorch/mmcv/device/mlu/_functions.py | 22 - .../pytorch/mmcv/device/mlu/data_parallel.py | 41 - .../pytorch/mmcv/device/mlu/distributed.py | 20 - .../pytorch/mmcv/device/mlu/scatter_gather.py | 59 - .../att_unet/pytorch/mmcv/engine/__init__.py | 8 - .../att_unet/pytorch/mmcv/engine/test.py | 202 --- .../att_unet/pytorch/mmcv/fileio/__init__.py | 11 - .../pytorch/mmcv/fileio/file_client.py | 1163 -------------- .../pytorch/mmcv/fileio/handlers/__init__.py | 7 - .../pytorch/mmcv/fileio/handlers/base.py | 30 - .../mmcv/fileio/handlers/json_handler.py | 36 - .../mmcv/fileio/handlers/pickle_handler.py | 28 - .../mmcv/fileio/handlers/yaml_handler.py | 25 - .../att_unet/pytorch/mmcv/fileio/io.py | 151 -- .../att_unet/pytorch/mmcv/fileio/parse.py | 97 -- .../att_unet/pytorch/mmcv/image/__init__.py | 28 - .../att_unet/pytorch/mmcv/image/colorspace.py | 306 ---- .../att_unet/pytorch/mmcv/image/geometric.py | 741 --------- .../att_unet/pytorch/mmcv/image/io.py | 314 ---- .../att_unet/pytorch/mmcv/image/misc.py | 53 - .../pytorch/mmcv/image/photometric.py | 428 ------ .../pytorch/mmcv/model_zoo/deprecated.json | 6 - .../pytorch/mmcv/model_zoo/mmcls.json | 59 - .../pytorch/mmcv/model_zoo/open_mmlab.json | 50 - .../mmcv/model_zoo/torchvision_0.12.json | 57 - .../att_unet/pytorch/mmcv/ops/__init__.py | 12 - .../att_unet/pytorch/mmcv/ops/cc_attention.py | 84 -- .../att_unet/pytorch/mmcv/ops/csrc/README.md | 170 --- .../csrc/common/cuda/common_cuda_helper.hpp | 120 -- .../csrc/common/cuda/psamask_cuda_kernel.cuh | 141 -- .../cuda/sigmoid_focal_loss_cuda_kernel.cuh | 71 - .../cuda/softmax_focal_loss_cuda_kernel.cuh | 72 - .../csrc/common/cuda/sync_bn_cuda_kernel.cuh | 331 ---- .../ops/csrc/common/pytorch_cpp_helper.hpp | 27 - .../ops/csrc/common/pytorch_cuda_helper.hpp | 19 - .../csrc/common/pytorch_device_registry.hpp | 141 -- .../mmcv/ops/csrc/pytorch/cuda/cudabind.cpp | 210 --- .../ops/csrc/pytorch/cuda/focal_loss_cuda.cu | 111 -- .../ops/csrc/pytorch/cuda/psamask_cuda.cu | 60 - .../ops/csrc/pytorch/cuda/sync_bn_cuda.cu | 110 -- .../mmcv/ops/csrc/pytorch/focal_loss.cpp | 53 - .../pytorch/mmcv/ops/csrc/pytorch/info.cpp | 56 - .../pytorch/mmcv/ops/csrc/pytorch/psamask.cpp | 41 - .../pytorch/mmcv/ops/csrc/pytorch/pybind.cpp | 106 -- .../pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp | 69 - .../pytorch/mmcv/ops/deprecated_wrappers.py | 46 - .../att_unet/pytorch/mmcv/ops/focal_loss.py | 213 --- .../att_unet/pytorch/mmcv/ops/info.py | 36 - .../att_unet/pytorch/mmcv/ops/point_sample.py | 346 ----- .../att_unet/pytorch/mmcv/ops/psa_mask.py | 92 -- .../att_unet/pytorch/mmcv/ops/sync_bn.py | 279 ---- .../pytorch/mmcv/parallel/__init__.py | 13 - .../pytorch/mmcv/parallel/_functions.py | 76 - .../att_unet/pytorch/mmcv/parallel/collate.py | 84 -- .../pytorch/mmcv/parallel/data_container.py | 89 -- .../pytorch/mmcv/parallel/data_parallel.py | 97 -- .../pytorch/mmcv/parallel/distributed.py | 138 -- .../mmcv/parallel/distributed_deprecated.py | 70 - .../pytorch/mmcv/parallel/registry.py | 8 - .../pytorch/mmcv/parallel/scatter_gather.py | 59 - .../att_unet/pytorch/mmcv/parallel/utils.py | 20 - .../att_unet/pytorch/mmcv/runner/__init__.py | 73 - .../pytorch/mmcv/runner/base_module.py | 208 --- .../pytorch/mmcv/runner/base_runner.py | 544 ------- .../att_unet/pytorch/mmcv/runner/builder.py | 24 - .../pytorch/mmcv/runner/checkpoint.py | 759 ---------- .../mmcv/runner/default_constructor.py | 45 - .../pytorch/mmcv/runner/dist_utils.py | 204 --- .../pytorch/mmcv/runner/epoch_based_runner.py | 188 --- .../pytorch/mmcv/runner/fp16_utils.py | 423 ------ .../pytorch/mmcv/runner/hooks/__init__.py | 48 - .../pytorch/mmcv/runner/hooks/checkpoint.py | 167 --- .../pytorch/mmcv/runner/hooks/closure.py | 11 - .../att_unet/pytorch/mmcv/runner/hooks/ema.py | 89 -- .../pytorch/mmcv/runner/hooks/evaluation.py | 511 ------- .../pytorch/mmcv/runner/hooks/hook.py | 92 -- .../pytorch/mmcv/runner/hooks/iter_timer.py | 31 - .../mmcv/runner/hooks/logger/__init__.py | 18 - .../pytorch/mmcv/runner/hooks/logger/base.py | 167 --- .../mmcv/runner/hooks/logger/clearml.py | 62 - .../mmcv/runner/hooks/logger/dvclive.py | 68 - .../mmcv/runner/hooks/logger/mlflow.py | 80 - .../mmcv/runner/hooks/logger/neptune.py | 88 -- .../pytorch/mmcv/runner/hooks/logger/pavi.py | 132 -- .../mmcv/runner/hooks/logger/segmind.py | 49 - .../mmcv/runner/hooks/logger/tensorboard.py | 69 - .../pytorch/mmcv/runner/hooks/logger/text.py | 256 ---- .../pytorch/mmcv/runner/hooks/logger/wandb.py | 107 -- .../pytorch/mmcv/runner/hooks/lr_updater.py | 730 --------- .../pytorch/mmcv/runner/hooks/memory.py | 25 - .../mmcv/runner/hooks/momentum_updater.py | 566 ------- .../pytorch/mmcv/runner/hooks/optimizer.py | 556 ------- .../pytorch/mmcv/runner/hooks/profiler.py | 180 --- .../pytorch/mmcv/runner/hooks/sampler_seed.py | 20 - .../pytorch/mmcv/runner/hooks/sync_buffer.py | 22 - .../pytorch/mmcv/runner/iter_based_runner.py | 273 ---- .../pytorch/mmcv/runner/log_buffer.py | 41 - .../pytorch/mmcv/runner/optimizer/__init__.py | 9 - .../pytorch/mmcv/runner/optimizer/builder.py | 44 - .../runner/optimizer/default_constructor.py | 250 --- .../att_unet/pytorch/mmcv/runner/priority.py | 60 - .../att_unet/pytorch/mmcv/runner/utils.py | 93 -- .../att_unet/pytorch/mmcv/utils/__init__.py | 78 - .../att_unet/pytorch/mmcv/utils/config.py | 719 --------- .../pytorch/mmcv/utils/device_type.py | 24 - .../att_unet/pytorch/mmcv/utils/env.py | 120 -- .../att_unet/pytorch/mmcv/utils/ext_loader.py | 72 - .../att_unet/pytorch/mmcv/utils/hub.py | 131 -- .../att_unet/pytorch/mmcv/utils/logging.py | 111 -- .../att_unet/pytorch/mmcv/utils/misc.py | 377 ----- .../pytorch/mmcv/utils/parrots_jit.py | 41 - .../pytorch/mmcv/utils/parrots_wrapper.py | 114 -- .../att_unet/pytorch/mmcv/utils/path.py | 101 -- .../pytorch/mmcv/utils/progressbar.py | 208 --- .../att_unet/pytorch/mmcv/utils/registry.py | 337 ----- .../att_unet/pytorch/mmcv/utils/seed.py | 23 - .../att_unet/pytorch/mmcv/utils/testing.py | 141 -- .../att_unet/pytorch/mmcv/utils/timer.py | 118 -- .../att_unet/pytorch/mmcv/utils/trace.py | 24 - .../pytorch/mmcv/utils/version_utils.py | 90 -- .../att_unet/pytorch/mmcv/version.py | 35 - .../att_unet/pytorch/mmseg/__init__.py | 62 - .../att_unet/pytorch/mmseg/apis/__init__.py | 11 - .../att_unet/pytorch/mmseg/apis/inference.py | 136 -- .../att_unet/pytorch/mmseg/apis/test.py | 233 --- .../att_unet/pytorch/mmseg/apis/train.py | 196 --- .../att_unet/pytorch/mmseg/core/__init__.py | 11 - .../att_unet/pytorch/mmseg/core/builder.py | 33 - .../pytorch/mmseg/core/evaluation/__init__.py | 11 - .../mmseg/core/evaluation/class_names.py | 316 ---- .../mmseg/core/evaluation/eval_hooks.py | 128 -- .../pytorch/mmseg/core/evaluation/metrics.py | 395 ----- .../pytorch/mmseg/core/optimizers/__init__.py | 7 - .../layer_decay_optimizer_constructor.py | 208 --- .../pytorch/mmseg/core/seg/__init__.py | 5 - .../pytorch/mmseg/core/seg/builder.py | 9 - .../mmseg/core/seg/sampler/__init__.py | 5 - .../core/seg/sampler/base_pixel_sampler.py | 13 - .../core/seg/sampler/ohem_pixel_sampler.py | 85 -- .../pytorch/mmseg/core/utils/__init__.py | 5 - .../pytorch/mmseg/core/utils/dist_util.py | 46 - .../att_unet/pytorch/mmseg/core/utils/misc.py | 18 - .../pytorch/mmseg/datasets/__init__.py | 10 - .../att_unet/pytorch/mmseg/datasets/ade.py | 167 --- .../pytorch/mmseg/datasets/builder.py | 191 --- .../pytorch/mmseg/datasets/cityscapes.py | 214 --- .../pytorch/mmseg/datasets/coco_stuff.py | 94 -- .../att_unet/pytorch/mmseg/datasets/custom.py | 487 ------ .../mmseg/datasets/dataset_wrappers.py | 277 ---- .../pytorch/mmseg/datasets/pascal_context.py | 103 -- .../mmseg/datasets/pipelines/__init__.py | 19 - .../mmseg/datasets/pipelines/compose.py | 52 - .../mmseg/datasets/pipelines/formating.py | 9 - .../mmseg/datasets/pipelines/formatting.py | 289 ---- .../mmseg/datasets/pipelines/loading.py | 158 -- .../mmseg/datasets/pipelines/test_time_aug.py | 134 -- .../mmseg/datasets/pipelines/transforms.py | 1335 ----------------- .../mmseg/datasets/samplers/__init__.py | 4 - .../datasets/samplers/distributed_sampler.py | 71 - .../att_unet/pytorch/mmseg/datasets/voc.py | 39 - .../att_unet/pytorch/mmseg/models/__init__.py | 13 - .../mmseg/models/backbones/__init__.py | 3 - .../pytorch/mmseg/models/backbones/resnet.py | 714 --------- .../att_unet/pytorch/mmseg/models/builder.py | 49 - .../mmseg/models/decode_heads/__init__.py | 4 - .../mmseg/models/decode_heads/attunet_head.py | 124 -- .../mmseg/models/decode_heads/decode_head.py | 266 ---- .../pytorch/mmseg/models/losses/__init__.py | 15 - .../pytorch/mmseg/models/losses/accuracy.py | 92 -- .../mmseg/models/losses/cross_entropy_loss.py | 296 ---- .../pytorch/mmseg/models/losses/dice_loss.py | 137 -- .../pytorch/mmseg/models/losses/focal_loss.py | 327 ---- .../mmseg/models/losses/lovasz_loss.py | 323 ---- .../pytorch/mmseg/models/losses/utils.py | 126 -- .../pytorch/mmseg/models/necks/__init__.py | 11 - .../mmseg/models/necks/featurepyramid.py | 67 - .../pytorch/mmseg/models/necks/fpn.py | 213 --- .../pytorch/mmseg/models/necks/ic_neck.py | 148 -- .../pytorch/mmseg/models/necks/jpu.py | 131 -- .../pytorch/mmseg/models/necks/mla_neck.py | 118 -- .../mmseg/models/necks/multilevel_neck.py | 78 - .../mmseg/models/segmentors/__init__.py | 6 - .../pytorch/mmseg/models/segmentors/base.py | 291 ---- .../segmentors/cascade_encoder_decoder.py | 88 -- .../models/segmentors/encoder_decoder.py | 284 ---- .../pytorch/mmseg/models/utils/__init__.py | 18 - .../pytorch/mmseg/models/utils/embed.py | 330 ---- .../mmseg/models/utils/inverted_residual.py | 213 --- .../mmseg/models/utils/make_divisible.py | 28 - .../pytorch/mmseg/models/utils/res_layer.py | 96 -- .../pytorch/mmseg/models/utils/se_layer.py | 58 - .../models/utils/self_attention_block.py | 160 -- .../mmseg/models/utils/shape_convert.py | 107 -- .../mmseg/models/utils/up_conv_block.py | 102 -- .../pytorch/mmseg/models/utils/wrappers.py | 51 - .../att_unet/pytorch/mmseg/ops/__init__.py | 5 - .../att_unet/pytorch/mmseg/ops/encoding.py | 75 - .../att_unet/pytorch/mmseg/ops/wrappers.py | 51 - .../pytorch/mmseg/registry/__init__.py | 15 - .../pytorch/mmseg/registry/registry.py | 116 -- .../att_unet/pytorch/mmseg/utils/__init__.py | 10 - .../pytorch/mmseg/utils/collect_env.py | 18 - .../att_unet/pytorch/mmseg/utils/logger.py | 28 - .../att_unet/pytorch/mmseg/utils/misc.py | 41 - .../att_unet/pytorch/mmseg/utils/set_env.py | 55 - .../att_unet/pytorch/mmseg/version.py | 18 - .../att_unet/pytorch/requirements.txt | 4 - .../att_unet/pytorch/requirements/apcnet.txt | 4 - .../pytorch/requirements/mmcv/build.txt | 1 - .../pytorch/requirements/mmcv/docs.txt | 8 - .../pytorch/requirements/mmcv/optional.txt | 1 - .../requirements/mmcv/requirements.txt | 4 - .../pytorch/requirements/mmcv/runtime.txt | 7 - .../pytorch/requirements/mmcv/test.txt | 9 - .../pytorch/requirements/mminstall.txt | 2 - .../pytorch/requirements/optional.txt | 1 - .../att_unet/pytorch/requirements/runtime.txt | 5 - .../att_unet/pytorch/setup.py | 258 ---- .../att_unet/pytorch/tools/analyze_logs.py | 128 -- .../att_unet/pytorch/tools/benchmark.py | 120 -- .../pytorch/tools/confusion_matrix.py | 184 --- .../tools/convert_datasets/cityscapes.py | 56 - .../tools/convert_datasets/coco_stuff10k.py | 307 ---- .../tools/convert_datasets/coco_stuff164k.py | 264 ---- .../tools/convert_datasets/pascal_context.py | 87 -- .../pytorch/tools/convert_datasets/voc_aug.py | 92 -- .../att_unet/pytorch/tools/get_flops.py | 60 - .../att_unet/pytorch/tools/print_config.py | 69 - .../att_unet/pytorch/tools/slurm_test.sh | 24 - .../att_unet/pytorch/tools/slurm_train.sh | 23 - .../att_unet/pytorch/tools/test.py | 319 ---- .../att_unet/pytorch/train.py | 243 --- .../att_unet/pytorch/train_dist.sh | 19 - 302 files changed, 20 insertions(+), 38837 deletions(-) delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/.gitignore delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/CITATION.cff delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/LICENSE delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/ade20k.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/ade20k_640x640.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_768x768.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_769x769.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_832x832.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/coco-stuff10k.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/coco-stuff164k.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_context_59.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_voc12.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/default_runtime.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/models/attunet_r34.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_160k.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_1k.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_20k.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_320k.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_40k.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_80k.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/configs/att_unet/attunet_r34_512x1024_160k_cityscapes.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/docker/Dockerfile delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/docker/serve/Dockerfile delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/docker/serve/config.properties delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/docker/serve/entrypoint.sh delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/activation.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/context_block.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv_module.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv_ws.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/drop.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/generalized_attention.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/hsigmoid.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/hswish.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/non_local.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/norm.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/padding.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/plugin.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/registry.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/scale.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/swish.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/transformer.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/upsample.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/wrappers.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/builder.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/flops_counter.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/sync_bn.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/weight_init.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/device/__init__.py delete mode 100755 cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/__init__.py delete mode 100755 cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/dataloader.py delete mode 100755 cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py delete mode 100755 cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/hook_wrapper.py delete mode 100755 cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/model_wrapper.py delete mode 100755 cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/runner.py delete mode 100755 cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/utils.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/_functions.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/data_parallel.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/distributed.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/scatter_gather.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/engine/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/engine/test.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/file_client.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/base.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/json_handler.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/pickle_handler.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/yaml_handler.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/io.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/parse.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/image/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/image/colorspace.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/image/geometric.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/image/io.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/image/misc.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/image/photometric.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/deprecated.json delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/mmcls.json delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/open_mmlab.json delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/torchvision_0.12.json delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/cc_attention.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/README.md delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/info.cpp delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/deprecated_wrappers.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/focal_loss.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/info.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/point_sample.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/psa_mask.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/sync_bn.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/_functions.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/collate.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/data_container.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/data_parallel.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/distributed.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/distributed_deprecated.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/registry.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/scatter_gather.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/utils.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/base_module.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/base_runner.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/builder.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/checkpoint.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/default_constructor.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/dist_utils.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/epoch_based_runner.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/fp16_utils.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/checkpoint.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/closure.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/ema.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/evaluation.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/hook.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/iter_timer.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/base.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/clearml.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/dvclive.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/mlflow.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/neptune.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/pavi.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/segmind.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/tensorboard.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/text.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/wandb.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/lr_updater.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/memory.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/momentum_updater.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/optimizer.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/profiler.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/sampler_seed.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/sync_buffer.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/iter_based_runner.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/log_buffer.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/optimizer/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/optimizer/builder.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/optimizer/default_constructor.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/priority.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/utils.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/config.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/device_type.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/env.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/ext_loader.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/hub.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/logging.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/misc.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/parrots_jit.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/parrots_wrapper.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/path.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/progressbar.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/registry.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/seed.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/testing.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/timer.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/trace.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/version_utils.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmcv/version.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/inference.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/test.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/train.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/builder.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/class_names.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/eval_hooks.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/metrics.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/optimizers/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/builder.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/sampler/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/utils/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/utils/dist_util.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/core/utils/misc.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/ade.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/builder.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/coco_stuff.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/custom.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/dataset_wrappers.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/compose.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/formating.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/formatting.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/loading.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/test_time_aug.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/transforms.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/samplers/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/samplers/distributed_sampler.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/voc.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/backbones/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/backbones/resnet.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/builder.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/decode_heads/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/decode_heads/attunet_head.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/decode_heads/decode_head.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/accuracy.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/cross_entropy_loss.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/dice_loss.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/focal_loss.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/lovasz_loss.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/utils.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/featurepyramid.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/fpn.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/ic_neck.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/jpu.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/mla_neck.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/multilevel_neck.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/base.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/encoder_decoder.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/embed.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/inverted_residual.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/make_divisible.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/res_layer.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/se_layer.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/self_attention_block.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/shape_convert.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/up_conv_block.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/wrappers.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/ops/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/ops/encoding.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/ops/wrappers.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/registry/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/registry/registry.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/__init__.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/collect_env.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/logger.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/misc.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/set_env.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/mmseg/version.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/requirements.txt delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/requirements/apcnet.txt delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/build.txt delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/docs.txt delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/optional.txt delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/requirements.txt delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/runtime.txt delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/test.txt delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/requirements/mminstall.txt delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/requirements/optional.txt delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/requirements/runtime.txt delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/setup.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/tools/analyze_logs.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/tools/benchmark.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/tools/confusion_matrix.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/coco_stuff10k.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/coco_stuff164k.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/voc_aug.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/tools/get_flops.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/tools/print_config.py delete mode 100755 cv/semantic_segmentation/att_unet/pytorch/tools/slurm_test.sh delete mode 100755 cv/semantic_segmentation/att_unet/pytorch/tools/slurm_train.sh delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/tools/test.py delete mode 100644 cv/semantic_segmentation/att_unet/pytorch/train.py delete mode 100755 cv/semantic_segmentation/att_unet/pytorch/train_dist.sh diff --git a/cv/semantic_segmentation/att_unet/pytorch/.gitignore b/cv/semantic_segmentation/att_unet/pytorch/.gitignore deleted file mode 100644 index 787d13ec6..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/.gitignore +++ /dev/null @@ -1,120 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/en/_build/ -docs/zh_cn/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ -.DS_Store - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -data -.vscode -.idea - -# custom -*.pkl -*.pkl.json -*.log.json -work_dirs/ -mmseg/.mim - -# Pytorch -*.pth diff --git a/cv/semantic_segmentation/att_unet/pytorch/CITATION.cff b/cv/semantic_segmentation/att_unet/pytorch/CITATION.cff deleted file mode 100644 index cfd7cab05..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/CITATION.cff +++ /dev/null @@ -1,8 +0,0 @@ -cff-version: 1.2.0 -message: "If you use this software, please cite it as below." -authors: - - name: "MMSegmentation Contributors" -title: "OpenMMLab Semantic Segmentation Toolbox and Benchmark" -date-released: 2020-07-10 -url: "https://github.com/open-mmlab/mmsegmentation" -license: Apache-2.0 diff --git a/cv/semantic_segmentation/att_unet/pytorch/LICENSE b/cv/semantic_segmentation/att_unet/pytorch/LICENSE deleted file mode 100644 index 38e625bf5..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Copyright 2020 The MMSegmentation Authors. All rights reserved. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020 The MMSegmentation Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cv/semantic_segmentation/att_unet/pytorch/README.md b/cv/semantic_segmentation/att_unet/pytorch/README.md index 1bd0ba7ac..36ec1e825 100644 --- a/cv/semantic_segmentation/att_unet/pytorch/README.md +++ b/cv/semantic_segmentation/att_unet/pytorch/README.md @@ -9,20 +9,18 @@ We propose a novel attention gate (AG) model for medical imaging that automatica ### Install packages ```bash -yum install mesa-libGL - -pip3 install -r requirements.txt - -wget http://www.zlib.net/fossils/zlib-1.2.9.tar.gz -tar xvf zlib-1.2.9.tar.gz -cd zlib-1.2.9/ -./configure && make install -``` - -### Build extension - -```bash -python3 setup.py build && cp build/lib.linux*/mmcv/_ext.cpython* mmcv +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +# install mmsegmentation +git clone -b v1.2.2 https://github.com/open-mmlab/mmsegmentation.git --depth=1 +cd mmsegmentation/ +pip install -v -e . + +pip install ftfy ``` ## Step 2: Preparing datasets @@ -59,79 +57,15 @@ ln -s /path/to/cityscapes data/ ## Step 3: Training -```bash -# Training on multiple cards -# "config" files can be found in the configs directory -bash train_dist.sh [training args] - -# Example -bash train_dist.sh configs/att_unet/attunet_r34_512x1024_160k_cityscapes.py 8 +### Training on single card +```shell +python3 tools/train.py configs/unet/unet-s5-d16_fcn_4xb4-160k_cityscapes-512x1024.py ``` -**Training arguments are as follows:** - -```python -# the dir to save logs and models -work-dir: str = None - -# the checkpoint file to load weights from -load-from: str = None - -# the checkpoint file to resume from -resume-from: str = None - -# whether not to evaluate the checkpoint during training -no-validate: bool = False - -# (Deprecated, please use --gpu-id) number of gpus to -# use (only applicable to non-distributed training) -gpus: int = None - -# (Deprecated, please use --gpu-id) ids of gpus to use -# (only applicable to non-distributed training) -gpu-ids: int = None - -# id of gpu to use (only applicable to non-distributed training) -gpu-id: int = 0 - -# random seed -seed: int = None - -# Whether or not set different seeds for different ranks -diff_seed: bool = False - -# whether to set deterministic options for CUDNN backend. -deterministic: bool = False - -# --options is deprecated in favor of --cfg_options' and it -# will not be supported in version v0.22.0. Override some -# settings in the used config, the key-value pair in xxx=yyy -# format will be merged into config file. If the value to be -# overwritten is a list, it should be like key="[a,b]" or key=a,b -# It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" -# Note that the quotation marks are necessary and that no white space -# is allowed. -options: str = None - -# override some settings in the used config, the key-value pair -# in xxx=yyy format will be merged into config file. If the value -# to be overwritten is a list, it should be like key="[a,b]" or key=a,b -# It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" -# Note that the quotation marks are necessary and that no white -# space is allowed. -cfg-options: str = None - -# job launcher -launcher: str = "none" - -# local rank -local_rank: int = 0 - -# distributed backend -dist_backend: str = None - -# resume from the latest checkpoint automatically. -auto-resume: bool = False +### Training on mutil-cards +```shell +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/unet/unet-s5-d16_fcn_4xb4-160k_cityscapes-512x1024.py 8 ``` ## Results @@ -141,5 +75,4 @@ auto-resume: bool = False | BI-V100 x8 | 512x1024 | 160000 | 54.5180 | 69.39 | ## Reference -- [cityscapes](https://mmsegmentation.readthedocs.io/en/latest/dataset_prepare.html#cityscapes) -- [mmsegmentation](https://github.com/open-mmlab/mmsegmentation) +[mmsegmentation](https://github.com/open-mmlab/mmsegmentation) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/ade20k.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/ade20k.py deleted file mode 100644 index efc8b4bb2..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/ade20k.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'ADE20KDataset' -data_root = 'data/ade/ADEChallengeData2016' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/ade20k_640x640.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/ade20k_640x640.py deleted file mode 100644 index 14a4bb092..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/ade20k_640x640.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'ADE20KDataset' -data_root = 'data/ade/ADEChallengeData2016' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (640, 640) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2560, 640), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2560, 640), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes.py deleted file mode 100644 index f21867c63..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'CityscapesDataset' -data_root = 'data/cityscapes/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 1024) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/train', - ann_dir='gtFine/train', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/val', - ann_dir='gtFine/val', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/val', - ann_dir='gtFine/val', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py deleted file mode 100644 index f98d92972..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (1024, 1024) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_768x768.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_768x768.py deleted file mode 100644 index fde9d7c7d..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_768x768.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (768, 768) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2049, 1025), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_769x769.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_769x769.py deleted file mode 100644 index 336c7b254..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_769x769.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (769, 769) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2049, 1025), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_832x832.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_832x832.py deleted file mode 100644 index b9325cc00..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/cityscapes_832x832.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (832, 832) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/coco-stuff10k.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/coco-stuff10k.py deleted file mode 100644 index ec0496928..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/coco-stuff10k.py +++ /dev/null @@ -1,57 +0,0 @@ -# dataset settings -dataset_type = 'COCOStuffDataset' -data_root = 'data/coco_stuff10k' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/train2014', - ann_dir='annotations/train2014', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/test2014', - ann_dir='annotations/test2014', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/test2014', - ann_dir='annotations/test2014', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/coco-stuff164k.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/coco-stuff164k.py deleted file mode 100644 index a6a38f2ac..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/coco-stuff164k.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'COCOStuffDataset' -data_root = 'data/coco_stuff164k' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/train2017', - ann_dir='annotations/train2017', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/val2017', - ann_dir='annotations/val2017', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/val2017', - ann_dir='annotations/val2017', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_context.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_context.py deleted file mode 100644 index ff65bad1b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_context.py +++ /dev/null @@ -1,60 +0,0 @@ -# dataset settings -dataset_type = 'PascalContextDataset' -data_root = 'data/VOCdevkit/VOC2010/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) - -img_scale = (520, 520) -crop_size = (480, 480) - -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_context_59.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_context_59.py deleted file mode 100644 index 37585abab..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_context_59.py +++ /dev/null @@ -1,60 +0,0 @@ -# dataset settings -dataset_type = 'PascalContextDataset59' -data_root = 'data/VOCdevkit/VOC2010/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) - -img_scale = (520, 520) -crop_size = (480, 480) - -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_voc12.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_voc12.py deleted file mode 100644 index e5ff704ae..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_voc12.py +++ /dev/null @@ -1,58 +0,0 @@ -# dataset settings - -dataset_type = 'PascalVOCDataset' -data_root = 'data/VOCdevkit/VOC2012' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py deleted file mode 100644 index 3f23b6717..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = './pascal_voc12.py' -# dataset settings -data = dict( - train=dict( - ann_dir=['SegmentationClass', 'SegmentationClassAug'], - split=[ - 'ImageSets/Segmentation/train.txt', - 'ImageSets/Segmentation/aug.txt' - ])) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/default_runtime.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/default_runtime.py deleted file mode 100644 index b564cc4e7..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/default_runtime.py +++ /dev/null @@ -1,14 +0,0 @@ -# yapf:disable -log_config = dict( - interval=50, - hooks=[ - dict(type='TextLoggerHook', by_epoch=False), - # dict(type='TensorboardLoggerHook') - ]) -# yapf:enable -dist_params = dict(backend='nccl') -log_level = 'INFO' -load_from = None -resume_from = None -workflow = [('train', 1)] -cudnn_benchmark = True diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/models/attunet_r34.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/models/attunet_r34.py deleted file mode 100644 index 4870b1d54..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/models/attunet_r34.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -# model settings -norm_cfg = dict(type='SyncBN', requires_grad=True) -model = dict( - type='EncoderDecoder', - pretrained=None, - backbone=dict(type='ResNet', - depth=34), - decode_head=dict( - type='ATTUNetHead', - in_channels=[64, 128, 256, 512], - channels=64, - input_transform=None, - # dropout_ratio=None, - num_classes=19, - norm_cfg=norm_cfg, - align_corners=False, - loss_decode=[ - dict( - type='CrossEntropyLoss', - loss_name='loss_ce', - use_sigmoid=False, - loss_weight=1.0), - dict(type='DiceLoss', loss_name='loss_dice', loss_weight=1.0), - dict(type="FocalLoss") - ]), - - # model training and testing settings - train_cfg=dict(), - test_cfg=dict(mode='whole') -) \ No newline at end of file diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_160k.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_160k.py deleted file mode 100644 index eae05127a..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_160k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=160000) -checkpoint_config = dict(by_epoch=False, interval=16000) -evaluation = dict(interval=4000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_1k.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_1k.py deleted file mode 100644 index 04cf41030..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_1k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=1000) -checkpoint_config = dict(by_epoch=False, interval=1000) -evaluation = dict(interval=1000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_20k.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_20k.py deleted file mode 100644 index 73c702197..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_20k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=20000) -checkpoint_config = dict(by_epoch=False, interval=2000) -evaluation = dict(interval=2000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_320k.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_320k.py deleted file mode 100644 index a0b230626..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_320k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=320000) -checkpoint_config = dict(by_epoch=False, interval=32000) -evaluation = dict(interval=32000, metric='mIoU') diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_40k.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_40k.py deleted file mode 100644 index d2c502325..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_40k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=40000) -checkpoint_config = dict(by_epoch=False, interval=4000) -evaluation = dict(interval=4000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_80k.py b/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_80k.py deleted file mode 100644 index 8365a878e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/_base_/schedules/schedule_80k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=80000) -checkpoint_config = dict(by_epoch=False, interval=8000) -evaluation = dict(interval=8000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/att_unet/pytorch/configs/att_unet/attunet_r34_512x1024_160k_cityscapes.py b/cv/semantic_segmentation/att_unet/pytorch/configs/att_unet/attunet_r34_512x1024_160k_cityscapes.py deleted file mode 100644 index f08096afe..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/configs/att_unet/attunet_r34_512x1024_160k_cityscapes.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - -_base_ = [ - '../_base_/models/attunet_r34.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_160k.py' -] \ No newline at end of file diff --git a/cv/semantic_segmentation/att_unet/pytorch/docker/Dockerfile b/cv/semantic_segmentation/att_unet/pytorch/docker/Dockerfile deleted file mode 100644 index 64482b472..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/docker/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -ARG PYTORCH="1.11.0" -ARG CUDA="11.3" -ARG CUDNN="8" - -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ENV TORCH_CUDA_ARCH_LIST="6.0 6.1 7.0+PTX" -ENV TORCH_NVCC_FLAGS="-Xfatbin -compress-all" -ENV CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" - -# To fix GPG key error when running apt-get update -RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub -RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/7fa2af80.pub - -RUN apt-get update && apt-get install -y git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -RUN conda clean --all - -# Install MMCV -ARG PYTORCH -ARG CUDA -ARG MMCV -RUN ["/bin/bash", "-c", "pip install --no-cache-dir mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] - -# Install MMSegmentation -RUN git clone https://github.com/open-mmlab/mmsegmentation.git /mmsegmentation -WORKDIR /mmsegmentation -ENV FORCE_CUDA="1" -RUN pip install -r requirements.txt -RUN pip install --no-cache-dir -e . diff --git a/cv/semantic_segmentation/att_unet/pytorch/docker/serve/Dockerfile b/cv/semantic_segmentation/att_unet/pytorch/docker/serve/Dockerfile deleted file mode 100644 index c1d154528..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/docker/serve/Dockerfile +++ /dev/null @@ -1,49 +0,0 @@ -ARG PYTORCH="1.11.0" -ARG CUDA="11.3" -ARG CUDNN="8" -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ARG MMCV="1.4.8" -ARG MMSEG="0.24.1" - -ENV PYTHONUNBUFFERED TRUE - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ - ca-certificates \ - g++ \ - openjdk-11-jre-headless \ - # MMDet Requirements - ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && rm -rf /var/lib/apt/lists/* - -ENV PATH="/opt/conda/bin:$PATH" -RUN export FORCE_CUDA=1 - -# TORCHSEVER -RUN pip install torchserve torch-model-archiver - -# MMLAB -ARG PYTORCH -ARG CUDA -RUN ["/bin/bash", "-c", "pip install mmcv-full==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] -RUN pip install mmsegmentation==${MMSEG} - -RUN useradd -m model-server \ - && mkdir -p /home/model-server/tmp - -COPY entrypoint.sh /usr/local/bin/entrypoint.sh - -RUN chmod +x /usr/local/bin/entrypoint.sh \ - && chown -R model-server /home/model-server - -COPY config.properties /home/model-server/config.properties -RUN mkdir /home/model-server/model-store && chown -R model-server /home/model-server/model-store - -EXPOSE 8080 8081 8082 - -USER model-server -WORKDIR /home/model-server -ENV TEMP=/home/model-server/tmp -ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] -CMD ["serve"] diff --git a/cv/semantic_segmentation/att_unet/pytorch/docker/serve/config.properties b/cv/semantic_segmentation/att_unet/pytorch/docker/serve/config.properties deleted file mode 100644 index efb9c47e4..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/docker/serve/config.properties +++ /dev/null @@ -1,5 +0,0 @@ -inference_address=http://0.0.0.0:8080 -management_address=http://0.0.0.0:8081 -metrics_address=http://0.0.0.0:8082 -model_store=/home/model-server/model-store -load_models=all diff --git a/cv/semantic_segmentation/att_unet/pytorch/docker/serve/entrypoint.sh b/cv/semantic_segmentation/att_unet/pytorch/docker/serve/entrypoint.sh deleted file mode 100644 index 41ba00b04..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/docker/serve/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - -if [[ "$1" = "serve" ]]; then - shift 1 - torchserve --start --ts-config /home/model-server/config.properties -else - eval "$@" -fi - -# prevent docker exit -tail -f /dev/null diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/__init__.py deleted file mode 100644 index 435429d48..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -from .fileio import * -from .image import * -from .utils import * -from .version import * - -# The following modules are not imported to this level, so mmcv may be used -# without PyTorch. -# - runner -# - parallel -# - op -# - device diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/__init__.py deleted file mode 100644 index 3d5599d9a..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# yapf: disable -from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, - ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, - ConvTranspose2d, ConvTranspose3d, ConvWS2d, - DepthwiseSeparableConvModule, GeneralizedAttention, - HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, - NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, - build_activation_layer, build_conv_layer, - build_norm_layer, build_padding_layer, build_plugin_layer, - build_upsample_layer, conv_ws_2d, is_norm) -from .builder import MODELS, build_model_from_cfg -# yapf: enable -from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, - NormalInit, PretrainedInit, TruncNormalInit, UniformInit, - XavierInit, bias_init_with_prob, caffe2_xavier_init, - constant_init, fuse_conv_bn, get_model_complexity_info, - initialize, kaiming_init, normal_init, trunc_normal_init, - uniform_init, xavier_init) - diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/__init__.py deleted file mode 100644 index 0f33124ed..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .activation import build_activation_layer -from .context_block import ContextBlock -from .conv import build_conv_layer -from .conv2d_adaptive_padding import Conv2dAdaptivePadding -from .conv_module import ConvModule -from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d -from .depthwise_separable_conv_module import DepthwiseSeparableConvModule -from .drop import Dropout, DropPath -from .generalized_attention import GeneralizedAttention -from .hsigmoid import HSigmoid -from .hswish import HSwish -from .non_local import NonLocal1d, NonLocal2d, NonLocal3d -from .norm import build_norm_layer, is_norm -from .padding import build_padding_layer -from .plugin import build_plugin_layer -from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) -from .scale import Scale -from .swish import Swish -from .upsample import build_upsample_layer -from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, - Linear, MaxPool2d, MaxPool3d) - -__all__ = [ - 'ConvModule', 'build_activation_layer', 'build_conv_layer', - 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', - 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', - 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', - 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', - 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', - 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', - 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/activation.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/activation.py deleted file mode 100644 index 26be59581..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/activation.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version -from .registry import ACTIVATION_LAYERS - -for module in [ - nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, - nn.Sigmoid, nn.Tanh -]: - ACTIVATION_LAYERS.register_module(module=module) - - -@ACTIVATION_LAYERS.register_module(name='Clip') -@ACTIVATION_LAYERS.register_module() -class Clamp(nn.Module): - """Clamp activation layer. - - This activation function is to clamp the feature map value within - :math:`[min, max]`. More details can be found in ``torch.clamp()``. - - Args: - min (Number | optional): Lower-bound of the range to be clamped to. - Default to -1. - max (Number | optional): Upper-bound of the range to be clamped to. - Default to 1. - """ - - def __init__(self, min=-1., max=1.): - super(Clamp, self).__init__() - self.min = min - self.max = max - - def forward(self, x): - """Forward function. - - Args: - x (torch.Tensor): The input tensor. - - Returns: - torch.Tensor: Clamped tensor. - """ - return torch.clamp(x, min=self.min, max=self.max) - - -class GELU(nn.Module): - r"""Applies the Gaussian Error Linear Units function: - - .. math:: - \text{GELU}(x) = x * \Phi(x) - where :math:`\Phi(x)` is the Cumulative Distribution Function for - Gaussian Distribution. - - Shape: - - Input: :math:`(N, *)` where `*` means, any number of additional - dimensions - - Output: :math:`(N, *)`, same shape as the input - - .. image:: scripts/activation_images/GELU.png - - Examples:: - - >>> m = nn.GELU() - >>> input = torch.randn(2) - >>> output = m(input) - """ - - def forward(self, input): - return F.gelu(input) - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.4')): - ACTIVATION_LAYERS.register_module(module=GELU) -else: - ACTIVATION_LAYERS.register_module(module=nn.GELU) - - -def build_activation_layer(cfg): - """Build activation layer. - - Args: - cfg (dict): The activation layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate an activation layer. - - Returns: - nn.Module: Created activation layer. - """ - return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/context_block.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/context_block.py deleted file mode 100644 index d60fdb904..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/context_block.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn - -from ..utils import constant_init, kaiming_init -from .registry import PLUGIN_LAYERS - - -def last_zero_init(m): - if isinstance(m, nn.Sequential): - constant_init(m[-1], val=0) - else: - constant_init(m, val=0) - - -@PLUGIN_LAYERS.register_module() -class ContextBlock(nn.Module): - """ContextBlock module in GCNet. - - See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' - (https://arxiv.org/abs/1904.11492) for details. - - Args: - in_channels (int): Channels of the input feature map. - ratio (float): Ratio of channels of transform bottleneck - pooling_type (str): Pooling method for context modeling. - Options are 'att' and 'avg', stand for attention pooling and - average pooling respectively. Default: 'att'. - fusion_types (Sequence[str]): Fusion method for feature fusion, - Options are 'channels_add', 'channel_mul', stand for channelwise - addition and multiplication respectively. Default: ('channel_add',) - """ - - _abbr_ = 'context_block' - - def __init__(self, - in_channels, - ratio, - pooling_type='att', - fusion_types=('channel_add', )): - super(ContextBlock, self).__init__() - assert pooling_type in ['avg', 'att'] - assert isinstance(fusion_types, (list, tuple)) - valid_fusion_types = ['channel_add', 'channel_mul'] - assert all([f in valid_fusion_types for f in fusion_types]) - assert len(fusion_types) > 0, 'at least one fusion should be used' - self.in_channels = in_channels - self.ratio = ratio - self.planes = int(in_channels * ratio) - self.pooling_type = pooling_type - self.fusion_types = fusion_types - if pooling_type == 'att': - self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) - self.softmax = nn.Softmax(dim=2) - else: - self.avg_pool = nn.AdaptiveAvgPool2d(1) - if 'channel_add' in fusion_types: - self.channel_add_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_add_conv = None - if 'channel_mul' in fusion_types: - self.channel_mul_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_mul_conv = None - self.reset_parameters() - - def reset_parameters(self): - if self.pooling_type == 'att': - kaiming_init(self.conv_mask, mode='fan_in') - self.conv_mask.inited = True - - if self.channel_add_conv is not None: - last_zero_init(self.channel_add_conv) - if self.channel_mul_conv is not None: - last_zero_init(self.channel_mul_conv) - - def spatial_pool(self, x): - batch, channel, height, width = x.size() - if self.pooling_type == 'att': - input_x = x - # [N, C, H * W] - input_x = input_x.view(batch, channel, height * width) - # [N, 1, C, H * W] - input_x = input_x.unsqueeze(1) - # [N, 1, H, W] - context_mask = self.conv_mask(x) - # [N, 1, H * W] - context_mask = context_mask.view(batch, 1, height * width) - # [N, 1, H * W] - context_mask = self.softmax(context_mask) - # [N, 1, H * W, 1] - context_mask = context_mask.unsqueeze(-1) - # [N, 1, C, 1] - context = torch.matmul(input_x, context_mask) - # [N, C, 1, 1] - context = context.view(batch, channel, 1, 1) - else: - # [N, C, 1, 1] - context = self.avg_pool(x) - - return context - - def forward(self, x): - # [N, C, 1, 1] - context = self.spatial_pool(x) - - out = x - if self.channel_mul_conv is not None: - # [N, C, 1, 1] - channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) - out = out * channel_mul_term - if self.channel_add_conv is not None: - # [N, C, 1, 1] - channel_add_term = self.channel_add_conv(context) - out = out + channel_add_term - - return out diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv.py deleted file mode 100644 index f6c35fd70..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from .registry import CONV_LAYERS - -CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) -CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) -CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) -CONV_LAYERS.register_module('Conv', module=nn.Conv2d) - - -def build_conv_layer(cfg, *args, **kwargs): - """Build convolution layer. - - Args: - cfg (None or dict): The conv layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an conv layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding conv layer. - - Returns: - nn.Module: Created conv layer. - """ - if cfg is None: - cfg_ = dict(type='Conv2d') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in CONV_LAYERS: - raise KeyError(f'Unrecognized layer type {layer_type}') - else: - conv_layer = CONV_LAYERS.get(layer_type) - - layer = conv_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py deleted file mode 100644 index b45e758ac..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from torch import nn -from torch.nn import functional as F - -from .registry import CONV_LAYERS - - -@CONV_LAYERS.register_module() -class Conv2dAdaptivePadding(nn.Conv2d): - """Implementation of 2D convolution in tensorflow with `padding` as "same", - which applies padding to input (if needed) so that input image gets fully - covered by filter and stride you specified. For stride 1, this will ensure - that output image size is same as input. For stride of 2, output dimensions - will be half, for example. - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the convolving kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If ``True``, adds a learnable bias to the - output. Default: ``True`` - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__(in_channels, out_channels, kernel_size, stride, 0, - dilation, groups, bias) - - def forward(self, x): - img_h, img_w = x.size()[-2:] - kernel_h, kernel_w = self.weight.size()[-2:] - stride_h, stride_w = self.stride - output_h = math.ceil(img_h / stride_h) - output_w = math.ceil(img_w / stride_w) - pad_h = ( - max((output_h - 1) * self.stride[0] + - (kernel_h - 1) * self.dilation[0] + 1 - img_h, 0)) - pad_w = ( - max((output_w - 1) * self.stride[1] + - (kernel_w - 1) * self.dilation[1] + 1 - img_w, 0)) - if pad_h > 0 or pad_w > 0: - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2 - ]) - return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv_module.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv_module.py deleted file mode 100644 index 0078647a1..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv_module.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from mmcv.utils import _BatchNorm, _InstanceNorm -from ..utils import constant_init, kaiming_init -from .activation import build_activation_layer -from .conv import build_conv_layer -from .norm import build_norm_layer -from .padding import build_padding_layer -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class ConvModule(nn.Module): - """A conv block that bundles conv/norm/activation layers. - - This block simplifies the usage of convolution layers, which are commonly - used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). - It is based upon three build methods: `build_conv_layer()`, - `build_norm_layer()` and `build_activation_layer()`. - - Besides, we add some additional features in this module. - 1. Automatically set `bias` of the conv layer. - 2. Spectral norm is supported. - 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only - supports zero and circular padding, and we add "reflect" padding mode. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. - groups (int): Number of blocked connections from input channels to - output channels. Same as that in ``nn._ConvNd``. - bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - inplace (bool): Whether to use inplace mode for activation. - Default: True. - with_spectral_norm (bool): Whether use spectral norm in conv module. - Default: False. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in PyTorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Common examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - Default: ('conv', 'norm', 'act'). - """ - - _abbr_ = 'conv_block' - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias='auto', - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - padding_mode='zeros', - order=('conv', 'norm', 'act')): - super(ConvModule, self).__init__() - assert conv_cfg is None or isinstance(conv_cfg, dict) - assert norm_cfg is None or isinstance(norm_cfg, dict) - assert act_cfg is None or isinstance(act_cfg, dict) - official_padding_mode = ['zeros', 'circular'] - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.with_explicit_padding = padding_mode not in official_padding_mode - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 3 - assert set(order) == set(['conv', 'norm', 'act']) - - self.with_norm = norm_cfg is not None - self.with_activation = act_cfg is not None - # if the conv layer is before a norm layer, bias is unnecessary. - if bias == 'auto': - bias = not self.with_norm - self.with_bias = bias - - if self.with_explicit_padding: - pad_cfg = dict(type=padding_mode) - self.padding_layer = build_padding_layer(pad_cfg, padding) - - # reset padding to 0 for conv module - conv_padding = 0 if self.with_explicit_padding else padding - # build convolution layer - self.conv = build_conv_layer( - conv_cfg, - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=conv_padding, - dilation=dilation, - groups=groups, - bias=bias) - # export the attributes of self.conv to a higher level for convenience - self.in_channels = self.conv.in_channels - self.out_channels = self.conv.out_channels - self.kernel_size = self.conv.kernel_size - self.stride = self.conv.stride - self.padding = padding - self.dilation = self.conv.dilation - self.transposed = self.conv.transposed - self.output_padding = self.conv.output_padding - self.groups = self.conv.groups - - if self.with_spectral_norm: - self.conv = nn.utils.spectral_norm(self.conv) - - # build normalization layers - if self.with_norm: - # norm layer is after conv layer - if order.index('norm') > order.index('conv'): - norm_channels = out_channels - else: - norm_channels = in_channels - self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) - self.add_module(self.norm_name, norm) - if self.with_bias: - if isinstance(norm, (_BatchNorm, _InstanceNorm)): - warnings.warn( - 'Unnecessary conv bias before batch/instance norm') - else: - self.norm_name = None - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - # nn.Tanh has no 'inplace' argument - if act_cfg_['type'] not in [ - 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish', 'GELU' - ]: - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - @property - def norm(self): - if self.norm_name: - return getattr(self, self.norm_name) - else: - return None - - def init_weights(self): - # 1. It is mainly for customized conv layers with their own - # initialization manners by calling their own ``init_weights()``, - # and we do not want ConvModule to override the initialization. - # 2. For customized conv layers without their own initialization - # manners (that is, they don't have their own ``init_weights()``) - # and PyTorch's conv layers, they will be initialized by - # this method with default ``kaiming_init``. - # Note: For PyTorch's conv layers, they will be overwritten by our - # initialization implementation using default ``kaiming_init``. - if not hasattr(self.conv, 'init_weights'): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) - if self.with_norm: - constant_init(self.norm, 1, bias=0) - - def forward(self, x, activate=True, norm=True): - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - x = self.conv(x) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv_ws.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv_ws.py deleted file mode 100644 index a3941e278..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/conv_ws.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from .registry import CONV_LAYERS - - -def conv_ws_2d(input, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - eps=1e-5): - c_in = weight.size(0) - weight_flat = weight.view(c_in, -1) - mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1) - std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1) - weight = (weight - mean) / (std + eps) - return F.conv2d(input, weight, bias, stride, padding, dilation, groups) - - -@CONV_LAYERS.register_module('ConvWS') -class ConvWS2d(nn.Conv2d): - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True, - eps=1e-5): - super(ConvWS2d, self).__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.eps = eps - - def forward(self, x): - return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups, self.eps) - - -@CONV_LAYERS.register_module(name='ConvAWS') -class ConvAWS2d(nn.Conv2d): - """AWS (Adaptive Weight Standardization) - - This is a variant of Weight Standardization - (https://arxiv.org/pdf/1903.10520.pdf) - It is used in DetectoRS to avoid NaN - (https://arxiv.org/pdf/2006.02334.pdf) - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the conv kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If set True, adds a learnable bias to the - output. Default: True - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.register_buffer('weight_gamma', - torch.ones(self.out_channels, 1, 1, 1)) - self.register_buffer('weight_beta', - torch.zeros(self.out_channels, 1, 1, 1)) - - def _get_weight(self, weight): - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - weight = (weight - mean) / std - weight = self.weight_gamma * weight + self.weight_beta - return weight - - def forward(self, x): - weight = self._get_weight(self.weight) - return F.conv2d(x, weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - """Override default load function. - - AWS overrides the function _load_from_state_dict to recover - weight_gamma and weight_beta if they are missing. If weight_gamma and - weight_beta are found in the checkpoint, this function will return - after super()._load_from_state_dict. Otherwise, it will compute the - mean and std of the pretrained weights and store them in weight_beta - and weight_gamma. - """ - - self.weight_gamma.data.fill_(-1) - local_missing_keys = [] - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, local_missing_keys, - unexpected_keys, error_msgs) - if self.weight_gamma.data.mean() > 0: - for k in local_missing_keys: - missing_keys.append(k) - return - weight = self.weight.data - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - self.weight_beta.data.copy_(mean) - self.weight_gamma.data.copy_(std) - missing_gamma_beta = [ - k for k in local_missing_keys - if k.endswith('weight_gamma') or k.endswith('weight_beta') - ] - for k in missing_gamma_beta: - local_missing_keys.remove(k) - for k in local_missing_keys: - missing_keys.append(k) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py deleted file mode 100644 index 722d5d8d7..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .conv_module import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if `norm_cfg` and `act_cfg` are specified. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. Default: 1. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. Default: 0. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super(DepthwiseSeparableConvModule, self).__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/drop.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/drop.py deleted file mode 100644 index b0a026654..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/drop.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from mmcv import build_from_cfg -from .registry import DROPOUT_LAYERS - - -def drop_path(x, drop_prob=0., training=False): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - """ - if drop_prob == 0. or not training: - return x - keep_prob = 1 - drop_prob - # handle tensors with different dimensions, not just 4D tensors. - shape = (x.shape[0], ) + (1, ) * (x.ndim - 1) - random_tensor = keep_prob + torch.rand( - shape, dtype=x.dtype, device=x.device) - output = x.div(keep_prob) * random_tensor.floor() - return output - - -@DROPOUT_LAYERS.register_module() -class DropPath(nn.Module): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - - Args: - drop_prob (float): Probability of the path to be zeroed. Default: 0.1 - """ - - def __init__(self, drop_prob=0.1): - super(DropPath, self).__init__() - self.drop_prob = drop_prob - - def forward(self, x): - return drop_path(x, self.drop_prob, self.training) - - -@DROPOUT_LAYERS.register_module() -class Dropout(nn.Dropout): - """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of - ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with - ``DropPath`` - - Args: - drop_prob (float): Probability of the elements to be - zeroed. Default: 0.5. - inplace (bool): Do the operation inplace or not. Default: False. - """ - - def __init__(self, drop_prob=0.5, inplace=False): - super().__init__(p=drop_prob, inplace=inplace) - - -def build_dropout(cfg, default_args=None): - """Builder for drop out layers.""" - return build_from_cfg(cfg, DROPOUT_LAYERS, default_args) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/generalized_attention.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/generalized_attention.py deleted file mode 100644 index c8a74d268..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/generalized_attention.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import kaiming_init -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class GeneralizedAttention(nn.Module): - """GeneralizedAttention module. - - See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' - (https://arxiv.org/abs/1711.07971) for details. - - Args: - in_channels (int): Channels of the input feature map. - spatial_range (int): The spatial range. -1 indicates no spatial range - constraint. Default: -1. - num_heads (int): The head number of empirical_attention module. - Default: 9. - position_embedding_dim (int): The position embedding dimension. - Default: -1. - position_magnitude (int): A multiplier acting on coord difference. - Default: 1. - kv_stride (int): The feature stride acting on key/value feature map. - Default: 2. - q_stride (int): The feature stride acting on query feature map. - Default: 1. - attention_type (str): A binary indicator string for indicating which - items in generalized empirical_attention module are used. - Default: '1111'. - - - '1000' indicates 'query and key content' (appr - appr) item, - - '0100' indicates 'query content and relative position' - (appr - position) item, - - '0010' indicates 'key content only' (bias - appr) item, - - '0001' indicates 'relative position only' (bias - position) item. - """ - - _abbr_ = 'gen_attention_block' - - def __init__(self, - in_channels, - spatial_range=-1, - num_heads=9, - position_embedding_dim=-1, - position_magnitude=1, - kv_stride=2, - q_stride=1, - attention_type='1111'): - - super(GeneralizedAttention, self).__init__() - - # hard range means local range for non-local operation - self.position_embedding_dim = ( - position_embedding_dim - if position_embedding_dim > 0 else in_channels) - - self.position_magnitude = position_magnitude - self.num_heads = num_heads - self.in_channels = in_channels - self.spatial_range = spatial_range - self.kv_stride = kv_stride - self.q_stride = q_stride - self.attention_type = [bool(int(_)) for _ in attention_type] - self.qk_embed_dim = in_channels // num_heads - out_c = self.qk_embed_dim * num_heads - - if self.attention_type[0] or self.attention_type[1]: - self.query_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.query_conv.kaiming_init = True - - if self.attention_type[0] or self.attention_type[2]: - self.key_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.key_conv.kaiming_init = True - - self.v_dim = in_channels // num_heads - self.value_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=self.v_dim * num_heads, - kernel_size=1, - bias=False) - self.value_conv.kaiming_init = True - - if self.attention_type[1] or self.attention_type[3]: - self.appr_geom_fc_x = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_x.kaiming_init = True - - self.appr_geom_fc_y = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_y.kaiming_init = True - - if self.attention_type[2]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.appr_bias = nn.Parameter(appr_bias_value) - - if self.attention_type[3]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.geom_bias = nn.Parameter(geom_bias_value) - - self.proj_conv = nn.Conv2d( - in_channels=self.v_dim * num_heads, - out_channels=in_channels, - kernel_size=1, - bias=True) - self.proj_conv.kaiming_init = True - self.gamma = nn.Parameter(torch.zeros(1)) - - if self.spatial_range >= 0: - # only works when non local is after 3*3 conv - if in_channels == 256: - max_len = 84 - elif in_channels == 512: - max_len = 42 - - max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) - local_constraint_map = np.ones( - (max_len, max_len, max_len_kv, max_len_kv), dtype=int) - for iy in range(max_len): - for ix in range(max_len): - local_constraint_map[ - iy, ix, - max((iy - self.spatial_range) // - self.kv_stride, 0):min((iy + self.spatial_range + - 1) // self.kv_stride + - 1, max_len), - max((ix - self.spatial_range) // - self.kv_stride, 0):min((ix + self.spatial_range + - 1) // self.kv_stride + - 1, max_len)] = 0 - - self.local_constraint_map = nn.Parameter( - torch.from_numpy(local_constraint_map).byte(), - requires_grad=False) - - if self.q_stride > 1: - self.q_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.q_stride) - else: - self.q_downsample = None - - if self.kv_stride > 1: - self.kv_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.kv_stride) - else: - self.kv_downsample = None - - self.init_weights() - - def get_position_embedding(self, - h, - w, - h_kv, - w_kv, - q_stride, - kv_stride, - device, - dtype, - feat_dim, - wave_length=1000): - # the default type of Tensor is float32, leading to type mismatch - # in fp16 mode. Cast it to support fp16 mode. - h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) - h_idxs = h_idxs.view((h, 1)) * q_stride - - w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) - w_idxs = w_idxs.view((w, 1)) * q_stride - - h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( - device=device, dtype=dtype) - h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride - - w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( - device=device, dtype=dtype) - w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride - - # (h, h_kv, 1) - h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) - h_diff *= self.position_magnitude - - # (w, w_kv, 1) - w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) - w_diff *= self.position_magnitude - - feat_range = torch.arange(0, feat_dim / 4).to( - device=device, dtype=dtype) - - dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) - dim_mat = dim_mat**((4. / feat_dim) * feat_range) - dim_mat = dim_mat.view((1, 1, -1)) - - embedding_x = torch.cat( - ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) - - embedding_y = torch.cat( - ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) - - return embedding_x, embedding_y - - def forward(self, x_input): - num_heads = self.num_heads - - # use empirical_attention - if self.q_downsample is not None: - x_q = self.q_downsample(x_input) - else: - x_q = x_input - n, _, h, w = x_q.shape - - if self.kv_downsample is not None: - x_kv = self.kv_downsample(x_input) - else: - x_kv = x_input - _, _, h_kv, w_kv = x_kv.shape - - if self.attention_type[0] or self.attention_type[1]: - proj_query = self.query_conv(x_q).view( - (n, num_heads, self.qk_embed_dim, h * w)) - proj_query = proj_query.permute(0, 1, 3, 2) - - if self.attention_type[0] or self.attention_type[2]: - proj_key = self.key_conv(x_kv).view( - (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) - - if self.attention_type[1] or self.attention_type[3]: - position_embed_x, position_embed_y = self.get_position_embedding( - h, w, h_kv, w_kv, self.q_stride, self.kv_stride, - x_input.device, x_input.dtype, self.position_embedding_dim) - # (n, num_heads, w, w_kv, dim) - position_feat_x = self.appr_geom_fc_x(position_embed_x).\ - view(1, w, w_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - # (n, num_heads, h, h_kv, dim) - position_feat_y = self.appr_geom_fc_y(position_embed_y).\ - view(1, h, h_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - position_feat_x /= math.sqrt(2) - position_feat_y /= math.sqrt(2) - - # accelerate for saliency only - if (np.sum(self.attention_type) == 1) and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy = torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, h_kv * w_kv) - - h = 1 - w = 1 - else: - # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for - if not self.attention_type[0]: - energy = torch.zeros( - n, - num_heads, - h, - w, - h_kv, - w_kv, - dtype=x_input.dtype, - device=x_input.device) - - # attention_type[0]: appr - appr - # attention_type[1]: appr - position - # attention_type[2]: bias - appr - # attention_type[3]: bias - position - if self.attention_type[0] or self.attention_type[2]: - if self.attention_type[0] and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - energy = torch.matmul(proj_query + appr_bias, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[0]: - energy = torch.matmul(proj_query, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy += torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, 1, h_kv, w_kv) - - if self.attention_type[1] or self.attention_type[3]: - if self.attention_type[1] and self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - - proj_query_reshape = (proj_query + geom_bias).\ - view(n, num_heads, h, w, self.qk_embed_dim) - - energy_x = torch.matmul( - proj_query_reshape.permute(0, 1, 3, 2, 4), - position_feat_x.permute(0, 1, 2, 4, 3)) - energy_x = energy_x.\ - permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul( - proj_query_reshape, - position_feat_y.permute(0, 1, 2, 4, 3)) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[1]: - proj_query_reshape = proj_query.\ - view(n, num_heads, h, w, self.qk_embed_dim) - proj_query_reshape = proj_query_reshape.\ - permute(0, 1, 3, 2, 4) - position_feat_x_reshape = position_feat_x.\ - permute(0, 1, 2, 4, 3) - position_feat_y_reshape = position_feat_y.\ - permute(0, 1, 2, 4, 3) - - energy_x = torch.matmul(proj_query_reshape, - position_feat_x_reshape) - energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul(proj_query_reshape, - position_feat_y_reshape) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, self.qk_embed_dim, 1).\ - repeat(n, 1, 1, 1) - - position_feat_x_reshape = position_feat_x.\ - view(n, num_heads, w * w_kv, self.qk_embed_dim) - - position_feat_y_reshape = position_feat_y.\ - view(n, num_heads, h * h_kv, self.qk_embed_dim) - - energy_x = torch.matmul(position_feat_x_reshape, geom_bias) - energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) - - energy_y = torch.matmul(position_feat_y_reshape, geom_bias) - energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) - - energy += energy_x + energy_y - - energy = energy.view(n, num_heads, h * w, h_kv * w_kv) - - if self.spatial_range >= 0: - cur_local_constraint_map = \ - self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ - contiguous().\ - view(1, 1, h*w, h_kv*w_kv) - - energy = energy.masked_fill_(cur_local_constraint_map, - float('-inf')) - - attention = F.softmax(energy, 3) - - proj_value = self.value_conv(x_kv) - proj_value_reshape = proj_value.\ - view((n, num_heads, self.v_dim, h_kv * w_kv)).\ - permute(0, 1, 3, 2) - - out = torch.matmul(attention, proj_value_reshape).\ - permute(0, 1, 3, 2).\ - contiguous().\ - view(n, self.v_dim * self.num_heads, h, w) - - out = self.proj_conv(out) - - # output is downsampled, upsample back to input size - if self.q_downsample is not None: - out = F.interpolate( - out, - size=x_input.shape[2:], - mode='bilinear', - align_corners=False) - - out = self.gamma * out + x_input - return out - - def init_weights(self): - for m in self.modules(): - if hasattr(m, 'kaiming_init') and m.kaiming_init: - kaiming_init( - m, - mode='fan_in', - nonlinearity='leaky_relu', - bias=0, - distribution='uniform', - a=1) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/hsigmoid.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/hsigmoid.py deleted file mode 100644 index e013d739e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/hsigmoid.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSigmoid(nn.Module): - """Hard Sigmoid Module. Apply the hard sigmoid function: - Hsigmoid(x) = min(max((x + bias) / divisor, min_value), max_value) - Default: Hsigmoid(x) = min(max((x + 3) / 6, 0), 1) - - Note: - In MMCV v1.4.4, we modified the default value of args to align with - PyTorch official. - - Args: - bias (float): Bias of the input feature map. Default: 3.0. - divisor (float): Divisor of the input feature map. Default: 6.0. - min_value (float): Lower bound value. Default: 0.0. - max_value (float): Upper bound value. Default: 1.0. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, bias=3.0, divisor=6.0, min_value=0.0, max_value=1.0): - super(HSigmoid, self).__init__() - warnings.warn( - 'In MMCV v1.4.4, we modified the default value of args to align ' - 'with PyTorch official. Previous Implementation: ' - 'Hsigmoid(x) = min(max((x + 1) / 2, 0), 1). ' - 'Current Implementation: ' - 'Hsigmoid(x) = min(max((x + 3) / 6, 0), 1).') - self.bias = bias - self.divisor = divisor - assert self.divisor != 0 - self.min_value = min_value - self.max_value = max_value - - def forward(self, x): - x = (x + self.bias) / self.divisor - - return x.clamp_(self.min_value, self.max_value) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/hswish.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/hswish.py deleted file mode 100644 index 27096832f..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/hswish.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import ACTIVATION_LAYERS - - -class HSwish(nn.Module): - """Hard Swish Module. - - This module applies the hard swish function: - - .. math:: - Hswish(x) = x * ReLU6(x + 3) / 6 - - Args: - inplace (bool): can optionally do the operation in-place. - Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.act = nn.ReLU6(inplace) - - def forward(self, x): - return x * self.act(x + 3) / 6 - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.7')): - # Hardswish is not supported when PyTorch version < 1.6. - # And Hardswish in PyTorch 1.6 does not support inplace. - ACTIVATION_LAYERS.register_module(module=HSwish) -else: - ACTIVATION_LAYERS.register_module(module=nn.Hardswish, name='HSwish') diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/non_local.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/non_local.py deleted file mode 100644 index 92d00155e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/non_local.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta - -import torch -import torch.nn as nn - -from ..utils import constant_init, normal_init -from .conv_module import ConvModule -from .registry import PLUGIN_LAYERS - - -class _NonLocalNd(nn.Module, metaclass=ABCMeta): - """Basic Non-local module. - - This module is proposed in - "Non-local Neural Networks" - Paper reference: https://arxiv.org/abs/1711.07971 - Code reference: https://github.com/AlexHex7/Non-local_pytorch - - Args: - in_channels (int): Channels of the input feature map. - reduction (int): Channel reduction ratio. Default: 2. - use_scale (bool): Whether to scale pairwise_weight by - `1/sqrt(inter_channels)` when the mode is `embedded_gaussian`. - Default: True. - conv_cfg (None | dict): The config dict for convolution layers. - If not specified, it will use `nn.Conv2d` for convolution layers. - Default: None. - norm_cfg (None | dict): The config dict for normalization layers. - Default: None. (This parameter is only applicable to conv_out.) - mode (str): Options are `gaussian`, `concatenation`, - `embedded_gaussian` and `dot_product`. Default: embedded_gaussian. - """ - - def __init__(self, - in_channels, - reduction=2, - use_scale=True, - conv_cfg=None, - norm_cfg=None, - mode='embedded_gaussian', - **kwargs): - super(_NonLocalNd, self).__init__() - self.in_channels = in_channels - self.reduction = reduction - self.use_scale = use_scale - self.inter_channels = max(in_channels // reduction, 1) - self.mode = mode - - if mode not in [ - 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation' - ]: - raise ValueError("Mode should be in 'gaussian', 'concatenation', " - f"'embedded_gaussian' or 'dot_product', but got " - f'{mode} instead.') - - # g, theta, phi are defaulted as `nn.ConvNd`. - # Here we use ConvModule for potential usage. - self.g = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.conv_out = ConvModule( - self.inter_channels, - self.in_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - if self.mode != 'gaussian': - self.theta = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.phi = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - - if self.mode == 'concatenation': - self.concat_project = ConvModule( - self.inter_channels * 2, - 1, - kernel_size=1, - stride=1, - padding=0, - bias=False, - act_cfg=dict(type='ReLU')) - - self.init_weights(**kwargs) - - def init_weights(self, std=0.01, zeros_init=True): - if self.mode != 'gaussian': - for m in [self.g, self.theta, self.phi]: - normal_init(m.conv, std=std) - else: - normal_init(self.g.conv, std=std) - if zeros_init: - if self.conv_out.norm_cfg is None: - constant_init(self.conv_out.conv, 0) - else: - constant_init(self.conv_out.norm, 0) - else: - if self.conv_out.norm_cfg is None: - normal_init(self.conv_out.conv, std=std) - else: - normal_init(self.conv_out.norm, std=std) - - def gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def embedded_gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - if self.use_scale: - # theta_x.shape[-1] is `self.inter_channels` - pairwise_weight /= theta_x.shape[-1]**0.5 - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def dot_product(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight /= pairwise_weight.shape[-1] - return pairwise_weight - - def concatenation(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - h = theta_x.size(2) - w = phi_x.size(3) - theta_x = theta_x.repeat(1, 1, 1, w) - phi_x = phi_x.repeat(1, 1, h, 1) - - concat_feature = torch.cat([theta_x, phi_x], dim=1) - pairwise_weight = self.concat_project(concat_feature) - n, _, h, w = pairwise_weight.size() - pairwise_weight = pairwise_weight.view(n, h, w) - pairwise_weight /= pairwise_weight.shape[-1] - - return pairwise_weight - - def forward(self, x): - # Assume `reduction = 1`, then `inter_channels = C` - # or `inter_channels = C` when `mode="gaussian"` - - # NonLocal1d x: [N, C, H] - # NonLocal2d x: [N, C, H, W] - # NonLocal3d x: [N, C, T, H, W] - n = x.size(0) - - # NonLocal1d g_x: [N, H, C] - # NonLocal2d g_x: [N, HxW, C] - # NonLocal3d g_x: [N, TxHxW, C] - g_x = self.g(x).view(n, self.inter_channels, -1) - g_x = g_x.permute(0, 2, 1) - - # NonLocal1d theta_x: [N, H, C], phi_x: [N, C, H] - # NonLocal2d theta_x: [N, HxW, C], phi_x: [N, C, HxW] - # NonLocal3d theta_x: [N, TxHxW, C], phi_x: [N, C, TxHxW] - if self.mode == 'gaussian': - theta_x = x.view(n, self.in_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - if self.sub_sample: - phi_x = self.phi(x).view(n, self.in_channels, -1) - else: - phi_x = x.view(n, self.in_channels, -1) - elif self.mode == 'concatenation': - theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) - phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) - else: - theta_x = self.theta(x).view(n, self.inter_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - phi_x = self.phi(x).view(n, self.inter_channels, -1) - - pairwise_func = getattr(self, self.mode) - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = pairwise_func(theta_x, phi_x) - - # NonLocal1d y: [N, H, C] - # NonLocal2d y: [N, HxW, C] - # NonLocal3d y: [N, TxHxW, C] - y = torch.matmul(pairwise_weight, g_x) - # NonLocal1d y: [N, C, H] - # NonLocal2d y: [N, C, H, W] - # NonLocal3d y: [N, C, T, H, W] - y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, - *x.size()[2:]) - - output = x + self.conv_out(y) - - return output - - -class NonLocal1d(_NonLocalNd): - """1D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv1d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv1d'), - **kwargs): - super(NonLocal1d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool1d(kernel_size=2) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -@PLUGIN_LAYERS.register_module() -class NonLocal2d(_NonLocalNd): - """2D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv2d'). - """ - - _abbr_ = 'nonlocal_block' - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv2d'), - **kwargs): - super(NonLocal2d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -class NonLocal3d(_NonLocalNd): - """3D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv3d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv3d'), - **kwargs): - super(NonLocal3d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/norm.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/norm.py deleted file mode 100644 index 51efdc184..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/norm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import torch.nn as nn - -from mmcv.utils import is_tuple_of -from mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm -from .registry import NORM_LAYERS - -NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) -NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) -NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) -NORM_LAYERS.register_module('GN', module=nn.GroupNorm) -NORM_LAYERS.register_module('LN', module=nn.LayerNorm) -NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) -NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - When we build a norm layer with `build_norm_layer()`, we want to preserve - the norm type in variable names, e.g, self.bn1, self.gn. This method will - infer the abbreviation to map class types to abbreviations. - - Rule 1: If the class has the property "_abbr_", return the property. - Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or - InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and - "in" respectively. - Rule 3: If the class name contains "batch", "group", "layer" or "instance", - the abbreviation of this layer will be "bn", "gn", "ln" and "in" - respectively. - Rule 4: Otherwise, the abbreviation falls back to "norm". - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN - return 'in' - elif issubclass(class_type, _BatchNorm): - return 'bn' - elif issubclass(class_type, nn.GroupNorm): - return 'gn' - elif issubclass(class_type, nn.LayerNorm): - return 'ln' - else: - class_name = class_type.__name__.lower() - if 'batch' in class_name: - return 'bn' - elif 'group' in class_name: - return 'gn' - elif 'layer' in class_name: - return 'ln' - elif 'instance' in class_name: - return 'in' - else: - return 'norm_layer' - - -def build_norm_layer(cfg, num_features, postfix=''): - """Build normalization layer. - - Args: - cfg (dict): The norm layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate a norm layer. - - requires_grad (bool, optional): Whether stop gradient updates. - num_features (int): Number of input channels. - postfix (int | str): The postfix to be appended into norm abbreviation - to create named layer. - - Returns: - tuple[str, nn.Module]: The first element is the layer name consisting - of abbreviation and postfix, e.g., bn1, gn. The second element is the - created norm layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in NORM_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - - norm_layer = NORM_LAYERS.get(layer_type) - abbr = infer_abbr(norm_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - requires_grad = cfg_.pop('requires_grad', True) - cfg_.setdefault('eps', 1e-5) - if layer_type != 'GN': - layer = norm_layer(num_features, **cfg_) - if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): - layer._specify_ddp_gpu_num(1) - else: - assert 'num_groups' in cfg_ - layer = norm_layer(num_channels=num_features, **cfg_) - - for param in layer.parameters(): - param.requires_grad = requires_grad - - return name, layer - - -def is_norm(layer, exclude=None): - """Check if a layer is a normalization layer. - - Args: - layer (nn.Module): The layer to be checked. - exclude (type | tuple[type]): Types to be excluded. - - Returns: - bool: Whether the layer is a norm layer. - """ - if exclude is not None: - if not isinstance(exclude, tuple): - exclude = (exclude, ) - if not is_tuple_of(exclude, type): - raise TypeError( - f'"exclude" must be either None or type or a tuple of types, ' - f'but got {type(exclude)}: {exclude}') - - if exclude and isinstance(layer, exclude): - return False - - all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) - return isinstance(layer, all_norm_bases) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/padding.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/padding.py deleted file mode 100644 index e4ac6b28a..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/padding.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import PADDING_LAYERS - -PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) -PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) -PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) - - -def build_padding_layer(cfg, *args, **kwargs): - """Build padding layer. - - Args: - cfg (None or dict): The padding layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate a padding layer. - - Returns: - nn.Module: Created padding layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - - cfg_ = cfg.copy() - padding_type = cfg_.pop('type') - if padding_type not in PADDING_LAYERS: - raise KeyError(f'Unrecognized padding type {padding_type}.') - else: - padding_layer = PADDING_LAYERS.get(padding_type) - - layer = padding_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/plugin.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/plugin.py deleted file mode 100644 index 009f7529b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/plugin.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import platform - -from .registry import PLUGIN_LAYERS - -if platform.system() == 'Windows': - import regex as re -else: - import re - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - This method will infer the abbreviation to map class types to - abbreviations. - - Rule 1: If the class has the property "abbr", return the property. - Rule 2: Otherwise, the abbreviation falls back to snake case of class - name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - - def camel2snack(word): - """Convert camel case word into snack case. - - Modified from `inflection lib - `_. - - Example:: - - >>> camel2snack("FancyBlock") - 'fancy_block' - """ - - word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) - word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) - word = word.replace('-', '_') - return word.lower() - - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - else: - return camel2snack(class_type.__name__) - - -def build_plugin_layer(cfg, postfix='', **kwargs): - """Build plugin layer. - - Args: - cfg (None or dict): cfg should contain: - - - type (str): identify plugin layer type. - - layer args: args needed to instantiate a plugin layer. - postfix (int, str): appended into norm abbreviation to - create named layer. Default: ''. - - Returns: - tuple[str, nn.Module]: The first one is the concatenation of - abbreviation and postfix. The second is the created plugin layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in PLUGIN_LAYERS: - raise KeyError(f'Unrecognized plugin type {layer_type}') - - plugin_layer = PLUGIN_LAYERS.get(layer_type) - abbr = infer_abbr(plugin_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - layer = plugin_layer(**kwargs, **cfg_) - - return name, layer diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/registry.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/registry.py deleted file mode 100644 index c29279776..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/registry.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -CONV_LAYERS = Registry('conv layer') -NORM_LAYERS = Registry('norm layer') -ACTIVATION_LAYERS = Registry('activation layer') -PADDING_LAYERS = Registry('padding layer') -UPSAMPLE_LAYERS = Registry('upsample layer') -PLUGIN_LAYERS = Registry('plugin layer') - -DROPOUT_LAYERS = Registry('drop out layers') -POSITIONAL_ENCODING = Registry('position encoding') -ATTENTION = Registry('attention') -FEEDFORWARD_NETWORK = Registry('feed-forward Network') -TRANSFORMER_LAYER = Registry('transformerLayer') -TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/scale.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/scale.py deleted file mode 100644 index c905fffcc..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/scale.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class Scale(nn.Module): - """A learnable scale parameter. - - This layer scales the input by a learnable factor. It multiplies a - learnable scale parameter of shape (1,) with input of any shape. - - Args: - scale (float): Initial value of scale factor. Default: 1.0 - """ - - def __init__(self, scale=1.0): - super(Scale, self).__init__() - self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) - - def forward(self, x): - return x * self.scale diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/swish.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/swish.py deleted file mode 100644 index e2ca8ed7b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/swish.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class Swish(nn.Module): - """Swish Module. - - This module applies the swish function: - - .. math:: - Swish(x) = x * Sigmoid(x) - - Returns: - Tensor: The output tensor. - """ - - def __init__(self): - super(Swish, self).__init__() - - def forward(self, x): - return x * torch.sigmoid(x) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/transformer.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/transformer.py deleted file mode 100644 index 70c6623c7..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/transformer.py +++ /dev/null @@ -1,944 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings -from typing import Sequence - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.cnn import (Linear, build_activation_layer, build_conv_layer, - build_norm_layer) -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential -from mmcv.utils import (ConfigDict, build_from_cfg, deprecated_api_warning, - to_2tuple) -from .drop import build_dropout -from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, - TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) - -# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file -try: - from mmcv.ops.multi_scale_deform_attn import \ - MultiScaleDeformableAttention # noqa F401 - warnings.warn( - ImportWarning( - '``MultiScaleDeformableAttention`` has been moved to ' - '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 - '``from mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 - 'to ``from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 - )) - -except ImportError: - warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' - '``mmcv.ops.multi_scale_deform_attn``, ' - 'You should install ``mmcv-full`` if you need this module. ') - - -def build_positional_encoding(cfg, default_args=None): - """Builder for Position Encoding.""" - return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) - - -def build_attention(cfg, default_args=None): - """Builder for attention.""" - return build_from_cfg(cfg, ATTENTION, default_args) - - -def build_feedforward_network(cfg, default_args=None): - """Builder for feed-forward network (FFN).""" - return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) - - -def build_transformer_layer(cfg, default_args=None): - """Builder for transformer layer.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) - - -def build_transformer_layer_sequence(cfg, default_args=None): - """Builder for transformer encoder and transformer decoder.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) - - -class AdaptivePadding(nn.Module): - """Applies padding adaptively to the input. - - This module can make input get fully covered by filter - you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad - zero around input. The "corner" mode would pad zero - to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel. Default: 1. - stride (int | tuple): Stride of the filter. Default: 1. - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - super(AdaptivePadding, self).__init__() - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - """Calculate the padding size of input. - - Args: - input_shape (:obj:`torch.Size`): arrange as (H, W). - - Returns: - Tuple[int]: The padding size along the - original H and W directions - """ - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - """Add padding to `x` - - Args: - x (Tensor): Input tensor has shape (B, C, H, W). - - Returns: - Tensor: The tensor with adaptive padding - """ - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The type of convolution - to generate patch embedding. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int): The slide stride of embedding conv. - Default: 16. - padding (int | tuple | string): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only works when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=16, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super(PatchEmbed, self).__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adaptive_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # e.g. when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adaptive_padding: - pad_h, pad_w = self.adaptive_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adaptive_padding: - x = self.adaptive_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map ((used in Swin Transformer)). - Our implementation uses `nn.Unfold` to - merge patches, which is about 25% faster than the original - implementation. However, we need to modify pretrained - models for compatibility. - - Args: - in_channels (int): The num of input channels. - to gets fully covered by filter and stride you specified. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adaptive_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - - if self.adaptive_padding: - x = self.adaptive_padding(x) - H, W = x.shape[-2:] - - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - x = self.sampler(x) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size - - -@ATTENTION.register_module() -class MultiheadAttention(BaseModule): - """A wrapper for ``torch.nn.MultiheadAttention``. - - This module implements MultiheadAttention with identity connection, - and positional encoding is also passed as input. - - Args: - embed_dims (int): The embedding dimension. - num_heads (int): Parallel attention heads. - attn_drop (float): A Dropout layer on attn_output_weights. - Default: 0.0. - proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. - Default: 0.0. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): When it is True, Key, Query and Value are shape of - (batch, n, embed_dim), otherwise (n, batch, embed_dim). - Default to False. - """ - - def __init__(self, - embed_dims, - num_heads, - attn_drop=0., - proj_drop=0., - dropout_layer=dict(type='Dropout', drop_prob=0.), - init_cfg=None, - batch_first=False, - **kwargs): - super(MultiheadAttention, self).__init__(init_cfg) - if 'dropout' in kwargs: - warnings.warn( - 'The arguments `dropout` in MultiheadAttention ' - 'has been deprecated, now you can separately ' - 'set `attn_drop`(float), proj_drop(float), ' - 'and `dropout_layer`(dict) ', DeprecationWarning) - attn_drop = kwargs['dropout'] - dropout_layer['drop_prob'] = kwargs.pop('dropout') - - self.embed_dims = embed_dims - self.num_heads = num_heads - self.batch_first = batch_first - - self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, - **kwargs) - - self.proj_drop = nn.Dropout(proj_drop) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else nn.Identity() - - @deprecated_api_warning({'residual': 'identity'}, - cls_name='MultiheadAttention') - def forward(self, - query, - key=None, - value=None, - identity=None, - query_pos=None, - key_pos=None, - attn_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `MultiheadAttention`. - - **kwargs allow passing a more general data flow when combining - with other operations in `transformerlayer`. - - Args: - query (Tensor): The input query with shape [num_queries, bs, - embed_dims] if self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - If None, the ``query`` will be used. Defaults to None. - value (Tensor): The value tensor with same shape as `key`. - Same in `nn.MultiheadAttention.forward`. Defaults to None. - If None, the `key` will be used. - identity (Tensor): This tensor, with the same shape as x, - will be used for the identity link. - If None, `x` will be used. Defaults to None. - query_pos (Tensor): The positional encoding for query, with - the same shape as `x`. If not None, it will - be added to `x` before forward function. Defaults to None. - key_pos (Tensor): The positional encoding for `key`, with the - same shape as `key`. Defaults to None. If not None, it will - be added to `key` before forward function. If None, and - `query_pos` has the same shape as `key`, then `query_pos` - will be used for `key_pos`. Defaults to None. - attn_mask (Tensor): ByteTensor mask with shape [num_queries, - num_keys]. Same in `nn.MultiheadAttention.forward`. - Defaults to None. - key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. - Defaults to None. - - Returns: - Tensor: forwarded results with shape - [num_queries, bs, embed_dims] - if self.batch_first is False, else - [bs, num_queries embed_dims]. - """ - - if key is None: - key = query - if value is None: - value = key - if identity is None: - identity = query - if key_pos is None: - if query_pos is not None: - # use query_pos if key_pos is not available - if query_pos.shape == key.shape: - key_pos = query_pos - else: - warnings.warn(f'position encoding of key is' - f'missing in {self.__class__.__name__}.') - if query_pos is not None: - query = query + query_pos - if key_pos is not None: - key = key + key_pos - - # Because the dataflow('key', 'query', 'value') of - # ``torch.nn.MultiheadAttention`` is (num_query, batch, - # embed_dims), We should adjust the shape of dataflow from - # batch_first (batch, num_query, embed_dims) to num_query_first - # (num_query ,batch, embed_dims), and recover ``attn_output`` - # from num_query_first to batch_first. - if self.batch_first: - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - out = self.attn( - query=query, - key=key, - value=value, - attn_mask=attn_mask, - key_padding_mask=key_padding_mask)[0] - - if self.batch_first: - out = out.transpose(0, 1) - - return identity + self.dropout_layer(self.proj_drop(out)) - - -@FEEDFORWARD_NETWORK.register_module() -class FFN(BaseModule): - """Implements feed-forward networks (FFNs) with identity connection. - - Args: - embed_dims (int): The feature dimension. Same as - `MultiheadAttention`. Defaults: 256. - feedforward_channels (int): The hidden dimension of FFNs. - Defaults: 1024. - num_fcs (int, optional): The number of fully-connected layers in - FFNs. Default: 2. - act_cfg (dict, optional): The activation config for FFNs. - Default: dict(type='ReLU') - ffn_drop (float, optional): Probability of an element to be - zeroed in FFN. Default 0.0. - add_identity (bool, optional): Whether to add the - identity connection. Default: `True`. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - @deprecated_api_warning( - { - 'dropout': 'ffn_drop', - 'add_residual': 'add_identity' - }, - cls_name='FFN') - def __init__(self, - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - act_cfg=dict(type='ReLU', inplace=True), - ffn_drop=0., - dropout_layer=None, - add_identity=True, - init_cfg=None, - **kwargs): - super(FFN, self).__init__(init_cfg) - assert num_fcs >= 2, 'num_fcs should be no less ' \ - f'than 2. got {num_fcs}.' - self.embed_dims = embed_dims - self.feedforward_channels = feedforward_channels - self.num_fcs = num_fcs - self.act_cfg = act_cfg - self.activate = build_activation_layer(act_cfg) - - layers = [] - in_channels = embed_dims - for _ in range(num_fcs - 1): - layers.append( - Sequential( - Linear(in_channels, feedforward_channels), self.activate, - nn.Dropout(ffn_drop))) - in_channels = feedforward_channels - layers.append(Linear(feedforward_channels, embed_dims)) - layers.append(nn.Dropout(ffn_drop)) - self.layers = Sequential(*layers) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else torch.nn.Identity() - self.add_identity = add_identity - - @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') - def forward(self, x, identity=None): - """Forward function for `FFN`. - - The function would add x to the output tensor if residue is None. - """ - out = self.layers(x) - if not self.add_identity: - return self.dropout_layer(out) - if identity is None: - identity = x - return identity + self.dropout_layer(out) - - -@TRANSFORMER_LAYER.register_module() -class BaseTransformerLayer(BaseModule): - """Base `TransformerLayer` for vision transformer. - - It can be built from `mmcv.ConfigDict` and support more flexible - customization, for example, using any number of `FFN or LN ` and - use different kinds of `attention` by specifying a list of `ConfigDict` - named `attn_cfgs`. It is worth mentioning that it supports `prenorm` - when you specifying `norm` as the first element of `operation_order`. - More details about the `prenorm`: `On Layer Normalization in the - Transformer Architecture `_ . - - Args: - attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for `self_attention` or `cross_attention` modules, - The order of the configs in the list should be consistent with - corresponding attentions in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. Default: None. - ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for FFN, The order of the configs in the list should be - consistent with corresponding ffn in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. - operation_order (tuple[str]): The execution order of operation - in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). - Support `prenorm` when you specifying first element as `norm`. - Default:None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): Key, Query and Value are shape - of (batch, n, embed_dim) - or (n, batch, embed_dim). Default to False. - """ - - def __init__(self, - attn_cfgs=None, - ffn_cfgs=dict( - type='FFN', - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - ffn_drop=0., - act_cfg=dict(type='ReLU', inplace=True), - ), - operation_order=None, - norm_cfg=dict(type='LN'), - init_cfg=None, - batch_first=False, - **kwargs): - - deprecated_args = dict( - feedforward_channels='feedforward_channels', - ffn_dropout='ffn_drop', - ffn_num_fcs='num_fcs') - for ori_name, new_name in deprecated_args.items(): - if ori_name in kwargs: - warnings.warn( - f'The arguments `{ori_name}` in BaseTransformerLayer ' - f'has been deprecated, now you should set `{new_name}` ' - f'and other FFN related arguments ' - f'to a dict named `ffn_cfgs`. ', DeprecationWarning) - ffn_cfgs[new_name] = kwargs[ori_name] - - super(BaseTransformerLayer, self).__init__(init_cfg) - - self.batch_first = batch_first - - assert set(operation_order) & set( - ['self_attn', 'norm', 'ffn', 'cross_attn']) == \ - set(operation_order), f'The operation_order of' \ - f' {self.__class__.__name__} should ' \ - f'contains all four operation type ' \ - f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" - - num_attn = operation_order.count('self_attn') + operation_order.count( - 'cross_attn') - if isinstance(attn_cfgs, dict): - attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] - else: - assert num_attn == len(attn_cfgs), f'The length ' \ - f'of attn_cfg {num_attn} is ' \ - f'not consistent with the number of attention' \ - f'in operation_order {operation_order}.' - - self.num_attn = num_attn - self.operation_order = operation_order - self.norm_cfg = norm_cfg - self.pre_norm = operation_order[0] == 'norm' - self.attentions = ModuleList() - - index = 0 - for operation_name in operation_order: - if operation_name in ['self_attn', 'cross_attn']: - if 'batch_first' in attn_cfgs[index]: - assert self.batch_first == attn_cfgs[index]['batch_first'] - else: - attn_cfgs[index]['batch_first'] = self.batch_first - attention = build_attention(attn_cfgs[index]) - # Some custom attentions used as `self_attn` - # or `cross_attn` can have different behavior. - attention.operation_name = operation_name - self.attentions.append(attention) - index += 1 - - self.embed_dims = self.attentions[0].embed_dims - - self.ffns = ModuleList() - num_ffns = operation_order.count('ffn') - if isinstance(ffn_cfgs, dict): - ffn_cfgs = ConfigDict(ffn_cfgs) - if isinstance(ffn_cfgs, dict): - ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] - assert len(ffn_cfgs) == num_ffns - for ffn_index in range(num_ffns): - if 'embed_dims' not in ffn_cfgs[ffn_index]: - ffn_cfgs[ffn_index]['embed_dims'] = self.embed_dims - else: - assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims - self.ffns.append( - build_feedforward_network(ffn_cfgs[ffn_index], - dict(type='FFN'))) - - self.norms = ModuleList() - num_norms = operation_order.count('norm') - for _ in range(num_norms): - self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) - - def forward(self, - query, - key=None, - value=None, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerDecoderLayer`. - - **kwargs contains some specific arguments of attentions. - - Args: - query (Tensor): The input query with shape - [num_queries, bs, embed_dims] if - self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - value (Tensor): The value tensor with same shape as `key`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor] | None): 2D Tensor used in - calculation of corresponding attention. The length of - it should equal to the number of `attention` in - `operation_order`. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in `self_attn` layer. - Defaults to None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: forwarded results with shape [num_queries, bs, embed_dims]. - """ - - norm_index = 0 - attn_index = 0 - ffn_index = 0 - identity = query - if attn_masks is None: - attn_masks = [None for _ in range(self.num_attn)] - elif isinstance(attn_masks, torch.Tensor): - attn_masks = [ - copy.deepcopy(attn_masks) for _ in range(self.num_attn) - ] - warnings.warn(f'Use same attn_mask in all attentions in ' - f'{self.__class__.__name__} ') - else: - assert len(attn_masks) == self.num_attn, f'The length of ' \ - f'attn_masks {len(attn_masks)} must be equal ' \ - f'to the number of attention in ' \ - f'operation_order {self.num_attn}' - - for layer in self.operation_order: - if layer == 'self_attn': - temp_key = temp_value = query - query = self.attentions[attn_index]( - query, - temp_key, - temp_value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=query_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=query_key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'norm': - query = self.norms[norm_index](query) - norm_index += 1 - - elif layer == 'cross_attn': - query = self.attentions[attn_index]( - query, - key, - value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=key_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'ffn': - query = self.ffns[ffn_index]( - query, identity if self.pre_norm else None) - ffn_index += 1 - - return query - - -@TRANSFORMER_LAYER_SEQUENCE.register_module() -class TransformerLayerSequence(BaseModule): - """Base class for TransformerEncoder and TransformerDecoder in vision - transformer. - - As base-class of Encoder and Decoder in vision transformer. - Support customization such as specifying different kind - of `transformer_layer` in `transformer_coder`. - - Args: - transformerlayer (list[obj:`mmcv.ConfigDict`] | - obj:`mmcv.ConfigDict`): Config of transformerlayer - in TransformerCoder. If it is obj:`mmcv.ConfigDict`, - it would be repeated `num_layer` times to a - list[`mmcv.ConfigDict`]. Default: None. - num_layers (int): The number of `TransformerLayer`. Default: None. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): - super(TransformerLayerSequence, self).__init__(init_cfg) - if isinstance(transformerlayers, dict): - transformerlayers = [ - copy.deepcopy(transformerlayers) for _ in range(num_layers) - ] - else: - assert isinstance(transformerlayers, list) and \ - len(transformerlayers) == num_layers - self.num_layers = num_layers - self.layers = ModuleList() - for i in range(num_layers): - self.layers.append(build_transformer_layer(transformerlayers[i])) - self.embed_dims = self.layers[0].embed_dims - self.pre_norm = self.layers[0].pre_norm - - def forward(self, - query, - key, - value, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerCoder`. - - Args: - query (Tensor): Input query with shape - `(num_queries, bs, embed_dims)`. - key (Tensor): The key tensor with shape - `(num_keys, bs, embed_dims)`. - value (Tensor): The value tensor with shape - `(num_keys, bs, embed_dims)`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor], optional): Each element is 2D Tensor - which is used in calculation of corresponding attention in - operation_order. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in self-attention - Default: None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: results with shape [num_queries, bs, embed_dims]. - """ - for layer in self.layers: - query = layer( - query, - key, - value, - query_pos=query_pos, - key_pos=key_pos, - attn_masks=attn_masks, - query_key_padding_mask=query_key_padding_mask, - key_padding_mask=key_padding_mask, - **kwargs) - return query diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/upsample.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/upsample.py deleted file mode 100644 index 0fd21fbf9..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/upsample.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import xavier_init -from .registry import UPSAMPLE_LAYERS - -UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) -UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) - - -@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') -class PixelShufflePack(nn.Module): - """Pixel Shuffle upsample layer. - - This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to - achieve a simple upsampling with pixel shuffle. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of the conv layer to expand the - channels. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super(PixelShufflePack, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - xavier_init(self.upsample_conv, distribution='uniform') - - def forward(self, x): - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x - - -def build_upsample_layer(cfg, *args, **kwargs): - """Build upsample layer. - - Args: - cfg (dict): The upsample layer config, which should contain: - - - type (str): Layer type. - - scale_factor (int): Upsample ratio, which is not applicable to - deconv. - - layer args: Args needed to instantiate a upsample layer. - args (argument list): Arguments passed to the ``__init__`` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the - ``__init__`` method of the corresponding conv layer. - - Returns: - nn.Module: Created upsample layer. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - raise KeyError( - f'the cfg dict must contain the key "type", but got {cfg}') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in UPSAMPLE_LAYERS: - raise KeyError(f'Unrecognized upsample type {layer_type}') - else: - upsample = UPSAMPLE_LAYERS.get(layer_type) - - if upsample is nn.Upsample: - cfg_['mode'] = layer_type - layer = upsample(*args, **kwargs, **cfg_) - return layer diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/wrappers.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/wrappers.py deleted file mode 100644 index 8aebf67bf..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/bricks/wrappers.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 - -Wrap some nn modules to support empty tensor input. Currently, these wrappers -are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask -heads are trained on only positive RoIs. -""" -import math - -import torch -import torch.nn as nn -from torch.nn.modules.utils import _pair, _triple - -from .registry import CONV_LAYERS, UPSAMPLE_LAYERS - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def obsolete_torch_version(torch_version, version_threshold): - return torch_version == 'parrots' or torch_version <= version_threshold - - -class NewEmptyTensorOp(torch.autograd.Function): - - @staticmethod - def forward(ctx, x, new_shape): - ctx.shape = x.shape - return x.new_empty(new_shape) - - @staticmethod - def backward(ctx, grad): - shape = ctx.shape - return NewEmptyTensorOp.apply(grad, shape), None - - -@CONV_LAYERS.register_module('Conv', force=True) -class Conv2d(nn.Conv2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module('Conv3d', force=True) -class Conv3d(nn.Conv3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv') -@UPSAMPLE_LAYERS.register_module('deconv', force=True) -class ConvTranspose2d(nn.ConvTranspose2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv3d') -@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) -class ConvTranspose3d(nn.ConvTranspose3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -class MaxPool2d(nn.MaxPool2d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), - _pair(self.padding), _pair(self.stride), - _pair(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class MaxPool3d(nn.MaxPool3d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), - _triple(self.padding), - _triple(self.stride), - _triple(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class Linear(torch.nn.Linear): - - def forward(self, x): - # empty tensor forward of Linear layer is supported in Pytorch 1.6 - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): - out_shape = [x.shape[0], self.out_features] - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/builder.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/builder.py deleted file mode 100644 index 7567316c5..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/builder.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..runner import Sequential -from ..utils import Registry, build_from_cfg - - -def build_model_from_cfg(cfg, registry, default_args=None): - """Build a PyTorch model from config dict(s). Different from - ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. - - Args: - cfg (dict, list[dict]): The config of modules, is is either a config - dict or a list of config dicts. If cfg is a list, a - the built modules will be wrapped with ``nn.Sequential``. - registry (:obj:`Registry`): A registry the module belongs to. - default_args (dict, optional): Default arguments to build the module. - Defaults to None. - - Returns: - nn.Module: A built nn module. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return Sequential(*modules) - else: - return build_from_cfg(cfg, registry, default_args) - - -MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/__init__.py deleted file mode 100644 index a263e31c1..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .flops_counter import get_model_complexity_info -from .fuse_conv_bn import fuse_conv_bn -from .sync_bn import revert_sync_batchnorm -from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, - KaimingInit, NormalInit, PretrainedInit, - TruncNormalInit, UniformInit, XavierInit, - bias_init_with_prob, caffe2_xavier_init, - constant_init, initialize, kaiming_init, normal_init, - trunc_normal_init, uniform_init, xavier_init) - -__all__ = [ - 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', - 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', - 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'revert_sync_batchnorm' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/flops_counter.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/flops_counter.py deleted file mode 100644 index a6045db84..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/flops_counter.py +++ /dev/null @@ -1,599 +0,0 @@ -# Modified from flops-counter.pytorch by Vladislav Sovrasov -# original repo: https://github.com/sovrasov/flops-counter.pytorch - -# MIT License - -# Copyright (c) 2018 Vladislav Sovrasov - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -import warnings -from functools import partial - -import numpy as np -import torch -import torch.nn as nn - -import mmcv - - -def get_model_complexity_info(model, - input_shape, - print_per_layer_stat=True, - as_strings=True, - input_constructor=None, - flush=False, - ost=sys.stdout): - """Get complexity information of a model. - - This method can calculate FLOPs and parameter counts of a model with - corresponding input shape. It can also print complexity information for - each layer in a model. - - Supported layers are listed as below: - - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. - - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, - ``nn.LeakyReLU``, ``nn.ReLU6``. - - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, - ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, - ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, - ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, - ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. - - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, - ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, - ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. - - Linear: ``nn.Linear``. - - Deconvolution: ``nn.ConvTranspose2d``. - - Upsample: ``nn.Upsample``. - - Args: - model (nn.Module): The model for complexity calculation. - input_shape (tuple): Input shape used for calculation. - print_per_layer_stat (bool): Whether to print complexity information - for each layer in a model. Default: True. - as_strings (bool): Output FLOPs and params counts in a string form. - Default: True. - input_constructor (None | callable): If specified, it takes a callable - method that generates input. otherwise, it will generate a random - tensor with input shape to calculate FLOPs. Default: None. - flush (bool): same as that in :func:`print`. Default: False. - ost (stream): same as ``file`` param in :func:`print`. - Default: sys.stdout. - - Returns: - tuple[float | str]: If ``as_strings`` is set to True, it will return - FLOPs and parameter counts in a string format. otherwise, it will - return those in a float number format. - """ - assert type(input_shape) is tuple - assert len(input_shape) >= 1 - assert isinstance(model, nn.Module) - flops_model = add_flops_counting_methods(model) - flops_model.eval() - flops_model.start_flops_count() - if input_constructor: - input = input_constructor(input_shape) - _ = flops_model(**input) - else: - try: - batch = torch.ones(()).new_empty( - (1, *input_shape), - dtype=next(flops_model.parameters()).dtype, - device=next(flops_model.parameters()).device) - except StopIteration: - # Avoid StopIteration for models which have no parameters, - # like `nn.Relu()`, `nn.AvgPool2d`, etc. - batch = torch.ones(()).new_empty((1, *input_shape)) - - _ = flops_model(batch) - - flops_count, params_count = flops_model.compute_average_flops_cost() - if print_per_layer_stat: - print_model_with_flops( - flops_model, flops_count, params_count, ost=ost, flush=flush) - flops_model.stop_flops_count() - - if as_strings: - return flops_to_string(flops_count), params_to_string(params_count) - - return flops_count, params_count - - -def flops_to_string(flops, units='GFLOPs', precision=2): - """Convert FLOPs number into a string. - - Note that Here we take a multiply-add counts as one FLOP. - - Args: - flops (float): FLOPs number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', - 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically - choose the most suitable unit for FLOPs. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted FLOPs number with units. - - Examples: - >>> flops_to_string(1e9) - '1.0 GFLOPs' - >>> flops_to_string(2e5, 'MFLOPs') - '0.2 MFLOPs' - >>> flops_to_string(3e-9, None) - '3e-09 FLOPs' - """ - if units is None: - if flops // 10**9 > 0: - return str(round(flops / 10.**9, precision)) + ' GFLOPs' - elif flops // 10**6 > 0: - return str(round(flops / 10.**6, precision)) + ' MFLOPs' - elif flops // 10**3 > 0: - return str(round(flops / 10.**3, precision)) + ' KFLOPs' - else: - return str(flops) + ' FLOPs' - else: - if units == 'GFLOPs': - return str(round(flops / 10.**9, precision)) + ' ' + units - elif units == 'MFLOPs': - return str(round(flops / 10.**6, precision)) + ' ' + units - elif units == 'KFLOPs': - return str(round(flops / 10.**3, precision)) + ' ' + units - else: - return str(flops) + ' FLOPs' - - -def params_to_string(num_params, units=None, precision=2): - """Convert parameter number into a string. - - Args: - num_params (float): Parameter number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'M', - 'K' and ''. If set to None, it will automatically choose the most - suitable unit for Parameter number. Default: None. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted parameter number with units. - - Examples: - >>> params_to_string(1e9) - '1000.0 M' - >>> params_to_string(2e5) - '200.0 k' - >>> params_to_string(3e-9) - '3e-09' - """ - if units is None: - if num_params // 10**6 > 0: - return str(round(num_params / 10**6, precision)) + ' M' - elif num_params // 10**3: - return str(round(num_params / 10**3, precision)) + ' k' - else: - return str(num_params) - else: - if units == 'M': - return str(round(num_params / 10.**6, precision)) + ' ' + units - elif units == 'K': - return str(round(num_params / 10.**3, precision)) + ' ' + units - else: - return str(num_params) - - -def print_model_with_flops(model, - total_flops, - total_params, - units='GFLOPs', - precision=3, - ost=sys.stdout, - flush=False): - """Print a model with FLOPs for each layer. - - Args: - model (nn.Module): The model to be printed. - total_flops (float): Total FLOPs of the model. - total_params (float): Total parameter counts of the model. - units (str | None): Converted FLOPs units. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 3. - ost (stream): same as `file` param in :func:`print`. - Default: sys.stdout. - flush (bool): same as that in :func:`print`. Default: False. - - Example: - >>> class ExampleModel(nn.Module): - - >>> def __init__(self): - >>> super().__init__() - >>> self.conv1 = nn.Conv2d(3, 8, 3) - >>> self.conv2 = nn.Conv2d(8, 256, 3) - >>> self.conv3 = nn.Conv2d(256, 8, 3) - >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) - >>> self.flatten = nn.Flatten() - >>> self.fc = nn.Linear(8, 1) - - >>> def forward(self, x): - >>> x = self.conv1(x) - >>> x = self.conv2(x) - >>> x = self.conv3(x) - >>> x = self.avg_pool(x) - >>> x = self.flatten(x) - >>> x = self.fc(x) - >>> return x - - >>> model = ExampleModel() - >>> x = (3, 16, 16) - to print the complexity information state for each layer, you can use - >>> get_model_complexity_info(model, x) - or directly use - >>> print_model_with_flops(model, 4579784.0, 37361) - ExampleModel( - 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, - (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 - (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) - (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) - (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) - (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) - (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) - ) - """ - - def accumulate_params(self): - if is_supported_instance(self): - return self.__params__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_params() - return sum - - def accumulate_flops(self): - if is_supported_instance(self): - return self.__flops__ / model.__batch_counter__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_flops() - return sum - - def flops_repr(self): - accumulated_num_params = self.accumulate_params() - accumulated_flops_cost = self.accumulate_flops() - return ', '.join([ - params_to_string( - accumulated_num_params, units='M', precision=precision), - '{:.3%} Params'.format(accumulated_num_params / total_params), - flops_to_string( - accumulated_flops_cost, units=units, precision=precision), - '{:.3%} FLOPs'.format(accumulated_flops_cost / total_flops), - self.original_extra_repr() - ]) - - def add_extra_repr(m): - m.accumulate_flops = accumulate_flops.__get__(m) - m.accumulate_params = accumulate_params.__get__(m) - flops_extra_repr = flops_repr.__get__(m) - if m.extra_repr != flops_extra_repr: - m.original_extra_repr = m.extra_repr - m.extra_repr = flops_extra_repr - assert m.extra_repr != m.original_extra_repr - - def del_extra_repr(m): - if hasattr(m, 'original_extra_repr'): - m.extra_repr = m.original_extra_repr - del m.original_extra_repr - if hasattr(m, 'accumulate_flops'): - del m.accumulate_flops - - model.apply(add_extra_repr) - print(model, file=ost, flush=flush) - model.apply(del_extra_repr) - - -def get_model_parameters_number(model): - """Calculate parameter number of a model. - - Args: - model (nn.module): The model for parameter number calculation. - - Returns: - float: Parameter number of the model. - """ - num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - return num_params - - -def add_flops_counting_methods(net_main_module): - # adding additional methods to the existing module object, - # this is done this way so that each function has access to self object - net_main_module.start_flops_count = start_flops_count.__get__( - net_main_module) - net_main_module.stop_flops_count = stop_flops_count.__get__( - net_main_module) - net_main_module.reset_flops_count = reset_flops_count.__get__( - net_main_module) - net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # noqa: E501 - net_main_module) - - net_main_module.reset_flops_count() - - return net_main_module - - -def compute_average_flops_cost(self): - """Compute average FLOPs cost. - - A method to compute average FLOPs cost, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - - Returns: - float: Current mean flops consumption per image. - """ - batches_count = self.__batch_counter__ - flops_sum = 0 - for module in self.modules(): - if is_supported_instance(module): - flops_sum += module.__flops__ - params_sum = get_model_parameters_number(self) - return flops_sum / batches_count, params_sum - - -def start_flops_count(self): - """Activate the computation of mean flops consumption per image. - - A method to activate the computation of mean flops consumption per image. - which will be available after ``add_flops_counting_methods()`` is called on - a desired net object. It should be called before running the network. - """ - add_batch_counter_hook_function(self) - - def add_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - return - - else: - handle = module.register_forward_hook( - get_modules_mapping()[type(module)]) - - module.__flops_handle__ = handle - - self.apply(partial(add_flops_counter_hook_function)) - - -def stop_flops_count(self): - """Stop computing the mean flops consumption per image. - - A method to stop computing the mean flops consumption per image, which will - be available after ``add_flops_counting_methods()`` is called on a desired - net object. It can be called to pause the computation whenever. - """ - remove_batch_counter_hook_function(self) - self.apply(remove_flops_counter_hook_function) - - -def reset_flops_count(self): - """Reset statistics computed so far. - - A method to Reset computed statistics, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - """ - add_batch_counter_variables_or_reset(self) - self.apply(add_flops_counter_variable_or_reset) - - -# ---- Internal functions -def empty_flops_counter_hook(module, input, output): - module.__flops__ += 0 - - -def upsample_flops_counter_hook(module, input, output): - output_size = output[0] - batch_size = output_size.shape[0] - output_elements_count = batch_size - for val in output_size.shape[1:]: - output_elements_count *= val - module.__flops__ += int(output_elements_count) - - -def relu_flops_counter_hook(module, input, output): - active_elements_count = output.numel() - module.__flops__ += int(active_elements_count) - - -def linear_flops_counter_hook(module, input, output): - input = input[0] - output_last_dim = output.shape[ - -1] # pytorch checks dimensions, so here we don't care much - module.__flops__ += int(np.prod(input.shape) * output_last_dim) - - -def pool_flops_counter_hook(module, input, output): - input = input[0] - module.__flops__ += int(np.prod(input.shape)) - - -def norm_flops_counter_hook(module, input, output): - input = input[0] - - batch_flops = np.prod(input.shape) - if (getattr(module, 'affine', False) - or getattr(module, 'elementwise_affine', False)): - batch_flops *= 2 - module.__flops__ += int(batch_flops) - - -def deconv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - input_height, input_width = input.shape[2:] - - kernel_height, kernel_width = conv_module.kernel_size - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = ( - kernel_height * kernel_width * in_channels * filters_per_channel) - - active_elements_count = batch_size * input_height * input_width - overall_conv_flops = conv_per_position_flops * active_elements_count - bias_flops = 0 - if conv_module.bias is not None: - output_height, output_width = output.shape[2:] - bias_flops = out_channels * batch_size * output_height * output_width - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def conv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - output_dims = list(output.shape[2:]) - - kernel_dims = list(conv_module.kernel_size) - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = int( - np.prod(kernel_dims)) * in_channels * filters_per_channel - - active_elements_count = batch_size * int(np.prod(output_dims)) - - overall_conv_flops = conv_per_position_flops * active_elements_count - - bias_flops = 0 - - if conv_module.bias is not None: - - bias_flops = out_channels * active_elements_count - - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def batch_counter_hook(module, input, output): - batch_size = 1 - if len(input) > 0: - # Can have multiple inputs, getting the first one - input = input[0] - batch_size = len(input) - else: - warnings.warn('No positional inputs found for a module, ' - 'assuming batch size is 1.') - module.__batch_counter__ += batch_size - - -def add_batch_counter_variables_or_reset(module): - - module.__batch_counter__ = 0 - - -def add_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - return - - handle = module.register_forward_hook(batch_counter_hook) - module.__batch_counter_handle__ = handle - - -def remove_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - module.__batch_counter_handle__.remove() - del module.__batch_counter_handle__ - - -def add_flops_counter_variable_or_reset(module): - if is_supported_instance(module): - if hasattr(module, '__flops__') or hasattr(module, '__params__'): - warnings.warn('variables __flops__ or __params__ are already ' - 'defined for the module' + type(module).__name__ + - ' ptflops can affect your code!') - module.__flops__ = 0 - module.__params__ = get_model_parameters_number(module) - - -def is_supported_instance(module): - if type(module) in get_modules_mapping(): - return True - return False - - -def remove_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - module.__flops_handle__.remove() - del module.__flops_handle__ - - -def get_modules_mapping(): - return { - # convolutions - nn.Conv1d: conv_flops_counter_hook, - nn.Conv2d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, - nn.Conv3d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, - # activations - nn.ReLU: relu_flops_counter_hook, - nn.PReLU: relu_flops_counter_hook, - nn.ELU: relu_flops_counter_hook, - nn.LeakyReLU: relu_flops_counter_hook, - nn.ReLU6: relu_flops_counter_hook, - # poolings - nn.MaxPool1d: pool_flops_counter_hook, - nn.AvgPool1d: pool_flops_counter_hook, - nn.AvgPool2d: pool_flops_counter_hook, - nn.MaxPool2d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, - nn.MaxPool3d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, - nn.AvgPool3d: pool_flops_counter_hook, - nn.AdaptiveMaxPool1d: pool_flops_counter_hook, - nn.AdaptiveAvgPool1d: pool_flops_counter_hook, - nn.AdaptiveMaxPool2d: pool_flops_counter_hook, - nn.AdaptiveAvgPool2d: pool_flops_counter_hook, - nn.AdaptiveMaxPool3d: pool_flops_counter_hook, - nn.AdaptiveAvgPool3d: pool_flops_counter_hook, - # normalizations - nn.BatchNorm1d: norm_flops_counter_hook, - nn.BatchNorm2d: norm_flops_counter_hook, - nn.BatchNorm3d: norm_flops_counter_hook, - nn.GroupNorm: norm_flops_counter_hook, - nn.InstanceNorm1d: norm_flops_counter_hook, - nn.InstanceNorm2d: norm_flops_counter_hook, - nn.InstanceNorm3d: norm_flops_counter_hook, - nn.LayerNorm: norm_flops_counter_hook, - # FC - nn.Linear: linear_flops_counter_hook, - mmcv.cnn.bricks.Linear: linear_flops_counter_hook, - # Upscale - nn.Upsample: upsample_flops_counter_hook, - # Deconvolution - nn.ConvTranspose2d: deconv_flops_counter_hook, - mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, - } diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py deleted file mode 100644 index cb7076f80..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def _fuse_conv_bn(conv, bn): - """Fuse conv and bn into one module. - - Args: - conv (nn.Module): Conv to be fused. - bn (nn.Module): BN to be fused. - - Returns: - nn.Module: Fused module. - """ - conv_w = conv.weight - conv_b = conv.bias if conv.bias is not None else torch.zeros_like( - bn.running_mean) - - factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) - conv.weight = nn.Parameter(conv_w * - factor.reshape([conv.out_channels, 1, 1, 1])) - conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) - return conv - - -def fuse_conv_bn(module): - """Recursively fuse conv and bn in a module. - - During inference, the functionary of batch norm layers is turned off - but only the mean and var alone channels are used, which exposes the - chance to fuse it with the preceding conv layers to save computations and - simplify network structures. - - Args: - module (nn.Module): Module to be fused. - - Returns: - nn.Module: Fused module. - """ - last_conv = None - last_conv_name = None - - for name, child in module.named_children(): - if isinstance(child, - (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): - if last_conv is None: # only fuse BN that is after Conv - continue - fused_conv = _fuse_conv_bn(last_conv, child) - module._modules[last_conv_name] = fused_conv - # To reduce changes, set BN as Identity instead of deleting it. - module._modules[name] = nn.Identity() - last_conv = None - elif isinstance(child, nn.Conv2d): - last_conv = child - last_conv_name = name - else: - fuse_conv_bn(child) - return module diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/sync_bn.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/sync_bn.py deleted file mode 100644 index 0c52526e9..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/sync_bn.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -import mmcv - - -class _BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): - """A general BatchNorm layer without input dimension check. - - Reproduced from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc - is `_check_input_dim` that is designed for tensor sanity checks. - The check has been bypassed in this class for the convenience of converting - SyncBatchNorm. - """ - - def _check_input_dim(self, input): - return - - -def revert_sync_batchnorm(module): - """Helper function to convert all `SyncBatchNorm` (SyncBN) and - `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to - `BatchNormXd` layers. - - Adapted from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - - Args: - module (nn.Module): The module containing `SyncBatchNorm` layers. - - Returns: - module_output: The converted module with `BatchNormXd` layers. - """ - module_output = module - module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] - if hasattr(mmcv, 'ops'): - module_checklist.append(mmcv.ops.SyncBatchNorm) - if isinstance(module, tuple(module_checklist)): - module_output = _BatchNormXd(module.num_features, module.eps, - module.momentum, module.affine, - module.track_running_stats) - if module.affine: - # no_grad() may not be needed here but - # just to be consistent with `convert_sync_batchnorm()` - with torch.no_grad(): - module_output.weight = module.weight - module_output.bias = module.bias - module_output.running_mean = module.running_mean - module_output.running_var = module.running_var - module_output.num_batches_tracked = module.num_batches_tracked - module_output.training = module.training - # qconfig exists in quantized models - if hasattr(module, 'qconfig'): - module_output.qconfig = module.qconfig - for name, child in module.named_children(): - module_output.add_module(name, revert_sync_batchnorm(child)) - del module - return module_output diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/weight_init.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/weight_init.py deleted file mode 100644 index 0ac08c87f..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/cnn/utils/weight_init.py +++ /dev/null @@ -1,685 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings - -import numpy as np -import torch -import torch.nn as nn -from torch import Tensor - -from mmcv.utils import Registry, build_from_cfg, get_logger, print_log - -INITIALIZERS = Registry('initializer') - - -def update_init_info(module, init_info): - """Update the `_params_init_info` in the module if the value of parameters - are changed. - - Args: - module (obj:`nn.Module`): The module of PyTorch with a user-defined - attribute `_params_init_info` which records the initialization - information. - init_info (str): The string that describes the initialization. - """ - assert hasattr( - module, - '_params_init_info'), f'Can not find `_params_init_info` in {module}' - for name, param in module.named_parameters(): - - assert param in module._params_init_info, ( - f'Find a new :obj:`Parameter` ' - f'named `{name}` during executing the ' - f'`init_weights` of ' - f'`{module.__class__.__name__}`. ' - f'Please do not add or ' - f'replace parameters during executing ' - f'the `init_weights`. ') - - # The parameter has been changed during executing the - # `init_weights` of module - mean_value = param.data.mean() - if module._params_init_info[param]['tmp_mean_value'] != mean_value: - module._params_init_info[param]['init_info'] = init_info - module._params_init_info[param]['tmp_mean_value'] = mean_value - - -def constant_init(module, val, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.constant_(module.weight, val) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def xavier_init(module, gain=1, bias=0, distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.xavier_uniform_(module.weight, gain=gain) - else: - nn.init.xavier_normal_(module.weight, gain=gain) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def normal_init(module, mean=0, std=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.normal_(module.weight, mean, std) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def trunc_normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - trunc_normal_(module.weight, mean, std, a, b) # type: ignore - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) # type: ignore - - -def uniform_init(module, a=0, b=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.uniform_(module.weight, a, b) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def kaiming_init(module, - a=0, - mode='fan_out', - nonlinearity='relu', - bias=0, - distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.kaiming_uniform_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - else: - nn.init.kaiming_normal_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def caffe2_xavier_init(module, bias=0): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - kaiming_init( - module, - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - bias=bias, - distribution='uniform') - - -def bias_init_with_prob(prior_prob): - """initialize conv/fc bias value according to a given probability value.""" - bias_init = float(-np.log((1 - prior_prob) / prior_prob)) - return bias_init - - -def _get_bases_name(m): - return [b.__name__ for b in m.__class__.__bases__] - - -class BaseInit(object): - - def __init__(self, *, bias=0, bias_prob=None, layer=None): - self.wholemodule = False - if not isinstance(bias, (int, float)): - raise TypeError(f'bias must be a number, but got a {type(bias)}') - - if bias_prob is not None: - if not isinstance(bias_prob, float): - raise TypeError(f'bias_prob type must be float, \ - but got {type(bias_prob)}') - - if layer is not None: - if not isinstance(layer, (str, list)): - raise TypeError(f'layer must be a str or a list of str, \ - but got a {type(layer)}') - else: - layer = [] - - if bias_prob is not None: - self.bias = bias_init_with_prob(bias_prob) - else: - self.bias = bias - self.layer = [layer] if isinstance(layer, str) else layer - - def _get_init_info(self): - info = f'{self.__class__.__name__}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Constant') -class ConstantInit(BaseInit): - """Initialize module parameters with constant values. - - Args: - val (int | float): the value to fill the weights in the module with - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, val, **kwargs): - super().__init__(**kwargs) - self.val = val - - def __call__(self, module): - - def init(m): - if self.wholemodule: - constant_init(m, self.val, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - constant_init(m, self.val, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Xavier') -class XavierInit(BaseInit): - r"""Initialize module parameters with values according to the method - described in `Understanding the difficulty of training deep feedforward - neural networks - Glorot, X. & Bengio, Y. (2010). - `_ - - Args: - gain (int | float): an optional scaling factor. Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` - or ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, gain=1, distribution='normal', **kwargs): - super().__init__(**kwargs) - self.gain = gain - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - xavier_init(m, self.gain, self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - xavier_init(m, self.gain, self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: gain={self.gain}, ' \ - f'distribution={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Normal') -class NormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. - - Args: - mean (int | float):the mean of the normal distribution. Defaults to 0. - std (int | float): the standard deviation of the normal distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, mean=0, std=1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.std = std - - def __call__(self, module): - - def init(m): - if self.wholemodule: - normal_init(m, self.mean, self.std, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - normal_init(m, self.mean, self.std, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: mean={self.mean},' \ - f' std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='TruncNormal') -class TruncNormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values - outside :math:`[a, b]`. - - Args: - mean (float): the mean of the normal distribution. Defaults to 0. - std (float): the standard deviation of the normal distribution. - Defaults to 1. - a (float): The minimum cutoff value. - b ( float): The maximum cutoff value. - bias (float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - **kwargs) -> None: - super().__init__(**kwargs) - self.mean = mean - self.std = std - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ - f' mean={self.mean}, std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Uniform') -class UniformInit(BaseInit): - r"""Initialize module parameters with values drawn from the uniform - distribution :math:`\mathcal{U}(a, b)`. - - Args: - a (int | float): the lower bound of the uniform distribution. - Defaults to 0. - b (int | float): the upper bound of the uniform distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, a=0, b=1, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def __call__(self, module): - - def init(m): - if self.wholemodule: - uniform_init(m, self.a, self.b, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - uniform_init(m, self.a, self.b, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a},' \ - f' b={self.b}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Kaiming') -class KaimingInit(BaseInit): - r"""Initialize module parameters with the values according to the method - described in `Delving deep into rectifiers: Surpassing human-level - performance on ImageNet classification - He, K. et al. (2015). - `_ - - Args: - a (int | float): the negative slope of the rectifier used after this - layer (only used with ``'leaky_relu'``). Defaults to 0. - mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing - ``'fan_in'`` preserves the magnitude of the variance of the weights - in the forward pass. Choosing ``'fan_out'`` preserves the - magnitudes in the backwards pass. Defaults to ``'fan_out'``. - nonlinearity (str): the non-linear function (`nn.functional` name), - recommended to use only with ``'relu'`` or ``'leaky_relu'`` . - Defaults to 'relu'. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` or - ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - a=0, - mode='fan_out', - nonlinearity='relu', - distribution='normal', - **kwargs): - super().__init__(**kwargs) - self.a = a - self.mode = mode - self.nonlinearity = nonlinearity - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ - f'nonlinearity={self.nonlinearity}, ' \ - f'distribution ={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Caffe2Xavier') -class Caffe2XavierInit(KaimingInit): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - def __init__(self, **kwargs): - super().__init__( - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='uniform', - **kwargs) - - def __call__(self, module): - super().__call__(module) - - -@INITIALIZERS.register_module(name='Pretrained') -class PretrainedInit(object): - """Initialize module by loading a pretrained model. - - Args: - checkpoint (str): the checkpoint file of the pretrained model should - be load. - prefix (str, optional): the prefix of a sub-module in the pretrained - model. it is for loading a part of the pretrained model to - initialize. For example, if we would like to only load the - backbone of a detector model, we can set ``prefix='backbone.'``. - Defaults to None. - map_location (str): map tensors into proper locations. - """ - - def __init__(self, checkpoint, prefix=None, map_location=None): - self.checkpoint = checkpoint - self.prefix = prefix - self.map_location = map_location - - def __call__(self, module): - from mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, - load_state_dict) - logger = get_logger('mmcv') - if self.prefix is None: - print_log(f'load model from: {self.checkpoint}', logger=logger) - load_checkpoint( - module, - self.checkpoint, - map_location=self.map_location, - strict=False, - logger=logger) - else: - print_log( - f'load {self.prefix} in model from: {self.checkpoint}', - logger=logger) - state_dict = _load_checkpoint_with_prefix( - self.prefix, self.checkpoint, map_location=self.map_location) - load_state_dict(module, state_dict, strict=False, logger=logger) - - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: load from {self.checkpoint}' - return info - - -def _initialize(module, cfg, wholemodule=False): - func = build_from_cfg(cfg, INITIALIZERS) - # wholemodule flag is for override mode, there is no layer key in override - # and initializer will give init values for the whole module with the name - # in override. - func.wholemodule = wholemodule - func(module) - - -def _initialize_override(module, override, cfg): - if not isinstance(override, (dict, list)): - raise TypeError(f'override must be a dict or a list of dict, \ - but got {type(override)}') - - override = [override] if isinstance(override, dict) else override - - for override_ in override: - - cp_override = copy.deepcopy(override_) - name = cp_override.pop('name', None) - if name is None: - raise ValueError('`override` must contain the key "name",' - f'but got {cp_override}') - # if override only has name key, it means use args in init_cfg - if not cp_override: - cp_override.update(cfg) - # if override has name key and other args except type key, it will - # raise error - elif 'type' not in cp_override.keys(): - raise ValueError( - f'`override` need "type" key, but got {cp_override}') - - if hasattr(module, name): - _initialize(getattr(module, name), cp_override, wholemodule=True) - else: - raise RuntimeError(f'module did not have attribute {name}, ' - f'but init_cfg is {cp_override}.') - - -def initialize(module, init_cfg): - r"""Initialize a module. - - Args: - module (``torch.nn.Module``): the module will be initialized. - init_cfg (dict | list[dict]): initialization configuration dict to - define initializer. OpenMMLab has implemented 6 initializers - including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, - ``Kaiming``, and ``Pretrained``. - - Example: - >>> module = nn.Linear(2, 3, bias=True) - >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) - >>> initialize(module, init_cfg) - - >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) - >>> # define key ``'layer'`` for initializing layer with different - >>> # configuration - >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), - dict(type='Constant', layer='Linear', val=2)] - >>> initialize(module, init_cfg) - - >>> # define key``'override'`` to initialize some specific part in - >>> # module - >>> class FooNet(nn.Module): - >>> def __init__(self): - >>> super().__init__() - >>> self.feat = nn.Conv2d(3, 16, 3) - >>> self.reg = nn.Conv2d(16, 10, 3) - >>> self.cls = nn.Conv2d(16, 5, 3) - >>> model = FooNet() - >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', - >>> override=dict(type='Constant', name='reg', val=3, bias=4)) - >>> initialize(model, init_cfg) - - >>> model = ResNet(depth=50) - >>> # Initialize weights with the pretrained model. - >>> init_cfg = dict(type='Pretrained', - checkpoint='torchvision://resnet50') - >>> initialize(model, init_cfg) - - >>> # Initialize weights of a sub-module with the specific part of - >>> # a pretrained model by using "prefix". - >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ - >>> 'retinanet_r50_fpn_1x_coco/'\ - >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' - >>> init_cfg = dict(type='Pretrained', - checkpoint=url, prefix='backbone.') - """ - if not isinstance(init_cfg, (dict, list)): - raise TypeError(f'init_cfg must be a dict or a list of dict, \ - but got {type(init_cfg)}') - - if isinstance(init_cfg, dict): - init_cfg = [init_cfg] - - for cfg in init_cfg: - # should deeply copy the original config because cfg may be used by - # other modules, e.g., one init_cfg shared by multiple bottleneck - # blocks, the expected cfg will be changed after pop and will change - # the initialization behavior of other modules - cp_cfg = copy.deepcopy(cfg) - override = cp_cfg.pop('override', None) - _initialize(module, cp_cfg) - - if override is not None: - cp_cfg.pop('layer', None) - _initialize_override(module, override, cp_cfg) - else: - # All attributes in module have same initialization. - pass - - -def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, - b: float) -> Tensor: - # Method based on - # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf - # Modified from - # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - def norm_cdf(x): - # Computes standard normal cumulative distribution function - return (1. + math.erf(x / math.sqrt(2.))) / 2. - - if (mean < a - 2 * std) or (mean > b + 2 * std): - warnings.warn( - 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' - 'The distribution of values may be incorrect.', - stacklevel=2) - - with torch.no_grad(): - # Values are generated by using a truncated uniform distribution and - # then using the inverse CDF for the normal distribution. - # Get upper and lower cdf values - lower = norm_cdf((a - mean) / std) - upper = norm_cdf((b - mean) / std) - - # Uniformly fill tensor with values from [lower, upper], then translate - # to [2lower-1, 2upper-1]. - tensor.uniform_(2 * lower - 1, 2 * upper - 1) - - # Use inverse cdf transform for normal distribution to get truncated - # standard normal - tensor.erfinv_() - - # Transform to proper mean, std - tensor.mul_(std * math.sqrt(2.)) - tensor.add_(mean) - - # Clamp to ensure it's in the proper range - tensor.clamp_(min=a, max=b) - return tensor - - -def trunc_normal_(tensor: Tensor, - mean: float = 0., - std: float = 1., - a: float = -2., - b: float = 2.) -> Tensor: - r"""Fills the input Tensor with values drawn from a truncated - normal distribution. The values are effectively drawn from the - normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` - with values outside :math:`[a, b]` redrawn until they are within - the bounds. The method used for generating the random values works - best when :math:`a \leq \text{mean} \leq b`. - - Modified from - https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - - Args: - tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. - mean (float): the mean of the normal distribution. - std (float): the standard deviation of the normal distribution. - a (float): the minimum cutoff value. - b (float): the maximum cutoff value. - """ - return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/__init__.py deleted file mode 100644 index 6ac55e63b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from . import ipu, mlu - -__all__ = ['mlu', 'ipu'] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/__init__.py deleted file mode 100755 index d550865ad..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import IPUFp16OptimizerHook - from .model_wrapper import ipu_model_wrapper - from .runner import IPUBaseRunner, IPUEpochBasedRunner, IPUIterBasedRunner - from .utils import cfg2options - __all__ = [ - 'cfg2options', 'ipu_model_wrapper', 'IPUFp16OptimizerHook', - 'IPUDataLoader', 'IPUBaseRunner', 'IPUEpochBasedRunner', - 'IPUIterBasedRunner' - ] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/dataloader.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/dataloader.py deleted file mode 100755 index 1485df2f3..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/dataloader.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence -from functools import partial - -import poptorch -from torch.utils.data.dataloader import default_collate - -from mmcv.parallel import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Put each data field into a tensor/DataContainer with outer dimension - batch size. - - TODO support for - :type:`~mmcv.parallel.DataContainer`. Currently, it will be ignored. - There are 3 cases. - - 1. cpu_only = True, e.g., meta data. - 2. cpu_only = False, stack = True, e.g., images tensors. - 3. cpu_only = False, stack = False, e.g., gt bboxes. - """ - - if not isinstance(batch, Sequence): - raise TypeError( - f'`batch` should be a sequence, but got {type(batch)}.') - - if isinstance(batch[0], DataContainer): - # TODO `DataContainer` will be supported in the future. - raise TypeError('DataContainer is not supported in ipu data loader.') - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - collated_batch = [] - for samples in transposed: - if not isinstance(samples[0], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch.append(collate(samples, samples_per_gpu)) - return collated_batch - elif isinstance(batch[0], Mapping): - collated_batch = {} - for key in batch[0]: - if not isinstance(batch[0][key], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch[key] = collate([d[key] for d in batch]) - return collated_batch - else: - return default_collate(batch) - - -class IPUDataLoader(poptorch.DataLoader): - """Thin wrapper of `torch.utils.data.DataLoader`. - - Compared with the pytorch DataLoder, this DataLoder changes the way of - calculation of batch size and adds the AsynchronousDataAccessor to - load and release data faster in cpu mode. - - If this data loader is used in a distributed execution environment, it will - ensure that each process uses a different subset of the dataset, providing - you first call ``options.randomSeed(N)`` with an integer N which is the - same across all hosts. - - Args: - dataset (torch.utils.data.Dataset): The dataset to get the data from. - options (poptorch.Options): Options that will be used to compile - and run the model. - batch_size (int, optional): This is the batch size in the conventional - sense of being the size that runs through an operation in the model - at any given time. - shuffle (bool, optional): set to ``True`` to have the data reshuffled - at every epoch (default: ``False``). - num_workers (int, optional): how many subprocesses to use for data - loading. ``0`` means that the data will be loaded in the main - process. (default: ``0``) - drop_last (bool, optional): If True and the number of elements in the - dataset is not a multiple of the combined batch size then the - incomplete batch at the end will be dropped. - persistent_workers (bool, optional): Re-use workers between - iterations if True. - auto_distributed_partitioning (bool, optional): If True, partitions the - dataset for distributed execution automatically. Otherwise, it is - assumed that partitioning has been handled manually. - mode (poptorch.DataLoaderMode, optional): If `DataLoaderMode.Async`, - uses an :py:class:`~poptorch.AsynchronousDataAccessor` to access - the dataset. If `DataLoaderMode.Sync`, accesses the dataset - synchronously. - async_options (Dict[str, Any], optional): Options to pass to - :py:class:`~poptorch.AsynchronousDataAccessor`. - rebatched_worker_size (int, optional): When using AsyncRebatched: batch - size of the tensors loaded by the workers. - Default to the combined batch size. - If specified the ``rebatched_worker_size`` must be less than - or equal to the combined batch size. - kwargs (Dict[str, Any], optional): Other options to pass to PyTorch's - ``DataLoader`` constructor. - """ - - def __init__(self, - dataset, - options, - batch_size=1, - shuffle=False, - num_workers=0, - drop_last=True, - persistent_workers=True, - auto_distributed_partitioning=True, - mode='sync', - async_options=None, - rebatched_worker_size=None, - **kwargs): - """Lazy init: - - In many frameworks, the dataloader will be constructed before the - initialization of the ipu options, so the lazy init method is used - here, and the real initialization will not be done until the dataloader - needs to be used and the options are input. - """ - # lazy init: sometimes, we cannot get IPU options when build data - # loader - self.kwargs = { - 'dataset': dataset, - 'batch_size': batch_size, - 'shuffle': shuffle, - 'num_workers': num_workers, - 'drop_last': drop_last, - 'persistent_workers': persistent_workers, - 'auto_distributed_partitioning': auto_distributed_partitioning, - 'mode': mode, - 'collate_fn': partial(collate, samples_per_gpu=batch_size), - 'async_options': async_options, - 'rebatched_worker_size': rebatched_worker_size, - **kwargs - } - self.dataset = dataset - self.initialized = False - if options: - self.init(options=options) - - def init(self, options, **kwargs): - if not self.initialized: - kwargs = {**self.kwargs, **kwargs, 'options': options} - if kwargs['mode'] == 'sync': - kwargs['mode'] = poptorch.DataLoaderMode.Sync - elif kwargs['mode'] == 'async': - kwargs['mode'] = poptorch.DataLoaderMode.AsyncRebatched - if kwargs['async_options'] is None: - kwargs['async_options'] = { - 'load_indefinitely': True, - 'buffer_size': 8 - } - if kwargs['rebatched_worker_size'] is None: - kwargs['rebatched_worker_size'] = 128 - super().__init__(**kwargs) - self.initialized = True - - return self diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py deleted file mode 100755 index a6f3b3cd2..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import numpy as np -import torch - -from mmcv.parallel import DataContainer - -# A customized None type for HierarchicalDataManager -HierarchicalDataNone = object() - - -class HierarchicalDataManager: - """A class manage all the tensors in the hierarchical data. - - At present, the input data structure accepted by IPU is limited, - when the input data structure of mmcv varies. - Here, an intermediate class is needed to get and update tensors - from the original data. - - HierarchicalDataManager will record a hierarchical input/output data in - self._hierarchical_data. For example, we have an input data: - {'img': tensorA, 'label': tensorB, 'img_metas': [tensorC, tensorD]} - To enable IPU to use the input, HierarchicalDataManager will collect - the torch tensors from self._hierarchical_data into a tuple like: - (tensorA, tensorB, tensorC, tensorD). - Meanwhile, the return of IPU is a tuple of tensors, HierarchicalDataManager - also have a function named update_all_tensors to update tensors in - self._hierarchical_data which is the output for upper calls. - - Args: - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - """ - - def __init__(self, logger=None): - self.atomic_types = (int, str, float, np.ndarray, type(None)) - self.warning = warnings.warn if logger is None else logger.warning - # enable or disable input data's shape and value check - self.quick_mode = False - self._hierarchical_data = None - - def quick(self): - self.quick_mode = True - - def compare_atomic_type(self, a, b): - """Compare data, supported datatypes are numpy array and python basic - types.""" - if isinstance(a, np.ndarray): - return np.all(a == b) - else: - return a == b - - def record_hierarchical_data(self, data): - """Record a hierarchical data.""" - if self._hierarchical_data is not None: - if isinstance(data, torch.Tensor): - assert isinstance(self._hierarchical_data, torch.Tensor), \ - 'original hierarchical data is not torch.tensor' - self._hierarchical_data = data - else: - self.update_hierarchical_data(data) - else: - self._hierarchical_data = data - - @property - def hierarchical_data(self): - return self._hierarchical_data - - def update_hierarchical_data(self, - dataA, - dataB=HierarchicalDataNone, - strict=True, - address='data'): - """Update dataB with dataA in-place. - - Args: - dataA (list or dict or tuple): New hierarchical data. - dataB (list or dict or tuple): hierarchical data to update. - if not specified, self.hierarchical_data will be updated then. - strict (bool, optional): If true, an error will be reported - when the following conditions occur: - 1. Non-torch.Tensor data changed. - 2. Torch.Tensor data shape changed. - address (str): Record the address of current data to be updated. - Default: 'data'. - """ - if dataB is HierarchicalDataNone: - dataB = self.hierarchical_data - - # Update with a da ta with the same structure - # but different values(tensors and basic python data types) - if isinstance(dataA, (tuple, list)): - for idx, node in enumerate(dataA): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(idx)}]' - assert isinstance(node, type(dataB[idx])),\ - f'data structure changed: {new_address}' - if isinstance(node, torch.Tensor): - dataB[idx] = node - else: - self.update_hierarchical_data( - node, dataB[idx], strict, address=new_address) - elif isinstance(dataA, dict): - for k, v in dataA.items(): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(k)}]' - assert isinstance(v, type(dataB[k])),\ - f'data structure changed: {new_address}' - if isinstance(v, torch.Tensor): - dataB[k] = v - else: - self.update_hierarchical_data( - v, dataB[k], strict, address=new_address) - elif isinstance(dataA, self.atomic_types): - if not self.quick_mode: - is_equal = self.compare_atomic_type(dataA, dataB) - if not is_equal: - if strict: - raise ValueError( - 'all data except torch.Tensor should be same, ' - f'but data({address}) is changed.') - else: - self.warning( - f'find a non-torch.Tensor data({type(dataA)}) ' - f'changed, and the address is {address}') - elif isinstance(dataA, DataContainer): - if not self.quick_mode: - assert isinstance(dataB, DataContainer) - new_address = address + '.data' - self.update_hierarchical_data( - dataA.data, dataB.data, False, address=new_address) - else: - raise NotImplementedError( - f'not supported datatype:{type(dataA)}, address is {address}') - - def collect_all_tensors(self, hierarchical_data=None): - """Collect torch.Tensor data from self.hierarchical_data to a list and - return.""" - # get a list of tensor from self._hierarchical_data - if hierarchical_data is None: - hierarchical_data = self._hierarchical_data - tensors = [] - if isinstance(hierarchical_data, torch.Tensor): - tensors = [hierarchical_data] - else: - self._collect_tensors(hierarchical_data, tensors) - return tensors - - def _collect_tensors(self, data, tensors): - if isinstance(data, (tuple, list)): - for node in data: - if isinstance(node, torch.Tensor): - tensors.append(node) - else: - self._collect_tensors(node, tensors) - elif isinstance(data, dict): - for v in data.values(): - if isinstance(v, torch.Tensor): - tensors.append(v) - else: - self._collect_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._collect_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def update_all_tensors(self, tensors): - """Put tensors from tuple back to self.hierarchical_data.""" - if isinstance(self._hierarchical_data, torch.Tensor): - print(tensors, len(tensors)) - assert len(tensors) == 1 - assert isinstance(tensors[0], torch.Tensor) - self._hierarchical_data = tensors[0] - else: - # convert to list if tensors is tuple - tensors = list(tensors) - self._set_tensors(self._hierarchical_data, tensors) - return self.hierarchical_data - - def _set_tensors(self, data, tensors): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = tensors.pop(0) - else: - self._set_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._set_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def clean_all_tensors(self): - """Delete tensors from self.hierarchical_data.""" - self._clean_tensors(self._hierarchical_data) - - def _clean_tensors(self, data): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = None - else: - self._clean_tensors(v) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._clean_tensors(data.data) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/hook_wrapper.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/hook_wrapper.py deleted file mode 100755 index 141afb86d..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/hook_wrapper.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import HOOKS, LrUpdaterHook, OptimizerHook -from mmcv.utils import TORCH_VERSION, digit_version - - -def wrap_lr_updater_hook(lr_hook_class): - """A wrapper function to wrap any subclass of LrUpdaterHook. - - IPU needs extra operations to upload optimizer settings. This wrapper will - override function(_set_lr) of a subclass of LrUpdaterHook. - """ - assert issubclass(lr_hook_class, LrUpdaterHook) - - class ipu_lr_hook_class(lr_hook_class): - - def _set_lr(self, runner, *args, **kwargs): - super()._set_lr(runner, *args, **kwargs) - # convert torch optimizer to poptorch optimizer - runner.model.setOptimizer(runner.optimizer) - - return ipu_lr_hook_class - - -def wrap_optimizer_hook(optimizer_hook_class): - """A wrapper function to wrap OptimizerHook. - - This is an non-intrusive implementation of wrapping optimizer hook (or you - need to change every config file to use IPU optimizer hook) IPU's clip-norm - implementation is different from pytorch, so there should be an error - raised when using clip-norm. - """ - - class ipu_optimizer_hook_class(OptimizerHook): - - def __init__(self, **kwargs): - super().__init__(**kwargs) - if self.grad_clip is not None: - raise NotImplementedError('IPU does not support gradient clip') - - return ipu_optimizer_hook_class - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class IPUFp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - assert grad_clip is None,\ - 'IPU mode does not support `grad_clip` currently' - assert coalesce,\ - 'implemented all reduce in distributed training currently' - assert bucket_size_mb == -1,\ - '`bucket_size_mb` should not be set in IPU mode' - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - raise NotImplementedError( - 'IPU mode does not support dynamic loss scale currently') - elif isinstance(loss_scale, float): - self.loss_scale = loss_scale - elif isinstance(loss_scale, dict): - raise NotImplementedError( - 'IPU mode supports single scale currently') - else: - raise ValueError( - f'loss_scale should be float, but got {loss_scale} ') - - def after_train_iter(self, runner): - pass - -else: - raise RuntimeError('The IPU mode only supports torch 1.6 and above') diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/model_wrapper.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/model_wrapper.py deleted file mode 100755 index c345537e2..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/model_wrapper.py +++ /dev/null @@ -1,721 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect -from collections import OrderedDict -from typing import Optional, Union - -import poptorch -import torch -import torch.nn as nn -from poptorch import PoplarExecutor, __version__, identity_loss -from poptorch._args_parser import ArgsParser - -from mmcv.runner import auto_fp16 -from .hierarchical_data_manager import HierarchicalDataManager -from .utils import compare_ndarray, model_sharding, recomputation_checkpoint - - -class DictArgsParser(ArgsParser): - """A helper class for handling model input. - - Args: - inputs (list): Inputs of model. - """ - - def __init__(self, inputs): - # Combine args and kwargs: - self._has_variadic_arguments = True - self._varnames = list(inputs.keys()) - self._defaults = [inspect.Parameter.empty for _ in self._varnames] - self._warned_not_contiguous_input = False - - -class WrappedNet(nn.Module): - """A net wrapper for model conversion. - - This wrapper will make some changes and add some extra functions to - training/inference model. - - Args: - model (:obj:`nn.Module`): The model to run. - inputs_manager (:obj:`HierarchicalDataManager`): A parser - converting inputs from tuple to dictionary. - outputs_manager (:obj:`HierarchicalDataManager`): A parser - converting outputs from dictionary to tuple. - inter_outputs_in_cpu (dict): Specify the features to be - recorded. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - model, - inputs_manager, - outputs_manager, - inter_outputs_in_cpu, - modules_to_record=None): - super().__init__() - self.model = model - self.inputs_manager = inputs_manager - self.outputs_manager = outputs_manager - self.training = model.training - # Register a hook function to capture the intermediate features - # generated by the network to align the outputs between ipu and cpu - # Used to confirm whether the implementation of CPU is consistent - # with the implementation of IPU - self.inter_outputs_in_cpu = inter_outputs_in_cpu - if modules_to_record is None: - modules_to_record = [] - - for idx, (name, module) in enumerate(model.named_modules()): - if name in modules_to_record or idx in modules_to_record: - features_hook = self.get_input_output_hook( - name, idx, self.inter_outputs_in_cpu) - module.register_forward_hook(hook=features_hook) - - def get_input_output_hook(self, name, idx, save_dict): - - def input_output_hook(module, fea_in, fea_out): - if isinstance(fea_in, tuple): - fea_in = list(fea_in) - if isinstance(fea_out, tuple): - fea_out = list(fea_out) - save_dict[name] = { - 'fea_in': fea_in, - 'fea_out': fea_out, - 'idx': idx - } - return None - - return input_output_hook - - def forward(self, inputs_tuple): - """This function is used to be compiled to ipu, the inputs and outputs - need to be tuples, so here we need to restore the input back to a - dictionary and convert the output to a tuple.""" - self.inputs_manager.update_all_tensors(inputs_tuple) - kwargs = {**(self.inputs_manager.hierarchical_data)} - if self.training: - outputs = self.forward_train(kwargs) - # tell poptorch which loss will be used finally - identity_loss(outputs['loss'], reduction='none') - else: - outputs = self.forward_eval(kwargs) - - if isinstance(outputs, torch.Tensor): - # currently not support single tensor output, - # need to wrap it with a dictionary, - # use a keyword to identify this case - outputs = {'output of WrappedNet: single tensor': outputs} - - # if there are some features need to be record, add extra outputs - for name in self.inter_outputs_in_cpu: - outputs[name] = self.inter_outputs_in_cpu[name] - - # record all the places of return tensors in the converting stage - # while in the real run stage, all the tensor are changed in-place - # that means the output can be obtained directly outside this function - self.outputs_manager.record_hierarchical_data(outputs) - plain_outputs = self.outputs_manager.collect_all_tensors() - return plain_outputs - - def forward_train(self, kwargs): - optimizer = kwargs.pop('optimizer') - outputs = self.train_step(kwargs, optimizer) - return outputs - - def train_step(self, data, optimizer=None, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating are also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer`, optional): The - optimizer of runner is passed to ``train_step()``. This - argument is unused and reserved. - - Returns: - dict: Dict of outputs. The following fields are contained. - - loss (torch.Tensor): A tensor for back propagation, which \ - can be a weighted sum of multiple losses. - - log_vars (dict): Dict contains all the variables to be sent \ - to the logger. - - num_samples (int): Indicates the batch size (when the model \ - is DDP, it means the batch size on each GPU), which is \ - used for averaging the logs. - """ - losses = self.model(**data) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, log_vars=log_vars, num_samples=len(data['img'].data)) - - return outputs - - def _parse_losses(self, losses): - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(loss.mean() for loss in loss_value) - elif isinstance(loss_value, dict): - for name, value in loss_value.items(): - log_vars[name] = value - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(value for key, value in log_vars.items() if 'loss' in key) - log_vars['loss'] = loss - - return loss, log_vars - - def forward_eval(self, kwargs): - img = kwargs.pop('img') - img_metas = kwargs.pop('img_metas', None) - return_loss = kwargs.pop('return_loss') - assert not return_loss - # TODO Temporarily hard-code to close post_process, - # otherwise, in the third trace(_check_trace), - # post_process will convert output tensor to numpy array automatically, - # resulting in _check_trace failure - outputs = self.model( - img, - img_metas=img_metas, - return_loss=return_loss, - post_process=False) - return outputs - - -class MMPoplarExecutor(PoplarExecutor): - """An executor for inputs/outputs parsing, model compilation, data - alignment and IPU upload/download. - - Args: - model (:obj:`nn.Module`): The model to be compiled. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - training (bool): Model in training mode or eval mode. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - args (argument list): Arguments passed to the `__init__` - method of PoplarExecutor. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of PoplarExecutor. - """ - - def __init__(self, - model, - logger=None, - training=True, - modules_to_record=None, - *args, - **kwargs): - # self.model == self._user_model: input pytorch model - # self._model: wrapped model which is used to compile - # and update weights, these two models use same weights - # wrapped model only accept and output tuple, so - # HierarchicalDataManager will convert dictionary - # to tuple and convert them back - self.inputs_manager = HierarchicalDataManager(logger=logger) - self.outputs_manager = HierarchicalDataManager(logger=logger) - self.logger = logger - # the features calculated by CPU - self.inter_outputs_in_cpu = {} - # the features calculated by IPU - self.inter_outputs_in_ipu = {} - if modules_to_record is None: - # It is possible that the IPU implementation of some operators - # is inconsistent with the expected (CPU), here you can use - # this method to confirm whether there is a problem - self.compare_with_cpu = False - else: - self.compare_with_cpu = True - # move model.fp16_enabled to self.fp16_enabled, - # modify the position where the input is automatically casted to half - if getattr(model, 'fp16_enabled', False): - model.fp16_enabled = False - self.fp16_enabled = True - # make torch.jit.trace convert self._model - model = WrappedNet( - model, - self.inputs_manager, - self.outputs_manager, - self.inter_outputs_in_cpu, - modules_to_record=modules_to_record) - super().__init__(model, training=training, *args, **kwargs) - # overwrite self._args_parser in train_step or val_step - self._args_parser = None - if training: - assert self.training - else: - assert not self.training - - @property - def training(self): - # If trying to get the attribute(training) of self, - # since the class has no training attribute, - # it will automatically look for the training attribute of self.model. - # However, the real attribute we want to check is self._training, - # self.model.training and self._training are often inconsistent. - # It is not clear whether it is a Poptorch bug or a special design, - # temporarily use this function to fix the problem - return self._training # comes from self.model._training - - @auto_fp16(supported_types=(PoplarExecutor, )) - def run_model(self, data_dict): - # this function is used to parse input_dict - # and convert to output_dict - if self.isCompiled(): - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - else: - # get tensors out of data and put them in a tuple - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - # turn logger in data manager off after compilation - self.inputs_manager.quick() - self.outputs_manager.quick() - - # parser args in the first iter - if self._args_parser is None: - self._args_parser = DictArgsParser({'args': inputs_tuple}) - - # run or convert model - # the plain_outputs will be used in converting stage - plain_outputs = self(inputs_tuple) - - self.inputs_manager.clean_all_tensors() - - # put list of tensors back to the output dict - # according to the same order - self.outputs_manager.update_all_tensors(plain_outputs) - # get the real output dictionary from self.outputs_manager - output_dict = self.outputs_manager.hierarchical_data - - # split output_dict into inter_outputs_in_ipu - # and output of the torch model - torch_model_output = {} - for name in output_dict: - if name in self.inter_outputs_in_cpu: - self.inter_outputs_in_ipu[name] = output_dict[name] - else: - torch_model_output[name] = output_dict[name] - - if 'output of WrappedNet: single tensor' in output_dict: - assert len(torch_model_output) == 1 - assert isinstance( - torch_model_output['output of WrappedNet: single tensor'], - torch.Tensor) - torch_model_output = \ - torch_model_output['output of WrappedNet: single tensor'] - - return torch_model_output - - def train_step(self, data, optimizer=None, **kwargs): - # arguments from mmcls/models/classifiers/base.py: - # BaseClassifier.train_step - assert self.training - assert len(kwargs) == 0 # TODO, support later if necessary - - # TODO support datacontainer as input - # currently, auto_fp16 and HierarchicalDataManager take too much - # time on traversing datacontainer - data['img_metas'] = None - num_samples = len(data['img'].data) - - # TODO we will ignore optimizer because it will not be used in model, - # support later if necessary - data['optimizer'] = None - output_dict = self.run_model(data) - - # outputs contained loss, log_vars, num_samples, - # only loss(torch.tensor) has been updated - # remove all unchanged vars, left torch.tensor - neat_output_dict = {'loss': output_dict['loss']} - - # re-parse outputs, get back log_vars and num_samples - loss, log_vars = self.model._parse_losses(neat_output_dict) - final_output_dict = dict( - loss=loss, log_vars=log_vars, num_samples=num_samples) - return final_output_dict - - def eval_call(self, img, img_metas=None, return_loss=True, **kwargs): - # arguments from mmdet/models/detectors/base.py:BaseDetector.forward - # tmp usssage for eval mode - assert not self.training - assert len(kwargs) == 0 # TODO, support later if necessary - assert not return_loss - data = {'img': img, 'img_metas': img_metas, 'return_loss': return_loss} - - output_dict = self.run_model(data) - - return output_dict - - def detachFromDevice(self): - if self.isCompiled() and self._is_attached: - super().detachFromDevice() - - def attachToDevice(self): - if self.isCompiled() and not self._is_attached: - super().attachToDevice() - - -class TrainEvalModel: - """A class maintaining training MMPoplarExecutor and inference - MMPoplarExecutor. - - Args: - train_model (:obj:`nn.Module`): The training model to be compiled. - ``train_model`` can be None if only executing validation. - eval_model (:obj:`nn.Module`): The inference model to be compiled. - options (mmcv.Config, dict): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - train_model, - eval_model, - options, - optimizer, - modules_to_record=None, - logger=None): - if train_model is None: - self._train_executor = None - self.training = False - else: - self._train_executor = get_training_model( - train_model, - options=options['training'], - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - self.training = True - self._eval_executor = get_inference_model( - eval_model, options=options['inference'], logger=logger) - - @property - def executor(self): - if self.training: - return self._train_executor - else: - return self._eval_executor - - def train(self, mode: bool = True): - """Sets the module in training mode. - - This has any effect only on certain modules. See documentations of - particular modules for details of their behaviors in - training/evaluation mode, if they are affected, - e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - Args: - mode (bool): whether to set training mode (``True``) or evaluation - mode (``False``). Default: ``True``. - - Returns: - Module: self - """ - if not isinstance(mode, bool): - raise ValueError('training mode is expected to be boolean, ' - f'but got {type(mode)}') - if self._train_executor is None and mode: - raise RuntimeError( - 'The train_executor is not initialized.' - 'If you want to initialize train_executor,' - 'you need to input optimizer when converting pytorch model') - - if mode == self.training: - self.model.train(mode) - return self - else: - if self.isCompiled(): - # copy weights from IPU to cpu before off-load current session - self.copyWeightsToHost() - # detach the current session before change the mode, - # if is training mode and weights are updated, - # poptorch will copy weights from IPU to host - self.detachFromDevice() - - self.training = mode # session will changed with mode changing - self.model.train(mode) - - # after changing mode, attach the current new session, - # and this function will copy weights of model to device - self.attachToDevice() - return self - - def eval(self): - """Sets the module in evaluation mode. - - This has any effect only on certain modules. - See documentations of particular modules - for details of their behaviors in training/evaluation mode, - if they are affected, e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - This is equivalent with :meth:`self.train(False) - `. - - See :ref:`locally-disable-grad-doc` for a comparison between - `.eval()` and several similar mechanisms that may be confused with it. - - Returns: - Module: self - """ - return self.train(False) - - def compare_data_between_ipu_and_cpu(self, inter_outputs_in_cpu, - inter_outputs_in_ipu): - for key, val in inter_outputs_in_cpu.items(): - is_tensor = isinstance(val['fea_in'], torch.Tensor) - fea_in_cpu = val['fea_in'] - fea_in_cpu_list = [fea_in_cpu] if is_tensor else fea_in_cpu - fea_in_ipu = inter_outputs_in_ipu[key]['fea_in'] - fea_in_ipu_list = [fea_in_ipu] if is_tensor else fea_in_ipu - - is_tensor = isinstance(val['fea_out'], torch.Tensor) - fea_out_cpu = val['fea_out'] - fea_out_cpu_list = [fea_out_cpu] if is_tensor else fea_out_cpu - fea_out_ipu = inter_outputs_in_ipu[key]['fea_out'] - fea_out_ipu_list = [fea_out_ipu] if is_tensor else fea_out_ipu - - print('comparing layer:', key) - for idx, (featA, featB) in \ - enumerate(zip(fea_in_cpu_list, fea_in_ipu_list)): - print('fea_in, tensor ', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - for idx, (featA, featB) in \ - enumerate(zip(fea_out_cpu_list, fea_out_ipu_list)): - print('fea_out, tensor', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def train_step(self, data, optimizer=None, **kwargs): - assert self.training, 'not supported train_step on eval mode' - inter_outputs_in_cpu = {} - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu): - self.copyWeightsToHost() - # run in CPU mode - self._train_executor.model.train_step(data, optimizer, **kwargs) - inter_outputs_in_cpu = { - **(self._train_executor.inter_outputs_in_cpu) - } - # run in IPU mode - result = self._train_executor.train_step(data, optimizer, **kwargs) - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu - and len(inter_outputs_in_cpu) > 0): - self.compare_data_between_ipu_and_cpu( - inter_outputs_in_cpu, - self._train_executor.inter_outputs_in_ipu) - return result - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def __call__(self, *args, **kwargs): - if self.training: - raise NotImplementedError('use train_step rather than __call__') - else: - return self._eval_executor.eval_call(*args, **kwargs) - - def __getattr__(self, attr): - return getattr(self.executor, attr) - - -def get_training_model(model: nn.Module, - options: Optional[poptorch.Options] = None, - optimizer: Optional[torch.optim.Optimizer] = None, - logger=None, - modules_to_record=None) -> poptorch.PoplarExecutor: - """Create a PopTorch training model from a PyTorch model, running on IPU - hardware in training mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned training model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.train()`` on the original model, which - changes the ``training`` bool of the model instance, will not alter the - model returned by this function. You may need to call ``model.train()`` - on your model before you call this function for correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): The optimizers - to apply during training. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place - of ``model``. - """ - # Create a copy of the original model in case it needs to be wrapped - maybe_wrapped_model = copy.copy(model) - - return MMPoplarExecutor( - model=maybe_wrapped_model, - logger=logger, - options=options, - training=True, - optimizer=optimizer, - user_model=model, - modules_to_record=modules_to_record, - poptorch_version=__version__) - - -def get_inference_model(model: Union[nn.Module, poptorch.PoplarExecutor], - options: Optional[poptorch.Options] = None, - logger=None) -> poptorch.PoplarExecutor: - """Create a PopTorch inference model from a PyTorch model, running on IPU - hardware in inference mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned inference model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.eval()`` on the original model will not alter - the model returned by this function. You may need to call - ``model.eval()`` on your model before you call this function for - correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place of - ``model``. - """ - - return MMPoplarExecutor( - model=copy.copy(model), - logger=logger, - options=options, - training=False, - poptorch_version=__version__) - - -def ipu_model_wrapper(model, - options, - optimizer=None, - logger=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None): - """Convert torch model to IPU model. - - Args: - model (nn.Module): The target model to be converted. - options (dict[str, poptorch.Options]): IPU options, generated - by :func:`cfg2options`. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during training. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (dict): A dictionary contains train_split_edges and - train_ckpt_nodes, See details in :func:`model_sharding` and - :func:`recomputation_checkpoint` functions. - fp16_cfg (dict): Config for IPU fp16 training. Currently supports - configs: `loss_scale`, `velocity_accum_type` and `accum_type`. - See details in - https://docs.graphcore.ai/projects/poptorch-user-guide/en/latest/index.html - - Returns: - TrainEvalModel: IPU wrapped model. - """ - if ipu_model_cfg is None: - ipu_model_cfg = {} - training = model.training if optimizer is not None else False - # set mixed-precision - if fp16_cfg is not None: - from mmcv.runner import wrap_fp16_model - loss_scale = fp16_cfg['loss_scale'] - wrap_fp16_model(model) - model.half() - # TODO tmp ussage to set loss scaling for torch original optimizer - if optimizer is not None: - optimizer.loss_scaling = loss_scale - if fp16_cfg.get('velocity_accum_type', False): - if fp16_cfg['velocity_accum_type'] == 'half': - optimizer.velocity_accum_type = torch.half - else: - optimizer.velocity_accum_type = torch.float32 - if fp16_cfg.get('accum_type', False): - if fp16_cfg['accum_type'] == 'half': - optimizer.accum_type = torch.half - else: - optimizer.accum_type = torch.float32 - # TODO support feature alignment for fp16 - if modules_to_record is not None: - raise NotImplementedError( - 'Feature alignment for fp16 is not implemented') - - # set model partition - if optimizer is None: - train_model = None - else: - # split model into multi-IPUs if specified - train_model = model_sharding( - copy.copy(model).train(), - ipu_model_cfg.get('train_split_edges', [])) - - recomputation_checkpoint(train_model, - ipu_model_cfg.get('train_ckpt_nodes', [])) - - # TODO support feature alignment for gradient accumulation mode - gradient_accumulation = \ - getattr(options['training'].Training, 'gradient_accumulation', 1) - if gradient_accumulation > 1: - assert modules_to_record is None, \ - 'Feature alignment for grad-accumulation mode not implemented' - - # TODO support feature alignment for multi-replica mode - replication_factor = \ - getattr(options['training'], 'replication_factor', 1) - if replication_factor > 1: - assert modules_to_record is None, \ - 'Feature alignment for multi-replica mode not implemented' - - # TODO supports different model partitions between train and eval mode - assert len(ipu_model_cfg.get('eval_split_edges', [])) == 0,\ - 'Currently, BeginBlock can only be used once on the same model' - eval_model = copy.copy(model).eval() - - # wrap model for compilation - model = TrainEvalModel( - train_model, - eval_model, - options=options, - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - model.train(training) - return model diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/runner.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/runner.py deleted file mode 100755 index e2d492267..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/runner.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.runner import (HOOKS, RUNNERS, BaseRunner, EpochBasedRunner, - IterBasedRunner) -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import (IPUFp16OptimizerHook, wrap_lr_updater_hook, - wrap_optimizer_hook) - from .model_wrapper import ipu_model_wrapper - from .utils import build_from_cfg_with_wrapper, cfg2options - - -class IPUBaseRunner(BaseRunner): - """A base runner for IPU. - - This runner has some extra processes for IPU which are shown below: - - 1. Parse options for IPU - 2. wrap pytorch model for IPU - 3. Raise errors while encountering illegal usage - 4. Input IPU options and initialize dataloader if finding an instance - of IPUDataLoader - - Args: - model (:obj:`nn.Module`): The model to run. - options_cfg (mmcv.Config, dict): Options that will be used to compile - and run the model. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (mmcv.Config, dict): Config of model partition and - recomputing checkpoint - fp16_cfg (mmcv.Config): Config for fp16 training. - batch_processor (callable): A callable method that process a data - batch. Should be None for IPU runner - kwargs (Dict[str, Any], optional): Keyword arguments will be passed to - ``base_runner.BaseRunner``. - """ - - def __init__(self, - model, - options_cfg=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None, - batch_processor=None, - **kwargs): - assert hasattr(model, 'train_step') and batch_processor is None,\ - 'only support model with train_step' - - if options_cfg is None: - options_cfg = {} - # call BaseRunner.__init__() here - super().__init__(model, **kwargs) - - # process options of ipu - if IS_IPU_AVAILABLE: - self.options = cfg2options(options_cfg) - self.model = ipu_model_wrapper( - self.model, - self.options, - self.optimizer, - self.logger, - modules_to_record=modules_to_record, - ipu_model_cfg=ipu_model_cfg, - fp16_cfg=fp16_cfg) - else: - raise NotImplementedError('cpu mode on IPURunner is not supported') - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - assert isinstance(lr_config, dict) - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, - # e.g., 'cyclic', then its first letter will be capitalized, - # e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, the string will not be changed - # if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = build_from_cfg_with_wrapper(lr_config, HOOKS, - wrap_lr_updater_hook) - self.register_hook(hook, priority='VERY_HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - assert isinstance(optimizer_config, (dict, IPUFp16OptimizerHook)) - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = build_from_cfg_with_wrapper(optimizer_config, HOOKS, - wrap_optimizer_hook) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def run(self, data_loaders, workflow, *args, **kwargs): - for i, flow in enumerate(workflow): - mode, _ = flow - # initialize IPU dataloader if not initialized - assert isinstance(data_loaders[i], IPUDataLoader),\ - 'IPU runner can only work with `IPUDataLoader`' - data_loaders[i].init(options=self.get_options(mode)) - - super().run(data_loaders, workflow, *args, **kwargs) - - def get_options(self, mode): - if mode == 'train': - return self.options['training'] - elif mode == 'val': - return self.options['inference'] - else: - raise ValueError(f'mode should be train or val but got {mode}') - - -@RUNNERS.register_module() -class IPUEpochBasedRunner(IPUBaseRunner, EpochBasedRunner): - """Epoch-based Runner for IPU. - - The Inheritance order(MRO) is: IPUEpochBasedRunner -> IPUBaseRunner -> - EpochBasedRunner -> BaseRunner This runner train models epoch by epoch. - """ - pass - - -@RUNNERS.register_module() -class IPUIterBasedRunner(IPUBaseRunner, IterBasedRunner): - """Iteration-based Runner for IPU. - - The Inheritance order(MRO) is: IPUIterBasedRunner -> IPUBaseRunner -> - IterBasedRunner -> BaseRunner This runner train models iteration by - iteration. - """ - pass diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/utils.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/utils.py deleted file mode 100755 index 79709db1e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/ipu/utils.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import numpy as np -import popart -import poptorch -import torch -import torch.nn as nn - -from mmcv.utils import Registry - - -def _options_assigner(cfg, options_node): - # set popart.options by config - # cfg: dict, python data type - # options_node: python module or function - if isinstance(cfg, dict): - for key in cfg: - _options_assigner(cfg[key], getattr(options_node, key)) - elif isinstance(cfg, (int, float, str, list)): - if callable(options_node): - options_node(cfg) - else: - error_msg = f'options_node type {type(options_node)} not supported' - raise NotImplementedError(error_msg) - else: - error_msg = f'cfg type {type(cfg)} not supported' - raise NotImplementedError(error_msg) - - -def cfg2options(cfg): - """Parse dictionary to ipu options. - - Args: - cfg (dict): A dictionary of ipu settings. - - Returns: - dict[str, poptorch.Options]: Training options and inference options - of IPU. - """ - # set ipu options for inference and training by config - train_cfg = cfg.pop('train_cfg', {}) - eval_cfg = cfg.pop('eval_cfg', {}) - eval_cfg['replicationFactor'] = 1 # eval mode only use one replica - eval_cfg['executionStrategy'] = 'ShardedExecution' - # overwrite default ipu cfg with specified train cfgs - training_ipu_cfg = {**cfg, **train_cfg} - # overwrite default ipu cfg with specified eval cfgs - inference_ipu_cfg = {**cfg, **eval_cfg} - - ipu_options = { - 'training': _cast_to_options(training_ipu_cfg), - 'inference': _cast_to_options(inference_ipu_cfg) - } - - # TODO configure these codes - ipu_options['training']._Popart.set('disableGradAccumulationTensorStreams', - True) - ipu_options['training']._Popart.set( - 'accumulateOuterFragmentSettings.schedule', - int(popart.AccumulateOuterFragmentSchedule.OverlapMemoryOptimized)) - ipu_options['training'].Precision.enableStochasticRounding(True) - - return ipu_options - - -def _cast_to_options(cfg): - # If it cannot be directly assigned, use if statement to parse it, - # and if it can be directly assigned, use _options_assigner to assign - options = poptorch.Options() - - if 'availableMemoryProportion' in cfg: - available_memory_proportion = cfg.pop('availableMemoryProportion') - mem_props = {} - for i, mem_prop in enumerate(available_memory_proportion): - mem_props[f'IPU{i}'] = mem_prop - options.setAvailableMemoryProportion(mem_props) - - if 'executionStrategy' in cfg: - execution_strategy = cfg.pop('executionStrategy') - if execution_strategy == 'SameAsIpu': - options.setExecutionStrategy( - poptorch.PipelinedExecution( - getattr(poptorch.AutoStage, execution_strategy))) - elif execution_strategy == 'ShardedExecution': - options.setExecutionStrategy(poptorch.ShardedExecution()) - else: - raise NotImplementedError( - 'executionStrategy should be "SameAsIpu" or "ShardedExecution"' - f', but got {execution_strategy}') - - if 'partialsType' in cfg: - partials_type = cfg.pop('partialsType') - options.Precision.setPartialsType(getattr( - torch, partials_type)) # half or float - - _options_assigner(cfg, options) - return options - - -def model_sharding(model, split_edges): - """split models in-place into multi-IPUs. - - Args: - model (nn.Module): The target model to be split. - split_edges (list of dict): Model layer names or layer numbers - of split edge. Each item of ``split_edges`` is a dictionary, - which may contain the following key-pairs: - - - layer_to_call: PyTorch module to assign to the block - - user_id (optional): A user defined identifier for the block. - - ipu_id: The id of the IPU to run on. - - Examples: - >>> split_edges = [ - ... dict(layer_to_call='model.conv1', ipu_id=0), - ... dict(layer_to_call='model.conv3', ipu_id=1)] - >>> sharding_model = model_sharding(torch_model, split_edges) - - Returns: - nn.Module: Split model. - """ - if len(split_edges) == 0: - return model - assert isinstance(split_edges, list) - spilt_edges_dict = {edge['layer_to_call']: edge for edge in split_edges} - - for idx, (name, module) in enumerate(model.named_modules()): - if idx in spilt_edges_dict and name in spilt_edges_dict: - raise ValueError( - 'The same layer is referenced twice while doing model' - f' partition: idx is {idx} and name is {name}') - - edge = spilt_edges_dict.pop(name, None) - edge = spilt_edges_dict.pop(idx, edge) - if edge is not None: - poptorch.BeginBlock(module, edge.get('user_id', name), - edge['ipu_id']) - - # ensure all split_edges are used - if len(spilt_edges_dict) > 0: - split_edge_names = list(spilt_edges_dict.keys()) - raise RuntimeError( - f'split_edges: {split_edge_names} are not contained in the model') - return model - - -def recomputation_checkpoint(model: nn.Module, module_names: list): - """Annotates the output of a module to be checkpointed instead of - recomputed. - - If recomputation mode is enabled, ipu will release the activations of - the middle layers to save memory. During the backward of gradient, - the activation of the middle layer will be recalculated again. - This function is used to declare the activations of some intermediate - layers that need to be saved in order to skip the recomputation of - some layers. - - Args: - model (nn.Module): The target model to apply recomputation - checkpoint. - module_names (list): Layer names of module. - """ - - def recompute_outputs(module, inputs, outputs): - if isinstance(outputs, tuple): - return tuple(poptorch.recomputationCheckpoint(y) for y in outputs) - else: - return poptorch.recomputationCheckpoint(outputs) - - for name, module in model.named_modules(): - if name in module_names: - module.register_forward_hook(recompute_outputs) - module_names.remove(name) - - # check all module_names are used - assert len(module_names) == 0,\ - f'recomputed nodes: {module_names} are not contained in the model' - - -def compare_ndarray(featA, featB, rtol=1e-3, atol=1e-5): - """Align data between two activations or weights.""" - try: - np.testing.assert_allclose(featA, featB, rtol=rtol, atol=atol) - except AssertionError as e: - print(e) - - -def build_from_cfg_with_wrapper(cfg, - registry, - wrapper_func=None, - default_args=None): - """Build a module from config dict and wrap module with "wrapper_func". - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - wrapper_func (function): Used to wrap class - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - - if wrapper_func is None: - wrapped_obj_cls = obj_cls - else: - wrapped_obj_cls = wrapper_func(obj_cls) - try: - return wrapped_obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{wrapped_obj_cls.__name__}: {e}') diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/__init__.py deleted file mode 100644 index 572c4da7e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .data_parallel import MLUDataParallel -from .distributed import MLUDistributedDataParallel -from .scatter_gather import scatter, scatter_kwargs - -__all__ = [ - 'MLUDataParallel', 'MLUDistributedDataParallel', 'scatter', - 'scatter_kwargs' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/_functions.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/_functions.py deleted file mode 100644 index 7c35e65a2..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/_functions.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def scatter(input, devices): - """scatter copies tensor to MLU directly.""" - if isinstance(input, list): - outputs = [scatter(_input, devices) for _input in input] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - return output.to('mlu') if devices != [-1] else output - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_mlus, input): - outputs = scatter(input, target_mlus) - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/data_parallel.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/data_parallel.py deleted file mode 100644 index b2d09d0b0..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/data_parallel.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -import torch - -from mmcv.parallel import MMDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDataParallel(MMDataParallel): - """The MLUDataParallel module that supports DataContainer. - - MLUDataParallel is a class inherited from MMDataParall, which supports - MLU training and inference only. - - The main differences with MMDataParallel: - - - It only supports single-card of MLU, and only use first card to - run training and inference. - - - It uses direct host-to-device copy instead of stream-background - scatter. - - .. warning:: - MLUDataParallel only supports single MLU training, if you need to - train with multiple MLUs, please use MLUDistributedDataParallel - instead. If you have multiple MLUs, you can set the environment - variable ``MLU_VISIBLE_DEVICES=0`` (or any other card number(s)) - to specify the running device. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MLUDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.device_ids = [0] - self.src_device_obj = torch.device('mlu:0') - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/distributed.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/distributed.py deleted file mode 100644 index 3768c754c..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/distributed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.parallel import MMDistributedDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDistributedDataParallel(MMDistributedDataParallel): - """The DDP module supports DataContainer. - - MLUDDP has one difference from MMDDP which moves data to MLU with coping - instead of scattering. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/scatter_gather.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/scatter_gather.py deleted file mode 100644 index 0b0c9b96f..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/device/mlu/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmcv.parallel.data_container import DataContainer -from ._functions import Scatter - - -def scatter(inputs, target_mlus, dim=0): - """Scatter inputs to target mlu. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_mlus != [-1]: - obj = obj.to('mlu') - return [obj] - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_mlus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_mlus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_mlus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_mlus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_mlus, dim) if inputs else [] - kwargs = scatter(kwargs, target_mlus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/engine/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/engine/__init__.py deleted file mode 100644 index 3193b7f66..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, - single_gpu_test) - -__all__ = [ - 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', - 'single_gpu_test' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/engine/test.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/engine/test.py deleted file mode 100644 index f236b1cda..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/engine/test.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, data_loader): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - # Assume result has the same length of batch_size - # refer to https://github.com/open-mmlab/mmcv/issues/985 - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting - ``gpu_collect=True``, it encodes results to gpu tensors and use gpu - communication for results collection. On cpu mode it saves the results on - different gpus to ``tmpdir`` and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - if rank == 0: - batch_size = len(result) - batch_size_all = batch_size * world_size - if batch_size_all + prog_bar.completed > len(dataset): - batch_size_all = len(dataset) - prog_bar.completed - for _ in range(batch_size_all): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results under cpu mode. - - On cpu mode, this function will save the results on different gpus to - ``tmpdir`` and collect them by the rank 0 worker. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - tmpdir (str | None): temporal directory for collected results to - store. If set to None, it will create a random temporal directory - for it. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_result = mmcv.load(part_file) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results under gpu mode. - - On gpu mode, this function will encode results to gpu tensors and use gpu - communication for results collection. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/__init__.py deleted file mode 100644 index 2051b85f7..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .file_client import BaseStorageBackend, FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler -from .io import dump, load, register_handler -from .parse import dict_from_file, list_from_file - -__all__ = [ - 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', - 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', - 'list_from_file', 'dict_from_file' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/file_client.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/file_client.py deleted file mode 100644 index e7fd7cdfa..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/file_client.py +++ /dev/null @@ -1,1163 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import os -import os.path as osp -import re -import tempfile -import warnings -from abc import ABCMeta, abstractmethod -from contextlib import contextmanager -from pathlib import Path -from typing import Iterable, Iterator, Optional, Tuple, Union -from urllib.request import urlopen - -import mmcv -from mmcv.utils.misc import has_method -from mmcv.utils.path import is_filepath - - -class BaseStorageBackend(metaclass=ABCMeta): - """Abstract class of storage backends. - - All backends need to implement two apis: ``get()`` and ``get_text()``. - ``get()`` reads the file as a byte stream and ``get_text()`` reads the file - as texts. - """ - - # a flag to indicate whether the backend can create a symlink for a file - _allow_symlink = False - - @property - def name(self): - return self.__class__.__name__ - - @property - def allow_symlink(self): - return self._allow_symlink - - @abstractmethod - def get(self, filepath): - pass - - @abstractmethod - def get_text(self, filepath): - pass - - -class CephBackend(BaseStorageBackend): - """Ceph storage backend (for internal use). - - Args: - path_mapping (dict|None): path mapping dict from local path to Petrel - path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` - will be replaced by ``dst``. Default: None. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - """ - - def __init__(self, path_mapping=None): - try: - import ceph - except ImportError: - raise ImportError('Please install ceph to enable CephBackend.') - - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - self._client = ceph.S3Client() - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def get(self, filepath): - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class PetrelBackend(BaseStorageBackend): - """Petrel storage backend (for internal use). - - PetrelBackend supports reading and writing data to multiple clusters. - If the file path contains the cluster name, PetrelBackend will read data - from specified cluster or write data to it. Otherwise, PetrelBackend will - access the default cluster. - - Args: - path_mapping (dict, optional): Path mapping dict from local path to - Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in - ``filepath`` will be replaced by ``dst``. Default: None. - enable_mc (bool, optional): Whether to enable memcached support. - Default: True. - - Examples: - >>> filepath1 = 's3://path/of/file' - >>> filepath2 = 'cluster-name:s3://path/of/file' - >>> client = PetrelBackend() - >>> client.get(filepath1) # get data from default cluster - >>> client.get(filepath2) # get data from 'cluster-name' cluster - """ - - def __init__(self, - path_mapping: Optional[dict] = None, - enable_mc: bool = True): - try: - from petrel_client import client - except ImportError: - raise ImportError('Please install petrel_client to enable ' - 'PetrelBackend.') - - self._client = client.Client(enable_mc=enable_mc) - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def _map_path(self, filepath: Union[str, Path]) -> str: - """Map ``filepath`` to a string path whose prefix will be replaced by - :attr:`self.path_mapping`. - - Args: - filepath (str): Path to be mapped. - """ - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - return filepath - - def _format_path(self, filepath: str) -> str: - """Convert a ``filepath`` to standard format of petrel oss. - - If the ``filepath`` is concatenated by ``os.path.join``, in a Windows - environment, the ``filepath`` will be the format of - 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the - above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. - - Args: - filepath (str): Path to be formatted. - """ - return re.sub(r'\\+', '/', filepath) - - def get(self, filepath: Union[str, Path]) -> memoryview: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - memoryview: A memory view of expected bytes object to avoid - copying. The memoryview object can be converted to bytes by - ``value_buf.tobytes()``. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return str(self.get(filepath), encoding=encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Save data to a given ``filepath``. - - Args: - obj (bytes): Data to be saved. - filepath (str or Path): Path to write data. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.put(filepath, obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Save data to a given ``filepath``. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to encode the ``obj``. - Default: 'utf-8'. - """ - self.put(bytes(obj, encoding=encoding), filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - if not has_method(self._client, 'delete'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `delete` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.delete(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - if not (has_method(self._client, 'contains') - and has_method(self._client, 'isdir')): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` and `isdir` methods, please use a higher' - 'version or dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) or self._client.isdir(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - if not has_method(self._client, 'isdir'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `isdir` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - if not has_method(self._client, 'contains'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` method, please use a higher version or ' - 'dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result after concatenation. - """ - filepath = self._format_path(self._map_path(filepath)) - if filepath.endswith('/'): - filepath = filepath[:-1] - formatted_paths = [filepath] - for path in filepaths: - formatted_paths.append(self._format_path(self._map_path(path))) - return '/'.join(formatted_paths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download a file from ``filepath`` and return a temporary path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str | Path): Download a file from ``filepath``. - - Examples: - >>> client = PetrelBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('s3://path/of/your/file') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one temporary path. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - assert self.isfile(filepath) - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - Petrel has no concept of directories but it simulates the directory - hierarchy in the filesystem through public prefixes. In addition, - if the returned path ends with '/', it means the path is a public - prefix which is a logical directory. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - In addition, the returned path of directory will not contains the - suffix '/' which is consistent with other backends. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if not has_method(self._client, 'list'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `list` method, please use a higher version or dev' - ' branch instead.')) - - dir_path = self._map_path(dir_path) - dir_path = self._format_path(dir_path) - if list_dir and suffix is not None: - raise TypeError( - '`list_dir` should be False when `suffix` is not None') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - # Petrel's simulated directory hierarchy assumes that directory paths - # should end with `/` - if not dir_path.endswith('/'): - dir_path += '/' - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for path in self._client.list(dir_path): - # the `self.isdir` is not used here to determine whether path - # is a directory, because `self.isdir` relies on - # `self._client.list` - if path.endswith('/'): # a directory path - next_dir_path = self.join_path(dir_path, path) - if list_dir: - # get the relative path and exclude the last - # character '/' - rel_dir = next_dir_path[len(root):-1] - yield rel_dir - if recursive: - yield from _list_dir_or_file(next_dir_path, list_dir, - list_file, suffix, - recursive) - else: # a file path - absolute_path = self.join_path(dir_path, path) - rel_path = absolute_path[len(root):] - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class MemcachedBackend(BaseStorageBackend): - """Memcached storage backend. - - Attributes: - server_list_cfg (str): Config file for memcached server list. - client_cfg (str): Config file for memcached client. - sys_path (str | None): Additional path to be appended to `sys.path`. - Default: None. - """ - - def __init__(self, server_list_cfg, client_cfg, sys_path=None): - if sys_path is not None: - import sys - sys.path.append(sys_path) - try: - import mc - except ImportError: - raise ImportError( - 'Please install memcached to enable MemcachedBackend.') - - self.server_list_cfg = server_list_cfg - self.client_cfg = client_cfg - self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, - self.client_cfg) - # mc.pyvector servers as a point which points to a memory cache - self._mc_buffer = mc.pyvector() - - def get(self, filepath): - filepath = str(filepath) - import mc - self._client.Get(filepath, self._mc_buffer) - value_buf = mc.ConvertBuffer(self._mc_buffer) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class LmdbBackend(BaseStorageBackend): - """Lmdb storage backend. - - Args: - db_path (str): Lmdb database path. - readonly (bool, optional): Lmdb environment parameter. If True, - disallow any write operations. Default: True. - lock (bool, optional): Lmdb environment parameter. If False, when - concurrent access occurs, do not lock the database. Default: False. - readahead (bool, optional): Lmdb environment parameter. If False, - disable the OS filesystem readahead mechanism, which may improve - random read performance when a database is larger than RAM. - Default: False. - - Attributes: - db_path (str): Lmdb database path. - """ - - def __init__(self, - db_path, - readonly=True, - lock=False, - readahead=False, - **kwargs): - try: - import lmdb # NOQA - except ImportError: - raise ImportError('Please install lmdb to enable LmdbBackend.') - - self.db_path = str(db_path) - self.readonly = readonly - self.lock = lock - self.readahead = readahead - self.kwargs = kwargs - self._client = None - - def get(self, filepath): - """Get values according to the filepath. - - Args: - filepath (str | obj:`Path`): Here, filepath is the lmdb key. - """ - if self._client is None: - self._client = self._get_client() - - with self._client.begin(write=False) as txn: - value_buf = txn.get(str(filepath).encode('utf-8')) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - def _get_client(self): - import lmdb - - return lmdb.open( - self.db_path, - readonly=self.readonly, - lock=self.lock, - readahead=self.readahead, - **self.kwargs) - - def __del__(self): - self._client.close() - - -class HardDiskBackend(BaseStorageBackend): - """Raw hard disks storage backend.""" - - _allow_symlink = True - - def get(self, filepath: Union[str, Path]) -> bytes: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes: Expected bytes object. - """ - with open(filepath, 'rb') as f: - value_buf = f.read() - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - with open(filepath, 'r', encoding=encoding) as f: - value_buf = f.read() - return value_buf - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` will create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'wb') as f: - f.write(obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` will create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'w', encoding=encoding) as f: - f.write(obj) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - os.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return osp.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return osp.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return osp.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return osp.join(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, filepath: Union[str, Path]) -> Iterable[Union[str, Path]]: - """Only for unified API and do nothing.""" - yield filepath - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if list_dir and suffix is not None: - raise TypeError('`suffix` should be None when `list_dir` is True') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - elif osp.isdir(entry.path): - if list_dir: - rel_dir = osp.relpath(entry.path, root) - yield rel_dir - if recursive: - yield from _list_dir_or_file(entry.path, list_dir, - list_file, suffix, - recursive) - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class HTTPBackend(BaseStorageBackend): - """HTTP and HTTPS storage bachend.""" - - def get(self, filepath): - value_buf = urlopen(filepath).read() - return value_buf - - def get_text(self, filepath, encoding='utf-8'): - value_buf = urlopen(filepath).read() - return value_buf.decode(encoding) - - @contextmanager - def get_local_path(self, filepath: str) -> Iterable[str]: - """Download a file from ``filepath``. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str): Download a file from ``filepath``. - - Examples: - >>> client = HTTPBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('http://path/of/your/file') as path: - ... # do something here - """ - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - -class FileClient: - """A general file client to access files in different backends. - - The client loads a file or text in a specified backend from its path - and returns it as a binary or text file. There are two ways to choose a - backend, the name of backend and the prefix of path. Although both of them - can be used to choose a storage backend, ``backend`` has a higher priority - that is if they are all set, the storage backend will be chosen by the - backend argument. If they are all `None`, the disk backend will be chosen. - Note that It can also register other backend accessor with a given name, - prefixes, and backend class. In addition, We use the singleton pattern to - avoid repeated object creation. If the arguments are the same, the same - object will be returned. - - Args: - backend (str, optional): The storage backend type. Options are "disk", - "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. - prefix (str, optional): The prefix of the registered storage backend. - Options are "s3", "http", "https". Default: None. - - Examples: - >>> # only set backend - >>> file_client = FileClient(backend='petrel') - >>> # only set prefix - >>> file_client = FileClient(prefix='s3') - >>> # set both backend and prefix but use backend to choose client - >>> file_client = FileClient(backend='petrel', prefix='s3') - >>> # if the arguments are the same, the same object is returned - >>> file_client1 = FileClient(backend='petrel') - >>> file_client1 is file_client - True - - Attributes: - client (:obj:`BaseStorageBackend`): The backend object. - """ - - _backends = { - 'disk': HardDiskBackend, - 'ceph': CephBackend, - 'memcached': MemcachedBackend, - 'lmdb': LmdbBackend, - 'petrel': PetrelBackend, - 'http': HTTPBackend, - } - # This collection is used to record the overridden backends, and when a - # backend appears in the collection, the singleton pattern is disabled for - # that backend, because if the singleton pattern is used, then the object - # returned will be the backend before overwriting - _overridden_backends = set() - _prefix_to_backends = { - 's3': PetrelBackend, - 'http': HTTPBackend, - 'https': HTTPBackend, - } - _overridden_prefixes = set() - - _instances = {} - - def __new__(cls, backend=None, prefix=None, **kwargs): - if backend is None and prefix is None: - backend = 'disk' - if backend is not None and backend not in cls._backends: - raise ValueError( - f'Backend {backend} is not supported. Currently supported ones' - f' are {list(cls._backends.keys())}') - if prefix is not None and prefix not in cls._prefix_to_backends: - raise ValueError( - f'prefix {prefix} is not supported. Currently supported ones ' - f'are {list(cls._prefix_to_backends.keys())}') - - # concatenate the arguments to a unique key for determining whether - # objects with the same arguments were created - arg_key = f'{backend}:{prefix}' - for key, value in kwargs.items(): - arg_key += f':{key}:{value}' - - # if a backend was overridden, it will create a new object - if (arg_key in cls._instances - and backend not in cls._overridden_backends - and prefix not in cls._overridden_prefixes): - _instance = cls._instances[arg_key] - else: - # create a new object and put it to _instance - _instance = super().__new__(cls) - if backend is not None: - _instance.client = cls._backends[backend](**kwargs) - else: - _instance.client = cls._prefix_to_backends[prefix](**kwargs) - - cls._instances[arg_key] = _instance - - return _instance - - @property - def name(self): - return self.client.name - - @property - def allow_symlink(self): - return self.client.allow_symlink - - @staticmethod - def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: - """Parse the prefix of a uri. - - Args: - uri (str | Path): Uri to be parsed that contains the file prefix. - - Examples: - >>> FileClient.parse_uri_prefix('s3://path/of/your/file') - 's3' - - Returns: - str | None: Return the prefix of uri if the uri contains '://' else - ``None``. - """ - assert is_filepath(uri) - uri = str(uri) - if '://' not in uri: - return None - else: - prefix, _ = uri.split('://') - # In the case of PetrelBackend, the prefix may contains the cluster - # name like clusterName:s3 - if ':' in prefix: - _, prefix = prefix.split(':') - return prefix - - @classmethod - def infer_client(cls, - file_client_args: Optional[dict] = None, - uri: Optional[Union[str, Path]] = None) -> 'FileClient': - """Infer a suitable file client based on the URI and arguments. - - Args: - file_client_args (dict, optional): Arguments to instantiate a - FileClient. Default: None. - uri (str | Path, optional): Uri to be parsed that contains the file - prefix. Default: None. - - Examples: - >>> uri = 's3://path/of/your/file' - >>> file_client = FileClient.infer_client(uri=uri) - >>> file_client_args = {'backend': 'petrel'} - >>> file_client = FileClient.infer_client(file_client_args) - - Returns: - FileClient: Instantiated FileClient object. - """ - assert file_client_args is not None or uri is not None - if file_client_args is None: - file_prefix = cls.parse_uri_prefix(uri) # type: ignore - return cls(prefix=file_prefix) - else: - return cls(**file_client_args) - - @classmethod - def _register_backend(cls, name, backend, force=False, prefixes=None): - if not isinstance(name, str): - raise TypeError('the backend name should be a string, ' - f'but got {type(name)}') - if not inspect.isclass(backend): - raise TypeError( - f'backend should be a class but got {type(backend)}') - if not issubclass(backend, BaseStorageBackend): - raise TypeError( - f'backend {backend} is not a subclass of BaseStorageBackend') - if not force and name in cls._backends: - raise KeyError( - f'{name} is already registered as a storage backend, ' - 'add "force=True" if you want to override it') - - if name in cls._backends and force: - cls._overridden_backends.add(name) - cls._backends[name] = backend - - if prefixes is not None: - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if prefix not in cls._prefix_to_backends: - cls._prefix_to_backends[prefix] = backend - elif (prefix in cls._prefix_to_backends) and force: - cls._overridden_prefixes.add(prefix) - cls._prefix_to_backends[prefix] = backend - else: - raise KeyError( - f'{prefix} is already registered as a storage backend,' - ' add "force=True" if you want to override it') - - @classmethod - def register_backend(cls, name, backend=None, force=False, prefixes=None): - """Register a backend to FileClient. - - This method can be used as a normal class method or a decorator. - - .. code-block:: python - - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - FileClient.register_backend('new', NewBackend) - - or - - .. code-block:: python - - @FileClient.register_backend('new') - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - Args: - name (str): The name of the registered backend. - backend (class, optional): The backend class to be registered, - which must be a subclass of :class:`BaseStorageBackend`. - When this method is used as a decorator, backend is None. - Defaults to None. - force (bool, optional): Whether to override the backend if the name - has already been registered. Defaults to False. - prefixes (str or list[str] or tuple[str], optional): The prefixes - of the registered storage backend. Default: None. - `New in version 1.3.15.` - """ - if backend is not None: - cls._register_backend( - name, backend, force=force, prefixes=prefixes) - return - - def _register(backend_cls): - cls._register_backend( - name, backend_cls, force=force, prefixes=prefixes) - return backend_cls - - return _register - - def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: - """Read data from a given ``filepath`` with 'rb' mode. - - Note: - There are two types of return values for ``get``, one is ``bytes`` - and the other is ``memoryview``. The advantage of using memoryview - is that you can avoid copying, and if you want to convert it to - ``bytes``, you can use ``.tobytes()``. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes | memoryview: Expected bytes object or a memory view of the - bytes object. - """ - return self.client.get(filepath) - - def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return self.client.get_text(filepath, encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` should create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - self.client.put(obj, filepath) - - def put_text(self, obj: str, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` should create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str, optional): The encoding format used to open the - `filepath`. Default: 'utf-8'. - """ - self.client.put_text(obj, filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str, Path): Path to be removed. - """ - self.client.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return self.client.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return self.client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return self.client.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return self.client.join_path(filepath, *filepaths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download data from ``filepath`` and write the data to local path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Note: - If the ``filepath`` is a local path, just return itself. - - .. warning:: - ``get_local_path`` is an experimental interface that may change in - the future. - - Args: - filepath (str or Path): Path to be read data. - - Examples: - >>> file_client = FileClient(prefix='s3') - >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one path. - """ - with self.client.get_local_path(str(filepath)) as local_path: - yield local_path - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, - suffix, recursive) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/__init__.py deleted file mode 100644 index aa24d9197..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseFileHandler -from .json_handler import JsonHandler -from .pickle_handler import PickleHandler -from .yaml_handler import YamlHandler - -__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/base.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/base.py deleted file mode 100644 index 288878bc5..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseFileHandler(metaclass=ABCMeta): - # `str_like` is a flag to indicate whether the type of file object is - # str-like object or bytes-like object. Pickle only processes bytes-like - # objects but json only processes str-like object. If it is str-like - # object, `StringIO` will be used to process the buffer. - str_like = True - - @abstractmethod - def load_from_fileobj(self, file, **kwargs): - pass - - @abstractmethod - def dump_to_fileobj(self, obj, file, **kwargs): - pass - - @abstractmethod - def dump_to_str(self, obj, **kwargs): - pass - - def load_from_path(self, filepath, mode='r', **kwargs): - with open(filepath, mode) as f: - return self.load_from_fileobj(f, **kwargs) - - def dump_to_path(self, obj, filepath, mode='w', **kwargs): - with open(filepath, mode) as f: - self.dump_to_fileobj(obj, f, **kwargs) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/json_handler.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/json_handler.py deleted file mode 100644 index 18d4f15f7..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/json_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -import numpy as np - -from .base import BaseFileHandler - - -def set_default(obj): - """Set default json values for non-serializable values. - - It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. - It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, - etc.) into plain numbers of plain python built-in types. - """ - if isinstance(obj, (set, range)): - return list(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, np.generic): - return obj.item() - raise TypeError(f'{type(obj)} is unsupported for json dump') - - -class JsonHandler(BaseFileHandler): - - def load_from_fileobj(self, file): - return json.load(file) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('default', set_default) - json.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('default', set_default) - return json.dumps(obj, **kwargs) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/pickle_handler.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/pickle_handler.py deleted file mode 100644 index b37c79bed..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/pickle_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import pickle - -from .base import BaseFileHandler - - -class PickleHandler(BaseFileHandler): - - str_like = False - - def load_from_fileobj(self, file, **kwargs): - return pickle.load(file, **kwargs) - - def load_from_path(self, filepath, **kwargs): - return super(PickleHandler, self).load_from_path( - filepath, mode='rb', **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('protocol', 2) - return pickle.dumps(obj, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('protocol', 2) - pickle.dump(obj, file, **kwargs) - - def dump_to_path(self, obj, filepath, **kwargs): - super(PickleHandler, self).dump_to_path( - obj, filepath, mode='wb', **kwargs) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/yaml_handler.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/yaml_handler.py deleted file mode 100644 index 60911e7e6..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/handlers/yaml_handler.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import yaml - -try: - from yaml import CDumper as Dumper - from yaml import CLoader as Loader -except ImportError: - from yaml import Loader, Dumper - -from .base import BaseFileHandler # isort:skip - - -class YamlHandler(BaseFileHandler): - - def load_from_fileobj(self, file, **kwargs): - kwargs.setdefault('Loader', Loader) - return yaml.load(file, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('Dumper', Dumper) - yaml.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('Dumper', Dumper) - return yaml.dump(obj, **kwargs) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/io.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/io.py deleted file mode 100644 index aaefde58a..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/io.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from io import BytesIO, StringIO -from pathlib import Path - -from ..utils import is_list_of, is_str -from .file_client import FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler - -file_handlers = { - 'json': JsonHandler(), - 'yaml': YamlHandler(), - 'yml': YamlHandler(), - 'pickle': PickleHandler(), - 'pkl': PickleHandler() -} - - -def load(file, file_format=None, file_client_args=None, **kwargs): - """Load data from json/yaml/pickle files. - - This method provides a unified api for loading data from serialized files. - - Note: - In v1.3.16 and later, ``load`` supports loading data from serialized - files those can be storaged in different backends. - - Args: - file (str or :obj:`Path` or file-like object): Filename or a file-like - object. - file_format (str, optional): If not specified, the file format will be - inferred from the file extension, otherwise use the specified one. - Currently supported formats include "json", "yaml/yml" and - "pickle/pkl". - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> load('/path/of/your/file') # file is storaged in disk - >>> load('https://path/of/your/file') # file is storaged in Internet - >>> load('s3://path/of/your/file') # file is storaged in petrel - - Returns: - The content from the file. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None and is_str(file): - file_format = file.split('.')[-1] - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO(file_client.get_text(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - else: - with BytesIO(file_client.get(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - elif hasattr(file, 'read'): - obj = handler.load_from_fileobj(file, **kwargs) - else: - raise TypeError('"file" must be a filepath str or a file-object') - return obj - - -def dump(obj, file=None, file_format=None, file_client_args=None, **kwargs): - """Dump data to json/yaml/pickle strings or files. - - This method provides a unified api for dumping data as strings or to files, - and also supports custom arguments for each file format. - - Note: - In v1.3.16 and later, ``dump`` supports dumping data as strings or to - files which is saved to different backends. - - Args: - obj (any): The python object to be dumped. - file (str or :obj:`Path` or file-like object, optional): If not - specified, then the object is dumped to a str, otherwise to a file - specified by the filename or file-like object. - file_format (str, optional): Same as :func:`load`. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dump('hello world', '/path/of/your/file') # disk - >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel - - Returns: - bool: True for success, False otherwise. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None: - if is_str(file): - file_format = file.split('.')[-1] - elif file is None: - raise ValueError( - 'file_format must be specified since file is None') - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if file is None: - return handler.dump_to_str(obj, **kwargs) - elif is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put_text(f.getvalue(), file) - else: - with BytesIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put(f.getvalue(), file) - elif hasattr(file, 'write'): - handler.dump_to_fileobj(obj, file, **kwargs) - else: - raise TypeError('"file" must be a filename str or a file-object') - - -def _register_handler(handler, file_formats): - """Register a handler for some file extensions. - - Args: - handler (:obj:`BaseFileHandler`): Handler to be registered. - file_formats (str or list[str]): File formats to be handled by this - handler. - """ - if not isinstance(handler, BaseFileHandler): - raise TypeError( - f'handler must be a child of BaseFileHandler, not {type(handler)}') - if isinstance(file_formats, str): - file_formats = [file_formats] - if not is_list_of(file_formats, str): - raise TypeError('file_formats must be a str or a list of str') - for ext in file_formats: - file_handlers[ext] = handler - - -def register_handler(file_formats, **kwargs): - - def wrap(cls): - _register_handler(cls(**kwargs), file_formats) - return cls - - return wrap diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/parse.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/parse.py deleted file mode 100644 index f60f0d611..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/fileio/parse.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from io import StringIO - -from .file_client import FileClient - - -def list_from_file(filename, - prefix='', - offset=0, - max_num=0, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a list of strings. - - Note: - In v1.3.16 and later, ``list_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a list for strings. - - Args: - filename (str): Filename. - prefix (str): The prefix to be inserted to the beginning of each item. - offset (int): The offset of lines. - max_num (int): The maximum number of lines to be read, - zeros and negatives mean no limitation. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> list_from_file('/path/of/your/file') # disk - ['hello', 'world'] - >>> list_from_file('s3://path/of/your/file') # ceph or petrel - ['hello', 'world'] - - Returns: - list[str]: A list of strings. - """ - cnt = 0 - item_list = [] - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for _ in range(offset): - f.readline() - for line in f: - if 0 < max_num <= cnt: - break - item_list.append(prefix + line.rstrip('\n\r')) - cnt += 1 - return item_list - - -def dict_from_file(filename, - key_type=str, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a dict. - - Each line of the text file will be two or more columns split by - whitespaces or tabs. The first column will be parsed as dict keys, and - the following columns will be parsed as dict values. - - Note: - In v1.3.16 and later, ``dict_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a dict. - - Args: - filename(str): Filename. - key_type(type): Type of the dict keys. str is user by default and - type conversion will be performed if specified. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dict_from_file('/path/of/your/file') # disk - {'key1': 'value1', 'key2': 'value2'} - >>> dict_from_file('s3://path/of/your/file') # ceph or petrel - {'key1': 'value1', 'key2': 'value2'} - - Returns: - dict: The parsed contents. - """ - mapping = {} - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for line in f: - items = line.rstrip('\n').split() - assert len(items) >= 2 - key = key_type(items[0]) - val = items[1:] if len(items) > 2 else items[1] - mapping[key] = val - return mapping diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/__init__.py deleted file mode 100644 index d0051d609..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, - gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, - rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) -from .geometric import (cutout, imcrop, imflip, imflip_, impad, - impad_to_multiple, imrescale, imresize, imresize_like, - imresize_to_multiple, imrotate, imshear, imtranslate, - rescale_size) -from .io import imfrombytes, imread, imwrite, supported_backends, use_backend -from .misc import tensor2imgs -from .photometric import (adjust_brightness, adjust_color, adjust_contrast, - adjust_lighting, adjust_sharpness, auto_contrast, - clahe, imdenormalize, imequalize, iminvert, - imnormalize, imnormalize_, lut_transform, posterize, - solarize) - -__all__ = [ - 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', - 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', - 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', - 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', - 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', - 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', - 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', - 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', - 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', - 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/colorspace.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/colorspace.py deleted file mode 100644 index 4337720ea..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/colorspace.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def imconvert(img, src, dst): - """Convert an image from the src colorspace to dst colorspace. - - Args: - img (ndarray): The input image. - src (str): The source colorspace, e.g., 'rgb', 'hsv'. - dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. - - Returns: - ndarray: The converted image. - """ - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - out_img = cv2.cvtColor(img, code) - return out_img - - -def bgr2gray(img, keepdim=False): - """Convert a BGR image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def rgb2gray(img, keepdim=False): - """Convert a RGB image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def gray2bgr(img): - """Convert a grayscale image to BGR image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted BGR image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return out_img - - -def gray2rgb(img): - """Convert a grayscale image to RGB image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted RGB image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) - return out_img - - -def _convert_input_type_range(img): - """Convert the type and range of the input image. - - It converts the input image to np.float32 type and range of [0, 1]. - It is mainly used for pre-processing the input image in colorspace - conversion functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - (ndarray): The converted image with type of np.float32 and range of - [0, 1]. - """ - img_type = img.dtype - img = img.astype(np.float32) - if img_type == np.float32: - pass - elif img_type == np.uint8: - img /= 255. - else: - raise TypeError('The img type should be np.float32 or np.uint8, ' - f'but got {img_type}') - return img - - -def _convert_output_type_range(img, dst_type): - """Convert the type and range of the image according to dst_type. - - It converts the image to desired type and range. If `dst_type` is np.uint8, - images will be converted to np.uint8 type with range [0, 255]. If - `dst_type` is np.float32, it converts the image to np.float32 type with - range [0, 1]. - It is mainly used for post-processing images in colorspace conversion - functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The image to be converted with np.float32 type and - range [0, 255]. - dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it - converts the image to np.uint8 type with range [0, 255]. If - dst_type is np.float32, it converts the image to np.float32 type - with range [0, 1]. - - Returns: - (ndarray): The converted image with desired type and range. - """ - if dst_type not in (np.uint8, np.float32): - raise TypeError('The dst_type should be np.float32 or np.uint8, ' - f'but got {dst_type}') - if dst_type == np.uint8: - img = img.round() - else: - img /= 255. - return img.astype(dst_type) - - -def rgb2ycbcr(img, y_only=False): - """Convert a RGB image to YCbCr image. - - This function produces the same results as Matlab's `rgb2ycbcr` function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 - else: - out_img = np.matmul( - img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], - [24.966, 112.0, -18.214]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def bgr2ycbcr(img, y_only=False): - """Convert a BGR image to YCbCr image. - - The bgr version of rgb2ycbcr. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 - else: - out_img = np.matmul( - img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], - [65.481, -37.797, 112.0]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2rgb(img): - """Convert a YCbCr image to RGB image. - - This function produces the same results as Matlab's ycbcr2rgb function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted RGB image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0, -0.00153632, 0.00791071], - [0.00625893, -0.00318811, 0]]) * 255.0 + [ - -222.921, 135.576, -276.836 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2bgr(img): - """Convert a YCbCr image to BGR image. - - The bgr version of ycbcr2rgb. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted BGR image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0.00791071, -0.00153632, 0], - [0, -0.00318811, 0.00625893]]) * 255.0 + [ - -276.836, 135.576, -222.921 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def convert_color_factory(src, dst): - - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - - def convert_color(img): - out_img = cv2.cvtColor(img, code) - return out_img - - convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} - image. - - Args: - img (ndarray or str): The input image. - - Returns: - ndarray: The converted {dst.upper()} image. - """ - - return convert_color - - -bgr2rgb = convert_color_factory('bgr', 'rgb') - -rgb2bgr = convert_color_factory('rgb', 'bgr') - -bgr2hsv = convert_color_factory('bgr', 'hsv') - -hsv2bgr = convert_color_factory('hsv', 'bgr') - -bgr2hls = convert_color_factory('bgr', 'hls') - -hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/geometric.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/geometric.py deleted file mode 100644 index 4c423bf2a..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/geometric.py +++ /dev/null @@ -1,741 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers - -import cv2 -import numpy as np - -from ..utils import to_2tuple -from .io import imread_backend - -try: - from PIL import Image -except ImportError: - Image = None - - -def _scale_size(size, scale): - """Rescale a size by a ratio. - - Args: - size (tuple[int]): (w, h). - scale (float | tuple(float)): Scaling factor. - - Returns: - tuple[int]: scaled size. - """ - if isinstance(scale, (float, int)): - scale = (scale, scale) - w, h = size - return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) - - -cv2_interp_codes = { - 'nearest': cv2.INTER_NEAREST, - 'bilinear': cv2.INTER_LINEAR, - 'bicubic': cv2.INTER_CUBIC, - 'area': cv2.INTER_AREA, - 'lanczos': cv2.INTER_LANCZOS4 -} - -# Pillow >=v9.1.0 use a slightly different naming scheme for filters. -# Set pillow_interp_codes according to the naming scheme used. -if Image is not None: - if hasattr(Image, 'Resampling'): - pillow_interp_codes = { - 'nearest': Image.Resampling.NEAREST, - 'bilinear': Image.Resampling.BILINEAR, - 'bicubic': Image.Resampling.BICUBIC, - 'box': Image.Resampling.BOX, - 'lanczos': Image.Resampling.LANCZOS, - 'hamming': Image.Resampling.HAMMING - } - else: - pillow_interp_codes = { - 'nearest': Image.NEAREST, - 'bilinear': Image.BILINEAR, - 'bicubic': Image.BICUBIC, - 'box': Image.BOX, - 'lanczos': Image.LANCZOS, - 'hamming': Image.HAMMING - } - - -def imresize(img, - size, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image to a given size. - - Args: - img (ndarray): The input image. - size (tuple[int]): Target size (w, h). - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if backend is None: - backend = imread_backend - if backend not in ['cv2', 'pillow']: - raise ValueError(f'backend: {backend} is not supported for resize.' - f"Supported backends are 'cv2', 'pillow'") - - if backend == 'pillow': - assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' - pil_image = Image.fromarray(img) - pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) - resized_img = np.array(pil_image) - else: - resized_img = cv2.resize( - img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) - if not return_scale: - return resized_img - else: - w_scale = size[0] / w - h_scale = size[1] / h - return resized_img, w_scale, h_scale - - -def imresize_to_multiple(img, - divisor, - size=None, - scale_factor=None, - keep_ratio=False, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image according to a given size or scale factor and then rounds - up the the resized or rescaled image size to the nearest value that can be - divided by the divisor. - - Args: - img (ndarray): The input image. - divisor (int | tuple): Resized image size will be a multiple of - divisor. If divisor is a tuple, divisor should be - (w_divisor, h_divisor). - size (None | int | tuple[int]): Target size (w, h). Default: None. - scale_factor (None | float | tuple[float]): Multiplier for spatial - size. Should match input size if it is a tuple and the 2D style is - (w_scale_factor, h_scale_factor). Default: None. - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: False. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if size is not None and scale_factor is not None: - raise ValueError('only one of size or scale_factor should be defined') - elif size is None and scale_factor is None: - raise ValueError('one of size or scale_factor should be defined') - elif size is not None: - size = to_2tuple(size) - if keep_ratio: - size = rescale_size((w, h), size, return_scale=False) - else: - size = _scale_size((w, h), scale_factor) - - divisor = to_2tuple(divisor) - size = tuple([int(np.ceil(s / d)) * d for s, d in zip(size, divisor)]) - resized_img, w_scale, h_scale = imresize( - img, - size, - return_scale=True, - interpolation=interpolation, - out=out, - backend=backend) - if return_scale: - return resized_img, w_scale, h_scale - else: - return resized_img - - -def imresize_like(img, - dst_img, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image to the same size of a given image. - - Args: - img (ndarray): The input image. - dst_img (ndarray): The target image. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = dst_img.shape[:2] - return imresize(img, (w, h), return_scale, interpolation, backend=backend) - - -def rescale_size(old_size, scale, return_scale=False): - """Calculate the new size to be rescaled to. - - Args: - old_size (tuple[int]): The old size (w, h) of image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image size. - - Returns: - tuple[int]: The new rescaled image size. - """ - w, h = old_size - if isinstance(scale, (float, int)): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - scale_factor = scale - elif isinstance(scale, tuple): - max_long_edge = max(scale) - max_short_edge = min(scale) - scale_factor = min(max_long_edge / max(h, w), - max_short_edge / min(h, w)) - else: - raise TypeError( - f'Scale must be a number or tuple of int, but got {type(scale)}') - - new_size = _scale_size((w, h), scale_factor) - - if return_scale: - return new_size, scale_factor - else: - return new_size - - -def imrescale(img, - scale, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image while keeping the aspect ratio. - - Args: - img (ndarray): The input image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - ndarray: The rescaled image. - """ - h, w = img.shape[:2] - new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) - rescaled_img = imresize( - img, new_size, interpolation=interpolation, backend=backend) - if return_scale: - return rescaled_img, scale_factor - else: - return rescaled_img - - -def imflip(img, direction='horizontal'): - """Flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image. - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return np.flip(img, axis=1) - elif direction == 'vertical': - return np.flip(img, axis=0) - else: - return np.flip(img, axis=(0, 1)) - - -def imflip_(img, direction='horizontal'): - """Inplace flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image (inplace). - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return cv2.flip(img, 1, img) - elif direction == 'vertical': - return cv2.flip(img, 0, img) - else: - return cv2.flip(img, -1, img) - - -def imrotate(img, - angle, - center=None, - scale=1.0, - border_value=0, - interpolation='bilinear', - auto_bound=False): - """Rotate an image. - - Args: - img (ndarray): Image to be rotated. - angle (float): Rotation angle in degrees, positive values mean - clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. - scale (float): Isotropic scale factor. - border_value (int): Border value. - interpolation (str): Same as :func:`resize`. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. - - Returns: - ndarray: The rotated image. - """ - if center is not None and auto_bound: - raise ValueError('`auto_bound` conflicts with `center`') - h, w = img.shape[:2] - if center is None: - center = ((w - 1) * 0.5, (h - 1) * 0.5) - assert isinstance(center, tuple) - - matrix = cv2.getRotationMatrix2D(center, -angle, scale) - if auto_bound: - cos = np.abs(matrix[0, 0]) - sin = np.abs(matrix[0, 1]) - new_w = h * sin + w * cos - new_h = h * cos + w * sin - matrix[0, 2] += (new_w - w) * 0.5 - matrix[1, 2] += (new_h - h) * 0.5 - w = int(np.round(new_w)) - h = int(np.round(new_h)) - rotated = cv2.warpAffine( - img, - matrix, (w, h), - flags=cv2_interp_codes[interpolation], - borderValue=border_value) - return rotated - - -def bbox_clip(bboxes, img_shape): - """Clip bboxes to fit the image shape. - - Args: - bboxes (ndarray): Shape (..., 4*k) - img_shape (tuple[int]): (height, width) of the image. - - Returns: - ndarray: Clipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) - cmin[0::2] = img_shape[1] - 1 - cmin[1::2] = img_shape[0] - 1 - clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) - return clipped_bboxes - - -def bbox_scaling(bboxes, scale, clip_shape=None): - """Scaling bboxes w.r.t the box center. - - Args: - bboxes (ndarray): Shape(..., 4). - scale (float): Scaling factor. - clip_shape (tuple[int], optional): If specified, bboxes that exceed the - boundary will be clipped according to the given shape (h, w). - - Returns: - ndarray: Scaled bboxes. - """ - if float(scale) == 1.0: - scaled_bboxes = bboxes.copy() - else: - w = bboxes[..., 2] - bboxes[..., 0] + 1 - h = bboxes[..., 3] - bboxes[..., 1] + 1 - dw = (w * (scale - 1)) * 0.5 - dh = (h * (scale - 1)) * 0.5 - scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) - if clip_shape is not None: - return bbox_clip(scaled_bboxes, clip_shape) - else: - return scaled_bboxes - - -def imcrop(img, bboxes, scale=1.0, pad_fill=None): - """Crop image patches. - - 3 steps: scale the bboxes -> clip bboxes -> crop and pad. - - Args: - img (ndarray): Image to be cropped. - bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. - scale (float, optional): Scale ratio of bboxes, the default value - 1.0 means no padding. - pad_fill (Number | list[Number]): Value to be filled for padding. - Default: None, which means no padding. - - Returns: - list[ndarray] | ndarray: The cropped image patches. - """ - chn = 1 if img.ndim == 2 else img.shape[2] - if pad_fill is not None: - if isinstance(pad_fill, (int, float)): - pad_fill = [pad_fill for _ in range(chn)] - assert len(pad_fill) == chn - - _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes - scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) - clipped_bbox = bbox_clip(scaled_bboxes, img.shape) - - patches = [] - for i in range(clipped_bbox.shape[0]): - x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) - if pad_fill is None: - patch = img[y1:y2 + 1, x1:x2 + 1, ...] - else: - _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) - if chn == 1: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) - else: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) - patch = np.array( - pad_fill, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - x_start = 0 if _x1 >= 0 else -_x1 - y_start = 0 if _y1 >= 0 else -_y1 - w = x2 - x1 + 1 - h = y2 - y1 + 1 - patch[y_start:y_start + h, x_start:x_start + w, - ...] = img[y1:y1 + h, x1:x1 + w, ...] - patches.append(patch) - - if bboxes.ndim == 1: - return patches[0] - else: - return patches - - -def impad(img, - *, - shape=None, - padding=None, - pad_val=0, - padding_mode='constant'): - """Pad the given image to a certain shape or pad on all sides with - specified padding mode and padding value. - - Args: - img (ndarray): Image to be padded. - shape (tuple[int]): Expected padding shape (h, w). Default: None. - padding (int or tuple[int]): Padding on each border. If a single int is - provided this is used to pad all borders. If tuple of length 2 is - provided this is the padding on left/right and top/bottom - respectively. If a tuple of length 4 is provided this is the - padding for the left, top, right and bottom borders respectively. - Default: None. Note that `shape` and `padding` can not be both - set. - pad_val (Number | Sequence[Number]): Values to be filled in padding - areas when padding_mode is 'constant'. Default: 0. - padding_mode (str): Type of padding. Should be: constant, edge, - reflect or symmetric. Default: constant. - - constant: pads with a constant value, this value is specified - with pad_val. - - edge: pads with the last value at the edge of the image. - - reflect: pads with reflection of image without repeating the last - value on the edge. For example, padding [1, 2, 3, 4] with 2 - elements on both sides in reflect mode will result in - [3, 2, 1, 2, 3, 4, 3, 2]. - - symmetric: pads with reflection of image repeating the last value - on the edge. For example, padding [1, 2, 3, 4] with 2 elements on - both sides in symmetric mode will result in - [2, 1, 1, 2, 3, 4, 4, 3] - - Returns: - ndarray: The padded image. - """ - - assert (shape is not None) ^ (padding is not None) - if shape is not None: - width = max(shape[1] - img.shape[1], 0) - height = max(shape[0] - img.shape[0], 0) - padding = (0, 0, width, height) - - # check pad_val - if isinstance(pad_val, tuple): - assert len(pad_val) == img.shape[-1] - elif not isinstance(pad_val, numbers.Number): - raise TypeError('pad_val must be a int or a tuple. ' - f'But received {type(pad_val)}') - - # check padding - if isinstance(padding, tuple) and len(padding) in [2, 4]: - if len(padding) == 2: - padding = (padding[0], padding[1], padding[0], padding[1]) - elif isinstance(padding, numbers.Number): - padding = (padding, padding, padding, padding) - else: - raise ValueError('Padding must be a int or a 2, or 4 element tuple.' - f'But received {padding}') - - # check padding mode - assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] - - border_type = { - 'constant': cv2.BORDER_CONSTANT, - 'edge': cv2.BORDER_REPLICATE, - 'reflect': cv2.BORDER_REFLECT_101, - 'symmetric': cv2.BORDER_REFLECT - } - img = cv2.copyMakeBorder( - img, - padding[1], - padding[3], - padding[0], - padding[2], - border_type[padding_mode], - value=pad_val) - - return img - - -def impad_to_multiple(img, divisor, pad_val=0): - """Pad an image to ensure each edge to be multiple to some number. - - Args: - img (ndarray): Image to be padded. - divisor (int): Padded image edges will be multiple to divisor. - pad_val (Number | Sequence[Number]): Same as :func:`impad`. - - Returns: - ndarray: The padded image. - """ - pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor - pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor - return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) - - -def cutout(img, shape, pad_val=0): - """Randomly cut out a rectangle from the original img. - - Args: - img (ndarray): Image to be cutout. - shape (int | tuple[int]): Expected cutout shape (h, w). If given as a - int, the value will be used for both h and w. - pad_val (int | float | tuple[int | float]): Values to be filled in the - cut area. Defaults to 0. - - Returns: - ndarray: The cutout image. - """ - - channels = 1 if img.ndim == 2 else img.shape[2] - if isinstance(shape, int): - cut_h, cut_w = shape, shape - else: - assert isinstance(shape, tuple) and len(shape) == 2, \ - f'shape must be a int or a tuple with length 2, but got type ' \ - f'{type(shape)} instead.' - cut_h, cut_w = shape - if isinstance(pad_val, (int, float)): - pad_val = tuple([pad_val] * channels) - elif isinstance(pad_val, tuple): - assert len(pad_val) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(pad_val), channels) - else: - raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') - - img_h, img_w = img.shape[:2] - y0 = np.random.uniform(img_h) - x0 = np.random.uniform(img_w) - - y1 = int(max(0, y0 - cut_h / 2.)) - x1 = int(max(0, x0 - cut_w / 2.)) - y2 = min(img_h, y1 + cut_h) - x2 = min(img_w, x1 + cut_w) - - if img.ndim == 2: - patch_shape = (y2 - y1, x2 - x1) - else: - patch_shape = (y2 - y1, x2 - x1, channels) - - img_cutout = img.copy() - patch = np.array( - pad_val, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - img_cutout[y1:y2, x1:x2, ...] = patch - - return img_cutout - - -def _get_shear_matrix(magnitude, direction='horizontal'): - """Generate the shear matrix for transformation. - - Args: - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - - Returns: - ndarray: The shear matrix with dtype float32. - """ - if direction == 'horizontal': - shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) - elif direction == 'vertical': - shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) - return shear_matrix - - -def imshear(img, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear an image. - - Args: - img (ndarray): Image to be sheared with format (h, w) - or (h, w, c). - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The sheared image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`') - shear_matrix = _get_shear_matrix(magnitude, direction) - sheared = cv2.warpAffine( - img, - shear_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. shearing masks whose channels large - # than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return sheared - - -def _get_translate_matrix(offset, direction='horizontal'): - """Generate the translate matrix. - - Args: - offset (int | float): The offset used for translate. - direction (str): The translate direction, either - "horizontal" or "vertical". - - Returns: - ndarray: The translate matrix with dtype float32. - """ - if direction == 'horizontal': - translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) - elif direction == 'vertical': - translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) - return translate_matrix - - -def imtranslate(img, - offset, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Translate an image. - - Args: - img (ndarray): Image to be translated with format - (h, w) or (h, w, c). - offset (int | float): The offset used for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The translated image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`.') - translate_matrix = _get_translate_matrix(offset, direction) - translated = cv2.warpAffine( - img, - translate_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. translating masks whose channels - # large than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return translated diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/io.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/io.py deleted file mode 100644 index ae81b561a..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/io.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os.path as osp -import warnings -from pathlib import Path - -import cv2 -import numpy as np -from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, - IMREAD_UNCHANGED) - -from mmcv.fileio import FileClient -from mmcv.utils import is_filepath, is_str - -try: - from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG -except ImportError: - TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None - -try: - from PIL import Image, ImageOps -except ImportError: - Image = None - -try: - import tifffile -except ImportError: - tifffile = None - -jpeg = None -supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] - -imread_flags = { - 'color': IMREAD_COLOR, - 'grayscale': IMREAD_GRAYSCALE, - 'unchanged': IMREAD_UNCHANGED, - 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, - 'grayscale_ignore_orientation': - IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE -} - -imread_backend = 'cv2' - - -def use_backend(backend): - """Select a backend for image decoding. - - Args: - backend (str): The image decoding backend type. Options are `cv2`, - `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) - and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` - file format. - """ - assert backend in supported_backends - global imread_backend - imread_backend = backend - if imread_backend == 'turbojpeg': - if TurboJPEG is None: - raise ImportError('`PyTurboJPEG` is not installed') - global jpeg - if jpeg is None: - jpeg = TurboJPEG() - elif imread_backend == 'pillow': - if Image is None: - raise ImportError('`Pillow` is not installed') - elif imread_backend == 'tifffile': - if tifffile is None: - raise ImportError('`tifffile` is not installed') - - -def _jpegflag(flag='color', channel_order='bgr'): - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'color': - if channel_order == 'bgr': - return TJPF_BGR - elif channel_order == 'rgb': - return TJCS_RGB - elif flag == 'grayscale': - return TJPF_GRAY - else: - raise ValueError('flag must be "color" or "grayscale"') - - -def _pillow2array(img, flag='color', channel_order='bgr'): - """Convert a pillow image to numpy array. - - Args: - img (:obj:`PIL.Image.Image`): The image loaded using PIL - flag (str): Flags specifying the color type of a loaded image, - candidates are 'color', 'grayscale' and 'unchanged'. - Default to 'color'. - channel_order (str): The channel order of the output image array, - candidates are 'bgr' and 'rgb'. Default to 'bgr'. - - Returns: - np.ndarray: The converted numpy array - """ - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'unchanged': - array = np.array(img) - if array.ndim >= 3 and array.shape[2] >= 3: # color image - array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR - else: - # Handle exif orientation tag - if flag in ['color', 'grayscale']: - img = ImageOps.exif_transpose(img) - # If the image mode is not 'RGB', convert it to 'RGB' first. - if img.mode != 'RGB': - if img.mode != 'LA': - # Most formats except 'LA' can be directly converted to RGB - img = img.convert('RGB') - else: - # When the mode is 'LA', the default conversion will fill in - # the canvas with black, which sometimes shadows black objects - # in the foreground. - # - # Therefore, a random color (124, 117, 104) is used for canvas - img_rgba = img.convert('RGBA') - img = Image.new('RGB', img_rgba.size, (124, 117, 104)) - img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha - if flag in ['color', 'color_ignore_orientation']: - array = np.array(img) - if channel_order != 'rgb': - array = array[:, :, ::-1] # RGB to BGR - elif flag in ['grayscale', 'grayscale_ignore_orientation']: - img = img.convert('L') - array = np.array(img) - else: - raise ValueError( - 'flag must be "color", "grayscale", "unchanged", ' - f'"color_ignore_orientation" or "grayscale_ignore_orientation"' - f' but got {flag}') - return array - - -def imread(img_or_path, - flag='color', - channel_order='bgr', - backend=None, - file_client_args=None): - """Read an image. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Args: - img_or_path (ndarray or str or Path): Either a numpy array or str or - pathlib.Path. If it is a numpy array (loaded image), then - it will be returned as is. - flag (str): Flags specifying the color type of a loaded image, - candidates are `color`, `grayscale`, `unchanged`, - `color_ignore_orientation` and `grayscale_ignore_orientation`. - By default, `cv2` and `pillow` backend would rotate the image - according to its EXIF info unless called with `unchanged` or - `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend - always ignore image's EXIF info regardless of the flag. - The `turbojpeg` backend only supports `color` and `grayscale`. - channel_order (str): Order of channel, candidates are `bgr` and `rgb`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. - If backend is None, the global imread_backend specified by - ``mmcv.use_backend()`` will be used. Default: None. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> import mmcv - >>> img_path = '/path/to/img.jpg' - >>> img = mmcv.imread(img_path) - >>> img = mmcv.imread(img_path, flag='color', channel_order='rgb', - ... backend='cv2') - >>> img = mmcv.imread(img_path, flag='color', channel_order='bgr', - ... backend='pillow') - >>> s3_img_path = 's3://bucket/img.jpg' - >>> # infer the file backend by the prefix s3 - >>> img = mmcv.imread(s3_img_path) - >>> # manually set the file backend petrel - >>> img = mmcv.imread(s3_img_path, file_client_args={ - ... 'backend': 'petrel'}) - >>> http_img_path = 'http://path/to/img.jpg' - >>> img = mmcv.imread(http_img_path) - >>> img = mmcv.imread(http_img_path, file_client_args={ - ... 'backend': 'http'}) - """ - - if isinstance(img_or_path, Path): - img_or_path = str(img_or_path) - - if isinstance(img_or_path, np.ndarray): - return img_or_path - elif is_str(img_or_path): - file_client = FileClient.infer_client(file_client_args, img_or_path) - img_bytes = file_client.get(img_or_path) - return imfrombytes(img_bytes, flag, channel_order, backend) - else: - raise TypeError('"img" must be a numpy array or a str or ' - 'a pathlib.Path object') - - -def imfrombytes(content, flag='color', channel_order='bgr', backend=None): - """Read an image from bytes. - - Args: - content (bytes): Image bytes got from files or other streams. - flag (str): Same as :func:`imread`. - channel_order (str): The channel order of the output, candidates - are 'bgr' and 'rgb'. Default to 'bgr'. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. If backend is - None, the global imread_backend specified by ``mmcv.use_backend()`` - will be used. Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> img_path = '/path/to/img.jpg' - >>> with open(img_path, 'rb') as f: - >>> img_buff = f.read() - >>> img = mmcv.imfrombytes(img_buff) - >>> img = mmcv.imfrombytes(img_buff, flag='color', channel_order='rgb') - >>> img = mmcv.imfrombytes(img_buff, backend='pillow') - >>> img = mmcv.imfrombytes(img_buff, backend='cv2') - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError( - f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow', 'tifffile'") - if backend == 'turbojpeg': - img = jpeg.decode(content, _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - with io.BytesIO(content) as buff: - img = Image.open(buff) - img = _pillow2array(img, flag, channel_order) - return img - elif backend == 'tifffile': - with io.BytesIO(content) as buff: - img = tifffile.imread(buff) - return img - else: - img_np = np.frombuffer(content, np.uint8) - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imdecode(img_np, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - - -def imwrite(img, - file_path, - params=None, - auto_mkdir=None, - file_client_args=None): - """Write image to file. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Warning: - The parameter `auto_mkdir` will be deprecated in the future and every - file clients will make directory automatically. - - Args: - img (ndarray): Image array to be written. - file_path (str): Image file path. - params (None or list): Same as opencv :func:`imwrite` interface. - auto_mkdir (bool): If the parent folder of `file_path` does not exist, - whether to create it automatically. It will be deprecated. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - bool: Successful or not. - - Examples: - >>> # write to hard disk client - >>> ret = mmcv.imwrite(img, '/path/to/img.jpg') - >>> # infer the file backend by the prefix s3 - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg') - >>> # manually set the file backend petrel - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg', file_client_args={ - ... 'backend': 'petrel'}) - """ - assert is_filepath(file_path) - file_path = str(file_path) - if auto_mkdir is not None: - warnings.warn( - 'The parameter `auto_mkdir` will be deprecated in the future and ' - 'every file clients will make directory automatically.') - file_client = FileClient.infer_client(file_client_args, file_path) - img_ext = osp.splitext(file_path)[-1] - # Encode image according to image suffix. - # For example, if image path is '/path/your/img.jpg', the encode - # format is '.jpg'. - flag, img_buff = cv2.imencode(img_ext, img, params) - file_client.put(img_buff.tobytes(), file_path) - return flag diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/misc.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/misc.py deleted file mode 100644 index 43934a689..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/misc.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - -import mmcv - -try: - import torch -except ImportError: - torch = None - - -def tensor2imgs(tensor, mean=None, std=None, to_rgb=True): - """Convert tensor to 3-channel images or 1-channel gray images. - - Args: - tensor (torch.Tensor): Tensor that contains multiple images, shape ( - N, C, H, W). :math:`C` can be either 3 or 1. - mean (tuple[float], optional): Mean of images. If None, - (0, 0, 0) will be used for tensor with 3-channel, - while (0, ) for tensor with 1-channel. Defaults to None. - std (tuple[float], optional): Standard deviation of images. If None, - (1, 1, 1) will be used for tensor with 3-channel, - while (1, ) for tensor with 1-channel. Defaults to None. - to_rgb (bool, optional): Whether the tensor was converted to RGB - format in the first place. If so, convert it back to BGR. - For the tensor with 1 channel, it must be False. Defaults to True. - - Returns: - list[np.ndarray]: A list that contains multiple images. - """ - - if torch is None: - raise RuntimeError('pytorch is not installed') - assert torch.is_tensor(tensor) and tensor.ndim == 4 - channels = tensor.size(1) - assert channels in [1, 3] - if mean is None: - mean = (0, ) * channels - if std is None: - std = (1, ) * channels - assert (channels == len(mean) == len(std) == 3) or \ - (channels == len(mean) == len(std) == 1 and not to_rgb) - - num_imgs = tensor.size(0) - mean = np.array(mean, dtype=np.float32) - std = np.array(std, dtype=np.float32) - imgs = [] - for img_id in range(num_imgs): - img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) - img = mmcv.imdenormalize( - img, mean, std, to_bgr=to_rgb).astype(np.uint8) - imgs.append(np.ascontiguousarray(img)) - return imgs diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/photometric.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/photometric.py deleted file mode 100644 index 5085d0120..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/image/photometric.py +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - -from ..utils import is_tuple_of -from .colorspace import bgr2gray, gray2bgr - - -def imnormalize(img, mean, std, to_rgb=True): - """Normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - img = img.copy().astype(np.float32) - return imnormalize_(img, mean, std, to_rgb) - - -def imnormalize_(img, mean, std, to_rgb=True): - """Inplace normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - # cv2 inplace normalization does not accept uint8 - assert img.dtype != np.uint8 - mean = np.float64(mean.reshape(1, -1)) - stdinv = 1 / np.float64(std.reshape(1, -1)) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - cv2.subtract(img, mean, img) # inplace - cv2.multiply(img, stdinv, img) # inplace - return img - - -def imdenormalize(img, mean, std, to_bgr=True): - assert img.dtype != np.uint8 - mean = mean.reshape(1, -1).astype(np.float64) - std = std.reshape(1, -1).astype(np.float64) - img = cv2.multiply(img, std) # make a copy - cv2.add(img, mean, img) # inplace - if to_bgr: - cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace - return img - - -def iminvert(img): - """Invert (negate) an image. - - Args: - img (ndarray): Image to be inverted. - - Returns: - ndarray: The inverted image. - """ - return np.full_like(img, 255) - img - - -def solarize(img, thr=128): - """Solarize an image (invert all pixel values above a threshold) - - Args: - img (ndarray): Image to be solarized. - thr (int): Threshold for solarizing (0 - 255). - - Returns: - ndarray: The solarized image. - """ - img = np.where(img < thr, img, 255 - img) - return img - - -def posterize(img, bits): - """Posterize an image (reduce the number of bits for each color channel) - - Args: - img (ndarray): Image to be posterized. - bits (int): Number of bits (1 to 8) to use for posterizing. - - Returns: - ndarray: The posterized image. - """ - shift = 8 - bits - img = np.left_shift(np.right_shift(img, shift), shift) - return img - - -def adjust_color(img, alpha=1, beta=None, gamma=0): - r"""It blends the source image and its gray image: - - .. math:: - output = img * alpha + gray\_img * beta + gamma - - Args: - img (ndarray): The input source image. - alpha (int | float): Weight for the source image. Default 1. - beta (int | float): Weight for the converted gray image. - If None, it's assigned the value (1 - `alpha`). - gamma (int | float): Scalar added to each sum. - Same as :func:`cv2.addWeighted`. Default 0. - - Returns: - ndarray: Colored image which has the same size and dtype as input. - """ - gray_img = bgr2gray(img) - gray_img = np.tile(gray_img[..., None], [1, 1, 3]) - if beta is None: - beta = 1 - alpha - colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) - if not colored_img.dtype == np.uint8: - # Note when the dtype of `img` is not the default `np.uint8` - # (e.g. np.float32), the value in `colored_img` got from cv2 - # is not guaranteed to be in range [0, 255], so here clip - # is needed. - colored_img = np.clip(colored_img, 0, 255) - return colored_img - - -def imequalize(img): - """Equalize the image histogram. - - This function applies a non-linear mapping to the input image, - in order to create a uniform distribution of grayscale values - in the output image. - - Args: - img (ndarray): Image to be equalized. - - Returns: - ndarray: The equalized image. - """ - - def _scale_channel(im, c): - """Scale the data in the corresponding channel.""" - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # For computing the step, filter out the nonzeros. - nonzero_histo = histo[histo > 0] - step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 - if not step: - lut = np.array(range(256)) - else: - # Compute the cumulative sum, shifted by step // 2 - # and then normalized by step. - lut = (np.cumsum(histo) + (step // 2)) // step - # Shift lut, prepending with 0. - lut = np.concatenate([[0], lut[:-1]], 0) - # handle potential integer overflow - lut[lut > 255] = 255 - # If step is zero, return the original image. - # Otherwise, index from lut. - return np.where(np.equal(step, 0), im, lut[im]) - - # Scales each channel independently and then stacks - # the result. - s1 = _scale_channel(img, 0) - s2 = _scale_channel(img, 1) - s3 = _scale_channel(img, 2) - equalized_img = np.stack([s1, s2, s3], axis=-1) - return equalized_img.astype(img.dtype) - - -def adjust_brightness(img, factor=1.): - """Adjust image brightness. - - This function controls the brightness of an image. An - enhancement factor of 0.0 gives a black image. - A factor of 1.0 gives the original image. This function - blends the source image and the degenerated black image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be brightened. - factor (float): A value controls the enhancement. - Factor 1.0 returns the original image, lower - factors mean less color (brightness, contrast, - etc), and higher values more. Default 1. - - Returns: - ndarray: The brightened image. - """ - degenerated = np.zeros_like(img) - # Note manually convert the dtype to np.float32, to - # achieve as close results as PIL.ImageEnhance.Brightness. - # Set beta=1-factor, and gamma=0 - brightened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - brightened_img = np.clip(brightened_img, 0, 255) - return brightened_img.astype(img.dtype) - - -def adjust_contrast(img, factor=1.): - """Adjust image contrast. - - This function controls the contrast of an image. An - enhancement factor of 0.0 gives a solid grey - image. A factor of 1.0 gives the original image. It - blends the source image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be contrasted. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - - Returns: - ndarray: The contrasted image. - """ - gray_img = bgr2gray(img) - hist = np.histogram(gray_img, 256, (0, 255))[0] - mean = round(np.sum(gray_img) / np.sum(hist)) - degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) - degenerated = gray2bgr(degenerated) - contrasted_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - contrasted_img = np.clip(contrasted_img, 0, 255) - return contrasted_img.astype(img.dtype) - - -def auto_contrast(img, cutoff=0): - """Auto adjust image contrast. - - This function maximize (normalize) image contrast by first removing cutoff - percent of the lightest and darkest pixels from the histogram and remapping - the image so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - Args: - img (ndarray): Image to be contrasted. BGR order. - cutoff (int | float | tuple): The cutoff percent of the lightest and - darkest pixels to be removed. If given as tuple, it shall be - (low, high). Otherwise, the single value will be used for both. - Defaults to 0. - - Returns: - ndarray: The contrasted image. - """ - - def _auto_contrast_channel(im, c, cutoff): - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # Remove cut-off percent pixels from histo - histo_sum = np.cumsum(histo) - cut_low = histo_sum[-1] * cutoff[0] // 100 - cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 - histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low - histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) - - # Compute mapping - low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] - # If all the values have been cut off, return the origin img - if low >= high: - return im - scale = 255.0 / (high - low) - offset = -low * scale - lut = np.array(range(256)) - lut = lut * scale + offset - lut = np.clip(lut, 0, 255) - return lut[im] - - if isinstance(cutoff, (int, float)): - cutoff = (cutoff, cutoff) - else: - assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ - f'float or tuple, but got {type(cutoff)} instead.' - # Auto adjusts contrast for each channel independently and then stacks - # the result. - s1 = _auto_contrast_channel(img, 0, cutoff) - s2 = _auto_contrast_channel(img, 1, cutoff) - s3 = _auto_contrast_channel(img, 2, cutoff) - contrasted_img = np.stack([s1, s2, s3], axis=-1) - return contrasted_img.astype(img.dtype) - - -def adjust_sharpness(img, factor=1., kernel=None): - """Adjust image sharpness. - - This function controls the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image. A - factor of 1.0 gives the original image. And a factor - of 2.0 gives a sharpened image. It blends the source - image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be sharpened. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - kernel (np.ndarray, optional): Filter kernel to be applied on the img - to obtain the degenerated img. Defaults to None. - - Note: - No value sanity check is enforced on the kernel set by users. So with - an inappropriate kernel, the ``adjust_sharpness`` may fail to perform - the function its name indicates but end up performing whatever - transform determined by the kernel. - - Returns: - ndarray: The sharpened image. - """ - - if kernel is None: - # adopted from PIL.ImageFilter.SMOOTH - kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 - assert isinstance(kernel, np.ndarray), \ - f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' - assert kernel.ndim == 2, \ - f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' - - degenerated = cv2.filter2D(img, -1, kernel) - sharpened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - sharpened_img = np.clip(sharpened_img, 0, 255) - return sharpened_img.astype(img.dtype) - - -def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): - """AlexNet-style PCA jitter. - - This data augmentation is proposed in `ImageNet Classification with Deep - Convolutional Neural Networks - `_. - - Args: - img (ndarray): Image to be adjusted lighting. BGR order. - eigval (ndarray): the eigenvalue of the convariance matrix of pixel - values, respectively. - eigvec (ndarray): the eigenvector of the convariance matrix of pixel - values, respectively. - alphastd (float): The standard deviation for distribution of alpha. - Defaults to 0.1 - to_rgb (bool): Whether to convert img to rgb. - - Returns: - ndarray: The adjusted image. - """ - assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ - f'eigval and eigvec should both be of type np.ndarray, got ' \ - f'{type(eigval)} and {type(eigvec)} instead.' - - assert eigval.ndim == 1 and eigvec.ndim == 2 - assert eigvec.shape == (3, eigval.shape[0]) - n_eigval = eigval.shape[0] - assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ - f'got {type(alphastd)} instead.' - - img = img.copy().astype(np.float32) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - - alpha = np.random.normal(0, alphastd, n_eigval) - alter = eigvec \ - * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ - * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) - alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) - img_adjusted = img + alter - return img_adjusted - - -def lut_transform(img, lut_table): - """Transform array by look-up table. - - The function lut_transform fills the output array with values from the - look-up table. Indices of the entries are taken from the input array. - - Args: - img (ndarray): Image to be transformed. - lut_table (ndarray): look-up table of 256 elements; in case of - multi-channel input array, the table should either have a single - channel (in this case the same table is used for all channels) or - the same number of channels as in the input array. - - Returns: - ndarray: The transformed image. - """ - assert isinstance(img, np.ndarray) - assert 0 <= np.min(img) and np.max(img) <= 255 - assert isinstance(lut_table, np.ndarray) - assert lut_table.shape == (256, ) - - return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) - - -def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - img (ndarray): Image to be processed. - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - - Returns: - ndarray: The processed image. - """ - assert isinstance(img, np.ndarray) - assert img.ndim == 2 - assert isinstance(clip_limit, (float, int)) - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - - clahe = cv2.createCLAHE(clip_limit, tile_grid_size) - return clahe.apply(np.array(img, dtype=np.uint8)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/deprecated.json b/cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/deprecated.json deleted file mode 100644 index 25cf6f28c..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/deprecated.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "resnet50_caffe": "detectron/resnet50_caffe", - "resnet50_caffe_bgr": "detectron2/resnet50_caffe_bgr", - "resnet101_caffe": "detectron/resnet101_caffe", - "resnet101_caffe_bgr": "detectron2/resnet101_caffe_bgr" -} diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/mmcls.json b/cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/mmcls.json deleted file mode 100644 index c073a41d0..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/mmcls.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "vgg11": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_batch256_imagenet_20210208-4271cd6c.pth", - "vgg13": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_batch256_imagenet_20210208-4d1d6080.pth", - "vgg16": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_batch256_imagenet_20210208-db26f1a5.pth", - "vgg19": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_batch256_imagenet_20210208-e6920e4a.pth", - "vgg11_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_bn_batch256_imagenet_20210207-f244902c.pth", - "vgg13_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_bn_batch256_imagenet_20210207-1a8b7864.pth", - "vgg16_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_bn_batch256_imagenet_20210208-7e55cd29.pth", - "vgg19_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_bn_batch256_imagenet_20210208-da620c4f.pth", - "resnet18": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet18_8xb32_in1k_20210831-fbbb1da6.pth", - "resnet34": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet34_8xb32_in1k_20210831-f257d4e6.pth", - "resnet50": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb32_in1k_20210831-ea4938fc.pth", - "resnet101": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet101_8xb32_in1k_20210831-539c63f8.pth", - "resnet152": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet152_8xb32_in1k_20210901-4d7582fa.pth", - "resnet50_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d50_b32x8_imagenet_20210531-db14775a.pth", - "resnet101_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d101_b32x8_imagenet_20210531-6e13bcd3.pth", - "resnet152_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d152_b32x8_imagenet_20210531-278cf22a.pth", - "resnext50_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext50_32x4d_b32x8_imagenet_20210429-56066e27.pth", - "resnext101_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x4d_b32x8_imagenet_20210506-e0fa3dd5.pth", - "resnext101_32x8d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x8d_b32x8_imagenet_20210506-23a247d5.pth", - "resnext152_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext152_32x4d_b32x8_imagenet_20210524-927787be.pth", - "se-resnet50": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet50_batch256_imagenet_20200804-ae206104.pth", - "se-resnet101": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet101_batch256_imagenet_20200804-ba5b51d4.pth", - "resnest50": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest50_imagenet_converted-1ebf0afe.pth", - "resnest101": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest101_imagenet_converted-032caa52.pth", - "resnest200": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest200_imagenet_converted-581a60f2.pth", - "resnest269": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest269_imagenet_converted-59930960.pth", - "shufflenet_v1": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v1/shufflenet_v1_batch1024_imagenet_20200804-5d6cec73.pth", - "shufflenet_v2": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v2/shufflenet_v2_batch1024_imagenet_20200812-5bf4721e.pth", - "mobilenet_v2": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v2/mobilenet_v2_batch256_imagenet_20200708-3b2dc3af.pth", - "mobilenet_v3_small": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_small-8427ecf0.pth", - "mobilenet_v3_large": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_large-3ea3c186.pth", - "repvgg_A0": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A0_3rdparty_4xb64-coslr-120e_in1k_20210909-883ab98c.pth", - "repvgg_A1": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A1_3rdparty_4xb64-coslr-120e_in1k_20210909-24003a24.pth", - "repvgg_A2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A2_3rdparty_4xb64-coslr-120e_in1k_20210909-97d7695a.pth", - "repvgg_B0": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B0_3rdparty_4xb64-coslr-120e_in1k_20210909-446375f4.pth", - "repvgg_B1": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1_3rdparty_4xb64-coslr-120e_in1k_20210909-750cdf67.pth", - "repvgg_B1g2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1g2_3rdparty_4xb64-coslr-120e_in1k_20210909-344f6422.pth", - "repvgg_B1g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1g4_3rdparty_4xb64-coslr-120e_in1k_20210909-d4c1a642.pth", - "repvgg_B2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B2_3rdparty_4xb64-coslr-120e_in1k_20210909-bd6b937c.pth", - "repvgg_B2g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B2g4_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-7b7955f0.pth", - "repvgg_B3": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B3_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-dda968bf.pth", - "repvgg_B3g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B3g4_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-4e54846a.pth", - "repvgg_D2se": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-D2se_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-cf3139b7.pth", - "res2net101_w26": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net101-w26-s4_3rdparty_8xb32_in1k_20210927-870b6c36.pth", - "res2net50_w14": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net50-w14-s8_3rdparty_8xb32_in1k_20210927-bc967bf1.pth", - "res2net50_w26": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net50-w26-s8_3rdparty_8xb32_in1k_20210927-f547a94b.pth", - "swin_tiny": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_tiny_224_b16x64_300e_imagenet_20210616_090925-66df6be6.pth", - "swin_small": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_small_224_b16x64_300e_imagenet_20210615_110219-7f9d988b.pth", - "swin_base": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/convert/swin_base_patch4_window7_224_22kto1k-f967f799.pth", - "swin_large": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/convert/swin_large_patch4_window7_224_22kto1k-5f0996db.pth", - "t2t_vit_t_14": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-14_3rdparty_8xb64_in1k_20210928-b7c09b62.pth", - "t2t_vit_t_19": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-19_3rdparty_8xb64_in1k_20210928-7f1478d5.pth", - "t2t_vit_t_24": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-24_3rdparty_8xb64_in1k_20210928-fe95a61b.pth", - "tnt_small": "https://download.openmmlab.com/mmclassification/v0/tnt/tnt-small-p16_3rdparty_in1k_20210903-c56ee7df.pth", - "vit_base_p16": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-98e8652b.pth", - "vit_base_p32": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p32_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-9cea8599.pth", - "vit_large_p16": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-large-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-b20ba619.pth" -} diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/open_mmlab.json b/cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/open_mmlab.json deleted file mode 100644 index 8311db4fe..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/open_mmlab.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "vgg16_caffe": "https://download.openmmlab.com/pretrain/third_party/vgg16_caffe-292e1171.pth", - "detectron/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_caffe-788b5fa3.pth", - "detectron2/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_msra-5891d200.pth", - "detectron/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_caffe-3ad79236.pth", - "detectron2/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_msra-6cc46731.pth", - "detectron2/resnext101_32x8d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x8d-1516f1aa.pth", - "resnext50_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext50-32x4d-0ab1a123.pth", - "resnext101_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d-a5af3160.pth", - "resnext101_64x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_64x4d-ee2c6f71.pth", - "contrib/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_thangvubk-ad1730dd.pth", - "detectron/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn-9186a21c.pth", - "detectron/resnet101_gn": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn-cac0ab98.pth", - "jhu/resnet50_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_ws-15beedd8.pth", - "jhu/resnet101_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn_ws-3e3c308c.pth", - "jhu/resnext50_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn_ws-0d87ac85.pth", - "jhu/resnext101_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn_ws-34ac1a9e.pth", - "jhu/resnext50_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn-c7e8b754.pth", - "jhu/resnext101_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn-ac3bb84e.pth", - "msra/hrnetv2_w18_small": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18_small-b5a04e21.pth", - "msra/hrnetv2_w18": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18-00eb2006.pth", - "msra/hrnetv2_w32": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w32-dc9eeb4f.pth", - "msra/hrnetv2_w40": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w40-ed0b031c.pth", - "msra/hrnetv2_w48": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w48-d2186c55.pth", - "bninception_caffe": "https://download.openmmlab.com/pretrain/third_party/bn_inception_caffe-ed2e8665.pth", - "kin400/i3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/i3d_r50_f32s2_k400-2c57e077.pth", - "kin400/nl3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/nl3d_r50_f32s2_k400-fa7e7caa.pth", - "res2net101_v1d_26w_4s": "https://download.openmmlab.com/pretrain/third_party/res2net101_v1d_26w_4s_mmdetv2-f0a600f9.pth", - "regnetx_400mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_400mf-a5b10d96.pth", - "regnetx_800mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_800mf-1f4be4c7.pth", - "regnetx_1.6gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_1.6gf-5791c176.pth", - "regnetx_3.2gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_3.2gf-c2599b0f.pth", - "regnetx_4.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_4.0gf-a88f671e.pth", - "regnetx_6.4gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_6.4gf-006af45d.pth", - "regnetx_8.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_8.0gf-3c68abe7.pth", - "regnetx_12gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_12gf-4c2a3350.pth", - "resnet18_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet18_v1c-b5776b93.pth", - "resnet50_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet50_v1c-2cccc1ad.pth", - "resnet101_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet101_v1c-e67eebb6.pth", - "mmedit/vgg16": "https://download.openmmlab.com/mmediting/third_party/vgg_state_dict.pth", - "mmedit/res34_en_nomixup": "https://download.openmmlab.com/mmediting/third_party/model_best_resnet34_En_nomixup.pth", - "mmedit/mobilenet_v2": "https://download.openmmlab.com/mmediting/third_party/mobilenet_v2.pth", - "contrib/mobilenet_v3_large": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_large-bc2c3fd3.pth", - "contrib/mobilenet_v3_small": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_small-47085aa1.pth", - "resnest50": "https://download.openmmlab.com/pretrain/third_party/resnest50_d2-7497a55b.pth", - "resnest101": "https://download.openmmlab.com/pretrain/third_party/resnest101_d2-f3b931b2.pth", - "resnest200": "https://download.openmmlab.com/pretrain/third_party/resnest200_d2-ca88e41f.pth", - "darknet53": "https://download.openmmlab.com/pretrain/third_party/darknet53-a628ea1b.pth", - "mmdet/mobilenet_v2": "https://download.openmmlab.com/mmdetection/v2.0/third_party/mobilenet_v2_batch256_imagenet-ff34753d.pth" -} diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/torchvision_0.12.json b/cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/torchvision_0.12.json deleted file mode 100644 index 06defe674..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/model_zoo/torchvision_0.12.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "alexnet": "https://download.pytorch.org/models/alexnet-owt-7be5be79.pth", - "densenet121": "https://download.pytorch.org/models/densenet121-a639ec97.pth", - "densenet169": "https://download.pytorch.org/models/densenet169-b2777c0a.pth", - "densenet201": "https://download.pytorch.org/models/densenet201-c1103571.pth", - "densenet161": "https://download.pytorch.org/models/densenet161-8d451a50.pth", - "efficientnet_b0": "https://download.pytorch.org/models/efficientnet_b0_rwightman-3dd342df.pth", - "efficientnet_b1": "https://download.pytorch.org/models/efficientnet_b1_rwightman-533bc792.pth", - "efficientnet_b2": "https://download.pytorch.org/models/efficientnet_b2_rwightman-bcdf34b7.pth", - "efficientnet_b3": "https://download.pytorch.org/models/efficientnet_b3_rwightman-cf984f9c.pth", - "efficientnet_b4": "https://download.pytorch.org/models/efficientnet_b4_rwightman-7eb33cd5.pth", - "efficientnet_b5": "https://download.pytorch.org/models/efficientnet_b5_lukemelas-b6417697.pth", - "efficientnet_b6": "https://download.pytorch.org/models/efficientnet_b6_lukemelas-c76e70fd.pth", - "efficientnet_b7": "https://download.pytorch.org/models/efficientnet_b7_lukemelas-dcc49843.pth", - "googlenet": "https://download.pytorch.org/models/googlenet-1378be20.pth", - "inception_v3_google": "https://download.pytorch.org/models/inception_v3_google-0cc3c7bd.pth", - "mobilenet_v2": "https://download.pytorch.org/models/mobilenet_v2-b0353104.pth", - "mobilenet_v3_large": "https://download.pytorch.org/models/mobilenet_v3_large-8738ca79.pth", - "mobilenet_v3_small": "https://download.pytorch.org/models/mobilenet_v3_small-047dcff4.pth", - "regnet_y_400mf": "https://download.pytorch.org/models/regnet_y_400mf-c65dace8.pth", - "regnet_y_800mf": "https://download.pytorch.org/models/regnet_y_800mf-1b27b58c.pth", - "regnet_y_1_6gf": "https://download.pytorch.org/models/regnet_y_1_6gf-b11a554e.pth", - "regnet_y_3_2gf": "https://download.pytorch.org/models/regnet_y_3_2gf-b5a9779c.pth", - "regnet_y_8gf": "https://download.pytorch.org/models/regnet_y_8gf-d0d0e4a8.pth", - "regnet_y_16gf": "https://download.pytorch.org/models/regnet_y_16gf-9e6ed7dd.pth", - "regnet_y_32gf": "https://download.pytorch.org/models/regnet_y_32gf-4dee3f7a.pth", - "regnet_x_400mf": "https://download.pytorch.org/models/regnet_x_400mf-adf1edd5.pth", - "regnet_x_800mf": "https://download.pytorch.org/models/regnet_x_800mf-ad17e45c.pth", - "regnet_x_1_6gf": "https://download.pytorch.org/models/regnet_x_1_6gf-e3633e7f.pth", - "regnet_x_3_2gf": "https://download.pytorch.org/models/regnet_x_3_2gf-f342aeae.pth", - "regnet_x_8gf": "https://download.pytorch.org/models/regnet_x_8gf-03ceed89.pth", - "regnet_x_16gf": "https://download.pytorch.org/models/regnet_x_16gf-2007eb11.pth", - "regnet_x_32gf": "https://download.pytorch.org/models/regnet_x_32gf-9d47f8d0.pth", - "resnet18": "https://download.pytorch.org/models/resnet18-f37072fd.pth", - "resnet34": "https://download.pytorch.org/models/resnet34-b627a593.pth", - "resnet50": "https://download.pytorch.org/models/resnet50-0676ba61.pth", - "resnet101": "https://download.pytorch.org/models/resnet101-63fe2227.pth", - "resnet152": "https://download.pytorch.org/models/resnet152-394f9c45.pth", - "resnext50_32x4d": "https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth", - "resnext101_32x8d": "https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth", - "wide_resnet50_2": "https://download.pytorch.org/models/wide_resnet50_2-95faca4d.pth", - "wide_resnet101_2": "https://download.pytorch.org/models/wide_resnet101_2-32ee1156.pth", - "shufflenetv2_x0.5": "https://download.pytorch.org/models/shufflenetv2_x0.5-f707e7126e.pth", - "shufflenetv2_x1.0": "https://download.pytorch.org/models/shufflenetv2_x1-5666bf0f80.pth", - "shufflenetv2_x1.5": null, - "shufflenetv2_x2.0": null, - "squeezenet1_0": "https://download.pytorch.org/models/squeezenet1_0-b66bff10.pth", - "squeezenet1_1": "https://download.pytorch.org/models/squeezenet1_1-b8a52dc0.pth", - "vgg11": "https://download.pytorch.org/models/vgg11-8a719046.pth", - "vgg13": "https://download.pytorch.org/models/vgg13-19584684.pth", - "vgg16": "https://download.pytorch.org/models/vgg16-397923af.pth", - "vgg19": "https://download.pytorch.org/models/vgg19-dcbb9e9d.pth", - "vgg11_bn": "https://download.pytorch.org/models/vgg11_bn-6002323d.pth", - "vgg13_bn": "https://download.pytorch.org/models/vgg13_bn-abd245e5.pth", - "vgg16_bn": "https://download.pytorch.org/models/vgg16_bn-6c64b313.pth", - "vgg19_bn": "https://download.pytorch.org/models/vgg19_bn-c79401a0.pth" -} diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/__init__.py deleted file mode 100644 index a12b79cef..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .deprecated_wrappers import Conv2d_deprecated as Conv2d -from .deprecated_wrappers import ConvTranspose2d_deprecated as ConvTranspose2d -from .deprecated_wrappers import Linear_deprecated as Linear -from .deprecated_wrappers import MaxPool2d_deprecated as MaxPool2d -from .focal_loss import (SigmoidFocalLoss, SoftmaxFocalLoss, - sigmoid_focal_loss, softmax_focal_loss) -from .sync_bn import SyncBatchNorm -from .cc_attention import CrissCrossAttention -from .point_sample import * -from .psa_mask import PSAMask, PSAMaskFunction -from .info import * \ No newline at end of file diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/cc_attention.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/cc_attention.py deleted file mode 100644 index 3fd83fcb9..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/cc_attention.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.cnn import PLUGIN_LAYERS, Scale - - -def NEG_INF_DIAG(n, device): - """Returns a diagonal matrix of size [n, n]. - - The diagonal are all "-inf". This is for avoiding calculating the - overlapped element in the Criss-Cross twice. - """ - return torch.diag(torch.tensor(float('-inf')).to(device).repeat(n), 0) - - -@PLUGIN_LAYERS.register_module() -class CrissCrossAttention(nn.Module): - """Criss-Cross Attention Module. - - .. note:: - Before v1.3.13, we use a CUDA op. Since v1.3.13, we switch - to a pure PyTorch and equivalent implementation. For more - details, please refer to https://github.com/open-mmlab/mmcv/pull/1201. - - Speed comparison for one forward pass - - - Input size: [2,512,97,97] - - Device: 1 NVIDIA GeForce RTX 2080 Ti - - +-----------------------+---------------+------------+---------------+ - | |PyTorch version|CUDA version|Relative speed | - +=======================+===============+============+===============+ - |with torch.no_grad() |0.00554402 s |0.0299619 s |5.4x | - +-----------------------+---------------+------------+---------------+ - |no with torch.no_grad()|0.00562803 s |0.0301349 s |5.4x | - +-----------------------+---------------+------------+---------------+ - - Args: - in_channels (int): Channels of the input feature map. - """ - - def __init__(self, in_channels): - super().__init__() - self.query_conv = nn.Conv2d(in_channels, in_channels // 8, 1) - self.key_conv = nn.Conv2d(in_channels, in_channels // 8, 1) - self.value_conv = nn.Conv2d(in_channels, in_channels, 1) - self.gamma = Scale(0.) - self.in_channels = in_channels - - def forward(self, x): - """forward function of Criss-Cross Attention. - - Args: - x (torch.Tensor): Input feature with the shape of - (batch_size, in_channels, height, width). - - Returns: - torch.Tensor: Output of the layer, with the shape of - (batch_size, in_channels, height, width) - """ - B, C, H, W = x.size() - query = self.query_conv(x) - key = self.key_conv(x) - value = self.value_conv(x) - energy_H = torch.einsum('bchw,bciw->bwhi', query, key) + NEG_INF_DIAG( - H, query.device) - energy_H = energy_H.transpose(1, 2) - energy_W = torch.einsum('bchw,bchj->bhwj', query, key) - attn = F.softmax( - torch.cat([energy_H, energy_W], dim=-1), dim=-1) # [B,H,W,(H+W)] - out = torch.einsum('bciw,bhwi->bchw', value, attn[..., :H]) - out += torch.einsum('bchj,bhwj->bchw', value, attn[..., H:]) - - out = self.gamma(out) + x - out = out.contiguous() - - return out - - def __repr__(self): - s = self.__class__.__name__ - s += f'(in_channels={self.in_channels})' - return s diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/README.md b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/README.md deleted file mode 100644 index 3bc020040..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/README.md +++ /dev/null @@ -1,170 +0,0 @@ -# Code Structure of CUDA operators - -This folder contains all non-python code for MMCV custom ops. Please follow the same architecture if you want to add new ops. - -## Directories Tree - -```folder -. -├── common -│ ├── box_iou_rotated_utils.hpp -│ ├── parrots_cpp_helper.hpp -│ ├── parrots_cuda_helper.hpp -│ ├── pytorch_cpp_helper.hpp -│ ├── pytorch_cuda_helper.hpp -│ ├── pytorch_device_registry.hpp -│   └── cuda -│   ├── common_cuda_helper.hpp -│   ├── parrots_cudawarpfunction.cuh -│   ├── ... -│   └── ops_cuda_kernel.cuh -├── onnxruntime -│   ├── onnxruntime_register.h -│   ├── onnxruntime_session_options_config_keys.h -│   ├── ort_mmcv_utils.h -│   ├── ... -│   ├── onnx_ops.h -│   └── cpu -│ ├── onnxruntime_register.cpp -│      ├── ... -│      └── onnx_ops_impl.cpp -├── parrots -│   ├── ... -│   ├── ops.cpp -│   ├── ops_parrots.cpp -│   └── ops_pytorch.h -├── pytorch -│   ├── info.cpp -│   ├── pybind.cpp -│   ├── ... -│   ├── ops.cpp -│   ├── cuda -│   │   ├── ... -│   │   └── ops_cuda.cu -│   └── cpu -│      ├── ... -│      └── ops.cpp -└── tensorrt - ├── trt_cuda_helper.cuh - ├── trt_plugin_helper.hpp - ├── trt_plugin.hpp - ├── trt_serialize.hpp - ├── ... - ├── trt_ops.hpp - └── plugins -    ├── trt_cuda_helper.cu -    ├── trt_plugin.cpp -    ├── ... -    ├── trt_ops.cpp -    └── trt_ops_kernel.cu -``` - -## Components - -- `common`: This directory contains all tools and shared codes. - - `cuda`: The cuda kernels which can be shared by all backends. **HIP** kernel is also here since they have similar syntax. -- `onnxruntime`: **ONNX Runtime** support for custom ops. - - `cpu`: CPU implementation of supported ops. -- `parrots`: **Parrots** is a deep learning frame for model training and inference. Parrots custom ops are placed in this directory. -- `pytorch`: **PyTorch** custom ops are supported by binding C++ to Python with **pybind11**. The ops implementation and binding codes are placed in this directory. - - `cuda`: This directory contains cuda kernel launchers, which feed memory pointers of tensor to the cuda kernel in `common/cuda`. The launchers provide c++ interface of cuda implementation of corresponding custom ops. - - `cpu`: This directory contain cpu implementations of corresponding custom ops. -- `tensorrt`: **TensorRT** support for custom ops. - - `plugins`: This directory contains the implementation of the supported custom ops. Some ops might also use shared cuda kernel in `common/cuda`. - -## How to add new PyTorch ops? - -1. (Optional) Add shared kernel in `common` to support special hardware platform. - - ```c++ - // src/common/cuda/new_ops_cuda_kernel.cuh - - template - __global__ void new_ops_forward_cuda_kernel(const T* input, T* output, ...) { - // forward here - } - - ``` - - Add cuda kernel launcher in `pytorch/cuda`. - - ```c++ - // src/pytorch/cuda - #include - - void NewOpsForwardCUDAKernelLauncher(Tensor input, Tensor output, ...){ - // initialize - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - ... - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "new_ops_forward_cuda_kernel", ([&] { - new_ops_forward_cuda_kernel - <<>>( - input.data_ptr(), output.data_ptr(),...); - })); - AT_CUDA_CHECK(cudaGetLastError()); - } - ``` - -2. Register implementation for different devices. - - ```c++ - // src/pytorch/cuda/cudabind.cpp - ... - - Tensor new_ops_forward_cuda(Tensor input, Tensor output, ...){ - // implement cuda forward here - // use `NewOpsForwardCUDAKernelLauncher` here - } - // declare interface here. - Tensor new_ops_forward_impl(Tensor input, Tensor output, ...); - // register the implementation for given device (CUDA here). - REGISTER_DEVICE_IMPL(new_ops_forward_impl, CUDA, new_ops_forward_cuda); - ``` - -3. Add ops implementation in `pytorch` directory. Select different implementations according to device type. - - ```c++ - // src/pytorch/new_ops.cpp - Tensor new_ops_forward_impl(Tensor input, Tensor output, ...){ - // dispatch the implementation according to the device type of input. - DISPATCH_DEVICE_IMPL(new_ops_forward_impl, input, output, ...); - } - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...){ - return new_ops_forward_impl(input, output, ...); - } - ``` - -4. Binding the implementation in `pytorch/pybind.cpp` - - ```c++ - // src/pytorch/pybind.cpp - - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...); - - ... - - // bind with pybind11 - m.def("new_ops_forward", &new_ops_forward, "new_ops_forward", - py::arg("input"), py::arg("output"), ...); - - ... - - ``` - -5. Build MMCV again. Enjoy new ops in python - - ```python - from ..utils import ext_loader - ext_module = ext_loader.load_ext('_ext', ['new_ops_forward']) - - ... - - ext_module.new_ops_forward(input, output, ...) - - ``` diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp deleted file mode 100644 index e18036bac..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef COMMON_CUDA_HELPER -#define COMMON_CUDA_HELPER - -#include - -#define CUDA_1D_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) - -#define CUDA_2D_KERNEL_LOOP(i, n, j, m) \ - for (size_t i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) \ - for (size_t j = blockIdx.y * blockDim.y + threadIdx.y; j < (m); \ - j += blockDim.y * gridDim.y) - -#define CUDA_2D_KERNEL_BLOCK_LOOP(i, n, j, m) \ - for (size_t i = blockIdx.x; i < (n); i += gridDim.x) \ - for (size_t j = blockIdx.y; j < (m); j += gridDim.y) - -#define THREADS_PER_BLOCK 512 - -inline int GET_BLOCKS(const int N, const int num_threads = THREADS_PER_BLOCK) { - int optimal_block_num = (N + num_threads - 1) / num_threads; - int max_block_num = 4096; - return std::min(optimal_block_num, max_block_num); -} - -template -__device__ T bilinear_interpolate(const T* input, const int height, - const int width, T y, T x, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) return 0; - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - int y_low = (int)y; - int x_low = (int)x; - int y_high; - int x_high; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - // do bilinear interpolation - T v1 = input[y_low * width + x_low]; - T v2 = input[y_low * width + x_high]; - T v3 = input[y_high * width + x_low]; - T v4 = input[y_high * width + x_high]; - T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - return val; -} - -template -__device__ void bilinear_interpolate_gradient( - const int height, const int width, T y, T x, T& w1, T& w2, T& w3, T& w4, - int& x_low, int& x_high, int& y_low, int& y_high, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - w1 = w2 = w3 = w4 = 0.; - x_low = x_high = y_low = y_high = -1; - return; - } - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - y_low = (int)y; - x_low = (int)x; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - - // reference in forward - // T v1 = input[y_low * width + x_low]; - // T v2 = input[y_low * width + x_high]; - // T v3 = input[y_high * width + x_low]; - // T v4 = input[y_high * width + x_high]; - // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - return; -} -#endif // COMMON_CUDA_HELPER diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh deleted file mode 100644 index 5d946686b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef PSAMASK_CUDA_KERNEL_CUH -#define PSAMASK_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -// CUDA: grid stride looping -#ifndef CUDA_KERNEL_LOOP -#define CUDA_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) -#endif - -template -__global__ void psamask_collect_forward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* mask_data, T* buffer_data) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - buffer_data[(n * h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)) * - h_feature * w_feature + - h * w_feature + w] = mask_data - [((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + h) * - w_feature + - w]; - } - } - } -} - -template -__global__ void psamask_distribute_forward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* mask_data, T* buffer_data) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - buffer_data[(n * h_feature * w_feature + h * w_feature + w) * - h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)] = mask_data - [((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + h) * - w_feature + - w]; - } - } - } -} - -template -__global__ void psamask_collect_backward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* buffer_diff, T* mask_diff) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - mask_diff[((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + - h) * - w_feature + - w] = buffer_diff[(n * h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)) * - h_feature * w_feature + - h * w_feature + w]; - } - } - } -} - -template -__global__ void psamask_distribute_backward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* buffer_diff, T* mask_diff) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - mask_diff[((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + - h) * - w_feature + - w] = - buffer_diff[(n * h_feature * w_feature + h * w_feature + w) * - h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)]; - } - } - } -} - -#endif // PSAMASK_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 1eb5f8fcc..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sigmoid_focal_loss_forward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* output, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + expf(-input[index])); - - // (1 - p)**gamma * log(p) - T term_p = pow(((T)1. - p), gamma) * log(max(p, (T)FLT_MIN)); - // p**gamma * log(1 - p) - T term_n = pow(p, gamma) * log(max((T)1. - p, (T)FLT_MIN)); - - output[index] = (T)0.; - output[index] += -flag_p * alpha * term_p; - output[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - output[index] *= weight[t]; - } - } -} - -template -__global__ void sigmoid_focal_loss_backward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* grad_input, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + exp(-input[index])); - - // (1 - p)**gamma * (1 - p - gamma*p*log(p)) - T term_p = pow(((T)1. - p), gamma) * - ((T)1. - p - (gamma * p * log(max(p, (T)FLT_MIN)))); - // p**gamma * (gamma * (1 - p) * log(1 - p) - p) - T term_n = pow(p, gamma) * - (gamma * ((T)1. - p) * log(max((T)1. - p, (T)FLT_MIN)) - p); - - grad_input[index] = (T)0.; - grad_input[index] += -flag_p * alpha * term_p; - grad_input[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - grad_input[index] *= weight[t]; - } - } -} - -#endif // SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 631b2c617..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void softmax_focal_loss_forward_cuda_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* output, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - output[index] = - -alpha * pow((T)1. - pred, gamma) * log(max(pred, (T)FLT_MIN)); - } else { - output[index] = 0; - } - if (weight != NULL) { - output[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda1_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* buff, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - buff[index] = alpha * (-pow((T)1. - pred, gamma) + - gamma * pow((T)1. - pred, gamma - 1) * pred * - log(max(pred, (T)FLT_MIN))); - } else { - buff[index] = 0; - } - if (weight != NULL) { - buff[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda2_kernel( - const int nthreads, const T* softmax, const int64_t* target, const T* buff, - T* grad_input, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - int64_t label = target[n]; - - if (label >= 0) { - T flag = (label == c ? (T)1. : (T)0.); - grad_input[index] = buff[n] * (flag - softmax[index]); - } else { - grad_input[index] = 0; - } - } -} - -#endif // SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh deleted file mode 100644 index 4ec6a4668..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SYNCBN_CUDA_KERNEL_CUH -#define SYNCBN_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sync_bn_forward_mean_cuda_kernel(const T *input, float *mean, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += input[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_mean_cuda_kernel(const phalf *input, - float *mean, int num, - int channels, int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += static_cast(input[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_var_cuda_kernel(const T *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = input[index] - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_var_cuda_kernel(const phalf *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = static_cast(input[index]) - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_output_cuda_kernel( - const T *input, const float *mean, const float *var, float *running_mean, - float *running_var, const float *weight, const float *bias, float *norm, - float *std, T *output, int num, int channels, int spatial, float eps, - float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = (input[index] - mean_value) / std_value; - output[index] = norm[index] * weight_value + bias_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - (input[index] - mean_value) / std_value * weight_value + bias_value; - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = norm[index] = (input[index] - mean_value) / std_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = (input[index] - mean_value) / std_value; - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template <> -__global__ void sync_bn_forward_output_cuda_kernel( - const phalf *input, const float *mean, const float *var, - float *running_mean, float *running_var, const float *weight, - const float *bias, float *norm, float *std, phalf *output, int num, - int channels, int spatial, float eps, float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = - static_cast(norm[index] * weight_value + bias_value); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - static_cast((static_cast(input[index]) - mean_value) / - std_value * weight_value + - bias_value); - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = static_cast(norm[index]); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = static_cast( - (static_cast(input[index]) - mean_value) / std_value); - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template -__global__ void sync_bn_backward_param_cuda_kernel(const T *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += grad_output[index] * norm[index]; - buffer2[tid] += grad_output[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template <> -__global__ void sync_bn_backward_param_cuda_kernel(const phalf *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += static_cast(grad_output[index]) * norm[index]; - buffer2[tid] += static_cast(grad_output[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const T *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, T *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = - weight[c] * - (grad_output[index] - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]; - } -} - -template <> -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const phalf *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, phalf *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = static_cast( - weight[c] * - (static_cast(grad_output[index]) - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]); - } -} - -#endif // SYNCBN_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp deleted file mode 100644 index f68e87405..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef PYTORCH_CPP_HELPER -#define PYTORCH_CPP_HELPER -#include - -#include - -using namespace at; - -#define CHECK_CUDA(x) \ - TORCH_CHECK(x.device().is_cuda(), #x " must be a CUDA tensor") -#define CHECK_MLU(x) \ - TORCH_CHECK(x.device().type() == at::kMLU, #x " must be a MLU tensor") -#define CHECK_CPU(x) \ - TORCH_CHECK(x.device().type() == at::kCPU, #x " must be a CPU tensor") -#define CHECK_CONTIGUOUS(x) \ - TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") -#define CHECK_CUDA_INPUT(x) \ - CHECK_CUDA(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_MLU_INPUT(x) \ - CHECK_MLU(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_CPU_INPUT(x) \ - CHECK_CPU(x); \ - CHECK_CONTIGUOUS(x) - -#endif // PYTORCH_CPP_HELPER diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp deleted file mode 100644 index 9869b535f..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PYTORCH_CUDA_HELPER -#define PYTORCH_CUDA_HELPER - -#include -#include -#include - -#include -#include - -#include "common_cuda_helper.hpp" - -using at::Half; -using at::Tensor; -using phalf = at::Half; - -#define __PHALF(x) (x) - -#endif // PYTORCH_CUDA_HELPER diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp deleted file mode 100644 index 2a32b7270..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef PYTORCH_DEVICE_REGISTRY_H -#define PYTORCH_DEVICE_REGISTRY_H - -// Using is recommended in the official documentation in -// https://pytorch.org/tutorials/advanced/cpp_extension.html#writing-the-c-op. -// However, we use for compatibility with CUDA 9.0 -// Read https://github.com/pytorch/extension-cpp/issues/35 for more details. -#include - -#include -#include -#include -#include - -inline std::string GetDeviceStr(const at::Device& device) { - std::string str = DeviceTypeName(device.type(), true); - if (device.has_index()) { - str.push_back(':'); - str.append(std::to_string(device.index())); - } - return str; -} - -// Registry -template -class DeviceRegistry; - -template -class DeviceRegistry { - public: - using FunctionType = Ret (*)(Args...); - static const int MAX_DEVICE_TYPES = - int8_t(at::DeviceType::COMPILE_TIME_MAX_DEVICE_TYPES); - - void Register(at::DeviceType device, FunctionType function) { - funcs_[int8_t(device)] = function; - } - - FunctionType Find(at::DeviceType device) const { - return funcs_[int8_t(device)]; - } - - static DeviceRegistry& instance() { - static DeviceRegistry inst; - return inst; - } - - private: - DeviceRegistry() { - for (size_t i = 0; i < MAX_DEVICE_TYPES; ++i) { - funcs_[i] = nullptr; - } - }; - FunctionType funcs_[MAX_DEVICE_TYPES]; -}; - -// get device of first tensor param - -template , at::Tensor>::value, - bool> = true> -at::Device GetFirstTensorDevice(T&& t, Args&&... args) { - return std::forward(t).device(); -} -template , at::Tensor>::value, - bool> = true> -at::Device GetFirstTensorDevice(T&& t, Args&&... args) { - return GetFirstTensorDevice(std::forward(args)...); -} - -// check device consistency - -inline std::pair CheckDeviceConsistency( - const at::Device& device, int index) { - return {index, device}; -} - -template , at::Tensor>::value, - bool> = true> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args); - -template , at::Tensor>::value, - bool> = true> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args) { - auto new_device = std::forward(t).device(); - if (new_device.type() != device.type() || - new_device.index() != device.index()) { - return {index, new_device}; - } - return CheckDeviceConsistency(device, index + 1, std::forward(args)...); -} - -template < - typename T, typename... Args, - std::enable_if_t, at::Tensor>::value, bool>> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args) { - return CheckDeviceConsistency(device, index + 1, std::forward(args)...); -} - -// dispatch - -template -auto Dispatch(const R& registry, const char* name, Args&&... args) { - auto device = GetFirstTensorDevice(std::forward(args)...); - auto inconsist = - CheckDeviceConsistency(device, 0, std::forward(args)...); - TORCH_CHECK(inconsist.first >= int(sizeof...(Args)), name, ": at param ", - inconsist.first, - ", inconsistent device: ", GetDeviceStr(inconsist.second).c_str(), - " vs ", GetDeviceStr(device).c_str(), "\n") - auto f_ptr = registry.Find(device.type()); - TORCH_CHECK(f_ptr != nullptr, name, ": implementation for device ", - GetDeviceStr(device).c_str(), " not found.\n") - return f_ptr(std::forward(args)...); -} - -// helper macro - -#define DEVICE_REGISTRY(key) DeviceRegistry::instance() - -#define REGISTER_DEVICE_IMPL(key, device, value) \ - struct key##_##device##_registerer { \ - key##_##device##_registerer() { \ - DEVICE_REGISTRY(key).Register(at::k##device, value); \ - } \ - }; \ - static key##_##device##_registerer _##key##_##device##_registerer; - -#define DISPATCH_DEVICE_IMPL(key, ...) \ - Dispatch(DEVICE_REGISTRY(key), #key, __VA_ARGS__) - -#endif // PYTORCH_DEVICE_REGISTRY diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp deleted file mode 100644 index dc376bb51..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp +++ /dev/null @@ -1,210 +0,0 @@ -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha); - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha); - -void sigmoid_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SigmoidFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void sigmoid_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - SigmoidFocalLossBackwardCUDAKernelLauncher(input, target, weight, grad_input, - gamma, alpha); -} - -void softmax_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SoftmaxFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void softmax_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - SoftmaxFocalLossBackwardCUDAKernelLauncher(input, target, weight, buff, - grad_input, gamma, alpha); -} - -void sigmoid_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha); - -void softmax_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha); - -REGISTER_DEVICE_IMPL(sigmoid_focal_loss_forward_impl, CUDA, - sigmoid_focal_loss_forward_cuda); -REGISTER_DEVICE_IMPL(sigmoid_focal_loss_backward_impl, CUDA, - sigmoid_focal_loss_backward_cuda); -REGISTER_DEVICE_IMPL(softmax_focal_loss_forward_impl, CUDA, - softmax_focal_loss_forward_cuda); -REGISTER_DEVICE_IMPL(softmax_focal_loss_backward_impl, CUDA, - softmax_focal_loss_backward_cuda); - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean); - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var); - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size); - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias); - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -void sync_bn_forward_mean_cuda(const Tensor input, Tensor mean) { - SyncBNForwardMeanCUDAKernelLauncher(input, mean); -} - -void sync_bn_forward_var_cuda(const Tensor input, const Tensor mean, - Tensor var) { - SyncBNForwardVarCUDAKernelLauncher(input, mean, var); -} - -void sync_bn_forward_output_cuda(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - SyncBNForwardOutputCUDAKernelLauncher(input, mean, var, running_mean, - running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_cuda(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - SyncBNBackwardParamCUDAKernelLauncher(grad_output, norm, grad_weight, - grad_bias); -} - -void sync_bn_backward_data_cuda(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - SyncBNBackwardDataCUDAKernelLauncher(grad_output, weight, grad_weight, - grad_bias, norm, std, grad_input); -} - -void sync_bn_forward_mean_impl(const Tensor input, Tensor mean); - -void sync_bn_forward_var_impl(const Tensor input, const Tensor mean, - Tensor var); - -void sync_bn_forward_output_impl(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param_impl(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data_impl(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input); - -REGISTER_DEVICE_IMPL(sync_bn_forward_mean_impl, CUDA, - sync_bn_forward_mean_cuda); -REGISTER_DEVICE_IMPL(sync_bn_forward_var_impl, CUDA, sync_bn_forward_var_cuda); -REGISTER_DEVICE_IMPL(sync_bn_forward_output_impl, CUDA, - sync_bn_forward_output_cuda); -REGISTER_DEVICE_IMPL(sync_bn_backward_param_impl, CUDA, - sync_bn_backward_param_cuda); -REGISTER_DEVICE_IMPL(sync_bn_backward_data_impl, CUDA, - sync_bn_backward_data_cuda); - - - -void PSAMaskForwardCUDAKernelLauncher(const int psa_type, const Tensor input, - Tensor output, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, - const int half_w_mask); - -void PSAMaskBackwardCUDAKernelLauncher( - const int psa_type, const Tensor grad_output, Tensor grad_input, - const int num_, const int h_feature, const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, const int half_w_mask); - -void psamask_forward_cuda(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask) { - PSAMaskForwardCUDAKernelLauncher(psa_type, input, output, num_, h_feature, - w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_backward_cuda(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - PSAMaskBackwardCUDAKernelLauncher(psa_type, grad_output, grad_input, num_, - h_feature, w_feature, h_mask, w_mask, - half_h_mask, half_w_mask); -} - -void psamask_forward_impl(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask); - -void psamask_backward_impl(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask); -REGISTER_DEVICE_IMPL(psamask_forward_impl, CUDA, psamask_forward_cuda); -REGISTER_DEVICE_IMPL(psamask_backward_impl, CUDA, psamask_backward_cuda); diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu deleted file mode 100644 index cb899f954..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sigmoid_focal_loss_cuda_kernel.cuh" -#include "softmax_focal_loss_cuda_kernel.cuh" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = input.size(1); - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_forward_cuda_kernel", [&] { - sigmoid_focal_loss_forward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha) { - int output_size = grad_input.numel(); - int num_classes = input.size(1); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_backward_cuda_kernel", [&] { - sigmoid_focal_loss_backward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - grad_input.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = softmax.size(1); - - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(softmax.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - softmax.scalar_type(), "softmax_focal_loss_forward_cuda_kernel", [&] { - softmax_focal_loss_forward_cuda_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha) { - int num_classes = softmax.size(1); - - int output_size = buff.numel(); - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda1_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda1_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - buff.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); - - output_size = grad_input.numel(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda2_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda2_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), buff.data_ptr(), - grad_input.data_ptr(), num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu deleted file mode 100644 index a0bdfa60c..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// Modified from -// https://github.com/hszhao/semseg/blob/master/lib/psa/src - -#include - -#include "psamask_cuda_kernel.cuh" -#include "pytorch_cuda_helper.hpp" - -void PSAMaskForwardCUDAKernelLauncher(const int psa_type, const Tensor input, - Tensor output, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, - const int half_w_mask) { - int nthreads = num_ * h_feature * w_feature; - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - if (psa_type == 0) - AT_DISPATCH_FLOATING_TYPES( - input.scalar_type(), "psamask_collect_forward_cuda", [&] { - psamask_collect_forward_cuda<<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, input.data_ptr(), - output.data_ptr()); - }); - else - AT_DISPATCH_FLOATING_TYPES( - input.scalar_type(), "psamask_distribute_forward_cuda", [&] { - psamask_distribute_forward_cuda - <<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, input.data_ptr(), - output.data_ptr()); - }); -} - -void PSAMaskBackwardCUDAKernelLauncher( - const int psa_type, const Tensor grad_output, Tensor grad_input, - const int num_, const int h_feature, const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, const int half_w_mask) { - int nthreads = num_ * h_feature * w_feature; - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - if (psa_type == 0) - AT_DISPATCH_FLOATING_TYPES( - grad_input.scalar_type(), "psamask_collect_backward_cuda", [&] { - psamask_collect_backward_cuda<<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, grad_output.data_ptr(), - grad_input.data_ptr()); - }); - else - AT_DISPATCH_FLOATING_TYPES( - grad_input.scalar_type(), "psamask_distribute_backward_cuda", [&] { - psamask_distribute_backward_cuda - <<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, grad_output.data_ptr(), - grad_input.data_ptr()); - }); -} diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu deleted file mode 100644 index 657c81701..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sync_bn_cuda_kernel.cuh" - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_mean_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_var_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), num, channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_output_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), running_mean.data_ptr(), - running_var.data_ptr(), weight.data_ptr(), - bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), output.data_ptr(), num, - channels, spatial, eps, momentum, group_size); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias) { - int num = grad_output.size(0); - int channels = grad_output.size(1); - int spatial = grad_output.size(2); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_param_cuda_kernel", [&] { - sync_bn_backward_param_cuda_kernel - <<>>( - grad_output.data_ptr(), norm.data_ptr(), - grad_weight.data_ptr(), grad_bias.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - int output_size = grad_input.numel(); - int num = grad_input.size(0); - int channels = grad_input.size(1); - int spatial = grad_input.size(2); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_data_cuda_kernel", [&] { - sync_bn_backward_data_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - weight.data_ptr(), grad_weight.data_ptr(), - grad_bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), grad_input.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp deleted file mode 100644 index ed0e21865..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void sigmoid_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(sigmoid_focal_loss_forward_impl, input, target, weight, - output, gamma, alpha); -} - -void sigmoid_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(sigmoid_focal_loss_backward_impl, input, target, weight, - grad_input, gamma, alpha); -} - -void softmax_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(softmax_focal_loss_forward_impl, input, target, weight, - output, gamma, alpha); -} - -void softmax_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - DISPATCH_DEVICE_IMPL(softmax_focal_loss_backward_impl, input, target, weight, - buff, grad_input, gamma, alpha); -} - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - sigmoid_focal_loss_forward_impl(input, target, weight, output, gamma, alpha); -} - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha) { - sigmoid_focal_loss_backward_impl(input, target, weight, grad_input, gamma, - alpha); -} - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - softmax_focal_loss_forward_impl(input, target, weight, output, gamma, alpha); -} - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha) { - softmax_focal_loss_backward_impl(input, target, weight, buff, grad_input, - gamma, alpha); -} diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/info.cpp b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/info.cpp deleted file mode 100644 index a08d227d4..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/info.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// modified from -// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/vision.cpp -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF -#include -int get_cudart_version() { return CUDART_VERSION; } -#endif -#endif - -std::string get_compiling_cuda_version() { -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF - std::ostringstream oss; - // copied from - // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/cuda/detail/CUDAHooks.cpp#L231 - auto printCudaStyleVersion = [&](int v) { - oss << (v / 1000) << "." << (v / 10 % 100); - if (v % 10 != 0) { - oss << "." << (v % 10); - } - }; - printCudaStyleVersion(get_cudart_version()); - return oss.str(); -#else - return std::string("rocm not available"); -#endif -#else - return std::string("not available"); -#endif -} - -// similar to -// https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/Version.cpp -std::string get_compiler_version() { - std::ostringstream ss; -#if defined(__GNUC__) -#ifndef __clang__ - { ss << "GCC " << __GNUC__ << "." << __GNUC_MINOR__; } -#endif -#endif - -#if defined(__clang_major__) - { - ss << "clang " << __clang_major__ << "." << __clang_minor__ << "." - << __clang_patchlevel__; - } -#endif - -#if defined(_MSC_VER) - { ss << "MSVC " << _MSC_FULL_VER; } -#endif - return ss.str(); -} diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp deleted file mode 100644 index 6064c9ba5..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// Modified from -// https://github.com/hszhao/semseg/blob/master/lib/psa/src -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void psamask_forward_impl(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask) { - DISPATCH_DEVICE_IMPL(psamask_forward_impl, psa_type, input, output, num_, - h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_backward_impl(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - DISPATCH_DEVICE_IMPL(psamask_backward_impl, psa_type, grad_output, grad_input, - num_, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_forward(const Tensor input, Tensor output, const int psa_type, - const int num_, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask) { - psamask_forward_impl(psa_type, input, output, num_, h_feature, w_feature, - h_mask, w_mask, half_h_mask, half_w_mask); -} - -void psamask_backward(Tensor grad_output, const Tensor grad_input, - const int psa_type, const int num_, const int h_feature, - const int w_feature, const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - psamask_backward_impl(psa_type, grad_output, grad_input, num_, h_feature, - w_feature, h_mask, w_mask, half_h_mask, half_w_mask); -} diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp deleted file mode 100644 index 1e99174d8..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include - -#include "pytorch_cpp_helper.hpp" - -std::string get_compiler_version(); -std::string get_compiling_cuda_version(); - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha); - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha); - -void sync_bn_forward_mean(const Tensor input, Tensor mean); - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var); - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - - -void psamask_forward(const Tensor input, Tensor output, const int psa_type, - const int num_, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask); - -void psamask_backward(Tensor grad_output, const Tensor grad_input, - const int psa_type, const int num_, const int h_feature, - const int w_feature, const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask); - - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("get_compiler_version", &get_compiler_version, "get_compiler_version"); - m.def("get_compiling_cuda_version", &get_compiling_cuda_version, - "get_compiling_cuda_version"); - - - m.def("sigmoid_focal_loss_forward", &sigmoid_focal_loss_forward, - "sigmoid_focal_loss_forward ", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("sigmoid_focal_loss_backward", &sigmoid_focal_loss_backward, - "sigmoid_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("grad_input"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_forward", &softmax_focal_loss_forward, - "softmax_focal_loss_forward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_backward", &softmax_focal_loss_backward, - "softmax_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("buff"), py::arg("grad_input"), - py::arg("gamma"), py::arg("alpha")); - - // SyncBN - m.def("sync_bn_forward_mean", &sync_bn_forward_mean, "sync_bn forward_mean", - py::arg("input"), py::arg("mean")); - m.def("sync_bn_forward_var", &sync_bn_forward_var, "sync_bn forward_var", - py::arg("input"), py::arg("mean"), py::arg("var")); - m.def("sync_bn_forward_output", &sync_bn_forward_output, - "sync_bn forward_output", py::arg("input"), py::arg("mean"), - py::arg("var"), py::arg("weight"), py::arg("bias"), - py::arg("running_mean"), py::arg("running_var"), py::arg("norm"), - py::arg("std"), py::arg("output"), py::arg("eps"), py::arg("momentum"), - py::arg("group_size")); - m.def("sync_bn_backward_param", &sync_bn_backward_param, - "sync_bn backward_param", py::arg("grad_output"), py::arg("norm"), - py::arg("grad_weight"), py::arg("grad_bias")); - m.def("sync_bn_backward_data", &sync_bn_backward_data, - "sync_bn backward_data", py::arg("grad_output"), py::arg("weight"), - py::arg("grad_weight"), py::arg("grad_bias"), py::arg("norm"), - py::arg("std"), py::arg("grad_input")); - - // PASMask - m.def("psamask_forward", &psamask_forward, "PSAMASK forward (CPU/CUDA)", - py::arg("input"), py::arg("output"), py::arg("psa_type"), - py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), - py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), - py::arg("half_w_mask")); - m.def("psamask_backward", &psamask_backward, "PSAMASK backward (CPU/CUDA)", - py::arg("grad_output"), py::arg("grad_input"), py::arg("psa_type"), - py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), - py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), - py::arg("half_w_mask")); -} diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp deleted file mode 100644 index fd5a51327..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void sync_bn_forward_mean_impl(const Tensor input, Tensor mean) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_mean_impl, input, mean); -} - -void sync_bn_forward_var_impl(const Tensor input, const Tensor mean, - Tensor var) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_var_impl, input, mean, var); -} - -void sync_bn_forward_output_impl(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_output_impl, input, mean, var, - running_mean, running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_impl(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - DISPATCH_DEVICE_IMPL(sync_bn_backward_param_impl, grad_output, norm, - grad_weight, grad_bias); -} - -void sync_bn_backward_data_impl(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - DISPATCH_DEVICE_IMPL(sync_bn_backward_data_impl, grad_output, weight, - grad_weight, grad_bias, norm, std, grad_input); -} - -void sync_bn_forward_mean(const Tensor input, Tensor mean) { - sync_bn_forward_mean_impl(input, mean); -} - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var) { - sync_bn_forward_var_impl(input, mean, var); -} - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - sync_bn_forward_output_impl(input, mean, var, running_mean, running_var, - weight, bias, norm, std, output, eps, momentum, - group_size); -} - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - sync_bn_backward_param_impl(grad_output, norm, grad_weight, grad_bias); -} - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - sync_bn_backward_data_impl(grad_output, weight, grad_weight, grad_bias, norm, - std, grad_input); -} diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/deprecated_wrappers.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/deprecated_wrappers.py deleted file mode 100644 index 629a8033f..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/deprecated_wrappers.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# This file is for backward compatibility. -# Module wrappers for empty tensor have been moved to mmcv.cnn.bricks. -import warnings - -from ..cnn.bricks.wrappers import Conv2d, ConvTranspose2d, Linear, MaxPool2d - - -class Conv2d_deprecated(Conv2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Conv2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) - - -class ConvTranspose2d_deprecated(ConvTranspose2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing ConvTranspose2d wrapper from "mmcv.ops" will be ' - 'deprecated in the future. Please import them from "mmcv.cnn" ' - 'instead', DeprecationWarning) - - -class MaxPool2d_deprecated(MaxPool2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing MaxPool2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) - - -class Linear_deprecated(Linear): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Linear wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/focal_loss.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/focal_loss.py deleted file mode 100644 index 805860516..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/focal_loss.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sigmoid_focal_loss_forward', 'sigmoid_focal_loss_backward', - 'softmax_focal_loss_forward', 'softmax_focal_loss_backward' -]) - - -class SigmoidFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSigmoidFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance( - target, (torch.Tensor, torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - output = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_forward( - input, target, weight, output, gamma=ctx.gamma, alpha=ctx.alpha) - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input, target, weight) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - input, target, weight = ctx.saved_tensors - - grad_input = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_backward( - input, - target, - weight, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input.size(0) - return grad_input, None, None, None, None, None - - -sigmoid_focal_loss = SigmoidFocalLossFunction.apply - - -class SigmoidFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SigmoidFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return sigmoid_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s - - -class SoftmaxFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSoftmaxFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - channel_stats, _ = torch.max(input, dim=1) - input_softmax = input - channel_stats.unsqueeze(1).expand_as(input) - input_softmax.exp_() - - channel_stats = input_softmax.sum(dim=1) - input_softmax /= channel_stats.unsqueeze(1).expand_as(input) - - output = input.new_zeros(input.size(0)) - ext_module.softmax_focal_loss_forward( - input_softmax, - target, - weight, - output, - gamma=ctx.gamma, - alpha=ctx.alpha) - - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input_softmax, target, weight) - return output - - @staticmethod - def backward(ctx, grad_output): - input_softmax, target, weight = ctx.saved_tensors - buff = input_softmax.new_zeros(input_softmax.size(0)) - grad_input = input_softmax.new_zeros(input_softmax.size()) - - ext_module.softmax_focal_loss_backward( - input_softmax, - target, - weight, - buff, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input_softmax.size(0) - return grad_input, None, None, None, None, None - - -softmax_focal_loss = SoftmaxFocalLossFunction.apply - - -class SoftmaxFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SoftmaxFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return softmax_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/info.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/info.py deleted file mode 100644 index 29f2e5598..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/info.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os - -import torch - -if torch.__version__ == 'parrots': - import parrots - - def get_compiler_version(): - return 'GCC ' + parrots.version.compiler - - def get_compiling_cuda_version(): - return parrots.version.cuda -else: - from ..utils import ext_loader - ext_module = ext_loader.load_ext( - '_ext', ['get_compiler_version', 'get_compiling_cuda_version']) - - def get_compiler_version(): - return ext_module.get_compiler_version() - - def get_compiling_cuda_version(): - return ext_module.get_compiling_cuda_version() - - -def get_onnxruntime_op_path(): - wildcard = os.path.join( - os.path.abspath(os.path.dirname(os.path.dirname(__file__))), - '_ext_ort.*.so') - - paths = glob.glob(wildcard) - if len(paths) > 0: - return paths[0] - else: - return '' diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/point_sample.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/point_sample.py deleted file mode 100644 index 9a70b28e2..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/point_sample.py +++ /dev/null @@ -1,346 +0,0 @@ -# Modified from https://github.com/facebookresearch/detectron2/tree/master/projects/PointRend # noqa - -from os import path as osp - -import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.nn.modules.utils import _pair -from torch.onnx.operators import shape_as_tensor - - -def bilinear_grid_sample(im, grid, align_corners=False): - """Given an input and a flow-field grid, computes the output using input - values and pixel locations from grid. Supported only bilinear interpolation - method to sample the input pixels. - - Args: - im (torch.Tensor): Input feature map, shape (N, C, H, W) - grid (torch.Tensor): Point coordinates, shape (N, Hg, Wg, 2) - align_corners {bool}: If set to True, the extrema (-1 and 1) are - considered as referring to the center points of the input’s - corner pixels. If set to False, they are instead considered as - referring to the corner points of the input’s corner pixels, - making the sampling more resolution agnostic. - - Returns: - torch.Tensor: A tensor with sampled points, shape (N, C, Hg, Wg) - """ - n, c, h, w = im.shape - gn, gh, gw, _ = grid.shape - assert n == gn - - x = grid[:, :, :, 0] - y = grid[:, :, :, 1] - - if align_corners: - x = ((x + 1) / 2) * (w - 1) - y = ((y + 1) / 2) * (h - 1) - else: - x = ((x + 1) * w - 1) / 2 - y = ((y + 1) * h - 1) / 2 - - x = x.view(n, -1) - y = y.view(n, -1) - - x0 = torch.floor(x).long() - y0 = torch.floor(y).long() - x1 = x0 + 1 - y1 = y0 + 1 - - wa = ((x1 - x) * (y1 - y)).unsqueeze(1) - wb = ((x1 - x) * (y - y0)).unsqueeze(1) - wc = ((x - x0) * (y1 - y)).unsqueeze(1) - wd = ((x - x0) * (y - y0)).unsqueeze(1) - - # Apply default for grid_sample function zero padding - im_padded = F.pad(im, pad=[1, 1, 1, 1], mode='constant', value=0) - padded_h = h + 2 - padded_w = w + 2 - # save points positions after padding - x0, x1, y0, y1 = x0 + 1, x1 + 1, y0 + 1, y1 + 1 - - # Clip coordinates to padded image size - x0 = torch.where(x0 < 0, torch.tensor(0), x0) - x0 = torch.where(x0 > padded_w - 1, torch.tensor(padded_w - 1), x0) - x1 = torch.where(x1 < 0, torch.tensor(0), x1) - x1 = torch.where(x1 > padded_w - 1, torch.tensor(padded_w - 1), x1) - y0 = torch.where(y0 < 0, torch.tensor(0), y0) - y0 = torch.where(y0 > padded_h - 1, torch.tensor(padded_h - 1), y0) - y1 = torch.where(y1 < 0, torch.tensor(0), y1) - y1 = torch.where(y1 > padded_h - 1, torch.tensor(padded_h - 1), y1) - - im_padded = im_padded.view(n, c, -1) - - x0_y0 = (x0 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x0_y1 = (x0 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y0 = (x1 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y1 = (x1 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - - Ia = torch.gather(im_padded, 2, x0_y0) - Ib = torch.gather(im_padded, 2, x0_y1) - Ic = torch.gather(im_padded, 2, x1_y0) - Id = torch.gather(im_padded, 2, x1_y1) - - return (Ia * wa + Ib * wb + Ic * wc + Id * wd).reshape(n, c, gh, gw) - - -def is_in_onnx_export_without_custom_ops(): - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - return torch.onnx.is_in_onnx_export( - ) and not osp.exists(ort_custom_op_path) - - -def normalize(grid): - """Normalize input grid from [-1, 1] to [0, 1] - - Args: - grid (torch.Tensor): The grid to be normalize, range [-1, 1]. - - Returns: - torch.Tensor: Normalized grid, range [0, 1]. - """ - - return (grid + 1.0) / 2.0 - - -def denormalize(grid): - """Denormalize input grid from range [0, 1] to [-1, 1] - - Args: - grid (torch.Tensor): The grid to be denormalize, range [0, 1]. - - Returns: - torch.Tensor: Denormalized grid, range [-1, 1]. - """ - - return grid * 2.0 - 1.0 - - -def generate_grid(num_grid, size, device): - """Generate regular square grid of points in [0, 1] x [0, 1] coordinate - space. - - Args: - num_grid (int): The number of grids to sample, one for each region. - size (tuple[int, int]): The side size of the regular grid. - device (torch.device): Desired device of returned tensor. - - Returns: - torch.Tensor: A tensor of shape (num_grid, size[0]*size[1], 2) that - contains coordinates for the regular grids. - """ - - affine_trans = torch.tensor([[[1., 0., 0.], [0., 1., 0.]]], device=device) - grid = F.affine_grid( - affine_trans, torch.Size((1, 1, *size)), align_corners=False) - grid = normalize(grid) - return grid.view(1, -1, 2).expand(num_grid, -1, -1) - - -def rel_roi_point_to_abs_img_point(rois, rel_roi_points): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (torch.Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (torch.Tensor): Point coordinates inside RoI, relative - to RoI, location, range (0, 1), shape (N, P, 2) - Returns: - torch.Tensor: Image based absolute point coordinates, shape (N, P, 2) - """ - - with torch.no_grad(): - assert rel_roi_points.size(0) == rois.size(0) - assert rois.dim() == 2 - assert rel_roi_points.dim() == 3 - assert rel_roi_points.size(2) == 2 - # remove batch idx - if rois.size(1) == 5: - rois = rois[:, 1:] - abs_img_points = rel_roi_points.clone() - # To avoid an error during exporting to onnx use independent - # variables instead inplace computation - xs = abs_img_points[:, :, 0] * (rois[:, None, 2] - rois[:, None, 0]) - ys = abs_img_points[:, :, 1] * (rois[:, None, 3] - rois[:, None, 1]) - xs += rois[:, None, 0] - ys += rois[:, None, 1] - abs_img_points = torch.stack([xs, ys], dim=2) - return abs_img_points - - -def get_shape_from_feature_map(x): - """Get spatial resolution of input feature map considering exporting to - onnx mode. - - Args: - x (torch.Tensor): Input tensor, shape (N, C, H, W) - - Returns: - torch.Tensor: Spatial resolution (width, height), shape (1, 1, 2) - """ - if torch.onnx.is_in_onnx_export(): - img_shape = shape_as_tensor(x)[2:].flip(0).view(1, 1, 2).to( - x.device).float() - else: - img_shape = torch.tensor(x.shape[2:]).flip(0).view(1, 1, 2).to( - x.device).float() - return img_shape - - -def abs_img_point_to_rel_img_point(abs_img_points, img, spatial_scale=1.): - """Convert image based absolute point coordinates to image based relative - coordinates for sampling. - - Args: - abs_img_points (torch.Tensor): Image based absolute point coordinates, - shape (N, P, 2) - img (tuple or torch.Tensor): (height, width) of image or feature map. - spatial_scale (float, optional): Scale points by this factor. - Default: 1. - - Returns: - Tensor: Image based relative point coordinates for sampling, shape - (N, P, 2). - """ - - assert (isinstance(img, tuple) and len(img) == 2) or \ - (isinstance(img, torch.Tensor) and len(img.shape) == 4) - - if isinstance(img, tuple): - h, w = img - scale = torch.tensor([w, h], - dtype=torch.float, - device=abs_img_points.device) - scale = scale.view(1, 1, 2) - else: - scale = get_shape_from_feature_map(img) - - return abs_img_points / scale * spatial_scale - - -def rel_roi_point_to_rel_img_point(rois, - rel_roi_points, - img, - spatial_scale=1.): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (torch.Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (torch.Tensor): Point coordinates inside RoI, relative - to RoI, location, range (0, 1), shape (N, P, 2) - img (tuple or torch.Tensor): (height, width) of image or feature map. - spatial_scale (float, optional): Scale points by this factor. - Default: 1. - - Returns: - torch.Tensor: Image based relative point coordinates for sampling, - shape (N, P, 2). - """ - - abs_img_point = rel_roi_point_to_abs_img_point(rois, rel_roi_points) - rel_img_point = abs_img_point_to_rel_img_point(abs_img_point, img, - spatial_scale) - - return rel_img_point - - -def point_sample(input, points, align_corners=False, **kwargs): - """A wrapper around :func:`grid_sample` to support 3D point_coords tensors - Unlike :func:`torch.nn.functional.grid_sample` it assumes point_coords to - lie inside ``[0, 1] x [0, 1]`` square. - - Args: - input (torch.Tensor): Feature map, shape (N, C, H, W). - points (torch.Tensor): Image based absolute point coordinates - (normalized), range [0, 1] x [0, 1], shape (N, P, 2) or - (N, Hgrid, Wgrid, 2). - align_corners (bool, optional): Whether align_corners. - Default: False - - Returns: - torch.Tensor: Features of `point` on `input`, shape (N, C, P) or - (N, C, Hgrid, Wgrid). - """ - - add_dim = False - if points.dim() == 3: - add_dim = True - points = points.unsqueeze(2) - if is_in_onnx_export_without_custom_ops(): - # If custom ops for onnx runtime not compiled use python - # implementation of grid_sample function to make onnx graph - # with supported nodes - output = bilinear_grid_sample( - input, denormalize(points), align_corners=align_corners) - else: - output = F.grid_sample( - input, denormalize(points), align_corners=align_corners, **kwargs) - if add_dim: - output = output.squeeze(3) - return output - - -class SimpleRoIAlign(nn.Module): - - def __init__(self, output_size, spatial_scale, aligned=True): - """Simple RoI align in PointRend, faster than standard RoIAlign. - - Args: - output_size (tuple[int]): h, w - spatial_scale (float): scale the input boxes by this number - aligned (bool): if False, use the legacy implementation in - MMDetection, align_corners=True will be used in F.grid_sample. - If True, align the results more perfectly. - """ - - super(SimpleRoIAlign, self).__init__() - self.output_size = _pair(output_size) - self.spatial_scale = float(spatial_scale) - # to be consistent with other RoI ops - self.use_torchvision = False - self.aligned = aligned - - def forward(self, features, rois): - num_imgs = features.size(0) - num_rois = rois.size(0) - rel_roi_points = generate_grid( - num_rois, self.output_size, device=rois.device) - - if torch.onnx.is_in_onnx_export(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois, rel_roi_points, features, self.spatial_scale) - rel_img_points = rel_img_points.reshape(num_imgs, -1, - *rel_img_points.shape[1:]) - point_feats = point_sample( - features, rel_img_points, align_corners=not self.aligned) - point_feats = point_feats.transpose(1, 2) - else: - point_feats = [] - for batch_ind in range(num_imgs): - # unravel batch dim - feat = features[batch_ind].unsqueeze(0) - inds = (rois[:, 0].long() == batch_ind) - if inds.any(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois[inds], rel_roi_points[inds], feat, - self.spatial_scale).unsqueeze(0) - point_feat = point_sample( - feat, rel_img_points, align_corners=not self.aligned) - point_feat = point_feat.squeeze(0).transpose(0, 1) - point_feats.append(point_feat) - - point_feats = torch.cat(point_feats, dim=0) - - channels = features.size(1) - roi_feats = point_feats.reshape(num_rois, channels, *self.output_size) - - return roi_feats - - def __repr__(self): - format_str = self.__class__.__name__ - format_str += '(output_size={}, spatial_scale={}'.format( - self.output_size, self.spatial_scale) - return format_str diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/psa_mask.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/psa_mask.py deleted file mode 100644 index cdf14e62b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/psa_mask.py +++ /dev/null @@ -1,92 +0,0 @@ -# Modified from https://github.com/hszhao/semseg/blob/master/lib/psa -from torch import nn -from torch.autograd import Function -from torch.nn.modules.utils import _pair - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', - ['psamask_forward', 'psamask_backward']) - - -class PSAMaskFunction(Function): - - @staticmethod - def symbolic(g, input, psa_type, mask_size): - return g.op( - 'mmcv::MMCVPSAMask', - input, - psa_type_i=psa_type, - mask_size_i=mask_size) - - @staticmethod - def forward(ctx, input, psa_type, mask_size): - ctx.psa_type = psa_type - ctx.mask_size = _pair(mask_size) - ctx.save_for_backward(input) - - h_mask, w_mask = ctx.mask_size - batch_size, channels, h_feature, w_feature = input.size() - assert channels == h_mask * w_mask - output = input.new_zeros( - (batch_size, h_feature * w_feature, h_feature, w_feature)) - - ext_module.psamask_forward( - input, - output, - psa_type=psa_type, - num_=batch_size, - h_feature=h_feature, - w_feature=w_feature, - h_mask=h_mask, - w_mask=w_mask, - half_h_mask=(h_mask - 1) // 2, - half_w_mask=(w_mask - 1) // 2) - return output - - @staticmethod - def backward(ctx, grad_output): - input = ctx.saved_tensors[0] - psa_type = ctx.psa_type - h_mask, w_mask = ctx.mask_size - batch_size, channels, h_feature, w_feature = input.size() - grad_input = grad_output.new_zeros( - (batch_size, channels, h_feature, w_feature)) - ext_module.psamask_backward( - grad_output, - grad_input, - psa_type=psa_type, - num_=batch_size, - h_feature=h_feature, - w_feature=w_feature, - h_mask=h_mask, - w_mask=w_mask, - half_h_mask=(h_mask - 1) // 2, - half_w_mask=(w_mask - 1) // 2) - return grad_input, None, None, None - - -psa_mask = PSAMaskFunction.apply - - -class PSAMask(nn.Module): - - def __init__(self, psa_type, mask_size=None): - super(PSAMask, self).__init__() - assert psa_type in ['collect', 'distribute'] - if psa_type == 'collect': - psa_type_enum = 0 - else: - psa_type_enum = 1 - self.psa_type_enum = psa_type_enum - self.mask_size = mask_size - self.psa_type = psa_type - - def forward(self, input): - return psa_mask(input, self.psa_type_enum, self.mask_size) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(psa_type={self.psa_type}, ' - s += f'mask_size={self.mask_size})' - return s diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/sync_bn.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/sync_bn.py deleted file mode 100644 index 04302f031..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/ops/sync_bn.py +++ /dev/null @@ -1,279 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn.functional as F -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.module import Module -from torch.nn.parameter import Parameter - -from mmcv.cnn import NORM_LAYERS -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sync_bn_forward_mean', 'sync_bn_forward_var', 'sync_bn_forward_output', - 'sync_bn_backward_param', 'sync_bn_backward_data' -]) - - -class SyncBatchNormFunction(Function): - - @staticmethod - def symbolic(g, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - return g.op( - 'mmcv::MMCVSyncBatchNorm', - input, - running_mean, - running_var, - weight, - bias, - momentum_f=momentum, - eps_f=eps, - group_i=group, - group_size_i=group_size, - stats_mode=stats_mode) - - @staticmethod - def forward(self, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - self.momentum = momentum - self.eps = eps - self.group = group - self.group_size = group_size - self.stats_mode = stats_mode - - assert isinstance( - input, (torch.HalfTensor, torch.FloatTensor, - torch.cuda.HalfTensor, torch.cuda.FloatTensor)), \ - f'only support Half or Float Tensor, but {input.type()}' - output = torch.zeros_like(input) - input3d = input.flatten(start_dim=2) - output3d = output.view_as(input3d) - num_channels = input3d.size(1) - - # ensure mean/var/norm/std are initialized as zeros - # ``torch.empty()`` does not guarantee that - mean = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - var = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - norm = torch.zeros_like( - input3d, dtype=torch.float, device=input3d.device) - std = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - - batch_size = input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_forward_mean(input3d, mean) - batch_flag = torch.ones([1], device=mean.device, dtype=mean.dtype) - else: - # skip updating mean and leave it as zeros when the input is empty - batch_flag = torch.zeros([1], device=mean.device, dtype=mean.dtype) - - # synchronize mean and the batch flag - vec = torch.cat([mean, batch_flag]) - if self.stats_mode == 'N': - vec *= batch_size - if self.group_size > 1: - dist.all_reduce(vec, group=self.group) - total_batch = vec[-1].detach() - mean = vec[:num_channels] - - if self.stats_mode == 'default': - mean = mean / self.group_size - elif self.stats_mode == 'N': - mean = mean / total_batch.clamp(min=1) - else: - raise NotImplementedError - - # leave var as zeros when the input is empty - if batch_size > 0: - ext_module.sync_bn_forward_var(input3d, mean, var) - - if self.stats_mode == 'N': - var *= batch_size - if self.group_size > 1: - dist.all_reduce(var, group=self.group) - - if self.stats_mode == 'default': - var /= self.group_size - elif self.stats_mode == 'N': - var /= total_batch.clamp(min=1) - else: - raise NotImplementedError - - # if the total batch size over all the ranks is zero, - # we should not update the statistics in the current batch - update_flag = total_batch.clamp(max=1) - momentum = update_flag * self.momentum - ext_module.sync_bn_forward_output( - input3d, - mean, - var, - weight, - bias, - running_mean, - running_var, - norm, - std, - output3d, - eps=self.eps, - momentum=momentum, - group_size=self.group_size) - self.save_for_backward(norm, std, weight) - return output - - @staticmethod - @once_differentiable - def backward(self, grad_output): - norm, std, weight = self.saved_tensors - grad_weight = torch.zeros_like(weight) - grad_bias = torch.zeros_like(weight) - grad_input = torch.zeros_like(grad_output) - grad_output3d = grad_output.flatten(start_dim=2) - grad_input3d = grad_input.view_as(grad_output3d) - - batch_size = grad_input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_backward_param(grad_output3d, norm, grad_weight, - grad_bias) - - # all reduce - if self.group_size > 1: - dist.all_reduce(grad_weight, group=self.group) - dist.all_reduce(grad_bias, group=self.group) - grad_weight /= self.group_size - grad_bias /= self.group_size - - if batch_size > 0: - ext_module.sync_bn_backward_data(grad_output3d, weight, - grad_weight, grad_bias, norm, std, - grad_input3d) - - return grad_input, None, None, grad_weight, grad_bias, \ - None, None, None, None, None - - -@NORM_LAYERS.register_module(name='MMSyncBN') -class SyncBatchNorm(Module): - """Synchronized Batch Normalization. - - Args: - num_features (int): number of features/chennels in input tensor - eps (float, optional): a value added to the denominator for numerical - stability. Defaults to 1e-5. - momentum (float, optional): the value used for the running_mean and - running_var computation. Defaults to 0.1. - affine (bool, optional): whether to use learnable affine parameters. - Defaults to True. - track_running_stats (bool, optional): whether to track the running - mean and variance during training. When set to False, this - module does not track such statistics, and initializes statistics - buffers ``running_mean`` and ``running_var`` as ``None``. When - these buffers are ``None``, this module always uses batch - statistics in both training and eval modes. Defaults to True. - group (int, optional): synchronization of stats happen within - each process group individually. By default it is synchronization - across the whole world. Defaults to None. - stats_mode (str, optional): The statistical mode. Available options - includes ``'default'`` and ``'N'``. Defaults to 'default'. - When ``stats_mode=='default'``, it computes the overall statistics - using those from each worker with equal weight, i.e., the - statistics are synchronized and simply divied by ``group``. This - mode will produce inaccurate statistics when empty tensors occur. - When ``stats_mode=='N'``, it compute the overall statistics using - the total number of batches in each worker ignoring the number of - group, i.e., the statistics are synchronized and then divied by - the total batch ``N``. This mode is beneficial when empty tensors - occur during training, as it average the total mean by the real - number of batch. - """ - - def __init__(self, - num_features, - eps=1e-5, - momentum=0.1, - affine=True, - track_running_stats=True, - group=None, - stats_mode='default'): - super(SyncBatchNorm, self).__init__() - self.num_features = num_features - self.eps = eps - self.momentum = momentum - self.affine = affine - self.track_running_stats = track_running_stats - group = dist.group.WORLD if group is None else group - self.group = group - self.group_size = dist.get_world_size(group) - assert stats_mode in ['default', 'N'], \ - f'"stats_mode" only accepts "default" and "N", got "{stats_mode}"' - self.stats_mode = stats_mode - if self.affine: - self.weight = Parameter(torch.Tensor(num_features)) - self.bias = Parameter(torch.Tensor(num_features)) - else: - self.register_parameter('weight', None) - self.register_parameter('bias', None) - if self.track_running_stats: - self.register_buffer('running_mean', torch.zeros(num_features)) - self.register_buffer('running_var', torch.ones(num_features)) - self.register_buffer('num_batches_tracked', - torch.tensor(0, dtype=torch.long)) - else: - self.register_buffer('running_mean', None) - self.register_buffer('running_var', None) - self.register_buffer('num_batches_tracked', None) - self.reset_parameters() - - def reset_running_stats(self): - if self.track_running_stats: - self.running_mean.zero_() - self.running_var.fill_(1) - self.num_batches_tracked.zero_() - - def reset_parameters(self): - self.reset_running_stats() - if self.affine: - self.weight.data.uniform_() # pytorch use ones_() - self.bias.data.zero_() - - def forward(self, input): - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input, got {input.dim()}D input') - if self.momentum is None: - exponential_average_factor = 0.0 - else: - exponential_average_factor = self.momentum - - if self.training and self.track_running_stats: - if self.num_batches_tracked is not None: - self.num_batches_tracked += 1 - if self.momentum is None: # use cumulative moving average - exponential_average_factor = 1.0 / float( - self.num_batches_tracked) - else: # use exponential moving average - exponential_average_factor = self.momentum - - if self.training or not self.track_running_stats: - return SyncBatchNormFunction.apply( - input, self.running_mean, self.running_var, self.weight, - self.bias, exponential_average_factor, self.eps, self.group, - self.group_size, self.stats_mode) - else: - return F.batch_norm(input, self.running_mean, self.running_var, - self.weight, self.bias, False, - exponential_average_factor, self.eps) - - def __repr__(self): - s = self.__class__.__name__ - s += f'({self.num_features}, ' - s += f'eps={self.eps}, ' - s += f'momentum={self.momentum}, ' - s += f'affine={self.affine}, ' - s += f'track_running_stats={self.track_running_stats}, ' - s += f'group_size={self.group_size},' - s += f'stats_mode={self.stats_mode})' - return s diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/__init__.py deleted file mode 100644 index 2ed2c17ad..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collate import collate -from .data_container import DataContainer -from .data_parallel import MMDataParallel -from .distributed import MMDistributedDataParallel -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter, scatter_kwargs -from .utils import is_module_wrapper - -__all__ = [ - 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', - 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/_functions.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/_functions.py deleted file mode 100644 index 95c58bf1a..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/_functions.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import _get_stream - - -def scatter(input, devices, streams=None): - """Scatters tensor across multiple GPUs.""" - if streams is None: - streams = [None] * len(devices) - - if isinstance(input, list): - chunk_size = (len(input) - 1) // len(devices) + 1 - outputs = [ - scatter(input[i], [devices[i // chunk_size]], - [streams[i // chunk_size]]) for i in range(len(input)) - ] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - # TODO: copy to a pinned buffer first (if copying from CPU) - stream = streams[0] if output.numel() > 0 else None - if devices != [-1]: - with torch.cuda.device(devices[0]), torch.cuda.stream(stream): - output = output.cuda(devices[0], non_blocking=True) - - return output - else: - raise Exception(f'Unknown type {type(input)}.') - - -def synchronize_stream(output, devices, streams): - if isinstance(output, list): - chunk_size = len(output) // len(devices) - for i in range(len(devices)): - for j in range(chunk_size): - synchronize_stream(output[i * chunk_size + j], [devices[i]], - [streams[i]]) - elif isinstance(output, torch.Tensor): - if output.numel() != 0: - with torch.cuda.device(devices[0]): - main_stream = torch.cuda.current_stream() - main_stream.wait_stream(streams[0]) - output.record_stream(main_stream) - else: - raise Exception(f'Unknown type {type(output)}.') - - -def get_input_device(input): - if isinstance(input, list): - for item in input: - input_device = get_input_device(item) - if input_device != -1: - return input_device - return -1 - elif isinstance(input, torch.Tensor): - return input.get_device() if input.is_cuda else -1 - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_gpus, input): - input_device = get_input_device(input) - streams = None - if input_device == -1 and target_gpus != [-1]: - # Perform CPU to GPU copies in a background stream - streams = [_get_stream(device) for device in target_gpus] - - outputs = scatter(input, target_gpus, streams) - # Synchronize with the copy stream - if streams is not None: - synchronize_stream(outputs, target_gpus, streams) - - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/collate.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/collate.py deleted file mode 100644 index ad749197d..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/collate.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence - -import torch -import torch.nn.functional as F -from torch.utils.data.dataloader import default_collate - -from .data_container import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Puts each data field into a tensor/DataContainer with outer dimension - batch size. - - Extend default_collate to add support for - :type:`~mmcv.parallel.DataContainer`. There are 3 cases. - - 1. cpu_only = True, e.g., meta data - 2. cpu_only = False, stack = True, e.g., images tensors - 3. cpu_only = False, stack = False, e.g., gt bboxes - """ - - if not isinstance(batch, Sequence): - raise TypeError(f'{batch.dtype} is not supported.') - - if isinstance(batch[0], DataContainer): - stacked = [] - if batch[0].cpu_only: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer( - stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) - elif batch[0].stack: - for i in range(0, len(batch), samples_per_gpu): - assert isinstance(batch[i].data, torch.Tensor) - - if batch[i].pad_dims is not None: - ndim = batch[i].dim() - assert ndim > batch[i].pad_dims - max_shape = [0 for _ in range(batch[i].pad_dims)] - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = batch[i].size(-dim) - for sample in batch[i:i + samples_per_gpu]: - for dim in range(0, ndim - batch[i].pad_dims): - assert batch[i].size(dim) == sample.size(dim) - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = max(max_shape[dim - 1], - sample.size(-dim)) - padded_samples = [] - for sample in batch[i:i + samples_per_gpu]: - pad = [0 for _ in range(batch[i].pad_dims * 2)] - for dim in range(1, batch[i].pad_dims + 1): - pad[2 * dim - - 1] = max_shape[dim - 1] - sample.size(-dim) - padded_samples.append( - F.pad( - sample.data, pad, value=sample.padding_value)) - stacked.append(default_collate(padded_samples)) - elif batch[i].pad_dims is None: - stacked.append( - default_collate([ - sample.data - for sample in batch[i:i + samples_per_gpu] - ])) - else: - raise ValueError( - 'pad_dims should be either None or integers (1-3)') - - else: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer(stacked, batch[0].stack, batch[0].padding_value) - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - return [collate(samples, samples_per_gpu) for samples in transposed] - elif isinstance(batch[0], Mapping): - return { - key: collate([d[key] for d in batch], samples_per_gpu) - for key in batch[0] - } - else: - return default_collate(batch) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/data_container.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/data_container.py deleted file mode 100644 index cedb0d32a..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/data_container.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch - - -def assert_tensor_type(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not isinstance(args[0].data, torch.Tensor): - raise AttributeError( - f'{args[0].__class__.__name__} has no attribute ' - f'{func.__name__} for type {args[0].datatype}') - return func(*args, **kwargs) - - return wrapper - - -class DataContainer: - """A container for any type of objects. - - Typically tensors will be stacked in the collate function and sliced along - some dimension in the scatter function. This behavior has some limitations. - 1. All tensors have to be the same size. - 2. Types are limited (numpy array or Tensor). - - We design `DataContainer` and `MMDataParallel` to overcome these - limitations. The behavior can be either of the following. - - - copy to GPU, pad all tensors to the same size and stack them - - copy to GPU without stacking - - leave the objects as is and pass it to the model - - pad_dims specifies the number of last few dimensions to do padding - """ - - def __init__(self, - data, - stack=False, - padding_value=0, - cpu_only=False, - pad_dims=2): - self._data = data - self._cpu_only = cpu_only - self._stack = stack - self._padding_value = padding_value - assert pad_dims in [None, 1, 2, 3] - self._pad_dims = pad_dims - - def __repr__(self): - return f'{self.__class__.__name__}({repr(self.data)})' - - def __len__(self): - return len(self._data) - - @property - def data(self): - return self._data - - @property - def datatype(self): - if isinstance(self.data, torch.Tensor): - return self.data.type() - else: - return type(self.data) - - @property - def cpu_only(self): - return self._cpu_only - - @property - def stack(self): - return self._stack - - @property - def padding_value(self): - return self._padding_value - - @property - def pad_dims(self): - return self._pad_dims - - @assert_tensor_type - def size(self, *args, **kwargs): - return self.data.size(*args, **kwargs) - - @assert_tensor_type - def dim(self): - return self.data.dim() diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/data_parallel.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/data_parallel.py deleted file mode 100644 index 7a5abeb6e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/data_parallel.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from itertools import chain - -from torch.nn.parallel import DataParallel - -from .scatter_gather import scatter_kwargs - - -class MMDataParallel(DataParallel): - """The DataParallel module that supports DataContainer. - - MMDataParallel has two main differences with PyTorch DataParallel: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data during both GPU and CPU inference. - - It implement two more APIs ``train_step()`` and ``val_step()``. - - .. warning:: - MMDataParallel only supports single GPU training, if you need to - train with multiple GPUs, please use MMDistributedDataParallel - instead. If you have multiple GPUs and you just want to use - MMDataParallel, you can set the environment variable - ``CUDA_VISIBLE_DEVICES=0`` or instantiate ``MMDataParallel`` with - ``device_ids=[0]``. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - device_ids (list[int]): Device IDS of modules to be scattered to. - Defaults to None when GPU is not available. - output_device (str | int): Device ID for output. Defaults to None. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MMDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.dim = dim - - def forward(self, *inputs, **kwargs): - """Override the original forward function. - - The main difference lies in the CPU inference where the data in - :class:`DataContainers` will still be gathered. - """ - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module(*inputs[0], **kwargs[0]) - else: - return super().forward(*inputs, **kwargs) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.train_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.train_step(*inputs[0], **kwargs[0]) - - def val_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.val_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/distributed.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/distributed.py deleted file mode 100644 index 0188ca4ab..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/distributed.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel.distributed import (DistributedDataParallel, - _find_tensors) - -from mmcv import print_log -from mmcv.utils import TORCH_VERSION, digit_version -from .scatter_gather import scatter_kwargs - - -class MMDistributedDataParallel(DistributedDataParallel): - """The DDP module that supports DataContainer. - - MMDDP has two main differences with PyTorch DDP: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data. - - It implement two APIs ``train_step()`` and ``val_step()``. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - """train_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.train_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.train_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.train_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output - - def val_step(self, *inputs, **kwargs): - """val_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.val_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.val_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.val_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/distributed_deprecated.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/distributed_deprecated.py deleted file mode 100644 index b593d4a9e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/distributed_deprecated.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn as nn -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter_kwargs - - -@MODULE_WRAPPERS.register_module() -class MMDistributedDataParallel(nn.Module): - - def __init__(self, - module, - dim=0, - broadcast_buffers=True, - bucket_cap_mb=25): - super(MMDistributedDataParallel, self).__init__() - self.module = module - self.dim = dim - self.broadcast_buffers = broadcast_buffers - - self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 - self._sync_params() - - def _dist_broadcast_coalesced(self, tensors, buffer_size): - for tensors in _take_tensors(tensors, buffer_size): - flat_tensors = _flatten_dense_tensors(tensors) - dist.broadcast(flat_tensors, 0) - for tensor, synced in zip( - tensors, _unflatten_dense_tensors(flat_tensors, tensors)): - tensor.copy_(synced) - - def _sync_params(self): - module_states = list(self.module.state_dict().values()) - if len(module_states) > 0: - self._dist_broadcast_coalesced(module_states, - self.broadcast_bucket_size) - if self.broadcast_buffers: - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) < digit_version('1.0')): - buffers = [b.data for b in self.module._all_buffers()] - else: - buffers = [b.data for b in self.module.buffers()] - if len(buffers) > 0: - self._dist_broadcast_coalesced(buffers, - self.broadcast_bucket_size) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/registry.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/registry.py deleted file mode 100644 index 144f9fb16..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel, DistributedDataParallel - -from mmcv.utils import Registry - -MODULE_WRAPPERS = Registry('module wrapper') -MODULE_WRAPPERS.register_module(module=DataParallel) -MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/scatter_gather.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/scatter_gather.py deleted file mode 100644 index 900ff8856..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import Scatter as OrigScatter - -from ._functions import Scatter -from .data_container import DataContainer - - -def scatter(inputs, target_gpus, dim=0): - """Scatter inputs to target gpus. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_gpus != [-1]: - return OrigScatter.apply(target_gpus, None, dim, obj) - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_gpus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_gpus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_gpus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_gpus, dim) if inputs else [] - kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/utils.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/utils.py deleted file mode 100644 index 0f5712cb4..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/parallel/utils.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import MODULE_WRAPPERS - - -def is_module_wrapper(module): - """Check if a module is a module wrapper. - - The following 3 modules in MMCV (and their subclasses) are regarded as - module wrappers: DataParallel, DistributedDataParallel, - MMDistributedDataParallel (the deprecated version). You may add you own - module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: True if the input module is a module wrapper. - """ - module_wrappers = tuple(MODULE_WRAPPERS.module_dict.values()) - return isinstance(module, module_wrappers) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/__init__.py deleted file mode 100644 index 183d53672..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_module import BaseModule, ModuleDict, ModuleList, Sequential -from .base_runner import BaseRunner -from .builder import RUNNERS, build_runner -from .checkpoint import (CheckpointLoader, _load_checkpoint, - _load_checkpoint_with_prefix, load_checkpoint, - load_state_dict, save_checkpoint, weights_to_cpu) -from .default_constructor import DefaultRunnerConstructor -from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, - init_dist, master_only) -from .epoch_based_runner import EpochBasedRunner, Runner -from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model -from .hooks import (HOOKS, CheckpointHook, ClearMLLoggerHook, ClosureHook, - DistEvalHook, DistSamplerSeedHook, DvcliveLoggerHook, - EMAHook, EvalHook, Fp16OptimizerHook, - GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, Hook, IterTimerHook, - LoggerHook, MlflowLoggerHook, NeptuneLoggerHook, - OptimizerHook, PaviLoggerHook, SegmindLoggerHook, - SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .hooks.lr_updater import StepLrUpdaterHook # noqa -from .hooks.lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, - InvLrUpdaterHook, LinearAnnealingLrUpdaterHook, - LrUpdaterHook, OneCycleLrUpdaterHook, - PolyLrUpdaterHook) -from .hooks.momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .iter_based_runner import IterBasedRunner, IterLoader -from .log_buffer import LogBuffer -from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, - DefaultOptimizerConstructor, build_optimizer, - build_optimizer_constructor) -from .priority import Priority, get_priority -from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed - -# initialize ipu to registor ipu runner to RUNNERS -from mmcv.device import ipu # isort:skip # noqa - -__all__ = [ - 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', - 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', - 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', - 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', - 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', - 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', - 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', - 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', - 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', - 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', - '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', - 'ModuleDict', 'ModuleList', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor', - 'SegmindLoggerHook', 'LinearAnnealingMomentumUpdaterHook', - 'LinearAnnealingLrUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/base_module.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/base_module.py deleted file mode 100644 index 7937eca37..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/base_module.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings -from abc import ABCMeta -from collections import defaultdict -from logging import FileHandler - -import torch.nn as nn - -from mmcv.runner.dist_utils import master_only -from mmcv.utils.logging import get_logger, logger_initialized, print_log - - -class BaseModule(nn.Module, metaclass=ABCMeta): - """Base module for all modules in openmmlab. - - ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional - functionality of parameter initialization. Compared with - ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. - - - ``init_cfg``: the config to control the initialization. - - ``init_weights``: The function of parameter initialization and recording - initialization information. - - ``_params_init_info``: Used to track the parameter initialization - information. This attribute only exists during executing the - ``init_weights``. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, init_cfg=None): - """Initialize BaseModule, inherited from `torch.nn.Module`""" - - # NOTE init_cfg can be defined in different levels, but init_cfg - # in low levels has a higher priority. - - super(BaseModule, self).__init__() - # define default value of init_cfg instead of hard code - # in init_weights() function - self._is_init = False - - self.init_cfg = copy.deepcopy(init_cfg) - - # Backward compatibility in derived classes - # if pretrained is not None: - # warnings.warn('DeprecationWarning: pretrained is a deprecated \ - # key, please consider using init_cfg') - # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - - @property - def is_init(self): - return self._is_init - - def init_weights(self): - """Initialize the weights.""" - - is_top_level_module = False - # check if it is top-level module - if not hasattr(self, '_params_init_info'): - # The `_params_init_info` is used to record the initialization - # information of the parameters - # the key should be the obj:`nn.Parameter` of model and the value - # should be a dict containing - # - init_info (str): The string that describes the initialization. - # - tmp_mean_value (FloatTensor): The mean of the parameter, - # which indicates whether the parameter has been modified. - # this attribute would be deleted after all parameters - # is initialized. - self._params_init_info = defaultdict(dict) - is_top_level_module = True - - # Initialize the `_params_init_info`, - # When detecting the `tmp_mean_value` of - # the corresponding parameter is changed, update related - # initialization information - for name, param in self.named_parameters(): - self._params_init_info[param][ - 'init_info'] = f'The value is the same before and ' \ - f'after calling `init_weights` ' \ - f'of {self.__class__.__name__} ' - self._params_init_info[param][ - 'tmp_mean_value'] = param.data.mean() - - # pass `params_init_info` to all submodules - # All submodules share the same `params_init_info`, - # so it will be updated when parameters are - # modified at any level of the model. - for sub_module in self.modules(): - sub_module._params_init_info = self._params_init_info - - # Get the initialized logger, if not exist, - # create a logger named `mmcv` - logger_names = list(logger_initialized.keys()) - logger_name = logger_names[0] if logger_names else 'mmcv' - - from ..cnn import initialize - from ..cnn.utils.weight_init import update_init_info - module_name = self.__class__.__name__ - if not self._is_init: - if self.init_cfg: - print_log( - f'initialize {module_name} with init_cfg {self.init_cfg}', - logger=logger_name) - initialize(self, self.init_cfg) - if isinstance(self.init_cfg, dict): - # prevent the parameters of - # the pre-trained model - # from being overwritten by - # the `init_weights` - if self.init_cfg['type'] == 'Pretrained': - return - - for m in self.children(): - if hasattr(m, 'init_weights'): - m.init_weights() - # users may overload the `init_weights` - update_init_info( - m, - init_info=f'Initialized by ' - f'user-defined `init_weights`' - f' in {m.__class__.__name__} ') - - self._is_init = True - else: - warnings.warn(f'init_weights of {self.__class__.__name__} has ' - f'been called more than once.') - - if is_top_level_module: - self._dump_init_info(logger_name) - - for sub_module in self.modules(): - del sub_module._params_init_info - - @master_only - def _dump_init_info(self, logger_name): - """Dump the initialization information to a file named - `initialization.log.json` in workdir. - - Args: - logger_name (str): The name of logger. - """ - - logger = get_logger(logger_name) - - with_file_handler = False - # dump the information to the logger file if there is a `FileHandler` - for handler in logger.handlers: - if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True - if not with_file_handler: - for name, param in self.named_parameters(): - print_log( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n ", - logger=logger_name) - - def __repr__(self): - s = super().__repr__() - if self.init_cfg: - s += f'\ninit_cfg={self.init_cfg}' - return s - - -class Sequential(BaseModule, nn.Sequential): - """Sequential module in openmmlab. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, *args, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.Sequential.__init__(self, *args) - - -class ModuleList(BaseModule, nn.ModuleList): - """ModuleList in openmmlab. - - Args: - modules (iterable, optional): an iterable of modules to add. - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleList.__init__(self, modules) - - -class ModuleDict(BaseModule, nn.ModuleDict): - """ModuleDict in openmmlab. - - Args: - modules (dict, optional): a mapping (dictionary) of (string: module) - or an iterable of key-value pairs of type (string, module). - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleDict.__init__(self, modules) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/base_runner.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/base_runner.py deleted file mode 100644 index 12a0025f8..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/base_runner.py +++ /dev/null @@ -1,544 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -import os.path as osp -import warnings -from abc import ABCMeta, abstractmethod - -import torch -from torch.optim import Optimizer - -import mmcv -from ..parallel import is_module_wrapper -from .checkpoint import load_checkpoint -from .dist_utils import get_dist_info -from .hooks import HOOKS, Hook -from .log_buffer import LogBuffer -from .priority import Priority, get_priority -from .utils import get_time_str - - -class BaseRunner(metaclass=ABCMeta): - """The base class of Runner, a training helper for PyTorch. - - All subclasses should implement the following APIs: - - - ``run()`` - - ``train()`` - - ``val()`` - - ``save_checkpoint()`` - - Args: - model (:obj:`torch.nn.Module`): The model to be run. - batch_processor (callable): A callable method that process a data - batch. The interface of this method should be - `batch_processor(model, data, train_mode) -> dict` - optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an - optimizer (in most cases) or a dict of optimizers (in models that - requires more than one optimizer, e.g., GAN). - work_dir (str, optional): The working directory to save checkpoints - and logs. Defaults to None. - logger (:obj:`logging.Logger`): Logger used during training. - Defaults to None. (The default value is just for backward - compatibility) - meta (dict | None): A dict records some import information such as - environment info and seed, which will be logged in logger hook. - Defaults to None. - max_epochs (int, optional): Total training epochs. - max_iters (int, optional): Total training iterations. - """ - - def __init__(self, - model, - batch_processor=None, - optimizer=None, - work_dir=None, - logger=None, - meta=None, - max_iters=None, - max_epochs=None): - if batch_processor is not None: - if not callable(batch_processor): - raise TypeError('batch_processor must be callable, ' - f'but got {type(batch_processor)}') - warnings.warn( - 'batch_processor is deprecated, please implement ' - 'train_step() and val_step() in the model instead.', - DeprecationWarning) - # raise an error is `batch_processor` is not None and - # `model.train_step()` exists. - if is_module_wrapper(model): - _model = model.module - else: - _model = model - if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): - raise RuntimeError( - 'batch_processor and model.train_step()/model.val_step() ' - 'cannot be both available.') - else: - assert hasattr(model, 'train_step') - - # check the type of `optimizer` - if isinstance(optimizer, dict): - for name, optim in optimizer.items(): - if not isinstance(optim, Optimizer): - raise TypeError( - f'optimizer must be a dict of torch.optim.Optimizers, ' - f'but optimizer["{name}"] is a {type(optim)}') - elif not isinstance(optimizer, Optimizer) and optimizer is not None: - raise TypeError( - f'optimizer must be a torch.optim.Optimizer object ' - f'or dict or None, but got {type(optimizer)}') - - # check the type of `logger` - if not isinstance(logger, logging.Logger): - raise TypeError(f'logger must be a logging.Logger object, ' - f'but got {type(logger)}') - - # check the type of `meta` - if meta is not None and not isinstance(meta, dict): - raise TypeError( - f'meta must be a dict or None, but got {type(meta)}') - - self.model = model - self.batch_processor = batch_processor - self.optimizer = optimizer - self.logger = logger - self.meta = meta - # create work_dir - if mmcv.is_str(work_dir): - self.work_dir = osp.abspath(work_dir) - mmcv.mkdir_or_exist(self.work_dir) - elif work_dir is None: - self.work_dir = None - else: - raise TypeError('"work_dir" must be a str or None') - - # get model name from the model class - if hasattr(self.model, 'module'): - self._model_name = self.model.module.__class__.__name__ - else: - self._model_name = self.model.__class__.__name__ - - self._rank, self._world_size = get_dist_info() - self.timestamp = get_time_str() - self.mode = None - self._hooks = [] - self._epoch = 0 - self._iter = 0 - self._inner_iter = 0 - - if max_epochs is not None and max_iters is not None: - raise ValueError( - 'Only one of `max_epochs` or `max_iters` can be set.') - - self._max_epochs = max_epochs - self._max_iters = max_iters - # TODO: Redesign LogBuffer, it is not flexible and elegant enough - self.log_buffer = LogBuffer() - - @property - def model_name(self): - """str: Name of the model, usually the module class name.""" - return self._model_name - - @property - def rank(self): - """int: Rank of current process. (distributed training)""" - return self._rank - - @property - def world_size(self): - """int: Number of processes participating in the job. - (distributed training)""" - return self._world_size - - @property - def hooks(self): - """list[:obj:`Hook`]: A list of registered hooks.""" - return self._hooks - - @property - def epoch(self): - """int: Current epoch.""" - return self._epoch - - @property - def iter(self): - """int: Current iteration.""" - return self._iter - - @property - def inner_iter(self): - """int: Iteration in an epoch.""" - return self._inner_iter - - @property - def max_epochs(self): - """int: Maximum training epochs.""" - return self._max_epochs - - @property - def max_iters(self): - """int: Maximum training iterations.""" - return self._max_iters - - @abstractmethod - def train(self): - pass - - @abstractmethod - def val(self): - pass - - @abstractmethod - def run(self, data_loaders, workflow, **kwargs): - pass - - @abstractmethod - def save_checkpoint(self, - out_dir, - filename_tmpl, - save_optimizer=True, - meta=None, - create_symlink=True): - pass - - def current_lr(self): - """Get current learning rates. - - Returns: - list[float] | dict[str, list[float]]: Current learning rates of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - if isinstance(self.optimizer, torch.optim.Optimizer): - lr = [group['lr'] for group in self.optimizer.param_groups] - elif isinstance(self.optimizer, dict): - lr = dict() - for name, optim in self.optimizer.items(): - lr[name] = [group['lr'] for group in optim.param_groups] - else: - raise RuntimeError( - 'lr is not applicable because optimizer does not exist.') - return lr - - def current_momentum(self): - """Get current momentums. - - Returns: - list[float] | dict[str, list[float]]: Current momentums of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - - def _get_momentum(optimizer): - momentums = [] - for group in optimizer.param_groups: - if 'momentum' in group.keys(): - momentums.append(group['momentum']) - elif 'betas' in group.keys(): - momentums.append(group['betas'][0]) - else: - momentums.append(0) - return momentums - - if self.optimizer is None: - raise RuntimeError( - 'momentum is not applicable because optimizer does not exist.') - elif isinstance(self.optimizer, torch.optim.Optimizer): - momentums = _get_momentum(self.optimizer) - elif isinstance(self.optimizer, dict): - momentums = dict() - for name, optim in self.optimizer.items(): - momentums[name] = _get_momentum(optim) - return momentums - - def register_hook(self, hook, priority='NORMAL'): - """Register a hook into the hook list. - - The hook will be inserted into a priority queue, with the specified - priority (See :class:`Priority` for details of priorities). - For hooks with the same priority, they will be triggered in the same - order as they are registered. - - Args: - hook (:obj:`Hook`): The hook to be registered. - priority (int or str or :obj:`Priority`): Hook priority. - Lower value means higher priority. - """ - assert isinstance(hook, Hook) - if hasattr(hook, 'priority'): - raise ValueError('"priority" is a reserved attribute for hooks') - priority = get_priority(priority) - hook.priority = priority - # insert the hook to a sorted list - inserted = False - for i in range(len(self._hooks) - 1, -1, -1): - if priority >= self._hooks[i].priority: - self._hooks.insert(i + 1, hook) - inserted = True - break - if not inserted: - self._hooks.insert(0, hook) - - def register_hook_from_cfg(self, hook_cfg): - """Register a hook from its cfg. - - Args: - hook_cfg (dict): Hook config. It should have at least keys 'type' - and 'priority' indicating its type and priority. - - Note: - The specific hook class to register should not use 'type' and - 'priority' arguments during initialization. - """ - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = mmcv.build_from_cfg(hook_cfg, HOOKS) - self.register_hook(hook, priority=priority) - - def call_hook(self, fn_name): - """Call all hooks. - - Args: - fn_name (str): The function name in each hook to be called, such as - "before_train_epoch". - """ - for hook in self._hooks: - getattr(hook, fn_name)(self) - - def get_hook_info(self): - # Get hooks info in each stage - stage_hook_map = {stage: [] for stage in Hook.stages} - for hook in self.hooks: - try: - priority = Priority(hook.priority).name - except ValueError: - priority = hook.priority - classname = hook.__class__.__name__ - hook_info = f'({priority:<12}) {classname:<35}' - for trigger_stage in hook.get_triggered_stages(): - stage_hook_map[trigger_stage].append(hook_info) - - stage_hook_infos = [] - for stage in Hook.stages: - hook_infos = stage_hook_map[stage] - if len(hook_infos) > 0: - info = f'{stage}:\n' - info += '\n'.join(hook_infos) - info += '\n -------------------- ' - stage_hook_infos.append(info) - return '\n'.join(stage_hook_infos) - - def load_checkpoint(self, - filename, - map_location='cpu', - strict=False, - revise_keys=[(r'^module.', '')]): - return load_checkpoint( - self.model, - filename, - map_location, - strict, - self.logger, - revise_keys=revise_keys) - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - if map_location == 'default': - if torch.cuda.is_available(): - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint(checkpoint) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - if self.meta is None: - self.meta = {} - self.meta.setdefault('hook_msgs', {}) - # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages - self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) - - # Re-calculate the number of iterations when resuming - # models with different number of GPUs - if 'config' in checkpoint['meta']: - config = mmcv.Config.fromstring( - checkpoint['meta']['config'], file_format='.py') - previous_gpu_ids = config.get('gpu_ids', None) - if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( - previous_gpu_ids) != self.world_size: - self._iter = int(self._iter * len(previous_gpu_ids) / - self.world_size) - self.logger.info('the iteration number is changed due to ' - 'change of GPU number') - - # resume meta information meta - self.meta = checkpoint['meta'] - - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - elif isinstance(lr_config, dict): - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = mmcv.build_from_cfg(lr_config, HOOKS) - else: - hook = lr_config - self.register_hook(hook, priority='VERY_HIGH') - - def register_momentum_hook(self, momentum_config): - if momentum_config is None: - return - if isinstance(momentum_config, dict): - assert 'policy' in momentum_config - policy_type = momentum_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of momentum updater. - # Since this is not applicable for - # `CosineAnnealingMomentumUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'MomentumUpdaterHook' - momentum_config['type'] = hook_type - hook = mmcv.build_from_cfg(momentum_config, HOOKS) - else: - hook = momentum_config - self.register_hook(hook, priority='HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = mmcv.build_from_cfg(optimizer_config, HOOKS) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def register_checkpoint_hook(self, checkpoint_config): - if checkpoint_config is None: - return - if isinstance(checkpoint_config, dict): - checkpoint_config.setdefault('type', 'CheckpointHook') - hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) - else: - hook = checkpoint_config - self.register_hook(hook, priority='NORMAL') - - def register_logger_hooks(self, log_config): - if log_config is None: - return - log_interval = log_config['interval'] - for info in log_config['hooks']: - logger_hook = mmcv.build_from_cfg( - info, HOOKS, default_args=dict(interval=log_interval)) - self.register_hook(logger_hook, priority='VERY_LOW') - - def register_timer_hook(self, timer_config): - if timer_config is None: - return - if isinstance(timer_config, dict): - timer_config_ = copy.deepcopy(timer_config) - hook = mmcv.build_from_cfg(timer_config_, HOOKS) - else: - hook = timer_config - self.register_hook(hook, priority='LOW') - - def register_custom_hooks(self, custom_config): - if custom_config is None: - return - - if not isinstance(custom_config, list): - custom_config = [custom_config] - - for item in custom_config: - if isinstance(item, dict): - self.register_hook_from_cfg(item) - else: - self.register_hook(item, priority='NORMAL') - - def register_profiler_hook(self, profiler_config): - if profiler_config is None: - return - if isinstance(profiler_config, dict): - profiler_config.setdefault('type', 'ProfilerHook') - hook = mmcv.build_from_cfg(profiler_config, HOOKS) - else: - hook = profiler_config - self.register_hook(hook) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - timer_config=dict(type='IterTimerHook'), - custom_hooks_config=None): - """Register default and custom hooks for training. - - Default and custom hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - self.register_lr_hook(lr_config) - self.register_momentum_hook(momentum_config) - self.register_optimizer_hook(optimizer_config) - self.register_checkpoint_hook(checkpoint_config) - self.register_timer_hook(timer_config) - self.register_logger_hooks(log_config) - self.register_custom_hooks(custom_hooks_config) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/builder.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/builder.py deleted file mode 100644 index 77c96ba0b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/builder.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from ..utils import Registry - -RUNNERS = Registry('runner') -RUNNER_BUILDERS = Registry('runner builder') - - -def build_runner_constructor(cfg): - return RUNNER_BUILDERS.build(cfg) - - -def build_runner(cfg, default_args=None): - runner_cfg = copy.deepcopy(cfg) - constructor_type = runner_cfg.pop('constructor', - 'DefaultRunnerConstructor') - runner_constructor = build_runner_constructor( - dict( - type=constructor_type, - runner_cfg=runner_cfg, - default_args=default_args)) - runner = runner_constructor() - return runner diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/checkpoint.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/checkpoint.py deleted file mode 100644 index 835ee725a..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/checkpoint.py +++ /dev/null @@ -1,759 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os -import os.path as osp -import pkgutil -import re -import time -import warnings -from collections import OrderedDict -from importlib import import_module -from tempfile import TemporaryDirectory - -import torch -import torchvision -from torch.optim import Optimizer - -import mmcv -from ..fileio import FileClient -from ..fileio import load as load_file -from ..parallel import is_module_wrapper -from ..utils import digit_version, load_url, mkdir_or_exist -from .dist_utils import get_dist_info - -ENV_MMCV_HOME = 'MMCV_HOME' -ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' -DEFAULT_CACHE_DIR = '~/.cache' - - -def _get_mmcv_home(): - mmcv_home = os.path.expanduser( - os.getenv( - ENV_MMCV_HOME, - os.path.join( - os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) - - mkdir_or_exist(mmcv_home) - return mmcv_home - - -def load_state_dict(module, state_dict, strict=False, logger=None): - """Load state_dict to a module. - - This method is modified from :meth:`torch.nn.Module.load_state_dict`. - Default value for ``strict`` is set to ``False`` and the message for - param mismatch will be shown even if strict is False. - - Args: - module (Module): Module that receives the state_dict. - state_dict (OrderedDict): Weights. - strict (bool): whether to strictly enforce that the keys - in :attr:`state_dict` match the keys returned by this module's - :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. - logger (:obj:`logging.Logger`, optional): Logger to log the error - message. If not specified, print function will be used. - """ - unexpected_keys = [] - all_missing_keys = [] - err_msg = [] - - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata - - # use _load_from_state_dict to enable checkpoint version control - def load(module, prefix=''): - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - local_metadata = {} if metadata is None else metadata.get( - prefix[:-1], {}) - module._load_from_state_dict(state_dict, prefix, local_metadata, True, - all_missing_keys, unexpected_keys, - err_msg) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(module) - load = None # break load->load reference cycle - - # ignore "num_batches_tracked" of BN layers - missing_keys = [ - key for key in all_missing_keys if 'num_batches_tracked' not in key - ] - - if unexpected_keys: - err_msg.append('unexpected key in source ' - f'state_dict: {", ".join(unexpected_keys)}\n') - if missing_keys: - err_msg.append( - f'missing keys in source state_dict: {", ".join(missing_keys)}\n') - - rank, _ = get_dist_info() - if len(err_msg) > 0 and rank == 0: - err_msg.insert( - 0, 'The model and loaded state dict do not match exactly\n') - err_msg = '\n'.join(err_msg) - if strict: - raise RuntimeError(err_msg) - elif logger is not None: - logger.warning(err_msg) - else: - print(err_msg) - - -def get_torchvision_models(): - if digit_version(torchvision.__version__) < digit_version('0.13.0a0'): - model_urls = dict() - # When the version of torchvision is lower than 0.13, the model url is - # not declared in `torchvision.model.__init__.py`, so we need to - # iterate through `torchvision.models.__path__` to get the url for each - # model. - for _, name, ispkg in pkgutil.walk_packages( - torchvision.models.__path__): - if ispkg: - continue - _zoo = import_module(f'torchvision.models.{name}') - if hasattr(_zoo, 'model_urls'): - _urls = getattr(_zoo, 'model_urls') - model_urls.update(_urls) - else: - # Since torchvision bumps to v0.13, the weight loading logic, - # model keys and model urls have been changed. Here the URLs of old - # version is loaded to avoid breaking back compatibility. If the - # torchvision version>=0.13.0, new URLs will be added. Users can get - # the resnet50 checkpoint by setting 'resnet50.imagent1k_v1', - # 'resnet50' or 'ResNet50_Weights.IMAGENET1K_V1' in the config. - json_path = osp.join(mmcv.__path__[0], - 'model_zoo/torchvision_0.12.json') - model_urls = mmcv.load(json_path) - for cls_name, cls in torchvision.models.__dict__.items(): - # The name of torchvision model weights classes ends with - # `_Weights` such as `ResNet18_Weights`. However, some model weight - # classes, such as `MNASNet0_75_Weights` does not have any urls in - # torchvision 0.13.0 and cannot be iterated. Here we simply check - # `DEFAULT` attribute to ensure the class is not empty. - if (not cls_name.endswith('_Weights') - or not hasattr(cls, 'DEFAULT')): - continue - # Since `cls.DEFAULT` can not be accessed by iterating cls, we set - # default urls explicitly. - cls_key = cls_name.replace('_Weights', '').lower() - model_urls[f'{cls_key}.default'] = cls.DEFAULT.url - for weight_enum in cls: - cls_key = cls_name.replace('_Weights', '').lower() - cls_key = f'{cls_key}.{weight_enum.name.lower()}' - model_urls[cls_key] = weight_enum.url - - return model_urls - - -def get_external_models(): - mmcv_home = _get_mmcv_home() - default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') - default_urls = load_file(default_json_path) - assert isinstance(default_urls, dict) - external_json_path = osp.join(mmcv_home, 'open_mmlab.json') - if osp.exists(external_json_path): - external_urls = load_file(external_json_path) - assert isinstance(external_urls, dict) - default_urls.update(external_urls) - - return default_urls - - -def get_mmcls_models(): - mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') - mmcls_urls = load_file(mmcls_json_path) - - return mmcls_urls - - -def get_deprecated_model_names(): - deprecate_json_path = osp.join(mmcv.__path__[0], - 'model_zoo/deprecated.json') - deprecate_urls = load_file(deprecate_json_path) - assert isinstance(deprecate_urls, dict) - - return deprecate_urls - - -def _process_mmcls_checkpoint(checkpoint): - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - # Some checkpoints converted from 3rd-party repo don't - # have the "state_dict" key. - state_dict = checkpoint - new_state_dict = OrderedDict() - for k, v in state_dict.items(): - if k.startswith('backbone.'): - new_state_dict[k[9:]] = v - new_checkpoint = dict(state_dict=new_state_dict) - - return new_checkpoint - - -class CheckpointLoader: - """A general checkpoint loader to manage all schemes.""" - - _schemes = {} - - @classmethod - def _register_scheme(cls, prefixes, loader, force=False): - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if (prefix not in cls._schemes) or force: - cls._schemes[prefix] = loader - else: - raise KeyError( - f'{prefix} is already registered as a loader backend, ' - 'add "force=True" if you want to override it') - # sort, longer prefixes take priority - cls._schemes = OrderedDict( - sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) - - @classmethod - def register_scheme(cls, prefixes, loader=None, force=False): - """Register a loader to CheckpointLoader. - - This method can be used as a normal class method or a decorator. - - Args: - prefixes (str or list[str] or tuple[str]): - The prefix of the registered loader. - loader (function, optional): The loader function to be registered. - When this method is used as a decorator, loader is None. - Defaults to None. - force (bool, optional): Whether to override the loader - if the prefix has already been registered. Defaults to False. - """ - - if loader is not None: - cls._register_scheme(prefixes, loader, force=force) - return - - def _register(loader_cls): - cls._register_scheme(prefixes, loader_cls, force=force) - return loader_cls - - return _register - - @classmethod - def _get_checkpoint_loader(cls, path): - """Finds a loader that supports the given path. Falls back to the local - loader if no other loader is found. - - Args: - path (str): checkpoint path - - Returns: - callable: checkpoint loader - """ - for p in cls._schemes: - # use regular match to handle some cases that where the prefix of - # loader has a prefix. For example, both 's3://path' and - # 'open-mmlab:s3://path' should return `load_from_ceph` - if re.match(p, path) is not None: - return cls._schemes[p] - - @classmethod - def load_checkpoint(cls, filename, map_location=None, logger=None): - """load checkpoint through URL scheme path. - - Args: - filename (str): checkpoint file name with given prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - logger (:mod:`logging.Logger`, optional): The logger for message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint_loader = cls._get_checkpoint_loader(filename) - class_name = checkpoint_loader.__name__ - mmcv.print_log( - f'load checkpoint from {class_name[10:]} path: {filename}', logger) - return checkpoint_loader(filename, map_location) - - -@CheckpointLoader.register_scheme(prefixes='') -def load_from_local(filename, map_location): - """load checkpoint by local file path. - - Args: - filename (str): local checkpoint file path - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - filename = osp.expanduser(filename) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) -def load_from_http(filename, map_location=None, model_dir=None): - """load checkpoint through HTTP or HTTPS scheme path. In distributed - setting, this function only download checkpoint at local rank 0. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - model_dir (string, optional): directory in which to save the object, - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - rank, world_size = get_dist_info() - if rank == 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - if world_size > 1: - torch.distributed.barrier() - if rank > 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='pavi://') -def load_from_pavi(filename, map_location=None): - """load checkpoint through the file path prefixed with pavi. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with pavi prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - assert filename.startswith('pavi://'), \ - f'Expected filename startswith `pavi://`, but get {filename}' - model_path = filename[7:] - - try: - from pavi import modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - - model = modelcloud.get(model_path) - with TemporaryDirectory() as tmp_dir: - downloaded_file = osp.join(tmp_dir, model.name) - model.download(downloaded_file) - checkpoint = torch.load(downloaded_file, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=r'(\S+\:)?s3://') -def load_from_ceph(filename, map_location=None, backend='petrel'): - """load checkpoint through the file path prefixed with s3. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Note: - Since v1.4.1, the registered scheme prefixes have been enhanced to - support bucket names in the path prefix, e.g. 's3://xx.xx/xx.path', - 'bucket1:s3://xx.xx/xx.path'. - - Args: - filename (str): checkpoint file path with s3 prefix - map_location (str, optional): Same as :func:`torch.load`. - backend (str, optional): The storage backend type. Options are 'ceph', - 'petrel'. Default: 'petrel'. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - allowed_backends = ['ceph', 'petrel'] - if backend not in allowed_backends: - raise ValueError(f'Load from Backend {backend} is not supported.') - - if backend == 'ceph': - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - - # CephClient and PetrelBackend have the same prefix 's3://' and the latter - # will be chosen as default. If PetrelBackend can not be instantiated - # successfully, the CephClient will be chosen. - try: - file_client = FileClient(backend=backend) - except ImportError: - allowed_backends.remove(backend) - file_client = FileClient(backend=allowed_backends[0]) - - with io.BytesIO(file_client.get(filename)) as buffer: - checkpoint = torch.load(buffer, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) -def load_from_torchvision(filename, map_location=None): - """load checkpoint through the file path prefixed with modelzoo or - torchvision. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - model_urls = get_torchvision_models() - if filename.startswith('modelzoo://'): - warnings.warn( - 'The URL scheme of "modelzoo://" is deprecated, please ' - 'use "torchvision://" instead', DeprecationWarning) - model_name = filename[11:] - else: - model_name = filename[14:] - - # Support getting model urls in the same way as torchvision - # `ResNet50_Weights.IMAGENET1K_V1` will be mapped to - # resnet50.imagenet1k_v1. - model_name = model_name.lower().replace('_weights', '') - return load_from_http(model_urls[model_name], map_location=map_location) - - -@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) -def load_from_openmmlab(filename, map_location=None): - """load checkpoint through the file path prefixed with open-mmlab or - openmmlab. - - Args: - filename (str): checkpoint file path with open-mmlab or - openmmlab prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_external_models() - prefix_str = 'open-mmlab://' - if filename.startswith(prefix_str): - model_name = filename[13:] - else: - model_name = filename[12:] - prefix_str = 'openmmlab://' - - deprecated_urls = get_deprecated_model_names() - if model_name in deprecated_urls: - warnings.warn( - f'{prefix_str}{model_name} is deprecated in favor ' - f'of {prefix_str}{deprecated_urls[model_name]}', - DeprecationWarning) - model_name = deprecated_urls[model_name] - model_url = model_urls[model_name] - # check if is url - if model_url.startswith(('http://', 'https://')): - checkpoint = load_from_http(model_url, map_location=map_location) - else: - filename = osp.join(_get_mmcv_home(), model_url) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='mmcls://') -def load_from_mmcls(filename, map_location=None): - """load checkpoint through the file path prefixed with mmcls. - - Args: - filename (str): checkpoint file path with mmcls prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_mmcls_models() - model_name = filename[8:] - checkpoint = load_from_http( - model_urls[model_name], map_location=map_location) - checkpoint = _process_mmcls_checkpoint(checkpoint) - return checkpoint - - -def _load_checkpoint(filename, map_location=None, logger=None): - """Load checkpoint from somewhere (modelzoo, file, url). - - Args: - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str, optional): Same as :func:`torch.load`. - Default: None. - logger (:mod:`logging.Logger`, optional): The logger for error message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. It can be either an - OrderedDict storing model weights or a dict containing other - information, which depends on the checkpoint. - """ - return CheckpointLoader.load_checkpoint(filename, map_location, logger) - - -def _load_checkpoint_with_prefix(prefix, filename, map_location=None): - """Load partial pretrained model with specific prefix. - - Args: - prefix (str): The prefix of sub-module. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str | None): Same as :func:`torch.load`. Default: None. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint = _load_checkpoint(filename, map_location=map_location) - - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - if not prefix.endswith('.'): - prefix += '.' - prefix_len = len(prefix) - - state_dict = { - k[prefix_len:]: v - for k, v in state_dict.items() if k.startswith(prefix) - } - - assert state_dict, f'{prefix} is not in the pretrained model' - return state_dict - - -def load_checkpoint(model, - filename, - map_location=None, - strict=False, - logger=None, - revise_keys=[(r'^module\.', '')]): - """Load checkpoint from a file or URI. - - Args: - model (Module): Module to load checkpoint. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str): Same as :func:`torch.load`. - strict (bool): Whether to allow different params for the model and - checkpoint. - logger (:mod:`logging.Logger` or None): The logger for error message. - revise_keys (list): A list of customized keywords to modify the - state_dict in checkpoint. Each item is a (pattern, replacement) - pair of the regular expression operations. Default: strip - the prefix 'module.' by [(r'^module\\.', '')]. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - checkpoint = _load_checkpoint(filename, map_location, logger) - # OrderedDict is a subclass of dict - if not isinstance(checkpoint, dict): - raise RuntimeError( - f'No state_dict found in checkpoint file {filename}') - # get state_dict from checkpoint - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - - # strip prefix of state_dict - metadata = getattr(state_dict, '_metadata', OrderedDict()) - for p, r in revise_keys: - state_dict = OrderedDict( - {re.sub(p, r, k): v - for k, v in state_dict.items()}) - # Keep metadata in state_dict - state_dict._metadata = metadata - - # load state_dict - load_state_dict(model, state_dict, strict, logger) - return checkpoint - - -def weights_to_cpu(state_dict): - """Copy a model state_dict to cpu. - - Args: - state_dict (OrderedDict): Model weights on GPU. - - Returns: - OrderedDict: Model weights on GPU. - """ - state_dict_cpu = OrderedDict() - for key, val in state_dict.items(): - state_dict_cpu[key] = val.cpu() - # Keep metadata in state_dict - state_dict_cpu._metadata = getattr(state_dict, '_metadata', OrderedDict()) - return state_dict_cpu - - -def _save_to_state_dict(module, destination, prefix, keep_vars): - """Saves module state to `destination` dictionary. - - This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. - - Args: - module (nn.Module): The module to generate state_dict. - destination (dict): A dict where state will be stored. - prefix (str): The prefix for parameters and buffers used in this - module. - """ - for name, param in module._parameters.items(): - if param is not None: - destination[prefix + name] = param if keep_vars else param.detach() - for name, buf in module._buffers.items(): - # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d - if buf is not None: - destination[prefix + name] = buf if keep_vars else buf.detach() - - -def get_state_dict(module, destination=None, prefix='', keep_vars=False): - """Returns a dictionary containing a whole state of the module. - - Both parameters and persistent buffers (e.g. running averages) are - included. Keys are corresponding parameter and buffer names. - - This method is modified from :meth:`torch.nn.Module.state_dict` to - recursively check parallel module in case that the model has a complicated - structure, e.g., nn.Module(nn.Module(DDP)). - - Args: - module (nn.Module): The module to generate state_dict. - destination (OrderedDict): Returned dict for the state of the - module. - prefix (str): Prefix of the key. - keep_vars (bool): Whether to keep the variable property of the - parameters. Default: False. - - Returns: - dict: A dictionary containing a whole state of the module. - """ - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - - # below is the same as torch.nn.Module.state_dict() - if destination is None: - destination = OrderedDict() - destination._metadata = OrderedDict() - destination._metadata[prefix[:-1]] = local_metadata = dict( - version=module._version) - _save_to_state_dict(module, destination, prefix, keep_vars) - for name, child in module._modules.items(): - if child is not None: - get_state_dict( - child, destination, prefix + name + '.', keep_vars=keep_vars) - for hook in module._state_dict_hooks.values(): - hook_result = hook(module, destination, prefix, local_metadata) - if hook_result is not None: - destination = hook_result - return destination - - -def save_checkpoint(model, - filename, - optimizer=None, - meta=None, - file_client_args=None): - """Save checkpoint to file. - - The checkpoint will have 3 fields: ``meta``, ``state_dict`` and - ``optimizer``. By default ``meta`` will contain version and time info. - - Args: - model (Module): Module whose params are to be saved. - filename (str): Checkpoint filename. - optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. - meta (dict, optional): Metadata to be saved in checkpoint. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError(f'meta must be a dict or None, but got {type(meta)}') - meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) - - if is_module_wrapper(model): - model = model.module - - if hasattr(model, 'CLASSES') and model.CLASSES is not None: - # save class name to the meta - meta.update(CLASSES=model.CLASSES) - - checkpoint = { - 'meta': meta, - 'state_dict': weights_to_cpu(get_state_dict(model)) - } - # save optimizer state dict in the checkpoint - if isinstance(optimizer, Optimizer): - checkpoint['optimizer'] = optimizer.state_dict() - elif isinstance(optimizer, dict): - checkpoint['optimizer'] = {} - for name, optim in optimizer.items(): - checkpoint['optimizer'][name] = optim.state_dict() - - if filename.startswith('pavi://'): - if file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" if filename starts with' - f'"pavi://", but got {file_client_args}') - try: - from pavi import exception, modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - model_path = filename[7:] - root = modelcloud.Folder() - model_dir, model_name = osp.split(model_path) - try: - model = modelcloud.get(model_dir) - except exception.NodeNotFoundError: - model = root.create_training_model(model_dir) - with TemporaryDirectory() as tmp_dir: - checkpoint_file = osp.join(tmp_dir, model_name) - with open(checkpoint_file, 'wb') as f: - torch.save(checkpoint, f) - f.flush() - model.create_file(checkpoint_file, name=model_name) - else: - file_client = FileClient.infer_client(file_client_args, filename) - with io.BytesIO() as f: - torch.save(checkpoint, f) - file_client.put(f.getvalue(), filename) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/default_constructor.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/default_constructor.py deleted file mode 100644 index 4a4f2cc64..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/default_constructor.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import RUNNER_BUILDERS, RUNNERS - - -@RUNNER_BUILDERS.register_module() -class DefaultRunnerConstructor: - """Default constructor for runners. - - Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. - For example, We can inject some new properties and functions for `Runner`. - - Example: - >>> from mmcv.runner import RUNNER_BUILDERS, build_runner - >>> # Define a new RunnerReconstructor - >>> @RUNNER_BUILDERS.register_module() - >>> class MyRunnerConstructor: - ... def __init__(self, runner_cfg, default_args=None): - ... if not isinstance(runner_cfg, dict): - ... raise TypeError('runner_cfg should be a dict', - ... f'but got {type(runner_cfg)}') - ... self.runner_cfg = runner_cfg - ... self.default_args = default_args - ... - ... def __call__(self): - ... runner = RUNNERS.build(self.runner_cfg, - ... default_args=self.default_args) - ... # Add new properties for existing runner - ... runner.my_name = 'my_runner' - ... runner.my_function = lambda self: print(self.my_name) - ... ... - >>> # build your runner - >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, - ... constructor='MyRunnerConstructor') - >>> runner = build_runner(runner_cfg) - """ - - def __init__(self, runner_cfg, default_args=None): - if not isinstance(runner_cfg, dict): - raise TypeError('runner_cfg should be a dict', - f'but got {type(runner_cfg)}') - self.runner_cfg = runner_cfg - self.default_args = default_args - - def __call__(self): - return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/dist_utils.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/dist_utils.py deleted file mode 100644 index 26d77a8f9..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/dist_utils.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved -import functools -import os -import socket -import subprocess -from collections import OrderedDict - -import torch -import torch.multiprocessing as mp -from torch import distributed as dist -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import IS_MLU_AVAILABLE - - -def _find_free_port(): - # Copied from https://github.com/facebookresearch/detectron2/blob/main/detectron2/engine/launch.py # noqa: E501 - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - # Binding to port 0 will cause the OS to find an available port for us - sock.bind(('', 0)) - port = sock.getsockname()[1] - sock.close() - # NOTE: there is still a chance the port could be taken by other processes. - return port - - -def _is_free_port(port): - ips = socket.gethostbyname_ex(socket.gethostname())[-1] - ips.append('localhost') - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - return all(s.connect_ex((ip, port)) != 0 for ip in ips) - - -def init_dist(launcher, backend='nccl', **kwargs): - if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') - if launcher == 'pytorch': - _init_dist_pytorch(backend, **kwargs) - elif launcher == 'mpi': - _init_dist_mpi(backend, **kwargs) - elif launcher == 'slurm': - _init_dist_slurm(backend, **kwargs) - else: - raise ValueError(f'Invalid launcher type: {launcher}') - - -def _init_dist_pytorch(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['RANK']) - if IS_MLU_AVAILABLE: - import torch_mlu # noqa: F401 - torch.mlu.set_device(rank) - dist.init_process_group( - backend='cncl', - rank=rank, - world_size=int(os.environ['WORLD_SIZE']), - **kwargs) - else: - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_mpi(backend, **kwargs): - local_rank = int(os.environ['OMPI_COMM_WORLD_LOCAL_RANK']) - torch.cuda.set_device(local_rank) - if 'MASTER_PORT' not in os.environ: - # 29500 is torch.distributed default port - os.environ['MASTER_PORT'] = '29500' - if 'MASTER_ADDR' not in os.environ: - raise KeyError('The environment variable MASTER_ADDR is not set') - os.environ['WORLD_SIZE'] = os.environ['OMPI_COMM_WORLD_SIZE'] - os.environ['RANK'] = os.environ['OMPI_COMM_WORLD_RANK'] - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_slurm(backend, port=None): - """Initialize slurm distributed training environment. - - If argument ``port`` is not specified, then the master port will be system - environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system - environment variable, then a default port ``29500`` will be used. - - Args: - backend (str): Backend of torch.distributed. - port (int, optional): Master port. Defaults to None. - """ - proc_id = int(os.environ['SLURM_PROCID']) - ntasks = int(os.environ['SLURM_NTASKS']) - node_list = os.environ['SLURM_NODELIST'] - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(proc_id % num_gpus) - addr = subprocess.getoutput( - f'scontrol show hostname {node_list} | head -n1') - # specify master port - if port is not None: - os.environ['MASTER_PORT'] = str(port) - elif 'MASTER_PORT' in os.environ: - pass # use MASTER_PORT in the environment variable - else: - # if torch.distributed default port(29500) is available - # then use it, else find a free port - if _is_free_port(29500): - os.environ['MASTER_PORT'] = '29500' - else: - os.environ['MASTER_PORT'] = str(_find_free_port()) - # use MASTER_ADDR in the environment variable if it already exists - if 'MASTER_ADDR' not in os.environ: - os.environ['MASTER_ADDR'] = addr - os.environ['WORLD_SIZE'] = str(ntasks) - os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) - os.environ['RANK'] = str(proc_id) - dist.init_process_group(backend=backend) - - -def get_dist_info(): - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - world_size = dist.get_world_size() - else: - rank = 0 - world_size = 1 - return rank, world_size - - -def master_only(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - rank, _ = get_dist_info() - if rank == 0: - return func(*args, **kwargs) - - return wrapper - - -def allreduce_params(params, coalesce=True, bucket_size_mb=-1): - """Allreduce parameters. - - Args: - params (list[torch.Parameters]): List of parameters or buffers of a - model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - _, world_size = get_dist_info() - if world_size == 1: - return - params = [param.data for param in params] - if coalesce: - _allreduce_coalesced(params, world_size, bucket_size_mb) - else: - for tensor in params: - dist.all_reduce(tensor.div_(world_size)) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - _, world_size = get_dist_info() - if world_size == 1: - return - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/epoch_based_runner.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/epoch_based_runner.py deleted file mode 100644 index 078e91df3..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/epoch_based_runner.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .utils import get_host_info - - -@RUNNERS.register_module() -class EpochBasedRunner(BaseRunner): - """Epoch-based Runner. - - This runner train models epoch by epoch. - """ - - def run_iter(self, data_batch, train_mode, **kwargs): - if self.batch_processor is not None: - outputs = self.batch_processor( - self.model, data_batch, train_mode=train_mode, **kwargs) - elif train_mode: - outputs = self.model.train_step(data_batch, self.optimizer, - **kwargs) - else: - outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('"batch_processor()" or "model.train_step()"' - 'and "model.val_step()" must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - self._iter += 1 - - self.call_hook('after_train_epoch') - self._epoch += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - self.call_hook('before_val_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_val_iter') - self.run_iter(data_batch, train_mode=False) - self.call_hook('after_val_iter') - - self.call_hook('after_val_epoch') - - def run(self, data_loaders, workflow, max_epochs=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, epochs) to specify the - running order and epochs. E.g, [('train', 2), ('val', 1)] means - running 2 epochs for training and 1 epoch for validation, - iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_epochs is not None: - warnings.warn( - 'setting max_epochs in run is deprecated, ' - 'please set max_epochs in runner_config', DeprecationWarning) - self._max_epochs = max_epochs - - assert self._max_epochs is not None, ( - 'max_epochs must be specified during instantiation') - - for i, flow in enumerate(workflow): - mode, epochs = flow - if mode == 'train': - self._max_iters = self._max_epochs * len(data_loaders[i]) - break - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d epochs', workflow, - self._max_epochs) - self.call_hook('before_run') - - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - mode, epochs = flow - if isinstance(mode, str): # self.train() - if not hasattr(self, mode): - raise ValueError( - f'runner has no method named "{mode}" to run an ' - 'epoch') - epoch_runner = getattr(self, mode) - else: - raise TypeError( - 'mode in workflow must be a str, but got {}'.format( - type(mode))) - - for _ in range(epochs): - if mode == 'train' and self.epoch >= self._max_epochs: - break - epoch_runner(data_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_run') - - def save_checkpoint(self, - out_dir, - filename_tmpl='epoch_{}.pth', - save_optimizer=True, - meta=None, - create_symlink=True): - """Save the checkpoint. - - Args: - out_dir (str): The directory that checkpoints are saved. - filename_tmpl (str, optional): The checkpoint filename template, - which contains a placeholder for the epoch number. - Defaults to 'epoch_{}.pth'. - save_optimizer (bool, optional): Whether to save the optimizer to - the checkpoint. Defaults to True. - meta (dict, optional): The meta information to be saved in the - checkpoint. Defaults to None. - create_symlink (bool, optional): Whether to create a symlink - "latest.pth" to point to the latest checkpoint. - Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.epoch + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - -@RUNNERS.register_module() -class Runner(EpochBasedRunner): - """Deprecated name of EpochBasedRunner.""" - - def __init__(self, *args, **kwargs): - warnings.warn( - 'Runner was deprecated, please use EpochBasedRunner instead', - DeprecationWarning) - super().__init__(*args, **kwargs) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/fp16_utils.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/fp16_utils.py deleted file mode 100644 index be3ac3a31..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/fp16_utils.py +++ /dev/null @@ -1,423 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import warnings -from collections import abc -from inspect import getfullargspec - -import numpy as np -import torch -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .dist_utils import allreduce_grads as _allreduce_grads - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 - # manually, so the behavior may not be consistent with real amp. - from torch.cuda.amp import autocast -except ImportError: - pass - - -def cast_tensor_type(inputs, src_type, dst_type): - """Recursively convert Tensor in inputs from src_type to dst_type. - - Note: - In v1.4.4 and later, ``cast_tersor_type`` will only convert the - torch.Tensor which is consistent with ``src_type`` to the ``dst_type``. - Before v1.4.4, it ignores the ``src_type`` argument, leading to some - potential problems. For example, - ``cast_tensor_type(inputs, torch.float, torch.half)`` will convert all - tensors in inputs to ``torch.half`` including those originally in - ``torch.Int`` or other types, which is not expected. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype): Source type.. - dst_type (torch.dtype): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - if isinstance(inputs, nn.Module): - return inputs - elif isinstance(inputs, torch.Tensor): - # we need to ensure that the type of inputs to be casted are the same - # as the argument `src_type`. - return inputs.to(dst_type) if inputs.dtype == src_type else inputs - elif isinstance(inputs, str): - return inputs - elif isinstance(inputs, np.ndarray): - return inputs - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type, dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type, dst_type) for item in inputs) - else: - return inputs - - -def auto_fp16(apply_to=None, out_fp32=False, supported_types=(nn.Module, )): - """Decorator to enable fp16 training automatically. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If inputs arguments are fp32 tensors, they will - be converted to fp16 automatically. Arguments other than fp32 tensors are - ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp32 (bool): Whether to convert the output back to fp32. - supported_types (tuple): Classes can be decorated by ``auto_fp16``. - `New in version 1.5.0.` - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp16 - >>> @auto_fp16() - >>> def forward(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp16 - >>> @auto_fp16(apply_to=('pred', )) - >>> def do_something(self, pred, others): - >>> pass - """ - - def auto_fp16_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], supported_types): - raise TypeError('@auto_fp16 can only be used to decorate the ' - f'method of those classes {supported_types}') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - # NOTE: default args are not taken into consideration - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.float, torch.half)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = {} - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.float, torch.half) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=True): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp32: - output = cast_tensor_type(output, torch.half, torch.float) - return output - - return new_func - - return auto_fp16_wrapper - - -def force_fp32(apply_to=None, out_fp16=False): - """Decorator to convert input arguments to fp32 in force. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If there are some inputs that must be processed - in fp32 mode, then this decorator can handle it. If inputs arguments are - fp16 tensors, they will be converted to fp32 automatically. Arguments other - than fp16 tensors are ignored. If you are using PyTorch >= 1.6, - torch.cuda.amp is used as the backend, otherwise, original mmcv - implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp16 (bool): Whether to convert the output back to fp16. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp32 - >>> @force_fp32() - >>> def loss(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp32 - >>> @force_fp32(apply_to=('pred', )) - >>> def post_process(self, pred, others): - >>> pass - """ - - def force_fp32_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@force_fp32 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.half, torch.float)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = dict() - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.half, torch.float) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=False): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp16: - output = cast_tensor_type(output, torch.float, torch.half) - return output - - return new_func - - return force_fp32_wrapper - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - warnings.warning( - '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' - 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads', - DeprecationWarning) - _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) - - -def wrap_fp16_model(model): - """Wrap the FP32 model to FP16. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - For PyTorch >= 1.6, this function will - 1. Set fp16 flag inside the model to True. - - Otherwise: - 1. Convert FP32 model to FP16. - 2. Remain some necessary layers to be FP32, e.g., normalization layers. - 3. Set `fp16_enabled` flag inside the model to True. - - Args: - model (nn.Module): Model in FP32. - """ - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.6.0')): - # convert model to fp16 - model.half() - # patch the normalization layers to make it work in fp32 mode - patch_norm_fp32(model) - # set `fp16_enabled` flag - for m in model.modules(): - if hasattr(m, 'fp16_enabled'): - m.fp16_enabled = True - - -def patch_norm_fp32(module): - """Recursively convert normalization layers from FP16 to FP32. - - Args: - module (nn.Module): The modules to be converted in FP16. - - Returns: - nn.Module: The converted module, the normalization layers have been - converted to FP32. - """ - if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): - module.float() - if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': - module.forward = patch_forward_method(module.forward, torch.half, - torch.float) - for child in module.children(): - patch_norm_fp32(child) - return module - - -def patch_forward_method(func, src_type, dst_type, convert_output=True): - """Patch the forward method of a module. - - Args: - func (callable): The original forward method. - src_type (torch.dtype): Type of input arguments to be converted from. - dst_type (torch.dtype): Type of input arguments to be converted to. - convert_output (bool): Whether to convert the output back to src_type. - - Returns: - callable: The patched forward method. - """ - - def new_forward(*args, **kwargs): - output = func(*cast_tensor_type(args, src_type, dst_type), - **cast_tensor_type(kwargs, src_type, dst_type)) - if convert_output: - output = cast_tensor_type(output, dst_type, src_type) - return output - - return new_forward - - -class LossScaler: - """Class that manages loss scaling in mixed precision training which - supports both dynamic or static mode. - - The implementation refers to - https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. - Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. - It's important to understand how :class:`LossScaler` operates. - Loss scaling is designed to combat the problem of underflowing - gradients encountered at long times when training fp16 networks. - Dynamic loss scaling begins by attempting a very high loss - scale. Ironically, this may result in OVERflowing gradients. - If overflowing gradients are encountered, :class:`FP16_Optimizer` then - skips the update step for this particular iteration/minibatch, - and :class:`LossScaler` adjusts the loss scale to a lower value. - If a certain number of iterations occur without overflowing gradients - detected,:class:`LossScaler` increases the loss scale once more. - In this way :class:`LossScaler` attempts to "ride the edge" of always - using the highest loss scale possible without incurring overflow. - - Args: - init_scale (float): Initial loss scale value, default: 2**32. - scale_factor (float): Factor used when adjusting the loss scale. - Default: 2. - mode (str): Loss scaling mode. 'dynamic' or 'static' - scale_window (int): Number of consecutive iterations without an - overflow to wait before increasing the loss scale. Default: 1000. - """ - - def __init__(self, - init_scale=2**32, - mode='dynamic', - scale_factor=2., - scale_window=1000): - self.cur_scale = init_scale - self.cur_iter = 0 - assert mode in ('dynamic', - 'static'), 'mode can only be dynamic or static' - self.mode = mode - self.last_overflow_iter = -1 - self.scale_factor = scale_factor - self.scale_window = scale_window - - def has_overflow(self, params): - """Check if params contain overflow.""" - if self.mode != 'dynamic': - return False - for p in params: - if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): - return True - return False - - def _has_inf_or_nan(x): - """Check if params contain NaN.""" - try: - cpu_sum = float(x.float().sum()) - except RuntimeError as instance: - if 'value cannot be converted' not in instance.args[0]: - raise - return True - else: - if cpu_sum == float('inf') or cpu_sum == -float('inf') \ - or cpu_sum != cpu_sum: - return True - return False - - def update_scale(self, overflow): - """update the current loss scale value when overflow happens.""" - if self.mode != 'dynamic': - return - if overflow: - self.cur_scale = max(self.cur_scale / self.scale_factor, 1) - self.last_overflow_iter = self.cur_iter - else: - if (self.cur_iter - self.last_overflow_iter) % \ - self.scale_window == 0: - self.cur_scale *= self.scale_factor - self.cur_iter += 1 - - def state_dict(self): - """Returns the state of the scaler as a :class:`dict`.""" - return dict( - cur_scale=self.cur_scale, - cur_iter=self.cur_iter, - mode=self.mode, - last_overflow_iter=self.last_overflow_iter, - scale_factor=self.scale_factor, - scale_window=self.scale_window) - - def load_state_dict(self, state_dict): - """Loads the loss_scaler state dict. - - Args: - state_dict (dict): scaler state. - """ - self.cur_scale = state_dict['cur_scale'] - self.cur_iter = state_dict['cur_iter'] - self.mode = state_dict['mode'] - self.last_overflow_iter = state_dict['last_overflow_iter'] - self.scale_factor = state_dict['scale_factor'] - self.scale_window = state_dict['scale_window'] - - @property - def loss_scale(self): - return self.cur_scale diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/__init__.py deleted file mode 100644 index 03e2a619e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkpoint import CheckpointHook -from .closure import ClosureHook -from .ema import EMAHook -from .evaluation import DistEvalHook, EvalHook -from .hook import HOOKS, Hook -from .iter_timer import IterTimerHook -from .logger import (ClearMLLoggerHook, DvcliveLoggerHook, LoggerHook, - MlflowLoggerHook, NeptuneLoggerHook, PaviLoggerHook, - SegmindLoggerHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, InvLrUpdaterHook, - LinearAnnealingLrUpdaterHook, LrUpdaterHook, - OneCycleLrUpdaterHook, PolyLrUpdaterHook, - StepLrUpdaterHook) -from .memory import EmptyCacheHook -from .momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, OptimizerHook) -from .profiler import ProfilerHook -from .sampler_seed import DistSamplerSeedHook -from .sync_buffer import SyncBuffersHook - -__all__ = [ - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'OptimizerHook', - 'Fp16OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', - 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TextLoggerHook', 'TensorboardLoggerHook', 'NeptuneLoggerHook', - 'WandbLoggerHook', 'DvcliveLoggerHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'SyncBuffersHook', 'EMAHook', 'EvalHook', 'DistEvalHook', 'ProfilerHook', - 'GradientCumulativeOptimizerHook', 'GradientCumulativeFp16OptimizerHook', - 'SegmindLoggerHook', 'LinearAnnealingLrUpdaterHook', - 'LinearAnnealingMomentumUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/checkpoint.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/checkpoint.py deleted file mode 100644 index 7bb75f402..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/checkpoint.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -from mmcv.fileio import FileClient -from ..dist_utils import allreduce_params, master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class CheckpointHook(Hook): - """Save checkpoints periodically. - - Args: - interval (int): The saving period. If ``by_epoch=True``, interval - indicates epochs, otherwise it indicates iterations. - Default: -1, which means "never". - by_epoch (bool): Saving checkpoints by epoch or by iteration. - Default: True. - save_optimizer (bool): Whether to save optimizer state_dict in the - checkpoint. It is usually used for resuming experiments. - Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, ``runner.work_dir`` will be used by default. If - specified, the ``out_dir`` will be the concatenation of ``out_dir`` - and the last level directory of ``runner.work_dir``. - `Changed in version 1.3.16.` - max_keep_ckpts (int, optional): The maximum checkpoints to keep. - In some cases we want only the latest few checkpoints and would - like to delete old ones to save the disk space. - Default: -1, which means unlimited. - save_last (bool, optional): Whether to force the last checkpoint to be - saved regardless of interval. Default: True. - sync_buffer (bool, optional): Whether to synchronize buffers in - different gpus. Default: False. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - - .. warning:: - Before v1.3.16, the ``out_dir`` argument indicates the path where the - checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the - root directory and the final path to save checkpoint is the - concatenation of ``out_dir`` and the last level directory of - ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" - and the value of ``runner.work_dir`` is "/path/of/B", then the final - path will be "/path/of/A/B". - """ - - def __init__(self, - interval=-1, - by_epoch=True, - save_optimizer=True, - out_dir=None, - max_keep_ckpts=-1, - save_last=True, - sync_buffer=False, - file_client_args=None, - **kwargs): - self.interval = interval - self.by_epoch = by_epoch - self.save_optimizer = save_optimizer - self.out_dir = out_dir - self.max_keep_ckpts = max_keep_ckpts - self.save_last = save_last - self.args = kwargs - self.sync_buffer = sync_buffer - self.file_client_args = file_client_args - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - - runner.logger.info((f'Checkpoints will be saved to {self.out_dir} by ' - f'{self.file_client.name}.')) - - # disable the create_symlink option because some file backends do not - # allow to create a symlink - if 'create_symlink' in self.args: - if self.args[ - 'create_symlink'] and not self.file_client.allow_symlink: - self.args['create_symlink'] = False - warnings.warn( - ('create_symlink is set as True by the user but is changed' - 'to be False because creating symbolic link is not ' - f'allowed in {self.file_client.name}')) - else: - self.args['create_symlink'] = self.file_client.allow_symlink - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` epochs - # 2. reach the last epoch of training - if self.every_n_epochs( - runner, self.interval) or (self.save_last - and self.is_last_epoch(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.epoch + 1} epochs') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) - - @master_only - def _save_checkpoint(self, runner): - """Save the current checkpoint and delete unwanted checkpoint.""" - runner.save_checkpoint( - self.out_dir, save_optimizer=self.save_optimizer, **self.args) - if runner.meta is not None: - if self.by_epoch: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) - else: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) - runner.meta.setdefault('hook_msgs', dict()) - runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( - self.out_dir, cur_ckpt_filename) - # remove other checkpoints - if self.max_keep_ckpts > 0: - if self.by_epoch: - name = 'epoch_{}.pth' - current_ckpt = runner.epoch + 1 - else: - name = 'iter_{}.pth' - current_ckpt = runner.iter + 1 - redundant_ckpts = range( - current_ckpt - self.max_keep_ckpts * self.interval, 0, - -self.interval) - filename_tmpl = self.args.get('filename_tmpl', name) - for _step in redundant_ckpts: - ckpt_path = self.file_client.join_path( - self.out_dir, filename_tmpl.format(_step)) - if self.file_client.isfile(ckpt_path): - self.file_client.remove(ckpt_path) - else: - break - - def after_train_iter(self, runner): - if self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` iterations - # 2. reach the last iteration of training - if self.every_n_iters( - runner, self.interval) or (self.save_last - and self.is_last_iter(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.iter + 1} iterations') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/closure.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/closure.py deleted file mode 100644 index b955f81f4..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/closure.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ClosureHook(Hook): - - def __init__(self, fn_name, fn): - assert hasattr(self, fn_name) - assert callable(fn) - setattr(self, fn_name, fn) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/ema.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/ema.py deleted file mode 100644 index 6ed77b84e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/ema.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...parallel import is_module_wrapper -from ..hooks.hook import HOOKS, Hook - - -@HOOKS.register_module() -class EMAHook(Hook): - r"""Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. - - .. math:: - - Xema\_{t+1} = (1 - \text{momentum}) \times - Xema\_{t} + \text{momentum} \times X_t - - Args: - momentum (float): The momentum used for updating ema parameter. - Defaults to 0.0002. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - warm_up (int): During first warm_up steps, we may use smaller momentum - to update ema parameters more slowly. Defaults to 100. - resume_from (str): The checkpoint path. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - warm_up=100, - resume_from=None): - assert isinstance(interval, int) and interval > 0 - self.warm_up = warm_up - self.interval = interval - assert momentum > 0 and momentum < 1 - self.momentum = momentum**interval - self.checkpoint = resume_from - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - self.model_parameters = dict(model.named_parameters(recurse=True)) - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers(recurse=True)) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - curr_step = runner.iter - # We warm up the momentum considering the instability at beginning - momentum = min(self.momentum, - (1 + curr_step) / (self.warm_up + curr_step)) - if curr_step % self.interval != 0: - return - for name, parameter in self.model_parameters.items(): - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/evaluation.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/evaluation.py deleted file mode 100644 index 2a57d535e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/evaluation.py +++ /dev/null @@ -1,511 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from math import inf - -import torch.distributed as dist -from torch.nn.modules.batchnorm import _BatchNorm -from torch.utils.data import DataLoader - -from mmcv.fileio import FileClient -from mmcv.utils import is_seq_of -from .hook import Hook -from .logger import LoggerHook - - -class EvalHook(Hook): - """Non-Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader, and return the test results. If ``None``, the default - test function ``mmcv.engine.single_gpu_test`` will be used. - (default: ``None``) - greater_keys (List[str] | None, optional): Metric keys that will be - inferred by 'greater' comparison rule. If ``None``, - _default_greater_keys will be used. (default: ``None``) - less_keys (List[str] | None, optional): Metric keys that will be - inferred by 'less' comparison rule. If ``None``, _default_less_keys - will be used. (default: ``None``) - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - `New in version 1.3.16.` - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - `New in version 1.3.16.` - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - - Note: - If new arguments are added for EvalHook, tools/test.py, - tools/eval_metric.py may be affected. - """ - - # Since the key for determine greater or less is related to the downstream - # tasks, downstream repos may need to overwrite the following inner - # variable accordingly. - - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - _default_greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - _default_less_keys = ['loss'] - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - out_dir=None, - file_client_args=None, - **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError(f'dataloader must be a pytorch DataLoader, ' - f'but got {type(dataloader)}') - - if interval <= 0: - raise ValueError(f'interval must be a positive number, ' - f'but got {interval}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' - - if start is not None and start < 0: - raise ValueError(f'The evaluation start epoch {start} is smaller ' - f'than 0') - - self.dataloader = dataloader - self.interval = interval - self.start = start - self.by_epoch = by_epoch - - assert isinstance(save_best, str) or save_best is None, \ - '""save_best"" should be a str or None ' \ - f'rather than {type(save_best)}' - self.save_best = save_best - self.eval_kwargs = eval_kwargs - self.initial_flag = True - - if test_fn is None: - from mmcv.engine import single_gpu_test - self.test_fn = single_gpu_test - else: - self.test_fn = test_fn - - if greater_keys is None: - self.greater_keys = self._default_greater_keys - else: - if not isinstance(greater_keys, (list, tuple)): - greater_keys = (greater_keys, ) - assert is_seq_of(greater_keys, str) - self.greater_keys = greater_keys - - if less_keys is None: - self.less_keys = self._default_less_keys - else: - if not isinstance(less_keys, (list, tuple)): - less_keys = (less_keys, ) - assert is_seq_of(less_keys, str) - self.less_keys = less_keys - - if self.save_best is not None: - self.best_ckpt_path = None - self._init_rule(rule, self.save_best) - - self.out_dir = out_dir - self.file_client_args = file_client_args - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific (note that the key indicator matching - is case-insensitive): - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator != 'auto': - # `_lc` here means we use the lower case of keys for - # case-insensitive matching - key_indicator_lc = key_indicator.lower() - greater_keys = [key.lower() for key in self.greater_keys] - less_keys = [key.lower() for key in self.less_keys] - - if key_indicator_lc in greater_keys: - rule = 'greater' - elif key_indicator_lc in less_keys: - rule = 'less' - elif any(key in key_indicator_lc for key in greater_keys): - rule = 'greater' - elif any(key in key_indicator_lc for key in less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - if self.rule is not None: - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'The best checkpoint will be saved to {self.out_dir} by ' - f'{self.file_client.name}')) - - if self.save_best is not None: - if runner.meta is None: - warnings.warn('runner.meta is None. Creating an empty one.') - runner.meta = dict() - runner.meta.setdefault('hook_msgs', dict()) - self.best_ckpt_path = runner.meta['hook_msgs'].get( - 'best_ckpt', None) - - def before_train_iter(self, runner): - """Evaluate the model only at the start of training by iteration.""" - if self.by_epoch or not self.initial_flag: - return - if self.start is not None and runner.iter >= self.start: - self.after_train_iter(runner) - self.initial_flag = False - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - if not (self.by_epoch and self.initial_flag): - return - if self.start is not None and runner.epoch >= self.start: - self.after_train_epoch(runner) - self.initial_flag = False - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch and self._should_evaluate(runner): - # Because the priority of EvalHook is higher than LoggerHook, the - # training log and the evaluating log are mixed. Therefore, - # we need to dump the training log and clear it before evaluating - # log is generated. In addition, this problem will only appear in - # `IterBasedRunner` whose `self.by_epoch` is False, because - # `EpochBasedRunner` whose `self.by_epoch` is True calls - # `_do_evaluate` in `after_train_epoch` stage, and at this stage - # the training log has been printed, so it will not cause any - # problem. more details at - # https://github.com/open-mmlab/mmsegmentation/issues/694 - for hook in runner._hooks: - if isinstance(hook, LoggerHook): - hook.after_train_iter(runner) - runner.log_buffer.clear() - - self._do_evaluate(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch and self._should_evaluate(runner): - self._do_evaluate(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - results = self.test_fn(runner.model, self.dataloader) - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - def _should_evaluate(self, runner): - """Judge whether to perform evaluation. - - Here is the rule to judge whether to perform evaluation: - 1. It will not perform evaluation during the epoch/iteration interval, - which is determined by ``self.interval``. - 2. It will not perform evaluation if the start time is larger than - current time. - 3. It will not perform evaluation when current time is larger than - the start time but during epoch/iteration interval. - - Returns: - bool: The flag indicating whether to perform evaluation. - """ - if self.by_epoch: - current = runner.epoch - check_time = self.every_n_epochs - else: - current = runner.iter - check_time = self.every_n_iters - - if self.start is None: - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - elif (current + 1) < self.start: - # No evaluation if start is larger than the current time. - return False - else: - # Evaluation only at epochs/iters 3, 5, 7... - # if start==3 and interval==2 - if (current + 1 - self.start) % self.interval: - return False - return True - - def _save_ckpt(self, runner, key_score): - """Save the best checkpoint. - - It will compare the score according to the compare function, write - related information (best score, best checkpoint path) and save the - best checkpoint into ``work_dir``. - """ - if self.by_epoch: - current = f'epoch_{runner.epoch + 1}' - cur_type, cur_time = 'epoch', runner.epoch + 1 - else: - current = f'iter_{runner.iter + 1}' - cur_type, cur_time = 'iter', runner.iter + 1 - - best_score = runner.meta['hook_msgs'].get( - 'best_score', self.init_value_map[self.rule]) - if self.compare_func(key_score, best_score): - best_score = key_score - runner.meta['hook_msgs']['best_score'] = best_score - - if self.best_ckpt_path and self.file_client.isfile( - self.best_ckpt_path): - self.file_client.remove(self.best_ckpt_path) - runner.logger.info( - (f'The previous best checkpoint {self.best_ckpt_path} was ' - 'removed')) - - best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' - self.best_ckpt_path = self.file_client.join_path( - self.out_dir, best_ckpt_name) - runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path - - runner.save_checkpoint( - self.out_dir, - filename_tmpl=best_ckpt_name, - create_symlink=False) - runner.logger.info( - f'Now best checkpoint is saved as {best_ckpt_name}.') - runner.logger.info( - f'Best {self.key_indicator} is {best_score:0.4f} ' - f'at {cur_time} {cur_type}.') - - def evaluate(self, runner, results): - """Evaluate the results. - - Args: - runner (:obj:`mmcv.Runner`): The underlined training runner. - results (list): Output results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - - if self.save_best is not None: - # If the performance of model is pool, the `eval_res` may be an - # empty dict and it will raise exception when `self.save_best` is - # not None. More details at - # https://github.com/open-mmlab/mmdetection/issues/6265. - if not eval_res: - warnings.warn( - 'Since `eval_res` is an empty dict, the behavior to save ' - 'the best checkpoint will be skipped in this evaluation.') - return None - - if self.key_indicator == 'auto': - # infer from eval_results - self._init_rule(self.rule, list(eval_res.keys())[0]) - return eval_res[self.key_indicator] - - return None - - -class DistEvalHook(EvalHook): - """Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader in a multi-gpu manner, and return the test results. If - ``None``, the default test function ``mmcv.engine.multi_gpu_test`` - will be used. (default: ``None``) - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - broadcast_bn_buffer (bool): Whether to broadcast the - buffer(running_mean and running_var) of rank 0 to other rank - before evaluation. Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - """ - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - broadcast_bn_buffer=True, - tmpdir=None, - gpu_collect=False, - out_dir=None, - file_client_args=None, - **eval_kwargs): - - if test_fn is None: - from mmcv.engine import multi_gpu_test - test_fn = multi_gpu_test - - super().__init__( - dataloader, - start=start, - interval=interval, - by_epoch=by_epoch, - save_best=save_best, - rule=rule, - test_fn=test_fn, - greater_keys=greater_keys, - less_keys=less_keys, - out_dir=out_dir, - file_client_args=file_client_args, - **eval_kwargs) - - self.broadcast_bn_buffer = broadcast_bn_buffer - self.tmpdir = tmpdir - self.gpu_collect = gpu_collect - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - results = self.test_fn( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to - # save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/hook.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/hook.py deleted file mode 100644 index f2d1c9865..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/hook.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, is_method_overridden - -HOOKS = Registry('hook') - - -class Hook: - stages = ('before_run', 'before_train_epoch', 'before_train_iter', - 'after_train_iter', 'after_train_epoch', 'before_val_epoch', - 'before_val_iter', 'after_val_iter', 'after_val_epoch', - 'after_run') - - def before_run(self, runner): - pass - - def after_run(self, runner): - pass - - def before_epoch(self, runner): - pass - - def after_epoch(self, runner): - pass - - def before_iter(self, runner): - pass - - def after_iter(self, runner): - pass - - def before_train_epoch(self, runner): - self.before_epoch(runner) - - def before_val_epoch(self, runner): - self.before_epoch(runner) - - def after_train_epoch(self, runner): - self.after_epoch(runner) - - def after_val_epoch(self, runner): - self.after_epoch(runner) - - def before_train_iter(self, runner): - self.before_iter(runner) - - def before_val_iter(self, runner): - self.before_iter(runner) - - def after_train_iter(self, runner): - self.after_iter(runner) - - def after_val_iter(self, runner): - self.after_iter(runner) - - def every_n_epochs(self, runner, n): - return (runner.epoch + 1) % n == 0 if n > 0 else False - - def every_n_inner_iters(self, runner, n): - return (runner.inner_iter + 1) % n == 0 if n > 0 else False - - def every_n_iters(self, runner, n): - return (runner.iter + 1) % n == 0 if n > 0 else False - - def end_of_epoch(self, runner): - return runner.inner_iter + 1 == len(runner.data_loader) - - def is_last_epoch(self, runner): - return runner.epoch + 1 == runner._max_epochs - - def is_last_iter(self, runner): - return runner.iter + 1 == runner._max_iters - - def get_triggered_stages(self): - trigger_stages = set() - for stage in Hook.stages: - if is_method_overridden(stage, Hook, self): - trigger_stages.add(stage) - - # some methods will be triggered in multi stages - # use this dict to map method to stages. - method_stages_map = { - 'before_epoch': ['before_train_epoch', 'before_val_epoch'], - 'after_epoch': ['after_train_epoch', 'after_val_epoch'], - 'before_iter': ['before_train_iter', 'before_val_iter'], - 'after_iter': ['after_train_iter', 'after_val_iter'], - } - - for method, map_stages in method_stages_map.items(): - if is_method_overridden(method, Hook, self): - trigger_stages.update(map_stages) - - return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/iter_timer.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/iter_timer.py deleted file mode 100644 index 16531be96..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/iter_timer.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import time -import torch.distributed as dist - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class IterTimerHook(Hook): - - def before_epoch(self, runner): - self.t = time.time() - - def before_iter(self, runner): - self.batch_size = runner.cfg.data.samples_per_gpu - runner.log_buffer.update({'data_time': time.time() - self.t}) - - def after_iter(self, runner): - iter_info = {'time': time.time() - self.t} - fps = self.batch_size / iter_info["time"] - - if dist.is_initialized(): - fps = fps * dist.get_world_size() - - iter_info["fps"] = fps - - runner.log_buffer.update(iter_info) - self.t = time.time() diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/__init__.py deleted file mode 100644 index 062709e70..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import LoggerHook -from .clearml import ClearMLLoggerHook -from .dvclive import DvcliveLoggerHook -from .mlflow import MlflowLoggerHook -from .neptune import NeptuneLoggerHook -from .pavi import PaviLoggerHook -from .segmind import SegmindLoggerHook -from .tensorboard import TensorboardLoggerHook -from .text import TextLoggerHook -from .wandb import WandbLoggerHook - -__all__ = [ - 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', - 'NeptuneLoggerHook', 'DvcliveLoggerHook', 'SegmindLoggerHook', - 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/base.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/base.py deleted file mode 100644 index 9f1a51b31..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/base.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from abc import ABCMeta, abstractmethod - -import numpy as np -import torch - -from ..hook import Hook - - -class LoggerHook(Hook): - """Base class for logger hooks. - - Args: - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - """ - - __metaclass__ = ABCMeta - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - self.interval = interval - self.ignore_last = ignore_last - self.reset_flag = reset_flag - self.by_epoch = by_epoch - - @abstractmethod - def log(self, runner): - pass - - @staticmethod - def is_scalar(val, include_np=True, include_torch=True): - """Tell the input variable is a scalar or not. - - Args: - val: Input variable. - include_np (bool): Whether include 0-d np.ndarray as a scalar. - include_torch (bool): Whether include 0-d torch.Tensor as a scalar. - - Returns: - bool: True or False. - """ - if isinstance(val, numbers.Number): - return True - elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: - return True - elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: - return True - else: - return False - - def get_mode(self, runner): - if runner.mode == 'train': - if 'time' in runner.log_buffer.output: - mode = 'train' - else: - mode = 'val' - elif runner.mode == 'val': - mode = 'val' - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return mode - - def get_epoch(self, runner): - if runner.mode == 'train': - epoch = runner.epoch + 1 - elif runner.mode == 'val': - # normal val mode - # runner.epoch += 1 has been done before val workflow - epoch = runner.epoch - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return epoch - - def get_iter(self, runner, inner_iter=False): - """Get the current training iteration step.""" - if self.by_epoch and inner_iter: - current_iter = runner.inner_iter + 1 - else: - current_iter = runner.iter + 1 - return current_iter - - def get_lr_tags(self, runner): - tags = {} - lrs = runner.current_lr() - if isinstance(lrs, dict): - for name, value in lrs.items(): - tags[f'learning_rate/{name}'] = value[0] - else: - tags['learning_rate'] = lrs[0] - return tags - - def get_momentum_tags(self, runner): - tags = {} - momentums = runner.current_momentum() - if isinstance(momentums, dict): - for name, value in momentums.items(): - tags[f'momentum/{name}'] = value[0] - else: - tags['momentum'] = momentums[0] - return tags - - def get_loggable_tags(self, - runner, - allow_scalar=True, - allow_text=False, - add_mode=True, - tags_to_skip=('time', 'data_time')): - tags = {} - for var, val in runner.log_buffer.output.items(): - if var in tags_to_skip: - continue - if self.is_scalar(val) and not allow_scalar: - continue - if isinstance(val, str) and not allow_text: - continue - if add_mode: - var = f'{self.get_mode(runner)}/{var}' - tags[var] = val - tags.update(self.get_lr_tags(runner)) - tags.update(self.get_momentum_tags(runner)) - return tags - - def before_run(self, runner): - for hook in runner.hooks[::-1]: - if isinstance(hook, LoggerHook): - hook.reset_flag = True - break - - def before_epoch(self, runner): - runner.log_buffer.clear() # clear logs of last epoch - - def after_train_iter(self, runner): - if self.by_epoch and self.every_n_inner_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif not self.by_epoch and self.every_n_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif self.end_of_epoch(runner) and not self.ignore_last: - # not precise but more stable - runner.log_buffer.average(self.interval) - - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_train_epoch(self, runner): - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_val_epoch(self, runner): - runner.log_buffer.average() - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/clearml.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/clearml.py deleted file mode 100644 index 1de71e152..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/clearml.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class ClearMLLoggerHook(LoggerHook): - """Class to log metrics with clearml. - - It requires `clearml`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the `clearml.Task.init` - initialization keys. See `taskinit`_ for more details. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _clearml: - https://clear.ml/docs/latest/docs/ - .. _taskinit: - https://clear.ml/docs/latest/docs/references/sdk/task/#taskinit - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(ClearMLLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_clearml() - self.init_kwargs = init_kwargs - - def import_clearml(self): - try: - import clearml - except ImportError: - raise ImportError( - 'Please run "pip install clearml" to install clearml') - self.clearml = clearml - - @master_only - def before_run(self, runner): - super(ClearMLLoggerHook, self).before_run(runner) - task_kwargs = self.init_kwargs if self.init_kwargs else {} - self.task = self.clearml.Task.init(**task_kwargs) - self.task_logger = self.task.get_logger() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - for tag, val in tags.items(): - self.task_logger.report_scalar(tag, tag, val, - self.get_iter(runner)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/dvclive.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/dvclive.py deleted file mode 100644 index c79eefa75..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/dvclive.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from pathlib import Path - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class DvcliveLoggerHook(LoggerHook): - """Class to log metrics with dvclive. - - It requires `dvclive`_ to be installed. - - Args: - model_file (str): Default None. If not None, after each epoch the - model will be saved to {model_file}. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - kwargs: Arguments for instantiating `Live`_. - - .. _dvclive: - https://dvc.org/doc/dvclive - - .. _Live: - https://dvc.org/doc/dvclive/api-reference/live#parameters - """ - - def __init__(self, - model_file=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - **kwargs): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.model_file = model_file - self.import_dvclive(**kwargs) - - def import_dvclive(self, **kwargs): - try: - from dvclive import Live - except ImportError: - raise ImportError( - 'Please run "pip install dvclive" to install dvclive') - self.dvclive = Live(**kwargs) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.dvclive.set_step(self.get_iter(runner)) - for k, v in tags.items(): - self.dvclive.log(k, v) - - @master_only - def after_train_epoch(self, runner): - super().after_train_epoch(runner) - if self.model_file is not None: - runner.save_checkpoint( - Path(self.model_file).parent, - filename_tmpl=Path(self.model_file).name, - create_symlink=False, - ) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/mlflow.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/mlflow.py deleted file mode 100644 index dcd87bcb5..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/mlflow.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import TORCH_VERSION -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class MlflowLoggerHook(LoggerHook): - """Class to log metrics and (optionally) a trained model to MLflow. - - It requires `MLflow`_ to be installed. - - Args: - exp_name (str, optional): Name of the experiment to be used. - Default None. If not None, set the active experiment. - If experiment does not exist, an experiment with provided name - will be created. - tags (Dict[str], optional): Tags for the current run. - Default None. If not None, set tags for the current run. - log_model (bool, optional): Whether to log an MLflow artifact. - Default True. If True, log runner.model as an MLflow artifact - for the current run. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _MLflow: - https://www.mlflow.org/docs/latest/index.html - """ - - def __init__(self, - exp_name=None, - tags=None, - log_model=True, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(MlflowLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_mlflow() - self.exp_name = exp_name - self.tags = tags - self.log_model = log_model - - def import_mlflow(self): - try: - import mlflow - import mlflow.pytorch as mlflow_pytorch - except ImportError: - raise ImportError( - 'Please run "pip install mlflow" to install mlflow') - self.mlflow = mlflow - self.mlflow_pytorch = mlflow_pytorch - - @master_only - def before_run(self, runner): - super(MlflowLoggerHook, self).before_run(runner) - if self.exp_name is not None: - self.mlflow.set_experiment(self.exp_name) - if self.tags is not None: - self.mlflow.set_tags(self.tags) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.mlflow.log_metrics(tags, step=self.get_iter(runner)) - - @master_only - def after_run(self, runner): - if self.log_model: - self.mlflow_pytorch.log_model( - runner.model, - 'models', - pip_requirements=[f'torch=={TORCH_VERSION}']) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/neptune.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/neptune.py deleted file mode 100644 index e0aafe91d..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/neptune.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class NeptuneLoggerHook(LoggerHook): - """Class to log metrics to NeptuneAI. - - It requires `Neptune`_ to be installed. - - Args: - init_kwargs (dict): a dict contains the initialization keys as below: - - - project (str): Name of a project in a form of - namespace/project_name. If None, the value of NEPTUNE_PROJECT - environment variable will be taken. - - api_token (str): User’s API token. If None, the value of - NEPTUNE_API_TOKEN environment variable will be taken. Note: It is - strongly recommended to use NEPTUNE_API_TOKEN environment - variable rather than placing your API token in plain text in your - source code. - - name (str, optional, default is 'Untitled'): Editable name of the - run. Name is displayed in the run's Details and in Runs table as - a column. - - Check https://docs.neptune.ai/api-reference/neptune#init for more - init arguments. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than ``interval``. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _Neptune: - https://docs.neptune.ai - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=True, - with_step=True, - by_epoch=True): - - super(NeptuneLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_neptune() - self.init_kwargs = init_kwargs - self.with_step = with_step - - def import_neptune(self): - try: - import neptune.new as neptune - except ImportError: - raise ImportError( - 'Please run "pip install neptune-client" to install neptune') - self.neptune = neptune - self.run = None - - @master_only - def before_run(self, runner): - if self.init_kwargs: - self.run = self.neptune.init(**self.init_kwargs) - else: - self.run = self.neptune.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for tag_name, tag_value in tags.items(): - if self.with_step: - self.run[tag_name].log( - tag_value, step=self.get_iter(runner)) - else: - tags['global_step'] = self.get_iter(runner) - self.run[tag_name].log(tags) - - @master_only - def after_run(self, runner): - self.run.stop() diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/pavi.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/pavi.py deleted file mode 100644 index d5d61f9e5..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/pavi.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp - -import torch -import yaml - -import mmcv -from ....parallel.utils import is_module_wrapper -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class PaviLoggerHook(LoggerHook): - """Class to visual model, log metrics (for internal use). - - Args: - init_kwargs (dict): A dict contains the initialization keys. - add_graph (bool): Whether to visual model. Default: False. - add_last_ckpt (bool): Whether to save checkpoint after run. - Default: False. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - img_key (string): Get image data from Dataset. Default: 'img_info'. - """ - - def __init__(self, - init_kwargs=None, - add_graph=False, - add_last_ckpt=False, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - img_key='img_info'): - super(PaviLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.init_kwargs = init_kwargs - self.add_graph = add_graph - self.add_last_ckpt = add_last_ckpt - self.img_key = img_key - - @master_only - def before_run(self, runner): - super(PaviLoggerHook, self).before_run(runner) - try: - from pavi import SummaryWriter - except ImportError: - raise ImportError('Please run "pip install pavi" to install pavi.') - - self.run_name = runner.work_dir.split('/')[-1] - - if not self.init_kwargs: - self.init_kwargs = dict() - self.init_kwargs['name'] = self.run_name - self.init_kwargs['model'] = runner._model_name - if runner.meta is not None: - if 'config_dict' in runner.meta: - config_dict = runner.meta['config_dict'] - assert isinstance( - config_dict, - dict), ('meta["config_dict"] has to be of a dict, ' - f'but got {type(config_dict)}') - elif 'config_file' in runner.meta: - config_file = runner.meta['config_file'] - config_dict = dict(mmcv.Config.fromfile(config_file)) - else: - config_dict = None - if config_dict is not None: - # 'max_.*iter' is parsed in pavi sdk as the maximum iterations - # to properly set up the progress bar. - config_dict = config_dict.copy() - config_dict.setdefault('max_iter', runner.max_iters) - # non-serializable values are first converted in - # mmcv.dump to json - config_dict = json.loads( - mmcv.dump(config_dict, file_format='json')) - session_text = yaml.dump(config_dict) - self.init_kwargs['session_text'] = session_text - self.writer = SummaryWriter(**self.init_kwargs) - - def get_step(self, runner): - """Get the total training step/epoch.""" - if self.get_mode(runner) == 'val' and self.by_epoch: - return self.get_epoch(runner) - else: - return self.get_iter(runner) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, add_mode=False) - if tags: - self.writer.add_scalars( - self.get_mode(runner), tags, self.get_step(runner)) - - @master_only - def after_run(self, runner): - if self.add_last_ckpt: - ckpt_path = osp.join(runner.work_dir, 'latest.pth') - if osp.islink(ckpt_path): - ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) - - if osp.isfile(ckpt_path): - # runner.epoch += 1 has been done before `after_run`. - iteration = runner.epoch if self.by_epoch else runner.iter - return self.writer.add_snapshot_file( - tag=self.run_name, - snapshot_file_path=ckpt_path, - iteration=iteration) - - # flush the buffer and send a task ending signal to Pavi - self.writer.close() - - @master_only - def before_epoch(self, runner): - if runner.epoch == 0 and self.add_graph: - if is_module_wrapper(runner.model): - _model = runner.model.module - else: - _model = runner.model - device = next(_model.parameters()).device - data = next(iter(runner.data_loader)) - image = data[self.img_key][0:1].to(device) - with torch.no_grad(): - self.writer.add_graph(_model, image) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/segmind.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/segmind.py deleted file mode 100644 index e262c7c1a..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/segmind.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class SegmindLoggerHook(LoggerHook): - """Class to log metrics to Segmind. - - It requires `Segmind`_ to be installed. - - Args: - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - - .. _Segmind: - https://docs.segmind.com/python-library - """ - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(SegmindLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_segmind() - - def import_segmind(self): - try: - import segmind - except ImportError: - raise ImportError( - "Please run 'pip install segmind' to install segmind") - self.log_metrics = segmind.tracking.fluent.log_metrics - self.mlflow_log = segmind.utils.logging_utils.try_mlflow_log - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - # logging metrics to segmind - self.mlflow_log( - self.log_metrics, tags, step=runner.epoch, epoch=runner.epoch) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/tensorboard.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/tensorboard.py deleted file mode 100644 index bf00d5742..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/tensorboard.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import TORCH_VERSION, digit_version -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TensorboardLoggerHook(LoggerHook): - """Class to log metrics to Tensorboard. - - Args: - log_dir (string): Save directory location. Default: None. If default - values are used, directory location is ``runner.work_dir``/tf_logs. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - """ - - def __init__(self, - log_dir=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(TensorboardLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.log_dir = log_dir - - @master_only - def before_run(self, runner): - super(TensorboardLoggerHook, self).before_run(runner) - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.1')): - try: - from tensorboardX import SummaryWriter - except ImportError: - raise ImportError('Please install tensorboardX to use ' - 'TensorboardLoggerHook.') - else: - try: - from torch.utils.tensorboard import SummaryWriter - except ImportError: - raise ImportError( - 'Please run "pip install future tensorboard" to install ' - 'the dependencies to use torch.utils.tensorboard ' - '(applicable to PyTorch 1.1 or higher)') - - if self.log_dir is None: - self.log_dir = osp.join(runner.work_dir, 'tf_logs') - self.writer = SummaryWriter(self.log_dir) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, allow_text=True) - for tag, val in tags.items(): - if isinstance(val, str): - self.writer.add_text(tag, val, self.get_iter(runner)) - else: - self.writer.add_scalar(tag, val, self.get_iter(runner)) - - @master_only - def after_run(self, runner): - self.writer.close() diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/text.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/text.py deleted file mode 100644 index 644ced2c9..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/text.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import datetime -import os -import os.path as osp -from collections import OrderedDict - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.fileio.file_client import FileClient -from mmcv.utils import is_tuple_of, scandir -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TextLoggerHook(LoggerHook): - """Logger hook in text. - - In this logger hook, the information will be printed on terminal and - saved in json file. - - Args: - by_epoch (bool, optional): Whether EpochBasedRunner is used. - Default: True. - interval (int, optional): Logging interval (every k iterations). - Default: 10. - ignore_last (bool, optional): Ignore the log of last iterations in each - epoch if less than :attr:`interval`. Default: True. - reset_flag (bool, optional): Whether to clear the output buffer after - logging. Default: False. - interval_exp_name (int, optional): Logging interval for experiment - name. This feature is to help users conveniently get the experiment - information from screen or log file. Default: 1000. - out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. - If ``out_dir`` is specified, logs will be copied to a new directory - which is the concatenation of ``out_dir`` and the last level - directory of ``runner.work_dir``. Default: None. - `New in version 1.3.16.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be copied to ``out_dir``. - Default: ('.log.json', '.log', '.py'). - `New in version 1.3.16.` - keep_local (bool, optional): Whether to keep local log when - :attr:`out_dir` is specified. If False, the local log will be - removed. Default: True. - `New in version 1.3.16.` - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - - def __init__(self, - by_epoch=True, - interval=10, - ignore_last=True, - reset_flag=False, - interval_exp_name=1000, - out_dir=None, - out_suffix=('.log.json', '.log', '.py'), - keep_local=True, - file_client_args=None): - super(TextLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.by_epoch = by_epoch - self.time_sec_tot = 0 - self.interval_exp_name = interval_exp_name - - if out_dir is None and file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" when `out_dir` is not' - 'specified.') - self.out_dir = out_dir - - if not (out_dir is None or isinstance(out_dir, str) - or is_tuple_of(out_dir, str)): - raise TypeError('out_dir should be "None" or string or tuple of ' - 'string, but got {out_dir}') - self.out_suffix = out_suffix - - self.keep_local = keep_local - self.file_client_args = file_client_args - if self.out_dir is not None: - self.file_client = FileClient.infer_client(file_client_args, - self.out_dir) - - def before_run(self, runner): - super(TextLoggerHook, self).before_run(runner) - - if self.out_dir is not None: - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - # The final `self.out_dir` is the concatenation of `self.out_dir` - # and the last level directory of `runner.work_dir` - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'Text logs will be saved to {self.out_dir} by ' - f'{self.file_client.name} after the training process.')) - - self.start_iter = runner.iter - self.json_log_path = osp.join(runner.work_dir, - f'{runner.timestamp}.log.json') - if runner.meta is not None: - self._dump_log(runner.meta, runner) - - def _get_max_memory(self, runner): - device = getattr(runner.model, 'output_device', None) - mem = torch.cuda.max_memory_allocated(device=device) - mem_mb = torch.tensor([int(mem) // (1024 * 1024)], - dtype=torch.int, - device=device) - if runner.world_size > 1: - dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) - return mem_mb.item() - - def _log_info(self, log_dict, runner): - # print exp name for users to distinguish experiments - # at every ``interval_exp_name`` iterations and the end of each epoch - if runner.meta is not None and 'exp_name' in runner.meta: - if (self.every_n_iters(runner, self.interval_exp_name)) or ( - self.by_epoch and self.end_of_epoch(runner)): - exp_info = f'Exp name: {runner.meta["exp_name"]}' - runner.logger.info(exp_info) - - if log_dict['mode'] == 'train': - if isinstance(log_dict['lr'], dict): - lr_str = [] - for k, val in log_dict['lr'].items(): - lr_str.append(f'lr_{k}: {val:.3e}') - lr_str = ' '.join(lr_str) - else: - lr_str = f'lr: {log_dict["lr"]:.3e}' - - # by epoch: Epoch [4][100/1000] - # by iter: Iter [100/100000] - if self.by_epoch: - log_str = f'Epoch [{log_dict["epoch"]}]' \ - f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' - else: - log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' - log_str += f'{lr_str}, ' - - if 'time' in log_dict.keys(): - self.time_sec_tot += (log_dict['time'] * self.interval) - time_sec_avg = self.time_sec_tot / ( - runner.iter - self.start_iter + 1) - eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) - eta_str = str(datetime.timedelta(seconds=int(eta_sec))) - log_str += f'eta: {eta_str}, ' - log_str += f'time: {log_dict["time"]:.3f}, ' \ - f'data_time: {log_dict["data_time"]:.3f}, ' - # statistic memory - if torch.cuda.is_available(): - log_str += f'memory: {log_dict["memory"]}, ' - else: - # val/test time - # here 1000 is the length of the val dataloader - # by epoch: Epoch[val] [4][1000] - # by iter: Iter[val] [1000] - if self.by_epoch: - log_str = f'Epoch({log_dict["mode"]}) ' \ - f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' - else: - log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' - - log_items = [] - for name, val in log_dict.items(): - # TODO: resolve this hack - # these items have been in log_str - if name in [ - 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', - 'memory', 'epoch' - ]: - continue - if isinstance(val, float): - val = f'{val:.4f}' - log_items.append(f'{name}: {val}') - log_str += ', '.join(log_items) - - runner.logger.info(log_str) - - def _dump_log(self, log_dict, runner): - # dump log in json format - json_log = OrderedDict() - for k, v in log_dict.items(): - json_log[k] = self._round_float(v) - # only append log at last line - if runner.rank == 0: - with open(self.json_log_path, 'a+') as f: - mmcv.dump(json_log, f, file_format='json') - f.write('\n') - - def _round_float(self, items): - if isinstance(items, list): - return [self._round_float(item) for item in items] - elif isinstance(items, float): - return round(items, 5) - else: - return items - - def log(self, runner): - if 'eval_iter_num' in runner.log_buffer.output: - # this doesn't modify runner.iter and is regardless of by_epoch - cur_iter = runner.log_buffer.output.pop('eval_iter_num') - else: - cur_iter = self.get_iter(runner, inner_iter=True) - - log_dict = OrderedDict( - mode=self.get_mode(runner), - epoch=self.get_epoch(runner), - iter=cur_iter) - - # only record lr of the first param group - cur_lr = runner.current_lr() - if isinstance(cur_lr, list): - log_dict['lr'] = cur_lr[0] - else: - assert isinstance(cur_lr, dict) - log_dict['lr'] = {} - for k, lr_ in cur_lr.items(): - assert isinstance(lr_, list) - log_dict['lr'].update({k: lr_[0]}) - - if 'time' in runner.log_buffer.output: - # statistic memory - if torch.cuda.is_available(): - log_dict['memory'] = self._get_max_memory(runner) - - log_dict = dict(log_dict, **runner.log_buffer.output) - - self._log_info(log_dict, runner) - self._dump_log(log_dict, runner) - return log_dict - - def after_run(self, runner): - # copy or upload logs to self.out_dir - if self.out_dir is not None: - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - out_filepath = self.file_client.join_path( - self.out_dir, filename) - with open(local_filepath, 'r') as f: - self.file_client.put_text(f.read(), out_filepath) - - runner.logger.info( - (f'The file {local_filepath} has been uploaded to ' - f'{out_filepath}.')) - - if not self.keep_local: - os.remove(local_filepath) - runner.logger.info( - (f'{local_filepath} was removed due to the ' - '`self.keep_local=False`')) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/wandb.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/wandb.py deleted file mode 100644 index 78b890ee1..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/logger/wandb.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import scandir -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class WandbLoggerHook(LoggerHook): - """Class to log metrics with wandb. - - It requires `wandb`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the initialization keys. Check - https://docs.wandb.ai/ref/python/init for more init arguments. - interval (int): Logging interval (every k iterations). - Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - commit (bool): Save the metrics dict to the wandb server and increment - the step. If false ``wandb.log`` just updates the current metrics - dict with the row argument and metrics won't be saved until - ``wandb.log`` is called with ``commit=True``. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - log_artifact (bool): If True, artifacts in {work_dir} will be uploaded - to wandb after training ends. - Default: True - `New in version 1.4.3.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be uploaded to wandb. - Default: ('.log.json', '.log', '.py'). - `New in version 1.4.3.` - - .. _wandb: - https://docs.wandb.ai - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - commit=True, - by_epoch=True, - with_step=True, - log_artifact=True, - out_suffix=('.log.json', '.log', '.py')): - super(WandbLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_wandb() - self.init_kwargs = init_kwargs - self.commit = commit - self.with_step = with_step - self.log_artifact = log_artifact - self.out_suffix = out_suffix - - def import_wandb(self): - try: - import wandb - except ImportError: - raise ImportError( - 'Please run "pip install wandb" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(WandbLoggerHook, self).before_run(runner) - if self.wandb is None: - self.import_wandb() - if self.init_kwargs: - self.wandb.init(**self.init_kwargs) - else: - self.wandb.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - if self.with_step: - self.wandb.log( - tags, step=self.get_iter(runner), commit=self.commit) - else: - tags['global_step'] = self.get_iter(runner) - self.wandb.log(tags, commit=self.commit) - - @master_only - def after_run(self, runner): - if self.log_artifact: - wandb_artifact = self.wandb.Artifact( - name='artifacts', type='model') - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - wandb_artifact.add_file(local_filepath) - self.wandb.log_artifact(wandb_artifact) - self.wandb.join() diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/lr_updater.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/lr_updater.py deleted file mode 100644 index ee2a53a65..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/lr_updater.py +++ /dev/null @@ -1,730 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from math import cos, pi - -import mmcv -from .hook import HOOKS, Hook - - -class LrUpdaterHook(Hook): - """LR Scheduler in MMCV. - - Args: - by_epoch (bool): LR changes epoch by epoch - warmup (string): Type of warmup used. It can be None(use no warmup), - 'constant', 'linear' or 'exp' - warmup_iters (int): The number of iterations or epochs that warmup - lasts - warmup_ratio (float): LR used at the beginning of warmup equals to - warmup_ratio * initial_lr - warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters - means the number of epochs that warmup lasts, otherwise means the - number of iteration that warmup lasts - """ - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.1, - warmup_by_epoch=False): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_ratio" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - self.warmup_by_epoch = warmup_by_epoch - - if self.warmup_by_epoch: - self.warmup_epochs = self.warmup_iters - self.warmup_iters = None - else: - self.warmup_epochs = None - - self.base_lr = [] # initial lr for all param groups - self.regular_lr = [] # expected lr if no warming up is performed - - def _set_lr(self, runner, lr_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, lr in zip(optim.param_groups, lr_groups[k]): - param_group['lr'] = lr - else: - for param_group, lr in zip(runner.optimizer.param_groups, - lr_groups): - param_group['lr'] = lr - - def get_lr(self, runner, base_lr): - raise NotImplementedError - - def get_regular_lr(self, runner): - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(runner, _base_lr) - for _base_lr in self.base_lr[k] - ] - lr_groups.update({k: _lr_group}) - - return lr_groups - else: - return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - if self.warmup == 'constant': - warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_lr = [_lr * (1 - k) for _lr in regular_lr] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.regular_lr, dict): - lr_groups = {} - for key, regular_lr in self.regular_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.regular_lr) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - group.setdefault('initial_lr', group['lr']) - _base_lr = [ - group['initial_lr'] for group in optim.param_groups - ] - self.base_lr.update({k: _base_lr}) - else: - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['initial_lr'] for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if self.warmup_iters is None: - epoch_len = len(runner.data_loader) - self.warmup_iters = self.warmup_epochs * epoch_len - - if not self.by_epoch: - return - - self.regular_lr = self.get_regular_lr(runner) - self._set_lr(runner, self.regular_lr) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_lr = self.get_regular_lr(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - - -@HOOKS.register_module() -class FixedLrUpdaterHook(LrUpdaterHook): - - def __init__(self, **kwargs): - super(FixedLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - return base_lr - - -@HOOKS.register_module() -class StepLrUpdaterHook(LrUpdaterHook): - """Step LR scheduler with min_lr clipping. - - Args: - step (int | list[int]): Step to decay the LR. If an int value is given, - regard it as the decay interval. If a list is given, decay LR at - these steps. - gamma (float, optional): Decay LR ratio. Default: 0.1. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. If None - is given, we don't perform lr clipping. Default: None. - """ - - def __init__(self, step, gamma=0.1, min_lr=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_lr = min_lr - super(StepLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - lr = base_lr * (self.gamma**exp) - if self.min_lr is not None: - # clip to a minimum value - lr = max(lr, self.min_lr) - return lr - - -@HOOKS.register_module() -class ExpLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, **kwargs): - self.gamma = gamma - super(ExpLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * self.gamma**progress - - -@HOOKS.register_module() -class PolyLrUpdaterHook(LrUpdaterHook): - - def __init__(self, power=1., min_lr=0., **kwargs): - self.power = power - self.min_lr = min_lr - super(PolyLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - coeff = (1 - progress / max_progress)**self.power - return (base_lr - self.min_lr) * coeff + self.min_lr - - -@HOOKS.register_module() -class InvLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, power=1., **kwargs): - self.gamma = gamma - self.power = power - super(InvLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * (1 + self.gamma * progress)**(-self.power) - - -@HOOKS.register_module() -class CosineAnnealingLrUpdaterHook(LrUpdaterHook): - """CosineAnnealing LR scheduler. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(CosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): - """Flat + Cosine lr schedule. - - Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 - - Args: - start_percent (float): When to start annealing the learning rate - after the percentage of the total training steps. - The value should be in range [0, 1). - Default: 0.75 - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - start_percent=0.75, - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - if start_percent < 0 or start_percent > 1 or not isinstance( - start_percent, float): - raise ValueError( - 'expected float between 0 and 1 start_percent, but ' - f'got {start_percent}') - self.start_percent = start_percent - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(FlatCosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - start = round(runner.max_epochs * self.start_percent) - progress = runner.epoch - start - max_progress = runner.max_epochs - start - else: - start = round(runner.max_iters * self.start_percent) - progress = runner.iter - start - max_progress = runner.max_iters - start - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress < 0: - return base_lr - else: - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class CosineRestartLrUpdaterHook(LrUpdaterHook): - """Cosine annealing with restarts learning rate scheme. - - Args: - periods (list[int]): Periods for each cosine anneling cycle. - restart_weights (list[float], optional): Restart weights at each - restart iteration. Default: [1]. - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - periods, - restart_weights=[1], - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.periods = periods - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - self.restart_weights = restart_weights - assert (len(self.periods) == len(self.restart_weights) - ), 'periods and restart_weights should have the same length.' - super(CosineRestartLrUpdaterHook, self).__init__(**kwargs) - - self.cumulative_periods = [ - sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) - ] - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - else: - progress = runner.iter - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - idx = get_position_from_periods(progress, self.cumulative_periods) - current_weight = self.restart_weights[idx] - nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] - current_periods = self.periods[idx] - - alpha = min((progress - nearest_restart) / current_periods, 1) - return annealing_cos(base_lr, target_lr, alpha, current_weight) - - -def get_position_from_periods(iteration, cumulative_periods): - """Get the position from a period list. - - It will return the index of the right-closest number in the period list. - For example, the cumulative_periods = [100, 200, 300, 400], - if iteration == 50, return 0; - if iteration == 210, return 2; - if iteration == 300, return 3. - - Args: - iteration (int): Current iteration. - cumulative_periods (list[int]): Cumulative period list. - - Returns: - int: The position of the right-closest number in the period list. - """ - for i, period in enumerate(cumulative_periods): - if iteration < period: - return i - raise ValueError(f'Current iteration {iteration} exceeds ' - f'cumulative_periods {cumulative_periods}') - - -@HOOKS.register_module() -class CyclicLrUpdaterHook(LrUpdaterHook): - """Cyclic LR Scheduler. - - Implement the cyclical learning rate policy (CLR) described in - https://arxiv.org/pdf/1506.01186.pdf - - Different from the original paper, we use cosine annealing rather than - triangular policy inside a cycle. This improves the performance in the - 3D detection area. - - Args: - by_epoch (bool, optional): Whether to update LR by epoch. - target_ratio (tuple[float], optional): Relative ratio of the highest LR - and the lowest LR to the initial LR. - cyclic_times (int, optional): Number of cycles during training - step_ratio_up (float, optional): The ratio of the increasing process of - LR in the total cycle. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch=False, - target_ratio=(10, 1e-4), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - gamma=1, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - assert 0 < gamma <= 1, \ - '"gamma" must be in range (0, 1]' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.max_iter_per_phase = None - self.lr_phases = [] # init lr_phases - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicLrUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicLrUpdaterHook, self).before_run(runner) - # initiate lr_phases - # total lr_phases are separated as up and down - self.max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * self.max_iter_per_phase) - self.lr_phases.append([0, iter_up_phase, 1, self.target_ratio[0]]) - self.lr_phases.append([ - iter_up_phase, self.max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - # Update weight decay - scale = self.gamma**curr_cycle - - for (start_iter, end_iter, start_ratio, end_ratio) in self.lr_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_lr and base lr. The target end_ratio can be - # expressed as: - # end_ratio = (base_lr + scale * (max_lr - base_lr)) / base_lr - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_lr * start_ratio, - base_lr * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleLrUpdaterHook(LrUpdaterHook): - """One Cycle LR Scheduler. - - The 1cycle learning rate policy changes the learning rate after every - batch. The one cycle learning rate policy is described in - https://arxiv.org/pdf/1708.07120.pdf - - Args: - max_lr (float or list): Upper learning rate boundaries in the cycle - for each parameter group. - total_steps (int, optional): The total number of steps in the cycle. - Note that if a value is not provided here, it will be the max_iter - of runner. Default: None. - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - div_factor (float): Determines the initial learning rate via - initial_lr = max_lr/div_factor - Default: 25 - final_div_factor (float): Determines the minimum learning rate via - min_lr = initial_lr/final_div_factor - Default: 1e4 - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - max_lr, - total_steps=None, - pct_start=0.3, - anneal_strategy='cos', - div_factor=25, - final_div_factor=1e4, - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch = False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(max_lr, (numbers.Number, list, dict)): - raise ValueError('the type of max_lr must be the one of list or ' - f'dict, but got {type(max_lr)}') - self._max_lr = max_lr - if total_steps is not None: - if not isinstance(total_steps, int): - raise ValueError('the type of total_steps must be int, but' - f'got {type(total_steps)}') - self.total_steps = total_steps - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.div_factor = div_factor - self.final_div_factor = final_div_factor - self.three_phase = three_phase - self.lr_phases = [] # init lr_phases - super(OneCycleLrUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if hasattr(self, 'total_steps'): - total_steps = self.total_steps - else: - total_steps = runner.max_iters - if total_steps < runner.max_iters: - raise ValueError( - 'The total steps must be greater than or equal to max ' - f'iterations {runner.max_iters} of runner, but total steps ' - f'is {total_steps}.') - - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - _max_lr = format_param(k, optim, self._max_lr) - self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(optim.param_groups, self.base_lr[k]): - group.setdefault('initial_lr', lr) - else: - k = type(runner.optimizer).__name__ - _max_lr = format_param(k, runner.optimizer, self._max_lr) - self.base_lr = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(runner.optimizer.param_groups, self.base_lr): - group.setdefault('initial_lr', lr) - - if self.three_phase: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append([ - float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 - ]) - self.lr_phases.append( - [total_steps - 1, 1, 1 / self.final_div_factor]) - else: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append( - [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - start_iter = 0 - for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): - if curr_iter <= end_iter: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, - pct) - break - start_iter = end_iter - return lr - - -@HOOKS.register_module() -class LinearAnnealingLrUpdaterHook(LrUpdaterHook): - """Linear annealing LR Scheduler decays the learning rate of each parameter - group linearly. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(LinearAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_linear(base_lr, target_lr, progress / max_progress) - - -def annealing_cos(start, end, factor, weight=1): - """Calculate annealing cos learning rate. - - Cosine anneal from `weight * start + (1 - weight) * end` to `end` as - percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the cosine annealing. - end (float): The ending learing rate of the cosine annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - weight (float, optional): The combination factor of `start` and `end` - when calculating the actual starting learning rate. Default to 1. - """ - cos_out = cos(pi * factor) + 1 - return end + 0.5 * weight * (start - end) * cos_out - - -def annealing_linear(start, end, factor): - """Calculate annealing linear learning rate. - - Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the linear annealing. - end (float): The ending learing rate of the linear annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - """ - return start + (end - start) * factor - - -def format_param(name, optim, param): - if isinstance(param, numbers.Number): - return [param] * len(optim.param_groups) - elif isinstance(param, (list, tuple)): # multi param groups - if len(param) != len(optim.param_groups): - raise ValueError(f'expected {len(optim.param_groups)} ' - f'values for {name}, got {len(param)}') - return param - else: # multi optimizers - if name not in param: - raise KeyError(f'{name} is not found in {param.keys()}') - return param[name] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/memory.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/memory.py deleted file mode 100644 index 70cf9a838..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class EmptyCacheHook(Hook): - - def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): - self._before_epoch = before_epoch - self._after_epoch = after_epoch - self._after_iter = after_iter - - def after_iter(self, runner): - if self._after_iter: - torch.cuda.empty_cache() - - def before_epoch(self, runner): - if self._before_epoch: - torch.cuda.empty_cache() - - def after_epoch(self, runner): - if self._after_epoch: - torch.cuda.empty_cache() diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/momentum_updater.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/momentum_updater.py deleted file mode 100644 index aa15fe23c..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/momentum_updater.py +++ /dev/null @@ -1,566 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -from .hook import HOOKS, Hook -from .lr_updater import annealing_cos, annealing_linear, format_param - - -class MomentumUpdaterHook(Hook): - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.9): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_momentum" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - - self.base_momentum = [] # initial momentum for all param groups - self.regular_momentum = [ - ] # expected momentum if no warming up is performed - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, base_momentum): - raise NotImplementedError - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k in runner.optimizer.keys(): - _momentum_group = [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum[k] - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - return [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum - ] - - def get_warmup_momentum(self, cur_iters): - - def _get_warmup_momentum(cur_iters, regular_momentum): - if self.warmup == 'constant': - warmup_momentum = [ - _momentum / self.warmup_ratio - for _momentum in regular_momentum - ] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_momentum = [ - _momentum / (1 - k) for _momentum in regular_momentum - ] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_momentum = [ - _momentum / k for _momentum in regular_momentum - ] - return warmup_momentum - - if isinstance(self.regular_momentum, dict): - momentum_groups = {} - for key, regular_momentum in self.regular_momentum.items(): - momentum_groups[key] = _get_warmup_momentum( - cur_iters, regular_momentum) - return momentum_groups - else: - return _get_warmup_momentum(cur_iters, self.regular_momentum) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, - # if 'initial_momentum' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_momentum = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - _base_momentum = [ - group['initial_momentum'] for group in optim.param_groups - ] - self.base_momentum.update({k: _base_momentum}) - else: - for group in runner.optimizer.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - self.base_momentum = [ - group['initial_momentum'] - for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if not self.by_epoch: - return - self.regular_momentum = self.get_regular_momentum(runner) - self._set_momentum(runner, self.regular_momentum) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_momentum = self.get_regular_momentum(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - - -@HOOKS.register_module() -class StepMomentumUpdaterHook(MomentumUpdaterHook): - """Step momentum scheduler with min value clipping. - - Args: - step (int | list[int]): Step to decay the momentum. If an int value is - given, regard it as the decay interval. If a list is given, decay - momentum at these steps. - gamma (float, optional): Decay momentum ratio. Default: 0.5. - min_momentum (float, optional): Minimum momentum value to keep. If - momentum after decay is lower than this value, it will be clipped - accordingly. If None is given, we don't perform lr clipping. - Default: None. - """ - - def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_momentum = min_momentum - super(StepMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - momentum = base_momentum * (self.gamma**exp) - if self.min_momentum is not None: - # clip to a minimum value - momentum = max(momentum, self.min_momentum) - return momentum - - -@HOOKS.register_module() -class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Cosine annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(CosineAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_cos(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class LinearAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Linear annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(LinearAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_linear(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class CyclicMomentumUpdaterHook(MomentumUpdaterHook): - """Cyclic momentum Scheduler. - - Implement the cyclical momentum scheduler policy described in - https://arxiv.org/pdf/1708.07120.pdf - - This momentum scheduler usually used together with the CyclicLRUpdater - to improve the performance in the 3D detection area. - - Args: - target_ratio (tuple[float]): Relative ratio of the lowest momentum and - the highest momentum to the initial momentum. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of momentum - in the total cycle. - by_epoch (bool): Whether to update momentum by epoch. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch=False, - target_ratio=(0.85 / 0.95, 1), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - gamma=1, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.momentum_phases = [] # init momentum_phases - - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - # currently only support by_epoch=False - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicMomentumUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicMomentumUpdaterHook, self).before_run(runner) - # initiate momentum_phases - # total momentum_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.max_iter_per_phase = max_iter_per_phase - self.momentum_phases.append( - [0, iter_up_phase, 1, self.target_ratio[0]]) - self.momentum_phases.append([ - iter_up_phase, max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_momentum(self, runner, base_momentum): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - scale = self.gamma**curr_cycle - for (start_iter, end_iter, start_ratio, end_ratio) \ - in self.momentum_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_momentum and base momentum. The target end_ratio - # can be expressed as: - # end_ratio = (base_momentum + scale * \ - # (max_momentum - base_momentum)) / base_momentum - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_momentum * start_ratio, - base_momentum * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): - """OneCycle momentum Scheduler. - - This momentum scheduler usually used together with the OneCycleLrUpdater - to improve the performance. - - Args: - base_momentum (float or list): Lower momentum boundaries in the cycle - for each parameter group. Note that momentum is cycled inversely - to learning rate; at the peak of a cycle, momentum is - 'base_momentum' and learning rate is 'max_lr'. - Default: 0.85 - max_momentum (float or list): Upper momentum boundaries in the cycle - for each parameter group. Functionally, - it defines the cycle amplitude (max_momentum - base_momentum). - Note that momentum is cycled inversely - to learning rate; at the start of a cycle, momentum is - 'max_momentum' and learning rate is 'base_lr' - Default: 0.95 - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - base_momentum=0.85, - max_momentum=0.95, - pct_start=0.3, - anneal_strategy='cos', - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch=False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(base_momentum, (float, list, dict)): - raise ValueError('base_momentum must be the type among of float,' - 'list or dict.') - self._base_momentum = base_momentum - if not isinstance(max_momentum, (float, list, dict)): - raise ValueError('max_momentum must be the type among of float,' - 'list or dict.') - self._max_momentum = max_momentum - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('Expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must by one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.three_phase = three_phase - self.momentum_phases = [] # init momentum_phases - super(OneCycleMomentumUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip( - optim.param_groups, _base_momentum, _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - else: - optim = runner.optimizer - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - k = type(optim).__name__ - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip(optim.param_groups, - _base_momentum, - _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - - if self.three_phase: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': - float(2 * self.pct_start * runner.max_iters) - 2, - 'start_momentum': - 'base_momentum', - 'end_momentum': - 'max_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'max_momentum', - 'end_momentum': 'max_momentum' - }) - else: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'base_momentum', - 'end_momentum': 'max_momentum' - }) - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, param_group): - curr_iter = runner.iter - start_iter = 0 - for i, phase in enumerate(self.momentum_phases): - end_iter = phase['end_iter'] - if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - momentum = self.anneal_func( - param_group[phase['start_momentum']], - param_group[phase['end_momentum']], pct) - break - start_iter = end_iter - return momentum - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k, optim in runner.optimizer.items(): - _momentum_group = [ - self.get_momentum(runner, param_group) - for param_group in optim.param_groups - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - momentum_groups = [] - for param_group in runner.optimizer.param_groups: - momentum_groups.append(self.get_momentum(runner, param_group)) - return momentum_groups diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/optimizer.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/optimizer.py deleted file mode 100644 index 12c885183..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/optimizer.py +++ /dev/null @@ -1,556 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -from collections import defaultdict -from itertools import chain - -from torch.nn.utils import clip_grad - -from mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version -from ..dist_utils import allreduce_grads -from ..fp16_utils import LossScaler, wrap_fp16_model -from .hook import HOOKS, Hook - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - from torch.cuda.amp import GradScaler -except ImportError: - pass - - -@HOOKS.register_module() -class OptimizerHook(Hook): - """A hook contains custom operations for the optimizer. - - Args: - grad_clip (dict, optional): A config dict to control the clip_grad. - Default: None. - detect_anomalous_params (bool): This option is only used for - debugging which will slow down the training speed. - Detect anomalous parameters that are not included in - the computational graph with `loss` as the root. - There are two cases - - - Parameters were not used during - forward pass. - - Parameters were not used to produce - loss. - Default: False. - """ - - def __init__(self, grad_clip=None, detect_anomalous_params=False): - self.grad_clip = grad_clip - self.detect_anomalous_params = detect_anomalous_params - - def clip_grads(self, params): - params = list( - filter(lambda p: p.requires_grad and p.grad is not None, params)) - if len(params) > 0: - return clip_grad.clip_grad_norm_(params, **self.grad_clip) - - def after_train_iter(self, runner): - runner.optimizer.zero_grad() - if self.detect_anomalous_params: - self.detect_anomalous_parameters(runner.outputs['loss'], runner) - runner.outputs['loss'].backward() - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - - def detect_anomalous_parameters(self, loss, runner): - logger = runner.logger - parameters_in_graph = set() - visited = set() - - def traverse(grad_fn): - if grad_fn is None: - return - if grad_fn not in visited: - visited.add(grad_fn) - if hasattr(grad_fn, 'variable'): - parameters_in_graph.add(grad_fn.variable) - parents = grad_fn.next_functions - if parents is not None: - for parent in parents: - grad_fn = parent[0] - traverse(grad_fn) - - traverse(loss.grad_fn) - for n, p in runner.model.named_parameters(): - if p not in parameters_in_graph and p.requires_grad: - logger.log( - level=logging.ERROR, - msg=f'{n} with shape {p.size()} is not ' - f'in the computational graph \n') - - -@HOOKS.register_module() -class GradientCumulativeOptimizerHook(OptimizerHook): - """Optimizer Hook implements multi-iters gradient cumulating. - - Args: - cumulative_iters (int, optional): Num of gradient cumulative iters. - The optimizer will step every `cumulative_iters` iters. - Defaults to 1. - - Examples: - >>> # Use cumulative_iters to simulate a large batch size - >>> # It is helpful when the hardware cannot handle a large batch size. - >>> loader = DataLoader(data, batch_size=64) - >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) - >>> # almost equals to - >>> loader = DataLoader(data, batch_size=256) - >>> optim_hook = OptimizerHook() - """ - - def __init__(self, cumulative_iters=1, **kwargs): - super(GradientCumulativeOptimizerHook, self).__init__(**kwargs) - - assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ - f'cumulative_iters only accepts positive int, but got ' \ - f'{type(cumulative_iters)} instead.' - - self.cumulative_iters = cumulative_iters - self.divisible_iters = 0 - self.remainder_iters = 0 - self.initialized = False - - def has_batch_norm(self, module): - if isinstance(module, _BatchNorm): - return True - for m in module.children(): - if self.has_batch_norm(m): - return True - return False - - def _init(self, runner): - if runner.iter % self.cumulative_iters != 0: - runner.logger.warning( - 'Resume iter number is not divisible by cumulative_iters in ' - 'GradientCumulativeOptimizerHook, which means the gradient of ' - 'some iters is lost and the result may be influenced slightly.' - ) - - if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: - runner.logger.warning( - 'GradientCumulativeOptimizerHook may slightly decrease ' - 'performance if the model has BatchNorm layers.') - - residual_iters = runner.max_iters - runner.iter - - self.divisible_iters = ( - residual_iters // self.cumulative_iters * self.cumulative_iters) - self.remainder_iters = residual_iters - self.divisible_iters - - self.initialized = True - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - runner.optimizer.zero_grad() - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - self.loss_scaler = GradScaler() - elif isinstance(loss_scale, float): - self._scale_update_param = loss_scale - self.loss_scaler = GradScaler(init_scale=loss_scale) - elif isinstance(loss_scale, dict): - self.loss_scaler = GradScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training.""" - # wrap model mode to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer to - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients. - 3. Unscale the optimizer’s gradient tensors. - 4. Call optimizer.step() and update scale factor. - 5. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - - self.loss_scaler.scale(runner.outputs['loss']).backward() - self.loss_scaler.unscale_(runner.optimizer) - # grad clip - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using PyTorch's implementation) implements - multi-iters gradient cumulating. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - """ - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - - self.loss_scaler.scale(loss).backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - self.loss_scaler.unscale_(runner.optimizer) - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() - -else: - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (mmcv's implementation). - - The steps of fp16 optimizer is as follows. - 1. Scale the loss value. - 2. BP in the fp16 model. - 2. Copy gradients from fp16 model to fp32 weights. - 3. Update fp32 weights. - 4. Copy updated parameters from fp32 weights to fp16 model. - - Refer to https://arxiv.org/abs/1710.03740 for more details. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of LossScaler. - Defaults to 512. - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - if loss_scale == 'dynamic': - self.loss_scaler = LossScaler(mode='dynamic') - elif isinstance(loss_scale, float): - self.loss_scaler = LossScaler( - init_scale=loss_scale, mode='static') - elif isinstance(loss_scale, dict): - self.loss_scaler = LossScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training. - - 1. Make a master copy of fp32 weights for optimization. - 2. Convert the main model from fp32 to fp16. - """ - # keep a copy of fp32 weights - old_groups = runner.optimizer.param_groups - runner.optimizer.param_groups = copy.deepcopy( - runner.optimizer.param_groups) - state = defaultdict(dict) - p_map = { - old_p: p - for old_p, p in zip( - chain(*(g['params'] for g in old_groups)), - chain(*(g['params'] - for g in runner.optimizer.param_groups))) - } - for k, v in runner.optimizer.state.items(): - state[p_map[k]] = v - runner.optimizer.state = state - # convert model to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer `loss_scalar.py` - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients (fp16). - 3. Copy gradients from the model to the fp32 weight copy. - 4. Scale the gradients back and update the fp32 weight copy. - 5. Copy back the params from fp32 weight copy to the fp16 model. - 6. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - # scale the loss value - scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale - scaled_loss.backward() - # copy fp16 grads in the model to fp32 params in the optimizer - - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - self.loss_scaler.update_scale(has_overflow) - if has_overflow: - runner.logger.warning('Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using mmcv implementation) implements multi- - iters gradient cumulating.""" - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - - loss = runner.outputs['loss'] - loss = loss / loss_factor - - # scale the loss value - scaled_loss = loss * self.loss_scaler.loss_scale - scaled_loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - else: - runner.logger.warning( - 'Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - self.loss_scaler.update_scale(has_overflow) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/profiler.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/profiler.py deleted file mode 100644 index fef9adc13..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/profiler.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Callable, List, Optional, Union - -import torch - -from ..dist_utils import master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ProfilerHook(Hook): - """Profiler to analyze performance during training. - - PyTorch Profiler is a tool that allows the collection of the performance - metrics during the training. More details on Profiler can be found at - https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile - - Args: - by_epoch (bool): Profile performance by epoch or by iteration. - Default: True. - profile_iters (int): Number of iterations for profiling. - If ``by_epoch=True``, profile_iters indicates that they are the - first profile_iters epochs at the beginning of the - training, otherwise it indicates the first profile_iters - iterations. Default: 1. - activities (list[str]): List of activity groups (CPU, CUDA) to use in - profiling. Default: ['cpu', 'cuda']. - schedule (dict, optional): Config of generating the callable schedule. - if schedule is None, profiler will not add step markers into the - trace and table view. Default: None. - on_trace_ready (callable, dict): Either a handler or a dict of generate - handler. Default: None. - record_shapes (bool): Save information about operator's input shapes. - Default: False. - profile_memory (bool): Track tensor memory allocation/deallocation. - Default: False. - with_stack (bool): Record source information (file and line number) - for the ops. Default: False. - with_flops (bool): Use formula to estimate the FLOPS of specific - operators (matrix multiplication and 2D convolution). - Default: False. - json_trace_path (str, optional): Exports the collected trace in Chrome - JSON format. Default: None. - - Example: - >>> runner = ... # instantiate a Runner - >>> # tensorboard trace - >>> trace_config = dict(type='tb_trace', dir_name='work_dir') - >>> profiler_config = dict(on_trace_ready=trace_config) - >>> runner.register_profiler_hook(profiler_config) - >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) - """ - - def __init__(self, - by_epoch: bool = True, - profile_iters: int = 1, - activities: List[str] = ['cpu', 'cuda'], - schedule: Optional[dict] = None, - on_trace_ready: Optional[Union[Callable, dict]] = None, - record_shapes: bool = False, - profile_memory: bool = False, - with_stack: bool = False, - with_flops: bool = False, - json_trace_path: Optional[str] = None) -> None: - try: - from torch import profiler # torch version >= 1.8.1 - except ImportError: - raise ImportError('profiler is the new feature of torch1.8.1, ' - f'but your version is {torch.__version__}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' - self.by_epoch = by_epoch - - if profile_iters < 1: - raise ValueError('profile_iters should be greater than 0, but got ' - f'{profile_iters}') - self.profile_iters = profile_iters - - if not isinstance(activities, list): - raise ValueError( - f'activities should be list, but got {type(activities)}') - self.activities = [] - for activity in activities: - activity = activity.lower() - if activity == 'cpu': - self.activities.append(profiler.ProfilerActivity.CPU) - elif activity == 'cuda': - self.activities.append(profiler.ProfilerActivity.CUDA) - else: - raise ValueError( - f'activity should be "cpu" or "cuda", but got {activity}') - - if schedule is not None: - self.schedule = profiler.schedule(**schedule) - else: - self.schedule = None - - self.on_trace_ready = on_trace_ready - self.record_shapes = record_shapes - self.profile_memory = profile_memory - self.with_stack = with_stack - self.with_flops = with_flops - self.json_trace_path = json_trace_path - - @master_only - def before_run(self, runner): - if self.by_epoch and runner.max_epochs < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_epochs}') - - if not self.by_epoch and runner.max_iters < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_iters}') - - if callable(self.on_trace_ready): # handler - _on_trace_ready = self.on_trace_ready - elif isinstance(self.on_trace_ready, dict): # config of handler - trace_cfg = self.on_trace_ready.copy() - trace_type = trace_cfg.pop('type') # log_trace handler - if trace_type == 'log_trace': - - def _log_handler(prof): - print(prof.key_averages().table(**trace_cfg)) - - _on_trace_ready = _log_handler - elif trace_type == 'tb_trace': # tensorboard_trace handler - try: - import torch_tb_profiler # noqa: F401 - except ImportError: - raise ImportError('please run "pip install ' - 'torch-tb-profiler" to install ' - 'torch_tb_profiler') - _on_trace_ready = torch.profiler.tensorboard_trace_handler( - **trace_cfg) - else: - raise ValueError('trace_type should be "log_trace" or ' - f'"tb_trace", but got {trace_type}') - elif self.on_trace_ready is None: - _on_trace_ready = None # type: ignore - else: - raise ValueError('on_trace_ready should be handler, dict or None, ' - f'but got {type(self.on_trace_ready)}') - - if self.by_epoch and runner.max_epochs > 1: - warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' - 'instead of 1 epoch. Since profiler will slow down ' - 'the training, it is recommended to train 1 epoch ' - 'with ProfilerHook and adjust your setting according' - ' to the profiler summary. During normal training ' - '(epoch > 1), you may disable the ProfilerHook.') - - self.profiler = torch.profiler.profile( - activities=self.activities, - schedule=self.schedule, - on_trace_ready=_on_trace_ready, - record_shapes=self.record_shapes, - profile_memory=self.profile_memory, - with_stack=self.with_stack, - with_flops=self.with_flops) - - self.profiler.__enter__() - runner.logger.info('profiler is profiling...') - - @master_only - def after_train_epoch(self, runner): - if self.by_epoch and runner.epoch == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) - - @master_only - def after_train_iter(self, runner): - self.profiler.step() - if not self.by_epoch and runner.iter == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/sampler_seed.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/sampler_seed.py deleted file mode 100644 index ee0dc6bdd..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/sampler_seed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class DistSamplerSeedHook(Hook): - """Data-loading sampler for distributed training. - - When distributed training, it is only useful in conjunction with - :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same - purpose with :obj:`IterLoader`. - """ - - def before_epoch(self, runner): - if hasattr(runner.data_loader.sampler, 'set_epoch'): - # in case the data loader uses `SequentialSampler` in Pytorch - runner.data_loader.sampler.set_epoch(runner.epoch) - elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): - # batch sampler in pytorch warps the sampler as its attributes. - runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/sync_buffer.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/sync_buffer.py deleted file mode 100644 index 6376b7ff8..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/hooks/sync_buffer.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..dist_utils import allreduce_params -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class SyncBuffersHook(Hook): - """Synchronize model buffers such as running_mean and running_var in BN at - the end of each epoch. - - Args: - distributed (bool): Whether distributed training is used. It is - effective only for distributed training. Defaults to True. - """ - - def __init__(self, distributed=True): - self.distributed = distributed - - def after_epoch(self, runner): - """All-reduce model buffers at the end of each epoch.""" - if self.distributed: - allreduce_params(runner.model.buffers()) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/iter_based_runner.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/iter_based_runner.py deleted file mode 100644 index 9892b07a4..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/iter_based_runner.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch -from torch.optim import Optimizer - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .hooks import IterTimerHook -from .utils import get_host_info - - -class IterLoader: - - def __init__(self, dataloader): - self._dataloader = dataloader - self.iter_loader = iter(self._dataloader) - self._epoch = 0 - - @property - def epoch(self): - return self._epoch - - def __next__(self): - try: - data = next(self.iter_loader) - except StopIteration: - self._epoch += 1 - if hasattr(self._dataloader.sampler, 'set_epoch'): - self._dataloader.sampler.set_epoch(self._epoch) - time.sleep(2) # Prevent possible deadlock during epoch transition - self.iter_loader = iter(self._dataloader) - data = next(self.iter_loader) - - return data - - def __len__(self): - return len(self._dataloader) - - -@RUNNERS.register_module() -class IterBasedRunner(BaseRunner): - """Iteration-based Runner. - - This runner train models iteration by iteration. - """ - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._epoch = data_loader.epoch - data_batch = next(data_loader) - self.call_hook('before_train_iter') - outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.train_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_train_iter') - self._inner_iter += 1 - self._iter += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - data_batch = next(data_loader) - self.call_hook('before_val_iter') - outputs = self.model.val_step(data_batch, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.val_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_val_iter') - self._inner_iter += 1 - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, iters) to specify the - running order and iterations. E.g, [('train', 10000), - ('val', 1000)] means running 10000 iterations for training and - 1000 iterations for validation, iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - """Resume model from checkpoint. - - Args: - checkpoint (str): Checkpoint to resume from. - resume_optimizer (bool, optional): Whether resume the optimizer(s) - if the checkpoint file includes optimizer(s). Default to True. - map_location (str, optional): Same as :func:`torch.load`. - Default to 'default'. - """ - if map_location == 'default': - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - self._inner_iter = checkpoint['meta']['iter'] - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') - - def save_checkpoint(self, - out_dir, - filename_tmpl='iter_{}.pth', - meta=None, - save_optimizer=True, - create_symlink=True): - """Save checkpoint to file. - - Args: - out_dir (str): Directory to save checkpoint files. - filename_tmpl (str, optional): Checkpoint file template. - Defaults to 'iter_{}.pth'. - meta (dict, optional): Metadata to be saved in checkpoint. - Defaults to None. - save_optimizer (bool, optional): Whether save optimizer. - Defaults to True. - create_symlink (bool, optional): Whether create symlink to the - latest checkpoint file. Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.iter + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - custom_hooks_config=None): - """Register default hooks for iter-based training. - - Checkpoint hook, optimizer stepper hook and logger hooks will be set to - `by_epoch=False` by default. - - Default hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - if checkpoint_config is not None: - checkpoint_config.setdefault('by_epoch', False) - if lr_config is not None: - lr_config.setdefault('by_epoch', False) - if log_config is not None: - for info in log_config['hooks']: - info.setdefault('by_epoch', False) - super(IterBasedRunner, self).register_training_hooks( - lr_config=lr_config, - momentum_config=momentum_config, - optimizer_config=optimizer_config, - checkpoint_config=checkpoint_config, - log_config=log_config, - timer_config=IterTimerHook(), - custom_hooks_config=custom_hooks_config) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/log_buffer.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/log_buffer.py deleted file mode 100644 index d949e2941..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/log_buffer.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import numpy as np - - -class LogBuffer: - - def __init__(self): - self.val_history = OrderedDict() - self.n_history = OrderedDict() - self.output = OrderedDict() - self.ready = False - - def clear(self): - self.val_history.clear() - self.n_history.clear() - self.clear_output() - - def clear_output(self): - self.output.clear() - self.ready = False - - def update(self, vars, count=1): - assert isinstance(vars, dict) - for key, var in vars.items(): - if key not in self.val_history: - self.val_history[key] = [] - self.n_history[key] = [] - self.val_history[key].append(var) - self.n_history[key].append(count) - - def average(self, n=0): - """Average latest n values or all values.""" - assert n >= 0 - for key in self.val_history: - values = np.array(self.val_history[key][-n:]) - nums = np.array(self.n_history[key][-n:]) - avg = np.sum(values * nums) / np.sum(nums) - self.output[key] = avg - self.ready = True diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/optimizer/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/optimizer/__init__.py deleted file mode 100644 index 53c34d047..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/optimizer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, - build_optimizer_constructor) -from .default_constructor import DefaultOptimizerConstructor - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', - 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/optimizer/builder.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/optimizer/builder.py deleted file mode 100644 index f9234eed8..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/optimizer/builder.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect - -import torch - -from ...utils import Registry, build_from_cfg - -OPTIMIZERS = Registry('optimizer') -OPTIMIZER_BUILDERS = Registry('optimizer builder') - - -def register_torch_optimizers(): - torch_optimizers = [] - for module_name in dir(torch.optim): - if module_name.startswith('__'): - continue - _optim = getattr(torch.optim, module_name) - if inspect.isclass(_optim) and issubclass(_optim, - torch.optim.Optimizer): - OPTIMIZERS.register_module()(_optim) - torch_optimizers.append(module_name) - return torch_optimizers - - -TORCH_OPTIMIZERS = register_torch_optimizers() - - -def build_optimizer_constructor(cfg): - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/optimizer/default_constructor.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/optimizer/default_constructor.py deleted file mode 100644 index ae97db880..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/optimizer/default_constructor.py +++ /dev/null @@ -1,250 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -from torch.nn import GroupNorm, LayerNorm - -from mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of -from mmcv.utils.ext_loader import check_ops_exist -from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS - - -@OPTIMIZER_BUILDERS.register_module() -class DefaultOptimizerConstructor: - """Default constructor for optimizers. - - By default each parameter share the same optimizer settings, and we - provide an argument ``paramwise_cfg`` to specify parameter-wise settings. - It is a dict and may contain the following fields: - - - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If - one of the keys in ``custom_keys`` is a substring of the name of one - parameter, then the setting of the parameter will be specified by - ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will - be ignored. It should be noted that the aforementioned ``key`` is the - longest key that is a substring of the name of the parameter. If there - are multiple matched keys with the same length, then the key with lower - alphabet order will be chosen. - ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` - and ``decay_mult``. See Example 2 below. - - ``bias_lr_mult`` (float): It will be multiplied to the learning - rate for all bias parameters (except for those in normalization - layers and offset layers of DCN). - - ``bias_decay_mult`` (float): It will be multiplied to the weight - decay for all bias parameters (except for those in - normalization layers, depthwise conv layers, offset layers of DCN). - - ``norm_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of normalization - layers. - - ``dwconv_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of depthwise conv - layers. - - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning - rate for parameters of offset layer in the deformable convs - of a model. - - ``bypass_duplicate`` (bool): If true, the duplicate parameters - would not be added into optimizer. Default: False. - - Note: - - 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will - override the effect of ``bias_lr_mult`` in the bias of offset layer. - So be careful when using both ``bias_lr_mult`` and - ``dcn_offset_lr_mult``. If you wish to apply both of them to the offset - layer in deformable convs, set ``dcn_offset_lr_mult`` to the original - ``dcn_offset_lr_mult`` * ``bias_lr_mult``. - - 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will - apply it to all the DCN layers in the model. So be careful when the - model contains multiple DCN layers in places other than backbone. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - optimizer_cfg (dict): The config dict of the optimizer. - Positional fields are - - - `type`: class name of the optimizer. - - Optional fields are - - - any arguments of the corresponding optimizer type, e.g., - lr, weight_decay, momentum, etc. - paramwise_cfg (dict, optional): Parameter-wise options. - - Example 1: - >>> model = torch.nn.modules.Conv1d(1, 1, 1) - >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, - >>> weight_decay=0.0001) - >>> paramwise_cfg = dict(norm_decay_mult=0.) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - - Example 2: - >>> # assume model have attribute model.backbone and model.cls_head - >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) - >>> paramwise_cfg = dict(custom_keys={ - 'backbone': dict(lr_mult=0.1, decay_mult=0.9)}) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - >>> # Then the `lr` and `weight_decay` for model.backbone is - >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for - >>> # model.cls_head is (0.01, 0.95). - """ - - def __init__(self, optimizer_cfg, paramwise_cfg=None): - if not isinstance(optimizer_cfg, dict): - raise TypeError('optimizer_cfg should be a dict', - f'but got {type(optimizer_cfg)}') - self.optimizer_cfg = optimizer_cfg - self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg - self.base_lr = optimizer_cfg.get('lr', None) - self.base_wd = optimizer_cfg.get('weight_decay', None) - self._validate_cfg() - - def _validate_cfg(self): - if not isinstance(self.paramwise_cfg, dict): - raise TypeError('paramwise_cfg should be None or a dict, ' - f'but got {type(self.paramwise_cfg)}') - - if 'custom_keys' in self.paramwise_cfg: - if not isinstance(self.paramwise_cfg['custom_keys'], dict): - raise TypeError( - 'If specified, custom_keys must be a dict, ' - f'but got {type(self.paramwise_cfg["custom_keys"])}') - if self.base_wd is None: - for key in self.paramwise_cfg['custom_keys']: - if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: - raise ValueError('base_wd should not be None') - - # get base lr and weight decay - # weight_decay must be explicitly specified if mult is specified - if ('bias_decay_mult' in self.paramwise_cfg - or 'norm_decay_mult' in self.paramwise_cfg - or 'dwconv_decay_mult' in self.paramwise_cfg): - if self.base_wd is None: - raise ValueError('base_wd should not be None') - - def _is_in(self, param_group, param_group_list): - assert is_list_of(param_group_list, dict) - param = set(param_group['params']) - param_set = set() - for group in param_group_list: - param_set.update(set(group['params'])) - - return not param.isdisjoint(param_set) - - def add_params(self, params, module, prefix='', is_dcn_module=None): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - prefix (str): The prefix of the module - is_dcn_module (int|float|None): If the current module is a - submodule of DCN, `is_dcn_module` will be passed to - control conv_offset layer's learning rate. Defaults to None. - """ - # get param-wise options - custom_keys = self.paramwise_cfg.get('custom_keys', {}) - # first sort with alphabet order and then sort with reversed len of str - sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) - - bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) - bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) - norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) - dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) - bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) - dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) - - # special rules for norm layers and depth-wise conv layers - is_norm = isinstance(module, - (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) - is_dwconv = ( - isinstance(module, torch.nn.Conv2d) - and module.in_channels == module.groups) - - for name, param in module.named_parameters(recurse=False): - param_group = {'params': [param]} - if not param.requires_grad: - params.append(param_group) - continue - if bypass_duplicate and self._is_in(param_group, params): - warnings.warn(f'{prefix} is duplicate. It is skipped since ' - f'bypass_duplicate={bypass_duplicate}') - continue - # if the parameter match one of the custom keys, ignore other rules - is_custom = False - for key in sorted_keys: - if key in f'{prefix}.{name}': - is_custom = True - lr_mult = custom_keys[key].get('lr_mult', 1.) - param_group['lr'] = self.base_lr * lr_mult - if self.base_wd is not None: - decay_mult = custom_keys[key].get('decay_mult', 1.) - param_group['weight_decay'] = self.base_wd * decay_mult - break - - if not is_custom: - # bias_lr_mult affects all bias parameters - # except for norm.bias dcn.conv_offset.bias - if name == 'bias' and not (is_norm or is_dcn_module): - param_group['lr'] = self.base_lr * bias_lr_mult - - if (prefix.find('conv_offset') != -1 and is_dcn_module - and isinstance(module, torch.nn.Conv2d)): - # deal with both dcn_offset's bias & weight - param_group['lr'] = self.base_lr * dcn_offset_lr_mult - - # apply weight decay policies - if self.base_wd is not None: - # norm decay - if is_norm: - param_group[ - 'weight_decay'] = self.base_wd * norm_decay_mult - # depth-wise conv - elif is_dwconv: - param_group[ - 'weight_decay'] = self.base_wd * dwconv_decay_mult - # bias lr and decay - elif name == 'bias' and not is_dcn_module: - # TODO: current bias_decay_mult will have affect on DCN - param_group[ - 'weight_decay'] = self.base_wd * bias_decay_mult - params.append(param_group) - - if check_ops_exist(): - from mmcv.ops import DeformConv2d, ModulatedDeformConv2d - is_dcn_module = isinstance(module, - (DeformConv2d, ModulatedDeformConv2d)) - else: - is_dcn_module = False - for child_name, child_mod in module.named_children(): - child_prefix = f'{prefix}.{child_name}' if prefix else child_name - self.add_params( - params, - child_mod, - prefix=child_prefix, - is_dcn_module=is_dcn_module) - - def __call__(self, model): - if hasattr(model, 'module'): - model = model.module - - optimizer_cfg = self.optimizer_cfg.copy() - # if no paramwise option is specified, just use the global setting - if not self.paramwise_cfg: - optimizer_cfg['params'] = model.parameters() - return build_from_cfg(optimizer_cfg, OPTIMIZERS) - - # set param-wise lr and weight decay recursively - params = [] - self.add_params(params, model) - optimizer_cfg['params'] = params - - return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/priority.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/priority.py deleted file mode 100644 index 64cc4e3a0..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/priority.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from enum import Enum - - -class Priority(Enum): - """Hook priority levels. - - +--------------+------------+ - | Level | Value | - +==============+============+ - | HIGHEST | 0 | - +--------------+------------+ - | VERY_HIGH | 10 | - +--------------+------------+ - | HIGH | 30 | - +--------------+------------+ - | ABOVE_NORMAL | 40 | - +--------------+------------+ - | NORMAL | 50 | - +--------------+------------+ - | BELOW_NORMAL | 60 | - +--------------+------------+ - | LOW | 70 | - +--------------+------------+ - | VERY_LOW | 90 | - +--------------+------------+ - | LOWEST | 100 | - +--------------+------------+ - """ - - HIGHEST = 0 - VERY_HIGH = 10 - HIGH = 30 - ABOVE_NORMAL = 40 - NORMAL = 50 - BELOW_NORMAL = 60 - LOW = 70 - VERY_LOW = 90 - LOWEST = 100 - - -def get_priority(priority): - """Get priority value. - - Args: - priority (int or str or :obj:`Priority`): Priority. - - Returns: - int: The priority value. - """ - if isinstance(priority, int): - if priority < 0 or priority > 100: - raise ValueError('priority must be between 0 and 100') - return priority - elif isinstance(priority, Priority): - return priority.value - elif isinstance(priority, str): - return Priority[priority.upper()].value - else: - raise TypeError('priority must be an integer or Priority enum value') diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/utils.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/utils.py deleted file mode 100644 index 144d11e1a..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/runner/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random -import sys -import time -import warnings -from getpass import getuser -from socket import gethostname - -import numpy as np -import torch - -import mmcv - - -def get_host_info(): - """Get hostname and username. - - Return empty string if exception raised, e.g. ``getpass.getuser()`` will - lead to error in docker container - """ - host = '' - try: - host = f'{getuser()}@{gethostname()}' - except Exception as e: - warnings.warn(f'Host or user not found: {str(e)}') - finally: - return host - - -def get_time_str(): - return time.strftime('%Y%m%d_%H%M%S', time.localtime()) - - -def obj_from_dict(info, parent=None, default_args=None): - """Initialize an object from dict. - - The dict must contain the key "type", which indicates the object type, it - can be either a string or type, such as "list" or ``list``. Remaining - fields are treated as the arguments for constructing the object. - - Args: - info (dict): Object types and arguments. - parent (:class:`module`): Module which may containing expected object - classes. - default_args (dict, optional): Default arguments for initializing the - object. - - Returns: - any type: Object built from the dict. - """ - assert isinstance(info, dict) and 'type' in info - assert isinstance(default_args, dict) or default_args is None - args = info.copy() - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if parent is not None: - obj_type = getattr(parent, obj_type) - else: - obj_type = sys.modules[obj_type] - elif not isinstance(obj_type, type): - raise TypeError('type must be a str or valid type, but ' - f'got {type(obj_type)}') - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - return obj_type(**args) - - -def set_random_seed(seed, deterministic=False, use_rank_shift=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - rank_shift (bool): Whether to add rank number to the random seed to - have different random seed in different threads. Default: False. - """ - if use_rank_shift: - rank, _ = mmcv.runner.get_dist_info() - seed += rank - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/__init__.py deleted file mode 100644 index e0825ed67..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/__init__.py +++ /dev/null @@ -1,78 +0,0 @@ -# flake8: noqa -# Copyright (c) OpenMMLab. All rights reserved. -from .config import Config, ConfigDict, DictAction -from .misc import (check_prerequisites, concat_list, deprecated_api_warning, - has_method, import_modules_from_strings, is_list_of, - is_method_overridden, is_seq_of, is_str, is_tuple_of, - iter_cast, list_cast, requires_executable, requires_package, - slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, - to_ntuple, tuple_cast) -from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, - scandir, symlink) -from .progressbar import (ProgressBar, track_iter_progress, - track_parallel_progress, track_progress) -from .testing import (assert_attrs_equal, assert_dict_contains_subset, - assert_dict_has_keys, assert_is_norm_layer, - assert_keys_equal, assert_params_all_zeros, - check_python_script) -from .timer import Timer, TimerError, check_time -from .version_utils import digit_version, get_git_hash - -try: - import torch -except ImportError: - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', - 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', - 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', - 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', - 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', - 'track_progress', 'track_iter_progress', 'track_parallel_progress', - 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', - 'digit_version', 'get_git_hash', 'import_modules_from_strings', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', - 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', - 'is_method_overridden', 'has_method' - ] -else: - from .device_type import IS_IPU_AVAILABLE, IS_MLU_AVAILABLE - from .env import collect_env - from .hub import load_url - from .logging import get_logger, print_log - from .parrots_jit import jit, skip_no_elena - # yapf: disable - from .parrots_wrapper import (IS_CUDA_AVAILABLE, TORCH_VERSION, - BuildExtension, CppExtension, CUDAExtension, - DataLoader, PoolDataLoader, SyncBatchNorm, - _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, - _AvgPoolNd, _BatchNorm, _ConvNd, - _ConvTransposeMixin, _get_cuda_home, - _InstanceNorm, _MaxPoolNd, get_build_config, - is_rocm_pytorch) - # yapf: enable - from .registry import Registry, build_from_cfg - from .seed import worker_init_fn - from .trace import is_jit_tracing - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', - 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', - 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', - 'check_prerequisites', 'requires_package', 'requires_executable', - 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', - 'symlink', 'scandir', 'ProgressBar', 'track_progress', - 'track_iter_progress', 'track_parallel_progress', 'Registry', - 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', - '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', - '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', - 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', - 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', - 'deprecated_api_warning', 'digit_version', 'get_git_hash', - 'import_modules_from_strings', 'jit', 'skip_no_elena', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', - 'assert_params_all_zeros', 'check_python_script', - 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', - '_get_cuda_home', 'load_url', 'has_method', 'IS_CUDA_AVAILABLE', - 'worker_init_fn', 'IS_MLU_AVAILABLE', 'IS_IPU_AVAILABLE' - ] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/config.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/config.py deleted file mode 100644 index 8efbc24e2..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/config.py +++ /dev/null @@ -1,719 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import ast -import copy -import os -import os.path as osp -import platform -import shutil -import sys -import tempfile -import types -import uuid -import warnings -from argparse import Action, ArgumentParser -from collections import abc -from importlib import import_module -from pathlib import Path - -from addict import Dict -from yapf.yapflib.yapf_api import FormatCode - -from .misc import import_modules_from_strings -from .path import check_file_exist - -if platform.system() == 'Windows': - import regex as re -else: - import re - -BASE_KEY = '_base_' -DELETE_KEY = '_delete_' -DEPRECATION_KEY = '_deprecation_' -RESERVED_KEYS = ['filename', 'text', 'pretty_text'] - - -class ConfigDict(Dict): - - def __missing__(self, name): - raise KeyError(name) - - def __getattr__(self, name): - try: - value = super(ConfigDict, self).__getattr__(name) - except KeyError: - ex = AttributeError(f"'{self.__class__.__name__}' object has no " - f"attribute '{name}'") - except Exception as e: - ex = e - else: - return value - raise ex - - -def add_args(parser, cfg, prefix=''): - for k, v in cfg.items(): - if isinstance(v, str): - parser.add_argument('--' + prefix + k) - elif isinstance(v, int): - parser.add_argument('--' + prefix + k, type=int) - elif isinstance(v, float): - parser.add_argument('--' + prefix + k, type=float) - elif isinstance(v, bool): - parser.add_argument('--' + prefix + k, action='store_true') - elif isinstance(v, dict): - add_args(parser, v, prefix + k + '.') - elif isinstance(v, abc.Iterable): - parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') - else: - print(f'cannot parse key {prefix + k} of type {type(v)}') - return parser - - -class Config: - """A facility for config and config files. - - It supports common file formats as configs: python/json/yaml. The interface - is the same as a dict object and also allows access config values as - attributes. - - Example: - >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - >>> cfg.a - 1 - >>> cfg.b - {'b1': [0, 1]} - >>> cfg.b.b1 - [0, 1] - >>> cfg = Config.fromfile('tests/data/config/a.py') - >>> cfg.filename - "/home/kchen/projects/mmcv/tests/data/config/a.py" - >>> cfg.item4 - 'test' - >>> cfg - "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " - "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" - """ - - @staticmethod - def _validate_py_syntax(filename): - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - content = f.read() - try: - ast.parse(content) - except SyntaxError as e: - raise SyntaxError('There are syntax errors in config ' - f'file {filename}: {e}') - - @staticmethod - def _substitute_predefined_vars(filename, temp_config_name): - file_dirname = osp.dirname(filename) - file_basename = osp.basename(filename) - file_basename_no_extension = osp.splitext(file_basename)[0] - file_extname = osp.splitext(filename)[1] - support_templates = dict( - fileDirname=file_dirname, - fileBasename=file_basename, - fileBasenameNoExtension=file_basename_no_extension, - fileExtname=file_extname) - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - for key, value in support_templates.items(): - regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' - value = value.replace('\\', '/') - config_file = re.sub(regexp, value, config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - - @staticmethod - def _pre_substitute_base_vars(filename, temp_config_name): - """Substitute base variable placehoders to string, so that parsing - would work.""" - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - base_var_dict = {} - regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' - base_vars = set(re.findall(regexp, config_file)) - for base_var in base_vars: - randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' - base_var_dict[randstr] = base_var - regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' - config_file = re.sub(regexp, f'"{randstr}"', config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - return base_var_dict - - @staticmethod - def _substitute_base_vars(cfg, base_var_dict, base_cfg): - """Substitute variable strings to their actual values.""" - cfg = copy.deepcopy(cfg) - - if isinstance(cfg, dict): - for k, v in cfg.items(): - if isinstance(v, str) and v in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[v].split('.'): - new_v = new_v[new_k] - cfg[k] = new_v - elif isinstance(v, (list, tuple, dict)): - cfg[k] = Config._substitute_base_vars( - v, base_var_dict, base_cfg) - elif isinstance(cfg, tuple): - cfg = tuple( - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg) - elif isinstance(cfg, list): - cfg = [ - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg - ] - elif isinstance(cfg, str) and cfg in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[cfg].split('.'): - new_v = new_v[new_k] - cfg = new_v - - return cfg - - @staticmethod - def _file2dict(filename, use_predefined_variables=True): - filename = osp.abspath(osp.expanduser(filename)) - check_file_exist(filename) - fileExtname = osp.splitext(filename)[1] - if fileExtname not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - - with tempfile.TemporaryDirectory() as temp_config_dir: - temp_config_file = tempfile.NamedTemporaryFile( - dir=temp_config_dir, suffix=fileExtname) - if platform.system() == 'Windows': - temp_config_file.close() - temp_config_name = osp.basename(temp_config_file.name) - # Substitute predefined variables - if use_predefined_variables: - Config._substitute_predefined_vars(filename, - temp_config_file.name) - else: - shutil.copyfile(filename, temp_config_file.name) - # Substitute base variables from placeholders to strings - base_var_dict = Config._pre_substitute_base_vars( - temp_config_file.name, temp_config_file.name) - - if filename.endswith('.py'): - temp_module_name = osp.splitext(temp_config_name)[0] - sys.path.insert(0, temp_config_dir) - Config._validate_py_syntax(filename) - mod = import_module(temp_module_name) - sys.path.pop(0) - cfg_dict = { - name: value - for name, value in mod.__dict__.items() - if not name.startswith('__') - and not isinstance(value, types.ModuleType) - and not isinstance(value, types.FunctionType) - } - # delete imported module - del sys.modules[temp_module_name] - elif filename.endswith(('.yml', '.yaml', '.json')): - import mmcv - cfg_dict = mmcv.load(temp_config_file.name) - # close temp file - temp_config_file.close() - - # check deprecation information - if DEPRECATION_KEY in cfg_dict: - deprecation_info = cfg_dict.pop(DEPRECATION_KEY) - warning_msg = f'The config file {filename} will be deprecated ' \ - 'in the future.' - if 'expected' in deprecation_info: - warning_msg += f' Please use {deprecation_info["expected"]} ' \ - 'instead.' - if 'reference' in deprecation_info: - warning_msg += ' More information can be found at ' \ - f'{deprecation_info["reference"]}' - warnings.warn(warning_msg, DeprecationWarning) - - cfg_text = filename + '\n' - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - cfg_text += f.read() - - if BASE_KEY in cfg_dict: - cfg_dir = osp.dirname(filename) - base_filename = cfg_dict.pop(BASE_KEY) - base_filename = base_filename if isinstance( - base_filename, list) else [base_filename] - - cfg_dict_list = list() - cfg_text_list = list() - for f in base_filename: - _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) - cfg_dict_list.append(_cfg_dict) - cfg_text_list.append(_cfg_text) - - base_cfg_dict = dict() - for c in cfg_dict_list: - duplicate_keys = base_cfg_dict.keys() & c.keys() - if len(duplicate_keys) > 0: - raise KeyError('Duplicate key is not allowed among bases. ' - f'Duplicate keys: {duplicate_keys}') - base_cfg_dict.update(c) - - # Substitute base variables from strings to their actual values - cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, - base_cfg_dict) - - base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) - cfg_dict = base_cfg_dict - - # merge cfg_text - cfg_text_list.append(cfg_text) - cfg_text = '\n'.join(cfg_text_list) - - return cfg_dict, cfg_text - - @staticmethod - def _merge_a_into_b(a, b, allow_list_keys=False): - """merge dict ``a`` into dict ``b`` (non-inplace). - - Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid - in-place modifications. - - Args: - a (dict): The source dict to be merged into ``b``. - b (dict): The origin dict to be fetch keys from ``a``. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in source ``a`` and will replace the element of the - corresponding index in b if b is a list. Default: False. - - Returns: - dict: The modified dict of ``b`` using ``a``. - - Examples: - # Normally merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # Delete b first and merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # b is a list - >>> Config._merge_a_into_b( - ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) - [{'a': 2}, {'b': 2}] - """ - b = b.copy() - for k, v in a.items(): - if allow_list_keys and k.isdigit() and isinstance(b, list): - k = int(k) - if len(b) <= k: - raise KeyError(f'Index {k} exceeds the length of list {b}') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - elif isinstance(v, dict): - if k in b and not v.pop(DELETE_KEY, False): - allowed_types = (dict, list) if allow_list_keys else dict - if not isinstance(b[k], allowed_types): - raise TypeError( - f'{k}={v} in child config cannot inherit from ' - f'base because {k} is a dict in the child config ' - f'but is of type {type(b[k])} in base config. ' - f'You may set `{DELETE_KEY}=True` to ignore the ' - f'base config.') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - else: - b[k] = ConfigDict(v) - else: - b[k] = v - return b - - @staticmethod - def fromfile(filename, - use_predefined_variables=True, - import_custom_modules=True): - if isinstance(filename, Path): - filename = str(filename) - cfg_dict, cfg_text = Config._file2dict(filename, - use_predefined_variables) - if import_custom_modules and cfg_dict.get('custom_imports', None): - import_modules_from_strings(**cfg_dict['custom_imports']) - return Config(cfg_dict, cfg_text=cfg_text, filename=filename) - - @staticmethod - def fromstring(cfg_str, file_format): - """Generate config from config str. - - Args: - cfg_str (str): Config str. - file_format (str): Config file format corresponding to the - config str. Only py/yml/yaml/json type are supported now! - - Returns: - :obj:`Config`: Config obj. - """ - if file_format not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - if file_format != '.py' and 'dict(' in cfg_str: - # check if users specify a wrong suffix for python - warnings.warn( - 'Please check "file_format", the file format may be .py') - with tempfile.NamedTemporaryFile( - 'w', encoding='utf-8', suffix=file_format, - delete=False) as temp_file: - temp_file.write(cfg_str) - # on windows, previous implementation cause error - # see PR 1077 for details - cfg = Config.fromfile(temp_file.name) - os.remove(temp_file.name) - return cfg - - @staticmethod - def auto_argparser(description=None): - """Generate argparser from config file automatically (experimental)""" - partial_parser = ArgumentParser(description=description) - partial_parser.add_argument('config', help='config file path') - cfg_file = partial_parser.parse_known_args()[0].config - cfg = Config.fromfile(cfg_file) - parser = ArgumentParser(description=description) - parser.add_argument('config', help='config file path') - add_args(parser, cfg) - return parser, cfg - - def __init__(self, cfg_dict=None, cfg_text=None, filename=None): - if cfg_dict is None: - cfg_dict = dict() - elif not isinstance(cfg_dict, dict): - raise TypeError('cfg_dict must be a dict, but ' - f'got {type(cfg_dict)}') - for key in cfg_dict: - if key in RESERVED_KEYS: - raise KeyError(f'{key} is reserved for config file') - - if isinstance(filename, Path): - filename = str(filename) - - super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) - super(Config, self).__setattr__('_filename', filename) - if cfg_text: - text = cfg_text - elif filename: - with open(filename, 'r') as f: - text = f.read() - else: - text = '' - super(Config, self).__setattr__('_text', text) - - @property - def filename(self): - return self._filename - - @property - def text(self): - return self._text - - @property - def pretty_text(self): - - indent = 4 - - def _indent(s_, num_spaces): - s = s_.split('\n') - if len(s) == 1: - return s_ - first = s.pop(0) - s = [(num_spaces * ' ') + line for line in s] - s = '\n'.join(s) - s = first + '\n' + s - return s - - def _format_basic_types(k, v, use_mapping=False): - if isinstance(v, str): - v_str = f"'{v}'" - else: - v_str = str(v) - - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) - - return attr_str - - def _format_list(k, v, use_mapping=False): - # check if all items in the list are dict - if all(isinstance(_, dict) for _ in v): - v_str = '[\n' - v_str += '\n'.join( - f'dict({_indent(_format_dict(v_), indent)}),' - for v_ in v).rstrip(',') - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) + ']' - else: - attr_str = _format_basic_types(k, v, use_mapping) - return attr_str - - def _contain_invalid_identifier(dict_str): - contain_invalid_identifier = False - for key_name in dict_str: - contain_invalid_identifier |= \ - (not str(key_name).isidentifier()) - return contain_invalid_identifier - - def _format_dict(input_dict, outest_level=False): - r = '' - s = [] - - use_mapping = _contain_invalid_identifier(input_dict) - if use_mapping: - r += '{' - for idx, (k, v) in enumerate(input_dict.items()): - is_last = idx >= len(input_dict) - 1 - end = '' if outest_level or is_last else ',' - if isinstance(v, dict): - v_str = '\n' + _format_dict(v) - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: dict({v_str}' - else: - attr_str = f'{str(k)}=dict({v_str}' - attr_str = _indent(attr_str, indent) + ')' + end - elif isinstance(v, list): - attr_str = _format_list(k, v, use_mapping) + end - else: - attr_str = _format_basic_types(k, v, use_mapping) + end - - s.append(attr_str) - r += '\n'.join(s) - if use_mapping: - r += '}' - return r - - cfg_dict = self._cfg_dict.to_dict() - text = _format_dict(cfg_dict, outest_level=True) - # copied from setup.cfg - yapf_style = dict( - based_on_style='pep8', - blank_line_before_nested_class_or_def=True, - split_before_expression_after_opening_paren=True) - text, _ = FormatCode(text, style_config=yapf_style, verify=True) - - return text - - def __repr__(self): - return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' - - def __len__(self): - return len(self._cfg_dict) - - def __getattr__(self, name): - return getattr(self._cfg_dict, name) - - def __getitem__(self, name): - return self._cfg_dict.__getitem__(name) - - def __setattr__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setattr__(name, value) - - def __setitem__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setitem__(name, value) - - def __iter__(self): - return iter(self._cfg_dict) - - def __getstate__(self): - return (self._cfg_dict, self._filename, self._text) - - def __copy__(self): - cls = self.__class__ - other = cls.__new__(cls) - other.__dict__.update(self.__dict__) - - return other - - def __deepcopy__(self, memo): - cls = self.__class__ - other = cls.__new__(cls) - memo[id(self)] = other - - for key, value in self.__dict__.items(): - super(Config, other).__setattr__(key, copy.deepcopy(value, memo)) - - return other - - def __setstate__(self, state): - _cfg_dict, _filename, _text = state - super(Config, self).__setattr__('_cfg_dict', _cfg_dict) - super(Config, self).__setattr__('_filename', _filename) - super(Config, self).__setattr__('_text', _text) - - def dump(self, file=None): - cfg_dict = super(Config, self).__getattribute__('_cfg_dict').to_dict() - if self.filename.endswith('.py'): - if file is None: - return self.pretty_text - else: - with open(file, 'w', encoding='utf-8') as f: - f.write(self.pretty_text) - else: - import mmcv - if file is None: - file_format = self.filename.split('.')[-1] - return mmcv.dump(cfg_dict, file_format=file_format) - else: - mmcv.dump(cfg_dict, file) - - def merge_from_dict(self, options, allow_list_keys=True): - """Merge list into cfg_dict. - - Merge the dict parsed by MultipleKVAction into this cfg. - - Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} - >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) - >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - - >>> # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) - >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) - >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - - Args: - options (dict): dict of configs to merge from. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in ``options`` and will replace the element of the - corresponding index in the config if the config is a list. - Default: True. - """ - option_cfg_dict = {} - for full_key, v in options.items(): - d = option_cfg_dict - key_list = full_key.split('.') - for subkey in key_list[:-1]: - d.setdefault(subkey, ConfigDict()) - d = d[subkey] - subkey = key_list[-1] - d[subkey] = v - - cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - super(Config, self).__setattr__( - '_cfg_dict', - Config._merge_a_into_b( - option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) - - -class DictAction(Action): - """ - argparse action to split an argument into KEY=VALUE form - on the first = and append to a dictionary. List options can - be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit - brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build - list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' - """ - - @staticmethod - def _parse_int_float_bool(val): - try: - return int(val) - except ValueError: - pass - try: - return float(val) - except ValueError: - pass - if val.lower() in ['true', 'false']: - return True if val.lower() == 'true' else False - if val == 'None': - return None - return val - - @staticmethod - def _parse_iterable(val): - """Parse iterable values in the string. - - All elements inside '()' or '[]' are treated as iterable values. - - Args: - val (str): Value string. - - Returns: - list | tuple: The expanded list or tuple from the string. - - Examples: - >>> DictAction._parse_iterable('1,2,3') - [1, 2, 3] - >>> DictAction._parse_iterable('[a, b, c]') - ['a', 'b', 'c'] - >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') - [(1, 2, 3), ['a', 'b'], 'c'] - """ - - def find_next_comma(string): - """Find the position of next comma in the string. - - If no ',' is found in the string, return the string length. All - chars inside '()' and '[]' are treated as one element and thus ',' - inside these brackets are ignored. - """ - assert (string.count('(') == string.count(')')) and ( - string.count('[') == string.count(']')), \ - f'Imbalanced brackets exist in {string}' - end = len(string) - for idx, char in enumerate(string): - pre = string[:idx] - # The string before this ',' is balanced - if ((char == ',') and (pre.count('(') == pre.count(')')) - and (pre.count('[') == pre.count(']'))): - end = idx - break - return end - - # Strip ' and " characters and replace whitespace. - val = val.strip('\'\"').replace(' ', '') - is_tuple = False - if val.startswith('(') and val.endswith(')'): - is_tuple = True - val = val[1:-1] - elif val.startswith('[') and val.endswith(']'): - val = val[1:-1] - elif ',' not in val: - # val is a single value - return DictAction._parse_int_float_bool(val) - - values = [] - while len(val) > 0: - comma_idx = find_next_comma(val) - element = DictAction._parse_iterable(val[:comma_idx]) - values.append(element) - val = val[comma_idx + 1:] - if is_tuple: - values = tuple(values) - return values - - def __call__(self, parser, namespace, values, option_string=None): - options = {} - for kv in values: - key, val = kv.split('=', maxsplit=1) - options[key] = self._parse_iterable(val) - setattr(namespace, self.dest, options) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/device_type.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/device_type.py deleted file mode 100644 index c29d944ab..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/device_type.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - - -def is_ipu_available(): - try: - import poptorch - return poptorch.ipuHardwareIsAvailable() - except ImportError: - return False - - -IS_IPU_AVAILABLE = is_ipu_available() - - -def is_mlu_available(): - try: - import torch - return (hasattr(torch, 'is_mlu_available') - and torch.is_mlu_available()) - except Exception: - return False - - -IS_MLU_AVAILABLE = is_mlu_available() diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/env.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/env.py deleted file mode 100644 index 511332506..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/env.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This file holding some environment constant for sharing by other files.""" - -import os.path as osp -import subprocess -import sys -from collections import defaultdict - -import cv2 -import torch - -import mmcv -from .parrots_wrapper import get_build_config - - -def collect_env(): - """Collect the information of the running environments. - - Returns: - dict: The environment information. The following fields are contained. - - - sys.platform: The variable of ``sys.platform``. - - Python: Python version. - - CUDA available: Bool, indicating if CUDA is available. - - GPU devices: Device type of each GPU. - - CUDA_HOME (optional): The env var ``CUDA_HOME``. - - NVCC (optional): NVCC version. - - GCC: GCC version, "n/a" if GCC is not installed. - - MSVC: Microsoft Virtual C++ Compiler version, Windows only. - - PyTorch: PyTorch version. - - PyTorch compiling details: The output of \ - ``torch.__config__.show()``. - - TorchVision (optional): TorchVision version. - - OpenCV: OpenCV version. - - MMCV: MMCV version. - - MMCV Compiler: The GCC version for compiling MMCV ops. - - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. - """ - env_info = {} - env_info['sys.platform'] = sys.platform - env_info['Python'] = sys.version.replace('\n', '') - - cuda_available = torch.cuda.is_available() - env_info['CUDA available'] = cuda_available - - if cuda_available: - devices = defaultdict(list) - for k in range(torch.cuda.device_count()): - devices[torch.cuda.get_device_name(k)].append(str(k)) - for name, device_ids in devices.items(): - env_info['GPU ' + ','.join(device_ids)] = name - - from mmcv.utils.parrots_wrapper import _get_cuda_home - CUDA_HOME = _get_cuda_home() - env_info['CUDA_HOME'] = CUDA_HOME - - if CUDA_HOME is not None and osp.isdir(CUDA_HOME): - try: - nvcc = osp.join(CUDA_HOME, 'bin/nvcc') - nvcc = subprocess.check_output(f'"{nvcc}" -V', shell=True) - nvcc = nvcc.decode('utf-8').strip() - release = nvcc.rfind('Cuda compilation tools') - build = nvcc.rfind('Build ') - nvcc = nvcc[release:build].strip() - except subprocess.SubprocessError: - nvcc = 'Not Available' - env_info['NVCC'] = nvcc - - try: - # Check C++ Compiler. - # For Unix-like, sysconfig has 'CC' variable like 'gcc -pthread ...', - # indicating the compiler used, we use this to get the compiler name - import sysconfig - cc = sysconfig.get_config_var('CC') - if cc: - cc = osp.basename(cc.split()[0]) - cc_info = subprocess.check_output(f'{cc} --version', shell=True) - env_info['GCC'] = cc_info.decode('utf-8').partition( - '\n')[0].strip() - else: - # on Windows, cl.exe is not in PATH. We need to find the path. - # distutils.ccompiler.new_compiler() returns a msvccompiler - # object and after initialization, path to cl.exe is found. - import locale - import os - from distutils.ccompiler import new_compiler - ccompiler = new_compiler() - ccompiler.initialize() - cc = subprocess.check_output( - f'{ccompiler.cc}', stderr=subprocess.STDOUT, shell=True) - encoding = os.device_encoding( - sys.stdout.fileno()) or locale.getpreferredencoding() - env_info['MSVC'] = cc.decode(encoding).partition('\n')[0].strip() - env_info['GCC'] = 'n/a' - except subprocess.CalledProcessError: - env_info['GCC'] = 'n/a' - - env_info['PyTorch'] = torch.__version__ - env_info['PyTorch compiling details'] = get_build_config() - - try: - import torchvision - env_info['TorchVision'] = torchvision.__version__ - except ModuleNotFoundError: - pass - - env_info['OpenCV'] = cv2.__version__ - - env_info['MMCV'] = mmcv.__version__ - - try: - from mmcv.ops import get_compiler_version, get_compiling_cuda_version - except ModuleNotFoundError: - env_info['MMCV Compiler'] = 'n/a' - env_info['MMCV CUDA Compiler'] = 'n/a' - else: - env_info['MMCV Compiler'] = get_compiler_version() - env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() - - return env_info diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/ext_loader.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/ext_loader.py deleted file mode 100644 index f82c6d568..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/ext_loader.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os -import pkgutil -import warnings -from collections import namedtuple - -import torch - -if torch.__version__ != 'parrots': - - def load_ext(name, funcs): - ext = importlib.import_module('mmcv.' + name) - for fun in funcs: - assert hasattr(ext, fun), f'{fun} miss in module {name}' - return ext -else: - from parrots import extension - from parrots.base import ParrotsException - - has_return_value_ops = [ - 'nms', - 'softnms', - 'nms_match', - 'nms_rotated', - 'top_pool_forward', - 'top_pool_backward', - 'bottom_pool_forward', - 'bottom_pool_backward', - 'left_pool_forward', - 'left_pool_backward', - 'right_pool_forward', - 'right_pool_backward', - 'fused_bias_leakyrelu', - 'upfirdn2d', - 'ms_deform_attn_forward', - 'pixel_group', - 'contour_expand', - 'diff_iou_rotated_sort_vertices_forward', - ] - - def get_fake_func(name, e): - - def fake_func(*args, **kwargs): - warnings.warn(f'{name} is not supported in parrots now') - raise e - - return fake_func - - def load_ext(name, funcs): - ExtModule = namedtuple('ExtModule', funcs) - ext_list = [] - lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - for fun in funcs: - try: - ext_fun = extension.load(fun, name, lib_dir=lib_root) - except ParrotsException as e: - if 'No element registered' not in e.message: - warnings.warn(e.message) - ext_fun = get_fake_func(fun, e) - ext_list.append(ext_fun) - else: - if fun in has_return_value_ops: - ext_list.append(ext_fun.op) - else: - ext_list.append(ext_fun.op_) - return ExtModule(*ext_list) - - -def check_ops_exist(): - ext_loader = pkgutil.find_loader('mmcv._ext') - return ext_loader is not None diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/hub.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/hub.py deleted file mode 100644 index 12fbff2ee..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/hub.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# The 1.6 release of PyTorch switched torch.save to use a new zipfile-based -# file format. It will cause RuntimeError when a checkpoint was saved in -# torch >= 1.6.0 but loaded in torch < 1.7.0. -# More details at https://github.com/open-mmlab/mmpose/issues/904 -from .parrots_wrapper import TORCH_VERSION -from .path import mkdir_or_exist -from .version_utils import digit_version - -if TORCH_VERSION != 'parrots' and digit_version(TORCH_VERSION) < digit_version( - '1.7.0'): - # Modified from https://github.com/pytorch/pytorch/blob/master/torch/hub.py - import os - import sys - import warnings - import zipfile - from urllib.parse import urlparse - - import torch - from torch.hub import HASH_REGEX, _get_torch_home, download_url_to_file - - # Hub used to support automatically extracts from zipfile manually - # compressed by users. The legacy zip format expects only one file from - # torch.save() < 1.6 in the zip. We should remove this support since - # zipfile is now default zipfile format for torch.save(). - def _is_legacy_zip_format(filename): - if zipfile.is_zipfile(filename): - infolist = zipfile.ZipFile(filename).infolist() - return len(infolist) == 1 and not infolist[0].is_dir() - return False - - def _legacy_zip_load(filename, model_dir, map_location): - warnings.warn( - 'Falling back to the old format < 1.6. This support will' - ' be deprecated in favor of default zipfile format ' - 'introduced in 1.6. Please redo torch.save() to save it ' - 'in the new zipfile format.', DeprecationWarning) - # Note: extractall() defaults to overwrite file if exists. No need to - # clean up beforehand. We deliberately don't handle tarfile here - # since our legacy serialization format was in tar. - # E.g. resnet18-5c106cde.pth which is widely used. - with zipfile.ZipFile(filename) as f: - members = f.infolist() - if len(members) != 1: - raise RuntimeError( - 'Only one file(not dir) is allowed in the zipfile') - f.extractall(model_dir) - extraced_name = members[0].filename - extracted_file = os.path.join(model_dir, extraced_name) - return torch.load(extracted_file, map_location=map_location) - - def load_url(url, - model_dir=None, - map_location=None, - progress=True, - check_hash=False, - file_name=None): - r"""Loads the Torch serialized object at the given URL. - - If downloaded file is a zip file, it will be automatically decompressed - - If the object is already present in `model_dir`, it's deserialized and - returned. - The default value of ``model_dir`` is ``/checkpoints`` where - ``hub_dir`` is the directory returned by :func:`~torch.hub.get_dir`. - - Args: - url (str): URL of the object to download - model_dir (str, optional): directory in which to save the object - map_location (optional): a function or a dict specifying how to - remap storage locations (see torch.load) - progress (bool, optional): whether or not to display a progress bar - to stderr. Default: True - check_hash(bool, optional): If True, the filename part of the URL - should follow the naming convention ``filename-.ext`` - where ```` is the first eight or more digits of the - SHA256 hash of the contents of the file. The hash is used to - ensure unique names and to verify the contents of the file. - Default: False - file_name (str, optional): name for the downloaded file. Filename - from ``url`` will be used if not set. Default: None. - - Example: - >>> url = ('https://s3.amazonaws.com/pytorch/models/resnet18-5c106' - ... 'cde.pth') - >>> state_dict = torch.hub.load_state_dict_from_url(url) - """ - # Issue warning to move data if old env is set - if os.getenv('TORCH_MODEL_ZOO'): - warnings.warn( - 'TORCH_MODEL_ZOO is deprecated, please use env ' - 'TORCH_HOME instead', DeprecationWarning) - - if model_dir is None: - torch_home = _get_torch_home() - model_dir = os.path.join(torch_home, 'checkpoints') - - mkdir_or_exist(model_dir) - - parts = urlparse(url) - filename = os.path.basename(parts.path) - if file_name is not None: - filename = file_name - cached_file = os.path.join(model_dir, filename) - if not os.path.exists(cached_file): - sys.stderr.write('Downloading: "{}" to {}\n'.format( - url, cached_file)) - hash_prefix = None - if check_hash: - r = HASH_REGEX.search(filename) # r is Optional[Match[str]] - hash_prefix = r.group(1) if r else None - download_url_to_file( - url, cached_file, hash_prefix, progress=progress) - - if _is_legacy_zip_format(cached_file): - return _legacy_zip_load(cached_file, model_dir, map_location) - - try: - return torch.load(cached_file, map_location=map_location) - except RuntimeError as error: - if digit_version(TORCH_VERSION) < digit_version('1.5.0'): - warnings.warn( - f'If the error is the same as "{cached_file} is a zip ' - 'archive (did you mean to use torch.jit.load()?)", you can' - ' upgrade your torch to 1.5.0 or higher (current torch ' - f'version is {TORCH_VERSION}). The error was raised ' - ' because the checkpoint was saved in torch>=1.6.0 but ' - 'loaded in torch<1.5.') - raise error -else: - from torch.utils.model_zoo import load_url # noqa: F401 diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/logging.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/logging.py deleted file mode 100644 index c4c7025f0..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/logging.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.distributed as dist - -logger_initialized = {} - - -def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): - """Initialize and get a logger by name. - - If the logger has not been initialized, this method will initialize the - logger by adding one or two handlers, otherwise the initialized logger will - be directly returned. During initialization, a StreamHandler will always be - added. If `log_file` is specified and the process rank is 0, a FileHandler - will also be added. - - Args: - name (str): Logger name. - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the logger. - log_level (int): The logger level. Note that only the process of - rank 0 is affected, and other processes will set the level to - "Error" thus be silent most of the time. - file_mode (str): The file mode used in opening log file. - Defaults to 'w'. - - Returns: - logging.Logger: The expected logger. - """ - logger = logging.getLogger(name) - if name in logger_initialized: - return logger - # handle hierarchical names - # e.g., logger "a" is initialized, then logger "a.b" will skip the - # initialization since it is a child of "a". - for logger_name in logger_initialized: - if name.startswith(logger_name): - return logger - - # handle duplicate logs to the console - # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) - # to the root logger. As logger.propagate is True by default, this root - # level handler causes logging messages from rank>0 processes to - # unexpectedly show up on the console, creating much unwanted clutter. - # To fix this issue, we set the root logger's StreamHandler, if any, to log - # at the ERROR level. - for handler in logger.root.handlers: - if type(handler) is logging.StreamHandler: - handler.setLevel(logging.ERROR) - - stream_handler = logging.StreamHandler() - handlers = [stream_handler] - - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - else: - rank = 0 - - # only rank 0 will add a FileHandler - if rank == 0 and log_file is not None: - # Here, the default behaviour of the official logger is 'a'. Thus, we - # provide an interface to change the file mode to the default - # behaviour. - file_handler = logging.FileHandler(log_file, file_mode) - handlers.append(file_handler) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - for handler in handlers: - handler.setFormatter(formatter) - handler.setLevel(log_level) - logger.addHandler(handler) - - if rank == 0: - logger.setLevel(log_level) - else: - logger.setLevel(logging.ERROR) - - logger_initialized[name] = True - - return logger - - -def print_log(msg, logger=None, level=logging.INFO): - """Print a log message. - - Args: - msg (str): The message to be logged. - logger (logging.Logger | str | None): The logger to be used. - Some special loggers are: - - - "silent": no message will be printed. - - other str: the logger obtained with `get_root_logger(logger)`. - - None: The `print()` method will be used to print log messages. - level (int): Logging level. Only available when `logger` is a Logger - object or "root". - """ - if logger is None: - print(msg) - elif isinstance(logger, logging.Logger): - logger.log(level, msg) - elif logger == 'silent': - pass - elif isinstance(logger, str): - _logger = get_logger(logger) - _logger.log(level, msg) - else: - raise TypeError( - 'logger should be either a logging.Logger object, str, ' - f'"silent" or None, but got {type(logger)}') diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/misc.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/misc.py deleted file mode 100644 index 7957ea89b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/misc.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections.abc -import functools -import itertools -import subprocess -import warnings -from collections import abc -from importlib import import_module -from inspect import getfullargspec -from itertools import repeat - - -# From PyTorch internals -def _ntuple(n): - - def parse(x): - if isinstance(x, collections.abc.Iterable): - return x - return tuple(repeat(x, n)) - - return parse - - -to_1tuple = _ntuple(1) -to_2tuple = _ntuple(2) -to_3tuple = _ntuple(3) -to_4tuple = _ntuple(4) -to_ntuple = _ntuple - - -def is_str(x): - """Whether the input is an string instance. - - Note: This method is deprecated since python 2 is no longer supported. - """ - return isinstance(x, str) - - -def import_modules_from_strings(imports, allow_failed_imports=False): - """Import modules from the given list of strings. - - Args: - imports (list | str | None): The given module names to be imported. - allow_failed_imports (bool): If True, the failed imports will return - None. Otherwise, an ImportError is raise. Default: False. - - Returns: - list[module] | module | None: The imported modules. - - Examples: - >>> osp, sys = import_modules_from_strings( - ... ['os.path', 'sys']) - >>> import os.path as osp_ - >>> import sys as sys_ - >>> assert osp == osp_ - >>> assert sys == sys_ - """ - if not imports: - return - single_import = False - if isinstance(imports, str): - single_import = True - imports = [imports] - if not isinstance(imports, list): - raise TypeError( - f'custom_imports must be a list but got type {type(imports)}') - imported = [] - for imp in imports: - if not isinstance(imp, str): - raise TypeError( - f'{imp} is of type {type(imp)} and cannot be imported.') - try: - imported_tmp = import_module(imp) - except ImportError: - if allow_failed_imports: - warnings.warn(f'{imp} failed to import and is ignored.', - UserWarning) - imported_tmp = None - else: - raise ImportError - imported.append(imported_tmp) - if single_import: - imported = imported[0] - return imported - - -def iter_cast(inputs, dst_type, return_type=None): - """Cast elements of an iterable object into some type. - - Args: - inputs (Iterable): The input object. - dst_type (type): Destination type. - return_type (type, optional): If specified, the output object will be - converted to this type, otherwise an iterator. - - Returns: - iterator or specified type: The converted object. - """ - if not isinstance(inputs, abc.Iterable): - raise TypeError('inputs must be an iterable object') - if not isinstance(dst_type, type): - raise TypeError('"dst_type" must be a valid type') - - out_iterable = map(dst_type, inputs) - - if return_type is None: - return out_iterable - else: - return return_type(out_iterable) - - -def list_cast(inputs, dst_type): - """Cast elements of an iterable object into a list of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=list) - - -def tuple_cast(inputs, dst_type): - """Cast elements of an iterable object into a tuple of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=tuple) - - -def is_seq_of(seq, expected_type, seq_type=None): - """Check whether it is a sequence of some type. - - Args: - seq (Sequence): The sequence to be checked. - expected_type (type): Expected type of sequence items. - seq_type (type, optional): Expected sequence type. - - Returns: - bool: Whether the sequence is valid. - """ - if seq_type is None: - exp_seq_type = abc.Sequence - else: - assert isinstance(seq_type, type) - exp_seq_type = seq_type - if not isinstance(seq, exp_seq_type): - return False - for item in seq: - if not isinstance(item, expected_type): - return False - return True - - -def is_list_of(seq, expected_type): - """Check whether it is a list of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=list) - - -def is_tuple_of(seq, expected_type): - """Check whether it is a tuple of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=tuple) - - -def slice_list(in_list, lens): - """Slice a list into several sub lists by a list of given length. - - Args: - in_list (list): The list to be sliced. - lens(int or list): The expected length of each out list. - - Returns: - list: A list of sliced list. - """ - if isinstance(lens, int): - assert len(in_list) % lens == 0 - lens = [lens] * int(len(in_list) / lens) - if not isinstance(lens, list): - raise TypeError('"indices" must be an integer or a list of integers') - elif sum(lens) != len(in_list): - raise ValueError('sum of lens and list length does not ' - f'match: {sum(lens)} != {len(in_list)}') - out_list = [] - idx = 0 - for i in range(len(lens)): - out_list.append(in_list[idx:idx + lens[i]]) - idx += lens[i] - return out_list - - -def concat_list(in_list): - """Concatenate a list of list into a single list. - - Args: - in_list (list): The list of list to be merged. - - Returns: - list: The concatenated flat list. - """ - return list(itertools.chain(*in_list)) - - -def check_prerequisites( - prerequisites, - checker, - msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' - 'found, please install them first.'): # yapf: disable - """A decorator factory to check if prerequisites are satisfied. - - Args: - prerequisites (str of list[str]): Prerequisites to be checked. - checker (callable): The checker method that returns True if a - prerequisite is meet, False otherwise. - msg_tmpl (str): The message template with two variables. - - Returns: - decorator: A specific decorator. - """ - - def wrap(func): - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - requirements = [prerequisites] if isinstance( - prerequisites, str) else prerequisites - missing = [] - for item in requirements: - if not checker(item): - missing.append(item) - if missing: - print(msg_tmpl.format(', '.join(missing), func.__name__)) - raise RuntimeError('Prerequisites not meet.') - else: - return func(*args, **kwargs) - - return wrapped_func - - return wrap - - -def _check_py_package(package): - try: - import_module(package) - except ImportError: - return False - else: - return True - - -def _check_executable(cmd): - if subprocess.call(f'which {cmd}', shell=True) != 0: - return False - else: - return True - - -def requires_package(prerequisites): - """A decorator to check if some python packages are installed. - - Example: - >>> @requires_package('numpy') - >>> func(arg1, args): - >>> return numpy.zeros(1) - array([0.]) - >>> @requires_package(['numpy', 'non_package']) - >>> func(arg1, args): - >>> return numpy.zeros(1) - ImportError - """ - return check_prerequisites(prerequisites, checker=_check_py_package) - - -def requires_executable(prerequisites): - """A decorator to check if some executable files are installed. - - Example: - >>> @requires_executable('ffmpeg') - >>> func(arg1, args): - >>> print(1) - 1 - """ - return check_prerequisites(prerequisites, checker=_check_executable) - - -def deprecated_api_warning(name_dict, cls_name=None): - """A decorator to check if some arguments are deprecate and try to replace - deprecate src_arg_name to dst_arg_name. - - Args: - name_dict(dict): - key (str): Deprecate argument names. - val (str): Expected argument names. - - Returns: - func: New function. - """ - - def api_warning_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get name of the function - func_name = old_func.__name__ - if cls_name is not None: - func_name = f'{cls_name}.{func_name}' - if args: - arg_names = args_info.args[:len(args)] - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in arg_names: - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - arg_names[arg_names.index(src_arg_name)] = dst_arg_name - if kwargs: - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in kwargs: - - assert dst_arg_name not in kwargs, ( - f'The expected behavior is to replace ' - f'the deprecated key `{src_arg_name}` to ' - f'new key `{dst_arg_name}`, but got them ' - f'in the arguments at the same time, which ' - f'is confusing. `{src_arg_name} will be ' - f'deprecated in the future, please ' - f'use `{dst_arg_name}` instead.') - - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - kwargs[dst_arg_name] = kwargs.pop(src_arg_name) - - # apply converted arguments to the decorated method - output = old_func(*args, **kwargs) - return output - - return new_func - - return api_warning_wrapper - - -def is_method_overridden(method, base_class, derived_class): - """Check if a method of base class is overridden in derived class. - - Args: - method (str): the method name to check. - base_class (type): the class of the base class. - derived_class (type | Any): the class or instance of the derived class. - """ - assert isinstance(base_class, type), \ - "base_class doesn't accept instance, Please pass class instead." - - if not isinstance(derived_class, type): - derived_class = derived_class.__class__ - - base_method = getattr(base_class, method) - derived_method = getattr(derived_class, method) - return derived_method != base_method - - -def has_method(obj: object, method: str) -> bool: - """Check whether the object has a method. - - Args: - method (str): The method name to check. - obj (object): The object to check. - - Returns: - bool: True if the object has the method else False. - """ - return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/parrots_jit.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/parrots_jit.py deleted file mode 100644 index 61873f6db..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/parrots_jit.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -from .parrots_wrapper import TORCH_VERSION - -parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') - -if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': - from parrots.jit import pat as jit -else: - - def jit(func=None, - check_input=None, - full_shape=True, - derivate=False, - coderize=False, - optimize=False): - - def wrapper(func): - - def wrapper_inner(*args, **kargs): - return func(*args, **kargs) - - return wrapper_inner - - if func is None: - return wrapper - else: - return func - - -if TORCH_VERSION == 'parrots': - from parrots.utils.tester import skip_no_elena -else: - - def skip_no_elena(func): - - def wrapper(*args, **kargs): - return func(*args, **kargs) - - return wrapper diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/parrots_wrapper.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/parrots_wrapper.py deleted file mode 100644 index 7e657b561..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/parrots_wrapper.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch - -TORCH_VERSION = torch.__version__ - - -def is_cuda_available() -> bool: - return torch.cuda.is_available() - - -IS_CUDA_AVAILABLE = is_cuda_available() - - -def is_rocm_pytorch() -> bool: - is_rocm = False - if TORCH_VERSION != 'parrots': - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - return is_rocm - - -def _get_cuda_home(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import CUDA_HOME - else: - if is_rocm_pytorch(): - from torch.utils.cpp_extension import ROCM_HOME - CUDA_HOME = ROCM_HOME - else: - from torch.utils.cpp_extension import CUDA_HOME - return CUDA_HOME - - -def get_build_config(): - if TORCH_VERSION == 'parrots': - from parrots.config import get_build_info - return get_build_info() - else: - return torch.__config__.show() - - -def _get_conv(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin - else: - from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin - return _ConvNd, _ConvTransposeMixin - - -def _get_dataloader(): - if TORCH_VERSION == 'parrots': - from torch.utils.data import DataLoader, PoolDataLoader - else: - from torch.utils.data import DataLoader - PoolDataLoader = DataLoader - return DataLoader, PoolDataLoader - - -def _get_extension(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import BuildExtension, Extension - CppExtension = partial(Extension, cuda=False) - CUDAExtension = partial(Extension, cuda=True) - else: - from torch.utils.cpp_extension import (BuildExtension, CppExtension, - CUDAExtension) - return BuildExtension, CppExtension, CUDAExtension - - -def _get_pool(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - else: - from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd - - -def _get_norm(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm2d - else: - from torch.nn.modules.batchnorm import _BatchNorm - from torch.nn.modules.instancenorm import _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm - return _BatchNorm, _InstanceNorm, SyncBatchNorm_ - - -_ConvNd, _ConvTransposeMixin = _get_conv() -DataLoader, PoolDataLoader = _get_dataloader() -BuildExtension, CppExtension, CUDAExtension = _get_extension() -_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() -_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() - - -class SyncBatchNorm(SyncBatchNorm_): - - def _check_input_dim(self, input): - if TORCH_VERSION == 'parrots': - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input (got {input.dim()}D input)') - else: - super()._check_input_dim(input) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/path.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/path.py deleted file mode 100644 index 568081837..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/path.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -from pathlib import Path - -from .misc import is_str - - -def is_filepath(x): - return is_str(x) or isinstance(x, Path) - - -def fopen(filepath, *args, **kwargs): - if is_str(filepath): - return open(filepath, *args, **kwargs) - elif isinstance(filepath, Path): - return filepath.open(*args, **kwargs) - raise ValueError('`filepath` should be a string or a Path') - - -def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): - if not osp.isfile(filename): - raise FileNotFoundError(msg_tmpl.format(filename)) - - -def mkdir_or_exist(dir_name, mode=0o777): - if dir_name == '': - return - dir_name = osp.expanduser(dir_name) - os.makedirs(dir_name, mode=mode, exist_ok=True) - - -def symlink(src, dst, overwrite=True, **kwargs): - if os.path.lexists(dst) and overwrite: - os.remove(dst) - os.symlink(src, dst, **kwargs) - - -def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): - """Scan a directory to find the interested files. - - Args: - dir_path (str | :obj:`Path`): Path of the directory. - suffix (str | tuple(str), optional): File suffix that we are - interested in. Default: None. - recursive (bool, optional): If set to True, recursively scan the - directory. Default: False. - case_sensitive (bool, optional) : If set to False, ignore the case of - suffix. Default: True. - - Returns: - A generator for all the interested files with relative paths. - """ - if isinstance(dir_path, (str, Path)): - dir_path = str(dir_path) - else: - raise TypeError('"dir_path" must be a string or Path object') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('"suffix" must be a string or tuple of strings') - - if suffix is not None and not case_sensitive: - suffix = suffix.lower() if isinstance(suffix, str) else tuple( - item.lower() for item in suffix) - - root = dir_path - - def _scandir(dir_path, suffix, recursive, case_sensitive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - _rel_path = rel_path if case_sensitive else rel_path.lower() - if suffix is None or _rel_path.endswith(suffix): - yield rel_path - elif recursive and os.path.isdir(entry.path): - # scan recursively if entry.path is a directory - yield from _scandir(entry.path, suffix, recursive, - case_sensitive) - - return _scandir(dir_path, suffix, recursive, case_sensitive) - - -def find_vcs_root(path, markers=('.git', )): - """Finds the root directory (including itself) of specified markers. - - Args: - path (str): Path of directory or file. - markers (list[str], optional): List of file or directory names. - - Returns: - The directory contained one of the markers or None if not found. - """ - if osp.isfile(path): - path = osp.dirname(path) - - prev, cur = None, osp.abspath(osp.expanduser(path)) - while cur != prev: - if any(osp.exists(osp.join(cur, marker)) for marker in markers): - return cur - prev, cur = cur, osp.split(cur)[0] - return None diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/progressbar.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/progressbar.py deleted file mode 100644 index 0062f670d..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/progressbar.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from collections.abc import Iterable -from multiprocessing import Pool -from shutil import get_terminal_size - -from .timer import Timer - - -class ProgressBar: - """A progress bar which can print the progress.""" - - def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): - self.task_num = task_num - self.bar_width = bar_width - self.completed = 0 - self.file = file - if start: - self.start() - - @property - def terminal_width(self): - width, _ = get_terminal_size() - return width - - def start(self): - if self.task_num > 0: - self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' - 'elapsed: 0s, ETA:') - else: - self.file.write('completed: 0, elapsed: 0s') - self.file.flush() - self.timer = Timer() - - def update(self, num_tasks=1): - assert num_tasks > 0 - self.completed += num_tasks - elapsed = self.timer.since_start() - if elapsed > 0: - fps = self.completed / elapsed - else: - fps = float('inf') - if self.task_num > 0: - percentage = self.completed / float(self.task_num) - eta = int(elapsed * (1 - percentage) / percentage + 0.5) - msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ - f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ - f'ETA: {eta:5}s' - - bar_width = min(self.bar_width, - int(self.terminal_width - len(msg)) + 2, - int(self.terminal_width * 0.6)) - bar_width = max(2, bar_width) - mark_width = int(bar_width * percentage) - bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) - self.file.write(msg.format(bar_chars)) - else: - self.file.write( - f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' - f' {fps:.1f} tasks/s') - self.file.flush() - - -def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): - """Track the progress of tasks execution with a progress bar. - - Tasks are done with a simple for-loop. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - results = [] - for task in tasks: - results.append(func(task, **kwargs)) - prog_bar.update() - prog_bar.file.write('\n') - return results - - -def init_pool(process_num, initializer=None, initargs=None): - if initializer is None: - return Pool(process_num) - elif initargs is None: - return Pool(process_num, initializer) - else: - if not isinstance(initargs, tuple): - raise TypeError('"initargs" must be a tuple') - return Pool(process_num, initializer, initargs) - - -def track_parallel_progress(func, - tasks, - nproc, - initializer=None, - initargs=None, - bar_width=50, - chunksize=1, - skip_first=False, - keep_order=True, - file=sys.stdout): - """Track the progress of parallel task execution with a progress bar. - - The built-in :mod:`multiprocessing` module is used for process pools and - tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - nproc (int): Process (worker) number. - initializer (None or callable): Refer to :class:`multiprocessing.Pool` - for details. - initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for - details. - chunksize (int): Refer to :class:`multiprocessing.Pool` for details. - bar_width (int): Width of progress bar. - skip_first (bool): Whether to skip the first sample for each worker - when estimating fps, since the initialization step may takes - longer. - keep_order (bool): If True, :func:`Pool.imap` is used, otherwise - :func:`Pool.imap_unordered` is used. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - pool = init_pool(nproc, initializer, initargs) - start = not skip_first - task_num -= nproc * chunksize * int(skip_first) - prog_bar = ProgressBar(task_num, bar_width, start, file=file) - results = [] - if keep_order: - gen = pool.imap(func, tasks, chunksize) - else: - gen = pool.imap_unordered(func, tasks, chunksize) - for result in gen: - results.append(result) - if skip_first: - if len(results) < nproc * chunksize: - continue - elif len(results) == nproc * chunksize: - prog_bar.start() - continue - prog_bar.update() - prog_bar.file.write('\n') - pool.close() - pool.join() - return results - - -def track_iter_progress(tasks, bar_width=50, file=sys.stdout): - """Track the progress of tasks iteration or enumeration with a progress - bar. - - Tasks are yielded with a simple for-loop. - - Args: - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Yields: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - for task in tasks: - yield task - prog_bar.update() - prog_bar.file.write('\n') diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/registry.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/registry.py deleted file mode 100644 index a6d92b68b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/registry.py +++ /dev/null @@ -1,337 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import warnings -from functools import partial - -from .misc import deprecated_api_warning, is_seq_of - - -def build_from_cfg(cfg, registry, default_args=None): - """Build a module from config dict when it is a class configuration, or - call a function from config dict when it is a function configuration. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = build_from_cfg(dict(type='Resnet'), MODELS) - >>> # Returns an instantiated object - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = build_from_cfg(dict(type='resnet50'), MODELS) - >>> # Return a result of the calling function - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type) or inspect.isfunction(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - try: - return obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{obj_cls.__name__}: {e}') - - -class Registry: - """A registry to map strings to classes or functions. - - Registered object could be built from registry. Meanwhile, registered - functions could be called from registry. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = MODELS.build(dict(type='ResNet')) - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = MODELS.build(dict(type='resnet50')) - - Please refer to - https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for - advanced usage. - - Args: - name (str): Registry name. - build_func(func, optional): Build function to construct instance from - Registry, func:`build_from_cfg` is used if neither ``parent`` or - ``build_func`` is specified. If ``parent`` is specified and - ``build_func`` is not given, ``build_func`` will be inherited - from ``parent``. Default: None. - parent (Registry, optional): Parent registry. The class registered in - children registry could be built from parent. Default: None. - scope (str, optional): The scope of registry. It is the key to search - for children registry. If not specified, scope will be the name of - the package where class is defined, e.g. mmdet, mmcls, mmseg. - Default: None. - """ - - def __init__(self, name, build_func=None, parent=None, scope=None): - self._name = name - self._module_dict = dict() - self._children = dict() - self._scope = self.infer_scope() if scope is None else scope - - # self.build_func will be set with the following priority: - # 1. build_func - # 2. parent.build_func - # 3. build_from_cfg - if build_func is None: - if parent is not None: - self.build_func = parent.build_func - else: - self.build_func = build_from_cfg - else: - self.build_func = build_func - if parent is not None: - assert isinstance(parent, Registry) - parent._add_children(self) - self.parent = parent - else: - self.parent = None - - def __len__(self): - return len(self._module_dict) - - def __contains__(self, key): - return self.get(key) is not None - - def __repr__(self): - format_str = self.__class__.__name__ + \ - f'(name={self._name}, ' \ - f'items={self._module_dict})' - return format_str - - @staticmethod - def infer_scope(): - """Infer the scope of registry. - - The name of the package where registry is defined will be returned. - - Example: - >>> # in mmdet/models/backbone/resnet.py - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - The scope of ``ResNet`` will be ``mmdet``. - - Returns: - str: The inferred scope name. - """ - # We access the caller using inspect.currentframe() instead of - # inspect.stack() for performance reasons. See details in PR #1844 - frame = inspect.currentframe() - # get the frame where `infer_scope()` is called - infer_scope_caller = frame.f_back.f_back - filename = inspect.getmodule(infer_scope_caller).__name__ - split_filename = filename.split('.') - return split_filename[0] - - @staticmethod - def split_scope_key(key): - """Split scope and key. - - The first scope will be split from key. - - Examples: - >>> Registry.split_scope_key('mmdet.ResNet') - 'mmdet', 'ResNet' - >>> Registry.split_scope_key('ResNet') - None, 'ResNet' - - Return: - tuple[str | None, str]: The former element is the first scope of - the key, which can be ``None``. The latter is the remaining key. - """ - split_index = key.find('.') - if split_index != -1: - return key[:split_index], key[split_index + 1:] - else: - return None, key - - @property - def name(self): - return self._name - - @property - def scope(self): - return self._scope - - @property - def module_dict(self): - return self._module_dict - - @property - def children(self): - return self._children - - def get(self, key): - """Get the registry record. - - Args: - key (str): The class name in string format. - - Returns: - class: The corresponding class. - """ - scope, real_key = self.split_scope_key(key) - if scope is None or scope == self._scope: - # get from self - if real_key in self._module_dict: - return self._module_dict[real_key] - else: - # get from self._children - if scope in self._children: - return self._children[scope].get(real_key) - else: - # goto root - parent = self.parent - while parent.parent is not None: - parent = parent.parent - return parent.get(key) - - def build(self, *args, **kwargs): - return self.build_func(*args, **kwargs, registry=self) - - def _add_children(self, registry): - """Add children for a registry. - - The ``registry`` will be added as children based on its scope. - The parent registry could build objects from children registry. - - Example: - >>> models = Registry('models') - >>> mmdet_models = Registry('models', parent=models) - >>> @mmdet_models.register_module() - >>> class ResNet: - >>> pass - >>> resnet = models.build(dict(type='mmdet.ResNet')) - """ - - assert isinstance(registry, Registry) - assert registry.scope is not None - assert registry.scope not in self.children, \ - f'scope {registry.scope} exists in {self.name} registry' - self.children[registry.scope] = registry - - @deprecated_api_warning(name_dict=dict(module_class='module')) - def _register_module(self, module, module_name=None, force=False): - if not inspect.isclass(module) and not inspect.isfunction(module): - raise TypeError('module must be a class or a function, ' - f'but got {type(module)}') - - if module_name is None: - module_name = module.__name__ - if isinstance(module_name, str): - module_name = [module_name] - for name in module_name: - if not force and name in self._module_dict: - raise KeyError(f'{name} is already registered ' - f'in {self.name}') - self._module_dict[name] = module - - def deprecated_register_module(self, cls=None, force=False): - warnings.warn( - 'The old API of register_module(module, force=False) ' - 'is deprecated and will be removed, please use the new API ' - 'register_module(name=None, force=False, module=None) instead.', - DeprecationWarning) - if cls is None: - return partial(self.deprecated_register_module, force=force) - self._register_module(cls, force=force) - return cls - - def register_module(self, name=None, force=False, module=None): - """Register a module. - - A record will be added to `self._module_dict`, whose key is the class - name or the specified name, and value is the class itself. - It can be used as a decorator or a normal function. - - Example: - >>> backbones = Registry('backbone') - >>> @backbones.register_module() - >>> class ResNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> @backbones.register_module(name='mnet') - >>> class MobileNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> class ResNet: - >>> pass - >>> backbones.register_module(ResNet) - - Args: - name (str | None): The module name to be registered. If not - specified, the class name will be used. - force (bool, optional): Whether to override an existing class with - the same name. Default: False. - module (type): Module class or function to be registered. - """ - if not isinstance(force, bool): - raise TypeError(f'force must be a boolean, but got {type(force)}') - # NOTE: This is a walkaround to be compatible with the old api, - # while it may introduce unexpected bugs. - if isinstance(name, type): - return self.deprecated_register_module(name, force=force) - - # raise the error ahead of time - if not (name is None or isinstance(name, str) or is_seq_of(name, str)): - raise TypeError( - 'name must be either of None, an instance of str or a sequence' - f' of str, but got {type(name)}') - - # use it as a normal method: x.register_module(module=SomeClass) - if module is not None: - self._register_module(module=module, module_name=name, force=force) - return module - - # use it as a decorator: @x.register_module() - def _register(module): - self._register_module(module=module, module_name=name, force=force) - return module - - return _register diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/seed.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/seed.py deleted file mode 100644 index 003f92367..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/seed.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import random - -import numpy as np -import torch - - -def worker_init_fn(worker_id: int, num_workers: int, rank: int, seed: int): - """Function to initialize each worker. - - The seed of each worker equals to - ``num_worker * rank + worker_id + user_seed``. - - Args: - worker_id (int): Id for each worker. - num_workers (int): Number of workers. - rank (int): Rank in distributed training. - seed (int): Random seed. - """ - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/testing.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/testing.py deleted file mode 100644 index 7b64e8fae..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/testing.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (c) Open-MMLab. -import sys -from collections.abc import Iterable -from runpy import run_path -from shlex import split -from typing import Any, Dict, List -from unittest.mock import patch - - -def check_python_script(cmd): - """Run the python cmd script with `__main__`. The difference between - `os.system` is that, this function exectues code in the current process, so - that it can be tracked by coverage tools. Currently it supports two forms: - - - ./tests/data/scripts/hello.py zz - - python tests/data/scripts/hello.py zz - """ - args = split(cmd) - if args[0] == 'python': - args = args[1:] - with patch.object(sys, 'argv', args): - run_path(args[0], run_name='__main__') - - -def _any(judge_result): - """Since built-in ``any`` works only when the element of iterable is not - iterable, implement the function.""" - if not isinstance(judge_result, Iterable): - return judge_result - - try: - for element in judge_result: - if _any(element): - return True - except TypeError: - # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) - if judge_result: - return True - return False - - -def assert_dict_contains_subset(dict_obj: Dict[Any, Any], - expected_subset: Dict[Any, Any]) -> bool: - """Check if the dict_obj contains the expected_subset. - - Args: - dict_obj (Dict[Any, Any]): Dict object to be checked. - expected_subset (Dict[Any, Any]): Subset expected to be contained in - dict_obj. - - Returns: - bool: Whether the dict_obj contains the expected_subset. - """ - - for key, value in expected_subset.items(): - if key not in dict_obj.keys() or _any(dict_obj[key] != value): - return False - return True - - -def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: - """Check if attribute of class object is correct. - - Args: - obj (object): Class object to be checked. - expected_attrs (Dict[str, Any]): Dict of the expected attrs. - - Returns: - bool: Whether the attribute of class object is correct. - """ - for attr, value in expected_attrs.items(): - if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): - return False - return True - - -def assert_dict_has_keys(obj: Dict[str, Any], - expected_keys: List[str]) -> bool: - """Check if the obj has all the expected_keys. - - Args: - obj (Dict[str, Any]): Object to be checked. - expected_keys (List[str]): Keys expected to contained in the keys of - the obj. - - Returns: - bool: Whether the obj has the expected keys. - """ - return set(expected_keys).issubset(set(obj.keys())) - - -def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: - """Check if target_keys is equal to result_keys. - - Args: - result_keys (List[str]): Result keys to be checked. - target_keys (List[str]): Target keys to be checked. - - Returns: - bool: Whether target_keys is equal to result_keys. - """ - return set(result_keys) == set(target_keys) - - -def assert_is_norm_layer(module) -> bool: - """Check if the module is a norm layer. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the module is a norm layer. - """ - from torch.nn import GroupNorm, LayerNorm - - from .parrots_wrapper import _BatchNorm, _InstanceNorm - norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) - return isinstance(module, norm_layer_candidates) - - -def assert_params_all_zeros(module) -> bool: - """Check if the parameters of the module is all zeros. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the parameters of the module is all zeros. - """ - weight_data = module.weight.data - is_weight_zero = weight_data.allclose( - weight_data.new_zeros(weight_data.size())) - - if hasattr(module, 'bias') and module.bias is not None: - bias_data = module.bias.data - is_bias_zero = bias_data.allclose( - bias_data.new_zeros(bias_data.size())) - else: - is_bias_zero = True - - return is_weight_zero and is_bias_zero diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/timer.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/timer.py deleted file mode 100644 index 02e96e537..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/timer.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from time import time - - -class TimerError(Exception): - - def __init__(self, message): - self.message = message - super(TimerError, self).__init__(message) - - -class Timer: - """A flexible Timer class. - - Examples: - >>> import time - >>> import mmcv - >>> with mmcv.Timer(): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - 1.000 - >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - it takes 1.0 seconds - >>> timer = mmcv.Timer() - >>> time.sleep(0.5) - >>> print(timer.since_start()) - 0.500 - >>> time.sleep(0.5) - >>> print(timer.since_last_check()) - 0.500 - >>> print(timer.since_start()) - 1.000 - """ - - def __init__(self, start=True, print_tmpl=None): - self._is_running = False - self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' - if start: - self.start() - - @property - def is_running(self): - """bool: indicate whether the timer is running""" - return self._is_running - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - print(self.print_tmpl.format(self.since_last_check())) - self._is_running = False - - def start(self): - """Start the timer.""" - if not self._is_running: - self._t_start = time() - self._is_running = True - self._t_last = time() - - def since_start(self): - """Total time since the timer is started. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - self._t_last = time() - return self._t_last - self._t_start - - def since_last_check(self): - """Time since the last checking. - - Either :func:`since_start` or :func:`since_last_check` is a checking - operation. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - dur = time() - self._t_last - self._t_last = time() - return dur - - -_g_timers = {} # global timers - - -def check_time(timer_id): - """Add check points in a single line. - - This method is suitable for running a task on a list of items. A timer will - be registered when the method is called for the first time. - - Examples: - >>> import time - >>> import mmcv - >>> for i in range(1, 6): - >>> # simulate a code block - >>> time.sleep(i) - >>> mmcv.check_time('task1') - 2.000 - 3.000 - 4.000 - 5.000 - - Args: - str: Timer identifier. - """ - if timer_id not in _g_timers: - _g_timers[timer_id] = Timer() - return 0 - else: - return _g_timers[timer_id].since_last_check() diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/trace.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/trace.py deleted file mode 100644 index 45423bd05..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/trace.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch - -from mmcv.utils import digit_version - - -def is_jit_tracing() -> bool: - if (torch.__version__ != 'parrots' - and digit_version(torch.__version__) >= digit_version('1.6.0')): - on_trace = torch.jit.is_tracing() - # In PyTorch 1.6, torch.jit.is_tracing has a bug. - # Refers to https://github.com/pytorch/pytorch/issues/42448 - if isinstance(on_trace, bool): - return on_trace - else: - return torch._C._is_tracing() - else: - warnings.warn( - 'torch.jit.is_tracing is only supported after v1.6.0. ' - 'Therefore is_tracing returns False automatically. Please ' - 'set on_trace manually if you are using trace.', UserWarning) - return False diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/version_utils.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/version_utils.py deleted file mode 100644 index 963c45a2e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/utils/version_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import subprocess -import warnings - -from packaging.version import parse - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - assert 'parrots' not in version_str - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - -def get_git_hash(fallback='unknown', digits=None): - """Get the git hash of the current repo. - - Args: - fallback (str, optional): The fallback string when git hash is - unavailable. Defaults to 'unknown'. - digits (int, optional): kept digits of the hash. Defaults to None, - meaning all digits are kept. - - Returns: - str: Git commit hash. - """ - - if digits is not None and not isinstance(digits, int): - raise TypeError('digits must be None or an integer') - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - sha = out.strip().decode('ascii') - if digits is not None: - sha = sha[:digits] - except OSError: - sha = fallback - - return sha diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmcv/version.py b/cv/semantic_segmentation/att_unet/pytorch/mmcv/version.py deleted file mode 100644 index a97ffc2dd..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmcv/version.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -__version__ = '1.5.0' - - -def parse_version_info(version_str: str, length: int = 4) -> tuple: - """Parse a version string into a tuple. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int | str]: The version info, e.g., "1.3.0" is parsed into - (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into - (2, 0, 0, 0, 'rc', 1) (when length is set to 4). - """ - from packaging.version import parse - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - release.extend(list(version.pre)) - elif version.is_postrelease: - release.extend(list(version.post)) - else: - release.extend([0, 0]) - return tuple(release) - - -version_info = tuple(int(x) for x in __version__.split('.')[:3]) - -__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/__init__.py deleted file mode 100644 index 360abfc85..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv -from packaging.version import parse - -from .version import __version__, version_info - -MMCV_MIN = '1.3.13' -MMCV_MAX = '1.6.0' - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -mmcv_min_version = digit_version(MMCV_MIN) -mmcv_max_version = digit_version(MMCV_MAX) -mmcv_version = digit_version(mmcv.__version__) - - -assert (mmcv_min_version <= mmcv_version <= mmcv_max_version), \ - f'MMCV=={mmcv.__version__} is used but incompatible. ' \ - f'Please install mmcv>={mmcv_min_version}, <={mmcv_max_version}.' - -__all__ = ['__version__', 'version_info', 'digit_version'] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/__init__.py deleted file mode 100644 index c68818053..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .inference import inference_segmentor, init_segmentor, show_result_pyplot -from .test import multi_gpu_test, single_gpu_test -from .train import (get_root_logger, init_random_seed, set_random_seed, - train_segmentor) - -__all__ = [ - 'get_root_logger', 'set_random_seed', 'train_segmentor', 'init_segmentor', - 'inference_segmentor', 'multi_gpu_test', 'single_gpu_test', - 'show_result_pyplot', 'init_random_seed' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/inference.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/inference.py deleted file mode 100644 index 906943804..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/inference.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import matplotlib.pyplot as plt -import mmcv -import torch -from mmcv.parallel import collate, scatter -from mmcv.runner import load_checkpoint - -from mmseg.datasets.pipelines import Compose -from mmseg.models import build_segmentor - - -def init_segmentor(config, checkpoint=None, device='cuda:0'): - """Initialize a segmentor from config file. - - Args: - config (str or :obj:`mmcv.Config`): Config file path or the config - object. - checkpoint (str, optional): Checkpoint path. If left as None, the model - will not load any weights. - device (str, optional) CPU/CUDA device option. Default 'cuda:0'. - Use 'cpu' for loading model on CPU. - Returns: - nn.Module: The constructed segmentor. - """ - if isinstance(config, str): - config = mmcv.Config.fromfile(config) - elif not isinstance(config, mmcv.Config): - raise TypeError('config must be a filename or Config object, ' - 'but got {}'.format(type(config))) - config.model.pretrained = None - config.model.train_cfg = None - model = build_segmentor(config.model, test_cfg=config.get('test_cfg')) - if checkpoint is not None: - checkpoint = load_checkpoint(model, checkpoint, map_location='cpu') - model.CLASSES = checkpoint['meta']['CLASSES'] - model.PALETTE = checkpoint['meta']['PALETTE'] - model.cfg = config # save the config in the model for convenience - model.to(device) - model.eval() - return model - - -class LoadImage: - """A simple pipeline to load image.""" - - def __call__(self, results): - """Call function to load images into results. - - Args: - results (dict): A result dict contains the file name - of the image to be read. - - Returns: - dict: ``results`` will be returned containing loaded image. - """ - - if isinstance(results['img'], str): - results['filename'] = results['img'] - results['ori_filename'] = results['img'] - else: - results['filename'] = None - results['ori_filename'] = None - img = mmcv.imread(results['img']) - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - return results - - -def inference_segmentor(model, img): - """Inference image(s) with the segmentor. - - Args: - model (nn.Module): The loaded segmentor. - imgs (str/ndarray or list[str/ndarray]): Either image files or loaded - images. - - Returns: - (list[Tensor]): The segmentation result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # build the data pipeline - test_pipeline = [LoadImage()] + cfg.data.test.pipeline[1:] - test_pipeline = Compose(test_pipeline) - # prepare data - data = dict(img=img) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if next(model.parameters()).is_cuda: - # scatter to specified GPU - data = scatter(data, [device])[0] - else: - data['img_metas'] = [i.data[0] for i in data['img_metas']] - - # forward the model - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - return result - - -def show_result_pyplot(model, - img, - result, - palette=None, - fig_size=(15, 10), - opacity=0.5, - title='', - block=True): - """Visualize the segmentation results on the image. - - Args: - model (nn.Module): The loaded segmentor. - img (str or np.ndarray): Image filename or loaded image. - result (list): The segmentation result. - palette (list[list[int]]] | None): The palette of segmentation - map. If None is given, random palette will be generated. - Default: None - fig_size (tuple): Figure size of the pyplot figure. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - title (str): The title of pyplot figure. - Default is ''. - block (bool): Whether to block the pyplot figure. - Default is True. - """ - if hasattr(model, 'module'): - model = model.module - img = model.show_result( - img, result, palette=palette, show=False, opacity=opacity) - plt.figure(figsize=fig_size) - plt.imshow(mmcv.bgr2rgb(img)) - plt.title(title) - plt.tight_layout() - plt.show(block=block) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/test.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/test.py deleted file mode 100644 index cc4fcc979..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/test.py +++ /dev/null @@ -1,233 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import tempfile -import warnings - -import mmcv -import numpy as np -import torch -from mmcv.engine import collect_results_cpu, collect_results_gpu -from mmcv.image import tensor2imgs -from mmcv.runner import get_dist_info - - -def np2tmp(array, temp_file_name=None, tmpdir=None): - """Save ndarray to local numpy file. - - Args: - array (ndarray): Ndarray to save. - temp_file_name (str): Numpy file name. If 'temp_file_name=None', this - function will generate a file name with tempfile.NamedTemporaryFile - to save ndarray. Default: None. - tmpdir (str): Temporary directory to save Ndarray files. Default: None. - Returns: - str: The numpy file name. - """ - - if temp_file_name is None: - temp_file_name = tempfile.NamedTemporaryFile( - suffix='.npy', delete=False, dir=tmpdir).name - np.save(temp_file_name, array) - return temp_file_name - - -def single_gpu_test(model, - data_loader, - show=False, - out_dir=None, - efficient_test=False, - opacity=0.5, - pre_eval=False, - format_only=False, - format_args={}): - """Test with single GPU by progressive mode. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - show (bool): Whether show results during inference. Default: False. - out_dir (str, optional): If specified, the results will be dumped into - the directory to save output results. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - loader_indices = data_loader.batch_sampler - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - - if show or out_dir: - img_tensor = data['img'][0] - img_metas = data['img_metas'][0].data[0] - imgs = tensor2imgs(img_tensor, **img_metas[0]['img_norm_cfg']) - assert len(imgs) == len(img_metas) - - for img, img_meta in zip(imgs, img_metas): - h, w, _ = img_meta['img_shape'] - img_show = img[:h, :w, :] - - ori_h, ori_w = img_meta['ori_shape'][:-1] - img_show = mmcv.imresize(img_show, (ori_w, ori_h)) - - if out_dir: - out_file = osp.join(out_dir, img_meta['ori_filename']) - else: - out_file = None - - model.module.show_result( - img_show, - result, - palette=dataset.PALETTE, - show=show, - out_file=out_file, - opacity=opacity) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - results.extend(result) - else: - results.extend(result) - - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - - return results - - -def multi_gpu_test(model, - data_loader, - tmpdir=None, - gpu_collect=False, - efficient_test=False, - pre_eval=False, - format_only=False, - format_args={}): - """Test model with multiple gpus by progressive mode. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' - it encodes results to gpu tensors and use gpu communication for results - collection. On cpu mode it saves the results on different gpus to 'tmpdir' - and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. The same path is used for efficient - test. Default: None. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - - # batch_sampler based on DistributedSampler, the indices only point to data - # samples of related machine. - loader_indices = data_loader.batch_sampler - - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - - results.extend(result) - - if rank == 0: - batch_size = len(result) * world_size - for _ in range(batch_size): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/train.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/train.py deleted file mode 100644 index 9be063785..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/apis/train.py +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import random -import warnings - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (HOOKS, DistSamplerSeedHook, EpochBasedRunner, - build_runner, get_dist_info) -from mmcv.utils import build_from_cfg - -from mmseg import digit_version -from mmseg.core import DistEvalHook, EvalHook, build_optimizer -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.utils import find_latest_checkpoint, get_root_logger - - -def init_random_seed(seed=None, device='cuda'): - """Initialize random seed. - - If the seed is not set, the seed will be automatically randomized, - and then broadcast to all processes to prevent some potential bugs. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is not None: - return seed - - # Make sure all ranks share the same random seed to prevent - # some potential bugs. Please refer to - # https://github.com/open-mmlab/mmdetection/issues/6339 - rank, world_size = get_dist_info() - seed = np.random.randint(2**31) - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() - - -def set_random_seed(seed, deterministic=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - """ - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - - -def train_segmentor(model, - dataset, - cfg, - distributed=False, - validate=False, - timestamp=None, - meta=None): - """Launch segmentor training.""" - logger = get_root_logger(cfg.log_level) - - # prepare data loaders - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - # The default loader config - loader_cfg = dict( - # cfg.gpus will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - seed=cfg.seed, - drop_last=True) - # The overall dataloader settings - loader_cfg.update({ - k: v - for k, v in cfg.data.items() if k not in [ - 'train', 'val', 'test', 'train_dataloader', 'val_dataloader', - 'test_dataloader' - ] - }) - - # The specific dataloader settings - train_loader_cfg = {**loader_cfg, **cfg.data.get('train_dataloader', {})} - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus - if distributed: - find_unused_parameters = cfg.get('find_unused_parameters', False) - # Sets the `find_unused_parameters` parameter in - # torch.nn.parallel.DistributedDataParallel - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - else: - if not torch.cuda.is_available(): - assert digit_version(mmcv.__version__) >= digit_version('1.4.4'), \ - 'Please use MMCV >= 1.4.4 for CPU training!' - model = MMDataParallel(model, device_ids=cfg.gpu_ids) - # build runner - optimizer = build_optimizer(model, cfg.optimizer) - - if cfg.get('runner') is None: - cfg.runner = {'type': 'IterBasedRunner', 'max_iters': cfg.total_iters} - warnings.warn( - 'config is now expected to have a `runner` section, ' - 'please set `runner` in your config.', UserWarning) - - runner = build_runner( - cfg.runner, - default_args=dict( - model=model, - batch_processor=None, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta)) - - runner.cfg = cfg - - # register hooks - runner.register_training_hooks(cfg.lr_config, cfg.optimizer_config, - cfg.checkpoint_config, cfg.log_config, - cfg.get('momentum_config', None)) - if distributed: - # when distributed training by epoch, using`DistSamplerSeedHook` to set - # the different seed to distributed sampler for each epoch, it will - # shuffle dataset at each epoch and avoid overfitting. - if isinstance(runner, EpochBasedRunner): - runner.register_hook(DistSamplerSeedHook()) - - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register eval hooks - if validate: - val_dataset = build_dataset(cfg.data.val, dict(test_mode=True)) - # The specific dataloader settings - val_loader_cfg = { - **loader_cfg, - 'samples_per_gpu': 1, - 'shuffle': False, # Not shuffle by default - **cfg.data.get('val_dataloader', {}), - } - val_dataloader = build_dataloader(val_dataset, **val_loader_cfg) - eval_cfg = cfg.get('evaluation', {}) - eval_cfg['by_epoch'] = cfg.runner['type'] != 'IterBasedRunner' - eval_hook = DistEvalHook if distributed else EvalHook - # In this PR (https://github.com/open-mmlab/mmcv/pull/1193), the - # priority of IterTimerHook has been modified from 'NORMAL' to 'LOW'. - runner.register_hook( - eval_hook(val_dataloader, **eval_cfg), priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from is None and cfg.get('auto_resume'): - resume_from = find_latest_checkpoint(cfg.work_dir) - if resume_from is not None: - cfg.resume_from = resume_from - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/__init__.py deleted file mode 100644 index 1a077d2f1..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, build_optimizer, - build_optimizer_constructor) -from .evaluation import * # noqa: F401, F403 -from .optimizers import * # noqa: F401, F403 -from .seg import * # noqa: F401, F403 -from .utils import * # noqa: F401, F403 - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/builder.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/builder.py deleted file mode 100644 index 406dd9b4b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/builder.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from mmcv.runner.optimizer import OPTIMIZER_BUILDERS as MMCV_OPTIMIZER_BUILDERS -from mmcv.utils import Registry, build_from_cfg - -OPTIMIZER_BUILDERS = Registry( - 'optimizer builder', parent=MMCV_OPTIMIZER_BUILDERS) - - -def build_optimizer_constructor(cfg): - constructor_type = cfg.get('type') - if constructor_type in OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - elif constructor_type in MMCV_OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, MMCV_OPTIMIZER_BUILDERS) - else: - raise KeyError(f'{constructor_type} is not registered ' - 'in the optimizer builder registry.') - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/__init__.py deleted file mode 100644 index 3d16d17e5..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .class_names import get_classes, get_palette -from .eval_hooks import DistEvalHook, EvalHook -from .metrics import (eval_metrics, intersect_and_union, mean_dice, - mean_fscore, mean_iou, pre_eval_to_metrics) - -__all__ = [ - 'EvalHook', 'DistEvalHook', 'mean_dice', 'mean_iou', 'mean_fscore', - 'eval_metrics', 'get_classes', 'get_palette', 'pre_eval_to_metrics', - 'intersect_and_union' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/class_names.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/class_names.py deleted file mode 100644 index e3bff6231..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/class_names.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - - -def cityscapes_classes(): - """Cityscapes class names for external use.""" - return [ - 'road', 'sidewalk', 'building', 'wall', 'fence', 'pole', - 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle' - ] - - -def ade_classes(): - """ADE20K class names for external use.""" - return [ - 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', - 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', - 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', - 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', - 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', - 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', - 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', - 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', - 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', - 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', - 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', - 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', - 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', - 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', - 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', - 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', - 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', - 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', - 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', - 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', - 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', - 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', - 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', - 'clock', 'flag' - ] - - -def voc_classes(): - """Pascal VOC class names for external use.""" - return [ - 'background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', - 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', - 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', - 'tvmonitor' - ] - - -def cocostuff_classes(): - """CocoStuff class names for external use.""" - return [ - 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', - 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', - 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', - 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', - 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', - 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', - 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', - 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', - 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', - 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', - 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', - 'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner', - 'blanket', 'branch', 'bridge', 'building-other', 'bush', 'cabinet', - 'cage', 'cardboard', 'carpet', 'ceiling-other', 'ceiling-tile', - 'cloth', 'clothes', 'clouds', 'counter', 'cupboard', 'curtain', - 'desk-stuff', 'dirt', 'door-stuff', 'fence', 'floor-marble', - 'floor-other', 'floor-stone', 'floor-tile', 'floor-wood', 'flower', - 'fog', 'food-other', 'fruit', 'furniture-other', 'grass', 'gravel', - 'ground-other', 'hill', 'house', 'leaves', 'light', 'mat', 'metal', - 'mirror-stuff', 'moss', 'mountain', 'mud', 'napkin', 'net', 'paper', - 'pavement', 'pillow', 'plant-other', 'plastic', 'platform', - 'playingfield', 'railing', 'railroad', 'river', 'road', 'rock', 'roof', - 'rug', 'salad', 'sand', 'sea', 'shelf', 'sky-other', 'skyscraper', - 'snow', 'solid-other', 'stairs', 'stone', 'straw', 'structural-other', - 'table', 'tent', 'textile-other', 'towel', 'tree', 'vegetable', - 'wall-brick', 'wall-concrete', 'wall-other', 'wall-panel', - 'wall-stone', 'wall-tile', 'wall-wood', 'water-other', 'waterdrops', - 'window-blind', 'window-other', 'wood' - ] - - -def loveda_classes(): - """LoveDA class names for external use.""" - return [ - 'background', 'building', 'road', 'water', 'barren', 'forest', - 'agricultural' - ] - - -def potsdam_classes(): - """Potsdam class names for external use.""" - return [ - 'impervious_surface', 'building', 'low_vegetation', 'tree', 'car', - 'clutter' - ] - - -def vaihingen_classes(): - """Vaihingen class names for external use.""" - return [ - 'impervious_surface', 'building', 'low_vegetation', 'tree', 'car', - 'clutter' - ] - - -def isaid_classes(): - """iSAID class names for external use.""" - return [ - 'background', 'ship', 'store_tank', 'baseball_diamond', 'tennis_court', - 'basketball_court', 'Ground_Track_Field', 'Bridge', 'Large_Vehicle', - 'Small_Vehicle', 'Helicopter', 'Swimming_pool', 'Roundabout', - 'Soccer_ball_field', 'plane', 'Harbor' - ] - - -def stare_classes(): - """stare class names for external use.""" - return ['background', 'vessel'] - - -def cityscapes_palette(): - """Cityscapes palette for external use.""" - return [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], - [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], - [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], - [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], [0, 80, 100], - [0, 0, 230], [119, 11, 32]] - - -def ade_palette(): - """ADE20K palette for external use.""" - return [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], - [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], - [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], - [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], - [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], - [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], - [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], - [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], - [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], - [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], - [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], - [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], - [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], - [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], - [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], - [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], - [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], - [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], - [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], - [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], - [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], - [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], - [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], - [102, 255, 0], [92, 0, 255]] - - -def voc_palette(): - """Pascal VOC palette for external use.""" - return [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], - [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], - [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], - [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], - [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] - - -def cocostuff_palette(): - """CocoStuff palette for external use.""" - return [[0, 192, 64], [0, 192, 64], [0, 64, 96], [128, 192, 192], - [0, 64, 64], [0, 192, 224], [0, 192, 192], [128, 192, 64], - [0, 192, 96], [128, 192, 64], [128, 32, 192], [0, 0, 224], - [0, 0, 64], [0, 160, 192], [128, 0, 96], [128, 0, 192], - [0, 32, 192], [128, 128, 224], [0, 0, 192], [128, 160, 192], - [128, 128, 0], [128, 0, 32], [128, 32, 0], [128, 0, 128], - [64, 128, 32], [0, 160, 0], [0, 0, 0], [192, 128, 160], [0, 32, 0], - [0, 128, 128], [64, 128, 160], [128, 160, 0], [0, 128, 0], - [192, 128, 32], [128, 96, 128], [0, 0, 128], [64, 0, 32], - [0, 224, 128], [128, 0, 0], [192, 0, 160], [0, 96, 128], - [128, 128, 128], [64, 0, 160], [128, 224, 128], [128, 128, 64], - [192, 0, 32], [128, 96, 0], [128, 0, 192], [0, 128, 32], - [64, 224, 0], [0, 0, 64], [128, 128, 160], [64, 96, 0], - [0, 128, 192], [0, 128, 160], [192, 224, 0], [0, 128, 64], - [128, 128, 32], [192, 32, 128], [0, 64, 192], [0, 0, 32], - [64, 160, 128], [128, 64, 64], [128, 0, 160], [64, 32, 128], - [128, 192, 192], [0, 0, 160], [192, 160, 128], [128, 192, 0], - [128, 0, 96], [192, 32, 0], [128, 64, 128], [64, 128, 96], - [64, 160, 0], [0, 64, 0], [192, 128, 224], [64, 32, 0], - [0, 192, 128], [64, 128, 224], [192, 160, 0], [0, 192, 0], - [192, 128, 96], [192, 96, 128], [0, 64, 128], [64, 0, 96], - [64, 224, 128], [128, 64, 0], [192, 0, 224], [64, 96, 128], - [128, 192, 128], [64, 0, 224], [192, 224, 128], [128, 192, 64], - [192, 0, 96], [192, 96, 0], [128, 64, 192], [0, 128, 96], - [0, 224, 0], [64, 64, 64], [128, 128, 224], [0, 96, 0], - [64, 192, 192], [0, 128, 224], [128, 224, 0], [64, 192, 64], - [128, 128, 96], [128, 32, 128], [64, 0, 192], [0, 64, 96], - [0, 160, 128], [192, 0, 64], [128, 64, 224], [0, 32, 128], - [192, 128, 192], [0, 64, 224], [128, 160, 128], [192, 128, 0], - [128, 64, 32], [128, 32, 64], [192, 0, 128], [64, 192, 32], - [0, 160, 64], [64, 0, 0], [192, 192, 160], [0, 32, 64], - [64, 128, 128], [64, 192, 160], [128, 160, 64], [64, 128, 0], - [192, 192, 32], [128, 96, 192], [64, 0, 128], [64, 64, 32], - [0, 224, 192], [192, 0, 0], [192, 64, 160], [0, 96, 192], - [192, 128, 128], [64, 64, 160], [128, 224, 192], [192, 128, 64], - [192, 64, 32], [128, 96, 64], [192, 0, 192], [0, 192, 32], - [64, 224, 64], [64, 0, 64], [128, 192, 160], [64, 96, 64], - [64, 128, 192], [0, 192, 160], [192, 224, 64], [64, 128, 64], - [128, 192, 32], [192, 32, 192], [64, 64, 192], [0, 64, 32], - [64, 160, 192], [192, 64, 64], [128, 64, 160], [64, 32, 192], - [192, 192, 192], [0, 64, 160], [192, 160, 192], [192, 192, 0], - [128, 64, 96], [192, 32, 64], [192, 64, 128], [64, 192, 96], - [64, 160, 64], [64, 64, 0]] - - -def loveda_palette(): - """LoveDA palette for external use.""" - return [[255, 255, 255], [255, 0, 0], [255, 255, 0], [0, 0, 255], - [159, 129, 183], [0, 255, 0], [255, 195, 128]] - - -def potsdam_palette(): - """Potsdam palette for external use.""" - return [[255, 255, 255], [0, 0, 255], [0, 255, 255], [0, 255, 0], - [255, 255, 0], [255, 0, 0]] - - -def vaihingen_palette(): - """Vaihingen palette for external use.""" - return [[255, 255, 255], [0, 0, 255], [0, 255, 255], [0, 255, 0], - [255, 255, 0], [255, 0, 0]] - - -def isaid_palette(): - """iSAID palette for external use.""" - return [[0, 0, 0], [0, 0, 63], [0, 63, 63], [0, 63, 0], [0, 63, 127], - [0, 63, 191], [0, 63, 255], [0, 127, 63], [0, 127, - 127], [0, 0, 127], - [0, 0, 191], [0, 0, 255], [0, 191, 127], [0, 127, 191], - [0, 127, 255], [0, 100, 155]] - - -def stare_palette(): - """STARE palette for external use.""" - return [[120, 120, 120], [6, 230, 230]] - - -dataset_aliases = { - 'cityscapes': ['cityscapes'], - 'ade': ['ade', 'ade20k'], - 'voc': ['voc', 'pascal_voc', 'voc12', 'voc12aug'], - 'loveda': ['loveda'], - 'potsdam': ['potsdam'], - 'vaihingen': ['vaihingen'], - 'cocostuff': [ - 'cocostuff', 'cocostuff10k', 'cocostuff164k', 'coco-stuff', - 'coco-stuff10k', 'coco-stuff164k', 'coco_stuff', 'coco_stuff10k', - 'coco_stuff164k' - ], - 'isaid': ['isaid', 'iSAID'], - 'stare': ['stare', 'STARE'] -} - - -def get_classes(dataset): - """Get class names of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_classes()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels - - -def get_palette(dataset): - """Get class palette (RGB) of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_palette()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/eval_hooks.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/eval_hooks.py deleted file mode 100644 index 952db3b0b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/eval_hooks.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -import torch.distributed as dist -from mmcv.runner import DistEvalHook as _DistEvalHook -from mmcv.runner import EvalHook as _EvalHook -from torch.nn.modules.batchnorm import _BatchNorm - - -class EvalHook(_EvalHook): - """Single GPU EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``single_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - if not self._should_evaluate(runner): - return - - from mmseg.apis import single_gpu_test - results = single_gpu_test( - runner.model, self.dataloader, show=False, pre_eval=self.pre_eval) - runner.log_buffer.clear() - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - if self.save_best: - self._save_ckpt(runner, key_score) - - -class DistEvalHook(_DistEvalHook): - """Distributed EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``multi_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - if not self._should_evaluate(runner): - return - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - from mmseg.apis import multi_gpu_test - results = multi_gpu_test( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect, - pre_eval=self.pre_eval) - - runner.log_buffer.clear() - - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - - if self.save_best: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/metrics.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/metrics.py deleted file mode 100644 index a1c0908e1..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/evaluation/metrics.py +++ /dev/null @@ -1,395 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import mmcv -import numpy as np -import torch - - -def f_score(precision, recall, beta=1): - """calculate the f-score value. - - Args: - precision (float | torch.Tensor): The precision value. - recall (float | torch.Tensor): The recall value. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - Returns: - [torch.tensor]: The f-score value. - """ - score = (1 + beta**2) * (precision * recall) / ( - (beta**2 * precision) + recall) - return score - - -def intersect_and_union(pred_label, - label, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate intersection and Union. - - Args: - pred_label (ndarray | str): Prediction segmentation map - or predict result filename. - label (ndarray | str): Ground truth segmentation map - or label filename. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. The parameter will - work only when label is str. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. The parameter will - work only when label is str. Default: False. - - Returns: - torch.Tensor: The intersection of prediction and ground truth - histogram on all classes. - torch.Tensor: The union of prediction and ground truth histogram on - all classes. - torch.Tensor: The prediction histogram on all classes. - torch.Tensor: The ground truth histogram on all classes. - """ - - if isinstance(pred_label, str): - pred_label = torch.from_numpy(np.load(pred_label)) - else: - pred_label = torch.from_numpy((pred_label)) - - if isinstance(label, str): - label = torch.from_numpy( - mmcv.imread(label, flag='unchanged', backend='pillow')) - else: - label = torch.from_numpy(label) - - if label_map is not None: - for old_id, new_id in label_map.items(): - label[label == old_id] = new_id - if reduce_zero_label: - label[label == 0] = 255 - label = label - 1 - label[label == 254] = 255 - - mask = (label != ignore_index) - pred_label = pred_label[mask] - label = label[mask] - - intersect = pred_label[pred_label == label] - area_intersect = torch.histc( - intersect.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_pred_label = torch.histc( - pred_label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_label = torch.histc( - label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_union = area_pred_label + area_label - area_intersect - return area_intersect, area_union, area_pred_label, area_label - - -def total_intersect_and_union(results, - gt_seg_maps, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate Total Intersection and Union. - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - ndarray: The intersection of prediction and ground truth histogram - on all classes. - ndarray: The union of prediction and ground truth histogram on all - classes. - ndarray: The prediction histogram on all classes. - ndarray: The ground truth histogram on all classes. - """ - total_area_intersect = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_union = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_pred_label = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_label = torch.zeros((num_classes, ), dtype=torch.float64) - for result, gt_seg_map in zip(results, gt_seg_maps): - area_intersect, area_union, area_pred_label, area_label = \ - intersect_and_union( - result, gt_seg_map, num_classes, ignore_index, - label_map, reduce_zero_label) - total_area_intersect += area_intersect - total_area_union += area_union - total_area_pred_label += area_pred_label - total_area_label += area_label - return total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label - - -def mean_iou(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category IoU, shape (num_classes, ). - """ - iou_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mIoU'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return iou_result - - -def mean_dice(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Dice (mDice) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category dice, shape (num_classes, ). - """ - - dice_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mDice'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return dice_result - - -def mean_fscore(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category recall, shape (num_classes, ). - ndarray: Per category precision, shape (num_classes, ). - ndarray: Per category f-score, shape (num_classes, ). - """ - fscore_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mFscore'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label, - beta=beta) - return fscore_result - - -def eval_metrics(results, - gt_seg_maps, - num_classes, - ignore_index, - metrics=['mIoU'], - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate evaluation metrics - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label = total_intersect_and_union( - results, gt_seg_maps, num_classes, ignore_index, label_map, - reduce_zero_label) - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def pre_eval_to_metrics(pre_eval_results, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Convert pre-eval results to metrics. - - Args: - pre_eval_results (list[tuple[torch.Tensor]]): per image eval results - for computing evaluation metric - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - # convert list of tuples to tuple of lists, e.g. - # [(A_1, B_1, C_1, D_1), ..., (A_n, B_n, C_n, D_n)] to - # ([A_1, ..., A_n], ..., [D_1, ..., D_n]) - pre_eval_results = tuple(zip(*pre_eval_results)) - assert len(pre_eval_results) == 4 - - total_area_intersect = sum(pre_eval_results[0]) - total_area_union = sum(pre_eval_results[1]) - total_area_pred_label = sum(pre_eval_results[2]) - total_area_label = sum(pre_eval_results[3]) - - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def total_area_to_metrics(total_area_intersect, - total_area_union, - total_area_pred_label, - total_area_label, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Calculate evaluation metrics - Args: - total_area_intersect (ndarray): The intersection of prediction and - ground truth histogram on all classes. - total_area_union (ndarray): The union of prediction and ground truth - histogram on all classes. - total_area_pred_label (ndarray): The prediction histogram on all - classes. - total_area_label (ndarray): The ground truth histogram on all classes. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - if isinstance(metrics, str): - metrics = [metrics] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metrics).issubset(set(allowed_metrics)): - raise KeyError('metrics {} is not supported'.format(metrics)) - - all_acc = total_area_intersect.sum() / total_area_label.sum() - ret_metrics = OrderedDict({'aAcc': all_acc}) - for metric in metrics: - if metric == 'mIoU': - iou = total_area_intersect / total_area_union - acc = total_area_intersect / total_area_label - ret_metrics['IoU'] = iou - ret_metrics['Acc'] = acc - elif metric == 'mDice': - dice = 2 * total_area_intersect / ( - total_area_pred_label + total_area_label) - acc = total_area_intersect / total_area_label - ret_metrics['Dice'] = dice - ret_metrics['Acc'] = acc - elif metric == 'mFscore': - precision = total_area_intersect / total_area_pred_label - recall = total_area_intersect / total_area_label - f_value = torch.tensor( - [f_score(x[0], x[1], beta) for x in zip(precision, recall)]) - ret_metrics['Fscore'] = f_value - ret_metrics['Precision'] = precision - ret_metrics['Recall'] = recall - - ret_metrics = { - metric: value.numpy() - for metric, value in ret_metrics.items() - } - if nan_to_num is not None: - ret_metrics = OrderedDict({ - metric: np.nan_to_num(metric_value, nan=nan_to_num) - for metric, metric_value in ret_metrics.items() - }) - return ret_metrics diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/optimizers/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/optimizers/__init__.py deleted file mode 100644 index 4fbf4ecfc..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/optimizers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .layer_decay_optimizer_constructor import ( - LayerDecayOptimizerConstructor, LearningRateDecayOptimizerConstructor) - -__all__ = [ - 'LearningRateDecayOptimizerConstructor', 'LayerDecayOptimizerConstructor' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py deleted file mode 100644 index 2b6b8ff9c..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import warnings - -from mmcv.runner import DefaultOptimizerConstructor, get_dist_info - -from mmseg.utils import get_root_logger -from ..builder import OPTIMIZER_BUILDERS - - -def get_layer_id_for_convnext(var_name, max_layer_id): - """Get the layer id to set the different learning rates in ``layer_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_layer_id (int): Maximum number of backbone layers. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - stage_id = int(var_name.split('.')[2]) - if stage_id == 0: - layer_id = 0 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - block_id = int(var_name.split('.')[3]) - if stage_id == 0: - layer_id = 1 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 + block_id // 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - else: - return max_layer_id + 1 - - -def get_stage_id_for_convnext(var_name, max_stage_id): - """Get the stage id to set the different learning rates in ``stage_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_stage_id (int): Maximum number of backbone layers. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - return 0 - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - return stage_id + 1 - else: - return max_stage_id - 1 - - -def get_layer_id_for_vit(var_name, max_layer_id): - """Get the layer id to set the different learning rates. - - Args: - var_name (str): The key of the model. - num_max_layer (int): Maximum number of backbone layers. - - Returns: - int: Returns the layer id of the key. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.patch_embed'): - return 0 - elif var_name.startswith('backbone.layers'): - layer_id = int(var_name.split('.')[2]) - return layer_id + 1 - else: - return max_layer_id - 1 - - -@OPTIMIZER_BUILDERS.register_module() -class LearningRateDecayOptimizerConstructor(DefaultOptimizerConstructor): - """Different learning rates are set for different layers of backbone. - - Note: Currently, this optimizer constructor is built for ConvNeXt, - BEiT and MAE. - """ - - def add_params(self, params, module, **kwargs): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - """ - logger = get_root_logger() - - parameter_groups = {} - logger.info(f'self.paramwise_cfg is {self.paramwise_cfg}') - num_layers = self.paramwise_cfg.get('num_layers') + 2 - decay_rate = self.paramwise_cfg.get('decay_rate') - decay_type = self.paramwise_cfg.get('decay_type', 'layer_wise') - logger.info('Build LearningRateDecayOptimizerConstructor ' - f'{decay_type} {decay_rate} - {num_layers}') - weight_decay = self.base_wd - for name, param in module.named_parameters(): - if not param.requires_grad: - continue # frozen weights - if len(param.shape) == 1 or name.endswith('.bias') or name in ( - 'pos_embed', 'cls_token'): - group_name = 'no_decay' - this_weight_decay = 0. - else: - group_name = 'decay' - this_weight_decay = weight_decay - if 'layer_wise' in decay_type: - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_convnext( - name, self.paramwise_cfg.get('num_layers')) - logger.info(f'set param {name} as id {layer_id}') - elif 'BEiT' in module.backbone.__class__.__name__ or \ - 'MAE' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_vit(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - elif decay_type == 'stage_wise': - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_stage_id_for_convnext(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - group_name = f'layer_{layer_id}_{group_name}' - - if group_name not in parameter_groups: - scale = decay_rate**(num_layers - layer_id - 1) - - parameter_groups[group_name] = { - 'weight_decay': this_weight_decay, - 'params': [], - 'param_names': [], - 'lr_scale': scale, - 'group_name': group_name, - 'lr': scale * self.base_lr, - } - - parameter_groups[group_name]['params'].append(param) - parameter_groups[group_name]['param_names'].append(name) - rank, _ = get_dist_info() - if rank == 0: - to_display = {} - for key in parameter_groups: - to_display[key] = { - 'param_names': parameter_groups[key]['param_names'], - 'lr_scale': parameter_groups[key]['lr_scale'], - 'lr': parameter_groups[key]['lr'], - 'weight_decay': parameter_groups[key]['weight_decay'], - } - logger.info(f'Param groups = {json.dumps(to_display, indent=2)}') - params.extend(parameter_groups.values()) - - -@OPTIMIZER_BUILDERS.register_module() -class LayerDecayOptimizerConstructor(LearningRateDecayOptimizerConstructor): - """Different learning rates are set for different layers of backbone. - - Note: Currently, this optimizer constructor is built for BEiT, - and it will be deprecated. - Please use ``LearningRateDecayOptimizerConstructor`` instead. - """ - - def __init__(self, optimizer_cfg, paramwise_cfg): - warnings.warn('DeprecationWarning: Original ' - 'LayerDecayOptimizerConstructor of BEiT ' - 'will be deprecated. Please use ' - 'LearningRateDecayOptimizerConstructor instead, ' - 'and set decay_type = layer_wise_vit in paramwise_cfg.') - paramwise_cfg.update({'decay_type': 'layer_wise_vit'}) - warnings.warn('DeprecationWarning: Layer_decay_rate will ' - 'be deleted, please use decay_rate instead.') - paramwise_cfg['decay_rate'] = paramwise_cfg.pop('layer_decay_rate') - super(LayerDecayOptimizerConstructor, - self).__init__(optimizer_cfg, paramwise_cfg) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/__init__.py deleted file mode 100644 index 5206b96be..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_pixel_sampler -from .sampler import BasePixelSampler, OHEMPixelSampler - -__all__ = ['build_pixel_sampler', 'BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/builder.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/builder.py deleted file mode 100644 index 1cecd347b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/builder.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, build_from_cfg - -PIXEL_SAMPLERS = Registry('pixel sampler') - - -def build_pixel_sampler(cfg, **default_args): - """Build pixel sampler for segmentation map.""" - return build_from_cfg(cfg, PIXEL_SAMPLERS, default_args) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/sampler/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/sampler/__init__.py deleted file mode 100644 index 5a7648564..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/sampler/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_pixel_sampler import BasePixelSampler -from .ohem_pixel_sampler import OHEMPixelSampler - -__all__ = ['BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py deleted file mode 100644 index 03672cd47..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BasePixelSampler(metaclass=ABCMeta): - """Base class of pixel sampler.""" - - def __init__(self, **kwargs): - pass - - @abstractmethod - def sample(self, seg_logit, seg_label): - """Placeholder for sample function.""" diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py deleted file mode 100644 index 833a28768..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import PIXEL_SAMPLERS -from .base_pixel_sampler import BasePixelSampler - - -@PIXEL_SAMPLERS.register_module() -class OHEMPixelSampler(BasePixelSampler): - """Online Hard Example Mining Sampler for segmentation. - - Args: - context (nn.Module): The context of sampler, subclass of - :obj:`BaseDecodeHead`. - thresh (float, optional): The threshold for hard example selection. - Below which, are prediction with low confidence. If not - specified, the hard examples will be pixels of top ``min_kept`` - loss. Default: None. - min_kept (int, optional): The minimum number of predictions to keep. - Default: 100000. - """ - - def __init__(self, context, thresh=None, min_kept=100000): - super(OHEMPixelSampler, self).__init__() - self.context = context - assert min_kept > 1 - self.thresh = thresh - self.min_kept = min_kept - - def sample(self, seg_logit, seg_label): - """Sample pixels that have high loss or with low prediction confidence. - - Args: - seg_logit (torch.Tensor): segmentation logits, shape (N, C, H, W) - seg_label (torch.Tensor): segmentation label, shape (N, 1, H, W) - - Returns: - torch.Tensor: segmentation weight, shape (N, H, W) - """ - with torch.no_grad(): - assert seg_logit.shape[2:] == seg_label.shape[2:] - assert seg_label.shape[1] == 1 - seg_label = seg_label.squeeze(1).long() - batch_kept = self.min_kept * seg_label.size(0) - valid_mask = seg_label != self.context.ignore_index - seg_weight = seg_logit.new_zeros(size=seg_label.size()) - valid_seg_weight = seg_weight[valid_mask] - if self.thresh is not None: - seg_prob = F.softmax(seg_logit, dim=1) - - tmp_seg_label = seg_label.clone().unsqueeze(1) - tmp_seg_label[tmp_seg_label == self.context.ignore_index] = 0 - seg_prob = seg_prob.gather(1, tmp_seg_label).squeeze(1) - sort_prob, sort_indices = seg_prob[valid_mask].sort() - - if sort_prob.numel() > 0: - min_threshold = sort_prob[min(batch_kept, - sort_prob.numel() - 1)] - else: - min_threshold = 0.0 - threshold = max(min_threshold, self.thresh) - valid_seg_weight[seg_prob[valid_mask] < threshold] = 1. - else: - if not isinstance(self.context.loss_decode, nn.ModuleList): - losses_decode = [self.context.loss_decode] - else: - losses_decode = self.context.loss_decode - losses = 0.0 - for loss_module in losses_decode: - losses += loss_module( - seg_logit, - seg_label, - weight=None, - ignore_index=self.context.ignore_index, - reduction_override='none') - - # faster than topk according to https://github.com/pytorch/pytorch/issues/22812 # noqa - _, sort_indices = losses[valid_mask].sort(descending=True) - valid_seg_weight[sort_indices[:batch_kept]] = 1. - - seg_weight[valid_mask] = valid_seg_weight - - return seg_weight diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/utils/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/utils/__init__.py deleted file mode 100644 index 28882893a..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/utils/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .dist_util import check_dist_init, sync_random_seed -from .misc import add_prefix - -__all__ = ['add_prefix', 'check_dist_init', 'sync_random_seed'] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/utils/dist_util.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/utils/dist_util.py deleted file mode 100644 index b3288519d..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/utils/dist_util.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def check_dist_init(): - return dist.is_available() and dist.is_initialized() - - -def sync_random_seed(seed=None, device='cuda'): - """Make sure different ranks share the same seed. All workers must call - this function, otherwise it will deadlock. This method is generally used in - `DistributedSampler`, because the seed should be identical across all - processes in the distributed group. - - In distributed sampling, different ranks should sample non-overlapped - data in the dataset. Therefore, this function is used to make sure that - each rank shuffles the data indices in the same order based - on the same seed. Then different ranks could use different indices - to select non-overlapped data from the same data list. - - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - - if seed is None: - seed = np.random.randint(2**31) - assert isinstance(seed, int) - - rank, world_size = get_dist_info() - - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/utils/misc.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/utils/misc.py deleted file mode 100644 index 282bb8d96..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/core/utils/misc.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def add_prefix(inputs, prefix): - """Add prefix for dict. - - Args: - inputs (dict): The input dict with str keys. - prefix (str): The prefix to add. - - Returns: - - dict: The dict with keys updated with ``prefix``. - """ - - outputs = dict() - for name, value in inputs.items(): - outputs[f'{prefix}.{name}'] = value - - return outputs diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/__init__.py deleted file mode 100644 index 3366f0aec..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .ade import ADE20KDataset -from .builder import DATASETS, PIPELINES, build_dataloader, build_dataset -from .cityscapes import CityscapesDataset -from .coco_stuff import COCOStuffDataset -from .custom import CustomDataset -from .dataset_wrappers import (ConcatDataset, MultiImageMixDataset, - RepeatDataset) -from .pascal_context import PascalContextDataset, PascalContextDataset59 -from .voc import PascalVOCDataset diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/ade.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/ade.py deleted file mode 100644 index db94cebd3..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/ade.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np -from PIL import Image - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class ADE20KDataset(CustomDataset): - """ADE20K dataset. - - In segmentation map annotation for ADE20K, 0 stands for background, which - is not included in 150 categories. ``reduce_zero_label`` is fixed to True. - The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is fixed to - '.png'. - """ - CLASSES = ( - 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', - 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', - 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', - 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', - 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', - 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', - 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', - 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', - 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', - 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', - 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', - 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', - 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', - 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', - 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', - 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', - 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', - 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', - 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', - 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', - 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', - 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', - 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', - 'clock', 'flag') - - PALETTE = [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], - [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], - [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], - [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], - [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], - [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], - [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], - [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], - [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], - [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], - [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], - [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], - [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], - [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], - [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], - [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], - [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], - [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], - [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], - [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], - [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], - [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], - [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], - [102, 255, 0], [92, 0, 255]] - - def __init__(self, **kwargs): - super(ADE20KDataset, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - reduce_zero_label=True, - **kwargs) - - def results2img(self, results, imgfile_prefix, to_label_id, indices=None): - """Write the segmentation results to images. - - Args: - results (list[ndarray]): Testing results of the - dataset. - imgfile_prefix (str): The filename prefix of the png files. - If the prefix is "somepath/xxx", - the png files will be named "somepath/xxx.png". - to_label_id (bool): whether convert output to label_id for - submission. - indices (list[int], optional): Indices of input results, if not - set, all the indices of the dataset will be used. - Default: None. - - Returns: - list[str: str]: result txt files which contains corresponding - semantic segmentation images. - """ - if indices is None: - indices = list(range(len(self))) - - mmcv.mkdir_or_exist(imgfile_prefix) - result_files = [] - for result, idx in zip(results, indices): - - filename = self.img_infos[idx]['filename'] - basename = osp.splitext(osp.basename(filename))[0] - - png_filename = osp.join(imgfile_prefix, f'{basename}.png') - - # The index range of official requirement is from 0 to 150. - # But the index range of output is from 0 to 149. - # That is because we set reduce_zero_label=True. - result = result + 1 - - output = Image.fromarray(result.astype(np.uint8)) - output.save(png_filename) - result_files.append(png_filename) - - return result_files - - def format_results(self, - results, - imgfile_prefix, - to_label_id=True, - indices=None): - """Format the results into dir (standard format for ade20k evaluation). - - Args: - results (list): Testing results of the dataset. - imgfile_prefix (str | None): The prefix of images files. It - includes the file path and the prefix of filename, e.g., - "a/b/prefix". - to_label_id (bool): whether convert output to label_id for - submission. Default: False - indices (list[int], optional): Indices of input results, if not - set, all the indices of the dataset will be used. - Default: None. - - Returns: - tuple: (result_files, tmp_dir), result_files is a list containing - the image paths, tmp_dir is the temporal directory created - for saving json/png files when img_prefix is not specified. - """ - - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - result_files = self.results2img(results, imgfile_prefix, to_label_id, - indices) - return result_files diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/builder.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/builder.py deleted file mode 100644 index 4d852d365..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/builder.py +++ /dev/null @@ -1,191 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import platform -import random -from functools import partial - -import numpy as np -import torch -from mmcv.parallel import collate -from mmcv.runner import get_dist_info -from mmcv.utils import Registry, build_from_cfg, digit_version -from torch.utils.data import DataLoader - -from .samplers import DistributedSampler - -if platform.system() != 'Windows': - # https://github.com/pytorch/pytorch/issues/973 - import resource - rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) - base_soft_limit = rlimit[0] - hard_limit = rlimit[1] - soft_limit = min(max(4096, base_soft_limit), hard_limit) - resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) - -DATASETS = Registry('dataset') -PIPELINES = Registry('pipeline') - - -def _concat_dataset(cfg, default_args=None): - """Build :obj:`ConcatDataset by.""" - from .dataset_wrappers import ConcatDataset - img_dir = cfg['img_dir'] - ann_dir = cfg.get('ann_dir', None) - split = cfg.get('split', None) - # pop 'separate_eval' since it is not a valid key for common datasets. - separate_eval = cfg.pop('separate_eval', True) - num_img_dir = len(img_dir) if isinstance(img_dir, (list, tuple)) else 1 - if ann_dir is not None: - num_ann_dir = len(ann_dir) if isinstance(ann_dir, (list, tuple)) else 1 - else: - num_ann_dir = 0 - if split is not None: - num_split = len(split) if isinstance(split, (list, tuple)) else 1 - else: - num_split = 0 - if num_img_dir > 1: - assert num_img_dir == num_ann_dir or num_ann_dir == 0 - assert num_img_dir == num_split or num_split == 0 - else: - assert num_split == num_ann_dir or num_ann_dir <= 1 - num_dset = max(num_split, num_img_dir) - - datasets = [] - for i in range(num_dset): - data_cfg = copy.deepcopy(cfg) - if isinstance(img_dir, (list, tuple)): - data_cfg['img_dir'] = img_dir[i] - if isinstance(ann_dir, (list, tuple)): - data_cfg['ann_dir'] = ann_dir[i] - if isinstance(split, (list, tuple)): - data_cfg['split'] = split[i] - datasets.append(build_dataset(data_cfg, default_args)) - - return ConcatDataset(datasets, separate_eval) - - -def build_dataset(cfg, default_args=None): - """Build datasets.""" - from .dataset_wrappers import (ConcatDataset, MultiImageMixDataset, - RepeatDataset) - if isinstance(cfg, (list, tuple)): - dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) - elif cfg['type'] == 'RepeatDataset': - dataset = RepeatDataset( - build_dataset(cfg['dataset'], default_args), cfg['times']) - elif cfg['type'] == 'MultiImageMixDataset': - cp_cfg = copy.deepcopy(cfg) - cp_cfg['dataset'] = build_dataset(cp_cfg['dataset']) - cp_cfg.pop('type') - dataset = MultiImageMixDataset(**cp_cfg) - elif isinstance(cfg.get('img_dir'), (list, tuple)) or isinstance( - cfg.get('split', None), (list, tuple)): - dataset = _concat_dataset(cfg, default_args) - else: - dataset = build_from_cfg(cfg, DATASETS, default_args) - - return dataset - - -def build_dataloader(dataset, - samples_per_gpu, - workers_per_gpu, - num_gpus=1, - dist=True, - shuffle=True, - seed=None, - drop_last=False, - pin_memory=True, - persistent_workers=True, - **kwargs): - """Build PyTorch DataLoader. - - In distributed training, each GPU/process has a dataloader. - In non-distributed training, there is only one dataloader for all GPUs. - - Args: - dataset (Dataset): A PyTorch dataset. - samples_per_gpu (int): Number of training samples on each GPU, i.e., - batch size of each GPU. - workers_per_gpu (int): How many subprocesses to use for data loading - for each GPU. - num_gpus (int): Number of GPUs. Only used in non-distributed training. - dist (bool): Distributed training/test or not. Default: True. - shuffle (bool): Whether to shuffle the data at every epoch. - Default: True. - seed (int | None): Seed to be used. Default: None. - drop_last (bool): Whether to drop the last incomplete batch in epoch. - Default: False - pin_memory (bool): Whether to use pin_memory in DataLoader. - Default: True - persistent_workers (bool): If True, the data loader will not shutdown - the worker processes after a dataset has been consumed once. - This allows to maintain the workers Dataset instances alive. - The argument also has effect in PyTorch>=1.7.0. - Default: True - kwargs: any keyword argument to be used to initialize DataLoader - - Returns: - DataLoader: A PyTorch dataloader. - """ - rank, world_size = get_dist_info() - if dist: - sampler = DistributedSampler( - dataset, world_size, rank, shuffle=shuffle, seed=seed) - shuffle = False - batch_size = samples_per_gpu - num_workers = workers_per_gpu - else: - sampler = None - batch_size = num_gpus * samples_per_gpu - num_workers = num_gpus * workers_per_gpu - - init_fn = partial( - worker_init_fn, num_workers=num_workers, rank=rank, - seed=seed) if seed is not None else None - - if digit_version(torch.__version__) >= digit_version('1.8.0'): - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - persistent_workers=persistent_workers, - **kwargs) - else: - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - **kwargs) - - return data_loader - - -def worker_init_fn(worker_id, num_workers, rank, seed): - """Worker init func for dataloader. - - The seed of each worker equals to num_worker * rank + worker_id + user_seed - - Args: - worker_id (int): Worker id. - num_workers (int): Number of workers. - rank (int): The rank of current process. - seed (int): The random seed to use. - """ - - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/cityscapes.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/cityscapes.py deleted file mode 100644 index ed633d00d..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/cityscapes.py +++ /dev/null @@ -1,214 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np -from mmcv.utils import print_log -from PIL import Image - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class CityscapesDataset(CustomDataset): - """Cityscapes dataset. - - The ``img_suffix`` is fixed to '_leftImg8bit.png' and ``seg_map_suffix`` is - fixed to '_gtFine_labelTrainIds.png' for Cityscapes dataset. - """ - - CLASSES = ('road', 'sidewalk', 'building', 'wall', 'fence', 'pole', - 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle') - - PALETTE = [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], - [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], - [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], - [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], - [0, 80, 100], [0, 0, 230], [119, 11, 32]] - - def __init__(self, - img_suffix='_leftImg8bit.png', - seg_map_suffix='_gtFine_labelTrainIds.png', - **kwargs): - super(CityscapesDataset, self).__init__( - img_suffix=img_suffix, seg_map_suffix=seg_map_suffix, **kwargs) - - @staticmethod - def _convert_to_label_id(result): - """Convert trainId to id for cityscapes.""" - if isinstance(result, str): - result = np.load(result) - import cityscapesscripts.helpers.labels as CSLabels - result_copy = result.copy() - for trainId, label in CSLabels.trainId2label.items(): - result_copy[result == trainId] = label.id - - return result_copy - - def results2img(self, results, imgfile_prefix, to_label_id, indices=None): - """Write the segmentation results to images. - - Args: - results (list[ndarray]): Testing results of the - dataset. - imgfile_prefix (str): The filename prefix of the png files. - If the prefix is "somepath/xxx", - the png files will be named "somepath/xxx.png". - to_label_id (bool): whether convert output to label_id for - submission. - indices (list[int], optional): Indices of input results, - if not set, all the indices of the dataset will be used. - Default: None. - - Returns: - list[str: str]: result txt files which contains corresponding - semantic segmentation images. - """ - if indices is None: - indices = list(range(len(self))) - - mmcv.mkdir_or_exist(imgfile_prefix) - result_files = [] - for result, idx in zip(results, indices): - if to_label_id: - result = self._convert_to_label_id(result) - filename = self.img_infos[idx]['filename'] - basename = osp.splitext(osp.basename(filename))[0] - - png_filename = osp.join(imgfile_prefix, f'{basename}.png') - - output = Image.fromarray(result.astype(np.uint8)).convert('P') - import cityscapesscripts.helpers.labels as CSLabels - palette = np.zeros((len(CSLabels.id2label), 3), dtype=np.uint8) - for label_id, label in CSLabels.id2label.items(): - palette[label_id] = label.color - - output.putpalette(palette) - output.save(png_filename) - result_files.append(png_filename) - - return result_files - - def format_results(self, - results, - imgfile_prefix, - to_label_id=True, - indices=None): - """Format the results into dir (standard format for Cityscapes - evaluation). - - Args: - results (list): Testing results of the dataset. - imgfile_prefix (str): The prefix of images files. It - includes the file path and the prefix of filename, e.g., - "a/b/prefix". - to_label_id (bool): whether convert output to label_id for - submission. Default: False - indices (list[int], optional): Indices of input results, - if not set, all the indices of the dataset will be used. - Default: None. - - Returns: - tuple: (result_files, tmp_dir), result_files is a list containing - the image paths, tmp_dir is the temporal directory created - for saving json/png files when img_prefix is not specified. - """ - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - result_files = self.results2img(results, imgfile_prefix, to_label_id, - indices) - - return result_files - - def evaluate(self, - results, - metric='mIoU', - logger=None, - imgfile_prefix=None): - """Evaluation in Cityscapes/default protocol. - - Args: - results (list): Testing results of the dataset. - metric (str | list[str]): Metrics to be evaluated. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - imgfile_prefix (str | None): The prefix of output image file, - for cityscapes evaluation only. It includes the file path and - the prefix of filename, e.g., "a/b/prefix". - If results are evaluated with cityscapes protocol, it would be - the prefix of output png files. The output files would be - png images under folder "a/b/prefix/xxx.png", where "xxx" is - the image name of cityscapes. If not specified, a temp file - will be created for evaluation. - Default: None. - - Returns: - dict[str, float]: Cityscapes/default metrics. - """ - - eval_results = dict() - metrics = metric.copy() if isinstance(metric, list) else [metric] - if 'cityscapes' in metrics: - eval_results.update( - self._evaluate_cityscapes(results, logger, imgfile_prefix)) - metrics.remove('cityscapes') - if len(metrics) > 0: - eval_results.update( - super(CityscapesDataset, - self).evaluate(results, metrics, logger)) - - return eval_results - - def _evaluate_cityscapes(self, results, logger, imgfile_prefix): - """Evaluation in Cityscapes protocol. - - Args: - results (list): Testing results of the dataset. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - imgfile_prefix (str | None): The prefix of output image file - - Returns: - dict[str: float]: Cityscapes evaluation results. - """ - try: - import cityscapesscripts.evaluation.evalPixelLevelSemanticLabeling as CSEval # noqa - except ImportError: - raise ImportError('Please run "pip install cityscapesscripts" to ' - 'install cityscapesscripts first.') - msg = 'Evaluating in Cityscapes style' - if logger is None: - msg = '\n' + msg - print_log(msg, logger=logger) - - result_dir = imgfile_prefix - - eval_results = dict() - print_log(f'Evaluating results under {result_dir} ...', logger=logger) - - CSEval.args.evalInstLevelScore = True - CSEval.args.predictionPath = osp.abspath(result_dir) - CSEval.args.evalPixelAccuracy = True - CSEval.args.JSONOutput = False - - seg_map_list = [] - pred_list = [] - - # when evaluating with official cityscapesscripts, - # **_gtFine_labelIds.png is used - for seg_map in mmcv.scandir( - self.ann_dir, 'gtFine_labelIds.png', recursive=True): - seg_map_list.append(osp.join(self.ann_dir, seg_map)) - pred_list.append(CSEval.getPrediction(CSEval.args, seg_map)) - - eval_results.update( - CSEval.evaluateImgLists(pred_list, seg_map_list, CSEval.args)) - - return eval_results diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/coco_stuff.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/coco_stuff.py deleted file mode 100644 index 24d089556..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/coco_stuff.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class COCOStuffDataset(CustomDataset): - """COCO-Stuff dataset. - - In segmentation map annotation for COCO-Stuff, Train-IDs of the 10k version - are from 1 to 171, where 0 is the ignore index, and Train-ID of COCO Stuff - 164k is from 0 to 170, where 255 is the ignore index. So, they are all 171 - semantic categories. ``reduce_zero_label`` is set to True and False for the - 10k and 164k versions, respectively. The ``img_suffix`` is fixed to '.jpg', - and ``seg_map_suffix`` is fixed to '.png'. - """ - CLASSES = ( - 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', - 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', - 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', - 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', - 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', - 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', - 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', - 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', - 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', - 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', - 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', - 'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner', - 'blanket', 'branch', 'bridge', 'building-other', 'bush', 'cabinet', - 'cage', 'cardboard', 'carpet', 'ceiling-other', 'ceiling-tile', - 'cloth', 'clothes', 'clouds', 'counter', 'cupboard', 'curtain', - 'desk-stuff', 'dirt', 'door-stuff', 'fence', 'floor-marble', - 'floor-other', 'floor-stone', 'floor-tile', 'floor-wood', - 'flower', 'fog', 'food-other', 'fruit', 'furniture-other', 'grass', - 'gravel', 'ground-other', 'hill', 'house', 'leaves', 'light', 'mat', - 'metal', 'mirror-stuff', 'moss', 'mountain', 'mud', 'napkin', 'net', - 'paper', 'pavement', 'pillow', 'plant-other', 'plastic', 'platform', - 'playingfield', 'railing', 'railroad', 'river', 'road', 'rock', 'roof', - 'rug', 'salad', 'sand', 'sea', 'shelf', 'sky-other', 'skyscraper', - 'snow', 'solid-other', 'stairs', 'stone', 'straw', 'structural-other', - 'table', 'tent', 'textile-other', 'towel', 'tree', 'vegetable', - 'wall-brick', 'wall-concrete', 'wall-other', 'wall-panel', - 'wall-stone', 'wall-tile', 'wall-wood', 'water-other', 'waterdrops', - 'window-blind', 'window-other', 'wood') - - PALETTE = [[0, 192, 64], [0, 192, 64], [0, 64, 96], [128, 192, 192], - [0, 64, 64], [0, 192, 224], [0, 192, 192], [128, 192, 64], - [0, 192, 96], [128, 192, 64], [128, 32, 192], [0, 0, 224], - [0, 0, 64], [0, 160, 192], [128, 0, 96], [128, 0, 192], - [0, 32, 192], [128, 128, 224], [0, 0, 192], [128, 160, 192], - [128, 128, 0], [128, 0, 32], [128, 32, 0], [128, 0, 128], - [64, 128, 32], [0, 160, 0], [0, 0, 0], [192, 128, 160], - [0, 32, 0], [0, 128, 128], [64, 128, 160], [128, 160, 0], - [0, 128, 0], [192, 128, 32], [128, 96, 128], [0, 0, 128], - [64, 0, 32], [0, 224, 128], [128, 0, 0], [192, 0, 160], - [0, 96, 128], [128, 128, 128], [64, 0, 160], [128, 224, 128], - [128, 128, 64], [192, 0, 32], [128, 96, 0], [128, 0, 192], - [0, 128, 32], [64, 224, 0], [0, 0, 64], [128, 128, 160], - [64, 96, 0], [0, 128, 192], [0, 128, 160], [192, 224, 0], - [0, 128, 64], [128, 128, 32], [192, 32, 128], [0, 64, 192], - [0, 0, 32], [64, 160, 128], [128, 64, 64], [128, 0, 160], - [64, 32, 128], [128, 192, 192], [0, 0, 160], [192, 160, 128], - [128, 192, 0], [128, 0, 96], [192, 32, 0], [128, 64, 128], - [64, 128, 96], [64, 160, 0], [0, 64, 0], [192, 128, 224], - [64, 32, 0], [0, 192, 128], [64, 128, 224], [192, 160, 0], - [0, 192, 0], [192, 128, 96], [192, 96, 128], [0, 64, 128], - [64, 0, 96], [64, 224, 128], [128, 64, 0], [192, 0, 224], - [64, 96, 128], [128, 192, 128], [64, 0, 224], [192, 224, 128], - [128, 192, 64], [192, 0, 96], [192, 96, 0], [128, 64, 192], - [0, 128, 96], [0, 224, 0], [64, 64, 64], [128, 128, 224], - [0, 96, 0], [64, 192, 192], [0, 128, 224], [128, 224, 0], - [64, 192, 64], [128, 128, 96], [128, 32, 128], [64, 0, 192], - [0, 64, 96], [0, 160, 128], [192, 0, 64], [128, 64, 224], - [0, 32, 128], [192, 128, 192], [0, 64, 224], [128, 160, 128], - [192, 128, 0], [128, 64, 32], [128, 32, 64], [192, 0, 128], - [64, 192, 32], [0, 160, 64], [64, 0, 0], [192, 192, 160], - [0, 32, 64], [64, 128, 128], [64, 192, 160], [128, 160, 64], - [64, 128, 0], [192, 192, 32], [128, 96, 192], [64, 0, 128], - [64, 64, 32], [0, 224, 192], [192, 0, 0], [192, 64, 160], - [0, 96, 192], [192, 128, 128], [64, 64, 160], [128, 224, 192], - [192, 128, 64], [192, 64, 32], [128, 96, 64], [192, 0, 192], - [0, 192, 32], [64, 224, 64], [64, 0, 64], [128, 192, 160], - [64, 96, 64], [64, 128, 192], [0, 192, 160], [192, 224, 64], - [64, 128, 64], [128, 192, 32], [192, 32, 192], [64, 64, 192], - [0, 64, 32], [64, 160, 192], [192, 64, 64], [128, 64, 160], - [64, 32, 192], [192, 192, 192], [0, 64, 160], [192, 160, 192], - [192, 192, 0], [128, 64, 96], [192, 32, 64], [192, 64, 128], - [64, 192, 96], [64, 160, 64], [64, 64, 0]] - - def __init__(self, **kwargs): - super(COCOStuffDataset, self).__init__( - img_suffix='.jpg', seg_map_suffix='_labelTrainIds.png', **kwargs) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/custom.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/custom.py deleted file mode 100644 index 4615d4114..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/custom.py +++ /dev/null @@ -1,487 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from collections import OrderedDict - -import mmcv -import numpy as np -from mmcv.utils import print_log -from prettytable import PrettyTable -from torch.utils.data import Dataset - -from mmseg.core import eval_metrics, intersect_and_union, pre_eval_to_metrics -from mmseg.utils import get_root_logger -from .builder import DATASETS -from .pipelines import Compose, LoadAnnotations - - -@DATASETS.register_module() -class CustomDataset(Dataset): - """Custom dataset for semantic segmentation. An example of file structure - is as followed. - - .. code-block:: none - - ├── data - │ ├── my_dataset - │ │ ├── img_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{img_suffix} - │ │ │ │ ├── yyy{img_suffix} - │ │ │ │ ├── zzz{img_suffix} - │ │ │ ├── val - │ │ ├── ann_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{seg_map_suffix} - │ │ │ │ ├── yyy{seg_map_suffix} - │ │ │ │ ├── zzz{seg_map_suffix} - │ │ │ ├── val - - The img/gt_semantic_seg pair of CustomDataset should be of the same - except suffix. A valid img/gt_semantic_seg filename pair should be like - ``xxx{img_suffix}`` and ``xxx{seg_map_suffix}`` (extension is also included - in the suffix). If split is given, then ``xxx`` is specified in txt file. - Otherwise, all files in ``img_dir/``and ``ann_dir`` will be loaded. - Please refer to ``docs/en/tutorials/new_dataset.md`` for more details. - - - Args: - pipeline (list[dict]): Processing pipeline - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. Default: '.jpg' - ann_dir (str, optional): Path to annotation directory. Default: None - seg_map_suffix (str): Suffix of segmentation maps. Default: '.png' - split (str, optional): Split txt file. If split is specified, only - file with suffix in the splits will be loaded. Otherwise, all - images in img_dir/ann_dir will be loaded. Default: None - data_root (str, optional): Data root for img_dir/ann_dir. Default: - None. - test_mode (bool): If test_mode=True, gt wouldn't be loaded. - ignore_index (int): The label index to be ignored. Default: 255 - reduce_zero_label (bool): Whether to mark label zero as ignored. - Default: False - classes (str | Sequence[str], optional): Specify classes to load. - If is None, ``cls.CLASSES`` will be used. Default: None. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, and - self.PALETTE is None, random palette will be generated. - Default: None - gt_seg_map_loader_cfg (dict, optional): build LoadAnnotations to - load gt for evaluation, load from disk by default. Default: None. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - CLASSES = None - - PALETTE = None - - def __init__(self, - pipeline, - img_dir, - img_suffix='.jpg', - ann_dir=None, - seg_map_suffix='.png', - split=None, - data_root=None, - test_mode=False, - ignore_index=255, - reduce_zero_label=False, - classes=None, - palette=None, - gt_seg_map_loader_cfg=None, - file_client_args=dict(backend='disk')): - self.pipeline = Compose(pipeline) - self.img_dir = img_dir - self.img_suffix = img_suffix - self.ann_dir = ann_dir - self.seg_map_suffix = seg_map_suffix - self.split = split - self.data_root = data_root - self.test_mode = test_mode - self.ignore_index = ignore_index - self.reduce_zero_label = reduce_zero_label - self.label_map = None - self.CLASSES, self.PALETTE = self.get_classes_and_palette( - classes, palette) - self.gt_seg_map_loader = LoadAnnotations( - ) if gt_seg_map_loader_cfg is None else LoadAnnotations( - **gt_seg_map_loader_cfg) - - self.file_client_args = file_client_args - self.file_client = mmcv.FileClient.infer_client(self.file_client_args) - - if test_mode: - assert self.CLASSES is not None, \ - '`cls.CLASSES` or `classes` should be specified when testing' - - # join paths if data_root is specified - if self.data_root is not None: - if not osp.isabs(self.img_dir): - self.img_dir = osp.join(self.data_root, self.img_dir) - if not (self.ann_dir is None or osp.isabs(self.ann_dir)): - self.ann_dir = osp.join(self.data_root, self.ann_dir) - if not (self.split is None or osp.isabs(self.split)): - self.split = osp.join(self.data_root, self.split) - - # load annotations - self.img_infos = self.load_annotations(self.img_dir, self.img_suffix, - self.ann_dir, - self.seg_map_suffix, self.split) - - def __len__(self): - """Total number of samples of data.""" - return len(self.img_infos) - - def load_annotations(self, img_dir, img_suffix, ann_dir, seg_map_suffix, - split): - """Load annotation from directory. - - Args: - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. - ann_dir (str|None): Path to annotation directory. - seg_map_suffix (str|None): Suffix of segmentation maps. - split (str|None): Split txt file. If split is specified, only file - with suffix in the splits will be loaded. Otherwise, all images - in img_dir/ann_dir will be loaded. Default: None - - Returns: - list[dict]: All image info of dataset. - """ - - img_infos = [] - if split is not None: - lines = mmcv.list_from_file( - split, file_client_args=self.file_client_args) - for line in lines: - img_name = line.strip() - img_info = dict(filename=img_name + img_suffix) - if ann_dir is not None: - seg_map = img_name + seg_map_suffix - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - else: - for img in self.file_client.list_dir_or_file( - dir_path=img_dir, - list_dir=False, - suffix=img_suffix, - recursive=True): - img_info = dict(filename=img) - if ann_dir is not None: - seg_map = img.replace(img_suffix, seg_map_suffix) - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - img_infos = sorted(img_infos, key=lambda x: x['filename']) - - print_log(f'Loaded {len(img_infos)} images', logger=get_root_logger()) - return img_infos - - def get_ann_info(self, idx): - """Get annotation by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - return self.img_infos[idx]['ann'] - - def pre_pipeline(self, results): - """Prepare results dict for pipeline.""" - results['seg_fields'] = [] - results['img_prefix'] = self.img_dir - results['seg_prefix'] = self.ann_dir - if self.custom_classes: - results['label_map'] = self.label_map - - def __getitem__(self, idx): - """Get training/test data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training/test data (with annotation if `test_mode` is set - False). - """ - - if self.test_mode: - return self.prepare_test_img(idx) - else: - return self.prepare_train_img(idx) - - def prepare_train_img(self, idx): - """Get training data and annotations after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training data and annotation after pipeline with new keys - introduced by pipeline. - """ - - img_info = self.img_infos[idx] - ann_info = self.get_ann_info(idx) - results = dict(img_info=img_info, ann_info=ann_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def prepare_test_img(self, idx): - """Get testing data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Testing data after pipeline with new keys introduced by - pipeline. - """ - - img_info = self.img_infos[idx] - results = dict(img_info=img_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """Place holder to format result to dataset specific output.""" - raise NotImplementedError - - def get_gt_seg_map_by_idx(self, index): - """Get one ground truth segmentation map for evaluation.""" - ann_info = self.get_ann_info(index) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - return results['gt_semantic_seg'] - - def get_gt_seg_maps(self, efficient_test=None): - """Get ground truth segmentation maps for evaluation.""" - if efficient_test is not None: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` has been deprecated ' - 'since MMSeg v0.16, the ``get_gt_seg_maps()`` is CPU memory ' - 'friendly by default. ') - - for idx in range(len(self)): - ann_info = self.get_ann_info(idx) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - yield results['gt_semantic_seg'] - - def pre_eval(self, preds, indices): - """Collect eval result from each iteration. - - Args: - preds (list[torch.Tensor] | torch.Tensor): the segmentation logit - after argmax, shape (N, H, W). - indices (list[int] | int): the prediction related ground truth - indices. - - Returns: - list[torch.Tensor]: (area_intersect, area_union, area_prediction, - area_ground_truth). - """ - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - - pre_eval_results = [] - - for pred, index in zip(preds, indices): - seg_map = self.get_gt_seg_map_by_idx(index) - pre_eval_results.append( - intersect_and_union( - pred, - seg_map, - len(self.CLASSES), - self.ignore_index, - # as the labels has been converted when dataset initialized - # in `get_palette_for_custom_classes ` this `label_map` - # should be `dict()`, see - # https://github.com/open-mmlab/mmsegmentation/issues/1415 - # for more ditails - label_map=dict(), - reduce_zero_label=self.reduce_zero_label)) - - return pre_eval_results - - def get_classes_and_palette(self, classes=None, palette=None): - """Get class names of current dataset. - - Args: - classes (Sequence[str] | str | None): If classes is None, use - default CLASSES defined by builtin dataset. If classes is a - string, take it as a file name. The file contains the name of - classes where each line contains one class name. If classes is - a tuple or list, override the CLASSES defined by the dataset. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, random - palette will be generated. Default: None - """ - if classes is None: - self.custom_classes = False - return self.CLASSES, self.PALETTE - - self.custom_classes = True - if isinstance(classes, str): - # take it as a file path - class_names = mmcv.list_from_file(classes) - elif isinstance(classes, (tuple, list)): - class_names = classes - else: - raise ValueError(f'Unsupported type {type(classes)} of classes.') - - if self.CLASSES: - if not set(class_names).issubset(self.CLASSES): - raise ValueError('classes is not a subset of CLASSES.') - - # dictionary, its keys are the old label ids and its values - # are the new label ids. - # used for changing pixel labels in load_annotations. - self.label_map = {} - for i, c in enumerate(self.CLASSES): - if c not in class_names: - self.label_map[i] = -1 - else: - self.label_map[i] = class_names.index(c) - - palette = self.get_palette_for_custom_classes(class_names, palette) - - return class_names, palette - - def get_palette_for_custom_classes(self, class_names, palette=None): - - if self.label_map is not None: - # return subset of palette - palette = [] - for old_id, new_id in sorted( - self.label_map.items(), key=lambda x: x[1]): - if new_id != -1: - palette.append(self.PALETTE[old_id]) - palette = type(self.PALETTE)(palette) - - elif palette is None: - if self.PALETTE is None: - # Get random state before set seed, and restore - # random state later. - # It will prevent loss of randomness, as the palette - # may be different in each iteration if not specified. - # See: https://github.com/open-mmlab/mmdetection/issues/5844 - state = np.random.get_state() - np.random.seed(42) - # random palette - palette = np.random.randint(0, 255, size=(len(class_names), 3)) - np.random.set_state(state) - else: - palette = self.PALETTE - - return palette - - def evaluate(self, - results, - metric='mIoU', - logger=None, - gt_seg_maps=None, - **kwargs): - """Evaluate the dataset. - - Args: - results (list[tuple[torch.Tensor]] | list[str]): per image pre_eval - results or predict segmentation map for computing evaluation - metric. - metric (str | list[str]): Metrics to be evaluated. 'mIoU', - 'mDice' and 'mFscore' are supported. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - gt_seg_maps (generator[ndarray]): Custom gt seg maps as input, - used in ConcatDataset - - Returns: - dict[str, float]: Default metrics. - """ - if isinstance(metric, str): - metric = [metric] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metric).issubset(set(allowed_metrics)): - raise KeyError('metric {} is not supported'.format(metric)) - - eval_results = {} - # test a list of files - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - if gt_seg_maps is None: - gt_seg_maps = self.get_gt_seg_maps() - num_classes = len(self.CLASSES) - ret_metrics = eval_metrics( - results, - gt_seg_maps, - num_classes, - self.ignore_index, - metric, - label_map=dict(), - reduce_zero_label=self.reduce_zero_label) - # test a list of pre_eval_results - else: - ret_metrics = pre_eval_to_metrics(results, metric) - - # Because dataset.CLASSES is required for per-eval. - if self.CLASSES is None: - class_names = tuple(range(num_classes)) - else: - class_names = self.CLASSES - - # summary table - ret_metrics_summary = OrderedDict({ - ret_metric: np.round(np.nanmean(ret_metric_value) * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - - # each class table - ret_metrics.pop('aAcc', None) - ret_metrics_class = OrderedDict({ - ret_metric: np.round(ret_metric_value * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - ret_metrics_class.update({'Class': class_names}) - ret_metrics_class.move_to_end('Class', last=False) - - # for logger - class_table_data = PrettyTable() - for key, val in ret_metrics_class.items(): - class_table_data.add_column(key, val) - - summary_table_data = PrettyTable() - for key, val in ret_metrics_summary.items(): - if key == 'aAcc': - summary_table_data.add_column(key, [val]) - else: - summary_table_data.add_column('m' + key, [val]) - - print_log('per class results:', logger) - print_log('\n' + class_table_data.get_string(), logger=logger) - print_log('Summary:', logger) - print_log('\n' + summary_table_data.get_string(), logger=logger) - - # each metric dict - for key, value in ret_metrics_summary.items(): - if key == 'aAcc': - eval_results[key] = value / 100.0 - else: - eval_results['m' + key] = value / 100.0 - - ret_metrics_class.pop('Class', None) - for key, value in ret_metrics_class.items(): - eval_results.update({ - key + '.' + str(name): value[idx] / 100.0 - for idx, name in enumerate(class_names) - }) - - return eval_results diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/dataset_wrappers.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/dataset_wrappers.py deleted file mode 100644 index 1fb089f9f..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/dataset_wrappers.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import bisect -import collections -import copy -from itertools import chain - -import mmcv -import numpy as np -from mmcv.utils import build_from_cfg, print_log -from torch.utils.data.dataset import ConcatDataset as _ConcatDataset - -from .builder import DATASETS, PIPELINES -from .cityscapes import CityscapesDataset - - -@DATASETS.register_module() -class ConcatDataset(_ConcatDataset): - """A wrapper of concatenated dataset. - - Same as :obj:`torch.utils.data.dataset.ConcatDataset`, but - support evaluation and formatting results - - Args: - datasets (list[:obj:`Dataset`]): A list of datasets. - separate_eval (bool): Whether to evaluate the concatenated - dataset results separately, Defaults to True. - """ - - def __init__(self, datasets, separate_eval=True): - super(ConcatDataset, self).__init__(datasets) - self.CLASSES = datasets[0].CLASSES - self.PALETTE = datasets[0].PALETTE - self.separate_eval = separate_eval - assert separate_eval in [True, False], \ - f'separate_eval can only be True or False,' \ - f'but get {separate_eval}' - if any([isinstance(ds, CityscapesDataset) for ds in datasets]): - raise NotImplementedError( - 'Evaluating ConcatDataset containing CityscapesDataset' - 'is not supported!') - - def evaluate(self, results, logger=None, **kwargs): - """Evaluate the results. - - Args: - results (list[tuple[torch.Tensor]] | list[str]]): per image - pre_eval results or predict segmentation map for - computing evaluation metric. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - - Returns: - dict[str: float]: evaluate results of the total dataset - or each separate - dataset if `self.separate_eval=True`. - """ - assert len(results) == self.cumulative_sizes[-1], \ - ('Dataset and results have different sizes: ' - f'{self.cumulative_sizes[-1]} v.s. {len(results)}') - - # Check whether all the datasets support evaluation - for dataset in self.datasets: - assert hasattr(dataset, 'evaluate'), \ - f'{type(dataset)} does not implement evaluate function' - - if self.separate_eval: - dataset_idx = -1 - total_eval_results = dict() - for size, dataset in zip(self.cumulative_sizes, self.datasets): - start_idx = 0 if dataset_idx == -1 else \ - self.cumulative_sizes[dataset_idx] - end_idx = self.cumulative_sizes[dataset_idx + 1] - - results_per_dataset = results[start_idx:end_idx] - print_log( - f'\nEvaluateing {dataset.img_dir} with ' - f'{len(results_per_dataset)} images now', - logger=logger) - - eval_results_per_dataset = dataset.evaluate( - results_per_dataset, logger=logger, **kwargs) - dataset_idx += 1 - for k, v in eval_results_per_dataset.items(): - total_eval_results.update({f'{dataset_idx}_{k}': v}) - - return total_eval_results - - if len(set([type(ds) for ds in self.datasets])) != 1: - raise NotImplementedError( - 'All the datasets should have same types when ' - 'self.separate_eval=False') - else: - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - # merge the generators of gt_seg_maps - gt_seg_maps = chain( - *[dataset.get_gt_seg_maps() for dataset in self.datasets]) - else: - # if the results are `pre_eval` results, - # we do not need gt_seg_maps to evaluate - gt_seg_maps = None - eval_results = self.datasets[0].evaluate( - results, gt_seg_maps=gt_seg_maps, logger=logger, **kwargs) - return eval_results - - def get_dataset_idx_and_sample_idx(self, indice): - """Return dataset and sample index when given an indice of - ConcatDataset. - - Args: - indice (int): indice of sample in ConcatDataset - - Returns: - int: the index of sub dataset the sample belong to - int: the index of sample in its corresponding subset - """ - if indice < 0: - if -indice > len(self): - raise ValueError( - 'absolute value of index should not exceed dataset length') - indice = len(self) + indice - dataset_idx = bisect.bisect_right(self.cumulative_sizes, indice) - if dataset_idx == 0: - sample_idx = indice - else: - sample_idx = indice - self.cumulative_sizes[dataset_idx - 1] - return dataset_idx, sample_idx - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """format result for every sample of ConcatDataset.""" - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].format_results( - [results[i]], - imgfile_prefix + f'/{dataset_idx}', - indices=[sample_idx], - **kwargs) - ret_res.append(res) - return sum(ret_res, []) - - def pre_eval(self, preds, indices): - """do pre eval for every sample of ConcatDataset.""" - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].pre_eval(preds[i], sample_idx) - ret_res.append(res) - return sum(ret_res, []) - - -@DATASETS.register_module() -class RepeatDataset(object): - """A wrapper of repeated dataset. - - The length of repeated dataset will be `times` larger than the original - dataset. This is useful when the data loading time is long but the dataset - is small. Using RepeatDataset can reduce the data loading time between - epochs. - - Args: - dataset (:obj:`Dataset`): The dataset to be repeated. - times (int): Repeat times. - """ - - def __init__(self, dataset, times): - self.dataset = dataset - self.times = times - self.CLASSES = dataset.CLASSES - self.PALETTE = dataset.PALETTE - self._ori_len = len(self.dataset) - - def __getitem__(self, idx): - """Get item from original dataset.""" - return self.dataset[idx % self._ori_len] - - def __len__(self): - """The length is multiplied by ``times``""" - return self.times * self._ori_len - - -@DATASETS.register_module() -class MultiImageMixDataset: - """A wrapper of multiple images mixed dataset. - - Suitable for training on multiple images mixed data augmentation like - mosaic and mixup. For the augmentation pipeline of mixed image data, - the `get_indexes` method needs to be provided to obtain the image - indexes, and you can set `skip_flags` to change the pipeline running - process. - - - Args: - dataset (:obj:`CustomDataset`): The dataset to be mixed. - pipeline (Sequence[dict]): Sequence of transform object or - config dict to be composed. - skip_type_keys (list[str], optional): Sequence of type string to - be skip pipeline. Default to None. - """ - - def __init__(self, dataset, pipeline, skip_type_keys=None): - assert isinstance(pipeline, collections.abc.Sequence) - if skip_type_keys is not None: - assert all([ - isinstance(skip_type_key, str) - for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys - - self.pipeline = [] - self.pipeline_types = [] - for transform in pipeline: - if isinstance(transform, dict): - self.pipeline_types.append(transform['type']) - transform = build_from_cfg(transform, PIPELINES) - self.pipeline.append(transform) - else: - raise TypeError('pipeline must be a dict') - - self.dataset = dataset - self.CLASSES = dataset.CLASSES - self.PALETTE = dataset.PALETTE - self.num_samples = len(dataset) - - def __len__(self): - return self.num_samples - - def __getitem__(self, idx): - results = copy.deepcopy(self.dataset[idx]) - for (transform, transform_type) in zip(self.pipeline, - self.pipeline_types): - if self._skip_type_keys is not None and \ - transform_type in self._skip_type_keys: - continue - - if hasattr(transform, 'get_indexes'): - indexes = transform.get_indexes(self.dataset) - if not isinstance(indexes, collections.abc.Sequence): - indexes = [indexes] - mix_results = [ - copy.deepcopy(self.dataset[index]) for index in indexes - ] - results['mix_results'] = mix_results - - results = transform(results) - - if 'mix_results' in results: - results.pop('mix_results') - - return results - - def update_skip_type_keys(self, skip_type_keys): - """Update skip_type_keys. - - It is called by an external hook. - - Args: - skip_type_keys (list[str], optional): Sequence of type - string to be skip pipeline. - """ - assert all([ - isinstance(skip_type_key, str) for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pascal_context.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pascal_context.py deleted file mode 100644 index efacee0f3..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pascal_context.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class PascalContextDataset(CustomDataset): - """PascalContext dataset. - - In segmentation map annotation for PascalContext, 0 stands for background, - which is included in 60 categories. ``reduce_zero_label`` is fixed to - False. The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is - fixed to '.png'. - - Args: - split (str): Split txt file for PascalContext. - """ - - CLASSES = ('background', 'aeroplane', 'bag', 'bed', 'bedclothes', 'bench', - 'bicycle', 'bird', 'boat', 'book', 'bottle', 'building', 'bus', - 'cabinet', 'car', 'cat', 'ceiling', 'chair', 'cloth', - 'computer', 'cow', 'cup', 'curtain', 'dog', 'door', 'fence', - 'floor', 'flower', 'food', 'grass', 'ground', 'horse', - 'keyboard', 'light', 'motorbike', 'mountain', 'mouse', 'person', - 'plate', 'platform', 'pottedplant', 'road', 'rock', 'sheep', - 'shelves', 'sidewalk', 'sign', 'sky', 'snow', 'sofa', 'table', - 'track', 'train', 'tree', 'truck', 'tvmonitor', 'wall', 'water', - 'window', 'wood') - - PALETTE = [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255]] - - def __init__(self, split, **kwargs): - super(PascalContextDataset, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - split=split, - reduce_zero_label=False, - **kwargs) - assert self.file_client.exists(self.img_dir) and self.split is not None - - -@DATASETS.register_module() -class PascalContextDataset59(CustomDataset): - """PascalContext dataset. - - In segmentation map annotation for PascalContext, 0 stands for background, - which is included in 60 categories. ``reduce_zero_label`` is fixed to - False. The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is - fixed to '.png'. - - Args: - split (str): Split txt file for PascalContext. - """ - - CLASSES = ('aeroplane', 'bag', 'bed', 'bedclothes', 'bench', 'bicycle', - 'bird', 'boat', 'book', 'bottle', 'building', 'bus', 'cabinet', - 'car', 'cat', 'ceiling', 'chair', 'cloth', 'computer', 'cow', - 'cup', 'curtain', 'dog', 'door', 'fence', 'floor', 'flower', - 'food', 'grass', 'ground', 'horse', 'keyboard', 'light', - 'motorbike', 'mountain', 'mouse', 'person', 'plate', 'platform', - 'pottedplant', 'road', 'rock', 'sheep', 'shelves', 'sidewalk', - 'sign', 'sky', 'snow', 'sofa', 'table', 'track', 'train', - 'tree', 'truck', 'tvmonitor', 'wall', 'water', 'window', 'wood') - - PALETTE = [[180, 120, 120], [6, 230, 230], [80, 50, 50], [4, 200, 3], - [120, 120, 80], [140, 140, 140], [204, 5, 255], [230, 230, 230], - [4, 250, 7], [224, 5, 255], [235, 255, 7], [150, 5, 61], - [120, 120, 70], [8, 255, 51], [255, 6, 82], [143, 255, 140], - [204, 255, 4], [255, 51, 7], [204, 70, 3], [0, 102, 200], - [61, 230, 250], [255, 6, 51], [11, 102, 255], [255, 7, 71], - [255, 9, 224], [9, 7, 230], [220, 220, 220], [255, 9, 92], - [112, 9, 255], [8, 255, 214], [7, 255, 224], [255, 184, 6], - [10, 255, 71], [255, 41, 10], [7, 255, 255], [224, 255, 8], - [102, 8, 255], [255, 61, 6], [255, 194, 7], [255, 122, 8], - [0, 255, 20], [255, 8, 41], [255, 5, 153], [6, 51, 255], - [235, 12, 255], [160, 150, 20], [0, 163, 255], [140, 140, 140], - [250, 10, 15], [20, 255, 0], [31, 255, 0], [255, 31, 0], - [255, 224, 0], [153, 255, 0], [0, 0, 255], [255, 71, 0], - [0, 235, 255], [0, 173, 255], [31, 0, 255]] - - def __init__(self, split, **kwargs): - super(PascalContextDataset59, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - split=split, - reduce_zero_label=True, - **kwargs) - assert self.file_client.exists(self.img_dir) and self.split is not None diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/__init__.py deleted file mode 100644 index 8256a6fe2..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .compose import Compose -from .formatting import (Collect, ImageToTensor, ToDataContainer, ToTensor, - Transpose, to_tensor) -from .loading import LoadAnnotations, LoadImageFromFile -from .test_time_aug import MultiScaleFlipAug -from .transforms import (CLAHE, AdjustGamma, Normalize, Pad, - PhotoMetricDistortion, RandomCrop, RandomCutOut, - RandomFlip, RandomMosaic, RandomRotate, Rerange, - Resize, RGB2Gray, SegRescale) - -__all__ = [ - 'Compose', 'to_tensor', 'ToTensor', 'ImageToTensor', 'ToDataContainer', - 'Transpose', 'Collect', 'LoadAnnotations', 'LoadImageFromFile', - 'MultiScaleFlipAug', 'Resize', 'RandomFlip', 'Pad', 'RandomCrop', - 'Normalize', 'SegRescale', 'PhotoMetricDistortion', 'RandomRotate', - 'AdjustGamma', 'CLAHE', 'Rerange', 'RGB2Gray', 'RandomCutOut', - 'RandomMosaic' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/compose.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/compose.py deleted file mode 100644 index 30280c133..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/compose.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections - -from mmcv.utils import build_from_cfg - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class Compose(object): - """Compose multiple transforms sequentially. - - Args: - transforms (Sequence[dict | callable]): Sequence of transform object or - config dict to be composed. - """ - - def __init__(self, transforms): - assert isinstance(transforms, collections.abc.Sequence) - self.transforms = [] - for transform in transforms: - if isinstance(transform, dict): - transform = build_from_cfg(transform, PIPELINES) - self.transforms.append(transform) - elif callable(transform): - self.transforms.append(transform) - else: - raise TypeError('transform must be callable or a dict') - - def __call__(self, data): - """Call function to apply transforms sequentially. - - Args: - data (dict): A result dict contains the data to transform. - - Returns: - dict: Transformed data. - """ - - for t in self.transforms: - data = t(data) - if data is None: - return None - return data - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - format_string += '\n' - format_string += f' {t}' - format_string += '\n)' - return format_string diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/formating.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/formating.py deleted file mode 100644 index f6e53bfeb..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/formating.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -import warnings - -from .formatting import * - -warnings.warn('DeprecationWarning: mmseg.datasets.pipelines.formating will be ' - 'deprecated in 2021, please replace it with ' - 'mmseg.datasets.pipelines.formatting.') diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/formatting.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/formatting.py deleted file mode 100644 index 4e057c1b8..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/formatting.py +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import mmcv -import numpy as np -import torch -from mmcv.parallel import DataContainer as DC - -from ..builder import PIPELINES - - -def to_tensor(data): - """Convert objects of various python types to :obj:`torch.Tensor`. - - Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, - :class:`Sequence`, :class:`int` and :class:`float`. - - Args: - data (torch.Tensor | numpy.ndarray | Sequence | int | float): Data to - be converted. - """ - - if isinstance(data, torch.Tensor): - return data - elif isinstance(data, np.ndarray): - return torch.from_numpy(data) - elif isinstance(data, Sequence) and not mmcv.is_str(data): - return torch.tensor(data) - elif isinstance(data, int): - return torch.LongTensor([data]) - elif isinstance(data, float): - return torch.FloatTensor([data]) - else: - raise TypeError(f'type {type(data)} cannot be converted to tensor.') - - -@PIPELINES.register_module() -class ToTensor(object): - """Convert some results to :obj:`torch.Tensor` by given keys. - - Args: - keys (Sequence[str]): Keys that need to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert data in results to :obj:`torch.Tensor`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted - to :obj:`torch.Tensor`. - """ - - for key in self.keys: - results[key] = to_tensor(results[key]) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class ImageToTensor(object): - """Convert image to :obj:`torch.Tensor` by given keys. - - The dimension order of input image is (H, W, C). The pipeline will convert - it to (C, H, W). If only 2 dimension (H, W) is given, the output would be - (1, H, W). - - Args: - keys (Sequence[str]): Key of images to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - img = results[key] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - results[key] = to_tensor(img.transpose(2, 0, 1)) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class Transpose(object): - """Transpose some results by given keys. - - Args: - keys (Sequence[str]): Keys of results to be transposed. - order (Sequence[int]): Order of transpose. - """ - - def __init__(self, keys, order): - self.keys = keys - self.order = order - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - results[key] = results[key].transpose(self.order) - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, order={self.order})' - - -@PIPELINES.register_module() -class ToDataContainer(object): - """Convert results to :obj:`mmcv.DataContainer` by given fields. - - Args: - fields (Sequence[dict]): Each field is a dict like - ``dict(key='xxx', **kwargs)``. The ``key`` in result will - be converted to :obj:`mmcv.DataContainer` with ``**kwargs``. - Default: ``(dict(key='img', stack=True), - dict(key='gt_semantic_seg'))``. - """ - - def __init__(self, - fields=(dict(key='img', - stack=True), dict(key='gt_semantic_seg'))): - self.fields = fields - - def __call__(self, results): - """Call function to convert data in results to - :obj:`mmcv.DataContainer`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted to - :obj:`mmcv.DataContainer`. - """ - - for field in self.fields: - field = field.copy() - key = field.pop('key') - results[key] = DC(results[key], **field) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(fields={self.fields})' - - -@PIPELINES.register_module() -class DefaultFormatBundle(object): - """Default formatting bundle. - - It simplifies the pipeline of formatting common fields, including "img" - and "gt_semantic_seg". These fields are formatted as follows. - - - img: (1)transpose, (2)to tensor, (3)to DataContainer (stack=True) - - gt_semantic_seg: (1)unsqueeze dim-0 (2)to tensor, - (3)to DataContainer (stack=True) - """ - - def __call__(self, results): - """Call function to transform and format common fields in results. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data that is formatted with - default bundle. - """ - - if 'img' in results: - img = results['img'] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - img = np.ascontiguousarray(img.transpose(2, 0, 1)) - results['img'] = DC(to_tensor(img), stack=True) - if 'gt_semantic_seg' in results: - # convert to long - results['gt_semantic_seg'] = DC( - to_tensor(results['gt_semantic_seg'][None, - ...].astype(np.int64)), - stack=True) - return results - - def __repr__(self): - return self.__class__.__name__ - - -@PIPELINES.register_module() -class Collect(object): - """Collect data from the loader relevant to the specific task. - - This is usually the last stage of the data loader pipeline. Typically keys - is set to some subset of "img", "gt_semantic_seg". - - The "img_meta" item is always populated. The contents of the "img_meta" - dictionary depends on "meta_keys". By default this includes: - - - "img_shape": shape of the image input to the network as a tuple - (h, w, c). Note that images may be zero padded on the bottom/right - if the batch tensor is larger than this shape. - - - "scale_factor": a float indicating the preprocessing scale - - - "flip": a boolean indicating if image flip transform was used - - - "filename": path to the image file - - - "ori_shape": original shape of the image as a tuple (h, w, c) - - - "pad_shape": image shape after padding - - - "img_norm_cfg": a dict of normalization information: - - mean - per channel mean subtraction - - std - per channel std divisor - - to_rgb - bool indicating if bgr was converted to rgb - - Args: - keys (Sequence[str]): Keys of results to be collected in ``data``. - meta_keys (Sequence[str], optional): Meta keys to be converted to - ``mmcv.DataContainer`` and collected in ``data[img_metas]``. - Default: (``filename``, ``ori_filename``, ``ori_shape``, - ``img_shape``, ``pad_shape``, ``scale_factor``, ``flip``, - ``flip_direction``, ``img_norm_cfg``) - """ - - def __init__(self, - keys, - meta_keys=('filename', 'ori_filename', 'ori_shape', - 'img_shape', 'pad_shape', 'scale_factor', 'flip', - 'flip_direction', 'img_norm_cfg')): - self.keys = keys - self.meta_keys = meta_keys - - def __call__(self, results): - """Call function to collect keys in results. The keys in ``meta_keys`` - will be converted to :obj:mmcv.DataContainer. - - Args: - results (dict): Result dict contains the data to collect. - - Returns: - dict: The result dict contains the following keys - - keys in``self.keys`` - - ``img_metas`` - """ - - data = {} - img_meta = {} - for key in self.meta_keys: - img_meta[key] = results[key] - data['img_metas'] = DC(img_meta, cpu_only=True) - for key in self.keys: - data[key] = results[key] - return data - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, meta_keys={self.meta_keys})' diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/loading.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/loading.py deleted file mode 100644 index 572e43431..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/loading.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class LoadImageFromFile(object): - """Load an image from file. - - Required keys are "img_prefix" and "img_info" (a dict that must contain the - key "filename"). Added or updated keys are "filename", "img", "img_shape", - "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`), - "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1). - - Args: - to_float32 (bool): Whether to convert the loaded image to a float32 - numpy array. If set to False, the loaded image is an uint8 array. - Defaults to False. - color_type (str): The flag argument for :func:`mmcv.imfrombytes`. - Defaults to 'color'. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'cv2' - """ - - def __init__(self, - to_float32=False, - color_type='color', - file_client_args=dict(backend='disk'), - imdecode_backend='cv2'): - self.to_float32 = to_float32 - self.color_type = color_type - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call functions to load image and get image meta information. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded image and meta information. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('img_prefix') is not None: - filename = osp.join(results['img_prefix'], - results['img_info']['filename']) - else: - filename = results['img_info']['filename'] - img_bytes = self.file_client.get(filename) - img = mmcv.imfrombytes( - img_bytes, flag=self.color_type, backend=self.imdecode_backend) - if self.to_float32: - img = img.astype(np.float32) - - results['filename'] = filename - results['ori_filename'] = results['img_info']['filename'] - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - # Set initial values for default meta_keys - results['pad_shape'] = img.shape - results['scale_factor'] = 1.0 - num_channels = 1 if len(img.shape) < 3 else img.shape[2] - results['img_norm_cfg'] = dict( - mean=np.zeros(num_channels, dtype=np.float32), - std=np.ones(num_channels, dtype=np.float32), - to_rgb=False) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(to_float32={self.to_float32},' - repr_str += f"color_type='{self.color_type}'," - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str - - -@PIPELINES.register_module() -class LoadAnnotations(object): - """Load annotations for semantic segmentation. - - Args: - reduce_zero_label (bool): Whether reduce all label value by 1. - Usually used for datasets where 0 is background label. - Default: False. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'pillow' - """ - - def __init__(self, - reduce_zero_label=False, - file_client_args=dict(backend='disk'), - imdecode_backend='pillow'): - self.reduce_zero_label = reduce_zero_label - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call function to load multiple types annotations. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded semantic segmentation annotations. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('seg_prefix', None) is not None: - filename = osp.join(results['seg_prefix'], - results['ann_info']['seg_map']) - else: - filename = results['ann_info']['seg_map'] - img_bytes = self.file_client.get(filename) - gt_semantic_seg = mmcv.imfrombytes( - img_bytes, flag='unchanged', - backend=self.imdecode_backend).squeeze().astype(np.uint8) - # modify if custom classes - if results.get('label_map', None) is not None: - # Add deep copy to solve bug of repeatedly - # replace `gt_semantic_seg`, which is reported in - # https://github.com/open-mmlab/mmsegmentation/pull/1445/ - gt_semantic_seg_copy = gt_semantic_seg.copy() - for old_id, new_id in results['label_map'].items(): - gt_semantic_seg[gt_semantic_seg_copy == old_id] = new_id - # reduce zero_label - if self.reduce_zero_label: - # avoid using underflow conversion - gt_semantic_seg[gt_semantic_seg == 0] = 255 - gt_semantic_seg = gt_semantic_seg - 1 - gt_semantic_seg[gt_semantic_seg == 254] = 255 - results['gt_semantic_seg'] = gt_semantic_seg - results['seg_fields'].append('gt_semantic_seg') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(reduce_zero_label={self.reduce_zero_label},' - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/test_time_aug.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/test_time_aug.py deleted file mode 100644 index 5c17cbbba..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/test_time_aug.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv - -from ..builder import PIPELINES -from .compose import Compose - - -@PIPELINES.register_module() -class MultiScaleFlipAug(object): - """Test-time augmentation with multiple scales and flipping. - - An example configuration is as followed: - - .. code-block:: - - img_scale=(2048, 1024), - img_ratios=[0.5, 1.0], - flip=True, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ] - - After MultiScaleFLipAug with above configuration, the results are wrapped - into lists of the same length as followed: - - .. code-block:: - - dict( - img=[...], - img_shape=[...], - scale=[(1024, 512), (1024, 512), (2048, 1024), (2048, 1024)] - flip=[False, True, False, True] - ... - ) - - Args: - transforms (list[dict]): Transforms to apply in each augmentation. - img_scale (None | tuple | list[tuple]): Images scales for resizing. - img_ratios (float | list[float]): Image ratios for resizing - flip (bool): Whether apply flip augmentation. Default: False. - flip_direction (str | list[str]): Flip augmentation directions, - options are "horizontal" and "vertical". If flip_direction is list, - multiple flip augmentations will be applied. - It has no effect when flip == False. Default: "horizontal". - """ - - def __init__(self, - transforms, - img_scale, - img_ratios=None, - flip=False, - flip_direction='horizontal'): - self.transforms = Compose(transforms) - if img_ratios is not None: - img_ratios = img_ratios if isinstance(img_ratios, - list) else [img_ratios] - assert mmcv.is_list_of(img_ratios, float) - if img_scale is None: - # mode 1: given img_scale=None and a range of image ratio - self.img_scale = None - assert mmcv.is_list_of(img_ratios, float) - elif isinstance(img_scale, tuple) and mmcv.is_list_of( - img_ratios, float): - assert len(img_scale) == 2 - # mode 2: given a scale and a range of image ratio - self.img_scale = [(int(img_scale[0] * ratio), - int(img_scale[1] * ratio)) - for ratio in img_ratios] - else: - # mode 3: given multiple scales - self.img_scale = img_scale if isinstance(img_scale, - list) else [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) or self.img_scale is None - self.flip = flip - self.img_ratios = img_ratios - self.flip_direction = flip_direction if isinstance( - flip_direction, list) else [flip_direction] - assert mmcv.is_list_of(self.flip_direction, str) - if not self.flip and self.flip_direction != ['horizontal']: - warnings.warn( - 'flip_direction has no effect when flip is set to False') - if (self.flip - and not any([t['type'] == 'RandomFlip' for t in transforms])): - warnings.warn( - 'flip has no effect when RandomFlip is not in transforms') - - def __call__(self, results): - """Call function to apply test time augment transforms on results. - - Args: - results (dict): Result dict contains the data to transform. - - Returns: - dict[str: list]: The augmented data, where each value is wrapped - into a list. - """ - - aug_data = [] - if self.img_scale is None and mmcv.is_list_of(self.img_ratios, float): - h, w = results['img'].shape[:2] - img_scale = [(int(w * ratio), int(h * ratio)) - for ratio in self.img_ratios] - else: - img_scale = self.img_scale - flip_aug = [False, True] if self.flip else [False] - for scale in img_scale: - for flip in flip_aug: - for direction in self.flip_direction: - _results = results.copy() - _results['scale'] = scale - _results['flip'] = flip - _results['flip_direction'] = direction - data = self.transforms(_results) - aug_data.append(data) - # list of dict to dict of list - aug_data_dict = {key: [] for key in aug_data[0]} - for data in aug_data: - for key, val in data.items(): - aug_data_dict[key].append(val) - return aug_data_dict - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(transforms={self.transforms}, ' - repr_str += f'img_scale={self.img_scale}, flip={self.flip})' - repr_str += f'flip_direction={self.flip_direction}' - return repr_str diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/transforms.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/transforms.py deleted file mode 100644 index 5673b646f..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/pipelines/transforms.py +++ /dev/null @@ -1,1335 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -import mmcv -import numpy as np -from mmcv.utils import deprecated_api_warning, is_tuple_of -from numpy import random - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class ResizeToMultiple(object): - """Resize images & seg to multiple of divisor. - - Args: - size_divisor (int): images and gt seg maps need to resize to multiple - of size_divisor. Default: 32. - interpolation (str, optional): The interpolation mode of image resize. - Default: None - """ - - def __init__(self, size_divisor=32, interpolation=None): - self.size_divisor = size_divisor - self.interpolation = interpolation - - def __call__(self, results): - """Call function to resize images, semantic segmentation map to - multiple of size divisor. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape' keys are updated. - """ - # Align image to multiple of size divisor. - img = results['img'] - img = mmcv.imresize_to_multiple( - img, - self.size_divisor, - scale_factor=1, - interpolation=self.interpolation - if self.interpolation else 'bilinear') - - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape - - # Align segmentation map to multiple of size divisor. - for key in results.get('seg_fields', []): - gt_seg = results[key] - gt_seg = mmcv.imresize_to_multiple( - gt_seg, - self.size_divisor, - scale_factor=1, - interpolation='nearest') - results[key] = gt_seg - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(size_divisor={self.size_divisor}, ' - f'interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class Resize(object): - """Resize images & seg. - - This transform resizes the input image to some scale. If the input dict - contains the key "scale", then the scale in the input dict is used, - otherwise the specified scale in the init method is used. - - ``img_scale`` can be None, a tuple (single-scale) or a list of tuple - (multi-scale). There are 4 multiscale modes: - - - ``ratio_range is not None``: - 1. When img_scale is None, img_scale is the shape of image in results - (img_scale = results['img'].shape[:2]) and the image is resized based - on the original size. (mode 1) - 2. When img_scale is a tuple (single-scale), randomly sample a ratio from - the ratio range and multiply it with the image scale. (mode 2) - - - ``ratio_range is None and multiscale_mode == "range"``: randomly sample a - scale from the a range. (mode 3) - - - ``ratio_range is None and multiscale_mode == "value"``: randomly sample a - scale from multiple scales. (mode 4) - - Args: - img_scale (tuple or list[tuple]): Images scales for resizing. - Default:None. - multiscale_mode (str): Either "range" or "value". - Default: 'range' - ratio_range (tuple[float]): (min_ratio, max_ratio). - Default: None - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: True - min_size (int, optional): The minimum size for input and the shape - of the image and seg map will not be less than ``min_size``. - As the shape of model input is fixed like 'SETR' and 'BEiT'. - Following the setting in these models, resized images must be - bigger than the crop size in ``slide_inference``. Default: None - """ - - def __init__(self, - img_scale=None, - multiscale_mode='range', - ratio_range=None, - keep_ratio=True, - min_size=None): - if img_scale is None: - self.img_scale = None - else: - if isinstance(img_scale, list): - self.img_scale = img_scale - else: - self.img_scale = [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) - - if ratio_range is not None: - # mode 1: given img_scale=None and a range of image ratio - # mode 2: given a scale and a range of image ratio - assert self.img_scale is None or len(self.img_scale) == 1 - else: - # mode 3 and 4: given multiple scales or a range of scales - assert multiscale_mode in ['value', 'range'] - - self.multiscale_mode = multiscale_mode - self.ratio_range = ratio_range - self.keep_ratio = keep_ratio - self.min_size = min_size - - @staticmethod - def random_select(img_scales): - """Randomly select an img_scale from given candidates. - - Args: - img_scales (list[tuple]): Images scales for selection. - - Returns: - (tuple, int): Returns a tuple ``(img_scale, scale_dix)``, - where ``img_scale`` is the selected image scale and - ``scale_idx`` is the selected index in the given candidates. - """ - - assert mmcv.is_list_of(img_scales, tuple) - scale_idx = np.random.randint(len(img_scales)) - img_scale = img_scales[scale_idx] - return img_scale, scale_idx - - @staticmethod - def random_sample(img_scales): - """Randomly sample an img_scale when ``multiscale_mode=='range'``. - - Args: - img_scales (list[tuple]): Images scale range for sampling. - There must be two tuples in img_scales, which specify the lower - and upper bound of image scales. - - Returns: - (tuple, None): Returns a tuple ``(img_scale, None)``, where - ``img_scale`` is sampled scale and None is just a placeholder - to be consistent with :func:`random_select`. - """ - - assert mmcv.is_list_of(img_scales, tuple) and len(img_scales) == 2 - img_scale_long = [max(s) for s in img_scales] - img_scale_short = [min(s) for s in img_scales] - long_edge = np.random.randint( - min(img_scale_long), - max(img_scale_long) + 1) - short_edge = np.random.randint( - min(img_scale_short), - max(img_scale_short) + 1) - img_scale = (long_edge, short_edge) - return img_scale, None - - @staticmethod - def random_sample_ratio(img_scale, ratio_range): - """Randomly sample an img_scale when ``ratio_range`` is specified. - - A ratio will be randomly sampled from the range specified by - ``ratio_range``. Then it would be multiplied with ``img_scale`` to - generate sampled scale. - - Args: - img_scale (tuple): Images scale base to multiply with ratio. - ratio_range (tuple[float]): The minimum and maximum ratio to scale - the ``img_scale``. - - Returns: - (tuple, None): Returns a tuple ``(scale, None)``, where - ``scale`` is sampled ratio multiplied with ``img_scale`` and - None is just a placeholder to be consistent with - :func:`random_select`. - """ - - assert isinstance(img_scale, tuple) and len(img_scale) == 2 - min_ratio, max_ratio = ratio_range - assert min_ratio <= max_ratio - ratio = np.random.random_sample() * (max_ratio - min_ratio) + min_ratio - scale = int(img_scale[0] * ratio), int(img_scale[1] * ratio) - return scale, None - - def _random_scale(self, results): - """Randomly sample an img_scale according to ``ratio_range`` and - ``multiscale_mode``. - - If ``ratio_range`` is specified, a ratio will be sampled and be - multiplied with ``img_scale``. - If multiple scales are specified by ``img_scale``, a scale will be - sampled according to ``multiscale_mode``. - Otherwise, single scale will be used. - - Args: - results (dict): Result dict from :obj:`dataset`. - - Returns: - dict: Two new keys 'scale` and 'scale_idx` are added into - ``results``, which would be used by subsequent pipelines. - """ - - if self.ratio_range is not None: - if self.img_scale is None: - h, w = results['img'].shape[:2] - scale, scale_idx = self.random_sample_ratio((w, h), - self.ratio_range) - else: - scale, scale_idx = self.random_sample_ratio( - self.img_scale[0], self.ratio_range) - elif len(self.img_scale) == 1: - scale, scale_idx = self.img_scale[0], 0 - elif self.multiscale_mode == 'range': - scale, scale_idx = self.random_sample(self.img_scale) - elif self.multiscale_mode == 'value': - scale, scale_idx = self.random_select(self.img_scale) - else: - raise NotImplementedError - - results['scale'] = scale - results['scale_idx'] = scale_idx - - def _resize_img(self, results): - """Resize images with ``results['scale']``.""" - if self.keep_ratio: - if self.min_size is not None: - # TODO: Now 'min_size' is an 'int' which means the minimum - # shape of images is (min_size, min_size, 3). 'min_size' - # with tuple type will be supported, i.e. the width and - # height are not equal. - if min(results['scale']) < self.min_size: - new_short = self.min_size - else: - new_short = min(results['scale']) - - h, w = results['img'].shape[:2] - if h > w: - new_h, new_w = new_short * h / w, new_short - else: - new_h, new_w = new_short, new_short * w / h - results['scale'] = (new_h, new_w) - - img, scale_factor = mmcv.imrescale( - results['img'], results['scale'], return_scale=True) - # the w_scale and h_scale has minor difference - # a real fix should be done in the mmcv.imrescale in the future - new_h, new_w = img.shape[:2] - h, w = results['img'].shape[:2] - w_scale = new_w / w - h_scale = new_h / h - else: - img, w_scale, h_scale = mmcv.imresize( - results['img'], results['scale'], return_scale=True) - scale_factor = np.array([w_scale, h_scale, w_scale, h_scale], - dtype=np.float32) - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape # in case that there is no padding - results['scale_factor'] = scale_factor - results['keep_ratio'] = self.keep_ratio - - def _resize_seg(self, results): - """Resize semantic segmentation map with ``results['scale']``.""" - for key in results.get('seg_fields', []): - if self.keep_ratio: - gt_seg = mmcv.imrescale( - results[key], results['scale'], interpolation='nearest') - else: - gt_seg = mmcv.imresize( - results[key], results['scale'], interpolation='nearest') - results[key] = gt_seg - - def __call__(self, results): - """Call function to resize images, bounding boxes, masks, semantic - segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape', 'scale_factor', - 'keep_ratio' keys are added into result dict. - """ - - if 'scale' not in results: - self._random_scale(results) - self._resize_img(results) - self._resize_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(img_scale={self.img_scale}, ' - f'multiscale_mode={self.multiscale_mode}, ' - f'ratio_range={self.ratio_range}, ' - f'keep_ratio={self.keep_ratio})') - return repr_str - - -@PIPELINES.register_module() -class RandomFlip(object): - """Flip the image & seg. - - If the input dict contains the key "flip", then the flag will be used, - otherwise it will be randomly decided by a ratio specified in the init - method. - - Args: - prob (float, optional): The flipping probability. Default: None. - direction(str, optional): The flipping direction. Options are - 'horizontal' and 'vertical'. Default: 'horizontal'. - """ - - @deprecated_api_warning({'flip_ratio': 'prob'}, cls_name='RandomFlip') - def __init__(self, prob=None, direction='horizontal'): - self.prob = prob - self.direction = direction - if prob is not None: - assert prob >= 0 and prob <= 1 - assert direction in ['horizontal', 'vertical'] - - def __call__(self, results): - """Call function to flip bounding boxes, masks, semantic segmentation - maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Flipped results, 'flip', 'flip_direction' keys are added into - result dict. - """ - - if 'flip' not in results: - flip = True if np.random.rand() < self.prob else False - results['flip'] = flip - if 'flip_direction' not in results: - results['flip_direction'] = self.direction - if results['flip']: - # flip image - results['img'] = mmcv.imflip( - results['img'], direction=results['flip_direction']) - - # flip segs - for key in results.get('seg_fields', []): - # use copy() to make numpy stride positive - results[key] = mmcv.imflip( - results[key], direction=results['flip_direction']).copy() - return results - - def __repr__(self): - return self.__class__.__name__ + f'(prob={self.prob})' - - -@PIPELINES.register_module() -class Pad(object): - """Pad the image & mask. - - There are two padding modes: (1) pad to a fixed size and (2) pad to the - minimum size that is divisible by some number. - Added keys are "pad_shape", "pad_fixed_size", "pad_size_divisor", - - Args: - size (tuple, optional): Fixed padding size. - size_divisor (int, optional): The divisor of padded size. - pad_val (float, optional): Padding value. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - """ - - def __init__(self, - size=None, - size_divisor=None, - pad_val=0, - seg_pad_val=255): - self.size = size - self.size_divisor = size_divisor - self.pad_val = pad_val - self.seg_pad_val = seg_pad_val - # only one of size and size_divisor should be valid - assert size is not None or size_divisor is not None - assert size is None or size_divisor is None - - def _pad_img(self, results): - """Pad images according to ``self.size``.""" - if self.size is not None: - padded_img = mmcv.impad( - results['img'], shape=self.size, pad_val=self.pad_val) - elif self.size_divisor is not None: - padded_img = mmcv.impad_to_multiple( - results['img'], self.size_divisor, pad_val=self.pad_val) - results['img'] = padded_img - results['pad_shape'] = padded_img.shape - results['pad_fixed_size'] = self.size - results['pad_size_divisor'] = self.size_divisor - - def _pad_seg(self, results): - """Pad masks according to ``results['pad_shape']``.""" - for key in results.get('seg_fields', []): - results[key] = mmcv.impad( - results[key], - shape=results['pad_shape'][:2], - pad_val=self.seg_pad_val) - - def __call__(self, results): - """Call function to pad images, masks, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Updated result dict. - """ - - self._pad_img(results) - self._pad_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(size={self.size}, size_divisor={self.size_divisor}, ' \ - f'pad_val={self.pad_val})' - return repr_str - - -@PIPELINES.register_module() -class Normalize(object): - """Normalize the image. - - Added key is "img_norm_cfg". - - Args: - mean (sequence): Mean values of 3 channels. - std (sequence): Std values of 3 channels. - to_rgb (bool): Whether to convert the image from BGR to RGB, - default is true. - """ - - def __init__(self, mean, std, to_rgb=True): - self.mean = np.array(mean, dtype=np.float32) - self.std = np.array(std, dtype=np.float32) - self.to_rgb = to_rgb - - def __call__(self, results): - """Call function to normalize images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Normalized results, 'img_norm_cfg' key is added into - result dict. - """ - - results['img'] = mmcv.imnormalize(results['img'], self.mean, self.std, - self.to_rgb) - results['img_norm_cfg'] = dict( - mean=self.mean, std=self.std, to_rgb=self.to_rgb) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(mean={self.mean}, std={self.std}, to_rgb=' \ - f'{self.to_rgb})' - return repr_str - - -@PIPELINES.register_module() -class Rerange(object): - """Rerange the image pixel value. - - Args: - min_value (float or int): Minimum value of the reranged image. - Default: 0. - max_value (float or int): Maximum value of the reranged image. - Default: 255. - """ - - def __init__(self, min_value=0, max_value=255): - assert isinstance(min_value, float) or isinstance(min_value, int) - assert isinstance(max_value, float) or isinstance(max_value, int) - assert min_value < max_value - self.min_value = min_value - self.max_value = max_value - - def __call__(self, results): - """Call function to rerange images. - - Args: - results (dict): Result dict from loading pipeline. - Returns: - dict: Reranged results. - """ - - img = results['img'] - img_min_value = np.min(img) - img_max_value = np.max(img) - - assert img_min_value < img_max_value - # rerange to [0, 1] - img = (img - img_min_value) / (img_max_value - img_min_value) - # rerange to [min_value, max_value] - img = img * (self.max_value - self.min_value) + self.min_value - results['img'] = img - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(min_value={self.min_value}, max_value={self.max_value})' - return repr_str - - -@PIPELINES.register_module() -class CLAHE(object): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - """ - - def __init__(self, clip_limit=40.0, tile_grid_size=(8, 8)): - assert isinstance(clip_limit, (float, int)) - self.clip_limit = clip_limit - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - self.tile_grid_size = tile_grid_size - - def __call__(self, results): - """Call function to Use CLAHE method process images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - for i in range(results['img'].shape[2]): - results['img'][:, :, i] = mmcv.clahe( - np.array(results['img'][:, :, i], dtype=np.uint8), - self.clip_limit, self.tile_grid_size) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(clip_limit={self.clip_limit}, '\ - f'tile_grid_size={self.tile_grid_size})' - return repr_str - - -@PIPELINES.register_module() -class RandomCrop(object): - """Random crop the image & seg. - - Args: - crop_size (tuple): Expected size after cropping, (h, w). - cat_max_ratio (float): The maximum ratio that single category could - occupy. - """ - - def __init__(self, crop_size, cat_max_ratio=1., ignore_index=255): - assert crop_size[0] > 0 and crop_size[1] > 0 - self.crop_size = crop_size - self.cat_max_ratio = cat_max_ratio - self.ignore_index = ignore_index - - def get_crop_bbox(self, img): - """Randomly get a crop bounding box.""" - margin_h = max(img.shape[0] - self.crop_size[0], 0) - margin_w = max(img.shape[1] - self.crop_size[1], 0) - offset_h = np.random.randint(0, margin_h + 1) - offset_w = np.random.randint(0, margin_w + 1) - crop_y1, crop_y2 = offset_h, offset_h + self.crop_size[0] - crop_x1, crop_x2 = offset_w, offset_w + self.crop_size[1] - - return crop_y1, crop_y2, crop_x1, crop_x2 - - def crop(self, img, crop_bbox): - """Crop from ``img``""" - crop_y1, crop_y2, crop_x1, crop_x2 = crop_bbox - img = img[crop_y1:crop_y2, crop_x1:crop_x2, ...] - return img - - def __call__(self, results): - """Call function to randomly crop images, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Randomly cropped results, 'img_shape' key in result dict is - updated according to crop size. - """ - - img = results['img'] - crop_bbox = self.get_crop_bbox(img) - if self.cat_max_ratio < 1.: - # Repeat 10 times - for _ in range(10): - seg_temp = self.crop(results['gt_semantic_seg'], crop_bbox) - labels, cnt = np.unique(seg_temp, return_counts=True) - cnt = cnt[labels != self.ignore_index] - if len(cnt) > 1 and np.max(cnt) / np.sum( - cnt) < self.cat_max_ratio: - break - crop_bbox = self.get_crop_bbox(img) - - # crop the image - img = self.crop(img, crop_bbox) - img_shape = img.shape - results['img'] = img - results['img_shape'] = img_shape - - # crop semantic seg - for key in results.get('seg_fields', []): - results[key] = self.crop(results[key], crop_bbox) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(crop_size={self.crop_size})' - - -@PIPELINES.register_module() -class RandomRotate(object): - """Rotate the image & seg. - - Args: - prob (float): The rotation probability. - degree (float, tuple[float]): Range of degrees to select from. If - degree is a number instead of tuple like (min, max), - the range of degree will be (``-degree``, ``+degree``) - pad_val (float, optional): Padding value of image. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. Default: None. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. Default: False - """ - - def __init__(self, - prob, - degree, - pad_val=0, - seg_pad_val=255, - center=None, - auto_bound=False): - self.prob = prob - assert prob >= 0 and prob <= 1 - if isinstance(degree, (float, int)): - assert degree > 0, f'degree {degree} should be positive' - self.degree = (-degree, degree) - else: - self.degree = degree - assert len(self.degree) == 2, f'degree {self.degree} should be a ' \ - f'tuple of (min, max)' - self.pal_val = pad_val - self.seg_pad_val = seg_pad_val - self.center = center - self.auto_bound = auto_bound - - def __call__(self, results): - """Call function to rotate image, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Rotated results. - """ - - rotate = True if np.random.rand() < self.prob else False - degree = np.random.uniform(min(*self.degree), max(*self.degree)) - if rotate: - # rotate image - results['img'] = mmcv.imrotate( - results['img'], - angle=degree, - border_value=self.pal_val, - center=self.center, - auto_bound=self.auto_bound) - - # rotate segs - for key in results.get('seg_fields', []): - results[key] = mmcv.imrotate( - results[key], - angle=degree, - border_value=self.seg_pad_val, - center=self.center, - auto_bound=self.auto_bound, - interpolation='nearest') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' \ - f'degree={self.degree}, ' \ - f'pad_val={self.pal_val}, ' \ - f'seg_pad_val={self.seg_pad_val}, ' \ - f'center={self.center}, ' \ - f'auto_bound={self.auto_bound})' - return repr_str - - -@PIPELINES.register_module() -class RGB2Gray(object): - """Convert RGB image to grayscale image. - - This transform calculate the weighted mean of input image channels with - ``weights`` and then expand the channels to ``out_channels``. When - ``out_channels`` is None, the number of output channels is the same as - input channels. - - Args: - out_channels (int): Expected number of output channels after - transforming. Default: None. - weights (tuple[float]): The weights to calculate the weighted mean. - Default: (0.299, 0.587, 0.114). - """ - - def __init__(self, out_channels=None, weights=(0.299, 0.587, 0.114)): - assert out_channels is None or out_channels > 0 - self.out_channels = out_channels - assert isinstance(weights, tuple) - for item in weights: - assert isinstance(item, (float, int)) - self.weights = weights - - def __call__(self, results): - """Call function to convert RGB image to grayscale image. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with grayscale image. - """ - img = results['img'] - assert len(img.shape) == 3 - assert img.shape[2] == len(self.weights) - weights = np.array(self.weights).reshape((1, 1, -1)) - img = (img * weights).sum(2, keepdims=True) - if self.out_channels is None: - img = img.repeat(weights.shape[2], axis=2) - else: - img = img.repeat(self.out_channels, axis=2) - - results['img'] = img - results['img_shape'] = img.shape - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(out_channels={self.out_channels}, ' \ - f'weights={self.weights})' - return repr_str - - -@PIPELINES.register_module() -class AdjustGamma(object): - """Using gamma correction to process the image. - - Args: - gamma (float or int): Gamma value used in gamma correction. - Default: 1.0. - """ - - def __init__(self, gamma=1.0): - assert isinstance(gamma, float) or isinstance(gamma, int) - assert gamma > 0 - self.gamma = gamma - inv_gamma = 1.0 / gamma - self.table = np.array([(i / 255.0)**inv_gamma * 255 - for i in np.arange(256)]).astype('uint8') - - def __call__(self, results): - """Call function to process the image with gamma correction. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - results['img'] = mmcv.lut_transform( - np.array(results['img'], dtype=np.uint8), self.table) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(gamma={self.gamma})' - - -@PIPELINES.register_module() -class SegRescale(object): - """Rescale semantic segmentation maps. - - Args: - scale_factor (float): The scale factor of the final output. - """ - - def __init__(self, scale_factor=1): - self.scale_factor = scale_factor - - def __call__(self, results): - """Call function to scale the semantic segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with semantic segmentation map scaled. - """ - for key in results.get('seg_fields', []): - if self.scale_factor != 1: - results[key] = mmcv.imrescale( - results[key], self.scale_factor, interpolation='nearest') - return results - - def __repr__(self): - return self.__class__.__name__ + f'(scale_factor={self.scale_factor})' - - -@PIPELINES.register_module() -class PhotoMetricDistortion(object): - """Apply photometric distortion to image sequentially, every transformation - is applied with a probability of 0.5. The position of random contrast is in - second or second to last. - - 1. random brightness - 2. random contrast (mode 0) - 3. convert color from BGR to HSV - 4. random saturation - 5. random hue - 6. convert color from HSV to BGR - 7. random contrast (mode 1) - - Args: - brightness_delta (int): delta of brightness. - contrast_range (tuple): range of contrast. - saturation_range (tuple): range of saturation. - hue_delta (int): delta of hue. - """ - - def __init__(self, - brightness_delta=32, - contrast_range=(0.5, 1.5), - saturation_range=(0.5, 1.5), - hue_delta=18): - self.brightness_delta = brightness_delta - self.contrast_lower, self.contrast_upper = contrast_range - self.saturation_lower, self.saturation_upper = saturation_range - self.hue_delta = hue_delta - - def convert(self, img, alpha=1, beta=0): - """Multiple with alpha and add beat with clip.""" - img = img.astype(np.float32) * alpha + beta - img = np.clip(img, 0, 255) - return img.astype(np.uint8) - - def brightness(self, img): - """Brightness distortion.""" - if random.randint(2): - return self.convert( - img, - beta=random.uniform(-self.brightness_delta, - self.brightness_delta)) - return img - - def contrast(self, img): - """Contrast distortion.""" - if random.randint(2): - return self.convert( - img, - alpha=random.uniform(self.contrast_lower, self.contrast_upper)) - return img - - def saturation(self, img): - """Saturation distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, 1] = self.convert( - img[:, :, 1], - alpha=random.uniform(self.saturation_lower, - self.saturation_upper)) - img = mmcv.hsv2bgr(img) - return img - - def hue(self, img): - """Hue distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, - 0] = (img[:, :, 0].astype(int) + - random.randint(-self.hue_delta, self.hue_delta)) % 180 - img = mmcv.hsv2bgr(img) - return img - - def __call__(self, results): - """Call function to perform photometric distortion on images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images distorted. - """ - - img = results['img'] - # random brightness - img = self.brightness(img) - - # mode == 0 --> do random contrast first - # mode == 1 --> do random contrast last - mode = random.randint(2) - if mode == 1: - img = self.contrast(img) - - # random saturation - img = self.saturation(img) - - # random hue - img = self.hue(img) - - # random contrast - if mode == 0: - img = self.contrast(img) - - results['img'] = img - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(brightness_delta={self.brightness_delta}, ' - f'contrast_range=({self.contrast_lower}, ' - f'{self.contrast_upper}), ' - f'saturation_range=({self.saturation_lower}, ' - f'{self.saturation_upper}), ' - f'hue_delta={self.hue_delta})') - return repr_str - - -@PIPELINES.register_module() -class RandomCutOut(object): - """CutOut operation. - - Randomly drop some regions of image used in - `Cutout `_. - Args: - prob (float): cutout probability. - n_holes (int | tuple[int, int]): Number of regions to be dropped. - If it is given as a list, number of holes will be randomly - selected from the closed interval [`n_holes[0]`, `n_holes[1]`]. - cutout_shape (tuple[int, int] | list[tuple[int, int]]): The candidate - shape of dropped regions. It can be `tuple[int, int]` to use a - fixed cutout shape, or `list[tuple[int, int]]` to randomly choose - shape from the list. - cutout_ratio (tuple[float, float] | list[tuple[float, float]]): The - candidate ratio of dropped regions. It can be `tuple[float, float]` - to use a fixed ratio or `list[tuple[float, float]]` to randomly - choose ratio from the list. Please note that `cutout_shape` - and `cutout_ratio` cannot be both given at the same time. - fill_in (tuple[float, float, float] | tuple[int, int, int]): The value - of pixel to fill in the dropped regions. Default: (0, 0, 0). - seg_fill_in (int): The labels of pixel to fill in the dropped regions. - If seg_fill_in is None, skip. Default: None. - """ - - def __init__(self, - prob, - n_holes, - cutout_shape=None, - cutout_ratio=None, - fill_in=(0, 0, 0), - seg_fill_in=None): - - assert 0 <= prob and prob <= 1 - assert (cutout_shape is None) ^ (cutout_ratio is None), \ - 'Either cutout_shape or cutout_ratio should be specified.' - assert (isinstance(cutout_shape, (list, tuple)) - or isinstance(cutout_ratio, (list, tuple))) - if isinstance(n_holes, tuple): - assert len(n_holes) == 2 and 0 <= n_holes[0] < n_holes[1] - else: - n_holes = (n_holes, n_holes) - if seg_fill_in is not None: - assert (isinstance(seg_fill_in, int) and 0 <= seg_fill_in - and seg_fill_in <= 255) - self.prob = prob - self.n_holes = n_holes - self.fill_in = fill_in - self.seg_fill_in = seg_fill_in - self.with_ratio = cutout_ratio is not None - self.candidates = cutout_ratio if self.with_ratio else cutout_shape - if not isinstance(self.candidates, list): - self.candidates = [self.candidates] - - def __call__(self, results): - """Call function to drop some regions of image.""" - cutout = True if np.random.rand() < self.prob else False - if cutout: - h, w, c = results['img'].shape - n_holes = np.random.randint(self.n_holes[0], self.n_holes[1] + 1) - for _ in range(n_holes): - x1 = np.random.randint(0, w) - y1 = np.random.randint(0, h) - index = np.random.randint(0, len(self.candidates)) - if not self.with_ratio: - cutout_w, cutout_h = self.candidates[index] - else: - cutout_w = int(self.candidates[index][0] * w) - cutout_h = int(self.candidates[index][1] * h) - - x2 = np.clip(x1 + cutout_w, 0, w) - y2 = np.clip(y1 + cutout_h, 0, h) - results['img'][y1:y2, x1:x2, :] = self.fill_in - - if self.seg_fill_in is not None: - for key in results.get('seg_fields', []): - results[key][y1:y2, x1:x2] = self.seg_fill_in - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' - repr_str += f'n_holes={self.n_holes}, ' - repr_str += (f'cutout_ratio={self.candidates}, ' if self.with_ratio - else f'cutout_shape={self.candidates}, ') - repr_str += f'fill_in={self.fill_in}, ' - repr_str += f'seg_fill_in={self.seg_fill_in})' - return repr_str - - -@PIPELINES.register_module() -class RandomMosaic(object): - """Mosaic augmentation. Given 4 images, mosaic transform combines them into - one output image. The output image is composed of the parts from each sub- - image. - - .. code:: text - - mosaic transform - center_x - +------------------------------+ - | pad | pad | - | +-----------+ | - | | | | - | | image1 |--------+ | - | | | | | - | | | image2 | | - center_y |----+-------------+-----------| - | | cropped | | - |pad | image3 | image4 | - | | | | - +----|-------------+-----------+ - | | - +-------------+ - - The mosaic transform steps are as follows: - 1. Choose the mosaic center as the intersections of 4 images - 2. Get the left top image according to the index, and randomly - sample another 3 images from the custom dataset. - 3. Sub image will be cropped if image is larger than mosaic patch - - Args: - prob (float): mosaic probability. - img_scale (Sequence[int]): Image size after mosaic pipeline of - a single image. The size of the output image is four times - that of a single image. The output image comprises 4 single images. - Default: (640, 640). - center_ratio_range (Sequence[float]): Center ratio range of mosaic - output. Default: (0.5, 1.5). - pad_val (int): Pad value. Default: 0. - seg_pad_val (int): Pad value of segmentation map. Default: 255. - """ - - def __init__(self, - prob, - img_scale=(640, 640), - center_ratio_range=(0.5, 1.5), - pad_val=0, - seg_pad_val=255): - assert 0 <= prob and prob <= 1 - assert isinstance(img_scale, tuple) - self.prob = prob - self.img_scale = img_scale - self.center_ratio_range = center_ratio_range - self.pad_val = pad_val - self.seg_pad_val = seg_pad_val - - def __call__(self, results): - """Call function to make a mosaic of image. - - Args: - results (dict): Result dict. - - Returns: - dict: Result dict with mosaic transformed. - """ - mosaic = True if np.random.rand() < self.prob else False - if mosaic: - results = self._mosaic_transform_img(results) - results = self._mosaic_transform_seg(results) - return results - - def get_indexes(self, dataset): - """Call function to collect indexes. - - Args: - dataset (:obj:`MultiImageMixDataset`): The dataset. - - Returns: - list: indexes. - """ - - indexes = [random.randint(0, len(dataset)) for _ in range(3)] - return indexes - - def _mosaic_transform_img(self, results): - """Mosaic transform function. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - if len(results['img'].shape) == 3: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2), 3), - self.pad_val, - dtype=results['img'].dtype) - else: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.pad_val, - dtype=results['img'].dtype) - - # mosaic center x, y - self.center_x = int( - random.uniform(*self.center_ratio_range) * self.img_scale[1]) - self.center_y = int( - random.uniform(*self.center_ratio_range) * self.img_scale[0]) - center_position = (self.center_x, self.center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - result_patch = copy.deepcopy(results) - else: - result_patch = copy.deepcopy(results['mix_results'][i - 1]) - - img_i = result_patch['img'] - h_i, w_i = img_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - img_i = mmcv.imresize( - img_i, (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i))) - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, img_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_img[y1_p:y2_p, x1_p:x2_p] = img_i[y1_c:y2_c, x1_c:x2_c] - - results['img'] = mosaic_img - results['img_shape'] = mosaic_img.shape - results['ori_shape'] = mosaic_img.shape - - return results - - def _mosaic_transform_seg(self, results): - """Mosaic transform function for label annotations. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - for key in results.get('seg_fields', []): - mosaic_seg = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.seg_pad_val, - dtype=results[key].dtype) - - # mosaic center x, y - center_position = (self.center_x, self.center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - result_patch = copy.deepcopy(results) - else: - result_patch = copy.deepcopy(results['mix_results'][i - 1]) - - gt_seg_i = result_patch[key] - h_i, w_i = gt_seg_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - gt_seg_i = mmcv.imresize( - gt_seg_i, - (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i)), - interpolation='nearest') - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, gt_seg_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_seg[y1_p:y2_p, x1_p:x2_p] = gt_seg_i[y1_c:y2_c, - x1_c:x2_c] - - results[key] = mosaic_seg - - return results - - def _mosaic_combine(self, loc, center_position_xy, img_shape_wh): - """Calculate global coordinate of mosaic image and local coordinate of - cropped sub-image. - - Args: - loc (str): Index for the sub-image, loc in ('top_left', - 'top_right', 'bottom_left', 'bottom_right'). - center_position_xy (Sequence[float]): Mixing center for 4 images, - (x, y). - img_shape_wh (Sequence[int]): Width and height of sub-image - - Returns: - tuple[tuple[float]]: Corresponding coordinate of pasting and - cropping - - paste_coord (tuple): paste corner coordinate in mosaic image. - - crop_coord (tuple): crop corner coordinate in mosaic image. - """ - - assert loc in ('top_left', 'top_right', 'bottom_left', 'bottom_right') - if loc == 'top_left': - # index0 to top left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - center_position_xy[0], \ - center_position_xy[1] - crop_coord = img_shape_wh[0] - (x2 - x1), img_shape_wh[1] - ( - y2 - y1), img_shape_wh[0], img_shape_wh[1] - - elif loc == 'top_right': - # index1 to top right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - center_position_xy[1] - crop_coord = 0, img_shape_wh[1] - (y2 - y1), min( - img_shape_wh[0], x2 - x1), img_shape_wh[1] - - elif loc == 'bottom_left': - # index2 to bottom left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - center_position_xy[1], \ - center_position_xy[0], \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = img_shape_wh[0] - (x2 - x1), 0, img_shape_wh[0], min( - y2 - y1, img_shape_wh[1]) - - else: - # index3 to bottom right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - center_position_xy[1], \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = 0, 0, min(img_shape_wh[0], - x2 - x1), min(y2 - y1, img_shape_wh[1]) - - paste_coord = x1, y1, x2, y2 - return paste_coord, crop_coord - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' - repr_str += f'img_scale={self.img_scale}, ' - repr_str += f'center_ratio_range={self.center_ratio_range}, ' - repr_str += f'pad_val={self.pad_val}, ' - repr_str += f'seg_pad_val={self.pad_val})' - return repr_str diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/samplers/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/samplers/__init__.py deleted file mode 100644 index da09effaf..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/samplers/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .distributed_sampler import DistributedSampler - -__all__ = ['DistributedSampler'] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/samplers/distributed_sampler.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/samplers/distributed_sampler.py deleted file mode 100644 index d1a13c716..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/samplers/distributed_sampler.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from __future__ import division -from typing import Iterator, Optional - -import torch -from torch.utils.data import Dataset -from torch.utils.data import DistributedSampler as _DistributedSampler - -from mmseg.core.utils import sync_random_seed - - -class DistributedSampler(_DistributedSampler): - """DistributedSampler inheriting from - `torch.utils.data.DistributedSampler`. - - Args: - datasets (Dataset): the dataset will be loaded. - num_replicas (int, optional): Number of processes participating in - distributed training. By default, world_size is retrieved from the - current distributed group. - rank (int, optional): Rank of the current process within num_replicas. - By default, rank is retrieved from the current distributed group. - shuffle (bool): If True (default), sampler will shuffle the indices. - seed (int): random seed used to shuffle the sampler if - :attr:`shuffle=True`. This number should be identical across all - processes in the distributed group. Default: ``0``. - """ - - def __init__(self, - dataset: Dataset, - num_replicas: Optional[int] = None, - rank: Optional[int] = None, - shuffle: bool = True, - seed=0) -> None: - super().__init__( - dataset, num_replicas=num_replicas, rank=rank, shuffle=shuffle) - - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - - def __iter__(self) -> Iterator: - """ - Yields: - Iterator: iterator of indices for rank. - """ - # deterministically shuffle based on epoch - if self.shuffle: - g = torch.Generator() - # When :attr:`shuffle=True`, this ensures all replicas - # use a different random ordering for each epoch. - # Otherwise, the next iteration of this sampler will - # yield the same ordering. - g.manual_seed(self.epoch + self.seed) - indices = torch.randperm(len(self.dataset), generator=g).tolist() - else: - indices = torch.arange(len(self.dataset)).tolist() - - # add extra samples to make it evenly divisible - indices += indices[:(self.total_size - len(indices))] - assert len(indices) == self.total_size - - # subsample - indices = indices[self.rank:self.total_size:self.num_replicas] - assert len(indices) == self.num_samples - - return iter(indices) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/voc.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/voc.py deleted file mode 100644 index 9eecc344f..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/datasets/voc.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class PascalVOCDataset(CustomDataset): - """Pascal VOC dataset. - - Args: - split (str): Split txt file for Pascal VOC. - """ - - CLASSES = ('background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', - 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', - 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', - 'train', 'tvmonitor') - - PALETTE = [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], - [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], - [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], - [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], - [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] - - def __init__(self, split, **kwargs): - - if "img_dir" in kwargs: - image_dir = kwargs["img_dir"] - if not osp.join(kwargs['data_root'], image_dir): - image_dir = "images" - if osp.join(kwargs['data_root'], image_dir): - kwargs["img_dir"] = image_dir - - super(PascalVOCDataset, self).__init__( - img_suffix='.jpg', seg_map_suffix='.png', split=split, **kwargs) - - assert osp.exists(self.img_dir) and self.split is not None diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/__init__.py deleted file mode 100644 index 87d8108e3..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .backbones import * # noqa: F401,F403 -from .builder import (BACKBONES, HEADS, LOSSES, SEGMENTORS, build_backbone, - build_head, build_loss, build_segmentor) -from .decode_heads import * # noqa: F401,F403 -from .losses import * # noqa: F401,F403 -from .necks import * # noqa: F401,F403 -from .segmentors import * # noqa: F401,F403 - -__all__ = [ - 'BACKBONES', 'HEADS', 'LOSSES', 'SEGMENTORS', 'build_backbone', - 'build_head', 'build_loss', 'build_segmentor' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/backbones/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/backbones/__init__.py deleted file mode 100644 index 464bfa9f4..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/backbones/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .resnet import ResNet, ResNetV1c, ResNetV1d - diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/backbones/resnet.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/backbones/resnet.py deleted file mode 100644 index e8b961d5f..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/backbones/resnet.py +++ /dev/null @@ -1,714 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.utils.checkpoint as cp -from mmcv.cnn import build_conv_layer, build_norm_layer, build_plugin_layer -from mmcv.runner import BaseModule -from mmcv.utils.parrots_wrapper import _BatchNorm - -from ..builder import BACKBONES -from ..utils import ResLayer - - -class BasicBlock(BaseModule): - """Basic block for ResNet.""" - - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(BasicBlock, self).__init__(init_cfg) - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - 3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - self.add_module(self.norm1_name, norm1) - self.conv2 = build_conv_layer( - conv_cfg, planes, planes, 3, padding=1, bias=False) - self.add_module(self.norm2_name, norm2) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.norm2(out) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -class Bottleneck(BaseModule): - """Bottleneck block for ResNet. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if it is - "caffe", the stride-two layer is the first 1x1 conv layer. - """ - - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(Bottleneck, self).__init__(init_cfg) - assert style in ['pytorch', 'caffe'] - assert dcn is None or isinstance(dcn, dict) - assert plugins is None or isinstance(plugins, list) - if plugins is not None: - allowed_position = ['after_conv1', 'after_conv2', 'after_conv3'] - assert all(p['position'] in allowed_position for p in plugins) - - self.inplanes = inplanes - self.planes = planes - self.stride = stride - self.dilation = dilation - self.style = style - self.with_cp = with_cp - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.dcn = dcn - self.with_dcn = dcn is not None - self.plugins = plugins - self.with_plugins = plugins is not None - - if self.with_plugins: - # collect plugins for conv1/conv2/conv3 - self.after_conv1_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv1' - ] - self.after_conv2_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv2' - ] - self.after_conv3_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv3' - ] - - if self.style == 'pytorch': - self.conv1_stride = 1 - self.conv2_stride = stride - else: - self.conv1_stride = stride - self.conv2_stride = 1 - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - self.norm3_name, norm3 = build_norm_layer( - norm_cfg, planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - fallback_on_stride = False - if self.with_dcn: - fallback_on_stride = dcn.pop('fallback_on_stride', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = build_conv_layer( - conv_cfg, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - else: - assert self.conv_cfg is None, 'conv_cfg must be None for DCN' - self.conv2 = build_conv_layer( - dcn, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.add_module(self.norm2_name, norm2) - self.conv3 = build_conv_layer( - conv_cfg, - planes, - planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - - if self.with_plugins: - self.after_conv1_plugin_names = self.make_block_plugins( - planes, self.after_conv1_plugins) - self.after_conv2_plugin_names = self.make_block_plugins( - planes, self.after_conv2_plugins) - self.after_conv3_plugin_names = self.make_block_plugins( - planes * self.expansion, self.after_conv3_plugins) - - def make_block_plugins(self, in_channels, plugins): - """make plugins for block. - - Args: - in_channels (int): Input channels of plugin. - plugins (list[dict]): List of plugins cfg to build. - - Returns: - list[str]: List of the names of plugin. - """ - assert isinstance(plugins, list) - plugin_names = [] - for plugin in plugins: - plugin = plugin.copy() - name, layer = build_plugin_layer( - plugin, - in_channels=in_channels, - postfix=plugin.pop('postfix', '')) - assert not hasattr(self, name), f'duplicate plugin {name}' - self.add_module(name, layer) - plugin_names.append(name) - return plugin_names - - def forward_plugin(self, x, plugin_names): - """Forward function for plugins.""" - out = x - for name in plugin_names: - out = getattr(self, name)(x) - return out - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - @property - def norm3(self): - """nn.Module: normalization layer after the third convolution layer""" - return getattr(self, self.norm3_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv1_plugin_names) - - out = self.conv2(out) - out = self.norm2(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv2_plugin_names) - - out = self.conv3(out) - out = self.norm3(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv3_plugin_names) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -@BACKBONES.register_module() -class ResNet(BaseModule): - """ResNet backbone. - - This backbone is the improved implementation of `Deep Residual Learning - for Image Recognition `_. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - in_channels (int): Number of input image channels. Default: 3. - stem_channels (int): Number of stem channels. Default: 64. - base_channels (int): Number of base channels of res layer. Default: 64. - num_stages (int): Resnet stages, normally 4. Default: 4. - strides (Sequence[int]): Strides of the first block of each stage. - Default: (1, 2, 2, 2). - dilations (Sequence[int]): Dilation of each stage. - Default: (1, 1, 1, 1). - out_indices (Sequence[int]): Output from which stages. - Default: (0, 1, 2, 3). - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. Default: 'pytorch'. - deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv. - Default: False. - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False. - frozen_stages (int): Stages to be frozen (stop grad and set eval mode). - -1 means not freezing any parameters. Default: -1. - conv_cfg (dict | None): Dictionary to construct and config conv layer. - When conv_cfg is None, cfg will be set to dict(type='Conv2d'). - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN', requires_grad=True). - norm_eval (bool): Whether to set norm layers to eval mode, namely, - freeze running stats (mean and var). Note: Effect on Batch Norm - and its variants only. Default: False. - dcn (dict | None): Dictionary to construct and config DCN conv layer. - When dcn is not None, conv_cfg must be None. Default: None. - stage_with_dcn (Sequence[bool]): Whether to set DCN conv for each - stage. The length of stage_with_dcn is equal to num_stages. - Default: (False, False, False, False). - plugins (list[dict]): List of plugins for stages, each dict contains: - - - cfg (dict, required): Cfg dict to build plugin. - - - position (str, required): Position inside block to insert plugin, - options: 'after_conv1', 'after_conv2', 'after_conv3'. - - - stages (tuple[bool], optional): Stages to apply plugin, length - should be same as 'num_stages'. - Default: None. - multi_grid (Sequence[int]|None): Multi grid dilation rates of last - stage. Default: None. - contract_dilation (bool): Whether contract first dilation of each layer - Default: False. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - zero_init_residual (bool): Whether to use zero init for last norm layer - in resblocks to let them behave as identity. Default: True. - pretrained (str, optional): model pretrained path. Default: None. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Example: - >>> from mmseg.models import ResNet - >>> import torch - >>> self = ResNet(depth=18) - >>> self.eval() - >>> inputs = torch.rand(1, 3, 32, 32) - >>> level_outputs = self.forward(inputs) - >>> for level_out in level_outputs: - ... print(tuple(level_out.shape)) - (1, 64, 8, 8) - (1, 128, 4, 4) - (1, 256, 2, 2) - (1, 512, 1, 1) - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - in_channels=3, - stem_channels=64, - base_channels=64, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - deep_stem=False, - avg_down=False, - frozen_stages=-1, - conv_cfg=None, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=False, - dcn=None, - stage_with_dcn=(False, False, False, False), - plugins=None, - multi_grid=None, - contract_dilation=False, - with_cp=False, - zero_init_residual=True, - pretrained=None, - init_cfg=None): - super(ResNet, self).__init__(init_cfg) - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - - self.pretrained = pretrained - self.zero_init_residual = zero_init_residual - block_init_cfg = None - assert not (init_cfg and pretrained), \ - 'init_cfg and pretrained cannot be setting at the same time' - if isinstance(pretrained, str): - warnings.warn('DeprecationWarning: pretrained is a deprecated, ' - 'please use "init_cfg" instead') - self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - elif pretrained is None: - if init_cfg is None: - self.init_cfg = [ - dict(type='Kaiming', layer='Conv2d'), - dict( - type='Constant', - val=1, - layer=['_BatchNorm', 'GroupNorm']) - ] - block = self.arch_settings[depth][0] - if self.zero_init_residual: - if block is BasicBlock: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm2')) - elif block is Bottleneck: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm3')) - else: - raise TypeError('pretrained must be a str or None') - - self.depth = depth - self.stem_channels = stem_channels - self.base_channels = base_channels - self.num_stages = num_stages - assert num_stages >= 1 and num_stages <= 4 - self.strides = strides - self.dilations = dilations - assert len(strides) == len(dilations) == num_stages - self.out_indices = out_indices - assert max(out_indices) < num_stages - self.style = style - self.deep_stem = deep_stem - self.avg_down = avg_down - self.frozen_stages = frozen_stages - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.with_cp = with_cp - self.norm_eval = norm_eval - self.dcn = dcn - self.stage_with_dcn = stage_with_dcn - if dcn is not None: - assert len(stage_with_dcn) == num_stages - self.plugins = plugins - self.multi_grid = multi_grid - self.contract_dilation = contract_dilation - self.block, stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - self.inplanes = stem_channels - - self._make_stem_layer(in_channels, stem_channels) - - self.res_layers = [] - for i, num_blocks in enumerate(self.stage_blocks): - stride = strides[i] - dilation = dilations[i] - dcn = self.dcn if self.stage_with_dcn[i] else None - if plugins is not None: - stage_plugins = self.make_stage_plugins(plugins, i) - else: - stage_plugins = None - # multi grid is applied to last layer only - stage_multi_grid = multi_grid if i == len( - self.stage_blocks) - 1 else None - planes = base_channels * 2**i - res_layer = self.make_res_layer( - block=self.block, - inplanes=self.inplanes, - planes=planes, - num_blocks=num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - avg_down=self.avg_down, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - dcn=dcn, - plugins=stage_plugins, - multi_grid=stage_multi_grid, - contract_dilation=contract_dilation, - init_cfg=block_init_cfg) - self.inplanes = planes * self.block.expansion - layer_name = f'layer{i+1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self._freeze_stages() - - self.feat_dim = self.block.expansion * base_channels * 2**( - len(self.stage_blocks) - 1) - - def make_stage_plugins(self, plugins, stage_idx): - """make plugins for ResNet 'stage_idx'th stage . - - Currently we support to insert 'context_block', - 'empirical_attention_block', 'nonlocal_block' into the backbone like - ResNet/ResNeXt. They could be inserted after conv1/conv2/conv3 of - Bottleneck. - - An example of plugins format could be : - >>> plugins=[ - ... dict(cfg=dict(type='xxx', arg1='xxx'), - ... stages=(False, True, True, True), - ... position='after_conv2'), - ... dict(cfg=dict(type='yyy'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='1'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='2'), - ... stages=(True, True, True, True), - ... position='after_conv3') - ... ] - >>> self = ResNet(depth=18) - >>> stage_plugins = self.make_stage_plugins(plugins, 0) - >>> assert len(stage_plugins) == 3 - - Suppose 'stage_idx=0', the structure of blocks in the stage would be: - conv1-> conv2->conv3->yyy->zzz1->zzz2 - Suppose 'stage_idx=1', the structure of blocks in the stage would be: - conv1-> conv2->xxx->conv3->yyy->zzz1->zzz2 - - If stages is missing, the plugin would be applied to all stages. - - Args: - plugins (list[dict]): List of plugins cfg to build. The postfix is - required if multiple same type plugins are inserted. - stage_idx (int): Index of stage to build - - Returns: - list[dict]: Plugins for current stage - """ - stage_plugins = [] - for plugin in plugins: - plugin = plugin.copy() - stages = plugin.pop('stages', None) - assert stages is None or len(stages) == self.num_stages - # whether to insert plugin into current stage - if stages is None or stages[stage_idx]: - stage_plugins.append(plugin) - - return stage_plugins - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``.""" - return ResLayer(**kwargs) - - @property - def norm1(self): - """nn.Module: the normalization layer named "norm1" """ - return getattr(self, self.norm1_name) - - def _make_stem_layer(self, in_channels, stem_channels): - """Make stem layer for ResNet.""" - if self.deep_stem: - self.stem = nn.Sequential( - build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels // 2, - kernel_size=3, - stride=2, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels // 2, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels)[1], - nn.ReLU(inplace=True)) - else: - self.conv1 = build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels, - kernel_size=7, - stride=2, - padding=3, - bias=False) - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, stem_channels, postfix=1) - self.add_module(self.norm1_name, norm1) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - def _freeze_stages(self): - """Freeze stages param and norm stats.""" - if self.frozen_stages >= 0: - if self.deep_stem: - self.stem.eval() - for param in self.stem.parameters(): - param.requires_grad = False - else: - self.norm1.eval() - for m in [self.conv1, self.norm1]: - for param in m.parameters(): - param.requires_grad = False - - for i in range(1, self.frozen_stages + 1): - m = getattr(self, f'layer{i}') - m.eval() - for param in m.parameters(): - param.requires_grad = False - - def forward(self, x): - """Forward function.""" - if self.deep_stem: - x = self.stem(x) - else: - x = self.conv1(x) - x = self.norm1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - return tuple(outs) - - def train(self, mode=True): - """Convert the model into training mode while keep normalization layer - freezed.""" - super(ResNet, self).train(mode) - self._freeze_stages() - if mode and self.norm_eval: - for m in self.modules(): - # trick: eval have effect on BatchNorm only - if isinstance(m, _BatchNorm): - m.eval() - - -@BACKBONES.register_module() -class ResNetV1c(ResNet): - """ResNetV1c variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1c replaces the 7x7 conv in - the input stem with three 3x3 convs. For more details please refer to `Bag - of Tricks for Image Classification with Convolutional Neural Networks - `_. - """ - - def __init__(self, **kwargs): - super(ResNetV1c, self).__init__( - deep_stem=True, avg_down=False, **kwargs) - - -@BACKBONES.register_module() -class ResNetV1d(ResNet): - """ResNetV1d variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1d replaces the 7x7 conv in - the input stem with three 3x3 convs. And in the downsampling block, a 2x2 - avg_pool with stride 2 is added before conv, whose stride is changed to 1. - """ - - def __init__(self, **kwargs): - super(ResNetV1d, self).__init__( - deep_stem=True, avg_down=True, **kwargs) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/builder.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/builder.py deleted file mode 100644 index 5e18e4e64..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/builder.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -from mmcv.cnn import MODELS as MMCV_MODELS -from mmcv.cnn.bricks.registry import ATTENTION as MMCV_ATTENTION -from mmcv.utils import Registry - -MODELS = Registry('models', parent=MMCV_MODELS) -ATTENTION = Registry('attention', parent=MMCV_ATTENTION) - -BACKBONES = MODELS -NECKS = MODELS -HEADS = MODELS -LOSSES = MODELS -SEGMENTORS = MODELS - - -def build_backbone(cfg): - """Build backbone.""" - return BACKBONES.build(cfg) - - -def build_neck(cfg): - """Build neck.""" - return NECKS.build(cfg) - - -def build_head(cfg): - """Build head.""" - return HEADS.build(cfg) - - -def build_loss(cfg): - """Build loss.""" - return LOSSES.build(cfg) - - -def build_segmentor(cfg, train_cfg=None, test_cfg=None): - """Build segmentor.""" - if train_cfg is not None or test_cfg is not None: - warnings.warn( - 'train_cfg and test_cfg is deprecated, ' - 'please specify them in model', UserWarning) - assert cfg.get('train_cfg') is None or train_cfg is None, \ - 'train_cfg specified in both outer field and model field ' - assert cfg.get('test_cfg') is None or test_cfg is None, \ - 'test_cfg specified in both outer field and model field ' - return SEGMENTORS.build( - cfg, default_args=dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/decode_heads/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/decode_heads/__init__.py deleted file mode 100644 index a7b0b2d68..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/decode_heads/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from .decode_head import BaseDecodeHead -from .attunet_head import ATTUNetHead diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/decode_heads/attunet_head.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/decode_heads/attunet_head.py deleted file mode 100644 index 86ed0f56b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/decode_heads/attunet_head.py +++ /dev/null @@ -1,124 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import HEADS -from .decode_head import BaseDecodeHead - -def conv_bn_relu(in_channels, out_channels, kernel_size=3, padding=1, stride=1): - return nn.Sequential(nn.Conv2d(in_channels, - out_channels, - kernel_size=kernel_size, - padding=padding, - stride=stride), - nn.BatchNorm2d(out_channels), - nn.ReLU(inplace=True)) - -class AttBlock(nn.Module): - def __init__(self, F_g, F_l, F_int): - super(AttBlock, self).__init__() - self.W_g = nn.Sequential( - nn.Conv2d(in_channels=F_g, - out_channels=F_int, - kernel_size=1, - stride=1, - padding=0, - bias=True), - nn.BatchNorm2d(F_int) - ) - - self.W_x = nn.Sequential( - nn.Conv2d(in_channels=F_l, - out_channels=F_int, - kernel_size=1, - stride=1, - padding=0, - bias=True), - nn.BatchNorm2d(F_int) - ) - - self.psi = nn.Sequential( - nn.Conv2d(in_channels=F_int, - out_channels=1, - kernel_size=1, - stride=1, - padding=0, - bias=True), - nn.BatchNorm2d(1), - nn.Sigmoid() - ) - - self.relu = nn.ReLU(inplace=True) - - def forward(self, g, x): - g1 = self.W_g(g) - x1 = self.W_x(x) - psi = self.relu(g1 + x1) - psi = self.psi(psi) - - return x * psi - - -class DecBlock(nn.Module): - def __init__( - self, - in_channels, - skip_channels, - out_channels - ): - super().__init__() - self.conv1 = conv_bn_relu(in_channels=in_channels + skip_channels, - out_channels=out_channels) - - self.conv2 = conv_bn_relu(in_channels=out_channels, - out_channels=out_channels) - - self.up = nn.Upsample(scale_factor=2, - mode='bilinear', - align_corners=True) - - self.att = AttBlock(F_g=in_channels, F_l=skip_channels, F_int=in_channels) - - def forward(self, x, skip=None): - x = self.up(x) - if skip is not None: - if hasattr(self, "att"): - skip = self.att(g=x, x=skip) - x = torch.cat([x, skip], dim=1) - x = self.conv1(x) - x = self.conv2(x) - return x - - -@HEADS.register_module() -class ATTUNetHead(BaseDecodeHead): - def __init__(self, **kwargs): - super(ATTUNetHead, self).__init__(**kwargs) - self.decoders = nn.ModuleList() - in_channels = self.in_channels[::-1] - skip_channels = in_channels[1:] - for in_c, skip_c in zip(in_channels, skip_channels): - self.decoders.append(DecBlock(in_c, skip_c, skip_c)) - - def forward(self, features): - features = features[::-1] - x = features[0] - skips = features[1:] - - for i, layer in enumerate(self.decoders): - if i < len(skips): - x = layer(x, skips[i]) - else: - x = layer(x) - - output = self.cls_seg(x) - - return output - - def init_weights(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') - elif isinstance(m, nn.BatchNorm2d): - nn.init.constant_(m.weight, 1) - nn.init.constant_(m.bias, 0) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/decode_heads/decode_head.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/decode_heads/decode_head.py deleted file mode 100644 index 098e1b4e4..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/decode_heads/decode_head.py +++ /dev/null @@ -1,266 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -import torch -import torch.nn as nn -from mmcv.runner import BaseModule, auto_fp16, force_fp32 - -from mmseg.core import build_pixel_sampler -from mmseg.ops import resize -from ..builder import build_loss -from ..losses import accuracy - - -class BaseDecodeHead(BaseModule, metaclass=ABCMeta): - """Base class for BaseDecodeHead. - - Args: - in_channels (int|Sequence[int]): Input channels. - channels (int): Channels after modules, before conv_seg. - num_classes (int): Number of classes. - dropout_ratio (float): Ratio of dropout layer. Default: 0.1. - conv_cfg (dict|None): Config of conv layers. Default: None. - norm_cfg (dict|None): Config of norm layers. Default: None. - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU') - in_index (int|Sequence[int]): Input feature index. Default: -1 - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - Default: None. - loss_decode (dict | Sequence[dict]): Config of decode loss. - The `loss_name` is property of corresponding loss function which - could be shown in training log. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - e.g. dict(type='CrossEntropyLoss'), - [dict(type='CrossEntropyLoss', loss_name='loss_ce'), - dict(type='DiceLoss', loss_name='loss_dice')] - Default: dict(type='CrossEntropyLoss'). - ignore_index (int | None): The label index to be ignored. When using - masked BCE loss, ignore_index should be set to None. Default: 255. - sampler (dict|None): The config of segmentation map sampler. - Default: None. - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - """ - - def __init__(self, - in_channels, - channels, - *, - num_classes, - dropout_ratio=0.1, - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - in_index=-1, - input_transform=None, - loss_decode=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0), - ignore_index=255, - sampler=None, - align_corners=False, - init_cfg=dict( - type='Normal', std=0.01, override=dict(name='conv_seg'))): - super(BaseDecodeHead, self).__init__(init_cfg) - self._init_inputs(in_channels, in_index, input_transform) - self.channels = channels - self.num_classes = num_classes - self.dropout_ratio = dropout_ratio - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.in_index = in_index - - self.ignore_index = ignore_index - self.align_corners = align_corners - - if isinstance(loss_decode, dict): - self.loss_decode = build_loss(loss_decode) - elif isinstance(loss_decode, (list, tuple)): - self.loss_decode = nn.ModuleList() - for loss in loss_decode: - self.loss_decode.append(build_loss(loss)) - else: - raise TypeError(f'loss_decode must be a dict or sequence of dict,\ - but got {type(loss_decode)}') - - if sampler is not None: - self.sampler = build_pixel_sampler(sampler, context=self) - else: - self.sampler = None - - self.conv_seg = nn.Conv2d(channels, num_classes, kernel_size=1) - if dropout_ratio > 0: - self.dropout = nn.Dropout2d(dropout_ratio) - else: - self.dropout = None - self.fp16_enabled = False - - def extra_repr(self): - """Extra repr.""" - s = f'input_transform={self.input_transform}, ' \ - f'ignore_index={self.ignore_index}, ' \ - f'align_corners={self.align_corners}' - return s - - def _init_inputs(self, in_channels, in_index, input_transform): - """Check and initialize input transforms. - - The in_channels, in_index and input_transform must match. - Specifically, when input_transform is None, only single feature map - will be selected. So in_channels and in_index must be of type int. - When input_transform - - Args: - in_channels (int|Sequence[int]): Input channels. - in_index (int|Sequence[int]): Input feature index. - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - """ - - if input_transform is not None: - assert input_transform in ['resize_concat', 'multiple_select'] - self.input_transform = input_transform - self.in_index = in_index - if input_transform is not None: - assert isinstance(in_channels, (list, tuple)) - assert isinstance(in_index, (list, tuple)) - assert len(in_channels) == len(in_index) - if input_transform == 'resize_concat': - self.in_channels = sum(in_channels) - else: - self.in_channels = in_channels - else: - assert isinstance(in_channels, (int, list, tuple)) - assert isinstance(in_index, int) - self.in_channels = in_channels - - def _transform_inputs(self, inputs): - """Transform inputs for decoder. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - Tensor: The transformed inputs - """ - - if self.input_transform == 'resize_concat': - inputs = [inputs[i] for i in self.in_index] - upsampled_inputs = [ - resize( - input=x, - size=inputs[0].shape[2:], - mode='bilinear', - align_corners=self.align_corners) for x in inputs - ] - inputs = torch.cat(upsampled_inputs, dim=1) - elif self.input_transform == 'multiple_select': - inputs = [inputs[i] for i in self.in_index] - else: - inputs = inputs[self.in_index] - - return inputs - - @auto_fp16() - @abstractmethod - def forward(self, inputs): - """Placeholder of forward function.""" - pass - - def forward_train(self, inputs, img_metas, gt_semantic_seg, train_cfg): - """Forward function for training. - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - train_cfg (dict): The training config. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - seg_logits = self.forward(inputs) - losses = self.losses(seg_logits, gt_semantic_seg) - return losses - - def forward_test(self, inputs, img_metas, test_cfg): - """Forward function for testing. - - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - test_cfg (dict): The testing config. - - Returns: - Tensor: Output segmentation map. - """ - return self.forward(inputs) - - def cls_seg(self, feat): - """Classify each pixel.""" - if self.dropout is not None: - feat = self.dropout(feat) - output = self.conv_seg(feat) - return output - - @force_fp32(apply_to=('seg_logit', )) - def losses(self, seg_logit, seg_label): - """Compute segmentation loss.""" - loss = dict() - seg_logit = resize( - input=seg_logit, - size=seg_label.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - if self.sampler is not None: - seg_weight = self.sampler.sample(seg_logit, seg_label) - else: - seg_weight = None - seg_label = seg_label.squeeze(1) - - if not isinstance(self.loss_decode, nn.ModuleList): - losses_decode = [self.loss_decode] - else: - losses_decode = self.loss_decode - for loss_decode in losses_decode: - if loss_decode.loss_name not in loss: - loss[loss_decode.loss_name] = loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - else: - loss[loss_decode.loss_name] += loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - - loss['acc_seg'] = accuracy( - seg_logit, seg_label, ignore_index=self.ignore_index) - return loss diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/__init__.py deleted file mode 100644 index fbc5b2d1b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .accuracy import Accuracy, accuracy -from .cross_entropy_loss import (CrossEntropyLoss, binary_cross_entropy, - cross_entropy, mask_cross_entropy) -from .dice_loss import DiceLoss -from .focal_loss import FocalLoss -from .lovasz_loss import LovaszLoss -from .utils import reduce_loss, weight_reduce_loss, weighted_loss - -__all__ = [ - 'accuracy', 'Accuracy', 'cross_entropy', 'binary_cross_entropy', - 'mask_cross_entropy', 'CrossEntropyLoss', 'reduce_loss', - 'weight_reduce_loss', 'weighted_loss', 'LovaszLoss', 'DiceLoss', - 'FocalLoss' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/accuracy.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/accuracy.py deleted file mode 100644 index 1d9e2d770..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/accuracy.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def accuracy(pred, target, topk=1, thresh=None, ignore_index=None): - """Calculate accuracy according to the prediction and target. - - Args: - pred (torch.Tensor): The model prediction, shape (N, num_class, ...) - target (torch.Tensor): The target of each prediction, shape (N, , ...) - ignore_index (int | None): The label index to be ignored. Default: None - topk (int | tuple[int], optional): If the predictions in ``topk`` - matches the target, the predictions will be regarded as - correct ones. Defaults to 1. - thresh (float, optional): If not None, predictions with scores under - this threshold are considered incorrect. Default to None. - - Returns: - float | tuple[float]: If the input ``topk`` is a single integer, - the function will return a single float as accuracy. If - ``topk`` is a tuple containing multiple integers, the - function will return a tuple containing accuracies of - each ``topk`` number. - """ - assert isinstance(topk, (int, tuple)) - if isinstance(topk, int): - topk = (topk, ) - return_single = True - else: - return_single = False - - maxk = max(topk) - if pred.size(0) == 0: - accu = [pred.new_tensor(0.) for i in range(len(topk))] - return accu[0] if return_single else accu - assert pred.ndim == target.ndim + 1 - assert pred.size(0) == target.size(0) - assert maxk <= pred.size(1), \ - f'maxk {maxk} exceeds pred dimension {pred.size(1)}' - pred_value, pred_label = pred.topk(maxk, dim=1) - # transpose to shape (maxk, N, ...) - pred_label = pred_label.transpose(0, 1) - correct = pred_label.eq(target.unsqueeze(0).expand_as(pred_label)) - if thresh is not None: - # Only prediction values larger than thresh are counted as correct - correct = correct & (pred_value > thresh).t() - if ignore_index is not None: - correct = correct[:, target != ignore_index] - res = [] - eps = torch.finfo(torch.float32).eps - for k in topk: - # Avoid causing ZeroDivisionError when all pixels - # of an image are ignored - correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) + eps - if ignore_index is not None: - total_num = target[target != ignore_index].numel() + eps - else: - total_num = target.numel() + eps - res.append(correct_k.mul_(100.0 / total_num)) - return res[0] if return_single else res - - -class Accuracy(nn.Module): - """Accuracy calculation module.""" - - def __init__(self, topk=(1, ), thresh=None, ignore_index=None): - """Module to calculate the accuracy. - - Args: - topk (tuple, optional): The criterion used to calculate the - accuracy. Defaults to (1,). - thresh (float, optional): If not None, predictions with scores - under this threshold are considered incorrect. Default to None. - """ - super().__init__() - self.topk = topk - self.thresh = thresh - self.ignore_index = ignore_index - - def forward(self, pred, target): - """Forward function to calculate accuracy. - - Args: - pred (torch.Tensor): Prediction of models. - target (torch.Tensor): Target for each prediction. - - Returns: - tuple[float]: The accuracies under different topk criterions. - """ - return accuracy(pred, target, self.topk, self.thresh, - self.ignore_index) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/cross_entropy_loss.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/cross_entropy_loss.py deleted file mode 100644 index 623fd58db..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/cross_entropy_loss.py +++ /dev/null @@ -1,296 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weight_reduce_loss - - -def cross_entropy(pred, - label, - weight=None, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=-100, - avg_non_ignore=False): - """cross_entropy. The wrapper function for :func:`F.cross_entropy` - - Args: - pred (torch.Tensor): The prediction with shape (N, 1). - label (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - Default: None. - class_weight (list[float], optional): The weight for each class. - Default: None. - reduction (str, optional): The method used to reduce the loss. - Options are 'none', 'mean' and 'sum'. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Default: None. - ignore_index (int): Specifies a target value that is ignored and - does not contribute to the input gradients. When - ``avg_non_ignore `` is ``True``, and the ``reduction`` is - ``''mean''``, the loss is averaged over non-ignored targets. - Defaults: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - """ - - # class_weight is a manual rescaling weight given to each class. - # If given, has to be a Tensor of size C element-wise losses - loss = F.cross_entropy( - pred, - label, - weight=class_weight, - reduction='none', - ignore_index=ignore_index) - - # apply weights and do the reduction - # average loss over non-ignored elements - # pytorch's official cross_entropy average loss over non-ignored elements - # refer to https://github.com/pytorch/pytorch/blob/56b43f4fec1f76953f15a627694d4bba34588969/torch/nn/functional.py#L2660 # noqa - if (avg_factor is None) and avg_non_ignore and reduction == 'mean': - avg_factor = label.numel() - (label == ignore_index).sum().item() - if weight is not None: - weight = weight.float() - loss = weight_reduce_loss( - loss, weight=weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def _expand_onehot_labels(labels, label_weights, target_shape, ignore_index): - """Expand onehot labels to match the size of prediction.""" - bin_labels = labels.new_zeros(target_shape) - valid_mask = (labels >= 0) & (labels != ignore_index) - inds = torch.nonzero(valid_mask, as_tuple=True) - - if inds[0].numel() > 0: - if labels.dim() == 3: - bin_labels[inds[0], labels[valid_mask], inds[1], inds[2]] = 1 - else: - bin_labels[inds[0], labels[valid_mask]] = 1 - - valid_mask = valid_mask.unsqueeze(1).expand(target_shape).float() - - if label_weights is None: - bin_label_weights = valid_mask - else: - bin_label_weights = label_weights.unsqueeze(1).expand(target_shape) - bin_label_weights = bin_label_weights * valid_mask - - return bin_labels, bin_label_weights, valid_mask - - -def binary_cross_entropy(pred, - label, - weight=None, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=-100, - avg_non_ignore=False, - **kwargs): - """Calculate the binary CrossEntropy loss. - - Args: - pred (torch.Tensor): The prediction with shape (N, 1). - label (torch.Tensor): The learning label of the prediction. - Note: In bce loss, label < 0 is invalid. - weight (torch.Tensor, optional): Sample-wise loss weight. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (int): The label index to be ignored. Default: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - - Returns: - torch.Tensor: The calculated loss - """ - if pred.size(1) == 1: - # For binary class segmentation, the shape of pred is - # [N, 1, H, W] and that of label is [N, H, W]. - # As the ignore_index often set as 255, so the - # binary class label check should mask out - # ignore_index - assert label[label != ignore_index].max() <= 1, \ - 'For pred with shape [N, 1, H, W], its label must have at ' \ - 'most 2 classes' - pred = pred.squeeze() - if pred.dim() != label.dim(): - assert (pred.dim() == 2 and label.dim() == 1) or ( - pred.dim() == 4 and label.dim() == 3), \ - 'Only pred shape [N, C], label shape [N] or pred shape [N, C, ' \ - 'H, W], label shape [N, H, W] are supported' - # `weight` returned from `_expand_onehot_labels` - # has been treated for valid (non-ignore) pixels - label, weight, valid_mask = _expand_onehot_labels( - label, weight, pred.shape, ignore_index) - else: - # should mask out the ignored elements - valid_mask = ((label >= 0) & (label != ignore_index)).float() - if weight is not None: - weight = weight * valid_mask - else: - weight = valid_mask - # average loss over non-ignored and valid elements - if reduction == 'mean' and avg_factor is None and avg_non_ignore: - avg_factor = valid_mask.sum().item() - - loss = F.binary_cross_entropy_with_logits( - pred, label.float(), pos_weight=class_weight, reduction='none') - # do the reduction for the weighted loss - loss = weight_reduce_loss( - loss, weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def mask_cross_entropy(pred, - target, - label, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=None, - **kwargs): - """Calculate the CrossEntropy loss for masks. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. - label (torch.Tensor): ``label`` indicates the class label of the mask' - corresponding object. This will be used to select the mask in the - of the class which the object belongs to when the mask prediction - if not class-agnostic. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (None): Placeholder, to be consistent with other loss. - Default: None. - - Returns: - torch.Tensor: The calculated loss - """ - assert ignore_index is None, 'BCE loss does not support ignore_index' - # TODO: handle these two reserved arguments - assert reduction == 'mean' and avg_factor is None - num_rois = pred.size()[0] - inds = torch.arange(0, num_rois, dtype=torch.long, device=pred.device) - pred_slice = pred[inds, label].squeeze(1) - return F.binary_cross_entropy_with_logits( - pred_slice, target, weight=class_weight, reduction='mean')[None] - - -@LOSSES.register_module() -class CrossEntropyLoss(nn.Module): - """CrossEntropyLoss. - - Args: - use_sigmoid (bool, optional): Whether the prediction uses sigmoid - of softmax. Defaults to False. - use_mask (bool, optional): Whether to use mask cross entropy loss. - Defaults to False. - reduction (str, optional): . Defaults to 'mean'. - Options are "none", "mean" and "sum". - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - """ - - def __init__(self, - use_sigmoid=False, - use_mask=False, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_ce', - avg_non_ignore=False): - super(CrossEntropyLoss, self).__init__() - assert (use_sigmoid is False) or (use_mask is False) - self.use_sigmoid = use_sigmoid - self.use_mask = use_mask - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = get_class_weight(class_weight) - self.avg_non_ignore = avg_non_ignore - if not self.avg_non_ignore and self.reduction == 'mean': - warnings.warn( - 'Default ``avg_non_ignore`` is False, if you would like to ' - 'ignore the certain label and average loss over non-ignore ' - 'labels, which is the same with PyTorch official ' - 'cross_entropy, set ``avg_non_ignore=True``.') - - if self.use_sigmoid: - self.cls_criterion = binary_cross_entropy - elif self.use_mask: - self.cls_criterion = mask_cross_entropy - else: - self.cls_criterion = cross_entropy - self._loss_name = loss_name - - def extra_repr(self): - """Extra repr.""" - s = f'avg_non_ignore={self.avg_non_ignore}' - return s - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - ignore_index=-100, - **kwargs): - """Forward function.""" - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = cls_score.new_tensor(self.class_weight) - else: - class_weight = None - # Note: for BCE loss, label < 0 is invalid. - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - weight, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - avg_non_ignore=self.avg_non_ignore, - ignore_index=ignore_index, - **kwargs) - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/dice_loss.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/dice_loss.py deleted file mode 100644 index 79a3abfc2..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/dice_loss.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/LikeLy-Journey/SegmenTron/blob/master/ -segmentron/solver/loss.py (Apache-2.0 License)""" -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weighted_loss - - -@weighted_loss -def dice_loss(pred, - target, - valid_mask, - smooth=1, - exponent=2, - class_weight=None, - ignore_index=255): - assert pred.shape[0] == target.shape[0] - total_loss = 0 - num_classes = pred.shape[1] - for i in range(num_classes): - if i != ignore_index: - dice_loss = binary_dice_loss( - pred[:, i], - target[..., i], - valid_mask=valid_mask, - smooth=smooth, - exponent=exponent) - if class_weight is not None: - dice_loss *= class_weight[i] - total_loss += dice_loss - return total_loss / num_classes - - -@weighted_loss -def binary_dice_loss(pred, target, valid_mask, smooth=1, exponent=2, **kwards): - assert pred.shape[0] == target.shape[0] - pred = pred.reshape(pred.shape[0], -1) - target = target.reshape(target.shape[0], -1) - valid_mask = valid_mask.reshape(valid_mask.shape[0], -1) - - num = torch.sum(torch.mul(pred, target) * valid_mask, dim=1) * 2 + smooth - den = torch.sum(pred.pow(exponent) + target.pow(exponent), dim=1) + smooth - - return 1 - num / den - - -@LOSSES.register_module() -class DiceLoss(nn.Module): - """DiceLoss. - - This loss is proposed in `V-Net: Fully Convolutional Neural Networks for - Volumetric Medical Image Segmentation `_. - - Args: - smooth (float): A float number to smooth loss, and avoid NaN error. - Default: 1 - exponent (float): An float number to calculate denominator - value: \\sum{x^exponent} + \\sum{y^exponent}. Default: 2. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Default to 1.0. - ignore_index (int | None): The label index to be ignored. Default: 255. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_dice'. - """ - - def __init__(self, - smooth=1, - exponent=2, - reduction='mean', - class_weight=None, - loss_weight=1.0, - ignore_index=255, - loss_name='loss_dice', - **kwards): - super(DiceLoss, self).__init__() - self.smooth = smooth - self.exponent = exponent - self.reduction = reduction - self.class_weight = get_class_weight(class_weight) - self.loss_weight = loss_weight - self.ignore_index = ignore_index - self._loss_name = loss_name - - def forward(self, - pred, - target, - avg_factor=None, - reduction_override=None, - **kwards): - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = pred.new_tensor(self.class_weight) - else: - class_weight = None - - pred = F.softmax(pred, dim=1) - num_classes = pred.shape[1] - one_hot_target = F.one_hot( - torch.clamp(target.long(), 0, num_classes - 1), - num_classes=num_classes) - valid_mask = (target != self.ignore_index).long() - - loss = self.loss_weight * dice_loss( - pred, - one_hot_target, - valid_mask=valid_mask, - reduction=reduction, - avg_factor=avg_factor, - smooth=self.smooth, - exponent=self.exponent, - class_weight=class_weight, - ignore_index=self.ignore_index) - return loss - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/focal_loss.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/focal_loss.py deleted file mode 100644 index af1c711df..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/focal_loss.py +++ /dev/null @@ -1,327 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Modified from https://github.com/open-mmlab/mmdetection -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.ops import sigmoid_focal_loss as _sigmoid_focal_loss - -from ..builder import LOSSES -from .utils import weight_reduce_loss - - -# This method is used when cuda is not available -def py_sigmoid_focal_loss(pred, - target, - one_hot_target=None, - weight=None, - gamma=2.0, - alpha=0.5, - class_weight=None, - valid_mask=None, - reduction='mean', - avg_factor=None): - """PyTorch version of `Focal Loss `_. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the - number of classes - target (torch.Tensor): The learning label of the prediction with - shape (N, C) - one_hot_target (None): Placeholder. It should be None. - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal Loss. - Defaults to 0.5. - class_weight (list[float], optional): Weight of each class. - Defaults to None. - valid_mask (torch.Tensor, optional): A mask uses 1 to mark the valid - samples and uses 0 to mark the ignored samples. Default: None. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - if isinstance(alpha, list): - alpha = pred.new_tensor(alpha) - pred_sigmoid = pred.sigmoid() - target = target.type_as(pred) - one_minus_pt = (1 - pred_sigmoid) * target + pred_sigmoid * (1 - target) - focal_weight = (alpha * target + (1 - alpha) * - (1 - target)) * one_minus_pt.pow(gamma) - - loss = F.binary_cross_entropy_with_logits( - pred, target, reduction='none') * focal_weight - final_weight = torch.ones(1, pred.size(1)).type_as(loss) - if weight is not None: - if weight.shape != loss.shape and weight.size(0) == loss.size(0): - # For most cases, weight is of shape (N, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - assert weight.dim() == loss.dim() - final_weight = final_weight * weight - if class_weight is not None: - final_weight = final_weight * pred.new_tensor(class_weight) - if valid_mask is not None: - final_weight = final_weight * valid_mask - loss = weight_reduce_loss(loss, final_weight, reduction, avg_factor) - return loss - - -def sigmoid_focal_loss(pred, - target, - one_hot_target, - weight=None, - gamma=2.0, - alpha=0.5, - class_weight=None, - valid_mask=None, - reduction='mean', - avg_factor=None): - r"""A warpper of cuda version `Focal Loss - `_. - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. It's shape - should be (N, ) - one_hot_target (torch.Tensor): The learning label with shape (N, C) - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal Loss. - Defaults to 0.5. - class_weight (list[float], optional): Weight of each class. - Defaults to None. - valid_mask (torch.Tensor, optional): A mask uses 1 to mark the valid - samples and uses 0 to mark the ignored samples. Default: None. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - # Function.apply does not accept keyword arguments, so the decorator - # "weighted_loss" is not applicable - final_weight = torch.ones(1, pred.size(1)).type_as(pred) - if isinstance(alpha, list): - # _sigmoid_focal_loss doesn't accept alpha of list type. Therefore, if - # a list is given, we set the input alpha as 0.5. This means setting - # equal weight for foreground class and background class. By - # multiplying the loss by 2, the effect of setting alpha as 0.5 is - # undone. The alpha of type list is used to regulate the loss in the - # post-processing process. - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), - gamma, 0.5, None, 'none') * 2 - alpha = pred.new_tensor(alpha) - final_weight = final_weight * ( - alpha * one_hot_target + (1 - alpha) * (1 - one_hot_target)) - else: - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), - gamma, alpha, None, 'none') - if weight is not None: - if weight.shape != loss.shape and weight.size(0) == loss.size(0): - # For most cases, weight is of shape (N, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - assert weight.dim() == loss.dim() - final_weight = final_weight * weight - if class_weight is not None: - final_weight = final_weight * pred.new_tensor(class_weight) - if valid_mask is not None: - final_weight = final_weight * valid_mask - loss = weight_reduce_loss(loss, final_weight, reduction, avg_factor) - return loss - - -@LOSSES.register_module() -class FocalLoss(nn.Module): - - def __init__(self, - use_sigmoid=True, - gamma=2.0, - alpha=0.5, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_focal'): - """`Focal Loss `_ - Args: - use_sigmoid (bool, optional): Whether to the prediction is - used for sigmoid or softmax. Defaults to True. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal - Loss. Defaults to 0.5. When a list is provided, the length - of the list should be equal to the number of classes. - Please be careful that this parameter is not the - class-wise weight but the weight of a binary classification - problem. This binary classification problem regards the - pixels which belong to one class as the foreground - and the other pixels as the background, each element in - the list is the weight of the corresponding foreground class. - The value of alpha or each element of alpha should be a float - in the interval [0, 1]. If you want to specify the class-wise - weight, please use `class_weight` parameter. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and - "sum". - class_weight (list[float], optional): Weight of each class. - Defaults to None. - loss_weight (float, optional): Weight of loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this - loss item to be included into the backward graph, `loss_` must - be the prefix of the name. Defaults to 'loss_focal'. - """ - super(FocalLoss, self).__init__() - assert use_sigmoid is True, \ - 'AssertionError: Only sigmoid focal loss supported now.' - assert reduction in ('none', 'mean', 'sum'), \ - "AssertionError: reduction should be 'none', 'mean' or " \ - "'sum'" - assert isinstance(alpha, (float, list)), \ - 'AssertionError: alpha should be of type float' - assert isinstance(gamma, float), \ - 'AssertionError: gamma should be of type float' - assert isinstance(loss_weight, float), \ - 'AssertionError: loss_weight should be of type float' - assert isinstance(loss_name, str), \ - 'AssertionError: loss_name should be of type str' - assert isinstance(class_weight, list) or class_weight is None, \ - 'AssertionError: class_weight must be None or of type list' - self.use_sigmoid = use_sigmoid - self.gamma = gamma - self.alpha = alpha - self.reduction = reduction - self.class_weight = class_weight - self.loss_weight = loss_weight - self._loss_name = loss_name - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - ignore_index=255, - **kwargs): - """Forward function. - - Args: - pred (torch.Tensor): The prediction with shape - (N, C) where C = number of classes, or - (N, C, d_1, d_2, ..., d_K) with K≥1 in the - case of K-dimensional loss. - target (torch.Tensor): The ground truth. If containing class - indices, shape (N) where each value is 0≤targets[i]≤C−1, - or (N, d_1, d_2, ..., d_K) with K≥1 in the case of - K-dimensional loss. If containing class probabilities, - same shape as the input. - weight (torch.Tensor, optional): The weight of loss for each - prediction. Defaults to None. - avg_factor (int, optional): Average factor that is used to - average the loss. Defaults to None. - reduction_override (str, optional): The reduction method used - to override the original reduction method of the loss. - Options are "none", "mean" and "sum". - ignore_index (int, optional): The label index to be ignored. - Default: 255 - Returns: - torch.Tensor: The calculated loss - """ - assert isinstance(ignore_index, int), \ - 'ignore_index must be of type int' - assert reduction_override in (None, 'none', 'mean', 'sum'), \ - "AssertionError: reduction should be 'none', 'mean' or " \ - "'sum'" - assert pred.shape == target.shape or \ - (pred.size(0) == target.size(0) and - pred.shape[2:] == target.shape[1:]), \ - "The shape of pred doesn't match the shape of target" - - original_shape = pred.shape - - # [B, C, d_1, d_2, ..., d_k] -> [C, B, d_1, d_2, ..., d_k] - pred = pred.transpose(0, 1) - # [C, B, d_1, d_2, ..., d_k] -> [C, N] - pred = pred.reshape(pred.size(0), -1) - # [C, N] -> [N, C] - pred = pred.transpose(0, 1).contiguous() - - if original_shape == target.shape: - # target with shape [B, C, d_1, d_2, ...] - # transform it's shape into [N, C] - # [B, C, d_1, d_2, ...] -> [C, B, d_1, d_2, ..., d_k] - target = target.transpose(0, 1) - # [C, B, d_1, d_2, ..., d_k] -> [C, N] - target = target.reshape(target.size(0), -1) - # [C, N] -> [N, C] - target = target.transpose(0, 1).contiguous() - else: - # target with shape [B, d_1, d_2, ...] - # transform it's shape into [N, ] - target = target.view(-1).contiguous() - valid_mask = (target != ignore_index).view(-1, 1) - # avoid raising error when using F.one_hot() - target = torch.where(target == ignore_index, target.new_tensor(0), - target) - - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.use_sigmoid: - num_classes = pred.size(1) - if torch.cuda.is_available() and pred.is_cuda: - if target.dim() == 1: - one_hot_target = F.one_hot(target, num_classes=num_classes) - else: - one_hot_target = target - target = target.argmax(dim=1) - valid_mask = (target != ignore_index).view(-1, 1) - calculate_loss_func = sigmoid_focal_loss - else: - one_hot_target = None - if target.dim() == 1: - target = F.one_hot(target, num_classes=num_classes) - else: - valid_mask = (target.argmax(dim=1) != ignore_index).view( - -1, 1) - calculate_loss_func = py_sigmoid_focal_loss - - loss_cls = self.loss_weight * calculate_loss_func( - pred, - target, - one_hot_target, - weight, - gamma=self.gamma, - alpha=self.alpha, - class_weight=self.class_weight, - valid_mask=valid_mask, - reduction=reduction, - avg_factor=avg_factor) - - if reduction == 'none': - # [N, C] -> [C, N] - loss_cls = loss_cls.transpose(0, 1) - # [C, N] -> [C, B, d1, d2, ...] - # original_shape: [B, C, d1, d2, ...] - loss_cls = loss_cls.reshape(original_shape[1], - original_shape[0], - *original_shape[2:]) - # [C, B, d1, d2, ...] -> [B, C, d1, d2, ...] - loss_cls = loss_cls.transpose(0, 1).contiguous() - else: - raise NotImplementedError - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/lovasz_loss.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/lovasz_loss.py deleted file mode 100644 index 2bb0fad39..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/lovasz_loss.py +++ /dev/null @@ -1,323 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/bermanmaxim/LovaszSoftmax/blob/master/pytor -ch/lovasz_losses.py Lovasz-Softmax and Jaccard hinge loss in PyTorch Maxim -Berman 2018 ESAT-PSI KU Leuven (MIT License)""" - -import mmcv -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weight_reduce_loss - - -def lovasz_grad(gt_sorted): - """Computes gradient of the Lovasz extension w.r.t sorted errors. - - See Alg. 1 in paper. - """ - p = len(gt_sorted) - gts = gt_sorted.sum() - intersection = gts - gt_sorted.float().cumsum(0) - union = gts + (1 - gt_sorted).float().cumsum(0) - jaccard = 1. - intersection / union - if p > 1: # cover 1-pixel case - jaccard[1:p] = jaccard[1:p] - jaccard[0:-1] - return jaccard - - -def flatten_binary_logits(logits, labels, ignore_index=None): - """Flattens predictions in the batch (binary case) Remove labels equal to - 'ignore_index'.""" - logits = logits.view(-1) - labels = labels.view(-1) - if ignore_index is None: - return logits, labels - valid = (labels != ignore_index) - vlogits = logits[valid] - vlabels = labels[valid] - return vlogits, vlabels - - -def flatten_probs(probs, labels, ignore_index=None): - """Flattens predictions in the batch.""" - if probs.dim() == 3: - # assumes output of a sigmoid layer - B, H, W = probs.size() - probs = probs.view(B, 1, H, W) - B, C, H, W = probs.size() - probs = probs.permute(0, 2, 3, 1).contiguous().view(-1, C) # B*H*W, C=P,C - labels = labels.view(-1) - if ignore_index is None: - return probs, labels - valid = (labels != ignore_index) - vprobs = probs[valid.nonzero().squeeze()] - vlabels = labels[valid] - return vprobs, vlabels - - -def lovasz_hinge_flat(logits, labels): - """Binary Lovasz hinge loss. - - Args: - logits (torch.Tensor): [P], logits at each prediction - (between -infty and +infty). - labels (torch.Tensor): [P], binary ground truth labels (0 or 1). - - Returns: - torch.Tensor: The calculated loss. - """ - if len(labels) == 0: - # only void pixels, the gradients should be 0 - return logits.sum() * 0. - signs = 2. * labels.float() - 1. - errors = (1. - logits * signs) - errors_sorted, perm = torch.sort(errors, dim=0, descending=True) - perm = perm.data - gt_sorted = labels[perm] - grad = lovasz_grad(gt_sorted) - loss = torch.dot(F.relu(errors_sorted), grad) - return loss - - -def lovasz_hinge(logits, - labels, - classes='present', - per_image=False, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=255): - """Binary Lovasz hinge loss. - - Args: - logits (torch.Tensor): [B, H, W], logits at each pixel - (between -infty and +infty). - labels (torch.Tensor): [B, H, W], binary ground truth masks (0 or 1). - classes (str | list[int], optional): Placeholder, to be consistent with - other loss. Default: None. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - class_weight (list[float], optional): Placeholder, to be consistent - with other loss. Default: None. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. This parameter only works when per_image is True. - Default: None. - ignore_index (int | None): The label index to be ignored. Default: 255. - - Returns: - torch.Tensor: The calculated loss. - """ - if per_image: - loss = [ - lovasz_hinge_flat(*flatten_binary_logits( - logit.unsqueeze(0), label.unsqueeze(0), ignore_index)) - for logit, label in zip(logits, labels) - ] - loss = weight_reduce_loss( - torch.stack(loss), None, reduction, avg_factor) - else: - loss = lovasz_hinge_flat( - *flatten_binary_logits(logits, labels, ignore_index)) - return loss - - -def lovasz_softmax_flat(probs, labels, classes='present', class_weight=None): - """Multi-class Lovasz-Softmax loss. - - Args: - probs (torch.Tensor): [P, C], class probabilities at each prediction - (between 0 and 1). - labels (torch.Tensor): [P], ground truth labels (between 0 and C - 1). - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - class_weight (list[float], optional): The weight for each class. - Default: None. - - Returns: - torch.Tensor: The calculated loss. - """ - if probs.numel() == 0: - # only void pixels, the gradients should be 0 - return probs * 0. - C = probs.size(1) - losses = [] - class_to_sum = list(range(C)) if classes in ['all', 'present'] else classes - for c in class_to_sum: - fg = (labels == c).float() # foreground for class c - if (classes == 'present' and fg.sum() == 0): - continue - if C == 1: - if len(classes) > 1: - raise ValueError('Sigmoid output possible only with 1 class') - class_pred = probs[:, 0] - else: - class_pred = probs[:, c] - errors = (fg - class_pred).abs() - errors_sorted, perm = torch.sort(errors, 0, descending=True) - perm = perm.data - fg_sorted = fg[perm] - loss = torch.dot(errors_sorted, lovasz_grad(fg_sorted)) - if class_weight is not None: - loss *= class_weight[c] - losses.append(loss) - return torch.stack(losses).mean() - - -def lovasz_softmax(probs, - labels, - classes='present', - per_image=False, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=255): - """Multi-class Lovasz-Softmax loss. - - Args: - probs (torch.Tensor): [B, C, H, W], class probabilities at each - prediction (between 0 and 1). - labels (torch.Tensor): [B, H, W], ground truth labels (between 0 and - C - 1). - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - class_weight (list[float], optional): The weight for each class. - Default: None. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. This parameter only works when per_image is True. - Default: None. - ignore_index (int | None): The label index to be ignored. Default: 255. - - Returns: - torch.Tensor: The calculated loss. - """ - - if per_image: - loss = [ - lovasz_softmax_flat( - *flatten_probs( - prob.unsqueeze(0), label.unsqueeze(0), ignore_index), - classes=classes, - class_weight=class_weight) - for prob, label in zip(probs, labels) - ] - loss = weight_reduce_loss( - torch.stack(loss), None, reduction, avg_factor) - else: - loss = lovasz_softmax_flat( - *flatten_probs(probs, labels, ignore_index), - classes=classes, - class_weight=class_weight) - return loss - - -@LOSSES.register_module() -class LovaszLoss(nn.Module): - """LovaszLoss. - - This loss is proposed in `The Lovasz-Softmax loss: A tractable surrogate - for the optimization of the intersection-over-union measure in neural - networks `_. - - Args: - loss_type (str, optional): Binary or multi-class loss. - Default: 'multi_class'. Options are "binary" and "multi_class". - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_lovasz'. - """ - - def __init__(self, - loss_type='multi_class', - classes='present', - per_image=False, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_lovasz'): - super(LovaszLoss, self).__init__() - assert loss_type in ('binary', 'multi_class'), "loss_type should be \ - 'binary' or 'multi_class'." - - if loss_type == 'binary': - self.cls_criterion = lovasz_hinge - else: - self.cls_criterion = lovasz_softmax - assert classes in ('all', 'present') or mmcv.is_list_of(classes, int) - if not per_image: - assert reduction == 'none', "reduction should be 'none' when \ - per_image is False." - - self.classes = classes - self.per_image = per_image - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = get_class_weight(class_weight) - self._loss_name = loss_name - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - """Forward function.""" - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = cls_score.new_tensor(self.class_weight) - else: - class_weight = None - - # if multi-class loss, transform logits to probs - if self.cls_criterion == lovasz_softmax: - cls_score = F.softmax(cls_score, dim=1) - - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - self.classes, - self.per_image, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/utils.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/utils.py deleted file mode 100644 index 621f57c74..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/losses/utils.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import mmcv -import numpy as np -import torch -import torch.nn.functional as F - - -def get_class_weight(class_weight): - """Get class weight for loss function. - - Args: - class_weight (list[float] | str | None): If class_weight is a str, - take it as a file name and read from it. - """ - if isinstance(class_weight, str): - # take it as a file path - if class_weight.endswith('.npy'): - class_weight = np.load(class_weight) - else: - # pkl, json or yaml - class_weight = mmcv.load(class_weight) - - return class_weight - - -def reduce_loss(loss, reduction): - """Reduce loss as specified. - - Args: - loss (Tensor): Elementwise loss tensor. - reduction (str): Options are "none", "mean" and "sum". - - Return: - Tensor: Reduced loss tensor. - """ - reduction_enum = F._Reduction.get_enum(reduction) - # none: 0, elementwise_mean:1, sum: 2 - if reduction_enum == 0: - return loss - elif reduction_enum == 1: - return loss.mean() - elif reduction_enum == 2: - return loss.sum() - - -def weight_reduce_loss(loss, weight=None, reduction='mean', avg_factor=None): - """Apply element-wise weight and reduce loss. - - Args: - loss (Tensor): Element-wise loss. - weight (Tensor): Element-wise weights. - reduction (str): Same as built-in losses of PyTorch. - avg_factor (float): Average factor when computing the mean of losses. - - Returns: - Tensor: Processed loss values. - """ - # if weight is specified, apply element-wise weight - if weight is not None: - assert weight.dim() == loss.dim() - if weight.dim() > 1: - assert weight.size(1) == 1 or weight.size(1) == loss.size(1) - loss = loss * weight - - # if avg_factor is not specified, just reduce the loss - if avg_factor is None: - loss = reduce_loss(loss, reduction) - else: - # if reduction is mean, then average the loss by avg_factor - if reduction == 'mean': - # Avoid causing ZeroDivisionError when avg_factor is 0.0, - # i.e., all labels of an image belong to ignore index. - eps = torch.finfo(torch.float32).eps - loss = loss.sum() / (avg_factor + eps) - # if reduction is 'none', then do nothing, otherwise raise an error - elif reduction != 'none': - raise ValueError('avg_factor can not be used with reduction="sum"') - return loss - - -def weighted_loss(loss_func): - """Create a weighted version of a given loss function. - - To use this decorator, the loss function must have the signature like - `loss_func(pred, target, **kwargs)`. The function only needs to compute - element-wise loss without any reduction. This decorator will add weight - and reduction arguments to the function. The decorated function will have - the signature like `loss_func(pred, target, weight=None, reduction='mean', - avg_factor=None, **kwargs)`. - - :Example: - - >>> import torch - >>> @weighted_loss - >>> def l1_loss(pred, target): - >>> return (pred - target).abs() - - >>> pred = torch.Tensor([0, 2, 3]) - >>> target = torch.Tensor([1, 1, 1]) - >>> weight = torch.Tensor([1, 0, 1]) - - >>> l1_loss(pred, target) - tensor(1.3333) - >>> l1_loss(pred, target, weight) - tensor(1.) - >>> l1_loss(pred, target, reduction='none') - tensor([1., 1., 2.]) - >>> l1_loss(pred, target, weight, avg_factor=2) - tensor(1.5000) - """ - - @functools.wraps(loss_func) - def wrapper(pred, - target, - weight=None, - reduction='mean', - avg_factor=None, - **kwargs): - # get element-wise loss - loss = loss_func(pred, target, **kwargs) - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - return wrapper diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/__init__.py deleted file mode 100644 index ff03186a9..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .featurepyramid import Feature2Pyramid -from .fpn import FPN -from .ic_neck import ICNeck -from .jpu import JPU -from .mla_neck import MLANeck -from .multilevel_neck import MultiLevelNeck - -__all__ = [ - 'FPN', 'MultiLevelNeck', 'MLANeck', 'ICNeck', 'JPU', 'Feature2Pyramid' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/featurepyramid.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/featurepyramid.py deleted file mode 100644 index 82a00ceb1..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/featurepyramid.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import build_norm_layer - -from ..builder import NECKS - - -@NECKS.register_module() -class Feature2Pyramid(nn.Module): - """Feature2Pyramid. - - A neck structure connect ViT backbone and decoder_heads. - - Args: - embed_dims (int): Embedding dimension. - rescales (list[float]): Different sampling multiples were - used to obtain pyramid features. Default: [4, 2, 1, 0.5]. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='SyncBN', requires_grad=True). - """ - - def __init__(self, - embed_dim, - rescales=[4, 2, 1, 0.5], - norm_cfg=dict(type='SyncBN', requires_grad=True)): - super(Feature2Pyramid, self).__init__() - self.rescales = rescales - self.upsample_4x = None - for k in self.rescales: - if k == 4: - self.upsample_4x = nn.Sequential( - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2), - build_norm_layer(norm_cfg, embed_dim)[1], - nn.GELU(), - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2), - ) - elif k == 2: - self.upsample_2x = nn.Sequential( - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2)) - elif k == 1: - self.identity = nn.Identity() - elif k == 0.5: - self.downsample_2x = nn.MaxPool2d(kernel_size=2, stride=2) - elif k == 0.25: - self.downsample_4x = nn.MaxPool2d(kernel_size=4, stride=4) - else: - raise KeyError(f'invalid {k} for feature2pyramid') - - def forward(self, inputs): - assert len(inputs) == len(self.rescales) - outputs = [] - if self.upsample_4x is not None: - ops = [ - self.upsample_4x, self.upsample_2x, self.identity, - self.downsample_2x - ] - else: - ops = [ - self.upsample_2x, self.identity, self.downsample_2x, - self.downsample_4x - ] - for i in range(len(inputs)): - outputs.append(ops[i](inputs[i])) - return tuple(outputs) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/fpn.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/fpn.py deleted file mode 100644 index 6997de9d4..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/fpn.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule, auto_fp16 - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class FPN(BaseModule): - """Feature Pyramid Network. - - This neck is the implementation of `Feature Pyramid Networks for Object - Detection `_. - - Args: - in_channels (list[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - num_outs (int): Number of output scales. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - add_extra_convs (bool | str): If bool, it decides whether to add conv - layers on top of the original feature maps. Default to False. - If True, its actual mode is specified by `extra_convs_on_inputs`. - If str, it specifies the source feature map of the extra convs. - Only the following options are allowed - - - 'on_input': Last feat map of neck inputs (i.e. backbone feature). - - 'on_lateral': Last feature map after lateral convs. - - 'on_output': The last output feature map after fpn convs. - extra_convs_on_inputs (bool, deprecated): Whether to apply extra convs - on the original feature from the backbone. If True, - it is equivalent to `add_extra_convs='on_input'`. If False, it is - equivalent to set `add_extra_convs='on_output'`. Default to True. - relu_before_extra_convs (bool): Whether to apply relu before the extra - conv. Default: False. - no_norm_on_lateral (bool): Whether to apply norm on lateral. - Default: False. - conv_cfg (dict): Config dict for convolution layer. Default: None. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - upsample_cfg (dict): Config dict for interpolate layer. - Default: dict(mode='nearest'). - init_cfg (dict or list[dict], optional): Initialization config dict. - - Example: - >>> import torch - >>> in_channels = [2, 3, 5, 7] - >>> scales = [340, 170, 84, 43] - >>> inputs = [torch.rand(1, c, s, s) - ... for c, s in zip(in_channels, scales)] - >>> self = FPN(in_channels, 11, len(in_channels)).eval() - >>> outputs = self.forward(inputs) - >>> for i in range(len(outputs)): - ... print(f'outputs[{i}].shape = {outputs[i].shape}') - outputs[0].shape = torch.Size([1, 11, 340, 340]) - outputs[1].shape = torch.Size([1, 11, 170, 170]) - outputs[2].shape = torch.Size([1, 11, 84, 84]) - outputs[3].shape = torch.Size([1, 11, 43, 43]) - """ - - def __init__(self, - in_channels, - out_channels, - num_outs, - start_level=0, - end_level=-1, - add_extra_convs=False, - extra_convs_on_inputs=False, - relu_before_extra_convs=False, - no_norm_on_lateral=False, - conv_cfg=None, - norm_cfg=None, - act_cfg=None, - upsample_cfg=dict(mode='nearest'), - init_cfg=dict( - type='Xavier', layer='Conv2d', distribution='uniform')): - super(FPN, self).__init__(init_cfg) - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.num_ins = len(in_channels) - self.num_outs = num_outs - self.relu_before_extra_convs = relu_before_extra_convs - self.no_norm_on_lateral = no_norm_on_lateral - self.fp16_enabled = False - self.upsample_cfg = upsample_cfg.copy() - - if end_level == -1: - self.backbone_end_level = self.num_ins - assert num_outs >= self.num_ins - start_level - else: - # if end_level < inputs, no extra level is allowed - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - assert num_outs == end_level - start_level - self.start_level = start_level - self.end_level = end_level - self.add_extra_convs = add_extra_convs - assert isinstance(add_extra_convs, (str, bool)) - if isinstance(add_extra_convs, str): - # Extra_convs_source choices: 'on_input', 'on_lateral', 'on_output' - assert add_extra_convs in ('on_input', 'on_lateral', 'on_output') - elif add_extra_convs: # True - if extra_convs_on_inputs: - # For compatibility with previous release - # TODO: deprecate `extra_convs_on_inputs` - self.add_extra_convs = 'on_input' - else: - self.add_extra_convs = 'on_output' - - self.lateral_convs = nn.ModuleList() - self.fpn_convs = nn.ModuleList() - - for i in range(self.start_level, self.backbone_end_level): - l_conv = ConvModule( - in_channels[i], - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg if not self.no_norm_on_lateral else None, - act_cfg=act_cfg, - inplace=False) - fpn_conv = ConvModule( - out_channels, - out_channels, - 3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - - self.lateral_convs.append(l_conv) - self.fpn_convs.append(fpn_conv) - - # add extra conv layers (e.g., RetinaNet) - extra_levels = num_outs - self.backbone_end_level + self.start_level - if self.add_extra_convs and extra_levels >= 1: - for i in range(extra_levels): - if i == 0 and self.add_extra_convs == 'on_input': - in_channels = self.in_channels[self.backbone_end_level - 1] - else: - in_channels = out_channels - extra_fpn_conv = ConvModule( - in_channels, - out_channels, - 3, - stride=2, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - self.fpn_convs.append(extra_fpn_conv) - - @auto_fp16() - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # build laterals - laterals = [ - lateral_conv(inputs[i + self.start_level]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - - # build top-down path - used_backbone_levels = len(laterals) - for i in range(used_backbone_levels - 1, 0, -1): - # In some cases, fixing `scale factor` (e.g. 2) is preferred, but - # it cannot co-exist with `size` in `F.interpolate`. - if 'scale_factor' in self.upsample_cfg: - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], **self.upsample_cfg) - else: - prev_shape = laterals[i - 1].shape[2:] - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], size=prev_shape, **self.upsample_cfg) - - # build outputs - # part 1: from original levels - outs = [ - self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels) - ] - # part 2: add extra levels - if self.num_outs > len(outs): - # use max pool to get more levels on top of outputs - # (e.g., Faster R-CNN, Mask R-CNN) - if not self.add_extra_convs: - for i in range(self.num_outs - used_backbone_levels): - outs.append(F.max_pool2d(outs[-1], 1, stride=2)) - # add conv layers on top of original feature maps (RetinaNet) - else: - if self.add_extra_convs == 'on_input': - extra_source = inputs[self.backbone_end_level - 1] - elif self.add_extra_convs == 'on_lateral': - extra_source = laterals[-1] - elif self.add_extra_convs == 'on_output': - extra_source = outs[-1] - else: - raise NotImplementedError - outs.append(self.fpn_convs[used_backbone_levels](extra_source)) - for i in range(used_backbone_levels + 1, self.num_outs): - if self.relu_before_extra_convs: - outs.append(self.fpn_convs[i](F.relu(outs[-1]))) - else: - outs.append(self.fpn_convs[i](outs[-1])) - return tuple(outs) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/ic_neck.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/ic_neck.py deleted file mode 100644 index a5d81cef8..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/ic_neck.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -class CascadeFeatureFusion(BaseModule): - """Cascade Feature Fusion Unit in ICNet. - - Args: - low_channels (int): The number of input channels for - low resolution feature map. - high_channels (int): The number of input channels for - high resolution feature map. - out_channels (int): The number of output channels. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Returns: - x (Tensor): The output tensor of shape (N, out_channels, H, W). - x_low (Tensor): The output tensor of shape (N, out_channels, H, W) - for Cascade Label Guidance in auxiliary heads. - """ - - def __init__(self, - low_channels, - high_channels, - out_channels, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(CascadeFeatureFusion, self).__init__(init_cfg=init_cfg) - self.align_corners = align_corners - self.conv_low = ConvModule( - low_channels, - out_channels, - 3, - padding=2, - dilation=2, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.conv_high = ConvModule( - high_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, x_low, x_high): - x_low = resize( - x_low, - size=x_high.size()[2:], - mode='bilinear', - align_corners=self.align_corners) - # Note: Different from original paper, `x_low` is underwent - # `self.conv_low` rather than another 1x1 conv classifier - # before being used for auxiliary head. - x_low = self.conv_low(x_low) - x_high = self.conv_high(x_high) - x = x_low + x_high - x = F.relu(x, inplace=True) - return x, x_low - - -@NECKS.register_module() -class ICNeck(BaseModule): - """ICNet for Real-Time Semantic Segmentation on High-Resolution Images. - - This head is the implementation of `ICHead - `_. - - Args: - in_channels (int): The number of input image channels. Default: 3. - out_channels (int): The numbers of output feature channels. - Default: 128. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(64, 256, 256), - out_channels=128, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(ICNeck, self).__init__(init_cfg=init_cfg) - assert len(in_channels) == 3, 'Length of input channels \ - must be 3!' - - self.in_channels = in_channels - self.out_channels = out_channels - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.align_corners = align_corners - self.cff_24 = CascadeFeatureFusion( - self.in_channels[2], - self.in_channels[1], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - self.cff_12 = CascadeFeatureFusion( - self.out_channels, - self.in_channels[0], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - def forward(self, inputs): - assert len(inputs) == 3, 'Length of input feature \ - maps must be 3!' - - x_sub1, x_sub2, x_sub4 = inputs - x_cff_24, x_24 = self.cff_24(x_sub4, x_sub2) - x_cff_12, x_12 = self.cff_12(x_cff_24, x_sub1) - # Note: `x_cff_12` is used for decode_head, - # `x_24` and `x_12` are used for auxiliary head. - return x_24, x_12, x_cff_12 diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/jpu.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/jpu.py deleted file mode 100644 index 3cc6b9f42..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/jpu.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, DepthwiseSeparableConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class JPU(BaseModule): - """FastFCN: Rethinking Dilated Convolution in the Backbone - for Semantic Segmentation. - - This Joint Pyramid Upsampling (JPU) neck is the implementation of - `FastFCN `_. - - Args: - in_channels (Tuple[int], optional): The number of input channels - for each convolution operations before upsampling. - Default: (512, 1024, 2048). - mid_channels (int): The number of output channels of JPU. - Default: 512. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - dilations (tuple[int]): Dilation rate of each Depthwise - Separable ConvModule. Default: (1, 2, 4, 8). - align_corners (bool, optional): The align_corners argument of - resize operation. Default: False. - conv_cfg (dict | None): Config of conv layers. - Default: None. - norm_cfg (dict | None): Config of norm layers. - Default: dict(type='BN'). - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU'). - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(512, 1024, 2048), - mid_channels=512, - start_level=0, - end_level=-1, - dilations=(1, 2, 4, 8), - align_corners=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(JPU, self).__init__(init_cfg=init_cfg) - assert isinstance(in_channels, tuple) - assert isinstance(dilations, tuple) - self.in_channels = in_channels - self.mid_channels = mid_channels - self.start_level = start_level - self.num_ins = len(in_channels) - if end_level == -1: - self.backbone_end_level = self.num_ins - else: - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - - self.dilations = dilations - self.align_corners = align_corners - - self.conv_layers = nn.ModuleList() - self.dilation_layers = nn.ModuleList() - for i in range(self.start_level, self.backbone_end_level): - conv_layer = nn.Sequential( - ConvModule( - self.in_channels[i], - self.mid_channels, - kernel_size=3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.conv_layers.append(conv_layer) - for i in range(len(dilations)): - dilation_layer = nn.Sequential( - DepthwiseSeparableConvModule( - in_channels=(self.backbone_end_level - self.start_level) * - self.mid_channels, - out_channels=self.mid_channels, - kernel_size=3, - stride=1, - padding=dilations[i], - dilation=dilations[i], - dw_norm_cfg=norm_cfg, - dw_act_cfg=None, - pw_norm_cfg=norm_cfg, - pw_act_cfg=act_cfg)) - self.dilation_layers.append(dilation_layer) - - def forward(self, inputs): - """Forward function.""" - assert len(inputs) == len(self.in_channels), 'Length of inputs must \ - be the same with self.in_channels!' - - feats = [ - self.conv_layers[i - self.start_level](inputs[i]) - for i in range(self.start_level, self.backbone_end_level) - ] - - h, w = feats[0].shape[2:] - for i in range(1, len(feats)): - feats[i] = resize( - feats[i], - size=(h, w), - mode='bilinear', - align_corners=self.align_corners) - - feat = torch.cat(feats, dim=1) - concat_feat = torch.cat([ - self.dilation_layers[i](feat) for i in range(len(self.dilations)) - ], - dim=1) - - outs = [] - - # Default: outs[2] is the output of JPU for decoder head, outs[1] is - # the feature map from backbone for auxiliary head. Additionally, - # outs[0] can also be used for auxiliary head. - for i in range(self.start_level, self.backbone_end_level - 1): - outs.append(inputs[i]) - outs.append(concat_feat) - return tuple(outs) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/mla_neck.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/mla_neck.py deleted file mode 100644 index 1513e296d..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/mla_neck.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, build_norm_layer - -from ..builder import NECKS - - -class MLAModule(nn.Module): - - def __init__(self, - in_channels=[1024, 1024, 1024, 1024], - out_channels=256, - norm_cfg=None, - act_cfg=None): - super(MLAModule, self).__init__() - self.channel_proj = nn.ModuleList() - for i in range(len(in_channels)): - self.channel_proj.append( - ConvModule( - in_channels=in_channels[i], - out_channels=out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.feat_extract = nn.ModuleList() - for i in range(len(in_channels)): - self.feat_extract.append( - ConvModule( - in_channels=out_channels, - out_channels=out_channels, - kernel_size=3, - padding=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, inputs): - - # feat_list -> [p2, p3, p4, p5] - feat_list = [] - for x, conv in zip(inputs, self.channel_proj): - feat_list.append(conv(x)) - - # feat_list -> [p5, p4, p3, p2] - # mid_list -> [m5, m4, m3, m2] - feat_list = feat_list[::-1] - mid_list = [] - for feat in feat_list: - if len(mid_list) == 0: - mid_list.append(feat) - else: - mid_list.append(mid_list[-1] + feat) - - # mid_list -> [m5, m4, m3, m2] - # out_list -> [o2, o3, o4, o5] - out_list = [] - for mid, conv in zip(mid_list, self.feat_extract): - out_list.append(conv(mid)) - - return tuple(out_list) - - -@NECKS.register_module() -class MLANeck(nn.Module): - """Multi-level Feature Aggregation. - - This neck is `The Multi-level Feature Aggregation construction of - SETR `_. - - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - norm_layer (dict): Config dict for input normalization. - Default: norm_layer=dict(type='LN', eps=1e-6, requires_grad=True). - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - norm_layer=dict(type='LN', eps=1e-6, requires_grad=True), - norm_cfg=None, - act_cfg=None): - super(MLANeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - - # In order to build general vision transformer backbone, we have to - # move MLA to neck. - self.norm = nn.ModuleList([ - build_norm_layer(norm_layer, in_channels[i])[1] - for i in range(len(in_channels)) - ]) - - self.mla = MLAModule( - in_channels=in_channels, - out_channels=out_channels, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # Convert from nchw to nlc - outs = [] - for i in range(len(inputs)): - x = inputs[i] - n, c, h, w = x.shape - x = x.reshape(n, c, h * w).transpose(2, 1).contiguous() - x = self.norm[i](x) - x = x.transpose(1, 2).reshape(n, c, h, w).contiguous() - outs.append(x) - - outs = self.mla(outs) - return tuple(outs) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/multilevel_neck.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/multilevel_neck.py deleted file mode 100644 index 5151f8762..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/necks/multilevel_neck.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, xavier_init - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class MultiLevelNeck(nn.Module): - """MultiLevelNeck. - - A neck structure connect vit backbone and decoder_heads. - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - scales (List[float]): Scale factors for each input feature map. - Default: [0.5, 1, 2, 4] - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - scales=[0.5, 1, 2, 4], - norm_cfg=None, - act_cfg=None): - super(MultiLevelNeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.scales = scales - self.num_outs = len(scales) - self.lateral_convs = nn.ModuleList() - self.convs = nn.ModuleList() - for in_channel in in_channels: - self.lateral_convs.append( - ConvModule( - in_channel, - out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - for _ in range(self.num_outs): - self.convs.append( - ConvModule( - out_channels, - out_channels, - kernel_size=3, - padding=1, - stride=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - # default init_weights for conv(msra) and norm in ConvModule - def init_weights(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - xavier_init(m, distribution='uniform') - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - inputs = [ - lateral_conv(inputs[i]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - # for len(inputs) not equal to self.num_outs - if len(inputs) == 1: - inputs = [inputs[0] for _ in range(self.num_outs)] - outs = [] - for i in range(self.num_outs): - x_resize = resize( - inputs[i], scale_factor=self.scales[i], mode='bilinear') - outs.append(self.convs[i](x_resize)) - return tuple(outs) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/__init__.py deleted file mode 100644 index 387c858bd..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseSegmentor -from .cascade_encoder_decoder import CascadeEncoderDecoder -from .encoder_decoder import EncoderDecoder - -__all__ = ['BaseSegmentor', 'EncoderDecoder', 'CascadeEncoderDecoder'] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/base.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/base.py deleted file mode 100644 index 76dc8f075..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/base.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from abc import ABCMeta, abstractmethod -from collections import OrderedDict - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import BaseModule, auto_fp16 - - -class BaseSegmentor(BaseModule, metaclass=ABCMeta): - """Base class for segmentors.""" - - def __init__(self, init_cfg=None): - super(BaseSegmentor, self).__init__(init_cfg) - self.fp16_enabled = False - - @property - def with_neck(self): - """bool: whether the segmentor has neck""" - return hasattr(self, 'neck') and self.neck is not None - - @property - def with_auxiliary_head(self): - """bool: whether the segmentor has auxiliary head""" - return hasattr(self, - 'auxiliary_head') and self.auxiliary_head is not None - - @property - def with_decode_head(self): - """bool: whether the segmentor has decode head""" - return hasattr(self, 'decode_head') and self.decode_head is not None - - @abstractmethod - def extract_feat(self, imgs): - """Placeholder for extract features from images.""" - pass - - @abstractmethod - def encode_decode(self, img, img_metas): - """Placeholder for encode images with backbone and decode into a - semantic segmentation map of the same size as input.""" - pass - - @abstractmethod - def forward_train(self, imgs, img_metas, **kwargs): - """Placeholder for Forward function for training.""" - pass - - @abstractmethod - def simple_test(self, img, img_meta, **kwargs): - """Placeholder for single image test.""" - pass - - @abstractmethod - def aug_test(self, imgs, img_metas, **kwargs): - """Placeholder for augmentation test.""" - pass - - def forward_test(self, imgs, img_metas, **kwargs): - """ - Args: - imgs (List[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains all images in the batch. - img_metas (List[List[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. - """ - for var, name in [(imgs, 'imgs'), (img_metas, 'img_metas')]: - if not isinstance(var, list): - raise TypeError(f'{name} must be a list, but got ' - f'{type(var)}') - - num_augs = len(imgs) - if num_augs != len(img_metas): - raise ValueError(f'num of augmentations ({len(imgs)}) != ' - f'num of image meta ({len(img_metas)})') - # all images in the same aug batch all of the same ori_shape and pad - # shape - for img_meta in img_metas: - ori_shapes = [_['ori_shape'] for _ in img_meta] - assert all(shape == ori_shapes[0] for shape in ori_shapes) - img_shapes = [_['img_shape'] for _ in img_meta] - assert all(shape == img_shapes[0] for shape in img_shapes) - pad_shapes = [_['pad_shape'] for _ in img_meta] - assert all(shape == pad_shapes[0] for shape in pad_shapes) - - if num_augs == 1: - return self.simple_test(imgs[0], img_metas[0], **kwargs) - else: - return self.aug_test(imgs, img_metas, **kwargs) - - @auto_fp16(apply_to=('img', )) - def forward(self, img, img_metas, return_loss=True, **kwargs): - """Calls either :func:`forward_train` or :func:`forward_test` depending - on whether ``return_loss`` is ``True``. - - Note this setting will change the expected inputs. When - ``return_loss=True``, img and img_meta are single-nested (i.e. Tensor - and List[dict]), and when ``resturn_loss=False``, img and img_meta - should be double nested (i.e. List[Tensor], List[List[dict]]), with - the outer list indicating test time augmentations. - """ - if return_loss: - return self.forward_train(img, img_metas, **kwargs) - else: - return self.forward_test(img, img_metas, **kwargs) - - def train_step(self, data_batch, optimizer, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating is also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer` | dict): The optimizer of - runner is passed to ``train_step()``. This argument is unused - and reserved. - - Returns: - dict: It should contain at least 3 keys: ``loss``, ``log_vars``, - ``num_samples``. - ``loss`` is a tensor for back propagation, which can be a - weighted sum of multiple losses. - ``log_vars`` contains all the variables to be sent to the - logger. - ``num_samples`` indicates the batch size (when the model is - DDP, it means the batch size on each GPU), which is used for - averaging the logs. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, - log_vars=log_vars, - num_samples=len(data_batch['img_metas'])) - - return outputs - - def val_step(self, data_batch, optimizer=None, **kwargs): - """The iteration step during validation. - - This method shares the same signature as :func:`train_step`, but used - during val epochs. Note that the evaluation after training epochs is - not implemented with this method, but an evaluation hook. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - log_vars_ = dict() - for loss_name, loss_value in log_vars.items(): - k = loss_name + '_val' - log_vars_[k] = loss_value - - outputs = dict( - loss=loss, - log_vars=log_vars_, - num_samples=len(data_batch['img_metas'])) - - return outputs - - @staticmethod - def _parse_losses(losses): - """Parse the raw outputs (losses) of the network. - - Args: - losses (dict): Raw output of the network, which usually contain - losses and other necessary information. - - Returns: - tuple[Tensor, dict]: (loss, log_vars), loss is the loss tensor - which may be a weighted sum of all losses, log_vars contains - all the variables to be sent to the logger. - """ - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(_value for _key, _value in log_vars.items() - if 'loss' in _key) - - # If the loss_vars has different length, raise assertion error - # to prevent GPUs from infinite waiting. - if dist.is_available() and dist.is_initialized(): - log_var_length = torch.tensor(len(log_vars), device=loss.device) - dist.all_reduce(log_var_length) - message = (f'rank {dist.get_rank()}' + - f' len(log_vars): {len(log_vars)}' + ' keys: ' + - ','.join(log_vars.keys()) + '\n') - assert log_var_length == len(log_vars) * dist.get_world_size(), \ - 'loss log variables are different across GPUs!\n' + message - - log_vars['loss'] = loss - for loss_name, loss_value in log_vars.items(): - # reduce loss when distributed training - if dist.is_available() and dist.is_initialized(): - loss_value = loss_value.data.clone() - dist.all_reduce(loss_value.div_(dist.get_world_size())) - log_vars[loss_name] = loss_value.item() - - return loss, log_vars - - def show_result(self, - img, - result, - palette=None, - win_name='', - show=False, - wait_time=0, - out_file=None, - opacity=0.5): - """Draw `result` over `img`. - - Args: - img (str or Tensor): The image to be displayed. - result (Tensor): The semantic segmentation results to draw over - `img`. - palette (list[list[int]]] | np.ndarray | None): The palette of - segmentation map. If None is given, random palette will be - generated. Default: None - win_name (str): The window name. - wait_time (int): Value of waitKey param. - Default: 0. - show (bool): Whether to show the image. - Default: False. - out_file (str or None): The filename to write the image. - Default: None. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - Returns: - img (Tensor): Only if not `show` or `out_file` - """ - img = mmcv.imread(img) - img = img.copy() - seg = result[0] - if palette is None: - if self.PALETTE is None: - # Get random state before set seed, - # and restore random state later. - # It will prevent loss of randomness, as the palette - # may be different in each iteration if not specified. - # See: https://github.com/open-mmlab/mmdetection/issues/5844 - state = np.random.get_state() - np.random.seed(42) - # random palette - palette = np.random.randint( - 0, 255, size=(len(self.CLASSES), 3)) - np.random.set_state(state) - else: - palette = self.PALETTE - palette = np.array(palette) - assert palette.shape[0] == len(self.CLASSES) - assert palette.shape[1] == 3 - assert len(palette.shape) == 2 - assert 0 < opacity <= 1.0 - color_seg = np.zeros((seg.shape[0], seg.shape[1], 3), dtype=np.uint8) - for label, color in enumerate(palette): - color_seg[seg == label, :] = color - # convert to BGR - color_seg = color_seg[..., ::-1] - - img = img * (1 - opacity) + color_seg * opacity - img = img.astype(np.uint8) - # if out_file specified, do not show image in window - if out_file is not None: - show = False - - if show: - mmcv.imshow(img, win_name, wait_time) - if out_file is not None: - mmcv.imwrite(img, out_file) - - if not (show or out_file): - warnings.warn('show==False and out_file is not specified, only ' - 'result image will be returned') - return img diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py deleted file mode 100644 index 1913a22e2..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from mmseg.core import add_prefix -from mmseg.ops import resize -from .. import builder -from ..builder import SEGMENTORS -from .encoder_decoder import EncoderDecoder - - -@SEGMENTORS.register_module() -class CascadeEncoderDecoder(EncoderDecoder): - """Cascade Encoder Decoder segmentors. - - CascadeEncoderDecoder almost the same as EncoderDecoder, while decoders of - CascadeEncoderDecoder are cascaded. The output of previous decoder_head - will be the input of next decoder_head. - """ - - def __init__(self, - num_stages, - backbone, - decode_head, - neck=None, - auxiliary_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - self.num_stages = num_stages - super(CascadeEncoderDecoder, self).__init__( - backbone=backbone, - decode_head=decode_head, - neck=neck, - auxiliary_head=auxiliary_head, - train_cfg=train_cfg, - test_cfg=test_cfg, - pretrained=pretrained, - init_cfg=init_cfg) - - def _init_decode_head(self, decode_head): - """Initialize ``decode_head``""" - assert isinstance(decode_head, list) - assert len(decode_head) == self.num_stages - self.decode_head = nn.ModuleList() - for i in range(self.num_stages): - self.decode_head.append(builder.build_head(decode_head[i])) - self.align_corners = self.decode_head[-1].align_corners - self.num_classes = self.decode_head[-1].num_classes - - def encode_decode(self, img, img_metas): - """Encode images with backbone and decode into a semantic segmentation - map of the same size as input.""" - x = self.extract_feat(img) - out = self.decode_head[0].forward_test(x, img_metas, self.test_cfg) - for i in range(1, self.num_stages): - out = self.decode_head[i].forward_test(x, out, img_metas, - self.test_cfg) - out = resize( - input=out, - size=img.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - return out - - def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for decode head in - training.""" - losses = dict() - - loss_decode = self.decode_head[0].forward_train( - x, img_metas, gt_semantic_seg, self.train_cfg) - - losses.update(add_prefix(loss_decode, 'decode_0')) - - for i in range(1, self.num_stages): - # forward test again, maybe unnecessary for most methods. - if i == 1: - prev_outputs = self.decode_head[0].forward_test( - x, img_metas, self.test_cfg) - else: - prev_outputs = self.decode_head[i - 1].forward_test( - x, prev_outputs, img_metas, self.test_cfg) - loss_decode = self.decode_head[i].forward_train( - x, prev_outputs, img_metas, gt_semantic_seg, self.train_cfg) - losses.update(add_prefix(loss_decode, f'decode_{i}')) - - return losses diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/encoder_decoder.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/encoder_decoder.py deleted file mode 100644 index 72467b469..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/segmentors/encoder_decoder.py +++ /dev/null @@ -1,284 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmseg.core import add_prefix -from mmseg.ops import resize -from .. import builder -from ..builder import SEGMENTORS -from .base import BaseSegmentor - - -@SEGMENTORS.register_module() -class EncoderDecoder(BaseSegmentor): - """Encoder Decoder segmentors. - - EncoderDecoder typically consists of backbone, decode_head, auxiliary_head. - Note that auxiliary_head is only used for deep supervision during training, - which could be dumped during inference. - """ - - def __init__(self, - backbone, - decode_head, - neck=None, - auxiliary_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - super(EncoderDecoder, self).__init__(init_cfg) - if pretrained is not None: - assert backbone.get('pretrained') is None, \ - 'both backbone and segmentor set pretrained weight' - backbone.pretrained = pretrained - self.backbone = builder.build_backbone(backbone) - if neck is not None: - self.neck = builder.build_neck(neck) - self._init_decode_head(decode_head) - self._init_auxiliary_head(auxiliary_head) - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - assert self.with_decode_head - - def _init_decode_head(self, decode_head): - """Initialize ``decode_head``""" - self.decode_head = builder.build_head(decode_head) - self.align_corners = self.decode_head.align_corners - self.num_classes = self.decode_head.num_classes - - def _init_auxiliary_head(self, auxiliary_head): - """Initialize ``auxiliary_head``""" - if auxiliary_head is not None: - if isinstance(auxiliary_head, list): - self.auxiliary_head = nn.ModuleList() - for head_cfg in auxiliary_head: - self.auxiliary_head.append(builder.build_head(head_cfg)) - else: - self.auxiliary_head = builder.build_head(auxiliary_head) - - def extract_feat(self, img): - """Extract features from images.""" - x = self.backbone(img) - if self.with_neck: - x = self.neck(x) - return x - - def encode_decode(self, img, img_metas): - """Encode images with backbone and decode into a semantic segmentation - map of the same size as input.""" - x = self.extract_feat(img) - out = self._decode_head_forward_test(x, img_metas) - out = resize( - input=out, - size=img.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - return out - - def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for decode head in - training.""" - losses = dict() - loss_decode = self.decode_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - - losses.update(add_prefix(loss_decode, 'decode')) - return losses - - def _decode_head_forward_test(self, x, img_metas): - """Run forward function and calculate loss for decode head in - inference.""" - seg_logits = self.decode_head.forward_test(x, img_metas, self.test_cfg) - return seg_logits - - def _auxiliary_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for auxiliary head in - training.""" - losses = dict() - if isinstance(self.auxiliary_head, nn.ModuleList): - for idx, aux_head in enumerate(self.auxiliary_head): - loss_aux = aux_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - losses.update(add_prefix(loss_aux, f'aux_{idx}')) - else: - loss_aux = self.auxiliary_head.forward_train( - x, img_metas, gt_semantic_seg, self.train_cfg) - losses.update(add_prefix(loss_aux, 'aux')) - - return losses - - def forward_dummy(self, img): - """Dummy forward function.""" - seg_logit = self.encode_decode(img, None) - - return seg_logit - - def forward_train(self, img, img_metas, gt_semantic_seg): - """Forward function for training. - - Args: - img (Tensor): Input images. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - - x = self.extract_feat(img) - - losses = dict() - - loss_decode = self._decode_head_forward_train(x, img_metas, - gt_semantic_seg) - losses.update(loss_decode) - - if self.with_auxiliary_head: - loss_aux = self._auxiliary_head_forward_train( - x, img_metas, gt_semantic_seg) - losses.update(loss_aux) - - return losses - - # TODO refactor - def slide_inference(self, img, img_meta, rescale): - """Inference by sliding-window with overlap. - - If h_crop > h_img or w_crop > w_img, the small patch will be used to - decode without padding. - """ - - h_stride, w_stride = self.test_cfg.stride - h_crop, w_crop = self.test_cfg.crop_size - batch_size, _, h_img, w_img = img.size() - num_classes = self.num_classes - h_grids = max(h_img - h_crop + h_stride - 1, 0) // h_stride + 1 - w_grids = max(w_img - w_crop + w_stride - 1, 0) // w_stride + 1 - preds = img.new_zeros((batch_size, num_classes, h_img, w_img)) - count_mat = img.new_zeros((batch_size, 1, h_img, w_img)) - for h_idx in range(h_grids): - for w_idx in range(w_grids): - y1 = h_idx * h_stride - x1 = w_idx * w_stride - y2 = min(y1 + h_crop, h_img) - x2 = min(x1 + w_crop, w_img) - y1 = max(y2 - h_crop, 0) - x1 = max(x2 - w_crop, 0) - crop_img = img[:, :, y1:y2, x1:x2] - crop_seg_logit = self.encode_decode(crop_img, img_meta) - preds += F.pad(crop_seg_logit, - (int(x1), int(preds.shape[3] - x2), int(y1), - int(preds.shape[2] - y2))) - - count_mat[:, :, y1:y2, x1:x2] += 1 - assert (count_mat == 0).sum() == 0 - if torch.onnx.is_in_onnx_export(): - # cast count_mat to constant while exporting to ONNX - count_mat = torch.from_numpy( - count_mat.cpu().detach().numpy()).to(device=img.device) - preds = preds / count_mat - if rescale: - preds = resize( - preds, - size=img_meta[0]['ori_shape'][:2], - mode='bilinear', - align_corners=self.align_corners, - warning=False) - return preds - - def whole_inference(self, img, img_meta, rescale): - """Inference with full image.""" - - seg_logit = self.encode_decode(img, img_meta) - if rescale: - # support dynamic shape for onnx - if torch.onnx.is_in_onnx_export(): - size = img.shape[2:] - else: - size = img_meta[0]['ori_shape'][:2] - seg_logit = resize( - seg_logit, - size=size, - mode='bilinear', - align_corners=self.align_corners, - warning=False) - - return seg_logit - - def inference(self, img, img_meta, rescale): - """Inference with slide/whole style. - - Args: - img (Tensor): The input image of shape (N, 3, H, W). - img_meta (dict): Image info dict where each dict has: 'img_shape', - 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - rescale (bool): Whether rescale back to original shape. - - Returns: - Tensor: The output segmentation map. - """ - - assert self.test_cfg.mode in ['slide', 'whole'] - ori_shape = img_meta[0]['ori_shape'] - assert all(_['ori_shape'] == ori_shape for _ in img_meta) - if self.test_cfg.mode == 'slide': - seg_logit = self.slide_inference(img, img_meta, rescale) - else: - seg_logit = self.whole_inference(img, img_meta, rescale) - output = F.softmax(seg_logit, dim=1) - flip = img_meta[0]['flip'] - if flip: - flip_direction = img_meta[0]['flip_direction'] - assert flip_direction in ['horizontal', 'vertical'] - if flip_direction == 'horizontal': - output = output.flip(dims=(3, )) - elif flip_direction == 'vertical': - output = output.flip(dims=(2, )) - - return output - - def simple_test(self, img, img_meta, rescale=True): - """Simple test with single image.""" - seg_logit = self.inference(img, img_meta, rescale) - seg_pred = seg_logit.argmax(dim=1) - if torch.onnx.is_in_onnx_export(): - # our inference backend only support 4D output - seg_pred = seg_pred.unsqueeze(0) - return seg_pred - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred - - def aug_test(self, imgs, img_metas, rescale=True): - """Test with augmentations. - - Only rescale=True is supported. - """ - # aug_test rescale all imgs back to ori_shape for now - assert rescale - # to save memory, we get augmented seg logit inplace - seg_logit = self.inference(imgs[0], img_metas[0], rescale) - for i in range(1, len(imgs)): - cur_seg_logit = self.inference(imgs[i], img_metas[i], rescale) - seg_logit += cur_seg_logit - seg_logit /= len(imgs) - seg_pred = seg_logit.argmax(dim=1) - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/__init__.py deleted file mode 100644 index fc281e3d6..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .embed import PatchEmbed -from .inverted_residual import InvertedResidual, InvertedResidualV3 -from .make_divisible import make_divisible -from .res_layer import ResLayer -from .se_layer import SELayer -from .self_attention_block import SelfAttentionBlock -from .shape_convert import (nchw2nlc2nchw, nchw_to_nlc, nlc2nchw2nlc, - nlc_to_nchw) -from .up_conv_block import UpConvBlock -from .wrappers import resize, Upsample - -__all__ = [ - 'ResLayer', 'SelfAttentionBlock', 'make_divisible', 'InvertedResidual', - 'UpConvBlock', 'InvertedResidualV3', 'SELayer', 'PatchEmbed', - 'nchw_to_nlc', 'nlc_to_nchw', 'nchw2nlc2nchw', 'nlc2nchw2nlc', - 'resize', 'Upsample' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/embed.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/embed.py deleted file mode 100644 index 1515675e1..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/embed.py +++ /dev/null @@ -1,330 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -from typing import Sequence - -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner.base_module import BaseModule -from mmcv.utils import to_2tuple - - -class AdaptivePadding(nn.Module): - """Applies padding to input (if needed) so that input can get fully covered - by filter you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad zero around - input. The "corner" mode would pad zero to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel: - stride (int | tuple): Stride of the filter. Default: 1: - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - - super(AdaptivePadding, self).__init__() - - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The config dict for embedding - conv layer type selection. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int, optional): The slide stride of embedding conv. - Default: None (Would be set as `kernel_size`). - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only work when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=None, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super(PatchEmbed, self).__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adap_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adap_padding: - pad_h, pad_w = self.adap_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adap_padding: - x = self.adap_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map. Our implementation uses `nn.Unfold` to - merge patch, which is about 25% faster than original implementation. - Instead, we need to modify pretrained models for compatibility. - - Args: - in_channels (int): The num of input channels. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adap_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - - if self.adap_padding: - x = self.adap_padding(x) - H, W = x.shape[-2:] - - x = self.sampler(x) - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/inverted_residual.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/inverted_residual.py deleted file mode 100644 index c9cda7682..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/inverted_residual.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import ConvModule -from torch import nn -from torch.utils import checkpoint as cp - -from .se_layer import SELayer - - -class InvertedResidual(nn.Module): - """InvertedResidual block for MobileNetV2. - - Args: - in_channels (int): The input channels of the InvertedResidual block. - out_channels (int): The output channels of the InvertedResidual block. - stride (int): Stride of the middle (first) 3x3 convolution. - expand_ratio (int): Adjusts number of channels of the hidden layer - in InvertedResidual by this amount. - dilation (int): Dilation rate of depthwise conv. Default: 1 - conv_cfg (dict): Config dict for convolution layer. - Default: None, which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU6'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - stride, - expand_ratio, - dilation=1, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU6'), - with_cp=False, - **kwargs): - super(InvertedResidual, self).__init__() - self.stride = stride - assert stride in [1, 2], f'stride must in [1, 2]. ' \ - f'But received {stride}.' - self.with_cp = with_cp - self.use_res_connect = self.stride == 1 and in_channels == out_channels - hidden_dim = int(round(in_channels * expand_ratio)) - - layers = [] - if expand_ratio != 1: - layers.append( - ConvModule( - in_channels=in_channels, - out_channels=hidden_dim, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs)) - layers.extend([ - ConvModule( - in_channels=hidden_dim, - out_channels=hidden_dim, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - groups=hidden_dim, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs), - ConvModule( - in_channels=hidden_dim, - out_channels=out_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None, - **kwargs) - ]) - self.conv = nn.Sequential(*layers) - - def forward(self, x): - - def _inner_forward(x): - if self.use_res_connect: - return x + self.conv(x) - else: - return self.conv(x) - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out - - -class InvertedResidualV3(nn.Module): - """Inverted Residual Block for MobileNetV3. - - Args: - in_channels (int): The input channels of this Module. - out_channels (int): The output channels of this Module. - mid_channels (int): The input channels of the depthwise convolution. - kernel_size (int): The kernel size of the depthwise convolution. - Default: 3. - stride (int): The stride of the depthwise convolution. Default: 1. - se_cfg (dict): Config dict for se layer. Default: None, which means no - se layer. - with_expand_conv (bool): Use expand conv or not. If set False, - mid_channels must be the same with in_channels. Default: True. - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - mid_channels, - kernel_size=3, - stride=1, - se_cfg=None, - with_expand_conv=True, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - with_cp=False): - super(InvertedResidualV3, self).__init__() - self.with_res_shortcut = (stride == 1 and in_channels == out_channels) - assert stride in [1, 2] - self.with_cp = with_cp - self.with_se = se_cfg is not None - self.with_expand_conv = with_expand_conv - - if self.with_se: - assert isinstance(se_cfg, dict) - if not self.with_expand_conv: - assert mid_channels == in_channels - - if self.with_expand_conv: - self.expand_conv = ConvModule( - in_channels=in_channels, - out_channels=mid_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.depthwise_conv = ConvModule( - in_channels=mid_channels, - out_channels=mid_channels, - kernel_size=kernel_size, - stride=stride, - padding=kernel_size // 2, - groups=mid_channels, - conv_cfg=dict( - type='Conv2dAdaptivePadding') if stride == 2 else conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - if self.with_se: - self.se = SELayer(**se_cfg) - - self.linear_conv = ConvModule( - in_channels=mid_channels, - out_channels=out_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - def forward(self, x): - - def _inner_forward(x): - out = x - - if self.with_expand_conv: - out = self.expand_conv(out) - - out = self.depthwise_conv(out) - - if self.with_se: - out = self.se(out) - - out = self.linear_conv(out) - - if self.with_res_shortcut: - return x + out - else: - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/make_divisible.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/make_divisible.py deleted file mode 100644 index ed42c2eee..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/make_divisible.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def make_divisible(value, divisor, min_value=None, min_ratio=0.9): - """Make divisible function. - - This function rounds the channel number to the nearest value that can be - divisible by the divisor. It is taken from the original tf repo. It ensures - that all layers have a channel number that is divisible by divisor. It can - be seen here: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py # noqa - - Args: - value (int): The original channel number. - divisor (int): The divisor to fully divide the channel number. - min_value (int): The minimum value of the output channel. - Default: None, means that the minimum value equal to the divisor. - min_ratio (float): The minimum ratio of the rounded channel number to - the original channel number. Default: 0.9. - - Returns: - int: The modified output channel number. - """ - - if min_value is None: - min_value = divisor - new_value = max(min_value, int(value + divisor / 2) // divisor * divisor) - # Make sure that round down does not go down by more than (1-min_ratio). - if new_value < min_ratio * value: - new_value += divisor - return new_value diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/res_layer.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/res_layer.py deleted file mode 100644 index 190a0c5d5..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/res_layer.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner import Sequential -from torch import nn as nn - - -class ResLayer(Sequential): - """ResLayer to build ResNet style backbone. - - Args: - block (nn.Module): block used to build ResLayer. - inplanes (int): inplanes of block. - planes (int): planes of block. - num_blocks (int): number of blocks. - stride (int): stride of the first block. Default: 1 - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False - conv_cfg (dict): dictionary to construct and config conv layer. - Default: None - norm_cfg (dict): dictionary to construct and config norm layer. - Default: dict(type='BN') - multi_grid (int | None): Multi grid dilation rates of last - stage. Default: None - contract_dilation (bool): Whether contract first dilation of each layer - Default: False - """ - - def __init__(self, - block, - inplanes, - planes, - num_blocks, - stride=1, - dilation=1, - avg_down=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - multi_grid=None, - contract_dilation=False, - **kwargs): - self.block = block - - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = [] - conv_stride = stride - if avg_down: - conv_stride = 1 - downsample.append( - nn.AvgPool2d( - kernel_size=stride, - stride=stride, - ceil_mode=True, - count_include_pad=False)) - downsample.extend([ - build_conv_layer( - conv_cfg, - inplanes, - planes * block.expansion, - kernel_size=1, - stride=conv_stride, - bias=False), - build_norm_layer(norm_cfg, planes * block.expansion)[1] - ]) - downsample = nn.Sequential(*downsample) - - layers = [] - if multi_grid is None: - if dilation > 1 and contract_dilation: - first_dilation = dilation // 2 - else: - first_dilation = dilation - else: - first_dilation = multi_grid[0] - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=stride, - dilation=first_dilation, - downsample=downsample, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - inplanes = planes * block.expansion - for i in range(1, num_blocks): - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=1, - dilation=dilation if multi_grid is None else multi_grid[i], - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - super(ResLayer, self).__init__(*layers) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/se_layer.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/se_layer.py deleted file mode 100644 index 16f52aa5c..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/se_layer.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch.nn as nn -from mmcv.cnn import ConvModule - -from .make_divisible import make_divisible - - -class SELayer(nn.Module): - """Squeeze-and-Excitation Module. - - Args: - channels (int): The input (and output) channels of the SE layer. - ratio (int): Squeeze ratio in SELayer, the intermediate channel will be - ``int(channels/ratio)``. Default: 16. - conv_cfg (None or dict): Config dict for convolution layer. - Default: None, which means using conv2d. - act_cfg (dict or Sequence[dict]): Config dict for activation layer. - If act_cfg is a dict, two activation layers will be configured - by this dict. If act_cfg is a sequence of dicts, the first - activation layer will be configured by the first dict and the - second activation layer will be configured by the second dict. - Default: (dict(type='ReLU'), dict(type='HSigmoid', bias=3.0, - divisor=6.0)). - """ - - def __init__(self, - channels, - ratio=16, - conv_cfg=None, - act_cfg=(dict(type='ReLU'), - dict(type='HSigmoid', bias=3.0, divisor=6.0))): - super(SELayer, self).__init__() - if isinstance(act_cfg, dict): - act_cfg = (act_cfg, act_cfg) - assert len(act_cfg) == 2 - assert mmcv.is_tuple_of(act_cfg, dict) - self.global_avgpool = nn.AdaptiveAvgPool2d(1) - self.conv1 = ConvModule( - in_channels=channels, - out_channels=make_divisible(channels // ratio, 8), - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[0]) - self.conv2 = ConvModule( - in_channels=make_divisible(channels // ratio, 8), - out_channels=channels, - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[1]) - - def forward(self, x): - out = self.global_avgpool(x) - out = self.conv1(out) - out = self.conv2(out) - return x * out diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/self_attention_block.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/self_attention_block.py deleted file mode 100644 index c945fa716..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/self_attention_block.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.cnn import ConvModule, constant_init -from torch import nn as nn -from torch.nn import functional as F - - -class SelfAttentionBlock(nn.Module): - """General self-attention block/non-local block. - - Please refer to https://arxiv.org/abs/1706.03762 for details about key, - query and value. - - Args: - key_in_channels (int): Input channels of key feature. - query_in_channels (int): Input channels of query feature. - channels (int): Output channels of key/query transform. - out_channels (int): Output channels. - share_key_query (bool): Whether share projection weight between key - and query projection. - query_downsample (nn.Module): Query downsample module. - key_downsample (nn.Module): Key downsample module. - key_query_num_convs (int): Number of convs for key/query projection. - value_num_convs (int): Number of convs for value projection. - matmul_norm (bool): Whether normalize attention map with sqrt of - channels - with_out (bool): Whether use out projection. - conv_cfg (dict|None): Config of conv layers. - norm_cfg (dict|None): Config of norm layers. - act_cfg (dict|None): Config of activation layers. - """ - - def __init__(self, key_in_channels, query_in_channels, channels, - out_channels, share_key_query, query_downsample, - key_downsample, key_query_num_convs, value_out_num_convs, - key_query_norm, value_out_norm, matmul_norm, with_out, - conv_cfg, norm_cfg, act_cfg): - super(SelfAttentionBlock, self).__init__() - if share_key_query: - assert key_in_channels == query_in_channels - self.key_in_channels = key_in_channels - self.query_in_channels = query_in_channels - self.out_channels = out_channels - self.channels = channels - self.share_key_query = share_key_query - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.key_project = self.build_project( - key_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if share_key_query: - self.query_project = self.key_project - else: - self.query_project = self.build_project( - query_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.value_project = self.build_project( - key_in_channels, - channels if with_out else out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if with_out: - self.out_project = self.build_project( - channels, - out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.out_project = None - - self.query_downsample = query_downsample - self.key_downsample = key_downsample - self.matmul_norm = matmul_norm - - self.init_weights() - - def init_weights(self): - """Initialize weight of later layer.""" - if self.out_project is not None: - if not isinstance(self.out_project, ConvModule): - constant_init(self.out_project, 0) - - def build_project(self, in_channels, channels, num_convs, use_conv_module, - conv_cfg, norm_cfg, act_cfg): - """Build projection layer for key/query/value/out.""" - if use_conv_module: - convs = [ - ConvModule( - in_channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - ] - for _ in range(num_convs - 1): - convs.append( - ConvModule( - channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - else: - convs = [nn.Conv2d(in_channels, channels, 1)] - for _ in range(num_convs - 1): - convs.append(nn.Conv2d(channels, channels, 1)) - if len(convs) > 1: - convs = nn.Sequential(*convs) - else: - convs = convs[0] - return convs - - def forward(self, query_feats, key_feats): - """Forward function.""" - batch_size = query_feats.size(0) - query = self.query_project(query_feats) - if self.query_downsample is not None: - query = self.query_downsample(query) - query = query.reshape(*query.shape[:2], -1) - query = query.permute(0, 2, 1).contiguous() - - key = self.key_project(key_feats) - value = self.value_project(key_feats) - if self.key_downsample is not None: - key = self.key_downsample(key) - value = self.key_downsample(value) - key = key.reshape(*key.shape[:2], -1) - value = value.reshape(*value.shape[:2], -1) - value = value.permute(0, 2, 1).contiguous() - - sim_map = torch.matmul(query, key) - if self.matmul_norm: - sim_map = (self.channels**-.5) * sim_map - sim_map = F.softmax(sim_map, dim=-1) - - context = torch.matmul(sim_map, value) - context = context.permute(0, 2, 1).contiguous() - context = context.reshape(batch_size, -1, *query_feats.shape[2:]) - if self.out_project is not None: - context = self.out_project(context) - return context diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/shape_convert.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/shape_convert.py deleted file mode 100644 index cce1e220b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/shape_convert.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def nlc_to_nchw(x, hw_shape): - """Convert [N, L, C] shape tensor to [N, C, H, W] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, L, C] before conversion. - hw_shape (Sequence[int]): The height and width of output feature map. - - Returns: - Tensor: The output tensor of shape [N, C, H, W] after conversion. - """ - H, W = hw_shape - assert len(x.shape) == 3 - B, L, C = x.shape - assert L == H * W, 'The seq_len doesn\'t match H, W' - return x.transpose(1, 2).reshape(B, C, H, W) - - -def nchw_to_nlc(x): - """Flatten [N, C, H, W] shape tensor to [N, L, C] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, C, H, W] before conversion. - - Returns: - Tensor: The output tensor of shape [N, L, C] after conversion. - """ - assert len(x.shape) == 4 - return x.flatten(2).transpose(1, 2).contiguous() - - -def nchw2nlc2nchw(module, x, contiguous=False, **kwargs): - """Flatten [N, C, H, W] shape tensor `x` to [N, L, C] shape tensor. Use the - reshaped tensor as the input of `module`, and the convert the output of - `module`, whose shape is. - - [N, L, C], to [N, C, H, W]. - - Args: - module (Callable): A callable object the takes a tensor - with shape [N, L, C] as input. - x (Tensor): The input tensor of shape [N, C, H, W]. - contiguous: - contiguous (Bool): Whether to make the tensor contiguous - after each shape transform. - - Returns: - Tensor: The output tensor of shape [N, C, H, W]. - - Example: - >>> import torch - >>> import torch.nn as nn - >>> norm = nn.LayerNorm(4) - >>> feature_map = torch.rand(4, 4, 5, 5) - >>> output = nchw2nlc2nchw(norm, feature_map) - """ - B, C, H, W = x.shape - if not contiguous: - x = x.flatten(2).transpose(1, 2) - x = module(x, **kwargs) - x = x.transpose(1, 2).reshape(B, C, H, W) - else: - x = x.flatten(2).transpose(1, 2).contiguous() - x = module(x, **kwargs) - x = x.transpose(1, 2).reshape(B, C, H, W).contiguous() - return x - - -def nlc2nchw2nlc(module, x, hw_shape, contiguous=False, **kwargs): - """Convert [N, L, C] shape tensor `x` to [N, C, H, W] shape tensor. Use the - reshaped tensor as the input of `module`, and convert the output of - `module`, whose shape is. - - [N, C, H, W], to [N, L, C]. - - Args: - module (Callable): A callable object the takes a tensor - with shape [N, C, H, W] as input. - x (Tensor): The input tensor of shape [N, L, C]. - hw_shape: (Sequence[int]): The height and width of the - feature map with shape [N, C, H, W]. - contiguous (Bool): Whether to make the tensor contiguous - after each shape transform. - - Returns: - Tensor: The output tensor of shape [N, L, C]. - - Example: - >>> import torch - >>> import torch.nn as nn - >>> conv = nn.Conv2d(16, 16, 3, 1, 1) - >>> feature_map = torch.rand(4, 25, 16) - >>> output = nlc2nchw2nlc(conv, feature_map, (5, 5)) - """ - H, W = hw_shape - assert len(x.shape) == 3 - B, L, C = x.shape - assert L == H * W, 'The seq_len doesn\'t match H, W' - if not contiguous: - x = x.transpose(1, 2).reshape(B, C, H, W) - x = module(x, **kwargs) - x = x.flatten(2).transpose(1, 2) - else: - x = x.transpose(1, 2).reshape(B, C, H, W).contiguous() - x = module(x, **kwargs) - x = x.flatten(2).transpose(1, 2).contiguous() - return x diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/up_conv_block.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/up_conv_block.py deleted file mode 100644 index d8396d9c2..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/up_conv_block.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, build_upsample_layer - - -class UpConvBlock(nn.Module): - """Upsample convolution block in decoder for UNet. - - This upsample convolution block consists of one upsample module - followed by one convolution block. The upsample module expands the - high-level low-resolution feature map and the convolution block fuses - the upsampled high-level low-resolution feature map and the low-level - high-resolution feature map from encoder. - - Args: - conv_block (nn.Sequential): Sequential of convolutional layers. - in_channels (int): Number of input channels of the high-level - skip_channels (int): Number of input channels of the low-level - high-resolution feature map from encoder. - out_channels (int): Number of output channels. - num_convs (int): Number of convolutional layers in the conv_block. - Default: 2. - stride (int): Stride of convolutional layer in conv_block. Default: 1. - dilation (int): Dilation rate of convolutional layer in conv_block. - Default: 1. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - conv_cfg (dict | None): Config dict for convolution layer. - Default: None. - norm_cfg (dict | None): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict | None): Config dict for activation layer in ConvModule. - Default: dict(type='ReLU'). - upsample_cfg (dict): The upsample config of the upsample module in - decoder. Default: dict(type='InterpConv'). If the size of - high-level feature map is the same as that of skip feature map - (low-level feature map from encoder), it does not need upsample the - high-level feature map and the upsample_cfg is None. - dcn (bool): Use deformable convolution in convolutional layer or not. - Default: None. - plugins (dict): plugins for convolutional layers. Default: None. - """ - - def __init__(self, - conv_block, - in_channels, - skip_channels, - out_channels, - num_convs=2, - stride=1, - dilation=1, - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - upsample_cfg=dict(type='InterpConv'), - dcn=None, - plugins=None): - super(UpConvBlock, self).__init__() - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.conv_block = conv_block( - in_channels=2 * skip_channels, - out_channels=out_channels, - num_convs=num_convs, - stride=stride, - dilation=dilation, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - dcn=None, - plugins=None) - if upsample_cfg is not None: - self.upsample = build_upsample_layer( - cfg=upsample_cfg, - in_channels=in_channels, - out_channels=skip_channels, - with_cp=with_cp, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.upsample = ConvModule( - in_channels, - skip_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, skip, x): - """Forward function.""" - - x = self.upsample(x) - out = torch.cat([skip, x], dim=1) - out = self.conv_block(out) - - return out diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/wrappers.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/wrappers.py deleted file mode 100644 index abbd0c029..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/models/utils/wrappers.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.nn.functional as F - - -def resize(input, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None, - warning=True): - if warning: - if size is not None and align_corners: - input_h, input_w = tuple(int(x) for x in input.shape[2:]) - output_h, output_w = tuple(int(x) for x in size) - if output_h > input_h or output_w > output_h: - if ((output_h > 1 and output_w > 1 and input_h > 1 - and input_w > 1) and (output_h - 1) % (input_h - 1) - and (output_w - 1) % (input_w - 1)): - warnings.warn( - f'When align_corners={align_corners}, ' - 'the output would more aligned if ' - f'input size {(input_h, input_w)} is `x+1` and ' - f'out size {(output_h, output_w)} is `nx+1`') - return F.interpolate(input, size, scale_factor, mode, align_corners) - - -class Upsample(nn.Module): - - def __init__(self, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None): - super().__init__() - self.size = size - if isinstance(scale_factor, tuple): - self.scale_factor = tuple(float(factor) for factor in scale_factor) - else: - self.scale_factor = float(scale_factor) if scale_factor else None - self.mode = mode - self.align_corners = align_corners - - def forward(self, x): - if not self.size: - size = [int(t * self.scale_factor) for t in x.shape[-2:]] - else: - size = self.size - return resize(x, size, None, self.mode, self.align_corners) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/ops/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/ops/__init__.py deleted file mode 100644 index bc075cd4e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/ops/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .encoding import Encoding -from .wrappers import Upsample, resize - -__all__ = ['Upsample', 'resize', 'Encoding'] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/ops/encoding.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/ops/encoding.py deleted file mode 100644 index f397cc54e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/ops/encoding.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn -from torch.nn import functional as F - - -class Encoding(nn.Module): - """Encoding Layer: a learnable residual encoder. - - Input is of shape (batch_size, channels, height, width). - Output is of shape (batch_size, num_codes, channels). - - Args: - channels: dimension of the features or feature channels - num_codes: number of code words - """ - - def __init__(self, channels, num_codes): - super(Encoding, self).__init__() - # init codewords and smoothing factor - self.channels, self.num_codes = channels, num_codes - std = 1. / ((num_codes * channels)**0.5) - # [num_codes, channels] - self.codewords = nn.Parameter( - torch.empty(num_codes, channels, - dtype=torch.float).uniform_(-std, std), - requires_grad=True) - # [num_codes] - self.scale = nn.Parameter( - torch.empty(num_codes, dtype=torch.float).uniform_(-1, 0), - requires_grad=True) - - @staticmethod - def scaled_l2(x, codewords, scale): - num_codes, channels = codewords.size() - batch_size = x.size(0) - reshaped_scale = scale.view((1, 1, num_codes)) - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - - scaled_l2_norm = reshaped_scale * ( - expanded_x - reshaped_codewords).pow(2).sum(dim=3) - return scaled_l2_norm - - @staticmethod - def aggregate(assignment_weights, x, codewords): - num_codes, channels = codewords.size() - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - batch_size = x.size(0) - - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - encoded_feat = (assignment_weights.unsqueeze(3) * - (expanded_x - reshaped_codewords)).sum(dim=1) - return encoded_feat - - def forward(self, x): - assert x.dim() == 4 and x.size(1) == self.channels - # [batch_size, channels, height, width] - batch_size = x.size(0) - # [batch_size, height x width, channels] - x = x.view(batch_size, self.channels, -1).transpose(1, 2).contiguous() - # assignment_weights: [batch_size, channels, num_codes] - assignment_weights = F.softmax( - self.scaled_l2(x, self.codewords, self.scale), dim=2) - # aggregate - encoded_feat = self.aggregate(assignment_weights, x, self.codewords) - return encoded_feat - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(Nx{self.channels}xHxW =>Nx{self.num_codes}' \ - f'x{self.channels})' - return repr_str diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/ops/wrappers.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/ops/wrappers.py deleted file mode 100644 index ce67e4beb..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/ops/wrappers.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.nn.functional as F - - -def resize(input, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None, - warning=True): - if warning: - if size is not None and align_corners: - input_h, input_w = tuple(int(x) for x in input.shape[2:]) - output_h, output_w = tuple(int(x) for x in size) - if output_h > input_h or output_w > output_h: - if ((output_h > 1 and output_w > 1 and input_h > 1 - and input_w > 1) and (output_h - 1) % (input_h - 1) - and (output_w - 1) % (input_w - 1)): - warnings.warn( - f'When align_corners={align_corners}, ' - 'the output would more aligned if ' - f'input size {(input_h, input_w)} is `x+1` and ' - f'out size {(output_h, output_w)} is `nx+1`') - return F.interpolate(input, size, scale_factor, mode, align_corners) - - -class Upsample(nn.Module): - - def __init__(self, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None): - super(Upsample, self).__init__() - self.size = size - if isinstance(scale_factor, tuple): - self.scale_factor = tuple(float(factor) for factor in scale_factor) - else: - self.scale_factor = float(scale_factor) if scale_factor else None - self.mode = mode - self.align_corners = align_corners - - def forward(self, x): - if not self.size: - size = [int(t * self.scale_factor) for t in x.shape[-2:]] - else: - size = self.size - return resize(x, size, None, self.mode, self.align_corners) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/registry/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/registry/__init__.py deleted file mode 100644 index ee514d1a2..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/registry/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import (DATA_SAMPLERS, DATASETS, EVALUATOR, HOOKS, INFERENCERS, - LOG_PROCESSORS, LOOPS, METRICS, MODEL_WRAPPERS, MODELS, - OPTIM_WRAPPER_CONSTRUCTORS, OPTIM_WRAPPERS, OPTIMIZERS, - PARAM_SCHEDULERS, RUNNER_CONSTRUCTORS, RUNNERS, - TASK_UTILS, TRANSFORMS, VISBACKENDS, VISUALIZERS, - WEIGHT_INITIALIZERS) - -__all__ = [ - 'HOOKS', 'DATASETS', 'DATA_SAMPLERS', 'TRANSFORMS', 'MODELS', - 'WEIGHT_INITIALIZERS', 'OPTIMIZERS', 'OPTIM_WRAPPER_CONSTRUCTORS', - 'TASK_UTILS', 'PARAM_SCHEDULERS', 'METRICS', 'MODEL_WRAPPERS', - 'VISBACKENDS', 'VISUALIZERS', 'RUNNERS', 'RUNNER_CONSTRUCTORS', 'LOOPS', - 'EVALUATOR', 'LOG_PROCESSORS', 'OPTIM_WRAPPERS', 'INFERENCERS' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/registry/registry.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/registry/registry.py deleted file mode 100644 index 1e423980d..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/registry/registry.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""MMSegmentation provides 21 registry nodes to support using modules across -projects. Each node is a child of the root registry in MMEngine. - -More details can be found at -https://mmengine.readthedocs.io/en/latest/advanced_tutorials/registry.html. -""" - -from mmengine.registry import DATA_SAMPLERS as MMENGINE_DATA_SAMPLERS -from mmengine.registry import DATASETS as MMENGINE_DATASETS -from mmengine.registry import EVALUATOR as MMENGINE_EVALUATOR -from mmengine.registry import HOOKS as MMENGINE_HOOKS -from mmengine.registry import INFERENCERS as MMENGINE_INFERENCERS -from mmengine.registry import LOG_PROCESSORS as MMENGINE_LOG_PROCESSORS -from mmengine.registry import LOOPS as MMENGINE_LOOPS -from mmengine.registry import METRICS as MMENGINE_METRICS -from mmengine.registry import MODEL_WRAPPERS as MMENGINE_MODEL_WRAPPERS -from mmengine.registry import MODELS as MMENGINE_MODELS -from mmengine.registry import \ - OPTIM_WRAPPER_CONSTRUCTORS as MMENGINE_OPTIM_WRAPPER_CONSTRUCTORS -from mmengine.registry import OPTIM_WRAPPERS as MMENGINE_OPTIM_WRAPPERS -from mmengine.registry import OPTIMIZERS as MMENGINE_OPTIMIZERS -from mmengine.registry import PARAM_SCHEDULERS as MMENGINE_PARAM_SCHEDULERS -from mmengine.registry import \ - RUNNER_CONSTRUCTORS as MMENGINE_RUNNER_CONSTRUCTORS -from mmengine.registry import RUNNERS as MMENGINE_RUNNERS -from mmengine.registry import TASK_UTILS as MMENGINE_TASK_UTILS -from mmengine.registry import TRANSFORMS as MMENGINE_TRANSFORMS -from mmengine.registry import VISBACKENDS as MMENGINE_VISBACKENDS -from mmengine.registry import VISUALIZERS as MMENGINE_VISUALIZERS -from mmengine.registry import \ - WEIGHT_INITIALIZERS as MMENGINE_WEIGHT_INITIALIZERS -from mmengine.registry import Registry - -# manage all kinds of runners like `EpochBasedRunner` and `IterBasedRunner` -RUNNERS = Registry('runner', parent=MMENGINE_RUNNERS) -# manage runner constructors that define how to initialize runners -RUNNER_CONSTRUCTORS = Registry( - 'runner constructor', parent=MMENGINE_RUNNER_CONSTRUCTORS) -# manage all kinds of loops like `EpochBasedTrainLoop` -LOOPS = Registry('loop', parent=MMENGINE_LOOPS) -# manage all kinds of hooks like `CheckpointHook` -HOOKS = Registry( - 'hook', parent=MMENGINE_HOOKS, locations=['mmseg.engine.hooks']) - -# manage data-related modules -DATASETS = Registry( - 'dataset', parent=MMENGINE_DATASETS, locations=['mmseg.datasets']) -DATA_SAMPLERS = Registry('data sampler', parent=MMENGINE_DATA_SAMPLERS) -TRANSFORMS = Registry( - 'transform', - parent=MMENGINE_TRANSFORMS, - locations=['mmseg.datasets.transforms']) - -# mangage all kinds of modules inheriting `nn.Module` -MODELS = Registry('model', parent=MMENGINE_MODELS, locations=['mmseg.models']) -# mangage all kinds of model wrappers like 'MMDistributedDataParallel' -MODEL_WRAPPERS = Registry( - 'model_wrapper', - parent=MMENGINE_MODEL_WRAPPERS, - locations=['mmseg.models']) -# mangage all kinds of weight initialization modules like `Uniform` -WEIGHT_INITIALIZERS = Registry( - 'weight initializer', - parent=MMENGINE_WEIGHT_INITIALIZERS, - locations=['mmseg.models']) - -# mangage all kinds of optimizers like `SGD` and `Adam` -OPTIMIZERS = Registry( - 'optimizer', - parent=MMENGINE_OPTIMIZERS, - locations=['mmseg.engine.optimizers']) -# manage optimizer wrapper -OPTIM_WRAPPERS = Registry( - 'optim_wrapper', - parent=MMENGINE_OPTIM_WRAPPERS, - locations=['mmseg.engine.optimizers']) -# manage constructors that customize the optimization hyperparameters. -OPTIM_WRAPPER_CONSTRUCTORS = Registry( - 'optimizer wrapper constructor', - parent=MMENGINE_OPTIM_WRAPPER_CONSTRUCTORS, - locations=['mmseg.engine.optimizers']) -# mangage all kinds of parameter schedulers like `MultiStepLR` -PARAM_SCHEDULERS = Registry( - 'parameter scheduler', parent=MMENGINE_PARAM_SCHEDULERS) - -# manage all kinds of metrics -METRICS = Registry( - 'metric', parent=MMENGINE_METRICS, locations=['mmseg.evaluation']) -# manage evaluator -EVALUATOR = Registry( - 'evaluator', parent=MMENGINE_EVALUATOR, locations=['mmseg.evaluation']) - -# manage task-specific modules like ohem pixel sampler -TASK_UTILS = Registry( - 'task util', parent=MMENGINE_TASK_UTILS, locations=['mmseg.models']) - -# manage visualizer -VISUALIZERS = Registry( - 'visualizer', - parent=MMENGINE_VISUALIZERS, - locations=['mmseg.visualization']) -# manage visualizer backend -VISBACKENDS = Registry( - 'vis_backend', - parent=MMENGINE_VISBACKENDS, - locations=['mmseg.visualization']) - -# manage logprocessor -LOG_PROCESSORS = Registry( - 'log_processor', - parent=MMENGINE_LOG_PROCESSORS, - locations=['mmseg.visualization']) - -# manage inferencer -INFERENCERS = Registry('inferencer', parent=MMENGINE_INFERENCERS) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/__init__.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/__init__.py deleted file mode 100644 index ed002c7de..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collect_env import collect_env -from .logger import get_root_logger -from .misc import find_latest_checkpoint -from .set_env import setup_multi_processes - -__all__ = [ - 'get_root_logger', 'collect_env', 'find_latest_checkpoint', - 'setup_multi_processes' -] diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/collect_env.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/collect_env.py deleted file mode 100644 index 3379ecb06..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/collect_env.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash - -import mmseg - - -def collect_env(): - """Collect the information of the running environments.""" - env_info = collect_base_env() - env_info['MMSegmentation'] = f'{mmseg.__version__}+{get_git_hash()[:7]}' - - return env_info - - -if __name__ == '__main__': - for name, val in collect_env().items(): - print('{}: {}'.format(name, val)) diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/logger.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/logger.py deleted file mode 100644 index 0cb3c78d6..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/logger.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -from mmcv.utils import get_logger - - -def get_root_logger(log_file=None, log_level=logging.INFO): - """Get the root logger. - - The logger will be initialized if it has not been initialized. By default a - StreamHandler will be added. If `log_file` is specified, a FileHandler will - also be added. The name of the root logger is the top-level package name, - e.g., "mmseg". - - Args: - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the root logger. - log_level (int): The root logger level. Note that only the process of - rank 0 is affected, while other processes will set the level to - "Error" and be silent most of the time. - - Returns: - logging.Logger: The root logger. - """ - - logger = get_logger(name='mmseg', log_file=log_file, log_level=log_level) - - return logger diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/misc.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/misc.py deleted file mode 100644 index bd1b6b163..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/misc.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os.path as osp -import warnings - - -def find_latest_checkpoint(path, suffix='pth'): - """This function is for finding the latest checkpoint. - - It will be used when automatically resume, modified from - https://github.com/open-mmlab/mmdetection/blob/dev-v2.20.0/mmdet/utils/misc.py - - Args: - path (str): The path to find checkpoints. - suffix (str): File extension for the checkpoint. Defaults to pth. - - Returns: - latest_path(str | None): File path of the latest checkpoint. - """ - if not osp.exists(path): - warnings.warn("The path of the checkpoints doesn't exist.") - return None - if osp.exists(osp.join(path, f'latest.{suffix}')): - return osp.join(path, f'latest.{suffix}') - - checkpoints = glob.glob(osp.join(path, f'*.{suffix}')) - if len(checkpoints) == 0: - warnings.warn('The are no checkpoints in the path') - return None - latest = -1 - latest_path = '' - for checkpoint in checkpoints: - if len(checkpoint) < len(latest_path): - continue - # `count` is iteration number, as checkpoints are saved as - # 'iter_xx.pth' or 'epoch_xx.pth' and xx is iteration number. - count = int(osp.basename(checkpoint).split('_')[-1].split('.')[0]) - if count > latest: - latest = count - latest_path = checkpoint - return latest_path diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/set_env.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/set_env.py deleted file mode 100644 index b2d3aaf14..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/utils/set_env.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import platform - -import cv2 -import torch.multiprocessing as mp - -from ..utils import get_root_logger - - -def setup_multi_processes(cfg): - """Setup multi-processing environment variables.""" - logger = get_root_logger() - - # set multi-process start method - if platform.system() != 'Windows': - mp_start_method = cfg.get('mp_start_method', None) - current_method = mp.get_start_method(allow_none=True) - if mp_start_method in ('fork', 'spawn', 'forkserver'): - logger.info( - f'Multi-processing start method `{mp_start_method}` is ' - f'different from the previous setting `{current_method}`.' - f'It will be force set to `{mp_start_method}`.') - mp.set_start_method(mp_start_method, force=True) - else: - logger.info( - f'Multi-processing start method is `{mp_start_method}`') - - # disable opencv multithreading to avoid system being overloaded - opencv_num_threads = cfg.get('opencv_num_threads', None) - if isinstance(opencv_num_threads, int): - logger.info(f'OpenCV num_threads is `{opencv_num_threads}`') - cv2.setNumThreads(opencv_num_threads) - else: - logger.info(f'OpenCV num_threads is `{cv2.getNumThreads}') - - if cfg.data.workers_per_gpu > 1: - # setup OMP threads - # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa - omp_num_threads = cfg.get('omp_num_threads', None) - if 'OMP_NUM_THREADS' not in os.environ: - if isinstance(omp_num_threads, int): - logger.info(f'OMP num threads is {omp_num_threads}') - os.environ['OMP_NUM_THREADS'] = str(omp_num_threads) - else: - logger.info(f'OMP num threads is {os.environ["OMP_NUM_THREADS"] }') - - # setup MKL threads - if 'MKL_NUM_THREADS' not in os.environ: - mkl_num_threads = cfg.get('mkl_num_threads', None) - if isinstance(mkl_num_threads, int): - logger.info(f'MKL num threads is {mkl_num_threads}') - os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads) - else: - logger.info(f'MKL num threads is {os.environ["MKL_NUM_THREADS"]}') diff --git a/cv/semantic_segmentation/att_unet/pytorch/mmseg/version.py b/cv/semantic_segmentation/att_unet/pytorch/mmseg/version.py deleted file mode 100644 index e05146f0a..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/mmseg/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) Open-MMLab. All rights reserved. - -__version__ = '0.24.1' - - -def parse_version_info(version_str): - version_info = [] - for x in version_str.split('.'): - if x.isdigit(): - version_info.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - version_info.append(int(patch_version[0])) - version_info.append(f'rc{patch_version[1]}') - return tuple(version_info) - - -version_info = parse_version_info(__version__) diff --git a/cv/semantic_segmentation/att_unet/pytorch/requirements.txt b/cv/semantic_segmentation/att_unet/pytorch/requirements.txt deleted file mode 100644 index a50fc71db..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ --r requirements/mmcv/runtime.txt --r requirements/apcnet.txt -opencv-python -cityscapesscripts \ No newline at end of file diff --git a/cv/semantic_segmentation/att_unet/pytorch/requirements/apcnet.txt b/cv/semantic_segmentation/att_unet/pytorch/requirements/apcnet.txt deleted file mode 100644 index 2712f504c..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/requirements/apcnet.txt +++ /dev/null @@ -1,4 +0,0 @@ -matplotlib -numpy -packaging -prettytable diff --git a/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/build.txt b/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/build.txt deleted file mode 100644 index abf514853..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/build.txt +++ /dev/null @@ -1 +0,0 @@ -pytest-runner diff --git a/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/docs.txt b/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/docs.txt deleted file mode 100644 index 6a5319af3..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/docs.txt +++ /dev/null @@ -1,8 +0,0 @@ -docutils==0.16.0 -myst-parser -opencv-python --e git+https://github.com/open-mmlab/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme -sphinx==4.0.2 -sphinx-copybutton -sphinx_markdown_tables -torch diff --git a/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/optional.txt b/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/optional.txt deleted file mode 100644 index 63730036f..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/optional.txt +++ /dev/null @@ -1 +0,0 @@ -ninja diff --git a/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/requirements.txt b/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/requirements.txt deleted file mode 100644 index 6f00e6303..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ --r requirements/mmcv/build.txt --r requirements/mmcv/optional.txt --r requirements/mmcv/runtime.txt --r requirements/mmcv/test.txt diff --git a/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/runtime.txt b/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/runtime.txt deleted file mode 100644 index 66e90d674..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/runtime.txt +++ /dev/null @@ -1,7 +0,0 @@ -addict -numpy -packaging -Pillow -pyyaml -regex;sys_platform=='win32' -yapf diff --git a/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/test.txt b/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/test.txt deleted file mode 100644 index 6d9d17b98..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/requirements/mmcv/test.txt +++ /dev/null @@ -1,9 +0,0 @@ -coverage -lmdb -onnx==1.7.0; python_version < '3.10' -onnxoptimizer; python_version < '3.10' -onnxruntime>=1.8.0; python_version < '3.10' -pytest -PyTurboJPEG -scipy -tifffile diff --git a/cv/semantic_segmentation/att_unet/pytorch/requirements/mminstall.txt b/cv/semantic_segmentation/att_unet/pytorch/requirements/mminstall.txt deleted file mode 100644 index bd43faf87..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/requirements/mminstall.txt +++ /dev/null @@ -1,2 +0,0 @@ -mmcls>=0.20.1 -mmcv-full>=1.4.4,<=1.6.0 diff --git a/cv/semantic_segmentation/att_unet/pytorch/requirements/optional.txt b/cv/semantic_segmentation/att_unet/pytorch/requirements/optional.txt deleted file mode 100644 index 47fa59331..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/requirements/optional.txt +++ /dev/null @@ -1 +0,0 @@ -cityscapesscripts diff --git a/cv/semantic_segmentation/att_unet/pytorch/requirements/runtime.txt b/cv/semantic_segmentation/att_unet/pytorch/requirements/runtime.txt deleted file mode 100644 index 520408fe8..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/requirements/runtime.txt +++ /dev/null @@ -1,5 +0,0 @@ -matplotlib -mmcls>=0.20.1 -numpy -packaging -prettytable diff --git a/cv/semantic_segmentation/att_unet/pytorch/setup.py b/cv/semantic_segmentation/att_unet/pytorch/setup.py deleted file mode 100644 index d409997db..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/setup.py +++ /dev/null @@ -1,258 +0,0 @@ -import glob -import os -import platform -import re -import warnings -from pkg_resources import DistributionNotFound, get_distribution -from setuptools import find_packages, setup - -EXT_TYPE = '' -try: - import torch - if torch.__version__ == 'parrots': - from parrots.utils.build_extension import BuildExtension - EXT_TYPE = 'parrots' - elif (hasattr(torch, 'is_mlu_available') and torch.is_mlu_available()) or \ - os.getenv('FORCE_MLU', '0') == '1': - from torch_mlu.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - else: - from torch.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - cmd_class = {'build_ext': BuildExtension} -except ModuleNotFoundError: - cmd_class = {} - print('Skip building ext ops due to the absence of torch.') - - -def choose_requirement(primary, secondary): - """If some version of primary requirement installed, return primary, else - return secondary.""" - try: - name = re.split(r'[!<>=]', primary)[0] - get_distribution(name) - except DistributionNotFound: - return secondary - - return str(primary) - - -def get_version(): - version_file = 'mmcv/version.py' - with open(version_file, 'r', encoding='utf-8') as f: - exec(compile(f.read(), version_file, 'exec')) - return locals()['__version__'] - - -def parse_requirements(fname='requirements/mmcv/runtime.txt', with_version=True): - """Parse the package dependencies listed in a requirements file but strips - specific versioning information. - - Args: - fname (str): path to requirements file - with_version (bool, default=False): if True include version specs - - Returns: - List[str]: list of requirements items - - CommandLine: - python -c "import setup; print(setup.parse_requirements())" - """ - import sys - from os.path import exists - require_fpath = fname - - def parse_line(line): - """Parse information from a line in a requirements text file.""" - if line.startswith('-r '): - # Allow specifying requirements in other files - target = line.split(' ')[1] - for info in parse_require_file(target): - yield info - else: - info = {'line': line} - if line.startswith('-e '): - info['package'] = line.split('#egg=')[1] - else: - # Remove versioning from the package - pat = '(' + '|'.join(['>=', '==', '>']) + ')' - parts = re.split(pat, line, maxsplit=1) - parts = [p.strip() for p in parts] - - info['package'] = parts[0] - if len(parts) > 1: - op, rest = parts[1:] - if ';' in rest: - # Handle platform specific dependencies - # http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies - version, platform_deps = map(str.strip, - rest.split(';')) - info['platform_deps'] = platform_deps - else: - version = rest # NOQA - info['version'] = (op, version) - yield info - - def parse_require_file(fpath): - with open(fpath, 'r') as f: - for line in f.readlines(): - line = line.strip() - if line and not line.startswith('#'): - for info in parse_line(line): - yield info - - def gen_packages_items(): - if exists(require_fpath): - for info in parse_require_file(require_fpath): - parts = [info['package']] - if with_version and 'version' in info: - parts.extend(info['version']) - if not sys.version.startswith('3.4'): - # apparently package_deps are broken in 3.4 - platform_deps = info.get('platform_deps') - if platform_deps is not None: - parts.append(';' + platform_deps) - item = ''.join(parts) - yield item - - packages = list(gen_packages_items()) - return packages - - -install_requires = parse_requirements() - -try: - # OpenCV installed via conda. - import cv2 # NOQA: F401 - major, minor, *rest = cv2.__version__.split('.') - if int(major) < 3: - raise RuntimeError( - f'OpenCV >=3 is required but {cv2.__version__} is installed') -except ImportError: - # If first not installed install second package - CHOOSE_INSTALL_REQUIRES = [('opencv-python-headless>=3', - 'opencv-python>=3')] - for main, secondary in CHOOSE_INSTALL_REQUIRES: - install_requires.append(choose_requirement(main, secondary)) - - -def get_extensions(): - extensions = [] - - if EXT_TYPE == 'pytorch': - ext_name = 'mmcv._ext' - from torch.utils.cpp_extension import CppExtension, CUDAExtension - - # prevent ninja from using too many resources - try: - import psutil - num_cpu = len(psutil.Process().cpu_affinity()) - cpu_use = max(4, num_cpu - 1) - except (ModuleNotFoundError, AttributeError): - cpu_use = 4 - - os.environ.setdefault('MAX_JOBS', str(cpu_use)) - define_macros = [] - - # Before PyTorch1.8.0, when compiling CUDA code, `cxx` is a - # required key passed to PyTorch. Even if there is no flag passed - # to cxx, users also need to pass an empty list to PyTorch. - # Since PyTorch1.8.0, it has a default value so users do not need - # to pass an empty list anymore. - # More details at https://github.com/pytorch/pytorch/pull/45956 - extra_compile_args = {'cxx': []} - - # Since the PR (https://github.com/open-mmlab/mmcv/pull/1463) uses - # c++14 features, the argument ['std=c++14'] must be added here. - # However, in the windows environment, some standard libraries - # will depend on c++17 or higher. In fact, for the windows - # environment, the compiler will choose the appropriate compiler - # to compile those cpp files, so there is no need to add the - # argument - if platform.system() != 'Windows': - extra_compile_args['cxx'] = ['-std=c++14'] - - include_dirs = [] - - is_rocm_pytorch = False - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm_pytorch = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - - if is_rocm_pytorch or torch.cuda.is_available() or os.getenv( - 'FORCE_CUDA', '0') == '1': - if is_rocm_pytorch: - define_macros += [('HIP_DIFF', None)] - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cpp') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - else: - print(f'Compiling {ext_name} only with CPU') - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') - extension = CppExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - - # Since the PR (https://github.com/open-mmlab/mmcv/pull/1463) uses - # c++14 features, the argument ['std=c++14'] must be added here. - # However, in the windows environment, some standard libraries - # will depend on c++17 or higher. In fact, for the windows - # environment, the compiler will choose the appropriate compiler - # to compile those cpp files, so there is no need to add the - # argument - if 'nvcc' in extra_compile_args and platform.system() != 'Windows': - extra_compile_args['nvcc'] += ['-std=c++14'] - - ext_ops = extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args) - extensions.append(ext_ops) - - return extensions - - -setup( - name='mmcv' if os.getenv('MMCV_WITH_OPS', '0') == '0' else 'mmcv-full', - version=get_version(), - description='OpenMMLab Computer Vision Foundation', - keywords='computer vision', - packages=find_packages(), - include_package_data=True, - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Topic :: Utilities', - ], - url='https://github.com/open-mmlab/mmcv', - author='MMCV Contributors', - author_email='openmmlab@gmail.com', - install_requires=install_requires, - extras_require={ - 'all': parse_requirements('requirements/mmcv/requirements.txt'), - 'tests': parse_requirements('requirements/mmcv/test.txt'), - 'build': parse_requirements('requirements/mmcv/build.txt'), - 'optional': parse_requirements('requirements/mmcv/optional.txt'), - }, - ext_modules=get_extensions(), - cmdclass=cmd_class, - zip_safe=False) diff --git a/cv/semantic_segmentation/att_unet/pytorch/tools/analyze_logs.py b/cv/semantic_segmentation/att_unet/pytorch/tools/analyze_logs.py deleted file mode 100644 index e2127d4d6..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/tools/analyze_logs.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/open- -mmlab/mmdetection/blob/master/tools/analysis_tools/analyze_logs.py.""" -import argparse -import json -from collections import defaultdict - -import matplotlib.pyplot as plt -import seaborn as sns - - -def plot_curve(log_dicts, args): - if args.backend is not None: - plt.switch_backend(args.backend) - sns.set_style(args.style) - # if legend is None, use {filename}_{key} as legend - legend = args.legend - if legend is None: - legend = [] - for json_log in args.json_logs: - for metric in args.keys: - legend.append(f'{json_log}_{metric}') - assert len(legend) == (len(args.json_logs) * len(args.keys)) - metrics = args.keys - - num_metrics = len(metrics) - for i, log_dict in enumerate(log_dicts): - epochs = list(log_dict.keys()) - for j, metric in enumerate(metrics): - print(f'plot curve of {args.json_logs[i]}, metric is {metric}') - plot_epochs = [] - plot_iters = [] - plot_values = [] - # In some log files exist lines of validation, - # `mode` list is used to only collect iter number - # of training line. - for epoch in epochs: - epoch_logs = log_dict[epoch] - if metric not in epoch_logs.keys(): - continue - if metric in ['mIoU', 'mAcc', 'aAcc']: - plot_epochs.append(epoch) - plot_values.append(epoch_logs[metric][0]) - else: - for idx in range(len(epoch_logs[metric])): - if epoch_logs['mode'][idx] == 'train': - plot_iters.append(epoch_logs['iter'][idx]) - plot_values.append(epoch_logs[metric][idx]) - ax = plt.gca() - label = legend[i * num_metrics + j] - if metric in ['mIoU', 'mAcc', 'aAcc']: - ax.set_xticks(plot_epochs) - plt.xlabel('epoch') - plt.plot(plot_epochs, plot_values, label=label, marker='o') - else: - plt.xlabel('iter') - plt.plot(plot_iters, plot_values, label=label, linewidth=0.5) - plt.legend() - if args.title is not None: - plt.title(args.title) - if args.out is None: - plt.show() - else: - print(f'save curve to: {args.out}') - plt.savefig(args.out) - plt.cla() - - -def parse_args(): - parser = argparse.ArgumentParser(description='Analyze Json Log') - parser.add_argument( - 'json_logs', - type=str, - nargs='+', - help='path of train log in json format') - parser.add_argument( - '--keys', - type=str, - nargs='+', - default=['mIoU'], - help='the metric that you want to plot') - parser.add_argument('--title', type=str, help='title of figure') - parser.add_argument( - '--legend', - type=str, - nargs='+', - default=None, - help='legend of each plot') - parser.add_argument( - '--backend', type=str, default=None, help='backend of plt') - parser.add_argument( - '--style', type=str, default='dark', help='style of plt') - parser.add_argument('--out', type=str, default=None) - args = parser.parse_args() - return args - - -def load_json_logs(json_logs): - # load and convert json_logs to log_dict, key is epoch, value is a sub dict - # keys of sub dict is different metrics - # value of sub dict is a list of corresponding values of all iterations - log_dicts = [dict() for _ in json_logs] - for json_log, log_dict in zip(json_logs, log_dicts): - with open(json_log, 'r') as log_file: - for line in log_file: - log = json.loads(line.strip()) - # skip lines without `epoch` field - if 'epoch' not in log: - continue - epoch = log.pop('epoch') - if epoch not in log_dict: - log_dict[epoch] = defaultdict(list) - for k, v in log.items(): - log_dict[epoch][k].append(v) - return log_dicts - - -def main(): - args = parse_args() - json_logs = args.json_logs - for json_log in json_logs: - assert json_log.endswith('.json') - log_dicts = load_json_logs(json_logs) - plot_curve(log_dicts, args) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/att_unet/pytorch/tools/benchmark.py b/cv/semantic_segmentation/att_unet/pytorch/tools/benchmark.py deleted file mode 100644 index f6d688848..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/tools/benchmark.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import time - -import mmcv -import numpy as np -import torch -from mmcv import Config -from mmcv.parallel import MMDataParallel -from mmcv.runner import load_checkpoint, wrap_fp16_model - -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='MMSeg benchmark a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--log-interval', type=int, default=50, help='interval of logging') - parser.add_argument( - '--work-dir', - help=('if specified, the results will be dumped ' - 'into the directory as json')) - parser.add_argument('--repeat-times', type=int, default=1) - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.work_dir is not None: - mmcv.mkdir_or_exist(osp.abspath(args.work_dir)) - json_file = osp.join(args.work_dir, f'fps_{timestamp}.json') - else: - # use config filename as default work_dir if cfg.work_dir is None - work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - mmcv.mkdir_or_exist(osp.abspath(work_dir)) - json_file = osp.join(work_dir, f'fps_{timestamp}.json') - - repeat_times = args.repeat_times - # set cudnn_benchmark - torch.backends.cudnn.benchmark = False - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - benchmark_dict = dict(config=args.config, unit='img / s') - overall_fps_list = [] - for time_index in range(repeat_times): - print(f'Run {time_index + 1}:') - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - data_loader = build_dataloader( - dataset, - samples_per_gpu=1, - workers_per_gpu=cfg.data.workers_per_gpu, - dist=False, - shuffle=False) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - if 'checkpoint' in args and osp.exists(args.checkpoint): - load_checkpoint(model, args.checkpoint, map_location='cpu') - - model = MMDataParallel(model, device_ids=[0]) - - model.eval() - - # the first several iterations may be very slow so skip them - num_warmup = 5 - pure_inf_time = 0 - total_iters = 200 - - # benchmark with 200 image and take the average - for i, data in enumerate(data_loader): - - torch.cuda.synchronize() - start_time = time.perf_counter() - - with torch.no_grad(): - model(return_loss=False, rescale=True, **data) - - torch.cuda.synchronize() - elapsed = time.perf_counter() - start_time - - if i >= num_warmup: - pure_inf_time += elapsed - if (i + 1) % args.log_interval == 0: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Done image [{i + 1:<3}/ {total_iters}], ' - f'fps: {fps:.2f} img / s') - - if (i + 1) == total_iters: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Overall fps: {fps:.2f} img / s\n') - benchmark_dict[f'overall_fps_{time_index + 1}'] = round(fps, 2) - overall_fps_list.append(fps) - break - benchmark_dict['average_fps'] = round(np.mean(overall_fps_list), 2) - benchmark_dict['fps_variance'] = round(np.var(overall_fps_list), 4) - print(f'Average fps of {repeat_times} evaluations: ' - f'{benchmark_dict["average_fps"]}') - print(f'The variance of {repeat_times} evaluations: ' - f'{benchmark_dict["fps_variance"]}') - mmcv.dump(benchmark_dict, json_file, indent=4) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/att_unet/pytorch/tools/confusion_matrix.py b/cv/semantic_segmentation/att_unet/pytorch/tools/confusion_matrix.py deleted file mode 100644 index 2c5b64cf4..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/tools/confusion_matrix.py +++ /dev/null @@ -1,184 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os - -import matplotlib.pyplot as plt -import mmcv -import numpy as np -from matplotlib.ticker import MultipleLocator -from mmcv import Config, DictAction - -from mmseg.datasets import build_dataset - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Generate confusion matrix from segmentation results') - parser.add_argument('config', help='test config file path') - parser.add_argument( - 'prediction_path', help='prediction path where test .pkl result') - parser.add_argument( - 'save_dir', help='directory where confusion matrix will be saved') - parser.add_argument( - '--show', action='store_true', help='show confusion matrix') - parser.add_argument( - '--color-theme', - default='winter', - help='theme of the matrix color map') - parser.add_argument( - '--title', - default='Normalized Confusion Matrix', - help='title of the matrix color map') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - args = parser.parse_args() - return args - - -def calculate_confusion_matrix(dataset, results): - """Calculate the confusion matrix. - - Args: - dataset (Dataset): Test or val dataset. - results (list[ndarray]): A list of segmentation results in each image. - """ - n = len(dataset.CLASSES) - confusion_matrix = np.zeros(shape=[n, n]) - assert len(dataset) == len(results) - prog_bar = mmcv.ProgressBar(len(results)) - for idx, per_img_res in enumerate(results): - res_segm = per_img_res - gt_segm = dataset.get_gt_seg_map_by_idx(idx) - inds = n * gt_segm + res_segm - inds = inds.flatten() - mat = np.bincount(inds, minlength=n**2).reshape(n, n) - confusion_matrix += mat - prog_bar.update() - return confusion_matrix - - -def plot_confusion_matrix(confusion_matrix, - labels, - save_dir=None, - show=True, - title='Normalized Confusion Matrix', - color_theme='winter'): - """Draw confusion matrix with matplotlib. - - Args: - confusion_matrix (ndarray): The confusion matrix. - labels (list[str]): List of class names. - save_dir (str|optional): If set, save the confusion matrix plot to the - given path. Default: None. - show (bool): Whether to show the plot. Default: True. - title (str): Title of the plot. Default: `Normalized Confusion Matrix`. - color_theme (str): Theme of the matrix color map. Default: `winter`. - """ - # normalize the confusion matrix - per_label_sums = confusion_matrix.sum(axis=1)[:, np.newaxis] - confusion_matrix = \ - confusion_matrix.astype(np.float32) / per_label_sums * 100 - - num_classes = len(labels) - fig, ax = plt.subplots( - figsize=(2 * num_classes, 2 * num_classes * 0.8), dpi=180) - cmap = plt.get_cmap(color_theme) - im = ax.imshow(confusion_matrix, cmap=cmap) - plt.colorbar(mappable=im, ax=ax) - - title_font = {'weight': 'bold', 'size': 12} - ax.set_title(title, fontdict=title_font) - label_font = {'size': 10} - plt.ylabel('Ground Truth Label', fontdict=label_font) - plt.xlabel('Prediction Label', fontdict=label_font) - - # draw locator - xmajor_locator = MultipleLocator(1) - xminor_locator = MultipleLocator(0.5) - ax.xaxis.set_major_locator(xmajor_locator) - ax.xaxis.set_minor_locator(xminor_locator) - ymajor_locator = MultipleLocator(1) - yminor_locator = MultipleLocator(0.5) - ax.yaxis.set_major_locator(ymajor_locator) - ax.yaxis.set_minor_locator(yminor_locator) - - # draw grid - ax.grid(True, which='minor', linestyle='-') - - # draw label - ax.set_xticks(np.arange(num_classes)) - ax.set_yticks(np.arange(num_classes)) - ax.set_xticklabels(labels) - ax.set_yticklabels(labels) - - ax.tick_params( - axis='x', bottom=False, top=True, labelbottom=False, labeltop=True) - plt.setp( - ax.get_xticklabels(), rotation=45, ha='left', rotation_mode='anchor') - - # draw confusion matrix value - for i in range(num_classes): - for j in range(num_classes): - ax.text( - j, - i, - '{}%'.format( - round(confusion_matrix[i, j], 2 - ) if not np.isnan(confusion_matrix[i, j]) else -1), - ha='center', - va='center', - color='w', - size=7) - - ax.set_ylim(len(confusion_matrix) - 0.5, -0.5) # matplotlib>3.1.1 - - fig.tight_layout() - if save_dir is not None: - plt.savefig( - os.path.join(save_dir, 'confusion_matrix.png'), format='png') - if show: - plt.show() - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - results = mmcv.load(args.prediction_path) - - assert isinstance(results, list) - if isinstance(results[0], np.ndarray): - pass - else: - raise TypeError('invalid type of prediction results') - - if isinstance(cfg.data.test, dict): - cfg.data.test.test_mode = True - elif isinstance(cfg.data.test, list): - for ds_cfg in cfg.data.test: - ds_cfg.test_mode = True - - dataset = build_dataset(cfg.data.test) - confusion_matrix = calculate_confusion_matrix(dataset, results) - plot_confusion_matrix( - confusion_matrix, - dataset.CLASSES, - save_dir=args.save_dir, - show=args.show, - title=args.title, - color_theme=args.color_theme) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/cityscapes.py b/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/cityscapes.py deleted file mode 100644 index 17b616847..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/cityscapes.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp - -import mmcv -from cityscapesscripts.preparation.json2labelImg import json2labelImg - - -def convert_json_to_label(json_file): - label_file = json_file.replace('_polygons.json', '_labelTrainIds.png') - json2labelImg(json_file, label_file, 'trainIds') - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert Cityscapes annotations to TrainIds') - parser.add_argument('cityscapes_path', help='cityscapes data path') - parser.add_argument('--gt-dir', default='gtFine', type=str) - parser.add_argument('-o', '--out-dir', help='output path') - parser.add_argument( - '--nproc', default=1, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - cityscapes_path = args.cityscapes_path - out_dir = args.out_dir if args.out_dir else cityscapes_path - mmcv.mkdir_or_exist(out_dir) - - gt_dir = osp.join(cityscapes_path, args.gt_dir) - - poly_files = [] - for poly in mmcv.scandir(gt_dir, '_polygons.json', recursive=True): - poly_file = osp.join(gt_dir, poly) - poly_files.append(poly_file) - if args.nproc > 1: - mmcv.track_parallel_progress(convert_json_to_label, poly_files, - args.nproc) - else: - mmcv.track_progress(convert_json_to_label, poly_files) - - split_names = ['train', 'val', 'test'] - - for split in split_names: - filenames = [] - for poly in mmcv.scandir( - osp.join(gt_dir, split), '_polygons.json', recursive=True): - filenames.append(poly.replace('_gtFine_polygons.json', '')) - with open(osp.join(out_dir, f'{split}.txt'), 'w') as f: - f.writelines(f + '\n' for f in filenames) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/coco_stuff10k.py b/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/coco_stuff10k.py deleted file mode 100644 index 374f81970..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/coco_stuff10k.py +++ /dev/null @@ -1,307 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import shutil -from functools import partial - -import mmcv -import numpy as np -from PIL import Image -from scipy.io import loadmat - -COCO_LEN = 10000 - -clsID_to_trID = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 13: 12, - 14: 13, - 15: 14, - 16: 15, - 17: 16, - 18: 17, - 19: 18, - 20: 19, - 21: 20, - 22: 21, - 23: 22, - 24: 23, - 25: 24, - 27: 25, - 28: 26, - 31: 27, - 32: 28, - 33: 29, - 34: 30, - 35: 31, - 36: 32, - 37: 33, - 38: 34, - 39: 35, - 40: 36, - 41: 37, - 42: 38, - 43: 39, - 44: 40, - 46: 41, - 47: 42, - 48: 43, - 49: 44, - 50: 45, - 51: 46, - 52: 47, - 53: 48, - 54: 49, - 55: 50, - 56: 51, - 57: 52, - 58: 53, - 59: 54, - 60: 55, - 61: 56, - 62: 57, - 63: 58, - 64: 59, - 65: 60, - 67: 61, - 70: 62, - 72: 63, - 73: 64, - 74: 65, - 75: 66, - 76: 67, - 77: 68, - 78: 69, - 79: 70, - 80: 71, - 81: 72, - 82: 73, - 84: 74, - 85: 75, - 86: 76, - 87: 77, - 88: 78, - 89: 79, - 90: 80, - 92: 81, - 93: 82, - 94: 83, - 95: 84, - 96: 85, - 97: 86, - 98: 87, - 99: 88, - 100: 89, - 101: 90, - 102: 91, - 103: 92, - 104: 93, - 105: 94, - 106: 95, - 107: 96, - 108: 97, - 109: 98, - 110: 99, - 111: 100, - 112: 101, - 113: 102, - 114: 103, - 115: 104, - 116: 105, - 117: 106, - 118: 107, - 119: 108, - 120: 109, - 121: 110, - 122: 111, - 123: 112, - 124: 113, - 125: 114, - 126: 115, - 127: 116, - 128: 117, - 129: 118, - 130: 119, - 131: 120, - 132: 121, - 133: 122, - 134: 123, - 135: 124, - 136: 125, - 137: 126, - 138: 127, - 139: 128, - 140: 129, - 141: 130, - 142: 131, - 143: 132, - 144: 133, - 145: 134, - 146: 135, - 147: 136, - 148: 137, - 149: 138, - 150: 139, - 151: 140, - 152: 141, - 153: 142, - 154: 143, - 155: 144, - 156: 145, - 157: 146, - 158: 147, - 159: 148, - 160: 149, - 161: 150, - 162: 151, - 163: 152, - 164: 153, - 165: 154, - 166: 155, - 167: 156, - 168: 157, - 169: 158, - 170: 159, - 171: 160, - 172: 161, - 173: 162, - 174: 163, - 175: 164, - 176: 165, - 177: 166, - 178: 167, - 179: 168, - 180: 169, - 181: 170, - 182: 171 -} - - -def convert_to_trainID(tuple_path, in_img_dir, in_ann_dir, out_img_dir, - out_mask_dir, is_train): - imgpath, maskpath = tuple_path - shutil.copyfile( - osp.join(in_img_dir, imgpath), - osp.join(out_img_dir, 'train2014', imgpath) if is_train else osp.join( - out_img_dir, 'test2014', imgpath)) - annotate = loadmat(osp.join(in_ann_dir, maskpath)) - mask = annotate['S'].astype(np.uint8) - mask_copy = mask.copy() - for clsID, trID in clsID_to_trID.items(): - mask_copy[mask == clsID] = trID - seg_filename = osp.join(out_mask_dir, 'train2014', - maskpath.split('.')[0] + - '_labelTrainIds.png') if is_train else osp.join( - out_mask_dir, 'test2014', - maskpath.split('.')[0] + '_labelTrainIds.png') - Image.fromarray(mask_copy).save(seg_filename, 'PNG') - - -def generate_coco_list(folder): - train_list = osp.join(folder, 'imageLists', 'train.txt') - test_list = osp.join(folder, 'imageLists', 'test.txt') - train_paths = [] - test_paths = [] - - with open(train_list) as f: - for filename in f: - basename = filename.strip() - imgpath = basename + '.jpg' - maskpath = basename + '.mat' - train_paths.append((imgpath, maskpath)) - - with open(test_list) as f: - for filename in f: - basename = filename.strip() - imgpath = basename + '.jpg' - maskpath = basename + '.mat' - test_paths.append((imgpath, maskpath)) - - return train_paths, test_paths - - -def parse_args(): - parser = argparse.ArgumentParser( - description=\ - 'Convert COCO Stuff 10k annotations to mmsegmentation format') # noqa - parser.add_argument('coco_path', help='coco stuff path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=16, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - coco_path = args.coco_path - nproc = args.nproc - - out_dir = args.out_dir or coco_path - out_img_dir = osp.join(out_dir, 'images') - out_mask_dir = osp.join(out_dir, 'annotations') - - mmcv.mkdir_or_exist(osp.join(out_img_dir, 'train2014')) - mmcv.mkdir_or_exist(osp.join(out_img_dir, 'test2014')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'train2014')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'test2014')) - - train_list, test_list = generate_coco_list(coco_path) - assert (len(train_list) + - len(test_list)) == COCO_LEN, 'Wrong length of list {} & {}'.format( - len(train_list), len(test_list)) - - if args.nproc > 1: - mmcv.track_parallel_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=True), - train_list, - nproc=nproc) - mmcv.track_parallel_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=False), - test_list, - nproc=nproc) - else: - mmcv.track_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=True), train_list) - mmcv.track_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=False), test_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/coco_stuff164k.py b/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/coco_stuff164k.py deleted file mode 100644 index 6d8e2f2a3..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/coco_stuff164k.py +++ /dev/null @@ -1,264 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import shutil -from functools import partial -from glob import glob - -import mmcv -import numpy as np -from PIL import Image - -COCO_LEN = 123287 - -clsID_to_trID = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 12: 11, - 13: 12, - 14: 13, - 15: 14, - 16: 15, - 17: 16, - 18: 17, - 19: 18, - 20: 19, - 21: 20, - 22: 21, - 23: 22, - 24: 23, - 26: 24, - 27: 25, - 30: 26, - 31: 27, - 32: 28, - 33: 29, - 34: 30, - 35: 31, - 36: 32, - 37: 33, - 38: 34, - 39: 35, - 40: 36, - 41: 37, - 42: 38, - 43: 39, - 45: 40, - 46: 41, - 47: 42, - 48: 43, - 49: 44, - 50: 45, - 51: 46, - 52: 47, - 53: 48, - 54: 49, - 55: 50, - 56: 51, - 57: 52, - 58: 53, - 59: 54, - 60: 55, - 61: 56, - 62: 57, - 63: 58, - 64: 59, - 66: 60, - 69: 61, - 71: 62, - 72: 63, - 73: 64, - 74: 65, - 75: 66, - 76: 67, - 77: 68, - 78: 69, - 79: 70, - 80: 71, - 81: 72, - 83: 73, - 84: 74, - 85: 75, - 86: 76, - 87: 77, - 88: 78, - 89: 79, - 91: 80, - 92: 81, - 93: 82, - 94: 83, - 95: 84, - 96: 85, - 97: 86, - 98: 87, - 99: 88, - 100: 89, - 101: 90, - 102: 91, - 103: 92, - 104: 93, - 105: 94, - 106: 95, - 107: 96, - 108: 97, - 109: 98, - 110: 99, - 111: 100, - 112: 101, - 113: 102, - 114: 103, - 115: 104, - 116: 105, - 117: 106, - 118: 107, - 119: 108, - 120: 109, - 121: 110, - 122: 111, - 123: 112, - 124: 113, - 125: 114, - 126: 115, - 127: 116, - 128: 117, - 129: 118, - 130: 119, - 131: 120, - 132: 121, - 133: 122, - 134: 123, - 135: 124, - 136: 125, - 137: 126, - 138: 127, - 139: 128, - 140: 129, - 141: 130, - 142: 131, - 143: 132, - 144: 133, - 145: 134, - 146: 135, - 147: 136, - 148: 137, - 149: 138, - 150: 139, - 151: 140, - 152: 141, - 153: 142, - 154: 143, - 155: 144, - 156: 145, - 157: 146, - 158: 147, - 159: 148, - 160: 149, - 161: 150, - 162: 151, - 163: 152, - 164: 153, - 165: 154, - 166: 155, - 167: 156, - 168: 157, - 169: 158, - 170: 159, - 171: 160, - 172: 161, - 173: 162, - 174: 163, - 175: 164, - 176: 165, - 177: 166, - 178: 167, - 179: 168, - 180: 169, - 181: 170, - 255: 255 -} - - -def convert_to_trainID(maskpath, out_mask_dir, is_train): - mask = np.array(Image.open(maskpath)) - mask_copy = mask.copy() - for clsID, trID in clsID_to_trID.items(): - mask_copy[mask == clsID] = trID - seg_filename = osp.join( - out_mask_dir, 'train2017', - osp.basename(maskpath).split('.')[0] + - '_labelTrainIds.png') if is_train else osp.join( - out_mask_dir, 'val2017', - osp.basename(maskpath).split('.')[0] + '_labelTrainIds.png') - Image.fromarray(mask_copy).save(seg_filename, 'PNG') - - -def parse_args(): - parser = argparse.ArgumentParser( - description=\ - 'Convert COCO Stuff 164k annotations to mmsegmentation format') # noqa - parser.add_argument('coco_path', help='coco stuff path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=16, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - coco_path = args.coco_path - nproc = args.nproc - - out_dir = args.out_dir or coco_path - out_img_dir = osp.join(out_dir, 'images') - out_mask_dir = osp.join(out_dir, 'annotations') - - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'train2017')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'val2017')) - - if out_dir != coco_path: - shutil.copytree(osp.join(coco_path, 'images'), out_img_dir) - - train_list = glob(osp.join(coco_path, 'annotations', 'train2017', '*.png')) - train_list = [file for file in train_list if '_labelTrainIds' not in file] - test_list = glob(osp.join(coco_path, 'annotations', 'val2017', '*.png')) - test_list = [file for file in test_list if '_labelTrainIds' not in file] - assert (len(train_list) + - len(test_list)) == COCO_LEN, 'Wrong length of list {} & {}'.format( - len(train_list), len(test_list)) - - if args.nproc > 1: - mmcv.track_parallel_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=True), - train_list, - nproc=nproc) - mmcv.track_parallel_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=False), - test_list, - nproc=nproc) - else: - mmcv.track_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=True), - train_list) - mmcv.track_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=False), - test_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/pascal_context.py b/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/pascal_context.py deleted file mode 100644 index 03b79d518..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/pascal_context.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -from functools import partial - -import mmcv -import numpy as np -from detail import Detail -from PIL import Image - -_mapping = np.sort( - np.array([ - 0, 2, 259, 260, 415, 324, 9, 258, 144, 18, 19, 22, 23, 397, 25, 284, - 158, 159, 416, 33, 162, 420, 454, 295, 296, 427, 44, 45, 46, 308, 59, - 440, 445, 31, 232, 65, 354, 424, 68, 326, 72, 458, 34, 207, 80, 355, - 85, 347, 220, 349, 360, 98, 187, 104, 105, 366, 189, 368, 113, 115 - ])) -_key = np.array(range(len(_mapping))).astype('uint8') - - -def generate_labels(img_id, detail, out_dir): - - def _class_to_index(mask, _mapping, _key): - # assert the values - values = np.unique(mask) - for i in range(len(values)): - assert (values[i] in _mapping) - index = np.digitize(mask.ravel(), _mapping, right=True) - return _key[index].reshape(mask.shape) - - mask = Image.fromarray( - _class_to_index(detail.getMask(img_id), _mapping=_mapping, _key=_key)) - filename = img_id['file_name'] - mask.save(osp.join(out_dir, filename.replace('jpg', 'png'))) - return osp.splitext(osp.basename(filename))[0] - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert PASCAL VOC annotations to mmsegmentation format') - parser.add_argument('devkit_path', help='pascal voc devkit path') - parser.add_argument('json_path', help='annoation json filepath') - parser.add_argument('-o', '--out_dir', help='output path') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - devkit_path = args.devkit_path - if args.out_dir is None: - out_dir = osp.join(devkit_path, 'VOC2010', 'SegmentationClassContext') - else: - out_dir = args.out_dir - json_path = args.json_path - mmcv.mkdir_or_exist(out_dir) - img_dir = osp.join(devkit_path, 'VOC2010', 'JPEGImages') - - train_detail = Detail(json_path, img_dir, 'train') - train_ids = train_detail.getImgs() - - val_detail = Detail(json_path, img_dir, 'val') - val_ids = val_detail.getImgs() - - mmcv.mkdir_or_exist( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext')) - - train_list = mmcv.track_progress( - partial(generate_labels, detail=train_detail, out_dir=out_dir), - train_ids) - with open( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext', - 'train.txt'), 'w') as f: - f.writelines(line + '\n' for line in sorted(train_list)) - - val_list = mmcv.track_progress( - partial(generate_labels, detail=val_detail, out_dir=out_dir), val_ids) - with open( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext', - 'val.txt'), 'w') as f: - f.writelines(line + '\n' for line in sorted(val_list)) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/voc_aug.py b/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/voc_aug.py deleted file mode 100644 index 1d42c2704..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/tools/convert_datasets/voc_aug.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -from functools import partial - -import mmcv -import numpy as np -from PIL import Image -from scipy.io import loadmat - -AUG_LEN = 10582 - - -def convert_mat(mat_file, in_dir, out_dir): - data = loadmat(osp.join(in_dir, mat_file)) - mask = data['GTcls'][0]['Segmentation'][0].astype(np.uint8) - seg_filename = osp.join(out_dir, mat_file.replace('.mat', '.png')) - Image.fromarray(mask).save(seg_filename, 'PNG') - - -def generate_aug_list(merged_list, excluded_list): - return list(set(merged_list) - set(excluded_list)) - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert PASCAL VOC annotations to mmsegmentation format') - parser.add_argument('devkit_path', help='pascal voc devkit path') - parser.add_argument('aug_path', help='pascal voc aug path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=1, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - devkit_path = args.devkit_path - aug_path = args.aug_path - nproc = args.nproc - if args.out_dir is None: - out_dir = osp.join(devkit_path, 'VOC2012', 'SegmentationClassAug') - else: - out_dir = args.out_dir - mmcv.mkdir_or_exist(out_dir) - in_dir = osp.join(aug_path, 'dataset', 'cls') - - mmcv.track_parallel_progress( - partial(convert_mat, in_dir=in_dir, out_dir=out_dir), - list(mmcv.scandir(in_dir, suffix='.mat')), - nproc=nproc) - - full_aug_list = [] - with open(osp.join(aug_path, 'dataset', 'train.txt')) as f: - full_aug_list += [line.strip() for line in f] - with open(osp.join(aug_path, 'dataset', 'val.txt')) as f: - full_aug_list += [line.strip() for line in f] - - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'train.txt')) as f: - ori_train_list = [line.strip() for line in f] - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'val.txt')) as f: - val_list = [line.strip() for line in f] - - aug_train_list = generate_aug_list(ori_train_list + full_aug_list, - val_list) - assert len(aug_train_list) == AUG_LEN, 'len(aug_train_list) != {}'.format( - AUG_LEN) - - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'trainaug.txt'), 'w') as f: - f.writelines(line + '\n' for line in aug_train_list) - - aug_list = generate_aug_list(full_aug_list, ori_train_list + val_list) - assert len(aug_list) == AUG_LEN - len( - ori_train_list), 'len(aug_list) != {}'.format(AUG_LEN - - len(ori_train_list)) - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', 'aug.txt'), - 'w') as f: - f.writelines(line + '\n' for line in aug_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/att_unet/pytorch/tools/get_flops.py b/cv/semantic_segmentation/att_unet/pytorch/tools/get_flops.py deleted file mode 100644 index e30c36fdf..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/tools/get_flops.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse - -from mmcv import Config -from mmcv.cnn import get_model_complexity_info - -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Get the FLOPs of a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument( - '--shape', - type=int, - nargs='+', - default=[2048, 1024], - help='input image size') - args = parser.parse_args() - return args - - -def main(): - - args = parse_args() - - if len(args.shape) == 1: - input_shape = (3, args.shape[0], args.shape[0]) - elif len(args.shape) == 2: - input_shape = (3, ) + tuple(args.shape) - else: - raise ValueError('invalid input shape') - - cfg = Config.fromfile(args.config) - cfg.model.pretrained = None - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')).cuda() - model.eval() - - if hasattr(model, 'forward_dummy'): - model.forward = model.forward_dummy - else: - raise NotImplementedError( - 'FLOPs counter is currently not currently supported with {}'. - format(model.__class__.__name__)) - - flops, params = get_model_complexity_info(model, input_shape) - split_line = '=' * 30 - print('{0}\nInput shape: {1}\nFlops: {2}\nParams: {3}\n{0}'.format( - split_line, input_shape, flops, params)) - print('!!!Please be cautious if you use the results in papers. ' - 'You may need to check if all ops are supported and verify that the ' - 'flops computation is correct.') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/att_unet/pytorch/tools/print_config.py b/cv/semantic_segmentation/att_unet/pytorch/tools/print_config.py deleted file mode 100644 index 3f9c08dd9..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/tools/print_config.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import warnings - -from mmcv import Config, DictAction - -from mmseg.apis import init_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='Print the whole config') - parser.add_argument('config', help='config file path') - parser.add_argument( - '--graph', action='store_true', help='print the models graph') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - args = parser.parse_args() - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options, ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - print(f'Config:\n{cfg.pretty_text}') - # dump config - cfg.dump('example.py') - # dump models graph - if args.graph: - model = init_segmentor(args.config, device='cpu') - print(f'Model graph:\n{str(model)}') - with open('example-graph.txt', 'w') as f: - f.writelines(str(model)) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/att_unet/pytorch/tools/slurm_test.sh b/cv/semantic_segmentation/att_unet/pytorch/tools/slurm_test.sh deleted file mode 100755 index 4e6f7bf4e..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/tools/slurm_test.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -set -x - -PARTITION=$1 -JOB_NAME=$2 -CONFIG=$3 -CHECKPOINT=$4 -GPUS=${GPUS:-4} -GPUS_PER_NODE=${GPUS_PER_NODE:-4} -CPUS_PER_TASK=${CPUS_PER_TASK:-5} -PY_ARGS=${@:5} -SRUN_ARGS=${SRUN_ARGS:-""} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -srun -p ${PARTITION} \ - --job-name=${JOB_NAME} \ - --gres=gpu:${GPUS_PER_NODE} \ - --ntasks=${GPUS} \ - --ntasks-per-node=${GPUS_PER_NODE} \ - --cpus-per-task=${CPUS_PER_TASK} \ - --kill-on-bad-exit=1 \ - ${SRUN_ARGS} \ - python -u tools/test.py ${CONFIG} ${CHECKPOINT} --launcher="slurm" ${PY_ARGS} diff --git a/cv/semantic_segmentation/att_unet/pytorch/tools/slurm_train.sh b/cv/semantic_segmentation/att_unet/pytorch/tools/slurm_train.sh deleted file mode 100755 index ab232105f..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/tools/slurm_train.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -set -x - -PARTITION=$1 -JOB_NAME=$2 -CONFIG=$3 -GPUS=${GPUS:-4} -GPUS_PER_NODE=${GPUS_PER_NODE:-4} -CPUS_PER_TASK=${CPUS_PER_TASK:-5} -SRUN_ARGS=${SRUN_ARGS:-""} -PY_ARGS=${@:4} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -srun -p ${PARTITION} \ - --job-name=${JOB_NAME} \ - --gres=gpu:${GPUS_PER_NODE} \ - --ntasks=${GPUS} \ - --ntasks-per-node=${GPUS_PER_NODE} \ - --cpus-per-task=${CPUS_PER_TASK} \ - --kill-on-bad-exit=1 \ - ${SRUN_ARGS} \ - python -u tools/train.py ${CONFIG} --launcher="slurm" ${PY_ARGS} diff --git a/cv/semantic_segmentation/att_unet/pytorch/tools/test.py b/cv/semantic_segmentation/att_unet/pytorch/tools/test.py deleted file mode 100644 index 12892ec9b..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/tools/test.py +++ /dev/null @@ -1,319 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os -import os.path as osp -import shutil -import time -import warnings - -import mmcv -import torch -from mmcv.cnn.utils import revert_sync_batchnorm -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (get_dist_info, init_dist, load_checkpoint, - wrap_fp16_model) -from mmcv.utils import DictAction - -from mmseg import digit_version -from mmseg.apis import multi_gpu_test, single_gpu_test -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor -from mmseg.utils import setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser( - description='mmseg test (and eval) a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--work-dir', - help=('if specified, the evaluation metric results will be dumped' - 'into the directory as json')) - parser.add_argument( - '--aug-test', action='store_true', help='Use Flip and Multi scale aug') - parser.add_argument('--out', help='output result file in pickle format') - parser.add_argument( - '--format-only', - action='store_true', - help='Format the output results without perform evaluation. It is' - 'useful when you want to format the result to a specific format and ' - 'submit it to the test server') - parser.add_argument( - '--eval', - type=str, - nargs='+', - help='evaluation metrics, which depends on the dataset, e.g., "mIoU"' - ' for generic datasets, and "cityscapes" for Cityscapes') - parser.add_argument('--show', action='store_true', help='show results') - parser.add_argument( - '--show-dir', help='directory where painted images will be saved') - parser.add_argument( - '--gpu-collect', - action='store_true', - help='whether to use gpu to collect results.') - parser.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed testing)') - parser.add_argument( - '--tmpdir', - help='tmp directory used for collecting results from multiple ' - 'workers, available when gpu_collect is not specified') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--eval-options', - nargs='+', - action=DictAction, - help='custom options for evaluation') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument( - '--opacity', - type=float, - default=0.5, - help='Opacity of painted segmentation map. In (0, 1] range.') - parser.add_argument('--local_rank', type=int, default=0) - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - assert args.out or args.eval or args.format_only or args.show \ - or args.show_dir, \ - ('Please specify at least one operation (save/eval/format/show the ' - 'results / save the results) with the argument "--out", "--eval"' - ', "--format-only", "--show" or "--show-dir"') - - if args.eval and args.format_only: - raise ValueError('--eval and --format_only cannot be both specified') - - if args.out is not None and not args.out.endswith(('.pkl', '.pickle')): - raise ValueError('The output file must be a pkl file.') - - cfg = mmcv.Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - if args.aug_test: - # hard code index - cfg.data.test.pipeline[1].img_ratios = [ - 0.5, 0.75, 1.0, 1.25, 1.5, 1.75 - ] - cfg.data.test.pipeline[1].flip = True - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - if args.gpu_id is not None: - cfg.gpu_ids = [args.gpu_id] - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - cfg.gpu_ids = [args.gpu_id] - distributed = False - if len(cfg.gpu_ids) > 1: - warnings.warn(f'The gpu-ids is reset from {cfg.gpu_ids} to ' - f'{cfg.gpu_ids[0:1]} to avoid potential error in ' - 'non-distribute testing time.') - cfg.gpu_ids = cfg.gpu_ids[0:1] - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - - rank, _ = get_dist_info() - # allows not to create - if args.work_dir is not None and rank == 0: - mmcv.mkdir_or_exist(osp.abspath(args.work_dir)) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.aug_test: - json_file = osp.join(args.work_dir, - f'eval_multi_scale_{timestamp}.json') - else: - json_file = osp.join(args.work_dir, - f'eval_single_scale_{timestamp}.json') - elif rank == 0: - work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - mmcv.mkdir_or_exist(osp.abspath(work_dir)) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.aug_test: - json_file = osp.join(work_dir, - f'eval_multi_scale_{timestamp}.json') - else: - json_file = osp.join(work_dir, - f'eval_single_scale_{timestamp}.json') - - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - # The default loader config - loader_cfg = dict( - # cfg.gpus will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - shuffle=False) - # The overall dataloader settings - loader_cfg.update({ - k: v - for k, v in cfg.data.items() if k not in [ - 'train', 'val', 'test', 'train_dataloader', 'val_dataloader', - 'test_dataloader' - ] - }) - test_loader_cfg = { - **loader_cfg, - 'samples_per_gpu': 1, - 'shuffle': False, # Not shuffle by default - **cfg.data.get('test_dataloader', {}) - } - # build the dataloader - data_loader = build_dataloader(dataset, **test_loader_cfg) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - checkpoint = load_checkpoint(model, args.checkpoint, map_location='cpu') - if 'CLASSES' in checkpoint.get('meta', {}): - model.CLASSES = checkpoint['meta']['CLASSES'] - else: - print('"CLASSES" not found in meta, use dataset.CLASSES instead') - model.CLASSES = dataset.CLASSES - if 'PALETTE' in checkpoint.get('meta', {}): - model.PALETTE = checkpoint['meta']['PALETTE'] - else: - print('"PALETTE" not found in meta, use dataset.PALETTE instead') - model.PALETTE = dataset.PALETTE - - # clean gpu memory when starting a new evaluation. - torch.cuda.empty_cache() - eval_kwargs = {} if args.eval_options is None else args.eval_options - - # Deprecated - efficient_test = eval_kwargs.get('efficient_test', False) - if efficient_test: - warnings.warn( - '``efficient_test=True`` does not have effect in tools/test.py, ' - 'the evaluation and format results are CPU memory efficient by ' - 'default') - - eval_on_format_results = ( - args.eval is not None and 'cityscapes' in args.eval) - if eval_on_format_results: - assert len(args.eval) == 1, 'eval on format results is not ' \ - 'applicable for metrics other than ' \ - 'cityscapes' - if args.format_only or eval_on_format_results: - if 'imgfile_prefix' in eval_kwargs: - tmpdir = eval_kwargs['imgfile_prefix'] - else: - tmpdir = '.format_cityscapes' - eval_kwargs.setdefault('imgfile_prefix', tmpdir) - mmcv.mkdir_or_exist(tmpdir) - else: - tmpdir = None - - if not distributed: - warnings.warn( - 'SyncBN is only supported with DDP. To be compatible with DP, ' - 'we convert SyncBN to BN. Please use dist_train.sh which can ' - 'avoid this error.') - if not torch.cuda.is_available(): - assert digit_version(mmcv.__version__) >= digit_version('1.4.4'), \ - 'Please use MMCV >= 1.4.4 for CPU training!' - model = revert_sync_batchnorm(model) - model = MMDataParallel(model, device_ids=cfg.gpu_ids) - results = single_gpu_test( - model, - data_loader, - args.show, - args.show_dir, - False, - args.opacity, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - else: - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False) - results = multi_gpu_test( - model, - data_loader, - args.tmpdir, - args.gpu_collect, - False, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - - rank, _ = get_dist_info() - if rank == 0: - if args.out: - warnings.warn( - 'The behavior of ``args.out`` has been changed since MMSeg ' - 'v0.16, the pickled outputs could be seg map as type of ' - 'np.array, pre-eval results or file paths for ' - '``dataset.format_results()``.') - print(f'\nwriting results to {args.out}') - mmcv.dump(results, args.out) - if args.eval: - eval_kwargs.update(metric=args.eval) - metric = dataset.evaluate(results, **eval_kwargs) - metric_dict = dict(config=args.config, metric=metric) - mmcv.dump(metric_dict, json_file, indent=4) - if tmpdir is not None and eval_on_format_results: - # remove tmp dir when cityscapes evaluation - shutil.rmtree(tmpdir) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/att_unet/pytorch/train.py b/cv/semantic_segmentation/att_unet/pytorch/train.py deleted file mode 100644 index e198dd60f..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/train.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import copy -import os -import os.path as osp -import time -import warnings - -import mmcv -import torch -import torch.distributed as dist -from mmcv.cnn.utils import revert_sync_batchnorm -from mmcv.runner import get_dist_info, init_dist -from mmcv.utils import Config, DictAction, get_git_hash - -from mmseg import __version__ -from mmseg.apis import init_random_seed, set_random_seed, train_segmentor -from mmseg.datasets import build_dataset -from mmseg.models import build_segmentor -from mmseg.utils import collect_env, get_root_logger, setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument('--work-dir', help='the dir to save logs and models') - parser.add_argument( - '--load-from', help='the checkpoint file to load weights from') - parser.add_argument( - '--resume-from', help='the checkpoint file to resume from') - parser.add_argument( - '--no-validate', - action='store_true', - help='whether not to evaluate the checkpoint during training') - group_gpus = parser.add_mutually_exclusive_group() - group_gpus.add_argument( - '--gpus', - type=int, - help='(Deprecated, please use --gpu-id) number of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-ids', - type=int, - nargs='+', - help='(Deprecated, please use --gpu-id) ids of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed training)') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--diff_seed', - action='store_true', - help='Whether or not set different seeds for different ranks') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - parser.add_argument('--dist_backend', type=str, default=None) - parser.add_argument( - '--auto-resume', - action='store_true', - help='resume from the latest checkpoint automatically.') - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - - # work_dir is determined in this priority: CLI > segment in file > filename - if args.work_dir is not None: - # update configs according to CLI args if args.work_dir is not None - cfg.work_dir = args.work_dir - elif cfg.get('work_dir', None) is None: - # use config filename as default work_dir if cfg.work_dir is None - cfg.work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - if args.load_from is not None: - cfg.load_from = args.load_from - if args.resume_from is not None: - cfg.resume_from = args.resume_from - if args.gpus is not None: - cfg.gpu_ids = range(1) - warnings.warn('`--gpus` is deprecated because we only support ' - 'single GPU mode in non-distributed training. ' - 'Use `gpus=1` now.') - if args.gpu_ids is not None: - cfg.gpu_ids = args.gpu_ids[0:1] - warnings.warn('`--gpu-ids` is deprecated, please use `--gpu-id`. ' - 'Because we only support single GPU mode in ' - 'non-distributed training. Use the first GPU ' - 'in `gpu_ids` now.') - if args.gpus is None and args.gpu_ids is None: - cfg.gpu_ids = [args.gpu_id] - - cfg.auto_resume = args.auto_resume - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - if args.dist_backend is not None: - cfg.dist_params.backend = args.dist_backend - init_dist(args.launcher, **cfg.dist_params) - # gpu_ids is used to calculate iter when resuming checkpoint - _, world_size = get_dist_info() - cfg.gpu_ids = range(world_size) - - # create work_dir - mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) - # dump config - cfg.dump(osp.join(cfg.work_dir, osp.basename(args.config))) - # init the logger before other steps - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - log_file = osp.join(cfg.work_dir, f'{timestamp}.log') - logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) - - # set multi-process settings - setup_multi_processes(cfg) - - # init the meta dict to record some important information such as - # environment info and seed, which will be logged - meta = dict() - # log env info - env_info_dict = collect_env() - env_info = '\n'.join([f'{k}: {v}' for k, v in env_info_dict.items()]) - dash_line = '-' * 60 + '\n' - logger.info('Environment info:\n' + dash_line + env_info + '\n' + - dash_line) - meta['env_info'] = env_info - - # log some basic info - logger.info(f'Distributed training: {distributed}') - logger.info(f'Config:\n{cfg.pretty_text}') - - # set random seeds - seed = init_random_seed(args.seed) - seed = seed + dist.get_rank() if args.diff_seed else seed - logger.info(f'Set random seed to {seed}, ' - f'deterministic: {args.deterministic}') - set_random_seed(seed, deterministic=args.deterministic) - cfg.seed = seed - meta['seed'] = seed - meta['exp_name'] = osp.basename(args.config) - - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')) - model.init_weights() - - # SyncBN is not support for DP - if not distributed: - warnings.warn( - 'SyncBN is only supported with DDP. To be compatible with DP, ' - 'we convert SyncBN to BN. Please use dist_train.sh which can ' - 'avoid this error.') - model = revert_sync_batchnorm(model) - - logger.info(model) - - datasets = [build_dataset(cfg.data.train)] - if len(cfg.workflow) == 2: - val_dataset = copy.deepcopy(cfg.data.val) - val_dataset.pipeline = cfg.data.train.pipeline - datasets.append(build_dataset(val_dataset)) - if cfg.checkpoint_config is not None: - # save mmseg version, config file content and class names in - # checkpoints as meta data - cfg.checkpoint_config.meta = dict( - mmseg_version=f'{__version__}+{get_git_hash()[:7]}', - config=cfg.pretty_text, - CLASSES=datasets[0].CLASSES, - PALETTE=datasets[0].PALETTE) - # add an attribute for visualization convenience - model.CLASSES = datasets[0].CLASSES - # passing checkpoint meta for saving best checkpoint - meta.update(cfg.checkpoint_config.meta) - train_segmentor( - model, - datasets, - cfg, - distributed=distributed, - validate=(not args.no_validate), - timestamp=timestamp, - meta=meta) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/att_unet/pytorch/train_dist.sh b/cv/semantic_segmentation/att_unet/pytorch/train_dist.sh deleted file mode 100755 index d09675129..000000000 --- a/cv/semantic_segmentation/att_unet/pytorch/train_dist.sh +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - -CONFIG=$1 -GPUS=$2 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/train.py \ - $CONFIG \ - --launcher pytorch ${@:3} -- Gitee From 5b53c98c9a25a240c651d9860d0581ec189c500e Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Wed, 5 Mar 2025 17:59:39 +0800 Subject: [PATCH 05/15] update ddrnet --- cv/3d_detection/bevformer/pytorch/README.md | 2 +- .../ddrnet/pytorch/.gitignore | 120 -- .../ddrnet/pytorch/CITATION.cff | 8 - .../ddrnet/pytorch/LICENSE | 203 --- .../ddrnet/pytorch/README.md | 110 +- .../pytorch/configs/_base_/datasets/ade20k.py | 54 - .../configs/_base_/datasets/ade20k_640x640.py | 54 - .../configs/_base_/datasets/cityscapes.py | 54 - .../_base_/datasets/cityscapes_1024x1024.py | 35 - .../_base_/datasets/cityscapes_768x768.py | 35 - .../_base_/datasets/cityscapes_769x769.py | 35 - .../_base_/datasets/cityscapes_832x832.py | 35 - .../configs/_base_/datasets/coco-stuff10k.py | 57 - .../configs/_base_/datasets/coco-stuff164k.py | 54 - .../configs/_base_/datasets/pascal_context.py | 60 - .../_base_/datasets/pascal_context_59.py | 60 - .../configs/_base_/datasets/pascal_voc12.py | 58 - .../_base_/datasets/pascal_voc12_aug.py | 9 - .../pytorch/configs/_base_/default_runtime.py | 14 - .../configs/_base_/models/ddrnet_23_slim.py | 37 - .../configs/_base_/schedules/schedule_160k.py | 9 - .../configs/_base_/schedules/schedule_1k.py | 9 - .../configs/_base_/schedules/schedule_20k.py | 9 - .../configs/_base_/schedules/schedule_320k.py | 9 - .../configs/_base_/schedules/schedule_40k.py | 9 - .../configs/_base_/schedules/schedule_80k.py | 9 - ...ddrnet_23_slim_512x1024_160k_cityscapes.py | 6 - .../ddrnet/pytorch/docker/Dockerfile | 32 - .../ddrnet/pytorch/docker/serve/Dockerfile | 49 - .../pytorch/docker/serve/config.properties | 5 - .../ddrnet/pytorch/docker/serve/entrypoint.sh | 12 - .../ddrnet/pytorch/mmcv/__init__.py | 13 - .../ddrnet/pytorch/mmcv/cnn/__init__.py | 21 - .../pytorch/mmcv/cnn/bricks/__init__.py | 35 - .../pytorch/mmcv/cnn/bricks/activation.py | 93 -- .../pytorch/mmcv/cnn/bricks/context_block.py | 125 -- .../ddrnet/pytorch/mmcv/cnn/bricks/conv.py | 44 - .../cnn/bricks/conv2d_adaptive_padding.py | 62 - .../pytorch/mmcv/cnn/bricks/conv_module.py | 206 --- .../ddrnet/pytorch/mmcv/cnn/bricks/conv_ws.py | 148 -- .../bricks/depthwise_separable_conv_module.py | 96 -- .../ddrnet/pytorch/mmcv/cnn/bricks/drop.py | 65 - .../mmcv/cnn/bricks/generalized_attention.py | 412 ----- .../pytorch/mmcv/cnn/bricks/hsigmoid.py | 46 - .../ddrnet/pytorch/mmcv/cnn/bricks/hswish.py | 38 - .../pytorch/mmcv/cnn/bricks/non_local.py | 306 ---- .../ddrnet/pytorch/mmcv/cnn/bricks/norm.py | 144 -- .../ddrnet/pytorch/mmcv/cnn/bricks/padding.py | 36 - .../ddrnet/pytorch/mmcv/cnn/bricks/plugin.py | 89 -- .../pytorch/mmcv/cnn/bricks/registry.py | 16 - .../ddrnet/pytorch/mmcv/cnn/bricks/scale.py | 21 - .../ddrnet/pytorch/mmcv/cnn/bricks/swish.py | 25 - .../pytorch/mmcv/cnn/bricks/transformer.py | 944 ------------ .../pytorch/mmcv/cnn/bricks/upsample.py | 84 -- .../pytorch/mmcv/cnn/bricks/wrappers.py | 180 --- .../ddrnet/pytorch/mmcv/cnn/builder.py | 30 - .../ddrnet/pytorch/mmcv/cnn/utils/__init__.py | 19 - .../pytorch/mmcv/cnn/utils/flops_counter.py | 599 -------- .../pytorch/mmcv/cnn/utils/fuse_conv_bn.py | 59 - .../ddrnet/pytorch/mmcv/cnn/utils/sync_bn.py | 60 - .../pytorch/mmcv/cnn/utils/weight_init.py | 685 --------- .../ddrnet/pytorch/mmcv/device/__init__.py | 4 - .../pytorch/mmcv/device/ipu/__init__.py | 14 - .../pytorch/mmcv/device/ipu/dataloader.py | 157 -- .../device/ipu/hierarchical_data_manager.py | 243 --- .../pytorch/mmcv/device/ipu/hook_wrapper.py | 105 -- .../pytorch/mmcv/device/ipu/model_wrapper.py | 721 --------- .../ddrnet/pytorch/mmcv/device/ipu/runner.py | 142 -- .../ddrnet/pytorch/mmcv/device/ipu/utils.py | 244 --- .../pytorch/mmcv/device/mlu/__init__.py | 9 - .../pytorch/mmcv/device/mlu/_functions.py | 22 - .../pytorch/mmcv/device/mlu/data_parallel.py | 41 - .../pytorch/mmcv/device/mlu/distributed.py | 20 - .../pytorch/mmcv/device/mlu/scatter_gather.py | 59 - .../ddrnet/pytorch/mmcv/engine/__init__.py | 8 - .../ddrnet/pytorch/mmcv/engine/test.py | 202 --- .../ddrnet/pytorch/mmcv/fileio/__init__.py | 11 - .../ddrnet/pytorch/mmcv/fileio/file_client.py | 1163 -------------- .../pytorch/mmcv/fileio/handlers/__init__.py | 7 - .../pytorch/mmcv/fileio/handlers/base.py | 30 - .../mmcv/fileio/handlers/json_handler.py | 36 - .../mmcv/fileio/handlers/pickle_handler.py | 28 - .../mmcv/fileio/handlers/yaml_handler.py | 25 - .../ddrnet/pytorch/mmcv/fileio/io.py | 151 -- .../ddrnet/pytorch/mmcv/fileio/parse.py | 97 -- .../ddrnet/pytorch/mmcv/image/__init__.py | 28 - .../ddrnet/pytorch/mmcv/image/colorspace.py | 306 ---- .../ddrnet/pytorch/mmcv/image/geometric.py | 741 --------- .../ddrnet/pytorch/mmcv/image/io.py | 314 ---- .../ddrnet/pytorch/mmcv/image/misc.py | 53 - .../ddrnet/pytorch/mmcv/image/photometric.py | 428 ------ .../pytorch/mmcv/model_zoo/deprecated.json | 6 - .../ddrnet/pytorch/mmcv/model_zoo/mmcls.json | 59 - .../pytorch/mmcv/model_zoo/open_mmlab.json | 50 - .../mmcv/model_zoo/torchvision_0.12.json | 57 - .../ddrnet/pytorch/mmcv/ops/__init__.py | 12 - .../ddrnet/pytorch/mmcv/ops/cc_attention.py | 84 -- .../ddrnet/pytorch/mmcv/ops/csrc/README.md | 170 --- .../csrc/common/cuda/common_cuda_helper.hpp | 120 -- .../csrc/common/cuda/psamask_cuda_kernel.cuh | 141 -- .../cuda/sigmoid_focal_loss_cuda_kernel.cuh | 71 - .../cuda/softmax_focal_loss_cuda_kernel.cuh | 72 - .../csrc/common/cuda/sync_bn_cuda_kernel.cuh | 331 ---- .../ops/csrc/common/pytorch_cpp_helper.hpp | 27 - .../ops/csrc/common/pytorch_cuda_helper.hpp | 19 - .../csrc/common/pytorch_device_registry.hpp | 141 -- .../mmcv/ops/csrc/pytorch/cuda/cudabind.cpp | 210 --- .../ops/csrc/pytorch/cuda/focal_loss_cuda.cu | 111 -- .../ops/csrc/pytorch/cuda/psamask_cuda.cu | 60 - .../ops/csrc/pytorch/cuda/sync_bn_cuda.cu | 110 -- .../mmcv/ops/csrc/pytorch/focal_loss.cpp | 53 - .../pytorch/mmcv/ops/csrc/pytorch/info.cpp | 56 - .../pytorch/mmcv/ops/csrc/pytorch/psamask.cpp | 41 - .../pytorch/mmcv/ops/csrc/pytorch/pybind.cpp | 106 -- .../pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp | 69 - .../pytorch/mmcv/ops/deprecated_wrappers.py | 46 - .../ddrnet/pytorch/mmcv/ops/focal_loss.py | 213 --- .../ddrnet/pytorch/mmcv/ops/info.py | 36 - .../ddrnet/pytorch/mmcv/ops/point_sample.py | 346 ----- .../ddrnet/pytorch/mmcv/ops/psa_mask.py | 92 -- .../ddrnet/pytorch/mmcv/ops/sync_bn.py | 279 ---- .../ddrnet/pytorch/mmcv/parallel/__init__.py | 13 - .../pytorch/mmcv/parallel/_functions.py | 76 - .../ddrnet/pytorch/mmcv/parallel/collate.py | 84 -- .../pytorch/mmcv/parallel/data_container.py | 89 -- .../pytorch/mmcv/parallel/data_parallel.py | 97 -- .../pytorch/mmcv/parallel/distributed.py | 138 -- .../mmcv/parallel/distributed_deprecated.py | 70 - .../ddrnet/pytorch/mmcv/parallel/registry.py | 8 - .../pytorch/mmcv/parallel/scatter_gather.py | 59 - .../ddrnet/pytorch/mmcv/parallel/utils.py | 20 - .../ddrnet/pytorch/mmcv/runner/__init__.py | 73 - .../ddrnet/pytorch/mmcv/runner/base_module.py | 208 --- .../ddrnet/pytorch/mmcv/runner/base_runner.py | 544 ------- .../ddrnet/pytorch/mmcv/runner/builder.py | 24 - .../ddrnet/pytorch/mmcv/runner/checkpoint.py | 759 ---------- .../mmcv/runner/default_constructor.py | 45 - .../ddrnet/pytorch/mmcv/runner/dist_utils.py | 204 --- .../pytorch/mmcv/runner/epoch_based_runner.py | 188 --- .../ddrnet/pytorch/mmcv/runner/fp16_utils.py | 423 ------ .../pytorch/mmcv/runner/hooks/__init__.py | 48 - .../pytorch/mmcv/runner/hooks/checkpoint.py | 167 --- .../pytorch/mmcv/runner/hooks/closure.py | 11 - .../ddrnet/pytorch/mmcv/runner/hooks/ema.py | 89 -- .../pytorch/mmcv/runner/hooks/evaluation.py | 511 ------- .../ddrnet/pytorch/mmcv/runner/hooks/hook.py | 92 -- .../pytorch/mmcv/runner/hooks/iter_timer.py | 31 - .../mmcv/runner/hooks/logger/__init__.py | 18 - .../pytorch/mmcv/runner/hooks/logger/base.py | 167 --- .../mmcv/runner/hooks/logger/clearml.py | 62 - .../mmcv/runner/hooks/logger/dvclive.py | 68 - .../mmcv/runner/hooks/logger/mlflow.py | 80 - .../mmcv/runner/hooks/logger/neptune.py | 88 -- .../pytorch/mmcv/runner/hooks/logger/pavi.py | 132 -- .../mmcv/runner/hooks/logger/segmind.py | 49 - .../mmcv/runner/hooks/logger/tensorboard.py | 69 - .../pytorch/mmcv/runner/hooks/logger/text.py | 256 ---- .../pytorch/mmcv/runner/hooks/logger/wandb.py | 107 -- .../pytorch/mmcv/runner/hooks/lr_updater.py | 730 --------- .../pytorch/mmcv/runner/hooks/memory.py | 25 - .../mmcv/runner/hooks/momentum_updater.py | 566 ------- .../pytorch/mmcv/runner/hooks/optimizer.py | 556 ------- .../pytorch/mmcv/runner/hooks/profiler.py | 180 --- .../pytorch/mmcv/runner/hooks/sampler_seed.py | 20 - .../pytorch/mmcv/runner/hooks/sync_buffer.py | 22 - .../pytorch/mmcv/runner/iter_based_runner.py | 273 ---- .../ddrnet/pytorch/mmcv/runner/log_buffer.py | 41 - .../pytorch/mmcv/runner/optimizer/__init__.py | 9 - .../pytorch/mmcv/runner/optimizer/builder.py | 44 - .../runner/optimizer/default_constructor.py | 250 --- .../ddrnet/pytorch/mmcv/runner/priority.py | 60 - .../ddrnet/pytorch/mmcv/runner/utils.py | 93 -- .../ddrnet/pytorch/mmcv/utils/__init__.py | 78 - .../ddrnet/pytorch/mmcv/utils/config.py | 719 --------- .../ddrnet/pytorch/mmcv/utils/device_type.py | 24 - .../ddrnet/pytorch/mmcv/utils/env.py | 120 -- .../ddrnet/pytorch/mmcv/utils/ext_loader.py | 72 - .../ddrnet/pytorch/mmcv/utils/hub.py | 131 -- .../ddrnet/pytorch/mmcv/utils/logging.py | 111 -- .../ddrnet/pytorch/mmcv/utils/misc.py | 377 ----- .../ddrnet/pytorch/mmcv/utils/parrots_jit.py | 41 - .../pytorch/mmcv/utils/parrots_wrapper.py | 114 -- .../ddrnet/pytorch/mmcv/utils/path.py | 101 -- .../ddrnet/pytorch/mmcv/utils/progressbar.py | 208 --- .../ddrnet/pytorch/mmcv/utils/registry.py | 337 ----- .../ddrnet/pytorch/mmcv/utils/seed.py | 23 - .../ddrnet/pytorch/mmcv/utils/testing.py | 141 -- .../ddrnet/pytorch/mmcv/utils/timer.py | 118 -- .../ddrnet/pytorch/mmcv/utils/trace.py | 24 - .../pytorch/mmcv/utils/version_utils.py | 90 -- .../ddrnet/pytorch/mmcv/version.py | 35 - .../ddrnet/pytorch/mmseg/__init__.py | 62 - .../ddrnet/pytorch/mmseg/apis/__init__.py | 11 - .../ddrnet/pytorch/mmseg/apis/inference.py | 136 -- .../ddrnet/pytorch/mmseg/apis/test.py | 233 --- .../ddrnet/pytorch/mmseg/apis/train.py | 196 --- .../ddrnet/pytorch/mmseg/core/__init__.py | 11 - .../ddrnet/pytorch/mmseg/core/builder.py | 33 - .../pytorch/mmseg/core/evaluation/__init__.py | 11 - .../mmseg/core/evaluation/class_names.py | 316 ---- .../mmseg/core/evaluation/eval_hooks.py | 128 -- .../pytorch/mmseg/core/evaluation/metrics.py | 395 ----- .../pytorch/mmseg/core/optimizers/__init__.py | 7 - .../layer_decay_optimizer_constructor.py | 208 --- .../ddrnet/pytorch/mmseg/core/seg/__init__.py | 5 - .../ddrnet/pytorch/mmseg/core/seg/builder.py | 9 - .../mmseg/core/seg/sampler/__init__.py | 5 - .../core/seg/sampler/base_pixel_sampler.py | 13 - .../core/seg/sampler/ohem_pixel_sampler.py | 85 -- .../pytorch/mmseg/core/utils/__init__.py | 5 - .../pytorch/mmseg/core/utils/dist_util.py | 46 - .../ddrnet/pytorch/mmseg/core/utils/misc.py | 18 - .../ddrnet/pytorch/mmseg/datasets/__init__.py | 10 - .../ddrnet/pytorch/mmseg/datasets/ade.py | 167 --- .../ddrnet/pytorch/mmseg/datasets/builder.py | 191 --- .../pytorch/mmseg/datasets/cityscapes.py | 214 --- .../pytorch/mmseg/datasets/coco_stuff.py | 94 -- .../ddrnet/pytorch/mmseg/datasets/custom.py | 487 ------ .../mmseg/datasets/dataset_wrappers.py | 277 ---- .../pytorch/mmseg/datasets/pascal_context.py | 103 -- .../mmseg/datasets/pipelines/__init__.py | 19 - .../mmseg/datasets/pipelines/compose.py | 52 - .../mmseg/datasets/pipelines/formating.py | 9 - .../mmseg/datasets/pipelines/formatting.py | 289 ---- .../mmseg/datasets/pipelines/loading.py | 158 -- .../mmseg/datasets/pipelines/test_time_aug.py | 134 -- .../mmseg/datasets/pipelines/transforms.py | 1335 ----------------- .../mmseg/datasets/samplers/__init__.py | 4 - .../datasets/samplers/distributed_sampler.py | 71 - .../ddrnet/pytorch/mmseg/datasets/voc.py | 39 - .../ddrnet/pytorch/mmseg/models/__init__.py | 13 - .../mmseg/models/backbones/__init__.py | 6 - .../mmseg/models/backbones/bisenetv1.py | 332 ---- .../pytorch/mmseg/models/backbones/ddrnet.py | 363 ----- .../pytorch/mmseg/models/backbones/resnest.py | 318 ---- .../pytorch/mmseg/models/backbones/resnet.py | 714 --------- .../pytorch/mmseg/models/backbones/resnext.py | 150 -- .../mmseg/models/backbones/timm_backbone.py | 63 - .../ddrnet/pytorch/mmseg/models/builder.py | 49 - .../mmseg/models/decode_heads/__init__.py | 6 - .../mmseg/models/decode_heads/aspp_head.py | 122 -- .../mmseg/models/decode_heads/cc_head.py | 43 - .../mmseg/models/decode_heads/ddr_head.py | 86 -- .../mmseg/models/decode_heads/decode_head.py | 266 ---- .../mmseg/models/decode_heads/fcn_head.py | 96 -- .../pytorch/mmseg/models/losses/__init__.py | 15 - .../pytorch/mmseg/models/losses/accuracy.py | 92 -- .../mmseg/models/losses/cross_entropy_loss.py | 296 ---- .../pytorch/mmseg/models/losses/dice_loss.py | 137 -- .../pytorch/mmseg/models/losses/focal_loss.py | 327 ---- .../mmseg/models/losses/lovasz_loss.py | 323 ---- .../pytorch/mmseg/models/losses/utils.py | 126 -- .../pytorch/mmseg/models/necks/__init__.py | 11 - .../mmseg/models/necks/featurepyramid.py | 67 - .../ddrnet/pytorch/mmseg/models/necks/fpn.py | 213 --- .../pytorch/mmseg/models/necks/ic_neck.py | 148 -- .../ddrnet/pytorch/mmseg/models/necks/jpu.py | 131 -- .../pytorch/mmseg/models/necks/mla_neck.py | 118 -- .../mmseg/models/necks/multilevel_neck.py | 78 - .../mmseg/models/segmentors/__init__.py | 6 - .../pytorch/mmseg/models/segmentors/base.py | 291 ---- .../segmentors/cascade_encoder_decoder.py | 88 -- .../models/segmentors/encoder_decoder.py | 284 ---- .../pytorch/mmseg/models/utils/__init__.py | 18 - .../pytorch/mmseg/models/utils/embed.py | 330 ---- .../mmseg/models/utils/inverted_residual.py | 213 --- .../mmseg/models/utils/make_divisible.py | 28 - .../pytorch/mmseg/models/utils/res_layer.py | 96 -- .../pytorch/mmseg/models/utils/se_layer.py | 58 - .../models/utils/self_attention_block.py | 160 -- .../mmseg/models/utils/shape_convert.py | 107 -- .../mmseg/models/utils/up_conv_block.py | 102 -- .../pytorch/mmseg/models/utils/wrappers.py | 51 - .../ddrnet/pytorch/mmseg/ops/__init__.py | 5 - .../ddrnet/pytorch/mmseg/ops/encoding.py | 75 - .../ddrnet/pytorch/mmseg/ops/wrappers.py | 51 - .../ddrnet/pytorch/mmseg/registry/__init__.py | 15 - .../ddrnet/pytorch/mmseg/registry/registry.py | 116 -- .../ddrnet/pytorch/mmseg/utils/__init__.py | 10 - .../ddrnet/pytorch/mmseg/utils/collect_env.py | 18 - .../ddrnet/pytorch/mmseg/utils/logger.py | 28 - .../ddrnet/pytorch/mmseg/utils/misc.py | 41 - .../ddrnet/pytorch/mmseg/utils/set_env.py | 55 - .../ddrnet/pytorch/mmseg/version.py | 18 - .../ddrnet/pytorch/requirements.txt | 4 - .../ddrnet/pytorch/requirements/apcnet.txt | 4 - .../pytorch/requirements/mmcv/build.txt | 1 - .../ddrnet/pytorch/requirements/mmcv/docs.txt | 8 - .../pytorch/requirements/mmcv/optional.txt | 1 - .../requirements/mmcv/requirements.txt | 4 - .../pytorch/requirements/mmcv/runtime.txt | 7 - .../ddrnet/pytorch/requirements/mmcv/test.txt | 9 - .../ddrnet/pytorch/requirements/mminstall.txt | 2 - .../ddrnet/pytorch/requirements/optional.txt | 1 - .../ddrnet/pytorch/requirements/runtime.txt | 5 - .../ddrnet/pytorch/setup.py | 258 ---- .../ddrnet/pytorch/tools/analyze_logs.py | 128 -- .../ddrnet/pytorch/tools/benchmark.py | 120 -- .../ddrnet/pytorch/tools/confusion_matrix.py | 184 --- .../tools/convert_datasets/cityscapes.py | 56 - .../tools/convert_datasets/coco_stuff10k.py | 307 ---- .../tools/convert_datasets/coco_stuff164k.py | 264 ---- .../tools/convert_datasets/pascal_context.py | 87 -- .../pytorch/tools/convert_datasets/voc_aug.py | 92 -- .../ddrnet/pytorch/tools/get_flops.py | 60 - .../ddrnet/pytorch/tools/print_config.py | 69 - .../ddrnet/pytorch/tools/slurm_test.sh | 24 - .../ddrnet/pytorch/tools/slurm_train.sh | 23 - .../ddrnet/pytorch/tools/test.py | 319 ---- .../ddrnet/pytorch/train.py | 243 --- .../ddrnet/pytorch/train_dist.sh | 19 - 311 files changed, 22 insertions(+), 40286 deletions(-) delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/.gitignore delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/CITATION.cff delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/LICENSE delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/ade20k.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/ade20k_640x640.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_768x768.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_832x832.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/coco-stuff10k.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/coco-stuff164k.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_context_59.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_voc12.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/default_runtime.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/models/ddrnet_23_slim.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_160k.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_1k.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_20k.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_320k.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_40k.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_80k.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/configs/ddrnet/ddrnet_23_slim_512x1024_160k_cityscapes.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/docker/Dockerfile delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/docker/serve/Dockerfile delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/docker/serve/config.properties delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/docker/serve/entrypoint.sh delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/activation.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/context_block.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv_module.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv_ws.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/drop.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/generalized_attention.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/hsigmoid.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/hswish.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/non_local.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/norm.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/padding.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/plugin.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/registry.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/scale.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/swish.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/transformer.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/upsample.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/wrappers.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/builder.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/flops_counter.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/sync_bn.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/weight_init.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/__init__.py delete mode 100755 cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/__init__.py delete mode 100755 cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/dataloader.py delete mode 100755 cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py delete mode 100755 cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/hook_wrapper.py delete mode 100755 cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/model_wrapper.py delete mode 100755 cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/runner.py delete mode 100755 cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/utils.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/_functions.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/data_parallel.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/distributed.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/scatter_gather.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/engine/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/engine/test.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/file_client.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/base.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/json_handler.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/pickle_handler.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/yaml_handler.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/io.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/parse.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/colorspace.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/geometric.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/io.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/misc.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/photometric.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/deprecated.json delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/mmcls.json delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/open_mmlab.json delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/torchvision_0.12.json delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/cc_attention.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/README.md delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/deprecated_wrappers.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/focal_loss.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/info.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/point_sample.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/psa_mask.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/sync_bn.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/_functions.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/collate.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/data_container.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/data_parallel.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/distributed.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/distributed_deprecated.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/registry.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/scatter_gather.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/utils.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/base_module.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/base_runner.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/builder.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/checkpoint.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/default_constructor.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/dist_utils.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/epoch_based_runner.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/fp16_utils.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/checkpoint.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/closure.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/ema.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/evaluation.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/hook.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/iter_timer.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/base.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/clearml.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/dvclive.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/mlflow.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/neptune.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/pavi.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/segmind.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/text.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/wandb.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/lr_updater.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/memory.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/momentum_updater.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/optimizer.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/profiler.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/sampler_seed.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/sync_buffer.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/iter_based_runner.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/log_buffer.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/optimizer/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/optimizer/builder.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/optimizer/default_constructor.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/priority.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/utils.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/config.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/device_type.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/env.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/ext_loader.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/hub.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/logging.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/misc.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/parrots_jit.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/parrots_wrapper.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/path.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/progressbar.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/registry.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/seed.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/testing.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/timer.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/trace.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/version_utils.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmcv/version.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/inference.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/test.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/train.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/builder.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/class_names.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/eval_hooks.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/metrics.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/optimizers/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/builder.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/sampler/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/utils/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/utils/dist_util.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/utils/misc.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/ade.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/builder.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/coco_stuff.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/custom.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/dataset_wrappers.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/compose.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/formating.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/formatting.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/loading.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/transforms.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/samplers/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/samplers/distributed_sampler.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/voc.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/bisenetv1.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/ddrnet.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/resnest.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/resnet.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/resnext.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/timm_backbone.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/builder.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/aspp_head.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/cc_head.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/ddr_head.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/decode_head.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/fcn_head.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/accuracy.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/cross_entropy_loss.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/dice_loss.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/focal_loss.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/lovasz_loss.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/utils.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/featurepyramid.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/fpn.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/ic_neck.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/jpu.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/mla_neck.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/multilevel_neck.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/base.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/encoder_decoder.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/embed.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/inverted_residual.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/make_divisible.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/res_layer.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/se_layer.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/self_attention_block.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/shape_convert.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/up_conv_block.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/wrappers.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/ops/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/ops/encoding.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/ops/wrappers.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/registry/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/registry/registry.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/__init__.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/collect_env.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/logger.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/misc.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/set_env.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/mmseg/version.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/requirements.txt delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/requirements/apcnet.txt delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/build.txt delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/docs.txt delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/optional.txt delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/requirements.txt delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/runtime.txt delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/test.txt delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/requirements/mminstall.txt delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/requirements/optional.txt delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/requirements/runtime.txt delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/setup.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/tools/analyze_logs.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/tools/benchmark.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/tools/confusion_matrix.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/coco_stuff10k.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/coco_stuff164k.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/voc_aug.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/tools/get_flops.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/tools/print_config.py delete mode 100755 cv/semantic_segmentation/ddrnet/pytorch/tools/slurm_test.sh delete mode 100755 cv/semantic_segmentation/ddrnet/pytorch/tools/slurm_train.sh delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/tools/test.py delete mode 100644 cv/semantic_segmentation/ddrnet/pytorch/train.py delete mode 100755 cv/semantic_segmentation/ddrnet/pytorch/train_dist.sh diff --git a/cv/3d_detection/bevformer/pytorch/README.md b/cv/3d_detection/bevformer/pytorch/README.md index f9420845f..22d54f0e5 100755 --- a/cv/3d_detection/bevformer/pytorch/README.md +++ b/cv/3d_detection/bevformer/pytorch/README.md @@ -96,4 +96,4 @@ and we provide another script to train BEVFormer with FP16. | 1x8 | bevformer_base | 0.3516 | 0.3701 | ## Reference: -[Paper in arXiv](http://arxiv.org/abs/2203.17270) +[BEVFormer](https://github.com/fundamentalvision/BEVFormer/tree/master) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/.gitignore b/cv/semantic_segmentation/ddrnet/pytorch/.gitignore deleted file mode 100644 index 787d13ec6..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/.gitignore +++ /dev/null @@ -1,120 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/en/_build/ -docs/zh_cn/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ -.DS_Store - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -data -.vscode -.idea - -# custom -*.pkl -*.pkl.json -*.log.json -work_dirs/ -mmseg/.mim - -# Pytorch -*.pth diff --git a/cv/semantic_segmentation/ddrnet/pytorch/CITATION.cff b/cv/semantic_segmentation/ddrnet/pytorch/CITATION.cff deleted file mode 100644 index cfd7cab05..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/CITATION.cff +++ /dev/null @@ -1,8 +0,0 @@ -cff-version: 1.2.0 -message: "If you use this software, please cite it as below." -authors: - - name: "MMSegmentation Contributors" -title: "OpenMMLab Semantic Segmentation Toolbox and Benchmark" -date-released: 2020-07-10 -url: "https://github.com/open-mmlab/mmsegmentation" -license: Apache-2.0 diff --git a/cv/semantic_segmentation/ddrnet/pytorch/LICENSE b/cv/semantic_segmentation/ddrnet/pytorch/LICENSE deleted file mode 100644 index 38e625bf5..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Copyright 2020 The MMSegmentation Authors. All rights reserved. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020 The MMSegmentation Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cv/semantic_segmentation/ddrnet/pytorch/README.md b/cv/semantic_segmentation/ddrnet/pytorch/README.md index 579b38c57..ca42fc9ce 100644 --- a/cv/semantic_segmentation/ddrnet/pytorch/README.md +++ b/cv/semantic_segmentation/ddrnet/pytorch/README.md @@ -9,20 +9,18 @@ we proposed a family of efficient backbones specially designed for real-time sem ### Install packages ```bash -pip3 install -r requirements.txt - -yum install mesa-libGL - -wget http://www.zlib.net/fossils/zlib-1.2.9.tar.gz -tar xvf zlib-1.2.9.tar.gz -cd zlib-1.2.9/ -./configure && make install -``` - -### Build extension - -```bash -python3 setup.py build && cp build/lib.linux*/mmcv/_ext.cpython* mmcv +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +# install mmsegmentation +git clone -b v1.2.2 https://github.com/open-mmlab/mmsegmentation.git --depth=1 +cd mmsegmentation/ +pip install -v -e . + +pip install ftfy ``` ## Step 2: Preparing datasets @@ -58,88 +56,22 @@ ln -s /path/to/cityscapes data/ ``` ## Step 3: Training - -```bash -# Training on multiple cards -# "config" file can be found in the configs directory -bash train_dist.sh [training args] - -# Example -bash train_dist.sh configs/ddrnet/ddrnet_23_slim_512x1024_160k_cityscapes.py 4 +### Training on single card +```shell +python3 tools/train.py configs/ddrnet/ddrnet_23-slim_in1k-pre_2xb6-120k_cityscapes-1024x1024.py ``` -**Training arguments are as follows:** - -```python -# the dir to save logs and models -work-dir: str = None - -# the checkpoint file to load weights from -load-from: str = None - -# the checkpoint file to resume from -resume-from: str = None - -# whether not to evaluate the checkpoint during training -no-validate: bool = False - -# (Deprecated, please use --gpu-id) number of gpus to -# use (only applicable to non-distributed training) -gpus: int = None - -# (Deprecated, please use --gpu-id) ids of gpus to use -# (only applicable to non-distributed training) -gpu-ids: int = None - -# id of gpu to use (only applicable to non-distributed training) -gpu-id: int = 0 - -# random seed -seed: int = None - -# Whether or not set different seeds for different ranks -diff_seed: bool = False - -# whether to set deterministic options for CUDNN backend. -deterministic: bool = False - -# --options is deprecated in favor of --cfg_options' and it -# will not be supported in version v0.22.0. Override some -# settings in the used config, the key-value pair in xxx=yyy -# format will be merged into config file. If the value to be -# overwritten is a list, it should be like key="[a,b]" or key=a,b -# It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" -# Note that the quotation marks are necessary and that no white space -# is allowed. -options: str = None - -# override some settings in the used config, the key-value pair -# in xxx=yyy format will be merged into config file. If the value -# to be overwritten is a list, it should be like key="[a,b]" or key=a,b -# It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" -# Note that the quotation marks are necessary and that no white -# space is allowed. -cfg-options: str = None - -# job launcher -launcher: str = "none" - -# local rank -local_rank: int = 0 - -# distributed backend -dist_backend: str = None - -# resume from the latest checkpoint automatically. -auto-resume: bool = False +### Training on mutil-cards +```shell +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/ddrnet/ddrnet_23-slim_in1k-pre_2xb6-120k_cityscapes-1024x1024.py 8 ``` ## Results | GPUs | Crop Size | Lr schd | FPS | mIoU| | ------ | --------- | ------: | -------- |--------------:| -| BI-V100 x8 | 512x1024 | 16000 | 33.085 | 74.8 | +| BI-V100 x8 | 1024x1024 | 12000 | 33.085 | 74.8 | ## Reference -- [cityscapes](https://mmsegmentation.readthedocs.io/en/latest/dataset_prepare.html#cityscapes) -- [mmsegmentation](https://github.com/open-mmlab/mmsegmentation) \ No newline at end of file +[mmsegmentation](https://github.com/open-mmlab/mmsegmentation) \ No newline at end of file diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/ade20k.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/ade20k.py deleted file mode 100644 index efc8b4bb2..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/ade20k.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'ADE20KDataset' -data_root = 'data/ade/ADEChallengeData2016' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/ade20k_640x640.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/ade20k_640x640.py deleted file mode 100644 index 14a4bb092..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/ade20k_640x640.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'ADE20KDataset' -data_root = 'data/ade/ADEChallengeData2016' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (640, 640) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2560, 640), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2560, 640), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes.py deleted file mode 100644 index f21867c63..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'CityscapesDataset' -data_root = 'data/cityscapes/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 1024) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/train', - ann_dir='gtFine/train', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/val', - ann_dir='gtFine/val', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/val', - ann_dir='gtFine/val', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py deleted file mode 100644 index f98d92972..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (1024, 1024) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_768x768.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_768x768.py deleted file mode 100644 index fde9d7c7d..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_768x768.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (768, 768) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2049, 1025), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py deleted file mode 100644 index 336c7b254..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (769, 769) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2049, 1025), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_832x832.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_832x832.py deleted file mode 100644 index b9325cc00..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/cityscapes_832x832.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (832, 832) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/coco-stuff10k.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/coco-stuff10k.py deleted file mode 100644 index ec0496928..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/coco-stuff10k.py +++ /dev/null @@ -1,57 +0,0 @@ -# dataset settings -dataset_type = 'COCOStuffDataset' -data_root = 'data/coco_stuff10k' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/train2014', - ann_dir='annotations/train2014', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/test2014', - ann_dir='annotations/test2014', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/test2014', - ann_dir='annotations/test2014', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/coco-stuff164k.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/coco-stuff164k.py deleted file mode 100644 index a6a38f2ac..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/coco-stuff164k.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'COCOStuffDataset' -data_root = 'data/coco_stuff164k' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/train2017', - ann_dir='annotations/train2017', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/val2017', - ann_dir='annotations/val2017', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/val2017', - ann_dir='annotations/val2017', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_context.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_context.py deleted file mode 100644 index ff65bad1b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_context.py +++ /dev/null @@ -1,60 +0,0 @@ -# dataset settings -dataset_type = 'PascalContextDataset' -data_root = 'data/VOCdevkit/VOC2010/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) - -img_scale = (520, 520) -crop_size = (480, 480) - -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_context_59.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_context_59.py deleted file mode 100644 index 37585abab..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_context_59.py +++ /dev/null @@ -1,60 +0,0 @@ -# dataset settings -dataset_type = 'PascalContextDataset59' -data_root = 'data/VOCdevkit/VOC2010/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) - -img_scale = (520, 520) -crop_size = (480, 480) - -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_voc12.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_voc12.py deleted file mode 100644 index e5ff704ae..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_voc12.py +++ /dev/null @@ -1,58 +0,0 @@ -# dataset settings - -dataset_type = 'PascalVOCDataset' -data_root = 'data/VOCdevkit/VOC2012' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py deleted file mode 100644 index 3f23b6717..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = './pascal_voc12.py' -# dataset settings -data = dict( - train=dict( - ann_dir=['SegmentationClass', 'SegmentationClassAug'], - split=[ - 'ImageSets/Segmentation/train.txt', - 'ImageSets/Segmentation/aug.txt' - ])) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/default_runtime.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/default_runtime.py deleted file mode 100644 index b564cc4e7..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/default_runtime.py +++ /dev/null @@ -1,14 +0,0 @@ -# yapf:disable -log_config = dict( - interval=50, - hooks=[ - dict(type='TextLoggerHook', by_epoch=False), - # dict(type='TensorboardLoggerHook') - ]) -# yapf:enable -dist_params = dict(backend='nccl') -log_level = 'INFO' -load_from = None -resume_from = None -workflow = [('train', 1)] -cudnn_benchmark = True diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/models/ddrnet_23_slim.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/models/ddrnet_23_slim.py deleted file mode 100644 index 9a8cfbc1d..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/models/ddrnet_23_slim.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - -# model settings -norm_cfg = dict(type='SyncBN', requires_grad=True) -model = dict( - type='EncoderDecoder', - pretrained=None, - backbone=dict( - type='DDRNet', - norm_cfg=norm_cfg, - norm_eval=False, - extra=dict( - layers=(2, 2, 2, 2), - planes=32, - spp_planes=128 - ) - ), - decode_head=dict( - type='DDRHead', - in_channels=128, - in_index=-1, - channels=64, - input_transform=None, - # dropout_ratio=None, - num_classes=19, - norm_cfg=norm_cfg, - align_corners=False, - loss_decode=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0 - ) - ), - # model training and testing settings - train_cfg=dict(), - test_cfg=dict(mode='whole') -) \ No newline at end of file diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_160k.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_160k.py deleted file mode 100644 index b4b2ac270..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_160k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=160000) -checkpoint_config = dict(by_epoch=False, interval=16000) -evaluation = dict(interval=8000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_1k.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_1k.py deleted file mode 100644 index 04cf41030..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_1k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=1000) -checkpoint_config = dict(by_epoch=False, interval=1000) -evaluation = dict(interval=1000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_20k.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_20k.py deleted file mode 100644 index 73c702197..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_20k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=20000) -checkpoint_config = dict(by_epoch=False, interval=2000) -evaluation = dict(interval=2000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_320k.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_320k.py deleted file mode 100644 index a0b230626..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_320k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=320000) -checkpoint_config = dict(by_epoch=False, interval=32000) -evaluation = dict(interval=32000, metric='mIoU') diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_40k.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_40k.py deleted file mode 100644 index d2c502325..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_40k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=40000) -checkpoint_config = dict(by_epoch=False, interval=4000) -evaluation = dict(interval=4000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_80k.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_80k.py deleted file mode 100644 index 8365a878e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/_base_/schedules/schedule_80k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=80000) -checkpoint_config = dict(by_epoch=False, interval=8000) -evaluation = dict(interval=8000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/configs/ddrnet/ddrnet_23_slim_512x1024_160k_cityscapes.py b/cv/semantic_segmentation/ddrnet/pytorch/configs/ddrnet/ddrnet_23_slim_512x1024_160k_cityscapes.py deleted file mode 100644 index 3e23f043d..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/configs/ddrnet/ddrnet_23_slim_512x1024_160k_cityscapes.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - -_base_ = [ - '../_base_/models/ddrnet_23_slim.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_160k.py' -] \ No newline at end of file diff --git a/cv/semantic_segmentation/ddrnet/pytorch/docker/Dockerfile b/cv/semantic_segmentation/ddrnet/pytorch/docker/Dockerfile deleted file mode 100644 index 64482b472..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/docker/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -ARG PYTORCH="1.11.0" -ARG CUDA="11.3" -ARG CUDNN="8" - -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ENV TORCH_CUDA_ARCH_LIST="6.0 6.1 7.0+PTX" -ENV TORCH_NVCC_FLAGS="-Xfatbin -compress-all" -ENV CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" - -# To fix GPG key error when running apt-get update -RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub -RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/7fa2af80.pub - -RUN apt-get update && apt-get install -y git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -RUN conda clean --all - -# Install MMCV -ARG PYTORCH -ARG CUDA -ARG MMCV -RUN ["/bin/bash", "-c", "pip install --no-cache-dir mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] - -# Install MMSegmentation -RUN git clone https://github.com/open-mmlab/mmsegmentation.git /mmsegmentation -WORKDIR /mmsegmentation -ENV FORCE_CUDA="1" -RUN pip install -r requirements.txt -RUN pip install --no-cache-dir -e . diff --git a/cv/semantic_segmentation/ddrnet/pytorch/docker/serve/Dockerfile b/cv/semantic_segmentation/ddrnet/pytorch/docker/serve/Dockerfile deleted file mode 100644 index c1d154528..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/docker/serve/Dockerfile +++ /dev/null @@ -1,49 +0,0 @@ -ARG PYTORCH="1.11.0" -ARG CUDA="11.3" -ARG CUDNN="8" -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ARG MMCV="1.4.8" -ARG MMSEG="0.24.1" - -ENV PYTHONUNBUFFERED TRUE - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ - ca-certificates \ - g++ \ - openjdk-11-jre-headless \ - # MMDet Requirements - ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && rm -rf /var/lib/apt/lists/* - -ENV PATH="/opt/conda/bin:$PATH" -RUN export FORCE_CUDA=1 - -# TORCHSEVER -RUN pip install torchserve torch-model-archiver - -# MMLAB -ARG PYTORCH -ARG CUDA -RUN ["/bin/bash", "-c", "pip install mmcv-full==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] -RUN pip install mmsegmentation==${MMSEG} - -RUN useradd -m model-server \ - && mkdir -p /home/model-server/tmp - -COPY entrypoint.sh /usr/local/bin/entrypoint.sh - -RUN chmod +x /usr/local/bin/entrypoint.sh \ - && chown -R model-server /home/model-server - -COPY config.properties /home/model-server/config.properties -RUN mkdir /home/model-server/model-store && chown -R model-server /home/model-server/model-store - -EXPOSE 8080 8081 8082 - -USER model-server -WORKDIR /home/model-server -ENV TEMP=/home/model-server/tmp -ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] -CMD ["serve"] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/docker/serve/config.properties b/cv/semantic_segmentation/ddrnet/pytorch/docker/serve/config.properties deleted file mode 100644 index efb9c47e4..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/docker/serve/config.properties +++ /dev/null @@ -1,5 +0,0 @@ -inference_address=http://0.0.0.0:8080 -management_address=http://0.0.0.0:8081 -metrics_address=http://0.0.0.0:8082 -model_store=/home/model-server/model-store -load_models=all diff --git a/cv/semantic_segmentation/ddrnet/pytorch/docker/serve/entrypoint.sh b/cv/semantic_segmentation/ddrnet/pytorch/docker/serve/entrypoint.sh deleted file mode 100644 index 41ba00b04..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/docker/serve/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - -if [[ "$1" = "serve" ]]; then - shift 1 - torchserve --start --ts-config /home/model-server/config.properties -else - eval "$@" -fi - -# prevent docker exit -tail -f /dev/null diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/__init__.py deleted file mode 100644 index 435429d48..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -from .fileio import * -from .image import * -from .utils import * -from .version import * - -# The following modules are not imported to this level, so mmcv may be used -# without PyTorch. -# - runner -# - parallel -# - op -# - device diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/__init__.py deleted file mode 100644 index 3d5599d9a..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# yapf: disable -from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, - ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, - ConvTranspose2d, ConvTranspose3d, ConvWS2d, - DepthwiseSeparableConvModule, GeneralizedAttention, - HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, - NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, - build_activation_layer, build_conv_layer, - build_norm_layer, build_padding_layer, build_plugin_layer, - build_upsample_layer, conv_ws_2d, is_norm) -from .builder import MODELS, build_model_from_cfg -# yapf: enable -from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, - NormalInit, PretrainedInit, TruncNormalInit, UniformInit, - XavierInit, bias_init_with_prob, caffe2_xavier_init, - constant_init, fuse_conv_bn, get_model_complexity_info, - initialize, kaiming_init, normal_init, trunc_normal_init, - uniform_init, xavier_init) - diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/__init__.py deleted file mode 100644 index 0f33124ed..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .activation import build_activation_layer -from .context_block import ContextBlock -from .conv import build_conv_layer -from .conv2d_adaptive_padding import Conv2dAdaptivePadding -from .conv_module import ConvModule -from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d -from .depthwise_separable_conv_module import DepthwiseSeparableConvModule -from .drop import Dropout, DropPath -from .generalized_attention import GeneralizedAttention -from .hsigmoid import HSigmoid -from .hswish import HSwish -from .non_local import NonLocal1d, NonLocal2d, NonLocal3d -from .norm import build_norm_layer, is_norm -from .padding import build_padding_layer -from .plugin import build_plugin_layer -from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) -from .scale import Scale -from .swish import Swish -from .upsample import build_upsample_layer -from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, - Linear, MaxPool2d, MaxPool3d) - -__all__ = [ - 'ConvModule', 'build_activation_layer', 'build_conv_layer', - 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', - 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', - 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', - 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', - 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', - 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', - 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/activation.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/activation.py deleted file mode 100644 index 26be59581..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/activation.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version -from .registry import ACTIVATION_LAYERS - -for module in [ - nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, - nn.Sigmoid, nn.Tanh -]: - ACTIVATION_LAYERS.register_module(module=module) - - -@ACTIVATION_LAYERS.register_module(name='Clip') -@ACTIVATION_LAYERS.register_module() -class Clamp(nn.Module): - """Clamp activation layer. - - This activation function is to clamp the feature map value within - :math:`[min, max]`. More details can be found in ``torch.clamp()``. - - Args: - min (Number | optional): Lower-bound of the range to be clamped to. - Default to -1. - max (Number | optional): Upper-bound of the range to be clamped to. - Default to 1. - """ - - def __init__(self, min=-1., max=1.): - super(Clamp, self).__init__() - self.min = min - self.max = max - - def forward(self, x): - """Forward function. - - Args: - x (torch.Tensor): The input tensor. - - Returns: - torch.Tensor: Clamped tensor. - """ - return torch.clamp(x, min=self.min, max=self.max) - - -class GELU(nn.Module): - r"""Applies the Gaussian Error Linear Units function: - - .. math:: - \text{GELU}(x) = x * \Phi(x) - where :math:`\Phi(x)` is the Cumulative Distribution Function for - Gaussian Distribution. - - Shape: - - Input: :math:`(N, *)` where `*` means, any number of additional - dimensions - - Output: :math:`(N, *)`, same shape as the input - - .. image:: scripts/activation_images/GELU.png - - Examples:: - - >>> m = nn.GELU() - >>> input = torch.randn(2) - >>> output = m(input) - """ - - def forward(self, input): - return F.gelu(input) - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.4')): - ACTIVATION_LAYERS.register_module(module=GELU) -else: - ACTIVATION_LAYERS.register_module(module=nn.GELU) - - -def build_activation_layer(cfg): - """Build activation layer. - - Args: - cfg (dict): The activation layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate an activation layer. - - Returns: - nn.Module: Created activation layer. - """ - return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/context_block.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/context_block.py deleted file mode 100644 index d60fdb904..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/context_block.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn - -from ..utils import constant_init, kaiming_init -from .registry import PLUGIN_LAYERS - - -def last_zero_init(m): - if isinstance(m, nn.Sequential): - constant_init(m[-1], val=0) - else: - constant_init(m, val=0) - - -@PLUGIN_LAYERS.register_module() -class ContextBlock(nn.Module): - """ContextBlock module in GCNet. - - See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' - (https://arxiv.org/abs/1904.11492) for details. - - Args: - in_channels (int): Channels of the input feature map. - ratio (float): Ratio of channels of transform bottleneck - pooling_type (str): Pooling method for context modeling. - Options are 'att' and 'avg', stand for attention pooling and - average pooling respectively. Default: 'att'. - fusion_types (Sequence[str]): Fusion method for feature fusion, - Options are 'channels_add', 'channel_mul', stand for channelwise - addition and multiplication respectively. Default: ('channel_add',) - """ - - _abbr_ = 'context_block' - - def __init__(self, - in_channels, - ratio, - pooling_type='att', - fusion_types=('channel_add', )): - super(ContextBlock, self).__init__() - assert pooling_type in ['avg', 'att'] - assert isinstance(fusion_types, (list, tuple)) - valid_fusion_types = ['channel_add', 'channel_mul'] - assert all([f in valid_fusion_types for f in fusion_types]) - assert len(fusion_types) > 0, 'at least one fusion should be used' - self.in_channels = in_channels - self.ratio = ratio - self.planes = int(in_channels * ratio) - self.pooling_type = pooling_type - self.fusion_types = fusion_types - if pooling_type == 'att': - self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) - self.softmax = nn.Softmax(dim=2) - else: - self.avg_pool = nn.AdaptiveAvgPool2d(1) - if 'channel_add' in fusion_types: - self.channel_add_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_add_conv = None - if 'channel_mul' in fusion_types: - self.channel_mul_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_mul_conv = None - self.reset_parameters() - - def reset_parameters(self): - if self.pooling_type == 'att': - kaiming_init(self.conv_mask, mode='fan_in') - self.conv_mask.inited = True - - if self.channel_add_conv is not None: - last_zero_init(self.channel_add_conv) - if self.channel_mul_conv is not None: - last_zero_init(self.channel_mul_conv) - - def spatial_pool(self, x): - batch, channel, height, width = x.size() - if self.pooling_type == 'att': - input_x = x - # [N, C, H * W] - input_x = input_x.view(batch, channel, height * width) - # [N, 1, C, H * W] - input_x = input_x.unsqueeze(1) - # [N, 1, H, W] - context_mask = self.conv_mask(x) - # [N, 1, H * W] - context_mask = context_mask.view(batch, 1, height * width) - # [N, 1, H * W] - context_mask = self.softmax(context_mask) - # [N, 1, H * W, 1] - context_mask = context_mask.unsqueeze(-1) - # [N, 1, C, 1] - context = torch.matmul(input_x, context_mask) - # [N, C, 1, 1] - context = context.view(batch, channel, 1, 1) - else: - # [N, C, 1, 1] - context = self.avg_pool(x) - - return context - - def forward(self, x): - # [N, C, 1, 1] - context = self.spatial_pool(x) - - out = x - if self.channel_mul_conv is not None: - # [N, C, 1, 1] - channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) - out = out * channel_mul_term - if self.channel_add_conv is not None: - # [N, C, 1, 1] - channel_add_term = self.channel_add_conv(context) - out = out + channel_add_term - - return out diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv.py deleted file mode 100644 index f6c35fd70..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from .registry import CONV_LAYERS - -CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) -CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) -CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) -CONV_LAYERS.register_module('Conv', module=nn.Conv2d) - - -def build_conv_layer(cfg, *args, **kwargs): - """Build convolution layer. - - Args: - cfg (None or dict): The conv layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an conv layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding conv layer. - - Returns: - nn.Module: Created conv layer. - """ - if cfg is None: - cfg_ = dict(type='Conv2d') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in CONV_LAYERS: - raise KeyError(f'Unrecognized layer type {layer_type}') - else: - conv_layer = CONV_LAYERS.get(layer_type) - - layer = conv_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py deleted file mode 100644 index b45e758ac..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from torch import nn -from torch.nn import functional as F - -from .registry import CONV_LAYERS - - -@CONV_LAYERS.register_module() -class Conv2dAdaptivePadding(nn.Conv2d): - """Implementation of 2D convolution in tensorflow with `padding` as "same", - which applies padding to input (if needed) so that input image gets fully - covered by filter and stride you specified. For stride 1, this will ensure - that output image size is same as input. For stride of 2, output dimensions - will be half, for example. - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the convolving kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If ``True``, adds a learnable bias to the - output. Default: ``True`` - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__(in_channels, out_channels, kernel_size, stride, 0, - dilation, groups, bias) - - def forward(self, x): - img_h, img_w = x.size()[-2:] - kernel_h, kernel_w = self.weight.size()[-2:] - stride_h, stride_w = self.stride - output_h = math.ceil(img_h / stride_h) - output_w = math.ceil(img_w / stride_w) - pad_h = ( - max((output_h - 1) * self.stride[0] + - (kernel_h - 1) * self.dilation[0] + 1 - img_h, 0)) - pad_w = ( - max((output_w - 1) * self.stride[1] + - (kernel_w - 1) * self.dilation[1] + 1 - img_w, 0)) - if pad_h > 0 or pad_w > 0: - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2 - ]) - return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv_module.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv_module.py deleted file mode 100644 index 0078647a1..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv_module.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from mmcv.utils import _BatchNorm, _InstanceNorm -from ..utils import constant_init, kaiming_init -from .activation import build_activation_layer -from .conv import build_conv_layer -from .norm import build_norm_layer -from .padding import build_padding_layer -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class ConvModule(nn.Module): - """A conv block that bundles conv/norm/activation layers. - - This block simplifies the usage of convolution layers, which are commonly - used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). - It is based upon three build methods: `build_conv_layer()`, - `build_norm_layer()` and `build_activation_layer()`. - - Besides, we add some additional features in this module. - 1. Automatically set `bias` of the conv layer. - 2. Spectral norm is supported. - 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only - supports zero and circular padding, and we add "reflect" padding mode. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. - groups (int): Number of blocked connections from input channels to - output channels. Same as that in ``nn._ConvNd``. - bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - inplace (bool): Whether to use inplace mode for activation. - Default: True. - with_spectral_norm (bool): Whether use spectral norm in conv module. - Default: False. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in PyTorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Common examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - Default: ('conv', 'norm', 'act'). - """ - - _abbr_ = 'conv_block' - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias='auto', - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - padding_mode='zeros', - order=('conv', 'norm', 'act')): - super(ConvModule, self).__init__() - assert conv_cfg is None or isinstance(conv_cfg, dict) - assert norm_cfg is None or isinstance(norm_cfg, dict) - assert act_cfg is None or isinstance(act_cfg, dict) - official_padding_mode = ['zeros', 'circular'] - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.with_explicit_padding = padding_mode not in official_padding_mode - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 3 - assert set(order) == set(['conv', 'norm', 'act']) - - self.with_norm = norm_cfg is not None - self.with_activation = act_cfg is not None - # if the conv layer is before a norm layer, bias is unnecessary. - if bias == 'auto': - bias = not self.with_norm - self.with_bias = bias - - if self.with_explicit_padding: - pad_cfg = dict(type=padding_mode) - self.padding_layer = build_padding_layer(pad_cfg, padding) - - # reset padding to 0 for conv module - conv_padding = 0 if self.with_explicit_padding else padding - # build convolution layer - self.conv = build_conv_layer( - conv_cfg, - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=conv_padding, - dilation=dilation, - groups=groups, - bias=bias) - # export the attributes of self.conv to a higher level for convenience - self.in_channels = self.conv.in_channels - self.out_channels = self.conv.out_channels - self.kernel_size = self.conv.kernel_size - self.stride = self.conv.stride - self.padding = padding - self.dilation = self.conv.dilation - self.transposed = self.conv.transposed - self.output_padding = self.conv.output_padding - self.groups = self.conv.groups - - if self.with_spectral_norm: - self.conv = nn.utils.spectral_norm(self.conv) - - # build normalization layers - if self.with_norm: - # norm layer is after conv layer - if order.index('norm') > order.index('conv'): - norm_channels = out_channels - else: - norm_channels = in_channels - self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) - self.add_module(self.norm_name, norm) - if self.with_bias: - if isinstance(norm, (_BatchNorm, _InstanceNorm)): - warnings.warn( - 'Unnecessary conv bias before batch/instance norm') - else: - self.norm_name = None - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - # nn.Tanh has no 'inplace' argument - if act_cfg_['type'] not in [ - 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish', 'GELU' - ]: - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - @property - def norm(self): - if self.norm_name: - return getattr(self, self.norm_name) - else: - return None - - def init_weights(self): - # 1. It is mainly for customized conv layers with their own - # initialization manners by calling their own ``init_weights()``, - # and we do not want ConvModule to override the initialization. - # 2. For customized conv layers without their own initialization - # manners (that is, they don't have their own ``init_weights()``) - # and PyTorch's conv layers, they will be initialized by - # this method with default ``kaiming_init``. - # Note: For PyTorch's conv layers, they will be overwritten by our - # initialization implementation using default ``kaiming_init``. - if not hasattr(self.conv, 'init_weights'): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) - if self.with_norm: - constant_init(self.norm, 1, bias=0) - - def forward(self, x, activate=True, norm=True): - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - x = self.conv(x) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv_ws.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv_ws.py deleted file mode 100644 index a3941e278..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/conv_ws.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from .registry import CONV_LAYERS - - -def conv_ws_2d(input, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - eps=1e-5): - c_in = weight.size(0) - weight_flat = weight.view(c_in, -1) - mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1) - std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1) - weight = (weight - mean) / (std + eps) - return F.conv2d(input, weight, bias, stride, padding, dilation, groups) - - -@CONV_LAYERS.register_module('ConvWS') -class ConvWS2d(nn.Conv2d): - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True, - eps=1e-5): - super(ConvWS2d, self).__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.eps = eps - - def forward(self, x): - return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups, self.eps) - - -@CONV_LAYERS.register_module(name='ConvAWS') -class ConvAWS2d(nn.Conv2d): - """AWS (Adaptive Weight Standardization) - - This is a variant of Weight Standardization - (https://arxiv.org/pdf/1903.10520.pdf) - It is used in DetectoRS to avoid NaN - (https://arxiv.org/pdf/2006.02334.pdf) - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the conv kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If set True, adds a learnable bias to the - output. Default: True - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.register_buffer('weight_gamma', - torch.ones(self.out_channels, 1, 1, 1)) - self.register_buffer('weight_beta', - torch.zeros(self.out_channels, 1, 1, 1)) - - def _get_weight(self, weight): - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - weight = (weight - mean) / std - weight = self.weight_gamma * weight + self.weight_beta - return weight - - def forward(self, x): - weight = self._get_weight(self.weight) - return F.conv2d(x, weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - """Override default load function. - - AWS overrides the function _load_from_state_dict to recover - weight_gamma and weight_beta if they are missing. If weight_gamma and - weight_beta are found in the checkpoint, this function will return - after super()._load_from_state_dict. Otherwise, it will compute the - mean and std of the pretrained weights and store them in weight_beta - and weight_gamma. - """ - - self.weight_gamma.data.fill_(-1) - local_missing_keys = [] - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, local_missing_keys, - unexpected_keys, error_msgs) - if self.weight_gamma.data.mean() > 0: - for k in local_missing_keys: - missing_keys.append(k) - return - weight = self.weight.data - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - self.weight_beta.data.copy_(mean) - self.weight_gamma.data.copy_(std) - missing_gamma_beta = [ - k for k in local_missing_keys - if k.endswith('weight_gamma') or k.endswith('weight_beta') - ] - for k in missing_gamma_beta: - local_missing_keys.remove(k) - for k in local_missing_keys: - missing_keys.append(k) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py deleted file mode 100644 index 722d5d8d7..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .conv_module import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if `norm_cfg` and `act_cfg` are specified. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. Default: 1. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. Default: 0. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super(DepthwiseSeparableConvModule, self).__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/drop.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/drop.py deleted file mode 100644 index b0a026654..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/drop.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from mmcv import build_from_cfg -from .registry import DROPOUT_LAYERS - - -def drop_path(x, drop_prob=0., training=False): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - """ - if drop_prob == 0. or not training: - return x - keep_prob = 1 - drop_prob - # handle tensors with different dimensions, not just 4D tensors. - shape = (x.shape[0], ) + (1, ) * (x.ndim - 1) - random_tensor = keep_prob + torch.rand( - shape, dtype=x.dtype, device=x.device) - output = x.div(keep_prob) * random_tensor.floor() - return output - - -@DROPOUT_LAYERS.register_module() -class DropPath(nn.Module): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - - Args: - drop_prob (float): Probability of the path to be zeroed. Default: 0.1 - """ - - def __init__(self, drop_prob=0.1): - super(DropPath, self).__init__() - self.drop_prob = drop_prob - - def forward(self, x): - return drop_path(x, self.drop_prob, self.training) - - -@DROPOUT_LAYERS.register_module() -class Dropout(nn.Dropout): - """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of - ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with - ``DropPath`` - - Args: - drop_prob (float): Probability of the elements to be - zeroed. Default: 0.5. - inplace (bool): Do the operation inplace or not. Default: False. - """ - - def __init__(self, drop_prob=0.5, inplace=False): - super().__init__(p=drop_prob, inplace=inplace) - - -def build_dropout(cfg, default_args=None): - """Builder for drop out layers.""" - return build_from_cfg(cfg, DROPOUT_LAYERS, default_args) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/generalized_attention.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/generalized_attention.py deleted file mode 100644 index c8a74d268..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/generalized_attention.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import kaiming_init -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class GeneralizedAttention(nn.Module): - """GeneralizedAttention module. - - See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' - (https://arxiv.org/abs/1711.07971) for details. - - Args: - in_channels (int): Channels of the input feature map. - spatial_range (int): The spatial range. -1 indicates no spatial range - constraint. Default: -1. - num_heads (int): The head number of empirical_attention module. - Default: 9. - position_embedding_dim (int): The position embedding dimension. - Default: -1. - position_magnitude (int): A multiplier acting on coord difference. - Default: 1. - kv_stride (int): The feature stride acting on key/value feature map. - Default: 2. - q_stride (int): The feature stride acting on query feature map. - Default: 1. - attention_type (str): A binary indicator string for indicating which - items in generalized empirical_attention module are used. - Default: '1111'. - - - '1000' indicates 'query and key content' (appr - appr) item, - - '0100' indicates 'query content and relative position' - (appr - position) item, - - '0010' indicates 'key content only' (bias - appr) item, - - '0001' indicates 'relative position only' (bias - position) item. - """ - - _abbr_ = 'gen_attention_block' - - def __init__(self, - in_channels, - spatial_range=-1, - num_heads=9, - position_embedding_dim=-1, - position_magnitude=1, - kv_stride=2, - q_stride=1, - attention_type='1111'): - - super(GeneralizedAttention, self).__init__() - - # hard range means local range for non-local operation - self.position_embedding_dim = ( - position_embedding_dim - if position_embedding_dim > 0 else in_channels) - - self.position_magnitude = position_magnitude - self.num_heads = num_heads - self.in_channels = in_channels - self.spatial_range = spatial_range - self.kv_stride = kv_stride - self.q_stride = q_stride - self.attention_type = [bool(int(_)) for _ in attention_type] - self.qk_embed_dim = in_channels // num_heads - out_c = self.qk_embed_dim * num_heads - - if self.attention_type[0] or self.attention_type[1]: - self.query_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.query_conv.kaiming_init = True - - if self.attention_type[0] or self.attention_type[2]: - self.key_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.key_conv.kaiming_init = True - - self.v_dim = in_channels // num_heads - self.value_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=self.v_dim * num_heads, - kernel_size=1, - bias=False) - self.value_conv.kaiming_init = True - - if self.attention_type[1] or self.attention_type[3]: - self.appr_geom_fc_x = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_x.kaiming_init = True - - self.appr_geom_fc_y = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_y.kaiming_init = True - - if self.attention_type[2]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.appr_bias = nn.Parameter(appr_bias_value) - - if self.attention_type[3]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.geom_bias = nn.Parameter(geom_bias_value) - - self.proj_conv = nn.Conv2d( - in_channels=self.v_dim * num_heads, - out_channels=in_channels, - kernel_size=1, - bias=True) - self.proj_conv.kaiming_init = True - self.gamma = nn.Parameter(torch.zeros(1)) - - if self.spatial_range >= 0: - # only works when non local is after 3*3 conv - if in_channels == 256: - max_len = 84 - elif in_channels == 512: - max_len = 42 - - max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) - local_constraint_map = np.ones( - (max_len, max_len, max_len_kv, max_len_kv), dtype=int) - for iy in range(max_len): - for ix in range(max_len): - local_constraint_map[ - iy, ix, - max((iy - self.spatial_range) // - self.kv_stride, 0):min((iy + self.spatial_range + - 1) // self.kv_stride + - 1, max_len), - max((ix - self.spatial_range) // - self.kv_stride, 0):min((ix + self.spatial_range + - 1) // self.kv_stride + - 1, max_len)] = 0 - - self.local_constraint_map = nn.Parameter( - torch.from_numpy(local_constraint_map).byte(), - requires_grad=False) - - if self.q_stride > 1: - self.q_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.q_stride) - else: - self.q_downsample = None - - if self.kv_stride > 1: - self.kv_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.kv_stride) - else: - self.kv_downsample = None - - self.init_weights() - - def get_position_embedding(self, - h, - w, - h_kv, - w_kv, - q_stride, - kv_stride, - device, - dtype, - feat_dim, - wave_length=1000): - # the default type of Tensor is float32, leading to type mismatch - # in fp16 mode. Cast it to support fp16 mode. - h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) - h_idxs = h_idxs.view((h, 1)) * q_stride - - w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) - w_idxs = w_idxs.view((w, 1)) * q_stride - - h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( - device=device, dtype=dtype) - h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride - - w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( - device=device, dtype=dtype) - w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride - - # (h, h_kv, 1) - h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) - h_diff *= self.position_magnitude - - # (w, w_kv, 1) - w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) - w_diff *= self.position_magnitude - - feat_range = torch.arange(0, feat_dim / 4).to( - device=device, dtype=dtype) - - dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) - dim_mat = dim_mat**((4. / feat_dim) * feat_range) - dim_mat = dim_mat.view((1, 1, -1)) - - embedding_x = torch.cat( - ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) - - embedding_y = torch.cat( - ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) - - return embedding_x, embedding_y - - def forward(self, x_input): - num_heads = self.num_heads - - # use empirical_attention - if self.q_downsample is not None: - x_q = self.q_downsample(x_input) - else: - x_q = x_input - n, _, h, w = x_q.shape - - if self.kv_downsample is not None: - x_kv = self.kv_downsample(x_input) - else: - x_kv = x_input - _, _, h_kv, w_kv = x_kv.shape - - if self.attention_type[0] or self.attention_type[1]: - proj_query = self.query_conv(x_q).view( - (n, num_heads, self.qk_embed_dim, h * w)) - proj_query = proj_query.permute(0, 1, 3, 2) - - if self.attention_type[0] or self.attention_type[2]: - proj_key = self.key_conv(x_kv).view( - (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) - - if self.attention_type[1] or self.attention_type[3]: - position_embed_x, position_embed_y = self.get_position_embedding( - h, w, h_kv, w_kv, self.q_stride, self.kv_stride, - x_input.device, x_input.dtype, self.position_embedding_dim) - # (n, num_heads, w, w_kv, dim) - position_feat_x = self.appr_geom_fc_x(position_embed_x).\ - view(1, w, w_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - # (n, num_heads, h, h_kv, dim) - position_feat_y = self.appr_geom_fc_y(position_embed_y).\ - view(1, h, h_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - position_feat_x /= math.sqrt(2) - position_feat_y /= math.sqrt(2) - - # accelerate for saliency only - if (np.sum(self.attention_type) == 1) and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy = torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, h_kv * w_kv) - - h = 1 - w = 1 - else: - # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for - if not self.attention_type[0]: - energy = torch.zeros( - n, - num_heads, - h, - w, - h_kv, - w_kv, - dtype=x_input.dtype, - device=x_input.device) - - # attention_type[0]: appr - appr - # attention_type[1]: appr - position - # attention_type[2]: bias - appr - # attention_type[3]: bias - position - if self.attention_type[0] or self.attention_type[2]: - if self.attention_type[0] and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - energy = torch.matmul(proj_query + appr_bias, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[0]: - energy = torch.matmul(proj_query, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy += torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, 1, h_kv, w_kv) - - if self.attention_type[1] or self.attention_type[3]: - if self.attention_type[1] and self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - - proj_query_reshape = (proj_query + geom_bias).\ - view(n, num_heads, h, w, self.qk_embed_dim) - - energy_x = torch.matmul( - proj_query_reshape.permute(0, 1, 3, 2, 4), - position_feat_x.permute(0, 1, 2, 4, 3)) - energy_x = energy_x.\ - permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul( - proj_query_reshape, - position_feat_y.permute(0, 1, 2, 4, 3)) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[1]: - proj_query_reshape = proj_query.\ - view(n, num_heads, h, w, self.qk_embed_dim) - proj_query_reshape = proj_query_reshape.\ - permute(0, 1, 3, 2, 4) - position_feat_x_reshape = position_feat_x.\ - permute(0, 1, 2, 4, 3) - position_feat_y_reshape = position_feat_y.\ - permute(0, 1, 2, 4, 3) - - energy_x = torch.matmul(proj_query_reshape, - position_feat_x_reshape) - energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul(proj_query_reshape, - position_feat_y_reshape) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, self.qk_embed_dim, 1).\ - repeat(n, 1, 1, 1) - - position_feat_x_reshape = position_feat_x.\ - view(n, num_heads, w * w_kv, self.qk_embed_dim) - - position_feat_y_reshape = position_feat_y.\ - view(n, num_heads, h * h_kv, self.qk_embed_dim) - - energy_x = torch.matmul(position_feat_x_reshape, geom_bias) - energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) - - energy_y = torch.matmul(position_feat_y_reshape, geom_bias) - energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) - - energy += energy_x + energy_y - - energy = energy.view(n, num_heads, h * w, h_kv * w_kv) - - if self.spatial_range >= 0: - cur_local_constraint_map = \ - self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ - contiguous().\ - view(1, 1, h*w, h_kv*w_kv) - - energy = energy.masked_fill_(cur_local_constraint_map, - float('-inf')) - - attention = F.softmax(energy, 3) - - proj_value = self.value_conv(x_kv) - proj_value_reshape = proj_value.\ - view((n, num_heads, self.v_dim, h_kv * w_kv)).\ - permute(0, 1, 3, 2) - - out = torch.matmul(attention, proj_value_reshape).\ - permute(0, 1, 3, 2).\ - contiguous().\ - view(n, self.v_dim * self.num_heads, h, w) - - out = self.proj_conv(out) - - # output is downsampled, upsample back to input size - if self.q_downsample is not None: - out = F.interpolate( - out, - size=x_input.shape[2:], - mode='bilinear', - align_corners=False) - - out = self.gamma * out + x_input - return out - - def init_weights(self): - for m in self.modules(): - if hasattr(m, 'kaiming_init') and m.kaiming_init: - kaiming_init( - m, - mode='fan_in', - nonlinearity='leaky_relu', - bias=0, - distribution='uniform', - a=1) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/hsigmoid.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/hsigmoid.py deleted file mode 100644 index e013d739e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/hsigmoid.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSigmoid(nn.Module): - """Hard Sigmoid Module. Apply the hard sigmoid function: - Hsigmoid(x) = min(max((x + bias) / divisor, min_value), max_value) - Default: Hsigmoid(x) = min(max((x + 3) / 6, 0), 1) - - Note: - In MMCV v1.4.4, we modified the default value of args to align with - PyTorch official. - - Args: - bias (float): Bias of the input feature map. Default: 3.0. - divisor (float): Divisor of the input feature map. Default: 6.0. - min_value (float): Lower bound value. Default: 0.0. - max_value (float): Upper bound value. Default: 1.0. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, bias=3.0, divisor=6.0, min_value=0.0, max_value=1.0): - super(HSigmoid, self).__init__() - warnings.warn( - 'In MMCV v1.4.4, we modified the default value of args to align ' - 'with PyTorch official. Previous Implementation: ' - 'Hsigmoid(x) = min(max((x + 1) / 2, 0), 1). ' - 'Current Implementation: ' - 'Hsigmoid(x) = min(max((x + 3) / 6, 0), 1).') - self.bias = bias - self.divisor = divisor - assert self.divisor != 0 - self.min_value = min_value - self.max_value = max_value - - def forward(self, x): - x = (x + self.bias) / self.divisor - - return x.clamp_(self.min_value, self.max_value) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/hswish.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/hswish.py deleted file mode 100644 index 27096832f..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/hswish.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import ACTIVATION_LAYERS - - -class HSwish(nn.Module): - """Hard Swish Module. - - This module applies the hard swish function: - - .. math:: - Hswish(x) = x * ReLU6(x + 3) / 6 - - Args: - inplace (bool): can optionally do the operation in-place. - Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.act = nn.ReLU6(inplace) - - def forward(self, x): - return x * self.act(x + 3) / 6 - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.7')): - # Hardswish is not supported when PyTorch version < 1.6. - # And Hardswish in PyTorch 1.6 does not support inplace. - ACTIVATION_LAYERS.register_module(module=HSwish) -else: - ACTIVATION_LAYERS.register_module(module=nn.Hardswish, name='HSwish') diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/non_local.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/non_local.py deleted file mode 100644 index 92d00155e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/non_local.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta - -import torch -import torch.nn as nn - -from ..utils import constant_init, normal_init -from .conv_module import ConvModule -from .registry import PLUGIN_LAYERS - - -class _NonLocalNd(nn.Module, metaclass=ABCMeta): - """Basic Non-local module. - - This module is proposed in - "Non-local Neural Networks" - Paper reference: https://arxiv.org/abs/1711.07971 - Code reference: https://github.com/AlexHex7/Non-local_pytorch - - Args: - in_channels (int): Channels of the input feature map. - reduction (int): Channel reduction ratio. Default: 2. - use_scale (bool): Whether to scale pairwise_weight by - `1/sqrt(inter_channels)` when the mode is `embedded_gaussian`. - Default: True. - conv_cfg (None | dict): The config dict for convolution layers. - If not specified, it will use `nn.Conv2d` for convolution layers. - Default: None. - norm_cfg (None | dict): The config dict for normalization layers. - Default: None. (This parameter is only applicable to conv_out.) - mode (str): Options are `gaussian`, `concatenation`, - `embedded_gaussian` and `dot_product`. Default: embedded_gaussian. - """ - - def __init__(self, - in_channels, - reduction=2, - use_scale=True, - conv_cfg=None, - norm_cfg=None, - mode='embedded_gaussian', - **kwargs): - super(_NonLocalNd, self).__init__() - self.in_channels = in_channels - self.reduction = reduction - self.use_scale = use_scale - self.inter_channels = max(in_channels // reduction, 1) - self.mode = mode - - if mode not in [ - 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation' - ]: - raise ValueError("Mode should be in 'gaussian', 'concatenation', " - f"'embedded_gaussian' or 'dot_product', but got " - f'{mode} instead.') - - # g, theta, phi are defaulted as `nn.ConvNd`. - # Here we use ConvModule for potential usage. - self.g = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.conv_out = ConvModule( - self.inter_channels, - self.in_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - if self.mode != 'gaussian': - self.theta = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.phi = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - - if self.mode == 'concatenation': - self.concat_project = ConvModule( - self.inter_channels * 2, - 1, - kernel_size=1, - stride=1, - padding=0, - bias=False, - act_cfg=dict(type='ReLU')) - - self.init_weights(**kwargs) - - def init_weights(self, std=0.01, zeros_init=True): - if self.mode != 'gaussian': - for m in [self.g, self.theta, self.phi]: - normal_init(m.conv, std=std) - else: - normal_init(self.g.conv, std=std) - if zeros_init: - if self.conv_out.norm_cfg is None: - constant_init(self.conv_out.conv, 0) - else: - constant_init(self.conv_out.norm, 0) - else: - if self.conv_out.norm_cfg is None: - normal_init(self.conv_out.conv, std=std) - else: - normal_init(self.conv_out.norm, std=std) - - def gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def embedded_gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - if self.use_scale: - # theta_x.shape[-1] is `self.inter_channels` - pairwise_weight /= theta_x.shape[-1]**0.5 - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def dot_product(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight /= pairwise_weight.shape[-1] - return pairwise_weight - - def concatenation(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - h = theta_x.size(2) - w = phi_x.size(3) - theta_x = theta_x.repeat(1, 1, 1, w) - phi_x = phi_x.repeat(1, 1, h, 1) - - concat_feature = torch.cat([theta_x, phi_x], dim=1) - pairwise_weight = self.concat_project(concat_feature) - n, _, h, w = pairwise_weight.size() - pairwise_weight = pairwise_weight.view(n, h, w) - pairwise_weight /= pairwise_weight.shape[-1] - - return pairwise_weight - - def forward(self, x): - # Assume `reduction = 1`, then `inter_channels = C` - # or `inter_channels = C` when `mode="gaussian"` - - # NonLocal1d x: [N, C, H] - # NonLocal2d x: [N, C, H, W] - # NonLocal3d x: [N, C, T, H, W] - n = x.size(0) - - # NonLocal1d g_x: [N, H, C] - # NonLocal2d g_x: [N, HxW, C] - # NonLocal3d g_x: [N, TxHxW, C] - g_x = self.g(x).view(n, self.inter_channels, -1) - g_x = g_x.permute(0, 2, 1) - - # NonLocal1d theta_x: [N, H, C], phi_x: [N, C, H] - # NonLocal2d theta_x: [N, HxW, C], phi_x: [N, C, HxW] - # NonLocal3d theta_x: [N, TxHxW, C], phi_x: [N, C, TxHxW] - if self.mode == 'gaussian': - theta_x = x.view(n, self.in_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - if self.sub_sample: - phi_x = self.phi(x).view(n, self.in_channels, -1) - else: - phi_x = x.view(n, self.in_channels, -1) - elif self.mode == 'concatenation': - theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) - phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) - else: - theta_x = self.theta(x).view(n, self.inter_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - phi_x = self.phi(x).view(n, self.inter_channels, -1) - - pairwise_func = getattr(self, self.mode) - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = pairwise_func(theta_x, phi_x) - - # NonLocal1d y: [N, H, C] - # NonLocal2d y: [N, HxW, C] - # NonLocal3d y: [N, TxHxW, C] - y = torch.matmul(pairwise_weight, g_x) - # NonLocal1d y: [N, C, H] - # NonLocal2d y: [N, C, H, W] - # NonLocal3d y: [N, C, T, H, W] - y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, - *x.size()[2:]) - - output = x + self.conv_out(y) - - return output - - -class NonLocal1d(_NonLocalNd): - """1D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv1d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv1d'), - **kwargs): - super(NonLocal1d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool1d(kernel_size=2) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -@PLUGIN_LAYERS.register_module() -class NonLocal2d(_NonLocalNd): - """2D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv2d'). - """ - - _abbr_ = 'nonlocal_block' - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv2d'), - **kwargs): - super(NonLocal2d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -class NonLocal3d(_NonLocalNd): - """3D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv3d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv3d'), - **kwargs): - super(NonLocal3d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/norm.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/norm.py deleted file mode 100644 index 51efdc184..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/norm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import torch.nn as nn - -from mmcv.utils import is_tuple_of -from mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm -from .registry import NORM_LAYERS - -NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) -NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) -NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) -NORM_LAYERS.register_module('GN', module=nn.GroupNorm) -NORM_LAYERS.register_module('LN', module=nn.LayerNorm) -NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) -NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - When we build a norm layer with `build_norm_layer()`, we want to preserve - the norm type in variable names, e.g, self.bn1, self.gn. This method will - infer the abbreviation to map class types to abbreviations. - - Rule 1: If the class has the property "_abbr_", return the property. - Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or - InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and - "in" respectively. - Rule 3: If the class name contains "batch", "group", "layer" or "instance", - the abbreviation of this layer will be "bn", "gn", "ln" and "in" - respectively. - Rule 4: Otherwise, the abbreviation falls back to "norm". - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN - return 'in' - elif issubclass(class_type, _BatchNorm): - return 'bn' - elif issubclass(class_type, nn.GroupNorm): - return 'gn' - elif issubclass(class_type, nn.LayerNorm): - return 'ln' - else: - class_name = class_type.__name__.lower() - if 'batch' in class_name: - return 'bn' - elif 'group' in class_name: - return 'gn' - elif 'layer' in class_name: - return 'ln' - elif 'instance' in class_name: - return 'in' - else: - return 'norm_layer' - - -def build_norm_layer(cfg, num_features, postfix=''): - """Build normalization layer. - - Args: - cfg (dict): The norm layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate a norm layer. - - requires_grad (bool, optional): Whether stop gradient updates. - num_features (int): Number of input channels. - postfix (int | str): The postfix to be appended into norm abbreviation - to create named layer. - - Returns: - tuple[str, nn.Module]: The first element is the layer name consisting - of abbreviation and postfix, e.g., bn1, gn. The second element is the - created norm layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in NORM_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - - norm_layer = NORM_LAYERS.get(layer_type) - abbr = infer_abbr(norm_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - requires_grad = cfg_.pop('requires_grad', True) - cfg_.setdefault('eps', 1e-5) - if layer_type != 'GN': - layer = norm_layer(num_features, **cfg_) - if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): - layer._specify_ddp_gpu_num(1) - else: - assert 'num_groups' in cfg_ - layer = norm_layer(num_channels=num_features, **cfg_) - - for param in layer.parameters(): - param.requires_grad = requires_grad - - return name, layer - - -def is_norm(layer, exclude=None): - """Check if a layer is a normalization layer. - - Args: - layer (nn.Module): The layer to be checked. - exclude (type | tuple[type]): Types to be excluded. - - Returns: - bool: Whether the layer is a norm layer. - """ - if exclude is not None: - if not isinstance(exclude, tuple): - exclude = (exclude, ) - if not is_tuple_of(exclude, type): - raise TypeError( - f'"exclude" must be either None or type or a tuple of types, ' - f'but got {type(exclude)}: {exclude}') - - if exclude and isinstance(layer, exclude): - return False - - all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) - return isinstance(layer, all_norm_bases) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/padding.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/padding.py deleted file mode 100644 index e4ac6b28a..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/padding.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import PADDING_LAYERS - -PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) -PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) -PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) - - -def build_padding_layer(cfg, *args, **kwargs): - """Build padding layer. - - Args: - cfg (None or dict): The padding layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate a padding layer. - - Returns: - nn.Module: Created padding layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - - cfg_ = cfg.copy() - padding_type = cfg_.pop('type') - if padding_type not in PADDING_LAYERS: - raise KeyError(f'Unrecognized padding type {padding_type}.') - else: - padding_layer = PADDING_LAYERS.get(padding_type) - - layer = padding_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/plugin.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/plugin.py deleted file mode 100644 index 009f7529b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/plugin.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import platform - -from .registry import PLUGIN_LAYERS - -if platform.system() == 'Windows': - import regex as re -else: - import re - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - This method will infer the abbreviation to map class types to - abbreviations. - - Rule 1: If the class has the property "abbr", return the property. - Rule 2: Otherwise, the abbreviation falls back to snake case of class - name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - - def camel2snack(word): - """Convert camel case word into snack case. - - Modified from `inflection lib - `_. - - Example:: - - >>> camel2snack("FancyBlock") - 'fancy_block' - """ - - word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) - word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) - word = word.replace('-', '_') - return word.lower() - - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - else: - return camel2snack(class_type.__name__) - - -def build_plugin_layer(cfg, postfix='', **kwargs): - """Build plugin layer. - - Args: - cfg (None or dict): cfg should contain: - - - type (str): identify plugin layer type. - - layer args: args needed to instantiate a plugin layer. - postfix (int, str): appended into norm abbreviation to - create named layer. Default: ''. - - Returns: - tuple[str, nn.Module]: The first one is the concatenation of - abbreviation and postfix. The second is the created plugin layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in PLUGIN_LAYERS: - raise KeyError(f'Unrecognized plugin type {layer_type}') - - plugin_layer = PLUGIN_LAYERS.get(layer_type) - abbr = infer_abbr(plugin_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - layer = plugin_layer(**kwargs, **cfg_) - - return name, layer diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/registry.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/registry.py deleted file mode 100644 index c29279776..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/registry.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -CONV_LAYERS = Registry('conv layer') -NORM_LAYERS = Registry('norm layer') -ACTIVATION_LAYERS = Registry('activation layer') -PADDING_LAYERS = Registry('padding layer') -UPSAMPLE_LAYERS = Registry('upsample layer') -PLUGIN_LAYERS = Registry('plugin layer') - -DROPOUT_LAYERS = Registry('drop out layers') -POSITIONAL_ENCODING = Registry('position encoding') -ATTENTION = Registry('attention') -FEEDFORWARD_NETWORK = Registry('feed-forward Network') -TRANSFORMER_LAYER = Registry('transformerLayer') -TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/scale.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/scale.py deleted file mode 100644 index c905fffcc..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/scale.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class Scale(nn.Module): - """A learnable scale parameter. - - This layer scales the input by a learnable factor. It multiplies a - learnable scale parameter of shape (1,) with input of any shape. - - Args: - scale (float): Initial value of scale factor. Default: 1.0 - """ - - def __init__(self, scale=1.0): - super(Scale, self).__init__() - self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) - - def forward(self, x): - return x * self.scale diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/swish.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/swish.py deleted file mode 100644 index e2ca8ed7b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/swish.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class Swish(nn.Module): - """Swish Module. - - This module applies the swish function: - - .. math:: - Swish(x) = x * Sigmoid(x) - - Returns: - Tensor: The output tensor. - """ - - def __init__(self): - super(Swish, self).__init__() - - def forward(self, x): - return x * torch.sigmoid(x) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/transformer.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/transformer.py deleted file mode 100644 index 70c6623c7..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/transformer.py +++ /dev/null @@ -1,944 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings -from typing import Sequence - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.cnn import (Linear, build_activation_layer, build_conv_layer, - build_norm_layer) -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential -from mmcv.utils import (ConfigDict, build_from_cfg, deprecated_api_warning, - to_2tuple) -from .drop import build_dropout -from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, - TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) - -# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file -try: - from mmcv.ops.multi_scale_deform_attn import \ - MultiScaleDeformableAttention # noqa F401 - warnings.warn( - ImportWarning( - '``MultiScaleDeformableAttention`` has been moved to ' - '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 - '``from mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 - 'to ``from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 - )) - -except ImportError: - warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' - '``mmcv.ops.multi_scale_deform_attn``, ' - 'You should install ``mmcv-full`` if you need this module. ') - - -def build_positional_encoding(cfg, default_args=None): - """Builder for Position Encoding.""" - return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) - - -def build_attention(cfg, default_args=None): - """Builder for attention.""" - return build_from_cfg(cfg, ATTENTION, default_args) - - -def build_feedforward_network(cfg, default_args=None): - """Builder for feed-forward network (FFN).""" - return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) - - -def build_transformer_layer(cfg, default_args=None): - """Builder for transformer layer.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) - - -def build_transformer_layer_sequence(cfg, default_args=None): - """Builder for transformer encoder and transformer decoder.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) - - -class AdaptivePadding(nn.Module): - """Applies padding adaptively to the input. - - This module can make input get fully covered by filter - you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad - zero around input. The "corner" mode would pad zero - to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel. Default: 1. - stride (int | tuple): Stride of the filter. Default: 1. - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - super(AdaptivePadding, self).__init__() - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - """Calculate the padding size of input. - - Args: - input_shape (:obj:`torch.Size`): arrange as (H, W). - - Returns: - Tuple[int]: The padding size along the - original H and W directions - """ - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - """Add padding to `x` - - Args: - x (Tensor): Input tensor has shape (B, C, H, W). - - Returns: - Tensor: The tensor with adaptive padding - """ - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The type of convolution - to generate patch embedding. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int): The slide stride of embedding conv. - Default: 16. - padding (int | tuple | string): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only works when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=16, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super(PatchEmbed, self).__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adaptive_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # e.g. when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adaptive_padding: - pad_h, pad_w = self.adaptive_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adaptive_padding: - x = self.adaptive_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map ((used in Swin Transformer)). - Our implementation uses `nn.Unfold` to - merge patches, which is about 25% faster than the original - implementation. However, we need to modify pretrained - models for compatibility. - - Args: - in_channels (int): The num of input channels. - to gets fully covered by filter and stride you specified. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adaptive_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - - if self.adaptive_padding: - x = self.adaptive_padding(x) - H, W = x.shape[-2:] - - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - x = self.sampler(x) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size - - -@ATTENTION.register_module() -class MultiheadAttention(BaseModule): - """A wrapper for ``torch.nn.MultiheadAttention``. - - This module implements MultiheadAttention with identity connection, - and positional encoding is also passed as input. - - Args: - embed_dims (int): The embedding dimension. - num_heads (int): Parallel attention heads. - attn_drop (float): A Dropout layer on attn_output_weights. - Default: 0.0. - proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. - Default: 0.0. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): When it is True, Key, Query and Value are shape of - (batch, n, embed_dim), otherwise (n, batch, embed_dim). - Default to False. - """ - - def __init__(self, - embed_dims, - num_heads, - attn_drop=0., - proj_drop=0., - dropout_layer=dict(type='Dropout', drop_prob=0.), - init_cfg=None, - batch_first=False, - **kwargs): - super(MultiheadAttention, self).__init__(init_cfg) - if 'dropout' in kwargs: - warnings.warn( - 'The arguments `dropout` in MultiheadAttention ' - 'has been deprecated, now you can separately ' - 'set `attn_drop`(float), proj_drop(float), ' - 'and `dropout_layer`(dict) ', DeprecationWarning) - attn_drop = kwargs['dropout'] - dropout_layer['drop_prob'] = kwargs.pop('dropout') - - self.embed_dims = embed_dims - self.num_heads = num_heads - self.batch_first = batch_first - - self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, - **kwargs) - - self.proj_drop = nn.Dropout(proj_drop) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else nn.Identity() - - @deprecated_api_warning({'residual': 'identity'}, - cls_name='MultiheadAttention') - def forward(self, - query, - key=None, - value=None, - identity=None, - query_pos=None, - key_pos=None, - attn_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `MultiheadAttention`. - - **kwargs allow passing a more general data flow when combining - with other operations in `transformerlayer`. - - Args: - query (Tensor): The input query with shape [num_queries, bs, - embed_dims] if self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - If None, the ``query`` will be used. Defaults to None. - value (Tensor): The value tensor with same shape as `key`. - Same in `nn.MultiheadAttention.forward`. Defaults to None. - If None, the `key` will be used. - identity (Tensor): This tensor, with the same shape as x, - will be used for the identity link. - If None, `x` will be used. Defaults to None. - query_pos (Tensor): The positional encoding for query, with - the same shape as `x`. If not None, it will - be added to `x` before forward function. Defaults to None. - key_pos (Tensor): The positional encoding for `key`, with the - same shape as `key`. Defaults to None. If not None, it will - be added to `key` before forward function. If None, and - `query_pos` has the same shape as `key`, then `query_pos` - will be used for `key_pos`. Defaults to None. - attn_mask (Tensor): ByteTensor mask with shape [num_queries, - num_keys]. Same in `nn.MultiheadAttention.forward`. - Defaults to None. - key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. - Defaults to None. - - Returns: - Tensor: forwarded results with shape - [num_queries, bs, embed_dims] - if self.batch_first is False, else - [bs, num_queries embed_dims]. - """ - - if key is None: - key = query - if value is None: - value = key - if identity is None: - identity = query - if key_pos is None: - if query_pos is not None: - # use query_pos if key_pos is not available - if query_pos.shape == key.shape: - key_pos = query_pos - else: - warnings.warn(f'position encoding of key is' - f'missing in {self.__class__.__name__}.') - if query_pos is not None: - query = query + query_pos - if key_pos is not None: - key = key + key_pos - - # Because the dataflow('key', 'query', 'value') of - # ``torch.nn.MultiheadAttention`` is (num_query, batch, - # embed_dims), We should adjust the shape of dataflow from - # batch_first (batch, num_query, embed_dims) to num_query_first - # (num_query ,batch, embed_dims), and recover ``attn_output`` - # from num_query_first to batch_first. - if self.batch_first: - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - out = self.attn( - query=query, - key=key, - value=value, - attn_mask=attn_mask, - key_padding_mask=key_padding_mask)[0] - - if self.batch_first: - out = out.transpose(0, 1) - - return identity + self.dropout_layer(self.proj_drop(out)) - - -@FEEDFORWARD_NETWORK.register_module() -class FFN(BaseModule): - """Implements feed-forward networks (FFNs) with identity connection. - - Args: - embed_dims (int): The feature dimension. Same as - `MultiheadAttention`. Defaults: 256. - feedforward_channels (int): The hidden dimension of FFNs. - Defaults: 1024. - num_fcs (int, optional): The number of fully-connected layers in - FFNs. Default: 2. - act_cfg (dict, optional): The activation config for FFNs. - Default: dict(type='ReLU') - ffn_drop (float, optional): Probability of an element to be - zeroed in FFN. Default 0.0. - add_identity (bool, optional): Whether to add the - identity connection. Default: `True`. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - @deprecated_api_warning( - { - 'dropout': 'ffn_drop', - 'add_residual': 'add_identity' - }, - cls_name='FFN') - def __init__(self, - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - act_cfg=dict(type='ReLU', inplace=True), - ffn_drop=0., - dropout_layer=None, - add_identity=True, - init_cfg=None, - **kwargs): - super(FFN, self).__init__(init_cfg) - assert num_fcs >= 2, 'num_fcs should be no less ' \ - f'than 2. got {num_fcs}.' - self.embed_dims = embed_dims - self.feedforward_channels = feedforward_channels - self.num_fcs = num_fcs - self.act_cfg = act_cfg - self.activate = build_activation_layer(act_cfg) - - layers = [] - in_channels = embed_dims - for _ in range(num_fcs - 1): - layers.append( - Sequential( - Linear(in_channels, feedforward_channels), self.activate, - nn.Dropout(ffn_drop))) - in_channels = feedforward_channels - layers.append(Linear(feedforward_channels, embed_dims)) - layers.append(nn.Dropout(ffn_drop)) - self.layers = Sequential(*layers) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else torch.nn.Identity() - self.add_identity = add_identity - - @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') - def forward(self, x, identity=None): - """Forward function for `FFN`. - - The function would add x to the output tensor if residue is None. - """ - out = self.layers(x) - if not self.add_identity: - return self.dropout_layer(out) - if identity is None: - identity = x - return identity + self.dropout_layer(out) - - -@TRANSFORMER_LAYER.register_module() -class BaseTransformerLayer(BaseModule): - """Base `TransformerLayer` for vision transformer. - - It can be built from `mmcv.ConfigDict` and support more flexible - customization, for example, using any number of `FFN or LN ` and - use different kinds of `attention` by specifying a list of `ConfigDict` - named `attn_cfgs`. It is worth mentioning that it supports `prenorm` - when you specifying `norm` as the first element of `operation_order`. - More details about the `prenorm`: `On Layer Normalization in the - Transformer Architecture `_ . - - Args: - attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for `self_attention` or `cross_attention` modules, - The order of the configs in the list should be consistent with - corresponding attentions in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. Default: None. - ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for FFN, The order of the configs in the list should be - consistent with corresponding ffn in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. - operation_order (tuple[str]): The execution order of operation - in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). - Support `prenorm` when you specifying first element as `norm`. - Default:None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): Key, Query and Value are shape - of (batch, n, embed_dim) - or (n, batch, embed_dim). Default to False. - """ - - def __init__(self, - attn_cfgs=None, - ffn_cfgs=dict( - type='FFN', - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - ffn_drop=0., - act_cfg=dict(type='ReLU', inplace=True), - ), - operation_order=None, - norm_cfg=dict(type='LN'), - init_cfg=None, - batch_first=False, - **kwargs): - - deprecated_args = dict( - feedforward_channels='feedforward_channels', - ffn_dropout='ffn_drop', - ffn_num_fcs='num_fcs') - for ori_name, new_name in deprecated_args.items(): - if ori_name in kwargs: - warnings.warn( - f'The arguments `{ori_name}` in BaseTransformerLayer ' - f'has been deprecated, now you should set `{new_name}` ' - f'and other FFN related arguments ' - f'to a dict named `ffn_cfgs`. ', DeprecationWarning) - ffn_cfgs[new_name] = kwargs[ori_name] - - super(BaseTransformerLayer, self).__init__(init_cfg) - - self.batch_first = batch_first - - assert set(operation_order) & set( - ['self_attn', 'norm', 'ffn', 'cross_attn']) == \ - set(operation_order), f'The operation_order of' \ - f' {self.__class__.__name__} should ' \ - f'contains all four operation type ' \ - f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" - - num_attn = operation_order.count('self_attn') + operation_order.count( - 'cross_attn') - if isinstance(attn_cfgs, dict): - attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] - else: - assert num_attn == len(attn_cfgs), f'The length ' \ - f'of attn_cfg {num_attn} is ' \ - f'not consistent with the number of attention' \ - f'in operation_order {operation_order}.' - - self.num_attn = num_attn - self.operation_order = operation_order - self.norm_cfg = norm_cfg - self.pre_norm = operation_order[0] == 'norm' - self.attentions = ModuleList() - - index = 0 - for operation_name in operation_order: - if operation_name in ['self_attn', 'cross_attn']: - if 'batch_first' in attn_cfgs[index]: - assert self.batch_first == attn_cfgs[index]['batch_first'] - else: - attn_cfgs[index]['batch_first'] = self.batch_first - attention = build_attention(attn_cfgs[index]) - # Some custom attentions used as `self_attn` - # or `cross_attn` can have different behavior. - attention.operation_name = operation_name - self.attentions.append(attention) - index += 1 - - self.embed_dims = self.attentions[0].embed_dims - - self.ffns = ModuleList() - num_ffns = operation_order.count('ffn') - if isinstance(ffn_cfgs, dict): - ffn_cfgs = ConfigDict(ffn_cfgs) - if isinstance(ffn_cfgs, dict): - ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] - assert len(ffn_cfgs) == num_ffns - for ffn_index in range(num_ffns): - if 'embed_dims' not in ffn_cfgs[ffn_index]: - ffn_cfgs[ffn_index]['embed_dims'] = self.embed_dims - else: - assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims - self.ffns.append( - build_feedforward_network(ffn_cfgs[ffn_index], - dict(type='FFN'))) - - self.norms = ModuleList() - num_norms = operation_order.count('norm') - for _ in range(num_norms): - self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) - - def forward(self, - query, - key=None, - value=None, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerDecoderLayer`. - - **kwargs contains some specific arguments of attentions. - - Args: - query (Tensor): The input query with shape - [num_queries, bs, embed_dims] if - self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - value (Tensor): The value tensor with same shape as `key`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor] | None): 2D Tensor used in - calculation of corresponding attention. The length of - it should equal to the number of `attention` in - `operation_order`. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in `self_attn` layer. - Defaults to None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: forwarded results with shape [num_queries, bs, embed_dims]. - """ - - norm_index = 0 - attn_index = 0 - ffn_index = 0 - identity = query - if attn_masks is None: - attn_masks = [None for _ in range(self.num_attn)] - elif isinstance(attn_masks, torch.Tensor): - attn_masks = [ - copy.deepcopy(attn_masks) for _ in range(self.num_attn) - ] - warnings.warn(f'Use same attn_mask in all attentions in ' - f'{self.__class__.__name__} ') - else: - assert len(attn_masks) == self.num_attn, f'The length of ' \ - f'attn_masks {len(attn_masks)} must be equal ' \ - f'to the number of attention in ' \ - f'operation_order {self.num_attn}' - - for layer in self.operation_order: - if layer == 'self_attn': - temp_key = temp_value = query - query = self.attentions[attn_index]( - query, - temp_key, - temp_value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=query_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=query_key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'norm': - query = self.norms[norm_index](query) - norm_index += 1 - - elif layer == 'cross_attn': - query = self.attentions[attn_index]( - query, - key, - value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=key_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'ffn': - query = self.ffns[ffn_index]( - query, identity if self.pre_norm else None) - ffn_index += 1 - - return query - - -@TRANSFORMER_LAYER_SEQUENCE.register_module() -class TransformerLayerSequence(BaseModule): - """Base class for TransformerEncoder and TransformerDecoder in vision - transformer. - - As base-class of Encoder and Decoder in vision transformer. - Support customization such as specifying different kind - of `transformer_layer` in `transformer_coder`. - - Args: - transformerlayer (list[obj:`mmcv.ConfigDict`] | - obj:`mmcv.ConfigDict`): Config of transformerlayer - in TransformerCoder. If it is obj:`mmcv.ConfigDict`, - it would be repeated `num_layer` times to a - list[`mmcv.ConfigDict`]. Default: None. - num_layers (int): The number of `TransformerLayer`. Default: None. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): - super(TransformerLayerSequence, self).__init__(init_cfg) - if isinstance(transformerlayers, dict): - transformerlayers = [ - copy.deepcopy(transformerlayers) for _ in range(num_layers) - ] - else: - assert isinstance(transformerlayers, list) and \ - len(transformerlayers) == num_layers - self.num_layers = num_layers - self.layers = ModuleList() - for i in range(num_layers): - self.layers.append(build_transformer_layer(transformerlayers[i])) - self.embed_dims = self.layers[0].embed_dims - self.pre_norm = self.layers[0].pre_norm - - def forward(self, - query, - key, - value, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerCoder`. - - Args: - query (Tensor): Input query with shape - `(num_queries, bs, embed_dims)`. - key (Tensor): The key tensor with shape - `(num_keys, bs, embed_dims)`. - value (Tensor): The value tensor with shape - `(num_keys, bs, embed_dims)`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor], optional): Each element is 2D Tensor - which is used in calculation of corresponding attention in - operation_order. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in self-attention - Default: None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: results with shape [num_queries, bs, embed_dims]. - """ - for layer in self.layers: - query = layer( - query, - key, - value, - query_pos=query_pos, - key_pos=key_pos, - attn_masks=attn_masks, - query_key_padding_mask=query_key_padding_mask, - key_padding_mask=key_padding_mask, - **kwargs) - return query diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/upsample.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/upsample.py deleted file mode 100644 index 0fd21fbf9..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/upsample.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import xavier_init -from .registry import UPSAMPLE_LAYERS - -UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) -UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) - - -@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') -class PixelShufflePack(nn.Module): - """Pixel Shuffle upsample layer. - - This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to - achieve a simple upsampling with pixel shuffle. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of the conv layer to expand the - channels. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super(PixelShufflePack, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - xavier_init(self.upsample_conv, distribution='uniform') - - def forward(self, x): - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x - - -def build_upsample_layer(cfg, *args, **kwargs): - """Build upsample layer. - - Args: - cfg (dict): The upsample layer config, which should contain: - - - type (str): Layer type. - - scale_factor (int): Upsample ratio, which is not applicable to - deconv. - - layer args: Args needed to instantiate a upsample layer. - args (argument list): Arguments passed to the ``__init__`` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the - ``__init__`` method of the corresponding conv layer. - - Returns: - nn.Module: Created upsample layer. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - raise KeyError( - f'the cfg dict must contain the key "type", but got {cfg}') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in UPSAMPLE_LAYERS: - raise KeyError(f'Unrecognized upsample type {layer_type}') - else: - upsample = UPSAMPLE_LAYERS.get(layer_type) - - if upsample is nn.Upsample: - cfg_['mode'] = layer_type - layer = upsample(*args, **kwargs, **cfg_) - return layer diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/wrappers.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/wrappers.py deleted file mode 100644 index 8aebf67bf..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/bricks/wrappers.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 - -Wrap some nn modules to support empty tensor input. Currently, these wrappers -are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask -heads are trained on only positive RoIs. -""" -import math - -import torch -import torch.nn as nn -from torch.nn.modules.utils import _pair, _triple - -from .registry import CONV_LAYERS, UPSAMPLE_LAYERS - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def obsolete_torch_version(torch_version, version_threshold): - return torch_version == 'parrots' or torch_version <= version_threshold - - -class NewEmptyTensorOp(torch.autograd.Function): - - @staticmethod - def forward(ctx, x, new_shape): - ctx.shape = x.shape - return x.new_empty(new_shape) - - @staticmethod - def backward(ctx, grad): - shape = ctx.shape - return NewEmptyTensorOp.apply(grad, shape), None - - -@CONV_LAYERS.register_module('Conv', force=True) -class Conv2d(nn.Conv2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module('Conv3d', force=True) -class Conv3d(nn.Conv3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv') -@UPSAMPLE_LAYERS.register_module('deconv', force=True) -class ConvTranspose2d(nn.ConvTranspose2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv3d') -@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) -class ConvTranspose3d(nn.ConvTranspose3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -class MaxPool2d(nn.MaxPool2d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), - _pair(self.padding), _pair(self.stride), - _pair(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class MaxPool3d(nn.MaxPool3d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), - _triple(self.padding), - _triple(self.stride), - _triple(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class Linear(torch.nn.Linear): - - def forward(self, x): - # empty tensor forward of Linear layer is supported in Pytorch 1.6 - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): - out_shape = [x.shape[0], self.out_features] - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/builder.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/builder.py deleted file mode 100644 index 7567316c5..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/builder.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..runner import Sequential -from ..utils import Registry, build_from_cfg - - -def build_model_from_cfg(cfg, registry, default_args=None): - """Build a PyTorch model from config dict(s). Different from - ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. - - Args: - cfg (dict, list[dict]): The config of modules, is is either a config - dict or a list of config dicts. If cfg is a list, a - the built modules will be wrapped with ``nn.Sequential``. - registry (:obj:`Registry`): A registry the module belongs to. - default_args (dict, optional): Default arguments to build the module. - Defaults to None. - - Returns: - nn.Module: A built nn module. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return Sequential(*modules) - else: - return build_from_cfg(cfg, registry, default_args) - - -MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/__init__.py deleted file mode 100644 index a263e31c1..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .flops_counter import get_model_complexity_info -from .fuse_conv_bn import fuse_conv_bn -from .sync_bn import revert_sync_batchnorm -from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, - KaimingInit, NormalInit, PretrainedInit, - TruncNormalInit, UniformInit, XavierInit, - bias_init_with_prob, caffe2_xavier_init, - constant_init, initialize, kaiming_init, normal_init, - trunc_normal_init, uniform_init, xavier_init) - -__all__ = [ - 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', - 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', - 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'revert_sync_batchnorm' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/flops_counter.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/flops_counter.py deleted file mode 100644 index a6045db84..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/flops_counter.py +++ /dev/null @@ -1,599 +0,0 @@ -# Modified from flops-counter.pytorch by Vladislav Sovrasov -# original repo: https://github.com/sovrasov/flops-counter.pytorch - -# MIT License - -# Copyright (c) 2018 Vladislav Sovrasov - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -import warnings -from functools import partial - -import numpy as np -import torch -import torch.nn as nn - -import mmcv - - -def get_model_complexity_info(model, - input_shape, - print_per_layer_stat=True, - as_strings=True, - input_constructor=None, - flush=False, - ost=sys.stdout): - """Get complexity information of a model. - - This method can calculate FLOPs and parameter counts of a model with - corresponding input shape. It can also print complexity information for - each layer in a model. - - Supported layers are listed as below: - - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. - - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, - ``nn.LeakyReLU``, ``nn.ReLU6``. - - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, - ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, - ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, - ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, - ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. - - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, - ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, - ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. - - Linear: ``nn.Linear``. - - Deconvolution: ``nn.ConvTranspose2d``. - - Upsample: ``nn.Upsample``. - - Args: - model (nn.Module): The model for complexity calculation. - input_shape (tuple): Input shape used for calculation. - print_per_layer_stat (bool): Whether to print complexity information - for each layer in a model. Default: True. - as_strings (bool): Output FLOPs and params counts in a string form. - Default: True. - input_constructor (None | callable): If specified, it takes a callable - method that generates input. otherwise, it will generate a random - tensor with input shape to calculate FLOPs. Default: None. - flush (bool): same as that in :func:`print`. Default: False. - ost (stream): same as ``file`` param in :func:`print`. - Default: sys.stdout. - - Returns: - tuple[float | str]: If ``as_strings`` is set to True, it will return - FLOPs and parameter counts in a string format. otherwise, it will - return those in a float number format. - """ - assert type(input_shape) is tuple - assert len(input_shape) >= 1 - assert isinstance(model, nn.Module) - flops_model = add_flops_counting_methods(model) - flops_model.eval() - flops_model.start_flops_count() - if input_constructor: - input = input_constructor(input_shape) - _ = flops_model(**input) - else: - try: - batch = torch.ones(()).new_empty( - (1, *input_shape), - dtype=next(flops_model.parameters()).dtype, - device=next(flops_model.parameters()).device) - except StopIteration: - # Avoid StopIteration for models which have no parameters, - # like `nn.Relu()`, `nn.AvgPool2d`, etc. - batch = torch.ones(()).new_empty((1, *input_shape)) - - _ = flops_model(batch) - - flops_count, params_count = flops_model.compute_average_flops_cost() - if print_per_layer_stat: - print_model_with_flops( - flops_model, flops_count, params_count, ost=ost, flush=flush) - flops_model.stop_flops_count() - - if as_strings: - return flops_to_string(flops_count), params_to_string(params_count) - - return flops_count, params_count - - -def flops_to_string(flops, units='GFLOPs', precision=2): - """Convert FLOPs number into a string. - - Note that Here we take a multiply-add counts as one FLOP. - - Args: - flops (float): FLOPs number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', - 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically - choose the most suitable unit for FLOPs. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted FLOPs number with units. - - Examples: - >>> flops_to_string(1e9) - '1.0 GFLOPs' - >>> flops_to_string(2e5, 'MFLOPs') - '0.2 MFLOPs' - >>> flops_to_string(3e-9, None) - '3e-09 FLOPs' - """ - if units is None: - if flops // 10**9 > 0: - return str(round(flops / 10.**9, precision)) + ' GFLOPs' - elif flops // 10**6 > 0: - return str(round(flops / 10.**6, precision)) + ' MFLOPs' - elif flops // 10**3 > 0: - return str(round(flops / 10.**3, precision)) + ' KFLOPs' - else: - return str(flops) + ' FLOPs' - else: - if units == 'GFLOPs': - return str(round(flops / 10.**9, precision)) + ' ' + units - elif units == 'MFLOPs': - return str(round(flops / 10.**6, precision)) + ' ' + units - elif units == 'KFLOPs': - return str(round(flops / 10.**3, precision)) + ' ' + units - else: - return str(flops) + ' FLOPs' - - -def params_to_string(num_params, units=None, precision=2): - """Convert parameter number into a string. - - Args: - num_params (float): Parameter number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'M', - 'K' and ''. If set to None, it will automatically choose the most - suitable unit for Parameter number. Default: None. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted parameter number with units. - - Examples: - >>> params_to_string(1e9) - '1000.0 M' - >>> params_to_string(2e5) - '200.0 k' - >>> params_to_string(3e-9) - '3e-09' - """ - if units is None: - if num_params // 10**6 > 0: - return str(round(num_params / 10**6, precision)) + ' M' - elif num_params // 10**3: - return str(round(num_params / 10**3, precision)) + ' k' - else: - return str(num_params) - else: - if units == 'M': - return str(round(num_params / 10.**6, precision)) + ' ' + units - elif units == 'K': - return str(round(num_params / 10.**3, precision)) + ' ' + units - else: - return str(num_params) - - -def print_model_with_flops(model, - total_flops, - total_params, - units='GFLOPs', - precision=3, - ost=sys.stdout, - flush=False): - """Print a model with FLOPs for each layer. - - Args: - model (nn.Module): The model to be printed. - total_flops (float): Total FLOPs of the model. - total_params (float): Total parameter counts of the model. - units (str | None): Converted FLOPs units. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 3. - ost (stream): same as `file` param in :func:`print`. - Default: sys.stdout. - flush (bool): same as that in :func:`print`. Default: False. - - Example: - >>> class ExampleModel(nn.Module): - - >>> def __init__(self): - >>> super().__init__() - >>> self.conv1 = nn.Conv2d(3, 8, 3) - >>> self.conv2 = nn.Conv2d(8, 256, 3) - >>> self.conv3 = nn.Conv2d(256, 8, 3) - >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) - >>> self.flatten = nn.Flatten() - >>> self.fc = nn.Linear(8, 1) - - >>> def forward(self, x): - >>> x = self.conv1(x) - >>> x = self.conv2(x) - >>> x = self.conv3(x) - >>> x = self.avg_pool(x) - >>> x = self.flatten(x) - >>> x = self.fc(x) - >>> return x - - >>> model = ExampleModel() - >>> x = (3, 16, 16) - to print the complexity information state for each layer, you can use - >>> get_model_complexity_info(model, x) - or directly use - >>> print_model_with_flops(model, 4579784.0, 37361) - ExampleModel( - 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, - (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 - (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) - (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) - (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) - (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) - (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) - ) - """ - - def accumulate_params(self): - if is_supported_instance(self): - return self.__params__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_params() - return sum - - def accumulate_flops(self): - if is_supported_instance(self): - return self.__flops__ / model.__batch_counter__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_flops() - return sum - - def flops_repr(self): - accumulated_num_params = self.accumulate_params() - accumulated_flops_cost = self.accumulate_flops() - return ', '.join([ - params_to_string( - accumulated_num_params, units='M', precision=precision), - '{:.3%} Params'.format(accumulated_num_params / total_params), - flops_to_string( - accumulated_flops_cost, units=units, precision=precision), - '{:.3%} FLOPs'.format(accumulated_flops_cost / total_flops), - self.original_extra_repr() - ]) - - def add_extra_repr(m): - m.accumulate_flops = accumulate_flops.__get__(m) - m.accumulate_params = accumulate_params.__get__(m) - flops_extra_repr = flops_repr.__get__(m) - if m.extra_repr != flops_extra_repr: - m.original_extra_repr = m.extra_repr - m.extra_repr = flops_extra_repr - assert m.extra_repr != m.original_extra_repr - - def del_extra_repr(m): - if hasattr(m, 'original_extra_repr'): - m.extra_repr = m.original_extra_repr - del m.original_extra_repr - if hasattr(m, 'accumulate_flops'): - del m.accumulate_flops - - model.apply(add_extra_repr) - print(model, file=ost, flush=flush) - model.apply(del_extra_repr) - - -def get_model_parameters_number(model): - """Calculate parameter number of a model. - - Args: - model (nn.module): The model for parameter number calculation. - - Returns: - float: Parameter number of the model. - """ - num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - return num_params - - -def add_flops_counting_methods(net_main_module): - # adding additional methods to the existing module object, - # this is done this way so that each function has access to self object - net_main_module.start_flops_count = start_flops_count.__get__( - net_main_module) - net_main_module.stop_flops_count = stop_flops_count.__get__( - net_main_module) - net_main_module.reset_flops_count = reset_flops_count.__get__( - net_main_module) - net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # noqa: E501 - net_main_module) - - net_main_module.reset_flops_count() - - return net_main_module - - -def compute_average_flops_cost(self): - """Compute average FLOPs cost. - - A method to compute average FLOPs cost, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - - Returns: - float: Current mean flops consumption per image. - """ - batches_count = self.__batch_counter__ - flops_sum = 0 - for module in self.modules(): - if is_supported_instance(module): - flops_sum += module.__flops__ - params_sum = get_model_parameters_number(self) - return flops_sum / batches_count, params_sum - - -def start_flops_count(self): - """Activate the computation of mean flops consumption per image. - - A method to activate the computation of mean flops consumption per image. - which will be available after ``add_flops_counting_methods()`` is called on - a desired net object. It should be called before running the network. - """ - add_batch_counter_hook_function(self) - - def add_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - return - - else: - handle = module.register_forward_hook( - get_modules_mapping()[type(module)]) - - module.__flops_handle__ = handle - - self.apply(partial(add_flops_counter_hook_function)) - - -def stop_flops_count(self): - """Stop computing the mean flops consumption per image. - - A method to stop computing the mean flops consumption per image, which will - be available after ``add_flops_counting_methods()`` is called on a desired - net object. It can be called to pause the computation whenever. - """ - remove_batch_counter_hook_function(self) - self.apply(remove_flops_counter_hook_function) - - -def reset_flops_count(self): - """Reset statistics computed so far. - - A method to Reset computed statistics, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - """ - add_batch_counter_variables_or_reset(self) - self.apply(add_flops_counter_variable_or_reset) - - -# ---- Internal functions -def empty_flops_counter_hook(module, input, output): - module.__flops__ += 0 - - -def upsample_flops_counter_hook(module, input, output): - output_size = output[0] - batch_size = output_size.shape[0] - output_elements_count = batch_size - for val in output_size.shape[1:]: - output_elements_count *= val - module.__flops__ += int(output_elements_count) - - -def relu_flops_counter_hook(module, input, output): - active_elements_count = output.numel() - module.__flops__ += int(active_elements_count) - - -def linear_flops_counter_hook(module, input, output): - input = input[0] - output_last_dim = output.shape[ - -1] # pytorch checks dimensions, so here we don't care much - module.__flops__ += int(np.prod(input.shape) * output_last_dim) - - -def pool_flops_counter_hook(module, input, output): - input = input[0] - module.__flops__ += int(np.prod(input.shape)) - - -def norm_flops_counter_hook(module, input, output): - input = input[0] - - batch_flops = np.prod(input.shape) - if (getattr(module, 'affine', False) - or getattr(module, 'elementwise_affine', False)): - batch_flops *= 2 - module.__flops__ += int(batch_flops) - - -def deconv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - input_height, input_width = input.shape[2:] - - kernel_height, kernel_width = conv_module.kernel_size - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = ( - kernel_height * kernel_width * in_channels * filters_per_channel) - - active_elements_count = batch_size * input_height * input_width - overall_conv_flops = conv_per_position_flops * active_elements_count - bias_flops = 0 - if conv_module.bias is not None: - output_height, output_width = output.shape[2:] - bias_flops = out_channels * batch_size * output_height * output_width - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def conv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - output_dims = list(output.shape[2:]) - - kernel_dims = list(conv_module.kernel_size) - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = int( - np.prod(kernel_dims)) * in_channels * filters_per_channel - - active_elements_count = batch_size * int(np.prod(output_dims)) - - overall_conv_flops = conv_per_position_flops * active_elements_count - - bias_flops = 0 - - if conv_module.bias is not None: - - bias_flops = out_channels * active_elements_count - - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def batch_counter_hook(module, input, output): - batch_size = 1 - if len(input) > 0: - # Can have multiple inputs, getting the first one - input = input[0] - batch_size = len(input) - else: - warnings.warn('No positional inputs found for a module, ' - 'assuming batch size is 1.') - module.__batch_counter__ += batch_size - - -def add_batch_counter_variables_or_reset(module): - - module.__batch_counter__ = 0 - - -def add_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - return - - handle = module.register_forward_hook(batch_counter_hook) - module.__batch_counter_handle__ = handle - - -def remove_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - module.__batch_counter_handle__.remove() - del module.__batch_counter_handle__ - - -def add_flops_counter_variable_or_reset(module): - if is_supported_instance(module): - if hasattr(module, '__flops__') or hasattr(module, '__params__'): - warnings.warn('variables __flops__ or __params__ are already ' - 'defined for the module' + type(module).__name__ + - ' ptflops can affect your code!') - module.__flops__ = 0 - module.__params__ = get_model_parameters_number(module) - - -def is_supported_instance(module): - if type(module) in get_modules_mapping(): - return True - return False - - -def remove_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - module.__flops_handle__.remove() - del module.__flops_handle__ - - -def get_modules_mapping(): - return { - # convolutions - nn.Conv1d: conv_flops_counter_hook, - nn.Conv2d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, - nn.Conv3d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, - # activations - nn.ReLU: relu_flops_counter_hook, - nn.PReLU: relu_flops_counter_hook, - nn.ELU: relu_flops_counter_hook, - nn.LeakyReLU: relu_flops_counter_hook, - nn.ReLU6: relu_flops_counter_hook, - # poolings - nn.MaxPool1d: pool_flops_counter_hook, - nn.AvgPool1d: pool_flops_counter_hook, - nn.AvgPool2d: pool_flops_counter_hook, - nn.MaxPool2d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, - nn.MaxPool3d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, - nn.AvgPool3d: pool_flops_counter_hook, - nn.AdaptiveMaxPool1d: pool_flops_counter_hook, - nn.AdaptiveAvgPool1d: pool_flops_counter_hook, - nn.AdaptiveMaxPool2d: pool_flops_counter_hook, - nn.AdaptiveAvgPool2d: pool_flops_counter_hook, - nn.AdaptiveMaxPool3d: pool_flops_counter_hook, - nn.AdaptiveAvgPool3d: pool_flops_counter_hook, - # normalizations - nn.BatchNorm1d: norm_flops_counter_hook, - nn.BatchNorm2d: norm_flops_counter_hook, - nn.BatchNorm3d: norm_flops_counter_hook, - nn.GroupNorm: norm_flops_counter_hook, - nn.InstanceNorm1d: norm_flops_counter_hook, - nn.InstanceNorm2d: norm_flops_counter_hook, - nn.InstanceNorm3d: norm_flops_counter_hook, - nn.LayerNorm: norm_flops_counter_hook, - # FC - nn.Linear: linear_flops_counter_hook, - mmcv.cnn.bricks.Linear: linear_flops_counter_hook, - # Upscale - nn.Upsample: upsample_flops_counter_hook, - # Deconvolution - nn.ConvTranspose2d: deconv_flops_counter_hook, - mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, - } diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py deleted file mode 100644 index cb7076f80..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def _fuse_conv_bn(conv, bn): - """Fuse conv and bn into one module. - - Args: - conv (nn.Module): Conv to be fused. - bn (nn.Module): BN to be fused. - - Returns: - nn.Module: Fused module. - """ - conv_w = conv.weight - conv_b = conv.bias if conv.bias is not None else torch.zeros_like( - bn.running_mean) - - factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) - conv.weight = nn.Parameter(conv_w * - factor.reshape([conv.out_channels, 1, 1, 1])) - conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) - return conv - - -def fuse_conv_bn(module): - """Recursively fuse conv and bn in a module. - - During inference, the functionary of batch norm layers is turned off - but only the mean and var alone channels are used, which exposes the - chance to fuse it with the preceding conv layers to save computations and - simplify network structures. - - Args: - module (nn.Module): Module to be fused. - - Returns: - nn.Module: Fused module. - """ - last_conv = None - last_conv_name = None - - for name, child in module.named_children(): - if isinstance(child, - (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): - if last_conv is None: # only fuse BN that is after Conv - continue - fused_conv = _fuse_conv_bn(last_conv, child) - module._modules[last_conv_name] = fused_conv - # To reduce changes, set BN as Identity instead of deleting it. - module._modules[name] = nn.Identity() - last_conv = None - elif isinstance(child, nn.Conv2d): - last_conv = child - last_conv_name = name - else: - fuse_conv_bn(child) - return module diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/sync_bn.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/sync_bn.py deleted file mode 100644 index 0c52526e9..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/sync_bn.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -import mmcv - - -class _BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): - """A general BatchNorm layer without input dimension check. - - Reproduced from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc - is `_check_input_dim` that is designed for tensor sanity checks. - The check has been bypassed in this class for the convenience of converting - SyncBatchNorm. - """ - - def _check_input_dim(self, input): - return - - -def revert_sync_batchnorm(module): - """Helper function to convert all `SyncBatchNorm` (SyncBN) and - `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to - `BatchNormXd` layers. - - Adapted from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - - Args: - module (nn.Module): The module containing `SyncBatchNorm` layers. - - Returns: - module_output: The converted module with `BatchNormXd` layers. - """ - module_output = module - module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] - if hasattr(mmcv, 'ops'): - module_checklist.append(mmcv.ops.SyncBatchNorm) - if isinstance(module, tuple(module_checklist)): - module_output = _BatchNormXd(module.num_features, module.eps, - module.momentum, module.affine, - module.track_running_stats) - if module.affine: - # no_grad() may not be needed here but - # just to be consistent with `convert_sync_batchnorm()` - with torch.no_grad(): - module_output.weight = module.weight - module_output.bias = module.bias - module_output.running_mean = module.running_mean - module_output.running_var = module.running_var - module_output.num_batches_tracked = module.num_batches_tracked - module_output.training = module.training - # qconfig exists in quantized models - if hasattr(module, 'qconfig'): - module_output.qconfig = module.qconfig - for name, child in module.named_children(): - module_output.add_module(name, revert_sync_batchnorm(child)) - del module - return module_output diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/weight_init.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/weight_init.py deleted file mode 100644 index 0ac08c87f..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/cnn/utils/weight_init.py +++ /dev/null @@ -1,685 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings - -import numpy as np -import torch -import torch.nn as nn -from torch import Tensor - -from mmcv.utils import Registry, build_from_cfg, get_logger, print_log - -INITIALIZERS = Registry('initializer') - - -def update_init_info(module, init_info): - """Update the `_params_init_info` in the module if the value of parameters - are changed. - - Args: - module (obj:`nn.Module`): The module of PyTorch with a user-defined - attribute `_params_init_info` which records the initialization - information. - init_info (str): The string that describes the initialization. - """ - assert hasattr( - module, - '_params_init_info'), f'Can not find `_params_init_info` in {module}' - for name, param in module.named_parameters(): - - assert param in module._params_init_info, ( - f'Find a new :obj:`Parameter` ' - f'named `{name}` during executing the ' - f'`init_weights` of ' - f'`{module.__class__.__name__}`. ' - f'Please do not add or ' - f'replace parameters during executing ' - f'the `init_weights`. ') - - # The parameter has been changed during executing the - # `init_weights` of module - mean_value = param.data.mean() - if module._params_init_info[param]['tmp_mean_value'] != mean_value: - module._params_init_info[param]['init_info'] = init_info - module._params_init_info[param]['tmp_mean_value'] = mean_value - - -def constant_init(module, val, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.constant_(module.weight, val) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def xavier_init(module, gain=1, bias=0, distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.xavier_uniform_(module.weight, gain=gain) - else: - nn.init.xavier_normal_(module.weight, gain=gain) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def normal_init(module, mean=0, std=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.normal_(module.weight, mean, std) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def trunc_normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - trunc_normal_(module.weight, mean, std, a, b) # type: ignore - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) # type: ignore - - -def uniform_init(module, a=0, b=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.uniform_(module.weight, a, b) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def kaiming_init(module, - a=0, - mode='fan_out', - nonlinearity='relu', - bias=0, - distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.kaiming_uniform_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - else: - nn.init.kaiming_normal_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def caffe2_xavier_init(module, bias=0): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - kaiming_init( - module, - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - bias=bias, - distribution='uniform') - - -def bias_init_with_prob(prior_prob): - """initialize conv/fc bias value according to a given probability value.""" - bias_init = float(-np.log((1 - prior_prob) / prior_prob)) - return bias_init - - -def _get_bases_name(m): - return [b.__name__ for b in m.__class__.__bases__] - - -class BaseInit(object): - - def __init__(self, *, bias=0, bias_prob=None, layer=None): - self.wholemodule = False - if not isinstance(bias, (int, float)): - raise TypeError(f'bias must be a number, but got a {type(bias)}') - - if bias_prob is not None: - if not isinstance(bias_prob, float): - raise TypeError(f'bias_prob type must be float, \ - but got {type(bias_prob)}') - - if layer is not None: - if not isinstance(layer, (str, list)): - raise TypeError(f'layer must be a str or a list of str, \ - but got a {type(layer)}') - else: - layer = [] - - if bias_prob is not None: - self.bias = bias_init_with_prob(bias_prob) - else: - self.bias = bias - self.layer = [layer] if isinstance(layer, str) else layer - - def _get_init_info(self): - info = f'{self.__class__.__name__}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Constant') -class ConstantInit(BaseInit): - """Initialize module parameters with constant values. - - Args: - val (int | float): the value to fill the weights in the module with - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, val, **kwargs): - super().__init__(**kwargs) - self.val = val - - def __call__(self, module): - - def init(m): - if self.wholemodule: - constant_init(m, self.val, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - constant_init(m, self.val, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Xavier') -class XavierInit(BaseInit): - r"""Initialize module parameters with values according to the method - described in `Understanding the difficulty of training deep feedforward - neural networks - Glorot, X. & Bengio, Y. (2010). - `_ - - Args: - gain (int | float): an optional scaling factor. Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` - or ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, gain=1, distribution='normal', **kwargs): - super().__init__(**kwargs) - self.gain = gain - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - xavier_init(m, self.gain, self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - xavier_init(m, self.gain, self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: gain={self.gain}, ' \ - f'distribution={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Normal') -class NormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. - - Args: - mean (int | float):the mean of the normal distribution. Defaults to 0. - std (int | float): the standard deviation of the normal distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, mean=0, std=1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.std = std - - def __call__(self, module): - - def init(m): - if self.wholemodule: - normal_init(m, self.mean, self.std, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - normal_init(m, self.mean, self.std, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: mean={self.mean},' \ - f' std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='TruncNormal') -class TruncNormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values - outside :math:`[a, b]`. - - Args: - mean (float): the mean of the normal distribution. Defaults to 0. - std (float): the standard deviation of the normal distribution. - Defaults to 1. - a (float): The minimum cutoff value. - b ( float): The maximum cutoff value. - bias (float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - **kwargs) -> None: - super().__init__(**kwargs) - self.mean = mean - self.std = std - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ - f' mean={self.mean}, std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Uniform') -class UniformInit(BaseInit): - r"""Initialize module parameters with values drawn from the uniform - distribution :math:`\mathcal{U}(a, b)`. - - Args: - a (int | float): the lower bound of the uniform distribution. - Defaults to 0. - b (int | float): the upper bound of the uniform distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, a=0, b=1, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def __call__(self, module): - - def init(m): - if self.wholemodule: - uniform_init(m, self.a, self.b, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - uniform_init(m, self.a, self.b, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a},' \ - f' b={self.b}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Kaiming') -class KaimingInit(BaseInit): - r"""Initialize module parameters with the values according to the method - described in `Delving deep into rectifiers: Surpassing human-level - performance on ImageNet classification - He, K. et al. (2015). - `_ - - Args: - a (int | float): the negative slope of the rectifier used after this - layer (only used with ``'leaky_relu'``). Defaults to 0. - mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing - ``'fan_in'`` preserves the magnitude of the variance of the weights - in the forward pass. Choosing ``'fan_out'`` preserves the - magnitudes in the backwards pass. Defaults to ``'fan_out'``. - nonlinearity (str): the non-linear function (`nn.functional` name), - recommended to use only with ``'relu'`` or ``'leaky_relu'`` . - Defaults to 'relu'. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` or - ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - a=0, - mode='fan_out', - nonlinearity='relu', - distribution='normal', - **kwargs): - super().__init__(**kwargs) - self.a = a - self.mode = mode - self.nonlinearity = nonlinearity - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ - f'nonlinearity={self.nonlinearity}, ' \ - f'distribution ={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Caffe2Xavier') -class Caffe2XavierInit(KaimingInit): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - def __init__(self, **kwargs): - super().__init__( - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='uniform', - **kwargs) - - def __call__(self, module): - super().__call__(module) - - -@INITIALIZERS.register_module(name='Pretrained') -class PretrainedInit(object): - """Initialize module by loading a pretrained model. - - Args: - checkpoint (str): the checkpoint file of the pretrained model should - be load. - prefix (str, optional): the prefix of a sub-module in the pretrained - model. it is for loading a part of the pretrained model to - initialize. For example, if we would like to only load the - backbone of a detector model, we can set ``prefix='backbone.'``. - Defaults to None. - map_location (str): map tensors into proper locations. - """ - - def __init__(self, checkpoint, prefix=None, map_location=None): - self.checkpoint = checkpoint - self.prefix = prefix - self.map_location = map_location - - def __call__(self, module): - from mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, - load_state_dict) - logger = get_logger('mmcv') - if self.prefix is None: - print_log(f'load model from: {self.checkpoint}', logger=logger) - load_checkpoint( - module, - self.checkpoint, - map_location=self.map_location, - strict=False, - logger=logger) - else: - print_log( - f'load {self.prefix} in model from: {self.checkpoint}', - logger=logger) - state_dict = _load_checkpoint_with_prefix( - self.prefix, self.checkpoint, map_location=self.map_location) - load_state_dict(module, state_dict, strict=False, logger=logger) - - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: load from {self.checkpoint}' - return info - - -def _initialize(module, cfg, wholemodule=False): - func = build_from_cfg(cfg, INITIALIZERS) - # wholemodule flag is for override mode, there is no layer key in override - # and initializer will give init values for the whole module with the name - # in override. - func.wholemodule = wholemodule - func(module) - - -def _initialize_override(module, override, cfg): - if not isinstance(override, (dict, list)): - raise TypeError(f'override must be a dict or a list of dict, \ - but got {type(override)}') - - override = [override] if isinstance(override, dict) else override - - for override_ in override: - - cp_override = copy.deepcopy(override_) - name = cp_override.pop('name', None) - if name is None: - raise ValueError('`override` must contain the key "name",' - f'but got {cp_override}') - # if override only has name key, it means use args in init_cfg - if not cp_override: - cp_override.update(cfg) - # if override has name key and other args except type key, it will - # raise error - elif 'type' not in cp_override.keys(): - raise ValueError( - f'`override` need "type" key, but got {cp_override}') - - if hasattr(module, name): - _initialize(getattr(module, name), cp_override, wholemodule=True) - else: - raise RuntimeError(f'module did not have attribute {name}, ' - f'but init_cfg is {cp_override}.') - - -def initialize(module, init_cfg): - r"""Initialize a module. - - Args: - module (``torch.nn.Module``): the module will be initialized. - init_cfg (dict | list[dict]): initialization configuration dict to - define initializer. OpenMMLab has implemented 6 initializers - including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, - ``Kaiming``, and ``Pretrained``. - - Example: - >>> module = nn.Linear(2, 3, bias=True) - >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) - >>> initialize(module, init_cfg) - - >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) - >>> # define key ``'layer'`` for initializing layer with different - >>> # configuration - >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), - dict(type='Constant', layer='Linear', val=2)] - >>> initialize(module, init_cfg) - - >>> # define key``'override'`` to initialize some specific part in - >>> # module - >>> class FooNet(nn.Module): - >>> def __init__(self): - >>> super().__init__() - >>> self.feat = nn.Conv2d(3, 16, 3) - >>> self.reg = nn.Conv2d(16, 10, 3) - >>> self.cls = nn.Conv2d(16, 5, 3) - >>> model = FooNet() - >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', - >>> override=dict(type='Constant', name='reg', val=3, bias=4)) - >>> initialize(model, init_cfg) - - >>> model = ResNet(depth=50) - >>> # Initialize weights with the pretrained model. - >>> init_cfg = dict(type='Pretrained', - checkpoint='torchvision://resnet50') - >>> initialize(model, init_cfg) - - >>> # Initialize weights of a sub-module with the specific part of - >>> # a pretrained model by using "prefix". - >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ - >>> 'retinanet_r50_fpn_1x_coco/'\ - >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' - >>> init_cfg = dict(type='Pretrained', - checkpoint=url, prefix='backbone.') - """ - if not isinstance(init_cfg, (dict, list)): - raise TypeError(f'init_cfg must be a dict or a list of dict, \ - but got {type(init_cfg)}') - - if isinstance(init_cfg, dict): - init_cfg = [init_cfg] - - for cfg in init_cfg: - # should deeply copy the original config because cfg may be used by - # other modules, e.g., one init_cfg shared by multiple bottleneck - # blocks, the expected cfg will be changed after pop and will change - # the initialization behavior of other modules - cp_cfg = copy.deepcopy(cfg) - override = cp_cfg.pop('override', None) - _initialize(module, cp_cfg) - - if override is not None: - cp_cfg.pop('layer', None) - _initialize_override(module, override, cp_cfg) - else: - # All attributes in module have same initialization. - pass - - -def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, - b: float) -> Tensor: - # Method based on - # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf - # Modified from - # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - def norm_cdf(x): - # Computes standard normal cumulative distribution function - return (1. + math.erf(x / math.sqrt(2.))) / 2. - - if (mean < a - 2 * std) or (mean > b + 2 * std): - warnings.warn( - 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' - 'The distribution of values may be incorrect.', - stacklevel=2) - - with torch.no_grad(): - # Values are generated by using a truncated uniform distribution and - # then using the inverse CDF for the normal distribution. - # Get upper and lower cdf values - lower = norm_cdf((a - mean) / std) - upper = norm_cdf((b - mean) / std) - - # Uniformly fill tensor with values from [lower, upper], then translate - # to [2lower-1, 2upper-1]. - tensor.uniform_(2 * lower - 1, 2 * upper - 1) - - # Use inverse cdf transform for normal distribution to get truncated - # standard normal - tensor.erfinv_() - - # Transform to proper mean, std - tensor.mul_(std * math.sqrt(2.)) - tensor.add_(mean) - - # Clamp to ensure it's in the proper range - tensor.clamp_(min=a, max=b) - return tensor - - -def trunc_normal_(tensor: Tensor, - mean: float = 0., - std: float = 1., - a: float = -2., - b: float = 2.) -> Tensor: - r"""Fills the input Tensor with values drawn from a truncated - normal distribution. The values are effectively drawn from the - normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` - with values outside :math:`[a, b]` redrawn until they are within - the bounds. The method used for generating the random values works - best when :math:`a \leq \text{mean} \leq b`. - - Modified from - https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - - Args: - tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. - mean (float): the mean of the normal distribution. - std (float): the standard deviation of the normal distribution. - a (float): the minimum cutoff value. - b (float): the maximum cutoff value. - """ - return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/__init__.py deleted file mode 100644 index 6ac55e63b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from . import ipu, mlu - -__all__ = ['mlu', 'ipu'] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/__init__.py deleted file mode 100755 index d550865ad..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import IPUFp16OptimizerHook - from .model_wrapper import ipu_model_wrapper - from .runner import IPUBaseRunner, IPUEpochBasedRunner, IPUIterBasedRunner - from .utils import cfg2options - __all__ = [ - 'cfg2options', 'ipu_model_wrapper', 'IPUFp16OptimizerHook', - 'IPUDataLoader', 'IPUBaseRunner', 'IPUEpochBasedRunner', - 'IPUIterBasedRunner' - ] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/dataloader.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/dataloader.py deleted file mode 100755 index 1485df2f3..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/dataloader.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence -from functools import partial - -import poptorch -from torch.utils.data.dataloader import default_collate - -from mmcv.parallel import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Put each data field into a tensor/DataContainer with outer dimension - batch size. - - TODO support for - :type:`~mmcv.parallel.DataContainer`. Currently, it will be ignored. - There are 3 cases. - - 1. cpu_only = True, e.g., meta data. - 2. cpu_only = False, stack = True, e.g., images tensors. - 3. cpu_only = False, stack = False, e.g., gt bboxes. - """ - - if not isinstance(batch, Sequence): - raise TypeError( - f'`batch` should be a sequence, but got {type(batch)}.') - - if isinstance(batch[0], DataContainer): - # TODO `DataContainer` will be supported in the future. - raise TypeError('DataContainer is not supported in ipu data loader.') - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - collated_batch = [] - for samples in transposed: - if not isinstance(samples[0], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch.append(collate(samples, samples_per_gpu)) - return collated_batch - elif isinstance(batch[0], Mapping): - collated_batch = {} - for key in batch[0]: - if not isinstance(batch[0][key], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch[key] = collate([d[key] for d in batch]) - return collated_batch - else: - return default_collate(batch) - - -class IPUDataLoader(poptorch.DataLoader): - """Thin wrapper of `torch.utils.data.DataLoader`. - - Compared with the pytorch DataLoder, this DataLoder changes the way of - calculation of batch size and adds the AsynchronousDataAccessor to - load and release data faster in cpu mode. - - If this data loader is used in a distributed execution environment, it will - ensure that each process uses a different subset of the dataset, providing - you first call ``options.randomSeed(N)`` with an integer N which is the - same across all hosts. - - Args: - dataset (torch.utils.data.Dataset): The dataset to get the data from. - options (poptorch.Options): Options that will be used to compile - and run the model. - batch_size (int, optional): This is the batch size in the conventional - sense of being the size that runs through an operation in the model - at any given time. - shuffle (bool, optional): set to ``True`` to have the data reshuffled - at every epoch (default: ``False``). - num_workers (int, optional): how many subprocesses to use for data - loading. ``0`` means that the data will be loaded in the main - process. (default: ``0``) - drop_last (bool, optional): If True and the number of elements in the - dataset is not a multiple of the combined batch size then the - incomplete batch at the end will be dropped. - persistent_workers (bool, optional): Re-use workers between - iterations if True. - auto_distributed_partitioning (bool, optional): If True, partitions the - dataset for distributed execution automatically. Otherwise, it is - assumed that partitioning has been handled manually. - mode (poptorch.DataLoaderMode, optional): If `DataLoaderMode.Async`, - uses an :py:class:`~poptorch.AsynchronousDataAccessor` to access - the dataset. If `DataLoaderMode.Sync`, accesses the dataset - synchronously. - async_options (Dict[str, Any], optional): Options to pass to - :py:class:`~poptorch.AsynchronousDataAccessor`. - rebatched_worker_size (int, optional): When using AsyncRebatched: batch - size of the tensors loaded by the workers. - Default to the combined batch size. - If specified the ``rebatched_worker_size`` must be less than - or equal to the combined batch size. - kwargs (Dict[str, Any], optional): Other options to pass to PyTorch's - ``DataLoader`` constructor. - """ - - def __init__(self, - dataset, - options, - batch_size=1, - shuffle=False, - num_workers=0, - drop_last=True, - persistent_workers=True, - auto_distributed_partitioning=True, - mode='sync', - async_options=None, - rebatched_worker_size=None, - **kwargs): - """Lazy init: - - In many frameworks, the dataloader will be constructed before the - initialization of the ipu options, so the lazy init method is used - here, and the real initialization will not be done until the dataloader - needs to be used and the options are input. - """ - # lazy init: sometimes, we cannot get IPU options when build data - # loader - self.kwargs = { - 'dataset': dataset, - 'batch_size': batch_size, - 'shuffle': shuffle, - 'num_workers': num_workers, - 'drop_last': drop_last, - 'persistent_workers': persistent_workers, - 'auto_distributed_partitioning': auto_distributed_partitioning, - 'mode': mode, - 'collate_fn': partial(collate, samples_per_gpu=batch_size), - 'async_options': async_options, - 'rebatched_worker_size': rebatched_worker_size, - **kwargs - } - self.dataset = dataset - self.initialized = False - if options: - self.init(options=options) - - def init(self, options, **kwargs): - if not self.initialized: - kwargs = {**self.kwargs, **kwargs, 'options': options} - if kwargs['mode'] == 'sync': - kwargs['mode'] = poptorch.DataLoaderMode.Sync - elif kwargs['mode'] == 'async': - kwargs['mode'] = poptorch.DataLoaderMode.AsyncRebatched - if kwargs['async_options'] is None: - kwargs['async_options'] = { - 'load_indefinitely': True, - 'buffer_size': 8 - } - if kwargs['rebatched_worker_size'] is None: - kwargs['rebatched_worker_size'] = 128 - super().__init__(**kwargs) - self.initialized = True - - return self diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py deleted file mode 100755 index a6f3b3cd2..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import numpy as np -import torch - -from mmcv.parallel import DataContainer - -# A customized None type for HierarchicalDataManager -HierarchicalDataNone = object() - - -class HierarchicalDataManager: - """A class manage all the tensors in the hierarchical data. - - At present, the input data structure accepted by IPU is limited, - when the input data structure of mmcv varies. - Here, an intermediate class is needed to get and update tensors - from the original data. - - HierarchicalDataManager will record a hierarchical input/output data in - self._hierarchical_data. For example, we have an input data: - {'img': tensorA, 'label': tensorB, 'img_metas': [tensorC, tensorD]} - To enable IPU to use the input, HierarchicalDataManager will collect - the torch tensors from self._hierarchical_data into a tuple like: - (tensorA, tensorB, tensorC, tensorD). - Meanwhile, the return of IPU is a tuple of tensors, HierarchicalDataManager - also have a function named update_all_tensors to update tensors in - self._hierarchical_data which is the output for upper calls. - - Args: - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - """ - - def __init__(self, logger=None): - self.atomic_types = (int, str, float, np.ndarray, type(None)) - self.warning = warnings.warn if logger is None else logger.warning - # enable or disable input data's shape and value check - self.quick_mode = False - self._hierarchical_data = None - - def quick(self): - self.quick_mode = True - - def compare_atomic_type(self, a, b): - """Compare data, supported datatypes are numpy array and python basic - types.""" - if isinstance(a, np.ndarray): - return np.all(a == b) - else: - return a == b - - def record_hierarchical_data(self, data): - """Record a hierarchical data.""" - if self._hierarchical_data is not None: - if isinstance(data, torch.Tensor): - assert isinstance(self._hierarchical_data, torch.Tensor), \ - 'original hierarchical data is not torch.tensor' - self._hierarchical_data = data - else: - self.update_hierarchical_data(data) - else: - self._hierarchical_data = data - - @property - def hierarchical_data(self): - return self._hierarchical_data - - def update_hierarchical_data(self, - dataA, - dataB=HierarchicalDataNone, - strict=True, - address='data'): - """Update dataB with dataA in-place. - - Args: - dataA (list or dict or tuple): New hierarchical data. - dataB (list or dict or tuple): hierarchical data to update. - if not specified, self.hierarchical_data will be updated then. - strict (bool, optional): If true, an error will be reported - when the following conditions occur: - 1. Non-torch.Tensor data changed. - 2. Torch.Tensor data shape changed. - address (str): Record the address of current data to be updated. - Default: 'data'. - """ - if dataB is HierarchicalDataNone: - dataB = self.hierarchical_data - - # Update with a da ta with the same structure - # but different values(tensors and basic python data types) - if isinstance(dataA, (tuple, list)): - for idx, node in enumerate(dataA): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(idx)}]' - assert isinstance(node, type(dataB[idx])),\ - f'data structure changed: {new_address}' - if isinstance(node, torch.Tensor): - dataB[idx] = node - else: - self.update_hierarchical_data( - node, dataB[idx], strict, address=new_address) - elif isinstance(dataA, dict): - for k, v in dataA.items(): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(k)}]' - assert isinstance(v, type(dataB[k])),\ - f'data structure changed: {new_address}' - if isinstance(v, torch.Tensor): - dataB[k] = v - else: - self.update_hierarchical_data( - v, dataB[k], strict, address=new_address) - elif isinstance(dataA, self.atomic_types): - if not self.quick_mode: - is_equal = self.compare_atomic_type(dataA, dataB) - if not is_equal: - if strict: - raise ValueError( - 'all data except torch.Tensor should be same, ' - f'but data({address}) is changed.') - else: - self.warning( - f'find a non-torch.Tensor data({type(dataA)}) ' - f'changed, and the address is {address}') - elif isinstance(dataA, DataContainer): - if not self.quick_mode: - assert isinstance(dataB, DataContainer) - new_address = address + '.data' - self.update_hierarchical_data( - dataA.data, dataB.data, False, address=new_address) - else: - raise NotImplementedError( - f'not supported datatype:{type(dataA)}, address is {address}') - - def collect_all_tensors(self, hierarchical_data=None): - """Collect torch.Tensor data from self.hierarchical_data to a list and - return.""" - # get a list of tensor from self._hierarchical_data - if hierarchical_data is None: - hierarchical_data = self._hierarchical_data - tensors = [] - if isinstance(hierarchical_data, torch.Tensor): - tensors = [hierarchical_data] - else: - self._collect_tensors(hierarchical_data, tensors) - return tensors - - def _collect_tensors(self, data, tensors): - if isinstance(data, (tuple, list)): - for node in data: - if isinstance(node, torch.Tensor): - tensors.append(node) - else: - self._collect_tensors(node, tensors) - elif isinstance(data, dict): - for v in data.values(): - if isinstance(v, torch.Tensor): - tensors.append(v) - else: - self._collect_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._collect_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def update_all_tensors(self, tensors): - """Put tensors from tuple back to self.hierarchical_data.""" - if isinstance(self._hierarchical_data, torch.Tensor): - print(tensors, len(tensors)) - assert len(tensors) == 1 - assert isinstance(tensors[0], torch.Tensor) - self._hierarchical_data = tensors[0] - else: - # convert to list if tensors is tuple - tensors = list(tensors) - self._set_tensors(self._hierarchical_data, tensors) - return self.hierarchical_data - - def _set_tensors(self, data, tensors): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = tensors.pop(0) - else: - self._set_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._set_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def clean_all_tensors(self): - """Delete tensors from self.hierarchical_data.""" - self._clean_tensors(self._hierarchical_data) - - def _clean_tensors(self, data): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = None - else: - self._clean_tensors(v) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._clean_tensors(data.data) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/hook_wrapper.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/hook_wrapper.py deleted file mode 100755 index 141afb86d..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/hook_wrapper.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import HOOKS, LrUpdaterHook, OptimizerHook -from mmcv.utils import TORCH_VERSION, digit_version - - -def wrap_lr_updater_hook(lr_hook_class): - """A wrapper function to wrap any subclass of LrUpdaterHook. - - IPU needs extra operations to upload optimizer settings. This wrapper will - override function(_set_lr) of a subclass of LrUpdaterHook. - """ - assert issubclass(lr_hook_class, LrUpdaterHook) - - class ipu_lr_hook_class(lr_hook_class): - - def _set_lr(self, runner, *args, **kwargs): - super()._set_lr(runner, *args, **kwargs) - # convert torch optimizer to poptorch optimizer - runner.model.setOptimizer(runner.optimizer) - - return ipu_lr_hook_class - - -def wrap_optimizer_hook(optimizer_hook_class): - """A wrapper function to wrap OptimizerHook. - - This is an non-intrusive implementation of wrapping optimizer hook (or you - need to change every config file to use IPU optimizer hook) IPU's clip-norm - implementation is different from pytorch, so there should be an error - raised when using clip-norm. - """ - - class ipu_optimizer_hook_class(OptimizerHook): - - def __init__(self, **kwargs): - super().__init__(**kwargs) - if self.grad_clip is not None: - raise NotImplementedError('IPU does not support gradient clip') - - return ipu_optimizer_hook_class - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class IPUFp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - assert grad_clip is None,\ - 'IPU mode does not support `grad_clip` currently' - assert coalesce,\ - 'implemented all reduce in distributed training currently' - assert bucket_size_mb == -1,\ - '`bucket_size_mb` should not be set in IPU mode' - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - raise NotImplementedError( - 'IPU mode does not support dynamic loss scale currently') - elif isinstance(loss_scale, float): - self.loss_scale = loss_scale - elif isinstance(loss_scale, dict): - raise NotImplementedError( - 'IPU mode supports single scale currently') - else: - raise ValueError( - f'loss_scale should be float, but got {loss_scale} ') - - def after_train_iter(self, runner): - pass - -else: - raise RuntimeError('The IPU mode only supports torch 1.6 and above') diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/model_wrapper.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/model_wrapper.py deleted file mode 100755 index c345537e2..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/model_wrapper.py +++ /dev/null @@ -1,721 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect -from collections import OrderedDict -from typing import Optional, Union - -import poptorch -import torch -import torch.nn as nn -from poptorch import PoplarExecutor, __version__, identity_loss -from poptorch._args_parser import ArgsParser - -from mmcv.runner import auto_fp16 -from .hierarchical_data_manager import HierarchicalDataManager -from .utils import compare_ndarray, model_sharding, recomputation_checkpoint - - -class DictArgsParser(ArgsParser): - """A helper class for handling model input. - - Args: - inputs (list): Inputs of model. - """ - - def __init__(self, inputs): - # Combine args and kwargs: - self._has_variadic_arguments = True - self._varnames = list(inputs.keys()) - self._defaults = [inspect.Parameter.empty for _ in self._varnames] - self._warned_not_contiguous_input = False - - -class WrappedNet(nn.Module): - """A net wrapper for model conversion. - - This wrapper will make some changes and add some extra functions to - training/inference model. - - Args: - model (:obj:`nn.Module`): The model to run. - inputs_manager (:obj:`HierarchicalDataManager`): A parser - converting inputs from tuple to dictionary. - outputs_manager (:obj:`HierarchicalDataManager`): A parser - converting outputs from dictionary to tuple. - inter_outputs_in_cpu (dict): Specify the features to be - recorded. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - model, - inputs_manager, - outputs_manager, - inter_outputs_in_cpu, - modules_to_record=None): - super().__init__() - self.model = model - self.inputs_manager = inputs_manager - self.outputs_manager = outputs_manager - self.training = model.training - # Register a hook function to capture the intermediate features - # generated by the network to align the outputs between ipu and cpu - # Used to confirm whether the implementation of CPU is consistent - # with the implementation of IPU - self.inter_outputs_in_cpu = inter_outputs_in_cpu - if modules_to_record is None: - modules_to_record = [] - - for idx, (name, module) in enumerate(model.named_modules()): - if name in modules_to_record or idx in modules_to_record: - features_hook = self.get_input_output_hook( - name, idx, self.inter_outputs_in_cpu) - module.register_forward_hook(hook=features_hook) - - def get_input_output_hook(self, name, idx, save_dict): - - def input_output_hook(module, fea_in, fea_out): - if isinstance(fea_in, tuple): - fea_in = list(fea_in) - if isinstance(fea_out, tuple): - fea_out = list(fea_out) - save_dict[name] = { - 'fea_in': fea_in, - 'fea_out': fea_out, - 'idx': idx - } - return None - - return input_output_hook - - def forward(self, inputs_tuple): - """This function is used to be compiled to ipu, the inputs and outputs - need to be tuples, so here we need to restore the input back to a - dictionary and convert the output to a tuple.""" - self.inputs_manager.update_all_tensors(inputs_tuple) - kwargs = {**(self.inputs_manager.hierarchical_data)} - if self.training: - outputs = self.forward_train(kwargs) - # tell poptorch which loss will be used finally - identity_loss(outputs['loss'], reduction='none') - else: - outputs = self.forward_eval(kwargs) - - if isinstance(outputs, torch.Tensor): - # currently not support single tensor output, - # need to wrap it with a dictionary, - # use a keyword to identify this case - outputs = {'output of WrappedNet: single tensor': outputs} - - # if there are some features need to be record, add extra outputs - for name in self.inter_outputs_in_cpu: - outputs[name] = self.inter_outputs_in_cpu[name] - - # record all the places of return tensors in the converting stage - # while in the real run stage, all the tensor are changed in-place - # that means the output can be obtained directly outside this function - self.outputs_manager.record_hierarchical_data(outputs) - plain_outputs = self.outputs_manager.collect_all_tensors() - return plain_outputs - - def forward_train(self, kwargs): - optimizer = kwargs.pop('optimizer') - outputs = self.train_step(kwargs, optimizer) - return outputs - - def train_step(self, data, optimizer=None, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating are also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer`, optional): The - optimizer of runner is passed to ``train_step()``. This - argument is unused and reserved. - - Returns: - dict: Dict of outputs. The following fields are contained. - - loss (torch.Tensor): A tensor for back propagation, which \ - can be a weighted sum of multiple losses. - - log_vars (dict): Dict contains all the variables to be sent \ - to the logger. - - num_samples (int): Indicates the batch size (when the model \ - is DDP, it means the batch size on each GPU), which is \ - used for averaging the logs. - """ - losses = self.model(**data) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, log_vars=log_vars, num_samples=len(data['img'].data)) - - return outputs - - def _parse_losses(self, losses): - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(loss.mean() for loss in loss_value) - elif isinstance(loss_value, dict): - for name, value in loss_value.items(): - log_vars[name] = value - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(value for key, value in log_vars.items() if 'loss' in key) - log_vars['loss'] = loss - - return loss, log_vars - - def forward_eval(self, kwargs): - img = kwargs.pop('img') - img_metas = kwargs.pop('img_metas', None) - return_loss = kwargs.pop('return_loss') - assert not return_loss - # TODO Temporarily hard-code to close post_process, - # otherwise, in the third trace(_check_trace), - # post_process will convert output tensor to numpy array automatically, - # resulting in _check_trace failure - outputs = self.model( - img, - img_metas=img_metas, - return_loss=return_loss, - post_process=False) - return outputs - - -class MMPoplarExecutor(PoplarExecutor): - """An executor for inputs/outputs parsing, model compilation, data - alignment and IPU upload/download. - - Args: - model (:obj:`nn.Module`): The model to be compiled. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - training (bool): Model in training mode or eval mode. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - args (argument list): Arguments passed to the `__init__` - method of PoplarExecutor. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of PoplarExecutor. - """ - - def __init__(self, - model, - logger=None, - training=True, - modules_to_record=None, - *args, - **kwargs): - # self.model == self._user_model: input pytorch model - # self._model: wrapped model which is used to compile - # and update weights, these two models use same weights - # wrapped model only accept and output tuple, so - # HierarchicalDataManager will convert dictionary - # to tuple and convert them back - self.inputs_manager = HierarchicalDataManager(logger=logger) - self.outputs_manager = HierarchicalDataManager(logger=logger) - self.logger = logger - # the features calculated by CPU - self.inter_outputs_in_cpu = {} - # the features calculated by IPU - self.inter_outputs_in_ipu = {} - if modules_to_record is None: - # It is possible that the IPU implementation of some operators - # is inconsistent with the expected (CPU), here you can use - # this method to confirm whether there is a problem - self.compare_with_cpu = False - else: - self.compare_with_cpu = True - # move model.fp16_enabled to self.fp16_enabled, - # modify the position where the input is automatically casted to half - if getattr(model, 'fp16_enabled', False): - model.fp16_enabled = False - self.fp16_enabled = True - # make torch.jit.trace convert self._model - model = WrappedNet( - model, - self.inputs_manager, - self.outputs_manager, - self.inter_outputs_in_cpu, - modules_to_record=modules_to_record) - super().__init__(model, training=training, *args, **kwargs) - # overwrite self._args_parser in train_step or val_step - self._args_parser = None - if training: - assert self.training - else: - assert not self.training - - @property - def training(self): - # If trying to get the attribute(training) of self, - # since the class has no training attribute, - # it will automatically look for the training attribute of self.model. - # However, the real attribute we want to check is self._training, - # self.model.training and self._training are often inconsistent. - # It is not clear whether it is a Poptorch bug or a special design, - # temporarily use this function to fix the problem - return self._training # comes from self.model._training - - @auto_fp16(supported_types=(PoplarExecutor, )) - def run_model(self, data_dict): - # this function is used to parse input_dict - # and convert to output_dict - if self.isCompiled(): - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - else: - # get tensors out of data and put them in a tuple - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - # turn logger in data manager off after compilation - self.inputs_manager.quick() - self.outputs_manager.quick() - - # parser args in the first iter - if self._args_parser is None: - self._args_parser = DictArgsParser({'args': inputs_tuple}) - - # run or convert model - # the plain_outputs will be used in converting stage - plain_outputs = self(inputs_tuple) - - self.inputs_manager.clean_all_tensors() - - # put list of tensors back to the output dict - # according to the same order - self.outputs_manager.update_all_tensors(plain_outputs) - # get the real output dictionary from self.outputs_manager - output_dict = self.outputs_manager.hierarchical_data - - # split output_dict into inter_outputs_in_ipu - # and output of the torch model - torch_model_output = {} - for name in output_dict: - if name in self.inter_outputs_in_cpu: - self.inter_outputs_in_ipu[name] = output_dict[name] - else: - torch_model_output[name] = output_dict[name] - - if 'output of WrappedNet: single tensor' in output_dict: - assert len(torch_model_output) == 1 - assert isinstance( - torch_model_output['output of WrappedNet: single tensor'], - torch.Tensor) - torch_model_output = \ - torch_model_output['output of WrappedNet: single tensor'] - - return torch_model_output - - def train_step(self, data, optimizer=None, **kwargs): - # arguments from mmcls/models/classifiers/base.py: - # BaseClassifier.train_step - assert self.training - assert len(kwargs) == 0 # TODO, support later if necessary - - # TODO support datacontainer as input - # currently, auto_fp16 and HierarchicalDataManager take too much - # time on traversing datacontainer - data['img_metas'] = None - num_samples = len(data['img'].data) - - # TODO we will ignore optimizer because it will not be used in model, - # support later if necessary - data['optimizer'] = None - output_dict = self.run_model(data) - - # outputs contained loss, log_vars, num_samples, - # only loss(torch.tensor) has been updated - # remove all unchanged vars, left torch.tensor - neat_output_dict = {'loss': output_dict['loss']} - - # re-parse outputs, get back log_vars and num_samples - loss, log_vars = self.model._parse_losses(neat_output_dict) - final_output_dict = dict( - loss=loss, log_vars=log_vars, num_samples=num_samples) - return final_output_dict - - def eval_call(self, img, img_metas=None, return_loss=True, **kwargs): - # arguments from mmdet/models/detectors/base.py:BaseDetector.forward - # tmp usssage for eval mode - assert not self.training - assert len(kwargs) == 0 # TODO, support later if necessary - assert not return_loss - data = {'img': img, 'img_metas': img_metas, 'return_loss': return_loss} - - output_dict = self.run_model(data) - - return output_dict - - def detachFromDevice(self): - if self.isCompiled() and self._is_attached: - super().detachFromDevice() - - def attachToDevice(self): - if self.isCompiled() and not self._is_attached: - super().attachToDevice() - - -class TrainEvalModel: - """A class maintaining training MMPoplarExecutor and inference - MMPoplarExecutor. - - Args: - train_model (:obj:`nn.Module`): The training model to be compiled. - ``train_model`` can be None if only executing validation. - eval_model (:obj:`nn.Module`): The inference model to be compiled. - options (mmcv.Config, dict): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - train_model, - eval_model, - options, - optimizer, - modules_to_record=None, - logger=None): - if train_model is None: - self._train_executor = None - self.training = False - else: - self._train_executor = get_training_model( - train_model, - options=options['training'], - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - self.training = True - self._eval_executor = get_inference_model( - eval_model, options=options['inference'], logger=logger) - - @property - def executor(self): - if self.training: - return self._train_executor - else: - return self._eval_executor - - def train(self, mode: bool = True): - """Sets the module in training mode. - - This has any effect only on certain modules. See documentations of - particular modules for details of their behaviors in - training/evaluation mode, if they are affected, - e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - Args: - mode (bool): whether to set training mode (``True``) or evaluation - mode (``False``). Default: ``True``. - - Returns: - Module: self - """ - if not isinstance(mode, bool): - raise ValueError('training mode is expected to be boolean, ' - f'but got {type(mode)}') - if self._train_executor is None and mode: - raise RuntimeError( - 'The train_executor is not initialized.' - 'If you want to initialize train_executor,' - 'you need to input optimizer when converting pytorch model') - - if mode == self.training: - self.model.train(mode) - return self - else: - if self.isCompiled(): - # copy weights from IPU to cpu before off-load current session - self.copyWeightsToHost() - # detach the current session before change the mode, - # if is training mode and weights are updated, - # poptorch will copy weights from IPU to host - self.detachFromDevice() - - self.training = mode # session will changed with mode changing - self.model.train(mode) - - # after changing mode, attach the current new session, - # and this function will copy weights of model to device - self.attachToDevice() - return self - - def eval(self): - """Sets the module in evaluation mode. - - This has any effect only on certain modules. - See documentations of particular modules - for details of their behaviors in training/evaluation mode, - if they are affected, e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - This is equivalent with :meth:`self.train(False) - `. - - See :ref:`locally-disable-grad-doc` for a comparison between - `.eval()` and several similar mechanisms that may be confused with it. - - Returns: - Module: self - """ - return self.train(False) - - def compare_data_between_ipu_and_cpu(self, inter_outputs_in_cpu, - inter_outputs_in_ipu): - for key, val in inter_outputs_in_cpu.items(): - is_tensor = isinstance(val['fea_in'], torch.Tensor) - fea_in_cpu = val['fea_in'] - fea_in_cpu_list = [fea_in_cpu] if is_tensor else fea_in_cpu - fea_in_ipu = inter_outputs_in_ipu[key]['fea_in'] - fea_in_ipu_list = [fea_in_ipu] if is_tensor else fea_in_ipu - - is_tensor = isinstance(val['fea_out'], torch.Tensor) - fea_out_cpu = val['fea_out'] - fea_out_cpu_list = [fea_out_cpu] if is_tensor else fea_out_cpu - fea_out_ipu = inter_outputs_in_ipu[key]['fea_out'] - fea_out_ipu_list = [fea_out_ipu] if is_tensor else fea_out_ipu - - print('comparing layer:', key) - for idx, (featA, featB) in \ - enumerate(zip(fea_in_cpu_list, fea_in_ipu_list)): - print('fea_in, tensor ', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - for idx, (featA, featB) in \ - enumerate(zip(fea_out_cpu_list, fea_out_ipu_list)): - print('fea_out, tensor', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def train_step(self, data, optimizer=None, **kwargs): - assert self.training, 'not supported train_step on eval mode' - inter_outputs_in_cpu = {} - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu): - self.copyWeightsToHost() - # run in CPU mode - self._train_executor.model.train_step(data, optimizer, **kwargs) - inter_outputs_in_cpu = { - **(self._train_executor.inter_outputs_in_cpu) - } - # run in IPU mode - result = self._train_executor.train_step(data, optimizer, **kwargs) - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu - and len(inter_outputs_in_cpu) > 0): - self.compare_data_between_ipu_and_cpu( - inter_outputs_in_cpu, - self._train_executor.inter_outputs_in_ipu) - return result - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def __call__(self, *args, **kwargs): - if self.training: - raise NotImplementedError('use train_step rather than __call__') - else: - return self._eval_executor.eval_call(*args, **kwargs) - - def __getattr__(self, attr): - return getattr(self.executor, attr) - - -def get_training_model(model: nn.Module, - options: Optional[poptorch.Options] = None, - optimizer: Optional[torch.optim.Optimizer] = None, - logger=None, - modules_to_record=None) -> poptorch.PoplarExecutor: - """Create a PopTorch training model from a PyTorch model, running on IPU - hardware in training mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned training model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.train()`` on the original model, which - changes the ``training`` bool of the model instance, will not alter the - model returned by this function. You may need to call ``model.train()`` - on your model before you call this function for correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): The optimizers - to apply during training. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place - of ``model``. - """ - # Create a copy of the original model in case it needs to be wrapped - maybe_wrapped_model = copy.copy(model) - - return MMPoplarExecutor( - model=maybe_wrapped_model, - logger=logger, - options=options, - training=True, - optimizer=optimizer, - user_model=model, - modules_to_record=modules_to_record, - poptorch_version=__version__) - - -def get_inference_model(model: Union[nn.Module, poptorch.PoplarExecutor], - options: Optional[poptorch.Options] = None, - logger=None) -> poptorch.PoplarExecutor: - """Create a PopTorch inference model from a PyTorch model, running on IPU - hardware in inference mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned inference model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.eval()`` on the original model will not alter - the model returned by this function. You may need to call - ``model.eval()`` on your model before you call this function for - correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place of - ``model``. - """ - - return MMPoplarExecutor( - model=copy.copy(model), - logger=logger, - options=options, - training=False, - poptorch_version=__version__) - - -def ipu_model_wrapper(model, - options, - optimizer=None, - logger=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None): - """Convert torch model to IPU model. - - Args: - model (nn.Module): The target model to be converted. - options (dict[str, poptorch.Options]): IPU options, generated - by :func:`cfg2options`. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during training. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (dict): A dictionary contains train_split_edges and - train_ckpt_nodes, See details in :func:`model_sharding` and - :func:`recomputation_checkpoint` functions. - fp16_cfg (dict): Config for IPU fp16 training. Currently supports - configs: `loss_scale`, `velocity_accum_type` and `accum_type`. - See details in - https://docs.graphcore.ai/projects/poptorch-user-guide/en/latest/index.html - - Returns: - TrainEvalModel: IPU wrapped model. - """ - if ipu_model_cfg is None: - ipu_model_cfg = {} - training = model.training if optimizer is not None else False - # set mixed-precision - if fp16_cfg is not None: - from mmcv.runner import wrap_fp16_model - loss_scale = fp16_cfg['loss_scale'] - wrap_fp16_model(model) - model.half() - # TODO tmp ussage to set loss scaling for torch original optimizer - if optimizer is not None: - optimizer.loss_scaling = loss_scale - if fp16_cfg.get('velocity_accum_type', False): - if fp16_cfg['velocity_accum_type'] == 'half': - optimizer.velocity_accum_type = torch.half - else: - optimizer.velocity_accum_type = torch.float32 - if fp16_cfg.get('accum_type', False): - if fp16_cfg['accum_type'] == 'half': - optimizer.accum_type = torch.half - else: - optimizer.accum_type = torch.float32 - # TODO support feature alignment for fp16 - if modules_to_record is not None: - raise NotImplementedError( - 'Feature alignment for fp16 is not implemented') - - # set model partition - if optimizer is None: - train_model = None - else: - # split model into multi-IPUs if specified - train_model = model_sharding( - copy.copy(model).train(), - ipu_model_cfg.get('train_split_edges', [])) - - recomputation_checkpoint(train_model, - ipu_model_cfg.get('train_ckpt_nodes', [])) - - # TODO support feature alignment for gradient accumulation mode - gradient_accumulation = \ - getattr(options['training'].Training, 'gradient_accumulation', 1) - if gradient_accumulation > 1: - assert modules_to_record is None, \ - 'Feature alignment for grad-accumulation mode not implemented' - - # TODO support feature alignment for multi-replica mode - replication_factor = \ - getattr(options['training'], 'replication_factor', 1) - if replication_factor > 1: - assert modules_to_record is None, \ - 'Feature alignment for multi-replica mode not implemented' - - # TODO supports different model partitions between train and eval mode - assert len(ipu_model_cfg.get('eval_split_edges', [])) == 0,\ - 'Currently, BeginBlock can only be used once on the same model' - eval_model = copy.copy(model).eval() - - # wrap model for compilation - model = TrainEvalModel( - train_model, - eval_model, - options=options, - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - model.train(training) - return model diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/runner.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/runner.py deleted file mode 100755 index e2d492267..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/runner.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.runner import (HOOKS, RUNNERS, BaseRunner, EpochBasedRunner, - IterBasedRunner) -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import (IPUFp16OptimizerHook, wrap_lr_updater_hook, - wrap_optimizer_hook) - from .model_wrapper import ipu_model_wrapper - from .utils import build_from_cfg_with_wrapper, cfg2options - - -class IPUBaseRunner(BaseRunner): - """A base runner for IPU. - - This runner has some extra processes for IPU which are shown below: - - 1. Parse options for IPU - 2. wrap pytorch model for IPU - 3. Raise errors while encountering illegal usage - 4. Input IPU options and initialize dataloader if finding an instance - of IPUDataLoader - - Args: - model (:obj:`nn.Module`): The model to run. - options_cfg (mmcv.Config, dict): Options that will be used to compile - and run the model. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (mmcv.Config, dict): Config of model partition and - recomputing checkpoint - fp16_cfg (mmcv.Config): Config for fp16 training. - batch_processor (callable): A callable method that process a data - batch. Should be None for IPU runner - kwargs (Dict[str, Any], optional): Keyword arguments will be passed to - ``base_runner.BaseRunner``. - """ - - def __init__(self, - model, - options_cfg=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None, - batch_processor=None, - **kwargs): - assert hasattr(model, 'train_step') and batch_processor is None,\ - 'only support model with train_step' - - if options_cfg is None: - options_cfg = {} - # call BaseRunner.__init__() here - super().__init__(model, **kwargs) - - # process options of ipu - if IS_IPU_AVAILABLE: - self.options = cfg2options(options_cfg) - self.model = ipu_model_wrapper( - self.model, - self.options, - self.optimizer, - self.logger, - modules_to_record=modules_to_record, - ipu_model_cfg=ipu_model_cfg, - fp16_cfg=fp16_cfg) - else: - raise NotImplementedError('cpu mode on IPURunner is not supported') - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - assert isinstance(lr_config, dict) - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, - # e.g., 'cyclic', then its first letter will be capitalized, - # e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, the string will not be changed - # if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = build_from_cfg_with_wrapper(lr_config, HOOKS, - wrap_lr_updater_hook) - self.register_hook(hook, priority='VERY_HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - assert isinstance(optimizer_config, (dict, IPUFp16OptimizerHook)) - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = build_from_cfg_with_wrapper(optimizer_config, HOOKS, - wrap_optimizer_hook) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def run(self, data_loaders, workflow, *args, **kwargs): - for i, flow in enumerate(workflow): - mode, _ = flow - # initialize IPU dataloader if not initialized - assert isinstance(data_loaders[i], IPUDataLoader),\ - 'IPU runner can only work with `IPUDataLoader`' - data_loaders[i].init(options=self.get_options(mode)) - - super().run(data_loaders, workflow, *args, **kwargs) - - def get_options(self, mode): - if mode == 'train': - return self.options['training'] - elif mode == 'val': - return self.options['inference'] - else: - raise ValueError(f'mode should be train or val but got {mode}') - - -@RUNNERS.register_module() -class IPUEpochBasedRunner(IPUBaseRunner, EpochBasedRunner): - """Epoch-based Runner for IPU. - - The Inheritance order(MRO) is: IPUEpochBasedRunner -> IPUBaseRunner -> - EpochBasedRunner -> BaseRunner This runner train models epoch by epoch. - """ - pass - - -@RUNNERS.register_module() -class IPUIterBasedRunner(IPUBaseRunner, IterBasedRunner): - """Iteration-based Runner for IPU. - - The Inheritance order(MRO) is: IPUIterBasedRunner -> IPUBaseRunner -> - IterBasedRunner -> BaseRunner This runner train models iteration by - iteration. - """ - pass diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/utils.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/utils.py deleted file mode 100755 index 79709db1e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/ipu/utils.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import numpy as np -import popart -import poptorch -import torch -import torch.nn as nn - -from mmcv.utils import Registry - - -def _options_assigner(cfg, options_node): - # set popart.options by config - # cfg: dict, python data type - # options_node: python module or function - if isinstance(cfg, dict): - for key in cfg: - _options_assigner(cfg[key], getattr(options_node, key)) - elif isinstance(cfg, (int, float, str, list)): - if callable(options_node): - options_node(cfg) - else: - error_msg = f'options_node type {type(options_node)} not supported' - raise NotImplementedError(error_msg) - else: - error_msg = f'cfg type {type(cfg)} not supported' - raise NotImplementedError(error_msg) - - -def cfg2options(cfg): - """Parse dictionary to ipu options. - - Args: - cfg (dict): A dictionary of ipu settings. - - Returns: - dict[str, poptorch.Options]: Training options and inference options - of IPU. - """ - # set ipu options for inference and training by config - train_cfg = cfg.pop('train_cfg', {}) - eval_cfg = cfg.pop('eval_cfg', {}) - eval_cfg['replicationFactor'] = 1 # eval mode only use one replica - eval_cfg['executionStrategy'] = 'ShardedExecution' - # overwrite default ipu cfg with specified train cfgs - training_ipu_cfg = {**cfg, **train_cfg} - # overwrite default ipu cfg with specified eval cfgs - inference_ipu_cfg = {**cfg, **eval_cfg} - - ipu_options = { - 'training': _cast_to_options(training_ipu_cfg), - 'inference': _cast_to_options(inference_ipu_cfg) - } - - # TODO configure these codes - ipu_options['training']._Popart.set('disableGradAccumulationTensorStreams', - True) - ipu_options['training']._Popart.set( - 'accumulateOuterFragmentSettings.schedule', - int(popart.AccumulateOuterFragmentSchedule.OverlapMemoryOptimized)) - ipu_options['training'].Precision.enableStochasticRounding(True) - - return ipu_options - - -def _cast_to_options(cfg): - # If it cannot be directly assigned, use if statement to parse it, - # and if it can be directly assigned, use _options_assigner to assign - options = poptorch.Options() - - if 'availableMemoryProportion' in cfg: - available_memory_proportion = cfg.pop('availableMemoryProportion') - mem_props = {} - for i, mem_prop in enumerate(available_memory_proportion): - mem_props[f'IPU{i}'] = mem_prop - options.setAvailableMemoryProportion(mem_props) - - if 'executionStrategy' in cfg: - execution_strategy = cfg.pop('executionStrategy') - if execution_strategy == 'SameAsIpu': - options.setExecutionStrategy( - poptorch.PipelinedExecution( - getattr(poptorch.AutoStage, execution_strategy))) - elif execution_strategy == 'ShardedExecution': - options.setExecutionStrategy(poptorch.ShardedExecution()) - else: - raise NotImplementedError( - 'executionStrategy should be "SameAsIpu" or "ShardedExecution"' - f', but got {execution_strategy}') - - if 'partialsType' in cfg: - partials_type = cfg.pop('partialsType') - options.Precision.setPartialsType(getattr( - torch, partials_type)) # half or float - - _options_assigner(cfg, options) - return options - - -def model_sharding(model, split_edges): - """split models in-place into multi-IPUs. - - Args: - model (nn.Module): The target model to be split. - split_edges (list of dict): Model layer names or layer numbers - of split edge. Each item of ``split_edges`` is a dictionary, - which may contain the following key-pairs: - - - layer_to_call: PyTorch module to assign to the block - - user_id (optional): A user defined identifier for the block. - - ipu_id: The id of the IPU to run on. - - Examples: - >>> split_edges = [ - ... dict(layer_to_call='model.conv1', ipu_id=0), - ... dict(layer_to_call='model.conv3', ipu_id=1)] - >>> sharding_model = model_sharding(torch_model, split_edges) - - Returns: - nn.Module: Split model. - """ - if len(split_edges) == 0: - return model - assert isinstance(split_edges, list) - spilt_edges_dict = {edge['layer_to_call']: edge for edge in split_edges} - - for idx, (name, module) in enumerate(model.named_modules()): - if idx in spilt_edges_dict and name in spilt_edges_dict: - raise ValueError( - 'The same layer is referenced twice while doing model' - f' partition: idx is {idx} and name is {name}') - - edge = spilt_edges_dict.pop(name, None) - edge = spilt_edges_dict.pop(idx, edge) - if edge is not None: - poptorch.BeginBlock(module, edge.get('user_id', name), - edge['ipu_id']) - - # ensure all split_edges are used - if len(spilt_edges_dict) > 0: - split_edge_names = list(spilt_edges_dict.keys()) - raise RuntimeError( - f'split_edges: {split_edge_names} are not contained in the model') - return model - - -def recomputation_checkpoint(model: nn.Module, module_names: list): - """Annotates the output of a module to be checkpointed instead of - recomputed. - - If recomputation mode is enabled, ipu will release the activations of - the middle layers to save memory. During the backward of gradient, - the activation of the middle layer will be recalculated again. - This function is used to declare the activations of some intermediate - layers that need to be saved in order to skip the recomputation of - some layers. - - Args: - model (nn.Module): The target model to apply recomputation - checkpoint. - module_names (list): Layer names of module. - """ - - def recompute_outputs(module, inputs, outputs): - if isinstance(outputs, tuple): - return tuple(poptorch.recomputationCheckpoint(y) for y in outputs) - else: - return poptorch.recomputationCheckpoint(outputs) - - for name, module in model.named_modules(): - if name in module_names: - module.register_forward_hook(recompute_outputs) - module_names.remove(name) - - # check all module_names are used - assert len(module_names) == 0,\ - f'recomputed nodes: {module_names} are not contained in the model' - - -def compare_ndarray(featA, featB, rtol=1e-3, atol=1e-5): - """Align data between two activations or weights.""" - try: - np.testing.assert_allclose(featA, featB, rtol=rtol, atol=atol) - except AssertionError as e: - print(e) - - -def build_from_cfg_with_wrapper(cfg, - registry, - wrapper_func=None, - default_args=None): - """Build a module from config dict and wrap module with "wrapper_func". - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - wrapper_func (function): Used to wrap class - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - - if wrapper_func is None: - wrapped_obj_cls = obj_cls - else: - wrapped_obj_cls = wrapper_func(obj_cls) - try: - return wrapped_obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{wrapped_obj_cls.__name__}: {e}') diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/__init__.py deleted file mode 100644 index 572c4da7e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .data_parallel import MLUDataParallel -from .distributed import MLUDistributedDataParallel -from .scatter_gather import scatter, scatter_kwargs - -__all__ = [ - 'MLUDataParallel', 'MLUDistributedDataParallel', 'scatter', - 'scatter_kwargs' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/_functions.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/_functions.py deleted file mode 100644 index 7c35e65a2..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/_functions.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def scatter(input, devices): - """scatter copies tensor to MLU directly.""" - if isinstance(input, list): - outputs = [scatter(_input, devices) for _input in input] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - return output.to('mlu') if devices != [-1] else output - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_mlus, input): - outputs = scatter(input, target_mlus) - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/data_parallel.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/data_parallel.py deleted file mode 100644 index b2d09d0b0..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/data_parallel.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -import torch - -from mmcv.parallel import MMDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDataParallel(MMDataParallel): - """The MLUDataParallel module that supports DataContainer. - - MLUDataParallel is a class inherited from MMDataParall, which supports - MLU training and inference only. - - The main differences with MMDataParallel: - - - It only supports single-card of MLU, and only use first card to - run training and inference. - - - It uses direct host-to-device copy instead of stream-background - scatter. - - .. warning:: - MLUDataParallel only supports single MLU training, if you need to - train with multiple MLUs, please use MLUDistributedDataParallel - instead. If you have multiple MLUs, you can set the environment - variable ``MLU_VISIBLE_DEVICES=0`` (or any other card number(s)) - to specify the running device. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MLUDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.device_ids = [0] - self.src_device_obj = torch.device('mlu:0') - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/distributed.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/distributed.py deleted file mode 100644 index 3768c754c..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/distributed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.parallel import MMDistributedDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDistributedDataParallel(MMDistributedDataParallel): - """The DDP module supports DataContainer. - - MLUDDP has one difference from MMDDP which moves data to MLU with coping - instead of scattering. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/scatter_gather.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/scatter_gather.py deleted file mode 100644 index 0b0c9b96f..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/device/mlu/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmcv.parallel.data_container import DataContainer -from ._functions import Scatter - - -def scatter(inputs, target_mlus, dim=0): - """Scatter inputs to target mlu. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_mlus != [-1]: - obj = obj.to('mlu') - return [obj] - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_mlus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_mlus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_mlus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_mlus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_mlus, dim) if inputs else [] - kwargs = scatter(kwargs, target_mlus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/engine/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/engine/__init__.py deleted file mode 100644 index 3193b7f66..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, - single_gpu_test) - -__all__ = [ - 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', - 'single_gpu_test' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/engine/test.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/engine/test.py deleted file mode 100644 index f236b1cda..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/engine/test.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, data_loader): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - # Assume result has the same length of batch_size - # refer to https://github.com/open-mmlab/mmcv/issues/985 - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting - ``gpu_collect=True``, it encodes results to gpu tensors and use gpu - communication for results collection. On cpu mode it saves the results on - different gpus to ``tmpdir`` and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - if rank == 0: - batch_size = len(result) - batch_size_all = batch_size * world_size - if batch_size_all + prog_bar.completed > len(dataset): - batch_size_all = len(dataset) - prog_bar.completed - for _ in range(batch_size_all): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results under cpu mode. - - On cpu mode, this function will save the results on different gpus to - ``tmpdir`` and collect them by the rank 0 worker. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - tmpdir (str | None): temporal directory for collected results to - store. If set to None, it will create a random temporal directory - for it. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_result = mmcv.load(part_file) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results under gpu mode. - - On gpu mode, this function will encode results to gpu tensors and use gpu - communication for results collection. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/__init__.py deleted file mode 100644 index 2051b85f7..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .file_client import BaseStorageBackend, FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler -from .io import dump, load, register_handler -from .parse import dict_from_file, list_from_file - -__all__ = [ - 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', - 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', - 'list_from_file', 'dict_from_file' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/file_client.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/file_client.py deleted file mode 100644 index e7fd7cdfa..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/file_client.py +++ /dev/null @@ -1,1163 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import os -import os.path as osp -import re -import tempfile -import warnings -from abc import ABCMeta, abstractmethod -from contextlib import contextmanager -from pathlib import Path -from typing import Iterable, Iterator, Optional, Tuple, Union -from urllib.request import urlopen - -import mmcv -from mmcv.utils.misc import has_method -from mmcv.utils.path import is_filepath - - -class BaseStorageBackend(metaclass=ABCMeta): - """Abstract class of storage backends. - - All backends need to implement two apis: ``get()`` and ``get_text()``. - ``get()`` reads the file as a byte stream and ``get_text()`` reads the file - as texts. - """ - - # a flag to indicate whether the backend can create a symlink for a file - _allow_symlink = False - - @property - def name(self): - return self.__class__.__name__ - - @property - def allow_symlink(self): - return self._allow_symlink - - @abstractmethod - def get(self, filepath): - pass - - @abstractmethod - def get_text(self, filepath): - pass - - -class CephBackend(BaseStorageBackend): - """Ceph storage backend (for internal use). - - Args: - path_mapping (dict|None): path mapping dict from local path to Petrel - path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` - will be replaced by ``dst``. Default: None. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - """ - - def __init__(self, path_mapping=None): - try: - import ceph - except ImportError: - raise ImportError('Please install ceph to enable CephBackend.') - - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - self._client = ceph.S3Client() - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def get(self, filepath): - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class PetrelBackend(BaseStorageBackend): - """Petrel storage backend (for internal use). - - PetrelBackend supports reading and writing data to multiple clusters. - If the file path contains the cluster name, PetrelBackend will read data - from specified cluster or write data to it. Otherwise, PetrelBackend will - access the default cluster. - - Args: - path_mapping (dict, optional): Path mapping dict from local path to - Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in - ``filepath`` will be replaced by ``dst``. Default: None. - enable_mc (bool, optional): Whether to enable memcached support. - Default: True. - - Examples: - >>> filepath1 = 's3://path/of/file' - >>> filepath2 = 'cluster-name:s3://path/of/file' - >>> client = PetrelBackend() - >>> client.get(filepath1) # get data from default cluster - >>> client.get(filepath2) # get data from 'cluster-name' cluster - """ - - def __init__(self, - path_mapping: Optional[dict] = None, - enable_mc: bool = True): - try: - from petrel_client import client - except ImportError: - raise ImportError('Please install petrel_client to enable ' - 'PetrelBackend.') - - self._client = client.Client(enable_mc=enable_mc) - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def _map_path(self, filepath: Union[str, Path]) -> str: - """Map ``filepath`` to a string path whose prefix will be replaced by - :attr:`self.path_mapping`. - - Args: - filepath (str): Path to be mapped. - """ - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - return filepath - - def _format_path(self, filepath: str) -> str: - """Convert a ``filepath`` to standard format of petrel oss. - - If the ``filepath`` is concatenated by ``os.path.join``, in a Windows - environment, the ``filepath`` will be the format of - 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the - above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. - - Args: - filepath (str): Path to be formatted. - """ - return re.sub(r'\\+', '/', filepath) - - def get(self, filepath: Union[str, Path]) -> memoryview: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - memoryview: A memory view of expected bytes object to avoid - copying. The memoryview object can be converted to bytes by - ``value_buf.tobytes()``. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return str(self.get(filepath), encoding=encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Save data to a given ``filepath``. - - Args: - obj (bytes): Data to be saved. - filepath (str or Path): Path to write data. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.put(filepath, obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Save data to a given ``filepath``. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to encode the ``obj``. - Default: 'utf-8'. - """ - self.put(bytes(obj, encoding=encoding), filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - if not has_method(self._client, 'delete'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `delete` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.delete(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - if not (has_method(self._client, 'contains') - and has_method(self._client, 'isdir')): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` and `isdir` methods, please use a higher' - 'version or dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) or self._client.isdir(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - if not has_method(self._client, 'isdir'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `isdir` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - if not has_method(self._client, 'contains'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` method, please use a higher version or ' - 'dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result after concatenation. - """ - filepath = self._format_path(self._map_path(filepath)) - if filepath.endswith('/'): - filepath = filepath[:-1] - formatted_paths = [filepath] - for path in filepaths: - formatted_paths.append(self._format_path(self._map_path(path))) - return '/'.join(formatted_paths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download a file from ``filepath`` and return a temporary path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str | Path): Download a file from ``filepath``. - - Examples: - >>> client = PetrelBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('s3://path/of/your/file') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one temporary path. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - assert self.isfile(filepath) - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - Petrel has no concept of directories but it simulates the directory - hierarchy in the filesystem through public prefixes. In addition, - if the returned path ends with '/', it means the path is a public - prefix which is a logical directory. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - In addition, the returned path of directory will not contains the - suffix '/' which is consistent with other backends. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if not has_method(self._client, 'list'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `list` method, please use a higher version or dev' - ' branch instead.')) - - dir_path = self._map_path(dir_path) - dir_path = self._format_path(dir_path) - if list_dir and suffix is not None: - raise TypeError( - '`list_dir` should be False when `suffix` is not None') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - # Petrel's simulated directory hierarchy assumes that directory paths - # should end with `/` - if not dir_path.endswith('/'): - dir_path += '/' - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for path in self._client.list(dir_path): - # the `self.isdir` is not used here to determine whether path - # is a directory, because `self.isdir` relies on - # `self._client.list` - if path.endswith('/'): # a directory path - next_dir_path = self.join_path(dir_path, path) - if list_dir: - # get the relative path and exclude the last - # character '/' - rel_dir = next_dir_path[len(root):-1] - yield rel_dir - if recursive: - yield from _list_dir_or_file(next_dir_path, list_dir, - list_file, suffix, - recursive) - else: # a file path - absolute_path = self.join_path(dir_path, path) - rel_path = absolute_path[len(root):] - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class MemcachedBackend(BaseStorageBackend): - """Memcached storage backend. - - Attributes: - server_list_cfg (str): Config file for memcached server list. - client_cfg (str): Config file for memcached client. - sys_path (str | None): Additional path to be appended to `sys.path`. - Default: None. - """ - - def __init__(self, server_list_cfg, client_cfg, sys_path=None): - if sys_path is not None: - import sys - sys.path.append(sys_path) - try: - import mc - except ImportError: - raise ImportError( - 'Please install memcached to enable MemcachedBackend.') - - self.server_list_cfg = server_list_cfg - self.client_cfg = client_cfg - self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, - self.client_cfg) - # mc.pyvector servers as a point which points to a memory cache - self._mc_buffer = mc.pyvector() - - def get(self, filepath): - filepath = str(filepath) - import mc - self._client.Get(filepath, self._mc_buffer) - value_buf = mc.ConvertBuffer(self._mc_buffer) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class LmdbBackend(BaseStorageBackend): - """Lmdb storage backend. - - Args: - db_path (str): Lmdb database path. - readonly (bool, optional): Lmdb environment parameter. If True, - disallow any write operations. Default: True. - lock (bool, optional): Lmdb environment parameter. If False, when - concurrent access occurs, do not lock the database. Default: False. - readahead (bool, optional): Lmdb environment parameter. If False, - disable the OS filesystem readahead mechanism, which may improve - random read performance when a database is larger than RAM. - Default: False. - - Attributes: - db_path (str): Lmdb database path. - """ - - def __init__(self, - db_path, - readonly=True, - lock=False, - readahead=False, - **kwargs): - try: - import lmdb # NOQA - except ImportError: - raise ImportError('Please install lmdb to enable LmdbBackend.') - - self.db_path = str(db_path) - self.readonly = readonly - self.lock = lock - self.readahead = readahead - self.kwargs = kwargs - self._client = None - - def get(self, filepath): - """Get values according to the filepath. - - Args: - filepath (str | obj:`Path`): Here, filepath is the lmdb key. - """ - if self._client is None: - self._client = self._get_client() - - with self._client.begin(write=False) as txn: - value_buf = txn.get(str(filepath).encode('utf-8')) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - def _get_client(self): - import lmdb - - return lmdb.open( - self.db_path, - readonly=self.readonly, - lock=self.lock, - readahead=self.readahead, - **self.kwargs) - - def __del__(self): - self._client.close() - - -class HardDiskBackend(BaseStorageBackend): - """Raw hard disks storage backend.""" - - _allow_symlink = True - - def get(self, filepath: Union[str, Path]) -> bytes: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes: Expected bytes object. - """ - with open(filepath, 'rb') as f: - value_buf = f.read() - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - with open(filepath, 'r', encoding=encoding) as f: - value_buf = f.read() - return value_buf - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` will create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'wb') as f: - f.write(obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` will create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'w', encoding=encoding) as f: - f.write(obj) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - os.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return osp.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return osp.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return osp.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return osp.join(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, filepath: Union[str, Path]) -> Iterable[Union[str, Path]]: - """Only for unified API and do nothing.""" - yield filepath - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if list_dir and suffix is not None: - raise TypeError('`suffix` should be None when `list_dir` is True') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - elif osp.isdir(entry.path): - if list_dir: - rel_dir = osp.relpath(entry.path, root) - yield rel_dir - if recursive: - yield from _list_dir_or_file(entry.path, list_dir, - list_file, suffix, - recursive) - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class HTTPBackend(BaseStorageBackend): - """HTTP and HTTPS storage bachend.""" - - def get(self, filepath): - value_buf = urlopen(filepath).read() - return value_buf - - def get_text(self, filepath, encoding='utf-8'): - value_buf = urlopen(filepath).read() - return value_buf.decode(encoding) - - @contextmanager - def get_local_path(self, filepath: str) -> Iterable[str]: - """Download a file from ``filepath``. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str): Download a file from ``filepath``. - - Examples: - >>> client = HTTPBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('http://path/of/your/file') as path: - ... # do something here - """ - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - -class FileClient: - """A general file client to access files in different backends. - - The client loads a file or text in a specified backend from its path - and returns it as a binary or text file. There are two ways to choose a - backend, the name of backend and the prefix of path. Although both of them - can be used to choose a storage backend, ``backend`` has a higher priority - that is if they are all set, the storage backend will be chosen by the - backend argument. If they are all `None`, the disk backend will be chosen. - Note that It can also register other backend accessor with a given name, - prefixes, and backend class. In addition, We use the singleton pattern to - avoid repeated object creation. If the arguments are the same, the same - object will be returned. - - Args: - backend (str, optional): The storage backend type. Options are "disk", - "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. - prefix (str, optional): The prefix of the registered storage backend. - Options are "s3", "http", "https". Default: None. - - Examples: - >>> # only set backend - >>> file_client = FileClient(backend='petrel') - >>> # only set prefix - >>> file_client = FileClient(prefix='s3') - >>> # set both backend and prefix but use backend to choose client - >>> file_client = FileClient(backend='petrel', prefix='s3') - >>> # if the arguments are the same, the same object is returned - >>> file_client1 = FileClient(backend='petrel') - >>> file_client1 is file_client - True - - Attributes: - client (:obj:`BaseStorageBackend`): The backend object. - """ - - _backends = { - 'disk': HardDiskBackend, - 'ceph': CephBackend, - 'memcached': MemcachedBackend, - 'lmdb': LmdbBackend, - 'petrel': PetrelBackend, - 'http': HTTPBackend, - } - # This collection is used to record the overridden backends, and when a - # backend appears in the collection, the singleton pattern is disabled for - # that backend, because if the singleton pattern is used, then the object - # returned will be the backend before overwriting - _overridden_backends = set() - _prefix_to_backends = { - 's3': PetrelBackend, - 'http': HTTPBackend, - 'https': HTTPBackend, - } - _overridden_prefixes = set() - - _instances = {} - - def __new__(cls, backend=None, prefix=None, **kwargs): - if backend is None and prefix is None: - backend = 'disk' - if backend is not None and backend not in cls._backends: - raise ValueError( - f'Backend {backend} is not supported. Currently supported ones' - f' are {list(cls._backends.keys())}') - if prefix is not None and prefix not in cls._prefix_to_backends: - raise ValueError( - f'prefix {prefix} is not supported. Currently supported ones ' - f'are {list(cls._prefix_to_backends.keys())}') - - # concatenate the arguments to a unique key for determining whether - # objects with the same arguments were created - arg_key = f'{backend}:{prefix}' - for key, value in kwargs.items(): - arg_key += f':{key}:{value}' - - # if a backend was overridden, it will create a new object - if (arg_key in cls._instances - and backend not in cls._overridden_backends - and prefix not in cls._overridden_prefixes): - _instance = cls._instances[arg_key] - else: - # create a new object and put it to _instance - _instance = super().__new__(cls) - if backend is not None: - _instance.client = cls._backends[backend](**kwargs) - else: - _instance.client = cls._prefix_to_backends[prefix](**kwargs) - - cls._instances[arg_key] = _instance - - return _instance - - @property - def name(self): - return self.client.name - - @property - def allow_symlink(self): - return self.client.allow_symlink - - @staticmethod - def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: - """Parse the prefix of a uri. - - Args: - uri (str | Path): Uri to be parsed that contains the file prefix. - - Examples: - >>> FileClient.parse_uri_prefix('s3://path/of/your/file') - 's3' - - Returns: - str | None: Return the prefix of uri if the uri contains '://' else - ``None``. - """ - assert is_filepath(uri) - uri = str(uri) - if '://' not in uri: - return None - else: - prefix, _ = uri.split('://') - # In the case of PetrelBackend, the prefix may contains the cluster - # name like clusterName:s3 - if ':' in prefix: - _, prefix = prefix.split(':') - return prefix - - @classmethod - def infer_client(cls, - file_client_args: Optional[dict] = None, - uri: Optional[Union[str, Path]] = None) -> 'FileClient': - """Infer a suitable file client based on the URI and arguments. - - Args: - file_client_args (dict, optional): Arguments to instantiate a - FileClient. Default: None. - uri (str | Path, optional): Uri to be parsed that contains the file - prefix. Default: None. - - Examples: - >>> uri = 's3://path/of/your/file' - >>> file_client = FileClient.infer_client(uri=uri) - >>> file_client_args = {'backend': 'petrel'} - >>> file_client = FileClient.infer_client(file_client_args) - - Returns: - FileClient: Instantiated FileClient object. - """ - assert file_client_args is not None or uri is not None - if file_client_args is None: - file_prefix = cls.parse_uri_prefix(uri) # type: ignore - return cls(prefix=file_prefix) - else: - return cls(**file_client_args) - - @classmethod - def _register_backend(cls, name, backend, force=False, prefixes=None): - if not isinstance(name, str): - raise TypeError('the backend name should be a string, ' - f'but got {type(name)}') - if not inspect.isclass(backend): - raise TypeError( - f'backend should be a class but got {type(backend)}') - if not issubclass(backend, BaseStorageBackend): - raise TypeError( - f'backend {backend} is not a subclass of BaseStorageBackend') - if not force and name in cls._backends: - raise KeyError( - f'{name} is already registered as a storage backend, ' - 'add "force=True" if you want to override it') - - if name in cls._backends and force: - cls._overridden_backends.add(name) - cls._backends[name] = backend - - if prefixes is not None: - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if prefix not in cls._prefix_to_backends: - cls._prefix_to_backends[prefix] = backend - elif (prefix in cls._prefix_to_backends) and force: - cls._overridden_prefixes.add(prefix) - cls._prefix_to_backends[prefix] = backend - else: - raise KeyError( - f'{prefix} is already registered as a storage backend,' - ' add "force=True" if you want to override it') - - @classmethod - def register_backend(cls, name, backend=None, force=False, prefixes=None): - """Register a backend to FileClient. - - This method can be used as a normal class method or a decorator. - - .. code-block:: python - - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - FileClient.register_backend('new', NewBackend) - - or - - .. code-block:: python - - @FileClient.register_backend('new') - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - Args: - name (str): The name of the registered backend. - backend (class, optional): The backend class to be registered, - which must be a subclass of :class:`BaseStorageBackend`. - When this method is used as a decorator, backend is None. - Defaults to None. - force (bool, optional): Whether to override the backend if the name - has already been registered. Defaults to False. - prefixes (str or list[str] or tuple[str], optional): The prefixes - of the registered storage backend. Default: None. - `New in version 1.3.15.` - """ - if backend is not None: - cls._register_backend( - name, backend, force=force, prefixes=prefixes) - return - - def _register(backend_cls): - cls._register_backend( - name, backend_cls, force=force, prefixes=prefixes) - return backend_cls - - return _register - - def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: - """Read data from a given ``filepath`` with 'rb' mode. - - Note: - There are two types of return values for ``get``, one is ``bytes`` - and the other is ``memoryview``. The advantage of using memoryview - is that you can avoid copying, and if you want to convert it to - ``bytes``, you can use ``.tobytes()``. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes | memoryview: Expected bytes object or a memory view of the - bytes object. - """ - return self.client.get(filepath) - - def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return self.client.get_text(filepath, encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` should create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - self.client.put(obj, filepath) - - def put_text(self, obj: str, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` should create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str, optional): The encoding format used to open the - `filepath`. Default: 'utf-8'. - """ - self.client.put_text(obj, filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str, Path): Path to be removed. - """ - self.client.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return self.client.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return self.client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return self.client.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return self.client.join_path(filepath, *filepaths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download data from ``filepath`` and write the data to local path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Note: - If the ``filepath`` is a local path, just return itself. - - .. warning:: - ``get_local_path`` is an experimental interface that may change in - the future. - - Args: - filepath (str or Path): Path to be read data. - - Examples: - >>> file_client = FileClient(prefix='s3') - >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one path. - """ - with self.client.get_local_path(str(filepath)) as local_path: - yield local_path - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, - suffix, recursive) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/__init__.py deleted file mode 100644 index aa24d9197..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseFileHandler -from .json_handler import JsonHandler -from .pickle_handler import PickleHandler -from .yaml_handler import YamlHandler - -__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/base.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/base.py deleted file mode 100644 index 288878bc5..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseFileHandler(metaclass=ABCMeta): - # `str_like` is a flag to indicate whether the type of file object is - # str-like object or bytes-like object. Pickle only processes bytes-like - # objects but json only processes str-like object. If it is str-like - # object, `StringIO` will be used to process the buffer. - str_like = True - - @abstractmethod - def load_from_fileobj(self, file, **kwargs): - pass - - @abstractmethod - def dump_to_fileobj(self, obj, file, **kwargs): - pass - - @abstractmethod - def dump_to_str(self, obj, **kwargs): - pass - - def load_from_path(self, filepath, mode='r', **kwargs): - with open(filepath, mode) as f: - return self.load_from_fileobj(f, **kwargs) - - def dump_to_path(self, obj, filepath, mode='w', **kwargs): - with open(filepath, mode) as f: - self.dump_to_fileobj(obj, f, **kwargs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/json_handler.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/json_handler.py deleted file mode 100644 index 18d4f15f7..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/json_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -import numpy as np - -from .base import BaseFileHandler - - -def set_default(obj): - """Set default json values for non-serializable values. - - It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. - It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, - etc.) into plain numbers of plain python built-in types. - """ - if isinstance(obj, (set, range)): - return list(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, np.generic): - return obj.item() - raise TypeError(f'{type(obj)} is unsupported for json dump') - - -class JsonHandler(BaseFileHandler): - - def load_from_fileobj(self, file): - return json.load(file) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('default', set_default) - json.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('default', set_default) - return json.dumps(obj, **kwargs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/pickle_handler.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/pickle_handler.py deleted file mode 100644 index b37c79bed..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/pickle_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import pickle - -from .base import BaseFileHandler - - -class PickleHandler(BaseFileHandler): - - str_like = False - - def load_from_fileobj(self, file, **kwargs): - return pickle.load(file, **kwargs) - - def load_from_path(self, filepath, **kwargs): - return super(PickleHandler, self).load_from_path( - filepath, mode='rb', **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('protocol', 2) - return pickle.dumps(obj, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('protocol', 2) - pickle.dump(obj, file, **kwargs) - - def dump_to_path(self, obj, filepath, **kwargs): - super(PickleHandler, self).dump_to_path( - obj, filepath, mode='wb', **kwargs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/yaml_handler.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/yaml_handler.py deleted file mode 100644 index 60911e7e6..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/handlers/yaml_handler.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import yaml - -try: - from yaml import CDumper as Dumper - from yaml import CLoader as Loader -except ImportError: - from yaml import Loader, Dumper - -from .base import BaseFileHandler # isort:skip - - -class YamlHandler(BaseFileHandler): - - def load_from_fileobj(self, file, **kwargs): - kwargs.setdefault('Loader', Loader) - return yaml.load(file, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('Dumper', Dumper) - yaml.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('Dumper', Dumper) - return yaml.dump(obj, **kwargs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/io.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/io.py deleted file mode 100644 index aaefde58a..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/io.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from io import BytesIO, StringIO -from pathlib import Path - -from ..utils import is_list_of, is_str -from .file_client import FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler - -file_handlers = { - 'json': JsonHandler(), - 'yaml': YamlHandler(), - 'yml': YamlHandler(), - 'pickle': PickleHandler(), - 'pkl': PickleHandler() -} - - -def load(file, file_format=None, file_client_args=None, **kwargs): - """Load data from json/yaml/pickle files. - - This method provides a unified api for loading data from serialized files. - - Note: - In v1.3.16 and later, ``load`` supports loading data from serialized - files those can be storaged in different backends. - - Args: - file (str or :obj:`Path` or file-like object): Filename or a file-like - object. - file_format (str, optional): If not specified, the file format will be - inferred from the file extension, otherwise use the specified one. - Currently supported formats include "json", "yaml/yml" and - "pickle/pkl". - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> load('/path/of/your/file') # file is storaged in disk - >>> load('https://path/of/your/file') # file is storaged in Internet - >>> load('s3://path/of/your/file') # file is storaged in petrel - - Returns: - The content from the file. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None and is_str(file): - file_format = file.split('.')[-1] - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO(file_client.get_text(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - else: - with BytesIO(file_client.get(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - elif hasattr(file, 'read'): - obj = handler.load_from_fileobj(file, **kwargs) - else: - raise TypeError('"file" must be a filepath str or a file-object') - return obj - - -def dump(obj, file=None, file_format=None, file_client_args=None, **kwargs): - """Dump data to json/yaml/pickle strings or files. - - This method provides a unified api for dumping data as strings or to files, - and also supports custom arguments for each file format. - - Note: - In v1.3.16 and later, ``dump`` supports dumping data as strings or to - files which is saved to different backends. - - Args: - obj (any): The python object to be dumped. - file (str or :obj:`Path` or file-like object, optional): If not - specified, then the object is dumped to a str, otherwise to a file - specified by the filename or file-like object. - file_format (str, optional): Same as :func:`load`. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dump('hello world', '/path/of/your/file') # disk - >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel - - Returns: - bool: True for success, False otherwise. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None: - if is_str(file): - file_format = file.split('.')[-1] - elif file is None: - raise ValueError( - 'file_format must be specified since file is None') - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if file is None: - return handler.dump_to_str(obj, **kwargs) - elif is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put_text(f.getvalue(), file) - else: - with BytesIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put(f.getvalue(), file) - elif hasattr(file, 'write'): - handler.dump_to_fileobj(obj, file, **kwargs) - else: - raise TypeError('"file" must be a filename str or a file-object') - - -def _register_handler(handler, file_formats): - """Register a handler for some file extensions. - - Args: - handler (:obj:`BaseFileHandler`): Handler to be registered. - file_formats (str or list[str]): File formats to be handled by this - handler. - """ - if not isinstance(handler, BaseFileHandler): - raise TypeError( - f'handler must be a child of BaseFileHandler, not {type(handler)}') - if isinstance(file_formats, str): - file_formats = [file_formats] - if not is_list_of(file_formats, str): - raise TypeError('file_formats must be a str or a list of str') - for ext in file_formats: - file_handlers[ext] = handler - - -def register_handler(file_formats, **kwargs): - - def wrap(cls): - _register_handler(cls(**kwargs), file_formats) - return cls - - return wrap diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/parse.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/parse.py deleted file mode 100644 index f60f0d611..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/fileio/parse.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from io import StringIO - -from .file_client import FileClient - - -def list_from_file(filename, - prefix='', - offset=0, - max_num=0, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a list of strings. - - Note: - In v1.3.16 and later, ``list_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a list for strings. - - Args: - filename (str): Filename. - prefix (str): The prefix to be inserted to the beginning of each item. - offset (int): The offset of lines. - max_num (int): The maximum number of lines to be read, - zeros and negatives mean no limitation. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> list_from_file('/path/of/your/file') # disk - ['hello', 'world'] - >>> list_from_file('s3://path/of/your/file') # ceph or petrel - ['hello', 'world'] - - Returns: - list[str]: A list of strings. - """ - cnt = 0 - item_list = [] - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for _ in range(offset): - f.readline() - for line in f: - if 0 < max_num <= cnt: - break - item_list.append(prefix + line.rstrip('\n\r')) - cnt += 1 - return item_list - - -def dict_from_file(filename, - key_type=str, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a dict. - - Each line of the text file will be two or more columns split by - whitespaces or tabs. The first column will be parsed as dict keys, and - the following columns will be parsed as dict values. - - Note: - In v1.3.16 and later, ``dict_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a dict. - - Args: - filename(str): Filename. - key_type(type): Type of the dict keys. str is user by default and - type conversion will be performed if specified. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dict_from_file('/path/of/your/file') # disk - {'key1': 'value1', 'key2': 'value2'} - >>> dict_from_file('s3://path/of/your/file') # ceph or petrel - {'key1': 'value1', 'key2': 'value2'} - - Returns: - dict: The parsed contents. - """ - mapping = {} - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for line in f: - items = line.rstrip('\n').split() - assert len(items) >= 2 - key = key_type(items[0]) - val = items[1:] if len(items) > 2 else items[1] - mapping[key] = val - return mapping diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/__init__.py deleted file mode 100644 index d0051d609..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, - gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, - rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) -from .geometric import (cutout, imcrop, imflip, imflip_, impad, - impad_to_multiple, imrescale, imresize, imresize_like, - imresize_to_multiple, imrotate, imshear, imtranslate, - rescale_size) -from .io import imfrombytes, imread, imwrite, supported_backends, use_backend -from .misc import tensor2imgs -from .photometric import (adjust_brightness, adjust_color, adjust_contrast, - adjust_lighting, adjust_sharpness, auto_contrast, - clahe, imdenormalize, imequalize, iminvert, - imnormalize, imnormalize_, lut_transform, posterize, - solarize) - -__all__ = [ - 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', - 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', - 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', - 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', - 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', - 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', - 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', - 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', - 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', - 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/colorspace.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/colorspace.py deleted file mode 100644 index 4337720ea..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/colorspace.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def imconvert(img, src, dst): - """Convert an image from the src colorspace to dst colorspace. - - Args: - img (ndarray): The input image. - src (str): The source colorspace, e.g., 'rgb', 'hsv'. - dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. - - Returns: - ndarray: The converted image. - """ - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - out_img = cv2.cvtColor(img, code) - return out_img - - -def bgr2gray(img, keepdim=False): - """Convert a BGR image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def rgb2gray(img, keepdim=False): - """Convert a RGB image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def gray2bgr(img): - """Convert a grayscale image to BGR image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted BGR image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return out_img - - -def gray2rgb(img): - """Convert a grayscale image to RGB image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted RGB image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) - return out_img - - -def _convert_input_type_range(img): - """Convert the type and range of the input image. - - It converts the input image to np.float32 type and range of [0, 1]. - It is mainly used for pre-processing the input image in colorspace - conversion functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - (ndarray): The converted image with type of np.float32 and range of - [0, 1]. - """ - img_type = img.dtype - img = img.astype(np.float32) - if img_type == np.float32: - pass - elif img_type == np.uint8: - img /= 255. - else: - raise TypeError('The img type should be np.float32 or np.uint8, ' - f'but got {img_type}') - return img - - -def _convert_output_type_range(img, dst_type): - """Convert the type and range of the image according to dst_type. - - It converts the image to desired type and range. If `dst_type` is np.uint8, - images will be converted to np.uint8 type with range [0, 255]. If - `dst_type` is np.float32, it converts the image to np.float32 type with - range [0, 1]. - It is mainly used for post-processing images in colorspace conversion - functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The image to be converted with np.float32 type and - range [0, 255]. - dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it - converts the image to np.uint8 type with range [0, 255]. If - dst_type is np.float32, it converts the image to np.float32 type - with range [0, 1]. - - Returns: - (ndarray): The converted image with desired type and range. - """ - if dst_type not in (np.uint8, np.float32): - raise TypeError('The dst_type should be np.float32 or np.uint8, ' - f'but got {dst_type}') - if dst_type == np.uint8: - img = img.round() - else: - img /= 255. - return img.astype(dst_type) - - -def rgb2ycbcr(img, y_only=False): - """Convert a RGB image to YCbCr image. - - This function produces the same results as Matlab's `rgb2ycbcr` function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 - else: - out_img = np.matmul( - img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], - [24.966, 112.0, -18.214]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def bgr2ycbcr(img, y_only=False): - """Convert a BGR image to YCbCr image. - - The bgr version of rgb2ycbcr. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 - else: - out_img = np.matmul( - img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], - [65.481, -37.797, 112.0]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2rgb(img): - """Convert a YCbCr image to RGB image. - - This function produces the same results as Matlab's ycbcr2rgb function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted RGB image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0, -0.00153632, 0.00791071], - [0.00625893, -0.00318811, 0]]) * 255.0 + [ - -222.921, 135.576, -276.836 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2bgr(img): - """Convert a YCbCr image to BGR image. - - The bgr version of ycbcr2rgb. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted BGR image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0.00791071, -0.00153632, 0], - [0, -0.00318811, 0.00625893]]) * 255.0 + [ - -276.836, 135.576, -222.921 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def convert_color_factory(src, dst): - - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - - def convert_color(img): - out_img = cv2.cvtColor(img, code) - return out_img - - convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} - image. - - Args: - img (ndarray or str): The input image. - - Returns: - ndarray: The converted {dst.upper()} image. - """ - - return convert_color - - -bgr2rgb = convert_color_factory('bgr', 'rgb') - -rgb2bgr = convert_color_factory('rgb', 'bgr') - -bgr2hsv = convert_color_factory('bgr', 'hsv') - -hsv2bgr = convert_color_factory('hsv', 'bgr') - -bgr2hls = convert_color_factory('bgr', 'hls') - -hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/geometric.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/geometric.py deleted file mode 100644 index 4c423bf2a..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/geometric.py +++ /dev/null @@ -1,741 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers - -import cv2 -import numpy as np - -from ..utils import to_2tuple -from .io import imread_backend - -try: - from PIL import Image -except ImportError: - Image = None - - -def _scale_size(size, scale): - """Rescale a size by a ratio. - - Args: - size (tuple[int]): (w, h). - scale (float | tuple(float)): Scaling factor. - - Returns: - tuple[int]: scaled size. - """ - if isinstance(scale, (float, int)): - scale = (scale, scale) - w, h = size - return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) - - -cv2_interp_codes = { - 'nearest': cv2.INTER_NEAREST, - 'bilinear': cv2.INTER_LINEAR, - 'bicubic': cv2.INTER_CUBIC, - 'area': cv2.INTER_AREA, - 'lanczos': cv2.INTER_LANCZOS4 -} - -# Pillow >=v9.1.0 use a slightly different naming scheme for filters. -# Set pillow_interp_codes according to the naming scheme used. -if Image is not None: - if hasattr(Image, 'Resampling'): - pillow_interp_codes = { - 'nearest': Image.Resampling.NEAREST, - 'bilinear': Image.Resampling.BILINEAR, - 'bicubic': Image.Resampling.BICUBIC, - 'box': Image.Resampling.BOX, - 'lanczos': Image.Resampling.LANCZOS, - 'hamming': Image.Resampling.HAMMING - } - else: - pillow_interp_codes = { - 'nearest': Image.NEAREST, - 'bilinear': Image.BILINEAR, - 'bicubic': Image.BICUBIC, - 'box': Image.BOX, - 'lanczos': Image.LANCZOS, - 'hamming': Image.HAMMING - } - - -def imresize(img, - size, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image to a given size. - - Args: - img (ndarray): The input image. - size (tuple[int]): Target size (w, h). - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if backend is None: - backend = imread_backend - if backend not in ['cv2', 'pillow']: - raise ValueError(f'backend: {backend} is not supported for resize.' - f"Supported backends are 'cv2', 'pillow'") - - if backend == 'pillow': - assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' - pil_image = Image.fromarray(img) - pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) - resized_img = np.array(pil_image) - else: - resized_img = cv2.resize( - img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) - if not return_scale: - return resized_img - else: - w_scale = size[0] / w - h_scale = size[1] / h - return resized_img, w_scale, h_scale - - -def imresize_to_multiple(img, - divisor, - size=None, - scale_factor=None, - keep_ratio=False, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image according to a given size or scale factor and then rounds - up the the resized or rescaled image size to the nearest value that can be - divided by the divisor. - - Args: - img (ndarray): The input image. - divisor (int | tuple): Resized image size will be a multiple of - divisor. If divisor is a tuple, divisor should be - (w_divisor, h_divisor). - size (None | int | tuple[int]): Target size (w, h). Default: None. - scale_factor (None | float | tuple[float]): Multiplier for spatial - size. Should match input size if it is a tuple and the 2D style is - (w_scale_factor, h_scale_factor). Default: None. - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: False. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if size is not None and scale_factor is not None: - raise ValueError('only one of size or scale_factor should be defined') - elif size is None and scale_factor is None: - raise ValueError('one of size or scale_factor should be defined') - elif size is not None: - size = to_2tuple(size) - if keep_ratio: - size = rescale_size((w, h), size, return_scale=False) - else: - size = _scale_size((w, h), scale_factor) - - divisor = to_2tuple(divisor) - size = tuple([int(np.ceil(s / d)) * d for s, d in zip(size, divisor)]) - resized_img, w_scale, h_scale = imresize( - img, - size, - return_scale=True, - interpolation=interpolation, - out=out, - backend=backend) - if return_scale: - return resized_img, w_scale, h_scale - else: - return resized_img - - -def imresize_like(img, - dst_img, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image to the same size of a given image. - - Args: - img (ndarray): The input image. - dst_img (ndarray): The target image. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = dst_img.shape[:2] - return imresize(img, (w, h), return_scale, interpolation, backend=backend) - - -def rescale_size(old_size, scale, return_scale=False): - """Calculate the new size to be rescaled to. - - Args: - old_size (tuple[int]): The old size (w, h) of image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image size. - - Returns: - tuple[int]: The new rescaled image size. - """ - w, h = old_size - if isinstance(scale, (float, int)): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - scale_factor = scale - elif isinstance(scale, tuple): - max_long_edge = max(scale) - max_short_edge = min(scale) - scale_factor = min(max_long_edge / max(h, w), - max_short_edge / min(h, w)) - else: - raise TypeError( - f'Scale must be a number or tuple of int, but got {type(scale)}') - - new_size = _scale_size((w, h), scale_factor) - - if return_scale: - return new_size, scale_factor - else: - return new_size - - -def imrescale(img, - scale, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image while keeping the aspect ratio. - - Args: - img (ndarray): The input image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - ndarray: The rescaled image. - """ - h, w = img.shape[:2] - new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) - rescaled_img = imresize( - img, new_size, interpolation=interpolation, backend=backend) - if return_scale: - return rescaled_img, scale_factor - else: - return rescaled_img - - -def imflip(img, direction='horizontal'): - """Flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image. - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return np.flip(img, axis=1) - elif direction == 'vertical': - return np.flip(img, axis=0) - else: - return np.flip(img, axis=(0, 1)) - - -def imflip_(img, direction='horizontal'): - """Inplace flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image (inplace). - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return cv2.flip(img, 1, img) - elif direction == 'vertical': - return cv2.flip(img, 0, img) - else: - return cv2.flip(img, -1, img) - - -def imrotate(img, - angle, - center=None, - scale=1.0, - border_value=0, - interpolation='bilinear', - auto_bound=False): - """Rotate an image. - - Args: - img (ndarray): Image to be rotated. - angle (float): Rotation angle in degrees, positive values mean - clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. - scale (float): Isotropic scale factor. - border_value (int): Border value. - interpolation (str): Same as :func:`resize`. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. - - Returns: - ndarray: The rotated image. - """ - if center is not None and auto_bound: - raise ValueError('`auto_bound` conflicts with `center`') - h, w = img.shape[:2] - if center is None: - center = ((w - 1) * 0.5, (h - 1) * 0.5) - assert isinstance(center, tuple) - - matrix = cv2.getRotationMatrix2D(center, -angle, scale) - if auto_bound: - cos = np.abs(matrix[0, 0]) - sin = np.abs(matrix[0, 1]) - new_w = h * sin + w * cos - new_h = h * cos + w * sin - matrix[0, 2] += (new_w - w) * 0.5 - matrix[1, 2] += (new_h - h) * 0.5 - w = int(np.round(new_w)) - h = int(np.round(new_h)) - rotated = cv2.warpAffine( - img, - matrix, (w, h), - flags=cv2_interp_codes[interpolation], - borderValue=border_value) - return rotated - - -def bbox_clip(bboxes, img_shape): - """Clip bboxes to fit the image shape. - - Args: - bboxes (ndarray): Shape (..., 4*k) - img_shape (tuple[int]): (height, width) of the image. - - Returns: - ndarray: Clipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) - cmin[0::2] = img_shape[1] - 1 - cmin[1::2] = img_shape[0] - 1 - clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) - return clipped_bboxes - - -def bbox_scaling(bboxes, scale, clip_shape=None): - """Scaling bboxes w.r.t the box center. - - Args: - bboxes (ndarray): Shape(..., 4). - scale (float): Scaling factor. - clip_shape (tuple[int], optional): If specified, bboxes that exceed the - boundary will be clipped according to the given shape (h, w). - - Returns: - ndarray: Scaled bboxes. - """ - if float(scale) == 1.0: - scaled_bboxes = bboxes.copy() - else: - w = bboxes[..., 2] - bboxes[..., 0] + 1 - h = bboxes[..., 3] - bboxes[..., 1] + 1 - dw = (w * (scale - 1)) * 0.5 - dh = (h * (scale - 1)) * 0.5 - scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) - if clip_shape is not None: - return bbox_clip(scaled_bboxes, clip_shape) - else: - return scaled_bboxes - - -def imcrop(img, bboxes, scale=1.0, pad_fill=None): - """Crop image patches. - - 3 steps: scale the bboxes -> clip bboxes -> crop and pad. - - Args: - img (ndarray): Image to be cropped. - bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. - scale (float, optional): Scale ratio of bboxes, the default value - 1.0 means no padding. - pad_fill (Number | list[Number]): Value to be filled for padding. - Default: None, which means no padding. - - Returns: - list[ndarray] | ndarray: The cropped image patches. - """ - chn = 1 if img.ndim == 2 else img.shape[2] - if pad_fill is not None: - if isinstance(pad_fill, (int, float)): - pad_fill = [pad_fill for _ in range(chn)] - assert len(pad_fill) == chn - - _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes - scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) - clipped_bbox = bbox_clip(scaled_bboxes, img.shape) - - patches = [] - for i in range(clipped_bbox.shape[0]): - x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) - if pad_fill is None: - patch = img[y1:y2 + 1, x1:x2 + 1, ...] - else: - _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) - if chn == 1: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) - else: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) - patch = np.array( - pad_fill, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - x_start = 0 if _x1 >= 0 else -_x1 - y_start = 0 if _y1 >= 0 else -_y1 - w = x2 - x1 + 1 - h = y2 - y1 + 1 - patch[y_start:y_start + h, x_start:x_start + w, - ...] = img[y1:y1 + h, x1:x1 + w, ...] - patches.append(patch) - - if bboxes.ndim == 1: - return patches[0] - else: - return patches - - -def impad(img, - *, - shape=None, - padding=None, - pad_val=0, - padding_mode='constant'): - """Pad the given image to a certain shape or pad on all sides with - specified padding mode and padding value. - - Args: - img (ndarray): Image to be padded. - shape (tuple[int]): Expected padding shape (h, w). Default: None. - padding (int or tuple[int]): Padding on each border. If a single int is - provided this is used to pad all borders. If tuple of length 2 is - provided this is the padding on left/right and top/bottom - respectively. If a tuple of length 4 is provided this is the - padding for the left, top, right and bottom borders respectively. - Default: None. Note that `shape` and `padding` can not be both - set. - pad_val (Number | Sequence[Number]): Values to be filled in padding - areas when padding_mode is 'constant'. Default: 0. - padding_mode (str): Type of padding. Should be: constant, edge, - reflect or symmetric. Default: constant. - - constant: pads with a constant value, this value is specified - with pad_val. - - edge: pads with the last value at the edge of the image. - - reflect: pads with reflection of image without repeating the last - value on the edge. For example, padding [1, 2, 3, 4] with 2 - elements on both sides in reflect mode will result in - [3, 2, 1, 2, 3, 4, 3, 2]. - - symmetric: pads with reflection of image repeating the last value - on the edge. For example, padding [1, 2, 3, 4] with 2 elements on - both sides in symmetric mode will result in - [2, 1, 1, 2, 3, 4, 4, 3] - - Returns: - ndarray: The padded image. - """ - - assert (shape is not None) ^ (padding is not None) - if shape is not None: - width = max(shape[1] - img.shape[1], 0) - height = max(shape[0] - img.shape[0], 0) - padding = (0, 0, width, height) - - # check pad_val - if isinstance(pad_val, tuple): - assert len(pad_val) == img.shape[-1] - elif not isinstance(pad_val, numbers.Number): - raise TypeError('pad_val must be a int or a tuple. ' - f'But received {type(pad_val)}') - - # check padding - if isinstance(padding, tuple) and len(padding) in [2, 4]: - if len(padding) == 2: - padding = (padding[0], padding[1], padding[0], padding[1]) - elif isinstance(padding, numbers.Number): - padding = (padding, padding, padding, padding) - else: - raise ValueError('Padding must be a int or a 2, or 4 element tuple.' - f'But received {padding}') - - # check padding mode - assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] - - border_type = { - 'constant': cv2.BORDER_CONSTANT, - 'edge': cv2.BORDER_REPLICATE, - 'reflect': cv2.BORDER_REFLECT_101, - 'symmetric': cv2.BORDER_REFLECT - } - img = cv2.copyMakeBorder( - img, - padding[1], - padding[3], - padding[0], - padding[2], - border_type[padding_mode], - value=pad_val) - - return img - - -def impad_to_multiple(img, divisor, pad_val=0): - """Pad an image to ensure each edge to be multiple to some number. - - Args: - img (ndarray): Image to be padded. - divisor (int): Padded image edges will be multiple to divisor. - pad_val (Number | Sequence[Number]): Same as :func:`impad`. - - Returns: - ndarray: The padded image. - """ - pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor - pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor - return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) - - -def cutout(img, shape, pad_val=0): - """Randomly cut out a rectangle from the original img. - - Args: - img (ndarray): Image to be cutout. - shape (int | tuple[int]): Expected cutout shape (h, w). If given as a - int, the value will be used for both h and w. - pad_val (int | float | tuple[int | float]): Values to be filled in the - cut area. Defaults to 0. - - Returns: - ndarray: The cutout image. - """ - - channels = 1 if img.ndim == 2 else img.shape[2] - if isinstance(shape, int): - cut_h, cut_w = shape, shape - else: - assert isinstance(shape, tuple) and len(shape) == 2, \ - f'shape must be a int or a tuple with length 2, but got type ' \ - f'{type(shape)} instead.' - cut_h, cut_w = shape - if isinstance(pad_val, (int, float)): - pad_val = tuple([pad_val] * channels) - elif isinstance(pad_val, tuple): - assert len(pad_val) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(pad_val), channels) - else: - raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') - - img_h, img_w = img.shape[:2] - y0 = np.random.uniform(img_h) - x0 = np.random.uniform(img_w) - - y1 = int(max(0, y0 - cut_h / 2.)) - x1 = int(max(0, x0 - cut_w / 2.)) - y2 = min(img_h, y1 + cut_h) - x2 = min(img_w, x1 + cut_w) - - if img.ndim == 2: - patch_shape = (y2 - y1, x2 - x1) - else: - patch_shape = (y2 - y1, x2 - x1, channels) - - img_cutout = img.copy() - patch = np.array( - pad_val, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - img_cutout[y1:y2, x1:x2, ...] = patch - - return img_cutout - - -def _get_shear_matrix(magnitude, direction='horizontal'): - """Generate the shear matrix for transformation. - - Args: - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - - Returns: - ndarray: The shear matrix with dtype float32. - """ - if direction == 'horizontal': - shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) - elif direction == 'vertical': - shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) - return shear_matrix - - -def imshear(img, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear an image. - - Args: - img (ndarray): Image to be sheared with format (h, w) - or (h, w, c). - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The sheared image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`') - shear_matrix = _get_shear_matrix(magnitude, direction) - sheared = cv2.warpAffine( - img, - shear_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. shearing masks whose channels large - # than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return sheared - - -def _get_translate_matrix(offset, direction='horizontal'): - """Generate the translate matrix. - - Args: - offset (int | float): The offset used for translate. - direction (str): The translate direction, either - "horizontal" or "vertical". - - Returns: - ndarray: The translate matrix with dtype float32. - """ - if direction == 'horizontal': - translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) - elif direction == 'vertical': - translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) - return translate_matrix - - -def imtranslate(img, - offset, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Translate an image. - - Args: - img (ndarray): Image to be translated with format - (h, w) or (h, w, c). - offset (int | float): The offset used for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The translated image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`.') - translate_matrix = _get_translate_matrix(offset, direction) - translated = cv2.warpAffine( - img, - translate_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. translating masks whose channels - # large than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return translated diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/io.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/io.py deleted file mode 100644 index ae81b561a..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/io.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os.path as osp -import warnings -from pathlib import Path - -import cv2 -import numpy as np -from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, - IMREAD_UNCHANGED) - -from mmcv.fileio import FileClient -from mmcv.utils import is_filepath, is_str - -try: - from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG -except ImportError: - TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None - -try: - from PIL import Image, ImageOps -except ImportError: - Image = None - -try: - import tifffile -except ImportError: - tifffile = None - -jpeg = None -supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] - -imread_flags = { - 'color': IMREAD_COLOR, - 'grayscale': IMREAD_GRAYSCALE, - 'unchanged': IMREAD_UNCHANGED, - 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, - 'grayscale_ignore_orientation': - IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE -} - -imread_backend = 'cv2' - - -def use_backend(backend): - """Select a backend for image decoding. - - Args: - backend (str): The image decoding backend type. Options are `cv2`, - `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) - and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` - file format. - """ - assert backend in supported_backends - global imread_backend - imread_backend = backend - if imread_backend == 'turbojpeg': - if TurboJPEG is None: - raise ImportError('`PyTurboJPEG` is not installed') - global jpeg - if jpeg is None: - jpeg = TurboJPEG() - elif imread_backend == 'pillow': - if Image is None: - raise ImportError('`Pillow` is not installed') - elif imread_backend == 'tifffile': - if tifffile is None: - raise ImportError('`tifffile` is not installed') - - -def _jpegflag(flag='color', channel_order='bgr'): - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'color': - if channel_order == 'bgr': - return TJPF_BGR - elif channel_order == 'rgb': - return TJCS_RGB - elif flag == 'grayscale': - return TJPF_GRAY - else: - raise ValueError('flag must be "color" or "grayscale"') - - -def _pillow2array(img, flag='color', channel_order='bgr'): - """Convert a pillow image to numpy array. - - Args: - img (:obj:`PIL.Image.Image`): The image loaded using PIL - flag (str): Flags specifying the color type of a loaded image, - candidates are 'color', 'grayscale' and 'unchanged'. - Default to 'color'. - channel_order (str): The channel order of the output image array, - candidates are 'bgr' and 'rgb'. Default to 'bgr'. - - Returns: - np.ndarray: The converted numpy array - """ - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'unchanged': - array = np.array(img) - if array.ndim >= 3 and array.shape[2] >= 3: # color image - array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR - else: - # Handle exif orientation tag - if flag in ['color', 'grayscale']: - img = ImageOps.exif_transpose(img) - # If the image mode is not 'RGB', convert it to 'RGB' first. - if img.mode != 'RGB': - if img.mode != 'LA': - # Most formats except 'LA' can be directly converted to RGB - img = img.convert('RGB') - else: - # When the mode is 'LA', the default conversion will fill in - # the canvas with black, which sometimes shadows black objects - # in the foreground. - # - # Therefore, a random color (124, 117, 104) is used for canvas - img_rgba = img.convert('RGBA') - img = Image.new('RGB', img_rgba.size, (124, 117, 104)) - img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha - if flag in ['color', 'color_ignore_orientation']: - array = np.array(img) - if channel_order != 'rgb': - array = array[:, :, ::-1] # RGB to BGR - elif flag in ['grayscale', 'grayscale_ignore_orientation']: - img = img.convert('L') - array = np.array(img) - else: - raise ValueError( - 'flag must be "color", "grayscale", "unchanged", ' - f'"color_ignore_orientation" or "grayscale_ignore_orientation"' - f' but got {flag}') - return array - - -def imread(img_or_path, - flag='color', - channel_order='bgr', - backend=None, - file_client_args=None): - """Read an image. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Args: - img_or_path (ndarray or str or Path): Either a numpy array or str or - pathlib.Path. If it is a numpy array (loaded image), then - it will be returned as is. - flag (str): Flags specifying the color type of a loaded image, - candidates are `color`, `grayscale`, `unchanged`, - `color_ignore_orientation` and `grayscale_ignore_orientation`. - By default, `cv2` and `pillow` backend would rotate the image - according to its EXIF info unless called with `unchanged` or - `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend - always ignore image's EXIF info regardless of the flag. - The `turbojpeg` backend only supports `color` and `grayscale`. - channel_order (str): Order of channel, candidates are `bgr` and `rgb`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. - If backend is None, the global imread_backend specified by - ``mmcv.use_backend()`` will be used. Default: None. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> import mmcv - >>> img_path = '/path/to/img.jpg' - >>> img = mmcv.imread(img_path) - >>> img = mmcv.imread(img_path, flag='color', channel_order='rgb', - ... backend='cv2') - >>> img = mmcv.imread(img_path, flag='color', channel_order='bgr', - ... backend='pillow') - >>> s3_img_path = 's3://bucket/img.jpg' - >>> # infer the file backend by the prefix s3 - >>> img = mmcv.imread(s3_img_path) - >>> # manually set the file backend petrel - >>> img = mmcv.imread(s3_img_path, file_client_args={ - ... 'backend': 'petrel'}) - >>> http_img_path = 'http://path/to/img.jpg' - >>> img = mmcv.imread(http_img_path) - >>> img = mmcv.imread(http_img_path, file_client_args={ - ... 'backend': 'http'}) - """ - - if isinstance(img_or_path, Path): - img_or_path = str(img_or_path) - - if isinstance(img_or_path, np.ndarray): - return img_or_path - elif is_str(img_or_path): - file_client = FileClient.infer_client(file_client_args, img_or_path) - img_bytes = file_client.get(img_or_path) - return imfrombytes(img_bytes, flag, channel_order, backend) - else: - raise TypeError('"img" must be a numpy array or a str or ' - 'a pathlib.Path object') - - -def imfrombytes(content, flag='color', channel_order='bgr', backend=None): - """Read an image from bytes. - - Args: - content (bytes): Image bytes got from files or other streams. - flag (str): Same as :func:`imread`. - channel_order (str): The channel order of the output, candidates - are 'bgr' and 'rgb'. Default to 'bgr'. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. If backend is - None, the global imread_backend specified by ``mmcv.use_backend()`` - will be used. Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> img_path = '/path/to/img.jpg' - >>> with open(img_path, 'rb') as f: - >>> img_buff = f.read() - >>> img = mmcv.imfrombytes(img_buff) - >>> img = mmcv.imfrombytes(img_buff, flag='color', channel_order='rgb') - >>> img = mmcv.imfrombytes(img_buff, backend='pillow') - >>> img = mmcv.imfrombytes(img_buff, backend='cv2') - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError( - f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow', 'tifffile'") - if backend == 'turbojpeg': - img = jpeg.decode(content, _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - with io.BytesIO(content) as buff: - img = Image.open(buff) - img = _pillow2array(img, flag, channel_order) - return img - elif backend == 'tifffile': - with io.BytesIO(content) as buff: - img = tifffile.imread(buff) - return img - else: - img_np = np.frombuffer(content, np.uint8) - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imdecode(img_np, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - - -def imwrite(img, - file_path, - params=None, - auto_mkdir=None, - file_client_args=None): - """Write image to file. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Warning: - The parameter `auto_mkdir` will be deprecated in the future and every - file clients will make directory automatically. - - Args: - img (ndarray): Image array to be written. - file_path (str): Image file path. - params (None or list): Same as opencv :func:`imwrite` interface. - auto_mkdir (bool): If the parent folder of `file_path` does not exist, - whether to create it automatically. It will be deprecated. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - bool: Successful or not. - - Examples: - >>> # write to hard disk client - >>> ret = mmcv.imwrite(img, '/path/to/img.jpg') - >>> # infer the file backend by the prefix s3 - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg') - >>> # manually set the file backend petrel - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg', file_client_args={ - ... 'backend': 'petrel'}) - """ - assert is_filepath(file_path) - file_path = str(file_path) - if auto_mkdir is not None: - warnings.warn( - 'The parameter `auto_mkdir` will be deprecated in the future and ' - 'every file clients will make directory automatically.') - file_client = FileClient.infer_client(file_client_args, file_path) - img_ext = osp.splitext(file_path)[-1] - # Encode image according to image suffix. - # For example, if image path is '/path/your/img.jpg', the encode - # format is '.jpg'. - flag, img_buff = cv2.imencode(img_ext, img, params) - file_client.put(img_buff.tobytes(), file_path) - return flag diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/misc.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/misc.py deleted file mode 100644 index 43934a689..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/misc.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - -import mmcv - -try: - import torch -except ImportError: - torch = None - - -def tensor2imgs(tensor, mean=None, std=None, to_rgb=True): - """Convert tensor to 3-channel images or 1-channel gray images. - - Args: - tensor (torch.Tensor): Tensor that contains multiple images, shape ( - N, C, H, W). :math:`C` can be either 3 or 1. - mean (tuple[float], optional): Mean of images. If None, - (0, 0, 0) will be used for tensor with 3-channel, - while (0, ) for tensor with 1-channel. Defaults to None. - std (tuple[float], optional): Standard deviation of images. If None, - (1, 1, 1) will be used for tensor with 3-channel, - while (1, ) for tensor with 1-channel. Defaults to None. - to_rgb (bool, optional): Whether the tensor was converted to RGB - format in the first place. If so, convert it back to BGR. - For the tensor with 1 channel, it must be False. Defaults to True. - - Returns: - list[np.ndarray]: A list that contains multiple images. - """ - - if torch is None: - raise RuntimeError('pytorch is not installed') - assert torch.is_tensor(tensor) and tensor.ndim == 4 - channels = tensor.size(1) - assert channels in [1, 3] - if mean is None: - mean = (0, ) * channels - if std is None: - std = (1, ) * channels - assert (channels == len(mean) == len(std) == 3) or \ - (channels == len(mean) == len(std) == 1 and not to_rgb) - - num_imgs = tensor.size(0) - mean = np.array(mean, dtype=np.float32) - std = np.array(std, dtype=np.float32) - imgs = [] - for img_id in range(num_imgs): - img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) - img = mmcv.imdenormalize( - img, mean, std, to_bgr=to_rgb).astype(np.uint8) - imgs.append(np.ascontiguousarray(img)) - return imgs diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/photometric.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/photometric.py deleted file mode 100644 index 5085d0120..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/image/photometric.py +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - -from ..utils import is_tuple_of -from .colorspace import bgr2gray, gray2bgr - - -def imnormalize(img, mean, std, to_rgb=True): - """Normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - img = img.copy().astype(np.float32) - return imnormalize_(img, mean, std, to_rgb) - - -def imnormalize_(img, mean, std, to_rgb=True): - """Inplace normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - # cv2 inplace normalization does not accept uint8 - assert img.dtype != np.uint8 - mean = np.float64(mean.reshape(1, -1)) - stdinv = 1 / np.float64(std.reshape(1, -1)) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - cv2.subtract(img, mean, img) # inplace - cv2.multiply(img, stdinv, img) # inplace - return img - - -def imdenormalize(img, mean, std, to_bgr=True): - assert img.dtype != np.uint8 - mean = mean.reshape(1, -1).astype(np.float64) - std = std.reshape(1, -1).astype(np.float64) - img = cv2.multiply(img, std) # make a copy - cv2.add(img, mean, img) # inplace - if to_bgr: - cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace - return img - - -def iminvert(img): - """Invert (negate) an image. - - Args: - img (ndarray): Image to be inverted. - - Returns: - ndarray: The inverted image. - """ - return np.full_like(img, 255) - img - - -def solarize(img, thr=128): - """Solarize an image (invert all pixel values above a threshold) - - Args: - img (ndarray): Image to be solarized. - thr (int): Threshold for solarizing (0 - 255). - - Returns: - ndarray: The solarized image. - """ - img = np.where(img < thr, img, 255 - img) - return img - - -def posterize(img, bits): - """Posterize an image (reduce the number of bits for each color channel) - - Args: - img (ndarray): Image to be posterized. - bits (int): Number of bits (1 to 8) to use for posterizing. - - Returns: - ndarray: The posterized image. - """ - shift = 8 - bits - img = np.left_shift(np.right_shift(img, shift), shift) - return img - - -def adjust_color(img, alpha=1, beta=None, gamma=0): - r"""It blends the source image and its gray image: - - .. math:: - output = img * alpha + gray\_img * beta + gamma - - Args: - img (ndarray): The input source image. - alpha (int | float): Weight for the source image. Default 1. - beta (int | float): Weight for the converted gray image. - If None, it's assigned the value (1 - `alpha`). - gamma (int | float): Scalar added to each sum. - Same as :func:`cv2.addWeighted`. Default 0. - - Returns: - ndarray: Colored image which has the same size and dtype as input. - """ - gray_img = bgr2gray(img) - gray_img = np.tile(gray_img[..., None], [1, 1, 3]) - if beta is None: - beta = 1 - alpha - colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) - if not colored_img.dtype == np.uint8: - # Note when the dtype of `img` is not the default `np.uint8` - # (e.g. np.float32), the value in `colored_img` got from cv2 - # is not guaranteed to be in range [0, 255], so here clip - # is needed. - colored_img = np.clip(colored_img, 0, 255) - return colored_img - - -def imequalize(img): - """Equalize the image histogram. - - This function applies a non-linear mapping to the input image, - in order to create a uniform distribution of grayscale values - in the output image. - - Args: - img (ndarray): Image to be equalized. - - Returns: - ndarray: The equalized image. - """ - - def _scale_channel(im, c): - """Scale the data in the corresponding channel.""" - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # For computing the step, filter out the nonzeros. - nonzero_histo = histo[histo > 0] - step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 - if not step: - lut = np.array(range(256)) - else: - # Compute the cumulative sum, shifted by step // 2 - # and then normalized by step. - lut = (np.cumsum(histo) + (step // 2)) // step - # Shift lut, prepending with 0. - lut = np.concatenate([[0], lut[:-1]], 0) - # handle potential integer overflow - lut[lut > 255] = 255 - # If step is zero, return the original image. - # Otherwise, index from lut. - return np.where(np.equal(step, 0), im, lut[im]) - - # Scales each channel independently and then stacks - # the result. - s1 = _scale_channel(img, 0) - s2 = _scale_channel(img, 1) - s3 = _scale_channel(img, 2) - equalized_img = np.stack([s1, s2, s3], axis=-1) - return equalized_img.astype(img.dtype) - - -def adjust_brightness(img, factor=1.): - """Adjust image brightness. - - This function controls the brightness of an image. An - enhancement factor of 0.0 gives a black image. - A factor of 1.0 gives the original image. This function - blends the source image and the degenerated black image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be brightened. - factor (float): A value controls the enhancement. - Factor 1.0 returns the original image, lower - factors mean less color (brightness, contrast, - etc), and higher values more. Default 1. - - Returns: - ndarray: The brightened image. - """ - degenerated = np.zeros_like(img) - # Note manually convert the dtype to np.float32, to - # achieve as close results as PIL.ImageEnhance.Brightness. - # Set beta=1-factor, and gamma=0 - brightened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - brightened_img = np.clip(brightened_img, 0, 255) - return brightened_img.astype(img.dtype) - - -def adjust_contrast(img, factor=1.): - """Adjust image contrast. - - This function controls the contrast of an image. An - enhancement factor of 0.0 gives a solid grey - image. A factor of 1.0 gives the original image. It - blends the source image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be contrasted. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - - Returns: - ndarray: The contrasted image. - """ - gray_img = bgr2gray(img) - hist = np.histogram(gray_img, 256, (0, 255))[0] - mean = round(np.sum(gray_img) / np.sum(hist)) - degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) - degenerated = gray2bgr(degenerated) - contrasted_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - contrasted_img = np.clip(contrasted_img, 0, 255) - return contrasted_img.astype(img.dtype) - - -def auto_contrast(img, cutoff=0): - """Auto adjust image contrast. - - This function maximize (normalize) image contrast by first removing cutoff - percent of the lightest and darkest pixels from the histogram and remapping - the image so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - Args: - img (ndarray): Image to be contrasted. BGR order. - cutoff (int | float | tuple): The cutoff percent of the lightest and - darkest pixels to be removed. If given as tuple, it shall be - (low, high). Otherwise, the single value will be used for both. - Defaults to 0. - - Returns: - ndarray: The contrasted image. - """ - - def _auto_contrast_channel(im, c, cutoff): - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # Remove cut-off percent pixels from histo - histo_sum = np.cumsum(histo) - cut_low = histo_sum[-1] * cutoff[0] // 100 - cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 - histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low - histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) - - # Compute mapping - low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] - # If all the values have been cut off, return the origin img - if low >= high: - return im - scale = 255.0 / (high - low) - offset = -low * scale - lut = np.array(range(256)) - lut = lut * scale + offset - lut = np.clip(lut, 0, 255) - return lut[im] - - if isinstance(cutoff, (int, float)): - cutoff = (cutoff, cutoff) - else: - assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ - f'float or tuple, but got {type(cutoff)} instead.' - # Auto adjusts contrast for each channel independently and then stacks - # the result. - s1 = _auto_contrast_channel(img, 0, cutoff) - s2 = _auto_contrast_channel(img, 1, cutoff) - s3 = _auto_contrast_channel(img, 2, cutoff) - contrasted_img = np.stack([s1, s2, s3], axis=-1) - return contrasted_img.astype(img.dtype) - - -def adjust_sharpness(img, factor=1., kernel=None): - """Adjust image sharpness. - - This function controls the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image. A - factor of 1.0 gives the original image. And a factor - of 2.0 gives a sharpened image. It blends the source - image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be sharpened. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - kernel (np.ndarray, optional): Filter kernel to be applied on the img - to obtain the degenerated img. Defaults to None. - - Note: - No value sanity check is enforced on the kernel set by users. So with - an inappropriate kernel, the ``adjust_sharpness`` may fail to perform - the function its name indicates but end up performing whatever - transform determined by the kernel. - - Returns: - ndarray: The sharpened image. - """ - - if kernel is None: - # adopted from PIL.ImageFilter.SMOOTH - kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 - assert isinstance(kernel, np.ndarray), \ - f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' - assert kernel.ndim == 2, \ - f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' - - degenerated = cv2.filter2D(img, -1, kernel) - sharpened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - sharpened_img = np.clip(sharpened_img, 0, 255) - return sharpened_img.astype(img.dtype) - - -def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): - """AlexNet-style PCA jitter. - - This data augmentation is proposed in `ImageNet Classification with Deep - Convolutional Neural Networks - `_. - - Args: - img (ndarray): Image to be adjusted lighting. BGR order. - eigval (ndarray): the eigenvalue of the convariance matrix of pixel - values, respectively. - eigvec (ndarray): the eigenvector of the convariance matrix of pixel - values, respectively. - alphastd (float): The standard deviation for distribution of alpha. - Defaults to 0.1 - to_rgb (bool): Whether to convert img to rgb. - - Returns: - ndarray: The adjusted image. - """ - assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ - f'eigval and eigvec should both be of type np.ndarray, got ' \ - f'{type(eigval)} and {type(eigvec)} instead.' - - assert eigval.ndim == 1 and eigvec.ndim == 2 - assert eigvec.shape == (3, eigval.shape[0]) - n_eigval = eigval.shape[0] - assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ - f'got {type(alphastd)} instead.' - - img = img.copy().astype(np.float32) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - - alpha = np.random.normal(0, alphastd, n_eigval) - alter = eigvec \ - * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ - * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) - alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) - img_adjusted = img + alter - return img_adjusted - - -def lut_transform(img, lut_table): - """Transform array by look-up table. - - The function lut_transform fills the output array with values from the - look-up table. Indices of the entries are taken from the input array. - - Args: - img (ndarray): Image to be transformed. - lut_table (ndarray): look-up table of 256 elements; in case of - multi-channel input array, the table should either have a single - channel (in this case the same table is used for all channels) or - the same number of channels as in the input array. - - Returns: - ndarray: The transformed image. - """ - assert isinstance(img, np.ndarray) - assert 0 <= np.min(img) and np.max(img) <= 255 - assert isinstance(lut_table, np.ndarray) - assert lut_table.shape == (256, ) - - return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) - - -def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - img (ndarray): Image to be processed. - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - - Returns: - ndarray: The processed image. - """ - assert isinstance(img, np.ndarray) - assert img.ndim == 2 - assert isinstance(clip_limit, (float, int)) - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - - clahe = cv2.createCLAHE(clip_limit, tile_grid_size) - return clahe.apply(np.array(img, dtype=np.uint8)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/deprecated.json b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/deprecated.json deleted file mode 100644 index 25cf6f28c..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/deprecated.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "resnet50_caffe": "detectron/resnet50_caffe", - "resnet50_caffe_bgr": "detectron2/resnet50_caffe_bgr", - "resnet101_caffe": "detectron/resnet101_caffe", - "resnet101_caffe_bgr": "detectron2/resnet101_caffe_bgr" -} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/mmcls.json b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/mmcls.json deleted file mode 100644 index c073a41d0..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/mmcls.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "vgg11": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_batch256_imagenet_20210208-4271cd6c.pth", - "vgg13": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_batch256_imagenet_20210208-4d1d6080.pth", - "vgg16": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_batch256_imagenet_20210208-db26f1a5.pth", - "vgg19": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_batch256_imagenet_20210208-e6920e4a.pth", - "vgg11_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_bn_batch256_imagenet_20210207-f244902c.pth", - "vgg13_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_bn_batch256_imagenet_20210207-1a8b7864.pth", - "vgg16_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_bn_batch256_imagenet_20210208-7e55cd29.pth", - "vgg19_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_bn_batch256_imagenet_20210208-da620c4f.pth", - "resnet18": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet18_8xb32_in1k_20210831-fbbb1da6.pth", - "resnet34": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet34_8xb32_in1k_20210831-f257d4e6.pth", - "resnet50": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb32_in1k_20210831-ea4938fc.pth", - "resnet101": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet101_8xb32_in1k_20210831-539c63f8.pth", - "resnet152": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet152_8xb32_in1k_20210901-4d7582fa.pth", - "resnet50_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d50_b32x8_imagenet_20210531-db14775a.pth", - "resnet101_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d101_b32x8_imagenet_20210531-6e13bcd3.pth", - "resnet152_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d152_b32x8_imagenet_20210531-278cf22a.pth", - "resnext50_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext50_32x4d_b32x8_imagenet_20210429-56066e27.pth", - "resnext101_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x4d_b32x8_imagenet_20210506-e0fa3dd5.pth", - "resnext101_32x8d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x8d_b32x8_imagenet_20210506-23a247d5.pth", - "resnext152_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext152_32x4d_b32x8_imagenet_20210524-927787be.pth", - "se-resnet50": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet50_batch256_imagenet_20200804-ae206104.pth", - "se-resnet101": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet101_batch256_imagenet_20200804-ba5b51d4.pth", - "resnest50": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest50_imagenet_converted-1ebf0afe.pth", - "resnest101": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest101_imagenet_converted-032caa52.pth", - "resnest200": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest200_imagenet_converted-581a60f2.pth", - "resnest269": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest269_imagenet_converted-59930960.pth", - "shufflenet_v1": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v1/shufflenet_v1_batch1024_imagenet_20200804-5d6cec73.pth", - "shufflenet_v2": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v2/shufflenet_v2_batch1024_imagenet_20200812-5bf4721e.pth", - "mobilenet_v2": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v2/mobilenet_v2_batch256_imagenet_20200708-3b2dc3af.pth", - "mobilenet_v3_small": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_small-8427ecf0.pth", - "mobilenet_v3_large": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_large-3ea3c186.pth", - "repvgg_A0": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A0_3rdparty_4xb64-coslr-120e_in1k_20210909-883ab98c.pth", - "repvgg_A1": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A1_3rdparty_4xb64-coslr-120e_in1k_20210909-24003a24.pth", - "repvgg_A2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A2_3rdparty_4xb64-coslr-120e_in1k_20210909-97d7695a.pth", - "repvgg_B0": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B0_3rdparty_4xb64-coslr-120e_in1k_20210909-446375f4.pth", - "repvgg_B1": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1_3rdparty_4xb64-coslr-120e_in1k_20210909-750cdf67.pth", - "repvgg_B1g2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1g2_3rdparty_4xb64-coslr-120e_in1k_20210909-344f6422.pth", - "repvgg_B1g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1g4_3rdparty_4xb64-coslr-120e_in1k_20210909-d4c1a642.pth", - "repvgg_B2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B2_3rdparty_4xb64-coslr-120e_in1k_20210909-bd6b937c.pth", - "repvgg_B2g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B2g4_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-7b7955f0.pth", - "repvgg_B3": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B3_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-dda968bf.pth", - "repvgg_B3g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B3g4_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-4e54846a.pth", - "repvgg_D2se": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-D2se_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-cf3139b7.pth", - "res2net101_w26": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net101-w26-s4_3rdparty_8xb32_in1k_20210927-870b6c36.pth", - "res2net50_w14": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net50-w14-s8_3rdparty_8xb32_in1k_20210927-bc967bf1.pth", - "res2net50_w26": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net50-w26-s8_3rdparty_8xb32_in1k_20210927-f547a94b.pth", - "swin_tiny": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_tiny_224_b16x64_300e_imagenet_20210616_090925-66df6be6.pth", - "swin_small": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_small_224_b16x64_300e_imagenet_20210615_110219-7f9d988b.pth", - "swin_base": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/convert/swin_base_patch4_window7_224_22kto1k-f967f799.pth", - "swin_large": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/convert/swin_large_patch4_window7_224_22kto1k-5f0996db.pth", - "t2t_vit_t_14": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-14_3rdparty_8xb64_in1k_20210928-b7c09b62.pth", - "t2t_vit_t_19": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-19_3rdparty_8xb64_in1k_20210928-7f1478d5.pth", - "t2t_vit_t_24": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-24_3rdparty_8xb64_in1k_20210928-fe95a61b.pth", - "tnt_small": "https://download.openmmlab.com/mmclassification/v0/tnt/tnt-small-p16_3rdparty_in1k_20210903-c56ee7df.pth", - "vit_base_p16": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-98e8652b.pth", - "vit_base_p32": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p32_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-9cea8599.pth", - "vit_large_p16": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-large-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-b20ba619.pth" -} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/open_mmlab.json b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/open_mmlab.json deleted file mode 100644 index 8311db4fe..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/open_mmlab.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "vgg16_caffe": "https://download.openmmlab.com/pretrain/third_party/vgg16_caffe-292e1171.pth", - "detectron/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_caffe-788b5fa3.pth", - "detectron2/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_msra-5891d200.pth", - "detectron/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_caffe-3ad79236.pth", - "detectron2/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_msra-6cc46731.pth", - "detectron2/resnext101_32x8d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x8d-1516f1aa.pth", - "resnext50_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext50-32x4d-0ab1a123.pth", - "resnext101_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d-a5af3160.pth", - "resnext101_64x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_64x4d-ee2c6f71.pth", - "contrib/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_thangvubk-ad1730dd.pth", - "detectron/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn-9186a21c.pth", - "detectron/resnet101_gn": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn-cac0ab98.pth", - "jhu/resnet50_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_ws-15beedd8.pth", - "jhu/resnet101_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn_ws-3e3c308c.pth", - "jhu/resnext50_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn_ws-0d87ac85.pth", - "jhu/resnext101_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn_ws-34ac1a9e.pth", - "jhu/resnext50_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn-c7e8b754.pth", - "jhu/resnext101_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn-ac3bb84e.pth", - "msra/hrnetv2_w18_small": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18_small-b5a04e21.pth", - "msra/hrnetv2_w18": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18-00eb2006.pth", - "msra/hrnetv2_w32": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w32-dc9eeb4f.pth", - "msra/hrnetv2_w40": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w40-ed0b031c.pth", - "msra/hrnetv2_w48": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w48-d2186c55.pth", - "bninception_caffe": "https://download.openmmlab.com/pretrain/third_party/bn_inception_caffe-ed2e8665.pth", - "kin400/i3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/i3d_r50_f32s2_k400-2c57e077.pth", - "kin400/nl3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/nl3d_r50_f32s2_k400-fa7e7caa.pth", - "res2net101_v1d_26w_4s": "https://download.openmmlab.com/pretrain/third_party/res2net101_v1d_26w_4s_mmdetv2-f0a600f9.pth", - "regnetx_400mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_400mf-a5b10d96.pth", - "regnetx_800mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_800mf-1f4be4c7.pth", - "regnetx_1.6gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_1.6gf-5791c176.pth", - "regnetx_3.2gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_3.2gf-c2599b0f.pth", - "regnetx_4.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_4.0gf-a88f671e.pth", - "regnetx_6.4gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_6.4gf-006af45d.pth", - "regnetx_8.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_8.0gf-3c68abe7.pth", - "regnetx_12gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_12gf-4c2a3350.pth", - "resnet18_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet18_v1c-b5776b93.pth", - "resnet50_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet50_v1c-2cccc1ad.pth", - "resnet101_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet101_v1c-e67eebb6.pth", - "mmedit/vgg16": "https://download.openmmlab.com/mmediting/third_party/vgg_state_dict.pth", - "mmedit/res34_en_nomixup": "https://download.openmmlab.com/mmediting/third_party/model_best_resnet34_En_nomixup.pth", - "mmedit/mobilenet_v2": "https://download.openmmlab.com/mmediting/third_party/mobilenet_v2.pth", - "contrib/mobilenet_v3_large": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_large-bc2c3fd3.pth", - "contrib/mobilenet_v3_small": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_small-47085aa1.pth", - "resnest50": "https://download.openmmlab.com/pretrain/third_party/resnest50_d2-7497a55b.pth", - "resnest101": "https://download.openmmlab.com/pretrain/third_party/resnest101_d2-f3b931b2.pth", - "resnest200": "https://download.openmmlab.com/pretrain/third_party/resnest200_d2-ca88e41f.pth", - "darknet53": "https://download.openmmlab.com/pretrain/third_party/darknet53-a628ea1b.pth", - "mmdet/mobilenet_v2": "https://download.openmmlab.com/mmdetection/v2.0/third_party/mobilenet_v2_batch256_imagenet-ff34753d.pth" -} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/torchvision_0.12.json b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/torchvision_0.12.json deleted file mode 100644 index 06defe674..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/model_zoo/torchvision_0.12.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "alexnet": "https://download.pytorch.org/models/alexnet-owt-7be5be79.pth", - "densenet121": "https://download.pytorch.org/models/densenet121-a639ec97.pth", - "densenet169": "https://download.pytorch.org/models/densenet169-b2777c0a.pth", - "densenet201": "https://download.pytorch.org/models/densenet201-c1103571.pth", - "densenet161": "https://download.pytorch.org/models/densenet161-8d451a50.pth", - "efficientnet_b0": "https://download.pytorch.org/models/efficientnet_b0_rwightman-3dd342df.pth", - "efficientnet_b1": "https://download.pytorch.org/models/efficientnet_b1_rwightman-533bc792.pth", - "efficientnet_b2": "https://download.pytorch.org/models/efficientnet_b2_rwightman-bcdf34b7.pth", - "efficientnet_b3": "https://download.pytorch.org/models/efficientnet_b3_rwightman-cf984f9c.pth", - "efficientnet_b4": "https://download.pytorch.org/models/efficientnet_b4_rwightman-7eb33cd5.pth", - "efficientnet_b5": "https://download.pytorch.org/models/efficientnet_b5_lukemelas-b6417697.pth", - "efficientnet_b6": "https://download.pytorch.org/models/efficientnet_b6_lukemelas-c76e70fd.pth", - "efficientnet_b7": "https://download.pytorch.org/models/efficientnet_b7_lukemelas-dcc49843.pth", - "googlenet": "https://download.pytorch.org/models/googlenet-1378be20.pth", - "inception_v3_google": "https://download.pytorch.org/models/inception_v3_google-0cc3c7bd.pth", - "mobilenet_v2": "https://download.pytorch.org/models/mobilenet_v2-b0353104.pth", - "mobilenet_v3_large": "https://download.pytorch.org/models/mobilenet_v3_large-8738ca79.pth", - "mobilenet_v3_small": "https://download.pytorch.org/models/mobilenet_v3_small-047dcff4.pth", - "regnet_y_400mf": "https://download.pytorch.org/models/regnet_y_400mf-c65dace8.pth", - "regnet_y_800mf": "https://download.pytorch.org/models/regnet_y_800mf-1b27b58c.pth", - "regnet_y_1_6gf": "https://download.pytorch.org/models/regnet_y_1_6gf-b11a554e.pth", - "regnet_y_3_2gf": "https://download.pytorch.org/models/regnet_y_3_2gf-b5a9779c.pth", - "regnet_y_8gf": "https://download.pytorch.org/models/regnet_y_8gf-d0d0e4a8.pth", - "regnet_y_16gf": "https://download.pytorch.org/models/regnet_y_16gf-9e6ed7dd.pth", - "regnet_y_32gf": "https://download.pytorch.org/models/regnet_y_32gf-4dee3f7a.pth", - "regnet_x_400mf": "https://download.pytorch.org/models/regnet_x_400mf-adf1edd5.pth", - "regnet_x_800mf": "https://download.pytorch.org/models/regnet_x_800mf-ad17e45c.pth", - "regnet_x_1_6gf": "https://download.pytorch.org/models/regnet_x_1_6gf-e3633e7f.pth", - "regnet_x_3_2gf": "https://download.pytorch.org/models/regnet_x_3_2gf-f342aeae.pth", - "regnet_x_8gf": "https://download.pytorch.org/models/regnet_x_8gf-03ceed89.pth", - "regnet_x_16gf": "https://download.pytorch.org/models/regnet_x_16gf-2007eb11.pth", - "regnet_x_32gf": "https://download.pytorch.org/models/regnet_x_32gf-9d47f8d0.pth", - "resnet18": "https://download.pytorch.org/models/resnet18-f37072fd.pth", - "resnet34": "https://download.pytorch.org/models/resnet34-b627a593.pth", - "resnet50": "https://download.pytorch.org/models/resnet50-0676ba61.pth", - "resnet101": "https://download.pytorch.org/models/resnet101-63fe2227.pth", - "resnet152": "https://download.pytorch.org/models/resnet152-394f9c45.pth", - "resnext50_32x4d": "https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth", - "resnext101_32x8d": "https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth", - "wide_resnet50_2": "https://download.pytorch.org/models/wide_resnet50_2-95faca4d.pth", - "wide_resnet101_2": "https://download.pytorch.org/models/wide_resnet101_2-32ee1156.pth", - "shufflenetv2_x0.5": "https://download.pytorch.org/models/shufflenetv2_x0.5-f707e7126e.pth", - "shufflenetv2_x1.0": "https://download.pytorch.org/models/shufflenetv2_x1-5666bf0f80.pth", - "shufflenetv2_x1.5": null, - "shufflenetv2_x2.0": null, - "squeezenet1_0": "https://download.pytorch.org/models/squeezenet1_0-b66bff10.pth", - "squeezenet1_1": "https://download.pytorch.org/models/squeezenet1_1-b8a52dc0.pth", - "vgg11": "https://download.pytorch.org/models/vgg11-8a719046.pth", - "vgg13": "https://download.pytorch.org/models/vgg13-19584684.pth", - "vgg16": "https://download.pytorch.org/models/vgg16-397923af.pth", - "vgg19": "https://download.pytorch.org/models/vgg19-dcbb9e9d.pth", - "vgg11_bn": "https://download.pytorch.org/models/vgg11_bn-6002323d.pth", - "vgg13_bn": "https://download.pytorch.org/models/vgg13_bn-abd245e5.pth", - "vgg16_bn": "https://download.pytorch.org/models/vgg16_bn-6c64b313.pth", - "vgg19_bn": "https://download.pytorch.org/models/vgg19_bn-c79401a0.pth" -} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/__init__.py deleted file mode 100644 index a12b79cef..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .deprecated_wrappers import Conv2d_deprecated as Conv2d -from .deprecated_wrappers import ConvTranspose2d_deprecated as ConvTranspose2d -from .deprecated_wrappers import Linear_deprecated as Linear -from .deprecated_wrappers import MaxPool2d_deprecated as MaxPool2d -from .focal_loss import (SigmoidFocalLoss, SoftmaxFocalLoss, - sigmoid_focal_loss, softmax_focal_loss) -from .sync_bn import SyncBatchNorm -from .cc_attention import CrissCrossAttention -from .point_sample import * -from .psa_mask import PSAMask, PSAMaskFunction -from .info import * \ No newline at end of file diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/cc_attention.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/cc_attention.py deleted file mode 100644 index 3fd83fcb9..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/cc_attention.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.cnn import PLUGIN_LAYERS, Scale - - -def NEG_INF_DIAG(n, device): - """Returns a diagonal matrix of size [n, n]. - - The diagonal are all "-inf". This is for avoiding calculating the - overlapped element in the Criss-Cross twice. - """ - return torch.diag(torch.tensor(float('-inf')).to(device).repeat(n), 0) - - -@PLUGIN_LAYERS.register_module() -class CrissCrossAttention(nn.Module): - """Criss-Cross Attention Module. - - .. note:: - Before v1.3.13, we use a CUDA op. Since v1.3.13, we switch - to a pure PyTorch and equivalent implementation. For more - details, please refer to https://github.com/open-mmlab/mmcv/pull/1201. - - Speed comparison for one forward pass - - - Input size: [2,512,97,97] - - Device: 1 NVIDIA GeForce RTX 2080 Ti - - +-----------------------+---------------+------------+---------------+ - | |PyTorch version|CUDA version|Relative speed | - +=======================+===============+============+===============+ - |with torch.no_grad() |0.00554402 s |0.0299619 s |5.4x | - +-----------------------+---------------+------------+---------------+ - |no with torch.no_grad()|0.00562803 s |0.0301349 s |5.4x | - +-----------------------+---------------+------------+---------------+ - - Args: - in_channels (int): Channels of the input feature map. - """ - - def __init__(self, in_channels): - super().__init__() - self.query_conv = nn.Conv2d(in_channels, in_channels // 8, 1) - self.key_conv = nn.Conv2d(in_channels, in_channels // 8, 1) - self.value_conv = nn.Conv2d(in_channels, in_channels, 1) - self.gamma = Scale(0.) - self.in_channels = in_channels - - def forward(self, x): - """forward function of Criss-Cross Attention. - - Args: - x (torch.Tensor): Input feature with the shape of - (batch_size, in_channels, height, width). - - Returns: - torch.Tensor: Output of the layer, with the shape of - (batch_size, in_channels, height, width) - """ - B, C, H, W = x.size() - query = self.query_conv(x) - key = self.key_conv(x) - value = self.value_conv(x) - energy_H = torch.einsum('bchw,bciw->bwhi', query, key) + NEG_INF_DIAG( - H, query.device) - energy_H = energy_H.transpose(1, 2) - energy_W = torch.einsum('bchw,bchj->bhwj', query, key) - attn = F.softmax( - torch.cat([energy_H, energy_W], dim=-1), dim=-1) # [B,H,W,(H+W)] - out = torch.einsum('bciw,bhwi->bchw', value, attn[..., :H]) - out += torch.einsum('bchj,bhwj->bchw', value, attn[..., H:]) - - out = self.gamma(out) + x - out = out.contiguous() - - return out - - def __repr__(self): - s = self.__class__.__name__ - s += f'(in_channels={self.in_channels})' - return s diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/README.md b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/README.md deleted file mode 100644 index 3bc020040..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/README.md +++ /dev/null @@ -1,170 +0,0 @@ -# Code Structure of CUDA operators - -This folder contains all non-python code for MMCV custom ops. Please follow the same architecture if you want to add new ops. - -## Directories Tree - -```folder -. -├── common -│ ├── box_iou_rotated_utils.hpp -│ ├── parrots_cpp_helper.hpp -│ ├── parrots_cuda_helper.hpp -│ ├── pytorch_cpp_helper.hpp -│ ├── pytorch_cuda_helper.hpp -│ ├── pytorch_device_registry.hpp -│   └── cuda -│   ├── common_cuda_helper.hpp -│   ├── parrots_cudawarpfunction.cuh -│   ├── ... -│   └── ops_cuda_kernel.cuh -├── onnxruntime -│   ├── onnxruntime_register.h -│   ├── onnxruntime_session_options_config_keys.h -│   ├── ort_mmcv_utils.h -│   ├── ... -│   ├── onnx_ops.h -│   └── cpu -│ ├── onnxruntime_register.cpp -│      ├── ... -│      └── onnx_ops_impl.cpp -├── parrots -│   ├── ... -│   ├── ops.cpp -│   ├── ops_parrots.cpp -│   └── ops_pytorch.h -├── pytorch -│   ├── info.cpp -│   ├── pybind.cpp -│   ├── ... -│   ├── ops.cpp -│   ├── cuda -│   │   ├── ... -│   │   └── ops_cuda.cu -│   └── cpu -│      ├── ... -│      └── ops.cpp -└── tensorrt - ├── trt_cuda_helper.cuh - ├── trt_plugin_helper.hpp - ├── trt_plugin.hpp - ├── trt_serialize.hpp - ├── ... - ├── trt_ops.hpp - └── plugins -    ├── trt_cuda_helper.cu -    ├── trt_plugin.cpp -    ├── ... -    ├── trt_ops.cpp -    └── trt_ops_kernel.cu -``` - -## Components - -- `common`: This directory contains all tools and shared codes. - - `cuda`: The cuda kernels which can be shared by all backends. **HIP** kernel is also here since they have similar syntax. -- `onnxruntime`: **ONNX Runtime** support for custom ops. - - `cpu`: CPU implementation of supported ops. -- `parrots`: **Parrots** is a deep learning frame for model training and inference. Parrots custom ops are placed in this directory. -- `pytorch`: **PyTorch** custom ops are supported by binding C++ to Python with **pybind11**. The ops implementation and binding codes are placed in this directory. - - `cuda`: This directory contains cuda kernel launchers, which feed memory pointers of tensor to the cuda kernel in `common/cuda`. The launchers provide c++ interface of cuda implementation of corresponding custom ops. - - `cpu`: This directory contain cpu implementations of corresponding custom ops. -- `tensorrt`: **TensorRT** support for custom ops. - - `plugins`: This directory contains the implementation of the supported custom ops. Some ops might also use shared cuda kernel in `common/cuda`. - -## How to add new PyTorch ops? - -1. (Optional) Add shared kernel in `common` to support special hardware platform. - - ```c++ - // src/common/cuda/new_ops_cuda_kernel.cuh - - template - __global__ void new_ops_forward_cuda_kernel(const T* input, T* output, ...) { - // forward here - } - - ``` - - Add cuda kernel launcher in `pytorch/cuda`. - - ```c++ - // src/pytorch/cuda - #include - - void NewOpsForwardCUDAKernelLauncher(Tensor input, Tensor output, ...){ - // initialize - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - ... - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "new_ops_forward_cuda_kernel", ([&] { - new_ops_forward_cuda_kernel - <<>>( - input.data_ptr(), output.data_ptr(),...); - })); - AT_CUDA_CHECK(cudaGetLastError()); - } - ``` - -2. Register implementation for different devices. - - ```c++ - // src/pytorch/cuda/cudabind.cpp - ... - - Tensor new_ops_forward_cuda(Tensor input, Tensor output, ...){ - // implement cuda forward here - // use `NewOpsForwardCUDAKernelLauncher` here - } - // declare interface here. - Tensor new_ops_forward_impl(Tensor input, Tensor output, ...); - // register the implementation for given device (CUDA here). - REGISTER_DEVICE_IMPL(new_ops_forward_impl, CUDA, new_ops_forward_cuda); - ``` - -3. Add ops implementation in `pytorch` directory. Select different implementations according to device type. - - ```c++ - // src/pytorch/new_ops.cpp - Tensor new_ops_forward_impl(Tensor input, Tensor output, ...){ - // dispatch the implementation according to the device type of input. - DISPATCH_DEVICE_IMPL(new_ops_forward_impl, input, output, ...); - } - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...){ - return new_ops_forward_impl(input, output, ...); - } - ``` - -4. Binding the implementation in `pytorch/pybind.cpp` - - ```c++ - // src/pytorch/pybind.cpp - - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...); - - ... - - // bind with pybind11 - m.def("new_ops_forward", &new_ops_forward, "new_ops_forward", - py::arg("input"), py::arg("output"), ...); - - ... - - ``` - -5. Build MMCV again. Enjoy new ops in python - - ```python - from ..utils import ext_loader - ext_module = ext_loader.load_ext('_ext', ['new_ops_forward']) - - ... - - ext_module.new_ops_forward(input, output, ...) - - ``` diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp deleted file mode 100644 index e18036bac..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef COMMON_CUDA_HELPER -#define COMMON_CUDA_HELPER - -#include - -#define CUDA_1D_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) - -#define CUDA_2D_KERNEL_LOOP(i, n, j, m) \ - for (size_t i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) \ - for (size_t j = blockIdx.y * blockDim.y + threadIdx.y; j < (m); \ - j += blockDim.y * gridDim.y) - -#define CUDA_2D_KERNEL_BLOCK_LOOP(i, n, j, m) \ - for (size_t i = blockIdx.x; i < (n); i += gridDim.x) \ - for (size_t j = blockIdx.y; j < (m); j += gridDim.y) - -#define THREADS_PER_BLOCK 512 - -inline int GET_BLOCKS(const int N, const int num_threads = THREADS_PER_BLOCK) { - int optimal_block_num = (N + num_threads - 1) / num_threads; - int max_block_num = 4096; - return std::min(optimal_block_num, max_block_num); -} - -template -__device__ T bilinear_interpolate(const T* input, const int height, - const int width, T y, T x, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) return 0; - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - int y_low = (int)y; - int x_low = (int)x; - int y_high; - int x_high; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - // do bilinear interpolation - T v1 = input[y_low * width + x_low]; - T v2 = input[y_low * width + x_high]; - T v3 = input[y_high * width + x_low]; - T v4 = input[y_high * width + x_high]; - T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - return val; -} - -template -__device__ void bilinear_interpolate_gradient( - const int height, const int width, T y, T x, T& w1, T& w2, T& w3, T& w4, - int& x_low, int& x_high, int& y_low, int& y_high, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - w1 = w2 = w3 = w4 = 0.; - x_low = x_high = y_low = y_high = -1; - return; - } - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - y_low = (int)y; - x_low = (int)x; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - - // reference in forward - // T v1 = input[y_low * width + x_low]; - // T v2 = input[y_low * width + x_high]; - // T v3 = input[y_high * width + x_low]; - // T v4 = input[y_high * width + x_high]; - // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - return; -} -#endif // COMMON_CUDA_HELPER diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh deleted file mode 100644 index 5d946686b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef PSAMASK_CUDA_KERNEL_CUH -#define PSAMASK_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -// CUDA: grid stride looping -#ifndef CUDA_KERNEL_LOOP -#define CUDA_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) -#endif - -template -__global__ void psamask_collect_forward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* mask_data, T* buffer_data) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - buffer_data[(n * h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)) * - h_feature * w_feature + - h * w_feature + w] = mask_data - [((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + h) * - w_feature + - w]; - } - } - } -} - -template -__global__ void psamask_distribute_forward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* mask_data, T* buffer_data) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - buffer_data[(n * h_feature * w_feature + h * w_feature + w) * - h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)] = mask_data - [((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + h) * - w_feature + - w]; - } - } - } -} - -template -__global__ void psamask_collect_backward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* buffer_diff, T* mask_diff) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - mask_diff[((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + - h) * - w_feature + - w] = buffer_diff[(n * h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)) * - h_feature * w_feature + - h * w_feature + w]; - } - } - } -} - -template -__global__ void psamask_distribute_backward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* buffer_diff, T* mask_diff) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - mask_diff[((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + - h) * - w_feature + - w] = - buffer_diff[(n * h_feature * w_feature + h * w_feature + w) * - h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)]; - } - } - } -} - -#endif // PSAMASK_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 1eb5f8fcc..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sigmoid_focal_loss_forward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* output, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + expf(-input[index])); - - // (1 - p)**gamma * log(p) - T term_p = pow(((T)1. - p), gamma) * log(max(p, (T)FLT_MIN)); - // p**gamma * log(1 - p) - T term_n = pow(p, gamma) * log(max((T)1. - p, (T)FLT_MIN)); - - output[index] = (T)0.; - output[index] += -flag_p * alpha * term_p; - output[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - output[index] *= weight[t]; - } - } -} - -template -__global__ void sigmoid_focal_loss_backward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* grad_input, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + exp(-input[index])); - - // (1 - p)**gamma * (1 - p - gamma*p*log(p)) - T term_p = pow(((T)1. - p), gamma) * - ((T)1. - p - (gamma * p * log(max(p, (T)FLT_MIN)))); - // p**gamma * (gamma * (1 - p) * log(1 - p) - p) - T term_n = pow(p, gamma) * - (gamma * ((T)1. - p) * log(max((T)1. - p, (T)FLT_MIN)) - p); - - grad_input[index] = (T)0.; - grad_input[index] += -flag_p * alpha * term_p; - grad_input[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - grad_input[index] *= weight[t]; - } - } -} - -#endif // SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 631b2c617..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void softmax_focal_loss_forward_cuda_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* output, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - output[index] = - -alpha * pow((T)1. - pred, gamma) * log(max(pred, (T)FLT_MIN)); - } else { - output[index] = 0; - } - if (weight != NULL) { - output[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda1_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* buff, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - buff[index] = alpha * (-pow((T)1. - pred, gamma) + - gamma * pow((T)1. - pred, gamma - 1) * pred * - log(max(pred, (T)FLT_MIN))); - } else { - buff[index] = 0; - } - if (weight != NULL) { - buff[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda2_kernel( - const int nthreads, const T* softmax, const int64_t* target, const T* buff, - T* grad_input, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - int64_t label = target[n]; - - if (label >= 0) { - T flag = (label == c ? (T)1. : (T)0.); - grad_input[index] = buff[n] * (flag - softmax[index]); - } else { - grad_input[index] = 0; - } - } -} - -#endif // SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh deleted file mode 100644 index 4ec6a4668..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SYNCBN_CUDA_KERNEL_CUH -#define SYNCBN_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sync_bn_forward_mean_cuda_kernel(const T *input, float *mean, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += input[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_mean_cuda_kernel(const phalf *input, - float *mean, int num, - int channels, int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += static_cast(input[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_var_cuda_kernel(const T *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = input[index] - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_var_cuda_kernel(const phalf *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = static_cast(input[index]) - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_output_cuda_kernel( - const T *input, const float *mean, const float *var, float *running_mean, - float *running_var, const float *weight, const float *bias, float *norm, - float *std, T *output, int num, int channels, int spatial, float eps, - float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = (input[index] - mean_value) / std_value; - output[index] = norm[index] * weight_value + bias_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - (input[index] - mean_value) / std_value * weight_value + bias_value; - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = norm[index] = (input[index] - mean_value) / std_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = (input[index] - mean_value) / std_value; - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template <> -__global__ void sync_bn_forward_output_cuda_kernel( - const phalf *input, const float *mean, const float *var, - float *running_mean, float *running_var, const float *weight, - const float *bias, float *norm, float *std, phalf *output, int num, - int channels, int spatial, float eps, float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = - static_cast(norm[index] * weight_value + bias_value); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - static_cast((static_cast(input[index]) - mean_value) / - std_value * weight_value + - bias_value); - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = static_cast(norm[index]); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = static_cast( - (static_cast(input[index]) - mean_value) / std_value); - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template -__global__ void sync_bn_backward_param_cuda_kernel(const T *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += grad_output[index] * norm[index]; - buffer2[tid] += grad_output[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template <> -__global__ void sync_bn_backward_param_cuda_kernel(const phalf *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += static_cast(grad_output[index]) * norm[index]; - buffer2[tid] += static_cast(grad_output[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const T *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, T *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = - weight[c] * - (grad_output[index] - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]; - } -} - -template <> -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const phalf *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, phalf *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = static_cast( - weight[c] * - (static_cast(grad_output[index]) - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]); - } -} - -#endif // SYNCBN_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp deleted file mode 100644 index f68e87405..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef PYTORCH_CPP_HELPER -#define PYTORCH_CPP_HELPER -#include - -#include - -using namespace at; - -#define CHECK_CUDA(x) \ - TORCH_CHECK(x.device().is_cuda(), #x " must be a CUDA tensor") -#define CHECK_MLU(x) \ - TORCH_CHECK(x.device().type() == at::kMLU, #x " must be a MLU tensor") -#define CHECK_CPU(x) \ - TORCH_CHECK(x.device().type() == at::kCPU, #x " must be a CPU tensor") -#define CHECK_CONTIGUOUS(x) \ - TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") -#define CHECK_CUDA_INPUT(x) \ - CHECK_CUDA(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_MLU_INPUT(x) \ - CHECK_MLU(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_CPU_INPUT(x) \ - CHECK_CPU(x); \ - CHECK_CONTIGUOUS(x) - -#endif // PYTORCH_CPP_HELPER diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp deleted file mode 100644 index 9869b535f..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PYTORCH_CUDA_HELPER -#define PYTORCH_CUDA_HELPER - -#include -#include -#include - -#include -#include - -#include "common_cuda_helper.hpp" - -using at::Half; -using at::Tensor; -using phalf = at::Half; - -#define __PHALF(x) (x) - -#endif // PYTORCH_CUDA_HELPER diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp deleted file mode 100644 index 2a32b7270..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef PYTORCH_DEVICE_REGISTRY_H -#define PYTORCH_DEVICE_REGISTRY_H - -// Using is recommended in the official documentation in -// https://pytorch.org/tutorials/advanced/cpp_extension.html#writing-the-c-op. -// However, we use for compatibility with CUDA 9.0 -// Read https://github.com/pytorch/extension-cpp/issues/35 for more details. -#include - -#include -#include -#include -#include - -inline std::string GetDeviceStr(const at::Device& device) { - std::string str = DeviceTypeName(device.type(), true); - if (device.has_index()) { - str.push_back(':'); - str.append(std::to_string(device.index())); - } - return str; -} - -// Registry -template -class DeviceRegistry; - -template -class DeviceRegistry { - public: - using FunctionType = Ret (*)(Args...); - static const int MAX_DEVICE_TYPES = - int8_t(at::DeviceType::COMPILE_TIME_MAX_DEVICE_TYPES); - - void Register(at::DeviceType device, FunctionType function) { - funcs_[int8_t(device)] = function; - } - - FunctionType Find(at::DeviceType device) const { - return funcs_[int8_t(device)]; - } - - static DeviceRegistry& instance() { - static DeviceRegistry inst; - return inst; - } - - private: - DeviceRegistry() { - for (size_t i = 0; i < MAX_DEVICE_TYPES; ++i) { - funcs_[i] = nullptr; - } - }; - FunctionType funcs_[MAX_DEVICE_TYPES]; -}; - -// get device of first tensor param - -template , at::Tensor>::value, - bool> = true> -at::Device GetFirstTensorDevice(T&& t, Args&&... args) { - return std::forward(t).device(); -} -template , at::Tensor>::value, - bool> = true> -at::Device GetFirstTensorDevice(T&& t, Args&&... args) { - return GetFirstTensorDevice(std::forward(args)...); -} - -// check device consistency - -inline std::pair CheckDeviceConsistency( - const at::Device& device, int index) { - return {index, device}; -} - -template , at::Tensor>::value, - bool> = true> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args); - -template , at::Tensor>::value, - bool> = true> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args) { - auto new_device = std::forward(t).device(); - if (new_device.type() != device.type() || - new_device.index() != device.index()) { - return {index, new_device}; - } - return CheckDeviceConsistency(device, index + 1, std::forward(args)...); -} - -template < - typename T, typename... Args, - std::enable_if_t, at::Tensor>::value, bool>> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args) { - return CheckDeviceConsistency(device, index + 1, std::forward(args)...); -} - -// dispatch - -template -auto Dispatch(const R& registry, const char* name, Args&&... args) { - auto device = GetFirstTensorDevice(std::forward(args)...); - auto inconsist = - CheckDeviceConsistency(device, 0, std::forward(args)...); - TORCH_CHECK(inconsist.first >= int(sizeof...(Args)), name, ": at param ", - inconsist.first, - ", inconsistent device: ", GetDeviceStr(inconsist.second).c_str(), - " vs ", GetDeviceStr(device).c_str(), "\n") - auto f_ptr = registry.Find(device.type()); - TORCH_CHECK(f_ptr != nullptr, name, ": implementation for device ", - GetDeviceStr(device).c_str(), " not found.\n") - return f_ptr(std::forward(args)...); -} - -// helper macro - -#define DEVICE_REGISTRY(key) DeviceRegistry::instance() - -#define REGISTER_DEVICE_IMPL(key, device, value) \ - struct key##_##device##_registerer { \ - key##_##device##_registerer() { \ - DEVICE_REGISTRY(key).Register(at::k##device, value); \ - } \ - }; \ - static key##_##device##_registerer _##key##_##device##_registerer; - -#define DISPATCH_DEVICE_IMPL(key, ...) \ - Dispatch(DEVICE_REGISTRY(key), #key, __VA_ARGS__) - -#endif // PYTORCH_DEVICE_REGISTRY diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp deleted file mode 100644 index dc376bb51..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp +++ /dev/null @@ -1,210 +0,0 @@ -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha); - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha); - -void sigmoid_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SigmoidFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void sigmoid_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - SigmoidFocalLossBackwardCUDAKernelLauncher(input, target, weight, grad_input, - gamma, alpha); -} - -void softmax_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SoftmaxFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void softmax_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - SoftmaxFocalLossBackwardCUDAKernelLauncher(input, target, weight, buff, - grad_input, gamma, alpha); -} - -void sigmoid_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha); - -void softmax_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha); - -REGISTER_DEVICE_IMPL(sigmoid_focal_loss_forward_impl, CUDA, - sigmoid_focal_loss_forward_cuda); -REGISTER_DEVICE_IMPL(sigmoid_focal_loss_backward_impl, CUDA, - sigmoid_focal_loss_backward_cuda); -REGISTER_DEVICE_IMPL(softmax_focal_loss_forward_impl, CUDA, - softmax_focal_loss_forward_cuda); -REGISTER_DEVICE_IMPL(softmax_focal_loss_backward_impl, CUDA, - softmax_focal_loss_backward_cuda); - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean); - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var); - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size); - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias); - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -void sync_bn_forward_mean_cuda(const Tensor input, Tensor mean) { - SyncBNForwardMeanCUDAKernelLauncher(input, mean); -} - -void sync_bn_forward_var_cuda(const Tensor input, const Tensor mean, - Tensor var) { - SyncBNForwardVarCUDAKernelLauncher(input, mean, var); -} - -void sync_bn_forward_output_cuda(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - SyncBNForwardOutputCUDAKernelLauncher(input, mean, var, running_mean, - running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_cuda(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - SyncBNBackwardParamCUDAKernelLauncher(grad_output, norm, grad_weight, - grad_bias); -} - -void sync_bn_backward_data_cuda(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - SyncBNBackwardDataCUDAKernelLauncher(grad_output, weight, grad_weight, - grad_bias, norm, std, grad_input); -} - -void sync_bn_forward_mean_impl(const Tensor input, Tensor mean); - -void sync_bn_forward_var_impl(const Tensor input, const Tensor mean, - Tensor var); - -void sync_bn_forward_output_impl(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param_impl(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data_impl(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input); - -REGISTER_DEVICE_IMPL(sync_bn_forward_mean_impl, CUDA, - sync_bn_forward_mean_cuda); -REGISTER_DEVICE_IMPL(sync_bn_forward_var_impl, CUDA, sync_bn_forward_var_cuda); -REGISTER_DEVICE_IMPL(sync_bn_forward_output_impl, CUDA, - sync_bn_forward_output_cuda); -REGISTER_DEVICE_IMPL(sync_bn_backward_param_impl, CUDA, - sync_bn_backward_param_cuda); -REGISTER_DEVICE_IMPL(sync_bn_backward_data_impl, CUDA, - sync_bn_backward_data_cuda); - - - -void PSAMaskForwardCUDAKernelLauncher(const int psa_type, const Tensor input, - Tensor output, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, - const int half_w_mask); - -void PSAMaskBackwardCUDAKernelLauncher( - const int psa_type, const Tensor grad_output, Tensor grad_input, - const int num_, const int h_feature, const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, const int half_w_mask); - -void psamask_forward_cuda(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask) { - PSAMaskForwardCUDAKernelLauncher(psa_type, input, output, num_, h_feature, - w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_backward_cuda(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - PSAMaskBackwardCUDAKernelLauncher(psa_type, grad_output, grad_input, num_, - h_feature, w_feature, h_mask, w_mask, - half_h_mask, half_w_mask); -} - -void psamask_forward_impl(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask); - -void psamask_backward_impl(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask); -REGISTER_DEVICE_IMPL(psamask_forward_impl, CUDA, psamask_forward_cuda); -REGISTER_DEVICE_IMPL(psamask_backward_impl, CUDA, psamask_backward_cuda); diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu deleted file mode 100644 index cb899f954..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sigmoid_focal_loss_cuda_kernel.cuh" -#include "softmax_focal_loss_cuda_kernel.cuh" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = input.size(1); - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_forward_cuda_kernel", [&] { - sigmoid_focal_loss_forward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha) { - int output_size = grad_input.numel(); - int num_classes = input.size(1); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_backward_cuda_kernel", [&] { - sigmoid_focal_loss_backward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - grad_input.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = softmax.size(1); - - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(softmax.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - softmax.scalar_type(), "softmax_focal_loss_forward_cuda_kernel", [&] { - softmax_focal_loss_forward_cuda_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha) { - int num_classes = softmax.size(1); - - int output_size = buff.numel(); - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda1_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda1_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - buff.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); - - output_size = grad_input.numel(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda2_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda2_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), buff.data_ptr(), - grad_input.data_ptr(), num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu deleted file mode 100644 index a0bdfa60c..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// Modified from -// https://github.com/hszhao/semseg/blob/master/lib/psa/src - -#include - -#include "psamask_cuda_kernel.cuh" -#include "pytorch_cuda_helper.hpp" - -void PSAMaskForwardCUDAKernelLauncher(const int psa_type, const Tensor input, - Tensor output, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, - const int half_w_mask) { - int nthreads = num_ * h_feature * w_feature; - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - if (psa_type == 0) - AT_DISPATCH_FLOATING_TYPES( - input.scalar_type(), "psamask_collect_forward_cuda", [&] { - psamask_collect_forward_cuda<<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, input.data_ptr(), - output.data_ptr()); - }); - else - AT_DISPATCH_FLOATING_TYPES( - input.scalar_type(), "psamask_distribute_forward_cuda", [&] { - psamask_distribute_forward_cuda - <<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, input.data_ptr(), - output.data_ptr()); - }); -} - -void PSAMaskBackwardCUDAKernelLauncher( - const int psa_type, const Tensor grad_output, Tensor grad_input, - const int num_, const int h_feature, const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, const int half_w_mask) { - int nthreads = num_ * h_feature * w_feature; - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - if (psa_type == 0) - AT_DISPATCH_FLOATING_TYPES( - grad_input.scalar_type(), "psamask_collect_backward_cuda", [&] { - psamask_collect_backward_cuda<<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, grad_output.data_ptr(), - grad_input.data_ptr()); - }); - else - AT_DISPATCH_FLOATING_TYPES( - grad_input.scalar_type(), "psamask_distribute_backward_cuda", [&] { - psamask_distribute_backward_cuda - <<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, grad_output.data_ptr(), - grad_input.data_ptr()); - }); -} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu deleted file mode 100644 index 657c81701..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sync_bn_cuda_kernel.cuh" - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_mean_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_var_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), num, channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_output_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), running_mean.data_ptr(), - running_var.data_ptr(), weight.data_ptr(), - bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), output.data_ptr(), num, - channels, spatial, eps, momentum, group_size); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias) { - int num = grad_output.size(0); - int channels = grad_output.size(1); - int spatial = grad_output.size(2); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_param_cuda_kernel", [&] { - sync_bn_backward_param_cuda_kernel - <<>>( - grad_output.data_ptr(), norm.data_ptr(), - grad_weight.data_ptr(), grad_bias.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - int output_size = grad_input.numel(); - int num = grad_input.size(0); - int channels = grad_input.size(1); - int spatial = grad_input.size(2); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_data_cuda_kernel", [&] { - sync_bn_backward_data_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - weight.data_ptr(), grad_weight.data_ptr(), - grad_bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), grad_input.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp deleted file mode 100644 index ed0e21865..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void sigmoid_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(sigmoid_focal_loss_forward_impl, input, target, weight, - output, gamma, alpha); -} - -void sigmoid_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(sigmoid_focal_loss_backward_impl, input, target, weight, - grad_input, gamma, alpha); -} - -void softmax_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(softmax_focal_loss_forward_impl, input, target, weight, - output, gamma, alpha); -} - -void softmax_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - DISPATCH_DEVICE_IMPL(softmax_focal_loss_backward_impl, input, target, weight, - buff, grad_input, gamma, alpha); -} - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - sigmoid_focal_loss_forward_impl(input, target, weight, output, gamma, alpha); -} - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha) { - sigmoid_focal_loss_backward_impl(input, target, weight, grad_input, gamma, - alpha); -} - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - softmax_focal_loss_forward_impl(input, target, weight, output, gamma, alpha); -} - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha) { - softmax_focal_loss_backward_impl(input, target, weight, buff, grad_input, - gamma, alpha); -} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp deleted file mode 100644 index a08d227d4..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// modified from -// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/vision.cpp -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF -#include -int get_cudart_version() { return CUDART_VERSION; } -#endif -#endif - -std::string get_compiling_cuda_version() { -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF - std::ostringstream oss; - // copied from - // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/cuda/detail/CUDAHooks.cpp#L231 - auto printCudaStyleVersion = [&](int v) { - oss << (v / 1000) << "." << (v / 10 % 100); - if (v % 10 != 0) { - oss << "." << (v % 10); - } - }; - printCudaStyleVersion(get_cudart_version()); - return oss.str(); -#else - return std::string("rocm not available"); -#endif -#else - return std::string("not available"); -#endif -} - -// similar to -// https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/Version.cpp -std::string get_compiler_version() { - std::ostringstream ss; -#if defined(__GNUC__) -#ifndef __clang__ - { ss << "GCC " << __GNUC__ << "." << __GNUC_MINOR__; } -#endif -#endif - -#if defined(__clang_major__) - { - ss << "clang " << __clang_major__ << "." << __clang_minor__ << "." - << __clang_patchlevel__; - } -#endif - -#if defined(_MSC_VER) - { ss << "MSVC " << _MSC_FULL_VER; } -#endif - return ss.str(); -} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp deleted file mode 100644 index 6064c9ba5..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// Modified from -// https://github.com/hszhao/semseg/blob/master/lib/psa/src -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void psamask_forward_impl(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask) { - DISPATCH_DEVICE_IMPL(psamask_forward_impl, psa_type, input, output, num_, - h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_backward_impl(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - DISPATCH_DEVICE_IMPL(psamask_backward_impl, psa_type, grad_output, grad_input, - num_, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_forward(const Tensor input, Tensor output, const int psa_type, - const int num_, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask) { - psamask_forward_impl(psa_type, input, output, num_, h_feature, w_feature, - h_mask, w_mask, half_h_mask, half_w_mask); -} - -void psamask_backward(Tensor grad_output, const Tensor grad_input, - const int psa_type, const int num_, const int h_feature, - const int w_feature, const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - psamask_backward_impl(psa_type, grad_output, grad_input, num_, h_feature, - w_feature, h_mask, w_mask, half_h_mask, half_w_mask); -} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp deleted file mode 100644 index 1e99174d8..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include - -#include "pytorch_cpp_helper.hpp" - -std::string get_compiler_version(); -std::string get_compiling_cuda_version(); - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha); - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha); - -void sync_bn_forward_mean(const Tensor input, Tensor mean); - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var); - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - - -void psamask_forward(const Tensor input, Tensor output, const int psa_type, - const int num_, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask); - -void psamask_backward(Tensor grad_output, const Tensor grad_input, - const int psa_type, const int num_, const int h_feature, - const int w_feature, const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask); - - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("get_compiler_version", &get_compiler_version, "get_compiler_version"); - m.def("get_compiling_cuda_version", &get_compiling_cuda_version, - "get_compiling_cuda_version"); - - - m.def("sigmoid_focal_loss_forward", &sigmoid_focal_loss_forward, - "sigmoid_focal_loss_forward ", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("sigmoid_focal_loss_backward", &sigmoid_focal_loss_backward, - "sigmoid_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("grad_input"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_forward", &softmax_focal_loss_forward, - "softmax_focal_loss_forward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_backward", &softmax_focal_loss_backward, - "softmax_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("buff"), py::arg("grad_input"), - py::arg("gamma"), py::arg("alpha")); - - // SyncBN - m.def("sync_bn_forward_mean", &sync_bn_forward_mean, "sync_bn forward_mean", - py::arg("input"), py::arg("mean")); - m.def("sync_bn_forward_var", &sync_bn_forward_var, "sync_bn forward_var", - py::arg("input"), py::arg("mean"), py::arg("var")); - m.def("sync_bn_forward_output", &sync_bn_forward_output, - "sync_bn forward_output", py::arg("input"), py::arg("mean"), - py::arg("var"), py::arg("weight"), py::arg("bias"), - py::arg("running_mean"), py::arg("running_var"), py::arg("norm"), - py::arg("std"), py::arg("output"), py::arg("eps"), py::arg("momentum"), - py::arg("group_size")); - m.def("sync_bn_backward_param", &sync_bn_backward_param, - "sync_bn backward_param", py::arg("grad_output"), py::arg("norm"), - py::arg("grad_weight"), py::arg("grad_bias")); - m.def("sync_bn_backward_data", &sync_bn_backward_data, - "sync_bn backward_data", py::arg("grad_output"), py::arg("weight"), - py::arg("grad_weight"), py::arg("grad_bias"), py::arg("norm"), - py::arg("std"), py::arg("grad_input")); - - // PASMask - m.def("psamask_forward", &psamask_forward, "PSAMASK forward (CPU/CUDA)", - py::arg("input"), py::arg("output"), py::arg("psa_type"), - py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), - py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), - py::arg("half_w_mask")); - m.def("psamask_backward", &psamask_backward, "PSAMASK backward (CPU/CUDA)", - py::arg("grad_output"), py::arg("grad_input"), py::arg("psa_type"), - py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), - py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), - py::arg("half_w_mask")); -} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp deleted file mode 100644 index fd5a51327..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void sync_bn_forward_mean_impl(const Tensor input, Tensor mean) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_mean_impl, input, mean); -} - -void sync_bn_forward_var_impl(const Tensor input, const Tensor mean, - Tensor var) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_var_impl, input, mean, var); -} - -void sync_bn_forward_output_impl(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_output_impl, input, mean, var, - running_mean, running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_impl(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - DISPATCH_DEVICE_IMPL(sync_bn_backward_param_impl, grad_output, norm, - grad_weight, grad_bias); -} - -void sync_bn_backward_data_impl(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - DISPATCH_DEVICE_IMPL(sync_bn_backward_data_impl, grad_output, weight, - grad_weight, grad_bias, norm, std, grad_input); -} - -void sync_bn_forward_mean(const Tensor input, Tensor mean) { - sync_bn_forward_mean_impl(input, mean); -} - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var) { - sync_bn_forward_var_impl(input, mean, var); -} - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - sync_bn_forward_output_impl(input, mean, var, running_mean, running_var, - weight, bias, norm, std, output, eps, momentum, - group_size); -} - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - sync_bn_backward_param_impl(grad_output, norm, grad_weight, grad_bias); -} - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - sync_bn_backward_data_impl(grad_output, weight, grad_weight, grad_bias, norm, - std, grad_input); -} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/deprecated_wrappers.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/deprecated_wrappers.py deleted file mode 100644 index 629a8033f..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/deprecated_wrappers.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# This file is for backward compatibility. -# Module wrappers for empty tensor have been moved to mmcv.cnn.bricks. -import warnings - -from ..cnn.bricks.wrappers import Conv2d, ConvTranspose2d, Linear, MaxPool2d - - -class Conv2d_deprecated(Conv2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Conv2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) - - -class ConvTranspose2d_deprecated(ConvTranspose2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing ConvTranspose2d wrapper from "mmcv.ops" will be ' - 'deprecated in the future. Please import them from "mmcv.cnn" ' - 'instead', DeprecationWarning) - - -class MaxPool2d_deprecated(MaxPool2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing MaxPool2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) - - -class Linear_deprecated(Linear): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Linear wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/focal_loss.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/focal_loss.py deleted file mode 100644 index 805860516..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/focal_loss.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sigmoid_focal_loss_forward', 'sigmoid_focal_loss_backward', - 'softmax_focal_loss_forward', 'softmax_focal_loss_backward' -]) - - -class SigmoidFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSigmoidFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance( - target, (torch.Tensor, torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - output = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_forward( - input, target, weight, output, gamma=ctx.gamma, alpha=ctx.alpha) - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input, target, weight) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - input, target, weight = ctx.saved_tensors - - grad_input = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_backward( - input, - target, - weight, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input.size(0) - return grad_input, None, None, None, None, None - - -sigmoid_focal_loss = SigmoidFocalLossFunction.apply - - -class SigmoidFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SigmoidFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return sigmoid_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s - - -class SoftmaxFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSoftmaxFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - channel_stats, _ = torch.max(input, dim=1) - input_softmax = input - channel_stats.unsqueeze(1).expand_as(input) - input_softmax.exp_() - - channel_stats = input_softmax.sum(dim=1) - input_softmax /= channel_stats.unsqueeze(1).expand_as(input) - - output = input.new_zeros(input.size(0)) - ext_module.softmax_focal_loss_forward( - input_softmax, - target, - weight, - output, - gamma=ctx.gamma, - alpha=ctx.alpha) - - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input_softmax, target, weight) - return output - - @staticmethod - def backward(ctx, grad_output): - input_softmax, target, weight = ctx.saved_tensors - buff = input_softmax.new_zeros(input_softmax.size(0)) - grad_input = input_softmax.new_zeros(input_softmax.size()) - - ext_module.softmax_focal_loss_backward( - input_softmax, - target, - weight, - buff, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input_softmax.size(0) - return grad_input, None, None, None, None, None - - -softmax_focal_loss = SoftmaxFocalLossFunction.apply - - -class SoftmaxFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SoftmaxFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return softmax_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/info.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/info.py deleted file mode 100644 index 29f2e5598..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/info.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os - -import torch - -if torch.__version__ == 'parrots': - import parrots - - def get_compiler_version(): - return 'GCC ' + parrots.version.compiler - - def get_compiling_cuda_version(): - return parrots.version.cuda -else: - from ..utils import ext_loader - ext_module = ext_loader.load_ext( - '_ext', ['get_compiler_version', 'get_compiling_cuda_version']) - - def get_compiler_version(): - return ext_module.get_compiler_version() - - def get_compiling_cuda_version(): - return ext_module.get_compiling_cuda_version() - - -def get_onnxruntime_op_path(): - wildcard = os.path.join( - os.path.abspath(os.path.dirname(os.path.dirname(__file__))), - '_ext_ort.*.so') - - paths = glob.glob(wildcard) - if len(paths) > 0: - return paths[0] - else: - return '' diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/point_sample.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/point_sample.py deleted file mode 100644 index 9a70b28e2..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/point_sample.py +++ /dev/null @@ -1,346 +0,0 @@ -# Modified from https://github.com/facebookresearch/detectron2/tree/master/projects/PointRend # noqa - -from os import path as osp - -import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.nn.modules.utils import _pair -from torch.onnx.operators import shape_as_tensor - - -def bilinear_grid_sample(im, grid, align_corners=False): - """Given an input and a flow-field grid, computes the output using input - values and pixel locations from grid. Supported only bilinear interpolation - method to sample the input pixels. - - Args: - im (torch.Tensor): Input feature map, shape (N, C, H, W) - grid (torch.Tensor): Point coordinates, shape (N, Hg, Wg, 2) - align_corners {bool}: If set to True, the extrema (-1 and 1) are - considered as referring to the center points of the input’s - corner pixels. If set to False, they are instead considered as - referring to the corner points of the input’s corner pixels, - making the sampling more resolution agnostic. - - Returns: - torch.Tensor: A tensor with sampled points, shape (N, C, Hg, Wg) - """ - n, c, h, w = im.shape - gn, gh, gw, _ = grid.shape - assert n == gn - - x = grid[:, :, :, 0] - y = grid[:, :, :, 1] - - if align_corners: - x = ((x + 1) / 2) * (w - 1) - y = ((y + 1) / 2) * (h - 1) - else: - x = ((x + 1) * w - 1) / 2 - y = ((y + 1) * h - 1) / 2 - - x = x.view(n, -1) - y = y.view(n, -1) - - x0 = torch.floor(x).long() - y0 = torch.floor(y).long() - x1 = x0 + 1 - y1 = y0 + 1 - - wa = ((x1 - x) * (y1 - y)).unsqueeze(1) - wb = ((x1 - x) * (y - y0)).unsqueeze(1) - wc = ((x - x0) * (y1 - y)).unsqueeze(1) - wd = ((x - x0) * (y - y0)).unsqueeze(1) - - # Apply default for grid_sample function zero padding - im_padded = F.pad(im, pad=[1, 1, 1, 1], mode='constant', value=0) - padded_h = h + 2 - padded_w = w + 2 - # save points positions after padding - x0, x1, y0, y1 = x0 + 1, x1 + 1, y0 + 1, y1 + 1 - - # Clip coordinates to padded image size - x0 = torch.where(x0 < 0, torch.tensor(0), x0) - x0 = torch.where(x0 > padded_w - 1, torch.tensor(padded_w - 1), x0) - x1 = torch.where(x1 < 0, torch.tensor(0), x1) - x1 = torch.where(x1 > padded_w - 1, torch.tensor(padded_w - 1), x1) - y0 = torch.where(y0 < 0, torch.tensor(0), y0) - y0 = torch.where(y0 > padded_h - 1, torch.tensor(padded_h - 1), y0) - y1 = torch.where(y1 < 0, torch.tensor(0), y1) - y1 = torch.where(y1 > padded_h - 1, torch.tensor(padded_h - 1), y1) - - im_padded = im_padded.view(n, c, -1) - - x0_y0 = (x0 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x0_y1 = (x0 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y0 = (x1 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y1 = (x1 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - - Ia = torch.gather(im_padded, 2, x0_y0) - Ib = torch.gather(im_padded, 2, x0_y1) - Ic = torch.gather(im_padded, 2, x1_y0) - Id = torch.gather(im_padded, 2, x1_y1) - - return (Ia * wa + Ib * wb + Ic * wc + Id * wd).reshape(n, c, gh, gw) - - -def is_in_onnx_export_without_custom_ops(): - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - return torch.onnx.is_in_onnx_export( - ) and not osp.exists(ort_custom_op_path) - - -def normalize(grid): - """Normalize input grid from [-1, 1] to [0, 1] - - Args: - grid (torch.Tensor): The grid to be normalize, range [-1, 1]. - - Returns: - torch.Tensor: Normalized grid, range [0, 1]. - """ - - return (grid + 1.0) / 2.0 - - -def denormalize(grid): - """Denormalize input grid from range [0, 1] to [-1, 1] - - Args: - grid (torch.Tensor): The grid to be denormalize, range [0, 1]. - - Returns: - torch.Tensor: Denormalized grid, range [-1, 1]. - """ - - return grid * 2.0 - 1.0 - - -def generate_grid(num_grid, size, device): - """Generate regular square grid of points in [0, 1] x [0, 1] coordinate - space. - - Args: - num_grid (int): The number of grids to sample, one for each region. - size (tuple[int, int]): The side size of the regular grid. - device (torch.device): Desired device of returned tensor. - - Returns: - torch.Tensor: A tensor of shape (num_grid, size[0]*size[1], 2) that - contains coordinates for the regular grids. - """ - - affine_trans = torch.tensor([[[1., 0., 0.], [0., 1., 0.]]], device=device) - grid = F.affine_grid( - affine_trans, torch.Size((1, 1, *size)), align_corners=False) - grid = normalize(grid) - return grid.view(1, -1, 2).expand(num_grid, -1, -1) - - -def rel_roi_point_to_abs_img_point(rois, rel_roi_points): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (torch.Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (torch.Tensor): Point coordinates inside RoI, relative - to RoI, location, range (0, 1), shape (N, P, 2) - Returns: - torch.Tensor: Image based absolute point coordinates, shape (N, P, 2) - """ - - with torch.no_grad(): - assert rel_roi_points.size(0) == rois.size(0) - assert rois.dim() == 2 - assert rel_roi_points.dim() == 3 - assert rel_roi_points.size(2) == 2 - # remove batch idx - if rois.size(1) == 5: - rois = rois[:, 1:] - abs_img_points = rel_roi_points.clone() - # To avoid an error during exporting to onnx use independent - # variables instead inplace computation - xs = abs_img_points[:, :, 0] * (rois[:, None, 2] - rois[:, None, 0]) - ys = abs_img_points[:, :, 1] * (rois[:, None, 3] - rois[:, None, 1]) - xs += rois[:, None, 0] - ys += rois[:, None, 1] - abs_img_points = torch.stack([xs, ys], dim=2) - return abs_img_points - - -def get_shape_from_feature_map(x): - """Get spatial resolution of input feature map considering exporting to - onnx mode. - - Args: - x (torch.Tensor): Input tensor, shape (N, C, H, W) - - Returns: - torch.Tensor: Spatial resolution (width, height), shape (1, 1, 2) - """ - if torch.onnx.is_in_onnx_export(): - img_shape = shape_as_tensor(x)[2:].flip(0).view(1, 1, 2).to( - x.device).float() - else: - img_shape = torch.tensor(x.shape[2:]).flip(0).view(1, 1, 2).to( - x.device).float() - return img_shape - - -def abs_img_point_to_rel_img_point(abs_img_points, img, spatial_scale=1.): - """Convert image based absolute point coordinates to image based relative - coordinates for sampling. - - Args: - abs_img_points (torch.Tensor): Image based absolute point coordinates, - shape (N, P, 2) - img (tuple or torch.Tensor): (height, width) of image or feature map. - spatial_scale (float, optional): Scale points by this factor. - Default: 1. - - Returns: - Tensor: Image based relative point coordinates for sampling, shape - (N, P, 2). - """ - - assert (isinstance(img, tuple) and len(img) == 2) or \ - (isinstance(img, torch.Tensor) and len(img.shape) == 4) - - if isinstance(img, tuple): - h, w = img - scale = torch.tensor([w, h], - dtype=torch.float, - device=abs_img_points.device) - scale = scale.view(1, 1, 2) - else: - scale = get_shape_from_feature_map(img) - - return abs_img_points / scale * spatial_scale - - -def rel_roi_point_to_rel_img_point(rois, - rel_roi_points, - img, - spatial_scale=1.): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (torch.Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (torch.Tensor): Point coordinates inside RoI, relative - to RoI, location, range (0, 1), shape (N, P, 2) - img (tuple or torch.Tensor): (height, width) of image or feature map. - spatial_scale (float, optional): Scale points by this factor. - Default: 1. - - Returns: - torch.Tensor: Image based relative point coordinates for sampling, - shape (N, P, 2). - """ - - abs_img_point = rel_roi_point_to_abs_img_point(rois, rel_roi_points) - rel_img_point = abs_img_point_to_rel_img_point(abs_img_point, img, - spatial_scale) - - return rel_img_point - - -def point_sample(input, points, align_corners=False, **kwargs): - """A wrapper around :func:`grid_sample` to support 3D point_coords tensors - Unlike :func:`torch.nn.functional.grid_sample` it assumes point_coords to - lie inside ``[0, 1] x [0, 1]`` square. - - Args: - input (torch.Tensor): Feature map, shape (N, C, H, W). - points (torch.Tensor): Image based absolute point coordinates - (normalized), range [0, 1] x [0, 1], shape (N, P, 2) or - (N, Hgrid, Wgrid, 2). - align_corners (bool, optional): Whether align_corners. - Default: False - - Returns: - torch.Tensor: Features of `point` on `input`, shape (N, C, P) or - (N, C, Hgrid, Wgrid). - """ - - add_dim = False - if points.dim() == 3: - add_dim = True - points = points.unsqueeze(2) - if is_in_onnx_export_without_custom_ops(): - # If custom ops for onnx runtime not compiled use python - # implementation of grid_sample function to make onnx graph - # with supported nodes - output = bilinear_grid_sample( - input, denormalize(points), align_corners=align_corners) - else: - output = F.grid_sample( - input, denormalize(points), align_corners=align_corners, **kwargs) - if add_dim: - output = output.squeeze(3) - return output - - -class SimpleRoIAlign(nn.Module): - - def __init__(self, output_size, spatial_scale, aligned=True): - """Simple RoI align in PointRend, faster than standard RoIAlign. - - Args: - output_size (tuple[int]): h, w - spatial_scale (float): scale the input boxes by this number - aligned (bool): if False, use the legacy implementation in - MMDetection, align_corners=True will be used in F.grid_sample. - If True, align the results more perfectly. - """ - - super(SimpleRoIAlign, self).__init__() - self.output_size = _pair(output_size) - self.spatial_scale = float(spatial_scale) - # to be consistent with other RoI ops - self.use_torchvision = False - self.aligned = aligned - - def forward(self, features, rois): - num_imgs = features.size(0) - num_rois = rois.size(0) - rel_roi_points = generate_grid( - num_rois, self.output_size, device=rois.device) - - if torch.onnx.is_in_onnx_export(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois, rel_roi_points, features, self.spatial_scale) - rel_img_points = rel_img_points.reshape(num_imgs, -1, - *rel_img_points.shape[1:]) - point_feats = point_sample( - features, rel_img_points, align_corners=not self.aligned) - point_feats = point_feats.transpose(1, 2) - else: - point_feats = [] - for batch_ind in range(num_imgs): - # unravel batch dim - feat = features[batch_ind].unsqueeze(0) - inds = (rois[:, 0].long() == batch_ind) - if inds.any(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois[inds], rel_roi_points[inds], feat, - self.spatial_scale).unsqueeze(0) - point_feat = point_sample( - feat, rel_img_points, align_corners=not self.aligned) - point_feat = point_feat.squeeze(0).transpose(0, 1) - point_feats.append(point_feat) - - point_feats = torch.cat(point_feats, dim=0) - - channels = features.size(1) - roi_feats = point_feats.reshape(num_rois, channels, *self.output_size) - - return roi_feats - - def __repr__(self): - format_str = self.__class__.__name__ - format_str += '(output_size={}, spatial_scale={}'.format( - self.output_size, self.spatial_scale) - return format_str diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/psa_mask.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/psa_mask.py deleted file mode 100644 index cdf14e62b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/psa_mask.py +++ /dev/null @@ -1,92 +0,0 @@ -# Modified from https://github.com/hszhao/semseg/blob/master/lib/psa -from torch import nn -from torch.autograd import Function -from torch.nn.modules.utils import _pair - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', - ['psamask_forward', 'psamask_backward']) - - -class PSAMaskFunction(Function): - - @staticmethod - def symbolic(g, input, psa_type, mask_size): - return g.op( - 'mmcv::MMCVPSAMask', - input, - psa_type_i=psa_type, - mask_size_i=mask_size) - - @staticmethod - def forward(ctx, input, psa_type, mask_size): - ctx.psa_type = psa_type - ctx.mask_size = _pair(mask_size) - ctx.save_for_backward(input) - - h_mask, w_mask = ctx.mask_size - batch_size, channels, h_feature, w_feature = input.size() - assert channels == h_mask * w_mask - output = input.new_zeros( - (batch_size, h_feature * w_feature, h_feature, w_feature)) - - ext_module.psamask_forward( - input, - output, - psa_type=psa_type, - num_=batch_size, - h_feature=h_feature, - w_feature=w_feature, - h_mask=h_mask, - w_mask=w_mask, - half_h_mask=(h_mask - 1) // 2, - half_w_mask=(w_mask - 1) // 2) - return output - - @staticmethod - def backward(ctx, grad_output): - input = ctx.saved_tensors[0] - psa_type = ctx.psa_type - h_mask, w_mask = ctx.mask_size - batch_size, channels, h_feature, w_feature = input.size() - grad_input = grad_output.new_zeros( - (batch_size, channels, h_feature, w_feature)) - ext_module.psamask_backward( - grad_output, - grad_input, - psa_type=psa_type, - num_=batch_size, - h_feature=h_feature, - w_feature=w_feature, - h_mask=h_mask, - w_mask=w_mask, - half_h_mask=(h_mask - 1) // 2, - half_w_mask=(w_mask - 1) // 2) - return grad_input, None, None, None - - -psa_mask = PSAMaskFunction.apply - - -class PSAMask(nn.Module): - - def __init__(self, psa_type, mask_size=None): - super(PSAMask, self).__init__() - assert psa_type in ['collect', 'distribute'] - if psa_type == 'collect': - psa_type_enum = 0 - else: - psa_type_enum = 1 - self.psa_type_enum = psa_type_enum - self.mask_size = mask_size - self.psa_type = psa_type - - def forward(self, input): - return psa_mask(input, self.psa_type_enum, self.mask_size) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(psa_type={self.psa_type}, ' - s += f'mask_size={self.mask_size})' - return s diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/sync_bn.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/sync_bn.py deleted file mode 100644 index 04302f031..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/ops/sync_bn.py +++ /dev/null @@ -1,279 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn.functional as F -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.module import Module -from torch.nn.parameter import Parameter - -from mmcv.cnn import NORM_LAYERS -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sync_bn_forward_mean', 'sync_bn_forward_var', 'sync_bn_forward_output', - 'sync_bn_backward_param', 'sync_bn_backward_data' -]) - - -class SyncBatchNormFunction(Function): - - @staticmethod - def symbolic(g, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - return g.op( - 'mmcv::MMCVSyncBatchNorm', - input, - running_mean, - running_var, - weight, - bias, - momentum_f=momentum, - eps_f=eps, - group_i=group, - group_size_i=group_size, - stats_mode=stats_mode) - - @staticmethod - def forward(self, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - self.momentum = momentum - self.eps = eps - self.group = group - self.group_size = group_size - self.stats_mode = stats_mode - - assert isinstance( - input, (torch.HalfTensor, torch.FloatTensor, - torch.cuda.HalfTensor, torch.cuda.FloatTensor)), \ - f'only support Half or Float Tensor, but {input.type()}' - output = torch.zeros_like(input) - input3d = input.flatten(start_dim=2) - output3d = output.view_as(input3d) - num_channels = input3d.size(1) - - # ensure mean/var/norm/std are initialized as zeros - # ``torch.empty()`` does not guarantee that - mean = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - var = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - norm = torch.zeros_like( - input3d, dtype=torch.float, device=input3d.device) - std = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - - batch_size = input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_forward_mean(input3d, mean) - batch_flag = torch.ones([1], device=mean.device, dtype=mean.dtype) - else: - # skip updating mean and leave it as zeros when the input is empty - batch_flag = torch.zeros([1], device=mean.device, dtype=mean.dtype) - - # synchronize mean and the batch flag - vec = torch.cat([mean, batch_flag]) - if self.stats_mode == 'N': - vec *= batch_size - if self.group_size > 1: - dist.all_reduce(vec, group=self.group) - total_batch = vec[-1].detach() - mean = vec[:num_channels] - - if self.stats_mode == 'default': - mean = mean / self.group_size - elif self.stats_mode == 'N': - mean = mean / total_batch.clamp(min=1) - else: - raise NotImplementedError - - # leave var as zeros when the input is empty - if batch_size > 0: - ext_module.sync_bn_forward_var(input3d, mean, var) - - if self.stats_mode == 'N': - var *= batch_size - if self.group_size > 1: - dist.all_reduce(var, group=self.group) - - if self.stats_mode == 'default': - var /= self.group_size - elif self.stats_mode == 'N': - var /= total_batch.clamp(min=1) - else: - raise NotImplementedError - - # if the total batch size over all the ranks is zero, - # we should not update the statistics in the current batch - update_flag = total_batch.clamp(max=1) - momentum = update_flag * self.momentum - ext_module.sync_bn_forward_output( - input3d, - mean, - var, - weight, - bias, - running_mean, - running_var, - norm, - std, - output3d, - eps=self.eps, - momentum=momentum, - group_size=self.group_size) - self.save_for_backward(norm, std, weight) - return output - - @staticmethod - @once_differentiable - def backward(self, grad_output): - norm, std, weight = self.saved_tensors - grad_weight = torch.zeros_like(weight) - grad_bias = torch.zeros_like(weight) - grad_input = torch.zeros_like(grad_output) - grad_output3d = grad_output.flatten(start_dim=2) - grad_input3d = grad_input.view_as(grad_output3d) - - batch_size = grad_input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_backward_param(grad_output3d, norm, grad_weight, - grad_bias) - - # all reduce - if self.group_size > 1: - dist.all_reduce(grad_weight, group=self.group) - dist.all_reduce(grad_bias, group=self.group) - grad_weight /= self.group_size - grad_bias /= self.group_size - - if batch_size > 0: - ext_module.sync_bn_backward_data(grad_output3d, weight, - grad_weight, grad_bias, norm, std, - grad_input3d) - - return grad_input, None, None, grad_weight, grad_bias, \ - None, None, None, None, None - - -@NORM_LAYERS.register_module(name='MMSyncBN') -class SyncBatchNorm(Module): - """Synchronized Batch Normalization. - - Args: - num_features (int): number of features/chennels in input tensor - eps (float, optional): a value added to the denominator for numerical - stability. Defaults to 1e-5. - momentum (float, optional): the value used for the running_mean and - running_var computation. Defaults to 0.1. - affine (bool, optional): whether to use learnable affine parameters. - Defaults to True. - track_running_stats (bool, optional): whether to track the running - mean and variance during training. When set to False, this - module does not track such statistics, and initializes statistics - buffers ``running_mean`` and ``running_var`` as ``None``. When - these buffers are ``None``, this module always uses batch - statistics in both training and eval modes. Defaults to True. - group (int, optional): synchronization of stats happen within - each process group individually. By default it is synchronization - across the whole world. Defaults to None. - stats_mode (str, optional): The statistical mode. Available options - includes ``'default'`` and ``'N'``. Defaults to 'default'. - When ``stats_mode=='default'``, it computes the overall statistics - using those from each worker with equal weight, i.e., the - statistics are synchronized and simply divied by ``group``. This - mode will produce inaccurate statistics when empty tensors occur. - When ``stats_mode=='N'``, it compute the overall statistics using - the total number of batches in each worker ignoring the number of - group, i.e., the statistics are synchronized and then divied by - the total batch ``N``. This mode is beneficial when empty tensors - occur during training, as it average the total mean by the real - number of batch. - """ - - def __init__(self, - num_features, - eps=1e-5, - momentum=0.1, - affine=True, - track_running_stats=True, - group=None, - stats_mode='default'): - super(SyncBatchNorm, self).__init__() - self.num_features = num_features - self.eps = eps - self.momentum = momentum - self.affine = affine - self.track_running_stats = track_running_stats - group = dist.group.WORLD if group is None else group - self.group = group - self.group_size = dist.get_world_size(group) - assert stats_mode in ['default', 'N'], \ - f'"stats_mode" only accepts "default" and "N", got "{stats_mode}"' - self.stats_mode = stats_mode - if self.affine: - self.weight = Parameter(torch.Tensor(num_features)) - self.bias = Parameter(torch.Tensor(num_features)) - else: - self.register_parameter('weight', None) - self.register_parameter('bias', None) - if self.track_running_stats: - self.register_buffer('running_mean', torch.zeros(num_features)) - self.register_buffer('running_var', torch.ones(num_features)) - self.register_buffer('num_batches_tracked', - torch.tensor(0, dtype=torch.long)) - else: - self.register_buffer('running_mean', None) - self.register_buffer('running_var', None) - self.register_buffer('num_batches_tracked', None) - self.reset_parameters() - - def reset_running_stats(self): - if self.track_running_stats: - self.running_mean.zero_() - self.running_var.fill_(1) - self.num_batches_tracked.zero_() - - def reset_parameters(self): - self.reset_running_stats() - if self.affine: - self.weight.data.uniform_() # pytorch use ones_() - self.bias.data.zero_() - - def forward(self, input): - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input, got {input.dim()}D input') - if self.momentum is None: - exponential_average_factor = 0.0 - else: - exponential_average_factor = self.momentum - - if self.training and self.track_running_stats: - if self.num_batches_tracked is not None: - self.num_batches_tracked += 1 - if self.momentum is None: # use cumulative moving average - exponential_average_factor = 1.0 / float( - self.num_batches_tracked) - else: # use exponential moving average - exponential_average_factor = self.momentum - - if self.training or not self.track_running_stats: - return SyncBatchNormFunction.apply( - input, self.running_mean, self.running_var, self.weight, - self.bias, exponential_average_factor, self.eps, self.group, - self.group_size, self.stats_mode) - else: - return F.batch_norm(input, self.running_mean, self.running_var, - self.weight, self.bias, False, - exponential_average_factor, self.eps) - - def __repr__(self): - s = self.__class__.__name__ - s += f'({self.num_features}, ' - s += f'eps={self.eps}, ' - s += f'momentum={self.momentum}, ' - s += f'affine={self.affine}, ' - s += f'track_running_stats={self.track_running_stats}, ' - s += f'group_size={self.group_size},' - s += f'stats_mode={self.stats_mode})' - return s diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/__init__.py deleted file mode 100644 index 2ed2c17ad..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collate import collate -from .data_container import DataContainer -from .data_parallel import MMDataParallel -from .distributed import MMDistributedDataParallel -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter, scatter_kwargs -from .utils import is_module_wrapper - -__all__ = [ - 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', - 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/_functions.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/_functions.py deleted file mode 100644 index 95c58bf1a..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/_functions.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import _get_stream - - -def scatter(input, devices, streams=None): - """Scatters tensor across multiple GPUs.""" - if streams is None: - streams = [None] * len(devices) - - if isinstance(input, list): - chunk_size = (len(input) - 1) // len(devices) + 1 - outputs = [ - scatter(input[i], [devices[i // chunk_size]], - [streams[i // chunk_size]]) for i in range(len(input)) - ] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - # TODO: copy to a pinned buffer first (if copying from CPU) - stream = streams[0] if output.numel() > 0 else None - if devices != [-1]: - with torch.cuda.device(devices[0]), torch.cuda.stream(stream): - output = output.cuda(devices[0], non_blocking=True) - - return output - else: - raise Exception(f'Unknown type {type(input)}.') - - -def synchronize_stream(output, devices, streams): - if isinstance(output, list): - chunk_size = len(output) // len(devices) - for i in range(len(devices)): - for j in range(chunk_size): - synchronize_stream(output[i * chunk_size + j], [devices[i]], - [streams[i]]) - elif isinstance(output, torch.Tensor): - if output.numel() != 0: - with torch.cuda.device(devices[0]): - main_stream = torch.cuda.current_stream() - main_stream.wait_stream(streams[0]) - output.record_stream(main_stream) - else: - raise Exception(f'Unknown type {type(output)}.') - - -def get_input_device(input): - if isinstance(input, list): - for item in input: - input_device = get_input_device(item) - if input_device != -1: - return input_device - return -1 - elif isinstance(input, torch.Tensor): - return input.get_device() if input.is_cuda else -1 - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_gpus, input): - input_device = get_input_device(input) - streams = None - if input_device == -1 and target_gpus != [-1]: - # Perform CPU to GPU copies in a background stream - streams = [_get_stream(device) for device in target_gpus] - - outputs = scatter(input, target_gpus, streams) - # Synchronize with the copy stream - if streams is not None: - synchronize_stream(outputs, target_gpus, streams) - - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/collate.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/collate.py deleted file mode 100644 index ad749197d..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/collate.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence - -import torch -import torch.nn.functional as F -from torch.utils.data.dataloader import default_collate - -from .data_container import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Puts each data field into a tensor/DataContainer with outer dimension - batch size. - - Extend default_collate to add support for - :type:`~mmcv.parallel.DataContainer`. There are 3 cases. - - 1. cpu_only = True, e.g., meta data - 2. cpu_only = False, stack = True, e.g., images tensors - 3. cpu_only = False, stack = False, e.g., gt bboxes - """ - - if not isinstance(batch, Sequence): - raise TypeError(f'{batch.dtype} is not supported.') - - if isinstance(batch[0], DataContainer): - stacked = [] - if batch[0].cpu_only: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer( - stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) - elif batch[0].stack: - for i in range(0, len(batch), samples_per_gpu): - assert isinstance(batch[i].data, torch.Tensor) - - if batch[i].pad_dims is not None: - ndim = batch[i].dim() - assert ndim > batch[i].pad_dims - max_shape = [0 for _ in range(batch[i].pad_dims)] - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = batch[i].size(-dim) - for sample in batch[i:i + samples_per_gpu]: - for dim in range(0, ndim - batch[i].pad_dims): - assert batch[i].size(dim) == sample.size(dim) - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = max(max_shape[dim - 1], - sample.size(-dim)) - padded_samples = [] - for sample in batch[i:i + samples_per_gpu]: - pad = [0 for _ in range(batch[i].pad_dims * 2)] - for dim in range(1, batch[i].pad_dims + 1): - pad[2 * dim - - 1] = max_shape[dim - 1] - sample.size(-dim) - padded_samples.append( - F.pad( - sample.data, pad, value=sample.padding_value)) - stacked.append(default_collate(padded_samples)) - elif batch[i].pad_dims is None: - stacked.append( - default_collate([ - sample.data - for sample in batch[i:i + samples_per_gpu] - ])) - else: - raise ValueError( - 'pad_dims should be either None or integers (1-3)') - - else: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer(stacked, batch[0].stack, batch[0].padding_value) - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - return [collate(samples, samples_per_gpu) for samples in transposed] - elif isinstance(batch[0], Mapping): - return { - key: collate([d[key] for d in batch], samples_per_gpu) - for key in batch[0] - } - else: - return default_collate(batch) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/data_container.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/data_container.py deleted file mode 100644 index cedb0d32a..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/data_container.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch - - -def assert_tensor_type(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not isinstance(args[0].data, torch.Tensor): - raise AttributeError( - f'{args[0].__class__.__name__} has no attribute ' - f'{func.__name__} for type {args[0].datatype}') - return func(*args, **kwargs) - - return wrapper - - -class DataContainer: - """A container for any type of objects. - - Typically tensors will be stacked in the collate function and sliced along - some dimension in the scatter function. This behavior has some limitations. - 1. All tensors have to be the same size. - 2. Types are limited (numpy array or Tensor). - - We design `DataContainer` and `MMDataParallel` to overcome these - limitations. The behavior can be either of the following. - - - copy to GPU, pad all tensors to the same size and stack them - - copy to GPU without stacking - - leave the objects as is and pass it to the model - - pad_dims specifies the number of last few dimensions to do padding - """ - - def __init__(self, - data, - stack=False, - padding_value=0, - cpu_only=False, - pad_dims=2): - self._data = data - self._cpu_only = cpu_only - self._stack = stack - self._padding_value = padding_value - assert pad_dims in [None, 1, 2, 3] - self._pad_dims = pad_dims - - def __repr__(self): - return f'{self.__class__.__name__}({repr(self.data)})' - - def __len__(self): - return len(self._data) - - @property - def data(self): - return self._data - - @property - def datatype(self): - if isinstance(self.data, torch.Tensor): - return self.data.type() - else: - return type(self.data) - - @property - def cpu_only(self): - return self._cpu_only - - @property - def stack(self): - return self._stack - - @property - def padding_value(self): - return self._padding_value - - @property - def pad_dims(self): - return self._pad_dims - - @assert_tensor_type - def size(self, *args, **kwargs): - return self.data.size(*args, **kwargs) - - @assert_tensor_type - def dim(self): - return self.data.dim() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/data_parallel.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/data_parallel.py deleted file mode 100644 index 7a5abeb6e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/data_parallel.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from itertools import chain - -from torch.nn.parallel import DataParallel - -from .scatter_gather import scatter_kwargs - - -class MMDataParallel(DataParallel): - """The DataParallel module that supports DataContainer. - - MMDataParallel has two main differences with PyTorch DataParallel: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data during both GPU and CPU inference. - - It implement two more APIs ``train_step()`` and ``val_step()``. - - .. warning:: - MMDataParallel only supports single GPU training, if you need to - train with multiple GPUs, please use MMDistributedDataParallel - instead. If you have multiple GPUs and you just want to use - MMDataParallel, you can set the environment variable - ``CUDA_VISIBLE_DEVICES=0`` or instantiate ``MMDataParallel`` with - ``device_ids=[0]``. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - device_ids (list[int]): Device IDS of modules to be scattered to. - Defaults to None when GPU is not available. - output_device (str | int): Device ID for output. Defaults to None. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MMDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.dim = dim - - def forward(self, *inputs, **kwargs): - """Override the original forward function. - - The main difference lies in the CPU inference where the data in - :class:`DataContainers` will still be gathered. - """ - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module(*inputs[0], **kwargs[0]) - else: - return super().forward(*inputs, **kwargs) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.train_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.train_step(*inputs[0], **kwargs[0]) - - def val_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.val_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/distributed.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/distributed.py deleted file mode 100644 index 0188ca4ab..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/distributed.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel.distributed import (DistributedDataParallel, - _find_tensors) - -from mmcv import print_log -from mmcv.utils import TORCH_VERSION, digit_version -from .scatter_gather import scatter_kwargs - - -class MMDistributedDataParallel(DistributedDataParallel): - """The DDP module that supports DataContainer. - - MMDDP has two main differences with PyTorch DDP: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data. - - It implement two APIs ``train_step()`` and ``val_step()``. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - """train_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.train_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.train_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.train_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output - - def val_step(self, *inputs, **kwargs): - """val_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.val_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.val_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.val_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/distributed_deprecated.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/distributed_deprecated.py deleted file mode 100644 index b593d4a9e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/distributed_deprecated.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn as nn -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter_kwargs - - -@MODULE_WRAPPERS.register_module() -class MMDistributedDataParallel(nn.Module): - - def __init__(self, - module, - dim=0, - broadcast_buffers=True, - bucket_cap_mb=25): - super(MMDistributedDataParallel, self).__init__() - self.module = module - self.dim = dim - self.broadcast_buffers = broadcast_buffers - - self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 - self._sync_params() - - def _dist_broadcast_coalesced(self, tensors, buffer_size): - for tensors in _take_tensors(tensors, buffer_size): - flat_tensors = _flatten_dense_tensors(tensors) - dist.broadcast(flat_tensors, 0) - for tensor, synced in zip( - tensors, _unflatten_dense_tensors(flat_tensors, tensors)): - tensor.copy_(synced) - - def _sync_params(self): - module_states = list(self.module.state_dict().values()) - if len(module_states) > 0: - self._dist_broadcast_coalesced(module_states, - self.broadcast_bucket_size) - if self.broadcast_buffers: - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) < digit_version('1.0')): - buffers = [b.data for b in self.module._all_buffers()] - else: - buffers = [b.data for b in self.module.buffers()] - if len(buffers) > 0: - self._dist_broadcast_coalesced(buffers, - self.broadcast_bucket_size) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/registry.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/registry.py deleted file mode 100644 index 144f9fb16..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel, DistributedDataParallel - -from mmcv.utils import Registry - -MODULE_WRAPPERS = Registry('module wrapper') -MODULE_WRAPPERS.register_module(module=DataParallel) -MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/scatter_gather.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/scatter_gather.py deleted file mode 100644 index 900ff8856..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import Scatter as OrigScatter - -from ._functions import Scatter -from .data_container import DataContainer - - -def scatter(inputs, target_gpus, dim=0): - """Scatter inputs to target gpus. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_gpus != [-1]: - return OrigScatter.apply(target_gpus, None, dim, obj) - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_gpus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_gpus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_gpus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_gpus, dim) if inputs else [] - kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/utils.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/utils.py deleted file mode 100644 index 0f5712cb4..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/parallel/utils.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import MODULE_WRAPPERS - - -def is_module_wrapper(module): - """Check if a module is a module wrapper. - - The following 3 modules in MMCV (and their subclasses) are regarded as - module wrappers: DataParallel, DistributedDataParallel, - MMDistributedDataParallel (the deprecated version). You may add you own - module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: True if the input module is a module wrapper. - """ - module_wrappers = tuple(MODULE_WRAPPERS.module_dict.values()) - return isinstance(module, module_wrappers) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/__init__.py deleted file mode 100644 index 183d53672..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_module import BaseModule, ModuleDict, ModuleList, Sequential -from .base_runner import BaseRunner -from .builder import RUNNERS, build_runner -from .checkpoint import (CheckpointLoader, _load_checkpoint, - _load_checkpoint_with_prefix, load_checkpoint, - load_state_dict, save_checkpoint, weights_to_cpu) -from .default_constructor import DefaultRunnerConstructor -from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, - init_dist, master_only) -from .epoch_based_runner import EpochBasedRunner, Runner -from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model -from .hooks import (HOOKS, CheckpointHook, ClearMLLoggerHook, ClosureHook, - DistEvalHook, DistSamplerSeedHook, DvcliveLoggerHook, - EMAHook, EvalHook, Fp16OptimizerHook, - GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, Hook, IterTimerHook, - LoggerHook, MlflowLoggerHook, NeptuneLoggerHook, - OptimizerHook, PaviLoggerHook, SegmindLoggerHook, - SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .hooks.lr_updater import StepLrUpdaterHook # noqa -from .hooks.lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, - InvLrUpdaterHook, LinearAnnealingLrUpdaterHook, - LrUpdaterHook, OneCycleLrUpdaterHook, - PolyLrUpdaterHook) -from .hooks.momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .iter_based_runner import IterBasedRunner, IterLoader -from .log_buffer import LogBuffer -from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, - DefaultOptimizerConstructor, build_optimizer, - build_optimizer_constructor) -from .priority import Priority, get_priority -from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed - -# initialize ipu to registor ipu runner to RUNNERS -from mmcv.device import ipu # isort:skip # noqa - -__all__ = [ - 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', - 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', - 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', - 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', - 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', - 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', - 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', - 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', - 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', - 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', - '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', - 'ModuleDict', 'ModuleList', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor', - 'SegmindLoggerHook', 'LinearAnnealingMomentumUpdaterHook', - 'LinearAnnealingLrUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/base_module.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/base_module.py deleted file mode 100644 index 7937eca37..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/base_module.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings -from abc import ABCMeta -from collections import defaultdict -from logging import FileHandler - -import torch.nn as nn - -from mmcv.runner.dist_utils import master_only -from mmcv.utils.logging import get_logger, logger_initialized, print_log - - -class BaseModule(nn.Module, metaclass=ABCMeta): - """Base module for all modules in openmmlab. - - ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional - functionality of parameter initialization. Compared with - ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. - - - ``init_cfg``: the config to control the initialization. - - ``init_weights``: The function of parameter initialization and recording - initialization information. - - ``_params_init_info``: Used to track the parameter initialization - information. This attribute only exists during executing the - ``init_weights``. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, init_cfg=None): - """Initialize BaseModule, inherited from `torch.nn.Module`""" - - # NOTE init_cfg can be defined in different levels, but init_cfg - # in low levels has a higher priority. - - super(BaseModule, self).__init__() - # define default value of init_cfg instead of hard code - # in init_weights() function - self._is_init = False - - self.init_cfg = copy.deepcopy(init_cfg) - - # Backward compatibility in derived classes - # if pretrained is not None: - # warnings.warn('DeprecationWarning: pretrained is a deprecated \ - # key, please consider using init_cfg') - # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - - @property - def is_init(self): - return self._is_init - - def init_weights(self): - """Initialize the weights.""" - - is_top_level_module = False - # check if it is top-level module - if not hasattr(self, '_params_init_info'): - # The `_params_init_info` is used to record the initialization - # information of the parameters - # the key should be the obj:`nn.Parameter` of model and the value - # should be a dict containing - # - init_info (str): The string that describes the initialization. - # - tmp_mean_value (FloatTensor): The mean of the parameter, - # which indicates whether the parameter has been modified. - # this attribute would be deleted after all parameters - # is initialized. - self._params_init_info = defaultdict(dict) - is_top_level_module = True - - # Initialize the `_params_init_info`, - # When detecting the `tmp_mean_value` of - # the corresponding parameter is changed, update related - # initialization information - for name, param in self.named_parameters(): - self._params_init_info[param][ - 'init_info'] = f'The value is the same before and ' \ - f'after calling `init_weights` ' \ - f'of {self.__class__.__name__} ' - self._params_init_info[param][ - 'tmp_mean_value'] = param.data.mean() - - # pass `params_init_info` to all submodules - # All submodules share the same `params_init_info`, - # so it will be updated when parameters are - # modified at any level of the model. - for sub_module in self.modules(): - sub_module._params_init_info = self._params_init_info - - # Get the initialized logger, if not exist, - # create a logger named `mmcv` - logger_names = list(logger_initialized.keys()) - logger_name = logger_names[0] if logger_names else 'mmcv' - - from ..cnn import initialize - from ..cnn.utils.weight_init import update_init_info - module_name = self.__class__.__name__ - if not self._is_init: - if self.init_cfg: - print_log( - f'initialize {module_name} with init_cfg {self.init_cfg}', - logger=logger_name) - initialize(self, self.init_cfg) - if isinstance(self.init_cfg, dict): - # prevent the parameters of - # the pre-trained model - # from being overwritten by - # the `init_weights` - if self.init_cfg['type'] == 'Pretrained': - return - - for m in self.children(): - if hasattr(m, 'init_weights'): - m.init_weights() - # users may overload the `init_weights` - update_init_info( - m, - init_info=f'Initialized by ' - f'user-defined `init_weights`' - f' in {m.__class__.__name__} ') - - self._is_init = True - else: - warnings.warn(f'init_weights of {self.__class__.__name__} has ' - f'been called more than once.') - - if is_top_level_module: - self._dump_init_info(logger_name) - - for sub_module in self.modules(): - del sub_module._params_init_info - - @master_only - def _dump_init_info(self, logger_name): - """Dump the initialization information to a file named - `initialization.log.json` in workdir. - - Args: - logger_name (str): The name of logger. - """ - - logger = get_logger(logger_name) - - with_file_handler = False - # dump the information to the logger file if there is a `FileHandler` - for handler in logger.handlers: - if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True - if not with_file_handler: - for name, param in self.named_parameters(): - print_log( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n ", - logger=logger_name) - - def __repr__(self): - s = super().__repr__() - if self.init_cfg: - s += f'\ninit_cfg={self.init_cfg}' - return s - - -class Sequential(BaseModule, nn.Sequential): - """Sequential module in openmmlab. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, *args, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.Sequential.__init__(self, *args) - - -class ModuleList(BaseModule, nn.ModuleList): - """ModuleList in openmmlab. - - Args: - modules (iterable, optional): an iterable of modules to add. - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleList.__init__(self, modules) - - -class ModuleDict(BaseModule, nn.ModuleDict): - """ModuleDict in openmmlab. - - Args: - modules (dict, optional): a mapping (dictionary) of (string: module) - or an iterable of key-value pairs of type (string, module). - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleDict.__init__(self, modules) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/base_runner.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/base_runner.py deleted file mode 100644 index 12a0025f8..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/base_runner.py +++ /dev/null @@ -1,544 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -import os.path as osp -import warnings -from abc import ABCMeta, abstractmethod - -import torch -from torch.optim import Optimizer - -import mmcv -from ..parallel import is_module_wrapper -from .checkpoint import load_checkpoint -from .dist_utils import get_dist_info -from .hooks import HOOKS, Hook -from .log_buffer import LogBuffer -from .priority import Priority, get_priority -from .utils import get_time_str - - -class BaseRunner(metaclass=ABCMeta): - """The base class of Runner, a training helper for PyTorch. - - All subclasses should implement the following APIs: - - - ``run()`` - - ``train()`` - - ``val()`` - - ``save_checkpoint()`` - - Args: - model (:obj:`torch.nn.Module`): The model to be run. - batch_processor (callable): A callable method that process a data - batch. The interface of this method should be - `batch_processor(model, data, train_mode) -> dict` - optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an - optimizer (in most cases) or a dict of optimizers (in models that - requires more than one optimizer, e.g., GAN). - work_dir (str, optional): The working directory to save checkpoints - and logs. Defaults to None. - logger (:obj:`logging.Logger`): Logger used during training. - Defaults to None. (The default value is just for backward - compatibility) - meta (dict | None): A dict records some import information such as - environment info and seed, which will be logged in logger hook. - Defaults to None. - max_epochs (int, optional): Total training epochs. - max_iters (int, optional): Total training iterations. - """ - - def __init__(self, - model, - batch_processor=None, - optimizer=None, - work_dir=None, - logger=None, - meta=None, - max_iters=None, - max_epochs=None): - if batch_processor is not None: - if not callable(batch_processor): - raise TypeError('batch_processor must be callable, ' - f'but got {type(batch_processor)}') - warnings.warn( - 'batch_processor is deprecated, please implement ' - 'train_step() and val_step() in the model instead.', - DeprecationWarning) - # raise an error is `batch_processor` is not None and - # `model.train_step()` exists. - if is_module_wrapper(model): - _model = model.module - else: - _model = model - if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): - raise RuntimeError( - 'batch_processor and model.train_step()/model.val_step() ' - 'cannot be both available.') - else: - assert hasattr(model, 'train_step') - - # check the type of `optimizer` - if isinstance(optimizer, dict): - for name, optim in optimizer.items(): - if not isinstance(optim, Optimizer): - raise TypeError( - f'optimizer must be a dict of torch.optim.Optimizers, ' - f'but optimizer["{name}"] is a {type(optim)}') - elif not isinstance(optimizer, Optimizer) and optimizer is not None: - raise TypeError( - f'optimizer must be a torch.optim.Optimizer object ' - f'or dict or None, but got {type(optimizer)}') - - # check the type of `logger` - if not isinstance(logger, logging.Logger): - raise TypeError(f'logger must be a logging.Logger object, ' - f'but got {type(logger)}') - - # check the type of `meta` - if meta is not None and not isinstance(meta, dict): - raise TypeError( - f'meta must be a dict or None, but got {type(meta)}') - - self.model = model - self.batch_processor = batch_processor - self.optimizer = optimizer - self.logger = logger - self.meta = meta - # create work_dir - if mmcv.is_str(work_dir): - self.work_dir = osp.abspath(work_dir) - mmcv.mkdir_or_exist(self.work_dir) - elif work_dir is None: - self.work_dir = None - else: - raise TypeError('"work_dir" must be a str or None') - - # get model name from the model class - if hasattr(self.model, 'module'): - self._model_name = self.model.module.__class__.__name__ - else: - self._model_name = self.model.__class__.__name__ - - self._rank, self._world_size = get_dist_info() - self.timestamp = get_time_str() - self.mode = None - self._hooks = [] - self._epoch = 0 - self._iter = 0 - self._inner_iter = 0 - - if max_epochs is not None and max_iters is not None: - raise ValueError( - 'Only one of `max_epochs` or `max_iters` can be set.') - - self._max_epochs = max_epochs - self._max_iters = max_iters - # TODO: Redesign LogBuffer, it is not flexible and elegant enough - self.log_buffer = LogBuffer() - - @property - def model_name(self): - """str: Name of the model, usually the module class name.""" - return self._model_name - - @property - def rank(self): - """int: Rank of current process. (distributed training)""" - return self._rank - - @property - def world_size(self): - """int: Number of processes participating in the job. - (distributed training)""" - return self._world_size - - @property - def hooks(self): - """list[:obj:`Hook`]: A list of registered hooks.""" - return self._hooks - - @property - def epoch(self): - """int: Current epoch.""" - return self._epoch - - @property - def iter(self): - """int: Current iteration.""" - return self._iter - - @property - def inner_iter(self): - """int: Iteration in an epoch.""" - return self._inner_iter - - @property - def max_epochs(self): - """int: Maximum training epochs.""" - return self._max_epochs - - @property - def max_iters(self): - """int: Maximum training iterations.""" - return self._max_iters - - @abstractmethod - def train(self): - pass - - @abstractmethod - def val(self): - pass - - @abstractmethod - def run(self, data_loaders, workflow, **kwargs): - pass - - @abstractmethod - def save_checkpoint(self, - out_dir, - filename_tmpl, - save_optimizer=True, - meta=None, - create_symlink=True): - pass - - def current_lr(self): - """Get current learning rates. - - Returns: - list[float] | dict[str, list[float]]: Current learning rates of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - if isinstance(self.optimizer, torch.optim.Optimizer): - lr = [group['lr'] for group in self.optimizer.param_groups] - elif isinstance(self.optimizer, dict): - lr = dict() - for name, optim in self.optimizer.items(): - lr[name] = [group['lr'] for group in optim.param_groups] - else: - raise RuntimeError( - 'lr is not applicable because optimizer does not exist.') - return lr - - def current_momentum(self): - """Get current momentums. - - Returns: - list[float] | dict[str, list[float]]: Current momentums of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - - def _get_momentum(optimizer): - momentums = [] - for group in optimizer.param_groups: - if 'momentum' in group.keys(): - momentums.append(group['momentum']) - elif 'betas' in group.keys(): - momentums.append(group['betas'][0]) - else: - momentums.append(0) - return momentums - - if self.optimizer is None: - raise RuntimeError( - 'momentum is not applicable because optimizer does not exist.') - elif isinstance(self.optimizer, torch.optim.Optimizer): - momentums = _get_momentum(self.optimizer) - elif isinstance(self.optimizer, dict): - momentums = dict() - for name, optim in self.optimizer.items(): - momentums[name] = _get_momentum(optim) - return momentums - - def register_hook(self, hook, priority='NORMAL'): - """Register a hook into the hook list. - - The hook will be inserted into a priority queue, with the specified - priority (See :class:`Priority` for details of priorities). - For hooks with the same priority, they will be triggered in the same - order as they are registered. - - Args: - hook (:obj:`Hook`): The hook to be registered. - priority (int or str or :obj:`Priority`): Hook priority. - Lower value means higher priority. - """ - assert isinstance(hook, Hook) - if hasattr(hook, 'priority'): - raise ValueError('"priority" is a reserved attribute for hooks') - priority = get_priority(priority) - hook.priority = priority - # insert the hook to a sorted list - inserted = False - for i in range(len(self._hooks) - 1, -1, -1): - if priority >= self._hooks[i].priority: - self._hooks.insert(i + 1, hook) - inserted = True - break - if not inserted: - self._hooks.insert(0, hook) - - def register_hook_from_cfg(self, hook_cfg): - """Register a hook from its cfg. - - Args: - hook_cfg (dict): Hook config. It should have at least keys 'type' - and 'priority' indicating its type and priority. - - Note: - The specific hook class to register should not use 'type' and - 'priority' arguments during initialization. - """ - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = mmcv.build_from_cfg(hook_cfg, HOOKS) - self.register_hook(hook, priority=priority) - - def call_hook(self, fn_name): - """Call all hooks. - - Args: - fn_name (str): The function name in each hook to be called, such as - "before_train_epoch". - """ - for hook in self._hooks: - getattr(hook, fn_name)(self) - - def get_hook_info(self): - # Get hooks info in each stage - stage_hook_map = {stage: [] for stage in Hook.stages} - for hook in self.hooks: - try: - priority = Priority(hook.priority).name - except ValueError: - priority = hook.priority - classname = hook.__class__.__name__ - hook_info = f'({priority:<12}) {classname:<35}' - for trigger_stage in hook.get_triggered_stages(): - stage_hook_map[trigger_stage].append(hook_info) - - stage_hook_infos = [] - for stage in Hook.stages: - hook_infos = stage_hook_map[stage] - if len(hook_infos) > 0: - info = f'{stage}:\n' - info += '\n'.join(hook_infos) - info += '\n -------------------- ' - stage_hook_infos.append(info) - return '\n'.join(stage_hook_infos) - - def load_checkpoint(self, - filename, - map_location='cpu', - strict=False, - revise_keys=[(r'^module.', '')]): - return load_checkpoint( - self.model, - filename, - map_location, - strict, - self.logger, - revise_keys=revise_keys) - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - if map_location == 'default': - if torch.cuda.is_available(): - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint(checkpoint) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - if self.meta is None: - self.meta = {} - self.meta.setdefault('hook_msgs', {}) - # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages - self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) - - # Re-calculate the number of iterations when resuming - # models with different number of GPUs - if 'config' in checkpoint['meta']: - config = mmcv.Config.fromstring( - checkpoint['meta']['config'], file_format='.py') - previous_gpu_ids = config.get('gpu_ids', None) - if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( - previous_gpu_ids) != self.world_size: - self._iter = int(self._iter * len(previous_gpu_ids) / - self.world_size) - self.logger.info('the iteration number is changed due to ' - 'change of GPU number') - - # resume meta information meta - self.meta = checkpoint['meta'] - - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - elif isinstance(lr_config, dict): - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = mmcv.build_from_cfg(lr_config, HOOKS) - else: - hook = lr_config - self.register_hook(hook, priority='VERY_HIGH') - - def register_momentum_hook(self, momentum_config): - if momentum_config is None: - return - if isinstance(momentum_config, dict): - assert 'policy' in momentum_config - policy_type = momentum_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of momentum updater. - # Since this is not applicable for - # `CosineAnnealingMomentumUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'MomentumUpdaterHook' - momentum_config['type'] = hook_type - hook = mmcv.build_from_cfg(momentum_config, HOOKS) - else: - hook = momentum_config - self.register_hook(hook, priority='HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = mmcv.build_from_cfg(optimizer_config, HOOKS) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def register_checkpoint_hook(self, checkpoint_config): - if checkpoint_config is None: - return - if isinstance(checkpoint_config, dict): - checkpoint_config.setdefault('type', 'CheckpointHook') - hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) - else: - hook = checkpoint_config - self.register_hook(hook, priority='NORMAL') - - def register_logger_hooks(self, log_config): - if log_config is None: - return - log_interval = log_config['interval'] - for info in log_config['hooks']: - logger_hook = mmcv.build_from_cfg( - info, HOOKS, default_args=dict(interval=log_interval)) - self.register_hook(logger_hook, priority='VERY_LOW') - - def register_timer_hook(self, timer_config): - if timer_config is None: - return - if isinstance(timer_config, dict): - timer_config_ = copy.deepcopy(timer_config) - hook = mmcv.build_from_cfg(timer_config_, HOOKS) - else: - hook = timer_config - self.register_hook(hook, priority='LOW') - - def register_custom_hooks(self, custom_config): - if custom_config is None: - return - - if not isinstance(custom_config, list): - custom_config = [custom_config] - - for item in custom_config: - if isinstance(item, dict): - self.register_hook_from_cfg(item) - else: - self.register_hook(item, priority='NORMAL') - - def register_profiler_hook(self, profiler_config): - if profiler_config is None: - return - if isinstance(profiler_config, dict): - profiler_config.setdefault('type', 'ProfilerHook') - hook = mmcv.build_from_cfg(profiler_config, HOOKS) - else: - hook = profiler_config - self.register_hook(hook) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - timer_config=dict(type='IterTimerHook'), - custom_hooks_config=None): - """Register default and custom hooks for training. - - Default and custom hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - self.register_lr_hook(lr_config) - self.register_momentum_hook(momentum_config) - self.register_optimizer_hook(optimizer_config) - self.register_checkpoint_hook(checkpoint_config) - self.register_timer_hook(timer_config) - self.register_logger_hooks(log_config) - self.register_custom_hooks(custom_hooks_config) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/builder.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/builder.py deleted file mode 100644 index 77c96ba0b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/builder.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from ..utils import Registry - -RUNNERS = Registry('runner') -RUNNER_BUILDERS = Registry('runner builder') - - -def build_runner_constructor(cfg): - return RUNNER_BUILDERS.build(cfg) - - -def build_runner(cfg, default_args=None): - runner_cfg = copy.deepcopy(cfg) - constructor_type = runner_cfg.pop('constructor', - 'DefaultRunnerConstructor') - runner_constructor = build_runner_constructor( - dict( - type=constructor_type, - runner_cfg=runner_cfg, - default_args=default_args)) - runner = runner_constructor() - return runner diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/checkpoint.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/checkpoint.py deleted file mode 100644 index 835ee725a..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/checkpoint.py +++ /dev/null @@ -1,759 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os -import os.path as osp -import pkgutil -import re -import time -import warnings -from collections import OrderedDict -from importlib import import_module -from tempfile import TemporaryDirectory - -import torch -import torchvision -from torch.optim import Optimizer - -import mmcv -from ..fileio import FileClient -from ..fileio import load as load_file -from ..parallel import is_module_wrapper -from ..utils import digit_version, load_url, mkdir_or_exist -from .dist_utils import get_dist_info - -ENV_MMCV_HOME = 'MMCV_HOME' -ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' -DEFAULT_CACHE_DIR = '~/.cache' - - -def _get_mmcv_home(): - mmcv_home = os.path.expanduser( - os.getenv( - ENV_MMCV_HOME, - os.path.join( - os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) - - mkdir_or_exist(mmcv_home) - return mmcv_home - - -def load_state_dict(module, state_dict, strict=False, logger=None): - """Load state_dict to a module. - - This method is modified from :meth:`torch.nn.Module.load_state_dict`. - Default value for ``strict`` is set to ``False`` and the message for - param mismatch will be shown even if strict is False. - - Args: - module (Module): Module that receives the state_dict. - state_dict (OrderedDict): Weights. - strict (bool): whether to strictly enforce that the keys - in :attr:`state_dict` match the keys returned by this module's - :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. - logger (:obj:`logging.Logger`, optional): Logger to log the error - message. If not specified, print function will be used. - """ - unexpected_keys = [] - all_missing_keys = [] - err_msg = [] - - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata - - # use _load_from_state_dict to enable checkpoint version control - def load(module, prefix=''): - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - local_metadata = {} if metadata is None else metadata.get( - prefix[:-1], {}) - module._load_from_state_dict(state_dict, prefix, local_metadata, True, - all_missing_keys, unexpected_keys, - err_msg) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(module) - load = None # break load->load reference cycle - - # ignore "num_batches_tracked" of BN layers - missing_keys = [ - key for key in all_missing_keys if 'num_batches_tracked' not in key - ] - - if unexpected_keys: - err_msg.append('unexpected key in source ' - f'state_dict: {", ".join(unexpected_keys)}\n') - if missing_keys: - err_msg.append( - f'missing keys in source state_dict: {", ".join(missing_keys)}\n') - - rank, _ = get_dist_info() - if len(err_msg) > 0 and rank == 0: - err_msg.insert( - 0, 'The model and loaded state dict do not match exactly\n') - err_msg = '\n'.join(err_msg) - if strict: - raise RuntimeError(err_msg) - elif logger is not None: - logger.warning(err_msg) - else: - print(err_msg) - - -def get_torchvision_models(): - if digit_version(torchvision.__version__) < digit_version('0.13.0a0'): - model_urls = dict() - # When the version of torchvision is lower than 0.13, the model url is - # not declared in `torchvision.model.__init__.py`, so we need to - # iterate through `torchvision.models.__path__` to get the url for each - # model. - for _, name, ispkg in pkgutil.walk_packages( - torchvision.models.__path__): - if ispkg: - continue - _zoo = import_module(f'torchvision.models.{name}') - if hasattr(_zoo, 'model_urls'): - _urls = getattr(_zoo, 'model_urls') - model_urls.update(_urls) - else: - # Since torchvision bumps to v0.13, the weight loading logic, - # model keys and model urls have been changed. Here the URLs of old - # version is loaded to avoid breaking back compatibility. If the - # torchvision version>=0.13.0, new URLs will be added. Users can get - # the resnet50 checkpoint by setting 'resnet50.imagent1k_v1', - # 'resnet50' or 'ResNet50_Weights.IMAGENET1K_V1' in the config. - json_path = osp.join(mmcv.__path__[0], - 'model_zoo/torchvision_0.12.json') - model_urls = mmcv.load(json_path) - for cls_name, cls in torchvision.models.__dict__.items(): - # The name of torchvision model weights classes ends with - # `_Weights` such as `ResNet18_Weights`. However, some model weight - # classes, such as `MNASNet0_75_Weights` does not have any urls in - # torchvision 0.13.0 and cannot be iterated. Here we simply check - # `DEFAULT` attribute to ensure the class is not empty. - if (not cls_name.endswith('_Weights') - or not hasattr(cls, 'DEFAULT')): - continue - # Since `cls.DEFAULT` can not be accessed by iterating cls, we set - # default urls explicitly. - cls_key = cls_name.replace('_Weights', '').lower() - model_urls[f'{cls_key}.default'] = cls.DEFAULT.url - for weight_enum in cls: - cls_key = cls_name.replace('_Weights', '').lower() - cls_key = f'{cls_key}.{weight_enum.name.lower()}' - model_urls[cls_key] = weight_enum.url - - return model_urls - - -def get_external_models(): - mmcv_home = _get_mmcv_home() - default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') - default_urls = load_file(default_json_path) - assert isinstance(default_urls, dict) - external_json_path = osp.join(mmcv_home, 'open_mmlab.json') - if osp.exists(external_json_path): - external_urls = load_file(external_json_path) - assert isinstance(external_urls, dict) - default_urls.update(external_urls) - - return default_urls - - -def get_mmcls_models(): - mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') - mmcls_urls = load_file(mmcls_json_path) - - return mmcls_urls - - -def get_deprecated_model_names(): - deprecate_json_path = osp.join(mmcv.__path__[0], - 'model_zoo/deprecated.json') - deprecate_urls = load_file(deprecate_json_path) - assert isinstance(deprecate_urls, dict) - - return deprecate_urls - - -def _process_mmcls_checkpoint(checkpoint): - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - # Some checkpoints converted from 3rd-party repo don't - # have the "state_dict" key. - state_dict = checkpoint - new_state_dict = OrderedDict() - for k, v in state_dict.items(): - if k.startswith('backbone.'): - new_state_dict[k[9:]] = v - new_checkpoint = dict(state_dict=new_state_dict) - - return new_checkpoint - - -class CheckpointLoader: - """A general checkpoint loader to manage all schemes.""" - - _schemes = {} - - @classmethod - def _register_scheme(cls, prefixes, loader, force=False): - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if (prefix not in cls._schemes) or force: - cls._schemes[prefix] = loader - else: - raise KeyError( - f'{prefix} is already registered as a loader backend, ' - 'add "force=True" if you want to override it') - # sort, longer prefixes take priority - cls._schemes = OrderedDict( - sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) - - @classmethod - def register_scheme(cls, prefixes, loader=None, force=False): - """Register a loader to CheckpointLoader. - - This method can be used as a normal class method or a decorator. - - Args: - prefixes (str or list[str] or tuple[str]): - The prefix of the registered loader. - loader (function, optional): The loader function to be registered. - When this method is used as a decorator, loader is None. - Defaults to None. - force (bool, optional): Whether to override the loader - if the prefix has already been registered. Defaults to False. - """ - - if loader is not None: - cls._register_scheme(prefixes, loader, force=force) - return - - def _register(loader_cls): - cls._register_scheme(prefixes, loader_cls, force=force) - return loader_cls - - return _register - - @classmethod - def _get_checkpoint_loader(cls, path): - """Finds a loader that supports the given path. Falls back to the local - loader if no other loader is found. - - Args: - path (str): checkpoint path - - Returns: - callable: checkpoint loader - """ - for p in cls._schemes: - # use regular match to handle some cases that where the prefix of - # loader has a prefix. For example, both 's3://path' and - # 'open-mmlab:s3://path' should return `load_from_ceph` - if re.match(p, path) is not None: - return cls._schemes[p] - - @classmethod - def load_checkpoint(cls, filename, map_location=None, logger=None): - """load checkpoint through URL scheme path. - - Args: - filename (str): checkpoint file name with given prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - logger (:mod:`logging.Logger`, optional): The logger for message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint_loader = cls._get_checkpoint_loader(filename) - class_name = checkpoint_loader.__name__ - mmcv.print_log( - f'load checkpoint from {class_name[10:]} path: {filename}', logger) - return checkpoint_loader(filename, map_location) - - -@CheckpointLoader.register_scheme(prefixes='') -def load_from_local(filename, map_location): - """load checkpoint by local file path. - - Args: - filename (str): local checkpoint file path - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - filename = osp.expanduser(filename) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) -def load_from_http(filename, map_location=None, model_dir=None): - """load checkpoint through HTTP or HTTPS scheme path. In distributed - setting, this function only download checkpoint at local rank 0. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - model_dir (string, optional): directory in which to save the object, - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - rank, world_size = get_dist_info() - if rank == 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - if world_size > 1: - torch.distributed.barrier() - if rank > 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='pavi://') -def load_from_pavi(filename, map_location=None): - """load checkpoint through the file path prefixed with pavi. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with pavi prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - assert filename.startswith('pavi://'), \ - f'Expected filename startswith `pavi://`, but get {filename}' - model_path = filename[7:] - - try: - from pavi import modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - - model = modelcloud.get(model_path) - with TemporaryDirectory() as tmp_dir: - downloaded_file = osp.join(tmp_dir, model.name) - model.download(downloaded_file) - checkpoint = torch.load(downloaded_file, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=r'(\S+\:)?s3://') -def load_from_ceph(filename, map_location=None, backend='petrel'): - """load checkpoint through the file path prefixed with s3. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Note: - Since v1.4.1, the registered scheme prefixes have been enhanced to - support bucket names in the path prefix, e.g. 's3://xx.xx/xx.path', - 'bucket1:s3://xx.xx/xx.path'. - - Args: - filename (str): checkpoint file path with s3 prefix - map_location (str, optional): Same as :func:`torch.load`. - backend (str, optional): The storage backend type. Options are 'ceph', - 'petrel'. Default: 'petrel'. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - allowed_backends = ['ceph', 'petrel'] - if backend not in allowed_backends: - raise ValueError(f'Load from Backend {backend} is not supported.') - - if backend == 'ceph': - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - - # CephClient and PetrelBackend have the same prefix 's3://' and the latter - # will be chosen as default. If PetrelBackend can not be instantiated - # successfully, the CephClient will be chosen. - try: - file_client = FileClient(backend=backend) - except ImportError: - allowed_backends.remove(backend) - file_client = FileClient(backend=allowed_backends[0]) - - with io.BytesIO(file_client.get(filename)) as buffer: - checkpoint = torch.load(buffer, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) -def load_from_torchvision(filename, map_location=None): - """load checkpoint through the file path prefixed with modelzoo or - torchvision. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - model_urls = get_torchvision_models() - if filename.startswith('modelzoo://'): - warnings.warn( - 'The URL scheme of "modelzoo://" is deprecated, please ' - 'use "torchvision://" instead', DeprecationWarning) - model_name = filename[11:] - else: - model_name = filename[14:] - - # Support getting model urls in the same way as torchvision - # `ResNet50_Weights.IMAGENET1K_V1` will be mapped to - # resnet50.imagenet1k_v1. - model_name = model_name.lower().replace('_weights', '') - return load_from_http(model_urls[model_name], map_location=map_location) - - -@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) -def load_from_openmmlab(filename, map_location=None): - """load checkpoint through the file path prefixed with open-mmlab or - openmmlab. - - Args: - filename (str): checkpoint file path with open-mmlab or - openmmlab prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_external_models() - prefix_str = 'open-mmlab://' - if filename.startswith(prefix_str): - model_name = filename[13:] - else: - model_name = filename[12:] - prefix_str = 'openmmlab://' - - deprecated_urls = get_deprecated_model_names() - if model_name in deprecated_urls: - warnings.warn( - f'{prefix_str}{model_name} is deprecated in favor ' - f'of {prefix_str}{deprecated_urls[model_name]}', - DeprecationWarning) - model_name = deprecated_urls[model_name] - model_url = model_urls[model_name] - # check if is url - if model_url.startswith(('http://', 'https://')): - checkpoint = load_from_http(model_url, map_location=map_location) - else: - filename = osp.join(_get_mmcv_home(), model_url) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='mmcls://') -def load_from_mmcls(filename, map_location=None): - """load checkpoint through the file path prefixed with mmcls. - - Args: - filename (str): checkpoint file path with mmcls prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_mmcls_models() - model_name = filename[8:] - checkpoint = load_from_http( - model_urls[model_name], map_location=map_location) - checkpoint = _process_mmcls_checkpoint(checkpoint) - return checkpoint - - -def _load_checkpoint(filename, map_location=None, logger=None): - """Load checkpoint from somewhere (modelzoo, file, url). - - Args: - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str, optional): Same as :func:`torch.load`. - Default: None. - logger (:mod:`logging.Logger`, optional): The logger for error message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. It can be either an - OrderedDict storing model weights or a dict containing other - information, which depends on the checkpoint. - """ - return CheckpointLoader.load_checkpoint(filename, map_location, logger) - - -def _load_checkpoint_with_prefix(prefix, filename, map_location=None): - """Load partial pretrained model with specific prefix. - - Args: - prefix (str): The prefix of sub-module. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str | None): Same as :func:`torch.load`. Default: None. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint = _load_checkpoint(filename, map_location=map_location) - - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - if not prefix.endswith('.'): - prefix += '.' - prefix_len = len(prefix) - - state_dict = { - k[prefix_len:]: v - for k, v in state_dict.items() if k.startswith(prefix) - } - - assert state_dict, f'{prefix} is not in the pretrained model' - return state_dict - - -def load_checkpoint(model, - filename, - map_location=None, - strict=False, - logger=None, - revise_keys=[(r'^module\.', '')]): - """Load checkpoint from a file or URI. - - Args: - model (Module): Module to load checkpoint. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str): Same as :func:`torch.load`. - strict (bool): Whether to allow different params for the model and - checkpoint. - logger (:mod:`logging.Logger` or None): The logger for error message. - revise_keys (list): A list of customized keywords to modify the - state_dict in checkpoint. Each item is a (pattern, replacement) - pair of the regular expression operations. Default: strip - the prefix 'module.' by [(r'^module\\.', '')]. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - checkpoint = _load_checkpoint(filename, map_location, logger) - # OrderedDict is a subclass of dict - if not isinstance(checkpoint, dict): - raise RuntimeError( - f'No state_dict found in checkpoint file {filename}') - # get state_dict from checkpoint - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - - # strip prefix of state_dict - metadata = getattr(state_dict, '_metadata', OrderedDict()) - for p, r in revise_keys: - state_dict = OrderedDict( - {re.sub(p, r, k): v - for k, v in state_dict.items()}) - # Keep metadata in state_dict - state_dict._metadata = metadata - - # load state_dict - load_state_dict(model, state_dict, strict, logger) - return checkpoint - - -def weights_to_cpu(state_dict): - """Copy a model state_dict to cpu. - - Args: - state_dict (OrderedDict): Model weights on GPU. - - Returns: - OrderedDict: Model weights on GPU. - """ - state_dict_cpu = OrderedDict() - for key, val in state_dict.items(): - state_dict_cpu[key] = val.cpu() - # Keep metadata in state_dict - state_dict_cpu._metadata = getattr(state_dict, '_metadata', OrderedDict()) - return state_dict_cpu - - -def _save_to_state_dict(module, destination, prefix, keep_vars): - """Saves module state to `destination` dictionary. - - This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. - - Args: - module (nn.Module): The module to generate state_dict. - destination (dict): A dict where state will be stored. - prefix (str): The prefix for parameters and buffers used in this - module. - """ - for name, param in module._parameters.items(): - if param is not None: - destination[prefix + name] = param if keep_vars else param.detach() - for name, buf in module._buffers.items(): - # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d - if buf is not None: - destination[prefix + name] = buf if keep_vars else buf.detach() - - -def get_state_dict(module, destination=None, prefix='', keep_vars=False): - """Returns a dictionary containing a whole state of the module. - - Both parameters and persistent buffers (e.g. running averages) are - included. Keys are corresponding parameter and buffer names. - - This method is modified from :meth:`torch.nn.Module.state_dict` to - recursively check parallel module in case that the model has a complicated - structure, e.g., nn.Module(nn.Module(DDP)). - - Args: - module (nn.Module): The module to generate state_dict. - destination (OrderedDict): Returned dict for the state of the - module. - prefix (str): Prefix of the key. - keep_vars (bool): Whether to keep the variable property of the - parameters. Default: False. - - Returns: - dict: A dictionary containing a whole state of the module. - """ - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - - # below is the same as torch.nn.Module.state_dict() - if destination is None: - destination = OrderedDict() - destination._metadata = OrderedDict() - destination._metadata[prefix[:-1]] = local_metadata = dict( - version=module._version) - _save_to_state_dict(module, destination, prefix, keep_vars) - for name, child in module._modules.items(): - if child is not None: - get_state_dict( - child, destination, prefix + name + '.', keep_vars=keep_vars) - for hook in module._state_dict_hooks.values(): - hook_result = hook(module, destination, prefix, local_metadata) - if hook_result is not None: - destination = hook_result - return destination - - -def save_checkpoint(model, - filename, - optimizer=None, - meta=None, - file_client_args=None): - """Save checkpoint to file. - - The checkpoint will have 3 fields: ``meta``, ``state_dict`` and - ``optimizer``. By default ``meta`` will contain version and time info. - - Args: - model (Module): Module whose params are to be saved. - filename (str): Checkpoint filename. - optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. - meta (dict, optional): Metadata to be saved in checkpoint. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError(f'meta must be a dict or None, but got {type(meta)}') - meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) - - if is_module_wrapper(model): - model = model.module - - if hasattr(model, 'CLASSES') and model.CLASSES is not None: - # save class name to the meta - meta.update(CLASSES=model.CLASSES) - - checkpoint = { - 'meta': meta, - 'state_dict': weights_to_cpu(get_state_dict(model)) - } - # save optimizer state dict in the checkpoint - if isinstance(optimizer, Optimizer): - checkpoint['optimizer'] = optimizer.state_dict() - elif isinstance(optimizer, dict): - checkpoint['optimizer'] = {} - for name, optim in optimizer.items(): - checkpoint['optimizer'][name] = optim.state_dict() - - if filename.startswith('pavi://'): - if file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" if filename starts with' - f'"pavi://", but got {file_client_args}') - try: - from pavi import exception, modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - model_path = filename[7:] - root = modelcloud.Folder() - model_dir, model_name = osp.split(model_path) - try: - model = modelcloud.get(model_dir) - except exception.NodeNotFoundError: - model = root.create_training_model(model_dir) - with TemporaryDirectory() as tmp_dir: - checkpoint_file = osp.join(tmp_dir, model_name) - with open(checkpoint_file, 'wb') as f: - torch.save(checkpoint, f) - f.flush() - model.create_file(checkpoint_file, name=model_name) - else: - file_client = FileClient.infer_client(file_client_args, filename) - with io.BytesIO() as f: - torch.save(checkpoint, f) - file_client.put(f.getvalue(), filename) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/default_constructor.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/default_constructor.py deleted file mode 100644 index 4a4f2cc64..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/default_constructor.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import RUNNER_BUILDERS, RUNNERS - - -@RUNNER_BUILDERS.register_module() -class DefaultRunnerConstructor: - """Default constructor for runners. - - Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. - For example, We can inject some new properties and functions for `Runner`. - - Example: - >>> from mmcv.runner import RUNNER_BUILDERS, build_runner - >>> # Define a new RunnerReconstructor - >>> @RUNNER_BUILDERS.register_module() - >>> class MyRunnerConstructor: - ... def __init__(self, runner_cfg, default_args=None): - ... if not isinstance(runner_cfg, dict): - ... raise TypeError('runner_cfg should be a dict', - ... f'but got {type(runner_cfg)}') - ... self.runner_cfg = runner_cfg - ... self.default_args = default_args - ... - ... def __call__(self): - ... runner = RUNNERS.build(self.runner_cfg, - ... default_args=self.default_args) - ... # Add new properties for existing runner - ... runner.my_name = 'my_runner' - ... runner.my_function = lambda self: print(self.my_name) - ... ... - >>> # build your runner - >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, - ... constructor='MyRunnerConstructor') - >>> runner = build_runner(runner_cfg) - """ - - def __init__(self, runner_cfg, default_args=None): - if not isinstance(runner_cfg, dict): - raise TypeError('runner_cfg should be a dict', - f'but got {type(runner_cfg)}') - self.runner_cfg = runner_cfg - self.default_args = default_args - - def __call__(self): - return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/dist_utils.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/dist_utils.py deleted file mode 100644 index 26d77a8f9..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/dist_utils.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved -import functools -import os -import socket -import subprocess -from collections import OrderedDict - -import torch -import torch.multiprocessing as mp -from torch import distributed as dist -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import IS_MLU_AVAILABLE - - -def _find_free_port(): - # Copied from https://github.com/facebookresearch/detectron2/blob/main/detectron2/engine/launch.py # noqa: E501 - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - # Binding to port 0 will cause the OS to find an available port for us - sock.bind(('', 0)) - port = sock.getsockname()[1] - sock.close() - # NOTE: there is still a chance the port could be taken by other processes. - return port - - -def _is_free_port(port): - ips = socket.gethostbyname_ex(socket.gethostname())[-1] - ips.append('localhost') - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - return all(s.connect_ex((ip, port)) != 0 for ip in ips) - - -def init_dist(launcher, backend='nccl', **kwargs): - if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') - if launcher == 'pytorch': - _init_dist_pytorch(backend, **kwargs) - elif launcher == 'mpi': - _init_dist_mpi(backend, **kwargs) - elif launcher == 'slurm': - _init_dist_slurm(backend, **kwargs) - else: - raise ValueError(f'Invalid launcher type: {launcher}') - - -def _init_dist_pytorch(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['RANK']) - if IS_MLU_AVAILABLE: - import torch_mlu # noqa: F401 - torch.mlu.set_device(rank) - dist.init_process_group( - backend='cncl', - rank=rank, - world_size=int(os.environ['WORLD_SIZE']), - **kwargs) - else: - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_mpi(backend, **kwargs): - local_rank = int(os.environ['OMPI_COMM_WORLD_LOCAL_RANK']) - torch.cuda.set_device(local_rank) - if 'MASTER_PORT' not in os.environ: - # 29500 is torch.distributed default port - os.environ['MASTER_PORT'] = '29500' - if 'MASTER_ADDR' not in os.environ: - raise KeyError('The environment variable MASTER_ADDR is not set') - os.environ['WORLD_SIZE'] = os.environ['OMPI_COMM_WORLD_SIZE'] - os.environ['RANK'] = os.environ['OMPI_COMM_WORLD_RANK'] - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_slurm(backend, port=None): - """Initialize slurm distributed training environment. - - If argument ``port`` is not specified, then the master port will be system - environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system - environment variable, then a default port ``29500`` will be used. - - Args: - backend (str): Backend of torch.distributed. - port (int, optional): Master port. Defaults to None. - """ - proc_id = int(os.environ['SLURM_PROCID']) - ntasks = int(os.environ['SLURM_NTASKS']) - node_list = os.environ['SLURM_NODELIST'] - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(proc_id % num_gpus) - addr = subprocess.getoutput( - f'scontrol show hostname {node_list} | head -n1') - # specify master port - if port is not None: - os.environ['MASTER_PORT'] = str(port) - elif 'MASTER_PORT' in os.environ: - pass # use MASTER_PORT in the environment variable - else: - # if torch.distributed default port(29500) is available - # then use it, else find a free port - if _is_free_port(29500): - os.environ['MASTER_PORT'] = '29500' - else: - os.environ['MASTER_PORT'] = str(_find_free_port()) - # use MASTER_ADDR in the environment variable if it already exists - if 'MASTER_ADDR' not in os.environ: - os.environ['MASTER_ADDR'] = addr - os.environ['WORLD_SIZE'] = str(ntasks) - os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) - os.environ['RANK'] = str(proc_id) - dist.init_process_group(backend=backend) - - -def get_dist_info(): - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - world_size = dist.get_world_size() - else: - rank = 0 - world_size = 1 - return rank, world_size - - -def master_only(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - rank, _ = get_dist_info() - if rank == 0: - return func(*args, **kwargs) - - return wrapper - - -def allreduce_params(params, coalesce=True, bucket_size_mb=-1): - """Allreduce parameters. - - Args: - params (list[torch.Parameters]): List of parameters or buffers of a - model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - _, world_size = get_dist_info() - if world_size == 1: - return - params = [param.data for param in params] - if coalesce: - _allreduce_coalesced(params, world_size, bucket_size_mb) - else: - for tensor in params: - dist.all_reduce(tensor.div_(world_size)) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - _, world_size = get_dist_info() - if world_size == 1: - return - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/epoch_based_runner.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/epoch_based_runner.py deleted file mode 100644 index 078e91df3..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/epoch_based_runner.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .utils import get_host_info - - -@RUNNERS.register_module() -class EpochBasedRunner(BaseRunner): - """Epoch-based Runner. - - This runner train models epoch by epoch. - """ - - def run_iter(self, data_batch, train_mode, **kwargs): - if self.batch_processor is not None: - outputs = self.batch_processor( - self.model, data_batch, train_mode=train_mode, **kwargs) - elif train_mode: - outputs = self.model.train_step(data_batch, self.optimizer, - **kwargs) - else: - outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('"batch_processor()" or "model.train_step()"' - 'and "model.val_step()" must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - self._iter += 1 - - self.call_hook('after_train_epoch') - self._epoch += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - self.call_hook('before_val_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_val_iter') - self.run_iter(data_batch, train_mode=False) - self.call_hook('after_val_iter') - - self.call_hook('after_val_epoch') - - def run(self, data_loaders, workflow, max_epochs=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, epochs) to specify the - running order and epochs. E.g, [('train', 2), ('val', 1)] means - running 2 epochs for training and 1 epoch for validation, - iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_epochs is not None: - warnings.warn( - 'setting max_epochs in run is deprecated, ' - 'please set max_epochs in runner_config', DeprecationWarning) - self._max_epochs = max_epochs - - assert self._max_epochs is not None, ( - 'max_epochs must be specified during instantiation') - - for i, flow in enumerate(workflow): - mode, epochs = flow - if mode == 'train': - self._max_iters = self._max_epochs * len(data_loaders[i]) - break - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d epochs', workflow, - self._max_epochs) - self.call_hook('before_run') - - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - mode, epochs = flow - if isinstance(mode, str): # self.train() - if not hasattr(self, mode): - raise ValueError( - f'runner has no method named "{mode}" to run an ' - 'epoch') - epoch_runner = getattr(self, mode) - else: - raise TypeError( - 'mode in workflow must be a str, but got {}'.format( - type(mode))) - - for _ in range(epochs): - if mode == 'train' and self.epoch >= self._max_epochs: - break - epoch_runner(data_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_run') - - def save_checkpoint(self, - out_dir, - filename_tmpl='epoch_{}.pth', - save_optimizer=True, - meta=None, - create_symlink=True): - """Save the checkpoint. - - Args: - out_dir (str): The directory that checkpoints are saved. - filename_tmpl (str, optional): The checkpoint filename template, - which contains a placeholder for the epoch number. - Defaults to 'epoch_{}.pth'. - save_optimizer (bool, optional): Whether to save the optimizer to - the checkpoint. Defaults to True. - meta (dict, optional): The meta information to be saved in the - checkpoint. Defaults to None. - create_symlink (bool, optional): Whether to create a symlink - "latest.pth" to point to the latest checkpoint. - Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.epoch + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - -@RUNNERS.register_module() -class Runner(EpochBasedRunner): - """Deprecated name of EpochBasedRunner.""" - - def __init__(self, *args, **kwargs): - warnings.warn( - 'Runner was deprecated, please use EpochBasedRunner instead', - DeprecationWarning) - super().__init__(*args, **kwargs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/fp16_utils.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/fp16_utils.py deleted file mode 100644 index be3ac3a31..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/fp16_utils.py +++ /dev/null @@ -1,423 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import warnings -from collections import abc -from inspect import getfullargspec - -import numpy as np -import torch -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .dist_utils import allreduce_grads as _allreduce_grads - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 - # manually, so the behavior may not be consistent with real amp. - from torch.cuda.amp import autocast -except ImportError: - pass - - -def cast_tensor_type(inputs, src_type, dst_type): - """Recursively convert Tensor in inputs from src_type to dst_type. - - Note: - In v1.4.4 and later, ``cast_tersor_type`` will only convert the - torch.Tensor which is consistent with ``src_type`` to the ``dst_type``. - Before v1.4.4, it ignores the ``src_type`` argument, leading to some - potential problems. For example, - ``cast_tensor_type(inputs, torch.float, torch.half)`` will convert all - tensors in inputs to ``torch.half`` including those originally in - ``torch.Int`` or other types, which is not expected. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype): Source type.. - dst_type (torch.dtype): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - if isinstance(inputs, nn.Module): - return inputs - elif isinstance(inputs, torch.Tensor): - # we need to ensure that the type of inputs to be casted are the same - # as the argument `src_type`. - return inputs.to(dst_type) if inputs.dtype == src_type else inputs - elif isinstance(inputs, str): - return inputs - elif isinstance(inputs, np.ndarray): - return inputs - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type, dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type, dst_type) for item in inputs) - else: - return inputs - - -def auto_fp16(apply_to=None, out_fp32=False, supported_types=(nn.Module, )): - """Decorator to enable fp16 training automatically. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If inputs arguments are fp32 tensors, they will - be converted to fp16 automatically. Arguments other than fp32 tensors are - ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp32 (bool): Whether to convert the output back to fp32. - supported_types (tuple): Classes can be decorated by ``auto_fp16``. - `New in version 1.5.0.` - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp16 - >>> @auto_fp16() - >>> def forward(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp16 - >>> @auto_fp16(apply_to=('pred', )) - >>> def do_something(self, pred, others): - >>> pass - """ - - def auto_fp16_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], supported_types): - raise TypeError('@auto_fp16 can only be used to decorate the ' - f'method of those classes {supported_types}') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - # NOTE: default args are not taken into consideration - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.float, torch.half)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = {} - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.float, torch.half) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=True): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp32: - output = cast_tensor_type(output, torch.half, torch.float) - return output - - return new_func - - return auto_fp16_wrapper - - -def force_fp32(apply_to=None, out_fp16=False): - """Decorator to convert input arguments to fp32 in force. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If there are some inputs that must be processed - in fp32 mode, then this decorator can handle it. If inputs arguments are - fp16 tensors, they will be converted to fp32 automatically. Arguments other - than fp16 tensors are ignored. If you are using PyTorch >= 1.6, - torch.cuda.amp is used as the backend, otherwise, original mmcv - implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp16 (bool): Whether to convert the output back to fp16. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp32 - >>> @force_fp32() - >>> def loss(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp32 - >>> @force_fp32(apply_to=('pred', )) - >>> def post_process(self, pred, others): - >>> pass - """ - - def force_fp32_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@force_fp32 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.half, torch.float)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = dict() - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.half, torch.float) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=False): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp16: - output = cast_tensor_type(output, torch.float, torch.half) - return output - - return new_func - - return force_fp32_wrapper - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - warnings.warning( - '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' - 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads', - DeprecationWarning) - _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) - - -def wrap_fp16_model(model): - """Wrap the FP32 model to FP16. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - For PyTorch >= 1.6, this function will - 1. Set fp16 flag inside the model to True. - - Otherwise: - 1. Convert FP32 model to FP16. - 2. Remain some necessary layers to be FP32, e.g., normalization layers. - 3. Set `fp16_enabled` flag inside the model to True. - - Args: - model (nn.Module): Model in FP32. - """ - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.6.0')): - # convert model to fp16 - model.half() - # patch the normalization layers to make it work in fp32 mode - patch_norm_fp32(model) - # set `fp16_enabled` flag - for m in model.modules(): - if hasattr(m, 'fp16_enabled'): - m.fp16_enabled = True - - -def patch_norm_fp32(module): - """Recursively convert normalization layers from FP16 to FP32. - - Args: - module (nn.Module): The modules to be converted in FP16. - - Returns: - nn.Module: The converted module, the normalization layers have been - converted to FP32. - """ - if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): - module.float() - if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': - module.forward = patch_forward_method(module.forward, torch.half, - torch.float) - for child in module.children(): - patch_norm_fp32(child) - return module - - -def patch_forward_method(func, src_type, dst_type, convert_output=True): - """Patch the forward method of a module. - - Args: - func (callable): The original forward method. - src_type (torch.dtype): Type of input arguments to be converted from. - dst_type (torch.dtype): Type of input arguments to be converted to. - convert_output (bool): Whether to convert the output back to src_type. - - Returns: - callable: The patched forward method. - """ - - def new_forward(*args, **kwargs): - output = func(*cast_tensor_type(args, src_type, dst_type), - **cast_tensor_type(kwargs, src_type, dst_type)) - if convert_output: - output = cast_tensor_type(output, dst_type, src_type) - return output - - return new_forward - - -class LossScaler: - """Class that manages loss scaling in mixed precision training which - supports both dynamic or static mode. - - The implementation refers to - https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. - Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. - It's important to understand how :class:`LossScaler` operates. - Loss scaling is designed to combat the problem of underflowing - gradients encountered at long times when training fp16 networks. - Dynamic loss scaling begins by attempting a very high loss - scale. Ironically, this may result in OVERflowing gradients. - If overflowing gradients are encountered, :class:`FP16_Optimizer` then - skips the update step for this particular iteration/minibatch, - and :class:`LossScaler` adjusts the loss scale to a lower value. - If a certain number of iterations occur without overflowing gradients - detected,:class:`LossScaler` increases the loss scale once more. - In this way :class:`LossScaler` attempts to "ride the edge" of always - using the highest loss scale possible without incurring overflow. - - Args: - init_scale (float): Initial loss scale value, default: 2**32. - scale_factor (float): Factor used when adjusting the loss scale. - Default: 2. - mode (str): Loss scaling mode. 'dynamic' or 'static' - scale_window (int): Number of consecutive iterations without an - overflow to wait before increasing the loss scale. Default: 1000. - """ - - def __init__(self, - init_scale=2**32, - mode='dynamic', - scale_factor=2., - scale_window=1000): - self.cur_scale = init_scale - self.cur_iter = 0 - assert mode in ('dynamic', - 'static'), 'mode can only be dynamic or static' - self.mode = mode - self.last_overflow_iter = -1 - self.scale_factor = scale_factor - self.scale_window = scale_window - - def has_overflow(self, params): - """Check if params contain overflow.""" - if self.mode != 'dynamic': - return False - for p in params: - if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): - return True - return False - - def _has_inf_or_nan(x): - """Check if params contain NaN.""" - try: - cpu_sum = float(x.float().sum()) - except RuntimeError as instance: - if 'value cannot be converted' not in instance.args[0]: - raise - return True - else: - if cpu_sum == float('inf') or cpu_sum == -float('inf') \ - or cpu_sum != cpu_sum: - return True - return False - - def update_scale(self, overflow): - """update the current loss scale value when overflow happens.""" - if self.mode != 'dynamic': - return - if overflow: - self.cur_scale = max(self.cur_scale / self.scale_factor, 1) - self.last_overflow_iter = self.cur_iter - else: - if (self.cur_iter - self.last_overflow_iter) % \ - self.scale_window == 0: - self.cur_scale *= self.scale_factor - self.cur_iter += 1 - - def state_dict(self): - """Returns the state of the scaler as a :class:`dict`.""" - return dict( - cur_scale=self.cur_scale, - cur_iter=self.cur_iter, - mode=self.mode, - last_overflow_iter=self.last_overflow_iter, - scale_factor=self.scale_factor, - scale_window=self.scale_window) - - def load_state_dict(self, state_dict): - """Loads the loss_scaler state dict. - - Args: - state_dict (dict): scaler state. - """ - self.cur_scale = state_dict['cur_scale'] - self.cur_iter = state_dict['cur_iter'] - self.mode = state_dict['mode'] - self.last_overflow_iter = state_dict['last_overflow_iter'] - self.scale_factor = state_dict['scale_factor'] - self.scale_window = state_dict['scale_window'] - - @property - def loss_scale(self): - return self.cur_scale diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/__init__.py deleted file mode 100644 index 03e2a619e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkpoint import CheckpointHook -from .closure import ClosureHook -from .ema import EMAHook -from .evaluation import DistEvalHook, EvalHook -from .hook import HOOKS, Hook -from .iter_timer import IterTimerHook -from .logger import (ClearMLLoggerHook, DvcliveLoggerHook, LoggerHook, - MlflowLoggerHook, NeptuneLoggerHook, PaviLoggerHook, - SegmindLoggerHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, InvLrUpdaterHook, - LinearAnnealingLrUpdaterHook, LrUpdaterHook, - OneCycleLrUpdaterHook, PolyLrUpdaterHook, - StepLrUpdaterHook) -from .memory import EmptyCacheHook -from .momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, OptimizerHook) -from .profiler import ProfilerHook -from .sampler_seed import DistSamplerSeedHook -from .sync_buffer import SyncBuffersHook - -__all__ = [ - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'OptimizerHook', - 'Fp16OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', - 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TextLoggerHook', 'TensorboardLoggerHook', 'NeptuneLoggerHook', - 'WandbLoggerHook', 'DvcliveLoggerHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'SyncBuffersHook', 'EMAHook', 'EvalHook', 'DistEvalHook', 'ProfilerHook', - 'GradientCumulativeOptimizerHook', 'GradientCumulativeFp16OptimizerHook', - 'SegmindLoggerHook', 'LinearAnnealingLrUpdaterHook', - 'LinearAnnealingMomentumUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/checkpoint.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/checkpoint.py deleted file mode 100644 index 7bb75f402..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/checkpoint.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -from mmcv.fileio import FileClient -from ..dist_utils import allreduce_params, master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class CheckpointHook(Hook): - """Save checkpoints periodically. - - Args: - interval (int): The saving period. If ``by_epoch=True``, interval - indicates epochs, otherwise it indicates iterations. - Default: -1, which means "never". - by_epoch (bool): Saving checkpoints by epoch or by iteration. - Default: True. - save_optimizer (bool): Whether to save optimizer state_dict in the - checkpoint. It is usually used for resuming experiments. - Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, ``runner.work_dir`` will be used by default. If - specified, the ``out_dir`` will be the concatenation of ``out_dir`` - and the last level directory of ``runner.work_dir``. - `Changed in version 1.3.16.` - max_keep_ckpts (int, optional): The maximum checkpoints to keep. - In some cases we want only the latest few checkpoints and would - like to delete old ones to save the disk space. - Default: -1, which means unlimited. - save_last (bool, optional): Whether to force the last checkpoint to be - saved regardless of interval. Default: True. - sync_buffer (bool, optional): Whether to synchronize buffers in - different gpus. Default: False. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - - .. warning:: - Before v1.3.16, the ``out_dir`` argument indicates the path where the - checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the - root directory and the final path to save checkpoint is the - concatenation of ``out_dir`` and the last level directory of - ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" - and the value of ``runner.work_dir`` is "/path/of/B", then the final - path will be "/path/of/A/B". - """ - - def __init__(self, - interval=-1, - by_epoch=True, - save_optimizer=True, - out_dir=None, - max_keep_ckpts=-1, - save_last=True, - sync_buffer=False, - file_client_args=None, - **kwargs): - self.interval = interval - self.by_epoch = by_epoch - self.save_optimizer = save_optimizer - self.out_dir = out_dir - self.max_keep_ckpts = max_keep_ckpts - self.save_last = save_last - self.args = kwargs - self.sync_buffer = sync_buffer - self.file_client_args = file_client_args - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - - runner.logger.info((f'Checkpoints will be saved to {self.out_dir} by ' - f'{self.file_client.name}.')) - - # disable the create_symlink option because some file backends do not - # allow to create a symlink - if 'create_symlink' in self.args: - if self.args[ - 'create_symlink'] and not self.file_client.allow_symlink: - self.args['create_symlink'] = False - warnings.warn( - ('create_symlink is set as True by the user but is changed' - 'to be False because creating symbolic link is not ' - f'allowed in {self.file_client.name}')) - else: - self.args['create_symlink'] = self.file_client.allow_symlink - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` epochs - # 2. reach the last epoch of training - if self.every_n_epochs( - runner, self.interval) or (self.save_last - and self.is_last_epoch(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.epoch + 1} epochs') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) - - @master_only - def _save_checkpoint(self, runner): - """Save the current checkpoint and delete unwanted checkpoint.""" - runner.save_checkpoint( - self.out_dir, save_optimizer=self.save_optimizer, **self.args) - if runner.meta is not None: - if self.by_epoch: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) - else: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) - runner.meta.setdefault('hook_msgs', dict()) - runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( - self.out_dir, cur_ckpt_filename) - # remove other checkpoints - if self.max_keep_ckpts > 0: - if self.by_epoch: - name = 'epoch_{}.pth' - current_ckpt = runner.epoch + 1 - else: - name = 'iter_{}.pth' - current_ckpt = runner.iter + 1 - redundant_ckpts = range( - current_ckpt - self.max_keep_ckpts * self.interval, 0, - -self.interval) - filename_tmpl = self.args.get('filename_tmpl', name) - for _step in redundant_ckpts: - ckpt_path = self.file_client.join_path( - self.out_dir, filename_tmpl.format(_step)) - if self.file_client.isfile(ckpt_path): - self.file_client.remove(ckpt_path) - else: - break - - def after_train_iter(self, runner): - if self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` iterations - # 2. reach the last iteration of training - if self.every_n_iters( - runner, self.interval) or (self.save_last - and self.is_last_iter(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.iter + 1} iterations') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/closure.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/closure.py deleted file mode 100644 index b955f81f4..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/closure.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ClosureHook(Hook): - - def __init__(self, fn_name, fn): - assert hasattr(self, fn_name) - assert callable(fn) - setattr(self, fn_name, fn) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/ema.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/ema.py deleted file mode 100644 index 6ed77b84e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/ema.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...parallel import is_module_wrapper -from ..hooks.hook import HOOKS, Hook - - -@HOOKS.register_module() -class EMAHook(Hook): - r"""Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. - - .. math:: - - Xema\_{t+1} = (1 - \text{momentum}) \times - Xema\_{t} + \text{momentum} \times X_t - - Args: - momentum (float): The momentum used for updating ema parameter. - Defaults to 0.0002. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - warm_up (int): During first warm_up steps, we may use smaller momentum - to update ema parameters more slowly. Defaults to 100. - resume_from (str): The checkpoint path. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - warm_up=100, - resume_from=None): - assert isinstance(interval, int) and interval > 0 - self.warm_up = warm_up - self.interval = interval - assert momentum > 0 and momentum < 1 - self.momentum = momentum**interval - self.checkpoint = resume_from - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - self.model_parameters = dict(model.named_parameters(recurse=True)) - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers(recurse=True)) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - curr_step = runner.iter - # We warm up the momentum considering the instability at beginning - momentum = min(self.momentum, - (1 + curr_step) / (self.warm_up + curr_step)) - if curr_step % self.interval != 0: - return - for name, parameter in self.model_parameters.items(): - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/evaluation.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/evaluation.py deleted file mode 100644 index 2a57d535e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/evaluation.py +++ /dev/null @@ -1,511 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from math import inf - -import torch.distributed as dist -from torch.nn.modules.batchnorm import _BatchNorm -from torch.utils.data import DataLoader - -from mmcv.fileio import FileClient -from mmcv.utils import is_seq_of -from .hook import Hook -from .logger import LoggerHook - - -class EvalHook(Hook): - """Non-Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader, and return the test results. If ``None``, the default - test function ``mmcv.engine.single_gpu_test`` will be used. - (default: ``None``) - greater_keys (List[str] | None, optional): Metric keys that will be - inferred by 'greater' comparison rule. If ``None``, - _default_greater_keys will be used. (default: ``None``) - less_keys (List[str] | None, optional): Metric keys that will be - inferred by 'less' comparison rule. If ``None``, _default_less_keys - will be used. (default: ``None``) - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - `New in version 1.3.16.` - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - `New in version 1.3.16.` - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - - Note: - If new arguments are added for EvalHook, tools/test.py, - tools/eval_metric.py may be affected. - """ - - # Since the key for determine greater or less is related to the downstream - # tasks, downstream repos may need to overwrite the following inner - # variable accordingly. - - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - _default_greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - _default_less_keys = ['loss'] - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - out_dir=None, - file_client_args=None, - **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError(f'dataloader must be a pytorch DataLoader, ' - f'but got {type(dataloader)}') - - if interval <= 0: - raise ValueError(f'interval must be a positive number, ' - f'but got {interval}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' - - if start is not None and start < 0: - raise ValueError(f'The evaluation start epoch {start} is smaller ' - f'than 0') - - self.dataloader = dataloader - self.interval = interval - self.start = start - self.by_epoch = by_epoch - - assert isinstance(save_best, str) or save_best is None, \ - '""save_best"" should be a str or None ' \ - f'rather than {type(save_best)}' - self.save_best = save_best - self.eval_kwargs = eval_kwargs - self.initial_flag = True - - if test_fn is None: - from mmcv.engine import single_gpu_test - self.test_fn = single_gpu_test - else: - self.test_fn = test_fn - - if greater_keys is None: - self.greater_keys = self._default_greater_keys - else: - if not isinstance(greater_keys, (list, tuple)): - greater_keys = (greater_keys, ) - assert is_seq_of(greater_keys, str) - self.greater_keys = greater_keys - - if less_keys is None: - self.less_keys = self._default_less_keys - else: - if not isinstance(less_keys, (list, tuple)): - less_keys = (less_keys, ) - assert is_seq_of(less_keys, str) - self.less_keys = less_keys - - if self.save_best is not None: - self.best_ckpt_path = None - self._init_rule(rule, self.save_best) - - self.out_dir = out_dir - self.file_client_args = file_client_args - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific (note that the key indicator matching - is case-insensitive): - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator != 'auto': - # `_lc` here means we use the lower case of keys for - # case-insensitive matching - key_indicator_lc = key_indicator.lower() - greater_keys = [key.lower() for key in self.greater_keys] - less_keys = [key.lower() for key in self.less_keys] - - if key_indicator_lc in greater_keys: - rule = 'greater' - elif key_indicator_lc in less_keys: - rule = 'less' - elif any(key in key_indicator_lc for key in greater_keys): - rule = 'greater' - elif any(key in key_indicator_lc for key in less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - if self.rule is not None: - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'The best checkpoint will be saved to {self.out_dir} by ' - f'{self.file_client.name}')) - - if self.save_best is not None: - if runner.meta is None: - warnings.warn('runner.meta is None. Creating an empty one.') - runner.meta = dict() - runner.meta.setdefault('hook_msgs', dict()) - self.best_ckpt_path = runner.meta['hook_msgs'].get( - 'best_ckpt', None) - - def before_train_iter(self, runner): - """Evaluate the model only at the start of training by iteration.""" - if self.by_epoch or not self.initial_flag: - return - if self.start is not None and runner.iter >= self.start: - self.after_train_iter(runner) - self.initial_flag = False - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - if not (self.by_epoch and self.initial_flag): - return - if self.start is not None and runner.epoch >= self.start: - self.after_train_epoch(runner) - self.initial_flag = False - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch and self._should_evaluate(runner): - # Because the priority of EvalHook is higher than LoggerHook, the - # training log and the evaluating log are mixed. Therefore, - # we need to dump the training log and clear it before evaluating - # log is generated. In addition, this problem will only appear in - # `IterBasedRunner` whose `self.by_epoch` is False, because - # `EpochBasedRunner` whose `self.by_epoch` is True calls - # `_do_evaluate` in `after_train_epoch` stage, and at this stage - # the training log has been printed, so it will not cause any - # problem. more details at - # https://github.com/open-mmlab/mmsegmentation/issues/694 - for hook in runner._hooks: - if isinstance(hook, LoggerHook): - hook.after_train_iter(runner) - runner.log_buffer.clear() - - self._do_evaluate(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch and self._should_evaluate(runner): - self._do_evaluate(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - results = self.test_fn(runner.model, self.dataloader) - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - def _should_evaluate(self, runner): - """Judge whether to perform evaluation. - - Here is the rule to judge whether to perform evaluation: - 1. It will not perform evaluation during the epoch/iteration interval, - which is determined by ``self.interval``. - 2. It will not perform evaluation if the start time is larger than - current time. - 3. It will not perform evaluation when current time is larger than - the start time but during epoch/iteration interval. - - Returns: - bool: The flag indicating whether to perform evaluation. - """ - if self.by_epoch: - current = runner.epoch - check_time = self.every_n_epochs - else: - current = runner.iter - check_time = self.every_n_iters - - if self.start is None: - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - elif (current + 1) < self.start: - # No evaluation if start is larger than the current time. - return False - else: - # Evaluation only at epochs/iters 3, 5, 7... - # if start==3 and interval==2 - if (current + 1 - self.start) % self.interval: - return False - return True - - def _save_ckpt(self, runner, key_score): - """Save the best checkpoint. - - It will compare the score according to the compare function, write - related information (best score, best checkpoint path) and save the - best checkpoint into ``work_dir``. - """ - if self.by_epoch: - current = f'epoch_{runner.epoch + 1}' - cur_type, cur_time = 'epoch', runner.epoch + 1 - else: - current = f'iter_{runner.iter + 1}' - cur_type, cur_time = 'iter', runner.iter + 1 - - best_score = runner.meta['hook_msgs'].get( - 'best_score', self.init_value_map[self.rule]) - if self.compare_func(key_score, best_score): - best_score = key_score - runner.meta['hook_msgs']['best_score'] = best_score - - if self.best_ckpt_path and self.file_client.isfile( - self.best_ckpt_path): - self.file_client.remove(self.best_ckpt_path) - runner.logger.info( - (f'The previous best checkpoint {self.best_ckpt_path} was ' - 'removed')) - - best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' - self.best_ckpt_path = self.file_client.join_path( - self.out_dir, best_ckpt_name) - runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path - - runner.save_checkpoint( - self.out_dir, - filename_tmpl=best_ckpt_name, - create_symlink=False) - runner.logger.info( - f'Now best checkpoint is saved as {best_ckpt_name}.') - runner.logger.info( - f'Best {self.key_indicator} is {best_score:0.4f} ' - f'at {cur_time} {cur_type}.') - - def evaluate(self, runner, results): - """Evaluate the results. - - Args: - runner (:obj:`mmcv.Runner`): The underlined training runner. - results (list): Output results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - - if self.save_best is not None: - # If the performance of model is pool, the `eval_res` may be an - # empty dict and it will raise exception when `self.save_best` is - # not None. More details at - # https://github.com/open-mmlab/mmdetection/issues/6265. - if not eval_res: - warnings.warn( - 'Since `eval_res` is an empty dict, the behavior to save ' - 'the best checkpoint will be skipped in this evaluation.') - return None - - if self.key_indicator == 'auto': - # infer from eval_results - self._init_rule(self.rule, list(eval_res.keys())[0]) - return eval_res[self.key_indicator] - - return None - - -class DistEvalHook(EvalHook): - """Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader in a multi-gpu manner, and return the test results. If - ``None``, the default test function ``mmcv.engine.multi_gpu_test`` - will be used. (default: ``None``) - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - broadcast_bn_buffer (bool): Whether to broadcast the - buffer(running_mean and running_var) of rank 0 to other rank - before evaluation. Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - """ - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - broadcast_bn_buffer=True, - tmpdir=None, - gpu_collect=False, - out_dir=None, - file_client_args=None, - **eval_kwargs): - - if test_fn is None: - from mmcv.engine import multi_gpu_test - test_fn = multi_gpu_test - - super().__init__( - dataloader, - start=start, - interval=interval, - by_epoch=by_epoch, - save_best=save_best, - rule=rule, - test_fn=test_fn, - greater_keys=greater_keys, - less_keys=less_keys, - out_dir=out_dir, - file_client_args=file_client_args, - **eval_kwargs) - - self.broadcast_bn_buffer = broadcast_bn_buffer - self.tmpdir = tmpdir - self.gpu_collect = gpu_collect - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - results = self.test_fn( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to - # save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/hook.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/hook.py deleted file mode 100644 index f2d1c9865..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/hook.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, is_method_overridden - -HOOKS = Registry('hook') - - -class Hook: - stages = ('before_run', 'before_train_epoch', 'before_train_iter', - 'after_train_iter', 'after_train_epoch', 'before_val_epoch', - 'before_val_iter', 'after_val_iter', 'after_val_epoch', - 'after_run') - - def before_run(self, runner): - pass - - def after_run(self, runner): - pass - - def before_epoch(self, runner): - pass - - def after_epoch(self, runner): - pass - - def before_iter(self, runner): - pass - - def after_iter(self, runner): - pass - - def before_train_epoch(self, runner): - self.before_epoch(runner) - - def before_val_epoch(self, runner): - self.before_epoch(runner) - - def after_train_epoch(self, runner): - self.after_epoch(runner) - - def after_val_epoch(self, runner): - self.after_epoch(runner) - - def before_train_iter(self, runner): - self.before_iter(runner) - - def before_val_iter(self, runner): - self.before_iter(runner) - - def after_train_iter(self, runner): - self.after_iter(runner) - - def after_val_iter(self, runner): - self.after_iter(runner) - - def every_n_epochs(self, runner, n): - return (runner.epoch + 1) % n == 0 if n > 0 else False - - def every_n_inner_iters(self, runner, n): - return (runner.inner_iter + 1) % n == 0 if n > 0 else False - - def every_n_iters(self, runner, n): - return (runner.iter + 1) % n == 0 if n > 0 else False - - def end_of_epoch(self, runner): - return runner.inner_iter + 1 == len(runner.data_loader) - - def is_last_epoch(self, runner): - return runner.epoch + 1 == runner._max_epochs - - def is_last_iter(self, runner): - return runner.iter + 1 == runner._max_iters - - def get_triggered_stages(self): - trigger_stages = set() - for stage in Hook.stages: - if is_method_overridden(stage, Hook, self): - trigger_stages.add(stage) - - # some methods will be triggered in multi stages - # use this dict to map method to stages. - method_stages_map = { - 'before_epoch': ['before_train_epoch', 'before_val_epoch'], - 'after_epoch': ['after_train_epoch', 'after_val_epoch'], - 'before_iter': ['before_train_iter', 'before_val_iter'], - 'after_iter': ['after_train_iter', 'after_val_iter'], - } - - for method, map_stages in method_stages_map.items(): - if is_method_overridden(method, Hook, self): - trigger_stages.update(map_stages) - - return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/iter_timer.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/iter_timer.py deleted file mode 100644 index 16531be96..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/iter_timer.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import time -import torch.distributed as dist - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class IterTimerHook(Hook): - - def before_epoch(self, runner): - self.t = time.time() - - def before_iter(self, runner): - self.batch_size = runner.cfg.data.samples_per_gpu - runner.log_buffer.update({'data_time': time.time() - self.t}) - - def after_iter(self, runner): - iter_info = {'time': time.time() - self.t} - fps = self.batch_size / iter_info["time"] - - if dist.is_initialized(): - fps = fps * dist.get_world_size() - - iter_info["fps"] = fps - - runner.log_buffer.update(iter_info) - self.t = time.time() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/__init__.py deleted file mode 100644 index 062709e70..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import LoggerHook -from .clearml import ClearMLLoggerHook -from .dvclive import DvcliveLoggerHook -from .mlflow import MlflowLoggerHook -from .neptune import NeptuneLoggerHook -from .pavi import PaviLoggerHook -from .segmind import SegmindLoggerHook -from .tensorboard import TensorboardLoggerHook -from .text import TextLoggerHook -from .wandb import WandbLoggerHook - -__all__ = [ - 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', - 'NeptuneLoggerHook', 'DvcliveLoggerHook', 'SegmindLoggerHook', - 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/base.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/base.py deleted file mode 100644 index 9f1a51b31..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/base.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from abc import ABCMeta, abstractmethod - -import numpy as np -import torch - -from ..hook import Hook - - -class LoggerHook(Hook): - """Base class for logger hooks. - - Args: - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - """ - - __metaclass__ = ABCMeta - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - self.interval = interval - self.ignore_last = ignore_last - self.reset_flag = reset_flag - self.by_epoch = by_epoch - - @abstractmethod - def log(self, runner): - pass - - @staticmethod - def is_scalar(val, include_np=True, include_torch=True): - """Tell the input variable is a scalar or not. - - Args: - val: Input variable. - include_np (bool): Whether include 0-d np.ndarray as a scalar. - include_torch (bool): Whether include 0-d torch.Tensor as a scalar. - - Returns: - bool: True or False. - """ - if isinstance(val, numbers.Number): - return True - elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: - return True - elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: - return True - else: - return False - - def get_mode(self, runner): - if runner.mode == 'train': - if 'time' in runner.log_buffer.output: - mode = 'train' - else: - mode = 'val' - elif runner.mode == 'val': - mode = 'val' - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return mode - - def get_epoch(self, runner): - if runner.mode == 'train': - epoch = runner.epoch + 1 - elif runner.mode == 'val': - # normal val mode - # runner.epoch += 1 has been done before val workflow - epoch = runner.epoch - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return epoch - - def get_iter(self, runner, inner_iter=False): - """Get the current training iteration step.""" - if self.by_epoch and inner_iter: - current_iter = runner.inner_iter + 1 - else: - current_iter = runner.iter + 1 - return current_iter - - def get_lr_tags(self, runner): - tags = {} - lrs = runner.current_lr() - if isinstance(lrs, dict): - for name, value in lrs.items(): - tags[f'learning_rate/{name}'] = value[0] - else: - tags['learning_rate'] = lrs[0] - return tags - - def get_momentum_tags(self, runner): - tags = {} - momentums = runner.current_momentum() - if isinstance(momentums, dict): - for name, value in momentums.items(): - tags[f'momentum/{name}'] = value[0] - else: - tags['momentum'] = momentums[0] - return tags - - def get_loggable_tags(self, - runner, - allow_scalar=True, - allow_text=False, - add_mode=True, - tags_to_skip=('time', 'data_time')): - tags = {} - for var, val in runner.log_buffer.output.items(): - if var in tags_to_skip: - continue - if self.is_scalar(val) and not allow_scalar: - continue - if isinstance(val, str) and not allow_text: - continue - if add_mode: - var = f'{self.get_mode(runner)}/{var}' - tags[var] = val - tags.update(self.get_lr_tags(runner)) - tags.update(self.get_momentum_tags(runner)) - return tags - - def before_run(self, runner): - for hook in runner.hooks[::-1]: - if isinstance(hook, LoggerHook): - hook.reset_flag = True - break - - def before_epoch(self, runner): - runner.log_buffer.clear() # clear logs of last epoch - - def after_train_iter(self, runner): - if self.by_epoch and self.every_n_inner_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif not self.by_epoch and self.every_n_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif self.end_of_epoch(runner) and not self.ignore_last: - # not precise but more stable - runner.log_buffer.average(self.interval) - - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_train_epoch(self, runner): - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_val_epoch(self, runner): - runner.log_buffer.average() - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/clearml.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/clearml.py deleted file mode 100644 index 1de71e152..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/clearml.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class ClearMLLoggerHook(LoggerHook): - """Class to log metrics with clearml. - - It requires `clearml`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the `clearml.Task.init` - initialization keys. See `taskinit`_ for more details. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _clearml: - https://clear.ml/docs/latest/docs/ - .. _taskinit: - https://clear.ml/docs/latest/docs/references/sdk/task/#taskinit - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(ClearMLLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_clearml() - self.init_kwargs = init_kwargs - - def import_clearml(self): - try: - import clearml - except ImportError: - raise ImportError( - 'Please run "pip install clearml" to install clearml') - self.clearml = clearml - - @master_only - def before_run(self, runner): - super(ClearMLLoggerHook, self).before_run(runner) - task_kwargs = self.init_kwargs if self.init_kwargs else {} - self.task = self.clearml.Task.init(**task_kwargs) - self.task_logger = self.task.get_logger() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - for tag, val in tags.items(): - self.task_logger.report_scalar(tag, tag, val, - self.get_iter(runner)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/dvclive.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/dvclive.py deleted file mode 100644 index c79eefa75..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/dvclive.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from pathlib import Path - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class DvcliveLoggerHook(LoggerHook): - """Class to log metrics with dvclive. - - It requires `dvclive`_ to be installed. - - Args: - model_file (str): Default None. If not None, after each epoch the - model will be saved to {model_file}. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - kwargs: Arguments for instantiating `Live`_. - - .. _dvclive: - https://dvc.org/doc/dvclive - - .. _Live: - https://dvc.org/doc/dvclive/api-reference/live#parameters - """ - - def __init__(self, - model_file=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - **kwargs): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.model_file = model_file - self.import_dvclive(**kwargs) - - def import_dvclive(self, **kwargs): - try: - from dvclive import Live - except ImportError: - raise ImportError( - 'Please run "pip install dvclive" to install dvclive') - self.dvclive = Live(**kwargs) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.dvclive.set_step(self.get_iter(runner)) - for k, v in tags.items(): - self.dvclive.log(k, v) - - @master_only - def after_train_epoch(self, runner): - super().after_train_epoch(runner) - if self.model_file is not None: - runner.save_checkpoint( - Path(self.model_file).parent, - filename_tmpl=Path(self.model_file).name, - create_symlink=False, - ) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/mlflow.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/mlflow.py deleted file mode 100644 index dcd87bcb5..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/mlflow.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import TORCH_VERSION -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class MlflowLoggerHook(LoggerHook): - """Class to log metrics and (optionally) a trained model to MLflow. - - It requires `MLflow`_ to be installed. - - Args: - exp_name (str, optional): Name of the experiment to be used. - Default None. If not None, set the active experiment. - If experiment does not exist, an experiment with provided name - will be created. - tags (Dict[str], optional): Tags for the current run. - Default None. If not None, set tags for the current run. - log_model (bool, optional): Whether to log an MLflow artifact. - Default True. If True, log runner.model as an MLflow artifact - for the current run. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _MLflow: - https://www.mlflow.org/docs/latest/index.html - """ - - def __init__(self, - exp_name=None, - tags=None, - log_model=True, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(MlflowLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_mlflow() - self.exp_name = exp_name - self.tags = tags - self.log_model = log_model - - def import_mlflow(self): - try: - import mlflow - import mlflow.pytorch as mlflow_pytorch - except ImportError: - raise ImportError( - 'Please run "pip install mlflow" to install mlflow') - self.mlflow = mlflow - self.mlflow_pytorch = mlflow_pytorch - - @master_only - def before_run(self, runner): - super(MlflowLoggerHook, self).before_run(runner) - if self.exp_name is not None: - self.mlflow.set_experiment(self.exp_name) - if self.tags is not None: - self.mlflow.set_tags(self.tags) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.mlflow.log_metrics(tags, step=self.get_iter(runner)) - - @master_only - def after_run(self, runner): - if self.log_model: - self.mlflow_pytorch.log_model( - runner.model, - 'models', - pip_requirements=[f'torch=={TORCH_VERSION}']) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/neptune.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/neptune.py deleted file mode 100644 index e0aafe91d..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/neptune.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class NeptuneLoggerHook(LoggerHook): - """Class to log metrics to NeptuneAI. - - It requires `Neptune`_ to be installed. - - Args: - init_kwargs (dict): a dict contains the initialization keys as below: - - - project (str): Name of a project in a form of - namespace/project_name. If None, the value of NEPTUNE_PROJECT - environment variable will be taken. - - api_token (str): User’s API token. If None, the value of - NEPTUNE_API_TOKEN environment variable will be taken. Note: It is - strongly recommended to use NEPTUNE_API_TOKEN environment - variable rather than placing your API token in plain text in your - source code. - - name (str, optional, default is 'Untitled'): Editable name of the - run. Name is displayed in the run's Details and in Runs table as - a column. - - Check https://docs.neptune.ai/api-reference/neptune#init for more - init arguments. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than ``interval``. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _Neptune: - https://docs.neptune.ai - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=True, - with_step=True, - by_epoch=True): - - super(NeptuneLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_neptune() - self.init_kwargs = init_kwargs - self.with_step = with_step - - def import_neptune(self): - try: - import neptune.new as neptune - except ImportError: - raise ImportError( - 'Please run "pip install neptune-client" to install neptune') - self.neptune = neptune - self.run = None - - @master_only - def before_run(self, runner): - if self.init_kwargs: - self.run = self.neptune.init(**self.init_kwargs) - else: - self.run = self.neptune.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for tag_name, tag_value in tags.items(): - if self.with_step: - self.run[tag_name].log( - tag_value, step=self.get_iter(runner)) - else: - tags['global_step'] = self.get_iter(runner) - self.run[tag_name].log(tags) - - @master_only - def after_run(self, runner): - self.run.stop() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/pavi.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/pavi.py deleted file mode 100644 index d5d61f9e5..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/pavi.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp - -import torch -import yaml - -import mmcv -from ....parallel.utils import is_module_wrapper -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class PaviLoggerHook(LoggerHook): - """Class to visual model, log metrics (for internal use). - - Args: - init_kwargs (dict): A dict contains the initialization keys. - add_graph (bool): Whether to visual model. Default: False. - add_last_ckpt (bool): Whether to save checkpoint after run. - Default: False. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - img_key (string): Get image data from Dataset. Default: 'img_info'. - """ - - def __init__(self, - init_kwargs=None, - add_graph=False, - add_last_ckpt=False, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - img_key='img_info'): - super(PaviLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.init_kwargs = init_kwargs - self.add_graph = add_graph - self.add_last_ckpt = add_last_ckpt - self.img_key = img_key - - @master_only - def before_run(self, runner): - super(PaviLoggerHook, self).before_run(runner) - try: - from pavi import SummaryWriter - except ImportError: - raise ImportError('Please run "pip install pavi" to install pavi.') - - self.run_name = runner.work_dir.split('/')[-1] - - if not self.init_kwargs: - self.init_kwargs = dict() - self.init_kwargs['name'] = self.run_name - self.init_kwargs['model'] = runner._model_name - if runner.meta is not None: - if 'config_dict' in runner.meta: - config_dict = runner.meta['config_dict'] - assert isinstance( - config_dict, - dict), ('meta["config_dict"] has to be of a dict, ' - f'but got {type(config_dict)}') - elif 'config_file' in runner.meta: - config_file = runner.meta['config_file'] - config_dict = dict(mmcv.Config.fromfile(config_file)) - else: - config_dict = None - if config_dict is not None: - # 'max_.*iter' is parsed in pavi sdk as the maximum iterations - # to properly set up the progress bar. - config_dict = config_dict.copy() - config_dict.setdefault('max_iter', runner.max_iters) - # non-serializable values are first converted in - # mmcv.dump to json - config_dict = json.loads( - mmcv.dump(config_dict, file_format='json')) - session_text = yaml.dump(config_dict) - self.init_kwargs['session_text'] = session_text - self.writer = SummaryWriter(**self.init_kwargs) - - def get_step(self, runner): - """Get the total training step/epoch.""" - if self.get_mode(runner) == 'val' and self.by_epoch: - return self.get_epoch(runner) - else: - return self.get_iter(runner) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, add_mode=False) - if tags: - self.writer.add_scalars( - self.get_mode(runner), tags, self.get_step(runner)) - - @master_only - def after_run(self, runner): - if self.add_last_ckpt: - ckpt_path = osp.join(runner.work_dir, 'latest.pth') - if osp.islink(ckpt_path): - ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) - - if osp.isfile(ckpt_path): - # runner.epoch += 1 has been done before `after_run`. - iteration = runner.epoch if self.by_epoch else runner.iter - return self.writer.add_snapshot_file( - tag=self.run_name, - snapshot_file_path=ckpt_path, - iteration=iteration) - - # flush the buffer and send a task ending signal to Pavi - self.writer.close() - - @master_only - def before_epoch(self, runner): - if runner.epoch == 0 and self.add_graph: - if is_module_wrapper(runner.model): - _model = runner.model.module - else: - _model = runner.model - device = next(_model.parameters()).device - data = next(iter(runner.data_loader)) - image = data[self.img_key][0:1].to(device) - with torch.no_grad(): - self.writer.add_graph(_model, image) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/segmind.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/segmind.py deleted file mode 100644 index e262c7c1a..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/segmind.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class SegmindLoggerHook(LoggerHook): - """Class to log metrics to Segmind. - - It requires `Segmind`_ to be installed. - - Args: - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - - .. _Segmind: - https://docs.segmind.com/python-library - """ - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(SegmindLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_segmind() - - def import_segmind(self): - try: - import segmind - except ImportError: - raise ImportError( - "Please run 'pip install segmind' to install segmind") - self.log_metrics = segmind.tracking.fluent.log_metrics - self.mlflow_log = segmind.utils.logging_utils.try_mlflow_log - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - # logging metrics to segmind - self.mlflow_log( - self.log_metrics, tags, step=runner.epoch, epoch=runner.epoch) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py deleted file mode 100644 index bf00d5742..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import TORCH_VERSION, digit_version -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TensorboardLoggerHook(LoggerHook): - """Class to log metrics to Tensorboard. - - Args: - log_dir (string): Save directory location. Default: None. If default - values are used, directory location is ``runner.work_dir``/tf_logs. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - """ - - def __init__(self, - log_dir=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(TensorboardLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.log_dir = log_dir - - @master_only - def before_run(self, runner): - super(TensorboardLoggerHook, self).before_run(runner) - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.1')): - try: - from tensorboardX import SummaryWriter - except ImportError: - raise ImportError('Please install tensorboardX to use ' - 'TensorboardLoggerHook.') - else: - try: - from torch.utils.tensorboard import SummaryWriter - except ImportError: - raise ImportError( - 'Please run "pip install future tensorboard" to install ' - 'the dependencies to use torch.utils.tensorboard ' - '(applicable to PyTorch 1.1 or higher)') - - if self.log_dir is None: - self.log_dir = osp.join(runner.work_dir, 'tf_logs') - self.writer = SummaryWriter(self.log_dir) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, allow_text=True) - for tag, val in tags.items(): - if isinstance(val, str): - self.writer.add_text(tag, val, self.get_iter(runner)) - else: - self.writer.add_scalar(tag, val, self.get_iter(runner)) - - @master_only - def after_run(self, runner): - self.writer.close() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/text.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/text.py deleted file mode 100644 index 644ced2c9..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/text.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import datetime -import os -import os.path as osp -from collections import OrderedDict - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.fileio.file_client import FileClient -from mmcv.utils import is_tuple_of, scandir -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TextLoggerHook(LoggerHook): - """Logger hook in text. - - In this logger hook, the information will be printed on terminal and - saved in json file. - - Args: - by_epoch (bool, optional): Whether EpochBasedRunner is used. - Default: True. - interval (int, optional): Logging interval (every k iterations). - Default: 10. - ignore_last (bool, optional): Ignore the log of last iterations in each - epoch if less than :attr:`interval`. Default: True. - reset_flag (bool, optional): Whether to clear the output buffer after - logging. Default: False. - interval_exp_name (int, optional): Logging interval for experiment - name. This feature is to help users conveniently get the experiment - information from screen or log file. Default: 1000. - out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. - If ``out_dir`` is specified, logs will be copied to a new directory - which is the concatenation of ``out_dir`` and the last level - directory of ``runner.work_dir``. Default: None. - `New in version 1.3.16.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be copied to ``out_dir``. - Default: ('.log.json', '.log', '.py'). - `New in version 1.3.16.` - keep_local (bool, optional): Whether to keep local log when - :attr:`out_dir` is specified. If False, the local log will be - removed. Default: True. - `New in version 1.3.16.` - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - - def __init__(self, - by_epoch=True, - interval=10, - ignore_last=True, - reset_flag=False, - interval_exp_name=1000, - out_dir=None, - out_suffix=('.log.json', '.log', '.py'), - keep_local=True, - file_client_args=None): - super(TextLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.by_epoch = by_epoch - self.time_sec_tot = 0 - self.interval_exp_name = interval_exp_name - - if out_dir is None and file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" when `out_dir` is not' - 'specified.') - self.out_dir = out_dir - - if not (out_dir is None or isinstance(out_dir, str) - or is_tuple_of(out_dir, str)): - raise TypeError('out_dir should be "None" or string or tuple of ' - 'string, but got {out_dir}') - self.out_suffix = out_suffix - - self.keep_local = keep_local - self.file_client_args = file_client_args - if self.out_dir is not None: - self.file_client = FileClient.infer_client(file_client_args, - self.out_dir) - - def before_run(self, runner): - super(TextLoggerHook, self).before_run(runner) - - if self.out_dir is not None: - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - # The final `self.out_dir` is the concatenation of `self.out_dir` - # and the last level directory of `runner.work_dir` - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'Text logs will be saved to {self.out_dir} by ' - f'{self.file_client.name} after the training process.')) - - self.start_iter = runner.iter - self.json_log_path = osp.join(runner.work_dir, - f'{runner.timestamp}.log.json') - if runner.meta is not None: - self._dump_log(runner.meta, runner) - - def _get_max_memory(self, runner): - device = getattr(runner.model, 'output_device', None) - mem = torch.cuda.max_memory_allocated(device=device) - mem_mb = torch.tensor([int(mem) // (1024 * 1024)], - dtype=torch.int, - device=device) - if runner.world_size > 1: - dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) - return mem_mb.item() - - def _log_info(self, log_dict, runner): - # print exp name for users to distinguish experiments - # at every ``interval_exp_name`` iterations and the end of each epoch - if runner.meta is not None and 'exp_name' in runner.meta: - if (self.every_n_iters(runner, self.interval_exp_name)) or ( - self.by_epoch and self.end_of_epoch(runner)): - exp_info = f'Exp name: {runner.meta["exp_name"]}' - runner.logger.info(exp_info) - - if log_dict['mode'] == 'train': - if isinstance(log_dict['lr'], dict): - lr_str = [] - for k, val in log_dict['lr'].items(): - lr_str.append(f'lr_{k}: {val:.3e}') - lr_str = ' '.join(lr_str) - else: - lr_str = f'lr: {log_dict["lr"]:.3e}' - - # by epoch: Epoch [4][100/1000] - # by iter: Iter [100/100000] - if self.by_epoch: - log_str = f'Epoch [{log_dict["epoch"]}]' \ - f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' - else: - log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' - log_str += f'{lr_str}, ' - - if 'time' in log_dict.keys(): - self.time_sec_tot += (log_dict['time'] * self.interval) - time_sec_avg = self.time_sec_tot / ( - runner.iter - self.start_iter + 1) - eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) - eta_str = str(datetime.timedelta(seconds=int(eta_sec))) - log_str += f'eta: {eta_str}, ' - log_str += f'time: {log_dict["time"]:.3f}, ' \ - f'data_time: {log_dict["data_time"]:.3f}, ' - # statistic memory - if torch.cuda.is_available(): - log_str += f'memory: {log_dict["memory"]}, ' - else: - # val/test time - # here 1000 is the length of the val dataloader - # by epoch: Epoch[val] [4][1000] - # by iter: Iter[val] [1000] - if self.by_epoch: - log_str = f'Epoch({log_dict["mode"]}) ' \ - f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' - else: - log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' - - log_items = [] - for name, val in log_dict.items(): - # TODO: resolve this hack - # these items have been in log_str - if name in [ - 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', - 'memory', 'epoch' - ]: - continue - if isinstance(val, float): - val = f'{val:.4f}' - log_items.append(f'{name}: {val}') - log_str += ', '.join(log_items) - - runner.logger.info(log_str) - - def _dump_log(self, log_dict, runner): - # dump log in json format - json_log = OrderedDict() - for k, v in log_dict.items(): - json_log[k] = self._round_float(v) - # only append log at last line - if runner.rank == 0: - with open(self.json_log_path, 'a+') as f: - mmcv.dump(json_log, f, file_format='json') - f.write('\n') - - def _round_float(self, items): - if isinstance(items, list): - return [self._round_float(item) for item in items] - elif isinstance(items, float): - return round(items, 5) - else: - return items - - def log(self, runner): - if 'eval_iter_num' in runner.log_buffer.output: - # this doesn't modify runner.iter and is regardless of by_epoch - cur_iter = runner.log_buffer.output.pop('eval_iter_num') - else: - cur_iter = self.get_iter(runner, inner_iter=True) - - log_dict = OrderedDict( - mode=self.get_mode(runner), - epoch=self.get_epoch(runner), - iter=cur_iter) - - # only record lr of the first param group - cur_lr = runner.current_lr() - if isinstance(cur_lr, list): - log_dict['lr'] = cur_lr[0] - else: - assert isinstance(cur_lr, dict) - log_dict['lr'] = {} - for k, lr_ in cur_lr.items(): - assert isinstance(lr_, list) - log_dict['lr'].update({k: lr_[0]}) - - if 'time' in runner.log_buffer.output: - # statistic memory - if torch.cuda.is_available(): - log_dict['memory'] = self._get_max_memory(runner) - - log_dict = dict(log_dict, **runner.log_buffer.output) - - self._log_info(log_dict, runner) - self._dump_log(log_dict, runner) - return log_dict - - def after_run(self, runner): - # copy or upload logs to self.out_dir - if self.out_dir is not None: - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - out_filepath = self.file_client.join_path( - self.out_dir, filename) - with open(local_filepath, 'r') as f: - self.file_client.put_text(f.read(), out_filepath) - - runner.logger.info( - (f'The file {local_filepath} has been uploaded to ' - f'{out_filepath}.')) - - if not self.keep_local: - os.remove(local_filepath) - runner.logger.info( - (f'{local_filepath} was removed due to the ' - '`self.keep_local=False`')) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/wandb.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/wandb.py deleted file mode 100644 index 78b890ee1..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/logger/wandb.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import scandir -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class WandbLoggerHook(LoggerHook): - """Class to log metrics with wandb. - - It requires `wandb`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the initialization keys. Check - https://docs.wandb.ai/ref/python/init for more init arguments. - interval (int): Logging interval (every k iterations). - Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - commit (bool): Save the metrics dict to the wandb server and increment - the step. If false ``wandb.log`` just updates the current metrics - dict with the row argument and metrics won't be saved until - ``wandb.log`` is called with ``commit=True``. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - log_artifact (bool): If True, artifacts in {work_dir} will be uploaded - to wandb after training ends. - Default: True - `New in version 1.4.3.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be uploaded to wandb. - Default: ('.log.json', '.log', '.py'). - `New in version 1.4.3.` - - .. _wandb: - https://docs.wandb.ai - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - commit=True, - by_epoch=True, - with_step=True, - log_artifact=True, - out_suffix=('.log.json', '.log', '.py')): - super(WandbLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_wandb() - self.init_kwargs = init_kwargs - self.commit = commit - self.with_step = with_step - self.log_artifact = log_artifact - self.out_suffix = out_suffix - - def import_wandb(self): - try: - import wandb - except ImportError: - raise ImportError( - 'Please run "pip install wandb" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(WandbLoggerHook, self).before_run(runner) - if self.wandb is None: - self.import_wandb() - if self.init_kwargs: - self.wandb.init(**self.init_kwargs) - else: - self.wandb.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - if self.with_step: - self.wandb.log( - tags, step=self.get_iter(runner), commit=self.commit) - else: - tags['global_step'] = self.get_iter(runner) - self.wandb.log(tags, commit=self.commit) - - @master_only - def after_run(self, runner): - if self.log_artifact: - wandb_artifact = self.wandb.Artifact( - name='artifacts', type='model') - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - wandb_artifact.add_file(local_filepath) - self.wandb.log_artifact(wandb_artifact) - self.wandb.join() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/lr_updater.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/lr_updater.py deleted file mode 100644 index ee2a53a65..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/lr_updater.py +++ /dev/null @@ -1,730 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from math import cos, pi - -import mmcv -from .hook import HOOKS, Hook - - -class LrUpdaterHook(Hook): - """LR Scheduler in MMCV. - - Args: - by_epoch (bool): LR changes epoch by epoch - warmup (string): Type of warmup used. It can be None(use no warmup), - 'constant', 'linear' or 'exp' - warmup_iters (int): The number of iterations or epochs that warmup - lasts - warmup_ratio (float): LR used at the beginning of warmup equals to - warmup_ratio * initial_lr - warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters - means the number of epochs that warmup lasts, otherwise means the - number of iteration that warmup lasts - """ - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.1, - warmup_by_epoch=False): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_ratio" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - self.warmup_by_epoch = warmup_by_epoch - - if self.warmup_by_epoch: - self.warmup_epochs = self.warmup_iters - self.warmup_iters = None - else: - self.warmup_epochs = None - - self.base_lr = [] # initial lr for all param groups - self.regular_lr = [] # expected lr if no warming up is performed - - def _set_lr(self, runner, lr_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, lr in zip(optim.param_groups, lr_groups[k]): - param_group['lr'] = lr - else: - for param_group, lr in zip(runner.optimizer.param_groups, - lr_groups): - param_group['lr'] = lr - - def get_lr(self, runner, base_lr): - raise NotImplementedError - - def get_regular_lr(self, runner): - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(runner, _base_lr) - for _base_lr in self.base_lr[k] - ] - lr_groups.update({k: _lr_group}) - - return lr_groups - else: - return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - if self.warmup == 'constant': - warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_lr = [_lr * (1 - k) for _lr in regular_lr] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.regular_lr, dict): - lr_groups = {} - for key, regular_lr in self.regular_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.regular_lr) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - group.setdefault('initial_lr', group['lr']) - _base_lr = [ - group['initial_lr'] for group in optim.param_groups - ] - self.base_lr.update({k: _base_lr}) - else: - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['initial_lr'] for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if self.warmup_iters is None: - epoch_len = len(runner.data_loader) - self.warmup_iters = self.warmup_epochs * epoch_len - - if not self.by_epoch: - return - - self.regular_lr = self.get_regular_lr(runner) - self._set_lr(runner, self.regular_lr) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_lr = self.get_regular_lr(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - - -@HOOKS.register_module() -class FixedLrUpdaterHook(LrUpdaterHook): - - def __init__(self, **kwargs): - super(FixedLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - return base_lr - - -@HOOKS.register_module() -class StepLrUpdaterHook(LrUpdaterHook): - """Step LR scheduler with min_lr clipping. - - Args: - step (int | list[int]): Step to decay the LR. If an int value is given, - regard it as the decay interval. If a list is given, decay LR at - these steps. - gamma (float, optional): Decay LR ratio. Default: 0.1. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. If None - is given, we don't perform lr clipping. Default: None. - """ - - def __init__(self, step, gamma=0.1, min_lr=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_lr = min_lr - super(StepLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - lr = base_lr * (self.gamma**exp) - if self.min_lr is not None: - # clip to a minimum value - lr = max(lr, self.min_lr) - return lr - - -@HOOKS.register_module() -class ExpLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, **kwargs): - self.gamma = gamma - super(ExpLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * self.gamma**progress - - -@HOOKS.register_module() -class PolyLrUpdaterHook(LrUpdaterHook): - - def __init__(self, power=1., min_lr=0., **kwargs): - self.power = power - self.min_lr = min_lr - super(PolyLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - coeff = (1 - progress / max_progress)**self.power - return (base_lr - self.min_lr) * coeff + self.min_lr - - -@HOOKS.register_module() -class InvLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, power=1., **kwargs): - self.gamma = gamma - self.power = power - super(InvLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * (1 + self.gamma * progress)**(-self.power) - - -@HOOKS.register_module() -class CosineAnnealingLrUpdaterHook(LrUpdaterHook): - """CosineAnnealing LR scheduler. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(CosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): - """Flat + Cosine lr schedule. - - Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 - - Args: - start_percent (float): When to start annealing the learning rate - after the percentage of the total training steps. - The value should be in range [0, 1). - Default: 0.75 - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - start_percent=0.75, - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - if start_percent < 0 or start_percent > 1 or not isinstance( - start_percent, float): - raise ValueError( - 'expected float between 0 and 1 start_percent, but ' - f'got {start_percent}') - self.start_percent = start_percent - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(FlatCosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - start = round(runner.max_epochs * self.start_percent) - progress = runner.epoch - start - max_progress = runner.max_epochs - start - else: - start = round(runner.max_iters * self.start_percent) - progress = runner.iter - start - max_progress = runner.max_iters - start - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress < 0: - return base_lr - else: - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class CosineRestartLrUpdaterHook(LrUpdaterHook): - """Cosine annealing with restarts learning rate scheme. - - Args: - periods (list[int]): Periods for each cosine anneling cycle. - restart_weights (list[float], optional): Restart weights at each - restart iteration. Default: [1]. - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - periods, - restart_weights=[1], - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.periods = periods - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - self.restart_weights = restart_weights - assert (len(self.periods) == len(self.restart_weights) - ), 'periods and restart_weights should have the same length.' - super(CosineRestartLrUpdaterHook, self).__init__(**kwargs) - - self.cumulative_periods = [ - sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) - ] - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - else: - progress = runner.iter - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - idx = get_position_from_periods(progress, self.cumulative_periods) - current_weight = self.restart_weights[idx] - nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] - current_periods = self.periods[idx] - - alpha = min((progress - nearest_restart) / current_periods, 1) - return annealing_cos(base_lr, target_lr, alpha, current_weight) - - -def get_position_from_periods(iteration, cumulative_periods): - """Get the position from a period list. - - It will return the index of the right-closest number in the period list. - For example, the cumulative_periods = [100, 200, 300, 400], - if iteration == 50, return 0; - if iteration == 210, return 2; - if iteration == 300, return 3. - - Args: - iteration (int): Current iteration. - cumulative_periods (list[int]): Cumulative period list. - - Returns: - int: The position of the right-closest number in the period list. - """ - for i, period in enumerate(cumulative_periods): - if iteration < period: - return i - raise ValueError(f'Current iteration {iteration} exceeds ' - f'cumulative_periods {cumulative_periods}') - - -@HOOKS.register_module() -class CyclicLrUpdaterHook(LrUpdaterHook): - """Cyclic LR Scheduler. - - Implement the cyclical learning rate policy (CLR) described in - https://arxiv.org/pdf/1506.01186.pdf - - Different from the original paper, we use cosine annealing rather than - triangular policy inside a cycle. This improves the performance in the - 3D detection area. - - Args: - by_epoch (bool, optional): Whether to update LR by epoch. - target_ratio (tuple[float], optional): Relative ratio of the highest LR - and the lowest LR to the initial LR. - cyclic_times (int, optional): Number of cycles during training - step_ratio_up (float, optional): The ratio of the increasing process of - LR in the total cycle. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch=False, - target_ratio=(10, 1e-4), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - gamma=1, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - assert 0 < gamma <= 1, \ - '"gamma" must be in range (0, 1]' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.max_iter_per_phase = None - self.lr_phases = [] # init lr_phases - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicLrUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicLrUpdaterHook, self).before_run(runner) - # initiate lr_phases - # total lr_phases are separated as up and down - self.max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * self.max_iter_per_phase) - self.lr_phases.append([0, iter_up_phase, 1, self.target_ratio[0]]) - self.lr_phases.append([ - iter_up_phase, self.max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - # Update weight decay - scale = self.gamma**curr_cycle - - for (start_iter, end_iter, start_ratio, end_ratio) in self.lr_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_lr and base lr. The target end_ratio can be - # expressed as: - # end_ratio = (base_lr + scale * (max_lr - base_lr)) / base_lr - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_lr * start_ratio, - base_lr * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleLrUpdaterHook(LrUpdaterHook): - """One Cycle LR Scheduler. - - The 1cycle learning rate policy changes the learning rate after every - batch. The one cycle learning rate policy is described in - https://arxiv.org/pdf/1708.07120.pdf - - Args: - max_lr (float or list): Upper learning rate boundaries in the cycle - for each parameter group. - total_steps (int, optional): The total number of steps in the cycle. - Note that if a value is not provided here, it will be the max_iter - of runner. Default: None. - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - div_factor (float): Determines the initial learning rate via - initial_lr = max_lr/div_factor - Default: 25 - final_div_factor (float): Determines the minimum learning rate via - min_lr = initial_lr/final_div_factor - Default: 1e4 - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - max_lr, - total_steps=None, - pct_start=0.3, - anneal_strategy='cos', - div_factor=25, - final_div_factor=1e4, - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch = False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(max_lr, (numbers.Number, list, dict)): - raise ValueError('the type of max_lr must be the one of list or ' - f'dict, but got {type(max_lr)}') - self._max_lr = max_lr - if total_steps is not None: - if not isinstance(total_steps, int): - raise ValueError('the type of total_steps must be int, but' - f'got {type(total_steps)}') - self.total_steps = total_steps - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.div_factor = div_factor - self.final_div_factor = final_div_factor - self.three_phase = three_phase - self.lr_phases = [] # init lr_phases - super(OneCycleLrUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if hasattr(self, 'total_steps'): - total_steps = self.total_steps - else: - total_steps = runner.max_iters - if total_steps < runner.max_iters: - raise ValueError( - 'The total steps must be greater than or equal to max ' - f'iterations {runner.max_iters} of runner, but total steps ' - f'is {total_steps}.') - - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - _max_lr = format_param(k, optim, self._max_lr) - self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(optim.param_groups, self.base_lr[k]): - group.setdefault('initial_lr', lr) - else: - k = type(runner.optimizer).__name__ - _max_lr = format_param(k, runner.optimizer, self._max_lr) - self.base_lr = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(runner.optimizer.param_groups, self.base_lr): - group.setdefault('initial_lr', lr) - - if self.three_phase: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append([ - float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 - ]) - self.lr_phases.append( - [total_steps - 1, 1, 1 / self.final_div_factor]) - else: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append( - [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - start_iter = 0 - for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): - if curr_iter <= end_iter: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, - pct) - break - start_iter = end_iter - return lr - - -@HOOKS.register_module() -class LinearAnnealingLrUpdaterHook(LrUpdaterHook): - """Linear annealing LR Scheduler decays the learning rate of each parameter - group linearly. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(LinearAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_linear(base_lr, target_lr, progress / max_progress) - - -def annealing_cos(start, end, factor, weight=1): - """Calculate annealing cos learning rate. - - Cosine anneal from `weight * start + (1 - weight) * end` to `end` as - percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the cosine annealing. - end (float): The ending learing rate of the cosine annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - weight (float, optional): The combination factor of `start` and `end` - when calculating the actual starting learning rate. Default to 1. - """ - cos_out = cos(pi * factor) + 1 - return end + 0.5 * weight * (start - end) * cos_out - - -def annealing_linear(start, end, factor): - """Calculate annealing linear learning rate. - - Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the linear annealing. - end (float): The ending learing rate of the linear annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - """ - return start + (end - start) * factor - - -def format_param(name, optim, param): - if isinstance(param, numbers.Number): - return [param] * len(optim.param_groups) - elif isinstance(param, (list, tuple)): # multi param groups - if len(param) != len(optim.param_groups): - raise ValueError(f'expected {len(optim.param_groups)} ' - f'values for {name}, got {len(param)}') - return param - else: # multi optimizers - if name not in param: - raise KeyError(f'{name} is not found in {param.keys()}') - return param[name] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/memory.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/memory.py deleted file mode 100644 index 70cf9a838..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class EmptyCacheHook(Hook): - - def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): - self._before_epoch = before_epoch - self._after_epoch = after_epoch - self._after_iter = after_iter - - def after_iter(self, runner): - if self._after_iter: - torch.cuda.empty_cache() - - def before_epoch(self, runner): - if self._before_epoch: - torch.cuda.empty_cache() - - def after_epoch(self, runner): - if self._after_epoch: - torch.cuda.empty_cache() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/momentum_updater.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/momentum_updater.py deleted file mode 100644 index aa15fe23c..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/momentum_updater.py +++ /dev/null @@ -1,566 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -from .hook import HOOKS, Hook -from .lr_updater import annealing_cos, annealing_linear, format_param - - -class MomentumUpdaterHook(Hook): - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.9): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_momentum" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - - self.base_momentum = [] # initial momentum for all param groups - self.regular_momentum = [ - ] # expected momentum if no warming up is performed - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, base_momentum): - raise NotImplementedError - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k in runner.optimizer.keys(): - _momentum_group = [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum[k] - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - return [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum - ] - - def get_warmup_momentum(self, cur_iters): - - def _get_warmup_momentum(cur_iters, regular_momentum): - if self.warmup == 'constant': - warmup_momentum = [ - _momentum / self.warmup_ratio - for _momentum in regular_momentum - ] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_momentum = [ - _momentum / (1 - k) for _momentum in regular_momentum - ] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_momentum = [ - _momentum / k for _momentum in regular_momentum - ] - return warmup_momentum - - if isinstance(self.regular_momentum, dict): - momentum_groups = {} - for key, regular_momentum in self.regular_momentum.items(): - momentum_groups[key] = _get_warmup_momentum( - cur_iters, regular_momentum) - return momentum_groups - else: - return _get_warmup_momentum(cur_iters, self.regular_momentum) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, - # if 'initial_momentum' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_momentum = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - _base_momentum = [ - group['initial_momentum'] for group in optim.param_groups - ] - self.base_momentum.update({k: _base_momentum}) - else: - for group in runner.optimizer.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - self.base_momentum = [ - group['initial_momentum'] - for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if not self.by_epoch: - return - self.regular_momentum = self.get_regular_momentum(runner) - self._set_momentum(runner, self.regular_momentum) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_momentum = self.get_regular_momentum(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - - -@HOOKS.register_module() -class StepMomentumUpdaterHook(MomentumUpdaterHook): - """Step momentum scheduler with min value clipping. - - Args: - step (int | list[int]): Step to decay the momentum. If an int value is - given, regard it as the decay interval. If a list is given, decay - momentum at these steps. - gamma (float, optional): Decay momentum ratio. Default: 0.5. - min_momentum (float, optional): Minimum momentum value to keep. If - momentum after decay is lower than this value, it will be clipped - accordingly. If None is given, we don't perform lr clipping. - Default: None. - """ - - def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_momentum = min_momentum - super(StepMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - momentum = base_momentum * (self.gamma**exp) - if self.min_momentum is not None: - # clip to a minimum value - momentum = max(momentum, self.min_momentum) - return momentum - - -@HOOKS.register_module() -class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Cosine annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(CosineAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_cos(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class LinearAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Linear annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(LinearAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_linear(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class CyclicMomentumUpdaterHook(MomentumUpdaterHook): - """Cyclic momentum Scheduler. - - Implement the cyclical momentum scheduler policy described in - https://arxiv.org/pdf/1708.07120.pdf - - This momentum scheduler usually used together with the CyclicLRUpdater - to improve the performance in the 3D detection area. - - Args: - target_ratio (tuple[float]): Relative ratio of the lowest momentum and - the highest momentum to the initial momentum. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of momentum - in the total cycle. - by_epoch (bool): Whether to update momentum by epoch. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch=False, - target_ratio=(0.85 / 0.95, 1), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - gamma=1, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.momentum_phases = [] # init momentum_phases - - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - # currently only support by_epoch=False - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicMomentumUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicMomentumUpdaterHook, self).before_run(runner) - # initiate momentum_phases - # total momentum_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.max_iter_per_phase = max_iter_per_phase - self.momentum_phases.append( - [0, iter_up_phase, 1, self.target_ratio[0]]) - self.momentum_phases.append([ - iter_up_phase, max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_momentum(self, runner, base_momentum): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - scale = self.gamma**curr_cycle - for (start_iter, end_iter, start_ratio, end_ratio) \ - in self.momentum_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_momentum and base momentum. The target end_ratio - # can be expressed as: - # end_ratio = (base_momentum + scale * \ - # (max_momentum - base_momentum)) / base_momentum - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_momentum * start_ratio, - base_momentum * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): - """OneCycle momentum Scheduler. - - This momentum scheduler usually used together with the OneCycleLrUpdater - to improve the performance. - - Args: - base_momentum (float or list): Lower momentum boundaries in the cycle - for each parameter group. Note that momentum is cycled inversely - to learning rate; at the peak of a cycle, momentum is - 'base_momentum' and learning rate is 'max_lr'. - Default: 0.85 - max_momentum (float or list): Upper momentum boundaries in the cycle - for each parameter group. Functionally, - it defines the cycle amplitude (max_momentum - base_momentum). - Note that momentum is cycled inversely - to learning rate; at the start of a cycle, momentum is - 'max_momentum' and learning rate is 'base_lr' - Default: 0.95 - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - base_momentum=0.85, - max_momentum=0.95, - pct_start=0.3, - anneal_strategy='cos', - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch=False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(base_momentum, (float, list, dict)): - raise ValueError('base_momentum must be the type among of float,' - 'list or dict.') - self._base_momentum = base_momentum - if not isinstance(max_momentum, (float, list, dict)): - raise ValueError('max_momentum must be the type among of float,' - 'list or dict.') - self._max_momentum = max_momentum - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('Expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must by one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.three_phase = three_phase - self.momentum_phases = [] # init momentum_phases - super(OneCycleMomentumUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip( - optim.param_groups, _base_momentum, _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - else: - optim = runner.optimizer - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - k = type(optim).__name__ - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip(optim.param_groups, - _base_momentum, - _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - - if self.three_phase: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': - float(2 * self.pct_start * runner.max_iters) - 2, - 'start_momentum': - 'base_momentum', - 'end_momentum': - 'max_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'max_momentum', - 'end_momentum': 'max_momentum' - }) - else: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'base_momentum', - 'end_momentum': 'max_momentum' - }) - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, param_group): - curr_iter = runner.iter - start_iter = 0 - for i, phase in enumerate(self.momentum_phases): - end_iter = phase['end_iter'] - if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - momentum = self.anneal_func( - param_group[phase['start_momentum']], - param_group[phase['end_momentum']], pct) - break - start_iter = end_iter - return momentum - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k, optim in runner.optimizer.items(): - _momentum_group = [ - self.get_momentum(runner, param_group) - for param_group in optim.param_groups - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - momentum_groups = [] - for param_group in runner.optimizer.param_groups: - momentum_groups.append(self.get_momentum(runner, param_group)) - return momentum_groups diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/optimizer.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/optimizer.py deleted file mode 100644 index 12c885183..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/optimizer.py +++ /dev/null @@ -1,556 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -from collections import defaultdict -from itertools import chain - -from torch.nn.utils import clip_grad - -from mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version -from ..dist_utils import allreduce_grads -from ..fp16_utils import LossScaler, wrap_fp16_model -from .hook import HOOKS, Hook - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - from torch.cuda.amp import GradScaler -except ImportError: - pass - - -@HOOKS.register_module() -class OptimizerHook(Hook): - """A hook contains custom operations for the optimizer. - - Args: - grad_clip (dict, optional): A config dict to control the clip_grad. - Default: None. - detect_anomalous_params (bool): This option is only used for - debugging which will slow down the training speed. - Detect anomalous parameters that are not included in - the computational graph with `loss` as the root. - There are two cases - - - Parameters were not used during - forward pass. - - Parameters were not used to produce - loss. - Default: False. - """ - - def __init__(self, grad_clip=None, detect_anomalous_params=False): - self.grad_clip = grad_clip - self.detect_anomalous_params = detect_anomalous_params - - def clip_grads(self, params): - params = list( - filter(lambda p: p.requires_grad and p.grad is not None, params)) - if len(params) > 0: - return clip_grad.clip_grad_norm_(params, **self.grad_clip) - - def after_train_iter(self, runner): - runner.optimizer.zero_grad() - if self.detect_anomalous_params: - self.detect_anomalous_parameters(runner.outputs['loss'], runner) - runner.outputs['loss'].backward() - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - - def detect_anomalous_parameters(self, loss, runner): - logger = runner.logger - parameters_in_graph = set() - visited = set() - - def traverse(grad_fn): - if grad_fn is None: - return - if grad_fn not in visited: - visited.add(grad_fn) - if hasattr(grad_fn, 'variable'): - parameters_in_graph.add(grad_fn.variable) - parents = grad_fn.next_functions - if parents is not None: - for parent in parents: - grad_fn = parent[0] - traverse(grad_fn) - - traverse(loss.grad_fn) - for n, p in runner.model.named_parameters(): - if p not in parameters_in_graph and p.requires_grad: - logger.log( - level=logging.ERROR, - msg=f'{n} with shape {p.size()} is not ' - f'in the computational graph \n') - - -@HOOKS.register_module() -class GradientCumulativeOptimizerHook(OptimizerHook): - """Optimizer Hook implements multi-iters gradient cumulating. - - Args: - cumulative_iters (int, optional): Num of gradient cumulative iters. - The optimizer will step every `cumulative_iters` iters. - Defaults to 1. - - Examples: - >>> # Use cumulative_iters to simulate a large batch size - >>> # It is helpful when the hardware cannot handle a large batch size. - >>> loader = DataLoader(data, batch_size=64) - >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) - >>> # almost equals to - >>> loader = DataLoader(data, batch_size=256) - >>> optim_hook = OptimizerHook() - """ - - def __init__(self, cumulative_iters=1, **kwargs): - super(GradientCumulativeOptimizerHook, self).__init__(**kwargs) - - assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ - f'cumulative_iters only accepts positive int, but got ' \ - f'{type(cumulative_iters)} instead.' - - self.cumulative_iters = cumulative_iters - self.divisible_iters = 0 - self.remainder_iters = 0 - self.initialized = False - - def has_batch_norm(self, module): - if isinstance(module, _BatchNorm): - return True - for m in module.children(): - if self.has_batch_norm(m): - return True - return False - - def _init(self, runner): - if runner.iter % self.cumulative_iters != 0: - runner.logger.warning( - 'Resume iter number is not divisible by cumulative_iters in ' - 'GradientCumulativeOptimizerHook, which means the gradient of ' - 'some iters is lost and the result may be influenced slightly.' - ) - - if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: - runner.logger.warning( - 'GradientCumulativeOptimizerHook may slightly decrease ' - 'performance if the model has BatchNorm layers.') - - residual_iters = runner.max_iters - runner.iter - - self.divisible_iters = ( - residual_iters // self.cumulative_iters * self.cumulative_iters) - self.remainder_iters = residual_iters - self.divisible_iters - - self.initialized = True - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - runner.optimizer.zero_grad() - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - self.loss_scaler = GradScaler() - elif isinstance(loss_scale, float): - self._scale_update_param = loss_scale - self.loss_scaler = GradScaler(init_scale=loss_scale) - elif isinstance(loss_scale, dict): - self.loss_scaler = GradScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training.""" - # wrap model mode to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer to - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients. - 3. Unscale the optimizer’s gradient tensors. - 4. Call optimizer.step() and update scale factor. - 5. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - - self.loss_scaler.scale(runner.outputs['loss']).backward() - self.loss_scaler.unscale_(runner.optimizer) - # grad clip - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using PyTorch's implementation) implements - multi-iters gradient cumulating. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - """ - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - - self.loss_scaler.scale(loss).backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - self.loss_scaler.unscale_(runner.optimizer) - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() - -else: - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (mmcv's implementation). - - The steps of fp16 optimizer is as follows. - 1. Scale the loss value. - 2. BP in the fp16 model. - 2. Copy gradients from fp16 model to fp32 weights. - 3. Update fp32 weights. - 4. Copy updated parameters from fp32 weights to fp16 model. - - Refer to https://arxiv.org/abs/1710.03740 for more details. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of LossScaler. - Defaults to 512. - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - if loss_scale == 'dynamic': - self.loss_scaler = LossScaler(mode='dynamic') - elif isinstance(loss_scale, float): - self.loss_scaler = LossScaler( - init_scale=loss_scale, mode='static') - elif isinstance(loss_scale, dict): - self.loss_scaler = LossScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training. - - 1. Make a master copy of fp32 weights for optimization. - 2. Convert the main model from fp32 to fp16. - """ - # keep a copy of fp32 weights - old_groups = runner.optimizer.param_groups - runner.optimizer.param_groups = copy.deepcopy( - runner.optimizer.param_groups) - state = defaultdict(dict) - p_map = { - old_p: p - for old_p, p in zip( - chain(*(g['params'] for g in old_groups)), - chain(*(g['params'] - for g in runner.optimizer.param_groups))) - } - for k, v in runner.optimizer.state.items(): - state[p_map[k]] = v - runner.optimizer.state = state - # convert model to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer `loss_scalar.py` - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients (fp16). - 3. Copy gradients from the model to the fp32 weight copy. - 4. Scale the gradients back and update the fp32 weight copy. - 5. Copy back the params from fp32 weight copy to the fp16 model. - 6. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - # scale the loss value - scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale - scaled_loss.backward() - # copy fp16 grads in the model to fp32 params in the optimizer - - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - self.loss_scaler.update_scale(has_overflow) - if has_overflow: - runner.logger.warning('Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using mmcv implementation) implements multi- - iters gradient cumulating.""" - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - - loss = runner.outputs['loss'] - loss = loss / loss_factor - - # scale the loss value - scaled_loss = loss * self.loss_scaler.loss_scale - scaled_loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - else: - runner.logger.warning( - 'Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - self.loss_scaler.update_scale(has_overflow) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/profiler.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/profiler.py deleted file mode 100644 index fef9adc13..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/profiler.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Callable, List, Optional, Union - -import torch - -from ..dist_utils import master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ProfilerHook(Hook): - """Profiler to analyze performance during training. - - PyTorch Profiler is a tool that allows the collection of the performance - metrics during the training. More details on Profiler can be found at - https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile - - Args: - by_epoch (bool): Profile performance by epoch or by iteration. - Default: True. - profile_iters (int): Number of iterations for profiling. - If ``by_epoch=True``, profile_iters indicates that they are the - first profile_iters epochs at the beginning of the - training, otherwise it indicates the first profile_iters - iterations. Default: 1. - activities (list[str]): List of activity groups (CPU, CUDA) to use in - profiling. Default: ['cpu', 'cuda']. - schedule (dict, optional): Config of generating the callable schedule. - if schedule is None, profiler will not add step markers into the - trace and table view. Default: None. - on_trace_ready (callable, dict): Either a handler or a dict of generate - handler. Default: None. - record_shapes (bool): Save information about operator's input shapes. - Default: False. - profile_memory (bool): Track tensor memory allocation/deallocation. - Default: False. - with_stack (bool): Record source information (file and line number) - for the ops. Default: False. - with_flops (bool): Use formula to estimate the FLOPS of specific - operators (matrix multiplication and 2D convolution). - Default: False. - json_trace_path (str, optional): Exports the collected trace in Chrome - JSON format. Default: None. - - Example: - >>> runner = ... # instantiate a Runner - >>> # tensorboard trace - >>> trace_config = dict(type='tb_trace', dir_name='work_dir') - >>> profiler_config = dict(on_trace_ready=trace_config) - >>> runner.register_profiler_hook(profiler_config) - >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) - """ - - def __init__(self, - by_epoch: bool = True, - profile_iters: int = 1, - activities: List[str] = ['cpu', 'cuda'], - schedule: Optional[dict] = None, - on_trace_ready: Optional[Union[Callable, dict]] = None, - record_shapes: bool = False, - profile_memory: bool = False, - with_stack: bool = False, - with_flops: bool = False, - json_trace_path: Optional[str] = None) -> None: - try: - from torch import profiler # torch version >= 1.8.1 - except ImportError: - raise ImportError('profiler is the new feature of torch1.8.1, ' - f'but your version is {torch.__version__}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' - self.by_epoch = by_epoch - - if profile_iters < 1: - raise ValueError('profile_iters should be greater than 0, but got ' - f'{profile_iters}') - self.profile_iters = profile_iters - - if not isinstance(activities, list): - raise ValueError( - f'activities should be list, but got {type(activities)}') - self.activities = [] - for activity in activities: - activity = activity.lower() - if activity == 'cpu': - self.activities.append(profiler.ProfilerActivity.CPU) - elif activity == 'cuda': - self.activities.append(profiler.ProfilerActivity.CUDA) - else: - raise ValueError( - f'activity should be "cpu" or "cuda", but got {activity}') - - if schedule is not None: - self.schedule = profiler.schedule(**schedule) - else: - self.schedule = None - - self.on_trace_ready = on_trace_ready - self.record_shapes = record_shapes - self.profile_memory = profile_memory - self.with_stack = with_stack - self.with_flops = with_flops - self.json_trace_path = json_trace_path - - @master_only - def before_run(self, runner): - if self.by_epoch and runner.max_epochs < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_epochs}') - - if not self.by_epoch and runner.max_iters < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_iters}') - - if callable(self.on_trace_ready): # handler - _on_trace_ready = self.on_trace_ready - elif isinstance(self.on_trace_ready, dict): # config of handler - trace_cfg = self.on_trace_ready.copy() - trace_type = trace_cfg.pop('type') # log_trace handler - if trace_type == 'log_trace': - - def _log_handler(prof): - print(prof.key_averages().table(**trace_cfg)) - - _on_trace_ready = _log_handler - elif trace_type == 'tb_trace': # tensorboard_trace handler - try: - import torch_tb_profiler # noqa: F401 - except ImportError: - raise ImportError('please run "pip install ' - 'torch-tb-profiler" to install ' - 'torch_tb_profiler') - _on_trace_ready = torch.profiler.tensorboard_trace_handler( - **trace_cfg) - else: - raise ValueError('trace_type should be "log_trace" or ' - f'"tb_trace", but got {trace_type}') - elif self.on_trace_ready is None: - _on_trace_ready = None # type: ignore - else: - raise ValueError('on_trace_ready should be handler, dict or None, ' - f'but got {type(self.on_trace_ready)}') - - if self.by_epoch and runner.max_epochs > 1: - warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' - 'instead of 1 epoch. Since profiler will slow down ' - 'the training, it is recommended to train 1 epoch ' - 'with ProfilerHook and adjust your setting according' - ' to the profiler summary. During normal training ' - '(epoch > 1), you may disable the ProfilerHook.') - - self.profiler = torch.profiler.profile( - activities=self.activities, - schedule=self.schedule, - on_trace_ready=_on_trace_ready, - record_shapes=self.record_shapes, - profile_memory=self.profile_memory, - with_stack=self.with_stack, - with_flops=self.with_flops) - - self.profiler.__enter__() - runner.logger.info('profiler is profiling...') - - @master_only - def after_train_epoch(self, runner): - if self.by_epoch and runner.epoch == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) - - @master_only - def after_train_iter(self, runner): - self.profiler.step() - if not self.by_epoch and runner.iter == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/sampler_seed.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/sampler_seed.py deleted file mode 100644 index ee0dc6bdd..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/sampler_seed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class DistSamplerSeedHook(Hook): - """Data-loading sampler for distributed training. - - When distributed training, it is only useful in conjunction with - :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same - purpose with :obj:`IterLoader`. - """ - - def before_epoch(self, runner): - if hasattr(runner.data_loader.sampler, 'set_epoch'): - # in case the data loader uses `SequentialSampler` in Pytorch - runner.data_loader.sampler.set_epoch(runner.epoch) - elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): - # batch sampler in pytorch warps the sampler as its attributes. - runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/sync_buffer.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/sync_buffer.py deleted file mode 100644 index 6376b7ff8..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/hooks/sync_buffer.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..dist_utils import allreduce_params -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class SyncBuffersHook(Hook): - """Synchronize model buffers such as running_mean and running_var in BN at - the end of each epoch. - - Args: - distributed (bool): Whether distributed training is used. It is - effective only for distributed training. Defaults to True. - """ - - def __init__(self, distributed=True): - self.distributed = distributed - - def after_epoch(self, runner): - """All-reduce model buffers at the end of each epoch.""" - if self.distributed: - allreduce_params(runner.model.buffers()) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/iter_based_runner.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/iter_based_runner.py deleted file mode 100644 index 9892b07a4..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/iter_based_runner.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch -from torch.optim import Optimizer - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .hooks import IterTimerHook -from .utils import get_host_info - - -class IterLoader: - - def __init__(self, dataloader): - self._dataloader = dataloader - self.iter_loader = iter(self._dataloader) - self._epoch = 0 - - @property - def epoch(self): - return self._epoch - - def __next__(self): - try: - data = next(self.iter_loader) - except StopIteration: - self._epoch += 1 - if hasattr(self._dataloader.sampler, 'set_epoch'): - self._dataloader.sampler.set_epoch(self._epoch) - time.sleep(2) # Prevent possible deadlock during epoch transition - self.iter_loader = iter(self._dataloader) - data = next(self.iter_loader) - - return data - - def __len__(self): - return len(self._dataloader) - - -@RUNNERS.register_module() -class IterBasedRunner(BaseRunner): - """Iteration-based Runner. - - This runner train models iteration by iteration. - """ - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._epoch = data_loader.epoch - data_batch = next(data_loader) - self.call_hook('before_train_iter') - outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.train_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_train_iter') - self._inner_iter += 1 - self._iter += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - data_batch = next(data_loader) - self.call_hook('before_val_iter') - outputs = self.model.val_step(data_batch, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.val_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_val_iter') - self._inner_iter += 1 - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, iters) to specify the - running order and iterations. E.g, [('train', 10000), - ('val', 1000)] means running 10000 iterations for training and - 1000 iterations for validation, iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - """Resume model from checkpoint. - - Args: - checkpoint (str): Checkpoint to resume from. - resume_optimizer (bool, optional): Whether resume the optimizer(s) - if the checkpoint file includes optimizer(s). Default to True. - map_location (str, optional): Same as :func:`torch.load`. - Default to 'default'. - """ - if map_location == 'default': - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - self._inner_iter = checkpoint['meta']['iter'] - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') - - def save_checkpoint(self, - out_dir, - filename_tmpl='iter_{}.pth', - meta=None, - save_optimizer=True, - create_symlink=True): - """Save checkpoint to file. - - Args: - out_dir (str): Directory to save checkpoint files. - filename_tmpl (str, optional): Checkpoint file template. - Defaults to 'iter_{}.pth'. - meta (dict, optional): Metadata to be saved in checkpoint. - Defaults to None. - save_optimizer (bool, optional): Whether save optimizer. - Defaults to True. - create_symlink (bool, optional): Whether create symlink to the - latest checkpoint file. Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.iter + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - custom_hooks_config=None): - """Register default hooks for iter-based training. - - Checkpoint hook, optimizer stepper hook and logger hooks will be set to - `by_epoch=False` by default. - - Default hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - if checkpoint_config is not None: - checkpoint_config.setdefault('by_epoch', False) - if lr_config is not None: - lr_config.setdefault('by_epoch', False) - if log_config is not None: - for info in log_config['hooks']: - info.setdefault('by_epoch', False) - super(IterBasedRunner, self).register_training_hooks( - lr_config=lr_config, - momentum_config=momentum_config, - optimizer_config=optimizer_config, - checkpoint_config=checkpoint_config, - log_config=log_config, - timer_config=IterTimerHook(), - custom_hooks_config=custom_hooks_config) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/log_buffer.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/log_buffer.py deleted file mode 100644 index d949e2941..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/log_buffer.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import numpy as np - - -class LogBuffer: - - def __init__(self): - self.val_history = OrderedDict() - self.n_history = OrderedDict() - self.output = OrderedDict() - self.ready = False - - def clear(self): - self.val_history.clear() - self.n_history.clear() - self.clear_output() - - def clear_output(self): - self.output.clear() - self.ready = False - - def update(self, vars, count=1): - assert isinstance(vars, dict) - for key, var in vars.items(): - if key not in self.val_history: - self.val_history[key] = [] - self.n_history[key] = [] - self.val_history[key].append(var) - self.n_history[key].append(count) - - def average(self, n=0): - """Average latest n values or all values.""" - assert n >= 0 - for key in self.val_history: - values = np.array(self.val_history[key][-n:]) - nums = np.array(self.n_history[key][-n:]) - avg = np.sum(values * nums) / np.sum(nums) - self.output[key] = avg - self.ready = True diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/optimizer/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/optimizer/__init__.py deleted file mode 100644 index 53c34d047..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/optimizer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, - build_optimizer_constructor) -from .default_constructor import DefaultOptimizerConstructor - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', - 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/optimizer/builder.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/optimizer/builder.py deleted file mode 100644 index f9234eed8..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/optimizer/builder.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect - -import torch - -from ...utils import Registry, build_from_cfg - -OPTIMIZERS = Registry('optimizer') -OPTIMIZER_BUILDERS = Registry('optimizer builder') - - -def register_torch_optimizers(): - torch_optimizers = [] - for module_name in dir(torch.optim): - if module_name.startswith('__'): - continue - _optim = getattr(torch.optim, module_name) - if inspect.isclass(_optim) and issubclass(_optim, - torch.optim.Optimizer): - OPTIMIZERS.register_module()(_optim) - torch_optimizers.append(module_name) - return torch_optimizers - - -TORCH_OPTIMIZERS = register_torch_optimizers() - - -def build_optimizer_constructor(cfg): - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/optimizer/default_constructor.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/optimizer/default_constructor.py deleted file mode 100644 index ae97db880..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/optimizer/default_constructor.py +++ /dev/null @@ -1,250 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -from torch.nn import GroupNorm, LayerNorm - -from mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of -from mmcv.utils.ext_loader import check_ops_exist -from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS - - -@OPTIMIZER_BUILDERS.register_module() -class DefaultOptimizerConstructor: - """Default constructor for optimizers. - - By default each parameter share the same optimizer settings, and we - provide an argument ``paramwise_cfg`` to specify parameter-wise settings. - It is a dict and may contain the following fields: - - - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If - one of the keys in ``custom_keys`` is a substring of the name of one - parameter, then the setting of the parameter will be specified by - ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will - be ignored. It should be noted that the aforementioned ``key`` is the - longest key that is a substring of the name of the parameter. If there - are multiple matched keys with the same length, then the key with lower - alphabet order will be chosen. - ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` - and ``decay_mult``. See Example 2 below. - - ``bias_lr_mult`` (float): It will be multiplied to the learning - rate for all bias parameters (except for those in normalization - layers and offset layers of DCN). - - ``bias_decay_mult`` (float): It will be multiplied to the weight - decay for all bias parameters (except for those in - normalization layers, depthwise conv layers, offset layers of DCN). - - ``norm_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of normalization - layers. - - ``dwconv_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of depthwise conv - layers. - - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning - rate for parameters of offset layer in the deformable convs - of a model. - - ``bypass_duplicate`` (bool): If true, the duplicate parameters - would not be added into optimizer. Default: False. - - Note: - - 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will - override the effect of ``bias_lr_mult`` in the bias of offset layer. - So be careful when using both ``bias_lr_mult`` and - ``dcn_offset_lr_mult``. If you wish to apply both of them to the offset - layer in deformable convs, set ``dcn_offset_lr_mult`` to the original - ``dcn_offset_lr_mult`` * ``bias_lr_mult``. - - 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will - apply it to all the DCN layers in the model. So be careful when the - model contains multiple DCN layers in places other than backbone. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - optimizer_cfg (dict): The config dict of the optimizer. - Positional fields are - - - `type`: class name of the optimizer. - - Optional fields are - - - any arguments of the corresponding optimizer type, e.g., - lr, weight_decay, momentum, etc. - paramwise_cfg (dict, optional): Parameter-wise options. - - Example 1: - >>> model = torch.nn.modules.Conv1d(1, 1, 1) - >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, - >>> weight_decay=0.0001) - >>> paramwise_cfg = dict(norm_decay_mult=0.) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - - Example 2: - >>> # assume model have attribute model.backbone and model.cls_head - >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) - >>> paramwise_cfg = dict(custom_keys={ - 'backbone': dict(lr_mult=0.1, decay_mult=0.9)}) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - >>> # Then the `lr` and `weight_decay` for model.backbone is - >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for - >>> # model.cls_head is (0.01, 0.95). - """ - - def __init__(self, optimizer_cfg, paramwise_cfg=None): - if not isinstance(optimizer_cfg, dict): - raise TypeError('optimizer_cfg should be a dict', - f'but got {type(optimizer_cfg)}') - self.optimizer_cfg = optimizer_cfg - self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg - self.base_lr = optimizer_cfg.get('lr', None) - self.base_wd = optimizer_cfg.get('weight_decay', None) - self._validate_cfg() - - def _validate_cfg(self): - if not isinstance(self.paramwise_cfg, dict): - raise TypeError('paramwise_cfg should be None or a dict, ' - f'but got {type(self.paramwise_cfg)}') - - if 'custom_keys' in self.paramwise_cfg: - if not isinstance(self.paramwise_cfg['custom_keys'], dict): - raise TypeError( - 'If specified, custom_keys must be a dict, ' - f'but got {type(self.paramwise_cfg["custom_keys"])}') - if self.base_wd is None: - for key in self.paramwise_cfg['custom_keys']: - if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: - raise ValueError('base_wd should not be None') - - # get base lr and weight decay - # weight_decay must be explicitly specified if mult is specified - if ('bias_decay_mult' in self.paramwise_cfg - or 'norm_decay_mult' in self.paramwise_cfg - or 'dwconv_decay_mult' in self.paramwise_cfg): - if self.base_wd is None: - raise ValueError('base_wd should not be None') - - def _is_in(self, param_group, param_group_list): - assert is_list_of(param_group_list, dict) - param = set(param_group['params']) - param_set = set() - for group in param_group_list: - param_set.update(set(group['params'])) - - return not param.isdisjoint(param_set) - - def add_params(self, params, module, prefix='', is_dcn_module=None): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - prefix (str): The prefix of the module - is_dcn_module (int|float|None): If the current module is a - submodule of DCN, `is_dcn_module` will be passed to - control conv_offset layer's learning rate. Defaults to None. - """ - # get param-wise options - custom_keys = self.paramwise_cfg.get('custom_keys', {}) - # first sort with alphabet order and then sort with reversed len of str - sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) - - bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) - bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) - norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) - dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) - bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) - dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) - - # special rules for norm layers and depth-wise conv layers - is_norm = isinstance(module, - (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) - is_dwconv = ( - isinstance(module, torch.nn.Conv2d) - and module.in_channels == module.groups) - - for name, param in module.named_parameters(recurse=False): - param_group = {'params': [param]} - if not param.requires_grad: - params.append(param_group) - continue - if bypass_duplicate and self._is_in(param_group, params): - warnings.warn(f'{prefix} is duplicate. It is skipped since ' - f'bypass_duplicate={bypass_duplicate}') - continue - # if the parameter match one of the custom keys, ignore other rules - is_custom = False - for key in sorted_keys: - if key in f'{prefix}.{name}': - is_custom = True - lr_mult = custom_keys[key].get('lr_mult', 1.) - param_group['lr'] = self.base_lr * lr_mult - if self.base_wd is not None: - decay_mult = custom_keys[key].get('decay_mult', 1.) - param_group['weight_decay'] = self.base_wd * decay_mult - break - - if not is_custom: - # bias_lr_mult affects all bias parameters - # except for norm.bias dcn.conv_offset.bias - if name == 'bias' and not (is_norm or is_dcn_module): - param_group['lr'] = self.base_lr * bias_lr_mult - - if (prefix.find('conv_offset') != -1 and is_dcn_module - and isinstance(module, torch.nn.Conv2d)): - # deal with both dcn_offset's bias & weight - param_group['lr'] = self.base_lr * dcn_offset_lr_mult - - # apply weight decay policies - if self.base_wd is not None: - # norm decay - if is_norm: - param_group[ - 'weight_decay'] = self.base_wd * norm_decay_mult - # depth-wise conv - elif is_dwconv: - param_group[ - 'weight_decay'] = self.base_wd * dwconv_decay_mult - # bias lr and decay - elif name == 'bias' and not is_dcn_module: - # TODO: current bias_decay_mult will have affect on DCN - param_group[ - 'weight_decay'] = self.base_wd * bias_decay_mult - params.append(param_group) - - if check_ops_exist(): - from mmcv.ops import DeformConv2d, ModulatedDeformConv2d - is_dcn_module = isinstance(module, - (DeformConv2d, ModulatedDeformConv2d)) - else: - is_dcn_module = False - for child_name, child_mod in module.named_children(): - child_prefix = f'{prefix}.{child_name}' if prefix else child_name - self.add_params( - params, - child_mod, - prefix=child_prefix, - is_dcn_module=is_dcn_module) - - def __call__(self, model): - if hasattr(model, 'module'): - model = model.module - - optimizer_cfg = self.optimizer_cfg.copy() - # if no paramwise option is specified, just use the global setting - if not self.paramwise_cfg: - optimizer_cfg['params'] = model.parameters() - return build_from_cfg(optimizer_cfg, OPTIMIZERS) - - # set param-wise lr and weight decay recursively - params = [] - self.add_params(params, model) - optimizer_cfg['params'] = params - - return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/priority.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/priority.py deleted file mode 100644 index 64cc4e3a0..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/priority.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from enum import Enum - - -class Priority(Enum): - """Hook priority levels. - - +--------------+------------+ - | Level | Value | - +==============+============+ - | HIGHEST | 0 | - +--------------+------------+ - | VERY_HIGH | 10 | - +--------------+------------+ - | HIGH | 30 | - +--------------+------------+ - | ABOVE_NORMAL | 40 | - +--------------+------------+ - | NORMAL | 50 | - +--------------+------------+ - | BELOW_NORMAL | 60 | - +--------------+------------+ - | LOW | 70 | - +--------------+------------+ - | VERY_LOW | 90 | - +--------------+------------+ - | LOWEST | 100 | - +--------------+------------+ - """ - - HIGHEST = 0 - VERY_HIGH = 10 - HIGH = 30 - ABOVE_NORMAL = 40 - NORMAL = 50 - BELOW_NORMAL = 60 - LOW = 70 - VERY_LOW = 90 - LOWEST = 100 - - -def get_priority(priority): - """Get priority value. - - Args: - priority (int or str or :obj:`Priority`): Priority. - - Returns: - int: The priority value. - """ - if isinstance(priority, int): - if priority < 0 or priority > 100: - raise ValueError('priority must be between 0 and 100') - return priority - elif isinstance(priority, Priority): - return priority.value - elif isinstance(priority, str): - return Priority[priority.upper()].value - else: - raise TypeError('priority must be an integer or Priority enum value') diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/utils.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/utils.py deleted file mode 100644 index 144d11e1a..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/runner/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random -import sys -import time -import warnings -from getpass import getuser -from socket import gethostname - -import numpy as np -import torch - -import mmcv - - -def get_host_info(): - """Get hostname and username. - - Return empty string if exception raised, e.g. ``getpass.getuser()`` will - lead to error in docker container - """ - host = '' - try: - host = f'{getuser()}@{gethostname()}' - except Exception as e: - warnings.warn(f'Host or user not found: {str(e)}') - finally: - return host - - -def get_time_str(): - return time.strftime('%Y%m%d_%H%M%S', time.localtime()) - - -def obj_from_dict(info, parent=None, default_args=None): - """Initialize an object from dict. - - The dict must contain the key "type", which indicates the object type, it - can be either a string or type, such as "list" or ``list``. Remaining - fields are treated as the arguments for constructing the object. - - Args: - info (dict): Object types and arguments. - parent (:class:`module`): Module which may containing expected object - classes. - default_args (dict, optional): Default arguments for initializing the - object. - - Returns: - any type: Object built from the dict. - """ - assert isinstance(info, dict) and 'type' in info - assert isinstance(default_args, dict) or default_args is None - args = info.copy() - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if parent is not None: - obj_type = getattr(parent, obj_type) - else: - obj_type = sys.modules[obj_type] - elif not isinstance(obj_type, type): - raise TypeError('type must be a str or valid type, but ' - f'got {type(obj_type)}') - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - return obj_type(**args) - - -def set_random_seed(seed, deterministic=False, use_rank_shift=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - rank_shift (bool): Whether to add rank number to the random seed to - have different random seed in different threads. Default: False. - """ - if use_rank_shift: - rank, _ = mmcv.runner.get_dist_info() - seed += rank - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/__init__.py deleted file mode 100644 index e0825ed67..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/__init__.py +++ /dev/null @@ -1,78 +0,0 @@ -# flake8: noqa -# Copyright (c) OpenMMLab. All rights reserved. -from .config import Config, ConfigDict, DictAction -from .misc import (check_prerequisites, concat_list, deprecated_api_warning, - has_method, import_modules_from_strings, is_list_of, - is_method_overridden, is_seq_of, is_str, is_tuple_of, - iter_cast, list_cast, requires_executable, requires_package, - slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, - to_ntuple, tuple_cast) -from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, - scandir, symlink) -from .progressbar import (ProgressBar, track_iter_progress, - track_parallel_progress, track_progress) -from .testing import (assert_attrs_equal, assert_dict_contains_subset, - assert_dict_has_keys, assert_is_norm_layer, - assert_keys_equal, assert_params_all_zeros, - check_python_script) -from .timer import Timer, TimerError, check_time -from .version_utils import digit_version, get_git_hash - -try: - import torch -except ImportError: - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', - 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', - 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', - 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', - 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', - 'track_progress', 'track_iter_progress', 'track_parallel_progress', - 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', - 'digit_version', 'get_git_hash', 'import_modules_from_strings', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', - 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', - 'is_method_overridden', 'has_method' - ] -else: - from .device_type import IS_IPU_AVAILABLE, IS_MLU_AVAILABLE - from .env import collect_env - from .hub import load_url - from .logging import get_logger, print_log - from .parrots_jit import jit, skip_no_elena - # yapf: disable - from .parrots_wrapper import (IS_CUDA_AVAILABLE, TORCH_VERSION, - BuildExtension, CppExtension, CUDAExtension, - DataLoader, PoolDataLoader, SyncBatchNorm, - _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, - _AvgPoolNd, _BatchNorm, _ConvNd, - _ConvTransposeMixin, _get_cuda_home, - _InstanceNorm, _MaxPoolNd, get_build_config, - is_rocm_pytorch) - # yapf: enable - from .registry import Registry, build_from_cfg - from .seed import worker_init_fn - from .trace import is_jit_tracing - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', - 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', - 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', - 'check_prerequisites', 'requires_package', 'requires_executable', - 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', - 'symlink', 'scandir', 'ProgressBar', 'track_progress', - 'track_iter_progress', 'track_parallel_progress', 'Registry', - 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', - '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', - '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', - 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', - 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', - 'deprecated_api_warning', 'digit_version', 'get_git_hash', - 'import_modules_from_strings', 'jit', 'skip_no_elena', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', - 'assert_params_all_zeros', 'check_python_script', - 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', - '_get_cuda_home', 'load_url', 'has_method', 'IS_CUDA_AVAILABLE', - 'worker_init_fn', 'IS_MLU_AVAILABLE', 'IS_IPU_AVAILABLE' - ] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/config.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/config.py deleted file mode 100644 index 8efbc24e2..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/config.py +++ /dev/null @@ -1,719 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import ast -import copy -import os -import os.path as osp -import platform -import shutil -import sys -import tempfile -import types -import uuid -import warnings -from argparse import Action, ArgumentParser -from collections import abc -from importlib import import_module -from pathlib import Path - -from addict import Dict -from yapf.yapflib.yapf_api import FormatCode - -from .misc import import_modules_from_strings -from .path import check_file_exist - -if platform.system() == 'Windows': - import regex as re -else: - import re - -BASE_KEY = '_base_' -DELETE_KEY = '_delete_' -DEPRECATION_KEY = '_deprecation_' -RESERVED_KEYS = ['filename', 'text', 'pretty_text'] - - -class ConfigDict(Dict): - - def __missing__(self, name): - raise KeyError(name) - - def __getattr__(self, name): - try: - value = super(ConfigDict, self).__getattr__(name) - except KeyError: - ex = AttributeError(f"'{self.__class__.__name__}' object has no " - f"attribute '{name}'") - except Exception as e: - ex = e - else: - return value - raise ex - - -def add_args(parser, cfg, prefix=''): - for k, v in cfg.items(): - if isinstance(v, str): - parser.add_argument('--' + prefix + k) - elif isinstance(v, int): - parser.add_argument('--' + prefix + k, type=int) - elif isinstance(v, float): - parser.add_argument('--' + prefix + k, type=float) - elif isinstance(v, bool): - parser.add_argument('--' + prefix + k, action='store_true') - elif isinstance(v, dict): - add_args(parser, v, prefix + k + '.') - elif isinstance(v, abc.Iterable): - parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') - else: - print(f'cannot parse key {prefix + k} of type {type(v)}') - return parser - - -class Config: - """A facility for config and config files. - - It supports common file formats as configs: python/json/yaml. The interface - is the same as a dict object and also allows access config values as - attributes. - - Example: - >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - >>> cfg.a - 1 - >>> cfg.b - {'b1': [0, 1]} - >>> cfg.b.b1 - [0, 1] - >>> cfg = Config.fromfile('tests/data/config/a.py') - >>> cfg.filename - "/home/kchen/projects/mmcv/tests/data/config/a.py" - >>> cfg.item4 - 'test' - >>> cfg - "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " - "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" - """ - - @staticmethod - def _validate_py_syntax(filename): - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - content = f.read() - try: - ast.parse(content) - except SyntaxError as e: - raise SyntaxError('There are syntax errors in config ' - f'file {filename}: {e}') - - @staticmethod - def _substitute_predefined_vars(filename, temp_config_name): - file_dirname = osp.dirname(filename) - file_basename = osp.basename(filename) - file_basename_no_extension = osp.splitext(file_basename)[0] - file_extname = osp.splitext(filename)[1] - support_templates = dict( - fileDirname=file_dirname, - fileBasename=file_basename, - fileBasenameNoExtension=file_basename_no_extension, - fileExtname=file_extname) - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - for key, value in support_templates.items(): - regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' - value = value.replace('\\', '/') - config_file = re.sub(regexp, value, config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - - @staticmethod - def _pre_substitute_base_vars(filename, temp_config_name): - """Substitute base variable placehoders to string, so that parsing - would work.""" - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - base_var_dict = {} - regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' - base_vars = set(re.findall(regexp, config_file)) - for base_var in base_vars: - randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' - base_var_dict[randstr] = base_var - regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' - config_file = re.sub(regexp, f'"{randstr}"', config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - return base_var_dict - - @staticmethod - def _substitute_base_vars(cfg, base_var_dict, base_cfg): - """Substitute variable strings to their actual values.""" - cfg = copy.deepcopy(cfg) - - if isinstance(cfg, dict): - for k, v in cfg.items(): - if isinstance(v, str) and v in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[v].split('.'): - new_v = new_v[new_k] - cfg[k] = new_v - elif isinstance(v, (list, tuple, dict)): - cfg[k] = Config._substitute_base_vars( - v, base_var_dict, base_cfg) - elif isinstance(cfg, tuple): - cfg = tuple( - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg) - elif isinstance(cfg, list): - cfg = [ - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg - ] - elif isinstance(cfg, str) and cfg in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[cfg].split('.'): - new_v = new_v[new_k] - cfg = new_v - - return cfg - - @staticmethod - def _file2dict(filename, use_predefined_variables=True): - filename = osp.abspath(osp.expanduser(filename)) - check_file_exist(filename) - fileExtname = osp.splitext(filename)[1] - if fileExtname not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - - with tempfile.TemporaryDirectory() as temp_config_dir: - temp_config_file = tempfile.NamedTemporaryFile( - dir=temp_config_dir, suffix=fileExtname) - if platform.system() == 'Windows': - temp_config_file.close() - temp_config_name = osp.basename(temp_config_file.name) - # Substitute predefined variables - if use_predefined_variables: - Config._substitute_predefined_vars(filename, - temp_config_file.name) - else: - shutil.copyfile(filename, temp_config_file.name) - # Substitute base variables from placeholders to strings - base_var_dict = Config._pre_substitute_base_vars( - temp_config_file.name, temp_config_file.name) - - if filename.endswith('.py'): - temp_module_name = osp.splitext(temp_config_name)[0] - sys.path.insert(0, temp_config_dir) - Config._validate_py_syntax(filename) - mod = import_module(temp_module_name) - sys.path.pop(0) - cfg_dict = { - name: value - for name, value in mod.__dict__.items() - if not name.startswith('__') - and not isinstance(value, types.ModuleType) - and not isinstance(value, types.FunctionType) - } - # delete imported module - del sys.modules[temp_module_name] - elif filename.endswith(('.yml', '.yaml', '.json')): - import mmcv - cfg_dict = mmcv.load(temp_config_file.name) - # close temp file - temp_config_file.close() - - # check deprecation information - if DEPRECATION_KEY in cfg_dict: - deprecation_info = cfg_dict.pop(DEPRECATION_KEY) - warning_msg = f'The config file {filename} will be deprecated ' \ - 'in the future.' - if 'expected' in deprecation_info: - warning_msg += f' Please use {deprecation_info["expected"]} ' \ - 'instead.' - if 'reference' in deprecation_info: - warning_msg += ' More information can be found at ' \ - f'{deprecation_info["reference"]}' - warnings.warn(warning_msg, DeprecationWarning) - - cfg_text = filename + '\n' - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - cfg_text += f.read() - - if BASE_KEY in cfg_dict: - cfg_dir = osp.dirname(filename) - base_filename = cfg_dict.pop(BASE_KEY) - base_filename = base_filename if isinstance( - base_filename, list) else [base_filename] - - cfg_dict_list = list() - cfg_text_list = list() - for f in base_filename: - _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) - cfg_dict_list.append(_cfg_dict) - cfg_text_list.append(_cfg_text) - - base_cfg_dict = dict() - for c in cfg_dict_list: - duplicate_keys = base_cfg_dict.keys() & c.keys() - if len(duplicate_keys) > 0: - raise KeyError('Duplicate key is not allowed among bases. ' - f'Duplicate keys: {duplicate_keys}') - base_cfg_dict.update(c) - - # Substitute base variables from strings to their actual values - cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, - base_cfg_dict) - - base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) - cfg_dict = base_cfg_dict - - # merge cfg_text - cfg_text_list.append(cfg_text) - cfg_text = '\n'.join(cfg_text_list) - - return cfg_dict, cfg_text - - @staticmethod - def _merge_a_into_b(a, b, allow_list_keys=False): - """merge dict ``a`` into dict ``b`` (non-inplace). - - Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid - in-place modifications. - - Args: - a (dict): The source dict to be merged into ``b``. - b (dict): The origin dict to be fetch keys from ``a``. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in source ``a`` and will replace the element of the - corresponding index in b if b is a list. Default: False. - - Returns: - dict: The modified dict of ``b`` using ``a``. - - Examples: - # Normally merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # Delete b first and merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # b is a list - >>> Config._merge_a_into_b( - ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) - [{'a': 2}, {'b': 2}] - """ - b = b.copy() - for k, v in a.items(): - if allow_list_keys and k.isdigit() and isinstance(b, list): - k = int(k) - if len(b) <= k: - raise KeyError(f'Index {k} exceeds the length of list {b}') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - elif isinstance(v, dict): - if k in b and not v.pop(DELETE_KEY, False): - allowed_types = (dict, list) if allow_list_keys else dict - if not isinstance(b[k], allowed_types): - raise TypeError( - f'{k}={v} in child config cannot inherit from ' - f'base because {k} is a dict in the child config ' - f'but is of type {type(b[k])} in base config. ' - f'You may set `{DELETE_KEY}=True` to ignore the ' - f'base config.') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - else: - b[k] = ConfigDict(v) - else: - b[k] = v - return b - - @staticmethod - def fromfile(filename, - use_predefined_variables=True, - import_custom_modules=True): - if isinstance(filename, Path): - filename = str(filename) - cfg_dict, cfg_text = Config._file2dict(filename, - use_predefined_variables) - if import_custom_modules and cfg_dict.get('custom_imports', None): - import_modules_from_strings(**cfg_dict['custom_imports']) - return Config(cfg_dict, cfg_text=cfg_text, filename=filename) - - @staticmethod - def fromstring(cfg_str, file_format): - """Generate config from config str. - - Args: - cfg_str (str): Config str. - file_format (str): Config file format corresponding to the - config str. Only py/yml/yaml/json type are supported now! - - Returns: - :obj:`Config`: Config obj. - """ - if file_format not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - if file_format != '.py' and 'dict(' in cfg_str: - # check if users specify a wrong suffix for python - warnings.warn( - 'Please check "file_format", the file format may be .py') - with tempfile.NamedTemporaryFile( - 'w', encoding='utf-8', suffix=file_format, - delete=False) as temp_file: - temp_file.write(cfg_str) - # on windows, previous implementation cause error - # see PR 1077 for details - cfg = Config.fromfile(temp_file.name) - os.remove(temp_file.name) - return cfg - - @staticmethod - def auto_argparser(description=None): - """Generate argparser from config file automatically (experimental)""" - partial_parser = ArgumentParser(description=description) - partial_parser.add_argument('config', help='config file path') - cfg_file = partial_parser.parse_known_args()[0].config - cfg = Config.fromfile(cfg_file) - parser = ArgumentParser(description=description) - parser.add_argument('config', help='config file path') - add_args(parser, cfg) - return parser, cfg - - def __init__(self, cfg_dict=None, cfg_text=None, filename=None): - if cfg_dict is None: - cfg_dict = dict() - elif not isinstance(cfg_dict, dict): - raise TypeError('cfg_dict must be a dict, but ' - f'got {type(cfg_dict)}') - for key in cfg_dict: - if key in RESERVED_KEYS: - raise KeyError(f'{key} is reserved for config file') - - if isinstance(filename, Path): - filename = str(filename) - - super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) - super(Config, self).__setattr__('_filename', filename) - if cfg_text: - text = cfg_text - elif filename: - with open(filename, 'r') as f: - text = f.read() - else: - text = '' - super(Config, self).__setattr__('_text', text) - - @property - def filename(self): - return self._filename - - @property - def text(self): - return self._text - - @property - def pretty_text(self): - - indent = 4 - - def _indent(s_, num_spaces): - s = s_.split('\n') - if len(s) == 1: - return s_ - first = s.pop(0) - s = [(num_spaces * ' ') + line for line in s] - s = '\n'.join(s) - s = first + '\n' + s - return s - - def _format_basic_types(k, v, use_mapping=False): - if isinstance(v, str): - v_str = f"'{v}'" - else: - v_str = str(v) - - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) - - return attr_str - - def _format_list(k, v, use_mapping=False): - # check if all items in the list are dict - if all(isinstance(_, dict) for _ in v): - v_str = '[\n' - v_str += '\n'.join( - f'dict({_indent(_format_dict(v_), indent)}),' - for v_ in v).rstrip(',') - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) + ']' - else: - attr_str = _format_basic_types(k, v, use_mapping) - return attr_str - - def _contain_invalid_identifier(dict_str): - contain_invalid_identifier = False - for key_name in dict_str: - contain_invalid_identifier |= \ - (not str(key_name).isidentifier()) - return contain_invalid_identifier - - def _format_dict(input_dict, outest_level=False): - r = '' - s = [] - - use_mapping = _contain_invalid_identifier(input_dict) - if use_mapping: - r += '{' - for idx, (k, v) in enumerate(input_dict.items()): - is_last = idx >= len(input_dict) - 1 - end = '' if outest_level or is_last else ',' - if isinstance(v, dict): - v_str = '\n' + _format_dict(v) - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: dict({v_str}' - else: - attr_str = f'{str(k)}=dict({v_str}' - attr_str = _indent(attr_str, indent) + ')' + end - elif isinstance(v, list): - attr_str = _format_list(k, v, use_mapping) + end - else: - attr_str = _format_basic_types(k, v, use_mapping) + end - - s.append(attr_str) - r += '\n'.join(s) - if use_mapping: - r += '}' - return r - - cfg_dict = self._cfg_dict.to_dict() - text = _format_dict(cfg_dict, outest_level=True) - # copied from setup.cfg - yapf_style = dict( - based_on_style='pep8', - blank_line_before_nested_class_or_def=True, - split_before_expression_after_opening_paren=True) - text, _ = FormatCode(text, style_config=yapf_style, verify=True) - - return text - - def __repr__(self): - return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' - - def __len__(self): - return len(self._cfg_dict) - - def __getattr__(self, name): - return getattr(self._cfg_dict, name) - - def __getitem__(self, name): - return self._cfg_dict.__getitem__(name) - - def __setattr__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setattr__(name, value) - - def __setitem__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setitem__(name, value) - - def __iter__(self): - return iter(self._cfg_dict) - - def __getstate__(self): - return (self._cfg_dict, self._filename, self._text) - - def __copy__(self): - cls = self.__class__ - other = cls.__new__(cls) - other.__dict__.update(self.__dict__) - - return other - - def __deepcopy__(self, memo): - cls = self.__class__ - other = cls.__new__(cls) - memo[id(self)] = other - - for key, value in self.__dict__.items(): - super(Config, other).__setattr__(key, copy.deepcopy(value, memo)) - - return other - - def __setstate__(self, state): - _cfg_dict, _filename, _text = state - super(Config, self).__setattr__('_cfg_dict', _cfg_dict) - super(Config, self).__setattr__('_filename', _filename) - super(Config, self).__setattr__('_text', _text) - - def dump(self, file=None): - cfg_dict = super(Config, self).__getattribute__('_cfg_dict').to_dict() - if self.filename.endswith('.py'): - if file is None: - return self.pretty_text - else: - with open(file, 'w', encoding='utf-8') as f: - f.write(self.pretty_text) - else: - import mmcv - if file is None: - file_format = self.filename.split('.')[-1] - return mmcv.dump(cfg_dict, file_format=file_format) - else: - mmcv.dump(cfg_dict, file) - - def merge_from_dict(self, options, allow_list_keys=True): - """Merge list into cfg_dict. - - Merge the dict parsed by MultipleKVAction into this cfg. - - Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} - >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) - >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - - >>> # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) - >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) - >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - - Args: - options (dict): dict of configs to merge from. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in ``options`` and will replace the element of the - corresponding index in the config if the config is a list. - Default: True. - """ - option_cfg_dict = {} - for full_key, v in options.items(): - d = option_cfg_dict - key_list = full_key.split('.') - for subkey in key_list[:-1]: - d.setdefault(subkey, ConfigDict()) - d = d[subkey] - subkey = key_list[-1] - d[subkey] = v - - cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - super(Config, self).__setattr__( - '_cfg_dict', - Config._merge_a_into_b( - option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) - - -class DictAction(Action): - """ - argparse action to split an argument into KEY=VALUE form - on the first = and append to a dictionary. List options can - be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit - brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build - list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' - """ - - @staticmethod - def _parse_int_float_bool(val): - try: - return int(val) - except ValueError: - pass - try: - return float(val) - except ValueError: - pass - if val.lower() in ['true', 'false']: - return True if val.lower() == 'true' else False - if val == 'None': - return None - return val - - @staticmethod - def _parse_iterable(val): - """Parse iterable values in the string. - - All elements inside '()' or '[]' are treated as iterable values. - - Args: - val (str): Value string. - - Returns: - list | tuple: The expanded list or tuple from the string. - - Examples: - >>> DictAction._parse_iterable('1,2,3') - [1, 2, 3] - >>> DictAction._parse_iterable('[a, b, c]') - ['a', 'b', 'c'] - >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') - [(1, 2, 3), ['a', 'b'], 'c'] - """ - - def find_next_comma(string): - """Find the position of next comma in the string. - - If no ',' is found in the string, return the string length. All - chars inside '()' and '[]' are treated as one element and thus ',' - inside these brackets are ignored. - """ - assert (string.count('(') == string.count(')')) and ( - string.count('[') == string.count(']')), \ - f'Imbalanced brackets exist in {string}' - end = len(string) - for idx, char in enumerate(string): - pre = string[:idx] - # The string before this ',' is balanced - if ((char == ',') and (pre.count('(') == pre.count(')')) - and (pre.count('[') == pre.count(']'))): - end = idx - break - return end - - # Strip ' and " characters and replace whitespace. - val = val.strip('\'\"').replace(' ', '') - is_tuple = False - if val.startswith('(') and val.endswith(')'): - is_tuple = True - val = val[1:-1] - elif val.startswith('[') and val.endswith(']'): - val = val[1:-1] - elif ',' not in val: - # val is a single value - return DictAction._parse_int_float_bool(val) - - values = [] - while len(val) > 0: - comma_idx = find_next_comma(val) - element = DictAction._parse_iterable(val[:comma_idx]) - values.append(element) - val = val[comma_idx + 1:] - if is_tuple: - values = tuple(values) - return values - - def __call__(self, parser, namespace, values, option_string=None): - options = {} - for kv in values: - key, val = kv.split('=', maxsplit=1) - options[key] = self._parse_iterable(val) - setattr(namespace, self.dest, options) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/device_type.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/device_type.py deleted file mode 100644 index c29d944ab..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/device_type.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - - -def is_ipu_available(): - try: - import poptorch - return poptorch.ipuHardwareIsAvailable() - except ImportError: - return False - - -IS_IPU_AVAILABLE = is_ipu_available() - - -def is_mlu_available(): - try: - import torch - return (hasattr(torch, 'is_mlu_available') - and torch.is_mlu_available()) - except Exception: - return False - - -IS_MLU_AVAILABLE = is_mlu_available() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/env.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/env.py deleted file mode 100644 index 511332506..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/env.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This file holding some environment constant for sharing by other files.""" - -import os.path as osp -import subprocess -import sys -from collections import defaultdict - -import cv2 -import torch - -import mmcv -from .parrots_wrapper import get_build_config - - -def collect_env(): - """Collect the information of the running environments. - - Returns: - dict: The environment information. The following fields are contained. - - - sys.platform: The variable of ``sys.platform``. - - Python: Python version. - - CUDA available: Bool, indicating if CUDA is available. - - GPU devices: Device type of each GPU. - - CUDA_HOME (optional): The env var ``CUDA_HOME``. - - NVCC (optional): NVCC version. - - GCC: GCC version, "n/a" if GCC is not installed. - - MSVC: Microsoft Virtual C++ Compiler version, Windows only. - - PyTorch: PyTorch version. - - PyTorch compiling details: The output of \ - ``torch.__config__.show()``. - - TorchVision (optional): TorchVision version. - - OpenCV: OpenCV version. - - MMCV: MMCV version. - - MMCV Compiler: The GCC version for compiling MMCV ops. - - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. - """ - env_info = {} - env_info['sys.platform'] = sys.platform - env_info['Python'] = sys.version.replace('\n', '') - - cuda_available = torch.cuda.is_available() - env_info['CUDA available'] = cuda_available - - if cuda_available: - devices = defaultdict(list) - for k in range(torch.cuda.device_count()): - devices[torch.cuda.get_device_name(k)].append(str(k)) - for name, device_ids in devices.items(): - env_info['GPU ' + ','.join(device_ids)] = name - - from mmcv.utils.parrots_wrapper import _get_cuda_home - CUDA_HOME = _get_cuda_home() - env_info['CUDA_HOME'] = CUDA_HOME - - if CUDA_HOME is not None and osp.isdir(CUDA_HOME): - try: - nvcc = osp.join(CUDA_HOME, 'bin/nvcc') - nvcc = subprocess.check_output(f'"{nvcc}" -V', shell=True) - nvcc = nvcc.decode('utf-8').strip() - release = nvcc.rfind('Cuda compilation tools') - build = nvcc.rfind('Build ') - nvcc = nvcc[release:build].strip() - except subprocess.SubprocessError: - nvcc = 'Not Available' - env_info['NVCC'] = nvcc - - try: - # Check C++ Compiler. - # For Unix-like, sysconfig has 'CC' variable like 'gcc -pthread ...', - # indicating the compiler used, we use this to get the compiler name - import sysconfig - cc = sysconfig.get_config_var('CC') - if cc: - cc = osp.basename(cc.split()[0]) - cc_info = subprocess.check_output(f'{cc} --version', shell=True) - env_info['GCC'] = cc_info.decode('utf-8').partition( - '\n')[0].strip() - else: - # on Windows, cl.exe is not in PATH. We need to find the path. - # distutils.ccompiler.new_compiler() returns a msvccompiler - # object and after initialization, path to cl.exe is found. - import locale - import os - from distutils.ccompiler import new_compiler - ccompiler = new_compiler() - ccompiler.initialize() - cc = subprocess.check_output( - f'{ccompiler.cc}', stderr=subprocess.STDOUT, shell=True) - encoding = os.device_encoding( - sys.stdout.fileno()) or locale.getpreferredencoding() - env_info['MSVC'] = cc.decode(encoding).partition('\n')[0].strip() - env_info['GCC'] = 'n/a' - except subprocess.CalledProcessError: - env_info['GCC'] = 'n/a' - - env_info['PyTorch'] = torch.__version__ - env_info['PyTorch compiling details'] = get_build_config() - - try: - import torchvision - env_info['TorchVision'] = torchvision.__version__ - except ModuleNotFoundError: - pass - - env_info['OpenCV'] = cv2.__version__ - - env_info['MMCV'] = mmcv.__version__ - - try: - from mmcv.ops import get_compiler_version, get_compiling_cuda_version - except ModuleNotFoundError: - env_info['MMCV Compiler'] = 'n/a' - env_info['MMCV CUDA Compiler'] = 'n/a' - else: - env_info['MMCV Compiler'] = get_compiler_version() - env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() - - return env_info diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/ext_loader.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/ext_loader.py deleted file mode 100644 index f82c6d568..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/ext_loader.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os -import pkgutil -import warnings -from collections import namedtuple - -import torch - -if torch.__version__ != 'parrots': - - def load_ext(name, funcs): - ext = importlib.import_module('mmcv.' + name) - for fun in funcs: - assert hasattr(ext, fun), f'{fun} miss in module {name}' - return ext -else: - from parrots import extension - from parrots.base import ParrotsException - - has_return_value_ops = [ - 'nms', - 'softnms', - 'nms_match', - 'nms_rotated', - 'top_pool_forward', - 'top_pool_backward', - 'bottom_pool_forward', - 'bottom_pool_backward', - 'left_pool_forward', - 'left_pool_backward', - 'right_pool_forward', - 'right_pool_backward', - 'fused_bias_leakyrelu', - 'upfirdn2d', - 'ms_deform_attn_forward', - 'pixel_group', - 'contour_expand', - 'diff_iou_rotated_sort_vertices_forward', - ] - - def get_fake_func(name, e): - - def fake_func(*args, **kwargs): - warnings.warn(f'{name} is not supported in parrots now') - raise e - - return fake_func - - def load_ext(name, funcs): - ExtModule = namedtuple('ExtModule', funcs) - ext_list = [] - lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - for fun in funcs: - try: - ext_fun = extension.load(fun, name, lib_dir=lib_root) - except ParrotsException as e: - if 'No element registered' not in e.message: - warnings.warn(e.message) - ext_fun = get_fake_func(fun, e) - ext_list.append(ext_fun) - else: - if fun in has_return_value_ops: - ext_list.append(ext_fun.op) - else: - ext_list.append(ext_fun.op_) - return ExtModule(*ext_list) - - -def check_ops_exist(): - ext_loader = pkgutil.find_loader('mmcv._ext') - return ext_loader is not None diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/hub.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/hub.py deleted file mode 100644 index 12fbff2ee..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/hub.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# The 1.6 release of PyTorch switched torch.save to use a new zipfile-based -# file format. It will cause RuntimeError when a checkpoint was saved in -# torch >= 1.6.0 but loaded in torch < 1.7.0. -# More details at https://github.com/open-mmlab/mmpose/issues/904 -from .parrots_wrapper import TORCH_VERSION -from .path import mkdir_or_exist -from .version_utils import digit_version - -if TORCH_VERSION != 'parrots' and digit_version(TORCH_VERSION) < digit_version( - '1.7.0'): - # Modified from https://github.com/pytorch/pytorch/blob/master/torch/hub.py - import os - import sys - import warnings - import zipfile - from urllib.parse import urlparse - - import torch - from torch.hub import HASH_REGEX, _get_torch_home, download_url_to_file - - # Hub used to support automatically extracts from zipfile manually - # compressed by users. The legacy zip format expects only one file from - # torch.save() < 1.6 in the zip. We should remove this support since - # zipfile is now default zipfile format for torch.save(). - def _is_legacy_zip_format(filename): - if zipfile.is_zipfile(filename): - infolist = zipfile.ZipFile(filename).infolist() - return len(infolist) == 1 and not infolist[0].is_dir() - return False - - def _legacy_zip_load(filename, model_dir, map_location): - warnings.warn( - 'Falling back to the old format < 1.6. This support will' - ' be deprecated in favor of default zipfile format ' - 'introduced in 1.6. Please redo torch.save() to save it ' - 'in the new zipfile format.', DeprecationWarning) - # Note: extractall() defaults to overwrite file if exists. No need to - # clean up beforehand. We deliberately don't handle tarfile here - # since our legacy serialization format was in tar. - # E.g. resnet18-5c106cde.pth which is widely used. - with zipfile.ZipFile(filename) as f: - members = f.infolist() - if len(members) != 1: - raise RuntimeError( - 'Only one file(not dir) is allowed in the zipfile') - f.extractall(model_dir) - extraced_name = members[0].filename - extracted_file = os.path.join(model_dir, extraced_name) - return torch.load(extracted_file, map_location=map_location) - - def load_url(url, - model_dir=None, - map_location=None, - progress=True, - check_hash=False, - file_name=None): - r"""Loads the Torch serialized object at the given URL. - - If downloaded file is a zip file, it will be automatically decompressed - - If the object is already present in `model_dir`, it's deserialized and - returned. - The default value of ``model_dir`` is ``/checkpoints`` where - ``hub_dir`` is the directory returned by :func:`~torch.hub.get_dir`. - - Args: - url (str): URL of the object to download - model_dir (str, optional): directory in which to save the object - map_location (optional): a function or a dict specifying how to - remap storage locations (see torch.load) - progress (bool, optional): whether or not to display a progress bar - to stderr. Default: True - check_hash(bool, optional): If True, the filename part of the URL - should follow the naming convention ``filename-.ext`` - where ```` is the first eight or more digits of the - SHA256 hash of the contents of the file. The hash is used to - ensure unique names and to verify the contents of the file. - Default: False - file_name (str, optional): name for the downloaded file. Filename - from ``url`` will be used if not set. Default: None. - - Example: - >>> url = ('https://s3.amazonaws.com/pytorch/models/resnet18-5c106' - ... 'cde.pth') - >>> state_dict = torch.hub.load_state_dict_from_url(url) - """ - # Issue warning to move data if old env is set - if os.getenv('TORCH_MODEL_ZOO'): - warnings.warn( - 'TORCH_MODEL_ZOO is deprecated, please use env ' - 'TORCH_HOME instead', DeprecationWarning) - - if model_dir is None: - torch_home = _get_torch_home() - model_dir = os.path.join(torch_home, 'checkpoints') - - mkdir_or_exist(model_dir) - - parts = urlparse(url) - filename = os.path.basename(parts.path) - if file_name is not None: - filename = file_name - cached_file = os.path.join(model_dir, filename) - if not os.path.exists(cached_file): - sys.stderr.write('Downloading: "{}" to {}\n'.format( - url, cached_file)) - hash_prefix = None - if check_hash: - r = HASH_REGEX.search(filename) # r is Optional[Match[str]] - hash_prefix = r.group(1) if r else None - download_url_to_file( - url, cached_file, hash_prefix, progress=progress) - - if _is_legacy_zip_format(cached_file): - return _legacy_zip_load(cached_file, model_dir, map_location) - - try: - return torch.load(cached_file, map_location=map_location) - except RuntimeError as error: - if digit_version(TORCH_VERSION) < digit_version('1.5.0'): - warnings.warn( - f'If the error is the same as "{cached_file} is a zip ' - 'archive (did you mean to use torch.jit.load()?)", you can' - ' upgrade your torch to 1.5.0 or higher (current torch ' - f'version is {TORCH_VERSION}). The error was raised ' - ' because the checkpoint was saved in torch>=1.6.0 but ' - 'loaded in torch<1.5.') - raise error -else: - from torch.utils.model_zoo import load_url # noqa: F401 diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/logging.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/logging.py deleted file mode 100644 index c4c7025f0..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/logging.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.distributed as dist - -logger_initialized = {} - - -def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): - """Initialize and get a logger by name. - - If the logger has not been initialized, this method will initialize the - logger by adding one or two handlers, otherwise the initialized logger will - be directly returned. During initialization, a StreamHandler will always be - added. If `log_file` is specified and the process rank is 0, a FileHandler - will also be added. - - Args: - name (str): Logger name. - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the logger. - log_level (int): The logger level. Note that only the process of - rank 0 is affected, and other processes will set the level to - "Error" thus be silent most of the time. - file_mode (str): The file mode used in opening log file. - Defaults to 'w'. - - Returns: - logging.Logger: The expected logger. - """ - logger = logging.getLogger(name) - if name in logger_initialized: - return logger - # handle hierarchical names - # e.g., logger "a" is initialized, then logger "a.b" will skip the - # initialization since it is a child of "a". - for logger_name in logger_initialized: - if name.startswith(logger_name): - return logger - - # handle duplicate logs to the console - # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) - # to the root logger. As logger.propagate is True by default, this root - # level handler causes logging messages from rank>0 processes to - # unexpectedly show up on the console, creating much unwanted clutter. - # To fix this issue, we set the root logger's StreamHandler, if any, to log - # at the ERROR level. - for handler in logger.root.handlers: - if type(handler) is logging.StreamHandler: - handler.setLevel(logging.ERROR) - - stream_handler = logging.StreamHandler() - handlers = [stream_handler] - - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - else: - rank = 0 - - # only rank 0 will add a FileHandler - if rank == 0 and log_file is not None: - # Here, the default behaviour of the official logger is 'a'. Thus, we - # provide an interface to change the file mode to the default - # behaviour. - file_handler = logging.FileHandler(log_file, file_mode) - handlers.append(file_handler) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - for handler in handlers: - handler.setFormatter(formatter) - handler.setLevel(log_level) - logger.addHandler(handler) - - if rank == 0: - logger.setLevel(log_level) - else: - logger.setLevel(logging.ERROR) - - logger_initialized[name] = True - - return logger - - -def print_log(msg, logger=None, level=logging.INFO): - """Print a log message. - - Args: - msg (str): The message to be logged. - logger (logging.Logger | str | None): The logger to be used. - Some special loggers are: - - - "silent": no message will be printed. - - other str: the logger obtained with `get_root_logger(logger)`. - - None: The `print()` method will be used to print log messages. - level (int): Logging level. Only available when `logger` is a Logger - object or "root". - """ - if logger is None: - print(msg) - elif isinstance(logger, logging.Logger): - logger.log(level, msg) - elif logger == 'silent': - pass - elif isinstance(logger, str): - _logger = get_logger(logger) - _logger.log(level, msg) - else: - raise TypeError( - 'logger should be either a logging.Logger object, str, ' - f'"silent" or None, but got {type(logger)}') diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/misc.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/misc.py deleted file mode 100644 index 7957ea89b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/misc.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections.abc -import functools -import itertools -import subprocess -import warnings -from collections import abc -from importlib import import_module -from inspect import getfullargspec -from itertools import repeat - - -# From PyTorch internals -def _ntuple(n): - - def parse(x): - if isinstance(x, collections.abc.Iterable): - return x - return tuple(repeat(x, n)) - - return parse - - -to_1tuple = _ntuple(1) -to_2tuple = _ntuple(2) -to_3tuple = _ntuple(3) -to_4tuple = _ntuple(4) -to_ntuple = _ntuple - - -def is_str(x): - """Whether the input is an string instance. - - Note: This method is deprecated since python 2 is no longer supported. - """ - return isinstance(x, str) - - -def import_modules_from_strings(imports, allow_failed_imports=False): - """Import modules from the given list of strings. - - Args: - imports (list | str | None): The given module names to be imported. - allow_failed_imports (bool): If True, the failed imports will return - None. Otherwise, an ImportError is raise. Default: False. - - Returns: - list[module] | module | None: The imported modules. - - Examples: - >>> osp, sys = import_modules_from_strings( - ... ['os.path', 'sys']) - >>> import os.path as osp_ - >>> import sys as sys_ - >>> assert osp == osp_ - >>> assert sys == sys_ - """ - if not imports: - return - single_import = False - if isinstance(imports, str): - single_import = True - imports = [imports] - if not isinstance(imports, list): - raise TypeError( - f'custom_imports must be a list but got type {type(imports)}') - imported = [] - for imp in imports: - if not isinstance(imp, str): - raise TypeError( - f'{imp} is of type {type(imp)} and cannot be imported.') - try: - imported_tmp = import_module(imp) - except ImportError: - if allow_failed_imports: - warnings.warn(f'{imp} failed to import and is ignored.', - UserWarning) - imported_tmp = None - else: - raise ImportError - imported.append(imported_tmp) - if single_import: - imported = imported[0] - return imported - - -def iter_cast(inputs, dst_type, return_type=None): - """Cast elements of an iterable object into some type. - - Args: - inputs (Iterable): The input object. - dst_type (type): Destination type. - return_type (type, optional): If specified, the output object will be - converted to this type, otherwise an iterator. - - Returns: - iterator or specified type: The converted object. - """ - if not isinstance(inputs, abc.Iterable): - raise TypeError('inputs must be an iterable object') - if not isinstance(dst_type, type): - raise TypeError('"dst_type" must be a valid type') - - out_iterable = map(dst_type, inputs) - - if return_type is None: - return out_iterable - else: - return return_type(out_iterable) - - -def list_cast(inputs, dst_type): - """Cast elements of an iterable object into a list of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=list) - - -def tuple_cast(inputs, dst_type): - """Cast elements of an iterable object into a tuple of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=tuple) - - -def is_seq_of(seq, expected_type, seq_type=None): - """Check whether it is a sequence of some type. - - Args: - seq (Sequence): The sequence to be checked. - expected_type (type): Expected type of sequence items. - seq_type (type, optional): Expected sequence type. - - Returns: - bool: Whether the sequence is valid. - """ - if seq_type is None: - exp_seq_type = abc.Sequence - else: - assert isinstance(seq_type, type) - exp_seq_type = seq_type - if not isinstance(seq, exp_seq_type): - return False - for item in seq: - if not isinstance(item, expected_type): - return False - return True - - -def is_list_of(seq, expected_type): - """Check whether it is a list of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=list) - - -def is_tuple_of(seq, expected_type): - """Check whether it is a tuple of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=tuple) - - -def slice_list(in_list, lens): - """Slice a list into several sub lists by a list of given length. - - Args: - in_list (list): The list to be sliced. - lens(int or list): The expected length of each out list. - - Returns: - list: A list of sliced list. - """ - if isinstance(lens, int): - assert len(in_list) % lens == 0 - lens = [lens] * int(len(in_list) / lens) - if not isinstance(lens, list): - raise TypeError('"indices" must be an integer or a list of integers') - elif sum(lens) != len(in_list): - raise ValueError('sum of lens and list length does not ' - f'match: {sum(lens)} != {len(in_list)}') - out_list = [] - idx = 0 - for i in range(len(lens)): - out_list.append(in_list[idx:idx + lens[i]]) - idx += lens[i] - return out_list - - -def concat_list(in_list): - """Concatenate a list of list into a single list. - - Args: - in_list (list): The list of list to be merged. - - Returns: - list: The concatenated flat list. - """ - return list(itertools.chain(*in_list)) - - -def check_prerequisites( - prerequisites, - checker, - msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' - 'found, please install them first.'): # yapf: disable - """A decorator factory to check if prerequisites are satisfied. - - Args: - prerequisites (str of list[str]): Prerequisites to be checked. - checker (callable): The checker method that returns True if a - prerequisite is meet, False otherwise. - msg_tmpl (str): The message template with two variables. - - Returns: - decorator: A specific decorator. - """ - - def wrap(func): - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - requirements = [prerequisites] if isinstance( - prerequisites, str) else prerequisites - missing = [] - for item in requirements: - if not checker(item): - missing.append(item) - if missing: - print(msg_tmpl.format(', '.join(missing), func.__name__)) - raise RuntimeError('Prerequisites not meet.') - else: - return func(*args, **kwargs) - - return wrapped_func - - return wrap - - -def _check_py_package(package): - try: - import_module(package) - except ImportError: - return False - else: - return True - - -def _check_executable(cmd): - if subprocess.call(f'which {cmd}', shell=True) != 0: - return False - else: - return True - - -def requires_package(prerequisites): - """A decorator to check if some python packages are installed. - - Example: - >>> @requires_package('numpy') - >>> func(arg1, args): - >>> return numpy.zeros(1) - array([0.]) - >>> @requires_package(['numpy', 'non_package']) - >>> func(arg1, args): - >>> return numpy.zeros(1) - ImportError - """ - return check_prerequisites(prerequisites, checker=_check_py_package) - - -def requires_executable(prerequisites): - """A decorator to check if some executable files are installed. - - Example: - >>> @requires_executable('ffmpeg') - >>> func(arg1, args): - >>> print(1) - 1 - """ - return check_prerequisites(prerequisites, checker=_check_executable) - - -def deprecated_api_warning(name_dict, cls_name=None): - """A decorator to check if some arguments are deprecate and try to replace - deprecate src_arg_name to dst_arg_name. - - Args: - name_dict(dict): - key (str): Deprecate argument names. - val (str): Expected argument names. - - Returns: - func: New function. - """ - - def api_warning_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get name of the function - func_name = old_func.__name__ - if cls_name is not None: - func_name = f'{cls_name}.{func_name}' - if args: - arg_names = args_info.args[:len(args)] - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in arg_names: - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - arg_names[arg_names.index(src_arg_name)] = dst_arg_name - if kwargs: - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in kwargs: - - assert dst_arg_name not in kwargs, ( - f'The expected behavior is to replace ' - f'the deprecated key `{src_arg_name}` to ' - f'new key `{dst_arg_name}`, but got them ' - f'in the arguments at the same time, which ' - f'is confusing. `{src_arg_name} will be ' - f'deprecated in the future, please ' - f'use `{dst_arg_name}` instead.') - - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - kwargs[dst_arg_name] = kwargs.pop(src_arg_name) - - # apply converted arguments to the decorated method - output = old_func(*args, **kwargs) - return output - - return new_func - - return api_warning_wrapper - - -def is_method_overridden(method, base_class, derived_class): - """Check if a method of base class is overridden in derived class. - - Args: - method (str): the method name to check. - base_class (type): the class of the base class. - derived_class (type | Any): the class or instance of the derived class. - """ - assert isinstance(base_class, type), \ - "base_class doesn't accept instance, Please pass class instead." - - if not isinstance(derived_class, type): - derived_class = derived_class.__class__ - - base_method = getattr(base_class, method) - derived_method = getattr(derived_class, method) - return derived_method != base_method - - -def has_method(obj: object, method: str) -> bool: - """Check whether the object has a method. - - Args: - method (str): The method name to check. - obj (object): The object to check. - - Returns: - bool: True if the object has the method else False. - """ - return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/parrots_jit.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/parrots_jit.py deleted file mode 100644 index 61873f6db..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/parrots_jit.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -from .parrots_wrapper import TORCH_VERSION - -parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') - -if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': - from parrots.jit import pat as jit -else: - - def jit(func=None, - check_input=None, - full_shape=True, - derivate=False, - coderize=False, - optimize=False): - - def wrapper(func): - - def wrapper_inner(*args, **kargs): - return func(*args, **kargs) - - return wrapper_inner - - if func is None: - return wrapper - else: - return func - - -if TORCH_VERSION == 'parrots': - from parrots.utils.tester import skip_no_elena -else: - - def skip_no_elena(func): - - def wrapper(*args, **kargs): - return func(*args, **kargs) - - return wrapper diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/parrots_wrapper.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/parrots_wrapper.py deleted file mode 100644 index 7e657b561..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/parrots_wrapper.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch - -TORCH_VERSION = torch.__version__ - - -def is_cuda_available() -> bool: - return torch.cuda.is_available() - - -IS_CUDA_AVAILABLE = is_cuda_available() - - -def is_rocm_pytorch() -> bool: - is_rocm = False - if TORCH_VERSION != 'parrots': - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - return is_rocm - - -def _get_cuda_home(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import CUDA_HOME - else: - if is_rocm_pytorch(): - from torch.utils.cpp_extension import ROCM_HOME - CUDA_HOME = ROCM_HOME - else: - from torch.utils.cpp_extension import CUDA_HOME - return CUDA_HOME - - -def get_build_config(): - if TORCH_VERSION == 'parrots': - from parrots.config import get_build_info - return get_build_info() - else: - return torch.__config__.show() - - -def _get_conv(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin - else: - from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin - return _ConvNd, _ConvTransposeMixin - - -def _get_dataloader(): - if TORCH_VERSION == 'parrots': - from torch.utils.data import DataLoader, PoolDataLoader - else: - from torch.utils.data import DataLoader - PoolDataLoader = DataLoader - return DataLoader, PoolDataLoader - - -def _get_extension(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import BuildExtension, Extension - CppExtension = partial(Extension, cuda=False) - CUDAExtension = partial(Extension, cuda=True) - else: - from torch.utils.cpp_extension import (BuildExtension, CppExtension, - CUDAExtension) - return BuildExtension, CppExtension, CUDAExtension - - -def _get_pool(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - else: - from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd - - -def _get_norm(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm2d - else: - from torch.nn.modules.batchnorm import _BatchNorm - from torch.nn.modules.instancenorm import _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm - return _BatchNorm, _InstanceNorm, SyncBatchNorm_ - - -_ConvNd, _ConvTransposeMixin = _get_conv() -DataLoader, PoolDataLoader = _get_dataloader() -BuildExtension, CppExtension, CUDAExtension = _get_extension() -_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() -_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() - - -class SyncBatchNorm(SyncBatchNorm_): - - def _check_input_dim(self, input): - if TORCH_VERSION == 'parrots': - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input (got {input.dim()}D input)') - else: - super()._check_input_dim(input) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/path.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/path.py deleted file mode 100644 index 568081837..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/path.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -from pathlib import Path - -from .misc import is_str - - -def is_filepath(x): - return is_str(x) or isinstance(x, Path) - - -def fopen(filepath, *args, **kwargs): - if is_str(filepath): - return open(filepath, *args, **kwargs) - elif isinstance(filepath, Path): - return filepath.open(*args, **kwargs) - raise ValueError('`filepath` should be a string or a Path') - - -def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): - if not osp.isfile(filename): - raise FileNotFoundError(msg_tmpl.format(filename)) - - -def mkdir_or_exist(dir_name, mode=0o777): - if dir_name == '': - return - dir_name = osp.expanduser(dir_name) - os.makedirs(dir_name, mode=mode, exist_ok=True) - - -def symlink(src, dst, overwrite=True, **kwargs): - if os.path.lexists(dst) and overwrite: - os.remove(dst) - os.symlink(src, dst, **kwargs) - - -def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): - """Scan a directory to find the interested files. - - Args: - dir_path (str | :obj:`Path`): Path of the directory. - suffix (str | tuple(str), optional): File suffix that we are - interested in. Default: None. - recursive (bool, optional): If set to True, recursively scan the - directory. Default: False. - case_sensitive (bool, optional) : If set to False, ignore the case of - suffix. Default: True. - - Returns: - A generator for all the interested files with relative paths. - """ - if isinstance(dir_path, (str, Path)): - dir_path = str(dir_path) - else: - raise TypeError('"dir_path" must be a string or Path object') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('"suffix" must be a string or tuple of strings') - - if suffix is not None and not case_sensitive: - suffix = suffix.lower() if isinstance(suffix, str) else tuple( - item.lower() for item in suffix) - - root = dir_path - - def _scandir(dir_path, suffix, recursive, case_sensitive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - _rel_path = rel_path if case_sensitive else rel_path.lower() - if suffix is None or _rel_path.endswith(suffix): - yield rel_path - elif recursive and os.path.isdir(entry.path): - # scan recursively if entry.path is a directory - yield from _scandir(entry.path, suffix, recursive, - case_sensitive) - - return _scandir(dir_path, suffix, recursive, case_sensitive) - - -def find_vcs_root(path, markers=('.git', )): - """Finds the root directory (including itself) of specified markers. - - Args: - path (str): Path of directory or file. - markers (list[str], optional): List of file or directory names. - - Returns: - The directory contained one of the markers or None if not found. - """ - if osp.isfile(path): - path = osp.dirname(path) - - prev, cur = None, osp.abspath(osp.expanduser(path)) - while cur != prev: - if any(osp.exists(osp.join(cur, marker)) for marker in markers): - return cur - prev, cur = cur, osp.split(cur)[0] - return None diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/progressbar.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/progressbar.py deleted file mode 100644 index 0062f670d..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/progressbar.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from collections.abc import Iterable -from multiprocessing import Pool -from shutil import get_terminal_size - -from .timer import Timer - - -class ProgressBar: - """A progress bar which can print the progress.""" - - def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): - self.task_num = task_num - self.bar_width = bar_width - self.completed = 0 - self.file = file - if start: - self.start() - - @property - def terminal_width(self): - width, _ = get_terminal_size() - return width - - def start(self): - if self.task_num > 0: - self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' - 'elapsed: 0s, ETA:') - else: - self.file.write('completed: 0, elapsed: 0s') - self.file.flush() - self.timer = Timer() - - def update(self, num_tasks=1): - assert num_tasks > 0 - self.completed += num_tasks - elapsed = self.timer.since_start() - if elapsed > 0: - fps = self.completed / elapsed - else: - fps = float('inf') - if self.task_num > 0: - percentage = self.completed / float(self.task_num) - eta = int(elapsed * (1 - percentage) / percentage + 0.5) - msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ - f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ - f'ETA: {eta:5}s' - - bar_width = min(self.bar_width, - int(self.terminal_width - len(msg)) + 2, - int(self.terminal_width * 0.6)) - bar_width = max(2, bar_width) - mark_width = int(bar_width * percentage) - bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) - self.file.write(msg.format(bar_chars)) - else: - self.file.write( - f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' - f' {fps:.1f} tasks/s') - self.file.flush() - - -def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): - """Track the progress of tasks execution with a progress bar. - - Tasks are done with a simple for-loop. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - results = [] - for task in tasks: - results.append(func(task, **kwargs)) - prog_bar.update() - prog_bar.file.write('\n') - return results - - -def init_pool(process_num, initializer=None, initargs=None): - if initializer is None: - return Pool(process_num) - elif initargs is None: - return Pool(process_num, initializer) - else: - if not isinstance(initargs, tuple): - raise TypeError('"initargs" must be a tuple') - return Pool(process_num, initializer, initargs) - - -def track_parallel_progress(func, - tasks, - nproc, - initializer=None, - initargs=None, - bar_width=50, - chunksize=1, - skip_first=False, - keep_order=True, - file=sys.stdout): - """Track the progress of parallel task execution with a progress bar. - - The built-in :mod:`multiprocessing` module is used for process pools and - tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - nproc (int): Process (worker) number. - initializer (None or callable): Refer to :class:`multiprocessing.Pool` - for details. - initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for - details. - chunksize (int): Refer to :class:`multiprocessing.Pool` for details. - bar_width (int): Width of progress bar. - skip_first (bool): Whether to skip the first sample for each worker - when estimating fps, since the initialization step may takes - longer. - keep_order (bool): If True, :func:`Pool.imap` is used, otherwise - :func:`Pool.imap_unordered` is used. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - pool = init_pool(nproc, initializer, initargs) - start = not skip_first - task_num -= nproc * chunksize * int(skip_first) - prog_bar = ProgressBar(task_num, bar_width, start, file=file) - results = [] - if keep_order: - gen = pool.imap(func, tasks, chunksize) - else: - gen = pool.imap_unordered(func, tasks, chunksize) - for result in gen: - results.append(result) - if skip_first: - if len(results) < nproc * chunksize: - continue - elif len(results) == nproc * chunksize: - prog_bar.start() - continue - prog_bar.update() - prog_bar.file.write('\n') - pool.close() - pool.join() - return results - - -def track_iter_progress(tasks, bar_width=50, file=sys.stdout): - """Track the progress of tasks iteration or enumeration with a progress - bar. - - Tasks are yielded with a simple for-loop. - - Args: - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Yields: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - for task in tasks: - yield task - prog_bar.update() - prog_bar.file.write('\n') diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/registry.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/registry.py deleted file mode 100644 index a6d92b68b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/registry.py +++ /dev/null @@ -1,337 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import warnings -from functools import partial - -from .misc import deprecated_api_warning, is_seq_of - - -def build_from_cfg(cfg, registry, default_args=None): - """Build a module from config dict when it is a class configuration, or - call a function from config dict when it is a function configuration. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = build_from_cfg(dict(type='Resnet'), MODELS) - >>> # Returns an instantiated object - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = build_from_cfg(dict(type='resnet50'), MODELS) - >>> # Return a result of the calling function - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type) or inspect.isfunction(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - try: - return obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{obj_cls.__name__}: {e}') - - -class Registry: - """A registry to map strings to classes or functions. - - Registered object could be built from registry. Meanwhile, registered - functions could be called from registry. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = MODELS.build(dict(type='ResNet')) - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = MODELS.build(dict(type='resnet50')) - - Please refer to - https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for - advanced usage. - - Args: - name (str): Registry name. - build_func(func, optional): Build function to construct instance from - Registry, func:`build_from_cfg` is used if neither ``parent`` or - ``build_func`` is specified. If ``parent`` is specified and - ``build_func`` is not given, ``build_func`` will be inherited - from ``parent``. Default: None. - parent (Registry, optional): Parent registry. The class registered in - children registry could be built from parent. Default: None. - scope (str, optional): The scope of registry. It is the key to search - for children registry. If not specified, scope will be the name of - the package where class is defined, e.g. mmdet, mmcls, mmseg. - Default: None. - """ - - def __init__(self, name, build_func=None, parent=None, scope=None): - self._name = name - self._module_dict = dict() - self._children = dict() - self._scope = self.infer_scope() if scope is None else scope - - # self.build_func will be set with the following priority: - # 1. build_func - # 2. parent.build_func - # 3. build_from_cfg - if build_func is None: - if parent is not None: - self.build_func = parent.build_func - else: - self.build_func = build_from_cfg - else: - self.build_func = build_func - if parent is not None: - assert isinstance(parent, Registry) - parent._add_children(self) - self.parent = parent - else: - self.parent = None - - def __len__(self): - return len(self._module_dict) - - def __contains__(self, key): - return self.get(key) is not None - - def __repr__(self): - format_str = self.__class__.__name__ + \ - f'(name={self._name}, ' \ - f'items={self._module_dict})' - return format_str - - @staticmethod - def infer_scope(): - """Infer the scope of registry. - - The name of the package where registry is defined will be returned. - - Example: - >>> # in mmdet/models/backbone/resnet.py - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - The scope of ``ResNet`` will be ``mmdet``. - - Returns: - str: The inferred scope name. - """ - # We access the caller using inspect.currentframe() instead of - # inspect.stack() for performance reasons. See details in PR #1844 - frame = inspect.currentframe() - # get the frame where `infer_scope()` is called - infer_scope_caller = frame.f_back.f_back - filename = inspect.getmodule(infer_scope_caller).__name__ - split_filename = filename.split('.') - return split_filename[0] - - @staticmethod - def split_scope_key(key): - """Split scope and key. - - The first scope will be split from key. - - Examples: - >>> Registry.split_scope_key('mmdet.ResNet') - 'mmdet', 'ResNet' - >>> Registry.split_scope_key('ResNet') - None, 'ResNet' - - Return: - tuple[str | None, str]: The former element is the first scope of - the key, which can be ``None``. The latter is the remaining key. - """ - split_index = key.find('.') - if split_index != -1: - return key[:split_index], key[split_index + 1:] - else: - return None, key - - @property - def name(self): - return self._name - - @property - def scope(self): - return self._scope - - @property - def module_dict(self): - return self._module_dict - - @property - def children(self): - return self._children - - def get(self, key): - """Get the registry record. - - Args: - key (str): The class name in string format. - - Returns: - class: The corresponding class. - """ - scope, real_key = self.split_scope_key(key) - if scope is None or scope == self._scope: - # get from self - if real_key in self._module_dict: - return self._module_dict[real_key] - else: - # get from self._children - if scope in self._children: - return self._children[scope].get(real_key) - else: - # goto root - parent = self.parent - while parent.parent is not None: - parent = parent.parent - return parent.get(key) - - def build(self, *args, **kwargs): - return self.build_func(*args, **kwargs, registry=self) - - def _add_children(self, registry): - """Add children for a registry. - - The ``registry`` will be added as children based on its scope. - The parent registry could build objects from children registry. - - Example: - >>> models = Registry('models') - >>> mmdet_models = Registry('models', parent=models) - >>> @mmdet_models.register_module() - >>> class ResNet: - >>> pass - >>> resnet = models.build(dict(type='mmdet.ResNet')) - """ - - assert isinstance(registry, Registry) - assert registry.scope is not None - assert registry.scope not in self.children, \ - f'scope {registry.scope} exists in {self.name} registry' - self.children[registry.scope] = registry - - @deprecated_api_warning(name_dict=dict(module_class='module')) - def _register_module(self, module, module_name=None, force=False): - if not inspect.isclass(module) and not inspect.isfunction(module): - raise TypeError('module must be a class or a function, ' - f'but got {type(module)}') - - if module_name is None: - module_name = module.__name__ - if isinstance(module_name, str): - module_name = [module_name] - for name in module_name: - if not force and name in self._module_dict: - raise KeyError(f'{name} is already registered ' - f'in {self.name}') - self._module_dict[name] = module - - def deprecated_register_module(self, cls=None, force=False): - warnings.warn( - 'The old API of register_module(module, force=False) ' - 'is deprecated and will be removed, please use the new API ' - 'register_module(name=None, force=False, module=None) instead.', - DeprecationWarning) - if cls is None: - return partial(self.deprecated_register_module, force=force) - self._register_module(cls, force=force) - return cls - - def register_module(self, name=None, force=False, module=None): - """Register a module. - - A record will be added to `self._module_dict`, whose key is the class - name or the specified name, and value is the class itself. - It can be used as a decorator or a normal function. - - Example: - >>> backbones = Registry('backbone') - >>> @backbones.register_module() - >>> class ResNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> @backbones.register_module(name='mnet') - >>> class MobileNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> class ResNet: - >>> pass - >>> backbones.register_module(ResNet) - - Args: - name (str | None): The module name to be registered. If not - specified, the class name will be used. - force (bool, optional): Whether to override an existing class with - the same name. Default: False. - module (type): Module class or function to be registered. - """ - if not isinstance(force, bool): - raise TypeError(f'force must be a boolean, but got {type(force)}') - # NOTE: This is a walkaround to be compatible with the old api, - # while it may introduce unexpected bugs. - if isinstance(name, type): - return self.deprecated_register_module(name, force=force) - - # raise the error ahead of time - if not (name is None or isinstance(name, str) or is_seq_of(name, str)): - raise TypeError( - 'name must be either of None, an instance of str or a sequence' - f' of str, but got {type(name)}') - - # use it as a normal method: x.register_module(module=SomeClass) - if module is not None: - self._register_module(module=module, module_name=name, force=force) - return module - - # use it as a decorator: @x.register_module() - def _register(module): - self._register_module(module=module, module_name=name, force=force) - return module - - return _register diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/seed.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/seed.py deleted file mode 100644 index 003f92367..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/seed.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import random - -import numpy as np -import torch - - -def worker_init_fn(worker_id: int, num_workers: int, rank: int, seed: int): - """Function to initialize each worker. - - The seed of each worker equals to - ``num_worker * rank + worker_id + user_seed``. - - Args: - worker_id (int): Id for each worker. - num_workers (int): Number of workers. - rank (int): Rank in distributed training. - seed (int): Random seed. - """ - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/testing.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/testing.py deleted file mode 100644 index 7b64e8fae..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/testing.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (c) Open-MMLab. -import sys -from collections.abc import Iterable -from runpy import run_path -from shlex import split -from typing import Any, Dict, List -from unittest.mock import patch - - -def check_python_script(cmd): - """Run the python cmd script with `__main__`. The difference between - `os.system` is that, this function exectues code in the current process, so - that it can be tracked by coverage tools. Currently it supports two forms: - - - ./tests/data/scripts/hello.py zz - - python tests/data/scripts/hello.py zz - """ - args = split(cmd) - if args[0] == 'python': - args = args[1:] - with patch.object(sys, 'argv', args): - run_path(args[0], run_name='__main__') - - -def _any(judge_result): - """Since built-in ``any`` works only when the element of iterable is not - iterable, implement the function.""" - if not isinstance(judge_result, Iterable): - return judge_result - - try: - for element in judge_result: - if _any(element): - return True - except TypeError: - # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) - if judge_result: - return True - return False - - -def assert_dict_contains_subset(dict_obj: Dict[Any, Any], - expected_subset: Dict[Any, Any]) -> bool: - """Check if the dict_obj contains the expected_subset. - - Args: - dict_obj (Dict[Any, Any]): Dict object to be checked. - expected_subset (Dict[Any, Any]): Subset expected to be contained in - dict_obj. - - Returns: - bool: Whether the dict_obj contains the expected_subset. - """ - - for key, value in expected_subset.items(): - if key not in dict_obj.keys() or _any(dict_obj[key] != value): - return False - return True - - -def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: - """Check if attribute of class object is correct. - - Args: - obj (object): Class object to be checked. - expected_attrs (Dict[str, Any]): Dict of the expected attrs. - - Returns: - bool: Whether the attribute of class object is correct. - """ - for attr, value in expected_attrs.items(): - if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): - return False - return True - - -def assert_dict_has_keys(obj: Dict[str, Any], - expected_keys: List[str]) -> bool: - """Check if the obj has all the expected_keys. - - Args: - obj (Dict[str, Any]): Object to be checked. - expected_keys (List[str]): Keys expected to contained in the keys of - the obj. - - Returns: - bool: Whether the obj has the expected keys. - """ - return set(expected_keys).issubset(set(obj.keys())) - - -def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: - """Check if target_keys is equal to result_keys. - - Args: - result_keys (List[str]): Result keys to be checked. - target_keys (List[str]): Target keys to be checked. - - Returns: - bool: Whether target_keys is equal to result_keys. - """ - return set(result_keys) == set(target_keys) - - -def assert_is_norm_layer(module) -> bool: - """Check if the module is a norm layer. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the module is a norm layer. - """ - from torch.nn import GroupNorm, LayerNorm - - from .parrots_wrapper import _BatchNorm, _InstanceNorm - norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) - return isinstance(module, norm_layer_candidates) - - -def assert_params_all_zeros(module) -> bool: - """Check if the parameters of the module is all zeros. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the parameters of the module is all zeros. - """ - weight_data = module.weight.data - is_weight_zero = weight_data.allclose( - weight_data.new_zeros(weight_data.size())) - - if hasattr(module, 'bias') and module.bias is not None: - bias_data = module.bias.data - is_bias_zero = bias_data.allclose( - bias_data.new_zeros(bias_data.size())) - else: - is_bias_zero = True - - return is_weight_zero and is_bias_zero diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/timer.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/timer.py deleted file mode 100644 index 02e96e537..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/timer.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from time import time - - -class TimerError(Exception): - - def __init__(self, message): - self.message = message - super(TimerError, self).__init__(message) - - -class Timer: - """A flexible Timer class. - - Examples: - >>> import time - >>> import mmcv - >>> with mmcv.Timer(): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - 1.000 - >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - it takes 1.0 seconds - >>> timer = mmcv.Timer() - >>> time.sleep(0.5) - >>> print(timer.since_start()) - 0.500 - >>> time.sleep(0.5) - >>> print(timer.since_last_check()) - 0.500 - >>> print(timer.since_start()) - 1.000 - """ - - def __init__(self, start=True, print_tmpl=None): - self._is_running = False - self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' - if start: - self.start() - - @property - def is_running(self): - """bool: indicate whether the timer is running""" - return self._is_running - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - print(self.print_tmpl.format(self.since_last_check())) - self._is_running = False - - def start(self): - """Start the timer.""" - if not self._is_running: - self._t_start = time() - self._is_running = True - self._t_last = time() - - def since_start(self): - """Total time since the timer is started. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - self._t_last = time() - return self._t_last - self._t_start - - def since_last_check(self): - """Time since the last checking. - - Either :func:`since_start` or :func:`since_last_check` is a checking - operation. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - dur = time() - self._t_last - self._t_last = time() - return dur - - -_g_timers = {} # global timers - - -def check_time(timer_id): - """Add check points in a single line. - - This method is suitable for running a task on a list of items. A timer will - be registered when the method is called for the first time. - - Examples: - >>> import time - >>> import mmcv - >>> for i in range(1, 6): - >>> # simulate a code block - >>> time.sleep(i) - >>> mmcv.check_time('task1') - 2.000 - 3.000 - 4.000 - 5.000 - - Args: - str: Timer identifier. - """ - if timer_id not in _g_timers: - _g_timers[timer_id] = Timer() - return 0 - else: - return _g_timers[timer_id].since_last_check() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/trace.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/trace.py deleted file mode 100644 index 45423bd05..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/trace.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch - -from mmcv.utils import digit_version - - -def is_jit_tracing() -> bool: - if (torch.__version__ != 'parrots' - and digit_version(torch.__version__) >= digit_version('1.6.0')): - on_trace = torch.jit.is_tracing() - # In PyTorch 1.6, torch.jit.is_tracing has a bug. - # Refers to https://github.com/pytorch/pytorch/issues/42448 - if isinstance(on_trace, bool): - return on_trace - else: - return torch._C._is_tracing() - else: - warnings.warn( - 'torch.jit.is_tracing is only supported after v1.6.0. ' - 'Therefore is_tracing returns False automatically. Please ' - 'set on_trace manually if you are using trace.', UserWarning) - return False diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/version_utils.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/version_utils.py deleted file mode 100644 index 963c45a2e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/utils/version_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import subprocess -import warnings - -from packaging.version import parse - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - assert 'parrots' not in version_str - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - -def get_git_hash(fallback='unknown', digits=None): - """Get the git hash of the current repo. - - Args: - fallback (str, optional): The fallback string when git hash is - unavailable. Defaults to 'unknown'. - digits (int, optional): kept digits of the hash. Defaults to None, - meaning all digits are kept. - - Returns: - str: Git commit hash. - """ - - if digits is not None and not isinstance(digits, int): - raise TypeError('digits must be None or an integer') - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - sha = out.strip().decode('ascii') - if digits is not None: - sha = sha[:digits] - except OSError: - sha = fallback - - return sha diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/version.py b/cv/semantic_segmentation/ddrnet/pytorch/mmcv/version.py deleted file mode 100644 index a97ffc2dd..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmcv/version.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -__version__ = '1.5.0' - - -def parse_version_info(version_str: str, length: int = 4) -> tuple: - """Parse a version string into a tuple. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int | str]: The version info, e.g., "1.3.0" is parsed into - (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into - (2, 0, 0, 0, 'rc', 1) (when length is set to 4). - """ - from packaging.version import parse - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - release.extend(list(version.pre)) - elif version.is_postrelease: - release.extend(list(version.post)) - else: - release.extend([0, 0]) - return tuple(release) - - -version_info = tuple(int(x) for x in __version__.split('.')[:3]) - -__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/__init__.py deleted file mode 100644 index 360abfc85..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv -from packaging.version import parse - -from .version import __version__, version_info - -MMCV_MIN = '1.3.13' -MMCV_MAX = '1.6.0' - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -mmcv_min_version = digit_version(MMCV_MIN) -mmcv_max_version = digit_version(MMCV_MAX) -mmcv_version = digit_version(mmcv.__version__) - - -assert (mmcv_min_version <= mmcv_version <= mmcv_max_version), \ - f'MMCV=={mmcv.__version__} is used but incompatible. ' \ - f'Please install mmcv>={mmcv_min_version}, <={mmcv_max_version}.' - -__all__ = ['__version__', 'version_info', 'digit_version'] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/__init__.py deleted file mode 100644 index c68818053..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .inference import inference_segmentor, init_segmentor, show_result_pyplot -from .test import multi_gpu_test, single_gpu_test -from .train import (get_root_logger, init_random_seed, set_random_seed, - train_segmentor) - -__all__ = [ - 'get_root_logger', 'set_random_seed', 'train_segmentor', 'init_segmentor', - 'inference_segmentor', 'multi_gpu_test', 'single_gpu_test', - 'show_result_pyplot', 'init_random_seed' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/inference.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/inference.py deleted file mode 100644 index 906943804..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/inference.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import matplotlib.pyplot as plt -import mmcv -import torch -from mmcv.parallel import collate, scatter -from mmcv.runner import load_checkpoint - -from mmseg.datasets.pipelines import Compose -from mmseg.models import build_segmentor - - -def init_segmentor(config, checkpoint=None, device='cuda:0'): - """Initialize a segmentor from config file. - - Args: - config (str or :obj:`mmcv.Config`): Config file path or the config - object. - checkpoint (str, optional): Checkpoint path. If left as None, the model - will not load any weights. - device (str, optional) CPU/CUDA device option. Default 'cuda:0'. - Use 'cpu' for loading model on CPU. - Returns: - nn.Module: The constructed segmentor. - """ - if isinstance(config, str): - config = mmcv.Config.fromfile(config) - elif not isinstance(config, mmcv.Config): - raise TypeError('config must be a filename or Config object, ' - 'but got {}'.format(type(config))) - config.model.pretrained = None - config.model.train_cfg = None - model = build_segmentor(config.model, test_cfg=config.get('test_cfg')) - if checkpoint is not None: - checkpoint = load_checkpoint(model, checkpoint, map_location='cpu') - model.CLASSES = checkpoint['meta']['CLASSES'] - model.PALETTE = checkpoint['meta']['PALETTE'] - model.cfg = config # save the config in the model for convenience - model.to(device) - model.eval() - return model - - -class LoadImage: - """A simple pipeline to load image.""" - - def __call__(self, results): - """Call function to load images into results. - - Args: - results (dict): A result dict contains the file name - of the image to be read. - - Returns: - dict: ``results`` will be returned containing loaded image. - """ - - if isinstance(results['img'], str): - results['filename'] = results['img'] - results['ori_filename'] = results['img'] - else: - results['filename'] = None - results['ori_filename'] = None - img = mmcv.imread(results['img']) - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - return results - - -def inference_segmentor(model, img): - """Inference image(s) with the segmentor. - - Args: - model (nn.Module): The loaded segmentor. - imgs (str/ndarray or list[str/ndarray]): Either image files or loaded - images. - - Returns: - (list[Tensor]): The segmentation result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # build the data pipeline - test_pipeline = [LoadImage()] + cfg.data.test.pipeline[1:] - test_pipeline = Compose(test_pipeline) - # prepare data - data = dict(img=img) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if next(model.parameters()).is_cuda: - # scatter to specified GPU - data = scatter(data, [device])[0] - else: - data['img_metas'] = [i.data[0] for i in data['img_metas']] - - # forward the model - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - return result - - -def show_result_pyplot(model, - img, - result, - palette=None, - fig_size=(15, 10), - opacity=0.5, - title='', - block=True): - """Visualize the segmentation results on the image. - - Args: - model (nn.Module): The loaded segmentor. - img (str or np.ndarray): Image filename or loaded image. - result (list): The segmentation result. - palette (list[list[int]]] | None): The palette of segmentation - map. If None is given, random palette will be generated. - Default: None - fig_size (tuple): Figure size of the pyplot figure. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - title (str): The title of pyplot figure. - Default is ''. - block (bool): Whether to block the pyplot figure. - Default is True. - """ - if hasattr(model, 'module'): - model = model.module - img = model.show_result( - img, result, palette=palette, show=False, opacity=opacity) - plt.figure(figsize=fig_size) - plt.imshow(mmcv.bgr2rgb(img)) - plt.title(title) - plt.tight_layout() - plt.show(block=block) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/test.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/test.py deleted file mode 100644 index cc4fcc979..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/test.py +++ /dev/null @@ -1,233 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import tempfile -import warnings - -import mmcv -import numpy as np -import torch -from mmcv.engine import collect_results_cpu, collect_results_gpu -from mmcv.image import tensor2imgs -from mmcv.runner import get_dist_info - - -def np2tmp(array, temp_file_name=None, tmpdir=None): - """Save ndarray to local numpy file. - - Args: - array (ndarray): Ndarray to save. - temp_file_name (str): Numpy file name. If 'temp_file_name=None', this - function will generate a file name with tempfile.NamedTemporaryFile - to save ndarray. Default: None. - tmpdir (str): Temporary directory to save Ndarray files. Default: None. - Returns: - str: The numpy file name. - """ - - if temp_file_name is None: - temp_file_name = tempfile.NamedTemporaryFile( - suffix='.npy', delete=False, dir=tmpdir).name - np.save(temp_file_name, array) - return temp_file_name - - -def single_gpu_test(model, - data_loader, - show=False, - out_dir=None, - efficient_test=False, - opacity=0.5, - pre_eval=False, - format_only=False, - format_args={}): - """Test with single GPU by progressive mode. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - show (bool): Whether show results during inference. Default: False. - out_dir (str, optional): If specified, the results will be dumped into - the directory to save output results. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - loader_indices = data_loader.batch_sampler - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - - if show or out_dir: - img_tensor = data['img'][0] - img_metas = data['img_metas'][0].data[0] - imgs = tensor2imgs(img_tensor, **img_metas[0]['img_norm_cfg']) - assert len(imgs) == len(img_metas) - - for img, img_meta in zip(imgs, img_metas): - h, w, _ = img_meta['img_shape'] - img_show = img[:h, :w, :] - - ori_h, ori_w = img_meta['ori_shape'][:-1] - img_show = mmcv.imresize(img_show, (ori_w, ori_h)) - - if out_dir: - out_file = osp.join(out_dir, img_meta['ori_filename']) - else: - out_file = None - - model.module.show_result( - img_show, - result, - palette=dataset.PALETTE, - show=show, - out_file=out_file, - opacity=opacity) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - results.extend(result) - else: - results.extend(result) - - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - - return results - - -def multi_gpu_test(model, - data_loader, - tmpdir=None, - gpu_collect=False, - efficient_test=False, - pre_eval=False, - format_only=False, - format_args={}): - """Test model with multiple gpus by progressive mode. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' - it encodes results to gpu tensors and use gpu communication for results - collection. On cpu mode it saves the results on different gpus to 'tmpdir' - and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. The same path is used for efficient - test. Default: None. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - - # batch_sampler based on DistributedSampler, the indices only point to data - # samples of related machine. - loader_indices = data_loader.batch_sampler - - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - - results.extend(result) - - if rank == 0: - batch_size = len(result) * world_size - for _ in range(batch_size): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/train.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/train.py deleted file mode 100644 index a6dd8cc89..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/apis/train.py +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import random -import warnings - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (HOOKS, DistSamplerSeedHook, EpochBasedRunner, - build_runner, get_dist_info) -from mmcv.utils import build_from_cfg - -from mmseg import digit_version -from mmseg.core import DistEvalHook, EvalHook, build_optimizer -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.utils import find_latest_checkpoint, get_root_logger - - -def init_random_seed(seed=None, device='cuda'): - """Initialize random seed. - - If the seed is not set, the seed will be automatically randomized, - and then broadcast to all processes to prevent some potential bugs. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is not None: - return seed - - # Make sure all ranks share the same random seed to prevent - # some potential bugs. Please refer to - # https://github.com/open-mmlab/mmdetection/issues/6339 - rank, world_size = get_dist_info() - seed = np.random.randint(2**31) - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() - - -def set_random_seed(seed, deterministic=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - """ - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - - -def train_segmentor(model, - dataset, - cfg, - distributed=False, - validate=False, - timestamp=None, - meta=None): - """Launch segmentor training.""" - logger = get_root_logger(cfg.log_level) - - # prepare data loaders - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - # The default loader config - loader_cfg = dict( - # cfg.gpus will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - seed=cfg.seed, - drop_last=True) - # The overall dataloader settings - loader_cfg.update({ - k: v - for k, v in cfg.data.items() if k not in [ - 'train', 'val', 'test', 'train_dataloader', 'val_dataloader', - 'test_dataloader' - ] - }) - - # The specific dataloader settings - train_loader_cfg = {**loader_cfg, **cfg.data.get('train_dataloader', {})} - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus - if distributed: - find_unused_parameters = True - # Sets the `find_unused_parameters` parameter in - # torch.nn.parallel.DistributedDataParallel - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - else: - if not torch.cuda.is_available(): - assert digit_version(mmcv.__version__) >= digit_version('1.4.4'), \ - 'Please use MMCV >= 1.4.4 for CPU training!' - model = MMDataParallel(model, device_ids=cfg.gpu_ids) - # build runner - optimizer = build_optimizer(model, cfg.optimizer) - - if cfg.get('runner') is None: - cfg.runner = {'type': 'IterBasedRunner', 'max_iters': cfg.total_iters} - warnings.warn( - 'config is now expected to have a `runner` section, ' - 'please set `runner` in your config.', UserWarning) - - runner = build_runner( - cfg.runner, - default_args=dict( - model=model, - batch_processor=None, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta)) - - runner.cfg = cfg - - # register hooks - runner.register_training_hooks(cfg.lr_config, cfg.optimizer_config, - cfg.checkpoint_config, cfg.log_config, - cfg.get('momentum_config', None)) - if distributed: - # when distributed training by epoch, using`DistSamplerSeedHook` to set - # the different seed to distributed sampler for each epoch, it will - # shuffle dataset at each epoch and avoid overfitting. - if isinstance(runner, EpochBasedRunner): - runner.register_hook(DistSamplerSeedHook()) - - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register eval hooks - if validate: - val_dataset = build_dataset(cfg.data.val, dict(test_mode=True)) - # The specific dataloader settings - val_loader_cfg = { - **loader_cfg, - 'samples_per_gpu': 1, - 'shuffle': False, # Not shuffle by default - **cfg.data.get('val_dataloader', {}), - } - val_dataloader = build_dataloader(val_dataset, **val_loader_cfg) - eval_cfg = cfg.get('evaluation', {}) - eval_cfg['by_epoch'] = cfg.runner['type'] != 'IterBasedRunner' - eval_hook = DistEvalHook if distributed else EvalHook - # In this PR (https://github.com/open-mmlab/mmcv/pull/1193), the - # priority of IterTimerHook has been modified from 'NORMAL' to 'LOW'. - runner.register_hook( - eval_hook(val_dataloader, **eval_cfg), priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from is None and cfg.get('auto_resume'): - resume_from = find_latest_checkpoint(cfg.work_dir) - if resume_from is not None: - cfg.resume_from = resume_from - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/__init__.py deleted file mode 100644 index 1a077d2f1..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, build_optimizer, - build_optimizer_constructor) -from .evaluation import * # noqa: F401, F403 -from .optimizers import * # noqa: F401, F403 -from .seg import * # noqa: F401, F403 -from .utils import * # noqa: F401, F403 - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/builder.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/builder.py deleted file mode 100644 index 406dd9b4b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/builder.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from mmcv.runner.optimizer import OPTIMIZER_BUILDERS as MMCV_OPTIMIZER_BUILDERS -from mmcv.utils import Registry, build_from_cfg - -OPTIMIZER_BUILDERS = Registry( - 'optimizer builder', parent=MMCV_OPTIMIZER_BUILDERS) - - -def build_optimizer_constructor(cfg): - constructor_type = cfg.get('type') - if constructor_type in OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - elif constructor_type in MMCV_OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, MMCV_OPTIMIZER_BUILDERS) - else: - raise KeyError(f'{constructor_type} is not registered ' - 'in the optimizer builder registry.') - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/__init__.py deleted file mode 100644 index 3d16d17e5..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .class_names import get_classes, get_palette -from .eval_hooks import DistEvalHook, EvalHook -from .metrics import (eval_metrics, intersect_and_union, mean_dice, - mean_fscore, mean_iou, pre_eval_to_metrics) - -__all__ = [ - 'EvalHook', 'DistEvalHook', 'mean_dice', 'mean_iou', 'mean_fscore', - 'eval_metrics', 'get_classes', 'get_palette', 'pre_eval_to_metrics', - 'intersect_and_union' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/class_names.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/class_names.py deleted file mode 100644 index e3bff6231..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/class_names.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - - -def cityscapes_classes(): - """Cityscapes class names for external use.""" - return [ - 'road', 'sidewalk', 'building', 'wall', 'fence', 'pole', - 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle' - ] - - -def ade_classes(): - """ADE20K class names for external use.""" - return [ - 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', - 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', - 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', - 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', - 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', - 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', - 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', - 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', - 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', - 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', - 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', - 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', - 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', - 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', - 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', - 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', - 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', - 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', - 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', - 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', - 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', - 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', - 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', - 'clock', 'flag' - ] - - -def voc_classes(): - """Pascal VOC class names for external use.""" - return [ - 'background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', - 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', - 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', - 'tvmonitor' - ] - - -def cocostuff_classes(): - """CocoStuff class names for external use.""" - return [ - 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', - 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', - 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', - 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', - 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', - 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', - 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', - 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', - 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', - 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', - 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', - 'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner', - 'blanket', 'branch', 'bridge', 'building-other', 'bush', 'cabinet', - 'cage', 'cardboard', 'carpet', 'ceiling-other', 'ceiling-tile', - 'cloth', 'clothes', 'clouds', 'counter', 'cupboard', 'curtain', - 'desk-stuff', 'dirt', 'door-stuff', 'fence', 'floor-marble', - 'floor-other', 'floor-stone', 'floor-tile', 'floor-wood', 'flower', - 'fog', 'food-other', 'fruit', 'furniture-other', 'grass', 'gravel', - 'ground-other', 'hill', 'house', 'leaves', 'light', 'mat', 'metal', - 'mirror-stuff', 'moss', 'mountain', 'mud', 'napkin', 'net', 'paper', - 'pavement', 'pillow', 'plant-other', 'plastic', 'platform', - 'playingfield', 'railing', 'railroad', 'river', 'road', 'rock', 'roof', - 'rug', 'salad', 'sand', 'sea', 'shelf', 'sky-other', 'skyscraper', - 'snow', 'solid-other', 'stairs', 'stone', 'straw', 'structural-other', - 'table', 'tent', 'textile-other', 'towel', 'tree', 'vegetable', - 'wall-brick', 'wall-concrete', 'wall-other', 'wall-panel', - 'wall-stone', 'wall-tile', 'wall-wood', 'water-other', 'waterdrops', - 'window-blind', 'window-other', 'wood' - ] - - -def loveda_classes(): - """LoveDA class names for external use.""" - return [ - 'background', 'building', 'road', 'water', 'barren', 'forest', - 'agricultural' - ] - - -def potsdam_classes(): - """Potsdam class names for external use.""" - return [ - 'impervious_surface', 'building', 'low_vegetation', 'tree', 'car', - 'clutter' - ] - - -def vaihingen_classes(): - """Vaihingen class names for external use.""" - return [ - 'impervious_surface', 'building', 'low_vegetation', 'tree', 'car', - 'clutter' - ] - - -def isaid_classes(): - """iSAID class names for external use.""" - return [ - 'background', 'ship', 'store_tank', 'baseball_diamond', 'tennis_court', - 'basketball_court', 'Ground_Track_Field', 'Bridge', 'Large_Vehicle', - 'Small_Vehicle', 'Helicopter', 'Swimming_pool', 'Roundabout', - 'Soccer_ball_field', 'plane', 'Harbor' - ] - - -def stare_classes(): - """stare class names for external use.""" - return ['background', 'vessel'] - - -def cityscapes_palette(): - """Cityscapes palette for external use.""" - return [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], - [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], - [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], - [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], [0, 80, 100], - [0, 0, 230], [119, 11, 32]] - - -def ade_palette(): - """ADE20K palette for external use.""" - return [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], - [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], - [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], - [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], - [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], - [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], - [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], - [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], - [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], - [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], - [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], - [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], - [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], - [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], - [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], - [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], - [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], - [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], - [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], - [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], - [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], - [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], - [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], - [102, 255, 0], [92, 0, 255]] - - -def voc_palette(): - """Pascal VOC palette for external use.""" - return [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], - [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], - [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], - [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], - [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] - - -def cocostuff_palette(): - """CocoStuff palette for external use.""" - return [[0, 192, 64], [0, 192, 64], [0, 64, 96], [128, 192, 192], - [0, 64, 64], [0, 192, 224], [0, 192, 192], [128, 192, 64], - [0, 192, 96], [128, 192, 64], [128, 32, 192], [0, 0, 224], - [0, 0, 64], [0, 160, 192], [128, 0, 96], [128, 0, 192], - [0, 32, 192], [128, 128, 224], [0, 0, 192], [128, 160, 192], - [128, 128, 0], [128, 0, 32], [128, 32, 0], [128, 0, 128], - [64, 128, 32], [0, 160, 0], [0, 0, 0], [192, 128, 160], [0, 32, 0], - [0, 128, 128], [64, 128, 160], [128, 160, 0], [0, 128, 0], - [192, 128, 32], [128, 96, 128], [0, 0, 128], [64, 0, 32], - [0, 224, 128], [128, 0, 0], [192, 0, 160], [0, 96, 128], - [128, 128, 128], [64, 0, 160], [128, 224, 128], [128, 128, 64], - [192, 0, 32], [128, 96, 0], [128, 0, 192], [0, 128, 32], - [64, 224, 0], [0, 0, 64], [128, 128, 160], [64, 96, 0], - [0, 128, 192], [0, 128, 160], [192, 224, 0], [0, 128, 64], - [128, 128, 32], [192, 32, 128], [0, 64, 192], [0, 0, 32], - [64, 160, 128], [128, 64, 64], [128, 0, 160], [64, 32, 128], - [128, 192, 192], [0, 0, 160], [192, 160, 128], [128, 192, 0], - [128, 0, 96], [192, 32, 0], [128, 64, 128], [64, 128, 96], - [64, 160, 0], [0, 64, 0], [192, 128, 224], [64, 32, 0], - [0, 192, 128], [64, 128, 224], [192, 160, 0], [0, 192, 0], - [192, 128, 96], [192, 96, 128], [0, 64, 128], [64, 0, 96], - [64, 224, 128], [128, 64, 0], [192, 0, 224], [64, 96, 128], - [128, 192, 128], [64, 0, 224], [192, 224, 128], [128, 192, 64], - [192, 0, 96], [192, 96, 0], [128, 64, 192], [0, 128, 96], - [0, 224, 0], [64, 64, 64], [128, 128, 224], [0, 96, 0], - [64, 192, 192], [0, 128, 224], [128, 224, 0], [64, 192, 64], - [128, 128, 96], [128, 32, 128], [64, 0, 192], [0, 64, 96], - [0, 160, 128], [192, 0, 64], [128, 64, 224], [0, 32, 128], - [192, 128, 192], [0, 64, 224], [128, 160, 128], [192, 128, 0], - [128, 64, 32], [128, 32, 64], [192, 0, 128], [64, 192, 32], - [0, 160, 64], [64, 0, 0], [192, 192, 160], [0, 32, 64], - [64, 128, 128], [64, 192, 160], [128, 160, 64], [64, 128, 0], - [192, 192, 32], [128, 96, 192], [64, 0, 128], [64, 64, 32], - [0, 224, 192], [192, 0, 0], [192, 64, 160], [0, 96, 192], - [192, 128, 128], [64, 64, 160], [128, 224, 192], [192, 128, 64], - [192, 64, 32], [128, 96, 64], [192, 0, 192], [0, 192, 32], - [64, 224, 64], [64, 0, 64], [128, 192, 160], [64, 96, 64], - [64, 128, 192], [0, 192, 160], [192, 224, 64], [64, 128, 64], - [128, 192, 32], [192, 32, 192], [64, 64, 192], [0, 64, 32], - [64, 160, 192], [192, 64, 64], [128, 64, 160], [64, 32, 192], - [192, 192, 192], [0, 64, 160], [192, 160, 192], [192, 192, 0], - [128, 64, 96], [192, 32, 64], [192, 64, 128], [64, 192, 96], - [64, 160, 64], [64, 64, 0]] - - -def loveda_palette(): - """LoveDA palette for external use.""" - return [[255, 255, 255], [255, 0, 0], [255, 255, 0], [0, 0, 255], - [159, 129, 183], [0, 255, 0], [255, 195, 128]] - - -def potsdam_palette(): - """Potsdam palette for external use.""" - return [[255, 255, 255], [0, 0, 255], [0, 255, 255], [0, 255, 0], - [255, 255, 0], [255, 0, 0]] - - -def vaihingen_palette(): - """Vaihingen palette for external use.""" - return [[255, 255, 255], [0, 0, 255], [0, 255, 255], [0, 255, 0], - [255, 255, 0], [255, 0, 0]] - - -def isaid_palette(): - """iSAID palette for external use.""" - return [[0, 0, 0], [0, 0, 63], [0, 63, 63], [0, 63, 0], [0, 63, 127], - [0, 63, 191], [0, 63, 255], [0, 127, 63], [0, 127, - 127], [0, 0, 127], - [0, 0, 191], [0, 0, 255], [0, 191, 127], [0, 127, 191], - [0, 127, 255], [0, 100, 155]] - - -def stare_palette(): - """STARE palette for external use.""" - return [[120, 120, 120], [6, 230, 230]] - - -dataset_aliases = { - 'cityscapes': ['cityscapes'], - 'ade': ['ade', 'ade20k'], - 'voc': ['voc', 'pascal_voc', 'voc12', 'voc12aug'], - 'loveda': ['loveda'], - 'potsdam': ['potsdam'], - 'vaihingen': ['vaihingen'], - 'cocostuff': [ - 'cocostuff', 'cocostuff10k', 'cocostuff164k', 'coco-stuff', - 'coco-stuff10k', 'coco-stuff164k', 'coco_stuff', 'coco_stuff10k', - 'coco_stuff164k' - ], - 'isaid': ['isaid', 'iSAID'], - 'stare': ['stare', 'STARE'] -} - - -def get_classes(dataset): - """Get class names of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_classes()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels - - -def get_palette(dataset): - """Get class palette (RGB) of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_palette()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/eval_hooks.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/eval_hooks.py deleted file mode 100644 index 952db3b0b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/eval_hooks.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -import torch.distributed as dist -from mmcv.runner import DistEvalHook as _DistEvalHook -from mmcv.runner import EvalHook as _EvalHook -from torch.nn.modules.batchnorm import _BatchNorm - - -class EvalHook(_EvalHook): - """Single GPU EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``single_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - if not self._should_evaluate(runner): - return - - from mmseg.apis import single_gpu_test - results = single_gpu_test( - runner.model, self.dataloader, show=False, pre_eval=self.pre_eval) - runner.log_buffer.clear() - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - if self.save_best: - self._save_ckpt(runner, key_score) - - -class DistEvalHook(_DistEvalHook): - """Distributed EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``multi_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - if not self._should_evaluate(runner): - return - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - from mmseg.apis import multi_gpu_test - results = multi_gpu_test( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect, - pre_eval=self.pre_eval) - - runner.log_buffer.clear() - - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - - if self.save_best: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/metrics.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/metrics.py deleted file mode 100644 index a1c0908e1..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/evaluation/metrics.py +++ /dev/null @@ -1,395 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import mmcv -import numpy as np -import torch - - -def f_score(precision, recall, beta=1): - """calculate the f-score value. - - Args: - precision (float | torch.Tensor): The precision value. - recall (float | torch.Tensor): The recall value. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - Returns: - [torch.tensor]: The f-score value. - """ - score = (1 + beta**2) * (precision * recall) / ( - (beta**2 * precision) + recall) - return score - - -def intersect_and_union(pred_label, - label, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate intersection and Union. - - Args: - pred_label (ndarray | str): Prediction segmentation map - or predict result filename. - label (ndarray | str): Ground truth segmentation map - or label filename. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. The parameter will - work only when label is str. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. The parameter will - work only when label is str. Default: False. - - Returns: - torch.Tensor: The intersection of prediction and ground truth - histogram on all classes. - torch.Tensor: The union of prediction and ground truth histogram on - all classes. - torch.Tensor: The prediction histogram on all classes. - torch.Tensor: The ground truth histogram on all classes. - """ - - if isinstance(pred_label, str): - pred_label = torch.from_numpy(np.load(pred_label)) - else: - pred_label = torch.from_numpy((pred_label)) - - if isinstance(label, str): - label = torch.from_numpy( - mmcv.imread(label, flag='unchanged', backend='pillow')) - else: - label = torch.from_numpy(label) - - if label_map is not None: - for old_id, new_id in label_map.items(): - label[label == old_id] = new_id - if reduce_zero_label: - label[label == 0] = 255 - label = label - 1 - label[label == 254] = 255 - - mask = (label != ignore_index) - pred_label = pred_label[mask] - label = label[mask] - - intersect = pred_label[pred_label == label] - area_intersect = torch.histc( - intersect.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_pred_label = torch.histc( - pred_label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_label = torch.histc( - label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_union = area_pred_label + area_label - area_intersect - return area_intersect, area_union, area_pred_label, area_label - - -def total_intersect_and_union(results, - gt_seg_maps, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate Total Intersection and Union. - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - ndarray: The intersection of prediction and ground truth histogram - on all classes. - ndarray: The union of prediction and ground truth histogram on all - classes. - ndarray: The prediction histogram on all classes. - ndarray: The ground truth histogram on all classes. - """ - total_area_intersect = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_union = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_pred_label = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_label = torch.zeros((num_classes, ), dtype=torch.float64) - for result, gt_seg_map in zip(results, gt_seg_maps): - area_intersect, area_union, area_pred_label, area_label = \ - intersect_and_union( - result, gt_seg_map, num_classes, ignore_index, - label_map, reduce_zero_label) - total_area_intersect += area_intersect - total_area_union += area_union - total_area_pred_label += area_pred_label - total_area_label += area_label - return total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label - - -def mean_iou(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category IoU, shape (num_classes, ). - """ - iou_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mIoU'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return iou_result - - -def mean_dice(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Dice (mDice) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category dice, shape (num_classes, ). - """ - - dice_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mDice'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return dice_result - - -def mean_fscore(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category recall, shape (num_classes, ). - ndarray: Per category precision, shape (num_classes, ). - ndarray: Per category f-score, shape (num_classes, ). - """ - fscore_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mFscore'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label, - beta=beta) - return fscore_result - - -def eval_metrics(results, - gt_seg_maps, - num_classes, - ignore_index, - metrics=['mIoU'], - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate evaluation metrics - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label = total_intersect_and_union( - results, gt_seg_maps, num_classes, ignore_index, label_map, - reduce_zero_label) - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def pre_eval_to_metrics(pre_eval_results, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Convert pre-eval results to metrics. - - Args: - pre_eval_results (list[tuple[torch.Tensor]]): per image eval results - for computing evaluation metric - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - # convert list of tuples to tuple of lists, e.g. - # [(A_1, B_1, C_1, D_1), ..., (A_n, B_n, C_n, D_n)] to - # ([A_1, ..., A_n], ..., [D_1, ..., D_n]) - pre_eval_results = tuple(zip(*pre_eval_results)) - assert len(pre_eval_results) == 4 - - total_area_intersect = sum(pre_eval_results[0]) - total_area_union = sum(pre_eval_results[1]) - total_area_pred_label = sum(pre_eval_results[2]) - total_area_label = sum(pre_eval_results[3]) - - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def total_area_to_metrics(total_area_intersect, - total_area_union, - total_area_pred_label, - total_area_label, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Calculate evaluation metrics - Args: - total_area_intersect (ndarray): The intersection of prediction and - ground truth histogram on all classes. - total_area_union (ndarray): The union of prediction and ground truth - histogram on all classes. - total_area_pred_label (ndarray): The prediction histogram on all - classes. - total_area_label (ndarray): The ground truth histogram on all classes. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - if isinstance(metrics, str): - metrics = [metrics] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metrics).issubset(set(allowed_metrics)): - raise KeyError('metrics {} is not supported'.format(metrics)) - - all_acc = total_area_intersect.sum() / total_area_label.sum() - ret_metrics = OrderedDict({'aAcc': all_acc}) - for metric in metrics: - if metric == 'mIoU': - iou = total_area_intersect / total_area_union - acc = total_area_intersect / total_area_label - ret_metrics['IoU'] = iou - ret_metrics['Acc'] = acc - elif metric == 'mDice': - dice = 2 * total_area_intersect / ( - total_area_pred_label + total_area_label) - acc = total_area_intersect / total_area_label - ret_metrics['Dice'] = dice - ret_metrics['Acc'] = acc - elif metric == 'mFscore': - precision = total_area_intersect / total_area_pred_label - recall = total_area_intersect / total_area_label - f_value = torch.tensor( - [f_score(x[0], x[1], beta) for x in zip(precision, recall)]) - ret_metrics['Fscore'] = f_value - ret_metrics['Precision'] = precision - ret_metrics['Recall'] = recall - - ret_metrics = { - metric: value.numpy() - for metric, value in ret_metrics.items() - } - if nan_to_num is not None: - ret_metrics = OrderedDict({ - metric: np.nan_to_num(metric_value, nan=nan_to_num) - for metric, metric_value in ret_metrics.items() - }) - return ret_metrics diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/optimizers/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/optimizers/__init__.py deleted file mode 100644 index 4fbf4ecfc..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/optimizers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .layer_decay_optimizer_constructor import ( - LayerDecayOptimizerConstructor, LearningRateDecayOptimizerConstructor) - -__all__ = [ - 'LearningRateDecayOptimizerConstructor', 'LayerDecayOptimizerConstructor' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py deleted file mode 100644 index 2b6b8ff9c..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import warnings - -from mmcv.runner import DefaultOptimizerConstructor, get_dist_info - -from mmseg.utils import get_root_logger -from ..builder import OPTIMIZER_BUILDERS - - -def get_layer_id_for_convnext(var_name, max_layer_id): - """Get the layer id to set the different learning rates in ``layer_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_layer_id (int): Maximum number of backbone layers. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - stage_id = int(var_name.split('.')[2]) - if stage_id == 0: - layer_id = 0 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - block_id = int(var_name.split('.')[3]) - if stage_id == 0: - layer_id = 1 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 + block_id // 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - else: - return max_layer_id + 1 - - -def get_stage_id_for_convnext(var_name, max_stage_id): - """Get the stage id to set the different learning rates in ``stage_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_stage_id (int): Maximum number of backbone layers. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - return 0 - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - return stage_id + 1 - else: - return max_stage_id - 1 - - -def get_layer_id_for_vit(var_name, max_layer_id): - """Get the layer id to set the different learning rates. - - Args: - var_name (str): The key of the model. - num_max_layer (int): Maximum number of backbone layers. - - Returns: - int: Returns the layer id of the key. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.patch_embed'): - return 0 - elif var_name.startswith('backbone.layers'): - layer_id = int(var_name.split('.')[2]) - return layer_id + 1 - else: - return max_layer_id - 1 - - -@OPTIMIZER_BUILDERS.register_module() -class LearningRateDecayOptimizerConstructor(DefaultOptimizerConstructor): - """Different learning rates are set for different layers of backbone. - - Note: Currently, this optimizer constructor is built for ConvNeXt, - BEiT and MAE. - """ - - def add_params(self, params, module, **kwargs): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - """ - logger = get_root_logger() - - parameter_groups = {} - logger.info(f'self.paramwise_cfg is {self.paramwise_cfg}') - num_layers = self.paramwise_cfg.get('num_layers') + 2 - decay_rate = self.paramwise_cfg.get('decay_rate') - decay_type = self.paramwise_cfg.get('decay_type', 'layer_wise') - logger.info('Build LearningRateDecayOptimizerConstructor ' - f'{decay_type} {decay_rate} - {num_layers}') - weight_decay = self.base_wd - for name, param in module.named_parameters(): - if not param.requires_grad: - continue # frozen weights - if len(param.shape) == 1 or name.endswith('.bias') or name in ( - 'pos_embed', 'cls_token'): - group_name = 'no_decay' - this_weight_decay = 0. - else: - group_name = 'decay' - this_weight_decay = weight_decay - if 'layer_wise' in decay_type: - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_convnext( - name, self.paramwise_cfg.get('num_layers')) - logger.info(f'set param {name} as id {layer_id}') - elif 'BEiT' in module.backbone.__class__.__name__ or \ - 'MAE' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_vit(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - elif decay_type == 'stage_wise': - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_stage_id_for_convnext(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - group_name = f'layer_{layer_id}_{group_name}' - - if group_name not in parameter_groups: - scale = decay_rate**(num_layers - layer_id - 1) - - parameter_groups[group_name] = { - 'weight_decay': this_weight_decay, - 'params': [], - 'param_names': [], - 'lr_scale': scale, - 'group_name': group_name, - 'lr': scale * self.base_lr, - } - - parameter_groups[group_name]['params'].append(param) - parameter_groups[group_name]['param_names'].append(name) - rank, _ = get_dist_info() - if rank == 0: - to_display = {} - for key in parameter_groups: - to_display[key] = { - 'param_names': parameter_groups[key]['param_names'], - 'lr_scale': parameter_groups[key]['lr_scale'], - 'lr': parameter_groups[key]['lr'], - 'weight_decay': parameter_groups[key]['weight_decay'], - } - logger.info(f'Param groups = {json.dumps(to_display, indent=2)}') - params.extend(parameter_groups.values()) - - -@OPTIMIZER_BUILDERS.register_module() -class LayerDecayOptimizerConstructor(LearningRateDecayOptimizerConstructor): - """Different learning rates are set for different layers of backbone. - - Note: Currently, this optimizer constructor is built for BEiT, - and it will be deprecated. - Please use ``LearningRateDecayOptimizerConstructor`` instead. - """ - - def __init__(self, optimizer_cfg, paramwise_cfg): - warnings.warn('DeprecationWarning: Original ' - 'LayerDecayOptimizerConstructor of BEiT ' - 'will be deprecated. Please use ' - 'LearningRateDecayOptimizerConstructor instead, ' - 'and set decay_type = layer_wise_vit in paramwise_cfg.') - paramwise_cfg.update({'decay_type': 'layer_wise_vit'}) - warnings.warn('DeprecationWarning: Layer_decay_rate will ' - 'be deleted, please use decay_rate instead.') - paramwise_cfg['decay_rate'] = paramwise_cfg.pop('layer_decay_rate') - super(LayerDecayOptimizerConstructor, - self).__init__(optimizer_cfg, paramwise_cfg) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/__init__.py deleted file mode 100644 index 5206b96be..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_pixel_sampler -from .sampler import BasePixelSampler, OHEMPixelSampler - -__all__ = ['build_pixel_sampler', 'BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/builder.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/builder.py deleted file mode 100644 index 1cecd347b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/builder.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, build_from_cfg - -PIXEL_SAMPLERS = Registry('pixel sampler') - - -def build_pixel_sampler(cfg, **default_args): - """Build pixel sampler for segmentation map.""" - return build_from_cfg(cfg, PIXEL_SAMPLERS, default_args) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/sampler/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/sampler/__init__.py deleted file mode 100644 index 5a7648564..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/sampler/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_pixel_sampler import BasePixelSampler -from .ohem_pixel_sampler import OHEMPixelSampler - -__all__ = ['BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py deleted file mode 100644 index 03672cd47..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BasePixelSampler(metaclass=ABCMeta): - """Base class of pixel sampler.""" - - def __init__(self, **kwargs): - pass - - @abstractmethod - def sample(self, seg_logit, seg_label): - """Placeholder for sample function.""" diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py deleted file mode 100644 index 833a28768..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import PIXEL_SAMPLERS -from .base_pixel_sampler import BasePixelSampler - - -@PIXEL_SAMPLERS.register_module() -class OHEMPixelSampler(BasePixelSampler): - """Online Hard Example Mining Sampler for segmentation. - - Args: - context (nn.Module): The context of sampler, subclass of - :obj:`BaseDecodeHead`. - thresh (float, optional): The threshold for hard example selection. - Below which, are prediction with low confidence. If not - specified, the hard examples will be pixels of top ``min_kept`` - loss. Default: None. - min_kept (int, optional): The minimum number of predictions to keep. - Default: 100000. - """ - - def __init__(self, context, thresh=None, min_kept=100000): - super(OHEMPixelSampler, self).__init__() - self.context = context - assert min_kept > 1 - self.thresh = thresh - self.min_kept = min_kept - - def sample(self, seg_logit, seg_label): - """Sample pixels that have high loss or with low prediction confidence. - - Args: - seg_logit (torch.Tensor): segmentation logits, shape (N, C, H, W) - seg_label (torch.Tensor): segmentation label, shape (N, 1, H, W) - - Returns: - torch.Tensor: segmentation weight, shape (N, H, W) - """ - with torch.no_grad(): - assert seg_logit.shape[2:] == seg_label.shape[2:] - assert seg_label.shape[1] == 1 - seg_label = seg_label.squeeze(1).long() - batch_kept = self.min_kept * seg_label.size(0) - valid_mask = seg_label != self.context.ignore_index - seg_weight = seg_logit.new_zeros(size=seg_label.size()) - valid_seg_weight = seg_weight[valid_mask] - if self.thresh is not None: - seg_prob = F.softmax(seg_logit, dim=1) - - tmp_seg_label = seg_label.clone().unsqueeze(1) - tmp_seg_label[tmp_seg_label == self.context.ignore_index] = 0 - seg_prob = seg_prob.gather(1, tmp_seg_label).squeeze(1) - sort_prob, sort_indices = seg_prob[valid_mask].sort() - - if sort_prob.numel() > 0: - min_threshold = sort_prob[min(batch_kept, - sort_prob.numel() - 1)] - else: - min_threshold = 0.0 - threshold = max(min_threshold, self.thresh) - valid_seg_weight[seg_prob[valid_mask] < threshold] = 1. - else: - if not isinstance(self.context.loss_decode, nn.ModuleList): - losses_decode = [self.context.loss_decode] - else: - losses_decode = self.context.loss_decode - losses = 0.0 - for loss_module in losses_decode: - losses += loss_module( - seg_logit, - seg_label, - weight=None, - ignore_index=self.context.ignore_index, - reduction_override='none') - - # faster than topk according to https://github.com/pytorch/pytorch/issues/22812 # noqa - _, sort_indices = losses[valid_mask].sort(descending=True) - valid_seg_weight[sort_indices[:batch_kept]] = 1. - - seg_weight[valid_mask] = valid_seg_weight - - return seg_weight diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/utils/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/utils/__init__.py deleted file mode 100644 index 28882893a..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/utils/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .dist_util import check_dist_init, sync_random_seed -from .misc import add_prefix - -__all__ = ['add_prefix', 'check_dist_init', 'sync_random_seed'] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/utils/dist_util.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/utils/dist_util.py deleted file mode 100644 index b3288519d..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/utils/dist_util.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def check_dist_init(): - return dist.is_available() and dist.is_initialized() - - -def sync_random_seed(seed=None, device='cuda'): - """Make sure different ranks share the same seed. All workers must call - this function, otherwise it will deadlock. This method is generally used in - `DistributedSampler`, because the seed should be identical across all - processes in the distributed group. - - In distributed sampling, different ranks should sample non-overlapped - data in the dataset. Therefore, this function is used to make sure that - each rank shuffles the data indices in the same order based - on the same seed. Then different ranks could use different indices - to select non-overlapped data from the same data list. - - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - - if seed is None: - seed = np.random.randint(2**31) - assert isinstance(seed, int) - - rank, world_size = get_dist_info() - - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/utils/misc.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/utils/misc.py deleted file mode 100644 index 282bb8d96..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/core/utils/misc.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def add_prefix(inputs, prefix): - """Add prefix for dict. - - Args: - inputs (dict): The input dict with str keys. - prefix (str): The prefix to add. - - Returns: - - dict: The dict with keys updated with ``prefix``. - """ - - outputs = dict() - for name, value in inputs.items(): - outputs[f'{prefix}.{name}'] = value - - return outputs diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/__init__.py deleted file mode 100644 index 3366f0aec..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .ade import ADE20KDataset -from .builder import DATASETS, PIPELINES, build_dataloader, build_dataset -from .cityscapes import CityscapesDataset -from .coco_stuff import COCOStuffDataset -from .custom import CustomDataset -from .dataset_wrappers import (ConcatDataset, MultiImageMixDataset, - RepeatDataset) -from .pascal_context import PascalContextDataset, PascalContextDataset59 -from .voc import PascalVOCDataset diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/ade.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/ade.py deleted file mode 100644 index db94cebd3..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/ade.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np -from PIL import Image - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class ADE20KDataset(CustomDataset): - """ADE20K dataset. - - In segmentation map annotation for ADE20K, 0 stands for background, which - is not included in 150 categories. ``reduce_zero_label`` is fixed to True. - The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is fixed to - '.png'. - """ - CLASSES = ( - 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', - 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', - 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', - 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', - 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', - 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', - 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', - 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', - 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', - 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', - 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', - 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', - 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', - 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', - 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', - 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', - 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', - 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', - 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', - 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', - 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', - 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', - 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', - 'clock', 'flag') - - PALETTE = [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], - [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], - [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], - [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], - [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], - [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], - [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], - [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], - [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], - [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], - [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], - [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], - [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], - [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], - [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], - [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], - [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], - [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], - [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], - [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], - [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], - [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], - [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], - [102, 255, 0], [92, 0, 255]] - - def __init__(self, **kwargs): - super(ADE20KDataset, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - reduce_zero_label=True, - **kwargs) - - def results2img(self, results, imgfile_prefix, to_label_id, indices=None): - """Write the segmentation results to images. - - Args: - results (list[ndarray]): Testing results of the - dataset. - imgfile_prefix (str): The filename prefix of the png files. - If the prefix is "somepath/xxx", - the png files will be named "somepath/xxx.png". - to_label_id (bool): whether convert output to label_id for - submission. - indices (list[int], optional): Indices of input results, if not - set, all the indices of the dataset will be used. - Default: None. - - Returns: - list[str: str]: result txt files which contains corresponding - semantic segmentation images. - """ - if indices is None: - indices = list(range(len(self))) - - mmcv.mkdir_or_exist(imgfile_prefix) - result_files = [] - for result, idx in zip(results, indices): - - filename = self.img_infos[idx]['filename'] - basename = osp.splitext(osp.basename(filename))[0] - - png_filename = osp.join(imgfile_prefix, f'{basename}.png') - - # The index range of official requirement is from 0 to 150. - # But the index range of output is from 0 to 149. - # That is because we set reduce_zero_label=True. - result = result + 1 - - output = Image.fromarray(result.astype(np.uint8)) - output.save(png_filename) - result_files.append(png_filename) - - return result_files - - def format_results(self, - results, - imgfile_prefix, - to_label_id=True, - indices=None): - """Format the results into dir (standard format for ade20k evaluation). - - Args: - results (list): Testing results of the dataset. - imgfile_prefix (str | None): The prefix of images files. It - includes the file path and the prefix of filename, e.g., - "a/b/prefix". - to_label_id (bool): whether convert output to label_id for - submission. Default: False - indices (list[int], optional): Indices of input results, if not - set, all the indices of the dataset will be used. - Default: None. - - Returns: - tuple: (result_files, tmp_dir), result_files is a list containing - the image paths, tmp_dir is the temporal directory created - for saving json/png files when img_prefix is not specified. - """ - - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - result_files = self.results2img(results, imgfile_prefix, to_label_id, - indices) - return result_files diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/builder.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/builder.py deleted file mode 100644 index 4d852d365..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/builder.py +++ /dev/null @@ -1,191 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import platform -import random -from functools import partial - -import numpy as np -import torch -from mmcv.parallel import collate -from mmcv.runner import get_dist_info -from mmcv.utils import Registry, build_from_cfg, digit_version -from torch.utils.data import DataLoader - -from .samplers import DistributedSampler - -if platform.system() != 'Windows': - # https://github.com/pytorch/pytorch/issues/973 - import resource - rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) - base_soft_limit = rlimit[0] - hard_limit = rlimit[1] - soft_limit = min(max(4096, base_soft_limit), hard_limit) - resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) - -DATASETS = Registry('dataset') -PIPELINES = Registry('pipeline') - - -def _concat_dataset(cfg, default_args=None): - """Build :obj:`ConcatDataset by.""" - from .dataset_wrappers import ConcatDataset - img_dir = cfg['img_dir'] - ann_dir = cfg.get('ann_dir', None) - split = cfg.get('split', None) - # pop 'separate_eval' since it is not a valid key for common datasets. - separate_eval = cfg.pop('separate_eval', True) - num_img_dir = len(img_dir) if isinstance(img_dir, (list, tuple)) else 1 - if ann_dir is not None: - num_ann_dir = len(ann_dir) if isinstance(ann_dir, (list, tuple)) else 1 - else: - num_ann_dir = 0 - if split is not None: - num_split = len(split) if isinstance(split, (list, tuple)) else 1 - else: - num_split = 0 - if num_img_dir > 1: - assert num_img_dir == num_ann_dir or num_ann_dir == 0 - assert num_img_dir == num_split or num_split == 0 - else: - assert num_split == num_ann_dir or num_ann_dir <= 1 - num_dset = max(num_split, num_img_dir) - - datasets = [] - for i in range(num_dset): - data_cfg = copy.deepcopy(cfg) - if isinstance(img_dir, (list, tuple)): - data_cfg['img_dir'] = img_dir[i] - if isinstance(ann_dir, (list, tuple)): - data_cfg['ann_dir'] = ann_dir[i] - if isinstance(split, (list, tuple)): - data_cfg['split'] = split[i] - datasets.append(build_dataset(data_cfg, default_args)) - - return ConcatDataset(datasets, separate_eval) - - -def build_dataset(cfg, default_args=None): - """Build datasets.""" - from .dataset_wrappers import (ConcatDataset, MultiImageMixDataset, - RepeatDataset) - if isinstance(cfg, (list, tuple)): - dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) - elif cfg['type'] == 'RepeatDataset': - dataset = RepeatDataset( - build_dataset(cfg['dataset'], default_args), cfg['times']) - elif cfg['type'] == 'MultiImageMixDataset': - cp_cfg = copy.deepcopy(cfg) - cp_cfg['dataset'] = build_dataset(cp_cfg['dataset']) - cp_cfg.pop('type') - dataset = MultiImageMixDataset(**cp_cfg) - elif isinstance(cfg.get('img_dir'), (list, tuple)) or isinstance( - cfg.get('split', None), (list, tuple)): - dataset = _concat_dataset(cfg, default_args) - else: - dataset = build_from_cfg(cfg, DATASETS, default_args) - - return dataset - - -def build_dataloader(dataset, - samples_per_gpu, - workers_per_gpu, - num_gpus=1, - dist=True, - shuffle=True, - seed=None, - drop_last=False, - pin_memory=True, - persistent_workers=True, - **kwargs): - """Build PyTorch DataLoader. - - In distributed training, each GPU/process has a dataloader. - In non-distributed training, there is only one dataloader for all GPUs. - - Args: - dataset (Dataset): A PyTorch dataset. - samples_per_gpu (int): Number of training samples on each GPU, i.e., - batch size of each GPU. - workers_per_gpu (int): How many subprocesses to use for data loading - for each GPU. - num_gpus (int): Number of GPUs. Only used in non-distributed training. - dist (bool): Distributed training/test or not. Default: True. - shuffle (bool): Whether to shuffle the data at every epoch. - Default: True. - seed (int | None): Seed to be used. Default: None. - drop_last (bool): Whether to drop the last incomplete batch in epoch. - Default: False - pin_memory (bool): Whether to use pin_memory in DataLoader. - Default: True - persistent_workers (bool): If True, the data loader will not shutdown - the worker processes after a dataset has been consumed once. - This allows to maintain the workers Dataset instances alive. - The argument also has effect in PyTorch>=1.7.0. - Default: True - kwargs: any keyword argument to be used to initialize DataLoader - - Returns: - DataLoader: A PyTorch dataloader. - """ - rank, world_size = get_dist_info() - if dist: - sampler = DistributedSampler( - dataset, world_size, rank, shuffle=shuffle, seed=seed) - shuffle = False - batch_size = samples_per_gpu - num_workers = workers_per_gpu - else: - sampler = None - batch_size = num_gpus * samples_per_gpu - num_workers = num_gpus * workers_per_gpu - - init_fn = partial( - worker_init_fn, num_workers=num_workers, rank=rank, - seed=seed) if seed is not None else None - - if digit_version(torch.__version__) >= digit_version('1.8.0'): - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - persistent_workers=persistent_workers, - **kwargs) - else: - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - **kwargs) - - return data_loader - - -def worker_init_fn(worker_id, num_workers, rank, seed): - """Worker init func for dataloader. - - The seed of each worker equals to num_worker * rank + worker_id + user_seed - - Args: - worker_id (int): Worker id. - num_workers (int): Number of workers. - rank (int): The rank of current process. - seed (int): The random seed to use. - """ - - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/cityscapes.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/cityscapes.py deleted file mode 100644 index ed633d00d..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/cityscapes.py +++ /dev/null @@ -1,214 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np -from mmcv.utils import print_log -from PIL import Image - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class CityscapesDataset(CustomDataset): - """Cityscapes dataset. - - The ``img_suffix`` is fixed to '_leftImg8bit.png' and ``seg_map_suffix`` is - fixed to '_gtFine_labelTrainIds.png' for Cityscapes dataset. - """ - - CLASSES = ('road', 'sidewalk', 'building', 'wall', 'fence', 'pole', - 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle') - - PALETTE = [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], - [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], - [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], - [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], - [0, 80, 100], [0, 0, 230], [119, 11, 32]] - - def __init__(self, - img_suffix='_leftImg8bit.png', - seg_map_suffix='_gtFine_labelTrainIds.png', - **kwargs): - super(CityscapesDataset, self).__init__( - img_suffix=img_suffix, seg_map_suffix=seg_map_suffix, **kwargs) - - @staticmethod - def _convert_to_label_id(result): - """Convert trainId to id for cityscapes.""" - if isinstance(result, str): - result = np.load(result) - import cityscapesscripts.helpers.labels as CSLabels - result_copy = result.copy() - for trainId, label in CSLabels.trainId2label.items(): - result_copy[result == trainId] = label.id - - return result_copy - - def results2img(self, results, imgfile_prefix, to_label_id, indices=None): - """Write the segmentation results to images. - - Args: - results (list[ndarray]): Testing results of the - dataset. - imgfile_prefix (str): The filename prefix of the png files. - If the prefix is "somepath/xxx", - the png files will be named "somepath/xxx.png". - to_label_id (bool): whether convert output to label_id for - submission. - indices (list[int], optional): Indices of input results, - if not set, all the indices of the dataset will be used. - Default: None. - - Returns: - list[str: str]: result txt files which contains corresponding - semantic segmentation images. - """ - if indices is None: - indices = list(range(len(self))) - - mmcv.mkdir_or_exist(imgfile_prefix) - result_files = [] - for result, idx in zip(results, indices): - if to_label_id: - result = self._convert_to_label_id(result) - filename = self.img_infos[idx]['filename'] - basename = osp.splitext(osp.basename(filename))[0] - - png_filename = osp.join(imgfile_prefix, f'{basename}.png') - - output = Image.fromarray(result.astype(np.uint8)).convert('P') - import cityscapesscripts.helpers.labels as CSLabels - palette = np.zeros((len(CSLabels.id2label), 3), dtype=np.uint8) - for label_id, label in CSLabels.id2label.items(): - palette[label_id] = label.color - - output.putpalette(palette) - output.save(png_filename) - result_files.append(png_filename) - - return result_files - - def format_results(self, - results, - imgfile_prefix, - to_label_id=True, - indices=None): - """Format the results into dir (standard format for Cityscapes - evaluation). - - Args: - results (list): Testing results of the dataset. - imgfile_prefix (str): The prefix of images files. It - includes the file path and the prefix of filename, e.g., - "a/b/prefix". - to_label_id (bool): whether convert output to label_id for - submission. Default: False - indices (list[int], optional): Indices of input results, - if not set, all the indices of the dataset will be used. - Default: None. - - Returns: - tuple: (result_files, tmp_dir), result_files is a list containing - the image paths, tmp_dir is the temporal directory created - for saving json/png files when img_prefix is not specified. - """ - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - result_files = self.results2img(results, imgfile_prefix, to_label_id, - indices) - - return result_files - - def evaluate(self, - results, - metric='mIoU', - logger=None, - imgfile_prefix=None): - """Evaluation in Cityscapes/default protocol. - - Args: - results (list): Testing results of the dataset. - metric (str | list[str]): Metrics to be evaluated. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - imgfile_prefix (str | None): The prefix of output image file, - for cityscapes evaluation only. It includes the file path and - the prefix of filename, e.g., "a/b/prefix". - If results are evaluated with cityscapes protocol, it would be - the prefix of output png files. The output files would be - png images under folder "a/b/prefix/xxx.png", where "xxx" is - the image name of cityscapes. If not specified, a temp file - will be created for evaluation. - Default: None. - - Returns: - dict[str, float]: Cityscapes/default metrics. - """ - - eval_results = dict() - metrics = metric.copy() if isinstance(metric, list) else [metric] - if 'cityscapes' in metrics: - eval_results.update( - self._evaluate_cityscapes(results, logger, imgfile_prefix)) - metrics.remove('cityscapes') - if len(metrics) > 0: - eval_results.update( - super(CityscapesDataset, - self).evaluate(results, metrics, logger)) - - return eval_results - - def _evaluate_cityscapes(self, results, logger, imgfile_prefix): - """Evaluation in Cityscapes protocol. - - Args: - results (list): Testing results of the dataset. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - imgfile_prefix (str | None): The prefix of output image file - - Returns: - dict[str: float]: Cityscapes evaluation results. - """ - try: - import cityscapesscripts.evaluation.evalPixelLevelSemanticLabeling as CSEval # noqa - except ImportError: - raise ImportError('Please run "pip install cityscapesscripts" to ' - 'install cityscapesscripts first.') - msg = 'Evaluating in Cityscapes style' - if logger is None: - msg = '\n' + msg - print_log(msg, logger=logger) - - result_dir = imgfile_prefix - - eval_results = dict() - print_log(f'Evaluating results under {result_dir} ...', logger=logger) - - CSEval.args.evalInstLevelScore = True - CSEval.args.predictionPath = osp.abspath(result_dir) - CSEval.args.evalPixelAccuracy = True - CSEval.args.JSONOutput = False - - seg_map_list = [] - pred_list = [] - - # when evaluating with official cityscapesscripts, - # **_gtFine_labelIds.png is used - for seg_map in mmcv.scandir( - self.ann_dir, 'gtFine_labelIds.png', recursive=True): - seg_map_list.append(osp.join(self.ann_dir, seg_map)) - pred_list.append(CSEval.getPrediction(CSEval.args, seg_map)) - - eval_results.update( - CSEval.evaluateImgLists(pred_list, seg_map_list, CSEval.args)) - - return eval_results diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/coco_stuff.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/coco_stuff.py deleted file mode 100644 index 24d089556..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/coco_stuff.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class COCOStuffDataset(CustomDataset): - """COCO-Stuff dataset. - - In segmentation map annotation for COCO-Stuff, Train-IDs of the 10k version - are from 1 to 171, where 0 is the ignore index, and Train-ID of COCO Stuff - 164k is from 0 to 170, where 255 is the ignore index. So, they are all 171 - semantic categories. ``reduce_zero_label`` is set to True and False for the - 10k and 164k versions, respectively. The ``img_suffix`` is fixed to '.jpg', - and ``seg_map_suffix`` is fixed to '.png'. - """ - CLASSES = ( - 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', - 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', - 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', - 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', - 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', - 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', - 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', - 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', - 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', - 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', - 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', - 'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner', - 'blanket', 'branch', 'bridge', 'building-other', 'bush', 'cabinet', - 'cage', 'cardboard', 'carpet', 'ceiling-other', 'ceiling-tile', - 'cloth', 'clothes', 'clouds', 'counter', 'cupboard', 'curtain', - 'desk-stuff', 'dirt', 'door-stuff', 'fence', 'floor-marble', - 'floor-other', 'floor-stone', 'floor-tile', 'floor-wood', - 'flower', 'fog', 'food-other', 'fruit', 'furniture-other', 'grass', - 'gravel', 'ground-other', 'hill', 'house', 'leaves', 'light', 'mat', - 'metal', 'mirror-stuff', 'moss', 'mountain', 'mud', 'napkin', 'net', - 'paper', 'pavement', 'pillow', 'plant-other', 'plastic', 'platform', - 'playingfield', 'railing', 'railroad', 'river', 'road', 'rock', 'roof', - 'rug', 'salad', 'sand', 'sea', 'shelf', 'sky-other', 'skyscraper', - 'snow', 'solid-other', 'stairs', 'stone', 'straw', 'structural-other', - 'table', 'tent', 'textile-other', 'towel', 'tree', 'vegetable', - 'wall-brick', 'wall-concrete', 'wall-other', 'wall-panel', - 'wall-stone', 'wall-tile', 'wall-wood', 'water-other', 'waterdrops', - 'window-blind', 'window-other', 'wood') - - PALETTE = [[0, 192, 64], [0, 192, 64], [0, 64, 96], [128, 192, 192], - [0, 64, 64], [0, 192, 224], [0, 192, 192], [128, 192, 64], - [0, 192, 96], [128, 192, 64], [128, 32, 192], [0, 0, 224], - [0, 0, 64], [0, 160, 192], [128, 0, 96], [128, 0, 192], - [0, 32, 192], [128, 128, 224], [0, 0, 192], [128, 160, 192], - [128, 128, 0], [128, 0, 32], [128, 32, 0], [128, 0, 128], - [64, 128, 32], [0, 160, 0], [0, 0, 0], [192, 128, 160], - [0, 32, 0], [0, 128, 128], [64, 128, 160], [128, 160, 0], - [0, 128, 0], [192, 128, 32], [128, 96, 128], [0, 0, 128], - [64, 0, 32], [0, 224, 128], [128, 0, 0], [192, 0, 160], - [0, 96, 128], [128, 128, 128], [64, 0, 160], [128, 224, 128], - [128, 128, 64], [192, 0, 32], [128, 96, 0], [128, 0, 192], - [0, 128, 32], [64, 224, 0], [0, 0, 64], [128, 128, 160], - [64, 96, 0], [0, 128, 192], [0, 128, 160], [192, 224, 0], - [0, 128, 64], [128, 128, 32], [192, 32, 128], [0, 64, 192], - [0, 0, 32], [64, 160, 128], [128, 64, 64], [128, 0, 160], - [64, 32, 128], [128, 192, 192], [0, 0, 160], [192, 160, 128], - [128, 192, 0], [128, 0, 96], [192, 32, 0], [128, 64, 128], - [64, 128, 96], [64, 160, 0], [0, 64, 0], [192, 128, 224], - [64, 32, 0], [0, 192, 128], [64, 128, 224], [192, 160, 0], - [0, 192, 0], [192, 128, 96], [192, 96, 128], [0, 64, 128], - [64, 0, 96], [64, 224, 128], [128, 64, 0], [192, 0, 224], - [64, 96, 128], [128, 192, 128], [64, 0, 224], [192, 224, 128], - [128, 192, 64], [192, 0, 96], [192, 96, 0], [128, 64, 192], - [0, 128, 96], [0, 224, 0], [64, 64, 64], [128, 128, 224], - [0, 96, 0], [64, 192, 192], [0, 128, 224], [128, 224, 0], - [64, 192, 64], [128, 128, 96], [128, 32, 128], [64, 0, 192], - [0, 64, 96], [0, 160, 128], [192, 0, 64], [128, 64, 224], - [0, 32, 128], [192, 128, 192], [0, 64, 224], [128, 160, 128], - [192, 128, 0], [128, 64, 32], [128, 32, 64], [192, 0, 128], - [64, 192, 32], [0, 160, 64], [64, 0, 0], [192, 192, 160], - [0, 32, 64], [64, 128, 128], [64, 192, 160], [128, 160, 64], - [64, 128, 0], [192, 192, 32], [128, 96, 192], [64, 0, 128], - [64, 64, 32], [0, 224, 192], [192, 0, 0], [192, 64, 160], - [0, 96, 192], [192, 128, 128], [64, 64, 160], [128, 224, 192], - [192, 128, 64], [192, 64, 32], [128, 96, 64], [192, 0, 192], - [0, 192, 32], [64, 224, 64], [64, 0, 64], [128, 192, 160], - [64, 96, 64], [64, 128, 192], [0, 192, 160], [192, 224, 64], - [64, 128, 64], [128, 192, 32], [192, 32, 192], [64, 64, 192], - [0, 64, 32], [64, 160, 192], [192, 64, 64], [128, 64, 160], - [64, 32, 192], [192, 192, 192], [0, 64, 160], [192, 160, 192], - [192, 192, 0], [128, 64, 96], [192, 32, 64], [192, 64, 128], - [64, 192, 96], [64, 160, 64], [64, 64, 0]] - - def __init__(self, **kwargs): - super(COCOStuffDataset, self).__init__( - img_suffix='.jpg', seg_map_suffix='_labelTrainIds.png', **kwargs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/custom.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/custom.py deleted file mode 100644 index 4615d4114..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/custom.py +++ /dev/null @@ -1,487 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from collections import OrderedDict - -import mmcv -import numpy as np -from mmcv.utils import print_log -from prettytable import PrettyTable -from torch.utils.data import Dataset - -from mmseg.core import eval_metrics, intersect_and_union, pre_eval_to_metrics -from mmseg.utils import get_root_logger -from .builder import DATASETS -from .pipelines import Compose, LoadAnnotations - - -@DATASETS.register_module() -class CustomDataset(Dataset): - """Custom dataset for semantic segmentation. An example of file structure - is as followed. - - .. code-block:: none - - ├── data - │ ├── my_dataset - │ │ ├── img_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{img_suffix} - │ │ │ │ ├── yyy{img_suffix} - │ │ │ │ ├── zzz{img_suffix} - │ │ │ ├── val - │ │ ├── ann_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{seg_map_suffix} - │ │ │ │ ├── yyy{seg_map_suffix} - │ │ │ │ ├── zzz{seg_map_suffix} - │ │ │ ├── val - - The img/gt_semantic_seg pair of CustomDataset should be of the same - except suffix. A valid img/gt_semantic_seg filename pair should be like - ``xxx{img_suffix}`` and ``xxx{seg_map_suffix}`` (extension is also included - in the suffix). If split is given, then ``xxx`` is specified in txt file. - Otherwise, all files in ``img_dir/``and ``ann_dir`` will be loaded. - Please refer to ``docs/en/tutorials/new_dataset.md`` for more details. - - - Args: - pipeline (list[dict]): Processing pipeline - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. Default: '.jpg' - ann_dir (str, optional): Path to annotation directory. Default: None - seg_map_suffix (str): Suffix of segmentation maps. Default: '.png' - split (str, optional): Split txt file. If split is specified, only - file with suffix in the splits will be loaded. Otherwise, all - images in img_dir/ann_dir will be loaded. Default: None - data_root (str, optional): Data root for img_dir/ann_dir. Default: - None. - test_mode (bool): If test_mode=True, gt wouldn't be loaded. - ignore_index (int): The label index to be ignored. Default: 255 - reduce_zero_label (bool): Whether to mark label zero as ignored. - Default: False - classes (str | Sequence[str], optional): Specify classes to load. - If is None, ``cls.CLASSES`` will be used. Default: None. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, and - self.PALETTE is None, random palette will be generated. - Default: None - gt_seg_map_loader_cfg (dict, optional): build LoadAnnotations to - load gt for evaluation, load from disk by default. Default: None. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - CLASSES = None - - PALETTE = None - - def __init__(self, - pipeline, - img_dir, - img_suffix='.jpg', - ann_dir=None, - seg_map_suffix='.png', - split=None, - data_root=None, - test_mode=False, - ignore_index=255, - reduce_zero_label=False, - classes=None, - palette=None, - gt_seg_map_loader_cfg=None, - file_client_args=dict(backend='disk')): - self.pipeline = Compose(pipeline) - self.img_dir = img_dir - self.img_suffix = img_suffix - self.ann_dir = ann_dir - self.seg_map_suffix = seg_map_suffix - self.split = split - self.data_root = data_root - self.test_mode = test_mode - self.ignore_index = ignore_index - self.reduce_zero_label = reduce_zero_label - self.label_map = None - self.CLASSES, self.PALETTE = self.get_classes_and_palette( - classes, palette) - self.gt_seg_map_loader = LoadAnnotations( - ) if gt_seg_map_loader_cfg is None else LoadAnnotations( - **gt_seg_map_loader_cfg) - - self.file_client_args = file_client_args - self.file_client = mmcv.FileClient.infer_client(self.file_client_args) - - if test_mode: - assert self.CLASSES is not None, \ - '`cls.CLASSES` or `classes` should be specified when testing' - - # join paths if data_root is specified - if self.data_root is not None: - if not osp.isabs(self.img_dir): - self.img_dir = osp.join(self.data_root, self.img_dir) - if not (self.ann_dir is None or osp.isabs(self.ann_dir)): - self.ann_dir = osp.join(self.data_root, self.ann_dir) - if not (self.split is None or osp.isabs(self.split)): - self.split = osp.join(self.data_root, self.split) - - # load annotations - self.img_infos = self.load_annotations(self.img_dir, self.img_suffix, - self.ann_dir, - self.seg_map_suffix, self.split) - - def __len__(self): - """Total number of samples of data.""" - return len(self.img_infos) - - def load_annotations(self, img_dir, img_suffix, ann_dir, seg_map_suffix, - split): - """Load annotation from directory. - - Args: - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. - ann_dir (str|None): Path to annotation directory. - seg_map_suffix (str|None): Suffix of segmentation maps. - split (str|None): Split txt file. If split is specified, only file - with suffix in the splits will be loaded. Otherwise, all images - in img_dir/ann_dir will be loaded. Default: None - - Returns: - list[dict]: All image info of dataset. - """ - - img_infos = [] - if split is not None: - lines = mmcv.list_from_file( - split, file_client_args=self.file_client_args) - for line in lines: - img_name = line.strip() - img_info = dict(filename=img_name + img_suffix) - if ann_dir is not None: - seg_map = img_name + seg_map_suffix - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - else: - for img in self.file_client.list_dir_or_file( - dir_path=img_dir, - list_dir=False, - suffix=img_suffix, - recursive=True): - img_info = dict(filename=img) - if ann_dir is not None: - seg_map = img.replace(img_suffix, seg_map_suffix) - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - img_infos = sorted(img_infos, key=lambda x: x['filename']) - - print_log(f'Loaded {len(img_infos)} images', logger=get_root_logger()) - return img_infos - - def get_ann_info(self, idx): - """Get annotation by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - return self.img_infos[idx]['ann'] - - def pre_pipeline(self, results): - """Prepare results dict for pipeline.""" - results['seg_fields'] = [] - results['img_prefix'] = self.img_dir - results['seg_prefix'] = self.ann_dir - if self.custom_classes: - results['label_map'] = self.label_map - - def __getitem__(self, idx): - """Get training/test data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training/test data (with annotation if `test_mode` is set - False). - """ - - if self.test_mode: - return self.prepare_test_img(idx) - else: - return self.prepare_train_img(idx) - - def prepare_train_img(self, idx): - """Get training data and annotations after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training data and annotation after pipeline with new keys - introduced by pipeline. - """ - - img_info = self.img_infos[idx] - ann_info = self.get_ann_info(idx) - results = dict(img_info=img_info, ann_info=ann_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def prepare_test_img(self, idx): - """Get testing data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Testing data after pipeline with new keys introduced by - pipeline. - """ - - img_info = self.img_infos[idx] - results = dict(img_info=img_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """Place holder to format result to dataset specific output.""" - raise NotImplementedError - - def get_gt_seg_map_by_idx(self, index): - """Get one ground truth segmentation map for evaluation.""" - ann_info = self.get_ann_info(index) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - return results['gt_semantic_seg'] - - def get_gt_seg_maps(self, efficient_test=None): - """Get ground truth segmentation maps for evaluation.""" - if efficient_test is not None: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` has been deprecated ' - 'since MMSeg v0.16, the ``get_gt_seg_maps()`` is CPU memory ' - 'friendly by default. ') - - for idx in range(len(self)): - ann_info = self.get_ann_info(idx) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - yield results['gt_semantic_seg'] - - def pre_eval(self, preds, indices): - """Collect eval result from each iteration. - - Args: - preds (list[torch.Tensor] | torch.Tensor): the segmentation logit - after argmax, shape (N, H, W). - indices (list[int] | int): the prediction related ground truth - indices. - - Returns: - list[torch.Tensor]: (area_intersect, area_union, area_prediction, - area_ground_truth). - """ - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - - pre_eval_results = [] - - for pred, index in zip(preds, indices): - seg_map = self.get_gt_seg_map_by_idx(index) - pre_eval_results.append( - intersect_and_union( - pred, - seg_map, - len(self.CLASSES), - self.ignore_index, - # as the labels has been converted when dataset initialized - # in `get_palette_for_custom_classes ` this `label_map` - # should be `dict()`, see - # https://github.com/open-mmlab/mmsegmentation/issues/1415 - # for more ditails - label_map=dict(), - reduce_zero_label=self.reduce_zero_label)) - - return pre_eval_results - - def get_classes_and_palette(self, classes=None, palette=None): - """Get class names of current dataset. - - Args: - classes (Sequence[str] | str | None): If classes is None, use - default CLASSES defined by builtin dataset. If classes is a - string, take it as a file name. The file contains the name of - classes where each line contains one class name. If classes is - a tuple or list, override the CLASSES defined by the dataset. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, random - palette will be generated. Default: None - """ - if classes is None: - self.custom_classes = False - return self.CLASSES, self.PALETTE - - self.custom_classes = True - if isinstance(classes, str): - # take it as a file path - class_names = mmcv.list_from_file(classes) - elif isinstance(classes, (tuple, list)): - class_names = classes - else: - raise ValueError(f'Unsupported type {type(classes)} of classes.') - - if self.CLASSES: - if not set(class_names).issubset(self.CLASSES): - raise ValueError('classes is not a subset of CLASSES.') - - # dictionary, its keys are the old label ids and its values - # are the new label ids. - # used for changing pixel labels in load_annotations. - self.label_map = {} - for i, c in enumerate(self.CLASSES): - if c not in class_names: - self.label_map[i] = -1 - else: - self.label_map[i] = class_names.index(c) - - palette = self.get_palette_for_custom_classes(class_names, palette) - - return class_names, palette - - def get_palette_for_custom_classes(self, class_names, palette=None): - - if self.label_map is not None: - # return subset of palette - palette = [] - for old_id, new_id in sorted( - self.label_map.items(), key=lambda x: x[1]): - if new_id != -1: - palette.append(self.PALETTE[old_id]) - palette = type(self.PALETTE)(palette) - - elif palette is None: - if self.PALETTE is None: - # Get random state before set seed, and restore - # random state later. - # It will prevent loss of randomness, as the palette - # may be different in each iteration if not specified. - # See: https://github.com/open-mmlab/mmdetection/issues/5844 - state = np.random.get_state() - np.random.seed(42) - # random palette - palette = np.random.randint(0, 255, size=(len(class_names), 3)) - np.random.set_state(state) - else: - palette = self.PALETTE - - return palette - - def evaluate(self, - results, - metric='mIoU', - logger=None, - gt_seg_maps=None, - **kwargs): - """Evaluate the dataset. - - Args: - results (list[tuple[torch.Tensor]] | list[str]): per image pre_eval - results or predict segmentation map for computing evaluation - metric. - metric (str | list[str]): Metrics to be evaluated. 'mIoU', - 'mDice' and 'mFscore' are supported. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - gt_seg_maps (generator[ndarray]): Custom gt seg maps as input, - used in ConcatDataset - - Returns: - dict[str, float]: Default metrics. - """ - if isinstance(metric, str): - metric = [metric] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metric).issubset(set(allowed_metrics)): - raise KeyError('metric {} is not supported'.format(metric)) - - eval_results = {} - # test a list of files - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - if gt_seg_maps is None: - gt_seg_maps = self.get_gt_seg_maps() - num_classes = len(self.CLASSES) - ret_metrics = eval_metrics( - results, - gt_seg_maps, - num_classes, - self.ignore_index, - metric, - label_map=dict(), - reduce_zero_label=self.reduce_zero_label) - # test a list of pre_eval_results - else: - ret_metrics = pre_eval_to_metrics(results, metric) - - # Because dataset.CLASSES is required for per-eval. - if self.CLASSES is None: - class_names = tuple(range(num_classes)) - else: - class_names = self.CLASSES - - # summary table - ret_metrics_summary = OrderedDict({ - ret_metric: np.round(np.nanmean(ret_metric_value) * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - - # each class table - ret_metrics.pop('aAcc', None) - ret_metrics_class = OrderedDict({ - ret_metric: np.round(ret_metric_value * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - ret_metrics_class.update({'Class': class_names}) - ret_metrics_class.move_to_end('Class', last=False) - - # for logger - class_table_data = PrettyTable() - for key, val in ret_metrics_class.items(): - class_table_data.add_column(key, val) - - summary_table_data = PrettyTable() - for key, val in ret_metrics_summary.items(): - if key == 'aAcc': - summary_table_data.add_column(key, [val]) - else: - summary_table_data.add_column('m' + key, [val]) - - print_log('per class results:', logger) - print_log('\n' + class_table_data.get_string(), logger=logger) - print_log('Summary:', logger) - print_log('\n' + summary_table_data.get_string(), logger=logger) - - # each metric dict - for key, value in ret_metrics_summary.items(): - if key == 'aAcc': - eval_results[key] = value / 100.0 - else: - eval_results['m' + key] = value / 100.0 - - ret_metrics_class.pop('Class', None) - for key, value in ret_metrics_class.items(): - eval_results.update({ - key + '.' + str(name): value[idx] / 100.0 - for idx, name in enumerate(class_names) - }) - - return eval_results diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/dataset_wrappers.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/dataset_wrappers.py deleted file mode 100644 index 1fb089f9f..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/dataset_wrappers.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import bisect -import collections -import copy -from itertools import chain - -import mmcv -import numpy as np -from mmcv.utils import build_from_cfg, print_log -from torch.utils.data.dataset import ConcatDataset as _ConcatDataset - -from .builder import DATASETS, PIPELINES -from .cityscapes import CityscapesDataset - - -@DATASETS.register_module() -class ConcatDataset(_ConcatDataset): - """A wrapper of concatenated dataset. - - Same as :obj:`torch.utils.data.dataset.ConcatDataset`, but - support evaluation and formatting results - - Args: - datasets (list[:obj:`Dataset`]): A list of datasets. - separate_eval (bool): Whether to evaluate the concatenated - dataset results separately, Defaults to True. - """ - - def __init__(self, datasets, separate_eval=True): - super(ConcatDataset, self).__init__(datasets) - self.CLASSES = datasets[0].CLASSES - self.PALETTE = datasets[0].PALETTE - self.separate_eval = separate_eval - assert separate_eval in [True, False], \ - f'separate_eval can only be True or False,' \ - f'but get {separate_eval}' - if any([isinstance(ds, CityscapesDataset) for ds in datasets]): - raise NotImplementedError( - 'Evaluating ConcatDataset containing CityscapesDataset' - 'is not supported!') - - def evaluate(self, results, logger=None, **kwargs): - """Evaluate the results. - - Args: - results (list[tuple[torch.Tensor]] | list[str]]): per image - pre_eval results or predict segmentation map for - computing evaluation metric. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - - Returns: - dict[str: float]: evaluate results of the total dataset - or each separate - dataset if `self.separate_eval=True`. - """ - assert len(results) == self.cumulative_sizes[-1], \ - ('Dataset and results have different sizes: ' - f'{self.cumulative_sizes[-1]} v.s. {len(results)}') - - # Check whether all the datasets support evaluation - for dataset in self.datasets: - assert hasattr(dataset, 'evaluate'), \ - f'{type(dataset)} does not implement evaluate function' - - if self.separate_eval: - dataset_idx = -1 - total_eval_results = dict() - for size, dataset in zip(self.cumulative_sizes, self.datasets): - start_idx = 0 if dataset_idx == -1 else \ - self.cumulative_sizes[dataset_idx] - end_idx = self.cumulative_sizes[dataset_idx + 1] - - results_per_dataset = results[start_idx:end_idx] - print_log( - f'\nEvaluateing {dataset.img_dir} with ' - f'{len(results_per_dataset)} images now', - logger=logger) - - eval_results_per_dataset = dataset.evaluate( - results_per_dataset, logger=logger, **kwargs) - dataset_idx += 1 - for k, v in eval_results_per_dataset.items(): - total_eval_results.update({f'{dataset_idx}_{k}': v}) - - return total_eval_results - - if len(set([type(ds) for ds in self.datasets])) != 1: - raise NotImplementedError( - 'All the datasets should have same types when ' - 'self.separate_eval=False') - else: - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - # merge the generators of gt_seg_maps - gt_seg_maps = chain( - *[dataset.get_gt_seg_maps() for dataset in self.datasets]) - else: - # if the results are `pre_eval` results, - # we do not need gt_seg_maps to evaluate - gt_seg_maps = None - eval_results = self.datasets[0].evaluate( - results, gt_seg_maps=gt_seg_maps, logger=logger, **kwargs) - return eval_results - - def get_dataset_idx_and_sample_idx(self, indice): - """Return dataset and sample index when given an indice of - ConcatDataset. - - Args: - indice (int): indice of sample in ConcatDataset - - Returns: - int: the index of sub dataset the sample belong to - int: the index of sample in its corresponding subset - """ - if indice < 0: - if -indice > len(self): - raise ValueError( - 'absolute value of index should not exceed dataset length') - indice = len(self) + indice - dataset_idx = bisect.bisect_right(self.cumulative_sizes, indice) - if dataset_idx == 0: - sample_idx = indice - else: - sample_idx = indice - self.cumulative_sizes[dataset_idx - 1] - return dataset_idx, sample_idx - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """format result for every sample of ConcatDataset.""" - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].format_results( - [results[i]], - imgfile_prefix + f'/{dataset_idx}', - indices=[sample_idx], - **kwargs) - ret_res.append(res) - return sum(ret_res, []) - - def pre_eval(self, preds, indices): - """do pre eval for every sample of ConcatDataset.""" - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].pre_eval(preds[i], sample_idx) - ret_res.append(res) - return sum(ret_res, []) - - -@DATASETS.register_module() -class RepeatDataset(object): - """A wrapper of repeated dataset. - - The length of repeated dataset will be `times` larger than the original - dataset. This is useful when the data loading time is long but the dataset - is small. Using RepeatDataset can reduce the data loading time between - epochs. - - Args: - dataset (:obj:`Dataset`): The dataset to be repeated. - times (int): Repeat times. - """ - - def __init__(self, dataset, times): - self.dataset = dataset - self.times = times - self.CLASSES = dataset.CLASSES - self.PALETTE = dataset.PALETTE - self._ori_len = len(self.dataset) - - def __getitem__(self, idx): - """Get item from original dataset.""" - return self.dataset[idx % self._ori_len] - - def __len__(self): - """The length is multiplied by ``times``""" - return self.times * self._ori_len - - -@DATASETS.register_module() -class MultiImageMixDataset: - """A wrapper of multiple images mixed dataset. - - Suitable for training on multiple images mixed data augmentation like - mosaic and mixup. For the augmentation pipeline of mixed image data, - the `get_indexes` method needs to be provided to obtain the image - indexes, and you can set `skip_flags` to change the pipeline running - process. - - - Args: - dataset (:obj:`CustomDataset`): The dataset to be mixed. - pipeline (Sequence[dict]): Sequence of transform object or - config dict to be composed. - skip_type_keys (list[str], optional): Sequence of type string to - be skip pipeline. Default to None. - """ - - def __init__(self, dataset, pipeline, skip_type_keys=None): - assert isinstance(pipeline, collections.abc.Sequence) - if skip_type_keys is not None: - assert all([ - isinstance(skip_type_key, str) - for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys - - self.pipeline = [] - self.pipeline_types = [] - for transform in pipeline: - if isinstance(transform, dict): - self.pipeline_types.append(transform['type']) - transform = build_from_cfg(transform, PIPELINES) - self.pipeline.append(transform) - else: - raise TypeError('pipeline must be a dict') - - self.dataset = dataset - self.CLASSES = dataset.CLASSES - self.PALETTE = dataset.PALETTE - self.num_samples = len(dataset) - - def __len__(self): - return self.num_samples - - def __getitem__(self, idx): - results = copy.deepcopy(self.dataset[idx]) - for (transform, transform_type) in zip(self.pipeline, - self.pipeline_types): - if self._skip_type_keys is not None and \ - transform_type in self._skip_type_keys: - continue - - if hasattr(transform, 'get_indexes'): - indexes = transform.get_indexes(self.dataset) - if not isinstance(indexes, collections.abc.Sequence): - indexes = [indexes] - mix_results = [ - copy.deepcopy(self.dataset[index]) for index in indexes - ] - results['mix_results'] = mix_results - - results = transform(results) - - if 'mix_results' in results: - results.pop('mix_results') - - return results - - def update_skip_type_keys(self, skip_type_keys): - """Update skip_type_keys. - - It is called by an external hook. - - Args: - skip_type_keys (list[str], optional): Sequence of type - string to be skip pipeline. - """ - assert all([ - isinstance(skip_type_key, str) for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pascal_context.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pascal_context.py deleted file mode 100644 index efacee0f3..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pascal_context.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class PascalContextDataset(CustomDataset): - """PascalContext dataset. - - In segmentation map annotation for PascalContext, 0 stands for background, - which is included in 60 categories. ``reduce_zero_label`` is fixed to - False. The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is - fixed to '.png'. - - Args: - split (str): Split txt file for PascalContext. - """ - - CLASSES = ('background', 'aeroplane', 'bag', 'bed', 'bedclothes', 'bench', - 'bicycle', 'bird', 'boat', 'book', 'bottle', 'building', 'bus', - 'cabinet', 'car', 'cat', 'ceiling', 'chair', 'cloth', - 'computer', 'cow', 'cup', 'curtain', 'dog', 'door', 'fence', - 'floor', 'flower', 'food', 'grass', 'ground', 'horse', - 'keyboard', 'light', 'motorbike', 'mountain', 'mouse', 'person', - 'plate', 'platform', 'pottedplant', 'road', 'rock', 'sheep', - 'shelves', 'sidewalk', 'sign', 'sky', 'snow', 'sofa', 'table', - 'track', 'train', 'tree', 'truck', 'tvmonitor', 'wall', 'water', - 'window', 'wood') - - PALETTE = [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255]] - - def __init__(self, split, **kwargs): - super(PascalContextDataset, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - split=split, - reduce_zero_label=False, - **kwargs) - assert self.file_client.exists(self.img_dir) and self.split is not None - - -@DATASETS.register_module() -class PascalContextDataset59(CustomDataset): - """PascalContext dataset. - - In segmentation map annotation for PascalContext, 0 stands for background, - which is included in 60 categories. ``reduce_zero_label`` is fixed to - False. The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is - fixed to '.png'. - - Args: - split (str): Split txt file for PascalContext. - """ - - CLASSES = ('aeroplane', 'bag', 'bed', 'bedclothes', 'bench', 'bicycle', - 'bird', 'boat', 'book', 'bottle', 'building', 'bus', 'cabinet', - 'car', 'cat', 'ceiling', 'chair', 'cloth', 'computer', 'cow', - 'cup', 'curtain', 'dog', 'door', 'fence', 'floor', 'flower', - 'food', 'grass', 'ground', 'horse', 'keyboard', 'light', - 'motorbike', 'mountain', 'mouse', 'person', 'plate', 'platform', - 'pottedplant', 'road', 'rock', 'sheep', 'shelves', 'sidewalk', - 'sign', 'sky', 'snow', 'sofa', 'table', 'track', 'train', - 'tree', 'truck', 'tvmonitor', 'wall', 'water', 'window', 'wood') - - PALETTE = [[180, 120, 120], [6, 230, 230], [80, 50, 50], [4, 200, 3], - [120, 120, 80], [140, 140, 140], [204, 5, 255], [230, 230, 230], - [4, 250, 7], [224, 5, 255], [235, 255, 7], [150, 5, 61], - [120, 120, 70], [8, 255, 51], [255, 6, 82], [143, 255, 140], - [204, 255, 4], [255, 51, 7], [204, 70, 3], [0, 102, 200], - [61, 230, 250], [255, 6, 51], [11, 102, 255], [255, 7, 71], - [255, 9, 224], [9, 7, 230], [220, 220, 220], [255, 9, 92], - [112, 9, 255], [8, 255, 214], [7, 255, 224], [255, 184, 6], - [10, 255, 71], [255, 41, 10], [7, 255, 255], [224, 255, 8], - [102, 8, 255], [255, 61, 6], [255, 194, 7], [255, 122, 8], - [0, 255, 20], [255, 8, 41], [255, 5, 153], [6, 51, 255], - [235, 12, 255], [160, 150, 20], [0, 163, 255], [140, 140, 140], - [250, 10, 15], [20, 255, 0], [31, 255, 0], [255, 31, 0], - [255, 224, 0], [153, 255, 0], [0, 0, 255], [255, 71, 0], - [0, 235, 255], [0, 173, 255], [31, 0, 255]] - - def __init__(self, split, **kwargs): - super(PascalContextDataset59, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - split=split, - reduce_zero_label=True, - **kwargs) - assert self.file_client.exists(self.img_dir) and self.split is not None diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/__init__.py deleted file mode 100644 index 8256a6fe2..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .compose import Compose -from .formatting import (Collect, ImageToTensor, ToDataContainer, ToTensor, - Transpose, to_tensor) -from .loading import LoadAnnotations, LoadImageFromFile -from .test_time_aug import MultiScaleFlipAug -from .transforms import (CLAHE, AdjustGamma, Normalize, Pad, - PhotoMetricDistortion, RandomCrop, RandomCutOut, - RandomFlip, RandomMosaic, RandomRotate, Rerange, - Resize, RGB2Gray, SegRescale) - -__all__ = [ - 'Compose', 'to_tensor', 'ToTensor', 'ImageToTensor', 'ToDataContainer', - 'Transpose', 'Collect', 'LoadAnnotations', 'LoadImageFromFile', - 'MultiScaleFlipAug', 'Resize', 'RandomFlip', 'Pad', 'RandomCrop', - 'Normalize', 'SegRescale', 'PhotoMetricDistortion', 'RandomRotate', - 'AdjustGamma', 'CLAHE', 'Rerange', 'RGB2Gray', 'RandomCutOut', - 'RandomMosaic' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/compose.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/compose.py deleted file mode 100644 index 30280c133..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/compose.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections - -from mmcv.utils import build_from_cfg - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class Compose(object): - """Compose multiple transforms sequentially. - - Args: - transforms (Sequence[dict | callable]): Sequence of transform object or - config dict to be composed. - """ - - def __init__(self, transforms): - assert isinstance(transforms, collections.abc.Sequence) - self.transforms = [] - for transform in transforms: - if isinstance(transform, dict): - transform = build_from_cfg(transform, PIPELINES) - self.transforms.append(transform) - elif callable(transform): - self.transforms.append(transform) - else: - raise TypeError('transform must be callable or a dict') - - def __call__(self, data): - """Call function to apply transforms sequentially. - - Args: - data (dict): A result dict contains the data to transform. - - Returns: - dict: Transformed data. - """ - - for t in self.transforms: - data = t(data) - if data is None: - return None - return data - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - format_string += '\n' - format_string += f' {t}' - format_string += '\n)' - return format_string diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/formating.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/formating.py deleted file mode 100644 index f6e53bfeb..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/formating.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -import warnings - -from .formatting import * - -warnings.warn('DeprecationWarning: mmseg.datasets.pipelines.formating will be ' - 'deprecated in 2021, please replace it with ' - 'mmseg.datasets.pipelines.formatting.') diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/formatting.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/formatting.py deleted file mode 100644 index 4e057c1b8..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/formatting.py +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import mmcv -import numpy as np -import torch -from mmcv.parallel import DataContainer as DC - -from ..builder import PIPELINES - - -def to_tensor(data): - """Convert objects of various python types to :obj:`torch.Tensor`. - - Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, - :class:`Sequence`, :class:`int` and :class:`float`. - - Args: - data (torch.Tensor | numpy.ndarray | Sequence | int | float): Data to - be converted. - """ - - if isinstance(data, torch.Tensor): - return data - elif isinstance(data, np.ndarray): - return torch.from_numpy(data) - elif isinstance(data, Sequence) and not mmcv.is_str(data): - return torch.tensor(data) - elif isinstance(data, int): - return torch.LongTensor([data]) - elif isinstance(data, float): - return torch.FloatTensor([data]) - else: - raise TypeError(f'type {type(data)} cannot be converted to tensor.') - - -@PIPELINES.register_module() -class ToTensor(object): - """Convert some results to :obj:`torch.Tensor` by given keys. - - Args: - keys (Sequence[str]): Keys that need to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert data in results to :obj:`torch.Tensor`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted - to :obj:`torch.Tensor`. - """ - - for key in self.keys: - results[key] = to_tensor(results[key]) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class ImageToTensor(object): - """Convert image to :obj:`torch.Tensor` by given keys. - - The dimension order of input image is (H, W, C). The pipeline will convert - it to (C, H, W). If only 2 dimension (H, W) is given, the output would be - (1, H, W). - - Args: - keys (Sequence[str]): Key of images to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - img = results[key] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - results[key] = to_tensor(img.transpose(2, 0, 1)) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class Transpose(object): - """Transpose some results by given keys. - - Args: - keys (Sequence[str]): Keys of results to be transposed. - order (Sequence[int]): Order of transpose. - """ - - def __init__(self, keys, order): - self.keys = keys - self.order = order - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - results[key] = results[key].transpose(self.order) - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, order={self.order})' - - -@PIPELINES.register_module() -class ToDataContainer(object): - """Convert results to :obj:`mmcv.DataContainer` by given fields. - - Args: - fields (Sequence[dict]): Each field is a dict like - ``dict(key='xxx', **kwargs)``. The ``key`` in result will - be converted to :obj:`mmcv.DataContainer` with ``**kwargs``. - Default: ``(dict(key='img', stack=True), - dict(key='gt_semantic_seg'))``. - """ - - def __init__(self, - fields=(dict(key='img', - stack=True), dict(key='gt_semantic_seg'))): - self.fields = fields - - def __call__(self, results): - """Call function to convert data in results to - :obj:`mmcv.DataContainer`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted to - :obj:`mmcv.DataContainer`. - """ - - for field in self.fields: - field = field.copy() - key = field.pop('key') - results[key] = DC(results[key], **field) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(fields={self.fields})' - - -@PIPELINES.register_module() -class DefaultFormatBundle(object): - """Default formatting bundle. - - It simplifies the pipeline of formatting common fields, including "img" - and "gt_semantic_seg". These fields are formatted as follows. - - - img: (1)transpose, (2)to tensor, (3)to DataContainer (stack=True) - - gt_semantic_seg: (1)unsqueeze dim-0 (2)to tensor, - (3)to DataContainer (stack=True) - """ - - def __call__(self, results): - """Call function to transform and format common fields in results. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data that is formatted with - default bundle. - """ - - if 'img' in results: - img = results['img'] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - img = np.ascontiguousarray(img.transpose(2, 0, 1)) - results['img'] = DC(to_tensor(img), stack=True) - if 'gt_semantic_seg' in results: - # convert to long - results['gt_semantic_seg'] = DC( - to_tensor(results['gt_semantic_seg'][None, - ...].astype(np.int64)), - stack=True) - return results - - def __repr__(self): - return self.__class__.__name__ - - -@PIPELINES.register_module() -class Collect(object): - """Collect data from the loader relevant to the specific task. - - This is usually the last stage of the data loader pipeline. Typically keys - is set to some subset of "img", "gt_semantic_seg". - - The "img_meta" item is always populated. The contents of the "img_meta" - dictionary depends on "meta_keys". By default this includes: - - - "img_shape": shape of the image input to the network as a tuple - (h, w, c). Note that images may be zero padded on the bottom/right - if the batch tensor is larger than this shape. - - - "scale_factor": a float indicating the preprocessing scale - - - "flip": a boolean indicating if image flip transform was used - - - "filename": path to the image file - - - "ori_shape": original shape of the image as a tuple (h, w, c) - - - "pad_shape": image shape after padding - - - "img_norm_cfg": a dict of normalization information: - - mean - per channel mean subtraction - - std - per channel std divisor - - to_rgb - bool indicating if bgr was converted to rgb - - Args: - keys (Sequence[str]): Keys of results to be collected in ``data``. - meta_keys (Sequence[str], optional): Meta keys to be converted to - ``mmcv.DataContainer`` and collected in ``data[img_metas]``. - Default: (``filename``, ``ori_filename``, ``ori_shape``, - ``img_shape``, ``pad_shape``, ``scale_factor``, ``flip``, - ``flip_direction``, ``img_norm_cfg``) - """ - - def __init__(self, - keys, - meta_keys=('filename', 'ori_filename', 'ori_shape', - 'img_shape', 'pad_shape', 'scale_factor', 'flip', - 'flip_direction', 'img_norm_cfg')): - self.keys = keys - self.meta_keys = meta_keys - - def __call__(self, results): - """Call function to collect keys in results. The keys in ``meta_keys`` - will be converted to :obj:mmcv.DataContainer. - - Args: - results (dict): Result dict contains the data to collect. - - Returns: - dict: The result dict contains the following keys - - keys in``self.keys`` - - ``img_metas`` - """ - - data = {} - img_meta = {} - for key in self.meta_keys: - img_meta[key] = results[key] - data['img_metas'] = DC(img_meta, cpu_only=True) - for key in self.keys: - data[key] = results[key] - return data - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, meta_keys={self.meta_keys})' diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/loading.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/loading.py deleted file mode 100644 index 572e43431..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/loading.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class LoadImageFromFile(object): - """Load an image from file. - - Required keys are "img_prefix" and "img_info" (a dict that must contain the - key "filename"). Added or updated keys are "filename", "img", "img_shape", - "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`), - "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1). - - Args: - to_float32 (bool): Whether to convert the loaded image to a float32 - numpy array. If set to False, the loaded image is an uint8 array. - Defaults to False. - color_type (str): The flag argument for :func:`mmcv.imfrombytes`. - Defaults to 'color'. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'cv2' - """ - - def __init__(self, - to_float32=False, - color_type='color', - file_client_args=dict(backend='disk'), - imdecode_backend='cv2'): - self.to_float32 = to_float32 - self.color_type = color_type - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call functions to load image and get image meta information. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded image and meta information. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('img_prefix') is not None: - filename = osp.join(results['img_prefix'], - results['img_info']['filename']) - else: - filename = results['img_info']['filename'] - img_bytes = self.file_client.get(filename) - img = mmcv.imfrombytes( - img_bytes, flag=self.color_type, backend=self.imdecode_backend) - if self.to_float32: - img = img.astype(np.float32) - - results['filename'] = filename - results['ori_filename'] = results['img_info']['filename'] - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - # Set initial values for default meta_keys - results['pad_shape'] = img.shape - results['scale_factor'] = 1.0 - num_channels = 1 if len(img.shape) < 3 else img.shape[2] - results['img_norm_cfg'] = dict( - mean=np.zeros(num_channels, dtype=np.float32), - std=np.ones(num_channels, dtype=np.float32), - to_rgb=False) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(to_float32={self.to_float32},' - repr_str += f"color_type='{self.color_type}'," - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str - - -@PIPELINES.register_module() -class LoadAnnotations(object): - """Load annotations for semantic segmentation. - - Args: - reduce_zero_label (bool): Whether reduce all label value by 1. - Usually used for datasets where 0 is background label. - Default: False. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'pillow' - """ - - def __init__(self, - reduce_zero_label=False, - file_client_args=dict(backend='disk'), - imdecode_backend='pillow'): - self.reduce_zero_label = reduce_zero_label - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call function to load multiple types annotations. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded semantic segmentation annotations. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('seg_prefix', None) is not None: - filename = osp.join(results['seg_prefix'], - results['ann_info']['seg_map']) - else: - filename = results['ann_info']['seg_map'] - img_bytes = self.file_client.get(filename) - gt_semantic_seg = mmcv.imfrombytes( - img_bytes, flag='unchanged', - backend=self.imdecode_backend).squeeze().astype(np.uint8) - # modify if custom classes - if results.get('label_map', None) is not None: - # Add deep copy to solve bug of repeatedly - # replace `gt_semantic_seg`, which is reported in - # https://github.com/open-mmlab/mmsegmentation/pull/1445/ - gt_semantic_seg_copy = gt_semantic_seg.copy() - for old_id, new_id in results['label_map'].items(): - gt_semantic_seg[gt_semantic_seg_copy == old_id] = new_id - # reduce zero_label - if self.reduce_zero_label: - # avoid using underflow conversion - gt_semantic_seg[gt_semantic_seg == 0] = 255 - gt_semantic_seg = gt_semantic_seg - 1 - gt_semantic_seg[gt_semantic_seg == 254] = 255 - results['gt_semantic_seg'] = gt_semantic_seg - results['seg_fields'].append('gt_semantic_seg') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(reduce_zero_label={self.reduce_zero_label},' - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py deleted file mode 100644 index 5c17cbbba..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv - -from ..builder import PIPELINES -from .compose import Compose - - -@PIPELINES.register_module() -class MultiScaleFlipAug(object): - """Test-time augmentation with multiple scales and flipping. - - An example configuration is as followed: - - .. code-block:: - - img_scale=(2048, 1024), - img_ratios=[0.5, 1.0], - flip=True, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ] - - After MultiScaleFLipAug with above configuration, the results are wrapped - into lists of the same length as followed: - - .. code-block:: - - dict( - img=[...], - img_shape=[...], - scale=[(1024, 512), (1024, 512), (2048, 1024), (2048, 1024)] - flip=[False, True, False, True] - ... - ) - - Args: - transforms (list[dict]): Transforms to apply in each augmentation. - img_scale (None | tuple | list[tuple]): Images scales for resizing. - img_ratios (float | list[float]): Image ratios for resizing - flip (bool): Whether apply flip augmentation. Default: False. - flip_direction (str | list[str]): Flip augmentation directions, - options are "horizontal" and "vertical". If flip_direction is list, - multiple flip augmentations will be applied. - It has no effect when flip == False. Default: "horizontal". - """ - - def __init__(self, - transforms, - img_scale, - img_ratios=None, - flip=False, - flip_direction='horizontal'): - self.transforms = Compose(transforms) - if img_ratios is not None: - img_ratios = img_ratios if isinstance(img_ratios, - list) else [img_ratios] - assert mmcv.is_list_of(img_ratios, float) - if img_scale is None: - # mode 1: given img_scale=None and a range of image ratio - self.img_scale = None - assert mmcv.is_list_of(img_ratios, float) - elif isinstance(img_scale, tuple) and mmcv.is_list_of( - img_ratios, float): - assert len(img_scale) == 2 - # mode 2: given a scale and a range of image ratio - self.img_scale = [(int(img_scale[0] * ratio), - int(img_scale[1] * ratio)) - for ratio in img_ratios] - else: - # mode 3: given multiple scales - self.img_scale = img_scale if isinstance(img_scale, - list) else [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) or self.img_scale is None - self.flip = flip - self.img_ratios = img_ratios - self.flip_direction = flip_direction if isinstance( - flip_direction, list) else [flip_direction] - assert mmcv.is_list_of(self.flip_direction, str) - if not self.flip and self.flip_direction != ['horizontal']: - warnings.warn( - 'flip_direction has no effect when flip is set to False') - if (self.flip - and not any([t['type'] == 'RandomFlip' for t in transforms])): - warnings.warn( - 'flip has no effect when RandomFlip is not in transforms') - - def __call__(self, results): - """Call function to apply test time augment transforms on results. - - Args: - results (dict): Result dict contains the data to transform. - - Returns: - dict[str: list]: The augmented data, where each value is wrapped - into a list. - """ - - aug_data = [] - if self.img_scale is None and mmcv.is_list_of(self.img_ratios, float): - h, w = results['img'].shape[:2] - img_scale = [(int(w * ratio), int(h * ratio)) - for ratio in self.img_ratios] - else: - img_scale = self.img_scale - flip_aug = [False, True] if self.flip else [False] - for scale in img_scale: - for flip in flip_aug: - for direction in self.flip_direction: - _results = results.copy() - _results['scale'] = scale - _results['flip'] = flip - _results['flip_direction'] = direction - data = self.transforms(_results) - aug_data.append(data) - # list of dict to dict of list - aug_data_dict = {key: [] for key in aug_data[0]} - for data in aug_data: - for key, val in data.items(): - aug_data_dict[key].append(val) - return aug_data_dict - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(transforms={self.transforms}, ' - repr_str += f'img_scale={self.img_scale}, flip={self.flip})' - repr_str += f'flip_direction={self.flip_direction}' - return repr_str diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/transforms.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/transforms.py deleted file mode 100644 index 5673b646f..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/pipelines/transforms.py +++ /dev/null @@ -1,1335 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -import mmcv -import numpy as np -from mmcv.utils import deprecated_api_warning, is_tuple_of -from numpy import random - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class ResizeToMultiple(object): - """Resize images & seg to multiple of divisor. - - Args: - size_divisor (int): images and gt seg maps need to resize to multiple - of size_divisor. Default: 32. - interpolation (str, optional): The interpolation mode of image resize. - Default: None - """ - - def __init__(self, size_divisor=32, interpolation=None): - self.size_divisor = size_divisor - self.interpolation = interpolation - - def __call__(self, results): - """Call function to resize images, semantic segmentation map to - multiple of size divisor. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape' keys are updated. - """ - # Align image to multiple of size divisor. - img = results['img'] - img = mmcv.imresize_to_multiple( - img, - self.size_divisor, - scale_factor=1, - interpolation=self.interpolation - if self.interpolation else 'bilinear') - - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape - - # Align segmentation map to multiple of size divisor. - for key in results.get('seg_fields', []): - gt_seg = results[key] - gt_seg = mmcv.imresize_to_multiple( - gt_seg, - self.size_divisor, - scale_factor=1, - interpolation='nearest') - results[key] = gt_seg - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(size_divisor={self.size_divisor}, ' - f'interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class Resize(object): - """Resize images & seg. - - This transform resizes the input image to some scale. If the input dict - contains the key "scale", then the scale in the input dict is used, - otherwise the specified scale in the init method is used. - - ``img_scale`` can be None, a tuple (single-scale) or a list of tuple - (multi-scale). There are 4 multiscale modes: - - - ``ratio_range is not None``: - 1. When img_scale is None, img_scale is the shape of image in results - (img_scale = results['img'].shape[:2]) and the image is resized based - on the original size. (mode 1) - 2. When img_scale is a tuple (single-scale), randomly sample a ratio from - the ratio range and multiply it with the image scale. (mode 2) - - - ``ratio_range is None and multiscale_mode == "range"``: randomly sample a - scale from the a range. (mode 3) - - - ``ratio_range is None and multiscale_mode == "value"``: randomly sample a - scale from multiple scales. (mode 4) - - Args: - img_scale (tuple or list[tuple]): Images scales for resizing. - Default:None. - multiscale_mode (str): Either "range" or "value". - Default: 'range' - ratio_range (tuple[float]): (min_ratio, max_ratio). - Default: None - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: True - min_size (int, optional): The minimum size for input and the shape - of the image and seg map will not be less than ``min_size``. - As the shape of model input is fixed like 'SETR' and 'BEiT'. - Following the setting in these models, resized images must be - bigger than the crop size in ``slide_inference``. Default: None - """ - - def __init__(self, - img_scale=None, - multiscale_mode='range', - ratio_range=None, - keep_ratio=True, - min_size=None): - if img_scale is None: - self.img_scale = None - else: - if isinstance(img_scale, list): - self.img_scale = img_scale - else: - self.img_scale = [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) - - if ratio_range is not None: - # mode 1: given img_scale=None and a range of image ratio - # mode 2: given a scale and a range of image ratio - assert self.img_scale is None or len(self.img_scale) == 1 - else: - # mode 3 and 4: given multiple scales or a range of scales - assert multiscale_mode in ['value', 'range'] - - self.multiscale_mode = multiscale_mode - self.ratio_range = ratio_range - self.keep_ratio = keep_ratio - self.min_size = min_size - - @staticmethod - def random_select(img_scales): - """Randomly select an img_scale from given candidates. - - Args: - img_scales (list[tuple]): Images scales for selection. - - Returns: - (tuple, int): Returns a tuple ``(img_scale, scale_dix)``, - where ``img_scale`` is the selected image scale and - ``scale_idx`` is the selected index in the given candidates. - """ - - assert mmcv.is_list_of(img_scales, tuple) - scale_idx = np.random.randint(len(img_scales)) - img_scale = img_scales[scale_idx] - return img_scale, scale_idx - - @staticmethod - def random_sample(img_scales): - """Randomly sample an img_scale when ``multiscale_mode=='range'``. - - Args: - img_scales (list[tuple]): Images scale range for sampling. - There must be two tuples in img_scales, which specify the lower - and upper bound of image scales. - - Returns: - (tuple, None): Returns a tuple ``(img_scale, None)``, where - ``img_scale`` is sampled scale and None is just a placeholder - to be consistent with :func:`random_select`. - """ - - assert mmcv.is_list_of(img_scales, tuple) and len(img_scales) == 2 - img_scale_long = [max(s) for s in img_scales] - img_scale_short = [min(s) for s in img_scales] - long_edge = np.random.randint( - min(img_scale_long), - max(img_scale_long) + 1) - short_edge = np.random.randint( - min(img_scale_short), - max(img_scale_short) + 1) - img_scale = (long_edge, short_edge) - return img_scale, None - - @staticmethod - def random_sample_ratio(img_scale, ratio_range): - """Randomly sample an img_scale when ``ratio_range`` is specified. - - A ratio will be randomly sampled from the range specified by - ``ratio_range``. Then it would be multiplied with ``img_scale`` to - generate sampled scale. - - Args: - img_scale (tuple): Images scale base to multiply with ratio. - ratio_range (tuple[float]): The minimum and maximum ratio to scale - the ``img_scale``. - - Returns: - (tuple, None): Returns a tuple ``(scale, None)``, where - ``scale`` is sampled ratio multiplied with ``img_scale`` and - None is just a placeholder to be consistent with - :func:`random_select`. - """ - - assert isinstance(img_scale, tuple) and len(img_scale) == 2 - min_ratio, max_ratio = ratio_range - assert min_ratio <= max_ratio - ratio = np.random.random_sample() * (max_ratio - min_ratio) + min_ratio - scale = int(img_scale[0] * ratio), int(img_scale[1] * ratio) - return scale, None - - def _random_scale(self, results): - """Randomly sample an img_scale according to ``ratio_range`` and - ``multiscale_mode``. - - If ``ratio_range`` is specified, a ratio will be sampled and be - multiplied with ``img_scale``. - If multiple scales are specified by ``img_scale``, a scale will be - sampled according to ``multiscale_mode``. - Otherwise, single scale will be used. - - Args: - results (dict): Result dict from :obj:`dataset`. - - Returns: - dict: Two new keys 'scale` and 'scale_idx` are added into - ``results``, which would be used by subsequent pipelines. - """ - - if self.ratio_range is not None: - if self.img_scale is None: - h, w = results['img'].shape[:2] - scale, scale_idx = self.random_sample_ratio((w, h), - self.ratio_range) - else: - scale, scale_idx = self.random_sample_ratio( - self.img_scale[0], self.ratio_range) - elif len(self.img_scale) == 1: - scale, scale_idx = self.img_scale[0], 0 - elif self.multiscale_mode == 'range': - scale, scale_idx = self.random_sample(self.img_scale) - elif self.multiscale_mode == 'value': - scale, scale_idx = self.random_select(self.img_scale) - else: - raise NotImplementedError - - results['scale'] = scale - results['scale_idx'] = scale_idx - - def _resize_img(self, results): - """Resize images with ``results['scale']``.""" - if self.keep_ratio: - if self.min_size is not None: - # TODO: Now 'min_size' is an 'int' which means the minimum - # shape of images is (min_size, min_size, 3). 'min_size' - # with tuple type will be supported, i.e. the width and - # height are not equal. - if min(results['scale']) < self.min_size: - new_short = self.min_size - else: - new_short = min(results['scale']) - - h, w = results['img'].shape[:2] - if h > w: - new_h, new_w = new_short * h / w, new_short - else: - new_h, new_w = new_short, new_short * w / h - results['scale'] = (new_h, new_w) - - img, scale_factor = mmcv.imrescale( - results['img'], results['scale'], return_scale=True) - # the w_scale and h_scale has minor difference - # a real fix should be done in the mmcv.imrescale in the future - new_h, new_w = img.shape[:2] - h, w = results['img'].shape[:2] - w_scale = new_w / w - h_scale = new_h / h - else: - img, w_scale, h_scale = mmcv.imresize( - results['img'], results['scale'], return_scale=True) - scale_factor = np.array([w_scale, h_scale, w_scale, h_scale], - dtype=np.float32) - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape # in case that there is no padding - results['scale_factor'] = scale_factor - results['keep_ratio'] = self.keep_ratio - - def _resize_seg(self, results): - """Resize semantic segmentation map with ``results['scale']``.""" - for key in results.get('seg_fields', []): - if self.keep_ratio: - gt_seg = mmcv.imrescale( - results[key], results['scale'], interpolation='nearest') - else: - gt_seg = mmcv.imresize( - results[key], results['scale'], interpolation='nearest') - results[key] = gt_seg - - def __call__(self, results): - """Call function to resize images, bounding boxes, masks, semantic - segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape', 'scale_factor', - 'keep_ratio' keys are added into result dict. - """ - - if 'scale' not in results: - self._random_scale(results) - self._resize_img(results) - self._resize_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(img_scale={self.img_scale}, ' - f'multiscale_mode={self.multiscale_mode}, ' - f'ratio_range={self.ratio_range}, ' - f'keep_ratio={self.keep_ratio})') - return repr_str - - -@PIPELINES.register_module() -class RandomFlip(object): - """Flip the image & seg. - - If the input dict contains the key "flip", then the flag will be used, - otherwise it will be randomly decided by a ratio specified in the init - method. - - Args: - prob (float, optional): The flipping probability. Default: None. - direction(str, optional): The flipping direction. Options are - 'horizontal' and 'vertical'. Default: 'horizontal'. - """ - - @deprecated_api_warning({'flip_ratio': 'prob'}, cls_name='RandomFlip') - def __init__(self, prob=None, direction='horizontal'): - self.prob = prob - self.direction = direction - if prob is not None: - assert prob >= 0 and prob <= 1 - assert direction in ['horizontal', 'vertical'] - - def __call__(self, results): - """Call function to flip bounding boxes, masks, semantic segmentation - maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Flipped results, 'flip', 'flip_direction' keys are added into - result dict. - """ - - if 'flip' not in results: - flip = True if np.random.rand() < self.prob else False - results['flip'] = flip - if 'flip_direction' not in results: - results['flip_direction'] = self.direction - if results['flip']: - # flip image - results['img'] = mmcv.imflip( - results['img'], direction=results['flip_direction']) - - # flip segs - for key in results.get('seg_fields', []): - # use copy() to make numpy stride positive - results[key] = mmcv.imflip( - results[key], direction=results['flip_direction']).copy() - return results - - def __repr__(self): - return self.__class__.__name__ + f'(prob={self.prob})' - - -@PIPELINES.register_module() -class Pad(object): - """Pad the image & mask. - - There are two padding modes: (1) pad to a fixed size and (2) pad to the - minimum size that is divisible by some number. - Added keys are "pad_shape", "pad_fixed_size", "pad_size_divisor", - - Args: - size (tuple, optional): Fixed padding size. - size_divisor (int, optional): The divisor of padded size. - pad_val (float, optional): Padding value. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - """ - - def __init__(self, - size=None, - size_divisor=None, - pad_val=0, - seg_pad_val=255): - self.size = size - self.size_divisor = size_divisor - self.pad_val = pad_val - self.seg_pad_val = seg_pad_val - # only one of size and size_divisor should be valid - assert size is not None or size_divisor is not None - assert size is None or size_divisor is None - - def _pad_img(self, results): - """Pad images according to ``self.size``.""" - if self.size is not None: - padded_img = mmcv.impad( - results['img'], shape=self.size, pad_val=self.pad_val) - elif self.size_divisor is not None: - padded_img = mmcv.impad_to_multiple( - results['img'], self.size_divisor, pad_val=self.pad_val) - results['img'] = padded_img - results['pad_shape'] = padded_img.shape - results['pad_fixed_size'] = self.size - results['pad_size_divisor'] = self.size_divisor - - def _pad_seg(self, results): - """Pad masks according to ``results['pad_shape']``.""" - for key in results.get('seg_fields', []): - results[key] = mmcv.impad( - results[key], - shape=results['pad_shape'][:2], - pad_val=self.seg_pad_val) - - def __call__(self, results): - """Call function to pad images, masks, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Updated result dict. - """ - - self._pad_img(results) - self._pad_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(size={self.size}, size_divisor={self.size_divisor}, ' \ - f'pad_val={self.pad_val})' - return repr_str - - -@PIPELINES.register_module() -class Normalize(object): - """Normalize the image. - - Added key is "img_norm_cfg". - - Args: - mean (sequence): Mean values of 3 channels. - std (sequence): Std values of 3 channels. - to_rgb (bool): Whether to convert the image from BGR to RGB, - default is true. - """ - - def __init__(self, mean, std, to_rgb=True): - self.mean = np.array(mean, dtype=np.float32) - self.std = np.array(std, dtype=np.float32) - self.to_rgb = to_rgb - - def __call__(self, results): - """Call function to normalize images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Normalized results, 'img_norm_cfg' key is added into - result dict. - """ - - results['img'] = mmcv.imnormalize(results['img'], self.mean, self.std, - self.to_rgb) - results['img_norm_cfg'] = dict( - mean=self.mean, std=self.std, to_rgb=self.to_rgb) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(mean={self.mean}, std={self.std}, to_rgb=' \ - f'{self.to_rgb})' - return repr_str - - -@PIPELINES.register_module() -class Rerange(object): - """Rerange the image pixel value. - - Args: - min_value (float or int): Minimum value of the reranged image. - Default: 0. - max_value (float or int): Maximum value of the reranged image. - Default: 255. - """ - - def __init__(self, min_value=0, max_value=255): - assert isinstance(min_value, float) or isinstance(min_value, int) - assert isinstance(max_value, float) or isinstance(max_value, int) - assert min_value < max_value - self.min_value = min_value - self.max_value = max_value - - def __call__(self, results): - """Call function to rerange images. - - Args: - results (dict): Result dict from loading pipeline. - Returns: - dict: Reranged results. - """ - - img = results['img'] - img_min_value = np.min(img) - img_max_value = np.max(img) - - assert img_min_value < img_max_value - # rerange to [0, 1] - img = (img - img_min_value) / (img_max_value - img_min_value) - # rerange to [min_value, max_value] - img = img * (self.max_value - self.min_value) + self.min_value - results['img'] = img - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(min_value={self.min_value}, max_value={self.max_value})' - return repr_str - - -@PIPELINES.register_module() -class CLAHE(object): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - """ - - def __init__(self, clip_limit=40.0, tile_grid_size=(8, 8)): - assert isinstance(clip_limit, (float, int)) - self.clip_limit = clip_limit - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - self.tile_grid_size = tile_grid_size - - def __call__(self, results): - """Call function to Use CLAHE method process images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - for i in range(results['img'].shape[2]): - results['img'][:, :, i] = mmcv.clahe( - np.array(results['img'][:, :, i], dtype=np.uint8), - self.clip_limit, self.tile_grid_size) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(clip_limit={self.clip_limit}, '\ - f'tile_grid_size={self.tile_grid_size})' - return repr_str - - -@PIPELINES.register_module() -class RandomCrop(object): - """Random crop the image & seg. - - Args: - crop_size (tuple): Expected size after cropping, (h, w). - cat_max_ratio (float): The maximum ratio that single category could - occupy. - """ - - def __init__(self, crop_size, cat_max_ratio=1., ignore_index=255): - assert crop_size[0] > 0 and crop_size[1] > 0 - self.crop_size = crop_size - self.cat_max_ratio = cat_max_ratio - self.ignore_index = ignore_index - - def get_crop_bbox(self, img): - """Randomly get a crop bounding box.""" - margin_h = max(img.shape[0] - self.crop_size[0], 0) - margin_w = max(img.shape[1] - self.crop_size[1], 0) - offset_h = np.random.randint(0, margin_h + 1) - offset_w = np.random.randint(0, margin_w + 1) - crop_y1, crop_y2 = offset_h, offset_h + self.crop_size[0] - crop_x1, crop_x2 = offset_w, offset_w + self.crop_size[1] - - return crop_y1, crop_y2, crop_x1, crop_x2 - - def crop(self, img, crop_bbox): - """Crop from ``img``""" - crop_y1, crop_y2, crop_x1, crop_x2 = crop_bbox - img = img[crop_y1:crop_y2, crop_x1:crop_x2, ...] - return img - - def __call__(self, results): - """Call function to randomly crop images, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Randomly cropped results, 'img_shape' key in result dict is - updated according to crop size. - """ - - img = results['img'] - crop_bbox = self.get_crop_bbox(img) - if self.cat_max_ratio < 1.: - # Repeat 10 times - for _ in range(10): - seg_temp = self.crop(results['gt_semantic_seg'], crop_bbox) - labels, cnt = np.unique(seg_temp, return_counts=True) - cnt = cnt[labels != self.ignore_index] - if len(cnt) > 1 and np.max(cnt) / np.sum( - cnt) < self.cat_max_ratio: - break - crop_bbox = self.get_crop_bbox(img) - - # crop the image - img = self.crop(img, crop_bbox) - img_shape = img.shape - results['img'] = img - results['img_shape'] = img_shape - - # crop semantic seg - for key in results.get('seg_fields', []): - results[key] = self.crop(results[key], crop_bbox) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(crop_size={self.crop_size})' - - -@PIPELINES.register_module() -class RandomRotate(object): - """Rotate the image & seg. - - Args: - prob (float): The rotation probability. - degree (float, tuple[float]): Range of degrees to select from. If - degree is a number instead of tuple like (min, max), - the range of degree will be (``-degree``, ``+degree``) - pad_val (float, optional): Padding value of image. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. Default: None. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. Default: False - """ - - def __init__(self, - prob, - degree, - pad_val=0, - seg_pad_val=255, - center=None, - auto_bound=False): - self.prob = prob - assert prob >= 0 and prob <= 1 - if isinstance(degree, (float, int)): - assert degree > 0, f'degree {degree} should be positive' - self.degree = (-degree, degree) - else: - self.degree = degree - assert len(self.degree) == 2, f'degree {self.degree} should be a ' \ - f'tuple of (min, max)' - self.pal_val = pad_val - self.seg_pad_val = seg_pad_val - self.center = center - self.auto_bound = auto_bound - - def __call__(self, results): - """Call function to rotate image, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Rotated results. - """ - - rotate = True if np.random.rand() < self.prob else False - degree = np.random.uniform(min(*self.degree), max(*self.degree)) - if rotate: - # rotate image - results['img'] = mmcv.imrotate( - results['img'], - angle=degree, - border_value=self.pal_val, - center=self.center, - auto_bound=self.auto_bound) - - # rotate segs - for key in results.get('seg_fields', []): - results[key] = mmcv.imrotate( - results[key], - angle=degree, - border_value=self.seg_pad_val, - center=self.center, - auto_bound=self.auto_bound, - interpolation='nearest') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' \ - f'degree={self.degree}, ' \ - f'pad_val={self.pal_val}, ' \ - f'seg_pad_val={self.seg_pad_val}, ' \ - f'center={self.center}, ' \ - f'auto_bound={self.auto_bound})' - return repr_str - - -@PIPELINES.register_module() -class RGB2Gray(object): - """Convert RGB image to grayscale image. - - This transform calculate the weighted mean of input image channels with - ``weights`` and then expand the channels to ``out_channels``. When - ``out_channels`` is None, the number of output channels is the same as - input channels. - - Args: - out_channels (int): Expected number of output channels after - transforming. Default: None. - weights (tuple[float]): The weights to calculate the weighted mean. - Default: (0.299, 0.587, 0.114). - """ - - def __init__(self, out_channels=None, weights=(0.299, 0.587, 0.114)): - assert out_channels is None or out_channels > 0 - self.out_channels = out_channels - assert isinstance(weights, tuple) - for item in weights: - assert isinstance(item, (float, int)) - self.weights = weights - - def __call__(self, results): - """Call function to convert RGB image to grayscale image. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with grayscale image. - """ - img = results['img'] - assert len(img.shape) == 3 - assert img.shape[2] == len(self.weights) - weights = np.array(self.weights).reshape((1, 1, -1)) - img = (img * weights).sum(2, keepdims=True) - if self.out_channels is None: - img = img.repeat(weights.shape[2], axis=2) - else: - img = img.repeat(self.out_channels, axis=2) - - results['img'] = img - results['img_shape'] = img.shape - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(out_channels={self.out_channels}, ' \ - f'weights={self.weights})' - return repr_str - - -@PIPELINES.register_module() -class AdjustGamma(object): - """Using gamma correction to process the image. - - Args: - gamma (float or int): Gamma value used in gamma correction. - Default: 1.0. - """ - - def __init__(self, gamma=1.0): - assert isinstance(gamma, float) or isinstance(gamma, int) - assert gamma > 0 - self.gamma = gamma - inv_gamma = 1.0 / gamma - self.table = np.array([(i / 255.0)**inv_gamma * 255 - for i in np.arange(256)]).astype('uint8') - - def __call__(self, results): - """Call function to process the image with gamma correction. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - results['img'] = mmcv.lut_transform( - np.array(results['img'], dtype=np.uint8), self.table) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(gamma={self.gamma})' - - -@PIPELINES.register_module() -class SegRescale(object): - """Rescale semantic segmentation maps. - - Args: - scale_factor (float): The scale factor of the final output. - """ - - def __init__(self, scale_factor=1): - self.scale_factor = scale_factor - - def __call__(self, results): - """Call function to scale the semantic segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with semantic segmentation map scaled. - """ - for key in results.get('seg_fields', []): - if self.scale_factor != 1: - results[key] = mmcv.imrescale( - results[key], self.scale_factor, interpolation='nearest') - return results - - def __repr__(self): - return self.__class__.__name__ + f'(scale_factor={self.scale_factor})' - - -@PIPELINES.register_module() -class PhotoMetricDistortion(object): - """Apply photometric distortion to image sequentially, every transformation - is applied with a probability of 0.5. The position of random contrast is in - second or second to last. - - 1. random brightness - 2. random contrast (mode 0) - 3. convert color from BGR to HSV - 4. random saturation - 5. random hue - 6. convert color from HSV to BGR - 7. random contrast (mode 1) - - Args: - brightness_delta (int): delta of brightness. - contrast_range (tuple): range of contrast. - saturation_range (tuple): range of saturation. - hue_delta (int): delta of hue. - """ - - def __init__(self, - brightness_delta=32, - contrast_range=(0.5, 1.5), - saturation_range=(0.5, 1.5), - hue_delta=18): - self.brightness_delta = brightness_delta - self.contrast_lower, self.contrast_upper = contrast_range - self.saturation_lower, self.saturation_upper = saturation_range - self.hue_delta = hue_delta - - def convert(self, img, alpha=1, beta=0): - """Multiple with alpha and add beat with clip.""" - img = img.astype(np.float32) * alpha + beta - img = np.clip(img, 0, 255) - return img.astype(np.uint8) - - def brightness(self, img): - """Brightness distortion.""" - if random.randint(2): - return self.convert( - img, - beta=random.uniform(-self.brightness_delta, - self.brightness_delta)) - return img - - def contrast(self, img): - """Contrast distortion.""" - if random.randint(2): - return self.convert( - img, - alpha=random.uniform(self.contrast_lower, self.contrast_upper)) - return img - - def saturation(self, img): - """Saturation distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, 1] = self.convert( - img[:, :, 1], - alpha=random.uniform(self.saturation_lower, - self.saturation_upper)) - img = mmcv.hsv2bgr(img) - return img - - def hue(self, img): - """Hue distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, - 0] = (img[:, :, 0].astype(int) + - random.randint(-self.hue_delta, self.hue_delta)) % 180 - img = mmcv.hsv2bgr(img) - return img - - def __call__(self, results): - """Call function to perform photometric distortion on images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images distorted. - """ - - img = results['img'] - # random brightness - img = self.brightness(img) - - # mode == 0 --> do random contrast first - # mode == 1 --> do random contrast last - mode = random.randint(2) - if mode == 1: - img = self.contrast(img) - - # random saturation - img = self.saturation(img) - - # random hue - img = self.hue(img) - - # random contrast - if mode == 0: - img = self.contrast(img) - - results['img'] = img - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(brightness_delta={self.brightness_delta}, ' - f'contrast_range=({self.contrast_lower}, ' - f'{self.contrast_upper}), ' - f'saturation_range=({self.saturation_lower}, ' - f'{self.saturation_upper}), ' - f'hue_delta={self.hue_delta})') - return repr_str - - -@PIPELINES.register_module() -class RandomCutOut(object): - """CutOut operation. - - Randomly drop some regions of image used in - `Cutout `_. - Args: - prob (float): cutout probability. - n_holes (int | tuple[int, int]): Number of regions to be dropped. - If it is given as a list, number of holes will be randomly - selected from the closed interval [`n_holes[0]`, `n_holes[1]`]. - cutout_shape (tuple[int, int] | list[tuple[int, int]]): The candidate - shape of dropped regions. It can be `tuple[int, int]` to use a - fixed cutout shape, or `list[tuple[int, int]]` to randomly choose - shape from the list. - cutout_ratio (tuple[float, float] | list[tuple[float, float]]): The - candidate ratio of dropped regions. It can be `tuple[float, float]` - to use a fixed ratio or `list[tuple[float, float]]` to randomly - choose ratio from the list. Please note that `cutout_shape` - and `cutout_ratio` cannot be both given at the same time. - fill_in (tuple[float, float, float] | tuple[int, int, int]): The value - of pixel to fill in the dropped regions. Default: (0, 0, 0). - seg_fill_in (int): The labels of pixel to fill in the dropped regions. - If seg_fill_in is None, skip. Default: None. - """ - - def __init__(self, - prob, - n_holes, - cutout_shape=None, - cutout_ratio=None, - fill_in=(0, 0, 0), - seg_fill_in=None): - - assert 0 <= prob and prob <= 1 - assert (cutout_shape is None) ^ (cutout_ratio is None), \ - 'Either cutout_shape or cutout_ratio should be specified.' - assert (isinstance(cutout_shape, (list, tuple)) - or isinstance(cutout_ratio, (list, tuple))) - if isinstance(n_holes, tuple): - assert len(n_holes) == 2 and 0 <= n_holes[0] < n_holes[1] - else: - n_holes = (n_holes, n_holes) - if seg_fill_in is not None: - assert (isinstance(seg_fill_in, int) and 0 <= seg_fill_in - and seg_fill_in <= 255) - self.prob = prob - self.n_holes = n_holes - self.fill_in = fill_in - self.seg_fill_in = seg_fill_in - self.with_ratio = cutout_ratio is not None - self.candidates = cutout_ratio if self.with_ratio else cutout_shape - if not isinstance(self.candidates, list): - self.candidates = [self.candidates] - - def __call__(self, results): - """Call function to drop some regions of image.""" - cutout = True if np.random.rand() < self.prob else False - if cutout: - h, w, c = results['img'].shape - n_holes = np.random.randint(self.n_holes[0], self.n_holes[1] + 1) - for _ in range(n_holes): - x1 = np.random.randint(0, w) - y1 = np.random.randint(0, h) - index = np.random.randint(0, len(self.candidates)) - if not self.with_ratio: - cutout_w, cutout_h = self.candidates[index] - else: - cutout_w = int(self.candidates[index][0] * w) - cutout_h = int(self.candidates[index][1] * h) - - x2 = np.clip(x1 + cutout_w, 0, w) - y2 = np.clip(y1 + cutout_h, 0, h) - results['img'][y1:y2, x1:x2, :] = self.fill_in - - if self.seg_fill_in is not None: - for key in results.get('seg_fields', []): - results[key][y1:y2, x1:x2] = self.seg_fill_in - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' - repr_str += f'n_holes={self.n_holes}, ' - repr_str += (f'cutout_ratio={self.candidates}, ' if self.with_ratio - else f'cutout_shape={self.candidates}, ') - repr_str += f'fill_in={self.fill_in}, ' - repr_str += f'seg_fill_in={self.seg_fill_in})' - return repr_str - - -@PIPELINES.register_module() -class RandomMosaic(object): - """Mosaic augmentation. Given 4 images, mosaic transform combines them into - one output image. The output image is composed of the parts from each sub- - image. - - .. code:: text - - mosaic transform - center_x - +------------------------------+ - | pad | pad | - | +-----------+ | - | | | | - | | image1 |--------+ | - | | | | | - | | | image2 | | - center_y |----+-------------+-----------| - | | cropped | | - |pad | image3 | image4 | - | | | | - +----|-------------+-----------+ - | | - +-------------+ - - The mosaic transform steps are as follows: - 1. Choose the mosaic center as the intersections of 4 images - 2. Get the left top image according to the index, and randomly - sample another 3 images from the custom dataset. - 3. Sub image will be cropped if image is larger than mosaic patch - - Args: - prob (float): mosaic probability. - img_scale (Sequence[int]): Image size after mosaic pipeline of - a single image. The size of the output image is four times - that of a single image. The output image comprises 4 single images. - Default: (640, 640). - center_ratio_range (Sequence[float]): Center ratio range of mosaic - output. Default: (0.5, 1.5). - pad_val (int): Pad value. Default: 0. - seg_pad_val (int): Pad value of segmentation map. Default: 255. - """ - - def __init__(self, - prob, - img_scale=(640, 640), - center_ratio_range=(0.5, 1.5), - pad_val=0, - seg_pad_val=255): - assert 0 <= prob and prob <= 1 - assert isinstance(img_scale, tuple) - self.prob = prob - self.img_scale = img_scale - self.center_ratio_range = center_ratio_range - self.pad_val = pad_val - self.seg_pad_val = seg_pad_val - - def __call__(self, results): - """Call function to make a mosaic of image. - - Args: - results (dict): Result dict. - - Returns: - dict: Result dict with mosaic transformed. - """ - mosaic = True if np.random.rand() < self.prob else False - if mosaic: - results = self._mosaic_transform_img(results) - results = self._mosaic_transform_seg(results) - return results - - def get_indexes(self, dataset): - """Call function to collect indexes. - - Args: - dataset (:obj:`MultiImageMixDataset`): The dataset. - - Returns: - list: indexes. - """ - - indexes = [random.randint(0, len(dataset)) for _ in range(3)] - return indexes - - def _mosaic_transform_img(self, results): - """Mosaic transform function. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - if len(results['img'].shape) == 3: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2), 3), - self.pad_val, - dtype=results['img'].dtype) - else: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.pad_val, - dtype=results['img'].dtype) - - # mosaic center x, y - self.center_x = int( - random.uniform(*self.center_ratio_range) * self.img_scale[1]) - self.center_y = int( - random.uniform(*self.center_ratio_range) * self.img_scale[0]) - center_position = (self.center_x, self.center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - result_patch = copy.deepcopy(results) - else: - result_patch = copy.deepcopy(results['mix_results'][i - 1]) - - img_i = result_patch['img'] - h_i, w_i = img_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - img_i = mmcv.imresize( - img_i, (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i))) - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, img_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_img[y1_p:y2_p, x1_p:x2_p] = img_i[y1_c:y2_c, x1_c:x2_c] - - results['img'] = mosaic_img - results['img_shape'] = mosaic_img.shape - results['ori_shape'] = mosaic_img.shape - - return results - - def _mosaic_transform_seg(self, results): - """Mosaic transform function for label annotations. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - for key in results.get('seg_fields', []): - mosaic_seg = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.seg_pad_val, - dtype=results[key].dtype) - - # mosaic center x, y - center_position = (self.center_x, self.center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - result_patch = copy.deepcopy(results) - else: - result_patch = copy.deepcopy(results['mix_results'][i - 1]) - - gt_seg_i = result_patch[key] - h_i, w_i = gt_seg_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - gt_seg_i = mmcv.imresize( - gt_seg_i, - (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i)), - interpolation='nearest') - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, gt_seg_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_seg[y1_p:y2_p, x1_p:x2_p] = gt_seg_i[y1_c:y2_c, - x1_c:x2_c] - - results[key] = mosaic_seg - - return results - - def _mosaic_combine(self, loc, center_position_xy, img_shape_wh): - """Calculate global coordinate of mosaic image and local coordinate of - cropped sub-image. - - Args: - loc (str): Index for the sub-image, loc in ('top_left', - 'top_right', 'bottom_left', 'bottom_right'). - center_position_xy (Sequence[float]): Mixing center for 4 images, - (x, y). - img_shape_wh (Sequence[int]): Width and height of sub-image - - Returns: - tuple[tuple[float]]: Corresponding coordinate of pasting and - cropping - - paste_coord (tuple): paste corner coordinate in mosaic image. - - crop_coord (tuple): crop corner coordinate in mosaic image. - """ - - assert loc in ('top_left', 'top_right', 'bottom_left', 'bottom_right') - if loc == 'top_left': - # index0 to top left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - center_position_xy[0], \ - center_position_xy[1] - crop_coord = img_shape_wh[0] - (x2 - x1), img_shape_wh[1] - ( - y2 - y1), img_shape_wh[0], img_shape_wh[1] - - elif loc == 'top_right': - # index1 to top right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - center_position_xy[1] - crop_coord = 0, img_shape_wh[1] - (y2 - y1), min( - img_shape_wh[0], x2 - x1), img_shape_wh[1] - - elif loc == 'bottom_left': - # index2 to bottom left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - center_position_xy[1], \ - center_position_xy[0], \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = img_shape_wh[0] - (x2 - x1), 0, img_shape_wh[0], min( - y2 - y1, img_shape_wh[1]) - - else: - # index3 to bottom right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - center_position_xy[1], \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = 0, 0, min(img_shape_wh[0], - x2 - x1), min(y2 - y1, img_shape_wh[1]) - - paste_coord = x1, y1, x2, y2 - return paste_coord, crop_coord - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' - repr_str += f'img_scale={self.img_scale}, ' - repr_str += f'center_ratio_range={self.center_ratio_range}, ' - repr_str += f'pad_val={self.pad_val}, ' - repr_str += f'seg_pad_val={self.pad_val})' - return repr_str diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/samplers/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/samplers/__init__.py deleted file mode 100644 index da09effaf..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/samplers/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .distributed_sampler import DistributedSampler - -__all__ = ['DistributedSampler'] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/samplers/distributed_sampler.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/samplers/distributed_sampler.py deleted file mode 100644 index d1a13c716..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/samplers/distributed_sampler.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from __future__ import division -from typing import Iterator, Optional - -import torch -from torch.utils.data import Dataset -from torch.utils.data import DistributedSampler as _DistributedSampler - -from mmseg.core.utils import sync_random_seed - - -class DistributedSampler(_DistributedSampler): - """DistributedSampler inheriting from - `torch.utils.data.DistributedSampler`. - - Args: - datasets (Dataset): the dataset will be loaded. - num_replicas (int, optional): Number of processes participating in - distributed training. By default, world_size is retrieved from the - current distributed group. - rank (int, optional): Rank of the current process within num_replicas. - By default, rank is retrieved from the current distributed group. - shuffle (bool): If True (default), sampler will shuffle the indices. - seed (int): random seed used to shuffle the sampler if - :attr:`shuffle=True`. This number should be identical across all - processes in the distributed group. Default: ``0``. - """ - - def __init__(self, - dataset: Dataset, - num_replicas: Optional[int] = None, - rank: Optional[int] = None, - shuffle: bool = True, - seed=0) -> None: - super().__init__( - dataset, num_replicas=num_replicas, rank=rank, shuffle=shuffle) - - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - - def __iter__(self) -> Iterator: - """ - Yields: - Iterator: iterator of indices for rank. - """ - # deterministically shuffle based on epoch - if self.shuffle: - g = torch.Generator() - # When :attr:`shuffle=True`, this ensures all replicas - # use a different random ordering for each epoch. - # Otherwise, the next iteration of this sampler will - # yield the same ordering. - g.manual_seed(self.epoch + self.seed) - indices = torch.randperm(len(self.dataset), generator=g).tolist() - else: - indices = torch.arange(len(self.dataset)).tolist() - - # add extra samples to make it evenly divisible - indices += indices[:(self.total_size - len(indices))] - assert len(indices) == self.total_size - - # subsample - indices = indices[self.rank:self.total_size:self.num_replicas] - assert len(indices) == self.num_samples - - return iter(indices) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/voc.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/voc.py deleted file mode 100644 index 9eecc344f..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/datasets/voc.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class PascalVOCDataset(CustomDataset): - """Pascal VOC dataset. - - Args: - split (str): Split txt file for Pascal VOC. - """ - - CLASSES = ('background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', - 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', - 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', - 'train', 'tvmonitor') - - PALETTE = [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], - [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], - [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], - [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], - [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] - - def __init__(self, split, **kwargs): - - if "img_dir" in kwargs: - image_dir = kwargs["img_dir"] - if not osp.join(kwargs['data_root'], image_dir): - image_dir = "images" - if osp.join(kwargs['data_root'], image_dir): - kwargs["img_dir"] = image_dir - - super(PascalVOCDataset, self).__init__( - img_suffix='.jpg', seg_map_suffix='.png', split=split, **kwargs) - - assert osp.exists(self.img_dir) and self.split is not None diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/__init__.py deleted file mode 100644 index 87d8108e3..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .backbones import * # noqa: F401,F403 -from .builder import (BACKBONES, HEADS, LOSSES, SEGMENTORS, build_backbone, - build_head, build_loss, build_segmentor) -from .decode_heads import * # noqa: F401,F403 -from .losses import * # noqa: F401,F403 -from .necks import * # noqa: F401,F403 -from .segmentors import * # noqa: F401,F403 - -__all__ = [ - 'BACKBONES', 'HEADS', 'LOSSES', 'SEGMENTORS', 'build_backbone', - 'build_head', 'build_loss', 'build_segmentor' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/__init__.py deleted file mode 100644 index 5c8cd5078..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .resnest import ResNeSt -from .resnet import ResNet, ResNetV1c, ResNetV1d -from .resnext import ResNeXt -from .bisenetv1 import AttentionRefinementModule -from .ddrnet import DDRNet diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/bisenetv1.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/bisenetv1.py deleted file mode 100644 index 4beb7b394..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/bisenetv1.py +++ /dev/null @@ -1,332 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import BACKBONES, build_backbone - - -class SpatialPath(BaseModule): - """Spatial Path to preserve the spatial size of the original input image - and encode affluent spatial information. - - Args: - in_channels(int): The number of channels of input - image. Default: 3. - num_channels (Tuple[int]): The number of channels of - each layers in Spatial Path. - Default: (64, 64, 64, 128). - Returns: - x (torch.Tensor): Feature map for Feature Fusion Module. - """ - - def __init__(self, - in_channels=3, - num_channels=(64, 64, 64, 128), - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(SpatialPath, self).__init__(init_cfg=init_cfg) - assert len(num_channels) == 4, 'Length of input channels \ - of Spatial Path must be 4!' - - self.layers = [] - for i in range(len(num_channels)): - layer_name = f'layer{i + 1}' - self.layers.append(layer_name) - if i == 0: - self.add_module( - layer_name, - ConvModule( - in_channels=in_channels, - out_channels=num_channels[i], - kernel_size=7, - stride=2, - padding=3, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - elif i == len(num_channels) - 1: - self.add_module( - layer_name, - ConvModule( - in_channels=num_channels[i - 1], - out_channels=num_channels[i], - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - else: - self.add_module( - layer_name, - ConvModule( - in_channels=num_channels[i - 1], - out_channels=num_channels[i], - kernel_size=3, - stride=2, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, x): - for i, layer_name in enumerate(self.layers): - layer_stage = getattr(self, layer_name) - x = layer_stage(x) - return x - - -class AttentionRefinementModule(BaseModule): - """Attention Refinement Module (ARM) to refine the features of each stage. - - Args: - in_channels (int): The number of input channels. - out_channels (int): The number of output channels. - Returns: - x_out (torch.Tensor): Feature map of Attention Refinement Module. - """ - - def __init__(self, - in_channels, - out_channel, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(AttentionRefinementModule, self).__init__(init_cfg=init_cfg) - self.conv_layer = ConvModule( - in_channels=in_channels, - out_channels=out_channel, - kernel_size=3, - stride=1, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.atten_conv_layer = nn.Sequential( - nn.AdaptiveAvgPool2d((1, 1)), - ConvModule( - in_channels=out_channel, - out_channels=out_channel, - kernel_size=1, - bias=False, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None), nn.Sigmoid()) - - def forward(self, x): - x = self.conv_layer(x) - x_atten = self.atten_conv_layer(x) - x_out = x * x_atten - return x_out - - -class ContextPath(BaseModule): - """Context Path to provide sufficient receptive field. - - Args: - backbone_cfg:(dict): Config of backbone of - Context Path. - context_channels (Tuple[int]): The number of channel numbers - of various modules in Context Path. - Default: (128, 256, 512). - align_corners (bool, optional): The align_corners argument of - resize operation. Default: False. - Returns: - x_16_up, x_32_up (torch.Tensor, torch.Tensor): Two feature maps - undergoing upsampling from 1/16 and 1/32 downsampling - feature maps. These two feature maps are used for Feature - Fusion Module and Auxiliary Head. - """ - - def __init__(self, - backbone_cfg, - context_channels=(128, 256, 512), - align_corners=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(ContextPath, self).__init__(init_cfg=init_cfg) - assert len(context_channels) == 3, 'Length of input channels \ - of Context Path must be 3!' - - self.backbone = build_backbone(backbone_cfg) - - self.align_corners = align_corners - self.arm16 = AttentionRefinementModule(context_channels[1], - context_channels[0]) - self.arm32 = AttentionRefinementModule(context_channels[2], - context_channels[0]) - self.conv_head32 = ConvModule( - in_channels=context_channels[0], - out_channels=context_channels[0], - kernel_size=3, - stride=1, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.conv_head16 = ConvModule( - in_channels=context_channels[0], - out_channels=context_channels[0], - kernel_size=3, - stride=1, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.gap_conv = nn.Sequential( - nn.AdaptiveAvgPool2d((1, 1)), - ConvModule( - in_channels=context_channels[2], - out_channels=context_channels[0], - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, x): - x_4, x_8, x_16, x_32 = self.backbone(x) - x_gap = self.gap_conv(x_32) - - x_32_arm = self.arm32(x_32) - x_32_sum = x_32_arm + x_gap - x_32_up = resize(input=x_32_sum, size=x_16.shape[2:], mode='nearest') - x_32_up = self.conv_head32(x_32_up) - - x_16_arm = self.arm16(x_16) - x_16_sum = x_16_arm + x_32_up - x_16_up = resize(input=x_16_sum, size=x_8.shape[2:], mode='nearest') - x_16_up = self.conv_head16(x_16_up) - - return x_16_up, x_32_up - - -class FeatureFusionModule(BaseModule): - """Feature Fusion Module to fuse low level output feature of Spatial Path - and high level output feature of Context Path. - - Args: - in_channels (int): The number of input channels. - out_channels (int): The number of output channels. - Returns: - x_out (torch.Tensor): Feature map of Feature Fusion Module. - """ - - def __init__(self, - in_channels, - out_channels, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(FeatureFusionModule, self).__init__(init_cfg=init_cfg) - self.conv1 = ConvModule( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.gap = nn.AdaptiveAvgPool2d((1, 1)) - self.conv_atten = nn.Sequential( - ConvModule( - in_channels=out_channels, - out_channels=out_channels, - kernel_size=1, - stride=1, - padding=0, - bias=False, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg), nn.Sigmoid()) - - def forward(self, x_sp, x_cp): - x_concat = torch.cat([x_sp, x_cp], dim=1) - x_fuse = self.conv1(x_concat) - x_atten = self.gap(x_fuse) - # Note: No BN and more 1x1 conv in paper. - x_atten = self.conv_atten(x_atten) - x_atten = x_fuse * x_atten - x_out = x_atten + x_fuse - return x_out - - -@BACKBONES.register_module() -class BiSeNetV1(BaseModule): - """BiSeNetV1 backbone. - - This backbone is the implementation of `BiSeNet: Bilateral - Segmentation Network for Real-time Semantic - Segmentation `_. - - Args: - backbone_cfg:(dict): Config of backbone of - Context Path. - in_channels (int): The number of channels of input - image. Default: 3. - spatial_channels (Tuple[int]): Size of channel numbers of - various layers in Spatial Path. - Default: (64, 64, 64, 128). - context_channels (Tuple[int]): Size of channel numbers of - various modules in Context Path. - Default: (128, 256, 512). - out_indices (Tuple[int] | int, optional): Output from which stages. - Default: (0, 1, 2). - align_corners (bool, optional): The align_corners argument of - resize operation in Bilateral Guided Aggregation Layer. - Default: False. - out_channels(int): The number of channels of output. - It must be the same with `in_channels` of decode_head. - Default: 256. - """ - - def __init__(self, - backbone_cfg, - in_channels=3, - spatial_channels=(64, 64, 64, 128), - context_channels=(128, 256, 512), - out_indices=(0, 1, 2), - align_corners=False, - out_channels=256, - conv_cfg=None, - norm_cfg=dict(type='BN', requires_grad=True), - act_cfg=dict(type='ReLU'), - init_cfg=None): - - super(BiSeNetV1, self).__init__(init_cfg=init_cfg) - assert len(spatial_channels) == 4, 'Length of input channels \ - of Spatial Path must be 4!' - - assert len(context_channels) == 3, 'Length of input channels \ - of Context Path must be 3!' - - self.out_indices = out_indices - self.align_corners = align_corners - self.context_path = ContextPath(backbone_cfg, context_channels, - self.align_corners) - self.spatial_path = SpatialPath(in_channels, spatial_channels) - self.ffm = FeatureFusionModule(context_channels[1], out_channels) - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - - def forward(self, x): - # stole refactoring code from Coin Cheung, thanks - x_context8, x_context16 = self.context_path(x) - x_spatial = self.spatial_path(x) - x_fuse = self.ffm(x_spatial, x_context8) - - outs = [x_fuse, x_context8, x_context16] - outs = [outs[i] for i in self.out_indices] - return tuple(outs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/ddrnet.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/ddrnet.py deleted file mode 100644 index ca9ad6bfc..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/ddrnet.py +++ /dev/null @@ -1,363 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.runner import load_checkpoint -from mmcv.utils.parrots_wrapper import _BatchNorm - -from mmseg.utils import get_root_logger -from ..builder import BACKBONES - -BatchNorm2d = nn.SyncBatchNorm -bn_mom = 0.1 - - -def conv3x3(in_planes, out_planes, stride=1): - """3x3 convolution with padding""" - - return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, - padding=1, bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, inplanes, planes, stride=1, downsample=None, no_relu=False): - super(BasicBlock, self).__init__() - - self.conv1 = conv3x3(inplanes, planes, stride) - self.bn1 = BatchNorm2d(planes, momentum=bn_mom) - self.relu = nn.ReLU(inplace=True) - self.conv2 = conv3x3(planes, planes) - self.bn2 = BatchNorm2d(planes, momentum=bn_mom) - self.downsample = downsample - self.stride = stride - self.no_relu = no_relu - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - - if self.no_relu: - return out - else: - return self.relu(out) - - -class Bottleneck(nn.Module): - expansion = 2 - - def __init__(self, inplanes, planes, stride=1, downsample=None, no_relu=True): - super(Bottleneck, self).__init__() - - self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) - self.bn1 = BatchNorm2d(planes, momentum=bn_mom) - self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, - padding=1, bias=False) - self.bn2 = BatchNorm2d(planes, momentum=bn_mom) - self.conv3 = nn.Conv2d(planes, planes * self.expansion, kernel_size=1, - bias=False) - self.bn3 = BatchNorm2d(planes * self.expansion, momentum=bn_mom) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.no_relu = no_relu - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - if self.no_relu: - return out - else: - return self.relu(out) - - -class DAPPM(nn.Module): - def __init__(self, inplanes, branch_planes, out_planes): - super(DAPPM, self).__init__() - - self.scale0 = nn.Sequential( - BatchNorm2d(inplanes, momentum=bn_mom), - nn.ReLU(inplace=True), - nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False) - ) - self.scale1 = nn.Sequential( - nn.AvgPool2d(kernel_size=5, stride=2, padding=2), - BatchNorm2d(inplanes, momentum=bn_mom), - nn.ReLU(inplace=True), - nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False) - ) - self.scale2 = nn.Sequential( - nn.AvgPool2d(kernel_size=9, stride=4, padding=4), - BatchNorm2d(inplanes, momentum=bn_mom), - nn.ReLU(inplace=True), - nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False) - ) - self.scale3 = nn.Sequential( - nn.AvgPool2d(kernel_size=17, stride=8, padding=8), - BatchNorm2d(inplanes, momentum=bn_mom), - nn.ReLU(inplace=True), - nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False) - ) - self.scale4 = nn.Sequential( - nn.AdaptiveAvgPool2d((1, 1)), - BatchNorm2d(inplanes, momentum=bn_mom), - nn.ReLU(inplace=True), - nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False) - ) - - self.process1 = nn.Sequential( - BatchNorm2d(branch_planes, momentum=bn_mom), - nn.ReLU(inplace=True), - nn.Conv2d(branch_planes, branch_planes, kernel_size=3, padding=1, bias=False) - ) - self.process2 = nn.Sequential( - BatchNorm2d(branch_planes, momentum=bn_mom), - nn.ReLU(inplace=True), - nn.Conv2d(branch_planes, branch_planes, kernel_size=3, padding=1, bias=False) - ) - self.process3 = nn.Sequential( - BatchNorm2d(branch_planes, momentum=bn_mom), - nn.ReLU(inplace=True), - nn.Conv2d(branch_planes, branch_planes, kernel_size=3, padding=1, bias=False) - ) - self.process4 = nn.Sequential( - BatchNorm2d(branch_planes, momentum=bn_mom), - nn.ReLU(inplace=True), - nn.Conv2d(branch_planes, branch_planes, kernel_size=3, padding=1, bias=False), - ) - self.compression = nn.Sequential( - BatchNorm2d(branch_planes * 5, momentum=bn_mom), - nn.ReLU(inplace=True), - nn.Conv2d(branch_planes * 5, out_planes, kernel_size=1, bias=False) - ) - - self.shortcut = nn.Sequential( - BatchNorm2d(inplanes, momentum=bn_mom), - nn.ReLU(inplace=True), - nn.Conv2d(inplanes, out_planes, kernel_size=1, bias=False) - ) - - def forward(self, x): - width = x.shape[-1] - height = x.shape[-2] - - y_list = [] - - y = self.scale0(x) - y_list.append(y) - - y_scaled1 = F.interpolate(self.scale1(x), size=[height, width], mode='bilinear') - y = self.process1(y_scaled1 + y) - y_list.append(y) - - y_scaled2 = F.interpolate(self.scale2(x), size=[height, width], mode='bilinear') - y = self.process2(y_scaled2 + y) - y_list.append(y) - - y_scaled3 = F.interpolate(self.scale3(x), size=[height, width], mode='bilinear') - y = self.process3(y_scaled3 + y) - y_list.append(y) - - y_scaled4 = F.interpolate(self.scale4(x), size=[height, width], mode='bilinear') - y = self.process4(y_scaled4 + y) - y_list.append(y) - - out = self.compression(torch.cat(y_list, 1)) + self.shortcut(x) - - return out - - -@BACKBONES.register_module() -class DDRNet(nn.Module): - """DDRNet backbone. - `Deep Dual-resolution Networks for Real-time and Accurate Semantic Segmentation of Road Scenes - `_ - """ - - def __init__(self, - extra, - in_channels=3, - conv_cfg=None, - norm_cfg=dict(type='BN'), - norm_eval=False): - super().__init__() - - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.norm_eval = norm_eval - - self.extra = extra - self.layers = self.extra['layers'] - self.planes = self.extra['planes'] - self.spp_planes = self.extra['spp_planes'] - - planes = self.planes - highres_planes = planes * 2 - block = BasicBlock - - self.conv1 = nn.Sequential( - nn.Conv2d(in_channels, planes, kernel_size=3, stride=2, padding=1), - BatchNorm2d(planes, momentum=bn_mom), - nn.ReLU(inplace=True), - nn.Conv2d(planes, planes, kernel_size=3, stride=2, padding=1), - BatchNorm2d(planes, momentum=bn_mom), - nn.ReLU(inplace=True), - ) - - self.relu = nn.ReLU(inplace=False) - self.layer1 = self._make_layer(block, planes, planes, self.layers[0]) - self.layer2 = self._make_layer(block, planes, planes * 2, self.layers[1], stride=2) - self.layer3 = self._make_layer(block, planes * 2, planes * 4, self.layers[2], stride=2) - self.layer4 = self._make_layer(block, planes * 4, planes * 8, self.layers[3], stride=2) - - self.compression3 = nn.Sequential( - nn.Conv2d(planes * 4, highres_planes, kernel_size=1, bias=False), - BatchNorm2d(highres_planes, momentum=bn_mom) - ) - self.compression4 = nn.Sequential( - nn.Conv2d(planes * 8, highres_planes, kernel_size=1, bias=False), - BatchNorm2d(highres_planes, momentum=bn_mom) - ) - - self.down3 = nn.Sequential( - nn.Conv2d(highres_planes, planes * 4, kernel_size=3, stride=2, padding=1, bias=False), - BatchNorm2d(planes * 4, momentum=bn_mom) - ) - self.down4 = nn.Sequential( - nn.Conv2d(highres_planes, planes * 4, kernel_size=3, stride=2, padding=1, bias=False), - BatchNorm2d(planes * 4, momentum=bn_mom), - nn.ReLU(inplace=True), - nn.Conv2d(planes * 4, planes * 8, kernel_size=3, stride=2, padding=1, bias=False), - BatchNorm2d(planes * 8, momentum=bn_mom) - ) - - self.layer3_ = self._make_layer(block, planes * 2, highres_planes, 2) - self.layer4_ = self._make_layer(block, highres_planes, highres_planes, 2) - self.layer5_ = self._make_layer(Bottleneck, highres_planes, highres_planes, 1) - self.layer5 = self._make_layer(Bottleneck, planes * 8, planes * 8, 1, stride=2) - - self.spp = DAPPM(planes * 16, self.spp_planes, planes * 4) - - @staticmethod - def _make_layer(block, inplanes, planes, blocks, stride=1): - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = nn.Sequential( - nn.Conv2d(inplanes, planes * block.expansion, - kernel_size=1, stride=stride, bias=False), - nn.BatchNorm2d(planes * block.expansion, momentum=bn_mom), - ) - - layers = [block(inplanes, planes, stride, downsample)] - inplanes = planes * block.expansion - for i in range(1, blocks): - if i == (blocks-1): - layers.append(block(inplanes, planes, stride=1, no_relu=True)) - else: - layers.append(block(inplanes, planes, stride=1, no_relu=False)) - - return nn.Sequential(*layers) - - def init_weights(self, pretrained=None): - """Initialize the weights in backbone. - Args: - pretrained (str, optional): Path to pre-trained weights. - Defaults to None. - """ - - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') - elif isinstance(m, BatchNorm2d): - nn.init.constant_(m.weight, 1) - nn.init.constant_(m.bias, 0) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - width_output = x.shape[-1] // 8 - height_output = x.shape[-2] // 8 - - x = self.conv1(x) - - x = self.layer1(x) - layers = [x] - - x = self.layer2(self.relu(x)) - layers.append(x) - - x = self.layer3(self.relu(x)) - layers.append(x) - x_ = self.layer3_(self.relu(layers[1])) - - x = x + self.down3(self.relu(x_)) - x_ = x_ + F.interpolate( - self.compression3(self.relu(layers[2])), - size=[height_output, width_output], - mode='bilinear' - ) - y_list = [x_] - - x = self.layer4(self.relu(x)) - layers.append(x) - x_ = self.layer4_(self.relu(x_)) - - x = x + self.down4(self.relu(x_)) - x_ = x_ + F.interpolate( - self.compression4(self.relu(layers[3])), - size=[height_output, width_output], - mode='bilinear' - ) - - x_ = self.layer5_(self.relu(x_)) - x = F.interpolate( - self.spp(self.layer5(self.relu(x))), - size=[height_output, width_output], - mode='bilinear' - ) - - out = x + x_ - y_list.append(out) - - return y_list - - def train(self, mode=True): - """Convert the model into training mode.""" - - super().train(mode) - - if mode and self.norm_eval: - for m in self.modules(): - if isinstance(m, _BatchNorm): - m.eval() \ No newline at end of file diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/resnest.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/resnest.py deleted file mode 100644 index 91952c2ca..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/resnest.py +++ /dev/null @@ -1,318 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import torch -import torch.nn as nn -import torch.nn.functional as F -import torch.utils.checkpoint as cp -from mmcv.cnn import build_conv_layer, build_norm_layer - -from ..builder import BACKBONES -from ..utils import ResLayer -from .resnet import Bottleneck as _Bottleneck -from .resnet import ResNetV1d - - -class RSoftmax(nn.Module): - """Radix Softmax module in ``SplitAttentionConv2d``. - - Args: - radix (int): Radix of input. - groups (int): Groups of input. - """ - - def __init__(self, radix, groups): - super().__init__() - self.radix = radix - self.groups = groups - - def forward(self, x): - batch = x.size(0) - if self.radix > 1: - x = x.view(batch, self.groups, self.radix, -1).transpose(1, 2) - x = F.softmax(x, dim=1) - x = x.reshape(batch, -1) - else: - x = torch.sigmoid(x) - return x - - -class SplitAttentionConv2d(nn.Module): - """Split-Attention Conv2d in ResNeSt. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int | tuple[int]): Same as nn.Conv2d. - stride (int | tuple[int]): Same as nn.Conv2d. - padding (int | tuple[int]): Same as nn.Conv2d. - dilation (int | tuple[int]): Same as nn.Conv2d. - groups (int): Same as nn.Conv2d. - radix (int): Radix of SpltAtConv2d. Default: 2 - reduction_factor (int): Reduction factor of inter_channels. Default: 4. - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - dcn (dict): Config dict for DCN. Default: None. - """ - - def __init__(self, - in_channels, - channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - radix=2, - reduction_factor=4, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None): - super(SplitAttentionConv2d, self).__init__() - inter_channels = max(in_channels * radix // reduction_factor, 32) - self.radix = radix - self.groups = groups - self.channels = channels - self.with_dcn = dcn is not None - self.dcn = dcn - fallback_on_stride = False - if self.with_dcn: - fallback_on_stride = self.dcn.pop('fallback_on_stride', False) - if self.with_dcn and not fallback_on_stride: - assert conv_cfg is None, 'conv_cfg must be None for DCN' - conv_cfg = dcn - self.conv = build_conv_layer( - conv_cfg, - in_channels, - channels * radix, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups * radix, - bias=False) - self.norm0_name, norm0 = build_norm_layer( - norm_cfg, channels * radix, postfix=0) - self.add_module(self.norm0_name, norm0) - self.relu = nn.ReLU(inplace=True) - self.fc1 = build_conv_layer( - None, channels, inter_channels, 1, groups=self.groups) - self.norm1_name, norm1 = build_norm_layer( - norm_cfg, inter_channels, postfix=1) - self.add_module(self.norm1_name, norm1) - self.fc2 = build_conv_layer( - None, inter_channels, channels * radix, 1, groups=self.groups) - self.rsoftmax = RSoftmax(radix, groups) - - @property - def norm0(self): - """nn.Module: the normalization layer named "norm0" """ - return getattr(self, self.norm0_name) - - @property - def norm1(self): - """nn.Module: the normalization layer named "norm1" """ - return getattr(self, self.norm1_name) - - def forward(self, x): - x = self.conv(x) - x = self.norm0(x) - x = self.relu(x) - - batch, rchannel = x.shape[:2] - batch = x.size(0) - if self.radix > 1: - splits = x.view(batch, self.radix, -1, *x.shape[2:]) - gap = splits.sum(dim=1) - else: - gap = x - gap = F.adaptive_avg_pool2d(gap, 1) - gap = self.fc1(gap) - - gap = self.norm1(gap) - gap = self.relu(gap) - - atten = self.fc2(gap) - atten = self.rsoftmax(atten).view(batch, -1, 1, 1) - - if self.radix > 1: - attens = atten.view(batch, self.radix, -1, *atten.shape[2:]) - out = torch.sum(attens * splits, dim=1) - else: - out = atten * x - return out.contiguous() - - -class Bottleneck(_Bottleneck): - """Bottleneck block for ResNeSt. - - Args: - inplane (int): Input planes of this block. - planes (int): Middle planes of this block. - groups (int): Groups of conv2. - width_per_group (int): Width per group of conv2. 64x4d indicates - ``groups=64, width_per_group=4`` and 32x8d indicates - ``groups=32, width_per_group=8``. - radix (int): Radix of SpltAtConv2d. Default: 2 - reduction_factor (int): Reduction factor of inter_channels in - SplitAttentionConv2d. Default: 4. - avg_down_stride (bool): Whether to use average pool for stride in - Bottleneck. Default: True. - kwargs (dict): Key word arguments for base class. - """ - expansion = 4 - - def __init__(self, - inplanes, - planes, - groups=1, - base_width=4, - base_channels=64, - radix=2, - reduction_factor=4, - avg_down_stride=True, - **kwargs): - """Bottleneck block for ResNeSt.""" - super(Bottleneck, self).__init__(inplanes, planes, **kwargs) - - if groups == 1: - width = self.planes - else: - width = math.floor(self.planes * - (base_width / base_channels)) * groups - - self.avg_down_stride = avg_down_stride and self.conv2_stride > 1 - - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, width, postfix=1) - self.norm3_name, norm3 = build_norm_layer( - self.norm_cfg, self.planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - self.conv_cfg, - self.inplanes, - width, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - self.with_modulated_dcn = False - self.conv2 = SplitAttentionConv2d( - width, - width, - kernel_size=3, - stride=1 if self.avg_down_stride else self.conv2_stride, - padding=self.dilation, - dilation=self.dilation, - groups=groups, - radix=radix, - reduction_factor=reduction_factor, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - dcn=self.dcn) - delattr(self, self.norm2_name) - - if self.avg_down_stride: - self.avd_layer = nn.AvgPool2d(3, self.conv2_stride, padding=1) - - self.conv3 = build_conv_layer( - self.conv_cfg, - width, - self.planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - def forward(self, x): - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv1_plugin_names) - - out = self.conv2(out) - - if self.avg_down_stride: - out = self.avd_layer(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv2_plugin_names) - - out = self.conv3(out) - out = self.norm3(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv3_plugin_names) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -@BACKBONES.register_module() -class ResNeSt(ResNetV1d): - """ResNeSt backbone. - - This backbone is the implementation of `ResNeSt: - Split-Attention Networks `_. - - Args: - groups (int): Number of groups of Bottleneck. Default: 1 - base_width (int): Base width of Bottleneck. Default: 4 - radix (int): Radix of SpltAtConv2d. Default: 2 - reduction_factor (int): Reduction factor of inter_channels in - SplitAttentionConv2d. Default: 4. - avg_down_stride (bool): Whether to use average pool for stride in - Bottleneck. Default: True. - kwargs (dict): Keyword arguments for ResNet. - """ - - arch_settings = { - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)), - 200: (Bottleneck, (3, 24, 36, 3)) - } - - def __init__(self, - groups=1, - base_width=4, - radix=2, - reduction_factor=4, - avg_down_stride=True, - **kwargs): - self.groups = groups - self.base_width = base_width - self.radix = radix - self.reduction_factor = reduction_factor - self.avg_down_stride = avg_down_stride - super(ResNeSt, self).__init__(**kwargs) - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``.""" - return ResLayer( - groups=self.groups, - base_width=self.base_width, - base_channels=self.base_channels, - radix=self.radix, - reduction_factor=self.reduction_factor, - avg_down_stride=self.avg_down_stride, - **kwargs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/resnet.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/resnet.py deleted file mode 100644 index e8b961d5f..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/resnet.py +++ /dev/null @@ -1,714 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.utils.checkpoint as cp -from mmcv.cnn import build_conv_layer, build_norm_layer, build_plugin_layer -from mmcv.runner import BaseModule -from mmcv.utils.parrots_wrapper import _BatchNorm - -from ..builder import BACKBONES -from ..utils import ResLayer - - -class BasicBlock(BaseModule): - """Basic block for ResNet.""" - - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(BasicBlock, self).__init__(init_cfg) - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - 3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - self.add_module(self.norm1_name, norm1) - self.conv2 = build_conv_layer( - conv_cfg, planes, planes, 3, padding=1, bias=False) - self.add_module(self.norm2_name, norm2) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.norm2(out) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -class Bottleneck(BaseModule): - """Bottleneck block for ResNet. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if it is - "caffe", the stride-two layer is the first 1x1 conv layer. - """ - - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(Bottleneck, self).__init__(init_cfg) - assert style in ['pytorch', 'caffe'] - assert dcn is None or isinstance(dcn, dict) - assert plugins is None or isinstance(plugins, list) - if plugins is not None: - allowed_position = ['after_conv1', 'after_conv2', 'after_conv3'] - assert all(p['position'] in allowed_position for p in plugins) - - self.inplanes = inplanes - self.planes = planes - self.stride = stride - self.dilation = dilation - self.style = style - self.with_cp = with_cp - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.dcn = dcn - self.with_dcn = dcn is not None - self.plugins = plugins - self.with_plugins = plugins is not None - - if self.with_plugins: - # collect plugins for conv1/conv2/conv3 - self.after_conv1_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv1' - ] - self.after_conv2_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv2' - ] - self.after_conv3_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv3' - ] - - if self.style == 'pytorch': - self.conv1_stride = 1 - self.conv2_stride = stride - else: - self.conv1_stride = stride - self.conv2_stride = 1 - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - self.norm3_name, norm3 = build_norm_layer( - norm_cfg, planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - fallback_on_stride = False - if self.with_dcn: - fallback_on_stride = dcn.pop('fallback_on_stride', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = build_conv_layer( - conv_cfg, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - else: - assert self.conv_cfg is None, 'conv_cfg must be None for DCN' - self.conv2 = build_conv_layer( - dcn, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.add_module(self.norm2_name, norm2) - self.conv3 = build_conv_layer( - conv_cfg, - planes, - planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - - if self.with_plugins: - self.after_conv1_plugin_names = self.make_block_plugins( - planes, self.after_conv1_plugins) - self.after_conv2_plugin_names = self.make_block_plugins( - planes, self.after_conv2_plugins) - self.after_conv3_plugin_names = self.make_block_plugins( - planes * self.expansion, self.after_conv3_plugins) - - def make_block_plugins(self, in_channels, plugins): - """make plugins for block. - - Args: - in_channels (int): Input channels of plugin. - plugins (list[dict]): List of plugins cfg to build. - - Returns: - list[str]: List of the names of plugin. - """ - assert isinstance(plugins, list) - plugin_names = [] - for plugin in plugins: - plugin = plugin.copy() - name, layer = build_plugin_layer( - plugin, - in_channels=in_channels, - postfix=plugin.pop('postfix', '')) - assert not hasattr(self, name), f'duplicate plugin {name}' - self.add_module(name, layer) - plugin_names.append(name) - return plugin_names - - def forward_plugin(self, x, plugin_names): - """Forward function for plugins.""" - out = x - for name in plugin_names: - out = getattr(self, name)(x) - return out - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - @property - def norm3(self): - """nn.Module: normalization layer after the third convolution layer""" - return getattr(self, self.norm3_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv1_plugin_names) - - out = self.conv2(out) - out = self.norm2(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv2_plugin_names) - - out = self.conv3(out) - out = self.norm3(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv3_plugin_names) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -@BACKBONES.register_module() -class ResNet(BaseModule): - """ResNet backbone. - - This backbone is the improved implementation of `Deep Residual Learning - for Image Recognition `_. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - in_channels (int): Number of input image channels. Default: 3. - stem_channels (int): Number of stem channels. Default: 64. - base_channels (int): Number of base channels of res layer. Default: 64. - num_stages (int): Resnet stages, normally 4. Default: 4. - strides (Sequence[int]): Strides of the first block of each stage. - Default: (1, 2, 2, 2). - dilations (Sequence[int]): Dilation of each stage. - Default: (1, 1, 1, 1). - out_indices (Sequence[int]): Output from which stages. - Default: (0, 1, 2, 3). - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. Default: 'pytorch'. - deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv. - Default: False. - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False. - frozen_stages (int): Stages to be frozen (stop grad and set eval mode). - -1 means not freezing any parameters. Default: -1. - conv_cfg (dict | None): Dictionary to construct and config conv layer. - When conv_cfg is None, cfg will be set to dict(type='Conv2d'). - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN', requires_grad=True). - norm_eval (bool): Whether to set norm layers to eval mode, namely, - freeze running stats (mean and var). Note: Effect on Batch Norm - and its variants only. Default: False. - dcn (dict | None): Dictionary to construct and config DCN conv layer. - When dcn is not None, conv_cfg must be None. Default: None. - stage_with_dcn (Sequence[bool]): Whether to set DCN conv for each - stage. The length of stage_with_dcn is equal to num_stages. - Default: (False, False, False, False). - plugins (list[dict]): List of plugins for stages, each dict contains: - - - cfg (dict, required): Cfg dict to build plugin. - - - position (str, required): Position inside block to insert plugin, - options: 'after_conv1', 'after_conv2', 'after_conv3'. - - - stages (tuple[bool], optional): Stages to apply plugin, length - should be same as 'num_stages'. - Default: None. - multi_grid (Sequence[int]|None): Multi grid dilation rates of last - stage. Default: None. - contract_dilation (bool): Whether contract first dilation of each layer - Default: False. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - zero_init_residual (bool): Whether to use zero init for last norm layer - in resblocks to let them behave as identity. Default: True. - pretrained (str, optional): model pretrained path. Default: None. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Example: - >>> from mmseg.models import ResNet - >>> import torch - >>> self = ResNet(depth=18) - >>> self.eval() - >>> inputs = torch.rand(1, 3, 32, 32) - >>> level_outputs = self.forward(inputs) - >>> for level_out in level_outputs: - ... print(tuple(level_out.shape)) - (1, 64, 8, 8) - (1, 128, 4, 4) - (1, 256, 2, 2) - (1, 512, 1, 1) - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - in_channels=3, - stem_channels=64, - base_channels=64, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - deep_stem=False, - avg_down=False, - frozen_stages=-1, - conv_cfg=None, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=False, - dcn=None, - stage_with_dcn=(False, False, False, False), - plugins=None, - multi_grid=None, - contract_dilation=False, - with_cp=False, - zero_init_residual=True, - pretrained=None, - init_cfg=None): - super(ResNet, self).__init__(init_cfg) - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - - self.pretrained = pretrained - self.zero_init_residual = zero_init_residual - block_init_cfg = None - assert not (init_cfg and pretrained), \ - 'init_cfg and pretrained cannot be setting at the same time' - if isinstance(pretrained, str): - warnings.warn('DeprecationWarning: pretrained is a deprecated, ' - 'please use "init_cfg" instead') - self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - elif pretrained is None: - if init_cfg is None: - self.init_cfg = [ - dict(type='Kaiming', layer='Conv2d'), - dict( - type='Constant', - val=1, - layer=['_BatchNorm', 'GroupNorm']) - ] - block = self.arch_settings[depth][0] - if self.zero_init_residual: - if block is BasicBlock: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm2')) - elif block is Bottleneck: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm3')) - else: - raise TypeError('pretrained must be a str or None') - - self.depth = depth - self.stem_channels = stem_channels - self.base_channels = base_channels - self.num_stages = num_stages - assert num_stages >= 1 and num_stages <= 4 - self.strides = strides - self.dilations = dilations - assert len(strides) == len(dilations) == num_stages - self.out_indices = out_indices - assert max(out_indices) < num_stages - self.style = style - self.deep_stem = deep_stem - self.avg_down = avg_down - self.frozen_stages = frozen_stages - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.with_cp = with_cp - self.norm_eval = norm_eval - self.dcn = dcn - self.stage_with_dcn = stage_with_dcn - if dcn is not None: - assert len(stage_with_dcn) == num_stages - self.plugins = plugins - self.multi_grid = multi_grid - self.contract_dilation = contract_dilation - self.block, stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - self.inplanes = stem_channels - - self._make_stem_layer(in_channels, stem_channels) - - self.res_layers = [] - for i, num_blocks in enumerate(self.stage_blocks): - stride = strides[i] - dilation = dilations[i] - dcn = self.dcn if self.stage_with_dcn[i] else None - if plugins is not None: - stage_plugins = self.make_stage_plugins(plugins, i) - else: - stage_plugins = None - # multi grid is applied to last layer only - stage_multi_grid = multi_grid if i == len( - self.stage_blocks) - 1 else None - planes = base_channels * 2**i - res_layer = self.make_res_layer( - block=self.block, - inplanes=self.inplanes, - planes=planes, - num_blocks=num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - avg_down=self.avg_down, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - dcn=dcn, - plugins=stage_plugins, - multi_grid=stage_multi_grid, - contract_dilation=contract_dilation, - init_cfg=block_init_cfg) - self.inplanes = planes * self.block.expansion - layer_name = f'layer{i+1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self._freeze_stages() - - self.feat_dim = self.block.expansion * base_channels * 2**( - len(self.stage_blocks) - 1) - - def make_stage_plugins(self, plugins, stage_idx): - """make plugins for ResNet 'stage_idx'th stage . - - Currently we support to insert 'context_block', - 'empirical_attention_block', 'nonlocal_block' into the backbone like - ResNet/ResNeXt. They could be inserted after conv1/conv2/conv3 of - Bottleneck. - - An example of plugins format could be : - >>> plugins=[ - ... dict(cfg=dict(type='xxx', arg1='xxx'), - ... stages=(False, True, True, True), - ... position='after_conv2'), - ... dict(cfg=dict(type='yyy'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='1'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='2'), - ... stages=(True, True, True, True), - ... position='after_conv3') - ... ] - >>> self = ResNet(depth=18) - >>> stage_plugins = self.make_stage_plugins(plugins, 0) - >>> assert len(stage_plugins) == 3 - - Suppose 'stage_idx=0', the structure of blocks in the stage would be: - conv1-> conv2->conv3->yyy->zzz1->zzz2 - Suppose 'stage_idx=1', the structure of blocks in the stage would be: - conv1-> conv2->xxx->conv3->yyy->zzz1->zzz2 - - If stages is missing, the plugin would be applied to all stages. - - Args: - plugins (list[dict]): List of plugins cfg to build. The postfix is - required if multiple same type plugins are inserted. - stage_idx (int): Index of stage to build - - Returns: - list[dict]: Plugins for current stage - """ - stage_plugins = [] - for plugin in plugins: - plugin = plugin.copy() - stages = plugin.pop('stages', None) - assert stages is None or len(stages) == self.num_stages - # whether to insert plugin into current stage - if stages is None or stages[stage_idx]: - stage_plugins.append(plugin) - - return stage_plugins - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``.""" - return ResLayer(**kwargs) - - @property - def norm1(self): - """nn.Module: the normalization layer named "norm1" """ - return getattr(self, self.norm1_name) - - def _make_stem_layer(self, in_channels, stem_channels): - """Make stem layer for ResNet.""" - if self.deep_stem: - self.stem = nn.Sequential( - build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels // 2, - kernel_size=3, - stride=2, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels // 2, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels)[1], - nn.ReLU(inplace=True)) - else: - self.conv1 = build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels, - kernel_size=7, - stride=2, - padding=3, - bias=False) - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, stem_channels, postfix=1) - self.add_module(self.norm1_name, norm1) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - def _freeze_stages(self): - """Freeze stages param and norm stats.""" - if self.frozen_stages >= 0: - if self.deep_stem: - self.stem.eval() - for param in self.stem.parameters(): - param.requires_grad = False - else: - self.norm1.eval() - for m in [self.conv1, self.norm1]: - for param in m.parameters(): - param.requires_grad = False - - for i in range(1, self.frozen_stages + 1): - m = getattr(self, f'layer{i}') - m.eval() - for param in m.parameters(): - param.requires_grad = False - - def forward(self, x): - """Forward function.""" - if self.deep_stem: - x = self.stem(x) - else: - x = self.conv1(x) - x = self.norm1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - return tuple(outs) - - def train(self, mode=True): - """Convert the model into training mode while keep normalization layer - freezed.""" - super(ResNet, self).train(mode) - self._freeze_stages() - if mode and self.norm_eval: - for m in self.modules(): - # trick: eval have effect on BatchNorm only - if isinstance(m, _BatchNorm): - m.eval() - - -@BACKBONES.register_module() -class ResNetV1c(ResNet): - """ResNetV1c variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1c replaces the 7x7 conv in - the input stem with three 3x3 convs. For more details please refer to `Bag - of Tricks for Image Classification with Convolutional Neural Networks - `_. - """ - - def __init__(self, **kwargs): - super(ResNetV1c, self).__init__( - deep_stem=True, avg_down=False, **kwargs) - - -@BACKBONES.register_module() -class ResNetV1d(ResNet): - """ResNetV1d variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1d replaces the 7x7 conv in - the input stem with three 3x3 convs. And in the downsampling block, a 2x2 - avg_pool with stride 2 is added before conv, whose stride is changed to 1. - """ - - def __init__(self, **kwargs): - super(ResNetV1d, self).__init__( - deep_stem=True, avg_down=True, **kwargs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/resnext.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/resnext.py deleted file mode 100644 index 805c27bf3..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/resnext.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from mmcv.cnn import build_conv_layer, build_norm_layer - -from ..builder import BACKBONES -from ..utils import ResLayer -from .resnet import Bottleneck as _Bottleneck -from .resnet import ResNet - - -class Bottleneck(_Bottleneck): - """Bottleneck block for ResNeXt. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if it is - "caffe", the stride-two layer is the first 1x1 conv layer. - """ - - def __init__(self, - inplanes, - planes, - groups=1, - base_width=4, - base_channels=64, - **kwargs): - super(Bottleneck, self).__init__(inplanes, planes, **kwargs) - - if groups == 1: - width = self.planes - else: - width = math.floor(self.planes * - (base_width / base_channels)) * groups - - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, width, postfix=1) - self.norm2_name, norm2 = build_norm_layer( - self.norm_cfg, width, postfix=2) - self.norm3_name, norm3 = build_norm_layer( - self.norm_cfg, self.planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - self.conv_cfg, - self.inplanes, - width, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - fallback_on_stride = False - self.with_modulated_dcn = False - if self.with_dcn: - fallback_on_stride = self.dcn.pop('fallback_on_stride', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = build_conv_layer( - self.conv_cfg, - width, - width, - kernel_size=3, - stride=self.conv2_stride, - padding=self.dilation, - dilation=self.dilation, - groups=groups, - bias=False) - else: - assert self.conv_cfg is None, 'conv_cfg must be None for DCN' - self.conv2 = build_conv_layer( - self.dcn, - width, - width, - kernel_size=3, - stride=self.conv2_stride, - padding=self.dilation, - dilation=self.dilation, - groups=groups, - bias=False) - - self.add_module(self.norm2_name, norm2) - self.conv3 = build_conv_layer( - self.conv_cfg, - width, - self.planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - -@BACKBONES.register_module() -class ResNeXt(ResNet): - """ResNeXt backbone. - - This backbone is the implementation of `Aggregated - Residual Transformations for Deep Neural - Networks `_. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - in_channels (int): Number of input image channels. Normally 3. - num_stages (int): Resnet stages, normally 4. - groups (int): Group of resnext. - base_width (int): Base width of resnext. - strides (Sequence[int]): Strides of the first block of each stage. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - norm_cfg (dict): dictionary to construct and config norm layer. - norm_eval (bool): Whether to set norm layers to eval mode, namely, - freeze running stats (mean and var). Note: Effect on Batch Norm - and its variants only. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. - zero_init_residual (bool): whether to use zero init for last norm layer - in resblocks to let them behave as identity. - - Example: - >>> from mmseg.models import ResNeXt - >>> import torch - >>> self = ResNeXt(depth=50) - >>> self.eval() - >>> inputs = torch.rand(1, 3, 32, 32) - >>> level_outputs = self.forward(inputs) - >>> for level_out in level_outputs: - ... print(tuple(level_out.shape)) - (1, 256, 8, 8) - (1, 512, 4, 4) - (1, 1024, 2, 2) - (1, 2048, 1, 1) - """ - - arch_settings = { - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, groups=1, base_width=4, **kwargs): - self.groups = groups - self.base_width = base_width - super(ResNeXt, self).__init__(**kwargs) - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``""" - return ResLayer( - groups=self.groups, - base_width=self.base_width, - base_channels=self.base_channels, - **kwargs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/timm_backbone.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/timm_backbone.py deleted file mode 100644 index 01b29fc5e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/backbones/timm_backbone.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -try: - import timm -except ImportError: - timm = None - -from mmcv.cnn.bricks.registry import NORM_LAYERS -from mmcv.runner import BaseModule - -from ..builder import BACKBONES - - -@BACKBONES.register_module() -class TIMMBackbone(BaseModule): - """Wrapper to use backbones from timm library. More details can be found in - `timm `_ . - - Args: - model_name (str): Name of timm model to instantiate. - pretrained (bool): Load pretrained weights if True. - checkpoint_path (str): Path of checkpoint to load after - model is initialized. - in_channels (int): Number of input image channels. Default: 3. - init_cfg (dict, optional): Initialization config dict - **kwargs: Other timm & model specific arguments. - """ - - def __init__( - self, - model_name, - features_only=True, - pretrained=True, - checkpoint_path='', - in_channels=3, - init_cfg=None, - **kwargs, - ): - if timm is None: - raise RuntimeError('timm is not installed') - super(TIMMBackbone, self).__init__(init_cfg) - if 'norm_layer' in kwargs: - kwargs['norm_layer'] = NORM_LAYERS.get(kwargs['norm_layer']) - self.timm_model = timm.create_model( - model_name=model_name, - features_only=features_only, - pretrained=pretrained, - in_chans=in_channels, - checkpoint_path=checkpoint_path, - **kwargs, - ) - - # Make unused parameters None - self.timm_model.global_pool = None - self.timm_model.fc = None - self.timm_model.classifier = None - - # Hack to use pretrained weights from timm - if pretrained or checkpoint_path: - self._is_init = True - - def forward(self, x): - features = self.timm_model(x) - return features diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/builder.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/builder.py deleted file mode 100644 index 5e18e4e64..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/builder.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -from mmcv.cnn import MODELS as MMCV_MODELS -from mmcv.cnn.bricks.registry import ATTENTION as MMCV_ATTENTION -from mmcv.utils import Registry - -MODELS = Registry('models', parent=MMCV_MODELS) -ATTENTION = Registry('attention', parent=MMCV_ATTENTION) - -BACKBONES = MODELS -NECKS = MODELS -HEADS = MODELS -LOSSES = MODELS -SEGMENTORS = MODELS - - -def build_backbone(cfg): - """Build backbone.""" - return BACKBONES.build(cfg) - - -def build_neck(cfg): - """Build neck.""" - return NECKS.build(cfg) - - -def build_head(cfg): - """Build head.""" - return HEADS.build(cfg) - - -def build_loss(cfg): - """Build loss.""" - return LOSSES.build(cfg) - - -def build_segmentor(cfg, train_cfg=None, test_cfg=None): - """Build segmentor.""" - if train_cfg is not None or test_cfg is not None: - warnings.warn( - 'train_cfg and test_cfg is deprecated, ' - 'please specify them in model', UserWarning) - assert cfg.get('train_cfg') is None or train_cfg is None, \ - 'train_cfg specified in both outer field and model field ' - assert cfg.get('test_cfg') is None or test_cfg is None, \ - 'test_cfg specified in both outer field and model field ' - return SEGMENTORS.build( - cfg, default_args=dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/__init__.py deleted file mode 100644 index 38e359606..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .aspp_head import ASPPHead -from .cc_head import CCHead -from .decode_head import BaseDecodeHead -from .fcn_head import FCNHead -from .ddr_head import DDRHead \ No newline at end of file diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/aspp_head.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/aspp_head.py deleted file mode 100644 index 7059aee96..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/aspp_head.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule - -from mmseg.ops import resize -from ..builder import HEADS -from .decode_head import BaseDecodeHead - - -class ASPPModule(nn.ModuleList): - """Atrous Spatial Pyramid Pooling (ASPP) Module. - - Args: - dilations (tuple[int]): Dilation rate of each layer. - in_channels (int): Input channels. - channels (int): Channels after modules, before conv_seg. - conv_cfg (dict|None): Config of conv layers. - norm_cfg (dict|None): Config of norm layers. - act_cfg (dict): Config of activation layers. - """ - - def __init__(self, dilations, in_channels, channels, conv_cfg, norm_cfg, - act_cfg): - super(ASPPModule, self).__init__() - self.dilations = dilations - self.in_channels = in_channels - self.channels = channels - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - for dilation in dilations: - self.append( - ConvModule( - self.in_channels, - self.channels, - 1 if dilation == 1 else 3, - dilation=dilation, - padding=0 if dilation == 1 else dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - - def forward(self, x): - """Forward function.""" - aspp_outs = [] - for aspp_module in self: - aspp_outs.append(aspp_module(x)) - - return aspp_outs - - -@HEADS.register_module() -class ASPPHead(BaseDecodeHead): - """Rethinking Atrous Convolution for Semantic Image Segmentation. - - This head is the implementation of `DeepLabV3 - `_. - - Args: - dilations (tuple[int]): Dilation rates for ASPP module. - Default: (1, 6, 12, 18). - """ - - def __init__(self, dilations=(1, 6, 12, 18), **kwargs): - super(ASPPHead, self).__init__(**kwargs) - assert isinstance(dilations, (list, tuple)) - self.dilations = dilations - self.image_pool = nn.Sequential( - nn.AdaptiveAvgPool2d(1), - ConvModule( - self.in_channels, - self.channels, - 1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - self.aspp_modules = ASPPModule( - dilations, - self.in_channels, - self.channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - self.bottleneck = ConvModule( - (len(dilations) + 1) * self.channels, - self.channels, - 3, - padding=1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - def _forward_feature(self, inputs): - """Forward function for feature maps before classifying each pixel with - ``self.cls_seg`` fc. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - feats (Tensor): A tensor of shape (batch_size, self.channels, - H, W) which is feature map for last layer of decoder head. - """ - x = self._transform_inputs(inputs) - aspp_outs = [ - resize( - self.image_pool(x), - size=x.size()[2:], - mode='bilinear', - align_corners=self.align_corners) - ] - aspp_outs.extend(self.aspp_modules(x)) - aspp_outs = torch.cat(aspp_outs, dim=1) - feats = self.bottleneck(aspp_outs) - return feats - - def forward(self, inputs): - """Forward function.""" - output = self._forward_feature(inputs) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/cc_head.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/cc_head.py deleted file mode 100644 index ed19eb46d..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/cc_head.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import HEADS -from .fcn_head import FCNHead - -try: - from mmcv.ops import CrissCrossAttention -except ModuleNotFoundError: - CrissCrossAttention = None - - -@HEADS.register_module() -class CCHead(FCNHead): - """CCNet: Criss-Cross Attention for Semantic Segmentation. - - This head is the implementation of `CCNet - `_. - - Args: - recurrence (int): Number of recurrence of Criss Cross Attention - module. Default: 2. - """ - - def __init__(self, recurrence=2, **kwargs): - if CrissCrossAttention is None: - raise RuntimeError('Please install mmcv-full for ' - 'CrissCrossAttention ops') - super(CCHead, self).__init__(num_convs=2, **kwargs) - self.recurrence = recurrence - self.cca = CrissCrossAttention(self.channels) - - def forward(self, inputs): - """Forward function.""" - x = self._transform_inputs(inputs) - output = self.convs[0](x) - for _ in range(self.recurrence): - output = self.cca(output) - output = self.convs[1](output) - if self.concat_input: - output = self.conv_cat(torch.cat([x, output], dim=1)) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/ddr_head.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/ddr_head.py deleted file mode 100644 index 2d5f3a61c..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/ddr_head.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import HEADS -from .decode_head import BaseDecodeHead - -BatchNorm2d = nn.SyncBatchNorm -bn_mom = 0.1 - - -class DDRModule(nn.ModuleList): - def __init__(self, - in_channels, - channels, - num_classes, - align_corners, - scale_factor=None): - super(DDRModule, self).__init__() - - self.in_channels = in_channels - self.channels = channels - self.num_classes = num_classes - self.scale_factor = scale_factor - self.align_corners = align_corners - - self.bn1 = BatchNorm2d(self.in_channels, momentum=bn_mom) - self.conv1 = nn.Conv2d(self.in_channels, self.channels, kernel_size=3, padding=1, bias=False) - self.bn2 = BatchNorm2d(self.channels, momentum=bn_mom) - self.relu = nn.ReLU(inplace=True) - self.conv2 = nn.Conv2d(self.channels, self.num_classes, kernel_size=1, padding=0, bias=True) - - def init_weights(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') - elif isinstance(m, BatchNorm2d): - nn.init.constant_(m.weight, 1) - nn.init.constant_(m.bias, 0) - - def forward(self, x): - """Forward function.""" - - - y = self.conv1(self.relu(self.bn1(x))) - output = self.conv2(self.relu(self.bn2(y))) - - if self.scale_factor is not None: - height = x.shape[-2] * self.scale_factor - width = x.shape[-1] * self.scale_factor - output = F.interpolate(output, size=[height, width], mode='bilinear') - - return output - -@HEADS.register_module() -class DDRHead(BaseDecodeHead): - def __init__(self, scale_factor=None, **kwargs): - super(DDRHead, self).__init__(**kwargs) - self.ddr_module = DDRModule( - self.in_channels, - self.channels, - self.num_classes, - self.align_corners, - scale_factor - ) - - def forward(self, inputs): - x = self._transform_inputs(inputs) - output = self.ddr_module(x) - # output = self.cls_seg(output) - return output \ No newline at end of file diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/decode_head.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/decode_head.py deleted file mode 100644 index d08b1d0b6..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/decode_head.py +++ /dev/null @@ -1,266 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -import torch -import torch.nn as nn -from mmcv.runner import BaseModule, auto_fp16, force_fp32 - -from mmseg.core import build_pixel_sampler -from mmseg.ops import resize -from ..builder import build_loss -from ..losses import accuracy - - -class BaseDecodeHead(BaseModule, metaclass=ABCMeta): - """Base class for BaseDecodeHead. - - Args: - in_channels (int|Sequence[int]): Input channels. - channels (int): Channels after modules, before conv_seg. - num_classes (int): Number of classes. - dropout_ratio (float): Ratio of dropout layer. Default: 0.1. - conv_cfg (dict|None): Config of conv layers. Default: None. - norm_cfg (dict|None): Config of norm layers. Default: None. - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU') - in_index (int|Sequence[int]): Input feature index. Default: -1 - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - Default: None. - loss_decode (dict | Sequence[dict]): Config of decode loss. - The `loss_name` is property of corresponding loss function which - could be shown in training log. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - e.g. dict(type='CrossEntropyLoss'), - [dict(type='CrossEntropyLoss', loss_name='loss_ce'), - dict(type='DiceLoss', loss_name='loss_dice')] - Default: dict(type='CrossEntropyLoss'). - ignore_index (int | None): The label index to be ignored. When using - masked BCE loss, ignore_index should be set to None. Default: 255. - sampler (dict|None): The config of segmentation map sampler. - Default: None. - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - """ - - def __init__(self, - in_channels, - channels, - *, - num_classes, - dropout_ratio=0.1, - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - in_index=-1, - input_transform=None, - loss_decode=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0), - ignore_index=255, - sampler=None, - align_corners=False, - init_cfg=dict( - type='Normal', std=0.01, override=dict(name='conv_seg'))): - super(BaseDecodeHead, self).__init__(init_cfg) - self._init_inputs(in_channels, in_index, input_transform) - self.channels = channels - self.num_classes = num_classes - self.dropout_ratio = dropout_ratio - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.in_index = in_index - - self.ignore_index = ignore_index - self.align_corners = align_corners - - if isinstance(loss_decode, dict): - self.loss_decode = build_loss(loss_decode) - elif isinstance(loss_decode, (list, tuple)): - self.loss_decode = nn.ModuleList() - for loss in loss_decode: - self.loss_decode.append(build_loss(loss)) - else: - raise TypeError(f'loss_decode must be a dict or sequence of dict,\ - but got {type(loss_decode)}') - - if sampler is not None: - self.sampler = build_pixel_sampler(sampler, context=self) - else: - self.sampler = None - - self.conv_seg = nn.Conv2d(channels, num_classes, kernel_size=1) - if dropout_ratio > 0: - self.dropout = nn.Dropout2d(dropout_ratio) - else: - self.dropout = None - self.fp16_enabled = False - - def extra_repr(self): - """Extra repr.""" - s = f'input_transform={self.input_transform}, ' \ - f'ignore_index={self.ignore_index}, ' \ - f'align_corners={self.align_corners}' - return s - - def _init_inputs(self, in_channels, in_index, input_transform): - """Check and initialize input transforms. - - The in_channels, in_index and input_transform must match. - Specifically, when input_transform is None, only single feature map - will be selected. So in_channels and in_index must be of type int. - When input_transform - - Args: - in_channels (int|Sequence[int]): Input channels. - in_index (int|Sequence[int]): Input feature index. - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - """ - - if input_transform is not None: - assert input_transform in ['resize_concat', 'multiple_select'] - self.input_transform = input_transform - self.in_index = in_index - if input_transform is not None: - assert isinstance(in_channels, (list, tuple)) - assert isinstance(in_index, (list, tuple)) - assert len(in_channels) == len(in_index) - if input_transform == 'resize_concat': - self.in_channels = sum(in_channels) - else: - self.in_channels = in_channels - else: - assert isinstance(in_channels, int) - assert isinstance(in_index, int) - self.in_channels = in_channels - - def _transform_inputs(self, inputs): - """Transform inputs for decoder. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - Tensor: The transformed inputs - """ - - if self.input_transform == 'resize_concat': - inputs = [inputs[i] for i in self.in_index] - upsampled_inputs = [ - resize( - input=x, - size=inputs[0].shape[2:], - mode='bilinear', - align_corners=self.align_corners) for x in inputs - ] - inputs = torch.cat(upsampled_inputs, dim=1) - elif self.input_transform == 'multiple_select': - inputs = [inputs[i] for i in self.in_index] - else: - inputs = inputs[self.in_index] - - return inputs - - @auto_fp16() - @abstractmethod - def forward(self, inputs): - """Placeholder of forward function.""" - pass - - def forward_train(self, inputs, img_metas, gt_semantic_seg, train_cfg): - """Forward function for training. - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - train_cfg (dict): The training config. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - seg_logits = self.forward(inputs) - losses = self.losses(seg_logits, gt_semantic_seg) - return losses - - def forward_test(self, inputs, img_metas, test_cfg): - """Forward function for testing. - - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - test_cfg (dict): The testing config. - - Returns: - Tensor: Output segmentation map. - """ - return self.forward(inputs) - - def cls_seg(self, feat): - """Classify each pixel.""" - if self.dropout is not None: - feat = self.dropout(feat) - output = self.conv_seg(feat) - return output - - @force_fp32(apply_to=('seg_logit', )) - def losses(self, seg_logit, seg_label): - """Compute segmentation loss.""" - loss = dict() - seg_logit = resize( - input=seg_logit, - size=seg_label.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - if self.sampler is not None: - seg_weight = self.sampler.sample(seg_logit, seg_label) - else: - seg_weight = None - seg_label = seg_label.squeeze(1) - - if not isinstance(self.loss_decode, nn.ModuleList): - losses_decode = [self.loss_decode] - else: - losses_decode = self.loss_decode - for loss_decode in losses_decode: - if loss_decode.loss_name not in loss: - loss[loss_decode.loss_name] = loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - else: - loss[loss_decode.loss_name] += loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - - loss['acc_seg'] = accuracy( - seg_logit, seg_label, ignore_index=self.ignore_index) - return loss diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/fcn_head.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/fcn_head.py deleted file mode 100644 index fb79a0d7c..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/decode_heads/fcn_head.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule - -from ..builder import HEADS -from .decode_head import BaseDecodeHead - - -@HEADS.register_module() -class FCNHead(BaseDecodeHead): - """Fully Convolution Networks for Semantic Segmentation. - - This head is implemented of `FCNNet `_. - - Args: - num_convs (int): Number of convs in the head. Default: 2. - kernel_size (int): The kernel size for convs in the head. Default: 3. - concat_input (bool): Whether concat the input and output of convs - before classification layer. - dilation (int): The dilation rate for convs in the head. Default: 1. - """ - - def __init__(self, - num_convs=2, - kernel_size=3, - concat_input=True, - dilation=1, - **kwargs): - assert num_convs >= 0 and dilation > 0 and isinstance(dilation, int) - self.num_convs = num_convs - self.concat_input = concat_input - self.kernel_size = kernel_size - super(FCNHead, self).__init__(**kwargs) - if num_convs == 0: - assert self.in_channels == self.channels - - conv_padding = (kernel_size // 2) * dilation - convs = [] - convs.append( - ConvModule( - self.in_channels, - self.channels, - kernel_size=kernel_size, - padding=conv_padding, - dilation=dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - for i in range(num_convs - 1): - convs.append( - ConvModule( - self.channels, - self.channels, - kernel_size=kernel_size, - padding=conv_padding, - dilation=dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - if num_convs == 0: - self.convs = nn.Identity() - else: - self.convs = nn.Sequential(*convs) - if self.concat_input: - self.conv_cat = ConvModule( - self.in_channels + self.channels, - self.channels, - kernel_size=kernel_size, - padding=kernel_size // 2, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - def _forward_feature(self, inputs): - """Forward function for feature maps before classifying each pixel with - ``self.cls_seg`` fc. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - feats (Tensor): A tensor of shape (batch_size, self.channels, - H, W) which is feature map for last layer of decoder head. - """ - x = self._transform_inputs(inputs) - feats = self.convs(x) - if self.concat_input: - feats = self.conv_cat(torch.cat([x, feats], dim=1)) - return feats - - def forward(self, inputs): - """Forward function.""" - output = self._forward_feature(inputs) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/__init__.py deleted file mode 100644 index fbc5b2d1b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .accuracy import Accuracy, accuracy -from .cross_entropy_loss import (CrossEntropyLoss, binary_cross_entropy, - cross_entropy, mask_cross_entropy) -from .dice_loss import DiceLoss -from .focal_loss import FocalLoss -from .lovasz_loss import LovaszLoss -from .utils import reduce_loss, weight_reduce_loss, weighted_loss - -__all__ = [ - 'accuracy', 'Accuracy', 'cross_entropy', 'binary_cross_entropy', - 'mask_cross_entropy', 'CrossEntropyLoss', 'reduce_loss', - 'weight_reduce_loss', 'weighted_loss', 'LovaszLoss', 'DiceLoss', - 'FocalLoss' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/accuracy.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/accuracy.py deleted file mode 100644 index 1d9e2d770..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/accuracy.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def accuracy(pred, target, topk=1, thresh=None, ignore_index=None): - """Calculate accuracy according to the prediction and target. - - Args: - pred (torch.Tensor): The model prediction, shape (N, num_class, ...) - target (torch.Tensor): The target of each prediction, shape (N, , ...) - ignore_index (int | None): The label index to be ignored. Default: None - topk (int | tuple[int], optional): If the predictions in ``topk`` - matches the target, the predictions will be regarded as - correct ones. Defaults to 1. - thresh (float, optional): If not None, predictions with scores under - this threshold are considered incorrect. Default to None. - - Returns: - float | tuple[float]: If the input ``topk`` is a single integer, - the function will return a single float as accuracy. If - ``topk`` is a tuple containing multiple integers, the - function will return a tuple containing accuracies of - each ``topk`` number. - """ - assert isinstance(topk, (int, tuple)) - if isinstance(topk, int): - topk = (topk, ) - return_single = True - else: - return_single = False - - maxk = max(topk) - if pred.size(0) == 0: - accu = [pred.new_tensor(0.) for i in range(len(topk))] - return accu[0] if return_single else accu - assert pred.ndim == target.ndim + 1 - assert pred.size(0) == target.size(0) - assert maxk <= pred.size(1), \ - f'maxk {maxk} exceeds pred dimension {pred.size(1)}' - pred_value, pred_label = pred.topk(maxk, dim=1) - # transpose to shape (maxk, N, ...) - pred_label = pred_label.transpose(0, 1) - correct = pred_label.eq(target.unsqueeze(0).expand_as(pred_label)) - if thresh is not None: - # Only prediction values larger than thresh are counted as correct - correct = correct & (pred_value > thresh).t() - if ignore_index is not None: - correct = correct[:, target != ignore_index] - res = [] - eps = torch.finfo(torch.float32).eps - for k in topk: - # Avoid causing ZeroDivisionError when all pixels - # of an image are ignored - correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) + eps - if ignore_index is not None: - total_num = target[target != ignore_index].numel() + eps - else: - total_num = target.numel() + eps - res.append(correct_k.mul_(100.0 / total_num)) - return res[0] if return_single else res - - -class Accuracy(nn.Module): - """Accuracy calculation module.""" - - def __init__(self, topk=(1, ), thresh=None, ignore_index=None): - """Module to calculate the accuracy. - - Args: - topk (tuple, optional): The criterion used to calculate the - accuracy. Defaults to (1,). - thresh (float, optional): If not None, predictions with scores - under this threshold are considered incorrect. Default to None. - """ - super().__init__() - self.topk = topk - self.thresh = thresh - self.ignore_index = ignore_index - - def forward(self, pred, target): - """Forward function to calculate accuracy. - - Args: - pred (torch.Tensor): Prediction of models. - target (torch.Tensor): Target for each prediction. - - Returns: - tuple[float]: The accuracies under different topk criterions. - """ - return accuracy(pred, target, self.topk, self.thresh, - self.ignore_index) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/cross_entropy_loss.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/cross_entropy_loss.py deleted file mode 100644 index 623fd58db..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/cross_entropy_loss.py +++ /dev/null @@ -1,296 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weight_reduce_loss - - -def cross_entropy(pred, - label, - weight=None, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=-100, - avg_non_ignore=False): - """cross_entropy. The wrapper function for :func:`F.cross_entropy` - - Args: - pred (torch.Tensor): The prediction with shape (N, 1). - label (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - Default: None. - class_weight (list[float], optional): The weight for each class. - Default: None. - reduction (str, optional): The method used to reduce the loss. - Options are 'none', 'mean' and 'sum'. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Default: None. - ignore_index (int): Specifies a target value that is ignored and - does not contribute to the input gradients. When - ``avg_non_ignore `` is ``True``, and the ``reduction`` is - ``''mean''``, the loss is averaged over non-ignored targets. - Defaults: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - """ - - # class_weight is a manual rescaling weight given to each class. - # If given, has to be a Tensor of size C element-wise losses - loss = F.cross_entropy( - pred, - label, - weight=class_weight, - reduction='none', - ignore_index=ignore_index) - - # apply weights and do the reduction - # average loss over non-ignored elements - # pytorch's official cross_entropy average loss over non-ignored elements - # refer to https://github.com/pytorch/pytorch/blob/56b43f4fec1f76953f15a627694d4bba34588969/torch/nn/functional.py#L2660 # noqa - if (avg_factor is None) and avg_non_ignore and reduction == 'mean': - avg_factor = label.numel() - (label == ignore_index).sum().item() - if weight is not None: - weight = weight.float() - loss = weight_reduce_loss( - loss, weight=weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def _expand_onehot_labels(labels, label_weights, target_shape, ignore_index): - """Expand onehot labels to match the size of prediction.""" - bin_labels = labels.new_zeros(target_shape) - valid_mask = (labels >= 0) & (labels != ignore_index) - inds = torch.nonzero(valid_mask, as_tuple=True) - - if inds[0].numel() > 0: - if labels.dim() == 3: - bin_labels[inds[0], labels[valid_mask], inds[1], inds[2]] = 1 - else: - bin_labels[inds[0], labels[valid_mask]] = 1 - - valid_mask = valid_mask.unsqueeze(1).expand(target_shape).float() - - if label_weights is None: - bin_label_weights = valid_mask - else: - bin_label_weights = label_weights.unsqueeze(1).expand(target_shape) - bin_label_weights = bin_label_weights * valid_mask - - return bin_labels, bin_label_weights, valid_mask - - -def binary_cross_entropy(pred, - label, - weight=None, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=-100, - avg_non_ignore=False, - **kwargs): - """Calculate the binary CrossEntropy loss. - - Args: - pred (torch.Tensor): The prediction with shape (N, 1). - label (torch.Tensor): The learning label of the prediction. - Note: In bce loss, label < 0 is invalid. - weight (torch.Tensor, optional): Sample-wise loss weight. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (int): The label index to be ignored. Default: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - - Returns: - torch.Tensor: The calculated loss - """ - if pred.size(1) == 1: - # For binary class segmentation, the shape of pred is - # [N, 1, H, W] and that of label is [N, H, W]. - # As the ignore_index often set as 255, so the - # binary class label check should mask out - # ignore_index - assert label[label != ignore_index].max() <= 1, \ - 'For pred with shape [N, 1, H, W], its label must have at ' \ - 'most 2 classes' - pred = pred.squeeze() - if pred.dim() != label.dim(): - assert (pred.dim() == 2 and label.dim() == 1) or ( - pred.dim() == 4 and label.dim() == 3), \ - 'Only pred shape [N, C], label shape [N] or pred shape [N, C, ' \ - 'H, W], label shape [N, H, W] are supported' - # `weight` returned from `_expand_onehot_labels` - # has been treated for valid (non-ignore) pixels - label, weight, valid_mask = _expand_onehot_labels( - label, weight, pred.shape, ignore_index) - else: - # should mask out the ignored elements - valid_mask = ((label >= 0) & (label != ignore_index)).float() - if weight is not None: - weight = weight * valid_mask - else: - weight = valid_mask - # average loss over non-ignored and valid elements - if reduction == 'mean' and avg_factor is None and avg_non_ignore: - avg_factor = valid_mask.sum().item() - - loss = F.binary_cross_entropy_with_logits( - pred, label.float(), pos_weight=class_weight, reduction='none') - # do the reduction for the weighted loss - loss = weight_reduce_loss( - loss, weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def mask_cross_entropy(pred, - target, - label, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=None, - **kwargs): - """Calculate the CrossEntropy loss for masks. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. - label (torch.Tensor): ``label`` indicates the class label of the mask' - corresponding object. This will be used to select the mask in the - of the class which the object belongs to when the mask prediction - if not class-agnostic. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (None): Placeholder, to be consistent with other loss. - Default: None. - - Returns: - torch.Tensor: The calculated loss - """ - assert ignore_index is None, 'BCE loss does not support ignore_index' - # TODO: handle these two reserved arguments - assert reduction == 'mean' and avg_factor is None - num_rois = pred.size()[0] - inds = torch.arange(0, num_rois, dtype=torch.long, device=pred.device) - pred_slice = pred[inds, label].squeeze(1) - return F.binary_cross_entropy_with_logits( - pred_slice, target, weight=class_weight, reduction='mean')[None] - - -@LOSSES.register_module() -class CrossEntropyLoss(nn.Module): - """CrossEntropyLoss. - - Args: - use_sigmoid (bool, optional): Whether the prediction uses sigmoid - of softmax. Defaults to False. - use_mask (bool, optional): Whether to use mask cross entropy loss. - Defaults to False. - reduction (str, optional): . Defaults to 'mean'. - Options are "none", "mean" and "sum". - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - """ - - def __init__(self, - use_sigmoid=False, - use_mask=False, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_ce', - avg_non_ignore=False): - super(CrossEntropyLoss, self).__init__() - assert (use_sigmoid is False) or (use_mask is False) - self.use_sigmoid = use_sigmoid - self.use_mask = use_mask - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = get_class_weight(class_weight) - self.avg_non_ignore = avg_non_ignore - if not self.avg_non_ignore and self.reduction == 'mean': - warnings.warn( - 'Default ``avg_non_ignore`` is False, if you would like to ' - 'ignore the certain label and average loss over non-ignore ' - 'labels, which is the same with PyTorch official ' - 'cross_entropy, set ``avg_non_ignore=True``.') - - if self.use_sigmoid: - self.cls_criterion = binary_cross_entropy - elif self.use_mask: - self.cls_criterion = mask_cross_entropy - else: - self.cls_criterion = cross_entropy - self._loss_name = loss_name - - def extra_repr(self): - """Extra repr.""" - s = f'avg_non_ignore={self.avg_non_ignore}' - return s - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - ignore_index=-100, - **kwargs): - """Forward function.""" - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = cls_score.new_tensor(self.class_weight) - else: - class_weight = None - # Note: for BCE loss, label < 0 is invalid. - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - weight, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - avg_non_ignore=self.avg_non_ignore, - ignore_index=ignore_index, - **kwargs) - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/dice_loss.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/dice_loss.py deleted file mode 100644 index 79a3abfc2..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/dice_loss.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/LikeLy-Journey/SegmenTron/blob/master/ -segmentron/solver/loss.py (Apache-2.0 License)""" -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weighted_loss - - -@weighted_loss -def dice_loss(pred, - target, - valid_mask, - smooth=1, - exponent=2, - class_weight=None, - ignore_index=255): - assert pred.shape[0] == target.shape[0] - total_loss = 0 - num_classes = pred.shape[1] - for i in range(num_classes): - if i != ignore_index: - dice_loss = binary_dice_loss( - pred[:, i], - target[..., i], - valid_mask=valid_mask, - smooth=smooth, - exponent=exponent) - if class_weight is not None: - dice_loss *= class_weight[i] - total_loss += dice_loss - return total_loss / num_classes - - -@weighted_loss -def binary_dice_loss(pred, target, valid_mask, smooth=1, exponent=2, **kwards): - assert pred.shape[0] == target.shape[0] - pred = pred.reshape(pred.shape[0], -1) - target = target.reshape(target.shape[0], -1) - valid_mask = valid_mask.reshape(valid_mask.shape[0], -1) - - num = torch.sum(torch.mul(pred, target) * valid_mask, dim=1) * 2 + smooth - den = torch.sum(pred.pow(exponent) + target.pow(exponent), dim=1) + smooth - - return 1 - num / den - - -@LOSSES.register_module() -class DiceLoss(nn.Module): - """DiceLoss. - - This loss is proposed in `V-Net: Fully Convolutional Neural Networks for - Volumetric Medical Image Segmentation `_. - - Args: - smooth (float): A float number to smooth loss, and avoid NaN error. - Default: 1 - exponent (float): An float number to calculate denominator - value: \\sum{x^exponent} + \\sum{y^exponent}. Default: 2. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Default to 1.0. - ignore_index (int | None): The label index to be ignored. Default: 255. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_dice'. - """ - - def __init__(self, - smooth=1, - exponent=2, - reduction='mean', - class_weight=None, - loss_weight=1.0, - ignore_index=255, - loss_name='loss_dice', - **kwards): - super(DiceLoss, self).__init__() - self.smooth = smooth - self.exponent = exponent - self.reduction = reduction - self.class_weight = get_class_weight(class_weight) - self.loss_weight = loss_weight - self.ignore_index = ignore_index - self._loss_name = loss_name - - def forward(self, - pred, - target, - avg_factor=None, - reduction_override=None, - **kwards): - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = pred.new_tensor(self.class_weight) - else: - class_weight = None - - pred = F.softmax(pred, dim=1) - num_classes = pred.shape[1] - one_hot_target = F.one_hot( - torch.clamp(target.long(), 0, num_classes - 1), - num_classes=num_classes) - valid_mask = (target != self.ignore_index).long() - - loss = self.loss_weight * dice_loss( - pred, - one_hot_target, - valid_mask=valid_mask, - reduction=reduction, - avg_factor=avg_factor, - smooth=self.smooth, - exponent=self.exponent, - class_weight=class_weight, - ignore_index=self.ignore_index) - return loss - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/focal_loss.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/focal_loss.py deleted file mode 100644 index af1c711df..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/focal_loss.py +++ /dev/null @@ -1,327 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Modified from https://github.com/open-mmlab/mmdetection -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.ops import sigmoid_focal_loss as _sigmoid_focal_loss - -from ..builder import LOSSES -from .utils import weight_reduce_loss - - -# This method is used when cuda is not available -def py_sigmoid_focal_loss(pred, - target, - one_hot_target=None, - weight=None, - gamma=2.0, - alpha=0.5, - class_weight=None, - valid_mask=None, - reduction='mean', - avg_factor=None): - """PyTorch version of `Focal Loss `_. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the - number of classes - target (torch.Tensor): The learning label of the prediction with - shape (N, C) - one_hot_target (None): Placeholder. It should be None. - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal Loss. - Defaults to 0.5. - class_weight (list[float], optional): Weight of each class. - Defaults to None. - valid_mask (torch.Tensor, optional): A mask uses 1 to mark the valid - samples and uses 0 to mark the ignored samples. Default: None. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - if isinstance(alpha, list): - alpha = pred.new_tensor(alpha) - pred_sigmoid = pred.sigmoid() - target = target.type_as(pred) - one_minus_pt = (1 - pred_sigmoid) * target + pred_sigmoid * (1 - target) - focal_weight = (alpha * target + (1 - alpha) * - (1 - target)) * one_minus_pt.pow(gamma) - - loss = F.binary_cross_entropy_with_logits( - pred, target, reduction='none') * focal_weight - final_weight = torch.ones(1, pred.size(1)).type_as(loss) - if weight is not None: - if weight.shape != loss.shape and weight.size(0) == loss.size(0): - # For most cases, weight is of shape (N, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - assert weight.dim() == loss.dim() - final_weight = final_weight * weight - if class_weight is not None: - final_weight = final_weight * pred.new_tensor(class_weight) - if valid_mask is not None: - final_weight = final_weight * valid_mask - loss = weight_reduce_loss(loss, final_weight, reduction, avg_factor) - return loss - - -def sigmoid_focal_loss(pred, - target, - one_hot_target, - weight=None, - gamma=2.0, - alpha=0.5, - class_weight=None, - valid_mask=None, - reduction='mean', - avg_factor=None): - r"""A warpper of cuda version `Focal Loss - `_. - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. It's shape - should be (N, ) - one_hot_target (torch.Tensor): The learning label with shape (N, C) - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal Loss. - Defaults to 0.5. - class_weight (list[float], optional): Weight of each class. - Defaults to None. - valid_mask (torch.Tensor, optional): A mask uses 1 to mark the valid - samples and uses 0 to mark the ignored samples. Default: None. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - # Function.apply does not accept keyword arguments, so the decorator - # "weighted_loss" is not applicable - final_weight = torch.ones(1, pred.size(1)).type_as(pred) - if isinstance(alpha, list): - # _sigmoid_focal_loss doesn't accept alpha of list type. Therefore, if - # a list is given, we set the input alpha as 0.5. This means setting - # equal weight for foreground class and background class. By - # multiplying the loss by 2, the effect of setting alpha as 0.5 is - # undone. The alpha of type list is used to regulate the loss in the - # post-processing process. - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), - gamma, 0.5, None, 'none') * 2 - alpha = pred.new_tensor(alpha) - final_weight = final_weight * ( - alpha * one_hot_target + (1 - alpha) * (1 - one_hot_target)) - else: - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), - gamma, alpha, None, 'none') - if weight is not None: - if weight.shape != loss.shape and weight.size(0) == loss.size(0): - # For most cases, weight is of shape (N, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - assert weight.dim() == loss.dim() - final_weight = final_weight * weight - if class_weight is not None: - final_weight = final_weight * pred.new_tensor(class_weight) - if valid_mask is not None: - final_weight = final_weight * valid_mask - loss = weight_reduce_loss(loss, final_weight, reduction, avg_factor) - return loss - - -@LOSSES.register_module() -class FocalLoss(nn.Module): - - def __init__(self, - use_sigmoid=True, - gamma=2.0, - alpha=0.5, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_focal'): - """`Focal Loss `_ - Args: - use_sigmoid (bool, optional): Whether to the prediction is - used for sigmoid or softmax. Defaults to True. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal - Loss. Defaults to 0.5. When a list is provided, the length - of the list should be equal to the number of classes. - Please be careful that this parameter is not the - class-wise weight but the weight of a binary classification - problem. This binary classification problem regards the - pixels which belong to one class as the foreground - and the other pixels as the background, each element in - the list is the weight of the corresponding foreground class. - The value of alpha or each element of alpha should be a float - in the interval [0, 1]. If you want to specify the class-wise - weight, please use `class_weight` parameter. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and - "sum". - class_weight (list[float], optional): Weight of each class. - Defaults to None. - loss_weight (float, optional): Weight of loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this - loss item to be included into the backward graph, `loss_` must - be the prefix of the name. Defaults to 'loss_focal'. - """ - super(FocalLoss, self).__init__() - assert use_sigmoid is True, \ - 'AssertionError: Only sigmoid focal loss supported now.' - assert reduction in ('none', 'mean', 'sum'), \ - "AssertionError: reduction should be 'none', 'mean' or " \ - "'sum'" - assert isinstance(alpha, (float, list)), \ - 'AssertionError: alpha should be of type float' - assert isinstance(gamma, float), \ - 'AssertionError: gamma should be of type float' - assert isinstance(loss_weight, float), \ - 'AssertionError: loss_weight should be of type float' - assert isinstance(loss_name, str), \ - 'AssertionError: loss_name should be of type str' - assert isinstance(class_weight, list) or class_weight is None, \ - 'AssertionError: class_weight must be None or of type list' - self.use_sigmoid = use_sigmoid - self.gamma = gamma - self.alpha = alpha - self.reduction = reduction - self.class_weight = class_weight - self.loss_weight = loss_weight - self._loss_name = loss_name - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - ignore_index=255, - **kwargs): - """Forward function. - - Args: - pred (torch.Tensor): The prediction with shape - (N, C) where C = number of classes, or - (N, C, d_1, d_2, ..., d_K) with K≥1 in the - case of K-dimensional loss. - target (torch.Tensor): The ground truth. If containing class - indices, shape (N) where each value is 0≤targets[i]≤C−1, - or (N, d_1, d_2, ..., d_K) with K≥1 in the case of - K-dimensional loss. If containing class probabilities, - same shape as the input. - weight (torch.Tensor, optional): The weight of loss for each - prediction. Defaults to None. - avg_factor (int, optional): Average factor that is used to - average the loss. Defaults to None. - reduction_override (str, optional): The reduction method used - to override the original reduction method of the loss. - Options are "none", "mean" and "sum". - ignore_index (int, optional): The label index to be ignored. - Default: 255 - Returns: - torch.Tensor: The calculated loss - """ - assert isinstance(ignore_index, int), \ - 'ignore_index must be of type int' - assert reduction_override in (None, 'none', 'mean', 'sum'), \ - "AssertionError: reduction should be 'none', 'mean' or " \ - "'sum'" - assert pred.shape == target.shape or \ - (pred.size(0) == target.size(0) and - pred.shape[2:] == target.shape[1:]), \ - "The shape of pred doesn't match the shape of target" - - original_shape = pred.shape - - # [B, C, d_1, d_2, ..., d_k] -> [C, B, d_1, d_2, ..., d_k] - pred = pred.transpose(0, 1) - # [C, B, d_1, d_2, ..., d_k] -> [C, N] - pred = pred.reshape(pred.size(0), -1) - # [C, N] -> [N, C] - pred = pred.transpose(0, 1).contiguous() - - if original_shape == target.shape: - # target with shape [B, C, d_1, d_2, ...] - # transform it's shape into [N, C] - # [B, C, d_1, d_2, ...] -> [C, B, d_1, d_2, ..., d_k] - target = target.transpose(0, 1) - # [C, B, d_1, d_2, ..., d_k] -> [C, N] - target = target.reshape(target.size(0), -1) - # [C, N] -> [N, C] - target = target.transpose(0, 1).contiguous() - else: - # target with shape [B, d_1, d_2, ...] - # transform it's shape into [N, ] - target = target.view(-1).contiguous() - valid_mask = (target != ignore_index).view(-1, 1) - # avoid raising error when using F.one_hot() - target = torch.where(target == ignore_index, target.new_tensor(0), - target) - - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.use_sigmoid: - num_classes = pred.size(1) - if torch.cuda.is_available() and pred.is_cuda: - if target.dim() == 1: - one_hot_target = F.one_hot(target, num_classes=num_classes) - else: - one_hot_target = target - target = target.argmax(dim=1) - valid_mask = (target != ignore_index).view(-1, 1) - calculate_loss_func = sigmoid_focal_loss - else: - one_hot_target = None - if target.dim() == 1: - target = F.one_hot(target, num_classes=num_classes) - else: - valid_mask = (target.argmax(dim=1) != ignore_index).view( - -1, 1) - calculate_loss_func = py_sigmoid_focal_loss - - loss_cls = self.loss_weight * calculate_loss_func( - pred, - target, - one_hot_target, - weight, - gamma=self.gamma, - alpha=self.alpha, - class_weight=self.class_weight, - valid_mask=valid_mask, - reduction=reduction, - avg_factor=avg_factor) - - if reduction == 'none': - # [N, C] -> [C, N] - loss_cls = loss_cls.transpose(0, 1) - # [C, N] -> [C, B, d1, d2, ...] - # original_shape: [B, C, d1, d2, ...] - loss_cls = loss_cls.reshape(original_shape[1], - original_shape[0], - *original_shape[2:]) - # [C, B, d1, d2, ...] -> [B, C, d1, d2, ...] - loss_cls = loss_cls.transpose(0, 1).contiguous() - else: - raise NotImplementedError - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/lovasz_loss.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/lovasz_loss.py deleted file mode 100644 index 2bb0fad39..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/lovasz_loss.py +++ /dev/null @@ -1,323 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/bermanmaxim/LovaszSoftmax/blob/master/pytor -ch/lovasz_losses.py Lovasz-Softmax and Jaccard hinge loss in PyTorch Maxim -Berman 2018 ESAT-PSI KU Leuven (MIT License)""" - -import mmcv -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weight_reduce_loss - - -def lovasz_grad(gt_sorted): - """Computes gradient of the Lovasz extension w.r.t sorted errors. - - See Alg. 1 in paper. - """ - p = len(gt_sorted) - gts = gt_sorted.sum() - intersection = gts - gt_sorted.float().cumsum(0) - union = gts + (1 - gt_sorted).float().cumsum(0) - jaccard = 1. - intersection / union - if p > 1: # cover 1-pixel case - jaccard[1:p] = jaccard[1:p] - jaccard[0:-1] - return jaccard - - -def flatten_binary_logits(logits, labels, ignore_index=None): - """Flattens predictions in the batch (binary case) Remove labels equal to - 'ignore_index'.""" - logits = logits.view(-1) - labels = labels.view(-1) - if ignore_index is None: - return logits, labels - valid = (labels != ignore_index) - vlogits = logits[valid] - vlabels = labels[valid] - return vlogits, vlabels - - -def flatten_probs(probs, labels, ignore_index=None): - """Flattens predictions in the batch.""" - if probs.dim() == 3: - # assumes output of a sigmoid layer - B, H, W = probs.size() - probs = probs.view(B, 1, H, W) - B, C, H, W = probs.size() - probs = probs.permute(0, 2, 3, 1).contiguous().view(-1, C) # B*H*W, C=P,C - labels = labels.view(-1) - if ignore_index is None: - return probs, labels - valid = (labels != ignore_index) - vprobs = probs[valid.nonzero().squeeze()] - vlabels = labels[valid] - return vprobs, vlabels - - -def lovasz_hinge_flat(logits, labels): - """Binary Lovasz hinge loss. - - Args: - logits (torch.Tensor): [P], logits at each prediction - (between -infty and +infty). - labels (torch.Tensor): [P], binary ground truth labels (0 or 1). - - Returns: - torch.Tensor: The calculated loss. - """ - if len(labels) == 0: - # only void pixels, the gradients should be 0 - return logits.sum() * 0. - signs = 2. * labels.float() - 1. - errors = (1. - logits * signs) - errors_sorted, perm = torch.sort(errors, dim=0, descending=True) - perm = perm.data - gt_sorted = labels[perm] - grad = lovasz_grad(gt_sorted) - loss = torch.dot(F.relu(errors_sorted), grad) - return loss - - -def lovasz_hinge(logits, - labels, - classes='present', - per_image=False, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=255): - """Binary Lovasz hinge loss. - - Args: - logits (torch.Tensor): [B, H, W], logits at each pixel - (between -infty and +infty). - labels (torch.Tensor): [B, H, W], binary ground truth masks (0 or 1). - classes (str | list[int], optional): Placeholder, to be consistent with - other loss. Default: None. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - class_weight (list[float], optional): Placeholder, to be consistent - with other loss. Default: None. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. This parameter only works when per_image is True. - Default: None. - ignore_index (int | None): The label index to be ignored. Default: 255. - - Returns: - torch.Tensor: The calculated loss. - """ - if per_image: - loss = [ - lovasz_hinge_flat(*flatten_binary_logits( - logit.unsqueeze(0), label.unsqueeze(0), ignore_index)) - for logit, label in zip(logits, labels) - ] - loss = weight_reduce_loss( - torch.stack(loss), None, reduction, avg_factor) - else: - loss = lovasz_hinge_flat( - *flatten_binary_logits(logits, labels, ignore_index)) - return loss - - -def lovasz_softmax_flat(probs, labels, classes='present', class_weight=None): - """Multi-class Lovasz-Softmax loss. - - Args: - probs (torch.Tensor): [P, C], class probabilities at each prediction - (between 0 and 1). - labels (torch.Tensor): [P], ground truth labels (between 0 and C - 1). - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - class_weight (list[float], optional): The weight for each class. - Default: None. - - Returns: - torch.Tensor: The calculated loss. - """ - if probs.numel() == 0: - # only void pixels, the gradients should be 0 - return probs * 0. - C = probs.size(1) - losses = [] - class_to_sum = list(range(C)) if classes in ['all', 'present'] else classes - for c in class_to_sum: - fg = (labels == c).float() # foreground for class c - if (classes == 'present' and fg.sum() == 0): - continue - if C == 1: - if len(classes) > 1: - raise ValueError('Sigmoid output possible only with 1 class') - class_pred = probs[:, 0] - else: - class_pred = probs[:, c] - errors = (fg - class_pred).abs() - errors_sorted, perm = torch.sort(errors, 0, descending=True) - perm = perm.data - fg_sorted = fg[perm] - loss = torch.dot(errors_sorted, lovasz_grad(fg_sorted)) - if class_weight is not None: - loss *= class_weight[c] - losses.append(loss) - return torch.stack(losses).mean() - - -def lovasz_softmax(probs, - labels, - classes='present', - per_image=False, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=255): - """Multi-class Lovasz-Softmax loss. - - Args: - probs (torch.Tensor): [B, C, H, W], class probabilities at each - prediction (between 0 and 1). - labels (torch.Tensor): [B, H, W], ground truth labels (between 0 and - C - 1). - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - class_weight (list[float], optional): The weight for each class. - Default: None. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. This parameter only works when per_image is True. - Default: None. - ignore_index (int | None): The label index to be ignored. Default: 255. - - Returns: - torch.Tensor: The calculated loss. - """ - - if per_image: - loss = [ - lovasz_softmax_flat( - *flatten_probs( - prob.unsqueeze(0), label.unsqueeze(0), ignore_index), - classes=classes, - class_weight=class_weight) - for prob, label in zip(probs, labels) - ] - loss = weight_reduce_loss( - torch.stack(loss), None, reduction, avg_factor) - else: - loss = lovasz_softmax_flat( - *flatten_probs(probs, labels, ignore_index), - classes=classes, - class_weight=class_weight) - return loss - - -@LOSSES.register_module() -class LovaszLoss(nn.Module): - """LovaszLoss. - - This loss is proposed in `The Lovasz-Softmax loss: A tractable surrogate - for the optimization of the intersection-over-union measure in neural - networks `_. - - Args: - loss_type (str, optional): Binary or multi-class loss. - Default: 'multi_class'. Options are "binary" and "multi_class". - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_lovasz'. - """ - - def __init__(self, - loss_type='multi_class', - classes='present', - per_image=False, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_lovasz'): - super(LovaszLoss, self).__init__() - assert loss_type in ('binary', 'multi_class'), "loss_type should be \ - 'binary' or 'multi_class'." - - if loss_type == 'binary': - self.cls_criterion = lovasz_hinge - else: - self.cls_criterion = lovasz_softmax - assert classes in ('all', 'present') or mmcv.is_list_of(classes, int) - if not per_image: - assert reduction == 'none', "reduction should be 'none' when \ - per_image is False." - - self.classes = classes - self.per_image = per_image - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = get_class_weight(class_weight) - self._loss_name = loss_name - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - """Forward function.""" - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = cls_score.new_tensor(self.class_weight) - else: - class_weight = None - - # if multi-class loss, transform logits to probs - if self.cls_criterion == lovasz_softmax: - cls_score = F.softmax(cls_score, dim=1) - - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - self.classes, - self.per_image, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/utils.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/utils.py deleted file mode 100644 index 621f57c74..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/losses/utils.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import mmcv -import numpy as np -import torch -import torch.nn.functional as F - - -def get_class_weight(class_weight): - """Get class weight for loss function. - - Args: - class_weight (list[float] | str | None): If class_weight is a str, - take it as a file name and read from it. - """ - if isinstance(class_weight, str): - # take it as a file path - if class_weight.endswith('.npy'): - class_weight = np.load(class_weight) - else: - # pkl, json or yaml - class_weight = mmcv.load(class_weight) - - return class_weight - - -def reduce_loss(loss, reduction): - """Reduce loss as specified. - - Args: - loss (Tensor): Elementwise loss tensor. - reduction (str): Options are "none", "mean" and "sum". - - Return: - Tensor: Reduced loss tensor. - """ - reduction_enum = F._Reduction.get_enum(reduction) - # none: 0, elementwise_mean:1, sum: 2 - if reduction_enum == 0: - return loss - elif reduction_enum == 1: - return loss.mean() - elif reduction_enum == 2: - return loss.sum() - - -def weight_reduce_loss(loss, weight=None, reduction='mean', avg_factor=None): - """Apply element-wise weight and reduce loss. - - Args: - loss (Tensor): Element-wise loss. - weight (Tensor): Element-wise weights. - reduction (str): Same as built-in losses of PyTorch. - avg_factor (float): Average factor when computing the mean of losses. - - Returns: - Tensor: Processed loss values. - """ - # if weight is specified, apply element-wise weight - if weight is not None: - assert weight.dim() == loss.dim() - if weight.dim() > 1: - assert weight.size(1) == 1 or weight.size(1) == loss.size(1) - loss = loss * weight - - # if avg_factor is not specified, just reduce the loss - if avg_factor is None: - loss = reduce_loss(loss, reduction) - else: - # if reduction is mean, then average the loss by avg_factor - if reduction == 'mean': - # Avoid causing ZeroDivisionError when avg_factor is 0.0, - # i.e., all labels of an image belong to ignore index. - eps = torch.finfo(torch.float32).eps - loss = loss.sum() / (avg_factor + eps) - # if reduction is 'none', then do nothing, otherwise raise an error - elif reduction != 'none': - raise ValueError('avg_factor can not be used with reduction="sum"') - return loss - - -def weighted_loss(loss_func): - """Create a weighted version of a given loss function. - - To use this decorator, the loss function must have the signature like - `loss_func(pred, target, **kwargs)`. The function only needs to compute - element-wise loss without any reduction. This decorator will add weight - and reduction arguments to the function. The decorated function will have - the signature like `loss_func(pred, target, weight=None, reduction='mean', - avg_factor=None, **kwargs)`. - - :Example: - - >>> import torch - >>> @weighted_loss - >>> def l1_loss(pred, target): - >>> return (pred - target).abs() - - >>> pred = torch.Tensor([0, 2, 3]) - >>> target = torch.Tensor([1, 1, 1]) - >>> weight = torch.Tensor([1, 0, 1]) - - >>> l1_loss(pred, target) - tensor(1.3333) - >>> l1_loss(pred, target, weight) - tensor(1.) - >>> l1_loss(pred, target, reduction='none') - tensor([1., 1., 2.]) - >>> l1_loss(pred, target, weight, avg_factor=2) - tensor(1.5000) - """ - - @functools.wraps(loss_func) - def wrapper(pred, - target, - weight=None, - reduction='mean', - avg_factor=None, - **kwargs): - # get element-wise loss - loss = loss_func(pred, target, **kwargs) - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - return wrapper diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/__init__.py deleted file mode 100644 index ff03186a9..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .featurepyramid import Feature2Pyramid -from .fpn import FPN -from .ic_neck import ICNeck -from .jpu import JPU -from .mla_neck import MLANeck -from .multilevel_neck import MultiLevelNeck - -__all__ = [ - 'FPN', 'MultiLevelNeck', 'MLANeck', 'ICNeck', 'JPU', 'Feature2Pyramid' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/featurepyramid.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/featurepyramid.py deleted file mode 100644 index 82a00ceb1..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/featurepyramid.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import build_norm_layer - -from ..builder import NECKS - - -@NECKS.register_module() -class Feature2Pyramid(nn.Module): - """Feature2Pyramid. - - A neck structure connect ViT backbone and decoder_heads. - - Args: - embed_dims (int): Embedding dimension. - rescales (list[float]): Different sampling multiples were - used to obtain pyramid features. Default: [4, 2, 1, 0.5]. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='SyncBN', requires_grad=True). - """ - - def __init__(self, - embed_dim, - rescales=[4, 2, 1, 0.5], - norm_cfg=dict(type='SyncBN', requires_grad=True)): - super(Feature2Pyramid, self).__init__() - self.rescales = rescales - self.upsample_4x = None - for k in self.rescales: - if k == 4: - self.upsample_4x = nn.Sequential( - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2), - build_norm_layer(norm_cfg, embed_dim)[1], - nn.GELU(), - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2), - ) - elif k == 2: - self.upsample_2x = nn.Sequential( - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2)) - elif k == 1: - self.identity = nn.Identity() - elif k == 0.5: - self.downsample_2x = nn.MaxPool2d(kernel_size=2, stride=2) - elif k == 0.25: - self.downsample_4x = nn.MaxPool2d(kernel_size=4, stride=4) - else: - raise KeyError(f'invalid {k} for feature2pyramid') - - def forward(self, inputs): - assert len(inputs) == len(self.rescales) - outputs = [] - if self.upsample_4x is not None: - ops = [ - self.upsample_4x, self.upsample_2x, self.identity, - self.downsample_2x - ] - else: - ops = [ - self.upsample_2x, self.identity, self.downsample_2x, - self.downsample_4x - ] - for i in range(len(inputs)): - outputs.append(ops[i](inputs[i])) - return tuple(outputs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/fpn.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/fpn.py deleted file mode 100644 index 6997de9d4..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/fpn.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule, auto_fp16 - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class FPN(BaseModule): - """Feature Pyramid Network. - - This neck is the implementation of `Feature Pyramid Networks for Object - Detection `_. - - Args: - in_channels (list[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - num_outs (int): Number of output scales. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - add_extra_convs (bool | str): If bool, it decides whether to add conv - layers on top of the original feature maps. Default to False. - If True, its actual mode is specified by `extra_convs_on_inputs`. - If str, it specifies the source feature map of the extra convs. - Only the following options are allowed - - - 'on_input': Last feat map of neck inputs (i.e. backbone feature). - - 'on_lateral': Last feature map after lateral convs. - - 'on_output': The last output feature map after fpn convs. - extra_convs_on_inputs (bool, deprecated): Whether to apply extra convs - on the original feature from the backbone. If True, - it is equivalent to `add_extra_convs='on_input'`. If False, it is - equivalent to set `add_extra_convs='on_output'`. Default to True. - relu_before_extra_convs (bool): Whether to apply relu before the extra - conv. Default: False. - no_norm_on_lateral (bool): Whether to apply norm on lateral. - Default: False. - conv_cfg (dict): Config dict for convolution layer. Default: None. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - upsample_cfg (dict): Config dict for interpolate layer. - Default: dict(mode='nearest'). - init_cfg (dict or list[dict], optional): Initialization config dict. - - Example: - >>> import torch - >>> in_channels = [2, 3, 5, 7] - >>> scales = [340, 170, 84, 43] - >>> inputs = [torch.rand(1, c, s, s) - ... for c, s in zip(in_channels, scales)] - >>> self = FPN(in_channels, 11, len(in_channels)).eval() - >>> outputs = self.forward(inputs) - >>> for i in range(len(outputs)): - ... print(f'outputs[{i}].shape = {outputs[i].shape}') - outputs[0].shape = torch.Size([1, 11, 340, 340]) - outputs[1].shape = torch.Size([1, 11, 170, 170]) - outputs[2].shape = torch.Size([1, 11, 84, 84]) - outputs[3].shape = torch.Size([1, 11, 43, 43]) - """ - - def __init__(self, - in_channels, - out_channels, - num_outs, - start_level=0, - end_level=-1, - add_extra_convs=False, - extra_convs_on_inputs=False, - relu_before_extra_convs=False, - no_norm_on_lateral=False, - conv_cfg=None, - norm_cfg=None, - act_cfg=None, - upsample_cfg=dict(mode='nearest'), - init_cfg=dict( - type='Xavier', layer='Conv2d', distribution='uniform')): - super(FPN, self).__init__(init_cfg) - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.num_ins = len(in_channels) - self.num_outs = num_outs - self.relu_before_extra_convs = relu_before_extra_convs - self.no_norm_on_lateral = no_norm_on_lateral - self.fp16_enabled = False - self.upsample_cfg = upsample_cfg.copy() - - if end_level == -1: - self.backbone_end_level = self.num_ins - assert num_outs >= self.num_ins - start_level - else: - # if end_level < inputs, no extra level is allowed - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - assert num_outs == end_level - start_level - self.start_level = start_level - self.end_level = end_level - self.add_extra_convs = add_extra_convs - assert isinstance(add_extra_convs, (str, bool)) - if isinstance(add_extra_convs, str): - # Extra_convs_source choices: 'on_input', 'on_lateral', 'on_output' - assert add_extra_convs in ('on_input', 'on_lateral', 'on_output') - elif add_extra_convs: # True - if extra_convs_on_inputs: - # For compatibility with previous release - # TODO: deprecate `extra_convs_on_inputs` - self.add_extra_convs = 'on_input' - else: - self.add_extra_convs = 'on_output' - - self.lateral_convs = nn.ModuleList() - self.fpn_convs = nn.ModuleList() - - for i in range(self.start_level, self.backbone_end_level): - l_conv = ConvModule( - in_channels[i], - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg if not self.no_norm_on_lateral else None, - act_cfg=act_cfg, - inplace=False) - fpn_conv = ConvModule( - out_channels, - out_channels, - 3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - - self.lateral_convs.append(l_conv) - self.fpn_convs.append(fpn_conv) - - # add extra conv layers (e.g., RetinaNet) - extra_levels = num_outs - self.backbone_end_level + self.start_level - if self.add_extra_convs and extra_levels >= 1: - for i in range(extra_levels): - if i == 0 and self.add_extra_convs == 'on_input': - in_channels = self.in_channels[self.backbone_end_level - 1] - else: - in_channels = out_channels - extra_fpn_conv = ConvModule( - in_channels, - out_channels, - 3, - stride=2, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - self.fpn_convs.append(extra_fpn_conv) - - @auto_fp16() - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # build laterals - laterals = [ - lateral_conv(inputs[i + self.start_level]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - - # build top-down path - used_backbone_levels = len(laterals) - for i in range(used_backbone_levels - 1, 0, -1): - # In some cases, fixing `scale factor` (e.g. 2) is preferred, but - # it cannot co-exist with `size` in `F.interpolate`. - if 'scale_factor' in self.upsample_cfg: - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], **self.upsample_cfg) - else: - prev_shape = laterals[i - 1].shape[2:] - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], size=prev_shape, **self.upsample_cfg) - - # build outputs - # part 1: from original levels - outs = [ - self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels) - ] - # part 2: add extra levels - if self.num_outs > len(outs): - # use max pool to get more levels on top of outputs - # (e.g., Faster R-CNN, Mask R-CNN) - if not self.add_extra_convs: - for i in range(self.num_outs - used_backbone_levels): - outs.append(F.max_pool2d(outs[-1], 1, stride=2)) - # add conv layers on top of original feature maps (RetinaNet) - else: - if self.add_extra_convs == 'on_input': - extra_source = inputs[self.backbone_end_level - 1] - elif self.add_extra_convs == 'on_lateral': - extra_source = laterals[-1] - elif self.add_extra_convs == 'on_output': - extra_source = outs[-1] - else: - raise NotImplementedError - outs.append(self.fpn_convs[used_backbone_levels](extra_source)) - for i in range(used_backbone_levels + 1, self.num_outs): - if self.relu_before_extra_convs: - outs.append(self.fpn_convs[i](F.relu(outs[-1]))) - else: - outs.append(self.fpn_convs[i](outs[-1])) - return tuple(outs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/ic_neck.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/ic_neck.py deleted file mode 100644 index a5d81cef8..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/ic_neck.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -class CascadeFeatureFusion(BaseModule): - """Cascade Feature Fusion Unit in ICNet. - - Args: - low_channels (int): The number of input channels for - low resolution feature map. - high_channels (int): The number of input channels for - high resolution feature map. - out_channels (int): The number of output channels. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Returns: - x (Tensor): The output tensor of shape (N, out_channels, H, W). - x_low (Tensor): The output tensor of shape (N, out_channels, H, W) - for Cascade Label Guidance in auxiliary heads. - """ - - def __init__(self, - low_channels, - high_channels, - out_channels, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(CascadeFeatureFusion, self).__init__(init_cfg=init_cfg) - self.align_corners = align_corners - self.conv_low = ConvModule( - low_channels, - out_channels, - 3, - padding=2, - dilation=2, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.conv_high = ConvModule( - high_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, x_low, x_high): - x_low = resize( - x_low, - size=x_high.size()[2:], - mode='bilinear', - align_corners=self.align_corners) - # Note: Different from original paper, `x_low` is underwent - # `self.conv_low` rather than another 1x1 conv classifier - # before being used for auxiliary head. - x_low = self.conv_low(x_low) - x_high = self.conv_high(x_high) - x = x_low + x_high - x = F.relu(x, inplace=True) - return x, x_low - - -@NECKS.register_module() -class ICNeck(BaseModule): - """ICNet for Real-Time Semantic Segmentation on High-Resolution Images. - - This head is the implementation of `ICHead - `_. - - Args: - in_channels (int): The number of input image channels. Default: 3. - out_channels (int): The numbers of output feature channels. - Default: 128. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(64, 256, 256), - out_channels=128, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(ICNeck, self).__init__(init_cfg=init_cfg) - assert len(in_channels) == 3, 'Length of input channels \ - must be 3!' - - self.in_channels = in_channels - self.out_channels = out_channels - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.align_corners = align_corners - self.cff_24 = CascadeFeatureFusion( - self.in_channels[2], - self.in_channels[1], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - self.cff_12 = CascadeFeatureFusion( - self.out_channels, - self.in_channels[0], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - def forward(self, inputs): - assert len(inputs) == 3, 'Length of input feature \ - maps must be 3!' - - x_sub1, x_sub2, x_sub4 = inputs - x_cff_24, x_24 = self.cff_24(x_sub4, x_sub2) - x_cff_12, x_12 = self.cff_12(x_cff_24, x_sub1) - # Note: `x_cff_12` is used for decode_head, - # `x_24` and `x_12` are used for auxiliary head. - return x_24, x_12, x_cff_12 diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/jpu.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/jpu.py deleted file mode 100644 index 3cc6b9f42..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/jpu.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, DepthwiseSeparableConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class JPU(BaseModule): - """FastFCN: Rethinking Dilated Convolution in the Backbone - for Semantic Segmentation. - - This Joint Pyramid Upsampling (JPU) neck is the implementation of - `FastFCN `_. - - Args: - in_channels (Tuple[int], optional): The number of input channels - for each convolution operations before upsampling. - Default: (512, 1024, 2048). - mid_channels (int): The number of output channels of JPU. - Default: 512. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - dilations (tuple[int]): Dilation rate of each Depthwise - Separable ConvModule. Default: (1, 2, 4, 8). - align_corners (bool, optional): The align_corners argument of - resize operation. Default: False. - conv_cfg (dict | None): Config of conv layers. - Default: None. - norm_cfg (dict | None): Config of norm layers. - Default: dict(type='BN'). - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU'). - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(512, 1024, 2048), - mid_channels=512, - start_level=0, - end_level=-1, - dilations=(1, 2, 4, 8), - align_corners=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(JPU, self).__init__(init_cfg=init_cfg) - assert isinstance(in_channels, tuple) - assert isinstance(dilations, tuple) - self.in_channels = in_channels - self.mid_channels = mid_channels - self.start_level = start_level - self.num_ins = len(in_channels) - if end_level == -1: - self.backbone_end_level = self.num_ins - else: - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - - self.dilations = dilations - self.align_corners = align_corners - - self.conv_layers = nn.ModuleList() - self.dilation_layers = nn.ModuleList() - for i in range(self.start_level, self.backbone_end_level): - conv_layer = nn.Sequential( - ConvModule( - self.in_channels[i], - self.mid_channels, - kernel_size=3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.conv_layers.append(conv_layer) - for i in range(len(dilations)): - dilation_layer = nn.Sequential( - DepthwiseSeparableConvModule( - in_channels=(self.backbone_end_level - self.start_level) * - self.mid_channels, - out_channels=self.mid_channels, - kernel_size=3, - stride=1, - padding=dilations[i], - dilation=dilations[i], - dw_norm_cfg=norm_cfg, - dw_act_cfg=None, - pw_norm_cfg=norm_cfg, - pw_act_cfg=act_cfg)) - self.dilation_layers.append(dilation_layer) - - def forward(self, inputs): - """Forward function.""" - assert len(inputs) == len(self.in_channels), 'Length of inputs must \ - be the same with self.in_channels!' - - feats = [ - self.conv_layers[i - self.start_level](inputs[i]) - for i in range(self.start_level, self.backbone_end_level) - ] - - h, w = feats[0].shape[2:] - for i in range(1, len(feats)): - feats[i] = resize( - feats[i], - size=(h, w), - mode='bilinear', - align_corners=self.align_corners) - - feat = torch.cat(feats, dim=1) - concat_feat = torch.cat([ - self.dilation_layers[i](feat) for i in range(len(self.dilations)) - ], - dim=1) - - outs = [] - - # Default: outs[2] is the output of JPU for decoder head, outs[1] is - # the feature map from backbone for auxiliary head. Additionally, - # outs[0] can also be used for auxiliary head. - for i in range(self.start_level, self.backbone_end_level - 1): - outs.append(inputs[i]) - outs.append(concat_feat) - return tuple(outs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/mla_neck.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/mla_neck.py deleted file mode 100644 index 1513e296d..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/mla_neck.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, build_norm_layer - -from ..builder import NECKS - - -class MLAModule(nn.Module): - - def __init__(self, - in_channels=[1024, 1024, 1024, 1024], - out_channels=256, - norm_cfg=None, - act_cfg=None): - super(MLAModule, self).__init__() - self.channel_proj = nn.ModuleList() - for i in range(len(in_channels)): - self.channel_proj.append( - ConvModule( - in_channels=in_channels[i], - out_channels=out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.feat_extract = nn.ModuleList() - for i in range(len(in_channels)): - self.feat_extract.append( - ConvModule( - in_channels=out_channels, - out_channels=out_channels, - kernel_size=3, - padding=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, inputs): - - # feat_list -> [p2, p3, p4, p5] - feat_list = [] - for x, conv in zip(inputs, self.channel_proj): - feat_list.append(conv(x)) - - # feat_list -> [p5, p4, p3, p2] - # mid_list -> [m5, m4, m3, m2] - feat_list = feat_list[::-1] - mid_list = [] - for feat in feat_list: - if len(mid_list) == 0: - mid_list.append(feat) - else: - mid_list.append(mid_list[-1] + feat) - - # mid_list -> [m5, m4, m3, m2] - # out_list -> [o2, o3, o4, o5] - out_list = [] - for mid, conv in zip(mid_list, self.feat_extract): - out_list.append(conv(mid)) - - return tuple(out_list) - - -@NECKS.register_module() -class MLANeck(nn.Module): - """Multi-level Feature Aggregation. - - This neck is `The Multi-level Feature Aggregation construction of - SETR `_. - - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - norm_layer (dict): Config dict for input normalization. - Default: norm_layer=dict(type='LN', eps=1e-6, requires_grad=True). - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - norm_layer=dict(type='LN', eps=1e-6, requires_grad=True), - norm_cfg=None, - act_cfg=None): - super(MLANeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - - # In order to build general vision transformer backbone, we have to - # move MLA to neck. - self.norm = nn.ModuleList([ - build_norm_layer(norm_layer, in_channels[i])[1] - for i in range(len(in_channels)) - ]) - - self.mla = MLAModule( - in_channels=in_channels, - out_channels=out_channels, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # Convert from nchw to nlc - outs = [] - for i in range(len(inputs)): - x = inputs[i] - n, c, h, w = x.shape - x = x.reshape(n, c, h * w).transpose(2, 1).contiguous() - x = self.norm[i](x) - x = x.transpose(1, 2).reshape(n, c, h, w).contiguous() - outs.append(x) - - outs = self.mla(outs) - return tuple(outs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/multilevel_neck.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/multilevel_neck.py deleted file mode 100644 index 5151f8762..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/necks/multilevel_neck.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, xavier_init - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class MultiLevelNeck(nn.Module): - """MultiLevelNeck. - - A neck structure connect vit backbone and decoder_heads. - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - scales (List[float]): Scale factors for each input feature map. - Default: [0.5, 1, 2, 4] - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - scales=[0.5, 1, 2, 4], - norm_cfg=None, - act_cfg=None): - super(MultiLevelNeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.scales = scales - self.num_outs = len(scales) - self.lateral_convs = nn.ModuleList() - self.convs = nn.ModuleList() - for in_channel in in_channels: - self.lateral_convs.append( - ConvModule( - in_channel, - out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - for _ in range(self.num_outs): - self.convs.append( - ConvModule( - out_channels, - out_channels, - kernel_size=3, - padding=1, - stride=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - # default init_weights for conv(msra) and norm in ConvModule - def init_weights(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - xavier_init(m, distribution='uniform') - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - inputs = [ - lateral_conv(inputs[i]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - # for len(inputs) not equal to self.num_outs - if len(inputs) == 1: - inputs = [inputs[0] for _ in range(self.num_outs)] - outs = [] - for i in range(self.num_outs): - x_resize = resize( - inputs[i], scale_factor=self.scales[i], mode='bilinear') - outs.append(self.convs[i](x_resize)) - return tuple(outs) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/__init__.py deleted file mode 100644 index 387c858bd..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseSegmentor -from .cascade_encoder_decoder import CascadeEncoderDecoder -from .encoder_decoder import EncoderDecoder - -__all__ = ['BaseSegmentor', 'EncoderDecoder', 'CascadeEncoderDecoder'] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/base.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/base.py deleted file mode 100644 index 76dc8f075..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/base.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from abc import ABCMeta, abstractmethod -from collections import OrderedDict - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import BaseModule, auto_fp16 - - -class BaseSegmentor(BaseModule, metaclass=ABCMeta): - """Base class for segmentors.""" - - def __init__(self, init_cfg=None): - super(BaseSegmentor, self).__init__(init_cfg) - self.fp16_enabled = False - - @property - def with_neck(self): - """bool: whether the segmentor has neck""" - return hasattr(self, 'neck') and self.neck is not None - - @property - def with_auxiliary_head(self): - """bool: whether the segmentor has auxiliary head""" - return hasattr(self, - 'auxiliary_head') and self.auxiliary_head is not None - - @property - def with_decode_head(self): - """bool: whether the segmentor has decode head""" - return hasattr(self, 'decode_head') and self.decode_head is not None - - @abstractmethod - def extract_feat(self, imgs): - """Placeholder for extract features from images.""" - pass - - @abstractmethod - def encode_decode(self, img, img_metas): - """Placeholder for encode images with backbone and decode into a - semantic segmentation map of the same size as input.""" - pass - - @abstractmethod - def forward_train(self, imgs, img_metas, **kwargs): - """Placeholder for Forward function for training.""" - pass - - @abstractmethod - def simple_test(self, img, img_meta, **kwargs): - """Placeholder for single image test.""" - pass - - @abstractmethod - def aug_test(self, imgs, img_metas, **kwargs): - """Placeholder for augmentation test.""" - pass - - def forward_test(self, imgs, img_metas, **kwargs): - """ - Args: - imgs (List[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains all images in the batch. - img_metas (List[List[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. - """ - for var, name in [(imgs, 'imgs'), (img_metas, 'img_metas')]: - if not isinstance(var, list): - raise TypeError(f'{name} must be a list, but got ' - f'{type(var)}') - - num_augs = len(imgs) - if num_augs != len(img_metas): - raise ValueError(f'num of augmentations ({len(imgs)}) != ' - f'num of image meta ({len(img_metas)})') - # all images in the same aug batch all of the same ori_shape and pad - # shape - for img_meta in img_metas: - ori_shapes = [_['ori_shape'] for _ in img_meta] - assert all(shape == ori_shapes[0] for shape in ori_shapes) - img_shapes = [_['img_shape'] for _ in img_meta] - assert all(shape == img_shapes[0] for shape in img_shapes) - pad_shapes = [_['pad_shape'] for _ in img_meta] - assert all(shape == pad_shapes[0] for shape in pad_shapes) - - if num_augs == 1: - return self.simple_test(imgs[0], img_metas[0], **kwargs) - else: - return self.aug_test(imgs, img_metas, **kwargs) - - @auto_fp16(apply_to=('img', )) - def forward(self, img, img_metas, return_loss=True, **kwargs): - """Calls either :func:`forward_train` or :func:`forward_test` depending - on whether ``return_loss`` is ``True``. - - Note this setting will change the expected inputs. When - ``return_loss=True``, img and img_meta are single-nested (i.e. Tensor - and List[dict]), and when ``resturn_loss=False``, img and img_meta - should be double nested (i.e. List[Tensor], List[List[dict]]), with - the outer list indicating test time augmentations. - """ - if return_loss: - return self.forward_train(img, img_metas, **kwargs) - else: - return self.forward_test(img, img_metas, **kwargs) - - def train_step(self, data_batch, optimizer, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating is also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer` | dict): The optimizer of - runner is passed to ``train_step()``. This argument is unused - and reserved. - - Returns: - dict: It should contain at least 3 keys: ``loss``, ``log_vars``, - ``num_samples``. - ``loss`` is a tensor for back propagation, which can be a - weighted sum of multiple losses. - ``log_vars`` contains all the variables to be sent to the - logger. - ``num_samples`` indicates the batch size (when the model is - DDP, it means the batch size on each GPU), which is used for - averaging the logs. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, - log_vars=log_vars, - num_samples=len(data_batch['img_metas'])) - - return outputs - - def val_step(self, data_batch, optimizer=None, **kwargs): - """The iteration step during validation. - - This method shares the same signature as :func:`train_step`, but used - during val epochs. Note that the evaluation after training epochs is - not implemented with this method, but an evaluation hook. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - log_vars_ = dict() - for loss_name, loss_value in log_vars.items(): - k = loss_name + '_val' - log_vars_[k] = loss_value - - outputs = dict( - loss=loss, - log_vars=log_vars_, - num_samples=len(data_batch['img_metas'])) - - return outputs - - @staticmethod - def _parse_losses(losses): - """Parse the raw outputs (losses) of the network. - - Args: - losses (dict): Raw output of the network, which usually contain - losses and other necessary information. - - Returns: - tuple[Tensor, dict]: (loss, log_vars), loss is the loss tensor - which may be a weighted sum of all losses, log_vars contains - all the variables to be sent to the logger. - """ - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(_value for _key, _value in log_vars.items() - if 'loss' in _key) - - # If the loss_vars has different length, raise assertion error - # to prevent GPUs from infinite waiting. - if dist.is_available() and dist.is_initialized(): - log_var_length = torch.tensor(len(log_vars), device=loss.device) - dist.all_reduce(log_var_length) - message = (f'rank {dist.get_rank()}' + - f' len(log_vars): {len(log_vars)}' + ' keys: ' + - ','.join(log_vars.keys()) + '\n') - assert log_var_length == len(log_vars) * dist.get_world_size(), \ - 'loss log variables are different across GPUs!\n' + message - - log_vars['loss'] = loss - for loss_name, loss_value in log_vars.items(): - # reduce loss when distributed training - if dist.is_available() and dist.is_initialized(): - loss_value = loss_value.data.clone() - dist.all_reduce(loss_value.div_(dist.get_world_size())) - log_vars[loss_name] = loss_value.item() - - return loss, log_vars - - def show_result(self, - img, - result, - palette=None, - win_name='', - show=False, - wait_time=0, - out_file=None, - opacity=0.5): - """Draw `result` over `img`. - - Args: - img (str or Tensor): The image to be displayed. - result (Tensor): The semantic segmentation results to draw over - `img`. - palette (list[list[int]]] | np.ndarray | None): The palette of - segmentation map. If None is given, random palette will be - generated. Default: None - win_name (str): The window name. - wait_time (int): Value of waitKey param. - Default: 0. - show (bool): Whether to show the image. - Default: False. - out_file (str or None): The filename to write the image. - Default: None. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - Returns: - img (Tensor): Only if not `show` or `out_file` - """ - img = mmcv.imread(img) - img = img.copy() - seg = result[0] - if palette is None: - if self.PALETTE is None: - # Get random state before set seed, - # and restore random state later. - # It will prevent loss of randomness, as the palette - # may be different in each iteration if not specified. - # See: https://github.com/open-mmlab/mmdetection/issues/5844 - state = np.random.get_state() - np.random.seed(42) - # random palette - palette = np.random.randint( - 0, 255, size=(len(self.CLASSES), 3)) - np.random.set_state(state) - else: - palette = self.PALETTE - palette = np.array(palette) - assert palette.shape[0] == len(self.CLASSES) - assert palette.shape[1] == 3 - assert len(palette.shape) == 2 - assert 0 < opacity <= 1.0 - color_seg = np.zeros((seg.shape[0], seg.shape[1], 3), dtype=np.uint8) - for label, color in enumerate(palette): - color_seg[seg == label, :] = color - # convert to BGR - color_seg = color_seg[..., ::-1] - - img = img * (1 - opacity) + color_seg * opacity - img = img.astype(np.uint8) - # if out_file specified, do not show image in window - if out_file is not None: - show = False - - if show: - mmcv.imshow(img, win_name, wait_time) - if out_file is not None: - mmcv.imwrite(img, out_file) - - if not (show or out_file): - warnings.warn('show==False and out_file is not specified, only ' - 'result image will be returned') - return img diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py deleted file mode 100644 index 1913a22e2..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from mmseg.core import add_prefix -from mmseg.ops import resize -from .. import builder -from ..builder import SEGMENTORS -from .encoder_decoder import EncoderDecoder - - -@SEGMENTORS.register_module() -class CascadeEncoderDecoder(EncoderDecoder): - """Cascade Encoder Decoder segmentors. - - CascadeEncoderDecoder almost the same as EncoderDecoder, while decoders of - CascadeEncoderDecoder are cascaded. The output of previous decoder_head - will be the input of next decoder_head. - """ - - def __init__(self, - num_stages, - backbone, - decode_head, - neck=None, - auxiliary_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - self.num_stages = num_stages - super(CascadeEncoderDecoder, self).__init__( - backbone=backbone, - decode_head=decode_head, - neck=neck, - auxiliary_head=auxiliary_head, - train_cfg=train_cfg, - test_cfg=test_cfg, - pretrained=pretrained, - init_cfg=init_cfg) - - def _init_decode_head(self, decode_head): - """Initialize ``decode_head``""" - assert isinstance(decode_head, list) - assert len(decode_head) == self.num_stages - self.decode_head = nn.ModuleList() - for i in range(self.num_stages): - self.decode_head.append(builder.build_head(decode_head[i])) - self.align_corners = self.decode_head[-1].align_corners - self.num_classes = self.decode_head[-1].num_classes - - def encode_decode(self, img, img_metas): - """Encode images with backbone and decode into a semantic segmentation - map of the same size as input.""" - x = self.extract_feat(img) - out = self.decode_head[0].forward_test(x, img_metas, self.test_cfg) - for i in range(1, self.num_stages): - out = self.decode_head[i].forward_test(x, out, img_metas, - self.test_cfg) - out = resize( - input=out, - size=img.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - return out - - def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for decode head in - training.""" - losses = dict() - - loss_decode = self.decode_head[0].forward_train( - x, img_metas, gt_semantic_seg, self.train_cfg) - - losses.update(add_prefix(loss_decode, 'decode_0')) - - for i in range(1, self.num_stages): - # forward test again, maybe unnecessary for most methods. - if i == 1: - prev_outputs = self.decode_head[0].forward_test( - x, img_metas, self.test_cfg) - else: - prev_outputs = self.decode_head[i - 1].forward_test( - x, prev_outputs, img_metas, self.test_cfg) - loss_decode = self.decode_head[i].forward_train( - x, prev_outputs, img_metas, gt_semantic_seg, self.train_cfg) - losses.update(add_prefix(loss_decode, f'decode_{i}')) - - return losses diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/encoder_decoder.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/encoder_decoder.py deleted file mode 100644 index 72467b469..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/segmentors/encoder_decoder.py +++ /dev/null @@ -1,284 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmseg.core import add_prefix -from mmseg.ops import resize -from .. import builder -from ..builder import SEGMENTORS -from .base import BaseSegmentor - - -@SEGMENTORS.register_module() -class EncoderDecoder(BaseSegmentor): - """Encoder Decoder segmentors. - - EncoderDecoder typically consists of backbone, decode_head, auxiliary_head. - Note that auxiliary_head is only used for deep supervision during training, - which could be dumped during inference. - """ - - def __init__(self, - backbone, - decode_head, - neck=None, - auxiliary_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - super(EncoderDecoder, self).__init__(init_cfg) - if pretrained is not None: - assert backbone.get('pretrained') is None, \ - 'both backbone and segmentor set pretrained weight' - backbone.pretrained = pretrained - self.backbone = builder.build_backbone(backbone) - if neck is not None: - self.neck = builder.build_neck(neck) - self._init_decode_head(decode_head) - self._init_auxiliary_head(auxiliary_head) - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - assert self.with_decode_head - - def _init_decode_head(self, decode_head): - """Initialize ``decode_head``""" - self.decode_head = builder.build_head(decode_head) - self.align_corners = self.decode_head.align_corners - self.num_classes = self.decode_head.num_classes - - def _init_auxiliary_head(self, auxiliary_head): - """Initialize ``auxiliary_head``""" - if auxiliary_head is not None: - if isinstance(auxiliary_head, list): - self.auxiliary_head = nn.ModuleList() - for head_cfg in auxiliary_head: - self.auxiliary_head.append(builder.build_head(head_cfg)) - else: - self.auxiliary_head = builder.build_head(auxiliary_head) - - def extract_feat(self, img): - """Extract features from images.""" - x = self.backbone(img) - if self.with_neck: - x = self.neck(x) - return x - - def encode_decode(self, img, img_metas): - """Encode images with backbone and decode into a semantic segmentation - map of the same size as input.""" - x = self.extract_feat(img) - out = self._decode_head_forward_test(x, img_metas) - out = resize( - input=out, - size=img.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - return out - - def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for decode head in - training.""" - losses = dict() - loss_decode = self.decode_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - - losses.update(add_prefix(loss_decode, 'decode')) - return losses - - def _decode_head_forward_test(self, x, img_metas): - """Run forward function and calculate loss for decode head in - inference.""" - seg_logits = self.decode_head.forward_test(x, img_metas, self.test_cfg) - return seg_logits - - def _auxiliary_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for auxiliary head in - training.""" - losses = dict() - if isinstance(self.auxiliary_head, nn.ModuleList): - for idx, aux_head in enumerate(self.auxiliary_head): - loss_aux = aux_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - losses.update(add_prefix(loss_aux, f'aux_{idx}')) - else: - loss_aux = self.auxiliary_head.forward_train( - x, img_metas, gt_semantic_seg, self.train_cfg) - losses.update(add_prefix(loss_aux, 'aux')) - - return losses - - def forward_dummy(self, img): - """Dummy forward function.""" - seg_logit = self.encode_decode(img, None) - - return seg_logit - - def forward_train(self, img, img_metas, gt_semantic_seg): - """Forward function for training. - - Args: - img (Tensor): Input images. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - - x = self.extract_feat(img) - - losses = dict() - - loss_decode = self._decode_head_forward_train(x, img_metas, - gt_semantic_seg) - losses.update(loss_decode) - - if self.with_auxiliary_head: - loss_aux = self._auxiliary_head_forward_train( - x, img_metas, gt_semantic_seg) - losses.update(loss_aux) - - return losses - - # TODO refactor - def slide_inference(self, img, img_meta, rescale): - """Inference by sliding-window with overlap. - - If h_crop > h_img or w_crop > w_img, the small patch will be used to - decode without padding. - """ - - h_stride, w_stride = self.test_cfg.stride - h_crop, w_crop = self.test_cfg.crop_size - batch_size, _, h_img, w_img = img.size() - num_classes = self.num_classes - h_grids = max(h_img - h_crop + h_stride - 1, 0) // h_stride + 1 - w_grids = max(w_img - w_crop + w_stride - 1, 0) // w_stride + 1 - preds = img.new_zeros((batch_size, num_classes, h_img, w_img)) - count_mat = img.new_zeros((batch_size, 1, h_img, w_img)) - for h_idx in range(h_grids): - for w_idx in range(w_grids): - y1 = h_idx * h_stride - x1 = w_idx * w_stride - y2 = min(y1 + h_crop, h_img) - x2 = min(x1 + w_crop, w_img) - y1 = max(y2 - h_crop, 0) - x1 = max(x2 - w_crop, 0) - crop_img = img[:, :, y1:y2, x1:x2] - crop_seg_logit = self.encode_decode(crop_img, img_meta) - preds += F.pad(crop_seg_logit, - (int(x1), int(preds.shape[3] - x2), int(y1), - int(preds.shape[2] - y2))) - - count_mat[:, :, y1:y2, x1:x2] += 1 - assert (count_mat == 0).sum() == 0 - if torch.onnx.is_in_onnx_export(): - # cast count_mat to constant while exporting to ONNX - count_mat = torch.from_numpy( - count_mat.cpu().detach().numpy()).to(device=img.device) - preds = preds / count_mat - if rescale: - preds = resize( - preds, - size=img_meta[0]['ori_shape'][:2], - mode='bilinear', - align_corners=self.align_corners, - warning=False) - return preds - - def whole_inference(self, img, img_meta, rescale): - """Inference with full image.""" - - seg_logit = self.encode_decode(img, img_meta) - if rescale: - # support dynamic shape for onnx - if torch.onnx.is_in_onnx_export(): - size = img.shape[2:] - else: - size = img_meta[0]['ori_shape'][:2] - seg_logit = resize( - seg_logit, - size=size, - mode='bilinear', - align_corners=self.align_corners, - warning=False) - - return seg_logit - - def inference(self, img, img_meta, rescale): - """Inference with slide/whole style. - - Args: - img (Tensor): The input image of shape (N, 3, H, W). - img_meta (dict): Image info dict where each dict has: 'img_shape', - 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - rescale (bool): Whether rescale back to original shape. - - Returns: - Tensor: The output segmentation map. - """ - - assert self.test_cfg.mode in ['slide', 'whole'] - ori_shape = img_meta[0]['ori_shape'] - assert all(_['ori_shape'] == ori_shape for _ in img_meta) - if self.test_cfg.mode == 'slide': - seg_logit = self.slide_inference(img, img_meta, rescale) - else: - seg_logit = self.whole_inference(img, img_meta, rescale) - output = F.softmax(seg_logit, dim=1) - flip = img_meta[0]['flip'] - if flip: - flip_direction = img_meta[0]['flip_direction'] - assert flip_direction in ['horizontal', 'vertical'] - if flip_direction == 'horizontal': - output = output.flip(dims=(3, )) - elif flip_direction == 'vertical': - output = output.flip(dims=(2, )) - - return output - - def simple_test(self, img, img_meta, rescale=True): - """Simple test with single image.""" - seg_logit = self.inference(img, img_meta, rescale) - seg_pred = seg_logit.argmax(dim=1) - if torch.onnx.is_in_onnx_export(): - # our inference backend only support 4D output - seg_pred = seg_pred.unsqueeze(0) - return seg_pred - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred - - def aug_test(self, imgs, img_metas, rescale=True): - """Test with augmentations. - - Only rescale=True is supported. - """ - # aug_test rescale all imgs back to ori_shape for now - assert rescale - # to save memory, we get augmented seg logit inplace - seg_logit = self.inference(imgs[0], img_metas[0], rescale) - for i in range(1, len(imgs)): - cur_seg_logit = self.inference(imgs[i], img_metas[i], rescale) - seg_logit += cur_seg_logit - seg_logit /= len(imgs) - seg_pred = seg_logit.argmax(dim=1) - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/__init__.py deleted file mode 100644 index fc281e3d6..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .embed import PatchEmbed -from .inverted_residual import InvertedResidual, InvertedResidualV3 -from .make_divisible import make_divisible -from .res_layer import ResLayer -from .se_layer import SELayer -from .self_attention_block import SelfAttentionBlock -from .shape_convert import (nchw2nlc2nchw, nchw_to_nlc, nlc2nchw2nlc, - nlc_to_nchw) -from .up_conv_block import UpConvBlock -from .wrappers import resize, Upsample - -__all__ = [ - 'ResLayer', 'SelfAttentionBlock', 'make_divisible', 'InvertedResidual', - 'UpConvBlock', 'InvertedResidualV3', 'SELayer', 'PatchEmbed', - 'nchw_to_nlc', 'nlc_to_nchw', 'nchw2nlc2nchw', 'nlc2nchw2nlc', - 'resize', 'Upsample' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/embed.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/embed.py deleted file mode 100644 index 1515675e1..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/embed.py +++ /dev/null @@ -1,330 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -from typing import Sequence - -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner.base_module import BaseModule -from mmcv.utils import to_2tuple - - -class AdaptivePadding(nn.Module): - """Applies padding to input (if needed) so that input can get fully covered - by filter you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad zero around - input. The "corner" mode would pad zero to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel: - stride (int | tuple): Stride of the filter. Default: 1: - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - - super(AdaptivePadding, self).__init__() - - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The config dict for embedding - conv layer type selection. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int, optional): The slide stride of embedding conv. - Default: None (Would be set as `kernel_size`). - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only work when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=None, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super(PatchEmbed, self).__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adap_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adap_padding: - pad_h, pad_w = self.adap_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adap_padding: - x = self.adap_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map. Our implementation uses `nn.Unfold` to - merge patch, which is about 25% faster than original implementation. - Instead, we need to modify pretrained models for compatibility. - - Args: - in_channels (int): The num of input channels. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adap_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - - if self.adap_padding: - x = self.adap_padding(x) - H, W = x.shape[-2:] - - x = self.sampler(x) - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/inverted_residual.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/inverted_residual.py deleted file mode 100644 index c9cda7682..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/inverted_residual.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import ConvModule -from torch import nn -from torch.utils import checkpoint as cp - -from .se_layer import SELayer - - -class InvertedResidual(nn.Module): - """InvertedResidual block for MobileNetV2. - - Args: - in_channels (int): The input channels of the InvertedResidual block. - out_channels (int): The output channels of the InvertedResidual block. - stride (int): Stride of the middle (first) 3x3 convolution. - expand_ratio (int): Adjusts number of channels of the hidden layer - in InvertedResidual by this amount. - dilation (int): Dilation rate of depthwise conv. Default: 1 - conv_cfg (dict): Config dict for convolution layer. - Default: None, which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU6'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - stride, - expand_ratio, - dilation=1, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU6'), - with_cp=False, - **kwargs): - super(InvertedResidual, self).__init__() - self.stride = stride - assert stride in [1, 2], f'stride must in [1, 2]. ' \ - f'But received {stride}.' - self.with_cp = with_cp - self.use_res_connect = self.stride == 1 and in_channels == out_channels - hidden_dim = int(round(in_channels * expand_ratio)) - - layers = [] - if expand_ratio != 1: - layers.append( - ConvModule( - in_channels=in_channels, - out_channels=hidden_dim, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs)) - layers.extend([ - ConvModule( - in_channels=hidden_dim, - out_channels=hidden_dim, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - groups=hidden_dim, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs), - ConvModule( - in_channels=hidden_dim, - out_channels=out_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None, - **kwargs) - ]) - self.conv = nn.Sequential(*layers) - - def forward(self, x): - - def _inner_forward(x): - if self.use_res_connect: - return x + self.conv(x) - else: - return self.conv(x) - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out - - -class InvertedResidualV3(nn.Module): - """Inverted Residual Block for MobileNetV3. - - Args: - in_channels (int): The input channels of this Module. - out_channels (int): The output channels of this Module. - mid_channels (int): The input channels of the depthwise convolution. - kernel_size (int): The kernel size of the depthwise convolution. - Default: 3. - stride (int): The stride of the depthwise convolution. Default: 1. - se_cfg (dict): Config dict for se layer. Default: None, which means no - se layer. - with_expand_conv (bool): Use expand conv or not. If set False, - mid_channels must be the same with in_channels. Default: True. - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - mid_channels, - kernel_size=3, - stride=1, - se_cfg=None, - with_expand_conv=True, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - with_cp=False): - super(InvertedResidualV3, self).__init__() - self.with_res_shortcut = (stride == 1 and in_channels == out_channels) - assert stride in [1, 2] - self.with_cp = with_cp - self.with_se = se_cfg is not None - self.with_expand_conv = with_expand_conv - - if self.with_se: - assert isinstance(se_cfg, dict) - if not self.with_expand_conv: - assert mid_channels == in_channels - - if self.with_expand_conv: - self.expand_conv = ConvModule( - in_channels=in_channels, - out_channels=mid_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.depthwise_conv = ConvModule( - in_channels=mid_channels, - out_channels=mid_channels, - kernel_size=kernel_size, - stride=stride, - padding=kernel_size // 2, - groups=mid_channels, - conv_cfg=dict( - type='Conv2dAdaptivePadding') if stride == 2 else conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - if self.with_se: - self.se = SELayer(**se_cfg) - - self.linear_conv = ConvModule( - in_channels=mid_channels, - out_channels=out_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - def forward(self, x): - - def _inner_forward(x): - out = x - - if self.with_expand_conv: - out = self.expand_conv(out) - - out = self.depthwise_conv(out) - - if self.with_se: - out = self.se(out) - - out = self.linear_conv(out) - - if self.with_res_shortcut: - return x + out - else: - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/make_divisible.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/make_divisible.py deleted file mode 100644 index ed42c2eee..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/make_divisible.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def make_divisible(value, divisor, min_value=None, min_ratio=0.9): - """Make divisible function. - - This function rounds the channel number to the nearest value that can be - divisible by the divisor. It is taken from the original tf repo. It ensures - that all layers have a channel number that is divisible by divisor. It can - be seen here: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py # noqa - - Args: - value (int): The original channel number. - divisor (int): The divisor to fully divide the channel number. - min_value (int): The minimum value of the output channel. - Default: None, means that the minimum value equal to the divisor. - min_ratio (float): The minimum ratio of the rounded channel number to - the original channel number. Default: 0.9. - - Returns: - int: The modified output channel number. - """ - - if min_value is None: - min_value = divisor - new_value = max(min_value, int(value + divisor / 2) // divisor * divisor) - # Make sure that round down does not go down by more than (1-min_ratio). - if new_value < min_ratio * value: - new_value += divisor - return new_value diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/res_layer.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/res_layer.py deleted file mode 100644 index 190a0c5d5..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/res_layer.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner import Sequential -from torch import nn as nn - - -class ResLayer(Sequential): - """ResLayer to build ResNet style backbone. - - Args: - block (nn.Module): block used to build ResLayer. - inplanes (int): inplanes of block. - planes (int): planes of block. - num_blocks (int): number of blocks. - stride (int): stride of the first block. Default: 1 - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False - conv_cfg (dict): dictionary to construct and config conv layer. - Default: None - norm_cfg (dict): dictionary to construct and config norm layer. - Default: dict(type='BN') - multi_grid (int | None): Multi grid dilation rates of last - stage. Default: None - contract_dilation (bool): Whether contract first dilation of each layer - Default: False - """ - - def __init__(self, - block, - inplanes, - planes, - num_blocks, - stride=1, - dilation=1, - avg_down=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - multi_grid=None, - contract_dilation=False, - **kwargs): - self.block = block - - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = [] - conv_stride = stride - if avg_down: - conv_stride = 1 - downsample.append( - nn.AvgPool2d( - kernel_size=stride, - stride=stride, - ceil_mode=True, - count_include_pad=False)) - downsample.extend([ - build_conv_layer( - conv_cfg, - inplanes, - planes * block.expansion, - kernel_size=1, - stride=conv_stride, - bias=False), - build_norm_layer(norm_cfg, planes * block.expansion)[1] - ]) - downsample = nn.Sequential(*downsample) - - layers = [] - if multi_grid is None: - if dilation > 1 and contract_dilation: - first_dilation = dilation // 2 - else: - first_dilation = dilation - else: - first_dilation = multi_grid[0] - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=stride, - dilation=first_dilation, - downsample=downsample, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - inplanes = planes * block.expansion - for i in range(1, num_blocks): - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=1, - dilation=dilation if multi_grid is None else multi_grid[i], - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - super(ResLayer, self).__init__(*layers) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/se_layer.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/se_layer.py deleted file mode 100644 index 16f52aa5c..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/se_layer.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch.nn as nn -from mmcv.cnn import ConvModule - -from .make_divisible import make_divisible - - -class SELayer(nn.Module): - """Squeeze-and-Excitation Module. - - Args: - channels (int): The input (and output) channels of the SE layer. - ratio (int): Squeeze ratio in SELayer, the intermediate channel will be - ``int(channels/ratio)``. Default: 16. - conv_cfg (None or dict): Config dict for convolution layer. - Default: None, which means using conv2d. - act_cfg (dict or Sequence[dict]): Config dict for activation layer. - If act_cfg is a dict, two activation layers will be configured - by this dict. If act_cfg is a sequence of dicts, the first - activation layer will be configured by the first dict and the - second activation layer will be configured by the second dict. - Default: (dict(type='ReLU'), dict(type='HSigmoid', bias=3.0, - divisor=6.0)). - """ - - def __init__(self, - channels, - ratio=16, - conv_cfg=None, - act_cfg=(dict(type='ReLU'), - dict(type='HSigmoid', bias=3.0, divisor=6.0))): - super(SELayer, self).__init__() - if isinstance(act_cfg, dict): - act_cfg = (act_cfg, act_cfg) - assert len(act_cfg) == 2 - assert mmcv.is_tuple_of(act_cfg, dict) - self.global_avgpool = nn.AdaptiveAvgPool2d(1) - self.conv1 = ConvModule( - in_channels=channels, - out_channels=make_divisible(channels // ratio, 8), - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[0]) - self.conv2 = ConvModule( - in_channels=make_divisible(channels // ratio, 8), - out_channels=channels, - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[1]) - - def forward(self, x): - out = self.global_avgpool(x) - out = self.conv1(out) - out = self.conv2(out) - return x * out diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/self_attention_block.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/self_attention_block.py deleted file mode 100644 index c945fa716..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/self_attention_block.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.cnn import ConvModule, constant_init -from torch import nn as nn -from torch.nn import functional as F - - -class SelfAttentionBlock(nn.Module): - """General self-attention block/non-local block. - - Please refer to https://arxiv.org/abs/1706.03762 for details about key, - query and value. - - Args: - key_in_channels (int): Input channels of key feature. - query_in_channels (int): Input channels of query feature. - channels (int): Output channels of key/query transform. - out_channels (int): Output channels. - share_key_query (bool): Whether share projection weight between key - and query projection. - query_downsample (nn.Module): Query downsample module. - key_downsample (nn.Module): Key downsample module. - key_query_num_convs (int): Number of convs for key/query projection. - value_num_convs (int): Number of convs for value projection. - matmul_norm (bool): Whether normalize attention map with sqrt of - channels - with_out (bool): Whether use out projection. - conv_cfg (dict|None): Config of conv layers. - norm_cfg (dict|None): Config of norm layers. - act_cfg (dict|None): Config of activation layers. - """ - - def __init__(self, key_in_channels, query_in_channels, channels, - out_channels, share_key_query, query_downsample, - key_downsample, key_query_num_convs, value_out_num_convs, - key_query_norm, value_out_norm, matmul_norm, with_out, - conv_cfg, norm_cfg, act_cfg): - super(SelfAttentionBlock, self).__init__() - if share_key_query: - assert key_in_channels == query_in_channels - self.key_in_channels = key_in_channels - self.query_in_channels = query_in_channels - self.out_channels = out_channels - self.channels = channels - self.share_key_query = share_key_query - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.key_project = self.build_project( - key_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if share_key_query: - self.query_project = self.key_project - else: - self.query_project = self.build_project( - query_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.value_project = self.build_project( - key_in_channels, - channels if with_out else out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if with_out: - self.out_project = self.build_project( - channels, - out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.out_project = None - - self.query_downsample = query_downsample - self.key_downsample = key_downsample - self.matmul_norm = matmul_norm - - self.init_weights() - - def init_weights(self): - """Initialize weight of later layer.""" - if self.out_project is not None: - if not isinstance(self.out_project, ConvModule): - constant_init(self.out_project, 0) - - def build_project(self, in_channels, channels, num_convs, use_conv_module, - conv_cfg, norm_cfg, act_cfg): - """Build projection layer for key/query/value/out.""" - if use_conv_module: - convs = [ - ConvModule( - in_channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - ] - for _ in range(num_convs - 1): - convs.append( - ConvModule( - channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - else: - convs = [nn.Conv2d(in_channels, channels, 1)] - for _ in range(num_convs - 1): - convs.append(nn.Conv2d(channels, channels, 1)) - if len(convs) > 1: - convs = nn.Sequential(*convs) - else: - convs = convs[0] - return convs - - def forward(self, query_feats, key_feats): - """Forward function.""" - batch_size = query_feats.size(0) - query = self.query_project(query_feats) - if self.query_downsample is not None: - query = self.query_downsample(query) - query = query.reshape(*query.shape[:2], -1) - query = query.permute(0, 2, 1).contiguous() - - key = self.key_project(key_feats) - value = self.value_project(key_feats) - if self.key_downsample is not None: - key = self.key_downsample(key) - value = self.key_downsample(value) - key = key.reshape(*key.shape[:2], -1) - value = value.reshape(*value.shape[:2], -1) - value = value.permute(0, 2, 1).contiguous() - - sim_map = torch.matmul(query, key) - if self.matmul_norm: - sim_map = (self.channels**-.5) * sim_map - sim_map = F.softmax(sim_map, dim=-1) - - context = torch.matmul(sim_map, value) - context = context.permute(0, 2, 1).contiguous() - context = context.reshape(batch_size, -1, *query_feats.shape[2:]) - if self.out_project is not None: - context = self.out_project(context) - return context diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/shape_convert.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/shape_convert.py deleted file mode 100644 index cce1e220b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/shape_convert.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def nlc_to_nchw(x, hw_shape): - """Convert [N, L, C] shape tensor to [N, C, H, W] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, L, C] before conversion. - hw_shape (Sequence[int]): The height and width of output feature map. - - Returns: - Tensor: The output tensor of shape [N, C, H, W] after conversion. - """ - H, W = hw_shape - assert len(x.shape) == 3 - B, L, C = x.shape - assert L == H * W, 'The seq_len doesn\'t match H, W' - return x.transpose(1, 2).reshape(B, C, H, W) - - -def nchw_to_nlc(x): - """Flatten [N, C, H, W] shape tensor to [N, L, C] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, C, H, W] before conversion. - - Returns: - Tensor: The output tensor of shape [N, L, C] after conversion. - """ - assert len(x.shape) == 4 - return x.flatten(2).transpose(1, 2).contiguous() - - -def nchw2nlc2nchw(module, x, contiguous=False, **kwargs): - """Flatten [N, C, H, W] shape tensor `x` to [N, L, C] shape tensor. Use the - reshaped tensor as the input of `module`, and the convert the output of - `module`, whose shape is. - - [N, L, C], to [N, C, H, W]. - - Args: - module (Callable): A callable object the takes a tensor - with shape [N, L, C] as input. - x (Tensor): The input tensor of shape [N, C, H, W]. - contiguous: - contiguous (Bool): Whether to make the tensor contiguous - after each shape transform. - - Returns: - Tensor: The output tensor of shape [N, C, H, W]. - - Example: - >>> import torch - >>> import torch.nn as nn - >>> norm = nn.LayerNorm(4) - >>> feature_map = torch.rand(4, 4, 5, 5) - >>> output = nchw2nlc2nchw(norm, feature_map) - """ - B, C, H, W = x.shape - if not contiguous: - x = x.flatten(2).transpose(1, 2) - x = module(x, **kwargs) - x = x.transpose(1, 2).reshape(B, C, H, W) - else: - x = x.flatten(2).transpose(1, 2).contiguous() - x = module(x, **kwargs) - x = x.transpose(1, 2).reshape(B, C, H, W).contiguous() - return x - - -def nlc2nchw2nlc(module, x, hw_shape, contiguous=False, **kwargs): - """Convert [N, L, C] shape tensor `x` to [N, C, H, W] shape tensor. Use the - reshaped tensor as the input of `module`, and convert the output of - `module`, whose shape is. - - [N, C, H, W], to [N, L, C]. - - Args: - module (Callable): A callable object the takes a tensor - with shape [N, C, H, W] as input. - x (Tensor): The input tensor of shape [N, L, C]. - hw_shape: (Sequence[int]): The height and width of the - feature map with shape [N, C, H, W]. - contiguous (Bool): Whether to make the tensor contiguous - after each shape transform. - - Returns: - Tensor: The output tensor of shape [N, L, C]. - - Example: - >>> import torch - >>> import torch.nn as nn - >>> conv = nn.Conv2d(16, 16, 3, 1, 1) - >>> feature_map = torch.rand(4, 25, 16) - >>> output = nlc2nchw2nlc(conv, feature_map, (5, 5)) - """ - H, W = hw_shape - assert len(x.shape) == 3 - B, L, C = x.shape - assert L == H * W, 'The seq_len doesn\'t match H, W' - if not contiguous: - x = x.transpose(1, 2).reshape(B, C, H, W) - x = module(x, **kwargs) - x = x.flatten(2).transpose(1, 2) - else: - x = x.transpose(1, 2).reshape(B, C, H, W).contiguous() - x = module(x, **kwargs) - x = x.flatten(2).transpose(1, 2).contiguous() - return x diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/up_conv_block.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/up_conv_block.py deleted file mode 100644 index d8396d9c2..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/up_conv_block.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, build_upsample_layer - - -class UpConvBlock(nn.Module): - """Upsample convolution block in decoder for UNet. - - This upsample convolution block consists of one upsample module - followed by one convolution block. The upsample module expands the - high-level low-resolution feature map and the convolution block fuses - the upsampled high-level low-resolution feature map and the low-level - high-resolution feature map from encoder. - - Args: - conv_block (nn.Sequential): Sequential of convolutional layers. - in_channels (int): Number of input channels of the high-level - skip_channels (int): Number of input channels of the low-level - high-resolution feature map from encoder. - out_channels (int): Number of output channels. - num_convs (int): Number of convolutional layers in the conv_block. - Default: 2. - stride (int): Stride of convolutional layer in conv_block. Default: 1. - dilation (int): Dilation rate of convolutional layer in conv_block. - Default: 1. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - conv_cfg (dict | None): Config dict for convolution layer. - Default: None. - norm_cfg (dict | None): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict | None): Config dict for activation layer in ConvModule. - Default: dict(type='ReLU'). - upsample_cfg (dict): The upsample config of the upsample module in - decoder. Default: dict(type='InterpConv'). If the size of - high-level feature map is the same as that of skip feature map - (low-level feature map from encoder), it does not need upsample the - high-level feature map and the upsample_cfg is None. - dcn (bool): Use deformable convolution in convolutional layer or not. - Default: None. - plugins (dict): plugins for convolutional layers. Default: None. - """ - - def __init__(self, - conv_block, - in_channels, - skip_channels, - out_channels, - num_convs=2, - stride=1, - dilation=1, - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - upsample_cfg=dict(type='InterpConv'), - dcn=None, - plugins=None): - super(UpConvBlock, self).__init__() - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.conv_block = conv_block( - in_channels=2 * skip_channels, - out_channels=out_channels, - num_convs=num_convs, - stride=stride, - dilation=dilation, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - dcn=None, - plugins=None) - if upsample_cfg is not None: - self.upsample = build_upsample_layer( - cfg=upsample_cfg, - in_channels=in_channels, - out_channels=skip_channels, - with_cp=with_cp, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.upsample = ConvModule( - in_channels, - skip_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, skip, x): - """Forward function.""" - - x = self.upsample(x) - out = torch.cat([skip, x], dim=1) - out = self.conv_block(out) - - return out diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/wrappers.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/wrappers.py deleted file mode 100644 index abbd0c029..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/models/utils/wrappers.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.nn.functional as F - - -def resize(input, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None, - warning=True): - if warning: - if size is not None and align_corners: - input_h, input_w = tuple(int(x) for x in input.shape[2:]) - output_h, output_w = tuple(int(x) for x in size) - if output_h > input_h or output_w > output_h: - if ((output_h > 1 and output_w > 1 and input_h > 1 - and input_w > 1) and (output_h - 1) % (input_h - 1) - and (output_w - 1) % (input_w - 1)): - warnings.warn( - f'When align_corners={align_corners}, ' - 'the output would more aligned if ' - f'input size {(input_h, input_w)} is `x+1` and ' - f'out size {(output_h, output_w)} is `nx+1`') - return F.interpolate(input, size, scale_factor, mode, align_corners) - - -class Upsample(nn.Module): - - def __init__(self, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None): - super().__init__() - self.size = size - if isinstance(scale_factor, tuple): - self.scale_factor = tuple(float(factor) for factor in scale_factor) - else: - self.scale_factor = float(scale_factor) if scale_factor else None - self.mode = mode - self.align_corners = align_corners - - def forward(self, x): - if not self.size: - size = [int(t * self.scale_factor) for t in x.shape[-2:]] - else: - size = self.size - return resize(x, size, None, self.mode, self.align_corners) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/ops/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/ops/__init__.py deleted file mode 100644 index bc075cd4e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/ops/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .encoding import Encoding -from .wrappers import Upsample, resize - -__all__ = ['Upsample', 'resize', 'Encoding'] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/ops/encoding.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/ops/encoding.py deleted file mode 100644 index f397cc54e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/ops/encoding.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn -from torch.nn import functional as F - - -class Encoding(nn.Module): - """Encoding Layer: a learnable residual encoder. - - Input is of shape (batch_size, channels, height, width). - Output is of shape (batch_size, num_codes, channels). - - Args: - channels: dimension of the features or feature channels - num_codes: number of code words - """ - - def __init__(self, channels, num_codes): - super(Encoding, self).__init__() - # init codewords and smoothing factor - self.channels, self.num_codes = channels, num_codes - std = 1. / ((num_codes * channels)**0.5) - # [num_codes, channels] - self.codewords = nn.Parameter( - torch.empty(num_codes, channels, - dtype=torch.float).uniform_(-std, std), - requires_grad=True) - # [num_codes] - self.scale = nn.Parameter( - torch.empty(num_codes, dtype=torch.float).uniform_(-1, 0), - requires_grad=True) - - @staticmethod - def scaled_l2(x, codewords, scale): - num_codes, channels = codewords.size() - batch_size = x.size(0) - reshaped_scale = scale.view((1, 1, num_codes)) - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - - scaled_l2_norm = reshaped_scale * ( - expanded_x - reshaped_codewords).pow(2).sum(dim=3) - return scaled_l2_norm - - @staticmethod - def aggregate(assignment_weights, x, codewords): - num_codes, channels = codewords.size() - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - batch_size = x.size(0) - - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - encoded_feat = (assignment_weights.unsqueeze(3) * - (expanded_x - reshaped_codewords)).sum(dim=1) - return encoded_feat - - def forward(self, x): - assert x.dim() == 4 and x.size(1) == self.channels - # [batch_size, channels, height, width] - batch_size = x.size(0) - # [batch_size, height x width, channels] - x = x.view(batch_size, self.channels, -1).transpose(1, 2).contiguous() - # assignment_weights: [batch_size, channels, num_codes] - assignment_weights = F.softmax( - self.scaled_l2(x, self.codewords, self.scale), dim=2) - # aggregate - encoded_feat = self.aggregate(assignment_weights, x, self.codewords) - return encoded_feat - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(Nx{self.channels}xHxW =>Nx{self.num_codes}' \ - f'x{self.channels})' - return repr_str diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/ops/wrappers.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/ops/wrappers.py deleted file mode 100644 index ce67e4beb..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/ops/wrappers.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.nn.functional as F - - -def resize(input, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None, - warning=True): - if warning: - if size is not None and align_corners: - input_h, input_w = tuple(int(x) for x in input.shape[2:]) - output_h, output_w = tuple(int(x) for x in size) - if output_h > input_h or output_w > output_h: - if ((output_h > 1 and output_w > 1 and input_h > 1 - and input_w > 1) and (output_h - 1) % (input_h - 1) - and (output_w - 1) % (input_w - 1)): - warnings.warn( - f'When align_corners={align_corners}, ' - 'the output would more aligned if ' - f'input size {(input_h, input_w)} is `x+1` and ' - f'out size {(output_h, output_w)} is `nx+1`') - return F.interpolate(input, size, scale_factor, mode, align_corners) - - -class Upsample(nn.Module): - - def __init__(self, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None): - super(Upsample, self).__init__() - self.size = size - if isinstance(scale_factor, tuple): - self.scale_factor = tuple(float(factor) for factor in scale_factor) - else: - self.scale_factor = float(scale_factor) if scale_factor else None - self.mode = mode - self.align_corners = align_corners - - def forward(self, x): - if not self.size: - size = [int(t * self.scale_factor) for t in x.shape[-2:]] - else: - size = self.size - return resize(x, size, None, self.mode, self.align_corners) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/registry/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/registry/__init__.py deleted file mode 100644 index ee514d1a2..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/registry/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import (DATA_SAMPLERS, DATASETS, EVALUATOR, HOOKS, INFERENCERS, - LOG_PROCESSORS, LOOPS, METRICS, MODEL_WRAPPERS, MODELS, - OPTIM_WRAPPER_CONSTRUCTORS, OPTIM_WRAPPERS, OPTIMIZERS, - PARAM_SCHEDULERS, RUNNER_CONSTRUCTORS, RUNNERS, - TASK_UTILS, TRANSFORMS, VISBACKENDS, VISUALIZERS, - WEIGHT_INITIALIZERS) - -__all__ = [ - 'HOOKS', 'DATASETS', 'DATA_SAMPLERS', 'TRANSFORMS', 'MODELS', - 'WEIGHT_INITIALIZERS', 'OPTIMIZERS', 'OPTIM_WRAPPER_CONSTRUCTORS', - 'TASK_UTILS', 'PARAM_SCHEDULERS', 'METRICS', 'MODEL_WRAPPERS', - 'VISBACKENDS', 'VISUALIZERS', 'RUNNERS', 'RUNNER_CONSTRUCTORS', 'LOOPS', - 'EVALUATOR', 'LOG_PROCESSORS', 'OPTIM_WRAPPERS', 'INFERENCERS' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/registry/registry.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/registry/registry.py deleted file mode 100644 index 1e423980d..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/registry/registry.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""MMSegmentation provides 21 registry nodes to support using modules across -projects. Each node is a child of the root registry in MMEngine. - -More details can be found at -https://mmengine.readthedocs.io/en/latest/advanced_tutorials/registry.html. -""" - -from mmengine.registry import DATA_SAMPLERS as MMENGINE_DATA_SAMPLERS -from mmengine.registry import DATASETS as MMENGINE_DATASETS -from mmengine.registry import EVALUATOR as MMENGINE_EVALUATOR -from mmengine.registry import HOOKS as MMENGINE_HOOKS -from mmengine.registry import INFERENCERS as MMENGINE_INFERENCERS -from mmengine.registry import LOG_PROCESSORS as MMENGINE_LOG_PROCESSORS -from mmengine.registry import LOOPS as MMENGINE_LOOPS -from mmengine.registry import METRICS as MMENGINE_METRICS -from mmengine.registry import MODEL_WRAPPERS as MMENGINE_MODEL_WRAPPERS -from mmengine.registry import MODELS as MMENGINE_MODELS -from mmengine.registry import \ - OPTIM_WRAPPER_CONSTRUCTORS as MMENGINE_OPTIM_WRAPPER_CONSTRUCTORS -from mmengine.registry import OPTIM_WRAPPERS as MMENGINE_OPTIM_WRAPPERS -from mmengine.registry import OPTIMIZERS as MMENGINE_OPTIMIZERS -from mmengine.registry import PARAM_SCHEDULERS as MMENGINE_PARAM_SCHEDULERS -from mmengine.registry import \ - RUNNER_CONSTRUCTORS as MMENGINE_RUNNER_CONSTRUCTORS -from mmengine.registry import RUNNERS as MMENGINE_RUNNERS -from mmengine.registry import TASK_UTILS as MMENGINE_TASK_UTILS -from mmengine.registry import TRANSFORMS as MMENGINE_TRANSFORMS -from mmengine.registry import VISBACKENDS as MMENGINE_VISBACKENDS -from mmengine.registry import VISUALIZERS as MMENGINE_VISUALIZERS -from mmengine.registry import \ - WEIGHT_INITIALIZERS as MMENGINE_WEIGHT_INITIALIZERS -from mmengine.registry import Registry - -# manage all kinds of runners like `EpochBasedRunner` and `IterBasedRunner` -RUNNERS = Registry('runner', parent=MMENGINE_RUNNERS) -# manage runner constructors that define how to initialize runners -RUNNER_CONSTRUCTORS = Registry( - 'runner constructor', parent=MMENGINE_RUNNER_CONSTRUCTORS) -# manage all kinds of loops like `EpochBasedTrainLoop` -LOOPS = Registry('loop', parent=MMENGINE_LOOPS) -# manage all kinds of hooks like `CheckpointHook` -HOOKS = Registry( - 'hook', parent=MMENGINE_HOOKS, locations=['mmseg.engine.hooks']) - -# manage data-related modules -DATASETS = Registry( - 'dataset', parent=MMENGINE_DATASETS, locations=['mmseg.datasets']) -DATA_SAMPLERS = Registry('data sampler', parent=MMENGINE_DATA_SAMPLERS) -TRANSFORMS = Registry( - 'transform', - parent=MMENGINE_TRANSFORMS, - locations=['mmseg.datasets.transforms']) - -# mangage all kinds of modules inheriting `nn.Module` -MODELS = Registry('model', parent=MMENGINE_MODELS, locations=['mmseg.models']) -# mangage all kinds of model wrappers like 'MMDistributedDataParallel' -MODEL_WRAPPERS = Registry( - 'model_wrapper', - parent=MMENGINE_MODEL_WRAPPERS, - locations=['mmseg.models']) -# mangage all kinds of weight initialization modules like `Uniform` -WEIGHT_INITIALIZERS = Registry( - 'weight initializer', - parent=MMENGINE_WEIGHT_INITIALIZERS, - locations=['mmseg.models']) - -# mangage all kinds of optimizers like `SGD` and `Adam` -OPTIMIZERS = Registry( - 'optimizer', - parent=MMENGINE_OPTIMIZERS, - locations=['mmseg.engine.optimizers']) -# manage optimizer wrapper -OPTIM_WRAPPERS = Registry( - 'optim_wrapper', - parent=MMENGINE_OPTIM_WRAPPERS, - locations=['mmseg.engine.optimizers']) -# manage constructors that customize the optimization hyperparameters. -OPTIM_WRAPPER_CONSTRUCTORS = Registry( - 'optimizer wrapper constructor', - parent=MMENGINE_OPTIM_WRAPPER_CONSTRUCTORS, - locations=['mmseg.engine.optimizers']) -# mangage all kinds of parameter schedulers like `MultiStepLR` -PARAM_SCHEDULERS = Registry( - 'parameter scheduler', parent=MMENGINE_PARAM_SCHEDULERS) - -# manage all kinds of metrics -METRICS = Registry( - 'metric', parent=MMENGINE_METRICS, locations=['mmseg.evaluation']) -# manage evaluator -EVALUATOR = Registry( - 'evaluator', parent=MMENGINE_EVALUATOR, locations=['mmseg.evaluation']) - -# manage task-specific modules like ohem pixel sampler -TASK_UTILS = Registry( - 'task util', parent=MMENGINE_TASK_UTILS, locations=['mmseg.models']) - -# manage visualizer -VISUALIZERS = Registry( - 'visualizer', - parent=MMENGINE_VISUALIZERS, - locations=['mmseg.visualization']) -# manage visualizer backend -VISBACKENDS = Registry( - 'vis_backend', - parent=MMENGINE_VISBACKENDS, - locations=['mmseg.visualization']) - -# manage logprocessor -LOG_PROCESSORS = Registry( - 'log_processor', - parent=MMENGINE_LOG_PROCESSORS, - locations=['mmseg.visualization']) - -# manage inferencer -INFERENCERS = Registry('inferencer', parent=MMENGINE_INFERENCERS) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/__init__.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/__init__.py deleted file mode 100644 index ed002c7de..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collect_env import collect_env -from .logger import get_root_logger -from .misc import find_latest_checkpoint -from .set_env import setup_multi_processes - -__all__ = [ - 'get_root_logger', 'collect_env', 'find_latest_checkpoint', - 'setup_multi_processes' -] diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/collect_env.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/collect_env.py deleted file mode 100644 index 3379ecb06..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/collect_env.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash - -import mmseg - - -def collect_env(): - """Collect the information of the running environments.""" - env_info = collect_base_env() - env_info['MMSegmentation'] = f'{mmseg.__version__}+{get_git_hash()[:7]}' - - return env_info - - -if __name__ == '__main__': - for name, val in collect_env().items(): - print('{}: {}'.format(name, val)) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/logger.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/logger.py deleted file mode 100644 index 0cb3c78d6..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/logger.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -from mmcv.utils import get_logger - - -def get_root_logger(log_file=None, log_level=logging.INFO): - """Get the root logger. - - The logger will be initialized if it has not been initialized. By default a - StreamHandler will be added. If `log_file` is specified, a FileHandler will - also be added. The name of the root logger is the top-level package name, - e.g., "mmseg". - - Args: - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the root logger. - log_level (int): The root logger level. Note that only the process of - rank 0 is affected, while other processes will set the level to - "Error" and be silent most of the time. - - Returns: - logging.Logger: The root logger. - """ - - logger = get_logger(name='mmseg', log_file=log_file, log_level=log_level) - - return logger diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/misc.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/misc.py deleted file mode 100644 index bd1b6b163..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/misc.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os.path as osp -import warnings - - -def find_latest_checkpoint(path, suffix='pth'): - """This function is for finding the latest checkpoint. - - It will be used when automatically resume, modified from - https://github.com/open-mmlab/mmdetection/blob/dev-v2.20.0/mmdet/utils/misc.py - - Args: - path (str): The path to find checkpoints. - suffix (str): File extension for the checkpoint. Defaults to pth. - - Returns: - latest_path(str | None): File path of the latest checkpoint. - """ - if not osp.exists(path): - warnings.warn("The path of the checkpoints doesn't exist.") - return None - if osp.exists(osp.join(path, f'latest.{suffix}')): - return osp.join(path, f'latest.{suffix}') - - checkpoints = glob.glob(osp.join(path, f'*.{suffix}')) - if len(checkpoints) == 0: - warnings.warn('The are no checkpoints in the path') - return None - latest = -1 - latest_path = '' - for checkpoint in checkpoints: - if len(checkpoint) < len(latest_path): - continue - # `count` is iteration number, as checkpoints are saved as - # 'iter_xx.pth' or 'epoch_xx.pth' and xx is iteration number. - count = int(osp.basename(checkpoint).split('_')[-1].split('.')[0]) - if count > latest: - latest = count - latest_path = checkpoint - return latest_path diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/set_env.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/set_env.py deleted file mode 100644 index b2d3aaf14..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/utils/set_env.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import platform - -import cv2 -import torch.multiprocessing as mp - -from ..utils import get_root_logger - - -def setup_multi_processes(cfg): - """Setup multi-processing environment variables.""" - logger = get_root_logger() - - # set multi-process start method - if platform.system() != 'Windows': - mp_start_method = cfg.get('mp_start_method', None) - current_method = mp.get_start_method(allow_none=True) - if mp_start_method in ('fork', 'spawn', 'forkserver'): - logger.info( - f'Multi-processing start method `{mp_start_method}` is ' - f'different from the previous setting `{current_method}`.' - f'It will be force set to `{mp_start_method}`.') - mp.set_start_method(mp_start_method, force=True) - else: - logger.info( - f'Multi-processing start method is `{mp_start_method}`') - - # disable opencv multithreading to avoid system being overloaded - opencv_num_threads = cfg.get('opencv_num_threads', None) - if isinstance(opencv_num_threads, int): - logger.info(f'OpenCV num_threads is `{opencv_num_threads}`') - cv2.setNumThreads(opencv_num_threads) - else: - logger.info(f'OpenCV num_threads is `{cv2.getNumThreads}') - - if cfg.data.workers_per_gpu > 1: - # setup OMP threads - # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa - omp_num_threads = cfg.get('omp_num_threads', None) - if 'OMP_NUM_THREADS' not in os.environ: - if isinstance(omp_num_threads, int): - logger.info(f'OMP num threads is {omp_num_threads}') - os.environ['OMP_NUM_THREADS'] = str(omp_num_threads) - else: - logger.info(f'OMP num threads is {os.environ["OMP_NUM_THREADS"] }') - - # setup MKL threads - if 'MKL_NUM_THREADS' not in os.environ: - mkl_num_threads = cfg.get('mkl_num_threads', None) - if isinstance(mkl_num_threads, int): - logger.info(f'MKL num threads is {mkl_num_threads}') - os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads) - else: - logger.info(f'MKL num threads is {os.environ["MKL_NUM_THREADS"]}') diff --git a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/version.py b/cv/semantic_segmentation/ddrnet/pytorch/mmseg/version.py deleted file mode 100644 index e05146f0a..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/mmseg/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) Open-MMLab. All rights reserved. - -__version__ = '0.24.1' - - -def parse_version_info(version_str): - version_info = [] - for x in version_str.split('.'): - if x.isdigit(): - version_info.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - version_info.append(int(patch_version[0])) - version_info.append(f'rc{patch_version[1]}') - return tuple(version_info) - - -version_info = parse_version_info(__version__) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/requirements.txt b/cv/semantic_segmentation/ddrnet/pytorch/requirements.txt deleted file mode 100644 index a50fc71db..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ --r requirements/mmcv/runtime.txt --r requirements/apcnet.txt -opencv-python -cityscapesscripts \ No newline at end of file diff --git a/cv/semantic_segmentation/ddrnet/pytorch/requirements/apcnet.txt b/cv/semantic_segmentation/ddrnet/pytorch/requirements/apcnet.txt deleted file mode 100644 index 2712f504c..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/requirements/apcnet.txt +++ /dev/null @@ -1,4 +0,0 @@ -matplotlib -numpy -packaging -prettytable diff --git a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/build.txt b/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/build.txt deleted file mode 100644 index abf514853..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/build.txt +++ /dev/null @@ -1 +0,0 @@ -pytest-runner diff --git a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/docs.txt b/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/docs.txt deleted file mode 100644 index 6a5319af3..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/docs.txt +++ /dev/null @@ -1,8 +0,0 @@ -docutils==0.16.0 -myst-parser -opencv-python --e git+https://github.com/open-mmlab/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme -sphinx==4.0.2 -sphinx-copybutton -sphinx_markdown_tables -torch diff --git a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/optional.txt b/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/optional.txt deleted file mode 100644 index 63730036f..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/optional.txt +++ /dev/null @@ -1 +0,0 @@ -ninja diff --git a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/requirements.txt b/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/requirements.txt deleted file mode 100644 index 6f00e6303..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ --r requirements/mmcv/build.txt --r requirements/mmcv/optional.txt --r requirements/mmcv/runtime.txt --r requirements/mmcv/test.txt diff --git a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/runtime.txt b/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/runtime.txt deleted file mode 100644 index 66e90d674..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/runtime.txt +++ /dev/null @@ -1,7 +0,0 @@ -addict -numpy -packaging -Pillow -pyyaml -regex;sys_platform=='win32' -yapf diff --git a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/test.txt b/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/test.txt deleted file mode 100644 index 6d9d17b98..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mmcv/test.txt +++ /dev/null @@ -1,9 +0,0 @@ -coverage -lmdb -onnx==1.7.0; python_version < '3.10' -onnxoptimizer; python_version < '3.10' -onnxruntime>=1.8.0; python_version < '3.10' -pytest -PyTurboJPEG -scipy -tifffile diff --git a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mminstall.txt b/cv/semantic_segmentation/ddrnet/pytorch/requirements/mminstall.txt deleted file mode 100644 index bd43faf87..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/requirements/mminstall.txt +++ /dev/null @@ -1,2 +0,0 @@ -mmcls>=0.20.1 -mmcv-full>=1.4.4,<=1.6.0 diff --git a/cv/semantic_segmentation/ddrnet/pytorch/requirements/optional.txt b/cv/semantic_segmentation/ddrnet/pytorch/requirements/optional.txt deleted file mode 100644 index 47fa59331..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/requirements/optional.txt +++ /dev/null @@ -1 +0,0 @@ -cityscapesscripts diff --git a/cv/semantic_segmentation/ddrnet/pytorch/requirements/runtime.txt b/cv/semantic_segmentation/ddrnet/pytorch/requirements/runtime.txt deleted file mode 100644 index 520408fe8..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/requirements/runtime.txt +++ /dev/null @@ -1,5 +0,0 @@ -matplotlib -mmcls>=0.20.1 -numpy -packaging -prettytable diff --git a/cv/semantic_segmentation/ddrnet/pytorch/setup.py b/cv/semantic_segmentation/ddrnet/pytorch/setup.py deleted file mode 100644 index d409997db..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/setup.py +++ /dev/null @@ -1,258 +0,0 @@ -import glob -import os -import platform -import re -import warnings -from pkg_resources import DistributionNotFound, get_distribution -from setuptools import find_packages, setup - -EXT_TYPE = '' -try: - import torch - if torch.__version__ == 'parrots': - from parrots.utils.build_extension import BuildExtension - EXT_TYPE = 'parrots' - elif (hasattr(torch, 'is_mlu_available') and torch.is_mlu_available()) or \ - os.getenv('FORCE_MLU', '0') == '1': - from torch_mlu.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - else: - from torch.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - cmd_class = {'build_ext': BuildExtension} -except ModuleNotFoundError: - cmd_class = {} - print('Skip building ext ops due to the absence of torch.') - - -def choose_requirement(primary, secondary): - """If some version of primary requirement installed, return primary, else - return secondary.""" - try: - name = re.split(r'[!<>=]', primary)[0] - get_distribution(name) - except DistributionNotFound: - return secondary - - return str(primary) - - -def get_version(): - version_file = 'mmcv/version.py' - with open(version_file, 'r', encoding='utf-8') as f: - exec(compile(f.read(), version_file, 'exec')) - return locals()['__version__'] - - -def parse_requirements(fname='requirements/mmcv/runtime.txt', with_version=True): - """Parse the package dependencies listed in a requirements file but strips - specific versioning information. - - Args: - fname (str): path to requirements file - with_version (bool, default=False): if True include version specs - - Returns: - List[str]: list of requirements items - - CommandLine: - python -c "import setup; print(setup.parse_requirements())" - """ - import sys - from os.path import exists - require_fpath = fname - - def parse_line(line): - """Parse information from a line in a requirements text file.""" - if line.startswith('-r '): - # Allow specifying requirements in other files - target = line.split(' ')[1] - for info in parse_require_file(target): - yield info - else: - info = {'line': line} - if line.startswith('-e '): - info['package'] = line.split('#egg=')[1] - else: - # Remove versioning from the package - pat = '(' + '|'.join(['>=', '==', '>']) + ')' - parts = re.split(pat, line, maxsplit=1) - parts = [p.strip() for p in parts] - - info['package'] = parts[0] - if len(parts) > 1: - op, rest = parts[1:] - if ';' in rest: - # Handle platform specific dependencies - # http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies - version, platform_deps = map(str.strip, - rest.split(';')) - info['platform_deps'] = platform_deps - else: - version = rest # NOQA - info['version'] = (op, version) - yield info - - def parse_require_file(fpath): - with open(fpath, 'r') as f: - for line in f.readlines(): - line = line.strip() - if line and not line.startswith('#'): - for info in parse_line(line): - yield info - - def gen_packages_items(): - if exists(require_fpath): - for info in parse_require_file(require_fpath): - parts = [info['package']] - if with_version and 'version' in info: - parts.extend(info['version']) - if not sys.version.startswith('3.4'): - # apparently package_deps are broken in 3.4 - platform_deps = info.get('platform_deps') - if platform_deps is not None: - parts.append(';' + platform_deps) - item = ''.join(parts) - yield item - - packages = list(gen_packages_items()) - return packages - - -install_requires = parse_requirements() - -try: - # OpenCV installed via conda. - import cv2 # NOQA: F401 - major, minor, *rest = cv2.__version__.split('.') - if int(major) < 3: - raise RuntimeError( - f'OpenCV >=3 is required but {cv2.__version__} is installed') -except ImportError: - # If first not installed install second package - CHOOSE_INSTALL_REQUIRES = [('opencv-python-headless>=3', - 'opencv-python>=3')] - for main, secondary in CHOOSE_INSTALL_REQUIRES: - install_requires.append(choose_requirement(main, secondary)) - - -def get_extensions(): - extensions = [] - - if EXT_TYPE == 'pytorch': - ext_name = 'mmcv._ext' - from torch.utils.cpp_extension import CppExtension, CUDAExtension - - # prevent ninja from using too many resources - try: - import psutil - num_cpu = len(psutil.Process().cpu_affinity()) - cpu_use = max(4, num_cpu - 1) - except (ModuleNotFoundError, AttributeError): - cpu_use = 4 - - os.environ.setdefault('MAX_JOBS', str(cpu_use)) - define_macros = [] - - # Before PyTorch1.8.0, when compiling CUDA code, `cxx` is a - # required key passed to PyTorch. Even if there is no flag passed - # to cxx, users also need to pass an empty list to PyTorch. - # Since PyTorch1.8.0, it has a default value so users do not need - # to pass an empty list anymore. - # More details at https://github.com/pytorch/pytorch/pull/45956 - extra_compile_args = {'cxx': []} - - # Since the PR (https://github.com/open-mmlab/mmcv/pull/1463) uses - # c++14 features, the argument ['std=c++14'] must be added here. - # However, in the windows environment, some standard libraries - # will depend on c++17 or higher. In fact, for the windows - # environment, the compiler will choose the appropriate compiler - # to compile those cpp files, so there is no need to add the - # argument - if platform.system() != 'Windows': - extra_compile_args['cxx'] = ['-std=c++14'] - - include_dirs = [] - - is_rocm_pytorch = False - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm_pytorch = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - - if is_rocm_pytorch or torch.cuda.is_available() or os.getenv( - 'FORCE_CUDA', '0') == '1': - if is_rocm_pytorch: - define_macros += [('HIP_DIFF', None)] - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cpp') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - else: - print(f'Compiling {ext_name} only with CPU') - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') - extension = CppExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - - # Since the PR (https://github.com/open-mmlab/mmcv/pull/1463) uses - # c++14 features, the argument ['std=c++14'] must be added here. - # However, in the windows environment, some standard libraries - # will depend on c++17 or higher. In fact, for the windows - # environment, the compiler will choose the appropriate compiler - # to compile those cpp files, so there is no need to add the - # argument - if 'nvcc' in extra_compile_args and platform.system() != 'Windows': - extra_compile_args['nvcc'] += ['-std=c++14'] - - ext_ops = extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args) - extensions.append(ext_ops) - - return extensions - - -setup( - name='mmcv' if os.getenv('MMCV_WITH_OPS', '0') == '0' else 'mmcv-full', - version=get_version(), - description='OpenMMLab Computer Vision Foundation', - keywords='computer vision', - packages=find_packages(), - include_package_data=True, - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Topic :: Utilities', - ], - url='https://github.com/open-mmlab/mmcv', - author='MMCV Contributors', - author_email='openmmlab@gmail.com', - install_requires=install_requires, - extras_require={ - 'all': parse_requirements('requirements/mmcv/requirements.txt'), - 'tests': parse_requirements('requirements/mmcv/test.txt'), - 'build': parse_requirements('requirements/mmcv/build.txt'), - 'optional': parse_requirements('requirements/mmcv/optional.txt'), - }, - ext_modules=get_extensions(), - cmdclass=cmd_class, - zip_safe=False) diff --git a/cv/semantic_segmentation/ddrnet/pytorch/tools/analyze_logs.py b/cv/semantic_segmentation/ddrnet/pytorch/tools/analyze_logs.py deleted file mode 100644 index e2127d4d6..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/tools/analyze_logs.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/open- -mmlab/mmdetection/blob/master/tools/analysis_tools/analyze_logs.py.""" -import argparse -import json -from collections import defaultdict - -import matplotlib.pyplot as plt -import seaborn as sns - - -def plot_curve(log_dicts, args): - if args.backend is not None: - plt.switch_backend(args.backend) - sns.set_style(args.style) - # if legend is None, use {filename}_{key} as legend - legend = args.legend - if legend is None: - legend = [] - for json_log in args.json_logs: - for metric in args.keys: - legend.append(f'{json_log}_{metric}') - assert len(legend) == (len(args.json_logs) * len(args.keys)) - metrics = args.keys - - num_metrics = len(metrics) - for i, log_dict in enumerate(log_dicts): - epochs = list(log_dict.keys()) - for j, metric in enumerate(metrics): - print(f'plot curve of {args.json_logs[i]}, metric is {metric}') - plot_epochs = [] - plot_iters = [] - plot_values = [] - # In some log files exist lines of validation, - # `mode` list is used to only collect iter number - # of training line. - for epoch in epochs: - epoch_logs = log_dict[epoch] - if metric not in epoch_logs.keys(): - continue - if metric in ['mIoU', 'mAcc', 'aAcc']: - plot_epochs.append(epoch) - plot_values.append(epoch_logs[metric][0]) - else: - for idx in range(len(epoch_logs[metric])): - if epoch_logs['mode'][idx] == 'train': - plot_iters.append(epoch_logs['iter'][idx]) - plot_values.append(epoch_logs[metric][idx]) - ax = plt.gca() - label = legend[i * num_metrics + j] - if metric in ['mIoU', 'mAcc', 'aAcc']: - ax.set_xticks(plot_epochs) - plt.xlabel('epoch') - plt.plot(plot_epochs, plot_values, label=label, marker='o') - else: - plt.xlabel('iter') - plt.plot(plot_iters, plot_values, label=label, linewidth=0.5) - plt.legend() - if args.title is not None: - plt.title(args.title) - if args.out is None: - plt.show() - else: - print(f'save curve to: {args.out}') - plt.savefig(args.out) - plt.cla() - - -def parse_args(): - parser = argparse.ArgumentParser(description='Analyze Json Log') - parser.add_argument( - 'json_logs', - type=str, - nargs='+', - help='path of train log in json format') - parser.add_argument( - '--keys', - type=str, - nargs='+', - default=['mIoU'], - help='the metric that you want to plot') - parser.add_argument('--title', type=str, help='title of figure') - parser.add_argument( - '--legend', - type=str, - nargs='+', - default=None, - help='legend of each plot') - parser.add_argument( - '--backend', type=str, default=None, help='backend of plt') - parser.add_argument( - '--style', type=str, default='dark', help='style of plt') - parser.add_argument('--out', type=str, default=None) - args = parser.parse_args() - return args - - -def load_json_logs(json_logs): - # load and convert json_logs to log_dict, key is epoch, value is a sub dict - # keys of sub dict is different metrics - # value of sub dict is a list of corresponding values of all iterations - log_dicts = [dict() for _ in json_logs] - for json_log, log_dict in zip(json_logs, log_dicts): - with open(json_log, 'r') as log_file: - for line in log_file: - log = json.loads(line.strip()) - # skip lines without `epoch` field - if 'epoch' not in log: - continue - epoch = log.pop('epoch') - if epoch not in log_dict: - log_dict[epoch] = defaultdict(list) - for k, v in log.items(): - log_dict[epoch][k].append(v) - return log_dicts - - -def main(): - args = parse_args() - json_logs = args.json_logs - for json_log in json_logs: - assert json_log.endswith('.json') - log_dicts = load_json_logs(json_logs) - plot_curve(log_dicts, args) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/tools/benchmark.py b/cv/semantic_segmentation/ddrnet/pytorch/tools/benchmark.py deleted file mode 100644 index f6d688848..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/tools/benchmark.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import time - -import mmcv -import numpy as np -import torch -from mmcv import Config -from mmcv.parallel import MMDataParallel -from mmcv.runner import load_checkpoint, wrap_fp16_model - -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='MMSeg benchmark a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--log-interval', type=int, default=50, help='interval of logging') - parser.add_argument( - '--work-dir', - help=('if specified, the results will be dumped ' - 'into the directory as json')) - parser.add_argument('--repeat-times', type=int, default=1) - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.work_dir is not None: - mmcv.mkdir_or_exist(osp.abspath(args.work_dir)) - json_file = osp.join(args.work_dir, f'fps_{timestamp}.json') - else: - # use config filename as default work_dir if cfg.work_dir is None - work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - mmcv.mkdir_or_exist(osp.abspath(work_dir)) - json_file = osp.join(work_dir, f'fps_{timestamp}.json') - - repeat_times = args.repeat_times - # set cudnn_benchmark - torch.backends.cudnn.benchmark = False - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - benchmark_dict = dict(config=args.config, unit='img / s') - overall_fps_list = [] - for time_index in range(repeat_times): - print(f'Run {time_index + 1}:') - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - data_loader = build_dataloader( - dataset, - samples_per_gpu=1, - workers_per_gpu=cfg.data.workers_per_gpu, - dist=False, - shuffle=False) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - if 'checkpoint' in args and osp.exists(args.checkpoint): - load_checkpoint(model, args.checkpoint, map_location='cpu') - - model = MMDataParallel(model, device_ids=[0]) - - model.eval() - - # the first several iterations may be very slow so skip them - num_warmup = 5 - pure_inf_time = 0 - total_iters = 200 - - # benchmark with 200 image and take the average - for i, data in enumerate(data_loader): - - torch.cuda.synchronize() - start_time = time.perf_counter() - - with torch.no_grad(): - model(return_loss=False, rescale=True, **data) - - torch.cuda.synchronize() - elapsed = time.perf_counter() - start_time - - if i >= num_warmup: - pure_inf_time += elapsed - if (i + 1) % args.log_interval == 0: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Done image [{i + 1:<3}/ {total_iters}], ' - f'fps: {fps:.2f} img / s') - - if (i + 1) == total_iters: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Overall fps: {fps:.2f} img / s\n') - benchmark_dict[f'overall_fps_{time_index + 1}'] = round(fps, 2) - overall_fps_list.append(fps) - break - benchmark_dict['average_fps'] = round(np.mean(overall_fps_list), 2) - benchmark_dict['fps_variance'] = round(np.var(overall_fps_list), 4) - print(f'Average fps of {repeat_times} evaluations: ' - f'{benchmark_dict["average_fps"]}') - print(f'The variance of {repeat_times} evaluations: ' - f'{benchmark_dict["fps_variance"]}') - mmcv.dump(benchmark_dict, json_file, indent=4) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/tools/confusion_matrix.py b/cv/semantic_segmentation/ddrnet/pytorch/tools/confusion_matrix.py deleted file mode 100644 index 2c5b64cf4..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/tools/confusion_matrix.py +++ /dev/null @@ -1,184 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os - -import matplotlib.pyplot as plt -import mmcv -import numpy as np -from matplotlib.ticker import MultipleLocator -from mmcv import Config, DictAction - -from mmseg.datasets import build_dataset - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Generate confusion matrix from segmentation results') - parser.add_argument('config', help='test config file path') - parser.add_argument( - 'prediction_path', help='prediction path where test .pkl result') - parser.add_argument( - 'save_dir', help='directory where confusion matrix will be saved') - parser.add_argument( - '--show', action='store_true', help='show confusion matrix') - parser.add_argument( - '--color-theme', - default='winter', - help='theme of the matrix color map') - parser.add_argument( - '--title', - default='Normalized Confusion Matrix', - help='title of the matrix color map') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - args = parser.parse_args() - return args - - -def calculate_confusion_matrix(dataset, results): - """Calculate the confusion matrix. - - Args: - dataset (Dataset): Test or val dataset. - results (list[ndarray]): A list of segmentation results in each image. - """ - n = len(dataset.CLASSES) - confusion_matrix = np.zeros(shape=[n, n]) - assert len(dataset) == len(results) - prog_bar = mmcv.ProgressBar(len(results)) - for idx, per_img_res in enumerate(results): - res_segm = per_img_res - gt_segm = dataset.get_gt_seg_map_by_idx(idx) - inds = n * gt_segm + res_segm - inds = inds.flatten() - mat = np.bincount(inds, minlength=n**2).reshape(n, n) - confusion_matrix += mat - prog_bar.update() - return confusion_matrix - - -def plot_confusion_matrix(confusion_matrix, - labels, - save_dir=None, - show=True, - title='Normalized Confusion Matrix', - color_theme='winter'): - """Draw confusion matrix with matplotlib. - - Args: - confusion_matrix (ndarray): The confusion matrix. - labels (list[str]): List of class names. - save_dir (str|optional): If set, save the confusion matrix plot to the - given path. Default: None. - show (bool): Whether to show the plot. Default: True. - title (str): Title of the plot. Default: `Normalized Confusion Matrix`. - color_theme (str): Theme of the matrix color map. Default: `winter`. - """ - # normalize the confusion matrix - per_label_sums = confusion_matrix.sum(axis=1)[:, np.newaxis] - confusion_matrix = \ - confusion_matrix.astype(np.float32) / per_label_sums * 100 - - num_classes = len(labels) - fig, ax = plt.subplots( - figsize=(2 * num_classes, 2 * num_classes * 0.8), dpi=180) - cmap = plt.get_cmap(color_theme) - im = ax.imshow(confusion_matrix, cmap=cmap) - plt.colorbar(mappable=im, ax=ax) - - title_font = {'weight': 'bold', 'size': 12} - ax.set_title(title, fontdict=title_font) - label_font = {'size': 10} - plt.ylabel('Ground Truth Label', fontdict=label_font) - plt.xlabel('Prediction Label', fontdict=label_font) - - # draw locator - xmajor_locator = MultipleLocator(1) - xminor_locator = MultipleLocator(0.5) - ax.xaxis.set_major_locator(xmajor_locator) - ax.xaxis.set_minor_locator(xminor_locator) - ymajor_locator = MultipleLocator(1) - yminor_locator = MultipleLocator(0.5) - ax.yaxis.set_major_locator(ymajor_locator) - ax.yaxis.set_minor_locator(yminor_locator) - - # draw grid - ax.grid(True, which='minor', linestyle='-') - - # draw label - ax.set_xticks(np.arange(num_classes)) - ax.set_yticks(np.arange(num_classes)) - ax.set_xticklabels(labels) - ax.set_yticklabels(labels) - - ax.tick_params( - axis='x', bottom=False, top=True, labelbottom=False, labeltop=True) - plt.setp( - ax.get_xticklabels(), rotation=45, ha='left', rotation_mode='anchor') - - # draw confusion matrix value - for i in range(num_classes): - for j in range(num_classes): - ax.text( - j, - i, - '{}%'.format( - round(confusion_matrix[i, j], 2 - ) if not np.isnan(confusion_matrix[i, j]) else -1), - ha='center', - va='center', - color='w', - size=7) - - ax.set_ylim(len(confusion_matrix) - 0.5, -0.5) # matplotlib>3.1.1 - - fig.tight_layout() - if save_dir is not None: - plt.savefig( - os.path.join(save_dir, 'confusion_matrix.png'), format='png') - if show: - plt.show() - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - results = mmcv.load(args.prediction_path) - - assert isinstance(results, list) - if isinstance(results[0], np.ndarray): - pass - else: - raise TypeError('invalid type of prediction results') - - if isinstance(cfg.data.test, dict): - cfg.data.test.test_mode = True - elif isinstance(cfg.data.test, list): - for ds_cfg in cfg.data.test: - ds_cfg.test_mode = True - - dataset = build_dataset(cfg.data.test) - confusion_matrix = calculate_confusion_matrix(dataset, results) - plot_confusion_matrix( - confusion_matrix, - dataset.CLASSES, - save_dir=args.save_dir, - show=args.show, - title=args.title, - color_theme=args.color_theme) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/cityscapes.py b/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/cityscapes.py deleted file mode 100644 index 17b616847..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/cityscapes.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp - -import mmcv -from cityscapesscripts.preparation.json2labelImg import json2labelImg - - -def convert_json_to_label(json_file): - label_file = json_file.replace('_polygons.json', '_labelTrainIds.png') - json2labelImg(json_file, label_file, 'trainIds') - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert Cityscapes annotations to TrainIds') - parser.add_argument('cityscapes_path', help='cityscapes data path') - parser.add_argument('--gt-dir', default='gtFine', type=str) - parser.add_argument('-o', '--out-dir', help='output path') - parser.add_argument( - '--nproc', default=1, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - cityscapes_path = args.cityscapes_path - out_dir = args.out_dir if args.out_dir else cityscapes_path - mmcv.mkdir_or_exist(out_dir) - - gt_dir = osp.join(cityscapes_path, args.gt_dir) - - poly_files = [] - for poly in mmcv.scandir(gt_dir, '_polygons.json', recursive=True): - poly_file = osp.join(gt_dir, poly) - poly_files.append(poly_file) - if args.nproc > 1: - mmcv.track_parallel_progress(convert_json_to_label, poly_files, - args.nproc) - else: - mmcv.track_progress(convert_json_to_label, poly_files) - - split_names = ['train', 'val', 'test'] - - for split in split_names: - filenames = [] - for poly in mmcv.scandir( - osp.join(gt_dir, split), '_polygons.json', recursive=True): - filenames.append(poly.replace('_gtFine_polygons.json', '')) - with open(osp.join(out_dir, f'{split}.txt'), 'w') as f: - f.writelines(f + '\n' for f in filenames) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/coco_stuff10k.py b/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/coco_stuff10k.py deleted file mode 100644 index 374f81970..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/coco_stuff10k.py +++ /dev/null @@ -1,307 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import shutil -from functools import partial - -import mmcv -import numpy as np -from PIL import Image -from scipy.io import loadmat - -COCO_LEN = 10000 - -clsID_to_trID = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 13: 12, - 14: 13, - 15: 14, - 16: 15, - 17: 16, - 18: 17, - 19: 18, - 20: 19, - 21: 20, - 22: 21, - 23: 22, - 24: 23, - 25: 24, - 27: 25, - 28: 26, - 31: 27, - 32: 28, - 33: 29, - 34: 30, - 35: 31, - 36: 32, - 37: 33, - 38: 34, - 39: 35, - 40: 36, - 41: 37, - 42: 38, - 43: 39, - 44: 40, - 46: 41, - 47: 42, - 48: 43, - 49: 44, - 50: 45, - 51: 46, - 52: 47, - 53: 48, - 54: 49, - 55: 50, - 56: 51, - 57: 52, - 58: 53, - 59: 54, - 60: 55, - 61: 56, - 62: 57, - 63: 58, - 64: 59, - 65: 60, - 67: 61, - 70: 62, - 72: 63, - 73: 64, - 74: 65, - 75: 66, - 76: 67, - 77: 68, - 78: 69, - 79: 70, - 80: 71, - 81: 72, - 82: 73, - 84: 74, - 85: 75, - 86: 76, - 87: 77, - 88: 78, - 89: 79, - 90: 80, - 92: 81, - 93: 82, - 94: 83, - 95: 84, - 96: 85, - 97: 86, - 98: 87, - 99: 88, - 100: 89, - 101: 90, - 102: 91, - 103: 92, - 104: 93, - 105: 94, - 106: 95, - 107: 96, - 108: 97, - 109: 98, - 110: 99, - 111: 100, - 112: 101, - 113: 102, - 114: 103, - 115: 104, - 116: 105, - 117: 106, - 118: 107, - 119: 108, - 120: 109, - 121: 110, - 122: 111, - 123: 112, - 124: 113, - 125: 114, - 126: 115, - 127: 116, - 128: 117, - 129: 118, - 130: 119, - 131: 120, - 132: 121, - 133: 122, - 134: 123, - 135: 124, - 136: 125, - 137: 126, - 138: 127, - 139: 128, - 140: 129, - 141: 130, - 142: 131, - 143: 132, - 144: 133, - 145: 134, - 146: 135, - 147: 136, - 148: 137, - 149: 138, - 150: 139, - 151: 140, - 152: 141, - 153: 142, - 154: 143, - 155: 144, - 156: 145, - 157: 146, - 158: 147, - 159: 148, - 160: 149, - 161: 150, - 162: 151, - 163: 152, - 164: 153, - 165: 154, - 166: 155, - 167: 156, - 168: 157, - 169: 158, - 170: 159, - 171: 160, - 172: 161, - 173: 162, - 174: 163, - 175: 164, - 176: 165, - 177: 166, - 178: 167, - 179: 168, - 180: 169, - 181: 170, - 182: 171 -} - - -def convert_to_trainID(tuple_path, in_img_dir, in_ann_dir, out_img_dir, - out_mask_dir, is_train): - imgpath, maskpath = tuple_path - shutil.copyfile( - osp.join(in_img_dir, imgpath), - osp.join(out_img_dir, 'train2014', imgpath) if is_train else osp.join( - out_img_dir, 'test2014', imgpath)) - annotate = loadmat(osp.join(in_ann_dir, maskpath)) - mask = annotate['S'].astype(np.uint8) - mask_copy = mask.copy() - for clsID, trID in clsID_to_trID.items(): - mask_copy[mask == clsID] = trID - seg_filename = osp.join(out_mask_dir, 'train2014', - maskpath.split('.')[0] + - '_labelTrainIds.png') if is_train else osp.join( - out_mask_dir, 'test2014', - maskpath.split('.')[0] + '_labelTrainIds.png') - Image.fromarray(mask_copy).save(seg_filename, 'PNG') - - -def generate_coco_list(folder): - train_list = osp.join(folder, 'imageLists', 'train.txt') - test_list = osp.join(folder, 'imageLists', 'test.txt') - train_paths = [] - test_paths = [] - - with open(train_list) as f: - for filename in f: - basename = filename.strip() - imgpath = basename + '.jpg' - maskpath = basename + '.mat' - train_paths.append((imgpath, maskpath)) - - with open(test_list) as f: - for filename in f: - basename = filename.strip() - imgpath = basename + '.jpg' - maskpath = basename + '.mat' - test_paths.append((imgpath, maskpath)) - - return train_paths, test_paths - - -def parse_args(): - parser = argparse.ArgumentParser( - description=\ - 'Convert COCO Stuff 10k annotations to mmsegmentation format') # noqa - parser.add_argument('coco_path', help='coco stuff path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=16, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - coco_path = args.coco_path - nproc = args.nproc - - out_dir = args.out_dir or coco_path - out_img_dir = osp.join(out_dir, 'images') - out_mask_dir = osp.join(out_dir, 'annotations') - - mmcv.mkdir_or_exist(osp.join(out_img_dir, 'train2014')) - mmcv.mkdir_or_exist(osp.join(out_img_dir, 'test2014')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'train2014')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'test2014')) - - train_list, test_list = generate_coco_list(coco_path) - assert (len(train_list) + - len(test_list)) == COCO_LEN, 'Wrong length of list {} & {}'.format( - len(train_list), len(test_list)) - - if args.nproc > 1: - mmcv.track_parallel_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=True), - train_list, - nproc=nproc) - mmcv.track_parallel_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=False), - test_list, - nproc=nproc) - else: - mmcv.track_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=True), train_list) - mmcv.track_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=False), test_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/coco_stuff164k.py b/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/coco_stuff164k.py deleted file mode 100644 index 6d8e2f2a3..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/coco_stuff164k.py +++ /dev/null @@ -1,264 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import shutil -from functools import partial -from glob import glob - -import mmcv -import numpy as np -from PIL import Image - -COCO_LEN = 123287 - -clsID_to_trID = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 12: 11, - 13: 12, - 14: 13, - 15: 14, - 16: 15, - 17: 16, - 18: 17, - 19: 18, - 20: 19, - 21: 20, - 22: 21, - 23: 22, - 24: 23, - 26: 24, - 27: 25, - 30: 26, - 31: 27, - 32: 28, - 33: 29, - 34: 30, - 35: 31, - 36: 32, - 37: 33, - 38: 34, - 39: 35, - 40: 36, - 41: 37, - 42: 38, - 43: 39, - 45: 40, - 46: 41, - 47: 42, - 48: 43, - 49: 44, - 50: 45, - 51: 46, - 52: 47, - 53: 48, - 54: 49, - 55: 50, - 56: 51, - 57: 52, - 58: 53, - 59: 54, - 60: 55, - 61: 56, - 62: 57, - 63: 58, - 64: 59, - 66: 60, - 69: 61, - 71: 62, - 72: 63, - 73: 64, - 74: 65, - 75: 66, - 76: 67, - 77: 68, - 78: 69, - 79: 70, - 80: 71, - 81: 72, - 83: 73, - 84: 74, - 85: 75, - 86: 76, - 87: 77, - 88: 78, - 89: 79, - 91: 80, - 92: 81, - 93: 82, - 94: 83, - 95: 84, - 96: 85, - 97: 86, - 98: 87, - 99: 88, - 100: 89, - 101: 90, - 102: 91, - 103: 92, - 104: 93, - 105: 94, - 106: 95, - 107: 96, - 108: 97, - 109: 98, - 110: 99, - 111: 100, - 112: 101, - 113: 102, - 114: 103, - 115: 104, - 116: 105, - 117: 106, - 118: 107, - 119: 108, - 120: 109, - 121: 110, - 122: 111, - 123: 112, - 124: 113, - 125: 114, - 126: 115, - 127: 116, - 128: 117, - 129: 118, - 130: 119, - 131: 120, - 132: 121, - 133: 122, - 134: 123, - 135: 124, - 136: 125, - 137: 126, - 138: 127, - 139: 128, - 140: 129, - 141: 130, - 142: 131, - 143: 132, - 144: 133, - 145: 134, - 146: 135, - 147: 136, - 148: 137, - 149: 138, - 150: 139, - 151: 140, - 152: 141, - 153: 142, - 154: 143, - 155: 144, - 156: 145, - 157: 146, - 158: 147, - 159: 148, - 160: 149, - 161: 150, - 162: 151, - 163: 152, - 164: 153, - 165: 154, - 166: 155, - 167: 156, - 168: 157, - 169: 158, - 170: 159, - 171: 160, - 172: 161, - 173: 162, - 174: 163, - 175: 164, - 176: 165, - 177: 166, - 178: 167, - 179: 168, - 180: 169, - 181: 170, - 255: 255 -} - - -def convert_to_trainID(maskpath, out_mask_dir, is_train): - mask = np.array(Image.open(maskpath)) - mask_copy = mask.copy() - for clsID, trID in clsID_to_trID.items(): - mask_copy[mask == clsID] = trID - seg_filename = osp.join( - out_mask_dir, 'train2017', - osp.basename(maskpath).split('.')[0] + - '_labelTrainIds.png') if is_train else osp.join( - out_mask_dir, 'val2017', - osp.basename(maskpath).split('.')[0] + '_labelTrainIds.png') - Image.fromarray(mask_copy).save(seg_filename, 'PNG') - - -def parse_args(): - parser = argparse.ArgumentParser( - description=\ - 'Convert COCO Stuff 164k annotations to mmsegmentation format') # noqa - parser.add_argument('coco_path', help='coco stuff path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=16, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - coco_path = args.coco_path - nproc = args.nproc - - out_dir = args.out_dir or coco_path - out_img_dir = osp.join(out_dir, 'images') - out_mask_dir = osp.join(out_dir, 'annotations') - - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'train2017')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'val2017')) - - if out_dir != coco_path: - shutil.copytree(osp.join(coco_path, 'images'), out_img_dir) - - train_list = glob(osp.join(coco_path, 'annotations', 'train2017', '*.png')) - train_list = [file for file in train_list if '_labelTrainIds' not in file] - test_list = glob(osp.join(coco_path, 'annotations', 'val2017', '*.png')) - test_list = [file for file in test_list if '_labelTrainIds' not in file] - assert (len(train_list) + - len(test_list)) == COCO_LEN, 'Wrong length of list {} & {}'.format( - len(train_list), len(test_list)) - - if args.nproc > 1: - mmcv.track_parallel_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=True), - train_list, - nproc=nproc) - mmcv.track_parallel_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=False), - test_list, - nproc=nproc) - else: - mmcv.track_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=True), - train_list) - mmcv.track_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=False), - test_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/pascal_context.py b/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/pascal_context.py deleted file mode 100644 index 03b79d518..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/pascal_context.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -from functools import partial - -import mmcv -import numpy as np -from detail import Detail -from PIL import Image - -_mapping = np.sort( - np.array([ - 0, 2, 259, 260, 415, 324, 9, 258, 144, 18, 19, 22, 23, 397, 25, 284, - 158, 159, 416, 33, 162, 420, 454, 295, 296, 427, 44, 45, 46, 308, 59, - 440, 445, 31, 232, 65, 354, 424, 68, 326, 72, 458, 34, 207, 80, 355, - 85, 347, 220, 349, 360, 98, 187, 104, 105, 366, 189, 368, 113, 115 - ])) -_key = np.array(range(len(_mapping))).astype('uint8') - - -def generate_labels(img_id, detail, out_dir): - - def _class_to_index(mask, _mapping, _key): - # assert the values - values = np.unique(mask) - for i in range(len(values)): - assert (values[i] in _mapping) - index = np.digitize(mask.ravel(), _mapping, right=True) - return _key[index].reshape(mask.shape) - - mask = Image.fromarray( - _class_to_index(detail.getMask(img_id), _mapping=_mapping, _key=_key)) - filename = img_id['file_name'] - mask.save(osp.join(out_dir, filename.replace('jpg', 'png'))) - return osp.splitext(osp.basename(filename))[0] - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert PASCAL VOC annotations to mmsegmentation format') - parser.add_argument('devkit_path', help='pascal voc devkit path') - parser.add_argument('json_path', help='annoation json filepath') - parser.add_argument('-o', '--out_dir', help='output path') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - devkit_path = args.devkit_path - if args.out_dir is None: - out_dir = osp.join(devkit_path, 'VOC2010', 'SegmentationClassContext') - else: - out_dir = args.out_dir - json_path = args.json_path - mmcv.mkdir_or_exist(out_dir) - img_dir = osp.join(devkit_path, 'VOC2010', 'JPEGImages') - - train_detail = Detail(json_path, img_dir, 'train') - train_ids = train_detail.getImgs() - - val_detail = Detail(json_path, img_dir, 'val') - val_ids = val_detail.getImgs() - - mmcv.mkdir_or_exist( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext')) - - train_list = mmcv.track_progress( - partial(generate_labels, detail=train_detail, out_dir=out_dir), - train_ids) - with open( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext', - 'train.txt'), 'w') as f: - f.writelines(line + '\n' for line in sorted(train_list)) - - val_list = mmcv.track_progress( - partial(generate_labels, detail=val_detail, out_dir=out_dir), val_ids) - with open( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext', - 'val.txt'), 'w') as f: - f.writelines(line + '\n' for line in sorted(val_list)) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/voc_aug.py b/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/voc_aug.py deleted file mode 100644 index 1d42c2704..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/tools/convert_datasets/voc_aug.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -from functools import partial - -import mmcv -import numpy as np -from PIL import Image -from scipy.io import loadmat - -AUG_LEN = 10582 - - -def convert_mat(mat_file, in_dir, out_dir): - data = loadmat(osp.join(in_dir, mat_file)) - mask = data['GTcls'][0]['Segmentation'][0].astype(np.uint8) - seg_filename = osp.join(out_dir, mat_file.replace('.mat', '.png')) - Image.fromarray(mask).save(seg_filename, 'PNG') - - -def generate_aug_list(merged_list, excluded_list): - return list(set(merged_list) - set(excluded_list)) - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert PASCAL VOC annotations to mmsegmentation format') - parser.add_argument('devkit_path', help='pascal voc devkit path') - parser.add_argument('aug_path', help='pascal voc aug path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=1, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - devkit_path = args.devkit_path - aug_path = args.aug_path - nproc = args.nproc - if args.out_dir is None: - out_dir = osp.join(devkit_path, 'VOC2012', 'SegmentationClassAug') - else: - out_dir = args.out_dir - mmcv.mkdir_or_exist(out_dir) - in_dir = osp.join(aug_path, 'dataset', 'cls') - - mmcv.track_parallel_progress( - partial(convert_mat, in_dir=in_dir, out_dir=out_dir), - list(mmcv.scandir(in_dir, suffix='.mat')), - nproc=nproc) - - full_aug_list = [] - with open(osp.join(aug_path, 'dataset', 'train.txt')) as f: - full_aug_list += [line.strip() for line in f] - with open(osp.join(aug_path, 'dataset', 'val.txt')) as f: - full_aug_list += [line.strip() for line in f] - - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'train.txt')) as f: - ori_train_list = [line.strip() for line in f] - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'val.txt')) as f: - val_list = [line.strip() for line in f] - - aug_train_list = generate_aug_list(ori_train_list + full_aug_list, - val_list) - assert len(aug_train_list) == AUG_LEN, 'len(aug_train_list) != {}'.format( - AUG_LEN) - - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'trainaug.txt'), 'w') as f: - f.writelines(line + '\n' for line in aug_train_list) - - aug_list = generate_aug_list(full_aug_list, ori_train_list + val_list) - assert len(aug_list) == AUG_LEN - len( - ori_train_list), 'len(aug_list) != {}'.format(AUG_LEN - - len(ori_train_list)) - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', 'aug.txt'), - 'w') as f: - f.writelines(line + '\n' for line in aug_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/tools/get_flops.py b/cv/semantic_segmentation/ddrnet/pytorch/tools/get_flops.py deleted file mode 100644 index e30c36fdf..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/tools/get_flops.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse - -from mmcv import Config -from mmcv.cnn import get_model_complexity_info - -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Get the FLOPs of a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument( - '--shape', - type=int, - nargs='+', - default=[2048, 1024], - help='input image size') - args = parser.parse_args() - return args - - -def main(): - - args = parse_args() - - if len(args.shape) == 1: - input_shape = (3, args.shape[0], args.shape[0]) - elif len(args.shape) == 2: - input_shape = (3, ) + tuple(args.shape) - else: - raise ValueError('invalid input shape') - - cfg = Config.fromfile(args.config) - cfg.model.pretrained = None - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')).cuda() - model.eval() - - if hasattr(model, 'forward_dummy'): - model.forward = model.forward_dummy - else: - raise NotImplementedError( - 'FLOPs counter is currently not currently supported with {}'. - format(model.__class__.__name__)) - - flops, params = get_model_complexity_info(model, input_shape) - split_line = '=' * 30 - print('{0}\nInput shape: {1}\nFlops: {2}\nParams: {3}\n{0}'.format( - split_line, input_shape, flops, params)) - print('!!!Please be cautious if you use the results in papers. ' - 'You may need to check if all ops are supported and verify that the ' - 'flops computation is correct.') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/tools/print_config.py b/cv/semantic_segmentation/ddrnet/pytorch/tools/print_config.py deleted file mode 100644 index 3f9c08dd9..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/tools/print_config.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import warnings - -from mmcv import Config, DictAction - -from mmseg.apis import init_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='Print the whole config') - parser.add_argument('config', help='config file path') - parser.add_argument( - '--graph', action='store_true', help='print the models graph') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - args = parser.parse_args() - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options, ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - print(f'Config:\n{cfg.pretty_text}') - # dump config - cfg.dump('example.py') - # dump models graph - if args.graph: - model = init_segmentor(args.config, device='cpu') - print(f'Model graph:\n{str(model)}') - with open('example-graph.txt', 'w') as f: - f.writelines(str(model)) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/tools/slurm_test.sh b/cv/semantic_segmentation/ddrnet/pytorch/tools/slurm_test.sh deleted file mode 100755 index 4e6f7bf4e..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/tools/slurm_test.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -set -x - -PARTITION=$1 -JOB_NAME=$2 -CONFIG=$3 -CHECKPOINT=$4 -GPUS=${GPUS:-4} -GPUS_PER_NODE=${GPUS_PER_NODE:-4} -CPUS_PER_TASK=${CPUS_PER_TASK:-5} -PY_ARGS=${@:5} -SRUN_ARGS=${SRUN_ARGS:-""} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -srun -p ${PARTITION} \ - --job-name=${JOB_NAME} \ - --gres=gpu:${GPUS_PER_NODE} \ - --ntasks=${GPUS} \ - --ntasks-per-node=${GPUS_PER_NODE} \ - --cpus-per-task=${CPUS_PER_TASK} \ - --kill-on-bad-exit=1 \ - ${SRUN_ARGS} \ - python -u tools/test.py ${CONFIG} ${CHECKPOINT} --launcher="slurm" ${PY_ARGS} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/tools/slurm_train.sh b/cv/semantic_segmentation/ddrnet/pytorch/tools/slurm_train.sh deleted file mode 100755 index ab232105f..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/tools/slurm_train.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -set -x - -PARTITION=$1 -JOB_NAME=$2 -CONFIG=$3 -GPUS=${GPUS:-4} -GPUS_PER_NODE=${GPUS_PER_NODE:-4} -CPUS_PER_TASK=${CPUS_PER_TASK:-5} -SRUN_ARGS=${SRUN_ARGS:-""} -PY_ARGS=${@:4} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -srun -p ${PARTITION} \ - --job-name=${JOB_NAME} \ - --gres=gpu:${GPUS_PER_NODE} \ - --ntasks=${GPUS} \ - --ntasks-per-node=${GPUS_PER_NODE} \ - --cpus-per-task=${CPUS_PER_TASK} \ - --kill-on-bad-exit=1 \ - ${SRUN_ARGS} \ - python -u tools/train.py ${CONFIG} --launcher="slurm" ${PY_ARGS} diff --git a/cv/semantic_segmentation/ddrnet/pytorch/tools/test.py b/cv/semantic_segmentation/ddrnet/pytorch/tools/test.py deleted file mode 100644 index 12892ec9b..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/tools/test.py +++ /dev/null @@ -1,319 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os -import os.path as osp -import shutil -import time -import warnings - -import mmcv -import torch -from mmcv.cnn.utils import revert_sync_batchnorm -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (get_dist_info, init_dist, load_checkpoint, - wrap_fp16_model) -from mmcv.utils import DictAction - -from mmseg import digit_version -from mmseg.apis import multi_gpu_test, single_gpu_test -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor -from mmseg.utils import setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser( - description='mmseg test (and eval) a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--work-dir', - help=('if specified, the evaluation metric results will be dumped' - 'into the directory as json')) - parser.add_argument( - '--aug-test', action='store_true', help='Use Flip and Multi scale aug') - parser.add_argument('--out', help='output result file in pickle format') - parser.add_argument( - '--format-only', - action='store_true', - help='Format the output results without perform evaluation. It is' - 'useful when you want to format the result to a specific format and ' - 'submit it to the test server') - parser.add_argument( - '--eval', - type=str, - nargs='+', - help='evaluation metrics, which depends on the dataset, e.g., "mIoU"' - ' for generic datasets, and "cityscapes" for Cityscapes') - parser.add_argument('--show', action='store_true', help='show results') - parser.add_argument( - '--show-dir', help='directory where painted images will be saved') - parser.add_argument( - '--gpu-collect', - action='store_true', - help='whether to use gpu to collect results.') - parser.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed testing)') - parser.add_argument( - '--tmpdir', - help='tmp directory used for collecting results from multiple ' - 'workers, available when gpu_collect is not specified') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--eval-options', - nargs='+', - action=DictAction, - help='custom options for evaluation') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument( - '--opacity', - type=float, - default=0.5, - help='Opacity of painted segmentation map. In (0, 1] range.') - parser.add_argument('--local_rank', type=int, default=0) - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - assert args.out or args.eval or args.format_only or args.show \ - or args.show_dir, \ - ('Please specify at least one operation (save/eval/format/show the ' - 'results / save the results) with the argument "--out", "--eval"' - ', "--format-only", "--show" or "--show-dir"') - - if args.eval and args.format_only: - raise ValueError('--eval and --format_only cannot be both specified') - - if args.out is not None and not args.out.endswith(('.pkl', '.pickle')): - raise ValueError('The output file must be a pkl file.') - - cfg = mmcv.Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - if args.aug_test: - # hard code index - cfg.data.test.pipeline[1].img_ratios = [ - 0.5, 0.75, 1.0, 1.25, 1.5, 1.75 - ] - cfg.data.test.pipeline[1].flip = True - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - if args.gpu_id is not None: - cfg.gpu_ids = [args.gpu_id] - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - cfg.gpu_ids = [args.gpu_id] - distributed = False - if len(cfg.gpu_ids) > 1: - warnings.warn(f'The gpu-ids is reset from {cfg.gpu_ids} to ' - f'{cfg.gpu_ids[0:1]} to avoid potential error in ' - 'non-distribute testing time.') - cfg.gpu_ids = cfg.gpu_ids[0:1] - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - - rank, _ = get_dist_info() - # allows not to create - if args.work_dir is not None and rank == 0: - mmcv.mkdir_or_exist(osp.abspath(args.work_dir)) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.aug_test: - json_file = osp.join(args.work_dir, - f'eval_multi_scale_{timestamp}.json') - else: - json_file = osp.join(args.work_dir, - f'eval_single_scale_{timestamp}.json') - elif rank == 0: - work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - mmcv.mkdir_or_exist(osp.abspath(work_dir)) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.aug_test: - json_file = osp.join(work_dir, - f'eval_multi_scale_{timestamp}.json') - else: - json_file = osp.join(work_dir, - f'eval_single_scale_{timestamp}.json') - - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - # The default loader config - loader_cfg = dict( - # cfg.gpus will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - shuffle=False) - # The overall dataloader settings - loader_cfg.update({ - k: v - for k, v in cfg.data.items() if k not in [ - 'train', 'val', 'test', 'train_dataloader', 'val_dataloader', - 'test_dataloader' - ] - }) - test_loader_cfg = { - **loader_cfg, - 'samples_per_gpu': 1, - 'shuffle': False, # Not shuffle by default - **cfg.data.get('test_dataloader', {}) - } - # build the dataloader - data_loader = build_dataloader(dataset, **test_loader_cfg) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - checkpoint = load_checkpoint(model, args.checkpoint, map_location='cpu') - if 'CLASSES' in checkpoint.get('meta', {}): - model.CLASSES = checkpoint['meta']['CLASSES'] - else: - print('"CLASSES" not found in meta, use dataset.CLASSES instead') - model.CLASSES = dataset.CLASSES - if 'PALETTE' in checkpoint.get('meta', {}): - model.PALETTE = checkpoint['meta']['PALETTE'] - else: - print('"PALETTE" not found in meta, use dataset.PALETTE instead') - model.PALETTE = dataset.PALETTE - - # clean gpu memory when starting a new evaluation. - torch.cuda.empty_cache() - eval_kwargs = {} if args.eval_options is None else args.eval_options - - # Deprecated - efficient_test = eval_kwargs.get('efficient_test', False) - if efficient_test: - warnings.warn( - '``efficient_test=True`` does not have effect in tools/test.py, ' - 'the evaluation and format results are CPU memory efficient by ' - 'default') - - eval_on_format_results = ( - args.eval is not None and 'cityscapes' in args.eval) - if eval_on_format_results: - assert len(args.eval) == 1, 'eval on format results is not ' \ - 'applicable for metrics other than ' \ - 'cityscapes' - if args.format_only or eval_on_format_results: - if 'imgfile_prefix' in eval_kwargs: - tmpdir = eval_kwargs['imgfile_prefix'] - else: - tmpdir = '.format_cityscapes' - eval_kwargs.setdefault('imgfile_prefix', tmpdir) - mmcv.mkdir_or_exist(tmpdir) - else: - tmpdir = None - - if not distributed: - warnings.warn( - 'SyncBN is only supported with DDP. To be compatible with DP, ' - 'we convert SyncBN to BN. Please use dist_train.sh which can ' - 'avoid this error.') - if not torch.cuda.is_available(): - assert digit_version(mmcv.__version__) >= digit_version('1.4.4'), \ - 'Please use MMCV >= 1.4.4 for CPU training!' - model = revert_sync_batchnorm(model) - model = MMDataParallel(model, device_ids=cfg.gpu_ids) - results = single_gpu_test( - model, - data_loader, - args.show, - args.show_dir, - False, - args.opacity, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - else: - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False) - results = multi_gpu_test( - model, - data_loader, - args.tmpdir, - args.gpu_collect, - False, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - - rank, _ = get_dist_info() - if rank == 0: - if args.out: - warnings.warn( - 'The behavior of ``args.out`` has been changed since MMSeg ' - 'v0.16, the pickled outputs could be seg map as type of ' - 'np.array, pre-eval results or file paths for ' - '``dataset.format_results()``.') - print(f'\nwriting results to {args.out}') - mmcv.dump(results, args.out) - if args.eval: - eval_kwargs.update(metric=args.eval) - metric = dataset.evaluate(results, **eval_kwargs) - metric_dict = dict(config=args.config, metric=metric) - mmcv.dump(metric_dict, json_file, indent=4) - if tmpdir is not None and eval_on_format_results: - # remove tmp dir when cityscapes evaluation - shutil.rmtree(tmpdir) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/train.py b/cv/semantic_segmentation/ddrnet/pytorch/train.py deleted file mode 100644 index e198dd60f..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/train.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import copy -import os -import os.path as osp -import time -import warnings - -import mmcv -import torch -import torch.distributed as dist -from mmcv.cnn.utils import revert_sync_batchnorm -from mmcv.runner import get_dist_info, init_dist -from mmcv.utils import Config, DictAction, get_git_hash - -from mmseg import __version__ -from mmseg.apis import init_random_seed, set_random_seed, train_segmentor -from mmseg.datasets import build_dataset -from mmseg.models import build_segmentor -from mmseg.utils import collect_env, get_root_logger, setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument('--work-dir', help='the dir to save logs and models') - parser.add_argument( - '--load-from', help='the checkpoint file to load weights from') - parser.add_argument( - '--resume-from', help='the checkpoint file to resume from') - parser.add_argument( - '--no-validate', - action='store_true', - help='whether not to evaluate the checkpoint during training') - group_gpus = parser.add_mutually_exclusive_group() - group_gpus.add_argument( - '--gpus', - type=int, - help='(Deprecated, please use --gpu-id) number of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-ids', - type=int, - nargs='+', - help='(Deprecated, please use --gpu-id) ids of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed training)') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--diff_seed', - action='store_true', - help='Whether or not set different seeds for different ranks') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - parser.add_argument('--dist_backend', type=str, default=None) - parser.add_argument( - '--auto-resume', - action='store_true', - help='resume from the latest checkpoint automatically.') - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - - # work_dir is determined in this priority: CLI > segment in file > filename - if args.work_dir is not None: - # update configs according to CLI args if args.work_dir is not None - cfg.work_dir = args.work_dir - elif cfg.get('work_dir', None) is None: - # use config filename as default work_dir if cfg.work_dir is None - cfg.work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - if args.load_from is not None: - cfg.load_from = args.load_from - if args.resume_from is not None: - cfg.resume_from = args.resume_from - if args.gpus is not None: - cfg.gpu_ids = range(1) - warnings.warn('`--gpus` is deprecated because we only support ' - 'single GPU mode in non-distributed training. ' - 'Use `gpus=1` now.') - if args.gpu_ids is not None: - cfg.gpu_ids = args.gpu_ids[0:1] - warnings.warn('`--gpu-ids` is deprecated, please use `--gpu-id`. ' - 'Because we only support single GPU mode in ' - 'non-distributed training. Use the first GPU ' - 'in `gpu_ids` now.') - if args.gpus is None and args.gpu_ids is None: - cfg.gpu_ids = [args.gpu_id] - - cfg.auto_resume = args.auto_resume - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - if args.dist_backend is not None: - cfg.dist_params.backend = args.dist_backend - init_dist(args.launcher, **cfg.dist_params) - # gpu_ids is used to calculate iter when resuming checkpoint - _, world_size = get_dist_info() - cfg.gpu_ids = range(world_size) - - # create work_dir - mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) - # dump config - cfg.dump(osp.join(cfg.work_dir, osp.basename(args.config))) - # init the logger before other steps - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - log_file = osp.join(cfg.work_dir, f'{timestamp}.log') - logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) - - # set multi-process settings - setup_multi_processes(cfg) - - # init the meta dict to record some important information such as - # environment info and seed, which will be logged - meta = dict() - # log env info - env_info_dict = collect_env() - env_info = '\n'.join([f'{k}: {v}' for k, v in env_info_dict.items()]) - dash_line = '-' * 60 + '\n' - logger.info('Environment info:\n' + dash_line + env_info + '\n' + - dash_line) - meta['env_info'] = env_info - - # log some basic info - logger.info(f'Distributed training: {distributed}') - logger.info(f'Config:\n{cfg.pretty_text}') - - # set random seeds - seed = init_random_seed(args.seed) - seed = seed + dist.get_rank() if args.diff_seed else seed - logger.info(f'Set random seed to {seed}, ' - f'deterministic: {args.deterministic}') - set_random_seed(seed, deterministic=args.deterministic) - cfg.seed = seed - meta['seed'] = seed - meta['exp_name'] = osp.basename(args.config) - - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')) - model.init_weights() - - # SyncBN is not support for DP - if not distributed: - warnings.warn( - 'SyncBN is only supported with DDP. To be compatible with DP, ' - 'we convert SyncBN to BN. Please use dist_train.sh which can ' - 'avoid this error.') - model = revert_sync_batchnorm(model) - - logger.info(model) - - datasets = [build_dataset(cfg.data.train)] - if len(cfg.workflow) == 2: - val_dataset = copy.deepcopy(cfg.data.val) - val_dataset.pipeline = cfg.data.train.pipeline - datasets.append(build_dataset(val_dataset)) - if cfg.checkpoint_config is not None: - # save mmseg version, config file content and class names in - # checkpoints as meta data - cfg.checkpoint_config.meta = dict( - mmseg_version=f'{__version__}+{get_git_hash()[:7]}', - config=cfg.pretty_text, - CLASSES=datasets[0].CLASSES, - PALETTE=datasets[0].PALETTE) - # add an attribute for visualization convenience - model.CLASSES = datasets[0].CLASSES - # passing checkpoint meta for saving best checkpoint - meta.update(cfg.checkpoint_config.meta) - train_segmentor( - model, - datasets, - cfg, - distributed=distributed, - validate=(not args.no_validate), - timestamp=timestamp, - meta=meta) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/ddrnet/pytorch/train_dist.sh b/cv/semantic_segmentation/ddrnet/pytorch/train_dist.sh deleted file mode 100755 index d09675129..000000000 --- a/cv/semantic_segmentation/ddrnet/pytorch/train_dist.sh +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - -CONFIG=$1 -GPUS=$2 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/train.py \ - $CONFIG \ - --launcher pytorch ${@:3} -- Gitee From d0e1a55c51ece924d6d3ad5b9a3fe79dc50e263d Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Wed, 5 Mar 2025 18:02:45 +0800 Subject: [PATCH 06/15] update gcnet --- .../gcnet/pytorch/.gitignore | 119 -- .../gcnet/pytorch/CITATION.cff | 8 - .../gcnet/pytorch/LICENSE | 203 --- .../gcnet/pytorch/README.md | 30 +- .../configs/_base_/datasets/cityscapes.py | 54 - .../_base_/datasets/cityscapes_769x769.py | 35 - .../pytorch/configs/_base_/default_runtime.py | 14 - .../configs/_base_/models/gcnet_r50-d8.py | 46 - .../configs/_base_/schedules/schedule_40k.py | 9 - .../gcnet/pytorch/configs/gcnet/README.md | 68 - .../gcnet_r50-d8_769x769_40k_cityscapes.py | 9 - .../gcnet/pytorch/dist_train.sh | 17 - .../gcnet/pytorch/mmcv/__init__.py | 15 - .../gcnet/pytorch/mmcv/cnn/__init__.py | 65 - .../gcnet/pytorch/mmcv/cnn/bricks/__init__.py | 50 - .../pytorch/mmcv/cnn/bricks/activation.py | 92 -- .../pytorch/mmcv/cnn/bricks/context_block.py | 125 -- .../gcnet/pytorch/mmcv/cnn/bricks/conv.py | 44 - .../pytorch/mmcv/cnn/bricks/conv_module.py | 206 --- .../bricks/depthwise_separable_conv_module.py | 96 -- .../mmcv/cnn/bricks/generalized_attention.py | 412 ------ .../gcnet/pytorch/mmcv/cnn/bricks/norm.py | 144 --- .../gcnet/pytorch/mmcv/cnn/bricks/padding.py | 36 - .../gcnet/pytorch/mmcv/cnn/bricks/plugin.py | 88 -- .../gcnet/pytorch/mmcv/cnn/bricks/registry.py | 16 - .../gcnet/pytorch/mmcv/cnn/bricks/scale.py | 21 - .../pytorch/mmcv/cnn/bricks/transformer.py | 595 --------- .../gcnet/pytorch/mmcv/cnn/bricks/upsample.py | 84 -- .../gcnet/pytorch/mmcv/cnn/bricks/wrappers.py | 180 --- .../gcnet/pytorch/mmcv/cnn/builder.py | 30 - .../gcnet/pytorch/mmcv/cnn/resnet.py | 316 ----- .../gcnet/pytorch/mmcv/cnn/utils/__init__.py | 19 - .../pytorch/mmcv/cnn/utils/flops_counter.py | 599 --------- .../pytorch/mmcv/cnn/utils/fuse_conv_bn.py | 59 - .../gcnet/pytorch/mmcv/cnn/utils/sync_bn.py | 59 - .../pytorch/mmcv/cnn/utils/weight_init.py | 684 ---------- .../gcnet/pytorch/mmcv/engine/__init__.py | 8 - .../gcnet/pytorch/mmcv/engine/test.py | 202 --- .../gcnet/pytorch/mmcv/fileio/__init__.py | 11 - .../gcnet/pytorch/mmcv/fileio/file_client.py | 1148 ----------------- .../pytorch/mmcv/fileio/handlers/__init__.py | 7 - .../pytorch/mmcv/fileio/handlers/base.py | 30 - .../mmcv/fileio/handlers/json_handler.py | 36 - .../mmcv/fileio/handlers/pickle_handler.py | 28 - .../mmcv/fileio/handlers/yaml_handler.py | 24 - .../gcnet/pytorch/mmcv/fileio/io.py | 151 --- .../gcnet/pytorch/mmcv/fileio/parse.py | 97 -- .../gcnet/pytorch/mmcv/image/__init__.py | 28 - .../gcnet/pytorch/mmcv/image/colorspace.py | 306 ----- .../gcnet/pytorch/mmcv/image/geometric.py | 728 ----------- .../gcnet/pytorch/mmcv/image/io.py | 258 ---- .../gcnet/pytorch/mmcv/image/misc.py | 44 - .../gcnet/pytorch/mmcv/image/photometric.py | 428 ------ .../gcnet/pytorch/mmcv/ops/__init__.py | 95 -- .../gcnet/pytorch/mmcv/ops/csrc/README.md | 169 --- .../csrc/common/cuda/common_cuda_helper.hpp | 112 -- .../cuda/sigmoid_focal_loss_cuda_kernel.cuh | 71 - .../cuda/softmax_focal_loss_cuda_kernel.cuh | 72 -- .../csrc/common/cuda/sync_bn_cuda_kernel.cuh | 331 ----- .../ops/csrc/common/pytorch_cpp_helper.hpp | 24 - .../ops/csrc/common/pytorch_cuda_helper.hpp | 19 - .../ops/csrc/pytorch/cuda/focal_loss_cuda.cu | 111 -- .../ops/csrc/pytorch/cuda/sync_bn_cuda.cu | 110 -- .../mmcv/ops/csrc/pytorch/focal_loss.cpp | 131 -- .../pytorch/mmcv/ops/csrc/pytorch/info.cpp | 56 - .../pytorch/mmcv/ops/csrc/pytorch/pybind.cpp | 687 ---------- .../pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp | 159 --- .../pytorch/mmcv/ops/deprecated_wrappers.py | 43 - .../gcnet/pytorch/mmcv/ops/focal_loss.py | 212 --- .../gcnet/pytorch/mmcv/ops/info.py | 36 - .../gcnet/pytorch/mmcv/ops/sync_bn.py | 279 ---- .../gcnet/pytorch/mmcv/parallel/__init__.py | 13 - .../gcnet/pytorch/mmcv/parallel/_functions.py | 79 -- .../gcnet/pytorch/mmcv/parallel/collate.py | 84 -- .../pytorch/mmcv/parallel/data_container.py | 89 -- .../pytorch/mmcv/parallel/data_parallel.py | 89 -- .../pytorch/mmcv/parallel/distributed.py | 112 -- .../mmcv/parallel/distributed_deprecated.py | 70 - .../gcnet/pytorch/mmcv/parallel/registry.py | 8 - .../pytorch/mmcv/parallel/scatter_gather.py | 59 - .../gcnet/pytorch/mmcv/parallel/utils.py | 20 - .../gcnet/pytorch/mmcv/runner/__init__.py | 47 - .../gcnet/pytorch/mmcv/runner/base_module.py | 195 --- .../gcnet/pytorch/mmcv/runner/base_runner.py | 542 -------- .../gcnet/pytorch/mmcv/runner/builder.py | 24 - .../gcnet/pytorch/mmcv/runner/checkpoint.py | 707 ---------- .../mmcv/runner/default_constructor.py | 44 - .../gcnet/pytorch/mmcv/runner/dist_utils.py | 164 --- .../pytorch/mmcv/runner/epoch_based_runner.py | 187 --- .../gcnet/pytorch/mmcv/runner/fp16_utils.py | 410 ------ .../pytorch/mmcv/runner/hooks/__init__.py | 29 - .../pytorch/mmcv/runner/hooks/checkpoint.py | 167 --- .../pytorch/mmcv/runner/hooks/closure.py | 11 - .../gcnet/pytorch/mmcv/runner/hooks/ema.py | 89 -- .../pytorch/mmcv/runner/hooks/evaluation.py | 509 -------- .../gcnet/pytorch/mmcv/runner/hooks/hook.py | 92 -- .../pytorch/mmcv/runner/hooks/iter_timer.py | 34 - .../mmcv/runner/hooks/logger/__init__.py | 15 - .../pytorch/mmcv/runner/hooks/logger/base.py | 166 --- .../mmcv/runner/hooks/logger/dvclive.py | 58 - .../mmcv/runner/hooks/logger/mlflow.py | 78 -- .../mmcv/runner/hooks/logger/neptune.py | 82 -- .../pytorch/mmcv/runner/hooks/logger/pavi.py | 117 -- .../mmcv/runner/hooks/logger/tensorboard.py | 57 - .../pytorch/mmcv/runner/hooks/logger/text.py | 256 ---- .../pytorch/mmcv/runner/hooks/logger/wandb.py | 56 - .../pytorch/mmcv/runner/hooks/lr_updater.py | 670 ---------- .../gcnet/pytorch/mmcv/runner/hooks/memory.py | 25 - .../mmcv/runner/hooks/momentum_updater.py | 493 ------- .../pytorch/mmcv/runner/hooks/optimizer.py | 508 -------- .../pytorch/mmcv/runner/hooks/profiler.py | 180 --- .../pytorch/mmcv/runner/hooks/sampler_seed.py | 20 - .../pytorch/mmcv/runner/hooks/sync_buffer.py | 22 - .../pytorch/mmcv/runner/iter_based_runner.py | 273 ---- .../gcnet/pytorch/mmcv/runner/log_buffer.py | 41 - .../pytorch/mmcv/runner/optimizer/__init__.py | 9 - .../pytorch/mmcv/runner/optimizer/builder.py | 44 - .../runner/optimizer/default_constructor.py | 249 ---- .../gcnet/pytorch/mmcv/runner/priority.py | 60 - .../gcnet/pytorch/mmcv/runner/utils.py | 93 -- .../gcnet/pytorch/mmcv/utils/__init__.py | 69 - .../gcnet/pytorch/mmcv/utils/config.py | 689 ---------- .../gcnet/pytorch/mmcv/utils/env.py | 95 -- .../gcnet/pytorch/mmcv/utils/ext_loader.py | 71 - .../gcnet/pytorch/mmcv/utils/logging.py | 110 -- .../gcnet/pytorch/mmcv/utils/misc.py | 377 ------ .../gcnet/pytorch/mmcv/utils/parrots_jit.py | 41 - .../pytorch/mmcv/utils/parrots_wrapper.py | 107 -- .../gcnet/pytorch/mmcv/utils/path.py | 101 -- .../gcnet/pytorch/mmcv/utils/progressbar.py | 208 --- .../gcnet/pytorch/mmcv/utils/registry.py | 315 ----- .../gcnet/pytorch/mmcv/utils/testing.py | 140 -- .../gcnet/pytorch/mmcv/utils/timer.py | 118 -- .../gcnet/pytorch/mmcv/utils/trace.py | 23 - .../gcnet/pytorch/mmcv/utils/version_utils.py | 90 -- .../gcnet/pytorch/mmcv/version.py | 35 - .../gcnet/pytorch/mmseg/__init__.py | 62 - .../gcnet/pytorch/mmseg/apis/__init__.py | 25 - .../gcnet/pytorch/mmseg/apis/inference.py | 136 -- .../gcnet/pytorch/mmseg/apis/test.py | 233 ---- .../gcnet/pytorch/mmseg/apis/train.py | 167 --- .../gcnet/pytorch/mmseg/core/__init__.py | 4 - .../pytorch/mmseg/core/evaluation/__init__.py | 11 - .../mmseg/core/evaluation/class_names.py | 153 --- .../mmseg/core/evaluation/eval_hooks.py | 128 -- .../pytorch/mmseg/core/evaluation/metrics.py | 395 ------ .../gcnet/pytorch/mmseg/core/seg/__init__.py | 5 - .../gcnet/pytorch/mmseg/core/seg/builder.py | 9 - .../mmseg/core/seg/sampler/__init__.py | 5 - .../core/seg/sampler/base_pixel_sampler.py | 13 - .../core/seg/sampler/ohem_pixel_sampler.py | 85 -- .../pytorch/mmseg/core/utils/__init__.py | 4 - .../gcnet/pytorch/mmseg/core/utils/misc.py | 18 - .../gcnet/pytorch/mmseg/datasets/__init__.py | 31 - .../gcnet/pytorch/mmseg/datasets/builder.py | 182 --- .../pytorch/mmseg/datasets/cityscapes.py | 214 --- .../gcnet/pytorch/mmseg/datasets/custom.py | 457 ------- .../mmseg/datasets/dataset_wrappers.py | 190 --- .../mmseg/datasets/pipelines/__init__.py | 18 - .../mmseg/datasets/pipelines/compose.py | 52 - .../mmseg/datasets/pipelines/formating.py | 9 - .../mmseg/datasets/pipelines/formatting.py | 289 ----- .../mmseg/datasets/pipelines/loading.py | 154 --- .../mmseg/datasets/pipelines/test_time_aug.py | 134 -- .../mmseg/datasets/pipelines/transforms.py | 1042 --------------- .../gcnet/pytorch/mmseg/models/__init__.py | 13 - .../mmseg/models/backbones/__init__.py | 18 - .../pytorch/mmseg/models/backbones/resnet.py | 714 ---------- .../gcnet/pytorch/mmseg/models/builder.py | 49 - .../mmseg/models/decode_heads/__init__.py | 53 - .../mmseg/models/decode_heads/decode_head.py | 265 ---- .../mmseg/models/decode_heads/fcn_head.py | 82 -- .../mmseg/models/decode_heads/gc_head.py | 48 - .../pytorch/mmseg/models/losses/__init__.py | 30 - .../pytorch/mmseg/models/losses/accuracy.py | 79 -- .../mmseg/models/losses/cross_entropy_loss.py | 218 ---- .../pytorch/mmseg/models/losses/utils.py | 122 -- .../pytorch/mmseg/models/necks/__init__.py | 8 - .../gcnet/pytorch/mmseg/models/necks/fpn.py | 213 --- .../pytorch/mmseg/models/necks/ic_neck.py | 147 --- .../gcnet/pytorch/mmseg/models/necks/jpu.py | 131 -- .../pytorch/mmseg/models/necks/mla_neck.py | 118 -- .../mmseg/models/necks/multilevel_neck.py | 78 -- .../mmseg/models/segmentors/__init__.py | 21 - .../pytorch/mmseg/models/segmentors/base.py | 277 ---- .../models/segmentors/encoder_decoder.py | 284 ---- .../pytorch/mmseg/models/utils/__init__.py | 14 - .../gcnet/pytorch/mmseg/models/utils/embed.py | 330 ----- .../mmseg/models/utils/inverted_residual.py | 213 --- .../mmseg/models/utils/make_divisible.py | 28 - .../pytorch/mmseg/models/utils/res_layer.py | 96 -- .../pytorch/mmseg/models/utils/se_layer.py | 58 - .../models/utils/self_attention_block.py | 160 --- .../mmseg/models/utils/shape_convert.py | 29 - .../mmseg/models/utils/up_conv_block.py | 102 -- .../gcnet/pytorch/mmseg/ops/__init__.py | 5 - .../gcnet/pytorch/mmseg/ops/encoding.py | 75 -- .../gcnet/pytorch/mmseg/ops/wrappers.py | 51 - .../gcnet/pytorch/mmseg/utils/__init__.py | 5 - .../gcnet/pytorch/mmseg/utils/collect_env.py | 18 - .../gcnet/pytorch/mmseg/utils/logger.py | 28 - .../gcnet/pytorch/mmseg/version.py | 18 - .../gcnet/pytorch/requirements.txt | 5 - .../gcnet/pytorch/requirements/docs.txt | 6 - .../gcnet/pytorch/requirements/mminstall.txt | 1 - .../gcnet/pytorch/requirements/optional.txt | 1 - .../pytorch/requirements/readthedocs.txt | 4 - .../gcnet/pytorch/requirements/runtime.txt | 4 - .../gcnet/pytorch/requirements/tests.txt | 7 - .../gcnet/pytorch/run_train.sh | 15 - .../gcnet/pytorch/setup.py | 353 ----- .../gcnet/pytorch/tools/analyze_logs.py | 130 -- .../gcnet/pytorch/tools/benchmark.py | 86 -- .../tools/convert_datasets/cityscapes.py | 56 - .../gcnet/pytorch/tools/get_flops.py | 59 - .../gcnet/pytorch/tools/print_config.py | 69 - .../gcnet/pytorch/tools/test.py | 259 ---- .../gcnet/pytorch/train.py | 211 --- 218 files changed, 21 insertions(+), 30601 deletions(-) delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/.gitignore delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/CITATION.cff delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/LICENSE delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/configs/_base_/datasets/cityscapes.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/configs/_base_/default_runtime.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/configs/_base_/models/gcnet_r50-d8.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/configs/_base_/schedules/schedule_40k.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/configs/gcnet/README.md delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/configs/gcnet/gcnet_r50-d8_769x769_40k_cityscapes.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/dist_train.sh delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/activation.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/context_block.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/conv.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/conv_module.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/generalized_attention.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/norm.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/padding.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/plugin.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/registry.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/scale.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/transformer.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/upsample.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/wrappers.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/builder.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/resnet.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/flops_counter.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/sync_bn.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/weight_init.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/engine/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/engine/test.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/file_client.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/base.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/json_handler.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/pickle_handler.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/yaml_handler.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/io.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/parse.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/image/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/image/colorspace.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/image/geometric.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/image/io.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/image/misc.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/image/photometric.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/README.md delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/deprecated_wrappers.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/focal_loss.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/info.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/sync_bn.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/_functions.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/collate.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/data_container.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/data_parallel.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/distributed.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/distributed_deprecated.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/registry.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/scatter_gather.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/utils.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/base_module.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/base_runner.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/builder.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/checkpoint.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/default_constructor.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/dist_utils.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/epoch_based_runner.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/fp16_utils.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/checkpoint.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/closure.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/ema.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/evaluation.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/hook.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/iter_timer.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/base.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/dvclive.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/mlflow.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/neptune.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/pavi.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/text.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/wandb.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/lr_updater.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/memory.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/momentum_updater.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/optimizer.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/profiler.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/sampler_seed.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/sync_buffer.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/iter_based_runner.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/log_buffer.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/optimizer/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/optimizer/builder.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/optimizer/default_constructor.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/priority.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/utils.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/config.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/env.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/ext_loader.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/logging.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/misc.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/parrots_jit.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/parrots_wrapper.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/path.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/progressbar.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/registry.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/testing.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/timer.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/trace.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/version_utils.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmcv/version.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/inference.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/test.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/train.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/core/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/class_names.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/eval_hooks.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/metrics.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/builder.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/sampler/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/core/utils/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/core/utils/misc.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/builder.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/cityscapes.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/custom.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/dataset_wrappers.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/compose.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/formating.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/formatting.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/loading.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/transforms.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/backbones/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/backbones/resnet.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/builder.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/decode_head.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/fcn_head.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/gc_head.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/accuracy.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/cross_entropy_loss.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/utils.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/fpn.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/ic_neck.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/jpu.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/mla_neck.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/multilevel_neck.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/segmentors/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/segmentors/base.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/segmentors/encoder_decoder.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/embed.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/inverted_residual.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/make_divisible.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/res_layer.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/se_layer.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/self_attention_block.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/shape_convert.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/up_conv_block.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/ops/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/ops/encoding.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/ops/wrappers.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/utils/__init__.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/utils/collect_env.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/utils/logger.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/mmseg/version.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/requirements.txt delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/requirements/docs.txt delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/requirements/mminstall.txt delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/requirements/optional.txt delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/requirements/readthedocs.txt delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/requirements/runtime.txt delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/requirements/tests.txt delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/run_train.sh delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/setup.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/tools/analyze_logs.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/tools/benchmark.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/tools/convert_datasets/cityscapes.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/tools/get_flops.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/tools/print_config.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/tools/test.py delete mode 100755 cv/semantic_segmentation/gcnet/pytorch/train.py diff --git a/cv/semantic_segmentation/gcnet/pytorch/.gitignore b/cv/semantic_segmentation/gcnet/pytorch/.gitignore deleted file mode 100755 index 9ece7304b..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/.gitignore +++ /dev/null @@ -1,119 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST -.circleci -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ -data -.so -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -data -.vscode -.idea - -# custom -*.pkl -*.pkl.json -*.log.json -work_dirs/ -mmseg/.mim - -# Pytorch -*.pth diff --git a/cv/semantic_segmentation/gcnet/pytorch/CITATION.cff b/cv/semantic_segmentation/gcnet/pytorch/CITATION.cff deleted file mode 100755 index cfd7cab05..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/CITATION.cff +++ /dev/null @@ -1,8 +0,0 @@ -cff-version: 1.2.0 -message: "If you use this software, please cite it as below." -authors: - - name: "MMSegmentation Contributors" -title: "OpenMMLab Semantic Segmentation Toolbox and Benchmark" -date-released: 2020-07-10 -url: "https://github.com/open-mmlab/mmsegmentation" -license: Apache-2.0 diff --git a/cv/semantic_segmentation/gcnet/pytorch/LICENSE b/cv/semantic_segmentation/gcnet/pytorch/LICENSE deleted file mode 100755 index 38e625bf5..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Copyright 2020 The MMSegmentation Authors. All rights reserved. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020 The MMSegmentation Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cv/semantic_segmentation/gcnet/pytorch/README.md b/cv/semantic_segmentation/gcnet/pytorch/README.md index 079b01aea..4b9aac50f 100755 --- a/cv/semantic_segmentation/gcnet/pytorch/README.md +++ b/cv/semantic_segmentation/gcnet/pytorch/README.md @@ -7,6 +7,23 @@ It is based on the Non-Local Network, but it modifies the architecture so less c Global context blocks are applied to multiple layers in a backbone network to construct the GCNet. ## Step 1: Installing +### Install packages + +```bash +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +# install mmsegmentation +git clone -b v1.2.2 https://github.com/open-mmlab/mmsegmentation.git --depth=1 +cd mmsegmentation/ +pip install -v -e . + +pip install ftfy +``` + ### Datasets Go to visit [Cityscapes official website](https://www.cityscapes-dataset.com/), then choose 'Download' to download the Cityscapes dataset. @@ -59,22 +76,17 @@ data/    └── val.lst ``` -### Build Extension -```shell -MMCV_WITH_OPS=1 python3 setup.py build && cp build/lib.linux*/mmcv/_ext.cpython* mmcv -pip3 install -r requirements.txt -``` ## Step 2: Training ### Training on single card ```shell -bash run_train.sh +python3 tools/train.py configs/gcnet/gcnet_r50-d8_4xb2-40k_cityscapes-769x769.py ``` ### Training on mutil-cards ```shell -bash dist_train.sh configs/gcnet/gcnet_r50-d8_769x769_40k_cityscapes.py 4 +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/gcnet/gcnet_r50-d8_4xb2-40k_cityscapes-769x769.py 8 ``` ## Reference - -Ref: https://github.com/LikeLy-Journey/SegmenTron +[mmsegmentation](https://github.com/open-mmlab/mmsegmentation) \ No newline at end of file diff --git a/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/datasets/cityscapes.py b/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/datasets/cityscapes.py deleted file mode 100755 index f21867c63..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/datasets/cityscapes.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'CityscapesDataset' -data_root = 'data/cityscapes/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 1024) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/train', - ann_dir='gtFine/train', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/val', - ann_dir='gtFine/val', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/val', - ann_dir='gtFine/val', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py b/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py deleted file mode 100755 index 336c7b254..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (769, 769) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2049, 1025), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/default_runtime.py b/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/default_runtime.py deleted file mode 100755 index b564cc4e7..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/default_runtime.py +++ /dev/null @@ -1,14 +0,0 @@ -# yapf:disable -log_config = dict( - interval=50, - hooks=[ - dict(type='TextLoggerHook', by_epoch=False), - # dict(type='TensorboardLoggerHook') - ]) -# yapf:enable -dist_params = dict(backend='nccl') -log_level = 'INFO' -load_from = None -resume_from = None -workflow = [('train', 1)] -cudnn_benchmark = True diff --git a/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/models/gcnet_r50-d8.py b/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/models/gcnet_r50-d8.py deleted file mode 100755 index 90518d52d..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/models/gcnet_r50-d8.py +++ /dev/null @@ -1,46 +0,0 @@ -# model settings -norm_cfg = dict(type='SyncBN', requires_grad=True) -model = dict( - type='EncoderDecoder', - pretrained='https://download.openmmlab.com/pretrain/third_party/resnet50_v1c-2cccc1ad.pth', - backbone=dict( - type='ResNetV1c', - depth=50, - num_stages=4, - out_indices=(0, 1, 2, 3), - dilations=(1, 1, 2, 4), - strides=(1, 2, 1, 1), - norm_cfg=norm_cfg, - norm_eval=False, - style='pytorch', - contract_dilation=True), - decode_head=dict( - type='GCHead', - in_channels=2048, - in_index=3, - channels=512, - ratio=1 / 4., - pooling_type='att', - fusion_types=('channel_add', ), - dropout_ratio=0.1, - num_classes=19, - norm_cfg=norm_cfg, - align_corners=False, - loss_decode=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), - auxiliary_head=dict( - type='FCNHead', - in_channels=1024, - in_index=2, - channels=256, - num_convs=1, - concat_input=False, - dropout_ratio=0.1, - num_classes=19, - norm_cfg=norm_cfg, - align_corners=False, - loss_decode=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), - # model training and testing settings - train_cfg=dict(), - test_cfg=dict(mode='whole')) diff --git a/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/schedules/schedule_40k.py b/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/schedules/schedule_40k.py deleted file mode 100755 index d2c502325..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/configs/_base_/schedules/schedule_40k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=40000) -checkpoint_config = dict(by_epoch=False, interval=4000) -evaluation = dict(interval=4000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/gcnet/pytorch/configs/gcnet/README.md b/cv/semantic_segmentation/gcnet/pytorch/configs/gcnet/README.md deleted file mode 100755 index b6a44b2bc..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/configs/gcnet/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond - -## Introduction - - - -Official Repo - -Code Snippet - -## Abstract - - - -The Non-Local Network (NLNet) presents a pioneering approach for capturing long-range dependencies, via aggregating query-specific global context to each query position. However, through a rigorous empirical analysis, we have found that the global contexts modeled by non-local network are almost the same for different query positions within an image. In this paper, we take advantage of this finding to create a simplified network based on a query-independent formulation, which maintains the accuracy of NLNet but with significantly less computation. We further observe that this simplified design shares similar structure with Squeeze-Excitation Network (SENet). Hence we unify them into a three-step general framework for global context modeling. Within the general framework, we design a better instantiation, called the global context (GC) block, which is lightweight and can effectively model the global context. The lightweight property allows us to apply it for multiple layers in a backbone network to construct a global context network (GCNet), which generally outperforms both simplified NLNet and SENet on major benchmarks for various recognition tasks. The code and configurations are released at [this https URL](https://github.com/xvjiarui/GCNet). - - -
- -
- -
-GCNet (ICCVW'2019/TPAMI'2020) - -```latex -@inproceedings{cao2019gcnet, - title={Gcnet: Non-local networks meet squeeze-excitation networks and beyond}, - author={Cao, Yue and Xu, Jiarui and Lin, Stephen and Wei, Fangyun and Hu, Han}, - booktitle={Proceedings of the IEEE International Conference on Computer Vision Workshops}, - pages={0--0}, - year={2019} -} -``` - -
- -## Results and models - -### Cityscapes - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| GCNet | R-50-D8 | 512x1024 | 40000 | 5.8 | 3.93 | 77.69 | 78.56 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r50-d8_512x1024_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_512x1024_40k_cityscapes/gcnet_r50-d8_512x1024_40k_cityscapes_20200618_074436-4b0fd17b.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_512x1024_40k_cityscapes/gcnet_r50-d8_512x1024_40k_cityscapes_20200618_074436.log.json) | -| GCNet | R-101-D8 | 512x1024 | 40000 | 9.2 | 2.61 | 78.28 | 79.34 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r101-d8_512x1024_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_512x1024_40k_cityscapes/gcnet_r101-d8_512x1024_40k_cityscapes_20200618_074436-5e62567f.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_512x1024_40k_cityscapes/gcnet_r101-d8_512x1024_40k_cityscapes_20200618_074436.log.json) | -| GCNet | R-50-D8 | 769x769 | 40000 | 6.5 | 1.67 | 78.12 | 80.09 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r50-d8_769x769_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_769x769_40k_cityscapes/gcnet_r50-d8_769x769_40k_cityscapes_20200618_182814-a26f4471.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_769x769_40k_cityscapes/gcnet_r50-d8_769x769_40k_cityscapes_20200618_182814.log.json) | -| GCNet | R-101-D8 | 769x769 | 40000 | 10.5 | 1.13 | 78.95 | 80.71 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r101-d8_769x769_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_769x769_40k_cityscapes/gcnet_r101-d8_769x769_40k_cityscapes_20200619_092550-ca4f0a84.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_769x769_40k_cityscapes/gcnet_r101-d8_769x769_40k_cityscapes_20200619_092550.log.json) | -| GCNet | R-50-D8 | 512x1024 | 80000 | - | - | 78.48 | 80.01 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r50-d8_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_512x1024_80k_cityscapes/gcnet_r50-d8_512x1024_80k_cityscapes_20200618_074450-ef8f069b.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_512x1024_80k_cityscapes/gcnet_r50-d8_512x1024_80k_cityscapes_20200618_074450.log.json) | -| GCNet | R-101-D8 | 512x1024 | 80000 | - | - | 79.03 | 79.84 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r101-d8_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_512x1024_80k_cityscapes/gcnet_r101-d8_512x1024_80k_cityscapes_20200618_074450-778ebf69.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_512x1024_80k_cityscapes/gcnet_r101-d8_512x1024_80k_cityscapes_20200618_074450.log.json) | -| GCNet | R-50-D8 | 769x769 | 80000 | - | - | 78.68 | 80.66 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r50-d8_769x769_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_769x769_80k_cityscapes/gcnet_r50-d8_769x769_80k_cityscapes_20200619_092516-4839565b.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_769x769_80k_cityscapes/gcnet_r50-d8_769x769_80k_cityscapes_20200619_092516.log.json) | -| GCNet | R-101-D8 | 769x769 | 80000 | - | - | 79.18 | 80.71 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r101-d8_769x769_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_769x769_80k_cityscapes/gcnet_r101-d8_769x769_80k_cityscapes_20200619_092628-8e043423.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_769x769_80k_cityscapes/gcnet_r101-d8_769x769_80k_cityscapes_20200619_092628.log.json) | - -### ADE20K - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| GCNet | R-50-D8 | 512x512 | 80000 | 8.5 | 23.38 | 41.47 | 42.85 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r50-d8_512x512_80k_ade20k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_512x512_80k_ade20k/gcnet_r50-d8_512x512_80k_ade20k_20200614_185146-91a6da41.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_512x512_80k_ade20k/gcnet_r50-d8_512x512_80k_ade20k_20200614_185146.log.json) | -| GCNet | R-101-D8 | 512x512 | 80000 | 12 | 15.20 | 42.82 | 44.54 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r101-d8_512x512_80k_ade20k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_512x512_80k_ade20k/gcnet_r101-d8_512x512_80k_ade20k_20200615_020811-c3fcb6dd.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_512x512_80k_ade20k/gcnet_r101-d8_512x512_80k_ade20k_20200615_020811.log.json) | -| GCNet | R-50-D8 | 512x512 | 160000 | - | - | 42.37 | 43.52 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r50-d8_512x512_160k_ade20k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_512x512_160k_ade20k/gcnet_r50-d8_512x512_160k_ade20k_20200615_224122-d95f3e1f.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_512x512_160k_ade20k/gcnet_r50-d8_512x512_160k_ade20k_20200615_224122.log.json) | -| GCNet | R-101-D8 | 512x512 | 160000 | - | - | 43.69 | 45.21 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r101-d8_512x512_160k_ade20k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_512x512_160k_ade20k/gcnet_r101-d8_512x512_160k_ade20k_20200615_225406-615528d7.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_512x512_160k_ade20k/gcnet_r101-d8_512x512_160k_ade20k_20200615_225406.log.json) | - -### Pascal VOC 2012 + Aug - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| GCNet | R-50-D8 | 512x512 | 20000 | 5.8 | 23.35 | 76.42 | 77.51 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r50-d8_512x512_20k_voc12aug.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_512x512_20k_voc12aug/gcnet_r50-d8_512x512_20k_voc12aug_20200617_165701-3cbfdab1.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_512x512_20k_voc12aug/gcnet_r50-d8_512x512_20k_voc12aug_20200617_165701.log.json) | -| GCNet | R-101-D8 | 512x512 | 20000 | 9.2 | 14.80 | 77.41 | 78.56 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r101-d8_512x512_20k_voc12aug.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_512x512_20k_voc12aug/gcnet_r101-d8_512x512_20k_voc12aug_20200617_165713-6c720aa9.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_512x512_20k_voc12aug/gcnet_r101-d8_512x512_20k_voc12aug_20200617_165713.log.json) | -| GCNet | R-50-D8 | 512x512 | 40000 | - | - | 76.24 | 77.63 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r50-d8_512x512_40k_voc12aug.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_512x512_40k_voc12aug/gcnet_r50-d8_512x512_40k_voc12aug_20200613_195105-9797336d.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r50-d8_512x512_40k_voc12aug/gcnet_r50-d8_512x512_40k_voc12aug_20200613_195105.log.json) | -| GCNet | R-101-D8 | 512x512 | 40000 | - | - | 77.84 | 78.59 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/gcnet/gcnet_r101-d8_512x512_40k_voc12aug.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_512x512_40k_voc12aug/gcnet_r101-d8_512x512_40k_voc12aug_20200613_185806-1e38208d.pth) | [log](https://download.openmmlab.com/mmsegmentation/v0.5/gcnet/gcnet_r101-d8_512x512_40k_voc12aug/gcnet_r101-d8_512x512_40k_voc12aug_20200613_185806.log.json) | diff --git a/cv/semantic_segmentation/gcnet/pytorch/configs/gcnet/gcnet_r50-d8_769x769_40k_cityscapes.py b/cv/semantic_segmentation/gcnet/pytorch/configs/gcnet/gcnet_r50-d8_769x769_40k_cityscapes.py deleted file mode 100755 index 332495d3d..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/configs/gcnet/gcnet_r50-d8_769x769_40k_cityscapes.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = [ - '../_base_/models/gcnet_r50-d8.py', - '../_base_/datasets/cityscapes_769x769.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_40k.py' -] -model = dict( - decode_head=dict(align_corners=True), - auxiliary_head=dict(align_corners=True), - test_cfg=dict(mode='slide', crop_size=(769, 769), stride=(513, 513))) diff --git a/cv/semantic_segmentation/gcnet/pytorch/dist_train.sh b/cv/semantic_segmentation/gcnet/pytorch/dist_train.sh deleted file mode 100755 index 6f564e0ab..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/dist_train.sh +++ /dev/null @@ -1,17 +0,0 @@ -CONFIG=$1 -GPUS=$2 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/train.py \ - $CONFIG \ - --launcher pytorch ${@:3} \ No newline at end of file diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/__init__.py deleted file mode 100755 index e1284b759..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -# from .arraymisc import * -from .fileio import * -from .image import * -from .utils import * -from .version import * -# from .video import * -# from .visualization import * - -# The following modules are not imported to this level, so mmcv may be used -# without PyTorch. -# - runner -# - parallel -# - op diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/__init__.py deleted file mode 100755 index e431dd720..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/__init__.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# from .alexnet import AlexNet -# yapf: disable -from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, - ContextBlock, Conv2d, ConvModule, - ConvTranspose2d, - DepthwiseSeparableConvModule, GeneralizedAttention, - Linear, MaxPool2d, - Scale, - build_activation_layer, build_conv_layer, - build_norm_layer, build_padding_layer, build_plugin_layer, - build_upsample_layer, is_norm) -# from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, -# PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, -# ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, -# ConvTranspose2d, ConvTranspose3d, ConvWS2d, -# DepthwiseSeparableConvModule, GeneralizedAttention, -# HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, -# NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, -# build_activation_layer, build_conv_layer, -# build_norm_layer, build_padding_layer, build_plugin_layer, -# build_upsample_layer, conv_ws_2d, is_norm) -from .builder import MODELS, build_model_from_cfg -# yapf: enable -from .resnet import ResNet, make_res_layer -from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, - NormalInit, PretrainedInit, TruncNormalInit, UniformInit, - XavierInit, bias_init_with_prob, caffe2_xavier_init, - constant_init, fuse_conv_bn, get_model_complexity_info, - initialize, kaiming_init, normal_init, trunc_normal_init, - uniform_init, xavier_init) -# from .vgg import VGG, make_vgg_layer - -# __all__ = [ -# 'AlexNet', 'VGG', 'make_vgg_layer', 'ResNet', 'make_res_layer', -# 'constant_init', 'xavier_init', 'normal_init', 'trunc_normal_init', -# 'uniform_init', 'kaiming_init', 'caffe2_xavier_init', -# 'bias_init_with_prob', 'ConvModule', 'build_activation_layer', -# 'build_conv_layer', 'build_norm_layer', 'build_padding_layer', -# 'build_upsample_layer', 'build_plugin_layer', 'is_norm', 'NonLocal1d', -# 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'HSigmoid', 'Swish', 'HSwish', -# 'GeneralizedAttention', 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', -# 'PADDING_LAYERS', 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', -# 'get_model_complexity_info', 'conv_ws_2d', 'ConvAWS2d', 'ConvWS2d', -# 'fuse_conv_bn', 'DepthwiseSeparableConvModule', 'Linear', 'Conv2d', -# 'ConvTranspose2d', 'MaxPool2d', 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', -# 'initialize', 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', -# 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', -# 'Caffe2XavierInit', 'MODELS', 'build_model_from_cfg' -# ] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/__init__.py deleted file mode 100755 index a012b5d56..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/__init__.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -from .activation import build_activation_layer -from .context_block import ContextBlock -from .conv import build_conv_layer -# from .conv2d_adaptive_padding import Conv2dAdaptivePadding -from .conv_module import ConvModule -# from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d -from .depthwise_separable_conv_module import DepthwiseSeparableConvModule -# from .drop import Dropout, DropPath -from .generalized_attention import GeneralizedAttention -# from .hsigmoid import HSigmoid -# from .hswish import HSwish -# from .non_local import NonLocal1d, NonLocal2d, NonLocal3d -from .norm import build_norm_layer, is_norm -from .padding import build_padding_layer -from .plugin import build_plugin_layer -from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) -from .scale import Scale -# from .swish import Swish -from .upsample import build_upsample_layer -from .wrappers import (Conv2d, ConvTranspose2d, - Linear, MaxPool2d) -# from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, -# Linear, MaxPool2d, MaxPool3d) -# __all__ = [ -# 'ConvModule', 'build_activation_layer', 'build_conv_layer', -# 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', -# 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', -# 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', -# 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', -# 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', -# 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', -# 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', -# 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' -# ] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/activation.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/activation.py deleted file mode 100755 index 79f198838..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/activation.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version -from .registry import ACTIVATION_LAYERS - -for module in [ - nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, - nn.Sigmoid, nn.Tanh -]: - ACTIVATION_LAYERS.register_module(module=module) - - -@ACTIVATION_LAYERS.register_module(name='Clip') -@ACTIVATION_LAYERS.register_module() -class Clamp(nn.Module): - """Clamp activation layer. - - This activation function is to clamp the feature map value within - :math:`[min, max]`. More details can be found in ``torch.clamp()``. - - Args: - min (Number | optional): Lower-bound of the range to be clamped to. - Default to -1. - max (Number | optional): Upper-bound of the range to be clamped to. - Default to 1. - """ - - def __init__(self, min=-1., max=1.): - super(Clamp, self).__init__() - self.min = min - self.max = max - - def forward(self, x): - """Forward function. - - Args: - x (torch.Tensor): The input tensor. - - Returns: - torch.Tensor: Clamped tensor. - """ - return torch.clamp(x, min=self.min, max=self.max) - - -class GELU(nn.Module): - r"""Applies the Gaussian Error Linear Units function: - - .. math:: - \text{GELU}(x) = x * \Phi(x) - where :math:`\Phi(x)` is the Cumulative Distribution Function for - Gaussian Distribution. - - Shape: - - Input: :math:`(N, *)` where `*` means, any number of additional - dimensions - - Output: :math:`(N, *)`, same shape as the input - - .. image:: scripts/activation_images/GELU.png - - Examples:: - - >>> m = nn.GELU() - >>> input = torch.randn(2) - >>> output = m(input) - """ - - def forward(self, input): - return F.gelu(input) - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.4')): - ACTIVATION_LAYERS.register_module(module=GELU) -else: - ACTIVATION_LAYERS.register_module(module=nn.GELU) - - -def build_activation_layer(cfg): - """Build activation layer. - - Args: - cfg (dict): The activation layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an activation layer. - - Returns: - nn.Module: Created activation layer. - """ - return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/context_block.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/context_block.py deleted file mode 100755 index d60fdb904..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/context_block.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn - -from ..utils import constant_init, kaiming_init -from .registry import PLUGIN_LAYERS - - -def last_zero_init(m): - if isinstance(m, nn.Sequential): - constant_init(m[-1], val=0) - else: - constant_init(m, val=0) - - -@PLUGIN_LAYERS.register_module() -class ContextBlock(nn.Module): - """ContextBlock module in GCNet. - - See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' - (https://arxiv.org/abs/1904.11492) for details. - - Args: - in_channels (int): Channels of the input feature map. - ratio (float): Ratio of channels of transform bottleneck - pooling_type (str): Pooling method for context modeling. - Options are 'att' and 'avg', stand for attention pooling and - average pooling respectively. Default: 'att'. - fusion_types (Sequence[str]): Fusion method for feature fusion, - Options are 'channels_add', 'channel_mul', stand for channelwise - addition and multiplication respectively. Default: ('channel_add',) - """ - - _abbr_ = 'context_block' - - def __init__(self, - in_channels, - ratio, - pooling_type='att', - fusion_types=('channel_add', )): - super(ContextBlock, self).__init__() - assert pooling_type in ['avg', 'att'] - assert isinstance(fusion_types, (list, tuple)) - valid_fusion_types = ['channel_add', 'channel_mul'] - assert all([f in valid_fusion_types for f in fusion_types]) - assert len(fusion_types) > 0, 'at least one fusion should be used' - self.in_channels = in_channels - self.ratio = ratio - self.planes = int(in_channels * ratio) - self.pooling_type = pooling_type - self.fusion_types = fusion_types - if pooling_type == 'att': - self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) - self.softmax = nn.Softmax(dim=2) - else: - self.avg_pool = nn.AdaptiveAvgPool2d(1) - if 'channel_add' in fusion_types: - self.channel_add_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_add_conv = None - if 'channel_mul' in fusion_types: - self.channel_mul_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_mul_conv = None - self.reset_parameters() - - def reset_parameters(self): - if self.pooling_type == 'att': - kaiming_init(self.conv_mask, mode='fan_in') - self.conv_mask.inited = True - - if self.channel_add_conv is not None: - last_zero_init(self.channel_add_conv) - if self.channel_mul_conv is not None: - last_zero_init(self.channel_mul_conv) - - def spatial_pool(self, x): - batch, channel, height, width = x.size() - if self.pooling_type == 'att': - input_x = x - # [N, C, H * W] - input_x = input_x.view(batch, channel, height * width) - # [N, 1, C, H * W] - input_x = input_x.unsqueeze(1) - # [N, 1, H, W] - context_mask = self.conv_mask(x) - # [N, 1, H * W] - context_mask = context_mask.view(batch, 1, height * width) - # [N, 1, H * W] - context_mask = self.softmax(context_mask) - # [N, 1, H * W, 1] - context_mask = context_mask.unsqueeze(-1) - # [N, 1, C, 1] - context = torch.matmul(input_x, context_mask) - # [N, C, 1, 1] - context = context.view(batch, channel, 1, 1) - else: - # [N, C, 1, 1] - context = self.avg_pool(x) - - return context - - def forward(self, x): - # [N, C, 1, 1] - context = self.spatial_pool(x) - - out = x - if self.channel_mul_conv is not None: - # [N, C, 1, 1] - channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) - out = out * channel_mul_term - if self.channel_add_conv is not None: - # [N, C, 1, 1] - channel_add_term = self.channel_add_conv(context) - out = out + channel_add_term - - return out diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/conv.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/conv.py deleted file mode 100755 index cf5449199..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/conv.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from .registry import CONV_LAYERS - -CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) -CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) -CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) -CONV_LAYERS.register_module('Conv', module=nn.Conv2d) - - -def build_conv_layer(cfg, *args, **kwargs): - """Build convolution layer. - - Args: - cfg (None or dict): The conv layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an conv layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding conv layer. - - Returns: - nn.Module: Created conv layer. - """ - if cfg is None: - cfg_ = dict(type='Conv2d') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in CONV_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - else: - conv_layer = CONV_LAYERS.get(layer_type) - - layer = conv_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/conv_module.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/conv_module.py deleted file mode 100755 index 4f19f1d0c..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/conv_module.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from mmcv.utils import _BatchNorm, _InstanceNorm -from ..utils import constant_init, kaiming_init -from .activation import build_activation_layer -from .conv import build_conv_layer -from .norm import build_norm_layer -from .padding import build_padding_layer -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class ConvModule(nn.Module): - """A conv block that bundles conv/norm/activation layers. - - This block simplifies the usage of convolution layers, which are commonly - used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). - It is based upon three build methods: `build_conv_layer()`, - `build_norm_layer()` and `build_activation_layer()`. - - Besides, we add some additional features in this module. - 1. Automatically set `bias` of the conv layer. - 2. Spectral norm is supported. - 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only - supports zero and circular padding, and we add "reflect" padding mode. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. - groups (int): Number of blocked connections from input channels to - output channels. Same as that in ``nn._ConvNd``. - bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - inplace (bool): Whether to use inplace mode for activation. - Default: True. - with_spectral_norm (bool): Whether use spectral norm in conv module. - Default: False. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in PyTorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Common examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - Default: ('conv', 'norm', 'act'). - """ - - _abbr_ = 'conv_block' - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias='auto', - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - padding_mode='zeros', - order=('conv', 'norm', 'act')): - super(ConvModule, self).__init__() - assert conv_cfg is None or isinstance(conv_cfg, dict) - assert norm_cfg is None or isinstance(norm_cfg, dict) - assert act_cfg is None or isinstance(act_cfg, dict) - official_padding_mode = ['zeros', 'circular'] - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.with_explicit_padding = padding_mode not in official_padding_mode - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 3 - assert set(order) == set(['conv', 'norm', 'act']) - - self.with_norm = norm_cfg is not None - self.with_activation = act_cfg is not None - # if the conv layer is before a norm layer, bias is unnecessary. - if bias == 'auto': - bias = not self.with_norm - self.with_bias = bias - - if self.with_explicit_padding: - pad_cfg = dict(type=padding_mode) - self.padding_layer = build_padding_layer(pad_cfg, padding) - - # reset padding to 0 for conv module - conv_padding = 0 if self.with_explicit_padding else padding - # build convolution layer - self.conv = build_conv_layer( - conv_cfg, - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=conv_padding, - dilation=dilation, - groups=groups, - bias=bias) - # export the attributes of self.conv to a higher level for convenience - self.in_channels = self.conv.in_channels - self.out_channels = self.conv.out_channels - self.kernel_size = self.conv.kernel_size - self.stride = self.conv.stride - self.padding = padding - self.dilation = self.conv.dilation - self.transposed = self.conv.transposed - self.output_padding = self.conv.output_padding - self.groups = self.conv.groups - - if self.with_spectral_norm: - self.conv = nn.utils.spectral_norm(self.conv) - - # build normalization layers - if self.with_norm: - # norm layer is after conv layer - if order.index('norm') > order.index('conv'): - norm_channels = out_channels - else: - norm_channels = in_channels - self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) - self.add_module(self.norm_name, norm) - if self.with_bias: - if isinstance(norm, (_BatchNorm, _InstanceNorm)): - warnings.warn( - 'Unnecessary conv bias before batch/instance norm') - else: - self.norm_name = None - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - # nn.Tanh has no 'inplace' argument - if act_cfg_['type'] not in [ - 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish' - ]: - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - @property - def norm(self): - if self.norm_name: - return getattr(self, self.norm_name) - else: - return None - - def init_weights(self): - # 1. It is mainly for customized conv layers with their own - # initialization manners by calling their own ``init_weights()``, - # and we do not want ConvModule to override the initialization. - # 2. For customized conv layers without their own initialization - # manners (that is, they don't have their own ``init_weights()``) - # and PyTorch's conv layers, they will be initialized by - # this method with default ``kaiming_init``. - # Note: For PyTorch's conv layers, they will be overwritten by our - # initialization implementation using default ``kaiming_init``. - if not hasattr(self.conv, 'init_weights'): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) - if self.with_norm: - constant_init(self.norm, 1, bias=0) - - def forward(self, x, activate=True, norm=True): - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - x = self.conv(x) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py deleted file mode 100755 index 722d5d8d7..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .conv_module import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if `norm_cfg` and `act_cfg` are specified. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. Default: 1. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. Default: 0. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super(DepthwiseSeparableConvModule, self).__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/generalized_attention.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/generalized_attention.py deleted file mode 100755 index 988d9adf2..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/generalized_attention.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import kaiming_init -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class GeneralizedAttention(nn.Module): - """GeneralizedAttention module. - - See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' - (https://arxiv.org/abs/1711.07971) for details. - - Args: - in_channels (int): Channels of the input feature map. - spatial_range (int): The spatial range. -1 indicates no spatial range - constraint. Default: -1. - num_heads (int): The head number of empirical_attention module. - Default: 9. - position_embedding_dim (int): The position embedding dimension. - Default: -1. - position_magnitude (int): A multiplier acting on coord difference. - Default: 1. - kv_stride (int): The feature stride acting on key/value feature map. - Default: 2. - q_stride (int): The feature stride acting on query feature map. - Default: 1. - attention_type (str): A binary indicator string for indicating which - items in generalized empirical_attention module are used. - Default: '1111'. - - - '1000' indicates 'query and key content' (appr - appr) item, - - '0100' indicates 'query content and relative position' - (appr - position) item, - - '0010' indicates 'key content only' (bias - appr) item, - - '0001' indicates 'relative position only' (bias - position) item. - """ - - _abbr_ = 'gen_attention_block' - - def __init__(self, - in_channels, - spatial_range=-1, - num_heads=9, - position_embedding_dim=-1, - position_magnitude=1, - kv_stride=2, - q_stride=1, - attention_type='1111'): - - super(GeneralizedAttention, self).__init__() - - # hard range means local range for non-local operation - self.position_embedding_dim = ( - position_embedding_dim - if position_embedding_dim > 0 else in_channels) - - self.position_magnitude = position_magnitude - self.num_heads = num_heads - self.in_channels = in_channels - self.spatial_range = spatial_range - self.kv_stride = kv_stride - self.q_stride = q_stride - self.attention_type = [bool(int(_)) for _ in attention_type] - self.qk_embed_dim = in_channels // num_heads - out_c = self.qk_embed_dim * num_heads - - if self.attention_type[0] or self.attention_type[1]: - self.query_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.query_conv.kaiming_init = True - - if self.attention_type[0] or self.attention_type[2]: - self.key_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.key_conv.kaiming_init = True - - self.v_dim = in_channels // num_heads - self.value_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=self.v_dim * num_heads, - kernel_size=1, - bias=False) - self.value_conv.kaiming_init = True - - if self.attention_type[1] or self.attention_type[3]: - self.appr_geom_fc_x = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_x.kaiming_init = True - - self.appr_geom_fc_y = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_y.kaiming_init = True - - if self.attention_type[2]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.appr_bias = nn.Parameter(appr_bias_value) - - if self.attention_type[3]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.geom_bias = nn.Parameter(geom_bias_value) - - self.proj_conv = nn.Conv2d( - in_channels=self.v_dim * num_heads, - out_channels=in_channels, - kernel_size=1, - bias=True) - self.proj_conv.kaiming_init = True - self.gamma = nn.Parameter(torch.zeros(1)) - - if self.spatial_range >= 0: - # only works when non local is after 3*3 conv - if in_channels == 256: - max_len = 84 - elif in_channels == 512: - max_len = 42 - - max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) - local_constraint_map = np.ones( - (max_len, max_len, max_len_kv, max_len_kv), dtype=np.int) - for iy in range(max_len): - for ix in range(max_len): - local_constraint_map[ - iy, ix, - max((iy - self.spatial_range) // - self.kv_stride, 0):min((iy + self.spatial_range + - 1) // self.kv_stride + - 1, max_len), - max((ix - self.spatial_range) // - self.kv_stride, 0):min((ix + self.spatial_range + - 1) // self.kv_stride + - 1, max_len)] = 0 - - self.local_constraint_map = nn.Parameter( - torch.from_numpy(local_constraint_map).byte(), - requires_grad=False) - - if self.q_stride > 1: - self.q_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.q_stride) - else: - self.q_downsample = None - - if self.kv_stride > 1: - self.kv_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.kv_stride) - else: - self.kv_downsample = None - - self.init_weights() - - def get_position_embedding(self, - h, - w, - h_kv, - w_kv, - q_stride, - kv_stride, - device, - dtype, - feat_dim, - wave_length=1000): - # the default type of Tensor is float32, leading to type mismatch - # in fp16 mode. Cast it to support fp16 mode. - h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) - h_idxs = h_idxs.view((h, 1)) * q_stride - - w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) - w_idxs = w_idxs.view((w, 1)) * q_stride - - h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( - device=device, dtype=dtype) - h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride - - w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( - device=device, dtype=dtype) - w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride - - # (h, h_kv, 1) - h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) - h_diff *= self.position_magnitude - - # (w, w_kv, 1) - w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) - w_diff *= self.position_magnitude - - feat_range = torch.arange(0, feat_dim / 4).to( - device=device, dtype=dtype) - - dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) - dim_mat = dim_mat**((4. / feat_dim) * feat_range) - dim_mat = dim_mat.view((1, 1, -1)) - - embedding_x = torch.cat( - ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) - - embedding_y = torch.cat( - ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) - - return embedding_x, embedding_y - - def forward(self, x_input): - num_heads = self.num_heads - - # use empirical_attention - if self.q_downsample is not None: - x_q = self.q_downsample(x_input) - else: - x_q = x_input - n, _, h, w = x_q.shape - - if self.kv_downsample is not None: - x_kv = self.kv_downsample(x_input) - else: - x_kv = x_input - _, _, h_kv, w_kv = x_kv.shape - - if self.attention_type[0] or self.attention_type[1]: - proj_query = self.query_conv(x_q).view( - (n, num_heads, self.qk_embed_dim, h * w)) - proj_query = proj_query.permute(0, 1, 3, 2) - - if self.attention_type[0] or self.attention_type[2]: - proj_key = self.key_conv(x_kv).view( - (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) - - if self.attention_type[1] or self.attention_type[3]: - position_embed_x, position_embed_y = self.get_position_embedding( - h, w, h_kv, w_kv, self.q_stride, self.kv_stride, - x_input.device, x_input.dtype, self.position_embedding_dim) - # (n, num_heads, w, w_kv, dim) - position_feat_x = self.appr_geom_fc_x(position_embed_x).\ - view(1, w, w_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - # (n, num_heads, h, h_kv, dim) - position_feat_y = self.appr_geom_fc_y(position_embed_y).\ - view(1, h, h_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - position_feat_x /= math.sqrt(2) - position_feat_y /= math.sqrt(2) - - # accelerate for saliency only - if (np.sum(self.attention_type) == 1) and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy = torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, h_kv * w_kv) - - h = 1 - w = 1 - else: - # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for - if not self.attention_type[0]: - energy = torch.zeros( - n, - num_heads, - h, - w, - h_kv, - w_kv, - dtype=x_input.dtype, - device=x_input.device) - - # attention_type[0]: appr - appr - # attention_type[1]: appr - position - # attention_type[2]: bias - appr - # attention_type[3]: bias - position - if self.attention_type[0] or self.attention_type[2]: - if self.attention_type[0] and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - energy = torch.matmul(proj_query + appr_bias, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[0]: - energy = torch.matmul(proj_query, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy += torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, 1, h_kv, w_kv) - - if self.attention_type[1] or self.attention_type[3]: - if self.attention_type[1] and self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - - proj_query_reshape = (proj_query + geom_bias).\ - view(n, num_heads, h, w, self.qk_embed_dim) - - energy_x = torch.matmul( - proj_query_reshape.permute(0, 1, 3, 2, 4), - position_feat_x.permute(0, 1, 2, 4, 3)) - energy_x = energy_x.\ - permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul( - proj_query_reshape, - position_feat_y.permute(0, 1, 2, 4, 3)) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[1]: - proj_query_reshape = proj_query.\ - view(n, num_heads, h, w, self.qk_embed_dim) - proj_query_reshape = proj_query_reshape.\ - permute(0, 1, 3, 2, 4) - position_feat_x_reshape = position_feat_x.\ - permute(0, 1, 2, 4, 3) - position_feat_y_reshape = position_feat_y.\ - permute(0, 1, 2, 4, 3) - - energy_x = torch.matmul(proj_query_reshape, - position_feat_x_reshape) - energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul(proj_query_reshape, - position_feat_y_reshape) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, self.qk_embed_dim, 1).\ - repeat(n, 1, 1, 1) - - position_feat_x_reshape = position_feat_x.\ - view(n, num_heads, w*w_kv, self.qk_embed_dim) - - position_feat_y_reshape = position_feat_y.\ - view(n, num_heads, h * h_kv, self.qk_embed_dim) - - energy_x = torch.matmul(position_feat_x_reshape, geom_bias) - energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) - - energy_y = torch.matmul(position_feat_y_reshape, geom_bias) - energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) - - energy += energy_x + energy_y - - energy = energy.view(n, num_heads, h * w, h_kv * w_kv) - - if self.spatial_range >= 0: - cur_local_constraint_map = \ - self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ - contiguous().\ - view(1, 1, h*w, h_kv*w_kv) - - energy = energy.masked_fill_(cur_local_constraint_map, - float('-inf')) - - attention = F.softmax(energy, 3) - - proj_value = self.value_conv(x_kv) - proj_value_reshape = proj_value.\ - view((n, num_heads, self.v_dim, h_kv * w_kv)).\ - permute(0, 1, 3, 2) - - out = torch.matmul(attention, proj_value_reshape).\ - permute(0, 1, 3, 2).\ - contiguous().\ - view(n, self.v_dim * self.num_heads, h, w) - - out = self.proj_conv(out) - - # output is downsampled, upsample back to input size - if self.q_downsample is not None: - out = F.interpolate( - out, - size=x_input.shape[2:], - mode='bilinear', - align_corners=False) - - out = self.gamma * out + x_input - return out - - def init_weights(self): - for m in self.modules(): - if hasattr(m, 'kaiming_init') and m.kaiming_init: - kaiming_init( - m, - mode='fan_in', - nonlinearity='leaky_relu', - bias=0, - distribution='uniform', - a=1) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/norm.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/norm.py deleted file mode 100755 index cfb326bdb..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/norm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import torch.nn as nn - -from mmcv.utils import is_tuple_of -from mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm -from .registry import NORM_LAYERS - -NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) -NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) -NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) -NORM_LAYERS.register_module('GN', module=nn.GroupNorm) -NORM_LAYERS.register_module('LN', module=nn.LayerNorm) -NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) -NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - When we build a norm layer with `build_norm_layer()`, we want to preserve - the norm type in variable names, e.g, self.bn1, self.gn. This method will - infer the abbreviation to map class types to abbreviations. - - Rule 1: If the class has the property "_abbr_", return the property. - Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or - InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and - "in" respectively. - Rule 3: If the class name contains "batch", "group", "layer" or "instance", - the abbreviation of this layer will be "bn", "gn", "ln" and "in" - respectively. - Rule 4: Otherwise, the abbreviation falls back to "norm". - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN - return 'in' - elif issubclass(class_type, _BatchNorm): - return 'bn' - elif issubclass(class_type, nn.GroupNorm): - return 'gn' - elif issubclass(class_type, nn.LayerNorm): - return 'ln' - else: - class_name = class_type.__name__.lower() - if 'batch' in class_name: - return 'bn' - elif 'group' in class_name: - return 'gn' - elif 'layer' in class_name: - return 'ln' - elif 'instance' in class_name: - return 'in' - else: - return 'norm_layer' - - -def build_norm_layer(cfg, num_features, postfix=''): - """Build normalization layer. - - Args: - cfg (dict): The norm layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate a norm layer. - - requires_grad (bool, optional): Whether stop gradient updates. - num_features (int): Number of input channels. - postfix (int | str): The postfix to be appended into norm abbreviation - to create named layer. - - Returns: - (str, nn.Module): The first element is the layer name consisting of - abbreviation and postfix, e.g., bn1, gn. The second element is the - created norm layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in NORM_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - - norm_layer = NORM_LAYERS.get(layer_type) - abbr = infer_abbr(norm_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - requires_grad = cfg_.pop('requires_grad', True) - cfg_.setdefault('eps', 1e-5) - if layer_type != 'GN': - layer = norm_layer(num_features, **cfg_) - if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): - layer._specify_ddp_gpu_num(1) - else: - assert 'num_groups' in cfg_ - layer = norm_layer(num_channels=num_features, **cfg_) - - for param in layer.parameters(): - param.requires_grad = requires_grad - - return name, layer - - -def is_norm(layer, exclude=None): - """Check if a layer is a normalization layer. - - Args: - layer (nn.Module): The layer to be checked. - exclude (type | tuple[type]): Types to be excluded. - - Returns: - bool: Whether the layer is a norm layer. - """ - if exclude is not None: - if not isinstance(exclude, tuple): - exclude = (exclude, ) - if not is_tuple_of(exclude, type): - raise TypeError( - f'"exclude" must be either None or type or a tuple of types, ' - f'but got {type(exclude)}: {exclude}') - - if exclude and isinstance(layer, exclude): - return False - - all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) - return isinstance(layer, all_norm_bases) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/padding.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/padding.py deleted file mode 100755 index e4ac6b28a..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/padding.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import PADDING_LAYERS - -PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) -PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) -PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) - - -def build_padding_layer(cfg, *args, **kwargs): - """Build padding layer. - - Args: - cfg (None or dict): The padding layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate a padding layer. - - Returns: - nn.Module: Created padding layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - - cfg_ = cfg.copy() - padding_type = cfg_.pop('type') - if padding_type not in PADDING_LAYERS: - raise KeyError(f'Unrecognized padding type {padding_type}.') - else: - padding_layer = PADDING_LAYERS.get(padding_type) - - layer = padding_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/plugin.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/plugin.py deleted file mode 100755 index 07c010d40..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/plugin.py +++ /dev/null @@ -1,88 +0,0 @@ -import inspect -import platform - -from .registry import PLUGIN_LAYERS - -if platform.system() == 'Windows': - import regex as re -else: - import re - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - This method will infer the abbreviation to map class types to - abbreviations. - - Rule 1: If the class has the property "abbr", return the property. - Rule 2: Otherwise, the abbreviation falls back to snake case of class - name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - - def camel2snack(word): - """Convert camel case word into snack case. - - Modified from `inflection lib - `_. - - Example:: - - >>> camel2snack("FancyBlock") - 'fancy_block' - """ - - word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) - word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) - word = word.replace('-', '_') - return word.lower() - - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - else: - return camel2snack(class_type.__name__) - - -def build_plugin_layer(cfg, postfix='', **kwargs): - """Build plugin layer. - - Args: - cfg (None or dict): cfg should contain: - type (str): identify plugin layer type. - layer args: args needed to instantiate a plugin layer. - postfix (int, str): appended into norm abbreviation to - create named layer. Default: ''. - - Returns: - tuple[str, nn.Module]: - name (str): abbreviation + postfix - layer (nn.Module): created plugin layer - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in PLUGIN_LAYERS: - raise KeyError(f'Unrecognized plugin type {layer_type}') - - plugin_layer = PLUGIN_LAYERS.get(layer_type) - abbr = infer_abbr(plugin_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - layer = plugin_layer(**kwargs, **cfg_) - - return name, layer diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/registry.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/registry.py deleted file mode 100755 index c29279776..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/registry.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -CONV_LAYERS = Registry('conv layer') -NORM_LAYERS = Registry('norm layer') -ACTIVATION_LAYERS = Registry('activation layer') -PADDING_LAYERS = Registry('padding layer') -UPSAMPLE_LAYERS = Registry('upsample layer') -PLUGIN_LAYERS = Registry('plugin layer') - -DROPOUT_LAYERS = Registry('drop out layers') -POSITIONAL_ENCODING = Registry('position encoding') -ATTENTION = Registry('attention') -FEEDFORWARD_NETWORK = Registry('feed-forward Network') -TRANSFORMER_LAYER = Registry('transformerLayer') -TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/scale.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/scale.py deleted file mode 100755 index c905fffcc..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/scale.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class Scale(nn.Module): - """A learnable scale parameter. - - This layer scales the input by a learnable factor. It multiplies a - learnable scale parameter of shape (1,) with input of any shape. - - Args: - scale (float): Initial value of scale factor. Default: 1.0 - """ - - def __init__(self, scale=1.0): - super(Scale, self).__init__() - self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) - - def forward(self, x): - return x * self.scale diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/transformer.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/transformer.py deleted file mode 100755 index ed32688af..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/transformer.py +++ /dev/null @@ -1,595 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings - -import torch -import torch.nn as nn - -from mmcv import ConfigDict, deprecated_api_warning -from mmcv.cnn import Linear, build_activation_layer, build_norm_layer -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential -from mmcv.utils import build_from_cfg -from .drop import build_dropout -from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, - TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) - -# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file -try: - from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention # noqa F401 - warnings.warn( - ImportWarning( - '``MultiScaleDeformableAttention`` has been moved to ' - '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 - '``from mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 - 'to ``from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 - )) - -except ImportError: - warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' - '``mmcv.ops.multi_scale_deform_attn``, ' - 'You should install ``mmcv-full`` if you need this module. ') - - -def build_positional_encoding(cfg, default_args=None): - """Builder for Position Encoding.""" - return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) - - -def build_attention(cfg, default_args=None): - """Builder for attention.""" - return build_from_cfg(cfg, ATTENTION, default_args) - - -def build_feedforward_network(cfg, default_args=None): - """Builder for feed-forward network (FFN).""" - return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) - - -def build_transformer_layer(cfg, default_args=None): - """Builder for transformer layer.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) - - -def build_transformer_layer_sequence(cfg, default_args=None): - """Builder for transformer encoder and transformer decoder.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) - - -@ATTENTION.register_module() -class MultiheadAttention(BaseModule): - """A wrapper for ``torch.nn.MultiheadAttention``. - - This module implements MultiheadAttention with identity connection, - and positional encoding is also passed as input. - - Args: - embed_dims (int): The embedding dimension. - num_heads (int): Parallel attention heads. - attn_drop (float): A Dropout layer on attn_output_weights. - Default: 0.0. - proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. - Default: 0.0. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): When it is True, Key, Query and Value are shape of - (batch, n, embed_dim), otherwise (n, batch, embed_dim). - Default to False. - """ - - def __init__(self, - embed_dims, - num_heads, - attn_drop=0., - proj_drop=0., - dropout_layer=dict(type='Dropout', drop_prob=0.), - init_cfg=None, - batch_first=False, - **kwargs): - super(MultiheadAttention, self).__init__(init_cfg) - if 'dropout' in kwargs: - warnings.warn('The arguments `dropout` in MultiheadAttention ' - 'has been deprecated, now you can separately ' - 'set `attn_drop`(float), proj_drop(float), ' - 'and `dropout_layer`(dict) ') - attn_drop = kwargs['dropout'] - dropout_layer['drop_prob'] = kwargs.pop('dropout') - - self.embed_dims = embed_dims - self.num_heads = num_heads - self.batch_first = batch_first - - self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, - **kwargs) - - self.proj_drop = nn.Dropout(proj_drop) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else nn.Identity() - - @deprecated_api_warning({'residual': 'identity'}, - cls_name='MultiheadAttention') - def forward(self, - query, - key=None, - value=None, - identity=None, - query_pos=None, - key_pos=None, - attn_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `MultiheadAttention`. - - **kwargs allow passing a more general data flow when combining - with other operations in `transformerlayer`. - - Args: - query (Tensor): The input query with shape [num_queries, bs, - embed_dims] if self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - If None, the ``query`` will be used. Defaults to None. - value (Tensor): The value tensor with same shape as `key`. - Same in `nn.MultiheadAttention.forward`. Defaults to None. - If None, the `key` will be used. - identity (Tensor): This tensor, with the same shape as x, - will be used for the identity link. - If None, `x` will be used. Defaults to None. - query_pos (Tensor): The positional encoding for query, with - the same shape as `x`. If not None, it will - be added to `x` before forward function. Defaults to None. - key_pos (Tensor): The positional encoding for `key`, with the - same shape as `key`. Defaults to None. If not None, it will - be added to `key` before forward function. If None, and - `query_pos` has the same shape as `key`, then `query_pos` - will be used for `key_pos`. Defaults to None. - attn_mask (Tensor): ByteTensor mask with shape [num_queries, - num_keys]. Same in `nn.MultiheadAttention.forward`. - Defaults to None. - key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. - Defaults to None. - - Returns: - Tensor: forwarded results with shape - [num_queries, bs, embed_dims] - if self.batch_first is False, else - [bs, num_queries embed_dims]. - """ - - if key is None: - key = query - if value is None: - value = key - if identity is None: - identity = query - if key_pos is None: - if query_pos is not None: - # use query_pos if key_pos is not available - if query_pos.shape == key.shape: - key_pos = query_pos - else: - warnings.warn(f'position encoding of key is' - f'missing in {self.__class__.__name__}.') - if query_pos is not None: - query = query + query_pos - if key_pos is not None: - key = key + key_pos - - # Because the dataflow('key', 'query', 'value') of - # ``torch.nn.MultiheadAttention`` is (num_query, batch, - # embed_dims), We should adjust the shape of dataflow from - # batch_first (batch, num_query, embed_dims) to num_query_first - # (num_query ,batch, embed_dims), and recover ``attn_output`` - # from num_query_first to batch_first. - if self.batch_first: - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - out = self.attn( - query=query, - key=key, - value=value, - attn_mask=attn_mask, - key_padding_mask=key_padding_mask)[0] - - if self.batch_first: - out = out.transpose(0, 1) - - return identity + self.dropout_layer(self.proj_drop(out)) - - -@FEEDFORWARD_NETWORK.register_module() -class FFN(BaseModule): - """Implements feed-forward networks (FFNs) with identity connection. - - Args: - embed_dims (int): The feature dimension. Same as - `MultiheadAttention`. Defaults: 256. - feedforward_channels (int): The hidden dimension of FFNs. - Defaults: 1024. - num_fcs (int, optional): The number of fully-connected layers in - FFNs. Default: 2. - act_cfg (dict, optional): The activation config for FFNs. - Default: dict(type='ReLU') - ffn_drop (float, optional): Probability of an element to be - zeroed in FFN. Default 0.0. - add_identity (bool, optional): Whether to add the - identity connection. Default: `True`. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - @deprecated_api_warning( - { - 'dropout': 'ffn_drop', - 'add_residual': 'add_identity' - }, - cls_name='FFN') - def __init__(self, - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - act_cfg=dict(type='ReLU', inplace=True), - ffn_drop=0., - dropout_layer=None, - add_identity=True, - init_cfg=None, - **kwargs): - super(FFN, self).__init__(init_cfg) - assert num_fcs >= 2, 'num_fcs should be no less ' \ - f'than 2. got {num_fcs}.' - self.embed_dims = embed_dims - self.feedforward_channels = feedforward_channels - self.num_fcs = num_fcs - self.act_cfg = act_cfg - self.activate = build_activation_layer(act_cfg) - - layers = [] - in_channels = embed_dims - for _ in range(num_fcs - 1): - layers.append( - Sequential( - Linear(in_channels, feedforward_channels), self.activate, - nn.Dropout(ffn_drop))) - in_channels = feedforward_channels - layers.append(Linear(feedforward_channels, embed_dims)) - layers.append(nn.Dropout(ffn_drop)) - self.layers = Sequential(*layers) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else torch.nn.Identity() - self.add_identity = add_identity - - @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') - def forward(self, x, identity=None): - """Forward function for `FFN`. - - The function would add x to the output tensor if residue is None. - """ - out = self.layers(x) - if not self.add_identity: - return self.dropout_layer(out) - if identity is None: - identity = x - return identity + self.dropout_layer(out) - - -@TRANSFORMER_LAYER.register_module() -class BaseTransformerLayer(BaseModule): - """Base `TransformerLayer` for vision transformer. - - It can be built from `mmcv.ConfigDict` and support more flexible - customization, for example, using any number of `FFN or LN ` and - use different kinds of `attention` by specifying a list of `ConfigDict` - named `attn_cfgs`. It is worth mentioning that it supports `prenorm` - when you specifying `norm` as the first element of `operation_order`. - More details about the `prenorm`: `On Layer Normalization in the - Transformer Architecture `_ . - - Args: - attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for `self_attention` or `cross_attention` modules, - The order of the configs in the list should be consistent with - corresponding attentions in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. Default: None. - ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for FFN, The order of the configs in the list should be - consistent with corresponding ffn in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. - operation_order (tuple[str]): The execution order of operation - in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). - Support `prenorm` when you specifying first element as `norm`. - Default:None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): Key, Query and Value are shape - of (batch, n, embed_dim) - or (n, batch, embed_dim). Default to False. - """ - - def __init__(self, - attn_cfgs=None, - ffn_cfgs=dict( - type='FFN', - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - ffn_drop=0., - act_cfg=dict(type='ReLU', inplace=True), - ), - operation_order=None, - norm_cfg=dict(type='LN'), - init_cfg=None, - batch_first=False, - **kwargs): - - deprecated_args = dict( - feedforward_channels='feedforward_channels', - ffn_dropout='ffn_drop', - ffn_num_fcs='num_fcs') - for ori_name, new_name in deprecated_args.items(): - if ori_name in kwargs: - warnings.warn( - f'The arguments `{ori_name}` in BaseTransformerLayer ' - f'has been deprecated, now you should set `{new_name}` ' - f'and other FFN related arguments ' - f'to a dict named `ffn_cfgs`. ') - ffn_cfgs[new_name] = kwargs[ori_name] - - super(BaseTransformerLayer, self).__init__(init_cfg) - - self.batch_first = batch_first - - assert set(operation_order) & set( - ['self_attn', 'norm', 'ffn', 'cross_attn']) == \ - set(operation_order), f'The operation_order of' \ - f' {self.__class__.__name__} should ' \ - f'contains all four operation type ' \ - f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" - - num_attn = operation_order.count('self_attn') + operation_order.count( - 'cross_attn') - if isinstance(attn_cfgs, dict): - attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] - else: - assert num_attn == len(attn_cfgs), f'The length ' \ - f'of attn_cfg {num_attn} is ' \ - f'not consistent with the number of attention' \ - f'in operation_order {operation_order}.' - - self.num_attn = num_attn - self.operation_order = operation_order - self.norm_cfg = norm_cfg - self.pre_norm = operation_order[0] == 'norm' - self.attentions = ModuleList() - - index = 0 - for operation_name in operation_order: - if operation_name in ['self_attn', 'cross_attn']: - if 'batch_first' in attn_cfgs[index]: - assert self.batch_first == attn_cfgs[index]['batch_first'] - else: - attn_cfgs[index]['batch_first'] = self.batch_first - attention = build_attention(attn_cfgs[index]) - # Some custom attentions used as `self_attn` - # or `cross_attn` can have different behavior. - attention.operation_name = operation_name - self.attentions.append(attention) - index += 1 - - self.embed_dims = self.attentions[0].embed_dims - - self.ffns = ModuleList() - num_ffns = operation_order.count('ffn') - if isinstance(ffn_cfgs, dict): - ffn_cfgs = ConfigDict(ffn_cfgs) - if isinstance(ffn_cfgs, dict): - ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] - assert len(ffn_cfgs) == num_ffns - for ffn_index in range(num_ffns): - if 'embed_dims' not in ffn_cfgs[ffn_index]: - ffn_cfgs['embed_dims'] = self.embed_dims - else: - assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims - self.ffns.append( - build_feedforward_network(ffn_cfgs[ffn_index], - dict(type='FFN'))) - - self.norms = ModuleList() - num_norms = operation_order.count('norm') - for _ in range(num_norms): - self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) - - def forward(self, - query, - key=None, - value=None, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerDecoderLayer`. - - **kwargs contains some specific arguments of attentions. - - Args: - query (Tensor): The input query with shape - [num_queries, bs, embed_dims] if - self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - value (Tensor): The value tensor with same shape as `key`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor] | None): 2D Tensor used in - calculation of corresponding attention. The length of - it should equal to the number of `attention` in - `operation_order`. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in `self_attn` layer. - Defaults to None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: forwarded results with shape [num_queries, bs, embed_dims]. - """ - - norm_index = 0 - attn_index = 0 - ffn_index = 0 - identity = query - if attn_masks is None: - attn_masks = [None for _ in range(self.num_attn)] - elif isinstance(attn_masks, torch.Tensor): - attn_masks = [ - copy.deepcopy(attn_masks) for _ in range(self.num_attn) - ] - warnings.warn(f'Use same attn_mask in all attentions in ' - f'{self.__class__.__name__} ') - else: - assert len(attn_masks) == self.num_attn, f'The length of ' \ - f'attn_masks {len(attn_masks)} must be equal ' \ - f'to the number of attention in ' \ - f'operation_order {self.num_attn}' - - for layer in self.operation_order: - if layer == 'self_attn': - temp_key = temp_value = query - query = self.attentions[attn_index]( - query, - temp_key, - temp_value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=query_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=query_key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'norm': - query = self.norms[norm_index](query) - norm_index += 1 - - elif layer == 'cross_attn': - query = self.attentions[attn_index]( - query, - key, - value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=key_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'ffn': - query = self.ffns[ffn_index]( - query, identity if self.pre_norm else None) - ffn_index += 1 - - return query - - -@TRANSFORMER_LAYER_SEQUENCE.register_module() -class TransformerLayerSequence(BaseModule): - """Base class for TransformerEncoder and TransformerDecoder in vision - transformer. - - As base-class of Encoder and Decoder in vision transformer. - Support customization such as specifying different kind - of `transformer_layer` in `transformer_coder`. - - Args: - transformerlayer (list[obj:`mmcv.ConfigDict`] | - obj:`mmcv.ConfigDict`): Config of transformerlayer - in TransformerCoder. If it is obj:`mmcv.ConfigDict`, - it would be repeated `num_layer` times to a - list[`mmcv.ConfigDict`]. Default: None. - num_layers (int): The number of `TransformerLayer`. Default: None. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): - super(TransformerLayerSequence, self).__init__(init_cfg) - if isinstance(transformerlayers, dict): - transformerlayers = [ - copy.deepcopy(transformerlayers) for _ in range(num_layers) - ] - else: - assert isinstance(transformerlayers, list) and \ - len(transformerlayers) == num_layers - self.num_layers = num_layers - self.layers = ModuleList() - for i in range(num_layers): - self.layers.append(build_transformer_layer(transformerlayers[i])) - self.embed_dims = self.layers[0].embed_dims - self.pre_norm = self.layers[0].pre_norm - - def forward(self, - query, - key, - value, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerCoder`. - - Args: - query (Tensor): Input query with shape - `(num_queries, bs, embed_dims)`. - key (Tensor): The key tensor with shape - `(num_keys, bs, embed_dims)`. - value (Tensor): The value tensor with shape - `(num_keys, bs, embed_dims)`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor], optional): Each element is 2D Tensor - which is used in calculation of corresponding attention in - operation_order. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in self-attention - Default: None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: results with shape [num_queries, bs, embed_dims]. - """ - for layer in self.layers: - query = layer( - query, - key, - value, - query_pos=query_pos, - key_pos=key_pos, - attn_masks=attn_masks, - query_key_padding_mask=query_key_padding_mask, - key_padding_mask=key_padding_mask, - **kwargs) - return query diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/upsample.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/upsample.py deleted file mode 100755 index a1a353767..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/upsample.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import xavier_init -from .registry import UPSAMPLE_LAYERS - -UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) -UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) - - -@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') -class PixelShufflePack(nn.Module): - """Pixel Shuffle upsample layer. - - This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to - achieve a simple upsampling with pixel shuffle. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of the conv layer to expand the - channels. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super(PixelShufflePack, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - xavier_init(self.upsample_conv, distribution='uniform') - - def forward(self, x): - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x - - -def build_upsample_layer(cfg, *args, **kwargs): - """Build upsample layer. - - Args: - cfg (dict): The upsample layer config, which should contain: - - - type (str): Layer type. - - scale_factor (int): Upsample ratio, which is not applicable to - deconv. - - layer args: Args needed to instantiate a upsample layer. - args (argument list): Arguments passed to the ``__init__`` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the - ``__init__`` method of the corresponding conv layer. - - Returns: - nn.Module: Created upsample layer. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - raise KeyError( - f'the cfg dict must contain the key "type", but got {cfg}') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in UPSAMPLE_LAYERS: - raise KeyError(f'Unrecognized upsample type {layer_type}') - else: - upsample = UPSAMPLE_LAYERS.get(layer_type) - - if upsample is nn.Upsample: - cfg_['mode'] = layer_type - layer = upsample(*args, **kwargs, **cfg_) - return layer diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/wrappers.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/wrappers.py deleted file mode 100755 index 8aebf67bf..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/bricks/wrappers.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 - -Wrap some nn modules to support empty tensor input. Currently, these wrappers -are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask -heads are trained on only positive RoIs. -""" -import math - -import torch -import torch.nn as nn -from torch.nn.modules.utils import _pair, _triple - -from .registry import CONV_LAYERS, UPSAMPLE_LAYERS - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def obsolete_torch_version(torch_version, version_threshold): - return torch_version == 'parrots' or torch_version <= version_threshold - - -class NewEmptyTensorOp(torch.autograd.Function): - - @staticmethod - def forward(ctx, x, new_shape): - ctx.shape = x.shape - return x.new_empty(new_shape) - - @staticmethod - def backward(ctx, grad): - shape = ctx.shape - return NewEmptyTensorOp.apply(grad, shape), None - - -@CONV_LAYERS.register_module('Conv', force=True) -class Conv2d(nn.Conv2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module('Conv3d', force=True) -class Conv3d(nn.Conv3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv') -@UPSAMPLE_LAYERS.register_module('deconv', force=True) -class ConvTranspose2d(nn.ConvTranspose2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv3d') -@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) -class ConvTranspose3d(nn.ConvTranspose3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -class MaxPool2d(nn.MaxPool2d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), - _pair(self.padding), _pair(self.stride), - _pair(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class MaxPool3d(nn.MaxPool3d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), - _triple(self.padding), - _triple(self.stride), - _triple(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class Linear(torch.nn.Linear): - - def forward(self, x): - # empty tensor forward of Linear layer is supported in Pytorch 1.6 - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): - out_shape = [x.shape[0], self.out_features] - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/builder.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/builder.py deleted file mode 100755 index 7567316c5..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/builder.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..runner import Sequential -from ..utils import Registry, build_from_cfg - - -def build_model_from_cfg(cfg, registry, default_args=None): - """Build a PyTorch model from config dict(s). Different from - ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. - - Args: - cfg (dict, list[dict]): The config of modules, is is either a config - dict or a list of config dicts. If cfg is a list, a - the built modules will be wrapped with ``nn.Sequential``. - registry (:obj:`Registry`): A registry the module belongs to. - default_args (dict, optional): Default arguments to build the module. - Defaults to None. - - Returns: - nn.Module: A built nn module. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return Sequential(*modules) - else: - return build_from_cfg(cfg, registry, default_args) - - -MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/resnet.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/resnet.py deleted file mode 100755 index 1cb3ac057..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/resnet.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn -import torch.utils.checkpoint as cp - -from .utils import constant_init, kaiming_init - - -def conv3x3(in_planes, out_planes, stride=1, dilation=1): - """3x3 convolution with padding.""" - return nn.Conv2d( - in_planes, - out_planes, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False): - super(BasicBlock, self).__init__() - assert style in ['pytorch', 'caffe'] - self.conv1 = conv3x3(inplanes, planes, stride, dilation) - self.bn1 = nn.BatchNorm2d(planes) - self.relu = nn.ReLU(inplace=True) - self.conv2 = conv3x3(planes, planes) - self.bn2 = nn.BatchNorm2d(planes) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - assert not with_cp - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Bottleneck(nn.Module): - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False): - """Bottleneck block. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if - it is "caffe", the stride-two layer is the first 1x1 conv layer. - """ - super(Bottleneck, self).__init__() - assert style in ['pytorch', 'caffe'] - if style == 'pytorch': - conv1_stride = 1 - conv2_stride = stride - else: - conv1_stride = stride - conv2_stride = 1 - self.conv1 = nn.Conv2d( - inplanes, planes, kernel_size=1, stride=conv1_stride, bias=False) - self.conv2 = nn.Conv2d( - planes, - planes, - kernel_size=3, - stride=conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.bn1 = nn.BatchNorm2d(planes) - self.bn2 = nn.BatchNorm2d(planes) - self.conv3 = nn.Conv2d( - planes, planes * self.expansion, kernel_size=1, bias=False) - self.bn3 = nn.BatchNorm2d(planes * self.expansion) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - def forward(self, x): - - def _inner_forward(x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -def make_res_layer(block, - inplanes, - planes, - blocks, - stride=1, - dilation=1, - style='pytorch', - with_cp=False): - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = nn.Sequential( - nn.Conv2d( - inplanes, - planes * block.expansion, - kernel_size=1, - stride=stride, - bias=False), - nn.BatchNorm2d(planes * block.expansion), - ) - - layers = [] - layers.append( - block( - inplanes, - planes, - stride, - dilation, - downsample, - style=style, - with_cp=with_cp)) - inplanes = planes * block.expansion - for _ in range(1, blocks): - layers.append( - block(inplanes, planes, 1, dilation, style=style, with_cp=with_cp)) - - return nn.Sequential(*layers) - - -class ResNet(nn.Module): - """ResNet backbone. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - num_stages (int): Resnet stages, normally 4. - strides (Sequence[int]): Strides of the first block of each stage. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze - running stats (mean and var). - bn_frozen (bool): Whether to freeze weight and bias of BN layers. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - frozen_stages=-1, - bn_eval=True, - bn_frozen=False, - with_cp=False): - super(ResNet, self).__init__() - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - assert num_stages >= 1 and num_stages <= 4 - block, stage_blocks = self.arch_settings[depth] - stage_blocks = stage_blocks[:num_stages] - assert len(strides) == len(dilations) == num_stages - assert max(out_indices) < num_stages - - self.out_indices = out_indices - self.style = style - self.frozen_stages = frozen_stages - self.bn_eval = bn_eval - self.bn_frozen = bn_frozen - self.with_cp = with_cp - - self.inplanes = 64 - self.conv1 = nn.Conv2d( - 3, 64, kernel_size=7, stride=2, padding=3, bias=False) - self.bn1 = nn.BatchNorm2d(64) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - self.res_layers = [] - for i, num_blocks in enumerate(stage_blocks): - stride = strides[i] - dilation = dilations[i] - planes = 64 * 2**i - res_layer = make_res_layer( - block, - self.inplanes, - planes, - num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - with_cp=with_cp) - self.inplanes = planes * block.expansion - layer_name = f'layer{i + 1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self.feat_dim = block.expansion * 64 * 2**(len(stage_blocks) - 1) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m) - elif isinstance(m, nn.BatchNorm2d): - constant_init(m, 1) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - if len(outs) == 1: - return outs[0] - else: - return tuple(outs) - - def train(self, mode=True): - super(ResNet, self).train(mode) - if self.bn_eval: - for m in self.modules(): - if isinstance(m, nn.BatchNorm2d): - m.eval() - if self.bn_frozen: - for params in m.parameters(): - params.requires_grad = False - if mode and self.frozen_stages >= 0: - for param in self.conv1.parameters(): - param.requires_grad = False - for param in self.bn1.parameters(): - param.requires_grad = False - self.bn1.eval() - self.bn1.weight.requires_grad = False - self.bn1.bias.requires_grad = False - for i in range(1, self.frozen_stages + 1): - mod = getattr(self, f'layer{i}') - mod.eval() - for param in mod.parameters(): - param.requires_grad = False diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/__init__.py deleted file mode 100755 index a263e31c1..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .flops_counter import get_model_complexity_info -from .fuse_conv_bn import fuse_conv_bn -from .sync_bn import revert_sync_batchnorm -from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, - KaimingInit, NormalInit, PretrainedInit, - TruncNormalInit, UniformInit, XavierInit, - bias_init_with_prob, caffe2_xavier_init, - constant_init, initialize, kaiming_init, normal_init, - trunc_normal_init, uniform_init, xavier_init) - -__all__ = [ - 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', - 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', - 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'revert_sync_batchnorm' -] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/flops_counter.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/flops_counter.py deleted file mode 100755 index dceeb398b..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/flops_counter.py +++ /dev/null @@ -1,599 +0,0 @@ -# Modified from flops-counter.pytorch by Vladislav Sovrasov -# original repo: https://github.com/sovrasov/flops-counter.pytorch - -# MIT License - -# Copyright (c) 2018 Vladislav Sovrasov - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -from functools import partial - -import numpy as np -import torch -import torch.nn as nn - -import mmcv - - -def get_model_complexity_info(model, - input_shape, - print_per_layer_stat=True, - as_strings=True, - input_constructor=None, - flush=False, - ost=sys.stdout): - """Get complexity information of a model. - - This method can calculate FLOPs and parameter counts of a model with - corresponding input shape. It can also print complexity information for - each layer in a model. - - Supported layers are listed as below: - - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. - - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, ``nn.LeakyReLU``, - ``nn.ReLU6``. - - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, - ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, - ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, - ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, - ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. - - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, - ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, - ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. - - Linear: ``nn.Linear``. - - Deconvolution: ``nn.ConvTranspose2d``. - - Upsample: ``nn.Upsample``. - - Args: - model (nn.Module): The model for complexity calculation. - input_shape (tuple): Input shape used for calculation. - print_per_layer_stat (bool): Whether to print complexity information - for each layer in a model. Default: True. - as_strings (bool): Output FLOPs and params counts in a string form. - Default: True. - input_constructor (None | callable): If specified, it takes a callable - method that generates input. otherwise, it will generate a random - tensor with input shape to calculate FLOPs. Default: None. - flush (bool): same as that in :func:`print`. Default: False. - ost (stream): same as ``file`` param in :func:`print`. - Default: sys.stdout. - - Returns: - tuple[float | str]: If ``as_strings`` is set to True, it will return - FLOPs and parameter counts in a string format. otherwise, it will - return those in a float number format. - """ - assert type(input_shape) is tuple - assert len(input_shape) >= 1 - assert isinstance(model, nn.Module) - flops_model = add_flops_counting_methods(model) - flops_model.eval() - flops_model.start_flops_count() - if input_constructor: - input = input_constructor(input_shape) - _ = flops_model(**input) - else: - try: - batch = torch.ones(()).new_empty( - (1, *input_shape), - dtype=next(flops_model.parameters()).dtype, - device=next(flops_model.parameters()).device) - except StopIteration: - # Avoid StopIteration for models which have no parameters, - # like `nn.Relu()`, `nn.AvgPool2d`, etc. - batch = torch.ones(()).new_empty((1, *input_shape)) - - _ = flops_model(batch) - - flops_count, params_count = flops_model.compute_average_flops_cost() - if print_per_layer_stat: - print_model_with_flops( - flops_model, flops_count, params_count, ost=ost, flush=flush) - flops_model.stop_flops_count() - - if as_strings: - return flops_to_string(flops_count), params_to_string(params_count) - - return flops_count, params_count - - -def flops_to_string(flops, units='GFLOPs', precision=2): - """Convert FLOPs number into a string. - - Note that Here we take a multiply-add counts as one FLOP. - - Args: - flops (float): FLOPs number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', - 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically - choose the most suitable unit for FLOPs. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted FLOPs number with units. - - Examples: - >>> flops_to_string(1e9) - '1.0 GFLOPs' - >>> flops_to_string(2e5, 'MFLOPs') - '0.2 MFLOPs' - >>> flops_to_string(3e-9, None) - '3e-09 FLOPs' - """ - if units is None: - if flops // 10**9 > 0: - return str(round(flops / 10.**9, precision)) + ' GFLOPs' - elif flops // 10**6 > 0: - return str(round(flops / 10.**6, precision)) + ' MFLOPs' - elif flops // 10**3 > 0: - return str(round(flops / 10.**3, precision)) + ' KFLOPs' - else: - return str(flops) + ' FLOPs' - else: - if units == 'GFLOPs': - return str(round(flops / 10.**9, precision)) + ' ' + units - elif units == 'MFLOPs': - return str(round(flops / 10.**6, precision)) + ' ' + units - elif units == 'KFLOPs': - return str(round(flops / 10.**3, precision)) + ' ' + units - else: - return str(flops) + ' FLOPs' - - -def params_to_string(num_params, units=None, precision=2): - """Convert parameter number into a string. - - Args: - num_params (float): Parameter number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'M', - 'K' and ''. If set to None, it will automatically choose the most - suitable unit for Parameter number. Default: None. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted parameter number with units. - - Examples: - >>> params_to_string(1e9) - '1000.0 M' - >>> params_to_string(2e5) - '200.0 k' - >>> params_to_string(3e-9) - '3e-09' - """ - if units is None: - if num_params // 10**6 > 0: - return str(round(num_params / 10**6, precision)) + ' M' - elif num_params // 10**3: - return str(round(num_params / 10**3, precision)) + ' k' - else: - return str(num_params) - else: - if units == 'M': - return str(round(num_params / 10.**6, precision)) + ' ' + units - elif units == 'K': - return str(round(num_params / 10.**3, precision)) + ' ' + units - else: - return str(num_params) - - -def print_model_with_flops(model, - total_flops, - total_params, - units='GFLOPs', - precision=3, - ost=sys.stdout, - flush=False): - """Print a model with FLOPs for each layer. - - Args: - model (nn.Module): The model to be printed. - total_flops (float): Total FLOPs of the model. - total_params (float): Total parameter counts of the model. - units (str | None): Converted FLOPs units. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 3. - ost (stream): same as `file` param in :func:`print`. - Default: sys.stdout. - flush (bool): same as that in :func:`print`. Default: False. - - Example: - >>> class ExampleModel(nn.Module): - - >>> def __init__(self): - >>> super().__init__() - >>> self.conv1 = nn.Conv2d(3, 8, 3) - >>> self.conv2 = nn.Conv2d(8, 256, 3) - >>> self.conv3 = nn.Conv2d(256, 8, 3) - >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) - >>> self.flatten = nn.Flatten() - >>> self.fc = nn.Linear(8, 1) - - >>> def forward(self, x): - >>> x = self.conv1(x) - >>> x = self.conv2(x) - >>> x = self.conv3(x) - >>> x = self.avg_pool(x) - >>> x = self.flatten(x) - >>> x = self.fc(x) - >>> return x - - >>> model = ExampleModel() - >>> x = (3, 16, 16) - to print the complexity information state for each layer, you can use - >>> get_model_complexity_info(model, x) - or directly use - >>> print_model_with_flops(model, 4579784.0, 37361) - ExampleModel( - 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, - (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 - (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) - (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) - (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) - (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) - (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) - ) - """ - - def accumulate_params(self): - if is_supported_instance(self): - return self.__params__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_params() - return sum - - def accumulate_flops(self): - if is_supported_instance(self): - return self.__flops__ / model.__batch_counter__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_flops() - return sum - - def flops_repr(self): - accumulated_num_params = self.accumulate_params() - accumulated_flops_cost = self.accumulate_flops() - return ', '.join([ - params_to_string( - accumulated_num_params, units='M', precision=precision), - '{:.3%} Params'.format(accumulated_num_params / total_params), - flops_to_string( - accumulated_flops_cost, units=units, precision=precision), - '{:.3%} FLOPs'.format(accumulated_flops_cost / total_flops), - self.original_extra_repr() - ]) - - def add_extra_repr(m): - m.accumulate_flops = accumulate_flops.__get__(m) - m.accumulate_params = accumulate_params.__get__(m) - flops_extra_repr = flops_repr.__get__(m) - if m.extra_repr != flops_extra_repr: - m.original_extra_repr = m.extra_repr - m.extra_repr = flops_extra_repr - assert m.extra_repr != m.original_extra_repr - - def del_extra_repr(m): - if hasattr(m, 'original_extra_repr'): - m.extra_repr = m.original_extra_repr - del m.original_extra_repr - if hasattr(m, 'accumulate_flops'): - del m.accumulate_flops - - model.apply(add_extra_repr) - print(model, file=ost, flush=flush) - model.apply(del_extra_repr) - - -def get_model_parameters_number(model): - """Calculate parameter number of a model. - - Args: - model (nn.module): The model for parameter number calculation. - - Returns: - float: Parameter number of the model. - """ - num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - return num_params - - -def add_flops_counting_methods(net_main_module): - # adding additional methods to the existing module object, - # this is done this way so that each function has access to self object - net_main_module.start_flops_count = start_flops_count.__get__( - net_main_module) - net_main_module.stop_flops_count = stop_flops_count.__get__( - net_main_module) - net_main_module.reset_flops_count = reset_flops_count.__get__( - net_main_module) - net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # noqa: E501 - net_main_module) - - net_main_module.reset_flops_count() - - return net_main_module - - -def compute_average_flops_cost(self): - """Compute average FLOPs cost. - - A method to compute average FLOPs cost, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - - Returns: - float: Current mean flops consumption per image. - """ - batches_count = self.__batch_counter__ - flops_sum = 0 - for module in self.modules(): - if is_supported_instance(module): - flops_sum += module.__flops__ - params_sum = get_model_parameters_number(self) - return flops_sum / batches_count, params_sum - - -def start_flops_count(self): - """Activate the computation of mean flops consumption per image. - - A method to activate the computation of mean flops consumption per image. - which will be available after ``add_flops_counting_methods()`` is called on - a desired net object. It should be called before running the network. - """ - add_batch_counter_hook_function(self) - - def add_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - return - - else: - handle = module.register_forward_hook( - get_modules_mapping()[type(module)]) - - module.__flops_handle__ = handle - - self.apply(partial(add_flops_counter_hook_function)) - - -def stop_flops_count(self): - """Stop computing the mean flops consumption per image. - - A method to stop computing the mean flops consumption per image, which will - be available after ``add_flops_counting_methods()`` is called on a desired - net object. It can be called to pause the computation whenever. - """ - remove_batch_counter_hook_function(self) - self.apply(remove_flops_counter_hook_function) - - -def reset_flops_count(self): - """Reset statistics computed so far. - - A method to Reset computed statistics, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - """ - add_batch_counter_variables_or_reset(self) - self.apply(add_flops_counter_variable_or_reset) - - -# ---- Internal functions -def empty_flops_counter_hook(module, input, output): - module.__flops__ += 0 - - -def upsample_flops_counter_hook(module, input, output): - output_size = output[0] - batch_size = output_size.shape[0] - output_elements_count = batch_size - for val in output_size.shape[1:]: - output_elements_count *= val - module.__flops__ += int(output_elements_count) - - -def relu_flops_counter_hook(module, input, output): - active_elements_count = output.numel() - module.__flops__ += int(active_elements_count) - - -def linear_flops_counter_hook(module, input, output): - input = input[0] - output_last_dim = output.shape[ - -1] # pytorch checks dimensions, so here we don't care much - module.__flops__ += int(np.prod(input.shape) * output_last_dim) - - -def pool_flops_counter_hook(module, input, output): - input = input[0] - module.__flops__ += int(np.prod(input.shape)) - - -def norm_flops_counter_hook(module, input, output): - input = input[0] - - batch_flops = np.prod(input.shape) - if (getattr(module, 'affine', False) - or getattr(module, 'elementwise_affine', False)): - batch_flops *= 2 - module.__flops__ += int(batch_flops) - - -def deconv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - input_height, input_width = input.shape[2:] - - kernel_height, kernel_width = conv_module.kernel_size - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = ( - kernel_height * kernel_width * in_channels * filters_per_channel) - - active_elements_count = batch_size * input_height * input_width - overall_conv_flops = conv_per_position_flops * active_elements_count - bias_flops = 0 - if conv_module.bias is not None: - output_height, output_width = output.shape[2:] - bias_flops = out_channels * batch_size * output_height * output_height - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def conv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - output_dims = list(output.shape[2:]) - - kernel_dims = list(conv_module.kernel_size) - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = int( - np.prod(kernel_dims)) * in_channels * filters_per_channel - - active_elements_count = batch_size * int(np.prod(output_dims)) - - overall_conv_flops = conv_per_position_flops * active_elements_count - - bias_flops = 0 - - if conv_module.bias is not None: - - bias_flops = out_channels * active_elements_count - - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def batch_counter_hook(module, input, output): - batch_size = 1 - if len(input) > 0: - # Can have multiple inputs, getting the first one - input = input[0] - batch_size = len(input) - else: - pass - print('Warning! No positional inputs found for a module, ' - 'assuming batch size is 1.') - module.__batch_counter__ += batch_size - - -def add_batch_counter_variables_or_reset(module): - - module.__batch_counter__ = 0 - - -def add_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - return - - handle = module.register_forward_hook(batch_counter_hook) - module.__batch_counter_handle__ = handle - - -def remove_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - module.__batch_counter_handle__.remove() - del module.__batch_counter_handle__ - - -def add_flops_counter_variable_or_reset(module): - if is_supported_instance(module): - if hasattr(module, '__flops__') or hasattr(module, '__params__'): - print('Warning: variables __flops__ or __params__ are already ' - 'defined for the module' + type(module).__name__ + - ' ptflops can affect your code!') - module.__flops__ = 0 - module.__params__ = get_model_parameters_number(module) - - -def is_supported_instance(module): - if type(module) in get_modules_mapping(): - return True - return False - - -def remove_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - module.__flops_handle__.remove() - del module.__flops_handle__ - - -def get_modules_mapping(): - return { - # convolutions - nn.Conv1d: conv_flops_counter_hook, - nn.Conv2d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, - nn.Conv3d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, - # activations - nn.ReLU: relu_flops_counter_hook, - nn.PReLU: relu_flops_counter_hook, - nn.ELU: relu_flops_counter_hook, - nn.LeakyReLU: relu_flops_counter_hook, - nn.ReLU6: relu_flops_counter_hook, - # poolings - nn.MaxPool1d: pool_flops_counter_hook, - nn.AvgPool1d: pool_flops_counter_hook, - nn.AvgPool2d: pool_flops_counter_hook, - nn.MaxPool2d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, - nn.MaxPool3d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, - nn.AvgPool3d: pool_flops_counter_hook, - nn.AdaptiveMaxPool1d: pool_flops_counter_hook, - nn.AdaptiveAvgPool1d: pool_flops_counter_hook, - nn.AdaptiveMaxPool2d: pool_flops_counter_hook, - nn.AdaptiveAvgPool2d: pool_flops_counter_hook, - nn.AdaptiveMaxPool3d: pool_flops_counter_hook, - nn.AdaptiveAvgPool3d: pool_flops_counter_hook, - # normalizations - nn.BatchNorm1d: norm_flops_counter_hook, - nn.BatchNorm2d: norm_flops_counter_hook, - nn.BatchNorm3d: norm_flops_counter_hook, - nn.GroupNorm: norm_flops_counter_hook, - nn.InstanceNorm1d: norm_flops_counter_hook, - nn.InstanceNorm2d: norm_flops_counter_hook, - nn.InstanceNorm3d: norm_flops_counter_hook, - nn.LayerNorm: norm_flops_counter_hook, - # FC - nn.Linear: linear_flops_counter_hook, - mmcv.cnn.bricks.Linear: linear_flops_counter_hook, - # Upscale - nn.Upsample: upsample_flops_counter_hook, - # Deconvolution - nn.ConvTranspose2d: deconv_flops_counter_hook, - mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, - } diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py deleted file mode 100755 index cb7076f80..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def _fuse_conv_bn(conv, bn): - """Fuse conv and bn into one module. - - Args: - conv (nn.Module): Conv to be fused. - bn (nn.Module): BN to be fused. - - Returns: - nn.Module: Fused module. - """ - conv_w = conv.weight - conv_b = conv.bias if conv.bias is not None else torch.zeros_like( - bn.running_mean) - - factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) - conv.weight = nn.Parameter(conv_w * - factor.reshape([conv.out_channels, 1, 1, 1])) - conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) - return conv - - -def fuse_conv_bn(module): - """Recursively fuse conv and bn in a module. - - During inference, the functionary of batch norm layers is turned off - but only the mean and var alone channels are used, which exposes the - chance to fuse it with the preceding conv layers to save computations and - simplify network structures. - - Args: - module (nn.Module): Module to be fused. - - Returns: - nn.Module: Fused module. - """ - last_conv = None - last_conv_name = None - - for name, child in module.named_children(): - if isinstance(child, - (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): - if last_conv is None: # only fuse BN that is after Conv - continue - fused_conv = _fuse_conv_bn(last_conv, child) - module._modules[last_conv_name] = fused_conv - # To reduce changes, set BN as Identity instead of deleting it. - module._modules[name] = nn.Identity() - last_conv = None - elif isinstance(child, nn.Conv2d): - last_conv = child - last_conv_name = name - else: - fuse_conv_bn(child) - return module diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/sync_bn.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/sync_bn.py deleted file mode 100755 index 8a79ff4a4..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/sync_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -import torch - -import mmcv - - -class _BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): - """A general BatchNorm layer without input dimension check. - - Reproduced from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc - is `_check_input_dim` that is designed for tensor sanity checks. - The check has been bypassed in this class for the convenience of converting - SyncBatchNorm. - """ - - def _check_input_dim(self, input): - return - - -def revert_sync_batchnorm(module): - """Helper function to convert all `SyncBatchNorm` (SyncBN) and - `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to - `BatchNormXd` layers. - - Adapted from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - - Args: - module (nn.Module): The module containing `SyncBatchNorm` layers. - - Returns: - module_output: The converted module with `BatchNormXd` layers. - """ - module_output = module - module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] - if hasattr(mmcv, 'ops'): - module_checklist.append(mmcv.ops.SyncBatchNorm) - if isinstance(module, tuple(module_checklist)): - module_output = _BatchNormXd(module.num_features, module.eps, - module.momentum, module.affine, - module.track_running_stats) - if module.affine: - # no_grad() may not be needed here but - # just to be consistent with `convert_sync_batchnorm()` - with torch.no_grad(): - module_output.weight = module.weight - module_output.bias = module.bias - module_output.running_mean = module.running_mean - module_output.running_var = module.running_var - module_output.num_batches_tracked = module.num_batches_tracked - module_output.training = module.training - # qconfig exists in quantized models - if hasattr(module, 'qconfig'): - module_output.qconfig = module.qconfig - for name, child in module.named_children(): - module_output.add_module(name, revert_sync_batchnorm(child)) - del module - return module_output diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/weight_init.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/weight_init.py deleted file mode 100755 index e1ac999e2..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/cnn/utils/weight_init.py +++ /dev/null @@ -1,684 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings - -import numpy as np -import torch -import torch.nn as nn -from torch import Tensor - -from mmcv.utils import Registry, build_from_cfg, get_logger, print_log - -INITIALIZERS = Registry('initializer') - - -def update_init_info(module, init_info): - """Update the `_params_init_info` in the module if the value of parameters - are changed. - - Args: - module (obj:`nn.Module`): The module of PyTorch with a user-defined - attribute `_params_init_info` which records the initialization - information. - init_info (str): The string that describes the initialization. - """ - assert hasattr( - module, - '_params_init_info'), f'Can not find `_params_init_info` in {module}' - for name, param in module.named_parameters(): - - assert param in module._params_init_info, ( - f'Find a new :obj:`Parameter` ' - f'named `{name}` during executing the ' - f'`init_weights` of ' - f'`{module.__class__.__name__}`. ' - f'Please do not add or ' - f'replace parameters during executing ' - f'the `init_weights`. ') - - # The parameter has been changed during executing the - # `init_weights` of module - mean_value = param.data.mean() - if module._params_init_info[param]['tmp_mean_value'] != mean_value: - module._params_init_info[param]['init_info'] = init_info - module._params_init_info[param]['tmp_mean_value'] = mean_value - - -def constant_init(module, val, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.constant_(module.weight, val) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def xavier_init(module, gain=1, bias=0, distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.xavier_uniform_(module.weight, gain=gain) - else: - nn.init.xavier_normal_(module.weight, gain=gain) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def normal_init(module, mean=0, std=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.normal_(module.weight, mean, std) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def trunc_normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - trunc_normal_(module.weight, mean, std, a, b) # type: ignore - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) # type: ignore - - -def uniform_init(module, a=0, b=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.uniform_(module.weight, a, b) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def kaiming_init(module, - a=0, - mode='fan_out', - nonlinearity='relu', - bias=0, - distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.kaiming_uniform_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - else: - nn.init.kaiming_normal_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def caffe2_xavier_init(module, bias=0): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - kaiming_init( - module, - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - bias=bias, - distribution='uniform') - - -def bias_init_with_prob(prior_prob): - """initialize conv/fc bias value according to a given probability value.""" - bias_init = float(-np.log((1 - prior_prob) / prior_prob)) - return bias_init - - -def _get_bases_name(m): - return [b.__name__ for b in m.__class__.__bases__] - - -class BaseInit(object): - - def __init__(self, *, bias=0, bias_prob=None, layer=None): - self.wholemodule = False - if not isinstance(bias, (int, float)): - raise TypeError(f'bias must be a number, but got a {type(bias)}') - - if bias_prob is not None: - if not isinstance(bias_prob, float): - raise TypeError(f'bias_prob type must be float, \ - but got {type(bias_prob)}') - - if layer is not None: - if not isinstance(layer, (str, list)): - raise TypeError(f'layer must be a str or a list of str, \ - but got a {type(layer)}') - else: - layer = [] - - if bias_prob is not None: - self.bias = bias_init_with_prob(bias_prob) - else: - self.bias = bias - self.layer = [layer] if isinstance(layer, str) else layer - - def _get_init_info(self): - info = f'{self.__class__.__name__}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Constant') -class ConstantInit(BaseInit): - """Initialize module parameters with constant values. - - Args: - val (int | float): the value to fill the weights in the module with - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, val, **kwargs): - super().__init__(**kwargs) - self.val = val - - def __call__(self, module): - - def init(m): - if self.wholemodule: - constant_init(m, self.val, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - constant_init(m, self.val, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Xavier') -class XavierInit(BaseInit): - r"""Initialize module parameters with values according to the method - described in `Understanding the difficulty of training deep feedforward - neural networks - Glorot, X. & Bengio, Y. (2010). - `_ - - Args: - gain (int | float): an optional scaling factor. Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` - or ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, gain=1, distribution='normal', **kwargs): - super().__init__(**kwargs) - self.gain = gain - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - xavier_init(m, self.gain, self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - xavier_init(m, self.gain, self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: gain={self.gain}, ' \ - f'distribution={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Normal') -class NormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. - - Args: - mean (int | float):the mean of the normal distribution. Defaults to 0. - std (int | float): the standard deviation of the normal distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, mean=0, std=1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.std = std - - def __call__(self, module): - - def init(m): - if self.wholemodule: - normal_init(m, self.mean, self.std, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - normal_init(m, self.mean, self.std, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: mean={self.mean},' \ - f' std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='TruncNormal') -class TruncNormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values - outside :math:`[a, b]`. - - Args: - mean (float): the mean of the normal distribution. Defaults to 0. - std (float): the standard deviation of the normal distribution. - Defaults to 1. - a (float): The minimum cutoff value. - b ( float): The maximum cutoff value. - bias (float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - **kwargs) -> None: - super().__init__(**kwargs) - self.mean = mean - self.std = std - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ - f' mean={self.mean}, std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Uniform') -class UniformInit(BaseInit): - r"""Initialize module parameters with values drawn from the uniform - distribution :math:`\mathcal{U}(a, b)`. - - Args: - a (int | float): the lower bound of the uniform distribution. - Defaults to 0. - b (int | float): the upper bound of the uniform distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, a=0, b=1, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def __call__(self, module): - - def init(m): - if self.wholemodule: - uniform_init(m, self.a, self.b, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - uniform_init(m, self.a, self.b, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a},' \ - f' b={self.b}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Kaiming') -class KaimingInit(BaseInit): - r"""Initialize module parameters with the values according to the method - described in `Delving deep into rectifiers: Surpassing human-level - performance on ImageNet classification - He, K. et al. (2015). - `_ - - Args: - a (int | float): the negative slope of the rectifier used after this - layer (only used with ``'leaky_relu'``). Defaults to 0. - mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing - ``'fan_in'`` preserves the magnitude of the variance of the weights - in the forward pass. Choosing ``'fan_out'`` preserves the - magnitudes in the backwards pass. Defaults to ``'fan_out'``. - nonlinearity (str): the non-linear function (`nn.functional` name), - recommended to use only with ``'relu'`` or ``'leaky_relu'`` . - Defaults to 'relu'. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` or - ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - a=0, - mode='fan_out', - nonlinearity='relu', - distribution='normal', - **kwargs): - super().__init__(**kwargs) - self.a = a - self.mode = mode - self.nonlinearity = nonlinearity - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ - f'nonlinearity={self.nonlinearity}, ' \ - f'distribution ={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Caffe2Xavier') -class Caffe2XavierInit(KaimingInit): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - def __init__(self, **kwargs): - super().__init__( - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='uniform', - **kwargs) - - def __call__(self, module): - super().__call__(module) - - -@INITIALIZERS.register_module(name='Pretrained') -class PretrainedInit(object): - """Initialize module by loading a pretrained model. - - Args: - checkpoint (str): the checkpoint file of the pretrained model should - be load. - prefix (str, optional): the prefix of a sub-module in the pretrained - model. it is for loading a part of the pretrained model to - initialize. For example, if we would like to only load the - backbone of a detector model, we can set ``prefix='backbone.'``. - Defaults to None. - map_location (str): map tensors into proper locations. - """ - - def __init__(self, checkpoint, prefix=None, map_location=None): - self.checkpoint = checkpoint - self.prefix = prefix - self.map_location = map_location - - def __call__(self, module): - from mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, - load_state_dict) - logger = get_logger('mmcv') - if self.prefix is None: - print_log(f'load model from: {self.checkpoint}', logger=logger) - load_checkpoint( - module, - self.checkpoint, - map_location=self.map_location, - strict=False, - logger=logger) - else: - print_log( - f'load {self.prefix} in model from: {self.checkpoint}', - logger=logger) - state_dict = _load_checkpoint_with_prefix( - self.prefix, self.checkpoint, map_location=self.map_location) - load_state_dict(module, state_dict, strict=False, logger=logger) - - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: load from {self.checkpoint}' - return info - - -def _initialize(module, cfg, wholemodule=False): - func = build_from_cfg(cfg, INITIALIZERS) - # wholemodule flag is for override mode, there is no layer key in override - # and initializer will give init values for the whole module with the name - # in override. - func.wholemodule = wholemodule - func(module) - - -def _initialize_override(module, override, cfg): - if not isinstance(override, (dict, list)): - raise TypeError(f'override must be a dict or a list of dict, \ - but got {type(override)}') - - override = [override] if isinstance(override, dict) else override - - for override_ in override: - - cp_override = copy.deepcopy(override_) - name = cp_override.pop('name', None) - if name is None: - raise ValueError('`override` must contain the key "name",' - f'but got {cp_override}') - # if override only has name key, it means use args in init_cfg - if not cp_override: - cp_override.update(cfg) - # if override has name key and other args except type key, it will - # raise error - elif 'type' not in cp_override.keys(): - raise ValueError( - f'`override` need "type" key, but got {cp_override}') - - if hasattr(module, name): - _initialize(getattr(module, name), cp_override, wholemodule=True) - else: - raise RuntimeError(f'module did not have attribute {name}, ' - f'but init_cfg is {cp_override}.') - - -def initialize(module, init_cfg): - """Initialize a module. - - Args: - module (``torch.nn.Module``): the module will be initialized. - init_cfg (dict | list[dict]): initialization configuration dict to - define initializer. OpenMMLab has implemented 6 initializers - including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, - ``Kaiming``, and ``Pretrained``. - Example: - >>> module = nn.Linear(2, 3, bias=True) - >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) - >>> initialize(module, init_cfg) - - >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) - >>> # define key ``'layer'`` for initializing layer with different - >>> # configuration - >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), - dict(type='Constant', layer='Linear', val=2)] - >>> initialize(module, init_cfg) - - >>> # define key``'override'`` to initialize some specific part in - >>> # module - >>> class FooNet(nn.Module): - >>> def __init__(self): - >>> super().__init__() - >>> self.feat = nn.Conv2d(3, 16, 3) - >>> self.reg = nn.Conv2d(16, 10, 3) - >>> self.cls = nn.Conv2d(16, 5, 3) - >>> model = FooNet() - >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', - >>> override=dict(type='Constant', name='reg', val=3, bias=4)) - >>> initialize(model, init_cfg) - - >>> model = ResNet(depth=50) - >>> # Initialize weights with the pretrained model. - >>> init_cfg = dict(type='Pretrained', - checkpoint='torchvision://resnet50') - >>> initialize(model, init_cfg) - - >>> # Initialize weights of a sub-module with the specific part of - >>> # a pretrained model by using "prefix". - >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ - >>> 'retinanet_r50_fpn_1x_coco/'\ - >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' - >>> init_cfg = dict(type='Pretrained', - checkpoint=url, prefix='backbone.') - """ - if not isinstance(init_cfg, (dict, list)): - raise TypeError(f'init_cfg must be a dict or a list of dict, \ - but got {type(init_cfg)}') - - if isinstance(init_cfg, dict): - init_cfg = [init_cfg] - - for cfg in init_cfg: - # should deeply copy the original config because cfg may be used by - # other modules, e.g., one init_cfg shared by multiple bottleneck - # blocks, the expected cfg will be changed after pop and will change - # the initialization behavior of other modules - cp_cfg = copy.deepcopy(cfg) - override = cp_cfg.pop('override', None) - _initialize(module, cp_cfg) - - if override is not None: - cp_cfg.pop('layer', None) - _initialize_override(module, override, cp_cfg) - else: - # All attributes in module have same initialization. - pass - - -def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, - b: float) -> Tensor: - # Method based on - # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf - # Modified from - # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - def norm_cdf(x): - # Computes standard normal cumulative distribution function - return (1. + math.erf(x / math.sqrt(2.))) / 2. - - if (mean < a - 2 * std) or (mean > b + 2 * std): - warnings.warn( - 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' - 'The distribution of values may be incorrect.', - stacklevel=2) - - with torch.no_grad(): - # Values are generated by using a truncated uniform distribution and - # then using the inverse CDF for the normal distribution. - # Get upper and lower cdf values - lower = norm_cdf((a - mean) / std) - upper = norm_cdf((b - mean) / std) - - # Uniformly fill tensor with values from [lower, upper], then translate - # to [2lower-1, 2upper-1]. - tensor.uniform_(2 * lower - 1, 2 * upper - 1) - - # Use inverse cdf transform for normal distribution to get truncated - # standard normal - tensor.erfinv_() - - # Transform to proper mean, std - tensor.mul_(std * math.sqrt(2.)) - tensor.add_(mean) - - # Clamp to ensure it's in the proper range - tensor.clamp_(min=a, max=b) - return tensor - - -def trunc_normal_(tensor: Tensor, - mean: float = 0., - std: float = 1., - a: float = -2., - b: float = 2.) -> Tensor: - r"""Fills the input Tensor with values drawn from a truncated - normal distribution. The values are effectively drawn from the - normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` - with values outside :math:`[a, b]` redrawn until they are within - the bounds. The method used for generating the random values works - best when :math:`a \leq \text{mean} \leq b`. - - Modified from - https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - - Args: - tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. - mean (float): the mean of the normal distribution. - std (float): the standard deviation of the normal distribution. - a (float): the minimum cutoff value. - b (float): the maximum cutoff value. - """ - return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/engine/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/engine/__init__.py deleted file mode 100755 index 3193b7f66..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, - single_gpu_test) - -__all__ = [ - 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', - 'single_gpu_test' -] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/engine/test.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/engine/test.py deleted file mode 100755 index f236b1cda..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/engine/test.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, data_loader): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - # Assume result has the same length of batch_size - # refer to https://github.com/open-mmlab/mmcv/issues/985 - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting - ``gpu_collect=True``, it encodes results to gpu tensors and use gpu - communication for results collection. On cpu mode it saves the results on - different gpus to ``tmpdir`` and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - if rank == 0: - batch_size = len(result) - batch_size_all = batch_size * world_size - if batch_size_all + prog_bar.completed > len(dataset): - batch_size_all = len(dataset) - prog_bar.completed - for _ in range(batch_size_all): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results under cpu mode. - - On cpu mode, this function will save the results on different gpus to - ``tmpdir`` and collect them by the rank 0 worker. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - tmpdir (str | None): temporal directory for collected results to - store. If set to None, it will create a random temporal directory - for it. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_result = mmcv.load(part_file) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results under gpu mode. - - On gpu mode, this function will encode results to gpu tensors and use gpu - communication for results collection. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/__init__.py deleted file mode 100755 index 2051b85f7..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .file_client import BaseStorageBackend, FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler -from .io import dump, load, register_handler -from .parse import dict_from_file, list_from_file - -__all__ = [ - 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', - 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', - 'list_from_file', 'dict_from_file' -] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/file_client.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/file_client.py deleted file mode 100755 index b2d622868..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/file_client.py +++ /dev/null @@ -1,1148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import os -import os.path as osp -import re -import tempfile -import warnings -from abc import ABCMeta, abstractmethod -from contextlib import contextmanager -from pathlib import Path -from typing import Iterable, Iterator, Optional, Tuple, Union -from urllib.request import urlopen - -import mmcv -from mmcv.utils.misc import has_method -from mmcv.utils.path import is_filepath - - -class BaseStorageBackend(metaclass=ABCMeta): - """Abstract class of storage backends. - - All backends need to implement two apis: ``get()`` and ``get_text()``. - ``get()`` reads the file as a byte stream and ``get_text()`` reads the file - as texts. - """ - - # a flag to indicate whether the backend can create a symlink for a file - _allow_symlink = False - - @property - def name(self): - return self.__class__.__name__ - - @property - def allow_symlink(self): - return self._allow_symlink - - @abstractmethod - def get(self, filepath): - pass - - @abstractmethod - def get_text(self, filepath): - pass - - -class CephBackend(BaseStorageBackend): - """Ceph storage backend (for internal use). - - Args: - path_mapping (dict|None): path mapping dict from local path to Petrel - path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` - will be replaced by ``dst``. Default: None. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - """ - - def __init__(self, path_mapping=None): - try: - import ceph - except ImportError: - raise ImportError('Please install ceph to enable CephBackend.') - - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead') - self._client = ceph.S3Client() - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def get(self, filepath): - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class PetrelBackend(BaseStorageBackend): - """Petrel storage backend (for internal use). - - PetrelBackend supports reading and writing data to multiple clusters. - If the file path contains the cluster name, PetrelBackend will read data - from specified cluster or write data to it. Otherwise, PetrelBackend will - access the default cluster. - - Args: - path_mapping (dict, optional): Path mapping dict from local path to - Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in - ``filepath`` will be replaced by ``dst``. Default: None. - enable_mc (bool, optional): Whether to enable memcached support. - Default: True. - - Examples: - >>> filepath1 = 's3://path/of/file' - >>> filepath2 = 'cluster-name:s3://path/of/file' - >>> client = PetrelBackend() - >>> client.get(filepath1) # get data from default cluster - >>> client.get(filepath2) # get data from 'cluster-name' cluster - """ - - def __init__(self, - path_mapping: Optional[dict] = None, - enable_mc: bool = True): - try: - from petrel_client import client - except ImportError: - raise ImportError('Please install petrel_client to enable ' - 'PetrelBackend.') - - self._client = client.Client(enable_mc=enable_mc) - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def _map_path(self, filepath: Union[str, Path]) -> str: - """Map ``filepath`` to a string path whose prefix will be replaced by - :attr:`self.path_mapping`. - - Args: - filepath (str): Path to be mapped. - """ - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - return filepath - - def _format_path(self, filepath: str) -> str: - """Convert a ``filepath`` to standard format of petrel oss. - - If the ``filepath`` is concatenated by ``os.path.join``, in a Windows - environment, the ``filepath`` will be the format of - 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the - above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. - - Args: - filepath (str): Path to be formatted. - """ - return re.sub(r'\\+', '/', filepath) - - def get(self, filepath: Union[str, Path]) -> memoryview: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - memoryview: A memory view of expected bytes object to avoid - copying. The memoryview object can be converted to bytes by - ``value_buf.tobytes()``. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return str(self.get(filepath), encoding=encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Save data to a given ``filepath``. - - Args: - obj (bytes): Data to be saved. - filepath (str or Path): Path to write data. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.put(filepath, obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Save data to a given ``filepath``. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to encode the ``obj``. - Default: 'utf-8'. - """ - self.put(bytes(obj, encoding=encoding), filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - if not has_method(self._client, 'delete'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `delete` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.delete(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - if not (has_method(self._client, 'contains') - and has_method(self._client, 'isdir')): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` and `isdir` methods, please use a higher' - 'version or dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) or self._client.isdir(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - if not has_method(self._client, 'isdir'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `isdir` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - if not has_method(self._client, 'contains'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` method, please use a higher version or ' - 'dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result after concatenation. - """ - filepath = self._format_path(self._map_path(filepath)) - if filepath.endswith('/'): - filepath = filepath[:-1] - formatted_paths = [filepath] - for path in filepaths: - formatted_paths.append(self._format_path(self._map_path(path))) - return '/'.join(formatted_paths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download a file from ``filepath`` and return a temporary path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str | Path): Download a file from ``filepath``. - - Examples: - >>> client = PetrelBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('s3://path/of/your/file') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one temporary path. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - assert self.isfile(filepath) - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - Petrel has no concept of directories but it simulates the directory - hierarchy in the filesystem through public prefixes. In addition, - if the returned path ends with '/', it means the path is a public - prefix which is a logical directory. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - In addition, the returned path of directory will not contains the - suffix '/' which is consistent with other backends. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if not has_method(self._client, 'list'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `list` method, please use a higher version or dev' - ' branch instead.')) - - dir_path = self._map_path(dir_path) - dir_path = self._format_path(dir_path) - if list_dir and suffix is not None: - raise TypeError( - '`list_dir` should be False when `suffix` is not None') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - # Petrel's simulated directory hierarchy assumes that directory paths - # should end with `/` - if not dir_path.endswith('/'): - dir_path += '/' - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for path in self._client.list(dir_path): - # the `self.isdir` is not used here to determine whether path - # is a directory, because `self.isdir` relies on - # `self._client.list` - if path.endswith('/'): # a directory path - next_dir_path = self.join_path(dir_path, path) - if list_dir: - # get the relative path and exclude the last - # character '/' - rel_dir = next_dir_path[len(root):-1] - yield rel_dir - if recursive: - yield from _list_dir_or_file(next_dir_path, list_dir, - list_file, suffix, - recursive) - else: # a file path - absolute_path = self.join_path(dir_path, path) - rel_path = absolute_path[len(root):] - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class MemcachedBackend(BaseStorageBackend): - """Memcached storage backend. - - Attributes: - server_list_cfg (str): Config file for memcached server list. - client_cfg (str): Config file for memcached client. - sys_path (str | None): Additional path to be appended to `sys.path`. - Default: None. - """ - - def __init__(self, server_list_cfg, client_cfg, sys_path=None): - if sys_path is not None: - import sys - sys.path.append(sys_path) - try: - import mc - except ImportError: - raise ImportError( - 'Please install memcached to enable MemcachedBackend.') - - self.server_list_cfg = server_list_cfg - self.client_cfg = client_cfg - self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, - self.client_cfg) - # mc.pyvector servers as a point which points to a memory cache - self._mc_buffer = mc.pyvector() - - def get(self, filepath): - filepath = str(filepath) - import mc - self._client.Get(filepath, self._mc_buffer) - value_buf = mc.ConvertBuffer(self._mc_buffer) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class LmdbBackend(BaseStorageBackend): - """Lmdb storage backend. - - Args: - db_path (str): Lmdb database path. - readonly (bool, optional): Lmdb environment parameter. If True, - disallow any write operations. Default: True. - lock (bool, optional): Lmdb environment parameter. If False, when - concurrent access occurs, do not lock the database. Default: False. - readahead (bool, optional): Lmdb environment parameter. If False, - disable the OS filesystem readahead mechanism, which may improve - random read performance when a database is larger than RAM. - Default: False. - - Attributes: - db_path (str): Lmdb database path. - """ - - def __init__(self, - db_path, - readonly=True, - lock=False, - readahead=False, - **kwargs): - try: - import lmdb - except ImportError: - raise ImportError('Please install lmdb to enable LmdbBackend.') - - self.db_path = str(db_path) - self._client = lmdb.open( - self.db_path, - readonly=readonly, - lock=lock, - readahead=readahead, - **kwargs) - - def get(self, filepath): - """Get values according to the filepath. - - Args: - filepath (str | obj:`Path`): Here, filepath is the lmdb key. - """ - filepath = str(filepath) - with self._client.begin(write=False) as txn: - value_buf = txn.get(filepath.encode('ascii')) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class HardDiskBackend(BaseStorageBackend): - """Raw hard disks storage backend.""" - - _allow_symlink = True - - def get(self, filepath: Union[str, Path]) -> bytes: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes: Expected bytes object. - """ - with open(filepath, 'rb') as f: - value_buf = f.read() - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - with open(filepath, 'r', encoding=encoding) as f: - value_buf = f.read() - return value_buf - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` will create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'wb') as f: - f.write(obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` will create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'w', encoding=encoding) as f: - f.write(obj) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - os.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return osp.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return osp.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return osp.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return osp.join(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, filepath: Union[str, Path]) -> Iterable[Union[str, Path]]: - """Only for unified API and do nothing.""" - yield filepath - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if list_dir and suffix is not None: - raise TypeError('`suffix` should be None when `list_dir` is True') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - elif osp.isdir(entry.path): - if list_dir: - rel_dir = osp.relpath(entry.path, root) - yield rel_dir - if recursive: - yield from _list_dir_or_file(entry.path, list_dir, - list_file, suffix, - recursive) - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class HTTPBackend(BaseStorageBackend): - """HTTP and HTTPS storage bachend.""" - - def get(self, filepath): - value_buf = urlopen(filepath).read() - return value_buf - - def get_text(self, filepath, encoding='utf-8'): - value_buf = urlopen(filepath).read() - return value_buf.decode(encoding) - - @contextmanager - def get_local_path(self, filepath: str) -> Iterable[str]: - """Download a file from ``filepath``. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str): Download a file from ``filepath``. - - Examples: - >>> client = HTTPBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('http://path/of/your/file') as path: - ... # do something here - """ - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - -class FileClient: - """A general file client to access files in different backends. - - The client loads a file or text in a specified backend from its path - and returns it as a binary or text file. There are two ways to choose a - backend, the name of backend and the prefix of path. Although both of them - can be used to choose a storage backend, ``backend`` has a higher priority - that is if they are all set, the storage backend will be chosen by the - backend argument. If they are all `None`, the disk backend will be chosen. - Note that It can also register other backend accessor with a given name, - prefixes, and backend class. In addition, We use the singleton pattern to - avoid repeated object creation. If the arguments are the same, the same - object will be returned. - - Args: - backend (str, optional): The storage backend type. Options are "disk", - "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. - prefix (str, optional): The prefix of the registered storage backend. - Options are "s3", "http", "https". Default: None. - - Examples: - >>> # only set backend - >>> file_client = FileClient(backend='petrel') - >>> # only set prefix - >>> file_client = FileClient(prefix='s3') - >>> # set both backend and prefix but use backend to choose client - >>> file_client = FileClient(backend='petrel', prefix='s3') - >>> # if the arguments are the same, the same object is returned - >>> file_client1 = FileClient(backend='petrel') - >>> file_client1 is file_client - True - - Attributes: - client (:obj:`BaseStorageBackend`): The backend object. - """ - - _backends = { - 'disk': HardDiskBackend, - 'ceph': CephBackend, - 'memcached': MemcachedBackend, - 'lmdb': LmdbBackend, - 'petrel': PetrelBackend, - 'http': HTTPBackend, - } - # This collection is used to record the overridden backends, and when a - # backend appears in the collection, the singleton pattern is disabled for - # that backend, because if the singleton pattern is used, then the object - # returned will be the backend before overwriting - _overridden_backends = set() - _prefix_to_backends = { - 's3': PetrelBackend, - 'http': HTTPBackend, - 'https': HTTPBackend, - } - _overridden_prefixes = set() - - _instances = {} - - def __new__(cls, backend=None, prefix=None, **kwargs): - if backend is None and prefix is None: - backend = 'disk' - if backend is not None and backend not in cls._backends: - raise ValueError( - f'Backend {backend} is not supported. Currently supported ones' - f' are {list(cls._backends.keys())}') - if prefix is not None and prefix not in cls._prefix_to_backends: - raise ValueError( - f'prefix {prefix} is not supported. Currently supported ones ' - f'are {list(cls._prefix_to_backends.keys())}') - - # concatenate the arguments to a unique key for determining whether - # objects with the same arguments were created - arg_key = f'{backend}:{prefix}' - for key, value in kwargs.items(): - arg_key += f':{key}:{value}' - - # if a backend was overridden, it will create a new object - if (arg_key in cls._instances - and backend not in cls._overridden_backends - and prefix not in cls._overridden_prefixes): - _instance = cls._instances[arg_key] - else: - # create a new object and put it to _instance - _instance = super().__new__(cls) - if backend is not None: - _instance.client = cls._backends[backend](**kwargs) - else: - _instance.client = cls._prefix_to_backends[prefix](**kwargs) - - cls._instances[arg_key] = _instance - - return _instance - - @property - def name(self): - return self.client.name - - @property - def allow_symlink(self): - return self.client.allow_symlink - - @staticmethod - def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: - """Parse the prefix of a uri. - - Args: - uri (str | Path): Uri to be parsed that contains the file prefix. - - Examples: - >>> FileClient.parse_uri_prefix('s3://path/of/your/file') - 's3' - - Returns: - str | None: Return the prefix of uri if the uri contains '://' - else ``None``. - """ - assert is_filepath(uri) - uri = str(uri) - if '://' not in uri: - return None - else: - prefix, _ = uri.split('://') - # In the case of PetrelBackend, the prefix may contains the cluster - # name like clusterName:s3 - if ':' in prefix: - _, prefix = prefix.split(':') - return prefix - - @classmethod - def infer_client(cls, - file_client_args: Optional[dict] = None, - uri: Optional[Union[str, Path]] = None) -> 'FileClient': - """Infer a suitable file client based on the URI and arguments. - - Args: - file_client_args (dict, optional): Arguments to instantiate a - FileClient. Default: None. - uri (str | Path, optional): Uri to be parsed that contains the file - prefix. Default: None. - - Examples: - >>> uri = 's3://path/of/your/file' - >>> file_client = FileClient.infer_client(uri=uri) - >>> file_client_args = {'backend': 'petrel'} - >>> file_client = FileClient.infer_client(file_client_args) - - Returns: - FileClient: Instantiated FileClient object. - """ - assert file_client_args is not None or uri is not None - if file_client_args is None: - file_prefix = cls.parse_uri_prefix(uri) # type: ignore - return cls(prefix=file_prefix) - else: - return cls(**file_client_args) - - @classmethod - def _register_backend(cls, name, backend, force=False, prefixes=None): - if not isinstance(name, str): - raise TypeError('the backend name should be a string, ' - f'but got {type(name)}') - if not inspect.isclass(backend): - raise TypeError( - f'backend should be a class but got {type(backend)}') - if not issubclass(backend, BaseStorageBackend): - raise TypeError( - f'backend {backend} is not a subclass of BaseStorageBackend') - if not force and name in cls._backends: - raise KeyError( - f'{name} is already registered as a storage backend, ' - 'add "force=True" if you want to override it') - - if name in cls._backends and force: - cls._overridden_backends.add(name) - cls._backends[name] = backend - - if prefixes is not None: - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if prefix not in cls._prefix_to_backends: - cls._prefix_to_backends[prefix] = backend - elif (prefix in cls._prefix_to_backends) and force: - cls._overridden_prefixes.add(prefix) - cls._prefix_to_backends[prefix] = backend - else: - raise KeyError( - f'{prefix} is already registered as a storage backend,' - ' add "force=True" if you want to override it') - - @classmethod - def register_backend(cls, name, backend=None, force=False, prefixes=None): - """Register a backend to FileClient. - - This method can be used as a normal class method or a decorator. - - .. code-block:: python - - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - FileClient.register_backend('new', NewBackend) - - or - - .. code-block:: python - - @FileClient.register_backend('new') - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - Args: - name (str): The name of the registered backend. - backend (class, optional): The backend class to be registered, - which must be a subclass of :class:`BaseStorageBackend`. - When this method is used as a decorator, backend is None. - Defaults to None. - force (bool, optional): Whether to override the backend if the name - has already been registered. Defaults to False. - prefixes (str or list[str] or tuple[str], optional): The prefixes - of the registered storage backend. Default: None. - `New in version 1.3.15.` - """ - if backend is not None: - cls._register_backend( - name, backend, force=force, prefixes=prefixes) - return - - def _register(backend_cls): - cls._register_backend( - name, backend_cls, force=force, prefixes=prefixes) - return backend_cls - - return _register - - def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: - """Read data from a given ``filepath`` with 'rb' mode. - - Note: - There are two types of return values for ``get``, one is ``bytes`` - and the other is ``memoryview``. The advantage of using memoryview - is that you can avoid copying, and if you want to convert it to - ``bytes``, you can use ``.tobytes()``. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes | memoryview: Expected bytes object or a memory view of the - bytes object. - """ - return self.client.get(filepath) - - def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return self.client.get_text(filepath, encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` should create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - self.client.put(obj, filepath) - - def put_text(self, obj: str, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` should create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str, optional): The encoding format used to open the - `filepath`. Default: 'utf-8'. - """ - self.client.put_text(obj, filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str, Path): Path to be removed. - """ - self.client.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return self.client.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return self.client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return self.client.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return self.client.join_path(filepath, *filepaths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download data from ``filepath`` and write the data to local path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Note: - If the ``filepath`` is a local path, just return itself. - - .. warning:: - ``get_local_path`` is an experimental interface that may change in - the future. - - Args: - filepath (str or Path): Path to be read data. - - Examples: - >>> file_client = FileClient(prefix='s3') - >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one path. - """ - with self.client.get_local_path(str(filepath)) as local_path: - yield local_path - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, - suffix, recursive) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/__init__.py deleted file mode 100755 index aa24d9197..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseFileHandler -from .json_handler import JsonHandler -from .pickle_handler import PickleHandler -from .yaml_handler import YamlHandler - -__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/base.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/base.py deleted file mode 100755 index 288878bc5..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseFileHandler(metaclass=ABCMeta): - # `str_like` is a flag to indicate whether the type of file object is - # str-like object or bytes-like object. Pickle only processes bytes-like - # objects but json only processes str-like object. If it is str-like - # object, `StringIO` will be used to process the buffer. - str_like = True - - @abstractmethod - def load_from_fileobj(self, file, **kwargs): - pass - - @abstractmethod - def dump_to_fileobj(self, obj, file, **kwargs): - pass - - @abstractmethod - def dump_to_str(self, obj, **kwargs): - pass - - def load_from_path(self, filepath, mode='r', **kwargs): - with open(filepath, mode) as f: - return self.load_from_fileobj(f, **kwargs) - - def dump_to_path(self, obj, filepath, mode='w', **kwargs): - with open(filepath, mode) as f: - self.dump_to_fileobj(obj, f, **kwargs) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/json_handler.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/json_handler.py deleted file mode 100755 index 18d4f15f7..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/json_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -import numpy as np - -from .base import BaseFileHandler - - -def set_default(obj): - """Set default json values for non-serializable values. - - It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. - It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, - etc.) into plain numbers of plain python built-in types. - """ - if isinstance(obj, (set, range)): - return list(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, np.generic): - return obj.item() - raise TypeError(f'{type(obj)} is unsupported for json dump') - - -class JsonHandler(BaseFileHandler): - - def load_from_fileobj(self, file): - return json.load(file) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('default', set_default) - json.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('default', set_default) - return json.dumps(obj, **kwargs) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/pickle_handler.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/pickle_handler.py deleted file mode 100755 index b37c79bed..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/pickle_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import pickle - -from .base import BaseFileHandler - - -class PickleHandler(BaseFileHandler): - - str_like = False - - def load_from_fileobj(self, file, **kwargs): - return pickle.load(file, **kwargs) - - def load_from_path(self, filepath, **kwargs): - return super(PickleHandler, self).load_from_path( - filepath, mode='rb', **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('protocol', 2) - return pickle.dumps(obj, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('protocol', 2) - pickle.dump(obj, file, **kwargs) - - def dump_to_path(self, obj, filepath, **kwargs): - super(PickleHandler, self).dump_to_path( - obj, filepath, mode='wb', **kwargs) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/yaml_handler.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/yaml_handler.py deleted file mode 100755 index c5aa2eea1..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/handlers/yaml_handler.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import yaml - -try: - from yaml import CLoader as Loader, CDumper as Dumper -except ImportError: - from yaml import Loader, Dumper - -from .base import BaseFileHandler # isort:skip - - -class YamlHandler(BaseFileHandler): - - def load_from_fileobj(self, file, **kwargs): - kwargs.setdefault('Loader', Loader) - return yaml.load(file, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('Dumper', Dumper) - yaml.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('Dumper', Dumper) - return yaml.dump(obj, **kwargs) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/io.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/io.py deleted file mode 100755 index aaefde58a..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/io.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from io import BytesIO, StringIO -from pathlib import Path - -from ..utils import is_list_of, is_str -from .file_client import FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler - -file_handlers = { - 'json': JsonHandler(), - 'yaml': YamlHandler(), - 'yml': YamlHandler(), - 'pickle': PickleHandler(), - 'pkl': PickleHandler() -} - - -def load(file, file_format=None, file_client_args=None, **kwargs): - """Load data from json/yaml/pickle files. - - This method provides a unified api for loading data from serialized files. - - Note: - In v1.3.16 and later, ``load`` supports loading data from serialized - files those can be storaged in different backends. - - Args: - file (str or :obj:`Path` or file-like object): Filename or a file-like - object. - file_format (str, optional): If not specified, the file format will be - inferred from the file extension, otherwise use the specified one. - Currently supported formats include "json", "yaml/yml" and - "pickle/pkl". - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> load('/path/of/your/file') # file is storaged in disk - >>> load('https://path/of/your/file') # file is storaged in Internet - >>> load('s3://path/of/your/file') # file is storaged in petrel - - Returns: - The content from the file. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None and is_str(file): - file_format = file.split('.')[-1] - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO(file_client.get_text(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - else: - with BytesIO(file_client.get(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - elif hasattr(file, 'read'): - obj = handler.load_from_fileobj(file, **kwargs) - else: - raise TypeError('"file" must be a filepath str or a file-object') - return obj - - -def dump(obj, file=None, file_format=None, file_client_args=None, **kwargs): - """Dump data to json/yaml/pickle strings or files. - - This method provides a unified api for dumping data as strings or to files, - and also supports custom arguments for each file format. - - Note: - In v1.3.16 and later, ``dump`` supports dumping data as strings or to - files which is saved to different backends. - - Args: - obj (any): The python object to be dumped. - file (str or :obj:`Path` or file-like object, optional): If not - specified, then the object is dumped to a str, otherwise to a file - specified by the filename or file-like object. - file_format (str, optional): Same as :func:`load`. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dump('hello world', '/path/of/your/file') # disk - >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel - - Returns: - bool: True for success, False otherwise. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None: - if is_str(file): - file_format = file.split('.')[-1] - elif file is None: - raise ValueError( - 'file_format must be specified since file is None') - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if file is None: - return handler.dump_to_str(obj, **kwargs) - elif is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put_text(f.getvalue(), file) - else: - with BytesIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put(f.getvalue(), file) - elif hasattr(file, 'write'): - handler.dump_to_fileobj(obj, file, **kwargs) - else: - raise TypeError('"file" must be a filename str or a file-object') - - -def _register_handler(handler, file_formats): - """Register a handler for some file extensions. - - Args: - handler (:obj:`BaseFileHandler`): Handler to be registered. - file_formats (str or list[str]): File formats to be handled by this - handler. - """ - if not isinstance(handler, BaseFileHandler): - raise TypeError( - f'handler must be a child of BaseFileHandler, not {type(handler)}') - if isinstance(file_formats, str): - file_formats = [file_formats] - if not is_list_of(file_formats, str): - raise TypeError('file_formats must be a str or a list of str') - for ext in file_formats: - file_handlers[ext] = handler - - -def register_handler(file_formats, **kwargs): - - def wrap(cls): - _register_handler(cls(**kwargs), file_formats) - return cls - - return wrap diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/parse.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/parse.py deleted file mode 100755 index f60f0d611..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/fileio/parse.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from io import StringIO - -from .file_client import FileClient - - -def list_from_file(filename, - prefix='', - offset=0, - max_num=0, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a list of strings. - - Note: - In v1.3.16 and later, ``list_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a list for strings. - - Args: - filename (str): Filename. - prefix (str): The prefix to be inserted to the beginning of each item. - offset (int): The offset of lines. - max_num (int): The maximum number of lines to be read, - zeros and negatives mean no limitation. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> list_from_file('/path/of/your/file') # disk - ['hello', 'world'] - >>> list_from_file('s3://path/of/your/file') # ceph or petrel - ['hello', 'world'] - - Returns: - list[str]: A list of strings. - """ - cnt = 0 - item_list = [] - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for _ in range(offset): - f.readline() - for line in f: - if 0 < max_num <= cnt: - break - item_list.append(prefix + line.rstrip('\n\r')) - cnt += 1 - return item_list - - -def dict_from_file(filename, - key_type=str, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a dict. - - Each line of the text file will be two or more columns split by - whitespaces or tabs. The first column will be parsed as dict keys, and - the following columns will be parsed as dict values. - - Note: - In v1.3.16 and later, ``dict_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a dict. - - Args: - filename(str): Filename. - key_type(type): Type of the dict keys. str is user by default and - type conversion will be performed if specified. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dict_from_file('/path/of/your/file') # disk - {'key1': 'value1', 'key2': 'value2'} - >>> dict_from_file('s3://path/of/your/file') # ceph or petrel - {'key1': 'value1', 'key2': 'value2'} - - Returns: - dict: The parsed contents. - """ - mapping = {} - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for line in f: - items = line.rstrip('\n').split() - assert len(items) >= 2 - key = key_type(items[0]) - val = items[1:] if len(items) > 2 else items[1] - mapping[key] = val - return mapping diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/__init__.py deleted file mode 100755 index d0051d609..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, - gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, - rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) -from .geometric import (cutout, imcrop, imflip, imflip_, impad, - impad_to_multiple, imrescale, imresize, imresize_like, - imresize_to_multiple, imrotate, imshear, imtranslate, - rescale_size) -from .io import imfrombytes, imread, imwrite, supported_backends, use_backend -from .misc import tensor2imgs -from .photometric import (adjust_brightness, adjust_color, adjust_contrast, - adjust_lighting, adjust_sharpness, auto_contrast, - clahe, imdenormalize, imequalize, iminvert, - imnormalize, imnormalize_, lut_transform, posterize, - solarize) - -__all__ = [ - 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', - 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', - 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', - 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', - 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', - 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', - 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', - 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', - 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', - 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting' -] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/colorspace.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/colorspace.py deleted file mode 100755 index 814533952..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/colorspace.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def imconvert(img, src, dst): - """Convert an image from the src colorspace to dst colorspace. - - Args: - img (ndarray): The input image. - src (str): The source colorspace, e.g., 'rgb', 'hsv'. - dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. - - Returns: - ndarray: The converted image. - """ - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - out_img = cv2.cvtColor(img, code) - return out_img - - -def bgr2gray(img, keepdim=False): - """Convert a BGR image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def rgb2gray(img, keepdim=False): - """Convert a RGB image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def gray2bgr(img): - """Convert a grayscale image to BGR image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted BGR image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return out_img - - -def gray2rgb(img): - """Convert a grayscale image to RGB image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted RGB image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) - return out_img - - -def _convert_input_type_range(img): - """Convert the type and range of the input image. - - It converts the input image to np.float32 type and range of [0, 1]. - It is mainly used for pre-processing the input image in colorspace - conversion functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - (ndarray): The converted image with type of np.float32 and range of - [0, 1]. - """ - img_type = img.dtype - img = img.astype(np.float32) - if img_type == np.float32: - pass - elif img_type == np.uint8: - img /= 255. - else: - raise TypeError('The img type should be np.float32 or np.uint8, ' - f'but got {img_type}') - return img - - -def _convert_output_type_range(img, dst_type): - """Convert the type and range of the image according to dst_type. - - It converts the image to desired type and range. If `dst_type` is np.uint8, - images will be converted to np.uint8 type with range [0, 255]. If - `dst_type` is np.float32, it converts the image to np.float32 type with - range [0, 1]. - It is mainly used for post-processing images in colorspace conversion - functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The image to be converted with np.float32 type and - range [0, 255]. - dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it - converts the image to np.uint8 type with range [0, 255]. If - dst_type is np.float32, it converts the image to np.float32 type - with range [0, 1]. - - Returns: - (ndarray): The converted image with desired type and range. - """ - if dst_type not in (np.uint8, np.float32): - raise TypeError('The dst_type should be np.float32 or np.uint8, ' - f'but got {dst_type}') - if dst_type == np.uint8: - img = img.round() - else: - img /= 255. - return img.astype(dst_type) - - -def rgb2ycbcr(img, y_only=False): - """Convert a RGB image to YCbCr image. - - This function produces the same results as Matlab's `rgb2ycbcr` function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 - else: - out_img = np.matmul( - img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], - [24.966, 112.0, -18.214]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def bgr2ycbcr(img, y_only=False): - """Convert a BGR image to YCbCr image. - - The bgr version of rgb2ycbcr. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 - else: - out_img = np.matmul( - img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], - [65.481, -37.797, 112.0]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2rgb(img): - """Convert a YCbCr image to RGB image. - - This function produces the same results as Matlab's ycbcr2rgb function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted RGB image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0, -0.00153632, 0.00791071], - [0.00625893, -0.00318811, 0]]) * 255.0 + [ - -222.921, 135.576, -276.836 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2bgr(img): - """Convert a YCbCr image to BGR image. - - The bgr version of ycbcr2rgb. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted BGR image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0.00791071, -0.00153632, 0], - [0, -0.00318811, 0.00625893]]) * 255.0 + [ - -276.836, 135.576, -222.921 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def convert_color_factory(src, dst): - - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - - def convert_color(img): - out_img = cv2.cvtColor(img, code) - return out_img - - convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} - image. - - Args: - img (ndarray or str): The input image. - - Returns: - ndarray: The converted {dst.upper()} image. - """ - - return convert_color - - -bgr2rgb = convert_color_factory('bgr', 'rgb') - -rgb2bgr = convert_color_factory('rgb', 'bgr') - -bgr2hsv = convert_color_factory('bgr', 'hsv') - -hsv2bgr = convert_color_factory('hsv', 'bgr') - -bgr2hls = convert_color_factory('bgr', 'hls') - -hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/geometric.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/geometric.py deleted file mode 100755 index cf97c201c..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/geometric.py +++ /dev/null @@ -1,728 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers - -import cv2 -import numpy as np - -from ..utils import to_2tuple -from .io import imread_backend - -try: - from PIL import Image -except ImportError: - Image = None - - -def _scale_size(size, scale): - """Rescale a size by a ratio. - - Args: - size (tuple[int]): (w, h). - scale (float | tuple(float)): Scaling factor. - - Returns: - tuple[int]: scaled size. - """ - if isinstance(scale, (float, int)): - scale = (scale, scale) - w, h = size - return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) - - -cv2_interp_codes = { - 'nearest': cv2.INTER_NEAREST, - 'bilinear': cv2.INTER_LINEAR, - 'bicubic': cv2.INTER_CUBIC, - 'area': cv2.INTER_AREA, - 'lanczos': cv2.INTER_LANCZOS4 -} - -if Image is not None: - pillow_interp_codes = { - 'nearest': Image.NEAREST, - 'bilinear': Image.BILINEAR, - 'bicubic': Image.BICUBIC, - 'box': Image.BOX, - 'lanczos': Image.LANCZOS, - 'hamming': Image.HAMMING - } - - -def imresize(img, - size, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image to a given size. - - Args: - img (ndarray): The input image. - size (tuple[int]): Target size (w, h). - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if backend is None: - backend = imread_backend - if backend not in ['cv2', 'pillow']: - raise ValueError(f'backend: {backend} is not supported for resize.' - f"Supported backends are 'cv2', 'pillow'") - - if backend == 'pillow': - assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' - pil_image = Image.fromarray(img) - pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) - resized_img = np.array(pil_image) - else: - resized_img = cv2.resize( - img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) - if not return_scale: - return resized_img - else: - w_scale = size[0] / w - h_scale = size[1] / h - return resized_img, w_scale, h_scale - - -def imresize_to_multiple(img, - divisor, - size=None, - scale_factor=None, - keep_ratio=False, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image according to a given size or scale factor and then rounds - up the the resized or rescaled image size to the nearest value that can be - divided by the divisor. - - Args: - img (ndarray): The input image. - divisor (int | tuple): Resized image size will be a multiple of - divisor. If divisor is a tuple, divisor should be - (w_divisor, h_divisor). - size (None | int | tuple[int]): Target size (w, h). Default: None. - scale_factor (None | float | tuple[float]): Multiplier for spatial - size. Should match input size if it is a tuple and the 2D style is - (w_scale_factor, h_scale_factor). Default: None. - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: False. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if size is not None and scale_factor is not None: - raise ValueError('only one of size or scale_factor should be defined') - elif size is None and scale_factor is None: - raise ValueError('one of size or scale_factor should be defined') - elif size is not None: - size = to_2tuple(size) - if keep_ratio: - size = rescale_size((w, h), size, return_scale=False) - else: - size = _scale_size((w, h), scale_factor) - - divisor = to_2tuple(divisor) - size = tuple([int(np.ceil(s / d)) * d for s, d in zip(size, divisor)]) - resized_img, w_scale, h_scale = imresize( - img, - size, - return_scale=True, - interpolation=interpolation, - out=out, - backend=backend) - if return_scale: - return resized_img, w_scale, h_scale - else: - return resized_img - - -def imresize_like(img, - dst_img, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image to the same size of a given image. - - Args: - img (ndarray): The input image. - dst_img (ndarray): The target image. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = dst_img.shape[:2] - return imresize(img, (w, h), return_scale, interpolation, backend=backend) - - -def rescale_size(old_size, scale, return_scale=False): - """Calculate the new size to be rescaled to. - - Args: - old_size (tuple[int]): The old size (w, h) of image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image size. - - Returns: - tuple[int]: The new rescaled image size. - """ - w, h = old_size - if isinstance(scale, (float, int)): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - scale_factor = scale - elif isinstance(scale, tuple): - max_long_edge = max(scale) - max_short_edge = min(scale) - scale_factor = min(max_long_edge / max(h, w), - max_short_edge / min(h, w)) - else: - raise TypeError( - f'Scale must be a number or tuple of int, but got {type(scale)}') - - new_size = _scale_size((w, h), scale_factor) - - if return_scale: - return new_size, scale_factor - else: - return new_size - - -def imrescale(img, - scale, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image while keeping the aspect ratio. - - Args: - img (ndarray): The input image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - ndarray: The rescaled image. - """ - h, w = img.shape[:2] - new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) - rescaled_img = imresize( - img, new_size, interpolation=interpolation, backend=backend) - if return_scale: - return rescaled_img, scale_factor - else: - return rescaled_img - - -def imflip(img, direction='horizontal'): - """Flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image. - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return np.flip(img, axis=1) - elif direction == 'vertical': - return np.flip(img, axis=0) - else: - return np.flip(img, axis=(0, 1)) - - -def imflip_(img, direction='horizontal'): - """Inplace flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image (inplace). - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return cv2.flip(img, 1, img) - elif direction == 'vertical': - return cv2.flip(img, 0, img) - else: - return cv2.flip(img, -1, img) - - -def imrotate(img, - angle, - center=None, - scale=1.0, - border_value=0, - interpolation='bilinear', - auto_bound=False): - """Rotate an image. - - Args: - img (ndarray): Image to be rotated. - angle (float): Rotation angle in degrees, positive values mean - clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. - scale (float): Isotropic scale factor. - border_value (int): Border value. - interpolation (str): Same as :func:`resize`. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. - - Returns: - ndarray: The rotated image. - """ - if center is not None and auto_bound: - raise ValueError('`auto_bound` conflicts with `center`') - h, w = img.shape[:2] - if center is None: - center = ((w - 1) * 0.5, (h - 1) * 0.5) - assert isinstance(center, tuple) - - matrix = cv2.getRotationMatrix2D(center, -angle, scale) - if auto_bound: - cos = np.abs(matrix[0, 0]) - sin = np.abs(matrix[0, 1]) - new_w = h * sin + w * cos - new_h = h * cos + w * sin - matrix[0, 2] += (new_w - w) * 0.5 - matrix[1, 2] += (new_h - h) * 0.5 - w = int(np.round(new_w)) - h = int(np.round(new_h)) - rotated = cv2.warpAffine( - img, - matrix, (w, h), - flags=cv2_interp_codes[interpolation], - borderValue=border_value) - return rotated - - -def bbox_clip(bboxes, img_shape): - """Clip bboxes to fit the image shape. - - Args: - bboxes (ndarray): Shape (..., 4*k) - img_shape (tuple[int]): (height, width) of the image. - - Returns: - ndarray: Clipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) - cmin[0::2] = img_shape[1] - 1 - cmin[1::2] = img_shape[0] - 1 - clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) - return clipped_bboxes - - -def bbox_scaling(bboxes, scale, clip_shape=None): - """Scaling bboxes w.r.t the box center. - - Args: - bboxes (ndarray): Shape(..., 4). - scale (float): Scaling factor. - clip_shape (tuple[int], optional): If specified, bboxes that exceed the - boundary will be clipped according to the given shape (h, w). - - Returns: - ndarray: Scaled bboxes. - """ - if float(scale) == 1.0: - scaled_bboxes = bboxes.copy() - else: - w = bboxes[..., 2] - bboxes[..., 0] + 1 - h = bboxes[..., 3] - bboxes[..., 1] + 1 - dw = (w * (scale - 1)) * 0.5 - dh = (h * (scale - 1)) * 0.5 - scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) - if clip_shape is not None: - return bbox_clip(scaled_bboxes, clip_shape) - else: - return scaled_bboxes - - -def imcrop(img, bboxes, scale=1.0, pad_fill=None): - """Crop image patches. - - 3 steps: scale the bboxes -> clip bboxes -> crop and pad. - - Args: - img (ndarray): Image to be cropped. - bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. - scale (float, optional): Scale ratio of bboxes, the default value - 1.0 means no padding. - pad_fill (Number | list[Number]): Value to be filled for padding. - Default: None, which means no padding. - - Returns: - list[ndarray] | ndarray: The cropped image patches. - """ - chn = 1 if img.ndim == 2 else img.shape[2] - if pad_fill is not None: - if isinstance(pad_fill, (int, float)): - pad_fill = [pad_fill for _ in range(chn)] - assert len(pad_fill) == chn - - _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes - scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) - clipped_bbox = bbox_clip(scaled_bboxes, img.shape) - - patches = [] - for i in range(clipped_bbox.shape[0]): - x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) - if pad_fill is None: - patch = img[y1:y2 + 1, x1:x2 + 1, ...] - else: - _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) - if chn == 1: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) - else: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) - patch = np.array( - pad_fill, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - x_start = 0 if _x1 >= 0 else -_x1 - y_start = 0 if _y1 >= 0 else -_y1 - w = x2 - x1 + 1 - h = y2 - y1 + 1 - patch[y_start:y_start + h, x_start:x_start + w, - ...] = img[y1:y1 + h, x1:x1 + w, ...] - patches.append(patch) - - if bboxes.ndim == 1: - return patches[0] - else: - return patches - - -def impad(img, - *, - shape=None, - padding=None, - pad_val=0, - padding_mode='constant'): - """Pad the given image to a certain shape or pad on all sides with - specified padding mode and padding value. - - Args: - img (ndarray): Image to be padded. - shape (tuple[int]): Expected padding shape (h, w). Default: None. - padding (int or tuple[int]): Padding on each border. If a single int is - provided this is used to pad all borders. If tuple of length 2 is - provided this is the padding on left/right and top/bottom - respectively. If a tuple of length 4 is provided this is the - padding for the left, top, right and bottom borders respectively. - Default: None. Note that `shape` and `padding` can not be both - set. - pad_val (Number | Sequence[Number]): Values to be filled in padding - areas when padding_mode is 'constant'. Default: 0. - padding_mode (str): Type of padding. Should be: constant, edge, - reflect or symmetric. Default: constant. - - - constant: pads with a constant value, this value is specified - with pad_val. - - edge: pads with the last value at the edge of the image. - - reflect: pads with reflection of image without repeating the - last value on the edge. For example, padding [1, 2, 3, 4] - with 2 elements on both sides in reflect mode will result - in [3, 2, 1, 2, 3, 4, 3, 2]. - - symmetric: pads with reflection of image repeating the last - value on the edge. For example, padding [1, 2, 3, 4] with - 2 elements on both sides in symmetric mode will result in - [2, 1, 1, 2, 3, 4, 4, 3] - - Returns: - ndarray: The padded image. - """ - - assert (shape is not None) ^ (padding is not None) - if shape is not None: - padding = (0, 0, shape[1] - img.shape[1], shape[0] - img.shape[0]) - - # check pad_val - if isinstance(pad_val, tuple): - assert len(pad_val) == img.shape[-1] - elif not isinstance(pad_val, numbers.Number): - raise TypeError('pad_val must be a int or a tuple. ' - f'But received {type(pad_val)}') - - # check padding - if isinstance(padding, tuple) and len(padding) in [2, 4]: - if len(padding) == 2: - padding = (padding[0], padding[1], padding[0], padding[1]) - elif isinstance(padding, numbers.Number): - padding = (padding, padding, padding, padding) - else: - raise ValueError('Padding must be a int or a 2, or 4 element tuple.' - f'But received {padding}') - - # check padding mode - assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] - - border_type = { - 'constant': cv2.BORDER_CONSTANT, - 'edge': cv2.BORDER_REPLICATE, - 'reflect': cv2.BORDER_REFLECT_101, - 'symmetric': cv2.BORDER_REFLECT - } - img = cv2.copyMakeBorder( - img, - padding[1], - padding[3], - padding[0], - padding[2], - border_type[padding_mode], - value=pad_val) - - return img - - -def impad_to_multiple(img, divisor, pad_val=0): - """Pad an image to ensure each edge to be multiple to some number. - - Args: - img (ndarray): Image to be padded. - divisor (int): Padded image edges will be multiple to divisor. - pad_val (Number | Sequence[Number]): Same as :func:`impad`. - - Returns: - ndarray: The padded image. - """ - pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor - pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor - return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) - - -def cutout(img, shape, pad_val=0): - """Randomly cut out a rectangle from the original img. - - Args: - img (ndarray): Image to be cutout. - shape (int | tuple[int]): Expected cutout shape (h, w). If given as a - int, the value will be used for both h and w. - pad_val (int | float | tuple[int | float]): Values to be filled in the - cut area. Defaults to 0. - - Returns: - ndarray: The cutout image. - """ - - channels = 1 if img.ndim == 2 else img.shape[2] - if isinstance(shape, int): - cut_h, cut_w = shape, shape - else: - assert isinstance(shape, tuple) and len(shape) == 2, \ - f'shape must be a int or a tuple with length 2, but got type ' \ - f'{type(shape)} instead.' - cut_h, cut_w = shape - if isinstance(pad_val, (int, float)): - pad_val = tuple([pad_val] * channels) - elif isinstance(pad_val, tuple): - assert len(pad_val) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(pad_val), channels) - else: - raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') - - img_h, img_w = img.shape[:2] - y0 = np.random.uniform(img_h) - x0 = np.random.uniform(img_w) - - y1 = int(max(0, y0 - cut_h / 2.)) - x1 = int(max(0, x0 - cut_w / 2.)) - y2 = min(img_h, y1 + cut_h) - x2 = min(img_w, x1 + cut_w) - - if img.ndim == 2: - patch_shape = (y2 - y1, x2 - x1) - else: - patch_shape = (y2 - y1, x2 - x1, channels) - - img_cutout = img.copy() - patch = np.array( - pad_val, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - img_cutout[y1:y2, x1:x2, ...] = patch - - return img_cutout - - -def _get_shear_matrix(magnitude, direction='horizontal'): - """Generate the shear matrix for transformation. - - Args: - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - - Returns: - ndarray: The shear matrix with dtype float32. - """ - if direction == 'horizontal': - shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) - elif direction == 'vertical': - shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) - return shear_matrix - - -def imshear(img, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear an image. - - Args: - img (ndarray): Image to be sheared with format (h, w) - or (h, w, c). - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The sheared image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`') - shear_matrix = _get_shear_matrix(magnitude, direction) - sheared = cv2.warpAffine( - img, - shear_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. shearing masks whose channels large - # than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return sheared - - -def _get_translate_matrix(offset, direction='horizontal'): - """Generate the translate matrix. - - Args: - offset (int | float): The offset used for translate. - direction (str): The translate direction, either - "horizontal" or "vertical". - - Returns: - ndarray: The translate matrix with dtype float32. - """ - if direction == 'horizontal': - translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) - elif direction == 'vertical': - translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) - return translate_matrix - - -def imtranslate(img, - offset, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Translate an image. - - Args: - img (ndarray): Image to be translated with format - (h, w) or (h, w, c). - offset (int | float): The offset used for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The translated image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`.') - translate_matrix = _get_translate_matrix(offset, direction) - translated = cv2.warpAffine( - img, - translate_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. translating masks whose channels - # large than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return translated diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/io.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/io.py deleted file mode 100755 index d47aaa845..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/io.py +++ /dev/null @@ -1,258 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os.path as osp -from pathlib import Path - -import cv2 -import numpy as np -from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, - IMREAD_UNCHANGED) - -from mmcv.utils import check_file_exist, is_str, mkdir_or_exist - -try: - from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG -except ImportError: - TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None - -try: - from PIL import Image, ImageOps -except ImportError: - Image = None - -try: - import tifffile -except ImportError: - tifffile = None - -jpeg = None -supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] - -imread_flags = { - 'color': IMREAD_COLOR, - 'grayscale': IMREAD_GRAYSCALE, - 'unchanged': IMREAD_UNCHANGED, - 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, - 'grayscale_ignore_orientation': - IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE -} - -imread_backend = 'cv2' - - -def use_backend(backend): - """Select a backend for image decoding. - - Args: - backend (str): The image decoding backend type. Options are `cv2`, - `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) - and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` - file format. - """ - assert backend in supported_backends - global imread_backend - imread_backend = backend - if imread_backend == 'turbojpeg': - if TurboJPEG is None: - raise ImportError('`PyTurboJPEG` is not installed') - global jpeg - if jpeg is None: - jpeg = TurboJPEG() - elif imread_backend == 'pillow': - if Image is None: - raise ImportError('`Pillow` is not installed') - elif imread_backend == 'tifffile': - if tifffile is None: - raise ImportError('`tifffile` is not installed') - - -def _jpegflag(flag='color', channel_order='bgr'): - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'color': - if channel_order == 'bgr': - return TJPF_BGR - elif channel_order == 'rgb': - return TJCS_RGB - elif flag == 'grayscale': - return TJPF_GRAY - else: - raise ValueError('flag must be "color" or "grayscale"') - - -def _pillow2array(img, flag='color', channel_order='bgr'): - """Convert a pillow image to numpy array. - - Args: - img (:obj:`PIL.Image.Image`): The image loaded using PIL - flag (str): Flags specifying the color type of a loaded image, - candidates are 'color', 'grayscale' and 'unchanged'. - Default to 'color'. - channel_order (str): The channel order of the output image array, - candidates are 'bgr' and 'rgb'. Default to 'bgr'. - - Returns: - np.ndarray: The converted numpy array - """ - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'unchanged': - array = np.array(img) - if array.ndim >= 3 and array.shape[2] >= 3: # color image - array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR - else: - # Handle exif orientation tag - if flag in ['color', 'grayscale']: - img = ImageOps.exif_transpose(img) - # If the image mode is not 'RGB', convert it to 'RGB' first. - if img.mode != 'RGB': - if img.mode != 'LA': - # Most formats except 'LA' can be directly converted to RGB - img = img.convert('RGB') - else: - # When the mode is 'LA', the default conversion will fill in - # the canvas with black, which sometimes shadows black objects - # in the foreground. - # - # Therefore, a random color (124, 117, 104) is used for canvas - img_rgba = img.convert('RGBA') - img = Image.new('RGB', img_rgba.size, (124, 117, 104)) - img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha - if flag in ['color', 'color_ignore_orientation']: - array = np.array(img) - if channel_order != 'rgb': - array = array[:, :, ::-1] # RGB to BGR - elif flag in ['grayscale', 'grayscale_ignore_orientation']: - img = img.convert('L') - array = np.array(img) - else: - raise ValueError( - 'flag must be "color", "grayscale", "unchanged", ' - f'"color_ignore_orientation" or "grayscale_ignore_orientation"' - f' but got {flag}') - return array - - -def imread(img_or_path, flag='color', channel_order='bgr', backend=None): - """Read an image. - - Args: - img_or_path (ndarray or str or Path): Either a numpy array or str or - pathlib.Path. If it is a numpy array (loaded image), then - it will be returned as is. - flag (str): Flags specifying the color type of a loaded image, - candidates are `color`, `grayscale`, `unchanged`, - `color_ignore_orientation` and `grayscale_ignore_orientation`. - By default, `cv2` and `pillow` backend would rotate the image - according to its EXIF info unless called with `unchanged` or - `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend - always ignore image's EXIF info regardless of the flag. - The `turbojpeg` backend only supports `color` and `grayscale`. - channel_order (str): Order of channel, candidates are `bgr` and `rgb`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. - If backend is None, the global imread_backend specified by - ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - ndarray: Loaded image array. - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError(f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow'") - if isinstance(img_or_path, Path): - img_or_path = str(img_or_path) - - if isinstance(img_or_path, np.ndarray): - return img_or_path - elif is_str(img_or_path): - check_file_exist(img_or_path, - f'img file does not exist: {img_or_path}') - if backend == 'turbojpeg': - with open(img_or_path, 'rb') as in_file: - img = jpeg.decode(in_file.read(), - _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - img = Image.open(img_or_path) - img = _pillow2array(img, flag, channel_order) - return img - elif backend == 'tifffile': - img = tifffile.imread(img_or_path) - return img - else: - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imread(img_or_path, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - else: - raise TypeError('"img" must be a numpy array or a str or ' - 'a pathlib.Path object') - - -def imfrombytes(content, flag='color', channel_order='bgr', backend=None): - """Read an image from bytes. - - Args: - content (bytes): Image bytes got from files or other streams. - flag (str): Same as :func:`imread`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `None`. If backend is None, the - global imread_backend specified by ``mmcv.use_backend()`` will be - used. Default: None. - - Returns: - ndarray: Loaded image array. - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError(f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow'") - if backend == 'turbojpeg': - img = jpeg.decode(content, _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - buff = io.BytesIO(content) - img = Image.open(buff) - img = _pillow2array(img, flag, channel_order) - return img - else: - img_np = np.frombuffer(content, np.uint8) - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imdecode(img_np, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - - -def imwrite(img, file_path, params=None, auto_mkdir=True): - """Write image to file. - - Args: - img (ndarray): Image array to be written. - file_path (str): Image file path. - params (None or list): Same as opencv :func:`imwrite` interface. - auto_mkdir (bool): If the parent folder of `file_path` does not exist, - whether to create it automatically. - - Returns: - bool: Successful or not. - """ - if auto_mkdir: - dir_name = osp.abspath(osp.dirname(file_path)) - mkdir_or_exist(dir_name) - return cv2.imwrite(file_path, img, params) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/misc.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/misc.py deleted file mode 100755 index dfc4a9c6e..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/misc.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - -import mmcv - -try: - import torch -except ImportError: - torch = None - - -def tensor2imgs(tensor, mean=(0, 0, 0), std=(1, 1, 1), to_rgb=True): - """Convert tensor to 3-channel images. - - Args: - tensor (torch.Tensor): Tensor that contains multiple images, shape ( - N, C, H, W). - mean (tuple[float], optional): Mean of images. Defaults to (0, 0, 0). - std (tuple[float], optional): Standard deviation of images. - Defaults to (1, 1, 1). - to_rgb (bool, optional): Whether the tensor was converted to RGB - format in the first place. If so, convert it back to BGR. - Defaults to True. - - Returns: - list[np.ndarray]: A list that contains multiple images. - """ - - if torch is None: - raise RuntimeError('pytorch is not installed') - assert torch.is_tensor(tensor) and tensor.ndim == 4 - assert len(mean) == 3 - assert len(std) == 3 - - num_imgs = tensor.size(0) - mean = np.array(mean, dtype=np.float32) - std = np.array(std, dtype=np.float32) - imgs = [] - for img_id in range(num_imgs): - img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) - img = mmcv.imdenormalize( - img, mean, std, to_bgr=to_rgb).astype(np.uint8) - imgs.append(np.ascontiguousarray(img)) - return imgs diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/photometric.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/photometric.py deleted file mode 100755 index 5085d0120..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/image/photometric.py +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - -from ..utils import is_tuple_of -from .colorspace import bgr2gray, gray2bgr - - -def imnormalize(img, mean, std, to_rgb=True): - """Normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - img = img.copy().astype(np.float32) - return imnormalize_(img, mean, std, to_rgb) - - -def imnormalize_(img, mean, std, to_rgb=True): - """Inplace normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - # cv2 inplace normalization does not accept uint8 - assert img.dtype != np.uint8 - mean = np.float64(mean.reshape(1, -1)) - stdinv = 1 / np.float64(std.reshape(1, -1)) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - cv2.subtract(img, mean, img) # inplace - cv2.multiply(img, stdinv, img) # inplace - return img - - -def imdenormalize(img, mean, std, to_bgr=True): - assert img.dtype != np.uint8 - mean = mean.reshape(1, -1).astype(np.float64) - std = std.reshape(1, -1).astype(np.float64) - img = cv2.multiply(img, std) # make a copy - cv2.add(img, mean, img) # inplace - if to_bgr: - cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace - return img - - -def iminvert(img): - """Invert (negate) an image. - - Args: - img (ndarray): Image to be inverted. - - Returns: - ndarray: The inverted image. - """ - return np.full_like(img, 255) - img - - -def solarize(img, thr=128): - """Solarize an image (invert all pixel values above a threshold) - - Args: - img (ndarray): Image to be solarized. - thr (int): Threshold for solarizing (0 - 255). - - Returns: - ndarray: The solarized image. - """ - img = np.where(img < thr, img, 255 - img) - return img - - -def posterize(img, bits): - """Posterize an image (reduce the number of bits for each color channel) - - Args: - img (ndarray): Image to be posterized. - bits (int): Number of bits (1 to 8) to use for posterizing. - - Returns: - ndarray: The posterized image. - """ - shift = 8 - bits - img = np.left_shift(np.right_shift(img, shift), shift) - return img - - -def adjust_color(img, alpha=1, beta=None, gamma=0): - r"""It blends the source image and its gray image: - - .. math:: - output = img * alpha + gray\_img * beta + gamma - - Args: - img (ndarray): The input source image. - alpha (int | float): Weight for the source image. Default 1. - beta (int | float): Weight for the converted gray image. - If None, it's assigned the value (1 - `alpha`). - gamma (int | float): Scalar added to each sum. - Same as :func:`cv2.addWeighted`. Default 0. - - Returns: - ndarray: Colored image which has the same size and dtype as input. - """ - gray_img = bgr2gray(img) - gray_img = np.tile(gray_img[..., None], [1, 1, 3]) - if beta is None: - beta = 1 - alpha - colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) - if not colored_img.dtype == np.uint8: - # Note when the dtype of `img` is not the default `np.uint8` - # (e.g. np.float32), the value in `colored_img` got from cv2 - # is not guaranteed to be in range [0, 255], so here clip - # is needed. - colored_img = np.clip(colored_img, 0, 255) - return colored_img - - -def imequalize(img): - """Equalize the image histogram. - - This function applies a non-linear mapping to the input image, - in order to create a uniform distribution of grayscale values - in the output image. - - Args: - img (ndarray): Image to be equalized. - - Returns: - ndarray: The equalized image. - """ - - def _scale_channel(im, c): - """Scale the data in the corresponding channel.""" - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # For computing the step, filter out the nonzeros. - nonzero_histo = histo[histo > 0] - step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 - if not step: - lut = np.array(range(256)) - else: - # Compute the cumulative sum, shifted by step // 2 - # and then normalized by step. - lut = (np.cumsum(histo) + (step // 2)) // step - # Shift lut, prepending with 0. - lut = np.concatenate([[0], lut[:-1]], 0) - # handle potential integer overflow - lut[lut > 255] = 255 - # If step is zero, return the original image. - # Otherwise, index from lut. - return np.where(np.equal(step, 0), im, lut[im]) - - # Scales each channel independently and then stacks - # the result. - s1 = _scale_channel(img, 0) - s2 = _scale_channel(img, 1) - s3 = _scale_channel(img, 2) - equalized_img = np.stack([s1, s2, s3], axis=-1) - return equalized_img.astype(img.dtype) - - -def adjust_brightness(img, factor=1.): - """Adjust image brightness. - - This function controls the brightness of an image. An - enhancement factor of 0.0 gives a black image. - A factor of 1.0 gives the original image. This function - blends the source image and the degenerated black image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be brightened. - factor (float): A value controls the enhancement. - Factor 1.0 returns the original image, lower - factors mean less color (brightness, contrast, - etc), and higher values more. Default 1. - - Returns: - ndarray: The brightened image. - """ - degenerated = np.zeros_like(img) - # Note manually convert the dtype to np.float32, to - # achieve as close results as PIL.ImageEnhance.Brightness. - # Set beta=1-factor, and gamma=0 - brightened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - brightened_img = np.clip(brightened_img, 0, 255) - return brightened_img.astype(img.dtype) - - -def adjust_contrast(img, factor=1.): - """Adjust image contrast. - - This function controls the contrast of an image. An - enhancement factor of 0.0 gives a solid grey - image. A factor of 1.0 gives the original image. It - blends the source image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be contrasted. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - - Returns: - ndarray: The contrasted image. - """ - gray_img = bgr2gray(img) - hist = np.histogram(gray_img, 256, (0, 255))[0] - mean = round(np.sum(gray_img) / np.sum(hist)) - degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) - degenerated = gray2bgr(degenerated) - contrasted_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - contrasted_img = np.clip(contrasted_img, 0, 255) - return contrasted_img.astype(img.dtype) - - -def auto_contrast(img, cutoff=0): - """Auto adjust image contrast. - - This function maximize (normalize) image contrast by first removing cutoff - percent of the lightest and darkest pixels from the histogram and remapping - the image so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - Args: - img (ndarray): Image to be contrasted. BGR order. - cutoff (int | float | tuple): The cutoff percent of the lightest and - darkest pixels to be removed. If given as tuple, it shall be - (low, high). Otherwise, the single value will be used for both. - Defaults to 0. - - Returns: - ndarray: The contrasted image. - """ - - def _auto_contrast_channel(im, c, cutoff): - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # Remove cut-off percent pixels from histo - histo_sum = np.cumsum(histo) - cut_low = histo_sum[-1] * cutoff[0] // 100 - cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 - histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low - histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) - - # Compute mapping - low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] - # If all the values have been cut off, return the origin img - if low >= high: - return im - scale = 255.0 / (high - low) - offset = -low * scale - lut = np.array(range(256)) - lut = lut * scale + offset - lut = np.clip(lut, 0, 255) - return lut[im] - - if isinstance(cutoff, (int, float)): - cutoff = (cutoff, cutoff) - else: - assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ - f'float or tuple, but got {type(cutoff)} instead.' - # Auto adjusts contrast for each channel independently and then stacks - # the result. - s1 = _auto_contrast_channel(img, 0, cutoff) - s2 = _auto_contrast_channel(img, 1, cutoff) - s3 = _auto_contrast_channel(img, 2, cutoff) - contrasted_img = np.stack([s1, s2, s3], axis=-1) - return contrasted_img.astype(img.dtype) - - -def adjust_sharpness(img, factor=1., kernel=None): - """Adjust image sharpness. - - This function controls the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image. A - factor of 1.0 gives the original image. And a factor - of 2.0 gives a sharpened image. It blends the source - image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be sharpened. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - kernel (np.ndarray, optional): Filter kernel to be applied on the img - to obtain the degenerated img. Defaults to None. - - Note: - No value sanity check is enforced on the kernel set by users. So with - an inappropriate kernel, the ``adjust_sharpness`` may fail to perform - the function its name indicates but end up performing whatever - transform determined by the kernel. - - Returns: - ndarray: The sharpened image. - """ - - if kernel is None: - # adopted from PIL.ImageFilter.SMOOTH - kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 - assert isinstance(kernel, np.ndarray), \ - f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' - assert kernel.ndim == 2, \ - f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' - - degenerated = cv2.filter2D(img, -1, kernel) - sharpened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - sharpened_img = np.clip(sharpened_img, 0, 255) - return sharpened_img.astype(img.dtype) - - -def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): - """AlexNet-style PCA jitter. - - This data augmentation is proposed in `ImageNet Classification with Deep - Convolutional Neural Networks - `_. - - Args: - img (ndarray): Image to be adjusted lighting. BGR order. - eigval (ndarray): the eigenvalue of the convariance matrix of pixel - values, respectively. - eigvec (ndarray): the eigenvector of the convariance matrix of pixel - values, respectively. - alphastd (float): The standard deviation for distribution of alpha. - Defaults to 0.1 - to_rgb (bool): Whether to convert img to rgb. - - Returns: - ndarray: The adjusted image. - """ - assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ - f'eigval and eigvec should both be of type np.ndarray, got ' \ - f'{type(eigval)} and {type(eigvec)} instead.' - - assert eigval.ndim == 1 and eigvec.ndim == 2 - assert eigvec.shape == (3, eigval.shape[0]) - n_eigval = eigval.shape[0] - assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ - f'got {type(alphastd)} instead.' - - img = img.copy().astype(np.float32) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - - alpha = np.random.normal(0, alphastd, n_eigval) - alter = eigvec \ - * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ - * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) - alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) - img_adjusted = img + alter - return img_adjusted - - -def lut_transform(img, lut_table): - """Transform array by look-up table. - - The function lut_transform fills the output array with values from the - look-up table. Indices of the entries are taken from the input array. - - Args: - img (ndarray): Image to be transformed. - lut_table (ndarray): look-up table of 256 elements; in case of - multi-channel input array, the table should either have a single - channel (in this case the same table is used for all channels) or - the same number of channels as in the input array. - - Returns: - ndarray: The transformed image. - """ - assert isinstance(img, np.ndarray) - assert 0 <= np.min(img) and np.max(img) <= 255 - assert isinstance(lut_table, np.ndarray) - assert lut_table.shape == (256, ) - - return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) - - -def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - img (ndarray): Image to be processed. - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - - Returns: - ndarray: The processed image. - """ - assert isinstance(img, np.ndarray) - assert img.ndim == 2 - assert isinstance(clip_limit, (float, int)) - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - - clahe = cv2.createCLAHE(clip_limit, tile_grid_size) - return clahe.apply(np.array(img, dtype=np.uint8)) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/__init__.py deleted file mode 100755 index b53f3843b..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/__init__.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# from .assign_score_withk import assign_score_withk -# from .ball_query import ball_query -# from .bbox import bbox_overlaps -# from .border_align import BorderAlign, border_align -# from .box_iou_rotated import box_iou_rotated -# from .carafe import CARAFE, CARAFENaive, CARAFEPack, carafe, carafe_naive -# from .cc_attention import CrissCrossAttention -# from .contour_expand import contour_expand -# from .corner_pool import CornerPool -# from .correlation import Correlation -# from .deform_conv import DeformConv2d, DeformConv2dPack, deform_conv2d -# from .deform_roi_pool import (DeformRoIPool, DeformRoIPoolPack, -# ModulatedDeformRoIPoolPack, deform_roi_pool) -from .deprecated_wrappers import Conv2d_deprecated as Conv2d -from .deprecated_wrappers import ConvTranspose2d_deprecated as ConvTranspose2d -from .deprecated_wrappers import Linear_deprecated as Linear -from .deprecated_wrappers import MaxPool2d_deprecated as MaxPool2d -from .focal_loss import (SigmoidFocalLoss, SoftmaxFocalLoss, - sigmoid_focal_loss, softmax_focal_loss) -# from .furthest_point_sample import (furthest_point_sample, -# furthest_point_sample_with_dist) -# from .fused_bias_leakyrelu import FusedBiasLeakyReLU, fused_bias_leakyrelu -# from .gather_points import gather_points -# from .group_points import GroupAll, QueryAndGroup, grouping_operation -from .info import (get_compiler_version, get_compiling_cuda_version, - get_onnxruntime_op_path) -# from .iou3d import boxes_iou_bev, nms_bev, nms_normal_bev -# from .knn import knn -# from .masked_conv import MaskedConv2d, masked_conv2d -# from .modulated_deform_conv import (ModulatedDeformConv2d, -# ModulatedDeformConv2dPack, -# modulated_deform_conv2d) -# from .multi_scale_deform_attn import MultiScaleDeformableAttention -# from .nms import batched_nms, nms, nms_match, nms_rotated, soft_nms -# from .pixel_group import pixel_group -# from .point_sample import (SimpleRoIAlign, point_sample, -# rel_roi_point_to_rel_img_point) -# from .points_in_boxes import (points_in_boxes_all, points_in_boxes_cpu, -# points_in_boxes_part) -# from .points_sampler import PointsSampler -# from .psa_mask import PSAMask -# from .roi_align import RoIAlign, roi_align -# from .roi_align_rotated import RoIAlignRotated, roi_align_rotated -# from .roi_pool import RoIPool, roi_pool -# from .roiaware_pool3d import RoIAwarePool3d -# from .roipoint_pool3d import RoIPointPool3d -# from .saconv import SAConv2d -# from .scatter_points import DynamicScatter, dynamic_scatter -from .sync_bn import SyncBatchNorm -# from .three_interpolate import three_interpolate -# from .three_nn import three_nn -# from .tin_shift import TINShift, tin_shift -# from .upfirdn2d import upfirdn2d -# from .voxelize import Voxelization, voxelization - -# __all__ = [ -# 'bbox_overlaps', 'CARAFE', 'CARAFENaive', 'CARAFEPack', 'carafe', -# 'carafe_naive', 'CornerPool', 'DeformConv2d', 'DeformConv2dPack', -# 'deform_conv2d', 'DeformRoIPool', 'DeformRoIPoolPack', -# 'ModulatedDeformRoIPoolPack', 'deform_roi_pool', 'SigmoidFocalLoss', -# 'SoftmaxFocalLoss', 'sigmoid_focal_loss', 'softmax_focal_loss', -# 'get_compiler_version', 'get_compiling_cuda_version', -# 'get_onnxruntime_op_path', 'MaskedConv2d', 'masked_conv2d', -# 'ModulatedDeformConv2d', 'ModulatedDeformConv2dPack', -# 'modulated_deform_conv2d', 'batched_nms', 'nms', 'soft_nms', 'nms_match', -# 'RoIAlign', 'roi_align', 'RoIPool', 'roi_pool', 'SyncBatchNorm', 'Conv2d', -# 'ConvTranspose2d', 'Linear', 'MaxPool2d', 'CrissCrossAttention', 'PSAMask', -# 'point_sample', 'rel_roi_point_to_rel_img_point', 'SimpleRoIAlign', -# 'SAConv2d', 'TINShift', 'tin_shift', 'assign_score_withk', -# 'box_iou_rotated', 'RoIPointPool3d', 'nms_rotated', 'knn', 'ball_query', -# 'upfirdn2d', 'FusedBiasLeakyReLU', 'fused_bias_leakyrelu', -# 'RoIAlignRotated', 'roi_align_rotated', 'pixel_group', 'QueryAndGroup', -# 'GroupAll', 'grouping_operation', 'contour_expand', 'three_nn', -# 'three_interpolate', 'MultiScaleDeformableAttention', 'BorderAlign', -# 'border_align', 'gather_points', 'furthest_point_sample', -# 'furthest_point_sample_with_dist', 'PointsSampler', 'Correlation', -# 'boxes_iou_bev', 'nms_bev', 'nms_normal_bev', 'Voxelization', -# 'voxelization', 'dynamic_scatter', 'DynamicScatter', 'RoIAwarePool3d', -# 'points_in_boxes_part', 'points_in_boxes_cpu', 'points_in_boxes_all' -# ] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/README.md b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/README.md deleted file mode 100755 index 91c237f3d..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/README.md +++ /dev/null @@ -1,169 +0,0 @@ -# Code Structure of CUDA operators - -This folder contains all non-python code for MMCV custom ops. Please follow the same architecture if you want to add new ops. - -## Directories Tree - -```folder -. -├── common -│ ├── box_iou_rotated_utils.hpp -│ ├── parrots_cpp_helper.hpp -│ ├── parrots_cuda_helper.hpp -│ ├── pytorch_cpp_helper.hpp -│ ├── pytorch_cuda_helper.hpp -│   └── cuda -│   ├── common_cuda_helper.hpp -│   ├── parrots_cudawarpfunction.cuh -│   ├── ... -│   └── ops_cuda_kernel.cuh -├── onnxruntime -│   ├── onnxruntime_register.h -│   ├── onnxruntime_session_options_config_keys.h -│   ├── ort_mmcv_utils.h -│   ├── ... -│   ├── onnx_ops.h -│   └── cpu -│ ├── onnxruntime_register.cpp -│      ├── ... -│      └── onnx_ops_impl.cpp -├── parrots -│   ├── ... -│   ├── ops.cpp -│   ├── ops_parrots.cpp -│   └── ops_pytorch.h -├── pytorch -│   ├── info.cpp -│   ├── pybind.cpp -│   ├── ... -│   ├── ops.cpp -│   └── cuda -│      ├── ... -│      └── ops_cuda.cu -└── tensorrt - ├── trt_cuda_helper.cuh - ├── trt_plugin_helper.hpp - ├── trt_plugin.hpp - ├── trt_serialize.hpp - ├── ... - ├── trt_ops.hpp - └── plugins -    ├── trt_cuda_helper.cu -    ├── trt_plugin.cpp -    ├── ... -    ├── trt_ops.cpp -    └── trt_ops_kernel.cu -``` - -## Components - -- `common`: This directory contains all tools and shared codes. - - `cuda`: The cuda kernels which can be shared by all backends. **HIP** kernel is also here since they have similar syntax. -- `onnxruntime`: **ONNX Runtime** support for custom ops. - - `cpu`: CPU implementation of supported ops. -- `parrots`: **Parrots** is a deep learning frame for model training and inference. Parrots custom ops are placed in this directory. -- `pytorch`: **PyTorch** custom ops are supported by binding C++ to Python with **pybind11**. The ops implementation and binding codes are placed in this directory. - - `cuda`: This directory contains cuda kernel launchers, which feed memory pointers of tensor to the cuda kernel in `common/cuda`. The launchers provide c++ interface of cuda implementation of corresponding custom ops. -- `tensorrt`: **TensorRT** support for custom ops. - - `plugins`: This directory contains the implementation of the supported custom ops. Some ops might also use shared cuda kernel in `common/cuda`. - -## How to add new PyTorch ops? - -1. (Optional) Add shared kernel in `common` to support special hardware platform. - - ```c++ - // src/common/cuda/new_ops_cuda_kernel.cuh - - template - __global__ void new_ops_forward_cuda_kernel(const T* input, T* output, ...) { - // forward here - } - - ``` - - Add cuda kernel launcher in `pytorch/cuda`. - - ```c++ - // src/pytorch/cuda - #include - - void NewOpsForwardCUDAKernelLauncher(Tensor input, Tensor output, ...){ - // initialize - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - ... - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "new_ops_forward_cuda_kernel", ([&] { - new_ops_forward_cuda_kernel - <<>>( - input.data_ptr(), output.data_ptr(),...); - })); - AT_CUDA_CHECK(cudaGetLastError()); - } - ``` - -2. Add ops implementation in `pytorch` directory. Select different implementations according to device type. - - ```c++ - // src/pytorch/new_ops.cpp - #ifdef MMCV_WITH_CUDA - Tensor new_ops_forward_cuda(Tensor input, Tensor output, ...){ - // implement cuda forward here - // use `NewOpsForwardCUDAKernelLauncher` here - } - #else - - Tensor new_ops_forward_cpu(Tensor input, Tensor output, ...){ - // implement cpu forward here - } - - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...){ - // select implementation by input device type - if (boxes.device().is_cuda()) { - #ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(output); - return new_ops_forward_cuda(input, output, ...); - #else - AT_ERROR("new ops is not compiled with GPU support"); - #endif - } else { - CHECK_CPU_INPUT(input); - CHECK_CPU_INPUT(output); - return new_ops_forward_cpu(input, output, ...); - } - } - ``` - -3. Binding the implementation in `pytorch/pybind.cpp` - - ```c++ - // src/pytorch/pybind.cpp - - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...); - - ... - - // bind with pybind11 - m.def("new_ops_forward", &new_ops_forward, "new_ops_forward", - py::arg("input"), py::arg("output"), ...); - - ... - - ``` - -4. Build MMCV again. Enjoy new ops in python - - ```python - from ..utils import ext_loader - ext_module = ext_loader.load_ext('_ext', ['new_ops_forward']) - - ... - - ext_module.new_ops_forward(input, output, ...) - - ``` diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp deleted file mode 100755 index a1e926adb..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef COMMON_CUDA_HELPER -#define COMMON_CUDA_HELPER - -#include - -#define CUDA_1D_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) - -#define THREADS_PER_BLOCK 512 - -#define DIVUP(m, n) ((m) / (n) + ((m) % (n) > 0)) - -inline int GET_BLOCKS(const int N) { - int optimal_block_num = (N + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; - int max_block_num = 4096; - return std::min(optimal_block_num, max_block_num); -} - -template -__device__ T bilinear_interpolate(const T* input, const int height, - const int width, T y, T x, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) return 0; - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - int y_low = (int)y; - int x_low = (int)x; - int y_high; - int x_high; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - // do bilinear interpolation - T v1 = input[y_low * width + x_low]; - T v2 = input[y_low * width + x_high]; - T v3 = input[y_high * width + x_low]; - T v4 = input[y_high * width + x_high]; - T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - return val; -} - -template -__device__ void bilinear_interpolate_gradient( - const int height, const int width, T y, T x, T& w1, T& w2, T& w3, T& w4, - int& x_low, int& x_high, int& y_low, int& y_high, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - w1 = w2 = w3 = w4 = 0.; - x_low = x_high = y_low = y_high = -1; - return; - } - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - y_low = (int)y; - x_low = (int)x; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - - // reference in forward - // T v1 = input[y_low * width + x_low]; - // T v2 = input[y_low * width + x_high]; - // T v3 = input[y_high * width + x_low]; - // T v4 = input[y_high * width + x_high]; - // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - return; -} -#endif // COMMON_CUDA_HELPER diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh deleted file mode 100755 index 1eb5f8fcc..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sigmoid_focal_loss_forward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* output, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + expf(-input[index])); - - // (1 - p)**gamma * log(p) - T term_p = pow(((T)1. - p), gamma) * log(max(p, (T)FLT_MIN)); - // p**gamma * log(1 - p) - T term_n = pow(p, gamma) * log(max((T)1. - p, (T)FLT_MIN)); - - output[index] = (T)0.; - output[index] += -flag_p * alpha * term_p; - output[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - output[index] *= weight[t]; - } - } -} - -template -__global__ void sigmoid_focal_loss_backward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* grad_input, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + exp(-input[index])); - - // (1 - p)**gamma * (1 - p - gamma*p*log(p)) - T term_p = pow(((T)1. - p), gamma) * - ((T)1. - p - (gamma * p * log(max(p, (T)FLT_MIN)))); - // p**gamma * (gamma * (1 - p) * log(1 - p) - p) - T term_n = pow(p, gamma) * - (gamma * ((T)1. - p) * log(max((T)1. - p, (T)FLT_MIN)) - p); - - grad_input[index] = (T)0.; - grad_input[index] += -flag_p * alpha * term_p; - grad_input[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - grad_input[index] *= weight[t]; - } - } -} - -#endif // SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh deleted file mode 100755 index 631b2c617..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void softmax_focal_loss_forward_cuda_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* output, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - output[index] = - -alpha * pow((T)1. - pred, gamma) * log(max(pred, (T)FLT_MIN)); - } else { - output[index] = 0; - } - if (weight != NULL) { - output[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda1_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* buff, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - buff[index] = alpha * (-pow((T)1. - pred, gamma) + - gamma * pow((T)1. - pred, gamma - 1) * pred * - log(max(pred, (T)FLT_MIN))); - } else { - buff[index] = 0; - } - if (weight != NULL) { - buff[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda2_kernel( - const int nthreads, const T* softmax, const int64_t* target, const T* buff, - T* grad_input, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - int64_t label = target[n]; - - if (label >= 0) { - T flag = (label == c ? (T)1. : (T)0.); - grad_input[index] = buff[n] * (flag - softmax[index]); - } else { - grad_input[index] = 0; - } - } -} - -#endif // SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh deleted file mode 100755 index 4ec6a4668..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SYNCBN_CUDA_KERNEL_CUH -#define SYNCBN_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sync_bn_forward_mean_cuda_kernel(const T *input, float *mean, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += input[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_mean_cuda_kernel(const phalf *input, - float *mean, int num, - int channels, int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += static_cast(input[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_var_cuda_kernel(const T *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = input[index] - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_var_cuda_kernel(const phalf *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = static_cast(input[index]) - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_output_cuda_kernel( - const T *input, const float *mean, const float *var, float *running_mean, - float *running_var, const float *weight, const float *bias, float *norm, - float *std, T *output, int num, int channels, int spatial, float eps, - float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = (input[index] - mean_value) / std_value; - output[index] = norm[index] * weight_value + bias_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - (input[index] - mean_value) / std_value * weight_value + bias_value; - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = norm[index] = (input[index] - mean_value) / std_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = (input[index] - mean_value) / std_value; - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template <> -__global__ void sync_bn_forward_output_cuda_kernel( - const phalf *input, const float *mean, const float *var, - float *running_mean, float *running_var, const float *weight, - const float *bias, float *norm, float *std, phalf *output, int num, - int channels, int spatial, float eps, float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = - static_cast(norm[index] * weight_value + bias_value); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - static_cast((static_cast(input[index]) - mean_value) / - std_value * weight_value + - bias_value); - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = static_cast(norm[index]); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = static_cast( - (static_cast(input[index]) - mean_value) / std_value); - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template -__global__ void sync_bn_backward_param_cuda_kernel(const T *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += grad_output[index] * norm[index]; - buffer2[tid] += grad_output[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template <> -__global__ void sync_bn_backward_param_cuda_kernel(const phalf *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += static_cast(grad_output[index]) * norm[index]; - buffer2[tid] += static_cast(grad_output[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const T *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, T *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = - weight[c] * - (grad_output[index] - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]; - } -} - -template <> -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const phalf *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, phalf *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = static_cast( - weight[c] * - (static_cast(grad_output[index]) - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]); - } -} - -#endif // SYNCBN_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp deleted file mode 100755 index c7f9f35b7..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef PYTORCH_CPP_HELPER -#define PYTORCH_CPP_HELPER -#include - -#include - -using namespace at; - -#define DIVUP(m, n) ((m) / (n) + ((m) % (n) > 0)) - -#define CHECK_CUDA(x) \ - TORCH_CHECK(x.device().is_cuda(), #x " must be a CUDA tensor") -#define CHECK_CPU(x) \ - TORCH_CHECK(!x.device().is_cuda(), #x " must be a CPU tensor") -#define CHECK_CONTIGUOUS(x) \ - TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") -#define CHECK_CUDA_INPUT(x) \ - CHECK_CUDA(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_CPU_INPUT(x) \ - CHECK_CPU(x); \ - CHECK_CONTIGUOUS(x) - -#endif // PYTORCH_CPP_HELPER diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp deleted file mode 100755 index 9869b535f..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PYTORCH_CUDA_HELPER -#define PYTORCH_CUDA_HELPER - -#include -#include -#include - -#include -#include - -#include "common_cuda_helper.hpp" - -using at::Half; -using at::Tensor; -using phalf = at::Half; - -#define __PHALF(x) (x) - -#endif // PYTORCH_CUDA_HELPER diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu deleted file mode 100755 index cb899f954..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sigmoid_focal_loss_cuda_kernel.cuh" -#include "softmax_focal_loss_cuda_kernel.cuh" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = input.size(1); - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_forward_cuda_kernel", [&] { - sigmoid_focal_loss_forward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha) { - int output_size = grad_input.numel(); - int num_classes = input.size(1); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_backward_cuda_kernel", [&] { - sigmoid_focal_loss_backward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - grad_input.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = softmax.size(1); - - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(softmax.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - softmax.scalar_type(), "softmax_focal_loss_forward_cuda_kernel", [&] { - softmax_focal_loss_forward_cuda_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha) { - int num_classes = softmax.size(1); - - int output_size = buff.numel(); - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda1_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda1_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - buff.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); - - output_size = grad_input.numel(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda2_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda2_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), buff.data_ptr(), - grad_input.data_ptr(), num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu deleted file mode 100755 index 657c81701..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sync_bn_cuda_kernel.cuh" - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_mean_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_var_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), num, channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_output_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), running_mean.data_ptr(), - running_var.data_ptr(), weight.data_ptr(), - bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), output.data_ptr(), num, - channels, spatial, eps, momentum, group_size); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias) { - int num = grad_output.size(0); - int channels = grad_output.size(1); - int spatial = grad_output.size(2); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_param_cuda_kernel", [&] { - sync_bn_backward_param_cuda_kernel - <<>>( - grad_output.data_ptr(), norm.data_ptr(), - grad_weight.data_ptr(), grad_bias.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - int output_size = grad_input.numel(); - int num = grad_input.size(0); - int channels = grad_input.size(1); - int spatial = grad_input.size(2); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_data_cuda_kernel", [&] { - sync_bn_backward_data_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - weight.data_ptr(), grad_weight.data_ptr(), - grad_bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), grad_input.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp deleted file mode 100755 index 3e2c92b27..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha); - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha); - -void sigmoid_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SigmoidFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void sigmoid_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - SigmoidFocalLossBackwardCUDAKernelLauncher(input, target, weight, grad_input, - gamma, alpha); -} - -void softmax_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SoftmaxFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void softmax_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - SoftmaxFocalLossBackwardCUDAKernelLauncher(input, target, weight, buff, - grad_input, gamma, alpha); -} -#endif - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(target); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(output); - - sigmoid_focal_loss_forward_cuda(input, target, weight, output, gamma, - alpha); -#else - AT_ERROR("SigmoidFocalLoss is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SigmoidFocalLoss is not implemented on CPU"); - } -} - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(target); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(grad_input); - - sigmoid_focal_loss_backward_cuda(input, target, weight, grad_input, gamma, - alpha); -#else - AT_ERROR("SigmoidFocalLoss is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SigmoidFocalLoss is not implemented on CPU"); - } -} - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(target); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(output); - - softmax_focal_loss_forward_cuda(input, target, weight, output, gamma, - alpha); -#else - AT_ERROR("SoftmaxFocalLoss is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SoftmaxFocalLoss is not implemented on CPU"); - } -} - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(target); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(buff); - CHECK_CUDA_INPUT(grad_input); - - softmax_focal_loss_backward_cuda(input, target, weight, buff, grad_input, - gamma, alpha); -#else - AT_ERROR("SoftmaxFocalLoss is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SoftmaxFocalLoss is not implemented on CPU"); - } -} diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp deleted file mode 100755 index a08d227d4..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// modified from -// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/vision.cpp -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF -#include -int get_cudart_version() { return CUDART_VERSION; } -#endif -#endif - -std::string get_compiling_cuda_version() { -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF - std::ostringstream oss; - // copied from - // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/cuda/detail/CUDAHooks.cpp#L231 - auto printCudaStyleVersion = [&](int v) { - oss << (v / 1000) << "." << (v / 10 % 100); - if (v % 10 != 0) { - oss << "." << (v % 10); - } - }; - printCudaStyleVersion(get_cudart_version()); - return oss.str(); -#else - return std::string("rocm not available"); -#endif -#else - return std::string("not available"); -#endif -} - -// similar to -// https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/Version.cpp -std::string get_compiler_version() { - std::ostringstream ss; -#if defined(__GNUC__) -#ifndef __clang__ - { ss << "GCC " << __GNUC__ << "." << __GNUC_MINOR__; } -#endif -#endif - -#if defined(__clang_major__) - { - ss << "clang " << __clang_major__ << "." << __clang_minor__ << "." - << __clang_patchlevel__; - } -#endif - -#if defined(_MSC_VER) - { ss << "MSVC " << _MSC_FULL_VER; } -#endif - return ss.str(); -} diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp deleted file mode 100755 index a94b9db83..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp +++ /dev/null @@ -1,687 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -std::string get_compiler_version(); -std::string get_compiling_cuda_version(); - -// void assign_score_withk_forward(const Tensor &points, const Tensor ¢ers, -// const Tensor &scores, const Tensor &knn_idx, -// Tensor &output, int B, int N0, int N1, int M, -// int K, int O, int aggregate); - -// void assign_score_withk_backward(const Tensor &grad_out, const Tensor &points, -// const Tensor ¢ers, const Tensor &scores, -// const Tensor &knn_idx, Tensor &grad_points, -// Tensor &grad_centers, Tensor &grad_scores, -// int B, int N0, int N1, int M, int K, int O, -// int aggregate); - -// void carafe_naive_forward(Tensor features, Tensor masks, Tensor output, -// int kernel_size, int group_size, int scale_factor); - -// void carafe_naive_backward(Tensor top_grad, Tensor features, Tensor masks, -// Tensor bottom_grad, Tensor mask_grad, -// int kernel_size, int group_size, int scale_factor); - -// void carafe_forward(Tensor features, Tensor masks, Tensor rfeatures, -// Tensor routput, Tensor rmasks, Tensor output, -// int kernel_size, int group_size, int scale_factor); - -// void carafe_backward(Tensor top_grad, Tensor rfeatures, Tensor masks, -// Tensor rtop_grad, Tensor rbottom_grad_hs, -// Tensor rbottom_grad, Tensor rmask_grad, Tensor bottom_grad, -// Tensor mask_grad, int kernel_size, int group_size, -// int scale_factor); - -// void deform_conv_forward(Tensor input, Tensor weight, Tensor offset, -// Tensor output, Tensor columns, Tensor ones, int kW, -// int kH, int dW, int dH, int padW, int padH, -// int dilationW, int dilationH, int group, -// int deformable_group, int im2col_step); - -// void deform_conv_backward_input(Tensor input, Tensor offset, Tensor gradOutput, -// Tensor gradInput, Tensor gradOffset, -// Tensor weight, Tensor columns, int kW, int kH, -// int dW, int dH, int padW, int padH, -// int dilationW, int dilationH, int group, -// int deformable_group, int im2col_step); - -// void deform_conv_backward_parameters(Tensor input, Tensor offset, -// Tensor gradOutput, Tensor gradWeight, -// Tensor columns, Tensor ones, int kW, -// int kH, int dW, int dH, int padW, int padH, -// int dilationW, int dilationH, int group, -// int deformable_group, float scale, -// int im2col_step); - -// void deform_roi_pool_forward(Tensor input, Tensor rois, Tensor offset, -// Tensor output, int pooled_height, int pooled_width, -// float spatial_scale, int sampling_ratio, -// float gamma); - -// void deform_roi_pool_backward(Tensor grad_output, Tensor input, Tensor rois, -// Tensor offset, Tensor grad_input, -// Tensor grad_offset, int pooled_height, -// int pooled_width, float spatial_scale, -// int sampling_ratio, float gamma); - -// void group_points_forward(int b, int c, int n, int npoints, int nsample, -// Tensor points_tensor, Tensor idx_tensor, -// Tensor out_tensor); - -// void group_points_backward(int b, int c, int n, int npoints, int nsample, -// Tensor grad_out_tensor, Tensor idx_tensor, -// Tensor grad_points_tensor); - -// void roipoint_pool3d_forward(Tensor xyz, Tensor boxes3d, Tensor pts_feature, -// Tensor pooled_features, Tensor pooled_empty_flag); - -// void gather_points_forward(Tensor points_tensor, Tensor idx_tensor, -// Tensor out_tensor, int b, int c, int n, int npoints); - -// void gather_points_backward(Tensor grad_out_tensor, Tensor idx_tensor, -// Tensor grad_points_tensor, int b, int c, int n, -// int npoints); - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha); - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha); - -// void three_interpolate_forward(Tensor points_tensor, Tensor idx_tensor, -// Tensor weight_tensor, Tensor out_tensor, int b, -// int c, int m, int n); - -// void three_interpolate_backward(Tensor grad_out_tensor, Tensor idx_tensor, -// Tensor weight_tensor, Tensor grad_points_tensor, -// int b, int c, int n, int m); - -// void three_nn_forward(Tensor unknown_tensor, Tensor known_tensor, -// Tensor dist2_tensor, Tensor idx_tensor, int b, int n, -// int m); - -// void bbox_overlaps(const Tensor bboxes1, const Tensor bboxes2, Tensor ious, -// const int mode, const bool aligned, const int offset); - -// void knn_forward(Tensor xyz_tensor, Tensor new_xyz_tensor, Tensor idx_tensor, -// Tensor dist2_tensor, int b, int n, int m, int nsample); -// void iou3d_boxes_overlap_bev_forward(Tensor boxes_a, Tensor boxes_b, -// Tensor ans_overlap); - -// void iou3d_boxes_iou_bev_forward(Tensor boxes_a, Tensor boxes_b, -// Tensor ans_iou); - -// int iou3d_nms_forward(Tensor boxes, Tensor keep, float nms_overlap_thresh); - -// int iou3d_nms_normal_forward(Tensor boxes, Tensor keep, -// float nms_overlap_thresh); - -// void furthest_point_sampling_forward(Tensor points_tensor, Tensor temp_tensor, -// Tensor idx_tensor, int b, int n, int m); - -// void furthest_point_sampling_with_dist_forward(Tensor points_tensor, -// Tensor temp_tensor, -// Tensor idx_tensor, int b, int n, -// int m); - -// void masked_im2col_forward(const Tensor im, const Tensor mask_h_idx, -// const Tensor mask_w_idx, Tensor col, -// const int kernel_h, const int kernel_w, -// const int pad_h, const int pad_w); - -// void masked_col2im_forward(const Tensor col, const Tensor mask_h_idx, -// const Tensor mask_w_idx, Tensor im, int height, -// int width, int channels); - -// void modulated_deform_conv_forward( -// Tensor input, Tensor weight, Tensor bias, Tensor ones, Tensor offset, -// Tensor mask, Tensor output, Tensor columns, int kernel_h, int kernel_w, -// const int stride_h, const int stride_w, const int pad_h, const int pad_w, -// const int dilation_h, const int dilation_w, const int group, -// const int deformable_group, const bool with_bias); - -// void modulated_deform_conv_backward( -// Tensor input, Tensor weight, Tensor bias, Tensor ones, Tensor offset, -// Tensor mask, Tensor columns, Tensor grad_input, Tensor grad_weight, -// Tensor grad_bias, Tensor grad_offset, Tensor grad_mask, Tensor grad_output, -// int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, -// int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, -// const bool with_bias); - -// Tensor ms_deform_attn_forward(const Tensor &value, const Tensor &spatial_shapes, -// const Tensor &level_start_index, -// const Tensor &sampling_loc, -// const Tensor &attn_weight, const int im2col_step); - -// void ms_deform_attn_backward(const Tensor &value, const Tensor &spatial_shapes, -// const Tensor &level_start_index, -// const Tensor &sampling_loc, -// const Tensor &attn_weight, -// const Tensor &grad_output, Tensor &grad_value, -// Tensor &grad_sampling_loc, -// Tensor &grad_attn_weight, const int im2col_step); - -// Tensor nms(Tensor boxes, Tensor scores, float iou_threshold, int offset); - -// Tensor softnms(Tensor boxes, Tensor scores, Tensor dets, float iou_threshold, -// float sigma, float min_score, int method, int offset); - -// std::vector> nms_match(Tensor dets, float iou_threshold); - -// std::vector> pixel_group( -// Tensor score, Tensor mask, Tensor embedding, Tensor kernel_label, -// Tensor kernel_contour, int kernel_region_num, float distance_threshold); - -// std::vector> contour_expand(Tensor kernel_mask, -// Tensor internal_kernel_label, -// int min_kernel_area, -// int kernel_num); - -// void roi_align_forward(Tensor input, Tensor rois, Tensor output, -// Tensor argmax_y, Tensor argmax_x, int aligned_height, -// int aligned_width, float spatial_scale, -// int sampling_ratio, int pool_mode, bool aligned); - -// void roi_align_backward(Tensor grad_output, Tensor rois, Tensor argmax_y, -// Tensor argmax_x, Tensor grad_input, int aligned_height, -// int aligned_width, float spatial_scale, -// int sampling_ratio, int pool_mode, bool aligned); - -// void roi_pool_forward(Tensor input, Tensor rois, Tensor output, Tensor argmax, -// int pooled_height, int pooled_width, float spatial_scale); - -// void roi_pool_backward(Tensor grad_output, Tensor rois, Tensor argmax, -// Tensor grad_input, int pooled_height, int pooled_width, -// float spatial_scale); - -void sync_bn_forward_mean(const Tensor input, Tensor mean); - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var); - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -// void psamask_forward(const Tensor input, Tensor output, const int psa_type, -// const int num_, const int h_feature, const int w_feature, -// const int h_mask, const int w_mask, const int half_h_mask, -// const int half_w_mask); - -// void psamask_backward(Tensor grad_output, const Tensor grad_input, -// const int psa_type, const int num_, const int h_feature, -// const int w_feature, const int h_mask, const int w_mask, -// const int half_h_mask, const int half_w_mask); - -// void tin_shift_forward(Tensor input, Tensor shift, Tensor output); - -// void tin_shift_backward(Tensor grad_output, Tensor shift, Tensor grad_input); - -// void ball_query_forward(Tensor new_xyz_tensor, Tensor xyz_tensor, -// Tensor idx_tensor, int b, int n, int m, -// float min_radius, float max_radius, int nsample); - -// Tensor bottom_pool_forward(Tensor input); - -// Tensor bottom_pool_backward(Tensor input, Tensor grad_output); - -// Tensor left_pool_forward(Tensor input); - -// Tensor left_pool_backward(Tensor input, Tensor grad_output); - -// Tensor right_pool_forward(Tensor input); - -// Tensor right_pool_backward(Tensor input, Tensor grad_output); - -// Tensor top_pool_forward(Tensor input); - -// Tensor top_pool_backward(Tensor input, Tensor grad_output); - -// void box_iou_rotated(const Tensor boxes1, const Tensor boxes2, Tensor ious, -// const int mode_flag, const bool aligned); - -// Tensor nms_rotated(const Tensor dets, const Tensor scores, const Tensor order, -// const Tensor dets_sorted, const float iou_threshold, -// const int multi_label); - -// Tensor upfirdn2d(const Tensor &input, const Tensor &kernel, int up_x, int up_y, -// int down_x, int down_y, int pad_x0, int pad_x1, int pad_y0, -// int pad_y1); - -// Tensor fused_bias_leakyrelu(const Tensor &input, const Tensor &bias, -// const Tensor &refer, int act, int grad, float alpha, -// float scale); - -// void roi_align_rotated_forward(Tensor input, Tensor rois, Tensor output, -// int pooled_height, int pooled_width, -// float spatial_scale, int sample_num, -// bool aligned, bool clockwise); - -// void roi_align_rotated_backward(Tensor grad_output, Tensor rois, -// Tensor grad_input, int pooled_height, -// int pooled_width, float spatial_scale, -// int sample_num, bool aligned, bool clockwise); - -// std::vector dynamic_point_to_voxel_forward( -// const torch::Tensor &feats, const torch::Tensor &coors, -// const std::string &reduce_type); - -// void dynamic_point_to_voxel_backward(torch::Tensor &grad_feats, -// const torch::Tensor &grad_reduced_feats, -// const torch::Tensor &feats, -// const torch::Tensor &reduced_feats, -// const torch::Tensor &coors_idx, -// const torch::Tensor &reduce_count, -// const std::string &reduce_type); - -// int hard_voxelize_forward(const at::Tensor &points, at::Tensor &voxels, -// at::Tensor &coors, at::Tensor &num_points_per_voxel, -// const std::vector voxel_size, -// const std::vector coors_range, -// const int max_points, const int max_voxels, -// const int NDim); - -// void dynamic_voxelize_forward(const at::Tensor &points, at::Tensor &coors, -// const std::vector voxel_size, -// const std::vector coors_range, -// const int NDim); - -// void border_align_forward(const Tensor &input, const Tensor &boxes, -// Tensor output, Tensor argmax_idx, -// const int pool_size); - -// void border_align_backward(const Tensor &grad_output, const Tensor &boxes, -// const Tensor &argmax_idx, Tensor grad_input, -// const int pool_size); - -// void points_in_boxes_cpu_forward(Tensor boxes_tensor, Tensor pts_tensor, -// Tensor pts_indices_tensor); - -// void points_in_boxes_part_forward(Tensor boxes_tensor, Tensor pts_tensor, -// Tensor box_idx_of_points_tensor); - -// void points_in_boxes_all_forward(Tensor boxes_tensor, Tensor pts_tensor, -// Tensor box_idx_of_points_tensor); - -// void roiaware_pool3d_forward(Tensor rois, Tensor pts, Tensor pts_feature, -// Tensor argmax, Tensor pts_idx_of_voxels, -// Tensor pooled_features, int pool_method); - -// void roiaware_pool3d_backward(Tensor pts_idx_of_voxels, Tensor argmax, -// Tensor grad_out, Tensor grad_in, int pool_method); - -// void correlation_forward(Tensor input1, Tensor input2, Tensor output, int kH, -// int kW, int patchH, int patchW, int padH, int padW, -// int dilationH, int dilationW, int dilation_patchH, -// int dilation_patchW, int dH, int dW); - -// void correlation_backward(Tensor grad_output, Tensor input1, Tensor input2, -// Tensor grad_input1, Tensor grad_input2, int kH, -// int kW, int patchH, int patchW, int padH, int padW, -// int dilationH, int dilationW, int dilation_patchH, -// int dilation_patchW, int dH, int dW); - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { -// m.def("upfirdn2d", &upfirdn2d, "upfirdn2d (CUDA)", py::arg("input"), -// py::arg("kernel"), py::arg("up_x"), py::arg("up_y"), py::arg("down_x"), -// py::arg("down_y"), py::arg("pad_x0"), py::arg("pad_x1"), -// py::arg("pad_y0"), py::arg("pad_y1")); -// m.def("fused_bias_leakyrelu", &fused_bias_leakyrelu, -// "fused_bias_leakyrelu (CUDA)", py::arg("input"), py::arg("bias"), -// py::arg("empty"), py::arg("act"), py::arg("grad"), py::arg("alpha"), -// py::arg("scale")); -// m.def("gather_points_forward", &gather_points_forward, -// "gather_points_forward", py::arg("points_tensor"), -// py::arg("idx_tensor"), py::arg("out_tensor"), py::arg("b"), -// py::arg("c"), py::arg("n"), py::arg("npoints")); -// m.def("gather_points_backward", &gather_points_backward, -// "gather_points_backward", py::arg("grad_out_tensor"), -// py::arg("idx_tensor"), py::arg("grad_points_tensor"), py::arg("b"), -// py::arg("c"), py::arg("n"), py::arg("npoints")); - m.def("get_compiler_version", &get_compiler_version, "get_compiler_version"); - m.def("get_compiling_cuda_version", &get_compiling_cuda_version, - "get_compiling_cuda_version"); -// m.def("assign_score_withk_forward", &assign_score_withk_forward, -// "assign_score_withk_forward", py::arg("points"), py::arg("centers"), -// py::arg("scores"), py::arg("knn_idx"), py::arg("output"), py::arg("B"), -// py::arg("N0"), py::arg("N1"), py::arg("M"), py::arg("K"), py::arg("O"), -// py::arg("aggregate")); -// m.def("assign_score_withk_backward", &assign_score_withk_backward, -// "assign_score_withk_backward", py::arg("grad_out"), py::arg("points"), -// py::arg("centers"), py::arg("scores"), py::arg("knn_idx"), -// py::arg("grad_points"), py::arg("grad_centers"), py::arg("grad_scores"), -// py::arg("B"), py::arg("N0"), py::arg("N1"), py::arg("M"), py::arg("K"), -// py::arg("O"), py::arg("aggregate")); -// m.def("knn_forward", &knn_forward, "knn_forward", py::arg("xyz_tensor"), -// py::arg("new_xyz_tensor"), py::arg("idx_tensor"), -// py::arg("dist2_tensor"), py::arg("b"), py::arg("n"), py::arg("m"), -// py::arg("nsample")); -// m.def("carafe_naive_forward", &carafe_naive_forward, "carafe_naive_forward", -// py::arg("features"), py::arg("masks"), py::arg("output"), -// py::arg("kernel_size"), py::arg("group_size"), py::arg("scale_factor")); -// m.def("carafe_naive_backward", &carafe_naive_backward, -// "carafe_naive_backward", py::arg("top_grad"), py::arg("features"), -// py::arg("masks"), py::arg("bottom_grad"), py::arg("mask_grad"), -// py::arg("kernel_size"), py::arg("group_size"), py::arg("scale_factor")); -// m.def("carafe_forward", &carafe_forward, "carafe_forward", -// py::arg("features"), py::arg("masks"), py::arg("rfeatures"), -// py::arg("routput"), py::arg("rmasks"), py::arg("output"), -// py::arg("kernel_size"), py::arg("group_size"), py::arg("scale_factor")); -// m.def("carafe_backward", &carafe_backward, "carafe_backward", -// py::arg("top_grad"), py::arg("rfeatures"), py::arg("masks"), -// py::arg("rtop_grad"), py::arg("rbottom_grad_hs"), -// py::arg("rbottom_grad"), py::arg("rmask_grad"), py::arg("bottom_grad"), -// py::arg("mask_grad"), py::arg("kernel_size"), py::arg("group_size"), -// py::arg("scale_factor")); -// m.def("deform_conv_forward", &deform_conv_forward, "deform_conv_forward", -// py::arg("input"), py::arg("weight"), py::arg("offset"), -// py::arg("output"), py::arg("columns"), py::arg("ones"), py::arg("kW"), -// py::arg("kH"), py::arg("dW"), py::arg("dH"), py::arg("padH"), -// py::arg("padW"), py::arg("dilationW"), py::arg("dilationH"), -// py::arg("group"), py::arg("deformable_group"), py::arg("im2col_step")); -// m.def("deform_conv_backward_input", &deform_conv_backward_input, -// "deform_conv_backward_input", py::arg("input"), py::arg("offset"), -// py::arg("gradOutput"), py::arg("gradInput"), py::arg("gradOffset"), -// py::arg("weight"), py::arg("columns"), py::arg("kW"), py::arg("kH"), -// py::arg("dW"), py::arg("dH"), py::arg("padH"), py::arg("padW"), -// py::arg("dilationW"), py::arg("dilationH"), py::arg("group"), -// py::arg("deformable_group"), py::arg("im2col_step")); -// m.def("deform_conv_backward_parameters", &deform_conv_backward_parameters, -// "deform_conv_backward_parameters", py::arg("input"), py::arg("offset"), -// py::arg("gradOutput"), py::arg("gradWeight"), py::arg("columns"), -// py::arg("ones"), py::arg("kW"), py::arg("kH"), py::arg("dW"), -// py::arg("dH"), py::arg("padH"), py::arg("padW"), py::arg("dilationW"), -// py::arg("dilationH"), py::arg("group"), py::arg("deformable_group"), -// py::arg("scale"), py::arg("im2col_step")); -// m.def("deform_roi_pool_forward", &deform_roi_pool_forward, -// "deform roi pool forward", py::arg("input"), py::arg("rois"), -// py::arg("offset"), py::arg("output"), py::arg("pooled_height"), -// py::arg("pooled_width"), py::arg("spatial_scale"), -// py::arg("sampling_ratio"), py::arg("gamma")); -// m.def("deform_roi_pool_backward", &deform_roi_pool_backward, -// "deform roi pool backward", py::arg("grad_output"), py::arg("input"), -// py::arg("rois"), py::arg("offset"), py::arg("grad_input"), -// py::arg("grad_offset"), py::arg("pooled_height"), -// py::arg("pooled_width"), py::arg("spatial_scale"), -// py::arg("sampling_ratio"), py::arg("gamma")); -// m.def("roipoint_pool3d_forward", &roipoint_pool3d_forward, -// "roipoint_pool3d_forward", py::arg("xyz"), py::arg("boxes3d"), -// py::arg("pts_feature"), py::arg("pooled_features"), -// py::arg("pooled_empty_flag")); - m.def("sigmoid_focal_loss_forward", &sigmoid_focal_loss_forward, - "sigmoid_focal_loss_forward ", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("sigmoid_focal_loss_backward", &sigmoid_focal_loss_backward, - "sigmoid_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("grad_input"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_forward", &softmax_focal_loss_forward, - "softmax_focal_loss_forward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_backward", &softmax_focal_loss_backward, - "softmax_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("buff"), py::arg("grad_input"), - py::arg("gamma"), py::arg("alpha")); -// m.def("three_interpolate_forward", &three_interpolate_forward, -// "three_interpolate_forward", py::arg("points_tensor"), -// py::arg("idx_tensor"), py::arg("weight_tensor"), py::arg("out_tensor"), -// py::arg("b"), py::arg("c"), py::arg("m"), py::arg("n")); -// m.def("three_interpolate_backward", &three_interpolate_backward, -// "three_interpolate_backward", py::arg("grad_out_tensor"), -// py::arg("idx_tensor"), py::arg("weight_tensor"), -// py::arg("grad_points_tensor"), py::arg("b"), py::arg("c"), py::arg("n"), -// py::arg("m")); -// m.def("three_nn_forward", &three_nn_forward, "three_nn_forward", -// py::arg("unknown_tensor"), py::arg("known_tensor"), -// py::arg("dist2_tensor"), py::arg("idx_tensor"), py::arg("b"), -// py::arg("n"), py::arg("m")); -// m.def("bbox_overlaps", &bbox_overlaps, "bbox_overlaps", py::arg("bboxes1"), -// py::arg("bboxes2"), py::arg("ious"), py::arg("mode"), -// py::arg("aligned"), py::arg("offset")); -// m.def("group_points_forward", &group_points_forward, "group_points_forward", -// py::arg("b"), py::arg("c"), py::arg("n"), py::arg("npoints"), -// py::arg("nsample"), py::arg("points_tensor"), py::arg("idx_tensor"), -// py::arg("out_tensor")); -// m.def("group_points_backward", &group_points_backward, -// "group_points_backward", py::arg("b"), py::arg("c"), py::arg("n"), -// py::arg("npoints"), py::arg("nsample"), py::arg("grad_out_tensor"), -// py::arg("idx_tensor"), py::arg("grad_points_tensor")); -// m.def("knn_forward", &knn_forward, "knn_forward", py::arg("b"), py::arg("n"), -// py::arg("m"), py::arg("nsample"), py::arg("xyz_tensor"), -// py::arg("new_xyz_tensor"), py::arg("idx_tensor"), -// py::arg("dist2_tensor")); -// m.def("iou3d_boxes_overlap_bev_forward", &iou3d_boxes_overlap_bev_forward, -// "iou3d_boxes_overlap_bev_forward", py::arg("boxes_a"), -// py::arg("boxes_b"), py::arg("ans_overlap")); -// m.def("iou3d_boxes_iou_bev_forward", &iou3d_boxes_iou_bev_forward, -// "iou3d_boxes_iou_bev_forward", py::arg("boxes_a"), py::arg("boxes_b"), -// py::arg("ans_iou")); -// m.def("iou3d_nms_forward", &iou3d_nms_forward, "iou3d_nms_forward", -// py::arg("boxes"), py::arg("keep"), py::arg("nms_overlap_thresh")); -// m.def("iou3d_nms_normal_forward", &iou3d_nms_normal_forward, -// "iou3d_nms_normal_forward", py::arg("boxes"), py::arg("keep"), -// py::arg("nms_overlap_thresh")); -// m.def("furthest_point_sampling_forward", &furthest_point_sampling_forward, -// "furthest_point_sampling_forward", py::arg("points_tensor"), -// py::arg("temp_tensor"), py::arg("idx_tensor"), py::arg("b"), -// py::arg("n"), py::arg("m")); -// m.def("furthest_point_sampling_with_dist_forward", -// &furthest_point_sampling_with_dist_forward, -// "furthest_point_sampling_with_dist_forward", py::arg("points_tensor"), -// py::arg("temp_tensor"), py::arg("idx_tensor"), py::arg("b"), -// py::arg("n"), py::arg("m")); -// m.def("masked_im2col_forward", &masked_im2col_forward, -// "masked_im2col_forward", py::arg("im"), py::arg("mask_h_idx"), -// py::arg("mask_w_idx"), py::arg("col"), py::arg("kernel_h"), -// py::arg("kernel_w"), py::arg("pad_h"), py::arg("pad_w")); -// m.def("masked_col2im_forward", &masked_col2im_forward, -// "masked_col2im_forward", py::arg("col"), py::arg("mask_h_idx"), -// py::arg("mask_w_idx"), py::arg("im"), py::arg("height"), -// py::arg("width"), py::arg("channels")); -// m.def("modulated_deform_conv_forward", &modulated_deform_conv_forward, -// "modulated deform conv forward", py::arg("input"), py::arg("weight"), -// py::arg("bias"), py::arg("ones"), py::arg("offset"), py::arg("mask"), -// py::arg("output"), py::arg("columns"), py::arg("kernel_h"), -// py::arg("kernel_w"), py::arg("stride_h"), py::arg("stride_w"), -// py::arg("pad_h"), py::arg("pad_w"), py::arg("dilation_h"), -// py::arg("dilation_w"), py::arg("group"), py::arg("deformable_group"), -// py::arg("with_bias")); -// m.def("modulated_deform_conv_backward", &modulated_deform_conv_backward, -// "modulated deform conv backward", py::arg("input"), py::arg("weight"), -// py::arg("bias"), py::arg("ones"), py::arg("offset"), py::arg("mask"), -// py::arg("columns"), py::arg("grad_input"), py::arg("grad_weight"), -// py::arg("grad_bias"), py::arg("grad_offset"), py::arg("grad_mask"), -// py::arg("grad_output"), py::arg("kernel_h"), py::arg("kernel_w"), -// py::arg("stride_h"), py::arg("stride_w"), py::arg("pad_h"), -// py::arg("pad_w"), py::arg("dilation_h"), py::arg("dilation_w"), -// py::arg("group"), py::arg("deformable_group"), py::arg("with_bias")); -// m.def("nms", &nms, "nms (CPU/CUDA) ", py::arg("boxes"), py::arg("scores"), -// py::arg("iou_threshold"), py::arg("offset")); -// m.def("softnms", &softnms, "softnms (CPU) ", py::arg("boxes"), -// py::arg("scores"), py::arg("dets"), py::arg("iou_threshold"), -// py::arg("sigma"), py::arg("min_score"), py::arg("method"), -// py::arg("offset")); -// m.def("nms_match", &nms_match, "nms_match (CPU) ", py::arg("dets"), -// py::arg("iou_threshold")); -// m.def("pixel_group", &pixel_group, "pixel group (CPU) ", py::arg("score"), -// py::arg("mask"), py::arg("embedding"), py::arg("kernel_label"), -// py::arg("kernel_contour"), py::arg("kernel_region_label"), -// py::arg("distance_threshold")); -// m.def("contour_expand", &contour_expand, "contour exapnd (CPU) ", -// py::arg("kernel_mask"), py::arg("internal_kernel_label"), -// py::arg("min_kernel_area"), py::arg("kernel_num")); -// m.def("roi_align_forward", &roi_align_forward, "roi_align forward", -// py::arg("input"), py::arg("rois"), py::arg("output"), -// py::arg("argmax_y"), py::arg("argmax_x"), py::arg("aligned_height"), -// py::arg("aligned_width"), py::arg("spatial_scale"), -// py::arg("sampling_ratio"), py::arg("pool_mode"), py::arg("aligned")); -// m.def("roi_align_backward", &roi_align_backward, "roi_align backward", -// py::arg("grad_output"), py::arg("rois"), py::arg("argmax_y"), -// py::arg("argmax_x"), py::arg("grad_input"), py::arg("aligned_height"), -// py::arg("aligned_width"), py::arg("spatial_scale"), -// py::arg("sampling_ratio"), py::arg("pool_mode"), py::arg("aligned")); -// m.def("roi_pool_forward", &roi_pool_forward, "roi_pool forward", -// py::arg("input"), py::arg("rois"), py::arg("output"), py::arg("argmax"), -// py::arg("pooled_height"), py::arg("pooled_width"), -// py::arg("spatial_scale")); -// m.def("roi_pool_backward", &roi_pool_backward, "roi_pool backward", -// py::arg("grad_output"), py::arg("rois"), py::arg("argmax"), -// py::arg("grad_input"), py::arg("pooled_height"), -// py::arg("pooled_width"), py::arg("spatial_scale")); - m.def("sync_bn_forward_mean", &sync_bn_forward_mean, "sync_bn forward_mean", - py::arg("input"), py::arg("mean")); - m.def("sync_bn_forward_var", &sync_bn_forward_var, "sync_bn forward_var", - py::arg("input"), py::arg("mean"), py::arg("var")); - m.def("sync_bn_forward_output", &sync_bn_forward_output, - "sync_bn forward_output", py::arg("input"), py::arg("mean"), - py::arg("var"), py::arg("weight"), py::arg("bias"), - py::arg("running_mean"), py::arg("running_var"), py::arg("norm"), - py::arg("std"), py::arg("output"), py::arg("eps"), py::arg("momentum"), - py::arg("group_size")); - m.def("sync_bn_backward_param", &sync_bn_backward_param, - "sync_bn backward_param", py::arg("grad_output"), py::arg("norm"), - py::arg("grad_weight"), py::arg("grad_bias")); - m.def("sync_bn_backward_data", &sync_bn_backward_data, - "sync_bn backward_data", py::arg("grad_output"), py::arg("weight"), - py::arg("grad_weight"), py::arg("grad_bias"), py::arg("norm"), - py::arg("std"), py::arg("grad_input")); -// m.def("psamask_forward", &psamask_forward, "PSAMASK forward (CPU/CUDA)", -// py::arg("input"), py::arg("output"), py::arg("psa_type"), -// py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), -// py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), -// py::arg("half_w_mask")); -// m.def("psamask_backward", &psamask_backward, "PSAMASK backward (CPU/CUDA)", -// py::arg("grad_output"), py::arg("grad_input"), py::arg("psa_type"), -// py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), -// py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), -// py::arg("half_w_mask")); -// m.def("tin_shift_forward", &tin_shift_forward, "tin_shift forward", -// py::arg("input"), py::arg("shift"), py::arg("output")); -// m.def("tin_shift_backward", &tin_shift_backward, "tin_shift backward", -// py::arg("grad_output"), py::arg("shift"), py::arg("grad_input")); -// m.def("bottom_pool_forward", &bottom_pool_forward, "Bottom Pool Forward", -// py::arg("input"), py::call_guard()); -// m.def("bottom_pool_backward", &bottom_pool_backward, "Bottom Pool Backward", -// py::arg("input"), py::arg("grad_output"), -// py::call_guard()); -// m.def("left_pool_forward", &left_pool_forward, "Left Pool Forward", -// py::arg("input"), py::call_guard()); -// m.def("left_pool_backward", &left_pool_backward, "Left Pool Backward", -// py::arg("input"), py::arg("grad_output"), -// py::call_guard()); -// m.def("right_pool_forward", &right_pool_forward, "Right Pool Forward", -// py::arg("input"), py::call_guard()); -// m.def("right_pool_backward", &right_pool_backward, "Right Pool Backward", -// py::arg("input"), py::arg("grad_output"), -// py::call_guard()); -// m.def("top_pool_forward", &top_pool_forward, "Top Pool Forward", -// py::arg("input"), py::call_guard()); -// m.def("top_pool_backward", &top_pool_backward, "Top Pool Backward", -// py::arg("input"), py::arg("grad_output"), -// py::call_guard()); -// m.def("box_iou_rotated", &box_iou_rotated, "IoU for rotated boxes", -// py::arg("boxes1"), py::arg("boxes2"), py::arg("ious"), -// py::arg("mode_flag"), py::arg("aligned")); -// m.def("nms_rotated", &nms_rotated, "NMS for rotated boxes", py::arg("dets"), -// py::arg("scores"), py::arg("order"), py::arg("dets_sorted"), -// py::arg("iou_threshold"), py::arg("multi_label")); -// m.def("ball_query_forward", &ball_query_forward, "ball_query_forward", -// py::arg("new_xyz_tensor"), py::arg("xyz_tensor"), py::arg("idx_tensor"), -// py::arg("b"), py::arg("n"), py::arg("m"), py::arg("min_radius"), -// py::arg("max_radius"), py::arg("nsample")); -// m.def("roi_align_rotated_forward", &roi_align_rotated_forward, -// "roi_align_rotated forward", py::arg("input"), py::arg("rois"), -// py::arg("output"), py::arg("pooled_height"), py::arg("pooled_width"), -// py::arg("spatial_scale"), py::arg("sample_num"), py::arg("aligned"), -// py::arg("clockwise")); -// m.def("roi_align_rotated_backward", &roi_align_rotated_backward, -// "roi_align_rotated backward", py::arg("grad_output"), py::arg("rois"), -// py::arg("grad_input"), py::arg("pooled_height"), -// py::arg("pooled_width"), py::arg("spatial_scale"), -// py::arg("sample_num"), py::arg("aligned"), py::arg("clockwise")); -// m.def("dynamic_point_to_voxel_forward", &dynamic_point_to_voxel_forward, -// "dynamic_point_to_voxel_forward", py::arg("feats"), py::arg("coors"), -// py::arg("reduce_type")); -// m.def("dynamic_point_to_voxel_backward", &dynamic_point_to_voxel_backward, -// "dynamic_point_to_voxel_backward", py::arg("grad_feats"), -// py::arg("grad_reduced_feats"), py::arg("feats"), -// py::arg("reduced_feats"), py::arg("coors_idx"), py::arg("reduce_count"), -// py::arg("reduce_type")); -// m.def("hard_voxelize_forward", &hard_voxelize_forward, -// "hard_voxelize_forward", py::arg("points"), py::arg("voxels"), -// py::arg("coors"), py::arg("num_points_per_voxel"), -// py::arg("voxel_size"), py::arg("coors_range"), py::arg("max_points"), -// py::arg("max_voxels"), py::arg("NDim")); -// m.def("dynamic_voxelize_forward", &dynamic_voxelize_forward, -// "dynamic_voxelize_forward", py::arg("points"), py::arg("coors"), -// py::arg("voxel_size"), py::arg("coors_range"), py::arg("NDim")); -// m.def("ms_deform_attn_forward", &ms_deform_attn_forward, -// "forward function of multi-scale deformable attention", -// py::arg("value"), py::arg("value_spatial_shapes"), -// py::arg("value_level_start_index"), py::arg("sampling_locations"), -// py::arg("attention_weights"), py::arg("im2col_step")); -// m.def("ms_deform_attn_backward", &ms_deform_attn_backward, -// "backward function of multi-scale deformable attention", -// py::arg("value"), py::arg("value_spatial_shapes"), -// py::arg("value_level_start_index"), py::arg("sampling_locations"), -// py::arg("attention_weights"), py::arg("grad_output"), -// py::arg("grad_value"), py::arg("grad_sampling_loc"), -// py::arg("grad_attn_weight"), py::arg("im2col_step")); -// m.def("border_align_forward", &border_align_forward, -// "forward function of border_align", py::arg("input"), py::arg("boxes"), -// py::arg("output"), py::arg("argmax_idx"), py::arg("pool_size")); -// m.def("border_align_backward", &border_align_backward, -// "backward function of border_align", py::arg("grad_output"), -// py::arg("boxes"), py::arg("argmax_idx"), py::arg("grad_input"), -// py::arg("pool_size")); -// m.def("correlation_forward", &correlation_forward, "Correlation forward", -// py::arg("input1"), py::arg("input2"), py::arg("output"), py::arg("kH"), -// py::arg("kW"), py::arg("patchH"), py::arg("patchW"), py::arg("padH"), -// py::arg("padW"), py::arg("dilationH"), py::arg("dilationW"), -// py::arg("dilation_patchH"), py::arg("dilation_patchW"), py::arg("dH"), -// py::arg("dW")); -// m.def("correlation_backward", &correlation_backward, "Correlation backward", -// py::arg("grad_output"), py::arg("input1"), py::arg("input2"), -// py::arg("grad_input1"), py::arg("grad_input2"), py::arg("kH"), -// py::arg("kW"), py::arg("patchH"), py::arg("patchW"), py::arg("padH"), -// py::arg("padW"), py::arg("dilationH"), py::arg("dilationW"), -// py::arg("dilation_patchH"), py::arg("dilation_patchW"), py::arg("dH"), -// py::arg("dW")); -// m.def("points_in_boxes_cpu_forward", &points_in_boxes_cpu_forward, -// "points_in_boxes_cpu_forward", py::arg("boxes_tensor"), -// py::arg("pts_tensor"), py::arg("pts_indices_tensor")); -// m.def("points_in_boxes_part_forward", &points_in_boxes_part_forward, -// "points_in_boxes_part_forward", py::arg("boxes_tensor"), -// py::arg("pts_tensor"), py::arg("box_idx_of_points_tensor")); -// m.def("points_in_boxes_all_forward", &points_in_boxes_all_forward, -// "points_in_boxes_all_forward", py::arg("boxes_tensor"), -// py::arg("pts_tensor"), py::arg("box_idx_of_points_tensor")); -// m.def("roiaware_pool3d_forward", &roiaware_pool3d_forward, -// "roiaware_pool3d_forward", py::arg("rois"), py::arg("pts"), -// py::arg("pts_feature"), py::arg("argmax"), py::arg("pts_idx_of_voxels"), -// py::arg("pooled_features"), py::arg("pool_method")); -// m.def("roiaware_pool3d_backward", &roiaware_pool3d_backward, -// "roiaware_pool3d_backward", py::arg("pts_idx_of_voxels"), -// py::arg("argmax"), py::arg("grad_out"), py::arg("grad_in"), -// py::arg("pool_method")); -} diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp deleted file mode 100755 index 2e023a859..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean); - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var); - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size); - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias); - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -void sync_bn_forward_mean_cuda(const Tensor input, Tensor mean) { - SyncBNForwardMeanCUDAKernelLauncher(input, mean); -} - -void sync_bn_forward_var_cuda(const Tensor input, const Tensor mean, - Tensor var) { - SyncBNForwardVarCUDAKernelLauncher(input, mean, var); -} - -void sync_bn_forward_output_cuda(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - SyncBNForwardOutputCUDAKernelLauncher(input, mean, var, running_mean, - running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_cuda(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - SyncBNBackwardParamCUDAKernelLauncher(grad_output, norm, grad_weight, - grad_bias); -} - -void sync_bn_backward_data_cuda(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - SyncBNBackwardDataCUDAKernelLauncher(grad_output, weight, grad_weight, - grad_bias, norm, std, grad_input); -} -#endif - -void sync_bn_forward_mean(const Tensor input, Tensor mean) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - sync_bn_forward_mean_cuda(input, mean); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - CHECK_CUDA_INPUT(var); - sync_bn_forward_var_cuda(input, mean, var); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - CHECK_CUDA_INPUT(var); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(bias); - CHECK_CUDA_INPUT(running_mean); - CHECK_CUDA_INPUT(running_var); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(std); - CHECK_CUDA_INPUT(output); - sync_bn_forward_output_cuda(input, mean, var, running_mean, running_var, - weight, bias, norm, std, output, eps, momentum, - group_size); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(grad_weight); - CHECK_CUDA_INPUT(grad_bias); - sync_bn_backward_param_cuda(grad_output, norm, grad_weight, grad_bias); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(grad_weight); - CHECK_CUDA_INPUT(grad_bias); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(std); - CHECK_CUDA_INPUT(grad_input); - sync_bn_backward_data_cuda(grad_output, weight, grad_weight, grad_bias, - norm, std, grad_input); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/deprecated_wrappers.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/deprecated_wrappers.py deleted file mode 100755 index a2e593df9..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/deprecated_wrappers.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# This file is for backward compatibility. -# Module wrappers for empty tensor have been moved to mmcv.cnn.bricks. -import warnings - -from ..cnn.bricks.wrappers import Conv2d, ConvTranspose2d, Linear, MaxPool2d - - -class Conv2d_deprecated(Conv2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Conv2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') - - -class ConvTranspose2d_deprecated(ConvTranspose2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing ConvTranspose2d wrapper from "mmcv.ops" will be ' - 'deprecated in the future. Please import them from "mmcv.cnn" ' - 'instead') - - -class MaxPool2d_deprecated(MaxPool2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing MaxPool2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') - - -class Linear_deprecated(Linear): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Linear wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/focal_loss.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/focal_loss.py deleted file mode 100755 index 763bc93bd..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/focal_loss.py +++ /dev/null @@ -1,212 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sigmoid_focal_loss_forward', 'sigmoid_focal_loss_backward', - 'softmax_focal_loss_forward', 'softmax_focal_loss_backward' -]) - - -class SigmoidFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSigmoidFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - output = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_forward( - input, target, weight, output, gamma=ctx.gamma, alpha=ctx.alpha) - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input, target, weight) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - input, target, weight = ctx.saved_tensors - - grad_input = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_backward( - input, - target, - weight, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input.size(0) - return grad_input, None, None, None, None, None - - -sigmoid_focal_loss = SigmoidFocalLossFunction.apply - - -class SigmoidFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SigmoidFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return sigmoid_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s - - -class SoftmaxFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSoftmaxFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - channel_stats, _ = torch.max(input, dim=1) - input_softmax = input - channel_stats.unsqueeze(1).expand_as(input) - input_softmax.exp_() - - channel_stats = input_softmax.sum(dim=1) - input_softmax /= channel_stats.unsqueeze(1).expand_as(input) - - output = input.new_zeros(input.size(0)) - ext_module.softmax_focal_loss_forward( - input_softmax, - target, - weight, - output, - gamma=ctx.gamma, - alpha=ctx.alpha) - - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input_softmax, target, weight) - return output - - @staticmethod - def backward(ctx, grad_output): - input_softmax, target, weight = ctx.saved_tensors - buff = input_softmax.new_zeros(input_softmax.size(0)) - grad_input = input_softmax.new_zeros(input_softmax.size()) - - ext_module.softmax_focal_loss_backward( - input_softmax, - target, - weight, - buff, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input_softmax.size(0) - return grad_input, None, None, None, None, None - - -softmax_focal_loss = SoftmaxFocalLossFunction.apply - - -class SoftmaxFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SoftmaxFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return softmax_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/info.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/info.py deleted file mode 100755 index 29f2e5598..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/info.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os - -import torch - -if torch.__version__ == 'parrots': - import parrots - - def get_compiler_version(): - return 'GCC ' + parrots.version.compiler - - def get_compiling_cuda_version(): - return parrots.version.cuda -else: - from ..utils import ext_loader - ext_module = ext_loader.load_ext( - '_ext', ['get_compiler_version', 'get_compiling_cuda_version']) - - def get_compiler_version(): - return ext_module.get_compiler_version() - - def get_compiling_cuda_version(): - return ext_module.get_compiling_cuda_version() - - -def get_onnxruntime_op_path(): - wildcard = os.path.join( - os.path.abspath(os.path.dirname(os.path.dirname(__file__))), - '_ext_ort.*.so') - - paths = glob.glob(wildcard) - if len(paths) > 0: - return paths[0] - else: - return '' diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/sync_bn.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/sync_bn.py deleted file mode 100755 index 04302f031..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/ops/sync_bn.py +++ /dev/null @@ -1,279 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn.functional as F -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.module import Module -from torch.nn.parameter import Parameter - -from mmcv.cnn import NORM_LAYERS -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sync_bn_forward_mean', 'sync_bn_forward_var', 'sync_bn_forward_output', - 'sync_bn_backward_param', 'sync_bn_backward_data' -]) - - -class SyncBatchNormFunction(Function): - - @staticmethod - def symbolic(g, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - return g.op( - 'mmcv::MMCVSyncBatchNorm', - input, - running_mean, - running_var, - weight, - bias, - momentum_f=momentum, - eps_f=eps, - group_i=group, - group_size_i=group_size, - stats_mode=stats_mode) - - @staticmethod - def forward(self, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - self.momentum = momentum - self.eps = eps - self.group = group - self.group_size = group_size - self.stats_mode = stats_mode - - assert isinstance( - input, (torch.HalfTensor, torch.FloatTensor, - torch.cuda.HalfTensor, torch.cuda.FloatTensor)), \ - f'only support Half or Float Tensor, but {input.type()}' - output = torch.zeros_like(input) - input3d = input.flatten(start_dim=2) - output3d = output.view_as(input3d) - num_channels = input3d.size(1) - - # ensure mean/var/norm/std are initialized as zeros - # ``torch.empty()`` does not guarantee that - mean = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - var = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - norm = torch.zeros_like( - input3d, dtype=torch.float, device=input3d.device) - std = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - - batch_size = input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_forward_mean(input3d, mean) - batch_flag = torch.ones([1], device=mean.device, dtype=mean.dtype) - else: - # skip updating mean and leave it as zeros when the input is empty - batch_flag = torch.zeros([1], device=mean.device, dtype=mean.dtype) - - # synchronize mean and the batch flag - vec = torch.cat([mean, batch_flag]) - if self.stats_mode == 'N': - vec *= batch_size - if self.group_size > 1: - dist.all_reduce(vec, group=self.group) - total_batch = vec[-1].detach() - mean = vec[:num_channels] - - if self.stats_mode == 'default': - mean = mean / self.group_size - elif self.stats_mode == 'N': - mean = mean / total_batch.clamp(min=1) - else: - raise NotImplementedError - - # leave var as zeros when the input is empty - if batch_size > 0: - ext_module.sync_bn_forward_var(input3d, mean, var) - - if self.stats_mode == 'N': - var *= batch_size - if self.group_size > 1: - dist.all_reduce(var, group=self.group) - - if self.stats_mode == 'default': - var /= self.group_size - elif self.stats_mode == 'N': - var /= total_batch.clamp(min=1) - else: - raise NotImplementedError - - # if the total batch size over all the ranks is zero, - # we should not update the statistics in the current batch - update_flag = total_batch.clamp(max=1) - momentum = update_flag * self.momentum - ext_module.sync_bn_forward_output( - input3d, - mean, - var, - weight, - bias, - running_mean, - running_var, - norm, - std, - output3d, - eps=self.eps, - momentum=momentum, - group_size=self.group_size) - self.save_for_backward(norm, std, weight) - return output - - @staticmethod - @once_differentiable - def backward(self, grad_output): - norm, std, weight = self.saved_tensors - grad_weight = torch.zeros_like(weight) - grad_bias = torch.zeros_like(weight) - grad_input = torch.zeros_like(grad_output) - grad_output3d = grad_output.flatten(start_dim=2) - grad_input3d = grad_input.view_as(grad_output3d) - - batch_size = grad_input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_backward_param(grad_output3d, norm, grad_weight, - grad_bias) - - # all reduce - if self.group_size > 1: - dist.all_reduce(grad_weight, group=self.group) - dist.all_reduce(grad_bias, group=self.group) - grad_weight /= self.group_size - grad_bias /= self.group_size - - if batch_size > 0: - ext_module.sync_bn_backward_data(grad_output3d, weight, - grad_weight, grad_bias, norm, std, - grad_input3d) - - return grad_input, None, None, grad_weight, grad_bias, \ - None, None, None, None, None - - -@NORM_LAYERS.register_module(name='MMSyncBN') -class SyncBatchNorm(Module): - """Synchronized Batch Normalization. - - Args: - num_features (int): number of features/chennels in input tensor - eps (float, optional): a value added to the denominator for numerical - stability. Defaults to 1e-5. - momentum (float, optional): the value used for the running_mean and - running_var computation. Defaults to 0.1. - affine (bool, optional): whether to use learnable affine parameters. - Defaults to True. - track_running_stats (bool, optional): whether to track the running - mean and variance during training. When set to False, this - module does not track such statistics, and initializes statistics - buffers ``running_mean`` and ``running_var`` as ``None``. When - these buffers are ``None``, this module always uses batch - statistics in both training and eval modes. Defaults to True. - group (int, optional): synchronization of stats happen within - each process group individually. By default it is synchronization - across the whole world. Defaults to None. - stats_mode (str, optional): The statistical mode. Available options - includes ``'default'`` and ``'N'``. Defaults to 'default'. - When ``stats_mode=='default'``, it computes the overall statistics - using those from each worker with equal weight, i.e., the - statistics are synchronized and simply divied by ``group``. This - mode will produce inaccurate statistics when empty tensors occur. - When ``stats_mode=='N'``, it compute the overall statistics using - the total number of batches in each worker ignoring the number of - group, i.e., the statistics are synchronized and then divied by - the total batch ``N``. This mode is beneficial when empty tensors - occur during training, as it average the total mean by the real - number of batch. - """ - - def __init__(self, - num_features, - eps=1e-5, - momentum=0.1, - affine=True, - track_running_stats=True, - group=None, - stats_mode='default'): - super(SyncBatchNorm, self).__init__() - self.num_features = num_features - self.eps = eps - self.momentum = momentum - self.affine = affine - self.track_running_stats = track_running_stats - group = dist.group.WORLD if group is None else group - self.group = group - self.group_size = dist.get_world_size(group) - assert stats_mode in ['default', 'N'], \ - f'"stats_mode" only accepts "default" and "N", got "{stats_mode}"' - self.stats_mode = stats_mode - if self.affine: - self.weight = Parameter(torch.Tensor(num_features)) - self.bias = Parameter(torch.Tensor(num_features)) - else: - self.register_parameter('weight', None) - self.register_parameter('bias', None) - if self.track_running_stats: - self.register_buffer('running_mean', torch.zeros(num_features)) - self.register_buffer('running_var', torch.ones(num_features)) - self.register_buffer('num_batches_tracked', - torch.tensor(0, dtype=torch.long)) - else: - self.register_buffer('running_mean', None) - self.register_buffer('running_var', None) - self.register_buffer('num_batches_tracked', None) - self.reset_parameters() - - def reset_running_stats(self): - if self.track_running_stats: - self.running_mean.zero_() - self.running_var.fill_(1) - self.num_batches_tracked.zero_() - - def reset_parameters(self): - self.reset_running_stats() - if self.affine: - self.weight.data.uniform_() # pytorch use ones_() - self.bias.data.zero_() - - def forward(self, input): - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input, got {input.dim()}D input') - if self.momentum is None: - exponential_average_factor = 0.0 - else: - exponential_average_factor = self.momentum - - if self.training and self.track_running_stats: - if self.num_batches_tracked is not None: - self.num_batches_tracked += 1 - if self.momentum is None: # use cumulative moving average - exponential_average_factor = 1.0 / float( - self.num_batches_tracked) - else: # use exponential moving average - exponential_average_factor = self.momentum - - if self.training or not self.track_running_stats: - return SyncBatchNormFunction.apply( - input, self.running_mean, self.running_var, self.weight, - self.bias, exponential_average_factor, self.eps, self.group, - self.group_size, self.stats_mode) - else: - return F.batch_norm(input, self.running_mean, self.running_var, - self.weight, self.bias, False, - exponential_average_factor, self.eps) - - def __repr__(self): - s = self.__class__.__name__ - s += f'({self.num_features}, ' - s += f'eps={self.eps}, ' - s += f'momentum={self.momentum}, ' - s += f'affine={self.affine}, ' - s += f'track_running_stats={self.track_running_stats}, ' - s += f'group_size={self.group_size},' - s += f'stats_mode={self.stats_mode})' - return s diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/__init__.py deleted file mode 100755 index 2ed2c17ad..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collate import collate -from .data_container import DataContainer -from .data_parallel import MMDataParallel -from .distributed import MMDistributedDataParallel -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter, scatter_kwargs -from .utils import is_module_wrapper - -__all__ = [ - 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', - 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' -] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/_functions.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/_functions.py deleted file mode 100755 index 9b5a8a444..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/_functions.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import _get_stream - - -def scatter(input, devices, streams=None): - """Scatters tensor across multiple GPUs.""" - if streams is None: - streams = [None] * len(devices) - - if isinstance(input, list): - chunk_size = (len(input) - 1) // len(devices) + 1 - outputs = [ - scatter(input[i], [devices[i // chunk_size]], - [streams[i // chunk_size]]) for i in range(len(input)) - ] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - # TODO: copy to a pinned buffer first (if copying from CPU) - stream = streams[0] if output.numel() > 0 else None - if devices != [-1]: - with torch.cuda.device(devices[0]), torch.cuda.stream(stream): - output = output.cuda(devices[0], non_blocking=True) - else: - # unsqueeze the first dimension thus the tensor's shape is the - # same as those scattered with GPU. - output = output.unsqueeze(0) - return output - else: - raise Exception(f'Unknown type {type(input)}.') - - -def synchronize_stream(output, devices, streams): - if isinstance(output, list): - chunk_size = len(output) // len(devices) - for i in range(len(devices)): - for j in range(chunk_size): - synchronize_stream(output[i * chunk_size + j], [devices[i]], - [streams[i]]) - elif isinstance(output, torch.Tensor): - if output.numel() != 0: - with torch.cuda.device(devices[0]): - main_stream = torch.cuda.current_stream() - main_stream.wait_stream(streams[0]) - output.record_stream(main_stream) - else: - raise Exception(f'Unknown type {type(output)}.') - - -def get_input_device(input): - if isinstance(input, list): - for item in input: - input_device = get_input_device(item) - if input_device != -1: - return input_device - return -1 - elif isinstance(input, torch.Tensor): - return input.get_device() if input.is_cuda else -1 - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_gpus, input): - input_device = get_input_device(input) - streams = None - if input_device == -1 and target_gpus != [-1]: - # Perform CPU to GPU copies in a background stream - streams = [_get_stream(device) for device in target_gpus] - - outputs = scatter(input, target_gpus, streams) - # Synchronize with the copy stream - if streams is not None: - synchronize_stream(outputs, target_gpus, streams) - - return tuple(outputs) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/collate.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/collate.py deleted file mode 100755 index ad749197d..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/collate.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence - -import torch -import torch.nn.functional as F -from torch.utils.data.dataloader import default_collate - -from .data_container import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Puts each data field into a tensor/DataContainer with outer dimension - batch size. - - Extend default_collate to add support for - :type:`~mmcv.parallel.DataContainer`. There are 3 cases. - - 1. cpu_only = True, e.g., meta data - 2. cpu_only = False, stack = True, e.g., images tensors - 3. cpu_only = False, stack = False, e.g., gt bboxes - """ - - if not isinstance(batch, Sequence): - raise TypeError(f'{batch.dtype} is not supported.') - - if isinstance(batch[0], DataContainer): - stacked = [] - if batch[0].cpu_only: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer( - stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) - elif batch[0].stack: - for i in range(0, len(batch), samples_per_gpu): - assert isinstance(batch[i].data, torch.Tensor) - - if batch[i].pad_dims is not None: - ndim = batch[i].dim() - assert ndim > batch[i].pad_dims - max_shape = [0 for _ in range(batch[i].pad_dims)] - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = batch[i].size(-dim) - for sample in batch[i:i + samples_per_gpu]: - for dim in range(0, ndim - batch[i].pad_dims): - assert batch[i].size(dim) == sample.size(dim) - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = max(max_shape[dim - 1], - sample.size(-dim)) - padded_samples = [] - for sample in batch[i:i + samples_per_gpu]: - pad = [0 for _ in range(batch[i].pad_dims * 2)] - for dim in range(1, batch[i].pad_dims + 1): - pad[2 * dim - - 1] = max_shape[dim - 1] - sample.size(-dim) - padded_samples.append( - F.pad( - sample.data, pad, value=sample.padding_value)) - stacked.append(default_collate(padded_samples)) - elif batch[i].pad_dims is None: - stacked.append( - default_collate([ - sample.data - for sample in batch[i:i + samples_per_gpu] - ])) - else: - raise ValueError( - 'pad_dims should be either None or integers (1-3)') - - else: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer(stacked, batch[0].stack, batch[0].padding_value) - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - return [collate(samples, samples_per_gpu) for samples in transposed] - elif isinstance(batch[0], Mapping): - return { - key: collate([d[key] for d in batch], samples_per_gpu) - for key in batch[0] - } - else: - return default_collate(batch) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/data_container.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/data_container.py deleted file mode 100755 index cedb0d32a..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/data_container.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch - - -def assert_tensor_type(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not isinstance(args[0].data, torch.Tensor): - raise AttributeError( - f'{args[0].__class__.__name__} has no attribute ' - f'{func.__name__} for type {args[0].datatype}') - return func(*args, **kwargs) - - return wrapper - - -class DataContainer: - """A container for any type of objects. - - Typically tensors will be stacked in the collate function and sliced along - some dimension in the scatter function. This behavior has some limitations. - 1. All tensors have to be the same size. - 2. Types are limited (numpy array or Tensor). - - We design `DataContainer` and `MMDataParallel` to overcome these - limitations. The behavior can be either of the following. - - - copy to GPU, pad all tensors to the same size and stack them - - copy to GPU without stacking - - leave the objects as is and pass it to the model - - pad_dims specifies the number of last few dimensions to do padding - """ - - def __init__(self, - data, - stack=False, - padding_value=0, - cpu_only=False, - pad_dims=2): - self._data = data - self._cpu_only = cpu_only - self._stack = stack - self._padding_value = padding_value - assert pad_dims in [None, 1, 2, 3] - self._pad_dims = pad_dims - - def __repr__(self): - return f'{self.__class__.__name__}({repr(self.data)})' - - def __len__(self): - return len(self._data) - - @property - def data(self): - return self._data - - @property - def datatype(self): - if isinstance(self.data, torch.Tensor): - return self.data.type() - else: - return type(self.data) - - @property - def cpu_only(self): - return self._cpu_only - - @property - def stack(self): - return self._stack - - @property - def padding_value(self): - return self._padding_value - - @property - def pad_dims(self): - return self._pad_dims - - @assert_tensor_type - def size(self, *args, **kwargs): - return self.data.size(*args, **kwargs) - - @assert_tensor_type - def dim(self): - return self.data.dim() diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/data_parallel.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/data_parallel.py deleted file mode 100755 index 79b5f69b6..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/data_parallel.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from itertools import chain - -from torch.nn.parallel import DataParallel - -from .scatter_gather import scatter_kwargs - - -class MMDataParallel(DataParallel): - """The DataParallel module that supports DataContainer. - - MMDataParallel has two main differences with PyTorch DataParallel: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data during both GPU and CPU inference. - - It implement two more APIs ``train_step()`` and ``val_step()``. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - device_ids (list[int]): Device IDS of modules to be scattered to. - Defaults to None when GPU is not available. - output_device (str | int): Device ID for output. Defaults to None. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MMDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.dim = dim - - def forward(self, *inputs, **kwargs): - """Override the original forward function. - - The main difference lies in the CPU inference where the data in - :class:`DataContainers` will still be gathered. - """ - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module(*inputs[0], **kwargs[0]) - else: - return super().forward(*inputs, **kwargs) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.train_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - 'instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.train_step(*inputs[0], **kwargs[0]) - - def val_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.val_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/distributed.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/distributed.py deleted file mode 100755 index b799a213d..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/distributed.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel.distributed import (DistributedDataParallel, - _find_tensors) - -from mmcv import print_log -from mmcv.utils import TORCH_VERSION, digit_version -from .scatter_gather import scatter_kwargs - - -class MMDistributedDataParallel(DistributedDataParallel): - """The DDP module that supports DataContainer. - - MMDDP has two main differences with PyTorch DDP: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data. - - It implement two APIs ``train_step()`` and ``val_step()``. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - """train_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.train_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if getattr(self, 'require_forward_param_sync', True): - self._sync_params() - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.train_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.train_step(*inputs, **kwargs) - - if torch.is_grad_enabled() and getattr( - self, 'require_backward_grad_sync', True): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output - - def val_step(self, *inputs, **kwargs): - """val_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.val_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if getattr(self, 'require_forward_param_sync', True): - self._sync_params() - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.val_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.val_step(*inputs, **kwargs) - - if torch.is_grad_enabled() and getattr( - self, 'require_backward_grad_sync', True): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/distributed_deprecated.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/distributed_deprecated.py deleted file mode 100755 index b593d4a9e..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/distributed_deprecated.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn as nn -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter_kwargs - - -@MODULE_WRAPPERS.register_module() -class MMDistributedDataParallel(nn.Module): - - def __init__(self, - module, - dim=0, - broadcast_buffers=True, - bucket_cap_mb=25): - super(MMDistributedDataParallel, self).__init__() - self.module = module - self.dim = dim - self.broadcast_buffers = broadcast_buffers - - self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 - self._sync_params() - - def _dist_broadcast_coalesced(self, tensors, buffer_size): - for tensors in _take_tensors(tensors, buffer_size): - flat_tensors = _flatten_dense_tensors(tensors) - dist.broadcast(flat_tensors, 0) - for tensor, synced in zip( - tensors, _unflatten_dense_tensors(flat_tensors, tensors)): - tensor.copy_(synced) - - def _sync_params(self): - module_states = list(self.module.state_dict().values()) - if len(module_states) > 0: - self._dist_broadcast_coalesced(module_states, - self.broadcast_bucket_size) - if self.broadcast_buffers: - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) < digit_version('1.0')): - buffers = [b.data for b in self.module._all_buffers()] - else: - buffers = [b.data for b in self.module.buffers()] - if len(buffers) > 0: - self._dist_broadcast_coalesced(buffers, - self.broadcast_bucket_size) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/registry.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/registry.py deleted file mode 100755 index 144f9fb16..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel, DistributedDataParallel - -from mmcv.utils import Registry - -MODULE_WRAPPERS = Registry('module wrapper') -MODULE_WRAPPERS.register_module(module=DataParallel) -MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/scatter_gather.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/scatter_gather.py deleted file mode 100755 index 900ff8856..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import Scatter as OrigScatter - -from ._functions import Scatter -from .data_container import DataContainer - - -def scatter(inputs, target_gpus, dim=0): - """Scatter inputs to target gpus. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_gpus != [-1]: - return OrigScatter.apply(target_gpus, None, dim, obj) - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_gpus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_gpus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_gpus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_gpus, dim) if inputs else [] - kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/utils.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/utils.py deleted file mode 100755 index 0f5712cb4..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/parallel/utils.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import MODULE_WRAPPERS - - -def is_module_wrapper(module): - """Check if a module is a module wrapper. - - The following 3 modules in MMCV (and their subclasses) are regarded as - module wrappers: DataParallel, DistributedDataParallel, - MMDistributedDataParallel (the deprecated version). You may add you own - module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: True if the input module is a module wrapper. - """ - module_wrappers = tuple(MODULE_WRAPPERS.module_dict.values()) - return isinstance(module, module_wrappers) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/__init__.py deleted file mode 100755 index 52e4b48d3..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_module import BaseModule, ModuleList, Sequential -from .base_runner import BaseRunner -from .builder import RUNNERS, build_runner -from .checkpoint import (CheckpointLoader, _load_checkpoint, - _load_checkpoint_with_prefix, load_checkpoint, - load_state_dict, save_checkpoint, weights_to_cpu) -from .default_constructor import DefaultRunnerConstructor -from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, - init_dist, master_only) -from .epoch_based_runner import EpochBasedRunner, Runner -from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model -from .hooks import (HOOKS, CheckpointHook, ClosureHook, DistEvalHook, - DistSamplerSeedHook, DvcliveLoggerHook, EMAHook, EvalHook, - Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, Hook, IterTimerHook, - LoggerHook, LrUpdaterHook, MlflowLoggerHook, - NeptuneLoggerHook, OptimizerHook, PaviLoggerHook, - SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .iter_based_runner import IterBasedRunner, IterLoader -from .log_buffer import LogBuffer -from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, - DefaultOptimizerConstructor, build_optimizer, - build_optimizer_constructor) -from .priority import Priority, get_priority -from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed - -__all__ = [ - 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', - 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', - 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', - 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', - 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', - 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', - 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', - 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', - 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', - 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', - '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', - 'ModuleList', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor' -] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/base_module.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/base_module.py deleted file mode 100755 index 529575b81..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/base_module.py +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings -from abc import ABCMeta -from collections import defaultdict -from logging import FileHandler - -import torch.nn as nn - -from mmcv.runner.dist_utils import master_only -from mmcv.utils.logging import get_logger, logger_initialized, print_log - - -class BaseModule(nn.Module, metaclass=ABCMeta): - """Base module for all modules in openmmlab. - - ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional - functionality of parameter initialization. Compared with - ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. - - - ``init_cfg``: the config to control the initialization. - - ``init_weights``: The function of parameter - initialization and recording initialization - information. - - ``_params_init_info``: Used to track the parameter - initialization information. This attribute only - exists during executing the ``init_weights``. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, init_cfg=None): - """Initialize BaseModule, inherited from `torch.nn.Module`""" - - # NOTE init_cfg can be defined in different levels, but init_cfg - # in low levels has a higher priority. - - super(BaseModule, self).__init__() - # define default value of init_cfg instead of hard code - # in init_weights() function - self._is_init = False - - self.init_cfg = copy.deepcopy(init_cfg) - - # Backward compatibility in derived classes - # if pretrained is not None: - # warnings.warn('DeprecationWarning: pretrained is a deprecated \ - # key, please consider using init_cfg') - # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - - @property - def is_init(self): - return self._is_init - - def init_weights(self): - """Initialize the weights.""" - - is_top_level_module = False - # check if it is top-level module - if not hasattr(self, '_params_init_info'): - # The `_params_init_info` is used to record the initialization - # information of the parameters - # the key should be the obj:`nn.Parameter` of model and the value - # should be a dict containing - # - init_info (str): The string that describes the initialization. - # - tmp_mean_value (FloatTensor): The mean of the parameter, - # which indicates whether the parameter has been modified. - # this attribute would be deleted after all parameters - # is initialized. - self._params_init_info = defaultdict(dict) - is_top_level_module = True - - # Initialize the `_params_init_info`, - # When detecting the `tmp_mean_value` of - # the corresponding parameter is changed, update related - # initialization information - for name, param in self.named_parameters(): - self._params_init_info[param][ - 'init_info'] = f'The value is the same before and ' \ - f'after calling `init_weights` ' \ - f'of {self.__class__.__name__} ' - self._params_init_info[param][ - 'tmp_mean_value'] = param.data.mean() - - # pass `params_init_info` to all submodules - # All submodules share the same `params_init_info`, - # so it will be updated when parameters are - # modified at any level of the model. - for sub_module in self.modules(): - sub_module._params_init_info = self._params_init_info - - # Get the initialized logger, if not exist, - # create a logger named `mmcv` - logger_names = list(logger_initialized.keys()) - logger_name = logger_names[0] if logger_names else 'mmcv' - - from ..cnn import initialize - from ..cnn.utils.weight_init import update_init_info - module_name = self.__class__.__name__ - if not self._is_init: - if self.init_cfg: - print_log( - f'initialize {module_name} with init_cfg {self.init_cfg}', - logger=logger_name) - initialize(self, self.init_cfg) - if isinstance(self.init_cfg, dict): - # prevent the parameters of - # the pre-trained model - # from being overwritten by - # the `init_weights` - if self.init_cfg['type'] == 'Pretrained': - return - - for m in self.children(): - if hasattr(m, 'init_weights'): - m.init_weights() - # users may overload the `init_weights` - update_init_info( - m, - init_info=f'Initialized by ' - f'user-defined `init_weights`' - f' in {m.__class__.__name__} ') - - self._is_init = True - else: - warnings.warn(f'init_weights of {self.__class__.__name__} has ' - f'been called more than once.') - - if is_top_level_module: - self._dump_init_info(logger_name) - - for sub_module in self.modules(): - del sub_module._params_init_info - - @master_only - def _dump_init_info(self, logger_name): - """Dump the initialization information to a file named - `initialization.log.json` in workdir. - - Args: - logger_name (str): The name of logger. - """ - - logger = get_logger(logger_name) - - with_file_handler = False - # dump the information to the logger file if there is a `FileHandler` - for handler in logger.handlers: - if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True - if not with_file_handler: - for name, param in self.named_parameters(): - print_log( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n ", - logger=logger_name) - - def __repr__(self): - s = super().__repr__() - if self.init_cfg: - s += f'\ninit_cfg={self.init_cfg}' - return s - - -class Sequential(BaseModule, nn.Sequential): - """Sequential module in openmmlab. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, *args, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.Sequential.__init__(self, *args) - - -class ModuleList(BaseModule, nn.ModuleList): - """ModuleList in openmmlab. - - Args: - modules (iterable, optional): an iterable of modules to add. - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleList.__init__(self, modules) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/base_runner.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/base_runner.py deleted file mode 100755 index 25cd98f51..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/base_runner.py +++ /dev/null @@ -1,542 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -import os.path as osp -import warnings -from abc import ABCMeta, abstractmethod - -import torch -from torch.optim import Optimizer - -import mmcv -from ..parallel import is_module_wrapper -from .checkpoint import load_checkpoint -from .dist_utils import get_dist_info -from .hooks import HOOKS, Hook -from .log_buffer import LogBuffer -from .priority import Priority, get_priority -from .utils import get_time_str - - -class BaseRunner(metaclass=ABCMeta): - """The base class of Runner, a training helper for PyTorch. - - All subclasses should implement the following APIs: - - - ``run()`` - - ``train()`` - - ``val()`` - - ``save_checkpoint()`` - - Args: - model (:obj:`torch.nn.Module`): The model to be run. - batch_processor (callable): A callable method that process a data - batch. The interface of this method should be - `batch_processor(model, data, train_mode) -> dict` - optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an - optimizer (in most cases) or a dict of optimizers (in models that - requires more than one optimizer, e.g., GAN). - work_dir (str, optional): The working directory to save checkpoints - and logs. Defaults to None. - logger (:obj:`logging.Logger`): Logger used during training. - Defaults to None. (The default value is just for backward - compatibility) - meta (dict | None): A dict records some import information such as - environment info and seed, which will be logged in logger hook. - Defaults to None. - max_epochs (int, optional): Total training epochs. - max_iters (int, optional): Total training iterations. - """ - - def __init__(self, - model, - batch_processor=None, - optimizer=None, - work_dir=None, - logger=None, - meta=None, - max_iters=None, - max_epochs=None): - if batch_processor is not None: - if not callable(batch_processor): - raise TypeError('batch_processor must be callable, ' - f'but got {type(batch_processor)}') - warnings.warn('batch_processor is deprecated, please implement ' - 'train_step() and val_step() in the model instead.') - # raise an error is `batch_processor` is not None and - # `model.train_step()` exists. - if is_module_wrapper(model): - _model = model.module - else: - _model = model - if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): - raise RuntimeError( - 'batch_processor and model.train_step()/model.val_step() ' - 'cannot be both available.') - else: - assert hasattr(model, 'train_step') - - # check the type of `optimizer` - if isinstance(optimizer, dict): - for name, optim in optimizer.items(): - if not isinstance(optim, Optimizer): - raise TypeError( - f'optimizer must be a dict of torch.optim.Optimizers, ' - f'but optimizer["{name}"] is a {type(optim)}') - elif not isinstance(optimizer, Optimizer) and optimizer is not None: - raise TypeError( - f'optimizer must be a torch.optim.Optimizer object ' - f'or dict or None, but got {type(optimizer)}') - - # check the type of `logger` - if not isinstance(logger, logging.Logger): - raise TypeError(f'logger must be a logging.Logger object, ' - f'but got {type(logger)}') - - # check the type of `meta` - if meta is not None and not isinstance(meta, dict): - raise TypeError( - f'meta must be a dict or None, but got {type(meta)}') - - self.model = model - self.batch_processor = batch_processor - self.optimizer = optimizer - self.logger = logger - self.meta = meta - # create work_dir - if mmcv.is_str(work_dir): - self.work_dir = osp.abspath(work_dir) - mmcv.mkdir_or_exist(self.work_dir) - elif work_dir is None: - self.work_dir = None - else: - raise TypeError('"work_dir" must be a str or None') - - # get model name from the model class - if hasattr(self.model, 'module'): - self._model_name = self.model.module.__class__.__name__ - else: - self._model_name = self.model.__class__.__name__ - - self._rank, self._world_size = get_dist_info() - self.timestamp = get_time_str() - self.mode = None - self._hooks = [] - self._epoch = 0 - self._iter = 0 - self._inner_iter = 0 - - if max_epochs is not None and max_iters is not None: - raise ValueError( - 'Only one of `max_epochs` or `max_iters` can be set.') - - self._max_epochs = max_epochs - self._max_iters = max_iters - # TODO: Redesign LogBuffer, it is not flexible and elegant enough - self.log_buffer = LogBuffer() - - @property - def model_name(self): - """str: Name of the model, usually the module class name.""" - return self._model_name - - @property - def rank(self): - """int: Rank of current process. (distributed training)""" - return self._rank - - @property - def world_size(self): - """int: Number of processes participating in the job. - (distributed training)""" - return self._world_size - - @property - def hooks(self): - """list[:obj:`Hook`]: A list of registered hooks.""" - return self._hooks - - @property - def epoch(self): - """int: Current epoch.""" - return self._epoch - - @property - def iter(self): - """int: Current iteration.""" - return self._iter - - @property - def inner_iter(self): - """int: Iteration in an epoch.""" - return self._inner_iter - - @property - def max_epochs(self): - """int: Maximum training epochs.""" - return self._max_epochs - - @property - def max_iters(self): - """int: Maximum training iterations.""" - return self._max_iters - - @abstractmethod - def train(self): - pass - - @abstractmethod - def val(self): - pass - - @abstractmethod - def run(self, data_loaders, workflow, **kwargs): - pass - - @abstractmethod - def save_checkpoint(self, - out_dir, - filename_tmpl, - save_optimizer=True, - meta=None, - create_symlink=True): - pass - - def current_lr(self): - """Get current learning rates. - - Returns: - list[float] | dict[str, list[float]]: Current learning rates of all - param groups. If the runner has a dict of optimizers, this - method will return a dict. - """ - if isinstance(self.optimizer, torch.optim.Optimizer): - lr = [group['lr'] for group in self.optimizer.param_groups] - elif isinstance(self.optimizer, dict): - lr = dict() - for name, optim in self.optimizer.items(): - lr[name] = [group['lr'] for group in optim.param_groups] - else: - raise RuntimeError( - 'lr is not applicable because optimizer does not exist.') - return lr - - def current_momentum(self): - """Get current momentums. - - Returns: - list[float] | dict[str, list[float]]: Current momentums of all - param groups. If the runner has a dict of optimizers, this - method will return a dict. - """ - - def _get_momentum(optimizer): - momentums = [] - for group in optimizer.param_groups: - if 'momentum' in group.keys(): - momentums.append(group['momentum']) - elif 'betas' in group.keys(): - momentums.append(group['betas'][0]) - else: - momentums.append(0) - return momentums - - if self.optimizer is None: - raise RuntimeError( - 'momentum is not applicable because optimizer does not exist.') - elif isinstance(self.optimizer, torch.optim.Optimizer): - momentums = _get_momentum(self.optimizer) - elif isinstance(self.optimizer, dict): - momentums = dict() - for name, optim in self.optimizer.items(): - momentums[name] = _get_momentum(optim) - return momentums - - def register_hook(self, hook, priority='NORMAL'): - """Register a hook into the hook list. - - The hook will be inserted into a priority queue, with the specified - priority (See :class:`Priority` for details of priorities). - For hooks with the same priority, they will be triggered in the same - order as they are registered. - - Args: - hook (:obj:`Hook`): The hook to be registered. - priority (int or str or :obj:`Priority`): Hook priority. - Lower value means higher priority. - """ - assert isinstance(hook, Hook) - if hasattr(hook, 'priority'): - raise ValueError('"priority" is a reserved attribute for hooks') - priority = get_priority(priority) - hook.priority = priority - # insert the hook to a sorted list - inserted = False - for i in range(len(self._hooks) - 1, -1, -1): - if priority >= self._hooks[i].priority: - self._hooks.insert(i + 1, hook) - inserted = True - break - if not inserted: - self._hooks.insert(0, hook) - - def register_hook_from_cfg(self, hook_cfg): - """Register a hook from its cfg. - - Args: - hook_cfg (dict): Hook config. It should have at least keys 'type' - and 'priority' indicating its type and priority. - - Notes: - The specific hook class to register should not use 'type' and - 'priority' arguments during initialization. - """ - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = mmcv.build_from_cfg(hook_cfg, HOOKS) - self.register_hook(hook, priority=priority) - - def call_hook(self, fn_name): - """Call all hooks. - - Args: - fn_name (str): The function name in each hook to be called, such as - "before_train_epoch". - """ - for hook in self._hooks: - getattr(hook, fn_name)(self) - - def get_hook_info(self): - # Get hooks info in each stage - stage_hook_map = {stage: [] for stage in Hook.stages} - for hook in self.hooks: - try: - priority = Priority(hook.priority).name - except ValueError: - priority = hook.priority - classname = hook.__class__.__name__ - hook_info = f'({priority:<12}) {classname:<35}' - for trigger_stage in hook.get_triggered_stages(): - stage_hook_map[trigger_stage].append(hook_info) - - stage_hook_infos = [] - for stage in Hook.stages: - hook_infos = stage_hook_map[stage] - if len(hook_infos) > 0: - info = f'{stage}:\n' - info += '\n'.join(hook_infos) - info += '\n -------------------- ' - stage_hook_infos.append(info) - return '\n'.join(stage_hook_infos) - - def load_checkpoint(self, - filename, - map_location='cpu', - strict=False, - revise_keys=[(r'^module.', '')]): - return load_checkpoint( - self.model, - filename, - map_location, - strict, - self.logger, - revise_keys=revise_keys) - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - if map_location == 'default': - if torch.cuda.is_available(): - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint(checkpoint) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - if self.meta is None: - self.meta = {} - self.meta.setdefault('hook_msgs', {}) - # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages - self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) - - # Re-calculate the number of iterations when resuming - # models with different number of GPUs - if 'config' in checkpoint['meta']: - config = mmcv.Config.fromstring( - checkpoint['meta']['config'], file_format='.py') - previous_gpu_ids = config.get('gpu_ids', None) - if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( - previous_gpu_ids) != self.world_size: - self._iter = int(self._iter * len(previous_gpu_ids) / - self.world_size) - self.logger.info('the iteration number is changed due to ' - 'change of GPU number') - - # resume meta information meta - self.meta = checkpoint['meta'] - - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - elif isinstance(lr_config, dict): - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = mmcv.build_from_cfg(lr_config, HOOKS) - else: - hook = lr_config - self.register_hook(hook, priority='VERY_HIGH') - - def register_momentum_hook(self, momentum_config): - if momentum_config is None: - return - if isinstance(momentum_config, dict): - assert 'policy' in momentum_config - policy_type = momentum_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of momentum updater. - # Since this is not applicable for - # `CosineAnnealingMomentumUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'MomentumUpdaterHook' - momentum_config['type'] = hook_type - hook = mmcv.build_from_cfg(momentum_config, HOOKS) - else: - hook = momentum_config - self.register_hook(hook, priority='HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = mmcv.build_from_cfg(optimizer_config, HOOKS) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def register_checkpoint_hook(self, checkpoint_config): - if checkpoint_config is None: - return - if isinstance(checkpoint_config, dict): - checkpoint_config.setdefault('type', 'CheckpointHook') - hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) - else: - hook = checkpoint_config - self.register_hook(hook, priority='NORMAL') - - def register_logger_hooks(self, log_config): - if log_config is None: - return - log_interval = log_config['interval'] - for info in log_config['hooks']: - logger_hook = mmcv.build_from_cfg( - info, HOOKS, default_args=dict(interval=log_interval)) - self.register_hook(logger_hook, priority='VERY_LOW') - - def register_timer_hook(self, timer_config): - if timer_config is None: - return - if isinstance(timer_config, dict): - timer_config_ = copy.deepcopy(timer_config) - hook = mmcv.build_from_cfg(timer_config_, HOOKS) - else: - hook = timer_config - self.register_hook(hook, priority='LOW') - - def register_custom_hooks(self, custom_config): - if custom_config is None: - return - - if not isinstance(custom_config, list): - custom_config = [custom_config] - - for item in custom_config: - if isinstance(item, dict): - self.register_hook_from_cfg(item) - else: - self.register_hook(item, priority='NORMAL') - - def register_profiler_hook(self, profiler_config): - if profiler_config is None: - return - if isinstance(profiler_config, dict): - profiler_config.setdefault('type', 'ProfilerHook') - hook = mmcv.build_from_cfg(profiler_config, HOOKS) - else: - hook = profiler_config - self.register_hook(hook) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - timer_config=dict(type='IterTimerHook'), - custom_hooks_config=None): - """Register default and custom hooks for training. - - Default and custom hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - self.register_lr_hook(lr_config) - self.register_momentum_hook(momentum_config) - self.register_optimizer_hook(optimizer_config) - self.register_checkpoint_hook(checkpoint_config) - self.register_timer_hook(timer_config) - self.register_logger_hooks(log_config) - self.register_custom_hooks(custom_hooks_config) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/builder.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/builder.py deleted file mode 100755 index 77c96ba0b..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/builder.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from ..utils import Registry - -RUNNERS = Registry('runner') -RUNNER_BUILDERS = Registry('runner builder') - - -def build_runner_constructor(cfg): - return RUNNER_BUILDERS.build(cfg) - - -def build_runner(cfg, default_args=None): - runner_cfg = copy.deepcopy(cfg) - constructor_type = runner_cfg.pop('constructor', - 'DefaultRunnerConstructor') - runner_constructor = build_runner_constructor( - dict( - type=constructor_type, - runner_cfg=runner_cfg, - default_args=default_args)) - runner = runner_constructor() - return runner diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/checkpoint.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/checkpoint.py deleted file mode 100755 index 6ad605b85..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/checkpoint.py +++ /dev/null @@ -1,707 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os -import os.path as osp -import pkgutil -import re -import time -import warnings -from collections import OrderedDict -from importlib import import_module -from tempfile import TemporaryDirectory - -import torch -import torchvision -from torch.optim import Optimizer -from torch.utils import model_zoo - -import mmcv -from ..fileio import FileClient -from ..fileio import load as load_file -from ..parallel import is_module_wrapper -from ..utils import mkdir_or_exist -from .dist_utils import get_dist_info - -ENV_MMCV_HOME = 'MMCV_HOME' -ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' -DEFAULT_CACHE_DIR = '~/.cache' - - -def _get_mmcv_home(): - mmcv_home = os.path.expanduser( - os.getenv( - ENV_MMCV_HOME, - os.path.join( - os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) - - mkdir_or_exist(mmcv_home) - return mmcv_home - - -def load_state_dict(module, state_dict, strict=False, logger=None): - """Load state_dict to a module. - - This method is modified from :meth:`torch.nn.Module.load_state_dict`. - Default value for ``strict`` is set to ``False`` and the message for - param mismatch will be shown even if strict is False. - - Args: - module (Module): Module that receives the state_dict. - state_dict (OrderedDict): Weights. - strict (bool): whether to strictly enforce that the keys - in :attr:`state_dict` match the keys returned by this module's - :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. - logger (:obj:`logging.Logger`, optional): Logger to log the error - message. If not specified, print function will be used. - """ - unexpected_keys = [] - all_missing_keys = [] - err_msg = [] - - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata - - # use _load_from_state_dict to enable checkpoint version control - def load(module, prefix=''): - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - local_metadata = {} if metadata is None else metadata.get( - prefix[:-1], {}) - module._load_from_state_dict(state_dict, prefix, local_metadata, True, - all_missing_keys, unexpected_keys, - err_msg) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(module) - load = None # break load->load reference cycle - - # ignore "num_batches_tracked" of BN layers - missing_keys = [ - key for key in all_missing_keys if 'num_batches_tracked' not in key - ] - - if unexpected_keys: - err_msg.append('unexpected key in source ' - f'state_dict: {", ".join(unexpected_keys)}\n') - if missing_keys: - err_msg.append( - f'missing keys in source state_dict: {", ".join(missing_keys)}\n') - - rank, _ = get_dist_info() - if len(err_msg) > 0 and rank == 0: - err_msg.insert( - 0, 'The model and loaded state dict do not match exactly\n') - err_msg = '\n'.join(err_msg) - if strict: - raise RuntimeError(err_msg) - elif logger is not None: - logger.warning(err_msg) - else: - print(err_msg) - - -def get_torchvision_models(): - model_urls = dict() - for _, name, ispkg in pkgutil.walk_packages(torchvision.models.__path__): - if ispkg: - continue - _zoo = import_module(f'torchvision.models.{name}') - if hasattr(_zoo, 'model_urls'): - _urls = getattr(_zoo, 'model_urls') - model_urls.update(_urls) - return model_urls - - -def get_external_models(): - mmcv_home = _get_mmcv_home() - default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') - default_urls = load_file(default_json_path) - assert isinstance(default_urls, dict) - external_json_path = osp.join(mmcv_home, 'open_mmlab.json') - if osp.exists(external_json_path): - external_urls = load_file(external_json_path) - assert isinstance(external_urls, dict) - default_urls.update(external_urls) - - return default_urls - - -def get_mmcls_models(): - mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') - mmcls_urls = load_file(mmcls_json_path) - - return mmcls_urls - - -def get_deprecated_model_names(): - deprecate_json_path = osp.join(mmcv.__path__[0], - 'model_zoo/deprecated.json') - deprecate_urls = load_file(deprecate_json_path) - assert isinstance(deprecate_urls, dict) - - return deprecate_urls - - -def _process_mmcls_checkpoint(checkpoint): - state_dict = checkpoint['state_dict'] - new_state_dict = OrderedDict() - for k, v in state_dict.items(): - if k.startswith('backbone.'): - new_state_dict[k[9:]] = v - new_checkpoint = dict(state_dict=new_state_dict) - - return new_checkpoint - - -class CheckpointLoader: - """A general checkpoint loader to manage all schemes.""" - - _schemes = {} - - @classmethod - def _register_scheme(cls, prefixes, loader, force=False): - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if (prefix not in cls._schemes) or force: - cls._schemes[prefix] = loader - else: - raise KeyError( - f'{prefix} is already registered as a loader backend, ' - 'add "force=True" if you want to override it') - # sort, longer prefixes take priority - cls._schemes = OrderedDict( - sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) - - @classmethod - def register_scheme(cls, prefixes, loader=None, force=False): - """Register a loader to CheckpointLoader. - - This method can be used as a normal class method or a decorator. - - Args: - prefixes (str or list[str] or tuple[str]): - The prefix of the registered loader. - loader (function, optional): The loader function to be registered. - When this method is used as a decorator, loader is None. - Defaults to None. - force (bool, optional): Whether to override the loader - if the prefix has already been registered. Defaults to False. - """ - - if loader is not None: - cls._register_scheme(prefixes, loader, force=force) - return - - def _register(loader_cls): - cls._register_scheme(prefixes, loader_cls, force=force) - return loader_cls - - return _register - - @classmethod - def _get_checkpoint_loader(cls, path): - """Finds a loader that supports the given path. Falls back to the local - loader if no other loader is found. - - Args: - path (str): checkpoint path - - Returns: - loader (function): checkpoint loader - """ - - for p in cls._schemes: - if path.startswith(p): - return cls._schemes[p] - - @classmethod - def load_checkpoint(cls, filename, map_location=None, logger=None): - """load checkpoint through URL scheme path. - - Args: - filename (str): checkpoint file name with given prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - logger (:mod:`logging.Logger`, optional): The logger for message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint_loader = cls._get_checkpoint_loader(filename) - class_name = checkpoint_loader.__name__ - mmcv.print_log( - f'load checkpoint from {class_name[10:]} path: {filename}', logger) - return checkpoint_loader(filename, map_location) - - -@CheckpointLoader.register_scheme(prefixes='') -def load_from_local(filename, map_location): - """load checkpoint by local file path. - - Args: - filename (str): local checkpoint file path - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - if not osp.isfile(filename): - raise IOError(f'{filename} is not a checkpoint file') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) -def load_from_http(filename, map_location=None, model_dir=None): - """load checkpoint through HTTP or HTTPS scheme path. In distributed - setting, this function only download checkpoint at local rank 0. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - model_dir (string, optional): directory in which to save the object, - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - rank, world_size = get_dist_info() - rank = int(os.environ.get('LOCAL_RANK', rank)) - if rank == 0: - checkpoint = model_zoo.load_url( - filename, model_dir=model_dir, map_location=map_location) - if world_size > 1: - torch.distributed.barrier() - if rank > 0: - checkpoint = model_zoo.load_url( - filename, model_dir=model_dir, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='pavi://') -def load_from_pavi(filename, map_location=None): - """load checkpoint through the file path prefixed with pavi. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with pavi prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - assert filename.startswith('pavi://'), \ - f'Expected filename startswith `pavi://`, but get {filename}' - model_path = filename[7:] - - try: - from pavi import modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - - model = modelcloud.get(model_path) - with TemporaryDirectory() as tmp_dir: - downloaded_file = osp.join(tmp_dir, model.name) - model.download(downloaded_file) - checkpoint = torch.load(downloaded_file, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='s3://') -def load_from_ceph(filename, map_location=None, backend='petrel'): - """load checkpoint through the file path prefixed with s3. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with s3 prefix - map_location (str, optional): Same as :func:`torch.load`. - backend (str, optional): The storage backend type. Options are 'ceph', - 'petrel'. Default: 'petrel'. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - allowed_backends = ['ceph', 'petrel'] - if backend not in allowed_backends: - raise ValueError(f'Load from Backend {backend} is not supported.') - - if backend == 'ceph': - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead') - - # CephClient and PetrelBackend have the same prefix 's3://' and the latter - # will be chosen as default. If PetrelBackend can not be instantiated - # successfully, the CephClient will be chosen. - try: - file_client = FileClient(backend=backend) - except ImportError: - allowed_backends.remove(backend) - file_client = FileClient(backend=allowed_backends[0]) - - with io.BytesIO(file_client.get(filename)) as buffer: - checkpoint = torch.load(buffer, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) -def load_from_torchvision(filename, map_location=None): - """load checkpoint through the file path prefixed with modelzoo or - torchvision. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - model_urls = get_torchvision_models() - if filename.startswith('modelzoo://'): - warnings.warn('The URL scheme of "modelzoo://" is deprecated, please ' - 'use "torchvision://" instead') - model_name = filename[11:] - else: - model_name = filename[14:] - return load_from_http(model_urls[model_name], map_location=map_location) - - -@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) -def load_from_openmmlab(filename, map_location=None): - """load checkpoint through the file path prefixed with open-mmlab or - openmmlab. - - Args: - filename (str): checkpoint file path with open-mmlab or - openmmlab prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_external_models() - prefix_str = 'open-mmlab://' - if filename.startswith(prefix_str): - model_name = filename[13:] - else: - model_name = filename[12:] - prefix_str = 'openmmlab://' - - deprecated_urls = get_deprecated_model_names() - if model_name in deprecated_urls: - warnings.warn(f'{prefix_str}{model_name} is deprecated in favor ' - f'of {prefix_str}{deprecated_urls[model_name]}') - model_name = deprecated_urls[model_name] - model_url = model_urls[model_name] - # check if is url - if model_url.startswith(('http://', 'https://')): - checkpoint = load_from_http(model_url, map_location=map_location) - else: - filename = osp.join(_get_mmcv_home(), model_url) - if not osp.isfile(filename): - raise IOError(f'{filename} is not a checkpoint file') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='mmcls://') -def load_from_mmcls(filename, map_location=None): - """load checkpoint through the file path prefixed with mmcls. - - Args: - filename (str): checkpoint file path with mmcls prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_mmcls_models() - model_name = filename[8:] - checkpoint = load_from_http( - model_urls[model_name], map_location=map_location) - checkpoint = _process_mmcls_checkpoint(checkpoint) - return checkpoint - - -def _load_checkpoint(filename, map_location=None, logger=None): - """Load checkpoint from somewhere (modelzoo, file, url). - - Args: - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str, optional): Same as :func:`torch.load`. - Default: None. - logger (:mod:`logging.Logger`, optional): The logger for error message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. It can be either an - OrderedDict storing model weights or a dict containing other - information, which depends on the checkpoint. - """ - return CheckpointLoader.load_checkpoint(filename, map_location, logger) - - -def _load_checkpoint_with_prefix(prefix, filename, map_location=None): - """Load partial pretrained model with specific prefix. - - Args: - prefix (str): The prefix of sub-module. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str | None): Same as :func:`torch.load`. Default: None. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint = _load_checkpoint(filename, map_location=map_location) - - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - if not prefix.endswith('.'): - prefix += '.' - prefix_len = len(prefix) - - state_dict = { - k[prefix_len:]: v - for k, v in state_dict.items() if k.startswith(prefix) - } - - assert state_dict, f'{prefix} is not in the pretrained model' - return state_dict - - -def load_checkpoint(model, - filename, - map_location=None, - strict=False, - logger=None, - revise_keys=[(r'^module\.', '')]): - """Load checkpoint from a file or URI. - - Args: - model (Module): Module to load checkpoint. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str): Same as :func:`torch.load`. - strict (bool): Whether to allow different params for the model and - checkpoint. - logger (:mod:`logging.Logger` or None): The logger for error message. - revise_keys (list): A list of customized keywords to modify the - state_dict in checkpoint. Each item is a (pattern, replacement) - pair of the regular expression operations. Default: strip - the prefix 'module.' by [(r'^module\\.', '')]. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - checkpoint = _load_checkpoint(filename, map_location, logger) - # OrderedDict is a subclass of dict - if not isinstance(checkpoint, dict): - raise RuntimeError( - f'No state_dict found in checkpoint file {filename}') - # get state_dict from checkpoint - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - - # strip prefix of state_dict - metadata = getattr(state_dict, '_metadata', OrderedDict()) - for p, r in revise_keys: - state_dict = OrderedDict( - {re.sub(p, r, k): v - for k, v in state_dict.items()}) - # Keep metadata in state_dict - state_dict._metadata = metadata - - # load state_dict - load_state_dict(model, state_dict, strict, logger) - return checkpoint - - -def weights_to_cpu(state_dict): - """Copy a model state_dict to cpu. - - Args: - state_dict (OrderedDict): Model weights on GPU. - - Returns: - OrderedDict: Model weights on GPU. - """ - state_dict_cpu = OrderedDict() - for key, val in state_dict.items(): - state_dict_cpu[key] = val.cpu() - # Keep metadata in state_dict - state_dict_cpu._metadata = getattr(state_dict, '_metadata', OrderedDict()) - return state_dict_cpu - - -def _save_to_state_dict(module, destination, prefix, keep_vars): - """Saves module state to `destination` dictionary. - - This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. - - Args: - module (nn.Module): The module to generate state_dict. - destination (dict): A dict where state will be stored. - prefix (str): The prefix for parameters and buffers used in this - module. - """ - for name, param in module._parameters.items(): - if param is not None: - destination[prefix + name] = param if keep_vars else param.detach() - for name, buf in module._buffers.items(): - # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d - if buf is not None: - destination[prefix + name] = buf if keep_vars else buf.detach() - - -def get_state_dict(module, destination=None, prefix='', keep_vars=False): - """Returns a dictionary containing a whole state of the module. - - Both parameters and persistent buffers (e.g. running averages) are - included. Keys are corresponding parameter and buffer names. - - This method is modified from :meth:`torch.nn.Module.state_dict` to - recursively check parallel module in case that the model has a complicated - structure, e.g., nn.Module(nn.Module(DDP)). - - Args: - module (nn.Module): The module to generate state_dict. - destination (OrderedDict): Returned dict for the state of the - module. - prefix (str): Prefix of the key. - keep_vars (bool): Whether to keep the variable property of the - parameters. Default: False. - - Returns: - dict: A dictionary containing a whole state of the module. - """ - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - - # below is the same as torch.nn.Module.state_dict() - if destination is None: - destination = OrderedDict() - destination._metadata = OrderedDict() - destination._metadata[prefix[:-1]] = local_metadata = dict( - version=module._version) - _save_to_state_dict(module, destination, prefix, keep_vars) - for name, child in module._modules.items(): - if child is not None: - get_state_dict( - child, destination, prefix + name + '.', keep_vars=keep_vars) - for hook in module._state_dict_hooks.values(): - hook_result = hook(module, destination, prefix, local_metadata) - if hook_result is not None: - destination = hook_result - return destination - - -def save_checkpoint(model, - filename, - optimizer=None, - meta=None, - file_client_args=None): - """Save checkpoint to file. - - The checkpoint will have 3 fields: ``meta``, ``state_dict`` and - ``optimizer``. By default ``meta`` will contain version and time info. - - Args: - model (Module): Module whose params are to be saved. - filename (str): Checkpoint filename. - optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. - meta (dict, optional): Metadata to be saved in checkpoint. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError(f'meta must be a dict or None, but got {type(meta)}') - meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) - - if is_module_wrapper(model): - model = model.module - - if hasattr(model, 'CLASSES') and model.CLASSES is not None: - # save class name to the meta - meta.update(CLASSES=model.CLASSES) - - checkpoint = { - 'meta': meta, - 'state_dict': weights_to_cpu(get_state_dict(model)) - } - # save optimizer state dict in the checkpoint - if isinstance(optimizer, Optimizer): - checkpoint['optimizer'] = optimizer.state_dict() - elif isinstance(optimizer, dict): - checkpoint['optimizer'] = {} - for name, optim in optimizer.items(): - checkpoint['optimizer'][name] = optim.state_dict() - - if filename.startswith('pavi://'): - if file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" if filename starts with' - f'"pavi://", but got {file_client_args}') - try: - from pavi import modelcloud - from pavi import exception - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - model_path = filename[7:] - root = modelcloud.Folder() - model_dir, model_name = osp.split(model_path) - try: - model = modelcloud.get(model_dir) - except exception.NodeNotFoundError: - model = root.create_training_model(model_dir) - with TemporaryDirectory() as tmp_dir: - checkpoint_file = osp.join(tmp_dir, model_name) - with open(checkpoint_file, 'wb') as f: - torch.save(checkpoint, f) - f.flush() - model.create_file(checkpoint_file, name=model_name) - else: - file_client = FileClient.infer_client(file_client_args, filename) - with io.BytesIO() as f: - torch.save(checkpoint, f) - file_client.put(f.getvalue(), filename) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/default_constructor.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/default_constructor.py deleted file mode 100755 index 0bad847f2..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/default_constructor.py +++ /dev/null @@ -1,44 +0,0 @@ -from .builder import RUNNER_BUILDERS, RUNNERS - - -@RUNNER_BUILDERS.register_module() -class DefaultRunnerConstructor: - """Default constructor for runners. - - Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. - For example, We can inject some new properties and functions for `Runner`. - - Example: - >>> from mmcv.runner import RUNNER_BUILDERS, build_runner - >>> # Define a new RunnerReconstructor - >>> @RUNNER_BUILDERS.register_module() - >>> class MyRunnerConstructor: - ... def __init__(self, runner_cfg, default_args=None): - ... if not isinstance(runner_cfg, dict): - ... raise TypeError('runner_cfg should be a dict', - ... f'but got {type(runner_cfg)}') - ... self.runner_cfg = runner_cfg - ... self.default_args = default_args - ... - ... def __call__(self): - ... runner = RUNNERS.build(self.runner_cfg, - ... default_args=self.default_args) - ... # Add new properties for existing runner - ... runner.my_name = 'my_runner' - ... runner.my_function = lambda self: print(self.my_name) - ... ... - >>> # build your runner - >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, - ... constructor='MyRunnerConstructor') - >>> runner = build_runner(runner_cfg) - """ - - def __init__(self, runner_cfg, default_args=None): - if not isinstance(runner_cfg, dict): - raise TypeError('runner_cfg should be a dict', - f'but got {type(runner_cfg)}') - self.runner_cfg = runner_cfg - self.default_args = default_args - - def __call__(self): - return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/dist_utils.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/dist_utils.py deleted file mode 100755 index d3a1ef3fd..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/dist_utils.py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import os -import subprocess -from collections import OrderedDict - -import torch -import torch.multiprocessing as mp -from torch import distributed as dist -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - - -def init_dist(launcher, backend='nccl', **kwargs): - if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') - if launcher == 'pytorch': - _init_dist_pytorch(backend, **kwargs) - elif launcher == 'mpi': - _init_dist_mpi(backend, **kwargs) - elif launcher == 'slurm': - _init_dist_slurm(backend, **kwargs) - else: - raise ValueError(f'Invalid launcher type: {launcher}') - - -def _init_dist_pytorch(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['RANK']) - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_mpi(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['OMPI_COMM_WORLD_RANK']) - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_slurm(backend, port=None): - """Initialize slurm distributed training environment. - - If argument ``port`` is not specified, then the master port will be system - environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system - environment variable, then a default port ``29500`` will be used. - - Args: - backend (str): Backend of torch.distributed. - port (int, optional): Master port. Defaults to None. - """ - proc_id = int(os.environ['SLURM_PROCID']) - ntasks = int(os.environ['SLURM_NTASKS']) - node_list = os.environ['SLURM_NODELIST'] - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(proc_id % num_gpus) - addr = subprocess.getoutput( - f'scontrol show hostname {node_list} | head -n1') - # specify master port - if port is not None: - os.environ['MASTER_PORT'] = str(port) - elif 'MASTER_PORT' in os.environ: - pass # use MASTER_PORT in the environment variable - else: - # 29500 is torch.distributed default port - os.environ['MASTER_PORT'] = '29500' - # use MASTER_ADDR in the environment variable if it already exists - if 'MASTER_ADDR' not in os.environ: - os.environ['MASTER_ADDR'] = addr - os.environ['WORLD_SIZE'] = str(ntasks) - os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) - os.environ['RANK'] = str(proc_id) - dist.init_process_group(backend=backend) - - -def get_dist_info(): - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - world_size = dist.get_world_size() - else: - rank = 0 - world_size = 1 - return rank, world_size - - -def master_only(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - rank, _ = get_dist_info() - if rank == 0: - return func(*args, **kwargs) - - return wrapper - - -def allreduce_params(params, coalesce=True, bucket_size_mb=-1): - """Allreduce parameters. - - Args: - params (list[torch.Parameters]): List of parameters or buffers of a - model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - _, world_size = get_dist_info() - if world_size == 1: - return - params = [param.data for param in params] - if coalesce: - _allreduce_coalesced(params, world_size, bucket_size_mb) - else: - for tensor in params: - dist.all_reduce(tensor.div_(world_size)) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - _, world_size = get_dist_info() - if world_size == 1: - return - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/epoch_based_runner.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/epoch_based_runner.py deleted file mode 100755 index 2dd29357a..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/epoch_based_runner.py +++ /dev/null @@ -1,187 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .utils import get_host_info - - -@RUNNERS.register_module() -class EpochBasedRunner(BaseRunner): - """Epoch-based Runner. - - This runner train models epoch by epoch. - """ - - def run_iter(self, data_batch, train_mode, **kwargs): - if self.batch_processor is not None: - outputs = self.batch_processor( - self.model, data_batch, train_mode=train_mode, **kwargs) - elif train_mode: - outputs = self.model.train_step(data_batch, self.optimizer, - **kwargs) - else: - outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('"batch_processor()" or "model.train_step()"' - 'and "model.val_step()" must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - self._iter += 1 - - self.call_hook('after_train_epoch') - self._epoch += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - self.call_hook('before_val_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_val_iter') - self.run_iter(data_batch, train_mode=False) - self.call_hook('after_val_iter') - - self.call_hook('after_val_epoch') - - def run(self, data_loaders, workflow, max_epochs=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, epochs) to specify the - running order and epochs. E.g, [('train', 2), ('val', 1)] means - running 2 epochs for training and 1 epoch for validation, - iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_epochs is not None: - warnings.warn( - 'setting max_epochs in run is deprecated, ' - 'please set max_epochs in runner_config', DeprecationWarning) - self._max_epochs = max_epochs - - assert self._max_epochs is not None, ( - 'max_epochs must be specified during instantiation') - - for i, flow in enumerate(workflow): - mode, epochs = flow - if mode == 'train': - self._max_iters = self._max_epochs * len(data_loaders[i]) - break - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d epochs', workflow, - self._max_epochs) - self.call_hook('before_run') - - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - mode, epochs = flow - if isinstance(mode, str): # self.train() - if not hasattr(self, mode): - raise ValueError( - f'runner has no method named "{mode}" to run an ' - 'epoch') - epoch_runner = getattr(self, mode) - else: - raise TypeError( - 'mode in workflow must be a str, but got {}'.format( - type(mode))) - - for _ in range(epochs): - if mode == 'train' and self.epoch >= self._max_epochs: - break - epoch_runner(data_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_run') - - def save_checkpoint(self, - out_dir, - filename_tmpl='epoch_{}.pth', - save_optimizer=True, - meta=None, - create_symlink=True): - """Save the checkpoint. - - Args: - out_dir (str): The directory that checkpoints are saved. - filename_tmpl (str, optional): The checkpoint filename template, - which contains a placeholder for the epoch number. - Defaults to 'epoch_{}.pth'. - save_optimizer (bool, optional): Whether to save the optimizer to - the checkpoint. Defaults to True. - meta (dict, optional): The meta information to be saved in the - checkpoint. Defaults to None. - create_symlink (bool, optional): Whether to create a symlink - "latest.pth" to point to the latest checkpoint. - Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.epoch + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - -@RUNNERS.register_module() -class Runner(EpochBasedRunner): - """Deprecated name of EpochBasedRunner.""" - - def __init__(self, *args, **kwargs): - warnings.warn( - 'Runner was deprecated, please use EpochBasedRunner instead') - super().__init__(*args, **kwargs) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/fp16_utils.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/fp16_utils.py deleted file mode 100755 index 4baab939a..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/fp16_utils.py +++ /dev/null @@ -1,410 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import warnings -from collections import abc -from inspect import getfullargspec - -import numpy as np -import torch -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .dist_utils import allreduce_grads as _allreduce_grads - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 - # manually, so the behavior may not be consistent with real amp. - from torch.cuda.amp import autocast -except ImportError: - pass - - -def cast_tensor_type(inputs, src_type, dst_type): - """Recursively convert Tensor in inputs from src_type to dst_type. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype): Source type.. - dst_type (torch.dtype): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - if isinstance(inputs, nn.Module): - return inputs - elif isinstance(inputs, torch.Tensor): - return inputs.to(dst_type) - elif isinstance(inputs, str): - return inputs - elif isinstance(inputs, np.ndarray): - return inputs - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type, dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type, dst_type) for item in inputs) - else: - return inputs - - -def auto_fp16(apply_to=None, out_fp32=False): - """Decorator to enable fp16 training automatically. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If inputs arguments are fp32 tensors, they will - be converted to fp16 automatically. Arguments other than fp32 tensors are - ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp32 (bool): Whether to convert the output back to fp32. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp16 - >>> @auto_fp16() - >>> def forward(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp16 - >>> @auto_fp16(apply_to=('pred', )) - >>> def do_something(self, pred, others): - >>> pass - """ - - def auto_fp16_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@auto_fp16 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - # NOTE: default args are not taken into consideration - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.float, torch.half)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = {} - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.float, torch.half) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=True): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp32: - output = cast_tensor_type(output, torch.half, torch.float) - return output - - return new_func - - return auto_fp16_wrapper - - -def force_fp32(apply_to=None, out_fp16=False): - """Decorator to convert input arguments to fp32 in force. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If there are some inputs that must be processed - in fp32 mode, then this decorator can handle it. If inputs arguments are - fp16 tensors, they will be converted to fp32 automatically. Arguments other - than fp16 tensors are ignored. If you are using PyTorch >= 1.6, - torch.cuda.amp is used as the backend, otherwise, original mmcv - implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp16 (bool): Whether to convert the output back to fp16. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp32 - >>> @force_fp32() - >>> def loss(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp32 - >>> @force_fp32(apply_to=('pred', )) - >>> def post_process(self, pred, others): - >>> pass - """ - - def force_fp32_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@force_fp32 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.half, torch.float)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = dict() - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.half, torch.float) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=False): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp16: - output = cast_tensor_type(output, torch.float, torch.half) - return output - - return new_func - - return force_fp32_wrapper - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - warnings.warning( - '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' - 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads') - _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) - - -def wrap_fp16_model(model): - """Wrap the FP32 model to FP16. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - For PyTorch >= 1.6, this function will - 1. Set fp16 flag inside the model to True. - - Otherwise: - 1. Convert FP32 model to FP16. - 2. Remain some necessary layers to be FP32, e.g., normalization layers. - 3. Set `fp16_enabled` flag inside the model to True. - - Args: - model (nn.Module): Model in FP32. - """ - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.6.0')): - # convert model to fp16 - model.half() - # patch the normalization layers to make it work in fp32 mode - patch_norm_fp32(model) - # set `fp16_enabled` flag - for m in model.modules(): - if hasattr(m, 'fp16_enabled'): - m.fp16_enabled = True - - -def patch_norm_fp32(module): - """Recursively convert normalization layers from FP16 to FP32. - - Args: - module (nn.Module): The modules to be converted in FP16. - - Returns: - nn.Module: The converted module, the normalization layers have been - converted to FP32. - """ - if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): - module.float() - if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': - module.forward = patch_forward_method(module.forward, torch.half, - torch.float) - for child in module.children(): - patch_norm_fp32(child) - return module - - -def patch_forward_method(func, src_type, dst_type, convert_output=True): - """Patch the forward method of a module. - - Args: - func (callable): The original forward method. - src_type (torch.dtype): Type of input arguments to be converted from. - dst_type (torch.dtype): Type of input arguments to be converted to. - convert_output (bool): Whether to convert the output back to src_type. - - Returns: - callable: The patched forward method. - """ - - def new_forward(*args, **kwargs): - output = func(*cast_tensor_type(args, src_type, dst_type), - **cast_tensor_type(kwargs, src_type, dst_type)) - if convert_output: - output = cast_tensor_type(output, dst_type, src_type) - return output - - return new_forward - - -class LossScaler: - """Class that manages loss scaling in mixed precision training which - supports both dynamic or static mode. - - The implementation refers to - https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. - Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. - It's important to understand how :class:`LossScaler` operates. - Loss scaling is designed to combat the problem of underflowing - gradients encountered at long times when training fp16 networks. - Dynamic loss scaling begins by attempting a very high loss - scale. Ironically, this may result in OVERflowing gradients. - If overflowing gradients are encountered, :class:`FP16_Optimizer` then - skips the update step for this particular iteration/minibatch, - and :class:`LossScaler` adjusts the loss scale to a lower value. - If a certain number of iterations occur without overflowing gradients - detected,:class:`LossScaler` increases the loss scale once more. - In this way :class:`LossScaler` attempts to "ride the edge" of always - using the highest loss scale possible without incurring overflow. - - Args: - init_scale (float): Initial loss scale value, default: 2**32. - scale_factor (float): Factor used when adjusting the loss scale. - Default: 2. - mode (str): Loss scaling mode. 'dynamic' or 'static' - scale_window (int): Number of consecutive iterations without an - overflow to wait before increasing the loss scale. Default: 1000. - """ - - def __init__(self, - init_scale=2**32, - mode='dynamic', - scale_factor=2., - scale_window=1000): - self.cur_scale = init_scale - self.cur_iter = 0 - assert mode in ('dynamic', - 'static'), 'mode can only be dynamic or static' - self.mode = mode - self.last_overflow_iter = -1 - self.scale_factor = scale_factor - self.scale_window = scale_window - - def has_overflow(self, params): - """Check if params contain overflow.""" - if self.mode != 'dynamic': - return False - for p in params: - if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): - return True - return False - - def _has_inf_or_nan(x): - """Check if params contain NaN.""" - try: - cpu_sum = float(x.float().sum()) - except RuntimeError as instance: - if 'value cannot be converted' not in instance.args[0]: - raise - return True - else: - if cpu_sum == float('inf') or cpu_sum == -float('inf') \ - or cpu_sum != cpu_sum: - return True - return False - - def update_scale(self, overflow): - """update the current loss scale value when overflow happens.""" - if self.mode != 'dynamic': - return - if overflow: - self.cur_scale = max(self.cur_scale / self.scale_factor, 1) - self.last_overflow_iter = self.cur_iter - else: - if (self.cur_iter - self.last_overflow_iter) % \ - self.scale_window == 0: - self.cur_scale *= self.scale_factor - self.cur_iter += 1 - - def state_dict(self): - """Returns the state of the scaler as a :class:`dict`.""" - return dict( - cur_scale=self.cur_scale, - cur_iter=self.cur_iter, - mode=self.mode, - last_overflow_iter=self.last_overflow_iter, - scale_factor=self.scale_factor, - scale_window=self.scale_window) - - def load_state_dict(self, state_dict): - """Loads the loss_scaler state dict. - - Args: - state_dict (dict): scaler state. - """ - self.cur_scale = state_dict['cur_scale'] - self.cur_iter = state_dict['cur_iter'] - self.mode = state_dict['mode'] - self.last_overflow_iter = state_dict['last_overflow_iter'] - self.scale_factor = state_dict['scale_factor'] - self.scale_window = state_dict['scale_window'] - - @property - def loss_scale(self): - return self.cur_scale diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/__init__.py deleted file mode 100755 index 915af28ce..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkpoint import CheckpointHook -from .closure import ClosureHook -from .ema import EMAHook -from .evaluation import DistEvalHook, EvalHook -from .hook import HOOKS, Hook -from .iter_timer import IterTimerHook -from .logger import (DvcliveLoggerHook, LoggerHook, MlflowLoggerHook, - NeptuneLoggerHook, PaviLoggerHook, TensorboardLoggerHook, - TextLoggerHook, WandbLoggerHook) -from .lr_updater import LrUpdaterHook -from .memory import EmptyCacheHook -from .momentum_updater import MomentumUpdaterHook -from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, OptimizerHook) -from .profiler import ProfilerHook -from .sampler_seed import DistSamplerSeedHook -from .sync_buffer import SyncBuffersHook - -__all__ = [ - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'OptimizerHook', 'Fp16OptimizerHook', 'IterTimerHook', - 'DistSamplerSeedHook', 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'DvcliveLoggerHook', - 'MomentumUpdaterHook', 'SyncBuffersHook', 'EMAHook', 'EvalHook', - 'DistEvalHook', 'ProfilerHook', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook' -] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/checkpoint.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/checkpoint.py deleted file mode 100755 index 7bb75f402..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/checkpoint.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -from mmcv.fileio import FileClient -from ..dist_utils import allreduce_params, master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class CheckpointHook(Hook): - """Save checkpoints periodically. - - Args: - interval (int): The saving period. If ``by_epoch=True``, interval - indicates epochs, otherwise it indicates iterations. - Default: -1, which means "never". - by_epoch (bool): Saving checkpoints by epoch or by iteration. - Default: True. - save_optimizer (bool): Whether to save optimizer state_dict in the - checkpoint. It is usually used for resuming experiments. - Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, ``runner.work_dir`` will be used by default. If - specified, the ``out_dir`` will be the concatenation of ``out_dir`` - and the last level directory of ``runner.work_dir``. - `Changed in version 1.3.16.` - max_keep_ckpts (int, optional): The maximum checkpoints to keep. - In some cases we want only the latest few checkpoints and would - like to delete old ones to save the disk space. - Default: -1, which means unlimited. - save_last (bool, optional): Whether to force the last checkpoint to be - saved regardless of interval. Default: True. - sync_buffer (bool, optional): Whether to synchronize buffers in - different gpus. Default: False. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - - .. warning:: - Before v1.3.16, the ``out_dir`` argument indicates the path where the - checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the - root directory and the final path to save checkpoint is the - concatenation of ``out_dir`` and the last level directory of - ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" - and the value of ``runner.work_dir`` is "/path/of/B", then the final - path will be "/path/of/A/B". - """ - - def __init__(self, - interval=-1, - by_epoch=True, - save_optimizer=True, - out_dir=None, - max_keep_ckpts=-1, - save_last=True, - sync_buffer=False, - file_client_args=None, - **kwargs): - self.interval = interval - self.by_epoch = by_epoch - self.save_optimizer = save_optimizer - self.out_dir = out_dir - self.max_keep_ckpts = max_keep_ckpts - self.save_last = save_last - self.args = kwargs - self.sync_buffer = sync_buffer - self.file_client_args = file_client_args - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - - runner.logger.info((f'Checkpoints will be saved to {self.out_dir} by ' - f'{self.file_client.name}.')) - - # disable the create_symlink option because some file backends do not - # allow to create a symlink - if 'create_symlink' in self.args: - if self.args[ - 'create_symlink'] and not self.file_client.allow_symlink: - self.args['create_symlink'] = False - warnings.warn( - ('create_symlink is set as True by the user but is changed' - 'to be False because creating symbolic link is not ' - f'allowed in {self.file_client.name}')) - else: - self.args['create_symlink'] = self.file_client.allow_symlink - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` epochs - # 2. reach the last epoch of training - if self.every_n_epochs( - runner, self.interval) or (self.save_last - and self.is_last_epoch(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.epoch + 1} epochs') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) - - @master_only - def _save_checkpoint(self, runner): - """Save the current checkpoint and delete unwanted checkpoint.""" - runner.save_checkpoint( - self.out_dir, save_optimizer=self.save_optimizer, **self.args) - if runner.meta is not None: - if self.by_epoch: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) - else: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) - runner.meta.setdefault('hook_msgs', dict()) - runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( - self.out_dir, cur_ckpt_filename) - # remove other checkpoints - if self.max_keep_ckpts > 0: - if self.by_epoch: - name = 'epoch_{}.pth' - current_ckpt = runner.epoch + 1 - else: - name = 'iter_{}.pth' - current_ckpt = runner.iter + 1 - redundant_ckpts = range( - current_ckpt - self.max_keep_ckpts * self.interval, 0, - -self.interval) - filename_tmpl = self.args.get('filename_tmpl', name) - for _step in redundant_ckpts: - ckpt_path = self.file_client.join_path( - self.out_dir, filename_tmpl.format(_step)) - if self.file_client.isfile(ckpt_path): - self.file_client.remove(ckpt_path) - else: - break - - def after_train_iter(self, runner): - if self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` iterations - # 2. reach the last iteration of training - if self.every_n_iters( - runner, self.interval) or (self.save_last - and self.is_last_iter(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.iter + 1} iterations') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/closure.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/closure.py deleted file mode 100755 index b955f81f4..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/closure.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ClosureHook(Hook): - - def __init__(self, fn_name, fn): - assert hasattr(self, fn_name) - assert callable(fn) - setattr(self, fn_name, fn) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/ema.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/ema.py deleted file mode 100755 index 15c7e6808..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/ema.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...parallel import is_module_wrapper -from ..hooks.hook import HOOKS, Hook - - -@HOOKS.register_module() -class EMAHook(Hook): - r"""Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. - - .. math:: - - \text{Xema\_{t+1}} = (1 - \text{momentum}) \times - \text{Xema\_{t}} + \text{momentum} \times X_t - - Args: - momentum (float): The momentum used for updating ema parameter. - Defaults to 0.0002. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - warm_up (int): During first warm_up steps, we may use smaller momentum - to update ema parameters more slowly. Defaults to 100. - resume_from (str): The checkpoint path. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - warm_up=100, - resume_from=None): - assert isinstance(interval, int) and interval > 0 - self.warm_up = warm_up - self.interval = interval - assert momentum > 0 and momentum < 1 - self.momentum = momentum**interval - self.checkpoint = resume_from - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - self.model_parameters = dict(model.named_parameters(recurse=True)) - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers(recurse=True)) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - curr_step = runner.iter - # We warm up the momentum considering the instability at beginning - momentum = min(self.momentum, - (1 + curr_step) / (self.warm_up + curr_step)) - if curr_step % self.interval != 0: - return - for name, parameter in self.model_parameters.items(): - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/evaluation.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/evaluation.py deleted file mode 100755 index 1eeb44650..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/evaluation.py +++ /dev/null @@ -1,509 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from math import inf - -import torch.distributed as dist -from torch.nn.modules.batchnorm import _BatchNorm -from torch.utils.data import DataLoader - -from mmcv.fileio import FileClient -from mmcv.utils import is_seq_of -from .hook import Hook -from .logger import LoggerHook - - -class EvalHook(Hook): - """Non-Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader, and return the test results. If ``None``, the default - test function ``mmcv.engine.single_gpu_test`` will be used. - (default: ``None``) - greater_keys (List[str] | None, optional): Metric keys that will be - inferred by 'greater' comparison rule. If ``None``, - _default_greater_keys will be used. (default: ``None``) - less_keys (List[str] | None, optional): Metric keys that will be - inferred by 'less' comparison rule. If ``None``, _default_less_keys - will be used. (default: ``None``) - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - `New in version 1.3.16.` - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - `New in version 1.3.16.` - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - - Notes: - If new arguments are added for EvalHook, tools/test.py, - tools/eval_metric.py may be affected. - """ - - # Since the key for determine greater or less is related to the downstream - # tasks, downstream repos may need to overwrite the following inner - # variable accordingly. - - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - _default_greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - _default_less_keys = ['loss'] - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - out_dir=None, - file_client_args=None, - **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError(f'dataloader must be a pytorch DataLoader, ' - f'but got {type(dataloader)}') - - if interval <= 0: - raise ValueError(f'interval must be a positive number, ' - f'but got {interval}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' - - if start is not None and start < 0: - raise ValueError(f'The evaluation start epoch {start} is smaller ' - f'than 0') - - self.dataloader = dataloader - self.interval = interval - self.start = start - self.by_epoch = by_epoch - - assert isinstance(save_best, str) or save_best is None, \ - '""save_best"" should be a str or None ' \ - f'rather than {type(save_best)}' - self.save_best = save_best - self.eval_kwargs = eval_kwargs - self.initial_flag = True - - if test_fn is None: - from mmcv.engine import single_gpu_test - self.test_fn = single_gpu_test - else: - self.test_fn = test_fn - - if greater_keys is None: - self.greater_keys = self._default_greater_keys - else: - if not isinstance(greater_keys, (list, tuple)): - greater_keys = (greater_keys, ) - assert is_seq_of(greater_keys, str) - self.greater_keys = greater_keys - - if less_keys is None: - self.less_keys = self._default_less_keys - else: - if not isinstance(less_keys, (list, tuple)): - less_keys = (less_keys, ) - assert is_seq_of(less_keys, str) - self.less_keys = less_keys - - if self.save_best is not None: - self.best_ckpt_path = None - self._init_rule(rule, self.save_best) - - self.out_dir = out_dir - self.file_client_args = file_client_args - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific (note that the key indicator matching - is case-insensitive): - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator != 'auto': - # `_lc` here means we use the lower case of keys for - # case-insensitive matching - key_indicator_lc = key_indicator.lower() - greater_keys = [key.lower() for key in self.greater_keys] - less_keys = [key.lower() for key in self.less_keys] - - if key_indicator_lc in greater_keys: - rule = 'greater' - elif key_indicator_lc in less_keys: - rule = 'less' - elif any(key in key_indicator_lc for key in greater_keys): - rule = 'greater' - elif any(key in key_indicator_lc for key in less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - if self.rule is not None: - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'The best checkpoint will be saved to {self.out_dir} by ' - f'{self.file_client.name}')) - - if self.save_best is not None: - if runner.meta is None: - warnings.warn('runner.meta is None. Creating an empty one.') - runner.meta = dict() - runner.meta.setdefault('hook_msgs', dict()) - self.best_ckpt_path = runner.meta['hook_msgs'].get( - 'best_ckpt', None) - - def before_train_iter(self, runner): - """Evaluate the model only at the start of training by iteration.""" - if self.by_epoch or not self.initial_flag: - return - if self.start is not None and runner.iter >= self.start: - self.after_train_iter(runner) - self.initial_flag = False - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - if not (self.by_epoch and self.initial_flag): - return - if self.start is not None and runner.epoch >= self.start: - self.after_train_epoch(runner) - self.initial_flag = False - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch and self._should_evaluate(runner): - # Because the priority of EvalHook is higher than LoggerHook, the - # training log and the evaluating log are mixed. Therefore, - # we need to dump the training log and clear it before evaluating - # log is generated. In addition, this problem will only appear in - # `IterBasedRunner` whose `self.by_epoch` is False, because - # `EpochBasedRunner` whose `self.by_epoch` is True calls - # `_do_evaluate` in `after_train_epoch` stage, and at this stage - # the training log has been printed, so it will not cause any - # problem. more details at - # https://github.com/open-mmlab/mmsegmentation/issues/694 - for hook in runner._hooks: - if isinstance(hook, LoggerHook): - hook.after_train_iter(runner) - runner.log_buffer.clear() - - self._do_evaluate(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch and self._should_evaluate(runner): - self._do_evaluate(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - results = self.test_fn(runner.model, self.dataloader) - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - def _should_evaluate(self, runner): - """Judge whether to perform evaluation. - - Here is the rule to judge whether to perform evaluation: - 1. It will not perform evaluation during the epoch/iteration interval, - which is determined by ``self.interval``. - 2. It will not perform evaluation if the start time is larger than - current time. - 3. It will not perform evaluation when current time is larger than - the start time but during epoch/iteration interval. - - Returns: - bool: The flag indicating whether to perform evaluation. - """ - if self.by_epoch: - current = runner.epoch - check_time = self.every_n_epochs - else: - current = runner.iter - check_time = self.every_n_iters - - if self.start is None: - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - elif (current + 1) < self.start: - # No evaluation if start is larger than the current time. - return False - else: - # Evaluation only at epochs/iters 3, 5, 7... - # if start==3 and interval==2 - if (current + 1 - self.start) % self.interval: - return False - return True - - def _save_ckpt(self, runner, key_score): - """Save the best checkpoint. - - It will compare the score according to the compare function, write - related information (best score, best checkpoint path) and save the - best checkpoint into ``work_dir``. - """ - if self.by_epoch: - current = f'epoch_{runner.epoch + 1}' - cur_type, cur_time = 'epoch', runner.epoch + 1 - else: - current = f'iter_{runner.iter + 1}' - cur_type, cur_time = 'iter', runner.iter + 1 - - best_score = runner.meta['hook_msgs'].get( - 'best_score', self.init_value_map[self.rule]) - if self.compare_func(key_score, best_score): - best_score = key_score - runner.meta['hook_msgs']['best_score'] = best_score - - if self.best_ckpt_path and self.file_client.isfile( - self.best_ckpt_path): - self.file_client.remove(self.best_ckpt_path) - runner.logger.info( - (f'The previous best checkpoint {self.best_ckpt_path} was ' - 'removed')) - - best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' - self.best_ckpt_path = self.file_client.join_path( - self.out_dir, best_ckpt_name) - runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path - - runner.save_checkpoint( - self.out_dir, best_ckpt_name, create_symlink=False) - runner.logger.info( - f'Now best checkpoint is saved as {best_ckpt_name}.') - runner.logger.info( - f'Best {self.key_indicator} is {best_score:0.4f} ' - f'at {cur_time} {cur_type}.') - - def evaluate(self, runner, results): - """Evaluate the results. - - Args: - runner (:obj:`mmcv.Runner`): The underlined training runner. - results (list): Output results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - - if self.save_best is not None: - # If the performance of model is pool, the `eval_res` may be an - # empty dict and it will raise exception when `self.save_best` is - # not None. More details at - # https://github.com/open-mmlab/mmdetection/issues/6265. - if not eval_res: - warnings.warn( - 'Since `eval_res` is an empty dict, the behavior to save ' - 'the best checkpoint will be skipped in this evaluation.') - return None - - if self.key_indicator == 'auto': - # infer from eval_results - self._init_rule(self.rule, list(eval_res.keys())[0]) - return eval_res[self.key_indicator] - - return None - - -class DistEvalHook(EvalHook): - """Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader in a multi-gpu manner, and return the test results. If - ``None``, the default test function ``mmcv.engine.multi_gpu_test`` - will be used. (default: ``None``) - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - broadcast_bn_buffer (bool): Whether to broadcast the - buffer(running_mean and running_var) of rank 0 to other rank - before evaluation. Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - """ - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - broadcast_bn_buffer=True, - tmpdir=None, - gpu_collect=False, - out_dir=None, - file_client_args=None, - **eval_kwargs): - - if test_fn is None: - from mmcv.engine import multi_gpu_test - test_fn = multi_gpu_test - - super().__init__( - dataloader, - start=start, - interval=interval, - by_epoch=by_epoch, - save_best=save_best, - rule=rule, - test_fn=test_fn, - greater_keys=greater_keys, - less_keys=less_keys, - out_dir=out_dir, - file_client_args=file_client_args, - **eval_kwargs) - - self.broadcast_bn_buffer = broadcast_bn_buffer - self.tmpdir = tmpdir - self.gpu_collect = gpu_collect - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - results = self.test_fn( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to - # save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/hook.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/hook.py deleted file mode 100755 index f2d1c9865..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/hook.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, is_method_overridden - -HOOKS = Registry('hook') - - -class Hook: - stages = ('before_run', 'before_train_epoch', 'before_train_iter', - 'after_train_iter', 'after_train_epoch', 'before_val_epoch', - 'before_val_iter', 'after_val_iter', 'after_val_epoch', - 'after_run') - - def before_run(self, runner): - pass - - def after_run(self, runner): - pass - - def before_epoch(self, runner): - pass - - def after_epoch(self, runner): - pass - - def before_iter(self, runner): - pass - - def after_iter(self, runner): - pass - - def before_train_epoch(self, runner): - self.before_epoch(runner) - - def before_val_epoch(self, runner): - self.before_epoch(runner) - - def after_train_epoch(self, runner): - self.after_epoch(runner) - - def after_val_epoch(self, runner): - self.after_epoch(runner) - - def before_train_iter(self, runner): - self.before_iter(runner) - - def before_val_iter(self, runner): - self.before_iter(runner) - - def after_train_iter(self, runner): - self.after_iter(runner) - - def after_val_iter(self, runner): - self.after_iter(runner) - - def every_n_epochs(self, runner, n): - return (runner.epoch + 1) % n == 0 if n > 0 else False - - def every_n_inner_iters(self, runner, n): - return (runner.inner_iter + 1) % n == 0 if n > 0 else False - - def every_n_iters(self, runner, n): - return (runner.iter + 1) % n == 0 if n > 0 else False - - def end_of_epoch(self, runner): - return runner.inner_iter + 1 == len(runner.data_loader) - - def is_last_epoch(self, runner): - return runner.epoch + 1 == runner._max_epochs - - def is_last_iter(self, runner): - return runner.iter + 1 == runner._max_iters - - def get_triggered_stages(self): - trigger_stages = set() - for stage in Hook.stages: - if is_method_overridden(stage, Hook, self): - trigger_stages.add(stage) - - # some methods will be triggered in multi stages - # use this dict to map method to stages. - method_stages_map = { - 'before_epoch': ['before_train_epoch', 'before_val_epoch'], - 'after_epoch': ['after_train_epoch', 'after_val_epoch'], - 'before_iter': ['before_train_iter', 'before_val_iter'], - 'after_iter': ['after_train_iter', 'after_val_iter'], - } - - for method, map_stages in method_stages_map.items(): - if is_method_overridden(method, Hook, self): - trigger_stages.update(map_stages) - - return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/iter_timer.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/iter_timer.py deleted file mode 100755 index 0d11c2ce8..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/iter_timer.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import time -import torch.distributed as dist - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class IterTimerHook(Hook): - - def before_epoch(self, runner): - self.t = time.time() - - def before_iter(self, runner): - try: - self.batch_size = runner.cfg.data.samples_per_gpu - except: - self.batch_size = None - runner.log_buffer.update({'data_time': time.time() - self.t}) - - def after_iter(self, runner): - iter_info = {'time': time.time() - self.t} - - if self.batch_size is not None: - fps = self.batch_size / iter_info["time"] - if dist.is_initialized(): - fps = fps * dist.get_world_size() - iter_info["fps"] = fps - - runner.log_buffer.update(iter_info) - self.t = time.time() \ No newline at end of file diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/__init__.py deleted file mode 100755 index a0b6b3456..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import LoggerHook -from .dvclive import DvcliveLoggerHook -from .mlflow import MlflowLoggerHook -from .neptune import NeptuneLoggerHook -from .pavi import PaviLoggerHook -from .tensorboard import TensorboardLoggerHook -from .text import TextLoggerHook -from .wandb import WandbLoggerHook - -__all__ = [ - 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', - 'NeptuneLoggerHook', 'DvcliveLoggerHook' -] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/base.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/base.py deleted file mode 100755 index f84525672..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/base.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from abc import ABCMeta, abstractmethod - -import numpy as np -import torch - -from ..hook import Hook - - -class LoggerHook(Hook): - """Base class for logger hooks. - - Args: - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging. - by_epoch (bool): Whether EpochBasedRunner is used. - """ - - __metaclass__ = ABCMeta - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - self.interval = interval - self.ignore_last = ignore_last - self.reset_flag = reset_flag - self.by_epoch = by_epoch - - @abstractmethod - def log(self, runner): - pass - - @staticmethod - def is_scalar(val, include_np=True, include_torch=True): - """Tell the input variable is a scalar or not. - - Args: - val: Input variable. - include_np (bool): Whether include 0-d np.ndarray as a scalar. - include_torch (bool): Whether include 0-d torch.Tensor as a scalar. - - Returns: - bool: True or False. - """ - if isinstance(val, numbers.Number): - return True - elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: - return True - elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: - return True - else: - return False - - def get_mode(self, runner): - if runner.mode == 'train': - if 'time' in runner.log_buffer.output: - mode = 'train' - else: - mode = 'val' - elif runner.mode == 'val': - mode = 'val' - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return mode - - def get_epoch(self, runner): - if runner.mode == 'train': - epoch = runner.epoch + 1 - elif runner.mode == 'val': - # normal val mode - # runner.epoch += 1 has been done before val workflow - epoch = runner.epoch - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return epoch - - def get_iter(self, runner, inner_iter=False): - """Get the current training iteration step.""" - if self.by_epoch and inner_iter: - current_iter = runner.inner_iter + 1 - else: - current_iter = runner.iter + 1 - return current_iter - - def get_lr_tags(self, runner): - tags = {} - lrs = runner.current_lr() - if isinstance(lrs, dict): - for name, value in lrs.items(): - tags[f'learning_rate/{name}'] = value[0] - else: - tags['learning_rate'] = lrs[0] - return tags - - def get_momentum_tags(self, runner): - tags = {} - momentums = runner.current_momentum() - if isinstance(momentums, dict): - for name, value in momentums.items(): - tags[f'momentum/{name}'] = value[0] - else: - tags['momentum'] = momentums[0] - return tags - - def get_loggable_tags(self, - runner, - allow_scalar=True, - allow_text=False, - add_mode=True, - tags_to_skip=('time', 'data_time')): - tags = {} - for var, val in runner.log_buffer.output.items(): - if var in tags_to_skip: - continue - if self.is_scalar(val) and not allow_scalar: - continue - if isinstance(val, str) and not allow_text: - continue - if add_mode: - var = f'{self.get_mode(runner)}/{var}' - tags[var] = val - tags.update(self.get_lr_tags(runner)) - tags.update(self.get_momentum_tags(runner)) - return tags - - def before_run(self, runner): - for hook in runner.hooks[::-1]: - if isinstance(hook, LoggerHook): - hook.reset_flag = True - break - - def before_epoch(self, runner): - runner.log_buffer.clear() # clear logs of last epoch - - def after_train_iter(self, runner): - if self.by_epoch and self.every_n_inner_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif not self.by_epoch and self.every_n_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif self.end_of_epoch(runner) and not self.ignore_last: - # not precise but more stable - runner.log_buffer.average(self.interval) - - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_train_epoch(self, runner): - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_val_epoch(self, runner): - runner.log_buffer.average() - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/dvclive.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/dvclive.py deleted file mode 100755 index 687cdc58c..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/dvclive.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class DvcliveLoggerHook(LoggerHook): - """Class to log metrics with dvclive. - - It requires `dvclive`_ to be installed. - - Args: - path (str): Directory where dvclive will write TSV log files. - interval (int): Logging interval (every k iterations). - Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. - Default: True. - - .. _dvclive: - https://dvc.org/doc/dvclive - """ - - def __init__(self, - path, - interval=10, - ignore_last=True, - reset_flag=True, - by_epoch=True): - - super(DvcliveLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.path = path - self.import_dvclive() - - def import_dvclive(self): - try: - import dvclive - except ImportError: - raise ImportError( - 'Please run "pip install dvclive" to install dvclive') - self.dvclive = dvclive - - @master_only - def before_run(self, runner): - self.dvclive.init(self.path) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for k, v in tags.items(): - self.dvclive.log(k, v, step=self.get_iter(runner)) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/mlflow.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/mlflow.py deleted file mode 100755 index f9a72592b..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/mlflow.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class MlflowLoggerHook(LoggerHook): - - def __init__(self, - exp_name=None, - tags=None, - log_model=True, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - """Class to log metrics and (optionally) a trained model to MLflow. - - It requires `MLflow`_ to be installed. - - Args: - exp_name (str, optional): Name of the experiment to be used. - Default None. - If not None, set the active experiment. - If experiment does not exist, an experiment with provided name - will be created. - tags (dict of str: str, optional): Tags for the current run. - Default None. - If not None, set tags for the current run. - log_model (bool, optional): Whether to log an MLflow artifact. - Default True. - If True, log runner.model as an MLflow artifact - for the current run. - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging - by_epoch (bool): Whether EpochBasedRunner is used. - - .. _MLflow: - https://www.mlflow.org/docs/latest/index.html - """ - super(MlflowLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_mlflow() - self.exp_name = exp_name - self.tags = tags - self.log_model = log_model - - def import_mlflow(self): - try: - import mlflow - import mlflow.pytorch as mlflow_pytorch - except ImportError: - raise ImportError( - 'Please run "pip install mlflow" to install mlflow') - self.mlflow = mlflow - self.mlflow_pytorch = mlflow_pytorch - - @master_only - def before_run(self, runner): - super(MlflowLoggerHook, self).before_run(runner) - if self.exp_name is not None: - self.mlflow.set_experiment(self.exp_name) - if self.tags is not None: - self.mlflow.set_tags(self.tags) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.mlflow.log_metrics(tags, step=self.get_iter(runner)) - - @master_only - def after_run(self, runner): - if self.log_model: - self.mlflow_pytorch.log_model(runner.model, 'models') diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/neptune.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/neptune.py deleted file mode 100755 index 7a38772b0..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/neptune.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class NeptuneLoggerHook(LoggerHook): - """Class to log metrics to NeptuneAI. - - It requires `neptune-client` to be installed. - - Args: - init_kwargs (dict): a dict contains the initialization keys as below: - - project (str): Name of a project in a form of - namespace/project_name. If None, the value of - NEPTUNE_PROJECT environment variable will be taken. - - api_token (str): User’s API token. - If None, the value of NEPTUNE_API_TOKEN environment - variable will be taken. Note: It is strongly recommended - to use NEPTUNE_API_TOKEN environment variable rather than - placing your API token in plain text in your source code. - - name (str, optional, default is 'Untitled'): Editable name of - the run. Name is displayed in the run's Details and in - Runs table as a column. - Check https://docs.neptune.ai/api-reference/neptune#init for - more init arguments. - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging - by_epoch (bool): Whether EpochBasedRunner is used. - - .. _NeptuneAI: - https://docs.neptune.ai/you-should-know/logging-metadata - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=True, - with_step=True, - by_epoch=True): - - super(NeptuneLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_neptune() - self.init_kwargs = init_kwargs - self.with_step = with_step - - def import_neptune(self): - try: - import neptune.new as neptune - except ImportError: - raise ImportError( - 'Please run "pip install neptune-client" to install neptune') - self.neptune = neptune - self.run = None - - @master_only - def before_run(self, runner): - if self.init_kwargs: - self.run = self.neptune.init(**self.init_kwargs) - else: - self.run = self.neptune.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for tag_name, tag_value in tags.items(): - if self.with_step: - self.run[tag_name].log( - tag_value, step=self.get_iter(runner)) - else: - tags['global_step'] = self.get_iter(runner) - self.run[tag_name].log(tags) - - @master_only - def after_run(self, runner): - self.run.stop() diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/pavi.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/pavi.py deleted file mode 100755 index ba2f6e8df..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/pavi.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp - -import torch -import yaml - -import mmcv -from ....parallel.utils import is_module_wrapper -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class PaviLoggerHook(LoggerHook): - - def __init__(self, - init_kwargs=None, - add_graph=False, - add_last_ckpt=False, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - img_key='img_info'): - super(PaviLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.init_kwargs = init_kwargs - self.add_graph = add_graph - self.add_last_ckpt = add_last_ckpt - self.img_key = img_key - - @master_only - def before_run(self, runner): - super(PaviLoggerHook, self).before_run(runner) - try: - from pavi import SummaryWriter - except ImportError: - raise ImportError('Please run "pip install pavi" to install pavi.') - - self.run_name = runner.work_dir.split('/')[-1] - - if not self.init_kwargs: - self.init_kwargs = dict() - self.init_kwargs['name'] = self.run_name - self.init_kwargs['model'] = runner._model_name - if runner.meta is not None: - if 'config_dict' in runner.meta: - config_dict = runner.meta['config_dict'] - assert isinstance( - config_dict, - dict), ('meta["config_dict"] has to be of a dict, ' - f'but got {type(config_dict)}') - elif 'config_file' in runner.meta: - config_file = runner.meta['config_file'] - config_dict = dict(mmcv.Config.fromfile(config_file)) - else: - config_dict = None - if config_dict is not None: - # 'max_.*iter' is parsed in pavi sdk as the maximum iterations - # to properly set up the progress bar. - config_dict = config_dict.copy() - config_dict.setdefault('max_iter', runner.max_iters) - # non-serializable values are first converted in - # mmcv.dump to json - config_dict = json.loads( - mmcv.dump(config_dict, file_format='json')) - session_text = yaml.dump(config_dict) - self.init_kwargs['session_text'] = session_text - self.writer = SummaryWriter(**self.init_kwargs) - - def get_step(self, runner): - """Get the total training step/epoch.""" - if self.get_mode(runner) == 'val' and self.by_epoch: - return self.get_epoch(runner) - else: - return self.get_iter(runner) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, add_mode=False) - if tags: - self.writer.add_scalars( - self.get_mode(runner), tags, self.get_step(runner)) - - @master_only - def after_run(self, runner): - if self.add_last_ckpt: - ckpt_path = osp.join(runner.work_dir, 'latest.pth') - if osp.islink(ckpt_path): - ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) - - if osp.isfile(ckpt_path): - # runner.epoch += 1 has been done before `after_run`. - iteration = runner.epoch if self.by_epoch else runner.iter - return self.writer.add_snapshot_file( - tag=self.run_name, - snapshot_file_path=ckpt_path, - iteration=iteration) - - # flush the buffer and send a task ending signal to Pavi - self.writer.close() - - @master_only - def before_epoch(self, runner): - if runner.epoch == 0 and self.add_graph: - if is_module_wrapper(runner.model): - _model = runner.model.module - else: - _model = runner.model - device = next(_model.parameters()).device - data = next(iter(runner.data_loader)) - image = data[self.img_key][0:1].to(device) - with torch.no_grad(): - self.writer.add_graph(_model, image) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py deleted file mode 100755 index a8d50366f..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import TORCH_VERSION, digit_version -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TensorboardLoggerHook(LoggerHook): - - def __init__(self, - log_dir=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(TensorboardLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.log_dir = log_dir - - @master_only - def before_run(self, runner): - super(TensorboardLoggerHook, self).before_run(runner) - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.1')): - try: - from tensorboardX import SummaryWriter - except ImportError: - raise ImportError('Please install tensorboardX to use ' - 'TensorboardLoggerHook.') - else: - try: - from torch.utils.tensorboard import SummaryWriter - except ImportError: - raise ImportError( - 'Please run "pip install future tensorboard" to install ' - 'the dependencies to use torch.utils.tensorboard ' - '(applicable to PyTorch 1.1 or higher)') - - if self.log_dir is None: - self.log_dir = osp.join(runner.work_dir, 'tf_logs') - self.writer = SummaryWriter(self.log_dir) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, allow_text=True) - for tag, val in tags.items(): - if isinstance(val, str): - self.writer.add_text(tag, val, self.get_iter(runner)) - else: - self.writer.add_scalar(tag, val, self.get_iter(runner)) - - @master_only - def after_run(self, runner): - self.writer.close() diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/text.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/text.py deleted file mode 100755 index 043c7bf20..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/text.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import datetime -import os -import os.path as osp -from collections import OrderedDict - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.fileio.file_client import FileClient -from mmcv.utils import is_tuple_of, scandir -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TextLoggerHook(LoggerHook): - """Logger hook in text. - - In this logger hook, the information will be printed on terminal and - saved in json file. - - Args: - by_epoch (bool, optional): Whether EpochBasedRunner is used. - Default: True. - interval (int, optional): Logging interval (every k iterations). - Default: 10. - ignore_last (bool, optional): Ignore the log of last iterations in each - epoch if less than :attr:`interval`. Default: True. - reset_flag (bool, optional): Whether to clear the output buffer after - logging. Default: False. - interval_exp_name (int, optional): Logging interval for experiment - name. This feature is to help users conveniently get the experiment - information from screen or log file. Default: 1000. - out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. - If ``out_dir`` is specified, logs will be copied to a new directory - which is the concatenation of ``out_dir`` and the last level - directory of ``runner.work_dir``. Default: None. - `New in version 1.3.16.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be copied to ``out_dir``. - Default: ('.log.json', '.log', '.py'). - `New in version 1.3.16.` - keep_local (bool, optional): Whether to keep local log when - :attr:`out_dir` is specified. If False, the local log will be - removed. Default: True. - `New in version 1.3.16.` - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - - def __init__(self, - by_epoch=True, - interval=10, - ignore_last=True, - reset_flag=False, - interval_exp_name=1000, - out_dir=None, - out_suffix=('.log.json', '.log', '.py'), - keep_local=True, - file_client_args=None): - super(TextLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.by_epoch = by_epoch - self.time_sec_tot = 0 - self.interval_exp_name = interval_exp_name - - if out_dir is None and file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" when `out_dir` is not' - 'specified.') - self.out_dir = out_dir - - if not (out_dir is None or isinstance(out_dir, str) - or is_tuple_of(out_dir, str)): - raise TypeError('out_dir should be "None" or string or tuple of ' - 'string, but got {out_dir}') - self.out_suffix = out_suffix - - self.keep_local = keep_local - self.file_client_args = file_client_args - if self.out_dir is not None: - self.file_client = FileClient.infer_client(file_client_args, - self.out_dir) - - def before_run(self, runner): - super(TextLoggerHook, self).before_run(runner) - - if self.out_dir is not None: - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - # The final `self.out_dir` is the concatenation of `self.out_dir` - # and the last level directory of `runner.work_dir` - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'Text logs will be saved to {self.out_dir} by ' - f'{self.file_client.name} after the training process.')) - - self.start_iter = runner.iter - self.json_log_path = osp.join(runner.work_dir, - f'{runner.timestamp}.log.json') - if runner.meta is not None: - self._dump_log(runner.meta, runner) - - def _get_max_memory(self, runner): - device = getattr(runner.model, 'output_device', None) - mem = torch.cuda.max_memory_allocated(device=device) - mem_mb = torch.tensor([mem / (1024 * 1024)], - dtype=torch.int, - device=device) - if runner.world_size > 1: - dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) - return mem_mb.item() - - def _log_info(self, log_dict, runner): - # print exp name for users to distinguish experiments - # at every ``interval_exp_name`` iterations and the end of each epoch - if runner.meta is not None and 'exp_name' in runner.meta: - if (self.every_n_iters(runner, self.interval_exp_name)) or ( - self.by_epoch and self.end_of_epoch(runner)): - exp_info = f'Exp name: {runner.meta["exp_name"]}' - runner.logger.info(exp_info) - - if log_dict['mode'] == 'train': - if isinstance(log_dict['lr'], dict): - lr_str = [] - for k, val in log_dict['lr'].items(): - lr_str.append(f'lr_{k}: {val:.3e}') - lr_str = ' '.join(lr_str) - else: - lr_str = f'lr: {log_dict["lr"]:.3e}' - - # by epoch: Epoch [4][100/1000] - # by iter: Iter [100/100000] - if self.by_epoch: - log_str = f'Epoch [{log_dict["epoch"]}]' \ - f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' - else: - log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' - log_str += f'{lr_str}, ' - - if 'time' in log_dict.keys(): - self.time_sec_tot += (log_dict['time'] * self.interval) - time_sec_avg = self.time_sec_tot / ( - runner.iter - self.start_iter + 1) - eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) - eta_str = str(datetime.timedelta(seconds=int(eta_sec))) - log_str += f'eta: {eta_str}, ' - log_str += f'time: {log_dict["time"]:.3f}, ' \ - f'data_time: {log_dict["data_time"]:.3f}, ' - # statistic memory - if torch.cuda.is_available(): - log_str += f'memory: {log_dict["memory"]}, ' - else: - # val/test time - # here 1000 is the length of the val dataloader - # by epoch: Epoch[val] [4][1000] - # by iter: Iter[val] [1000] - if self.by_epoch: - log_str = f'Epoch({log_dict["mode"]}) ' \ - f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' - else: - log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' - - log_items = [] - for name, val in log_dict.items(): - # TODO: resolve this hack - # these items have been in log_str - if name in [ - 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', - 'memory', 'epoch' - ]: - continue - if isinstance(val, float): - val = f'{val:.4f}' - log_items.append(f'{name}: {val}') - log_str += ', '.join(log_items) - - runner.logger.info(log_str) - - def _dump_log(self, log_dict, runner): - # dump log in json format - json_log = OrderedDict() - for k, v in log_dict.items(): - json_log[k] = self._round_float(v) - # only append log at last line - if runner.rank == 0: - with open(self.json_log_path, 'a+') as f: - mmcv.dump(json_log, f, file_format='json') - f.write('\n') - - def _round_float(self, items): - if isinstance(items, list): - return [self._round_float(item) for item in items] - elif isinstance(items, float): - return round(items, 5) - else: - return items - - def log(self, runner): - if 'eval_iter_num' in runner.log_buffer.output: - # this doesn't modify runner.iter and is regardless of by_epoch - cur_iter = runner.log_buffer.output.pop('eval_iter_num') - else: - cur_iter = self.get_iter(runner, inner_iter=True) - - log_dict = OrderedDict( - mode=self.get_mode(runner), - epoch=self.get_epoch(runner), - iter=cur_iter) - - # only record lr of the first param group - cur_lr = runner.current_lr() - if isinstance(cur_lr, list): - log_dict['lr'] = cur_lr[0] - else: - assert isinstance(cur_lr, dict) - log_dict['lr'] = {} - for k, lr_ in cur_lr.items(): - assert isinstance(lr_, list) - log_dict['lr'].update({k: lr_[0]}) - - if 'time' in runner.log_buffer.output: - # statistic memory - if torch.cuda.is_available(): - log_dict['memory'] = self._get_max_memory(runner) - - log_dict = dict(log_dict, **runner.log_buffer.output) - - self._log_info(log_dict, runner) - self._dump_log(log_dict, runner) - return log_dict - - def after_run(self, runner): - # copy or upload logs to self.out_dir - if self.out_dir is not None: - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - out_filepath = self.file_client.join_path( - self.out_dir, filename) - with open(local_filepath, 'r') as f: - self.file_client.put_text(f.read(), out_filepath) - - runner.logger.info( - (f'The file {local_filepath} has been uploaded to ' - f'{out_filepath}.')) - - if not self.keep_local: - os.remove(local_filepath) - runner.logger.info( - (f'{local_filepath} was removed due to the ' - '`self.keep_local=False`')) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/wandb.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/wandb.py deleted file mode 100755 index 9f6808462..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/logger/wandb.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class WandbLoggerHook(LoggerHook): - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - commit=True, - by_epoch=True, - with_step=True): - super(WandbLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_wandb() - self.init_kwargs = init_kwargs - self.commit = commit - self.with_step = with_step - - def import_wandb(self): - try: - import wandb - except ImportError: - raise ImportError( - 'Please run "pip install wandb" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(WandbLoggerHook, self).before_run(runner) - if self.wandb is None: - self.import_wandb() - if self.init_kwargs: - self.wandb.init(**self.init_kwargs) - else: - self.wandb.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - if self.with_step: - self.wandb.log( - tags, step=self.get_iter(runner), commit=self.commit) - else: - tags['global_step'] = self.get_iter(runner) - self.wandb.log(tags, commit=self.commit) - - @master_only - def after_run(self, runner): - self.wandb.join() diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/lr_updater.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/lr_updater.py deleted file mode 100755 index e5a124157..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/lr_updater.py +++ /dev/null @@ -1,670 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from math import cos, pi - -import mmcv -from .hook import HOOKS, Hook - - -class LrUpdaterHook(Hook): - """LR Scheduler in MMCV. - - Args: - by_epoch (bool): LR changes epoch by epoch - warmup (string): Type of warmup used. It can be None(use no warmup), - 'constant', 'linear' or 'exp' - warmup_iters (int): The number of iterations or epochs that warmup - lasts - warmup_ratio (float): LR used at the beginning of warmup equals to - warmup_ratio * initial_lr - warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters - means the number of epochs that warmup lasts, otherwise means the - number of iteration that warmup lasts - """ - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.1, - warmup_by_epoch=False): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_ratio" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - self.warmup_by_epoch = warmup_by_epoch - - if self.warmup_by_epoch: - self.warmup_epochs = self.warmup_iters - self.warmup_iters = None - else: - self.warmup_epochs = None - - self.base_lr = [] # initial lr for all param groups - self.regular_lr = [] # expected lr if no warming up is performed - - def _set_lr(self, runner, lr_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, lr in zip(optim.param_groups, lr_groups[k]): - param_group['lr'] = lr - else: - for param_group, lr in zip(runner.optimizer.param_groups, - lr_groups): - param_group['lr'] = lr - - def get_lr(self, runner, base_lr): - raise NotImplementedError - - def get_regular_lr(self, runner): - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(runner, _base_lr) - for _base_lr in self.base_lr[k] - ] - lr_groups.update({k: _lr_group}) - - return lr_groups - else: - return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - if self.warmup == 'constant': - warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_lr = [_lr * (1 - k) for _lr in regular_lr] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.regular_lr, dict): - lr_groups = {} - for key, regular_lr in self.regular_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.regular_lr) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - group.setdefault('initial_lr', group['lr']) - _base_lr = [ - group['initial_lr'] for group in optim.param_groups - ] - self.base_lr.update({k: _base_lr}) - else: - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['initial_lr'] for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if self.warmup_iters is None: - epoch_len = len(runner.data_loader) - self.warmup_iters = self.warmup_epochs * epoch_len - - if not self.by_epoch: - return - - self.regular_lr = self.get_regular_lr(runner) - self._set_lr(runner, self.regular_lr) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_lr = self.get_regular_lr(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - - -@HOOKS.register_module() -class FixedLrUpdaterHook(LrUpdaterHook): - - def __init__(self, **kwargs): - super(FixedLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - return base_lr - - -@HOOKS.register_module() -class StepLrUpdaterHook(LrUpdaterHook): - """Step LR scheduler with min_lr clipping. - - Args: - step (int | list[int]): Step to decay the LR. If an int value is given, - regard it as the decay interval. If a list is given, decay LR at - these steps. - gamma (float, optional): Decay LR ratio. Default: 0.1. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. If None - is given, we don't perform lr clipping. Default: None. - """ - - def __init__(self, step, gamma=0.1, min_lr=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_lr = min_lr - super(StepLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - lr = base_lr * (self.gamma**exp) - if self.min_lr is not None: - # clip to a minimum value - lr = max(lr, self.min_lr) - return lr - - -@HOOKS.register_module() -class ExpLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, **kwargs): - self.gamma = gamma - super(ExpLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * self.gamma**progress - - -@HOOKS.register_module() -class PolyLrUpdaterHook(LrUpdaterHook): - - def __init__(self, power=1., min_lr=0., **kwargs): - self.power = power - self.min_lr = min_lr - super(PolyLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - coeff = (1 - progress / max_progress)**self.power - return (base_lr - self.min_lr) * coeff + self.min_lr - - -@HOOKS.register_module() -class InvLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, power=1., **kwargs): - self.gamma = gamma - self.power = power - super(InvLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * (1 + self.gamma * progress)**(-self.power) - - -@HOOKS.register_module() -class CosineAnnealingLrUpdaterHook(LrUpdaterHook): - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(CosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): - """Flat + Cosine lr schedule. - - Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 - - Args: - start_percent (float): When to start annealing the learning rate - after the percentage of the total training steps. - The value should be in range [0, 1). - Default: 0.75 - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - start_percent=0.75, - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - if start_percent < 0 or start_percent > 1 or not isinstance( - start_percent, float): - raise ValueError( - 'expected float between 0 and 1 start_percent, but ' - f'got {start_percent}') - self.start_percent = start_percent - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(FlatCosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - start = round(runner.max_epochs * self.start_percent) - progress = runner.epoch - start - max_progress = runner.max_epochs - start - else: - start = round(runner.max_iters * self.start_percent) - progress = runner.iter - start - max_progress = runner.max_iters - start - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress < 0: - return base_lr - else: - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class CosineRestartLrUpdaterHook(LrUpdaterHook): - """Cosine annealing with restarts learning rate scheme. - - Args: - periods (list[int]): Periods for each cosine anneling cycle. - restart_weights (list[float], optional): Restart weights at each - restart iteration. Default: [1]. - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - periods, - restart_weights=[1], - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.periods = periods - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - self.restart_weights = restart_weights - assert (len(self.periods) == len(self.restart_weights) - ), 'periods and restart_weights should have the same length.' - super(CosineRestartLrUpdaterHook, self).__init__(**kwargs) - - self.cumulative_periods = [ - sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) - ] - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - else: - progress = runner.iter - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - idx = get_position_from_periods(progress, self.cumulative_periods) - current_weight = self.restart_weights[idx] - nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] - current_periods = self.periods[idx] - - alpha = min((progress - nearest_restart) / current_periods, 1) - return annealing_cos(base_lr, target_lr, alpha, current_weight) - - -def get_position_from_periods(iteration, cumulative_periods): - """Get the position from a period list. - - It will return the index of the right-closest number in the period list. - For example, the cumulative_periods = [100, 200, 300, 400], - if iteration == 50, return 0; - if iteration == 210, return 2; - if iteration == 300, return 3. - - Args: - iteration (int): Current iteration. - cumulative_periods (list[int]): Cumulative period list. - - Returns: - int: The position of the right-closest number in the period list. - """ - for i, period in enumerate(cumulative_periods): - if iteration < period: - return i - raise ValueError(f'Current iteration {iteration} exceeds ' - f'cumulative_periods {cumulative_periods}') - - -@HOOKS.register_module() -class CyclicLrUpdaterHook(LrUpdaterHook): - """Cyclic LR Scheduler. - - Implement the cyclical learning rate policy (CLR) described in - https://arxiv.org/pdf/1506.01186.pdf - - Different from the original paper, we use cosine annealing rather than - triangular policy inside a cycle. This improves the performance in the - 3D detection area. - - Args: - by_epoch (bool): Whether to update LR by epoch. - target_ratio (tuple[float]): Relative ratio of the highest LR and the - lowest LR to the initial LR. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of LR in - the total cycle. - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - """ - - def __init__(self, - by_epoch=False, - target_ratio=(10, 1e-4), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.lr_phases = [] # init lr_phases - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicLrUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicLrUpdaterHook, self).before_run(runner) - # initiate lr_phases - # total lr_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.lr_phases.append( - [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) - self.lr_phases.append([ - iter_up_phase, max_iter_per_phase, max_iter_per_phase, - self.target_ratio[0], self.target_ratio[1] - ]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - for (start_iter, end_iter, max_iter_per_phase, start_ratio, - end_ratio) in self.lr_phases: - curr_iter %= max_iter_per_phase - if start_iter <= curr_iter < end_iter: - progress = curr_iter - start_iter - return self.anneal_func(base_lr * start_ratio, - base_lr * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleLrUpdaterHook(LrUpdaterHook): - """One Cycle LR Scheduler. - - The 1cycle learning rate policy changes the learning rate after every - batch. The one cycle learning rate policy is described in - https://arxiv.org/pdf/1708.07120.pdf - - Args: - max_lr (float or list): Upper learning rate boundaries in the cycle - for each parameter group. - total_steps (int, optional): The total number of steps in the cycle. - Note that if a value is not provided here, it will be the max_iter - of runner. Default: None. - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - div_factor (float): Determines the initial learning rate via - initial_lr = max_lr/div_factor - Default: 25 - final_div_factor (float): Determines the minimum learning rate via - min_lr = initial_lr/final_div_factor - Default: 1e4 - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - max_lr, - total_steps=None, - pct_start=0.3, - anneal_strategy='cos', - div_factor=25, - final_div_factor=1e4, - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch = False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(max_lr, (numbers.Number, list, dict)): - raise ValueError('the type of max_lr must be the one of list or ' - f'dict, but got {type(max_lr)}') - self._max_lr = max_lr - if total_steps is not None: - if not isinstance(total_steps, int): - raise ValueError('the type of total_steps must be int, but' - f'got {type(total_steps)}') - self.total_steps = total_steps - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.div_factor = div_factor - self.final_div_factor = final_div_factor - self.three_phase = three_phase - self.lr_phases = [] # init lr_phases - super(OneCycleLrUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if hasattr(self, 'total_steps'): - total_steps = self.total_steps - else: - total_steps = runner.max_iters - if total_steps < runner.max_iters: - raise ValueError( - 'The total steps must be greater than or equal to max ' - f'iterations {runner.max_iters} of runner, but total steps ' - f'is {total_steps}.') - - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - _max_lr = format_param(k, optim, self._max_lr) - self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(optim.param_groups, self.base_lr[k]): - group.setdefault('initial_lr', lr) - else: - k = type(runner.optimizer).__name__ - _max_lr = format_param(k, runner.optimizer, self._max_lr) - self.base_lr = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(runner.optimizer.param_groups, self.base_lr): - group.setdefault('initial_lr', lr) - - if self.three_phase: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append([ - float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 - ]) - self.lr_phases.append( - [total_steps - 1, 1, 1 / self.final_div_factor]) - else: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append( - [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - start_iter = 0 - for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): - if curr_iter <= end_iter: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, - pct) - break - start_iter = end_iter - return lr - - -def annealing_cos(start, end, factor, weight=1): - """Calculate annealing cos learning rate. - - Cosine anneal from `weight * start + (1 - weight) * end` to `end` as - percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the cosine annealing. - end (float): The ending learing rate of the cosine annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - weight (float, optional): The combination factor of `start` and `end` - when calculating the actual starting learning rate. Default to 1. - """ - cos_out = cos(pi * factor) + 1 - return end + 0.5 * weight * (start - end) * cos_out - - -def annealing_linear(start, end, factor): - """Calculate annealing linear learning rate. - - Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the linear annealing. - end (float): The ending learing rate of the linear annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - """ - return start + (end - start) * factor - - -def format_param(name, optim, param): - if isinstance(param, numbers.Number): - return [param] * len(optim.param_groups) - elif isinstance(param, (list, tuple)): # multi param groups - if len(param) != len(optim.param_groups): - raise ValueError(f'expected {len(optim.param_groups)} ' - f'values for {name}, got {len(param)}') - return param - else: # multi optimizers - if name not in param: - raise KeyError(f'{name} is not found in {param.keys()}') - return param[name] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/memory.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/memory.py deleted file mode 100755 index 70cf9a838..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class EmptyCacheHook(Hook): - - def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): - self._before_epoch = before_epoch - self._after_epoch = after_epoch - self._after_iter = after_iter - - def after_iter(self, runner): - if self._after_iter: - torch.cuda.empty_cache() - - def before_epoch(self, runner): - if self._before_epoch: - torch.cuda.empty_cache() - - def after_epoch(self, runner): - if self._after_epoch: - torch.cuda.empty_cache() diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/momentum_updater.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/momentum_updater.py deleted file mode 100755 index 13d0e2fab..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/momentum_updater.py +++ /dev/null @@ -1,493 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -from .hook import HOOKS, Hook -from .lr_updater import annealing_cos, annealing_linear, format_param - - -class MomentumUpdaterHook(Hook): - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.9): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_momentum" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - - self.base_momentum = [] # initial momentum for all param groups - self.regular_momentum = [ - ] # expected momentum if no warming up is performed - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, base_momentum): - raise NotImplementedError - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k in runner.optimizer.keys(): - _momentum_group = [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum[k] - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - return [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum - ] - - def get_warmup_momentum(self, cur_iters): - - def _get_warmup_momentum(cur_iters, regular_momentum): - if self.warmup == 'constant': - warmup_momentum = [ - _momentum / self.warmup_ratio - for _momentum in self.regular_momentum - ] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_momentum = [ - _momentum / (1 - k) for _momentum in self.regular_mom - ] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_momentum = [ - _momentum / k for _momentum in self.regular_mom - ] - return warmup_momentum - - if isinstance(self.regular_momentum, dict): - momentum_groups = {} - for key, regular_momentum in self.regular_momentum.items(): - momentum_groups[key] = _get_warmup_momentum( - cur_iters, regular_momentum) - return momentum_groups - else: - return _get_warmup_momentum(cur_iters, self.regular_momentum) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, - # if 'initial_momentum' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_momentum = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - _base_momentum = [ - group['initial_momentum'] for group in optim.param_groups - ] - self.base_momentum.update({k: _base_momentum}) - else: - for group in runner.optimizer.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - self.base_momentum = [ - group['initial_momentum'] - for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if not self.by_epoch: - return - self.regular_mom = self.get_regular_momentum(runner) - self._set_momentum(runner, self.regular_mom) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_mom = self.get_regular_momentum(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_momentum(runner, self.regular_mom) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_momentum(runner, self.regular_mom) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - - -@HOOKS.register_module() -class StepMomentumUpdaterHook(MomentumUpdaterHook): - """Step momentum scheduler with min value clipping. - - Args: - step (int | list[int]): Step to decay the momentum. If an int value is - given, regard it as the decay interval. If a list is given, decay - momentum at these steps. - gamma (float, optional): Decay momentum ratio. Default: 0.5. - min_momentum (float, optional): Minimum momentum value to keep. If - momentum after decay is lower than this value, it will be clipped - accordingly. If None is given, we don't perform lr clipping. - Default: None. - """ - - def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_momentum = min_momentum - super(StepMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - momentum = base_momentum * (self.gamma**exp) - if self.min_momentum is not None: - # clip to a minimum value - momentum = max(momentum, self.min_momentum) - return momentum - - -@HOOKS.register_module() -class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(CosineAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_cos(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class CyclicMomentumUpdaterHook(MomentumUpdaterHook): - """Cyclic momentum Scheduler. - - Implement the cyclical momentum scheduler policy described in - https://arxiv.org/pdf/1708.07120.pdf - - This momentum scheduler usually used together with the CyclicLRUpdater - to improve the performance in the 3D detection area. - - Attributes: - target_ratio (tuple[float]): Relative ratio of the lowest momentum and - the highest momentum to the initial momentum. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of momentum - in the total cycle. - by_epoch (bool): Whether to update momentum by epoch. - """ - - def __init__(self, - by_epoch=False, - target_ratio=(0.85 / 0.95, 1), - cyclic_times=1, - step_ratio_up=0.4, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.momentum_phases = [] # init momentum_phases - # currently only support by_epoch=False - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicMomentumUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicMomentumUpdaterHook, self).before_run(runner) - # initiate momentum_phases - # total momentum_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.momentum_phases.append( - [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) - self.momentum_phases.append([ - iter_up_phase, max_iter_per_phase, max_iter_per_phase, - self.target_ratio[0], self.target_ratio[1] - ]) - - def get_momentum(self, runner, base_momentum): - curr_iter = runner.iter - for (start_iter, end_iter, max_iter_per_phase, start_ratio, - end_ratio) in self.momentum_phases: - curr_iter %= max_iter_per_phase - if start_iter <= curr_iter < end_iter: - progress = curr_iter - start_iter - return annealing_cos(base_momentum * start_ratio, - base_momentum * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): - """OneCycle momentum Scheduler. - - This momentum scheduler usually used together with the OneCycleLrUpdater - to improve the performance. - - Args: - base_momentum (float or list): Lower momentum boundaries in the cycle - for each parameter group. Note that momentum is cycled inversely - to learning rate; at the peak of a cycle, momentum is - 'base_momentum' and learning rate is 'max_lr'. - Default: 0.85 - max_momentum (float or list): Upper momentum boundaries in the cycle - for each parameter group. Functionally, - it defines the cycle amplitude (max_momentum - base_momentum). - Note that momentum is cycled inversely - to learning rate; at the start of a cycle, momentum is - 'max_momentum' and learning rate is 'base_lr' - Default: 0.95 - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - base_momentum=0.85, - max_momentum=0.95, - pct_start=0.3, - anneal_strategy='cos', - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch=False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(base_momentum, (float, list, dict)): - raise ValueError('base_momentum must be the type among of float,' - 'list or dict.') - self._base_momentum = base_momentum - if not isinstance(max_momentum, (float, list, dict)): - raise ValueError('max_momentum must be the type among of float,' - 'list or dict.') - self._max_momentum = max_momentum - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('Expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must by one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.three_phase = three_phase - self.momentum_phases = [] # init momentum_phases - super(OneCycleMomentumUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip( - optim.param_groups, _base_momentum, _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - else: - optim = runner.optimizer - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - k = type(optim).__name__ - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip(optim.param_groups, - _base_momentum, - _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - - if self.three_phase: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': - float(2 * self.pct_start * runner.max_iters) - 2, - 'start_momentum': - 'base_momentum', - 'end_momentum': - 'max_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'max_momentum', - 'end_momentum': 'max_momentum' - }) - else: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'base_momentum', - 'end_momentum': 'max_momentum' - }) - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, param_group): - curr_iter = runner.iter - start_iter = 0 - for i, phase in enumerate(self.momentum_phases): - end_iter = phase['end_iter'] - if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - momentum = self.anneal_func( - param_group[phase['start_momentum']], - param_group[phase['end_momentum']], pct) - break - start_iter = end_iter - return momentum - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k, optim in runner.optimizer.items(): - _momentum_group = [ - self.get_momentum(runner, param_group) - for param_group in optim.param_groups - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - momentum_groups = [] - for param_group in runner.optimizer.param_groups: - momentum_groups.append(self.get_momentum(runner, param_group)) - return momentum_groups diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/optimizer.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/optimizer.py deleted file mode 100755 index f575ceda0..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/optimizer.py +++ /dev/null @@ -1,508 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -from collections import defaultdict -from itertools import chain - -from torch.nn.utils import clip_grad - -from mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version -from ..dist_utils import allreduce_grads -from ..fp16_utils import LossScaler, wrap_fp16_model -from .hook import HOOKS, Hook - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - from torch.cuda.amp import GradScaler -except ImportError: - pass - - -@HOOKS.register_module() -class OptimizerHook(Hook): - - def __init__(self, grad_clip=None): - self.grad_clip = grad_clip - - def clip_grads(self, params): - params = list( - filter(lambda p: p.requires_grad and p.grad is not None, params)) - if len(params) > 0: - return clip_grad.clip_grad_norm_(params, **self.grad_clip) - - def after_train_iter(self, runner): - runner.optimizer.zero_grad() - runner.outputs['loss'].backward() - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - - -@HOOKS.register_module() -class GradientCumulativeOptimizerHook(OptimizerHook): - """Optimizer Hook implements multi-iters gradient cumulating. - - Args: - cumulative_iters (int, optional): Num of gradient cumulative iters. - The optimizer will step every `cumulative_iters` iters. - Defaults to 1. - - Examples: - >>> # Use cumulative_iters to simulate a large batch size - >>> # It is helpful when the hardware cannot handle a large batch size. - >>> loader = DataLoader(data, batch_size=64) - >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) - >>> # almost equals to - >>> loader = DataLoader(data, batch_size=256) - >>> optim_hook = OptimizerHook() - """ - - def __init__(self, cumulative_iters=1, **kwargs): - super(GradientCumulativeOptimizerHook, self).__init__(**kwargs) - - assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ - f'cumulative_iters only accepts positive int, but got ' \ - f'{type(cumulative_iters)} instead.' - - self.cumulative_iters = cumulative_iters - self.divisible_iters = 0 - self.remainder_iters = 0 - self.initialized = False - - def has_batch_norm(self, module): - if isinstance(module, _BatchNorm): - return True - for m in module.children(): - if self.has_batch_norm(m): - return True - return False - - def _init(self, runner): - if runner.iter % self.cumulative_iters != 0: - runner.logger.warning( - 'Resume iter number is not divisible by cumulative_iters in ' - 'GradientCumulativeOptimizerHook, which means the gradient of ' - 'some iters is lost and the result may be influenced slightly.' - ) - - if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: - runner.logger.warning( - 'GradientCumulativeOptimizerHook may slightly decrease ' - 'performance if the model has BatchNorm layers.') - - residual_iters = runner.max_iters - runner.iter - - self.divisible_iters = ( - residual_iters // self.cumulative_iters * self.cumulative_iters) - self.remainder_iters = residual_iters - self.divisible_iters - - self.initialized = True - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - runner.optimizer.zero_grad() - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - self.loss_scaler = GradScaler() - elif isinstance(loss_scale, float): - self._scale_update_param = loss_scale - self.loss_scaler = GradScaler(init_scale=loss_scale) - elif isinstance(loss_scale, dict): - self.loss_scaler = GradScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training.""" - # wrap model mode to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer to - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients. - 3. Unscale the optimizer’s gradient tensors. - 4. Call optimizer.step() and update scale factor. - 5. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - - self.loss_scaler.scale(runner.outputs['loss']).backward() - self.loss_scaler.unscale_(runner.optimizer) - # grad clip - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using PyTorch's implementation) implements - multi-iters gradient cumulating. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - """ - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - - self.loss_scaler.scale(loss).backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - self.loss_scaler.unscale_(runner.optimizer) - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() - -else: - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (mmcv's implementation). - - The steps of fp16 optimizer is as follows. - 1. Scale the loss value. - 2. BP in the fp16 model. - 2. Copy gradients from fp16 model to fp32 weights. - 3. Update fp32 weights. - 4. Copy updated parameters from fp32 weights to fp16 model. - - Refer to https://arxiv.org/abs/1710.03740 for more details. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of LossScaler. - Defaults to 512. - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - if loss_scale == 'dynamic': - self.loss_scaler = LossScaler(mode='dynamic') - elif isinstance(loss_scale, float): - self.loss_scaler = LossScaler( - init_scale=loss_scale, mode='static') - elif isinstance(loss_scale, dict): - self.loss_scaler = LossScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training. - - 1. Make a master copy of fp32 weights for optimization. - 2. Convert the main model from fp32 to fp16. - """ - # keep a copy of fp32 weights - old_groups = runner.optimizer.param_groups - runner.optimizer.param_groups = copy.deepcopy( - runner.optimizer.param_groups) - state = defaultdict(dict) - p_map = { - old_p: p - for old_p, p in zip( - chain(*(g['params'] for g in old_groups)), - chain(*(g['params'] - for g in runner.optimizer.param_groups))) - } - for k, v in runner.optimizer.state.items(): - state[p_map[k]] = v - runner.optimizer.state = state - # convert model to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer `loss_scalar.py` - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients (fp16). - 3. Copy gradients from the model to the fp32 weight copy. - 4. Scale the gradients back and update the fp32 weight copy. - 5. Copy back the params from fp32 weight copy to the fp16 model. - 6. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - # scale the loss value - scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale - scaled_loss.backward() - # copy fp16 grads in the model to fp32 params in the optimizer - - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - self.loss_scaler.update_scale(has_overflow) - if has_overflow: - runner.logger.warning('Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using mmcv implementation) implements multi- - iters gradient cumulating.""" - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - - loss = runner.outputs['loss'] - loss = loss / loss_factor - - # scale the loss value - scaled_loss = loss * self.loss_scaler.loss_scale - scaled_loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - else: - runner.logger.warning( - 'Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - self.loss_scaler.update_scale(has_overflow) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/profiler.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/profiler.py deleted file mode 100755 index b70236997..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/profiler.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Callable, List, Optional, Union - -import torch - -from ..dist_utils import master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ProfilerHook(Hook): - """Profiler to analyze performance during training. - - PyTorch Profiler is a tool that allows the collection of the performance - metrics during the training. More details on Profiler can be found at - https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile - - Args: - by_epoch (bool): Profile performance by epoch or by iteration. - Default: True. - profile_iters (int): Number of iterations for profiling. - If ``by_epoch=True``, profile_iters indicates that they are the - first profile_iters epochs at the beginning of the - training, otherwise it indicates the first profile_iters - iterations. Default: 1. - activities (list[str]): List of activity groups (CPU, CUDA) to use in - profiling. Default: ['cpu', 'cuda']. - schedule (dict, optional): Config of generating the callable schedule. - if schedule is None, profiler will not add step markers into the - trace and table view. Default: None. - on_trace_ready (callable, dict): Either a handler or a dict of generate - handler. Default: None. - record_shapes (bool): Save information about operator's input shapes. - Default: False. - profile_memory (bool): Track tensor memory allocation/deallocation. - Default: False. - with_stack (bool): Record source information (file and line number) - for the ops. Default: False. - with_flops (bool): Use formula to estimate the FLOPS of specific - operators (matrix multiplication and 2D convolution). - Default: False. - json_trace_path (str, optional): Exports the collected trace in Chrome - JSON format. Default: None. - - Example: - >>> runner = ... # instantiate a Runner - >>> # tensorboard trace - >>> trace_config = dict(type='tb_trace', dir_name='work_dir') - >>> profiler_config = dict(on_trace_ready=trace_config) - >>> runner.register_profiler_hook(profiler_config) - >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) - """ - - def __init__(self, - by_epoch: bool = True, - profile_iters: int = 1, - activities: List[str] = ['cpu', 'cuda'], - schedule: Optional[dict] = None, - on_trace_ready: Optional[Union[Callable, dict]] = None, - record_shapes: bool = False, - profile_memory: bool = False, - with_stack: bool = False, - with_flops: bool = False, - json_trace_path: Optional[str] = None) -> None: - try: - from torch import profiler # torch version >= 1.8.1 - except ImportError: - raise ImportError('profiler is the new feature of torch1.8.1, ' - f'but your version is {torch.__version__}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' - self.by_epoch = by_epoch - - if profile_iters < 1: - raise ValueError('profile_iters should be greater than 0, but got ' - f'{profile_iters}') - self.profile_iters = profile_iters - - if not isinstance(activities, list): - raise ValueError( - f'activities should be list, but got {type(activities)}') - self.activities = [] - for activity in activities: - activity = activity.lower() - if activity == 'cpu': - self.activities.append(profiler.ProfilerActivity.CPU) - elif activity == 'cuda': - self.activities.append(profiler.ProfilerActivity.CUDA) - else: - raise ValueError( - f'activity should be "cpu" or "cuda", but got {activity}') - - if schedule is not None: - self.schedule = profiler.schedule(**schedule) - else: - self.schedule = None - - self.on_trace_ready = on_trace_ready - self.record_shapes = record_shapes - self.profile_memory = profile_memory - self.with_stack = with_stack - self.with_flops = with_flops - self.json_trace_path = json_trace_path - - @master_only - def before_run(self, runner): - if self.by_epoch and runner.max_epochs < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_epochs}') - - if not self.by_epoch and runner.max_iters < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_iters}') - - if callable(self.on_trace_ready): # handler - _on_trace_ready = self.on_trace_ready - elif isinstance(self.on_trace_ready, dict): # config of handler - trace_cfg = self.on_trace_ready.copy() - trace_type = trace_cfg.pop('type') # log_trace handler - if trace_type == 'log_trace': - - def _log_handler(prof): - print(prof.key_averages().table(**trace_cfg)) - - _on_trace_ready = _log_handler - elif trace_type == 'tb_trace': # tensorboard_trace handler - try: - import torch_tb_profiler # noqa: F401 - except ImportError: - raise ImportError('please run "pip install ' - 'torch-tb-profiler" to install ' - 'torch_tb_profiler') - _on_trace_ready = torch.profiler.tensorboard_trace_handler( - **trace_cfg) - else: - raise ValueError('trace_type should be "log_trace" or ' - f'"tb_trace", but got {trace_type}') - elif self.on_trace_ready is None: - _on_trace_ready = None # type: ignore - else: - raise ValueError('on_trace_ready should be handler, dict or None, ' - f'but got {type(self.on_trace_ready)}') - - if runner.max_epochs > 1: - warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' - 'instead of 1 epoch. Since profiler will slow down ' - 'the training, it is recommended to train 1 epoch ' - 'with ProfilerHook and adjust your setting according' - ' to the profiler summary. During normal training ' - '(epoch > 1), you may disable the ProfilerHook.') - - self.profiler = torch.profiler.profile( - activities=self.activities, - schedule=self.schedule, - on_trace_ready=_on_trace_ready, - record_shapes=self.record_shapes, - profile_memory=self.profile_memory, - with_stack=self.with_stack, - with_flops=self.with_flops) - - self.profiler.__enter__() - runner.logger.info('profiler is profiling...') - - @master_only - def after_train_epoch(self, runner): - if self.by_epoch and runner.epoch == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) - - @master_only - def after_train_iter(self, runner): - self.profiler.step() - if not self.by_epoch and runner.iter == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/sampler_seed.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/sampler_seed.py deleted file mode 100755 index ee0dc6bdd..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/sampler_seed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class DistSamplerSeedHook(Hook): - """Data-loading sampler for distributed training. - - When distributed training, it is only useful in conjunction with - :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same - purpose with :obj:`IterLoader`. - """ - - def before_epoch(self, runner): - if hasattr(runner.data_loader.sampler, 'set_epoch'): - # in case the data loader uses `SequentialSampler` in Pytorch - runner.data_loader.sampler.set_epoch(runner.epoch) - elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): - # batch sampler in pytorch warps the sampler as its attributes. - runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/sync_buffer.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/sync_buffer.py deleted file mode 100755 index 6376b7ff8..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/hooks/sync_buffer.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..dist_utils import allreduce_params -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class SyncBuffersHook(Hook): - """Synchronize model buffers such as running_mean and running_var in BN at - the end of each epoch. - - Args: - distributed (bool): Whether distributed training is used. It is - effective only for distributed training. Defaults to True. - """ - - def __init__(self, distributed=True): - self.distributed = distributed - - def after_epoch(self, runner): - """All-reduce model buffers at the end of each epoch.""" - if self.distributed: - allreduce_params(runner.model.buffers()) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/iter_based_runner.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/iter_based_runner.py deleted file mode 100755 index 9892b07a4..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/iter_based_runner.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch -from torch.optim import Optimizer - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .hooks import IterTimerHook -from .utils import get_host_info - - -class IterLoader: - - def __init__(self, dataloader): - self._dataloader = dataloader - self.iter_loader = iter(self._dataloader) - self._epoch = 0 - - @property - def epoch(self): - return self._epoch - - def __next__(self): - try: - data = next(self.iter_loader) - except StopIteration: - self._epoch += 1 - if hasattr(self._dataloader.sampler, 'set_epoch'): - self._dataloader.sampler.set_epoch(self._epoch) - time.sleep(2) # Prevent possible deadlock during epoch transition - self.iter_loader = iter(self._dataloader) - data = next(self.iter_loader) - - return data - - def __len__(self): - return len(self._dataloader) - - -@RUNNERS.register_module() -class IterBasedRunner(BaseRunner): - """Iteration-based Runner. - - This runner train models iteration by iteration. - """ - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._epoch = data_loader.epoch - data_batch = next(data_loader) - self.call_hook('before_train_iter') - outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.train_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_train_iter') - self._inner_iter += 1 - self._iter += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - data_batch = next(data_loader) - self.call_hook('before_val_iter') - outputs = self.model.val_step(data_batch, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.val_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_val_iter') - self._inner_iter += 1 - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, iters) to specify the - running order and iterations. E.g, [('train', 10000), - ('val', 1000)] means running 10000 iterations for training and - 1000 iterations for validation, iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - """Resume model from checkpoint. - - Args: - checkpoint (str): Checkpoint to resume from. - resume_optimizer (bool, optional): Whether resume the optimizer(s) - if the checkpoint file includes optimizer(s). Default to True. - map_location (str, optional): Same as :func:`torch.load`. - Default to 'default'. - """ - if map_location == 'default': - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - self._inner_iter = checkpoint['meta']['iter'] - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') - - def save_checkpoint(self, - out_dir, - filename_tmpl='iter_{}.pth', - meta=None, - save_optimizer=True, - create_symlink=True): - """Save checkpoint to file. - - Args: - out_dir (str): Directory to save checkpoint files. - filename_tmpl (str, optional): Checkpoint file template. - Defaults to 'iter_{}.pth'. - meta (dict, optional): Metadata to be saved in checkpoint. - Defaults to None. - save_optimizer (bool, optional): Whether save optimizer. - Defaults to True. - create_symlink (bool, optional): Whether create symlink to the - latest checkpoint file. Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.iter + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - custom_hooks_config=None): - """Register default hooks for iter-based training. - - Checkpoint hook, optimizer stepper hook and logger hooks will be set to - `by_epoch=False` by default. - - Default hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - if checkpoint_config is not None: - checkpoint_config.setdefault('by_epoch', False) - if lr_config is not None: - lr_config.setdefault('by_epoch', False) - if log_config is not None: - for info in log_config['hooks']: - info.setdefault('by_epoch', False) - super(IterBasedRunner, self).register_training_hooks( - lr_config=lr_config, - momentum_config=momentum_config, - optimizer_config=optimizer_config, - checkpoint_config=checkpoint_config, - log_config=log_config, - timer_config=IterTimerHook(), - custom_hooks_config=custom_hooks_config) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/log_buffer.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/log_buffer.py deleted file mode 100755 index d949e2941..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/log_buffer.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import numpy as np - - -class LogBuffer: - - def __init__(self): - self.val_history = OrderedDict() - self.n_history = OrderedDict() - self.output = OrderedDict() - self.ready = False - - def clear(self): - self.val_history.clear() - self.n_history.clear() - self.clear_output() - - def clear_output(self): - self.output.clear() - self.ready = False - - def update(self, vars, count=1): - assert isinstance(vars, dict) - for key, var in vars.items(): - if key not in self.val_history: - self.val_history[key] = [] - self.n_history[key] = [] - self.val_history[key].append(var) - self.n_history[key].append(count) - - def average(self, n=0): - """Average latest n values or all values.""" - assert n >= 0 - for key in self.val_history: - values = np.array(self.val_history[key][-n:]) - nums = np.array(self.n_history[key][-n:]) - avg = np.sum(values * nums) / np.sum(nums) - self.output[key] = avg - self.ready = True diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/optimizer/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/optimizer/__init__.py deleted file mode 100755 index 53c34d047..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/optimizer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, - build_optimizer_constructor) -from .default_constructor import DefaultOptimizerConstructor - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', - 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/optimizer/builder.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/optimizer/builder.py deleted file mode 100755 index f9234eed8..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/optimizer/builder.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect - -import torch - -from ...utils import Registry, build_from_cfg - -OPTIMIZERS = Registry('optimizer') -OPTIMIZER_BUILDERS = Registry('optimizer builder') - - -def register_torch_optimizers(): - torch_optimizers = [] - for module_name in dir(torch.optim): - if module_name.startswith('__'): - continue - _optim = getattr(torch.optim, module_name) - if inspect.isclass(_optim) and issubclass(_optim, - torch.optim.Optimizer): - OPTIMIZERS.register_module()(_optim) - torch_optimizers.append(module_name) - return torch_optimizers - - -TORCH_OPTIMIZERS = register_torch_optimizers() - - -def build_optimizer_constructor(cfg): - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/optimizer/default_constructor.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/optimizer/default_constructor.py deleted file mode 100755 index effd1e170..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/optimizer/default_constructor.py +++ /dev/null @@ -1,249 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -from torch.nn import GroupNorm, LayerNorm - -from mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of -from mmcv.utils.ext_loader import check_ops_exist -from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS - - -@OPTIMIZER_BUILDERS.register_module() -class DefaultOptimizerConstructor: - """Default constructor for optimizers. - - By default each parameter share the same optimizer settings, and we - provide an argument ``paramwise_cfg`` to specify parameter-wise settings. - It is a dict and may contain the following fields: - - - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If - one of the keys in ``custom_keys`` is a substring of the name of one - parameter, then the setting of the parameter will be specified by - ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will - be ignored. It should be noted that the aforementioned ``key`` is the - longest key that is a substring of the name of the parameter. If there - are multiple matched keys with the same length, then the key with lower - alphabet order will be chosen. - ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` - and ``decay_mult``. See Example 2 below. - - ``bias_lr_mult`` (float): It will be multiplied to the learning - rate for all bias parameters (except for those in normalization - layers and offset layers of DCN). - - ``bias_decay_mult`` (float): It will be multiplied to the weight - decay for all bias parameters (except for those in - normalization layers, depthwise conv layers, offset layers of DCN). - - ``norm_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of normalization - layers. - - ``dwconv_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of depthwise conv - layers. - - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning - rate for parameters of offset layer in the deformable convs - of a model. - - ``bypass_duplicate`` (bool): If true, the duplicate parameters - would not be added into optimizer. Default: False. - - Note: - 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will - override the effect of ``bias_lr_mult`` in the bias of offset - layer. So be careful when using both ``bias_lr_mult`` and - ``dcn_offset_lr_mult``. If you wish to apply both of them to the - offset layer in deformable convs, set ``dcn_offset_lr_mult`` - to the original ``dcn_offset_lr_mult`` * ``bias_lr_mult``. - 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will - apply it to all the DCN layers in the model. So be careful when - the model contains multiple DCN layers in places other than - backbone. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - optimizer_cfg (dict): The config dict of the optimizer. - Positional fields are - - - `type`: class name of the optimizer. - - Optional fields are - - - any arguments of the corresponding optimizer type, e.g., - lr, weight_decay, momentum, etc. - paramwise_cfg (dict, optional): Parameter-wise options. - - Example 1: - >>> model = torch.nn.modules.Conv1d(1, 1, 1) - >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, - >>> weight_decay=0.0001) - >>> paramwise_cfg = dict(norm_decay_mult=0.) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - - Example 2: - >>> # assume model have attribute model.backbone and model.cls_head - >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) - >>> paramwise_cfg = dict(custom_keys={ - '.backbone': dict(lr_mult=0.1, decay_mult=0.9)}) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - >>> # Then the `lr` and `weight_decay` for model.backbone is - >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for - >>> # model.cls_head is (0.01, 0.95). - """ - - def __init__(self, optimizer_cfg, paramwise_cfg=None): - if not isinstance(optimizer_cfg, dict): - raise TypeError('optimizer_cfg should be a dict', - f'but got {type(optimizer_cfg)}') - self.optimizer_cfg = optimizer_cfg - self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg - self.base_lr = optimizer_cfg.get('lr', None) - self.base_wd = optimizer_cfg.get('weight_decay', None) - self._validate_cfg() - - def _validate_cfg(self): - if not isinstance(self.paramwise_cfg, dict): - raise TypeError('paramwise_cfg should be None or a dict, ' - f'but got {type(self.paramwise_cfg)}') - - if 'custom_keys' in self.paramwise_cfg: - if not isinstance(self.paramwise_cfg['custom_keys'], dict): - raise TypeError( - 'If specified, custom_keys must be a dict, ' - f'but got {type(self.paramwise_cfg["custom_keys"])}') - if self.base_wd is None: - for key in self.paramwise_cfg['custom_keys']: - if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: - raise ValueError('base_wd should not be None') - - # get base lr and weight decay - # weight_decay must be explicitly specified if mult is specified - if ('bias_decay_mult' in self.paramwise_cfg - or 'norm_decay_mult' in self.paramwise_cfg - or 'dwconv_decay_mult' in self.paramwise_cfg): - if self.base_wd is None: - raise ValueError('base_wd should not be None') - - def _is_in(self, param_group, param_group_list): - assert is_list_of(param_group_list, dict) - param = set(param_group['params']) - param_set = set() - for group in param_group_list: - param_set.update(set(group['params'])) - - return not param.isdisjoint(param_set) - - def add_params(self, params, module, prefix='', is_dcn_module=None): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - prefix (str): The prefix of the module - is_dcn_module (int|float|None): If the current module is a - submodule of DCN, `is_dcn_module` will be passed to - control conv_offset layer's learning rate. Defaults to None. - """ - # get param-wise options - custom_keys = self.paramwise_cfg.get('custom_keys', {}) - # first sort with alphabet order and then sort with reversed len of str - sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) - - bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) - bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) - norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) - dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) - bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) - dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) - - # special rules for norm layers and depth-wise conv layers - is_norm = isinstance(module, - (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) - is_dwconv = ( - isinstance(module, torch.nn.Conv2d) - and module.in_channels == module.groups) - - for name, param in module.named_parameters(recurse=False): - param_group = {'params': [param]} - if not param.requires_grad: - params.append(param_group) - continue - if bypass_duplicate and self._is_in(param_group, params): - warnings.warn(f'{prefix} is duplicate. It is skipped since ' - f'bypass_duplicate={bypass_duplicate}') - continue - # if the parameter match one of the custom keys, ignore other rules - is_custom = False - for key in sorted_keys: - if key in f'{prefix}.{name}': - is_custom = True - lr_mult = custom_keys[key].get('lr_mult', 1.) - param_group['lr'] = self.base_lr * lr_mult - if self.base_wd is not None: - decay_mult = custom_keys[key].get('decay_mult', 1.) - param_group['weight_decay'] = self.base_wd * decay_mult - break - - if not is_custom: - # bias_lr_mult affects all bias parameters - # except for norm.bias dcn.conv_offset.bias - if name == 'bias' and not (is_norm or is_dcn_module): - param_group['lr'] = self.base_lr * bias_lr_mult - - if (prefix.find('conv_offset') != -1 and is_dcn_module - and isinstance(module, torch.nn.Conv2d)): - # deal with both dcn_offset's bias & weight - param_group['lr'] = self.base_lr * dcn_offset_lr_mult - - # apply weight decay policies - if self.base_wd is not None: - # norm decay - if is_norm: - param_group[ - 'weight_decay'] = self.base_wd * norm_decay_mult - # depth-wise conv - elif is_dwconv: - param_group[ - 'weight_decay'] = self.base_wd * dwconv_decay_mult - # bias lr and decay - elif name == 'bias' and not is_dcn_module: - # TODO: current bias_decay_mult will have affect on DCN - param_group[ - 'weight_decay'] = self.base_wd * bias_decay_mult - params.append(param_group) - - if check_ops_exist(): - from mmcv.ops import DeformConv2d, ModulatedDeformConv2d - is_dcn_module = isinstance(module, - (DeformConv2d, ModulatedDeformConv2d)) - else: - is_dcn_module = False - for child_name, child_mod in module.named_children(): - child_prefix = f'{prefix}.{child_name}' if prefix else child_name - self.add_params( - params, - child_mod, - prefix=child_prefix, - is_dcn_module=is_dcn_module) - - def __call__(self, model): - if hasattr(model, 'module'): - model = model.module - - optimizer_cfg = self.optimizer_cfg.copy() - # if no paramwise option is specified, just use the global setting - if not self.paramwise_cfg: - optimizer_cfg['params'] = model.parameters() - return build_from_cfg(optimizer_cfg, OPTIMIZERS) - - # set param-wise lr and weight decay recursively - params = [] - self.add_params(params, model) - optimizer_cfg['params'] = params - - return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/priority.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/priority.py deleted file mode 100755 index 64cc4e3a0..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/priority.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from enum import Enum - - -class Priority(Enum): - """Hook priority levels. - - +--------------+------------+ - | Level | Value | - +==============+============+ - | HIGHEST | 0 | - +--------------+------------+ - | VERY_HIGH | 10 | - +--------------+------------+ - | HIGH | 30 | - +--------------+------------+ - | ABOVE_NORMAL | 40 | - +--------------+------------+ - | NORMAL | 50 | - +--------------+------------+ - | BELOW_NORMAL | 60 | - +--------------+------------+ - | LOW | 70 | - +--------------+------------+ - | VERY_LOW | 90 | - +--------------+------------+ - | LOWEST | 100 | - +--------------+------------+ - """ - - HIGHEST = 0 - VERY_HIGH = 10 - HIGH = 30 - ABOVE_NORMAL = 40 - NORMAL = 50 - BELOW_NORMAL = 60 - LOW = 70 - VERY_LOW = 90 - LOWEST = 100 - - -def get_priority(priority): - """Get priority value. - - Args: - priority (int or str or :obj:`Priority`): Priority. - - Returns: - int: The priority value. - """ - if isinstance(priority, int): - if priority < 0 or priority > 100: - raise ValueError('priority must be between 0 and 100') - return priority - elif isinstance(priority, Priority): - return priority.value - elif isinstance(priority, str): - return Priority[priority.upper()].value - else: - raise TypeError('priority must be an integer or Priority enum value') diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/utils.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/utils.py deleted file mode 100755 index 144d11e1a..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/runner/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random -import sys -import time -import warnings -from getpass import getuser -from socket import gethostname - -import numpy as np -import torch - -import mmcv - - -def get_host_info(): - """Get hostname and username. - - Return empty string if exception raised, e.g. ``getpass.getuser()`` will - lead to error in docker container - """ - host = '' - try: - host = f'{getuser()}@{gethostname()}' - except Exception as e: - warnings.warn(f'Host or user not found: {str(e)}') - finally: - return host - - -def get_time_str(): - return time.strftime('%Y%m%d_%H%M%S', time.localtime()) - - -def obj_from_dict(info, parent=None, default_args=None): - """Initialize an object from dict. - - The dict must contain the key "type", which indicates the object type, it - can be either a string or type, such as "list" or ``list``. Remaining - fields are treated as the arguments for constructing the object. - - Args: - info (dict): Object types and arguments. - parent (:class:`module`): Module which may containing expected object - classes. - default_args (dict, optional): Default arguments for initializing the - object. - - Returns: - any type: Object built from the dict. - """ - assert isinstance(info, dict) and 'type' in info - assert isinstance(default_args, dict) or default_args is None - args = info.copy() - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if parent is not None: - obj_type = getattr(parent, obj_type) - else: - obj_type = sys.modules[obj_type] - elif not isinstance(obj_type, type): - raise TypeError('type must be a str or valid type, but ' - f'got {type(obj_type)}') - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - return obj_type(**args) - - -def set_random_seed(seed, deterministic=False, use_rank_shift=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - rank_shift (bool): Whether to add rank number to the random seed to - have different random seed in different threads. Default: False. - """ - if use_rank_shift: - rank, _ = mmcv.runner.get_dist_info() - seed += rank - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/__init__.py deleted file mode 100755 index 378a00684..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/__init__.py +++ /dev/null @@ -1,69 +0,0 @@ -# flake8: noqa -# Copyright (c) OpenMMLab. All rights reserved. -from .config import Config, ConfigDict, DictAction -from .misc import (check_prerequisites, concat_list, deprecated_api_warning, - has_method, import_modules_from_strings, is_list_of, - is_method_overridden, is_seq_of, is_str, is_tuple_of, - iter_cast, list_cast, requires_executable, requires_package, - slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, - to_ntuple, tuple_cast) -from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, - scandir, symlink) -from .progressbar import (ProgressBar, track_iter_progress, - track_parallel_progress, track_progress) -from .testing import (assert_attrs_equal, assert_dict_contains_subset, - assert_dict_has_keys, assert_is_norm_layer, - assert_keys_equal, assert_params_all_zeros, - check_python_script) -from .timer import Timer, TimerError, check_time -from .version_utils import digit_version, get_git_hash - -try: - import torch -except ImportError: - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', - 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', - 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', - 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', - 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', - 'track_progress', 'track_iter_progress', 'track_parallel_progress', - 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', - 'digit_version', 'get_git_hash', 'import_modules_from_strings', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', - 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', - 'is_method_overridden', 'has_method' - ] -else: - from .env import collect_env - from .logging import get_logger, print_log - from .parrots_jit import jit, skip_no_elena - from .parrots_wrapper import ( - TORCH_VERSION, BuildExtension, CppExtension, CUDAExtension, DataLoader, - PoolDataLoader, SyncBatchNorm, _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, - _AvgPoolNd, _BatchNorm, _ConvNd, _ConvTransposeMixin, _InstanceNorm, - _MaxPoolNd, get_build_config, is_rocm_pytorch, _get_cuda_home) - from .registry import Registry, build_from_cfg - from .trace import is_jit_tracing - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', - 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', - 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', - 'check_prerequisites', 'requires_package', 'requires_executable', - 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', - 'symlink', 'scandir', 'ProgressBar', 'track_progress', - 'track_iter_progress', 'track_parallel_progress', 'Registry', - 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', - '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', - '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', - 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', - 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', - 'deprecated_api_warning', 'digit_version', 'get_git_hash', - 'import_modules_from_strings', 'jit', 'skip_no_elena', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', - 'assert_params_all_zeros', 'check_python_script', - 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', - '_get_cuda_home', 'has_method' - ] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/config.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/config.py deleted file mode 100755 index db9b35fa4..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/config.py +++ /dev/null @@ -1,689 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import ast -import copy -import os -import os.path as osp -import platform -import shutil -import sys -import tempfile -import uuid -import warnings -from argparse import Action, ArgumentParser -from collections import abc -from importlib import import_module - -from addict import Dict -from yapf.yapflib.yapf_api import FormatCode - -from .misc import import_modules_from_strings -from .path import check_file_exist - -if platform.system() == 'Windows': - import regex as re -else: - import re - -BASE_KEY = '_base_' -DELETE_KEY = '_delete_' -DEPRECATION_KEY = '_deprecation_' -RESERVED_KEYS = ['filename', 'text', 'pretty_text'] - - -class ConfigDict(Dict): - - def __missing__(self, name): - raise KeyError(name) - - def __getattr__(self, name): - try: - value = super(ConfigDict, self).__getattr__(name) - except KeyError: - ex = AttributeError(f"'{self.__class__.__name__}' object has no " - f"attribute '{name}'") - except Exception as e: - ex = e - else: - return value - raise ex - - -def add_args(parser, cfg, prefix=''): - for k, v in cfg.items(): - if isinstance(v, str): - parser.add_argument('--' + prefix + k) - elif isinstance(v, int): - parser.add_argument('--' + prefix + k, type=int) - elif isinstance(v, float): - parser.add_argument('--' + prefix + k, type=float) - elif isinstance(v, bool): - parser.add_argument('--' + prefix + k, action='store_true') - elif isinstance(v, dict): - add_args(parser, v, prefix + k + '.') - elif isinstance(v, abc.Iterable): - parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') - else: - print(f'cannot parse key {prefix + k} of type {type(v)}') - return parser - - -class Config: - """A facility for config and config files. - - It supports common file formats as configs: python/json/yaml. The interface - is the same as a dict object and also allows access config values as - attributes. - - Example: - >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - >>> cfg.a - 1 - >>> cfg.b - {'b1': [0, 1]} - >>> cfg.b.b1 - [0, 1] - >>> cfg = Config.fromfile('tests/data/config/a.py') - >>> cfg.filename - "/home/kchen/projects/mmcv/tests/data/config/a.py" - >>> cfg.item4 - 'test' - >>> cfg - "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " - "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" - """ - - @staticmethod - def _validate_py_syntax(filename): - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - content = f.read() - try: - ast.parse(content) - except SyntaxError as e: - raise SyntaxError('There are syntax errors in config ' - f'file {filename}: {e}') - - @staticmethod - def _substitute_predefined_vars(filename, temp_config_name): - file_dirname = osp.dirname(filename) - file_basename = osp.basename(filename) - file_basename_no_extension = osp.splitext(file_basename)[0] - file_extname = osp.splitext(filename)[1] - support_templates = dict( - fileDirname=file_dirname, - fileBasename=file_basename, - fileBasenameNoExtension=file_basename_no_extension, - fileExtname=file_extname) - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - for key, value in support_templates.items(): - regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' - value = value.replace('\\', '/') - config_file = re.sub(regexp, value, config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - - @staticmethod - def _pre_substitute_base_vars(filename, temp_config_name): - """Substitute base variable placehoders to string, so that parsing - would work.""" - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - base_var_dict = {} - regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' - base_vars = set(re.findall(regexp, config_file)) - for base_var in base_vars: - randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' - base_var_dict[randstr] = base_var - regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' - config_file = re.sub(regexp, f'"{randstr}"', config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - return base_var_dict - - @staticmethod - def _substitute_base_vars(cfg, base_var_dict, base_cfg): - """Substitute variable strings to their actual values.""" - cfg = copy.deepcopy(cfg) - - if isinstance(cfg, dict): - for k, v in cfg.items(): - if isinstance(v, str) and v in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[v].split('.'): - new_v = new_v[new_k] - cfg[k] = new_v - elif isinstance(v, (list, tuple, dict)): - cfg[k] = Config._substitute_base_vars( - v, base_var_dict, base_cfg) - elif isinstance(cfg, tuple): - cfg = tuple( - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg) - elif isinstance(cfg, list): - cfg = [ - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg - ] - elif isinstance(cfg, str) and cfg in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[cfg].split('.'): - new_v = new_v[new_k] - cfg = new_v - - return cfg - - @staticmethod - def _file2dict(filename, use_predefined_variables=True): - filename = osp.abspath(osp.expanduser(filename)) - check_file_exist(filename) - fileExtname = osp.splitext(filename)[1] - if fileExtname not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - - with tempfile.TemporaryDirectory() as temp_config_dir: - temp_config_file = tempfile.NamedTemporaryFile( - dir=temp_config_dir, suffix=fileExtname) - if platform.system() == 'Windows': - temp_config_file.close() - temp_config_name = osp.basename(temp_config_file.name) - # Substitute predefined variables - if use_predefined_variables: - Config._substitute_predefined_vars(filename, - temp_config_file.name) - else: - shutil.copyfile(filename, temp_config_file.name) - # Substitute base variables from placeholders to strings - base_var_dict = Config._pre_substitute_base_vars( - temp_config_file.name, temp_config_file.name) - - if filename.endswith('.py'): - temp_module_name = osp.splitext(temp_config_name)[0] - sys.path.insert(0, temp_config_dir) - Config._validate_py_syntax(filename) - mod = import_module(temp_module_name) - sys.path.pop(0) - cfg_dict = { - name: value - for name, value in mod.__dict__.items() - if not name.startswith('__') - } - # delete imported module - del sys.modules[temp_module_name] - elif filename.endswith(('.yml', '.yaml', '.json')): - import mmcv - cfg_dict = mmcv.load(temp_config_file.name) - # close temp file - temp_config_file.close() - - # check deprecation information - if DEPRECATION_KEY in cfg_dict: - deprecation_info = cfg_dict.pop(DEPRECATION_KEY) - warning_msg = f'The config file {filename} will be deprecated ' \ - 'in the future.' - if 'expected' in deprecation_info: - warning_msg += f' Please use {deprecation_info["expected"]} ' \ - 'instead.' - if 'reference' in deprecation_info: - warning_msg += ' More information can be found at ' \ - f'{deprecation_info["reference"]}' - warnings.warn(warning_msg) - - cfg_text = filename + '\n' - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - cfg_text += f.read() - - if BASE_KEY in cfg_dict: - cfg_dir = osp.dirname(filename) - base_filename = cfg_dict.pop(BASE_KEY) - base_filename = base_filename if isinstance( - base_filename, list) else [base_filename] - - cfg_dict_list = list() - cfg_text_list = list() - for f in base_filename: - _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) - cfg_dict_list.append(_cfg_dict) - cfg_text_list.append(_cfg_text) - - base_cfg_dict = dict() - for c in cfg_dict_list: - duplicate_keys = base_cfg_dict.keys() & c.keys() - if len(duplicate_keys) > 0: - raise KeyError('Duplicate key is not allowed among bases. ' - f'Duplicate keys: {duplicate_keys}') - base_cfg_dict.update(c) - - # Substitute base variables from strings to their actual values - cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, - base_cfg_dict) - - base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) - cfg_dict = base_cfg_dict - - # merge cfg_text - cfg_text_list.append(cfg_text) - cfg_text = '\n'.join(cfg_text_list) - - return cfg_dict, cfg_text - - @staticmethod - def _merge_a_into_b(a, b, allow_list_keys=False): - """merge dict ``a`` into dict ``b`` (non-inplace). - - Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid - in-place modifications. - - Args: - a (dict): The source dict to be merged into ``b``. - b (dict): The origin dict to be fetch keys from ``a``. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in source ``a`` and will replace the element of the - corresponding index in b if b is a list. Default: False. - - Returns: - dict: The modified dict of ``b`` using ``a``. - - Examples: - # Normally merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # Delete b first and merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # b is a list - >>> Config._merge_a_into_b( - ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) - [{'a': 2}, {'b': 2}] - """ - b = b.copy() - for k, v in a.items(): - if allow_list_keys and k.isdigit() and isinstance(b, list): - k = int(k) - if len(b) <= k: - raise KeyError(f'Index {k} exceeds the length of list {b}') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - elif isinstance(v, - dict) and k in b and not v.pop(DELETE_KEY, False): - allowed_types = (dict, list) if allow_list_keys else dict - if not isinstance(b[k], allowed_types): - raise TypeError( - f'{k}={v} in child config cannot inherit from base ' - f'because {k} is a dict in the child config but is of ' - f'type {type(b[k])} in base config. You may set ' - f'`{DELETE_KEY}=True` to ignore the base config') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - else: - b[k] = v - return b - - @staticmethod - def fromfile(filename, - use_predefined_variables=True, - import_custom_modules=True): - cfg_dict, cfg_text = Config._file2dict(filename, - use_predefined_variables) - if import_custom_modules and cfg_dict.get('custom_imports', None): - import_modules_from_strings(**cfg_dict['custom_imports']) - return Config(cfg_dict, cfg_text=cfg_text, filename=filename) - - @staticmethod - def fromstring(cfg_str, file_format): - """Generate config from config str. - - Args: - cfg_str (str): Config str. - file_format (str): Config file format corresponding to the - config str. Only py/yml/yaml/json type are supported now! - - Returns: - obj:`Config`: Config obj. - """ - if file_format not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - if file_format != '.py' and 'dict(' in cfg_str: - # check if users specify a wrong suffix for python - warnings.warn( - 'Please check "file_format", the file format may be .py') - with tempfile.NamedTemporaryFile( - 'w', encoding='utf-8', suffix=file_format, - delete=False) as temp_file: - temp_file.write(cfg_str) - # on windows, previous implementation cause error - # see PR 1077 for details - cfg = Config.fromfile(temp_file.name) - os.remove(temp_file.name) - return cfg - - @staticmethod - def auto_argparser(description=None): - """Generate argparser from config file automatically (experimental)""" - partial_parser = ArgumentParser(description=description) - partial_parser.add_argument('config', help='config file path') - cfg_file = partial_parser.parse_known_args()[0].config - cfg = Config.fromfile(cfg_file) - parser = ArgumentParser(description=description) - parser.add_argument('config', help='config file path') - add_args(parser, cfg) - return parser, cfg - - def __init__(self, cfg_dict=None, cfg_text=None, filename=None): - if cfg_dict is None: - cfg_dict = dict() - elif not isinstance(cfg_dict, dict): - raise TypeError('cfg_dict must be a dict, but ' - f'got {type(cfg_dict)}') - for key in cfg_dict: - if key in RESERVED_KEYS: - raise KeyError(f'{key} is reserved for config file') - - super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) - super(Config, self).__setattr__('_filename', filename) - if cfg_text: - text = cfg_text - elif filename: - with open(filename, 'r') as f: - text = f.read() - else: - text = '' - super(Config, self).__setattr__('_text', text) - - @property - def filename(self): - return self._filename - - @property - def text(self): - return self._text - - @property - def pretty_text(self): - - indent = 4 - - def _indent(s_, num_spaces): - s = s_.split('\n') - if len(s) == 1: - return s_ - first = s.pop(0) - s = [(num_spaces * ' ') + line for line in s] - s = '\n'.join(s) - s = first + '\n' + s - return s - - def _format_basic_types(k, v, use_mapping=False): - if isinstance(v, str): - v_str = f"'{v}'" - else: - v_str = str(v) - - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) - - return attr_str - - def _format_list(k, v, use_mapping=False): - # check if all items in the list are dict - if all(isinstance(_, dict) for _ in v): - v_str = '[\n' - v_str += '\n'.join( - f'dict({_indent(_format_dict(v_), indent)}),' - for v_ in v).rstrip(',') - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) + ']' - else: - attr_str = _format_basic_types(k, v, use_mapping) - return attr_str - - def _contain_invalid_identifier(dict_str): - contain_invalid_identifier = False - for key_name in dict_str: - contain_invalid_identifier |= \ - (not str(key_name).isidentifier()) - return contain_invalid_identifier - - def _format_dict(input_dict, outest_level=False): - r = '' - s = [] - - use_mapping = _contain_invalid_identifier(input_dict) - if use_mapping: - r += '{' - for idx, (k, v) in enumerate(input_dict.items()): - is_last = idx >= len(input_dict) - 1 - end = '' if outest_level or is_last else ',' - if isinstance(v, dict): - v_str = '\n' + _format_dict(v) - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: dict({v_str}' - else: - attr_str = f'{str(k)}=dict({v_str}' - attr_str = _indent(attr_str, indent) + ')' + end - elif isinstance(v, list): - attr_str = _format_list(k, v, use_mapping) + end - else: - attr_str = _format_basic_types(k, v, use_mapping) + end - - s.append(attr_str) - r += '\n'.join(s) - if use_mapping: - r += '}' - return r - - cfg_dict = self._cfg_dict.to_dict() - text = _format_dict(cfg_dict, outest_level=True) - # copied from setup.cfg - yapf_style = dict( - based_on_style='pep8', - blank_line_before_nested_class_or_def=True, - split_before_expression_after_opening_paren=True) - text, _ = FormatCode(text, style_config=yapf_style) - #text, _ = FormatCode(text, style_config=yapf_style, verify=True) - - return text - - def __repr__(self): - return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' - - def __len__(self): - return len(self._cfg_dict) - - def __getattr__(self, name): - return getattr(self._cfg_dict, name) - - def __getitem__(self, name): - return self._cfg_dict.__getitem__(name) - - def __setattr__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setattr__(name, value) - - def __setitem__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setitem__(name, value) - - def __iter__(self): - return iter(self._cfg_dict) - - def __getstate__(self): - return (self._cfg_dict, self._filename, self._text) - - def __setstate__(self, state): - _cfg_dict, _filename, _text = state - super(Config, self).__setattr__('_cfg_dict', _cfg_dict) - super(Config, self).__setattr__('_filename', _filename) - super(Config, self).__setattr__('_text', _text) - - def dump(self, file=None): - cfg_dict = super(Config, self).__getattribute__('_cfg_dict').to_dict() - if self.filename.endswith('.py'): - if file is None: - return self.pretty_text - else: - with open(file, 'w', encoding='utf-8') as f: - f.write(self.pretty_text) - else: - import mmcv - if file is None: - file_format = self.filename.split('.')[-1] - return mmcv.dump(cfg_dict, file_format=file_format) - else: - mmcv.dump(cfg_dict, file) - - def merge_from_dict(self, options, allow_list_keys=True): - """Merge list into cfg_dict. - - Merge the dict parsed by MultipleKVAction into this cfg. - - Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} - >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) - >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - - # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) - >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) - >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - - Args: - options (dict): dict of configs to merge from. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in ``options`` and will replace the element of the - corresponding index in the config if the config is a list. - Default: True. - """ - option_cfg_dict = {} - for full_key, v in options.items(): - d = option_cfg_dict - key_list = full_key.split('.') - for subkey in key_list[:-1]: - d.setdefault(subkey, ConfigDict()) - d = d[subkey] - subkey = key_list[-1] - d[subkey] = v - - cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - super(Config, self).__setattr__( - '_cfg_dict', - Config._merge_a_into_b( - option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) - - -class DictAction(Action): - """ - argparse action to split an argument into KEY=VALUE form - on the first = and append to a dictionary. List options can - be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit - brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build - list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' - """ - - @staticmethod - def _parse_int_float_bool(val): - try: - return int(val) - except ValueError: - pass - try: - return float(val) - except ValueError: - pass - if val.lower() in ['true', 'false']: - return True if val.lower() == 'true' else False - return val - - @staticmethod - def _parse_iterable(val): - """Parse iterable values in the string. - - All elements inside '()' or '[]' are treated as iterable values. - - Args: - val (str): Value string. - - Returns: - list | tuple: The expanded list or tuple from the string. - - Examples: - >>> DictAction._parse_iterable('1,2,3') - [1, 2, 3] - >>> DictAction._parse_iterable('[a, b, c]') - ['a', 'b', 'c'] - >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') - [(1, 2, 3), ['a', 'b'], 'c'] - """ - - def find_next_comma(string): - """Find the position of next comma in the string. - - If no ',' is found in the string, return the string length. All - chars inside '()' and '[]' are treated as one element and thus ',' - inside these brackets are ignored. - """ - assert (string.count('(') == string.count(')')) and ( - string.count('[') == string.count(']')), \ - f'Imbalanced brackets exist in {string}' - end = len(string) - for idx, char in enumerate(string): - pre = string[:idx] - # The string before this ',' is balanced - if ((char == ',') and (pre.count('(') == pre.count(')')) - and (pre.count('[') == pre.count(']'))): - end = idx - break - return end - - # Strip ' and " characters and replace whitespace. - val = val.strip('\'\"').replace(' ', '') - is_tuple = False - if val.startswith('(') and val.endswith(')'): - is_tuple = True - val = val[1:-1] - elif val.startswith('[') and val.endswith(']'): - val = val[1:-1] - elif ',' not in val: - # val is a single value - return DictAction._parse_int_float_bool(val) - - values = [] - while len(val) > 0: - comma_idx = find_next_comma(val) - element = DictAction._parse_iterable(val[:comma_idx]) - values.append(element) - val = val[comma_idx + 1:] - if is_tuple: - values = tuple(values) - return values - - def __call__(self, parser, namespace, values, option_string=None): - options = {} - for kv in values: - key, val = kv.split('=', maxsplit=1) - options[key] = self._parse_iterable(val) - setattr(namespace, self.dest, options) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/env.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/env.py deleted file mode 100755 index e46a1094f..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/env.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This file holding some environment constant for sharing by other files.""" - -import os.path as osp -import subprocess -import sys -from collections import defaultdict - -import cv2 -import torch - -import mmcv -from .parrots_wrapper import get_build_config - - -def collect_env(): - """Collect the information of the running environments. - - Returns: - dict: The environment information. The following fields are contained. - - - sys.platform: The variable of ``sys.platform``. - - Python: Python version. - - CUDA available: Bool, indicating if CUDA is available. - - GPU devices: Device type of each GPU. - - CUDA_HOME (optional): The env var ``CUDA_HOME``. - - NVCC (optional): NVCC version. - - GCC: GCC version, "n/a" if GCC is not installed. - - PyTorch: PyTorch version. - - PyTorch compiling details: The output of \ - ``torch.__config__.show()``. - - TorchVision (optional): TorchVision version. - - OpenCV: OpenCV version. - - MMCV: MMCV version. - - MMCV Compiler: The GCC version for compiling MMCV ops. - - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. - """ - env_info = {} - env_info['sys.platform'] = sys.platform - env_info['Python'] = sys.version.replace('\n', '') - - cuda_available = torch.cuda.is_available() - env_info['CUDA available'] = cuda_available - - if cuda_available: - devices = defaultdict(list) - for k in range(torch.cuda.device_count()): - devices[torch.cuda.get_device_name(k)].append(str(k)) - for name, device_ids in devices.items(): - env_info['GPU ' + ','.join(device_ids)] = name - - from mmcv.utils.parrots_wrapper import _get_cuda_home - CUDA_HOME = _get_cuda_home() - env_info['CUDA_HOME'] = CUDA_HOME - - if CUDA_HOME is not None and osp.isdir(CUDA_HOME): - try: - nvcc = osp.join(CUDA_HOME, 'bin/nvcc') - nvcc = subprocess.check_output( - f'"{nvcc}" -V | tail -n1', shell=True) - nvcc = nvcc.decode('utf-8').strip() - except subprocess.SubprocessError: - nvcc = 'Not Available' - env_info['NVCC'] = nvcc - - try: - gcc = subprocess.check_output('gcc --version | head -n1', shell=True) - gcc = gcc.decode('utf-8').strip() - env_info['GCC'] = gcc - except subprocess.CalledProcessError: # gcc is unavailable - env_info['GCC'] = 'n/a' - - env_info['PyTorch'] = torch.__version__ - env_info['PyTorch compiling details'] = get_build_config() - - try: - import torchvision - env_info['TorchVision'] = torchvision.__version__ - except ModuleNotFoundError: - pass - - env_info['OpenCV'] = cv2.__version__ - - env_info['MMCV'] = mmcv.__version__ - - try: - from mmcv.ops import get_compiler_version, get_compiling_cuda_version - except ModuleNotFoundError: - env_info['MMCV Compiler'] = 'n/a' - env_info['MMCV CUDA Compiler'] = 'n/a' - else: - env_info['MMCV Compiler'] = get_compiler_version() - env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() - - return env_info diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/ext_loader.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/ext_loader.py deleted file mode 100755 index 08132d2c1..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/ext_loader.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os -import pkgutil -import warnings -from collections import namedtuple - -import torch - -if torch.__version__ != 'parrots': - - def load_ext(name, funcs): - ext = importlib.import_module('mmcv.' + name) - for fun in funcs: - assert hasattr(ext, fun), f'{fun} miss in module {name}' - return ext -else: - from parrots import extension - from parrots.base import ParrotsException - - has_return_value_ops = [ - 'nms', - 'softnms', - 'nms_match', - 'nms_rotated', - 'top_pool_forward', - 'top_pool_backward', - 'bottom_pool_forward', - 'bottom_pool_backward', - 'left_pool_forward', - 'left_pool_backward', - 'right_pool_forward', - 'right_pool_backward', - 'fused_bias_leakyrelu', - 'upfirdn2d', - 'ms_deform_attn_forward', - 'pixel_group', - 'contour_expand', - ] - - def get_fake_func(name, e): - - def fake_func(*args, **kwargs): - warnings.warn(f'{name} is not supported in parrots now') - raise e - - return fake_func - - def load_ext(name, funcs): - ExtModule = namedtuple('ExtModule', funcs) - ext_list = [] - lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - for fun in funcs: - try: - ext_fun = extension.load(fun, name, lib_dir=lib_root) - except ParrotsException as e: - if 'No element registered' not in e.message: - warnings.warn(e.message) - ext_fun = get_fake_func(fun, e) - ext_list.append(ext_fun) - else: - if fun in has_return_value_ops: - ext_list.append(ext_fun.op) - else: - ext_list.append(ext_fun.op_) - return ExtModule(*ext_list) - - -def check_ops_exist(): - ext_loader = pkgutil.find_loader('mmcv._ext') - return ext_loader is not None diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/logging.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/logging.py deleted file mode 100755 index 4aa0e04bb..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/logging.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.distributed as dist - -logger_initialized = {} - - -def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): - """Initialize and get a logger by name. - - If the logger has not been initialized, this method will initialize the - logger by adding one or two handlers, otherwise the initialized logger will - be directly returned. During initialization, a StreamHandler will always be - added. If `log_file` is specified and the process rank is 0, a FileHandler - will also be added. - - Args: - name (str): Logger name. - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the logger. - log_level (int): The logger level. Note that only the process of - rank 0 is affected, and other processes will set the level to - "Error" thus be silent most of the time. - file_mode (str): The file mode used in opening log file. - Defaults to 'w'. - - Returns: - logging.Logger: The expected logger. - """ - logger = logging.getLogger(name) - if name in logger_initialized: - return logger - # handle hierarchical names - # e.g., logger "a" is initialized, then logger "a.b" will skip the - # initialization since it is a child of "a". - for logger_name in logger_initialized: - if name.startswith(logger_name): - return logger - - # handle duplicate logs to the console - # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) - # to the root logger. As logger.propagate is True by default, this root - # level handler causes logging messages from rank>0 processes to - # unexpectedly show up on the console, creating much unwanted clutter. - # To fix this issue, we set the root logger's StreamHandler, if any, to log - # at the ERROR level. - for handler in logger.root.handlers: - if type(handler) is logging.StreamHandler: - handler.setLevel(logging.ERROR) - - stream_handler = logging.StreamHandler() - handlers = [stream_handler] - - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - else: - rank = 0 - - # only rank 0 will add a FileHandler - if rank == 0 and log_file is not None: - # Here, the default behaviour of the official logger is 'a'. Thus, we - # provide an interface to change the file mode to the default - # behaviour. - file_handler = logging.FileHandler(log_file, file_mode) - handlers.append(file_handler) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - for handler in handlers: - handler.setFormatter(formatter) - handler.setLevel(log_level) - logger.addHandler(handler) - - if rank == 0: - logger.setLevel(log_level) - else: - logger.setLevel(logging.ERROR) - - logger_initialized[name] = True - - return logger - - -def print_log(msg, logger=None, level=logging.INFO): - """Print a log message. - - Args: - msg (str): The message to be logged. - logger (logging.Logger | str | None): The logger to be used. - Some special loggers are: - - "silent": no message will be printed. - - other str: the logger obtained with `get_root_logger(logger)`. - - None: The `print()` method will be used to print log messages. - level (int): Logging level. Only available when `logger` is a Logger - object or "root". - """ - if logger is None: - print(msg) - elif isinstance(logger, logging.Logger): - logger.log(level, msg) - elif logger == 'silent': - pass - elif isinstance(logger, str): - _logger = get_logger(logger) - _logger.log(level, msg) - else: - raise TypeError( - 'logger should be either a logging.Logger object, str, ' - f'"silent" or None, but got {type(logger)}') diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/misc.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/misc.py deleted file mode 100755 index 2c58d0d7f..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/misc.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections.abc -import functools -import itertools -import subprocess -import warnings -from collections import abc -from importlib import import_module -from inspect import getfullargspec -from itertools import repeat - - -# From PyTorch internals -def _ntuple(n): - - def parse(x): - if isinstance(x, collections.abc.Iterable): - return x - return tuple(repeat(x, n)) - - return parse - - -to_1tuple = _ntuple(1) -to_2tuple = _ntuple(2) -to_3tuple = _ntuple(3) -to_4tuple = _ntuple(4) -to_ntuple = _ntuple - - -def is_str(x): - """Whether the input is an string instance. - - Note: This method is deprecated since python 2 is no longer supported. - """ - return isinstance(x, str) - - -def import_modules_from_strings(imports, allow_failed_imports=False): - """Import modules from the given list of strings. - - Args: - imports (list | str | None): The given module names to be imported. - allow_failed_imports (bool): If True, the failed imports will return - None. Otherwise, an ImportError is raise. Default: False. - - Returns: - list[module] | module | None: The imported modules. - - Examples: - >>> osp, sys = import_modules_from_strings( - ... ['os.path', 'sys']) - >>> import os.path as osp_ - >>> import sys as sys_ - >>> assert osp == osp_ - >>> assert sys == sys_ - """ - if not imports: - return - single_import = False - if isinstance(imports, str): - single_import = True - imports = [imports] - if not isinstance(imports, list): - raise TypeError( - f'custom_imports must be a list but got type {type(imports)}') - imported = [] - for imp in imports: - if not isinstance(imp, str): - raise TypeError( - f'{imp} is of type {type(imp)} and cannot be imported.') - try: - imported_tmp = import_module(imp) - except ImportError: - if allow_failed_imports: - warnings.warn(f'{imp} failed to import and is ignored.', - UserWarning) - imported_tmp = None - else: - raise ImportError - imported.append(imported_tmp) - if single_import: - imported = imported[0] - return imported - - -def iter_cast(inputs, dst_type, return_type=None): - """Cast elements of an iterable object into some type. - - Args: - inputs (Iterable): The input object. - dst_type (type): Destination type. - return_type (type, optional): If specified, the output object will be - converted to this type, otherwise an iterator. - - Returns: - iterator or specified type: The converted object. - """ - if not isinstance(inputs, abc.Iterable): - raise TypeError('inputs must be an iterable object') - if not isinstance(dst_type, type): - raise TypeError('"dst_type" must be a valid type') - - out_iterable = map(dst_type, inputs) - - if return_type is None: - return out_iterable - else: - return return_type(out_iterable) - - -def list_cast(inputs, dst_type): - """Cast elements of an iterable object into a list of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=list) - - -def tuple_cast(inputs, dst_type): - """Cast elements of an iterable object into a tuple of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=tuple) - - -def is_seq_of(seq, expected_type, seq_type=None): - """Check whether it is a sequence of some type. - - Args: - seq (Sequence): The sequence to be checked. - expected_type (type): Expected type of sequence items. - seq_type (type, optional): Expected sequence type. - - Returns: - bool: Whether the sequence is valid. - """ - if seq_type is None: - exp_seq_type = abc.Sequence - else: - assert isinstance(seq_type, type) - exp_seq_type = seq_type - if not isinstance(seq, exp_seq_type): - return False - for item in seq: - if not isinstance(item, expected_type): - return False - return True - - -def is_list_of(seq, expected_type): - """Check whether it is a list of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=list) - - -def is_tuple_of(seq, expected_type): - """Check whether it is a tuple of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=tuple) - - -def slice_list(in_list, lens): - """Slice a list into several sub lists by a list of given length. - - Args: - in_list (list): The list to be sliced. - lens(int or list): The expected length of each out list. - - Returns: - list: A list of sliced list. - """ - if isinstance(lens, int): - assert len(in_list) % lens == 0 - lens = [lens] * int(len(in_list) / lens) - if not isinstance(lens, list): - raise TypeError('"indices" must be an integer or a list of integers') - elif sum(lens) != len(in_list): - raise ValueError('sum of lens and list length does not ' - f'match: {sum(lens)} != {len(in_list)}') - out_list = [] - idx = 0 - for i in range(len(lens)): - out_list.append(in_list[idx:idx + lens[i]]) - idx += lens[i] - return out_list - - -def concat_list(in_list): - """Concatenate a list of list into a single list. - - Args: - in_list (list): The list of list to be merged. - - Returns: - list: The concatenated flat list. - """ - return list(itertools.chain(*in_list)) - - -def check_prerequisites( - prerequisites, - checker, - msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' - 'found, please install them first.'): # yapf: disable - """A decorator factory to check if prerequisites are satisfied. - - Args: - prerequisites (str of list[str]): Prerequisites to be checked. - checker (callable): The checker method that returns True if a - prerequisite is meet, False otherwise. - msg_tmpl (str): The message template with two variables. - - Returns: - decorator: A specific decorator. - """ - - def wrap(func): - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - requirements = [prerequisites] if isinstance( - prerequisites, str) else prerequisites - missing = [] - for item in requirements: - if not checker(item): - missing.append(item) - if missing: - print(msg_tmpl.format(', '.join(missing), func.__name__)) - raise RuntimeError('Prerequisites not meet.') - else: - return func(*args, **kwargs) - - return wrapped_func - - return wrap - - -def _check_py_package(package): - try: - import_module(package) - except ImportError: - return False - else: - return True - - -def _check_executable(cmd): - if subprocess.call(f'which {cmd}', shell=True) != 0: - return False - else: - return True - - -def requires_package(prerequisites): - """A decorator to check if some python packages are installed. - - Example: - >>> @requires_package('numpy') - >>> func(arg1, args): - >>> return numpy.zeros(1) - array([0.]) - >>> @requires_package(['numpy', 'non_package']) - >>> func(arg1, args): - >>> return numpy.zeros(1) - ImportError - """ - return check_prerequisites(prerequisites, checker=_check_py_package) - - -def requires_executable(prerequisites): - """A decorator to check if some executable files are installed. - - Example: - >>> @requires_executable('ffmpeg') - >>> func(arg1, args): - >>> print(1) - 1 - """ - return check_prerequisites(prerequisites, checker=_check_executable) - - -def deprecated_api_warning(name_dict, cls_name=None): - """A decorator to check if some arguments are deprecate and try to replace - deprecate src_arg_name to dst_arg_name. - - Args: - name_dict(dict): - key (str): Deprecate argument names. - val (str): Expected argument names. - - Returns: - func: New function. - """ - - def api_warning_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get name of the function - func_name = old_func.__name__ - if cls_name is not None: - func_name = f'{cls_name}.{func_name}' - if args: - arg_names = args_info.args[:len(args)] - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in arg_names: - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead') - arg_names[arg_names.index(src_arg_name)] = dst_arg_name - if kwargs: - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in kwargs: - - assert dst_arg_name not in kwargs, ( - f'The expected behavior is to replace ' - f'the deprecated key `{src_arg_name}` to ' - f'new key `{dst_arg_name}`, but got them ' - f'in the arguments at the same time, which ' - f'is confusing. `{src_arg_name} will be ' - f'deprecated in the future, please ' - f'use `{dst_arg_name}` instead.') - - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead') - kwargs[dst_arg_name] = kwargs.pop(src_arg_name) - - # apply converted arguments to the decorated method - output = old_func(*args, **kwargs) - return output - - return new_func - - return api_warning_wrapper - - -def is_method_overridden(method, base_class, derived_class): - """Check if a method of base class is overridden in derived class. - - Args: - method (str): the method name to check. - base_class (type): the class of the base class. - derived_class (type | Any): the class or instance of the derived class. - """ - assert isinstance(base_class, type), \ - "base_class doesn't accept instance, Please pass class instead." - - if not isinstance(derived_class, type): - derived_class = derived_class.__class__ - - base_method = getattr(base_class, method) - derived_method = getattr(derived_class, method) - return derived_method != base_method - - -def has_method(obj: object, method: str) -> bool: - """Check whether the object has a method. - - Args: - method (str): The method name to check. - obj (object): The object to check. - - Returns: - bool: True if the object has the method else False. - """ - return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/parrots_jit.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/parrots_jit.py deleted file mode 100755 index 61873f6db..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/parrots_jit.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -from .parrots_wrapper import TORCH_VERSION - -parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') - -if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': - from parrots.jit import pat as jit -else: - - def jit(func=None, - check_input=None, - full_shape=True, - derivate=False, - coderize=False, - optimize=False): - - def wrapper(func): - - def wrapper_inner(*args, **kargs): - return func(*args, **kargs) - - return wrapper_inner - - if func is None: - return wrapper - else: - return func - - -if TORCH_VERSION == 'parrots': - from parrots.utils.tester import skip_no_elena -else: - - def skip_no_elena(func): - - def wrapper(*args, **kargs): - return func(*args, **kargs) - - return wrapper diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/parrots_wrapper.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/parrots_wrapper.py deleted file mode 100755 index 93c97640d..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/parrots_wrapper.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch - -TORCH_VERSION = torch.__version__ - - -def is_rocm_pytorch() -> bool: - is_rocm = False - if TORCH_VERSION != 'parrots': - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - return is_rocm - - -def _get_cuda_home(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import CUDA_HOME - else: - if is_rocm_pytorch(): - from torch.utils.cpp_extension import ROCM_HOME - CUDA_HOME = ROCM_HOME - else: - from torch.utils.cpp_extension import CUDA_HOME - return CUDA_HOME - - -def get_build_config(): - if TORCH_VERSION == 'parrots': - from parrots.config import get_build_info - return get_build_info() - else: - return torch.__config__.show() - - -def _get_conv(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin - else: - from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin - return _ConvNd, _ConvTransposeMixin - - -def _get_dataloader(): - if TORCH_VERSION == 'parrots': - from torch.utils.data import DataLoader, PoolDataLoader - else: - from torch.utils.data import DataLoader - PoolDataLoader = DataLoader - return DataLoader, PoolDataLoader - - -def _get_extension(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import BuildExtension, Extension - CppExtension = partial(Extension, cuda=False) - CUDAExtension = partial(Extension, cuda=True) - else: - from torch.utils.cpp_extension import (BuildExtension, CppExtension, - CUDAExtension) - return BuildExtension, CppExtension, CUDAExtension - - -def _get_pool(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - else: - from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd - - -def _get_norm(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm2d - else: - from torch.nn.modules.instancenorm import _InstanceNorm - from torch.nn.modules.batchnorm import _BatchNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm - return _BatchNorm, _InstanceNorm, SyncBatchNorm_ - - -_ConvNd, _ConvTransposeMixin = _get_conv() -DataLoader, PoolDataLoader = _get_dataloader() -BuildExtension, CppExtension, CUDAExtension = _get_extension() -_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() -_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() - - -class SyncBatchNorm(SyncBatchNorm_): - - def _check_input_dim(self, input): - if TORCH_VERSION == 'parrots': - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input (got {input.dim()}D input)') - else: - super()._check_input_dim(input) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/path.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/path.py deleted file mode 100755 index 7dab4b304..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/path.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -from pathlib import Path - -from .misc import is_str - - -def is_filepath(x): - return is_str(x) or isinstance(x, Path) - - -def fopen(filepath, *args, **kwargs): - if is_str(filepath): - return open(filepath, *args, **kwargs) - elif isinstance(filepath, Path): - return filepath.open(*args, **kwargs) - raise ValueError('`filepath` should be a string or a Path') - - -def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): - if not osp.isfile(filename): - raise FileNotFoundError(msg_tmpl.format(filename)) - - -def mkdir_or_exist(dir_name, mode=0o777): - if dir_name == '': - return - dir_name = osp.expanduser(dir_name) - os.makedirs(dir_name, mode=mode, exist_ok=True) - - -def symlink(src, dst, overwrite=True, **kwargs): - if os.path.lexists(dst) and overwrite: - os.remove(dst) - os.symlink(src, dst, **kwargs) - - -def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): - """Scan a directory to find the interested files. - - Args: - dir_path (str | obj:`Path`): Path of the directory. - suffix (str | tuple(str), optional): File suffix that we are - interested in. Default: None. - recursive (bool, optional): If set to True, recursively scan the - directory. Default: False. - case_sensitive (bool, optional) : If set to False, ignore the case of - suffix. Default: True. - - Returns: - A generator for all the interested files with relative paths. - """ - if isinstance(dir_path, (str, Path)): - dir_path = str(dir_path) - else: - raise TypeError('"dir_path" must be a string or Path object') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('"suffix" must be a string or tuple of strings') - - if suffix is not None and not case_sensitive: - suffix = suffix.lower() if isinstance(suffix, str) else tuple( - item.lower() for item in suffix) - - root = dir_path - - def _scandir(dir_path, suffix, recursive, case_sensitive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - _rel_path = rel_path if case_sensitive else rel_path.lower() - if suffix is None or _rel_path.endswith(suffix): - yield rel_path - elif recursive and os.path.isdir(entry.path): - # scan recursively if entry.path is a directory - yield from _scandir(entry.path, suffix, recursive, - case_sensitive) - - return _scandir(dir_path, suffix, recursive, case_sensitive) - - -def find_vcs_root(path, markers=('.git', )): - """Finds the root directory (including itself) of specified markers. - - Args: - path (str): Path of directory or file. - markers (list[str], optional): List of file or directory names. - - Returns: - The directory contained one of the markers or None if not found. - """ - if osp.isfile(path): - path = osp.dirname(path) - - prev, cur = None, osp.abspath(osp.expanduser(path)) - while cur != prev: - if any(osp.exists(osp.join(cur, marker)) for marker in markers): - return cur - prev, cur = cur, osp.split(cur)[0] - return None diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/progressbar.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/progressbar.py deleted file mode 100755 index 0062f670d..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/progressbar.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from collections.abc import Iterable -from multiprocessing import Pool -from shutil import get_terminal_size - -from .timer import Timer - - -class ProgressBar: - """A progress bar which can print the progress.""" - - def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): - self.task_num = task_num - self.bar_width = bar_width - self.completed = 0 - self.file = file - if start: - self.start() - - @property - def terminal_width(self): - width, _ = get_terminal_size() - return width - - def start(self): - if self.task_num > 0: - self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' - 'elapsed: 0s, ETA:') - else: - self.file.write('completed: 0, elapsed: 0s') - self.file.flush() - self.timer = Timer() - - def update(self, num_tasks=1): - assert num_tasks > 0 - self.completed += num_tasks - elapsed = self.timer.since_start() - if elapsed > 0: - fps = self.completed / elapsed - else: - fps = float('inf') - if self.task_num > 0: - percentage = self.completed / float(self.task_num) - eta = int(elapsed * (1 - percentage) / percentage + 0.5) - msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ - f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ - f'ETA: {eta:5}s' - - bar_width = min(self.bar_width, - int(self.terminal_width - len(msg)) + 2, - int(self.terminal_width * 0.6)) - bar_width = max(2, bar_width) - mark_width = int(bar_width * percentage) - bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) - self.file.write(msg.format(bar_chars)) - else: - self.file.write( - f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' - f' {fps:.1f} tasks/s') - self.file.flush() - - -def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): - """Track the progress of tasks execution with a progress bar. - - Tasks are done with a simple for-loop. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - results = [] - for task in tasks: - results.append(func(task, **kwargs)) - prog_bar.update() - prog_bar.file.write('\n') - return results - - -def init_pool(process_num, initializer=None, initargs=None): - if initializer is None: - return Pool(process_num) - elif initargs is None: - return Pool(process_num, initializer) - else: - if not isinstance(initargs, tuple): - raise TypeError('"initargs" must be a tuple') - return Pool(process_num, initializer, initargs) - - -def track_parallel_progress(func, - tasks, - nproc, - initializer=None, - initargs=None, - bar_width=50, - chunksize=1, - skip_first=False, - keep_order=True, - file=sys.stdout): - """Track the progress of parallel task execution with a progress bar. - - The built-in :mod:`multiprocessing` module is used for process pools and - tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - nproc (int): Process (worker) number. - initializer (None or callable): Refer to :class:`multiprocessing.Pool` - for details. - initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for - details. - chunksize (int): Refer to :class:`multiprocessing.Pool` for details. - bar_width (int): Width of progress bar. - skip_first (bool): Whether to skip the first sample for each worker - when estimating fps, since the initialization step may takes - longer. - keep_order (bool): If True, :func:`Pool.imap` is used, otherwise - :func:`Pool.imap_unordered` is used. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - pool = init_pool(nproc, initializer, initargs) - start = not skip_first - task_num -= nproc * chunksize * int(skip_first) - prog_bar = ProgressBar(task_num, bar_width, start, file=file) - results = [] - if keep_order: - gen = pool.imap(func, tasks, chunksize) - else: - gen = pool.imap_unordered(func, tasks, chunksize) - for result in gen: - results.append(result) - if skip_first: - if len(results) < nproc * chunksize: - continue - elif len(results) == nproc * chunksize: - prog_bar.start() - continue - prog_bar.update() - prog_bar.file.write('\n') - pool.close() - pool.join() - return results - - -def track_iter_progress(tasks, bar_width=50, file=sys.stdout): - """Track the progress of tasks iteration or enumeration with a progress - bar. - - Tasks are yielded with a simple for-loop. - - Args: - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Yields: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - for task in tasks: - yield task - prog_bar.update() - prog_bar.file.write('\n') diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/registry.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/registry.py deleted file mode 100755 index fa9df39bc..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/registry.py +++ /dev/null @@ -1,315 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import warnings -from functools import partial - -from .misc import is_seq_of - - -def build_from_cfg(cfg, registry, default_args=None): - """Build a module from config dict. - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - try: - return obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{obj_cls.__name__}: {e}') - - -class Registry: - """A registry to map strings to classes. - - Registered object could be built from registry. - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = MODELS.build(dict(type='ResNet')) - - Please refer to - https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for - advanced usage. - - Args: - name (str): Registry name. - build_func(func, optional): Build function to construct instance from - Registry, func:`build_from_cfg` is used if neither ``parent`` or - ``build_func`` is specified. If ``parent`` is specified and - ``build_func`` is not given, ``build_func`` will be inherited - from ``parent``. Default: None. - parent (Registry, optional): Parent registry. The class registered in - children registry could be built from parent. Default: None. - scope (str, optional): The scope of registry. It is the key to search - for children registry. If not specified, scope will be the name of - the package where class is defined, e.g. mmdet, mmcls, mmseg. - Default: None. - """ - - def __init__(self, name, build_func=None, parent=None, scope=None): - self._name = name - self._module_dict = dict() - self._children = dict() - self._scope = self.infer_scope() if scope is None else scope - - # self.build_func will be set with the following priority: - # 1. build_func - # 2. parent.build_func - # 3. build_from_cfg - if build_func is None: - if parent is not None: - self.build_func = parent.build_func - else: - self.build_func = build_from_cfg - else: - self.build_func = build_func - if parent is not None: - assert isinstance(parent, Registry) - parent._add_children(self) - self.parent = parent - else: - self.parent = None - - def __len__(self): - return len(self._module_dict) - - def __contains__(self, key): - return self.get(key) is not None - - def __repr__(self): - format_str = self.__class__.__name__ + \ - f'(name={self._name}, ' \ - f'items={self._module_dict})' - return format_str - - @staticmethod - def infer_scope(): - """Infer the scope of registry. - - The name of the package where registry is defined will be returned. - - Example: - # in mmdet/models/backbone/resnet.py - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - The scope of ``ResNet`` will be ``mmdet``. - - - Returns: - scope (str): The inferred scope name. - """ - # inspect.stack() trace where this function is called, the index-2 - # indicates the frame where `infer_scope()` is called - filename = inspect.getmodule(inspect.stack()[2][0]).__name__ - split_filename = filename.split('.') - return split_filename[0] - - @staticmethod - def split_scope_key(key): - """Split scope and key. - - The first scope will be split from key. - - Examples: - >>> Registry.split_scope_key('mmdet.ResNet') - 'mmdet', 'ResNet' - >>> Registry.split_scope_key('ResNet') - None, 'ResNet' - - Return: - scope (str, None): The first scope. - key (str): The remaining key. - """ - split_index = key.find('.') - if split_index != -1: - return key[:split_index], key[split_index + 1:] - else: - return None, key - - @property - def name(self): - return self._name - - @property - def scope(self): - return self._scope - - @property - def module_dict(self): - return self._module_dict - - @property - def children(self): - return self._children - - def get(self, key): - """Get the registry record. - - Args: - key (str): The class name in string format. - - Returns: - class: The corresponding class. - """ - scope, real_key = self.split_scope_key(key) - if scope is None or scope == self._scope: - # get from self - if real_key in self._module_dict: - return self._module_dict[real_key] - else: - # get from self._children - if scope in self._children: - return self._children[scope].get(real_key) - else: - # goto root - parent = self.parent - while parent.parent is not None: - parent = parent.parent - return parent.get(key) - - def build(self, *args, **kwargs): - return self.build_func(*args, **kwargs, registry=self) - - def _add_children(self, registry): - """Add children for a registry. - - The ``registry`` will be added as children based on its scope. - The parent registry could build objects from children registry. - - Example: - >>> models = Registry('models') - >>> mmdet_models = Registry('models', parent=models) - >>> @mmdet_models.register_module() - >>> class ResNet: - >>> pass - >>> resnet = models.build(dict(type='mmdet.ResNet')) - """ - - assert isinstance(registry, Registry) - assert registry.scope is not None - assert registry.scope not in self.children, \ - f'scope {registry.scope} exists in {self.name} registry' - self.children[registry.scope] = registry - - def _register_module(self, module_class, module_name=None, force=False): - if not inspect.isclass(module_class): - raise TypeError('module must be a class, ' - f'but got {type(module_class)}') - - if module_name is None: - module_name = module_class.__name__ - if isinstance(module_name, str): - module_name = [module_name] - for name in module_name: - if not force and name in self._module_dict: - raise KeyError(f'{name} is already registered ' - f'in {self.name}') - self._module_dict[name] = module_class - - def deprecated_register_module(self, cls=None, force=False): - warnings.warn( - 'The old API of register_module(module, force=False) ' - 'is deprecated and will be removed, please use the new API ' - 'register_module(name=None, force=False, module=None) instead.') - if cls is None: - return partial(self.deprecated_register_module, force=force) - self._register_module(cls, force=force) - return cls - - def register_module(self, name=None, force=False, module=None): - """Register a module. - - A record will be added to `self._module_dict`, whose key is the class - name or the specified name, and value is the class itself. - It can be used as a decorator or a normal function. - - Example: - >>> backbones = Registry('backbone') - >>> @backbones.register_module() - >>> class ResNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> @backbones.register_module(name='mnet') - >>> class MobileNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> class ResNet: - >>> pass - >>> backbones.register_module(ResNet) - - Args: - name (str | None): The module name to be registered. If not - specified, the class name will be used. - force (bool, optional): Whether to override an existing class with - the same name. Default: False. - module (type): Module class to be registered. - """ - if not isinstance(force, bool): - raise TypeError(f'force must be a boolean, but got {type(force)}') - # NOTE: This is a walkaround to be compatible with the old api, - # while it may introduce unexpected bugs. - if isinstance(name, type): - return self.deprecated_register_module(name, force=force) - - # raise the error ahead of time - if not (name is None or isinstance(name, str) or is_seq_of(name, str)): - raise TypeError( - 'name must be either of None, an instance of str or a sequence' - f' of str, but got {type(name)}') - - # use it as a normal method: x.register_module(module=SomeClass) - if module is not None: - self._register_module( - module_class=module, module_name=name, force=force) - return module - - # use it as a decorator: @x.register_module() - def _register(cls): - self._register_module( - module_class=cls, module_name=name, force=force) - return cls - - return _register diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/testing.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/testing.py deleted file mode 100755 index a27f936da..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/testing.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) Open-MMLab. -import sys -from collections.abc import Iterable -from runpy import run_path -from shlex import split -from typing import Any, Dict, List -from unittest.mock import patch - - -def check_python_script(cmd): - """Run the python cmd script with `__main__`. The difference between - `os.system` is that, this function exectues code in the current process, so - that it can be tracked by coverage tools. Currently it supports two forms: - - - ./tests/data/scripts/hello.py zz - - python tests/data/scripts/hello.py zz - """ - args = split(cmd) - if args[0] == 'python': - args = args[1:] - with patch.object(sys, 'argv', args): - run_path(args[0], run_name='__main__') - - -def _any(judge_result): - """Since built-in ``any`` works only when the element of iterable is not - iterable, implement the function.""" - if not isinstance(judge_result, Iterable): - return judge_result - - try: - for element in judge_result: - if _any(element): - return True - except TypeError: - # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) - if judge_result: - return True - return False - - -def assert_dict_contains_subset(dict_obj: Dict[Any, Any], - expected_subset: Dict[Any, Any]) -> bool: - """Check if the dict_obj contains the expected_subset. - - Args: - dict_obj (Dict[Any, Any]): Dict object to be checked. - expected_subset (Dict[Any, Any]): Subset expected to be contained in - dict_obj. - - Returns: - bool: Whether the dict_obj contains the expected_subset. - """ - - for key, value in expected_subset.items(): - if key not in dict_obj.keys() or _any(dict_obj[key] != value): - return False - return True - - -def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: - """Check if attribute of class object is correct. - - Args: - obj (object): Class object to be checked. - expected_attrs (Dict[str, Any]): Dict of the expected attrs. - - Returns: - bool: Whether the attribute of class object is correct. - """ - for attr, value in expected_attrs.items(): - if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): - return False - return True - - -def assert_dict_has_keys(obj: Dict[str, Any], - expected_keys: List[str]) -> bool: - """Check if the obj has all the expected_keys. - - Args: - obj (Dict[str, Any]): Object to be checked. - expected_keys (List[str]): Keys expected to contained in the keys of - the obj. - - Returns: - bool: Whether the obj has the expected keys. - """ - return set(expected_keys).issubset(set(obj.keys())) - - -def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: - """Check if target_keys is equal to result_keys. - - Args: - result_keys (List[str]): Result keys to be checked. - target_keys (List[str]): Target keys to be checked. - - Returns: - bool: Whether target_keys is equal to result_keys. - """ - return set(result_keys) == set(target_keys) - - -def assert_is_norm_layer(module) -> bool: - """Check if the module is a norm layer. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the module is a norm layer. - """ - from .parrots_wrapper import _BatchNorm, _InstanceNorm - from torch.nn import GroupNorm, LayerNorm - norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) - return isinstance(module, norm_layer_candidates) - - -def assert_params_all_zeros(module) -> bool: - """Check if the parameters of the module is all zeros. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the parameters of the module is all zeros. - """ - weight_data = module.weight.data - is_weight_zero = weight_data.allclose( - weight_data.new_zeros(weight_data.size())) - - if hasattr(module, 'bias') and module.bias is not None: - bias_data = module.bias.data - is_bias_zero = bias_data.allclose( - bias_data.new_zeros(bias_data.size())) - else: - is_bias_zero = True - - return is_weight_zero and is_bias_zero diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/timer.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/timer.py deleted file mode 100755 index 66d4a78a8..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/timer.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from time import time - - -class TimerError(Exception): - - def __init__(self, message): - self.message = message - super(TimerError, self).__init__(message) - - -class Timer: - """A flexible Timer class. - - :Example: - - >>> import time - >>> import mmcv - >>> with mmcv.Timer(): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - 1.000 - >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - it takes 1.0 seconds - >>> timer = mmcv.Timer() - >>> time.sleep(0.5) - >>> print(timer.since_start()) - 0.500 - >>> time.sleep(0.5) - >>> print(timer.since_last_check()) - 0.500 - >>> print(timer.since_start()) - 1.000 - """ - - def __init__(self, start=True, print_tmpl=None): - self._is_running = False - self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' - if start: - self.start() - - @property - def is_running(self): - """bool: indicate whether the timer is running""" - return self._is_running - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - print(self.print_tmpl.format(self.since_last_check())) - self._is_running = False - - def start(self): - """Start the timer.""" - if not self._is_running: - self._t_start = time() - self._is_running = True - self._t_last = time() - - def since_start(self): - """Total time since the timer is started. - - Returns (float): Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - self._t_last = time() - return self._t_last - self._t_start - - def since_last_check(self): - """Time since the last checking. - - Either :func:`since_start` or :func:`since_last_check` is a checking - operation. - - Returns (float): Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - dur = time() - self._t_last - self._t_last = time() - return dur - - -_g_timers = {} # global timers - - -def check_time(timer_id): - """Add check points in a single line. - - This method is suitable for running a task on a list of items. A timer will - be registered when the method is called for the first time. - - :Example: - - >>> import time - >>> import mmcv - >>> for i in range(1, 6): - >>> # simulate a code block - >>> time.sleep(i) - >>> mmcv.check_time('task1') - 2.000 - 3.000 - 4.000 - 5.000 - - Args: - timer_id (str): Timer identifier. - """ - if timer_id not in _g_timers: - _g_timers[timer_id] = Timer() - return 0 - else: - return _g_timers[timer_id].since_last_check() diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/trace.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/trace.py deleted file mode 100755 index 8e49bfd38..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/trace.py +++ /dev/null @@ -1,23 +0,0 @@ -import warnings - -import torch - -from mmcv.utils import digit_version - - -def is_jit_tracing() -> bool: - if (torch.__version__ != 'parrots' - and digit_version(torch.__version__) >= digit_version('1.6.0')): - on_trace = torch.jit.is_tracing() - # In PyTorch 1.6, torch.jit.is_tracing has a bug. - # Refers to https://github.com/pytorch/pytorch/issues/42448 - if isinstance(on_trace, bool): - return on_trace - else: - return torch._C._is_tracing() - else: - warnings.warn( - 'torch.jit.is_tracing is only supported after v1.6.0. ' - 'Therefore is_tracing returns False automatically. Please ' - 'set on_trace manually if you are using trace.', UserWarning) - return False diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/version_utils.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/version_utils.py deleted file mode 100755 index 963c45a2e..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/utils/version_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import subprocess -import warnings - -from packaging.version import parse - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - assert 'parrots' not in version_str - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - -def get_git_hash(fallback='unknown', digits=None): - """Get the git hash of the current repo. - - Args: - fallback (str, optional): The fallback string when git hash is - unavailable. Defaults to 'unknown'. - digits (int, optional): kept digits of the hash. Defaults to None, - meaning all digits are kept. - - Returns: - str: Git commit hash. - """ - - if digits is not None and not isinstance(digits, int): - raise TypeError('digits must be None or an integer') - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - sha = out.strip().decode('ascii') - if digits is not None: - sha = sha[:digits] - except OSError: - sha = fallback - - return sha diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmcv/version.py b/cv/semantic_segmentation/gcnet/pytorch/mmcv/version.py deleted file mode 100755 index 1cce4e50b..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmcv/version.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -__version__ = '1.3.17' - - -def parse_version_info(version_str: str, length: int = 4) -> tuple: - """Parse a version string into a tuple. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int | str]: The version info, e.g., "1.3.0" is parsed into - (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into - (2, 0, 0, 0, 'rc', 1) (when length is set to 4). - """ - from packaging.version import parse - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - release.extend(list(version.pre)) - elif version.is_postrelease: - release.extend(list(version.post)) - else: - release.extend([0, 0]) - return tuple(release) - - -version_info = tuple(int(x) for x in __version__.split('.')[:3]) - -__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/__init__.py deleted file mode 100755 index 8da9bc6fe..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv -from packaging.version import parse - -from .version import __version__, version_info - -MMCV_MIN = '1.3.13' -MMCV_MAX = '1.5.0' - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -mmcv_min_version = digit_version(MMCV_MIN) -mmcv_max_version = digit_version(MMCV_MAX) -mmcv_version = digit_version(mmcv.__version__) - - -assert (mmcv_min_version <= mmcv_version <= mmcv_max_version), \ - f'MMCV=={mmcv.__version__} is used but incompatible. ' \ - f'Please install mmcv>={mmcv_min_version}, <={mmcv_max_version}.' - -__all__ = ['__version__', 'version_info', 'digit_version'] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/__init__.py deleted file mode 100755 index 4ab7b61d1..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# from .inference import inference_segmentor, init_segmentor, show_result_pyplot -from .test import multi_gpu_test, single_gpu_test -from .train import (get_root_logger, init_random_seed, set_random_seed, - train_segmentor) - -__all__ = [ - 'get_root_logger', 'set_random_seed', 'train_segmentor', 'init_segmentor', - 'inference_segmentor', 'multi_gpu_test', 'single_gpu_test', - 'show_result_pyplot', 'init_random_seed' -] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/inference.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/inference.py deleted file mode 100755 index 906943804..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/inference.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import matplotlib.pyplot as plt -import mmcv -import torch -from mmcv.parallel import collate, scatter -from mmcv.runner import load_checkpoint - -from mmseg.datasets.pipelines import Compose -from mmseg.models import build_segmentor - - -def init_segmentor(config, checkpoint=None, device='cuda:0'): - """Initialize a segmentor from config file. - - Args: - config (str or :obj:`mmcv.Config`): Config file path or the config - object. - checkpoint (str, optional): Checkpoint path. If left as None, the model - will not load any weights. - device (str, optional) CPU/CUDA device option. Default 'cuda:0'. - Use 'cpu' for loading model on CPU. - Returns: - nn.Module: The constructed segmentor. - """ - if isinstance(config, str): - config = mmcv.Config.fromfile(config) - elif not isinstance(config, mmcv.Config): - raise TypeError('config must be a filename or Config object, ' - 'but got {}'.format(type(config))) - config.model.pretrained = None - config.model.train_cfg = None - model = build_segmentor(config.model, test_cfg=config.get('test_cfg')) - if checkpoint is not None: - checkpoint = load_checkpoint(model, checkpoint, map_location='cpu') - model.CLASSES = checkpoint['meta']['CLASSES'] - model.PALETTE = checkpoint['meta']['PALETTE'] - model.cfg = config # save the config in the model for convenience - model.to(device) - model.eval() - return model - - -class LoadImage: - """A simple pipeline to load image.""" - - def __call__(self, results): - """Call function to load images into results. - - Args: - results (dict): A result dict contains the file name - of the image to be read. - - Returns: - dict: ``results`` will be returned containing loaded image. - """ - - if isinstance(results['img'], str): - results['filename'] = results['img'] - results['ori_filename'] = results['img'] - else: - results['filename'] = None - results['ori_filename'] = None - img = mmcv.imread(results['img']) - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - return results - - -def inference_segmentor(model, img): - """Inference image(s) with the segmentor. - - Args: - model (nn.Module): The loaded segmentor. - imgs (str/ndarray or list[str/ndarray]): Either image files or loaded - images. - - Returns: - (list[Tensor]): The segmentation result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # build the data pipeline - test_pipeline = [LoadImage()] + cfg.data.test.pipeline[1:] - test_pipeline = Compose(test_pipeline) - # prepare data - data = dict(img=img) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if next(model.parameters()).is_cuda: - # scatter to specified GPU - data = scatter(data, [device])[0] - else: - data['img_metas'] = [i.data[0] for i in data['img_metas']] - - # forward the model - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - return result - - -def show_result_pyplot(model, - img, - result, - palette=None, - fig_size=(15, 10), - opacity=0.5, - title='', - block=True): - """Visualize the segmentation results on the image. - - Args: - model (nn.Module): The loaded segmentor. - img (str or np.ndarray): Image filename or loaded image. - result (list): The segmentation result. - palette (list[list[int]]] | None): The palette of segmentation - map. If None is given, random palette will be generated. - Default: None - fig_size (tuple): Figure size of the pyplot figure. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - title (str): The title of pyplot figure. - Default is ''. - block (bool): Whether to block the pyplot figure. - Default is True. - """ - if hasattr(model, 'module'): - model = model.module - img = model.show_result( - img, result, palette=palette, show=False, opacity=opacity) - plt.figure(figsize=fig_size) - plt.imshow(mmcv.bgr2rgb(img)) - plt.title(title) - plt.tight_layout() - plt.show(block=block) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/test.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/test.py deleted file mode 100755 index cc4fcc979..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/test.py +++ /dev/null @@ -1,233 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import tempfile -import warnings - -import mmcv -import numpy as np -import torch -from mmcv.engine import collect_results_cpu, collect_results_gpu -from mmcv.image import tensor2imgs -from mmcv.runner import get_dist_info - - -def np2tmp(array, temp_file_name=None, tmpdir=None): - """Save ndarray to local numpy file. - - Args: - array (ndarray): Ndarray to save. - temp_file_name (str): Numpy file name. If 'temp_file_name=None', this - function will generate a file name with tempfile.NamedTemporaryFile - to save ndarray. Default: None. - tmpdir (str): Temporary directory to save Ndarray files. Default: None. - Returns: - str: The numpy file name. - """ - - if temp_file_name is None: - temp_file_name = tempfile.NamedTemporaryFile( - suffix='.npy', delete=False, dir=tmpdir).name - np.save(temp_file_name, array) - return temp_file_name - - -def single_gpu_test(model, - data_loader, - show=False, - out_dir=None, - efficient_test=False, - opacity=0.5, - pre_eval=False, - format_only=False, - format_args={}): - """Test with single GPU by progressive mode. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - show (bool): Whether show results during inference. Default: False. - out_dir (str, optional): If specified, the results will be dumped into - the directory to save output results. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - loader_indices = data_loader.batch_sampler - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - - if show or out_dir: - img_tensor = data['img'][0] - img_metas = data['img_metas'][0].data[0] - imgs = tensor2imgs(img_tensor, **img_metas[0]['img_norm_cfg']) - assert len(imgs) == len(img_metas) - - for img, img_meta in zip(imgs, img_metas): - h, w, _ = img_meta['img_shape'] - img_show = img[:h, :w, :] - - ori_h, ori_w = img_meta['ori_shape'][:-1] - img_show = mmcv.imresize(img_show, (ori_w, ori_h)) - - if out_dir: - out_file = osp.join(out_dir, img_meta['ori_filename']) - else: - out_file = None - - model.module.show_result( - img_show, - result, - palette=dataset.PALETTE, - show=show, - out_file=out_file, - opacity=opacity) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - results.extend(result) - else: - results.extend(result) - - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - - return results - - -def multi_gpu_test(model, - data_loader, - tmpdir=None, - gpu_collect=False, - efficient_test=False, - pre_eval=False, - format_only=False, - format_args={}): - """Test model with multiple gpus by progressive mode. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' - it encodes results to gpu tensors and use gpu communication for results - collection. On cpu mode it saves the results on different gpus to 'tmpdir' - and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. The same path is used for efficient - test. Default: None. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - - # batch_sampler based on DistributedSampler, the indices only point to data - # samples of related machine. - loader_indices = data_loader.batch_sampler - - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - - results.extend(result) - - if rank == 0: - batch_size = len(result) * world_size - for _ in range(batch_size): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/train.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/train.py deleted file mode 100755 index 7e1096bce..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/apis/train.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import random -import warnings - -import numpy as np -import torch -import torch.distributed as dist -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import HOOKS, build_optimizer, build_runner, get_dist_info -from mmcv.utils import build_from_cfg - -from mmseg.core import DistEvalHook, EvalHook -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.utils import get_root_logger - - -def init_random_seed(seed=None, device='cuda'): - """Initialize random seed. - - If the seed is not set, the seed will be automatically randomized, - and then broadcast to all processes to prevent some potential bugs. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is not None: - return seed - - # Make sure all ranks share the same random seed to prevent - # some potential bugs. Please refer to - # https://github.com/open-mmlab/mmdetection/issues/6339 - rank, world_size = get_dist_info() - seed = np.random.randint(2**31) - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() - - -def set_random_seed(seed, deterministic=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - """ - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - - -def train_segmentor(model, - dataset, - cfg, - distributed=False, - validate=False, - timestamp=None, - meta=None): - """Launch segmentor training.""" - logger = get_root_logger(cfg.log_level) - - # prepare data loaders - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - data_loaders = [ - build_dataloader( - ds, - cfg.data.samples_per_gpu, - cfg.data.workers_per_gpu, - # cfg.gpus will be ignored if distributed - len(cfg.gpu_ids), - dist=distributed, - seed=cfg.seed, - drop_last=True) for ds in dataset - ] - - # put model on gpus - if distributed: - find_unused_parameters = cfg.get('find_unused_parameters', False) - # Sets the `find_unused_parameters` parameter in - # torch.nn.parallel.DistributedDataParallel - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - else: - model = MMDataParallel( - model.cuda(cfg.gpu_ids[0]), device_ids=cfg.gpu_ids) - - # build runner - optimizer = build_optimizer(model, cfg.optimizer) - - if cfg.get('runner') is None: - cfg.runner = {'type': 'IterBasedRunner', 'max_iters': cfg.total_iters} - warnings.warn( - 'config is now expected to have a `runner` section, ' - 'please set `runner` in your config.', UserWarning) - - runner = build_runner( - cfg.runner, - default_args=dict( - model=model, - batch_processor=None, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta)) - - # register hooks - runner.register_training_hooks(cfg.lr_config, cfg.optimizer_config, - cfg.checkpoint_config, cfg.log_config, - cfg.get('momentum_config', None)) - - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register eval hooks - if validate: - val_dataset = build_dataset(cfg.data.val, dict(test_mode=True)) - val_dataloader = build_dataloader( - val_dataset, - samples_per_gpu=1, - workers_per_gpu=cfg.data.workers_per_gpu, - dist=distributed, - shuffle=False) - eval_cfg = cfg.get('evaluation', {}) - eval_cfg['by_epoch'] = cfg.runner['type'] != 'IterBasedRunner' - eval_hook = DistEvalHook if distributed else EvalHook - # In this PR (https://github.com/open-mmlab/mmcv/pull/1193), the - # priority of IterTimerHook has been modified from 'NORMAL' to 'LOW'. - runner.register_hook( - eval_hook(val_dataloader, **eval_cfg), priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/__init__.py deleted file mode 100755 index 402278618..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .evaluation import * # noqa: F401, F403 -from .seg import * # noqa: F401, F403 -from .utils import * # noqa: F401, F403 diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/__init__.py deleted file mode 100755 index 3d16d17e5..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .class_names import get_classes, get_palette -from .eval_hooks import DistEvalHook, EvalHook -from .metrics import (eval_metrics, intersect_and_union, mean_dice, - mean_fscore, mean_iou, pre_eval_to_metrics) - -__all__ = [ - 'EvalHook', 'DistEvalHook', 'mean_dice', 'mean_iou', 'mean_fscore', - 'eval_metrics', 'get_classes', 'get_palette', 'pre_eval_to_metrics', - 'intersect_and_union' -] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/class_names.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/class_names.py deleted file mode 100755 index 4527fbaf1..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/class_names.py +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - - -def cityscapes_classes(): - """Cityscapes class names for external use.""" - return [ - 'road', 'sidewalk', 'building', 'wall', 'fence', 'pole', - 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle' - ] - - -def ade_classes(): - """ADE20K class names for external use.""" - return [ - 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', - 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', - 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', - 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', - 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', - 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', - 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', - 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', - 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', - 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', - 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', - 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', - 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', - 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', - 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', - 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', - 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', - 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', - 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', - 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', - 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', - 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', - 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', - 'clock', 'flag' - ] - - -def voc_classes(): - """Pascal VOC class names for external use.""" - return [ - 'background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', - 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', - 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', - 'tvmonitor' - ] - - -def cityscapes_palette(): - """Cityscapes palette for external use.""" - return [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], - [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], - [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], - [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], [0, 80, 100], - [0, 0, 230], [119, 11, 32]] - - -def ade_palette(): - """ADE20K palette for external use.""" - return [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], - [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], - [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], - [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], - [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], - [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], - [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], - [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], - [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], - [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], - [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], - [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], - [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], - [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], - [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], - [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], - [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], - [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], - [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], - [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], - [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], - [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], - [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], - [102, 255, 0], [92, 0, 255]] - - -def voc_palette(): - """Pascal VOC palette for external use.""" - return [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], - [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], - [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], - [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], - [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] - - -dataset_aliases = { - 'cityscapes': ['cityscapes'], - 'ade': ['ade', 'ade20k'], - 'voc': ['voc', 'pascal_voc', 'voc12', 'voc12aug'] -} - - -def get_classes(dataset): - """Get class names of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_classes()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels - - -def get_palette(dataset): - """Get class palette (RGB) of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_palette()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/eval_hooks.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/eval_hooks.py deleted file mode 100755 index 952db3b0b..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/eval_hooks.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -import torch.distributed as dist -from mmcv.runner import DistEvalHook as _DistEvalHook -from mmcv.runner import EvalHook as _EvalHook -from torch.nn.modules.batchnorm import _BatchNorm - - -class EvalHook(_EvalHook): - """Single GPU EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``single_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - if not self._should_evaluate(runner): - return - - from mmseg.apis import single_gpu_test - results = single_gpu_test( - runner.model, self.dataloader, show=False, pre_eval=self.pre_eval) - runner.log_buffer.clear() - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - if self.save_best: - self._save_ckpt(runner, key_score) - - -class DistEvalHook(_DistEvalHook): - """Distributed EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``multi_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - if not self._should_evaluate(runner): - return - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - from mmseg.apis import multi_gpu_test - results = multi_gpu_test( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect, - pre_eval=self.pre_eval) - - runner.log_buffer.clear() - - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - - if self.save_best: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/metrics.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/metrics.py deleted file mode 100755 index a1c0908e1..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/evaluation/metrics.py +++ /dev/null @@ -1,395 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import mmcv -import numpy as np -import torch - - -def f_score(precision, recall, beta=1): - """calculate the f-score value. - - Args: - precision (float | torch.Tensor): The precision value. - recall (float | torch.Tensor): The recall value. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - Returns: - [torch.tensor]: The f-score value. - """ - score = (1 + beta**2) * (precision * recall) / ( - (beta**2 * precision) + recall) - return score - - -def intersect_and_union(pred_label, - label, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate intersection and Union. - - Args: - pred_label (ndarray | str): Prediction segmentation map - or predict result filename. - label (ndarray | str): Ground truth segmentation map - or label filename. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. The parameter will - work only when label is str. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. The parameter will - work only when label is str. Default: False. - - Returns: - torch.Tensor: The intersection of prediction and ground truth - histogram on all classes. - torch.Tensor: The union of prediction and ground truth histogram on - all classes. - torch.Tensor: The prediction histogram on all classes. - torch.Tensor: The ground truth histogram on all classes. - """ - - if isinstance(pred_label, str): - pred_label = torch.from_numpy(np.load(pred_label)) - else: - pred_label = torch.from_numpy((pred_label)) - - if isinstance(label, str): - label = torch.from_numpy( - mmcv.imread(label, flag='unchanged', backend='pillow')) - else: - label = torch.from_numpy(label) - - if label_map is not None: - for old_id, new_id in label_map.items(): - label[label == old_id] = new_id - if reduce_zero_label: - label[label == 0] = 255 - label = label - 1 - label[label == 254] = 255 - - mask = (label != ignore_index) - pred_label = pred_label[mask] - label = label[mask] - - intersect = pred_label[pred_label == label] - area_intersect = torch.histc( - intersect.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_pred_label = torch.histc( - pred_label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_label = torch.histc( - label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_union = area_pred_label + area_label - area_intersect - return area_intersect, area_union, area_pred_label, area_label - - -def total_intersect_and_union(results, - gt_seg_maps, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate Total Intersection and Union. - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - ndarray: The intersection of prediction and ground truth histogram - on all classes. - ndarray: The union of prediction and ground truth histogram on all - classes. - ndarray: The prediction histogram on all classes. - ndarray: The ground truth histogram on all classes. - """ - total_area_intersect = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_union = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_pred_label = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_label = torch.zeros((num_classes, ), dtype=torch.float64) - for result, gt_seg_map in zip(results, gt_seg_maps): - area_intersect, area_union, area_pred_label, area_label = \ - intersect_and_union( - result, gt_seg_map, num_classes, ignore_index, - label_map, reduce_zero_label) - total_area_intersect += area_intersect - total_area_union += area_union - total_area_pred_label += area_pred_label - total_area_label += area_label - return total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label - - -def mean_iou(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category IoU, shape (num_classes, ). - """ - iou_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mIoU'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return iou_result - - -def mean_dice(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Dice (mDice) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category dice, shape (num_classes, ). - """ - - dice_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mDice'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return dice_result - - -def mean_fscore(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category recall, shape (num_classes, ). - ndarray: Per category precision, shape (num_classes, ). - ndarray: Per category f-score, shape (num_classes, ). - """ - fscore_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mFscore'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label, - beta=beta) - return fscore_result - - -def eval_metrics(results, - gt_seg_maps, - num_classes, - ignore_index, - metrics=['mIoU'], - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate evaluation metrics - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label = total_intersect_and_union( - results, gt_seg_maps, num_classes, ignore_index, label_map, - reduce_zero_label) - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def pre_eval_to_metrics(pre_eval_results, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Convert pre-eval results to metrics. - - Args: - pre_eval_results (list[tuple[torch.Tensor]]): per image eval results - for computing evaluation metric - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - # convert list of tuples to tuple of lists, e.g. - # [(A_1, B_1, C_1, D_1), ..., (A_n, B_n, C_n, D_n)] to - # ([A_1, ..., A_n], ..., [D_1, ..., D_n]) - pre_eval_results = tuple(zip(*pre_eval_results)) - assert len(pre_eval_results) == 4 - - total_area_intersect = sum(pre_eval_results[0]) - total_area_union = sum(pre_eval_results[1]) - total_area_pred_label = sum(pre_eval_results[2]) - total_area_label = sum(pre_eval_results[3]) - - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def total_area_to_metrics(total_area_intersect, - total_area_union, - total_area_pred_label, - total_area_label, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Calculate evaluation metrics - Args: - total_area_intersect (ndarray): The intersection of prediction and - ground truth histogram on all classes. - total_area_union (ndarray): The union of prediction and ground truth - histogram on all classes. - total_area_pred_label (ndarray): The prediction histogram on all - classes. - total_area_label (ndarray): The ground truth histogram on all classes. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - if isinstance(metrics, str): - metrics = [metrics] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metrics).issubset(set(allowed_metrics)): - raise KeyError('metrics {} is not supported'.format(metrics)) - - all_acc = total_area_intersect.sum() / total_area_label.sum() - ret_metrics = OrderedDict({'aAcc': all_acc}) - for metric in metrics: - if metric == 'mIoU': - iou = total_area_intersect / total_area_union - acc = total_area_intersect / total_area_label - ret_metrics['IoU'] = iou - ret_metrics['Acc'] = acc - elif metric == 'mDice': - dice = 2 * total_area_intersect / ( - total_area_pred_label + total_area_label) - acc = total_area_intersect / total_area_label - ret_metrics['Dice'] = dice - ret_metrics['Acc'] = acc - elif metric == 'mFscore': - precision = total_area_intersect / total_area_pred_label - recall = total_area_intersect / total_area_label - f_value = torch.tensor( - [f_score(x[0], x[1], beta) for x in zip(precision, recall)]) - ret_metrics['Fscore'] = f_value - ret_metrics['Precision'] = precision - ret_metrics['Recall'] = recall - - ret_metrics = { - metric: value.numpy() - for metric, value in ret_metrics.items() - } - if nan_to_num is not None: - ret_metrics = OrderedDict({ - metric: np.nan_to_num(metric_value, nan=nan_to_num) - for metric, metric_value in ret_metrics.items() - }) - return ret_metrics diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/__init__.py deleted file mode 100755 index 5206b96be..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_pixel_sampler -from .sampler import BasePixelSampler, OHEMPixelSampler - -__all__ = ['build_pixel_sampler', 'BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/builder.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/builder.py deleted file mode 100755 index 1cecd347b..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/builder.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, build_from_cfg - -PIXEL_SAMPLERS = Registry('pixel sampler') - - -def build_pixel_sampler(cfg, **default_args): - """Build pixel sampler for segmentation map.""" - return build_from_cfg(cfg, PIXEL_SAMPLERS, default_args) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/sampler/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/sampler/__init__.py deleted file mode 100755 index 5a7648564..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/sampler/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_pixel_sampler import BasePixelSampler -from .ohem_pixel_sampler import OHEMPixelSampler - -__all__ = ['BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py deleted file mode 100755 index 03672cd47..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BasePixelSampler(metaclass=ABCMeta): - """Base class of pixel sampler.""" - - def __init__(self, **kwargs): - pass - - @abstractmethod - def sample(self, seg_logit, seg_label): - """Placeholder for sample function.""" diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py deleted file mode 100755 index 833a28768..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import PIXEL_SAMPLERS -from .base_pixel_sampler import BasePixelSampler - - -@PIXEL_SAMPLERS.register_module() -class OHEMPixelSampler(BasePixelSampler): - """Online Hard Example Mining Sampler for segmentation. - - Args: - context (nn.Module): The context of sampler, subclass of - :obj:`BaseDecodeHead`. - thresh (float, optional): The threshold for hard example selection. - Below which, are prediction with low confidence. If not - specified, the hard examples will be pixels of top ``min_kept`` - loss. Default: None. - min_kept (int, optional): The minimum number of predictions to keep. - Default: 100000. - """ - - def __init__(self, context, thresh=None, min_kept=100000): - super(OHEMPixelSampler, self).__init__() - self.context = context - assert min_kept > 1 - self.thresh = thresh - self.min_kept = min_kept - - def sample(self, seg_logit, seg_label): - """Sample pixels that have high loss or with low prediction confidence. - - Args: - seg_logit (torch.Tensor): segmentation logits, shape (N, C, H, W) - seg_label (torch.Tensor): segmentation label, shape (N, 1, H, W) - - Returns: - torch.Tensor: segmentation weight, shape (N, H, W) - """ - with torch.no_grad(): - assert seg_logit.shape[2:] == seg_label.shape[2:] - assert seg_label.shape[1] == 1 - seg_label = seg_label.squeeze(1).long() - batch_kept = self.min_kept * seg_label.size(0) - valid_mask = seg_label != self.context.ignore_index - seg_weight = seg_logit.new_zeros(size=seg_label.size()) - valid_seg_weight = seg_weight[valid_mask] - if self.thresh is not None: - seg_prob = F.softmax(seg_logit, dim=1) - - tmp_seg_label = seg_label.clone().unsqueeze(1) - tmp_seg_label[tmp_seg_label == self.context.ignore_index] = 0 - seg_prob = seg_prob.gather(1, tmp_seg_label).squeeze(1) - sort_prob, sort_indices = seg_prob[valid_mask].sort() - - if sort_prob.numel() > 0: - min_threshold = sort_prob[min(batch_kept, - sort_prob.numel() - 1)] - else: - min_threshold = 0.0 - threshold = max(min_threshold, self.thresh) - valid_seg_weight[seg_prob[valid_mask] < threshold] = 1. - else: - if not isinstance(self.context.loss_decode, nn.ModuleList): - losses_decode = [self.context.loss_decode] - else: - losses_decode = self.context.loss_decode - losses = 0.0 - for loss_module in losses_decode: - losses += loss_module( - seg_logit, - seg_label, - weight=None, - ignore_index=self.context.ignore_index, - reduction_override='none') - - # faster than topk according to https://github.com/pytorch/pytorch/issues/22812 # noqa - _, sort_indices = losses[valid_mask].sort(descending=True) - valid_seg_weight[sort_indices[:batch_kept]] = 1. - - seg_weight[valid_mask] = valid_seg_weight - - return seg_weight diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/utils/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/utils/__init__.py deleted file mode 100755 index be9de558d..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/utils/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .misc import add_prefix - -__all__ = ['add_prefix'] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/utils/misc.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/utils/misc.py deleted file mode 100755 index 282bb8d96..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/core/utils/misc.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def add_prefix(inputs, prefix): - """Add prefix for dict. - - Args: - inputs (dict): The input dict with str keys. - prefix (str): The prefix to add. - - Returns: - - dict: The dict with keys updated with ``prefix``. - """ - - outputs = dict() - for name, value in inputs.items(): - outputs[f'{prefix}.{name}'] = value - - return outputs diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/__init__.py deleted file mode 100755 index cc1a30aa0..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# from .ade import ADE20KDataset -from .builder import DATASETS, PIPELINES, build_dataloader, build_dataset -#from .chase_db1 import ChaseDB1Dataset -from .cityscapes import CityscapesDataset -# from .coco_stuff import COCOStuffDataset -from .custom import CustomDataset -# from .dark_zurich import DarkZurichDataset -from .dataset_wrappers import ConcatDataset, RepeatDataset -# from .drive import DRIVEDataset -# from .hrf import HRFDataset -# from .loveda import LoveDADataset -# from .night_driving import NightDrivingDataset -# from .pascal_context import PascalContextDataset, PascalContextDataset59 -# # from .stare import STAREDataset -# from .voc import PascalVOCDataset - diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/builder.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/builder.py deleted file mode 100755 index 7ab645958..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/builder.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import platform -import random -from functools import partial - -import numpy as np -import torch -from mmcv.parallel import collate -from mmcv.runner import get_dist_info -from mmcv.utils import Registry, build_from_cfg, digit_version -from torch.utils.data import DataLoader, DistributedSampler - -if platform.system() != 'Windows': - # https://github.com/pytorch/pytorch/issues/973 - import resource - rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) - base_soft_limit = rlimit[0] - hard_limit = rlimit[1] - soft_limit = min(max(4096, base_soft_limit), hard_limit) - resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) - -DATASETS = Registry('dataset') -PIPELINES = Registry('pipeline') - - -def _concat_dataset(cfg, default_args=None): - """Build :obj:`ConcatDataset by.""" - from .dataset_wrappers import ConcatDataset - img_dir = cfg['img_dir'] - ann_dir = cfg.get('ann_dir', None) - split = cfg.get('split', None) - # pop 'separate_eval' since it is not a valid key for common datasets. - separate_eval = cfg.pop('separate_eval', True) - num_img_dir = len(img_dir) if isinstance(img_dir, (list, tuple)) else 1 - if ann_dir is not None: - num_ann_dir = len(ann_dir) if isinstance(ann_dir, (list, tuple)) else 1 - else: - num_ann_dir = 0 - if split is not None: - num_split = len(split) if isinstance(split, (list, tuple)) else 1 - else: - num_split = 0 - if num_img_dir > 1: - assert num_img_dir == num_ann_dir or num_ann_dir == 0 - assert num_img_dir == num_split or num_split == 0 - else: - assert num_split == num_ann_dir or num_ann_dir <= 1 - num_dset = max(num_split, num_img_dir) - - datasets = [] - for i in range(num_dset): - data_cfg = copy.deepcopy(cfg) - if isinstance(img_dir, (list, tuple)): - data_cfg['img_dir'] = img_dir[i] - if isinstance(ann_dir, (list, tuple)): - data_cfg['ann_dir'] = ann_dir[i] - if isinstance(split, (list, tuple)): - data_cfg['split'] = split[i] - datasets.append(build_dataset(data_cfg, default_args)) - - return ConcatDataset(datasets, separate_eval) - - -def build_dataset(cfg, default_args=None): - """Build datasets.""" - from .dataset_wrappers import ConcatDataset, RepeatDataset - if isinstance(cfg, (list, tuple)): - dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) - elif cfg['type'] == 'RepeatDataset': - dataset = RepeatDataset( - build_dataset(cfg['dataset'], default_args), cfg['times']) - elif isinstance(cfg.get('img_dir'), (list, tuple)) or isinstance( - cfg.get('split', None), (list, tuple)): - dataset = _concat_dataset(cfg, default_args) - else: - dataset = build_from_cfg(cfg, DATASETS, default_args) - - return dataset - - -def build_dataloader(dataset, - samples_per_gpu, - workers_per_gpu, - num_gpus=1, - dist=True, - shuffle=True, - seed=None, - drop_last=False, - pin_memory=True, - persistent_workers=True, - **kwargs): - """Build PyTorch DataLoader. - - In distributed training, each GPU/process has a dataloader. - In non-distributed training, there is only one dataloader for all GPUs. - - Args: - dataset (Dataset): A PyTorch dataset. - samples_per_gpu (int): Number of training samples on each GPU, i.e., - batch size of each GPU. - workers_per_gpu (int): How many subprocesses to use for data loading - for each GPU. - num_gpus (int): Number of GPUs. Only used in non-distributed training. - dist (bool): Distributed training/test or not. Default: True. - shuffle (bool): Whether to shuffle the data at every epoch. - Default: True. - seed (int | None): Seed to be used. Default: None. - drop_last (bool): Whether to drop the last incomplete batch in epoch. - Default: False - pin_memory (bool): Whether to use pin_memory in DataLoader. - Default: True - persistent_workers (bool): If True, the data loader will not shutdown - the worker processes after a dataset has been consumed once. - This allows to maintain the workers Dataset instances alive. - The argument also has effect in PyTorch>=1.7.0. - Default: True - kwargs: any keyword argument to be used to initialize DataLoader - - Returns: - DataLoader: A PyTorch dataloader. - """ - rank, world_size = get_dist_info() - if dist: - sampler = DistributedSampler( - dataset, world_size, rank, shuffle=shuffle) - shuffle = False - batch_size = samples_per_gpu - num_workers = workers_per_gpu - else: - sampler = None - batch_size = num_gpus * samples_per_gpu - num_workers = num_gpus * workers_per_gpu - - init_fn = partial( - worker_init_fn, num_workers=num_workers, rank=rank, - seed=seed) if seed is not None else None - - if digit_version(torch.__version__) >= digit_version('1.8.0'): - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - persistent_workers=persistent_workers, - **kwargs) - else: - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - **kwargs) - - return data_loader - - -def worker_init_fn(worker_id, num_workers, rank, seed): - """Worker init func for dataloader. - - The seed of each worker equals to num_worker * rank + worker_id + user_seed - - Args: - worker_id (int): Worker id. - num_workers (int): Number of workers. - rank (int): The rank of current process. - seed (int): The random seed to use. - """ - - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/cityscapes.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/cityscapes.py deleted file mode 100755 index ed633d00d..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/cityscapes.py +++ /dev/null @@ -1,214 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np -from mmcv.utils import print_log -from PIL import Image - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class CityscapesDataset(CustomDataset): - """Cityscapes dataset. - - The ``img_suffix`` is fixed to '_leftImg8bit.png' and ``seg_map_suffix`` is - fixed to '_gtFine_labelTrainIds.png' for Cityscapes dataset. - """ - - CLASSES = ('road', 'sidewalk', 'building', 'wall', 'fence', 'pole', - 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle') - - PALETTE = [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], - [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], - [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], - [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], - [0, 80, 100], [0, 0, 230], [119, 11, 32]] - - def __init__(self, - img_suffix='_leftImg8bit.png', - seg_map_suffix='_gtFine_labelTrainIds.png', - **kwargs): - super(CityscapesDataset, self).__init__( - img_suffix=img_suffix, seg_map_suffix=seg_map_suffix, **kwargs) - - @staticmethod - def _convert_to_label_id(result): - """Convert trainId to id for cityscapes.""" - if isinstance(result, str): - result = np.load(result) - import cityscapesscripts.helpers.labels as CSLabels - result_copy = result.copy() - for trainId, label in CSLabels.trainId2label.items(): - result_copy[result == trainId] = label.id - - return result_copy - - def results2img(self, results, imgfile_prefix, to_label_id, indices=None): - """Write the segmentation results to images. - - Args: - results (list[ndarray]): Testing results of the - dataset. - imgfile_prefix (str): The filename prefix of the png files. - If the prefix is "somepath/xxx", - the png files will be named "somepath/xxx.png". - to_label_id (bool): whether convert output to label_id for - submission. - indices (list[int], optional): Indices of input results, - if not set, all the indices of the dataset will be used. - Default: None. - - Returns: - list[str: str]: result txt files which contains corresponding - semantic segmentation images. - """ - if indices is None: - indices = list(range(len(self))) - - mmcv.mkdir_or_exist(imgfile_prefix) - result_files = [] - for result, idx in zip(results, indices): - if to_label_id: - result = self._convert_to_label_id(result) - filename = self.img_infos[idx]['filename'] - basename = osp.splitext(osp.basename(filename))[0] - - png_filename = osp.join(imgfile_prefix, f'{basename}.png') - - output = Image.fromarray(result.astype(np.uint8)).convert('P') - import cityscapesscripts.helpers.labels as CSLabels - palette = np.zeros((len(CSLabels.id2label), 3), dtype=np.uint8) - for label_id, label in CSLabels.id2label.items(): - palette[label_id] = label.color - - output.putpalette(palette) - output.save(png_filename) - result_files.append(png_filename) - - return result_files - - def format_results(self, - results, - imgfile_prefix, - to_label_id=True, - indices=None): - """Format the results into dir (standard format for Cityscapes - evaluation). - - Args: - results (list): Testing results of the dataset. - imgfile_prefix (str): The prefix of images files. It - includes the file path and the prefix of filename, e.g., - "a/b/prefix". - to_label_id (bool): whether convert output to label_id for - submission. Default: False - indices (list[int], optional): Indices of input results, - if not set, all the indices of the dataset will be used. - Default: None. - - Returns: - tuple: (result_files, tmp_dir), result_files is a list containing - the image paths, tmp_dir is the temporal directory created - for saving json/png files when img_prefix is not specified. - """ - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - result_files = self.results2img(results, imgfile_prefix, to_label_id, - indices) - - return result_files - - def evaluate(self, - results, - metric='mIoU', - logger=None, - imgfile_prefix=None): - """Evaluation in Cityscapes/default protocol. - - Args: - results (list): Testing results of the dataset. - metric (str | list[str]): Metrics to be evaluated. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - imgfile_prefix (str | None): The prefix of output image file, - for cityscapes evaluation only. It includes the file path and - the prefix of filename, e.g., "a/b/prefix". - If results are evaluated with cityscapes protocol, it would be - the prefix of output png files. The output files would be - png images under folder "a/b/prefix/xxx.png", where "xxx" is - the image name of cityscapes. If not specified, a temp file - will be created for evaluation. - Default: None. - - Returns: - dict[str, float]: Cityscapes/default metrics. - """ - - eval_results = dict() - metrics = metric.copy() if isinstance(metric, list) else [metric] - if 'cityscapes' in metrics: - eval_results.update( - self._evaluate_cityscapes(results, logger, imgfile_prefix)) - metrics.remove('cityscapes') - if len(metrics) > 0: - eval_results.update( - super(CityscapesDataset, - self).evaluate(results, metrics, logger)) - - return eval_results - - def _evaluate_cityscapes(self, results, logger, imgfile_prefix): - """Evaluation in Cityscapes protocol. - - Args: - results (list): Testing results of the dataset. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - imgfile_prefix (str | None): The prefix of output image file - - Returns: - dict[str: float]: Cityscapes evaluation results. - """ - try: - import cityscapesscripts.evaluation.evalPixelLevelSemanticLabeling as CSEval # noqa - except ImportError: - raise ImportError('Please run "pip install cityscapesscripts" to ' - 'install cityscapesscripts first.') - msg = 'Evaluating in Cityscapes style' - if logger is None: - msg = '\n' + msg - print_log(msg, logger=logger) - - result_dir = imgfile_prefix - - eval_results = dict() - print_log(f'Evaluating results under {result_dir} ...', logger=logger) - - CSEval.args.evalInstLevelScore = True - CSEval.args.predictionPath = osp.abspath(result_dir) - CSEval.args.evalPixelAccuracy = True - CSEval.args.JSONOutput = False - - seg_map_list = [] - pred_list = [] - - # when evaluating with official cityscapesscripts, - # **_gtFine_labelIds.png is used - for seg_map in mmcv.scandir( - self.ann_dir, 'gtFine_labelIds.png', recursive=True): - seg_map_list.append(osp.join(self.ann_dir, seg_map)) - pred_list.append(CSEval.getPrediction(CSEval.args, seg_map)) - - eval_results.update( - CSEval.evaluateImgLists(pred_list, seg_map_list, CSEval.args)) - - return eval_results diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/custom.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/custom.py deleted file mode 100755 index 64964ed8b..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/custom.py +++ /dev/null @@ -1,457 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from collections import OrderedDict - -import mmcv -import numpy as np -from mmcv.utils import print_log -from prettytable import PrettyTable -from torch.utils.data import Dataset - -from mmseg.core import eval_metrics, intersect_and_union, pre_eval_to_metrics -from mmseg.utils import get_root_logger -from .builder import DATASETS -from .pipelines import Compose, LoadAnnotations - - -@DATASETS.register_module() -class CustomDataset(Dataset): - """Custom dataset for semantic segmentation. An example of file structure - is as followed. - - .. code-block:: none - - ├── data - │ ├── my_dataset - │ │ ├── img_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{img_suffix} - │ │ │ │ ├── yyy{img_suffix} - │ │ │ │ ├── zzz{img_suffix} - │ │ │ ├── val - │ │ ├── ann_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{seg_map_suffix} - │ │ │ │ ├── yyy{seg_map_suffix} - │ │ │ │ ├── zzz{seg_map_suffix} - │ │ │ ├── val - - The img/gt_semantic_seg pair of CustomDataset should be of the same - except suffix. A valid img/gt_semantic_seg filename pair should be like - ``xxx{img_suffix}`` and ``xxx{seg_map_suffix}`` (extension is also included - in the suffix). If split is given, then ``xxx`` is specified in txt file. - Otherwise, all files in ``img_dir/``and ``ann_dir`` will be loaded. - Please refer to ``docs/tutorials/new_dataset.md`` for more details. - - - Args: - pipeline (list[dict]): Processing pipeline - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. Default: '.jpg' - ann_dir (str, optional): Path to annotation directory. Default: None - seg_map_suffix (str): Suffix of segmentation maps. Default: '.png' - split (str, optional): Split txt file. If split is specified, only - file with suffix in the splits will be loaded. Otherwise, all - images in img_dir/ann_dir will be loaded. Default: None - data_root (str, optional): Data root for img_dir/ann_dir. Default: - None. - test_mode (bool): If test_mode=True, gt wouldn't be loaded. - ignore_index (int): The label index to be ignored. Default: 255 - reduce_zero_label (bool): Whether to mark label zero as ignored. - Default: False - classes (str | Sequence[str], optional): Specify classes to load. - If is None, ``cls.CLASSES`` will be used. Default: None. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, and - self.PALETTE is None, random palette will be generated. - Default: None - gt_seg_map_loader_cfg (dict, optional): build LoadAnnotations to - load gt for evaluation, load from disk by default. Default: None. - """ - - CLASSES = None - - PALETTE = None - - def __init__(self, - pipeline, - img_dir, - img_suffix='.jpg', - ann_dir=None, - seg_map_suffix='.png', - split=None, - data_root=None, - test_mode=False, - ignore_index=255, - reduce_zero_label=False, - classes=None, - palette=None, - gt_seg_map_loader_cfg=None): - self.pipeline = Compose(pipeline) - self.img_dir = img_dir - self.img_suffix = img_suffix - self.ann_dir = ann_dir - self.seg_map_suffix = seg_map_suffix - self.split = split - self.data_root = data_root - self.test_mode = test_mode - self.ignore_index = ignore_index - self.reduce_zero_label = reduce_zero_label - self.label_map = None - self.CLASSES, self.PALETTE = self.get_classes_and_palette( - classes, palette) - self.gt_seg_map_loader = LoadAnnotations( - ) if gt_seg_map_loader_cfg is None else LoadAnnotations( - **gt_seg_map_loader_cfg) - - if test_mode: - assert self.CLASSES is not None, \ - '`cls.CLASSES` or `classes` should be specified when testing' - - # join paths if data_root is specified - if self.data_root is not None: - if not osp.isabs(self.img_dir): - self.img_dir = osp.join(self.data_root, self.img_dir) - if not (self.ann_dir is None or osp.isabs(self.ann_dir)): - self.ann_dir = osp.join(self.data_root, self.ann_dir) - if not (self.split is None or osp.isabs(self.split)): - self.split = osp.join(self.data_root, self.split) - - # load annotations - self.img_infos = self.load_annotations(self.img_dir, self.img_suffix, - self.ann_dir, - self.seg_map_suffix, self.split) - - def __len__(self): - """Total number of samples of data.""" - return len(self.img_infos) - - def load_annotations(self, img_dir, img_suffix, ann_dir, seg_map_suffix, - split): - """Load annotation from directory. - - Args: - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. - ann_dir (str|None): Path to annotation directory. - seg_map_suffix (str|None): Suffix of segmentation maps. - split (str|None): Split txt file. If split is specified, only file - with suffix in the splits will be loaded. Otherwise, all images - in img_dir/ann_dir will be loaded. Default: None - - Returns: - list[dict]: All image info of dataset. - """ - - img_infos = [] - if split is not None: - with open(split) as f: - for line in f: - img_name = line.strip() - img_info = dict(filename=img_name + img_suffix) - if ann_dir is not None: - seg_map = img_name + seg_map_suffix - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - else: - for img in mmcv.scandir(img_dir, img_suffix, recursive=True): - img_info = dict(filename=img) - if ann_dir is not None: - seg_map = img.replace(img_suffix, seg_map_suffix) - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - img_infos = sorted(img_infos, key=lambda x: x['filename']) - - print_log(f'Loaded {len(img_infos)} images', logger=get_root_logger()) - return img_infos - - def get_ann_info(self, idx): - """Get annotation by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - return self.img_infos[idx]['ann'] - - def pre_pipeline(self, results): - """Prepare results dict for pipeline.""" - results['seg_fields'] = [] - results['img_prefix'] = self.img_dir - results['seg_prefix'] = self.ann_dir - if self.custom_classes: - results['label_map'] = self.label_map - - def __getitem__(self, idx): - """Get training/test data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training/test data (with annotation if `test_mode` is set - False). - """ - - if self.test_mode: - return self.prepare_test_img(idx) - else: - return self.prepare_train_img(idx) - - def prepare_train_img(self, idx): - """Get training data and annotations after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training data and annotation after pipeline with new keys - introduced by pipeline. - """ - - img_info = self.img_infos[idx] - ann_info = self.get_ann_info(idx) - results = dict(img_info=img_info, ann_info=ann_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def prepare_test_img(self, idx): - """Get testing data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Testing data after pipeline with new keys introduced by - pipeline. - """ - - img_info = self.img_infos[idx] - results = dict(img_info=img_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """Place holder to format result to dataset specific output.""" - raise NotImplementedError - - def get_gt_seg_map_by_idx(self, index): - """Get one ground truth segmentation map for evaluation.""" - ann_info = self.get_ann_info(index) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - return results['gt_semantic_seg'] - - def get_gt_seg_maps(self, efficient_test=None): - """Get ground truth segmentation maps for evaluation.""" - if efficient_test is not None: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` has been deprecated ' - 'since MMSeg v0.16, the ``get_gt_seg_maps()`` is CPU memory ' - 'friendly by default. ') - - for idx in range(len(self)): - ann_info = self.get_ann_info(idx) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - yield results['gt_semantic_seg'] - - def pre_eval(self, preds, indices): - """Collect eval result from each iteration. - - Args: - preds (list[torch.Tensor] | torch.Tensor): the segmentation logit - after argmax, shape (N, H, W). - indices (list[int] | int): the prediction related ground truth - indices. - - Returns: - list[torch.Tensor]: (area_intersect, area_union, area_prediction, - area_ground_truth). - """ - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - - pre_eval_results = [] - - for pred, index in zip(preds, indices): - seg_map = self.get_gt_seg_map_by_idx(index) - pre_eval_results.append( - intersect_and_union(pred, seg_map, len(self.CLASSES), - self.ignore_index, self.label_map, - self.reduce_zero_label)) - - return pre_eval_results - - def get_classes_and_palette(self, classes=None, palette=None): - """Get class names of current dataset. - - Args: - classes (Sequence[str] | str | None): If classes is None, use - default CLASSES defined by builtin dataset. If classes is a - string, take it as a file name. The file contains the name of - classes where each line contains one class name. If classes is - a tuple or list, override the CLASSES defined by the dataset. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, random - palette will be generated. Default: None - """ - if classes is None: - self.custom_classes = False - return self.CLASSES, self.PALETTE - - self.custom_classes = True - if isinstance(classes, str): - # take it as a file path - class_names = mmcv.list_from_file(classes) - elif isinstance(classes, (tuple, list)): - class_names = classes - else: - raise ValueError(f'Unsupported type {type(classes)} of classes.') - - if self.CLASSES: - if not set(class_names).issubset(self.CLASSES): - raise ValueError('classes is not a subset of CLASSES.') - - # dictionary, its keys are the old label ids and its values - # are the new label ids. - # used for changing pixel labels in load_annotations. - self.label_map = {} - for i, c in enumerate(self.CLASSES): - if c not in class_names: - self.label_map[i] = -1 - else: - self.label_map[i] = class_names.index(c) - - palette = self.get_palette_for_custom_classes(class_names, palette) - - return class_names, palette - - def get_palette_for_custom_classes(self, class_names, palette=None): - - if self.label_map is not None: - # return subset of palette - palette = [] - for old_id, new_id in sorted( - self.label_map.items(), key=lambda x: x[1]): - if new_id != -1: - palette.append(self.PALETTE[old_id]) - palette = type(self.PALETTE)(palette) - - elif palette is None: - if self.PALETTE is None: - palette = np.random.randint(0, 255, size=(len(class_names), 3)) - else: - palette = self.PALETTE - - return palette - - def evaluate(self, - results, - metric='mIoU', - logger=None, - gt_seg_maps=None, - **kwargs): - """Evaluate the dataset. - - Args: - results (list[tuple[torch.Tensor]] | list[str]): per image pre_eval - results or predict segmentation map for computing evaluation - metric. - metric (str | list[str]): Metrics to be evaluated. 'mIoU', - 'mDice' and 'mFscore' are supported. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - gt_seg_maps (generator[ndarray]): Custom gt seg maps as input, - used in ConcatDataset - - Returns: - dict[str, float]: Default metrics. - """ - if isinstance(metric, str): - metric = [metric] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metric).issubset(set(allowed_metrics)): - raise KeyError('metric {} is not supported'.format(metric)) - - eval_results = {} - # test a list of files - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - if gt_seg_maps is None: - gt_seg_maps = self.get_gt_seg_maps() - num_classes = len(self.CLASSES) - ret_metrics = eval_metrics( - results, - gt_seg_maps, - num_classes, - self.ignore_index, - metric, - label_map=self.label_map, - reduce_zero_label=self.reduce_zero_label) - # test a list of pre_eval_results - else: - ret_metrics = pre_eval_to_metrics(results, metric) - - # Because dataset.CLASSES is required for per-eval. - if self.CLASSES is None: - class_names = tuple(range(num_classes)) - else: - class_names = self.CLASSES - - # summary table - ret_metrics_summary = OrderedDict({ - ret_metric: np.round(np.nanmean(ret_metric_value) * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - - # each class table - ret_metrics.pop('aAcc', None) - ret_metrics_class = OrderedDict({ - ret_metric: np.round(ret_metric_value * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - ret_metrics_class.update({'Class': class_names}) - ret_metrics_class.move_to_end('Class', last=False) - - # for logger - class_table_data = PrettyTable() - for key, val in ret_metrics_class.items(): - class_table_data.add_column(key, val) - - summary_table_data = PrettyTable() - for key, val in ret_metrics_summary.items(): - if key == 'aAcc': - summary_table_data.add_column(key, [val]) - else: - summary_table_data.add_column('m' + key, [val]) - - print_log('per class results:', logger) - print_log('\n' + class_table_data.get_string(), logger=logger) - print_log('Summary:', logger) - print_log('\n' + summary_table_data.get_string(), logger=logger) - - # each metric dict - for key, value in ret_metrics_summary.items(): - if key == 'aAcc': - eval_results[key] = value / 100.0 - else: - eval_results['m' + key] = value / 100.0 - - ret_metrics_class.pop('Class', None) - for key, value in ret_metrics_class.items(): - eval_results.update({ - key + '.' + str(name): value[idx] / 100.0 - for idx, name in enumerate(class_names) - }) - - return eval_results \ No newline at end of file diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/dataset_wrappers.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/dataset_wrappers.py deleted file mode 100755 index 0349332ee..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/dataset_wrappers.py +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import bisect -from itertools import chain - -import mmcv -import numpy as np -from mmcv.utils import print_log -from torch.utils.data.dataset import ConcatDataset as _ConcatDataset - -from .builder import DATASETS -from .cityscapes import CityscapesDataset - - -@DATASETS.register_module() -class ConcatDataset(_ConcatDataset): - """A wrapper of concatenated dataset. - - Same as :obj:`torch.utils.data.dataset.ConcatDataset`, but - support evaluation and formatting results - - Args: - datasets (list[:obj:`Dataset`]): A list of datasets. - separate_eval (bool): Whether to evaluate the concatenated - dataset results separately, Defaults to True. - """ - - def __init__(self, datasets, separate_eval=True): - super(ConcatDataset, self).__init__(datasets) - self.CLASSES = datasets[0].CLASSES - self.PALETTE = datasets[0].PALETTE - self.separate_eval = separate_eval - assert separate_eval in [True, False], \ - f'separate_eval can only be True or False,' \ - f'but get {separate_eval}' - if any([isinstance(ds, CityscapesDataset) for ds in datasets]): - raise NotImplementedError( - 'Evaluating ConcatDataset containing CityscapesDataset' - 'is not supported!') - - def evaluate(self, results, logger=None, **kwargs): - """Evaluate the results. - - Args: - results (list[tuple[torch.Tensor]] | list[str]]): per image - pre_eval results or predict segmentation map for - computing evaluation metric. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - - Returns: - dict[str: float]: evaluate results of the total dataset - or each separate - dataset if `self.separate_eval=True`. - """ - assert len(results) == self.cumulative_sizes[-1], \ - ('Dataset and results have different sizes: ' - f'{self.cumulative_sizes[-1]} v.s. {len(results)}') - - # Check whether all the datasets support evaluation - for dataset in self.datasets: - assert hasattr(dataset, 'evaluate'), \ - f'{type(dataset)} does not implement evaluate function' - - if self.separate_eval: - dataset_idx = -1 - total_eval_results = dict() - for size, dataset in zip(self.cumulative_sizes, self.datasets): - start_idx = 0 if dataset_idx == -1 else \ - self.cumulative_sizes[dataset_idx] - end_idx = self.cumulative_sizes[dataset_idx + 1] - - results_per_dataset = results[start_idx:end_idx] - print_log( - f'\nEvaluateing {dataset.img_dir} with ' - f'{len(results_per_dataset)} images now', - logger=logger) - - eval_results_per_dataset = dataset.evaluate( - results_per_dataset, logger=logger, **kwargs) - dataset_idx += 1 - for k, v in eval_results_per_dataset.items(): - total_eval_results.update({f'{dataset_idx}_{k}': v}) - - return total_eval_results - - if len(set([type(ds) for ds in self.datasets])) != 1: - raise NotImplementedError( - 'All the datasets should have same types when ' - 'self.separate_eval=False') - else: - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - # merge the generators of gt_seg_maps - gt_seg_maps = chain( - *[dataset.get_gt_seg_maps() for dataset in self.datasets]) - else: - # if the results are `pre_eval` results, - # we do not need gt_seg_maps to evaluate - gt_seg_maps = None - eval_results = self.datasets[0].evaluate( - results, gt_seg_maps=gt_seg_maps, logger=logger, **kwargs) - return eval_results - - def get_dataset_idx_and_sample_idx(self, indice): - """Return dataset and sample index when given an indice of - ConcatDataset. - - Args: - indice (int): indice of sample in ConcatDataset - - Returns: - int: the index of sub dataset the sample belong to - int: the index of sample in its corresponding subset - """ - if indice < 0: - if -indice > len(self): - raise ValueError( - 'absolute value of index should not exceed dataset length') - indice = len(self) + indice - dataset_idx = bisect.bisect_right(self.cumulative_sizes, indice) - if dataset_idx == 0: - sample_idx = indice - else: - sample_idx = indice - self.cumulative_sizes[dataset_idx - 1] - return dataset_idx, sample_idx - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """format result for every sample of ConcatDataset.""" - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].format_results( - [results[i]], - imgfile_prefix + f'/{dataset_idx}', - indices=[sample_idx], - **kwargs) - ret_res.append(res) - return sum(ret_res, []) - - def pre_eval(self, preds, indices): - """do pre eval for every sample of ConcatDataset.""" - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].pre_eval(preds[i], sample_idx) - ret_res.append(res) - return sum(ret_res, []) - - -@DATASETS.register_module() -class RepeatDataset(object): - """A wrapper of repeated dataset. - - The length of repeated dataset will be `times` larger than the original - dataset. This is useful when the data loading time is long but the dataset - is small. Using RepeatDataset can reduce the data loading time between - epochs. - - Args: - dataset (:obj:`Dataset`): The dataset to be repeated. - times (int): Repeat times. - """ - - def __init__(self, dataset, times): - self.dataset = dataset - self.times = times - self.CLASSES = dataset.CLASSES - self.PALETTE = dataset.PALETTE - self._ori_len = len(self.dataset) - - def __getitem__(self, idx): - """Get item from original dataset.""" - return self.dataset[idx % self._ori_len] - - def __len__(self): - """The length is multiplied by ``times``""" - return self.times * self._ori_len diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/__init__.py deleted file mode 100755 index 91d9e4749..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .compose import Compose -from .formatting import (Collect, ImageToTensor, ToDataContainer, ToTensor, - Transpose, to_tensor) -from .loading import LoadAnnotations, LoadImageFromFile -from .test_time_aug import MultiScaleFlipAug -from .transforms import (CLAHE, AdjustGamma, Normalize, Pad, - PhotoMetricDistortion, RandomCrop, RandomCutOut, - RandomFlip, RandomRotate, Rerange, Resize, RGB2Gray, - SegRescale) - -__all__ = [ - 'Compose', 'to_tensor', 'ToTensor', 'ImageToTensor', 'ToDataContainer', - 'Transpose', 'Collect', 'LoadAnnotations', 'LoadImageFromFile', - 'MultiScaleFlipAug', 'Resize', 'RandomFlip', 'Pad', 'RandomCrop', - 'Normalize', 'SegRescale', 'PhotoMetricDistortion', 'RandomRotate', - 'AdjustGamma', 'CLAHE', 'Rerange', 'RGB2Gray', 'RandomCutOut' -] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/compose.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/compose.py deleted file mode 100755 index 30280c133..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/compose.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections - -from mmcv.utils import build_from_cfg - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class Compose(object): - """Compose multiple transforms sequentially. - - Args: - transforms (Sequence[dict | callable]): Sequence of transform object or - config dict to be composed. - """ - - def __init__(self, transforms): - assert isinstance(transforms, collections.abc.Sequence) - self.transforms = [] - for transform in transforms: - if isinstance(transform, dict): - transform = build_from_cfg(transform, PIPELINES) - self.transforms.append(transform) - elif callable(transform): - self.transforms.append(transform) - else: - raise TypeError('transform must be callable or a dict') - - def __call__(self, data): - """Call function to apply transforms sequentially. - - Args: - data (dict): A result dict contains the data to transform. - - Returns: - dict: Transformed data. - """ - - for t in self.transforms: - data = t(data) - if data is None: - return None - return data - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - format_string += '\n' - format_string += f' {t}' - format_string += '\n)' - return format_string diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/formating.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/formating.py deleted file mode 100755 index f6e53bfeb..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/formating.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -import warnings - -from .formatting import * - -warnings.warn('DeprecationWarning: mmseg.datasets.pipelines.formating will be ' - 'deprecated in 2021, please replace it with ' - 'mmseg.datasets.pipelines.formatting.') diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/formatting.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/formatting.py deleted file mode 100755 index 4e057c1b8..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/formatting.py +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import mmcv -import numpy as np -import torch -from mmcv.parallel import DataContainer as DC - -from ..builder import PIPELINES - - -def to_tensor(data): - """Convert objects of various python types to :obj:`torch.Tensor`. - - Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, - :class:`Sequence`, :class:`int` and :class:`float`. - - Args: - data (torch.Tensor | numpy.ndarray | Sequence | int | float): Data to - be converted. - """ - - if isinstance(data, torch.Tensor): - return data - elif isinstance(data, np.ndarray): - return torch.from_numpy(data) - elif isinstance(data, Sequence) and not mmcv.is_str(data): - return torch.tensor(data) - elif isinstance(data, int): - return torch.LongTensor([data]) - elif isinstance(data, float): - return torch.FloatTensor([data]) - else: - raise TypeError(f'type {type(data)} cannot be converted to tensor.') - - -@PIPELINES.register_module() -class ToTensor(object): - """Convert some results to :obj:`torch.Tensor` by given keys. - - Args: - keys (Sequence[str]): Keys that need to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert data in results to :obj:`torch.Tensor`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted - to :obj:`torch.Tensor`. - """ - - for key in self.keys: - results[key] = to_tensor(results[key]) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class ImageToTensor(object): - """Convert image to :obj:`torch.Tensor` by given keys. - - The dimension order of input image is (H, W, C). The pipeline will convert - it to (C, H, W). If only 2 dimension (H, W) is given, the output would be - (1, H, W). - - Args: - keys (Sequence[str]): Key of images to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - img = results[key] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - results[key] = to_tensor(img.transpose(2, 0, 1)) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class Transpose(object): - """Transpose some results by given keys. - - Args: - keys (Sequence[str]): Keys of results to be transposed. - order (Sequence[int]): Order of transpose. - """ - - def __init__(self, keys, order): - self.keys = keys - self.order = order - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - results[key] = results[key].transpose(self.order) - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, order={self.order})' - - -@PIPELINES.register_module() -class ToDataContainer(object): - """Convert results to :obj:`mmcv.DataContainer` by given fields. - - Args: - fields (Sequence[dict]): Each field is a dict like - ``dict(key='xxx', **kwargs)``. The ``key`` in result will - be converted to :obj:`mmcv.DataContainer` with ``**kwargs``. - Default: ``(dict(key='img', stack=True), - dict(key='gt_semantic_seg'))``. - """ - - def __init__(self, - fields=(dict(key='img', - stack=True), dict(key='gt_semantic_seg'))): - self.fields = fields - - def __call__(self, results): - """Call function to convert data in results to - :obj:`mmcv.DataContainer`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted to - :obj:`mmcv.DataContainer`. - """ - - for field in self.fields: - field = field.copy() - key = field.pop('key') - results[key] = DC(results[key], **field) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(fields={self.fields})' - - -@PIPELINES.register_module() -class DefaultFormatBundle(object): - """Default formatting bundle. - - It simplifies the pipeline of formatting common fields, including "img" - and "gt_semantic_seg". These fields are formatted as follows. - - - img: (1)transpose, (2)to tensor, (3)to DataContainer (stack=True) - - gt_semantic_seg: (1)unsqueeze dim-0 (2)to tensor, - (3)to DataContainer (stack=True) - """ - - def __call__(self, results): - """Call function to transform and format common fields in results. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data that is formatted with - default bundle. - """ - - if 'img' in results: - img = results['img'] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - img = np.ascontiguousarray(img.transpose(2, 0, 1)) - results['img'] = DC(to_tensor(img), stack=True) - if 'gt_semantic_seg' in results: - # convert to long - results['gt_semantic_seg'] = DC( - to_tensor(results['gt_semantic_seg'][None, - ...].astype(np.int64)), - stack=True) - return results - - def __repr__(self): - return self.__class__.__name__ - - -@PIPELINES.register_module() -class Collect(object): - """Collect data from the loader relevant to the specific task. - - This is usually the last stage of the data loader pipeline. Typically keys - is set to some subset of "img", "gt_semantic_seg". - - The "img_meta" item is always populated. The contents of the "img_meta" - dictionary depends on "meta_keys". By default this includes: - - - "img_shape": shape of the image input to the network as a tuple - (h, w, c). Note that images may be zero padded on the bottom/right - if the batch tensor is larger than this shape. - - - "scale_factor": a float indicating the preprocessing scale - - - "flip": a boolean indicating if image flip transform was used - - - "filename": path to the image file - - - "ori_shape": original shape of the image as a tuple (h, w, c) - - - "pad_shape": image shape after padding - - - "img_norm_cfg": a dict of normalization information: - - mean - per channel mean subtraction - - std - per channel std divisor - - to_rgb - bool indicating if bgr was converted to rgb - - Args: - keys (Sequence[str]): Keys of results to be collected in ``data``. - meta_keys (Sequence[str], optional): Meta keys to be converted to - ``mmcv.DataContainer`` and collected in ``data[img_metas]``. - Default: (``filename``, ``ori_filename``, ``ori_shape``, - ``img_shape``, ``pad_shape``, ``scale_factor``, ``flip``, - ``flip_direction``, ``img_norm_cfg``) - """ - - def __init__(self, - keys, - meta_keys=('filename', 'ori_filename', 'ori_shape', - 'img_shape', 'pad_shape', 'scale_factor', 'flip', - 'flip_direction', 'img_norm_cfg')): - self.keys = keys - self.meta_keys = meta_keys - - def __call__(self, results): - """Call function to collect keys in results. The keys in ``meta_keys`` - will be converted to :obj:mmcv.DataContainer. - - Args: - results (dict): Result dict contains the data to collect. - - Returns: - dict: The result dict contains the following keys - - keys in``self.keys`` - - ``img_metas`` - """ - - data = {} - img_meta = {} - for key in self.meta_keys: - img_meta[key] = results[key] - data['img_metas'] = DC(img_meta, cpu_only=True) - for key in self.keys: - data[key] = results[key] - return data - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, meta_keys={self.meta_keys})' diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/loading.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/loading.py deleted file mode 100755 index e1c82bd39..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/loading.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class LoadImageFromFile(object): - """Load an image from file. - - Required keys are "img_prefix" and "img_info" (a dict that must contain the - key "filename"). Added or updated keys are "filename", "img", "img_shape", - "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`), - "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1). - - Args: - to_float32 (bool): Whether to convert the loaded image to a float32 - numpy array. If set to False, the loaded image is an uint8 array. - Defaults to False. - color_type (str): The flag argument for :func:`mmcv.imfrombytes`. - Defaults to 'color'. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'cv2' - """ - - def __init__(self, - to_float32=False, - color_type='color', - file_client_args=dict(backend='disk'), - imdecode_backend='cv2'): - self.to_float32 = to_float32 - self.color_type = color_type - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call functions to load image and get image meta information. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded image and meta information. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('img_prefix') is not None: - filename = osp.join(results['img_prefix'], - results['img_info']['filename']) - else: - filename = results['img_info']['filename'] - img_bytes = self.file_client.get(filename) - img = mmcv.imfrombytes( - img_bytes, flag=self.color_type, backend=self.imdecode_backend) - if self.to_float32: - img = img.astype(np.float32) - - results['filename'] = filename - results['ori_filename'] = results['img_info']['filename'] - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - # Set initial values for default meta_keys - results['pad_shape'] = img.shape - results['scale_factor'] = 1.0 - num_channels = 1 if len(img.shape) < 3 else img.shape[2] - results['img_norm_cfg'] = dict( - mean=np.zeros(num_channels, dtype=np.float32), - std=np.ones(num_channels, dtype=np.float32), - to_rgb=False) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(to_float32={self.to_float32},' - repr_str += f"color_type='{self.color_type}'," - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str - - -@PIPELINES.register_module() -class LoadAnnotations(object): - """Load annotations for semantic segmentation. - - Args: - reduce_zero_label (bool): Whether reduce all label value by 1. - Usually used for datasets where 0 is background label. - Default: False. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'pillow' - """ - - def __init__(self, - reduce_zero_label=False, - file_client_args=dict(backend='disk'), - imdecode_backend='pillow'): - self.reduce_zero_label = reduce_zero_label - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call function to load multiple types annotations. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded semantic segmentation annotations. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('seg_prefix', None) is not None: - filename = osp.join(results['seg_prefix'], - results['ann_info']['seg_map']) - else: - filename = results['ann_info']['seg_map'] - img_bytes = self.file_client.get(filename) - gt_semantic_seg = mmcv.imfrombytes( - img_bytes, flag='unchanged', - backend=self.imdecode_backend).squeeze().astype(np.uint8) - # modify if custom classes - if results.get('label_map', None) is not None: - for old_id, new_id in results['label_map'].items(): - gt_semantic_seg[gt_semantic_seg == old_id] = new_id - # reduce zero_label - if self.reduce_zero_label: - # avoid using underflow conversion - gt_semantic_seg[gt_semantic_seg == 0] = 255 - gt_semantic_seg = gt_semantic_seg - 1 - gt_semantic_seg[gt_semantic_seg == 254] = 255 - results['gt_semantic_seg'] = gt_semantic_seg - results['seg_fields'].append('gt_semantic_seg') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(reduce_zero_label={self.reduce_zero_label},' - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py deleted file mode 100755 index 5c17cbbba..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv - -from ..builder import PIPELINES -from .compose import Compose - - -@PIPELINES.register_module() -class MultiScaleFlipAug(object): - """Test-time augmentation with multiple scales and flipping. - - An example configuration is as followed: - - .. code-block:: - - img_scale=(2048, 1024), - img_ratios=[0.5, 1.0], - flip=True, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ] - - After MultiScaleFLipAug with above configuration, the results are wrapped - into lists of the same length as followed: - - .. code-block:: - - dict( - img=[...], - img_shape=[...], - scale=[(1024, 512), (1024, 512), (2048, 1024), (2048, 1024)] - flip=[False, True, False, True] - ... - ) - - Args: - transforms (list[dict]): Transforms to apply in each augmentation. - img_scale (None | tuple | list[tuple]): Images scales for resizing. - img_ratios (float | list[float]): Image ratios for resizing - flip (bool): Whether apply flip augmentation. Default: False. - flip_direction (str | list[str]): Flip augmentation directions, - options are "horizontal" and "vertical". If flip_direction is list, - multiple flip augmentations will be applied. - It has no effect when flip == False. Default: "horizontal". - """ - - def __init__(self, - transforms, - img_scale, - img_ratios=None, - flip=False, - flip_direction='horizontal'): - self.transforms = Compose(transforms) - if img_ratios is not None: - img_ratios = img_ratios if isinstance(img_ratios, - list) else [img_ratios] - assert mmcv.is_list_of(img_ratios, float) - if img_scale is None: - # mode 1: given img_scale=None and a range of image ratio - self.img_scale = None - assert mmcv.is_list_of(img_ratios, float) - elif isinstance(img_scale, tuple) and mmcv.is_list_of( - img_ratios, float): - assert len(img_scale) == 2 - # mode 2: given a scale and a range of image ratio - self.img_scale = [(int(img_scale[0] * ratio), - int(img_scale[1] * ratio)) - for ratio in img_ratios] - else: - # mode 3: given multiple scales - self.img_scale = img_scale if isinstance(img_scale, - list) else [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) or self.img_scale is None - self.flip = flip - self.img_ratios = img_ratios - self.flip_direction = flip_direction if isinstance( - flip_direction, list) else [flip_direction] - assert mmcv.is_list_of(self.flip_direction, str) - if not self.flip and self.flip_direction != ['horizontal']: - warnings.warn( - 'flip_direction has no effect when flip is set to False') - if (self.flip - and not any([t['type'] == 'RandomFlip' for t in transforms])): - warnings.warn( - 'flip has no effect when RandomFlip is not in transforms') - - def __call__(self, results): - """Call function to apply test time augment transforms on results. - - Args: - results (dict): Result dict contains the data to transform. - - Returns: - dict[str: list]: The augmented data, where each value is wrapped - into a list. - """ - - aug_data = [] - if self.img_scale is None and mmcv.is_list_of(self.img_ratios, float): - h, w = results['img'].shape[:2] - img_scale = [(int(w * ratio), int(h * ratio)) - for ratio in self.img_ratios] - else: - img_scale = self.img_scale - flip_aug = [False, True] if self.flip else [False] - for scale in img_scale: - for flip in flip_aug: - for direction in self.flip_direction: - _results = results.copy() - _results['scale'] = scale - _results['flip'] = flip - _results['flip_direction'] = direction - data = self.transforms(_results) - aug_data.append(data) - # list of dict to dict of list - aug_data_dict = {key: [] for key in aug_data[0]} - for data in aug_data: - for key, val in data.items(): - aug_data_dict[key].append(val) - return aug_data_dict - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(transforms={self.transforms}, ' - repr_str += f'img_scale={self.img_scale}, flip={self.flip})' - repr_str += f'flip_direction={self.flip_direction}' - return repr_str diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/transforms.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/transforms.py deleted file mode 100755 index 567c960a1..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/datasets/pipelines/transforms.py +++ /dev/null @@ -1,1042 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np -from mmcv.utils import deprecated_api_warning, is_tuple_of -from numpy import random - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class ResizeToMultiple(object): - """Resize images & seg to multiple of divisor. - - Args: - size_divisor (int): images and gt seg maps need to resize to multiple - of size_divisor. Default: 32. - interpolation (str, optional): The interpolation mode of image resize. - Default: None - """ - - def __init__(self, size_divisor=32, interpolation=None): - self.size_divisor = size_divisor - self.interpolation = interpolation - - def __call__(self, results): - """Call function to resize images, semantic segmentation map to - multiple of size divisor. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape' keys are updated. - """ - # Align image to multiple of size divisor. - img = results['img'] - img = mmcv.imresize_to_multiple( - img, - self.size_divisor, - scale_factor=1, - interpolation=self.interpolation - if self.interpolation else 'bilinear') - - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape - - # Align segmentation map to multiple of size divisor. - for key in results.get('seg_fields', []): - gt_seg = results[key] - gt_seg = mmcv.imresize_to_multiple( - gt_seg, - self.size_divisor, - scale_factor=1, - interpolation='nearest') - results[key] = gt_seg - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(size_divisor={self.size_divisor}, ' - f'interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class Resize(object): - """Resize images & seg. - - This transform resizes the input image to some scale. If the input dict - contains the key "scale", then the scale in the input dict is used, - otherwise the specified scale in the init method is used. - - ``img_scale`` can be None, a tuple (single-scale) or a list of tuple - (multi-scale). There are 4 multiscale modes: - - - ``ratio_range is not None``: - 1. When img_scale is None, img_scale is the shape of image in results - (img_scale = results['img'].shape[:2]) and the image is resized based - on the original size. (mode 1) - 2. When img_scale is a tuple (single-scale), randomly sample a ratio from - the ratio range and multiply it with the image scale. (mode 2) - - - ``ratio_range is None and multiscale_mode == "range"``: randomly sample a - scale from the a range. (mode 3) - - - ``ratio_range is None and multiscale_mode == "value"``: randomly sample a - scale from multiple scales. (mode 4) - - Args: - img_scale (tuple or list[tuple]): Images scales for resizing. - Default:None. - multiscale_mode (str): Either "range" or "value". - Default: 'range' - ratio_range (tuple[float]): (min_ratio, max_ratio). - Default: None - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: True - """ - - def __init__(self, - img_scale=None, - multiscale_mode='range', - ratio_range=None, - keep_ratio=True): - if img_scale is None: - self.img_scale = None - else: - if isinstance(img_scale, list): - self.img_scale = img_scale - else: - self.img_scale = [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) - - if ratio_range is not None: - # mode 1: given img_scale=None and a range of image ratio - # mode 2: given a scale and a range of image ratio - assert self.img_scale is None or len(self.img_scale) == 1 - else: - # mode 3 and 4: given multiple scales or a range of scales - assert multiscale_mode in ['value', 'range'] - - self.multiscale_mode = multiscale_mode - self.ratio_range = ratio_range - self.keep_ratio = keep_ratio - - @staticmethod - def random_select(img_scales): - """Randomly select an img_scale from given candidates. - - Args: - img_scales (list[tuple]): Images scales for selection. - - Returns: - (tuple, int): Returns a tuple ``(img_scale, scale_dix)``, - where ``img_scale`` is the selected image scale and - ``scale_idx`` is the selected index in the given candidates. - """ - - assert mmcv.is_list_of(img_scales, tuple) - scale_idx = np.random.randint(len(img_scales)) - img_scale = img_scales[scale_idx] - return img_scale, scale_idx - - @staticmethod - def random_sample(img_scales): - """Randomly sample an img_scale when ``multiscale_mode=='range'``. - - Args: - img_scales (list[tuple]): Images scale range for sampling. - There must be two tuples in img_scales, which specify the lower - and upper bound of image scales. - - Returns: - (tuple, None): Returns a tuple ``(img_scale, None)``, where - ``img_scale`` is sampled scale and None is just a placeholder - to be consistent with :func:`random_select`. - """ - - assert mmcv.is_list_of(img_scales, tuple) and len(img_scales) == 2 - img_scale_long = [max(s) for s in img_scales] - img_scale_short = [min(s) for s in img_scales] - long_edge = np.random.randint( - min(img_scale_long), - max(img_scale_long) + 1) - short_edge = np.random.randint( - min(img_scale_short), - max(img_scale_short) + 1) - img_scale = (long_edge, short_edge) - return img_scale, None - - @staticmethod - def random_sample_ratio(img_scale, ratio_range): - """Randomly sample an img_scale when ``ratio_range`` is specified. - - A ratio will be randomly sampled from the range specified by - ``ratio_range``. Then it would be multiplied with ``img_scale`` to - generate sampled scale. - - Args: - img_scale (tuple): Images scale base to multiply with ratio. - ratio_range (tuple[float]): The minimum and maximum ratio to scale - the ``img_scale``. - - Returns: - (tuple, None): Returns a tuple ``(scale, None)``, where - ``scale`` is sampled ratio multiplied with ``img_scale`` and - None is just a placeholder to be consistent with - :func:`random_select`. - """ - - assert isinstance(img_scale, tuple) and len(img_scale) == 2 - min_ratio, max_ratio = ratio_range - assert min_ratio <= max_ratio - ratio = np.random.random_sample() * (max_ratio - min_ratio) + min_ratio - scale = int(img_scale[0] * ratio), int(img_scale[1] * ratio) - return scale, None - - def _random_scale(self, results): - """Randomly sample an img_scale according to ``ratio_range`` and - ``multiscale_mode``. - - If ``ratio_range`` is specified, a ratio will be sampled and be - multiplied with ``img_scale``. - If multiple scales are specified by ``img_scale``, a scale will be - sampled according to ``multiscale_mode``. - Otherwise, single scale will be used. - - Args: - results (dict): Result dict from :obj:`dataset`. - - Returns: - dict: Two new keys 'scale` and 'scale_idx` are added into - ``results``, which would be used by subsequent pipelines. - """ - - if self.ratio_range is not None: - if self.img_scale is None: - h, w = results['img'].shape[:2] - scale, scale_idx = self.random_sample_ratio((w, h), - self.ratio_range) - else: - scale, scale_idx = self.random_sample_ratio( - self.img_scale[0], self.ratio_range) - elif len(self.img_scale) == 1: - scale, scale_idx = self.img_scale[0], 0 - elif self.multiscale_mode == 'range': - scale, scale_idx = self.random_sample(self.img_scale) - elif self.multiscale_mode == 'value': - scale, scale_idx = self.random_select(self.img_scale) - else: - raise NotImplementedError - - results['scale'] = scale - results['scale_idx'] = scale_idx - - def _resize_img(self, results): - """Resize images with ``results['scale']``.""" - if self.keep_ratio: - img, scale_factor = mmcv.imrescale( - results['img'], results['scale'], return_scale=True) - # the w_scale and h_scale has minor difference - # a real fix should be done in the mmcv.imrescale in the future - new_h, new_w = img.shape[:2] - h, w = results['img'].shape[:2] - w_scale = new_w / w - h_scale = new_h / h - else: - img, w_scale, h_scale = mmcv.imresize( - results['img'], results['scale'], return_scale=True) - scale_factor = np.array([w_scale, h_scale, w_scale, h_scale], - dtype=np.float32) - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape # in case that there is no padding - results['scale_factor'] = scale_factor - results['keep_ratio'] = self.keep_ratio - - def _resize_seg(self, results): - """Resize semantic segmentation map with ``results['scale']``.""" - for key in results.get('seg_fields', []): - if self.keep_ratio: - gt_seg = mmcv.imrescale( - results[key], results['scale'], interpolation='nearest') - else: - gt_seg = mmcv.imresize( - results[key], results['scale'], interpolation='nearest') - results[key] = gt_seg - - def __call__(self, results): - """Call function to resize images, bounding boxes, masks, semantic - segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape', 'scale_factor', - 'keep_ratio' keys are added into result dict. - """ - - if 'scale' not in results: - self._random_scale(results) - self._resize_img(results) - self._resize_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(img_scale={self.img_scale}, ' - f'multiscale_mode={self.multiscale_mode}, ' - f'ratio_range={self.ratio_range}, ' - f'keep_ratio={self.keep_ratio})') - return repr_str - - -@PIPELINES.register_module() -class RandomFlip(object): - """Flip the image & seg. - - If the input dict contains the key "flip", then the flag will be used, - otherwise it will be randomly decided by a ratio specified in the init - method. - - Args: - prob (float, optional): The flipping probability. Default: None. - direction(str, optional): The flipping direction. Options are - 'horizontal' and 'vertical'. Default: 'horizontal'. - """ - - @deprecated_api_warning({'flip_ratio': 'prob'}, cls_name='RandomFlip') - def __init__(self, prob=None, direction='horizontal'): - self.prob = prob - self.direction = direction - if prob is not None: - assert prob >= 0 and prob <= 1 - assert direction in ['horizontal', 'vertical'] - - def __call__(self, results): - """Call function to flip bounding boxes, masks, semantic segmentation - maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Flipped results, 'flip', 'flip_direction' keys are added into - result dict. - """ - - if 'flip' not in results: - flip = True if np.random.rand() < self.prob else False - results['flip'] = flip - if 'flip_direction' not in results: - results['flip_direction'] = self.direction - if results['flip']: - # flip image - results['img'] = mmcv.imflip( - results['img'], direction=results['flip_direction']) - - # flip segs - for key in results.get('seg_fields', []): - # use copy() to make numpy stride positive - results[key] = mmcv.imflip( - results[key], direction=results['flip_direction']).copy() - return results - - def __repr__(self): - return self.__class__.__name__ + f'(prob={self.prob})' - - -@PIPELINES.register_module() -class Pad(object): - """Pad the image & mask. - - There are two padding modes: (1) pad to a fixed size and (2) pad to the - minimum size that is divisible by some number. - Added keys are "pad_shape", "pad_fixed_size", "pad_size_divisor", - - Args: - size (tuple, optional): Fixed padding size. - size_divisor (int, optional): The divisor of padded size. - pad_val (float, optional): Padding value. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - """ - - def __init__(self, - size=None, - size_divisor=None, - pad_val=0, - seg_pad_val=255): - self.size = size - self.size_divisor = size_divisor - self.pad_val = pad_val - self.seg_pad_val = seg_pad_val - # only one of size and size_divisor should be valid - assert size is not None or size_divisor is not None - assert size is None or size_divisor is None - - def _pad_img(self, results): - """Pad images according to ``self.size``.""" - if self.size is not None: - padded_img = mmcv.impad( - results['img'], shape=self.size, pad_val=self.pad_val) - elif self.size_divisor is not None: - padded_img = mmcv.impad_to_multiple( - results['img'], self.size_divisor, pad_val=self.pad_val) - results['img'] = padded_img - results['pad_shape'] = padded_img.shape - results['pad_fixed_size'] = self.size - results['pad_size_divisor'] = self.size_divisor - - def _pad_seg(self, results): - """Pad masks according to ``results['pad_shape']``.""" - for key in results.get('seg_fields', []): - results[key] = mmcv.impad( - results[key], - shape=results['pad_shape'][:2], - pad_val=self.seg_pad_val) - - def __call__(self, results): - """Call function to pad images, masks, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Updated result dict. - """ - - self._pad_img(results) - self._pad_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(size={self.size}, size_divisor={self.size_divisor}, ' \ - f'pad_val={self.pad_val})' - return repr_str - - -@PIPELINES.register_module() -class Normalize(object): - """Normalize the image. - - Added key is "img_norm_cfg". - - Args: - mean (sequence): Mean values of 3 channels. - std (sequence): Std values of 3 channels. - to_rgb (bool): Whether to convert the image from BGR to RGB, - default is true. - """ - - def __init__(self, mean, std, to_rgb=True): - self.mean = np.array(mean, dtype=np.float32) - self.std = np.array(std, dtype=np.float32) - self.to_rgb = to_rgb - - def __call__(self, results): - """Call function to normalize images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Normalized results, 'img_norm_cfg' key is added into - result dict. - """ - - results['img'] = mmcv.imnormalize(results['img'], self.mean, self.std, - self.to_rgb) - results['img_norm_cfg'] = dict( - mean=self.mean, std=self.std, to_rgb=self.to_rgb) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(mean={self.mean}, std={self.std}, to_rgb=' \ - f'{self.to_rgb})' - return repr_str - - -@PIPELINES.register_module() -class Rerange(object): - """Rerange the image pixel value. - - Args: - min_value (float or int): Minimum value of the reranged image. - Default: 0. - max_value (float or int): Maximum value of the reranged image. - Default: 255. - """ - - def __init__(self, min_value=0, max_value=255): - assert isinstance(min_value, float) or isinstance(min_value, int) - assert isinstance(max_value, float) or isinstance(max_value, int) - assert min_value < max_value - self.min_value = min_value - self.max_value = max_value - - def __call__(self, results): - """Call function to rerange images. - - Args: - results (dict): Result dict from loading pipeline. - Returns: - dict: Reranged results. - """ - - img = results['img'] - img_min_value = np.min(img) - img_max_value = np.max(img) - - assert img_min_value < img_max_value - # rerange to [0, 1] - img = (img - img_min_value) / (img_max_value - img_min_value) - # rerange to [min_value, max_value] - img = img * (self.max_value - self.min_value) + self.min_value - results['img'] = img - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(min_value={self.min_value}, max_value={self.max_value})' - return repr_str - - -@PIPELINES.register_module() -class CLAHE(object): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - """ - - def __init__(self, clip_limit=40.0, tile_grid_size=(8, 8)): - assert isinstance(clip_limit, (float, int)) - self.clip_limit = clip_limit - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - self.tile_grid_size = tile_grid_size - - def __call__(self, results): - """Call function to Use CLAHE method process images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - for i in range(results['img'].shape[2]): - results['img'][:, :, i] = mmcv.clahe( - np.array(results['img'][:, :, i], dtype=np.uint8), - self.clip_limit, self.tile_grid_size) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(clip_limit={self.clip_limit}, '\ - f'tile_grid_size={self.tile_grid_size})' - return repr_str - - -@PIPELINES.register_module() -class RandomCrop(object): - """Random crop the image & seg. - - Args: - crop_size (tuple): Expected size after cropping, (h, w). - cat_max_ratio (float): The maximum ratio that single category could - occupy. - """ - - def __init__(self, crop_size, cat_max_ratio=1., ignore_index=255): - assert crop_size[0] > 0 and crop_size[1] > 0 - self.crop_size = crop_size - self.cat_max_ratio = cat_max_ratio - self.ignore_index = ignore_index - - def get_crop_bbox(self, img): - """Randomly get a crop bounding box.""" - margin_h = max(img.shape[0] - self.crop_size[0], 0) - margin_w = max(img.shape[1] - self.crop_size[1], 0) - offset_h = np.random.randint(0, margin_h + 1) - offset_w = np.random.randint(0, margin_w + 1) - crop_y1, crop_y2 = offset_h, offset_h + self.crop_size[0] - crop_x1, crop_x2 = offset_w, offset_w + self.crop_size[1] - - return crop_y1, crop_y2, crop_x1, crop_x2 - - def crop(self, img, crop_bbox): - """Crop from ``img``""" - crop_y1, crop_y2, crop_x1, crop_x2 = crop_bbox - img = img[crop_y1:crop_y2, crop_x1:crop_x2, ...] - return img - - def __call__(self, results): - """Call function to randomly crop images, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Randomly cropped results, 'img_shape' key in result dict is - updated according to crop size. - """ - - img = results['img'] - crop_bbox = self.get_crop_bbox(img) - if self.cat_max_ratio < 1.: - # Repeat 10 times - for _ in range(10): - seg_temp = self.crop(results['gt_semantic_seg'], crop_bbox) - labels, cnt = np.unique(seg_temp, return_counts=True) - cnt = cnt[labels != self.ignore_index] - if len(cnt) > 1 and np.max(cnt) / np.sum( - cnt) < self.cat_max_ratio: - break - crop_bbox = self.get_crop_bbox(img) - - # crop the image - img = self.crop(img, crop_bbox) - img_shape = img.shape - results['img'] = img - results['img_shape'] = img_shape - - # crop semantic seg - for key in results.get('seg_fields', []): - results[key] = self.crop(results[key], crop_bbox) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(crop_size={self.crop_size})' - - -@PIPELINES.register_module() -class RandomRotate(object): - """Rotate the image & seg. - - Args: - prob (float): The rotation probability. - degree (float, tuple[float]): Range of degrees to select from. If - degree is a number instead of tuple like (min, max), - the range of degree will be (``-degree``, ``+degree``) - pad_val (float, optional): Padding value of image. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. Default: None. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. Default: False - """ - - def __init__(self, - prob, - degree, - pad_val=0, - seg_pad_val=255, - center=None, - auto_bound=False): - self.prob = prob - assert prob >= 0 and prob <= 1 - if isinstance(degree, (float, int)): - assert degree > 0, f'degree {degree} should be positive' - self.degree = (-degree, degree) - else: - self.degree = degree - assert len(self.degree) == 2, f'degree {self.degree} should be a ' \ - f'tuple of (min, max)' - self.pal_val = pad_val - self.seg_pad_val = seg_pad_val - self.center = center - self.auto_bound = auto_bound - - def __call__(self, results): - """Call function to rotate image, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Rotated results. - """ - - rotate = True if np.random.rand() < self.prob else False - degree = np.random.uniform(min(*self.degree), max(*self.degree)) - if rotate: - # rotate image - results['img'] = mmcv.imrotate( - results['img'], - angle=degree, - border_value=self.pal_val, - center=self.center, - auto_bound=self.auto_bound) - - # rotate segs - for key in results.get('seg_fields', []): - results[key] = mmcv.imrotate( - results[key], - angle=degree, - border_value=self.seg_pad_val, - center=self.center, - auto_bound=self.auto_bound, - interpolation='nearest') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' \ - f'degree={self.degree}, ' \ - f'pad_val={self.pal_val}, ' \ - f'seg_pad_val={self.seg_pad_val}, ' \ - f'center={self.center}, ' \ - f'auto_bound={self.auto_bound})' - return repr_str - - -@PIPELINES.register_module() -class RGB2Gray(object): - """Convert RGB image to grayscale image. - - This transform calculate the weighted mean of input image channels with - ``weights`` and then expand the channels to ``out_channels``. When - ``out_channels`` is None, the number of output channels is the same as - input channels. - - Args: - out_channels (int): Expected number of output channels after - transforming. Default: None. - weights (tuple[float]): The weights to calculate the weighted mean. - Default: (0.299, 0.587, 0.114). - """ - - def __init__(self, out_channels=None, weights=(0.299, 0.587, 0.114)): - assert out_channels is None or out_channels > 0 - self.out_channels = out_channels - assert isinstance(weights, tuple) - for item in weights: - assert isinstance(item, (float, int)) - self.weights = weights - - def __call__(self, results): - """Call function to convert RGB image to grayscale image. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with grayscale image. - """ - img = results['img'] - assert len(img.shape) == 3 - assert img.shape[2] == len(self.weights) - weights = np.array(self.weights).reshape((1, 1, -1)) - img = (img * weights).sum(2, keepdims=True) - if self.out_channels is None: - img = img.repeat(weights.shape[2], axis=2) - else: - img = img.repeat(self.out_channels, axis=2) - - results['img'] = img - results['img_shape'] = img.shape - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(out_channels={self.out_channels}, ' \ - f'weights={self.weights})' - return repr_str - - -@PIPELINES.register_module() -class AdjustGamma(object): - """Using gamma correction to process the image. - - Args: - gamma (float or int): Gamma value used in gamma correction. - Default: 1.0. - """ - - def __init__(self, gamma=1.0): - assert isinstance(gamma, float) or isinstance(gamma, int) - assert gamma > 0 - self.gamma = gamma - inv_gamma = 1.0 / gamma - self.table = np.array([(i / 255.0)**inv_gamma * 255 - for i in np.arange(256)]).astype('uint8') - - def __call__(self, results): - """Call function to process the image with gamma correction. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - results['img'] = mmcv.lut_transform( - np.array(results['img'], dtype=np.uint8), self.table) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(gamma={self.gamma})' - - -@PIPELINES.register_module() -class SegRescale(object): - """Rescale semantic segmentation maps. - - Args: - scale_factor (float): The scale factor of the final output. - """ - - def __init__(self, scale_factor=1): - self.scale_factor = scale_factor - - def __call__(self, results): - """Call function to scale the semantic segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with semantic segmentation map scaled. - """ - for key in results.get('seg_fields', []): - if self.scale_factor != 1: - results[key] = mmcv.imrescale( - results[key], self.scale_factor, interpolation='nearest') - return results - - def __repr__(self): - return self.__class__.__name__ + f'(scale_factor={self.scale_factor})' - - -@PIPELINES.register_module() -class PhotoMetricDistortion(object): - """Apply photometric distortion to image sequentially, every transformation - is applied with a probability of 0.5. The position of random contrast is in - second or second to last. - - 1. random brightness - 2. random contrast (mode 0) - 3. convert color from BGR to HSV - 4. random saturation - 5. random hue - 6. convert color from HSV to BGR - 7. random contrast (mode 1) - - Args: - brightness_delta (int): delta of brightness. - contrast_range (tuple): range of contrast. - saturation_range (tuple): range of saturation. - hue_delta (int): delta of hue. - """ - - def __init__(self, - brightness_delta=32, - contrast_range=(0.5, 1.5), - saturation_range=(0.5, 1.5), - hue_delta=18): - self.brightness_delta = brightness_delta - self.contrast_lower, self.contrast_upper = contrast_range - self.saturation_lower, self.saturation_upper = saturation_range - self.hue_delta = hue_delta - - def convert(self, img, alpha=1, beta=0): - """Multiple with alpha and add beat with clip.""" - img = img.astype(np.float32) * alpha + beta - img = np.clip(img, 0, 255) - return img.astype(np.uint8) - - def brightness(self, img): - """Brightness distortion.""" - if random.randint(2): - return self.convert( - img, - beta=random.uniform(-self.brightness_delta, - self.brightness_delta)) - return img - - def contrast(self, img): - """Contrast distortion.""" - if random.randint(2): - return self.convert( - img, - alpha=random.uniform(self.contrast_lower, self.contrast_upper)) - return img - - def saturation(self, img): - """Saturation distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, 1] = self.convert( - img[:, :, 1], - alpha=random.uniform(self.saturation_lower, - self.saturation_upper)) - img = mmcv.hsv2bgr(img) - return img - - def hue(self, img): - """Hue distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, - 0] = (img[:, :, 0].astype(int) + - random.randint(-self.hue_delta, self.hue_delta)) % 180 - img = mmcv.hsv2bgr(img) - return img - - def __call__(self, results): - """Call function to perform photometric distortion on images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images distorted. - """ - - img = results['img'] - # random brightness - img = self.brightness(img) - - # mode == 0 --> do random contrast first - # mode == 1 --> do random contrast last - mode = random.randint(2) - if mode == 1: - img = self.contrast(img) - - # random saturation - img = self.saturation(img) - - # random hue - img = self.hue(img) - - # random contrast - if mode == 0: - img = self.contrast(img) - - results['img'] = img - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(brightness_delta={self.brightness_delta}, ' - f'contrast_range=({self.contrast_lower}, ' - f'{self.contrast_upper}), ' - f'saturation_range=({self.saturation_lower}, ' - f'{self.saturation_upper}), ' - f'hue_delta={self.hue_delta})') - return repr_str - - -@PIPELINES.register_module() -class RandomCutOut(object): - """CutOut operation. - - Randomly drop some regions of image used in - `Cutout `_. - Args: - prob (float): cutout probability. - n_holes (int | tuple[int, int]): Number of regions to be dropped. - If it is given as a list, number of holes will be randomly - selected from the closed interval [`n_holes[0]`, `n_holes[1]`]. - cutout_shape (tuple[int, int] | list[tuple[int, int]]): The candidate - shape of dropped regions. It can be `tuple[int, int]` to use a - fixed cutout shape, or `list[tuple[int, int]]` to randomly choose - shape from the list. - cutout_ratio (tuple[float, float] | list[tuple[float, float]]): The - candidate ratio of dropped regions. It can be `tuple[float, float]` - to use a fixed ratio or `list[tuple[float, float]]` to randomly - choose ratio from the list. Please note that `cutout_shape` - and `cutout_ratio` cannot be both given at the same time. - fill_in (tuple[float, float, float] | tuple[int, int, int]): The value - of pixel to fill in the dropped regions. Default: (0, 0, 0). - seg_fill_in (int): The labels of pixel to fill in the dropped regions. - If seg_fill_in is None, skip. Default: None. - """ - - def __init__(self, - prob, - n_holes, - cutout_shape=None, - cutout_ratio=None, - fill_in=(0, 0, 0), - seg_fill_in=None): - - assert 0 <= prob and prob <= 1 - assert (cutout_shape is None) ^ (cutout_ratio is None), \ - 'Either cutout_shape or cutout_ratio should be specified.' - assert (isinstance(cutout_shape, (list, tuple)) - or isinstance(cutout_ratio, (list, tuple))) - if isinstance(n_holes, tuple): - assert len(n_holes) == 2 and 0 <= n_holes[0] < n_holes[1] - else: - n_holes = (n_holes, n_holes) - if seg_fill_in is not None: - assert (isinstance(seg_fill_in, int) and 0 <= seg_fill_in - and seg_fill_in <= 255) - self.prob = prob - self.n_holes = n_holes - self.fill_in = fill_in - self.seg_fill_in = seg_fill_in - self.with_ratio = cutout_ratio is not None - self.candidates = cutout_ratio if self.with_ratio else cutout_shape - if not isinstance(self.candidates, list): - self.candidates = [self.candidates] - - def __call__(self, results): - """Call function to drop some regions of image.""" - cutout = True if np.random.rand() < self.prob else False - if cutout: - h, w, c = results['img'].shape - n_holes = np.random.randint(self.n_holes[0], self.n_holes[1] + 1) - for _ in range(n_holes): - x1 = np.random.randint(0, w) - y1 = np.random.randint(0, h) - index = np.random.randint(0, len(self.candidates)) - if not self.with_ratio: - cutout_w, cutout_h = self.candidates[index] - else: - cutout_w = int(self.candidates[index][0] * w) - cutout_h = int(self.candidates[index][1] * h) - - x2 = np.clip(x1 + cutout_w, 0, w) - y2 = np.clip(y1 + cutout_h, 0, h) - results['img'][y1:y2, x1:x2, :] = self.fill_in - - if self.seg_fill_in is not None: - for key in results.get('seg_fields', []): - results[key][y1:y2, x1:x2] = self.seg_fill_in - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' - repr_str += f'n_holes={self.n_holes}, ' - repr_str += (f'cutout_ratio={self.candidates}, ' if self.with_ratio - else f'cutout_shape={self.candidates}, ') - repr_str += f'fill_in={self.fill_in}, ' - repr_str += f'seg_fill_in={self.seg_fill_in})' - return repr_str diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/__init__.py deleted file mode 100755 index c6697b897..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .backbones import * # noqa: F401,F403 -from .builder import (BACKBONES, HEADS, LOSSES, SEGMENTORS, build_backbone, - build_head, build_loss, build_segmentor) -from .decode_heads import * # noqa: F401,F403 -from .losses import * # noqa: F401,F403 -from .necks import * # noqa: F401,F403 -from .segmentors import * # noqa: F401,F403 - -__all__ = [ - 'BACKBONES', 'HEADS', 'LOSSES', 'SEGMENTORS', 'build_backbone', - 'build_head', 'build_loss', 'build_segmentor' -] \ No newline at end of file diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/backbones/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/backbones/__init__.py deleted file mode 100755 index 0b0fc9109..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/backbones/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# from .resnest import ResNeSt -from .resnet import ResNet, ResNetV1c, ResNetV1d -# from .resnext import ResNeXt -# from .timm_backbone import TIMMBackbone \ No newline at end of file diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/backbones/resnet.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/backbones/resnet.py deleted file mode 100755 index e8b961d5f..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/backbones/resnet.py +++ /dev/null @@ -1,714 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.utils.checkpoint as cp -from mmcv.cnn import build_conv_layer, build_norm_layer, build_plugin_layer -from mmcv.runner import BaseModule -from mmcv.utils.parrots_wrapper import _BatchNorm - -from ..builder import BACKBONES -from ..utils import ResLayer - - -class BasicBlock(BaseModule): - """Basic block for ResNet.""" - - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(BasicBlock, self).__init__(init_cfg) - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - 3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - self.add_module(self.norm1_name, norm1) - self.conv2 = build_conv_layer( - conv_cfg, planes, planes, 3, padding=1, bias=False) - self.add_module(self.norm2_name, norm2) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.norm2(out) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -class Bottleneck(BaseModule): - """Bottleneck block for ResNet. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if it is - "caffe", the stride-two layer is the first 1x1 conv layer. - """ - - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(Bottleneck, self).__init__(init_cfg) - assert style in ['pytorch', 'caffe'] - assert dcn is None or isinstance(dcn, dict) - assert plugins is None or isinstance(plugins, list) - if plugins is not None: - allowed_position = ['after_conv1', 'after_conv2', 'after_conv3'] - assert all(p['position'] in allowed_position for p in plugins) - - self.inplanes = inplanes - self.planes = planes - self.stride = stride - self.dilation = dilation - self.style = style - self.with_cp = with_cp - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.dcn = dcn - self.with_dcn = dcn is not None - self.plugins = plugins - self.with_plugins = plugins is not None - - if self.with_plugins: - # collect plugins for conv1/conv2/conv3 - self.after_conv1_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv1' - ] - self.after_conv2_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv2' - ] - self.after_conv3_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv3' - ] - - if self.style == 'pytorch': - self.conv1_stride = 1 - self.conv2_stride = stride - else: - self.conv1_stride = stride - self.conv2_stride = 1 - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - self.norm3_name, norm3 = build_norm_layer( - norm_cfg, planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - fallback_on_stride = False - if self.with_dcn: - fallback_on_stride = dcn.pop('fallback_on_stride', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = build_conv_layer( - conv_cfg, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - else: - assert self.conv_cfg is None, 'conv_cfg must be None for DCN' - self.conv2 = build_conv_layer( - dcn, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.add_module(self.norm2_name, norm2) - self.conv3 = build_conv_layer( - conv_cfg, - planes, - planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - - if self.with_plugins: - self.after_conv1_plugin_names = self.make_block_plugins( - planes, self.after_conv1_plugins) - self.after_conv2_plugin_names = self.make_block_plugins( - planes, self.after_conv2_plugins) - self.after_conv3_plugin_names = self.make_block_plugins( - planes * self.expansion, self.after_conv3_plugins) - - def make_block_plugins(self, in_channels, plugins): - """make plugins for block. - - Args: - in_channels (int): Input channels of plugin. - plugins (list[dict]): List of plugins cfg to build. - - Returns: - list[str]: List of the names of plugin. - """ - assert isinstance(plugins, list) - plugin_names = [] - for plugin in plugins: - plugin = plugin.copy() - name, layer = build_plugin_layer( - plugin, - in_channels=in_channels, - postfix=plugin.pop('postfix', '')) - assert not hasattr(self, name), f'duplicate plugin {name}' - self.add_module(name, layer) - plugin_names.append(name) - return plugin_names - - def forward_plugin(self, x, plugin_names): - """Forward function for plugins.""" - out = x - for name in plugin_names: - out = getattr(self, name)(x) - return out - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - @property - def norm3(self): - """nn.Module: normalization layer after the third convolution layer""" - return getattr(self, self.norm3_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv1_plugin_names) - - out = self.conv2(out) - out = self.norm2(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv2_plugin_names) - - out = self.conv3(out) - out = self.norm3(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv3_plugin_names) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -@BACKBONES.register_module() -class ResNet(BaseModule): - """ResNet backbone. - - This backbone is the improved implementation of `Deep Residual Learning - for Image Recognition `_. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - in_channels (int): Number of input image channels. Default: 3. - stem_channels (int): Number of stem channels. Default: 64. - base_channels (int): Number of base channels of res layer. Default: 64. - num_stages (int): Resnet stages, normally 4. Default: 4. - strides (Sequence[int]): Strides of the first block of each stage. - Default: (1, 2, 2, 2). - dilations (Sequence[int]): Dilation of each stage. - Default: (1, 1, 1, 1). - out_indices (Sequence[int]): Output from which stages. - Default: (0, 1, 2, 3). - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. Default: 'pytorch'. - deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv. - Default: False. - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False. - frozen_stages (int): Stages to be frozen (stop grad and set eval mode). - -1 means not freezing any parameters. Default: -1. - conv_cfg (dict | None): Dictionary to construct and config conv layer. - When conv_cfg is None, cfg will be set to dict(type='Conv2d'). - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN', requires_grad=True). - norm_eval (bool): Whether to set norm layers to eval mode, namely, - freeze running stats (mean and var). Note: Effect on Batch Norm - and its variants only. Default: False. - dcn (dict | None): Dictionary to construct and config DCN conv layer. - When dcn is not None, conv_cfg must be None. Default: None. - stage_with_dcn (Sequence[bool]): Whether to set DCN conv for each - stage. The length of stage_with_dcn is equal to num_stages. - Default: (False, False, False, False). - plugins (list[dict]): List of plugins for stages, each dict contains: - - - cfg (dict, required): Cfg dict to build plugin. - - - position (str, required): Position inside block to insert plugin, - options: 'after_conv1', 'after_conv2', 'after_conv3'. - - - stages (tuple[bool], optional): Stages to apply plugin, length - should be same as 'num_stages'. - Default: None. - multi_grid (Sequence[int]|None): Multi grid dilation rates of last - stage. Default: None. - contract_dilation (bool): Whether contract first dilation of each layer - Default: False. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - zero_init_residual (bool): Whether to use zero init for last norm layer - in resblocks to let them behave as identity. Default: True. - pretrained (str, optional): model pretrained path. Default: None. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Example: - >>> from mmseg.models import ResNet - >>> import torch - >>> self = ResNet(depth=18) - >>> self.eval() - >>> inputs = torch.rand(1, 3, 32, 32) - >>> level_outputs = self.forward(inputs) - >>> for level_out in level_outputs: - ... print(tuple(level_out.shape)) - (1, 64, 8, 8) - (1, 128, 4, 4) - (1, 256, 2, 2) - (1, 512, 1, 1) - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - in_channels=3, - stem_channels=64, - base_channels=64, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - deep_stem=False, - avg_down=False, - frozen_stages=-1, - conv_cfg=None, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=False, - dcn=None, - stage_with_dcn=(False, False, False, False), - plugins=None, - multi_grid=None, - contract_dilation=False, - with_cp=False, - zero_init_residual=True, - pretrained=None, - init_cfg=None): - super(ResNet, self).__init__(init_cfg) - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - - self.pretrained = pretrained - self.zero_init_residual = zero_init_residual - block_init_cfg = None - assert not (init_cfg and pretrained), \ - 'init_cfg and pretrained cannot be setting at the same time' - if isinstance(pretrained, str): - warnings.warn('DeprecationWarning: pretrained is a deprecated, ' - 'please use "init_cfg" instead') - self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - elif pretrained is None: - if init_cfg is None: - self.init_cfg = [ - dict(type='Kaiming', layer='Conv2d'), - dict( - type='Constant', - val=1, - layer=['_BatchNorm', 'GroupNorm']) - ] - block = self.arch_settings[depth][0] - if self.zero_init_residual: - if block is BasicBlock: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm2')) - elif block is Bottleneck: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm3')) - else: - raise TypeError('pretrained must be a str or None') - - self.depth = depth - self.stem_channels = stem_channels - self.base_channels = base_channels - self.num_stages = num_stages - assert num_stages >= 1 and num_stages <= 4 - self.strides = strides - self.dilations = dilations - assert len(strides) == len(dilations) == num_stages - self.out_indices = out_indices - assert max(out_indices) < num_stages - self.style = style - self.deep_stem = deep_stem - self.avg_down = avg_down - self.frozen_stages = frozen_stages - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.with_cp = with_cp - self.norm_eval = norm_eval - self.dcn = dcn - self.stage_with_dcn = stage_with_dcn - if dcn is not None: - assert len(stage_with_dcn) == num_stages - self.plugins = plugins - self.multi_grid = multi_grid - self.contract_dilation = contract_dilation - self.block, stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - self.inplanes = stem_channels - - self._make_stem_layer(in_channels, stem_channels) - - self.res_layers = [] - for i, num_blocks in enumerate(self.stage_blocks): - stride = strides[i] - dilation = dilations[i] - dcn = self.dcn if self.stage_with_dcn[i] else None - if plugins is not None: - stage_plugins = self.make_stage_plugins(plugins, i) - else: - stage_plugins = None - # multi grid is applied to last layer only - stage_multi_grid = multi_grid if i == len( - self.stage_blocks) - 1 else None - planes = base_channels * 2**i - res_layer = self.make_res_layer( - block=self.block, - inplanes=self.inplanes, - planes=planes, - num_blocks=num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - avg_down=self.avg_down, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - dcn=dcn, - plugins=stage_plugins, - multi_grid=stage_multi_grid, - contract_dilation=contract_dilation, - init_cfg=block_init_cfg) - self.inplanes = planes * self.block.expansion - layer_name = f'layer{i+1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self._freeze_stages() - - self.feat_dim = self.block.expansion * base_channels * 2**( - len(self.stage_blocks) - 1) - - def make_stage_plugins(self, plugins, stage_idx): - """make plugins for ResNet 'stage_idx'th stage . - - Currently we support to insert 'context_block', - 'empirical_attention_block', 'nonlocal_block' into the backbone like - ResNet/ResNeXt. They could be inserted after conv1/conv2/conv3 of - Bottleneck. - - An example of plugins format could be : - >>> plugins=[ - ... dict(cfg=dict(type='xxx', arg1='xxx'), - ... stages=(False, True, True, True), - ... position='after_conv2'), - ... dict(cfg=dict(type='yyy'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='1'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='2'), - ... stages=(True, True, True, True), - ... position='after_conv3') - ... ] - >>> self = ResNet(depth=18) - >>> stage_plugins = self.make_stage_plugins(plugins, 0) - >>> assert len(stage_plugins) == 3 - - Suppose 'stage_idx=0', the structure of blocks in the stage would be: - conv1-> conv2->conv3->yyy->zzz1->zzz2 - Suppose 'stage_idx=1', the structure of blocks in the stage would be: - conv1-> conv2->xxx->conv3->yyy->zzz1->zzz2 - - If stages is missing, the plugin would be applied to all stages. - - Args: - plugins (list[dict]): List of plugins cfg to build. The postfix is - required if multiple same type plugins are inserted. - stage_idx (int): Index of stage to build - - Returns: - list[dict]: Plugins for current stage - """ - stage_plugins = [] - for plugin in plugins: - plugin = plugin.copy() - stages = plugin.pop('stages', None) - assert stages is None or len(stages) == self.num_stages - # whether to insert plugin into current stage - if stages is None or stages[stage_idx]: - stage_plugins.append(plugin) - - return stage_plugins - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``.""" - return ResLayer(**kwargs) - - @property - def norm1(self): - """nn.Module: the normalization layer named "norm1" """ - return getattr(self, self.norm1_name) - - def _make_stem_layer(self, in_channels, stem_channels): - """Make stem layer for ResNet.""" - if self.deep_stem: - self.stem = nn.Sequential( - build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels // 2, - kernel_size=3, - stride=2, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels // 2, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels)[1], - nn.ReLU(inplace=True)) - else: - self.conv1 = build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels, - kernel_size=7, - stride=2, - padding=3, - bias=False) - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, stem_channels, postfix=1) - self.add_module(self.norm1_name, norm1) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - def _freeze_stages(self): - """Freeze stages param and norm stats.""" - if self.frozen_stages >= 0: - if self.deep_stem: - self.stem.eval() - for param in self.stem.parameters(): - param.requires_grad = False - else: - self.norm1.eval() - for m in [self.conv1, self.norm1]: - for param in m.parameters(): - param.requires_grad = False - - for i in range(1, self.frozen_stages + 1): - m = getattr(self, f'layer{i}') - m.eval() - for param in m.parameters(): - param.requires_grad = False - - def forward(self, x): - """Forward function.""" - if self.deep_stem: - x = self.stem(x) - else: - x = self.conv1(x) - x = self.norm1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - return tuple(outs) - - def train(self, mode=True): - """Convert the model into training mode while keep normalization layer - freezed.""" - super(ResNet, self).train(mode) - self._freeze_stages() - if mode and self.norm_eval: - for m in self.modules(): - # trick: eval have effect on BatchNorm only - if isinstance(m, _BatchNorm): - m.eval() - - -@BACKBONES.register_module() -class ResNetV1c(ResNet): - """ResNetV1c variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1c replaces the 7x7 conv in - the input stem with three 3x3 convs. For more details please refer to `Bag - of Tricks for Image Classification with Convolutional Neural Networks - `_. - """ - - def __init__(self, **kwargs): - super(ResNetV1c, self).__init__( - deep_stem=True, avg_down=False, **kwargs) - - -@BACKBONES.register_module() -class ResNetV1d(ResNet): - """ResNetV1d variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1d replaces the 7x7 conv in - the input stem with three 3x3 convs. And in the downsampling block, a 2x2 - avg_pool with stride 2 is added before conv, whose stride is changed to 1. - """ - - def __init__(self, **kwargs): - super(ResNetV1d, self).__init__( - deep_stem=True, avg_down=True, **kwargs) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/builder.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/builder.py deleted file mode 100755 index 5e18e4e64..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/builder.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -from mmcv.cnn import MODELS as MMCV_MODELS -from mmcv.cnn.bricks.registry import ATTENTION as MMCV_ATTENTION -from mmcv.utils import Registry - -MODELS = Registry('models', parent=MMCV_MODELS) -ATTENTION = Registry('attention', parent=MMCV_ATTENTION) - -BACKBONES = MODELS -NECKS = MODELS -HEADS = MODELS -LOSSES = MODELS -SEGMENTORS = MODELS - - -def build_backbone(cfg): - """Build backbone.""" - return BACKBONES.build(cfg) - - -def build_neck(cfg): - """Build neck.""" - return NECKS.build(cfg) - - -def build_head(cfg): - """Build head.""" - return HEADS.build(cfg) - - -def build_loss(cfg): - """Build loss.""" - return LOSSES.build(cfg) - - -def build_segmentor(cfg, train_cfg=None, test_cfg=None): - """Build segmentor.""" - if train_cfg is not None or test_cfg is not None: - warnings.warn( - 'train_cfg and test_cfg is deprecated, ' - 'please specify them in model', UserWarning) - assert cfg.get('train_cfg') is None or train_cfg is None, \ - 'train_cfg specified in both outer field and model field ' - assert cfg.get('test_cfg') is None or test_cfg is None, \ - 'test_cfg specified in both outer field and model field ' - return SEGMENTORS.build( - cfg, default_args=dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/__init__.py deleted file mode 100755 index 45d97b96b..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/__init__.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -# from .ann_head import ANNHead -# from .apc_head import APCHead -# from .aspp_head import ASPPHead -# from .cc_head import CCHead -# from .da_head import DAHead -# from .dm_head import DMHead -# from .dnl_head import DNLHead -# from .dpt_head import DPTHead -# from .ema_head import EMAHead -# from .enc_head import EncHead -from .fcn_head import FCNHead -# from .fpn_head import FPNHead -from .gc_head import GCHead -from .decode_head import BaseDecodeHead -# from .isa_head import ISAHead -# from .lraspp_head import LRASPPHead -# from .nl_head import NLHead -# from .ocr_head import OCRHead -# from .point_head import PointHead -# from .psa_head import PSAHead -# from .psp_head import PSPHead -# from .segformer_head import SegformerHead -# from .sep_aspp_head import DepthwiseSeparableASPPHead -# from .sep_fcn_head import DepthwiseSeparableFCNHead -# from .setr_mla_head import SETRMLAHead -# from .setr_up_head import SETRUPHead -# from .stdc_head import STDCHead -# from .uper_head import UPerHead - -# __all__ = [ -# 'FCNHead', 'PSPHead', 'ASPPHead', 'PSAHead', 'NLHead', 'GCHead', 'CCHead', -# 'UPerHead', 'DepthwiseSeparableASPPHead', 'ANNHead', 'DAHead', 'OCRHead', -# 'EncHead', 'DepthwiseSeparableFCNHead', 'FPNHead', 'EMAHead', 'DNLHead', -# 'PointHead', 'APCHead', 'DMHead', 'LRASPPHead', 'SETRUPHead', -# 'SETRMLAHead', 'DPTHead', 'SETRMLAHead', 'SegformerHead', 'ISAHead', -# 'STDCHead' -# ] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/decode_head.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/decode_head.py deleted file mode 100755 index 1443a81da..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/decode_head.py +++ /dev/null @@ -1,265 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -import torch -import torch.nn as nn -from mmcv.runner import BaseModule, auto_fp16, force_fp32 - -from mmseg.core import build_pixel_sampler -from mmseg.ops import resize -from ..builder import build_loss -from ..losses import accuracy - - -class BaseDecodeHead(BaseModule, metaclass=ABCMeta): - """Base class for BaseDecodeHead. - - Args: - in_channels (int|Sequence[int]): Input channels. - channels (int): Channels after modules, before conv_seg. - num_classes (int): Number of classes. - dropout_ratio (float): Ratio of dropout layer. Default: 0.1. - conv_cfg (dict|None): Config of conv layers. Default: None. - norm_cfg (dict|None): Config of norm layers. Default: None. - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU') - in_index (int|Sequence[int]): Input feature index. Default: -1 - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - Default: None. - loss_decode (dict | Sequence[dict]): Config of decode loss. - The `loss_name` is property of corresponding loss function which - could be shown in training log. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - e.g. dict(type='CrossEntropyLoss'), - [dict(type='CrossEntropyLoss', loss_name='loss_ce'), - dict(type='DiceLoss', loss_name='loss_dice')] - Default: dict(type='CrossEntropyLoss'). - ignore_index (int | None): The label index to be ignored. When using - masked BCE loss, ignore_index should be set to None. Default: 255. - sampler (dict|None): The config of segmentation map sampler. - Default: None. - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - """ - - def __init__(self, - in_channels, - channels, - *, - num_classes, - dropout_ratio=0.1, - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - in_index=-1, - input_transform=None, - loss_decode=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0), - ignore_index=255, - sampler=None, - align_corners=False, - init_cfg=dict( - type='Normal', std=0.01, override=dict(name='conv_seg'))): - super(BaseDecodeHead, self).__init__(init_cfg) - self._init_inputs(in_channels, in_index, input_transform) - self.channels = channels - self.num_classes = num_classes - self.dropout_ratio = dropout_ratio - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.in_index = in_index - - self.ignore_index = ignore_index - self.align_corners = align_corners - - if isinstance(loss_decode, dict): - self.loss_decode = build_loss(loss_decode) - elif isinstance(loss_decode, (list, tuple)): - self.loss_decode = nn.ModuleList() - for loss in loss_decode: - self.loss_decode.append(build_loss(loss)) - else: - raise TypeError(f'loss_decode must be a dict or sequence of dict,\ - but got {type(loss_decode)}') - - if sampler is not None: - self.sampler = build_pixel_sampler(sampler, context=self) - else: - self.sampler = None - - self.conv_seg = nn.Conv2d(channels, num_classes, kernel_size=1) - if dropout_ratio > 0: - self.dropout = nn.Dropout2d(dropout_ratio) - else: - self.dropout = None - self.fp16_enabled = False - - def extra_repr(self): - """Extra repr.""" - s = f'input_transform={self.input_transform}, ' \ - f'ignore_index={self.ignore_index}, ' \ - f'align_corners={self.align_corners}' - return s - - def _init_inputs(self, in_channels, in_index, input_transform): - """Check and initialize input transforms. - - The in_channels, in_index and input_transform must match. - Specifically, when input_transform is None, only single feature map - will be selected. So in_channels and in_index must be of type int. - When input_transform - - Args: - in_channels (int|Sequence[int]): Input channels. - in_index (int|Sequence[int]): Input feature index. - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - """ - - if input_transform is not None: - assert input_transform in ['resize_concat', 'multiple_select'] - self.input_transform = input_transform - self.in_index = in_index - if input_transform is not None: - assert isinstance(in_channels, (list, tuple)) - assert isinstance(in_index, (list, tuple)) - assert len(in_channels) == len(in_index) - if input_transform == 'resize_concat': - self.in_channels = sum(in_channels) - else: - self.in_channels = in_channels - else: - assert isinstance(in_channels, int) - assert isinstance(in_index, int) - self.in_channels = in_channels - - def _transform_inputs(self, inputs): - """Transform inputs for decoder. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - Tensor: The transformed inputs - """ - - if self.input_transform == 'resize_concat': - inputs = [inputs[i] for i in self.in_index] - upsampled_inputs = [ - resize( - input=x, - size=inputs[0].shape[2:], - mode='bilinear', - align_corners=self.align_corners) for x in inputs - ] - inputs = torch.cat(upsampled_inputs, dim=1) - elif self.input_transform == 'multiple_select': - inputs = [inputs[i] for i in self.in_index] - else: - inputs = inputs[self.in_index] - - return inputs - - @auto_fp16() - @abstractmethod - def forward(self, inputs): - """Placeholder of forward function.""" - pass - - def forward_train(self, inputs, img_metas, gt_semantic_seg, train_cfg): - """Forward function for training. - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - train_cfg (dict): The training config. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - seg_logits = self.forward(inputs) - losses = self.losses(seg_logits, gt_semantic_seg) - return losses - - def forward_test(self, inputs, img_metas, test_cfg): - """Forward function for testing. - - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - test_cfg (dict): The testing config. - - Returns: - Tensor: Output segmentation map. - """ - return self.forward(inputs) - - def cls_seg(self, feat): - """Classify each pixel.""" - if self.dropout is not None: - feat = self.dropout(feat) - output = self.conv_seg(feat) - return output - - @force_fp32(apply_to=('seg_logit', )) - def losses(self, seg_logit, seg_label): - """Compute segmentation loss.""" - loss = dict() - seg_logit = resize( - input=seg_logit, - size=seg_label.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - if self.sampler is not None: - seg_weight = self.sampler.sample(seg_logit, seg_label) - else: - seg_weight = None - seg_label = seg_label.squeeze(1) - - if not isinstance(self.loss_decode, nn.ModuleList): - losses_decode = [self.loss_decode] - else: - losses_decode = self.loss_decode - for loss_decode in losses_decode: - if loss_decode.loss_name not in loss: - loss[loss_decode.loss_name] = loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - else: - loss[loss_decode.loss_name] += loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - - loss['acc_seg'] = accuracy(seg_logit, seg_label) - return loss diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/fcn_head.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/fcn_head.py deleted file mode 100755 index 3c8de51f6..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/fcn_head.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule - -from ..builder import HEADS -from .decode_head import BaseDecodeHead - - -@HEADS.register_module() -class FCNHead(BaseDecodeHead): - """Fully Convolution Networks for Semantic Segmentation. - - This head is implemented of `FCNNet `_. - - Args: - num_convs (int): Number of convs in the head. Default: 2. - kernel_size (int): The kernel size for convs in the head. Default: 3. - concat_input (bool): Whether concat the input and output of convs - before classification layer. - dilation (int): The dilation rate for convs in the head. Default: 1. - """ - - def __init__(self, - num_convs=2, - kernel_size=3, - concat_input=True, - dilation=1, - **kwargs): - assert num_convs >= 0 and dilation > 0 and isinstance(dilation, int) - self.num_convs = num_convs - self.concat_input = concat_input - self.kernel_size = kernel_size - super(FCNHead, self).__init__(**kwargs) - if num_convs == 0: - assert self.in_channels == self.channels - - conv_padding = (kernel_size // 2) * dilation - convs = [] - convs.append( - ConvModule( - self.in_channels, - self.channels, - kernel_size=kernel_size, - padding=conv_padding, - dilation=dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - for i in range(num_convs - 1): - convs.append( - ConvModule( - self.channels, - self.channels, - kernel_size=kernel_size, - padding=conv_padding, - dilation=dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - if num_convs == 0: - self.convs = nn.Identity() - else: - self.convs = nn.Sequential(*convs) - if self.concat_input: - self.conv_cat = ConvModule( - self.in_channels + self.channels, - self.channels, - kernel_size=kernel_size, - padding=kernel_size // 2, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - def forward(self, inputs): - """Forward function.""" - x = self._transform_inputs(inputs) - output = self.convs(x) - if self.concat_input: - output = self.conv_cat(torch.cat([x, output], dim=1)) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/gc_head.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/gc_head.py deleted file mode 100755 index eed507425..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/decode_heads/gc_head.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.cnn import ContextBlock - -from ..builder import HEADS -from .fcn_head import FCNHead - - -@HEADS.register_module() -class GCHead(FCNHead): - """GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond. - - This head is the implementation of `GCNet - `_. - - Args: - ratio (float): Multiplier of channels ratio. Default: 1/4. - pooling_type (str): The pooling type of context aggregation. - Options are 'att', 'avg'. Default: 'avg'. - fusion_types (tuple[str]): The fusion type for feature fusion. - Options are 'channel_add', 'channel_mul'. Default: ('channel_add',) - """ - - def __init__(self, - ratio=1 / 4., - pooling_type='att', - fusion_types=('channel_add', ), - **kwargs): - super(GCHead, self).__init__(num_convs=2, **kwargs) - self.ratio = ratio - self.pooling_type = pooling_type - self.fusion_types = fusion_types - self.gc_block = ContextBlock( - in_channels=self.channels, - ratio=self.ratio, - pooling_type=self.pooling_type, - fusion_types=self.fusion_types) - - def forward(self, inputs): - """Forward function.""" - x = self._transform_inputs(inputs) - output = self.convs[0](x) - output = self.gc_block(output) - output = self.convs[1](output) - if self.concat_input: - output = self.conv_cat(torch.cat([x, output], dim=1)) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/__init__.py deleted file mode 100755 index ef29f7589..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from .accuracy import Accuracy, accuracy -from .cross_entropy_loss import (CrossEntropyLoss, binary_cross_entropy, - cross_entropy, mask_cross_entropy) -# from .dice_loss import DiceLoss -# from .focal_loss import FocalLoss -# from .lovasz_loss import LovaszLoss -from .utils import reduce_loss, weight_reduce_loss, weighted_loss - -# __all__ = [ -# 'accuracy', 'Accuracy', 'cross_entropy', 'binary_cross_entropy', -# 'mask_cross_entropy', 'CrossEntropyLoss', 'reduce_loss', -# 'weight_reduce_loss', 'weighted_loss', 'LovaszLoss', 'DiceLoss', -# 'FocalLoss' -# ] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/accuracy.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/accuracy.py deleted file mode 100755 index f2cd16b7f..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/accuracy.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - - -def accuracy(pred, target, topk=1, thresh=None): - """Calculate accuracy according to the prediction and target. - - Args: - pred (torch.Tensor): The model prediction, shape (N, num_class, ...) - target (torch.Tensor): The target of each prediction, shape (N, , ...) - topk (int | tuple[int], optional): If the predictions in ``topk`` - matches the target, the predictions will be regarded as - correct ones. Defaults to 1. - thresh (float, optional): If not None, predictions with scores under - this threshold are considered incorrect. Default to None. - - Returns: - float | tuple[float]: If the input ``topk`` is a single integer, - the function will return a single float as accuracy. If - ``topk`` is a tuple containing multiple integers, the - function will return a tuple containing accuracies of - each ``topk`` number. - """ - assert isinstance(topk, (int, tuple)) - if isinstance(topk, int): - topk = (topk, ) - return_single = True - else: - return_single = False - - maxk = max(topk) - if pred.size(0) == 0: - accu = [pred.new_tensor(0.) for i in range(len(topk))] - return accu[0] if return_single else accu - assert pred.ndim == target.ndim + 1 - assert pred.size(0) == target.size(0) - assert maxk <= pred.size(1), \ - f'maxk {maxk} exceeds pred dimension {pred.size(1)}' - pred_value, pred_label = pred.topk(maxk, dim=1) - # transpose to shape (maxk, N, ...) - pred_label = pred_label.transpose(0, 1) - correct = pred_label.eq(target.unsqueeze(0).expand_as(pred_label)) - if thresh is not None: - # Only prediction values larger than thresh are counted as correct - correct = correct & (pred_value > thresh).t() - res = [] - for k in topk: - correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) - res.append(correct_k.mul_(100.0 / target.numel())) - return res[0] if return_single else res - - -class Accuracy(nn.Module): - """Accuracy calculation module.""" - - def __init__(self, topk=(1, ), thresh=None): - """Module to calculate the accuracy. - - Args: - topk (tuple, optional): The criterion used to calculate the - accuracy. Defaults to (1,). - thresh (float, optional): If not None, predictions with scores - under this threshold are considered incorrect. Default to None. - """ - super().__init__() - self.topk = topk - self.thresh = thresh - - def forward(self, pred, target): - """Forward function to calculate accuracy. - - Args: - pred (torch.Tensor): Prediction of models. - target (torch.Tensor): Target for each prediction. - - Returns: - tuple[float]: The accuracies under different topk criterions. - """ - return accuracy(pred, target, self.topk, self.thresh) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/cross_entropy_loss.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/cross_entropy_loss.py deleted file mode 100755 index ee489a888..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/cross_entropy_loss.py +++ /dev/null @@ -1,218 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weight_reduce_loss - - -def cross_entropy(pred, - label, - weight=None, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=-100): - """The wrapper function for :func:`F.cross_entropy`""" - # class_weight is a manual rescaling weight given to each class. - # If given, has to be a Tensor of size C element-wise losses - loss = F.cross_entropy( - pred, - label, - weight=class_weight, - reduction='none', - ignore_index=ignore_index) - - # apply weights and do the reduction - if weight is not None: - weight = weight.float() - loss = weight_reduce_loss( - loss, weight=weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def _expand_onehot_labels(labels, label_weights, target_shape, ignore_index): - """Expand onehot labels to match the size of prediction.""" - bin_labels = labels.new_zeros(target_shape) - valid_mask = (labels >= 0) & (labels != ignore_index) - inds = torch.nonzero(valid_mask, as_tuple=True) - - if inds[0].numel() > 0: - if labels.dim() == 3: - bin_labels[inds[0], labels[valid_mask], inds[1], inds[2]] = 1 - else: - bin_labels[inds[0], labels[valid_mask]] = 1 - - valid_mask = valid_mask.unsqueeze(1).expand(target_shape).float() - if label_weights is None: - bin_label_weights = valid_mask - else: - bin_label_weights = label_weights.unsqueeze(1).expand(target_shape) - bin_label_weights *= valid_mask - - return bin_labels, bin_label_weights - - -def binary_cross_entropy(pred, - label, - weight=None, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=255): - """Calculate the binary CrossEntropy loss. - - Args: - pred (torch.Tensor): The prediction with shape (N, 1). - label (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (int | None): The label index to be ignored. Default: 255 - - Returns: - torch.Tensor: The calculated loss - """ - if pred.dim() != label.dim(): - assert (pred.dim() == 2 and label.dim() == 1) or ( - pred.dim() == 4 and label.dim() == 3), \ - 'Only pred shape [N, C], label shape [N] or pred shape [N, C, ' \ - 'H, W], label shape [N, H, W] are supported' - label, weight = _expand_onehot_labels(label, weight, pred.shape, - ignore_index) - - # weighted element-wise losses - if weight is not None: - weight = weight.float() - loss = F.binary_cross_entropy_with_logits( - pred, label.float(), pos_weight=class_weight, reduction='none') - # do the reduction for the weighted loss - loss = weight_reduce_loss( - loss, weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def mask_cross_entropy(pred, - target, - label, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=None): - """Calculate the CrossEntropy loss for masks. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. - label (torch.Tensor): ``label`` indicates the class label of the mask' - corresponding object. This will be used to select the mask in the - of the class which the object belongs to when the mask prediction - if not class-agnostic. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (None): Placeholder, to be consistent with other loss. - Default: None. - - Returns: - torch.Tensor: The calculated loss - """ - assert ignore_index is None, 'BCE loss does not support ignore_index' - # TODO: handle these two reserved arguments - assert reduction == 'mean' and avg_factor is None - num_rois = pred.size()[0] - inds = torch.arange(0, num_rois, dtype=torch.long, device=pred.device) - pred_slice = pred[inds, label].squeeze(1) - return F.binary_cross_entropy_with_logits( - pred_slice, target, weight=class_weight, reduction='mean')[None] - - -@LOSSES.register_module() -class CrossEntropyLoss(nn.Module): - """CrossEntropyLoss. - - Args: - use_sigmoid (bool, optional): Whether the prediction uses sigmoid - of softmax. Defaults to False. - use_mask (bool, optional): Whether to use mask cross entropy loss. - Defaults to False. - reduction (str, optional): . Defaults to 'mean'. - Options are "none", "mean" and "sum". - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - """ - - def __init__(self, - use_sigmoid=False, - use_mask=False, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_ce'): - super(CrossEntropyLoss, self).__init__() - assert (use_sigmoid is False) or (use_mask is False) - self.use_sigmoid = use_sigmoid - self.use_mask = use_mask - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = get_class_weight(class_weight) - - if self.use_sigmoid: - self.cls_criterion = binary_cross_entropy - elif self.use_mask: - self.cls_criterion = mask_cross_entropy - else: - self.cls_criterion = cross_entropy - self._loss_name = loss_name - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - """Forward function.""" - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = cls_score.new_tensor(self.class_weight) - else: - class_weight = None - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - weight, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/utils.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/utils.py deleted file mode 100755 index c37875fad..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/losses/utils.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import mmcv -import numpy as np -import torch.nn.functional as F - - -def get_class_weight(class_weight): - """Get class weight for loss function. - - Args: - class_weight (list[float] | str | None): If class_weight is a str, - take it as a file name and read from it. - """ - if isinstance(class_weight, str): - # take it as a file path - if class_weight.endswith('.npy'): - class_weight = np.load(class_weight) - else: - # pkl, json or yaml - class_weight = mmcv.load(class_weight) - - return class_weight - - -def reduce_loss(loss, reduction): - """Reduce loss as specified. - - Args: - loss (Tensor): Elementwise loss tensor. - reduction (str): Options are "none", "mean" and "sum". - - Return: - Tensor: Reduced loss tensor. - """ - reduction_enum = F._Reduction.get_enum(reduction) - # none: 0, elementwise_mean:1, sum: 2 - if reduction_enum == 0: - return loss - elif reduction_enum == 1: - return loss.mean() - elif reduction_enum == 2: - return loss.sum() - - -def weight_reduce_loss(loss, weight=None, reduction='mean', avg_factor=None): - """Apply element-wise weight and reduce loss. - - Args: - loss (Tensor): Element-wise loss. - weight (Tensor): Element-wise weights. - reduction (str): Same as built-in losses of PyTorch. - avg_factor (float): Average factor when computing the mean of losses. - - Returns: - Tensor: Processed loss values. - """ - # if weight is specified, apply element-wise weight - if weight is not None: - assert weight.dim() == loss.dim() - if weight.dim() > 1: - assert weight.size(1) == 1 or weight.size(1) == loss.size(1) - loss = loss * weight - - # if avg_factor is not specified, just reduce the loss - if avg_factor is None: - loss = reduce_loss(loss, reduction) - else: - # if reduction is mean, then average the loss by avg_factor - if reduction == 'mean': - loss = loss.sum() / avg_factor - # if reduction is 'none', then do nothing, otherwise raise an error - elif reduction != 'none': - raise ValueError('avg_factor can not be used with reduction="sum"') - return loss - - -def weighted_loss(loss_func): - """Create a weighted version of a given loss function. - - To use this decorator, the loss function must have the signature like - `loss_func(pred, target, **kwargs)`. The function only needs to compute - element-wise loss without any reduction. This decorator will add weight - and reduction arguments to the function. The decorated function will have - the signature like `loss_func(pred, target, weight=None, reduction='mean', - avg_factor=None, **kwargs)`. - - :Example: - - >>> import torch - >>> @weighted_loss - >>> def l1_loss(pred, target): - >>> return (pred - target).abs() - - >>> pred = torch.Tensor([0, 2, 3]) - >>> target = torch.Tensor([1, 1, 1]) - >>> weight = torch.Tensor([1, 0, 1]) - - >>> l1_loss(pred, target) - tensor(1.3333) - >>> l1_loss(pred, target, weight) - tensor(1.) - >>> l1_loss(pred, target, reduction='none') - tensor([1., 1., 2.]) - >>> l1_loss(pred, target, weight, avg_factor=2) - tensor(1.5000) - """ - - @functools.wraps(loss_func) - def wrapper(pred, - target, - weight=None, - reduction='mean', - avg_factor=None, - **kwargs): - # get element-wise loss - loss = loss_func(pred, target, **kwargs) - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - return wrapper diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/__init__.py deleted file mode 100755 index aba73f165..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .fpn import FPN -from .ic_neck import ICNeck -from .jpu import JPU -from .mla_neck import MLANeck -from .multilevel_neck import MultiLevelNeck - -__all__ = ['FPN', 'MultiLevelNeck', 'MLANeck', 'ICNeck', 'JPU'] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/fpn.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/fpn.py deleted file mode 100755 index 975a48e8b..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/fpn.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule, auto_fp16 - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class FPN(BaseModule): - """Feature Pyramid Network. - - This neck is the implementation of `Feature Pyramid Networks for Object - Detection `_. - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale) - num_outs (int): Number of output scales. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - add_extra_convs (bool | str): If bool, it decides whether to add conv - layers on top of the original feature maps. Default to False. - If True, its actual mode is specified by `extra_convs_on_inputs`. - If str, it specifies the source feature map of the extra convs. - Only the following options are allowed - - - 'on_input': Last feat map of neck inputs (i.e. backbone feature). - - 'on_lateral': Last feature map after lateral convs. - - 'on_output': The last output feature map after fpn convs. - extra_convs_on_inputs (bool, deprecated): Whether to apply extra convs - on the original feature from the backbone. If True, - it is equivalent to `add_extra_convs='on_input'`. If False, it is - equivalent to set `add_extra_convs='on_output'`. Default to True. - relu_before_extra_convs (bool): Whether to apply relu before the extra - conv. Default: False. - no_norm_on_lateral (bool): Whether to apply norm on lateral. - Default: False. - conv_cfg (dict): Config dict for convolution layer. Default: None. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (str): Config dict for activation layer in ConvModule. - Default: None. - upsample_cfg (dict): Config dict for interpolate layer. - Default: `dict(mode='nearest')` - init_cfg (dict or list[dict], optional): Initialization config dict. - - Example: - >>> import torch - >>> in_channels = [2, 3, 5, 7] - >>> scales = [340, 170, 84, 43] - >>> inputs = [torch.rand(1, c, s, s) - ... for c, s in zip(in_channels, scales)] - >>> self = FPN(in_channels, 11, len(in_channels)).eval() - >>> outputs = self.forward(inputs) - >>> for i in range(len(outputs)): - ... print(f'outputs[{i}].shape = {outputs[i].shape}') - outputs[0].shape = torch.Size([1, 11, 340, 340]) - outputs[1].shape = torch.Size([1, 11, 170, 170]) - outputs[2].shape = torch.Size([1, 11, 84, 84]) - outputs[3].shape = torch.Size([1, 11, 43, 43]) - """ - - def __init__(self, - in_channels, - out_channels, - num_outs, - start_level=0, - end_level=-1, - add_extra_convs=False, - extra_convs_on_inputs=False, - relu_before_extra_convs=False, - no_norm_on_lateral=False, - conv_cfg=None, - norm_cfg=None, - act_cfg=None, - upsample_cfg=dict(mode='nearest'), - init_cfg=dict( - type='Xavier', layer='Conv2d', distribution='uniform')): - super(FPN, self).__init__(init_cfg) - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.num_ins = len(in_channels) - self.num_outs = num_outs - self.relu_before_extra_convs = relu_before_extra_convs - self.no_norm_on_lateral = no_norm_on_lateral - self.fp16_enabled = False - self.upsample_cfg = upsample_cfg.copy() - - if end_level == -1: - self.backbone_end_level = self.num_ins - assert num_outs >= self.num_ins - start_level - else: - # if end_level < inputs, no extra level is allowed - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - assert num_outs == end_level - start_level - self.start_level = start_level - self.end_level = end_level - self.add_extra_convs = add_extra_convs - assert isinstance(add_extra_convs, (str, bool)) - if isinstance(add_extra_convs, str): - # Extra_convs_source choices: 'on_input', 'on_lateral', 'on_output' - assert add_extra_convs in ('on_input', 'on_lateral', 'on_output') - elif add_extra_convs: # True - if extra_convs_on_inputs: - # For compatibility with previous release - # TODO: deprecate `extra_convs_on_inputs` - self.add_extra_convs = 'on_input' - else: - self.add_extra_convs = 'on_output' - - self.lateral_convs = nn.ModuleList() - self.fpn_convs = nn.ModuleList() - - for i in range(self.start_level, self.backbone_end_level): - l_conv = ConvModule( - in_channels[i], - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg if not self.no_norm_on_lateral else None, - act_cfg=act_cfg, - inplace=False) - fpn_conv = ConvModule( - out_channels, - out_channels, - 3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - - self.lateral_convs.append(l_conv) - self.fpn_convs.append(fpn_conv) - - # add extra conv layers (e.g., RetinaNet) - extra_levels = num_outs - self.backbone_end_level + self.start_level - if self.add_extra_convs and extra_levels >= 1: - for i in range(extra_levels): - if i == 0 and self.add_extra_convs == 'on_input': - in_channels = self.in_channels[self.backbone_end_level - 1] - else: - in_channels = out_channels - extra_fpn_conv = ConvModule( - in_channels, - out_channels, - 3, - stride=2, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - self.fpn_convs.append(extra_fpn_conv) - - @auto_fp16() - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # build laterals - laterals = [ - lateral_conv(inputs[i + self.start_level]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - - # build top-down path - used_backbone_levels = len(laterals) - for i in range(used_backbone_levels - 1, 0, -1): - # In some cases, fixing `scale factor` (e.g. 2) is preferred, but - # it cannot co-exist with `size` in `F.interpolate`. - if 'scale_factor' in self.upsample_cfg: - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], **self.upsample_cfg) - else: - prev_shape = laterals[i - 1].shape[2:] - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], size=prev_shape, **self.upsample_cfg) - - # build outputs - # part 1: from original levels - outs = [ - self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels) - ] - # part 2: add extra levels - if self.num_outs > len(outs): - # use max pool to get more levels on top of outputs - # (e.g., Faster R-CNN, Mask R-CNN) - if not self.add_extra_convs: - for i in range(self.num_outs - used_backbone_levels): - outs.append(F.max_pool2d(outs[-1], 1, stride=2)) - # add conv layers on top of original feature maps (RetinaNet) - else: - if self.add_extra_convs == 'on_input': - extra_source = inputs[self.backbone_end_level - 1] - elif self.add_extra_convs == 'on_lateral': - extra_source = laterals[-1] - elif self.add_extra_convs == 'on_output': - extra_source = outs[-1] - else: - raise NotImplementedError - outs.append(self.fpn_convs[used_backbone_levels](extra_source)) - for i in range(used_backbone_levels + 1, self.num_outs): - if self.relu_before_extra_convs: - outs.append(self.fpn_convs[i](F.relu(outs[-1]))) - else: - outs.append(self.fpn_convs[i](outs[-1])) - return tuple(outs) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/ic_neck.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/ic_neck.py deleted file mode 100755 index d836a6b9c..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/ic_neck.py +++ /dev/null @@ -1,147 +0,0 @@ -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -class CascadeFeatureFusion(BaseModule): - """Cascade Feature Fusion Unit in ICNet. - - Args: - low_channels (int): The number of input channels for - low resolution feature map. - high_channels (int): The number of input channels for - high resolution feature map. - out_channels (int): The number of output channels. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Returns: - x (Tensor): The output tensor of shape (N, out_channels, H, W). - x_low (Tensor): The output tensor of shape (N, out_channels, H, W) - for Cascade Label Guidance in auxiliary heads. - """ - - def __init__(self, - low_channels, - high_channels, - out_channels, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(CascadeFeatureFusion, self).__init__(init_cfg=init_cfg) - self.align_corners = align_corners - self.conv_low = ConvModule( - low_channels, - out_channels, - 3, - padding=2, - dilation=2, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.conv_high = ConvModule( - high_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, x_low, x_high): - x_low = resize( - x_low, - size=x_high.size()[2:], - mode='bilinear', - align_corners=self.align_corners) - # Note: Different from original paper, `x_low` is underwent - # `self.conv_low` rather than another 1x1 conv classifier - # before being used for auxiliary head. - x_low = self.conv_low(x_low) - x_high = self.conv_high(x_high) - x = x_low + x_high - x = F.relu(x, inplace=True) - return x, x_low - - -@NECKS.register_module() -class ICNeck(BaseModule): - """ICNet for Real-Time Semantic Segmentation on High-Resolution Images. - - This head is the implementation of `ICHead - `_. - - Args: - in_channels (int): The number of input image channels. Default: 3. - out_channels (int): The numbers of output feature channels. - Default: 128. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(64, 256, 256), - out_channels=128, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(ICNeck, self).__init__(init_cfg=init_cfg) - assert len(in_channels) == 3, 'Length of input channels \ - must be 3!' - - self.in_channels = in_channels - self.out_channels = out_channels - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.align_corners = align_corners - self.cff_24 = CascadeFeatureFusion( - self.in_channels[2], - self.in_channels[1], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - self.cff_12 = CascadeFeatureFusion( - self.out_channels, - self.in_channels[0], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - def forward(self, inputs): - assert len(inputs) == 3, 'Length of input feature \ - maps must be 3!' - - x_sub1, x_sub2, x_sub4 = inputs - x_cff_24, x_24 = self.cff_24(x_sub4, x_sub2) - x_cff_12, x_12 = self.cff_12(x_cff_24, x_sub1) - # Note: `x_cff_12` is used for decode_head, - # `x_24` and `x_12` are used for auxiliary head. - return x_24, x_12, x_cff_12 diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/jpu.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/jpu.py deleted file mode 100755 index 3cc6b9f42..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/jpu.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, DepthwiseSeparableConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class JPU(BaseModule): - """FastFCN: Rethinking Dilated Convolution in the Backbone - for Semantic Segmentation. - - This Joint Pyramid Upsampling (JPU) neck is the implementation of - `FastFCN `_. - - Args: - in_channels (Tuple[int], optional): The number of input channels - for each convolution operations before upsampling. - Default: (512, 1024, 2048). - mid_channels (int): The number of output channels of JPU. - Default: 512. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - dilations (tuple[int]): Dilation rate of each Depthwise - Separable ConvModule. Default: (1, 2, 4, 8). - align_corners (bool, optional): The align_corners argument of - resize operation. Default: False. - conv_cfg (dict | None): Config of conv layers. - Default: None. - norm_cfg (dict | None): Config of norm layers. - Default: dict(type='BN'). - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU'). - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(512, 1024, 2048), - mid_channels=512, - start_level=0, - end_level=-1, - dilations=(1, 2, 4, 8), - align_corners=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(JPU, self).__init__(init_cfg=init_cfg) - assert isinstance(in_channels, tuple) - assert isinstance(dilations, tuple) - self.in_channels = in_channels - self.mid_channels = mid_channels - self.start_level = start_level - self.num_ins = len(in_channels) - if end_level == -1: - self.backbone_end_level = self.num_ins - else: - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - - self.dilations = dilations - self.align_corners = align_corners - - self.conv_layers = nn.ModuleList() - self.dilation_layers = nn.ModuleList() - for i in range(self.start_level, self.backbone_end_level): - conv_layer = nn.Sequential( - ConvModule( - self.in_channels[i], - self.mid_channels, - kernel_size=3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.conv_layers.append(conv_layer) - for i in range(len(dilations)): - dilation_layer = nn.Sequential( - DepthwiseSeparableConvModule( - in_channels=(self.backbone_end_level - self.start_level) * - self.mid_channels, - out_channels=self.mid_channels, - kernel_size=3, - stride=1, - padding=dilations[i], - dilation=dilations[i], - dw_norm_cfg=norm_cfg, - dw_act_cfg=None, - pw_norm_cfg=norm_cfg, - pw_act_cfg=act_cfg)) - self.dilation_layers.append(dilation_layer) - - def forward(self, inputs): - """Forward function.""" - assert len(inputs) == len(self.in_channels), 'Length of inputs must \ - be the same with self.in_channels!' - - feats = [ - self.conv_layers[i - self.start_level](inputs[i]) - for i in range(self.start_level, self.backbone_end_level) - ] - - h, w = feats[0].shape[2:] - for i in range(1, len(feats)): - feats[i] = resize( - feats[i], - size=(h, w), - mode='bilinear', - align_corners=self.align_corners) - - feat = torch.cat(feats, dim=1) - concat_feat = torch.cat([ - self.dilation_layers[i](feat) for i in range(len(self.dilations)) - ], - dim=1) - - outs = [] - - # Default: outs[2] is the output of JPU for decoder head, outs[1] is - # the feature map from backbone for auxiliary head. Additionally, - # outs[0] can also be used for auxiliary head. - for i in range(self.start_level, self.backbone_end_level - 1): - outs.append(inputs[i]) - outs.append(concat_feat) - return tuple(outs) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/mla_neck.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/mla_neck.py deleted file mode 100755 index 1513e296d..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/mla_neck.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, build_norm_layer - -from ..builder import NECKS - - -class MLAModule(nn.Module): - - def __init__(self, - in_channels=[1024, 1024, 1024, 1024], - out_channels=256, - norm_cfg=None, - act_cfg=None): - super(MLAModule, self).__init__() - self.channel_proj = nn.ModuleList() - for i in range(len(in_channels)): - self.channel_proj.append( - ConvModule( - in_channels=in_channels[i], - out_channels=out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.feat_extract = nn.ModuleList() - for i in range(len(in_channels)): - self.feat_extract.append( - ConvModule( - in_channels=out_channels, - out_channels=out_channels, - kernel_size=3, - padding=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, inputs): - - # feat_list -> [p2, p3, p4, p5] - feat_list = [] - for x, conv in zip(inputs, self.channel_proj): - feat_list.append(conv(x)) - - # feat_list -> [p5, p4, p3, p2] - # mid_list -> [m5, m4, m3, m2] - feat_list = feat_list[::-1] - mid_list = [] - for feat in feat_list: - if len(mid_list) == 0: - mid_list.append(feat) - else: - mid_list.append(mid_list[-1] + feat) - - # mid_list -> [m5, m4, m3, m2] - # out_list -> [o2, o3, o4, o5] - out_list = [] - for mid, conv in zip(mid_list, self.feat_extract): - out_list.append(conv(mid)) - - return tuple(out_list) - - -@NECKS.register_module() -class MLANeck(nn.Module): - """Multi-level Feature Aggregation. - - This neck is `The Multi-level Feature Aggregation construction of - SETR `_. - - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - norm_layer (dict): Config dict for input normalization. - Default: norm_layer=dict(type='LN', eps=1e-6, requires_grad=True). - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - norm_layer=dict(type='LN', eps=1e-6, requires_grad=True), - norm_cfg=None, - act_cfg=None): - super(MLANeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - - # In order to build general vision transformer backbone, we have to - # move MLA to neck. - self.norm = nn.ModuleList([ - build_norm_layer(norm_layer, in_channels[i])[1] - for i in range(len(in_channels)) - ]) - - self.mla = MLAModule( - in_channels=in_channels, - out_channels=out_channels, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # Convert from nchw to nlc - outs = [] - for i in range(len(inputs)): - x = inputs[i] - n, c, h, w = x.shape - x = x.reshape(n, c, h * w).transpose(2, 1).contiguous() - x = self.norm[i](x) - x = x.transpose(1, 2).reshape(n, c, h, w).contiguous() - outs.append(x) - - outs = self.mla(outs) - return tuple(outs) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/multilevel_neck.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/multilevel_neck.py deleted file mode 100755 index 5151f8762..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/necks/multilevel_neck.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, xavier_init - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class MultiLevelNeck(nn.Module): - """MultiLevelNeck. - - A neck structure connect vit backbone and decoder_heads. - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - scales (List[float]): Scale factors for each input feature map. - Default: [0.5, 1, 2, 4] - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - scales=[0.5, 1, 2, 4], - norm_cfg=None, - act_cfg=None): - super(MultiLevelNeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.scales = scales - self.num_outs = len(scales) - self.lateral_convs = nn.ModuleList() - self.convs = nn.ModuleList() - for in_channel in in_channels: - self.lateral_convs.append( - ConvModule( - in_channel, - out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - for _ in range(self.num_outs): - self.convs.append( - ConvModule( - out_channels, - out_channels, - kernel_size=3, - padding=1, - stride=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - # default init_weights for conv(msra) and norm in ConvModule - def init_weights(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - xavier_init(m, distribution='uniform') - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - inputs = [ - lateral_conv(inputs[i]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - # for len(inputs) not equal to self.num_outs - if len(inputs) == 1: - inputs = [inputs[0] for _ in range(self.num_outs)] - outs = [] - for i in range(self.num_outs): - x_resize = resize( - inputs[i], scale_factor=self.scales[i], mode='bilinear') - outs.append(self.convs[i](x_resize)) - return tuple(outs) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/segmentors/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/segmentors/__init__.py deleted file mode 100755 index 8dcb8632a..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/segmentors/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from .base import BaseSegmentor -# from .cascade_encoder_decoder import CascadeEncoderDecoder -from .encoder_decoder import EncoderDecoder - -# __all__ = ['BaseSegmentor', 'EncoderDecoder', 'CascadeEncoderDecoder'] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/segmentors/base.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/segmentors/base.py deleted file mode 100755 index f0f320ffb..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/segmentors/base.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from abc import ABCMeta, abstractmethod -from collections import OrderedDict - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import BaseModule, auto_fp16 - - -class BaseSegmentor(BaseModule, metaclass=ABCMeta): - """Base class for segmentors.""" - - def __init__(self, init_cfg=None): - super(BaseSegmentor, self).__init__(init_cfg) - self.fp16_enabled = False - - @property - def with_neck(self): - """bool: whether the segmentor has neck""" - return hasattr(self, 'neck') and self.neck is not None - - @property - def with_auxiliary_head(self): - """bool: whether the segmentor has auxiliary head""" - return hasattr(self, - 'auxiliary_head') and self.auxiliary_head is not None - - @property - def with_decode_head(self): - """bool: whether the segmentor has decode head""" - return hasattr(self, 'decode_head') and self.decode_head is not None - - @abstractmethod - def extract_feat(self, imgs): - """Placeholder for extract features from images.""" - pass - - @abstractmethod - def encode_decode(self, img, img_metas): - """Placeholder for encode images with backbone and decode into a - semantic segmentation map of the same size as input.""" - pass - - @abstractmethod - def forward_train(self, imgs, img_metas, **kwargs): - """Placeholder for Forward function for training.""" - pass - - @abstractmethod - def simple_test(self, img, img_meta, **kwargs): - """Placeholder for single image test.""" - pass - - @abstractmethod - def aug_test(self, imgs, img_metas, **kwargs): - """Placeholder for augmentation test.""" - pass - - def forward_test(self, imgs, img_metas, **kwargs): - """ - Args: - imgs (List[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains all images in the batch. - img_metas (List[List[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. - """ - for var, name in [(imgs, 'imgs'), (img_metas, 'img_metas')]: - if not isinstance(var, list): - raise TypeError(f'{name} must be a list, but got ' - f'{type(var)}') - - num_augs = len(imgs) - if num_augs != len(img_metas): - raise ValueError(f'num of augmentations ({len(imgs)}) != ' - f'num of image meta ({len(img_metas)})') - # all images in the same aug batch all of the same ori_shape and pad - # shape - for img_meta in img_metas: - ori_shapes = [_['ori_shape'] for _ in img_meta] - assert all(shape == ori_shapes[0] for shape in ori_shapes) - img_shapes = [_['img_shape'] for _ in img_meta] - assert all(shape == img_shapes[0] for shape in img_shapes) - pad_shapes = [_['pad_shape'] for _ in img_meta] - assert all(shape == pad_shapes[0] for shape in pad_shapes) - - if num_augs == 1: - return self.simple_test(imgs[0], img_metas[0], **kwargs) - else: - return self.aug_test(imgs, img_metas, **kwargs) - - @auto_fp16(apply_to=('img', )) - def forward(self, img, img_metas, return_loss=True, **kwargs): - """Calls either :func:`forward_train` or :func:`forward_test` depending - on whether ``return_loss`` is ``True``. - - Note this setting will change the expected inputs. When - ``return_loss=True``, img and img_meta are single-nested (i.e. Tensor - and List[dict]), and when ``resturn_loss=False``, img and img_meta - should be double nested (i.e. List[Tensor], List[List[dict]]), with - the outer list indicating test time augmentations. - """ - if return_loss: - return self.forward_train(img, img_metas, **kwargs) - else: - return self.forward_test(img, img_metas, **kwargs) - - def train_step(self, data_batch, optimizer, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating is also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer` | dict): The optimizer of - runner is passed to ``train_step()``. This argument is unused - and reserved. - - Returns: - dict: It should contain at least 3 keys: ``loss``, ``log_vars``, - ``num_samples``. - ``loss`` is a tensor for back propagation, which can be a - weighted sum of multiple losses. - ``log_vars`` contains all the variables to be sent to the - logger. - ``num_samples`` indicates the batch size (when the model is - DDP, it means the batch size on each GPU), which is used for - averaging the logs. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, - log_vars=log_vars, - num_samples=len(data_batch['img_metas'])) - - return outputs - - def val_step(self, data_batch, optimizer=None, **kwargs): - """The iteration step during validation. - - This method shares the same signature as :func:`train_step`, but used - during val epochs. Note that the evaluation after training epochs is - not implemented with this method, but an evaluation hook. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, - log_vars=log_vars, - num_samples=len(data_batch['img_metas'])) - - return outputs - - @staticmethod - def _parse_losses(losses): - """Parse the raw outputs (losses) of the network. - - Args: - losses (dict): Raw output of the network, which usually contain - losses and other necessary information. - - Returns: - tuple[Tensor, dict]: (loss, log_vars), loss is the loss tensor - which may be a weighted sum of all losses, log_vars contains - all the variables to be sent to the logger. - """ - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(_value for _key, _value in log_vars.items() - if 'loss' in _key) - - # If the loss_vars has different length, raise assertion error - # to prevent GPUs from infinite waiting. - if dist.is_available() and dist.is_initialized(): - log_var_length = torch.tensor(len(log_vars), device=loss.device) - dist.all_reduce(log_var_length) - message = (f'rank {dist.get_rank()}' + - f' len(log_vars): {len(log_vars)}' + ' keys: ' + - ','.join(log_vars.keys()) + '\n') - assert log_var_length == len(log_vars) * dist.get_world_size(), \ - 'loss log variables are different across GPUs!\n' + message - - log_vars['loss'] = loss - for loss_name, loss_value in log_vars.items(): - # reduce loss when distributed training - if dist.is_available() and dist.is_initialized(): - loss_value = loss_value.data.clone() - dist.all_reduce(loss_value.div_(dist.get_world_size())) - log_vars[loss_name] = loss_value.item() - - return loss, log_vars - - def show_result(self, - img, - result, - palette=None, - win_name='', - show=False, - wait_time=0, - out_file=None, - opacity=0.5): - """Draw `result` over `img`. - - Args: - img (str or Tensor): The image to be displayed. - result (Tensor): The semantic segmentation results to draw over - `img`. - palette (list[list[int]]] | np.ndarray | None): The palette of - segmentation map. If None is given, random palette will be - generated. Default: None - win_name (str): The window name. - wait_time (int): Value of waitKey param. - Default: 0. - show (bool): Whether to show the image. - Default: False. - out_file (str or None): The filename to write the image. - Default: None. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - Returns: - img (Tensor): Only if not `show` or `out_file` - """ - img = mmcv.imread(img) - img = img.copy() - seg = result[0] - if palette is None: - if self.PALETTE is None: - palette = np.random.randint( - 0, 255, size=(len(self.CLASSES), 3)) - else: - palette = self.PALETTE - palette = np.array(palette) - assert palette.shape[0] == len(self.CLASSES) - assert palette.shape[1] == 3 - assert len(palette.shape) == 2 - assert 0 < opacity <= 1.0 - color_seg = np.zeros((seg.shape[0], seg.shape[1], 3), dtype=np.uint8) - for label, color in enumerate(palette): - color_seg[seg == label, :] = color - # convert to BGR - color_seg = color_seg[..., ::-1] - - img = img * (1 - opacity) + color_seg * opacity - img = img.astype(np.uint8) - # if out_file specified, do not show image in window - if out_file is not None: - show = False - - if show: - mmcv.imshow(img, win_name, wait_time) - if out_file is not None: - mmcv.imwrite(img, out_file) - - if not (show or out_file): - warnings.warn('show==False and out_file is not specified, only ' - 'result image will be returned') - return img diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/segmentors/encoder_decoder.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/segmentors/encoder_decoder.py deleted file mode 100755 index 72467b469..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/segmentors/encoder_decoder.py +++ /dev/null @@ -1,284 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmseg.core import add_prefix -from mmseg.ops import resize -from .. import builder -from ..builder import SEGMENTORS -from .base import BaseSegmentor - - -@SEGMENTORS.register_module() -class EncoderDecoder(BaseSegmentor): - """Encoder Decoder segmentors. - - EncoderDecoder typically consists of backbone, decode_head, auxiliary_head. - Note that auxiliary_head is only used for deep supervision during training, - which could be dumped during inference. - """ - - def __init__(self, - backbone, - decode_head, - neck=None, - auxiliary_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - super(EncoderDecoder, self).__init__(init_cfg) - if pretrained is not None: - assert backbone.get('pretrained') is None, \ - 'both backbone and segmentor set pretrained weight' - backbone.pretrained = pretrained - self.backbone = builder.build_backbone(backbone) - if neck is not None: - self.neck = builder.build_neck(neck) - self._init_decode_head(decode_head) - self._init_auxiliary_head(auxiliary_head) - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - assert self.with_decode_head - - def _init_decode_head(self, decode_head): - """Initialize ``decode_head``""" - self.decode_head = builder.build_head(decode_head) - self.align_corners = self.decode_head.align_corners - self.num_classes = self.decode_head.num_classes - - def _init_auxiliary_head(self, auxiliary_head): - """Initialize ``auxiliary_head``""" - if auxiliary_head is not None: - if isinstance(auxiliary_head, list): - self.auxiliary_head = nn.ModuleList() - for head_cfg in auxiliary_head: - self.auxiliary_head.append(builder.build_head(head_cfg)) - else: - self.auxiliary_head = builder.build_head(auxiliary_head) - - def extract_feat(self, img): - """Extract features from images.""" - x = self.backbone(img) - if self.with_neck: - x = self.neck(x) - return x - - def encode_decode(self, img, img_metas): - """Encode images with backbone and decode into a semantic segmentation - map of the same size as input.""" - x = self.extract_feat(img) - out = self._decode_head_forward_test(x, img_metas) - out = resize( - input=out, - size=img.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - return out - - def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for decode head in - training.""" - losses = dict() - loss_decode = self.decode_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - - losses.update(add_prefix(loss_decode, 'decode')) - return losses - - def _decode_head_forward_test(self, x, img_metas): - """Run forward function and calculate loss for decode head in - inference.""" - seg_logits = self.decode_head.forward_test(x, img_metas, self.test_cfg) - return seg_logits - - def _auxiliary_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for auxiliary head in - training.""" - losses = dict() - if isinstance(self.auxiliary_head, nn.ModuleList): - for idx, aux_head in enumerate(self.auxiliary_head): - loss_aux = aux_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - losses.update(add_prefix(loss_aux, f'aux_{idx}')) - else: - loss_aux = self.auxiliary_head.forward_train( - x, img_metas, gt_semantic_seg, self.train_cfg) - losses.update(add_prefix(loss_aux, 'aux')) - - return losses - - def forward_dummy(self, img): - """Dummy forward function.""" - seg_logit = self.encode_decode(img, None) - - return seg_logit - - def forward_train(self, img, img_metas, gt_semantic_seg): - """Forward function for training. - - Args: - img (Tensor): Input images. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - - x = self.extract_feat(img) - - losses = dict() - - loss_decode = self._decode_head_forward_train(x, img_metas, - gt_semantic_seg) - losses.update(loss_decode) - - if self.with_auxiliary_head: - loss_aux = self._auxiliary_head_forward_train( - x, img_metas, gt_semantic_seg) - losses.update(loss_aux) - - return losses - - # TODO refactor - def slide_inference(self, img, img_meta, rescale): - """Inference by sliding-window with overlap. - - If h_crop > h_img or w_crop > w_img, the small patch will be used to - decode without padding. - """ - - h_stride, w_stride = self.test_cfg.stride - h_crop, w_crop = self.test_cfg.crop_size - batch_size, _, h_img, w_img = img.size() - num_classes = self.num_classes - h_grids = max(h_img - h_crop + h_stride - 1, 0) // h_stride + 1 - w_grids = max(w_img - w_crop + w_stride - 1, 0) // w_stride + 1 - preds = img.new_zeros((batch_size, num_classes, h_img, w_img)) - count_mat = img.new_zeros((batch_size, 1, h_img, w_img)) - for h_idx in range(h_grids): - for w_idx in range(w_grids): - y1 = h_idx * h_stride - x1 = w_idx * w_stride - y2 = min(y1 + h_crop, h_img) - x2 = min(x1 + w_crop, w_img) - y1 = max(y2 - h_crop, 0) - x1 = max(x2 - w_crop, 0) - crop_img = img[:, :, y1:y2, x1:x2] - crop_seg_logit = self.encode_decode(crop_img, img_meta) - preds += F.pad(crop_seg_logit, - (int(x1), int(preds.shape[3] - x2), int(y1), - int(preds.shape[2] - y2))) - - count_mat[:, :, y1:y2, x1:x2] += 1 - assert (count_mat == 0).sum() == 0 - if torch.onnx.is_in_onnx_export(): - # cast count_mat to constant while exporting to ONNX - count_mat = torch.from_numpy( - count_mat.cpu().detach().numpy()).to(device=img.device) - preds = preds / count_mat - if rescale: - preds = resize( - preds, - size=img_meta[0]['ori_shape'][:2], - mode='bilinear', - align_corners=self.align_corners, - warning=False) - return preds - - def whole_inference(self, img, img_meta, rescale): - """Inference with full image.""" - - seg_logit = self.encode_decode(img, img_meta) - if rescale: - # support dynamic shape for onnx - if torch.onnx.is_in_onnx_export(): - size = img.shape[2:] - else: - size = img_meta[0]['ori_shape'][:2] - seg_logit = resize( - seg_logit, - size=size, - mode='bilinear', - align_corners=self.align_corners, - warning=False) - - return seg_logit - - def inference(self, img, img_meta, rescale): - """Inference with slide/whole style. - - Args: - img (Tensor): The input image of shape (N, 3, H, W). - img_meta (dict): Image info dict where each dict has: 'img_shape', - 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - rescale (bool): Whether rescale back to original shape. - - Returns: - Tensor: The output segmentation map. - """ - - assert self.test_cfg.mode in ['slide', 'whole'] - ori_shape = img_meta[0]['ori_shape'] - assert all(_['ori_shape'] == ori_shape for _ in img_meta) - if self.test_cfg.mode == 'slide': - seg_logit = self.slide_inference(img, img_meta, rescale) - else: - seg_logit = self.whole_inference(img, img_meta, rescale) - output = F.softmax(seg_logit, dim=1) - flip = img_meta[0]['flip'] - if flip: - flip_direction = img_meta[0]['flip_direction'] - assert flip_direction in ['horizontal', 'vertical'] - if flip_direction == 'horizontal': - output = output.flip(dims=(3, )) - elif flip_direction == 'vertical': - output = output.flip(dims=(2, )) - - return output - - def simple_test(self, img, img_meta, rescale=True): - """Simple test with single image.""" - seg_logit = self.inference(img, img_meta, rescale) - seg_pred = seg_logit.argmax(dim=1) - if torch.onnx.is_in_onnx_export(): - # our inference backend only support 4D output - seg_pred = seg_pred.unsqueeze(0) - return seg_pred - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred - - def aug_test(self, imgs, img_metas, rescale=True): - """Test with augmentations. - - Only rescale=True is supported. - """ - # aug_test rescale all imgs back to ori_shape for now - assert rescale - # to save memory, we get augmented seg logit inplace - seg_logit = self.inference(imgs[0], img_metas[0], rescale) - for i in range(1, len(imgs)): - cur_seg_logit = self.inference(imgs[i], img_metas[i], rescale) - seg_logit += cur_seg_logit - seg_logit /= len(imgs) - seg_pred = seg_logit.argmax(dim=1) - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/__init__.py deleted file mode 100755 index 2417c5183..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -from .embed import PatchEmbed -from .inverted_residual import InvertedResidual, InvertedResidualV3 -from .make_divisible import make_divisible -from .res_layer import ResLayer -from .se_layer import SELayer -from .self_attention_block import SelfAttentionBlock -from .shape_convert import nchw_to_nlc, nlc_to_nchw -from .up_conv_block import UpConvBlock - -__all__ = [ - 'ResLayer', 'SelfAttentionBlock', 'make_divisible', 'InvertedResidual', - 'UpConvBlock', 'InvertedResidualV3', 'SELayer', 'PatchEmbed', - 'nchw_to_nlc', 'nlc_to_nchw' -] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/embed.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/embed.py deleted file mode 100755 index 1515675e1..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/embed.py +++ /dev/null @@ -1,330 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -from typing import Sequence - -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner.base_module import BaseModule -from mmcv.utils import to_2tuple - - -class AdaptivePadding(nn.Module): - """Applies padding to input (if needed) so that input can get fully covered - by filter you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad zero around - input. The "corner" mode would pad zero to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel: - stride (int | tuple): Stride of the filter. Default: 1: - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - - super(AdaptivePadding, self).__init__() - - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The config dict for embedding - conv layer type selection. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int, optional): The slide stride of embedding conv. - Default: None (Would be set as `kernel_size`). - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only work when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=None, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super(PatchEmbed, self).__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adap_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adap_padding: - pad_h, pad_w = self.adap_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adap_padding: - x = self.adap_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map. Our implementation uses `nn.Unfold` to - merge patch, which is about 25% faster than original implementation. - Instead, we need to modify pretrained models for compatibility. - - Args: - in_channels (int): The num of input channels. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adap_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - - if self.adap_padding: - x = self.adap_padding(x) - H, W = x.shape[-2:] - - x = self.sampler(x) - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/inverted_residual.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/inverted_residual.py deleted file mode 100755 index c9cda7682..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/inverted_residual.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import ConvModule -from torch import nn -from torch.utils import checkpoint as cp - -from .se_layer import SELayer - - -class InvertedResidual(nn.Module): - """InvertedResidual block for MobileNetV2. - - Args: - in_channels (int): The input channels of the InvertedResidual block. - out_channels (int): The output channels of the InvertedResidual block. - stride (int): Stride of the middle (first) 3x3 convolution. - expand_ratio (int): Adjusts number of channels of the hidden layer - in InvertedResidual by this amount. - dilation (int): Dilation rate of depthwise conv. Default: 1 - conv_cfg (dict): Config dict for convolution layer. - Default: None, which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU6'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - stride, - expand_ratio, - dilation=1, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU6'), - with_cp=False, - **kwargs): - super(InvertedResidual, self).__init__() - self.stride = stride - assert stride in [1, 2], f'stride must in [1, 2]. ' \ - f'But received {stride}.' - self.with_cp = with_cp - self.use_res_connect = self.stride == 1 and in_channels == out_channels - hidden_dim = int(round(in_channels * expand_ratio)) - - layers = [] - if expand_ratio != 1: - layers.append( - ConvModule( - in_channels=in_channels, - out_channels=hidden_dim, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs)) - layers.extend([ - ConvModule( - in_channels=hidden_dim, - out_channels=hidden_dim, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - groups=hidden_dim, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs), - ConvModule( - in_channels=hidden_dim, - out_channels=out_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None, - **kwargs) - ]) - self.conv = nn.Sequential(*layers) - - def forward(self, x): - - def _inner_forward(x): - if self.use_res_connect: - return x + self.conv(x) - else: - return self.conv(x) - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out - - -class InvertedResidualV3(nn.Module): - """Inverted Residual Block for MobileNetV3. - - Args: - in_channels (int): The input channels of this Module. - out_channels (int): The output channels of this Module. - mid_channels (int): The input channels of the depthwise convolution. - kernel_size (int): The kernel size of the depthwise convolution. - Default: 3. - stride (int): The stride of the depthwise convolution. Default: 1. - se_cfg (dict): Config dict for se layer. Default: None, which means no - se layer. - with_expand_conv (bool): Use expand conv or not. If set False, - mid_channels must be the same with in_channels. Default: True. - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - mid_channels, - kernel_size=3, - stride=1, - se_cfg=None, - with_expand_conv=True, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - with_cp=False): - super(InvertedResidualV3, self).__init__() - self.with_res_shortcut = (stride == 1 and in_channels == out_channels) - assert stride in [1, 2] - self.with_cp = with_cp - self.with_se = se_cfg is not None - self.with_expand_conv = with_expand_conv - - if self.with_se: - assert isinstance(se_cfg, dict) - if not self.with_expand_conv: - assert mid_channels == in_channels - - if self.with_expand_conv: - self.expand_conv = ConvModule( - in_channels=in_channels, - out_channels=mid_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.depthwise_conv = ConvModule( - in_channels=mid_channels, - out_channels=mid_channels, - kernel_size=kernel_size, - stride=stride, - padding=kernel_size // 2, - groups=mid_channels, - conv_cfg=dict( - type='Conv2dAdaptivePadding') if stride == 2 else conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - if self.with_se: - self.se = SELayer(**se_cfg) - - self.linear_conv = ConvModule( - in_channels=mid_channels, - out_channels=out_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - def forward(self, x): - - def _inner_forward(x): - out = x - - if self.with_expand_conv: - out = self.expand_conv(out) - - out = self.depthwise_conv(out) - - if self.with_se: - out = self.se(out) - - out = self.linear_conv(out) - - if self.with_res_shortcut: - return x + out - else: - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/make_divisible.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/make_divisible.py deleted file mode 100755 index ed42c2eee..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/make_divisible.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def make_divisible(value, divisor, min_value=None, min_ratio=0.9): - """Make divisible function. - - This function rounds the channel number to the nearest value that can be - divisible by the divisor. It is taken from the original tf repo. It ensures - that all layers have a channel number that is divisible by divisor. It can - be seen here: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py # noqa - - Args: - value (int): The original channel number. - divisor (int): The divisor to fully divide the channel number. - min_value (int): The minimum value of the output channel. - Default: None, means that the minimum value equal to the divisor. - min_ratio (float): The minimum ratio of the rounded channel number to - the original channel number. Default: 0.9. - - Returns: - int: The modified output channel number. - """ - - if min_value is None: - min_value = divisor - new_value = max(min_value, int(value + divisor / 2) // divisor * divisor) - # Make sure that round down does not go down by more than (1-min_ratio). - if new_value < min_ratio * value: - new_value += divisor - return new_value diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/res_layer.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/res_layer.py deleted file mode 100755 index 190a0c5d5..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/res_layer.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner import Sequential -from torch import nn as nn - - -class ResLayer(Sequential): - """ResLayer to build ResNet style backbone. - - Args: - block (nn.Module): block used to build ResLayer. - inplanes (int): inplanes of block. - planes (int): planes of block. - num_blocks (int): number of blocks. - stride (int): stride of the first block. Default: 1 - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False - conv_cfg (dict): dictionary to construct and config conv layer. - Default: None - norm_cfg (dict): dictionary to construct and config norm layer. - Default: dict(type='BN') - multi_grid (int | None): Multi grid dilation rates of last - stage. Default: None - contract_dilation (bool): Whether contract first dilation of each layer - Default: False - """ - - def __init__(self, - block, - inplanes, - planes, - num_blocks, - stride=1, - dilation=1, - avg_down=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - multi_grid=None, - contract_dilation=False, - **kwargs): - self.block = block - - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = [] - conv_stride = stride - if avg_down: - conv_stride = 1 - downsample.append( - nn.AvgPool2d( - kernel_size=stride, - stride=stride, - ceil_mode=True, - count_include_pad=False)) - downsample.extend([ - build_conv_layer( - conv_cfg, - inplanes, - planes * block.expansion, - kernel_size=1, - stride=conv_stride, - bias=False), - build_norm_layer(norm_cfg, planes * block.expansion)[1] - ]) - downsample = nn.Sequential(*downsample) - - layers = [] - if multi_grid is None: - if dilation > 1 and contract_dilation: - first_dilation = dilation // 2 - else: - first_dilation = dilation - else: - first_dilation = multi_grid[0] - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=stride, - dilation=first_dilation, - downsample=downsample, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - inplanes = planes * block.expansion - for i in range(1, num_blocks): - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=1, - dilation=dilation if multi_grid is None else multi_grid[i], - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - super(ResLayer, self).__init__(*layers) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/se_layer.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/se_layer.py deleted file mode 100755 index 16f52aa5c..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/se_layer.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch.nn as nn -from mmcv.cnn import ConvModule - -from .make_divisible import make_divisible - - -class SELayer(nn.Module): - """Squeeze-and-Excitation Module. - - Args: - channels (int): The input (and output) channels of the SE layer. - ratio (int): Squeeze ratio in SELayer, the intermediate channel will be - ``int(channels/ratio)``. Default: 16. - conv_cfg (None or dict): Config dict for convolution layer. - Default: None, which means using conv2d. - act_cfg (dict or Sequence[dict]): Config dict for activation layer. - If act_cfg is a dict, two activation layers will be configured - by this dict. If act_cfg is a sequence of dicts, the first - activation layer will be configured by the first dict and the - second activation layer will be configured by the second dict. - Default: (dict(type='ReLU'), dict(type='HSigmoid', bias=3.0, - divisor=6.0)). - """ - - def __init__(self, - channels, - ratio=16, - conv_cfg=None, - act_cfg=(dict(type='ReLU'), - dict(type='HSigmoid', bias=3.0, divisor=6.0))): - super(SELayer, self).__init__() - if isinstance(act_cfg, dict): - act_cfg = (act_cfg, act_cfg) - assert len(act_cfg) == 2 - assert mmcv.is_tuple_of(act_cfg, dict) - self.global_avgpool = nn.AdaptiveAvgPool2d(1) - self.conv1 = ConvModule( - in_channels=channels, - out_channels=make_divisible(channels // ratio, 8), - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[0]) - self.conv2 = ConvModule( - in_channels=make_divisible(channels // ratio, 8), - out_channels=channels, - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[1]) - - def forward(self, x): - out = self.global_avgpool(x) - out = self.conv1(out) - out = self.conv2(out) - return x * out diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/self_attention_block.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/self_attention_block.py deleted file mode 100755 index c945fa716..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/self_attention_block.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.cnn import ConvModule, constant_init -from torch import nn as nn -from torch.nn import functional as F - - -class SelfAttentionBlock(nn.Module): - """General self-attention block/non-local block. - - Please refer to https://arxiv.org/abs/1706.03762 for details about key, - query and value. - - Args: - key_in_channels (int): Input channels of key feature. - query_in_channels (int): Input channels of query feature. - channels (int): Output channels of key/query transform. - out_channels (int): Output channels. - share_key_query (bool): Whether share projection weight between key - and query projection. - query_downsample (nn.Module): Query downsample module. - key_downsample (nn.Module): Key downsample module. - key_query_num_convs (int): Number of convs for key/query projection. - value_num_convs (int): Number of convs for value projection. - matmul_norm (bool): Whether normalize attention map with sqrt of - channels - with_out (bool): Whether use out projection. - conv_cfg (dict|None): Config of conv layers. - norm_cfg (dict|None): Config of norm layers. - act_cfg (dict|None): Config of activation layers. - """ - - def __init__(self, key_in_channels, query_in_channels, channels, - out_channels, share_key_query, query_downsample, - key_downsample, key_query_num_convs, value_out_num_convs, - key_query_norm, value_out_norm, matmul_norm, with_out, - conv_cfg, norm_cfg, act_cfg): - super(SelfAttentionBlock, self).__init__() - if share_key_query: - assert key_in_channels == query_in_channels - self.key_in_channels = key_in_channels - self.query_in_channels = query_in_channels - self.out_channels = out_channels - self.channels = channels - self.share_key_query = share_key_query - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.key_project = self.build_project( - key_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if share_key_query: - self.query_project = self.key_project - else: - self.query_project = self.build_project( - query_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.value_project = self.build_project( - key_in_channels, - channels if with_out else out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if with_out: - self.out_project = self.build_project( - channels, - out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.out_project = None - - self.query_downsample = query_downsample - self.key_downsample = key_downsample - self.matmul_norm = matmul_norm - - self.init_weights() - - def init_weights(self): - """Initialize weight of later layer.""" - if self.out_project is not None: - if not isinstance(self.out_project, ConvModule): - constant_init(self.out_project, 0) - - def build_project(self, in_channels, channels, num_convs, use_conv_module, - conv_cfg, norm_cfg, act_cfg): - """Build projection layer for key/query/value/out.""" - if use_conv_module: - convs = [ - ConvModule( - in_channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - ] - for _ in range(num_convs - 1): - convs.append( - ConvModule( - channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - else: - convs = [nn.Conv2d(in_channels, channels, 1)] - for _ in range(num_convs - 1): - convs.append(nn.Conv2d(channels, channels, 1)) - if len(convs) > 1: - convs = nn.Sequential(*convs) - else: - convs = convs[0] - return convs - - def forward(self, query_feats, key_feats): - """Forward function.""" - batch_size = query_feats.size(0) - query = self.query_project(query_feats) - if self.query_downsample is not None: - query = self.query_downsample(query) - query = query.reshape(*query.shape[:2], -1) - query = query.permute(0, 2, 1).contiguous() - - key = self.key_project(key_feats) - value = self.value_project(key_feats) - if self.key_downsample is not None: - key = self.key_downsample(key) - value = self.key_downsample(value) - key = key.reshape(*key.shape[:2], -1) - value = value.reshape(*value.shape[:2], -1) - value = value.permute(0, 2, 1).contiguous() - - sim_map = torch.matmul(query, key) - if self.matmul_norm: - sim_map = (self.channels**-.5) * sim_map - sim_map = F.softmax(sim_map, dim=-1) - - context = torch.matmul(sim_map, value) - context = context.permute(0, 2, 1).contiguous() - context = context.reshape(batch_size, -1, *query_feats.shape[2:]) - if self.out_project is not None: - context = self.out_project(context) - return context diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/shape_convert.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/shape_convert.py deleted file mode 100755 index 0677348c8..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/shape_convert.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def nlc_to_nchw(x, hw_shape): - """Convert [N, L, C] shape tensor to [N, C, H, W] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, L, C] before conversion. - hw_shape (Sequence[int]): The height and width of output feature map. - - Returns: - Tensor: The output tensor of shape [N, C, H, W] after conversion. - """ - H, W = hw_shape - assert len(x.shape) == 3 - B, L, C = x.shape - assert L == H * W, 'The seq_len doesn\'t match H, W' - return x.transpose(1, 2).reshape(B, C, H, W) - - -def nchw_to_nlc(x): - """Flatten [N, C, H, W] shape tensor to [N, L, C] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, C, H, W] before conversion. - - Returns: - Tensor: The output tensor of shape [N, L, C] after conversion. - """ - assert len(x.shape) == 4 - return x.flatten(2).transpose(1, 2).contiguous() diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/up_conv_block.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/up_conv_block.py deleted file mode 100755 index d8396d9c2..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/models/utils/up_conv_block.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, build_upsample_layer - - -class UpConvBlock(nn.Module): - """Upsample convolution block in decoder for UNet. - - This upsample convolution block consists of one upsample module - followed by one convolution block. The upsample module expands the - high-level low-resolution feature map and the convolution block fuses - the upsampled high-level low-resolution feature map and the low-level - high-resolution feature map from encoder. - - Args: - conv_block (nn.Sequential): Sequential of convolutional layers. - in_channels (int): Number of input channels of the high-level - skip_channels (int): Number of input channels of the low-level - high-resolution feature map from encoder. - out_channels (int): Number of output channels. - num_convs (int): Number of convolutional layers in the conv_block. - Default: 2. - stride (int): Stride of convolutional layer in conv_block. Default: 1. - dilation (int): Dilation rate of convolutional layer in conv_block. - Default: 1. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - conv_cfg (dict | None): Config dict for convolution layer. - Default: None. - norm_cfg (dict | None): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict | None): Config dict for activation layer in ConvModule. - Default: dict(type='ReLU'). - upsample_cfg (dict): The upsample config of the upsample module in - decoder. Default: dict(type='InterpConv'). If the size of - high-level feature map is the same as that of skip feature map - (low-level feature map from encoder), it does not need upsample the - high-level feature map and the upsample_cfg is None. - dcn (bool): Use deformable convolution in convolutional layer or not. - Default: None. - plugins (dict): plugins for convolutional layers. Default: None. - """ - - def __init__(self, - conv_block, - in_channels, - skip_channels, - out_channels, - num_convs=2, - stride=1, - dilation=1, - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - upsample_cfg=dict(type='InterpConv'), - dcn=None, - plugins=None): - super(UpConvBlock, self).__init__() - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.conv_block = conv_block( - in_channels=2 * skip_channels, - out_channels=out_channels, - num_convs=num_convs, - stride=stride, - dilation=dilation, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - dcn=None, - plugins=None) - if upsample_cfg is not None: - self.upsample = build_upsample_layer( - cfg=upsample_cfg, - in_channels=in_channels, - out_channels=skip_channels, - with_cp=with_cp, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.upsample = ConvModule( - in_channels, - skip_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, skip, x): - """Forward function.""" - - x = self.upsample(x) - out = torch.cat([skip, x], dim=1) - out = self.conv_block(out) - - return out diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/ops/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/ops/__init__.py deleted file mode 100755 index bc075cd4e..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/ops/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .encoding import Encoding -from .wrappers import Upsample, resize - -__all__ = ['Upsample', 'resize', 'Encoding'] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/ops/encoding.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/ops/encoding.py deleted file mode 100755 index f397cc54e..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/ops/encoding.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn -from torch.nn import functional as F - - -class Encoding(nn.Module): - """Encoding Layer: a learnable residual encoder. - - Input is of shape (batch_size, channels, height, width). - Output is of shape (batch_size, num_codes, channels). - - Args: - channels: dimension of the features or feature channels - num_codes: number of code words - """ - - def __init__(self, channels, num_codes): - super(Encoding, self).__init__() - # init codewords and smoothing factor - self.channels, self.num_codes = channels, num_codes - std = 1. / ((num_codes * channels)**0.5) - # [num_codes, channels] - self.codewords = nn.Parameter( - torch.empty(num_codes, channels, - dtype=torch.float).uniform_(-std, std), - requires_grad=True) - # [num_codes] - self.scale = nn.Parameter( - torch.empty(num_codes, dtype=torch.float).uniform_(-1, 0), - requires_grad=True) - - @staticmethod - def scaled_l2(x, codewords, scale): - num_codes, channels = codewords.size() - batch_size = x.size(0) - reshaped_scale = scale.view((1, 1, num_codes)) - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - - scaled_l2_norm = reshaped_scale * ( - expanded_x - reshaped_codewords).pow(2).sum(dim=3) - return scaled_l2_norm - - @staticmethod - def aggregate(assignment_weights, x, codewords): - num_codes, channels = codewords.size() - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - batch_size = x.size(0) - - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - encoded_feat = (assignment_weights.unsqueeze(3) * - (expanded_x - reshaped_codewords)).sum(dim=1) - return encoded_feat - - def forward(self, x): - assert x.dim() == 4 and x.size(1) == self.channels - # [batch_size, channels, height, width] - batch_size = x.size(0) - # [batch_size, height x width, channels] - x = x.view(batch_size, self.channels, -1).transpose(1, 2).contiguous() - # assignment_weights: [batch_size, channels, num_codes] - assignment_weights = F.softmax( - self.scaled_l2(x, self.codewords, self.scale), dim=2) - # aggregate - encoded_feat = self.aggregate(assignment_weights, x, self.codewords) - return encoded_feat - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(Nx{self.channels}xHxW =>Nx{self.num_codes}' \ - f'x{self.channels})' - return repr_str diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/ops/wrappers.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/ops/wrappers.py deleted file mode 100755 index ce67e4beb..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/ops/wrappers.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.nn.functional as F - - -def resize(input, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None, - warning=True): - if warning: - if size is not None and align_corners: - input_h, input_w = tuple(int(x) for x in input.shape[2:]) - output_h, output_w = tuple(int(x) for x in size) - if output_h > input_h or output_w > output_h: - if ((output_h > 1 and output_w > 1 and input_h > 1 - and input_w > 1) and (output_h - 1) % (input_h - 1) - and (output_w - 1) % (input_w - 1)): - warnings.warn( - f'When align_corners={align_corners}, ' - 'the output would more aligned if ' - f'input size {(input_h, input_w)} is `x+1` and ' - f'out size {(output_h, output_w)} is `nx+1`') - return F.interpolate(input, size, scale_factor, mode, align_corners) - - -class Upsample(nn.Module): - - def __init__(self, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None): - super(Upsample, self).__init__() - self.size = size - if isinstance(scale_factor, tuple): - self.scale_factor = tuple(float(factor) for factor in scale_factor) - else: - self.scale_factor = float(scale_factor) if scale_factor else None - self.mode = mode - self.align_corners = align_corners - - def forward(self, x): - if not self.size: - size = [int(t * self.scale_factor) for t in x.shape[-2:]] - else: - size = self.size - return resize(x, size, None, self.mode, self.align_corners) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/utils/__init__.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/utils/__init__.py deleted file mode 100755 index 3f1558052..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/utils/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collect_env import collect_env -from .logger import get_root_logger - -__all__ = ['get_root_logger', 'collect_env'] diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/utils/collect_env.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/utils/collect_env.py deleted file mode 100755 index 3379ecb06..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/utils/collect_env.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash - -import mmseg - - -def collect_env(): - """Collect the information of the running environments.""" - env_info = collect_base_env() - env_info['MMSegmentation'] = f'{mmseg.__version__}+{get_git_hash()[:7]}' - - return env_info - - -if __name__ == '__main__': - for name, val in collect_env().items(): - print('{}: {}'.format(name, val)) diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/utils/logger.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/utils/logger.py deleted file mode 100755 index 0cb3c78d6..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/utils/logger.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -from mmcv.utils import get_logger - - -def get_root_logger(log_file=None, log_level=logging.INFO): - """Get the root logger. - - The logger will be initialized if it has not been initialized. By default a - StreamHandler will be added. If `log_file` is specified, a FileHandler will - also be added. The name of the root logger is the top-level package name, - e.g., "mmseg". - - Args: - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the root logger. - log_level (int): The root logger level. Note that only the process of - rank 0 is affected, while other processes will set the level to - "Error" and be silent most of the time. - - Returns: - logging.Logger: The root logger. - """ - - logger = get_logger(name='mmseg', log_file=log_file, log_level=log_level) - - return logger diff --git a/cv/semantic_segmentation/gcnet/pytorch/mmseg/version.py b/cv/semantic_segmentation/gcnet/pytorch/mmseg/version.py deleted file mode 100755 index c5db4851f..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/mmseg/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) Open-MMLab. All rights reserved. - -__version__ = '0.20.2' - - -def parse_version_info(version_str): - version_info = [] - for x in version_str.split('.'): - if x.isdigit(): - version_info.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - version_info.append(int(patch_version[0])) - version_info.append(f'rc{patch_version[1]}') - return tuple(version_info) - - -version_info = parse_version_info(__version__) diff --git a/cv/semantic_segmentation/gcnet/pytorch/requirements.txt b/cv/semantic_segmentation/gcnet/pytorch/requirements.txt deleted file mode 100755 index f003d1db6..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ --r requirements/optional.txt --r requirements/runtime.txt --r requirements/tests.txt -addict -yapf \ No newline at end of file diff --git a/cv/semantic_segmentation/gcnet/pytorch/requirements/docs.txt b/cv/semantic_segmentation/gcnet/pytorch/requirements/docs.txt deleted file mode 100755 index 20170845c..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/requirements/docs.txt +++ /dev/null @@ -1,6 +0,0 @@ -docutils==0.16.0 -myst-parser --e git+https://github.com/gaotongxiao/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme -sphinx==4.0.2 -sphinx_copybutton -sphinx_markdown_tables diff --git a/cv/semantic_segmentation/gcnet/pytorch/requirements/mminstall.txt b/cv/semantic_segmentation/gcnet/pytorch/requirements/mminstall.txt deleted file mode 100755 index b1c42eb46..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/requirements/mminstall.txt +++ /dev/null @@ -1 +0,0 @@ -mmcv-full>=1.3.1,<=1.4.0 diff --git a/cv/semantic_segmentation/gcnet/pytorch/requirements/optional.txt b/cv/semantic_segmentation/gcnet/pytorch/requirements/optional.txt deleted file mode 100755 index 47fa59331..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/requirements/optional.txt +++ /dev/null @@ -1 +0,0 @@ -cityscapesscripts diff --git a/cv/semantic_segmentation/gcnet/pytorch/requirements/readthedocs.txt b/cv/semantic_segmentation/gcnet/pytorch/requirements/readthedocs.txt deleted file mode 100755 index 22a894bd7..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/requirements/readthedocs.txt +++ /dev/null @@ -1,4 +0,0 @@ -mmcv -prettytable -torch -torchvision diff --git a/cv/semantic_segmentation/gcnet/pytorch/requirements/runtime.txt b/cv/semantic_segmentation/gcnet/pytorch/requirements/runtime.txt deleted file mode 100755 index 2712f504c..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/requirements/runtime.txt +++ /dev/null @@ -1,4 +0,0 @@ -matplotlib -numpy -packaging -prettytable diff --git a/cv/semantic_segmentation/gcnet/pytorch/requirements/tests.txt b/cv/semantic_segmentation/gcnet/pytorch/requirements/tests.txt deleted file mode 100755 index 991fd711d..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/requirements/tests.txt +++ /dev/null @@ -1,7 +0,0 @@ -codecov -flake8 -interrogate -isort==4.3.21 -pytest -xdoctest>=0.10.0 -yapf diff --git a/cv/semantic_segmentation/gcnet/pytorch/run_train.sh b/cv/semantic_segmentation/gcnet/pytorch/run_train.sh deleted file mode 100755 index d24e020e1..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/run_train.sh +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -python3 train.py configs/gcnet/gcnet_r50-d8_769x769_40k_cityscapes.py diff --git a/cv/semantic_segmentation/gcnet/pytorch/setup.py b/cv/semantic_segmentation/gcnet/pytorch/setup.py deleted file mode 100755 index e9ea12f3d..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/setup.py +++ /dev/null @@ -1,353 +0,0 @@ -import glob -import os -import re -from pkg_resources import DistributionNotFound, get_distribution -from setuptools import find_packages, setup - -EXT_TYPE = '' -try: - import torch - if torch.__version__ == 'parrots': - from parrots.utils.build_extension import BuildExtension - EXT_TYPE = 'parrots' - else: - from torch.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - cmd_class = {'build_ext': BuildExtension} -except ModuleNotFoundError: - cmd_class = {} - print('Skip building ext ops due to the absence of torch.') - - -def choose_requirement(primary, secondary): - """If some version of primary requirement installed, return primary, else - return secondary.""" - try: - name = re.split(r'[!<>=]', primary)[0] - get_distribution(name) - except DistributionNotFound: - return secondary - - return str(primary) - - -def get_version(): - version_file = 'mmcv/version.py' - with open(version_file, 'r', encoding='utf-8') as f: - exec(compile(f.read(), version_file, 'exec')) - version = locals()['__version__'] - local_version_identifier = os.environ.get('MMCV_LOCAL_VERSION_IDENTIFIER', '') - if local_version_identifier != '': - version += '+' + local_version_identifier - return version - - -def parse_requirements(fname='requirements/runtime.txt', with_version=True): - """Parse the package dependencies listed in a requirements file but strips - specific versioning information. - - Args: - fname (str): path to requirements file - with_version (bool, default=False): if True include version specs - - Returns: - List[str]: list of requirements items - - CommandLine: - python -c "import setup; print(setup.parse_requirements())" - """ - import sys - from os.path import exists - require_fpath = fname - - def parse_line(line): - """Parse information from a line in a requirements text file.""" - if line.startswith('-r '): - # Allow specifying requirements in other files - target = line.split(' ')[1] - for info in parse_require_file(target): - yield info - else: - info = {'line': line} - if line.startswith('-e '): - info['package'] = line.split('#egg=')[1] - else: - # Remove versioning from the package - pat = '(' + '|'.join(['>=', '==', '>']) + ')' - parts = re.split(pat, line, maxsplit=1) - parts = [p.strip() for p in parts] - - info['package'] = parts[0] - if len(parts) > 1: - op, rest = parts[1:] - if ';' in rest: - # Handle platform specific dependencies - # http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies - version, platform_deps = map(str.strip, - rest.split(';')) - info['platform_deps'] = platform_deps - else: - version = rest # NOQA - info['version'] = (op, version) - yield info - - def parse_require_file(fpath): - with open(fpath, 'r') as f: - for line in f.readlines(): - line = line.strip() - if line and not line.startswith('#'): - for info in parse_line(line): - yield info - - def gen_packages_items(): - if exists(require_fpath): - for info in parse_require_file(require_fpath): - parts = [info['package']] - if with_version and 'version' in info: - parts.extend(info['version']) - if not sys.version.startswith('3.4'): - # apparently package_deps are broken in 3.4 - platform_deps = info.get('platform_deps') - if platform_deps is not None: - parts.append(';' + platform_deps) - item = ''.join(parts) - yield item - - packages = list(gen_packages_items()) - return packages - - -install_requires = parse_requirements() - -try: - # OpenCV installed via conda. - import cv2 # NOQA: F401 - major, minor, *rest = cv2.__version__.split('.') - if int(major) < 3: - raise RuntimeError( - f'OpenCV >=3 is required but {cv2.__version__} is installed') -except ImportError: - # If first not installed install second package - CHOOSE_INSTALL_REQUIRES = [('opencv-python-headless>=3', - 'opencv-python>=3')] - for main, secondary in CHOOSE_INSTALL_REQUIRES: - install_requires.append(choose_requirement(main, secondary)) - - -def get_extensions(): - extensions = [] - - if os.getenv('MMCV_WITH_TRT', '0') != '0': - ext_name = 'mmcv._ext_trt' - from torch.utils.cpp_extension import include_paths, library_paths - library_dirs = [] - libraries = [] - include_dirs = [] - tensorrt_path = os.getenv('TENSORRT_DIR', '0') - tensorrt_lib_path = glob.glob( - os.path.join(tensorrt_path, 'targets', '*', 'lib'))[0] - library_dirs += [tensorrt_lib_path] - libraries += ['nvinfer', 'nvparsers', 'nvinfer_plugin'] - libraries += ['cudart'] - define_macros = [] - extra_compile_args = {'cxx': []} - - include_path = os.path.abspath('./mmcv/ops/csrc/common/cuda') - include_trt_path = os.path.abspath('./mmcv/ops/csrc/tensorrt') - include_dirs.append(include_path) - include_dirs.append(include_trt_path) - include_dirs.append(os.path.join(tensorrt_path, 'include')) - include_dirs += include_paths(cuda=True) - - op_files = glob.glob('./mmcv/ops/csrc/tensorrt/plugins/*') - define_macros += [('MMCV_WITH_CUDA', None)] - define_macros += [('MMCV_WITH_TRT', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - library_dirs += library_paths(cuda=True) - - from setuptools import Extension - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - language='c++', - library_dirs=library_dirs, - libraries=libraries) - extensions.append(ext_ops) - - if os.getenv('MMCV_WITH_OPS', '0') == '0': - return extensions - - if EXT_TYPE == 'parrots': - ext_name = 'mmcv._ext' - from parrots.utils.build_extension import Extension - # new parrots op impl do not use MMCV_USE_PARROTS - # define_macros = [('MMCV_USE_PARROTS', None)] - define_macros = [] - include_dirs = [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') +\ - glob.glob('./mmcv/ops/csrc/parrots/*.cpp') - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args = { - 'nvcc': [cuda_args] if cuda_args else [], - 'cxx': [], - } - if torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - extra_compile_args['nvcc'] += [ - '-D__CUDA_NO_HALF_OPERATORS__', - '-D__CUDA_NO_HALF_CONVERSIONS__', - '-D__CUDA_NO_HALF2_OPERATORS__', - ] - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - cuda=True, - pytorch=True) - extensions.append(ext_ops) - elif EXT_TYPE == 'pytorch': - ext_name = 'mmcv._ext' - from torch.utils.cpp_extension import CppExtension, CUDAExtension - - # prevent ninja from using too many resources - try: - import psutil - num_cpu = len(psutil.Process().cpu_affinity()) - cpu_use = max(4, num_cpu - 1) - except (ModuleNotFoundError, AttributeError): - cpu_use = 4 - - os.environ.setdefault('MAX_JOBS', str(cpu_use)) - define_macros = [] - extra_compile_args = {'cxx': []} - include_dirs = [] - - is_rocm_pytorch = False - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm_pytorch = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - - project_dir = 'mmcv/ops/csrc/' - if is_rocm_pytorch: - from torch.utils.hipify import hipify_python - - hipify_python.hipify( - project_directory=project_dir, - output_directory=project_dir, - includes='mmcv/ops/csrc/*', - show_detailed=True, - is_pytorch_extension=True, - ) - define_macros += [('MMCV_WITH_CUDA', None)] - define_macros += [('HIP_DIFF', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/hip/*') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/hip')) - elif torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - else: - print(f'Compiling {ext_name} without CUDA') - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') - extension = CppExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - - ext_ops = extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args) - extensions.append(ext_ops) - - if EXT_TYPE == 'pytorch' and os.getenv('MMCV_WITH_ORT', '0') != '0': - ext_name = 'mmcv._ext_ort' - from torch.utils.cpp_extension import library_paths, include_paths - import onnxruntime - library_dirs = [] - libraries = [] - include_dirs = [] - ort_path = os.getenv('ONNXRUNTIME_DIR', '0') - library_dirs += [os.path.join(ort_path, 'lib')] - libraries.append('onnxruntime') - define_macros = [] - extra_compile_args = {'cxx': []} - - include_path = os.path.abspath('./mmcv/ops/csrc/onnxruntime') - include_dirs.append(include_path) - include_dirs.append(os.path.join(ort_path, 'include')) - - op_files = glob.glob('./mmcv/ops/csrc/onnxruntime/cpu/*') - if onnxruntime.get_device() == 'GPU' or os.getenv('FORCE_CUDA', - '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files += glob.glob('./mmcv/ops/csrc/onnxruntime/gpu/*') - include_dirs += include_paths(cuda=True) - library_dirs += library_paths(cuda=True) - else: - include_dirs += include_paths(cuda=False) - library_dirs += library_paths(cuda=False) - - from setuptools import Extension - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - language='c++', - library_dirs=library_dirs, - libraries=libraries) - extensions.append(ext_ops) - - return extensions - - -setup( - name='mmcv' if os.getenv('MMCV_WITH_OPS', '0') == '0' else 'mmcv-full', - version=get_version(), - description='OpenMMLab Computer Vision Foundation', - keywords='computer vision', - packages=find_packages(), - include_package_data=True, - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Topic :: Utilities', - ], - url='https://github.com/open-mmlab/mmcv', - author='MMCV Contributors', - author_email='openmmlab@gmail.com', - setup_requires=[], - tests_require=['pytest'], - install_requires=install_requires, - ext_modules=get_extensions(), - cmdclass=cmd_class, - zip_safe=False) diff --git a/cv/semantic_segmentation/gcnet/pytorch/tools/analyze_logs.py b/cv/semantic_segmentation/gcnet/pytorch/tools/analyze_logs.py deleted file mode 100755 index 8c62a34fc..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/tools/analyze_logs.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/open- -mmlab/mmdetection/blob/master/tools/analysis_tools/analyze_logs.py.""" -import argparse -import json -from collections import defaultdict - -import matplotlib.pyplot as plt -import seaborn as sns - - -def plot_curve(log_dicts, args): - if args.backend is not None: - plt.switch_backend(args.backend) - sns.set_style(args.style) - # if legend is None, use {filename}_{key} as legend - legend = args.legend - if legend is None: - legend = [] - for json_log in args.json_logs: - for metric in args.keys: - legend.append(f'{json_log}_{metric}') - assert len(legend) == (len(args.json_logs) * len(args.keys)) - metrics = args.keys - - num_metrics = len(metrics) - for i, log_dict in enumerate(log_dicts): - epochs = list(log_dict.keys()) - for j, metric in enumerate(metrics): - print(f'plot curve of {args.json_logs[i]}, metric is {metric}') - plot_epochs = [] - plot_iters = [] - plot_values = [] - # In some log files, iters number is not correct, `pre_iter` is - # used to prevent generate wrong lines. - pre_iter = -1 - for epoch in epochs: - epoch_logs = log_dict[epoch] - if metric not in epoch_logs.keys(): - continue - if metric in ['mIoU', 'mAcc', 'aAcc']: - plot_epochs.append(epoch) - plot_values.append(epoch_logs[metric][0]) - else: - for idx in range(len(epoch_logs[metric])): - if pre_iter > epoch_logs['iter'][idx]: - continue - pre_iter = epoch_logs['iter'][idx] - plot_iters.append(epoch_logs['iter'][idx]) - plot_values.append(epoch_logs[metric][idx]) - ax = plt.gca() - label = legend[i * num_metrics + j] - if metric in ['mIoU', 'mAcc', 'aAcc']: - ax.set_xticks(plot_epochs) - plt.xlabel('epoch') - plt.plot(plot_epochs, plot_values, label=label, marker='o') - else: - plt.xlabel('iter') - plt.plot(plot_iters, plot_values, label=label, linewidth=0.5) - plt.legend() - if args.title is not None: - plt.title(args.title) - if args.out is None: - plt.show() - else: - print(f'save curve to: {args.out}') - plt.savefig(args.out) - plt.cla() - - -def parse_args(): - parser = argparse.ArgumentParser(description='Analyze Json Log') - parser.add_argument( - 'json_logs', - type=str, - nargs='+', - help='path of train log in json format') - parser.add_argument( - '--keys', - type=str, - nargs='+', - default=['mIoU'], - help='the metric that you want to plot') - parser.add_argument('--title', type=str, help='title of figure') - parser.add_argument( - '--legend', - type=str, - nargs='+', - default=None, - help='legend of each plot') - parser.add_argument( - '--backend', type=str, default=None, help='backend of plt') - parser.add_argument( - '--style', type=str, default='dark', help='style of plt') - parser.add_argument('--out', type=str, default=None) - args = parser.parse_args() - return args - - -def load_json_logs(json_logs): - # load and convert json_logs to log_dict, key is epoch, value is a sub dict - # keys of sub dict is different metrics - # value of sub dict is a list of corresponding values of all iterations - log_dicts = [dict() for _ in json_logs] - for json_log, log_dict in zip(json_logs, log_dicts): - with open(json_log, 'r') as log_file: - for line in log_file: - log = json.loads(line.strip()) - # skip lines without `epoch` field - if 'epoch' not in log: - continue - epoch = log.pop('epoch') - if epoch not in log_dict: - log_dict[epoch] = defaultdict(list) - for k, v in log.items(): - log_dict[epoch][k].append(v) - return log_dicts - - -def main(): - args = parse_args() - json_logs = args.json_logs - for json_log in json_logs: - assert json_log.endswith('.json') - log_dicts = load_json_logs(json_logs) - plot_curve(log_dicts, args) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/gcnet/pytorch/tools/benchmark.py b/cv/semantic_segmentation/gcnet/pytorch/tools/benchmark.py deleted file mode 100755 index d72980ebd..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/tools/benchmark.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import time - -import torch -from mmcv import Config -from mmcv.parallel import MMDataParallel -from mmcv.runner import load_checkpoint, wrap_fp16_model - -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='MMSeg benchmark a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--log-interval', type=int, default=50, help='interval of logging') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - # set cudnn_benchmark - torch.backends.cudnn.benchmark = False - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - data_loader = build_dataloader( - dataset, - samples_per_gpu=1, - workers_per_gpu=cfg.data.workers_per_gpu, - dist=False, - shuffle=False) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - load_checkpoint(model, args.checkpoint, map_location='cpu') - - model = MMDataParallel(model, device_ids=[0]) - - model.eval() - - # the first several iterations may be very slow so skip them - num_warmup = 5 - pure_inf_time = 0 - total_iters = 200 - - # benchmark with 200 image and take the average - for i, data in enumerate(data_loader): - - torch.cuda.synchronize() - start_time = time.perf_counter() - - with torch.no_grad(): - model(return_loss=False, rescale=True, **data) - - torch.cuda.synchronize() - elapsed = time.perf_counter() - start_time - - if i >= num_warmup: - pure_inf_time += elapsed - if (i + 1) % args.log_interval == 0: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Done image [{i + 1:<3}/ {total_iters}], ' - f'fps: {fps:.2f} img / s') - - if (i + 1) == total_iters: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Overall fps: {fps:.2f} img / s') - break - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/gcnet/pytorch/tools/convert_datasets/cityscapes.py b/cv/semantic_segmentation/gcnet/pytorch/tools/convert_datasets/cityscapes.py deleted file mode 100755 index 17b616847..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/tools/convert_datasets/cityscapes.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp - -import mmcv -from cityscapesscripts.preparation.json2labelImg import json2labelImg - - -def convert_json_to_label(json_file): - label_file = json_file.replace('_polygons.json', '_labelTrainIds.png') - json2labelImg(json_file, label_file, 'trainIds') - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert Cityscapes annotations to TrainIds') - parser.add_argument('cityscapes_path', help='cityscapes data path') - parser.add_argument('--gt-dir', default='gtFine', type=str) - parser.add_argument('-o', '--out-dir', help='output path') - parser.add_argument( - '--nproc', default=1, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - cityscapes_path = args.cityscapes_path - out_dir = args.out_dir if args.out_dir else cityscapes_path - mmcv.mkdir_or_exist(out_dir) - - gt_dir = osp.join(cityscapes_path, args.gt_dir) - - poly_files = [] - for poly in mmcv.scandir(gt_dir, '_polygons.json', recursive=True): - poly_file = osp.join(gt_dir, poly) - poly_files.append(poly_file) - if args.nproc > 1: - mmcv.track_parallel_progress(convert_json_to_label, poly_files, - args.nproc) - else: - mmcv.track_progress(convert_json_to_label, poly_files) - - split_names = ['train', 'val', 'test'] - - for split in split_names: - filenames = [] - for poly in mmcv.scandir( - osp.join(gt_dir, split), '_polygons.json', recursive=True): - filenames.append(poly.replace('_gtFine_polygons.json', '')) - with open(osp.join(out_dir, f'{split}.txt'), 'w') as f: - f.writelines(f + '\n' for f in filenames) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/gcnet/pytorch/tools/get_flops.py b/cv/semantic_segmentation/gcnet/pytorch/tools/get_flops.py deleted file mode 100755 index 83dea0a03..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/tools/get_flops.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse - -from mmcv import Config -from mmcv.cnn import get_model_complexity_info - -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument( - '--shape', - type=int, - nargs='+', - default=[2048, 1024], - help='input image size') - args = parser.parse_args() - return args - - -def main(): - - args = parse_args() - - if len(args.shape) == 1: - input_shape = (3, args.shape[0], args.shape[0]) - elif len(args.shape) == 2: - input_shape = (3, ) + tuple(args.shape) - else: - raise ValueError('invalid input shape') - - cfg = Config.fromfile(args.config) - cfg.model.pretrained = None - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')).cuda() - model.eval() - - if hasattr(model, 'forward_dummy'): - model.forward = model.forward_dummy - else: - raise NotImplementedError( - 'FLOPs counter is currently not currently supported with {}'. - format(model.__class__.__name__)) - - flops, params = get_model_complexity_info(model, input_shape) - split_line = '=' * 30 - print('{0}\nInput shape: {1}\nFlops: {2}\nParams: {3}\n{0}'.format( - split_line, input_shape, flops, params)) - print('!!!Please be cautious if you use the results in papers. ' - 'You may need to check if all ops are supported and verify that the ' - 'flops computation is correct.') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/gcnet/pytorch/tools/print_config.py b/cv/semantic_segmentation/gcnet/pytorch/tools/print_config.py deleted file mode 100755 index 3f9c08dd9..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/tools/print_config.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import warnings - -from mmcv import Config, DictAction - -from mmseg.apis import init_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='Print the whole config') - parser.add_argument('config', help='config file path') - parser.add_argument( - '--graph', action='store_true', help='print the models graph') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - args = parser.parse_args() - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options, ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - print(f'Config:\n{cfg.pretty_text}') - # dump config - cfg.dump('example.py') - # dump models graph - if args.graph: - model = init_segmentor(args.config, device='cpu') - print(f'Model graph:\n{str(model)}') - with open('example-graph.txt', 'w') as f: - f.writelines(str(model)) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/gcnet/pytorch/tools/test.py b/cv/semantic_segmentation/gcnet/pytorch/tools/test.py deleted file mode 100755 index b04727413..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/tools/test.py +++ /dev/null @@ -1,259 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os -import os.path as osp -import shutil -import time -import warnings - -import mmcv -import torch -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (get_dist_info, init_dist, load_checkpoint, - wrap_fp16_model) -from mmcv.utils import DictAction - -from mmseg.apis import multi_gpu_test, single_gpu_test -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser( - description='mmseg test (and eval) a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--work-dir', - help=('if specified, the evaluation metric results will be dumped' - 'into the directory as json')) - parser.add_argument( - '--aug-test', action='store_true', help='Use Flip and Multi scale aug') - parser.add_argument('--out', help='output result file in pickle format') - parser.add_argument( - '--format-only', - action='store_true', - help='Format the output results without perform evaluation. It is' - 'useful when you want to format the result to a specific format and ' - 'submit it to the test server') - parser.add_argument( - '--eval', - type=str, - nargs='+', - help='evaluation metrics, which depends on the dataset, e.g., "mIoU"' - ' for generic datasets, and "cityscapes" for Cityscapes') - parser.add_argument('--show', action='store_true', help='show results') - parser.add_argument( - '--show-dir', help='directory where painted images will be saved') - parser.add_argument( - '--gpu-collect', - action='store_true', - help='whether to use gpu to collect results.') - parser.add_argument( - '--tmpdir', - help='tmp directory used for collecting results from multiple ' - 'workers, available when gpu_collect is not specified') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--eval-options', - nargs='+', - action=DictAction, - help='custom options for evaluation') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument( - '--opacity', - type=float, - default=0.5, - help='Opacity of painted segmentation map. In (0, 1] range.') - parser.add_argument('--local_rank', type=int, default=0) - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - assert args.out or args.eval or args.format_only or args.show \ - or args.show_dir, \ - ('Please specify at least one operation (save/eval/format/show the ' - 'results / save the results) with the argument "--out", "--eval"' - ', "--format-only", "--show" or "--show-dir"') - - if args.eval and args.format_only: - raise ValueError('--eval and --format_only cannot be both specified') - - if args.out is not None and not args.out.endswith(('.pkl', '.pickle')): - raise ValueError('The output file must be a pkl file.') - - cfg = mmcv.Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - if args.aug_test: - # hard code index - cfg.data.test.pipeline[1].img_ratios = [ - 0.5, 0.75, 1.0, 1.25, 1.5, 1.75 - ] - cfg.data.test.pipeline[1].flip = True - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - - rank, _ = get_dist_info() - # allows not to create - if args.work_dir is not None and rank == 0: - mmcv.mkdir_or_exist(osp.abspath(args.work_dir)) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - json_file = osp.join(args.work_dir, f'eval_{timestamp}.json') - - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - data_loader = build_dataloader( - dataset, - samples_per_gpu=1, - workers_per_gpu=cfg.data.workers_per_gpu, - dist=distributed, - shuffle=False) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - checkpoint = load_checkpoint(model, args.checkpoint, map_location='cpu') - if 'CLASSES' in checkpoint.get('meta', {}): - model.CLASSES = checkpoint['meta']['CLASSES'] - else: - print('"CLASSES" not found in meta, use dataset.CLASSES instead') - model.CLASSES = dataset.CLASSES - if 'PALETTE' in checkpoint.get('meta', {}): - model.PALETTE = checkpoint['meta']['PALETTE'] - else: - print('"PALETTE" not found in meta, use dataset.PALETTE instead') - model.PALETTE = dataset.PALETTE - - # clean gpu memory when starting a new evaluation. - torch.cuda.empty_cache() - eval_kwargs = {} if args.eval_options is None else args.eval_options - - # Deprecated - efficient_test = eval_kwargs.get('efficient_test', False) - if efficient_test: - warnings.warn( - '``efficient_test=True`` does not have effect in tools/test.py, ' - 'the evaluation and format results are CPU memory efficient by ' - 'default') - - eval_on_format_results = ( - args.eval is not None and 'cityscapes' in args.eval) - if eval_on_format_results: - assert len(args.eval) == 1, 'eval on format results is not ' \ - 'applicable for metrics other than ' \ - 'cityscapes' - if args.format_only or eval_on_format_results: - if 'imgfile_prefix' in eval_kwargs: - tmpdir = eval_kwargs['imgfile_prefix'] - else: - tmpdir = '.format_cityscapes' - eval_kwargs.setdefault('imgfile_prefix', tmpdir) - mmcv.mkdir_or_exist(tmpdir) - else: - tmpdir = None - - if not distributed: - model = MMDataParallel(model, device_ids=[0]) - results = single_gpu_test( - model, - data_loader, - args.show, - args.show_dir, - False, - args.opacity, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - else: - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False) - results = multi_gpu_test( - model, - data_loader, - args.tmpdir, - args.gpu_collect, - False, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - - rank, _ = get_dist_info() - if rank == 0: - if args.out: - warnings.warn( - 'The behavior of ``args.out`` has been changed since MMSeg ' - 'v0.16, the pickled outputs could be seg map as type of ' - 'np.array, pre-eval results or file paths for ' - '``dataset.format_results()``.') - print(f'\nwriting results to {args.out}') - mmcv.dump(results, args.out) - if args.eval: - eval_kwargs.update(metric=args.eval) - metric = dataset.evaluate(results, **eval_kwargs) - metric_dict = dict(config=args.config, metric=metric) - if args.work_dir is not None and rank == 0: - mmcv.dump(metric_dict, json_file, indent=4) - if tmpdir is not None and eval_on_format_results: - # remove tmp dir when cityscapes evaluation - shutil.rmtree(tmpdir) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/gcnet/pytorch/train.py b/cv/semantic_segmentation/gcnet/pytorch/train.py deleted file mode 100755 index 25cdbe359..000000000 --- a/cv/semantic_segmentation/gcnet/pytorch/train.py +++ /dev/null @@ -1,211 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import copy -import os -import os.path as osp -import time -import warnings -import ssl -ssl._create_default_https_context = ssl._create_unverified_context - -import mmcv -import torch -from mmcv.cnn.utils import revert_sync_batchnorm -from mmcv.runner import get_dist_info, init_dist -from mmcv.utils import Config, DictAction, get_git_hash - -from mmseg import __version__ -from mmseg.apis import init_random_seed, set_random_seed, train_segmentor -from mmseg.datasets import build_dataset -from mmseg.models import build_segmentor -from mmseg.utils import collect_env, get_root_logger - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument('--work-dir', help='the dir to save logs and models') - parser.add_argument( - '--load-from', help='the checkpoint file to load weights from') - parser.add_argument( - '--resume-from', help='the checkpoint file to resume from') - parser.add_argument( - '--no-validate', - action='store_true', - help='whether not to evaluate the checkpoint during training') - group_gpus = parser.add_mutually_exclusive_group() - group_gpus.add_argument( - '--gpus', - type=int, - help='number of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-ids', - type=int, - nargs='+', - help='ids of gpus to use ' - '(only applicable to non-distributed training)') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - - # work_dir is determined in this priority: CLI > segment in file > filename - if args.work_dir is not None: - # update configs according to CLI args if args.work_dir is not None - cfg.work_dir = args.work_dir - elif cfg.get('work_dir', None) is None: - # use config filename as default work_dir if cfg.work_dir is None - cfg.work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - if args.load_from is not None: - cfg.load_from = args.load_from - if args.resume_from is not None: - cfg.resume_from = args.resume_from - if args.gpu_ids is not None: - cfg.gpu_ids = args.gpu_ids - else: - cfg.gpu_ids = range(1) if args.gpus is None else range(args.gpus) - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - # gpu_ids is used to calculate iter when resuming checkpoint - _, world_size = get_dist_info() - cfg.gpu_ids = range(world_size) - - # create work_dir - mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) - # dump config - cfg.dump(osp.join(cfg.work_dir, osp.basename(args.config))) - # init the logger before other steps - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - log_file = osp.join(cfg.work_dir, f'{timestamp}.log') - logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) - - # init the meta dict to record some important information such as - # environment info and seed, which will be logged - meta = dict() - # log env info - env_info_dict = collect_env() - env_info = '\n'.join([f'{k}: {v}' for k, v in env_info_dict.items()]) - dash_line = '-' * 60 + '\n' - logger.info('Environment info:\n' + dash_line + env_info + '\n' + - dash_line) - meta['env_info'] = env_info - - # log some basic info - logger.info(f'Distributed training: {distributed}') - logger.info(f'Config:\n{cfg.pretty_text}') - - # set random seeds - seed = init_random_seed(args.seed) - logger.info(f'Set random seed to {seed}, ' - f'deterministic: {args.deterministic}') - set_random_seed(seed, deterministic=args.deterministic) - cfg.seed = seed - meta['seed'] = seed - meta['exp_name'] = osp.basename(args.config) - - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')) - model.init_weights() - - # SyncBN is not support for DP - if not distributed: - warnings.warn( - 'SyncBN is only supported with DDP. To be compatible with DP, ' - 'we convert SyncBN to BN. Please use dist_train.sh which can ' - 'avoid this error.') - model = revert_sync_batchnorm(model) - - logger.info(model) - - datasets = [build_dataset(cfg.data.train)] - if len(cfg.workflow) == 2: - val_dataset = copy.deepcopy(cfg.data.val) - val_dataset.pipeline = cfg.data.train.pipeline - datasets.append(build_dataset(val_dataset)) - if cfg.checkpoint_config is not None: - # save mmseg version, config file content and class names in - # checkpoints as meta data - cfg.checkpoint_config.meta = dict( - mmseg_version=f'{__version__}+{get_git_hash()[:7]}', - config=cfg.pretty_text, - CLASSES=datasets[0].CLASSES, - PALETTE=datasets[0].PALETTE) - # add an attribute for visualization convenience - model.CLASSES = datasets[0].CLASSES - # passing checkpoint meta for saving best checkpoint - meta.update(cfg.checkpoint_config.meta) - train_segmentor( - model, - datasets, - cfg, - distributed=distributed, - validate=(not args.no_validate), - timestamp=timestamp, - meta=meta) - - -if __name__ == '__main__': - main() -- Gitee From b2cbbf83971c5d496243ec1bb5472c8232ce9581 Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Thu, 6 Mar 2025 09:36:45 +0800 Subject: [PATCH 07/15] update pspnet --- .../pspnet/pytorch/.gitignore | 120 -- .../pspnet/pytorch/CITATION.cff | 8 - .../pspnet/pytorch/LICENSE | 203 --- .../pspnet/pytorch/README.md | 139 +- .../pspnet/pytorch/README_origin.md | 205 --- .../pspnet/pytorch/README_zh-CN.md | 220 --- .../pytorch/configs/_base_/datasets/ade20k.py | 54 - .../configs/_base_/datasets/ade20k_640x640.py | 54 - .../configs/_base_/datasets/chase_db1.py | 59 - .../configs/_base_/datasets/cityscapes.py | 54 - .../_base_/datasets/cityscapes_1024x1024.py | 35 - .../_base_/datasets/cityscapes_768x768.py | 35 - .../_base_/datasets/cityscapes_769x769.py | 35 - .../_base_/datasets/cityscapes_832x832.py | 35 - .../configs/_base_/datasets/coco-stuff10k.py | 57 - .../configs/_base_/datasets/coco-stuff164k.py | 54 - .../configs/_base_/datasets/datasets.md | 135 -- .../pytorch/configs/_base_/datasets/drive.py | 59 - .../pytorch/configs/_base_/datasets/hrf.py | 59 - .../pytorch/configs/_base_/datasets/isaid.py | 62 - .../pytorch/configs/_base_/datasets/loveda.py | 54 - .../configs/_base_/datasets/pascal_context.py | 60 - .../_base_/datasets/pascal_context_59.py | 60 - .../configs/_base_/datasets/pascal_voc12.py | 58 - .../_base_/datasets/pascal_voc12_aug.py | 9 - .../configs/_base_/datasets/potsdam.py | 54 - .../pytorch/configs/_base_/datasets/stare.py | 59 - .../configs/_base_/datasets/vaihingen.py | 54 - .../pytorch/configs/_base_/default_runtime.py | 14 - .../configs/_base_/models/pspnet_r50-d8.py | 44 - .../configs/_base_/schedules/schedule_160k.py | 9 - .../configs/_base_/schedules/schedule_1k.py | 9 - .../configs/_base_/schedules/schedule_20k.py | 9 - .../configs/_base_/schedules/schedule_320k.py | 9 - .../configs/_base_/schedules/schedule_40k.py | 9 - .../configs/_base_/schedules/schedule_80k.py | 9 - .../pspnet/pytorch/configs/pspnet/README.md | 177 --- .../pspnet/pytorch/configs/pspnet/pspnet.yml | 1077 ------------- ...pnet_r101-d8_480x480_40k_pascal_context.py | 2 - ...t_r101-d8_480x480_40k_pascal_context_59.py | 2 - ...pnet_r101-d8_480x480_80k_pascal_context.py | 2 - ...t_r101-d8_480x480_80k_pascal_context_59.py | 2 - .../pspnet_r101-d8_4x4_512x512_80k_potsdam.py | 2 - ...spnet_r101-d8_4x4_512x512_80k_vaihingen.py | 2 - .../pspnet_r101-d8_512x1024_40k_cityscapes.py | 2 - .../pspnet_r101-d8_512x1024_40k_dark.py | 2 - ...pnet_r101-d8_512x1024_40k_night_driving.py | 2 - .../pspnet_r101-d8_512x1024_80k_cityscapes.py | 2 - .../pspnet_r101-d8_512x512_160k_ade20k.py | 2 - .../pspnet_r101-d8_512x512_20k_voc12aug.py | 2 - .../pspnet_r101-d8_512x512_40k_voc12aug.py | 2 - ...r101-d8_512x512_4x4_160k_coco-stuff164k.py | 2 - ...t_r101-d8_512x512_4x4_20k_coco-stuff10k.py | 2 - ...r101-d8_512x512_4x4_320k_coco-stuff164k.py | 2 - ...t_r101-d8_512x512_4x4_40k_coco-stuff10k.py | 2 - ..._r101-d8_512x512_4x4_80k_coco-stuff164k.py | 2 - .../pspnet_r101-d8_512x512_80k_ade20k.py | 2 - .../pspnet_r101-d8_512x512_80k_loveda.py | 6 - .../pspnet_r101-d8_769x769_40k_cityscapes.py | 2 - .../pspnet_r101-d8_769x769_80k_cityscapes.py | 2 - ...et_r101-d8_fp16_512x1024_80k_cityscapes.py | 5 - ...pspnet_r101b-d8_512x1024_80k_cityscapes.py | 4 - .../pspnet_r101b-d8_512x1024_80k_dark.py | 4 - ...net_r101b-d8_512x1024_80k_night_driving.py | 4 - .../pspnet_r101b-d8_769x769_80k_cityscapes.py | 4 - .../pspnet_r18-d8_4x4_512x512_80k_potsdam.py | 9 - ...pspnet_r18-d8_4x4_512x512_80k_vaihingen.py | 9 - .../pspnet_r18-d8_4x4_896x896_80k_isaid.py | 9 - .../pspnet_r18-d8_512x1024_80k_cityscapes.py | 9 - .../pspnet_r18-d8_512x512_80k_loveda.py | 11 - .../pspnet_r18-d8_769x769_80k_cityscapes.py | 9 - .../pspnet_r18b-d8_512x1024_80k_cityscapes.py | 9 - .../pspnet_r18b-d8_769x769_80k_cityscapes.py | 9 - .../pspnet_r50-d32_512x1024_80k_cityscapes.py | 5 - ...-pretrain_512x1024_adamw_80k_cityscapes.py | 25 - ...spnet_r50-d8_480x480_40k_pascal_context.py | 10 - ...et_r50-d8_480x480_40k_pascal_context_59.py | 10 - ...spnet_r50-d8_480x480_80k_pascal_context.py | 10 - ...et_r50-d8_480x480_80k_pascal_context_59.py | 10 - .../pspnet_r50-d8_4x4_512x512_80k_potsdam.py | 6 - ...pspnet_r50-d8_4x4_512x512_80k_vaihingen.py | 6 - .../pspnet_r50-d8_4x4_896x896_80k_isaid.py | 6 - .../pspnet_r50-d8_512x1024_40k_cityscapes.py | 4 - .../pspnet/pspnet_r50-d8_512x1024_40k_dark.py | 29 - ...spnet_r50-d8_512x1024_40k_night_driving.py | 29 - .../pspnet_r50-d8_512x1024_80k_cityscapes.py | 4 - .../pspnet/pspnet_r50-d8_512x1024_80k_dark.py | 30 - ...spnet_r50-d8_512x1024_80k_night_driving.py | 29 - .../pspnet_r50-d8_512x512_160k_ade20k.py | 6 - .../pspnet_r50-d8_512x512_20k_voc12aug.py | 7 - .../pspnet_r50-d8_512x512_40k_voc12aug.py | 7 - ..._r50-d8_512x512_4x4_160k_coco-stuff164k.py | 7 - ...et_r50-d8_512x512_4x4_20k_coco-stuff10k.py | 6 - ..._r50-d8_512x512_4x4_320k_coco-stuff164k.py | 7 - ...et_r50-d8_512x512_4x4_40k_coco-stuff10k.py | 6 - ...t_r50-d8_512x512_4x4_80k_coco-stuff164k.py | 7 - .../pspnet_r50-d8_512x512_80k_ade20k.py | 6 - .../pspnet_r50-d8_512x512_80k_loveda.py | 6 - .../pspnet_r50-d8_769x769_40k_cityscapes.py | 9 - .../pspnet_r50-d8_769x769_80k_cityscapes.py | 9 - ...-pretrain_512x1024_adamw_80k_cityscapes.py | 23 - ...pspnet_r50b-d32_512x1024_80k_cityscapes.py | 7 - .../pspnet_r50b-d8_512x1024_80k_cityscapes.py | 2 - .../pspnet_r50b-d8_769x769_80k_cityscapes.py | 2 - .../pspnet/pytorch/docker/Dockerfile | 32 - .../pspnet/pytorch/docker/serve/Dockerfile | 49 - .../pytorch/docker/serve/config.properties | 5 - .../pspnet/pytorch/docker/serve/entrypoint.sh | 12 - .../pspnet/pytorch/mmcv/__init__.py | 13 - .../pspnet/pytorch/mmcv/cnn/__init__.py | 21 - .../pytorch/mmcv/cnn/bricks/__init__.py | 35 - .../pytorch/mmcv/cnn/bricks/activation.py | 93 -- .../pytorch/mmcv/cnn/bricks/context_block.py | 125 -- .../pspnet/pytorch/mmcv/cnn/bricks/conv.py | 44 - .../cnn/bricks/conv2d_adaptive_padding.py | 62 - .../pytorch/mmcv/cnn/bricks/conv_module.py | 206 --- .../pspnet/pytorch/mmcv/cnn/bricks/conv_ws.py | 148 -- .../bricks/depthwise_separable_conv_module.py | 96 -- .../pspnet/pytorch/mmcv/cnn/bricks/drop.py | 65 - .../mmcv/cnn/bricks/generalized_attention.py | 412 ----- .../pytorch/mmcv/cnn/bricks/hsigmoid.py | 46 - .../pspnet/pytorch/mmcv/cnn/bricks/hswish.py | 38 - .../pytorch/mmcv/cnn/bricks/non_local.py | 306 ---- .../pspnet/pytorch/mmcv/cnn/bricks/norm.py | 144 -- .../pspnet/pytorch/mmcv/cnn/bricks/padding.py | 36 - .../pspnet/pytorch/mmcv/cnn/bricks/plugin.py | 89 -- .../pytorch/mmcv/cnn/bricks/registry.py | 16 - .../pspnet/pytorch/mmcv/cnn/bricks/scale.py | 21 - .../pspnet/pytorch/mmcv/cnn/bricks/swish.py | 25 - .../pytorch/mmcv/cnn/bricks/transformer.py | 944 ------------ .../pytorch/mmcv/cnn/bricks/upsample.py | 84 -- .../pytorch/mmcv/cnn/bricks/wrappers.py | 180 --- .../pspnet/pytorch/mmcv/cnn/builder.py | 30 - .../pspnet/pytorch/mmcv/cnn/utils/__init__.py | 19 - .../pytorch/mmcv/cnn/utils/flops_counter.py | 599 -------- .../pytorch/mmcv/cnn/utils/fuse_conv_bn.py | 59 - .../pspnet/pytorch/mmcv/cnn/utils/sync_bn.py | 60 - .../pytorch/mmcv/cnn/utils/weight_init.py | 685 --------- .../pspnet/pytorch/mmcv/device/__init__.py | 4 - .../pytorch/mmcv/device/ipu/__init__.py | 14 - .../pytorch/mmcv/device/ipu/dataloader.py | 157 -- .../device/ipu/hierarchical_data_manager.py | 243 --- .../pytorch/mmcv/device/ipu/hook_wrapper.py | 105 -- .../pytorch/mmcv/device/ipu/model_wrapper.py | 721 --------- .../pspnet/pytorch/mmcv/device/ipu/runner.py | 142 -- .../pspnet/pytorch/mmcv/device/ipu/utils.py | 244 --- .../pytorch/mmcv/device/mlu/__init__.py | 9 - .../pytorch/mmcv/device/mlu/_functions.py | 22 - .../pytorch/mmcv/device/mlu/data_parallel.py | 41 - .../pytorch/mmcv/device/mlu/distributed.py | 20 - .../pytorch/mmcv/device/mlu/scatter_gather.py | 59 - .../pspnet/pytorch/mmcv/engine/__init__.py | 8 - .../pspnet/pytorch/mmcv/engine/test.py | 202 --- .../pspnet/pytorch/mmcv/fileio/__init__.py | 11 - .../pspnet/pytorch/mmcv/fileio/file_client.py | 1163 -------------- .../pytorch/mmcv/fileio/handlers/__init__.py | 7 - .../pytorch/mmcv/fileio/handlers/base.py | 30 - .../mmcv/fileio/handlers/json_handler.py | 36 - .../mmcv/fileio/handlers/pickle_handler.py | 28 - .../mmcv/fileio/handlers/yaml_handler.py | 25 - .../pspnet/pytorch/mmcv/fileio/io.py | 151 -- .../pspnet/pytorch/mmcv/fileio/parse.py | 97 -- .../pspnet/pytorch/mmcv/image/__init__.py | 28 - .../pspnet/pytorch/mmcv/image/colorspace.py | 306 ---- .../pspnet/pytorch/mmcv/image/geometric.py | 741 --------- .../pspnet/pytorch/mmcv/image/io.py | 314 ---- .../pspnet/pytorch/mmcv/image/misc.py | 53 - .../pspnet/pytorch/mmcv/image/photometric.py | 428 ------ .../pytorch/mmcv/model_zoo/deprecated.json | 6 - .../pspnet/pytorch/mmcv/model_zoo/mmcls.json | 59 - .../pytorch/mmcv/model_zoo/open_mmlab.json | 50 - .../mmcv/model_zoo/torchvision_0.12.json | 57 - .../pspnet/pytorch/mmcv/ops/__init__.py | 12 - .../pspnet/pytorch/mmcv/ops/cc_attention.py | 84 -- .../pspnet/pytorch/mmcv/ops/csrc/README.md | 170 --- .../csrc/common/cuda/common_cuda_helper.hpp | 120 -- .../csrc/common/cuda/psamask_cuda_kernel.cuh | 141 -- .../cuda/sigmoid_focal_loss_cuda_kernel.cuh | 71 - .../cuda/softmax_focal_loss_cuda_kernel.cuh | 72 - .../csrc/common/cuda/sync_bn_cuda_kernel.cuh | 331 ---- .../ops/csrc/common/pytorch_cpp_helper.hpp | 27 - .../ops/csrc/common/pytorch_cuda_helper.hpp | 19 - .../csrc/common/pytorch_device_registry.hpp | 141 -- .../mmcv/ops/csrc/pytorch/cuda/cudabind.cpp | 210 --- .../ops/csrc/pytorch/cuda/focal_loss_cuda.cu | 111 -- .../ops/csrc/pytorch/cuda/psamask_cuda.cu | 60 - .../ops/csrc/pytorch/cuda/sync_bn_cuda.cu | 110 -- .../mmcv/ops/csrc/pytorch/focal_loss.cpp | 53 - .../pytorch/mmcv/ops/csrc/pytorch/info.cpp | 56 - .../pytorch/mmcv/ops/csrc/pytorch/psamask.cpp | 41 - .../pytorch/mmcv/ops/csrc/pytorch/pybind.cpp | 106 -- .../pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp | 69 - .../pytorch/mmcv/ops/deprecated_wrappers.py | 46 - .../pspnet/pytorch/mmcv/ops/focal_loss.py | 213 --- .../pspnet/pytorch/mmcv/ops/info.py | 36 - .../pspnet/pytorch/mmcv/ops/point_sample.py | 346 ----- .../pspnet/pytorch/mmcv/ops/psa_mask.py | 92 -- .../pspnet/pytorch/mmcv/ops/sync_bn.py | 279 ---- .../pspnet/pytorch/mmcv/parallel/__init__.py | 13 - .../pytorch/mmcv/parallel/_functions.py | 76 - .../pspnet/pytorch/mmcv/parallel/collate.py | 84 -- .../pytorch/mmcv/parallel/data_container.py | 89 -- .../pytorch/mmcv/parallel/data_parallel.py | 97 -- .../pytorch/mmcv/parallel/distributed.py | 138 -- .../mmcv/parallel/distributed_deprecated.py | 70 - .../pspnet/pytorch/mmcv/parallel/registry.py | 8 - .../pytorch/mmcv/parallel/scatter_gather.py | 59 - .../pspnet/pytorch/mmcv/parallel/utils.py | 20 - .../pspnet/pytorch/mmcv/runner/__init__.py | 73 - .../pspnet/pytorch/mmcv/runner/base_module.py | 208 --- .../pspnet/pytorch/mmcv/runner/base_runner.py | 544 ------- .../pspnet/pytorch/mmcv/runner/builder.py | 24 - .../pspnet/pytorch/mmcv/runner/checkpoint.py | 759 ---------- .../mmcv/runner/default_constructor.py | 45 - .../pspnet/pytorch/mmcv/runner/dist_utils.py | 204 --- .../pytorch/mmcv/runner/epoch_based_runner.py | 188 --- .../pspnet/pytorch/mmcv/runner/fp16_utils.py | 423 ------ .../pytorch/mmcv/runner/hooks/__init__.py | 48 - .../pytorch/mmcv/runner/hooks/checkpoint.py | 167 --- .../pytorch/mmcv/runner/hooks/closure.py | 11 - .../pspnet/pytorch/mmcv/runner/hooks/ema.py | 89 -- .../pytorch/mmcv/runner/hooks/evaluation.py | 511 ------- .../pspnet/pytorch/mmcv/runner/hooks/hook.py | 92 -- .../pytorch/mmcv/runner/hooks/iter_timer.py | 31 - .../mmcv/runner/hooks/logger/__init__.py | 18 - .../pytorch/mmcv/runner/hooks/logger/base.py | 167 --- .../mmcv/runner/hooks/logger/clearml.py | 62 - .../mmcv/runner/hooks/logger/dvclive.py | 68 - .../mmcv/runner/hooks/logger/mlflow.py | 80 - .../mmcv/runner/hooks/logger/neptune.py | 88 -- .../pytorch/mmcv/runner/hooks/logger/pavi.py | 132 -- .../mmcv/runner/hooks/logger/segmind.py | 49 - .../mmcv/runner/hooks/logger/tensorboard.py | 69 - .../pytorch/mmcv/runner/hooks/logger/text.py | 256 ---- .../pytorch/mmcv/runner/hooks/logger/wandb.py | 107 -- .../pytorch/mmcv/runner/hooks/lr_updater.py | 730 --------- .../pytorch/mmcv/runner/hooks/memory.py | 25 - .../mmcv/runner/hooks/momentum_updater.py | 566 ------- .../pytorch/mmcv/runner/hooks/optimizer.py | 556 ------- .../pytorch/mmcv/runner/hooks/profiler.py | 180 --- .../pytorch/mmcv/runner/hooks/sampler_seed.py | 20 - .../pytorch/mmcv/runner/hooks/sync_buffer.py | 22 - .../pytorch/mmcv/runner/iter_based_runner.py | 273 ---- .../pspnet/pytorch/mmcv/runner/log_buffer.py | 41 - .../pytorch/mmcv/runner/optimizer/__init__.py | 9 - .../pytorch/mmcv/runner/optimizer/builder.py | 44 - .../runner/optimizer/default_constructor.py | 250 --- .../pspnet/pytorch/mmcv/runner/priority.py | 60 - .../pspnet/pytorch/mmcv/runner/utils.py | 93 -- .../pspnet/pytorch/mmcv/utils/__init__.py | 78 - .../pspnet/pytorch/mmcv/utils/config.py | 719 --------- .../pspnet/pytorch/mmcv/utils/device_type.py | 24 - .../pspnet/pytorch/mmcv/utils/env.py | 120 -- .../pspnet/pytorch/mmcv/utils/ext_loader.py | 72 - .../pspnet/pytorch/mmcv/utils/hub.py | 131 -- .../pspnet/pytorch/mmcv/utils/logging.py | 111 -- .../pspnet/pytorch/mmcv/utils/misc.py | 377 ----- .../pspnet/pytorch/mmcv/utils/parrots_jit.py | 41 - .../pytorch/mmcv/utils/parrots_wrapper.py | 114 -- .../pspnet/pytorch/mmcv/utils/path.py | 101 -- .../pspnet/pytorch/mmcv/utils/progressbar.py | 208 --- .../pspnet/pytorch/mmcv/utils/registry.py | 337 ----- .../pspnet/pytorch/mmcv/utils/seed.py | 23 - .../pspnet/pytorch/mmcv/utils/testing.py | 141 -- .../pspnet/pytorch/mmcv/utils/timer.py | 118 -- .../pspnet/pytorch/mmcv/utils/trace.py | 24 - .../pytorch/mmcv/utils/version_utils.py | 90 -- .../pspnet/pytorch/mmcv/version.py | 35 - .../pspnet/pytorch/mmseg/__init__.py | 62 - .../pspnet/pytorch/mmseg/apis/__init__.py | 11 - .../pspnet/pytorch/mmseg/apis/inference.py | 136 -- .../pspnet/pytorch/mmseg/apis/test.py | 233 --- .../pspnet/pytorch/mmseg/apis/train.py | 196 --- .../pspnet/pytorch/mmseg/core/__init__.py | 11 - .../pspnet/pytorch/mmseg/core/builder.py | 33 - .../pytorch/mmseg/core/evaluation/__init__.py | 11 - .../mmseg/core/evaluation/class_names.py | 316 ---- .../mmseg/core/evaluation/eval_hooks.py | 128 -- .../pytorch/mmseg/core/evaluation/metrics.py | 395 ----- .../pytorch/mmseg/core/optimizers/__init__.py | 7 - .../layer_decay_optimizer_constructor.py | 208 --- .../pspnet/pytorch/mmseg/core/seg/__init__.py | 5 - .../pspnet/pytorch/mmseg/core/seg/builder.py | 9 - .../mmseg/core/seg/sampler/__init__.py | 5 - .../core/seg/sampler/base_pixel_sampler.py | 13 - .../core/seg/sampler/ohem_pixel_sampler.py | 85 -- .../pytorch/mmseg/core/utils/__init__.py | 5 - .../pytorch/mmseg/core/utils/dist_util.py | 46 - .../pspnet/pytorch/mmseg/core/utils/misc.py | 18 - .../pspnet/pytorch/mmseg/datasets/__init__.py | 10 - .../pspnet/pytorch/mmseg/datasets/ade.py | 167 --- .../pspnet/pytorch/mmseg/datasets/builder.py | 191 --- .../pytorch/mmseg/datasets/cityscapes.py | 214 --- .../pytorch/mmseg/datasets/coco_stuff.py | 94 -- .../pspnet/pytorch/mmseg/datasets/custom.py | 487 ------ .../mmseg/datasets/dataset_wrappers.py | 277 ---- .../pytorch/mmseg/datasets/pascal_context.py | 103 -- .../mmseg/datasets/pipelines/__init__.py | 19 - .../mmseg/datasets/pipelines/compose.py | 52 - .../mmseg/datasets/pipelines/formating.py | 9 - .../mmseg/datasets/pipelines/formatting.py | 289 ---- .../mmseg/datasets/pipelines/loading.py | 158 -- .../mmseg/datasets/pipelines/test_time_aug.py | 134 -- .../mmseg/datasets/pipelines/transforms.py | 1335 ----------------- .../mmseg/datasets/samplers/__init__.py | 4 - .../datasets/samplers/distributed_sampler.py | 71 - .../pspnet/pytorch/mmseg/datasets/voc.py | 39 - .../pspnet/pytorch/mmseg/models/__init__.py | 13 - .../mmseg/models/backbones/__init__.py | 4 - .../pytorch/mmseg/models/backbones/resnest.py | 318 ---- .../pytorch/mmseg/models/backbones/resnet.py | 714 --------- .../pytorch/mmseg/models/backbones/resnext.py | 150 -- .../mmseg/models/backbones/timm_backbone.py | 63 - .../pspnet/pytorch/mmseg/models/builder.py | 49 - .../mmseg/models/decode_heads/__init__.py | 6 - .../mmseg/models/decode_heads/aspp_head.py | 122 -- .../mmseg/models/decode_heads/cc_head.py | 43 - .../mmseg/models/decode_heads/decode_head.py | 266 ---- .../mmseg/models/decode_heads/fcn_head.py | 96 -- .../mmseg/models/decode_heads/psp_head.py | 117 -- .../pytorch/mmseg/models/losses/__init__.py | 15 - .../pytorch/mmseg/models/losses/accuracy.py | 92 -- .../mmseg/models/losses/cross_entropy_loss.py | 296 ---- .../pytorch/mmseg/models/losses/dice_loss.py | 137 -- .../pytorch/mmseg/models/losses/focal_loss.py | 327 ---- .../mmseg/models/losses/lovasz_loss.py | 323 ---- .../pytorch/mmseg/models/losses/utils.py | 126 -- .../pytorch/mmseg/models/necks/__init__.py | 11 - .../mmseg/models/necks/featurepyramid.py | 67 - .../pspnet/pytorch/mmseg/models/necks/fpn.py | 213 --- .../pytorch/mmseg/models/necks/ic_neck.py | 148 -- .../pspnet/pytorch/mmseg/models/necks/jpu.py | 131 -- .../pytorch/mmseg/models/necks/mla_neck.py | 118 -- .../mmseg/models/necks/multilevel_neck.py | 78 - .../mmseg/models/segmentors/__init__.py | 6 - .../pytorch/mmseg/models/segmentors/base.py | 291 ---- .../segmentors/cascade_encoder_decoder.py | 88 -- .../models/segmentors/encoder_decoder.py | 284 ---- .../pytorch/mmseg/models/utils/__init__.py | 16 - .../pytorch/mmseg/models/utils/embed.py | 330 ---- .../mmseg/models/utils/inverted_residual.py | 213 --- .../mmseg/models/utils/make_divisible.py | 28 - .../pytorch/mmseg/models/utils/res_layer.py | 96 -- .../pytorch/mmseg/models/utils/se_layer.py | 58 - .../models/utils/self_attention_block.py | 160 -- .../mmseg/models/utils/shape_convert.py | 107 -- .../mmseg/models/utils/up_conv_block.py | 102 -- .../pspnet/pytorch/mmseg/ops/__init__.py | 5 - .../pspnet/pytorch/mmseg/ops/encoding.py | 75 - .../pspnet/pytorch/mmseg/ops/wrappers.py | 51 - .../pspnet/pytorch/mmseg/utils/__init__.py | 10 - .../pspnet/pytorch/mmseg/utils/collect_env.py | 18 - .../pspnet/pytorch/mmseg/utils/logger.py | 28 - .../pspnet/pytorch/mmseg/utils/misc.py | 41 - .../pspnet/pytorch/mmseg/utils/set_env.py | 55 - .../pspnet/pytorch/mmseg/version.py | 18 - .../pspnet/pytorch/requirements.txt | 3 - .../pytorch/requirements/mmcv/build.txt | 1 - .../pspnet/pytorch/requirements/mmcv/docs.txt | 8 - .../pytorch/requirements/mmcv/optional.txt | 1 - .../requirements/mmcv/requirements.txt | 4 - .../pytorch/requirements/mmcv/runtime.txt | 7 - .../pspnet/pytorch/requirements/mmcv/test.txt | 9 - .../pspnet/pytorch/requirements/mminstall.txt | 2 - .../pspnet/pytorch/requirements/optional.txt | 1 - .../pspnet/pytorch/requirements/pspnet.txt | 4 - .../pspnet/pytorch/requirements/runtime.txt | 5 - .../pspnet/pytorch/setup.py | 258 ---- .../pspnet/pytorch/tools/analyze_logs.py | 128 -- .../pspnet/pytorch/tools/benchmark.py | 120 -- .../pspnet/pytorch/tools/confusion_matrix.py | 184 --- .../tools/convert_datasets/cityscapes.py | 56 - .../tools/convert_datasets/coco_stuff10k.py | 307 ---- .../tools/convert_datasets/coco_stuff164k.py | 264 ---- .../tools/convert_datasets/pascal_context.py | 87 -- .../pytorch/tools/convert_datasets/voc_aug.py | 92 -- .../pspnet/pytorch/tools/get_flops.py | 60 - .../pspnet/pytorch/tools/print_config.py | 69 - .../pspnet/pytorch/tools/slurm_test.sh | 24 - .../pspnet/pytorch/tools/slurm_train.sh | 23 - .../pspnet/pytorch/tools/test.py | 319 ---- .../pspnet/pytorch/train.py | 243 --- .../pspnet/pytorch/train.sh | 20 - .../pspnet/pytorch/train_dist.sh | 31 - 384 files changed, 43 insertions(+), 42221 deletions(-) delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/.gitignore delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/CITATION.cff delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/LICENSE delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/README_origin.md delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/README_zh-CN.md delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/ade20k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/ade20k_640x640.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/chase_db1.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_768x768.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_832x832.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/coco-stuff10k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/coco-stuff164k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/datasets.md delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/drive.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/hrf.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/isaid.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/loveda.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_context_59.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_voc12.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/potsdam.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/stare.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/vaihingen.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/default_runtime.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/models/pspnet_r50-d8.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_160k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_1k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_20k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_320k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_40k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_80k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/README.md delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet.yml delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_40k_pascal_context.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_40k_pascal_context_59.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_80k_pascal_context.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_80k_pascal_context_59.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_4x4_512x512_80k_potsdam.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_4x4_512x512_80k_vaihingen.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_40k_dark.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_40k_night_driving.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_160k_ade20k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_20k_voc12aug.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_40k_voc12aug.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_160k_coco-stuff164k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_20k_coco-stuff10k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_320k_coco-stuff164k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_40k_coco-stuff10k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_80k_coco-stuff164k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_80k_ade20k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_80k_loveda.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_769x769_40k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_769x769_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_fp16_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_512x1024_80k_dark.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_512x1024_80k_night_driving.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_769x769_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_4x4_512x512_80k_potsdam.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_4x4_512x512_80k_vaihingen.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_4x4_896x896_80k_isaid.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_512x512_80k_loveda.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_769x769_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18b-d8_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18b-d8_769x769_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d32_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d32_rsb-pretrain_512x1024_adamw_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_40k_pascal_context.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_40k_pascal_context_59.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_80k_pascal_context.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_80k_pascal_context_59.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_4x4_512x512_80k_potsdam.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_4x4_512x512_80k_vaihingen.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_4x4_896x896_80k_isaid.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_40k_dark.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_40k_night_driving.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_80k_dark.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_80k_night_driving.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_160k_ade20k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_20k_voc12aug.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_40k_voc12aug.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_80k_ade20k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_80k_loveda.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_769x769_40k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_769x769_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_rsb-pretrain_512x1024_adamw_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50b-d32_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50b-d8_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50b-d8_769x769_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/docker/Dockerfile delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/docker/serve/Dockerfile delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/docker/serve/config.properties delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/docker/serve/entrypoint.sh delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/activation.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/context_block.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv_module.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv_ws.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/drop.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/generalized_attention.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/hsigmoid.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/hswish.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/non_local.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/norm.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/padding.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/plugin.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/registry.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/scale.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/swish.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/transformer.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/upsample.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/wrappers.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/builder.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/flops_counter.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/sync_bn.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/weight_init.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/device/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/dataloader.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/hook_wrapper.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/model_wrapper.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/runner.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/utils.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/_functions.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/data_parallel.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/distributed.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/scatter_gather.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/engine/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/engine/test.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/file_client.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/base.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/json_handler.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/pickle_handler.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/yaml_handler.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/io.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/parse.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/image/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/image/colorspace.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/image/geometric.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/image/io.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/image/misc.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/image/photometric.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/deprecated.json delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/mmcls.json delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/open_mmlab.json delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/torchvision_0.12.json delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/cc_attention.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/README.md delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/deprecated_wrappers.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/focal_loss.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/info.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/point_sample.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/psa_mask.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/sync_bn.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/_functions.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/collate.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/data_container.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/data_parallel.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/distributed.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/distributed_deprecated.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/registry.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/scatter_gather.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/utils.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/base_module.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/base_runner.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/builder.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/checkpoint.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/default_constructor.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/dist_utils.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/epoch_based_runner.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/fp16_utils.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/checkpoint.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/closure.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/ema.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/evaluation.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/hook.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/iter_timer.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/base.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/clearml.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/dvclive.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/mlflow.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/neptune.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/pavi.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/segmind.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/text.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/wandb.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/lr_updater.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/memory.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/momentum_updater.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/optimizer.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/profiler.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/sampler_seed.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/sync_buffer.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/iter_based_runner.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/log_buffer.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/optimizer/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/optimizer/builder.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/optimizer/default_constructor.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/priority.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/utils.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/config.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/device_type.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/env.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/ext_loader.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/hub.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/logging.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/misc.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/parrots_jit.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/parrots_wrapper.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/path.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/progressbar.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/registry.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/seed.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/testing.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/timer.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/trace.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/version_utils.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmcv/version.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/inference.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/test.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/train.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/builder.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/class_names.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/eval_hooks.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/metrics.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/optimizers/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/builder.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/sampler/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/utils/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/utils/dist_util.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/core/utils/misc.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/ade.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/builder.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/coco_stuff.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/custom.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/dataset_wrappers.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/compose.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/formating.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/formatting.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/loading.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/transforms.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/samplers/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/samplers/distributed_sampler.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/voc.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/resnest.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/resnet.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/resnext.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/timm_backbone.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/builder.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/aspp_head.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/cc_head.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/decode_head.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/fcn_head.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/psp_head.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/accuracy.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/cross_entropy_loss.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/dice_loss.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/focal_loss.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/lovasz_loss.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/utils.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/featurepyramid.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/fpn.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/ic_neck.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/jpu.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/mla_neck.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/multilevel_neck.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/base.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/encoder_decoder.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/embed.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/inverted_residual.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/make_divisible.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/res_layer.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/se_layer.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/self_attention_block.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/shape_convert.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/up_conv_block.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/ops/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/ops/encoding.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/ops/wrappers.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/__init__.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/collect_env.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/logger.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/misc.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/set_env.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/mmseg/version.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/requirements.txt delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/build.txt delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/docs.txt delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/optional.txt delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/requirements.txt delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/runtime.txt delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/test.txt delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/requirements/mminstall.txt delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/requirements/optional.txt delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/requirements/pspnet.txt delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/requirements/runtime.txt delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/setup.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/tools/analyze_logs.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/tools/benchmark.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/tools/confusion_matrix.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/coco_stuff10k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/coco_stuff164k.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/voc_aug.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/tools/get_flops.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/tools/print_config.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/tools/slurm_test.sh delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/tools/slurm_train.sh delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/tools/test.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/train.py delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/train.sh delete mode 100644 cv/semantic_segmentation/pspnet/pytorch/train_dist.sh diff --git a/cv/semantic_segmentation/pspnet/pytorch/.gitignore b/cv/semantic_segmentation/pspnet/pytorch/.gitignore deleted file mode 100644 index 787d13ec6..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/.gitignore +++ /dev/null @@ -1,120 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/en/_build/ -docs/zh_cn/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ -.DS_Store - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -data -.vscode -.idea - -# custom -*.pkl -*.pkl.json -*.log.json -work_dirs/ -mmseg/.mim - -# Pytorch -*.pth diff --git a/cv/semantic_segmentation/pspnet/pytorch/CITATION.cff b/cv/semantic_segmentation/pspnet/pytorch/CITATION.cff deleted file mode 100644 index cfd7cab05..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/CITATION.cff +++ /dev/null @@ -1,8 +0,0 @@ -cff-version: 1.2.0 -message: "If you use this software, please cite it as below." -authors: - - name: "MMSegmentation Contributors" -title: "OpenMMLab Semantic Segmentation Toolbox and Benchmark" -date-released: 2020-07-10 -url: "https://github.com/open-mmlab/mmsegmentation" -license: Apache-2.0 diff --git a/cv/semantic_segmentation/pspnet/pytorch/LICENSE b/cv/semantic_segmentation/pspnet/pytorch/LICENSE deleted file mode 100644 index 38e625bf5..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Copyright 2020 The MMSegmentation Authors. All rights reserved. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020 The MMSegmentation Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cv/semantic_segmentation/pspnet/pytorch/README.md b/cv/semantic_segmentation/pspnet/pytorch/README.md index 92f047233..c17af4366 100644 --- a/cv/semantic_segmentation/pspnet/pytorch/README.md +++ b/cv/semantic_segmentation/pspnet/pytorch/README.md @@ -10,115 +10,62 @@ PSPNet provides a superior framework for pixellevel prediction. The proposed app ### Install packages ```shell -$ pip3 install -r requirements.txt -``` - -### Build Extension - -```shell -$ python3 setup.py build && cp build/lib.linux*/mmcv/_ext.cpython* mmcv +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +# install mmsegmentation +git clone -b v1.2.2 https://github.com/open-mmlab/mmsegmentation.git --depth=1 +cd mmsegmentation/ +pip install -v -e . + +pip install ftfy ``` ### Prepare datasets -**You need to arrange datasets as follow:** -[Datasets](configs/_base_/datasets/datasets.md) - -```shell -mkdir -p data -ln -s /path/to/datasets/ data/ +Go to visit [Cityscapes official website](https://www.cityscapes-dataset.com/), then choose 'Download' to download the Cityscapes dataset. + +Specify `/path/to/cityscapes` to your Cityscapes path in later training process, the unzipped dataset path structure sholud look like: + +```bash +cityscapes/ +├── gtFine +│   ├── test +│   ├── train +│   │   ├── aachen +│   │   └── bochum +│   └── val +│   ├── frankfurt +│   ├── lindau +│   └── munster +└── leftImg8bit + ├── train + │   ├── aachen + │   └── bochum + └── val + ├── frankfurt + ├── lindau + └── munster ``` +```bash +mkdir data/ +ln -s /path/to/cityscapes data/cityscapes +``` ## Step 2: Training - -**There are available configs in ./configs/pspnet/** - ### Training on single card ```shell -$ bash train.sh [training args] # config file can be found in the configs directory +python3 tools/train.py configs/pspnet/pspnet_r50-d8_4xb2-40k_cityscapes-512x1024.py ``` ### Training on mutil-cards ```shell -$ bash train_dist.sh [training args] # config file can be found in the configs directory +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/pspnet/pspnet_r50-d8_4xb2-40k_cityscapes-512x1024.py 8 ``` -### Example - -```shell -bash train_dist.sh pspnet_r50-d8_512x1024_40k_cityscapes 8 -``` - -### Training arguments - -```python -# the dir to save logs and models -work-dir: str = None - -# the checkpoint file to load weights from -load-from: str = None - -# the checkpoint file to resume from -resume-from: str = None - -# whether not to evaluate the checkpoint during training -no-validate: bool = False - -# (Deprecated, please use --gpu-id) number of gpus to -# use (only applicable to non-distributed training) -gpus: int = None - -# (Deprecated, please use --gpu-id) ids of gpus to use -# (only applicable to non-distributed training) -gpu-ids: int = None - -# id of gpu to use (only applicable to non-distributed training) -gpu-id: int = 0 - -# random seed -seed: int = None - -# Whether or not set different seeds for different ranks -diff_seed: bool = False - -# whether to set deterministic options for CUDNN backend. -deterministic: bool = False - -# --options is deprecated in favor of --cfg_options' and it -# will not be supported in version v0.22.0. Override some -# settings in the used config, the key-value pair in xxx=yyy -# format will be merged into config file. If the value to be -# overwritten is a list, it should be like key="[a,b]" or key=a,b -# It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" -# Note that the quotation marks are necessary and that no white space -# is allowed. -options: str = None - -# override some settings in the used config, the key-value pair -# in xxx=yyy format will be merged into config file. If the value -# to be overwritten is a list, it should be like key="[a,b]" or key=a,b -# It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" -# Note that the quotation marks are necessary and that no white -# space is allowed. -cfg-options: str = None - -# job launcher -launcher: str = "none" - -# local rank -local_rank: int = 0 - -# distributed backend -dist_backend: str = None - -# resume from the latest checkpoint automatically. -auto-resume: bool = False -``` - -## Results - - ## Reference --Ref: https://mmsegmentation.readthedocs.io/en/latest/dataset_prepare.html#cityscapes --Ref: [Author Results](configs/pspnet/README.md) --Ref: https://github.com/open-mmlab/mmsegmentation +[mmsegmentation](https://github.com/open-mmlab/mmsegmentation) diff --git a/cv/semantic_segmentation/pspnet/pytorch/README_origin.md b/cv/semantic_segmentation/pspnet/pytorch/README_origin.md deleted file mode 100644 index ba1d3a444..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/README_origin.md +++ /dev/null @@ -1,205 +0,0 @@ -
- -
 
-
- OpenMMLab website - - - HOT - - -      - OpenMMLab platform - - - TRY IT OUT - - -
-
 
-
-
- -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/mmsegmentation)](https://pypi.org/project/mmsegmentation/) -[![PyPI](https://img.shields.io/pypi/v/mmsegmentation)](https://pypi.org/project/mmsegmentation) -[![docs](https://img.shields.io/badge/docs-latest-blue)](https://mmsegmentation.readthedocs.io/en/latest/) -[![badge](https://github.com/open-mmlab/mmsegmentation/workflows/build/badge.svg)](https://github.com/open-mmlab/mmsegmentation/actions) -[![codecov](https://codecov.io/gh/open-mmlab/mmsegmentation/branch/master/graph/badge.svg)](https://codecov.io/gh/open-mmlab/mmsegmentation) -[![license](https://img.shields.io/github/license/open-mmlab/mmsegmentation.svg)](https://github.com/open-mmlab/mmsegmentation/blob/master/LICENSE) -[![issue resolution](https://isitmaintained.com/badge/resolution/open-mmlab/mmsegmentation.svg)](https://github.com/open-mmlab/mmsegmentation/issues) -[![open issues](https://isitmaintained.com/badge/open/open-mmlab/mmsegmentation.svg)](https://github.com/open-mmlab/mmsegmentation/issues) - -Documentation: https://mmsegmentation.readthedocs.io/ - -English | [简体中文](README_zh-CN.md) - -## Introduction - -MMSegmentation is an open source semantic segmentation toolbox based on PyTorch. -It is a part of the OpenMMLab project. - -The master branch works with **PyTorch 1.5+**. - -![demo image](resources/seg_demo.gif) - -### Major features - -- **Unified Benchmark** - - We provide a unified benchmark toolbox for various semantic segmentation methods. - -- **Modular Design** - - We decompose the semantic segmentation framework into different components and one can easily construct a customized semantic segmentation framework by combining different modules. - -- **Support of multiple methods out of box** - - The toolbox directly supports popular and contemporary semantic segmentation frameworks, *e.g.* PSPNet, DeepLabV3, PSANet, DeepLabV3+, etc. - -- **High efficiency** - - The training speed is faster than or comparable to other codebases. - -## License - -This project is released under the [Apache 2.0 license](LICENSE). - -## Changelog - -v0.24.1 was released in 5/1/2022. -Please refer to [changelog.md](docs/en/changelog.md) for details and release history. - -## Benchmark and model zoo - -Results and models are available in the [model zoo](docs/en/model_zoo.md). - -Supported backbones: - -- [x] ResNet (CVPR'2016) -- [x] ResNeXt (CVPR'2017) -- [x] [HRNet (CVPR'2019)](configs/hrnet) -- [x] [ResNeSt (ArXiv'2020)](configs/resnest) -- [x] [MobileNetV2 (CVPR'2018)](configs/mobilenet_v2) -- [x] [MobileNetV3 (ICCV'2019)](configs/mobilenet_v3) -- [x] [Vision Transformer (ICLR'2021)](configs/vit) -- [x] [Swin Transformer (ICCV'2021)](configs/swin) -- [x] [Twins (NeurIPS'2021)](configs/twins) -- [x] [BEiT (ICLR'2022)](configs/beit) -- [x] [ConvNeXt (CVPR'2022)](configs/convnext) -- [x] [MAE (CVPR'2022)](configs/mae) - -Supported methods: - -- [x] [FCN (CVPR'2015/TPAMI'2017)](configs/fcn) -- [x] [ERFNet (T-ITS'2017)](configs/erfnet) -- [x] [UNet (MICCAI'2016/Nat. Methods'2019)](configs/unet) -- [x] [PSPNet (CVPR'2017)](configs/pspnet) -- [x] [DeepLabV3 (ArXiv'2017)](configs/deeplabv3) -- [x] [BiSeNetV1 (ECCV'2018)](configs/bisenetv1) -- [x] [PSANet (ECCV'2018)](configs/psanet) -- [x] [DeepLabV3+ (CVPR'2018)](configs/deeplabv3plus) -- [x] [UPerNet (ECCV'2018)](configs/upernet) -- [x] [ICNet (ECCV'2018)](configs/icnet) -- [x] [NonLocal Net (CVPR'2018)](configs/nonlocal_net) -- [x] [EncNet (CVPR'2018)](configs/encnet) -- [x] [Semantic FPN (CVPR'2019)](configs/sem_fpn) -- [x] [DANet (CVPR'2019)](configs/danet) -- [x] [APCNet (CVPR'2019)](configs/apcnet) -- [x] [EMANet (ICCV'2019)](configs/emanet) -- [x] [CCNet (ICCV'2019)](configs/ccnet) -- [x] [DMNet (ICCV'2019)](configs/dmnet) -- [x] [ANN (ICCV'2019)](configs/ann) -- [x] [GCNet (ICCVW'2019/TPAMI'2020)](configs/gcnet) -- [x] [FastFCN (ArXiv'2019)](configs/fastfcn) -- [x] [Fast-SCNN (ArXiv'2019)](configs/fastscnn) -- [x] [ISANet (ArXiv'2019/IJCV'2021)](configs/isanet) -- [x] [OCRNet (ECCV'2020)](configs/ocrnet) -- [x] [DNLNet (ECCV'2020)](configs/dnlnet) -- [x] [PointRend (CVPR'2020)](configs/point_rend) -- [x] [CGNet (TIP'2020)](configs/cgnet) -- [x] [BiSeNetV2 (IJCV'2021)](configs/bisenetv2) -- [x] [STDC (CVPR'2021)](configs/stdc) -- [x] [SETR (CVPR'2021)](configs/setr) -- [x] [DPT (ArXiv'2021)](configs/dpt) -- [x] [Segmenter (ICCV'2021)](configs/segmenter) -- [x] [SegFormer (NeurIPS'2021)](configs/segformer) -- [x] [K-Net (NeurIPS'2021)](configs/knet) - -Supported datasets: - -- [x] [Cityscapes](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#cityscapes) -- [x] [PASCAL VOC](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#pascal-voc) -- [x] [ADE20K](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#ade20k) -- [x] [Pascal Context](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#pascal-context) -- [x] [COCO-Stuff 10k](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#coco-stuff-10k) -- [x] [COCO-Stuff 164k](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#coco-stuff-164k) -- [x] [CHASE_DB1](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#chase-db1) -- [x] [DRIVE](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#drive) -- [x] [HRF](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#hrf) -- [x] [STARE](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#stare) -- [x] [Dark Zurich](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#dark-zurich) -- [x] [Nighttime Driving](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#nighttime-driving) -- [x] [LoveDA](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#loveda) -- [x] [Potsdam](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#isprs-potsdam) -- [x] [Vaihingen](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#isprs-vaihingen) -- [x] [iSAID](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#isaid) - -## Installation - -Please refer to [get_started.md](docs/en/get_started.md#installation) for installation and [dataset_prepare.md](docs/en/dataset_prepare.md#prepare-datasets) for dataset preparation. - -## Get Started - -Please see [train.md](docs/en/train.md) and [inference.md](docs/en/inference.md) for the basic usage of MMSegmentation. -There are also tutorials for [customizing dataset](docs/en/tutorials/customize_datasets.md), [designing data pipeline](docs/en/tutorials/data_pipeline.md), [customizing modules](docs/en/tutorials/customize_models.md), and [customizing runtime](docs/en/tutorials/customize_runtime.md). -We also provide many [training tricks](docs/en/tutorials/training_tricks.md) for better training and [useful tools](docs/en/useful_tools.md) for deployment. - -A Colab tutorial is also provided. You may preview the notebook [here](demo/MMSegmentation_Tutorial.ipynb) or directly [run](https://colab.research.google.com/github/open-mmlab/mmsegmentation/blob/master/demo/MMSegmentation_Tutorial.ipynb) on Colab. - -Please refer to [FAQ](docs/en/faq.md) for frequently asked questions. - -## Citation - -If you find this project useful in your research, please consider cite: - -```bibtex -@misc{mmseg2020, - title={{MMSegmentation}: OpenMMLab Semantic Segmentation Toolbox and Benchmark}, - author={MMSegmentation Contributors}, - howpublished = {\url{https://github.com/open-mmlab/mmsegmentation}}, - year={2020} -} -``` - -## Contributing - -We appreciate all contributions to improve MMSegmentation. Please refer to [CONTRIBUTING.md](.github/CONTRIBUTING.md) for the contributing guideline. - -## Acknowledgement - -MMSegmentation is an open source project that welcome any contribution and feedback. -We wish that the toolbox and benchmark could serve the growing research -community by providing a flexible as well as standardized toolkit to reimplement existing methods -and develop their own new semantic segmentation methods. - -## Projects in OpenMMLab - -- [MMCV](https://github.com/open-mmlab/mmcv): OpenMMLab foundational library for computer vision. -- [MIM](https://github.com/open-mmlab/mim): MIM installs OpenMMLab packages. -- [MMClassification](https://github.com/open-mmlab/mmclassification): OpenMMLab image classification toolbox and benchmark. -- [MMDetection](https://github.com/open-mmlab/mmdetection): OpenMMLab detection toolbox and benchmark. -- [MMDetection3D](https://github.com/open-mmlab/mmdetection3d): OpenMMLab's next-generation platform for general 3D object detection. -- [MMRotate](https://github.com/open-mmlab/mmrotate): OpenMMLab rotated object detection toolbox and benchmark. -- [MMSegmentation](https://github.com/open-mmlab/mmsegmentation): OpenMMLab semantic segmentation toolbox and benchmark. -- [MMOCR](https://github.com/open-mmlab/mmocr): OpenMMLab text detection, recognition, and understanding toolbox. -- [MMPose](https://github.com/open-mmlab/mmpose): OpenMMLab pose estimation toolbox and benchmark. -- [MMHuman3D](https://github.com/open-mmlab/mmhuman3d): OpenMMLab 3D human parametric model toolbox and benchmark. -- [MMSelfSup](https://github.com/open-mmlab/mmselfsup): OpenMMLab self-supervised learning toolbox and benchmark. -- [MMRazor](https://github.com/open-mmlab/mmrazor): OpenMMLab model compression toolbox and benchmark. -- [MMFewShot](https://github.com/open-mmlab/mmfewshot): OpenMMLab fewshot learning toolbox and benchmark. -- [MMAction2](https://github.com/open-mmlab/mmaction2): OpenMMLab's next-generation action understanding toolbox and benchmark. -- [MMTracking](https://github.com/open-mmlab/mmtracking): OpenMMLab video perception toolbox and benchmark. -- [MMFlow](https://github.com/open-mmlab/mmflow): OpenMMLab optical flow toolbox and benchmark. -- [MMEditing](https://github.com/open-mmlab/mmediting): OpenMMLab image and video editing toolbox. -- [MMGeneration](https://github.com/open-mmlab/mmgeneration): OpenMMLab image and video generative models toolbox. -- [MMDeploy](https://github.com/open-mmlab/mmdeploy): OpenMMLab Model Deployment Framework. diff --git a/cv/semantic_segmentation/pspnet/pytorch/README_zh-CN.md b/cv/semantic_segmentation/pspnet/pytorch/README_zh-CN.md deleted file mode 100644 index f7b3a91cd..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/README_zh-CN.md +++ /dev/null @@ -1,220 +0,0 @@ -
- -
 
-
- OpenMMLab 官网 - - - HOT - - -      - OpenMMLab 开放平台 - - - TRY IT OUT - - -
-
 
-
-
- -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/mmsegmentation)](https://pypi.org/project/mmsegmentation/) -[![PyPI](https://img.shields.io/pypi/v/mmsegmentation)](https://pypi.org/project/mmsegmentation) -[![docs](https://img.shields.io/badge/docs-latest-blue)](https://mmsegmentation.readthedocs.io/zh_CN/latest/) -[![badge](https://github.com/open-mmlab/mmsegmentation/workflows/build/badge.svg)](https://github.com/open-mmlab/mmsegmentation/actions) -[![codecov](https://codecov.io/gh/open-mmlab/mmsegmentation/branch/master/graph/badge.svg)](https://codecov.io/gh/open-mmlab/mmsegmentation) -[![license](https://img.shields.io/github/license/open-mmlab/mmsegmentation.svg)](https://github.com/open-mmlab/mmsegmentation/blob/master/LICENSE) -[![issue resolution](https://isitmaintained.com/badge/resolution/open-mmlab/mmsegmentation.svg)](https://github.com/open-mmlab/mmsegmentation/issues) -[![open issues](https://isitmaintained.com/badge/open/open-mmlab/mmsegmentation.svg)](https://github.com/open-mmlab/mmsegmentation/issues) - -文档: https://mmsegmentation.readthedocs.io/zh_CN/latest - -[English](README.md) | 简体中文 - -## 简介 - -MMSegmentation 是一个基于 PyTorch 的语义分割开源工具箱。它是 OpenMMLab 项目的一部分。 - -主分支代码目前支持 PyTorch 1.5 以上的版本。 - -![示例图片](resources/seg_demo.gif) - -### 主要特性 - -- **统一的基准平台** - - 我们将各种各样的语义分割算法集成到了一个统一的工具箱,进行基准测试。 - -- **模块化设计** - - MMSegmentation 将分割框架解耦成不同的模块组件,通过组合不同的模块组件,用户可以便捷地构建自定义的分割模型。 - -- **丰富的即插即用的算法和模型** - - MMSegmentation 支持了众多主流的和最新的检测算法,例如 PSPNet,DeepLabV3,PSANet,DeepLabV3+ 等. - -- **速度快** - - 训练速度比其他语义分割代码库更快或者相当。 - -## 开源许可证 - -该项目采用 [Apache 2.0 开源许可证](LICENSE)。 - -## 更新日志 - -最新版本 v0.24.1 在 2022.5.1 发布。 -如果想了解更多版本更新细节和历史信息,请阅读[更新日志](docs/en/changelog.md)。 - -## 基准测试和模型库 - -测试结果和模型可以在[模型库](docs/zh_cn/model_zoo.md)中找到。 - -已支持的骨干网络: - -- [x] ResNet (CVPR'2016) -- [x] ResNeXt (CVPR'2017) -- [x] [HRNet (CVPR'2019)](configs/hrnet) -- [x] [ResNeSt (ArXiv'2020)](configs/resnest) -- [x] [MobileNetV2 (CVPR'2018)](configs/mobilenet_v2) -- [x] [MobileNetV3 (ICCV'2019)](configs/mobilenet_v3) -- [x] [Vision Transformer (ICLR'2021)](configs/vit) -- [x] [Swin Transformer (ICCV'2021)](configs/swin) -- [x] [Twins (NeurIPS'2021)](configs/twins) -- [x] [BEiT (ICLR'2022)](configs/beit) -- [x] [ConvNeXt (CVPR'2022)](configs/convnext) -- [x] [MAE (CVPR'2022)](configs/mae) - -已支持的算法: - -- [x] [FCN (CVPR'2015/TPAMI'2017)](configs/fcn) -- [x] [ERFNet (T-ITS'2017)](configs/erfnet) -- [x] [UNet (MICCAI'2016/Nat. Methods'2019)](configs/unet) -- [x] [PSPNet (CVPR'2017)](configs/pspnet) -- [x] [DeepLabV3 (ArXiv'2017)](configs/deeplabv3) -- [x] [BiSeNetV1 (ECCV'2018)](configs/bisenetv1) -- [x] [PSANet (ECCV'2018)](configs/psanet) -- [x] [DeepLabV3+ (CVPR'2018)](configs/deeplabv3plus) -- [x] [UPerNet (ECCV'2018)](configs/upernet) -- [x] [ICNet (ECCV'2018)](configs/icnet) -- [x] [NonLocal Net (CVPR'2018)](configs/nonlocal_net) -- [x] [EncNet (CVPR'2018)](configs/encnet) -- [x] [Semantic FPN (CVPR'2019)](configs/sem_fpn) -- [x] [DANet (CVPR'2019)](configs/danet) -- [x] [APCNet (CVPR'2019)](configs/apcnet) -- [x] [EMANet (ICCV'2019)](configs/emanet) -- [x] [CCNet (ICCV'2019)](configs/ccnet) -- [x] [DMNet (ICCV'2019)](configs/dmnet) -- [x] [ANN (ICCV'2019)](configs/ann) -- [x] [GCNet (ICCVW'2019/TPAMI'2020)](configs/gcnet) -- [x] [FastFCN (ArXiv'2019)](configs/fastfcn) -- [x] [Fast-SCNN (ArXiv'2019)](configs/fastscnn) -- [x] [ISANet (ArXiv'2019/IJCV'2021)](configs/isanet) -- [x] [OCRNet (ECCV'2020)](configs/ocrnet) -- [x] [DNLNet (ECCV'2020)](configs/dnlnet) -- [x] [PointRend (CVPR'2020)](configs/point_rend) -- [x] [CGNet (TIP'2020)](configs/cgnet) -- [x] [BiSeNetV2 (IJCV'2021)](configs/bisenetv2) -- [x] [STDC (CVPR'2021)](configs/stdc) -- [x] [SETR (CVPR'2021)](configs/setr) -- [x] [DPT (ArXiv'2021)](configs/dpt) -- [x] [Segmenter (ICCV'2021)](configs/segmenter) -- [x] [SegFormer (NeurIPS'2021)](configs/segformer) -- [x] [K-Net (NeurIPS'2021)](configs/knet) - -已支持的数据集: - -- [x] [Cityscapes](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#cityscapes) -- [x] [PASCAL VOC](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#pascal-voc) -- [x] [ADE20K](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#ade20k) -- [x] [Pascal Context](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#pascal-context) -- [x] [COCO-Stuff 10k](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#coco-stuff-10k) -- [x] [COCO-Stuff 164k](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#coco-stuff-164k) -- [x] [CHASE_DB1](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#chase-db1) -- [x] [DRIVE](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#drive) -- [x] [HRF](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#hrf) -- [x] [STARE](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#stare) -- [x] [Dark Zurich](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#dark-zurich) -- [x] [Nighttime Driving](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#nighttime-driving) -- [x] [LoveDA](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#loveda) -- [x] [Potsdam](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#isprs-potsdam) -- [x] [Vaihingen](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#isprs-vaihingen) -- [x] [iSAID](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#isaid) - -## 安装 - -请参考[快速入门文档](docs/zh_cn/get_started.md#installation)进行安装,参考[数据集准备](docs/zh_cn/dataset_prepare.md)处理数据。 - -## 快速入门 - -请参考[训练教程](docs/zh_cn/train.md)和[测试教程](docs/zh_cn/inference.md)学习 MMSegmentation 的基本使用。 -我们也提供了一些进阶教程,内容覆盖了[增加自定义数据集](docs/zh_cn/tutorials/customize_datasets.md),[设计新的数据预处理流程](docs/zh_cn/tutorials/data_pipeline.md),[增加自定义模型](docs/zh_cn/tutorials/customize_models.md),[增加自定义的运行时配置](docs/zh_cn/tutorials/customize_runtime.md)。 -除此之外,我们也提供了很多实用的[训练技巧说明](docs/zh_cn/tutorials/training_tricks.md)和模型部署相关的[有用的工具](docs/zh_cn/useful_tools.md)。 - -同时,我们提供了 Colab 教程。你可以在[这里](demo/MMSegmentation_Tutorial.ipynb)浏览教程,或者直接在 Colab 上[运行](https://colab.research.google.com/github/open-mmlab/mmsegmentation/blob/master/demo/MMSegmentation_Tutorial.ipynb)。 - -如果遇到问题,请参考 [常见问题解答](docs/zh_cn/faq.md)。 - -## 引用 - -如果你觉得本项目对你的研究工作有所帮助,请参考如下 bibtex 引用 MMSegmentation。 - -```bibtex -@misc{mmseg2020, - title={{MMSegmentation}: OpenMMLab Semantic Segmentation Toolbox and Benchmark}, - author={MMSegmentation Contributors}, - howpublished = {\url{https://github.com/open-mmlab/mmsegmentation}}, - year={2020} -} -``` - -## 贡献指南 - -我们感谢所有的贡献者为改进和提升 MMSegmentation 所作出的努力。请参考[贡献指南](.github/CONTRIBUTING.md)来了解参与项目贡献的相关指引。 - -## 致谢 - -MMSegmentation 是一个由来自不同高校和企业的研发人员共同参与贡献的开源项目。我们感谢所有为项目提供算法复现和新功能支持的贡献者,以及提供宝贵反馈的用户。 我们希望这个工具箱和基准测试可以为社区提供灵活的代码工具,供用户复现已有算法并开发自己的新模型,从而不断为开源社区提供贡献。 - -## OpenMMLab 的其他项目 - -- [MMCV](https://github.com/open-mmlab/mmcv): OpenMMLab 计算机视觉基础库 -- [MIM](https://github.com/open-mmlab/mim): MIM 是 OpenMMlab 项目、算法、模型的统一入口 -- [MMClassification](https://github.com/open-mmlab/mmclassification): OpenMMLab 图像分类工具箱 -- [MMDetection](https://github.com/open-mmlab/mmdetection): OpenMMLab 目标检测工具箱 -- [MMDetection3D](https://github.com/open-mmlab/mmdetection3d): OpenMMLab 新一代通用 3D 目标检测平台 -- [MMRotate](https://github.com/open-mmlab/mmrotate): OpenMMLab 旋转框检测工具箱与测试基准 -- [MMSegmentation](https://github.com/open-mmlab/mmsegmentation): OpenMMLab 语义分割工具箱 -- [MMOCR](https://github.com/open-mmlab/mmocr): OpenMMLab 全流程文字检测识别理解工具包 -- [MMPose](https://github.com/open-mmlab/mmpose): OpenMMLab 姿态估计工具箱 -- [MMHuman3D](https://github.com/open-mmlab/mmhuman3d): OpenMMLab 人体参数化模型工具箱与测试基准 -- [MMSelfSup](https://github.com/open-mmlab/mmselfsup): OpenMMLab 自监督学习工具箱与测试基准 -- [MMRazor](https://github.com/open-mmlab/mmrazor): OpenMMLab 模型压缩工具箱与测试基准 -- [MMFewShot](https://github.com/open-mmlab/mmfewshot): OpenMMLab 少样本学习工具箱与测试基准 -- [MMAction2](https://github.com/open-mmlab/mmaction2): OpenMMLab 新一代视频理解工具箱 -- [MMTracking](https://github.com/open-mmlab/mmtracking): OpenMMLab 一体化视频目标感知平台 -- [MMFlow](https://github.com/open-mmlab/mmflow): OpenMMLab 光流估计工具箱与测试基准 -- [MMEditing](https://github.com/open-mmlab/mmediting): OpenMMLab 图像视频编辑工具箱 -- [MMGeneration](https://github.com/open-mmlab/mmgeneration): OpenMMLab 图片视频生成模型工具箱 -- [MMDeploy](https://github.com/open-mmlab/mmdeploy): OpenMMLab 模型部署框架 - -## 欢迎加入 OpenMMLab 社区 - - 扫描下方的二维码可关注 OpenMMLab 团队的 [知乎官方账号](https://www.zhihu.com/people/openmmlab),加入 [OpenMMLab 团队](https://jq.qq.com/?_wv=1027&k=aCvMxdr3) 以及 [MMSegmentation](https://jq.qq.com/?_wv=1027&k=ukevz6Ie) 的 QQ 群。 - -
- -
- - 我们会在 OpenMMLab 社区为大家 - -- 📢 分享 AI 框架的前沿核心技术 -- 💻 解读 PyTorch 常用模块源码 -- 📰 发布 OpenMMLab 的相关新闻 -- 🚀 介绍 OpenMMLab 开发的前沿算法 -- 🏃 获取更高效的问题答疑和意见反馈 -- 🔥 提供与各行各业开发者充分交流的平台 - - 干货满满 📘,等你来撩 💗,OpenMMLab 社区期待您的加入 👬 diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/ade20k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/ade20k.py deleted file mode 100644 index efc8b4bb2..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/ade20k.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'ADE20KDataset' -data_root = 'data/ade/ADEChallengeData2016' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/ade20k_640x640.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/ade20k_640x640.py deleted file mode 100644 index 14a4bb092..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/ade20k_640x640.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'ADE20KDataset' -data_root = 'data/ade/ADEChallengeData2016' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (640, 640) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2560, 640), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2560, 640), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/chase_db1.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/chase_db1.py deleted file mode 100644 index 298594ea9..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/chase_db1.py +++ /dev/null @@ -1,59 +0,0 @@ -# dataset settings -dataset_type = 'ChaseDB1Dataset' -data_root = 'data/CHASE_DB1' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -img_scale = (960, 999) -crop_size = (128, 128) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']) -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']) - ]) -] - -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type='RepeatDataset', - times=40000, - dataset=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline)), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes.py deleted file mode 100644 index f21867c63..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'CityscapesDataset' -data_root = 'data/cityscapes/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 1024) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/train', - ann_dir='gtFine/train', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/val', - ann_dir='gtFine/val', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/val', - ann_dir='gtFine/val', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py deleted file mode 100644 index f98d92972..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (1024, 1024) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_768x768.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_768x768.py deleted file mode 100644 index fde9d7c7d..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_768x768.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (768, 768) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2049, 1025), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py deleted file mode 100644 index 336c7b254..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_769x769.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (769, 769) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2049, 1025), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_832x832.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_832x832.py deleted file mode 100644 index b9325cc00..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/cityscapes_832x832.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (832, 832) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/coco-stuff10k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/coco-stuff10k.py deleted file mode 100644 index ec0496928..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/coco-stuff10k.py +++ /dev/null @@ -1,57 +0,0 @@ -# dataset settings -dataset_type = 'COCOStuffDataset' -data_root = 'data/coco_stuff10k' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/train2014', - ann_dir='annotations/train2014', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/test2014', - ann_dir='annotations/test2014', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/test2014', - ann_dir='annotations/test2014', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/coco-stuff164k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/coco-stuff164k.py deleted file mode 100644 index a6a38f2ac..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/coco-stuff164k.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'COCOStuffDataset' -data_root = 'data/coco_stuff164k' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/train2017', - ann_dir='annotations/train2017', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/val2017', - ann_dir='annotations/val2017', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/val2017', - ann_dir='annotations/val2017', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/datasets.md b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/datasets.md deleted file mode 100644 index c66d75efe..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/datasets.md +++ /dev/null @@ -1,135 +0,0 @@ -# Datasets arrangement format - -```shell -├── data -│ ├── cityscapes -│ │ ├── leftImg8bit -│ │ │ ├── train -│ │ │ ├── val -│ │ ├── gtFine -│ │ │ ├── train -│ │ │ ├── val -│ ├── VOCdevkit -│ │ ├── VOC2012 -│ │ │ ├── JPEGImages -│ │ │ ├── SegmentationClass -│ │ │ ├── ImageSets -│ │ │ │ ├── Segmentation -│ │ ├── VOC2010 -│ │ │ ├── JPEGImages -│ │ │ ├── SegmentationClassContext -│ │ │ ├── ImageSets -│ │ │ │ ├── SegmentationContext -│ │ │ │ │ ├── train.txt -│ │ │ │ │ ├── val.txt -│ │ │ ├── trainval_merged.json -│ │ ├── VOCaug -│ │ │ ├── dataset -│ │ │ │ ├── cls -│ ├── ade -│ │ ├── ADEChallengeData2016 -│ │ │ ├── annotations -│ │ │ │ ├── training -│ │ │ │ ├── validation -│ │ │ ├── images -│ │ │ │ ├── training -│ │ │ │ ├── validation -│ ├── coco_stuff10k -│ │ ├── images -│ │ │ ├── train2014 -│ │ │ ├── test2014 -│ │ ├── annotations -│ │ │ ├── train2014 -│ │ │ ├── test2014 -│ │ ├── imagesLists -│ │ │ ├── train.txt -│ │ │ ├── test.txt -│ │ │ ├── all.txt -│ ├── coco_stuff164k -│ │ ├── images -│ │ │ ├── train2017 -│ │ │ ├── val2017 -│ │ ├── annotations -│ │ │ ├── train2017 -│ │ │ ├── val2017 -│ ├── CHASE_DB1 -│ │ ├── images -│ │ │ ├── training -│ │ │ ├── validation -│ │ ├── annotations -│ │ │ ├── training -│ │ │ ├── validation -│ ├── DRIVE -│ │ ├── images -│ │ │ ├── training -│ │ │ ├── validation -│ │ ├── annotations -│ │ │ ├── training -│ │ │ ├── validation -│ ├── HRF -│ │ ├── images -│ │ │ ├── training -│ │ │ ├── validation -│ │ ├── annotations -│ │ │ ├── training -│ │ │ ├── validation -│ ├── STARE -│ │ ├── images -│ │ │ ├── training -│ │ │ ├── validation -│ │ ├── annotations -│ │ │ ├── training -│ │ │ ├── validation -| ├── dark_zurich -| │ ├── gps -| │ │ ├── val -| │ │ └── val_ref -| │ ├── gt -| │ │ └── val -| │ ├── LICENSE.txt -| │ ├── lists_file_names -| │ │ ├── val_filenames.txt -| │ │ └── val_ref_filenames.txt -| │ ├── README.md -| │ └── rgb_anon -| │ | ├── val -| │ | └── val_ref -| ├── NighttimeDrivingTest -| | ├── gtCoarse_daytime_trainvaltest -| | │ └── test -| | │ └── night -| | └── leftImg8bit -| | | └── test -| | | └── night -│ ├── loveDA -│ │ ├── img_dir -│ │ │ ├── train -│ │ │ ├── val -│ │ │ ├── test -│ │ ├── ann_dir -│ │ │ ├── train -│ │ │ ├── val -│ ├── potsdam -│ │ ├── img_dir -│ │ │ ├── train -│ │ │ ├── val -│ │ ├── ann_dir -│ │ │ ├── train -│ │ │ ├── val -│ ├── vaihingen -│ │ ├── img_dir -│ │ │ ├── train -│ │ │ ├── val -│ │ ├── ann_dir -│ │ │ ├── train -│ │ │ ├── val -│ ├── iSAID -│ │ ├── img_dir -│ │ │ ├── train -│ │ │ ├── val -│ │ │ ├── test -│ │ ├── ann_dir -│ │ │ ├── train -│ │ │ ├── val - -``` diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/drive.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/drive.py deleted file mode 100644 index 06e8ff606..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/drive.py +++ /dev/null @@ -1,59 +0,0 @@ -# dataset settings -dataset_type = 'DRIVEDataset' -data_root = 'data/DRIVE' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -img_scale = (584, 565) -crop_size = (64, 64) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']) -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']) - ]) -] - -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type='RepeatDataset', - times=40000, - dataset=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline)), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/hrf.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/hrf.py deleted file mode 100644 index 242d790eb..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/hrf.py +++ /dev/null @@ -1,59 +0,0 @@ -# dataset settings -dataset_type = 'HRFDataset' -data_root = 'data/HRF' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -img_scale = (2336, 3504) -crop_size = (256, 256) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']) -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']) - ]) -] - -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type='RepeatDataset', - times=40000, - dataset=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline)), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/isaid.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/isaid.py deleted file mode 100644 index 8e4c26abb..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/isaid.py +++ /dev/null @@ -1,62 +0,0 @@ -# dataset settings -dataset_type = 'iSAIDDataset' -data_root = 'data/iSAID' - -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -""" -This crop_size setting is followed by the implementation of -`PointFlow: Flowing Semantics Through Points for Aerial Image -Segmentation `_. -""" - -crop_size = (896, 896) - -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(896, 896), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(896, 896), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='img_dir/train', - ann_dir='ann_dir/train', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='img_dir/val', - ann_dir='ann_dir/val', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='img_dir/val', - ann_dir='ann_dir/val', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/loveda.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/loveda.py deleted file mode 100644 index e55335695..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/loveda.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'LoveDADataset' -data_root = 'data/loveDA' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(1024, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='img_dir/train', - ann_dir='ann_dir/train', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='img_dir/val', - ann_dir='ann_dir/val', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='img_dir/val', - ann_dir='ann_dir/val', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_context.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_context.py deleted file mode 100644 index ff65bad1b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_context.py +++ /dev/null @@ -1,60 +0,0 @@ -# dataset settings -dataset_type = 'PascalContextDataset' -data_root = 'data/VOCdevkit/VOC2010/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) - -img_scale = (520, 520) -crop_size = (480, 480) - -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_context_59.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_context_59.py deleted file mode 100644 index 37585abab..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_context_59.py +++ /dev/null @@ -1,60 +0,0 @@ -# dataset settings -dataset_type = 'PascalContextDataset59' -data_root = 'data/VOCdevkit/VOC2010/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) - -img_scale = (520, 520) -crop_size = (480, 480) - -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_voc12.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_voc12.py deleted file mode 100644 index e5ff704ae..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_voc12.py +++ /dev/null @@ -1,58 +0,0 @@ -# dataset settings - -dataset_type = 'PascalVOCDataset' -data_root = 'data/VOCdevkit/VOC2012' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py deleted file mode 100644 index 3f23b6717..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/pascal_voc12_aug.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = './pascal_voc12.py' -# dataset settings -data = dict( - train=dict( - ann_dir=['SegmentationClass', 'SegmentationClassAug'], - split=[ - 'ImageSets/Segmentation/train.txt', - 'ImageSets/Segmentation/aug.txt' - ])) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/potsdam.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/potsdam.py deleted file mode 100644 index f74c4a56c..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/potsdam.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'PotsdamDataset' -data_root = 'data/potsdam' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(512, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(512, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='img_dir/train', - ann_dir='ann_dir/train', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='img_dir/val', - ann_dir='ann_dir/val', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='img_dir/val', - ann_dir='ann_dir/val', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/stare.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/stare.py deleted file mode 100644 index 3f71b2548..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/stare.py +++ /dev/null @@ -1,59 +0,0 @@ -# dataset settings -dataset_type = 'STAREDataset' -data_root = 'data/STARE' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -img_scale = (605, 700) -crop_size = (128, 128) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']) -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']) - ]) -] - -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type='RepeatDataset', - times=40000, - dataset=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline)), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/vaihingen.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/vaihingen.py deleted file mode 100644 index c0df282c4..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/datasets/vaihingen.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'ISPRSDataset' -data_root = 'data/vaihingen' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(512, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(512, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='img_dir/train', - ann_dir='ann_dir/train', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='img_dir/val', - ann_dir='ann_dir/val', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='img_dir/val', - ann_dir='ann_dir/val', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/default_runtime.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/default_runtime.py deleted file mode 100644 index b564cc4e7..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/default_runtime.py +++ /dev/null @@ -1,14 +0,0 @@ -# yapf:disable -log_config = dict( - interval=50, - hooks=[ - dict(type='TextLoggerHook', by_epoch=False), - # dict(type='TensorboardLoggerHook') - ]) -# yapf:enable -dist_params = dict(backend='nccl') -log_level = 'INFO' -load_from = None -resume_from = None -workflow = [('train', 1)] -cudnn_benchmark = True diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/models/pspnet_r50-d8.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/models/pspnet_r50-d8.py deleted file mode 100644 index f451e08ad..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/models/pspnet_r50-d8.py +++ /dev/null @@ -1,44 +0,0 @@ -# model settings -norm_cfg = dict(type='SyncBN', requires_grad=True) -model = dict( - type='EncoderDecoder', - pretrained='open-mmlab://resnet50_v1c', - backbone=dict( - type='ResNetV1c', - depth=50, - num_stages=4, - out_indices=(0, 1, 2, 3), - dilations=(1, 1, 2, 4), - strides=(1, 2, 1, 1), - norm_cfg=norm_cfg, - norm_eval=False, - style='pytorch', - contract_dilation=True), - decode_head=dict( - type='PSPHead', - in_channels=2048, - in_index=3, - channels=512, - pool_scales=(1, 2, 3, 6), - dropout_ratio=0.1, - num_classes=19, - norm_cfg=norm_cfg, - align_corners=False, - loss_decode=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), - auxiliary_head=dict( - type='FCNHead', - in_channels=1024, - in_index=2, - channels=256, - num_convs=1, - concat_input=False, - dropout_ratio=0.1, - num_classes=19, - norm_cfg=norm_cfg, - align_corners=False, - loss_decode=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)), - # model training and testing settings - train_cfg=dict(), - test_cfg=dict(mode='whole')) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_160k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_160k.py deleted file mode 100644 index 39630f215..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_160k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=160000) -checkpoint_config = dict(by_epoch=False, interval=16000) -evaluation = dict(interval=16000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_1k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_1k.py deleted file mode 100644 index 04cf41030..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_1k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=1000) -checkpoint_config = dict(by_epoch=False, interval=1000) -evaluation = dict(interval=1000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_20k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_20k.py deleted file mode 100644 index 73c702197..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_20k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=20000) -checkpoint_config = dict(by_epoch=False, interval=2000) -evaluation = dict(interval=2000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_320k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_320k.py deleted file mode 100644 index a0b230626..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_320k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=320000) -checkpoint_config = dict(by_epoch=False, interval=32000) -evaluation = dict(interval=32000, metric='mIoU') diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_40k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_40k.py deleted file mode 100644 index d2c502325..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_40k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=40000) -checkpoint_config = dict(by_epoch=False, interval=4000) -evaluation = dict(interval=4000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_80k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_80k.py deleted file mode 100644 index 8365a878e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/_base_/schedules/schedule_80k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=80000) -checkpoint_config = dict(by_epoch=False, interval=8000) -evaluation = dict(interval=8000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/README.md b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/README.md deleted file mode 100644 index a87111074..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/README.md +++ /dev/null @@ -1,177 +0,0 @@ -# PSPNet - -[Pyramid Scene Parsing Network](https://arxiv.org/abs/1612.01105) - -## Introduction - - - -Official Repo - -Code Snippet - -## Abstract - - - -Scene parsing is challenging for unrestricted open vocabulary and diverse scenes. In this paper, we exploit the capability of global context information by different-region-based context aggregation through our pyramid pooling module together with the proposed pyramid scene parsing network (PSPNet). Our global prior representation is effective to produce good quality results on the scene parsing task, while PSPNet provides a superior framework for pixel-level prediction tasks. The proposed approach achieves state-of-the-art performance on various datasets. It came first in ImageNet scene parsing challenge 2016, PASCAL VOC 2012 benchmark and Cityscapes benchmark. A single PSPNet yields new record of mIoU accuracy 85.4% on PASCAL VOC 2012 and accuracy 80.2% on Cityscapes. - - - -
- -
- -## Citation - -```bibtex -@inproceedings{zhao2017pspnet, - title={Pyramid Scene Parsing Network}, - author={Zhao, Hengshuang and Shi, Jianping and Qi, Xiaojuan and Wang, Xiaogang and Jia, Jiaya}, - booktitle={CVPR}, - year={2017} -} -``` - -```bibtex -@article{wightman2021resnet, - title={Resnet strikes back: An improved training procedure in timm}, - author={Wightman, Ross and Touvron, Hugo and J{\'e}gou, Herv{\'e}}, - journal={arXiv preprint arXiv:2110.00476}, - year={2021} -} -``` - -## Results and models - -### Cityscapes - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------------- | ------------- | --------- | ------: | -------- | -------------- | ----: | ------------: | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| PSPNet | R-50-D8 | 512x1024 | 40000 | 6.1 | 4.07 | 77.85 | 79.18 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338-2966598c.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338.log.json) | -| PSPNet | R-101-D8 | 512x1024 | 40000 | 9.6 | 2.68 | 78.34 | 79.74 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes/pspnet_r101-d8_512x1024_40k_cityscapes_20200604_232751-467e7cf4.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes/pspnet_r101-d8_512x1024_40k_cityscapes_20200604_232751.log.json) | -| PSPNet | R-50-D8 | 769x769 | 40000 | 6.9 | 1.76 | 78.26 | 79.88 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_769x769_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_769x769_40k_cityscapes/pspnet_r50-d8_769x769_40k_cityscapes_20200606_112725-86638686.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_769x769_40k_cityscapes/pspnet_r50-d8_769x769_40k_cityscapes_20200606_112725.log.json) | -| PSPNet | R-101-D8 | 769x769 | 40000 | 10.9 | 1.15 | 79.08 | 80.28 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_769x769_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_769x769_40k_cityscapes/pspnet_r101-d8_769x769_40k_cityscapes_20200606_112753-61c6f5be.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_769x769_40k_cityscapes/pspnet_r101-d8_769x769_40k_cityscapes_20200606_112753.log.json) | -| PSPNet | R-18-D8 | 512x1024 | 80000 | 1.7 | 15.71 | 74.87 | 76.04 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r18-d8_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_512x1024_80k_cityscapes/pspnet_r18-d8_512x1024_80k_cityscapes_20201225_021458-09ffa746.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_512x1024_80k_cityscapes/pspnet_r18-d8_512x1024_80k_cityscapes-20201225_021458.log.json) | -| PSPNet | R-50-D8 | 512x1024 | 80000 | - | - | 78.55 | 79.79 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes/pspnet_r50-d8_512x1024_80k_cityscapes_20200606_112131-2376f12b.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes/pspnet_r50-d8_512x1024_80k_cityscapes_20200606_112131.log.json) | -| PSPNet | R-50b-D8 rsb | 512x1024 | 80000 | 6.2 | 3.82 | 78.47 | 79.45 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_rsb-pretrain_512x1024_adamw_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_rsb-pretrain_512x1024_adamw_80k_cityscapes/pspnet_r50-d8_rsb-pretrain_512x1024_adamw_80k_cityscapes_20220315_123238-588c30be.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_rsb-pretrain_512x1024_adamw_80k_cityscapes/pspnet_r50-d8_rsb-pretrain_512x1024_adamw_80k_cityscapes_20220315_123238.log.json) | -| PSPNet | R-101-D8 | 512x1024 | 80000 | - | - | 79.76 | 81.01 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes/pspnet_r101-d8_512x1024_80k_cityscapes_20200606_112211-e1e1100f.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes/pspnet_r101-d8_512x1024_80k_cityscapes_20200606_112211.log.json) | -| PSPNet (FP16) | R-101-D8 | 512x1024 | 80000 | 5.34 | 8.77 | 79.46 | - | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_fp16_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_fp16_512x1024_80k_cityscapes/pspnet_r101-d8_fp16_512x1024_80k_cityscapes_20200717_230919-a0875e5c.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_fp16_512x1024_80k_cityscapes/pspnet_r101-d8_fp16_512x1024_80k_cityscapes_20200717_230919.log.json) | -| PSPNet | R-18-D8 | 769x769 | 80000 | 1.9 | 6.20 | 75.90 | 77.86 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r18-d8_769x769_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_769x769_80k_cityscapes/pspnet_r18-d8_769x769_80k_cityscapes_20201225_021458-3deefc62.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_769x769_80k_cityscapes/pspnet_r18-d8_769x769_80k_cityscapes-20201225_021458.log.json) | -| PSPNet | R-50-D8 | 769x769 | 80000 | - | - | 79.59 | 80.69 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_769x769_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_769x769_80k_cityscapes/pspnet_r50-d8_769x769_80k_cityscapes_20200606_210121-5ccf03dd.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_769x769_80k_cityscapes/pspnet_r50-d8_769x769_80k_cityscapes_20200606_210121.log.json) | -| PSPNet | R-101-D8 | 769x769 | 80000 | - | - | 79.77 | 81.06 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_769x769_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_769x769_80k_cityscapes/pspnet_r101-d8_769x769_80k_cityscapes_20200606_225055-dba412fa.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_769x769_80k_cityscapes/pspnet_r101-d8_769x769_80k_cityscapes_20200606_225055.log.json) | -| PSPNet | R-18b-D8 | 512x1024 | 80000 | 1.5 | 16.28 | 74.23 | 75.79 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r18b-d8_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18b-d8_512x1024_80k_cityscapes/pspnet_r18b-d8_512x1024_80k_cityscapes_20201226_063116-26928a60.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18b-d8_512x1024_80k_cityscapes/pspnet_r18b-d8_512x1024_80k_cityscapes-20201226_063116.log.json) | -| PSPNet | R-50b-D8 | 512x1024 | 80000 | 6.0 | 4.30 | 78.22 | 79.46 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50b-d8_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50b-d8_512x1024_80k_cityscapes/pspnet_r50b-d8_512x1024_80k_cityscapes_20201225_094315-6344287a.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50b-d8_512x1024_80k_cityscapes/pspnet_r50b-d8_512x1024_80k_cityscapes-20201225_094315.log.json) | -| PSPNet | R-101b-D8 | 512x1024 | 80000 | 9.5 | 2.76 | 79.69 | 80.79 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes/pspnet_r101b-d8_512x1024_80k_cityscapes_20201226_170012-3a4d38ab.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes/pspnet_r101b-d8_512x1024_80k_cityscapes-20201226_170012.log.json) | -| PSPNet | R-18b-D8 | 769x769 | 80000 | 1.7 | 6.41 | 74.92 | 76.90 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r18b-d8_769x769_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18b-d8_769x769_80k_cityscapes/pspnet_r18b-d8_769x769_80k_cityscapes_20201226_080942-bf98d186.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18b-d8_769x769_80k_cityscapes/pspnet_r18b-d8_769x769_80k_cityscapes-20201226_080942.log.json) | -| PSPNet | R-50b-D8 | 769x769 | 80000 | 6.8 | 1.88 | 78.50 | 79.96 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50b-d8_769x769_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50b-d8_769x769_80k_cityscapes/pspnet_r50b-d8_769x769_80k_cityscapes_20201225_094316-4c643cf6.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50b-d8_769x769_80k_cityscapes/pspnet_r50b-d8_769x769_80k_cityscapes-20201225_094316.log.json) | -| PSPNet | R-101b-D8 | 769x769 | 80000 | 10.8 | 1.17 | 78.87 | 80.04 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101b-d8_769x769_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101b-d8_769x769_80k_cityscapes/pspnet_r101b-d8_769x769_80k_cityscapes_20201226_171823-f0e7c293.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101b-d8_769x769_80k_cityscapes/pspnet_r101b-d8_769x769_80k_cityscapes-20201226_171823.log.json) | -| PSPNet | R-50-D32 | 512x1024 | 80000 | 3.0 | 15.21 | 73.88 | 76.85 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d32_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d32_512x1024_80k_cityscapes/pspnet_r50-d32_512x1024_80k_cityscapes_20220316_224840-9092b254.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d32_512x1024_80k_cityscapes/pspnet_r50-d32_512x1024_80k_cityscapes_20220316_224840.log.json) | -| PSPNet | R-50b-D32 rsb | 512x1024 | 80000 | 3.1 | 16.08 | 74.09 | 77.18 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d32_rsb-pretrain_512x1024_adamw_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d32_rsb-pretrain_512x1024_adamw_80k_cityscapes/pspnet_r50-d32_rsb-pretrain_512x1024_adamw_80k_cityscapes_20220316_141229-dd9c9610.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d32_rsb-pretrain_512x1024_adamw_80k_cityscapes/pspnet_r50-d32_rsb-pretrain_512x1024_adamw_80k_cityscapes_20220316_141229.log.json) | -| PSPNet | R-50b-D32 | 512x1024 | 80000 | 2.9 | 15.41 | 72.61 | 75.51 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50b-d32_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50b-d32_512x1024_80k_cityscapes/pspnet_r50b-d32_512x1024_80k_cityscapes_20220311_152152-23bcaf8c.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50b-d32_512x1024_80k_cityscapes/pspnet_r50b-d32_512x1024_80k_cityscapes_20220311_152152.log.json) | - -### ADE20K - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| PSPNet | R-50-D8 | 512x512 | 80000 | 8.5 | 23.53 | 41.13 | 41.94 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x512_80k_ade20k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_80k_ade20k/pspnet_r50-d8_512x512_80k_ade20k_20200615_014128-15a8b914.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_80k_ade20k/pspnet_r50-d8_512x512_80k_ade20k_20200615_014128.log.json) | -| PSPNet | R-101-D8 | 512x512 | 80000 | 12 | 15.30 | 43.57 | 44.35 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x512_80k_ade20k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_80k_ade20k/pspnet_r101-d8_512x512_80k_ade20k_20200614_031423-b6e782f0.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_80k_ade20k/pspnet_r101-d8_512x512_80k_ade20k_20200614_031423.log.json) | -| PSPNet | R-50-D8 | 512x512 | 160000 | - | - | 42.48 | 43.44 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x512_160k_ade20k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_160k_ade20k/pspnet_r50-d8_512x512_160k_ade20k_20200615_184358-1890b0bd.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_160k_ade20k/pspnet_r50-d8_512x512_160k_ade20k_20200615_184358.log.json) | -| PSPNet | R-101-D8 | 512x512 | 160000 | - | - | 44.39 | 45.35 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x512_160k_ade20k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_160k_ade20k/pspnet_r101-d8_512x512_160k_ade20k_20200615_100650-967c316f.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_160k_ade20k/pspnet_r101-d8_512x512_160k_ade20k_20200615_100650.log.json) | - -### Pascal VOC 2012 + Aug - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| PSPNet | R-50-D8 | 512x512 | 20000 | 6.1 | 23.59 | 76.78 | 77.61 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x512_20k_voc12aug.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_20k_voc12aug/pspnet_r50-d8_512x512_20k_voc12aug_20200617_101958-ed5dfbd9.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_20k_voc12aug/pspnet_r50-d8_512x512_20k_voc12aug_20200617_101958.log.json) | -| PSPNet | R-101-D8 | 512x512 | 20000 | 9.6 | 15.02 | 78.47 | 79.25 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x512_20k_voc12aug.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_20k_voc12aug/pspnet_r101-d8_512x512_20k_voc12aug_20200617_102003-4aef3c9a.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_20k_voc12aug/pspnet_r101-d8_512x512_20k_voc12aug_20200617_102003.log.json) | -| PSPNet | R-50-D8 | 512x512 | 40000 | - | - | 77.29 | 78.48 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x512_40k_voc12aug.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_40k_voc12aug/pspnet_r50-d8_512x512_40k_voc12aug_20200613_161222-ae9c1b8c.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_40k_voc12aug/pspnet_r50-d8_512x512_40k_voc12aug_20200613_161222.log.json) | -| PSPNet | R-101-D8 | 512x512 | 40000 | - | - | 78.52 | 79.57 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x512_40k_voc12aug.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_40k_voc12aug/pspnet_r101-d8_512x512_40k_voc12aug_20200613_161222-bc933b18.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_40k_voc12aug/pspnet_r101-d8_512x512_40k_voc12aug_20200613_161222.log.json) | - -### Pascal Context - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| PSPNet | R-101-D8 | 480x480 | 40000 | 8.8 | 9.68 | 46.60 | 47.78 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_480x480_40k_pascal_context.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_480x480_40k_pascal_context/pspnet_r101-d8_480x480_40k_pascal_context_20200911_211210-bf0f5d7c.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_480x480_40k_pascal_context/pspnet_r101-d8_480x480_40k_pascal_context-20200911_211210.log.json) | -| PSPNet | R-101-D8 | 480x480 | 80000 | - | - | 46.03 | 47.15 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_480x480_80k_pascal_context.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_480x480_80k_pascal_context/pspnet_r101-d8_480x480_80k_pascal_context_20200911_190530-c86d6233.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_480x480_80k_pascal_context/pspnet_r101-d8_480x480_80k_pascal_context-20200911_190530.log.json) | - -### Pascal Context 59 - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| PSPNet | R-101-D8 | 480x480 | 40000 | - | - | 52.02 | 53.54 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_480x480_40k_pascal_context_59.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_480x480_40k_pascal_context_59/pspnet_r101-d8_480x480_40k_pascal_context_59_20210416_114524-86d44cd4.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_480x480_40k_pascal_context_59/pspnet_r101-d8_480x480_40k_pascal_context_59-20210416_114524.log.json) | -| PSPNet | R-101-D8 | 480x480 | 80000 | - | - | 52.47 | 53.99 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_480x480_80k_pascal_context_59.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_480x480_80k_pascal_context_59/pspnet_r101-d8_480x480_80k_pascal_context_59_20210416_114418-fa6caaa2.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_480x480_80k_pascal_context_59/pspnet_r101-d8_480x480_80k_pascal_context_59-20210416_114418.log.json) | - -### Dark Zurich and Nighttime Driving - -We support evaluation results on these two datasets using models above trained on Cityscapes training set. - -| Method | Backbone | Training Dataset | Test Dataset | mIoU | config | evaluation checkpoint | -| ------ | --------- | ----------------------- | ------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| PSPNet | R-50-D8 | Cityscapes Training set | Dark Zurich | 10.91 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x1024_40k_dark.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338-2966598c.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338.log.json) | -| PSPNet | R-50-D8 | Cityscapes Training set | Nighttime Driving | 23.02 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x1024_40k_night_driving.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338-2966598c.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338.log.json) | -| PSPNet | R-50-D8 | Cityscapes Training set | Cityscapes Validation set | 77.85 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338-2966598c.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338.log.json) | -| PSPNet | R-101-D8 | Cityscapes Training set | Dark Zurich | 10.16 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x1024_40k_dark.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes/pspnet_r101-d8_512x1024_40k_cityscapes_20200604_232751-467e7cf4.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes/pspnet_r101-d8_512x1024_40k_cityscapes_20200604_232751.log.json) | -| PSPNet | R-101-D8 | Cityscapes Training set | Nighttime Driving | 20.25 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x1024_40k_night_driving.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes/pspnet_r101-d8_512x1024_40k_cityscapes_20200604_232751-467e7cf4.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes/pspnet_r101-d8_512x1024_40k_cityscapes_20200604_232751.log.json) | -| PSPNet | R-101-D8 | Cityscapes Training set | Cityscapes Validation set | 78.34 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes/pspnet_r101-d8_512x1024_40k_cityscapes_20200604_232751-467e7cf4.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes/pspnet_r101-d8_512x1024_40k_cityscapes_20200604_232751.log.json) | -| PSPNet | R-101b-D8 | Cityscapes Training set | Dark Zurich | 15.54 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101b-d8_512x1024_80k_dark.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes/pspnet_r101b-d8_512x1024_80k_cityscapes_20201226_170012-3a4d38ab.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes/pspnet_r101b-d8_512x1024_80k_cityscapes-20201226_170012.log.json) | -| PSPNet | R-101b-D8 | Cityscapes Training set | Nighttime Driving | 22.25 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101b-d8_512x1024_80k_night_driving.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes/pspnet_r101b-d8_512x1024_80k_cityscapes_20201226_170012-3a4d38ab.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes/pspnet_r101b-d8_512x1024_80k_cityscapes-20201226_170012.log.json) | -| PSPNet | R-101b-D8 | Cityscapes Training set | Cityscapes Validation set | 79.69 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes/pspnet_r101b-d8_512x1024_80k_cityscapes_20201226_170012-3a4d38ab.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes/pspnet_r101b-d8_512x1024_80k_cityscapes-20201226_170012.log.json) | - -### COCO-Stuff 10k - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| PSPNet | R-50-D8 | 512x512 | 20000 | 9.6 | 20.5 | 35.69 | 36.62 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k/pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k_20210820_203258-b88df27f.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k/pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k_20210820_203258.log.json) | -| PSPNet | R-101-D8 | 512x512 | 20000 | 13.2 | 11.1 | 37.26 | 38.52 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x512_4x4_20k_coco-stuff10k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_20k_coco-stuff10k/pspnet_r101-d8_512x512_4x4_20k_coco-stuff10k_20210820_232135-76aae482.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_20k_coco-stuff10k/pspnet_r101-d8_512x512_4x4_20k_coco-stuff10k_20210820_232135.log.json) | -| PSPNet | R-50-D8 | 512x512 | 40000 | - | - | 36.33 | 37.24 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k/pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k_20210821_030857-92e2902b.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k/pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k_20210821_030857.log.json) | -| PSPNet | R-101-D8 | 512x512 | 40000 | - | - | 37.76 | 38.86 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x512_4x4_40k_coco-stuff10k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_40k_coco-stuff10k/pspnet_r101-d8_512x512_4x4_40k_coco-stuff10k_20210821_014022-831aec95.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_40k_coco-stuff10k/pspnet_r101-d8_512x512_4x4_40k_coco-stuff10k_20210821_014022.log.json) | - -### COCO-Stuff 164k - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | ----------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| PSPNet | R-50-D8 | 512x512 | 80000 | 9.6 | 20.5 | 38.80 | 39.19 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k/pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k_20210707_152034-0e41b2db.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k/pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k_20210707_152034.log.json) | -| PSPNet | R-101-D8 | 512x512 | 80000 | 13.2 | 11.1 | 40.34 | 40.79 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x512_4x4_80k_coco-stuff164k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_80k_coco-stuff164k/pspnet_r101-d8_512x512_4x4_80k_coco-stuff164k_20210707_152034-7eb41789.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_80k_coco-stuff164k/pspnet_r101-d8_512x512_4x4_80k_coco-stuff164k_20210707_152034.log.json) | -| PSPNet | R-50-D8 | 512x512 | 160000 | - | - | 39.64 | 39.97 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k/pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k_20210707_152004-51276a57.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k/pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k_20210707_152004.log.json) | -| PSPNet | R-101-D8 | 512x512 | 160000 | - | - | 41.28 | 41.66 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x512_4x4_160k_coco-stuff164k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_160k_coco-stuff164k/pspnet_r101-d8_512x512_4x4_160k_coco-stuff164k_20210707_152004-4af9621b.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_160k_coco-stuff164k/pspnet_r101-d8_512x512_4x4_160k_coco-stuff164k_20210707_152004.log.json) | -| PSPNet | R-50-D8 | 512x512 | 320000 | - | - | 40.53 | 40.75 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k/pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k_20210707_152004-be9610cc.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k/pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k_20210707_152004.log.json) | -| PSPNet | R-101-D8 | 512x512 | 320000 | - | - | 41.95 | 42.42 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x512_4x4_320k_coco-stuff164k.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_320k_coco-stuff164k/pspnet_r101-d8_512x512_4x4_320k_coco-stuff164k_20210707_152004-72220c60.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_320k_coco-stuff164k/pspnet_r101-d8_512x512_4x4_320k_coco-stuff164k_20210707_152004.log.json) | - -### LoveDA - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| PSPNet | R-18-D8 | 512x512 | 80000 | 1.45 | 26.87 | 48.62 | 47.57 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r18-d8_512x512_80k_loveda.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_512x512_80k_loveda/pspnet_r18-d8_512x512_80k_loveda_20211105_052100-b97697f1.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_512x512_80k_loveda/pspnet_r18-d8_512x512_80k_loveda_20211105_052100.log.json) | -| PSPNet | R-50-D8 | 512x512 | 80000 | 6.14 | 6.60 | 50.46 | 50.19 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_512x512_80k_loveda.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_80k_loveda/pspnet_r50-d8_512x512_80k_loveda_20211104_155728-88610f9f.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_80k_loveda/pspnet_r50-d8_512x512_80k_loveda_20211104_155728.log.json) | -| PSPNet | R-101-D8 | 512x512 | 80000 | 9.61 | 4.58 | 51.86 | 51.34 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_512x512_80k_loveda.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_80k_loveda/pspnet_r101-d8_512x512_80k_loveda_20211104_153212-1c06c6a8.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_80k_loveda/pspnet_r101-d8_512x512_80k_loveda_20211104_153212.log.json) | - -### Potsdam - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| PSPNet | R-18-D8 | 512x512 | 80000 | 1.50 | 85.12 | 77.09 | 78.30 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r18-d8_4x4_512x512_80k_potsdam.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_4x4_512x512_80k_potsdam/pspnet_r18-d8_4x4_512x512_80k_potsdam_20211220_125612-7cd046e1.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_4x4_512x512_80k_potsdam/pspnet_r18-d8_4x4_512x512_80k_potsdam_20211220_125612.log.json) | -| PSPNet | R-50-D8 | 512x512 | 80000 | 6.14 | 30.21 | 78.12 | 78.98 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_4x4_512x512_80k_potsdam.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_4x4_512x512_80k_potsdam/pspnet_r50-d8_4x4_512x512_80k_potsdam_20211219_043541-2dd5fe67.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_4x4_512x512_80k_potsdam/pspnet_r50-d8_4x4_512x512_80k_potsdam_20211219_043541.log.json) | -| PSPNet | R-101-D8 | 512x512 | 80000 | 9.61 | 19.40 | 78.62 | 79.47 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_4x4_512x512_80k_potsdam.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_4x4_512x512_80k_potsdam/pspnet_r101-d8_4x4_512x512_80k_potsdam_20211220_125612-aed036c4.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_4x4_512x512_80k_potsdam/pspnet_r101-d8_4x4_512x512_80k_potsdam_20211220_125612.log.json) | - -### Vaihingen - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| PSPNet | R-18-D8 | 512x512 | 80000 | 1.45 | 85.06 | 71.46 | 73.36 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r18-d8_4x4_512x512_80k_vaihingen.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_4x4_512x512_80k_vaihingen/pspnet_r18-d8_4x4_512x512_80k_vaihingen_20211228_160355-52a8a6f6.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_4x4_512x512_80k_vaihingen/pspnet_r18-d8_4x4_512x512_80k_vaihingen_20211228_160355.log.json) | -| PSPNet | R-50-D8 | 512x512 | 80000 | 6.14 | 30.29 | 72.36 | 73.75 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_4x4_512x512_80k_vaihingen.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_4x4_512x512_80k_vaihingen/pspnet_r50-d8_4x4_512x512_80k_vaihingen_20211228_160355-382f8f5b.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_4x4_512x512_80k_vaihingen/pspnet_r50-d8_4x4_512x512_80k_vaihingen_20211228_160355.log.json) | -| PSPNet | R-101-D8 | 512x512 | 80000 | 9.61 | 19.97 | 72.61 | 74.18 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r101-d8_4x4_512x512_80k_vaihingen.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_4x4_512x512_80k_vaihingen/pspnet_r101-d8_4x4_512x512_80k_vaihingen_20211231_230806-8eba0a09.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_4x4_512x512_80k_vaihingen/pspnet_r101-d8_4x4_512x512_80k_vaihingen_20211231_230806.log.json) | - -### iSAID - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| ------ | -------- | --------- | ------: | -------- | -------------- | ----: | ------------: | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| PSPNet | R-18-D8 | 896x896 | 80000 | 4.52 | 26.91 | 60.22 | 61.25 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r18-d8_4x4_896x896_80k_isaid.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_4x4_896x896_80k_isaid/pspnet_r18-d8_4x4_896x896_80k_isaid_20220110_180526-e84c0b6a.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_4x4_896x896_80k_isaid/pspnet_r18-d8_4x4_896x896_80k_isaid_20220110_180526.log.json) | -| PSPNet | R-50-D8 | 896x896 | 80000 | 16.58 | 8.88 | 65.36 | 66.48 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/pspnet/pspnet_r50-d8_4x4_896x896_80k_isaid.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_4x4_896x896_80k_isaid/pspnet_r50-d8_4x4_896x896_80k_isaid_20220110_180629-1f21dc32.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_4x4_896x896_80k_isaid/pspnet_r50-d8_4x4_896x896_80k_isaid_20220110_180629.log.json) | - -Note: - -- `FP16` means Mixed Precision (FP16) is adopted in training. -- `896x896` is the Crop Size of iSAID dataset, which is followed by the implementation of [PointFlow: Flowing Semantics Through Points for Aerial Image Segmentation](https://arxiv.org/pdf/2103.06564.pdf) -- `rsb` is short for 'Resnet strikes back'. -- The `b` in `R-50b` means ResNetV1b, which is a standard ResNet backbone. In MMSegmentation, default backbone is ResNetV1c, which usually performs better in semantic segmentation task. diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet.yml b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet.yml deleted file mode 100644 index abbaf6c16..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet.yml +++ /dev/null @@ -1,1077 +0,0 @@ -Collections: -- Name: PSPNet - Metadata: - Training Data: - - Cityscapes - - ADE20K - - Pascal VOC 2012 + Aug - - Pascal Context - - Pascal Context 59 - - Dark Zurich and Nighttime Driving - - COCO-Stuff 10k - - COCO-Stuff 164k - - LoveDA - - Potsdam - - Vaihingen - - iSAID - Paper: - URL: https://arxiv.org/abs/1612.01105 - Title: Pyramid Scene Parsing Network - README: configs/pspnet/README.md - Code: - URL: https://github.com/open-mmlab/mmsegmentation/blob/v0.17.0/mmseg/models/decode_heads/psp_head.py#L63 - Version: v0.17.0 - Converted From: - Code: https://github.com/hszhao/PSPNet -Models: -- Name: pspnet_r50-d8_512x1024_40k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,1024) - lr schd: 40000 - inference time (ms/im): - - value: 245.7 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 6.1 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 77.85 - mIoU(ms+flip): 79.18 - Config: configs/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes/pspnet_r50-d8_512x1024_40k_cityscapes_20200605_003338-2966598c.pth -- Name: pspnet_r101-d8_512x1024_40k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,1024) - lr schd: 40000 - inference time (ms/im): - - value: 373.13 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 9.6 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 78.34 - mIoU(ms+flip): 79.74 - Config: configs/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes/pspnet_r101-d8_512x1024_40k_cityscapes_20200604_232751-467e7cf4.pth -- Name: pspnet_r50-d8_769x769_40k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (769,769) - lr schd: 40000 - inference time (ms/im): - - value: 568.18 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (769,769) - Training Memory (GB): 6.9 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 78.26 - mIoU(ms+flip): 79.88 - Config: configs/pspnet/pspnet_r50-d8_769x769_40k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_769x769_40k_cityscapes/pspnet_r50-d8_769x769_40k_cityscapes_20200606_112725-86638686.pth -- Name: pspnet_r101-d8_769x769_40k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (769,769) - lr schd: 40000 - inference time (ms/im): - - value: 869.57 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (769,769) - Training Memory (GB): 10.9 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 79.08 - mIoU(ms+flip): 80.28 - Config: configs/pspnet/pspnet_r101-d8_769x769_40k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_769x769_40k_cityscapes/pspnet_r101-d8_769x769_40k_cityscapes_20200606_112753-61c6f5be.pth -- Name: pspnet_r18-d8_512x1024_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-18-D8 - crop size: (512,1024) - lr schd: 80000 - inference time (ms/im): - - value: 63.65 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 1.7 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 74.87 - mIoU(ms+flip): 76.04 - Config: configs/pspnet/pspnet_r18-d8_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_512x1024_80k_cityscapes/pspnet_r18-d8_512x1024_80k_cityscapes_20201225_021458-09ffa746.pth -- Name: pspnet_r50-d8_512x1024_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,1024) - lr schd: 80000 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 78.55 - mIoU(ms+flip): 79.79 - Config: configs/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes/pspnet_r50-d8_512x1024_80k_cityscapes_20200606_112131-2376f12b.pth -- Name: pspnet_r50-d8_rsb-pretrain_512x1024_adamw_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-50b-D8 rsb - crop size: (512,1024) - lr schd: 80000 - inference time (ms/im): - - value: 261.78 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 6.2 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 78.47 - mIoU(ms+flip): 79.45 - Config: configs/pspnet/pspnet_r50-d8_rsb-pretrain_512x1024_adamw_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_rsb-pretrain_512x1024_adamw_80k_cityscapes/pspnet_r50-d8_rsb-pretrain_512x1024_adamw_80k_cityscapes_20220315_123238-588c30be.pth -- Name: pspnet_r101-d8_512x1024_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,1024) - lr schd: 80000 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 79.76 - mIoU(ms+flip): 81.01 - Config: configs/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes/pspnet_r101-d8_512x1024_80k_cityscapes_20200606_112211-e1e1100f.pth -- Name: pspnet_r101-d8_fp16_512x1024_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,1024) - lr schd: 80000 - inference time (ms/im): - - value: 114.03 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP16 - resolution: (512,1024) - Training Memory (GB): 5.34 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 79.46 - Config: configs/pspnet/pspnet_r101-d8_fp16_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_fp16_512x1024_80k_cityscapes/pspnet_r101-d8_fp16_512x1024_80k_cityscapes_20200717_230919-a0875e5c.pth -- Name: pspnet_r18-d8_769x769_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-18-D8 - crop size: (769,769) - lr schd: 80000 - inference time (ms/im): - - value: 161.29 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (769,769) - Training Memory (GB): 1.9 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 75.9 - mIoU(ms+flip): 77.86 - Config: configs/pspnet/pspnet_r18-d8_769x769_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_769x769_80k_cityscapes/pspnet_r18-d8_769x769_80k_cityscapes_20201225_021458-3deefc62.pth -- Name: pspnet_r50-d8_769x769_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (769,769) - lr schd: 80000 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 79.59 - mIoU(ms+flip): 80.69 - Config: configs/pspnet/pspnet_r50-d8_769x769_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_769x769_80k_cityscapes/pspnet_r50-d8_769x769_80k_cityscapes_20200606_210121-5ccf03dd.pth -- Name: pspnet_r101-d8_769x769_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (769,769) - lr schd: 80000 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 79.77 - mIoU(ms+flip): 81.06 - Config: configs/pspnet/pspnet_r101-d8_769x769_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_769x769_80k_cityscapes/pspnet_r101-d8_769x769_80k_cityscapes_20200606_225055-dba412fa.pth -- Name: pspnet_r18b-d8_512x1024_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-18b-D8 - crop size: (512,1024) - lr schd: 80000 - inference time (ms/im): - - value: 61.43 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 1.5 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 74.23 - mIoU(ms+flip): 75.79 - Config: configs/pspnet/pspnet_r18b-d8_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18b-d8_512x1024_80k_cityscapes/pspnet_r18b-d8_512x1024_80k_cityscapes_20201226_063116-26928a60.pth -- Name: pspnet_r50b-d8_512x1024_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-50b-D8 - crop size: (512,1024) - lr schd: 80000 - inference time (ms/im): - - value: 232.56 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 6.0 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 78.22 - mIoU(ms+flip): 79.46 - Config: configs/pspnet/pspnet_r50b-d8_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50b-d8_512x1024_80k_cityscapes/pspnet_r50b-d8_512x1024_80k_cityscapes_20201225_094315-6344287a.pth -- Name: pspnet_r101b-d8_512x1024_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-101b-D8 - crop size: (512,1024) - lr schd: 80000 - inference time (ms/im): - - value: 362.32 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 9.5 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 79.69 - mIoU(ms+flip): 80.79 - Config: configs/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes/pspnet_r101b-d8_512x1024_80k_cityscapes_20201226_170012-3a4d38ab.pth -- Name: pspnet_r18b-d8_769x769_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-18b-D8 - crop size: (769,769) - lr schd: 80000 - inference time (ms/im): - - value: 156.01 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (769,769) - Training Memory (GB): 1.7 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 74.92 - mIoU(ms+flip): 76.9 - Config: configs/pspnet/pspnet_r18b-d8_769x769_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18b-d8_769x769_80k_cityscapes/pspnet_r18b-d8_769x769_80k_cityscapes_20201226_080942-bf98d186.pth -- Name: pspnet_r50b-d8_769x769_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-50b-D8 - crop size: (769,769) - lr schd: 80000 - inference time (ms/im): - - value: 531.91 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (769,769) - Training Memory (GB): 6.8 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 78.5 - mIoU(ms+flip): 79.96 - Config: configs/pspnet/pspnet_r50b-d8_769x769_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50b-d8_769x769_80k_cityscapes/pspnet_r50b-d8_769x769_80k_cityscapes_20201225_094316-4c643cf6.pth -- Name: pspnet_r101b-d8_769x769_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-101b-D8 - crop size: (769,769) - lr schd: 80000 - inference time (ms/im): - - value: 854.7 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (769,769) - Training Memory (GB): 10.8 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 78.87 - mIoU(ms+flip): 80.04 - Config: configs/pspnet/pspnet_r101b-d8_769x769_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101b-d8_769x769_80k_cityscapes/pspnet_r101b-d8_769x769_80k_cityscapes_20201226_171823-f0e7c293.pth -- Name: pspnet_r50-d32_512x1024_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-50-D32 - crop size: (512,1024) - lr schd: 80000 - inference time (ms/im): - - value: 65.75 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 3.0 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 73.88 - mIoU(ms+flip): 76.85 - Config: configs/pspnet/pspnet_r50-d32_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d32_512x1024_80k_cityscapes/pspnet_r50-d32_512x1024_80k_cityscapes_20220316_224840-9092b254.pth -- Name: pspnet_r50-d32_rsb-pretrain_512x1024_adamw_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-50b-D32 rsb - crop size: (512,1024) - lr schd: 80000 - inference time (ms/im): - - value: 62.19 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 3.1 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 74.09 - mIoU(ms+flip): 77.18 - Config: configs/pspnet/pspnet_r50-d32_rsb-pretrain_512x1024_adamw_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d32_rsb-pretrain_512x1024_adamw_80k_cityscapes/pspnet_r50-d32_rsb-pretrain_512x1024_adamw_80k_cityscapes_20220316_141229-dd9c9610.pth -- Name: pspnet_r50b-d32_512x1024_80k_cityscapes - In Collection: PSPNet - Metadata: - backbone: R-50b-D32 - crop size: (512,1024) - lr schd: 80000 - inference time (ms/im): - - value: 64.89 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 2.9 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 72.61 - mIoU(ms+flip): 75.51 - Config: configs/pspnet/pspnet_r50b-d32_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50b-d32_512x1024_80k_cityscapes/pspnet_r50b-d32_512x1024_80k_cityscapes_20220311_152152-23bcaf8c.pth -- Name: pspnet_r50-d8_512x512_80k_ade20k - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 42.5 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 8.5 - Results: - - Task: Semantic Segmentation - Dataset: ADE20K - Metrics: - mIoU: 41.13 - mIoU(ms+flip): 41.94 - Config: configs/pspnet/pspnet_r50-d8_512x512_80k_ade20k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_80k_ade20k/pspnet_r50-d8_512x512_80k_ade20k_20200615_014128-15a8b914.pth -- Name: pspnet_r101-d8_512x512_80k_ade20k - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 65.36 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 12.0 - Results: - - Task: Semantic Segmentation - Dataset: ADE20K - Metrics: - mIoU: 43.57 - mIoU(ms+flip): 44.35 - Config: configs/pspnet/pspnet_r101-d8_512x512_80k_ade20k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_80k_ade20k/pspnet_r101-d8_512x512_80k_ade20k_20200614_031423-b6e782f0.pth -- Name: pspnet_r50-d8_512x512_160k_ade20k - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 160000 - Results: - - Task: Semantic Segmentation - Dataset: ADE20K - Metrics: - mIoU: 42.48 - mIoU(ms+flip): 43.44 - Config: configs/pspnet/pspnet_r50-d8_512x512_160k_ade20k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_160k_ade20k/pspnet_r50-d8_512x512_160k_ade20k_20200615_184358-1890b0bd.pth -- Name: pspnet_r101-d8_512x512_160k_ade20k - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 160000 - Results: - - Task: Semantic Segmentation - Dataset: ADE20K - Metrics: - mIoU: 44.39 - mIoU(ms+flip): 45.35 - Config: configs/pspnet/pspnet_r101-d8_512x512_160k_ade20k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_160k_ade20k/pspnet_r101-d8_512x512_160k_ade20k_20200615_100650-967c316f.pth -- Name: pspnet_r50-d8_512x512_20k_voc12aug - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 20000 - inference time (ms/im): - - value: 42.39 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 6.1 - Results: - - Task: Semantic Segmentation - Dataset: Pascal VOC 2012 + Aug - Metrics: - mIoU: 76.78 - mIoU(ms+flip): 77.61 - Config: configs/pspnet/pspnet_r50-d8_512x512_20k_voc12aug.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_20k_voc12aug/pspnet_r50-d8_512x512_20k_voc12aug_20200617_101958-ed5dfbd9.pth -- Name: pspnet_r101-d8_512x512_20k_voc12aug - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 20000 - inference time (ms/im): - - value: 66.58 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 9.6 - Results: - - Task: Semantic Segmentation - Dataset: Pascal VOC 2012 + Aug - Metrics: - mIoU: 78.47 - mIoU(ms+flip): 79.25 - Config: configs/pspnet/pspnet_r101-d8_512x512_20k_voc12aug.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_20k_voc12aug/pspnet_r101-d8_512x512_20k_voc12aug_20200617_102003-4aef3c9a.pth -- Name: pspnet_r50-d8_512x512_40k_voc12aug - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 40000 - Results: - - Task: Semantic Segmentation - Dataset: Pascal VOC 2012 + Aug - Metrics: - mIoU: 77.29 - mIoU(ms+flip): 78.48 - Config: configs/pspnet/pspnet_r50-d8_512x512_40k_voc12aug.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_40k_voc12aug/pspnet_r50-d8_512x512_40k_voc12aug_20200613_161222-ae9c1b8c.pth -- Name: pspnet_r101-d8_512x512_40k_voc12aug - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 40000 - Results: - - Task: Semantic Segmentation - Dataset: Pascal VOC 2012 + Aug - Metrics: - mIoU: 78.52 - mIoU(ms+flip): 79.57 - Config: configs/pspnet/pspnet_r101-d8_512x512_40k_voc12aug.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_40k_voc12aug/pspnet_r101-d8_512x512_40k_voc12aug_20200613_161222-bc933b18.pth -- Name: pspnet_r101-d8_480x480_40k_pascal_context - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (480,480) - lr schd: 40000 - inference time (ms/im): - - value: 103.31 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (480,480) - Training Memory (GB): 8.8 - Results: - - Task: Semantic Segmentation - Dataset: Pascal Context - Metrics: - mIoU: 46.6 - mIoU(ms+flip): 47.78 - Config: configs/pspnet/pspnet_r101-d8_480x480_40k_pascal_context.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_480x480_40k_pascal_context/pspnet_r101-d8_480x480_40k_pascal_context_20200911_211210-bf0f5d7c.pth -- Name: pspnet_r101-d8_480x480_80k_pascal_context - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (480,480) - lr schd: 80000 - Results: - - Task: Semantic Segmentation - Dataset: Pascal Context - Metrics: - mIoU: 46.03 - mIoU(ms+flip): 47.15 - Config: configs/pspnet/pspnet_r101-d8_480x480_80k_pascal_context.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_480x480_80k_pascal_context/pspnet_r101-d8_480x480_80k_pascal_context_20200911_190530-c86d6233.pth -- Name: pspnet_r101-d8_480x480_40k_pascal_context_59 - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (480,480) - lr schd: 40000 - Results: - - Task: Semantic Segmentation - Dataset: Pascal Context 59 - Metrics: - mIoU: 52.02 - mIoU(ms+flip): 53.54 - Config: configs/pspnet/pspnet_r101-d8_480x480_40k_pascal_context_59.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_480x480_40k_pascal_context_59/pspnet_r101-d8_480x480_40k_pascal_context_59_20210416_114524-86d44cd4.pth -- Name: pspnet_r101-d8_480x480_80k_pascal_context_59 - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (480,480) - lr schd: 80000 - Results: - - Task: Semantic Segmentation - Dataset: Pascal Context 59 - Metrics: - mIoU: 52.47 - mIoU(ms+flip): 53.99 - Config: configs/pspnet/pspnet_r101-d8_480x480_80k_pascal_context_59.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_480x480_80k_pascal_context_59/pspnet_r101-d8_480x480_80k_pascal_context_59_20210416_114418-fa6caaa2.pth -- Name: pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 20000 - inference time (ms/im): - - value: 48.78 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 9.6 - Results: - - Task: Semantic Segmentation - Dataset: COCO-Stuff 10k - Metrics: - mIoU: 35.69 - mIoU(ms+flip): 36.62 - Config: configs/pspnet/pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k/pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k_20210820_203258-b88df27f.pth -- Name: pspnet_r101-d8_512x512_4x4_20k_coco-stuff10k - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 20000 - inference time (ms/im): - - value: 90.09 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 13.2 - Results: - - Task: Semantic Segmentation - Dataset: COCO-Stuff 10k - Metrics: - mIoU: 37.26 - mIoU(ms+flip): 38.52 - Config: configs/pspnet/pspnet_r101-d8_512x512_4x4_20k_coco-stuff10k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_20k_coco-stuff10k/pspnet_r101-d8_512x512_4x4_20k_coco-stuff10k_20210820_232135-76aae482.pth -- Name: pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 40000 - Results: - - Task: Semantic Segmentation - Dataset: COCO-Stuff 10k - Metrics: - mIoU: 36.33 - mIoU(ms+flip): 37.24 - Config: configs/pspnet/pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k/pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k_20210821_030857-92e2902b.pth -- Name: pspnet_r101-d8_512x512_4x4_40k_coco-stuff10k - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 40000 - Results: - - Task: Semantic Segmentation - Dataset: COCO-Stuff 10k - Metrics: - mIoU: 37.76 - mIoU(ms+flip): 38.86 - Config: configs/pspnet/pspnet_r101-d8_512x512_4x4_40k_coco-stuff10k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_40k_coco-stuff10k/pspnet_r101-d8_512x512_4x4_40k_coco-stuff10k_20210821_014022-831aec95.pth -- Name: pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 48.78 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 9.6 - Results: - - Task: Semantic Segmentation - Dataset: COCO-Stuff 164k - Metrics: - mIoU: 38.8 - mIoU(ms+flip): 39.19 - Config: configs/pspnet/pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k/pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k_20210707_152034-0e41b2db.pth -- Name: pspnet_r101-d8_512x512_4x4_80k_coco-stuff164k - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 90.09 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 13.2 - Results: - - Task: Semantic Segmentation - Dataset: COCO-Stuff 164k - Metrics: - mIoU: 40.34 - mIoU(ms+flip): 40.79 - Config: configs/pspnet/pspnet_r101-d8_512x512_4x4_80k_coco-stuff164k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_80k_coco-stuff164k/pspnet_r101-d8_512x512_4x4_80k_coco-stuff164k_20210707_152034-7eb41789.pth -- Name: pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 160000 - Results: - - Task: Semantic Segmentation - Dataset: COCO-Stuff 164k - Metrics: - mIoU: 39.64 - mIoU(ms+flip): 39.97 - Config: configs/pspnet/pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k/pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k_20210707_152004-51276a57.pth -- Name: pspnet_r101-d8_512x512_4x4_160k_coco-stuff164k - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 160000 - Results: - - Task: Semantic Segmentation - Dataset: COCO-Stuff 164k - Metrics: - mIoU: 41.28 - mIoU(ms+flip): 41.66 - Config: configs/pspnet/pspnet_r101-d8_512x512_4x4_160k_coco-stuff164k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_160k_coco-stuff164k/pspnet_r101-d8_512x512_4x4_160k_coco-stuff164k_20210707_152004-4af9621b.pth -- Name: pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 320000 - Results: - - Task: Semantic Segmentation - Dataset: COCO-Stuff 164k - Metrics: - mIoU: 40.53 - mIoU(ms+flip): 40.75 - Config: configs/pspnet/pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k/pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k_20210707_152004-be9610cc.pth -- Name: pspnet_r101-d8_512x512_4x4_320k_coco-stuff164k - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 320000 - Results: - - Task: Semantic Segmentation - Dataset: COCO-Stuff 164k - Metrics: - mIoU: 41.95 - mIoU(ms+flip): 42.42 - Config: configs/pspnet/pspnet_r101-d8_512x512_4x4_320k_coco-stuff164k.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_4x4_320k_coco-stuff164k/pspnet_r101-d8_512x512_4x4_320k_coco-stuff164k_20210707_152004-72220c60.pth -- Name: pspnet_r18-d8_512x512_80k_loveda - In Collection: PSPNet - Metadata: - backbone: R-18-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 37.22 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 1.45 - Results: - - Task: Semantic Segmentation - Dataset: LoveDA - Metrics: - mIoU: 48.62 - mIoU(ms+flip): 47.57 - Config: configs/pspnet/pspnet_r18-d8_512x512_80k_loveda.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_512x512_80k_loveda/pspnet_r18-d8_512x512_80k_loveda_20211105_052100-b97697f1.pth -- Name: pspnet_r50-d8_512x512_80k_loveda - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 151.52 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 6.14 - Results: - - Task: Semantic Segmentation - Dataset: LoveDA - Metrics: - mIoU: 50.46 - mIoU(ms+flip): 50.19 - Config: configs/pspnet/pspnet_r50-d8_512x512_80k_loveda.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_512x512_80k_loveda/pspnet_r50-d8_512x512_80k_loveda_20211104_155728-88610f9f.pth -- Name: pspnet_r101-d8_512x512_80k_loveda - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 218.34 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 9.61 - Results: - - Task: Semantic Segmentation - Dataset: LoveDA - Metrics: - mIoU: 51.86 - mIoU(ms+flip): 51.34 - Config: configs/pspnet/pspnet_r101-d8_512x512_80k_loveda.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_512x512_80k_loveda/pspnet_r101-d8_512x512_80k_loveda_20211104_153212-1c06c6a8.pth -- Name: pspnet_r18-d8_4x4_512x512_80k_potsdam - In Collection: PSPNet - Metadata: - backbone: R-18-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 11.75 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 1.5 - Results: - - Task: Semantic Segmentation - Dataset: Potsdam - Metrics: - mIoU: 77.09 - mIoU(ms+flip): 78.3 - Config: configs/pspnet/pspnet_r18-d8_4x4_512x512_80k_potsdam.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_4x4_512x512_80k_potsdam/pspnet_r18-d8_4x4_512x512_80k_potsdam_20211220_125612-7cd046e1.pth -- Name: pspnet_r50-d8_4x4_512x512_80k_potsdam - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 33.1 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 6.14 - Results: - - Task: Semantic Segmentation - Dataset: Potsdam - Metrics: - mIoU: 78.12 - mIoU(ms+flip): 78.98 - Config: configs/pspnet/pspnet_r50-d8_4x4_512x512_80k_potsdam.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_4x4_512x512_80k_potsdam/pspnet_r50-d8_4x4_512x512_80k_potsdam_20211219_043541-2dd5fe67.pth -- Name: pspnet_r101-d8_4x4_512x512_80k_potsdam - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 51.55 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 9.61 - Results: - - Task: Semantic Segmentation - Dataset: Potsdam - Metrics: - mIoU: 78.62 - mIoU(ms+flip): 79.47 - Config: configs/pspnet/pspnet_r101-d8_4x4_512x512_80k_potsdam.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_4x4_512x512_80k_potsdam/pspnet_r101-d8_4x4_512x512_80k_potsdam_20211220_125612-aed036c4.pth -- Name: pspnet_r18-d8_4x4_512x512_80k_vaihingen - In Collection: PSPNet - Metadata: - backbone: R-18-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 11.76 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 1.45 - Results: - - Task: Semantic Segmentation - Dataset: Vaihingen - Metrics: - mIoU: 71.46 - mIoU(ms+flip): 73.36 - Config: configs/pspnet/pspnet_r18-d8_4x4_512x512_80k_vaihingen.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_4x4_512x512_80k_vaihingen/pspnet_r18-d8_4x4_512x512_80k_vaihingen_20211228_160355-52a8a6f6.pth -- Name: pspnet_r50-d8_4x4_512x512_80k_vaihingen - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 33.01 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 6.14 - Results: - - Task: Semantic Segmentation - Dataset: Vaihingen - Metrics: - mIoU: 72.36 - mIoU(ms+flip): 73.75 - Config: configs/pspnet/pspnet_r50-d8_4x4_512x512_80k_vaihingen.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_4x4_512x512_80k_vaihingen/pspnet_r50-d8_4x4_512x512_80k_vaihingen_20211228_160355-382f8f5b.pth -- Name: pspnet_r101-d8_4x4_512x512_80k_vaihingen - In Collection: PSPNet - Metadata: - backbone: R-101-D8 - crop size: (512,512) - lr schd: 80000 - inference time (ms/im): - - value: 50.08 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,512) - Training Memory (GB): 9.61 - Results: - - Task: Semantic Segmentation - Dataset: Vaihingen - Metrics: - mIoU: 72.61 - mIoU(ms+flip): 74.18 - Config: configs/pspnet/pspnet_r101-d8_4x4_512x512_80k_vaihingen.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r101-d8_4x4_512x512_80k_vaihingen/pspnet_r101-d8_4x4_512x512_80k_vaihingen_20211231_230806-8eba0a09.pth -- Name: pspnet_r18-d8_4x4_896x896_80k_isaid - In Collection: PSPNet - Metadata: - backbone: R-18-D8 - crop size: (896,896) - lr schd: 80000 - inference time (ms/im): - - value: 37.16 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (896,896) - Training Memory (GB): 4.52 - Results: - - Task: Semantic Segmentation - Dataset: iSAID - Metrics: - mIoU: 60.22 - mIoU(ms+flip): 61.25 - Config: configs/pspnet/pspnet_r18-d8_4x4_896x896_80k_isaid.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r18-d8_4x4_896x896_80k_isaid/pspnet_r18-d8_4x4_896x896_80k_isaid_20220110_180526-e84c0b6a.pth -- Name: pspnet_r50-d8_4x4_896x896_80k_isaid - In Collection: PSPNet - Metadata: - backbone: R-50-D8 - crop size: (896,896) - lr schd: 80000 - inference time (ms/im): - - value: 112.61 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (896,896) - Training Memory (GB): 16.58 - Results: - - Task: Semantic Segmentation - Dataset: iSAID - Metrics: - mIoU: 65.36 - mIoU(ms+flip): 66.48 - Config: configs/pspnet/pspnet_r50-d8_4x4_896x896_80k_isaid.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/pspnet/pspnet_r50-d8_4x4_896x896_80k_isaid/pspnet_r50-d8_4x4_896x896_80k_isaid_20220110_180629-1f21dc32.pth diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_40k_pascal_context.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_40k_pascal_context.py deleted file mode 100644 index 0b5a99060..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_40k_pascal_context.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_480x480_40k_pascal_context.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_40k_pascal_context_59.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_40k_pascal_context_59.py deleted file mode 100644 index 081cb3732..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_40k_pascal_context_59.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_480x480_40k_pascal_context_59.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_80k_pascal_context.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_80k_pascal_context.py deleted file mode 100644 index fda911060..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_80k_pascal_context.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_480x480_80k_pascal_context.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_80k_pascal_context_59.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_80k_pascal_context_59.py deleted file mode 100644 index 795c51f8c..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_480x480_80k_pascal_context_59.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_480x480_80k_pascal_context_59.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_4x4_512x512_80k_potsdam.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_4x4_512x512_80k_potsdam.py deleted file mode 100644 index 98343dd76..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_4x4_512x512_80k_potsdam.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_4x4_512x512_80k_potsdam.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_4x4_512x512_80k_vaihingen.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_4x4_512x512_80k_vaihingen.py deleted file mode 100644 index fd79492e7..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_4x4_512x512_80k_vaihingen.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_4x4_512x512_80k_vaihingen.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes.py deleted file mode 100644 index 38fee11bc..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_40k_cityscapes.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x1024_40k_cityscapes.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_40k_dark.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_40k_dark.py deleted file mode 100644 index 105763914..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_40k_dark.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x1024_40k_dark.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_40k_night_driving.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_40k_night_driving.py deleted file mode 100644 index 0ecb9303a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_40k_night_driving.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x1024_40k_night_driving.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes.py deleted file mode 100644 index 9931a07bc..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x1024_80k_cityscapes.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_160k_ade20k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_160k_ade20k.py deleted file mode 100644 index 6107b4154..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_160k_ade20k.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x512_160k_ade20k.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_20k_voc12aug.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_20k_voc12aug.py deleted file mode 100644 index 2221b202d..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_20k_voc12aug.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x512_20k_voc12aug.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_40k_voc12aug.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_40k_voc12aug.py deleted file mode 100644 index 15f578b60..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_40k_voc12aug.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x512_40k_voc12aug.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_160k_coco-stuff164k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_160k_coco-stuff164k.py deleted file mode 100644 index 7ae2061c5..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_160k_coco-stuff164k.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_20k_coco-stuff10k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_20k_coco-stuff10k.py deleted file mode 100644 index a448496b1..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_20k_coco-stuff10k.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_320k_coco-stuff164k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_320k_coco-stuff164k.py deleted file mode 100644 index 90512b875..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_320k_coco-stuff164k.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_40k_coco-stuff10k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_40k_coco-stuff10k.py deleted file mode 100644 index 36aa44385..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_40k_coco-stuff10k.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_80k_coco-stuff164k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_80k_coco-stuff164k.py deleted file mode 100644 index fdddec465..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_4x4_80k_coco-stuff164k.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_80k_ade20k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_80k_ade20k.py deleted file mode 100644 index fb7c3d55d..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_80k_ade20k.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x512_80k_ade20k.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_80k_loveda.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_80k_loveda.py deleted file mode 100644 index 03c0251f6..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_512x512_80k_loveda.py +++ /dev/null @@ -1,6 +0,0 @@ -_base_ = './pspnet_r50-d8_512x512_80k_loveda.py' -model = dict( - backbone=dict( - depth=101, - init_cfg=dict( - type='Pretrained', checkpoint='open-mmlab://resnet101_v1c'))) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_769x769_40k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_769x769_40k_cityscapes.py deleted file mode 100644 index c6e7e5850..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_769x769_40k_cityscapes.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_769x769_40k_cityscapes.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_769x769_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_769x769_80k_cityscapes.py deleted file mode 100644 index 59b8c6dd5..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_769x769_80k_cityscapes.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_769x769_80k_cityscapes.py' -model = dict(pretrained='open-mmlab://resnet101_v1c', backbone=dict(depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_fp16_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_fp16_512x1024_80k_cityscapes.py deleted file mode 100644 index c71b7f638..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101-d8_fp16_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,5 +0,0 @@ -_base_ = './pspnet_r101-d8_512x1024_80k_cityscapes.py' -# fp16 settings -optimizer_config = dict(type='Fp16OptimizerHook', loss_scale=512.) -# fp16 placeholder -fp16 = dict() diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes.py deleted file mode 100644 index ab8a3d3e3..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,4 +0,0 @@ -_base_ = './pspnet_r50-d8_512x1024_80k_cityscapes.py' -model = dict( - pretrained='torchvision://resnet101', - backbone=dict(type='ResNet', depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_512x1024_80k_dark.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_512x1024_80k_dark.py deleted file mode 100644 index 49231d81b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_512x1024_80k_dark.py +++ /dev/null @@ -1,4 +0,0 @@ -_base_ = './pspnet_r50-d8_512x1024_80k_dark.py' -model = dict( - pretrained='torchvision://resnet101', - backbone=dict(type='ResNet', depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_512x1024_80k_night_driving.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_512x1024_80k_night_driving.py deleted file mode 100644 index c3ed2f147..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_512x1024_80k_night_driving.py +++ /dev/null @@ -1,4 +0,0 @@ -_base_ = './pspnet_r50-d8_512x1024_80k_night_driving.py' -model = dict( - pretrained='torchvision://resnet101', - backbone=dict(type='ResNet', depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_769x769_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_769x769_80k_cityscapes.py deleted file mode 100644 index 1a7cb708e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r101b-d8_769x769_80k_cityscapes.py +++ /dev/null @@ -1,4 +0,0 @@ -_base_ = './pspnet_r50-d8_769x769_80k_cityscapes.py' -model = dict( - pretrained='torchvision://resnet101', - backbone=dict(type='ResNet', depth=101)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_4x4_512x512_80k_potsdam.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_4x4_512x512_80k_potsdam.py deleted file mode 100644 index be9dc7254..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_4x4_512x512_80k_potsdam.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = './pspnet_r50-d8_4x4_512x512_80k_potsdam.py' -model = dict( - pretrained='open-mmlab://resnet18_v1c', - backbone=dict(depth=18), - decode_head=dict( - in_channels=512, - channels=128, - ), - auxiliary_head=dict(in_channels=256, channels=64)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_4x4_512x512_80k_vaihingen.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_4x4_512x512_80k_vaihingen.py deleted file mode 100644 index 2cb69228f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_4x4_512x512_80k_vaihingen.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = './pspnet_r50-d8_4x4_512x512_80k_vaihingen.py' -model = dict( - pretrained='open-mmlab://resnet18_v1c', - backbone=dict(depth=18), - decode_head=dict( - in_channels=512, - channels=128, - ), - auxiliary_head=dict(in_channels=256, channels=64)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_4x4_896x896_80k_isaid.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_4x4_896x896_80k_isaid.py deleted file mode 100644 index 4f6f9ab25..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_4x4_896x896_80k_isaid.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = './pspnet_r50-d8_4x4_896x896_80k_isaid.py' -model = dict( - pretrained='open-mmlab://resnet18_v1c', - backbone=dict(depth=18), - decode_head=dict( - in_channels=512, - channels=128, - ), - auxiliary_head=dict(in_channels=256, channels=64)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_512x1024_80k_cityscapes.py deleted file mode 100644 index d914f93c0..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = './pspnet_r50-d8_512x1024_80k_cityscapes.py' -model = dict( - pretrained='open-mmlab://resnet18_v1c', - backbone=dict(depth=18), - decode_head=dict( - in_channels=512, - channels=128, - ), - auxiliary_head=dict(in_channels=256, channels=64)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_512x512_80k_loveda.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_512x512_80k_loveda.py deleted file mode 100644 index dbb832b24..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_512x512_80k_loveda.py +++ /dev/null @@ -1,11 +0,0 @@ -_base_ = './pspnet_r50-d8_512x512_80k_loveda.py' -model = dict( - backbone=dict( - depth=18, - init_cfg=dict( - type='Pretrained', checkpoint='open-mmlab://resnet18_v1c')), - decode_head=dict( - in_channels=512, - channels=128, - ), - auxiliary_head=dict(in_channels=256, channels=64)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_769x769_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_769x769_80k_cityscapes.py deleted file mode 100644 index 5893e66a4..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18-d8_769x769_80k_cityscapes.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = './pspnet_r50-d8_769x769_80k_cityscapes.py' -model = dict( - pretrained='open-mmlab://resnet18_v1c', - backbone=dict(depth=18), - decode_head=dict( - in_channels=512, - channels=128, - ), - auxiliary_head=dict(in_channels=256, channels=64)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18b-d8_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18b-d8_512x1024_80k_cityscapes.py deleted file mode 100644 index abeeedf84..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18b-d8_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = './pspnet_r50-d8_512x1024_80k_cityscapes.py' -model = dict( - pretrained='torchvision://resnet18', - backbone=dict(type='ResNet', depth=18), - decode_head=dict( - in_channels=512, - channels=128, - ), - auxiliary_head=dict(in_channels=256, channels=64)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18b-d8_769x769_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18b-d8_769x769_80k_cityscapes.py deleted file mode 100644 index 284be6d09..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r18b-d8_769x769_80k_cityscapes.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = './pspnet_r50-d8_769x769_80k_cityscapes.py' -model = dict( - pretrained='torchvision://resnet18', - backbone=dict(type='ResNet', depth=18), - decode_head=dict( - in_channels=512, - channels=128, - ), - auxiliary_head=dict(in_channels=256, channels=64)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d32_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d32_512x1024_80k_cityscapes.py deleted file mode 100644 index 6bfeef319..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d32_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,5 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] -model = dict(backbone=dict(dilations=(1, 1, 2, 4), strides=(1, 2, 2, 2))) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d32_rsb-pretrain_512x1024_adamw_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d32_rsb-pretrain_512x1024_adamw_80k_cityscapes.py deleted file mode 100644 index 028387621..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d32_rsb-pretrain_512x1024_adamw_80k_cityscapes.py +++ /dev/null @@ -1,25 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] -checkpoint = 'https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb256-rsb-a1-600e_in1k_20211228-20e21305.pth' # noqa -model = dict( - pretrained=None, - backbone=dict( - type='ResNet', - init_cfg=dict( - type='Pretrained', prefix='backbone.', checkpoint=checkpoint), - dilations=(1, 1, 2, 4), - strides=(1, 2, 2, 2))) - -optimizer = dict(_delete_=True, type='AdamW', lr=0.0005, weight_decay=0.05) -optimizer_config = dict(grad_clip=dict(max_norm=1, norm_type=2)) -# learning policy -lr_config = dict( - _delete_=True, - policy='step', - warmup='linear', - warmup_iters=1000, - warmup_ratio=0.001, - step=[60000, 72000], - by_epoch=False) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_40k_pascal_context.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_40k_pascal_context.py deleted file mode 100644 index 30abe46e7..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_40k_pascal_context.py +++ /dev/null @@ -1,10 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', - '../_base_/datasets/pascal_context.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_40k.py' -] -model = dict( - decode_head=dict(num_classes=60), - auxiliary_head=dict(num_classes=60), - test_cfg=dict(mode='slide', crop_size=(480, 480), stride=(320, 320))) -optimizer = dict(type='SGD', lr=0.004, momentum=0.9, weight_decay=0.0001) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_40k_pascal_context_59.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_40k_pascal_context_59.py deleted file mode 100644 index 88041c681..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_40k_pascal_context_59.py +++ /dev/null @@ -1,10 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', - '../_base_/datasets/pascal_context_59.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_40k.py' -] -model = dict( - decode_head=dict(num_classes=59), - auxiliary_head=dict(num_classes=59), - test_cfg=dict(mode='slide', crop_size=(480, 480), stride=(320, 320))) -optimizer = dict(type='SGD', lr=0.004, momentum=0.9, weight_decay=0.0001) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_80k_pascal_context.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_80k_pascal_context.py deleted file mode 100644 index 09e96dabf..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_80k_pascal_context.py +++ /dev/null @@ -1,10 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', - '../_base_/datasets/pascal_context.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_80k.py' -] -model = dict( - decode_head=dict(num_classes=60), - auxiliary_head=dict(num_classes=60), - test_cfg=dict(mode='slide', crop_size=(480, 480), stride=(320, 320))) -optimizer = dict(type='SGD', lr=0.004, momentum=0.9, weight_decay=0.0001) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_80k_pascal_context_59.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_80k_pascal_context_59.py deleted file mode 100644 index d4065ec05..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_480x480_80k_pascal_context_59.py +++ /dev/null @@ -1,10 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', - '../_base_/datasets/pascal_context_59.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_80k.py' -] -model = dict( - decode_head=dict(num_classes=59), - auxiliary_head=dict(num_classes=59), - test_cfg=dict(mode='slide', crop_size=(480, 480), stride=(320, 320))) -optimizer = dict(type='SGD', lr=0.004, momentum=0.9, weight_decay=0.0001) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_4x4_512x512_80k_potsdam.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_4x4_512x512_80k_potsdam.py deleted file mode 100644 index f78faff0a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_4x4_512x512_80k_potsdam.py +++ /dev/null @@ -1,6 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/potsdam.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] -model = dict( - decode_head=dict(num_classes=6), auxiliary_head=dict(num_classes=6)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_4x4_512x512_80k_vaihingen.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_4x4_512x512_80k_vaihingen.py deleted file mode 100644 index dfdd294e8..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_4x4_512x512_80k_vaihingen.py +++ /dev/null @@ -1,6 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/vaihingen.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] -model = dict( - decode_head=dict(num_classes=6), auxiliary_head=dict(num_classes=6)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_4x4_896x896_80k_isaid.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_4x4_896x896_80k_isaid.py deleted file mode 100644 index ef7eb9928..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_4x4_896x896_80k_isaid.py +++ /dev/null @@ -1,6 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/isaid.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] -model = dict( - decode_head=dict(num_classes=16), auxiliary_head=dict(num_classes=16)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes.py deleted file mode 100644 index 5deb5872b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_40k_cityscapes.py +++ /dev/null @@ -1,4 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_40k.py' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_40k_dark.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_40k_dark.py deleted file mode 100644 index 9abb5113c..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_40k_dark.py +++ /dev/null @@ -1,29 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_40k.py' -] -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(1920, 1080), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] - -data = dict( - test=dict( - type='DarkZurichDataset', - data_root='data/dark_zurich/', - img_dir='rgb_anon/val/night/GOPR0356', - ann_dir='gt/val/night/GOPR0356', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_40k_night_driving.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_40k_night_driving.py deleted file mode 100644 index 195aeea5e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_40k_night_driving.py +++ /dev/null @@ -1,29 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_40k.py' -] - -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(1920, 1080), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - test=dict( - type='NightDrivingDataset', - data_root='data/NighttimeDrivingTest/', - img_dir='leftImg8bit/test/night', - ann_dir='gtCoarse_daytime_trainvaltest/test/night', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes.py deleted file mode 100644 index 4e9972849..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,4 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_80k_dark.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_80k_dark.py deleted file mode 100644 index 2f16171ac..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_80k_dark.py +++ /dev/null @@ -1,30 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] - -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(1920, 1080), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] - -data = dict( - test=dict( - type='DarkZurichDataset', - data_root='data/dark_zurich/', - img_dir='rgb_anon/val/night/GOPR0356', - ann_dir='gt/val/night/GOPR0356', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_80k_night_driving.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_80k_night_driving.py deleted file mode 100644 index ecc5d99d7..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x1024_80k_night_driving.py +++ /dev/null @@ -1,29 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] - -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(1920, 1080), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - test=dict( - type='NightDrivingDataset', - data_root='data/NighttimeDrivingTest/', - img_dir='leftImg8bit/test/night', - ann_dir='gtCoarse_daytime_trainvaltest/test/night', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_160k_ade20k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_160k_ade20k.py deleted file mode 100644 index 86584573a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_160k_ade20k.py +++ /dev/null @@ -1,6 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/ade20k.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_160k.py' -] -model = dict( - decode_head=dict(num_classes=150), auxiliary_head=dict(num_classes=150)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_20k_voc12aug.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_20k_voc12aug.py deleted file mode 100644 index cd88154d5..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_20k_voc12aug.py +++ /dev/null @@ -1,7 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', - '../_base_/datasets/pascal_voc12_aug.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_20k.py' -] -model = dict( - decode_head=dict(num_classes=21), auxiliary_head=dict(num_classes=21)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_40k_voc12aug.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_40k_voc12aug.py deleted file mode 100644 index f0c20c12f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_40k_voc12aug.py +++ /dev/null @@ -1,7 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', - '../_base_/datasets/pascal_voc12_aug.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_40k.py' -] -model = dict( - decode_head=dict(num_classes=21), auxiliary_head=dict(num_classes=21)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k.py deleted file mode 100644 index e1f8887a2..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_160k_coco-stuff164k.py +++ /dev/null @@ -1,7 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', - '../_base_/datasets/coco-stuff164k.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_160k.py' -] -model = dict( - decode_head=dict(num_classes=171), auxiliary_head=dict(num_classes=171)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k.py deleted file mode 100644 index 6cd94f9a0..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_20k_coco-stuff10k.py +++ /dev/null @@ -1,6 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/coco-stuff10k.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_20k.py' -] -model = dict( - decode_head=dict(num_classes=171), auxiliary_head=dict(num_classes=171)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k.py deleted file mode 100644 index 32b3281d0..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_320k_coco-stuff164k.py +++ /dev/null @@ -1,7 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', - '../_base_/datasets/coco-stuff164k.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_320k.py' -] -model = dict( - decode_head=dict(num_classes=171), auxiliary_head=dict(num_classes=171)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k.py deleted file mode 100644 index c792bb4e7..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_40k_coco-stuff10k.py +++ /dev/null @@ -1,6 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/coco-stuff10k.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_40k.py' -] -model = dict( - decode_head=dict(num_classes=171), auxiliary_head=dict(num_classes=171)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k.py deleted file mode 100644 index 7f7bc6400..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_4x4_80k_coco-stuff164k.py +++ /dev/null @@ -1,7 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', - '../_base_/datasets/coco-stuff164k.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_80k.py' -] -model = dict( - decode_head=dict(num_classes=171), auxiliary_head=dict(num_classes=171)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_80k_ade20k.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_80k_ade20k.py deleted file mode 100644 index 52efdf51d..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_80k_ade20k.py +++ /dev/null @@ -1,6 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/ade20k.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] -model = dict( - decode_head=dict(num_classes=150), auxiliary_head=dict(num_classes=150)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_80k_loveda.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_80k_loveda.py deleted file mode 100644 index 830af482e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_512x512_80k_loveda.py +++ /dev/null @@ -1,6 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/loveda.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] -model = dict( - decode_head=dict(num_classes=7), auxiliary_head=dict(num_classes=7)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_769x769_40k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_769x769_40k_cityscapes.py deleted file mode 100644 index 145cadb24..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_769x769_40k_cityscapes.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', - '../_base_/datasets/cityscapes_769x769.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_40k.py' -] -model = dict( - decode_head=dict(align_corners=True), - auxiliary_head=dict(align_corners=True), - test_cfg=dict(mode='slide', crop_size=(769, 769), stride=(513, 513))) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_769x769_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_769x769_80k_cityscapes.py deleted file mode 100644 index 23a81eb7e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_769x769_80k_cityscapes.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', - '../_base_/datasets/cityscapes_769x769.py', '../_base_/default_runtime.py', - '../_base_/schedules/schedule_80k.py' -] -model = dict( - decode_head=dict(align_corners=True), - auxiliary_head=dict(align_corners=True), - test_cfg=dict(mode='slide', crop_size=(769, 769), stride=(513, 513))) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_rsb-pretrain_512x1024_adamw_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_rsb-pretrain_512x1024_adamw_80k_cityscapes.py deleted file mode 100644 index a8a80bff1..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50-d8_rsb-pretrain_512x1024_adamw_80k_cityscapes.py +++ /dev/null @@ -1,23 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] -checkpoint = 'https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb256-rsb-a1-600e_in1k_20211228-20e21305.pth' # noqa -model = dict( - pretrained=None, - backbone=dict( - type='ResNet', - init_cfg=dict( - type='Pretrained', prefix='backbone.', checkpoint=checkpoint))) - -optimizer = dict(_delete_=True, type='AdamW', lr=0.0005, weight_decay=0.05) -optimizer_config = dict(grad_clip=dict(max_norm=1, norm_type=2)) -# learning policy -lr_config = dict( - _delete_=True, - policy='step', - warmup='linear', - warmup_iters=1000, - warmup_ratio=0.001, - step=[60000, 72000], - by_epoch=False) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50b-d32_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50b-d32_512x1024_80k_cityscapes.py deleted file mode 100644 index 7f4f6c9b4..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50b-d32_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,7 +0,0 @@ -_base_ = [ - '../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] -model = dict( - pretrained='torchvision://resnet50', - backbone=dict(type='ResNet', dilations=(1, 1, 2, 4), strides=(1, 2, 2, 2))) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50b-d8_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50b-d8_512x1024_80k_cityscapes.py deleted file mode 100644 index 946bf4fc8..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50b-d8_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_512x1024_80k_cityscapes.py' -model = dict(pretrained='torchvision://resnet50', backbone=dict(type='ResNet')) diff --git a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50b-d8_769x769_80k_cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50b-d8_769x769_80k_cityscapes.py deleted file mode 100644 index b6087dcf9..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/configs/pspnet/pspnet_r50b-d8_769x769_80k_cityscapes.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './pspnet_r50-d8_769x769_80k_cityscapes.py' -model = dict(pretrained='torchvision://resnet50', backbone=dict(type='ResNet')) diff --git a/cv/semantic_segmentation/pspnet/pytorch/docker/Dockerfile b/cv/semantic_segmentation/pspnet/pytorch/docker/Dockerfile deleted file mode 100644 index 64482b472..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/docker/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -ARG PYTORCH="1.11.0" -ARG CUDA="11.3" -ARG CUDNN="8" - -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ENV TORCH_CUDA_ARCH_LIST="6.0 6.1 7.0+PTX" -ENV TORCH_NVCC_FLAGS="-Xfatbin -compress-all" -ENV CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" - -# To fix GPG key error when running apt-get update -RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub -RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/7fa2af80.pub - -RUN apt-get update && apt-get install -y git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -RUN conda clean --all - -# Install MMCV -ARG PYTORCH -ARG CUDA -ARG MMCV -RUN ["/bin/bash", "-c", "pip install --no-cache-dir mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] - -# Install MMSegmentation -RUN git clone https://github.com/open-mmlab/mmsegmentation.git /mmsegmentation -WORKDIR /mmsegmentation -ENV FORCE_CUDA="1" -RUN pip install -r requirements.txt -RUN pip install --no-cache-dir -e . diff --git a/cv/semantic_segmentation/pspnet/pytorch/docker/serve/Dockerfile b/cv/semantic_segmentation/pspnet/pytorch/docker/serve/Dockerfile deleted file mode 100644 index c1d154528..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/docker/serve/Dockerfile +++ /dev/null @@ -1,49 +0,0 @@ -ARG PYTORCH="1.11.0" -ARG CUDA="11.3" -ARG CUDNN="8" -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ARG MMCV="1.4.8" -ARG MMSEG="0.24.1" - -ENV PYTHONUNBUFFERED TRUE - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ - ca-certificates \ - g++ \ - openjdk-11-jre-headless \ - # MMDet Requirements - ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && rm -rf /var/lib/apt/lists/* - -ENV PATH="/opt/conda/bin:$PATH" -RUN export FORCE_CUDA=1 - -# TORCHSEVER -RUN pip install torchserve torch-model-archiver - -# MMLAB -ARG PYTORCH -ARG CUDA -RUN ["/bin/bash", "-c", "pip install mmcv-full==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] -RUN pip install mmsegmentation==${MMSEG} - -RUN useradd -m model-server \ - && mkdir -p /home/model-server/tmp - -COPY entrypoint.sh /usr/local/bin/entrypoint.sh - -RUN chmod +x /usr/local/bin/entrypoint.sh \ - && chown -R model-server /home/model-server - -COPY config.properties /home/model-server/config.properties -RUN mkdir /home/model-server/model-store && chown -R model-server /home/model-server/model-store - -EXPOSE 8080 8081 8082 - -USER model-server -WORKDIR /home/model-server -ENV TEMP=/home/model-server/tmp -ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] -CMD ["serve"] diff --git a/cv/semantic_segmentation/pspnet/pytorch/docker/serve/config.properties b/cv/semantic_segmentation/pspnet/pytorch/docker/serve/config.properties deleted file mode 100644 index efb9c47e4..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/docker/serve/config.properties +++ /dev/null @@ -1,5 +0,0 @@ -inference_address=http://0.0.0.0:8080 -management_address=http://0.0.0.0:8081 -metrics_address=http://0.0.0.0:8082 -model_store=/home/model-server/model-store -load_models=all diff --git a/cv/semantic_segmentation/pspnet/pytorch/docker/serve/entrypoint.sh b/cv/semantic_segmentation/pspnet/pytorch/docker/serve/entrypoint.sh deleted file mode 100644 index 41ba00b04..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/docker/serve/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - -if [[ "$1" = "serve" ]]; then - shift 1 - torchserve --start --ts-config /home/model-server/config.properties -else - eval "$@" -fi - -# prevent docker exit -tail -f /dev/null diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/__init__.py deleted file mode 100644 index 435429d48..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -from .fileio import * -from .image import * -from .utils import * -from .version import * - -# The following modules are not imported to this level, so mmcv may be used -# without PyTorch. -# - runner -# - parallel -# - op -# - device diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/__init__.py deleted file mode 100644 index 3d5599d9a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# yapf: disable -from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, - ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, - ConvTranspose2d, ConvTranspose3d, ConvWS2d, - DepthwiseSeparableConvModule, GeneralizedAttention, - HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, - NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, - build_activation_layer, build_conv_layer, - build_norm_layer, build_padding_layer, build_plugin_layer, - build_upsample_layer, conv_ws_2d, is_norm) -from .builder import MODELS, build_model_from_cfg -# yapf: enable -from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, - NormalInit, PretrainedInit, TruncNormalInit, UniformInit, - XavierInit, bias_init_with_prob, caffe2_xavier_init, - constant_init, fuse_conv_bn, get_model_complexity_info, - initialize, kaiming_init, normal_init, trunc_normal_init, - uniform_init, xavier_init) - diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/__init__.py deleted file mode 100644 index 0f33124ed..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .activation import build_activation_layer -from .context_block import ContextBlock -from .conv import build_conv_layer -from .conv2d_adaptive_padding import Conv2dAdaptivePadding -from .conv_module import ConvModule -from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d -from .depthwise_separable_conv_module import DepthwiseSeparableConvModule -from .drop import Dropout, DropPath -from .generalized_attention import GeneralizedAttention -from .hsigmoid import HSigmoid -from .hswish import HSwish -from .non_local import NonLocal1d, NonLocal2d, NonLocal3d -from .norm import build_norm_layer, is_norm -from .padding import build_padding_layer -from .plugin import build_plugin_layer -from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) -from .scale import Scale -from .swish import Swish -from .upsample import build_upsample_layer -from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, - Linear, MaxPool2d, MaxPool3d) - -__all__ = [ - 'ConvModule', 'build_activation_layer', 'build_conv_layer', - 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', - 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', - 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', - 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', - 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', - 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', - 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/activation.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/activation.py deleted file mode 100644 index 26be59581..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/activation.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version -from .registry import ACTIVATION_LAYERS - -for module in [ - nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, - nn.Sigmoid, nn.Tanh -]: - ACTIVATION_LAYERS.register_module(module=module) - - -@ACTIVATION_LAYERS.register_module(name='Clip') -@ACTIVATION_LAYERS.register_module() -class Clamp(nn.Module): - """Clamp activation layer. - - This activation function is to clamp the feature map value within - :math:`[min, max]`. More details can be found in ``torch.clamp()``. - - Args: - min (Number | optional): Lower-bound of the range to be clamped to. - Default to -1. - max (Number | optional): Upper-bound of the range to be clamped to. - Default to 1. - """ - - def __init__(self, min=-1., max=1.): - super(Clamp, self).__init__() - self.min = min - self.max = max - - def forward(self, x): - """Forward function. - - Args: - x (torch.Tensor): The input tensor. - - Returns: - torch.Tensor: Clamped tensor. - """ - return torch.clamp(x, min=self.min, max=self.max) - - -class GELU(nn.Module): - r"""Applies the Gaussian Error Linear Units function: - - .. math:: - \text{GELU}(x) = x * \Phi(x) - where :math:`\Phi(x)` is the Cumulative Distribution Function for - Gaussian Distribution. - - Shape: - - Input: :math:`(N, *)` where `*` means, any number of additional - dimensions - - Output: :math:`(N, *)`, same shape as the input - - .. image:: scripts/activation_images/GELU.png - - Examples:: - - >>> m = nn.GELU() - >>> input = torch.randn(2) - >>> output = m(input) - """ - - def forward(self, input): - return F.gelu(input) - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.4')): - ACTIVATION_LAYERS.register_module(module=GELU) -else: - ACTIVATION_LAYERS.register_module(module=nn.GELU) - - -def build_activation_layer(cfg): - """Build activation layer. - - Args: - cfg (dict): The activation layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate an activation layer. - - Returns: - nn.Module: Created activation layer. - """ - return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/context_block.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/context_block.py deleted file mode 100644 index d60fdb904..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/context_block.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn - -from ..utils import constant_init, kaiming_init -from .registry import PLUGIN_LAYERS - - -def last_zero_init(m): - if isinstance(m, nn.Sequential): - constant_init(m[-1], val=0) - else: - constant_init(m, val=0) - - -@PLUGIN_LAYERS.register_module() -class ContextBlock(nn.Module): - """ContextBlock module in GCNet. - - See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' - (https://arxiv.org/abs/1904.11492) for details. - - Args: - in_channels (int): Channels of the input feature map. - ratio (float): Ratio of channels of transform bottleneck - pooling_type (str): Pooling method for context modeling. - Options are 'att' and 'avg', stand for attention pooling and - average pooling respectively. Default: 'att'. - fusion_types (Sequence[str]): Fusion method for feature fusion, - Options are 'channels_add', 'channel_mul', stand for channelwise - addition and multiplication respectively. Default: ('channel_add',) - """ - - _abbr_ = 'context_block' - - def __init__(self, - in_channels, - ratio, - pooling_type='att', - fusion_types=('channel_add', )): - super(ContextBlock, self).__init__() - assert pooling_type in ['avg', 'att'] - assert isinstance(fusion_types, (list, tuple)) - valid_fusion_types = ['channel_add', 'channel_mul'] - assert all([f in valid_fusion_types for f in fusion_types]) - assert len(fusion_types) > 0, 'at least one fusion should be used' - self.in_channels = in_channels - self.ratio = ratio - self.planes = int(in_channels * ratio) - self.pooling_type = pooling_type - self.fusion_types = fusion_types - if pooling_type == 'att': - self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) - self.softmax = nn.Softmax(dim=2) - else: - self.avg_pool = nn.AdaptiveAvgPool2d(1) - if 'channel_add' in fusion_types: - self.channel_add_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_add_conv = None - if 'channel_mul' in fusion_types: - self.channel_mul_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_mul_conv = None - self.reset_parameters() - - def reset_parameters(self): - if self.pooling_type == 'att': - kaiming_init(self.conv_mask, mode='fan_in') - self.conv_mask.inited = True - - if self.channel_add_conv is not None: - last_zero_init(self.channel_add_conv) - if self.channel_mul_conv is not None: - last_zero_init(self.channel_mul_conv) - - def spatial_pool(self, x): - batch, channel, height, width = x.size() - if self.pooling_type == 'att': - input_x = x - # [N, C, H * W] - input_x = input_x.view(batch, channel, height * width) - # [N, 1, C, H * W] - input_x = input_x.unsqueeze(1) - # [N, 1, H, W] - context_mask = self.conv_mask(x) - # [N, 1, H * W] - context_mask = context_mask.view(batch, 1, height * width) - # [N, 1, H * W] - context_mask = self.softmax(context_mask) - # [N, 1, H * W, 1] - context_mask = context_mask.unsqueeze(-1) - # [N, 1, C, 1] - context = torch.matmul(input_x, context_mask) - # [N, C, 1, 1] - context = context.view(batch, channel, 1, 1) - else: - # [N, C, 1, 1] - context = self.avg_pool(x) - - return context - - def forward(self, x): - # [N, C, 1, 1] - context = self.spatial_pool(x) - - out = x - if self.channel_mul_conv is not None: - # [N, C, 1, 1] - channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) - out = out * channel_mul_term - if self.channel_add_conv is not None: - # [N, C, 1, 1] - channel_add_term = self.channel_add_conv(context) - out = out + channel_add_term - - return out diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv.py deleted file mode 100644 index f6c35fd70..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from .registry import CONV_LAYERS - -CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) -CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) -CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) -CONV_LAYERS.register_module('Conv', module=nn.Conv2d) - - -def build_conv_layer(cfg, *args, **kwargs): - """Build convolution layer. - - Args: - cfg (None or dict): The conv layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an conv layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding conv layer. - - Returns: - nn.Module: Created conv layer. - """ - if cfg is None: - cfg_ = dict(type='Conv2d') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in CONV_LAYERS: - raise KeyError(f'Unrecognized layer type {layer_type}') - else: - conv_layer = CONV_LAYERS.get(layer_type) - - layer = conv_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py deleted file mode 100644 index b45e758ac..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from torch import nn -from torch.nn import functional as F - -from .registry import CONV_LAYERS - - -@CONV_LAYERS.register_module() -class Conv2dAdaptivePadding(nn.Conv2d): - """Implementation of 2D convolution in tensorflow with `padding` as "same", - which applies padding to input (if needed) so that input image gets fully - covered by filter and stride you specified. For stride 1, this will ensure - that output image size is same as input. For stride of 2, output dimensions - will be half, for example. - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the convolving kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If ``True``, adds a learnable bias to the - output. Default: ``True`` - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__(in_channels, out_channels, kernel_size, stride, 0, - dilation, groups, bias) - - def forward(self, x): - img_h, img_w = x.size()[-2:] - kernel_h, kernel_w = self.weight.size()[-2:] - stride_h, stride_w = self.stride - output_h = math.ceil(img_h / stride_h) - output_w = math.ceil(img_w / stride_w) - pad_h = ( - max((output_h - 1) * self.stride[0] + - (kernel_h - 1) * self.dilation[0] + 1 - img_h, 0)) - pad_w = ( - max((output_w - 1) * self.stride[1] + - (kernel_w - 1) * self.dilation[1] + 1 - img_w, 0)) - if pad_h > 0 or pad_w > 0: - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2 - ]) - return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv_module.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv_module.py deleted file mode 100644 index 0078647a1..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv_module.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from mmcv.utils import _BatchNorm, _InstanceNorm -from ..utils import constant_init, kaiming_init -from .activation import build_activation_layer -from .conv import build_conv_layer -from .norm import build_norm_layer -from .padding import build_padding_layer -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class ConvModule(nn.Module): - """A conv block that bundles conv/norm/activation layers. - - This block simplifies the usage of convolution layers, which are commonly - used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). - It is based upon three build methods: `build_conv_layer()`, - `build_norm_layer()` and `build_activation_layer()`. - - Besides, we add some additional features in this module. - 1. Automatically set `bias` of the conv layer. - 2. Spectral norm is supported. - 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only - supports zero and circular padding, and we add "reflect" padding mode. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. - groups (int): Number of blocked connections from input channels to - output channels. Same as that in ``nn._ConvNd``. - bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - inplace (bool): Whether to use inplace mode for activation. - Default: True. - with_spectral_norm (bool): Whether use spectral norm in conv module. - Default: False. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in PyTorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Common examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - Default: ('conv', 'norm', 'act'). - """ - - _abbr_ = 'conv_block' - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias='auto', - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - padding_mode='zeros', - order=('conv', 'norm', 'act')): - super(ConvModule, self).__init__() - assert conv_cfg is None or isinstance(conv_cfg, dict) - assert norm_cfg is None or isinstance(norm_cfg, dict) - assert act_cfg is None or isinstance(act_cfg, dict) - official_padding_mode = ['zeros', 'circular'] - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.with_explicit_padding = padding_mode not in official_padding_mode - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 3 - assert set(order) == set(['conv', 'norm', 'act']) - - self.with_norm = norm_cfg is not None - self.with_activation = act_cfg is not None - # if the conv layer is before a norm layer, bias is unnecessary. - if bias == 'auto': - bias = not self.with_norm - self.with_bias = bias - - if self.with_explicit_padding: - pad_cfg = dict(type=padding_mode) - self.padding_layer = build_padding_layer(pad_cfg, padding) - - # reset padding to 0 for conv module - conv_padding = 0 if self.with_explicit_padding else padding - # build convolution layer - self.conv = build_conv_layer( - conv_cfg, - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=conv_padding, - dilation=dilation, - groups=groups, - bias=bias) - # export the attributes of self.conv to a higher level for convenience - self.in_channels = self.conv.in_channels - self.out_channels = self.conv.out_channels - self.kernel_size = self.conv.kernel_size - self.stride = self.conv.stride - self.padding = padding - self.dilation = self.conv.dilation - self.transposed = self.conv.transposed - self.output_padding = self.conv.output_padding - self.groups = self.conv.groups - - if self.with_spectral_norm: - self.conv = nn.utils.spectral_norm(self.conv) - - # build normalization layers - if self.with_norm: - # norm layer is after conv layer - if order.index('norm') > order.index('conv'): - norm_channels = out_channels - else: - norm_channels = in_channels - self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) - self.add_module(self.norm_name, norm) - if self.with_bias: - if isinstance(norm, (_BatchNorm, _InstanceNorm)): - warnings.warn( - 'Unnecessary conv bias before batch/instance norm') - else: - self.norm_name = None - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - # nn.Tanh has no 'inplace' argument - if act_cfg_['type'] not in [ - 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish', 'GELU' - ]: - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - @property - def norm(self): - if self.norm_name: - return getattr(self, self.norm_name) - else: - return None - - def init_weights(self): - # 1. It is mainly for customized conv layers with their own - # initialization manners by calling their own ``init_weights()``, - # and we do not want ConvModule to override the initialization. - # 2. For customized conv layers without their own initialization - # manners (that is, they don't have their own ``init_weights()``) - # and PyTorch's conv layers, they will be initialized by - # this method with default ``kaiming_init``. - # Note: For PyTorch's conv layers, they will be overwritten by our - # initialization implementation using default ``kaiming_init``. - if not hasattr(self.conv, 'init_weights'): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) - if self.with_norm: - constant_init(self.norm, 1, bias=0) - - def forward(self, x, activate=True, norm=True): - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - x = self.conv(x) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv_ws.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv_ws.py deleted file mode 100644 index a3941e278..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/conv_ws.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from .registry import CONV_LAYERS - - -def conv_ws_2d(input, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - eps=1e-5): - c_in = weight.size(0) - weight_flat = weight.view(c_in, -1) - mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1) - std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1) - weight = (weight - mean) / (std + eps) - return F.conv2d(input, weight, bias, stride, padding, dilation, groups) - - -@CONV_LAYERS.register_module('ConvWS') -class ConvWS2d(nn.Conv2d): - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True, - eps=1e-5): - super(ConvWS2d, self).__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.eps = eps - - def forward(self, x): - return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups, self.eps) - - -@CONV_LAYERS.register_module(name='ConvAWS') -class ConvAWS2d(nn.Conv2d): - """AWS (Adaptive Weight Standardization) - - This is a variant of Weight Standardization - (https://arxiv.org/pdf/1903.10520.pdf) - It is used in DetectoRS to avoid NaN - (https://arxiv.org/pdf/2006.02334.pdf) - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the conv kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If set True, adds a learnable bias to the - output. Default: True - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.register_buffer('weight_gamma', - torch.ones(self.out_channels, 1, 1, 1)) - self.register_buffer('weight_beta', - torch.zeros(self.out_channels, 1, 1, 1)) - - def _get_weight(self, weight): - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - weight = (weight - mean) / std - weight = self.weight_gamma * weight + self.weight_beta - return weight - - def forward(self, x): - weight = self._get_weight(self.weight) - return F.conv2d(x, weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - """Override default load function. - - AWS overrides the function _load_from_state_dict to recover - weight_gamma and weight_beta if they are missing. If weight_gamma and - weight_beta are found in the checkpoint, this function will return - after super()._load_from_state_dict. Otherwise, it will compute the - mean and std of the pretrained weights and store them in weight_beta - and weight_gamma. - """ - - self.weight_gamma.data.fill_(-1) - local_missing_keys = [] - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, local_missing_keys, - unexpected_keys, error_msgs) - if self.weight_gamma.data.mean() > 0: - for k in local_missing_keys: - missing_keys.append(k) - return - weight = self.weight.data - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - self.weight_beta.data.copy_(mean) - self.weight_gamma.data.copy_(std) - missing_gamma_beta = [ - k for k in local_missing_keys - if k.endswith('weight_gamma') or k.endswith('weight_beta') - ] - for k in missing_gamma_beta: - local_missing_keys.remove(k) - for k in local_missing_keys: - missing_keys.append(k) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py deleted file mode 100644 index 722d5d8d7..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .conv_module import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if `norm_cfg` and `act_cfg` are specified. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. Default: 1. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. Default: 0. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super(DepthwiseSeparableConvModule, self).__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/drop.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/drop.py deleted file mode 100644 index b0a026654..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/drop.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from mmcv import build_from_cfg -from .registry import DROPOUT_LAYERS - - -def drop_path(x, drop_prob=0., training=False): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - """ - if drop_prob == 0. or not training: - return x - keep_prob = 1 - drop_prob - # handle tensors with different dimensions, not just 4D tensors. - shape = (x.shape[0], ) + (1, ) * (x.ndim - 1) - random_tensor = keep_prob + torch.rand( - shape, dtype=x.dtype, device=x.device) - output = x.div(keep_prob) * random_tensor.floor() - return output - - -@DROPOUT_LAYERS.register_module() -class DropPath(nn.Module): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - - Args: - drop_prob (float): Probability of the path to be zeroed. Default: 0.1 - """ - - def __init__(self, drop_prob=0.1): - super(DropPath, self).__init__() - self.drop_prob = drop_prob - - def forward(self, x): - return drop_path(x, self.drop_prob, self.training) - - -@DROPOUT_LAYERS.register_module() -class Dropout(nn.Dropout): - """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of - ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with - ``DropPath`` - - Args: - drop_prob (float): Probability of the elements to be - zeroed. Default: 0.5. - inplace (bool): Do the operation inplace or not. Default: False. - """ - - def __init__(self, drop_prob=0.5, inplace=False): - super().__init__(p=drop_prob, inplace=inplace) - - -def build_dropout(cfg, default_args=None): - """Builder for drop out layers.""" - return build_from_cfg(cfg, DROPOUT_LAYERS, default_args) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/generalized_attention.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/generalized_attention.py deleted file mode 100644 index c8a74d268..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/generalized_attention.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import kaiming_init -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class GeneralizedAttention(nn.Module): - """GeneralizedAttention module. - - See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' - (https://arxiv.org/abs/1711.07971) for details. - - Args: - in_channels (int): Channels of the input feature map. - spatial_range (int): The spatial range. -1 indicates no spatial range - constraint. Default: -1. - num_heads (int): The head number of empirical_attention module. - Default: 9. - position_embedding_dim (int): The position embedding dimension. - Default: -1. - position_magnitude (int): A multiplier acting on coord difference. - Default: 1. - kv_stride (int): The feature stride acting on key/value feature map. - Default: 2. - q_stride (int): The feature stride acting on query feature map. - Default: 1. - attention_type (str): A binary indicator string for indicating which - items in generalized empirical_attention module are used. - Default: '1111'. - - - '1000' indicates 'query and key content' (appr - appr) item, - - '0100' indicates 'query content and relative position' - (appr - position) item, - - '0010' indicates 'key content only' (bias - appr) item, - - '0001' indicates 'relative position only' (bias - position) item. - """ - - _abbr_ = 'gen_attention_block' - - def __init__(self, - in_channels, - spatial_range=-1, - num_heads=9, - position_embedding_dim=-1, - position_magnitude=1, - kv_stride=2, - q_stride=1, - attention_type='1111'): - - super(GeneralizedAttention, self).__init__() - - # hard range means local range for non-local operation - self.position_embedding_dim = ( - position_embedding_dim - if position_embedding_dim > 0 else in_channels) - - self.position_magnitude = position_magnitude - self.num_heads = num_heads - self.in_channels = in_channels - self.spatial_range = spatial_range - self.kv_stride = kv_stride - self.q_stride = q_stride - self.attention_type = [bool(int(_)) for _ in attention_type] - self.qk_embed_dim = in_channels // num_heads - out_c = self.qk_embed_dim * num_heads - - if self.attention_type[0] or self.attention_type[1]: - self.query_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.query_conv.kaiming_init = True - - if self.attention_type[0] or self.attention_type[2]: - self.key_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.key_conv.kaiming_init = True - - self.v_dim = in_channels // num_heads - self.value_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=self.v_dim * num_heads, - kernel_size=1, - bias=False) - self.value_conv.kaiming_init = True - - if self.attention_type[1] or self.attention_type[3]: - self.appr_geom_fc_x = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_x.kaiming_init = True - - self.appr_geom_fc_y = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_y.kaiming_init = True - - if self.attention_type[2]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.appr_bias = nn.Parameter(appr_bias_value) - - if self.attention_type[3]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.geom_bias = nn.Parameter(geom_bias_value) - - self.proj_conv = nn.Conv2d( - in_channels=self.v_dim * num_heads, - out_channels=in_channels, - kernel_size=1, - bias=True) - self.proj_conv.kaiming_init = True - self.gamma = nn.Parameter(torch.zeros(1)) - - if self.spatial_range >= 0: - # only works when non local is after 3*3 conv - if in_channels == 256: - max_len = 84 - elif in_channels == 512: - max_len = 42 - - max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) - local_constraint_map = np.ones( - (max_len, max_len, max_len_kv, max_len_kv), dtype=int) - for iy in range(max_len): - for ix in range(max_len): - local_constraint_map[ - iy, ix, - max((iy - self.spatial_range) // - self.kv_stride, 0):min((iy + self.spatial_range + - 1) // self.kv_stride + - 1, max_len), - max((ix - self.spatial_range) // - self.kv_stride, 0):min((ix + self.spatial_range + - 1) // self.kv_stride + - 1, max_len)] = 0 - - self.local_constraint_map = nn.Parameter( - torch.from_numpy(local_constraint_map).byte(), - requires_grad=False) - - if self.q_stride > 1: - self.q_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.q_stride) - else: - self.q_downsample = None - - if self.kv_stride > 1: - self.kv_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.kv_stride) - else: - self.kv_downsample = None - - self.init_weights() - - def get_position_embedding(self, - h, - w, - h_kv, - w_kv, - q_stride, - kv_stride, - device, - dtype, - feat_dim, - wave_length=1000): - # the default type of Tensor is float32, leading to type mismatch - # in fp16 mode. Cast it to support fp16 mode. - h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) - h_idxs = h_idxs.view((h, 1)) * q_stride - - w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) - w_idxs = w_idxs.view((w, 1)) * q_stride - - h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( - device=device, dtype=dtype) - h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride - - w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( - device=device, dtype=dtype) - w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride - - # (h, h_kv, 1) - h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) - h_diff *= self.position_magnitude - - # (w, w_kv, 1) - w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) - w_diff *= self.position_magnitude - - feat_range = torch.arange(0, feat_dim / 4).to( - device=device, dtype=dtype) - - dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) - dim_mat = dim_mat**((4. / feat_dim) * feat_range) - dim_mat = dim_mat.view((1, 1, -1)) - - embedding_x = torch.cat( - ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) - - embedding_y = torch.cat( - ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) - - return embedding_x, embedding_y - - def forward(self, x_input): - num_heads = self.num_heads - - # use empirical_attention - if self.q_downsample is not None: - x_q = self.q_downsample(x_input) - else: - x_q = x_input - n, _, h, w = x_q.shape - - if self.kv_downsample is not None: - x_kv = self.kv_downsample(x_input) - else: - x_kv = x_input - _, _, h_kv, w_kv = x_kv.shape - - if self.attention_type[0] or self.attention_type[1]: - proj_query = self.query_conv(x_q).view( - (n, num_heads, self.qk_embed_dim, h * w)) - proj_query = proj_query.permute(0, 1, 3, 2) - - if self.attention_type[0] or self.attention_type[2]: - proj_key = self.key_conv(x_kv).view( - (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) - - if self.attention_type[1] or self.attention_type[3]: - position_embed_x, position_embed_y = self.get_position_embedding( - h, w, h_kv, w_kv, self.q_stride, self.kv_stride, - x_input.device, x_input.dtype, self.position_embedding_dim) - # (n, num_heads, w, w_kv, dim) - position_feat_x = self.appr_geom_fc_x(position_embed_x).\ - view(1, w, w_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - # (n, num_heads, h, h_kv, dim) - position_feat_y = self.appr_geom_fc_y(position_embed_y).\ - view(1, h, h_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - position_feat_x /= math.sqrt(2) - position_feat_y /= math.sqrt(2) - - # accelerate for saliency only - if (np.sum(self.attention_type) == 1) and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy = torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, h_kv * w_kv) - - h = 1 - w = 1 - else: - # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for - if not self.attention_type[0]: - energy = torch.zeros( - n, - num_heads, - h, - w, - h_kv, - w_kv, - dtype=x_input.dtype, - device=x_input.device) - - # attention_type[0]: appr - appr - # attention_type[1]: appr - position - # attention_type[2]: bias - appr - # attention_type[3]: bias - position - if self.attention_type[0] or self.attention_type[2]: - if self.attention_type[0] and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - energy = torch.matmul(proj_query + appr_bias, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[0]: - energy = torch.matmul(proj_query, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy += torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, 1, h_kv, w_kv) - - if self.attention_type[1] or self.attention_type[3]: - if self.attention_type[1] and self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - - proj_query_reshape = (proj_query + geom_bias).\ - view(n, num_heads, h, w, self.qk_embed_dim) - - energy_x = torch.matmul( - proj_query_reshape.permute(0, 1, 3, 2, 4), - position_feat_x.permute(0, 1, 2, 4, 3)) - energy_x = energy_x.\ - permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul( - proj_query_reshape, - position_feat_y.permute(0, 1, 2, 4, 3)) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[1]: - proj_query_reshape = proj_query.\ - view(n, num_heads, h, w, self.qk_embed_dim) - proj_query_reshape = proj_query_reshape.\ - permute(0, 1, 3, 2, 4) - position_feat_x_reshape = position_feat_x.\ - permute(0, 1, 2, 4, 3) - position_feat_y_reshape = position_feat_y.\ - permute(0, 1, 2, 4, 3) - - energy_x = torch.matmul(proj_query_reshape, - position_feat_x_reshape) - energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul(proj_query_reshape, - position_feat_y_reshape) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, self.qk_embed_dim, 1).\ - repeat(n, 1, 1, 1) - - position_feat_x_reshape = position_feat_x.\ - view(n, num_heads, w * w_kv, self.qk_embed_dim) - - position_feat_y_reshape = position_feat_y.\ - view(n, num_heads, h * h_kv, self.qk_embed_dim) - - energy_x = torch.matmul(position_feat_x_reshape, geom_bias) - energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) - - energy_y = torch.matmul(position_feat_y_reshape, geom_bias) - energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) - - energy += energy_x + energy_y - - energy = energy.view(n, num_heads, h * w, h_kv * w_kv) - - if self.spatial_range >= 0: - cur_local_constraint_map = \ - self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ - contiguous().\ - view(1, 1, h*w, h_kv*w_kv) - - energy = energy.masked_fill_(cur_local_constraint_map, - float('-inf')) - - attention = F.softmax(energy, 3) - - proj_value = self.value_conv(x_kv) - proj_value_reshape = proj_value.\ - view((n, num_heads, self.v_dim, h_kv * w_kv)).\ - permute(0, 1, 3, 2) - - out = torch.matmul(attention, proj_value_reshape).\ - permute(0, 1, 3, 2).\ - contiguous().\ - view(n, self.v_dim * self.num_heads, h, w) - - out = self.proj_conv(out) - - # output is downsampled, upsample back to input size - if self.q_downsample is not None: - out = F.interpolate( - out, - size=x_input.shape[2:], - mode='bilinear', - align_corners=False) - - out = self.gamma * out + x_input - return out - - def init_weights(self): - for m in self.modules(): - if hasattr(m, 'kaiming_init') and m.kaiming_init: - kaiming_init( - m, - mode='fan_in', - nonlinearity='leaky_relu', - bias=0, - distribution='uniform', - a=1) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/hsigmoid.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/hsigmoid.py deleted file mode 100644 index e013d739e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/hsigmoid.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSigmoid(nn.Module): - """Hard Sigmoid Module. Apply the hard sigmoid function: - Hsigmoid(x) = min(max((x + bias) / divisor, min_value), max_value) - Default: Hsigmoid(x) = min(max((x + 3) / 6, 0), 1) - - Note: - In MMCV v1.4.4, we modified the default value of args to align with - PyTorch official. - - Args: - bias (float): Bias of the input feature map. Default: 3.0. - divisor (float): Divisor of the input feature map. Default: 6.0. - min_value (float): Lower bound value. Default: 0.0. - max_value (float): Upper bound value. Default: 1.0. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, bias=3.0, divisor=6.0, min_value=0.0, max_value=1.0): - super(HSigmoid, self).__init__() - warnings.warn( - 'In MMCV v1.4.4, we modified the default value of args to align ' - 'with PyTorch official. Previous Implementation: ' - 'Hsigmoid(x) = min(max((x + 1) / 2, 0), 1). ' - 'Current Implementation: ' - 'Hsigmoid(x) = min(max((x + 3) / 6, 0), 1).') - self.bias = bias - self.divisor = divisor - assert self.divisor != 0 - self.min_value = min_value - self.max_value = max_value - - def forward(self, x): - x = (x + self.bias) / self.divisor - - return x.clamp_(self.min_value, self.max_value) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/hswish.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/hswish.py deleted file mode 100644 index 27096832f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/hswish.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import ACTIVATION_LAYERS - - -class HSwish(nn.Module): - """Hard Swish Module. - - This module applies the hard swish function: - - .. math:: - Hswish(x) = x * ReLU6(x + 3) / 6 - - Args: - inplace (bool): can optionally do the operation in-place. - Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.act = nn.ReLU6(inplace) - - def forward(self, x): - return x * self.act(x + 3) / 6 - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.7')): - # Hardswish is not supported when PyTorch version < 1.6. - # And Hardswish in PyTorch 1.6 does not support inplace. - ACTIVATION_LAYERS.register_module(module=HSwish) -else: - ACTIVATION_LAYERS.register_module(module=nn.Hardswish, name='HSwish') diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/non_local.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/non_local.py deleted file mode 100644 index 92d00155e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/non_local.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta - -import torch -import torch.nn as nn - -from ..utils import constant_init, normal_init -from .conv_module import ConvModule -from .registry import PLUGIN_LAYERS - - -class _NonLocalNd(nn.Module, metaclass=ABCMeta): - """Basic Non-local module. - - This module is proposed in - "Non-local Neural Networks" - Paper reference: https://arxiv.org/abs/1711.07971 - Code reference: https://github.com/AlexHex7/Non-local_pytorch - - Args: - in_channels (int): Channels of the input feature map. - reduction (int): Channel reduction ratio. Default: 2. - use_scale (bool): Whether to scale pairwise_weight by - `1/sqrt(inter_channels)` when the mode is `embedded_gaussian`. - Default: True. - conv_cfg (None | dict): The config dict for convolution layers. - If not specified, it will use `nn.Conv2d` for convolution layers. - Default: None. - norm_cfg (None | dict): The config dict for normalization layers. - Default: None. (This parameter is only applicable to conv_out.) - mode (str): Options are `gaussian`, `concatenation`, - `embedded_gaussian` and `dot_product`. Default: embedded_gaussian. - """ - - def __init__(self, - in_channels, - reduction=2, - use_scale=True, - conv_cfg=None, - norm_cfg=None, - mode='embedded_gaussian', - **kwargs): - super(_NonLocalNd, self).__init__() - self.in_channels = in_channels - self.reduction = reduction - self.use_scale = use_scale - self.inter_channels = max(in_channels // reduction, 1) - self.mode = mode - - if mode not in [ - 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation' - ]: - raise ValueError("Mode should be in 'gaussian', 'concatenation', " - f"'embedded_gaussian' or 'dot_product', but got " - f'{mode} instead.') - - # g, theta, phi are defaulted as `nn.ConvNd`. - # Here we use ConvModule for potential usage. - self.g = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.conv_out = ConvModule( - self.inter_channels, - self.in_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - if self.mode != 'gaussian': - self.theta = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.phi = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - - if self.mode == 'concatenation': - self.concat_project = ConvModule( - self.inter_channels * 2, - 1, - kernel_size=1, - stride=1, - padding=0, - bias=False, - act_cfg=dict(type='ReLU')) - - self.init_weights(**kwargs) - - def init_weights(self, std=0.01, zeros_init=True): - if self.mode != 'gaussian': - for m in [self.g, self.theta, self.phi]: - normal_init(m.conv, std=std) - else: - normal_init(self.g.conv, std=std) - if zeros_init: - if self.conv_out.norm_cfg is None: - constant_init(self.conv_out.conv, 0) - else: - constant_init(self.conv_out.norm, 0) - else: - if self.conv_out.norm_cfg is None: - normal_init(self.conv_out.conv, std=std) - else: - normal_init(self.conv_out.norm, std=std) - - def gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def embedded_gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - if self.use_scale: - # theta_x.shape[-1] is `self.inter_channels` - pairwise_weight /= theta_x.shape[-1]**0.5 - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def dot_product(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight /= pairwise_weight.shape[-1] - return pairwise_weight - - def concatenation(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - h = theta_x.size(2) - w = phi_x.size(3) - theta_x = theta_x.repeat(1, 1, 1, w) - phi_x = phi_x.repeat(1, 1, h, 1) - - concat_feature = torch.cat([theta_x, phi_x], dim=1) - pairwise_weight = self.concat_project(concat_feature) - n, _, h, w = pairwise_weight.size() - pairwise_weight = pairwise_weight.view(n, h, w) - pairwise_weight /= pairwise_weight.shape[-1] - - return pairwise_weight - - def forward(self, x): - # Assume `reduction = 1`, then `inter_channels = C` - # or `inter_channels = C` when `mode="gaussian"` - - # NonLocal1d x: [N, C, H] - # NonLocal2d x: [N, C, H, W] - # NonLocal3d x: [N, C, T, H, W] - n = x.size(0) - - # NonLocal1d g_x: [N, H, C] - # NonLocal2d g_x: [N, HxW, C] - # NonLocal3d g_x: [N, TxHxW, C] - g_x = self.g(x).view(n, self.inter_channels, -1) - g_x = g_x.permute(0, 2, 1) - - # NonLocal1d theta_x: [N, H, C], phi_x: [N, C, H] - # NonLocal2d theta_x: [N, HxW, C], phi_x: [N, C, HxW] - # NonLocal3d theta_x: [N, TxHxW, C], phi_x: [N, C, TxHxW] - if self.mode == 'gaussian': - theta_x = x.view(n, self.in_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - if self.sub_sample: - phi_x = self.phi(x).view(n, self.in_channels, -1) - else: - phi_x = x.view(n, self.in_channels, -1) - elif self.mode == 'concatenation': - theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) - phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) - else: - theta_x = self.theta(x).view(n, self.inter_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - phi_x = self.phi(x).view(n, self.inter_channels, -1) - - pairwise_func = getattr(self, self.mode) - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = pairwise_func(theta_x, phi_x) - - # NonLocal1d y: [N, H, C] - # NonLocal2d y: [N, HxW, C] - # NonLocal3d y: [N, TxHxW, C] - y = torch.matmul(pairwise_weight, g_x) - # NonLocal1d y: [N, C, H] - # NonLocal2d y: [N, C, H, W] - # NonLocal3d y: [N, C, T, H, W] - y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, - *x.size()[2:]) - - output = x + self.conv_out(y) - - return output - - -class NonLocal1d(_NonLocalNd): - """1D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv1d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv1d'), - **kwargs): - super(NonLocal1d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool1d(kernel_size=2) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -@PLUGIN_LAYERS.register_module() -class NonLocal2d(_NonLocalNd): - """2D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv2d'). - """ - - _abbr_ = 'nonlocal_block' - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv2d'), - **kwargs): - super(NonLocal2d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -class NonLocal3d(_NonLocalNd): - """3D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv3d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv3d'), - **kwargs): - super(NonLocal3d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/norm.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/norm.py deleted file mode 100644 index 51efdc184..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/norm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import torch.nn as nn - -from mmcv.utils import is_tuple_of -from mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm -from .registry import NORM_LAYERS - -NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) -NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) -NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) -NORM_LAYERS.register_module('GN', module=nn.GroupNorm) -NORM_LAYERS.register_module('LN', module=nn.LayerNorm) -NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) -NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - When we build a norm layer with `build_norm_layer()`, we want to preserve - the norm type in variable names, e.g, self.bn1, self.gn. This method will - infer the abbreviation to map class types to abbreviations. - - Rule 1: If the class has the property "_abbr_", return the property. - Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or - InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and - "in" respectively. - Rule 3: If the class name contains "batch", "group", "layer" or "instance", - the abbreviation of this layer will be "bn", "gn", "ln" and "in" - respectively. - Rule 4: Otherwise, the abbreviation falls back to "norm". - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN - return 'in' - elif issubclass(class_type, _BatchNorm): - return 'bn' - elif issubclass(class_type, nn.GroupNorm): - return 'gn' - elif issubclass(class_type, nn.LayerNorm): - return 'ln' - else: - class_name = class_type.__name__.lower() - if 'batch' in class_name: - return 'bn' - elif 'group' in class_name: - return 'gn' - elif 'layer' in class_name: - return 'ln' - elif 'instance' in class_name: - return 'in' - else: - return 'norm_layer' - - -def build_norm_layer(cfg, num_features, postfix=''): - """Build normalization layer. - - Args: - cfg (dict): The norm layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate a norm layer. - - requires_grad (bool, optional): Whether stop gradient updates. - num_features (int): Number of input channels. - postfix (int | str): The postfix to be appended into norm abbreviation - to create named layer. - - Returns: - tuple[str, nn.Module]: The first element is the layer name consisting - of abbreviation and postfix, e.g., bn1, gn. The second element is the - created norm layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in NORM_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - - norm_layer = NORM_LAYERS.get(layer_type) - abbr = infer_abbr(norm_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - requires_grad = cfg_.pop('requires_grad', True) - cfg_.setdefault('eps', 1e-5) - if layer_type != 'GN': - layer = norm_layer(num_features, **cfg_) - if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): - layer._specify_ddp_gpu_num(1) - else: - assert 'num_groups' in cfg_ - layer = norm_layer(num_channels=num_features, **cfg_) - - for param in layer.parameters(): - param.requires_grad = requires_grad - - return name, layer - - -def is_norm(layer, exclude=None): - """Check if a layer is a normalization layer. - - Args: - layer (nn.Module): The layer to be checked. - exclude (type | tuple[type]): Types to be excluded. - - Returns: - bool: Whether the layer is a norm layer. - """ - if exclude is not None: - if not isinstance(exclude, tuple): - exclude = (exclude, ) - if not is_tuple_of(exclude, type): - raise TypeError( - f'"exclude" must be either None or type or a tuple of types, ' - f'but got {type(exclude)}: {exclude}') - - if exclude and isinstance(layer, exclude): - return False - - all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) - return isinstance(layer, all_norm_bases) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/padding.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/padding.py deleted file mode 100644 index e4ac6b28a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/padding.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import PADDING_LAYERS - -PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) -PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) -PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) - - -def build_padding_layer(cfg, *args, **kwargs): - """Build padding layer. - - Args: - cfg (None or dict): The padding layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate a padding layer. - - Returns: - nn.Module: Created padding layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - - cfg_ = cfg.copy() - padding_type = cfg_.pop('type') - if padding_type not in PADDING_LAYERS: - raise KeyError(f'Unrecognized padding type {padding_type}.') - else: - padding_layer = PADDING_LAYERS.get(padding_type) - - layer = padding_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/plugin.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/plugin.py deleted file mode 100644 index 009f7529b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/plugin.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import platform - -from .registry import PLUGIN_LAYERS - -if platform.system() == 'Windows': - import regex as re -else: - import re - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - This method will infer the abbreviation to map class types to - abbreviations. - - Rule 1: If the class has the property "abbr", return the property. - Rule 2: Otherwise, the abbreviation falls back to snake case of class - name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - - def camel2snack(word): - """Convert camel case word into snack case. - - Modified from `inflection lib - `_. - - Example:: - - >>> camel2snack("FancyBlock") - 'fancy_block' - """ - - word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) - word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) - word = word.replace('-', '_') - return word.lower() - - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - else: - return camel2snack(class_type.__name__) - - -def build_plugin_layer(cfg, postfix='', **kwargs): - """Build plugin layer. - - Args: - cfg (None or dict): cfg should contain: - - - type (str): identify plugin layer type. - - layer args: args needed to instantiate a plugin layer. - postfix (int, str): appended into norm abbreviation to - create named layer. Default: ''. - - Returns: - tuple[str, nn.Module]: The first one is the concatenation of - abbreviation and postfix. The second is the created plugin layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in PLUGIN_LAYERS: - raise KeyError(f'Unrecognized plugin type {layer_type}') - - plugin_layer = PLUGIN_LAYERS.get(layer_type) - abbr = infer_abbr(plugin_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - layer = plugin_layer(**kwargs, **cfg_) - - return name, layer diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/registry.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/registry.py deleted file mode 100644 index c29279776..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/registry.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -CONV_LAYERS = Registry('conv layer') -NORM_LAYERS = Registry('norm layer') -ACTIVATION_LAYERS = Registry('activation layer') -PADDING_LAYERS = Registry('padding layer') -UPSAMPLE_LAYERS = Registry('upsample layer') -PLUGIN_LAYERS = Registry('plugin layer') - -DROPOUT_LAYERS = Registry('drop out layers') -POSITIONAL_ENCODING = Registry('position encoding') -ATTENTION = Registry('attention') -FEEDFORWARD_NETWORK = Registry('feed-forward Network') -TRANSFORMER_LAYER = Registry('transformerLayer') -TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/scale.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/scale.py deleted file mode 100644 index c905fffcc..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/scale.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class Scale(nn.Module): - """A learnable scale parameter. - - This layer scales the input by a learnable factor. It multiplies a - learnable scale parameter of shape (1,) with input of any shape. - - Args: - scale (float): Initial value of scale factor. Default: 1.0 - """ - - def __init__(self, scale=1.0): - super(Scale, self).__init__() - self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) - - def forward(self, x): - return x * self.scale diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/swish.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/swish.py deleted file mode 100644 index e2ca8ed7b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/swish.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class Swish(nn.Module): - """Swish Module. - - This module applies the swish function: - - .. math:: - Swish(x) = x * Sigmoid(x) - - Returns: - Tensor: The output tensor. - """ - - def __init__(self): - super(Swish, self).__init__() - - def forward(self, x): - return x * torch.sigmoid(x) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/transformer.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/transformer.py deleted file mode 100644 index 70c6623c7..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/transformer.py +++ /dev/null @@ -1,944 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings -from typing import Sequence - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.cnn import (Linear, build_activation_layer, build_conv_layer, - build_norm_layer) -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential -from mmcv.utils import (ConfigDict, build_from_cfg, deprecated_api_warning, - to_2tuple) -from .drop import build_dropout -from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, - TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) - -# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file -try: - from mmcv.ops.multi_scale_deform_attn import \ - MultiScaleDeformableAttention # noqa F401 - warnings.warn( - ImportWarning( - '``MultiScaleDeformableAttention`` has been moved to ' - '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 - '``from mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 - 'to ``from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 - )) - -except ImportError: - warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' - '``mmcv.ops.multi_scale_deform_attn``, ' - 'You should install ``mmcv-full`` if you need this module. ') - - -def build_positional_encoding(cfg, default_args=None): - """Builder for Position Encoding.""" - return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) - - -def build_attention(cfg, default_args=None): - """Builder for attention.""" - return build_from_cfg(cfg, ATTENTION, default_args) - - -def build_feedforward_network(cfg, default_args=None): - """Builder for feed-forward network (FFN).""" - return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) - - -def build_transformer_layer(cfg, default_args=None): - """Builder for transformer layer.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) - - -def build_transformer_layer_sequence(cfg, default_args=None): - """Builder for transformer encoder and transformer decoder.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) - - -class AdaptivePadding(nn.Module): - """Applies padding adaptively to the input. - - This module can make input get fully covered by filter - you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad - zero around input. The "corner" mode would pad zero - to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel. Default: 1. - stride (int | tuple): Stride of the filter. Default: 1. - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - super(AdaptivePadding, self).__init__() - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - """Calculate the padding size of input. - - Args: - input_shape (:obj:`torch.Size`): arrange as (H, W). - - Returns: - Tuple[int]: The padding size along the - original H and W directions - """ - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - """Add padding to `x` - - Args: - x (Tensor): Input tensor has shape (B, C, H, W). - - Returns: - Tensor: The tensor with adaptive padding - """ - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The type of convolution - to generate patch embedding. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int): The slide stride of embedding conv. - Default: 16. - padding (int | tuple | string): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only works when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=16, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super(PatchEmbed, self).__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adaptive_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # e.g. when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adaptive_padding: - pad_h, pad_w = self.adaptive_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adaptive_padding: - x = self.adaptive_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map ((used in Swin Transformer)). - Our implementation uses `nn.Unfold` to - merge patches, which is about 25% faster than the original - implementation. However, we need to modify pretrained - models for compatibility. - - Args: - in_channels (int): The num of input channels. - to gets fully covered by filter and stride you specified. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adaptive_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - - if self.adaptive_padding: - x = self.adaptive_padding(x) - H, W = x.shape[-2:] - - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - x = self.sampler(x) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size - - -@ATTENTION.register_module() -class MultiheadAttention(BaseModule): - """A wrapper for ``torch.nn.MultiheadAttention``. - - This module implements MultiheadAttention with identity connection, - and positional encoding is also passed as input. - - Args: - embed_dims (int): The embedding dimension. - num_heads (int): Parallel attention heads. - attn_drop (float): A Dropout layer on attn_output_weights. - Default: 0.0. - proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. - Default: 0.0. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): When it is True, Key, Query and Value are shape of - (batch, n, embed_dim), otherwise (n, batch, embed_dim). - Default to False. - """ - - def __init__(self, - embed_dims, - num_heads, - attn_drop=0., - proj_drop=0., - dropout_layer=dict(type='Dropout', drop_prob=0.), - init_cfg=None, - batch_first=False, - **kwargs): - super(MultiheadAttention, self).__init__(init_cfg) - if 'dropout' in kwargs: - warnings.warn( - 'The arguments `dropout` in MultiheadAttention ' - 'has been deprecated, now you can separately ' - 'set `attn_drop`(float), proj_drop(float), ' - 'and `dropout_layer`(dict) ', DeprecationWarning) - attn_drop = kwargs['dropout'] - dropout_layer['drop_prob'] = kwargs.pop('dropout') - - self.embed_dims = embed_dims - self.num_heads = num_heads - self.batch_first = batch_first - - self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, - **kwargs) - - self.proj_drop = nn.Dropout(proj_drop) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else nn.Identity() - - @deprecated_api_warning({'residual': 'identity'}, - cls_name='MultiheadAttention') - def forward(self, - query, - key=None, - value=None, - identity=None, - query_pos=None, - key_pos=None, - attn_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `MultiheadAttention`. - - **kwargs allow passing a more general data flow when combining - with other operations in `transformerlayer`. - - Args: - query (Tensor): The input query with shape [num_queries, bs, - embed_dims] if self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - If None, the ``query`` will be used. Defaults to None. - value (Tensor): The value tensor with same shape as `key`. - Same in `nn.MultiheadAttention.forward`. Defaults to None. - If None, the `key` will be used. - identity (Tensor): This tensor, with the same shape as x, - will be used for the identity link. - If None, `x` will be used. Defaults to None. - query_pos (Tensor): The positional encoding for query, with - the same shape as `x`. If not None, it will - be added to `x` before forward function. Defaults to None. - key_pos (Tensor): The positional encoding for `key`, with the - same shape as `key`. Defaults to None. If not None, it will - be added to `key` before forward function. If None, and - `query_pos` has the same shape as `key`, then `query_pos` - will be used for `key_pos`. Defaults to None. - attn_mask (Tensor): ByteTensor mask with shape [num_queries, - num_keys]. Same in `nn.MultiheadAttention.forward`. - Defaults to None. - key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. - Defaults to None. - - Returns: - Tensor: forwarded results with shape - [num_queries, bs, embed_dims] - if self.batch_first is False, else - [bs, num_queries embed_dims]. - """ - - if key is None: - key = query - if value is None: - value = key - if identity is None: - identity = query - if key_pos is None: - if query_pos is not None: - # use query_pos if key_pos is not available - if query_pos.shape == key.shape: - key_pos = query_pos - else: - warnings.warn(f'position encoding of key is' - f'missing in {self.__class__.__name__}.') - if query_pos is not None: - query = query + query_pos - if key_pos is not None: - key = key + key_pos - - # Because the dataflow('key', 'query', 'value') of - # ``torch.nn.MultiheadAttention`` is (num_query, batch, - # embed_dims), We should adjust the shape of dataflow from - # batch_first (batch, num_query, embed_dims) to num_query_first - # (num_query ,batch, embed_dims), and recover ``attn_output`` - # from num_query_first to batch_first. - if self.batch_first: - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - out = self.attn( - query=query, - key=key, - value=value, - attn_mask=attn_mask, - key_padding_mask=key_padding_mask)[0] - - if self.batch_first: - out = out.transpose(0, 1) - - return identity + self.dropout_layer(self.proj_drop(out)) - - -@FEEDFORWARD_NETWORK.register_module() -class FFN(BaseModule): - """Implements feed-forward networks (FFNs) with identity connection. - - Args: - embed_dims (int): The feature dimension. Same as - `MultiheadAttention`. Defaults: 256. - feedforward_channels (int): The hidden dimension of FFNs. - Defaults: 1024. - num_fcs (int, optional): The number of fully-connected layers in - FFNs. Default: 2. - act_cfg (dict, optional): The activation config for FFNs. - Default: dict(type='ReLU') - ffn_drop (float, optional): Probability of an element to be - zeroed in FFN. Default 0.0. - add_identity (bool, optional): Whether to add the - identity connection. Default: `True`. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - @deprecated_api_warning( - { - 'dropout': 'ffn_drop', - 'add_residual': 'add_identity' - }, - cls_name='FFN') - def __init__(self, - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - act_cfg=dict(type='ReLU', inplace=True), - ffn_drop=0., - dropout_layer=None, - add_identity=True, - init_cfg=None, - **kwargs): - super(FFN, self).__init__(init_cfg) - assert num_fcs >= 2, 'num_fcs should be no less ' \ - f'than 2. got {num_fcs}.' - self.embed_dims = embed_dims - self.feedforward_channels = feedforward_channels - self.num_fcs = num_fcs - self.act_cfg = act_cfg - self.activate = build_activation_layer(act_cfg) - - layers = [] - in_channels = embed_dims - for _ in range(num_fcs - 1): - layers.append( - Sequential( - Linear(in_channels, feedforward_channels), self.activate, - nn.Dropout(ffn_drop))) - in_channels = feedforward_channels - layers.append(Linear(feedforward_channels, embed_dims)) - layers.append(nn.Dropout(ffn_drop)) - self.layers = Sequential(*layers) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else torch.nn.Identity() - self.add_identity = add_identity - - @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') - def forward(self, x, identity=None): - """Forward function for `FFN`. - - The function would add x to the output tensor if residue is None. - """ - out = self.layers(x) - if not self.add_identity: - return self.dropout_layer(out) - if identity is None: - identity = x - return identity + self.dropout_layer(out) - - -@TRANSFORMER_LAYER.register_module() -class BaseTransformerLayer(BaseModule): - """Base `TransformerLayer` for vision transformer. - - It can be built from `mmcv.ConfigDict` and support more flexible - customization, for example, using any number of `FFN or LN ` and - use different kinds of `attention` by specifying a list of `ConfigDict` - named `attn_cfgs`. It is worth mentioning that it supports `prenorm` - when you specifying `norm` as the first element of `operation_order`. - More details about the `prenorm`: `On Layer Normalization in the - Transformer Architecture `_ . - - Args: - attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for `self_attention` or `cross_attention` modules, - The order of the configs in the list should be consistent with - corresponding attentions in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. Default: None. - ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for FFN, The order of the configs in the list should be - consistent with corresponding ffn in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. - operation_order (tuple[str]): The execution order of operation - in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). - Support `prenorm` when you specifying first element as `norm`. - Default:None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): Key, Query and Value are shape - of (batch, n, embed_dim) - or (n, batch, embed_dim). Default to False. - """ - - def __init__(self, - attn_cfgs=None, - ffn_cfgs=dict( - type='FFN', - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - ffn_drop=0., - act_cfg=dict(type='ReLU', inplace=True), - ), - operation_order=None, - norm_cfg=dict(type='LN'), - init_cfg=None, - batch_first=False, - **kwargs): - - deprecated_args = dict( - feedforward_channels='feedforward_channels', - ffn_dropout='ffn_drop', - ffn_num_fcs='num_fcs') - for ori_name, new_name in deprecated_args.items(): - if ori_name in kwargs: - warnings.warn( - f'The arguments `{ori_name}` in BaseTransformerLayer ' - f'has been deprecated, now you should set `{new_name}` ' - f'and other FFN related arguments ' - f'to a dict named `ffn_cfgs`. ', DeprecationWarning) - ffn_cfgs[new_name] = kwargs[ori_name] - - super(BaseTransformerLayer, self).__init__(init_cfg) - - self.batch_first = batch_first - - assert set(operation_order) & set( - ['self_attn', 'norm', 'ffn', 'cross_attn']) == \ - set(operation_order), f'The operation_order of' \ - f' {self.__class__.__name__} should ' \ - f'contains all four operation type ' \ - f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" - - num_attn = operation_order.count('self_attn') + operation_order.count( - 'cross_attn') - if isinstance(attn_cfgs, dict): - attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] - else: - assert num_attn == len(attn_cfgs), f'The length ' \ - f'of attn_cfg {num_attn} is ' \ - f'not consistent with the number of attention' \ - f'in operation_order {operation_order}.' - - self.num_attn = num_attn - self.operation_order = operation_order - self.norm_cfg = norm_cfg - self.pre_norm = operation_order[0] == 'norm' - self.attentions = ModuleList() - - index = 0 - for operation_name in operation_order: - if operation_name in ['self_attn', 'cross_attn']: - if 'batch_first' in attn_cfgs[index]: - assert self.batch_first == attn_cfgs[index]['batch_first'] - else: - attn_cfgs[index]['batch_first'] = self.batch_first - attention = build_attention(attn_cfgs[index]) - # Some custom attentions used as `self_attn` - # or `cross_attn` can have different behavior. - attention.operation_name = operation_name - self.attentions.append(attention) - index += 1 - - self.embed_dims = self.attentions[0].embed_dims - - self.ffns = ModuleList() - num_ffns = operation_order.count('ffn') - if isinstance(ffn_cfgs, dict): - ffn_cfgs = ConfigDict(ffn_cfgs) - if isinstance(ffn_cfgs, dict): - ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] - assert len(ffn_cfgs) == num_ffns - for ffn_index in range(num_ffns): - if 'embed_dims' not in ffn_cfgs[ffn_index]: - ffn_cfgs[ffn_index]['embed_dims'] = self.embed_dims - else: - assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims - self.ffns.append( - build_feedforward_network(ffn_cfgs[ffn_index], - dict(type='FFN'))) - - self.norms = ModuleList() - num_norms = operation_order.count('norm') - for _ in range(num_norms): - self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) - - def forward(self, - query, - key=None, - value=None, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerDecoderLayer`. - - **kwargs contains some specific arguments of attentions. - - Args: - query (Tensor): The input query with shape - [num_queries, bs, embed_dims] if - self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - value (Tensor): The value tensor with same shape as `key`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor] | None): 2D Tensor used in - calculation of corresponding attention. The length of - it should equal to the number of `attention` in - `operation_order`. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in `self_attn` layer. - Defaults to None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: forwarded results with shape [num_queries, bs, embed_dims]. - """ - - norm_index = 0 - attn_index = 0 - ffn_index = 0 - identity = query - if attn_masks is None: - attn_masks = [None for _ in range(self.num_attn)] - elif isinstance(attn_masks, torch.Tensor): - attn_masks = [ - copy.deepcopy(attn_masks) for _ in range(self.num_attn) - ] - warnings.warn(f'Use same attn_mask in all attentions in ' - f'{self.__class__.__name__} ') - else: - assert len(attn_masks) == self.num_attn, f'The length of ' \ - f'attn_masks {len(attn_masks)} must be equal ' \ - f'to the number of attention in ' \ - f'operation_order {self.num_attn}' - - for layer in self.operation_order: - if layer == 'self_attn': - temp_key = temp_value = query - query = self.attentions[attn_index]( - query, - temp_key, - temp_value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=query_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=query_key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'norm': - query = self.norms[norm_index](query) - norm_index += 1 - - elif layer == 'cross_attn': - query = self.attentions[attn_index]( - query, - key, - value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=key_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'ffn': - query = self.ffns[ffn_index]( - query, identity if self.pre_norm else None) - ffn_index += 1 - - return query - - -@TRANSFORMER_LAYER_SEQUENCE.register_module() -class TransformerLayerSequence(BaseModule): - """Base class for TransformerEncoder and TransformerDecoder in vision - transformer. - - As base-class of Encoder and Decoder in vision transformer. - Support customization such as specifying different kind - of `transformer_layer` in `transformer_coder`. - - Args: - transformerlayer (list[obj:`mmcv.ConfigDict`] | - obj:`mmcv.ConfigDict`): Config of transformerlayer - in TransformerCoder. If it is obj:`mmcv.ConfigDict`, - it would be repeated `num_layer` times to a - list[`mmcv.ConfigDict`]. Default: None. - num_layers (int): The number of `TransformerLayer`. Default: None. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): - super(TransformerLayerSequence, self).__init__(init_cfg) - if isinstance(transformerlayers, dict): - transformerlayers = [ - copy.deepcopy(transformerlayers) for _ in range(num_layers) - ] - else: - assert isinstance(transformerlayers, list) and \ - len(transformerlayers) == num_layers - self.num_layers = num_layers - self.layers = ModuleList() - for i in range(num_layers): - self.layers.append(build_transformer_layer(transformerlayers[i])) - self.embed_dims = self.layers[0].embed_dims - self.pre_norm = self.layers[0].pre_norm - - def forward(self, - query, - key, - value, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerCoder`. - - Args: - query (Tensor): Input query with shape - `(num_queries, bs, embed_dims)`. - key (Tensor): The key tensor with shape - `(num_keys, bs, embed_dims)`. - value (Tensor): The value tensor with shape - `(num_keys, bs, embed_dims)`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor], optional): Each element is 2D Tensor - which is used in calculation of corresponding attention in - operation_order. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in self-attention - Default: None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: results with shape [num_queries, bs, embed_dims]. - """ - for layer in self.layers: - query = layer( - query, - key, - value, - query_pos=query_pos, - key_pos=key_pos, - attn_masks=attn_masks, - query_key_padding_mask=query_key_padding_mask, - key_padding_mask=key_padding_mask, - **kwargs) - return query diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/upsample.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/upsample.py deleted file mode 100644 index 0fd21fbf9..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/upsample.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import xavier_init -from .registry import UPSAMPLE_LAYERS - -UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) -UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) - - -@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') -class PixelShufflePack(nn.Module): - """Pixel Shuffle upsample layer. - - This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to - achieve a simple upsampling with pixel shuffle. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of the conv layer to expand the - channels. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super(PixelShufflePack, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - xavier_init(self.upsample_conv, distribution='uniform') - - def forward(self, x): - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x - - -def build_upsample_layer(cfg, *args, **kwargs): - """Build upsample layer. - - Args: - cfg (dict): The upsample layer config, which should contain: - - - type (str): Layer type. - - scale_factor (int): Upsample ratio, which is not applicable to - deconv. - - layer args: Args needed to instantiate a upsample layer. - args (argument list): Arguments passed to the ``__init__`` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the - ``__init__`` method of the corresponding conv layer. - - Returns: - nn.Module: Created upsample layer. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - raise KeyError( - f'the cfg dict must contain the key "type", but got {cfg}') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in UPSAMPLE_LAYERS: - raise KeyError(f'Unrecognized upsample type {layer_type}') - else: - upsample = UPSAMPLE_LAYERS.get(layer_type) - - if upsample is nn.Upsample: - cfg_['mode'] = layer_type - layer = upsample(*args, **kwargs, **cfg_) - return layer diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/wrappers.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/wrappers.py deleted file mode 100644 index 8aebf67bf..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/bricks/wrappers.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 - -Wrap some nn modules to support empty tensor input. Currently, these wrappers -are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask -heads are trained on only positive RoIs. -""" -import math - -import torch -import torch.nn as nn -from torch.nn.modules.utils import _pair, _triple - -from .registry import CONV_LAYERS, UPSAMPLE_LAYERS - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def obsolete_torch_version(torch_version, version_threshold): - return torch_version == 'parrots' or torch_version <= version_threshold - - -class NewEmptyTensorOp(torch.autograd.Function): - - @staticmethod - def forward(ctx, x, new_shape): - ctx.shape = x.shape - return x.new_empty(new_shape) - - @staticmethod - def backward(ctx, grad): - shape = ctx.shape - return NewEmptyTensorOp.apply(grad, shape), None - - -@CONV_LAYERS.register_module('Conv', force=True) -class Conv2d(nn.Conv2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module('Conv3d', force=True) -class Conv3d(nn.Conv3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv') -@UPSAMPLE_LAYERS.register_module('deconv', force=True) -class ConvTranspose2d(nn.ConvTranspose2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv3d') -@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) -class ConvTranspose3d(nn.ConvTranspose3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -class MaxPool2d(nn.MaxPool2d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), - _pair(self.padding), _pair(self.stride), - _pair(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class MaxPool3d(nn.MaxPool3d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), - _triple(self.padding), - _triple(self.stride), - _triple(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class Linear(torch.nn.Linear): - - def forward(self, x): - # empty tensor forward of Linear layer is supported in Pytorch 1.6 - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): - out_shape = [x.shape[0], self.out_features] - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/builder.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/builder.py deleted file mode 100644 index 7567316c5..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/builder.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..runner import Sequential -from ..utils import Registry, build_from_cfg - - -def build_model_from_cfg(cfg, registry, default_args=None): - """Build a PyTorch model from config dict(s). Different from - ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. - - Args: - cfg (dict, list[dict]): The config of modules, is is either a config - dict or a list of config dicts. If cfg is a list, a - the built modules will be wrapped with ``nn.Sequential``. - registry (:obj:`Registry`): A registry the module belongs to. - default_args (dict, optional): Default arguments to build the module. - Defaults to None. - - Returns: - nn.Module: A built nn module. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return Sequential(*modules) - else: - return build_from_cfg(cfg, registry, default_args) - - -MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/__init__.py deleted file mode 100644 index a263e31c1..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .flops_counter import get_model_complexity_info -from .fuse_conv_bn import fuse_conv_bn -from .sync_bn import revert_sync_batchnorm -from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, - KaimingInit, NormalInit, PretrainedInit, - TruncNormalInit, UniformInit, XavierInit, - bias_init_with_prob, caffe2_xavier_init, - constant_init, initialize, kaiming_init, normal_init, - trunc_normal_init, uniform_init, xavier_init) - -__all__ = [ - 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', - 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', - 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'revert_sync_batchnorm' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/flops_counter.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/flops_counter.py deleted file mode 100644 index a6045db84..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/flops_counter.py +++ /dev/null @@ -1,599 +0,0 @@ -# Modified from flops-counter.pytorch by Vladislav Sovrasov -# original repo: https://github.com/sovrasov/flops-counter.pytorch - -# MIT License - -# Copyright (c) 2018 Vladislav Sovrasov - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -import warnings -from functools import partial - -import numpy as np -import torch -import torch.nn as nn - -import mmcv - - -def get_model_complexity_info(model, - input_shape, - print_per_layer_stat=True, - as_strings=True, - input_constructor=None, - flush=False, - ost=sys.stdout): - """Get complexity information of a model. - - This method can calculate FLOPs and parameter counts of a model with - corresponding input shape. It can also print complexity information for - each layer in a model. - - Supported layers are listed as below: - - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. - - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, - ``nn.LeakyReLU``, ``nn.ReLU6``. - - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, - ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, - ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, - ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, - ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. - - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, - ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, - ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. - - Linear: ``nn.Linear``. - - Deconvolution: ``nn.ConvTranspose2d``. - - Upsample: ``nn.Upsample``. - - Args: - model (nn.Module): The model for complexity calculation. - input_shape (tuple): Input shape used for calculation. - print_per_layer_stat (bool): Whether to print complexity information - for each layer in a model. Default: True. - as_strings (bool): Output FLOPs and params counts in a string form. - Default: True. - input_constructor (None | callable): If specified, it takes a callable - method that generates input. otherwise, it will generate a random - tensor with input shape to calculate FLOPs. Default: None. - flush (bool): same as that in :func:`print`. Default: False. - ost (stream): same as ``file`` param in :func:`print`. - Default: sys.stdout. - - Returns: - tuple[float | str]: If ``as_strings`` is set to True, it will return - FLOPs and parameter counts in a string format. otherwise, it will - return those in a float number format. - """ - assert type(input_shape) is tuple - assert len(input_shape) >= 1 - assert isinstance(model, nn.Module) - flops_model = add_flops_counting_methods(model) - flops_model.eval() - flops_model.start_flops_count() - if input_constructor: - input = input_constructor(input_shape) - _ = flops_model(**input) - else: - try: - batch = torch.ones(()).new_empty( - (1, *input_shape), - dtype=next(flops_model.parameters()).dtype, - device=next(flops_model.parameters()).device) - except StopIteration: - # Avoid StopIteration for models which have no parameters, - # like `nn.Relu()`, `nn.AvgPool2d`, etc. - batch = torch.ones(()).new_empty((1, *input_shape)) - - _ = flops_model(batch) - - flops_count, params_count = flops_model.compute_average_flops_cost() - if print_per_layer_stat: - print_model_with_flops( - flops_model, flops_count, params_count, ost=ost, flush=flush) - flops_model.stop_flops_count() - - if as_strings: - return flops_to_string(flops_count), params_to_string(params_count) - - return flops_count, params_count - - -def flops_to_string(flops, units='GFLOPs', precision=2): - """Convert FLOPs number into a string. - - Note that Here we take a multiply-add counts as one FLOP. - - Args: - flops (float): FLOPs number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', - 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically - choose the most suitable unit for FLOPs. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted FLOPs number with units. - - Examples: - >>> flops_to_string(1e9) - '1.0 GFLOPs' - >>> flops_to_string(2e5, 'MFLOPs') - '0.2 MFLOPs' - >>> flops_to_string(3e-9, None) - '3e-09 FLOPs' - """ - if units is None: - if flops // 10**9 > 0: - return str(round(flops / 10.**9, precision)) + ' GFLOPs' - elif flops // 10**6 > 0: - return str(round(flops / 10.**6, precision)) + ' MFLOPs' - elif flops // 10**3 > 0: - return str(round(flops / 10.**3, precision)) + ' KFLOPs' - else: - return str(flops) + ' FLOPs' - else: - if units == 'GFLOPs': - return str(round(flops / 10.**9, precision)) + ' ' + units - elif units == 'MFLOPs': - return str(round(flops / 10.**6, precision)) + ' ' + units - elif units == 'KFLOPs': - return str(round(flops / 10.**3, precision)) + ' ' + units - else: - return str(flops) + ' FLOPs' - - -def params_to_string(num_params, units=None, precision=2): - """Convert parameter number into a string. - - Args: - num_params (float): Parameter number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'M', - 'K' and ''. If set to None, it will automatically choose the most - suitable unit for Parameter number. Default: None. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted parameter number with units. - - Examples: - >>> params_to_string(1e9) - '1000.0 M' - >>> params_to_string(2e5) - '200.0 k' - >>> params_to_string(3e-9) - '3e-09' - """ - if units is None: - if num_params // 10**6 > 0: - return str(round(num_params / 10**6, precision)) + ' M' - elif num_params // 10**3: - return str(round(num_params / 10**3, precision)) + ' k' - else: - return str(num_params) - else: - if units == 'M': - return str(round(num_params / 10.**6, precision)) + ' ' + units - elif units == 'K': - return str(round(num_params / 10.**3, precision)) + ' ' + units - else: - return str(num_params) - - -def print_model_with_flops(model, - total_flops, - total_params, - units='GFLOPs', - precision=3, - ost=sys.stdout, - flush=False): - """Print a model with FLOPs for each layer. - - Args: - model (nn.Module): The model to be printed. - total_flops (float): Total FLOPs of the model. - total_params (float): Total parameter counts of the model. - units (str | None): Converted FLOPs units. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 3. - ost (stream): same as `file` param in :func:`print`. - Default: sys.stdout. - flush (bool): same as that in :func:`print`. Default: False. - - Example: - >>> class ExampleModel(nn.Module): - - >>> def __init__(self): - >>> super().__init__() - >>> self.conv1 = nn.Conv2d(3, 8, 3) - >>> self.conv2 = nn.Conv2d(8, 256, 3) - >>> self.conv3 = nn.Conv2d(256, 8, 3) - >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) - >>> self.flatten = nn.Flatten() - >>> self.fc = nn.Linear(8, 1) - - >>> def forward(self, x): - >>> x = self.conv1(x) - >>> x = self.conv2(x) - >>> x = self.conv3(x) - >>> x = self.avg_pool(x) - >>> x = self.flatten(x) - >>> x = self.fc(x) - >>> return x - - >>> model = ExampleModel() - >>> x = (3, 16, 16) - to print the complexity information state for each layer, you can use - >>> get_model_complexity_info(model, x) - or directly use - >>> print_model_with_flops(model, 4579784.0, 37361) - ExampleModel( - 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, - (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 - (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) - (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) - (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) - (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) - (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) - ) - """ - - def accumulate_params(self): - if is_supported_instance(self): - return self.__params__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_params() - return sum - - def accumulate_flops(self): - if is_supported_instance(self): - return self.__flops__ / model.__batch_counter__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_flops() - return sum - - def flops_repr(self): - accumulated_num_params = self.accumulate_params() - accumulated_flops_cost = self.accumulate_flops() - return ', '.join([ - params_to_string( - accumulated_num_params, units='M', precision=precision), - '{:.3%} Params'.format(accumulated_num_params / total_params), - flops_to_string( - accumulated_flops_cost, units=units, precision=precision), - '{:.3%} FLOPs'.format(accumulated_flops_cost / total_flops), - self.original_extra_repr() - ]) - - def add_extra_repr(m): - m.accumulate_flops = accumulate_flops.__get__(m) - m.accumulate_params = accumulate_params.__get__(m) - flops_extra_repr = flops_repr.__get__(m) - if m.extra_repr != flops_extra_repr: - m.original_extra_repr = m.extra_repr - m.extra_repr = flops_extra_repr - assert m.extra_repr != m.original_extra_repr - - def del_extra_repr(m): - if hasattr(m, 'original_extra_repr'): - m.extra_repr = m.original_extra_repr - del m.original_extra_repr - if hasattr(m, 'accumulate_flops'): - del m.accumulate_flops - - model.apply(add_extra_repr) - print(model, file=ost, flush=flush) - model.apply(del_extra_repr) - - -def get_model_parameters_number(model): - """Calculate parameter number of a model. - - Args: - model (nn.module): The model for parameter number calculation. - - Returns: - float: Parameter number of the model. - """ - num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - return num_params - - -def add_flops_counting_methods(net_main_module): - # adding additional methods to the existing module object, - # this is done this way so that each function has access to self object - net_main_module.start_flops_count = start_flops_count.__get__( - net_main_module) - net_main_module.stop_flops_count = stop_flops_count.__get__( - net_main_module) - net_main_module.reset_flops_count = reset_flops_count.__get__( - net_main_module) - net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # noqa: E501 - net_main_module) - - net_main_module.reset_flops_count() - - return net_main_module - - -def compute_average_flops_cost(self): - """Compute average FLOPs cost. - - A method to compute average FLOPs cost, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - - Returns: - float: Current mean flops consumption per image. - """ - batches_count = self.__batch_counter__ - flops_sum = 0 - for module in self.modules(): - if is_supported_instance(module): - flops_sum += module.__flops__ - params_sum = get_model_parameters_number(self) - return flops_sum / batches_count, params_sum - - -def start_flops_count(self): - """Activate the computation of mean flops consumption per image. - - A method to activate the computation of mean flops consumption per image. - which will be available after ``add_flops_counting_methods()`` is called on - a desired net object. It should be called before running the network. - """ - add_batch_counter_hook_function(self) - - def add_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - return - - else: - handle = module.register_forward_hook( - get_modules_mapping()[type(module)]) - - module.__flops_handle__ = handle - - self.apply(partial(add_flops_counter_hook_function)) - - -def stop_flops_count(self): - """Stop computing the mean flops consumption per image. - - A method to stop computing the mean flops consumption per image, which will - be available after ``add_flops_counting_methods()`` is called on a desired - net object. It can be called to pause the computation whenever. - """ - remove_batch_counter_hook_function(self) - self.apply(remove_flops_counter_hook_function) - - -def reset_flops_count(self): - """Reset statistics computed so far. - - A method to Reset computed statistics, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - """ - add_batch_counter_variables_or_reset(self) - self.apply(add_flops_counter_variable_or_reset) - - -# ---- Internal functions -def empty_flops_counter_hook(module, input, output): - module.__flops__ += 0 - - -def upsample_flops_counter_hook(module, input, output): - output_size = output[0] - batch_size = output_size.shape[0] - output_elements_count = batch_size - for val in output_size.shape[1:]: - output_elements_count *= val - module.__flops__ += int(output_elements_count) - - -def relu_flops_counter_hook(module, input, output): - active_elements_count = output.numel() - module.__flops__ += int(active_elements_count) - - -def linear_flops_counter_hook(module, input, output): - input = input[0] - output_last_dim = output.shape[ - -1] # pytorch checks dimensions, so here we don't care much - module.__flops__ += int(np.prod(input.shape) * output_last_dim) - - -def pool_flops_counter_hook(module, input, output): - input = input[0] - module.__flops__ += int(np.prod(input.shape)) - - -def norm_flops_counter_hook(module, input, output): - input = input[0] - - batch_flops = np.prod(input.shape) - if (getattr(module, 'affine', False) - or getattr(module, 'elementwise_affine', False)): - batch_flops *= 2 - module.__flops__ += int(batch_flops) - - -def deconv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - input_height, input_width = input.shape[2:] - - kernel_height, kernel_width = conv_module.kernel_size - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = ( - kernel_height * kernel_width * in_channels * filters_per_channel) - - active_elements_count = batch_size * input_height * input_width - overall_conv_flops = conv_per_position_flops * active_elements_count - bias_flops = 0 - if conv_module.bias is not None: - output_height, output_width = output.shape[2:] - bias_flops = out_channels * batch_size * output_height * output_width - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def conv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - output_dims = list(output.shape[2:]) - - kernel_dims = list(conv_module.kernel_size) - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = int( - np.prod(kernel_dims)) * in_channels * filters_per_channel - - active_elements_count = batch_size * int(np.prod(output_dims)) - - overall_conv_flops = conv_per_position_flops * active_elements_count - - bias_flops = 0 - - if conv_module.bias is not None: - - bias_flops = out_channels * active_elements_count - - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def batch_counter_hook(module, input, output): - batch_size = 1 - if len(input) > 0: - # Can have multiple inputs, getting the first one - input = input[0] - batch_size = len(input) - else: - warnings.warn('No positional inputs found for a module, ' - 'assuming batch size is 1.') - module.__batch_counter__ += batch_size - - -def add_batch_counter_variables_or_reset(module): - - module.__batch_counter__ = 0 - - -def add_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - return - - handle = module.register_forward_hook(batch_counter_hook) - module.__batch_counter_handle__ = handle - - -def remove_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - module.__batch_counter_handle__.remove() - del module.__batch_counter_handle__ - - -def add_flops_counter_variable_or_reset(module): - if is_supported_instance(module): - if hasattr(module, '__flops__') or hasattr(module, '__params__'): - warnings.warn('variables __flops__ or __params__ are already ' - 'defined for the module' + type(module).__name__ + - ' ptflops can affect your code!') - module.__flops__ = 0 - module.__params__ = get_model_parameters_number(module) - - -def is_supported_instance(module): - if type(module) in get_modules_mapping(): - return True - return False - - -def remove_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - module.__flops_handle__.remove() - del module.__flops_handle__ - - -def get_modules_mapping(): - return { - # convolutions - nn.Conv1d: conv_flops_counter_hook, - nn.Conv2d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, - nn.Conv3d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, - # activations - nn.ReLU: relu_flops_counter_hook, - nn.PReLU: relu_flops_counter_hook, - nn.ELU: relu_flops_counter_hook, - nn.LeakyReLU: relu_flops_counter_hook, - nn.ReLU6: relu_flops_counter_hook, - # poolings - nn.MaxPool1d: pool_flops_counter_hook, - nn.AvgPool1d: pool_flops_counter_hook, - nn.AvgPool2d: pool_flops_counter_hook, - nn.MaxPool2d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, - nn.MaxPool3d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, - nn.AvgPool3d: pool_flops_counter_hook, - nn.AdaptiveMaxPool1d: pool_flops_counter_hook, - nn.AdaptiveAvgPool1d: pool_flops_counter_hook, - nn.AdaptiveMaxPool2d: pool_flops_counter_hook, - nn.AdaptiveAvgPool2d: pool_flops_counter_hook, - nn.AdaptiveMaxPool3d: pool_flops_counter_hook, - nn.AdaptiveAvgPool3d: pool_flops_counter_hook, - # normalizations - nn.BatchNorm1d: norm_flops_counter_hook, - nn.BatchNorm2d: norm_flops_counter_hook, - nn.BatchNorm3d: norm_flops_counter_hook, - nn.GroupNorm: norm_flops_counter_hook, - nn.InstanceNorm1d: norm_flops_counter_hook, - nn.InstanceNorm2d: norm_flops_counter_hook, - nn.InstanceNorm3d: norm_flops_counter_hook, - nn.LayerNorm: norm_flops_counter_hook, - # FC - nn.Linear: linear_flops_counter_hook, - mmcv.cnn.bricks.Linear: linear_flops_counter_hook, - # Upscale - nn.Upsample: upsample_flops_counter_hook, - # Deconvolution - nn.ConvTranspose2d: deconv_flops_counter_hook, - mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, - } diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py deleted file mode 100644 index cb7076f80..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/fuse_conv_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def _fuse_conv_bn(conv, bn): - """Fuse conv and bn into one module. - - Args: - conv (nn.Module): Conv to be fused. - bn (nn.Module): BN to be fused. - - Returns: - nn.Module: Fused module. - """ - conv_w = conv.weight - conv_b = conv.bias if conv.bias is not None else torch.zeros_like( - bn.running_mean) - - factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) - conv.weight = nn.Parameter(conv_w * - factor.reshape([conv.out_channels, 1, 1, 1])) - conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) - return conv - - -def fuse_conv_bn(module): - """Recursively fuse conv and bn in a module. - - During inference, the functionary of batch norm layers is turned off - but only the mean and var alone channels are used, which exposes the - chance to fuse it with the preceding conv layers to save computations and - simplify network structures. - - Args: - module (nn.Module): Module to be fused. - - Returns: - nn.Module: Fused module. - """ - last_conv = None - last_conv_name = None - - for name, child in module.named_children(): - if isinstance(child, - (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): - if last_conv is None: # only fuse BN that is after Conv - continue - fused_conv = _fuse_conv_bn(last_conv, child) - module._modules[last_conv_name] = fused_conv - # To reduce changes, set BN as Identity instead of deleting it. - module._modules[name] = nn.Identity() - last_conv = None - elif isinstance(child, nn.Conv2d): - last_conv = child - last_conv_name = name - else: - fuse_conv_bn(child) - return module diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/sync_bn.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/sync_bn.py deleted file mode 100644 index 0c52526e9..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/sync_bn.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -import mmcv - - -class _BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): - """A general BatchNorm layer without input dimension check. - - Reproduced from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc - is `_check_input_dim` that is designed for tensor sanity checks. - The check has been bypassed in this class for the convenience of converting - SyncBatchNorm. - """ - - def _check_input_dim(self, input): - return - - -def revert_sync_batchnorm(module): - """Helper function to convert all `SyncBatchNorm` (SyncBN) and - `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to - `BatchNormXd` layers. - - Adapted from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - - Args: - module (nn.Module): The module containing `SyncBatchNorm` layers. - - Returns: - module_output: The converted module with `BatchNormXd` layers. - """ - module_output = module - module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] - if hasattr(mmcv, 'ops'): - module_checklist.append(mmcv.ops.SyncBatchNorm) - if isinstance(module, tuple(module_checklist)): - module_output = _BatchNormXd(module.num_features, module.eps, - module.momentum, module.affine, - module.track_running_stats) - if module.affine: - # no_grad() may not be needed here but - # just to be consistent with `convert_sync_batchnorm()` - with torch.no_grad(): - module_output.weight = module.weight - module_output.bias = module.bias - module_output.running_mean = module.running_mean - module_output.running_var = module.running_var - module_output.num_batches_tracked = module.num_batches_tracked - module_output.training = module.training - # qconfig exists in quantized models - if hasattr(module, 'qconfig'): - module_output.qconfig = module.qconfig - for name, child in module.named_children(): - module_output.add_module(name, revert_sync_batchnorm(child)) - del module - return module_output diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/weight_init.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/weight_init.py deleted file mode 100644 index 0ac08c87f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/cnn/utils/weight_init.py +++ /dev/null @@ -1,685 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings - -import numpy as np -import torch -import torch.nn as nn -from torch import Tensor - -from mmcv.utils import Registry, build_from_cfg, get_logger, print_log - -INITIALIZERS = Registry('initializer') - - -def update_init_info(module, init_info): - """Update the `_params_init_info` in the module if the value of parameters - are changed. - - Args: - module (obj:`nn.Module`): The module of PyTorch with a user-defined - attribute `_params_init_info` which records the initialization - information. - init_info (str): The string that describes the initialization. - """ - assert hasattr( - module, - '_params_init_info'), f'Can not find `_params_init_info` in {module}' - for name, param in module.named_parameters(): - - assert param in module._params_init_info, ( - f'Find a new :obj:`Parameter` ' - f'named `{name}` during executing the ' - f'`init_weights` of ' - f'`{module.__class__.__name__}`. ' - f'Please do not add or ' - f'replace parameters during executing ' - f'the `init_weights`. ') - - # The parameter has been changed during executing the - # `init_weights` of module - mean_value = param.data.mean() - if module._params_init_info[param]['tmp_mean_value'] != mean_value: - module._params_init_info[param]['init_info'] = init_info - module._params_init_info[param]['tmp_mean_value'] = mean_value - - -def constant_init(module, val, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.constant_(module.weight, val) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def xavier_init(module, gain=1, bias=0, distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.xavier_uniform_(module.weight, gain=gain) - else: - nn.init.xavier_normal_(module.weight, gain=gain) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def normal_init(module, mean=0, std=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.normal_(module.weight, mean, std) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def trunc_normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - trunc_normal_(module.weight, mean, std, a, b) # type: ignore - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) # type: ignore - - -def uniform_init(module, a=0, b=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.uniform_(module.weight, a, b) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def kaiming_init(module, - a=0, - mode='fan_out', - nonlinearity='relu', - bias=0, - distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.kaiming_uniform_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - else: - nn.init.kaiming_normal_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def caffe2_xavier_init(module, bias=0): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - kaiming_init( - module, - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - bias=bias, - distribution='uniform') - - -def bias_init_with_prob(prior_prob): - """initialize conv/fc bias value according to a given probability value.""" - bias_init = float(-np.log((1 - prior_prob) / prior_prob)) - return bias_init - - -def _get_bases_name(m): - return [b.__name__ for b in m.__class__.__bases__] - - -class BaseInit(object): - - def __init__(self, *, bias=0, bias_prob=None, layer=None): - self.wholemodule = False - if not isinstance(bias, (int, float)): - raise TypeError(f'bias must be a number, but got a {type(bias)}') - - if bias_prob is not None: - if not isinstance(bias_prob, float): - raise TypeError(f'bias_prob type must be float, \ - but got {type(bias_prob)}') - - if layer is not None: - if not isinstance(layer, (str, list)): - raise TypeError(f'layer must be a str or a list of str, \ - but got a {type(layer)}') - else: - layer = [] - - if bias_prob is not None: - self.bias = bias_init_with_prob(bias_prob) - else: - self.bias = bias - self.layer = [layer] if isinstance(layer, str) else layer - - def _get_init_info(self): - info = f'{self.__class__.__name__}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Constant') -class ConstantInit(BaseInit): - """Initialize module parameters with constant values. - - Args: - val (int | float): the value to fill the weights in the module with - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, val, **kwargs): - super().__init__(**kwargs) - self.val = val - - def __call__(self, module): - - def init(m): - if self.wholemodule: - constant_init(m, self.val, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - constant_init(m, self.val, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Xavier') -class XavierInit(BaseInit): - r"""Initialize module parameters with values according to the method - described in `Understanding the difficulty of training deep feedforward - neural networks - Glorot, X. & Bengio, Y. (2010). - `_ - - Args: - gain (int | float): an optional scaling factor. Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` - or ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, gain=1, distribution='normal', **kwargs): - super().__init__(**kwargs) - self.gain = gain - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - xavier_init(m, self.gain, self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - xavier_init(m, self.gain, self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: gain={self.gain}, ' \ - f'distribution={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Normal') -class NormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. - - Args: - mean (int | float):the mean of the normal distribution. Defaults to 0. - std (int | float): the standard deviation of the normal distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, mean=0, std=1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.std = std - - def __call__(self, module): - - def init(m): - if self.wholemodule: - normal_init(m, self.mean, self.std, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - normal_init(m, self.mean, self.std, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: mean={self.mean},' \ - f' std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='TruncNormal') -class TruncNormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values - outside :math:`[a, b]`. - - Args: - mean (float): the mean of the normal distribution. Defaults to 0. - std (float): the standard deviation of the normal distribution. - Defaults to 1. - a (float): The minimum cutoff value. - b ( float): The maximum cutoff value. - bias (float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - **kwargs) -> None: - super().__init__(**kwargs) - self.mean = mean - self.std = std - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ - f' mean={self.mean}, std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Uniform') -class UniformInit(BaseInit): - r"""Initialize module parameters with values drawn from the uniform - distribution :math:`\mathcal{U}(a, b)`. - - Args: - a (int | float): the lower bound of the uniform distribution. - Defaults to 0. - b (int | float): the upper bound of the uniform distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, a=0, b=1, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def __call__(self, module): - - def init(m): - if self.wholemodule: - uniform_init(m, self.a, self.b, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - uniform_init(m, self.a, self.b, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a},' \ - f' b={self.b}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Kaiming') -class KaimingInit(BaseInit): - r"""Initialize module parameters with the values according to the method - described in `Delving deep into rectifiers: Surpassing human-level - performance on ImageNet classification - He, K. et al. (2015). - `_ - - Args: - a (int | float): the negative slope of the rectifier used after this - layer (only used with ``'leaky_relu'``). Defaults to 0. - mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing - ``'fan_in'`` preserves the magnitude of the variance of the weights - in the forward pass. Choosing ``'fan_out'`` preserves the - magnitudes in the backwards pass. Defaults to ``'fan_out'``. - nonlinearity (str): the non-linear function (`nn.functional` name), - recommended to use only with ``'relu'`` or ``'leaky_relu'`` . - Defaults to 'relu'. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` or - ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - a=0, - mode='fan_out', - nonlinearity='relu', - distribution='normal', - **kwargs): - super().__init__(**kwargs) - self.a = a - self.mode = mode - self.nonlinearity = nonlinearity - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ - f'nonlinearity={self.nonlinearity}, ' \ - f'distribution ={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Caffe2Xavier') -class Caffe2XavierInit(KaimingInit): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - def __init__(self, **kwargs): - super().__init__( - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='uniform', - **kwargs) - - def __call__(self, module): - super().__call__(module) - - -@INITIALIZERS.register_module(name='Pretrained') -class PretrainedInit(object): - """Initialize module by loading a pretrained model. - - Args: - checkpoint (str): the checkpoint file of the pretrained model should - be load. - prefix (str, optional): the prefix of a sub-module in the pretrained - model. it is for loading a part of the pretrained model to - initialize. For example, if we would like to only load the - backbone of a detector model, we can set ``prefix='backbone.'``. - Defaults to None. - map_location (str): map tensors into proper locations. - """ - - def __init__(self, checkpoint, prefix=None, map_location=None): - self.checkpoint = checkpoint - self.prefix = prefix - self.map_location = map_location - - def __call__(self, module): - from mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, - load_state_dict) - logger = get_logger('mmcv') - if self.prefix is None: - print_log(f'load model from: {self.checkpoint}', logger=logger) - load_checkpoint( - module, - self.checkpoint, - map_location=self.map_location, - strict=False, - logger=logger) - else: - print_log( - f'load {self.prefix} in model from: {self.checkpoint}', - logger=logger) - state_dict = _load_checkpoint_with_prefix( - self.prefix, self.checkpoint, map_location=self.map_location) - load_state_dict(module, state_dict, strict=False, logger=logger) - - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: load from {self.checkpoint}' - return info - - -def _initialize(module, cfg, wholemodule=False): - func = build_from_cfg(cfg, INITIALIZERS) - # wholemodule flag is for override mode, there is no layer key in override - # and initializer will give init values for the whole module with the name - # in override. - func.wholemodule = wholemodule - func(module) - - -def _initialize_override(module, override, cfg): - if not isinstance(override, (dict, list)): - raise TypeError(f'override must be a dict or a list of dict, \ - but got {type(override)}') - - override = [override] if isinstance(override, dict) else override - - for override_ in override: - - cp_override = copy.deepcopy(override_) - name = cp_override.pop('name', None) - if name is None: - raise ValueError('`override` must contain the key "name",' - f'but got {cp_override}') - # if override only has name key, it means use args in init_cfg - if not cp_override: - cp_override.update(cfg) - # if override has name key and other args except type key, it will - # raise error - elif 'type' not in cp_override.keys(): - raise ValueError( - f'`override` need "type" key, but got {cp_override}') - - if hasattr(module, name): - _initialize(getattr(module, name), cp_override, wholemodule=True) - else: - raise RuntimeError(f'module did not have attribute {name}, ' - f'but init_cfg is {cp_override}.') - - -def initialize(module, init_cfg): - r"""Initialize a module. - - Args: - module (``torch.nn.Module``): the module will be initialized. - init_cfg (dict | list[dict]): initialization configuration dict to - define initializer. OpenMMLab has implemented 6 initializers - including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, - ``Kaiming``, and ``Pretrained``. - - Example: - >>> module = nn.Linear(2, 3, bias=True) - >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) - >>> initialize(module, init_cfg) - - >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) - >>> # define key ``'layer'`` for initializing layer with different - >>> # configuration - >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), - dict(type='Constant', layer='Linear', val=2)] - >>> initialize(module, init_cfg) - - >>> # define key``'override'`` to initialize some specific part in - >>> # module - >>> class FooNet(nn.Module): - >>> def __init__(self): - >>> super().__init__() - >>> self.feat = nn.Conv2d(3, 16, 3) - >>> self.reg = nn.Conv2d(16, 10, 3) - >>> self.cls = nn.Conv2d(16, 5, 3) - >>> model = FooNet() - >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', - >>> override=dict(type='Constant', name='reg', val=3, bias=4)) - >>> initialize(model, init_cfg) - - >>> model = ResNet(depth=50) - >>> # Initialize weights with the pretrained model. - >>> init_cfg = dict(type='Pretrained', - checkpoint='torchvision://resnet50') - >>> initialize(model, init_cfg) - - >>> # Initialize weights of a sub-module with the specific part of - >>> # a pretrained model by using "prefix". - >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ - >>> 'retinanet_r50_fpn_1x_coco/'\ - >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' - >>> init_cfg = dict(type='Pretrained', - checkpoint=url, prefix='backbone.') - """ - if not isinstance(init_cfg, (dict, list)): - raise TypeError(f'init_cfg must be a dict or a list of dict, \ - but got {type(init_cfg)}') - - if isinstance(init_cfg, dict): - init_cfg = [init_cfg] - - for cfg in init_cfg: - # should deeply copy the original config because cfg may be used by - # other modules, e.g., one init_cfg shared by multiple bottleneck - # blocks, the expected cfg will be changed after pop and will change - # the initialization behavior of other modules - cp_cfg = copy.deepcopy(cfg) - override = cp_cfg.pop('override', None) - _initialize(module, cp_cfg) - - if override is not None: - cp_cfg.pop('layer', None) - _initialize_override(module, override, cp_cfg) - else: - # All attributes in module have same initialization. - pass - - -def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, - b: float) -> Tensor: - # Method based on - # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf - # Modified from - # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - def norm_cdf(x): - # Computes standard normal cumulative distribution function - return (1. + math.erf(x / math.sqrt(2.))) / 2. - - if (mean < a - 2 * std) or (mean > b + 2 * std): - warnings.warn( - 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' - 'The distribution of values may be incorrect.', - stacklevel=2) - - with torch.no_grad(): - # Values are generated by using a truncated uniform distribution and - # then using the inverse CDF for the normal distribution. - # Get upper and lower cdf values - lower = norm_cdf((a - mean) / std) - upper = norm_cdf((b - mean) / std) - - # Uniformly fill tensor with values from [lower, upper], then translate - # to [2lower-1, 2upper-1]. - tensor.uniform_(2 * lower - 1, 2 * upper - 1) - - # Use inverse cdf transform for normal distribution to get truncated - # standard normal - tensor.erfinv_() - - # Transform to proper mean, std - tensor.mul_(std * math.sqrt(2.)) - tensor.add_(mean) - - # Clamp to ensure it's in the proper range - tensor.clamp_(min=a, max=b) - return tensor - - -def trunc_normal_(tensor: Tensor, - mean: float = 0., - std: float = 1., - a: float = -2., - b: float = 2.) -> Tensor: - r"""Fills the input Tensor with values drawn from a truncated - normal distribution. The values are effectively drawn from the - normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` - with values outside :math:`[a, b]` redrawn until they are within - the bounds. The method used for generating the random values works - best when :math:`a \leq \text{mean} \leq b`. - - Modified from - https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - - Args: - tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. - mean (float): the mean of the normal distribution. - std (float): the standard deviation of the normal distribution. - a (float): the minimum cutoff value. - b (float): the maximum cutoff value. - """ - return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/__init__.py deleted file mode 100644 index 6ac55e63b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from . import ipu, mlu - -__all__ = ['mlu', 'ipu'] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/__init__.py deleted file mode 100644 index d550865ad..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import IPUFp16OptimizerHook - from .model_wrapper import ipu_model_wrapper - from .runner import IPUBaseRunner, IPUEpochBasedRunner, IPUIterBasedRunner - from .utils import cfg2options - __all__ = [ - 'cfg2options', 'ipu_model_wrapper', 'IPUFp16OptimizerHook', - 'IPUDataLoader', 'IPUBaseRunner', 'IPUEpochBasedRunner', - 'IPUIterBasedRunner' - ] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/dataloader.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/dataloader.py deleted file mode 100644 index 1485df2f3..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/dataloader.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence -from functools import partial - -import poptorch -from torch.utils.data.dataloader import default_collate - -from mmcv.parallel import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Put each data field into a tensor/DataContainer with outer dimension - batch size. - - TODO support for - :type:`~mmcv.parallel.DataContainer`. Currently, it will be ignored. - There are 3 cases. - - 1. cpu_only = True, e.g., meta data. - 2. cpu_only = False, stack = True, e.g., images tensors. - 3. cpu_only = False, stack = False, e.g., gt bboxes. - """ - - if not isinstance(batch, Sequence): - raise TypeError( - f'`batch` should be a sequence, but got {type(batch)}.') - - if isinstance(batch[0], DataContainer): - # TODO `DataContainer` will be supported in the future. - raise TypeError('DataContainer is not supported in ipu data loader.') - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - collated_batch = [] - for samples in transposed: - if not isinstance(samples[0], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch.append(collate(samples, samples_per_gpu)) - return collated_batch - elif isinstance(batch[0], Mapping): - collated_batch = {} - for key in batch[0]: - if not isinstance(batch[0][key], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch[key] = collate([d[key] for d in batch]) - return collated_batch - else: - return default_collate(batch) - - -class IPUDataLoader(poptorch.DataLoader): - """Thin wrapper of `torch.utils.data.DataLoader`. - - Compared with the pytorch DataLoder, this DataLoder changes the way of - calculation of batch size and adds the AsynchronousDataAccessor to - load and release data faster in cpu mode. - - If this data loader is used in a distributed execution environment, it will - ensure that each process uses a different subset of the dataset, providing - you first call ``options.randomSeed(N)`` with an integer N which is the - same across all hosts. - - Args: - dataset (torch.utils.data.Dataset): The dataset to get the data from. - options (poptorch.Options): Options that will be used to compile - and run the model. - batch_size (int, optional): This is the batch size in the conventional - sense of being the size that runs through an operation in the model - at any given time. - shuffle (bool, optional): set to ``True`` to have the data reshuffled - at every epoch (default: ``False``). - num_workers (int, optional): how many subprocesses to use for data - loading. ``0`` means that the data will be loaded in the main - process. (default: ``0``) - drop_last (bool, optional): If True and the number of elements in the - dataset is not a multiple of the combined batch size then the - incomplete batch at the end will be dropped. - persistent_workers (bool, optional): Re-use workers between - iterations if True. - auto_distributed_partitioning (bool, optional): If True, partitions the - dataset for distributed execution automatically. Otherwise, it is - assumed that partitioning has been handled manually. - mode (poptorch.DataLoaderMode, optional): If `DataLoaderMode.Async`, - uses an :py:class:`~poptorch.AsynchronousDataAccessor` to access - the dataset. If `DataLoaderMode.Sync`, accesses the dataset - synchronously. - async_options (Dict[str, Any], optional): Options to pass to - :py:class:`~poptorch.AsynchronousDataAccessor`. - rebatched_worker_size (int, optional): When using AsyncRebatched: batch - size of the tensors loaded by the workers. - Default to the combined batch size. - If specified the ``rebatched_worker_size`` must be less than - or equal to the combined batch size. - kwargs (Dict[str, Any], optional): Other options to pass to PyTorch's - ``DataLoader`` constructor. - """ - - def __init__(self, - dataset, - options, - batch_size=1, - shuffle=False, - num_workers=0, - drop_last=True, - persistent_workers=True, - auto_distributed_partitioning=True, - mode='sync', - async_options=None, - rebatched_worker_size=None, - **kwargs): - """Lazy init: - - In many frameworks, the dataloader will be constructed before the - initialization of the ipu options, so the lazy init method is used - here, and the real initialization will not be done until the dataloader - needs to be used and the options are input. - """ - # lazy init: sometimes, we cannot get IPU options when build data - # loader - self.kwargs = { - 'dataset': dataset, - 'batch_size': batch_size, - 'shuffle': shuffle, - 'num_workers': num_workers, - 'drop_last': drop_last, - 'persistent_workers': persistent_workers, - 'auto_distributed_partitioning': auto_distributed_partitioning, - 'mode': mode, - 'collate_fn': partial(collate, samples_per_gpu=batch_size), - 'async_options': async_options, - 'rebatched_worker_size': rebatched_worker_size, - **kwargs - } - self.dataset = dataset - self.initialized = False - if options: - self.init(options=options) - - def init(self, options, **kwargs): - if not self.initialized: - kwargs = {**self.kwargs, **kwargs, 'options': options} - if kwargs['mode'] == 'sync': - kwargs['mode'] = poptorch.DataLoaderMode.Sync - elif kwargs['mode'] == 'async': - kwargs['mode'] = poptorch.DataLoaderMode.AsyncRebatched - if kwargs['async_options'] is None: - kwargs['async_options'] = { - 'load_indefinitely': True, - 'buffer_size': 8 - } - if kwargs['rebatched_worker_size'] is None: - kwargs['rebatched_worker_size'] = 128 - super().__init__(**kwargs) - self.initialized = True - - return self diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py deleted file mode 100644 index a6f3b3cd2..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/hierarchical_data_manager.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import numpy as np -import torch - -from mmcv.parallel import DataContainer - -# A customized None type for HierarchicalDataManager -HierarchicalDataNone = object() - - -class HierarchicalDataManager: - """A class manage all the tensors in the hierarchical data. - - At present, the input data structure accepted by IPU is limited, - when the input data structure of mmcv varies. - Here, an intermediate class is needed to get and update tensors - from the original data. - - HierarchicalDataManager will record a hierarchical input/output data in - self._hierarchical_data. For example, we have an input data: - {'img': tensorA, 'label': tensorB, 'img_metas': [tensorC, tensorD]} - To enable IPU to use the input, HierarchicalDataManager will collect - the torch tensors from self._hierarchical_data into a tuple like: - (tensorA, tensorB, tensorC, tensorD). - Meanwhile, the return of IPU is a tuple of tensors, HierarchicalDataManager - also have a function named update_all_tensors to update tensors in - self._hierarchical_data which is the output for upper calls. - - Args: - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - """ - - def __init__(self, logger=None): - self.atomic_types = (int, str, float, np.ndarray, type(None)) - self.warning = warnings.warn if logger is None else logger.warning - # enable or disable input data's shape and value check - self.quick_mode = False - self._hierarchical_data = None - - def quick(self): - self.quick_mode = True - - def compare_atomic_type(self, a, b): - """Compare data, supported datatypes are numpy array and python basic - types.""" - if isinstance(a, np.ndarray): - return np.all(a == b) - else: - return a == b - - def record_hierarchical_data(self, data): - """Record a hierarchical data.""" - if self._hierarchical_data is not None: - if isinstance(data, torch.Tensor): - assert isinstance(self._hierarchical_data, torch.Tensor), \ - 'original hierarchical data is not torch.tensor' - self._hierarchical_data = data - else: - self.update_hierarchical_data(data) - else: - self._hierarchical_data = data - - @property - def hierarchical_data(self): - return self._hierarchical_data - - def update_hierarchical_data(self, - dataA, - dataB=HierarchicalDataNone, - strict=True, - address='data'): - """Update dataB with dataA in-place. - - Args: - dataA (list or dict or tuple): New hierarchical data. - dataB (list or dict or tuple): hierarchical data to update. - if not specified, self.hierarchical_data will be updated then. - strict (bool, optional): If true, an error will be reported - when the following conditions occur: - 1. Non-torch.Tensor data changed. - 2. Torch.Tensor data shape changed. - address (str): Record the address of current data to be updated. - Default: 'data'. - """ - if dataB is HierarchicalDataNone: - dataB = self.hierarchical_data - - # Update with a da ta with the same structure - # but different values(tensors and basic python data types) - if isinstance(dataA, (tuple, list)): - for idx, node in enumerate(dataA): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(idx)}]' - assert isinstance(node, type(dataB[idx])),\ - f'data structure changed: {new_address}' - if isinstance(node, torch.Tensor): - dataB[idx] = node - else: - self.update_hierarchical_data( - node, dataB[idx], strict, address=new_address) - elif isinstance(dataA, dict): - for k, v in dataA.items(): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(k)}]' - assert isinstance(v, type(dataB[k])),\ - f'data structure changed: {new_address}' - if isinstance(v, torch.Tensor): - dataB[k] = v - else: - self.update_hierarchical_data( - v, dataB[k], strict, address=new_address) - elif isinstance(dataA, self.atomic_types): - if not self.quick_mode: - is_equal = self.compare_atomic_type(dataA, dataB) - if not is_equal: - if strict: - raise ValueError( - 'all data except torch.Tensor should be same, ' - f'but data({address}) is changed.') - else: - self.warning( - f'find a non-torch.Tensor data({type(dataA)}) ' - f'changed, and the address is {address}') - elif isinstance(dataA, DataContainer): - if not self.quick_mode: - assert isinstance(dataB, DataContainer) - new_address = address + '.data' - self.update_hierarchical_data( - dataA.data, dataB.data, False, address=new_address) - else: - raise NotImplementedError( - f'not supported datatype:{type(dataA)}, address is {address}') - - def collect_all_tensors(self, hierarchical_data=None): - """Collect torch.Tensor data from self.hierarchical_data to a list and - return.""" - # get a list of tensor from self._hierarchical_data - if hierarchical_data is None: - hierarchical_data = self._hierarchical_data - tensors = [] - if isinstance(hierarchical_data, torch.Tensor): - tensors = [hierarchical_data] - else: - self._collect_tensors(hierarchical_data, tensors) - return tensors - - def _collect_tensors(self, data, tensors): - if isinstance(data, (tuple, list)): - for node in data: - if isinstance(node, torch.Tensor): - tensors.append(node) - else: - self._collect_tensors(node, tensors) - elif isinstance(data, dict): - for v in data.values(): - if isinstance(v, torch.Tensor): - tensors.append(v) - else: - self._collect_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._collect_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def update_all_tensors(self, tensors): - """Put tensors from tuple back to self.hierarchical_data.""" - if isinstance(self._hierarchical_data, torch.Tensor): - print(tensors, len(tensors)) - assert len(tensors) == 1 - assert isinstance(tensors[0], torch.Tensor) - self._hierarchical_data = tensors[0] - else: - # convert to list if tensors is tuple - tensors = list(tensors) - self._set_tensors(self._hierarchical_data, tensors) - return self.hierarchical_data - - def _set_tensors(self, data, tensors): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = tensors.pop(0) - else: - self._set_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._set_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def clean_all_tensors(self): - """Delete tensors from self.hierarchical_data.""" - self._clean_tensors(self._hierarchical_data) - - def _clean_tensors(self, data): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = None - else: - self._clean_tensors(v) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._clean_tensors(data.data) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/hook_wrapper.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/hook_wrapper.py deleted file mode 100644 index 141afb86d..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/hook_wrapper.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import HOOKS, LrUpdaterHook, OptimizerHook -from mmcv.utils import TORCH_VERSION, digit_version - - -def wrap_lr_updater_hook(lr_hook_class): - """A wrapper function to wrap any subclass of LrUpdaterHook. - - IPU needs extra operations to upload optimizer settings. This wrapper will - override function(_set_lr) of a subclass of LrUpdaterHook. - """ - assert issubclass(lr_hook_class, LrUpdaterHook) - - class ipu_lr_hook_class(lr_hook_class): - - def _set_lr(self, runner, *args, **kwargs): - super()._set_lr(runner, *args, **kwargs) - # convert torch optimizer to poptorch optimizer - runner.model.setOptimizer(runner.optimizer) - - return ipu_lr_hook_class - - -def wrap_optimizer_hook(optimizer_hook_class): - """A wrapper function to wrap OptimizerHook. - - This is an non-intrusive implementation of wrapping optimizer hook (or you - need to change every config file to use IPU optimizer hook) IPU's clip-norm - implementation is different from pytorch, so there should be an error - raised when using clip-norm. - """ - - class ipu_optimizer_hook_class(OptimizerHook): - - def __init__(self, **kwargs): - super().__init__(**kwargs) - if self.grad_clip is not None: - raise NotImplementedError('IPU does not support gradient clip') - - return ipu_optimizer_hook_class - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class IPUFp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - assert grad_clip is None,\ - 'IPU mode does not support `grad_clip` currently' - assert coalesce,\ - 'implemented all reduce in distributed training currently' - assert bucket_size_mb == -1,\ - '`bucket_size_mb` should not be set in IPU mode' - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - raise NotImplementedError( - 'IPU mode does not support dynamic loss scale currently') - elif isinstance(loss_scale, float): - self.loss_scale = loss_scale - elif isinstance(loss_scale, dict): - raise NotImplementedError( - 'IPU mode supports single scale currently') - else: - raise ValueError( - f'loss_scale should be float, but got {loss_scale} ') - - def after_train_iter(self, runner): - pass - -else: - raise RuntimeError('The IPU mode only supports torch 1.6 and above') diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/model_wrapper.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/model_wrapper.py deleted file mode 100644 index c345537e2..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/model_wrapper.py +++ /dev/null @@ -1,721 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect -from collections import OrderedDict -from typing import Optional, Union - -import poptorch -import torch -import torch.nn as nn -from poptorch import PoplarExecutor, __version__, identity_loss -from poptorch._args_parser import ArgsParser - -from mmcv.runner import auto_fp16 -from .hierarchical_data_manager import HierarchicalDataManager -from .utils import compare_ndarray, model_sharding, recomputation_checkpoint - - -class DictArgsParser(ArgsParser): - """A helper class for handling model input. - - Args: - inputs (list): Inputs of model. - """ - - def __init__(self, inputs): - # Combine args and kwargs: - self._has_variadic_arguments = True - self._varnames = list(inputs.keys()) - self._defaults = [inspect.Parameter.empty for _ in self._varnames] - self._warned_not_contiguous_input = False - - -class WrappedNet(nn.Module): - """A net wrapper for model conversion. - - This wrapper will make some changes and add some extra functions to - training/inference model. - - Args: - model (:obj:`nn.Module`): The model to run. - inputs_manager (:obj:`HierarchicalDataManager`): A parser - converting inputs from tuple to dictionary. - outputs_manager (:obj:`HierarchicalDataManager`): A parser - converting outputs from dictionary to tuple. - inter_outputs_in_cpu (dict): Specify the features to be - recorded. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - model, - inputs_manager, - outputs_manager, - inter_outputs_in_cpu, - modules_to_record=None): - super().__init__() - self.model = model - self.inputs_manager = inputs_manager - self.outputs_manager = outputs_manager - self.training = model.training - # Register a hook function to capture the intermediate features - # generated by the network to align the outputs between ipu and cpu - # Used to confirm whether the implementation of CPU is consistent - # with the implementation of IPU - self.inter_outputs_in_cpu = inter_outputs_in_cpu - if modules_to_record is None: - modules_to_record = [] - - for idx, (name, module) in enumerate(model.named_modules()): - if name in modules_to_record or idx in modules_to_record: - features_hook = self.get_input_output_hook( - name, idx, self.inter_outputs_in_cpu) - module.register_forward_hook(hook=features_hook) - - def get_input_output_hook(self, name, idx, save_dict): - - def input_output_hook(module, fea_in, fea_out): - if isinstance(fea_in, tuple): - fea_in = list(fea_in) - if isinstance(fea_out, tuple): - fea_out = list(fea_out) - save_dict[name] = { - 'fea_in': fea_in, - 'fea_out': fea_out, - 'idx': idx - } - return None - - return input_output_hook - - def forward(self, inputs_tuple): - """This function is used to be compiled to ipu, the inputs and outputs - need to be tuples, so here we need to restore the input back to a - dictionary and convert the output to a tuple.""" - self.inputs_manager.update_all_tensors(inputs_tuple) - kwargs = {**(self.inputs_manager.hierarchical_data)} - if self.training: - outputs = self.forward_train(kwargs) - # tell poptorch which loss will be used finally - identity_loss(outputs['loss'], reduction='none') - else: - outputs = self.forward_eval(kwargs) - - if isinstance(outputs, torch.Tensor): - # currently not support single tensor output, - # need to wrap it with a dictionary, - # use a keyword to identify this case - outputs = {'output of WrappedNet: single tensor': outputs} - - # if there are some features need to be record, add extra outputs - for name in self.inter_outputs_in_cpu: - outputs[name] = self.inter_outputs_in_cpu[name] - - # record all the places of return tensors in the converting stage - # while in the real run stage, all the tensor are changed in-place - # that means the output can be obtained directly outside this function - self.outputs_manager.record_hierarchical_data(outputs) - plain_outputs = self.outputs_manager.collect_all_tensors() - return plain_outputs - - def forward_train(self, kwargs): - optimizer = kwargs.pop('optimizer') - outputs = self.train_step(kwargs, optimizer) - return outputs - - def train_step(self, data, optimizer=None, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating are also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer`, optional): The - optimizer of runner is passed to ``train_step()``. This - argument is unused and reserved. - - Returns: - dict: Dict of outputs. The following fields are contained. - - loss (torch.Tensor): A tensor for back propagation, which \ - can be a weighted sum of multiple losses. - - log_vars (dict): Dict contains all the variables to be sent \ - to the logger. - - num_samples (int): Indicates the batch size (when the model \ - is DDP, it means the batch size on each GPU), which is \ - used for averaging the logs. - """ - losses = self.model(**data) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, log_vars=log_vars, num_samples=len(data['img'].data)) - - return outputs - - def _parse_losses(self, losses): - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(loss.mean() for loss in loss_value) - elif isinstance(loss_value, dict): - for name, value in loss_value.items(): - log_vars[name] = value - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(value for key, value in log_vars.items() if 'loss' in key) - log_vars['loss'] = loss - - return loss, log_vars - - def forward_eval(self, kwargs): - img = kwargs.pop('img') - img_metas = kwargs.pop('img_metas', None) - return_loss = kwargs.pop('return_loss') - assert not return_loss - # TODO Temporarily hard-code to close post_process, - # otherwise, in the third trace(_check_trace), - # post_process will convert output tensor to numpy array automatically, - # resulting in _check_trace failure - outputs = self.model( - img, - img_metas=img_metas, - return_loss=return_loss, - post_process=False) - return outputs - - -class MMPoplarExecutor(PoplarExecutor): - """An executor for inputs/outputs parsing, model compilation, data - alignment and IPU upload/download. - - Args: - model (:obj:`nn.Module`): The model to be compiled. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - training (bool): Model in training mode or eval mode. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - args (argument list): Arguments passed to the `__init__` - method of PoplarExecutor. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of PoplarExecutor. - """ - - def __init__(self, - model, - logger=None, - training=True, - modules_to_record=None, - *args, - **kwargs): - # self.model == self._user_model: input pytorch model - # self._model: wrapped model which is used to compile - # and update weights, these two models use same weights - # wrapped model only accept and output tuple, so - # HierarchicalDataManager will convert dictionary - # to tuple and convert them back - self.inputs_manager = HierarchicalDataManager(logger=logger) - self.outputs_manager = HierarchicalDataManager(logger=logger) - self.logger = logger - # the features calculated by CPU - self.inter_outputs_in_cpu = {} - # the features calculated by IPU - self.inter_outputs_in_ipu = {} - if modules_to_record is None: - # It is possible that the IPU implementation of some operators - # is inconsistent with the expected (CPU), here you can use - # this method to confirm whether there is a problem - self.compare_with_cpu = False - else: - self.compare_with_cpu = True - # move model.fp16_enabled to self.fp16_enabled, - # modify the position where the input is automatically casted to half - if getattr(model, 'fp16_enabled', False): - model.fp16_enabled = False - self.fp16_enabled = True - # make torch.jit.trace convert self._model - model = WrappedNet( - model, - self.inputs_manager, - self.outputs_manager, - self.inter_outputs_in_cpu, - modules_to_record=modules_to_record) - super().__init__(model, training=training, *args, **kwargs) - # overwrite self._args_parser in train_step or val_step - self._args_parser = None - if training: - assert self.training - else: - assert not self.training - - @property - def training(self): - # If trying to get the attribute(training) of self, - # since the class has no training attribute, - # it will automatically look for the training attribute of self.model. - # However, the real attribute we want to check is self._training, - # self.model.training and self._training are often inconsistent. - # It is not clear whether it is a Poptorch bug or a special design, - # temporarily use this function to fix the problem - return self._training # comes from self.model._training - - @auto_fp16(supported_types=(PoplarExecutor, )) - def run_model(self, data_dict): - # this function is used to parse input_dict - # and convert to output_dict - if self.isCompiled(): - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - else: - # get tensors out of data and put them in a tuple - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - # turn logger in data manager off after compilation - self.inputs_manager.quick() - self.outputs_manager.quick() - - # parser args in the first iter - if self._args_parser is None: - self._args_parser = DictArgsParser({'args': inputs_tuple}) - - # run or convert model - # the plain_outputs will be used in converting stage - plain_outputs = self(inputs_tuple) - - self.inputs_manager.clean_all_tensors() - - # put list of tensors back to the output dict - # according to the same order - self.outputs_manager.update_all_tensors(plain_outputs) - # get the real output dictionary from self.outputs_manager - output_dict = self.outputs_manager.hierarchical_data - - # split output_dict into inter_outputs_in_ipu - # and output of the torch model - torch_model_output = {} - for name in output_dict: - if name in self.inter_outputs_in_cpu: - self.inter_outputs_in_ipu[name] = output_dict[name] - else: - torch_model_output[name] = output_dict[name] - - if 'output of WrappedNet: single tensor' in output_dict: - assert len(torch_model_output) == 1 - assert isinstance( - torch_model_output['output of WrappedNet: single tensor'], - torch.Tensor) - torch_model_output = \ - torch_model_output['output of WrappedNet: single tensor'] - - return torch_model_output - - def train_step(self, data, optimizer=None, **kwargs): - # arguments from mmcls/models/classifiers/base.py: - # BaseClassifier.train_step - assert self.training - assert len(kwargs) == 0 # TODO, support later if necessary - - # TODO support datacontainer as input - # currently, auto_fp16 and HierarchicalDataManager take too much - # time on traversing datacontainer - data['img_metas'] = None - num_samples = len(data['img'].data) - - # TODO we will ignore optimizer because it will not be used in model, - # support later if necessary - data['optimizer'] = None - output_dict = self.run_model(data) - - # outputs contained loss, log_vars, num_samples, - # only loss(torch.tensor) has been updated - # remove all unchanged vars, left torch.tensor - neat_output_dict = {'loss': output_dict['loss']} - - # re-parse outputs, get back log_vars and num_samples - loss, log_vars = self.model._parse_losses(neat_output_dict) - final_output_dict = dict( - loss=loss, log_vars=log_vars, num_samples=num_samples) - return final_output_dict - - def eval_call(self, img, img_metas=None, return_loss=True, **kwargs): - # arguments from mmdet/models/detectors/base.py:BaseDetector.forward - # tmp usssage for eval mode - assert not self.training - assert len(kwargs) == 0 # TODO, support later if necessary - assert not return_loss - data = {'img': img, 'img_metas': img_metas, 'return_loss': return_loss} - - output_dict = self.run_model(data) - - return output_dict - - def detachFromDevice(self): - if self.isCompiled() and self._is_attached: - super().detachFromDevice() - - def attachToDevice(self): - if self.isCompiled() and not self._is_attached: - super().attachToDevice() - - -class TrainEvalModel: - """A class maintaining training MMPoplarExecutor and inference - MMPoplarExecutor. - - Args: - train_model (:obj:`nn.Module`): The training model to be compiled. - ``train_model`` can be None if only executing validation. - eval_model (:obj:`nn.Module`): The inference model to be compiled. - options (mmcv.Config, dict): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - train_model, - eval_model, - options, - optimizer, - modules_to_record=None, - logger=None): - if train_model is None: - self._train_executor = None - self.training = False - else: - self._train_executor = get_training_model( - train_model, - options=options['training'], - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - self.training = True - self._eval_executor = get_inference_model( - eval_model, options=options['inference'], logger=logger) - - @property - def executor(self): - if self.training: - return self._train_executor - else: - return self._eval_executor - - def train(self, mode: bool = True): - """Sets the module in training mode. - - This has any effect only on certain modules. See documentations of - particular modules for details of their behaviors in - training/evaluation mode, if they are affected, - e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - Args: - mode (bool): whether to set training mode (``True``) or evaluation - mode (``False``). Default: ``True``. - - Returns: - Module: self - """ - if not isinstance(mode, bool): - raise ValueError('training mode is expected to be boolean, ' - f'but got {type(mode)}') - if self._train_executor is None and mode: - raise RuntimeError( - 'The train_executor is not initialized.' - 'If you want to initialize train_executor,' - 'you need to input optimizer when converting pytorch model') - - if mode == self.training: - self.model.train(mode) - return self - else: - if self.isCompiled(): - # copy weights from IPU to cpu before off-load current session - self.copyWeightsToHost() - # detach the current session before change the mode, - # if is training mode and weights are updated, - # poptorch will copy weights from IPU to host - self.detachFromDevice() - - self.training = mode # session will changed with mode changing - self.model.train(mode) - - # after changing mode, attach the current new session, - # and this function will copy weights of model to device - self.attachToDevice() - return self - - def eval(self): - """Sets the module in evaluation mode. - - This has any effect only on certain modules. - See documentations of particular modules - for details of their behaviors in training/evaluation mode, - if they are affected, e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - This is equivalent with :meth:`self.train(False) - `. - - See :ref:`locally-disable-grad-doc` for a comparison between - `.eval()` and several similar mechanisms that may be confused with it. - - Returns: - Module: self - """ - return self.train(False) - - def compare_data_between_ipu_and_cpu(self, inter_outputs_in_cpu, - inter_outputs_in_ipu): - for key, val in inter_outputs_in_cpu.items(): - is_tensor = isinstance(val['fea_in'], torch.Tensor) - fea_in_cpu = val['fea_in'] - fea_in_cpu_list = [fea_in_cpu] if is_tensor else fea_in_cpu - fea_in_ipu = inter_outputs_in_ipu[key]['fea_in'] - fea_in_ipu_list = [fea_in_ipu] if is_tensor else fea_in_ipu - - is_tensor = isinstance(val['fea_out'], torch.Tensor) - fea_out_cpu = val['fea_out'] - fea_out_cpu_list = [fea_out_cpu] if is_tensor else fea_out_cpu - fea_out_ipu = inter_outputs_in_ipu[key]['fea_out'] - fea_out_ipu_list = [fea_out_ipu] if is_tensor else fea_out_ipu - - print('comparing layer:', key) - for idx, (featA, featB) in \ - enumerate(zip(fea_in_cpu_list, fea_in_ipu_list)): - print('fea_in, tensor ', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - for idx, (featA, featB) in \ - enumerate(zip(fea_out_cpu_list, fea_out_ipu_list)): - print('fea_out, tensor', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def train_step(self, data, optimizer=None, **kwargs): - assert self.training, 'not supported train_step on eval mode' - inter_outputs_in_cpu = {} - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu): - self.copyWeightsToHost() - # run in CPU mode - self._train_executor.model.train_step(data, optimizer, **kwargs) - inter_outputs_in_cpu = { - **(self._train_executor.inter_outputs_in_cpu) - } - # run in IPU mode - result = self._train_executor.train_step(data, optimizer, **kwargs) - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu - and len(inter_outputs_in_cpu) > 0): - self.compare_data_between_ipu_and_cpu( - inter_outputs_in_cpu, - self._train_executor.inter_outputs_in_ipu) - return result - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def __call__(self, *args, **kwargs): - if self.training: - raise NotImplementedError('use train_step rather than __call__') - else: - return self._eval_executor.eval_call(*args, **kwargs) - - def __getattr__(self, attr): - return getattr(self.executor, attr) - - -def get_training_model(model: nn.Module, - options: Optional[poptorch.Options] = None, - optimizer: Optional[torch.optim.Optimizer] = None, - logger=None, - modules_to_record=None) -> poptorch.PoplarExecutor: - """Create a PopTorch training model from a PyTorch model, running on IPU - hardware in training mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned training model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.train()`` on the original model, which - changes the ``training`` bool of the model instance, will not alter the - model returned by this function. You may need to call ``model.train()`` - on your model before you call this function for correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): The optimizers - to apply during training. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place - of ``model``. - """ - # Create a copy of the original model in case it needs to be wrapped - maybe_wrapped_model = copy.copy(model) - - return MMPoplarExecutor( - model=maybe_wrapped_model, - logger=logger, - options=options, - training=True, - optimizer=optimizer, - user_model=model, - modules_to_record=modules_to_record, - poptorch_version=__version__) - - -def get_inference_model(model: Union[nn.Module, poptorch.PoplarExecutor], - options: Optional[poptorch.Options] = None, - logger=None) -> poptorch.PoplarExecutor: - """Create a PopTorch inference model from a PyTorch model, running on IPU - hardware in inference mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned inference model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.eval()`` on the original model will not alter - the model returned by this function. You may need to call - ``model.eval()`` on your model before you call this function for - correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place of - ``model``. - """ - - return MMPoplarExecutor( - model=copy.copy(model), - logger=logger, - options=options, - training=False, - poptorch_version=__version__) - - -def ipu_model_wrapper(model, - options, - optimizer=None, - logger=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None): - """Convert torch model to IPU model. - - Args: - model (nn.Module): The target model to be converted. - options (dict[str, poptorch.Options]): IPU options, generated - by :func:`cfg2options`. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during training. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (dict): A dictionary contains train_split_edges and - train_ckpt_nodes, See details in :func:`model_sharding` and - :func:`recomputation_checkpoint` functions. - fp16_cfg (dict): Config for IPU fp16 training. Currently supports - configs: `loss_scale`, `velocity_accum_type` and `accum_type`. - See details in - https://docs.graphcore.ai/projects/poptorch-user-guide/en/latest/index.html - - Returns: - TrainEvalModel: IPU wrapped model. - """ - if ipu_model_cfg is None: - ipu_model_cfg = {} - training = model.training if optimizer is not None else False - # set mixed-precision - if fp16_cfg is not None: - from mmcv.runner import wrap_fp16_model - loss_scale = fp16_cfg['loss_scale'] - wrap_fp16_model(model) - model.half() - # TODO tmp ussage to set loss scaling for torch original optimizer - if optimizer is not None: - optimizer.loss_scaling = loss_scale - if fp16_cfg.get('velocity_accum_type', False): - if fp16_cfg['velocity_accum_type'] == 'half': - optimizer.velocity_accum_type = torch.half - else: - optimizer.velocity_accum_type = torch.float32 - if fp16_cfg.get('accum_type', False): - if fp16_cfg['accum_type'] == 'half': - optimizer.accum_type = torch.half - else: - optimizer.accum_type = torch.float32 - # TODO support feature alignment for fp16 - if modules_to_record is not None: - raise NotImplementedError( - 'Feature alignment for fp16 is not implemented') - - # set model partition - if optimizer is None: - train_model = None - else: - # split model into multi-IPUs if specified - train_model = model_sharding( - copy.copy(model).train(), - ipu_model_cfg.get('train_split_edges', [])) - - recomputation_checkpoint(train_model, - ipu_model_cfg.get('train_ckpt_nodes', [])) - - # TODO support feature alignment for gradient accumulation mode - gradient_accumulation = \ - getattr(options['training'].Training, 'gradient_accumulation', 1) - if gradient_accumulation > 1: - assert modules_to_record is None, \ - 'Feature alignment for grad-accumulation mode not implemented' - - # TODO support feature alignment for multi-replica mode - replication_factor = \ - getattr(options['training'], 'replication_factor', 1) - if replication_factor > 1: - assert modules_to_record is None, \ - 'Feature alignment for multi-replica mode not implemented' - - # TODO supports different model partitions between train and eval mode - assert len(ipu_model_cfg.get('eval_split_edges', [])) == 0,\ - 'Currently, BeginBlock can only be used once on the same model' - eval_model = copy.copy(model).eval() - - # wrap model for compilation - model = TrainEvalModel( - train_model, - eval_model, - options=options, - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - model.train(training) - return model diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/runner.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/runner.py deleted file mode 100644 index e2d492267..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/runner.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.runner import (HOOKS, RUNNERS, BaseRunner, EpochBasedRunner, - IterBasedRunner) -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import (IPUFp16OptimizerHook, wrap_lr_updater_hook, - wrap_optimizer_hook) - from .model_wrapper import ipu_model_wrapper - from .utils import build_from_cfg_with_wrapper, cfg2options - - -class IPUBaseRunner(BaseRunner): - """A base runner for IPU. - - This runner has some extra processes for IPU which are shown below: - - 1. Parse options for IPU - 2. wrap pytorch model for IPU - 3. Raise errors while encountering illegal usage - 4. Input IPU options and initialize dataloader if finding an instance - of IPUDataLoader - - Args: - model (:obj:`nn.Module`): The model to run. - options_cfg (mmcv.Config, dict): Options that will be used to compile - and run the model. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (mmcv.Config, dict): Config of model partition and - recomputing checkpoint - fp16_cfg (mmcv.Config): Config for fp16 training. - batch_processor (callable): A callable method that process a data - batch. Should be None for IPU runner - kwargs (Dict[str, Any], optional): Keyword arguments will be passed to - ``base_runner.BaseRunner``. - """ - - def __init__(self, - model, - options_cfg=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None, - batch_processor=None, - **kwargs): - assert hasattr(model, 'train_step') and batch_processor is None,\ - 'only support model with train_step' - - if options_cfg is None: - options_cfg = {} - # call BaseRunner.__init__() here - super().__init__(model, **kwargs) - - # process options of ipu - if IS_IPU_AVAILABLE: - self.options = cfg2options(options_cfg) - self.model = ipu_model_wrapper( - self.model, - self.options, - self.optimizer, - self.logger, - modules_to_record=modules_to_record, - ipu_model_cfg=ipu_model_cfg, - fp16_cfg=fp16_cfg) - else: - raise NotImplementedError('cpu mode on IPURunner is not supported') - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - assert isinstance(lr_config, dict) - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, - # e.g., 'cyclic', then its first letter will be capitalized, - # e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, the string will not be changed - # if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = build_from_cfg_with_wrapper(lr_config, HOOKS, - wrap_lr_updater_hook) - self.register_hook(hook, priority='VERY_HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - assert isinstance(optimizer_config, (dict, IPUFp16OptimizerHook)) - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = build_from_cfg_with_wrapper(optimizer_config, HOOKS, - wrap_optimizer_hook) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def run(self, data_loaders, workflow, *args, **kwargs): - for i, flow in enumerate(workflow): - mode, _ = flow - # initialize IPU dataloader if not initialized - assert isinstance(data_loaders[i], IPUDataLoader),\ - 'IPU runner can only work with `IPUDataLoader`' - data_loaders[i].init(options=self.get_options(mode)) - - super().run(data_loaders, workflow, *args, **kwargs) - - def get_options(self, mode): - if mode == 'train': - return self.options['training'] - elif mode == 'val': - return self.options['inference'] - else: - raise ValueError(f'mode should be train or val but got {mode}') - - -@RUNNERS.register_module() -class IPUEpochBasedRunner(IPUBaseRunner, EpochBasedRunner): - """Epoch-based Runner for IPU. - - The Inheritance order(MRO) is: IPUEpochBasedRunner -> IPUBaseRunner -> - EpochBasedRunner -> BaseRunner This runner train models epoch by epoch. - """ - pass - - -@RUNNERS.register_module() -class IPUIterBasedRunner(IPUBaseRunner, IterBasedRunner): - """Iteration-based Runner for IPU. - - The Inheritance order(MRO) is: IPUIterBasedRunner -> IPUBaseRunner -> - IterBasedRunner -> BaseRunner This runner train models iteration by - iteration. - """ - pass diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/utils.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/utils.py deleted file mode 100644 index 79709db1e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/ipu/utils.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import numpy as np -import popart -import poptorch -import torch -import torch.nn as nn - -from mmcv.utils import Registry - - -def _options_assigner(cfg, options_node): - # set popart.options by config - # cfg: dict, python data type - # options_node: python module or function - if isinstance(cfg, dict): - for key in cfg: - _options_assigner(cfg[key], getattr(options_node, key)) - elif isinstance(cfg, (int, float, str, list)): - if callable(options_node): - options_node(cfg) - else: - error_msg = f'options_node type {type(options_node)} not supported' - raise NotImplementedError(error_msg) - else: - error_msg = f'cfg type {type(cfg)} not supported' - raise NotImplementedError(error_msg) - - -def cfg2options(cfg): - """Parse dictionary to ipu options. - - Args: - cfg (dict): A dictionary of ipu settings. - - Returns: - dict[str, poptorch.Options]: Training options and inference options - of IPU. - """ - # set ipu options for inference and training by config - train_cfg = cfg.pop('train_cfg', {}) - eval_cfg = cfg.pop('eval_cfg', {}) - eval_cfg['replicationFactor'] = 1 # eval mode only use one replica - eval_cfg['executionStrategy'] = 'ShardedExecution' - # overwrite default ipu cfg with specified train cfgs - training_ipu_cfg = {**cfg, **train_cfg} - # overwrite default ipu cfg with specified eval cfgs - inference_ipu_cfg = {**cfg, **eval_cfg} - - ipu_options = { - 'training': _cast_to_options(training_ipu_cfg), - 'inference': _cast_to_options(inference_ipu_cfg) - } - - # TODO configure these codes - ipu_options['training']._Popart.set('disableGradAccumulationTensorStreams', - True) - ipu_options['training']._Popart.set( - 'accumulateOuterFragmentSettings.schedule', - int(popart.AccumulateOuterFragmentSchedule.OverlapMemoryOptimized)) - ipu_options['training'].Precision.enableStochasticRounding(True) - - return ipu_options - - -def _cast_to_options(cfg): - # If it cannot be directly assigned, use if statement to parse it, - # and if it can be directly assigned, use _options_assigner to assign - options = poptorch.Options() - - if 'availableMemoryProportion' in cfg: - available_memory_proportion = cfg.pop('availableMemoryProportion') - mem_props = {} - for i, mem_prop in enumerate(available_memory_proportion): - mem_props[f'IPU{i}'] = mem_prop - options.setAvailableMemoryProportion(mem_props) - - if 'executionStrategy' in cfg: - execution_strategy = cfg.pop('executionStrategy') - if execution_strategy == 'SameAsIpu': - options.setExecutionStrategy( - poptorch.PipelinedExecution( - getattr(poptorch.AutoStage, execution_strategy))) - elif execution_strategy == 'ShardedExecution': - options.setExecutionStrategy(poptorch.ShardedExecution()) - else: - raise NotImplementedError( - 'executionStrategy should be "SameAsIpu" or "ShardedExecution"' - f', but got {execution_strategy}') - - if 'partialsType' in cfg: - partials_type = cfg.pop('partialsType') - options.Precision.setPartialsType(getattr( - torch, partials_type)) # half or float - - _options_assigner(cfg, options) - return options - - -def model_sharding(model, split_edges): - """split models in-place into multi-IPUs. - - Args: - model (nn.Module): The target model to be split. - split_edges (list of dict): Model layer names or layer numbers - of split edge. Each item of ``split_edges`` is a dictionary, - which may contain the following key-pairs: - - - layer_to_call: PyTorch module to assign to the block - - user_id (optional): A user defined identifier for the block. - - ipu_id: The id of the IPU to run on. - - Examples: - >>> split_edges = [ - ... dict(layer_to_call='model.conv1', ipu_id=0), - ... dict(layer_to_call='model.conv3', ipu_id=1)] - >>> sharding_model = model_sharding(torch_model, split_edges) - - Returns: - nn.Module: Split model. - """ - if len(split_edges) == 0: - return model - assert isinstance(split_edges, list) - spilt_edges_dict = {edge['layer_to_call']: edge for edge in split_edges} - - for idx, (name, module) in enumerate(model.named_modules()): - if idx in spilt_edges_dict and name in spilt_edges_dict: - raise ValueError( - 'The same layer is referenced twice while doing model' - f' partition: idx is {idx} and name is {name}') - - edge = spilt_edges_dict.pop(name, None) - edge = spilt_edges_dict.pop(idx, edge) - if edge is not None: - poptorch.BeginBlock(module, edge.get('user_id', name), - edge['ipu_id']) - - # ensure all split_edges are used - if len(spilt_edges_dict) > 0: - split_edge_names = list(spilt_edges_dict.keys()) - raise RuntimeError( - f'split_edges: {split_edge_names} are not contained in the model') - return model - - -def recomputation_checkpoint(model: nn.Module, module_names: list): - """Annotates the output of a module to be checkpointed instead of - recomputed. - - If recomputation mode is enabled, ipu will release the activations of - the middle layers to save memory. During the backward of gradient, - the activation of the middle layer will be recalculated again. - This function is used to declare the activations of some intermediate - layers that need to be saved in order to skip the recomputation of - some layers. - - Args: - model (nn.Module): The target model to apply recomputation - checkpoint. - module_names (list): Layer names of module. - """ - - def recompute_outputs(module, inputs, outputs): - if isinstance(outputs, tuple): - return tuple(poptorch.recomputationCheckpoint(y) for y in outputs) - else: - return poptorch.recomputationCheckpoint(outputs) - - for name, module in model.named_modules(): - if name in module_names: - module.register_forward_hook(recompute_outputs) - module_names.remove(name) - - # check all module_names are used - assert len(module_names) == 0,\ - f'recomputed nodes: {module_names} are not contained in the model' - - -def compare_ndarray(featA, featB, rtol=1e-3, atol=1e-5): - """Align data between two activations or weights.""" - try: - np.testing.assert_allclose(featA, featB, rtol=rtol, atol=atol) - except AssertionError as e: - print(e) - - -def build_from_cfg_with_wrapper(cfg, - registry, - wrapper_func=None, - default_args=None): - """Build a module from config dict and wrap module with "wrapper_func". - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - wrapper_func (function): Used to wrap class - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - - if wrapper_func is None: - wrapped_obj_cls = obj_cls - else: - wrapped_obj_cls = wrapper_func(obj_cls) - try: - return wrapped_obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{wrapped_obj_cls.__name__}: {e}') diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/__init__.py deleted file mode 100644 index 572c4da7e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .data_parallel import MLUDataParallel -from .distributed import MLUDistributedDataParallel -from .scatter_gather import scatter, scatter_kwargs - -__all__ = [ - 'MLUDataParallel', 'MLUDistributedDataParallel', 'scatter', - 'scatter_kwargs' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/_functions.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/_functions.py deleted file mode 100644 index 7c35e65a2..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/_functions.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def scatter(input, devices): - """scatter copies tensor to MLU directly.""" - if isinstance(input, list): - outputs = [scatter(_input, devices) for _input in input] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - return output.to('mlu') if devices != [-1] else output - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_mlus, input): - outputs = scatter(input, target_mlus) - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/data_parallel.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/data_parallel.py deleted file mode 100644 index b2d09d0b0..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/data_parallel.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -import torch - -from mmcv.parallel import MMDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDataParallel(MMDataParallel): - """The MLUDataParallel module that supports DataContainer. - - MLUDataParallel is a class inherited from MMDataParall, which supports - MLU training and inference only. - - The main differences with MMDataParallel: - - - It only supports single-card of MLU, and only use first card to - run training and inference. - - - It uses direct host-to-device copy instead of stream-background - scatter. - - .. warning:: - MLUDataParallel only supports single MLU training, if you need to - train with multiple MLUs, please use MLUDistributedDataParallel - instead. If you have multiple MLUs, you can set the environment - variable ``MLU_VISIBLE_DEVICES=0`` (or any other card number(s)) - to specify the running device. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MLUDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.device_ids = [0] - self.src_device_obj = torch.device('mlu:0') - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/distributed.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/distributed.py deleted file mode 100644 index 3768c754c..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/distributed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.parallel import MMDistributedDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDistributedDataParallel(MMDistributedDataParallel): - """The DDP module supports DataContainer. - - MLUDDP has one difference from MMDDP which moves data to MLU with coping - instead of scattering. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/scatter_gather.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/scatter_gather.py deleted file mode 100644 index 0b0c9b96f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/device/mlu/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmcv.parallel.data_container import DataContainer -from ._functions import Scatter - - -def scatter(inputs, target_mlus, dim=0): - """Scatter inputs to target mlu. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_mlus != [-1]: - obj = obj.to('mlu') - return [obj] - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_mlus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_mlus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_mlus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_mlus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_mlus, dim) if inputs else [] - kwargs = scatter(kwargs, target_mlus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/engine/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/engine/__init__.py deleted file mode 100644 index 3193b7f66..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, - single_gpu_test) - -__all__ = [ - 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', - 'single_gpu_test' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/engine/test.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/engine/test.py deleted file mode 100644 index f236b1cda..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/engine/test.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, data_loader): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - # Assume result has the same length of batch_size - # refer to https://github.com/open-mmlab/mmcv/issues/985 - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting - ``gpu_collect=True``, it encodes results to gpu tensors and use gpu - communication for results collection. On cpu mode it saves the results on - different gpus to ``tmpdir`` and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - if rank == 0: - batch_size = len(result) - batch_size_all = batch_size * world_size - if batch_size_all + prog_bar.completed > len(dataset): - batch_size_all = len(dataset) - prog_bar.completed - for _ in range(batch_size_all): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results under cpu mode. - - On cpu mode, this function will save the results on different gpus to - ``tmpdir`` and collect them by the rank 0 worker. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - tmpdir (str | None): temporal directory for collected results to - store. If set to None, it will create a random temporal directory - for it. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_result = mmcv.load(part_file) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results under gpu mode. - - On gpu mode, this function will encode results to gpu tensors and use gpu - communication for results collection. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/__init__.py deleted file mode 100644 index 2051b85f7..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .file_client import BaseStorageBackend, FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler -from .io import dump, load, register_handler -from .parse import dict_from_file, list_from_file - -__all__ = [ - 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', - 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', - 'list_from_file', 'dict_from_file' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/file_client.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/file_client.py deleted file mode 100644 index e7fd7cdfa..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/file_client.py +++ /dev/null @@ -1,1163 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import os -import os.path as osp -import re -import tempfile -import warnings -from abc import ABCMeta, abstractmethod -from contextlib import contextmanager -from pathlib import Path -from typing import Iterable, Iterator, Optional, Tuple, Union -from urllib.request import urlopen - -import mmcv -from mmcv.utils.misc import has_method -from mmcv.utils.path import is_filepath - - -class BaseStorageBackend(metaclass=ABCMeta): - """Abstract class of storage backends. - - All backends need to implement two apis: ``get()`` and ``get_text()``. - ``get()`` reads the file as a byte stream and ``get_text()`` reads the file - as texts. - """ - - # a flag to indicate whether the backend can create a symlink for a file - _allow_symlink = False - - @property - def name(self): - return self.__class__.__name__ - - @property - def allow_symlink(self): - return self._allow_symlink - - @abstractmethod - def get(self, filepath): - pass - - @abstractmethod - def get_text(self, filepath): - pass - - -class CephBackend(BaseStorageBackend): - """Ceph storage backend (for internal use). - - Args: - path_mapping (dict|None): path mapping dict from local path to Petrel - path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` - will be replaced by ``dst``. Default: None. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - """ - - def __init__(self, path_mapping=None): - try: - import ceph - except ImportError: - raise ImportError('Please install ceph to enable CephBackend.') - - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - self._client = ceph.S3Client() - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def get(self, filepath): - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class PetrelBackend(BaseStorageBackend): - """Petrel storage backend (for internal use). - - PetrelBackend supports reading and writing data to multiple clusters. - If the file path contains the cluster name, PetrelBackend will read data - from specified cluster or write data to it. Otherwise, PetrelBackend will - access the default cluster. - - Args: - path_mapping (dict, optional): Path mapping dict from local path to - Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in - ``filepath`` will be replaced by ``dst``. Default: None. - enable_mc (bool, optional): Whether to enable memcached support. - Default: True. - - Examples: - >>> filepath1 = 's3://path/of/file' - >>> filepath2 = 'cluster-name:s3://path/of/file' - >>> client = PetrelBackend() - >>> client.get(filepath1) # get data from default cluster - >>> client.get(filepath2) # get data from 'cluster-name' cluster - """ - - def __init__(self, - path_mapping: Optional[dict] = None, - enable_mc: bool = True): - try: - from petrel_client import client - except ImportError: - raise ImportError('Please install petrel_client to enable ' - 'PetrelBackend.') - - self._client = client.Client(enable_mc=enable_mc) - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def _map_path(self, filepath: Union[str, Path]) -> str: - """Map ``filepath`` to a string path whose prefix will be replaced by - :attr:`self.path_mapping`. - - Args: - filepath (str): Path to be mapped. - """ - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - return filepath - - def _format_path(self, filepath: str) -> str: - """Convert a ``filepath`` to standard format of petrel oss. - - If the ``filepath`` is concatenated by ``os.path.join``, in a Windows - environment, the ``filepath`` will be the format of - 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the - above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. - - Args: - filepath (str): Path to be formatted. - """ - return re.sub(r'\\+', '/', filepath) - - def get(self, filepath: Union[str, Path]) -> memoryview: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - memoryview: A memory view of expected bytes object to avoid - copying. The memoryview object can be converted to bytes by - ``value_buf.tobytes()``. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return str(self.get(filepath), encoding=encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Save data to a given ``filepath``. - - Args: - obj (bytes): Data to be saved. - filepath (str or Path): Path to write data. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.put(filepath, obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Save data to a given ``filepath``. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to encode the ``obj``. - Default: 'utf-8'. - """ - self.put(bytes(obj, encoding=encoding), filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - if not has_method(self._client, 'delete'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `delete` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.delete(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - if not (has_method(self._client, 'contains') - and has_method(self._client, 'isdir')): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` and `isdir` methods, please use a higher' - 'version or dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) or self._client.isdir(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - if not has_method(self._client, 'isdir'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `isdir` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - if not has_method(self._client, 'contains'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` method, please use a higher version or ' - 'dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result after concatenation. - """ - filepath = self._format_path(self._map_path(filepath)) - if filepath.endswith('/'): - filepath = filepath[:-1] - formatted_paths = [filepath] - for path in filepaths: - formatted_paths.append(self._format_path(self._map_path(path))) - return '/'.join(formatted_paths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download a file from ``filepath`` and return a temporary path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str | Path): Download a file from ``filepath``. - - Examples: - >>> client = PetrelBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('s3://path/of/your/file') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one temporary path. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - assert self.isfile(filepath) - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - Petrel has no concept of directories but it simulates the directory - hierarchy in the filesystem through public prefixes. In addition, - if the returned path ends with '/', it means the path is a public - prefix which is a logical directory. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - In addition, the returned path of directory will not contains the - suffix '/' which is consistent with other backends. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if not has_method(self._client, 'list'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `list` method, please use a higher version or dev' - ' branch instead.')) - - dir_path = self._map_path(dir_path) - dir_path = self._format_path(dir_path) - if list_dir and suffix is not None: - raise TypeError( - '`list_dir` should be False when `suffix` is not None') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - # Petrel's simulated directory hierarchy assumes that directory paths - # should end with `/` - if not dir_path.endswith('/'): - dir_path += '/' - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for path in self._client.list(dir_path): - # the `self.isdir` is not used here to determine whether path - # is a directory, because `self.isdir` relies on - # `self._client.list` - if path.endswith('/'): # a directory path - next_dir_path = self.join_path(dir_path, path) - if list_dir: - # get the relative path and exclude the last - # character '/' - rel_dir = next_dir_path[len(root):-1] - yield rel_dir - if recursive: - yield from _list_dir_or_file(next_dir_path, list_dir, - list_file, suffix, - recursive) - else: # a file path - absolute_path = self.join_path(dir_path, path) - rel_path = absolute_path[len(root):] - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class MemcachedBackend(BaseStorageBackend): - """Memcached storage backend. - - Attributes: - server_list_cfg (str): Config file for memcached server list. - client_cfg (str): Config file for memcached client. - sys_path (str | None): Additional path to be appended to `sys.path`. - Default: None. - """ - - def __init__(self, server_list_cfg, client_cfg, sys_path=None): - if sys_path is not None: - import sys - sys.path.append(sys_path) - try: - import mc - except ImportError: - raise ImportError( - 'Please install memcached to enable MemcachedBackend.') - - self.server_list_cfg = server_list_cfg - self.client_cfg = client_cfg - self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, - self.client_cfg) - # mc.pyvector servers as a point which points to a memory cache - self._mc_buffer = mc.pyvector() - - def get(self, filepath): - filepath = str(filepath) - import mc - self._client.Get(filepath, self._mc_buffer) - value_buf = mc.ConvertBuffer(self._mc_buffer) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class LmdbBackend(BaseStorageBackend): - """Lmdb storage backend. - - Args: - db_path (str): Lmdb database path. - readonly (bool, optional): Lmdb environment parameter. If True, - disallow any write operations. Default: True. - lock (bool, optional): Lmdb environment parameter. If False, when - concurrent access occurs, do not lock the database. Default: False. - readahead (bool, optional): Lmdb environment parameter. If False, - disable the OS filesystem readahead mechanism, which may improve - random read performance when a database is larger than RAM. - Default: False. - - Attributes: - db_path (str): Lmdb database path. - """ - - def __init__(self, - db_path, - readonly=True, - lock=False, - readahead=False, - **kwargs): - try: - import lmdb # NOQA - except ImportError: - raise ImportError('Please install lmdb to enable LmdbBackend.') - - self.db_path = str(db_path) - self.readonly = readonly - self.lock = lock - self.readahead = readahead - self.kwargs = kwargs - self._client = None - - def get(self, filepath): - """Get values according to the filepath. - - Args: - filepath (str | obj:`Path`): Here, filepath is the lmdb key. - """ - if self._client is None: - self._client = self._get_client() - - with self._client.begin(write=False) as txn: - value_buf = txn.get(str(filepath).encode('utf-8')) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - def _get_client(self): - import lmdb - - return lmdb.open( - self.db_path, - readonly=self.readonly, - lock=self.lock, - readahead=self.readahead, - **self.kwargs) - - def __del__(self): - self._client.close() - - -class HardDiskBackend(BaseStorageBackend): - """Raw hard disks storage backend.""" - - _allow_symlink = True - - def get(self, filepath: Union[str, Path]) -> bytes: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes: Expected bytes object. - """ - with open(filepath, 'rb') as f: - value_buf = f.read() - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - with open(filepath, 'r', encoding=encoding) as f: - value_buf = f.read() - return value_buf - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` will create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'wb') as f: - f.write(obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` will create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'w', encoding=encoding) as f: - f.write(obj) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - os.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return osp.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return osp.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return osp.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return osp.join(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, filepath: Union[str, Path]) -> Iterable[Union[str, Path]]: - """Only for unified API and do nothing.""" - yield filepath - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if list_dir and suffix is not None: - raise TypeError('`suffix` should be None when `list_dir` is True') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - elif osp.isdir(entry.path): - if list_dir: - rel_dir = osp.relpath(entry.path, root) - yield rel_dir - if recursive: - yield from _list_dir_or_file(entry.path, list_dir, - list_file, suffix, - recursive) - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class HTTPBackend(BaseStorageBackend): - """HTTP and HTTPS storage bachend.""" - - def get(self, filepath): - value_buf = urlopen(filepath).read() - return value_buf - - def get_text(self, filepath, encoding='utf-8'): - value_buf = urlopen(filepath).read() - return value_buf.decode(encoding) - - @contextmanager - def get_local_path(self, filepath: str) -> Iterable[str]: - """Download a file from ``filepath``. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str): Download a file from ``filepath``. - - Examples: - >>> client = HTTPBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('http://path/of/your/file') as path: - ... # do something here - """ - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - -class FileClient: - """A general file client to access files in different backends. - - The client loads a file or text in a specified backend from its path - and returns it as a binary or text file. There are two ways to choose a - backend, the name of backend and the prefix of path. Although both of them - can be used to choose a storage backend, ``backend`` has a higher priority - that is if they are all set, the storage backend will be chosen by the - backend argument. If they are all `None`, the disk backend will be chosen. - Note that It can also register other backend accessor with a given name, - prefixes, and backend class. In addition, We use the singleton pattern to - avoid repeated object creation. If the arguments are the same, the same - object will be returned. - - Args: - backend (str, optional): The storage backend type. Options are "disk", - "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. - prefix (str, optional): The prefix of the registered storage backend. - Options are "s3", "http", "https". Default: None. - - Examples: - >>> # only set backend - >>> file_client = FileClient(backend='petrel') - >>> # only set prefix - >>> file_client = FileClient(prefix='s3') - >>> # set both backend and prefix but use backend to choose client - >>> file_client = FileClient(backend='petrel', prefix='s3') - >>> # if the arguments are the same, the same object is returned - >>> file_client1 = FileClient(backend='petrel') - >>> file_client1 is file_client - True - - Attributes: - client (:obj:`BaseStorageBackend`): The backend object. - """ - - _backends = { - 'disk': HardDiskBackend, - 'ceph': CephBackend, - 'memcached': MemcachedBackend, - 'lmdb': LmdbBackend, - 'petrel': PetrelBackend, - 'http': HTTPBackend, - } - # This collection is used to record the overridden backends, and when a - # backend appears in the collection, the singleton pattern is disabled for - # that backend, because if the singleton pattern is used, then the object - # returned will be the backend before overwriting - _overridden_backends = set() - _prefix_to_backends = { - 's3': PetrelBackend, - 'http': HTTPBackend, - 'https': HTTPBackend, - } - _overridden_prefixes = set() - - _instances = {} - - def __new__(cls, backend=None, prefix=None, **kwargs): - if backend is None and prefix is None: - backend = 'disk' - if backend is not None and backend not in cls._backends: - raise ValueError( - f'Backend {backend} is not supported. Currently supported ones' - f' are {list(cls._backends.keys())}') - if prefix is not None and prefix not in cls._prefix_to_backends: - raise ValueError( - f'prefix {prefix} is not supported. Currently supported ones ' - f'are {list(cls._prefix_to_backends.keys())}') - - # concatenate the arguments to a unique key for determining whether - # objects with the same arguments were created - arg_key = f'{backend}:{prefix}' - for key, value in kwargs.items(): - arg_key += f':{key}:{value}' - - # if a backend was overridden, it will create a new object - if (arg_key in cls._instances - and backend not in cls._overridden_backends - and prefix not in cls._overridden_prefixes): - _instance = cls._instances[arg_key] - else: - # create a new object and put it to _instance - _instance = super().__new__(cls) - if backend is not None: - _instance.client = cls._backends[backend](**kwargs) - else: - _instance.client = cls._prefix_to_backends[prefix](**kwargs) - - cls._instances[arg_key] = _instance - - return _instance - - @property - def name(self): - return self.client.name - - @property - def allow_symlink(self): - return self.client.allow_symlink - - @staticmethod - def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: - """Parse the prefix of a uri. - - Args: - uri (str | Path): Uri to be parsed that contains the file prefix. - - Examples: - >>> FileClient.parse_uri_prefix('s3://path/of/your/file') - 's3' - - Returns: - str | None: Return the prefix of uri if the uri contains '://' else - ``None``. - """ - assert is_filepath(uri) - uri = str(uri) - if '://' not in uri: - return None - else: - prefix, _ = uri.split('://') - # In the case of PetrelBackend, the prefix may contains the cluster - # name like clusterName:s3 - if ':' in prefix: - _, prefix = prefix.split(':') - return prefix - - @classmethod - def infer_client(cls, - file_client_args: Optional[dict] = None, - uri: Optional[Union[str, Path]] = None) -> 'FileClient': - """Infer a suitable file client based on the URI and arguments. - - Args: - file_client_args (dict, optional): Arguments to instantiate a - FileClient. Default: None. - uri (str | Path, optional): Uri to be parsed that contains the file - prefix. Default: None. - - Examples: - >>> uri = 's3://path/of/your/file' - >>> file_client = FileClient.infer_client(uri=uri) - >>> file_client_args = {'backend': 'petrel'} - >>> file_client = FileClient.infer_client(file_client_args) - - Returns: - FileClient: Instantiated FileClient object. - """ - assert file_client_args is not None or uri is not None - if file_client_args is None: - file_prefix = cls.parse_uri_prefix(uri) # type: ignore - return cls(prefix=file_prefix) - else: - return cls(**file_client_args) - - @classmethod - def _register_backend(cls, name, backend, force=False, prefixes=None): - if not isinstance(name, str): - raise TypeError('the backend name should be a string, ' - f'but got {type(name)}') - if not inspect.isclass(backend): - raise TypeError( - f'backend should be a class but got {type(backend)}') - if not issubclass(backend, BaseStorageBackend): - raise TypeError( - f'backend {backend} is not a subclass of BaseStorageBackend') - if not force and name in cls._backends: - raise KeyError( - f'{name} is already registered as a storage backend, ' - 'add "force=True" if you want to override it') - - if name in cls._backends and force: - cls._overridden_backends.add(name) - cls._backends[name] = backend - - if prefixes is not None: - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if prefix not in cls._prefix_to_backends: - cls._prefix_to_backends[prefix] = backend - elif (prefix in cls._prefix_to_backends) and force: - cls._overridden_prefixes.add(prefix) - cls._prefix_to_backends[prefix] = backend - else: - raise KeyError( - f'{prefix} is already registered as a storage backend,' - ' add "force=True" if you want to override it') - - @classmethod - def register_backend(cls, name, backend=None, force=False, prefixes=None): - """Register a backend to FileClient. - - This method can be used as a normal class method or a decorator. - - .. code-block:: python - - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - FileClient.register_backend('new', NewBackend) - - or - - .. code-block:: python - - @FileClient.register_backend('new') - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - Args: - name (str): The name of the registered backend. - backend (class, optional): The backend class to be registered, - which must be a subclass of :class:`BaseStorageBackend`. - When this method is used as a decorator, backend is None. - Defaults to None. - force (bool, optional): Whether to override the backend if the name - has already been registered. Defaults to False. - prefixes (str or list[str] or tuple[str], optional): The prefixes - of the registered storage backend. Default: None. - `New in version 1.3.15.` - """ - if backend is not None: - cls._register_backend( - name, backend, force=force, prefixes=prefixes) - return - - def _register(backend_cls): - cls._register_backend( - name, backend_cls, force=force, prefixes=prefixes) - return backend_cls - - return _register - - def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: - """Read data from a given ``filepath`` with 'rb' mode. - - Note: - There are two types of return values for ``get``, one is ``bytes`` - and the other is ``memoryview``. The advantage of using memoryview - is that you can avoid copying, and if you want to convert it to - ``bytes``, you can use ``.tobytes()``. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes | memoryview: Expected bytes object or a memory view of the - bytes object. - """ - return self.client.get(filepath) - - def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return self.client.get_text(filepath, encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` should create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - self.client.put(obj, filepath) - - def put_text(self, obj: str, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` should create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str, optional): The encoding format used to open the - `filepath`. Default: 'utf-8'. - """ - self.client.put_text(obj, filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str, Path): Path to be removed. - """ - self.client.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return self.client.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return self.client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return self.client.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return self.client.join_path(filepath, *filepaths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download data from ``filepath`` and write the data to local path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Note: - If the ``filepath`` is a local path, just return itself. - - .. warning:: - ``get_local_path`` is an experimental interface that may change in - the future. - - Args: - filepath (str or Path): Path to be read data. - - Examples: - >>> file_client = FileClient(prefix='s3') - >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one path. - """ - with self.client.get_local_path(str(filepath)) as local_path: - yield local_path - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, - suffix, recursive) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/__init__.py deleted file mode 100644 index aa24d9197..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseFileHandler -from .json_handler import JsonHandler -from .pickle_handler import PickleHandler -from .yaml_handler import YamlHandler - -__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/base.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/base.py deleted file mode 100644 index 288878bc5..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseFileHandler(metaclass=ABCMeta): - # `str_like` is a flag to indicate whether the type of file object is - # str-like object or bytes-like object. Pickle only processes bytes-like - # objects but json only processes str-like object. If it is str-like - # object, `StringIO` will be used to process the buffer. - str_like = True - - @abstractmethod - def load_from_fileobj(self, file, **kwargs): - pass - - @abstractmethod - def dump_to_fileobj(self, obj, file, **kwargs): - pass - - @abstractmethod - def dump_to_str(self, obj, **kwargs): - pass - - def load_from_path(self, filepath, mode='r', **kwargs): - with open(filepath, mode) as f: - return self.load_from_fileobj(f, **kwargs) - - def dump_to_path(self, obj, filepath, mode='w', **kwargs): - with open(filepath, mode) as f: - self.dump_to_fileobj(obj, f, **kwargs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/json_handler.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/json_handler.py deleted file mode 100644 index 18d4f15f7..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/json_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -import numpy as np - -from .base import BaseFileHandler - - -def set_default(obj): - """Set default json values for non-serializable values. - - It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. - It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, - etc.) into plain numbers of plain python built-in types. - """ - if isinstance(obj, (set, range)): - return list(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, np.generic): - return obj.item() - raise TypeError(f'{type(obj)} is unsupported for json dump') - - -class JsonHandler(BaseFileHandler): - - def load_from_fileobj(self, file): - return json.load(file) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('default', set_default) - json.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('default', set_default) - return json.dumps(obj, **kwargs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/pickle_handler.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/pickle_handler.py deleted file mode 100644 index b37c79bed..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/pickle_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import pickle - -from .base import BaseFileHandler - - -class PickleHandler(BaseFileHandler): - - str_like = False - - def load_from_fileobj(self, file, **kwargs): - return pickle.load(file, **kwargs) - - def load_from_path(self, filepath, **kwargs): - return super(PickleHandler, self).load_from_path( - filepath, mode='rb', **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('protocol', 2) - return pickle.dumps(obj, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('protocol', 2) - pickle.dump(obj, file, **kwargs) - - def dump_to_path(self, obj, filepath, **kwargs): - super(PickleHandler, self).dump_to_path( - obj, filepath, mode='wb', **kwargs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/yaml_handler.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/yaml_handler.py deleted file mode 100644 index 60911e7e6..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/handlers/yaml_handler.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import yaml - -try: - from yaml import CDumper as Dumper - from yaml import CLoader as Loader -except ImportError: - from yaml import Loader, Dumper - -from .base import BaseFileHandler # isort:skip - - -class YamlHandler(BaseFileHandler): - - def load_from_fileobj(self, file, **kwargs): - kwargs.setdefault('Loader', Loader) - return yaml.load(file, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('Dumper', Dumper) - yaml.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('Dumper', Dumper) - return yaml.dump(obj, **kwargs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/io.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/io.py deleted file mode 100644 index aaefde58a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/io.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from io import BytesIO, StringIO -from pathlib import Path - -from ..utils import is_list_of, is_str -from .file_client import FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler - -file_handlers = { - 'json': JsonHandler(), - 'yaml': YamlHandler(), - 'yml': YamlHandler(), - 'pickle': PickleHandler(), - 'pkl': PickleHandler() -} - - -def load(file, file_format=None, file_client_args=None, **kwargs): - """Load data from json/yaml/pickle files. - - This method provides a unified api for loading data from serialized files. - - Note: - In v1.3.16 and later, ``load`` supports loading data from serialized - files those can be storaged in different backends. - - Args: - file (str or :obj:`Path` or file-like object): Filename or a file-like - object. - file_format (str, optional): If not specified, the file format will be - inferred from the file extension, otherwise use the specified one. - Currently supported formats include "json", "yaml/yml" and - "pickle/pkl". - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> load('/path/of/your/file') # file is storaged in disk - >>> load('https://path/of/your/file') # file is storaged in Internet - >>> load('s3://path/of/your/file') # file is storaged in petrel - - Returns: - The content from the file. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None and is_str(file): - file_format = file.split('.')[-1] - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO(file_client.get_text(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - else: - with BytesIO(file_client.get(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - elif hasattr(file, 'read'): - obj = handler.load_from_fileobj(file, **kwargs) - else: - raise TypeError('"file" must be a filepath str or a file-object') - return obj - - -def dump(obj, file=None, file_format=None, file_client_args=None, **kwargs): - """Dump data to json/yaml/pickle strings or files. - - This method provides a unified api for dumping data as strings or to files, - and also supports custom arguments for each file format. - - Note: - In v1.3.16 and later, ``dump`` supports dumping data as strings or to - files which is saved to different backends. - - Args: - obj (any): The python object to be dumped. - file (str or :obj:`Path` or file-like object, optional): If not - specified, then the object is dumped to a str, otherwise to a file - specified by the filename or file-like object. - file_format (str, optional): Same as :func:`load`. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dump('hello world', '/path/of/your/file') # disk - >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel - - Returns: - bool: True for success, False otherwise. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None: - if is_str(file): - file_format = file.split('.')[-1] - elif file is None: - raise ValueError( - 'file_format must be specified since file is None') - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if file is None: - return handler.dump_to_str(obj, **kwargs) - elif is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put_text(f.getvalue(), file) - else: - with BytesIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put(f.getvalue(), file) - elif hasattr(file, 'write'): - handler.dump_to_fileobj(obj, file, **kwargs) - else: - raise TypeError('"file" must be a filename str or a file-object') - - -def _register_handler(handler, file_formats): - """Register a handler for some file extensions. - - Args: - handler (:obj:`BaseFileHandler`): Handler to be registered. - file_formats (str or list[str]): File formats to be handled by this - handler. - """ - if not isinstance(handler, BaseFileHandler): - raise TypeError( - f'handler must be a child of BaseFileHandler, not {type(handler)}') - if isinstance(file_formats, str): - file_formats = [file_formats] - if not is_list_of(file_formats, str): - raise TypeError('file_formats must be a str or a list of str') - for ext in file_formats: - file_handlers[ext] = handler - - -def register_handler(file_formats, **kwargs): - - def wrap(cls): - _register_handler(cls(**kwargs), file_formats) - return cls - - return wrap diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/parse.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/parse.py deleted file mode 100644 index f60f0d611..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/fileio/parse.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from io import StringIO - -from .file_client import FileClient - - -def list_from_file(filename, - prefix='', - offset=0, - max_num=0, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a list of strings. - - Note: - In v1.3.16 and later, ``list_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a list for strings. - - Args: - filename (str): Filename. - prefix (str): The prefix to be inserted to the beginning of each item. - offset (int): The offset of lines. - max_num (int): The maximum number of lines to be read, - zeros and negatives mean no limitation. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> list_from_file('/path/of/your/file') # disk - ['hello', 'world'] - >>> list_from_file('s3://path/of/your/file') # ceph or petrel - ['hello', 'world'] - - Returns: - list[str]: A list of strings. - """ - cnt = 0 - item_list = [] - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for _ in range(offset): - f.readline() - for line in f: - if 0 < max_num <= cnt: - break - item_list.append(prefix + line.rstrip('\n\r')) - cnt += 1 - return item_list - - -def dict_from_file(filename, - key_type=str, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a dict. - - Each line of the text file will be two or more columns split by - whitespaces or tabs. The first column will be parsed as dict keys, and - the following columns will be parsed as dict values. - - Note: - In v1.3.16 and later, ``dict_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a dict. - - Args: - filename(str): Filename. - key_type(type): Type of the dict keys. str is user by default and - type conversion will be performed if specified. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dict_from_file('/path/of/your/file') # disk - {'key1': 'value1', 'key2': 'value2'} - >>> dict_from_file('s3://path/of/your/file') # ceph or petrel - {'key1': 'value1', 'key2': 'value2'} - - Returns: - dict: The parsed contents. - """ - mapping = {} - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for line in f: - items = line.rstrip('\n').split() - assert len(items) >= 2 - key = key_type(items[0]) - val = items[1:] if len(items) > 2 else items[1] - mapping[key] = val - return mapping diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/__init__.py deleted file mode 100644 index d0051d609..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, - gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, - rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) -from .geometric import (cutout, imcrop, imflip, imflip_, impad, - impad_to_multiple, imrescale, imresize, imresize_like, - imresize_to_multiple, imrotate, imshear, imtranslate, - rescale_size) -from .io import imfrombytes, imread, imwrite, supported_backends, use_backend -from .misc import tensor2imgs -from .photometric import (adjust_brightness, adjust_color, adjust_contrast, - adjust_lighting, adjust_sharpness, auto_contrast, - clahe, imdenormalize, imequalize, iminvert, - imnormalize, imnormalize_, lut_transform, posterize, - solarize) - -__all__ = [ - 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', - 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', - 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', - 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', - 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', - 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', - 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', - 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', - 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', - 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/colorspace.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/colorspace.py deleted file mode 100644 index 4337720ea..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/colorspace.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def imconvert(img, src, dst): - """Convert an image from the src colorspace to dst colorspace. - - Args: - img (ndarray): The input image. - src (str): The source colorspace, e.g., 'rgb', 'hsv'. - dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. - - Returns: - ndarray: The converted image. - """ - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - out_img = cv2.cvtColor(img, code) - return out_img - - -def bgr2gray(img, keepdim=False): - """Convert a BGR image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def rgb2gray(img, keepdim=False): - """Convert a RGB image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def gray2bgr(img): - """Convert a grayscale image to BGR image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted BGR image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return out_img - - -def gray2rgb(img): - """Convert a grayscale image to RGB image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted RGB image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) - return out_img - - -def _convert_input_type_range(img): - """Convert the type and range of the input image. - - It converts the input image to np.float32 type and range of [0, 1]. - It is mainly used for pre-processing the input image in colorspace - conversion functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - (ndarray): The converted image with type of np.float32 and range of - [0, 1]. - """ - img_type = img.dtype - img = img.astype(np.float32) - if img_type == np.float32: - pass - elif img_type == np.uint8: - img /= 255. - else: - raise TypeError('The img type should be np.float32 or np.uint8, ' - f'but got {img_type}') - return img - - -def _convert_output_type_range(img, dst_type): - """Convert the type and range of the image according to dst_type. - - It converts the image to desired type and range. If `dst_type` is np.uint8, - images will be converted to np.uint8 type with range [0, 255]. If - `dst_type` is np.float32, it converts the image to np.float32 type with - range [0, 1]. - It is mainly used for post-processing images in colorspace conversion - functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The image to be converted with np.float32 type and - range [0, 255]. - dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it - converts the image to np.uint8 type with range [0, 255]. If - dst_type is np.float32, it converts the image to np.float32 type - with range [0, 1]. - - Returns: - (ndarray): The converted image with desired type and range. - """ - if dst_type not in (np.uint8, np.float32): - raise TypeError('The dst_type should be np.float32 or np.uint8, ' - f'but got {dst_type}') - if dst_type == np.uint8: - img = img.round() - else: - img /= 255. - return img.astype(dst_type) - - -def rgb2ycbcr(img, y_only=False): - """Convert a RGB image to YCbCr image. - - This function produces the same results as Matlab's `rgb2ycbcr` function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 - else: - out_img = np.matmul( - img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], - [24.966, 112.0, -18.214]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def bgr2ycbcr(img, y_only=False): - """Convert a BGR image to YCbCr image. - - The bgr version of rgb2ycbcr. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 - else: - out_img = np.matmul( - img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], - [65.481, -37.797, 112.0]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2rgb(img): - """Convert a YCbCr image to RGB image. - - This function produces the same results as Matlab's ycbcr2rgb function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted RGB image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0, -0.00153632, 0.00791071], - [0.00625893, -0.00318811, 0]]) * 255.0 + [ - -222.921, 135.576, -276.836 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2bgr(img): - """Convert a YCbCr image to BGR image. - - The bgr version of ycbcr2rgb. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted BGR image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0.00791071, -0.00153632, 0], - [0, -0.00318811, 0.00625893]]) * 255.0 + [ - -276.836, 135.576, -222.921 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def convert_color_factory(src, dst): - - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - - def convert_color(img): - out_img = cv2.cvtColor(img, code) - return out_img - - convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} - image. - - Args: - img (ndarray or str): The input image. - - Returns: - ndarray: The converted {dst.upper()} image. - """ - - return convert_color - - -bgr2rgb = convert_color_factory('bgr', 'rgb') - -rgb2bgr = convert_color_factory('rgb', 'bgr') - -bgr2hsv = convert_color_factory('bgr', 'hsv') - -hsv2bgr = convert_color_factory('hsv', 'bgr') - -bgr2hls = convert_color_factory('bgr', 'hls') - -hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/geometric.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/geometric.py deleted file mode 100644 index 4c423bf2a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/geometric.py +++ /dev/null @@ -1,741 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers - -import cv2 -import numpy as np - -from ..utils import to_2tuple -from .io import imread_backend - -try: - from PIL import Image -except ImportError: - Image = None - - -def _scale_size(size, scale): - """Rescale a size by a ratio. - - Args: - size (tuple[int]): (w, h). - scale (float | tuple(float)): Scaling factor. - - Returns: - tuple[int]: scaled size. - """ - if isinstance(scale, (float, int)): - scale = (scale, scale) - w, h = size - return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) - - -cv2_interp_codes = { - 'nearest': cv2.INTER_NEAREST, - 'bilinear': cv2.INTER_LINEAR, - 'bicubic': cv2.INTER_CUBIC, - 'area': cv2.INTER_AREA, - 'lanczos': cv2.INTER_LANCZOS4 -} - -# Pillow >=v9.1.0 use a slightly different naming scheme for filters. -# Set pillow_interp_codes according to the naming scheme used. -if Image is not None: - if hasattr(Image, 'Resampling'): - pillow_interp_codes = { - 'nearest': Image.Resampling.NEAREST, - 'bilinear': Image.Resampling.BILINEAR, - 'bicubic': Image.Resampling.BICUBIC, - 'box': Image.Resampling.BOX, - 'lanczos': Image.Resampling.LANCZOS, - 'hamming': Image.Resampling.HAMMING - } - else: - pillow_interp_codes = { - 'nearest': Image.NEAREST, - 'bilinear': Image.BILINEAR, - 'bicubic': Image.BICUBIC, - 'box': Image.BOX, - 'lanczos': Image.LANCZOS, - 'hamming': Image.HAMMING - } - - -def imresize(img, - size, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image to a given size. - - Args: - img (ndarray): The input image. - size (tuple[int]): Target size (w, h). - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if backend is None: - backend = imread_backend - if backend not in ['cv2', 'pillow']: - raise ValueError(f'backend: {backend} is not supported for resize.' - f"Supported backends are 'cv2', 'pillow'") - - if backend == 'pillow': - assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' - pil_image = Image.fromarray(img) - pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) - resized_img = np.array(pil_image) - else: - resized_img = cv2.resize( - img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) - if not return_scale: - return resized_img - else: - w_scale = size[0] / w - h_scale = size[1] / h - return resized_img, w_scale, h_scale - - -def imresize_to_multiple(img, - divisor, - size=None, - scale_factor=None, - keep_ratio=False, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image according to a given size or scale factor and then rounds - up the the resized or rescaled image size to the nearest value that can be - divided by the divisor. - - Args: - img (ndarray): The input image. - divisor (int | tuple): Resized image size will be a multiple of - divisor. If divisor is a tuple, divisor should be - (w_divisor, h_divisor). - size (None | int | tuple[int]): Target size (w, h). Default: None. - scale_factor (None | float | tuple[float]): Multiplier for spatial - size. Should match input size if it is a tuple and the 2D style is - (w_scale_factor, h_scale_factor). Default: None. - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: False. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if size is not None and scale_factor is not None: - raise ValueError('only one of size or scale_factor should be defined') - elif size is None and scale_factor is None: - raise ValueError('one of size or scale_factor should be defined') - elif size is not None: - size = to_2tuple(size) - if keep_ratio: - size = rescale_size((w, h), size, return_scale=False) - else: - size = _scale_size((w, h), scale_factor) - - divisor = to_2tuple(divisor) - size = tuple([int(np.ceil(s / d)) * d for s, d in zip(size, divisor)]) - resized_img, w_scale, h_scale = imresize( - img, - size, - return_scale=True, - interpolation=interpolation, - out=out, - backend=backend) - if return_scale: - return resized_img, w_scale, h_scale - else: - return resized_img - - -def imresize_like(img, - dst_img, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image to the same size of a given image. - - Args: - img (ndarray): The input image. - dst_img (ndarray): The target image. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = dst_img.shape[:2] - return imresize(img, (w, h), return_scale, interpolation, backend=backend) - - -def rescale_size(old_size, scale, return_scale=False): - """Calculate the new size to be rescaled to. - - Args: - old_size (tuple[int]): The old size (w, h) of image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image size. - - Returns: - tuple[int]: The new rescaled image size. - """ - w, h = old_size - if isinstance(scale, (float, int)): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - scale_factor = scale - elif isinstance(scale, tuple): - max_long_edge = max(scale) - max_short_edge = min(scale) - scale_factor = min(max_long_edge / max(h, w), - max_short_edge / min(h, w)) - else: - raise TypeError( - f'Scale must be a number or tuple of int, but got {type(scale)}') - - new_size = _scale_size((w, h), scale_factor) - - if return_scale: - return new_size, scale_factor - else: - return new_size - - -def imrescale(img, - scale, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image while keeping the aspect ratio. - - Args: - img (ndarray): The input image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - ndarray: The rescaled image. - """ - h, w = img.shape[:2] - new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) - rescaled_img = imresize( - img, new_size, interpolation=interpolation, backend=backend) - if return_scale: - return rescaled_img, scale_factor - else: - return rescaled_img - - -def imflip(img, direction='horizontal'): - """Flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image. - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return np.flip(img, axis=1) - elif direction == 'vertical': - return np.flip(img, axis=0) - else: - return np.flip(img, axis=(0, 1)) - - -def imflip_(img, direction='horizontal'): - """Inplace flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image (inplace). - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return cv2.flip(img, 1, img) - elif direction == 'vertical': - return cv2.flip(img, 0, img) - else: - return cv2.flip(img, -1, img) - - -def imrotate(img, - angle, - center=None, - scale=1.0, - border_value=0, - interpolation='bilinear', - auto_bound=False): - """Rotate an image. - - Args: - img (ndarray): Image to be rotated. - angle (float): Rotation angle in degrees, positive values mean - clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. - scale (float): Isotropic scale factor. - border_value (int): Border value. - interpolation (str): Same as :func:`resize`. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. - - Returns: - ndarray: The rotated image. - """ - if center is not None and auto_bound: - raise ValueError('`auto_bound` conflicts with `center`') - h, w = img.shape[:2] - if center is None: - center = ((w - 1) * 0.5, (h - 1) * 0.5) - assert isinstance(center, tuple) - - matrix = cv2.getRotationMatrix2D(center, -angle, scale) - if auto_bound: - cos = np.abs(matrix[0, 0]) - sin = np.abs(matrix[0, 1]) - new_w = h * sin + w * cos - new_h = h * cos + w * sin - matrix[0, 2] += (new_w - w) * 0.5 - matrix[1, 2] += (new_h - h) * 0.5 - w = int(np.round(new_w)) - h = int(np.round(new_h)) - rotated = cv2.warpAffine( - img, - matrix, (w, h), - flags=cv2_interp_codes[interpolation], - borderValue=border_value) - return rotated - - -def bbox_clip(bboxes, img_shape): - """Clip bboxes to fit the image shape. - - Args: - bboxes (ndarray): Shape (..., 4*k) - img_shape (tuple[int]): (height, width) of the image. - - Returns: - ndarray: Clipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) - cmin[0::2] = img_shape[1] - 1 - cmin[1::2] = img_shape[0] - 1 - clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) - return clipped_bboxes - - -def bbox_scaling(bboxes, scale, clip_shape=None): - """Scaling bboxes w.r.t the box center. - - Args: - bboxes (ndarray): Shape(..., 4). - scale (float): Scaling factor. - clip_shape (tuple[int], optional): If specified, bboxes that exceed the - boundary will be clipped according to the given shape (h, w). - - Returns: - ndarray: Scaled bboxes. - """ - if float(scale) == 1.0: - scaled_bboxes = bboxes.copy() - else: - w = bboxes[..., 2] - bboxes[..., 0] + 1 - h = bboxes[..., 3] - bboxes[..., 1] + 1 - dw = (w * (scale - 1)) * 0.5 - dh = (h * (scale - 1)) * 0.5 - scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) - if clip_shape is not None: - return bbox_clip(scaled_bboxes, clip_shape) - else: - return scaled_bboxes - - -def imcrop(img, bboxes, scale=1.0, pad_fill=None): - """Crop image patches. - - 3 steps: scale the bboxes -> clip bboxes -> crop and pad. - - Args: - img (ndarray): Image to be cropped. - bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. - scale (float, optional): Scale ratio of bboxes, the default value - 1.0 means no padding. - pad_fill (Number | list[Number]): Value to be filled for padding. - Default: None, which means no padding. - - Returns: - list[ndarray] | ndarray: The cropped image patches. - """ - chn = 1 if img.ndim == 2 else img.shape[2] - if pad_fill is not None: - if isinstance(pad_fill, (int, float)): - pad_fill = [pad_fill for _ in range(chn)] - assert len(pad_fill) == chn - - _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes - scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) - clipped_bbox = bbox_clip(scaled_bboxes, img.shape) - - patches = [] - for i in range(clipped_bbox.shape[0]): - x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) - if pad_fill is None: - patch = img[y1:y2 + 1, x1:x2 + 1, ...] - else: - _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) - if chn == 1: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) - else: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) - patch = np.array( - pad_fill, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - x_start = 0 if _x1 >= 0 else -_x1 - y_start = 0 if _y1 >= 0 else -_y1 - w = x2 - x1 + 1 - h = y2 - y1 + 1 - patch[y_start:y_start + h, x_start:x_start + w, - ...] = img[y1:y1 + h, x1:x1 + w, ...] - patches.append(patch) - - if bboxes.ndim == 1: - return patches[0] - else: - return patches - - -def impad(img, - *, - shape=None, - padding=None, - pad_val=0, - padding_mode='constant'): - """Pad the given image to a certain shape or pad on all sides with - specified padding mode and padding value. - - Args: - img (ndarray): Image to be padded. - shape (tuple[int]): Expected padding shape (h, w). Default: None. - padding (int or tuple[int]): Padding on each border. If a single int is - provided this is used to pad all borders. If tuple of length 2 is - provided this is the padding on left/right and top/bottom - respectively. If a tuple of length 4 is provided this is the - padding for the left, top, right and bottom borders respectively. - Default: None. Note that `shape` and `padding` can not be both - set. - pad_val (Number | Sequence[Number]): Values to be filled in padding - areas when padding_mode is 'constant'. Default: 0. - padding_mode (str): Type of padding. Should be: constant, edge, - reflect or symmetric. Default: constant. - - constant: pads with a constant value, this value is specified - with pad_val. - - edge: pads with the last value at the edge of the image. - - reflect: pads with reflection of image without repeating the last - value on the edge. For example, padding [1, 2, 3, 4] with 2 - elements on both sides in reflect mode will result in - [3, 2, 1, 2, 3, 4, 3, 2]. - - symmetric: pads with reflection of image repeating the last value - on the edge. For example, padding [1, 2, 3, 4] with 2 elements on - both sides in symmetric mode will result in - [2, 1, 1, 2, 3, 4, 4, 3] - - Returns: - ndarray: The padded image. - """ - - assert (shape is not None) ^ (padding is not None) - if shape is not None: - width = max(shape[1] - img.shape[1], 0) - height = max(shape[0] - img.shape[0], 0) - padding = (0, 0, width, height) - - # check pad_val - if isinstance(pad_val, tuple): - assert len(pad_val) == img.shape[-1] - elif not isinstance(pad_val, numbers.Number): - raise TypeError('pad_val must be a int or a tuple. ' - f'But received {type(pad_val)}') - - # check padding - if isinstance(padding, tuple) and len(padding) in [2, 4]: - if len(padding) == 2: - padding = (padding[0], padding[1], padding[0], padding[1]) - elif isinstance(padding, numbers.Number): - padding = (padding, padding, padding, padding) - else: - raise ValueError('Padding must be a int or a 2, or 4 element tuple.' - f'But received {padding}') - - # check padding mode - assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] - - border_type = { - 'constant': cv2.BORDER_CONSTANT, - 'edge': cv2.BORDER_REPLICATE, - 'reflect': cv2.BORDER_REFLECT_101, - 'symmetric': cv2.BORDER_REFLECT - } - img = cv2.copyMakeBorder( - img, - padding[1], - padding[3], - padding[0], - padding[2], - border_type[padding_mode], - value=pad_val) - - return img - - -def impad_to_multiple(img, divisor, pad_val=0): - """Pad an image to ensure each edge to be multiple to some number. - - Args: - img (ndarray): Image to be padded. - divisor (int): Padded image edges will be multiple to divisor. - pad_val (Number | Sequence[Number]): Same as :func:`impad`. - - Returns: - ndarray: The padded image. - """ - pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor - pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor - return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) - - -def cutout(img, shape, pad_val=0): - """Randomly cut out a rectangle from the original img. - - Args: - img (ndarray): Image to be cutout. - shape (int | tuple[int]): Expected cutout shape (h, w). If given as a - int, the value will be used for both h and w. - pad_val (int | float | tuple[int | float]): Values to be filled in the - cut area. Defaults to 0. - - Returns: - ndarray: The cutout image. - """ - - channels = 1 if img.ndim == 2 else img.shape[2] - if isinstance(shape, int): - cut_h, cut_w = shape, shape - else: - assert isinstance(shape, tuple) and len(shape) == 2, \ - f'shape must be a int or a tuple with length 2, but got type ' \ - f'{type(shape)} instead.' - cut_h, cut_w = shape - if isinstance(pad_val, (int, float)): - pad_val = tuple([pad_val] * channels) - elif isinstance(pad_val, tuple): - assert len(pad_val) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(pad_val), channels) - else: - raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') - - img_h, img_w = img.shape[:2] - y0 = np.random.uniform(img_h) - x0 = np.random.uniform(img_w) - - y1 = int(max(0, y0 - cut_h / 2.)) - x1 = int(max(0, x0 - cut_w / 2.)) - y2 = min(img_h, y1 + cut_h) - x2 = min(img_w, x1 + cut_w) - - if img.ndim == 2: - patch_shape = (y2 - y1, x2 - x1) - else: - patch_shape = (y2 - y1, x2 - x1, channels) - - img_cutout = img.copy() - patch = np.array( - pad_val, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - img_cutout[y1:y2, x1:x2, ...] = patch - - return img_cutout - - -def _get_shear_matrix(magnitude, direction='horizontal'): - """Generate the shear matrix for transformation. - - Args: - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - - Returns: - ndarray: The shear matrix with dtype float32. - """ - if direction == 'horizontal': - shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) - elif direction == 'vertical': - shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) - return shear_matrix - - -def imshear(img, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear an image. - - Args: - img (ndarray): Image to be sheared with format (h, w) - or (h, w, c). - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The sheared image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`') - shear_matrix = _get_shear_matrix(magnitude, direction) - sheared = cv2.warpAffine( - img, - shear_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. shearing masks whose channels large - # than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return sheared - - -def _get_translate_matrix(offset, direction='horizontal'): - """Generate the translate matrix. - - Args: - offset (int | float): The offset used for translate. - direction (str): The translate direction, either - "horizontal" or "vertical". - - Returns: - ndarray: The translate matrix with dtype float32. - """ - if direction == 'horizontal': - translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) - elif direction == 'vertical': - translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) - return translate_matrix - - -def imtranslate(img, - offset, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Translate an image. - - Args: - img (ndarray): Image to be translated with format - (h, w) or (h, w, c). - offset (int | float): The offset used for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The translated image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`.') - translate_matrix = _get_translate_matrix(offset, direction) - translated = cv2.warpAffine( - img, - translate_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. translating masks whose channels - # large than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return translated diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/io.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/io.py deleted file mode 100644 index ae81b561a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/io.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os.path as osp -import warnings -from pathlib import Path - -import cv2 -import numpy as np -from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, - IMREAD_UNCHANGED) - -from mmcv.fileio import FileClient -from mmcv.utils import is_filepath, is_str - -try: - from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG -except ImportError: - TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None - -try: - from PIL import Image, ImageOps -except ImportError: - Image = None - -try: - import tifffile -except ImportError: - tifffile = None - -jpeg = None -supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] - -imread_flags = { - 'color': IMREAD_COLOR, - 'grayscale': IMREAD_GRAYSCALE, - 'unchanged': IMREAD_UNCHANGED, - 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, - 'grayscale_ignore_orientation': - IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE -} - -imread_backend = 'cv2' - - -def use_backend(backend): - """Select a backend for image decoding. - - Args: - backend (str): The image decoding backend type. Options are `cv2`, - `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) - and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` - file format. - """ - assert backend in supported_backends - global imread_backend - imread_backend = backend - if imread_backend == 'turbojpeg': - if TurboJPEG is None: - raise ImportError('`PyTurboJPEG` is not installed') - global jpeg - if jpeg is None: - jpeg = TurboJPEG() - elif imread_backend == 'pillow': - if Image is None: - raise ImportError('`Pillow` is not installed') - elif imread_backend == 'tifffile': - if tifffile is None: - raise ImportError('`tifffile` is not installed') - - -def _jpegflag(flag='color', channel_order='bgr'): - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'color': - if channel_order == 'bgr': - return TJPF_BGR - elif channel_order == 'rgb': - return TJCS_RGB - elif flag == 'grayscale': - return TJPF_GRAY - else: - raise ValueError('flag must be "color" or "grayscale"') - - -def _pillow2array(img, flag='color', channel_order='bgr'): - """Convert a pillow image to numpy array. - - Args: - img (:obj:`PIL.Image.Image`): The image loaded using PIL - flag (str): Flags specifying the color type of a loaded image, - candidates are 'color', 'grayscale' and 'unchanged'. - Default to 'color'. - channel_order (str): The channel order of the output image array, - candidates are 'bgr' and 'rgb'. Default to 'bgr'. - - Returns: - np.ndarray: The converted numpy array - """ - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'unchanged': - array = np.array(img) - if array.ndim >= 3 and array.shape[2] >= 3: # color image - array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR - else: - # Handle exif orientation tag - if flag in ['color', 'grayscale']: - img = ImageOps.exif_transpose(img) - # If the image mode is not 'RGB', convert it to 'RGB' first. - if img.mode != 'RGB': - if img.mode != 'LA': - # Most formats except 'LA' can be directly converted to RGB - img = img.convert('RGB') - else: - # When the mode is 'LA', the default conversion will fill in - # the canvas with black, which sometimes shadows black objects - # in the foreground. - # - # Therefore, a random color (124, 117, 104) is used for canvas - img_rgba = img.convert('RGBA') - img = Image.new('RGB', img_rgba.size, (124, 117, 104)) - img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha - if flag in ['color', 'color_ignore_orientation']: - array = np.array(img) - if channel_order != 'rgb': - array = array[:, :, ::-1] # RGB to BGR - elif flag in ['grayscale', 'grayscale_ignore_orientation']: - img = img.convert('L') - array = np.array(img) - else: - raise ValueError( - 'flag must be "color", "grayscale", "unchanged", ' - f'"color_ignore_orientation" or "grayscale_ignore_orientation"' - f' but got {flag}') - return array - - -def imread(img_or_path, - flag='color', - channel_order='bgr', - backend=None, - file_client_args=None): - """Read an image. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Args: - img_or_path (ndarray or str or Path): Either a numpy array or str or - pathlib.Path. If it is a numpy array (loaded image), then - it will be returned as is. - flag (str): Flags specifying the color type of a loaded image, - candidates are `color`, `grayscale`, `unchanged`, - `color_ignore_orientation` and `grayscale_ignore_orientation`. - By default, `cv2` and `pillow` backend would rotate the image - according to its EXIF info unless called with `unchanged` or - `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend - always ignore image's EXIF info regardless of the flag. - The `turbojpeg` backend only supports `color` and `grayscale`. - channel_order (str): Order of channel, candidates are `bgr` and `rgb`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. - If backend is None, the global imread_backend specified by - ``mmcv.use_backend()`` will be used. Default: None. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> import mmcv - >>> img_path = '/path/to/img.jpg' - >>> img = mmcv.imread(img_path) - >>> img = mmcv.imread(img_path, flag='color', channel_order='rgb', - ... backend='cv2') - >>> img = mmcv.imread(img_path, flag='color', channel_order='bgr', - ... backend='pillow') - >>> s3_img_path = 's3://bucket/img.jpg' - >>> # infer the file backend by the prefix s3 - >>> img = mmcv.imread(s3_img_path) - >>> # manually set the file backend petrel - >>> img = mmcv.imread(s3_img_path, file_client_args={ - ... 'backend': 'petrel'}) - >>> http_img_path = 'http://path/to/img.jpg' - >>> img = mmcv.imread(http_img_path) - >>> img = mmcv.imread(http_img_path, file_client_args={ - ... 'backend': 'http'}) - """ - - if isinstance(img_or_path, Path): - img_or_path = str(img_or_path) - - if isinstance(img_or_path, np.ndarray): - return img_or_path - elif is_str(img_or_path): - file_client = FileClient.infer_client(file_client_args, img_or_path) - img_bytes = file_client.get(img_or_path) - return imfrombytes(img_bytes, flag, channel_order, backend) - else: - raise TypeError('"img" must be a numpy array or a str or ' - 'a pathlib.Path object') - - -def imfrombytes(content, flag='color', channel_order='bgr', backend=None): - """Read an image from bytes. - - Args: - content (bytes): Image bytes got from files or other streams. - flag (str): Same as :func:`imread`. - channel_order (str): The channel order of the output, candidates - are 'bgr' and 'rgb'. Default to 'bgr'. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. If backend is - None, the global imread_backend specified by ``mmcv.use_backend()`` - will be used. Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> img_path = '/path/to/img.jpg' - >>> with open(img_path, 'rb') as f: - >>> img_buff = f.read() - >>> img = mmcv.imfrombytes(img_buff) - >>> img = mmcv.imfrombytes(img_buff, flag='color', channel_order='rgb') - >>> img = mmcv.imfrombytes(img_buff, backend='pillow') - >>> img = mmcv.imfrombytes(img_buff, backend='cv2') - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError( - f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow', 'tifffile'") - if backend == 'turbojpeg': - img = jpeg.decode(content, _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - with io.BytesIO(content) as buff: - img = Image.open(buff) - img = _pillow2array(img, flag, channel_order) - return img - elif backend == 'tifffile': - with io.BytesIO(content) as buff: - img = tifffile.imread(buff) - return img - else: - img_np = np.frombuffer(content, np.uint8) - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imdecode(img_np, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - - -def imwrite(img, - file_path, - params=None, - auto_mkdir=None, - file_client_args=None): - """Write image to file. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Warning: - The parameter `auto_mkdir` will be deprecated in the future and every - file clients will make directory automatically. - - Args: - img (ndarray): Image array to be written. - file_path (str): Image file path. - params (None or list): Same as opencv :func:`imwrite` interface. - auto_mkdir (bool): If the parent folder of `file_path` does not exist, - whether to create it automatically. It will be deprecated. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - bool: Successful or not. - - Examples: - >>> # write to hard disk client - >>> ret = mmcv.imwrite(img, '/path/to/img.jpg') - >>> # infer the file backend by the prefix s3 - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg') - >>> # manually set the file backend petrel - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg', file_client_args={ - ... 'backend': 'petrel'}) - """ - assert is_filepath(file_path) - file_path = str(file_path) - if auto_mkdir is not None: - warnings.warn( - 'The parameter `auto_mkdir` will be deprecated in the future and ' - 'every file clients will make directory automatically.') - file_client = FileClient.infer_client(file_client_args, file_path) - img_ext = osp.splitext(file_path)[-1] - # Encode image according to image suffix. - # For example, if image path is '/path/your/img.jpg', the encode - # format is '.jpg'. - flag, img_buff = cv2.imencode(img_ext, img, params) - file_client.put(img_buff.tobytes(), file_path) - return flag diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/misc.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/misc.py deleted file mode 100644 index 43934a689..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/misc.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - -import mmcv - -try: - import torch -except ImportError: - torch = None - - -def tensor2imgs(tensor, mean=None, std=None, to_rgb=True): - """Convert tensor to 3-channel images or 1-channel gray images. - - Args: - tensor (torch.Tensor): Tensor that contains multiple images, shape ( - N, C, H, W). :math:`C` can be either 3 or 1. - mean (tuple[float], optional): Mean of images. If None, - (0, 0, 0) will be used for tensor with 3-channel, - while (0, ) for tensor with 1-channel. Defaults to None. - std (tuple[float], optional): Standard deviation of images. If None, - (1, 1, 1) will be used for tensor with 3-channel, - while (1, ) for tensor with 1-channel. Defaults to None. - to_rgb (bool, optional): Whether the tensor was converted to RGB - format in the first place. If so, convert it back to BGR. - For the tensor with 1 channel, it must be False. Defaults to True. - - Returns: - list[np.ndarray]: A list that contains multiple images. - """ - - if torch is None: - raise RuntimeError('pytorch is not installed') - assert torch.is_tensor(tensor) and tensor.ndim == 4 - channels = tensor.size(1) - assert channels in [1, 3] - if mean is None: - mean = (0, ) * channels - if std is None: - std = (1, ) * channels - assert (channels == len(mean) == len(std) == 3) or \ - (channels == len(mean) == len(std) == 1 and not to_rgb) - - num_imgs = tensor.size(0) - mean = np.array(mean, dtype=np.float32) - std = np.array(std, dtype=np.float32) - imgs = [] - for img_id in range(num_imgs): - img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) - img = mmcv.imdenormalize( - img, mean, std, to_bgr=to_rgb).astype(np.uint8) - imgs.append(np.ascontiguousarray(img)) - return imgs diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/photometric.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/photometric.py deleted file mode 100644 index 5085d0120..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/image/photometric.py +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - -from ..utils import is_tuple_of -from .colorspace import bgr2gray, gray2bgr - - -def imnormalize(img, mean, std, to_rgb=True): - """Normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - img = img.copy().astype(np.float32) - return imnormalize_(img, mean, std, to_rgb) - - -def imnormalize_(img, mean, std, to_rgb=True): - """Inplace normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - # cv2 inplace normalization does not accept uint8 - assert img.dtype != np.uint8 - mean = np.float64(mean.reshape(1, -1)) - stdinv = 1 / np.float64(std.reshape(1, -1)) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - cv2.subtract(img, mean, img) # inplace - cv2.multiply(img, stdinv, img) # inplace - return img - - -def imdenormalize(img, mean, std, to_bgr=True): - assert img.dtype != np.uint8 - mean = mean.reshape(1, -1).astype(np.float64) - std = std.reshape(1, -1).astype(np.float64) - img = cv2.multiply(img, std) # make a copy - cv2.add(img, mean, img) # inplace - if to_bgr: - cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace - return img - - -def iminvert(img): - """Invert (negate) an image. - - Args: - img (ndarray): Image to be inverted. - - Returns: - ndarray: The inverted image. - """ - return np.full_like(img, 255) - img - - -def solarize(img, thr=128): - """Solarize an image (invert all pixel values above a threshold) - - Args: - img (ndarray): Image to be solarized. - thr (int): Threshold for solarizing (0 - 255). - - Returns: - ndarray: The solarized image. - """ - img = np.where(img < thr, img, 255 - img) - return img - - -def posterize(img, bits): - """Posterize an image (reduce the number of bits for each color channel) - - Args: - img (ndarray): Image to be posterized. - bits (int): Number of bits (1 to 8) to use for posterizing. - - Returns: - ndarray: The posterized image. - """ - shift = 8 - bits - img = np.left_shift(np.right_shift(img, shift), shift) - return img - - -def adjust_color(img, alpha=1, beta=None, gamma=0): - r"""It blends the source image and its gray image: - - .. math:: - output = img * alpha + gray\_img * beta + gamma - - Args: - img (ndarray): The input source image. - alpha (int | float): Weight for the source image. Default 1. - beta (int | float): Weight for the converted gray image. - If None, it's assigned the value (1 - `alpha`). - gamma (int | float): Scalar added to each sum. - Same as :func:`cv2.addWeighted`. Default 0. - - Returns: - ndarray: Colored image which has the same size and dtype as input. - """ - gray_img = bgr2gray(img) - gray_img = np.tile(gray_img[..., None], [1, 1, 3]) - if beta is None: - beta = 1 - alpha - colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) - if not colored_img.dtype == np.uint8: - # Note when the dtype of `img` is not the default `np.uint8` - # (e.g. np.float32), the value in `colored_img` got from cv2 - # is not guaranteed to be in range [0, 255], so here clip - # is needed. - colored_img = np.clip(colored_img, 0, 255) - return colored_img - - -def imequalize(img): - """Equalize the image histogram. - - This function applies a non-linear mapping to the input image, - in order to create a uniform distribution of grayscale values - in the output image. - - Args: - img (ndarray): Image to be equalized. - - Returns: - ndarray: The equalized image. - """ - - def _scale_channel(im, c): - """Scale the data in the corresponding channel.""" - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # For computing the step, filter out the nonzeros. - nonzero_histo = histo[histo > 0] - step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 - if not step: - lut = np.array(range(256)) - else: - # Compute the cumulative sum, shifted by step // 2 - # and then normalized by step. - lut = (np.cumsum(histo) + (step // 2)) // step - # Shift lut, prepending with 0. - lut = np.concatenate([[0], lut[:-1]], 0) - # handle potential integer overflow - lut[lut > 255] = 255 - # If step is zero, return the original image. - # Otherwise, index from lut. - return np.where(np.equal(step, 0), im, lut[im]) - - # Scales each channel independently and then stacks - # the result. - s1 = _scale_channel(img, 0) - s2 = _scale_channel(img, 1) - s3 = _scale_channel(img, 2) - equalized_img = np.stack([s1, s2, s3], axis=-1) - return equalized_img.astype(img.dtype) - - -def adjust_brightness(img, factor=1.): - """Adjust image brightness. - - This function controls the brightness of an image. An - enhancement factor of 0.0 gives a black image. - A factor of 1.0 gives the original image. This function - blends the source image and the degenerated black image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be brightened. - factor (float): A value controls the enhancement. - Factor 1.0 returns the original image, lower - factors mean less color (brightness, contrast, - etc), and higher values more. Default 1. - - Returns: - ndarray: The brightened image. - """ - degenerated = np.zeros_like(img) - # Note manually convert the dtype to np.float32, to - # achieve as close results as PIL.ImageEnhance.Brightness. - # Set beta=1-factor, and gamma=0 - brightened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - brightened_img = np.clip(brightened_img, 0, 255) - return brightened_img.astype(img.dtype) - - -def adjust_contrast(img, factor=1.): - """Adjust image contrast. - - This function controls the contrast of an image. An - enhancement factor of 0.0 gives a solid grey - image. A factor of 1.0 gives the original image. It - blends the source image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be contrasted. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - - Returns: - ndarray: The contrasted image. - """ - gray_img = bgr2gray(img) - hist = np.histogram(gray_img, 256, (0, 255))[0] - mean = round(np.sum(gray_img) / np.sum(hist)) - degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) - degenerated = gray2bgr(degenerated) - contrasted_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - contrasted_img = np.clip(contrasted_img, 0, 255) - return contrasted_img.astype(img.dtype) - - -def auto_contrast(img, cutoff=0): - """Auto adjust image contrast. - - This function maximize (normalize) image contrast by first removing cutoff - percent of the lightest and darkest pixels from the histogram and remapping - the image so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - Args: - img (ndarray): Image to be contrasted. BGR order. - cutoff (int | float | tuple): The cutoff percent of the lightest and - darkest pixels to be removed. If given as tuple, it shall be - (low, high). Otherwise, the single value will be used for both. - Defaults to 0. - - Returns: - ndarray: The contrasted image. - """ - - def _auto_contrast_channel(im, c, cutoff): - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # Remove cut-off percent pixels from histo - histo_sum = np.cumsum(histo) - cut_low = histo_sum[-1] * cutoff[0] // 100 - cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 - histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low - histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) - - # Compute mapping - low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] - # If all the values have been cut off, return the origin img - if low >= high: - return im - scale = 255.0 / (high - low) - offset = -low * scale - lut = np.array(range(256)) - lut = lut * scale + offset - lut = np.clip(lut, 0, 255) - return lut[im] - - if isinstance(cutoff, (int, float)): - cutoff = (cutoff, cutoff) - else: - assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ - f'float or tuple, but got {type(cutoff)} instead.' - # Auto adjusts contrast for each channel independently and then stacks - # the result. - s1 = _auto_contrast_channel(img, 0, cutoff) - s2 = _auto_contrast_channel(img, 1, cutoff) - s3 = _auto_contrast_channel(img, 2, cutoff) - contrasted_img = np.stack([s1, s2, s3], axis=-1) - return contrasted_img.astype(img.dtype) - - -def adjust_sharpness(img, factor=1., kernel=None): - """Adjust image sharpness. - - This function controls the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image. A - factor of 1.0 gives the original image. And a factor - of 2.0 gives a sharpened image. It blends the source - image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be sharpened. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - kernel (np.ndarray, optional): Filter kernel to be applied on the img - to obtain the degenerated img. Defaults to None. - - Note: - No value sanity check is enforced on the kernel set by users. So with - an inappropriate kernel, the ``adjust_sharpness`` may fail to perform - the function its name indicates but end up performing whatever - transform determined by the kernel. - - Returns: - ndarray: The sharpened image. - """ - - if kernel is None: - # adopted from PIL.ImageFilter.SMOOTH - kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 - assert isinstance(kernel, np.ndarray), \ - f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' - assert kernel.ndim == 2, \ - f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' - - degenerated = cv2.filter2D(img, -1, kernel) - sharpened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - sharpened_img = np.clip(sharpened_img, 0, 255) - return sharpened_img.astype(img.dtype) - - -def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): - """AlexNet-style PCA jitter. - - This data augmentation is proposed in `ImageNet Classification with Deep - Convolutional Neural Networks - `_. - - Args: - img (ndarray): Image to be adjusted lighting. BGR order. - eigval (ndarray): the eigenvalue of the convariance matrix of pixel - values, respectively. - eigvec (ndarray): the eigenvector of the convariance matrix of pixel - values, respectively. - alphastd (float): The standard deviation for distribution of alpha. - Defaults to 0.1 - to_rgb (bool): Whether to convert img to rgb. - - Returns: - ndarray: The adjusted image. - """ - assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ - f'eigval and eigvec should both be of type np.ndarray, got ' \ - f'{type(eigval)} and {type(eigvec)} instead.' - - assert eigval.ndim == 1 and eigvec.ndim == 2 - assert eigvec.shape == (3, eigval.shape[0]) - n_eigval = eigval.shape[0] - assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ - f'got {type(alphastd)} instead.' - - img = img.copy().astype(np.float32) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - - alpha = np.random.normal(0, alphastd, n_eigval) - alter = eigvec \ - * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ - * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) - alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) - img_adjusted = img + alter - return img_adjusted - - -def lut_transform(img, lut_table): - """Transform array by look-up table. - - The function lut_transform fills the output array with values from the - look-up table. Indices of the entries are taken from the input array. - - Args: - img (ndarray): Image to be transformed. - lut_table (ndarray): look-up table of 256 elements; in case of - multi-channel input array, the table should either have a single - channel (in this case the same table is used for all channels) or - the same number of channels as in the input array. - - Returns: - ndarray: The transformed image. - """ - assert isinstance(img, np.ndarray) - assert 0 <= np.min(img) and np.max(img) <= 255 - assert isinstance(lut_table, np.ndarray) - assert lut_table.shape == (256, ) - - return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) - - -def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - img (ndarray): Image to be processed. - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - - Returns: - ndarray: The processed image. - """ - assert isinstance(img, np.ndarray) - assert img.ndim == 2 - assert isinstance(clip_limit, (float, int)) - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - - clahe = cv2.createCLAHE(clip_limit, tile_grid_size) - return clahe.apply(np.array(img, dtype=np.uint8)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/deprecated.json b/cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/deprecated.json deleted file mode 100644 index 25cf6f28c..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/deprecated.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "resnet50_caffe": "detectron/resnet50_caffe", - "resnet50_caffe_bgr": "detectron2/resnet50_caffe_bgr", - "resnet101_caffe": "detectron/resnet101_caffe", - "resnet101_caffe_bgr": "detectron2/resnet101_caffe_bgr" -} diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/mmcls.json b/cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/mmcls.json deleted file mode 100644 index c073a41d0..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/mmcls.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "vgg11": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_batch256_imagenet_20210208-4271cd6c.pth", - "vgg13": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_batch256_imagenet_20210208-4d1d6080.pth", - "vgg16": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_batch256_imagenet_20210208-db26f1a5.pth", - "vgg19": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_batch256_imagenet_20210208-e6920e4a.pth", - "vgg11_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_bn_batch256_imagenet_20210207-f244902c.pth", - "vgg13_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_bn_batch256_imagenet_20210207-1a8b7864.pth", - "vgg16_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_bn_batch256_imagenet_20210208-7e55cd29.pth", - "vgg19_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_bn_batch256_imagenet_20210208-da620c4f.pth", - "resnet18": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet18_8xb32_in1k_20210831-fbbb1da6.pth", - "resnet34": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet34_8xb32_in1k_20210831-f257d4e6.pth", - "resnet50": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb32_in1k_20210831-ea4938fc.pth", - "resnet101": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet101_8xb32_in1k_20210831-539c63f8.pth", - "resnet152": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet152_8xb32_in1k_20210901-4d7582fa.pth", - "resnet50_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d50_b32x8_imagenet_20210531-db14775a.pth", - "resnet101_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d101_b32x8_imagenet_20210531-6e13bcd3.pth", - "resnet152_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d152_b32x8_imagenet_20210531-278cf22a.pth", - "resnext50_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext50_32x4d_b32x8_imagenet_20210429-56066e27.pth", - "resnext101_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x4d_b32x8_imagenet_20210506-e0fa3dd5.pth", - "resnext101_32x8d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x8d_b32x8_imagenet_20210506-23a247d5.pth", - "resnext152_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext152_32x4d_b32x8_imagenet_20210524-927787be.pth", - "se-resnet50": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet50_batch256_imagenet_20200804-ae206104.pth", - "se-resnet101": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet101_batch256_imagenet_20200804-ba5b51d4.pth", - "resnest50": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest50_imagenet_converted-1ebf0afe.pth", - "resnest101": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest101_imagenet_converted-032caa52.pth", - "resnest200": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest200_imagenet_converted-581a60f2.pth", - "resnest269": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest269_imagenet_converted-59930960.pth", - "shufflenet_v1": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v1/shufflenet_v1_batch1024_imagenet_20200804-5d6cec73.pth", - "shufflenet_v2": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v2/shufflenet_v2_batch1024_imagenet_20200812-5bf4721e.pth", - "mobilenet_v2": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v2/mobilenet_v2_batch256_imagenet_20200708-3b2dc3af.pth", - "mobilenet_v3_small": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_small-8427ecf0.pth", - "mobilenet_v3_large": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_large-3ea3c186.pth", - "repvgg_A0": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A0_3rdparty_4xb64-coslr-120e_in1k_20210909-883ab98c.pth", - "repvgg_A1": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A1_3rdparty_4xb64-coslr-120e_in1k_20210909-24003a24.pth", - "repvgg_A2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A2_3rdparty_4xb64-coslr-120e_in1k_20210909-97d7695a.pth", - "repvgg_B0": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B0_3rdparty_4xb64-coslr-120e_in1k_20210909-446375f4.pth", - "repvgg_B1": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1_3rdparty_4xb64-coslr-120e_in1k_20210909-750cdf67.pth", - "repvgg_B1g2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1g2_3rdparty_4xb64-coslr-120e_in1k_20210909-344f6422.pth", - "repvgg_B1g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1g4_3rdparty_4xb64-coslr-120e_in1k_20210909-d4c1a642.pth", - "repvgg_B2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B2_3rdparty_4xb64-coslr-120e_in1k_20210909-bd6b937c.pth", - "repvgg_B2g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B2g4_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-7b7955f0.pth", - "repvgg_B3": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B3_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-dda968bf.pth", - "repvgg_B3g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B3g4_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-4e54846a.pth", - "repvgg_D2se": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-D2se_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-cf3139b7.pth", - "res2net101_w26": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net101-w26-s4_3rdparty_8xb32_in1k_20210927-870b6c36.pth", - "res2net50_w14": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net50-w14-s8_3rdparty_8xb32_in1k_20210927-bc967bf1.pth", - "res2net50_w26": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net50-w26-s8_3rdparty_8xb32_in1k_20210927-f547a94b.pth", - "swin_tiny": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_tiny_224_b16x64_300e_imagenet_20210616_090925-66df6be6.pth", - "swin_small": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_small_224_b16x64_300e_imagenet_20210615_110219-7f9d988b.pth", - "swin_base": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/convert/swin_base_patch4_window7_224_22kto1k-f967f799.pth", - "swin_large": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/convert/swin_large_patch4_window7_224_22kto1k-5f0996db.pth", - "t2t_vit_t_14": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-14_3rdparty_8xb64_in1k_20210928-b7c09b62.pth", - "t2t_vit_t_19": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-19_3rdparty_8xb64_in1k_20210928-7f1478d5.pth", - "t2t_vit_t_24": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-24_3rdparty_8xb64_in1k_20210928-fe95a61b.pth", - "tnt_small": "https://download.openmmlab.com/mmclassification/v0/tnt/tnt-small-p16_3rdparty_in1k_20210903-c56ee7df.pth", - "vit_base_p16": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-98e8652b.pth", - "vit_base_p32": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p32_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-9cea8599.pth", - "vit_large_p16": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-large-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-b20ba619.pth" -} diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/open_mmlab.json b/cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/open_mmlab.json deleted file mode 100644 index 8311db4fe..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/open_mmlab.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "vgg16_caffe": "https://download.openmmlab.com/pretrain/third_party/vgg16_caffe-292e1171.pth", - "detectron/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_caffe-788b5fa3.pth", - "detectron2/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_msra-5891d200.pth", - "detectron/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_caffe-3ad79236.pth", - "detectron2/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_msra-6cc46731.pth", - "detectron2/resnext101_32x8d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x8d-1516f1aa.pth", - "resnext50_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext50-32x4d-0ab1a123.pth", - "resnext101_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d-a5af3160.pth", - "resnext101_64x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_64x4d-ee2c6f71.pth", - "contrib/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_thangvubk-ad1730dd.pth", - "detectron/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn-9186a21c.pth", - "detectron/resnet101_gn": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn-cac0ab98.pth", - "jhu/resnet50_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_ws-15beedd8.pth", - "jhu/resnet101_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn_ws-3e3c308c.pth", - "jhu/resnext50_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn_ws-0d87ac85.pth", - "jhu/resnext101_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn_ws-34ac1a9e.pth", - "jhu/resnext50_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn-c7e8b754.pth", - "jhu/resnext101_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn-ac3bb84e.pth", - "msra/hrnetv2_w18_small": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18_small-b5a04e21.pth", - "msra/hrnetv2_w18": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18-00eb2006.pth", - "msra/hrnetv2_w32": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w32-dc9eeb4f.pth", - "msra/hrnetv2_w40": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w40-ed0b031c.pth", - "msra/hrnetv2_w48": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w48-d2186c55.pth", - "bninception_caffe": "https://download.openmmlab.com/pretrain/third_party/bn_inception_caffe-ed2e8665.pth", - "kin400/i3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/i3d_r50_f32s2_k400-2c57e077.pth", - "kin400/nl3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/nl3d_r50_f32s2_k400-fa7e7caa.pth", - "res2net101_v1d_26w_4s": "https://download.openmmlab.com/pretrain/third_party/res2net101_v1d_26w_4s_mmdetv2-f0a600f9.pth", - "regnetx_400mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_400mf-a5b10d96.pth", - "regnetx_800mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_800mf-1f4be4c7.pth", - "regnetx_1.6gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_1.6gf-5791c176.pth", - "regnetx_3.2gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_3.2gf-c2599b0f.pth", - "regnetx_4.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_4.0gf-a88f671e.pth", - "regnetx_6.4gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_6.4gf-006af45d.pth", - "regnetx_8.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_8.0gf-3c68abe7.pth", - "regnetx_12gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_12gf-4c2a3350.pth", - "resnet18_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet18_v1c-b5776b93.pth", - "resnet50_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet50_v1c-2cccc1ad.pth", - "resnet101_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet101_v1c-e67eebb6.pth", - "mmedit/vgg16": "https://download.openmmlab.com/mmediting/third_party/vgg_state_dict.pth", - "mmedit/res34_en_nomixup": "https://download.openmmlab.com/mmediting/third_party/model_best_resnet34_En_nomixup.pth", - "mmedit/mobilenet_v2": "https://download.openmmlab.com/mmediting/third_party/mobilenet_v2.pth", - "contrib/mobilenet_v3_large": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_large-bc2c3fd3.pth", - "contrib/mobilenet_v3_small": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_small-47085aa1.pth", - "resnest50": "https://download.openmmlab.com/pretrain/third_party/resnest50_d2-7497a55b.pth", - "resnest101": "https://download.openmmlab.com/pretrain/third_party/resnest101_d2-f3b931b2.pth", - "resnest200": "https://download.openmmlab.com/pretrain/third_party/resnest200_d2-ca88e41f.pth", - "darknet53": "https://download.openmmlab.com/pretrain/third_party/darknet53-a628ea1b.pth", - "mmdet/mobilenet_v2": "https://download.openmmlab.com/mmdetection/v2.0/third_party/mobilenet_v2_batch256_imagenet-ff34753d.pth" -} diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/torchvision_0.12.json b/cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/torchvision_0.12.json deleted file mode 100644 index 06defe674..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/model_zoo/torchvision_0.12.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "alexnet": "https://download.pytorch.org/models/alexnet-owt-7be5be79.pth", - "densenet121": "https://download.pytorch.org/models/densenet121-a639ec97.pth", - "densenet169": "https://download.pytorch.org/models/densenet169-b2777c0a.pth", - "densenet201": "https://download.pytorch.org/models/densenet201-c1103571.pth", - "densenet161": "https://download.pytorch.org/models/densenet161-8d451a50.pth", - "efficientnet_b0": "https://download.pytorch.org/models/efficientnet_b0_rwightman-3dd342df.pth", - "efficientnet_b1": "https://download.pytorch.org/models/efficientnet_b1_rwightman-533bc792.pth", - "efficientnet_b2": "https://download.pytorch.org/models/efficientnet_b2_rwightman-bcdf34b7.pth", - "efficientnet_b3": "https://download.pytorch.org/models/efficientnet_b3_rwightman-cf984f9c.pth", - "efficientnet_b4": "https://download.pytorch.org/models/efficientnet_b4_rwightman-7eb33cd5.pth", - "efficientnet_b5": "https://download.pytorch.org/models/efficientnet_b5_lukemelas-b6417697.pth", - "efficientnet_b6": "https://download.pytorch.org/models/efficientnet_b6_lukemelas-c76e70fd.pth", - "efficientnet_b7": "https://download.pytorch.org/models/efficientnet_b7_lukemelas-dcc49843.pth", - "googlenet": "https://download.pytorch.org/models/googlenet-1378be20.pth", - "inception_v3_google": "https://download.pytorch.org/models/inception_v3_google-0cc3c7bd.pth", - "mobilenet_v2": "https://download.pytorch.org/models/mobilenet_v2-b0353104.pth", - "mobilenet_v3_large": "https://download.pytorch.org/models/mobilenet_v3_large-8738ca79.pth", - "mobilenet_v3_small": "https://download.pytorch.org/models/mobilenet_v3_small-047dcff4.pth", - "regnet_y_400mf": "https://download.pytorch.org/models/regnet_y_400mf-c65dace8.pth", - "regnet_y_800mf": "https://download.pytorch.org/models/regnet_y_800mf-1b27b58c.pth", - "regnet_y_1_6gf": "https://download.pytorch.org/models/regnet_y_1_6gf-b11a554e.pth", - "regnet_y_3_2gf": "https://download.pytorch.org/models/regnet_y_3_2gf-b5a9779c.pth", - "regnet_y_8gf": "https://download.pytorch.org/models/regnet_y_8gf-d0d0e4a8.pth", - "regnet_y_16gf": "https://download.pytorch.org/models/regnet_y_16gf-9e6ed7dd.pth", - "regnet_y_32gf": "https://download.pytorch.org/models/regnet_y_32gf-4dee3f7a.pth", - "regnet_x_400mf": "https://download.pytorch.org/models/regnet_x_400mf-adf1edd5.pth", - "regnet_x_800mf": "https://download.pytorch.org/models/regnet_x_800mf-ad17e45c.pth", - "regnet_x_1_6gf": "https://download.pytorch.org/models/regnet_x_1_6gf-e3633e7f.pth", - "regnet_x_3_2gf": "https://download.pytorch.org/models/regnet_x_3_2gf-f342aeae.pth", - "regnet_x_8gf": "https://download.pytorch.org/models/regnet_x_8gf-03ceed89.pth", - "regnet_x_16gf": "https://download.pytorch.org/models/regnet_x_16gf-2007eb11.pth", - "regnet_x_32gf": "https://download.pytorch.org/models/regnet_x_32gf-9d47f8d0.pth", - "resnet18": "https://download.pytorch.org/models/resnet18-f37072fd.pth", - "resnet34": "https://download.pytorch.org/models/resnet34-b627a593.pth", - "resnet50": "https://download.pytorch.org/models/resnet50-0676ba61.pth", - "resnet101": "https://download.pytorch.org/models/resnet101-63fe2227.pth", - "resnet152": "https://download.pytorch.org/models/resnet152-394f9c45.pth", - "resnext50_32x4d": "https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth", - "resnext101_32x8d": "https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth", - "wide_resnet50_2": "https://download.pytorch.org/models/wide_resnet50_2-95faca4d.pth", - "wide_resnet101_2": "https://download.pytorch.org/models/wide_resnet101_2-32ee1156.pth", - "shufflenetv2_x0.5": "https://download.pytorch.org/models/shufflenetv2_x0.5-f707e7126e.pth", - "shufflenetv2_x1.0": "https://download.pytorch.org/models/shufflenetv2_x1-5666bf0f80.pth", - "shufflenetv2_x1.5": null, - "shufflenetv2_x2.0": null, - "squeezenet1_0": "https://download.pytorch.org/models/squeezenet1_0-b66bff10.pth", - "squeezenet1_1": "https://download.pytorch.org/models/squeezenet1_1-b8a52dc0.pth", - "vgg11": "https://download.pytorch.org/models/vgg11-8a719046.pth", - "vgg13": "https://download.pytorch.org/models/vgg13-19584684.pth", - "vgg16": "https://download.pytorch.org/models/vgg16-397923af.pth", - "vgg19": "https://download.pytorch.org/models/vgg19-dcbb9e9d.pth", - "vgg11_bn": "https://download.pytorch.org/models/vgg11_bn-6002323d.pth", - "vgg13_bn": "https://download.pytorch.org/models/vgg13_bn-abd245e5.pth", - "vgg16_bn": "https://download.pytorch.org/models/vgg16_bn-6c64b313.pth", - "vgg19_bn": "https://download.pytorch.org/models/vgg19_bn-c79401a0.pth" -} diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/__init__.py deleted file mode 100644 index a12b79cef..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .deprecated_wrappers import Conv2d_deprecated as Conv2d -from .deprecated_wrappers import ConvTranspose2d_deprecated as ConvTranspose2d -from .deprecated_wrappers import Linear_deprecated as Linear -from .deprecated_wrappers import MaxPool2d_deprecated as MaxPool2d -from .focal_loss import (SigmoidFocalLoss, SoftmaxFocalLoss, - sigmoid_focal_loss, softmax_focal_loss) -from .sync_bn import SyncBatchNorm -from .cc_attention import CrissCrossAttention -from .point_sample import * -from .psa_mask import PSAMask, PSAMaskFunction -from .info import * \ No newline at end of file diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/cc_attention.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/cc_attention.py deleted file mode 100644 index 3fd83fcb9..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/cc_attention.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.cnn import PLUGIN_LAYERS, Scale - - -def NEG_INF_DIAG(n, device): - """Returns a diagonal matrix of size [n, n]. - - The diagonal are all "-inf". This is for avoiding calculating the - overlapped element in the Criss-Cross twice. - """ - return torch.diag(torch.tensor(float('-inf')).to(device).repeat(n), 0) - - -@PLUGIN_LAYERS.register_module() -class CrissCrossAttention(nn.Module): - """Criss-Cross Attention Module. - - .. note:: - Before v1.3.13, we use a CUDA op. Since v1.3.13, we switch - to a pure PyTorch and equivalent implementation. For more - details, please refer to https://github.com/open-mmlab/mmcv/pull/1201. - - Speed comparison for one forward pass - - - Input size: [2,512,97,97] - - Device: 1 NVIDIA GeForce RTX 2080 Ti - - +-----------------------+---------------+------------+---------------+ - | |PyTorch version|CUDA version|Relative speed | - +=======================+===============+============+===============+ - |with torch.no_grad() |0.00554402 s |0.0299619 s |5.4x | - +-----------------------+---------------+------------+---------------+ - |no with torch.no_grad()|0.00562803 s |0.0301349 s |5.4x | - +-----------------------+---------------+------------+---------------+ - - Args: - in_channels (int): Channels of the input feature map. - """ - - def __init__(self, in_channels): - super().__init__() - self.query_conv = nn.Conv2d(in_channels, in_channels // 8, 1) - self.key_conv = nn.Conv2d(in_channels, in_channels // 8, 1) - self.value_conv = nn.Conv2d(in_channels, in_channels, 1) - self.gamma = Scale(0.) - self.in_channels = in_channels - - def forward(self, x): - """forward function of Criss-Cross Attention. - - Args: - x (torch.Tensor): Input feature with the shape of - (batch_size, in_channels, height, width). - - Returns: - torch.Tensor: Output of the layer, with the shape of - (batch_size, in_channels, height, width) - """ - B, C, H, W = x.size() - query = self.query_conv(x) - key = self.key_conv(x) - value = self.value_conv(x) - energy_H = torch.einsum('bchw,bciw->bwhi', query, key) + NEG_INF_DIAG( - H, query.device) - energy_H = energy_H.transpose(1, 2) - energy_W = torch.einsum('bchw,bchj->bhwj', query, key) - attn = F.softmax( - torch.cat([energy_H, energy_W], dim=-1), dim=-1) # [B,H,W,(H+W)] - out = torch.einsum('bciw,bhwi->bchw', value, attn[..., :H]) - out += torch.einsum('bchj,bhwj->bchw', value, attn[..., H:]) - - out = self.gamma(out) + x - out = out.contiguous() - - return out - - def __repr__(self): - s = self.__class__.__name__ - s += f'(in_channels={self.in_channels})' - return s diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/README.md b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/README.md deleted file mode 100644 index 3bc020040..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/README.md +++ /dev/null @@ -1,170 +0,0 @@ -# Code Structure of CUDA operators - -This folder contains all non-python code for MMCV custom ops. Please follow the same architecture if you want to add new ops. - -## Directories Tree - -```folder -. -├── common -│ ├── box_iou_rotated_utils.hpp -│ ├── parrots_cpp_helper.hpp -│ ├── parrots_cuda_helper.hpp -│ ├── pytorch_cpp_helper.hpp -│ ├── pytorch_cuda_helper.hpp -│ ├── pytorch_device_registry.hpp -│   └── cuda -│   ├── common_cuda_helper.hpp -│   ├── parrots_cudawarpfunction.cuh -│   ├── ... -│   └── ops_cuda_kernel.cuh -├── onnxruntime -│   ├── onnxruntime_register.h -│   ├── onnxruntime_session_options_config_keys.h -│   ├── ort_mmcv_utils.h -│   ├── ... -│   ├── onnx_ops.h -│   └── cpu -│ ├── onnxruntime_register.cpp -│      ├── ... -│      └── onnx_ops_impl.cpp -├── parrots -│   ├── ... -│   ├── ops.cpp -│   ├── ops_parrots.cpp -│   └── ops_pytorch.h -├── pytorch -│   ├── info.cpp -│   ├── pybind.cpp -│   ├── ... -│   ├── ops.cpp -│   ├── cuda -│   │   ├── ... -│   │   └── ops_cuda.cu -│   └── cpu -│      ├── ... -│      └── ops.cpp -└── tensorrt - ├── trt_cuda_helper.cuh - ├── trt_plugin_helper.hpp - ├── trt_plugin.hpp - ├── trt_serialize.hpp - ├── ... - ├── trt_ops.hpp - └── plugins -    ├── trt_cuda_helper.cu -    ├── trt_plugin.cpp -    ├── ... -    ├── trt_ops.cpp -    └── trt_ops_kernel.cu -``` - -## Components - -- `common`: This directory contains all tools and shared codes. - - `cuda`: The cuda kernels which can be shared by all backends. **HIP** kernel is also here since they have similar syntax. -- `onnxruntime`: **ONNX Runtime** support for custom ops. - - `cpu`: CPU implementation of supported ops. -- `parrots`: **Parrots** is a deep learning frame for model training and inference. Parrots custom ops are placed in this directory. -- `pytorch`: **PyTorch** custom ops are supported by binding C++ to Python with **pybind11**. The ops implementation and binding codes are placed in this directory. - - `cuda`: This directory contains cuda kernel launchers, which feed memory pointers of tensor to the cuda kernel in `common/cuda`. The launchers provide c++ interface of cuda implementation of corresponding custom ops. - - `cpu`: This directory contain cpu implementations of corresponding custom ops. -- `tensorrt`: **TensorRT** support for custom ops. - - `plugins`: This directory contains the implementation of the supported custom ops. Some ops might also use shared cuda kernel in `common/cuda`. - -## How to add new PyTorch ops? - -1. (Optional) Add shared kernel in `common` to support special hardware platform. - - ```c++ - // src/common/cuda/new_ops_cuda_kernel.cuh - - template - __global__ void new_ops_forward_cuda_kernel(const T* input, T* output, ...) { - // forward here - } - - ``` - - Add cuda kernel launcher in `pytorch/cuda`. - - ```c++ - // src/pytorch/cuda - #include - - void NewOpsForwardCUDAKernelLauncher(Tensor input, Tensor output, ...){ - // initialize - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - ... - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "new_ops_forward_cuda_kernel", ([&] { - new_ops_forward_cuda_kernel - <<>>( - input.data_ptr(), output.data_ptr(),...); - })); - AT_CUDA_CHECK(cudaGetLastError()); - } - ``` - -2. Register implementation for different devices. - - ```c++ - // src/pytorch/cuda/cudabind.cpp - ... - - Tensor new_ops_forward_cuda(Tensor input, Tensor output, ...){ - // implement cuda forward here - // use `NewOpsForwardCUDAKernelLauncher` here - } - // declare interface here. - Tensor new_ops_forward_impl(Tensor input, Tensor output, ...); - // register the implementation for given device (CUDA here). - REGISTER_DEVICE_IMPL(new_ops_forward_impl, CUDA, new_ops_forward_cuda); - ``` - -3. Add ops implementation in `pytorch` directory. Select different implementations according to device type. - - ```c++ - // src/pytorch/new_ops.cpp - Tensor new_ops_forward_impl(Tensor input, Tensor output, ...){ - // dispatch the implementation according to the device type of input. - DISPATCH_DEVICE_IMPL(new_ops_forward_impl, input, output, ...); - } - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...){ - return new_ops_forward_impl(input, output, ...); - } - ``` - -4. Binding the implementation in `pytorch/pybind.cpp` - - ```c++ - // src/pytorch/pybind.cpp - - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...); - - ... - - // bind with pybind11 - m.def("new_ops_forward", &new_ops_forward, "new_ops_forward", - py::arg("input"), py::arg("output"), ...); - - ... - - ``` - -5. Build MMCV again. Enjoy new ops in python - - ```python - from ..utils import ext_loader - ext_module = ext_loader.load_ext('_ext', ['new_ops_forward']) - - ... - - ext_module.new_ops_forward(input, output, ...) - - ``` diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp deleted file mode 100644 index e18036bac..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef COMMON_CUDA_HELPER -#define COMMON_CUDA_HELPER - -#include - -#define CUDA_1D_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) - -#define CUDA_2D_KERNEL_LOOP(i, n, j, m) \ - for (size_t i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) \ - for (size_t j = blockIdx.y * blockDim.y + threadIdx.y; j < (m); \ - j += blockDim.y * gridDim.y) - -#define CUDA_2D_KERNEL_BLOCK_LOOP(i, n, j, m) \ - for (size_t i = blockIdx.x; i < (n); i += gridDim.x) \ - for (size_t j = blockIdx.y; j < (m); j += gridDim.y) - -#define THREADS_PER_BLOCK 512 - -inline int GET_BLOCKS(const int N, const int num_threads = THREADS_PER_BLOCK) { - int optimal_block_num = (N + num_threads - 1) / num_threads; - int max_block_num = 4096; - return std::min(optimal_block_num, max_block_num); -} - -template -__device__ T bilinear_interpolate(const T* input, const int height, - const int width, T y, T x, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) return 0; - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - int y_low = (int)y; - int x_low = (int)x; - int y_high; - int x_high; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - // do bilinear interpolation - T v1 = input[y_low * width + x_low]; - T v2 = input[y_low * width + x_high]; - T v3 = input[y_high * width + x_low]; - T v4 = input[y_high * width + x_high]; - T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - return val; -} - -template -__device__ void bilinear_interpolate_gradient( - const int height, const int width, T y, T x, T& w1, T& w2, T& w3, T& w4, - int& x_low, int& x_high, int& y_low, int& y_high, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - w1 = w2 = w3 = w4 = 0.; - x_low = x_high = y_low = y_high = -1; - return; - } - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - y_low = (int)y; - x_low = (int)x; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - - // reference in forward - // T v1 = input[y_low * width + x_low]; - // T v2 = input[y_low * width + x_high]; - // T v3 = input[y_high * width + x_low]; - // T v4 = input[y_high * width + x_high]; - // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - return; -} -#endif // COMMON_CUDA_HELPER diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh deleted file mode 100644 index 5d946686b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef PSAMASK_CUDA_KERNEL_CUH -#define PSAMASK_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -// CUDA: grid stride looping -#ifndef CUDA_KERNEL_LOOP -#define CUDA_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) -#endif - -template -__global__ void psamask_collect_forward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* mask_data, T* buffer_data) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - buffer_data[(n * h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)) * - h_feature * w_feature + - h * w_feature + w] = mask_data - [((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + h) * - w_feature + - w]; - } - } - } -} - -template -__global__ void psamask_distribute_forward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* mask_data, T* buffer_data) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - buffer_data[(n * h_feature * w_feature + h * w_feature + w) * - h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)] = mask_data - [((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + h) * - w_feature + - w]; - } - } - } -} - -template -__global__ void psamask_collect_backward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* buffer_diff, T* mask_diff) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - mask_diff[((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + - h) * - w_feature + - w] = buffer_diff[(n * h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)) * - h_feature * w_feature + - h * w_feature + w]; - } - } - } -} - -template -__global__ void psamask_distribute_backward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* buffer_diff, T* mask_diff) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - mask_diff[((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + - h) * - w_feature + - w] = - buffer_diff[(n * h_feature * w_feature + h * w_feature + w) * - h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)]; - } - } - } -} - -#endif // PSAMASK_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 1eb5f8fcc..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sigmoid_focal_loss_forward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* output, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + expf(-input[index])); - - // (1 - p)**gamma * log(p) - T term_p = pow(((T)1. - p), gamma) * log(max(p, (T)FLT_MIN)); - // p**gamma * log(1 - p) - T term_n = pow(p, gamma) * log(max((T)1. - p, (T)FLT_MIN)); - - output[index] = (T)0.; - output[index] += -flag_p * alpha * term_p; - output[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - output[index] *= weight[t]; - } - } -} - -template -__global__ void sigmoid_focal_loss_backward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* grad_input, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + exp(-input[index])); - - // (1 - p)**gamma * (1 - p - gamma*p*log(p)) - T term_p = pow(((T)1. - p), gamma) * - ((T)1. - p - (gamma * p * log(max(p, (T)FLT_MIN)))); - // p**gamma * (gamma * (1 - p) * log(1 - p) - p) - T term_n = pow(p, gamma) * - (gamma * ((T)1. - p) * log(max((T)1. - p, (T)FLT_MIN)) - p); - - grad_input[index] = (T)0.; - grad_input[index] += -flag_p * alpha * term_p; - grad_input[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - grad_input[index] *= weight[t]; - } - } -} - -#endif // SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 631b2c617..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void softmax_focal_loss_forward_cuda_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* output, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - output[index] = - -alpha * pow((T)1. - pred, gamma) * log(max(pred, (T)FLT_MIN)); - } else { - output[index] = 0; - } - if (weight != NULL) { - output[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda1_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* buff, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - buff[index] = alpha * (-pow((T)1. - pred, gamma) + - gamma * pow((T)1. - pred, gamma - 1) * pred * - log(max(pred, (T)FLT_MIN))); - } else { - buff[index] = 0; - } - if (weight != NULL) { - buff[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda2_kernel( - const int nthreads, const T* softmax, const int64_t* target, const T* buff, - T* grad_input, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - int64_t label = target[n]; - - if (label >= 0) { - T flag = (label == c ? (T)1. : (T)0.); - grad_input[index] = buff[n] * (flag - softmax[index]); - } else { - grad_input[index] = 0; - } - } -} - -#endif // SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh deleted file mode 100644 index 4ec6a4668..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SYNCBN_CUDA_KERNEL_CUH -#define SYNCBN_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sync_bn_forward_mean_cuda_kernel(const T *input, float *mean, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += input[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_mean_cuda_kernel(const phalf *input, - float *mean, int num, - int channels, int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += static_cast(input[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_var_cuda_kernel(const T *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = input[index] - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_var_cuda_kernel(const phalf *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = static_cast(input[index]) - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_output_cuda_kernel( - const T *input, const float *mean, const float *var, float *running_mean, - float *running_var, const float *weight, const float *bias, float *norm, - float *std, T *output, int num, int channels, int spatial, float eps, - float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = (input[index] - mean_value) / std_value; - output[index] = norm[index] * weight_value + bias_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - (input[index] - mean_value) / std_value * weight_value + bias_value; - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = norm[index] = (input[index] - mean_value) / std_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = (input[index] - mean_value) / std_value; - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template <> -__global__ void sync_bn_forward_output_cuda_kernel( - const phalf *input, const float *mean, const float *var, - float *running_mean, float *running_var, const float *weight, - const float *bias, float *norm, float *std, phalf *output, int num, - int channels, int spatial, float eps, float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = - static_cast(norm[index] * weight_value + bias_value); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - static_cast((static_cast(input[index]) - mean_value) / - std_value * weight_value + - bias_value); - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = static_cast(norm[index]); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = static_cast( - (static_cast(input[index]) - mean_value) / std_value); - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template -__global__ void sync_bn_backward_param_cuda_kernel(const T *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += grad_output[index] * norm[index]; - buffer2[tid] += grad_output[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template <> -__global__ void sync_bn_backward_param_cuda_kernel(const phalf *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += static_cast(grad_output[index]) * norm[index]; - buffer2[tid] += static_cast(grad_output[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const T *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, T *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = - weight[c] * - (grad_output[index] - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]; - } -} - -template <> -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const phalf *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, phalf *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = static_cast( - weight[c] * - (static_cast(grad_output[index]) - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]); - } -} - -#endif // SYNCBN_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp deleted file mode 100644 index f68e87405..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef PYTORCH_CPP_HELPER -#define PYTORCH_CPP_HELPER -#include - -#include - -using namespace at; - -#define CHECK_CUDA(x) \ - TORCH_CHECK(x.device().is_cuda(), #x " must be a CUDA tensor") -#define CHECK_MLU(x) \ - TORCH_CHECK(x.device().type() == at::kMLU, #x " must be a MLU tensor") -#define CHECK_CPU(x) \ - TORCH_CHECK(x.device().type() == at::kCPU, #x " must be a CPU tensor") -#define CHECK_CONTIGUOUS(x) \ - TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") -#define CHECK_CUDA_INPUT(x) \ - CHECK_CUDA(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_MLU_INPUT(x) \ - CHECK_MLU(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_CPU_INPUT(x) \ - CHECK_CPU(x); \ - CHECK_CONTIGUOUS(x) - -#endif // PYTORCH_CPP_HELPER diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp deleted file mode 100644 index 9869b535f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PYTORCH_CUDA_HELPER -#define PYTORCH_CUDA_HELPER - -#include -#include -#include - -#include -#include - -#include "common_cuda_helper.hpp" - -using at::Half; -using at::Tensor; -using phalf = at::Half; - -#define __PHALF(x) (x) - -#endif // PYTORCH_CUDA_HELPER diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp deleted file mode 100644 index 2a32b7270..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef PYTORCH_DEVICE_REGISTRY_H -#define PYTORCH_DEVICE_REGISTRY_H - -// Using is recommended in the official documentation in -// https://pytorch.org/tutorials/advanced/cpp_extension.html#writing-the-c-op. -// However, we use for compatibility with CUDA 9.0 -// Read https://github.com/pytorch/extension-cpp/issues/35 for more details. -#include - -#include -#include -#include -#include - -inline std::string GetDeviceStr(const at::Device& device) { - std::string str = DeviceTypeName(device.type(), true); - if (device.has_index()) { - str.push_back(':'); - str.append(std::to_string(device.index())); - } - return str; -} - -// Registry -template -class DeviceRegistry; - -template -class DeviceRegistry { - public: - using FunctionType = Ret (*)(Args...); - static const int MAX_DEVICE_TYPES = - int8_t(at::DeviceType::COMPILE_TIME_MAX_DEVICE_TYPES); - - void Register(at::DeviceType device, FunctionType function) { - funcs_[int8_t(device)] = function; - } - - FunctionType Find(at::DeviceType device) const { - return funcs_[int8_t(device)]; - } - - static DeviceRegistry& instance() { - static DeviceRegistry inst; - return inst; - } - - private: - DeviceRegistry() { - for (size_t i = 0; i < MAX_DEVICE_TYPES; ++i) { - funcs_[i] = nullptr; - } - }; - FunctionType funcs_[MAX_DEVICE_TYPES]; -}; - -// get device of first tensor param - -template , at::Tensor>::value, - bool> = true> -at::Device GetFirstTensorDevice(T&& t, Args&&... args) { - return std::forward(t).device(); -} -template , at::Tensor>::value, - bool> = true> -at::Device GetFirstTensorDevice(T&& t, Args&&... args) { - return GetFirstTensorDevice(std::forward(args)...); -} - -// check device consistency - -inline std::pair CheckDeviceConsistency( - const at::Device& device, int index) { - return {index, device}; -} - -template , at::Tensor>::value, - bool> = true> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args); - -template , at::Tensor>::value, - bool> = true> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args) { - auto new_device = std::forward(t).device(); - if (new_device.type() != device.type() || - new_device.index() != device.index()) { - return {index, new_device}; - } - return CheckDeviceConsistency(device, index + 1, std::forward(args)...); -} - -template < - typename T, typename... Args, - std::enable_if_t, at::Tensor>::value, bool>> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args) { - return CheckDeviceConsistency(device, index + 1, std::forward(args)...); -} - -// dispatch - -template -auto Dispatch(const R& registry, const char* name, Args&&... args) { - auto device = GetFirstTensorDevice(std::forward(args)...); - auto inconsist = - CheckDeviceConsistency(device, 0, std::forward(args)...); - TORCH_CHECK(inconsist.first >= int(sizeof...(Args)), name, ": at param ", - inconsist.first, - ", inconsistent device: ", GetDeviceStr(inconsist.second).c_str(), - " vs ", GetDeviceStr(device).c_str(), "\n") - auto f_ptr = registry.Find(device.type()); - TORCH_CHECK(f_ptr != nullptr, name, ": implementation for device ", - GetDeviceStr(device).c_str(), " not found.\n") - return f_ptr(std::forward(args)...); -} - -// helper macro - -#define DEVICE_REGISTRY(key) DeviceRegistry::instance() - -#define REGISTER_DEVICE_IMPL(key, device, value) \ - struct key##_##device##_registerer { \ - key##_##device##_registerer() { \ - DEVICE_REGISTRY(key).Register(at::k##device, value); \ - } \ - }; \ - static key##_##device##_registerer _##key##_##device##_registerer; - -#define DISPATCH_DEVICE_IMPL(key, ...) \ - Dispatch(DEVICE_REGISTRY(key), #key, __VA_ARGS__) - -#endif // PYTORCH_DEVICE_REGISTRY diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp deleted file mode 100644 index dc376bb51..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp +++ /dev/null @@ -1,210 +0,0 @@ -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha); - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha); - -void sigmoid_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SigmoidFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void sigmoid_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - SigmoidFocalLossBackwardCUDAKernelLauncher(input, target, weight, grad_input, - gamma, alpha); -} - -void softmax_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SoftmaxFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void softmax_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - SoftmaxFocalLossBackwardCUDAKernelLauncher(input, target, weight, buff, - grad_input, gamma, alpha); -} - -void sigmoid_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha); - -void softmax_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha); - -REGISTER_DEVICE_IMPL(sigmoid_focal_loss_forward_impl, CUDA, - sigmoid_focal_loss_forward_cuda); -REGISTER_DEVICE_IMPL(sigmoid_focal_loss_backward_impl, CUDA, - sigmoid_focal_loss_backward_cuda); -REGISTER_DEVICE_IMPL(softmax_focal_loss_forward_impl, CUDA, - softmax_focal_loss_forward_cuda); -REGISTER_DEVICE_IMPL(softmax_focal_loss_backward_impl, CUDA, - softmax_focal_loss_backward_cuda); - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean); - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var); - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size); - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias); - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -void sync_bn_forward_mean_cuda(const Tensor input, Tensor mean) { - SyncBNForwardMeanCUDAKernelLauncher(input, mean); -} - -void sync_bn_forward_var_cuda(const Tensor input, const Tensor mean, - Tensor var) { - SyncBNForwardVarCUDAKernelLauncher(input, mean, var); -} - -void sync_bn_forward_output_cuda(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - SyncBNForwardOutputCUDAKernelLauncher(input, mean, var, running_mean, - running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_cuda(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - SyncBNBackwardParamCUDAKernelLauncher(grad_output, norm, grad_weight, - grad_bias); -} - -void sync_bn_backward_data_cuda(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - SyncBNBackwardDataCUDAKernelLauncher(grad_output, weight, grad_weight, - grad_bias, norm, std, grad_input); -} - -void sync_bn_forward_mean_impl(const Tensor input, Tensor mean); - -void sync_bn_forward_var_impl(const Tensor input, const Tensor mean, - Tensor var); - -void sync_bn_forward_output_impl(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param_impl(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data_impl(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input); - -REGISTER_DEVICE_IMPL(sync_bn_forward_mean_impl, CUDA, - sync_bn_forward_mean_cuda); -REGISTER_DEVICE_IMPL(sync_bn_forward_var_impl, CUDA, sync_bn_forward_var_cuda); -REGISTER_DEVICE_IMPL(sync_bn_forward_output_impl, CUDA, - sync_bn_forward_output_cuda); -REGISTER_DEVICE_IMPL(sync_bn_backward_param_impl, CUDA, - sync_bn_backward_param_cuda); -REGISTER_DEVICE_IMPL(sync_bn_backward_data_impl, CUDA, - sync_bn_backward_data_cuda); - - - -void PSAMaskForwardCUDAKernelLauncher(const int psa_type, const Tensor input, - Tensor output, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, - const int half_w_mask); - -void PSAMaskBackwardCUDAKernelLauncher( - const int psa_type, const Tensor grad_output, Tensor grad_input, - const int num_, const int h_feature, const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, const int half_w_mask); - -void psamask_forward_cuda(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask) { - PSAMaskForwardCUDAKernelLauncher(psa_type, input, output, num_, h_feature, - w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_backward_cuda(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - PSAMaskBackwardCUDAKernelLauncher(psa_type, grad_output, grad_input, num_, - h_feature, w_feature, h_mask, w_mask, - half_h_mask, half_w_mask); -} - -void psamask_forward_impl(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask); - -void psamask_backward_impl(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask); -REGISTER_DEVICE_IMPL(psamask_forward_impl, CUDA, psamask_forward_cuda); -REGISTER_DEVICE_IMPL(psamask_backward_impl, CUDA, psamask_backward_cuda); diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu deleted file mode 100644 index cb899f954..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sigmoid_focal_loss_cuda_kernel.cuh" -#include "softmax_focal_loss_cuda_kernel.cuh" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = input.size(1); - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_forward_cuda_kernel", [&] { - sigmoid_focal_loss_forward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha) { - int output_size = grad_input.numel(); - int num_classes = input.size(1); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_backward_cuda_kernel", [&] { - sigmoid_focal_loss_backward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - grad_input.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = softmax.size(1); - - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(softmax.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - softmax.scalar_type(), "softmax_focal_loss_forward_cuda_kernel", [&] { - softmax_focal_loss_forward_cuda_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha) { - int num_classes = softmax.size(1); - - int output_size = buff.numel(); - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda1_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda1_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - buff.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); - - output_size = grad_input.numel(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda2_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda2_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), buff.data_ptr(), - grad_input.data_ptr(), num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu deleted file mode 100644 index a0bdfa60c..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// Modified from -// https://github.com/hszhao/semseg/blob/master/lib/psa/src - -#include - -#include "psamask_cuda_kernel.cuh" -#include "pytorch_cuda_helper.hpp" - -void PSAMaskForwardCUDAKernelLauncher(const int psa_type, const Tensor input, - Tensor output, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, - const int half_w_mask) { - int nthreads = num_ * h_feature * w_feature; - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - if (psa_type == 0) - AT_DISPATCH_FLOATING_TYPES( - input.scalar_type(), "psamask_collect_forward_cuda", [&] { - psamask_collect_forward_cuda<<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, input.data_ptr(), - output.data_ptr()); - }); - else - AT_DISPATCH_FLOATING_TYPES( - input.scalar_type(), "psamask_distribute_forward_cuda", [&] { - psamask_distribute_forward_cuda - <<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, input.data_ptr(), - output.data_ptr()); - }); -} - -void PSAMaskBackwardCUDAKernelLauncher( - const int psa_type, const Tensor grad_output, Tensor grad_input, - const int num_, const int h_feature, const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, const int half_w_mask) { - int nthreads = num_ * h_feature * w_feature; - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - if (psa_type == 0) - AT_DISPATCH_FLOATING_TYPES( - grad_input.scalar_type(), "psamask_collect_backward_cuda", [&] { - psamask_collect_backward_cuda<<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, grad_output.data_ptr(), - grad_input.data_ptr()); - }); - else - AT_DISPATCH_FLOATING_TYPES( - grad_input.scalar_type(), "psamask_distribute_backward_cuda", [&] { - psamask_distribute_backward_cuda - <<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, grad_output.data_ptr(), - grad_input.data_ptr()); - }); -} diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu deleted file mode 100644 index 657c81701..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sync_bn_cuda_kernel.cuh" - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_mean_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_var_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), num, channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_output_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), running_mean.data_ptr(), - running_var.data_ptr(), weight.data_ptr(), - bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), output.data_ptr(), num, - channels, spatial, eps, momentum, group_size); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias) { - int num = grad_output.size(0); - int channels = grad_output.size(1); - int spatial = grad_output.size(2); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_param_cuda_kernel", [&] { - sync_bn_backward_param_cuda_kernel - <<>>( - grad_output.data_ptr(), norm.data_ptr(), - grad_weight.data_ptr(), grad_bias.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - int output_size = grad_input.numel(); - int num = grad_input.size(0); - int channels = grad_input.size(1); - int spatial = grad_input.size(2); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_data_cuda_kernel", [&] { - sync_bn_backward_data_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - weight.data_ptr(), grad_weight.data_ptr(), - grad_bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), grad_input.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp deleted file mode 100644 index ed0e21865..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void sigmoid_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(sigmoid_focal_loss_forward_impl, input, target, weight, - output, gamma, alpha); -} - -void sigmoid_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(sigmoid_focal_loss_backward_impl, input, target, weight, - grad_input, gamma, alpha); -} - -void softmax_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(softmax_focal_loss_forward_impl, input, target, weight, - output, gamma, alpha); -} - -void softmax_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - DISPATCH_DEVICE_IMPL(softmax_focal_loss_backward_impl, input, target, weight, - buff, grad_input, gamma, alpha); -} - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - sigmoid_focal_loss_forward_impl(input, target, weight, output, gamma, alpha); -} - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha) { - sigmoid_focal_loss_backward_impl(input, target, weight, grad_input, gamma, - alpha); -} - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - softmax_focal_loss_forward_impl(input, target, weight, output, gamma, alpha); -} - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha) { - softmax_focal_loss_backward_impl(input, target, weight, buff, grad_input, - gamma, alpha); -} diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp deleted file mode 100644 index a08d227d4..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/info.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// modified from -// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/vision.cpp -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF -#include -int get_cudart_version() { return CUDART_VERSION; } -#endif -#endif - -std::string get_compiling_cuda_version() { -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF - std::ostringstream oss; - // copied from - // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/cuda/detail/CUDAHooks.cpp#L231 - auto printCudaStyleVersion = [&](int v) { - oss << (v / 1000) << "." << (v / 10 % 100); - if (v % 10 != 0) { - oss << "." << (v % 10); - } - }; - printCudaStyleVersion(get_cudart_version()); - return oss.str(); -#else - return std::string("rocm not available"); -#endif -#else - return std::string("not available"); -#endif -} - -// similar to -// https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/Version.cpp -std::string get_compiler_version() { - std::ostringstream ss; -#if defined(__GNUC__) -#ifndef __clang__ - { ss << "GCC " << __GNUC__ << "." << __GNUC_MINOR__; } -#endif -#endif - -#if defined(__clang_major__) - { - ss << "clang " << __clang_major__ << "." << __clang_minor__ << "." - << __clang_patchlevel__; - } -#endif - -#if defined(_MSC_VER) - { ss << "MSVC " << _MSC_FULL_VER; } -#endif - return ss.str(); -} diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp deleted file mode 100644 index 6064c9ba5..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// Modified from -// https://github.com/hszhao/semseg/blob/master/lib/psa/src -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void psamask_forward_impl(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask) { - DISPATCH_DEVICE_IMPL(psamask_forward_impl, psa_type, input, output, num_, - h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_backward_impl(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - DISPATCH_DEVICE_IMPL(psamask_backward_impl, psa_type, grad_output, grad_input, - num_, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_forward(const Tensor input, Tensor output, const int psa_type, - const int num_, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask) { - psamask_forward_impl(psa_type, input, output, num_, h_feature, w_feature, - h_mask, w_mask, half_h_mask, half_w_mask); -} - -void psamask_backward(Tensor grad_output, const Tensor grad_input, - const int psa_type, const int num_, const int h_feature, - const int w_feature, const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - psamask_backward_impl(psa_type, grad_output, grad_input, num_, h_feature, - w_feature, h_mask, w_mask, half_h_mask, half_w_mask); -} diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp deleted file mode 100644 index 1e99174d8..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include - -#include "pytorch_cpp_helper.hpp" - -std::string get_compiler_version(); -std::string get_compiling_cuda_version(); - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha); - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha); - -void sync_bn_forward_mean(const Tensor input, Tensor mean); - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var); - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - - -void psamask_forward(const Tensor input, Tensor output, const int psa_type, - const int num_, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask); - -void psamask_backward(Tensor grad_output, const Tensor grad_input, - const int psa_type, const int num_, const int h_feature, - const int w_feature, const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask); - - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("get_compiler_version", &get_compiler_version, "get_compiler_version"); - m.def("get_compiling_cuda_version", &get_compiling_cuda_version, - "get_compiling_cuda_version"); - - - m.def("sigmoid_focal_loss_forward", &sigmoid_focal_loss_forward, - "sigmoid_focal_loss_forward ", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("sigmoid_focal_loss_backward", &sigmoid_focal_loss_backward, - "sigmoid_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("grad_input"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_forward", &softmax_focal_loss_forward, - "softmax_focal_loss_forward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_backward", &softmax_focal_loss_backward, - "softmax_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("buff"), py::arg("grad_input"), - py::arg("gamma"), py::arg("alpha")); - - // SyncBN - m.def("sync_bn_forward_mean", &sync_bn_forward_mean, "sync_bn forward_mean", - py::arg("input"), py::arg("mean")); - m.def("sync_bn_forward_var", &sync_bn_forward_var, "sync_bn forward_var", - py::arg("input"), py::arg("mean"), py::arg("var")); - m.def("sync_bn_forward_output", &sync_bn_forward_output, - "sync_bn forward_output", py::arg("input"), py::arg("mean"), - py::arg("var"), py::arg("weight"), py::arg("bias"), - py::arg("running_mean"), py::arg("running_var"), py::arg("norm"), - py::arg("std"), py::arg("output"), py::arg("eps"), py::arg("momentum"), - py::arg("group_size")); - m.def("sync_bn_backward_param", &sync_bn_backward_param, - "sync_bn backward_param", py::arg("grad_output"), py::arg("norm"), - py::arg("grad_weight"), py::arg("grad_bias")); - m.def("sync_bn_backward_data", &sync_bn_backward_data, - "sync_bn backward_data", py::arg("grad_output"), py::arg("weight"), - py::arg("grad_weight"), py::arg("grad_bias"), py::arg("norm"), - py::arg("std"), py::arg("grad_input")); - - // PASMask - m.def("psamask_forward", &psamask_forward, "PSAMASK forward (CPU/CUDA)", - py::arg("input"), py::arg("output"), py::arg("psa_type"), - py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), - py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), - py::arg("half_w_mask")); - m.def("psamask_backward", &psamask_backward, "PSAMASK backward (CPU/CUDA)", - py::arg("grad_output"), py::arg("grad_input"), py::arg("psa_type"), - py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), - py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), - py::arg("half_w_mask")); -} diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp deleted file mode 100644 index fd5a51327..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void sync_bn_forward_mean_impl(const Tensor input, Tensor mean) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_mean_impl, input, mean); -} - -void sync_bn_forward_var_impl(const Tensor input, const Tensor mean, - Tensor var) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_var_impl, input, mean, var); -} - -void sync_bn_forward_output_impl(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_output_impl, input, mean, var, - running_mean, running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_impl(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - DISPATCH_DEVICE_IMPL(sync_bn_backward_param_impl, grad_output, norm, - grad_weight, grad_bias); -} - -void sync_bn_backward_data_impl(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - DISPATCH_DEVICE_IMPL(sync_bn_backward_data_impl, grad_output, weight, - grad_weight, grad_bias, norm, std, grad_input); -} - -void sync_bn_forward_mean(const Tensor input, Tensor mean) { - sync_bn_forward_mean_impl(input, mean); -} - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var) { - sync_bn_forward_var_impl(input, mean, var); -} - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - sync_bn_forward_output_impl(input, mean, var, running_mean, running_var, - weight, bias, norm, std, output, eps, momentum, - group_size); -} - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - sync_bn_backward_param_impl(grad_output, norm, grad_weight, grad_bias); -} - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - sync_bn_backward_data_impl(grad_output, weight, grad_weight, grad_bias, norm, - std, grad_input); -} diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/deprecated_wrappers.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/deprecated_wrappers.py deleted file mode 100644 index 629a8033f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/deprecated_wrappers.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# This file is for backward compatibility. -# Module wrappers for empty tensor have been moved to mmcv.cnn.bricks. -import warnings - -from ..cnn.bricks.wrappers import Conv2d, ConvTranspose2d, Linear, MaxPool2d - - -class Conv2d_deprecated(Conv2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Conv2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) - - -class ConvTranspose2d_deprecated(ConvTranspose2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing ConvTranspose2d wrapper from "mmcv.ops" will be ' - 'deprecated in the future. Please import them from "mmcv.cnn" ' - 'instead', DeprecationWarning) - - -class MaxPool2d_deprecated(MaxPool2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing MaxPool2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) - - -class Linear_deprecated(Linear): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Linear wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/focal_loss.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/focal_loss.py deleted file mode 100644 index 805860516..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/focal_loss.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sigmoid_focal_loss_forward', 'sigmoid_focal_loss_backward', - 'softmax_focal_loss_forward', 'softmax_focal_loss_backward' -]) - - -class SigmoidFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSigmoidFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance( - target, (torch.Tensor, torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - output = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_forward( - input, target, weight, output, gamma=ctx.gamma, alpha=ctx.alpha) - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input, target, weight) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - input, target, weight = ctx.saved_tensors - - grad_input = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_backward( - input, - target, - weight, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input.size(0) - return grad_input, None, None, None, None, None - - -sigmoid_focal_loss = SigmoidFocalLossFunction.apply - - -class SigmoidFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SigmoidFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return sigmoid_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s - - -class SoftmaxFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSoftmaxFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - channel_stats, _ = torch.max(input, dim=1) - input_softmax = input - channel_stats.unsqueeze(1).expand_as(input) - input_softmax.exp_() - - channel_stats = input_softmax.sum(dim=1) - input_softmax /= channel_stats.unsqueeze(1).expand_as(input) - - output = input.new_zeros(input.size(0)) - ext_module.softmax_focal_loss_forward( - input_softmax, - target, - weight, - output, - gamma=ctx.gamma, - alpha=ctx.alpha) - - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input_softmax, target, weight) - return output - - @staticmethod - def backward(ctx, grad_output): - input_softmax, target, weight = ctx.saved_tensors - buff = input_softmax.new_zeros(input_softmax.size(0)) - grad_input = input_softmax.new_zeros(input_softmax.size()) - - ext_module.softmax_focal_loss_backward( - input_softmax, - target, - weight, - buff, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input_softmax.size(0) - return grad_input, None, None, None, None, None - - -softmax_focal_loss = SoftmaxFocalLossFunction.apply - - -class SoftmaxFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SoftmaxFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return softmax_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/info.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/info.py deleted file mode 100644 index 29f2e5598..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/info.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os - -import torch - -if torch.__version__ == 'parrots': - import parrots - - def get_compiler_version(): - return 'GCC ' + parrots.version.compiler - - def get_compiling_cuda_version(): - return parrots.version.cuda -else: - from ..utils import ext_loader - ext_module = ext_loader.load_ext( - '_ext', ['get_compiler_version', 'get_compiling_cuda_version']) - - def get_compiler_version(): - return ext_module.get_compiler_version() - - def get_compiling_cuda_version(): - return ext_module.get_compiling_cuda_version() - - -def get_onnxruntime_op_path(): - wildcard = os.path.join( - os.path.abspath(os.path.dirname(os.path.dirname(__file__))), - '_ext_ort.*.so') - - paths = glob.glob(wildcard) - if len(paths) > 0: - return paths[0] - else: - return '' diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/point_sample.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/point_sample.py deleted file mode 100644 index 9a70b28e2..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/point_sample.py +++ /dev/null @@ -1,346 +0,0 @@ -# Modified from https://github.com/facebookresearch/detectron2/tree/master/projects/PointRend # noqa - -from os import path as osp - -import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.nn.modules.utils import _pair -from torch.onnx.operators import shape_as_tensor - - -def bilinear_grid_sample(im, grid, align_corners=False): - """Given an input and a flow-field grid, computes the output using input - values and pixel locations from grid. Supported only bilinear interpolation - method to sample the input pixels. - - Args: - im (torch.Tensor): Input feature map, shape (N, C, H, W) - grid (torch.Tensor): Point coordinates, shape (N, Hg, Wg, 2) - align_corners {bool}: If set to True, the extrema (-1 and 1) are - considered as referring to the center points of the input’s - corner pixels. If set to False, they are instead considered as - referring to the corner points of the input’s corner pixels, - making the sampling more resolution agnostic. - - Returns: - torch.Tensor: A tensor with sampled points, shape (N, C, Hg, Wg) - """ - n, c, h, w = im.shape - gn, gh, gw, _ = grid.shape - assert n == gn - - x = grid[:, :, :, 0] - y = grid[:, :, :, 1] - - if align_corners: - x = ((x + 1) / 2) * (w - 1) - y = ((y + 1) / 2) * (h - 1) - else: - x = ((x + 1) * w - 1) / 2 - y = ((y + 1) * h - 1) / 2 - - x = x.view(n, -1) - y = y.view(n, -1) - - x0 = torch.floor(x).long() - y0 = torch.floor(y).long() - x1 = x0 + 1 - y1 = y0 + 1 - - wa = ((x1 - x) * (y1 - y)).unsqueeze(1) - wb = ((x1 - x) * (y - y0)).unsqueeze(1) - wc = ((x - x0) * (y1 - y)).unsqueeze(1) - wd = ((x - x0) * (y - y0)).unsqueeze(1) - - # Apply default for grid_sample function zero padding - im_padded = F.pad(im, pad=[1, 1, 1, 1], mode='constant', value=0) - padded_h = h + 2 - padded_w = w + 2 - # save points positions after padding - x0, x1, y0, y1 = x0 + 1, x1 + 1, y0 + 1, y1 + 1 - - # Clip coordinates to padded image size - x0 = torch.where(x0 < 0, torch.tensor(0), x0) - x0 = torch.where(x0 > padded_w - 1, torch.tensor(padded_w - 1), x0) - x1 = torch.where(x1 < 0, torch.tensor(0), x1) - x1 = torch.where(x1 > padded_w - 1, torch.tensor(padded_w - 1), x1) - y0 = torch.where(y0 < 0, torch.tensor(0), y0) - y0 = torch.where(y0 > padded_h - 1, torch.tensor(padded_h - 1), y0) - y1 = torch.where(y1 < 0, torch.tensor(0), y1) - y1 = torch.where(y1 > padded_h - 1, torch.tensor(padded_h - 1), y1) - - im_padded = im_padded.view(n, c, -1) - - x0_y0 = (x0 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x0_y1 = (x0 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y0 = (x1 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y1 = (x1 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - - Ia = torch.gather(im_padded, 2, x0_y0) - Ib = torch.gather(im_padded, 2, x0_y1) - Ic = torch.gather(im_padded, 2, x1_y0) - Id = torch.gather(im_padded, 2, x1_y1) - - return (Ia * wa + Ib * wb + Ic * wc + Id * wd).reshape(n, c, gh, gw) - - -def is_in_onnx_export_without_custom_ops(): - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - return torch.onnx.is_in_onnx_export( - ) and not osp.exists(ort_custom_op_path) - - -def normalize(grid): - """Normalize input grid from [-1, 1] to [0, 1] - - Args: - grid (torch.Tensor): The grid to be normalize, range [-1, 1]. - - Returns: - torch.Tensor: Normalized grid, range [0, 1]. - """ - - return (grid + 1.0) / 2.0 - - -def denormalize(grid): - """Denormalize input grid from range [0, 1] to [-1, 1] - - Args: - grid (torch.Tensor): The grid to be denormalize, range [0, 1]. - - Returns: - torch.Tensor: Denormalized grid, range [-1, 1]. - """ - - return grid * 2.0 - 1.0 - - -def generate_grid(num_grid, size, device): - """Generate regular square grid of points in [0, 1] x [0, 1] coordinate - space. - - Args: - num_grid (int): The number of grids to sample, one for each region. - size (tuple[int, int]): The side size of the regular grid. - device (torch.device): Desired device of returned tensor. - - Returns: - torch.Tensor: A tensor of shape (num_grid, size[0]*size[1], 2) that - contains coordinates for the regular grids. - """ - - affine_trans = torch.tensor([[[1., 0., 0.], [0., 1., 0.]]], device=device) - grid = F.affine_grid( - affine_trans, torch.Size((1, 1, *size)), align_corners=False) - grid = normalize(grid) - return grid.view(1, -1, 2).expand(num_grid, -1, -1) - - -def rel_roi_point_to_abs_img_point(rois, rel_roi_points): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (torch.Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (torch.Tensor): Point coordinates inside RoI, relative - to RoI, location, range (0, 1), shape (N, P, 2) - Returns: - torch.Tensor: Image based absolute point coordinates, shape (N, P, 2) - """ - - with torch.no_grad(): - assert rel_roi_points.size(0) == rois.size(0) - assert rois.dim() == 2 - assert rel_roi_points.dim() == 3 - assert rel_roi_points.size(2) == 2 - # remove batch idx - if rois.size(1) == 5: - rois = rois[:, 1:] - abs_img_points = rel_roi_points.clone() - # To avoid an error during exporting to onnx use independent - # variables instead inplace computation - xs = abs_img_points[:, :, 0] * (rois[:, None, 2] - rois[:, None, 0]) - ys = abs_img_points[:, :, 1] * (rois[:, None, 3] - rois[:, None, 1]) - xs += rois[:, None, 0] - ys += rois[:, None, 1] - abs_img_points = torch.stack([xs, ys], dim=2) - return abs_img_points - - -def get_shape_from_feature_map(x): - """Get spatial resolution of input feature map considering exporting to - onnx mode. - - Args: - x (torch.Tensor): Input tensor, shape (N, C, H, W) - - Returns: - torch.Tensor: Spatial resolution (width, height), shape (1, 1, 2) - """ - if torch.onnx.is_in_onnx_export(): - img_shape = shape_as_tensor(x)[2:].flip(0).view(1, 1, 2).to( - x.device).float() - else: - img_shape = torch.tensor(x.shape[2:]).flip(0).view(1, 1, 2).to( - x.device).float() - return img_shape - - -def abs_img_point_to_rel_img_point(abs_img_points, img, spatial_scale=1.): - """Convert image based absolute point coordinates to image based relative - coordinates for sampling. - - Args: - abs_img_points (torch.Tensor): Image based absolute point coordinates, - shape (N, P, 2) - img (tuple or torch.Tensor): (height, width) of image or feature map. - spatial_scale (float, optional): Scale points by this factor. - Default: 1. - - Returns: - Tensor: Image based relative point coordinates for sampling, shape - (N, P, 2). - """ - - assert (isinstance(img, tuple) and len(img) == 2) or \ - (isinstance(img, torch.Tensor) and len(img.shape) == 4) - - if isinstance(img, tuple): - h, w = img - scale = torch.tensor([w, h], - dtype=torch.float, - device=abs_img_points.device) - scale = scale.view(1, 1, 2) - else: - scale = get_shape_from_feature_map(img) - - return abs_img_points / scale * spatial_scale - - -def rel_roi_point_to_rel_img_point(rois, - rel_roi_points, - img, - spatial_scale=1.): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (torch.Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (torch.Tensor): Point coordinates inside RoI, relative - to RoI, location, range (0, 1), shape (N, P, 2) - img (tuple or torch.Tensor): (height, width) of image or feature map. - spatial_scale (float, optional): Scale points by this factor. - Default: 1. - - Returns: - torch.Tensor: Image based relative point coordinates for sampling, - shape (N, P, 2). - """ - - abs_img_point = rel_roi_point_to_abs_img_point(rois, rel_roi_points) - rel_img_point = abs_img_point_to_rel_img_point(abs_img_point, img, - spatial_scale) - - return rel_img_point - - -def point_sample(input, points, align_corners=False, **kwargs): - """A wrapper around :func:`grid_sample` to support 3D point_coords tensors - Unlike :func:`torch.nn.functional.grid_sample` it assumes point_coords to - lie inside ``[0, 1] x [0, 1]`` square. - - Args: - input (torch.Tensor): Feature map, shape (N, C, H, W). - points (torch.Tensor): Image based absolute point coordinates - (normalized), range [0, 1] x [0, 1], shape (N, P, 2) or - (N, Hgrid, Wgrid, 2). - align_corners (bool, optional): Whether align_corners. - Default: False - - Returns: - torch.Tensor: Features of `point` on `input`, shape (N, C, P) or - (N, C, Hgrid, Wgrid). - """ - - add_dim = False - if points.dim() == 3: - add_dim = True - points = points.unsqueeze(2) - if is_in_onnx_export_without_custom_ops(): - # If custom ops for onnx runtime not compiled use python - # implementation of grid_sample function to make onnx graph - # with supported nodes - output = bilinear_grid_sample( - input, denormalize(points), align_corners=align_corners) - else: - output = F.grid_sample( - input, denormalize(points), align_corners=align_corners, **kwargs) - if add_dim: - output = output.squeeze(3) - return output - - -class SimpleRoIAlign(nn.Module): - - def __init__(self, output_size, spatial_scale, aligned=True): - """Simple RoI align in PointRend, faster than standard RoIAlign. - - Args: - output_size (tuple[int]): h, w - spatial_scale (float): scale the input boxes by this number - aligned (bool): if False, use the legacy implementation in - MMDetection, align_corners=True will be used in F.grid_sample. - If True, align the results more perfectly. - """ - - super(SimpleRoIAlign, self).__init__() - self.output_size = _pair(output_size) - self.spatial_scale = float(spatial_scale) - # to be consistent with other RoI ops - self.use_torchvision = False - self.aligned = aligned - - def forward(self, features, rois): - num_imgs = features.size(0) - num_rois = rois.size(0) - rel_roi_points = generate_grid( - num_rois, self.output_size, device=rois.device) - - if torch.onnx.is_in_onnx_export(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois, rel_roi_points, features, self.spatial_scale) - rel_img_points = rel_img_points.reshape(num_imgs, -1, - *rel_img_points.shape[1:]) - point_feats = point_sample( - features, rel_img_points, align_corners=not self.aligned) - point_feats = point_feats.transpose(1, 2) - else: - point_feats = [] - for batch_ind in range(num_imgs): - # unravel batch dim - feat = features[batch_ind].unsqueeze(0) - inds = (rois[:, 0].long() == batch_ind) - if inds.any(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois[inds], rel_roi_points[inds], feat, - self.spatial_scale).unsqueeze(0) - point_feat = point_sample( - feat, rel_img_points, align_corners=not self.aligned) - point_feat = point_feat.squeeze(0).transpose(0, 1) - point_feats.append(point_feat) - - point_feats = torch.cat(point_feats, dim=0) - - channels = features.size(1) - roi_feats = point_feats.reshape(num_rois, channels, *self.output_size) - - return roi_feats - - def __repr__(self): - format_str = self.__class__.__name__ - format_str += '(output_size={}, spatial_scale={}'.format( - self.output_size, self.spatial_scale) - return format_str diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/psa_mask.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/psa_mask.py deleted file mode 100644 index cdf14e62b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/psa_mask.py +++ /dev/null @@ -1,92 +0,0 @@ -# Modified from https://github.com/hszhao/semseg/blob/master/lib/psa -from torch import nn -from torch.autograd import Function -from torch.nn.modules.utils import _pair - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', - ['psamask_forward', 'psamask_backward']) - - -class PSAMaskFunction(Function): - - @staticmethod - def symbolic(g, input, psa_type, mask_size): - return g.op( - 'mmcv::MMCVPSAMask', - input, - psa_type_i=psa_type, - mask_size_i=mask_size) - - @staticmethod - def forward(ctx, input, psa_type, mask_size): - ctx.psa_type = psa_type - ctx.mask_size = _pair(mask_size) - ctx.save_for_backward(input) - - h_mask, w_mask = ctx.mask_size - batch_size, channels, h_feature, w_feature = input.size() - assert channels == h_mask * w_mask - output = input.new_zeros( - (batch_size, h_feature * w_feature, h_feature, w_feature)) - - ext_module.psamask_forward( - input, - output, - psa_type=psa_type, - num_=batch_size, - h_feature=h_feature, - w_feature=w_feature, - h_mask=h_mask, - w_mask=w_mask, - half_h_mask=(h_mask - 1) // 2, - half_w_mask=(w_mask - 1) // 2) - return output - - @staticmethod - def backward(ctx, grad_output): - input = ctx.saved_tensors[0] - psa_type = ctx.psa_type - h_mask, w_mask = ctx.mask_size - batch_size, channels, h_feature, w_feature = input.size() - grad_input = grad_output.new_zeros( - (batch_size, channels, h_feature, w_feature)) - ext_module.psamask_backward( - grad_output, - grad_input, - psa_type=psa_type, - num_=batch_size, - h_feature=h_feature, - w_feature=w_feature, - h_mask=h_mask, - w_mask=w_mask, - half_h_mask=(h_mask - 1) // 2, - half_w_mask=(w_mask - 1) // 2) - return grad_input, None, None, None - - -psa_mask = PSAMaskFunction.apply - - -class PSAMask(nn.Module): - - def __init__(self, psa_type, mask_size=None): - super(PSAMask, self).__init__() - assert psa_type in ['collect', 'distribute'] - if psa_type == 'collect': - psa_type_enum = 0 - else: - psa_type_enum = 1 - self.psa_type_enum = psa_type_enum - self.mask_size = mask_size - self.psa_type = psa_type - - def forward(self, input): - return psa_mask(input, self.psa_type_enum, self.mask_size) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(psa_type={self.psa_type}, ' - s += f'mask_size={self.mask_size})' - return s diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/sync_bn.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/sync_bn.py deleted file mode 100644 index 04302f031..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/ops/sync_bn.py +++ /dev/null @@ -1,279 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn.functional as F -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.module import Module -from torch.nn.parameter import Parameter - -from mmcv.cnn import NORM_LAYERS -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sync_bn_forward_mean', 'sync_bn_forward_var', 'sync_bn_forward_output', - 'sync_bn_backward_param', 'sync_bn_backward_data' -]) - - -class SyncBatchNormFunction(Function): - - @staticmethod - def symbolic(g, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - return g.op( - 'mmcv::MMCVSyncBatchNorm', - input, - running_mean, - running_var, - weight, - bias, - momentum_f=momentum, - eps_f=eps, - group_i=group, - group_size_i=group_size, - stats_mode=stats_mode) - - @staticmethod - def forward(self, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - self.momentum = momentum - self.eps = eps - self.group = group - self.group_size = group_size - self.stats_mode = stats_mode - - assert isinstance( - input, (torch.HalfTensor, torch.FloatTensor, - torch.cuda.HalfTensor, torch.cuda.FloatTensor)), \ - f'only support Half or Float Tensor, but {input.type()}' - output = torch.zeros_like(input) - input3d = input.flatten(start_dim=2) - output3d = output.view_as(input3d) - num_channels = input3d.size(1) - - # ensure mean/var/norm/std are initialized as zeros - # ``torch.empty()`` does not guarantee that - mean = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - var = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - norm = torch.zeros_like( - input3d, dtype=torch.float, device=input3d.device) - std = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - - batch_size = input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_forward_mean(input3d, mean) - batch_flag = torch.ones([1], device=mean.device, dtype=mean.dtype) - else: - # skip updating mean and leave it as zeros when the input is empty - batch_flag = torch.zeros([1], device=mean.device, dtype=mean.dtype) - - # synchronize mean and the batch flag - vec = torch.cat([mean, batch_flag]) - if self.stats_mode == 'N': - vec *= batch_size - if self.group_size > 1: - dist.all_reduce(vec, group=self.group) - total_batch = vec[-1].detach() - mean = vec[:num_channels] - - if self.stats_mode == 'default': - mean = mean / self.group_size - elif self.stats_mode == 'N': - mean = mean / total_batch.clamp(min=1) - else: - raise NotImplementedError - - # leave var as zeros when the input is empty - if batch_size > 0: - ext_module.sync_bn_forward_var(input3d, mean, var) - - if self.stats_mode == 'N': - var *= batch_size - if self.group_size > 1: - dist.all_reduce(var, group=self.group) - - if self.stats_mode == 'default': - var /= self.group_size - elif self.stats_mode == 'N': - var /= total_batch.clamp(min=1) - else: - raise NotImplementedError - - # if the total batch size over all the ranks is zero, - # we should not update the statistics in the current batch - update_flag = total_batch.clamp(max=1) - momentum = update_flag * self.momentum - ext_module.sync_bn_forward_output( - input3d, - mean, - var, - weight, - bias, - running_mean, - running_var, - norm, - std, - output3d, - eps=self.eps, - momentum=momentum, - group_size=self.group_size) - self.save_for_backward(norm, std, weight) - return output - - @staticmethod - @once_differentiable - def backward(self, grad_output): - norm, std, weight = self.saved_tensors - grad_weight = torch.zeros_like(weight) - grad_bias = torch.zeros_like(weight) - grad_input = torch.zeros_like(grad_output) - grad_output3d = grad_output.flatten(start_dim=2) - grad_input3d = grad_input.view_as(grad_output3d) - - batch_size = grad_input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_backward_param(grad_output3d, norm, grad_weight, - grad_bias) - - # all reduce - if self.group_size > 1: - dist.all_reduce(grad_weight, group=self.group) - dist.all_reduce(grad_bias, group=self.group) - grad_weight /= self.group_size - grad_bias /= self.group_size - - if batch_size > 0: - ext_module.sync_bn_backward_data(grad_output3d, weight, - grad_weight, grad_bias, norm, std, - grad_input3d) - - return grad_input, None, None, grad_weight, grad_bias, \ - None, None, None, None, None - - -@NORM_LAYERS.register_module(name='MMSyncBN') -class SyncBatchNorm(Module): - """Synchronized Batch Normalization. - - Args: - num_features (int): number of features/chennels in input tensor - eps (float, optional): a value added to the denominator for numerical - stability. Defaults to 1e-5. - momentum (float, optional): the value used for the running_mean and - running_var computation. Defaults to 0.1. - affine (bool, optional): whether to use learnable affine parameters. - Defaults to True. - track_running_stats (bool, optional): whether to track the running - mean and variance during training. When set to False, this - module does not track such statistics, and initializes statistics - buffers ``running_mean`` and ``running_var`` as ``None``. When - these buffers are ``None``, this module always uses batch - statistics in both training and eval modes. Defaults to True. - group (int, optional): synchronization of stats happen within - each process group individually. By default it is synchronization - across the whole world. Defaults to None. - stats_mode (str, optional): The statistical mode. Available options - includes ``'default'`` and ``'N'``. Defaults to 'default'. - When ``stats_mode=='default'``, it computes the overall statistics - using those from each worker with equal weight, i.e., the - statistics are synchronized and simply divied by ``group``. This - mode will produce inaccurate statistics when empty tensors occur. - When ``stats_mode=='N'``, it compute the overall statistics using - the total number of batches in each worker ignoring the number of - group, i.e., the statistics are synchronized and then divied by - the total batch ``N``. This mode is beneficial when empty tensors - occur during training, as it average the total mean by the real - number of batch. - """ - - def __init__(self, - num_features, - eps=1e-5, - momentum=0.1, - affine=True, - track_running_stats=True, - group=None, - stats_mode='default'): - super(SyncBatchNorm, self).__init__() - self.num_features = num_features - self.eps = eps - self.momentum = momentum - self.affine = affine - self.track_running_stats = track_running_stats - group = dist.group.WORLD if group is None else group - self.group = group - self.group_size = dist.get_world_size(group) - assert stats_mode in ['default', 'N'], \ - f'"stats_mode" only accepts "default" and "N", got "{stats_mode}"' - self.stats_mode = stats_mode - if self.affine: - self.weight = Parameter(torch.Tensor(num_features)) - self.bias = Parameter(torch.Tensor(num_features)) - else: - self.register_parameter('weight', None) - self.register_parameter('bias', None) - if self.track_running_stats: - self.register_buffer('running_mean', torch.zeros(num_features)) - self.register_buffer('running_var', torch.ones(num_features)) - self.register_buffer('num_batches_tracked', - torch.tensor(0, dtype=torch.long)) - else: - self.register_buffer('running_mean', None) - self.register_buffer('running_var', None) - self.register_buffer('num_batches_tracked', None) - self.reset_parameters() - - def reset_running_stats(self): - if self.track_running_stats: - self.running_mean.zero_() - self.running_var.fill_(1) - self.num_batches_tracked.zero_() - - def reset_parameters(self): - self.reset_running_stats() - if self.affine: - self.weight.data.uniform_() # pytorch use ones_() - self.bias.data.zero_() - - def forward(self, input): - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input, got {input.dim()}D input') - if self.momentum is None: - exponential_average_factor = 0.0 - else: - exponential_average_factor = self.momentum - - if self.training and self.track_running_stats: - if self.num_batches_tracked is not None: - self.num_batches_tracked += 1 - if self.momentum is None: # use cumulative moving average - exponential_average_factor = 1.0 / float( - self.num_batches_tracked) - else: # use exponential moving average - exponential_average_factor = self.momentum - - if self.training or not self.track_running_stats: - return SyncBatchNormFunction.apply( - input, self.running_mean, self.running_var, self.weight, - self.bias, exponential_average_factor, self.eps, self.group, - self.group_size, self.stats_mode) - else: - return F.batch_norm(input, self.running_mean, self.running_var, - self.weight, self.bias, False, - exponential_average_factor, self.eps) - - def __repr__(self): - s = self.__class__.__name__ - s += f'({self.num_features}, ' - s += f'eps={self.eps}, ' - s += f'momentum={self.momentum}, ' - s += f'affine={self.affine}, ' - s += f'track_running_stats={self.track_running_stats}, ' - s += f'group_size={self.group_size},' - s += f'stats_mode={self.stats_mode})' - return s diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/__init__.py deleted file mode 100644 index 2ed2c17ad..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collate import collate -from .data_container import DataContainer -from .data_parallel import MMDataParallel -from .distributed import MMDistributedDataParallel -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter, scatter_kwargs -from .utils import is_module_wrapper - -__all__ = [ - 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', - 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/_functions.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/_functions.py deleted file mode 100644 index 95c58bf1a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/_functions.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import _get_stream - - -def scatter(input, devices, streams=None): - """Scatters tensor across multiple GPUs.""" - if streams is None: - streams = [None] * len(devices) - - if isinstance(input, list): - chunk_size = (len(input) - 1) // len(devices) + 1 - outputs = [ - scatter(input[i], [devices[i // chunk_size]], - [streams[i // chunk_size]]) for i in range(len(input)) - ] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - # TODO: copy to a pinned buffer first (if copying from CPU) - stream = streams[0] if output.numel() > 0 else None - if devices != [-1]: - with torch.cuda.device(devices[0]), torch.cuda.stream(stream): - output = output.cuda(devices[0], non_blocking=True) - - return output - else: - raise Exception(f'Unknown type {type(input)}.') - - -def synchronize_stream(output, devices, streams): - if isinstance(output, list): - chunk_size = len(output) // len(devices) - for i in range(len(devices)): - for j in range(chunk_size): - synchronize_stream(output[i * chunk_size + j], [devices[i]], - [streams[i]]) - elif isinstance(output, torch.Tensor): - if output.numel() != 0: - with torch.cuda.device(devices[0]): - main_stream = torch.cuda.current_stream() - main_stream.wait_stream(streams[0]) - output.record_stream(main_stream) - else: - raise Exception(f'Unknown type {type(output)}.') - - -def get_input_device(input): - if isinstance(input, list): - for item in input: - input_device = get_input_device(item) - if input_device != -1: - return input_device - return -1 - elif isinstance(input, torch.Tensor): - return input.get_device() if input.is_cuda else -1 - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_gpus, input): - input_device = get_input_device(input) - streams = None - if input_device == -1 and target_gpus != [-1]: - # Perform CPU to GPU copies in a background stream - streams = [_get_stream(device) for device in target_gpus] - - outputs = scatter(input, target_gpus, streams) - # Synchronize with the copy stream - if streams is not None: - synchronize_stream(outputs, target_gpus, streams) - - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/collate.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/collate.py deleted file mode 100644 index ad749197d..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/collate.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence - -import torch -import torch.nn.functional as F -from torch.utils.data.dataloader import default_collate - -from .data_container import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Puts each data field into a tensor/DataContainer with outer dimension - batch size. - - Extend default_collate to add support for - :type:`~mmcv.parallel.DataContainer`. There are 3 cases. - - 1. cpu_only = True, e.g., meta data - 2. cpu_only = False, stack = True, e.g., images tensors - 3. cpu_only = False, stack = False, e.g., gt bboxes - """ - - if not isinstance(batch, Sequence): - raise TypeError(f'{batch.dtype} is not supported.') - - if isinstance(batch[0], DataContainer): - stacked = [] - if batch[0].cpu_only: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer( - stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) - elif batch[0].stack: - for i in range(0, len(batch), samples_per_gpu): - assert isinstance(batch[i].data, torch.Tensor) - - if batch[i].pad_dims is not None: - ndim = batch[i].dim() - assert ndim > batch[i].pad_dims - max_shape = [0 for _ in range(batch[i].pad_dims)] - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = batch[i].size(-dim) - for sample in batch[i:i + samples_per_gpu]: - for dim in range(0, ndim - batch[i].pad_dims): - assert batch[i].size(dim) == sample.size(dim) - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = max(max_shape[dim - 1], - sample.size(-dim)) - padded_samples = [] - for sample in batch[i:i + samples_per_gpu]: - pad = [0 for _ in range(batch[i].pad_dims * 2)] - for dim in range(1, batch[i].pad_dims + 1): - pad[2 * dim - - 1] = max_shape[dim - 1] - sample.size(-dim) - padded_samples.append( - F.pad( - sample.data, pad, value=sample.padding_value)) - stacked.append(default_collate(padded_samples)) - elif batch[i].pad_dims is None: - stacked.append( - default_collate([ - sample.data - for sample in batch[i:i + samples_per_gpu] - ])) - else: - raise ValueError( - 'pad_dims should be either None or integers (1-3)') - - else: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer(stacked, batch[0].stack, batch[0].padding_value) - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - return [collate(samples, samples_per_gpu) for samples in transposed] - elif isinstance(batch[0], Mapping): - return { - key: collate([d[key] for d in batch], samples_per_gpu) - for key in batch[0] - } - else: - return default_collate(batch) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/data_container.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/data_container.py deleted file mode 100644 index cedb0d32a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/data_container.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch - - -def assert_tensor_type(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not isinstance(args[0].data, torch.Tensor): - raise AttributeError( - f'{args[0].__class__.__name__} has no attribute ' - f'{func.__name__} for type {args[0].datatype}') - return func(*args, **kwargs) - - return wrapper - - -class DataContainer: - """A container for any type of objects. - - Typically tensors will be stacked in the collate function and sliced along - some dimension in the scatter function. This behavior has some limitations. - 1. All tensors have to be the same size. - 2. Types are limited (numpy array or Tensor). - - We design `DataContainer` and `MMDataParallel` to overcome these - limitations. The behavior can be either of the following. - - - copy to GPU, pad all tensors to the same size and stack them - - copy to GPU without stacking - - leave the objects as is and pass it to the model - - pad_dims specifies the number of last few dimensions to do padding - """ - - def __init__(self, - data, - stack=False, - padding_value=0, - cpu_only=False, - pad_dims=2): - self._data = data - self._cpu_only = cpu_only - self._stack = stack - self._padding_value = padding_value - assert pad_dims in [None, 1, 2, 3] - self._pad_dims = pad_dims - - def __repr__(self): - return f'{self.__class__.__name__}({repr(self.data)})' - - def __len__(self): - return len(self._data) - - @property - def data(self): - return self._data - - @property - def datatype(self): - if isinstance(self.data, torch.Tensor): - return self.data.type() - else: - return type(self.data) - - @property - def cpu_only(self): - return self._cpu_only - - @property - def stack(self): - return self._stack - - @property - def padding_value(self): - return self._padding_value - - @property - def pad_dims(self): - return self._pad_dims - - @assert_tensor_type - def size(self, *args, **kwargs): - return self.data.size(*args, **kwargs) - - @assert_tensor_type - def dim(self): - return self.data.dim() diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/data_parallel.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/data_parallel.py deleted file mode 100644 index 7a5abeb6e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/data_parallel.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from itertools import chain - -from torch.nn.parallel import DataParallel - -from .scatter_gather import scatter_kwargs - - -class MMDataParallel(DataParallel): - """The DataParallel module that supports DataContainer. - - MMDataParallel has two main differences with PyTorch DataParallel: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data during both GPU and CPU inference. - - It implement two more APIs ``train_step()`` and ``val_step()``. - - .. warning:: - MMDataParallel only supports single GPU training, if you need to - train with multiple GPUs, please use MMDistributedDataParallel - instead. If you have multiple GPUs and you just want to use - MMDataParallel, you can set the environment variable - ``CUDA_VISIBLE_DEVICES=0`` or instantiate ``MMDataParallel`` with - ``device_ids=[0]``. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - device_ids (list[int]): Device IDS of modules to be scattered to. - Defaults to None when GPU is not available. - output_device (str | int): Device ID for output. Defaults to None. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MMDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.dim = dim - - def forward(self, *inputs, **kwargs): - """Override the original forward function. - - The main difference lies in the CPU inference where the data in - :class:`DataContainers` will still be gathered. - """ - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module(*inputs[0], **kwargs[0]) - else: - return super().forward(*inputs, **kwargs) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.train_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.train_step(*inputs[0], **kwargs[0]) - - def val_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.val_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/distributed.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/distributed.py deleted file mode 100644 index 0188ca4ab..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/distributed.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel.distributed import (DistributedDataParallel, - _find_tensors) - -from mmcv import print_log -from mmcv.utils import TORCH_VERSION, digit_version -from .scatter_gather import scatter_kwargs - - -class MMDistributedDataParallel(DistributedDataParallel): - """The DDP module that supports DataContainer. - - MMDDP has two main differences with PyTorch DDP: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data. - - It implement two APIs ``train_step()`` and ``val_step()``. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - """train_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.train_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.train_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.train_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output - - def val_step(self, *inputs, **kwargs): - """val_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.val_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.val_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.val_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/distributed_deprecated.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/distributed_deprecated.py deleted file mode 100644 index b593d4a9e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/distributed_deprecated.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn as nn -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter_kwargs - - -@MODULE_WRAPPERS.register_module() -class MMDistributedDataParallel(nn.Module): - - def __init__(self, - module, - dim=0, - broadcast_buffers=True, - bucket_cap_mb=25): - super(MMDistributedDataParallel, self).__init__() - self.module = module - self.dim = dim - self.broadcast_buffers = broadcast_buffers - - self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 - self._sync_params() - - def _dist_broadcast_coalesced(self, tensors, buffer_size): - for tensors in _take_tensors(tensors, buffer_size): - flat_tensors = _flatten_dense_tensors(tensors) - dist.broadcast(flat_tensors, 0) - for tensor, synced in zip( - tensors, _unflatten_dense_tensors(flat_tensors, tensors)): - tensor.copy_(synced) - - def _sync_params(self): - module_states = list(self.module.state_dict().values()) - if len(module_states) > 0: - self._dist_broadcast_coalesced(module_states, - self.broadcast_bucket_size) - if self.broadcast_buffers: - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) < digit_version('1.0')): - buffers = [b.data for b in self.module._all_buffers()] - else: - buffers = [b.data for b in self.module.buffers()] - if len(buffers) > 0: - self._dist_broadcast_coalesced(buffers, - self.broadcast_bucket_size) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/registry.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/registry.py deleted file mode 100644 index 144f9fb16..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel, DistributedDataParallel - -from mmcv.utils import Registry - -MODULE_WRAPPERS = Registry('module wrapper') -MODULE_WRAPPERS.register_module(module=DataParallel) -MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/scatter_gather.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/scatter_gather.py deleted file mode 100644 index 900ff8856..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import Scatter as OrigScatter - -from ._functions import Scatter -from .data_container import DataContainer - - -def scatter(inputs, target_gpus, dim=0): - """Scatter inputs to target gpus. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_gpus != [-1]: - return OrigScatter.apply(target_gpus, None, dim, obj) - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_gpus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_gpus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_gpus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_gpus, dim) if inputs else [] - kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/utils.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/utils.py deleted file mode 100644 index 0f5712cb4..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/parallel/utils.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import MODULE_WRAPPERS - - -def is_module_wrapper(module): - """Check if a module is a module wrapper. - - The following 3 modules in MMCV (and their subclasses) are regarded as - module wrappers: DataParallel, DistributedDataParallel, - MMDistributedDataParallel (the deprecated version). You may add you own - module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: True if the input module is a module wrapper. - """ - module_wrappers = tuple(MODULE_WRAPPERS.module_dict.values()) - return isinstance(module, module_wrappers) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/__init__.py deleted file mode 100644 index 183d53672..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_module import BaseModule, ModuleDict, ModuleList, Sequential -from .base_runner import BaseRunner -from .builder import RUNNERS, build_runner -from .checkpoint import (CheckpointLoader, _load_checkpoint, - _load_checkpoint_with_prefix, load_checkpoint, - load_state_dict, save_checkpoint, weights_to_cpu) -from .default_constructor import DefaultRunnerConstructor -from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, - init_dist, master_only) -from .epoch_based_runner import EpochBasedRunner, Runner -from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model -from .hooks import (HOOKS, CheckpointHook, ClearMLLoggerHook, ClosureHook, - DistEvalHook, DistSamplerSeedHook, DvcliveLoggerHook, - EMAHook, EvalHook, Fp16OptimizerHook, - GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, Hook, IterTimerHook, - LoggerHook, MlflowLoggerHook, NeptuneLoggerHook, - OptimizerHook, PaviLoggerHook, SegmindLoggerHook, - SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .hooks.lr_updater import StepLrUpdaterHook # noqa -from .hooks.lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, - InvLrUpdaterHook, LinearAnnealingLrUpdaterHook, - LrUpdaterHook, OneCycleLrUpdaterHook, - PolyLrUpdaterHook) -from .hooks.momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .iter_based_runner import IterBasedRunner, IterLoader -from .log_buffer import LogBuffer -from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, - DefaultOptimizerConstructor, build_optimizer, - build_optimizer_constructor) -from .priority import Priority, get_priority -from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed - -# initialize ipu to registor ipu runner to RUNNERS -from mmcv.device import ipu # isort:skip # noqa - -__all__ = [ - 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', - 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', - 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', - 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', - 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', - 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', - 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', - 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', - 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', - 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', - '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', - 'ModuleDict', 'ModuleList', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor', - 'SegmindLoggerHook', 'LinearAnnealingMomentumUpdaterHook', - 'LinearAnnealingLrUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/base_module.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/base_module.py deleted file mode 100644 index 7937eca37..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/base_module.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings -from abc import ABCMeta -from collections import defaultdict -from logging import FileHandler - -import torch.nn as nn - -from mmcv.runner.dist_utils import master_only -from mmcv.utils.logging import get_logger, logger_initialized, print_log - - -class BaseModule(nn.Module, metaclass=ABCMeta): - """Base module for all modules in openmmlab. - - ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional - functionality of parameter initialization. Compared with - ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. - - - ``init_cfg``: the config to control the initialization. - - ``init_weights``: The function of parameter initialization and recording - initialization information. - - ``_params_init_info``: Used to track the parameter initialization - information. This attribute only exists during executing the - ``init_weights``. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, init_cfg=None): - """Initialize BaseModule, inherited from `torch.nn.Module`""" - - # NOTE init_cfg can be defined in different levels, but init_cfg - # in low levels has a higher priority. - - super(BaseModule, self).__init__() - # define default value of init_cfg instead of hard code - # in init_weights() function - self._is_init = False - - self.init_cfg = copy.deepcopy(init_cfg) - - # Backward compatibility in derived classes - # if pretrained is not None: - # warnings.warn('DeprecationWarning: pretrained is a deprecated \ - # key, please consider using init_cfg') - # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - - @property - def is_init(self): - return self._is_init - - def init_weights(self): - """Initialize the weights.""" - - is_top_level_module = False - # check if it is top-level module - if not hasattr(self, '_params_init_info'): - # The `_params_init_info` is used to record the initialization - # information of the parameters - # the key should be the obj:`nn.Parameter` of model and the value - # should be a dict containing - # - init_info (str): The string that describes the initialization. - # - tmp_mean_value (FloatTensor): The mean of the parameter, - # which indicates whether the parameter has been modified. - # this attribute would be deleted after all parameters - # is initialized. - self._params_init_info = defaultdict(dict) - is_top_level_module = True - - # Initialize the `_params_init_info`, - # When detecting the `tmp_mean_value` of - # the corresponding parameter is changed, update related - # initialization information - for name, param in self.named_parameters(): - self._params_init_info[param][ - 'init_info'] = f'The value is the same before and ' \ - f'after calling `init_weights` ' \ - f'of {self.__class__.__name__} ' - self._params_init_info[param][ - 'tmp_mean_value'] = param.data.mean() - - # pass `params_init_info` to all submodules - # All submodules share the same `params_init_info`, - # so it will be updated when parameters are - # modified at any level of the model. - for sub_module in self.modules(): - sub_module._params_init_info = self._params_init_info - - # Get the initialized logger, if not exist, - # create a logger named `mmcv` - logger_names = list(logger_initialized.keys()) - logger_name = logger_names[0] if logger_names else 'mmcv' - - from ..cnn import initialize - from ..cnn.utils.weight_init import update_init_info - module_name = self.__class__.__name__ - if not self._is_init: - if self.init_cfg: - print_log( - f'initialize {module_name} with init_cfg {self.init_cfg}', - logger=logger_name) - initialize(self, self.init_cfg) - if isinstance(self.init_cfg, dict): - # prevent the parameters of - # the pre-trained model - # from being overwritten by - # the `init_weights` - if self.init_cfg['type'] == 'Pretrained': - return - - for m in self.children(): - if hasattr(m, 'init_weights'): - m.init_weights() - # users may overload the `init_weights` - update_init_info( - m, - init_info=f'Initialized by ' - f'user-defined `init_weights`' - f' in {m.__class__.__name__} ') - - self._is_init = True - else: - warnings.warn(f'init_weights of {self.__class__.__name__} has ' - f'been called more than once.') - - if is_top_level_module: - self._dump_init_info(logger_name) - - for sub_module in self.modules(): - del sub_module._params_init_info - - @master_only - def _dump_init_info(self, logger_name): - """Dump the initialization information to a file named - `initialization.log.json` in workdir. - - Args: - logger_name (str): The name of logger. - """ - - logger = get_logger(logger_name) - - with_file_handler = False - # dump the information to the logger file if there is a `FileHandler` - for handler in logger.handlers: - if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True - if not with_file_handler: - for name, param in self.named_parameters(): - print_log( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n ", - logger=logger_name) - - def __repr__(self): - s = super().__repr__() - if self.init_cfg: - s += f'\ninit_cfg={self.init_cfg}' - return s - - -class Sequential(BaseModule, nn.Sequential): - """Sequential module in openmmlab. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, *args, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.Sequential.__init__(self, *args) - - -class ModuleList(BaseModule, nn.ModuleList): - """ModuleList in openmmlab. - - Args: - modules (iterable, optional): an iterable of modules to add. - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleList.__init__(self, modules) - - -class ModuleDict(BaseModule, nn.ModuleDict): - """ModuleDict in openmmlab. - - Args: - modules (dict, optional): a mapping (dictionary) of (string: module) - or an iterable of key-value pairs of type (string, module). - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleDict.__init__(self, modules) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/base_runner.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/base_runner.py deleted file mode 100644 index 12a0025f8..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/base_runner.py +++ /dev/null @@ -1,544 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -import os.path as osp -import warnings -from abc import ABCMeta, abstractmethod - -import torch -from torch.optim import Optimizer - -import mmcv -from ..parallel import is_module_wrapper -from .checkpoint import load_checkpoint -from .dist_utils import get_dist_info -from .hooks import HOOKS, Hook -from .log_buffer import LogBuffer -from .priority import Priority, get_priority -from .utils import get_time_str - - -class BaseRunner(metaclass=ABCMeta): - """The base class of Runner, a training helper for PyTorch. - - All subclasses should implement the following APIs: - - - ``run()`` - - ``train()`` - - ``val()`` - - ``save_checkpoint()`` - - Args: - model (:obj:`torch.nn.Module`): The model to be run. - batch_processor (callable): A callable method that process a data - batch. The interface of this method should be - `batch_processor(model, data, train_mode) -> dict` - optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an - optimizer (in most cases) or a dict of optimizers (in models that - requires more than one optimizer, e.g., GAN). - work_dir (str, optional): The working directory to save checkpoints - and logs. Defaults to None. - logger (:obj:`logging.Logger`): Logger used during training. - Defaults to None. (The default value is just for backward - compatibility) - meta (dict | None): A dict records some import information such as - environment info and seed, which will be logged in logger hook. - Defaults to None. - max_epochs (int, optional): Total training epochs. - max_iters (int, optional): Total training iterations. - """ - - def __init__(self, - model, - batch_processor=None, - optimizer=None, - work_dir=None, - logger=None, - meta=None, - max_iters=None, - max_epochs=None): - if batch_processor is not None: - if not callable(batch_processor): - raise TypeError('batch_processor must be callable, ' - f'but got {type(batch_processor)}') - warnings.warn( - 'batch_processor is deprecated, please implement ' - 'train_step() and val_step() in the model instead.', - DeprecationWarning) - # raise an error is `batch_processor` is not None and - # `model.train_step()` exists. - if is_module_wrapper(model): - _model = model.module - else: - _model = model - if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): - raise RuntimeError( - 'batch_processor and model.train_step()/model.val_step() ' - 'cannot be both available.') - else: - assert hasattr(model, 'train_step') - - # check the type of `optimizer` - if isinstance(optimizer, dict): - for name, optim in optimizer.items(): - if not isinstance(optim, Optimizer): - raise TypeError( - f'optimizer must be a dict of torch.optim.Optimizers, ' - f'but optimizer["{name}"] is a {type(optim)}') - elif not isinstance(optimizer, Optimizer) and optimizer is not None: - raise TypeError( - f'optimizer must be a torch.optim.Optimizer object ' - f'or dict or None, but got {type(optimizer)}') - - # check the type of `logger` - if not isinstance(logger, logging.Logger): - raise TypeError(f'logger must be a logging.Logger object, ' - f'but got {type(logger)}') - - # check the type of `meta` - if meta is not None and not isinstance(meta, dict): - raise TypeError( - f'meta must be a dict or None, but got {type(meta)}') - - self.model = model - self.batch_processor = batch_processor - self.optimizer = optimizer - self.logger = logger - self.meta = meta - # create work_dir - if mmcv.is_str(work_dir): - self.work_dir = osp.abspath(work_dir) - mmcv.mkdir_or_exist(self.work_dir) - elif work_dir is None: - self.work_dir = None - else: - raise TypeError('"work_dir" must be a str or None') - - # get model name from the model class - if hasattr(self.model, 'module'): - self._model_name = self.model.module.__class__.__name__ - else: - self._model_name = self.model.__class__.__name__ - - self._rank, self._world_size = get_dist_info() - self.timestamp = get_time_str() - self.mode = None - self._hooks = [] - self._epoch = 0 - self._iter = 0 - self._inner_iter = 0 - - if max_epochs is not None and max_iters is not None: - raise ValueError( - 'Only one of `max_epochs` or `max_iters` can be set.') - - self._max_epochs = max_epochs - self._max_iters = max_iters - # TODO: Redesign LogBuffer, it is not flexible and elegant enough - self.log_buffer = LogBuffer() - - @property - def model_name(self): - """str: Name of the model, usually the module class name.""" - return self._model_name - - @property - def rank(self): - """int: Rank of current process. (distributed training)""" - return self._rank - - @property - def world_size(self): - """int: Number of processes participating in the job. - (distributed training)""" - return self._world_size - - @property - def hooks(self): - """list[:obj:`Hook`]: A list of registered hooks.""" - return self._hooks - - @property - def epoch(self): - """int: Current epoch.""" - return self._epoch - - @property - def iter(self): - """int: Current iteration.""" - return self._iter - - @property - def inner_iter(self): - """int: Iteration in an epoch.""" - return self._inner_iter - - @property - def max_epochs(self): - """int: Maximum training epochs.""" - return self._max_epochs - - @property - def max_iters(self): - """int: Maximum training iterations.""" - return self._max_iters - - @abstractmethod - def train(self): - pass - - @abstractmethod - def val(self): - pass - - @abstractmethod - def run(self, data_loaders, workflow, **kwargs): - pass - - @abstractmethod - def save_checkpoint(self, - out_dir, - filename_tmpl, - save_optimizer=True, - meta=None, - create_symlink=True): - pass - - def current_lr(self): - """Get current learning rates. - - Returns: - list[float] | dict[str, list[float]]: Current learning rates of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - if isinstance(self.optimizer, torch.optim.Optimizer): - lr = [group['lr'] for group in self.optimizer.param_groups] - elif isinstance(self.optimizer, dict): - lr = dict() - for name, optim in self.optimizer.items(): - lr[name] = [group['lr'] for group in optim.param_groups] - else: - raise RuntimeError( - 'lr is not applicable because optimizer does not exist.') - return lr - - def current_momentum(self): - """Get current momentums. - - Returns: - list[float] | dict[str, list[float]]: Current momentums of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - - def _get_momentum(optimizer): - momentums = [] - for group in optimizer.param_groups: - if 'momentum' in group.keys(): - momentums.append(group['momentum']) - elif 'betas' in group.keys(): - momentums.append(group['betas'][0]) - else: - momentums.append(0) - return momentums - - if self.optimizer is None: - raise RuntimeError( - 'momentum is not applicable because optimizer does not exist.') - elif isinstance(self.optimizer, torch.optim.Optimizer): - momentums = _get_momentum(self.optimizer) - elif isinstance(self.optimizer, dict): - momentums = dict() - for name, optim in self.optimizer.items(): - momentums[name] = _get_momentum(optim) - return momentums - - def register_hook(self, hook, priority='NORMAL'): - """Register a hook into the hook list. - - The hook will be inserted into a priority queue, with the specified - priority (See :class:`Priority` for details of priorities). - For hooks with the same priority, they will be triggered in the same - order as they are registered. - - Args: - hook (:obj:`Hook`): The hook to be registered. - priority (int or str or :obj:`Priority`): Hook priority. - Lower value means higher priority. - """ - assert isinstance(hook, Hook) - if hasattr(hook, 'priority'): - raise ValueError('"priority" is a reserved attribute for hooks') - priority = get_priority(priority) - hook.priority = priority - # insert the hook to a sorted list - inserted = False - for i in range(len(self._hooks) - 1, -1, -1): - if priority >= self._hooks[i].priority: - self._hooks.insert(i + 1, hook) - inserted = True - break - if not inserted: - self._hooks.insert(0, hook) - - def register_hook_from_cfg(self, hook_cfg): - """Register a hook from its cfg. - - Args: - hook_cfg (dict): Hook config. It should have at least keys 'type' - and 'priority' indicating its type and priority. - - Note: - The specific hook class to register should not use 'type' and - 'priority' arguments during initialization. - """ - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = mmcv.build_from_cfg(hook_cfg, HOOKS) - self.register_hook(hook, priority=priority) - - def call_hook(self, fn_name): - """Call all hooks. - - Args: - fn_name (str): The function name in each hook to be called, such as - "before_train_epoch". - """ - for hook in self._hooks: - getattr(hook, fn_name)(self) - - def get_hook_info(self): - # Get hooks info in each stage - stage_hook_map = {stage: [] for stage in Hook.stages} - for hook in self.hooks: - try: - priority = Priority(hook.priority).name - except ValueError: - priority = hook.priority - classname = hook.__class__.__name__ - hook_info = f'({priority:<12}) {classname:<35}' - for trigger_stage in hook.get_triggered_stages(): - stage_hook_map[trigger_stage].append(hook_info) - - stage_hook_infos = [] - for stage in Hook.stages: - hook_infos = stage_hook_map[stage] - if len(hook_infos) > 0: - info = f'{stage}:\n' - info += '\n'.join(hook_infos) - info += '\n -------------------- ' - stage_hook_infos.append(info) - return '\n'.join(stage_hook_infos) - - def load_checkpoint(self, - filename, - map_location='cpu', - strict=False, - revise_keys=[(r'^module.', '')]): - return load_checkpoint( - self.model, - filename, - map_location, - strict, - self.logger, - revise_keys=revise_keys) - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - if map_location == 'default': - if torch.cuda.is_available(): - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint(checkpoint) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - if self.meta is None: - self.meta = {} - self.meta.setdefault('hook_msgs', {}) - # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages - self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) - - # Re-calculate the number of iterations when resuming - # models with different number of GPUs - if 'config' in checkpoint['meta']: - config = mmcv.Config.fromstring( - checkpoint['meta']['config'], file_format='.py') - previous_gpu_ids = config.get('gpu_ids', None) - if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( - previous_gpu_ids) != self.world_size: - self._iter = int(self._iter * len(previous_gpu_ids) / - self.world_size) - self.logger.info('the iteration number is changed due to ' - 'change of GPU number') - - # resume meta information meta - self.meta = checkpoint['meta'] - - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - elif isinstance(lr_config, dict): - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = mmcv.build_from_cfg(lr_config, HOOKS) - else: - hook = lr_config - self.register_hook(hook, priority='VERY_HIGH') - - def register_momentum_hook(self, momentum_config): - if momentum_config is None: - return - if isinstance(momentum_config, dict): - assert 'policy' in momentum_config - policy_type = momentum_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of momentum updater. - # Since this is not applicable for - # `CosineAnnealingMomentumUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'MomentumUpdaterHook' - momentum_config['type'] = hook_type - hook = mmcv.build_from_cfg(momentum_config, HOOKS) - else: - hook = momentum_config - self.register_hook(hook, priority='HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = mmcv.build_from_cfg(optimizer_config, HOOKS) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def register_checkpoint_hook(self, checkpoint_config): - if checkpoint_config is None: - return - if isinstance(checkpoint_config, dict): - checkpoint_config.setdefault('type', 'CheckpointHook') - hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) - else: - hook = checkpoint_config - self.register_hook(hook, priority='NORMAL') - - def register_logger_hooks(self, log_config): - if log_config is None: - return - log_interval = log_config['interval'] - for info in log_config['hooks']: - logger_hook = mmcv.build_from_cfg( - info, HOOKS, default_args=dict(interval=log_interval)) - self.register_hook(logger_hook, priority='VERY_LOW') - - def register_timer_hook(self, timer_config): - if timer_config is None: - return - if isinstance(timer_config, dict): - timer_config_ = copy.deepcopy(timer_config) - hook = mmcv.build_from_cfg(timer_config_, HOOKS) - else: - hook = timer_config - self.register_hook(hook, priority='LOW') - - def register_custom_hooks(self, custom_config): - if custom_config is None: - return - - if not isinstance(custom_config, list): - custom_config = [custom_config] - - for item in custom_config: - if isinstance(item, dict): - self.register_hook_from_cfg(item) - else: - self.register_hook(item, priority='NORMAL') - - def register_profiler_hook(self, profiler_config): - if profiler_config is None: - return - if isinstance(profiler_config, dict): - profiler_config.setdefault('type', 'ProfilerHook') - hook = mmcv.build_from_cfg(profiler_config, HOOKS) - else: - hook = profiler_config - self.register_hook(hook) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - timer_config=dict(type='IterTimerHook'), - custom_hooks_config=None): - """Register default and custom hooks for training. - - Default and custom hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - self.register_lr_hook(lr_config) - self.register_momentum_hook(momentum_config) - self.register_optimizer_hook(optimizer_config) - self.register_checkpoint_hook(checkpoint_config) - self.register_timer_hook(timer_config) - self.register_logger_hooks(log_config) - self.register_custom_hooks(custom_hooks_config) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/builder.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/builder.py deleted file mode 100644 index 77c96ba0b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/builder.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from ..utils import Registry - -RUNNERS = Registry('runner') -RUNNER_BUILDERS = Registry('runner builder') - - -def build_runner_constructor(cfg): - return RUNNER_BUILDERS.build(cfg) - - -def build_runner(cfg, default_args=None): - runner_cfg = copy.deepcopy(cfg) - constructor_type = runner_cfg.pop('constructor', - 'DefaultRunnerConstructor') - runner_constructor = build_runner_constructor( - dict( - type=constructor_type, - runner_cfg=runner_cfg, - default_args=default_args)) - runner = runner_constructor() - return runner diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/checkpoint.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/checkpoint.py deleted file mode 100644 index 835ee725a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/checkpoint.py +++ /dev/null @@ -1,759 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os -import os.path as osp -import pkgutil -import re -import time -import warnings -from collections import OrderedDict -from importlib import import_module -from tempfile import TemporaryDirectory - -import torch -import torchvision -from torch.optim import Optimizer - -import mmcv -from ..fileio import FileClient -from ..fileio import load as load_file -from ..parallel import is_module_wrapper -from ..utils import digit_version, load_url, mkdir_or_exist -from .dist_utils import get_dist_info - -ENV_MMCV_HOME = 'MMCV_HOME' -ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' -DEFAULT_CACHE_DIR = '~/.cache' - - -def _get_mmcv_home(): - mmcv_home = os.path.expanduser( - os.getenv( - ENV_MMCV_HOME, - os.path.join( - os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) - - mkdir_or_exist(mmcv_home) - return mmcv_home - - -def load_state_dict(module, state_dict, strict=False, logger=None): - """Load state_dict to a module. - - This method is modified from :meth:`torch.nn.Module.load_state_dict`. - Default value for ``strict`` is set to ``False`` and the message for - param mismatch will be shown even if strict is False. - - Args: - module (Module): Module that receives the state_dict. - state_dict (OrderedDict): Weights. - strict (bool): whether to strictly enforce that the keys - in :attr:`state_dict` match the keys returned by this module's - :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. - logger (:obj:`logging.Logger`, optional): Logger to log the error - message. If not specified, print function will be used. - """ - unexpected_keys = [] - all_missing_keys = [] - err_msg = [] - - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata - - # use _load_from_state_dict to enable checkpoint version control - def load(module, prefix=''): - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - local_metadata = {} if metadata is None else metadata.get( - prefix[:-1], {}) - module._load_from_state_dict(state_dict, prefix, local_metadata, True, - all_missing_keys, unexpected_keys, - err_msg) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(module) - load = None # break load->load reference cycle - - # ignore "num_batches_tracked" of BN layers - missing_keys = [ - key for key in all_missing_keys if 'num_batches_tracked' not in key - ] - - if unexpected_keys: - err_msg.append('unexpected key in source ' - f'state_dict: {", ".join(unexpected_keys)}\n') - if missing_keys: - err_msg.append( - f'missing keys in source state_dict: {", ".join(missing_keys)}\n') - - rank, _ = get_dist_info() - if len(err_msg) > 0 and rank == 0: - err_msg.insert( - 0, 'The model and loaded state dict do not match exactly\n') - err_msg = '\n'.join(err_msg) - if strict: - raise RuntimeError(err_msg) - elif logger is not None: - logger.warning(err_msg) - else: - print(err_msg) - - -def get_torchvision_models(): - if digit_version(torchvision.__version__) < digit_version('0.13.0a0'): - model_urls = dict() - # When the version of torchvision is lower than 0.13, the model url is - # not declared in `torchvision.model.__init__.py`, so we need to - # iterate through `torchvision.models.__path__` to get the url for each - # model. - for _, name, ispkg in pkgutil.walk_packages( - torchvision.models.__path__): - if ispkg: - continue - _zoo = import_module(f'torchvision.models.{name}') - if hasattr(_zoo, 'model_urls'): - _urls = getattr(_zoo, 'model_urls') - model_urls.update(_urls) - else: - # Since torchvision bumps to v0.13, the weight loading logic, - # model keys and model urls have been changed. Here the URLs of old - # version is loaded to avoid breaking back compatibility. If the - # torchvision version>=0.13.0, new URLs will be added. Users can get - # the resnet50 checkpoint by setting 'resnet50.imagent1k_v1', - # 'resnet50' or 'ResNet50_Weights.IMAGENET1K_V1' in the config. - json_path = osp.join(mmcv.__path__[0], - 'model_zoo/torchvision_0.12.json') - model_urls = mmcv.load(json_path) - for cls_name, cls in torchvision.models.__dict__.items(): - # The name of torchvision model weights classes ends with - # `_Weights` such as `ResNet18_Weights`. However, some model weight - # classes, such as `MNASNet0_75_Weights` does not have any urls in - # torchvision 0.13.0 and cannot be iterated. Here we simply check - # `DEFAULT` attribute to ensure the class is not empty. - if (not cls_name.endswith('_Weights') - or not hasattr(cls, 'DEFAULT')): - continue - # Since `cls.DEFAULT` can not be accessed by iterating cls, we set - # default urls explicitly. - cls_key = cls_name.replace('_Weights', '').lower() - model_urls[f'{cls_key}.default'] = cls.DEFAULT.url - for weight_enum in cls: - cls_key = cls_name.replace('_Weights', '').lower() - cls_key = f'{cls_key}.{weight_enum.name.lower()}' - model_urls[cls_key] = weight_enum.url - - return model_urls - - -def get_external_models(): - mmcv_home = _get_mmcv_home() - default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') - default_urls = load_file(default_json_path) - assert isinstance(default_urls, dict) - external_json_path = osp.join(mmcv_home, 'open_mmlab.json') - if osp.exists(external_json_path): - external_urls = load_file(external_json_path) - assert isinstance(external_urls, dict) - default_urls.update(external_urls) - - return default_urls - - -def get_mmcls_models(): - mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') - mmcls_urls = load_file(mmcls_json_path) - - return mmcls_urls - - -def get_deprecated_model_names(): - deprecate_json_path = osp.join(mmcv.__path__[0], - 'model_zoo/deprecated.json') - deprecate_urls = load_file(deprecate_json_path) - assert isinstance(deprecate_urls, dict) - - return deprecate_urls - - -def _process_mmcls_checkpoint(checkpoint): - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - # Some checkpoints converted from 3rd-party repo don't - # have the "state_dict" key. - state_dict = checkpoint - new_state_dict = OrderedDict() - for k, v in state_dict.items(): - if k.startswith('backbone.'): - new_state_dict[k[9:]] = v - new_checkpoint = dict(state_dict=new_state_dict) - - return new_checkpoint - - -class CheckpointLoader: - """A general checkpoint loader to manage all schemes.""" - - _schemes = {} - - @classmethod - def _register_scheme(cls, prefixes, loader, force=False): - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if (prefix not in cls._schemes) or force: - cls._schemes[prefix] = loader - else: - raise KeyError( - f'{prefix} is already registered as a loader backend, ' - 'add "force=True" if you want to override it') - # sort, longer prefixes take priority - cls._schemes = OrderedDict( - sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) - - @classmethod - def register_scheme(cls, prefixes, loader=None, force=False): - """Register a loader to CheckpointLoader. - - This method can be used as a normal class method or a decorator. - - Args: - prefixes (str or list[str] or tuple[str]): - The prefix of the registered loader. - loader (function, optional): The loader function to be registered. - When this method is used as a decorator, loader is None. - Defaults to None. - force (bool, optional): Whether to override the loader - if the prefix has already been registered. Defaults to False. - """ - - if loader is not None: - cls._register_scheme(prefixes, loader, force=force) - return - - def _register(loader_cls): - cls._register_scheme(prefixes, loader_cls, force=force) - return loader_cls - - return _register - - @classmethod - def _get_checkpoint_loader(cls, path): - """Finds a loader that supports the given path. Falls back to the local - loader if no other loader is found. - - Args: - path (str): checkpoint path - - Returns: - callable: checkpoint loader - """ - for p in cls._schemes: - # use regular match to handle some cases that where the prefix of - # loader has a prefix. For example, both 's3://path' and - # 'open-mmlab:s3://path' should return `load_from_ceph` - if re.match(p, path) is not None: - return cls._schemes[p] - - @classmethod - def load_checkpoint(cls, filename, map_location=None, logger=None): - """load checkpoint through URL scheme path. - - Args: - filename (str): checkpoint file name with given prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - logger (:mod:`logging.Logger`, optional): The logger for message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint_loader = cls._get_checkpoint_loader(filename) - class_name = checkpoint_loader.__name__ - mmcv.print_log( - f'load checkpoint from {class_name[10:]} path: {filename}', logger) - return checkpoint_loader(filename, map_location) - - -@CheckpointLoader.register_scheme(prefixes='') -def load_from_local(filename, map_location): - """load checkpoint by local file path. - - Args: - filename (str): local checkpoint file path - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - filename = osp.expanduser(filename) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) -def load_from_http(filename, map_location=None, model_dir=None): - """load checkpoint through HTTP or HTTPS scheme path. In distributed - setting, this function only download checkpoint at local rank 0. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - model_dir (string, optional): directory in which to save the object, - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - rank, world_size = get_dist_info() - if rank == 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - if world_size > 1: - torch.distributed.barrier() - if rank > 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='pavi://') -def load_from_pavi(filename, map_location=None): - """load checkpoint through the file path prefixed with pavi. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with pavi prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - assert filename.startswith('pavi://'), \ - f'Expected filename startswith `pavi://`, but get {filename}' - model_path = filename[7:] - - try: - from pavi import modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - - model = modelcloud.get(model_path) - with TemporaryDirectory() as tmp_dir: - downloaded_file = osp.join(tmp_dir, model.name) - model.download(downloaded_file) - checkpoint = torch.load(downloaded_file, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=r'(\S+\:)?s3://') -def load_from_ceph(filename, map_location=None, backend='petrel'): - """load checkpoint through the file path prefixed with s3. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Note: - Since v1.4.1, the registered scheme prefixes have been enhanced to - support bucket names in the path prefix, e.g. 's3://xx.xx/xx.path', - 'bucket1:s3://xx.xx/xx.path'. - - Args: - filename (str): checkpoint file path with s3 prefix - map_location (str, optional): Same as :func:`torch.load`. - backend (str, optional): The storage backend type. Options are 'ceph', - 'petrel'. Default: 'petrel'. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - allowed_backends = ['ceph', 'petrel'] - if backend not in allowed_backends: - raise ValueError(f'Load from Backend {backend} is not supported.') - - if backend == 'ceph': - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - - # CephClient and PetrelBackend have the same prefix 's3://' and the latter - # will be chosen as default. If PetrelBackend can not be instantiated - # successfully, the CephClient will be chosen. - try: - file_client = FileClient(backend=backend) - except ImportError: - allowed_backends.remove(backend) - file_client = FileClient(backend=allowed_backends[0]) - - with io.BytesIO(file_client.get(filename)) as buffer: - checkpoint = torch.load(buffer, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) -def load_from_torchvision(filename, map_location=None): - """load checkpoint through the file path prefixed with modelzoo or - torchvision. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - model_urls = get_torchvision_models() - if filename.startswith('modelzoo://'): - warnings.warn( - 'The URL scheme of "modelzoo://" is deprecated, please ' - 'use "torchvision://" instead', DeprecationWarning) - model_name = filename[11:] - else: - model_name = filename[14:] - - # Support getting model urls in the same way as torchvision - # `ResNet50_Weights.IMAGENET1K_V1` will be mapped to - # resnet50.imagenet1k_v1. - model_name = model_name.lower().replace('_weights', '') - return load_from_http(model_urls[model_name], map_location=map_location) - - -@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) -def load_from_openmmlab(filename, map_location=None): - """load checkpoint through the file path prefixed with open-mmlab or - openmmlab. - - Args: - filename (str): checkpoint file path with open-mmlab or - openmmlab prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_external_models() - prefix_str = 'open-mmlab://' - if filename.startswith(prefix_str): - model_name = filename[13:] - else: - model_name = filename[12:] - prefix_str = 'openmmlab://' - - deprecated_urls = get_deprecated_model_names() - if model_name in deprecated_urls: - warnings.warn( - f'{prefix_str}{model_name} is deprecated in favor ' - f'of {prefix_str}{deprecated_urls[model_name]}', - DeprecationWarning) - model_name = deprecated_urls[model_name] - model_url = model_urls[model_name] - # check if is url - if model_url.startswith(('http://', 'https://')): - checkpoint = load_from_http(model_url, map_location=map_location) - else: - filename = osp.join(_get_mmcv_home(), model_url) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='mmcls://') -def load_from_mmcls(filename, map_location=None): - """load checkpoint through the file path prefixed with mmcls. - - Args: - filename (str): checkpoint file path with mmcls prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_mmcls_models() - model_name = filename[8:] - checkpoint = load_from_http( - model_urls[model_name], map_location=map_location) - checkpoint = _process_mmcls_checkpoint(checkpoint) - return checkpoint - - -def _load_checkpoint(filename, map_location=None, logger=None): - """Load checkpoint from somewhere (modelzoo, file, url). - - Args: - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str, optional): Same as :func:`torch.load`. - Default: None. - logger (:mod:`logging.Logger`, optional): The logger for error message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. It can be either an - OrderedDict storing model weights or a dict containing other - information, which depends on the checkpoint. - """ - return CheckpointLoader.load_checkpoint(filename, map_location, logger) - - -def _load_checkpoint_with_prefix(prefix, filename, map_location=None): - """Load partial pretrained model with specific prefix. - - Args: - prefix (str): The prefix of sub-module. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str | None): Same as :func:`torch.load`. Default: None. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint = _load_checkpoint(filename, map_location=map_location) - - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - if not prefix.endswith('.'): - prefix += '.' - prefix_len = len(prefix) - - state_dict = { - k[prefix_len:]: v - for k, v in state_dict.items() if k.startswith(prefix) - } - - assert state_dict, f'{prefix} is not in the pretrained model' - return state_dict - - -def load_checkpoint(model, - filename, - map_location=None, - strict=False, - logger=None, - revise_keys=[(r'^module\.', '')]): - """Load checkpoint from a file or URI. - - Args: - model (Module): Module to load checkpoint. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str): Same as :func:`torch.load`. - strict (bool): Whether to allow different params for the model and - checkpoint. - logger (:mod:`logging.Logger` or None): The logger for error message. - revise_keys (list): A list of customized keywords to modify the - state_dict in checkpoint. Each item is a (pattern, replacement) - pair of the regular expression operations. Default: strip - the prefix 'module.' by [(r'^module\\.', '')]. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - checkpoint = _load_checkpoint(filename, map_location, logger) - # OrderedDict is a subclass of dict - if not isinstance(checkpoint, dict): - raise RuntimeError( - f'No state_dict found in checkpoint file {filename}') - # get state_dict from checkpoint - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - - # strip prefix of state_dict - metadata = getattr(state_dict, '_metadata', OrderedDict()) - for p, r in revise_keys: - state_dict = OrderedDict( - {re.sub(p, r, k): v - for k, v in state_dict.items()}) - # Keep metadata in state_dict - state_dict._metadata = metadata - - # load state_dict - load_state_dict(model, state_dict, strict, logger) - return checkpoint - - -def weights_to_cpu(state_dict): - """Copy a model state_dict to cpu. - - Args: - state_dict (OrderedDict): Model weights on GPU. - - Returns: - OrderedDict: Model weights on GPU. - """ - state_dict_cpu = OrderedDict() - for key, val in state_dict.items(): - state_dict_cpu[key] = val.cpu() - # Keep metadata in state_dict - state_dict_cpu._metadata = getattr(state_dict, '_metadata', OrderedDict()) - return state_dict_cpu - - -def _save_to_state_dict(module, destination, prefix, keep_vars): - """Saves module state to `destination` dictionary. - - This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. - - Args: - module (nn.Module): The module to generate state_dict. - destination (dict): A dict where state will be stored. - prefix (str): The prefix for parameters and buffers used in this - module. - """ - for name, param in module._parameters.items(): - if param is not None: - destination[prefix + name] = param if keep_vars else param.detach() - for name, buf in module._buffers.items(): - # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d - if buf is not None: - destination[prefix + name] = buf if keep_vars else buf.detach() - - -def get_state_dict(module, destination=None, prefix='', keep_vars=False): - """Returns a dictionary containing a whole state of the module. - - Both parameters and persistent buffers (e.g. running averages) are - included. Keys are corresponding parameter and buffer names. - - This method is modified from :meth:`torch.nn.Module.state_dict` to - recursively check parallel module in case that the model has a complicated - structure, e.g., nn.Module(nn.Module(DDP)). - - Args: - module (nn.Module): The module to generate state_dict. - destination (OrderedDict): Returned dict for the state of the - module. - prefix (str): Prefix of the key. - keep_vars (bool): Whether to keep the variable property of the - parameters. Default: False. - - Returns: - dict: A dictionary containing a whole state of the module. - """ - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - - # below is the same as torch.nn.Module.state_dict() - if destination is None: - destination = OrderedDict() - destination._metadata = OrderedDict() - destination._metadata[prefix[:-1]] = local_metadata = dict( - version=module._version) - _save_to_state_dict(module, destination, prefix, keep_vars) - for name, child in module._modules.items(): - if child is not None: - get_state_dict( - child, destination, prefix + name + '.', keep_vars=keep_vars) - for hook in module._state_dict_hooks.values(): - hook_result = hook(module, destination, prefix, local_metadata) - if hook_result is not None: - destination = hook_result - return destination - - -def save_checkpoint(model, - filename, - optimizer=None, - meta=None, - file_client_args=None): - """Save checkpoint to file. - - The checkpoint will have 3 fields: ``meta``, ``state_dict`` and - ``optimizer``. By default ``meta`` will contain version and time info. - - Args: - model (Module): Module whose params are to be saved. - filename (str): Checkpoint filename. - optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. - meta (dict, optional): Metadata to be saved in checkpoint. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError(f'meta must be a dict or None, but got {type(meta)}') - meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) - - if is_module_wrapper(model): - model = model.module - - if hasattr(model, 'CLASSES') and model.CLASSES is not None: - # save class name to the meta - meta.update(CLASSES=model.CLASSES) - - checkpoint = { - 'meta': meta, - 'state_dict': weights_to_cpu(get_state_dict(model)) - } - # save optimizer state dict in the checkpoint - if isinstance(optimizer, Optimizer): - checkpoint['optimizer'] = optimizer.state_dict() - elif isinstance(optimizer, dict): - checkpoint['optimizer'] = {} - for name, optim in optimizer.items(): - checkpoint['optimizer'][name] = optim.state_dict() - - if filename.startswith('pavi://'): - if file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" if filename starts with' - f'"pavi://", but got {file_client_args}') - try: - from pavi import exception, modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - model_path = filename[7:] - root = modelcloud.Folder() - model_dir, model_name = osp.split(model_path) - try: - model = modelcloud.get(model_dir) - except exception.NodeNotFoundError: - model = root.create_training_model(model_dir) - with TemporaryDirectory() as tmp_dir: - checkpoint_file = osp.join(tmp_dir, model_name) - with open(checkpoint_file, 'wb') as f: - torch.save(checkpoint, f) - f.flush() - model.create_file(checkpoint_file, name=model_name) - else: - file_client = FileClient.infer_client(file_client_args, filename) - with io.BytesIO() as f: - torch.save(checkpoint, f) - file_client.put(f.getvalue(), filename) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/default_constructor.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/default_constructor.py deleted file mode 100644 index 4a4f2cc64..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/default_constructor.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import RUNNER_BUILDERS, RUNNERS - - -@RUNNER_BUILDERS.register_module() -class DefaultRunnerConstructor: - """Default constructor for runners. - - Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. - For example, We can inject some new properties and functions for `Runner`. - - Example: - >>> from mmcv.runner import RUNNER_BUILDERS, build_runner - >>> # Define a new RunnerReconstructor - >>> @RUNNER_BUILDERS.register_module() - >>> class MyRunnerConstructor: - ... def __init__(self, runner_cfg, default_args=None): - ... if not isinstance(runner_cfg, dict): - ... raise TypeError('runner_cfg should be a dict', - ... f'but got {type(runner_cfg)}') - ... self.runner_cfg = runner_cfg - ... self.default_args = default_args - ... - ... def __call__(self): - ... runner = RUNNERS.build(self.runner_cfg, - ... default_args=self.default_args) - ... # Add new properties for existing runner - ... runner.my_name = 'my_runner' - ... runner.my_function = lambda self: print(self.my_name) - ... ... - >>> # build your runner - >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, - ... constructor='MyRunnerConstructor') - >>> runner = build_runner(runner_cfg) - """ - - def __init__(self, runner_cfg, default_args=None): - if not isinstance(runner_cfg, dict): - raise TypeError('runner_cfg should be a dict', - f'but got {type(runner_cfg)}') - self.runner_cfg = runner_cfg - self.default_args = default_args - - def __call__(self): - return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/dist_utils.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/dist_utils.py deleted file mode 100644 index 26d77a8f9..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/dist_utils.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved -import functools -import os -import socket -import subprocess -from collections import OrderedDict - -import torch -import torch.multiprocessing as mp -from torch import distributed as dist -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import IS_MLU_AVAILABLE - - -def _find_free_port(): - # Copied from https://github.com/facebookresearch/detectron2/blob/main/detectron2/engine/launch.py # noqa: E501 - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - # Binding to port 0 will cause the OS to find an available port for us - sock.bind(('', 0)) - port = sock.getsockname()[1] - sock.close() - # NOTE: there is still a chance the port could be taken by other processes. - return port - - -def _is_free_port(port): - ips = socket.gethostbyname_ex(socket.gethostname())[-1] - ips.append('localhost') - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - return all(s.connect_ex((ip, port)) != 0 for ip in ips) - - -def init_dist(launcher, backend='nccl', **kwargs): - if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') - if launcher == 'pytorch': - _init_dist_pytorch(backend, **kwargs) - elif launcher == 'mpi': - _init_dist_mpi(backend, **kwargs) - elif launcher == 'slurm': - _init_dist_slurm(backend, **kwargs) - else: - raise ValueError(f'Invalid launcher type: {launcher}') - - -def _init_dist_pytorch(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['RANK']) - if IS_MLU_AVAILABLE: - import torch_mlu # noqa: F401 - torch.mlu.set_device(rank) - dist.init_process_group( - backend='cncl', - rank=rank, - world_size=int(os.environ['WORLD_SIZE']), - **kwargs) - else: - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_mpi(backend, **kwargs): - local_rank = int(os.environ['OMPI_COMM_WORLD_LOCAL_RANK']) - torch.cuda.set_device(local_rank) - if 'MASTER_PORT' not in os.environ: - # 29500 is torch.distributed default port - os.environ['MASTER_PORT'] = '29500' - if 'MASTER_ADDR' not in os.environ: - raise KeyError('The environment variable MASTER_ADDR is not set') - os.environ['WORLD_SIZE'] = os.environ['OMPI_COMM_WORLD_SIZE'] - os.environ['RANK'] = os.environ['OMPI_COMM_WORLD_RANK'] - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_slurm(backend, port=None): - """Initialize slurm distributed training environment. - - If argument ``port`` is not specified, then the master port will be system - environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system - environment variable, then a default port ``29500`` will be used. - - Args: - backend (str): Backend of torch.distributed. - port (int, optional): Master port. Defaults to None. - """ - proc_id = int(os.environ['SLURM_PROCID']) - ntasks = int(os.environ['SLURM_NTASKS']) - node_list = os.environ['SLURM_NODELIST'] - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(proc_id % num_gpus) - addr = subprocess.getoutput( - f'scontrol show hostname {node_list} | head -n1') - # specify master port - if port is not None: - os.environ['MASTER_PORT'] = str(port) - elif 'MASTER_PORT' in os.environ: - pass # use MASTER_PORT in the environment variable - else: - # if torch.distributed default port(29500) is available - # then use it, else find a free port - if _is_free_port(29500): - os.environ['MASTER_PORT'] = '29500' - else: - os.environ['MASTER_PORT'] = str(_find_free_port()) - # use MASTER_ADDR in the environment variable if it already exists - if 'MASTER_ADDR' not in os.environ: - os.environ['MASTER_ADDR'] = addr - os.environ['WORLD_SIZE'] = str(ntasks) - os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) - os.environ['RANK'] = str(proc_id) - dist.init_process_group(backend=backend) - - -def get_dist_info(): - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - world_size = dist.get_world_size() - else: - rank = 0 - world_size = 1 - return rank, world_size - - -def master_only(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - rank, _ = get_dist_info() - if rank == 0: - return func(*args, **kwargs) - - return wrapper - - -def allreduce_params(params, coalesce=True, bucket_size_mb=-1): - """Allreduce parameters. - - Args: - params (list[torch.Parameters]): List of parameters or buffers of a - model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - _, world_size = get_dist_info() - if world_size == 1: - return - params = [param.data for param in params] - if coalesce: - _allreduce_coalesced(params, world_size, bucket_size_mb) - else: - for tensor in params: - dist.all_reduce(tensor.div_(world_size)) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - _, world_size = get_dist_info() - if world_size == 1: - return - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/epoch_based_runner.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/epoch_based_runner.py deleted file mode 100644 index 078e91df3..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/epoch_based_runner.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .utils import get_host_info - - -@RUNNERS.register_module() -class EpochBasedRunner(BaseRunner): - """Epoch-based Runner. - - This runner train models epoch by epoch. - """ - - def run_iter(self, data_batch, train_mode, **kwargs): - if self.batch_processor is not None: - outputs = self.batch_processor( - self.model, data_batch, train_mode=train_mode, **kwargs) - elif train_mode: - outputs = self.model.train_step(data_batch, self.optimizer, - **kwargs) - else: - outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('"batch_processor()" or "model.train_step()"' - 'and "model.val_step()" must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - self._iter += 1 - - self.call_hook('after_train_epoch') - self._epoch += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - self.call_hook('before_val_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_val_iter') - self.run_iter(data_batch, train_mode=False) - self.call_hook('after_val_iter') - - self.call_hook('after_val_epoch') - - def run(self, data_loaders, workflow, max_epochs=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, epochs) to specify the - running order and epochs. E.g, [('train', 2), ('val', 1)] means - running 2 epochs for training and 1 epoch for validation, - iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_epochs is not None: - warnings.warn( - 'setting max_epochs in run is deprecated, ' - 'please set max_epochs in runner_config', DeprecationWarning) - self._max_epochs = max_epochs - - assert self._max_epochs is not None, ( - 'max_epochs must be specified during instantiation') - - for i, flow in enumerate(workflow): - mode, epochs = flow - if mode == 'train': - self._max_iters = self._max_epochs * len(data_loaders[i]) - break - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d epochs', workflow, - self._max_epochs) - self.call_hook('before_run') - - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - mode, epochs = flow - if isinstance(mode, str): # self.train() - if not hasattr(self, mode): - raise ValueError( - f'runner has no method named "{mode}" to run an ' - 'epoch') - epoch_runner = getattr(self, mode) - else: - raise TypeError( - 'mode in workflow must be a str, but got {}'.format( - type(mode))) - - for _ in range(epochs): - if mode == 'train' and self.epoch >= self._max_epochs: - break - epoch_runner(data_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_run') - - def save_checkpoint(self, - out_dir, - filename_tmpl='epoch_{}.pth', - save_optimizer=True, - meta=None, - create_symlink=True): - """Save the checkpoint. - - Args: - out_dir (str): The directory that checkpoints are saved. - filename_tmpl (str, optional): The checkpoint filename template, - which contains a placeholder for the epoch number. - Defaults to 'epoch_{}.pth'. - save_optimizer (bool, optional): Whether to save the optimizer to - the checkpoint. Defaults to True. - meta (dict, optional): The meta information to be saved in the - checkpoint. Defaults to None. - create_symlink (bool, optional): Whether to create a symlink - "latest.pth" to point to the latest checkpoint. - Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.epoch + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - -@RUNNERS.register_module() -class Runner(EpochBasedRunner): - """Deprecated name of EpochBasedRunner.""" - - def __init__(self, *args, **kwargs): - warnings.warn( - 'Runner was deprecated, please use EpochBasedRunner instead', - DeprecationWarning) - super().__init__(*args, **kwargs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/fp16_utils.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/fp16_utils.py deleted file mode 100644 index be3ac3a31..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/fp16_utils.py +++ /dev/null @@ -1,423 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import warnings -from collections import abc -from inspect import getfullargspec - -import numpy as np -import torch -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .dist_utils import allreduce_grads as _allreduce_grads - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 - # manually, so the behavior may not be consistent with real amp. - from torch.cuda.amp import autocast -except ImportError: - pass - - -def cast_tensor_type(inputs, src_type, dst_type): - """Recursively convert Tensor in inputs from src_type to dst_type. - - Note: - In v1.4.4 and later, ``cast_tersor_type`` will only convert the - torch.Tensor which is consistent with ``src_type`` to the ``dst_type``. - Before v1.4.4, it ignores the ``src_type`` argument, leading to some - potential problems. For example, - ``cast_tensor_type(inputs, torch.float, torch.half)`` will convert all - tensors in inputs to ``torch.half`` including those originally in - ``torch.Int`` or other types, which is not expected. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype): Source type.. - dst_type (torch.dtype): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - if isinstance(inputs, nn.Module): - return inputs - elif isinstance(inputs, torch.Tensor): - # we need to ensure that the type of inputs to be casted are the same - # as the argument `src_type`. - return inputs.to(dst_type) if inputs.dtype == src_type else inputs - elif isinstance(inputs, str): - return inputs - elif isinstance(inputs, np.ndarray): - return inputs - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type, dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type, dst_type) for item in inputs) - else: - return inputs - - -def auto_fp16(apply_to=None, out_fp32=False, supported_types=(nn.Module, )): - """Decorator to enable fp16 training automatically. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If inputs arguments are fp32 tensors, they will - be converted to fp16 automatically. Arguments other than fp32 tensors are - ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp32 (bool): Whether to convert the output back to fp32. - supported_types (tuple): Classes can be decorated by ``auto_fp16``. - `New in version 1.5.0.` - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp16 - >>> @auto_fp16() - >>> def forward(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp16 - >>> @auto_fp16(apply_to=('pred', )) - >>> def do_something(self, pred, others): - >>> pass - """ - - def auto_fp16_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], supported_types): - raise TypeError('@auto_fp16 can only be used to decorate the ' - f'method of those classes {supported_types}') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - # NOTE: default args are not taken into consideration - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.float, torch.half)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = {} - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.float, torch.half) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=True): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp32: - output = cast_tensor_type(output, torch.half, torch.float) - return output - - return new_func - - return auto_fp16_wrapper - - -def force_fp32(apply_to=None, out_fp16=False): - """Decorator to convert input arguments to fp32 in force. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If there are some inputs that must be processed - in fp32 mode, then this decorator can handle it. If inputs arguments are - fp16 tensors, they will be converted to fp32 automatically. Arguments other - than fp16 tensors are ignored. If you are using PyTorch >= 1.6, - torch.cuda.amp is used as the backend, otherwise, original mmcv - implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp16 (bool): Whether to convert the output back to fp16. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp32 - >>> @force_fp32() - >>> def loss(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp32 - >>> @force_fp32(apply_to=('pred', )) - >>> def post_process(self, pred, others): - >>> pass - """ - - def force_fp32_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@force_fp32 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.half, torch.float)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = dict() - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.half, torch.float) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=False): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp16: - output = cast_tensor_type(output, torch.float, torch.half) - return output - - return new_func - - return force_fp32_wrapper - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - warnings.warning( - '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' - 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads', - DeprecationWarning) - _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) - - -def wrap_fp16_model(model): - """Wrap the FP32 model to FP16. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - For PyTorch >= 1.6, this function will - 1. Set fp16 flag inside the model to True. - - Otherwise: - 1. Convert FP32 model to FP16. - 2. Remain some necessary layers to be FP32, e.g., normalization layers. - 3. Set `fp16_enabled` flag inside the model to True. - - Args: - model (nn.Module): Model in FP32. - """ - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.6.0')): - # convert model to fp16 - model.half() - # patch the normalization layers to make it work in fp32 mode - patch_norm_fp32(model) - # set `fp16_enabled` flag - for m in model.modules(): - if hasattr(m, 'fp16_enabled'): - m.fp16_enabled = True - - -def patch_norm_fp32(module): - """Recursively convert normalization layers from FP16 to FP32. - - Args: - module (nn.Module): The modules to be converted in FP16. - - Returns: - nn.Module: The converted module, the normalization layers have been - converted to FP32. - """ - if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): - module.float() - if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': - module.forward = patch_forward_method(module.forward, torch.half, - torch.float) - for child in module.children(): - patch_norm_fp32(child) - return module - - -def patch_forward_method(func, src_type, dst_type, convert_output=True): - """Patch the forward method of a module. - - Args: - func (callable): The original forward method. - src_type (torch.dtype): Type of input arguments to be converted from. - dst_type (torch.dtype): Type of input arguments to be converted to. - convert_output (bool): Whether to convert the output back to src_type. - - Returns: - callable: The patched forward method. - """ - - def new_forward(*args, **kwargs): - output = func(*cast_tensor_type(args, src_type, dst_type), - **cast_tensor_type(kwargs, src_type, dst_type)) - if convert_output: - output = cast_tensor_type(output, dst_type, src_type) - return output - - return new_forward - - -class LossScaler: - """Class that manages loss scaling in mixed precision training which - supports both dynamic or static mode. - - The implementation refers to - https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. - Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. - It's important to understand how :class:`LossScaler` operates. - Loss scaling is designed to combat the problem of underflowing - gradients encountered at long times when training fp16 networks. - Dynamic loss scaling begins by attempting a very high loss - scale. Ironically, this may result in OVERflowing gradients. - If overflowing gradients are encountered, :class:`FP16_Optimizer` then - skips the update step for this particular iteration/minibatch, - and :class:`LossScaler` adjusts the loss scale to a lower value. - If a certain number of iterations occur without overflowing gradients - detected,:class:`LossScaler` increases the loss scale once more. - In this way :class:`LossScaler` attempts to "ride the edge" of always - using the highest loss scale possible without incurring overflow. - - Args: - init_scale (float): Initial loss scale value, default: 2**32. - scale_factor (float): Factor used when adjusting the loss scale. - Default: 2. - mode (str): Loss scaling mode. 'dynamic' or 'static' - scale_window (int): Number of consecutive iterations without an - overflow to wait before increasing the loss scale. Default: 1000. - """ - - def __init__(self, - init_scale=2**32, - mode='dynamic', - scale_factor=2., - scale_window=1000): - self.cur_scale = init_scale - self.cur_iter = 0 - assert mode in ('dynamic', - 'static'), 'mode can only be dynamic or static' - self.mode = mode - self.last_overflow_iter = -1 - self.scale_factor = scale_factor - self.scale_window = scale_window - - def has_overflow(self, params): - """Check if params contain overflow.""" - if self.mode != 'dynamic': - return False - for p in params: - if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): - return True - return False - - def _has_inf_or_nan(x): - """Check if params contain NaN.""" - try: - cpu_sum = float(x.float().sum()) - except RuntimeError as instance: - if 'value cannot be converted' not in instance.args[0]: - raise - return True - else: - if cpu_sum == float('inf') or cpu_sum == -float('inf') \ - or cpu_sum != cpu_sum: - return True - return False - - def update_scale(self, overflow): - """update the current loss scale value when overflow happens.""" - if self.mode != 'dynamic': - return - if overflow: - self.cur_scale = max(self.cur_scale / self.scale_factor, 1) - self.last_overflow_iter = self.cur_iter - else: - if (self.cur_iter - self.last_overflow_iter) % \ - self.scale_window == 0: - self.cur_scale *= self.scale_factor - self.cur_iter += 1 - - def state_dict(self): - """Returns the state of the scaler as a :class:`dict`.""" - return dict( - cur_scale=self.cur_scale, - cur_iter=self.cur_iter, - mode=self.mode, - last_overflow_iter=self.last_overflow_iter, - scale_factor=self.scale_factor, - scale_window=self.scale_window) - - def load_state_dict(self, state_dict): - """Loads the loss_scaler state dict. - - Args: - state_dict (dict): scaler state. - """ - self.cur_scale = state_dict['cur_scale'] - self.cur_iter = state_dict['cur_iter'] - self.mode = state_dict['mode'] - self.last_overflow_iter = state_dict['last_overflow_iter'] - self.scale_factor = state_dict['scale_factor'] - self.scale_window = state_dict['scale_window'] - - @property - def loss_scale(self): - return self.cur_scale diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/__init__.py deleted file mode 100644 index 03e2a619e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkpoint import CheckpointHook -from .closure import ClosureHook -from .ema import EMAHook -from .evaluation import DistEvalHook, EvalHook -from .hook import HOOKS, Hook -from .iter_timer import IterTimerHook -from .logger import (ClearMLLoggerHook, DvcliveLoggerHook, LoggerHook, - MlflowLoggerHook, NeptuneLoggerHook, PaviLoggerHook, - SegmindLoggerHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, InvLrUpdaterHook, - LinearAnnealingLrUpdaterHook, LrUpdaterHook, - OneCycleLrUpdaterHook, PolyLrUpdaterHook, - StepLrUpdaterHook) -from .memory import EmptyCacheHook -from .momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, OptimizerHook) -from .profiler import ProfilerHook -from .sampler_seed import DistSamplerSeedHook -from .sync_buffer import SyncBuffersHook - -__all__ = [ - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'OptimizerHook', - 'Fp16OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', - 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TextLoggerHook', 'TensorboardLoggerHook', 'NeptuneLoggerHook', - 'WandbLoggerHook', 'DvcliveLoggerHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'SyncBuffersHook', 'EMAHook', 'EvalHook', 'DistEvalHook', 'ProfilerHook', - 'GradientCumulativeOptimizerHook', 'GradientCumulativeFp16OptimizerHook', - 'SegmindLoggerHook', 'LinearAnnealingLrUpdaterHook', - 'LinearAnnealingMomentumUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/checkpoint.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/checkpoint.py deleted file mode 100644 index 7bb75f402..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/checkpoint.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -from mmcv.fileio import FileClient -from ..dist_utils import allreduce_params, master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class CheckpointHook(Hook): - """Save checkpoints periodically. - - Args: - interval (int): The saving period. If ``by_epoch=True``, interval - indicates epochs, otherwise it indicates iterations. - Default: -1, which means "never". - by_epoch (bool): Saving checkpoints by epoch or by iteration. - Default: True. - save_optimizer (bool): Whether to save optimizer state_dict in the - checkpoint. It is usually used for resuming experiments. - Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, ``runner.work_dir`` will be used by default. If - specified, the ``out_dir`` will be the concatenation of ``out_dir`` - and the last level directory of ``runner.work_dir``. - `Changed in version 1.3.16.` - max_keep_ckpts (int, optional): The maximum checkpoints to keep. - In some cases we want only the latest few checkpoints and would - like to delete old ones to save the disk space. - Default: -1, which means unlimited. - save_last (bool, optional): Whether to force the last checkpoint to be - saved regardless of interval. Default: True. - sync_buffer (bool, optional): Whether to synchronize buffers in - different gpus. Default: False. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - - .. warning:: - Before v1.3.16, the ``out_dir`` argument indicates the path where the - checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the - root directory and the final path to save checkpoint is the - concatenation of ``out_dir`` and the last level directory of - ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" - and the value of ``runner.work_dir`` is "/path/of/B", then the final - path will be "/path/of/A/B". - """ - - def __init__(self, - interval=-1, - by_epoch=True, - save_optimizer=True, - out_dir=None, - max_keep_ckpts=-1, - save_last=True, - sync_buffer=False, - file_client_args=None, - **kwargs): - self.interval = interval - self.by_epoch = by_epoch - self.save_optimizer = save_optimizer - self.out_dir = out_dir - self.max_keep_ckpts = max_keep_ckpts - self.save_last = save_last - self.args = kwargs - self.sync_buffer = sync_buffer - self.file_client_args = file_client_args - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - - runner.logger.info((f'Checkpoints will be saved to {self.out_dir} by ' - f'{self.file_client.name}.')) - - # disable the create_symlink option because some file backends do not - # allow to create a symlink - if 'create_symlink' in self.args: - if self.args[ - 'create_symlink'] and not self.file_client.allow_symlink: - self.args['create_symlink'] = False - warnings.warn( - ('create_symlink is set as True by the user but is changed' - 'to be False because creating symbolic link is not ' - f'allowed in {self.file_client.name}')) - else: - self.args['create_symlink'] = self.file_client.allow_symlink - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` epochs - # 2. reach the last epoch of training - if self.every_n_epochs( - runner, self.interval) or (self.save_last - and self.is_last_epoch(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.epoch + 1} epochs') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) - - @master_only - def _save_checkpoint(self, runner): - """Save the current checkpoint and delete unwanted checkpoint.""" - runner.save_checkpoint( - self.out_dir, save_optimizer=self.save_optimizer, **self.args) - if runner.meta is not None: - if self.by_epoch: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) - else: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) - runner.meta.setdefault('hook_msgs', dict()) - runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( - self.out_dir, cur_ckpt_filename) - # remove other checkpoints - if self.max_keep_ckpts > 0: - if self.by_epoch: - name = 'epoch_{}.pth' - current_ckpt = runner.epoch + 1 - else: - name = 'iter_{}.pth' - current_ckpt = runner.iter + 1 - redundant_ckpts = range( - current_ckpt - self.max_keep_ckpts * self.interval, 0, - -self.interval) - filename_tmpl = self.args.get('filename_tmpl', name) - for _step in redundant_ckpts: - ckpt_path = self.file_client.join_path( - self.out_dir, filename_tmpl.format(_step)) - if self.file_client.isfile(ckpt_path): - self.file_client.remove(ckpt_path) - else: - break - - def after_train_iter(self, runner): - if self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` iterations - # 2. reach the last iteration of training - if self.every_n_iters( - runner, self.interval) or (self.save_last - and self.is_last_iter(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.iter + 1} iterations') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/closure.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/closure.py deleted file mode 100644 index b955f81f4..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/closure.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ClosureHook(Hook): - - def __init__(self, fn_name, fn): - assert hasattr(self, fn_name) - assert callable(fn) - setattr(self, fn_name, fn) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/ema.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/ema.py deleted file mode 100644 index 6ed77b84e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/ema.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...parallel import is_module_wrapper -from ..hooks.hook import HOOKS, Hook - - -@HOOKS.register_module() -class EMAHook(Hook): - r"""Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. - - .. math:: - - Xema\_{t+1} = (1 - \text{momentum}) \times - Xema\_{t} + \text{momentum} \times X_t - - Args: - momentum (float): The momentum used for updating ema parameter. - Defaults to 0.0002. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - warm_up (int): During first warm_up steps, we may use smaller momentum - to update ema parameters more slowly. Defaults to 100. - resume_from (str): The checkpoint path. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - warm_up=100, - resume_from=None): - assert isinstance(interval, int) and interval > 0 - self.warm_up = warm_up - self.interval = interval - assert momentum > 0 and momentum < 1 - self.momentum = momentum**interval - self.checkpoint = resume_from - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - self.model_parameters = dict(model.named_parameters(recurse=True)) - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers(recurse=True)) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - curr_step = runner.iter - # We warm up the momentum considering the instability at beginning - momentum = min(self.momentum, - (1 + curr_step) / (self.warm_up + curr_step)) - if curr_step % self.interval != 0: - return - for name, parameter in self.model_parameters.items(): - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/evaluation.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/evaluation.py deleted file mode 100644 index 2a57d535e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/evaluation.py +++ /dev/null @@ -1,511 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from math import inf - -import torch.distributed as dist -from torch.nn.modules.batchnorm import _BatchNorm -from torch.utils.data import DataLoader - -from mmcv.fileio import FileClient -from mmcv.utils import is_seq_of -from .hook import Hook -from .logger import LoggerHook - - -class EvalHook(Hook): - """Non-Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader, and return the test results. If ``None``, the default - test function ``mmcv.engine.single_gpu_test`` will be used. - (default: ``None``) - greater_keys (List[str] | None, optional): Metric keys that will be - inferred by 'greater' comparison rule. If ``None``, - _default_greater_keys will be used. (default: ``None``) - less_keys (List[str] | None, optional): Metric keys that will be - inferred by 'less' comparison rule. If ``None``, _default_less_keys - will be used. (default: ``None``) - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - `New in version 1.3.16.` - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - `New in version 1.3.16.` - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - - Note: - If new arguments are added for EvalHook, tools/test.py, - tools/eval_metric.py may be affected. - """ - - # Since the key for determine greater or less is related to the downstream - # tasks, downstream repos may need to overwrite the following inner - # variable accordingly. - - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - _default_greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - _default_less_keys = ['loss'] - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - out_dir=None, - file_client_args=None, - **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError(f'dataloader must be a pytorch DataLoader, ' - f'but got {type(dataloader)}') - - if interval <= 0: - raise ValueError(f'interval must be a positive number, ' - f'but got {interval}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' - - if start is not None and start < 0: - raise ValueError(f'The evaluation start epoch {start} is smaller ' - f'than 0') - - self.dataloader = dataloader - self.interval = interval - self.start = start - self.by_epoch = by_epoch - - assert isinstance(save_best, str) or save_best is None, \ - '""save_best"" should be a str or None ' \ - f'rather than {type(save_best)}' - self.save_best = save_best - self.eval_kwargs = eval_kwargs - self.initial_flag = True - - if test_fn is None: - from mmcv.engine import single_gpu_test - self.test_fn = single_gpu_test - else: - self.test_fn = test_fn - - if greater_keys is None: - self.greater_keys = self._default_greater_keys - else: - if not isinstance(greater_keys, (list, tuple)): - greater_keys = (greater_keys, ) - assert is_seq_of(greater_keys, str) - self.greater_keys = greater_keys - - if less_keys is None: - self.less_keys = self._default_less_keys - else: - if not isinstance(less_keys, (list, tuple)): - less_keys = (less_keys, ) - assert is_seq_of(less_keys, str) - self.less_keys = less_keys - - if self.save_best is not None: - self.best_ckpt_path = None - self._init_rule(rule, self.save_best) - - self.out_dir = out_dir - self.file_client_args = file_client_args - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific (note that the key indicator matching - is case-insensitive): - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator != 'auto': - # `_lc` here means we use the lower case of keys for - # case-insensitive matching - key_indicator_lc = key_indicator.lower() - greater_keys = [key.lower() for key in self.greater_keys] - less_keys = [key.lower() for key in self.less_keys] - - if key_indicator_lc in greater_keys: - rule = 'greater' - elif key_indicator_lc in less_keys: - rule = 'less' - elif any(key in key_indicator_lc for key in greater_keys): - rule = 'greater' - elif any(key in key_indicator_lc for key in less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - if self.rule is not None: - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'The best checkpoint will be saved to {self.out_dir} by ' - f'{self.file_client.name}')) - - if self.save_best is not None: - if runner.meta is None: - warnings.warn('runner.meta is None. Creating an empty one.') - runner.meta = dict() - runner.meta.setdefault('hook_msgs', dict()) - self.best_ckpt_path = runner.meta['hook_msgs'].get( - 'best_ckpt', None) - - def before_train_iter(self, runner): - """Evaluate the model only at the start of training by iteration.""" - if self.by_epoch or not self.initial_flag: - return - if self.start is not None and runner.iter >= self.start: - self.after_train_iter(runner) - self.initial_flag = False - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - if not (self.by_epoch and self.initial_flag): - return - if self.start is not None and runner.epoch >= self.start: - self.after_train_epoch(runner) - self.initial_flag = False - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch and self._should_evaluate(runner): - # Because the priority of EvalHook is higher than LoggerHook, the - # training log and the evaluating log are mixed. Therefore, - # we need to dump the training log and clear it before evaluating - # log is generated. In addition, this problem will only appear in - # `IterBasedRunner` whose `self.by_epoch` is False, because - # `EpochBasedRunner` whose `self.by_epoch` is True calls - # `_do_evaluate` in `after_train_epoch` stage, and at this stage - # the training log has been printed, so it will not cause any - # problem. more details at - # https://github.com/open-mmlab/mmsegmentation/issues/694 - for hook in runner._hooks: - if isinstance(hook, LoggerHook): - hook.after_train_iter(runner) - runner.log_buffer.clear() - - self._do_evaluate(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch and self._should_evaluate(runner): - self._do_evaluate(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - results = self.test_fn(runner.model, self.dataloader) - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - def _should_evaluate(self, runner): - """Judge whether to perform evaluation. - - Here is the rule to judge whether to perform evaluation: - 1. It will not perform evaluation during the epoch/iteration interval, - which is determined by ``self.interval``. - 2. It will not perform evaluation if the start time is larger than - current time. - 3. It will not perform evaluation when current time is larger than - the start time but during epoch/iteration interval. - - Returns: - bool: The flag indicating whether to perform evaluation. - """ - if self.by_epoch: - current = runner.epoch - check_time = self.every_n_epochs - else: - current = runner.iter - check_time = self.every_n_iters - - if self.start is None: - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - elif (current + 1) < self.start: - # No evaluation if start is larger than the current time. - return False - else: - # Evaluation only at epochs/iters 3, 5, 7... - # if start==3 and interval==2 - if (current + 1 - self.start) % self.interval: - return False - return True - - def _save_ckpt(self, runner, key_score): - """Save the best checkpoint. - - It will compare the score according to the compare function, write - related information (best score, best checkpoint path) and save the - best checkpoint into ``work_dir``. - """ - if self.by_epoch: - current = f'epoch_{runner.epoch + 1}' - cur_type, cur_time = 'epoch', runner.epoch + 1 - else: - current = f'iter_{runner.iter + 1}' - cur_type, cur_time = 'iter', runner.iter + 1 - - best_score = runner.meta['hook_msgs'].get( - 'best_score', self.init_value_map[self.rule]) - if self.compare_func(key_score, best_score): - best_score = key_score - runner.meta['hook_msgs']['best_score'] = best_score - - if self.best_ckpt_path and self.file_client.isfile( - self.best_ckpt_path): - self.file_client.remove(self.best_ckpt_path) - runner.logger.info( - (f'The previous best checkpoint {self.best_ckpt_path} was ' - 'removed')) - - best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' - self.best_ckpt_path = self.file_client.join_path( - self.out_dir, best_ckpt_name) - runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path - - runner.save_checkpoint( - self.out_dir, - filename_tmpl=best_ckpt_name, - create_symlink=False) - runner.logger.info( - f'Now best checkpoint is saved as {best_ckpt_name}.') - runner.logger.info( - f'Best {self.key_indicator} is {best_score:0.4f} ' - f'at {cur_time} {cur_type}.') - - def evaluate(self, runner, results): - """Evaluate the results. - - Args: - runner (:obj:`mmcv.Runner`): The underlined training runner. - results (list): Output results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - - if self.save_best is not None: - # If the performance of model is pool, the `eval_res` may be an - # empty dict and it will raise exception when `self.save_best` is - # not None. More details at - # https://github.com/open-mmlab/mmdetection/issues/6265. - if not eval_res: - warnings.warn( - 'Since `eval_res` is an empty dict, the behavior to save ' - 'the best checkpoint will be skipped in this evaluation.') - return None - - if self.key_indicator == 'auto': - # infer from eval_results - self._init_rule(self.rule, list(eval_res.keys())[0]) - return eval_res[self.key_indicator] - - return None - - -class DistEvalHook(EvalHook): - """Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader in a multi-gpu manner, and return the test results. If - ``None``, the default test function ``mmcv.engine.multi_gpu_test`` - will be used. (default: ``None``) - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - broadcast_bn_buffer (bool): Whether to broadcast the - buffer(running_mean and running_var) of rank 0 to other rank - before evaluation. Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - """ - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - broadcast_bn_buffer=True, - tmpdir=None, - gpu_collect=False, - out_dir=None, - file_client_args=None, - **eval_kwargs): - - if test_fn is None: - from mmcv.engine import multi_gpu_test - test_fn = multi_gpu_test - - super().__init__( - dataloader, - start=start, - interval=interval, - by_epoch=by_epoch, - save_best=save_best, - rule=rule, - test_fn=test_fn, - greater_keys=greater_keys, - less_keys=less_keys, - out_dir=out_dir, - file_client_args=file_client_args, - **eval_kwargs) - - self.broadcast_bn_buffer = broadcast_bn_buffer - self.tmpdir = tmpdir - self.gpu_collect = gpu_collect - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - results = self.test_fn( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to - # save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/hook.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/hook.py deleted file mode 100644 index f2d1c9865..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/hook.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, is_method_overridden - -HOOKS = Registry('hook') - - -class Hook: - stages = ('before_run', 'before_train_epoch', 'before_train_iter', - 'after_train_iter', 'after_train_epoch', 'before_val_epoch', - 'before_val_iter', 'after_val_iter', 'after_val_epoch', - 'after_run') - - def before_run(self, runner): - pass - - def after_run(self, runner): - pass - - def before_epoch(self, runner): - pass - - def after_epoch(self, runner): - pass - - def before_iter(self, runner): - pass - - def after_iter(self, runner): - pass - - def before_train_epoch(self, runner): - self.before_epoch(runner) - - def before_val_epoch(self, runner): - self.before_epoch(runner) - - def after_train_epoch(self, runner): - self.after_epoch(runner) - - def after_val_epoch(self, runner): - self.after_epoch(runner) - - def before_train_iter(self, runner): - self.before_iter(runner) - - def before_val_iter(self, runner): - self.before_iter(runner) - - def after_train_iter(self, runner): - self.after_iter(runner) - - def after_val_iter(self, runner): - self.after_iter(runner) - - def every_n_epochs(self, runner, n): - return (runner.epoch + 1) % n == 0 if n > 0 else False - - def every_n_inner_iters(self, runner, n): - return (runner.inner_iter + 1) % n == 0 if n > 0 else False - - def every_n_iters(self, runner, n): - return (runner.iter + 1) % n == 0 if n > 0 else False - - def end_of_epoch(self, runner): - return runner.inner_iter + 1 == len(runner.data_loader) - - def is_last_epoch(self, runner): - return runner.epoch + 1 == runner._max_epochs - - def is_last_iter(self, runner): - return runner.iter + 1 == runner._max_iters - - def get_triggered_stages(self): - trigger_stages = set() - for stage in Hook.stages: - if is_method_overridden(stage, Hook, self): - trigger_stages.add(stage) - - # some methods will be triggered in multi stages - # use this dict to map method to stages. - method_stages_map = { - 'before_epoch': ['before_train_epoch', 'before_val_epoch'], - 'after_epoch': ['after_train_epoch', 'after_val_epoch'], - 'before_iter': ['before_train_iter', 'before_val_iter'], - 'after_iter': ['after_train_iter', 'after_val_iter'], - } - - for method, map_stages in method_stages_map.items(): - if is_method_overridden(method, Hook, self): - trigger_stages.update(map_stages) - - return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/iter_timer.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/iter_timer.py deleted file mode 100644 index 16531be96..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/iter_timer.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import time -import torch.distributed as dist - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class IterTimerHook(Hook): - - def before_epoch(self, runner): - self.t = time.time() - - def before_iter(self, runner): - self.batch_size = runner.cfg.data.samples_per_gpu - runner.log_buffer.update({'data_time': time.time() - self.t}) - - def after_iter(self, runner): - iter_info = {'time': time.time() - self.t} - fps = self.batch_size / iter_info["time"] - - if dist.is_initialized(): - fps = fps * dist.get_world_size() - - iter_info["fps"] = fps - - runner.log_buffer.update(iter_info) - self.t = time.time() diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/__init__.py deleted file mode 100644 index 062709e70..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import LoggerHook -from .clearml import ClearMLLoggerHook -from .dvclive import DvcliveLoggerHook -from .mlflow import MlflowLoggerHook -from .neptune import NeptuneLoggerHook -from .pavi import PaviLoggerHook -from .segmind import SegmindLoggerHook -from .tensorboard import TensorboardLoggerHook -from .text import TextLoggerHook -from .wandb import WandbLoggerHook - -__all__ = [ - 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', - 'NeptuneLoggerHook', 'DvcliveLoggerHook', 'SegmindLoggerHook', - 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/base.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/base.py deleted file mode 100644 index 9f1a51b31..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/base.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from abc import ABCMeta, abstractmethod - -import numpy as np -import torch - -from ..hook import Hook - - -class LoggerHook(Hook): - """Base class for logger hooks. - - Args: - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - """ - - __metaclass__ = ABCMeta - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - self.interval = interval - self.ignore_last = ignore_last - self.reset_flag = reset_flag - self.by_epoch = by_epoch - - @abstractmethod - def log(self, runner): - pass - - @staticmethod - def is_scalar(val, include_np=True, include_torch=True): - """Tell the input variable is a scalar or not. - - Args: - val: Input variable. - include_np (bool): Whether include 0-d np.ndarray as a scalar. - include_torch (bool): Whether include 0-d torch.Tensor as a scalar. - - Returns: - bool: True or False. - """ - if isinstance(val, numbers.Number): - return True - elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: - return True - elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: - return True - else: - return False - - def get_mode(self, runner): - if runner.mode == 'train': - if 'time' in runner.log_buffer.output: - mode = 'train' - else: - mode = 'val' - elif runner.mode == 'val': - mode = 'val' - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return mode - - def get_epoch(self, runner): - if runner.mode == 'train': - epoch = runner.epoch + 1 - elif runner.mode == 'val': - # normal val mode - # runner.epoch += 1 has been done before val workflow - epoch = runner.epoch - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return epoch - - def get_iter(self, runner, inner_iter=False): - """Get the current training iteration step.""" - if self.by_epoch and inner_iter: - current_iter = runner.inner_iter + 1 - else: - current_iter = runner.iter + 1 - return current_iter - - def get_lr_tags(self, runner): - tags = {} - lrs = runner.current_lr() - if isinstance(lrs, dict): - for name, value in lrs.items(): - tags[f'learning_rate/{name}'] = value[0] - else: - tags['learning_rate'] = lrs[0] - return tags - - def get_momentum_tags(self, runner): - tags = {} - momentums = runner.current_momentum() - if isinstance(momentums, dict): - for name, value in momentums.items(): - tags[f'momentum/{name}'] = value[0] - else: - tags['momentum'] = momentums[0] - return tags - - def get_loggable_tags(self, - runner, - allow_scalar=True, - allow_text=False, - add_mode=True, - tags_to_skip=('time', 'data_time')): - tags = {} - for var, val in runner.log_buffer.output.items(): - if var in tags_to_skip: - continue - if self.is_scalar(val) and not allow_scalar: - continue - if isinstance(val, str) and not allow_text: - continue - if add_mode: - var = f'{self.get_mode(runner)}/{var}' - tags[var] = val - tags.update(self.get_lr_tags(runner)) - tags.update(self.get_momentum_tags(runner)) - return tags - - def before_run(self, runner): - for hook in runner.hooks[::-1]: - if isinstance(hook, LoggerHook): - hook.reset_flag = True - break - - def before_epoch(self, runner): - runner.log_buffer.clear() # clear logs of last epoch - - def after_train_iter(self, runner): - if self.by_epoch and self.every_n_inner_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif not self.by_epoch and self.every_n_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif self.end_of_epoch(runner) and not self.ignore_last: - # not precise but more stable - runner.log_buffer.average(self.interval) - - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_train_epoch(self, runner): - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_val_epoch(self, runner): - runner.log_buffer.average() - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/clearml.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/clearml.py deleted file mode 100644 index 1de71e152..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/clearml.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class ClearMLLoggerHook(LoggerHook): - """Class to log metrics with clearml. - - It requires `clearml`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the `clearml.Task.init` - initialization keys. See `taskinit`_ for more details. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _clearml: - https://clear.ml/docs/latest/docs/ - .. _taskinit: - https://clear.ml/docs/latest/docs/references/sdk/task/#taskinit - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(ClearMLLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_clearml() - self.init_kwargs = init_kwargs - - def import_clearml(self): - try: - import clearml - except ImportError: - raise ImportError( - 'Please run "pip install clearml" to install clearml') - self.clearml = clearml - - @master_only - def before_run(self, runner): - super(ClearMLLoggerHook, self).before_run(runner) - task_kwargs = self.init_kwargs if self.init_kwargs else {} - self.task = self.clearml.Task.init(**task_kwargs) - self.task_logger = self.task.get_logger() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - for tag, val in tags.items(): - self.task_logger.report_scalar(tag, tag, val, - self.get_iter(runner)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/dvclive.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/dvclive.py deleted file mode 100644 index c79eefa75..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/dvclive.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from pathlib import Path - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class DvcliveLoggerHook(LoggerHook): - """Class to log metrics with dvclive. - - It requires `dvclive`_ to be installed. - - Args: - model_file (str): Default None. If not None, after each epoch the - model will be saved to {model_file}. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - kwargs: Arguments for instantiating `Live`_. - - .. _dvclive: - https://dvc.org/doc/dvclive - - .. _Live: - https://dvc.org/doc/dvclive/api-reference/live#parameters - """ - - def __init__(self, - model_file=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - **kwargs): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.model_file = model_file - self.import_dvclive(**kwargs) - - def import_dvclive(self, **kwargs): - try: - from dvclive import Live - except ImportError: - raise ImportError( - 'Please run "pip install dvclive" to install dvclive') - self.dvclive = Live(**kwargs) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.dvclive.set_step(self.get_iter(runner)) - for k, v in tags.items(): - self.dvclive.log(k, v) - - @master_only - def after_train_epoch(self, runner): - super().after_train_epoch(runner) - if self.model_file is not None: - runner.save_checkpoint( - Path(self.model_file).parent, - filename_tmpl=Path(self.model_file).name, - create_symlink=False, - ) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/mlflow.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/mlflow.py deleted file mode 100644 index dcd87bcb5..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/mlflow.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import TORCH_VERSION -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class MlflowLoggerHook(LoggerHook): - """Class to log metrics and (optionally) a trained model to MLflow. - - It requires `MLflow`_ to be installed. - - Args: - exp_name (str, optional): Name of the experiment to be used. - Default None. If not None, set the active experiment. - If experiment does not exist, an experiment with provided name - will be created. - tags (Dict[str], optional): Tags for the current run. - Default None. If not None, set tags for the current run. - log_model (bool, optional): Whether to log an MLflow artifact. - Default True. If True, log runner.model as an MLflow artifact - for the current run. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _MLflow: - https://www.mlflow.org/docs/latest/index.html - """ - - def __init__(self, - exp_name=None, - tags=None, - log_model=True, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(MlflowLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_mlflow() - self.exp_name = exp_name - self.tags = tags - self.log_model = log_model - - def import_mlflow(self): - try: - import mlflow - import mlflow.pytorch as mlflow_pytorch - except ImportError: - raise ImportError( - 'Please run "pip install mlflow" to install mlflow') - self.mlflow = mlflow - self.mlflow_pytorch = mlflow_pytorch - - @master_only - def before_run(self, runner): - super(MlflowLoggerHook, self).before_run(runner) - if self.exp_name is not None: - self.mlflow.set_experiment(self.exp_name) - if self.tags is not None: - self.mlflow.set_tags(self.tags) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.mlflow.log_metrics(tags, step=self.get_iter(runner)) - - @master_only - def after_run(self, runner): - if self.log_model: - self.mlflow_pytorch.log_model( - runner.model, - 'models', - pip_requirements=[f'torch=={TORCH_VERSION}']) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/neptune.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/neptune.py deleted file mode 100644 index e0aafe91d..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/neptune.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class NeptuneLoggerHook(LoggerHook): - """Class to log metrics to NeptuneAI. - - It requires `Neptune`_ to be installed. - - Args: - init_kwargs (dict): a dict contains the initialization keys as below: - - - project (str): Name of a project in a form of - namespace/project_name. If None, the value of NEPTUNE_PROJECT - environment variable will be taken. - - api_token (str): User’s API token. If None, the value of - NEPTUNE_API_TOKEN environment variable will be taken. Note: It is - strongly recommended to use NEPTUNE_API_TOKEN environment - variable rather than placing your API token in plain text in your - source code. - - name (str, optional, default is 'Untitled'): Editable name of the - run. Name is displayed in the run's Details and in Runs table as - a column. - - Check https://docs.neptune.ai/api-reference/neptune#init for more - init arguments. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than ``interval``. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _Neptune: - https://docs.neptune.ai - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=True, - with_step=True, - by_epoch=True): - - super(NeptuneLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_neptune() - self.init_kwargs = init_kwargs - self.with_step = with_step - - def import_neptune(self): - try: - import neptune.new as neptune - except ImportError: - raise ImportError( - 'Please run "pip install neptune-client" to install neptune') - self.neptune = neptune - self.run = None - - @master_only - def before_run(self, runner): - if self.init_kwargs: - self.run = self.neptune.init(**self.init_kwargs) - else: - self.run = self.neptune.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for tag_name, tag_value in tags.items(): - if self.with_step: - self.run[tag_name].log( - tag_value, step=self.get_iter(runner)) - else: - tags['global_step'] = self.get_iter(runner) - self.run[tag_name].log(tags) - - @master_only - def after_run(self, runner): - self.run.stop() diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/pavi.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/pavi.py deleted file mode 100644 index d5d61f9e5..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/pavi.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp - -import torch -import yaml - -import mmcv -from ....parallel.utils import is_module_wrapper -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class PaviLoggerHook(LoggerHook): - """Class to visual model, log metrics (for internal use). - - Args: - init_kwargs (dict): A dict contains the initialization keys. - add_graph (bool): Whether to visual model. Default: False. - add_last_ckpt (bool): Whether to save checkpoint after run. - Default: False. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - img_key (string): Get image data from Dataset. Default: 'img_info'. - """ - - def __init__(self, - init_kwargs=None, - add_graph=False, - add_last_ckpt=False, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - img_key='img_info'): - super(PaviLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.init_kwargs = init_kwargs - self.add_graph = add_graph - self.add_last_ckpt = add_last_ckpt - self.img_key = img_key - - @master_only - def before_run(self, runner): - super(PaviLoggerHook, self).before_run(runner) - try: - from pavi import SummaryWriter - except ImportError: - raise ImportError('Please run "pip install pavi" to install pavi.') - - self.run_name = runner.work_dir.split('/')[-1] - - if not self.init_kwargs: - self.init_kwargs = dict() - self.init_kwargs['name'] = self.run_name - self.init_kwargs['model'] = runner._model_name - if runner.meta is not None: - if 'config_dict' in runner.meta: - config_dict = runner.meta['config_dict'] - assert isinstance( - config_dict, - dict), ('meta["config_dict"] has to be of a dict, ' - f'but got {type(config_dict)}') - elif 'config_file' in runner.meta: - config_file = runner.meta['config_file'] - config_dict = dict(mmcv.Config.fromfile(config_file)) - else: - config_dict = None - if config_dict is not None: - # 'max_.*iter' is parsed in pavi sdk as the maximum iterations - # to properly set up the progress bar. - config_dict = config_dict.copy() - config_dict.setdefault('max_iter', runner.max_iters) - # non-serializable values are first converted in - # mmcv.dump to json - config_dict = json.loads( - mmcv.dump(config_dict, file_format='json')) - session_text = yaml.dump(config_dict) - self.init_kwargs['session_text'] = session_text - self.writer = SummaryWriter(**self.init_kwargs) - - def get_step(self, runner): - """Get the total training step/epoch.""" - if self.get_mode(runner) == 'val' and self.by_epoch: - return self.get_epoch(runner) - else: - return self.get_iter(runner) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, add_mode=False) - if tags: - self.writer.add_scalars( - self.get_mode(runner), tags, self.get_step(runner)) - - @master_only - def after_run(self, runner): - if self.add_last_ckpt: - ckpt_path = osp.join(runner.work_dir, 'latest.pth') - if osp.islink(ckpt_path): - ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) - - if osp.isfile(ckpt_path): - # runner.epoch += 1 has been done before `after_run`. - iteration = runner.epoch if self.by_epoch else runner.iter - return self.writer.add_snapshot_file( - tag=self.run_name, - snapshot_file_path=ckpt_path, - iteration=iteration) - - # flush the buffer and send a task ending signal to Pavi - self.writer.close() - - @master_only - def before_epoch(self, runner): - if runner.epoch == 0 and self.add_graph: - if is_module_wrapper(runner.model): - _model = runner.model.module - else: - _model = runner.model - device = next(_model.parameters()).device - data = next(iter(runner.data_loader)) - image = data[self.img_key][0:1].to(device) - with torch.no_grad(): - self.writer.add_graph(_model, image) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/segmind.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/segmind.py deleted file mode 100644 index e262c7c1a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/segmind.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class SegmindLoggerHook(LoggerHook): - """Class to log metrics to Segmind. - - It requires `Segmind`_ to be installed. - - Args: - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - - .. _Segmind: - https://docs.segmind.com/python-library - """ - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(SegmindLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_segmind() - - def import_segmind(self): - try: - import segmind - except ImportError: - raise ImportError( - "Please run 'pip install segmind' to install segmind") - self.log_metrics = segmind.tracking.fluent.log_metrics - self.mlflow_log = segmind.utils.logging_utils.try_mlflow_log - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - # logging metrics to segmind - self.mlflow_log( - self.log_metrics, tags, step=runner.epoch, epoch=runner.epoch) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py deleted file mode 100644 index bf00d5742..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/tensorboard.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import TORCH_VERSION, digit_version -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TensorboardLoggerHook(LoggerHook): - """Class to log metrics to Tensorboard. - - Args: - log_dir (string): Save directory location. Default: None. If default - values are used, directory location is ``runner.work_dir``/tf_logs. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - """ - - def __init__(self, - log_dir=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(TensorboardLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.log_dir = log_dir - - @master_only - def before_run(self, runner): - super(TensorboardLoggerHook, self).before_run(runner) - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.1')): - try: - from tensorboardX import SummaryWriter - except ImportError: - raise ImportError('Please install tensorboardX to use ' - 'TensorboardLoggerHook.') - else: - try: - from torch.utils.tensorboard import SummaryWriter - except ImportError: - raise ImportError( - 'Please run "pip install future tensorboard" to install ' - 'the dependencies to use torch.utils.tensorboard ' - '(applicable to PyTorch 1.1 or higher)') - - if self.log_dir is None: - self.log_dir = osp.join(runner.work_dir, 'tf_logs') - self.writer = SummaryWriter(self.log_dir) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, allow_text=True) - for tag, val in tags.items(): - if isinstance(val, str): - self.writer.add_text(tag, val, self.get_iter(runner)) - else: - self.writer.add_scalar(tag, val, self.get_iter(runner)) - - @master_only - def after_run(self, runner): - self.writer.close() diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/text.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/text.py deleted file mode 100644 index 644ced2c9..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/text.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import datetime -import os -import os.path as osp -from collections import OrderedDict - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.fileio.file_client import FileClient -from mmcv.utils import is_tuple_of, scandir -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TextLoggerHook(LoggerHook): - """Logger hook in text. - - In this logger hook, the information will be printed on terminal and - saved in json file. - - Args: - by_epoch (bool, optional): Whether EpochBasedRunner is used. - Default: True. - interval (int, optional): Logging interval (every k iterations). - Default: 10. - ignore_last (bool, optional): Ignore the log of last iterations in each - epoch if less than :attr:`interval`. Default: True. - reset_flag (bool, optional): Whether to clear the output buffer after - logging. Default: False. - interval_exp_name (int, optional): Logging interval for experiment - name. This feature is to help users conveniently get the experiment - information from screen or log file. Default: 1000. - out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. - If ``out_dir`` is specified, logs will be copied to a new directory - which is the concatenation of ``out_dir`` and the last level - directory of ``runner.work_dir``. Default: None. - `New in version 1.3.16.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be copied to ``out_dir``. - Default: ('.log.json', '.log', '.py'). - `New in version 1.3.16.` - keep_local (bool, optional): Whether to keep local log when - :attr:`out_dir` is specified. If False, the local log will be - removed. Default: True. - `New in version 1.3.16.` - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - - def __init__(self, - by_epoch=True, - interval=10, - ignore_last=True, - reset_flag=False, - interval_exp_name=1000, - out_dir=None, - out_suffix=('.log.json', '.log', '.py'), - keep_local=True, - file_client_args=None): - super(TextLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.by_epoch = by_epoch - self.time_sec_tot = 0 - self.interval_exp_name = interval_exp_name - - if out_dir is None and file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" when `out_dir` is not' - 'specified.') - self.out_dir = out_dir - - if not (out_dir is None or isinstance(out_dir, str) - or is_tuple_of(out_dir, str)): - raise TypeError('out_dir should be "None" or string or tuple of ' - 'string, but got {out_dir}') - self.out_suffix = out_suffix - - self.keep_local = keep_local - self.file_client_args = file_client_args - if self.out_dir is not None: - self.file_client = FileClient.infer_client(file_client_args, - self.out_dir) - - def before_run(self, runner): - super(TextLoggerHook, self).before_run(runner) - - if self.out_dir is not None: - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - # The final `self.out_dir` is the concatenation of `self.out_dir` - # and the last level directory of `runner.work_dir` - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'Text logs will be saved to {self.out_dir} by ' - f'{self.file_client.name} after the training process.')) - - self.start_iter = runner.iter - self.json_log_path = osp.join(runner.work_dir, - f'{runner.timestamp}.log.json') - if runner.meta is not None: - self._dump_log(runner.meta, runner) - - def _get_max_memory(self, runner): - device = getattr(runner.model, 'output_device', None) - mem = torch.cuda.max_memory_allocated(device=device) - mem_mb = torch.tensor([int(mem) // (1024 * 1024)], - dtype=torch.int, - device=device) - if runner.world_size > 1: - dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) - return mem_mb.item() - - def _log_info(self, log_dict, runner): - # print exp name for users to distinguish experiments - # at every ``interval_exp_name`` iterations and the end of each epoch - if runner.meta is not None and 'exp_name' in runner.meta: - if (self.every_n_iters(runner, self.interval_exp_name)) or ( - self.by_epoch and self.end_of_epoch(runner)): - exp_info = f'Exp name: {runner.meta["exp_name"]}' - runner.logger.info(exp_info) - - if log_dict['mode'] == 'train': - if isinstance(log_dict['lr'], dict): - lr_str = [] - for k, val in log_dict['lr'].items(): - lr_str.append(f'lr_{k}: {val:.3e}') - lr_str = ' '.join(lr_str) - else: - lr_str = f'lr: {log_dict["lr"]:.3e}' - - # by epoch: Epoch [4][100/1000] - # by iter: Iter [100/100000] - if self.by_epoch: - log_str = f'Epoch [{log_dict["epoch"]}]' \ - f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' - else: - log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' - log_str += f'{lr_str}, ' - - if 'time' in log_dict.keys(): - self.time_sec_tot += (log_dict['time'] * self.interval) - time_sec_avg = self.time_sec_tot / ( - runner.iter - self.start_iter + 1) - eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) - eta_str = str(datetime.timedelta(seconds=int(eta_sec))) - log_str += f'eta: {eta_str}, ' - log_str += f'time: {log_dict["time"]:.3f}, ' \ - f'data_time: {log_dict["data_time"]:.3f}, ' - # statistic memory - if torch.cuda.is_available(): - log_str += f'memory: {log_dict["memory"]}, ' - else: - # val/test time - # here 1000 is the length of the val dataloader - # by epoch: Epoch[val] [4][1000] - # by iter: Iter[val] [1000] - if self.by_epoch: - log_str = f'Epoch({log_dict["mode"]}) ' \ - f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' - else: - log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' - - log_items = [] - for name, val in log_dict.items(): - # TODO: resolve this hack - # these items have been in log_str - if name in [ - 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', - 'memory', 'epoch' - ]: - continue - if isinstance(val, float): - val = f'{val:.4f}' - log_items.append(f'{name}: {val}') - log_str += ', '.join(log_items) - - runner.logger.info(log_str) - - def _dump_log(self, log_dict, runner): - # dump log in json format - json_log = OrderedDict() - for k, v in log_dict.items(): - json_log[k] = self._round_float(v) - # only append log at last line - if runner.rank == 0: - with open(self.json_log_path, 'a+') as f: - mmcv.dump(json_log, f, file_format='json') - f.write('\n') - - def _round_float(self, items): - if isinstance(items, list): - return [self._round_float(item) for item in items] - elif isinstance(items, float): - return round(items, 5) - else: - return items - - def log(self, runner): - if 'eval_iter_num' in runner.log_buffer.output: - # this doesn't modify runner.iter and is regardless of by_epoch - cur_iter = runner.log_buffer.output.pop('eval_iter_num') - else: - cur_iter = self.get_iter(runner, inner_iter=True) - - log_dict = OrderedDict( - mode=self.get_mode(runner), - epoch=self.get_epoch(runner), - iter=cur_iter) - - # only record lr of the first param group - cur_lr = runner.current_lr() - if isinstance(cur_lr, list): - log_dict['lr'] = cur_lr[0] - else: - assert isinstance(cur_lr, dict) - log_dict['lr'] = {} - for k, lr_ in cur_lr.items(): - assert isinstance(lr_, list) - log_dict['lr'].update({k: lr_[0]}) - - if 'time' in runner.log_buffer.output: - # statistic memory - if torch.cuda.is_available(): - log_dict['memory'] = self._get_max_memory(runner) - - log_dict = dict(log_dict, **runner.log_buffer.output) - - self._log_info(log_dict, runner) - self._dump_log(log_dict, runner) - return log_dict - - def after_run(self, runner): - # copy or upload logs to self.out_dir - if self.out_dir is not None: - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - out_filepath = self.file_client.join_path( - self.out_dir, filename) - with open(local_filepath, 'r') as f: - self.file_client.put_text(f.read(), out_filepath) - - runner.logger.info( - (f'The file {local_filepath} has been uploaded to ' - f'{out_filepath}.')) - - if not self.keep_local: - os.remove(local_filepath) - runner.logger.info( - (f'{local_filepath} was removed due to the ' - '`self.keep_local=False`')) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/wandb.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/wandb.py deleted file mode 100644 index 78b890ee1..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/logger/wandb.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import scandir -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class WandbLoggerHook(LoggerHook): - """Class to log metrics with wandb. - - It requires `wandb`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the initialization keys. Check - https://docs.wandb.ai/ref/python/init for more init arguments. - interval (int): Logging interval (every k iterations). - Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - commit (bool): Save the metrics dict to the wandb server and increment - the step. If false ``wandb.log`` just updates the current metrics - dict with the row argument and metrics won't be saved until - ``wandb.log`` is called with ``commit=True``. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - log_artifact (bool): If True, artifacts in {work_dir} will be uploaded - to wandb after training ends. - Default: True - `New in version 1.4.3.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be uploaded to wandb. - Default: ('.log.json', '.log', '.py'). - `New in version 1.4.3.` - - .. _wandb: - https://docs.wandb.ai - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - commit=True, - by_epoch=True, - with_step=True, - log_artifact=True, - out_suffix=('.log.json', '.log', '.py')): - super(WandbLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_wandb() - self.init_kwargs = init_kwargs - self.commit = commit - self.with_step = with_step - self.log_artifact = log_artifact - self.out_suffix = out_suffix - - def import_wandb(self): - try: - import wandb - except ImportError: - raise ImportError( - 'Please run "pip install wandb" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(WandbLoggerHook, self).before_run(runner) - if self.wandb is None: - self.import_wandb() - if self.init_kwargs: - self.wandb.init(**self.init_kwargs) - else: - self.wandb.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - if self.with_step: - self.wandb.log( - tags, step=self.get_iter(runner), commit=self.commit) - else: - tags['global_step'] = self.get_iter(runner) - self.wandb.log(tags, commit=self.commit) - - @master_only - def after_run(self, runner): - if self.log_artifact: - wandb_artifact = self.wandb.Artifact( - name='artifacts', type='model') - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - wandb_artifact.add_file(local_filepath) - self.wandb.log_artifact(wandb_artifact) - self.wandb.join() diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/lr_updater.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/lr_updater.py deleted file mode 100644 index ee2a53a65..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/lr_updater.py +++ /dev/null @@ -1,730 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from math import cos, pi - -import mmcv -from .hook import HOOKS, Hook - - -class LrUpdaterHook(Hook): - """LR Scheduler in MMCV. - - Args: - by_epoch (bool): LR changes epoch by epoch - warmup (string): Type of warmup used. It can be None(use no warmup), - 'constant', 'linear' or 'exp' - warmup_iters (int): The number of iterations or epochs that warmup - lasts - warmup_ratio (float): LR used at the beginning of warmup equals to - warmup_ratio * initial_lr - warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters - means the number of epochs that warmup lasts, otherwise means the - number of iteration that warmup lasts - """ - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.1, - warmup_by_epoch=False): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_ratio" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - self.warmup_by_epoch = warmup_by_epoch - - if self.warmup_by_epoch: - self.warmup_epochs = self.warmup_iters - self.warmup_iters = None - else: - self.warmup_epochs = None - - self.base_lr = [] # initial lr for all param groups - self.regular_lr = [] # expected lr if no warming up is performed - - def _set_lr(self, runner, lr_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, lr in zip(optim.param_groups, lr_groups[k]): - param_group['lr'] = lr - else: - for param_group, lr in zip(runner.optimizer.param_groups, - lr_groups): - param_group['lr'] = lr - - def get_lr(self, runner, base_lr): - raise NotImplementedError - - def get_regular_lr(self, runner): - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(runner, _base_lr) - for _base_lr in self.base_lr[k] - ] - lr_groups.update({k: _lr_group}) - - return lr_groups - else: - return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - if self.warmup == 'constant': - warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_lr = [_lr * (1 - k) for _lr in regular_lr] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.regular_lr, dict): - lr_groups = {} - for key, regular_lr in self.regular_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.regular_lr) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - group.setdefault('initial_lr', group['lr']) - _base_lr = [ - group['initial_lr'] for group in optim.param_groups - ] - self.base_lr.update({k: _base_lr}) - else: - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['initial_lr'] for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if self.warmup_iters is None: - epoch_len = len(runner.data_loader) - self.warmup_iters = self.warmup_epochs * epoch_len - - if not self.by_epoch: - return - - self.regular_lr = self.get_regular_lr(runner) - self._set_lr(runner, self.regular_lr) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_lr = self.get_regular_lr(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - - -@HOOKS.register_module() -class FixedLrUpdaterHook(LrUpdaterHook): - - def __init__(self, **kwargs): - super(FixedLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - return base_lr - - -@HOOKS.register_module() -class StepLrUpdaterHook(LrUpdaterHook): - """Step LR scheduler with min_lr clipping. - - Args: - step (int | list[int]): Step to decay the LR. If an int value is given, - regard it as the decay interval. If a list is given, decay LR at - these steps. - gamma (float, optional): Decay LR ratio. Default: 0.1. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. If None - is given, we don't perform lr clipping. Default: None. - """ - - def __init__(self, step, gamma=0.1, min_lr=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_lr = min_lr - super(StepLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - lr = base_lr * (self.gamma**exp) - if self.min_lr is not None: - # clip to a minimum value - lr = max(lr, self.min_lr) - return lr - - -@HOOKS.register_module() -class ExpLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, **kwargs): - self.gamma = gamma - super(ExpLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * self.gamma**progress - - -@HOOKS.register_module() -class PolyLrUpdaterHook(LrUpdaterHook): - - def __init__(self, power=1., min_lr=0., **kwargs): - self.power = power - self.min_lr = min_lr - super(PolyLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - coeff = (1 - progress / max_progress)**self.power - return (base_lr - self.min_lr) * coeff + self.min_lr - - -@HOOKS.register_module() -class InvLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, power=1., **kwargs): - self.gamma = gamma - self.power = power - super(InvLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * (1 + self.gamma * progress)**(-self.power) - - -@HOOKS.register_module() -class CosineAnnealingLrUpdaterHook(LrUpdaterHook): - """CosineAnnealing LR scheduler. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(CosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): - """Flat + Cosine lr schedule. - - Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 - - Args: - start_percent (float): When to start annealing the learning rate - after the percentage of the total training steps. - The value should be in range [0, 1). - Default: 0.75 - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - start_percent=0.75, - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - if start_percent < 0 or start_percent > 1 or not isinstance( - start_percent, float): - raise ValueError( - 'expected float between 0 and 1 start_percent, but ' - f'got {start_percent}') - self.start_percent = start_percent - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(FlatCosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - start = round(runner.max_epochs * self.start_percent) - progress = runner.epoch - start - max_progress = runner.max_epochs - start - else: - start = round(runner.max_iters * self.start_percent) - progress = runner.iter - start - max_progress = runner.max_iters - start - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress < 0: - return base_lr - else: - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class CosineRestartLrUpdaterHook(LrUpdaterHook): - """Cosine annealing with restarts learning rate scheme. - - Args: - periods (list[int]): Periods for each cosine anneling cycle. - restart_weights (list[float], optional): Restart weights at each - restart iteration. Default: [1]. - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - periods, - restart_weights=[1], - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.periods = periods - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - self.restart_weights = restart_weights - assert (len(self.periods) == len(self.restart_weights) - ), 'periods and restart_weights should have the same length.' - super(CosineRestartLrUpdaterHook, self).__init__(**kwargs) - - self.cumulative_periods = [ - sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) - ] - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - else: - progress = runner.iter - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - idx = get_position_from_periods(progress, self.cumulative_periods) - current_weight = self.restart_weights[idx] - nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] - current_periods = self.periods[idx] - - alpha = min((progress - nearest_restart) / current_periods, 1) - return annealing_cos(base_lr, target_lr, alpha, current_weight) - - -def get_position_from_periods(iteration, cumulative_periods): - """Get the position from a period list. - - It will return the index of the right-closest number in the period list. - For example, the cumulative_periods = [100, 200, 300, 400], - if iteration == 50, return 0; - if iteration == 210, return 2; - if iteration == 300, return 3. - - Args: - iteration (int): Current iteration. - cumulative_periods (list[int]): Cumulative period list. - - Returns: - int: The position of the right-closest number in the period list. - """ - for i, period in enumerate(cumulative_periods): - if iteration < period: - return i - raise ValueError(f'Current iteration {iteration} exceeds ' - f'cumulative_periods {cumulative_periods}') - - -@HOOKS.register_module() -class CyclicLrUpdaterHook(LrUpdaterHook): - """Cyclic LR Scheduler. - - Implement the cyclical learning rate policy (CLR) described in - https://arxiv.org/pdf/1506.01186.pdf - - Different from the original paper, we use cosine annealing rather than - triangular policy inside a cycle. This improves the performance in the - 3D detection area. - - Args: - by_epoch (bool, optional): Whether to update LR by epoch. - target_ratio (tuple[float], optional): Relative ratio of the highest LR - and the lowest LR to the initial LR. - cyclic_times (int, optional): Number of cycles during training - step_ratio_up (float, optional): The ratio of the increasing process of - LR in the total cycle. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch=False, - target_ratio=(10, 1e-4), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - gamma=1, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - assert 0 < gamma <= 1, \ - '"gamma" must be in range (0, 1]' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.max_iter_per_phase = None - self.lr_phases = [] # init lr_phases - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicLrUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicLrUpdaterHook, self).before_run(runner) - # initiate lr_phases - # total lr_phases are separated as up and down - self.max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * self.max_iter_per_phase) - self.lr_phases.append([0, iter_up_phase, 1, self.target_ratio[0]]) - self.lr_phases.append([ - iter_up_phase, self.max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - # Update weight decay - scale = self.gamma**curr_cycle - - for (start_iter, end_iter, start_ratio, end_ratio) in self.lr_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_lr and base lr. The target end_ratio can be - # expressed as: - # end_ratio = (base_lr + scale * (max_lr - base_lr)) / base_lr - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_lr * start_ratio, - base_lr * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleLrUpdaterHook(LrUpdaterHook): - """One Cycle LR Scheduler. - - The 1cycle learning rate policy changes the learning rate after every - batch. The one cycle learning rate policy is described in - https://arxiv.org/pdf/1708.07120.pdf - - Args: - max_lr (float or list): Upper learning rate boundaries in the cycle - for each parameter group. - total_steps (int, optional): The total number of steps in the cycle. - Note that if a value is not provided here, it will be the max_iter - of runner. Default: None. - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - div_factor (float): Determines the initial learning rate via - initial_lr = max_lr/div_factor - Default: 25 - final_div_factor (float): Determines the minimum learning rate via - min_lr = initial_lr/final_div_factor - Default: 1e4 - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - max_lr, - total_steps=None, - pct_start=0.3, - anneal_strategy='cos', - div_factor=25, - final_div_factor=1e4, - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch = False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(max_lr, (numbers.Number, list, dict)): - raise ValueError('the type of max_lr must be the one of list or ' - f'dict, but got {type(max_lr)}') - self._max_lr = max_lr - if total_steps is not None: - if not isinstance(total_steps, int): - raise ValueError('the type of total_steps must be int, but' - f'got {type(total_steps)}') - self.total_steps = total_steps - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.div_factor = div_factor - self.final_div_factor = final_div_factor - self.three_phase = three_phase - self.lr_phases = [] # init lr_phases - super(OneCycleLrUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if hasattr(self, 'total_steps'): - total_steps = self.total_steps - else: - total_steps = runner.max_iters - if total_steps < runner.max_iters: - raise ValueError( - 'The total steps must be greater than or equal to max ' - f'iterations {runner.max_iters} of runner, but total steps ' - f'is {total_steps}.') - - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - _max_lr = format_param(k, optim, self._max_lr) - self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(optim.param_groups, self.base_lr[k]): - group.setdefault('initial_lr', lr) - else: - k = type(runner.optimizer).__name__ - _max_lr = format_param(k, runner.optimizer, self._max_lr) - self.base_lr = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(runner.optimizer.param_groups, self.base_lr): - group.setdefault('initial_lr', lr) - - if self.three_phase: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append([ - float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 - ]) - self.lr_phases.append( - [total_steps - 1, 1, 1 / self.final_div_factor]) - else: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append( - [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - start_iter = 0 - for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): - if curr_iter <= end_iter: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, - pct) - break - start_iter = end_iter - return lr - - -@HOOKS.register_module() -class LinearAnnealingLrUpdaterHook(LrUpdaterHook): - """Linear annealing LR Scheduler decays the learning rate of each parameter - group linearly. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(LinearAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_linear(base_lr, target_lr, progress / max_progress) - - -def annealing_cos(start, end, factor, weight=1): - """Calculate annealing cos learning rate. - - Cosine anneal from `weight * start + (1 - weight) * end` to `end` as - percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the cosine annealing. - end (float): The ending learing rate of the cosine annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - weight (float, optional): The combination factor of `start` and `end` - when calculating the actual starting learning rate. Default to 1. - """ - cos_out = cos(pi * factor) + 1 - return end + 0.5 * weight * (start - end) * cos_out - - -def annealing_linear(start, end, factor): - """Calculate annealing linear learning rate. - - Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the linear annealing. - end (float): The ending learing rate of the linear annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - """ - return start + (end - start) * factor - - -def format_param(name, optim, param): - if isinstance(param, numbers.Number): - return [param] * len(optim.param_groups) - elif isinstance(param, (list, tuple)): # multi param groups - if len(param) != len(optim.param_groups): - raise ValueError(f'expected {len(optim.param_groups)} ' - f'values for {name}, got {len(param)}') - return param - else: # multi optimizers - if name not in param: - raise KeyError(f'{name} is not found in {param.keys()}') - return param[name] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/memory.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/memory.py deleted file mode 100644 index 70cf9a838..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class EmptyCacheHook(Hook): - - def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): - self._before_epoch = before_epoch - self._after_epoch = after_epoch - self._after_iter = after_iter - - def after_iter(self, runner): - if self._after_iter: - torch.cuda.empty_cache() - - def before_epoch(self, runner): - if self._before_epoch: - torch.cuda.empty_cache() - - def after_epoch(self, runner): - if self._after_epoch: - torch.cuda.empty_cache() diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/momentum_updater.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/momentum_updater.py deleted file mode 100644 index aa15fe23c..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/momentum_updater.py +++ /dev/null @@ -1,566 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -from .hook import HOOKS, Hook -from .lr_updater import annealing_cos, annealing_linear, format_param - - -class MomentumUpdaterHook(Hook): - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.9): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_momentum" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - - self.base_momentum = [] # initial momentum for all param groups - self.regular_momentum = [ - ] # expected momentum if no warming up is performed - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, base_momentum): - raise NotImplementedError - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k in runner.optimizer.keys(): - _momentum_group = [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum[k] - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - return [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum - ] - - def get_warmup_momentum(self, cur_iters): - - def _get_warmup_momentum(cur_iters, regular_momentum): - if self.warmup == 'constant': - warmup_momentum = [ - _momentum / self.warmup_ratio - for _momentum in regular_momentum - ] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_momentum = [ - _momentum / (1 - k) for _momentum in regular_momentum - ] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_momentum = [ - _momentum / k for _momentum in regular_momentum - ] - return warmup_momentum - - if isinstance(self.regular_momentum, dict): - momentum_groups = {} - for key, regular_momentum in self.regular_momentum.items(): - momentum_groups[key] = _get_warmup_momentum( - cur_iters, regular_momentum) - return momentum_groups - else: - return _get_warmup_momentum(cur_iters, self.regular_momentum) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, - # if 'initial_momentum' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_momentum = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - _base_momentum = [ - group['initial_momentum'] for group in optim.param_groups - ] - self.base_momentum.update({k: _base_momentum}) - else: - for group in runner.optimizer.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - self.base_momentum = [ - group['initial_momentum'] - for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if not self.by_epoch: - return - self.regular_momentum = self.get_regular_momentum(runner) - self._set_momentum(runner, self.regular_momentum) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_momentum = self.get_regular_momentum(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - - -@HOOKS.register_module() -class StepMomentumUpdaterHook(MomentumUpdaterHook): - """Step momentum scheduler with min value clipping. - - Args: - step (int | list[int]): Step to decay the momentum. If an int value is - given, regard it as the decay interval. If a list is given, decay - momentum at these steps. - gamma (float, optional): Decay momentum ratio. Default: 0.5. - min_momentum (float, optional): Minimum momentum value to keep. If - momentum after decay is lower than this value, it will be clipped - accordingly. If None is given, we don't perform lr clipping. - Default: None. - """ - - def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_momentum = min_momentum - super(StepMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - momentum = base_momentum * (self.gamma**exp) - if self.min_momentum is not None: - # clip to a minimum value - momentum = max(momentum, self.min_momentum) - return momentum - - -@HOOKS.register_module() -class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Cosine annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(CosineAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_cos(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class LinearAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Linear annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(LinearAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_linear(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class CyclicMomentumUpdaterHook(MomentumUpdaterHook): - """Cyclic momentum Scheduler. - - Implement the cyclical momentum scheduler policy described in - https://arxiv.org/pdf/1708.07120.pdf - - This momentum scheduler usually used together with the CyclicLRUpdater - to improve the performance in the 3D detection area. - - Args: - target_ratio (tuple[float]): Relative ratio of the lowest momentum and - the highest momentum to the initial momentum. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of momentum - in the total cycle. - by_epoch (bool): Whether to update momentum by epoch. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch=False, - target_ratio=(0.85 / 0.95, 1), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - gamma=1, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.momentum_phases = [] # init momentum_phases - - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - # currently only support by_epoch=False - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicMomentumUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicMomentumUpdaterHook, self).before_run(runner) - # initiate momentum_phases - # total momentum_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.max_iter_per_phase = max_iter_per_phase - self.momentum_phases.append( - [0, iter_up_phase, 1, self.target_ratio[0]]) - self.momentum_phases.append([ - iter_up_phase, max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_momentum(self, runner, base_momentum): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - scale = self.gamma**curr_cycle - for (start_iter, end_iter, start_ratio, end_ratio) \ - in self.momentum_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_momentum and base momentum. The target end_ratio - # can be expressed as: - # end_ratio = (base_momentum + scale * \ - # (max_momentum - base_momentum)) / base_momentum - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_momentum * start_ratio, - base_momentum * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): - """OneCycle momentum Scheduler. - - This momentum scheduler usually used together with the OneCycleLrUpdater - to improve the performance. - - Args: - base_momentum (float or list): Lower momentum boundaries in the cycle - for each parameter group. Note that momentum is cycled inversely - to learning rate; at the peak of a cycle, momentum is - 'base_momentum' and learning rate is 'max_lr'. - Default: 0.85 - max_momentum (float or list): Upper momentum boundaries in the cycle - for each parameter group. Functionally, - it defines the cycle amplitude (max_momentum - base_momentum). - Note that momentum is cycled inversely - to learning rate; at the start of a cycle, momentum is - 'max_momentum' and learning rate is 'base_lr' - Default: 0.95 - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - base_momentum=0.85, - max_momentum=0.95, - pct_start=0.3, - anneal_strategy='cos', - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch=False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(base_momentum, (float, list, dict)): - raise ValueError('base_momentum must be the type among of float,' - 'list or dict.') - self._base_momentum = base_momentum - if not isinstance(max_momentum, (float, list, dict)): - raise ValueError('max_momentum must be the type among of float,' - 'list or dict.') - self._max_momentum = max_momentum - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('Expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must by one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.three_phase = three_phase - self.momentum_phases = [] # init momentum_phases - super(OneCycleMomentumUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip( - optim.param_groups, _base_momentum, _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - else: - optim = runner.optimizer - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - k = type(optim).__name__ - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip(optim.param_groups, - _base_momentum, - _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - - if self.three_phase: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': - float(2 * self.pct_start * runner.max_iters) - 2, - 'start_momentum': - 'base_momentum', - 'end_momentum': - 'max_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'max_momentum', - 'end_momentum': 'max_momentum' - }) - else: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'base_momentum', - 'end_momentum': 'max_momentum' - }) - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, param_group): - curr_iter = runner.iter - start_iter = 0 - for i, phase in enumerate(self.momentum_phases): - end_iter = phase['end_iter'] - if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - momentum = self.anneal_func( - param_group[phase['start_momentum']], - param_group[phase['end_momentum']], pct) - break - start_iter = end_iter - return momentum - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k, optim in runner.optimizer.items(): - _momentum_group = [ - self.get_momentum(runner, param_group) - for param_group in optim.param_groups - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - momentum_groups = [] - for param_group in runner.optimizer.param_groups: - momentum_groups.append(self.get_momentum(runner, param_group)) - return momentum_groups diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/optimizer.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/optimizer.py deleted file mode 100644 index 12c885183..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/optimizer.py +++ /dev/null @@ -1,556 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -from collections import defaultdict -from itertools import chain - -from torch.nn.utils import clip_grad - -from mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version -from ..dist_utils import allreduce_grads -from ..fp16_utils import LossScaler, wrap_fp16_model -from .hook import HOOKS, Hook - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - from torch.cuda.amp import GradScaler -except ImportError: - pass - - -@HOOKS.register_module() -class OptimizerHook(Hook): - """A hook contains custom operations for the optimizer. - - Args: - grad_clip (dict, optional): A config dict to control the clip_grad. - Default: None. - detect_anomalous_params (bool): This option is only used for - debugging which will slow down the training speed. - Detect anomalous parameters that are not included in - the computational graph with `loss` as the root. - There are two cases - - - Parameters were not used during - forward pass. - - Parameters were not used to produce - loss. - Default: False. - """ - - def __init__(self, grad_clip=None, detect_anomalous_params=False): - self.grad_clip = grad_clip - self.detect_anomalous_params = detect_anomalous_params - - def clip_grads(self, params): - params = list( - filter(lambda p: p.requires_grad and p.grad is not None, params)) - if len(params) > 0: - return clip_grad.clip_grad_norm_(params, **self.grad_clip) - - def after_train_iter(self, runner): - runner.optimizer.zero_grad() - if self.detect_anomalous_params: - self.detect_anomalous_parameters(runner.outputs['loss'], runner) - runner.outputs['loss'].backward() - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - - def detect_anomalous_parameters(self, loss, runner): - logger = runner.logger - parameters_in_graph = set() - visited = set() - - def traverse(grad_fn): - if grad_fn is None: - return - if grad_fn not in visited: - visited.add(grad_fn) - if hasattr(grad_fn, 'variable'): - parameters_in_graph.add(grad_fn.variable) - parents = grad_fn.next_functions - if parents is not None: - for parent in parents: - grad_fn = parent[0] - traverse(grad_fn) - - traverse(loss.grad_fn) - for n, p in runner.model.named_parameters(): - if p not in parameters_in_graph and p.requires_grad: - logger.log( - level=logging.ERROR, - msg=f'{n} with shape {p.size()} is not ' - f'in the computational graph \n') - - -@HOOKS.register_module() -class GradientCumulativeOptimizerHook(OptimizerHook): - """Optimizer Hook implements multi-iters gradient cumulating. - - Args: - cumulative_iters (int, optional): Num of gradient cumulative iters. - The optimizer will step every `cumulative_iters` iters. - Defaults to 1. - - Examples: - >>> # Use cumulative_iters to simulate a large batch size - >>> # It is helpful when the hardware cannot handle a large batch size. - >>> loader = DataLoader(data, batch_size=64) - >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) - >>> # almost equals to - >>> loader = DataLoader(data, batch_size=256) - >>> optim_hook = OptimizerHook() - """ - - def __init__(self, cumulative_iters=1, **kwargs): - super(GradientCumulativeOptimizerHook, self).__init__(**kwargs) - - assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ - f'cumulative_iters only accepts positive int, but got ' \ - f'{type(cumulative_iters)} instead.' - - self.cumulative_iters = cumulative_iters - self.divisible_iters = 0 - self.remainder_iters = 0 - self.initialized = False - - def has_batch_norm(self, module): - if isinstance(module, _BatchNorm): - return True - for m in module.children(): - if self.has_batch_norm(m): - return True - return False - - def _init(self, runner): - if runner.iter % self.cumulative_iters != 0: - runner.logger.warning( - 'Resume iter number is not divisible by cumulative_iters in ' - 'GradientCumulativeOptimizerHook, which means the gradient of ' - 'some iters is lost and the result may be influenced slightly.' - ) - - if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: - runner.logger.warning( - 'GradientCumulativeOptimizerHook may slightly decrease ' - 'performance if the model has BatchNorm layers.') - - residual_iters = runner.max_iters - runner.iter - - self.divisible_iters = ( - residual_iters // self.cumulative_iters * self.cumulative_iters) - self.remainder_iters = residual_iters - self.divisible_iters - - self.initialized = True - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - runner.optimizer.zero_grad() - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - self.loss_scaler = GradScaler() - elif isinstance(loss_scale, float): - self._scale_update_param = loss_scale - self.loss_scaler = GradScaler(init_scale=loss_scale) - elif isinstance(loss_scale, dict): - self.loss_scaler = GradScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training.""" - # wrap model mode to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer to - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients. - 3. Unscale the optimizer’s gradient tensors. - 4. Call optimizer.step() and update scale factor. - 5. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - - self.loss_scaler.scale(runner.outputs['loss']).backward() - self.loss_scaler.unscale_(runner.optimizer) - # grad clip - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using PyTorch's implementation) implements - multi-iters gradient cumulating. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - """ - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - - self.loss_scaler.scale(loss).backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - self.loss_scaler.unscale_(runner.optimizer) - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() - -else: - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (mmcv's implementation). - - The steps of fp16 optimizer is as follows. - 1. Scale the loss value. - 2. BP in the fp16 model. - 2. Copy gradients from fp16 model to fp32 weights. - 3. Update fp32 weights. - 4. Copy updated parameters from fp32 weights to fp16 model. - - Refer to https://arxiv.org/abs/1710.03740 for more details. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of LossScaler. - Defaults to 512. - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - if loss_scale == 'dynamic': - self.loss_scaler = LossScaler(mode='dynamic') - elif isinstance(loss_scale, float): - self.loss_scaler = LossScaler( - init_scale=loss_scale, mode='static') - elif isinstance(loss_scale, dict): - self.loss_scaler = LossScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training. - - 1. Make a master copy of fp32 weights for optimization. - 2. Convert the main model from fp32 to fp16. - """ - # keep a copy of fp32 weights - old_groups = runner.optimizer.param_groups - runner.optimizer.param_groups = copy.deepcopy( - runner.optimizer.param_groups) - state = defaultdict(dict) - p_map = { - old_p: p - for old_p, p in zip( - chain(*(g['params'] for g in old_groups)), - chain(*(g['params'] - for g in runner.optimizer.param_groups))) - } - for k, v in runner.optimizer.state.items(): - state[p_map[k]] = v - runner.optimizer.state = state - # convert model to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer `loss_scalar.py` - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients (fp16). - 3. Copy gradients from the model to the fp32 weight copy. - 4. Scale the gradients back and update the fp32 weight copy. - 5. Copy back the params from fp32 weight copy to the fp16 model. - 6. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - # scale the loss value - scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale - scaled_loss.backward() - # copy fp16 grads in the model to fp32 params in the optimizer - - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - self.loss_scaler.update_scale(has_overflow) - if has_overflow: - runner.logger.warning('Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using mmcv implementation) implements multi- - iters gradient cumulating.""" - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - - loss = runner.outputs['loss'] - loss = loss / loss_factor - - # scale the loss value - scaled_loss = loss * self.loss_scaler.loss_scale - scaled_loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - else: - runner.logger.warning( - 'Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - self.loss_scaler.update_scale(has_overflow) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/profiler.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/profiler.py deleted file mode 100644 index fef9adc13..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/profiler.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Callable, List, Optional, Union - -import torch - -from ..dist_utils import master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ProfilerHook(Hook): - """Profiler to analyze performance during training. - - PyTorch Profiler is a tool that allows the collection of the performance - metrics during the training. More details on Profiler can be found at - https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile - - Args: - by_epoch (bool): Profile performance by epoch or by iteration. - Default: True. - profile_iters (int): Number of iterations for profiling. - If ``by_epoch=True``, profile_iters indicates that they are the - first profile_iters epochs at the beginning of the - training, otherwise it indicates the first profile_iters - iterations. Default: 1. - activities (list[str]): List of activity groups (CPU, CUDA) to use in - profiling. Default: ['cpu', 'cuda']. - schedule (dict, optional): Config of generating the callable schedule. - if schedule is None, profiler will not add step markers into the - trace and table view. Default: None. - on_trace_ready (callable, dict): Either a handler or a dict of generate - handler. Default: None. - record_shapes (bool): Save information about operator's input shapes. - Default: False. - profile_memory (bool): Track tensor memory allocation/deallocation. - Default: False. - with_stack (bool): Record source information (file and line number) - for the ops. Default: False. - with_flops (bool): Use formula to estimate the FLOPS of specific - operators (matrix multiplication and 2D convolution). - Default: False. - json_trace_path (str, optional): Exports the collected trace in Chrome - JSON format. Default: None. - - Example: - >>> runner = ... # instantiate a Runner - >>> # tensorboard trace - >>> trace_config = dict(type='tb_trace', dir_name='work_dir') - >>> profiler_config = dict(on_trace_ready=trace_config) - >>> runner.register_profiler_hook(profiler_config) - >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) - """ - - def __init__(self, - by_epoch: bool = True, - profile_iters: int = 1, - activities: List[str] = ['cpu', 'cuda'], - schedule: Optional[dict] = None, - on_trace_ready: Optional[Union[Callable, dict]] = None, - record_shapes: bool = False, - profile_memory: bool = False, - with_stack: bool = False, - with_flops: bool = False, - json_trace_path: Optional[str] = None) -> None: - try: - from torch import profiler # torch version >= 1.8.1 - except ImportError: - raise ImportError('profiler is the new feature of torch1.8.1, ' - f'but your version is {torch.__version__}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' - self.by_epoch = by_epoch - - if profile_iters < 1: - raise ValueError('profile_iters should be greater than 0, but got ' - f'{profile_iters}') - self.profile_iters = profile_iters - - if not isinstance(activities, list): - raise ValueError( - f'activities should be list, but got {type(activities)}') - self.activities = [] - for activity in activities: - activity = activity.lower() - if activity == 'cpu': - self.activities.append(profiler.ProfilerActivity.CPU) - elif activity == 'cuda': - self.activities.append(profiler.ProfilerActivity.CUDA) - else: - raise ValueError( - f'activity should be "cpu" or "cuda", but got {activity}') - - if schedule is not None: - self.schedule = profiler.schedule(**schedule) - else: - self.schedule = None - - self.on_trace_ready = on_trace_ready - self.record_shapes = record_shapes - self.profile_memory = profile_memory - self.with_stack = with_stack - self.with_flops = with_flops - self.json_trace_path = json_trace_path - - @master_only - def before_run(self, runner): - if self.by_epoch and runner.max_epochs < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_epochs}') - - if not self.by_epoch and runner.max_iters < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_iters}') - - if callable(self.on_trace_ready): # handler - _on_trace_ready = self.on_trace_ready - elif isinstance(self.on_trace_ready, dict): # config of handler - trace_cfg = self.on_trace_ready.copy() - trace_type = trace_cfg.pop('type') # log_trace handler - if trace_type == 'log_trace': - - def _log_handler(prof): - print(prof.key_averages().table(**trace_cfg)) - - _on_trace_ready = _log_handler - elif trace_type == 'tb_trace': # tensorboard_trace handler - try: - import torch_tb_profiler # noqa: F401 - except ImportError: - raise ImportError('please run "pip install ' - 'torch-tb-profiler" to install ' - 'torch_tb_profiler') - _on_trace_ready = torch.profiler.tensorboard_trace_handler( - **trace_cfg) - else: - raise ValueError('trace_type should be "log_trace" or ' - f'"tb_trace", but got {trace_type}') - elif self.on_trace_ready is None: - _on_trace_ready = None # type: ignore - else: - raise ValueError('on_trace_ready should be handler, dict or None, ' - f'but got {type(self.on_trace_ready)}') - - if self.by_epoch and runner.max_epochs > 1: - warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' - 'instead of 1 epoch. Since profiler will slow down ' - 'the training, it is recommended to train 1 epoch ' - 'with ProfilerHook and adjust your setting according' - ' to the profiler summary. During normal training ' - '(epoch > 1), you may disable the ProfilerHook.') - - self.profiler = torch.profiler.profile( - activities=self.activities, - schedule=self.schedule, - on_trace_ready=_on_trace_ready, - record_shapes=self.record_shapes, - profile_memory=self.profile_memory, - with_stack=self.with_stack, - with_flops=self.with_flops) - - self.profiler.__enter__() - runner.logger.info('profiler is profiling...') - - @master_only - def after_train_epoch(self, runner): - if self.by_epoch and runner.epoch == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) - - @master_only - def after_train_iter(self, runner): - self.profiler.step() - if not self.by_epoch and runner.iter == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/sampler_seed.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/sampler_seed.py deleted file mode 100644 index ee0dc6bdd..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/sampler_seed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class DistSamplerSeedHook(Hook): - """Data-loading sampler for distributed training. - - When distributed training, it is only useful in conjunction with - :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same - purpose with :obj:`IterLoader`. - """ - - def before_epoch(self, runner): - if hasattr(runner.data_loader.sampler, 'set_epoch'): - # in case the data loader uses `SequentialSampler` in Pytorch - runner.data_loader.sampler.set_epoch(runner.epoch) - elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): - # batch sampler in pytorch warps the sampler as its attributes. - runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/sync_buffer.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/sync_buffer.py deleted file mode 100644 index 6376b7ff8..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/hooks/sync_buffer.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..dist_utils import allreduce_params -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class SyncBuffersHook(Hook): - """Synchronize model buffers such as running_mean and running_var in BN at - the end of each epoch. - - Args: - distributed (bool): Whether distributed training is used. It is - effective only for distributed training. Defaults to True. - """ - - def __init__(self, distributed=True): - self.distributed = distributed - - def after_epoch(self, runner): - """All-reduce model buffers at the end of each epoch.""" - if self.distributed: - allreduce_params(runner.model.buffers()) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/iter_based_runner.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/iter_based_runner.py deleted file mode 100644 index 9892b07a4..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/iter_based_runner.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch -from torch.optim import Optimizer - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .hooks import IterTimerHook -from .utils import get_host_info - - -class IterLoader: - - def __init__(self, dataloader): - self._dataloader = dataloader - self.iter_loader = iter(self._dataloader) - self._epoch = 0 - - @property - def epoch(self): - return self._epoch - - def __next__(self): - try: - data = next(self.iter_loader) - except StopIteration: - self._epoch += 1 - if hasattr(self._dataloader.sampler, 'set_epoch'): - self._dataloader.sampler.set_epoch(self._epoch) - time.sleep(2) # Prevent possible deadlock during epoch transition - self.iter_loader = iter(self._dataloader) - data = next(self.iter_loader) - - return data - - def __len__(self): - return len(self._dataloader) - - -@RUNNERS.register_module() -class IterBasedRunner(BaseRunner): - """Iteration-based Runner. - - This runner train models iteration by iteration. - """ - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._epoch = data_loader.epoch - data_batch = next(data_loader) - self.call_hook('before_train_iter') - outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.train_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_train_iter') - self._inner_iter += 1 - self._iter += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - data_batch = next(data_loader) - self.call_hook('before_val_iter') - outputs = self.model.val_step(data_batch, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.val_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_val_iter') - self._inner_iter += 1 - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, iters) to specify the - running order and iterations. E.g, [('train', 10000), - ('val', 1000)] means running 10000 iterations for training and - 1000 iterations for validation, iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - """Resume model from checkpoint. - - Args: - checkpoint (str): Checkpoint to resume from. - resume_optimizer (bool, optional): Whether resume the optimizer(s) - if the checkpoint file includes optimizer(s). Default to True. - map_location (str, optional): Same as :func:`torch.load`. - Default to 'default'. - """ - if map_location == 'default': - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - self._inner_iter = checkpoint['meta']['iter'] - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') - - def save_checkpoint(self, - out_dir, - filename_tmpl='iter_{}.pth', - meta=None, - save_optimizer=True, - create_symlink=True): - """Save checkpoint to file. - - Args: - out_dir (str): Directory to save checkpoint files. - filename_tmpl (str, optional): Checkpoint file template. - Defaults to 'iter_{}.pth'. - meta (dict, optional): Metadata to be saved in checkpoint. - Defaults to None. - save_optimizer (bool, optional): Whether save optimizer. - Defaults to True. - create_symlink (bool, optional): Whether create symlink to the - latest checkpoint file. Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.iter + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - custom_hooks_config=None): - """Register default hooks for iter-based training. - - Checkpoint hook, optimizer stepper hook and logger hooks will be set to - `by_epoch=False` by default. - - Default hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - if checkpoint_config is not None: - checkpoint_config.setdefault('by_epoch', False) - if lr_config is not None: - lr_config.setdefault('by_epoch', False) - if log_config is not None: - for info in log_config['hooks']: - info.setdefault('by_epoch', False) - super(IterBasedRunner, self).register_training_hooks( - lr_config=lr_config, - momentum_config=momentum_config, - optimizer_config=optimizer_config, - checkpoint_config=checkpoint_config, - log_config=log_config, - timer_config=IterTimerHook(), - custom_hooks_config=custom_hooks_config) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/log_buffer.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/log_buffer.py deleted file mode 100644 index d949e2941..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/log_buffer.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import numpy as np - - -class LogBuffer: - - def __init__(self): - self.val_history = OrderedDict() - self.n_history = OrderedDict() - self.output = OrderedDict() - self.ready = False - - def clear(self): - self.val_history.clear() - self.n_history.clear() - self.clear_output() - - def clear_output(self): - self.output.clear() - self.ready = False - - def update(self, vars, count=1): - assert isinstance(vars, dict) - for key, var in vars.items(): - if key not in self.val_history: - self.val_history[key] = [] - self.n_history[key] = [] - self.val_history[key].append(var) - self.n_history[key].append(count) - - def average(self, n=0): - """Average latest n values or all values.""" - assert n >= 0 - for key in self.val_history: - values = np.array(self.val_history[key][-n:]) - nums = np.array(self.n_history[key][-n:]) - avg = np.sum(values * nums) / np.sum(nums) - self.output[key] = avg - self.ready = True diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/optimizer/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/optimizer/__init__.py deleted file mode 100644 index 53c34d047..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/optimizer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, - build_optimizer_constructor) -from .default_constructor import DefaultOptimizerConstructor - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', - 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/optimizer/builder.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/optimizer/builder.py deleted file mode 100644 index f9234eed8..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/optimizer/builder.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect - -import torch - -from ...utils import Registry, build_from_cfg - -OPTIMIZERS = Registry('optimizer') -OPTIMIZER_BUILDERS = Registry('optimizer builder') - - -def register_torch_optimizers(): - torch_optimizers = [] - for module_name in dir(torch.optim): - if module_name.startswith('__'): - continue - _optim = getattr(torch.optim, module_name) - if inspect.isclass(_optim) and issubclass(_optim, - torch.optim.Optimizer): - OPTIMIZERS.register_module()(_optim) - torch_optimizers.append(module_name) - return torch_optimizers - - -TORCH_OPTIMIZERS = register_torch_optimizers() - - -def build_optimizer_constructor(cfg): - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/optimizer/default_constructor.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/optimizer/default_constructor.py deleted file mode 100644 index ae97db880..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/optimizer/default_constructor.py +++ /dev/null @@ -1,250 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -from torch.nn import GroupNorm, LayerNorm - -from mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of -from mmcv.utils.ext_loader import check_ops_exist -from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS - - -@OPTIMIZER_BUILDERS.register_module() -class DefaultOptimizerConstructor: - """Default constructor for optimizers. - - By default each parameter share the same optimizer settings, and we - provide an argument ``paramwise_cfg`` to specify parameter-wise settings. - It is a dict and may contain the following fields: - - - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If - one of the keys in ``custom_keys`` is a substring of the name of one - parameter, then the setting of the parameter will be specified by - ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will - be ignored. It should be noted that the aforementioned ``key`` is the - longest key that is a substring of the name of the parameter. If there - are multiple matched keys with the same length, then the key with lower - alphabet order will be chosen. - ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` - and ``decay_mult``. See Example 2 below. - - ``bias_lr_mult`` (float): It will be multiplied to the learning - rate for all bias parameters (except for those in normalization - layers and offset layers of DCN). - - ``bias_decay_mult`` (float): It will be multiplied to the weight - decay for all bias parameters (except for those in - normalization layers, depthwise conv layers, offset layers of DCN). - - ``norm_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of normalization - layers. - - ``dwconv_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of depthwise conv - layers. - - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning - rate for parameters of offset layer in the deformable convs - of a model. - - ``bypass_duplicate`` (bool): If true, the duplicate parameters - would not be added into optimizer. Default: False. - - Note: - - 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will - override the effect of ``bias_lr_mult`` in the bias of offset layer. - So be careful when using both ``bias_lr_mult`` and - ``dcn_offset_lr_mult``. If you wish to apply both of them to the offset - layer in deformable convs, set ``dcn_offset_lr_mult`` to the original - ``dcn_offset_lr_mult`` * ``bias_lr_mult``. - - 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will - apply it to all the DCN layers in the model. So be careful when the - model contains multiple DCN layers in places other than backbone. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - optimizer_cfg (dict): The config dict of the optimizer. - Positional fields are - - - `type`: class name of the optimizer. - - Optional fields are - - - any arguments of the corresponding optimizer type, e.g., - lr, weight_decay, momentum, etc. - paramwise_cfg (dict, optional): Parameter-wise options. - - Example 1: - >>> model = torch.nn.modules.Conv1d(1, 1, 1) - >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, - >>> weight_decay=0.0001) - >>> paramwise_cfg = dict(norm_decay_mult=0.) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - - Example 2: - >>> # assume model have attribute model.backbone and model.cls_head - >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) - >>> paramwise_cfg = dict(custom_keys={ - 'backbone': dict(lr_mult=0.1, decay_mult=0.9)}) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - >>> # Then the `lr` and `weight_decay` for model.backbone is - >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for - >>> # model.cls_head is (0.01, 0.95). - """ - - def __init__(self, optimizer_cfg, paramwise_cfg=None): - if not isinstance(optimizer_cfg, dict): - raise TypeError('optimizer_cfg should be a dict', - f'but got {type(optimizer_cfg)}') - self.optimizer_cfg = optimizer_cfg - self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg - self.base_lr = optimizer_cfg.get('lr', None) - self.base_wd = optimizer_cfg.get('weight_decay', None) - self._validate_cfg() - - def _validate_cfg(self): - if not isinstance(self.paramwise_cfg, dict): - raise TypeError('paramwise_cfg should be None or a dict, ' - f'but got {type(self.paramwise_cfg)}') - - if 'custom_keys' in self.paramwise_cfg: - if not isinstance(self.paramwise_cfg['custom_keys'], dict): - raise TypeError( - 'If specified, custom_keys must be a dict, ' - f'but got {type(self.paramwise_cfg["custom_keys"])}') - if self.base_wd is None: - for key in self.paramwise_cfg['custom_keys']: - if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: - raise ValueError('base_wd should not be None') - - # get base lr and weight decay - # weight_decay must be explicitly specified if mult is specified - if ('bias_decay_mult' in self.paramwise_cfg - or 'norm_decay_mult' in self.paramwise_cfg - or 'dwconv_decay_mult' in self.paramwise_cfg): - if self.base_wd is None: - raise ValueError('base_wd should not be None') - - def _is_in(self, param_group, param_group_list): - assert is_list_of(param_group_list, dict) - param = set(param_group['params']) - param_set = set() - for group in param_group_list: - param_set.update(set(group['params'])) - - return not param.isdisjoint(param_set) - - def add_params(self, params, module, prefix='', is_dcn_module=None): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - prefix (str): The prefix of the module - is_dcn_module (int|float|None): If the current module is a - submodule of DCN, `is_dcn_module` will be passed to - control conv_offset layer's learning rate. Defaults to None. - """ - # get param-wise options - custom_keys = self.paramwise_cfg.get('custom_keys', {}) - # first sort with alphabet order and then sort with reversed len of str - sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) - - bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) - bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) - norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) - dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) - bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) - dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) - - # special rules for norm layers and depth-wise conv layers - is_norm = isinstance(module, - (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) - is_dwconv = ( - isinstance(module, torch.nn.Conv2d) - and module.in_channels == module.groups) - - for name, param in module.named_parameters(recurse=False): - param_group = {'params': [param]} - if not param.requires_grad: - params.append(param_group) - continue - if bypass_duplicate and self._is_in(param_group, params): - warnings.warn(f'{prefix} is duplicate. It is skipped since ' - f'bypass_duplicate={bypass_duplicate}') - continue - # if the parameter match one of the custom keys, ignore other rules - is_custom = False - for key in sorted_keys: - if key in f'{prefix}.{name}': - is_custom = True - lr_mult = custom_keys[key].get('lr_mult', 1.) - param_group['lr'] = self.base_lr * lr_mult - if self.base_wd is not None: - decay_mult = custom_keys[key].get('decay_mult', 1.) - param_group['weight_decay'] = self.base_wd * decay_mult - break - - if not is_custom: - # bias_lr_mult affects all bias parameters - # except for norm.bias dcn.conv_offset.bias - if name == 'bias' and not (is_norm or is_dcn_module): - param_group['lr'] = self.base_lr * bias_lr_mult - - if (prefix.find('conv_offset') != -1 and is_dcn_module - and isinstance(module, torch.nn.Conv2d)): - # deal with both dcn_offset's bias & weight - param_group['lr'] = self.base_lr * dcn_offset_lr_mult - - # apply weight decay policies - if self.base_wd is not None: - # norm decay - if is_norm: - param_group[ - 'weight_decay'] = self.base_wd * norm_decay_mult - # depth-wise conv - elif is_dwconv: - param_group[ - 'weight_decay'] = self.base_wd * dwconv_decay_mult - # bias lr and decay - elif name == 'bias' and not is_dcn_module: - # TODO: current bias_decay_mult will have affect on DCN - param_group[ - 'weight_decay'] = self.base_wd * bias_decay_mult - params.append(param_group) - - if check_ops_exist(): - from mmcv.ops import DeformConv2d, ModulatedDeformConv2d - is_dcn_module = isinstance(module, - (DeformConv2d, ModulatedDeformConv2d)) - else: - is_dcn_module = False - for child_name, child_mod in module.named_children(): - child_prefix = f'{prefix}.{child_name}' if prefix else child_name - self.add_params( - params, - child_mod, - prefix=child_prefix, - is_dcn_module=is_dcn_module) - - def __call__(self, model): - if hasattr(model, 'module'): - model = model.module - - optimizer_cfg = self.optimizer_cfg.copy() - # if no paramwise option is specified, just use the global setting - if not self.paramwise_cfg: - optimizer_cfg['params'] = model.parameters() - return build_from_cfg(optimizer_cfg, OPTIMIZERS) - - # set param-wise lr and weight decay recursively - params = [] - self.add_params(params, model) - optimizer_cfg['params'] = params - - return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/priority.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/priority.py deleted file mode 100644 index 64cc4e3a0..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/priority.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from enum import Enum - - -class Priority(Enum): - """Hook priority levels. - - +--------------+------------+ - | Level | Value | - +==============+============+ - | HIGHEST | 0 | - +--------------+------------+ - | VERY_HIGH | 10 | - +--------------+------------+ - | HIGH | 30 | - +--------------+------------+ - | ABOVE_NORMAL | 40 | - +--------------+------------+ - | NORMAL | 50 | - +--------------+------------+ - | BELOW_NORMAL | 60 | - +--------------+------------+ - | LOW | 70 | - +--------------+------------+ - | VERY_LOW | 90 | - +--------------+------------+ - | LOWEST | 100 | - +--------------+------------+ - """ - - HIGHEST = 0 - VERY_HIGH = 10 - HIGH = 30 - ABOVE_NORMAL = 40 - NORMAL = 50 - BELOW_NORMAL = 60 - LOW = 70 - VERY_LOW = 90 - LOWEST = 100 - - -def get_priority(priority): - """Get priority value. - - Args: - priority (int or str or :obj:`Priority`): Priority. - - Returns: - int: The priority value. - """ - if isinstance(priority, int): - if priority < 0 or priority > 100: - raise ValueError('priority must be between 0 and 100') - return priority - elif isinstance(priority, Priority): - return priority.value - elif isinstance(priority, str): - return Priority[priority.upper()].value - else: - raise TypeError('priority must be an integer or Priority enum value') diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/utils.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/utils.py deleted file mode 100644 index 144d11e1a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/runner/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random -import sys -import time -import warnings -from getpass import getuser -from socket import gethostname - -import numpy as np -import torch - -import mmcv - - -def get_host_info(): - """Get hostname and username. - - Return empty string if exception raised, e.g. ``getpass.getuser()`` will - lead to error in docker container - """ - host = '' - try: - host = f'{getuser()}@{gethostname()}' - except Exception as e: - warnings.warn(f'Host or user not found: {str(e)}') - finally: - return host - - -def get_time_str(): - return time.strftime('%Y%m%d_%H%M%S', time.localtime()) - - -def obj_from_dict(info, parent=None, default_args=None): - """Initialize an object from dict. - - The dict must contain the key "type", which indicates the object type, it - can be either a string or type, such as "list" or ``list``. Remaining - fields are treated as the arguments for constructing the object. - - Args: - info (dict): Object types and arguments. - parent (:class:`module`): Module which may containing expected object - classes. - default_args (dict, optional): Default arguments for initializing the - object. - - Returns: - any type: Object built from the dict. - """ - assert isinstance(info, dict) and 'type' in info - assert isinstance(default_args, dict) or default_args is None - args = info.copy() - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if parent is not None: - obj_type = getattr(parent, obj_type) - else: - obj_type = sys.modules[obj_type] - elif not isinstance(obj_type, type): - raise TypeError('type must be a str or valid type, but ' - f'got {type(obj_type)}') - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - return obj_type(**args) - - -def set_random_seed(seed, deterministic=False, use_rank_shift=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - rank_shift (bool): Whether to add rank number to the random seed to - have different random seed in different threads. Default: False. - """ - if use_rank_shift: - rank, _ = mmcv.runner.get_dist_info() - seed += rank - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/__init__.py deleted file mode 100644 index e0825ed67..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/__init__.py +++ /dev/null @@ -1,78 +0,0 @@ -# flake8: noqa -# Copyright (c) OpenMMLab. All rights reserved. -from .config import Config, ConfigDict, DictAction -from .misc import (check_prerequisites, concat_list, deprecated_api_warning, - has_method, import_modules_from_strings, is_list_of, - is_method_overridden, is_seq_of, is_str, is_tuple_of, - iter_cast, list_cast, requires_executable, requires_package, - slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, - to_ntuple, tuple_cast) -from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, - scandir, symlink) -from .progressbar import (ProgressBar, track_iter_progress, - track_parallel_progress, track_progress) -from .testing import (assert_attrs_equal, assert_dict_contains_subset, - assert_dict_has_keys, assert_is_norm_layer, - assert_keys_equal, assert_params_all_zeros, - check_python_script) -from .timer import Timer, TimerError, check_time -from .version_utils import digit_version, get_git_hash - -try: - import torch -except ImportError: - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', - 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', - 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', - 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', - 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', - 'track_progress', 'track_iter_progress', 'track_parallel_progress', - 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', - 'digit_version', 'get_git_hash', 'import_modules_from_strings', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', - 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', - 'is_method_overridden', 'has_method' - ] -else: - from .device_type import IS_IPU_AVAILABLE, IS_MLU_AVAILABLE - from .env import collect_env - from .hub import load_url - from .logging import get_logger, print_log - from .parrots_jit import jit, skip_no_elena - # yapf: disable - from .parrots_wrapper import (IS_CUDA_AVAILABLE, TORCH_VERSION, - BuildExtension, CppExtension, CUDAExtension, - DataLoader, PoolDataLoader, SyncBatchNorm, - _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, - _AvgPoolNd, _BatchNorm, _ConvNd, - _ConvTransposeMixin, _get_cuda_home, - _InstanceNorm, _MaxPoolNd, get_build_config, - is_rocm_pytorch) - # yapf: enable - from .registry import Registry, build_from_cfg - from .seed import worker_init_fn - from .trace import is_jit_tracing - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', - 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', - 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', - 'check_prerequisites', 'requires_package', 'requires_executable', - 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', - 'symlink', 'scandir', 'ProgressBar', 'track_progress', - 'track_iter_progress', 'track_parallel_progress', 'Registry', - 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', - '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', - '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', - 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', - 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', - 'deprecated_api_warning', 'digit_version', 'get_git_hash', - 'import_modules_from_strings', 'jit', 'skip_no_elena', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', - 'assert_params_all_zeros', 'check_python_script', - 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', - '_get_cuda_home', 'load_url', 'has_method', 'IS_CUDA_AVAILABLE', - 'worker_init_fn', 'IS_MLU_AVAILABLE', 'IS_IPU_AVAILABLE' - ] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/config.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/config.py deleted file mode 100644 index 8efbc24e2..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/config.py +++ /dev/null @@ -1,719 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import ast -import copy -import os -import os.path as osp -import platform -import shutil -import sys -import tempfile -import types -import uuid -import warnings -from argparse import Action, ArgumentParser -from collections import abc -from importlib import import_module -from pathlib import Path - -from addict import Dict -from yapf.yapflib.yapf_api import FormatCode - -from .misc import import_modules_from_strings -from .path import check_file_exist - -if platform.system() == 'Windows': - import regex as re -else: - import re - -BASE_KEY = '_base_' -DELETE_KEY = '_delete_' -DEPRECATION_KEY = '_deprecation_' -RESERVED_KEYS = ['filename', 'text', 'pretty_text'] - - -class ConfigDict(Dict): - - def __missing__(self, name): - raise KeyError(name) - - def __getattr__(self, name): - try: - value = super(ConfigDict, self).__getattr__(name) - except KeyError: - ex = AttributeError(f"'{self.__class__.__name__}' object has no " - f"attribute '{name}'") - except Exception as e: - ex = e - else: - return value - raise ex - - -def add_args(parser, cfg, prefix=''): - for k, v in cfg.items(): - if isinstance(v, str): - parser.add_argument('--' + prefix + k) - elif isinstance(v, int): - parser.add_argument('--' + prefix + k, type=int) - elif isinstance(v, float): - parser.add_argument('--' + prefix + k, type=float) - elif isinstance(v, bool): - parser.add_argument('--' + prefix + k, action='store_true') - elif isinstance(v, dict): - add_args(parser, v, prefix + k + '.') - elif isinstance(v, abc.Iterable): - parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') - else: - print(f'cannot parse key {prefix + k} of type {type(v)}') - return parser - - -class Config: - """A facility for config and config files. - - It supports common file formats as configs: python/json/yaml. The interface - is the same as a dict object and also allows access config values as - attributes. - - Example: - >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - >>> cfg.a - 1 - >>> cfg.b - {'b1': [0, 1]} - >>> cfg.b.b1 - [0, 1] - >>> cfg = Config.fromfile('tests/data/config/a.py') - >>> cfg.filename - "/home/kchen/projects/mmcv/tests/data/config/a.py" - >>> cfg.item4 - 'test' - >>> cfg - "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " - "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" - """ - - @staticmethod - def _validate_py_syntax(filename): - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - content = f.read() - try: - ast.parse(content) - except SyntaxError as e: - raise SyntaxError('There are syntax errors in config ' - f'file {filename}: {e}') - - @staticmethod - def _substitute_predefined_vars(filename, temp_config_name): - file_dirname = osp.dirname(filename) - file_basename = osp.basename(filename) - file_basename_no_extension = osp.splitext(file_basename)[0] - file_extname = osp.splitext(filename)[1] - support_templates = dict( - fileDirname=file_dirname, - fileBasename=file_basename, - fileBasenameNoExtension=file_basename_no_extension, - fileExtname=file_extname) - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - for key, value in support_templates.items(): - regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' - value = value.replace('\\', '/') - config_file = re.sub(regexp, value, config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - - @staticmethod - def _pre_substitute_base_vars(filename, temp_config_name): - """Substitute base variable placehoders to string, so that parsing - would work.""" - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - base_var_dict = {} - regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' - base_vars = set(re.findall(regexp, config_file)) - for base_var in base_vars: - randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' - base_var_dict[randstr] = base_var - regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' - config_file = re.sub(regexp, f'"{randstr}"', config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - return base_var_dict - - @staticmethod - def _substitute_base_vars(cfg, base_var_dict, base_cfg): - """Substitute variable strings to their actual values.""" - cfg = copy.deepcopy(cfg) - - if isinstance(cfg, dict): - for k, v in cfg.items(): - if isinstance(v, str) and v in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[v].split('.'): - new_v = new_v[new_k] - cfg[k] = new_v - elif isinstance(v, (list, tuple, dict)): - cfg[k] = Config._substitute_base_vars( - v, base_var_dict, base_cfg) - elif isinstance(cfg, tuple): - cfg = tuple( - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg) - elif isinstance(cfg, list): - cfg = [ - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg - ] - elif isinstance(cfg, str) and cfg in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[cfg].split('.'): - new_v = new_v[new_k] - cfg = new_v - - return cfg - - @staticmethod - def _file2dict(filename, use_predefined_variables=True): - filename = osp.abspath(osp.expanduser(filename)) - check_file_exist(filename) - fileExtname = osp.splitext(filename)[1] - if fileExtname not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - - with tempfile.TemporaryDirectory() as temp_config_dir: - temp_config_file = tempfile.NamedTemporaryFile( - dir=temp_config_dir, suffix=fileExtname) - if platform.system() == 'Windows': - temp_config_file.close() - temp_config_name = osp.basename(temp_config_file.name) - # Substitute predefined variables - if use_predefined_variables: - Config._substitute_predefined_vars(filename, - temp_config_file.name) - else: - shutil.copyfile(filename, temp_config_file.name) - # Substitute base variables from placeholders to strings - base_var_dict = Config._pre_substitute_base_vars( - temp_config_file.name, temp_config_file.name) - - if filename.endswith('.py'): - temp_module_name = osp.splitext(temp_config_name)[0] - sys.path.insert(0, temp_config_dir) - Config._validate_py_syntax(filename) - mod = import_module(temp_module_name) - sys.path.pop(0) - cfg_dict = { - name: value - for name, value in mod.__dict__.items() - if not name.startswith('__') - and not isinstance(value, types.ModuleType) - and not isinstance(value, types.FunctionType) - } - # delete imported module - del sys.modules[temp_module_name] - elif filename.endswith(('.yml', '.yaml', '.json')): - import mmcv - cfg_dict = mmcv.load(temp_config_file.name) - # close temp file - temp_config_file.close() - - # check deprecation information - if DEPRECATION_KEY in cfg_dict: - deprecation_info = cfg_dict.pop(DEPRECATION_KEY) - warning_msg = f'The config file {filename} will be deprecated ' \ - 'in the future.' - if 'expected' in deprecation_info: - warning_msg += f' Please use {deprecation_info["expected"]} ' \ - 'instead.' - if 'reference' in deprecation_info: - warning_msg += ' More information can be found at ' \ - f'{deprecation_info["reference"]}' - warnings.warn(warning_msg, DeprecationWarning) - - cfg_text = filename + '\n' - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - cfg_text += f.read() - - if BASE_KEY in cfg_dict: - cfg_dir = osp.dirname(filename) - base_filename = cfg_dict.pop(BASE_KEY) - base_filename = base_filename if isinstance( - base_filename, list) else [base_filename] - - cfg_dict_list = list() - cfg_text_list = list() - for f in base_filename: - _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) - cfg_dict_list.append(_cfg_dict) - cfg_text_list.append(_cfg_text) - - base_cfg_dict = dict() - for c in cfg_dict_list: - duplicate_keys = base_cfg_dict.keys() & c.keys() - if len(duplicate_keys) > 0: - raise KeyError('Duplicate key is not allowed among bases. ' - f'Duplicate keys: {duplicate_keys}') - base_cfg_dict.update(c) - - # Substitute base variables from strings to their actual values - cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, - base_cfg_dict) - - base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) - cfg_dict = base_cfg_dict - - # merge cfg_text - cfg_text_list.append(cfg_text) - cfg_text = '\n'.join(cfg_text_list) - - return cfg_dict, cfg_text - - @staticmethod - def _merge_a_into_b(a, b, allow_list_keys=False): - """merge dict ``a`` into dict ``b`` (non-inplace). - - Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid - in-place modifications. - - Args: - a (dict): The source dict to be merged into ``b``. - b (dict): The origin dict to be fetch keys from ``a``. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in source ``a`` and will replace the element of the - corresponding index in b if b is a list. Default: False. - - Returns: - dict: The modified dict of ``b`` using ``a``. - - Examples: - # Normally merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # Delete b first and merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # b is a list - >>> Config._merge_a_into_b( - ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) - [{'a': 2}, {'b': 2}] - """ - b = b.copy() - for k, v in a.items(): - if allow_list_keys and k.isdigit() and isinstance(b, list): - k = int(k) - if len(b) <= k: - raise KeyError(f'Index {k} exceeds the length of list {b}') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - elif isinstance(v, dict): - if k in b and not v.pop(DELETE_KEY, False): - allowed_types = (dict, list) if allow_list_keys else dict - if not isinstance(b[k], allowed_types): - raise TypeError( - f'{k}={v} in child config cannot inherit from ' - f'base because {k} is a dict in the child config ' - f'but is of type {type(b[k])} in base config. ' - f'You may set `{DELETE_KEY}=True` to ignore the ' - f'base config.') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - else: - b[k] = ConfigDict(v) - else: - b[k] = v - return b - - @staticmethod - def fromfile(filename, - use_predefined_variables=True, - import_custom_modules=True): - if isinstance(filename, Path): - filename = str(filename) - cfg_dict, cfg_text = Config._file2dict(filename, - use_predefined_variables) - if import_custom_modules and cfg_dict.get('custom_imports', None): - import_modules_from_strings(**cfg_dict['custom_imports']) - return Config(cfg_dict, cfg_text=cfg_text, filename=filename) - - @staticmethod - def fromstring(cfg_str, file_format): - """Generate config from config str. - - Args: - cfg_str (str): Config str. - file_format (str): Config file format corresponding to the - config str. Only py/yml/yaml/json type are supported now! - - Returns: - :obj:`Config`: Config obj. - """ - if file_format not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - if file_format != '.py' and 'dict(' in cfg_str: - # check if users specify a wrong suffix for python - warnings.warn( - 'Please check "file_format", the file format may be .py') - with tempfile.NamedTemporaryFile( - 'w', encoding='utf-8', suffix=file_format, - delete=False) as temp_file: - temp_file.write(cfg_str) - # on windows, previous implementation cause error - # see PR 1077 for details - cfg = Config.fromfile(temp_file.name) - os.remove(temp_file.name) - return cfg - - @staticmethod - def auto_argparser(description=None): - """Generate argparser from config file automatically (experimental)""" - partial_parser = ArgumentParser(description=description) - partial_parser.add_argument('config', help='config file path') - cfg_file = partial_parser.parse_known_args()[0].config - cfg = Config.fromfile(cfg_file) - parser = ArgumentParser(description=description) - parser.add_argument('config', help='config file path') - add_args(parser, cfg) - return parser, cfg - - def __init__(self, cfg_dict=None, cfg_text=None, filename=None): - if cfg_dict is None: - cfg_dict = dict() - elif not isinstance(cfg_dict, dict): - raise TypeError('cfg_dict must be a dict, but ' - f'got {type(cfg_dict)}') - for key in cfg_dict: - if key in RESERVED_KEYS: - raise KeyError(f'{key} is reserved for config file') - - if isinstance(filename, Path): - filename = str(filename) - - super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) - super(Config, self).__setattr__('_filename', filename) - if cfg_text: - text = cfg_text - elif filename: - with open(filename, 'r') as f: - text = f.read() - else: - text = '' - super(Config, self).__setattr__('_text', text) - - @property - def filename(self): - return self._filename - - @property - def text(self): - return self._text - - @property - def pretty_text(self): - - indent = 4 - - def _indent(s_, num_spaces): - s = s_.split('\n') - if len(s) == 1: - return s_ - first = s.pop(0) - s = [(num_spaces * ' ') + line for line in s] - s = '\n'.join(s) - s = first + '\n' + s - return s - - def _format_basic_types(k, v, use_mapping=False): - if isinstance(v, str): - v_str = f"'{v}'" - else: - v_str = str(v) - - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) - - return attr_str - - def _format_list(k, v, use_mapping=False): - # check if all items in the list are dict - if all(isinstance(_, dict) for _ in v): - v_str = '[\n' - v_str += '\n'.join( - f'dict({_indent(_format_dict(v_), indent)}),' - for v_ in v).rstrip(',') - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) + ']' - else: - attr_str = _format_basic_types(k, v, use_mapping) - return attr_str - - def _contain_invalid_identifier(dict_str): - contain_invalid_identifier = False - for key_name in dict_str: - contain_invalid_identifier |= \ - (not str(key_name).isidentifier()) - return contain_invalid_identifier - - def _format_dict(input_dict, outest_level=False): - r = '' - s = [] - - use_mapping = _contain_invalid_identifier(input_dict) - if use_mapping: - r += '{' - for idx, (k, v) in enumerate(input_dict.items()): - is_last = idx >= len(input_dict) - 1 - end = '' if outest_level or is_last else ',' - if isinstance(v, dict): - v_str = '\n' + _format_dict(v) - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: dict({v_str}' - else: - attr_str = f'{str(k)}=dict({v_str}' - attr_str = _indent(attr_str, indent) + ')' + end - elif isinstance(v, list): - attr_str = _format_list(k, v, use_mapping) + end - else: - attr_str = _format_basic_types(k, v, use_mapping) + end - - s.append(attr_str) - r += '\n'.join(s) - if use_mapping: - r += '}' - return r - - cfg_dict = self._cfg_dict.to_dict() - text = _format_dict(cfg_dict, outest_level=True) - # copied from setup.cfg - yapf_style = dict( - based_on_style='pep8', - blank_line_before_nested_class_or_def=True, - split_before_expression_after_opening_paren=True) - text, _ = FormatCode(text, style_config=yapf_style, verify=True) - - return text - - def __repr__(self): - return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' - - def __len__(self): - return len(self._cfg_dict) - - def __getattr__(self, name): - return getattr(self._cfg_dict, name) - - def __getitem__(self, name): - return self._cfg_dict.__getitem__(name) - - def __setattr__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setattr__(name, value) - - def __setitem__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setitem__(name, value) - - def __iter__(self): - return iter(self._cfg_dict) - - def __getstate__(self): - return (self._cfg_dict, self._filename, self._text) - - def __copy__(self): - cls = self.__class__ - other = cls.__new__(cls) - other.__dict__.update(self.__dict__) - - return other - - def __deepcopy__(self, memo): - cls = self.__class__ - other = cls.__new__(cls) - memo[id(self)] = other - - for key, value in self.__dict__.items(): - super(Config, other).__setattr__(key, copy.deepcopy(value, memo)) - - return other - - def __setstate__(self, state): - _cfg_dict, _filename, _text = state - super(Config, self).__setattr__('_cfg_dict', _cfg_dict) - super(Config, self).__setattr__('_filename', _filename) - super(Config, self).__setattr__('_text', _text) - - def dump(self, file=None): - cfg_dict = super(Config, self).__getattribute__('_cfg_dict').to_dict() - if self.filename.endswith('.py'): - if file is None: - return self.pretty_text - else: - with open(file, 'w', encoding='utf-8') as f: - f.write(self.pretty_text) - else: - import mmcv - if file is None: - file_format = self.filename.split('.')[-1] - return mmcv.dump(cfg_dict, file_format=file_format) - else: - mmcv.dump(cfg_dict, file) - - def merge_from_dict(self, options, allow_list_keys=True): - """Merge list into cfg_dict. - - Merge the dict parsed by MultipleKVAction into this cfg. - - Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} - >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) - >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - - >>> # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) - >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) - >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - - Args: - options (dict): dict of configs to merge from. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in ``options`` and will replace the element of the - corresponding index in the config if the config is a list. - Default: True. - """ - option_cfg_dict = {} - for full_key, v in options.items(): - d = option_cfg_dict - key_list = full_key.split('.') - for subkey in key_list[:-1]: - d.setdefault(subkey, ConfigDict()) - d = d[subkey] - subkey = key_list[-1] - d[subkey] = v - - cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - super(Config, self).__setattr__( - '_cfg_dict', - Config._merge_a_into_b( - option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) - - -class DictAction(Action): - """ - argparse action to split an argument into KEY=VALUE form - on the first = and append to a dictionary. List options can - be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit - brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build - list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' - """ - - @staticmethod - def _parse_int_float_bool(val): - try: - return int(val) - except ValueError: - pass - try: - return float(val) - except ValueError: - pass - if val.lower() in ['true', 'false']: - return True if val.lower() == 'true' else False - if val == 'None': - return None - return val - - @staticmethod - def _parse_iterable(val): - """Parse iterable values in the string. - - All elements inside '()' or '[]' are treated as iterable values. - - Args: - val (str): Value string. - - Returns: - list | tuple: The expanded list or tuple from the string. - - Examples: - >>> DictAction._parse_iterable('1,2,3') - [1, 2, 3] - >>> DictAction._parse_iterable('[a, b, c]') - ['a', 'b', 'c'] - >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') - [(1, 2, 3), ['a', 'b'], 'c'] - """ - - def find_next_comma(string): - """Find the position of next comma in the string. - - If no ',' is found in the string, return the string length. All - chars inside '()' and '[]' are treated as one element and thus ',' - inside these brackets are ignored. - """ - assert (string.count('(') == string.count(')')) and ( - string.count('[') == string.count(']')), \ - f'Imbalanced brackets exist in {string}' - end = len(string) - for idx, char in enumerate(string): - pre = string[:idx] - # The string before this ',' is balanced - if ((char == ',') and (pre.count('(') == pre.count(')')) - and (pre.count('[') == pre.count(']'))): - end = idx - break - return end - - # Strip ' and " characters and replace whitespace. - val = val.strip('\'\"').replace(' ', '') - is_tuple = False - if val.startswith('(') and val.endswith(')'): - is_tuple = True - val = val[1:-1] - elif val.startswith('[') and val.endswith(']'): - val = val[1:-1] - elif ',' not in val: - # val is a single value - return DictAction._parse_int_float_bool(val) - - values = [] - while len(val) > 0: - comma_idx = find_next_comma(val) - element = DictAction._parse_iterable(val[:comma_idx]) - values.append(element) - val = val[comma_idx + 1:] - if is_tuple: - values = tuple(values) - return values - - def __call__(self, parser, namespace, values, option_string=None): - options = {} - for kv in values: - key, val = kv.split('=', maxsplit=1) - options[key] = self._parse_iterable(val) - setattr(namespace, self.dest, options) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/device_type.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/device_type.py deleted file mode 100644 index c29d944ab..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/device_type.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - - -def is_ipu_available(): - try: - import poptorch - return poptorch.ipuHardwareIsAvailable() - except ImportError: - return False - - -IS_IPU_AVAILABLE = is_ipu_available() - - -def is_mlu_available(): - try: - import torch - return (hasattr(torch, 'is_mlu_available') - and torch.is_mlu_available()) - except Exception: - return False - - -IS_MLU_AVAILABLE = is_mlu_available() diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/env.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/env.py deleted file mode 100644 index 511332506..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/env.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This file holding some environment constant for sharing by other files.""" - -import os.path as osp -import subprocess -import sys -from collections import defaultdict - -import cv2 -import torch - -import mmcv -from .parrots_wrapper import get_build_config - - -def collect_env(): - """Collect the information of the running environments. - - Returns: - dict: The environment information. The following fields are contained. - - - sys.platform: The variable of ``sys.platform``. - - Python: Python version. - - CUDA available: Bool, indicating if CUDA is available. - - GPU devices: Device type of each GPU. - - CUDA_HOME (optional): The env var ``CUDA_HOME``. - - NVCC (optional): NVCC version. - - GCC: GCC version, "n/a" if GCC is not installed. - - MSVC: Microsoft Virtual C++ Compiler version, Windows only. - - PyTorch: PyTorch version. - - PyTorch compiling details: The output of \ - ``torch.__config__.show()``. - - TorchVision (optional): TorchVision version. - - OpenCV: OpenCV version. - - MMCV: MMCV version. - - MMCV Compiler: The GCC version for compiling MMCV ops. - - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. - """ - env_info = {} - env_info['sys.platform'] = sys.platform - env_info['Python'] = sys.version.replace('\n', '') - - cuda_available = torch.cuda.is_available() - env_info['CUDA available'] = cuda_available - - if cuda_available: - devices = defaultdict(list) - for k in range(torch.cuda.device_count()): - devices[torch.cuda.get_device_name(k)].append(str(k)) - for name, device_ids in devices.items(): - env_info['GPU ' + ','.join(device_ids)] = name - - from mmcv.utils.parrots_wrapper import _get_cuda_home - CUDA_HOME = _get_cuda_home() - env_info['CUDA_HOME'] = CUDA_HOME - - if CUDA_HOME is not None and osp.isdir(CUDA_HOME): - try: - nvcc = osp.join(CUDA_HOME, 'bin/nvcc') - nvcc = subprocess.check_output(f'"{nvcc}" -V', shell=True) - nvcc = nvcc.decode('utf-8').strip() - release = nvcc.rfind('Cuda compilation tools') - build = nvcc.rfind('Build ') - nvcc = nvcc[release:build].strip() - except subprocess.SubprocessError: - nvcc = 'Not Available' - env_info['NVCC'] = nvcc - - try: - # Check C++ Compiler. - # For Unix-like, sysconfig has 'CC' variable like 'gcc -pthread ...', - # indicating the compiler used, we use this to get the compiler name - import sysconfig - cc = sysconfig.get_config_var('CC') - if cc: - cc = osp.basename(cc.split()[0]) - cc_info = subprocess.check_output(f'{cc} --version', shell=True) - env_info['GCC'] = cc_info.decode('utf-8').partition( - '\n')[0].strip() - else: - # on Windows, cl.exe is not in PATH. We need to find the path. - # distutils.ccompiler.new_compiler() returns a msvccompiler - # object and after initialization, path to cl.exe is found. - import locale - import os - from distutils.ccompiler import new_compiler - ccompiler = new_compiler() - ccompiler.initialize() - cc = subprocess.check_output( - f'{ccompiler.cc}', stderr=subprocess.STDOUT, shell=True) - encoding = os.device_encoding( - sys.stdout.fileno()) or locale.getpreferredencoding() - env_info['MSVC'] = cc.decode(encoding).partition('\n')[0].strip() - env_info['GCC'] = 'n/a' - except subprocess.CalledProcessError: - env_info['GCC'] = 'n/a' - - env_info['PyTorch'] = torch.__version__ - env_info['PyTorch compiling details'] = get_build_config() - - try: - import torchvision - env_info['TorchVision'] = torchvision.__version__ - except ModuleNotFoundError: - pass - - env_info['OpenCV'] = cv2.__version__ - - env_info['MMCV'] = mmcv.__version__ - - try: - from mmcv.ops import get_compiler_version, get_compiling_cuda_version - except ModuleNotFoundError: - env_info['MMCV Compiler'] = 'n/a' - env_info['MMCV CUDA Compiler'] = 'n/a' - else: - env_info['MMCV Compiler'] = get_compiler_version() - env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() - - return env_info diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/ext_loader.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/ext_loader.py deleted file mode 100644 index f82c6d568..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/ext_loader.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os -import pkgutil -import warnings -from collections import namedtuple - -import torch - -if torch.__version__ != 'parrots': - - def load_ext(name, funcs): - ext = importlib.import_module('mmcv.' + name) - for fun in funcs: - assert hasattr(ext, fun), f'{fun} miss in module {name}' - return ext -else: - from parrots import extension - from parrots.base import ParrotsException - - has_return_value_ops = [ - 'nms', - 'softnms', - 'nms_match', - 'nms_rotated', - 'top_pool_forward', - 'top_pool_backward', - 'bottom_pool_forward', - 'bottom_pool_backward', - 'left_pool_forward', - 'left_pool_backward', - 'right_pool_forward', - 'right_pool_backward', - 'fused_bias_leakyrelu', - 'upfirdn2d', - 'ms_deform_attn_forward', - 'pixel_group', - 'contour_expand', - 'diff_iou_rotated_sort_vertices_forward', - ] - - def get_fake_func(name, e): - - def fake_func(*args, **kwargs): - warnings.warn(f'{name} is not supported in parrots now') - raise e - - return fake_func - - def load_ext(name, funcs): - ExtModule = namedtuple('ExtModule', funcs) - ext_list = [] - lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - for fun in funcs: - try: - ext_fun = extension.load(fun, name, lib_dir=lib_root) - except ParrotsException as e: - if 'No element registered' not in e.message: - warnings.warn(e.message) - ext_fun = get_fake_func(fun, e) - ext_list.append(ext_fun) - else: - if fun in has_return_value_ops: - ext_list.append(ext_fun.op) - else: - ext_list.append(ext_fun.op_) - return ExtModule(*ext_list) - - -def check_ops_exist(): - ext_loader = pkgutil.find_loader('mmcv._ext') - return ext_loader is not None diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/hub.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/hub.py deleted file mode 100644 index 12fbff2ee..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/hub.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# The 1.6 release of PyTorch switched torch.save to use a new zipfile-based -# file format. It will cause RuntimeError when a checkpoint was saved in -# torch >= 1.6.0 but loaded in torch < 1.7.0. -# More details at https://github.com/open-mmlab/mmpose/issues/904 -from .parrots_wrapper import TORCH_VERSION -from .path import mkdir_or_exist -from .version_utils import digit_version - -if TORCH_VERSION != 'parrots' and digit_version(TORCH_VERSION) < digit_version( - '1.7.0'): - # Modified from https://github.com/pytorch/pytorch/blob/master/torch/hub.py - import os - import sys - import warnings - import zipfile - from urllib.parse import urlparse - - import torch - from torch.hub import HASH_REGEX, _get_torch_home, download_url_to_file - - # Hub used to support automatically extracts from zipfile manually - # compressed by users. The legacy zip format expects only one file from - # torch.save() < 1.6 in the zip. We should remove this support since - # zipfile is now default zipfile format for torch.save(). - def _is_legacy_zip_format(filename): - if zipfile.is_zipfile(filename): - infolist = zipfile.ZipFile(filename).infolist() - return len(infolist) == 1 and not infolist[0].is_dir() - return False - - def _legacy_zip_load(filename, model_dir, map_location): - warnings.warn( - 'Falling back to the old format < 1.6. This support will' - ' be deprecated in favor of default zipfile format ' - 'introduced in 1.6. Please redo torch.save() to save it ' - 'in the new zipfile format.', DeprecationWarning) - # Note: extractall() defaults to overwrite file if exists. No need to - # clean up beforehand. We deliberately don't handle tarfile here - # since our legacy serialization format was in tar. - # E.g. resnet18-5c106cde.pth which is widely used. - with zipfile.ZipFile(filename) as f: - members = f.infolist() - if len(members) != 1: - raise RuntimeError( - 'Only one file(not dir) is allowed in the zipfile') - f.extractall(model_dir) - extraced_name = members[0].filename - extracted_file = os.path.join(model_dir, extraced_name) - return torch.load(extracted_file, map_location=map_location) - - def load_url(url, - model_dir=None, - map_location=None, - progress=True, - check_hash=False, - file_name=None): - r"""Loads the Torch serialized object at the given URL. - - If downloaded file is a zip file, it will be automatically decompressed - - If the object is already present in `model_dir`, it's deserialized and - returned. - The default value of ``model_dir`` is ``/checkpoints`` where - ``hub_dir`` is the directory returned by :func:`~torch.hub.get_dir`. - - Args: - url (str): URL of the object to download - model_dir (str, optional): directory in which to save the object - map_location (optional): a function or a dict specifying how to - remap storage locations (see torch.load) - progress (bool, optional): whether or not to display a progress bar - to stderr. Default: True - check_hash(bool, optional): If True, the filename part of the URL - should follow the naming convention ``filename-.ext`` - where ```` is the first eight or more digits of the - SHA256 hash of the contents of the file. The hash is used to - ensure unique names and to verify the contents of the file. - Default: False - file_name (str, optional): name for the downloaded file. Filename - from ``url`` will be used if not set. Default: None. - - Example: - >>> url = ('https://s3.amazonaws.com/pytorch/models/resnet18-5c106' - ... 'cde.pth') - >>> state_dict = torch.hub.load_state_dict_from_url(url) - """ - # Issue warning to move data if old env is set - if os.getenv('TORCH_MODEL_ZOO'): - warnings.warn( - 'TORCH_MODEL_ZOO is deprecated, please use env ' - 'TORCH_HOME instead', DeprecationWarning) - - if model_dir is None: - torch_home = _get_torch_home() - model_dir = os.path.join(torch_home, 'checkpoints') - - mkdir_or_exist(model_dir) - - parts = urlparse(url) - filename = os.path.basename(parts.path) - if file_name is not None: - filename = file_name - cached_file = os.path.join(model_dir, filename) - if not os.path.exists(cached_file): - sys.stderr.write('Downloading: "{}" to {}\n'.format( - url, cached_file)) - hash_prefix = None - if check_hash: - r = HASH_REGEX.search(filename) # r is Optional[Match[str]] - hash_prefix = r.group(1) if r else None - download_url_to_file( - url, cached_file, hash_prefix, progress=progress) - - if _is_legacy_zip_format(cached_file): - return _legacy_zip_load(cached_file, model_dir, map_location) - - try: - return torch.load(cached_file, map_location=map_location) - except RuntimeError as error: - if digit_version(TORCH_VERSION) < digit_version('1.5.0'): - warnings.warn( - f'If the error is the same as "{cached_file} is a zip ' - 'archive (did you mean to use torch.jit.load()?)", you can' - ' upgrade your torch to 1.5.0 or higher (current torch ' - f'version is {TORCH_VERSION}). The error was raised ' - ' because the checkpoint was saved in torch>=1.6.0 but ' - 'loaded in torch<1.5.') - raise error -else: - from torch.utils.model_zoo import load_url # noqa: F401 diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/logging.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/logging.py deleted file mode 100644 index c4c7025f0..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/logging.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.distributed as dist - -logger_initialized = {} - - -def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): - """Initialize and get a logger by name. - - If the logger has not been initialized, this method will initialize the - logger by adding one or two handlers, otherwise the initialized logger will - be directly returned. During initialization, a StreamHandler will always be - added. If `log_file` is specified and the process rank is 0, a FileHandler - will also be added. - - Args: - name (str): Logger name. - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the logger. - log_level (int): The logger level. Note that only the process of - rank 0 is affected, and other processes will set the level to - "Error" thus be silent most of the time. - file_mode (str): The file mode used in opening log file. - Defaults to 'w'. - - Returns: - logging.Logger: The expected logger. - """ - logger = logging.getLogger(name) - if name in logger_initialized: - return logger - # handle hierarchical names - # e.g., logger "a" is initialized, then logger "a.b" will skip the - # initialization since it is a child of "a". - for logger_name in logger_initialized: - if name.startswith(logger_name): - return logger - - # handle duplicate logs to the console - # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) - # to the root logger. As logger.propagate is True by default, this root - # level handler causes logging messages from rank>0 processes to - # unexpectedly show up on the console, creating much unwanted clutter. - # To fix this issue, we set the root logger's StreamHandler, if any, to log - # at the ERROR level. - for handler in logger.root.handlers: - if type(handler) is logging.StreamHandler: - handler.setLevel(logging.ERROR) - - stream_handler = logging.StreamHandler() - handlers = [stream_handler] - - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - else: - rank = 0 - - # only rank 0 will add a FileHandler - if rank == 0 and log_file is not None: - # Here, the default behaviour of the official logger is 'a'. Thus, we - # provide an interface to change the file mode to the default - # behaviour. - file_handler = logging.FileHandler(log_file, file_mode) - handlers.append(file_handler) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - for handler in handlers: - handler.setFormatter(formatter) - handler.setLevel(log_level) - logger.addHandler(handler) - - if rank == 0: - logger.setLevel(log_level) - else: - logger.setLevel(logging.ERROR) - - logger_initialized[name] = True - - return logger - - -def print_log(msg, logger=None, level=logging.INFO): - """Print a log message. - - Args: - msg (str): The message to be logged. - logger (logging.Logger | str | None): The logger to be used. - Some special loggers are: - - - "silent": no message will be printed. - - other str: the logger obtained with `get_root_logger(logger)`. - - None: The `print()` method will be used to print log messages. - level (int): Logging level. Only available when `logger` is a Logger - object or "root". - """ - if logger is None: - print(msg) - elif isinstance(logger, logging.Logger): - logger.log(level, msg) - elif logger == 'silent': - pass - elif isinstance(logger, str): - _logger = get_logger(logger) - _logger.log(level, msg) - else: - raise TypeError( - 'logger should be either a logging.Logger object, str, ' - f'"silent" or None, but got {type(logger)}') diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/misc.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/misc.py deleted file mode 100644 index 7957ea89b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/misc.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections.abc -import functools -import itertools -import subprocess -import warnings -from collections import abc -from importlib import import_module -from inspect import getfullargspec -from itertools import repeat - - -# From PyTorch internals -def _ntuple(n): - - def parse(x): - if isinstance(x, collections.abc.Iterable): - return x - return tuple(repeat(x, n)) - - return parse - - -to_1tuple = _ntuple(1) -to_2tuple = _ntuple(2) -to_3tuple = _ntuple(3) -to_4tuple = _ntuple(4) -to_ntuple = _ntuple - - -def is_str(x): - """Whether the input is an string instance. - - Note: This method is deprecated since python 2 is no longer supported. - """ - return isinstance(x, str) - - -def import_modules_from_strings(imports, allow_failed_imports=False): - """Import modules from the given list of strings. - - Args: - imports (list | str | None): The given module names to be imported. - allow_failed_imports (bool): If True, the failed imports will return - None. Otherwise, an ImportError is raise. Default: False. - - Returns: - list[module] | module | None: The imported modules. - - Examples: - >>> osp, sys = import_modules_from_strings( - ... ['os.path', 'sys']) - >>> import os.path as osp_ - >>> import sys as sys_ - >>> assert osp == osp_ - >>> assert sys == sys_ - """ - if not imports: - return - single_import = False - if isinstance(imports, str): - single_import = True - imports = [imports] - if not isinstance(imports, list): - raise TypeError( - f'custom_imports must be a list but got type {type(imports)}') - imported = [] - for imp in imports: - if not isinstance(imp, str): - raise TypeError( - f'{imp} is of type {type(imp)} and cannot be imported.') - try: - imported_tmp = import_module(imp) - except ImportError: - if allow_failed_imports: - warnings.warn(f'{imp} failed to import and is ignored.', - UserWarning) - imported_tmp = None - else: - raise ImportError - imported.append(imported_tmp) - if single_import: - imported = imported[0] - return imported - - -def iter_cast(inputs, dst_type, return_type=None): - """Cast elements of an iterable object into some type. - - Args: - inputs (Iterable): The input object. - dst_type (type): Destination type. - return_type (type, optional): If specified, the output object will be - converted to this type, otherwise an iterator. - - Returns: - iterator or specified type: The converted object. - """ - if not isinstance(inputs, abc.Iterable): - raise TypeError('inputs must be an iterable object') - if not isinstance(dst_type, type): - raise TypeError('"dst_type" must be a valid type') - - out_iterable = map(dst_type, inputs) - - if return_type is None: - return out_iterable - else: - return return_type(out_iterable) - - -def list_cast(inputs, dst_type): - """Cast elements of an iterable object into a list of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=list) - - -def tuple_cast(inputs, dst_type): - """Cast elements of an iterable object into a tuple of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=tuple) - - -def is_seq_of(seq, expected_type, seq_type=None): - """Check whether it is a sequence of some type. - - Args: - seq (Sequence): The sequence to be checked. - expected_type (type): Expected type of sequence items. - seq_type (type, optional): Expected sequence type. - - Returns: - bool: Whether the sequence is valid. - """ - if seq_type is None: - exp_seq_type = abc.Sequence - else: - assert isinstance(seq_type, type) - exp_seq_type = seq_type - if not isinstance(seq, exp_seq_type): - return False - for item in seq: - if not isinstance(item, expected_type): - return False - return True - - -def is_list_of(seq, expected_type): - """Check whether it is a list of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=list) - - -def is_tuple_of(seq, expected_type): - """Check whether it is a tuple of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=tuple) - - -def slice_list(in_list, lens): - """Slice a list into several sub lists by a list of given length. - - Args: - in_list (list): The list to be sliced. - lens(int or list): The expected length of each out list. - - Returns: - list: A list of sliced list. - """ - if isinstance(lens, int): - assert len(in_list) % lens == 0 - lens = [lens] * int(len(in_list) / lens) - if not isinstance(lens, list): - raise TypeError('"indices" must be an integer or a list of integers') - elif sum(lens) != len(in_list): - raise ValueError('sum of lens and list length does not ' - f'match: {sum(lens)} != {len(in_list)}') - out_list = [] - idx = 0 - for i in range(len(lens)): - out_list.append(in_list[idx:idx + lens[i]]) - idx += lens[i] - return out_list - - -def concat_list(in_list): - """Concatenate a list of list into a single list. - - Args: - in_list (list): The list of list to be merged. - - Returns: - list: The concatenated flat list. - """ - return list(itertools.chain(*in_list)) - - -def check_prerequisites( - prerequisites, - checker, - msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' - 'found, please install them first.'): # yapf: disable - """A decorator factory to check if prerequisites are satisfied. - - Args: - prerequisites (str of list[str]): Prerequisites to be checked. - checker (callable): The checker method that returns True if a - prerequisite is meet, False otherwise. - msg_tmpl (str): The message template with two variables. - - Returns: - decorator: A specific decorator. - """ - - def wrap(func): - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - requirements = [prerequisites] if isinstance( - prerequisites, str) else prerequisites - missing = [] - for item in requirements: - if not checker(item): - missing.append(item) - if missing: - print(msg_tmpl.format(', '.join(missing), func.__name__)) - raise RuntimeError('Prerequisites not meet.') - else: - return func(*args, **kwargs) - - return wrapped_func - - return wrap - - -def _check_py_package(package): - try: - import_module(package) - except ImportError: - return False - else: - return True - - -def _check_executable(cmd): - if subprocess.call(f'which {cmd}', shell=True) != 0: - return False - else: - return True - - -def requires_package(prerequisites): - """A decorator to check if some python packages are installed. - - Example: - >>> @requires_package('numpy') - >>> func(arg1, args): - >>> return numpy.zeros(1) - array([0.]) - >>> @requires_package(['numpy', 'non_package']) - >>> func(arg1, args): - >>> return numpy.zeros(1) - ImportError - """ - return check_prerequisites(prerequisites, checker=_check_py_package) - - -def requires_executable(prerequisites): - """A decorator to check if some executable files are installed. - - Example: - >>> @requires_executable('ffmpeg') - >>> func(arg1, args): - >>> print(1) - 1 - """ - return check_prerequisites(prerequisites, checker=_check_executable) - - -def deprecated_api_warning(name_dict, cls_name=None): - """A decorator to check if some arguments are deprecate and try to replace - deprecate src_arg_name to dst_arg_name. - - Args: - name_dict(dict): - key (str): Deprecate argument names. - val (str): Expected argument names. - - Returns: - func: New function. - """ - - def api_warning_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get name of the function - func_name = old_func.__name__ - if cls_name is not None: - func_name = f'{cls_name}.{func_name}' - if args: - arg_names = args_info.args[:len(args)] - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in arg_names: - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - arg_names[arg_names.index(src_arg_name)] = dst_arg_name - if kwargs: - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in kwargs: - - assert dst_arg_name not in kwargs, ( - f'The expected behavior is to replace ' - f'the deprecated key `{src_arg_name}` to ' - f'new key `{dst_arg_name}`, but got them ' - f'in the arguments at the same time, which ' - f'is confusing. `{src_arg_name} will be ' - f'deprecated in the future, please ' - f'use `{dst_arg_name}` instead.') - - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - kwargs[dst_arg_name] = kwargs.pop(src_arg_name) - - # apply converted arguments to the decorated method - output = old_func(*args, **kwargs) - return output - - return new_func - - return api_warning_wrapper - - -def is_method_overridden(method, base_class, derived_class): - """Check if a method of base class is overridden in derived class. - - Args: - method (str): the method name to check. - base_class (type): the class of the base class. - derived_class (type | Any): the class or instance of the derived class. - """ - assert isinstance(base_class, type), \ - "base_class doesn't accept instance, Please pass class instead." - - if not isinstance(derived_class, type): - derived_class = derived_class.__class__ - - base_method = getattr(base_class, method) - derived_method = getattr(derived_class, method) - return derived_method != base_method - - -def has_method(obj: object, method: str) -> bool: - """Check whether the object has a method. - - Args: - method (str): The method name to check. - obj (object): The object to check. - - Returns: - bool: True if the object has the method else False. - """ - return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/parrots_jit.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/parrots_jit.py deleted file mode 100644 index 61873f6db..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/parrots_jit.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -from .parrots_wrapper import TORCH_VERSION - -parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') - -if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': - from parrots.jit import pat as jit -else: - - def jit(func=None, - check_input=None, - full_shape=True, - derivate=False, - coderize=False, - optimize=False): - - def wrapper(func): - - def wrapper_inner(*args, **kargs): - return func(*args, **kargs) - - return wrapper_inner - - if func is None: - return wrapper - else: - return func - - -if TORCH_VERSION == 'parrots': - from parrots.utils.tester import skip_no_elena -else: - - def skip_no_elena(func): - - def wrapper(*args, **kargs): - return func(*args, **kargs) - - return wrapper diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/parrots_wrapper.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/parrots_wrapper.py deleted file mode 100644 index 7e657b561..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/parrots_wrapper.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch - -TORCH_VERSION = torch.__version__ - - -def is_cuda_available() -> bool: - return torch.cuda.is_available() - - -IS_CUDA_AVAILABLE = is_cuda_available() - - -def is_rocm_pytorch() -> bool: - is_rocm = False - if TORCH_VERSION != 'parrots': - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - return is_rocm - - -def _get_cuda_home(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import CUDA_HOME - else: - if is_rocm_pytorch(): - from torch.utils.cpp_extension import ROCM_HOME - CUDA_HOME = ROCM_HOME - else: - from torch.utils.cpp_extension import CUDA_HOME - return CUDA_HOME - - -def get_build_config(): - if TORCH_VERSION == 'parrots': - from parrots.config import get_build_info - return get_build_info() - else: - return torch.__config__.show() - - -def _get_conv(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin - else: - from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin - return _ConvNd, _ConvTransposeMixin - - -def _get_dataloader(): - if TORCH_VERSION == 'parrots': - from torch.utils.data import DataLoader, PoolDataLoader - else: - from torch.utils.data import DataLoader - PoolDataLoader = DataLoader - return DataLoader, PoolDataLoader - - -def _get_extension(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import BuildExtension, Extension - CppExtension = partial(Extension, cuda=False) - CUDAExtension = partial(Extension, cuda=True) - else: - from torch.utils.cpp_extension import (BuildExtension, CppExtension, - CUDAExtension) - return BuildExtension, CppExtension, CUDAExtension - - -def _get_pool(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - else: - from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd - - -def _get_norm(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm2d - else: - from torch.nn.modules.batchnorm import _BatchNorm - from torch.nn.modules.instancenorm import _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm - return _BatchNorm, _InstanceNorm, SyncBatchNorm_ - - -_ConvNd, _ConvTransposeMixin = _get_conv() -DataLoader, PoolDataLoader = _get_dataloader() -BuildExtension, CppExtension, CUDAExtension = _get_extension() -_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() -_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() - - -class SyncBatchNorm(SyncBatchNorm_): - - def _check_input_dim(self, input): - if TORCH_VERSION == 'parrots': - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input (got {input.dim()}D input)') - else: - super()._check_input_dim(input) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/path.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/path.py deleted file mode 100644 index 568081837..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/path.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -from pathlib import Path - -from .misc import is_str - - -def is_filepath(x): - return is_str(x) or isinstance(x, Path) - - -def fopen(filepath, *args, **kwargs): - if is_str(filepath): - return open(filepath, *args, **kwargs) - elif isinstance(filepath, Path): - return filepath.open(*args, **kwargs) - raise ValueError('`filepath` should be a string or a Path') - - -def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): - if not osp.isfile(filename): - raise FileNotFoundError(msg_tmpl.format(filename)) - - -def mkdir_or_exist(dir_name, mode=0o777): - if dir_name == '': - return - dir_name = osp.expanduser(dir_name) - os.makedirs(dir_name, mode=mode, exist_ok=True) - - -def symlink(src, dst, overwrite=True, **kwargs): - if os.path.lexists(dst) and overwrite: - os.remove(dst) - os.symlink(src, dst, **kwargs) - - -def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): - """Scan a directory to find the interested files. - - Args: - dir_path (str | :obj:`Path`): Path of the directory. - suffix (str | tuple(str), optional): File suffix that we are - interested in. Default: None. - recursive (bool, optional): If set to True, recursively scan the - directory. Default: False. - case_sensitive (bool, optional) : If set to False, ignore the case of - suffix. Default: True. - - Returns: - A generator for all the interested files with relative paths. - """ - if isinstance(dir_path, (str, Path)): - dir_path = str(dir_path) - else: - raise TypeError('"dir_path" must be a string or Path object') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('"suffix" must be a string or tuple of strings') - - if suffix is not None and not case_sensitive: - suffix = suffix.lower() if isinstance(suffix, str) else tuple( - item.lower() for item in suffix) - - root = dir_path - - def _scandir(dir_path, suffix, recursive, case_sensitive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - _rel_path = rel_path if case_sensitive else rel_path.lower() - if suffix is None or _rel_path.endswith(suffix): - yield rel_path - elif recursive and os.path.isdir(entry.path): - # scan recursively if entry.path is a directory - yield from _scandir(entry.path, suffix, recursive, - case_sensitive) - - return _scandir(dir_path, suffix, recursive, case_sensitive) - - -def find_vcs_root(path, markers=('.git', )): - """Finds the root directory (including itself) of specified markers. - - Args: - path (str): Path of directory or file. - markers (list[str], optional): List of file or directory names. - - Returns: - The directory contained one of the markers or None if not found. - """ - if osp.isfile(path): - path = osp.dirname(path) - - prev, cur = None, osp.abspath(osp.expanduser(path)) - while cur != prev: - if any(osp.exists(osp.join(cur, marker)) for marker in markers): - return cur - prev, cur = cur, osp.split(cur)[0] - return None diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/progressbar.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/progressbar.py deleted file mode 100644 index 0062f670d..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/progressbar.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from collections.abc import Iterable -from multiprocessing import Pool -from shutil import get_terminal_size - -from .timer import Timer - - -class ProgressBar: - """A progress bar which can print the progress.""" - - def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): - self.task_num = task_num - self.bar_width = bar_width - self.completed = 0 - self.file = file - if start: - self.start() - - @property - def terminal_width(self): - width, _ = get_terminal_size() - return width - - def start(self): - if self.task_num > 0: - self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' - 'elapsed: 0s, ETA:') - else: - self.file.write('completed: 0, elapsed: 0s') - self.file.flush() - self.timer = Timer() - - def update(self, num_tasks=1): - assert num_tasks > 0 - self.completed += num_tasks - elapsed = self.timer.since_start() - if elapsed > 0: - fps = self.completed / elapsed - else: - fps = float('inf') - if self.task_num > 0: - percentage = self.completed / float(self.task_num) - eta = int(elapsed * (1 - percentage) / percentage + 0.5) - msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ - f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ - f'ETA: {eta:5}s' - - bar_width = min(self.bar_width, - int(self.terminal_width - len(msg)) + 2, - int(self.terminal_width * 0.6)) - bar_width = max(2, bar_width) - mark_width = int(bar_width * percentage) - bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) - self.file.write(msg.format(bar_chars)) - else: - self.file.write( - f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' - f' {fps:.1f} tasks/s') - self.file.flush() - - -def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): - """Track the progress of tasks execution with a progress bar. - - Tasks are done with a simple for-loop. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - results = [] - for task in tasks: - results.append(func(task, **kwargs)) - prog_bar.update() - prog_bar.file.write('\n') - return results - - -def init_pool(process_num, initializer=None, initargs=None): - if initializer is None: - return Pool(process_num) - elif initargs is None: - return Pool(process_num, initializer) - else: - if not isinstance(initargs, tuple): - raise TypeError('"initargs" must be a tuple') - return Pool(process_num, initializer, initargs) - - -def track_parallel_progress(func, - tasks, - nproc, - initializer=None, - initargs=None, - bar_width=50, - chunksize=1, - skip_first=False, - keep_order=True, - file=sys.stdout): - """Track the progress of parallel task execution with a progress bar. - - The built-in :mod:`multiprocessing` module is used for process pools and - tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - nproc (int): Process (worker) number. - initializer (None or callable): Refer to :class:`multiprocessing.Pool` - for details. - initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for - details. - chunksize (int): Refer to :class:`multiprocessing.Pool` for details. - bar_width (int): Width of progress bar. - skip_first (bool): Whether to skip the first sample for each worker - when estimating fps, since the initialization step may takes - longer. - keep_order (bool): If True, :func:`Pool.imap` is used, otherwise - :func:`Pool.imap_unordered` is used. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - pool = init_pool(nproc, initializer, initargs) - start = not skip_first - task_num -= nproc * chunksize * int(skip_first) - prog_bar = ProgressBar(task_num, bar_width, start, file=file) - results = [] - if keep_order: - gen = pool.imap(func, tasks, chunksize) - else: - gen = pool.imap_unordered(func, tasks, chunksize) - for result in gen: - results.append(result) - if skip_first: - if len(results) < nproc * chunksize: - continue - elif len(results) == nproc * chunksize: - prog_bar.start() - continue - prog_bar.update() - prog_bar.file.write('\n') - pool.close() - pool.join() - return results - - -def track_iter_progress(tasks, bar_width=50, file=sys.stdout): - """Track the progress of tasks iteration or enumeration with a progress - bar. - - Tasks are yielded with a simple for-loop. - - Args: - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Yields: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - for task in tasks: - yield task - prog_bar.update() - prog_bar.file.write('\n') diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/registry.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/registry.py deleted file mode 100644 index a6d92b68b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/registry.py +++ /dev/null @@ -1,337 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import warnings -from functools import partial - -from .misc import deprecated_api_warning, is_seq_of - - -def build_from_cfg(cfg, registry, default_args=None): - """Build a module from config dict when it is a class configuration, or - call a function from config dict when it is a function configuration. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = build_from_cfg(dict(type='Resnet'), MODELS) - >>> # Returns an instantiated object - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = build_from_cfg(dict(type='resnet50'), MODELS) - >>> # Return a result of the calling function - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type) or inspect.isfunction(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - try: - return obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{obj_cls.__name__}: {e}') - - -class Registry: - """A registry to map strings to classes or functions. - - Registered object could be built from registry. Meanwhile, registered - functions could be called from registry. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = MODELS.build(dict(type='ResNet')) - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = MODELS.build(dict(type='resnet50')) - - Please refer to - https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for - advanced usage. - - Args: - name (str): Registry name. - build_func(func, optional): Build function to construct instance from - Registry, func:`build_from_cfg` is used if neither ``parent`` or - ``build_func`` is specified. If ``parent`` is specified and - ``build_func`` is not given, ``build_func`` will be inherited - from ``parent``. Default: None. - parent (Registry, optional): Parent registry. The class registered in - children registry could be built from parent. Default: None. - scope (str, optional): The scope of registry. It is the key to search - for children registry. If not specified, scope will be the name of - the package where class is defined, e.g. mmdet, mmcls, mmseg. - Default: None. - """ - - def __init__(self, name, build_func=None, parent=None, scope=None): - self._name = name - self._module_dict = dict() - self._children = dict() - self._scope = self.infer_scope() if scope is None else scope - - # self.build_func will be set with the following priority: - # 1. build_func - # 2. parent.build_func - # 3. build_from_cfg - if build_func is None: - if parent is not None: - self.build_func = parent.build_func - else: - self.build_func = build_from_cfg - else: - self.build_func = build_func - if parent is not None: - assert isinstance(parent, Registry) - parent._add_children(self) - self.parent = parent - else: - self.parent = None - - def __len__(self): - return len(self._module_dict) - - def __contains__(self, key): - return self.get(key) is not None - - def __repr__(self): - format_str = self.__class__.__name__ + \ - f'(name={self._name}, ' \ - f'items={self._module_dict})' - return format_str - - @staticmethod - def infer_scope(): - """Infer the scope of registry. - - The name of the package where registry is defined will be returned. - - Example: - >>> # in mmdet/models/backbone/resnet.py - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - The scope of ``ResNet`` will be ``mmdet``. - - Returns: - str: The inferred scope name. - """ - # We access the caller using inspect.currentframe() instead of - # inspect.stack() for performance reasons. See details in PR #1844 - frame = inspect.currentframe() - # get the frame where `infer_scope()` is called - infer_scope_caller = frame.f_back.f_back - filename = inspect.getmodule(infer_scope_caller).__name__ - split_filename = filename.split('.') - return split_filename[0] - - @staticmethod - def split_scope_key(key): - """Split scope and key. - - The first scope will be split from key. - - Examples: - >>> Registry.split_scope_key('mmdet.ResNet') - 'mmdet', 'ResNet' - >>> Registry.split_scope_key('ResNet') - None, 'ResNet' - - Return: - tuple[str | None, str]: The former element is the first scope of - the key, which can be ``None``. The latter is the remaining key. - """ - split_index = key.find('.') - if split_index != -1: - return key[:split_index], key[split_index + 1:] - else: - return None, key - - @property - def name(self): - return self._name - - @property - def scope(self): - return self._scope - - @property - def module_dict(self): - return self._module_dict - - @property - def children(self): - return self._children - - def get(self, key): - """Get the registry record. - - Args: - key (str): The class name in string format. - - Returns: - class: The corresponding class. - """ - scope, real_key = self.split_scope_key(key) - if scope is None or scope == self._scope: - # get from self - if real_key in self._module_dict: - return self._module_dict[real_key] - else: - # get from self._children - if scope in self._children: - return self._children[scope].get(real_key) - else: - # goto root - parent = self.parent - while parent.parent is not None: - parent = parent.parent - return parent.get(key) - - def build(self, *args, **kwargs): - return self.build_func(*args, **kwargs, registry=self) - - def _add_children(self, registry): - """Add children for a registry. - - The ``registry`` will be added as children based on its scope. - The parent registry could build objects from children registry. - - Example: - >>> models = Registry('models') - >>> mmdet_models = Registry('models', parent=models) - >>> @mmdet_models.register_module() - >>> class ResNet: - >>> pass - >>> resnet = models.build(dict(type='mmdet.ResNet')) - """ - - assert isinstance(registry, Registry) - assert registry.scope is not None - assert registry.scope not in self.children, \ - f'scope {registry.scope} exists in {self.name} registry' - self.children[registry.scope] = registry - - @deprecated_api_warning(name_dict=dict(module_class='module')) - def _register_module(self, module, module_name=None, force=False): - if not inspect.isclass(module) and not inspect.isfunction(module): - raise TypeError('module must be a class or a function, ' - f'but got {type(module)}') - - if module_name is None: - module_name = module.__name__ - if isinstance(module_name, str): - module_name = [module_name] - for name in module_name: - if not force and name in self._module_dict: - raise KeyError(f'{name} is already registered ' - f'in {self.name}') - self._module_dict[name] = module - - def deprecated_register_module(self, cls=None, force=False): - warnings.warn( - 'The old API of register_module(module, force=False) ' - 'is deprecated and will be removed, please use the new API ' - 'register_module(name=None, force=False, module=None) instead.', - DeprecationWarning) - if cls is None: - return partial(self.deprecated_register_module, force=force) - self._register_module(cls, force=force) - return cls - - def register_module(self, name=None, force=False, module=None): - """Register a module. - - A record will be added to `self._module_dict`, whose key is the class - name or the specified name, and value is the class itself. - It can be used as a decorator or a normal function. - - Example: - >>> backbones = Registry('backbone') - >>> @backbones.register_module() - >>> class ResNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> @backbones.register_module(name='mnet') - >>> class MobileNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> class ResNet: - >>> pass - >>> backbones.register_module(ResNet) - - Args: - name (str | None): The module name to be registered. If not - specified, the class name will be used. - force (bool, optional): Whether to override an existing class with - the same name. Default: False. - module (type): Module class or function to be registered. - """ - if not isinstance(force, bool): - raise TypeError(f'force must be a boolean, but got {type(force)}') - # NOTE: This is a walkaround to be compatible with the old api, - # while it may introduce unexpected bugs. - if isinstance(name, type): - return self.deprecated_register_module(name, force=force) - - # raise the error ahead of time - if not (name is None or isinstance(name, str) or is_seq_of(name, str)): - raise TypeError( - 'name must be either of None, an instance of str or a sequence' - f' of str, but got {type(name)}') - - # use it as a normal method: x.register_module(module=SomeClass) - if module is not None: - self._register_module(module=module, module_name=name, force=force) - return module - - # use it as a decorator: @x.register_module() - def _register(module): - self._register_module(module=module, module_name=name, force=force) - return module - - return _register diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/seed.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/seed.py deleted file mode 100644 index 003f92367..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/seed.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import random - -import numpy as np -import torch - - -def worker_init_fn(worker_id: int, num_workers: int, rank: int, seed: int): - """Function to initialize each worker. - - The seed of each worker equals to - ``num_worker * rank + worker_id + user_seed``. - - Args: - worker_id (int): Id for each worker. - num_workers (int): Number of workers. - rank (int): Rank in distributed training. - seed (int): Random seed. - """ - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/testing.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/testing.py deleted file mode 100644 index 7b64e8fae..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/testing.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (c) Open-MMLab. -import sys -from collections.abc import Iterable -from runpy import run_path -from shlex import split -from typing import Any, Dict, List -from unittest.mock import patch - - -def check_python_script(cmd): - """Run the python cmd script with `__main__`. The difference between - `os.system` is that, this function exectues code in the current process, so - that it can be tracked by coverage tools. Currently it supports two forms: - - - ./tests/data/scripts/hello.py zz - - python tests/data/scripts/hello.py zz - """ - args = split(cmd) - if args[0] == 'python': - args = args[1:] - with patch.object(sys, 'argv', args): - run_path(args[0], run_name='__main__') - - -def _any(judge_result): - """Since built-in ``any`` works only when the element of iterable is not - iterable, implement the function.""" - if not isinstance(judge_result, Iterable): - return judge_result - - try: - for element in judge_result: - if _any(element): - return True - except TypeError: - # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) - if judge_result: - return True - return False - - -def assert_dict_contains_subset(dict_obj: Dict[Any, Any], - expected_subset: Dict[Any, Any]) -> bool: - """Check if the dict_obj contains the expected_subset. - - Args: - dict_obj (Dict[Any, Any]): Dict object to be checked. - expected_subset (Dict[Any, Any]): Subset expected to be contained in - dict_obj. - - Returns: - bool: Whether the dict_obj contains the expected_subset. - """ - - for key, value in expected_subset.items(): - if key not in dict_obj.keys() or _any(dict_obj[key] != value): - return False - return True - - -def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: - """Check if attribute of class object is correct. - - Args: - obj (object): Class object to be checked. - expected_attrs (Dict[str, Any]): Dict of the expected attrs. - - Returns: - bool: Whether the attribute of class object is correct. - """ - for attr, value in expected_attrs.items(): - if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): - return False - return True - - -def assert_dict_has_keys(obj: Dict[str, Any], - expected_keys: List[str]) -> bool: - """Check if the obj has all the expected_keys. - - Args: - obj (Dict[str, Any]): Object to be checked. - expected_keys (List[str]): Keys expected to contained in the keys of - the obj. - - Returns: - bool: Whether the obj has the expected keys. - """ - return set(expected_keys).issubset(set(obj.keys())) - - -def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: - """Check if target_keys is equal to result_keys. - - Args: - result_keys (List[str]): Result keys to be checked. - target_keys (List[str]): Target keys to be checked. - - Returns: - bool: Whether target_keys is equal to result_keys. - """ - return set(result_keys) == set(target_keys) - - -def assert_is_norm_layer(module) -> bool: - """Check if the module is a norm layer. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the module is a norm layer. - """ - from torch.nn import GroupNorm, LayerNorm - - from .parrots_wrapper import _BatchNorm, _InstanceNorm - norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) - return isinstance(module, norm_layer_candidates) - - -def assert_params_all_zeros(module) -> bool: - """Check if the parameters of the module is all zeros. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the parameters of the module is all zeros. - """ - weight_data = module.weight.data - is_weight_zero = weight_data.allclose( - weight_data.new_zeros(weight_data.size())) - - if hasattr(module, 'bias') and module.bias is not None: - bias_data = module.bias.data - is_bias_zero = bias_data.allclose( - bias_data.new_zeros(bias_data.size())) - else: - is_bias_zero = True - - return is_weight_zero and is_bias_zero diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/timer.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/timer.py deleted file mode 100644 index 02e96e537..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/timer.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from time import time - - -class TimerError(Exception): - - def __init__(self, message): - self.message = message - super(TimerError, self).__init__(message) - - -class Timer: - """A flexible Timer class. - - Examples: - >>> import time - >>> import mmcv - >>> with mmcv.Timer(): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - 1.000 - >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - it takes 1.0 seconds - >>> timer = mmcv.Timer() - >>> time.sleep(0.5) - >>> print(timer.since_start()) - 0.500 - >>> time.sleep(0.5) - >>> print(timer.since_last_check()) - 0.500 - >>> print(timer.since_start()) - 1.000 - """ - - def __init__(self, start=True, print_tmpl=None): - self._is_running = False - self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' - if start: - self.start() - - @property - def is_running(self): - """bool: indicate whether the timer is running""" - return self._is_running - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - print(self.print_tmpl.format(self.since_last_check())) - self._is_running = False - - def start(self): - """Start the timer.""" - if not self._is_running: - self._t_start = time() - self._is_running = True - self._t_last = time() - - def since_start(self): - """Total time since the timer is started. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - self._t_last = time() - return self._t_last - self._t_start - - def since_last_check(self): - """Time since the last checking. - - Either :func:`since_start` or :func:`since_last_check` is a checking - operation. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - dur = time() - self._t_last - self._t_last = time() - return dur - - -_g_timers = {} # global timers - - -def check_time(timer_id): - """Add check points in a single line. - - This method is suitable for running a task on a list of items. A timer will - be registered when the method is called for the first time. - - Examples: - >>> import time - >>> import mmcv - >>> for i in range(1, 6): - >>> # simulate a code block - >>> time.sleep(i) - >>> mmcv.check_time('task1') - 2.000 - 3.000 - 4.000 - 5.000 - - Args: - str: Timer identifier. - """ - if timer_id not in _g_timers: - _g_timers[timer_id] = Timer() - return 0 - else: - return _g_timers[timer_id].since_last_check() diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/trace.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/trace.py deleted file mode 100644 index 45423bd05..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/trace.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch - -from mmcv.utils import digit_version - - -def is_jit_tracing() -> bool: - if (torch.__version__ != 'parrots' - and digit_version(torch.__version__) >= digit_version('1.6.0')): - on_trace = torch.jit.is_tracing() - # In PyTorch 1.6, torch.jit.is_tracing has a bug. - # Refers to https://github.com/pytorch/pytorch/issues/42448 - if isinstance(on_trace, bool): - return on_trace - else: - return torch._C._is_tracing() - else: - warnings.warn( - 'torch.jit.is_tracing is only supported after v1.6.0. ' - 'Therefore is_tracing returns False automatically. Please ' - 'set on_trace manually if you are using trace.', UserWarning) - return False diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/version_utils.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/version_utils.py deleted file mode 100644 index 963c45a2e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/utils/version_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import subprocess -import warnings - -from packaging.version import parse - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - assert 'parrots' not in version_str - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - -def get_git_hash(fallback='unknown', digits=None): - """Get the git hash of the current repo. - - Args: - fallback (str, optional): The fallback string when git hash is - unavailable. Defaults to 'unknown'. - digits (int, optional): kept digits of the hash. Defaults to None, - meaning all digits are kept. - - Returns: - str: Git commit hash. - """ - - if digits is not None and not isinstance(digits, int): - raise TypeError('digits must be None or an integer') - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - sha = out.strip().decode('ascii') - if digits is not None: - sha = sha[:digits] - except OSError: - sha = fallback - - return sha diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmcv/version.py b/cv/semantic_segmentation/pspnet/pytorch/mmcv/version.py deleted file mode 100644 index a97ffc2dd..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmcv/version.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -__version__ = '1.5.0' - - -def parse_version_info(version_str: str, length: int = 4) -> tuple: - """Parse a version string into a tuple. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int | str]: The version info, e.g., "1.3.0" is parsed into - (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into - (2, 0, 0, 0, 'rc', 1) (when length is set to 4). - """ - from packaging.version import parse - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - release.extend(list(version.pre)) - elif version.is_postrelease: - release.extend(list(version.post)) - else: - release.extend([0, 0]) - return tuple(release) - - -version_info = tuple(int(x) for x in __version__.split('.')[:3]) - -__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/__init__.py deleted file mode 100644 index 360abfc85..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv -from packaging.version import parse - -from .version import __version__, version_info - -MMCV_MIN = '1.3.13' -MMCV_MAX = '1.6.0' - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -mmcv_min_version = digit_version(MMCV_MIN) -mmcv_max_version = digit_version(MMCV_MAX) -mmcv_version = digit_version(mmcv.__version__) - - -assert (mmcv_min_version <= mmcv_version <= mmcv_max_version), \ - f'MMCV=={mmcv.__version__} is used but incompatible. ' \ - f'Please install mmcv>={mmcv_min_version}, <={mmcv_max_version}.' - -__all__ = ['__version__', 'version_info', 'digit_version'] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/__init__.py deleted file mode 100644 index c68818053..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .inference import inference_segmentor, init_segmentor, show_result_pyplot -from .test import multi_gpu_test, single_gpu_test -from .train import (get_root_logger, init_random_seed, set_random_seed, - train_segmentor) - -__all__ = [ - 'get_root_logger', 'set_random_seed', 'train_segmentor', 'init_segmentor', - 'inference_segmentor', 'multi_gpu_test', 'single_gpu_test', - 'show_result_pyplot', 'init_random_seed' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/inference.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/inference.py deleted file mode 100644 index 906943804..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/inference.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import matplotlib.pyplot as plt -import mmcv -import torch -from mmcv.parallel import collate, scatter -from mmcv.runner import load_checkpoint - -from mmseg.datasets.pipelines import Compose -from mmseg.models import build_segmentor - - -def init_segmentor(config, checkpoint=None, device='cuda:0'): - """Initialize a segmentor from config file. - - Args: - config (str or :obj:`mmcv.Config`): Config file path or the config - object. - checkpoint (str, optional): Checkpoint path. If left as None, the model - will not load any weights. - device (str, optional) CPU/CUDA device option. Default 'cuda:0'. - Use 'cpu' for loading model on CPU. - Returns: - nn.Module: The constructed segmentor. - """ - if isinstance(config, str): - config = mmcv.Config.fromfile(config) - elif not isinstance(config, mmcv.Config): - raise TypeError('config must be a filename or Config object, ' - 'but got {}'.format(type(config))) - config.model.pretrained = None - config.model.train_cfg = None - model = build_segmentor(config.model, test_cfg=config.get('test_cfg')) - if checkpoint is not None: - checkpoint = load_checkpoint(model, checkpoint, map_location='cpu') - model.CLASSES = checkpoint['meta']['CLASSES'] - model.PALETTE = checkpoint['meta']['PALETTE'] - model.cfg = config # save the config in the model for convenience - model.to(device) - model.eval() - return model - - -class LoadImage: - """A simple pipeline to load image.""" - - def __call__(self, results): - """Call function to load images into results. - - Args: - results (dict): A result dict contains the file name - of the image to be read. - - Returns: - dict: ``results`` will be returned containing loaded image. - """ - - if isinstance(results['img'], str): - results['filename'] = results['img'] - results['ori_filename'] = results['img'] - else: - results['filename'] = None - results['ori_filename'] = None - img = mmcv.imread(results['img']) - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - return results - - -def inference_segmentor(model, img): - """Inference image(s) with the segmentor. - - Args: - model (nn.Module): The loaded segmentor. - imgs (str/ndarray or list[str/ndarray]): Either image files or loaded - images. - - Returns: - (list[Tensor]): The segmentation result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # build the data pipeline - test_pipeline = [LoadImage()] + cfg.data.test.pipeline[1:] - test_pipeline = Compose(test_pipeline) - # prepare data - data = dict(img=img) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if next(model.parameters()).is_cuda: - # scatter to specified GPU - data = scatter(data, [device])[0] - else: - data['img_metas'] = [i.data[0] for i in data['img_metas']] - - # forward the model - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - return result - - -def show_result_pyplot(model, - img, - result, - palette=None, - fig_size=(15, 10), - opacity=0.5, - title='', - block=True): - """Visualize the segmentation results on the image. - - Args: - model (nn.Module): The loaded segmentor. - img (str or np.ndarray): Image filename or loaded image. - result (list): The segmentation result. - palette (list[list[int]]] | None): The palette of segmentation - map. If None is given, random palette will be generated. - Default: None - fig_size (tuple): Figure size of the pyplot figure. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - title (str): The title of pyplot figure. - Default is ''. - block (bool): Whether to block the pyplot figure. - Default is True. - """ - if hasattr(model, 'module'): - model = model.module - img = model.show_result( - img, result, palette=palette, show=False, opacity=opacity) - plt.figure(figsize=fig_size) - plt.imshow(mmcv.bgr2rgb(img)) - plt.title(title) - plt.tight_layout() - plt.show(block=block) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/test.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/test.py deleted file mode 100644 index cc4fcc979..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/test.py +++ /dev/null @@ -1,233 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import tempfile -import warnings - -import mmcv -import numpy as np -import torch -from mmcv.engine import collect_results_cpu, collect_results_gpu -from mmcv.image import tensor2imgs -from mmcv.runner import get_dist_info - - -def np2tmp(array, temp_file_name=None, tmpdir=None): - """Save ndarray to local numpy file. - - Args: - array (ndarray): Ndarray to save. - temp_file_name (str): Numpy file name. If 'temp_file_name=None', this - function will generate a file name with tempfile.NamedTemporaryFile - to save ndarray. Default: None. - tmpdir (str): Temporary directory to save Ndarray files. Default: None. - Returns: - str: The numpy file name. - """ - - if temp_file_name is None: - temp_file_name = tempfile.NamedTemporaryFile( - suffix='.npy', delete=False, dir=tmpdir).name - np.save(temp_file_name, array) - return temp_file_name - - -def single_gpu_test(model, - data_loader, - show=False, - out_dir=None, - efficient_test=False, - opacity=0.5, - pre_eval=False, - format_only=False, - format_args={}): - """Test with single GPU by progressive mode. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - show (bool): Whether show results during inference. Default: False. - out_dir (str, optional): If specified, the results will be dumped into - the directory to save output results. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - loader_indices = data_loader.batch_sampler - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - - if show or out_dir: - img_tensor = data['img'][0] - img_metas = data['img_metas'][0].data[0] - imgs = tensor2imgs(img_tensor, **img_metas[0]['img_norm_cfg']) - assert len(imgs) == len(img_metas) - - for img, img_meta in zip(imgs, img_metas): - h, w, _ = img_meta['img_shape'] - img_show = img[:h, :w, :] - - ori_h, ori_w = img_meta['ori_shape'][:-1] - img_show = mmcv.imresize(img_show, (ori_w, ori_h)) - - if out_dir: - out_file = osp.join(out_dir, img_meta['ori_filename']) - else: - out_file = None - - model.module.show_result( - img_show, - result, - palette=dataset.PALETTE, - show=show, - out_file=out_file, - opacity=opacity) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - results.extend(result) - else: - results.extend(result) - - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - - return results - - -def multi_gpu_test(model, - data_loader, - tmpdir=None, - gpu_collect=False, - efficient_test=False, - pre_eval=False, - format_only=False, - format_args={}): - """Test model with multiple gpus by progressive mode. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' - it encodes results to gpu tensors and use gpu communication for results - collection. On cpu mode it saves the results on different gpus to 'tmpdir' - and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. The same path is used for efficient - test. Default: None. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - - # batch_sampler based on DistributedSampler, the indices only point to data - # samples of related machine. - loader_indices = data_loader.batch_sampler - - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - - results.extend(result) - - if rank == 0: - batch_size = len(result) * world_size - for _ in range(batch_size): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/train.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/train.py deleted file mode 100644 index 9be063785..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/apis/train.py +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import random -import warnings - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (HOOKS, DistSamplerSeedHook, EpochBasedRunner, - build_runner, get_dist_info) -from mmcv.utils import build_from_cfg - -from mmseg import digit_version -from mmseg.core import DistEvalHook, EvalHook, build_optimizer -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.utils import find_latest_checkpoint, get_root_logger - - -def init_random_seed(seed=None, device='cuda'): - """Initialize random seed. - - If the seed is not set, the seed will be automatically randomized, - and then broadcast to all processes to prevent some potential bugs. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is not None: - return seed - - # Make sure all ranks share the same random seed to prevent - # some potential bugs. Please refer to - # https://github.com/open-mmlab/mmdetection/issues/6339 - rank, world_size = get_dist_info() - seed = np.random.randint(2**31) - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() - - -def set_random_seed(seed, deterministic=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - """ - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - - -def train_segmentor(model, - dataset, - cfg, - distributed=False, - validate=False, - timestamp=None, - meta=None): - """Launch segmentor training.""" - logger = get_root_logger(cfg.log_level) - - # prepare data loaders - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - # The default loader config - loader_cfg = dict( - # cfg.gpus will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - seed=cfg.seed, - drop_last=True) - # The overall dataloader settings - loader_cfg.update({ - k: v - for k, v in cfg.data.items() if k not in [ - 'train', 'val', 'test', 'train_dataloader', 'val_dataloader', - 'test_dataloader' - ] - }) - - # The specific dataloader settings - train_loader_cfg = {**loader_cfg, **cfg.data.get('train_dataloader', {})} - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus - if distributed: - find_unused_parameters = cfg.get('find_unused_parameters', False) - # Sets the `find_unused_parameters` parameter in - # torch.nn.parallel.DistributedDataParallel - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - else: - if not torch.cuda.is_available(): - assert digit_version(mmcv.__version__) >= digit_version('1.4.4'), \ - 'Please use MMCV >= 1.4.4 for CPU training!' - model = MMDataParallel(model, device_ids=cfg.gpu_ids) - # build runner - optimizer = build_optimizer(model, cfg.optimizer) - - if cfg.get('runner') is None: - cfg.runner = {'type': 'IterBasedRunner', 'max_iters': cfg.total_iters} - warnings.warn( - 'config is now expected to have a `runner` section, ' - 'please set `runner` in your config.', UserWarning) - - runner = build_runner( - cfg.runner, - default_args=dict( - model=model, - batch_processor=None, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta)) - - runner.cfg = cfg - - # register hooks - runner.register_training_hooks(cfg.lr_config, cfg.optimizer_config, - cfg.checkpoint_config, cfg.log_config, - cfg.get('momentum_config', None)) - if distributed: - # when distributed training by epoch, using`DistSamplerSeedHook` to set - # the different seed to distributed sampler for each epoch, it will - # shuffle dataset at each epoch and avoid overfitting. - if isinstance(runner, EpochBasedRunner): - runner.register_hook(DistSamplerSeedHook()) - - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register eval hooks - if validate: - val_dataset = build_dataset(cfg.data.val, dict(test_mode=True)) - # The specific dataloader settings - val_loader_cfg = { - **loader_cfg, - 'samples_per_gpu': 1, - 'shuffle': False, # Not shuffle by default - **cfg.data.get('val_dataloader', {}), - } - val_dataloader = build_dataloader(val_dataset, **val_loader_cfg) - eval_cfg = cfg.get('evaluation', {}) - eval_cfg['by_epoch'] = cfg.runner['type'] != 'IterBasedRunner' - eval_hook = DistEvalHook if distributed else EvalHook - # In this PR (https://github.com/open-mmlab/mmcv/pull/1193), the - # priority of IterTimerHook has been modified from 'NORMAL' to 'LOW'. - runner.register_hook( - eval_hook(val_dataloader, **eval_cfg), priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from is None and cfg.get('auto_resume'): - resume_from = find_latest_checkpoint(cfg.work_dir) - if resume_from is not None: - cfg.resume_from = resume_from - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/__init__.py deleted file mode 100644 index 1a077d2f1..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, build_optimizer, - build_optimizer_constructor) -from .evaluation import * # noqa: F401, F403 -from .optimizers import * # noqa: F401, F403 -from .seg import * # noqa: F401, F403 -from .utils import * # noqa: F401, F403 - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/builder.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/builder.py deleted file mode 100644 index 406dd9b4b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/builder.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from mmcv.runner.optimizer import OPTIMIZER_BUILDERS as MMCV_OPTIMIZER_BUILDERS -from mmcv.utils import Registry, build_from_cfg - -OPTIMIZER_BUILDERS = Registry( - 'optimizer builder', parent=MMCV_OPTIMIZER_BUILDERS) - - -def build_optimizer_constructor(cfg): - constructor_type = cfg.get('type') - if constructor_type in OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - elif constructor_type in MMCV_OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, MMCV_OPTIMIZER_BUILDERS) - else: - raise KeyError(f'{constructor_type} is not registered ' - 'in the optimizer builder registry.') - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/__init__.py deleted file mode 100644 index 3d16d17e5..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .class_names import get_classes, get_palette -from .eval_hooks import DistEvalHook, EvalHook -from .metrics import (eval_metrics, intersect_and_union, mean_dice, - mean_fscore, mean_iou, pre_eval_to_metrics) - -__all__ = [ - 'EvalHook', 'DistEvalHook', 'mean_dice', 'mean_iou', 'mean_fscore', - 'eval_metrics', 'get_classes', 'get_palette', 'pre_eval_to_metrics', - 'intersect_and_union' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/class_names.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/class_names.py deleted file mode 100644 index e3bff6231..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/class_names.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - - -def cityscapes_classes(): - """Cityscapes class names for external use.""" - return [ - 'road', 'sidewalk', 'building', 'wall', 'fence', 'pole', - 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle' - ] - - -def ade_classes(): - """ADE20K class names for external use.""" - return [ - 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', - 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', - 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', - 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', - 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', - 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', - 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', - 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', - 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', - 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', - 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', - 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', - 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', - 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', - 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', - 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', - 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', - 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', - 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', - 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', - 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', - 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', - 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', - 'clock', 'flag' - ] - - -def voc_classes(): - """Pascal VOC class names for external use.""" - return [ - 'background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', - 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', - 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', - 'tvmonitor' - ] - - -def cocostuff_classes(): - """CocoStuff class names for external use.""" - return [ - 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', - 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', - 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', - 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', - 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', - 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', - 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', - 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', - 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', - 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', - 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', - 'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner', - 'blanket', 'branch', 'bridge', 'building-other', 'bush', 'cabinet', - 'cage', 'cardboard', 'carpet', 'ceiling-other', 'ceiling-tile', - 'cloth', 'clothes', 'clouds', 'counter', 'cupboard', 'curtain', - 'desk-stuff', 'dirt', 'door-stuff', 'fence', 'floor-marble', - 'floor-other', 'floor-stone', 'floor-tile', 'floor-wood', 'flower', - 'fog', 'food-other', 'fruit', 'furniture-other', 'grass', 'gravel', - 'ground-other', 'hill', 'house', 'leaves', 'light', 'mat', 'metal', - 'mirror-stuff', 'moss', 'mountain', 'mud', 'napkin', 'net', 'paper', - 'pavement', 'pillow', 'plant-other', 'plastic', 'platform', - 'playingfield', 'railing', 'railroad', 'river', 'road', 'rock', 'roof', - 'rug', 'salad', 'sand', 'sea', 'shelf', 'sky-other', 'skyscraper', - 'snow', 'solid-other', 'stairs', 'stone', 'straw', 'structural-other', - 'table', 'tent', 'textile-other', 'towel', 'tree', 'vegetable', - 'wall-brick', 'wall-concrete', 'wall-other', 'wall-panel', - 'wall-stone', 'wall-tile', 'wall-wood', 'water-other', 'waterdrops', - 'window-blind', 'window-other', 'wood' - ] - - -def loveda_classes(): - """LoveDA class names for external use.""" - return [ - 'background', 'building', 'road', 'water', 'barren', 'forest', - 'agricultural' - ] - - -def potsdam_classes(): - """Potsdam class names for external use.""" - return [ - 'impervious_surface', 'building', 'low_vegetation', 'tree', 'car', - 'clutter' - ] - - -def vaihingen_classes(): - """Vaihingen class names for external use.""" - return [ - 'impervious_surface', 'building', 'low_vegetation', 'tree', 'car', - 'clutter' - ] - - -def isaid_classes(): - """iSAID class names for external use.""" - return [ - 'background', 'ship', 'store_tank', 'baseball_diamond', 'tennis_court', - 'basketball_court', 'Ground_Track_Field', 'Bridge', 'Large_Vehicle', - 'Small_Vehicle', 'Helicopter', 'Swimming_pool', 'Roundabout', - 'Soccer_ball_field', 'plane', 'Harbor' - ] - - -def stare_classes(): - """stare class names for external use.""" - return ['background', 'vessel'] - - -def cityscapes_palette(): - """Cityscapes palette for external use.""" - return [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], - [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], - [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], - [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], [0, 80, 100], - [0, 0, 230], [119, 11, 32]] - - -def ade_palette(): - """ADE20K palette for external use.""" - return [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], - [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], - [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], - [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], - [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], - [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], - [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], - [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], - [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], - [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], - [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], - [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], - [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], - [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], - [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], - [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], - [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], - [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], - [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], - [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], - [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], - [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], - [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], - [102, 255, 0], [92, 0, 255]] - - -def voc_palette(): - """Pascal VOC palette for external use.""" - return [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], - [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], - [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], - [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], - [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] - - -def cocostuff_palette(): - """CocoStuff palette for external use.""" - return [[0, 192, 64], [0, 192, 64], [0, 64, 96], [128, 192, 192], - [0, 64, 64], [0, 192, 224], [0, 192, 192], [128, 192, 64], - [0, 192, 96], [128, 192, 64], [128, 32, 192], [0, 0, 224], - [0, 0, 64], [0, 160, 192], [128, 0, 96], [128, 0, 192], - [0, 32, 192], [128, 128, 224], [0, 0, 192], [128, 160, 192], - [128, 128, 0], [128, 0, 32], [128, 32, 0], [128, 0, 128], - [64, 128, 32], [0, 160, 0], [0, 0, 0], [192, 128, 160], [0, 32, 0], - [0, 128, 128], [64, 128, 160], [128, 160, 0], [0, 128, 0], - [192, 128, 32], [128, 96, 128], [0, 0, 128], [64, 0, 32], - [0, 224, 128], [128, 0, 0], [192, 0, 160], [0, 96, 128], - [128, 128, 128], [64, 0, 160], [128, 224, 128], [128, 128, 64], - [192, 0, 32], [128, 96, 0], [128, 0, 192], [0, 128, 32], - [64, 224, 0], [0, 0, 64], [128, 128, 160], [64, 96, 0], - [0, 128, 192], [0, 128, 160], [192, 224, 0], [0, 128, 64], - [128, 128, 32], [192, 32, 128], [0, 64, 192], [0, 0, 32], - [64, 160, 128], [128, 64, 64], [128, 0, 160], [64, 32, 128], - [128, 192, 192], [0, 0, 160], [192, 160, 128], [128, 192, 0], - [128, 0, 96], [192, 32, 0], [128, 64, 128], [64, 128, 96], - [64, 160, 0], [0, 64, 0], [192, 128, 224], [64, 32, 0], - [0, 192, 128], [64, 128, 224], [192, 160, 0], [0, 192, 0], - [192, 128, 96], [192, 96, 128], [0, 64, 128], [64, 0, 96], - [64, 224, 128], [128, 64, 0], [192, 0, 224], [64, 96, 128], - [128, 192, 128], [64, 0, 224], [192, 224, 128], [128, 192, 64], - [192, 0, 96], [192, 96, 0], [128, 64, 192], [0, 128, 96], - [0, 224, 0], [64, 64, 64], [128, 128, 224], [0, 96, 0], - [64, 192, 192], [0, 128, 224], [128, 224, 0], [64, 192, 64], - [128, 128, 96], [128, 32, 128], [64, 0, 192], [0, 64, 96], - [0, 160, 128], [192, 0, 64], [128, 64, 224], [0, 32, 128], - [192, 128, 192], [0, 64, 224], [128, 160, 128], [192, 128, 0], - [128, 64, 32], [128, 32, 64], [192, 0, 128], [64, 192, 32], - [0, 160, 64], [64, 0, 0], [192, 192, 160], [0, 32, 64], - [64, 128, 128], [64, 192, 160], [128, 160, 64], [64, 128, 0], - [192, 192, 32], [128, 96, 192], [64, 0, 128], [64, 64, 32], - [0, 224, 192], [192, 0, 0], [192, 64, 160], [0, 96, 192], - [192, 128, 128], [64, 64, 160], [128, 224, 192], [192, 128, 64], - [192, 64, 32], [128, 96, 64], [192, 0, 192], [0, 192, 32], - [64, 224, 64], [64, 0, 64], [128, 192, 160], [64, 96, 64], - [64, 128, 192], [0, 192, 160], [192, 224, 64], [64, 128, 64], - [128, 192, 32], [192, 32, 192], [64, 64, 192], [0, 64, 32], - [64, 160, 192], [192, 64, 64], [128, 64, 160], [64, 32, 192], - [192, 192, 192], [0, 64, 160], [192, 160, 192], [192, 192, 0], - [128, 64, 96], [192, 32, 64], [192, 64, 128], [64, 192, 96], - [64, 160, 64], [64, 64, 0]] - - -def loveda_palette(): - """LoveDA palette for external use.""" - return [[255, 255, 255], [255, 0, 0], [255, 255, 0], [0, 0, 255], - [159, 129, 183], [0, 255, 0], [255, 195, 128]] - - -def potsdam_palette(): - """Potsdam palette for external use.""" - return [[255, 255, 255], [0, 0, 255], [0, 255, 255], [0, 255, 0], - [255, 255, 0], [255, 0, 0]] - - -def vaihingen_palette(): - """Vaihingen palette for external use.""" - return [[255, 255, 255], [0, 0, 255], [0, 255, 255], [0, 255, 0], - [255, 255, 0], [255, 0, 0]] - - -def isaid_palette(): - """iSAID palette for external use.""" - return [[0, 0, 0], [0, 0, 63], [0, 63, 63], [0, 63, 0], [0, 63, 127], - [0, 63, 191], [0, 63, 255], [0, 127, 63], [0, 127, - 127], [0, 0, 127], - [0, 0, 191], [0, 0, 255], [0, 191, 127], [0, 127, 191], - [0, 127, 255], [0, 100, 155]] - - -def stare_palette(): - """STARE palette for external use.""" - return [[120, 120, 120], [6, 230, 230]] - - -dataset_aliases = { - 'cityscapes': ['cityscapes'], - 'ade': ['ade', 'ade20k'], - 'voc': ['voc', 'pascal_voc', 'voc12', 'voc12aug'], - 'loveda': ['loveda'], - 'potsdam': ['potsdam'], - 'vaihingen': ['vaihingen'], - 'cocostuff': [ - 'cocostuff', 'cocostuff10k', 'cocostuff164k', 'coco-stuff', - 'coco-stuff10k', 'coco-stuff164k', 'coco_stuff', 'coco_stuff10k', - 'coco_stuff164k' - ], - 'isaid': ['isaid', 'iSAID'], - 'stare': ['stare', 'STARE'] -} - - -def get_classes(dataset): - """Get class names of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_classes()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels - - -def get_palette(dataset): - """Get class palette (RGB) of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_palette()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/eval_hooks.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/eval_hooks.py deleted file mode 100644 index 952db3b0b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/eval_hooks.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -import torch.distributed as dist -from mmcv.runner import DistEvalHook as _DistEvalHook -from mmcv.runner import EvalHook as _EvalHook -from torch.nn.modules.batchnorm import _BatchNorm - - -class EvalHook(_EvalHook): - """Single GPU EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``single_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - if not self._should_evaluate(runner): - return - - from mmseg.apis import single_gpu_test - results = single_gpu_test( - runner.model, self.dataloader, show=False, pre_eval=self.pre_eval) - runner.log_buffer.clear() - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - if self.save_best: - self._save_ckpt(runner, key_score) - - -class DistEvalHook(_DistEvalHook): - """Distributed EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``multi_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - if not self._should_evaluate(runner): - return - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - from mmseg.apis import multi_gpu_test - results = multi_gpu_test( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect, - pre_eval=self.pre_eval) - - runner.log_buffer.clear() - - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - - if self.save_best: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/metrics.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/metrics.py deleted file mode 100644 index a1c0908e1..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/evaluation/metrics.py +++ /dev/null @@ -1,395 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import mmcv -import numpy as np -import torch - - -def f_score(precision, recall, beta=1): - """calculate the f-score value. - - Args: - precision (float | torch.Tensor): The precision value. - recall (float | torch.Tensor): The recall value. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - Returns: - [torch.tensor]: The f-score value. - """ - score = (1 + beta**2) * (precision * recall) / ( - (beta**2 * precision) + recall) - return score - - -def intersect_and_union(pred_label, - label, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate intersection and Union. - - Args: - pred_label (ndarray | str): Prediction segmentation map - or predict result filename. - label (ndarray | str): Ground truth segmentation map - or label filename. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. The parameter will - work only when label is str. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. The parameter will - work only when label is str. Default: False. - - Returns: - torch.Tensor: The intersection of prediction and ground truth - histogram on all classes. - torch.Tensor: The union of prediction and ground truth histogram on - all classes. - torch.Tensor: The prediction histogram on all classes. - torch.Tensor: The ground truth histogram on all classes. - """ - - if isinstance(pred_label, str): - pred_label = torch.from_numpy(np.load(pred_label)) - else: - pred_label = torch.from_numpy((pred_label)) - - if isinstance(label, str): - label = torch.from_numpy( - mmcv.imread(label, flag='unchanged', backend='pillow')) - else: - label = torch.from_numpy(label) - - if label_map is not None: - for old_id, new_id in label_map.items(): - label[label == old_id] = new_id - if reduce_zero_label: - label[label == 0] = 255 - label = label - 1 - label[label == 254] = 255 - - mask = (label != ignore_index) - pred_label = pred_label[mask] - label = label[mask] - - intersect = pred_label[pred_label == label] - area_intersect = torch.histc( - intersect.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_pred_label = torch.histc( - pred_label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_label = torch.histc( - label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_union = area_pred_label + area_label - area_intersect - return area_intersect, area_union, area_pred_label, area_label - - -def total_intersect_and_union(results, - gt_seg_maps, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate Total Intersection and Union. - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - ndarray: The intersection of prediction and ground truth histogram - on all classes. - ndarray: The union of prediction and ground truth histogram on all - classes. - ndarray: The prediction histogram on all classes. - ndarray: The ground truth histogram on all classes. - """ - total_area_intersect = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_union = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_pred_label = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_label = torch.zeros((num_classes, ), dtype=torch.float64) - for result, gt_seg_map in zip(results, gt_seg_maps): - area_intersect, area_union, area_pred_label, area_label = \ - intersect_and_union( - result, gt_seg_map, num_classes, ignore_index, - label_map, reduce_zero_label) - total_area_intersect += area_intersect - total_area_union += area_union - total_area_pred_label += area_pred_label - total_area_label += area_label - return total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label - - -def mean_iou(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category IoU, shape (num_classes, ). - """ - iou_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mIoU'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return iou_result - - -def mean_dice(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Dice (mDice) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category dice, shape (num_classes, ). - """ - - dice_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mDice'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return dice_result - - -def mean_fscore(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category recall, shape (num_classes, ). - ndarray: Per category precision, shape (num_classes, ). - ndarray: Per category f-score, shape (num_classes, ). - """ - fscore_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mFscore'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label, - beta=beta) - return fscore_result - - -def eval_metrics(results, - gt_seg_maps, - num_classes, - ignore_index, - metrics=['mIoU'], - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate evaluation metrics - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label = total_intersect_and_union( - results, gt_seg_maps, num_classes, ignore_index, label_map, - reduce_zero_label) - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def pre_eval_to_metrics(pre_eval_results, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Convert pre-eval results to metrics. - - Args: - pre_eval_results (list[tuple[torch.Tensor]]): per image eval results - for computing evaluation metric - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - # convert list of tuples to tuple of lists, e.g. - # [(A_1, B_1, C_1, D_1), ..., (A_n, B_n, C_n, D_n)] to - # ([A_1, ..., A_n], ..., [D_1, ..., D_n]) - pre_eval_results = tuple(zip(*pre_eval_results)) - assert len(pre_eval_results) == 4 - - total_area_intersect = sum(pre_eval_results[0]) - total_area_union = sum(pre_eval_results[1]) - total_area_pred_label = sum(pre_eval_results[2]) - total_area_label = sum(pre_eval_results[3]) - - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def total_area_to_metrics(total_area_intersect, - total_area_union, - total_area_pred_label, - total_area_label, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Calculate evaluation metrics - Args: - total_area_intersect (ndarray): The intersection of prediction and - ground truth histogram on all classes. - total_area_union (ndarray): The union of prediction and ground truth - histogram on all classes. - total_area_pred_label (ndarray): The prediction histogram on all - classes. - total_area_label (ndarray): The ground truth histogram on all classes. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - if isinstance(metrics, str): - metrics = [metrics] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metrics).issubset(set(allowed_metrics)): - raise KeyError('metrics {} is not supported'.format(metrics)) - - all_acc = total_area_intersect.sum() / total_area_label.sum() - ret_metrics = OrderedDict({'aAcc': all_acc}) - for metric in metrics: - if metric == 'mIoU': - iou = total_area_intersect / total_area_union - acc = total_area_intersect / total_area_label - ret_metrics['IoU'] = iou - ret_metrics['Acc'] = acc - elif metric == 'mDice': - dice = 2 * total_area_intersect / ( - total_area_pred_label + total_area_label) - acc = total_area_intersect / total_area_label - ret_metrics['Dice'] = dice - ret_metrics['Acc'] = acc - elif metric == 'mFscore': - precision = total_area_intersect / total_area_pred_label - recall = total_area_intersect / total_area_label - f_value = torch.tensor( - [f_score(x[0], x[1], beta) for x in zip(precision, recall)]) - ret_metrics['Fscore'] = f_value - ret_metrics['Precision'] = precision - ret_metrics['Recall'] = recall - - ret_metrics = { - metric: value.numpy() - for metric, value in ret_metrics.items() - } - if nan_to_num is not None: - ret_metrics = OrderedDict({ - metric: np.nan_to_num(metric_value, nan=nan_to_num) - for metric, metric_value in ret_metrics.items() - }) - return ret_metrics diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/optimizers/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/optimizers/__init__.py deleted file mode 100644 index 4fbf4ecfc..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/optimizers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .layer_decay_optimizer_constructor import ( - LayerDecayOptimizerConstructor, LearningRateDecayOptimizerConstructor) - -__all__ = [ - 'LearningRateDecayOptimizerConstructor', 'LayerDecayOptimizerConstructor' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py deleted file mode 100644 index 2b6b8ff9c..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import warnings - -from mmcv.runner import DefaultOptimizerConstructor, get_dist_info - -from mmseg.utils import get_root_logger -from ..builder import OPTIMIZER_BUILDERS - - -def get_layer_id_for_convnext(var_name, max_layer_id): - """Get the layer id to set the different learning rates in ``layer_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_layer_id (int): Maximum number of backbone layers. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - stage_id = int(var_name.split('.')[2]) - if stage_id == 0: - layer_id = 0 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - block_id = int(var_name.split('.')[3]) - if stage_id == 0: - layer_id = 1 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 + block_id // 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - else: - return max_layer_id + 1 - - -def get_stage_id_for_convnext(var_name, max_stage_id): - """Get the stage id to set the different learning rates in ``stage_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_stage_id (int): Maximum number of backbone layers. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - return 0 - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - return stage_id + 1 - else: - return max_stage_id - 1 - - -def get_layer_id_for_vit(var_name, max_layer_id): - """Get the layer id to set the different learning rates. - - Args: - var_name (str): The key of the model. - num_max_layer (int): Maximum number of backbone layers. - - Returns: - int: Returns the layer id of the key. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.patch_embed'): - return 0 - elif var_name.startswith('backbone.layers'): - layer_id = int(var_name.split('.')[2]) - return layer_id + 1 - else: - return max_layer_id - 1 - - -@OPTIMIZER_BUILDERS.register_module() -class LearningRateDecayOptimizerConstructor(DefaultOptimizerConstructor): - """Different learning rates are set for different layers of backbone. - - Note: Currently, this optimizer constructor is built for ConvNeXt, - BEiT and MAE. - """ - - def add_params(self, params, module, **kwargs): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - """ - logger = get_root_logger() - - parameter_groups = {} - logger.info(f'self.paramwise_cfg is {self.paramwise_cfg}') - num_layers = self.paramwise_cfg.get('num_layers') + 2 - decay_rate = self.paramwise_cfg.get('decay_rate') - decay_type = self.paramwise_cfg.get('decay_type', 'layer_wise') - logger.info('Build LearningRateDecayOptimizerConstructor ' - f'{decay_type} {decay_rate} - {num_layers}') - weight_decay = self.base_wd - for name, param in module.named_parameters(): - if not param.requires_grad: - continue # frozen weights - if len(param.shape) == 1 or name.endswith('.bias') or name in ( - 'pos_embed', 'cls_token'): - group_name = 'no_decay' - this_weight_decay = 0. - else: - group_name = 'decay' - this_weight_decay = weight_decay - if 'layer_wise' in decay_type: - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_convnext( - name, self.paramwise_cfg.get('num_layers')) - logger.info(f'set param {name} as id {layer_id}') - elif 'BEiT' in module.backbone.__class__.__name__ or \ - 'MAE' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_vit(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - elif decay_type == 'stage_wise': - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_stage_id_for_convnext(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - group_name = f'layer_{layer_id}_{group_name}' - - if group_name not in parameter_groups: - scale = decay_rate**(num_layers - layer_id - 1) - - parameter_groups[group_name] = { - 'weight_decay': this_weight_decay, - 'params': [], - 'param_names': [], - 'lr_scale': scale, - 'group_name': group_name, - 'lr': scale * self.base_lr, - } - - parameter_groups[group_name]['params'].append(param) - parameter_groups[group_name]['param_names'].append(name) - rank, _ = get_dist_info() - if rank == 0: - to_display = {} - for key in parameter_groups: - to_display[key] = { - 'param_names': parameter_groups[key]['param_names'], - 'lr_scale': parameter_groups[key]['lr_scale'], - 'lr': parameter_groups[key]['lr'], - 'weight_decay': parameter_groups[key]['weight_decay'], - } - logger.info(f'Param groups = {json.dumps(to_display, indent=2)}') - params.extend(parameter_groups.values()) - - -@OPTIMIZER_BUILDERS.register_module() -class LayerDecayOptimizerConstructor(LearningRateDecayOptimizerConstructor): - """Different learning rates are set for different layers of backbone. - - Note: Currently, this optimizer constructor is built for BEiT, - and it will be deprecated. - Please use ``LearningRateDecayOptimizerConstructor`` instead. - """ - - def __init__(self, optimizer_cfg, paramwise_cfg): - warnings.warn('DeprecationWarning: Original ' - 'LayerDecayOptimizerConstructor of BEiT ' - 'will be deprecated. Please use ' - 'LearningRateDecayOptimizerConstructor instead, ' - 'and set decay_type = layer_wise_vit in paramwise_cfg.') - paramwise_cfg.update({'decay_type': 'layer_wise_vit'}) - warnings.warn('DeprecationWarning: Layer_decay_rate will ' - 'be deleted, please use decay_rate instead.') - paramwise_cfg['decay_rate'] = paramwise_cfg.pop('layer_decay_rate') - super(LayerDecayOptimizerConstructor, - self).__init__(optimizer_cfg, paramwise_cfg) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/__init__.py deleted file mode 100644 index 5206b96be..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_pixel_sampler -from .sampler import BasePixelSampler, OHEMPixelSampler - -__all__ = ['build_pixel_sampler', 'BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/builder.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/builder.py deleted file mode 100644 index 1cecd347b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/builder.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, build_from_cfg - -PIXEL_SAMPLERS = Registry('pixel sampler') - - -def build_pixel_sampler(cfg, **default_args): - """Build pixel sampler for segmentation map.""" - return build_from_cfg(cfg, PIXEL_SAMPLERS, default_args) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/sampler/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/sampler/__init__.py deleted file mode 100644 index 5a7648564..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/sampler/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_pixel_sampler import BasePixelSampler -from .ohem_pixel_sampler import OHEMPixelSampler - -__all__ = ['BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py deleted file mode 100644 index 03672cd47..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BasePixelSampler(metaclass=ABCMeta): - """Base class of pixel sampler.""" - - def __init__(self, **kwargs): - pass - - @abstractmethod - def sample(self, seg_logit, seg_label): - """Placeholder for sample function.""" diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py deleted file mode 100644 index 833a28768..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import PIXEL_SAMPLERS -from .base_pixel_sampler import BasePixelSampler - - -@PIXEL_SAMPLERS.register_module() -class OHEMPixelSampler(BasePixelSampler): - """Online Hard Example Mining Sampler for segmentation. - - Args: - context (nn.Module): The context of sampler, subclass of - :obj:`BaseDecodeHead`. - thresh (float, optional): The threshold for hard example selection. - Below which, are prediction with low confidence. If not - specified, the hard examples will be pixels of top ``min_kept`` - loss. Default: None. - min_kept (int, optional): The minimum number of predictions to keep. - Default: 100000. - """ - - def __init__(self, context, thresh=None, min_kept=100000): - super(OHEMPixelSampler, self).__init__() - self.context = context - assert min_kept > 1 - self.thresh = thresh - self.min_kept = min_kept - - def sample(self, seg_logit, seg_label): - """Sample pixels that have high loss or with low prediction confidence. - - Args: - seg_logit (torch.Tensor): segmentation logits, shape (N, C, H, W) - seg_label (torch.Tensor): segmentation label, shape (N, 1, H, W) - - Returns: - torch.Tensor: segmentation weight, shape (N, H, W) - """ - with torch.no_grad(): - assert seg_logit.shape[2:] == seg_label.shape[2:] - assert seg_label.shape[1] == 1 - seg_label = seg_label.squeeze(1).long() - batch_kept = self.min_kept * seg_label.size(0) - valid_mask = seg_label != self.context.ignore_index - seg_weight = seg_logit.new_zeros(size=seg_label.size()) - valid_seg_weight = seg_weight[valid_mask] - if self.thresh is not None: - seg_prob = F.softmax(seg_logit, dim=1) - - tmp_seg_label = seg_label.clone().unsqueeze(1) - tmp_seg_label[tmp_seg_label == self.context.ignore_index] = 0 - seg_prob = seg_prob.gather(1, tmp_seg_label).squeeze(1) - sort_prob, sort_indices = seg_prob[valid_mask].sort() - - if sort_prob.numel() > 0: - min_threshold = sort_prob[min(batch_kept, - sort_prob.numel() - 1)] - else: - min_threshold = 0.0 - threshold = max(min_threshold, self.thresh) - valid_seg_weight[seg_prob[valid_mask] < threshold] = 1. - else: - if not isinstance(self.context.loss_decode, nn.ModuleList): - losses_decode = [self.context.loss_decode] - else: - losses_decode = self.context.loss_decode - losses = 0.0 - for loss_module in losses_decode: - losses += loss_module( - seg_logit, - seg_label, - weight=None, - ignore_index=self.context.ignore_index, - reduction_override='none') - - # faster than topk according to https://github.com/pytorch/pytorch/issues/22812 # noqa - _, sort_indices = losses[valid_mask].sort(descending=True) - valid_seg_weight[sort_indices[:batch_kept]] = 1. - - seg_weight[valid_mask] = valid_seg_weight - - return seg_weight diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/utils/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/utils/__init__.py deleted file mode 100644 index 28882893a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/utils/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .dist_util import check_dist_init, sync_random_seed -from .misc import add_prefix - -__all__ = ['add_prefix', 'check_dist_init', 'sync_random_seed'] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/utils/dist_util.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/utils/dist_util.py deleted file mode 100644 index b3288519d..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/utils/dist_util.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def check_dist_init(): - return dist.is_available() and dist.is_initialized() - - -def sync_random_seed(seed=None, device='cuda'): - """Make sure different ranks share the same seed. All workers must call - this function, otherwise it will deadlock. This method is generally used in - `DistributedSampler`, because the seed should be identical across all - processes in the distributed group. - - In distributed sampling, different ranks should sample non-overlapped - data in the dataset. Therefore, this function is used to make sure that - each rank shuffles the data indices in the same order based - on the same seed. Then different ranks could use different indices - to select non-overlapped data from the same data list. - - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - - if seed is None: - seed = np.random.randint(2**31) - assert isinstance(seed, int) - - rank, world_size = get_dist_info() - - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/utils/misc.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/utils/misc.py deleted file mode 100644 index 282bb8d96..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/core/utils/misc.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def add_prefix(inputs, prefix): - """Add prefix for dict. - - Args: - inputs (dict): The input dict with str keys. - prefix (str): The prefix to add. - - Returns: - - dict: The dict with keys updated with ``prefix``. - """ - - outputs = dict() - for name, value in inputs.items(): - outputs[f'{prefix}.{name}'] = value - - return outputs diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/__init__.py deleted file mode 100644 index 3366f0aec..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .ade import ADE20KDataset -from .builder import DATASETS, PIPELINES, build_dataloader, build_dataset -from .cityscapes import CityscapesDataset -from .coco_stuff import COCOStuffDataset -from .custom import CustomDataset -from .dataset_wrappers import (ConcatDataset, MultiImageMixDataset, - RepeatDataset) -from .pascal_context import PascalContextDataset, PascalContextDataset59 -from .voc import PascalVOCDataset diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/ade.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/ade.py deleted file mode 100644 index db94cebd3..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/ade.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np -from PIL import Image - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class ADE20KDataset(CustomDataset): - """ADE20K dataset. - - In segmentation map annotation for ADE20K, 0 stands for background, which - is not included in 150 categories. ``reduce_zero_label`` is fixed to True. - The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is fixed to - '.png'. - """ - CLASSES = ( - 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', - 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', - 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', - 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', - 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', - 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', - 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', - 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', - 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', - 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', - 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', - 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', - 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', - 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', - 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', - 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', - 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', - 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', - 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', - 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', - 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', - 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', - 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', - 'clock', 'flag') - - PALETTE = [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], - [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], - [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], - [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], - [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], - [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], - [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], - [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], - [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], - [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], - [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], - [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], - [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], - [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], - [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], - [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], - [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], - [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], - [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], - [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], - [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], - [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], - [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], - [102, 255, 0], [92, 0, 255]] - - def __init__(self, **kwargs): - super(ADE20KDataset, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - reduce_zero_label=True, - **kwargs) - - def results2img(self, results, imgfile_prefix, to_label_id, indices=None): - """Write the segmentation results to images. - - Args: - results (list[ndarray]): Testing results of the - dataset. - imgfile_prefix (str): The filename prefix of the png files. - If the prefix is "somepath/xxx", - the png files will be named "somepath/xxx.png". - to_label_id (bool): whether convert output to label_id for - submission. - indices (list[int], optional): Indices of input results, if not - set, all the indices of the dataset will be used. - Default: None. - - Returns: - list[str: str]: result txt files which contains corresponding - semantic segmentation images. - """ - if indices is None: - indices = list(range(len(self))) - - mmcv.mkdir_or_exist(imgfile_prefix) - result_files = [] - for result, idx in zip(results, indices): - - filename = self.img_infos[idx]['filename'] - basename = osp.splitext(osp.basename(filename))[0] - - png_filename = osp.join(imgfile_prefix, f'{basename}.png') - - # The index range of official requirement is from 0 to 150. - # But the index range of output is from 0 to 149. - # That is because we set reduce_zero_label=True. - result = result + 1 - - output = Image.fromarray(result.astype(np.uint8)) - output.save(png_filename) - result_files.append(png_filename) - - return result_files - - def format_results(self, - results, - imgfile_prefix, - to_label_id=True, - indices=None): - """Format the results into dir (standard format for ade20k evaluation). - - Args: - results (list): Testing results of the dataset. - imgfile_prefix (str | None): The prefix of images files. It - includes the file path and the prefix of filename, e.g., - "a/b/prefix". - to_label_id (bool): whether convert output to label_id for - submission. Default: False - indices (list[int], optional): Indices of input results, if not - set, all the indices of the dataset will be used. - Default: None. - - Returns: - tuple: (result_files, tmp_dir), result_files is a list containing - the image paths, tmp_dir is the temporal directory created - for saving json/png files when img_prefix is not specified. - """ - - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - result_files = self.results2img(results, imgfile_prefix, to_label_id, - indices) - return result_files diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/builder.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/builder.py deleted file mode 100644 index 4d852d365..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/builder.py +++ /dev/null @@ -1,191 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import platform -import random -from functools import partial - -import numpy as np -import torch -from mmcv.parallel import collate -from mmcv.runner import get_dist_info -from mmcv.utils import Registry, build_from_cfg, digit_version -from torch.utils.data import DataLoader - -from .samplers import DistributedSampler - -if platform.system() != 'Windows': - # https://github.com/pytorch/pytorch/issues/973 - import resource - rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) - base_soft_limit = rlimit[0] - hard_limit = rlimit[1] - soft_limit = min(max(4096, base_soft_limit), hard_limit) - resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) - -DATASETS = Registry('dataset') -PIPELINES = Registry('pipeline') - - -def _concat_dataset(cfg, default_args=None): - """Build :obj:`ConcatDataset by.""" - from .dataset_wrappers import ConcatDataset - img_dir = cfg['img_dir'] - ann_dir = cfg.get('ann_dir', None) - split = cfg.get('split', None) - # pop 'separate_eval' since it is not a valid key for common datasets. - separate_eval = cfg.pop('separate_eval', True) - num_img_dir = len(img_dir) if isinstance(img_dir, (list, tuple)) else 1 - if ann_dir is not None: - num_ann_dir = len(ann_dir) if isinstance(ann_dir, (list, tuple)) else 1 - else: - num_ann_dir = 0 - if split is not None: - num_split = len(split) if isinstance(split, (list, tuple)) else 1 - else: - num_split = 0 - if num_img_dir > 1: - assert num_img_dir == num_ann_dir or num_ann_dir == 0 - assert num_img_dir == num_split or num_split == 0 - else: - assert num_split == num_ann_dir or num_ann_dir <= 1 - num_dset = max(num_split, num_img_dir) - - datasets = [] - for i in range(num_dset): - data_cfg = copy.deepcopy(cfg) - if isinstance(img_dir, (list, tuple)): - data_cfg['img_dir'] = img_dir[i] - if isinstance(ann_dir, (list, tuple)): - data_cfg['ann_dir'] = ann_dir[i] - if isinstance(split, (list, tuple)): - data_cfg['split'] = split[i] - datasets.append(build_dataset(data_cfg, default_args)) - - return ConcatDataset(datasets, separate_eval) - - -def build_dataset(cfg, default_args=None): - """Build datasets.""" - from .dataset_wrappers import (ConcatDataset, MultiImageMixDataset, - RepeatDataset) - if isinstance(cfg, (list, tuple)): - dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) - elif cfg['type'] == 'RepeatDataset': - dataset = RepeatDataset( - build_dataset(cfg['dataset'], default_args), cfg['times']) - elif cfg['type'] == 'MultiImageMixDataset': - cp_cfg = copy.deepcopy(cfg) - cp_cfg['dataset'] = build_dataset(cp_cfg['dataset']) - cp_cfg.pop('type') - dataset = MultiImageMixDataset(**cp_cfg) - elif isinstance(cfg.get('img_dir'), (list, tuple)) or isinstance( - cfg.get('split', None), (list, tuple)): - dataset = _concat_dataset(cfg, default_args) - else: - dataset = build_from_cfg(cfg, DATASETS, default_args) - - return dataset - - -def build_dataloader(dataset, - samples_per_gpu, - workers_per_gpu, - num_gpus=1, - dist=True, - shuffle=True, - seed=None, - drop_last=False, - pin_memory=True, - persistent_workers=True, - **kwargs): - """Build PyTorch DataLoader. - - In distributed training, each GPU/process has a dataloader. - In non-distributed training, there is only one dataloader for all GPUs. - - Args: - dataset (Dataset): A PyTorch dataset. - samples_per_gpu (int): Number of training samples on each GPU, i.e., - batch size of each GPU. - workers_per_gpu (int): How many subprocesses to use for data loading - for each GPU. - num_gpus (int): Number of GPUs. Only used in non-distributed training. - dist (bool): Distributed training/test or not. Default: True. - shuffle (bool): Whether to shuffle the data at every epoch. - Default: True. - seed (int | None): Seed to be used. Default: None. - drop_last (bool): Whether to drop the last incomplete batch in epoch. - Default: False - pin_memory (bool): Whether to use pin_memory in DataLoader. - Default: True - persistent_workers (bool): If True, the data loader will not shutdown - the worker processes after a dataset has been consumed once. - This allows to maintain the workers Dataset instances alive. - The argument also has effect in PyTorch>=1.7.0. - Default: True - kwargs: any keyword argument to be used to initialize DataLoader - - Returns: - DataLoader: A PyTorch dataloader. - """ - rank, world_size = get_dist_info() - if dist: - sampler = DistributedSampler( - dataset, world_size, rank, shuffle=shuffle, seed=seed) - shuffle = False - batch_size = samples_per_gpu - num_workers = workers_per_gpu - else: - sampler = None - batch_size = num_gpus * samples_per_gpu - num_workers = num_gpus * workers_per_gpu - - init_fn = partial( - worker_init_fn, num_workers=num_workers, rank=rank, - seed=seed) if seed is not None else None - - if digit_version(torch.__version__) >= digit_version('1.8.0'): - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - persistent_workers=persistent_workers, - **kwargs) - else: - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - **kwargs) - - return data_loader - - -def worker_init_fn(worker_id, num_workers, rank, seed): - """Worker init func for dataloader. - - The seed of each worker equals to num_worker * rank + worker_id + user_seed - - Args: - worker_id (int): Worker id. - num_workers (int): Number of workers. - rank (int): The rank of current process. - seed (int): The random seed to use. - """ - - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/cityscapes.py deleted file mode 100644 index ed633d00d..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/cityscapes.py +++ /dev/null @@ -1,214 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np -from mmcv.utils import print_log -from PIL import Image - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class CityscapesDataset(CustomDataset): - """Cityscapes dataset. - - The ``img_suffix`` is fixed to '_leftImg8bit.png' and ``seg_map_suffix`` is - fixed to '_gtFine_labelTrainIds.png' for Cityscapes dataset. - """ - - CLASSES = ('road', 'sidewalk', 'building', 'wall', 'fence', 'pole', - 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle') - - PALETTE = [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], - [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], - [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], - [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], - [0, 80, 100], [0, 0, 230], [119, 11, 32]] - - def __init__(self, - img_suffix='_leftImg8bit.png', - seg_map_suffix='_gtFine_labelTrainIds.png', - **kwargs): - super(CityscapesDataset, self).__init__( - img_suffix=img_suffix, seg_map_suffix=seg_map_suffix, **kwargs) - - @staticmethod - def _convert_to_label_id(result): - """Convert trainId to id for cityscapes.""" - if isinstance(result, str): - result = np.load(result) - import cityscapesscripts.helpers.labels as CSLabels - result_copy = result.copy() - for trainId, label in CSLabels.trainId2label.items(): - result_copy[result == trainId] = label.id - - return result_copy - - def results2img(self, results, imgfile_prefix, to_label_id, indices=None): - """Write the segmentation results to images. - - Args: - results (list[ndarray]): Testing results of the - dataset. - imgfile_prefix (str): The filename prefix of the png files. - If the prefix is "somepath/xxx", - the png files will be named "somepath/xxx.png". - to_label_id (bool): whether convert output to label_id for - submission. - indices (list[int], optional): Indices of input results, - if not set, all the indices of the dataset will be used. - Default: None. - - Returns: - list[str: str]: result txt files which contains corresponding - semantic segmentation images. - """ - if indices is None: - indices = list(range(len(self))) - - mmcv.mkdir_or_exist(imgfile_prefix) - result_files = [] - for result, idx in zip(results, indices): - if to_label_id: - result = self._convert_to_label_id(result) - filename = self.img_infos[idx]['filename'] - basename = osp.splitext(osp.basename(filename))[0] - - png_filename = osp.join(imgfile_prefix, f'{basename}.png') - - output = Image.fromarray(result.astype(np.uint8)).convert('P') - import cityscapesscripts.helpers.labels as CSLabels - palette = np.zeros((len(CSLabels.id2label), 3), dtype=np.uint8) - for label_id, label in CSLabels.id2label.items(): - palette[label_id] = label.color - - output.putpalette(palette) - output.save(png_filename) - result_files.append(png_filename) - - return result_files - - def format_results(self, - results, - imgfile_prefix, - to_label_id=True, - indices=None): - """Format the results into dir (standard format for Cityscapes - evaluation). - - Args: - results (list): Testing results of the dataset. - imgfile_prefix (str): The prefix of images files. It - includes the file path and the prefix of filename, e.g., - "a/b/prefix". - to_label_id (bool): whether convert output to label_id for - submission. Default: False - indices (list[int], optional): Indices of input results, - if not set, all the indices of the dataset will be used. - Default: None. - - Returns: - tuple: (result_files, tmp_dir), result_files is a list containing - the image paths, tmp_dir is the temporal directory created - for saving json/png files when img_prefix is not specified. - """ - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - result_files = self.results2img(results, imgfile_prefix, to_label_id, - indices) - - return result_files - - def evaluate(self, - results, - metric='mIoU', - logger=None, - imgfile_prefix=None): - """Evaluation in Cityscapes/default protocol. - - Args: - results (list): Testing results of the dataset. - metric (str | list[str]): Metrics to be evaluated. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - imgfile_prefix (str | None): The prefix of output image file, - for cityscapes evaluation only. It includes the file path and - the prefix of filename, e.g., "a/b/prefix". - If results are evaluated with cityscapes protocol, it would be - the prefix of output png files. The output files would be - png images under folder "a/b/prefix/xxx.png", where "xxx" is - the image name of cityscapes. If not specified, a temp file - will be created for evaluation. - Default: None. - - Returns: - dict[str, float]: Cityscapes/default metrics. - """ - - eval_results = dict() - metrics = metric.copy() if isinstance(metric, list) else [metric] - if 'cityscapes' in metrics: - eval_results.update( - self._evaluate_cityscapes(results, logger, imgfile_prefix)) - metrics.remove('cityscapes') - if len(metrics) > 0: - eval_results.update( - super(CityscapesDataset, - self).evaluate(results, metrics, logger)) - - return eval_results - - def _evaluate_cityscapes(self, results, logger, imgfile_prefix): - """Evaluation in Cityscapes protocol. - - Args: - results (list): Testing results of the dataset. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - imgfile_prefix (str | None): The prefix of output image file - - Returns: - dict[str: float]: Cityscapes evaluation results. - """ - try: - import cityscapesscripts.evaluation.evalPixelLevelSemanticLabeling as CSEval # noqa - except ImportError: - raise ImportError('Please run "pip install cityscapesscripts" to ' - 'install cityscapesscripts first.') - msg = 'Evaluating in Cityscapes style' - if logger is None: - msg = '\n' + msg - print_log(msg, logger=logger) - - result_dir = imgfile_prefix - - eval_results = dict() - print_log(f'Evaluating results under {result_dir} ...', logger=logger) - - CSEval.args.evalInstLevelScore = True - CSEval.args.predictionPath = osp.abspath(result_dir) - CSEval.args.evalPixelAccuracy = True - CSEval.args.JSONOutput = False - - seg_map_list = [] - pred_list = [] - - # when evaluating with official cityscapesscripts, - # **_gtFine_labelIds.png is used - for seg_map in mmcv.scandir( - self.ann_dir, 'gtFine_labelIds.png', recursive=True): - seg_map_list.append(osp.join(self.ann_dir, seg_map)) - pred_list.append(CSEval.getPrediction(CSEval.args, seg_map)) - - eval_results.update( - CSEval.evaluateImgLists(pred_list, seg_map_list, CSEval.args)) - - return eval_results diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/coco_stuff.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/coco_stuff.py deleted file mode 100644 index 24d089556..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/coco_stuff.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class COCOStuffDataset(CustomDataset): - """COCO-Stuff dataset. - - In segmentation map annotation for COCO-Stuff, Train-IDs of the 10k version - are from 1 to 171, where 0 is the ignore index, and Train-ID of COCO Stuff - 164k is from 0 to 170, where 255 is the ignore index. So, they are all 171 - semantic categories. ``reduce_zero_label`` is set to True and False for the - 10k and 164k versions, respectively. The ``img_suffix`` is fixed to '.jpg', - and ``seg_map_suffix`` is fixed to '.png'. - """ - CLASSES = ( - 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', - 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', - 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', - 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', - 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', - 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', - 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', - 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', - 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', - 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', - 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', - 'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner', - 'blanket', 'branch', 'bridge', 'building-other', 'bush', 'cabinet', - 'cage', 'cardboard', 'carpet', 'ceiling-other', 'ceiling-tile', - 'cloth', 'clothes', 'clouds', 'counter', 'cupboard', 'curtain', - 'desk-stuff', 'dirt', 'door-stuff', 'fence', 'floor-marble', - 'floor-other', 'floor-stone', 'floor-tile', 'floor-wood', - 'flower', 'fog', 'food-other', 'fruit', 'furniture-other', 'grass', - 'gravel', 'ground-other', 'hill', 'house', 'leaves', 'light', 'mat', - 'metal', 'mirror-stuff', 'moss', 'mountain', 'mud', 'napkin', 'net', - 'paper', 'pavement', 'pillow', 'plant-other', 'plastic', 'platform', - 'playingfield', 'railing', 'railroad', 'river', 'road', 'rock', 'roof', - 'rug', 'salad', 'sand', 'sea', 'shelf', 'sky-other', 'skyscraper', - 'snow', 'solid-other', 'stairs', 'stone', 'straw', 'structural-other', - 'table', 'tent', 'textile-other', 'towel', 'tree', 'vegetable', - 'wall-brick', 'wall-concrete', 'wall-other', 'wall-panel', - 'wall-stone', 'wall-tile', 'wall-wood', 'water-other', 'waterdrops', - 'window-blind', 'window-other', 'wood') - - PALETTE = [[0, 192, 64], [0, 192, 64], [0, 64, 96], [128, 192, 192], - [0, 64, 64], [0, 192, 224], [0, 192, 192], [128, 192, 64], - [0, 192, 96], [128, 192, 64], [128, 32, 192], [0, 0, 224], - [0, 0, 64], [0, 160, 192], [128, 0, 96], [128, 0, 192], - [0, 32, 192], [128, 128, 224], [0, 0, 192], [128, 160, 192], - [128, 128, 0], [128, 0, 32], [128, 32, 0], [128, 0, 128], - [64, 128, 32], [0, 160, 0], [0, 0, 0], [192, 128, 160], - [0, 32, 0], [0, 128, 128], [64, 128, 160], [128, 160, 0], - [0, 128, 0], [192, 128, 32], [128, 96, 128], [0, 0, 128], - [64, 0, 32], [0, 224, 128], [128, 0, 0], [192, 0, 160], - [0, 96, 128], [128, 128, 128], [64, 0, 160], [128, 224, 128], - [128, 128, 64], [192, 0, 32], [128, 96, 0], [128, 0, 192], - [0, 128, 32], [64, 224, 0], [0, 0, 64], [128, 128, 160], - [64, 96, 0], [0, 128, 192], [0, 128, 160], [192, 224, 0], - [0, 128, 64], [128, 128, 32], [192, 32, 128], [0, 64, 192], - [0, 0, 32], [64, 160, 128], [128, 64, 64], [128, 0, 160], - [64, 32, 128], [128, 192, 192], [0, 0, 160], [192, 160, 128], - [128, 192, 0], [128, 0, 96], [192, 32, 0], [128, 64, 128], - [64, 128, 96], [64, 160, 0], [0, 64, 0], [192, 128, 224], - [64, 32, 0], [0, 192, 128], [64, 128, 224], [192, 160, 0], - [0, 192, 0], [192, 128, 96], [192, 96, 128], [0, 64, 128], - [64, 0, 96], [64, 224, 128], [128, 64, 0], [192, 0, 224], - [64, 96, 128], [128, 192, 128], [64, 0, 224], [192, 224, 128], - [128, 192, 64], [192, 0, 96], [192, 96, 0], [128, 64, 192], - [0, 128, 96], [0, 224, 0], [64, 64, 64], [128, 128, 224], - [0, 96, 0], [64, 192, 192], [0, 128, 224], [128, 224, 0], - [64, 192, 64], [128, 128, 96], [128, 32, 128], [64, 0, 192], - [0, 64, 96], [0, 160, 128], [192, 0, 64], [128, 64, 224], - [0, 32, 128], [192, 128, 192], [0, 64, 224], [128, 160, 128], - [192, 128, 0], [128, 64, 32], [128, 32, 64], [192, 0, 128], - [64, 192, 32], [0, 160, 64], [64, 0, 0], [192, 192, 160], - [0, 32, 64], [64, 128, 128], [64, 192, 160], [128, 160, 64], - [64, 128, 0], [192, 192, 32], [128, 96, 192], [64, 0, 128], - [64, 64, 32], [0, 224, 192], [192, 0, 0], [192, 64, 160], - [0, 96, 192], [192, 128, 128], [64, 64, 160], [128, 224, 192], - [192, 128, 64], [192, 64, 32], [128, 96, 64], [192, 0, 192], - [0, 192, 32], [64, 224, 64], [64, 0, 64], [128, 192, 160], - [64, 96, 64], [64, 128, 192], [0, 192, 160], [192, 224, 64], - [64, 128, 64], [128, 192, 32], [192, 32, 192], [64, 64, 192], - [0, 64, 32], [64, 160, 192], [192, 64, 64], [128, 64, 160], - [64, 32, 192], [192, 192, 192], [0, 64, 160], [192, 160, 192], - [192, 192, 0], [128, 64, 96], [192, 32, 64], [192, 64, 128], - [64, 192, 96], [64, 160, 64], [64, 64, 0]] - - def __init__(self, **kwargs): - super(COCOStuffDataset, self).__init__( - img_suffix='.jpg', seg_map_suffix='_labelTrainIds.png', **kwargs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/custom.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/custom.py deleted file mode 100644 index 4615d4114..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/custom.py +++ /dev/null @@ -1,487 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from collections import OrderedDict - -import mmcv -import numpy as np -from mmcv.utils import print_log -from prettytable import PrettyTable -from torch.utils.data import Dataset - -from mmseg.core import eval_metrics, intersect_and_union, pre_eval_to_metrics -from mmseg.utils import get_root_logger -from .builder import DATASETS -from .pipelines import Compose, LoadAnnotations - - -@DATASETS.register_module() -class CustomDataset(Dataset): - """Custom dataset for semantic segmentation. An example of file structure - is as followed. - - .. code-block:: none - - ├── data - │ ├── my_dataset - │ │ ├── img_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{img_suffix} - │ │ │ │ ├── yyy{img_suffix} - │ │ │ │ ├── zzz{img_suffix} - │ │ │ ├── val - │ │ ├── ann_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{seg_map_suffix} - │ │ │ │ ├── yyy{seg_map_suffix} - │ │ │ │ ├── zzz{seg_map_suffix} - │ │ │ ├── val - - The img/gt_semantic_seg pair of CustomDataset should be of the same - except suffix. A valid img/gt_semantic_seg filename pair should be like - ``xxx{img_suffix}`` and ``xxx{seg_map_suffix}`` (extension is also included - in the suffix). If split is given, then ``xxx`` is specified in txt file. - Otherwise, all files in ``img_dir/``and ``ann_dir`` will be loaded. - Please refer to ``docs/en/tutorials/new_dataset.md`` for more details. - - - Args: - pipeline (list[dict]): Processing pipeline - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. Default: '.jpg' - ann_dir (str, optional): Path to annotation directory. Default: None - seg_map_suffix (str): Suffix of segmentation maps. Default: '.png' - split (str, optional): Split txt file. If split is specified, only - file with suffix in the splits will be loaded. Otherwise, all - images in img_dir/ann_dir will be loaded. Default: None - data_root (str, optional): Data root for img_dir/ann_dir. Default: - None. - test_mode (bool): If test_mode=True, gt wouldn't be loaded. - ignore_index (int): The label index to be ignored. Default: 255 - reduce_zero_label (bool): Whether to mark label zero as ignored. - Default: False - classes (str | Sequence[str], optional): Specify classes to load. - If is None, ``cls.CLASSES`` will be used. Default: None. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, and - self.PALETTE is None, random palette will be generated. - Default: None - gt_seg_map_loader_cfg (dict, optional): build LoadAnnotations to - load gt for evaluation, load from disk by default. Default: None. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - CLASSES = None - - PALETTE = None - - def __init__(self, - pipeline, - img_dir, - img_suffix='.jpg', - ann_dir=None, - seg_map_suffix='.png', - split=None, - data_root=None, - test_mode=False, - ignore_index=255, - reduce_zero_label=False, - classes=None, - palette=None, - gt_seg_map_loader_cfg=None, - file_client_args=dict(backend='disk')): - self.pipeline = Compose(pipeline) - self.img_dir = img_dir - self.img_suffix = img_suffix - self.ann_dir = ann_dir - self.seg_map_suffix = seg_map_suffix - self.split = split - self.data_root = data_root - self.test_mode = test_mode - self.ignore_index = ignore_index - self.reduce_zero_label = reduce_zero_label - self.label_map = None - self.CLASSES, self.PALETTE = self.get_classes_and_palette( - classes, palette) - self.gt_seg_map_loader = LoadAnnotations( - ) if gt_seg_map_loader_cfg is None else LoadAnnotations( - **gt_seg_map_loader_cfg) - - self.file_client_args = file_client_args - self.file_client = mmcv.FileClient.infer_client(self.file_client_args) - - if test_mode: - assert self.CLASSES is not None, \ - '`cls.CLASSES` or `classes` should be specified when testing' - - # join paths if data_root is specified - if self.data_root is not None: - if not osp.isabs(self.img_dir): - self.img_dir = osp.join(self.data_root, self.img_dir) - if not (self.ann_dir is None or osp.isabs(self.ann_dir)): - self.ann_dir = osp.join(self.data_root, self.ann_dir) - if not (self.split is None or osp.isabs(self.split)): - self.split = osp.join(self.data_root, self.split) - - # load annotations - self.img_infos = self.load_annotations(self.img_dir, self.img_suffix, - self.ann_dir, - self.seg_map_suffix, self.split) - - def __len__(self): - """Total number of samples of data.""" - return len(self.img_infos) - - def load_annotations(self, img_dir, img_suffix, ann_dir, seg_map_suffix, - split): - """Load annotation from directory. - - Args: - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. - ann_dir (str|None): Path to annotation directory. - seg_map_suffix (str|None): Suffix of segmentation maps. - split (str|None): Split txt file. If split is specified, only file - with suffix in the splits will be loaded. Otherwise, all images - in img_dir/ann_dir will be loaded. Default: None - - Returns: - list[dict]: All image info of dataset. - """ - - img_infos = [] - if split is not None: - lines = mmcv.list_from_file( - split, file_client_args=self.file_client_args) - for line in lines: - img_name = line.strip() - img_info = dict(filename=img_name + img_suffix) - if ann_dir is not None: - seg_map = img_name + seg_map_suffix - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - else: - for img in self.file_client.list_dir_or_file( - dir_path=img_dir, - list_dir=False, - suffix=img_suffix, - recursive=True): - img_info = dict(filename=img) - if ann_dir is not None: - seg_map = img.replace(img_suffix, seg_map_suffix) - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - img_infos = sorted(img_infos, key=lambda x: x['filename']) - - print_log(f'Loaded {len(img_infos)} images', logger=get_root_logger()) - return img_infos - - def get_ann_info(self, idx): - """Get annotation by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - return self.img_infos[idx]['ann'] - - def pre_pipeline(self, results): - """Prepare results dict for pipeline.""" - results['seg_fields'] = [] - results['img_prefix'] = self.img_dir - results['seg_prefix'] = self.ann_dir - if self.custom_classes: - results['label_map'] = self.label_map - - def __getitem__(self, idx): - """Get training/test data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training/test data (with annotation if `test_mode` is set - False). - """ - - if self.test_mode: - return self.prepare_test_img(idx) - else: - return self.prepare_train_img(idx) - - def prepare_train_img(self, idx): - """Get training data and annotations after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training data and annotation after pipeline with new keys - introduced by pipeline. - """ - - img_info = self.img_infos[idx] - ann_info = self.get_ann_info(idx) - results = dict(img_info=img_info, ann_info=ann_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def prepare_test_img(self, idx): - """Get testing data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Testing data after pipeline with new keys introduced by - pipeline. - """ - - img_info = self.img_infos[idx] - results = dict(img_info=img_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """Place holder to format result to dataset specific output.""" - raise NotImplementedError - - def get_gt_seg_map_by_idx(self, index): - """Get one ground truth segmentation map for evaluation.""" - ann_info = self.get_ann_info(index) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - return results['gt_semantic_seg'] - - def get_gt_seg_maps(self, efficient_test=None): - """Get ground truth segmentation maps for evaluation.""" - if efficient_test is not None: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` has been deprecated ' - 'since MMSeg v0.16, the ``get_gt_seg_maps()`` is CPU memory ' - 'friendly by default. ') - - for idx in range(len(self)): - ann_info = self.get_ann_info(idx) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - yield results['gt_semantic_seg'] - - def pre_eval(self, preds, indices): - """Collect eval result from each iteration. - - Args: - preds (list[torch.Tensor] | torch.Tensor): the segmentation logit - after argmax, shape (N, H, W). - indices (list[int] | int): the prediction related ground truth - indices. - - Returns: - list[torch.Tensor]: (area_intersect, area_union, area_prediction, - area_ground_truth). - """ - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - - pre_eval_results = [] - - for pred, index in zip(preds, indices): - seg_map = self.get_gt_seg_map_by_idx(index) - pre_eval_results.append( - intersect_and_union( - pred, - seg_map, - len(self.CLASSES), - self.ignore_index, - # as the labels has been converted when dataset initialized - # in `get_palette_for_custom_classes ` this `label_map` - # should be `dict()`, see - # https://github.com/open-mmlab/mmsegmentation/issues/1415 - # for more ditails - label_map=dict(), - reduce_zero_label=self.reduce_zero_label)) - - return pre_eval_results - - def get_classes_and_palette(self, classes=None, palette=None): - """Get class names of current dataset. - - Args: - classes (Sequence[str] | str | None): If classes is None, use - default CLASSES defined by builtin dataset. If classes is a - string, take it as a file name. The file contains the name of - classes where each line contains one class name. If classes is - a tuple or list, override the CLASSES defined by the dataset. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, random - palette will be generated. Default: None - """ - if classes is None: - self.custom_classes = False - return self.CLASSES, self.PALETTE - - self.custom_classes = True - if isinstance(classes, str): - # take it as a file path - class_names = mmcv.list_from_file(classes) - elif isinstance(classes, (tuple, list)): - class_names = classes - else: - raise ValueError(f'Unsupported type {type(classes)} of classes.') - - if self.CLASSES: - if not set(class_names).issubset(self.CLASSES): - raise ValueError('classes is not a subset of CLASSES.') - - # dictionary, its keys are the old label ids and its values - # are the new label ids. - # used for changing pixel labels in load_annotations. - self.label_map = {} - for i, c in enumerate(self.CLASSES): - if c not in class_names: - self.label_map[i] = -1 - else: - self.label_map[i] = class_names.index(c) - - palette = self.get_palette_for_custom_classes(class_names, palette) - - return class_names, palette - - def get_palette_for_custom_classes(self, class_names, palette=None): - - if self.label_map is not None: - # return subset of palette - palette = [] - for old_id, new_id in sorted( - self.label_map.items(), key=lambda x: x[1]): - if new_id != -1: - palette.append(self.PALETTE[old_id]) - palette = type(self.PALETTE)(palette) - - elif palette is None: - if self.PALETTE is None: - # Get random state before set seed, and restore - # random state later. - # It will prevent loss of randomness, as the palette - # may be different in each iteration if not specified. - # See: https://github.com/open-mmlab/mmdetection/issues/5844 - state = np.random.get_state() - np.random.seed(42) - # random palette - palette = np.random.randint(0, 255, size=(len(class_names), 3)) - np.random.set_state(state) - else: - palette = self.PALETTE - - return palette - - def evaluate(self, - results, - metric='mIoU', - logger=None, - gt_seg_maps=None, - **kwargs): - """Evaluate the dataset. - - Args: - results (list[tuple[torch.Tensor]] | list[str]): per image pre_eval - results or predict segmentation map for computing evaluation - metric. - metric (str | list[str]): Metrics to be evaluated. 'mIoU', - 'mDice' and 'mFscore' are supported. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - gt_seg_maps (generator[ndarray]): Custom gt seg maps as input, - used in ConcatDataset - - Returns: - dict[str, float]: Default metrics. - """ - if isinstance(metric, str): - metric = [metric] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metric).issubset(set(allowed_metrics)): - raise KeyError('metric {} is not supported'.format(metric)) - - eval_results = {} - # test a list of files - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - if gt_seg_maps is None: - gt_seg_maps = self.get_gt_seg_maps() - num_classes = len(self.CLASSES) - ret_metrics = eval_metrics( - results, - gt_seg_maps, - num_classes, - self.ignore_index, - metric, - label_map=dict(), - reduce_zero_label=self.reduce_zero_label) - # test a list of pre_eval_results - else: - ret_metrics = pre_eval_to_metrics(results, metric) - - # Because dataset.CLASSES is required for per-eval. - if self.CLASSES is None: - class_names = tuple(range(num_classes)) - else: - class_names = self.CLASSES - - # summary table - ret_metrics_summary = OrderedDict({ - ret_metric: np.round(np.nanmean(ret_metric_value) * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - - # each class table - ret_metrics.pop('aAcc', None) - ret_metrics_class = OrderedDict({ - ret_metric: np.round(ret_metric_value * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - ret_metrics_class.update({'Class': class_names}) - ret_metrics_class.move_to_end('Class', last=False) - - # for logger - class_table_data = PrettyTable() - for key, val in ret_metrics_class.items(): - class_table_data.add_column(key, val) - - summary_table_data = PrettyTable() - for key, val in ret_metrics_summary.items(): - if key == 'aAcc': - summary_table_data.add_column(key, [val]) - else: - summary_table_data.add_column('m' + key, [val]) - - print_log('per class results:', logger) - print_log('\n' + class_table_data.get_string(), logger=logger) - print_log('Summary:', logger) - print_log('\n' + summary_table_data.get_string(), logger=logger) - - # each metric dict - for key, value in ret_metrics_summary.items(): - if key == 'aAcc': - eval_results[key] = value / 100.0 - else: - eval_results['m' + key] = value / 100.0 - - ret_metrics_class.pop('Class', None) - for key, value in ret_metrics_class.items(): - eval_results.update({ - key + '.' + str(name): value[idx] / 100.0 - for idx, name in enumerate(class_names) - }) - - return eval_results diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/dataset_wrappers.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/dataset_wrappers.py deleted file mode 100644 index 1fb089f9f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/dataset_wrappers.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import bisect -import collections -import copy -from itertools import chain - -import mmcv -import numpy as np -from mmcv.utils import build_from_cfg, print_log -from torch.utils.data.dataset import ConcatDataset as _ConcatDataset - -from .builder import DATASETS, PIPELINES -from .cityscapes import CityscapesDataset - - -@DATASETS.register_module() -class ConcatDataset(_ConcatDataset): - """A wrapper of concatenated dataset. - - Same as :obj:`torch.utils.data.dataset.ConcatDataset`, but - support evaluation and formatting results - - Args: - datasets (list[:obj:`Dataset`]): A list of datasets. - separate_eval (bool): Whether to evaluate the concatenated - dataset results separately, Defaults to True. - """ - - def __init__(self, datasets, separate_eval=True): - super(ConcatDataset, self).__init__(datasets) - self.CLASSES = datasets[0].CLASSES - self.PALETTE = datasets[0].PALETTE - self.separate_eval = separate_eval - assert separate_eval in [True, False], \ - f'separate_eval can only be True or False,' \ - f'but get {separate_eval}' - if any([isinstance(ds, CityscapesDataset) for ds in datasets]): - raise NotImplementedError( - 'Evaluating ConcatDataset containing CityscapesDataset' - 'is not supported!') - - def evaluate(self, results, logger=None, **kwargs): - """Evaluate the results. - - Args: - results (list[tuple[torch.Tensor]] | list[str]]): per image - pre_eval results or predict segmentation map for - computing evaluation metric. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - - Returns: - dict[str: float]: evaluate results of the total dataset - or each separate - dataset if `self.separate_eval=True`. - """ - assert len(results) == self.cumulative_sizes[-1], \ - ('Dataset and results have different sizes: ' - f'{self.cumulative_sizes[-1]} v.s. {len(results)}') - - # Check whether all the datasets support evaluation - for dataset in self.datasets: - assert hasattr(dataset, 'evaluate'), \ - f'{type(dataset)} does not implement evaluate function' - - if self.separate_eval: - dataset_idx = -1 - total_eval_results = dict() - for size, dataset in zip(self.cumulative_sizes, self.datasets): - start_idx = 0 if dataset_idx == -1 else \ - self.cumulative_sizes[dataset_idx] - end_idx = self.cumulative_sizes[dataset_idx + 1] - - results_per_dataset = results[start_idx:end_idx] - print_log( - f'\nEvaluateing {dataset.img_dir} with ' - f'{len(results_per_dataset)} images now', - logger=logger) - - eval_results_per_dataset = dataset.evaluate( - results_per_dataset, logger=logger, **kwargs) - dataset_idx += 1 - for k, v in eval_results_per_dataset.items(): - total_eval_results.update({f'{dataset_idx}_{k}': v}) - - return total_eval_results - - if len(set([type(ds) for ds in self.datasets])) != 1: - raise NotImplementedError( - 'All the datasets should have same types when ' - 'self.separate_eval=False') - else: - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - # merge the generators of gt_seg_maps - gt_seg_maps = chain( - *[dataset.get_gt_seg_maps() for dataset in self.datasets]) - else: - # if the results are `pre_eval` results, - # we do not need gt_seg_maps to evaluate - gt_seg_maps = None - eval_results = self.datasets[0].evaluate( - results, gt_seg_maps=gt_seg_maps, logger=logger, **kwargs) - return eval_results - - def get_dataset_idx_and_sample_idx(self, indice): - """Return dataset and sample index when given an indice of - ConcatDataset. - - Args: - indice (int): indice of sample in ConcatDataset - - Returns: - int: the index of sub dataset the sample belong to - int: the index of sample in its corresponding subset - """ - if indice < 0: - if -indice > len(self): - raise ValueError( - 'absolute value of index should not exceed dataset length') - indice = len(self) + indice - dataset_idx = bisect.bisect_right(self.cumulative_sizes, indice) - if dataset_idx == 0: - sample_idx = indice - else: - sample_idx = indice - self.cumulative_sizes[dataset_idx - 1] - return dataset_idx, sample_idx - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """format result for every sample of ConcatDataset.""" - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].format_results( - [results[i]], - imgfile_prefix + f'/{dataset_idx}', - indices=[sample_idx], - **kwargs) - ret_res.append(res) - return sum(ret_res, []) - - def pre_eval(self, preds, indices): - """do pre eval for every sample of ConcatDataset.""" - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].pre_eval(preds[i], sample_idx) - ret_res.append(res) - return sum(ret_res, []) - - -@DATASETS.register_module() -class RepeatDataset(object): - """A wrapper of repeated dataset. - - The length of repeated dataset will be `times` larger than the original - dataset. This is useful when the data loading time is long but the dataset - is small. Using RepeatDataset can reduce the data loading time between - epochs. - - Args: - dataset (:obj:`Dataset`): The dataset to be repeated. - times (int): Repeat times. - """ - - def __init__(self, dataset, times): - self.dataset = dataset - self.times = times - self.CLASSES = dataset.CLASSES - self.PALETTE = dataset.PALETTE - self._ori_len = len(self.dataset) - - def __getitem__(self, idx): - """Get item from original dataset.""" - return self.dataset[idx % self._ori_len] - - def __len__(self): - """The length is multiplied by ``times``""" - return self.times * self._ori_len - - -@DATASETS.register_module() -class MultiImageMixDataset: - """A wrapper of multiple images mixed dataset. - - Suitable for training on multiple images mixed data augmentation like - mosaic and mixup. For the augmentation pipeline of mixed image data, - the `get_indexes` method needs to be provided to obtain the image - indexes, and you can set `skip_flags` to change the pipeline running - process. - - - Args: - dataset (:obj:`CustomDataset`): The dataset to be mixed. - pipeline (Sequence[dict]): Sequence of transform object or - config dict to be composed. - skip_type_keys (list[str], optional): Sequence of type string to - be skip pipeline. Default to None. - """ - - def __init__(self, dataset, pipeline, skip_type_keys=None): - assert isinstance(pipeline, collections.abc.Sequence) - if skip_type_keys is not None: - assert all([ - isinstance(skip_type_key, str) - for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys - - self.pipeline = [] - self.pipeline_types = [] - for transform in pipeline: - if isinstance(transform, dict): - self.pipeline_types.append(transform['type']) - transform = build_from_cfg(transform, PIPELINES) - self.pipeline.append(transform) - else: - raise TypeError('pipeline must be a dict') - - self.dataset = dataset - self.CLASSES = dataset.CLASSES - self.PALETTE = dataset.PALETTE - self.num_samples = len(dataset) - - def __len__(self): - return self.num_samples - - def __getitem__(self, idx): - results = copy.deepcopy(self.dataset[idx]) - for (transform, transform_type) in zip(self.pipeline, - self.pipeline_types): - if self._skip_type_keys is not None and \ - transform_type in self._skip_type_keys: - continue - - if hasattr(transform, 'get_indexes'): - indexes = transform.get_indexes(self.dataset) - if not isinstance(indexes, collections.abc.Sequence): - indexes = [indexes] - mix_results = [ - copy.deepcopy(self.dataset[index]) for index in indexes - ] - results['mix_results'] = mix_results - - results = transform(results) - - if 'mix_results' in results: - results.pop('mix_results') - - return results - - def update_skip_type_keys(self, skip_type_keys): - """Update skip_type_keys. - - It is called by an external hook. - - Args: - skip_type_keys (list[str], optional): Sequence of type - string to be skip pipeline. - """ - assert all([ - isinstance(skip_type_key, str) for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pascal_context.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pascal_context.py deleted file mode 100644 index efacee0f3..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pascal_context.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class PascalContextDataset(CustomDataset): - """PascalContext dataset. - - In segmentation map annotation for PascalContext, 0 stands for background, - which is included in 60 categories. ``reduce_zero_label`` is fixed to - False. The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is - fixed to '.png'. - - Args: - split (str): Split txt file for PascalContext. - """ - - CLASSES = ('background', 'aeroplane', 'bag', 'bed', 'bedclothes', 'bench', - 'bicycle', 'bird', 'boat', 'book', 'bottle', 'building', 'bus', - 'cabinet', 'car', 'cat', 'ceiling', 'chair', 'cloth', - 'computer', 'cow', 'cup', 'curtain', 'dog', 'door', 'fence', - 'floor', 'flower', 'food', 'grass', 'ground', 'horse', - 'keyboard', 'light', 'motorbike', 'mountain', 'mouse', 'person', - 'plate', 'platform', 'pottedplant', 'road', 'rock', 'sheep', - 'shelves', 'sidewalk', 'sign', 'sky', 'snow', 'sofa', 'table', - 'track', 'train', 'tree', 'truck', 'tvmonitor', 'wall', 'water', - 'window', 'wood') - - PALETTE = [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255]] - - def __init__(self, split, **kwargs): - super(PascalContextDataset, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - split=split, - reduce_zero_label=False, - **kwargs) - assert self.file_client.exists(self.img_dir) and self.split is not None - - -@DATASETS.register_module() -class PascalContextDataset59(CustomDataset): - """PascalContext dataset. - - In segmentation map annotation for PascalContext, 0 stands for background, - which is included in 60 categories. ``reduce_zero_label`` is fixed to - False. The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is - fixed to '.png'. - - Args: - split (str): Split txt file for PascalContext. - """ - - CLASSES = ('aeroplane', 'bag', 'bed', 'bedclothes', 'bench', 'bicycle', - 'bird', 'boat', 'book', 'bottle', 'building', 'bus', 'cabinet', - 'car', 'cat', 'ceiling', 'chair', 'cloth', 'computer', 'cow', - 'cup', 'curtain', 'dog', 'door', 'fence', 'floor', 'flower', - 'food', 'grass', 'ground', 'horse', 'keyboard', 'light', - 'motorbike', 'mountain', 'mouse', 'person', 'plate', 'platform', - 'pottedplant', 'road', 'rock', 'sheep', 'shelves', 'sidewalk', - 'sign', 'sky', 'snow', 'sofa', 'table', 'track', 'train', - 'tree', 'truck', 'tvmonitor', 'wall', 'water', 'window', 'wood') - - PALETTE = [[180, 120, 120], [6, 230, 230], [80, 50, 50], [4, 200, 3], - [120, 120, 80], [140, 140, 140], [204, 5, 255], [230, 230, 230], - [4, 250, 7], [224, 5, 255], [235, 255, 7], [150, 5, 61], - [120, 120, 70], [8, 255, 51], [255, 6, 82], [143, 255, 140], - [204, 255, 4], [255, 51, 7], [204, 70, 3], [0, 102, 200], - [61, 230, 250], [255, 6, 51], [11, 102, 255], [255, 7, 71], - [255, 9, 224], [9, 7, 230], [220, 220, 220], [255, 9, 92], - [112, 9, 255], [8, 255, 214], [7, 255, 224], [255, 184, 6], - [10, 255, 71], [255, 41, 10], [7, 255, 255], [224, 255, 8], - [102, 8, 255], [255, 61, 6], [255, 194, 7], [255, 122, 8], - [0, 255, 20], [255, 8, 41], [255, 5, 153], [6, 51, 255], - [235, 12, 255], [160, 150, 20], [0, 163, 255], [140, 140, 140], - [250, 10, 15], [20, 255, 0], [31, 255, 0], [255, 31, 0], - [255, 224, 0], [153, 255, 0], [0, 0, 255], [255, 71, 0], - [0, 235, 255], [0, 173, 255], [31, 0, 255]] - - def __init__(self, split, **kwargs): - super(PascalContextDataset59, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - split=split, - reduce_zero_label=True, - **kwargs) - assert self.file_client.exists(self.img_dir) and self.split is not None diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/__init__.py deleted file mode 100644 index 8256a6fe2..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .compose import Compose -from .formatting import (Collect, ImageToTensor, ToDataContainer, ToTensor, - Transpose, to_tensor) -from .loading import LoadAnnotations, LoadImageFromFile -from .test_time_aug import MultiScaleFlipAug -from .transforms import (CLAHE, AdjustGamma, Normalize, Pad, - PhotoMetricDistortion, RandomCrop, RandomCutOut, - RandomFlip, RandomMosaic, RandomRotate, Rerange, - Resize, RGB2Gray, SegRescale) - -__all__ = [ - 'Compose', 'to_tensor', 'ToTensor', 'ImageToTensor', 'ToDataContainer', - 'Transpose', 'Collect', 'LoadAnnotations', 'LoadImageFromFile', - 'MultiScaleFlipAug', 'Resize', 'RandomFlip', 'Pad', 'RandomCrop', - 'Normalize', 'SegRescale', 'PhotoMetricDistortion', 'RandomRotate', - 'AdjustGamma', 'CLAHE', 'Rerange', 'RGB2Gray', 'RandomCutOut', - 'RandomMosaic' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/compose.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/compose.py deleted file mode 100644 index 30280c133..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/compose.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections - -from mmcv.utils import build_from_cfg - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class Compose(object): - """Compose multiple transforms sequentially. - - Args: - transforms (Sequence[dict | callable]): Sequence of transform object or - config dict to be composed. - """ - - def __init__(self, transforms): - assert isinstance(transforms, collections.abc.Sequence) - self.transforms = [] - for transform in transforms: - if isinstance(transform, dict): - transform = build_from_cfg(transform, PIPELINES) - self.transforms.append(transform) - elif callable(transform): - self.transforms.append(transform) - else: - raise TypeError('transform must be callable or a dict') - - def __call__(self, data): - """Call function to apply transforms sequentially. - - Args: - data (dict): A result dict contains the data to transform. - - Returns: - dict: Transformed data. - """ - - for t in self.transforms: - data = t(data) - if data is None: - return None - return data - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - format_string += '\n' - format_string += f' {t}' - format_string += '\n)' - return format_string diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/formating.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/formating.py deleted file mode 100644 index f6e53bfeb..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/formating.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -import warnings - -from .formatting import * - -warnings.warn('DeprecationWarning: mmseg.datasets.pipelines.formating will be ' - 'deprecated in 2021, please replace it with ' - 'mmseg.datasets.pipelines.formatting.') diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/formatting.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/formatting.py deleted file mode 100644 index 4e057c1b8..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/formatting.py +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import mmcv -import numpy as np -import torch -from mmcv.parallel import DataContainer as DC - -from ..builder import PIPELINES - - -def to_tensor(data): - """Convert objects of various python types to :obj:`torch.Tensor`. - - Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, - :class:`Sequence`, :class:`int` and :class:`float`. - - Args: - data (torch.Tensor | numpy.ndarray | Sequence | int | float): Data to - be converted. - """ - - if isinstance(data, torch.Tensor): - return data - elif isinstance(data, np.ndarray): - return torch.from_numpy(data) - elif isinstance(data, Sequence) and not mmcv.is_str(data): - return torch.tensor(data) - elif isinstance(data, int): - return torch.LongTensor([data]) - elif isinstance(data, float): - return torch.FloatTensor([data]) - else: - raise TypeError(f'type {type(data)} cannot be converted to tensor.') - - -@PIPELINES.register_module() -class ToTensor(object): - """Convert some results to :obj:`torch.Tensor` by given keys. - - Args: - keys (Sequence[str]): Keys that need to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert data in results to :obj:`torch.Tensor`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted - to :obj:`torch.Tensor`. - """ - - for key in self.keys: - results[key] = to_tensor(results[key]) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class ImageToTensor(object): - """Convert image to :obj:`torch.Tensor` by given keys. - - The dimension order of input image is (H, W, C). The pipeline will convert - it to (C, H, W). If only 2 dimension (H, W) is given, the output would be - (1, H, W). - - Args: - keys (Sequence[str]): Key of images to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - img = results[key] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - results[key] = to_tensor(img.transpose(2, 0, 1)) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class Transpose(object): - """Transpose some results by given keys. - - Args: - keys (Sequence[str]): Keys of results to be transposed. - order (Sequence[int]): Order of transpose. - """ - - def __init__(self, keys, order): - self.keys = keys - self.order = order - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - results[key] = results[key].transpose(self.order) - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, order={self.order})' - - -@PIPELINES.register_module() -class ToDataContainer(object): - """Convert results to :obj:`mmcv.DataContainer` by given fields. - - Args: - fields (Sequence[dict]): Each field is a dict like - ``dict(key='xxx', **kwargs)``. The ``key`` in result will - be converted to :obj:`mmcv.DataContainer` with ``**kwargs``. - Default: ``(dict(key='img', stack=True), - dict(key='gt_semantic_seg'))``. - """ - - def __init__(self, - fields=(dict(key='img', - stack=True), dict(key='gt_semantic_seg'))): - self.fields = fields - - def __call__(self, results): - """Call function to convert data in results to - :obj:`mmcv.DataContainer`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted to - :obj:`mmcv.DataContainer`. - """ - - for field in self.fields: - field = field.copy() - key = field.pop('key') - results[key] = DC(results[key], **field) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(fields={self.fields})' - - -@PIPELINES.register_module() -class DefaultFormatBundle(object): - """Default formatting bundle. - - It simplifies the pipeline of formatting common fields, including "img" - and "gt_semantic_seg". These fields are formatted as follows. - - - img: (1)transpose, (2)to tensor, (3)to DataContainer (stack=True) - - gt_semantic_seg: (1)unsqueeze dim-0 (2)to tensor, - (3)to DataContainer (stack=True) - """ - - def __call__(self, results): - """Call function to transform and format common fields in results. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data that is formatted with - default bundle. - """ - - if 'img' in results: - img = results['img'] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - img = np.ascontiguousarray(img.transpose(2, 0, 1)) - results['img'] = DC(to_tensor(img), stack=True) - if 'gt_semantic_seg' in results: - # convert to long - results['gt_semantic_seg'] = DC( - to_tensor(results['gt_semantic_seg'][None, - ...].astype(np.int64)), - stack=True) - return results - - def __repr__(self): - return self.__class__.__name__ - - -@PIPELINES.register_module() -class Collect(object): - """Collect data from the loader relevant to the specific task. - - This is usually the last stage of the data loader pipeline. Typically keys - is set to some subset of "img", "gt_semantic_seg". - - The "img_meta" item is always populated. The contents of the "img_meta" - dictionary depends on "meta_keys". By default this includes: - - - "img_shape": shape of the image input to the network as a tuple - (h, w, c). Note that images may be zero padded on the bottom/right - if the batch tensor is larger than this shape. - - - "scale_factor": a float indicating the preprocessing scale - - - "flip": a boolean indicating if image flip transform was used - - - "filename": path to the image file - - - "ori_shape": original shape of the image as a tuple (h, w, c) - - - "pad_shape": image shape after padding - - - "img_norm_cfg": a dict of normalization information: - - mean - per channel mean subtraction - - std - per channel std divisor - - to_rgb - bool indicating if bgr was converted to rgb - - Args: - keys (Sequence[str]): Keys of results to be collected in ``data``. - meta_keys (Sequence[str], optional): Meta keys to be converted to - ``mmcv.DataContainer`` and collected in ``data[img_metas]``. - Default: (``filename``, ``ori_filename``, ``ori_shape``, - ``img_shape``, ``pad_shape``, ``scale_factor``, ``flip``, - ``flip_direction``, ``img_norm_cfg``) - """ - - def __init__(self, - keys, - meta_keys=('filename', 'ori_filename', 'ori_shape', - 'img_shape', 'pad_shape', 'scale_factor', 'flip', - 'flip_direction', 'img_norm_cfg')): - self.keys = keys - self.meta_keys = meta_keys - - def __call__(self, results): - """Call function to collect keys in results. The keys in ``meta_keys`` - will be converted to :obj:mmcv.DataContainer. - - Args: - results (dict): Result dict contains the data to collect. - - Returns: - dict: The result dict contains the following keys - - keys in``self.keys`` - - ``img_metas`` - """ - - data = {} - img_meta = {} - for key in self.meta_keys: - img_meta[key] = results[key] - data['img_metas'] = DC(img_meta, cpu_only=True) - for key in self.keys: - data[key] = results[key] - return data - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, meta_keys={self.meta_keys})' diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/loading.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/loading.py deleted file mode 100644 index 572e43431..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/loading.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class LoadImageFromFile(object): - """Load an image from file. - - Required keys are "img_prefix" and "img_info" (a dict that must contain the - key "filename"). Added or updated keys are "filename", "img", "img_shape", - "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`), - "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1). - - Args: - to_float32 (bool): Whether to convert the loaded image to a float32 - numpy array. If set to False, the loaded image is an uint8 array. - Defaults to False. - color_type (str): The flag argument for :func:`mmcv.imfrombytes`. - Defaults to 'color'. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'cv2' - """ - - def __init__(self, - to_float32=False, - color_type='color', - file_client_args=dict(backend='disk'), - imdecode_backend='cv2'): - self.to_float32 = to_float32 - self.color_type = color_type - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call functions to load image and get image meta information. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded image and meta information. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('img_prefix') is not None: - filename = osp.join(results['img_prefix'], - results['img_info']['filename']) - else: - filename = results['img_info']['filename'] - img_bytes = self.file_client.get(filename) - img = mmcv.imfrombytes( - img_bytes, flag=self.color_type, backend=self.imdecode_backend) - if self.to_float32: - img = img.astype(np.float32) - - results['filename'] = filename - results['ori_filename'] = results['img_info']['filename'] - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - # Set initial values for default meta_keys - results['pad_shape'] = img.shape - results['scale_factor'] = 1.0 - num_channels = 1 if len(img.shape) < 3 else img.shape[2] - results['img_norm_cfg'] = dict( - mean=np.zeros(num_channels, dtype=np.float32), - std=np.ones(num_channels, dtype=np.float32), - to_rgb=False) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(to_float32={self.to_float32},' - repr_str += f"color_type='{self.color_type}'," - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str - - -@PIPELINES.register_module() -class LoadAnnotations(object): - """Load annotations for semantic segmentation. - - Args: - reduce_zero_label (bool): Whether reduce all label value by 1. - Usually used for datasets where 0 is background label. - Default: False. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'pillow' - """ - - def __init__(self, - reduce_zero_label=False, - file_client_args=dict(backend='disk'), - imdecode_backend='pillow'): - self.reduce_zero_label = reduce_zero_label - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call function to load multiple types annotations. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded semantic segmentation annotations. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('seg_prefix', None) is not None: - filename = osp.join(results['seg_prefix'], - results['ann_info']['seg_map']) - else: - filename = results['ann_info']['seg_map'] - img_bytes = self.file_client.get(filename) - gt_semantic_seg = mmcv.imfrombytes( - img_bytes, flag='unchanged', - backend=self.imdecode_backend).squeeze().astype(np.uint8) - # modify if custom classes - if results.get('label_map', None) is not None: - # Add deep copy to solve bug of repeatedly - # replace `gt_semantic_seg`, which is reported in - # https://github.com/open-mmlab/mmsegmentation/pull/1445/ - gt_semantic_seg_copy = gt_semantic_seg.copy() - for old_id, new_id in results['label_map'].items(): - gt_semantic_seg[gt_semantic_seg_copy == old_id] = new_id - # reduce zero_label - if self.reduce_zero_label: - # avoid using underflow conversion - gt_semantic_seg[gt_semantic_seg == 0] = 255 - gt_semantic_seg = gt_semantic_seg - 1 - gt_semantic_seg[gt_semantic_seg == 254] = 255 - results['gt_semantic_seg'] = gt_semantic_seg - results['seg_fields'].append('gt_semantic_seg') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(reduce_zero_label={self.reduce_zero_label},' - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py deleted file mode 100644 index 5c17cbbba..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/test_time_aug.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv - -from ..builder import PIPELINES -from .compose import Compose - - -@PIPELINES.register_module() -class MultiScaleFlipAug(object): - """Test-time augmentation with multiple scales and flipping. - - An example configuration is as followed: - - .. code-block:: - - img_scale=(2048, 1024), - img_ratios=[0.5, 1.0], - flip=True, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ] - - After MultiScaleFLipAug with above configuration, the results are wrapped - into lists of the same length as followed: - - .. code-block:: - - dict( - img=[...], - img_shape=[...], - scale=[(1024, 512), (1024, 512), (2048, 1024), (2048, 1024)] - flip=[False, True, False, True] - ... - ) - - Args: - transforms (list[dict]): Transforms to apply in each augmentation. - img_scale (None | tuple | list[tuple]): Images scales for resizing. - img_ratios (float | list[float]): Image ratios for resizing - flip (bool): Whether apply flip augmentation. Default: False. - flip_direction (str | list[str]): Flip augmentation directions, - options are "horizontal" and "vertical". If flip_direction is list, - multiple flip augmentations will be applied. - It has no effect when flip == False. Default: "horizontal". - """ - - def __init__(self, - transforms, - img_scale, - img_ratios=None, - flip=False, - flip_direction='horizontal'): - self.transforms = Compose(transforms) - if img_ratios is not None: - img_ratios = img_ratios if isinstance(img_ratios, - list) else [img_ratios] - assert mmcv.is_list_of(img_ratios, float) - if img_scale is None: - # mode 1: given img_scale=None and a range of image ratio - self.img_scale = None - assert mmcv.is_list_of(img_ratios, float) - elif isinstance(img_scale, tuple) and mmcv.is_list_of( - img_ratios, float): - assert len(img_scale) == 2 - # mode 2: given a scale and a range of image ratio - self.img_scale = [(int(img_scale[0] * ratio), - int(img_scale[1] * ratio)) - for ratio in img_ratios] - else: - # mode 3: given multiple scales - self.img_scale = img_scale if isinstance(img_scale, - list) else [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) or self.img_scale is None - self.flip = flip - self.img_ratios = img_ratios - self.flip_direction = flip_direction if isinstance( - flip_direction, list) else [flip_direction] - assert mmcv.is_list_of(self.flip_direction, str) - if not self.flip and self.flip_direction != ['horizontal']: - warnings.warn( - 'flip_direction has no effect when flip is set to False') - if (self.flip - and not any([t['type'] == 'RandomFlip' for t in transforms])): - warnings.warn( - 'flip has no effect when RandomFlip is not in transforms') - - def __call__(self, results): - """Call function to apply test time augment transforms on results. - - Args: - results (dict): Result dict contains the data to transform. - - Returns: - dict[str: list]: The augmented data, where each value is wrapped - into a list. - """ - - aug_data = [] - if self.img_scale is None and mmcv.is_list_of(self.img_ratios, float): - h, w = results['img'].shape[:2] - img_scale = [(int(w * ratio), int(h * ratio)) - for ratio in self.img_ratios] - else: - img_scale = self.img_scale - flip_aug = [False, True] if self.flip else [False] - for scale in img_scale: - for flip in flip_aug: - for direction in self.flip_direction: - _results = results.copy() - _results['scale'] = scale - _results['flip'] = flip - _results['flip_direction'] = direction - data = self.transforms(_results) - aug_data.append(data) - # list of dict to dict of list - aug_data_dict = {key: [] for key in aug_data[0]} - for data in aug_data: - for key, val in data.items(): - aug_data_dict[key].append(val) - return aug_data_dict - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(transforms={self.transforms}, ' - repr_str += f'img_scale={self.img_scale}, flip={self.flip})' - repr_str += f'flip_direction={self.flip_direction}' - return repr_str diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/transforms.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/transforms.py deleted file mode 100644 index 5673b646f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/pipelines/transforms.py +++ /dev/null @@ -1,1335 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -import mmcv -import numpy as np -from mmcv.utils import deprecated_api_warning, is_tuple_of -from numpy import random - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class ResizeToMultiple(object): - """Resize images & seg to multiple of divisor. - - Args: - size_divisor (int): images and gt seg maps need to resize to multiple - of size_divisor. Default: 32. - interpolation (str, optional): The interpolation mode of image resize. - Default: None - """ - - def __init__(self, size_divisor=32, interpolation=None): - self.size_divisor = size_divisor - self.interpolation = interpolation - - def __call__(self, results): - """Call function to resize images, semantic segmentation map to - multiple of size divisor. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape' keys are updated. - """ - # Align image to multiple of size divisor. - img = results['img'] - img = mmcv.imresize_to_multiple( - img, - self.size_divisor, - scale_factor=1, - interpolation=self.interpolation - if self.interpolation else 'bilinear') - - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape - - # Align segmentation map to multiple of size divisor. - for key in results.get('seg_fields', []): - gt_seg = results[key] - gt_seg = mmcv.imresize_to_multiple( - gt_seg, - self.size_divisor, - scale_factor=1, - interpolation='nearest') - results[key] = gt_seg - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(size_divisor={self.size_divisor}, ' - f'interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class Resize(object): - """Resize images & seg. - - This transform resizes the input image to some scale. If the input dict - contains the key "scale", then the scale in the input dict is used, - otherwise the specified scale in the init method is used. - - ``img_scale`` can be None, a tuple (single-scale) or a list of tuple - (multi-scale). There are 4 multiscale modes: - - - ``ratio_range is not None``: - 1. When img_scale is None, img_scale is the shape of image in results - (img_scale = results['img'].shape[:2]) and the image is resized based - on the original size. (mode 1) - 2. When img_scale is a tuple (single-scale), randomly sample a ratio from - the ratio range and multiply it with the image scale. (mode 2) - - - ``ratio_range is None and multiscale_mode == "range"``: randomly sample a - scale from the a range. (mode 3) - - - ``ratio_range is None and multiscale_mode == "value"``: randomly sample a - scale from multiple scales. (mode 4) - - Args: - img_scale (tuple or list[tuple]): Images scales for resizing. - Default:None. - multiscale_mode (str): Either "range" or "value". - Default: 'range' - ratio_range (tuple[float]): (min_ratio, max_ratio). - Default: None - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: True - min_size (int, optional): The minimum size for input and the shape - of the image and seg map will not be less than ``min_size``. - As the shape of model input is fixed like 'SETR' and 'BEiT'. - Following the setting in these models, resized images must be - bigger than the crop size in ``slide_inference``. Default: None - """ - - def __init__(self, - img_scale=None, - multiscale_mode='range', - ratio_range=None, - keep_ratio=True, - min_size=None): - if img_scale is None: - self.img_scale = None - else: - if isinstance(img_scale, list): - self.img_scale = img_scale - else: - self.img_scale = [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) - - if ratio_range is not None: - # mode 1: given img_scale=None and a range of image ratio - # mode 2: given a scale and a range of image ratio - assert self.img_scale is None or len(self.img_scale) == 1 - else: - # mode 3 and 4: given multiple scales or a range of scales - assert multiscale_mode in ['value', 'range'] - - self.multiscale_mode = multiscale_mode - self.ratio_range = ratio_range - self.keep_ratio = keep_ratio - self.min_size = min_size - - @staticmethod - def random_select(img_scales): - """Randomly select an img_scale from given candidates. - - Args: - img_scales (list[tuple]): Images scales for selection. - - Returns: - (tuple, int): Returns a tuple ``(img_scale, scale_dix)``, - where ``img_scale`` is the selected image scale and - ``scale_idx`` is the selected index in the given candidates. - """ - - assert mmcv.is_list_of(img_scales, tuple) - scale_idx = np.random.randint(len(img_scales)) - img_scale = img_scales[scale_idx] - return img_scale, scale_idx - - @staticmethod - def random_sample(img_scales): - """Randomly sample an img_scale when ``multiscale_mode=='range'``. - - Args: - img_scales (list[tuple]): Images scale range for sampling. - There must be two tuples in img_scales, which specify the lower - and upper bound of image scales. - - Returns: - (tuple, None): Returns a tuple ``(img_scale, None)``, where - ``img_scale`` is sampled scale and None is just a placeholder - to be consistent with :func:`random_select`. - """ - - assert mmcv.is_list_of(img_scales, tuple) and len(img_scales) == 2 - img_scale_long = [max(s) for s in img_scales] - img_scale_short = [min(s) for s in img_scales] - long_edge = np.random.randint( - min(img_scale_long), - max(img_scale_long) + 1) - short_edge = np.random.randint( - min(img_scale_short), - max(img_scale_short) + 1) - img_scale = (long_edge, short_edge) - return img_scale, None - - @staticmethod - def random_sample_ratio(img_scale, ratio_range): - """Randomly sample an img_scale when ``ratio_range`` is specified. - - A ratio will be randomly sampled from the range specified by - ``ratio_range``. Then it would be multiplied with ``img_scale`` to - generate sampled scale. - - Args: - img_scale (tuple): Images scale base to multiply with ratio. - ratio_range (tuple[float]): The minimum and maximum ratio to scale - the ``img_scale``. - - Returns: - (tuple, None): Returns a tuple ``(scale, None)``, where - ``scale`` is sampled ratio multiplied with ``img_scale`` and - None is just a placeholder to be consistent with - :func:`random_select`. - """ - - assert isinstance(img_scale, tuple) and len(img_scale) == 2 - min_ratio, max_ratio = ratio_range - assert min_ratio <= max_ratio - ratio = np.random.random_sample() * (max_ratio - min_ratio) + min_ratio - scale = int(img_scale[0] * ratio), int(img_scale[1] * ratio) - return scale, None - - def _random_scale(self, results): - """Randomly sample an img_scale according to ``ratio_range`` and - ``multiscale_mode``. - - If ``ratio_range`` is specified, a ratio will be sampled and be - multiplied with ``img_scale``. - If multiple scales are specified by ``img_scale``, a scale will be - sampled according to ``multiscale_mode``. - Otherwise, single scale will be used. - - Args: - results (dict): Result dict from :obj:`dataset`. - - Returns: - dict: Two new keys 'scale` and 'scale_idx` are added into - ``results``, which would be used by subsequent pipelines. - """ - - if self.ratio_range is not None: - if self.img_scale is None: - h, w = results['img'].shape[:2] - scale, scale_idx = self.random_sample_ratio((w, h), - self.ratio_range) - else: - scale, scale_idx = self.random_sample_ratio( - self.img_scale[0], self.ratio_range) - elif len(self.img_scale) == 1: - scale, scale_idx = self.img_scale[0], 0 - elif self.multiscale_mode == 'range': - scale, scale_idx = self.random_sample(self.img_scale) - elif self.multiscale_mode == 'value': - scale, scale_idx = self.random_select(self.img_scale) - else: - raise NotImplementedError - - results['scale'] = scale - results['scale_idx'] = scale_idx - - def _resize_img(self, results): - """Resize images with ``results['scale']``.""" - if self.keep_ratio: - if self.min_size is not None: - # TODO: Now 'min_size' is an 'int' which means the minimum - # shape of images is (min_size, min_size, 3). 'min_size' - # with tuple type will be supported, i.e. the width and - # height are not equal. - if min(results['scale']) < self.min_size: - new_short = self.min_size - else: - new_short = min(results['scale']) - - h, w = results['img'].shape[:2] - if h > w: - new_h, new_w = new_short * h / w, new_short - else: - new_h, new_w = new_short, new_short * w / h - results['scale'] = (new_h, new_w) - - img, scale_factor = mmcv.imrescale( - results['img'], results['scale'], return_scale=True) - # the w_scale and h_scale has minor difference - # a real fix should be done in the mmcv.imrescale in the future - new_h, new_w = img.shape[:2] - h, w = results['img'].shape[:2] - w_scale = new_w / w - h_scale = new_h / h - else: - img, w_scale, h_scale = mmcv.imresize( - results['img'], results['scale'], return_scale=True) - scale_factor = np.array([w_scale, h_scale, w_scale, h_scale], - dtype=np.float32) - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape # in case that there is no padding - results['scale_factor'] = scale_factor - results['keep_ratio'] = self.keep_ratio - - def _resize_seg(self, results): - """Resize semantic segmentation map with ``results['scale']``.""" - for key in results.get('seg_fields', []): - if self.keep_ratio: - gt_seg = mmcv.imrescale( - results[key], results['scale'], interpolation='nearest') - else: - gt_seg = mmcv.imresize( - results[key], results['scale'], interpolation='nearest') - results[key] = gt_seg - - def __call__(self, results): - """Call function to resize images, bounding boxes, masks, semantic - segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape', 'scale_factor', - 'keep_ratio' keys are added into result dict. - """ - - if 'scale' not in results: - self._random_scale(results) - self._resize_img(results) - self._resize_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(img_scale={self.img_scale}, ' - f'multiscale_mode={self.multiscale_mode}, ' - f'ratio_range={self.ratio_range}, ' - f'keep_ratio={self.keep_ratio})') - return repr_str - - -@PIPELINES.register_module() -class RandomFlip(object): - """Flip the image & seg. - - If the input dict contains the key "flip", then the flag will be used, - otherwise it will be randomly decided by a ratio specified in the init - method. - - Args: - prob (float, optional): The flipping probability. Default: None. - direction(str, optional): The flipping direction. Options are - 'horizontal' and 'vertical'. Default: 'horizontal'. - """ - - @deprecated_api_warning({'flip_ratio': 'prob'}, cls_name='RandomFlip') - def __init__(self, prob=None, direction='horizontal'): - self.prob = prob - self.direction = direction - if prob is not None: - assert prob >= 0 and prob <= 1 - assert direction in ['horizontal', 'vertical'] - - def __call__(self, results): - """Call function to flip bounding boxes, masks, semantic segmentation - maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Flipped results, 'flip', 'flip_direction' keys are added into - result dict. - """ - - if 'flip' not in results: - flip = True if np.random.rand() < self.prob else False - results['flip'] = flip - if 'flip_direction' not in results: - results['flip_direction'] = self.direction - if results['flip']: - # flip image - results['img'] = mmcv.imflip( - results['img'], direction=results['flip_direction']) - - # flip segs - for key in results.get('seg_fields', []): - # use copy() to make numpy stride positive - results[key] = mmcv.imflip( - results[key], direction=results['flip_direction']).copy() - return results - - def __repr__(self): - return self.__class__.__name__ + f'(prob={self.prob})' - - -@PIPELINES.register_module() -class Pad(object): - """Pad the image & mask. - - There are two padding modes: (1) pad to a fixed size and (2) pad to the - minimum size that is divisible by some number. - Added keys are "pad_shape", "pad_fixed_size", "pad_size_divisor", - - Args: - size (tuple, optional): Fixed padding size. - size_divisor (int, optional): The divisor of padded size. - pad_val (float, optional): Padding value. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - """ - - def __init__(self, - size=None, - size_divisor=None, - pad_val=0, - seg_pad_val=255): - self.size = size - self.size_divisor = size_divisor - self.pad_val = pad_val - self.seg_pad_val = seg_pad_val - # only one of size and size_divisor should be valid - assert size is not None or size_divisor is not None - assert size is None or size_divisor is None - - def _pad_img(self, results): - """Pad images according to ``self.size``.""" - if self.size is not None: - padded_img = mmcv.impad( - results['img'], shape=self.size, pad_val=self.pad_val) - elif self.size_divisor is not None: - padded_img = mmcv.impad_to_multiple( - results['img'], self.size_divisor, pad_val=self.pad_val) - results['img'] = padded_img - results['pad_shape'] = padded_img.shape - results['pad_fixed_size'] = self.size - results['pad_size_divisor'] = self.size_divisor - - def _pad_seg(self, results): - """Pad masks according to ``results['pad_shape']``.""" - for key in results.get('seg_fields', []): - results[key] = mmcv.impad( - results[key], - shape=results['pad_shape'][:2], - pad_val=self.seg_pad_val) - - def __call__(self, results): - """Call function to pad images, masks, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Updated result dict. - """ - - self._pad_img(results) - self._pad_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(size={self.size}, size_divisor={self.size_divisor}, ' \ - f'pad_val={self.pad_val})' - return repr_str - - -@PIPELINES.register_module() -class Normalize(object): - """Normalize the image. - - Added key is "img_norm_cfg". - - Args: - mean (sequence): Mean values of 3 channels. - std (sequence): Std values of 3 channels. - to_rgb (bool): Whether to convert the image from BGR to RGB, - default is true. - """ - - def __init__(self, mean, std, to_rgb=True): - self.mean = np.array(mean, dtype=np.float32) - self.std = np.array(std, dtype=np.float32) - self.to_rgb = to_rgb - - def __call__(self, results): - """Call function to normalize images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Normalized results, 'img_norm_cfg' key is added into - result dict. - """ - - results['img'] = mmcv.imnormalize(results['img'], self.mean, self.std, - self.to_rgb) - results['img_norm_cfg'] = dict( - mean=self.mean, std=self.std, to_rgb=self.to_rgb) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(mean={self.mean}, std={self.std}, to_rgb=' \ - f'{self.to_rgb})' - return repr_str - - -@PIPELINES.register_module() -class Rerange(object): - """Rerange the image pixel value. - - Args: - min_value (float or int): Minimum value of the reranged image. - Default: 0. - max_value (float or int): Maximum value of the reranged image. - Default: 255. - """ - - def __init__(self, min_value=0, max_value=255): - assert isinstance(min_value, float) or isinstance(min_value, int) - assert isinstance(max_value, float) or isinstance(max_value, int) - assert min_value < max_value - self.min_value = min_value - self.max_value = max_value - - def __call__(self, results): - """Call function to rerange images. - - Args: - results (dict): Result dict from loading pipeline. - Returns: - dict: Reranged results. - """ - - img = results['img'] - img_min_value = np.min(img) - img_max_value = np.max(img) - - assert img_min_value < img_max_value - # rerange to [0, 1] - img = (img - img_min_value) / (img_max_value - img_min_value) - # rerange to [min_value, max_value] - img = img * (self.max_value - self.min_value) + self.min_value - results['img'] = img - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(min_value={self.min_value}, max_value={self.max_value})' - return repr_str - - -@PIPELINES.register_module() -class CLAHE(object): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - """ - - def __init__(self, clip_limit=40.0, tile_grid_size=(8, 8)): - assert isinstance(clip_limit, (float, int)) - self.clip_limit = clip_limit - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - self.tile_grid_size = tile_grid_size - - def __call__(self, results): - """Call function to Use CLAHE method process images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - for i in range(results['img'].shape[2]): - results['img'][:, :, i] = mmcv.clahe( - np.array(results['img'][:, :, i], dtype=np.uint8), - self.clip_limit, self.tile_grid_size) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(clip_limit={self.clip_limit}, '\ - f'tile_grid_size={self.tile_grid_size})' - return repr_str - - -@PIPELINES.register_module() -class RandomCrop(object): - """Random crop the image & seg. - - Args: - crop_size (tuple): Expected size after cropping, (h, w). - cat_max_ratio (float): The maximum ratio that single category could - occupy. - """ - - def __init__(self, crop_size, cat_max_ratio=1., ignore_index=255): - assert crop_size[0] > 0 and crop_size[1] > 0 - self.crop_size = crop_size - self.cat_max_ratio = cat_max_ratio - self.ignore_index = ignore_index - - def get_crop_bbox(self, img): - """Randomly get a crop bounding box.""" - margin_h = max(img.shape[0] - self.crop_size[0], 0) - margin_w = max(img.shape[1] - self.crop_size[1], 0) - offset_h = np.random.randint(0, margin_h + 1) - offset_w = np.random.randint(0, margin_w + 1) - crop_y1, crop_y2 = offset_h, offset_h + self.crop_size[0] - crop_x1, crop_x2 = offset_w, offset_w + self.crop_size[1] - - return crop_y1, crop_y2, crop_x1, crop_x2 - - def crop(self, img, crop_bbox): - """Crop from ``img``""" - crop_y1, crop_y2, crop_x1, crop_x2 = crop_bbox - img = img[crop_y1:crop_y2, crop_x1:crop_x2, ...] - return img - - def __call__(self, results): - """Call function to randomly crop images, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Randomly cropped results, 'img_shape' key in result dict is - updated according to crop size. - """ - - img = results['img'] - crop_bbox = self.get_crop_bbox(img) - if self.cat_max_ratio < 1.: - # Repeat 10 times - for _ in range(10): - seg_temp = self.crop(results['gt_semantic_seg'], crop_bbox) - labels, cnt = np.unique(seg_temp, return_counts=True) - cnt = cnt[labels != self.ignore_index] - if len(cnt) > 1 and np.max(cnt) / np.sum( - cnt) < self.cat_max_ratio: - break - crop_bbox = self.get_crop_bbox(img) - - # crop the image - img = self.crop(img, crop_bbox) - img_shape = img.shape - results['img'] = img - results['img_shape'] = img_shape - - # crop semantic seg - for key in results.get('seg_fields', []): - results[key] = self.crop(results[key], crop_bbox) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(crop_size={self.crop_size})' - - -@PIPELINES.register_module() -class RandomRotate(object): - """Rotate the image & seg. - - Args: - prob (float): The rotation probability. - degree (float, tuple[float]): Range of degrees to select from. If - degree is a number instead of tuple like (min, max), - the range of degree will be (``-degree``, ``+degree``) - pad_val (float, optional): Padding value of image. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. Default: None. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. Default: False - """ - - def __init__(self, - prob, - degree, - pad_val=0, - seg_pad_val=255, - center=None, - auto_bound=False): - self.prob = prob - assert prob >= 0 and prob <= 1 - if isinstance(degree, (float, int)): - assert degree > 0, f'degree {degree} should be positive' - self.degree = (-degree, degree) - else: - self.degree = degree - assert len(self.degree) == 2, f'degree {self.degree} should be a ' \ - f'tuple of (min, max)' - self.pal_val = pad_val - self.seg_pad_val = seg_pad_val - self.center = center - self.auto_bound = auto_bound - - def __call__(self, results): - """Call function to rotate image, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Rotated results. - """ - - rotate = True if np.random.rand() < self.prob else False - degree = np.random.uniform(min(*self.degree), max(*self.degree)) - if rotate: - # rotate image - results['img'] = mmcv.imrotate( - results['img'], - angle=degree, - border_value=self.pal_val, - center=self.center, - auto_bound=self.auto_bound) - - # rotate segs - for key in results.get('seg_fields', []): - results[key] = mmcv.imrotate( - results[key], - angle=degree, - border_value=self.seg_pad_val, - center=self.center, - auto_bound=self.auto_bound, - interpolation='nearest') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' \ - f'degree={self.degree}, ' \ - f'pad_val={self.pal_val}, ' \ - f'seg_pad_val={self.seg_pad_val}, ' \ - f'center={self.center}, ' \ - f'auto_bound={self.auto_bound})' - return repr_str - - -@PIPELINES.register_module() -class RGB2Gray(object): - """Convert RGB image to grayscale image. - - This transform calculate the weighted mean of input image channels with - ``weights`` and then expand the channels to ``out_channels``. When - ``out_channels`` is None, the number of output channels is the same as - input channels. - - Args: - out_channels (int): Expected number of output channels after - transforming. Default: None. - weights (tuple[float]): The weights to calculate the weighted mean. - Default: (0.299, 0.587, 0.114). - """ - - def __init__(self, out_channels=None, weights=(0.299, 0.587, 0.114)): - assert out_channels is None or out_channels > 0 - self.out_channels = out_channels - assert isinstance(weights, tuple) - for item in weights: - assert isinstance(item, (float, int)) - self.weights = weights - - def __call__(self, results): - """Call function to convert RGB image to grayscale image. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with grayscale image. - """ - img = results['img'] - assert len(img.shape) == 3 - assert img.shape[2] == len(self.weights) - weights = np.array(self.weights).reshape((1, 1, -1)) - img = (img * weights).sum(2, keepdims=True) - if self.out_channels is None: - img = img.repeat(weights.shape[2], axis=2) - else: - img = img.repeat(self.out_channels, axis=2) - - results['img'] = img - results['img_shape'] = img.shape - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(out_channels={self.out_channels}, ' \ - f'weights={self.weights})' - return repr_str - - -@PIPELINES.register_module() -class AdjustGamma(object): - """Using gamma correction to process the image. - - Args: - gamma (float or int): Gamma value used in gamma correction. - Default: 1.0. - """ - - def __init__(self, gamma=1.0): - assert isinstance(gamma, float) or isinstance(gamma, int) - assert gamma > 0 - self.gamma = gamma - inv_gamma = 1.0 / gamma - self.table = np.array([(i / 255.0)**inv_gamma * 255 - for i in np.arange(256)]).astype('uint8') - - def __call__(self, results): - """Call function to process the image with gamma correction. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - results['img'] = mmcv.lut_transform( - np.array(results['img'], dtype=np.uint8), self.table) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(gamma={self.gamma})' - - -@PIPELINES.register_module() -class SegRescale(object): - """Rescale semantic segmentation maps. - - Args: - scale_factor (float): The scale factor of the final output. - """ - - def __init__(self, scale_factor=1): - self.scale_factor = scale_factor - - def __call__(self, results): - """Call function to scale the semantic segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with semantic segmentation map scaled. - """ - for key in results.get('seg_fields', []): - if self.scale_factor != 1: - results[key] = mmcv.imrescale( - results[key], self.scale_factor, interpolation='nearest') - return results - - def __repr__(self): - return self.__class__.__name__ + f'(scale_factor={self.scale_factor})' - - -@PIPELINES.register_module() -class PhotoMetricDistortion(object): - """Apply photometric distortion to image sequentially, every transformation - is applied with a probability of 0.5. The position of random contrast is in - second or second to last. - - 1. random brightness - 2. random contrast (mode 0) - 3. convert color from BGR to HSV - 4. random saturation - 5. random hue - 6. convert color from HSV to BGR - 7. random contrast (mode 1) - - Args: - brightness_delta (int): delta of brightness. - contrast_range (tuple): range of contrast. - saturation_range (tuple): range of saturation. - hue_delta (int): delta of hue. - """ - - def __init__(self, - brightness_delta=32, - contrast_range=(0.5, 1.5), - saturation_range=(0.5, 1.5), - hue_delta=18): - self.brightness_delta = brightness_delta - self.contrast_lower, self.contrast_upper = contrast_range - self.saturation_lower, self.saturation_upper = saturation_range - self.hue_delta = hue_delta - - def convert(self, img, alpha=1, beta=0): - """Multiple with alpha and add beat with clip.""" - img = img.astype(np.float32) * alpha + beta - img = np.clip(img, 0, 255) - return img.astype(np.uint8) - - def brightness(self, img): - """Brightness distortion.""" - if random.randint(2): - return self.convert( - img, - beta=random.uniform(-self.brightness_delta, - self.brightness_delta)) - return img - - def contrast(self, img): - """Contrast distortion.""" - if random.randint(2): - return self.convert( - img, - alpha=random.uniform(self.contrast_lower, self.contrast_upper)) - return img - - def saturation(self, img): - """Saturation distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, 1] = self.convert( - img[:, :, 1], - alpha=random.uniform(self.saturation_lower, - self.saturation_upper)) - img = mmcv.hsv2bgr(img) - return img - - def hue(self, img): - """Hue distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, - 0] = (img[:, :, 0].astype(int) + - random.randint(-self.hue_delta, self.hue_delta)) % 180 - img = mmcv.hsv2bgr(img) - return img - - def __call__(self, results): - """Call function to perform photometric distortion on images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images distorted. - """ - - img = results['img'] - # random brightness - img = self.brightness(img) - - # mode == 0 --> do random contrast first - # mode == 1 --> do random contrast last - mode = random.randint(2) - if mode == 1: - img = self.contrast(img) - - # random saturation - img = self.saturation(img) - - # random hue - img = self.hue(img) - - # random contrast - if mode == 0: - img = self.contrast(img) - - results['img'] = img - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(brightness_delta={self.brightness_delta}, ' - f'contrast_range=({self.contrast_lower}, ' - f'{self.contrast_upper}), ' - f'saturation_range=({self.saturation_lower}, ' - f'{self.saturation_upper}), ' - f'hue_delta={self.hue_delta})') - return repr_str - - -@PIPELINES.register_module() -class RandomCutOut(object): - """CutOut operation. - - Randomly drop some regions of image used in - `Cutout `_. - Args: - prob (float): cutout probability. - n_holes (int | tuple[int, int]): Number of regions to be dropped. - If it is given as a list, number of holes will be randomly - selected from the closed interval [`n_holes[0]`, `n_holes[1]`]. - cutout_shape (tuple[int, int] | list[tuple[int, int]]): The candidate - shape of dropped regions. It can be `tuple[int, int]` to use a - fixed cutout shape, or `list[tuple[int, int]]` to randomly choose - shape from the list. - cutout_ratio (tuple[float, float] | list[tuple[float, float]]): The - candidate ratio of dropped regions. It can be `tuple[float, float]` - to use a fixed ratio or `list[tuple[float, float]]` to randomly - choose ratio from the list. Please note that `cutout_shape` - and `cutout_ratio` cannot be both given at the same time. - fill_in (tuple[float, float, float] | tuple[int, int, int]): The value - of pixel to fill in the dropped regions. Default: (0, 0, 0). - seg_fill_in (int): The labels of pixel to fill in the dropped regions. - If seg_fill_in is None, skip. Default: None. - """ - - def __init__(self, - prob, - n_holes, - cutout_shape=None, - cutout_ratio=None, - fill_in=(0, 0, 0), - seg_fill_in=None): - - assert 0 <= prob and prob <= 1 - assert (cutout_shape is None) ^ (cutout_ratio is None), \ - 'Either cutout_shape or cutout_ratio should be specified.' - assert (isinstance(cutout_shape, (list, tuple)) - or isinstance(cutout_ratio, (list, tuple))) - if isinstance(n_holes, tuple): - assert len(n_holes) == 2 and 0 <= n_holes[0] < n_holes[1] - else: - n_holes = (n_holes, n_holes) - if seg_fill_in is not None: - assert (isinstance(seg_fill_in, int) and 0 <= seg_fill_in - and seg_fill_in <= 255) - self.prob = prob - self.n_holes = n_holes - self.fill_in = fill_in - self.seg_fill_in = seg_fill_in - self.with_ratio = cutout_ratio is not None - self.candidates = cutout_ratio if self.with_ratio else cutout_shape - if not isinstance(self.candidates, list): - self.candidates = [self.candidates] - - def __call__(self, results): - """Call function to drop some regions of image.""" - cutout = True if np.random.rand() < self.prob else False - if cutout: - h, w, c = results['img'].shape - n_holes = np.random.randint(self.n_holes[0], self.n_holes[1] + 1) - for _ in range(n_holes): - x1 = np.random.randint(0, w) - y1 = np.random.randint(0, h) - index = np.random.randint(0, len(self.candidates)) - if not self.with_ratio: - cutout_w, cutout_h = self.candidates[index] - else: - cutout_w = int(self.candidates[index][0] * w) - cutout_h = int(self.candidates[index][1] * h) - - x2 = np.clip(x1 + cutout_w, 0, w) - y2 = np.clip(y1 + cutout_h, 0, h) - results['img'][y1:y2, x1:x2, :] = self.fill_in - - if self.seg_fill_in is not None: - for key in results.get('seg_fields', []): - results[key][y1:y2, x1:x2] = self.seg_fill_in - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' - repr_str += f'n_holes={self.n_holes}, ' - repr_str += (f'cutout_ratio={self.candidates}, ' if self.with_ratio - else f'cutout_shape={self.candidates}, ') - repr_str += f'fill_in={self.fill_in}, ' - repr_str += f'seg_fill_in={self.seg_fill_in})' - return repr_str - - -@PIPELINES.register_module() -class RandomMosaic(object): - """Mosaic augmentation. Given 4 images, mosaic transform combines them into - one output image. The output image is composed of the parts from each sub- - image. - - .. code:: text - - mosaic transform - center_x - +------------------------------+ - | pad | pad | - | +-----------+ | - | | | | - | | image1 |--------+ | - | | | | | - | | | image2 | | - center_y |----+-------------+-----------| - | | cropped | | - |pad | image3 | image4 | - | | | | - +----|-------------+-----------+ - | | - +-------------+ - - The mosaic transform steps are as follows: - 1. Choose the mosaic center as the intersections of 4 images - 2. Get the left top image according to the index, and randomly - sample another 3 images from the custom dataset. - 3. Sub image will be cropped if image is larger than mosaic patch - - Args: - prob (float): mosaic probability. - img_scale (Sequence[int]): Image size after mosaic pipeline of - a single image. The size of the output image is four times - that of a single image. The output image comprises 4 single images. - Default: (640, 640). - center_ratio_range (Sequence[float]): Center ratio range of mosaic - output. Default: (0.5, 1.5). - pad_val (int): Pad value. Default: 0. - seg_pad_val (int): Pad value of segmentation map. Default: 255. - """ - - def __init__(self, - prob, - img_scale=(640, 640), - center_ratio_range=(0.5, 1.5), - pad_val=0, - seg_pad_val=255): - assert 0 <= prob and prob <= 1 - assert isinstance(img_scale, tuple) - self.prob = prob - self.img_scale = img_scale - self.center_ratio_range = center_ratio_range - self.pad_val = pad_val - self.seg_pad_val = seg_pad_val - - def __call__(self, results): - """Call function to make a mosaic of image. - - Args: - results (dict): Result dict. - - Returns: - dict: Result dict with mosaic transformed. - """ - mosaic = True if np.random.rand() < self.prob else False - if mosaic: - results = self._mosaic_transform_img(results) - results = self._mosaic_transform_seg(results) - return results - - def get_indexes(self, dataset): - """Call function to collect indexes. - - Args: - dataset (:obj:`MultiImageMixDataset`): The dataset. - - Returns: - list: indexes. - """ - - indexes = [random.randint(0, len(dataset)) for _ in range(3)] - return indexes - - def _mosaic_transform_img(self, results): - """Mosaic transform function. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - if len(results['img'].shape) == 3: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2), 3), - self.pad_val, - dtype=results['img'].dtype) - else: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.pad_val, - dtype=results['img'].dtype) - - # mosaic center x, y - self.center_x = int( - random.uniform(*self.center_ratio_range) * self.img_scale[1]) - self.center_y = int( - random.uniform(*self.center_ratio_range) * self.img_scale[0]) - center_position = (self.center_x, self.center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - result_patch = copy.deepcopy(results) - else: - result_patch = copy.deepcopy(results['mix_results'][i - 1]) - - img_i = result_patch['img'] - h_i, w_i = img_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - img_i = mmcv.imresize( - img_i, (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i))) - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, img_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_img[y1_p:y2_p, x1_p:x2_p] = img_i[y1_c:y2_c, x1_c:x2_c] - - results['img'] = mosaic_img - results['img_shape'] = mosaic_img.shape - results['ori_shape'] = mosaic_img.shape - - return results - - def _mosaic_transform_seg(self, results): - """Mosaic transform function for label annotations. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - for key in results.get('seg_fields', []): - mosaic_seg = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.seg_pad_val, - dtype=results[key].dtype) - - # mosaic center x, y - center_position = (self.center_x, self.center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - result_patch = copy.deepcopy(results) - else: - result_patch = copy.deepcopy(results['mix_results'][i - 1]) - - gt_seg_i = result_patch[key] - h_i, w_i = gt_seg_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - gt_seg_i = mmcv.imresize( - gt_seg_i, - (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i)), - interpolation='nearest') - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, gt_seg_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_seg[y1_p:y2_p, x1_p:x2_p] = gt_seg_i[y1_c:y2_c, - x1_c:x2_c] - - results[key] = mosaic_seg - - return results - - def _mosaic_combine(self, loc, center_position_xy, img_shape_wh): - """Calculate global coordinate of mosaic image and local coordinate of - cropped sub-image. - - Args: - loc (str): Index for the sub-image, loc in ('top_left', - 'top_right', 'bottom_left', 'bottom_right'). - center_position_xy (Sequence[float]): Mixing center for 4 images, - (x, y). - img_shape_wh (Sequence[int]): Width and height of sub-image - - Returns: - tuple[tuple[float]]: Corresponding coordinate of pasting and - cropping - - paste_coord (tuple): paste corner coordinate in mosaic image. - - crop_coord (tuple): crop corner coordinate in mosaic image. - """ - - assert loc in ('top_left', 'top_right', 'bottom_left', 'bottom_right') - if loc == 'top_left': - # index0 to top left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - center_position_xy[0], \ - center_position_xy[1] - crop_coord = img_shape_wh[0] - (x2 - x1), img_shape_wh[1] - ( - y2 - y1), img_shape_wh[0], img_shape_wh[1] - - elif loc == 'top_right': - # index1 to top right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - center_position_xy[1] - crop_coord = 0, img_shape_wh[1] - (y2 - y1), min( - img_shape_wh[0], x2 - x1), img_shape_wh[1] - - elif loc == 'bottom_left': - # index2 to bottom left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - center_position_xy[1], \ - center_position_xy[0], \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = img_shape_wh[0] - (x2 - x1), 0, img_shape_wh[0], min( - y2 - y1, img_shape_wh[1]) - - else: - # index3 to bottom right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - center_position_xy[1], \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = 0, 0, min(img_shape_wh[0], - x2 - x1), min(y2 - y1, img_shape_wh[1]) - - paste_coord = x1, y1, x2, y2 - return paste_coord, crop_coord - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' - repr_str += f'img_scale={self.img_scale}, ' - repr_str += f'center_ratio_range={self.center_ratio_range}, ' - repr_str += f'pad_val={self.pad_val}, ' - repr_str += f'seg_pad_val={self.pad_val})' - return repr_str diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/samplers/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/samplers/__init__.py deleted file mode 100644 index da09effaf..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/samplers/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .distributed_sampler import DistributedSampler - -__all__ = ['DistributedSampler'] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/samplers/distributed_sampler.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/samplers/distributed_sampler.py deleted file mode 100644 index d1a13c716..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/samplers/distributed_sampler.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from __future__ import division -from typing import Iterator, Optional - -import torch -from torch.utils.data import Dataset -from torch.utils.data import DistributedSampler as _DistributedSampler - -from mmseg.core.utils import sync_random_seed - - -class DistributedSampler(_DistributedSampler): - """DistributedSampler inheriting from - `torch.utils.data.DistributedSampler`. - - Args: - datasets (Dataset): the dataset will be loaded. - num_replicas (int, optional): Number of processes participating in - distributed training. By default, world_size is retrieved from the - current distributed group. - rank (int, optional): Rank of the current process within num_replicas. - By default, rank is retrieved from the current distributed group. - shuffle (bool): If True (default), sampler will shuffle the indices. - seed (int): random seed used to shuffle the sampler if - :attr:`shuffle=True`. This number should be identical across all - processes in the distributed group. Default: ``0``. - """ - - def __init__(self, - dataset: Dataset, - num_replicas: Optional[int] = None, - rank: Optional[int] = None, - shuffle: bool = True, - seed=0) -> None: - super().__init__( - dataset, num_replicas=num_replicas, rank=rank, shuffle=shuffle) - - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - - def __iter__(self) -> Iterator: - """ - Yields: - Iterator: iterator of indices for rank. - """ - # deterministically shuffle based on epoch - if self.shuffle: - g = torch.Generator() - # When :attr:`shuffle=True`, this ensures all replicas - # use a different random ordering for each epoch. - # Otherwise, the next iteration of this sampler will - # yield the same ordering. - g.manual_seed(self.epoch + self.seed) - indices = torch.randperm(len(self.dataset), generator=g).tolist() - else: - indices = torch.arange(len(self.dataset)).tolist() - - # add extra samples to make it evenly divisible - indices += indices[:(self.total_size - len(indices))] - assert len(indices) == self.total_size - - # subsample - indices = indices[self.rank:self.total_size:self.num_replicas] - assert len(indices) == self.num_samples - - return iter(indices) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/voc.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/voc.py deleted file mode 100644 index 9eecc344f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/datasets/voc.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class PascalVOCDataset(CustomDataset): - """Pascal VOC dataset. - - Args: - split (str): Split txt file for Pascal VOC. - """ - - CLASSES = ('background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', - 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', - 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', - 'train', 'tvmonitor') - - PALETTE = [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], - [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], - [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], - [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], - [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] - - def __init__(self, split, **kwargs): - - if "img_dir" in kwargs: - image_dir = kwargs["img_dir"] - if not osp.join(kwargs['data_root'], image_dir): - image_dir = "images" - if osp.join(kwargs['data_root'], image_dir): - kwargs["img_dir"] = image_dir - - super(PascalVOCDataset, self).__init__( - img_suffix='.jpg', seg_map_suffix='.png', split=split, **kwargs) - - assert osp.exists(self.img_dir) and self.split is not None diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/__init__.py deleted file mode 100644 index 87d8108e3..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .backbones import * # noqa: F401,F403 -from .builder import (BACKBONES, HEADS, LOSSES, SEGMENTORS, build_backbone, - build_head, build_loss, build_segmentor) -from .decode_heads import * # noqa: F401,F403 -from .losses import * # noqa: F401,F403 -from .necks import * # noqa: F401,F403 -from .segmentors import * # noqa: F401,F403 - -__all__ = [ - 'BACKBONES', 'HEADS', 'LOSSES', 'SEGMENTORS', 'build_backbone', - 'build_head', 'build_loss', 'build_segmentor' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/__init__.py deleted file mode 100644 index f842617d8..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .resnest import ResNeSt -from .resnet import ResNet, ResNetV1c, ResNetV1d -from .resnext import ResNeXt diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/resnest.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/resnest.py deleted file mode 100644 index 91952c2ca..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/resnest.py +++ /dev/null @@ -1,318 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import torch -import torch.nn as nn -import torch.nn.functional as F -import torch.utils.checkpoint as cp -from mmcv.cnn import build_conv_layer, build_norm_layer - -from ..builder import BACKBONES -from ..utils import ResLayer -from .resnet import Bottleneck as _Bottleneck -from .resnet import ResNetV1d - - -class RSoftmax(nn.Module): - """Radix Softmax module in ``SplitAttentionConv2d``. - - Args: - radix (int): Radix of input. - groups (int): Groups of input. - """ - - def __init__(self, radix, groups): - super().__init__() - self.radix = radix - self.groups = groups - - def forward(self, x): - batch = x.size(0) - if self.radix > 1: - x = x.view(batch, self.groups, self.radix, -1).transpose(1, 2) - x = F.softmax(x, dim=1) - x = x.reshape(batch, -1) - else: - x = torch.sigmoid(x) - return x - - -class SplitAttentionConv2d(nn.Module): - """Split-Attention Conv2d in ResNeSt. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int | tuple[int]): Same as nn.Conv2d. - stride (int | tuple[int]): Same as nn.Conv2d. - padding (int | tuple[int]): Same as nn.Conv2d. - dilation (int | tuple[int]): Same as nn.Conv2d. - groups (int): Same as nn.Conv2d. - radix (int): Radix of SpltAtConv2d. Default: 2 - reduction_factor (int): Reduction factor of inter_channels. Default: 4. - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - dcn (dict): Config dict for DCN. Default: None. - """ - - def __init__(self, - in_channels, - channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - radix=2, - reduction_factor=4, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None): - super(SplitAttentionConv2d, self).__init__() - inter_channels = max(in_channels * radix // reduction_factor, 32) - self.radix = radix - self.groups = groups - self.channels = channels - self.with_dcn = dcn is not None - self.dcn = dcn - fallback_on_stride = False - if self.with_dcn: - fallback_on_stride = self.dcn.pop('fallback_on_stride', False) - if self.with_dcn and not fallback_on_stride: - assert conv_cfg is None, 'conv_cfg must be None for DCN' - conv_cfg = dcn - self.conv = build_conv_layer( - conv_cfg, - in_channels, - channels * radix, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups * radix, - bias=False) - self.norm0_name, norm0 = build_norm_layer( - norm_cfg, channels * radix, postfix=0) - self.add_module(self.norm0_name, norm0) - self.relu = nn.ReLU(inplace=True) - self.fc1 = build_conv_layer( - None, channels, inter_channels, 1, groups=self.groups) - self.norm1_name, norm1 = build_norm_layer( - norm_cfg, inter_channels, postfix=1) - self.add_module(self.norm1_name, norm1) - self.fc2 = build_conv_layer( - None, inter_channels, channels * radix, 1, groups=self.groups) - self.rsoftmax = RSoftmax(radix, groups) - - @property - def norm0(self): - """nn.Module: the normalization layer named "norm0" """ - return getattr(self, self.norm0_name) - - @property - def norm1(self): - """nn.Module: the normalization layer named "norm1" """ - return getattr(self, self.norm1_name) - - def forward(self, x): - x = self.conv(x) - x = self.norm0(x) - x = self.relu(x) - - batch, rchannel = x.shape[:2] - batch = x.size(0) - if self.radix > 1: - splits = x.view(batch, self.radix, -1, *x.shape[2:]) - gap = splits.sum(dim=1) - else: - gap = x - gap = F.adaptive_avg_pool2d(gap, 1) - gap = self.fc1(gap) - - gap = self.norm1(gap) - gap = self.relu(gap) - - atten = self.fc2(gap) - atten = self.rsoftmax(atten).view(batch, -1, 1, 1) - - if self.radix > 1: - attens = atten.view(batch, self.radix, -1, *atten.shape[2:]) - out = torch.sum(attens * splits, dim=1) - else: - out = atten * x - return out.contiguous() - - -class Bottleneck(_Bottleneck): - """Bottleneck block for ResNeSt. - - Args: - inplane (int): Input planes of this block. - planes (int): Middle planes of this block. - groups (int): Groups of conv2. - width_per_group (int): Width per group of conv2. 64x4d indicates - ``groups=64, width_per_group=4`` and 32x8d indicates - ``groups=32, width_per_group=8``. - radix (int): Radix of SpltAtConv2d. Default: 2 - reduction_factor (int): Reduction factor of inter_channels in - SplitAttentionConv2d. Default: 4. - avg_down_stride (bool): Whether to use average pool for stride in - Bottleneck. Default: True. - kwargs (dict): Key word arguments for base class. - """ - expansion = 4 - - def __init__(self, - inplanes, - planes, - groups=1, - base_width=4, - base_channels=64, - radix=2, - reduction_factor=4, - avg_down_stride=True, - **kwargs): - """Bottleneck block for ResNeSt.""" - super(Bottleneck, self).__init__(inplanes, planes, **kwargs) - - if groups == 1: - width = self.planes - else: - width = math.floor(self.planes * - (base_width / base_channels)) * groups - - self.avg_down_stride = avg_down_stride and self.conv2_stride > 1 - - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, width, postfix=1) - self.norm3_name, norm3 = build_norm_layer( - self.norm_cfg, self.planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - self.conv_cfg, - self.inplanes, - width, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - self.with_modulated_dcn = False - self.conv2 = SplitAttentionConv2d( - width, - width, - kernel_size=3, - stride=1 if self.avg_down_stride else self.conv2_stride, - padding=self.dilation, - dilation=self.dilation, - groups=groups, - radix=radix, - reduction_factor=reduction_factor, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - dcn=self.dcn) - delattr(self, self.norm2_name) - - if self.avg_down_stride: - self.avd_layer = nn.AvgPool2d(3, self.conv2_stride, padding=1) - - self.conv3 = build_conv_layer( - self.conv_cfg, - width, - self.planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - def forward(self, x): - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv1_plugin_names) - - out = self.conv2(out) - - if self.avg_down_stride: - out = self.avd_layer(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv2_plugin_names) - - out = self.conv3(out) - out = self.norm3(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv3_plugin_names) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -@BACKBONES.register_module() -class ResNeSt(ResNetV1d): - """ResNeSt backbone. - - This backbone is the implementation of `ResNeSt: - Split-Attention Networks `_. - - Args: - groups (int): Number of groups of Bottleneck. Default: 1 - base_width (int): Base width of Bottleneck. Default: 4 - radix (int): Radix of SpltAtConv2d. Default: 2 - reduction_factor (int): Reduction factor of inter_channels in - SplitAttentionConv2d. Default: 4. - avg_down_stride (bool): Whether to use average pool for stride in - Bottleneck. Default: True. - kwargs (dict): Keyword arguments for ResNet. - """ - - arch_settings = { - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)), - 200: (Bottleneck, (3, 24, 36, 3)) - } - - def __init__(self, - groups=1, - base_width=4, - radix=2, - reduction_factor=4, - avg_down_stride=True, - **kwargs): - self.groups = groups - self.base_width = base_width - self.radix = radix - self.reduction_factor = reduction_factor - self.avg_down_stride = avg_down_stride - super(ResNeSt, self).__init__(**kwargs) - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``.""" - return ResLayer( - groups=self.groups, - base_width=self.base_width, - base_channels=self.base_channels, - radix=self.radix, - reduction_factor=self.reduction_factor, - avg_down_stride=self.avg_down_stride, - **kwargs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/resnet.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/resnet.py deleted file mode 100644 index e8b961d5f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/resnet.py +++ /dev/null @@ -1,714 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.utils.checkpoint as cp -from mmcv.cnn import build_conv_layer, build_norm_layer, build_plugin_layer -from mmcv.runner import BaseModule -from mmcv.utils.parrots_wrapper import _BatchNorm - -from ..builder import BACKBONES -from ..utils import ResLayer - - -class BasicBlock(BaseModule): - """Basic block for ResNet.""" - - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(BasicBlock, self).__init__(init_cfg) - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - 3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - self.add_module(self.norm1_name, norm1) - self.conv2 = build_conv_layer( - conv_cfg, planes, planes, 3, padding=1, bias=False) - self.add_module(self.norm2_name, norm2) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.norm2(out) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -class Bottleneck(BaseModule): - """Bottleneck block for ResNet. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if it is - "caffe", the stride-two layer is the first 1x1 conv layer. - """ - - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(Bottleneck, self).__init__(init_cfg) - assert style in ['pytorch', 'caffe'] - assert dcn is None or isinstance(dcn, dict) - assert plugins is None or isinstance(plugins, list) - if plugins is not None: - allowed_position = ['after_conv1', 'after_conv2', 'after_conv3'] - assert all(p['position'] in allowed_position for p in plugins) - - self.inplanes = inplanes - self.planes = planes - self.stride = stride - self.dilation = dilation - self.style = style - self.with_cp = with_cp - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.dcn = dcn - self.with_dcn = dcn is not None - self.plugins = plugins - self.with_plugins = plugins is not None - - if self.with_plugins: - # collect plugins for conv1/conv2/conv3 - self.after_conv1_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv1' - ] - self.after_conv2_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv2' - ] - self.after_conv3_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv3' - ] - - if self.style == 'pytorch': - self.conv1_stride = 1 - self.conv2_stride = stride - else: - self.conv1_stride = stride - self.conv2_stride = 1 - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - self.norm3_name, norm3 = build_norm_layer( - norm_cfg, planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - fallback_on_stride = False - if self.with_dcn: - fallback_on_stride = dcn.pop('fallback_on_stride', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = build_conv_layer( - conv_cfg, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - else: - assert self.conv_cfg is None, 'conv_cfg must be None for DCN' - self.conv2 = build_conv_layer( - dcn, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.add_module(self.norm2_name, norm2) - self.conv3 = build_conv_layer( - conv_cfg, - planes, - planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - - if self.with_plugins: - self.after_conv1_plugin_names = self.make_block_plugins( - planes, self.after_conv1_plugins) - self.after_conv2_plugin_names = self.make_block_plugins( - planes, self.after_conv2_plugins) - self.after_conv3_plugin_names = self.make_block_plugins( - planes * self.expansion, self.after_conv3_plugins) - - def make_block_plugins(self, in_channels, plugins): - """make plugins for block. - - Args: - in_channels (int): Input channels of plugin. - plugins (list[dict]): List of plugins cfg to build. - - Returns: - list[str]: List of the names of plugin. - """ - assert isinstance(plugins, list) - plugin_names = [] - for plugin in plugins: - plugin = plugin.copy() - name, layer = build_plugin_layer( - plugin, - in_channels=in_channels, - postfix=plugin.pop('postfix', '')) - assert not hasattr(self, name), f'duplicate plugin {name}' - self.add_module(name, layer) - plugin_names.append(name) - return plugin_names - - def forward_plugin(self, x, plugin_names): - """Forward function for plugins.""" - out = x - for name in plugin_names: - out = getattr(self, name)(x) - return out - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - @property - def norm3(self): - """nn.Module: normalization layer after the third convolution layer""" - return getattr(self, self.norm3_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv1_plugin_names) - - out = self.conv2(out) - out = self.norm2(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv2_plugin_names) - - out = self.conv3(out) - out = self.norm3(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv3_plugin_names) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -@BACKBONES.register_module() -class ResNet(BaseModule): - """ResNet backbone. - - This backbone is the improved implementation of `Deep Residual Learning - for Image Recognition `_. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - in_channels (int): Number of input image channels. Default: 3. - stem_channels (int): Number of stem channels. Default: 64. - base_channels (int): Number of base channels of res layer. Default: 64. - num_stages (int): Resnet stages, normally 4. Default: 4. - strides (Sequence[int]): Strides of the first block of each stage. - Default: (1, 2, 2, 2). - dilations (Sequence[int]): Dilation of each stage. - Default: (1, 1, 1, 1). - out_indices (Sequence[int]): Output from which stages. - Default: (0, 1, 2, 3). - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. Default: 'pytorch'. - deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv. - Default: False. - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False. - frozen_stages (int): Stages to be frozen (stop grad and set eval mode). - -1 means not freezing any parameters. Default: -1. - conv_cfg (dict | None): Dictionary to construct and config conv layer. - When conv_cfg is None, cfg will be set to dict(type='Conv2d'). - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN', requires_grad=True). - norm_eval (bool): Whether to set norm layers to eval mode, namely, - freeze running stats (mean and var). Note: Effect on Batch Norm - and its variants only. Default: False. - dcn (dict | None): Dictionary to construct and config DCN conv layer. - When dcn is not None, conv_cfg must be None. Default: None. - stage_with_dcn (Sequence[bool]): Whether to set DCN conv for each - stage. The length of stage_with_dcn is equal to num_stages. - Default: (False, False, False, False). - plugins (list[dict]): List of plugins for stages, each dict contains: - - - cfg (dict, required): Cfg dict to build plugin. - - - position (str, required): Position inside block to insert plugin, - options: 'after_conv1', 'after_conv2', 'after_conv3'. - - - stages (tuple[bool], optional): Stages to apply plugin, length - should be same as 'num_stages'. - Default: None. - multi_grid (Sequence[int]|None): Multi grid dilation rates of last - stage. Default: None. - contract_dilation (bool): Whether contract first dilation of each layer - Default: False. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - zero_init_residual (bool): Whether to use zero init for last norm layer - in resblocks to let them behave as identity. Default: True. - pretrained (str, optional): model pretrained path. Default: None. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Example: - >>> from mmseg.models import ResNet - >>> import torch - >>> self = ResNet(depth=18) - >>> self.eval() - >>> inputs = torch.rand(1, 3, 32, 32) - >>> level_outputs = self.forward(inputs) - >>> for level_out in level_outputs: - ... print(tuple(level_out.shape)) - (1, 64, 8, 8) - (1, 128, 4, 4) - (1, 256, 2, 2) - (1, 512, 1, 1) - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - in_channels=3, - stem_channels=64, - base_channels=64, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - deep_stem=False, - avg_down=False, - frozen_stages=-1, - conv_cfg=None, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=False, - dcn=None, - stage_with_dcn=(False, False, False, False), - plugins=None, - multi_grid=None, - contract_dilation=False, - with_cp=False, - zero_init_residual=True, - pretrained=None, - init_cfg=None): - super(ResNet, self).__init__(init_cfg) - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - - self.pretrained = pretrained - self.zero_init_residual = zero_init_residual - block_init_cfg = None - assert not (init_cfg and pretrained), \ - 'init_cfg and pretrained cannot be setting at the same time' - if isinstance(pretrained, str): - warnings.warn('DeprecationWarning: pretrained is a deprecated, ' - 'please use "init_cfg" instead') - self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - elif pretrained is None: - if init_cfg is None: - self.init_cfg = [ - dict(type='Kaiming', layer='Conv2d'), - dict( - type='Constant', - val=1, - layer=['_BatchNorm', 'GroupNorm']) - ] - block = self.arch_settings[depth][0] - if self.zero_init_residual: - if block is BasicBlock: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm2')) - elif block is Bottleneck: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm3')) - else: - raise TypeError('pretrained must be a str or None') - - self.depth = depth - self.stem_channels = stem_channels - self.base_channels = base_channels - self.num_stages = num_stages - assert num_stages >= 1 and num_stages <= 4 - self.strides = strides - self.dilations = dilations - assert len(strides) == len(dilations) == num_stages - self.out_indices = out_indices - assert max(out_indices) < num_stages - self.style = style - self.deep_stem = deep_stem - self.avg_down = avg_down - self.frozen_stages = frozen_stages - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.with_cp = with_cp - self.norm_eval = norm_eval - self.dcn = dcn - self.stage_with_dcn = stage_with_dcn - if dcn is not None: - assert len(stage_with_dcn) == num_stages - self.plugins = plugins - self.multi_grid = multi_grid - self.contract_dilation = contract_dilation - self.block, stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - self.inplanes = stem_channels - - self._make_stem_layer(in_channels, stem_channels) - - self.res_layers = [] - for i, num_blocks in enumerate(self.stage_blocks): - stride = strides[i] - dilation = dilations[i] - dcn = self.dcn if self.stage_with_dcn[i] else None - if plugins is not None: - stage_plugins = self.make_stage_plugins(plugins, i) - else: - stage_plugins = None - # multi grid is applied to last layer only - stage_multi_grid = multi_grid if i == len( - self.stage_blocks) - 1 else None - planes = base_channels * 2**i - res_layer = self.make_res_layer( - block=self.block, - inplanes=self.inplanes, - planes=planes, - num_blocks=num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - avg_down=self.avg_down, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - dcn=dcn, - plugins=stage_plugins, - multi_grid=stage_multi_grid, - contract_dilation=contract_dilation, - init_cfg=block_init_cfg) - self.inplanes = planes * self.block.expansion - layer_name = f'layer{i+1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self._freeze_stages() - - self.feat_dim = self.block.expansion * base_channels * 2**( - len(self.stage_blocks) - 1) - - def make_stage_plugins(self, plugins, stage_idx): - """make plugins for ResNet 'stage_idx'th stage . - - Currently we support to insert 'context_block', - 'empirical_attention_block', 'nonlocal_block' into the backbone like - ResNet/ResNeXt. They could be inserted after conv1/conv2/conv3 of - Bottleneck. - - An example of plugins format could be : - >>> plugins=[ - ... dict(cfg=dict(type='xxx', arg1='xxx'), - ... stages=(False, True, True, True), - ... position='after_conv2'), - ... dict(cfg=dict(type='yyy'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='1'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='2'), - ... stages=(True, True, True, True), - ... position='after_conv3') - ... ] - >>> self = ResNet(depth=18) - >>> stage_plugins = self.make_stage_plugins(plugins, 0) - >>> assert len(stage_plugins) == 3 - - Suppose 'stage_idx=0', the structure of blocks in the stage would be: - conv1-> conv2->conv3->yyy->zzz1->zzz2 - Suppose 'stage_idx=1', the structure of blocks in the stage would be: - conv1-> conv2->xxx->conv3->yyy->zzz1->zzz2 - - If stages is missing, the plugin would be applied to all stages. - - Args: - plugins (list[dict]): List of plugins cfg to build. The postfix is - required if multiple same type plugins are inserted. - stage_idx (int): Index of stage to build - - Returns: - list[dict]: Plugins for current stage - """ - stage_plugins = [] - for plugin in plugins: - plugin = plugin.copy() - stages = plugin.pop('stages', None) - assert stages is None or len(stages) == self.num_stages - # whether to insert plugin into current stage - if stages is None or stages[stage_idx]: - stage_plugins.append(plugin) - - return stage_plugins - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``.""" - return ResLayer(**kwargs) - - @property - def norm1(self): - """nn.Module: the normalization layer named "norm1" """ - return getattr(self, self.norm1_name) - - def _make_stem_layer(self, in_channels, stem_channels): - """Make stem layer for ResNet.""" - if self.deep_stem: - self.stem = nn.Sequential( - build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels // 2, - kernel_size=3, - stride=2, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels // 2, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels)[1], - nn.ReLU(inplace=True)) - else: - self.conv1 = build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels, - kernel_size=7, - stride=2, - padding=3, - bias=False) - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, stem_channels, postfix=1) - self.add_module(self.norm1_name, norm1) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - def _freeze_stages(self): - """Freeze stages param and norm stats.""" - if self.frozen_stages >= 0: - if self.deep_stem: - self.stem.eval() - for param in self.stem.parameters(): - param.requires_grad = False - else: - self.norm1.eval() - for m in [self.conv1, self.norm1]: - for param in m.parameters(): - param.requires_grad = False - - for i in range(1, self.frozen_stages + 1): - m = getattr(self, f'layer{i}') - m.eval() - for param in m.parameters(): - param.requires_grad = False - - def forward(self, x): - """Forward function.""" - if self.deep_stem: - x = self.stem(x) - else: - x = self.conv1(x) - x = self.norm1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - return tuple(outs) - - def train(self, mode=True): - """Convert the model into training mode while keep normalization layer - freezed.""" - super(ResNet, self).train(mode) - self._freeze_stages() - if mode and self.norm_eval: - for m in self.modules(): - # trick: eval have effect on BatchNorm only - if isinstance(m, _BatchNorm): - m.eval() - - -@BACKBONES.register_module() -class ResNetV1c(ResNet): - """ResNetV1c variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1c replaces the 7x7 conv in - the input stem with three 3x3 convs. For more details please refer to `Bag - of Tricks for Image Classification with Convolutional Neural Networks - `_. - """ - - def __init__(self, **kwargs): - super(ResNetV1c, self).__init__( - deep_stem=True, avg_down=False, **kwargs) - - -@BACKBONES.register_module() -class ResNetV1d(ResNet): - """ResNetV1d variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1d replaces the 7x7 conv in - the input stem with three 3x3 convs. And in the downsampling block, a 2x2 - avg_pool with stride 2 is added before conv, whose stride is changed to 1. - """ - - def __init__(self, **kwargs): - super(ResNetV1d, self).__init__( - deep_stem=True, avg_down=True, **kwargs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/resnext.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/resnext.py deleted file mode 100644 index 805c27bf3..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/resnext.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from mmcv.cnn import build_conv_layer, build_norm_layer - -from ..builder import BACKBONES -from ..utils import ResLayer -from .resnet import Bottleneck as _Bottleneck -from .resnet import ResNet - - -class Bottleneck(_Bottleneck): - """Bottleneck block for ResNeXt. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if it is - "caffe", the stride-two layer is the first 1x1 conv layer. - """ - - def __init__(self, - inplanes, - planes, - groups=1, - base_width=4, - base_channels=64, - **kwargs): - super(Bottleneck, self).__init__(inplanes, planes, **kwargs) - - if groups == 1: - width = self.planes - else: - width = math.floor(self.planes * - (base_width / base_channels)) * groups - - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, width, postfix=1) - self.norm2_name, norm2 = build_norm_layer( - self.norm_cfg, width, postfix=2) - self.norm3_name, norm3 = build_norm_layer( - self.norm_cfg, self.planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - self.conv_cfg, - self.inplanes, - width, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - fallback_on_stride = False - self.with_modulated_dcn = False - if self.with_dcn: - fallback_on_stride = self.dcn.pop('fallback_on_stride', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = build_conv_layer( - self.conv_cfg, - width, - width, - kernel_size=3, - stride=self.conv2_stride, - padding=self.dilation, - dilation=self.dilation, - groups=groups, - bias=False) - else: - assert self.conv_cfg is None, 'conv_cfg must be None for DCN' - self.conv2 = build_conv_layer( - self.dcn, - width, - width, - kernel_size=3, - stride=self.conv2_stride, - padding=self.dilation, - dilation=self.dilation, - groups=groups, - bias=False) - - self.add_module(self.norm2_name, norm2) - self.conv3 = build_conv_layer( - self.conv_cfg, - width, - self.planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - -@BACKBONES.register_module() -class ResNeXt(ResNet): - """ResNeXt backbone. - - This backbone is the implementation of `Aggregated - Residual Transformations for Deep Neural - Networks `_. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - in_channels (int): Number of input image channels. Normally 3. - num_stages (int): Resnet stages, normally 4. - groups (int): Group of resnext. - base_width (int): Base width of resnext. - strides (Sequence[int]): Strides of the first block of each stage. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - norm_cfg (dict): dictionary to construct and config norm layer. - norm_eval (bool): Whether to set norm layers to eval mode, namely, - freeze running stats (mean and var). Note: Effect on Batch Norm - and its variants only. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. - zero_init_residual (bool): whether to use zero init for last norm layer - in resblocks to let them behave as identity. - - Example: - >>> from mmseg.models import ResNeXt - >>> import torch - >>> self = ResNeXt(depth=50) - >>> self.eval() - >>> inputs = torch.rand(1, 3, 32, 32) - >>> level_outputs = self.forward(inputs) - >>> for level_out in level_outputs: - ... print(tuple(level_out.shape)) - (1, 256, 8, 8) - (1, 512, 4, 4) - (1, 1024, 2, 2) - (1, 2048, 1, 1) - """ - - arch_settings = { - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, groups=1, base_width=4, **kwargs): - self.groups = groups - self.base_width = base_width - super(ResNeXt, self).__init__(**kwargs) - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``""" - return ResLayer( - groups=self.groups, - base_width=self.base_width, - base_channels=self.base_channels, - **kwargs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/timm_backbone.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/timm_backbone.py deleted file mode 100644 index 01b29fc5e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/backbones/timm_backbone.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -try: - import timm -except ImportError: - timm = None - -from mmcv.cnn.bricks.registry import NORM_LAYERS -from mmcv.runner import BaseModule - -from ..builder import BACKBONES - - -@BACKBONES.register_module() -class TIMMBackbone(BaseModule): - """Wrapper to use backbones from timm library. More details can be found in - `timm `_ . - - Args: - model_name (str): Name of timm model to instantiate. - pretrained (bool): Load pretrained weights if True. - checkpoint_path (str): Path of checkpoint to load after - model is initialized. - in_channels (int): Number of input image channels. Default: 3. - init_cfg (dict, optional): Initialization config dict - **kwargs: Other timm & model specific arguments. - """ - - def __init__( - self, - model_name, - features_only=True, - pretrained=True, - checkpoint_path='', - in_channels=3, - init_cfg=None, - **kwargs, - ): - if timm is None: - raise RuntimeError('timm is not installed') - super(TIMMBackbone, self).__init__(init_cfg) - if 'norm_layer' in kwargs: - kwargs['norm_layer'] = NORM_LAYERS.get(kwargs['norm_layer']) - self.timm_model = timm.create_model( - model_name=model_name, - features_only=features_only, - pretrained=pretrained, - in_chans=in_channels, - checkpoint_path=checkpoint_path, - **kwargs, - ) - - # Make unused parameters None - self.timm_model.global_pool = None - self.timm_model.fc = None - self.timm_model.classifier = None - - # Hack to use pretrained weights from timm - if pretrained or checkpoint_path: - self._is_init = True - - def forward(self, x): - features = self.timm_model(x) - return features diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/builder.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/builder.py deleted file mode 100644 index 5e18e4e64..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/builder.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -from mmcv.cnn import MODELS as MMCV_MODELS -from mmcv.cnn.bricks.registry import ATTENTION as MMCV_ATTENTION -from mmcv.utils import Registry - -MODELS = Registry('models', parent=MMCV_MODELS) -ATTENTION = Registry('attention', parent=MMCV_ATTENTION) - -BACKBONES = MODELS -NECKS = MODELS -HEADS = MODELS -LOSSES = MODELS -SEGMENTORS = MODELS - - -def build_backbone(cfg): - """Build backbone.""" - return BACKBONES.build(cfg) - - -def build_neck(cfg): - """Build neck.""" - return NECKS.build(cfg) - - -def build_head(cfg): - """Build head.""" - return HEADS.build(cfg) - - -def build_loss(cfg): - """Build loss.""" - return LOSSES.build(cfg) - - -def build_segmentor(cfg, train_cfg=None, test_cfg=None): - """Build segmentor.""" - if train_cfg is not None or test_cfg is not None: - warnings.warn( - 'train_cfg and test_cfg is deprecated, ' - 'please specify them in model', UserWarning) - assert cfg.get('train_cfg') is None or train_cfg is None, \ - 'train_cfg specified in both outer field and model field ' - assert cfg.get('test_cfg') is None or test_cfg is None, \ - 'test_cfg specified in both outer field and model field ' - return SEGMENTORS.build( - cfg, default_args=dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/__init__.py deleted file mode 100644 index bbaae4243..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .psp_head import PSPHead -from .aspp_head import ASPPHead -from .cc_head import CCHead -from .decode_head import BaseDecodeHead -from .fcn_head import FCNHead diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/aspp_head.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/aspp_head.py deleted file mode 100644 index 7059aee96..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/aspp_head.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule - -from mmseg.ops import resize -from ..builder import HEADS -from .decode_head import BaseDecodeHead - - -class ASPPModule(nn.ModuleList): - """Atrous Spatial Pyramid Pooling (ASPP) Module. - - Args: - dilations (tuple[int]): Dilation rate of each layer. - in_channels (int): Input channels. - channels (int): Channels after modules, before conv_seg. - conv_cfg (dict|None): Config of conv layers. - norm_cfg (dict|None): Config of norm layers. - act_cfg (dict): Config of activation layers. - """ - - def __init__(self, dilations, in_channels, channels, conv_cfg, norm_cfg, - act_cfg): - super(ASPPModule, self).__init__() - self.dilations = dilations - self.in_channels = in_channels - self.channels = channels - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - for dilation in dilations: - self.append( - ConvModule( - self.in_channels, - self.channels, - 1 if dilation == 1 else 3, - dilation=dilation, - padding=0 if dilation == 1 else dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - - def forward(self, x): - """Forward function.""" - aspp_outs = [] - for aspp_module in self: - aspp_outs.append(aspp_module(x)) - - return aspp_outs - - -@HEADS.register_module() -class ASPPHead(BaseDecodeHead): - """Rethinking Atrous Convolution for Semantic Image Segmentation. - - This head is the implementation of `DeepLabV3 - `_. - - Args: - dilations (tuple[int]): Dilation rates for ASPP module. - Default: (1, 6, 12, 18). - """ - - def __init__(self, dilations=(1, 6, 12, 18), **kwargs): - super(ASPPHead, self).__init__(**kwargs) - assert isinstance(dilations, (list, tuple)) - self.dilations = dilations - self.image_pool = nn.Sequential( - nn.AdaptiveAvgPool2d(1), - ConvModule( - self.in_channels, - self.channels, - 1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - self.aspp_modules = ASPPModule( - dilations, - self.in_channels, - self.channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - self.bottleneck = ConvModule( - (len(dilations) + 1) * self.channels, - self.channels, - 3, - padding=1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - def _forward_feature(self, inputs): - """Forward function for feature maps before classifying each pixel with - ``self.cls_seg`` fc. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - feats (Tensor): A tensor of shape (batch_size, self.channels, - H, W) which is feature map for last layer of decoder head. - """ - x = self._transform_inputs(inputs) - aspp_outs = [ - resize( - self.image_pool(x), - size=x.size()[2:], - mode='bilinear', - align_corners=self.align_corners) - ] - aspp_outs.extend(self.aspp_modules(x)) - aspp_outs = torch.cat(aspp_outs, dim=1) - feats = self.bottleneck(aspp_outs) - return feats - - def forward(self, inputs): - """Forward function.""" - output = self._forward_feature(inputs) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/cc_head.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/cc_head.py deleted file mode 100644 index ed19eb46d..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/cc_head.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import HEADS -from .fcn_head import FCNHead - -try: - from mmcv.ops import CrissCrossAttention -except ModuleNotFoundError: - CrissCrossAttention = None - - -@HEADS.register_module() -class CCHead(FCNHead): - """CCNet: Criss-Cross Attention for Semantic Segmentation. - - This head is the implementation of `CCNet - `_. - - Args: - recurrence (int): Number of recurrence of Criss Cross Attention - module. Default: 2. - """ - - def __init__(self, recurrence=2, **kwargs): - if CrissCrossAttention is None: - raise RuntimeError('Please install mmcv-full for ' - 'CrissCrossAttention ops') - super(CCHead, self).__init__(num_convs=2, **kwargs) - self.recurrence = recurrence - self.cca = CrissCrossAttention(self.channels) - - def forward(self, inputs): - """Forward function.""" - x = self._transform_inputs(inputs) - output = self.convs[0](x) - for _ in range(self.recurrence): - output = self.cca(output) - output = self.convs[1](output) - if self.concat_input: - output = self.conv_cat(torch.cat([x, output], dim=1)) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/decode_head.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/decode_head.py deleted file mode 100644 index d08b1d0b6..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/decode_head.py +++ /dev/null @@ -1,266 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -import torch -import torch.nn as nn -from mmcv.runner import BaseModule, auto_fp16, force_fp32 - -from mmseg.core import build_pixel_sampler -from mmseg.ops import resize -from ..builder import build_loss -from ..losses import accuracy - - -class BaseDecodeHead(BaseModule, metaclass=ABCMeta): - """Base class for BaseDecodeHead. - - Args: - in_channels (int|Sequence[int]): Input channels. - channels (int): Channels after modules, before conv_seg. - num_classes (int): Number of classes. - dropout_ratio (float): Ratio of dropout layer. Default: 0.1. - conv_cfg (dict|None): Config of conv layers. Default: None. - norm_cfg (dict|None): Config of norm layers. Default: None. - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU') - in_index (int|Sequence[int]): Input feature index. Default: -1 - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - Default: None. - loss_decode (dict | Sequence[dict]): Config of decode loss. - The `loss_name` is property of corresponding loss function which - could be shown in training log. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - e.g. dict(type='CrossEntropyLoss'), - [dict(type='CrossEntropyLoss', loss_name='loss_ce'), - dict(type='DiceLoss', loss_name='loss_dice')] - Default: dict(type='CrossEntropyLoss'). - ignore_index (int | None): The label index to be ignored. When using - masked BCE loss, ignore_index should be set to None. Default: 255. - sampler (dict|None): The config of segmentation map sampler. - Default: None. - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - """ - - def __init__(self, - in_channels, - channels, - *, - num_classes, - dropout_ratio=0.1, - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - in_index=-1, - input_transform=None, - loss_decode=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0), - ignore_index=255, - sampler=None, - align_corners=False, - init_cfg=dict( - type='Normal', std=0.01, override=dict(name='conv_seg'))): - super(BaseDecodeHead, self).__init__(init_cfg) - self._init_inputs(in_channels, in_index, input_transform) - self.channels = channels - self.num_classes = num_classes - self.dropout_ratio = dropout_ratio - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.in_index = in_index - - self.ignore_index = ignore_index - self.align_corners = align_corners - - if isinstance(loss_decode, dict): - self.loss_decode = build_loss(loss_decode) - elif isinstance(loss_decode, (list, tuple)): - self.loss_decode = nn.ModuleList() - for loss in loss_decode: - self.loss_decode.append(build_loss(loss)) - else: - raise TypeError(f'loss_decode must be a dict or sequence of dict,\ - but got {type(loss_decode)}') - - if sampler is not None: - self.sampler = build_pixel_sampler(sampler, context=self) - else: - self.sampler = None - - self.conv_seg = nn.Conv2d(channels, num_classes, kernel_size=1) - if dropout_ratio > 0: - self.dropout = nn.Dropout2d(dropout_ratio) - else: - self.dropout = None - self.fp16_enabled = False - - def extra_repr(self): - """Extra repr.""" - s = f'input_transform={self.input_transform}, ' \ - f'ignore_index={self.ignore_index}, ' \ - f'align_corners={self.align_corners}' - return s - - def _init_inputs(self, in_channels, in_index, input_transform): - """Check and initialize input transforms. - - The in_channels, in_index and input_transform must match. - Specifically, when input_transform is None, only single feature map - will be selected. So in_channels and in_index must be of type int. - When input_transform - - Args: - in_channels (int|Sequence[int]): Input channels. - in_index (int|Sequence[int]): Input feature index. - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - """ - - if input_transform is not None: - assert input_transform in ['resize_concat', 'multiple_select'] - self.input_transform = input_transform - self.in_index = in_index - if input_transform is not None: - assert isinstance(in_channels, (list, tuple)) - assert isinstance(in_index, (list, tuple)) - assert len(in_channels) == len(in_index) - if input_transform == 'resize_concat': - self.in_channels = sum(in_channels) - else: - self.in_channels = in_channels - else: - assert isinstance(in_channels, int) - assert isinstance(in_index, int) - self.in_channels = in_channels - - def _transform_inputs(self, inputs): - """Transform inputs for decoder. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - Tensor: The transformed inputs - """ - - if self.input_transform == 'resize_concat': - inputs = [inputs[i] for i in self.in_index] - upsampled_inputs = [ - resize( - input=x, - size=inputs[0].shape[2:], - mode='bilinear', - align_corners=self.align_corners) for x in inputs - ] - inputs = torch.cat(upsampled_inputs, dim=1) - elif self.input_transform == 'multiple_select': - inputs = [inputs[i] for i in self.in_index] - else: - inputs = inputs[self.in_index] - - return inputs - - @auto_fp16() - @abstractmethod - def forward(self, inputs): - """Placeholder of forward function.""" - pass - - def forward_train(self, inputs, img_metas, gt_semantic_seg, train_cfg): - """Forward function for training. - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - train_cfg (dict): The training config. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - seg_logits = self.forward(inputs) - losses = self.losses(seg_logits, gt_semantic_seg) - return losses - - def forward_test(self, inputs, img_metas, test_cfg): - """Forward function for testing. - - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - test_cfg (dict): The testing config. - - Returns: - Tensor: Output segmentation map. - """ - return self.forward(inputs) - - def cls_seg(self, feat): - """Classify each pixel.""" - if self.dropout is not None: - feat = self.dropout(feat) - output = self.conv_seg(feat) - return output - - @force_fp32(apply_to=('seg_logit', )) - def losses(self, seg_logit, seg_label): - """Compute segmentation loss.""" - loss = dict() - seg_logit = resize( - input=seg_logit, - size=seg_label.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - if self.sampler is not None: - seg_weight = self.sampler.sample(seg_logit, seg_label) - else: - seg_weight = None - seg_label = seg_label.squeeze(1) - - if not isinstance(self.loss_decode, nn.ModuleList): - losses_decode = [self.loss_decode] - else: - losses_decode = self.loss_decode - for loss_decode in losses_decode: - if loss_decode.loss_name not in loss: - loss[loss_decode.loss_name] = loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - else: - loss[loss_decode.loss_name] += loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - - loss['acc_seg'] = accuracy( - seg_logit, seg_label, ignore_index=self.ignore_index) - return loss diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/fcn_head.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/fcn_head.py deleted file mode 100644 index fb79a0d7c..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/fcn_head.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule - -from ..builder import HEADS -from .decode_head import BaseDecodeHead - - -@HEADS.register_module() -class FCNHead(BaseDecodeHead): - """Fully Convolution Networks for Semantic Segmentation. - - This head is implemented of `FCNNet `_. - - Args: - num_convs (int): Number of convs in the head. Default: 2. - kernel_size (int): The kernel size for convs in the head. Default: 3. - concat_input (bool): Whether concat the input and output of convs - before classification layer. - dilation (int): The dilation rate for convs in the head. Default: 1. - """ - - def __init__(self, - num_convs=2, - kernel_size=3, - concat_input=True, - dilation=1, - **kwargs): - assert num_convs >= 0 and dilation > 0 and isinstance(dilation, int) - self.num_convs = num_convs - self.concat_input = concat_input - self.kernel_size = kernel_size - super(FCNHead, self).__init__(**kwargs) - if num_convs == 0: - assert self.in_channels == self.channels - - conv_padding = (kernel_size // 2) * dilation - convs = [] - convs.append( - ConvModule( - self.in_channels, - self.channels, - kernel_size=kernel_size, - padding=conv_padding, - dilation=dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - for i in range(num_convs - 1): - convs.append( - ConvModule( - self.channels, - self.channels, - kernel_size=kernel_size, - padding=conv_padding, - dilation=dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - if num_convs == 0: - self.convs = nn.Identity() - else: - self.convs = nn.Sequential(*convs) - if self.concat_input: - self.conv_cat = ConvModule( - self.in_channels + self.channels, - self.channels, - kernel_size=kernel_size, - padding=kernel_size // 2, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - def _forward_feature(self, inputs): - """Forward function for feature maps before classifying each pixel with - ``self.cls_seg`` fc. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - feats (Tensor): A tensor of shape (batch_size, self.channels, - H, W) which is feature map for last layer of decoder head. - """ - x = self._transform_inputs(inputs) - feats = self.convs(x) - if self.concat_input: - feats = self.conv_cat(torch.cat([x, feats], dim=1)) - return feats - - def forward(self, inputs): - """Forward function.""" - output = self._forward_feature(inputs) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/psp_head.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/psp_head.py deleted file mode 100644 index 6990676ff..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/decode_heads/psp_head.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule - -from mmseg.ops import resize -from ..builder import HEADS -from .decode_head import BaseDecodeHead - - -class PPM(nn.ModuleList): - """Pooling Pyramid Module used in PSPNet. - - Args: - pool_scales (tuple[int]): Pooling scales used in Pooling Pyramid - Module. - in_channels (int): Input channels. - channels (int): Channels after modules, before conv_seg. - conv_cfg (dict|None): Config of conv layers. - norm_cfg (dict|None): Config of norm layers. - act_cfg (dict): Config of activation layers. - align_corners (bool): align_corners argument of F.interpolate. - """ - - def __init__(self, pool_scales, in_channels, channels, conv_cfg, norm_cfg, - act_cfg, align_corners, **kwargs): - super(PPM, self).__init__() - self.pool_scales = pool_scales - self.align_corners = align_corners - self.in_channels = in_channels - self.channels = channels - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - for pool_scale in pool_scales: - self.append( - nn.Sequential( - nn.AdaptiveAvgPool2d(pool_scale), - ConvModule( - self.in_channels, - self.channels, - 1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - **kwargs))) - - def forward(self, x): - """Forward function.""" - ppm_outs = [] - for ppm in self: - ppm_out = ppm(x) - upsampled_ppm_out = resize( - ppm_out, - size=x.size()[2:], - mode='bilinear', - align_corners=self.align_corners) - ppm_outs.append(upsampled_ppm_out) - return ppm_outs - - -@HEADS.register_module() -class PSPHead(BaseDecodeHead): - """Pyramid Scene Parsing Network. - - This head is the implementation of - `PSPNet `_. - - Args: - pool_scales (tuple[int]): Pooling scales used in Pooling Pyramid - Module. Default: (1, 2, 3, 6). - """ - - def __init__(self, pool_scales=(1, 2, 3, 6), **kwargs): - super(PSPHead, self).__init__(**kwargs) - assert isinstance(pool_scales, (list, tuple)) - self.pool_scales = pool_scales - self.psp_modules = PPM( - self.pool_scales, - self.in_channels, - self.channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - self.bottleneck = ConvModule( - self.in_channels + len(pool_scales) * self.channels, - self.channels, - 3, - padding=1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - def _forward_feature(self, inputs): - """Forward function for feature maps before classifying each pixel with - ``self.cls_seg`` fc. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - feats (Tensor): A tensor of shape (batch_size, self.channels, - H, W) which is feature map for last layer of decoder head. - """ - x = self._transform_inputs(inputs) - psp_outs = [x] - psp_outs.extend(self.psp_modules(x)) - psp_outs = torch.cat(psp_outs, dim=1) - feats = self.bottleneck(psp_outs) - return feats - - def forward(self, inputs): - """Forward function.""" - output = self._forward_feature(inputs) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/__init__.py deleted file mode 100644 index fbc5b2d1b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .accuracy import Accuracy, accuracy -from .cross_entropy_loss import (CrossEntropyLoss, binary_cross_entropy, - cross_entropy, mask_cross_entropy) -from .dice_loss import DiceLoss -from .focal_loss import FocalLoss -from .lovasz_loss import LovaszLoss -from .utils import reduce_loss, weight_reduce_loss, weighted_loss - -__all__ = [ - 'accuracy', 'Accuracy', 'cross_entropy', 'binary_cross_entropy', - 'mask_cross_entropy', 'CrossEntropyLoss', 'reduce_loss', - 'weight_reduce_loss', 'weighted_loss', 'LovaszLoss', 'DiceLoss', - 'FocalLoss' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/accuracy.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/accuracy.py deleted file mode 100644 index 1d9e2d770..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/accuracy.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def accuracy(pred, target, topk=1, thresh=None, ignore_index=None): - """Calculate accuracy according to the prediction and target. - - Args: - pred (torch.Tensor): The model prediction, shape (N, num_class, ...) - target (torch.Tensor): The target of each prediction, shape (N, , ...) - ignore_index (int | None): The label index to be ignored. Default: None - topk (int | tuple[int], optional): If the predictions in ``topk`` - matches the target, the predictions will be regarded as - correct ones. Defaults to 1. - thresh (float, optional): If not None, predictions with scores under - this threshold are considered incorrect. Default to None. - - Returns: - float | tuple[float]: If the input ``topk`` is a single integer, - the function will return a single float as accuracy. If - ``topk`` is a tuple containing multiple integers, the - function will return a tuple containing accuracies of - each ``topk`` number. - """ - assert isinstance(topk, (int, tuple)) - if isinstance(topk, int): - topk = (topk, ) - return_single = True - else: - return_single = False - - maxk = max(topk) - if pred.size(0) == 0: - accu = [pred.new_tensor(0.) for i in range(len(topk))] - return accu[0] if return_single else accu - assert pred.ndim == target.ndim + 1 - assert pred.size(0) == target.size(0) - assert maxk <= pred.size(1), \ - f'maxk {maxk} exceeds pred dimension {pred.size(1)}' - pred_value, pred_label = pred.topk(maxk, dim=1) - # transpose to shape (maxk, N, ...) - pred_label = pred_label.transpose(0, 1) - correct = pred_label.eq(target.unsqueeze(0).expand_as(pred_label)) - if thresh is not None: - # Only prediction values larger than thresh are counted as correct - correct = correct & (pred_value > thresh).t() - if ignore_index is not None: - correct = correct[:, target != ignore_index] - res = [] - eps = torch.finfo(torch.float32).eps - for k in topk: - # Avoid causing ZeroDivisionError when all pixels - # of an image are ignored - correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) + eps - if ignore_index is not None: - total_num = target[target != ignore_index].numel() + eps - else: - total_num = target.numel() + eps - res.append(correct_k.mul_(100.0 / total_num)) - return res[0] if return_single else res - - -class Accuracy(nn.Module): - """Accuracy calculation module.""" - - def __init__(self, topk=(1, ), thresh=None, ignore_index=None): - """Module to calculate the accuracy. - - Args: - topk (tuple, optional): The criterion used to calculate the - accuracy. Defaults to (1,). - thresh (float, optional): If not None, predictions with scores - under this threshold are considered incorrect. Default to None. - """ - super().__init__() - self.topk = topk - self.thresh = thresh - self.ignore_index = ignore_index - - def forward(self, pred, target): - """Forward function to calculate accuracy. - - Args: - pred (torch.Tensor): Prediction of models. - target (torch.Tensor): Target for each prediction. - - Returns: - tuple[float]: The accuracies under different topk criterions. - """ - return accuracy(pred, target, self.topk, self.thresh, - self.ignore_index) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/cross_entropy_loss.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/cross_entropy_loss.py deleted file mode 100644 index 623fd58db..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/cross_entropy_loss.py +++ /dev/null @@ -1,296 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weight_reduce_loss - - -def cross_entropy(pred, - label, - weight=None, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=-100, - avg_non_ignore=False): - """cross_entropy. The wrapper function for :func:`F.cross_entropy` - - Args: - pred (torch.Tensor): The prediction with shape (N, 1). - label (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - Default: None. - class_weight (list[float], optional): The weight for each class. - Default: None. - reduction (str, optional): The method used to reduce the loss. - Options are 'none', 'mean' and 'sum'. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Default: None. - ignore_index (int): Specifies a target value that is ignored and - does not contribute to the input gradients. When - ``avg_non_ignore `` is ``True``, and the ``reduction`` is - ``''mean''``, the loss is averaged over non-ignored targets. - Defaults: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - """ - - # class_weight is a manual rescaling weight given to each class. - # If given, has to be a Tensor of size C element-wise losses - loss = F.cross_entropy( - pred, - label, - weight=class_weight, - reduction='none', - ignore_index=ignore_index) - - # apply weights and do the reduction - # average loss over non-ignored elements - # pytorch's official cross_entropy average loss over non-ignored elements - # refer to https://github.com/pytorch/pytorch/blob/56b43f4fec1f76953f15a627694d4bba34588969/torch/nn/functional.py#L2660 # noqa - if (avg_factor is None) and avg_non_ignore and reduction == 'mean': - avg_factor = label.numel() - (label == ignore_index).sum().item() - if weight is not None: - weight = weight.float() - loss = weight_reduce_loss( - loss, weight=weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def _expand_onehot_labels(labels, label_weights, target_shape, ignore_index): - """Expand onehot labels to match the size of prediction.""" - bin_labels = labels.new_zeros(target_shape) - valid_mask = (labels >= 0) & (labels != ignore_index) - inds = torch.nonzero(valid_mask, as_tuple=True) - - if inds[0].numel() > 0: - if labels.dim() == 3: - bin_labels[inds[0], labels[valid_mask], inds[1], inds[2]] = 1 - else: - bin_labels[inds[0], labels[valid_mask]] = 1 - - valid_mask = valid_mask.unsqueeze(1).expand(target_shape).float() - - if label_weights is None: - bin_label_weights = valid_mask - else: - bin_label_weights = label_weights.unsqueeze(1).expand(target_shape) - bin_label_weights = bin_label_weights * valid_mask - - return bin_labels, bin_label_weights, valid_mask - - -def binary_cross_entropy(pred, - label, - weight=None, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=-100, - avg_non_ignore=False, - **kwargs): - """Calculate the binary CrossEntropy loss. - - Args: - pred (torch.Tensor): The prediction with shape (N, 1). - label (torch.Tensor): The learning label of the prediction. - Note: In bce loss, label < 0 is invalid. - weight (torch.Tensor, optional): Sample-wise loss weight. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (int): The label index to be ignored. Default: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - - Returns: - torch.Tensor: The calculated loss - """ - if pred.size(1) == 1: - # For binary class segmentation, the shape of pred is - # [N, 1, H, W] and that of label is [N, H, W]. - # As the ignore_index often set as 255, so the - # binary class label check should mask out - # ignore_index - assert label[label != ignore_index].max() <= 1, \ - 'For pred with shape [N, 1, H, W], its label must have at ' \ - 'most 2 classes' - pred = pred.squeeze() - if pred.dim() != label.dim(): - assert (pred.dim() == 2 and label.dim() == 1) or ( - pred.dim() == 4 and label.dim() == 3), \ - 'Only pred shape [N, C], label shape [N] or pred shape [N, C, ' \ - 'H, W], label shape [N, H, W] are supported' - # `weight` returned from `_expand_onehot_labels` - # has been treated for valid (non-ignore) pixels - label, weight, valid_mask = _expand_onehot_labels( - label, weight, pred.shape, ignore_index) - else: - # should mask out the ignored elements - valid_mask = ((label >= 0) & (label != ignore_index)).float() - if weight is not None: - weight = weight * valid_mask - else: - weight = valid_mask - # average loss over non-ignored and valid elements - if reduction == 'mean' and avg_factor is None and avg_non_ignore: - avg_factor = valid_mask.sum().item() - - loss = F.binary_cross_entropy_with_logits( - pred, label.float(), pos_weight=class_weight, reduction='none') - # do the reduction for the weighted loss - loss = weight_reduce_loss( - loss, weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def mask_cross_entropy(pred, - target, - label, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=None, - **kwargs): - """Calculate the CrossEntropy loss for masks. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. - label (torch.Tensor): ``label`` indicates the class label of the mask' - corresponding object. This will be used to select the mask in the - of the class which the object belongs to when the mask prediction - if not class-agnostic. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (None): Placeholder, to be consistent with other loss. - Default: None. - - Returns: - torch.Tensor: The calculated loss - """ - assert ignore_index is None, 'BCE loss does not support ignore_index' - # TODO: handle these two reserved arguments - assert reduction == 'mean' and avg_factor is None - num_rois = pred.size()[0] - inds = torch.arange(0, num_rois, dtype=torch.long, device=pred.device) - pred_slice = pred[inds, label].squeeze(1) - return F.binary_cross_entropy_with_logits( - pred_slice, target, weight=class_weight, reduction='mean')[None] - - -@LOSSES.register_module() -class CrossEntropyLoss(nn.Module): - """CrossEntropyLoss. - - Args: - use_sigmoid (bool, optional): Whether the prediction uses sigmoid - of softmax. Defaults to False. - use_mask (bool, optional): Whether to use mask cross entropy loss. - Defaults to False. - reduction (str, optional): . Defaults to 'mean'. - Options are "none", "mean" and "sum". - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - """ - - def __init__(self, - use_sigmoid=False, - use_mask=False, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_ce', - avg_non_ignore=False): - super(CrossEntropyLoss, self).__init__() - assert (use_sigmoid is False) or (use_mask is False) - self.use_sigmoid = use_sigmoid - self.use_mask = use_mask - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = get_class_weight(class_weight) - self.avg_non_ignore = avg_non_ignore - if not self.avg_non_ignore and self.reduction == 'mean': - warnings.warn( - 'Default ``avg_non_ignore`` is False, if you would like to ' - 'ignore the certain label and average loss over non-ignore ' - 'labels, which is the same with PyTorch official ' - 'cross_entropy, set ``avg_non_ignore=True``.') - - if self.use_sigmoid: - self.cls_criterion = binary_cross_entropy - elif self.use_mask: - self.cls_criterion = mask_cross_entropy - else: - self.cls_criterion = cross_entropy - self._loss_name = loss_name - - def extra_repr(self): - """Extra repr.""" - s = f'avg_non_ignore={self.avg_non_ignore}' - return s - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - ignore_index=-100, - **kwargs): - """Forward function.""" - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = cls_score.new_tensor(self.class_weight) - else: - class_weight = None - # Note: for BCE loss, label < 0 is invalid. - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - weight, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - avg_non_ignore=self.avg_non_ignore, - ignore_index=ignore_index, - **kwargs) - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/dice_loss.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/dice_loss.py deleted file mode 100644 index 79a3abfc2..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/dice_loss.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/LikeLy-Journey/SegmenTron/blob/master/ -segmentron/solver/loss.py (Apache-2.0 License)""" -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weighted_loss - - -@weighted_loss -def dice_loss(pred, - target, - valid_mask, - smooth=1, - exponent=2, - class_weight=None, - ignore_index=255): - assert pred.shape[0] == target.shape[0] - total_loss = 0 - num_classes = pred.shape[1] - for i in range(num_classes): - if i != ignore_index: - dice_loss = binary_dice_loss( - pred[:, i], - target[..., i], - valid_mask=valid_mask, - smooth=smooth, - exponent=exponent) - if class_weight is not None: - dice_loss *= class_weight[i] - total_loss += dice_loss - return total_loss / num_classes - - -@weighted_loss -def binary_dice_loss(pred, target, valid_mask, smooth=1, exponent=2, **kwards): - assert pred.shape[0] == target.shape[0] - pred = pred.reshape(pred.shape[0], -1) - target = target.reshape(target.shape[0], -1) - valid_mask = valid_mask.reshape(valid_mask.shape[0], -1) - - num = torch.sum(torch.mul(pred, target) * valid_mask, dim=1) * 2 + smooth - den = torch.sum(pred.pow(exponent) + target.pow(exponent), dim=1) + smooth - - return 1 - num / den - - -@LOSSES.register_module() -class DiceLoss(nn.Module): - """DiceLoss. - - This loss is proposed in `V-Net: Fully Convolutional Neural Networks for - Volumetric Medical Image Segmentation `_. - - Args: - smooth (float): A float number to smooth loss, and avoid NaN error. - Default: 1 - exponent (float): An float number to calculate denominator - value: \\sum{x^exponent} + \\sum{y^exponent}. Default: 2. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Default to 1.0. - ignore_index (int | None): The label index to be ignored. Default: 255. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_dice'. - """ - - def __init__(self, - smooth=1, - exponent=2, - reduction='mean', - class_weight=None, - loss_weight=1.0, - ignore_index=255, - loss_name='loss_dice', - **kwards): - super(DiceLoss, self).__init__() - self.smooth = smooth - self.exponent = exponent - self.reduction = reduction - self.class_weight = get_class_weight(class_weight) - self.loss_weight = loss_weight - self.ignore_index = ignore_index - self._loss_name = loss_name - - def forward(self, - pred, - target, - avg_factor=None, - reduction_override=None, - **kwards): - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = pred.new_tensor(self.class_weight) - else: - class_weight = None - - pred = F.softmax(pred, dim=1) - num_classes = pred.shape[1] - one_hot_target = F.one_hot( - torch.clamp(target.long(), 0, num_classes - 1), - num_classes=num_classes) - valid_mask = (target != self.ignore_index).long() - - loss = self.loss_weight * dice_loss( - pred, - one_hot_target, - valid_mask=valid_mask, - reduction=reduction, - avg_factor=avg_factor, - smooth=self.smooth, - exponent=self.exponent, - class_weight=class_weight, - ignore_index=self.ignore_index) - return loss - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/focal_loss.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/focal_loss.py deleted file mode 100644 index af1c711df..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/focal_loss.py +++ /dev/null @@ -1,327 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Modified from https://github.com/open-mmlab/mmdetection -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.ops import sigmoid_focal_loss as _sigmoid_focal_loss - -from ..builder import LOSSES -from .utils import weight_reduce_loss - - -# This method is used when cuda is not available -def py_sigmoid_focal_loss(pred, - target, - one_hot_target=None, - weight=None, - gamma=2.0, - alpha=0.5, - class_weight=None, - valid_mask=None, - reduction='mean', - avg_factor=None): - """PyTorch version of `Focal Loss `_. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the - number of classes - target (torch.Tensor): The learning label of the prediction with - shape (N, C) - one_hot_target (None): Placeholder. It should be None. - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal Loss. - Defaults to 0.5. - class_weight (list[float], optional): Weight of each class. - Defaults to None. - valid_mask (torch.Tensor, optional): A mask uses 1 to mark the valid - samples and uses 0 to mark the ignored samples. Default: None. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - if isinstance(alpha, list): - alpha = pred.new_tensor(alpha) - pred_sigmoid = pred.sigmoid() - target = target.type_as(pred) - one_minus_pt = (1 - pred_sigmoid) * target + pred_sigmoid * (1 - target) - focal_weight = (alpha * target + (1 - alpha) * - (1 - target)) * one_minus_pt.pow(gamma) - - loss = F.binary_cross_entropy_with_logits( - pred, target, reduction='none') * focal_weight - final_weight = torch.ones(1, pred.size(1)).type_as(loss) - if weight is not None: - if weight.shape != loss.shape and weight.size(0) == loss.size(0): - # For most cases, weight is of shape (N, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - assert weight.dim() == loss.dim() - final_weight = final_weight * weight - if class_weight is not None: - final_weight = final_weight * pred.new_tensor(class_weight) - if valid_mask is not None: - final_weight = final_weight * valid_mask - loss = weight_reduce_loss(loss, final_weight, reduction, avg_factor) - return loss - - -def sigmoid_focal_loss(pred, - target, - one_hot_target, - weight=None, - gamma=2.0, - alpha=0.5, - class_weight=None, - valid_mask=None, - reduction='mean', - avg_factor=None): - r"""A warpper of cuda version `Focal Loss - `_. - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. It's shape - should be (N, ) - one_hot_target (torch.Tensor): The learning label with shape (N, C) - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal Loss. - Defaults to 0.5. - class_weight (list[float], optional): Weight of each class. - Defaults to None. - valid_mask (torch.Tensor, optional): A mask uses 1 to mark the valid - samples and uses 0 to mark the ignored samples. Default: None. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - # Function.apply does not accept keyword arguments, so the decorator - # "weighted_loss" is not applicable - final_weight = torch.ones(1, pred.size(1)).type_as(pred) - if isinstance(alpha, list): - # _sigmoid_focal_loss doesn't accept alpha of list type. Therefore, if - # a list is given, we set the input alpha as 0.5. This means setting - # equal weight for foreground class and background class. By - # multiplying the loss by 2, the effect of setting alpha as 0.5 is - # undone. The alpha of type list is used to regulate the loss in the - # post-processing process. - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), - gamma, 0.5, None, 'none') * 2 - alpha = pred.new_tensor(alpha) - final_weight = final_weight * ( - alpha * one_hot_target + (1 - alpha) * (1 - one_hot_target)) - else: - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), - gamma, alpha, None, 'none') - if weight is not None: - if weight.shape != loss.shape and weight.size(0) == loss.size(0): - # For most cases, weight is of shape (N, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - assert weight.dim() == loss.dim() - final_weight = final_weight * weight - if class_weight is not None: - final_weight = final_weight * pred.new_tensor(class_weight) - if valid_mask is not None: - final_weight = final_weight * valid_mask - loss = weight_reduce_loss(loss, final_weight, reduction, avg_factor) - return loss - - -@LOSSES.register_module() -class FocalLoss(nn.Module): - - def __init__(self, - use_sigmoid=True, - gamma=2.0, - alpha=0.5, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_focal'): - """`Focal Loss `_ - Args: - use_sigmoid (bool, optional): Whether to the prediction is - used for sigmoid or softmax. Defaults to True. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal - Loss. Defaults to 0.5. When a list is provided, the length - of the list should be equal to the number of classes. - Please be careful that this parameter is not the - class-wise weight but the weight of a binary classification - problem. This binary classification problem regards the - pixels which belong to one class as the foreground - and the other pixels as the background, each element in - the list is the weight of the corresponding foreground class. - The value of alpha or each element of alpha should be a float - in the interval [0, 1]. If you want to specify the class-wise - weight, please use `class_weight` parameter. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and - "sum". - class_weight (list[float], optional): Weight of each class. - Defaults to None. - loss_weight (float, optional): Weight of loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this - loss item to be included into the backward graph, `loss_` must - be the prefix of the name. Defaults to 'loss_focal'. - """ - super(FocalLoss, self).__init__() - assert use_sigmoid is True, \ - 'AssertionError: Only sigmoid focal loss supported now.' - assert reduction in ('none', 'mean', 'sum'), \ - "AssertionError: reduction should be 'none', 'mean' or " \ - "'sum'" - assert isinstance(alpha, (float, list)), \ - 'AssertionError: alpha should be of type float' - assert isinstance(gamma, float), \ - 'AssertionError: gamma should be of type float' - assert isinstance(loss_weight, float), \ - 'AssertionError: loss_weight should be of type float' - assert isinstance(loss_name, str), \ - 'AssertionError: loss_name should be of type str' - assert isinstance(class_weight, list) or class_weight is None, \ - 'AssertionError: class_weight must be None or of type list' - self.use_sigmoid = use_sigmoid - self.gamma = gamma - self.alpha = alpha - self.reduction = reduction - self.class_weight = class_weight - self.loss_weight = loss_weight - self._loss_name = loss_name - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - ignore_index=255, - **kwargs): - """Forward function. - - Args: - pred (torch.Tensor): The prediction with shape - (N, C) where C = number of classes, or - (N, C, d_1, d_2, ..., d_K) with K≥1 in the - case of K-dimensional loss. - target (torch.Tensor): The ground truth. If containing class - indices, shape (N) where each value is 0≤targets[i]≤C−1, - or (N, d_1, d_2, ..., d_K) with K≥1 in the case of - K-dimensional loss. If containing class probabilities, - same shape as the input. - weight (torch.Tensor, optional): The weight of loss for each - prediction. Defaults to None. - avg_factor (int, optional): Average factor that is used to - average the loss. Defaults to None. - reduction_override (str, optional): The reduction method used - to override the original reduction method of the loss. - Options are "none", "mean" and "sum". - ignore_index (int, optional): The label index to be ignored. - Default: 255 - Returns: - torch.Tensor: The calculated loss - """ - assert isinstance(ignore_index, int), \ - 'ignore_index must be of type int' - assert reduction_override in (None, 'none', 'mean', 'sum'), \ - "AssertionError: reduction should be 'none', 'mean' or " \ - "'sum'" - assert pred.shape == target.shape or \ - (pred.size(0) == target.size(0) and - pred.shape[2:] == target.shape[1:]), \ - "The shape of pred doesn't match the shape of target" - - original_shape = pred.shape - - # [B, C, d_1, d_2, ..., d_k] -> [C, B, d_1, d_2, ..., d_k] - pred = pred.transpose(0, 1) - # [C, B, d_1, d_2, ..., d_k] -> [C, N] - pred = pred.reshape(pred.size(0), -1) - # [C, N] -> [N, C] - pred = pred.transpose(0, 1).contiguous() - - if original_shape == target.shape: - # target with shape [B, C, d_1, d_2, ...] - # transform it's shape into [N, C] - # [B, C, d_1, d_2, ...] -> [C, B, d_1, d_2, ..., d_k] - target = target.transpose(0, 1) - # [C, B, d_1, d_2, ..., d_k] -> [C, N] - target = target.reshape(target.size(0), -1) - # [C, N] -> [N, C] - target = target.transpose(0, 1).contiguous() - else: - # target with shape [B, d_1, d_2, ...] - # transform it's shape into [N, ] - target = target.view(-1).contiguous() - valid_mask = (target != ignore_index).view(-1, 1) - # avoid raising error when using F.one_hot() - target = torch.where(target == ignore_index, target.new_tensor(0), - target) - - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.use_sigmoid: - num_classes = pred.size(1) - if torch.cuda.is_available() and pred.is_cuda: - if target.dim() == 1: - one_hot_target = F.one_hot(target, num_classes=num_classes) - else: - one_hot_target = target - target = target.argmax(dim=1) - valid_mask = (target != ignore_index).view(-1, 1) - calculate_loss_func = sigmoid_focal_loss - else: - one_hot_target = None - if target.dim() == 1: - target = F.one_hot(target, num_classes=num_classes) - else: - valid_mask = (target.argmax(dim=1) != ignore_index).view( - -1, 1) - calculate_loss_func = py_sigmoid_focal_loss - - loss_cls = self.loss_weight * calculate_loss_func( - pred, - target, - one_hot_target, - weight, - gamma=self.gamma, - alpha=self.alpha, - class_weight=self.class_weight, - valid_mask=valid_mask, - reduction=reduction, - avg_factor=avg_factor) - - if reduction == 'none': - # [N, C] -> [C, N] - loss_cls = loss_cls.transpose(0, 1) - # [C, N] -> [C, B, d1, d2, ...] - # original_shape: [B, C, d1, d2, ...] - loss_cls = loss_cls.reshape(original_shape[1], - original_shape[0], - *original_shape[2:]) - # [C, B, d1, d2, ...] -> [B, C, d1, d2, ...] - loss_cls = loss_cls.transpose(0, 1).contiguous() - else: - raise NotImplementedError - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/lovasz_loss.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/lovasz_loss.py deleted file mode 100644 index 2bb0fad39..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/lovasz_loss.py +++ /dev/null @@ -1,323 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/bermanmaxim/LovaszSoftmax/blob/master/pytor -ch/lovasz_losses.py Lovasz-Softmax and Jaccard hinge loss in PyTorch Maxim -Berman 2018 ESAT-PSI KU Leuven (MIT License)""" - -import mmcv -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weight_reduce_loss - - -def lovasz_grad(gt_sorted): - """Computes gradient of the Lovasz extension w.r.t sorted errors. - - See Alg. 1 in paper. - """ - p = len(gt_sorted) - gts = gt_sorted.sum() - intersection = gts - gt_sorted.float().cumsum(0) - union = gts + (1 - gt_sorted).float().cumsum(0) - jaccard = 1. - intersection / union - if p > 1: # cover 1-pixel case - jaccard[1:p] = jaccard[1:p] - jaccard[0:-1] - return jaccard - - -def flatten_binary_logits(logits, labels, ignore_index=None): - """Flattens predictions in the batch (binary case) Remove labels equal to - 'ignore_index'.""" - logits = logits.view(-1) - labels = labels.view(-1) - if ignore_index is None: - return logits, labels - valid = (labels != ignore_index) - vlogits = logits[valid] - vlabels = labels[valid] - return vlogits, vlabels - - -def flatten_probs(probs, labels, ignore_index=None): - """Flattens predictions in the batch.""" - if probs.dim() == 3: - # assumes output of a sigmoid layer - B, H, W = probs.size() - probs = probs.view(B, 1, H, W) - B, C, H, W = probs.size() - probs = probs.permute(0, 2, 3, 1).contiguous().view(-1, C) # B*H*W, C=P,C - labels = labels.view(-1) - if ignore_index is None: - return probs, labels - valid = (labels != ignore_index) - vprobs = probs[valid.nonzero().squeeze()] - vlabels = labels[valid] - return vprobs, vlabels - - -def lovasz_hinge_flat(logits, labels): - """Binary Lovasz hinge loss. - - Args: - logits (torch.Tensor): [P], logits at each prediction - (between -infty and +infty). - labels (torch.Tensor): [P], binary ground truth labels (0 or 1). - - Returns: - torch.Tensor: The calculated loss. - """ - if len(labels) == 0: - # only void pixels, the gradients should be 0 - return logits.sum() * 0. - signs = 2. * labels.float() - 1. - errors = (1. - logits * signs) - errors_sorted, perm = torch.sort(errors, dim=0, descending=True) - perm = perm.data - gt_sorted = labels[perm] - grad = lovasz_grad(gt_sorted) - loss = torch.dot(F.relu(errors_sorted), grad) - return loss - - -def lovasz_hinge(logits, - labels, - classes='present', - per_image=False, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=255): - """Binary Lovasz hinge loss. - - Args: - logits (torch.Tensor): [B, H, W], logits at each pixel - (between -infty and +infty). - labels (torch.Tensor): [B, H, W], binary ground truth masks (0 or 1). - classes (str | list[int], optional): Placeholder, to be consistent with - other loss. Default: None. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - class_weight (list[float], optional): Placeholder, to be consistent - with other loss. Default: None. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. This parameter only works when per_image is True. - Default: None. - ignore_index (int | None): The label index to be ignored. Default: 255. - - Returns: - torch.Tensor: The calculated loss. - """ - if per_image: - loss = [ - lovasz_hinge_flat(*flatten_binary_logits( - logit.unsqueeze(0), label.unsqueeze(0), ignore_index)) - for logit, label in zip(logits, labels) - ] - loss = weight_reduce_loss( - torch.stack(loss), None, reduction, avg_factor) - else: - loss = lovasz_hinge_flat( - *flatten_binary_logits(logits, labels, ignore_index)) - return loss - - -def lovasz_softmax_flat(probs, labels, classes='present', class_weight=None): - """Multi-class Lovasz-Softmax loss. - - Args: - probs (torch.Tensor): [P, C], class probabilities at each prediction - (between 0 and 1). - labels (torch.Tensor): [P], ground truth labels (between 0 and C - 1). - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - class_weight (list[float], optional): The weight for each class. - Default: None. - - Returns: - torch.Tensor: The calculated loss. - """ - if probs.numel() == 0: - # only void pixels, the gradients should be 0 - return probs * 0. - C = probs.size(1) - losses = [] - class_to_sum = list(range(C)) if classes in ['all', 'present'] else classes - for c in class_to_sum: - fg = (labels == c).float() # foreground for class c - if (classes == 'present' and fg.sum() == 0): - continue - if C == 1: - if len(classes) > 1: - raise ValueError('Sigmoid output possible only with 1 class') - class_pred = probs[:, 0] - else: - class_pred = probs[:, c] - errors = (fg - class_pred).abs() - errors_sorted, perm = torch.sort(errors, 0, descending=True) - perm = perm.data - fg_sorted = fg[perm] - loss = torch.dot(errors_sorted, lovasz_grad(fg_sorted)) - if class_weight is not None: - loss *= class_weight[c] - losses.append(loss) - return torch.stack(losses).mean() - - -def lovasz_softmax(probs, - labels, - classes='present', - per_image=False, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=255): - """Multi-class Lovasz-Softmax loss. - - Args: - probs (torch.Tensor): [B, C, H, W], class probabilities at each - prediction (between 0 and 1). - labels (torch.Tensor): [B, H, W], ground truth labels (between 0 and - C - 1). - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - class_weight (list[float], optional): The weight for each class. - Default: None. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. This parameter only works when per_image is True. - Default: None. - ignore_index (int | None): The label index to be ignored. Default: 255. - - Returns: - torch.Tensor: The calculated loss. - """ - - if per_image: - loss = [ - lovasz_softmax_flat( - *flatten_probs( - prob.unsqueeze(0), label.unsqueeze(0), ignore_index), - classes=classes, - class_weight=class_weight) - for prob, label in zip(probs, labels) - ] - loss = weight_reduce_loss( - torch.stack(loss), None, reduction, avg_factor) - else: - loss = lovasz_softmax_flat( - *flatten_probs(probs, labels, ignore_index), - classes=classes, - class_weight=class_weight) - return loss - - -@LOSSES.register_module() -class LovaszLoss(nn.Module): - """LovaszLoss. - - This loss is proposed in `The Lovasz-Softmax loss: A tractable surrogate - for the optimization of the intersection-over-union measure in neural - networks `_. - - Args: - loss_type (str, optional): Binary or multi-class loss. - Default: 'multi_class'. Options are "binary" and "multi_class". - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_lovasz'. - """ - - def __init__(self, - loss_type='multi_class', - classes='present', - per_image=False, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_lovasz'): - super(LovaszLoss, self).__init__() - assert loss_type in ('binary', 'multi_class'), "loss_type should be \ - 'binary' or 'multi_class'." - - if loss_type == 'binary': - self.cls_criterion = lovasz_hinge - else: - self.cls_criterion = lovasz_softmax - assert classes in ('all', 'present') or mmcv.is_list_of(classes, int) - if not per_image: - assert reduction == 'none', "reduction should be 'none' when \ - per_image is False." - - self.classes = classes - self.per_image = per_image - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = get_class_weight(class_weight) - self._loss_name = loss_name - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - """Forward function.""" - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = cls_score.new_tensor(self.class_weight) - else: - class_weight = None - - # if multi-class loss, transform logits to probs - if self.cls_criterion == lovasz_softmax: - cls_score = F.softmax(cls_score, dim=1) - - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - self.classes, - self.per_image, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/utils.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/utils.py deleted file mode 100644 index 621f57c74..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/losses/utils.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import mmcv -import numpy as np -import torch -import torch.nn.functional as F - - -def get_class_weight(class_weight): - """Get class weight for loss function. - - Args: - class_weight (list[float] | str | None): If class_weight is a str, - take it as a file name and read from it. - """ - if isinstance(class_weight, str): - # take it as a file path - if class_weight.endswith('.npy'): - class_weight = np.load(class_weight) - else: - # pkl, json or yaml - class_weight = mmcv.load(class_weight) - - return class_weight - - -def reduce_loss(loss, reduction): - """Reduce loss as specified. - - Args: - loss (Tensor): Elementwise loss tensor. - reduction (str): Options are "none", "mean" and "sum". - - Return: - Tensor: Reduced loss tensor. - """ - reduction_enum = F._Reduction.get_enum(reduction) - # none: 0, elementwise_mean:1, sum: 2 - if reduction_enum == 0: - return loss - elif reduction_enum == 1: - return loss.mean() - elif reduction_enum == 2: - return loss.sum() - - -def weight_reduce_loss(loss, weight=None, reduction='mean', avg_factor=None): - """Apply element-wise weight and reduce loss. - - Args: - loss (Tensor): Element-wise loss. - weight (Tensor): Element-wise weights. - reduction (str): Same as built-in losses of PyTorch. - avg_factor (float): Average factor when computing the mean of losses. - - Returns: - Tensor: Processed loss values. - """ - # if weight is specified, apply element-wise weight - if weight is not None: - assert weight.dim() == loss.dim() - if weight.dim() > 1: - assert weight.size(1) == 1 or weight.size(1) == loss.size(1) - loss = loss * weight - - # if avg_factor is not specified, just reduce the loss - if avg_factor is None: - loss = reduce_loss(loss, reduction) - else: - # if reduction is mean, then average the loss by avg_factor - if reduction == 'mean': - # Avoid causing ZeroDivisionError when avg_factor is 0.0, - # i.e., all labels of an image belong to ignore index. - eps = torch.finfo(torch.float32).eps - loss = loss.sum() / (avg_factor + eps) - # if reduction is 'none', then do nothing, otherwise raise an error - elif reduction != 'none': - raise ValueError('avg_factor can not be used with reduction="sum"') - return loss - - -def weighted_loss(loss_func): - """Create a weighted version of a given loss function. - - To use this decorator, the loss function must have the signature like - `loss_func(pred, target, **kwargs)`. The function only needs to compute - element-wise loss without any reduction. This decorator will add weight - and reduction arguments to the function. The decorated function will have - the signature like `loss_func(pred, target, weight=None, reduction='mean', - avg_factor=None, **kwargs)`. - - :Example: - - >>> import torch - >>> @weighted_loss - >>> def l1_loss(pred, target): - >>> return (pred - target).abs() - - >>> pred = torch.Tensor([0, 2, 3]) - >>> target = torch.Tensor([1, 1, 1]) - >>> weight = torch.Tensor([1, 0, 1]) - - >>> l1_loss(pred, target) - tensor(1.3333) - >>> l1_loss(pred, target, weight) - tensor(1.) - >>> l1_loss(pred, target, reduction='none') - tensor([1., 1., 2.]) - >>> l1_loss(pred, target, weight, avg_factor=2) - tensor(1.5000) - """ - - @functools.wraps(loss_func) - def wrapper(pred, - target, - weight=None, - reduction='mean', - avg_factor=None, - **kwargs): - # get element-wise loss - loss = loss_func(pred, target, **kwargs) - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - return wrapper diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/__init__.py deleted file mode 100644 index ff03186a9..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .featurepyramid import Feature2Pyramid -from .fpn import FPN -from .ic_neck import ICNeck -from .jpu import JPU -from .mla_neck import MLANeck -from .multilevel_neck import MultiLevelNeck - -__all__ = [ - 'FPN', 'MultiLevelNeck', 'MLANeck', 'ICNeck', 'JPU', 'Feature2Pyramid' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/featurepyramid.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/featurepyramid.py deleted file mode 100644 index 82a00ceb1..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/featurepyramid.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import build_norm_layer - -from ..builder import NECKS - - -@NECKS.register_module() -class Feature2Pyramid(nn.Module): - """Feature2Pyramid. - - A neck structure connect ViT backbone and decoder_heads. - - Args: - embed_dims (int): Embedding dimension. - rescales (list[float]): Different sampling multiples were - used to obtain pyramid features. Default: [4, 2, 1, 0.5]. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='SyncBN', requires_grad=True). - """ - - def __init__(self, - embed_dim, - rescales=[4, 2, 1, 0.5], - norm_cfg=dict(type='SyncBN', requires_grad=True)): - super(Feature2Pyramid, self).__init__() - self.rescales = rescales - self.upsample_4x = None - for k in self.rescales: - if k == 4: - self.upsample_4x = nn.Sequential( - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2), - build_norm_layer(norm_cfg, embed_dim)[1], - nn.GELU(), - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2), - ) - elif k == 2: - self.upsample_2x = nn.Sequential( - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2)) - elif k == 1: - self.identity = nn.Identity() - elif k == 0.5: - self.downsample_2x = nn.MaxPool2d(kernel_size=2, stride=2) - elif k == 0.25: - self.downsample_4x = nn.MaxPool2d(kernel_size=4, stride=4) - else: - raise KeyError(f'invalid {k} for feature2pyramid') - - def forward(self, inputs): - assert len(inputs) == len(self.rescales) - outputs = [] - if self.upsample_4x is not None: - ops = [ - self.upsample_4x, self.upsample_2x, self.identity, - self.downsample_2x - ] - else: - ops = [ - self.upsample_2x, self.identity, self.downsample_2x, - self.downsample_4x - ] - for i in range(len(inputs)): - outputs.append(ops[i](inputs[i])) - return tuple(outputs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/fpn.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/fpn.py deleted file mode 100644 index 6997de9d4..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/fpn.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule, auto_fp16 - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class FPN(BaseModule): - """Feature Pyramid Network. - - This neck is the implementation of `Feature Pyramid Networks for Object - Detection `_. - - Args: - in_channels (list[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - num_outs (int): Number of output scales. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - add_extra_convs (bool | str): If bool, it decides whether to add conv - layers on top of the original feature maps. Default to False. - If True, its actual mode is specified by `extra_convs_on_inputs`. - If str, it specifies the source feature map of the extra convs. - Only the following options are allowed - - - 'on_input': Last feat map of neck inputs (i.e. backbone feature). - - 'on_lateral': Last feature map after lateral convs. - - 'on_output': The last output feature map after fpn convs. - extra_convs_on_inputs (bool, deprecated): Whether to apply extra convs - on the original feature from the backbone. If True, - it is equivalent to `add_extra_convs='on_input'`. If False, it is - equivalent to set `add_extra_convs='on_output'`. Default to True. - relu_before_extra_convs (bool): Whether to apply relu before the extra - conv. Default: False. - no_norm_on_lateral (bool): Whether to apply norm on lateral. - Default: False. - conv_cfg (dict): Config dict for convolution layer. Default: None. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - upsample_cfg (dict): Config dict for interpolate layer. - Default: dict(mode='nearest'). - init_cfg (dict or list[dict], optional): Initialization config dict. - - Example: - >>> import torch - >>> in_channels = [2, 3, 5, 7] - >>> scales = [340, 170, 84, 43] - >>> inputs = [torch.rand(1, c, s, s) - ... for c, s in zip(in_channels, scales)] - >>> self = FPN(in_channels, 11, len(in_channels)).eval() - >>> outputs = self.forward(inputs) - >>> for i in range(len(outputs)): - ... print(f'outputs[{i}].shape = {outputs[i].shape}') - outputs[0].shape = torch.Size([1, 11, 340, 340]) - outputs[1].shape = torch.Size([1, 11, 170, 170]) - outputs[2].shape = torch.Size([1, 11, 84, 84]) - outputs[3].shape = torch.Size([1, 11, 43, 43]) - """ - - def __init__(self, - in_channels, - out_channels, - num_outs, - start_level=0, - end_level=-1, - add_extra_convs=False, - extra_convs_on_inputs=False, - relu_before_extra_convs=False, - no_norm_on_lateral=False, - conv_cfg=None, - norm_cfg=None, - act_cfg=None, - upsample_cfg=dict(mode='nearest'), - init_cfg=dict( - type='Xavier', layer='Conv2d', distribution='uniform')): - super(FPN, self).__init__(init_cfg) - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.num_ins = len(in_channels) - self.num_outs = num_outs - self.relu_before_extra_convs = relu_before_extra_convs - self.no_norm_on_lateral = no_norm_on_lateral - self.fp16_enabled = False - self.upsample_cfg = upsample_cfg.copy() - - if end_level == -1: - self.backbone_end_level = self.num_ins - assert num_outs >= self.num_ins - start_level - else: - # if end_level < inputs, no extra level is allowed - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - assert num_outs == end_level - start_level - self.start_level = start_level - self.end_level = end_level - self.add_extra_convs = add_extra_convs - assert isinstance(add_extra_convs, (str, bool)) - if isinstance(add_extra_convs, str): - # Extra_convs_source choices: 'on_input', 'on_lateral', 'on_output' - assert add_extra_convs in ('on_input', 'on_lateral', 'on_output') - elif add_extra_convs: # True - if extra_convs_on_inputs: - # For compatibility with previous release - # TODO: deprecate `extra_convs_on_inputs` - self.add_extra_convs = 'on_input' - else: - self.add_extra_convs = 'on_output' - - self.lateral_convs = nn.ModuleList() - self.fpn_convs = nn.ModuleList() - - for i in range(self.start_level, self.backbone_end_level): - l_conv = ConvModule( - in_channels[i], - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg if not self.no_norm_on_lateral else None, - act_cfg=act_cfg, - inplace=False) - fpn_conv = ConvModule( - out_channels, - out_channels, - 3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - - self.lateral_convs.append(l_conv) - self.fpn_convs.append(fpn_conv) - - # add extra conv layers (e.g., RetinaNet) - extra_levels = num_outs - self.backbone_end_level + self.start_level - if self.add_extra_convs and extra_levels >= 1: - for i in range(extra_levels): - if i == 0 and self.add_extra_convs == 'on_input': - in_channels = self.in_channels[self.backbone_end_level - 1] - else: - in_channels = out_channels - extra_fpn_conv = ConvModule( - in_channels, - out_channels, - 3, - stride=2, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - self.fpn_convs.append(extra_fpn_conv) - - @auto_fp16() - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # build laterals - laterals = [ - lateral_conv(inputs[i + self.start_level]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - - # build top-down path - used_backbone_levels = len(laterals) - for i in range(used_backbone_levels - 1, 0, -1): - # In some cases, fixing `scale factor` (e.g. 2) is preferred, but - # it cannot co-exist with `size` in `F.interpolate`. - if 'scale_factor' in self.upsample_cfg: - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], **self.upsample_cfg) - else: - prev_shape = laterals[i - 1].shape[2:] - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], size=prev_shape, **self.upsample_cfg) - - # build outputs - # part 1: from original levels - outs = [ - self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels) - ] - # part 2: add extra levels - if self.num_outs > len(outs): - # use max pool to get more levels on top of outputs - # (e.g., Faster R-CNN, Mask R-CNN) - if not self.add_extra_convs: - for i in range(self.num_outs - used_backbone_levels): - outs.append(F.max_pool2d(outs[-1], 1, stride=2)) - # add conv layers on top of original feature maps (RetinaNet) - else: - if self.add_extra_convs == 'on_input': - extra_source = inputs[self.backbone_end_level - 1] - elif self.add_extra_convs == 'on_lateral': - extra_source = laterals[-1] - elif self.add_extra_convs == 'on_output': - extra_source = outs[-1] - else: - raise NotImplementedError - outs.append(self.fpn_convs[used_backbone_levels](extra_source)) - for i in range(used_backbone_levels + 1, self.num_outs): - if self.relu_before_extra_convs: - outs.append(self.fpn_convs[i](F.relu(outs[-1]))) - else: - outs.append(self.fpn_convs[i](outs[-1])) - return tuple(outs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/ic_neck.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/ic_neck.py deleted file mode 100644 index a5d81cef8..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/ic_neck.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -class CascadeFeatureFusion(BaseModule): - """Cascade Feature Fusion Unit in ICNet. - - Args: - low_channels (int): The number of input channels for - low resolution feature map. - high_channels (int): The number of input channels for - high resolution feature map. - out_channels (int): The number of output channels. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Returns: - x (Tensor): The output tensor of shape (N, out_channels, H, W). - x_low (Tensor): The output tensor of shape (N, out_channels, H, W) - for Cascade Label Guidance in auxiliary heads. - """ - - def __init__(self, - low_channels, - high_channels, - out_channels, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(CascadeFeatureFusion, self).__init__(init_cfg=init_cfg) - self.align_corners = align_corners - self.conv_low = ConvModule( - low_channels, - out_channels, - 3, - padding=2, - dilation=2, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.conv_high = ConvModule( - high_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, x_low, x_high): - x_low = resize( - x_low, - size=x_high.size()[2:], - mode='bilinear', - align_corners=self.align_corners) - # Note: Different from original paper, `x_low` is underwent - # `self.conv_low` rather than another 1x1 conv classifier - # before being used for auxiliary head. - x_low = self.conv_low(x_low) - x_high = self.conv_high(x_high) - x = x_low + x_high - x = F.relu(x, inplace=True) - return x, x_low - - -@NECKS.register_module() -class ICNeck(BaseModule): - """ICNet for Real-Time Semantic Segmentation on High-Resolution Images. - - This head is the implementation of `ICHead - `_. - - Args: - in_channels (int): The number of input image channels. Default: 3. - out_channels (int): The numbers of output feature channels. - Default: 128. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(64, 256, 256), - out_channels=128, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(ICNeck, self).__init__(init_cfg=init_cfg) - assert len(in_channels) == 3, 'Length of input channels \ - must be 3!' - - self.in_channels = in_channels - self.out_channels = out_channels - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.align_corners = align_corners - self.cff_24 = CascadeFeatureFusion( - self.in_channels[2], - self.in_channels[1], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - self.cff_12 = CascadeFeatureFusion( - self.out_channels, - self.in_channels[0], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - def forward(self, inputs): - assert len(inputs) == 3, 'Length of input feature \ - maps must be 3!' - - x_sub1, x_sub2, x_sub4 = inputs - x_cff_24, x_24 = self.cff_24(x_sub4, x_sub2) - x_cff_12, x_12 = self.cff_12(x_cff_24, x_sub1) - # Note: `x_cff_12` is used for decode_head, - # `x_24` and `x_12` are used for auxiliary head. - return x_24, x_12, x_cff_12 diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/jpu.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/jpu.py deleted file mode 100644 index 3cc6b9f42..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/jpu.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, DepthwiseSeparableConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class JPU(BaseModule): - """FastFCN: Rethinking Dilated Convolution in the Backbone - for Semantic Segmentation. - - This Joint Pyramid Upsampling (JPU) neck is the implementation of - `FastFCN `_. - - Args: - in_channels (Tuple[int], optional): The number of input channels - for each convolution operations before upsampling. - Default: (512, 1024, 2048). - mid_channels (int): The number of output channels of JPU. - Default: 512. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - dilations (tuple[int]): Dilation rate of each Depthwise - Separable ConvModule. Default: (1, 2, 4, 8). - align_corners (bool, optional): The align_corners argument of - resize operation. Default: False. - conv_cfg (dict | None): Config of conv layers. - Default: None. - norm_cfg (dict | None): Config of norm layers. - Default: dict(type='BN'). - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU'). - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(512, 1024, 2048), - mid_channels=512, - start_level=0, - end_level=-1, - dilations=(1, 2, 4, 8), - align_corners=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(JPU, self).__init__(init_cfg=init_cfg) - assert isinstance(in_channels, tuple) - assert isinstance(dilations, tuple) - self.in_channels = in_channels - self.mid_channels = mid_channels - self.start_level = start_level - self.num_ins = len(in_channels) - if end_level == -1: - self.backbone_end_level = self.num_ins - else: - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - - self.dilations = dilations - self.align_corners = align_corners - - self.conv_layers = nn.ModuleList() - self.dilation_layers = nn.ModuleList() - for i in range(self.start_level, self.backbone_end_level): - conv_layer = nn.Sequential( - ConvModule( - self.in_channels[i], - self.mid_channels, - kernel_size=3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.conv_layers.append(conv_layer) - for i in range(len(dilations)): - dilation_layer = nn.Sequential( - DepthwiseSeparableConvModule( - in_channels=(self.backbone_end_level - self.start_level) * - self.mid_channels, - out_channels=self.mid_channels, - kernel_size=3, - stride=1, - padding=dilations[i], - dilation=dilations[i], - dw_norm_cfg=norm_cfg, - dw_act_cfg=None, - pw_norm_cfg=norm_cfg, - pw_act_cfg=act_cfg)) - self.dilation_layers.append(dilation_layer) - - def forward(self, inputs): - """Forward function.""" - assert len(inputs) == len(self.in_channels), 'Length of inputs must \ - be the same with self.in_channels!' - - feats = [ - self.conv_layers[i - self.start_level](inputs[i]) - for i in range(self.start_level, self.backbone_end_level) - ] - - h, w = feats[0].shape[2:] - for i in range(1, len(feats)): - feats[i] = resize( - feats[i], - size=(h, w), - mode='bilinear', - align_corners=self.align_corners) - - feat = torch.cat(feats, dim=1) - concat_feat = torch.cat([ - self.dilation_layers[i](feat) for i in range(len(self.dilations)) - ], - dim=1) - - outs = [] - - # Default: outs[2] is the output of JPU for decoder head, outs[1] is - # the feature map from backbone for auxiliary head. Additionally, - # outs[0] can also be used for auxiliary head. - for i in range(self.start_level, self.backbone_end_level - 1): - outs.append(inputs[i]) - outs.append(concat_feat) - return tuple(outs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/mla_neck.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/mla_neck.py deleted file mode 100644 index 1513e296d..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/mla_neck.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, build_norm_layer - -from ..builder import NECKS - - -class MLAModule(nn.Module): - - def __init__(self, - in_channels=[1024, 1024, 1024, 1024], - out_channels=256, - norm_cfg=None, - act_cfg=None): - super(MLAModule, self).__init__() - self.channel_proj = nn.ModuleList() - for i in range(len(in_channels)): - self.channel_proj.append( - ConvModule( - in_channels=in_channels[i], - out_channels=out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.feat_extract = nn.ModuleList() - for i in range(len(in_channels)): - self.feat_extract.append( - ConvModule( - in_channels=out_channels, - out_channels=out_channels, - kernel_size=3, - padding=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, inputs): - - # feat_list -> [p2, p3, p4, p5] - feat_list = [] - for x, conv in zip(inputs, self.channel_proj): - feat_list.append(conv(x)) - - # feat_list -> [p5, p4, p3, p2] - # mid_list -> [m5, m4, m3, m2] - feat_list = feat_list[::-1] - mid_list = [] - for feat in feat_list: - if len(mid_list) == 0: - mid_list.append(feat) - else: - mid_list.append(mid_list[-1] + feat) - - # mid_list -> [m5, m4, m3, m2] - # out_list -> [o2, o3, o4, o5] - out_list = [] - for mid, conv in zip(mid_list, self.feat_extract): - out_list.append(conv(mid)) - - return tuple(out_list) - - -@NECKS.register_module() -class MLANeck(nn.Module): - """Multi-level Feature Aggregation. - - This neck is `The Multi-level Feature Aggregation construction of - SETR `_. - - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - norm_layer (dict): Config dict for input normalization. - Default: norm_layer=dict(type='LN', eps=1e-6, requires_grad=True). - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - norm_layer=dict(type='LN', eps=1e-6, requires_grad=True), - norm_cfg=None, - act_cfg=None): - super(MLANeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - - # In order to build general vision transformer backbone, we have to - # move MLA to neck. - self.norm = nn.ModuleList([ - build_norm_layer(norm_layer, in_channels[i])[1] - for i in range(len(in_channels)) - ]) - - self.mla = MLAModule( - in_channels=in_channels, - out_channels=out_channels, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # Convert from nchw to nlc - outs = [] - for i in range(len(inputs)): - x = inputs[i] - n, c, h, w = x.shape - x = x.reshape(n, c, h * w).transpose(2, 1).contiguous() - x = self.norm[i](x) - x = x.transpose(1, 2).reshape(n, c, h, w).contiguous() - outs.append(x) - - outs = self.mla(outs) - return tuple(outs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/multilevel_neck.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/multilevel_neck.py deleted file mode 100644 index 5151f8762..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/necks/multilevel_neck.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, xavier_init - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class MultiLevelNeck(nn.Module): - """MultiLevelNeck. - - A neck structure connect vit backbone and decoder_heads. - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - scales (List[float]): Scale factors for each input feature map. - Default: [0.5, 1, 2, 4] - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - scales=[0.5, 1, 2, 4], - norm_cfg=None, - act_cfg=None): - super(MultiLevelNeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.scales = scales - self.num_outs = len(scales) - self.lateral_convs = nn.ModuleList() - self.convs = nn.ModuleList() - for in_channel in in_channels: - self.lateral_convs.append( - ConvModule( - in_channel, - out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - for _ in range(self.num_outs): - self.convs.append( - ConvModule( - out_channels, - out_channels, - kernel_size=3, - padding=1, - stride=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - # default init_weights for conv(msra) and norm in ConvModule - def init_weights(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - xavier_init(m, distribution='uniform') - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - inputs = [ - lateral_conv(inputs[i]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - # for len(inputs) not equal to self.num_outs - if len(inputs) == 1: - inputs = [inputs[0] for _ in range(self.num_outs)] - outs = [] - for i in range(self.num_outs): - x_resize = resize( - inputs[i], scale_factor=self.scales[i], mode='bilinear') - outs.append(self.convs[i](x_resize)) - return tuple(outs) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/__init__.py deleted file mode 100644 index 387c858bd..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseSegmentor -from .cascade_encoder_decoder import CascadeEncoderDecoder -from .encoder_decoder import EncoderDecoder - -__all__ = ['BaseSegmentor', 'EncoderDecoder', 'CascadeEncoderDecoder'] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/base.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/base.py deleted file mode 100644 index 76dc8f075..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/base.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from abc import ABCMeta, abstractmethod -from collections import OrderedDict - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import BaseModule, auto_fp16 - - -class BaseSegmentor(BaseModule, metaclass=ABCMeta): - """Base class for segmentors.""" - - def __init__(self, init_cfg=None): - super(BaseSegmentor, self).__init__(init_cfg) - self.fp16_enabled = False - - @property - def with_neck(self): - """bool: whether the segmentor has neck""" - return hasattr(self, 'neck') and self.neck is not None - - @property - def with_auxiliary_head(self): - """bool: whether the segmentor has auxiliary head""" - return hasattr(self, - 'auxiliary_head') and self.auxiliary_head is not None - - @property - def with_decode_head(self): - """bool: whether the segmentor has decode head""" - return hasattr(self, 'decode_head') and self.decode_head is not None - - @abstractmethod - def extract_feat(self, imgs): - """Placeholder for extract features from images.""" - pass - - @abstractmethod - def encode_decode(self, img, img_metas): - """Placeholder for encode images with backbone and decode into a - semantic segmentation map of the same size as input.""" - pass - - @abstractmethod - def forward_train(self, imgs, img_metas, **kwargs): - """Placeholder for Forward function for training.""" - pass - - @abstractmethod - def simple_test(self, img, img_meta, **kwargs): - """Placeholder for single image test.""" - pass - - @abstractmethod - def aug_test(self, imgs, img_metas, **kwargs): - """Placeholder for augmentation test.""" - pass - - def forward_test(self, imgs, img_metas, **kwargs): - """ - Args: - imgs (List[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains all images in the batch. - img_metas (List[List[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. - """ - for var, name in [(imgs, 'imgs'), (img_metas, 'img_metas')]: - if not isinstance(var, list): - raise TypeError(f'{name} must be a list, but got ' - f'{type(var)}') - - num_augs = len(imgs) - if num_augs != len(img_metas): - raise ValueError(f'num of augmentations ({len(imgs)}) != ' - f'num of image meta ({len(img_metas)})') - # all images in the same aug batch all of the same ori_shape and pad - # shape - for img_meta in img_metas: - ori_shapes = [_['ori_shape'] for _ in img_meta] - assert all(shape == ori_shapes[0] for shape in ori_shapes) - img_shapes = [_['img_shape'] for _ in img_meta] - assert all(shape == img_shapes[0] for shape in img_shapes) - pad_shapes = [_['pad_shape'] for _ in img_meta] - assert all(shape == pad_shapes[0] for shape in pad_shapes) - - if num_augs == 1: - return self.simple_test(imgs[0], img_metas[0], **kwargs) - else: - return self.aug_test(imgs, img_metas, **kwargs) - - @auto_fp16(apply_to=('img', )) - def forward(self, img, img_metas, return_loss=True, **kwargs): - """Calls either :func:`forward_train` or :func:`forward_test` depending - on whether ``return_loss`` is ``True``. - - Note this setting will change the expected inputs. When - ``return_loss=True``, img and img_meta are single-nested (i.e. Tensor - and List[dict]), and when ``resturn_loss=False``, img and img_meta - should be double nested (i.e. List[Tensor], List[List[dict]]), with - the outer list indicating test time augmentations. - """ - if return_loss: - return self.forward_train(img, img_metas, **kwargs) - else: - return self.forward_test(img, img_metas, **kwargs) - - def train_step(self, data_batch, optimizer, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating is also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer` | dict): The optimizer of - runner is passed to ``train_step()``. This argument is unused - and reserved. - - Returns: - dict: It should contain at least 3 keys: ``loss``, ``log_vars``, - ``num_samples``. - ``loss`` is a tensor for back propagation, which can be a - weighted sum of multiple losses. - ``log_vars`` contains all the variables to be sent to the - logger. - ``num_samples`` indicates the batch size (when the model is - DDP, it means the batch size on each GPU), which is used for - averaging the logs. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, - log_vars=log_vars, - num_samples=len(data_batch['img_metas'])) - - return outputs - - def val_step(self, data_batch, optimizer=None, **kwargs): - """The iteration step during validation. - - This method shares the same signature as :func:`train_step`, but used - during val epochs. Note that the evaluation after training epochs is - not implemented with this method, but an evaluation hook. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - log_vars_ = dict() - for loss_name, loss_value in log_vars.items(): - k = loss_name + '_val' - log_vars_[k] = loss_value - - outputs = dict( - loss=loss, - log_vars=log_vars_, - num_samples=len(data_batch['img_metas'])) - - return outputs - - @staticmethod - def _parse_losses(losses): - """Parse the raw outputs (losses) of the network. - - Args: - losses (dict): Raw output of the network, which usually contain - losses and other necessary information. - - Returns: - tuple[Tensor, dict]: (loss, log_vars), loss is the loss tensor - which may be a weighted sum of all losses, log_vars contains - all the variables to be sent to the logger. - """ - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(_value for _key, _value in log_vars.items() - if 'loss' in _key) - - # If the loss_vars has different length, raise assertion error - # to prevent GPUs from infinite waiting. - if dist.is_available() and dist.is_initialized(): - log_var_length = torch.tensor(len(log_vars), device=loss.device) - dist.all_reduce(log_var_length) - message = (f'rank {dist.get_rank()}' + - f' len(log_vars): {len(log_vars)}' + ' keys: ' + - ','.join(log_vars.keys()) + '\n') - assert log_var_length == len(log_vars) * dist.get_world_size(), \ - 'loss log variables are different across GPUs!\n' + message - - log_vars['loss'] = loss - for loss_name, loss_value in log_vars.items(): - # reduce loss when distributed training - if dist.is_available() and dist.is_initialized(): - loss_value = loss_value.data.clone() - dist.all_reduce(loss_value.div_(dist.get_world_size())) - log_vars[loss_name] = loss_value.item() - - return loss, log_vars - - def show_result(self, - img, - result, - palette=None, - win_name='', - show=False, - wait_time=0, - out_file=None, - opacity=0.5): - """Draw `result` over `img`. - - Args: - img (str or Tensor): The image to be displayed. - result (Tensor): The semantic segmentation results to draw over - `img`. - palette (list[list[int]]] | np.ndarray | None): The palette of - segmentation map. If None is given, random palette will be - generated. Default: None - win_name (str): The window name. - wait_time (int): Value of waitKey param. - Default: 0. - show (bool): Whether to show the image. - Default: False. - out_file (str or None): The filename to write the image. - Default: None. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - Returns: - img (Tensor): Only if not `show` or `out_file` - """ - img = mmcv.imread(img) - img = img.copy() - seg = result[0] - if palette is None: - if self.PALETTE is None: - # Get random state before set seed, - # and restore random state later. - # It will prevent loss of randomness, as the palette - # may be different in each iteration if not specified. - # See: https://github.com/open-mmlab/mmdetection/issues/5844 - state = np.random.get_state() - np.random.seed(42) - # random palette - palette = np.random.randint( - 0, 255, size=(len(self.CLASSES), 3)) - np.random.set_state(state) - else: - palette = self.PALETTE - palette = np.array(palette) - assert palette.shape[0] == len(self.CLASSES) - assert palette.shape[1] == 3 - assert len(palette.shape) == 2 - assert 0 < opacity <= 1.0 - color_seg = np.zeros((seg.shape[0], seg.shape[1], 3), dtype=np.uint8) - for label, color in enumerate(palette): - color_seg[seg == label, :] = color - # convert to BGR - color_seg = color_seg[..., ::-1] - - img = img * (1 - opacity) + color_seg * opacity - img = img.astype(np.uint8) - # if out_file specified, do not show image in window - if out_file is not None: - show = False - - if show: - mmcv.imshow(img, win_name, wait_time) - if out_file is not None: - mmcv.imwrite(img, out_file) - - if not (show or out_file): - warnings.warn('show==False and out_file is not specified, only ' - 'result image will be returned') - return img diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py deleted file mode 100644 index 1913a22e2..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from mmseg.core import add_prefix -from mmseg.ops import resize -from .. import builder -from ..builder import SEGMENTORS -from .encoder_decoder import EncoderDecoder - - -@SEGMENTORS.register_module() -class CascadeEncoderDecoder(EncoderDecoder): - """Cascade Encoder Decoder segmentors. - - CascadeEncoderDecoder almost the same as EncoderDecoder, while decoders of - CascadeEncoderDecoder are cascaded. The output of previous decoder_head - will be the input of next decoder_head. - """ - - def __init__(self, - num_stages, - backbone, - decode_head, - neck=None, - auxiliary_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - self.num_stages = num_stages - super(CascadeEncoderDecoder, self).__init__( - backbone=backbone, - decode_head=decode_head, - neck=neck, - auxiliary_head=auxiliary_head, - train_cfg=train_cfg, - test_cfg=test_cfg, - pretrained=pretrained, - init_cfg=init_cfg) - - def _init_decode_head(self, decode_head): - """Initialize ``decode_head``""" - assert isinstance(decode_head, list) - assert len(decode_head) == self.num_stages - self.decode_head = nn.ModuleList() - for i in range(self.num_stages): - self.decode_head.append(builder.build_head(decode_head[i])) - self.align_corners = self.decode_head[-1].align_corners - self.num_classes = self.decode_head[-1].num_classes - - def encode_decode(self, img, img_metas): - """Encode images with backbone and decode into a semantic segmentation - map of the same size as input.""" - x = self.extract_feat(img) - out = self.decode_head[0].forward_test(x, img_metas, self.test_cfg) - for i in range(1, self.num_stages): - out = self.decode_head[i].forward_test(x, out, img_metas, - self.test_cfg) - out = resize( - input=out, - size=img.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - return out - - def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for decode head in - training.""" - losses = dict() - - loss_decode = self.decode_head[0].forward_train( - x, img_metas, gt_semantic_seg, self.train_cfg) - - losses.update(add_prefix(loss_decode, 'decode_0')) - - for i in range(1, self.num_stages): - # forward test again, maybe unnecessary for most methods. - if i == 1: - prev_outputs = self.decode_head[0].forward_test( - x, img_metas, self.test_cfg) - else: - prev_outputs = self.decode_head[i - 1].forward_test( - x, prev_outputs, img_metas, self.test_cfg) - loss_decode = self.decode_head[i].forward_train( - x, prev_outputs, img_metas, gt_semantic_seg, self.train_cfg) - losses.update(add_prefix(loss_decode, f'decode_{i}')) - - return losses diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/encoder_decoder.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/encoder_decoder.py deleted file mode 100644 index 72467b469..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/segmentors/encoder_decoder.py +++ /dev/null @@ -1,284 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmseg.core import add_prefix -from mmseg.ops import resize -from .. import builder -from ..builder import SEGMENTORS -from .base import BaseSegmentor - - -@SEGMENTORS.register_module() -class EncoderDecoder(BaseSegmentor): - """Encoder Decoder segmentors. - - EncoderDecoder typically consists of backbone, decode_head, auxiliary_head. - Note that auxiliary_head is only used for deep supervision during training, - which could be dumped during inference. - """ - - def __init__(self, - backbone, - decode_head, - neck=None, - auxiliary_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - super(EncoderDecoder, self).__init__(init_cfg) - if pretrained is not None: - assert backbone.get('pretrained') is None, \ - 'both backbone and segmentor set pretrained weight' - backbone.pretrained = pretrained - self.backbone = builder.build_backbone(backbone) - if neck is not None: - self.neck = builder.build_neck(neck) - self._init_decode_head(decode_head) - self._init_auxiliary_head(auxiliary_head) - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - assert self.with_decode_head - - def _init_decode_head(self, decode_head): - """Initialize ``decode_head``""" - self.decode_head = builder.build_head(decode_head) - self.align_corners = self.decode_head.align_corners - self.num_classes = self.decode_head.num_classes - - def _init_auxiliary_head(self, auxiliary_head): - """Initialize ``auxiliary_head``""" - if auxiliary_head is not None: - if isinstance(auxiliary_head, list): - self.auxiliary_head = nn.ModuleList() - for head_cfg in auxiliary_head: - self.auxiliary_head.append(builder.build_head(head_cfg)) - else: - self.auxiliary_head = builder.build_head(auxiliary_head) - - def extract_feat(self, img): - """Extract features from images.""" - x = self.backbone(img) - if self.with_neck: - x = self.neck(x) - return x - - def encode_decode(self, img, img_metas): - """Encode images with backbone and decode into a semantic segmentation - map of the same size as input.""" - x = self.extract_feat(img) - out = self._decode_head_forward_test(x, img_metas) - out = resize( - input=out, - size=img.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - return out - - def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for decode head in - training.""" - losses = dict() - loss_decode = self.decode_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - - losses.update(add_prefix(loss_decode, 'decode')) - return losses - - def _decode_head_forward_test(self, x, img_metas): - """Run forward function and calculate loss for decode head in - inference.""" - seg_logits = self.decode_head.forward_test(x, img_metas, self.test_cfg) - return seg_logits - - def _auxiliary_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for auxiliary head in - training.""" - losses = dict() - if isinstance(self.auxiliary_head, nn.ModuleList): - for idx, aux_head in enumerate(self.auxiliary_head): - loss_aux = aux_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - losses.update(add_prefix(loss_aux, f'aux_{idx}')) - else: - loss_aux = self.auxiliary_head.forward_train( - x, img_metas, gt_semantic_seg, self.train_cfg) - losses.update(add_prefix(loss_aux, 'aux')) - - return losses - - def forward_dummy(self, img): - """Dummy forward function.""" - seg_logit = self.encode_decode(img, None) - - return seg_logit - - def forward_train(self, img, img_metas, gt_semantic_seg): - """Forward function for training. - - Args: - img (Tensor): Input images. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - - x = self.extract_feat(img) - - losses = dict() - - loss_decode = self._decode_head_forward_train(x, img_metas, - gt_semantic_seg) - losses.update(loss_decode) - - if self.with_auxiliary_head: - loss_aux = self._auxiliary_head_forward_train( - x, img_metas, gt_semantic_seg) - losses.update(loss_aux) - - return losses - - # TODO refactor - def slide_inference(self, img, img_meta, rescale): - """Inference by sliding-window with overlap. - - If h_crop > h_img or w_crop > w_img, the small patch will be used to - decode without padding. - """ - - h_stride, w_stride = self.test_cfg.stride - h_crop, w_crop = self.test_cfg.crop_size - batch_size, _, h_img, w_img = img.size() - num_classes = self.num_classes - h_grids = max(h_img - h_crop + h_stride - 1, 0) // h_stride + 1 - w_grids = max(w_img - w_crop + w_stride - 1, 0) // w_stride + 1 - preds = img.new_zeros((batch_size, num_classes, h_img, w_img)) - count_mat = img.new_zeros((batch_size, 1, h_img, w_img)) - for h_idx in range(h_grids): - for w_idx in range(w_grids): - y1 = h_idx * h_stride - x1 = w_idx * w_stride - y2 = min(y1 + h_crop, h_img) - x2 = min(x1 + w_crop, w_img) - y1 = max(y2 - h_crop, 0) - x1 = max(x2 - w_crop, 0) - crop_img = img[:, :, y1:y2, x1:x2] - crop_seg_logit = self.encode_decode(crop_img, img_meta) - preds += F.pad(crop_seg_logit, - (int(x1), int(preds.shape[3] - x2), int(y1), - int(preds.shape[2] - y2))) - - count_mat[:, :, y1:y2, x1:x2] += 1 - assert (count_mat == 0).sum() == 0 - if torch.onnx.is_in_onnx_export(): - # cast count_mat to constant while exporting to ONNX - count_mat = torch.from_numpy( - count_mat.cpu().detach().numpy()).to(device=img.device) - preds = preds / count_mat - if rescale: - preds = resize( - preds, - size=img_meta[0]['ori_shape'][:2], - mode='bilinear', - align_corners=self.align_corners, - warning=False) - return preds - - def whole_inference(self, img, img_meta, rescale): - """Inference with full image.""" - - seg_logit = self.encode_decode(img, img_meta) - if rescale: - # support dynamic shape for onnx - if torch.onnx.is_in_onnx_export(): - size = img.shape[2:] - else: - size = img_meta[0]['ori_shape'][:2] - seg_logit = resize( - seg_logit, - size=size, - mode='bilinear', - align_corners=self.align_corners, - warning=False) - - return seg_logit - - def inference(self, img, img_meta, rescale): - """Inference with slide/whole style. - - Args: - img (Tensor): The input image of shape (N, 3, H, W). - img_meta (dict): Image info dict where each dict has: 'img_shape', - 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - rescale (bool): Whether rescale back to original shape. - - Returns: - Tensor: The output segmentation map. - """ - - assert self.test_cfg.mode in ['slide', 'whole'] - ori_shape = img_meta[0]['ori_shape'] - assert all(_['ori_shape'] == ori_shape for _ in img_meta) - if self.test_cfg.mode == 'slide': - seg_logit = self.slide_inference(img, img_meta, rescale) - else: - seg_logit = self.whole_inference(img, img_meta, rescale) - output = F.softmax(seg_logit, dim=1) - flip = img_meta[0]['flip'] - if flip: - flip_direction = img_meta[0]['flip_direction'] - assert flip_direction in ['horizontal', 'vertical'] - if flip_direction == 'horizontal': - output = output.flip(dims=(3, )) - elif flip_direction == 'vertical': - output = output.flip(dims=(2, )) - - return output - - def simple_test(self, img, img_meta, rescale=True): - """Simple test with single image.""" - seg_logit = self.inference(img, img_meta, rescale) - seg_pred = seg_logit.argmax(dim=1) - if torch.onnx.is_in_onnx_export(): - # our inference backend only support 4D output - seg_pred = seg_pred.unsqueeze(0) - return seg_pred - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred - - def aug_test(self, imgs, img_metas, rescale=True): - """Test with augmentations. - - Only rescale=True is supported. - """ - # aug_test rescale all imgs back to ori_shape for now - assert rescale - # to save memory, we get augmented seg logit inplace - seg_logit = self.inference(imgs[0], img_metas[0], rescale) - for i in range(1, len(imgs)): - cur_seg_logit = self.inference(imgs[i], img_metas[i], rescale) - seg_logit += cur_seg_logit - seg_logit /= len(imgs) - seg_pred = seg_logit.argmax(dim=1) - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/__init__.py deleted file mode 100644 index 6d8329021..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .embed import PatchEmbed -from .inverted_residual import InvertedResidual, InvertedResidualV3 -from .make_divisible import make_divisible -from .res_layer import ResLayer -from .se_layer import SELayer -from .self_attention_block import SelfAttentionBlock -from .shape_convert import (nchw2nlc2nchw, nchw_to_nlc, nlc2nchw2nlc, - nlc_to_nchw) -from .up_conv_block import UpConvBlock - -__all__ = [ - 'ResLayer', 'SelfAttentionBlock', 'make_divisible', 'InvertedResidual', - 'UpConvBlock', 'InvertedResidualV3', 'SELayer', 'PatchEmbed', - 'nchw_to_nlc', 'nlc_to_nchw', 'nchw2nlc2nchw', 'nlc2nchw2nlc' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/embed.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/embed.py deleted file mode 100644 index 1515675e1..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/embed.py +++ /dev/null @@ -1,330 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -from typing import Sequence - -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner.base_module import BaseModule -from mmcv.utils import to_2tuple - - -class AdaptivePadding(nn.Module): - """Applies padding to input (if needed) so that input can get fully covered - by filter you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad zero around - input. The "corner" mode would pad zero to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel: - stride (int | tuple): Stride of the filter. Default: 1: - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - - super(AdaptivePadding, self).__init__() - - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The config dict for embedding - conv layer type selection. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int, optional): The slide stride of embedding conv. - Default: None (Would be set as `kernel_size`). - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only work when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=None, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super(PatchEmbed, self).__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adap_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adap_padding: - pad_h, pad_w = self.adap_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adap_padding: - x = self.adap_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map. Our implementation uses `nn.Unfold` to - merge patch, which is about 25% faster than original implementation. - Instead, we need to modify pretrained models for compatibility. - - Args: - in_channels (int): The num of input channels. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adap_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - - if self.adap_padding: - x = self.adap_padding(x) - H, W = x.shape[-2:] - - x = self.sampler(x) - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/inverted_residual.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/inverted_residual.py deleted file mode 100644 index c9cda7682..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/inverted_residual.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import ConvModule -from torch import nn -from torch.utils import checkpoint as cp - -from .se_layer import SELayer - - -class InvertedResidual(nn.Module): - """InvertedResidual block for MobileNetV2. - - Args: - in_channels (int): The input channels of the InvertedResidual block. - out_channels (int): The output channels of the InvertedResidual block. - stride (int): Stride of the middle (first) 3x3 convolution. - expand_ratio (int): Adjusts number of channels of the hidden layer - in InvertedResidual by this amount. - dilation (int): Dilation rate of depthwise conv. Default: 1 - conv_cfg (dict): Config dict for convolution layer. - Default: None, which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU6'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - stride, - expand_ratio, - dilation=1, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU6'), - with_cp=False, - **kwargs): - super(InvertedResidual, self).__init__() - self.stride = stride - assert stride in [1, 2], f'stride must in [1, 2]. ' \ - f'But received {stride}.' - self.with_cp = with_cp - self.use_res_connect = self.stride == 1 and in_channels == out_channels - hidden_dim = int(round(in_channels * expand_ratio)) - - layers = [] - if expand_ratio != 1: - layers.append( - ConvModule( - in_channels=in_channels, - out_channels=hidden_dim, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs)) - layers.extend([ - ConvModule( - in_channels=hidden_dim, - out_channels=hidden_dim, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - groups=hidden_dim, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs), - ConvModule( - in_channels=hidden_dim, - out_channels=out_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None, - **kwargs) - ]) - self.conv = nn.Sequential(*layers) - - def forward(self, x): - - def _inner_forward(x): - if self.use_res_connect: - return x + self.conv(x) - else: - return self.conv(x) - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out - - -class InvertedResidualV3(nn.Module): - """Inverted Residual Block for MobileNetV3. - - Args: - in_channels (int): The input channels of this Module. - out_channels (int): The output channels of this Module. - mid_channels (int): The input channels of the depthwise convolution. - kernel_size (int): The kernel size of the depthwise convolution. - Default: 3. - stride (int): The stride of the depthwise convolution. Default: 1. - se_cfg (dict): Config dict for se layer. Default: None, which means no - se layer. - with_expand_conv (bool): Use expand conv or not. If set False, - mid_channels must be the same with in_channels. Default: True. - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - mid_channels, - kernel_size=3, - stride=1, - se_cfg=None, - with_expand_conv=True, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - with_cp=False): - super(InvertedResidualV3, self).__init__() - self.with_res_shortcut = (stride == 1 and in_channels == out_channels) - assert stride in [1, 2] - self.with_cp = with_cp - self.with_se = se_cfg is not None - self.with_expand_conv = with_expand_conv - - if self.with_se: - assert isinstance(se_cfg, dict) - if not self.with_expand_conv: - assert mid_channels == in_channels - - if self.with_expand_conv: - self.expand_conv = ConvModule( - in_channels=in_channels, - out_channels=mid_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.depthwise_conv = ConvModule( - in_channels=mid_channels, - out_channels=mid_channels, - kernel_size=kernel_size, - stride=stride, - padding=kernel_size // 2, - groups=mid_channels, - conv_cfg=dict( - type='Conv2dAdaptivePadding') if stride == 2 else conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - if self.with_se: - self.se = SELayer(**se_cfg) - - self.linear_conv = ConvModule( - in_channels=mid_channels, - out_channels=out_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - def forward(self, x): - - def _inner_forward(x): - out = x - - if self.with_expand_conv: - out = self.expand_conv(out) - - out = self.depthwise_conv(out) - - if self.with_se: - out = self.se(out) - - out = self.linear_conv(out) - - if self.with_res_shortcut: - return x + out - else: - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/make_divisible.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/make_divisible.py deleted file mode 100644 index ed42c2eee..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/make_divisible.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def make_divisible(value, divisor, min_value=None, min_ratio=0.9): - """Make divisible function. - - This function rounds the channel number to the nearest value that can be - divisible by the divisor. It is taken from the original tf repo. It ensures - that all layers have a channel number that is divisible by divisor. It can - be seen here: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py # noqa - - Args: - value (int): The original channel number. - divisor (int): The divisor to fully divide the channel number. - min_value (int): The minimum value of the output channel. - Default: None, means that the minimum value equal to the divisor. - min_ratio (float): The minimum ratio of the rounded channel number to - the original channel number. Default: 0.9. - - Returns: - int: The modified output channel number. - """ - - if min_value is None: - min_value = divisor - new_value = max(min_value, int(value + divisor / 2) // divisor * divisor) - # Make sure that round down does not go down by more than (1-min_ratio). - if new_value < min_ratio * value: - new_value += divisor - return new_value diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/res_layer.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/res_layer.py deleted file mode 100644 index 190a0c5d5..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/res_layer.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner import Sequential -from torch import nn as nn - - -class ResLayer(Sequential): - """ResLayer to build ResNet style backbone. - - Args: - block (nn.Module): block used to build ResLayer. - inplanes (int): inplanes of block. - planes (int): planes of block. - num_blocks (int): number of blocks. - stride (int): stride of the first block. Default: 1 - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False - conv_cfg (dict): dictionary to construct and config conv layer. - Default: None - norm_cfg (dict): dictionary to construct and config norm layer. - Default: dict(type='BN') - multi_grid (int | None): Multi grid dilation rates of last - stage. Default: None - contract_dilation (bool): Whether contract first dilation of each layer - Default: False - """ - - def __init__(self, - block, - inplanes, - planes, - num_blocks, - stride=1, - dilation=1, - avg_down=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - multi_grid=None, - contract_dilation=False, - **kwargs): - self.block = block - - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = [] - conv_stride = stride - if avg_down: - conv_stride = 1 - downsample.append( - nn.AvgPool2d( - kernel_size=stride, - stride=stride, - ceil_mode=True, - count_include_pad=False)) - downsample.extend([ - build_conv_layer( - conv_cfg, - inplanes, - planes * block.expansion, - kernel_size=1, - stride=conv_stride, - bias=False), - build_norm_layer(norm_cfg, planes * block.expansion)[1] - ]) - downsample = nn.Sequential(*downsample) - - layers = [] - if multi_grid is None: - if dilation > 1 and contract_dilation: - first_dilation = dilation // 2 - else: - first_dilation = dilation - else: - first_dilation = multi_grid[0] - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=stride, - dilation=first_dilation, - downsample=downsample, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - inplanes = planes * block.expansion - for i in range(1, num_blocks): - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=1, - dilation=dilation if multi_grid is None else multi_grid[i], - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - super(ResLayer, self).__init__(*layers) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/se_layer.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/se_layer.py deleted file mode 100644 index 16f52aa5c..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/se_layer.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch.nn as nn -from mmcv.cnn import ConvModule - -from .make_divisible import make_divisible - - -class SELayer(nn.Module): - """Squeeze-and-Excitation Module. - - Args: - channels (int): The input (and output) channels of the SE layer. - ratio (int): Squeeze ratio in SELayer, the intermediate channel will be - ``int(channels/ratio)``. Default: 16. - conv_cfg (None or dict): Config dict for convolution layer. - Default: None, which means using conv2d. - act_cfg (dict or Sequence[dict]): Config dict for activation layer. - If act_cfg is a dict, two activation layers will be configured - by this dict. If act_cfg is a sequence of dicts, the first - activation layer will be configured by the first dict and the - second activation layer will be configured by the second dict. - Default: (dict(type='ReLU'), dict(type='HSigmoid', bias=3.0, - divisor=6.0)). - """ - - def __init__(self, - channels, - ratio=16, - conv_cfg=None, - act_cfg=(dict(type='ReLU'), - dict(type='HSigmoid', bias=3.0, divisor=6.0))): - super(SELayer, self).__init__() - if isinstance(act_cfg, dict): - act_cfg = (act_cfg, act_cfg) - assert len(act_cfg) == 2 - assert mmcv.is_tuple_of(act_cfg, dict) - self.global_avgpool = nn.AdaptiveAvgPool2d(1) - self.conv1 = ConvModule( - in_channels=channels, - out_channels=make_divisible(channels // ratio, 8), - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[0]) - self.conv2 = ConvModule( - in_channels=make_divisible(channels // ratio, 8), - out_channels=channels, - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[1]) - - def forward(self, x): - out = self.global_avgpool(x) - out = self.conv1(out) - out = self.conv2(out) - return x * out diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/self_attention_block.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/self_attention_block.py deleted file mode 100644 index c945fa716..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/self_attention_block.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.cnn import ConvModule, constant_init -from torch import nn as nn -from torch.nn import functional as F - - -class SelfAttentionBlock(nn.Module): - """General self-attention block/non-local block. - - Please refer to https://arxiv.org/abs/1706.03762 for details about key, - query and value. - - Args: - key_in_channels (int): Input channels of key feature. - query_in_channels (int): Input channels of query feature. - channels (int): Output channels of key/query transform. - out_channels (int): Output channels. - share_key_query (bool): Whether share projection weight between key - and query projection. - query_downsample (nn.Module): Query downsample module. - key_downsample (nn.Module): Key downsample module. - key_query_num_convs (int): Number of convs for key/query projection. - value_num_convs (int): Number of convs for value projection. - matmul_norm (bool): Whether normalize attention map with sqrt of - channels - with_out (bool): Whether use out projection. - conv_cfg (dict|None): Config of conv layers. - norm_cfg (dict|None): Config of norm layers. - act_cfg (dict|None): Config of activation layers. - """ - - def __init__(self, key_in_channels, query_in_channels, channels, - out_channels, share_key_query, query_downsample, - key_downsample, key_query_num_convs, value_out_num_convs, - key_query_norm, value_out_norm, matmul_norm, with_out, - conv_cfg, norm_cfg, act_cfg): - super(SelfAttentionBlock, self).__init__() - if share_key_query: - assert key_in_channels == query_in_channels - self.key_in_channels = key_in_channels - self.query_in_channels = query_in_channels - self.out_channels = out_channels - self.channels = channels - self.share_key_query = share_key_query - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.key_project = self.build_project( - key_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if share_key_query: - self.query_project = self.key_project - else: - self.query_project = self.build_project( - query_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.value_project = self.build_project( - key_in_channels, - channels if with_out else out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if with_out: - self.out_project = self.build_project( - channels, - out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.out_project = None - - self.query_downsample = query_downsample - self.key_downsample = key_downsample - self.matmul_norm = matmul_norm - - self.init_weights() - - def init_weights(self): - """Initialize weight of later layer.""" - if self.out_project is not None: - if not isinstance(self.out_project, ConvModule): - constant_init(self.out_project, 0) - - def build_project(self, in_channels, channels, num_convs, use_conv_module, - conv_cfg, norm_cfg, act_cfg): - """Build projection layer for key/query/value/out.""" - if use_conv_module: - convs = [ - ConvModule( - in_channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - ] - for _ in range(num_convs - 1): - convs.append( - ConvModule( - channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - else: - convs = [nn.Conv2d(in_channels, channels, 1)] - for _ in range(num_convs - 1): - convs.append(nn.Conv2d(channels, channels, 1)) - if len(convs) > 1: - convs = nn.Sequential(*convs) - else: - convs = convs[0] - return convs - - def forward(self, query_feats, key_feats): - """Forward function.""" - batch_size = query_feats.size(0) - query = self.query_project(query_feats) - if self.query_downsample is not None: - query = self.query_downsample(query) - query = query.reshape(*query.shape[:2], -1) - query = query.permute(0, 2, 1).contiguous() - - key = self.key_project(key_feats) - value = self.value_project(key_feats) - if self.key_downsample is not None: - key = self.key_downsample(key) - value = self.key_downsample(value) - key = key.reshape(*key.shape[:2], -1) - value = value.reshape(*value.shape[:2], -1) - value = value.permute(0, 2, 1).contiguous() - - sim_map = torch.matmul(query, key) - if self.matmul_norm: - sim_map = (self.channels**-.5) * sim_map - sim_map = F.softmax(sim_map, dim=-1) - - context = torch.matmul(sim_map, value) - context = context.permute(0, 2, 1).contiguous() - context = context.reshape(batch_size, -1, *query_feats.shape[2:]) - if self.out_project is not None: - context = self.out_project(context) - return context diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/shape_convert.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/shape_convert.py deleted file mode 100644 index cce1e220b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/shape_convert.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def nlc_to_nchw(x, hw_shape): - """Convert [N, L, C] shape tensor to [N, C, H, W] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, L, C] before conversion. - hw_shape (Sequence[int]): The height and width of output feature map. - - Returns: - Tensor: The output tensor of shape [N, C, H, W] after conversion. - """ - H, W = hw_shape - assert len(x.shape) == 3 - B, L, C = x.shape - assert L == H * W, 'The seq_len doesn\'t match H, W' - return x.transpose(1, 2).reshape(B, C, H, W) - - -def nchw_to_nlc(x): - """Flatten [N, C, H, W] shape tensor to [N, L, C] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, C, H, W] before conversion. - - Returns: - Tensor: The output tensor of shape [N, L, C] after conversion. - """ - assert len(x.shape) == 4 - return x.flatten(2).transpose(1, 2).contiguous() - - -def nchw2nlc2nchw(module, x, contiguous=False, **kwargs): - """Flatten [N, C, H, W] shape tensor `x` to [N, L, C] shape tensor. Use the - reshaped tensor as the input of `module`, and the convert the output of - `module`, whose shape is. - - [N, L, C], to [N, C, H, W]. - - Args: - module (Callable): A callable object the takes a tensor - with shape [N, L, C] as input. - x (Tensor): The input tensor of shape [N, C, H, W]. - contiguous: - contiguous (Bool): Whether to make the tensor contiguous - after each shape transform. - - Returns: - Tensor: The output tensor of shape [N, C, H, W]. - - Example: - >>> import torch - >>> import torch.nn as nn - >>> norm = nn.LayerNorm(4) - >>> feature_map = torch.rand(4, 4, 5, 5) - >>> output = nchw2nlc2nchw(norm, feature_map) - """ - B, C, H, W = x.shape - if not contiguous: - x = x.flatten(2).transpose(1, 2) - x = module(x, **kwargs) - x = x.transpose(1, 2).reshape(B, C, H, W) - else: - x = x.flatten(2).transpose(1, 2).contiguous() - x = module(x, **kwargs) - x = x.transpose(1, 2).reshape(B, C, H, W).contiguous() - return x - - -def nlc2nchw2nlc(module, x, hw_shape, contiguous=False, **kwargs): - """Convert [N, L, C] shape tensor `x` to [N, C, H, W] shape tensor. Use the - reshaped tensor as the input of `module`, and convert the output of - `module`, whose shape is. - - [N, C, H, W], to [N, L, C]. - - Args: - module (Callable): A callable object the takes a tensor - with shape [N, C, H, W] as input. - x (Tensor): The input tensor of shape [N, L, C]. - hw_shape: (Sequence[int]): The height and width of the - feature map with shape [N, C, H, W]. - contiguous (Bool): Whether to make the tensor contiguous - after each shape transform. - - Returns: - Tensor: The output tensor of shape [N, L, C]. - - Example: - >>> import torch - >>> import torch.nn as nn - >>> conv = nn.Conv2d(16, 16, 3, 1, 1) - >>> feature_map = torch.rand(4, 25, 16) - >>> output = nlc2nchw2nlc(conv, feature_map, (5, 5)) - """ - H, W = hw_shape - assert len(x.shape) == 3 - B, L, C = x.shape - assert L == H * W, 'The seq_len doesn\'t match H, W' - if not contiguous: - x = x.transpose(1, 2).reshape(B, C, H, W) - x = module(x, **kwargs) - x = x.flatten(2).transpose(1, 2) - else: - x = x.transpose(1, 2).reshape(B, C, H, W).contiguous() - x = module(x, **kwargs) - x = x.flatten(2).transpose(1, 2).contiguous() - return x diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/up_conv_block.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/up_conv_block.py deleted file mode 100644 index d8396d9c2..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/models/utils/up_conv_block.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, build_upsample_layer - - -class UpConvBlock(nn.Module): - """Upsample convolution block in decoder for UNet. - - This upsample convolution block consists of one upsample module - followed by one convolution block. The upsample module expands the - high-level low-resolution feature map and the convolution block fuses - the upsampled high-level low-resolution feature map and the low-level - high-resolution feature map from encoder. - - Args: - conv_block (nn.Sequential): Sequential of convolutional layers. - in_channels (int): Number of input channels of the high-level - skip_channels (int): Number of input channels of the low-level - high-resolution feature map from encoder. - out_channels (int): Number of output channels. - num_convs (int): Number of convolutional layers in the conv_block. - Default: 2. - stride (int): Stride of convolutional layer in conv_block. Default: 1. - dilation (int): Dilation rate of convolutional layer in conv_block. - Default: 1. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - conv_cfg (dict | None): Config dict for convolution layer. - Default: None. - norm_cfg (dict | None): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict | None): Config dict for activation layer in ConvModule. - Default: dict(type='ReLU'). - upsample_cfg (dict): The upsample config of the upsample module in - decoder. Default: dict(type='InterpConv'). If the size of - high-level feature map is the same as that of skip feature map - (low-level feature map from encoder), it does not need upsample the - high-level feature map and the upsample_cfg is None. - dcn (bool): Use deformable convolution in convolutional layer or not. - Default: None. - plugins (dict): plugins for convolutional layers. Default: None. - """ - - def __init__(self, - conv_block, - in_channels, - skip_channels, - out_channels, - num_convs=2, - stride=1, - dilation=1, - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - upsample_cfg=dict(type='InterpConv'), - dcn=None, - plugins=None): - super(UpConvBlock, self).__init__() - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.conv_block = conv_block( - in_channels=2 * skip_channels, - out_channels=out_channels, - num_convs=num_convs, - stride=stride, - dilation=dilation, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - dcn=None, - plugins=None) - if upsample_cfg is not None: - self.upsample = build_upsample_layer( - cfg=upsample_cfg, - in_channels=in_channels, - out_channels=skip_channels, - with_cp=with_cp, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.upsample = ConvModule( - in_channels, - skip_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, skip, x): - """Forward function.""" - - x = self.upsample(x) - out = torch.cat([skip, x], dim=1) - out = self.conv_block(out) - - return out diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/ops/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/ops/__init__.py deleted file mode 100644 index bc075cd4e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/ops/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .encoding import Encoding -from .wrappers import Upsample, resize - -__all__ = ['Upsample', 'resize', 'Encoding'] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/ops/encoding.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/ops/encoding.py deleted file mode 100644 index f397cc54e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/ops/encoding.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn -from torch.nn import functional as F - - -class Encoding(nn.Module): - """Encoding Layer: a learnable residual encoder. - - Input is of shape (batch_size, channels, height, width). - Output is of shape (batch_size, num_codes, channels). - - Args: - channels: dimension of the features or feature channels - num_codes: number of code words - """ - - def __init__(self, channels, num_codes): - super(Encoding, self).__init__() - # init codewords and smoothing factor - self.channels, self.num_codes = channels, num_codes - std = 1. / ((num_codes * channels)**0.5) - # [num_codes, channels] - self.codewords = nn.Parameter( - torch.empty(num_codes, channels, - dtype=torch.float).uniform_(-std, std), - requires_grad=True) - # [num_codes] - self.scale = nn.Parameter( - torch.empty(num_codes, dtype=torch.float).uniform_(-1, 0), - requires_grad=True) - - @staticmethod - def scaled_l2(x, codewords, scale): - num_codes, channels = codewords.size() - batch_size = x.size(0) - reshaped_scale = scale.view((1, 1, num_codes)) - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - - scaled_l2_norm = reshaped_scale * ( - expanded_x - reshaped_codewords).pow(2).sum(dim=3) - return scaled_l2_norm - - @staticmethod - def aggregate(assignment_weights, x, codewords): - num_codes, channels = codewords.size() - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - batch_size = x.size(0) - - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - encoded_feat = (assignment_weights.unsqueeze(3) * - (expanded_x - reshaped_codewords)).sum(dim=1) - return encoded_feat - - def forward(self, x): - assert x.dim() == 4 and x.size(1) == self.channels - # [batch_size, channels, height, width] - batch_size = x.size(0) - # [batch_size, height x width, channels] - x = x.view(batch_size, self.channels, -1).transpose(1, 2).contiguous() - # assignment_weights: [batch_size, channels, num_codes] - assignment_weights = F.softmax( - self.scaled_l2(x, self.codewords, self.scale), dim=2) - # aggregate - encoded_feat = self.aggregate(assignment_weights, x, self.codewords) - return encoded_feat - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(Nx{self.channels}xHxW =>Nx{self.num_codes}' \ - f'x{self.channels})' - return repr_str diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/ops/wrappers.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/ops/wrappers.py deleted file mode 100644 index ce67e4beb..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/ops/wrappers.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.nn.functional as F - - -def resize(input, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None, - warning=True): - if warning: - if size is not None and align_corners: - input_h, input_w = tuple(int(x) for x in input.shape[2:]) - output_h, output_w = tuple(int(x) for x in size) - if output_h > input_h or output_w > output_h: - if ((output_h > 1 and output_w > 1 and input_h > 1 - and input_w > 1) and (output_h - 1) % (input_h - 1) - and (output_w - 1) % (input_w - 1)): - warnings.warn( - f'When align_corners={align_corners}, ' - 'the output would more aligned if ' - f'input size {(input_h, input_w)} is `x+1` and ' - f'out size {(output_h, output_w)} is `nx+1`') - return F.interpolate(input, size, scale_factor, mode, align_corners) - - -class Upsample(nn.Module): - - def __init__(self, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None): - super(Upsample, self).__init__() - self.size = size - if isinstance(scale_factor, tuple): - self.scale_factor = tuple(float(factor) for factor in scale_factor) - else: - self.scale_factor = float(scale_factor) if scale_factor else None - self.mode = mode - self.align_corners = align_corners - - def forward(self, x): - if not self.size: - size = [int(t * self.scale_factor) for t in x.shape[-2:]] - else: - size = self.size - return resize(x, size, None, self.mode, self.align_corners) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/__init__.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/__init__.py deleted file mode 100644 index ed002c7de..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collect_env import collect_env -from .logger import get_root_logger -from .misc import find_latest_checkpoint -from .set_env import setup_multi_processes - -__all__ = [ - 'get_root_logger', 'collect_env', 'find_latest_checkpoint', - 'setup_multi_processes' -] diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/collect_env.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/collect_env.py deleted file mode 100644 index 3379ecb06..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/collect_env.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash - -import mmseg - - -def collect_env(): - """Collect the information of the running environments.""" - env_info = collect_base_env() - env_info['MMSegmentation'] = f'{mmseg.__version__}+{get_git_hash()[:7]}' - - return env_info - - -if __name__ == '__main__': - for name, val in collect_env().items(): - print('{}: {}'.format(name, val)) diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/logger.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/logger.py deleted file mode 100644 index 0cb3c78d6..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/logger.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -from mmcv.utils import get_logger - - -def get_root_logger(log_file=None, log_level=logging.INFO): - """Get the root logger. - - The logger will be initialized if it has not been initialized. By default a - StreamHandler will be added. If `log_file` is specified, a FileHandler will - also be added. The name of the root logger is the top-level package name, - e.g., "mmseg". - - Args: - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the root logger. - log_level (int): The root logger level. Note that only the process of - rank 0 is affected, while other processes will set the level to - "Error" and be silent most of the time. - - Returns: - logging.Logger: The root logger. - """ - - logger = get_logger(name='mmseg', log_file=log_file, log_level=log_level) - - return logger diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/misc.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/misc.py deleted file mode 100644 index bd1b6b163..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/misc.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os.path as osp -import warnings - - -def find_latest_checkpoint(path, suffix='pth'): - """This function is for finding the latest checkpoint. - - It will be used when automatically resume, modified from - https://github.com/open-mmlab/mmdetection/blob/dev-v2.20.0/mmdet/utils/misc.py - - Args: - path (str): The path to find checkpoints. - suffix (str): File extension for the checkpoint. Defaults to pth. - - Returns: - latest_path(str | None): File path of the latest checkpoint. - """ - if not osp.exists(path): - warnings.warn("The path of the checkpoints doesn't exist.") - return None - if osp.exists(osp.join(path, f'latest.{suffix}')): - return osp.join(path, f'latest.{suffix}') - - checkpoints = glob.glob(osp.join(path, f'*.{suffix}')) - if len(checkpoints) == 0: - warnings.warn('The are no checkpoints in the path') - return None - latest = -1 - latest_path = '' - for checkpoint in checkpoints: - if len(checkpoint) < len(latest_path): - continue - # `count` is iteration number, as checkpoints are saved as - # 'iter_xx.pth' or 'epoch_xx.pth' and xx is iteration number. - count = int(osp.basename(checkpoint).split('_')[-1].split('.')[0]) - if count > latest: - latest = count - latest_path = checkpoint - return latest_path diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/set_env.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/set_env.py deleted file mode 100644 index b2d3aaf14..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/utils/set_env.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import platform - -import cv2 -import torch.multiprocessing as mp - -from ..utils import get_root_logger - - -def setup_multi_processes(cfg): - """Setup multi-processing environment variables.""" - logger = get_root_logger() - - # set multi-process start method - if platform.system() != 'Windows': - mp_start_method = cfg.get('mp_start_method', None) - current_method = mp.get_start_method(allow_none=True) - if mp_start_method in ('fork', 'spawn', 'forkserver'): - logger.info( - f'Multi-processing start method `{mp_start_method}` is ' - f'different from the previous setting `{current_method}`.' - f'It will be force set to `{mp_start_method}`.') - mp.set_start_method(mp_start_method, force=True) - else: - logger.info( - f'Multi-processing start method is `{mp_start_method}`') - - # disable opencv multithreading to avoid system being overloaded - opencv_num_threads = cfg.get('opencv_num_threads', None) - if isinstance(opencv_num_threads, int): - logger.info(f'OpenCV num_threads is `{opencv_num_threads}`') - cv2.setNumThreads(opencv_num_threads) - else: - logger.info(f'OpenCV num_threads is `{cv2.getNumThreads}') - - if cfg.data.workers_per_gpu > 1: - # setup OMP threads - # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa - omp_num_threads = cfg.get('omp_num_threads', None) - if 'OMP_NUM_THREADS' not in os.environ: - if isinstance(omp_num_threads, int): - logger.info(f'OMP num threads is {omp_num_threads}') - os.environ['OMP_NUM_THREADS'] = str(omp_num_threads) - else: - logger.info(f'OMP num threads is {os.environ["OMP_NUM_THREADS"] }') - - # setup MKL threads - if 'MKL_NUM_THREADS' not in os.environ: - mkl_num_threads = cfg.get('mkl_num_threads', None) - if isinstance(mkl_num_threads, int): - logger.info(f'MKL num threads is {mkl_num_threads}') - os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads) - else: - logger.info(f'MKL num threads is {os.environ["MKL_NUM_THREADS"]}') diff --git a/cv/semantic_segmentation/pspnet/pytorch/mmseg/version.py b/cv/semantic_segmentation/pspnet/pytorch/mmseg/version.py deleted file mode 100644 index e05146f0a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/mmseg/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) Open-MMLab. All rights reserved. - -__version__ = '0.24.1' - - -def parse_version_info(version_str): - version_info = [] - for x in version_str.split('.'): - if x.isdigit(): - version_info.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - version_info.append(int(patch_version[0])) - version_info.append(f'rc{patch_version[1]}') - return tuple(version_info) - - -version_info = parse_version_info(__version__) diff --git a/cv/semantic_segmentation/pspnet/pytorch/requirements.txt b/cv/semantic_segmentation/pspnet/pytorch/requirements.txt deleted file mode 100644 index 3f02ef5ca..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ --r requirements/mmcv/runtime.txt --r requirements/pspnet.txt -cityscapesscripts \ No newline at end of file diff --git a/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/build.txt b/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/build.txt deleted file mode 100644 index abf514853..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/build.txt +++ /dev/null @@ -1 +0,0 @@ -pytest-runner diff --git a/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/docs.txt b/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/docs.txt deleted file mode 100644 index 6a5319af3..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/docs.txt +++ /dev/null @@ -1,8 +0,0 @@ -docutils==0.16.0 -myst-parser -opencv-python --e git+https://github.com/open-mmlab/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme -sphinx==4.0.2 -sphinx-copybutton -sphinx_markdown_tables -torch diff --git a/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/optional.txt b/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/optional.txt deleted file mode 100644 index 63730036f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/optional.txt +++ /dev/null @@ -1 +0,0 @@ -ninja diff --git a/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/requirements.txt b/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/requirements.txt deleted file mode 100644 index 6f00e6303..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ --r requirements/mmcv/build.txt --r requirements/mmcv/optional.txt --r requirements/mmcv/runtime.txt --r requirements/mmcv/test.txt diff --git a/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/runtime.txt b/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/runtime.txt deleted file mode 100644 index 66e90d674..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/runtime.txt +++ /dev/null @@ -1,7 +0,0 @@ -addict -numpy -packaging -Pillow -pyyaml -regex;sys_platform=='win32' -yapf diff --git a/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/test.txt b/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/test.txt deleted file mode 100644 index 6d9d17b98..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/requirements/mmcv/test.txt +++ /dev/null @@ -1,9 +0,0 @@ -coverage -lmdb -onnx==1.7.0; python_version < '3.10' -onnxoptimizer; python_version < '3.10' -onnxruntime>=1.8.0; python_version < '3.10' -pytest -PyTurboJPEG -scipy -tifffile diff --git a/cv/semantic_segmentation/pspnet/pytorch/requirements/mminstall.txt b/cv/semantic_segmentation/pspnet/pytorch/requirements/mminstall.txt deleted file mode 100644 index bd43faf87..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/requirements/mminstall.txt +++ /dev/null @@ -1,2 +0,0 @@ -mmcls>=0.20.1 -mmcv-full>=1.4.4,<=1.6.0 diff --git a/cv/semantic_segmentation/pspnet/pytorch/requirements/optional.txt b/cv/semantic_segmentation/pspnet/pytorch/requirements/optional.txt deleted file mode 100644 index 47fa59331..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/requirements/optional.txt +++ /dev/null @@ -1 +0,0 @@ -cityscapesscripts diff --git a/cv/semantic_segmentation/pspnet/pytorch/requirements/pspnet.txt b/cv/semantic_segmentation/pspnet/pytorch/requirements/pspnet.txt deleted file mode 100644 index 2712f504c..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/requirements/pspnet.txt +++ /dev/null @@ -1,4 +0,0 @@ -matplotlib -numpy -packaging -prettytable diff --git a/cv/semantic_segmentation/pspnet/pytorch/requirements/runtime.txt b/cv/semantic_segmentation/pspnet/pytorch/requirements/runtime.txt deleted file mode 100644 index 520408fe8..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/requirements/runtime.txt +++ /dev/null @@ -1,5 +0,0 @@ -matplotlib -mmcls>=0.20.1 -numpy -packaging -prettytable diff --git a/cv/semantic_segmentation/pspnet/pytorch/setup.py b/cv/semantic_segmentation/pspnet/pytorch/setup.py deleted file mode 100644 index d409997db..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/setup.py +++ /dev/null @@ -1,258 +0,0 @@ -import glob -import os -import platform -import re -import warnings -from pkg_resources import DistributionNotFound, get_distribution -from setuptools import find_packages, setup - -EXT_TYPE = '' -try: - import torch - if torch.__version__ == 'parrots': - from parrots.utils.build_extension import BuildExtension - EXT_TYPE = 'parrots' - elif (hasattr(torch, 'is_mlu_available') and torch.is_mlu_available()) or \ - os.getenv('FORCE_MLU', '0') == '1': - from torch_mlu.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - else: - from torch.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - cmd_class = {'build_ext': BuildExtension} -except ModuleNotFoundError: - cmd_class = {} - print('Skip building ext ops due to the absence of torch.') - - -def choose_requirement(primary, secondary): - """If some version of primary requirement installed, return primary, else - return secondary.""" - try: - name = re.split(r'[!<>=]', primary)[0] - get_distribution(name) - except DistributionNotFound: - return secondary - - return str(primary) - - -def get_version(): - version_file = 'mmcv/version.py' - with open(version_file, 'r', encoding='utf-8') as f: - exec(compile(f.read(), version_file, 'exec')) - return locals()['__version__'] - - -def parse_requirements(fname='requirements/mmcv/runtime.txt', with_version=True): - """Parse the package dependencies listed in a requirements file but strips - specific versioning information. - - Args: - fname (str): path to requirements file - with_version (bool, default=False): if True include version specs - - Returns: - List[str]: list of requirements items - - CommandLine: - python -c "import setup; print(setup.parse_requirements())" - """ - import sys - from os.path import exists - require_fpath = fname - - def parse_line(line): - """Parse information from a line in a requirements text file.""" - if line.startswith('-r '): - # Allow specifying requirements in other files - target = line.split(' ')[1] - for info in parse_require_file(target): - yield info - else: - info = {'line': line} - if line.startswith('-e '): - info['package'] = line.split('#egg=')[1] - else: - # Remove versioning from the package - pat = '(' + '|'.join(['>=', '==', '>']) + ')' - parts = re.split(pat, line, maxsplit=1) - parts = [p.strip() for p in parts] - - info['package'] = parts[0] - if len(parts) > 1: - op, rest = parts[1:] - if ';' in rest: - # Handle platform specific dependencies - # http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies - version, platform_deps = map(str.strip, - rest.split(';')) - info['platform_deps'] = platform_deps - else: - version = rest # NOQA - info['version'] = (op, version) - yield info - - def parse_require_file(fpath): - with open(fpath, 'r') as f: - for line in f.readlines(): - line = line.strip() - if line and not line.startswith('#'): - for info in parse_line(line): - yield info - - def gen_packages_items(): - if exists(require_fpath): - for info in parse_require_file(require_fpath): - parts = [info['package']] - if with_version and 'version' in info: - parts.extend(info['version']) - if not sys.version.startswith('3.4'): - # apparently package_deps are broken in 3.4 - platform_deps = info.get('platform_deps') - if platform_deps is not None: - parts.append(';' + platform_deps) - item = ''.join(parts) - yield item - - packages = list(gen_packages_items()) - return packages - - -install_requires = parse_requirements() - -try: - # OpenCV installed via conda. - import cv2 # NOQA: F401 - major, minor, *rest = cv2.__version__.split('.') - if int(major) < 3: - raise RuntimeError( - f'OpenCV >=3 is required but {cv2.__version__} is installed') -except ImportError: - # If first not installed install second package - CHOOSE_INSTALL_REQUIRES = [('opencv-python-headless>=3', - 'opencv-python>=3')] - for main, secondary in CHOOSE_INSTALL_REQUIRES: - install_requires.append(choose_requirement(main, secondary)) - - -def get_extensions(): - extensions = [] - - if EXT_TYPE == 'pytorch': - ext_name = 'mmcv._ext' - from torch.utils.cpp_extension import CppExtension, CUDAExtension - - # prevent ninja from using too many resources - try: - import psutil - num_cpu = len(psutil.Process().cpu_affinity()) - cpu_use = max(4, num_cpu - 1) - except (ModuleNotFoundError, AttributeError): - cpu_use = 4 - - os.environ.setdefault('MAX_JOBS', str(cpu_use)) - define_macros = [] - - # Before PyTorch1.8.0, when compiling CUDA code, `cxx` is a - # required key passed to PyTorch. Even if there is no flag passed - # to cxx, users also need to pass an empty list to PyTorch. - # Since PyTorch1.8.0, it has a default value so users do not need - # to pass an empty list anymore. - # More details at https://github.com/pytorch/pytorch/pull/45956 - extra_compile_args = {'cxx': []} - - # Since the PR (https://github.com/open-mmlab/mmcv/pull/1463) uses - # c++14 features, the argument ['std=c++14'] must be added here. - # However, in the windows environment, some standard libraries - # will depend on c++17 or higher. In fact, for the windows - # environment, the compiler will choose the appropriate compiler - # to compile those cpp files, so there is no need to add the - # argument - if platform.system() != 'Windows': - extra_compile_args['cxx'] = ['-std=c++14'] - - include_dirs = [] - - is_rocm_pytorch = False - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm_pytorch = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - - if is_rocm_pytorch or torch.cuda.is_available() or os.getenv( - 'FORCE_CUDA', '0') == '1': - if is_rocm_pytorch: - define_macros += [('HIP_DIFF', None)] - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cpp') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - else: - print(f'Compiling {ext_name} only with CPU') - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') - extension = CppExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - - # Since the PR (https://github.com/open-mmlab/mmcv/pull/1463) uses - # c++14 features, the argument ['std=c++14'] must be added here. - # However, in the windows environment, some standard libraries - # will depend on c++17 or higher. In fact, for the windows - # environment, the compiler will choose the appropriate compiler - # to compile those cpp files, so there is no need to add the - # argument - if 'nvcc' in extra_compile_args and platform.system() != 'Windows': - extra_compile_args['nvcc'] += ['-std=c++14'] - - ext_ops = extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args) - extensions.append(ext_ops) - - return extensions - - -setup( - name='mmcv' if os.getenv('MMCV_WITH_OPS', '0') == '0' else 'mmcv-full', - version=get_version(), - description='OpenMMLab Computer Vision Foundation', - keywords='computer vision', - packages=find_packages(), - include_package_data=True, - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Topic :: Utilities', - ], - url='https://github.com/open-mmlab/mmcv', - author='MMCV Contributors', - author_email='openmmlab@gmail.com', - install_requires=install_requires, - extras_require={ - 'all': parse_requirements('requirements/mmcv/requirements.txt'), - 'tests': parse_requirements('requirements/mmcv/test.txt'), - 'build': parse_requirements('requirements/mmcv/build.txt'), - 'optional': parse_requirements('requirements/mmcv/optional.txt'), - }, - ext_modules=get_extensions(), - cmdclass=cmd_class, - zip_safe=False) diff --git a/cv/semantic_segmentation/pspnet/pytorch/tools/analyze_logs.py b/cv/semantic_segmentation/pspnet/pytorch/tools/analyze_logs.py deleted file mode 100644 index e2127d4d6..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/tools/analyze_logs.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/open- -mmlab/mmdetection/blob/master/tools/analysis_tools/analyze_logs.py.""" -import argparse -import json -from collections import defaultdict - -import matplotlib.pyplot as plt -import seaborn as sns - - -def plot_curve(log_dicts, args): - if args.backend is not None: - plt.switch_backend(args.backend) - sns.set_style(args.style) - # if legend is None, use {filename}_{key} as legend - legend = args.legend - if legend is None: - legend = [] - for json_log in args.json_logs: - for metric in args.keys: - legend.append(f'{json_log}_{metric}') - assert len(legend) == (len(args.json_logs) * len(args.keys)) - metrics = args.keys - - num_metrics = len(metrics) - for i, log_dict in enumerate(log_dicts): - epochs = list(log_dict.keys()) - for j, metric in enumerate(metrics): - print(f'plot curve of {args.json_logs[i]}, metric is {metric}') - plot_epochs = [] - plot_iters = [] - plot_values = [] - # In some log files exist lines of validation, - # `mode` list is used to only collect iter number - # of training line. - for epoch in epochs: - epoch_logs = log_dict[epoch] - if metric not in epoch_logs.keys(): - continue - if metric in ['mIoU', 'mAcc', 'aAcc']: - plot_epochs.append(epoch) - plot_values.append(epoch_logs[metric][0]) - else: - for idx in range(len(epoch_logs[metric])): - if epoch_logs['mode'][idx] == 'train': - plot_iters.append(epoch_logs['iter'][idx]) - plot_values.append(epoch_logs[metric][idx]) - ax = plt.gca() - label = legend[i * num_metrics + j] - if metric in ['mIoU', 'mAcc', 'aAcc']: - ax.set_xticks(plot_epochs) - plt.xlabel('epoch') - plt.plot(plot_epochs, plot_values, label=label, marker='o') - else: - plt.xlabel('iter') - plt.plot(plot_iters, plot_values, label=label, linewidth=0.5) - plt.legend() - if args.title is not None: - plt.title(args.title) - if args.out is None: - plt.show() - else: - print(f'save curve to: {args.out}') - plt.savefig(args.out) - plt.cla() - - -def parse_args(): - parser = argparse.ArgumentParser(description='Analyze Json Log') - parser.add_argument( - 'json_logs', - type=str, - nargs='+', - help='path of train log in json format') - parser.add_argument( - '--keys', - type=str, - nargs='+', - default=['mIoU'], - help='the metric that you want to plot') - parser.add_argument('--title', type=str, help='title of figure') - parser.add_argument( - '--legend', - type=str, - nargs='+', - default=None, - help='legend of each plot') - parser.add_argument( - '--backend', type=str, default=None, help='backend of plt') - parser.add_argument( - '--style', type=str, default='dark', help='style of plt') - parser.add_argument('--out', type=str, default=None) - args = parser.parse_args() - return args - - -def load_json_logs(json_logs): - # load and convert json_logs to log_dict, key is epoch, value is a sub dict - # keys of sub dict is different metrics - # value of sub dict is a list of corresponding values of all iterations - log_dicts = [dict() for _ in json_logs] - for json_log, log_dict in zip(json_logs, log_dicts): - with open(json_log, 'r') as log_file: - for line in log_file: - log = json.loads(line.strip()) - # skip lines without `epoch` field - if 'epoch' not in log: - continue - epoch = log.pop('epoch') - if epoch not in log_dict: - log_dict[epoch] = defaultdict(list) - for k, v in log.items(): - log_dict[epoch][k].append(v) - return log_dicts - - -def main(): - args = parse_args() - json_logs = args.json_logs - for json_log in json_logs: - assert json_log.endswith('.json') - log_dicts = load_json_logs(json_logs) - plot_curve(log_dicts, args) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/pspnet/pytorch/tools/benchmark.py b/cv/semantic_segmentation/pspnet/pytorch/tools/benchmark.py deleted file mode 100644 index f6d688848..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/tools/benchmark.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import time - -import mmcv -import numpy as np -import torch -from mmcv import Config -from mmcv.parallel import MMDataParallel -from mmcv.runner import load_checkpoint, wrap_fp16_model - -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='MMSeg benchmark a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--log-interval', type=int, default=50, help='interval of logging') - parser.add_argument( - '--work-dir', - help=('if specified, the results will be dumped ' - 'into the directory as json')) - parser.add_argument('--repeat-times', type=int, default=1) - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.work_dir is not None: - mmcv.mkdir_or_exist(osp.abspath(args.work_dir)) - json_file = osp.join(args.work_dir, f'fps_{timestamp}.json') - else: - # use config filename as default work_dir if cfg.work_dir is None - work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - mmcv.mkdir_or_exist(osp.abspath(work_dir)) - json_file = osp.join(work_dir, f'fps_{timestamp}.json') - - repeat_times = args.repeat_times - # set cudnn_benchmark - torch.backends.cudnn.benchmark = False - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - benchmark_dict = dict(config=args.config, unit='img / s') - overall_fps_list = [] - for time_index in range(repeat_times): - print(f'Run {time_index + 1}:') - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - data_loader = build_dataloader( - dataset, - samples_per_gpu=1, - workers_per_gpu=cfg.data.workers_per_gpu, - dist=False, - shuffle=False) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - if 'checkpoint' in args and osp.exists(args.checkpoint): - load_checkpoint(model, args.checkpoint, map_location='cpu') - - model = MMDataParallel(model, device_ids=[0]) - - model.eval() - - # the first several iterations may be very slow so skip them - num_warmup = 5 - pure_inf_time = 0 - total_iters = 200 - - # benchmark with 200 image and take the average - for i, data in enumerate(data_loader): - - torch.cuda.synchronize() - start_time = time.perf_counter() - - with torch.no_grad(): - model(return_loss=False, rescale=True, **data) - - torch.cuda.synchronize() - elapsed = time.perf_counter() - start_time - - if i >= num_warmup: - pure_inf_time += elapsed - if (i + 1) % args.log_interval == 0: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Done image [{i + 1:<3}/ {total_iters}], ' - f'fps: {fps:.2f} img / s') - - if (i + 1) == total_iters: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Overall fps: {fps:.2f} img / s\n') - benchmark_dict[f'overall_fps_{time_index + 1}'] = round(fps, 2) - overall_fps_list.append(fps) - break - benchmark_dict['average_fps'] = round(np.mean(overall_fps_list), 2) - benchmark_dict['fps_variance'] = round(np.var(overall_fps_list), 4) - print(f'Average fps of {repeat_times} evaluations: ' - f'{benchmark_dict["average_fps"]}') - print(f'The variance of {repeat_times} evaluations: ' - f'{benchmark_dict["fps_variance"]}') - mmcv.dump(benchmark_dict, json_file, indent=4) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/pspnet/pytorch/tools/confusion_matrix.py b/cv/semantic_segmentation/pspnet/pytorch/tools/confusion_matrix.py deleted file mode 100644 index 2c5b64cf4..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/tools/confusion_matrix.py +++ /dev/null @@ -1,184 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os - -import matplotlib.pyplot as plt -import mmcv -import numpy as np -from matplotlib.ticker import MultipleLocator -from mmcv import Config, DictAction - -from mmseg.datasets import build_dataset - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Generate confusion matrix from segmentation results') - parser.add_argument('config', help='test config file path') - parser.add_argument( - 'prediction_path', help='prediction path where test .pkl result') - parser.add_argument( - 'save_dir', help='directory where confusion matrix will be saved') - parser.add_argument( - '--show', action='store_true', help='show confusion matrix') - parser.add_argument( - '--color-theme', - default='winter', - help='theme of the matrix color map') - parser.add_argument( - '--title', - default='Normalized Confusion Matrix', - help='title of the matrix color map') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - args = parser.parse_args() - return args - - -def calculate_confusion_matrix(dataset, results): - """Calculate the confusion matrix. - - Args: - dataset (Dataset): Test or val dataset. - results (list[ndarray]): A list of segmentation results in each image. - """ - n = len(dataset.CLASSES) - confusion_matrix = np.zeros(shape=[n, n]) - assert len(dataset) == len(results) - prog_bar = mmcv.ProgressBar(len(results)) - for idx, per_img_res in enumerate(results): - res_segm = per_img_res - gt_segm = dataset.get_gt_seg_map_by_idx(idx) - inds = n * gt_segm + res_segm - inds = inds.flatten() - mat = np.bincount(inds, minlength=n**2).reshape(n, n) - confusion_matrix += mat - prog_bar.update() - return confusion_matrix - - -def plot_confusion_matrix(confusion_matrix, - labels, - save_dir=None, - show=True, - title='Normalized Confusion Matrix', - color_theme='winter'): - """Draw confusion matrix with matplotlib. - - Args: - confusion_matrix (ndarray): The confusion matrix. - labels (list[str]): List of class names. - save_dir (str|optional): If set, save the confusion matrix plot to the - given path. Default: None. - show (bool): Whether to show the plot. Default: True. - title (str): Title of the plot. Default: `Normalized Confusion Matrix`. - color_theme (str): Theme of the matrix color map. Default: `winter`. - """ - # normalize the confusion matrix - per_label_sums = confusion_matrix.sum(axis=1)[:, np.newaxis] - confusion_matrix = \ - confusion_matrix.astype(np.float32) / per_label_sums * 100 - - num_classes = len(labels) - fig, ax = plt.subplots( - figsize=(2 * num_classes, 2 * num_classes * 0.8), dpi=180) - cmap = plt.get_cmap(color_theme) - im = ax.imshow(confusion_matrix, cmap=cmap) - plt.colorbar(mappable=im, ax=ax) - - title_font = {'weight': 'bold', 'size': 12} - ax.set_title(title, fontdict=title_font) - label_font = {'size': 10} - plt.ylabel('Ground Truth Label', fontdict=label_font) - plt.xlabel('Prediction Label', fontdict=label_font) - - # draw locator - xmajor_locator = MultipleLocator(1) - xminor_locator = MultipleLocator(0.5) - ax.xaxis.set_major_locator(xmajor_locator) - ax.xaxis.set_minor_locator(xminor_locator) - ymajor_locator = MultipleLocator(1) - yminor_locator = MultipleLocator(0.5) - ax.yaxis.set_major_locator(ymajor_locator) - ax.yaxis.set_minor_locator(yminor_locator) - - # draw grid - ax.grid(True, which='minor', linestyle='-') - - # draw label - ax.set_xticks(np.arange(num_classes)) - ax.set_yticks(np.arange(num_classes)) - ax.set_xticklabels(labels) - ax.set_yticklabels(labels) - - ax.tick_params( - axis='x', bottom=False, top=True, labelbottom=False, labeltop=True) - plt.setp( - ax.get_xticklabels(), rotation=45, ha='left', rotation_mode='anchor') - - # draw confusion matrix value - for i in range(num_classes): - for j in range(num_classes): - ax.text( - j, - i, - '{}%'.format( - round(confusion_matrix[i, j], 2 - ) if not np.isnan(confusion_matrix[i, j]) else -1), - ha='center', - va='center', - color='w', - size=7) - - ax.set_ylim(len(confusion_matrix) - 0.5, -0.5) # matplotlib>3.1.1 - - fig.tight_layout() - if save_dir is not None: - plt.savefig( - os.path.join(save_dir, 'confusion_matrix.png'), format='png') - if show: - plt.show() - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - results = mmcv.load(args.prediction_path) - - assert isinstance(results, list) - if isinstance(results[0], np.ndarray): - pass - else: - raise TypeError('invalid type of prediction results') - - if isinstance(cfg.data.test, dict): - cfg.data.test.test_mode = True - elif isinstance(cfg.data.test, list): - for ds_cfg in cfg.data.test: - ds_cfg.test_mode = True - - dataset = build_dataset(cfg.data.test) - confusion_matrix = calculate_confusion_matrix(dataset, results) - plot_confusion_matrix( - confusion_matrix, - dataset.CLASSES, - save_dir=args.save_dir, - show=args.show, - title=args.title, - color_theme=args.color_theme) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/cityscapes.py b/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/cityscapes.py deleted file mode 100644 index 17b616847..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/cityscapes.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp - -import mmcv -from cityscapesscripts.preparation.json2labelImg import json2labelImg - - -def convert_json_to_label(json_file): - label_file = json_file.replace('_polygons.json', '_labelTrainIds.png') - json2labelImg(json_file, label_file, 'trainIds') - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert Cityscapes annotations to TrainIds') - parser.add_argument('cityscapes_path', help='cityscapes data path') - parser.add_argument('--gt-dir', default='gtFine', type=str) - parser.add_argument('-o', '--out-dir', help='output path') - parser.add_argument( - '--nproc', default=1, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - cityscapes_path = args.cityscapes_path - out_dir = args.out_dir if args.out_dir else cityscapes_path - mmcv.mkdir_or_exist(out_dir) - - gt_dir = osp.join(cityscapes_path, args.gt_dir) - - poly_files = [] - for poly in mmcv.scandir(gt_dir, '_polygons.json', recursive=True): - poly_file = osp.join(gt_dir, poly) - poly_files.append(poly_file) - if args.nproc > 1: - mmcv.track_parallel_progress(convert_json_to_label, poly_files, - args.nproc) - else: - mmcv.track_progress(convert_json_to_label, poly_files) - - split_names = ['train', 'val', 'test'] - - for split in split_names: - filenames = [] - for poly in mmcv.scandir( - osp.join(gt_dir, split), '_polygons.json', recursive=True): - filenames.append(poly.replace('_gtFine_polygons.json', '')) - with open(osp.join(out_dir, f'{split}.txt'), 'w') as f: - f.writelines(f + '\n' for f in filenames) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/coco_stuff10k.py b/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/coco_stuff10k.py deleted file mode 100644 index 374f81970..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/coco_stuff10k.py +++ /dev/null @@ -1,307 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import shutil -from functools import partial - -import mmcv -import numpy as np -from PIL import Image -from scipy.io import loadmat - -COCO_LEN = 10000 - -clsID_to_trID = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 13: 12, - 14: 13, - 15: 14, - 16: 15, - 17: 16, - 18: 17, - 19: 18, - 20: 19, - 21: 20, - 22: 21, - 23: 22, - 24: 23, - 25: 24, - 27: 25, - 28: 26, - 31: 27, - 32: 28, - 33: 29, - 34: 30, - 35: 31, - 36: 32, - 37: 33, - 38: 34, - 39: 35, - 40: 36, - 41: 37, - 42: 38, - 43: 39, - 44: 40, - 46: 41, - 47: 42, - 48: 43, - 49: 44, - 50: 45, - 51: 46, - 52: 47, - 53: 48, - 54: 49, - 55: 50, - 56: 51, - 57: 52, - 58: 53, - 59: 54, - 60: 55, - 61: 56, - 62: 57, - 63: 58, - 64: 59, - 65: 60, - 67: 61, - 70: 62, - 72: 63, - 73: 64, - 74: 65, - 75: 66, - 76: 67, - 77: 68, - 78: 69, - 79: 70, - 80: 71, - 81: 72, - 82: 73, - 84: 74, - 85: 75, - 86: 76, - 87: 77, - 88: 78, - 89: 79, - 90: 80, - 92: 81, - 93: 82, - 94: 83, - 95: 84, - 96: 85, - 97: 86, - 98: 87, - 99: 88, - 100: 89, - 101: 90, - 102: 91, - 103: 92, - 104: 93, - 105: 94, - 106: 95, - 107: 96, - 108: 97, - 109: 98, - 110: 99, - 111: 100, - 112: 101, - 113: 102, - 114: 103, - 115: 104, - 116: 105, - 117: 106, - 118: 107, - 119: 108, - 120: 109, - 121: 110, - 122: 111, - 123: 112, - 124: 113, - 125: 114, - 126: 115, - 127: 116, - 128: 117, - 129: 118, - 130: 119, - 131: 120, - 132: 121, - 133: 122, - 134: 123, - 135: 124, - 136: 125, - 137: 126, - 138: 127, - 139: 128, - 140: 129, - 141: 130, - 142: 131, - 143: 132, - 144: 133, - 145: 134, - 146: 135, - 147: 136, - 148: 137, - 149: 138, - 150: 139, - 151: 140, - 152: 141, - 153: 142, - 154: 143, - 155: 144, - 156: 145, - 157: 146, - 158: 147, - 159: 148, - 160: 149, - 161: 150, - 162: 151, - 163: 152, - 164: 153, - 165: 154, - 166: 155, - 167: 156, - 168: 157, - 169: 158, - 170: 159, - 171: 160, - 172: 161, - 173: 162, - 174: 163, - 175: 164, - 176: 165, - 177: 166, - 178: 167, - 179: 168, - 180: 169, - 181: 170, - 182: 171 -} - - -def convert_to_trainID(tuple_path, in_img_dir, in_ann_dir, out_img_dir, - out_mask_dir, is_train): - imgpath, maskpath = tuple_path - shutil.copyfile( - osp.join(in_img_dir, imgpath), - osp.join(out_img_dir, 'train2014', imgpath) if is_train else osp.join( - out_img_dir, 'test2014', imgpath)) - annotate = loadmat(osp.join(in_ann_dir, maskpath)) - mask = annotate['S'].astype(np.uint8) - mask_copy = mask.copy() - for clsID, trID in clsID_to_trID.items(): - mask_copy[mask == clsID] = trID - seg_filename = osp.join(out_mask_dir, 'train2014', - maskpath.split('.')[0] + - '_labelTrainIds.png') if is_train else osp.join( - out_mask_dir, 'test2014', - maskpath.split('.')[0] + '_labelTrainIds.png') - Image.fromarray(mask_copy).save(seg_filename, 'PNG') - - -def generate_coco_list(folder): - train_list = osp.join(folder, 'imageLists', 'train.txt') - test_list = osp.join(folder, 'imageLists', 'test.txt') - train_paths = [] - test_paths = [] - - with open(train_list) as f: - for filename in f: - basename = filename.strip() - imgpath = basename + '.jpg' - maskpath = basename + '.mat' - train_paths.append((imgpath, maskpath)) - - with open(test_list) as f: - for filename in f: - basename = filename.strip() - imgpath = basename + '.jpg' - maskpath = basename + '.mat' - test_paths.append((imgpath, maskpath)) - - return train_paths, test_paths - - -def parse_args(): - parser = argparse.ArgumentParser( - description=\ - 'Convert COCO Stuff 10k annotations to mmsegmentation format') # noqa - parser.add_argument('coco_path', help='coco stuff path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=16, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - coco_path = args.coco_path - nproc = args.nproc - - out_dir = args.out_dir or coco_path - out_img_dir = osp.join(out_dir, 'images') - out_mask_dir = osp.join(out_dir, 'annotations') - - mmcv.mkdir_or_exist(osp.join(out_img_dir, 'train2014')) - mmcv.mkdir_or_exist(osp.join(out_img_dir, 'test2014')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'train2014')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'test2014')) - - train_list, test_list = generate_coco_list(coco_path) - assert (len(train_list) + - len(test_list)) == COCO_LEN, 'Wrong length of list {} & {}'.format( - len(train_list), len(test_list)) - - if args.nproc > 1: - mmcv.track_parallel_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=True), - train_list, - nproc=nproc) - mmcv.track_parallel_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=False), - test_list, - nproc=nproc) - else: - mmcv.track_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=True), train_list) - mmcv.track_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=False), test_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/coco_stuff164k.py b/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/coco_stuff164k.py deleted file mode 100644 index 6d8e2f2a3..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/coco_stuff164k.py +++ /dev/null @@ -1,264 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import shutil -from functools import partial -from glob import glob - -import mmcv -import numpy as np -from PIL import Image - -COCO_LEN = 123287 - -clsID_to_trID = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 12: 11, - 13: 12, - 14: 13, - 15: 14, - 16: 15, - 17: 16, - 18: 17, - 19: 18, - 20: 19, - 21: 20, - 22: 21, - 23: 22, - 24: 23, - 26: 24, - 27: 25, - 30: 26, - 31: 27, - 32: 28, - 33: 29, - 34: 30, - 35: 31, - 36: 32, - 37: 33, - 38: 34, - 39: 35, - 40: 36, - 41: 37, - 42: 38, - 43: 39, - 45: 40, - 46: 41, - 47: 42, - 48: 43, - 49: 44, - 50: 45, - 51: 46, - 52: 47, - 53: 48, - 54: 49, - 55: 50, - 56: 51, - 57: 52, - 58: 53, - 59: 54, - 60: 55, - 61: 56, - 62: 57, - 63: 58, - 64: 59, - 66: 60, - 69: 61, - 71: 62, - 72: 63, - 73: 64, - 74: 65, - 75: 66, - 76: 67, - 77: 68, - 78: 69, - 79: 70, - 80: 71, - 81: 72, - 83: 73, - 84: 74, - 85: 75, - 86: 76, - 87: 77, - 88: 78, - 89: 79, - 91: 80, - 92: 81, - 93: 82, - 94: 83, - 95: 84, - 96: 85, - 97: 86, - 98: 87, - 99: 88, - 100: 89, - 101: 90, - 102: 91, - 103: 92, - 104: 93, - 105: 94, - 106: 95, - 107: 96, - 108: 97, - 109: 98, - 110: 99, - 111: 100, - 112: 101, - 113: 102, - 114: 103, - 115: 104, - 116: 105, - 117: 106, - 118: 107, - 119: 108, - 120: 109, - 121: 110, - 122: 111, - 123: 112, - 124: 113, - 125: 114, - 126: 115, - 127: 116, - 128: 117, - 129: 118, - 130: 119, - 131: 120, - 132: 121, - 133: 122, - 134: 123, - 135: 124, - 136: 125, - 137: 126, - 138: 127, - 139: 128, - 140: 129, - 141: 130, - 142: 131, - 143: 132, - 144: 133, - 145: 134, - 146: 135, - 147: 136, - 148: 137, - 149: 138, - 150: 139, - 151: 140, - 152: 141, - 153: 142, - 154: 143, - 155: 144, - 156: 145, - 157: 146, - 158: 147, - 159: 148, - 160: 149, - 161: 150, - 162: 151, - 163: 152, - 164: 153, - 165: 154, - 166: 155, - 167: 156, - 168: 157, - 169: 158, - 170: 159, - 171: 160, - 172: 161, - 173: 162, - 174: 163, - 175: 164, - 176: 165, - 177: 166, - 178: 167, - 179: 168, - 180: 169, - 181: 170, - 255: 255 -} - - -def convert_to_trainID(maskpath, out_mask_dir, is_train): - mask = np.array(Image.open(maskpath)) - mask_copy = mask.copy() - for clsID, trID in clsID_to_trID.items(): - mask_copy[mask == clsID] = trID - seg_filename = osp.join( - out_mask_dir, 'train2017', - osp.basename(maskpath).split('.')[0] + - '_labelTrainIds.png') if is_train else osp.join( - out_mask_dir, 'val2017', - osp.basename(maskpath).split('.')[0] + '_labelTrainIds.png') - Image.fromarray(mask_copy).save(seg_filename, 'PNG') - - -def parse_args(): - parser = argparse.ArgumentParser( - description=\ - 'Convert COCO Stuff 164k annotations to mmsegmentation format') # noqa - parser.add_argument('coco_path', help='coco stuff path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=16, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - coco_path = args.coco_path - nproc = args.nproc - - out_dir = args.out_dir or coco_path - out_img_dir = osp.join(out_dir, 'images') - out_mask_dir = osp.join(out_dir, 'annotations') - - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'train2017')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'val2017')) - - if out_dir != coco_path: - shutil.copytree(osp.join(coco_path, 'images'), out_img_dir) - - train_list = glob(osp.join(coco_path, 'annotations', 'train2017', '*.png')) - train_list = [file for file in train_list if '_labelTrainIds' not in file] - test_list = glob(osp.join(coco_path, 'annotations', 'val2017', '*.png')) - test_list = [file for file in test_list if '_labelTrainIds' not in file] - assert (len(train_list) + - len(test_list)) == COCO_LEN, 'Wrong length of list {} & {}'.format( - len(train_list), len(test_list)) - - if args.nproc > 1: - mmcv.track_parallel_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=True), - train_list, - nproc=nproc) - mmcv.track_parallel_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=False), - test_list, - nproc=nproc) - else: - mmcv.track_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=True), - train_list) - mmcv.track_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=False), - test_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/pascal_context.py b/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/pascal_context.py deleted file mode 100644 index 03b79d518..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/pascal_context.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -from functools import partial - -import mmcv -import numpy as np -from detail import Detail -from PIL import Image - -_mapping = np.sort( - np.array([ - 0, 2, 259, 260, 415, 324, 9, 258, 144, 18, 19, 22, 23, 397, 25, 284, - 158, 159, 416, 33, 162, 420, 454, 295, 296, 427, 44, 45, 46, 308, 59, - 440, 445, 31, 232, 65, 354, 424, 68, 326, 72, 458, 34, 207, 80, 355, - 85, 347, 220, 349, 360, 98, 187, 104, 105, 366, 189, 368, 113, 115 - ])) -_key = np.array(range(len(_mapping))).astype('uint8') - - -def generate_labels(img_id, detail, out_dir): - - def _class_to_index(mask, _mapping, _key): - # assert the values - values = np.unique(mask) - for i in range(len(values)): - assert (values[i] in _mapping) - index = np.digitize(mask.ravel(), _mapping, right=True) - return _key[index].reshape(mask.shape) - - mask = Image.fromarray( - _class_to_index(detail.getMask(img_id), _mapping=_mapping, _key=_key)) - filename = img_id['file_name'] - mask.save(osp.join(out_dir, filename.replace('jpg', 'png'))) - return osp.splitext(osp.basename(filename))[0] - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert PASCAL VOC annotations to mmsegmentation format') - parser.add_argument('devkit_path', help='pascal voc devkit path') - parser.add_argument('json_path', help='annoation json filepath') - parser.add_argument('-o', '--out_dir', help='output path') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - devkit_path = args.devkit_path - if args.out_dir is None: - out_dir = osp.join(devkit_path, 'VOC2010', 'SegmentationClassContext') - else: - out_dir = args.out_dir - json_path = args.json_path - mmcv.mkdir_or_exist(out_dir) - img_dir = osp.join(devkit_path, 'VOC2010', 'JPEGImages') - - train_detail = Detail(json_path, img_dir, 'train') - train_ids = train_detail.getImgs() - - val_detail = Detail(json_path, img_dir, 'val') - val_ids = val_detail.getImgs() - - mmcv.mkdir_or_exist( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext')) - - train_list = mmcv.track_progress( - partial(generate_labels, detail=train_detail, out_dir=out_dir), - train_ids) - with open( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext', - 'train.txt'), 'w') as f: - f.writelines(line + '\n' for line in sorted(train_list)) - - val_list = mmcv.track_progress( - partial(generate_labels, detail=val_detail, out_dir=out_dir), val_ids) - with open( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext', - 'val.txt'), 'w') as f: - f.writelines(line + '\n' for line in sorted(val_list)) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/voc_aug.py b/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/voc_aug.py deleted file mode 100644 index 1d42c2704..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/tools/convert_datasets/voc_aug.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -from functools import partial - -import mmcv -import numpy as np -from PIL import Image -from scipy.io import loadmat - -AUG_LEN = 10582 - - -def convert_mat(mat_file, in_dir, out_dir): - data = loadmat(osp.join(in_dir, mat_file)) - mask = data['GTcls'][0]['Segmentation'][0].astype(np.uint8) - seg_filename = osp.join(out_dir, mat_file.replace('.mat', '.png')) - Image.fromarray(mask).save(seg_filename, 'PNG') - - -def generate_aug_list(merged_list, excluded_list): - return list(set(merged_list) - set(excluded_list)) - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert PASCAL VOC annotations to mmsegmentation format') - parser.add_argument('devkit_path', help='pascal voc devkit path') - parser.add_argument('aug_path', help='pascal voc aug path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=1, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - devkit_path = args.devkit_path - aug_path = args.aug_path - nproc = args.nproc - if args.out_dir is None: - out_dir = osp.join(devkit_path, 'VOC2012', 'SegmentationClassAug') - else: - out_dir = args.out_dir - mmcv.mkdir_or_exist(out_dir) - in_dir = osp.join(aug_path, 'dataset', 'cls') - - mmcv.track_parallel_progress( - partial(convert_mat, in_dir=in_dir, out_dir=out_dir), - list(mmcv.scandir(in_dir, suffix='.mat')), - nproc=nproc) - - full_aug_list = [] - with open(osp.join(aug_path, 'dataset', 'train.txt')) as f: - full_aug_list += [line.strip() for line in f] - with open(osp.join(aug_path, 'dataset', 'val.txt')) as f: - full_aug_list += [line.strip() for line in f] - - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'train.txt')) as f: - ori_train_list = [line.strip() for line in f] - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'val.txt')) as f: - val_list = [line.strip() for line in f] - - aug_train_list = generate_aug_list(ori_train_list + full_aug_list, - val_list) - assert len(aug_train_list) == AUG_LEN, 'len(aug_train_list) != {}'.format( - AUG_LEN) - - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'trainaug.txt'), 'w') as f: - f.writelines(line + '\n' for line in aug_train_list) - - aug_list = generate_aug_list(full_aug_list, ori_train_list + val_list) - assert len(aug_list) == AUG_LEN - len( - ori_train_list), 'len(aug_list) != {}'.format(AUG_LEN - - len(ori_train_list)) - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', 'aug.txt'), - 'w') as f: - f.writelines(line + '\n' for line in aug_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/pspnet/pytorch/tools/get_flops.py b/cv/semantic_segmentation/pspnet/pytorch/tools/get_flops.py deleted file mode 100644 index e30c36fdf..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/tools/get_flops.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse - -from mmcv import Config -from mmcv.cnn import get_model_complexity_info - -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Get the FLOPs of a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument( - '--shape', - type=int, - nargs='+', - default=[2048, 1024], - help='input image size') - args = parser.parse_args() - return args - - -def main(): - - args = parse_args() - - if len(args.shape) == 1: - input_shape = (3, args.shape[0], args.shape[0]) - elif len(args.shape) == 2: - input_shape = (3, ) + tuple(args.shape) - else: - raise ValueError('invalid input shape') - - cfg = Config.fromfile(args.config) - cfg.model.pretrained = None - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')).cuda() - model.eval() - - if hasattr(model, 'forward_dummy'): - model.forward = model.forward_dummy - else: - raise NotImplementedError( - 'FLOPs counter is currently not currently supported with {}'. - format(model.__class__.__name__)) - - flops, params = get_model_complexity_info(model, input_shape) - split_line = '=' * 30 - print('{0}\nInput shape: {1}\nFlops: {2}\nParams: {3}\n{0}'.format( - split_line, input_shape, flops, params)) - print('!!!Please be cautious if you use the results in papers. ' - 'You may need to check if all ops are supported and verify that the ' - 'flops computation is correct.') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/pspnet/pytorch/tools/print_config.py b/cv/semantic_segmentation/pspnet/pytorch/tools/print_config.py deleted file mode 100644 index 3f9c08dd9..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/tools/print_config.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import warnings - -from mmcv import Config, DictAction - -from mmseg.apis import init_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='Print the whole config') - parser.add_argument('config', help='config file path') - parser.add_argument( - '--graph', action='store_true', help='print the models graph') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - args = parser.parse_args() - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options, ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - print(f'Config:\n{cfg.pretty_text}') - # dump config - cfg.dump('example.py') - # dump models graph - if args.graph: - model = init_segmentor(args.config, device='cpu') - print(f'Model graph:\n{str(model)}') - with open('example-graph.txt', 'w') as f: - f.writelines(str(model)) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/pspnet/pytorch/tools/slurm_test.sh b/cv/semantic_segmentation/pspnet/pytorch/tools/slurm_test.sh deleted file mode 100644 index 4e6f7bf4e..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/tools/slurm_test.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -set -x - -PARTITION=$1 -JOB_NAME=$2 -CONFIG=$3 -CHECKPOINT=$4 -GPUS=${GPUS:-4} -GPUS_PER_NODE=${GPUS_PER_NODE:-4} -CPUS_PER_TASK=${CPUS_PER_TASK:-5} -PY_ARGS=${@:5} -SRUN_ARGS=${SRUN_ARGS:-""} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -srun -p ${PARTITION} \ - --job-name=${JOB_NAME} \ - --gres=gpu:${GPUS_PER_NODE} \ - --ntasks=${GPUS} \ - --ntasks-per-node=${GPUS_PER_NODE} \ - --cpus-per-task=${CPUS_PER_TASK} \ - --kill-on-bad-exit=1 \ - ${SRUN_ARGS} \ - python -u tools/test.py ${CONFIG} ${CHECKPOINT} --launcher="slurm" ${PY_ARGS} diff --git a/cv/semantic_segmentation/pspnet/pytorch/tools/slurm_train.sh b/cv/semantic_segmentation/pspnet/pytorch/tools/slurm_train.sh deleted file mode 100644 index ab232105f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/tools/slurm_train.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -set -x - -PARTITION=$1 -JOB_NAME=$2 -CONFIG=$3 -GPUS=${GPUS:-4} -GPUS_PER_NODE=${GPUS_PER_NODE:-4} -CPUS_PER_TASK=${CPUS_PER_TASK:-5} -SRUN_ARGS=${SRUN_ARGS:-""} -PY_ARGS=${@:4} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -srun -p ${PARTITION} \ - --job-name=${JOB_NAME} \ - --gres=gpu:${GPUS_PER_NODE} \ - --ntasks=${GPUS} \ - --ntasks-per-node=${GPUS_PER_NODE} \ - --cpus-per-task=${CPUS_PER_TASK} \ - --kill-on-bad-exit=1 \ - ${SRUN_ARGS} \ - python -u tools/train.py ${CONFIG} --launcher="slurm" ${PY_ARGS} diff --git a/cv/semantic_segmentation/pspnet/pytorch/tools/test.py b/cv/semantic_segmentation/pspnet/pytorch/tools/test.py deleted file mode 100644 index 12892ec9b..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/tools/test.py +++ /dev/null @@ -1,319 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os -import os.path as osp -import shutil -import time -import warnings - -import mmcv -import torch -from mmcv.cnn.utils import revert_sync_batchnorm -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (get_dist_info, init_dist, load_checkpoint, - wrap_fp16_model) -from mmcv.utils import DictAction - -from mmseg import digit_version -from mmseg.apis import multi_gpu_test, single_gpu_test -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor -from mmseg.utils import setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser( - description='mmseg test (and eval) a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--work-dir', - help=('if specified, the evaluation metric results will be dumped' - 'into the directory as json')) - parser.add_argument( - '--aug-test', action='store_true', help='Use Flip and Multi scale aug') - parser.add_argument('--out', help='output result file in pickle format') - parser.add_argument( - '--format-only', - action='store_true', - help='Format the output results without perform evaluation. It is' - 'useful when you want to format the result to a specific format and ' - 'submit it to the test server') - parser.add_argument( - '--eval', - type=str, - nargs='+', - help='evaluation metrics, which depends on the dataset, e.g., "mIoU"' - ' for generic datasets, and "cityscapes" for Cityscapes') - parser.add_argument('--show', action='store_true', help='show results') - parser.add_argument( - '--show-dir', help='directory where painted images will be saved') - parser.add_argument( - '--gpu-collect', - action='store_true', - help='whether to use gpu to collect results.') - parser.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed testing)') - parser.add_argument( - '--tmpdir', - help='tmp directory used for collecting results from multiple ' - 'workers, available when gpu_collect is not specified') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--eval-options', - nargs='+', - action=DictAction, - help='custom options for evaluation') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument( - '--opacity', - type=float, - default=0.5, - help='Opacity of painted segmentation map. In (0, 1] range.') - parser.add_argument('--local_rank', type=int, default=0) - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - assert args.out or args.eval or args.format_only or args.show \ - or args.show_dir, \ - ('Please specify at least one operation (save/eval/format/show the ' - 'results / save the results) with the argument "--out", "--eval"' - ', "--format-only", "--show" or "--show-dir"') - - if args.eval and args.format_only: - raise ValueError('--eval and --format_only cannot be both specified') - - if args.out is not None and not args.out.endswith(('.pkl', '.pickle')): - raise ValueError('The output file must be a pkl file.') - - cfg = mmcv.Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - if args.aug_test: - # hard code index - cfg.data.test.pipeline[1].img_ratios = [ - 0.5, 0.75, 1.0, 1.25, 1.5, 1.75 - ] - cfg.data.test.pipeline[1].flip = True - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - if args.gpu_id is not None: - cfg.gpu_ids = [args.gpu_id] - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - cfg.gpu_ids = [args.gpu_id] - distributed = False - if len(cfg.gpu_ids) > 1: - warnings.warn(f'The gpu-ids is reset from {cfg.gpu_ids} to ' - f'{cfg.gpu_ids[0:1]} to avoid potential error in ' - 'non-distribute testing time.') - cfg.gpu_ids = cfg.gpu_ids[0:1] - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - - rank, _ = get_dist_info() - # allows not to create - if args.work_dir is not None and rank == 0: - mmcv.mkdir_or_exist(osp.abspath(args.work_dir)) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.aug_test: - json_file = osp.join(args.work_dir, - f'eval_multi_scale_{timestamp}.json') - else: - json_file = osp.join(args.work_dir, - f'eval_single_scale_{timestamp}.json') - elif rank == 0: - work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - mmcv.mkdir_or_exist(osp.abspath(work_dir)) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.aug_test: - json_file = osp.join(work_dir, - f'eval_multi_scale_{timestamp}.json') - else: - json_file = osp.join(work_dir, - f'eval_single_scale_{timestamp}.json') - - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - # The default loader config - loader_cfg = dict( - # cfg.gpus will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - shuffle=False) - # The overall dataloader settings - loader_cfg.update({ - k: v - for k, v in cfg.data.items() if k not in [ - 'train', 'val', 'test', 'train_dataloader', 'val_dataloader', - 'test_dataloader' - ] - }) - test_loader_cfg = { - **loader_cfg, - 'samples_per_gpu': 1, - 'shuffle': False, # Not shuffle by default - **cfg.data.get('test_dataloader', {}) - } - # build the dataloader - data_loader = build_dataloader(dataset, **test_loader_cfg) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - checkpoint = load_checkpoint(model, args.checkpoint, map_location='cpu') - if 'CLASSES' in checkpoint.get('meta', {}): - model.CLASSES = checkpoint['meta']['CLASSES'] - else: - print('"CLASSES" not found in meta, use dataset.CLASSES instead') - model.CLASSES = dataset.CLASSES - if 'PALETTE' in checkpoint.get('meta', {}): - model.PALETTE = checkpoint['meta']['PALETTE'] - else: - print('"PALETTE" not found in meta, use dataset.PALETTE instead') - model.PALETTE = dataset.PALETTE - - # clean gpu memory when starting a new evaluation. - torch.cuda.empty_cache() - eval_kwargs = {} if args.eval_options is None else args.eval_options - - # Deprecated - efficient_test = eval_kwargs.get('efficient_test', False) - if efficient_test: - warnings.warn( - '``efficient_test=True`` does not have effect in tools/test.py, ' - 'the evaluation and format results are CPU memory efficient by ' - 'default') - - eval_on_format_results = ( - args.eval is not None and 'cityscapes' in args.eval) - if eval_on_format_results: - assert len(args.eval) == 1, 'eval on format results is not ' \ - 'applicable for metrics other than ' \ - 'cityscapes' - if args.format_only or eval_on_format_results: - if 'imgfile_prefix' in eval_kwargs: - tmpdir = eval_kwargs['imgfile_prefix'] - else: - tmpdir = '.format_cityscapes' - eval_kwargs.setdefault('imgfile_prefix', tmpdir) - mmcv.mkdir_or_exist(tmpdir) - else: - tmpdir = None - - if not distributed: - warnings.warn( - 'SyncBN is only supported with DDP. To be compatible with DP, ' - 'we convert SyncBN to BN. Please use dist_train.sh which can ' - 'avoid this error.') - if not torch.cuda.is_available(): - assert digit_version(mmcv.__version__) >= digit_version('1.4.4'), \ - 'Please use MMCV >= 1.4.4 for CPU training!' - model = revert_sync_batchnorm(model) - model = MMDataParallel(model, device_ids=cfg.gpu_ids) - results = single_gpu_test( - model, - data_loader, - args.show, - args.show_dir, - False, - args.opacity, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - else: - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False) - results = multi_gpu_test( - model, - data_loader, - args.tmpdir, - args.gpu_collect, - False, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - - rank, _ = get_dist_info() - if rank == 0: - if args.out: - warnings.warn( - 'The behavior of ``args.out`` has been changed since MMSeg ' - 'v0.16, the pickled outputs could be seg map as type of ' - 'np.array, pre-eval results or file paths for ' - '``dataset.format_results()``.') - print(f'\nwriting results to {args.out}') - mmcv.dump(results, args.out) - if args.eval: - eval_kwargs.update(metric=args.eval) - metric = dataset.evaluate(results, **eval_kwargs) - metric_dict = dict(config=args.config, metric=metric) - mmcv.dump(metric_dict, json_file, indent=4) - if tmpdir is not None and eval_on_format_results: - # remove tmp dir when cityscapes evaluation - shutil.rmtree(tmpdir) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/pspnet/pytorch/train.py b/cv/semantic_segmentation/pspnet/pytorch/train.py deleted file mode 100644 index e198dd60f..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/train.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import copy -import os -import os.path as osp -import time -import warnings - -import mmcv -import torch -import torch.distributed as dist -from mmcv.cnn.utils import revert_sync_batchnorm -from mmcv.runner import get_dist_info, init_dist -from mmcv.utils import Config, DictAction, get_git_hash - -from mmseg import __version__ -from mmseg.apis import init_random_seed, set_random_seed, train_segmentor -from mmseg.datasets import build_dataset -from mmseg.models import build_segmentor -from mmseg.utils import collect_env, get_root_logger, setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument('--work-dir', help='the dir to save logs and models') - parser.add_argument( - '--load-from', help='the checkpoint file to load weights from') - parser.add_argument( - '--resume-from', help='the checkpoint file to resume from') - parser.add_argument( - '--no-validate', - action='store_true', - help='whether not to evaluate the checkpoint during training') - group_gpus = parser.add_mutually_exclusive_group() - group_gpus.add_argument( - '--gpus', - type=int, - help='(Deprecated, please use --gpu-id) number of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-ids', - type=int, - nargs='+', - help='(Deprecated, please use --gpu-id) ids of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed training)') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--diff_seed', - action='store_true', - help='Whether or not set different seeds for different ranks') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - parser.add_argument('--dist_backend', type=str, default=None) - parser.add_argument( - '--auto-resume', - action='store_true', - help='resume from the latest checkpoint automatically.') - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - - # work_dir is determined in this priority: CLI > segment in file > filename - if args.work_dir is not None: - # update configs according to CLI args if args.work_dir is not None - cfg.work_dir = args.work_dir - elif cfg.get('work_dir', None) is None: - # use config filename as default work_dir if cfg.work_dir is None - cfg.work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - if args.load_from is not None: - cfg.load_from = args.load_from - if args.resume_from is not None: - cfg.resume_from = args.resume_from - if args.gpus is not None: - cfg.gpu_ids = range(1) - warnings.warn('`--gpus` is deprecated because we only support ' - 'single GPU mode in non-distributed training. ' - 'Use `gpus=1` now.') - if args.gpu_ids is not None: - cfg.gpu_ids = args.gpu_ids[0:1] - warnings.warn('`--gpu-ids` is deprecated, please use `--gpu-id`. ' - 'Because we only support single GPU mode in ' - 'non-distributed training. Use the first GPU ' - 'in `gpu_ids` now.') - if args.gpus is None and args.gpu_ids is None: - cfg.gpu_ids = [args.gpu_id] - - cfg.auto_resume = args.auto_resume - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - if args.dist_backend is not None: - cfg.dist_params.backend = args.dist_backend - init_dist(args.launcher, **cfg.dist_params) - # gpu_ids is used to calculate iter when resuming checkpoint - _, world_size = get_dist_info() - cfg.gpu_ids = range(world_size) - - # create work_dir - mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) - # dump config - cfg.dump(osp.join(cfg.work_dir, osp.basename(args.config))) - # init the logger before other steps - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - log_file = osp.join(cfg.work_dir, f'{timestamp}.log') - logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) - - # set multi-process settings - setup_multi_processes(cfg) - - # init the meta dict to record some important information such as - # environment info and seed, which will be logged - meta = dict() - # log env info - env_info_dict = collect_env() - env_info = '\n'.join([f'{k}: {v}' for k, v in env_info_dict.items()]) - dash_line = '-' * 60 + '\n' - logger.info('Environment info:\n' + dash_line + env_info + '\n' + - dash_line) - meta['env_info'] = env_info - - # log some basic info - logger.info(f'Distributed training: {distributed}') - logger.info(f'Config:\n{cfg.pretty_text}') - - # set random seeds - seed = init_random_seed(args.seed) - seed = seed + dist.get_rank() if args.diff_seed else seed - logger.info(f'Set random seed to {seed}, ' - f'deterministic: {args.deterministic}') - set_random_seed(seed, deterministic=args.deterministic) - cfg.seed = seed - meta['seed'] = seed - meta['exp_name'] = osp.basename(args.config) - - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')) - model.init_weights() - - # SyncBN is not support for DP - if not distributed: - warnings.warn( - 'SyncBN is only supported with DDP. To be compatible with DP, ' - 'we convert SyncBN to BN. Please use dist_train.sh which can ' - 'avoid this error.') - model = revert_sync_batchnorm(model) - - logger.info(model) - - datasets = [build_dataset(cfg.data.train)] - if len(cfg.workflow) == 2: - val_dataset = copy.deepcopy(cfg.data.val) - val_dataset.pipeline = cfg.data.train.pipeline - datasets.append(build_dataset(val_dataset)) - if cfg.checkpoint_config is not None: - # save mmseg version, config file content and class names in - # checkpoints as meta data - cfg.checkpoint_config.meta = dict( - mmseg_version=f'{__version__}+{get_git_hash()[:7]}', - config=cfg.pretty_text, - CLASSES=datasets[0].CLASSES, - PALETTE=datasets[0].PALETTE) - # add an attribute for visualization convenience - model.CLASSES = datasets[0].CLASSES - # passing checkpoint meta for saving best checkpoint - meta.update(cfg.checkpoint_config.meta) - train_segmentor( - model, - datasets, - cfg, - distributed=distributed, - validate=(not args.no_validate), - timestamp=timestamp, - meta=meta) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/pspnet/pytorch/train.sh b/cv/semantic_segmentation/pspnet/pytorch/train.sh deleted file mode 100644 index 82c28c20a..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/train.sh +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -CONFIG=$1 - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 $(dirname "$0")/train.py \ - $CONFIG \ - --launcher pytorch ${@:2} diff --git a/cv/semantic_segmentation/pspnet/pytorch/train_dist.sh b/cv/semantic_segmentation/pspnet/pytorch/train_dist.sh deleted file mode 100644 index 6c02b3a31..000000000 --- a/cv/semantic_segmentation/pspnet/pytorch/train_dist.sh +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -CONFIG=$1 -GPUS=$2 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/train.py \ - $CONFIG \ - --launcher pytorch ${@:3} -- Gitee From 4b12f50a81dc946d6fa1de10c8cbea9d662a8f03 Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Thu, 6 Mar 2025 09:39:38 +0800 Subject: [PATCH 08/15] update stdc --- .../stdc/pytorch/.gitignore | 120 -- .../stdc/pytorch/CITATION.cff | 8 - cv/semantic_segmentation/stdc/pytorch/LICENSE | 203 --- .../stdc/pytorch/README.md | 115 +- .../pytorch/configs/_base_/datasets/ade20k.py | 54 - .../configs/_base_/datasets/ade20k_640x640.py | 54 - .../configs/_base_/datasets/cityscapes.py | 54 - .../_base_/datasets/cityscapes_1024x1024.py | 35 - .../_base_/datasets/cityscapes_768x768.py | 35 - .../_base_/datasets/cityscapes_769x769.py | 35 - .../_base_/datasets/cityscapes_832x832.py | 35 - .../configs/_base_/datasets/coco-stuff10k.py | 57 - .../configs/_base_/datasets/coco-stuff164k.py | 54 - .../configs/_base_/datasets/pascal_context.py | 60 - .../_base_/datasets/pascal_context_59.py | 60 - .../configs/_base_/datasets/pascal_voc12.py | 58 - .../_base_/datasets/pascal_voc12_aug.py | 9 - .../pytorch/configs/_base_/default_runtime.py | 14 - .../pytorch/configs/_base_/models/stdc.py | 83 - .../configs/_base_/schedules/schedule_160k.py | 9 - .../configs/_base_/schedules/schedule_1k.py | 9 - .../configs/_base_/schedules/schedule_20k.py | 9 - .../configs/_base_/schedules/schedule_320k.py | 9 - .../configs/_base_/schedules/schedule_40k.py | 9 - .../configs/_base_/schedules/schedule_80k.py | 9 - .../stdc/pytorch/configs/stdc/README.md | 73 - .../stdc/pytorch/configs/stdc/stdc.yml | 87 -- .../stdc/stdc1_512x1024_80k_cityscapes.py | 9 - .../stdc1_in1k-pre_512x1024_80k_cityscapes.py | 6 - .../stdc/stdc2_512x1024_80k_cityscapes.py | 2 - .../stdc2_in1k-pre_512x1024_80k_cityscapes.py | 6 - .../stdc/pytorch/docker/Dockerfile | 32 - .../stdc/pytorch/docker/serve/Dockerfile | 49 - .../pytorch/docker/serve/config.properties | 5 - .../stdc/pytorch/docker/serve/entrypoint.sh | 12 - .../stdc/pytorch/mmcv/__init__.py | 13 - .../stdc/pytorch/mmcv/cnn/__init__.py | 21 - .../stdc/pytorch/mmcv/cnn/bricks/__init__.py | 35 - .../pytorch/mmcv/cnn/bricks/activation.py | 93 -- .../pytorch/mmcv/cnn/bricks/context_block.py | 125 -- .../stdc/pytorch/mmcv/cnn/bricks/conv.py | 44 - .../cnn/bricks/conv2d_adaptive_padding.py | 62 - .../pytorch/mmcv/cnn/bricks/conv_module.py | 206 --- .../stdc/pytorch/mmcv/cnn/bricks/conv_ws.py | 148 -- .../bricks/depthwise_separable_conv_module.py | 96 -- .../stdc/pytorch/mmcv/cnn/bricks/drop.py | 65 - .../mmcv/cnn/bricks/generalized_attention.py | 412 ----- .../stdc/pytorch/mmcv/cnn/bricks/hsigmoid.py | 46 - .../stdc/pytorch/mmcv/cnn/bricks/hswish.py | 38 - .../stdc/pytorch/mmcv/cnn/bricks/non_local.py | 306 ---- .../stdc/pytorch/mmcv/cnn/bricks/norm.py | 144 -- .../stdc/pytorch/mmcv/cnn/bricks/padding.py | 36 - .../stdc/pytorch/mmcv/cnn/bricks/plugin.py | 89 -- .../stdc/pytorch/mmcv/cnn/bricks/registry.py | 16 - .../stdc/pytorch/mmcv/cnn/bricks/scale.py | 21 - .../stdc/pytorch/mmcv/cnn/bricks/swish.py | 25 - .../pytorch/mmcv/cnn/bricks/transformer.py | 944 ------------ .../stdc/pytorch/mmcv/cnn/bricks/upsample.py | 84 -- .../stdc/pytorch/mmcv/cnn/bricks/wrappers.py | 180 --- .../stdc/pytorch/mmcv/cnn/builder.py | 30 - .../stdc/pytorch/mmcv/cnn/utils/__init__.py | 19 - .../pytorch/mmcv/cnn/utils/flops_counter.py | 599 -------- .../pytorch/mmcv/cnn/utils/fuse_conv_bn.py | 59 - .../stdc/pytorch/mmcv/cnn/utils/sync_bn.py | 60 - .../pytorch/mmcv/cnn/utils/weight_init.py | 685 --------- .../stdc/pytorch/mmcv/device/__init__.py | 4 - .../stdc/pytorch/mmcv/device/ipu/__init__.py | 14 - .../pytorch/mmcv/device/ipu/dataloader.py | 157 -- .../device/ipu/hierarchical_data_manager.py | 243 --- .../pytorch/mmcv/device/ipu/hook_wrapper.py | 105 -- .../pytorch/mmcv/device/ipu/model_wrapper.py | 721 --------- .../stdc/pytorch/mmcv/device/ipu/runner.py | 142 -- .../stdc/pytorch/mmcv/device/ipu/utils.py | 244 --- .../stdc/pytorch/mmcv/device/mlu/__init__.py | 9 - .../pytorch/mmcv/device/mlu/_functions.py | 22 - .../pytorch/mmcv/device/mlu/data_parallel.py | 41 - .../pytorch/mmcv/device/mlu/distributed.py | 20 - .../pytorch/mmcv/device/mlu/scatter_gather.py | 59 - .../stdc/pytorch/mmcv/engine/__init__.py | 8 - .../stdc/pytorch/mmcv/engine/test.py | 202 --- .../stdc/pytorch/mmcv/fileio/__init__.py | 11 - .../stdc/pytorch/mmcv/fileio/file_client.py | 1163 -------------- .../pytorch/mmcv/fileio/handlers/__init__.py | 7 - .../stdc/pytorch/mmcv/fileio/handlers/base.py | 30 - .../mmcv/fileio/handlers/json_handler.py | 36 - .../mmcv/fileio/handlers/pickle_handler.py | 28 - .../mmcv/fileio/handlers/yaml_handler.py | 25 - .../stdc/pytorch/mmcv/fileio/io.py | 151 -- .../stdc/pytorch/mmcv/fileio/parse.py | 97 -- .../stdc/pytorch/mmcv/image/__init__.py | 28 - .../stdc/pytorch/mmcv/image/colorspace.py | 306 ---- .../stdc/pytorch/mmcv/image/geometric.py | 741 --------- .../stdc/pytorch/mmcv/image/io.py | 314 ---- .../stdc/pytorch/mmcv/image/misc.py | 53 - .../stdc/pytorch/mmcv/image/photometric.py | 428 ------ .../pytorch/mmcv/model_zoo/deprecated.json | 6 - .../stdc/pytorch/mmcv/model_zoo/mmcls.json | 59 - .../pytorch/mmcv/model_zoo/open_mmlab.json | 50 - .../mmcv/model_zoo/torchvision_0.12.json | 57 - .../stdc/pytorch/mmcv/ops/__init__.py | 12 - .../stdc/pytorch/mmcv/ops/cc_attention.py | 84 -- .../stdc/pytorch/mmcv/ops/csrc/README.md | 170 --- .../csrc/common/cuda/common_cuda_helper.hpp | 120 -- .../csrc/common/cuda/psamask_cuda_kernel.cuh | 141 -- .../cuda/sigmoid_focal_loss_cuda_kernel.cuh | 71 - .../cuda/softmax_focal_loss_cuda_kernel.cuh | 72 - .../csrc/common/cuda/sync_bn_cuda_kernel.cuh | 331 ---- .../ops/csrc/common/pytorch_cpp_helper.hpp | 27 - .../ops/csrc/common/pytorch_cuda_helper.hpp | 19 - .../csrc/common/pytorch_device_registry.hpp | 141 -- .../mmcv/ops/csrc/pytorch/cuda/cudabind.cpp | 210 --- .../ops/csrc/pytorch/cuda/focal_loss_cuda.cu | 111 -- .../ops/csrc/pytorch/cuda/psamask_cuda.cu | 60 - .../ops/csrc/pytorch/cuda/sync_bn_cuda.cu | 110 -- .../mmcv/ops/csrc/pytorch/focal_loss.cpp | 53 - .../pytorch/mmcv/ops/csrc/pytorch/info.cpp | 56 - .../pytorch/mmcv/ops/csrc/pytorch/psamask.cpp | 41 - .../pytorch/mmcv/ops/csrc/pytorch/pybind.cpp | 106 -- .../pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp | 69 - .../pytorch/mmcv/ops/deprecated_wrappers.py | 46 - .../stdc/pytorch/mmcv/ops/focal_loss.py | 213 --- .../stdc/pytorch/mmcv/ops/info.py | 36 - .../stdc/pytorch/mmcv/ops/point_sample.py | 346 ----- .../stdc/pytorch/mmcv/ops/psa_mask.py | 92 -- .../stdc/pytorch/mmcv/ops/sync_bn.py | 279 ---- .../stdc/pytorch/mmcv/parallel/__init__.py | 13 - .../stdc/pytorch/mmcv/parallel/_functions.py | 76 - .../stdc/pytorch/mmcv/parallel/collate.py | 84 -- .../pytorch/mmcv/parallel/data_container.py | 89 -- .../pytorch/mmcv/parallel/data_parallel.py | 97 -- .../stdc/pytorch/mmcv/parallel/distributed.py | 138 -- .../mmcv/parallel/distributed_deprecated.py | 70 - .../stdc/pytorch/mmcv/parallel/registry.py | 8 - .../pytorch/mmcv/parallel/scatter_gather.py | 59 - .../stdc/pytorch/mmcv/parallel/utils.py | 20 - .../stdc/pytorch/mmcv/runner/__init__.py | 73 - .../stdc/pytorch/mmcv/runner/base_module.py | 208 --- .../stdc/pytorch/mmcv/runner/base_runner.py | 544 ------- .../stdc/pytorch/mmcv/runner/builder.py | 24 - .../stdc/pytorch/mmcv/runner/checkpoint.py | 759 ---------- .../mmcv/runner/default_constructor.py | 45 - .../stdc/pytorch/mmcv/runner/dist_utils.py | 204 --- .../pytorch/mmcv/runner/epoch_based_runner.py | 188 --- .../stdc/pytorch/mmcv/runner/fp16_utils.py | 423 ------ .../pytorch/mmcv/runner/hooks/__init__.py | 48 - .../pytorch/mmcv/runner/hooks/checkpoint.py | 167 --- .../stdc/pytorch/mmcv/runner/hooks/closure.py | 11 - .../stdc/pytorch/mmcv/runner/hooks/ema.py | 89 -- .../pytorch/mmcv/runner/hooks/evaluation.py | 511 ------- .../stdc/pytorch/mmcv/runner/hooks/hook.py | 92 -- .../pytorch/mmcv/runner/hooks/iter_timer.py | 31 - .../mmcv/runner/hooks/logger/__init__.py | 18 - .../pytorch/mmcv/runner/hooks/logger/base.py | 167 --- .../mmcv/runner/hooks/logger/clearml.py | 62 - .../mmcv/runner/hooks/logger/dvclive.py | 68 - .../mmcv/runner/hooks/logger/mlflow.py | 80 - .../mmcv/runner/hooks/logger/neptune.py | 88 -- .../pytorch/mmcv/runner/hooks/logger/pavi.py | 132 -- .../mmcv/runner/hooks/logger/segmind.py | 49 - .../mmcv/runner/hooks/logger/tensorboard.py | 69 - .../pytorch/mmcv/runner/hooks/logger/text.py | 256 ---- .../pytorch/mmcv/runner/hooks/logger/wandb.py | 107 -- .../pytorch/mmcv/runner/hooks/lr_updater.py | 730 --------- .../stdc/pytorch/mmcv/runner/hooks/memory.py | 25 - .../mmcv/runner/hooks/momentum_updater.py | 566 ------- .../pytorch/mmcv/runner/hooks/optimizer.py | 556 ------- .../pytorch/mmcv/runner/hooks/profiler.py | 180 --- .../pytorch/mmcv/runner/hooks/sampler_seed.py | 20 - .../pytorch/mmcv/runner/hooks/sync_buffer.py | 22 - .../pytorch/mmcv/runner/iter_based_runner.py | 273 ---- .../stdc/pytorch/mmcv/runner/log_buffer.py | 41 - .../pytorch/mmcv/runner/optimizer/__init__.py | 9 - .../pytorch/mmcv/runner/optimizer/builder.py | 44 - .../runner/optimizer/default_constructor.py | 250 --- .../stdc/pytorch/mmcv/runner/priority.py | 60 - .../stdc/pytorch/mmcv/runner/utils.py | 93 -- .../stdc/pytorch/mmcv/utils/__init__.py | 78 - .../stdc/pytorch/mmcv/utils/config.py | 719 --------- .../stdc/pytorch/mmcv/utils/device_type.py | 24 - .../stdc/pytorch/mmcv/utils/env.py | 120 -- .../stdc/pytorch/mmcv/utils/ext_loader.py | 72 - .../stdc/pytorch/mmcv/utils/hub.py | 131 -- .../stdc/pytorch/mmcv/utils/logging.py | 111 -- .../stdc/pytorch/mmcv/utils/misc.py | 377 ----- .../stdc/pytorch/mmcv/utils/parrots_jit.py | 41 - .../pytorch/mmcv/utils/parrots_wrapper.py | 114 -- .../stdc/pytorch/mmcv/utils/path.py | 101 -- .../stdc/pytorch/mmcv/utils/progressbar.py | 208 --- .../stdc/pytorch/mmcv/utils/registry.py | 337 ----- .../stdc/pytorch/mmcv/utils/seed.py | 23 - .../stdc/pytorch/mmcv/utils/testing.py | 141 -- .../stdc/pytorch/mmcv/utils/timer.py | 118 -- .../stdc/pytorch/mmcv/utils/trace.py | 24 - .../stdc/pytorch/mmcv/utils/version_utils.py | 90 -- .../stdc/pytorch/mmcv/version.py | 35 - .../stdc/pytorch/mmseg/__init__.py | 62 - .../stdc/pytorch/mmseg/apis/__init__.py | 11 - .../stdc/pytorch/mmseg/apis/inference.py | 136 -- .../stdc/pytorch/mmseg/apis/test.py | 233 --- .../stdc/pytorch/mmseg/apis/train.py | 196 --- .../stdc/pytorch/mmseg/core/__init__.py | 11 - .../stdc/pytorch/mmseg/core/builder.py | 33 - .../pytorch/mmseg/core/evaluation/__init__.py | 11 - .../mmseg/core/evaluation/class_names.py | 316 ---- .../mmseg/core/evaluation/eval_hooks.py | 128 -- .../pytorch/mmseg/core/evaluation/metrics.py | 395 ----- .../pytorch/mmseg/core/optimizers/__init__.py | 7 - .../layer_decay_optimizer_constructor.py | 208 --- .../stdc/pytorch/mmseg/core/seg/__init__.py | 5 - .../stdc/pytorch/mmseg/core/seg/builder.py | 9 - .../mmseg/core/seg/sampler/__init__.py | 5 - .../core/seg/sampler/base_pixel_sampler.py | 13 - .../core/seg/sampler/ohem_pixel_sampler.py | 85 -- .../stdc/pytorch/mmseg/core/utils/__init__.py | 5 - .../pytorch/mmseg/core/utils/dist_util.py | 46 - .../stdc/pytorch/mmseg/core/utils/misc.py | 18 - .../stdc/pytorch/mmseg/datasets/__init__.py | 10 - .../stdc/pytorch/mmseg/datasets/ade.py | 167 --- .../stdc/pytorch/mmseg/datasets/builder.py | 191 --- .../stdc/pytorch/mmseg/datasets/cityscapes.py | 214 --- .../stdc/pytorch/mmseg/datasets/coco_stuff.py | 94 -- .../stdc/pytorch/mmseg/datasets/custom.py | 487 ------ .../mmseg/datasets/dataset_wrappers.py | 277 ---- .../pytorch/mmseg/datasets/pascal_context.py | 103 -- .../mmseg/datasets/pipelines/__init__.py | 19 - .../mmseg/datasets/pipelines/compose.py | 52 - .../mmseg/datasets/pipelines/formating.py | 9 - .../mmseg/datasets/pipelines/formatting.py | 289 ---- .../mmseg/datasets/pipelines/loading.py | 158 -- .../mmseg/datasets/pipelines/test_time_aug.py | 134 -- .../mmseg/datasets/pipelines/transforms.py | 1335 ----------------- .../mmseg/datasets/samplers/__init__.py | 4 - .../datasets/samplers/distributed_sampler.py | 71 - .../stdc/pytorch/mmseg/datasets/voc.py | 39 - .../stdc/pytorch/mmseg/models/__init__.py | 13 - .../mmseg/models/backbones/__init__.py | 6 - .../mmseg/models/backbones/bisenetv1.py | 332 ---- .../pytorch/mmseg/models/backbones/resnest.py | 318 ---- .../pytorch/mmseg/models/backbones/resnet.py | 714 --------- .../pytorch/mmseg/models/backbones/resnext.py | 150 -- .../pytorch/mmseg/models/backbones/stdc.py | 422 ------ .../mmseg/models/backbones/timm_backbone.py | 63 - .../stdc/pytorch/mmseg/models/builder.py | 49 - .../mmseg/models/decode_heads/__init__.py | 6 - .../mmseg/models/decode_heads/aspp_head.py | 122 -- .../mmseg/models/decode_heads/cc_head.py | 43 - .../mmseg/models/decode_heads/decode_head.py | 266 ---- .../mmseg/models/decode_heads/fcn_head.py | 96 -- .../mmseg/models/decode_heads/stdc_head.py | 85 -- .../pytorch/mmseg/models/losses/__init__.py | 15 - .../pytorch/mmseg/models/losses/accuracy.py | 92 -- .../mmseg/models/losses/cross_entropy_loss.py | 296 ---- .../pytorch/mmseg/models/losses/dice_loss.py | 137 -- .../pytorch/mmseg/models/losses/focal_loss.py | 327 ---- .../mmseg/models/losses/lovasz_loss.py | 323 ---- .../stdc/pytorch/mmseg/models/losses/utils.py | 126 -- .../pytorch/mmseg/models/necks/__init__.py | 11 - .../mmseg/models/necks/featurepyramid.py | 67 - .../stdc/pytorch/mmseg/models/necks/fpn.py | 213 --- .../pytorch/mmseg/models/necks/ic_neck.py | 148 -- .../stdc/pytorch/mmseg/models/necks/jpu.py | 131 -- .../pytorch/mmseg/models/necks/mla_neck.py | 118 -- .../mmseg/models/necks/multilevel_neck.py | 78 - .../mmseg/models/segmentors/__init__.py | 6 - .../pytorch/mmseg/models/segmentors/base.py | 291 ---- .../segmentors/cascade_encoder_decoder.py | 88 -- .../models/segmentors/encoder_decoder.py | 284 ---- .../pytorch/mmseg/models/utils/__init__.py | 18 - .../stdc/pytorch/mmseg/models/utils/embed.py | 330 ---- .../mmseg/models/utils/inverted_residual.py | 213 --- .../mmseg/models/utils/make_divisible.py | 28 - .../pytorch/mmseg/models/utils/res_layer.py | 96 -- .../pytorch/mmseg/models/utils/se_layer.py | 58 - .../models/utils/self_attention_block.py | 160 -- .../mmseg/models/utils/shape_convert.py | 107 -- .../mmseg/models/utils/up_conv_block.py | 102 -- .../pytorch/mmseg/models/utils/wrappers.py | 51 - .../stdc/pytorch/mmseg/ops/__init__.py | 5 - .../stdc/pytorch/mmseg/ops/encoding.py | 75 - .../stdc/pytorch/mmseg/ops/wrappers.py | 51 - .../stdc/pytorch/mmseg/registry/__init__.py | 15 - .../stdc/pytorch/mmseg/registry/registry.py | 116 -- .../stdc/pytorch/mmseg/utils/__init__.py | 10 - .../stdc/pytorch/mmseg/utils/collect_env.py | 18 - .../stdc/pytorch/mmseg/utils/logger.py | 28 - .../stdc/pytorch/mmseg/utils/misc.py | 41 - .../stdc/pytorch/mmseg/utils/set_env.py | 55 - .../stdc/pytorch/mmseg/version.py | 18 - .../stdc/pytorch/requirements.txt | 4 - .../stdc/pytorch/requirements/apcnet.txt | 4 - .../stdc/pytorch/requirements/mmcv/build.txt | 1 - .../stdc/pytorch/requirements/mmcv/docs.txt | 8 - .../pytorch/requirements/mmcv/optional.txt | 1 - .../requirements/mmcv/requirements.txt | 4 - .../pytorch/requirements/mmcv/runtime.txt | 7 - .../stdc/pytorch/requirements/mmcv/test.txt | 9 - .../stdc/pytorch/requirements/mminstall.txt | 2 - .../stdc/pytorch/requirements/optional.txt | 1 - .../stdc/pytorch/requirements/runtime.txt | 5 - .../stdc/pytorch/setup.py | 258 ---- .../stdc/pytorch/tools/analyze_logs.py | 128 -- .../stdc/pytorch/tools/benchmark.py | 120 -- .../stdc/pytorch/tools/confusion_matrix.py | 184 --- .../tools/convert_datasets/cityscapes.py | 56 - .../tools/convert_datasets/coco_stuff10k.py | 307 ---- .../tools/convert_datasets/coco_stuff164k.py | 264 ---- .../tools/convert_datasets/pascal_context.py | 87 -- .../pytorch/tools/convert_datasets/voc_aug.py | 92 -- .../stdc/pytorch/tools/get_flops.py | 60 - .../stdc/pytorch/tools/print_config.py | 69 - .../stdc/pytorch/tools/slurm_test.sh | 24 - .../stdc/pytorch/tools/slurm_train.sh | 23 - .../stdc/pytorch/tools/test.py | 319 ---- .../stdc/pytorch/train.py | 243 --- .../stdc/pytorch/train_dist.sh | 19 - 315 files changed, 20 insertions(+), 40572 deletions(-) delete mode 100644 cv/semantic_segmentation/stdc/pytorch/.gitignore delete mode 100644 cv/semantic_segmentation/stdc/pytorch/CITATION.cff delete mode 100644 cv/semantic_segmentation/stdc/pytorch/LICENSE delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/ade20k.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/ade20k_640x640.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_768x768.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_769x769.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_832x832.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/coco-stuff10k.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/coco-stuff164k.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_context_59.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_voc12.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_voc12_aug.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/default_runtime.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/models/stdc.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_160k.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_1k.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_20k.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_320k.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_40k.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_80k.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/stdc/README.md delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc.yml delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc1_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc1_in1k-pre_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc2_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc2_in1k-pre_512x1024_80k_cityscapes.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/docker/Dockerfile delete mode 100644 cv/semantic_segmentation/stdc/pytorch/docker/serve/Dockerfile delete mode 100644 cv/semantic_segmentation/stdc/pytorch/docker/serve/config.properties delete mode 100644 cv/semantic_segmentation/stdc/pytorch/docker/serve/entrypoint.sh delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/activation.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/context_block.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv_module.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv_ws.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/drop.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/generalized_attention.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/hsigmoid.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/hswish.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/non_local.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/norm.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/padding.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/plugin.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/registry.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/scale.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/swish.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/transformer.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/upsample.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/wrappers.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/builder.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/flops_counter.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/fuse_conv_bn.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/sync_bn.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/weight_init.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/device/__init__.py delete mode 100755 cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/__init__.py delete mode 100755 cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/dataloader.py delete mode 100755 cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/hierarchical_data_manager.py delete mode 100755 cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/hook_wrapper.py delete mode 100755 cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/model_wrapper.py delete mode 100755 cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/runner.py delete mode 100755 cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/utils.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/_functions.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/data_parallel.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/distributed.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/scatter_gather.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/engine/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/engine/test.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/file_client.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/base.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/json_handler.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/pickle_handler.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/yaml_handler.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/io.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/parse.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/image/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/image/colorspace.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/image/geometric.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/image/io.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/image/misc.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/image/photometric.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/deprecated.json delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/mmcls.json delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/open_mmlab.json delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/torchvision_0.12.json delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/cc_attention.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/README.md delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/info.cpp delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/deprecated_wrappers.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/focal_loss.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/info.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/point_sample.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/psa_mask.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/ops/sync_bn.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/_functions.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/collate.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/data_container.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/data_parallel.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/distributed.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/distributed_deprecated.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/registry.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/scatter_gather.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/utils.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/base_module.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/base_runner.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/builder.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/checkpoint.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/default_constructor.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/dist_utils.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/epoch_based_runner.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/fp16_utils.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/checkpoint.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/closure.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/ema.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/evaluation.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/hook.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/iter_timer.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/base.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/clearml.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/dvclive.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/mlflow.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/neptune.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/pavi.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/segmind.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/tensorboard.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/text.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/wandb.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/lr_updater.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/memory.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/momentum_updater.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/optimizer.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/profiler.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/sampler_seed.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/sync_buffer.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/iter_based_runner.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/log_buffer.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/optimizer/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/optimizer/builder.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/optimizer/default_constructor.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/priority.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/runner/utils.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/config.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/device_type.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/env.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/ext_loader.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/hub.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/logging.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/misc.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/parrots_jit.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/parrots_wrapper.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/path.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/progressbar.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/registry.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/seed.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/testing.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/timer.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/trace.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/utils/version_utils.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmcv/version.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/apis/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/apis/inference.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/apis/test.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/apis/train.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/builder.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/class_names.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/eval_hooks.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/metrics.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/optimizers/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/builder.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/sampler/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/utils/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/utils/dist_util.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/core/utils/misc.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/ade.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/builder.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/coco_stuff.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/custom.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/dataset_wrappers.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/compose.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/formating.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/formatting.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/loading.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/test_time_aug.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/transforms.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/samplers/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/samplers/distributed_sampler.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/voc.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/bisenetv1.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/resnest.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/resnet.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/resnext.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/stdc.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/timm_backbone.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/builder.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/aspp_head.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/cc_head.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/decode_head.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/fcn_head.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/stdc_head.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/accuracy.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/cross_entropy_loss.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/dice_loss.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/focal_loss.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/lovasz_loss.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/utils.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/featurepyramid.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/fpn.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/ic_neck.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/jpu.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/mla_neck.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/multilevel_neck.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/base.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/encoder_decoder.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/embed.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/inverted_residual.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/make_divisible.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/res_layer.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/se_layer.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/self_attention_block.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/shape_convert.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/up_conv_block.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/wrappers.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/ops/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/ops/encoding.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/ops/wrappers.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/registry/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/registry/registry.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/utils/__init__.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/utils/collect_env.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/utils/logger.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/utils/misc.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/utils/set_env.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/mmseg/version.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/requirements.txt delete mode 100644 cv/semantic_segmentation/stdc/pytorch/requirements/apcnet.txt delete mode 100644 cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/build.txt delete mode 100644 cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/docs.txt delete mode 100644 cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/optional.txt delete mode 100644 cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/requirements.txt delete mode 100644 cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/runtime.txt delete mode 100644 cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/test.txt delete mode 100644 cv/semantic_segmentation/stdc/pytorch/requirements/mminstall.txt delete mode 100644 cv/semantic_segmentation/stdc/pytorch/requirements/optional.txt delete mode 100644 cv/semantic_segmentation/stdc/pytorch/requirements/runtime.txt delete mode 100644 cv/semantic_segmentation/stdc/pytorch/setup.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/tools/analyze_logs.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/tools/benchmark.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/tools/confusion_matrix.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/cityscapes.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/coco_stuff10k.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/coco_stuff164k.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/pascal_context.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/voc_aug.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/tools/get_flops.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/tools/print_config.py delete mode 100755 cv/semantic_segmentation/stdc/pytorch/tools/slurm_test.sh delete mode 100755 cv/semantic_segmentation/stdc/pytorch/tools/slurm_train.sh delete mode 100644 cv/semantic_segmentation/stdc/pytorch/tools/test.py delete mode 100644 cv/semantic_segmentation/stdc/pytorch/train.py delete mode 100755 cv/semantic_segmentation/stdc/pytorch/train_dist.sh diff --git a/cv/semantic_segmentation/stdc/pytorch/.gitignore b/cv/semantic_segmentation/stdc/pytorch/.gitignore deleted file mode 100644 index 787d13ec6..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/.gitignore +++ /dev/null @@ -1,120 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/en/_build/ -docs/zh_cn/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ -.DS_Store - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -data -.vscode -.idea - -# custom -*.pkl -*.pkl.json -*.log.json -work_dirs/ -mmseg/.mim - -# Pytorch -*.pth diff --git a/cv/semantic_segmentation/stdc/pytorch/CITATION.cff b/cv/semantic_segmentation/stdc/pytorch/CITATION.cff deleted file mode 100644 index cfd7cab05..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/CITATION.cff +++ /dev/null @@ -1,8 +0,0 @@ -cff-version: 1.2.0 -message: "If you use this software, please cite it as below." -authors: - - name: "MMSegmentation Contributors" -title: "OpenMMLab Semantic Segmentation Toolbox and Benchmark" -date-released: 2020-07-10 -url: "https://github.com/open-mmlab/mmsegmentation" -license: Apache-2.0 diff --git a/cv/semantic_segmentation/stdc/pytorch/LICENSE b/cv/semantic_segmentation/stdc/pytorch/LICENSE deleted file mode 100644 index 38e625bf5..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Copyright 2020 The MMSegmentation Authors. All rights reserved. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020 The MMSegmentation Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cv/semantic_segmentation/stdc/pytorch/README.md b/cv/semantic_segmentation/stdc/pytorch/README.md index 35fe857bb..260b68495 100644 --- a/cv/semantic_segmentation/stdc/pytorch/README.md +++ b/cv/semantic_segmentation/stdc/pytorch/README.md @@ -12,20 +12,18 @@ the low-level features and deep features are fused to predict the final segmenta ### Install packages ```bash -pip3 install -r requirements.txt - -yum install mesa-libGL - -wget http://www.zlib.net/fossils/zlib-1.2.9.tar.gz -tar xvf zlib-1.2.9.tar.gz -cd zlib-1.2.9/ -./configure && make install -``` - -### Build extension - -```bash -python3 setup.py build && cp build/lib.linux*/mmcv/_ext.cpython* mmcv +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +# install mmsegmentation +git clone -b v1.2.2 https://github.com/open-mmlab/mmsegmentation.git --depth=1 +cd mmsegmentation/ +pip install -v -e . + +pip install ftfy ``` ## Step 2: Preparing datasets @@ -62,87 +60,15 @@ ln -s /path/to/cityscapes data/ ## Step 3: Training -**The available configs are as follows:** - -- stdc1_512x1024_20k_cityscapes -- stdc1_512x1024_80k_cityscapes -- stdc1_in1k-pre_512x1024_80k_cityscapes -- stdc2_512x1024_80k_cityscapes -- stdc2_in1k-pre_512x1024_80k_cityscapes - -```bash -# Training on multiple cards -# "config" files can be found in the configs directory -bash train_dist.sh [training args] - -# Example -bash train_dist.sh configs/stdc/stdc1_512x1024_80k_cityscapes.py 8 +### Training on single card +```shell +python3 tools/train.py configs/stdc/stdc1_4xb12-80k_cityscapes-512x1024.py ``` -**Training arguments are as follows:** - -```python -# the dir to save logs and models -work-dir: str = None - -# the checkpoint file to load weights from -load-from: str = None - -# the checkpoint file to resume from -resume-from: str = None - -# whether not to evaluate the checkpoint during training -no-validate: bool = False - -# (Deprecated, please use --gpu-id) number of gpus to -# use (only applicable to non-distributed training) -gpus: int = None - -# (Deprecated, please use --gpu-id) ids of gpus to use -# (only applicable to non-distributed training) -gpu-ids: int = None - -# id of gpu to use (only applicable to non-distributed training) -gpu-id: int = 0 - -# random seed -seed: int = None - -# Whether or not set different seeds for different ranks -diff_seed: bool = False - -# whether to set deterministic options for CUDNN backend. -deterministic: bool = False - -# --options is deprecated in favor of --cfg_options' and it -# will not be supported in version v0.22.0. Override some -# settings in the used config, the key-value pair in xxx=yyy -# format will be merged into config file. If the value to be -# overwritten is a list, it should be like key="[a,b]" or key=a,b -# It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" -# Note that the quotation marks are necessary and that no white space -# is allowed. -options: str = None - -# override some settings in the used config, the key-value pair -# in xxx=yyy format will be merged into config file. If the value -# to be overwritten is a list, it should be like key="[a,b]" or key=a,b -# It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" -# Note that the quotation marks are necessary and that no white -# space is allowed. -cfg-options: str = None - -# job launcher -launcher: str = "none" - -# local rank -local_rank: int = 0 - -# distributed backend -dist_backend: str = None - -# resume from the latest checkpoint automatically. -auto-resume: bool = False +### Training on mutil-cards +```shell +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/stdc/stdc1_4xb12-80k_cityscapes-512x1024.py 8 ``` ## Results @@ -152,5 +78,4 @@ auto-resume: bool = False | BI-V100 x8 | 512x1024 | 20000 | 39.38 | 70.74 | ## Reference -- [cityscapes](https://mmsegmentation.readthedocs.io/en/latest/dataset_prepare.html#cityscapes) -- [mmsegmentation](https://github.com/open-mmlab/mmsegmentation) +[mmsegmentation](https://github.com/open-mmlab/mmsegmentation) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/ade20k.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/ade20k.py deleted file mode 100644 index efc8b4bb2..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/ade20k.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'ADE20KDataset' -data_root = 'data/ade/ADEChallengeData2016' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/ade20k_640x640.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/ade20k_640x640.py deleted file mode 100644 index 14a4bb092..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/ade20k_640x640.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'ADE20KDataset' -data_root = 'data/ade/ADEChallengeData2016' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (640, 640) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2560, 640), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2560, 640), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/training', - ann_dir='annotations/training', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/validation', - ann_dir='annotations/validation', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes.py deleted file mode 100644 index f21867c63..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'CityscapesDataset' -data_root = 'data/cityscapes/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 1024) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/train', - ann_dir='gtFine/train', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/val', - ann_dir='gtFine/val', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='leftImg8bit/val', - ann_dir='gtFine/val', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py deleted file mode 100644 index f98d92972..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_1024x1024.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (1024, 1024) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_768x768.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_768x768.py deleted file mode 100644 index fde9d7c7d..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_768x768.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (768, 768) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2049, 1025), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_769x769.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_769x769.py deleted file mode 100644 index 336c7b254..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_769x769.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (769, 769) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2049, 1025), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_832x832.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_832x832.py deleted file mode 100644 index b9325cc00..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/cityscapes_832x832.py +++ /dev/null @@ -1,35 +0,0 @@ -_base_ = './cityscapes.py' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (832, 832) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/coco-stuff10k.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/coco-stuff10k.py deleted file mode 100644 index ec0496928..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/coco-stuff10k.py +++ /dev/null @@ -1,57 +0,0 @@ -# dataset settings -dataset_type = 'COCOStuffDataset' -data_root = 'data/coco_stuff10k' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/train2014', - ann_dir='annotations/train2014', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/test2014', - ann_dir='annotations/test2014', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - reduce_zero_label=True, - img_dir='images/test2014', - ann_dir='annotations/test2014', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/coco-stuff164k.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/coco-stuff164k.py deleted file mode 100644 index a6a38f2ac..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/coco-stuff164k.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'COCOStuffDataset' -data_root = 'data/coco_stuff164k' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/train2017', - ann_dir='annotations/train2017', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/val2017', - ann_dir='annotations/val2017', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='images/val2017', - ann_dir='annotations/val2017', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_context.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_context.py deleted file mode 100644 index ff65bad1b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_context.py +++ /dev/null @@ -1,60 +0,0 @@ -# dataset settings -dataset_type = 'PascalContextDataset' -data_root = 'data/VOCdevkit/VOC2010/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) - -img_scale = (520, 520) -crop_size = (480, 480) - -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_context_59.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_context_59.py deleted file mode 100644 index 37585abab..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_context_59.py +++ /dev/null @@ -1,60 +0,0 @@ -# dataset settings -dataset_type = 'PascalContextDataset59' -data_root = 'data/VOCdevkit/VOC2010/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) - -img_scale = (520, 520) -crop_size = (480, 480) - -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', reduce_zero_label=True), - dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=img_scale, - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClassContext', - split='ImageSets/SegmentationContext/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_voc12.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_voc12.py deleted file mode 100644 index e5ff704ae..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_voc12.py +++ /dev/null @@ -1,58 +0,0 @@ -# dataset settings - -dataset_type = 'PascalVOCDataset' -data_root = 'data/VOCdevkit/VOC2012' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -crop_size = (512, 512) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations'), - dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), - dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), - dict(type='RandomFlip', prob=0.5), - dict(type='PhotoMetricDistortion'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 512), - # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=4, - workers_per_gpu=4, - train=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/train.txt', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/val.txt', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - data_root=data_root, - img_dir='JPEGImages', - ann_dir='SegmentationClass', - split='ImageSets/Segmentation/val.txt', - pipeline=test_pipeline)) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_voc12_aug.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_voc12_aug.py deleted file mode 100644 index 3f23b6717..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/datasets/pascal_voc12_aug.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = './pascal_voc12.py' -# dataset settings -data = dict( - train=dict( - ann_dir=['SegmentationClass', 'SegmentationClassAug'], - split=[ - 'ImageSets/Segmentation/train.txt', - 'ImageSets/Segmentation/aug.txt' - ])) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/default_runtime.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/default_runtime.py deleted file mode 100644 index b564cc4e7..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/default_runtime.py +++ /dev/null @@ -1,14 +0,0 @@ -# yapf:disable -log_config = dict( - interval=50, - hooks=[ - dict(type='TextLoggerHook', by_epoch=False), - # dict(type='TensorboardLoggerHook') - ]) -# yapf:enable -dist_params = dict(backend='nccl') -log_level = 'INFO' -load_from = None -resume_from = None -workflow = [('train', 1)] -cudnn_benchmark = True diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/models/stdc.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/models/stdc.py deleted file mode 100644 index 341a4ec58..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/models/stdc.py +++ /dev/null @@ -1,83 +0,0 @@ -norm_cfg = dict(type='BN', requires_grad=True) -model = dict( - type='EncoderDecoder', - pretrained=None, - backbone=dict( - type='STDCContextPathNet', - backbone_cfg=dict( - type='STDCNet', - stdc_type='STDCNet1', - in_channels=3, - channels=(32, 64, 256, 512, 1024), - bottleneck_type='cat', - num_convs=4, - norm_cfg=norm_cfg, - act_cfg=dict(type='ReLU'), - with_final_conv=False), - last_in_channels=(1024, 512), - out_channels=128, - ffm_cfg=dict(in_channels=384, out_channels=256, scale_factor=4)), - decode_head=dict( - type='FCNHead', - in_channels=256, - channels=256, - num_convs=1, - num_classes=19, - in_index=3, - concat_input=False, - dropout_ratio=0.1, - norm_cfg=norm_cfg, - align_corners=True, - sampler=dict(type='OHEMPixelSampler', thresh=0.7, min_kept=10000), - loss_decode=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), - auxiliary_head=[ - dict( - type='FCNHead', - in_channels=128, - channels=64, - num_convs=1, - num_classes=19, - in_index=2, - norm_cfg=norm_cfg, - concat_input=False, - align_corners=False, - sampler=dict(type='OHEMPixelSampler', thresh=0.7, min_kept=10000), - loss_decode=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), - dict( - type='FCNHead', - in_channels=128, - channels=64, - num_convs=1, - num_classes=19, - in_index=1, - norm_cfg=norm_cfg, - concat_input=False, - align_corners=False, - sampler=dict(type='OHEMPixelSampler', thresh=0.7, min_kept=10000), - loss_decode=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)), - dict( - type='STDCHead', - in_channels=256, - channels=64, - num_convs=1, - num_classes=2, - boundary_threshold=0.1, - in_index=0, - norm_cfg=norm_cfg, - concat_input=False, - align_corners=True, - loss_decode=[ - dict( - type='CrossEntropyLoss', - loss_name='loss_ce', - use_sigmoid=True, - loss_weight=1.0), - dict(type='DiceLoss', loss_name='loss_dice', loss_weight=1.0) - ]), - ], - # model training and testing settings - train_cfg=dict(), - test_cfg=dict(mode='whole')) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_160k.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_160k.py deleted file mode 100644 index 39630f215..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_160k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=160000) -checkpoint_config = dict(by_epoch=False, interval=16000) -evaluation = dict(interval=16000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_1k.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_1k.py deleted file mode 100644 index 04cf41030..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_1k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=1000) -checkpoint_config = dict(by_epoch=False, interval=1000) -evaluation = dict(interval=1000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_20k.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_20k.py deleted file mode 100644 index 73c702197..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_20k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=20000) -checkpoint_config = dict(by_epoch=False, interval=2000) -evaluation = dict(interval=2000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_320k.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_320k.py deleted file mode 100644 index a0b230626..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_320k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=320000) -checkpoint_config = dict(by_epoch=False, interval=32000) -evaluation = dict(interval=32000, metric='mIoU') diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_40k.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_40k.py deleted file mode 100644 index d2c502325..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_40k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=40000) -checkpoint_config = dict(by_epoch=False, interval=4000) -evaluation = dict(interval=4000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_80k.py b/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_80k.py deleted file mode 100644 index 8365a878e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/_base_/schedules/schedule_80k.py +++ /dev/null @@ -1,9 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005) -optimizer_config = dict() -# learning policy -lr_config = dict(policy='poly', power=0.9, min_lr=1e-4, by_epoch=False) -# runtime settings -runner = dict(type='IterBasedRunner', max_iters=80000) -checkpoint_config = dict(by_epoch=False, interval=8000) -evaluation = dict(interval=8000, metric='mIoU', pre_eval=True) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/stdc/README.md b/cv/semantic_segmentation/stdc/pytorch/configs/stdc/README.md deleted file mode 100644 index 1c6d70a25..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/stdc/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# STDC - -[Rethinking BiSeNet For Real-time Semantic Segmentation](https://arxiv.org/abs/2104.13188) - -## Introduction - - - -Official Repo - -Code Snippet - -## Abstract - - - -BiSeNet has been proved to be a popular two-stream network for real-time segmentation. However, its principle of adding an extra path to encode spatial information is time-consuming, and the backbones borrowed from pretrained tasks, e.g., image classification, may be inefficient for image segmentation due to the deficiency of task-specific design. To handle these problems, we propose a novel and efficient structure named Short-Term Dense Concatenate network (STDC network) by removing structure redundancy. Specifically, we gradually reduce the dimension of feature maps and use the aggregation of them for image representation, which forms the basic module of STDC network. In the decoder, we propose a Detail Aggregation module by integrating the learning of spatial information into low-level layers in single-stream manner. Finally, the low-level features and deep features are fused to predict the final segmentation results. Extensive experiments on Cityscapes and CamVid dataset demonstrate the effectiveness of our method by achieving promising trade-off between segmentation accuracy and inference speed. On Cityscapes, we achieve 71.9% mIoU on the test set with a speed of 250.4 FPS on NVIDIA GTX 1080Ti, which is 45.2% faster than the latest methods, and achieve 76.8% mIoU with 97.0 FPS while inferring on higher resolution images. - - - -
- -
- -## Citation - -```bibtex -@inproceedings{fan2021rethinking, - title={Rethinking BiSeNet For Real-time Semantic Segmentation}, - author={Fan, Mingyuan and Lai, Shenqi and Huang, Junshi and Wei, Xiaoming and Chai, Zhenhua and Luo, Junfeng and Wei, Xiaolin}, - booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition}, - pages={9716--9725}, - year={2021} -} -``` - -## Usage - -We have provided [ImageNet Pretrained STDCNet Weights](https://drive.google.com/drive/folders/1wROFwRt8qWHD4jSo8Zu1gp1d6oYJ3ns1) models converted from [official repo](https://github.com/MichaelFan01/STDC-Seg). - -If you want to convert keys on your own to use official repositories' pre-trained models, we also provide a script [`stdc2mmseg.py`](../../tools/model_converters/stdc2mmseg.py) in the tools directory to convert the key of models from [the official repo](https://github.com/MichaelFan01/STDC-Seg) to MMSegmentation style. - -```shell -python tools/model_converters/stdc2mmseg.py ${PRETRAIN_PATH} ${STORE_PATH} ${STDC_TYPE} -``` - -E.g. - -```shell -python tools/model_converters/stdc2mmseg.py ./STDCNet813M_73.91.tar ./pretrained/stdc1.pth STDC1 - -python tools/model_converters/stdc2mmseg.py ./STDCNet1446_76.47.tar ./pretrained/stdc2.pth STDC2 -``` - -This script convert model from `PRETRAIN_PATH` and store the converted model in `STORE_PATH`. - -## Results and models - -### Cityscapes - -| Method | Backbone | Crop Size | Lr schd | Mem (GB) | Inf time (fps) | mIoU | mIoU(ms+flip) | config | download | -| -------------------- | -------- | --------- | ------: | -------- | -------------- | ----: | ------------- | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| STDC 1 (No Pretrain) | STDC1 | 512x1024 | 80000 | 7.15 | 23.06 | 71.82 | 73.89 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/stdc/stdc1_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/stdc/stdc1_512x1024_80k_cityscapes/stdc1_512x1024_80k_cityscapes_20220224_073048-74e6920a.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/stdc/stdc1_512x1024_80k_cityscapes/stdc1_512x1024_80k_cityscapes_20220224_073048.log.json) | -| STDC 1 | STDC1 | 512x1024 | 80000 | - | - | 74.94 | 76.97 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/stdc/stdc1_in1k-pre_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/stdc/stdc1_in1k-pre_512x1024_80k_cityscapes/stdc1_in1k-pre_512x1024_80k_cityscapes_20220224_141648-3d4c2981.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/stdc/stdc1_in1k-pre_512x1024_80k_cityscapes/stdc1_in1k-pre_512x1024_80k_cityscapes_20220224_141648.log.json) | -| STDC 2 (No Pretrain) | STDC2 | 512x1024 | 80000 | 8.27 | 23.71 | 73.15 | 76.13 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/stdc/stdc2_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/stdc/stdc2_512x1024_80k_cityscapes/stdc2_512x1024_80k_cityscapes_20220222_132015-fb1e3a1a.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/stdc/stdc2_512x1024_80k_cityscapes/stdc2_512x1024_80k_cityscapes_20220222_132015.log.json) | -| STDC 2 | STDC2 | 512x1024 | 80000 | - | - | 76.67 | 78.67 | [config](https://github.com/open-mmlab/mmsegmentation/blob/master/configs/stdc/stdc2_in1k-pre_512x1024_80k_cityscapes.py) | [model](https://download.openmmlab.com/mmsegmentation/v0.5/stdc/stdc2_in1k-pre_512x1024_80k_cityscapes/stdc2_in1k-pre_512x1024_80k_cityscapes_20220224_073048-1f8f0f6c.pth) \| [log](https://download.openmmlab.com/mmsegmentation/v0.5/stdc/stdc2_in1k-pre_512x1024_80k_cityscapes/stdc2_in1k-pre_512x1024_80k_cityscapes_20220224_073048.log.json) | - -Note: - -- For STDC on Cityscapes dataset, default setting is 4 GPUs with 12 samples per GPU in training. -- `No Pretrain` means the model is trained from scratch. -- The FPS is for reference only. The environment is also different from paper setting, whose input size is `512x1024` and `768x1536`, i.e., 50% and 75% of our input size, respectively and using TensorRT. -- The parameter `fusion_kernel` in `STDCHead` is not learnable. In official repo, `find_unused_parameters=True` is set [here](https://github.com/MichaelFan01/STDC-Seg/blob/59ff37fbd693b99972c76fcefe97caa14aeb619f/train.py#L220). You may check it by printing model parameters of original repo on your own. diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc.yml b/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc.yml deleted file mode 100644 index f584b74bc..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc.yml +++ /dev/null @@ -1,87 +0,0 @@ -Collections: -- Name: STDC - Metadata: - Training Data: - - Cityscapes - Paper: - URL: https://arxiv.org/abs/2104.13188 - Title: Rethinking BiSeNet For Real-time Semantic Segmentation - README: configs/stdc/README.md - Code: - URL: https://github.com/open-mmlab/mmsegmentation/blob/v0.20.0/mmseg/models/backbones/stdc.py#L394 - Version: v0.20.0 - Converted From: - Code: https://github.com/MichaelFan01/STDC-Seg -Models: -- Name: stdc1_512x1024_80k_cityscapes - In Collection: STDC - Metadata: - backbone: STDC1 - crop size: (512,1024) - lr schd: 80000 - inference time (ms/im): - - value: 43.37 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 7.15 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 71.82 - mIoU(ms+flip): 73.89 - Config: configs/stdc/stdc1_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/stdc/stdc1_512x1024_80k_cityscapes/stdc1_512x1024_80k_cityscapes_20220224_073048-74e6920a.pth -- Name: stdc1_in1k-pre_512x1024_80k_cityscapes - In Collection: STDC - Metadata: - backbone: STDC1 - crop size: (512,1024) - lr schd: 80000 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 74.94 - mIoU(ms+flip): 76.97 - Config: configs/stdc/stdc1_in1k-pre_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/stdc/stdc1_in1k-pre_512x1024_80k_cityscapes/stdc1_in1k-pre_512x1024_80k_cityscapes_20220224_141648-3d4c2981.pth -- Name: stdc2_512x1024_80k_cityscapes - In Collection: STDC - Metadata: - backbone: STDC2 - crop size: (512,1024) - lr schd: 80000 - inference time (ms/im): - - value: 42.18 - hardware: V100 - backend: PyTorch - batch size: 1 - mode: FP32 - resolution: (512,1024) - Training Memory (GB): 8.27 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 73.15 - mIoU(ms+flip): 76.13 - Config: configs/stdc/stdc2_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/stdc/stdc2_512x1024_80k_cityscapes/stdc2_512x1024_80k_cityscapes_20220222_132015-fb1e3a1a.pth -- Name: stdc2_in1k-pre_512x1024_80k_cityscapes - In Collection: STDC - Metadata: - backbone: STDC2 - crop size: (512,1024) - lr schd: 80000 - Results: - - Task: Semantic Segmentation - Dataset: Cityscapes - Metrics: - mIoU: 76.67 - mIoU(ms+flip): 78.67 - Config: configs/stdc/stdc2_in1k-pre_512x1024_80k_cityscapes.py - Weights: https://download.openmmlab.com/mmsegmentation/v0.5/stdc/stdc2_in1k-pre_512x1024_80k_cityscapes/stdc2_in1k-pre_512x1024_80k_cityscapes_20220224_073048-1f8f0f6c.pth diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc1_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc1_512x1024_80k_cityscapes.py deleted file mode 100644 index 348436eb9..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc1_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,9 +0,0 @@ -_base_ = [ - '../_base_/models/stdc.py', '../_base_/datasets/cityscapes.py', - '../_base_/default_runtime.py', '../_base_/schedules/schedule_80k.py' -] -lr_config = dict(warmup='linear', warmup_iters=1000) -data = dict( - samples_per_gpu=12, - workers_per_gpu=8, -) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc1_in1k-pre_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc1_in1k-pre_512x1024_80k_cityscapes.py deleted file mode 100644 index f295bf494..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc1_in1k-pre_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,6 +0,0 @@ -checkpoint = 'https://download.openmmlab.com/mmsegmentation/v0.5/pretrain/stdc/stdc1_20220308-5368626c.pth' # noqa -_base_ = './stdc1_512x1024_80k_cityscapes.py' -model = dict( - backbone=dict( - backbone_cfg=dict( - init_cfg=dict(type='Pretrained', checkpoint=checkpoint)))) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc2_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc2_512x1024_80k_cityscapes.py deleted file mode 100644 index f7afb506a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc2_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,2 +0,0 @@ -_base_ = './stdc1_512x1024_80k_cityscapes.py' -model = dict(backbone=dict(backbone_cfg=dict(stdc_type='STDCNet2'))) diff --git a/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc2_in1k-pre_512x1024_80k_cityscapes.py b/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc2_in1k-pre_512x1024_80k_cityscapes.py deleted file mode 100644 index 4148ac4fd..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/configs/stdc/stdc2_in1k-pre_512x1024_80k_cityscapes.py +++ /dev/null @@ -1,6 +0,0 @@ -checkpoint = 'https://download.openmmlab.com/mmsegmentation/v0.5/pretrain/stdc/stdc2_20220308-7dbd9127.pth' # noqa -_base_ = './stdc2_512x1024_80k_cityscapes.py' -model = dict( - backbone=dict( - backbone_cfg=dict( - init_cfg=dict(type='Pretrained', checkpoint=checkpoint)))) diff --git a/cv/semantic_segmentation/stdc/pytorch/docker/Dockerfile b/cv/semantic_segmentation/stdc/pytorch/docker/Dockerfile deleted file mode 100644 index 64482b472..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/docker/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -ARG PYTORCH="1.11.0" -ARG CUDA="11.3" -ARG CUDNN="8" - -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ENV TORCH_CUDA_ARCH_LIST="6.0 6.1 7.0+PTX" -ENV TORCH_NVCC_FLAGS="-Xfatbin -compress-all" -ENV CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" - -# To fix GPG key error when running apt-get update -RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub -RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/7fa2af80.pub - -RUN apt-get update && apt-get install -y git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -RUN conda clean --all - -# Install MMCV -ARG PYTORCH -ARG CUDA -ARG MMCV -RUN ["/bin/bash", "-c", "pip install --no-cache-dir mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] - -# Install MMSegmentation -RUN git clone https://github.com/open-mmlab/mmsegmentation.git /mmsegmentation -WORKDIR /mmsegmentation -ENV FORCE_CUDA="1" -RUN pip install -r requirements.txt -RUN pip install --no-cache-dir -e . diff --git a/cv/semantic_segmentation/stdc/pytorch/docker/serve/Dockerfile b/cv/semantic_segmentation/stdc/pytorch/docker/serve/Dockerfile deleted file mode 100644 index c1d154528..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/docker/serve/Dockerfile +++ /dev/null @@ -1,49 +0,0 @@ -ARG PYTORCH="1.11.0" -ARG CUDA="11.3" -ARG CUDNN="8" -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ARG MMCV="1.4.8" -ARG MMSEG="0.24.1" - -ENV PYTHONUNBUFFERED TRUE - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ - ca-certificates \ - g++ \ - openjdk-11-jre-headless \ - # MMDet Requirements - ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && rm -rf /var/lib/apt/lists/* - -ENV PATH="/opt/conda/bin:$PATH" -RUN export FORCE_CUDA=1 - -# TORCHSEVER -RUN pip install torchserve torch-model-archiver - -# MMLAB -ARG PYTORCH -ARG CUDA -RUN ["/bin/bash", "-c", "pip install mmcv-full==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] -RUN pip install mmsegmentation==${MMSEG} - -RUN useradd -m model-server \ - && mkdir -p /home/model-server/tmp - -COPY entrypoint.sh /usr/local/bin/entrypoint.sh - -RUN chmod +x /usr/local/bin/entrypoint.sh \ - && chown -R model-server /home/model-server - -COPY config.properties /home/model-server/config.properties -RUN mkdir /home/model-server/model-store && chown -R model-server /home/model-server/model-store - -EXPOSE 8080 8081 8082 - -USER model-server -WORKDIR /home/model-server -ENV TEMP=/home/model-server/tmp -ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] -CMD ["serve"] diff --git a/cv/semantic_segmentation/stdc/pytorch/docker/serve/config.properties b/cv/semantic_segmentation/stdc/pytorch/docker/serve/config.properties deleted file mode 100644 index efb9c47e4..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/docker/serve/config.properties +++ /dev/null @@ -1,5 +0,0 @@ -inference_address=http://0.0.0.0:8080 -management_address=http://0.0.0.0:8081 -metrics_address=http://0.0.0.0:8082 -model_store=/home/model-server/model-store -load_models=all diff --git a/cv/semantic_segmentation/stdc/pytorch/docker/serve/entrypoint.sh b/cv/semantic_segmentation/stdc/pytorch/docker/serve/entrypoint.sh deleted file mode 100644 index 41ba00b04..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/docker/serve/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - -if [[ "$1" = "serve" ]]; then - shift 1 - torchserve --start --ts-config /home/model-server/config.properties -else - eval "$@" -fi - -# prevent docker exit -tail -f /dev/null diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/__init__.py deleted file mode 100644 index 435429d48..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -from .fileio import * -from .image import * -from .utils import * -from .version import * - -# The following modules are not imported to this level, so mmcv may be used -# without PyTorch. -# - runner -# - parallel -# - op -# - device diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/__init__.py deleted file mode 100644 index 3d5599d9a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# yapf: disable -from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, - ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, - ConvTranspose2d, ConvTranspose3d, ConvWS2d, - DepthwiseSeparableConvModule, GeneralizedAttention, - HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, - NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, - build_activation_layer, build_conv_layer, - build_norm_layer, build_padding_layer, build_plugin_layer, - build_upsample_layer, conv_ws_2d, is_norm) -from .builder import MODELS, build_model_from_cfg -# yapf: enable -from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, - NormalInit, PretrainedInit, TruncNormalInit, UniformInit, - XavierInit, bias_init_with_prob, caffe2_xavier_init, - constant_init, fuse_conv_bn, get_model_complexity_info, - initialize, kaiming_init, normal_init, trunc_normal_init, - uniform_init, xavier_init) - diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/__init__.py deleted file mode 100644 index 0f33124ed..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .activation import build_activation_layer -from .context_block import ContextBlock -from .conv import build_conv_layer -from .conv2d_adaptive_padding import Conv2dAdaptivePadding -from .conv_module import ConvModule -from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d -from .depthwise_separable_conv_module import DepthwiseSeparableConvModule -from .drop import Dropout, DropPath -from .generalized_attention import GeneralizedAttention -from .hsigmoid import HSigmoid -from .hswish import HSwish -from .non_local import NonLocal1d, NonLocal2d, NonLocal3d -from .norm import build_norm_layer, is_norm -from .padding import build_padding_layer -from .plugin import build_plugin_layer -from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) -from .scale import Scale -from .swish import Swish -from .upsample import build_upsample_layer -from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, - Linear, MaxPool2d, MaxPool3d) - -__all__ = [ - 'ConvModule', 'build_activation_layer', 'build_conv_layer', - 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', - 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', - 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', - 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', - 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', - 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', - 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/activation.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/activation.py deleted file mode 100644 index 26be59581..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/activation.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version -from .registry import ACTIVATION_LAYERS - -for module in [ - nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, - nn.Sigmoid, nn.Tanh -]: - ACTIVATION_LAYERS.register_module(module=module) - - -@ACTIVATION_LAYERS.register_module(name='Clip') -@ACTIVATION_LAYERS.register_module() -class Clamp(nn.Module): - """Clamp activation layer. - - This activation function is to clamp the feature map value within - :math:`[min, max]`. More details can be found in ``torch.clamp()``. - - Args: - min (Number | optional): Lower-bound of the range to be clamped to. - Default to -1. - max (Number | optional): Upper-bound of the range to be clamped to. - Default to 1. - """ - - def __init__(self, min=-1., max=1.): - super(Clamp, self).__init__() - self.min = min - self.max = max - - def forward(self, x): - """Forward function. - - Args: - x (torch.Tensor): The input tensor. - - Returns: - torch.Tensor: Clamped tensor. - """ - return torch.clamp(x, min=self.min, max=self.max) - - -class GELU(nn.Module): - r"""Applies the Gaussian Error Linear Units function: - - .. math:: - \text{GELU}(x) = x * \Phi(x) - where :math:`\Phi(x)` is the Cumulative Distribution Function for - Gaussian Distribution. - - Shape: - - Input: :math:`(N, *)` where `*` means, any number of additional - dimensions - - Output: :math:`(N, *)`, same shape as the input - - .. image:: scripts/activation_images/GELU.png - - Examples:: - - >>> m = nn.GELU() - >>> input = torch.randn(2) - >>> output = m(input) - """ - - def forward(self, input): - return F.gelu(input) - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.4')): - ACTIVATION_LAYERS.register_module(module=GELU) -else: - ACTIVATION_LAYERS.register_module(module=nn.GELU) - - -def build_activation_layer(cfg): - """Build activation layer. - - Args: - cfg (dict): The activation layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate an activation layer. - - Returns: - nn.Module: Created activation layer. - """ - return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/context_block.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/context_block.py deleted file mode 100644 index d60fdb904..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/context_block.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn - -from ..utils import constant_init, kaiming_init -from .registry import PLUGIN_LAYERS - - -def last_zero_init(m): - if isinstance(m, nn.Sequential): - constant_init(m[-1], val=0) - else: - constant_init(m, val=0) - - -@PLUGIN_LAYERS.register_module() -class ContextBlock(nn.Module): - """ContextBlock module in GCNet. - - See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' - (https://arxiv.org/abs/1904.11492) for details. - - Args: - in_channels (int): Channels of the input feature map. - ratio (float): Ratio of channels of transform bottleneck - pooling_type (str): Pooling method for context modeling. - Options are 'att' and 'avg', stand for attention pooling and - average pooling respectively. Default: 'att'. - fusion_types (Sequence[str]): Fusion method for feature fusion, - Options are 'channels_add', 'channel_mul', stand for channelwise - addition and multiplication respectively. Default: ('channel_add',) - """ - - _abbr_ = 'context_block' - - def __init__(self, - in_channels, - ratio, - pooling_type='att', - fusion_types=('channel_add', )): - super(ContextBlock, self).__init__() - assert pooling_type in ['avg', 'att'] - assert isinstance(fusion_types, (list, tuple)) - valid_fusion_types = ['channel_add', 'channel_mul'] - assert all([f in valid_fusion_types for f in fusion_types]) - assert len(fusion_types) > 0, 'at least one fusion should be used' - self.in_channels = in_channels - self.ratio = ratio - self.planes = int(in_channels * ratio) - self.pooling_type = pooling_type - self.fusion_types = fusion_types - if pooling_type == 'att': - self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) - self.softmax = nn.Softmax(dim=2) - else: - self.avg_pool = nn.AdaptiveAvgPool2d(1) - if 'channel_add' in fusion_types: - self.channel_add_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_add_conv = None - if 'channel_mul' in fusion_types: - self.channel_mul_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_mul_conv = None - self.reset_parameters() - - def reset_parameters(self): - if self.pooling_type == 'att': - kaiming_init(self.conv_mask, mode='fan_in') - self.conv_mask.inited = True - - if self.channel_add_conv is not None: - last_zero_init(self.channel_add_conv) - if self.channel_mul_conv is not None: - last_zero_init(self.channel_mul_conv) - - def spatial_pool(self, x): - batch, channel, height, width = x.size() - if self.pooling_type == 'att': - input_x = x - # [N, C, H * W] - input_x = input_x.view(batch, channel, height * width) - # [N, 1, C, H * W] - input_x = input_x.unsqueeze(1) - # [N, 1, H, W] - context_mask = self.conv_mask(x) - # [N, 1, H * W] - context_mask = context_mask.view(batch, 1, height * width) - # [N, 1, H * W] - context_mask = self.softmax(context_mask) - # [N, 1, H * W, 1] - context_mask = context_mask.unsqueeze(-1) - # [N, 1, C, 1] - context = torch.matmul(input_x, context_mask) - # [N, C, 1, 1] - context = context.view(batch, channel, 1, 1) - else: - # [N, C, 1, 1] - context = self.avg_pool(x) - - return context - - def forward(self, x): - # [N, C, 1, 1] - context = self.spatial_pool(x) - - out = x - if self.channel_mul_conv is not None: - # [N, C, 1, 1] - channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) - out = out * channel_mul_term - if self.channel_add_conv is not None: - # [N, C, 1, 1] - channel_add_term = self.channel_add_conv(context) - out = out + channel_add_term - - return out diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv.py deleted file mode 100644 index f6c35fd70..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from .registry import CONV_LAYERS - -CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) -CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) -CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) -CONV_LAYERS.register_module('Conv', module=nn.Conv2d) - - -def build_conv_layer(cfg, *args, **kwargs): - """Build convolution layer. - - Args: - cfg (None or dict): The conv layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an conv layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding conv layer. - - Returns: - nn.Module: Created conv layer. - """ - if cfg is None: - cfg_ = dict(type='Conv2d') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in CONV_LAYERS: - raise KeyError(f'Unrecognized layer type {layer_type}') - else: - conv_layer = CONV_LAYERS.get(layer_type) - - layer = conv_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py deleted file mode 100644 index b45e758ac..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from torch import nn -from torch.nn import functional as F - -from .registry import CONV_LAYERS - - -@CONV_LAYERS.register_module() -class Conv2dAdaptivePadding(nn.Conv2d): - """Implementation of 2D convolution in tensorflow with `padding` as "same", - which applies padding to input (if needed) so that input image gets fully - covered by filter and stride you specified. For stride 1, this will ensure - that output image size is same as input. For stride of 2, output dimensions - will be half, for example. - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the convolving kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If ``True``, adds a learnable bias to the - output. Default: ``True`` - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__(in_channels, out_channels, kernel_size, stride, 0, - dilation, groups, bias) - - def forward(self, x): - img_h, img_w = x.size()[-2:] - kernel_h, kernel_w = self.weight.size()[-2:] - stride_h, stride_w = self.stride - output_h = math.ceil(img_h / stride_h) - output_w = math.ceil(img_w / stride_w) - pad_h = ( - max((output_h - 1) * self.stride[0] + - (kernel_h - 1) * self.dilation[0] + 1 - img_h, 0)) - pad_w = ( - max((output_w - 1) * self.stride[1] + - (kernel_w - 1) * self.dilation[1] + 1 - img_w, 0)) - if pad_h > 0 or pad_w > 0: - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2 - ]) - return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv_module.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv_module.py deleted file mode 100644 index 0078647a1..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv_module.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from mmcv.utils import _BatchNorm, _InstanceNorm -from ..utils import constant_init, kaiming_init -from .activation import build_activation_layer -from .conv import build_conv_layer -from .norm import build_norm_layer -from .padding import build_padding_layer -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class ConvModule(nn.Module): - """A conv block that bundles conv/norm/activation layers. - - This block simplifies the usage of convolution layers, which are commonly - used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). - It is based upon three build methods: `build_conv_layer()`, - `build_norm_layer()` and `build_activation_layer()`. - - Besides, we add some additional features in this module. - 1. Automatically set `bias` of the conv layer. - 2. Spectral norm is supported. - 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only - supports zero and circular padding, and we add "reflect" padding mode. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. - groups (int): Number of blocked connections from input channels to - output channels. Same as that in ``nn._ConvNd``. - bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - inplace (bool): Whether to use inplace mode for activation. - Default: True. - with_spectral_norm (bool): Whether use spectral norm in conv module. - Default: False. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in PyTorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Common examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - Default: ('conv', 'norm', 'act'). - """ - - _abbr_ = 'conv_block' - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias='auto', - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - padding_mode='zeros', - order=('conv', 'norm', 'act')): - super(ConvModule, self).__init__() - assert conv_cfg is None or isinstance(conv_cfg, dict) - assert norm_cfg is None or isinstance(norm_cfg, dict) - assert act_cfg is None or isinstance(act_cfg, dict) - official_padding_mode = ['zeros', 'circular'] - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.with_explicit_padding = padding_mode not in official_padding_mode - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 3 - assert set(order) == set(['conv', 'norm', 'act']) - - self.with_norm = norm_cfg is not None - self.with_activation = act_cfg is not None - # if the conv layer is before a norm layer, bias is unnecessary. - if bias == 'auto': - bias = not self.with_norm - self.with_bias = bias - - if self.with_explicit_padding: - pad_cfg = dict(type=padding_mode) - self.padding_layer = build_padding_layer(pad_cfg, padding) - - # reset padding to 0 for conv module - conv_padding = 0 if self.with_explicit_padding else padding - # build convolution layer - self.conv = build_conv_layer( - conv_cfg, - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=conv_padding, - dilation=dilation, - groups=groups, - bias=bias) - # export the attributes of self.conv to a higher level for convenience - self.in_channels = self.conv.in_channels - self.out_channels = self.conv.out_channels - self.kernel_size = self.conv.kernel_size - self.stride = self.conv.stride - self.padding = padding - self.dilation = self.conv.dilation - self.transposed = self.conv.transposed - self.output_padding = self.conv.output_padding - self.groups = self.conv.groups - - if self.with_spectral_norm: - self.conv = nn.utils.spectral_norm(self.conv) - - # build normalization layers - if self.with_norm: - # norm layer is after conv layer - if order.index('norm') > order.index('conv'): - norm_channels = out_channels - else: - norm_channels = in_channels - self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) - self.add_module(self.norm_name, norm) - if self.with_bias: - if isinstance(norm, (_BatchNorm, _InstanceNorm)): - warnings.warn( - 'Unnecessary conv bias before batch/instance norm') - else: - self.norm_name = None - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - # nn.Tanh has no 'inplace' argument - if act_cfg_['type'] not in [ - 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish', 'GELU' - ]: - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - @property - def norm(self): - if self.norm_name: - return getattr(self, self.norm_name) - else: - return None - - def init_weights(self): - # 1. It is mainly for customized conv layers with their own - # initialization manners by calling their own ``init_weights()``, - # and we do not want ConvModule to override the initialization. - # 2. For customized conv layers without their own initialization - # manners (that is, they don't have their own ``init_weights()``) - # and PyTorch's conv layers, they will be initialized by - # this method with default ``kaiming_init``. - # Note: For PyTorch's conv layers, they will be overwritten by our - # initialization implementation using default ``kaiming_init``. - if not hasattr(self.conv, 'init_weights'): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) - if self.with_norm: - constant_init(self.norm, 1, bias=0) - - def forward(self, x, activate=True, norm=True): - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - x = self.conv(x) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv_ws.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv_ws.py deleted file mode 100644 index a3941e278..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/conv_ws.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from .registry import CONV_LAYERS - - -def conv_ws_2d(input, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - eps=1e-5): - c_in = weight.size(0) - weight_flat = weight.view(c_in, -1) - mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1) - std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1) - weight = (weight - mean) / (std + eps) - return F.conv2d(input, weight, bias, stride, padding, dilation, groups) - - -@CONV_LAYERS.register_module('ConvWS') -class ConvWS2d(nn.Conv2d): - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True, - eps=1e-5): - super(ConvWS2d, self).__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.eps = eps - - def forward(self, x): - return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups, self.eps) - - -@CONV_LAYERS.register_module(name='ConvAWS') -class ConvAWS2d(nn.Conv2d): - """AWS (Adaptive Weight Standardization) - - This is a variant of Weight Standardization - (https://arxiv.org/pdf/1903.10520.pdf) - It is used in DetectoRS to avoid NaN - (https://arxiv.org/pdf/2006.02334.pdf) - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the conv kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If set True, adds a learnable bias to the - output. Default: True - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.register_buffer('weight_gamma', - torch.ones(self.out_channels, 1, 1, 1)) - self.register_buffer('weight_beta', - torch.zeros(self.out_channels, 1, 1, 1)) - - def _get_weight(self, weight): - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - weight = (weight - mean) / std - weight = self.weight_gamma * weight + self.weight_beta - return weight - - def forward(self, x): - weight = self._get_weight(self.weight) - return F.conv2d(x, weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - """Override default load function. - - AWS overrides the function _load_from_state_dict to recover - weight_gamma and weight_beta if they are missing. If weight_gamma and - weight_beta are found in the checkpoint, this function will return - after super()._load_from_state_dict. Otherwise, it will compute the - mean and std of the pretrained weights and store them in weight_beta - and weight_gamma. - """ - - self.weight_gamma.data.fill_(-1) - local_missing_keys = [] - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, local_missing_keys, - unexpected_keys, error_msgs) - if self.weight_gamma.data.mean() > 0: - for k in local_missing_keys: - missing_keys.append(k) - return - weight = self.weight.data - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - self.weight_beta.data.copy_(mean) - self.weight_gamma.data.copy_(std) - missing_gamma_beta = [ - k for k in local_missing_keys - if k.endswith('weight_gamma') or k.endswith('weight_beta') - ] - for k in missing_gamma_beta: - local_missing_keys.remove(k) - for k in local_missing_keys: - missing_keys.append(k) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py deleted file mode 100644 index 722d5d8d7..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .conv_module import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if `norm_cfg` and `act_cfg` are specified. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. Default: 1. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. Default: 0. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super(DepthwiseSeparableConvModule, self).__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/drop.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/drop.py deleted file mode 100644 index b0a026654..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/drop.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from mmcv import build_from_cfg -from .registry import DROPOUT_LAYERS - - -def drop_path(x, drop_prob=0., training=False): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - """ - if drop_prob == 0. or not training: - return x - keep_prob = 1 - drop_prob - # handle tensors with different dimensions, not just 4D tensors. - shape = (x.shape[0], ) + (1, ) * (x.ndim - 1) - random_tensor = keep_prob + torch.rand( - shape, dtype=x.dtype, device=x.device) - output = x.div(keep_prob) * random_tensor.floor() - return output - - -@DROPOUT_LAYERS.register_module() -class DropPath(nn.Module): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - - Args: - drop_prob (float): Probability of the path to be zeroed. Default: 0.1 - """ - - def __init__(self, drop_prob=0.1): - super(DropPath, self).__init__() - self.drop_prob = drop_prob - - def forward(self, x): - return drop_path(x, self.drop_prob, self.training) - - -@DROPOUT_LAYERS.register_module() -class Dropout(nn.Dropout): - """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of - ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with - ``DropPath`` - - Args: - drop_prob (float): Probability of the elements to be - zeroed. Default: 0.5. - inplace (bool): Do the operation inplace or not. Default: False. - """ - - def __init__(self, drop_prob=0.5, inplace=False): - super().__init__(p=drop_prob, inplace=inplace) - - -def build_dropout(cfg, default_args=None): - """Builder for drop out layers.""" - return build_from_cfg(cfg, DROPOUT_LAYERS, default_args) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/generalized_attention.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/generalized_attention.py deleted file mode 100644 index c8a74d268..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/generalized_attention.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import kaiming_init -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class GeneralizedAttention(nn.Module): - """GeneralizedAttention module. - - See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' - (https://arxiv.org/abs/1711.07971) for details. - - Args: - in_channels (int): Channels of the input feature map. - spatial_range (int): The spatial range. -1 indicates no spatial range - constraint. Default: -1. - num_heads (int): The head number of empirical_attention module. - Default: 9. - position_embedding_dim (int): The position embedding dimension. - Default: -1. - position_magnitude (int): A multiplier acting on coord difference. - Default: 1. - kv_stride (int): The feature stride acting on key/value feature map. - Default: 2. - q_stride (int): The feature stride acting on query feature map. - Default: 1. - attention_type (str): A binary indicator string for indicating which - items in generalized empirical_attention module are used. - Default: '1111'. - - - '1000' indicates 'query and key content' (appr - appr) item, - - '0100' indicates 'query content and relative position' - (appr - position) item, - - '0010' indicates 'key content only' (bias - appr) item, - - '0001' indicates 'relative position only' (bias - position) item. - """ - - _abbr_ = 'gen_attention_block' - - def __init__(self, - in_channels, - spatial_range=-1, - num_heads=9, - position_embedding_dim=-1, - position_magnitude=1, - kv_stride=2, - q_stride=1, - attention_type='1111'): - - super(GeneralizedAttention, self).__init__() - - # hard range means local range for non-local operation - self.position_embedding_dim = ( - position_embedding_dim - if position_embedding_dim > 0 else in_channels) - - self.position_magnitude = position_magnitude - self.num_heads = num_heads - self.in_channels = in_channels - self.spatial_range = spatial_range - self.kv_stride = kv_stride - self.q_stride = q_stride - self.attention_type = [bool(int(_)) for _ in attention_type] - self.qk_embed_dim = in_channels // num_heads - out_c = self.qk_embed_dim * num_heads - - if self.attention_type[0] or self.attention_type[1]: - self.query_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.query_conv.kaiming_init = True - - if self.attention_type[0] or self.attention_type[2]: - self.key_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.key_conv.kaiming_init = True - - self.v_dim = in_channels // num_heads - self.value_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=self.v_dim * num_heads, - kernel_size=1, - bias=False) - self.value_conv.kaiming_init = True - - if self.attention_type[1] or self.attention_type[3]: - self.appr_geom_fc_x = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_x.kaiming_init = True - - self.appr_geom_fc_y = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_y.kaiming_init = True - - if self.attention_type[2]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.appr_bias = nn.Parameter(appr_bias_value) - - if self.attention_type[3]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.geom_bias = nn.Parameter(geom_bias_value) - - self.proj_conv = nn.Conv2d( - in_channels=self.v_dim * num_heads, - out_channels=in_channels, - kernel_size=1, - bias=True) - self.proj_conv.kaiming_init = True - self.gamma = nn.Parameter(torch.zeros(1)) - - if self.spatial_range >= 0: - # only works when non local is after 3*3 conv - if in_channels == 256: - max_len = 84 - elif in_channels == 512: - max_len = 42 - - max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) - local_constraint_map = np.ones( - (max_len, max_len, max_len_kv, max_len_kv), dtype=int) - for iy in range(max_len): - for ix in range(max_len): - local_constraint_map[ - iy, ix, - max((iy - self.spatial_range) // - self.kv_stride, 0):min((iy + self.spatial_range + - 1) // self.kv_stride + - 1, max_len), - max((ix - self.spatial_range) // - self.kv_stride, 0):min((ix + self.spatial_range + - 1) // self.kv_stride + - 1, max_len)] = 0 - - self.local_constraint_map = nn.Parameter( - torch.from_numpy(local_constraint_map).byte(), - requires_grad=False) - - if self.q_stride > 1: - self.q_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.q_stride) - else: - self.q_downsample = None - - if self.kv_stride > 1: - self.kv_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.kv_stride) - else: - self.kv_downsample = None - - self.init_weights() - - def get_position_embedding(self, - h, - w, - h_kv, - w_kv, - q_stride, - kv_stride, - device, - dtype, - feat_dim, - wave_length=1000): - # the default type of Tensor is float32, leading to type mismatch - # in fp16 mode. Cast it to support fp16 mode. - h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) - h_idxs = h_idxs.view((h, 1)) * q_stride - - w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) - w_idxs = w_idxs.view((w, 1)) * q_stride - - h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( - device=device, dtype=dtype) - h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride - - w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( - device=device, dtype=dtype) - w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride - - # (h, h_kv, 1) - h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) - h_diff *= self.position_magnitude - - # (w, w_kv, 1) - w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) - w_diff *= self.position_magnitude - - feat_range = torch.arange(0, feat_dim / 4).to( - device=device, dtype=dtype) - - dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) - dim_mat = dim_mat**((4. / feat_dim) * feat_range) - dim_mat = dim_mat.view((1, 1, -1)) - - embedding_x = torch.cat( - ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) - - embedding_y = torch.cat( - ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) - - return embedding_x, embedding_y - - def forward(self, x_input): - num_heads = self.num_heads - - # use empirical_attention - if self.q_downsample is not None: - x_q = self.q_downsample(x_input) - else: - x_q = x_input - n, _, h, w = x_q.shape - - if self.kv_downsample is not None: - x_kv = self.kv_downsample(x_input) - else: - x_kv = x_input - _, _, h_kv, w_kv = x_kv.shape - - if self.attention_type[0] or self.attention_type[1]: - proj_query = self.query_conv(x_q).view( - (n, num_heads, self.qk_embed_dim, h * w)) - proj_query = proj_query.permute(0, 1, 3, 2) - - if self.attention_type[0] or self.attention_type[2]: - proj_key = self.key_conv(x_kv).view( - (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) - - if self.attention_type[1] or self.attention_type[3]: - position_embed_x, position_embed_y = self.get_position_embedding( - h, w, h_kv, w_kv, self.q_stride, self.kv_stride, - x_input.device, x_input.dtype, self.position_embedding_dim) - # (n, num_heads, w, w_kv, dim) - position_feat_x = self.appr_geom_fc_x(position_embed_x).\ - view(1, w, w_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - # (n, num_heads, h, h_kv, dim) - position_feat_y = self.appr_geom_fc_y(position_embed_y).\ - view(1, h, h_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - position_feat_x /= math.sqrt(2) - position_feat_y /= math.sqrt(2) - - # accelerate for saliency only - if (np.sum(self.attention_type) == 1) and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy = torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, h_kv * w_kv) - - h = 1 - w = 1 - else: - # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for - if not self.attention_type[0]: - energy = torch.zeros( - n, - num_heads, - h, - w, - h_kv, - w_kv, - dtype=x_input.dtype, - device=x_input.device) - - # attention_type[0]: appr - appr - # attention_type[1]: appr - position - # attention_type[2]: bias - appr - # attention_type[3]: bias - position - if self.attention_type[0] or self.attention_type[2]: - if self.attention_type[0] and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - energy = torch.matmul(proj_query + appr_bias, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[0]: - energy = torch.matmul(proj_query, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy += torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, 1, h_kv, w_kv) - - if self.attention_type[1] or self.attention_type[3]: - if self.attention_type[1] and self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - - proj_query_reshape = (proj_query + geom_bias).\ - view(n, num_heads, h, w, self.qk_embed_dim) - - energy_x = torch.matmul( - proj_query_reshape.permute(0, 1, 3, 2, 4), - position_feat_x.permute(0, 1, 2, 4, 3)) - energy_x = energy_x.\ - permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul( - proj_query_reshape, - position_feat_y.permute(0, 1, 2, 4, 3)) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[1]: - proj_query_reshape = proj_query.\ - view(n, num_heads, h, w, self.qk_embed_dim) - proj_query_reshape = proj_query_reshape.\ - permute(0, 1, 3, 2, 4) - position_feat_x_reshape = position_feat_x.\ - permute(0, 1, 2, 4, 3) - position_feat_y_reshape = position_feat_y.\ - permute(0, 1, 2, 4, 3) - - energy_x = torch.matmul(proj_query_reshape, - position_feat_x_reshape) - energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul(proj_query_reshape, - position_feat_y_reshape) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, self.qk_embed_dim, 1).\ - repeat(n, 1, 1, 1) - - position_feat_x_reshape = position_feat_x.\ - view(n, num_heads, w * w_kv, self.qk_embed_dim) - - position_feat_y_reshape = position_feat_y.\ - view(n, num_heads, h * h_kv, self.qk_embed_dim) - - energy_x = torch.matmul(position_feat_x_reshape, geom_bias) - energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) - - energy_y = torch.matmul(position_feat_y_reshape, geom_bias) - energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) - - energy += energy_x + energy_y - - energy = energy.view(n, num_heads, h * w, h_kv * w_kv) - - if self.spatial_range >= 0: - cur_local_constraint_map = \ - self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ - contiguous().\ - view(1, 1, h*w, h_kv*w_kv) - - energy = energy.masked_fill_(cur_local_constraint_map, - float('-inf')) - - attention = F.softmax(energy, 3) - - proj_value = self.value_conv(x_kv) - proj_value_reshape = proj_value.\ - view((n, num_heads, self.v_dim, h_kv * w_kv)).\ - permute(0, 1, 3, 2) - - out = torch.matmul(attention, proj_value_reshape).\ - permute(0, 1, 3, 2).\ - contiguous().\ - view(n, self.v_dim * self.num_heads, h, w) - - out = self.proj_conv(out) - - # output is downsampled, upsample back to input size - if self.q_downsample is not None: - out = F.interpolate( - out, - size=x_input.shape[2:], - mode='bilinear', - align_corners=False) - - out = self.gamma * out + x_input - return out - - def init_weights(self): - for m in self.modules(): - if hasattr(m, 'kaiming_init') and m.kaiming_init: - kaiming_init( - m, - mode='fan_in', - nonlinearity='leaky_relu', - bias=0, - distribution='uniform', - a=1) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/hsigmoid.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/hsigmoid.py deleted file mode 100644 index e013d739e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/hsigmoid.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSigmoid(nn.Module): - """Hard Sigmoid Module. Apply the hard sigmoid function: - Hsigmoid(x) = min(max((x + bias) / divisor, min_value), max_value) - Default: Hsigmoid(x) = min(max((x + 3) / 6, 0), 1) - - Note: - In MMCV v1.4.4, we modified the default value of args to align with - PyTorch official. - - Args: - bias (float): Bias of the input feature map. Default: 3.0. - divisor (float): Divisor of the input feature map. Default: 6.0. - min_value (float): Lower bound value. Default: 0.0. - max_value (float): Upper bound value. Default: 1.0. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, bias=3.0, divisor=6.0, min_value=0.0, max_value=1.0): - super(HSigmoid, self).__init__() - warnings.warn( - 'In MMCV v1.4.4, we modified the default value of args to align ' - 'with PyTorch official. Previous Implementation: ' - 'Hsigmoid(x) = min(max((x + 1) / 2, 0), 1). ' - 'Current Implementation: ' - 'Hsigmoid(x) = min(max((x + 3) / 6, 0), 1).') - self.bias = bias - self.divisor = divisor - assert self.divisor != 0 - self.min_value = min_value - self.max_value = max_value - - def forward(self, x): - x = (x + self.bias) / self.divisor - - return x.clamp_(self.min_value, self.max_value) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/hswish.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/hswish.py deleted file mode 100644 index 27096832f..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/hswish.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import ACTIVATION_LAYERS - - -class HSwish(nn.Module): - """Hard Swish Module. - - This module applies the hard swish function: - - .. math:: - Hswish(x) = x * ReLU6(x + 3) / 6 - - Args: - inplace (bool): can optionally do the operation in-place. - Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.act = nn.ReLU6(inplace) - - def forward(self, x): - return x * self.act(x + 3) / 6 - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.7')): - # Hardswish is not supported when PyTorch version < 1.6. - # And Hardswish in PyTorch 1.6 does not support inplace. - ACTIVATION_LAYERS.register_module(module=HSwish) -else: - ACTIVATION_LAYERS.register_module(module=nn.Hardswish, name='HSwish') diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/non_local.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/non_local.py deleted file mode 100644 index 92d00155e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/non_local.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta - -import torch -import torch.nn as nn - -from ..utils import constant_init, normal_init -from .conv_module import ConvModule -from .registry import PLUGIN_LAYERS - - -class _NonLocalNd(nn.Module, metaclass=ABCMeta): - """Basic Non-local module. - - This module is proposed in - "Non-local Neural Networks" - Paper reference: https://arxiv.org/abs/1711.07971 - Code reference: https://github.com/AlexHex7/Non-local_pytorch - - Args: - in_channels (int): Channels of the input feature map. - reduction (int): Channel reduction ratio. Default: 2. - use_scale (bool): Whether to scale pairwise_weight by - `1/sqrt(inter_channels)` when the mode is `embedded_gaussian`. - Default: True. - conv_cfg (None | dict): The config dict for convolution layers. - If not specified, it will use `nn.Conv2d` for convolution layers. - Default: None. - norm_cfg (None | dict): The config dict for normalization layers. - Default: None. (This parameter is only applicable to conv_out.) - mode (str): Options are `gaussian`, `concatenation`, - `embedded_gaussian` and `dot_product`. Default: embedded_gaussian. - """ - - def __init__(self, - in_channels, - reduction=2, - use_scale=True, - conv_cfg=None, - norm_cfg=None, - mode='embedded_gaussian', - **kwargs): - super(_NonLocalNd, self).__init__() - self.in_channels = in_channels - self.reduction = reduction - self.use_scale = use_scale - self.inter_channels = max(in_channels // reduction, 1) - self.mode = mode - - if mode not in [ - 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation' - ]: - raise ValueError("Mode should be in 'gaussian', 'concatenation', " - f"'embedded_gaussian' or 'dot_product', but got " - f'{mode} instead.') - - # g, theta, phi are defaulted as `nn.ConvNd`. - # Here we use ConvModule for potential usage. - self.g = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.conv_out = ConvModule( - self.inter_channels, - self.in_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - if self.mode != 'gaussian': - self.theta = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.phi = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - - if self.mode == 'concatenation': - self.concat_project = ConvModule( - self.inter_channels * 2, - 1, - kernel_size=1, - stride=1, - padding=0, - bias=False, - act_cfg=dict(type='ReLU')) - - self.init_weights(**kwargs) - - def init_weights(self, std=0.01, zeros_init=True): - if self.mode != 'gaussian': - for m in [self.g, self.theta, self.phi]: - normal_init(m.conv, std=std) - else: - normal_init(self.g.conv, std=std) - if zeros_init: - if self.conv_out.norm_cfg is None: - constant_init(self.conv_out.conv, 0) - else: - constant_init(self.conv_out.norm, 0) - else: - if self.conv_out.norm_cfg is None: - normal_init(self.conv_out.conv, std=std) - else: - normal_init(self.conv_out.norm, std=std) - - def gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def embedded_gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - if self.use_scale: - # theta_x.shape[-1] is `self.inter_channels` - pairwise_weight /= theta_x.shape[-1]**0.5 - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def dot_product(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight /= pairwise_weight.shape[-1] - return pairwise_weight - - def concatenation(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - h = theta_x.size(2) - w = phi_x.size(3) - theta_x = theta_x.repeat(1, 1, 1, w) - phi_x = phi_x.repeat(1, 1, h, 1) - - concat_feature = torch.cat([theta_x, phi_x], dim=1) - pairwise_weight = self.concat_project(concat_feature) - n, _, h, w = pairwise_weight.size() - pairwise_weight = pairwise_weight.view(n, h, w) - pairwise_weight /= pairwise_weight.shape[-1] - - return pairwise_weight - - def forward(self, x): - # Assume `reduction = 1`, then `inter_channels = C` - # or `inter_channels = C` when `mode="gaussian"` - - # NonLocal1d x: [N, C, H] - # NonLocal2d x: [N, C, H, W] - # NonLocal3d x: [N, C, T, H, W] - n = x.size(0) - - # NonLocal1d g_x: [N, H, C] - # NonLocal2d g_x: [N, HxW, C] - # NonLocal3d g_x: [N, TxHxW, C] - g_x = self.g(x).view(n, self.inter_channels, -1) - g_x = g_x.permute(0, 2, 1) - - # NonLocal1d theta_x: [N, H, C], phi_x: [N, C, H] - # NonLocal2d theta_x: [N, HxW, C], phi_x: [N, C, HxW] - # NonLocal3d theta_x: [N, TxHxW, C], phi_x: [N, C, TxHxW] - if self.mode == 'gaussian': - theta_x = x.view(n, self.in_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - if self.sub_sample: - phi_x = self.phi(x).view(n, self.in_channels, -1) - else: - phi_x = x.view(n, self.in_channels, -1) - elif self.mode == 'concatenation': - theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) - phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) - else: - theta_x = self.theta(x).view(n, self.inter_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - phi_x = self.phi(x).view(n, self.inter_channels, -1) - - pairwise_func = getattr(self, self.mode) - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = pairwise_func(theta_x, phi_x) - - # NonLocal1d y: [N, H, C] - # NonLocal2d y: [N, HxW, C] - # NonLocal3d y: [N, TxHxW, C] - y = torch.matmul(pairwise_weight, g_x) - # NonLocal1d y: [N, C, H] - # NonLocal2d y: [N, C, H, W] - # NonLocal3d y: [N, C, T, H, W] - y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, - *x.size()[2:]) - - output = x + self.conv_out(y) - - return output - - -class NonLocal1d(_NonLocalNd): - """1D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv1d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv1d'), - **kwargs): - super(NonLocal1d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool1d(kernel_size=2) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -@PLUGIN_LAYERS.register_module() -class NonLocal2d(_NonLocalNd): - """2D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv2d'). - """ - - _abbr_ = 'nonlocal_block' - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv2d'), - **kwargs): - super(NonLocal2d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -class NonLocal3d(_NonLocalNd): - """3D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv3d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv3d'), - **kwargs): - super(NonLocal3d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/norm.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/norm.py deleted file mode 100644 index 51efdc184..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/norm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import torch.nn as nn - -from mmcv.utils import is_tuple_of -from mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm -from .registry import NORM_LAYERS - -NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) -NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) -NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) -NORM_LAYERS.register_module('GN', module=nn.GroupNorm) -NORM_LAYERS.register_module('LN', module=nn.LayerNorm) -NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) -NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - When we build a norm layer with `build_norm_layer()`, we want to preserve - the norm type in variable names, e.g, self.bn1, self.gn. This method will - infer the abbreviation to map class types to abbreviations. - - Rule 1: If the class has the property "_abbr_", return the property. - Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or - InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and - "in" respectively. - Rule 3: If the class name contains "batch", "group", "layer" or "instance", - the abbreviation of this layer will be "bn", "gn", "ln" and "in" - respectively. - Rule 4: Otherwise, the abbreviation falls back to "norm". - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN - return 'in' - elif issubclass(class_type, _BatchNorm): - return 'bn' - elif issubclass(class_type, nn.GroupNorm): - return 'gn' - elif issubclass(class_type, nn.LayerNorm): - return 'ln' - else: - class_name = class_type.__name__.lower() - if 'batch' in class_name: - return 'bn' - elif 'group' in class_name: - return 'gn' - elif 'layer' in class_name: - return 'ln' - elif 'instance' in class_name: - return 'in' - else: - return 'norm_layer' - - -def build_norm_layer(cfg, num_features, postfix=''): - """Build normalization layer. - - Args: - cfg (dict): The norm layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate a norm layer. - - requires_grad (bool, optional): Whether stop gradient updates. - num_features (int): Number of input channels. - postfix (int | str): The postfix to be appended into norm abbreviation - to create named layer. - - Returns: - tuple[str, nn.Module]: The first element is the layer name consisting - of abbreviation and postfix, e.g., bn1, gn. The second element is the - created norm layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in NORM_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - - norm_layer = NORM_LAYERS.get(layer_type) - abbr = infer_abbr(norm_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - requires_grad = cfg_.pop('requires_grad', True) - cfg_.setdefault('eps', 1e-5) - if layer_type != 'GN': - layer = norm_layer(num_features, **cfg_) - if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): - layer._specify_ddp_gpu_num(1) - else: - assert 'num_groups' in cfg_ - layer = norm_layer(num_channels=num_features, **cfg_) - - for param in layer.parameters(): - param.requires_grad = requires_grad - - return name, layer - - -def is_norm(layer, exclude=None): - """Check if a layer is a normalization layer. - - Args: - layer (nn.Module): The layer to be checked. - exclude (type | tuple[type]): Types to be excluded. - - Returns: - bool: Whether the layer is a norm layer. - """ - if exclude is not None: - if not isinstance(exclude, tuple): - exclude = (exclude, ) - if not is_tuple_of(exclude, type): - raise TypeError( - f'"exclude" must be either None or type or a tuple of types, ' - f'but got {type(exclude)}: {exclude}') - - if exclude and isinstance(layer, exclude): - return False - - all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) - return isinstance(layer, all_norm_bases) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/padding.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/padding.py deleted file mode 100644 index e4ac6b28a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/padding.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import PADDING_LAYERS - -PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) -PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) -PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) - - -def build_padding_layer(cfg, *args, **kwargs): - """Build padding layer. - - Args: - cfg (None or dict): The padding layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate a padding layer. - - Returns: - nn.Module: Created padding layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - - cfg_ = cfg.copy() - padding_type = cfg_.pop('type') - if padding_type not in PADDING_LAYERS: - raise KeyError(f'Unrecognized padding type {padding_type}.') - else: - padding_layer = PADDING_LAYERS.get(padding_type) - - layer = padding_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/plugin.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/plugin.py deleted file mode 100644 index 009f7529b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/plugin.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import platform - -from .registry import PLUGIN_LAYERS - -if platform.system() == 'Windows': - import regex as re -else: - import re - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - This method will infer the abbreviation to map class types to - abbreviations. - - Rule 1: If the class has the property "abbr", return the property. - Rule 2: Otherwise, the abbreviation falls back to snake case of class - name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - - def camel2snack(word): - """Convert camel case word into snack case. - - Modified from `inflection lib - `_. - - Example:: - - >>> camel2snack("FancyBlock") - 'fancy_block' - """ - - word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) - word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) - word = word.replace('-', '_') - return word.lower() - - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - else: - return camel2snack(class_type.__name__) - - -def build_plugin_layer(cfg, postfix='', **kwargs): - """Build plugin layer. - - Args: - cfg (None or dict): cfg should contain: - - - type (str): identify plugin layer type. - - layer args: args needed to instantiate a plugin layer. - postfix (int, str): appended into norm abbreviation to - create named layer. Default: ''. - - Returns: - tuple[str, nn.Module]: The first one is the concatenation of - abbreviation and postfix. The second is the created plugin layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in PLUGIN_LAYERS: - raise KeyError(f'Unrecognized plugin type {layer_type}') - - plugin_layer = PLUGIN_LAYERS.get(layer_type) - abbr = infer_abbr(plugin_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - layer = plugin_layer(**kwargs, **cfg_) - - return name, layer diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/registry.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/registry.py deleted file mode 100644 index c29279776..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/registry.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -CONV_LAYERS = Registry('conv layer') -NORM_LAYERS = Registry('norm layer') -ACTIVATION_LAYERS = Registry('activation layer') -PADDING_LAYERS = Registry('padding layer') -UPSAMPLE_LAYERS = Registry('upsample layer') -PLUGIN_LAYERS = Registry('plugin layer') - -DROPOUT_LAYERS = Registry('drop out layers') -POSITIONAL_ENCODING = Registry('position encoding') -ATTENTION = Registry('attention') -FEEDFORWARD_NETWORK = Registry('feed-forward Network') -TRANSFORMER_LAYER = Registry('transformerLayer') -TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/scale.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/scale.py deleted file mode 100644 index c905fffcc..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/scale.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class Scale(nn.Module): - """A learnable scale parameter. - - This layer scales the input by a learnable factor. It multiplies a - learnable scale parameter of shape (1,) with input of any shape. - - Args: - scale (float): Initial value of scale factor. Default: 1.0 - """ - - def __init__(self, scale=1.0): - super(Scale, self).__init__() - self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) - - def forward(self, x): - return x * self.scale diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/swish.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/swish.py deleted file mode 100644 index e2ca8ed7b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/swish.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class Swish(nn.Module): - """Swish Module. - - This module applies the swish function: - - .. math:: - Swish(x) = x * Sigmoid(x) - - Returns: - Tensor: The output tensor. - """ - - def __init__(self): - super(Swish, self).__init__() - - def forward(self, x): - return x * torch.sigmoid(x) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/transformer.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/transformer.py deleted file mode 100644 index 70c6623c7..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/transformer.py +++ /dev/null @@ -1,944 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings -from typing import Sequence - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.cnn import (Linear, build_activation_layer, build_conv_layer, - build_norm_layer) -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential -from mmcv.utils import (ConfigDict, build_from_cfg, deprecated_api_warning, - to_2tuple) -from .drop import build_dropout -from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, - TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) - -# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file -try: - from mmcv.ops.multi_scale_deform_attn import \ - MultiScaleDeformableAttention # noqa F401 - warnings.warn( - ImportWarning( - '``MultiScaleDeformableAttention`` has been moved to ' - '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 - '``from mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 - 'to ``from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 - )) - -except ImportError: - warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' - '``mmcv.ops.multi_scale_deform_attn``, ' - 'You should install ``mmcv-full`` if you need this module. ') - - -def build_positional_encoding(cfg, default_args=None): - """Builder for Position Encoding.""" - return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) - - -def build_attention(cfg, default_args=None): - """Builder for attention.""" - return build_from_cfg(cfg, ATTENTION, default_args) - - -def build_feedforward_network(cfg, default_args=None): - """Builder for feed-forward network (FFN).""" - return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) - - -def build_transformer_layer(cfg, default_args=None): - """Builder for transformer layer.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) - - -def build_transformer_layer_sequence(cfg, default_args=None): - """Builder for transformer encoder and transformer decoder.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) - - -class AdaptivePadding(nn.Module): - """Applies padding adaptively to the input. - - This module can make input get fully covered by filter - you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad - zero around input. The "corner" mode would pad zero - to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel. Default: 1. - stride (int | tuple): Stride of the filter. Default: 1. - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - super(AdaptivePadding, self).__init__() - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - """Calculate the padding size of input. - - Args: - input_shape (:obj:`torch.Size`): arrange as (H, W). - - Returns: - Tuple[int]: The padding size along the - original H and W directions - """ - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - """Add padding to `x` - - Args: - x (Tensor): Input tensor has shape (B, C, H, W). - - Returns: - Tensor: The tensor with adaptive padding - """ - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The type of convolution - to generate patch embedding. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int): The slide stride of embedding conv. - Default: 16. - padding (int | tuple | string): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only works when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=16, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super(PatchEmbed, self).__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adaptive_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # e.g. when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adaptive_padding: - pad_h, pad_w = self.adaptive_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adaptive_padding: - x = self.adaptive_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map ((used in Swin Transformer)). - Our implementation uses `nn.Unfold` to - merge patches, which is about 25% faster than the original - implementation. However, we need to modify pretrained - models for compatibility. - - Args: - in_channels (int): The num of input channels. - to gets fully covered by filter and stride you specified. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adaptive_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - - if self.adaptive_padding: - x = self.adaptive_padding(x) - H, W = x.shape[-2:] - - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - x = self.sampler(x) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size - - -@ATTENTION.register_module() -class MultiheadAttention(BaseModule): - """A wrapper for ``torch.nn.MultiheadAttention``. - - This module implements MultiheadAttention with identity connection, - and positional encoding is also passed as input. - - Args: - embed_dims (int): The embedding dimension. - num_heads (int): Parallel attention heads. - attn_drop (float): A Dropout layer on attn_output_weights. - Default: 0.0. - proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. - Default: 0.0. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): When it is True, Key, Query and Value are shape of - (batch, n, embed_dim), otherwise (n, batch, embed_dim). - Default to False. - """ - - def __init__(self, - embed_dims, - num_heads, - attn_drop=0., - proj_drop=0., - dropout_layer=dict(type='Dropout', drop_prob=0.), - init_cfg=None, - batch_first=False, - **kwargs): - super(MultiheadAttention, self).__init__(init_cfg) - if 'dropout' in kwargs: - warnings.warn( - 'The arguments `dropout` in MultiheadAttention ' - 'has been deprecated, now you can separately ' - 'set `attn_drop`(float), proj_drop(float), ' - 'and `dropout_layer`(dict) ', DeprecationWarning) - attn_drop = kwargs['dropout'] - dropout_layer['drop_prob'] = kwargs.pop('dropout') - - self.embed_dims = embed_dims - self.num_heads = num_heads - self.batch_first = batch_first - - self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, - **kwargs) - - self.proj_drop = nn.Dropout(proj_drop) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else nn.Identity() - - @deprecated_api_warning({'residual': 'identity'}, - cls_name='MultiheadAttention') - def forward(self, - query, - key=None, - value=None, - identity=None, - query_pos=None, - key_pos=None, - attn_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `MultiheadAttention`. - - **kwargs allow passing a more general data flow when combining - with other operations in `transformerlayer`. - - Args: - query (Tensor): The input query with shape [num_queries, bs, - embed_dims] if self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - If None, the ``query`` will be used. Defaults to None. - value (Tensor): The value tensor with same shape as `key`. - Same in `nn.MultiheadAttention.forward`. Defaults to None. - If None, the `key` will be used. - identity (Tensor): This tensor, with the same shape as x, - will be used for the identity link. - If None, `x` will be used. Defaults to None. - query_pos (Tensor): The positional encoding for query, with - the same shape as `x`. If not None, it will - be added to `x` before forward function. Defaults to None. - key_pos (Tensor): The positional encoding for `key`, with the - same shape as `key`. Defaults to None. If not None, it will - be added to `key` before forward function. If None, and - `query_pos` has the same shape as `key`, then `query_pos` - will be used for `key_pos`. Defaults to None. - attn_mask (Tensor): ByteTensor mask with shape [num_queries, - num_keys]. Same in `nn.MultiheadAttention.forward`. - Defaults to None. - key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. - Defaults to None. - - Returns: - Tensor: forwarded results with shape - [num_queries, bs, embed_dims] - if self.batch_first is False, else - [bs, num_queries embed_dims]. - """ - - if key is None: - key = query - if value is None: - value = key - if identity is None: - identity = query - if key_pos is None: - if query_pos is not None: - # use query_pos if key_pos is not available - if query_pos.shape == key.shape: - key_pos = query_pos - else: - warnings.warn(f'position encoding of key is' - f'missing in {self.__class__.__name__}.') - if query_pos is not None: - query = query + query_pos - if key_pos is not None: - key = key + key_pos - - # Because the dataflow('key', 'query', 'value') of - # ``torch.nn.MultiheadAttention`` is (num_query, batch, - # embed_dims), We should adjust the shape of dataflow from - # batch_first (batch, num_query, embed_dims) to num_query_first - # (num_query ,batch, embed_dims), and recover ``attn_output`` - # from num_query_first to batch_first. - if self.batch_first: - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - out = self.attn( - query=query, - key=key, - value=value, - attn_mask=attn_mask, - key_padding_mask=key_padding_mask)[0] - - if self.batch_first: - out = out.transpose(0, 1) - - return identity + self.dropout_layer(self.proj_drop(out)) - - -@FEEDFORWARD_NETWORK.register_module() -class FFN(BaseModule): - """Implements feed-forward networks (FFNs) with identity connection. - - Args: - embed_dims (int): The feature dimension. Same as - `MultiheadAttention`. Defaults: 256. - feedforward_channels (int): The hidden dimension of FFNs. - Defaults: 1024. - num_fcs (int, optional): The number of fully-connected layers in - FFNs. Default: 2. - act_cfg (dict, optional): The activation config for FFNs. - Default: dict(type='ReLU') - ffn_drop (float, optional): Probability of an element to be - zeroed in FFN. Default 0.0. - add_identity (bool, optional): Whether to add the - identity connection. Default: `True`. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - @deprecated_api_warning( - { - 'dropout': 'ffn_drop', - 'add_residual': 'add_identity' - }, - cls_name='FFN') - def __init__(self, - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - act_cfg=dict(type='ReLU', inplace=True), - ffn_drop=0., - dropout_layer=None, - add_identity=True, - init_cfg=None, - **kwargs): - super(FFN, self).__init__(init_cfg) - assert num_fcs >= 2, 'num_fcs should be no less ' \ - f'than 2. got {num_fcs}.' - self.embed_dims = embed_dims - self.feedforward_channels = feedforward_channels - self.num_fcs = num_fcs - self.act_cfg = act_cfg - self.activate = build_activation_layer(act_cfg) - - layers = [] - in_channels = embed_dims - for _ in range(num_fcs - 1): - layers.append( - Sequential( - Linear(in_channels, feedforward_channels), self.activate, - nn.Dropout(ffn_drop))) - in_channels = feedforward_channels - layers.append(Linear(feedforward_channels, embed_dims)) - layers.append(nn.Dropout(ffn_drop)) - self.layers = Sequential(*layers) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else torch.nn.Identity() - self.add_identity = add_identity - - @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') - def forward(self, x, identity=None): - """Forward function for `FFN`. - - The function would add x to the output tensor if residue is None. - """ - out = self.layers(x) - if not self.add_identity: - return self.dropout_layer(out) - if identity is None: - identity = x - return identity + self.dropout_layer(out) - - -@TRANSFORMER_LAYER.register_module() -class BaseTransformerLayer(BaseModule): - """Base `TransformerLayer` for vision transformer. - - It can be built from `mmcv.ConfigDict` and support more flexible - customization, for example, using any number of `FFN or LN ` and - use different kinds of `attention` by specifying a list of `ConfigDict` - named `attn_cfgs`. It is worth mentioning that it supports `prenorm` - when you specifying `norm` as the first element of `operation_order`. - More details about the `prenorm`: `On Layer Normalization in the - Transformer Architecture `_ . - - Args: - attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for `self_attention` or `cross_attention` modules, - The order of the configs in the list should be consistent with - corresponding attentions in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. Default: None. - ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for FFN, The order of the configs in the list should be - consistent with corresponding ffn in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. - operation_order (tuple[str]): The execution order of operation - in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). - Support `prenorm` when you specifying first element as `norm`. - Default:None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): Key, Query and Value are shape - of (batch, n, embed_dim) - or (n, batch, embed_dim). Default to False. - """ - - def __init__(self, - attn_cfgs=None, - ffn_cfgs=dict( - type='FFN', - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - ffn_drop=0., - act_cfg=dict(type='ReLU', inplace=True), - ), - operation_order=None, - norm_cfg=dict(type='LN'), - init_cfg=None, - batch_first=False, - **kwargs): - - deprecated_args = dict( - feedforward_channels='feedforward_channels', - ffn_dropout='ffn_drop', - ffn_num_fcs='num_fcs') - for ori_name, new_name in deprecated_args.items(): - if ori_name in kwargs: - warnings.warn( - f'The arguments `{ori_name}` in BaseTransformerLayer ' - f'has been deprecated, now you should set `{new_name}` ' - f'and other FFN related arguments ' - f'to a dict named `ffn_cfgs`. ', DeprecationWarning) - ffn_cfgs[new_name] = kwargs[ori_name] - - super(BaseTransformerLayer, self).__init__(init_cfg) - - self.batch_first = batch_first - - assert set(operation_order) & set( - ['self_attn', 'norm', 'ffn', 'cross_attn']) == \ - set(operation_order), f'The operation_order of' \ - f' {self.__class__.__name__} should ' \ - f'contains all four operation type ' \ - f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" - - num_attn = operation_order.count('self_attn') + operation_order.count( - 'cross_attn') - if isinstance(attn_cfgs, dict): - attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] - else: - assert num_attn == len(attn_cfgs), f'The length ' \ - f'of attn_cfg {num_attn} is ' \ - f'not consistent with the number of attention' \ - f'in operation_order {operation_order}.' - - self.num_attn = num_attn - self.operation_order = operation_order - self.norm_cfg = norm_cfg - self.pre_norm = operation_order[0] == 'norm' - self.attentions = ModuleList() - - index = 0 - for operation_name in operation_order: - if operation_name in ['self_attn', 'cross_attn']: - if 'batch_first' in attn_cfgs[index]: - assert self.batch_first == attn_cfgs[index]['batch_first'] - else: - attn_cfgs[index]['batch_first'] = self.batch_first - attention = build_attention(attn_cfgs[index]) - # Some custom attentions used as `self_attn` - # or `cross_attn` can have different behavior. - attention.operation_name = operation_name - self.attentions.append(attention) - index += 1 - - self.embed_dims = self.attentions[0].embed_dims - - self.ffns = ModuleList() - num_ffns = operation_order.count('ffn') - if isinstance(ffn_cfgs, dict): - ffn_cfgs = ConfigDict(ffn_cfgs) - if isinstance(ffn_cfgs, dict): - ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] - assert len(ffn_cfgs) == num_ffns - for ffn_index in range(num_ffns): - if 'embed_dims' not in ffn_cfgs[ffn_index]: - ffn_cfgs[ffn_index]['embed_dims'] = self.embed_dims - else: - assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims - self.ffns.append( - build_feedforward_network(ffn_cfgs[ffn_index], - dict(type='FFN'))) - - self.norms = ModuleList() - num_norms = operation_order.count('norm') - for _ in range(num_norms): - self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) - - def forward(self, - query, - key=None, - value=None, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerDecoderLayer`. - - **kwargs contains some specific arguments of attentions. - - Args: - query (Tensor): The input query with shape - [num_queries, bs, embed_dims] if - self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - value (Tensor): The value tensor with same shape as `key`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor] | None): 2D Tensor used in - calculation of corresponding attention. The length of - it should equal to the number of `attention` in - `operation_order`. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in `self_attn` layer. - Defaults to None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: forwarded results with shape [num_queries, bs, embed_dims]. - """ - - norm_index = 0 - attn_index = 0 - ffn_index = 0 - identity = query - if attn_masks is None: - attn_masks = [None for _ in range(self.num_attn)] - elif isinstance(attn_masks, torch.Tensor): - attn_masks = [ - copy.deepcopy(attn_masks) for _ in range(self.num_attn) - ] - warnings.warn(f'Use same attn_mask in all attentions in ' - f'{self.__class__.__name__} ') - else: - assert len(attn_masks) == self.num_attn, f'The length of ' \ - f'attn_masks {len(attn_masks)} must be equal ' \ - f'to the number of attention in ' \ - f'operation_order {self.num_attn}' - - for layer in self.operation_order: - if layer == 'self_attn': - temp_key = temp_value = query - query = self.attentions[attn_index]( - query, - temp_key, - temp_value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=query_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=query_key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'norm': - query = self.norms[norm_index](query) - norm_index += 1 - - elif layer == 'cross_attn': - query = self.attentions[attn_index]( - query, - key, - value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=key_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'ffn': - query = self.ffns[ffn_index]( - query, identity if self.pre_norm else None) - ffn_index += 1 - - return query - - -@TRANSFORMER_LAYER_SEQUENCE.register_module() -class TransformerLayerSequence(BaseModule): - """Base class for TransformerEncoder and TransformerDecoder in vision - transformer. - - As base-class of Encoder and Decoder in vision transformer. - Support customization such as specifying different kind - of `transformer_layer` in `transformer_coder`. - - Args: - transformerlayer (list[obj:`mmcv.ConfigDict`] | - obj:`mmcv.ConfigDict`): Config of transformerlayer - in TransformerCoder. If it is obj:`mmcv.ConfigDict`, - it would be repeated `num_layer` times to a - list[`mmcv.ConfigDict`]. Default: None. - num_layers (int): The number of `TransformerLayer`. Default: None. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): - super(TransformerLayerSequence, self).__init__(init_cfg) - if isinstance(transformerlayers, dict): - transformerlayers = [ - copy.deepcopy(transformerlayers) for _ in range(num_layers) - ] - else: - assert isinstance(transformerlayers, list) and \ - len(transformerlayers) == num_layers - self.num_layers = num_layers - self.layers = ModuleList() - for i in range(num_layers): - self.layers.append(build_transformer_layer(transformerlayers[i])) - self.embed_dims = self.layers[0].embed_dims - self.pre_norm = self.layers[0].pre_norm - - def forward(self, - query, - key, - value, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerCoder`. - - Args: - query (Tensor): Input query with shape - `(num_queries, bs, embed_dims)`. - key (Tensor): The key tensor with shape - `(num_keys, bs, embed_dims)`. - value (Tensor): The value tensor with shape - `(num_keys, bs, embed_dims)`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor], optional): Each element is 2D Tensor - which is used in calculation of corresponding attention in - operation_order. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in self-attention - Default: None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: results with shape [num_queries, bs, embed_dims]. - """ - for layer in self.layers: - query = layer( - query, - key, - value, - query_pos=query_pos, - key_pos=key_pos, - attn_masks=attn_masks, - query_key_padding_mask=query_key_padding_mask, - key_padding_mask=key_padding_mask, - **kwargs) - return query diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/upsample.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/upsample.py deleted file mode 100644 index 0fd21fbf9..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/upsample.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import xavier_init -from .registry import UPSAMPLE_LAYERS - -UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) -UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) - - -@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') -class PixelShufflePack(nn.Module): - """Pixel Shuffle upsample layer. - - This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to - achieve a simple upsampling with pixel shuffle. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of the conv layer to expand the - channels. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super(PixelShufflePack, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - xavier_init(self.upsample_conv, distribution='uniform') - - def forward(self, x): - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x - - -def build_upsample_layer(cfg, *args, **kwargs): - """Build upsample layer. - - Args: - cfg (dict): The upsample layer config, which should contain: - - - type (str): Layer type. - - scale_factor (int): Upsample ratio, which is not applicable to - deconv. - - layer args: Args needed to instantiate a upsample layer. - args (argument list): Arguments passed to the ``__init__`` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the - ``__init__`` method of the corresponding conv layer. - - Returns: - nn.Module: Created upsample layer. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - raise KeyError( - f'the cfg dict must contain the key "type", but got {cfg}') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in UPSAMPLE_LAYERS: - raise KeyError(f'Unrecognized upsample type {layer_type}') - else: - upsample = UPSAMPLE_LAYERS.get(layer_type) - - if upsample is nn.Upsample: - cfg_['mode'] = layer_type - layer = upsample(*args, **kwargs, **cfg_) - return layer diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/wrappers.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/wrappers.py deleted file mode 100644 index 8aebf67bf..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/bricks/wrappers.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 - -Wrap some nn modules to support empty tensor input. Currently, these wrappers -are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask -heads are trained on only positive RoIs. -""" -import math - -import torch -import torch.nn as nn -from torch.nn.modules.utils import _pair, _triple - -from .registry import CONV_LAYERS, UPSAMPLE_LAYERS - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def obsolete_torch_version(torch_version, version_threshold): - return torch_version == 'parrots' or torch_version <= version_threshold - - -class NewEmptyTensorOp(torch.autograd.Function): - - @staticmethod - def forward(ctx, x, new_shape): - ctx.shape = x.shape - return x.new_empty(new_shape) - - @staticmethod - def backward(ctx, grad): - shape = ctx.shape - return NewEmptyTensorOp.apply(grad, shape), None - - -@CONV_LAYERS.register_module('Conv', force=True) -class Conv2d(nn.Conv2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module('Conv3d', force=True) -class Conv3d(nn.Conv3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv') -@UPSAMPLE_LAYERS.register_module('deconv', force=True) -class ConvTranspose2d(nn.ConvTranspose2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv3d') -@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) -class ConvTranspose3d(nn.ConvTranspose3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -class MaxPool2d(nn.MaxPool2d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), - _pair(self.padding), _pair(self.stride), - _pair(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class MaxPool3d(nn.MaxPool3d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), - _triple(self.padding), - _triple(self.stride), - _triple(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class Linear(torch.nn.Linear): - - def forward(self, x): - # empty tensor forward of Linear layer is supported in Pytorch 1.6 - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): - out_shape = [x.shape[0], self.out_features] - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/builder.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/builder.py deleted file mode 100644 index 7567316c5..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/builder.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..runner import Sequential -from ..utils import Registry, build_from_cfg - - -def build_model_from_cfg(cfg, registry, default_args=None): - """Build a PyTorch model from config dict(s). Different from - ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. - - Args: - cfg (dict, list[dict]): The config of modules, is is either a config - dict or a list of config dicts. If cfg is a list, a - the built modules will be wrapped with ``nn.Sequential``. - registry (:obj:`Registry`): A registry the module belongs to. - default_args (dict, optional): Default arguments to build the module. - Defaults to None. - - Returns: - nn.Module: A built nn module. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return Sequential(*modules) - else: - return build_from_cfg(cfg, registry, default_args) - - -MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/__init__.py deleted file mode 100644 index a263e31c1..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .flops_counter import get_model_complexity_info -from .fuse_conv_bn import fuse_conv_bn -from .sync_bn import revert_sync_batchnorm -from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, - KaimingInit, NormalInit, PretrainedInit, - TruncNormalInit, UniformInit, XavierInit, - bias_init_with_prob, caffe2_xavier_init, - constant_init, initialize, kaiming_init, normal_init, - trunc_normal_init, uniform_init, xavier_init) - -__all__ = [ - 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', - 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', - 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'revert_sync_batchnorm' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/flops_counter.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/flops_counter.py deleted file mode 100644 index a6045db84..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/flops_counter.py +++ /dev/null @@ -1,599 +0,0 @@ -# Modified from flops-counter.pytorch by Vladislav Sovrasov -# original repo: https://github.com/sovrasov/flops-counter.pytorch - -# MIT License - -# Copyright (c) 2018 Vladislav Sovrasov - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -import warnings -from functools import partial - -import numpy as np -import torch -import torch.nn as nn - -import mmcv - - -def get_model_complexity_info(model, - input_shape, - print_per_layer_stat=True, - as_strings=True, - input_constructor=None, - flush=False, - ost=sys.stdout): - """Get complexity information of a model. - - This method can calculate FLOPs and parameter counts of a model with - corresponding input shape. It can also print complexity information for - each layer in a model. - - Supported layers are listed as below: - - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. - - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, - ``nn.LeakyReLU``, ``nn.ReLU6``. - - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, - ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, - ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, - ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, - ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. - - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, - ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, - ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. - - Linear: ``nn.Linear``. - - Deconvolution: ``nn.ConvTranspose2d``. - - Upsample: ``nn.Upsample``. - - Args: - model (nn.Module): The model for complexity calculation. - input_shape (tuple): Input shape used for calculation. - print_per_layer_stat (bool): Whether to print complexity information - for each layer in a model. Default: True. - as_strings (bool): Output FLOPs and params counts in a string form. - Default: True. - input_constructor (None | callable): If specified, it takes a callable - method that generates input. otherwise, it will generate a random - tensor with input shape to calculate FLOPs. Default: None. - flush (bool): same as that in :func:`print`. Default: False. - ost (stream): same as ``file`` param in :func:`print`. - Default: sys.stdout. - - Returns: - tuple[float | str]: If ``as_strings`` is set to True, it will return - FLOPs and parameter counts in a string format. otherwise, it will - return those in a float number format. - """ - assert type(input_shape) is tuple - assert len(input_shape) >= 1 - assert isinstance(model, nn.Module) - flops_model = add_flops_counting_methods(model) - flops_model.eval() - flops_model.start_flops_count() - if input_constructor: - input = input_constructor(input_shape) - _ = flops_model(**input) - else: - try: - batch = torch.ones(()).new_empty( - (1, *input_shape), - dtype=next(flops_model.parameters()).dtype, - device=next(flops_model.parameters()).device) - except StopIteration: - # Avoid StopIteration for models which have no parameters, - # like `nn.Relu()`, `nn.AvgPool2d`, etc. - batch = torch.ones(()).new_empty((1, *input_shape)) - - _ = flops_model(batch) - - flops_count, params_count = flops_model.compute_average_flops_cost() - if print_per_layer_stat: - print_model_with_flops( - flops_model, flops_count, params_count, ost=ost, flush=flush) - flops_model.stop_flops_count() - - if as_strings: - return flops_to_string(flops_count), params_to_string(params_count) - - return flops_count, params_count - - -def flops_to_string(flops, units='GFLOPs', precision=2): - """Convert FLOPs number into a string. - - Note that Here we take a multiply-add counts as one FLOP. - - Args: - flops (float): FLOPs number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', - 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically - choose the most suitable unit for FLOPs. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted FLOPs number with units. - - Examples: - >>> flops_to_string(1e9) - '1.0 GFLOPs' - >>> flops_to_string(2e5, 'MFLOPs') - '0.2 MFLOPs' - >>> flops_to_string(3e-9, None) - '3e-09 FLOPs' - """ - if units is None: - if flops // 10**9 > 0: - return str(round(flops / 10.**9, precision)) + ' GFLOPs' - elif flops // 10**6 > 0: - return str(round(flops / 10.**6, precision)) + ' MFLOPs' - elif flops // 10**3 > 0: - return str(round(flops / 10.**3, precision)) + ' KFLOPs' - else: - return str(flops) + ' FLOPs' - else: - if units == 'GFLOPs': - return str(round(flops / 10.**9, precision)) + ' ' + units - elif units == 'MFLOPs': - return str(round(flops / 10.**6, precision)) + ' ' + units - elif units == 'KFLOPs': - return str(round(flops / 10.**3, precision)) + ' ' + units - else: - return str(flops) + ' FLOPs' - - -def params_to_string(num_params, units=None, precision=2): - """Convert parameter number into a string. - - Args: - num_params (float): Parameter number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'M', - 'K' and ''. If set to None, it will automatically choose the most - suitable unit for Parameter number. Default: None. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted parameter number with units. - - Examples: - >>> params_to_string(1e9) - '1000.0 M' - >>> params_to_string(2e5) - '200.0 k' - >>> params_to_string(3e-9) - '3e-09' - """ - if units is None: - if num_params // 10**6 > 0: - return str(round(num_params / 10**6, precision)) + ' M' - elif num_params // 10**3: - return str(round(num_params / 10**3, precision)) + ' k' - else: - return str(num_params) - else: - if units == 'M': - return str(round(num_params / 10.**6, precision)) + ' ' + units - elif units == 'K': - return str(round(num_params / 10.**3, precision)) + ' ' + units - else: - return str(num_params) - - -def print_model_with_flops(model, - total_flops, - total_params, - units='GFLOPs', - precision=3, - ost=sys.stdout, - flush=False): - """Print a model with FLOPs for each layer. - - Args: - model (nn.Module): The model to be printed. - total_flops (float): Total FLOPs of the model. - total_params (float): Total parameter counts of the model. - units (str | None): Converted FLOPs units. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 3. - ost (stream): same as `file` param in :func:`print`. - Default: sys.stdout. - flush (bool): same as that in :func:`print`. Default: False. - - Example: - >>> class ExampleModel(nn.Module): - - >>> def __init__(self): - >>> super().__init__() - >>> self.conv1 = nn.Conv2d(3, 8, 3) - >>> self.conv2 = nn.Conv2d(8, 256, 3) - >>> self.conv3 = nn.Conv2d(256, 8, 3) - >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) - >>> self.flatten = nn.Flatten() - >>> self.fc = nn.Linear(8, 1) - - >>> def forward(self, x): - >>> x = self.conv1(x) - >>> x = self.conv2(x) - >>> x = self.conv3(x) - >>> x = self.avg_pool(x) - >>> x = self.flatten(x) - >>> x = self.fc(x) - >>> return x - - >>> model = ExampleModel() - >>> x = (3, 16, 16) - to print the complexity information state for each layer, you can use - >>> get_model_complexity_info(model, x) - or directly use - >>> print_model_with_flops(model, 4579784.0, 37361) - ExampleModel( - 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, - (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 - (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) - (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) - (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) - (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) - (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) - ) - """ - - def accumulate_params(self): - if is_supported_instance(self): - return self.__params__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_params() - return sum - - def accumulate_flops(self): - if is_supported_instance(self): - return self.__flops__ / model.__batch_counter__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_flops() - return sum - - def flops_repr(self): - accumulated_num_params = self.accumulate_params() - accumulated_flops_cost = self.accumulate_flops() - return ', '.join([ - params_to_string( - accumulated_num_params, units='M', precision=precision), - '{:.3%} Params'.format(accumulated_num_params / total_params), - flops_to_string( - accumulated_flops_cost, units=units, precision=precision), - '{:.3%} FLOPs'.format(accumulated_flops_cost / total_flops), - self.original_extra_repr() - ]) - - def add_extra_repr(m): - m.accumulate_flops = accumulate_flops.__get__(m) - m.accumulate_params = accumulate_params.__get__(m) - flops_extra_repr = flops_repr.__get__(m) - if m.extra_repr != flops_extra_repr: - m.original_extra_repr = m.extra_repr - m.extra_repr = flops_extra_repr - assert m.extra_repr != m.original_extra_repr - - def del_extra_repr(m): - if hasattr(m, 'original_extra_repr'): - m.extra_repr = m.original_extra_repr - del m.original_extra_repr - if hasattr(m, 'accumulate_flops'): - del m.accumulate_flops - - model.apply(add_extra_repr) - print(model, file=ost, flush=flush) - model.apply(del_extra_repr) - - -def get_model_parameters_number(model): - """Calculate parameter number of a model. - - Args: - model (nn.module): The model for parameter number calculation. - - Returns: - float: Parameter number of the model. - """ - num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - return num_params - - -def add_flops_counting_methods(net_main_module): - # adding additional methods to the existing module object, - # this is done this way so that each function has access to self object - net_main_module.start_flops_count = start_flops_count.__get__( - net_main_module) - net_main_module.stop_flops_count = stop_flops_count.__get__( - net_main_module) - net_main_module.reset_flops_count = reset_flops_count.__get__( - net_main_module) - net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # noqa: E501 - net_main_module) - - net_main_module.reset_flops_count() - - return net_main_module - - -def compute_average_flops_cost(self): - """Compute average FLOPs cost. - - A method to compute average FLOPs cost, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - - Returns: - float: Current mean flops consumption per image. - """ - batches_count = self.__batch_counter__ - flops_sum = 0 - for module in self.modules(): - if is_supported_instance(module): - flops_sum += module.__flops__ - params_sum = get_model_parameters_number(self) - return flops_sum / batches_count, params_sum - - -def start_flops_count(self): - """Activate the computation of mean flops consumption per image. - - A method to activate the computation of mean flops consumption per image. - which will be available after ``add_flops_counting_methods()`` is called on - a desired net object. It should be called before running the network. - """ - add_batch_counter_hook_function(self) - - def add_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - return - - else: - handle = module.register_forward_hook( - get_modules_mapping()[type(module)]) - - module.__flops_handle__ = handle - - self.apply(partial(add_flops_counter_hook_function)) - - -def stop_flops_count(self): - """Stop computing the mean flops consumption per image. - - A method to stop computing the mean flops consumption per image, which will - be available after ``add_flops_counting_methods()`` is called on a desired - net object. It can be called to pause the computation whenever. - """ - remove_batch_counter_hook_function(self) - self.apply(remove_flops_counter_hook_function) - - -def reset_flops_count(self): - """Reset statistics computed so far. - - A method to Reset computed statistics, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - """ - add_batch_counter_variables_or_reset(self) - self.apply(add_flops_counter_variable_or_reset) - - -# ---- Internal functions -def empty_flops_counter_hook(module, input, output): - module.__flops__ += 0 - - -def upsample_flops_counter_hook(module, input, output): - output_size = output[0] - batch_size = output_size.shape[0] - output_elements_count = batch_size - for val in output_size.shape[1:]: - output_elements_count *= val - module.__flops__ += int(output_elements_count) - - -def relu_flops_counter_hook(module, input, output): - active_elements_count = output.numel() - module.__flops__ += int(active_elements_count) - - -def linear_flops_counter_hook(module, input, output): - input = input[0] - output_last_dim = output.shape[ - -1] # pytorch checks dimensions, so here we don't care much - module.__flops__ += int(np.prod(input.shape) * output_last_dim) - - -def pool_flops_counter_hook(module, input, output): - input = input[0] - module.__flops__ += int(np.prod(input.shape)) - - -def norm_flops_counter_hook(module, input, output): - input = input[0] - - batch_flops = np.prod(input.shape) - if (getattr(module, 'affine', False) - or getattr(module, 'elementwise_affine', False)): - batch_flops *= 2 - module.__flops__ += int(batch_flops) - - -def deconv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - input_height, input_width = input.shape[2:] - - kernel_height, kernel_width = conv_module.kernel_size - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = ( - kernel_height * kernel_width * in_channels * filters_per_channel) - - active_elements_count = batch_size * input_height * input_width - overall_conv_flops = conv_per_position_flops * active_elements_count - bias_flops = 0 - if conv_module.bias is not None: - output_height, output_width = output.shape[2:] - bias_flops = out_channels * batch_size * output_height * output_width - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def conv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - output_dims = list(output.shape[2:]) - - kernel_dims = list(conv_module.kernel_size) - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = int( - np.prod(kernel_dims)) * in_channels * filters_per_channel - - active_elements_count = batch_size * int(np.prod(output_dims)) - - overall_conv_flops = conv_per_position_flops * active_elements_count - - bias_flops = 0 - - if conv_module.bias is not None: - - bias_flops = out_channels * active_elements_count - - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def batch_counter_hook(module, input, output): - batch_size = 1 - if len(input) > 0: - # Can have multiple inputs, getting the first one - input = input[0] - batch_size = len(input) - else: - warnings.warn('No positional inputs found for a module, ' - 'assuming batch size is 1.') - module.__batch_counter__ += batch_size - - -def add_batch_counter_variables_or_reset(module): - - module.__batch_counter__ = 0 - - -def add_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - return - - handle = module.register_forward_hook(batch_counter_hook) - module.__batch_counter_handle__ = handle - - -def remove_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - module.__batch_counter_handle__.remove() - del module.__batch_counter_handle__ - - -def add_flops_counter_variable_or_reset(module): - if is_supported_instance(module): - if hasattr(module, '__flops__') or hasattr(module, '__params__'): - warnings.warn('variables __flops__ or __params__ are already ' - 'defined for the module' + type(module).__name__ + - ' ptflops can affect your code!') - module.__flops__ = 0 - module.__params__ = get_model_parameters_number(module) - - -def is_supported_instance(module): - if type(module) in get_modules_mapping(): - return True - return False - - -def remove_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - module.__flops_handle__.remove() - del module.__flops_handle__ - - -def get_modules_mapping(): - return { - # convolutions - nn.Conv1d: conv_flops_counter_hook, - nn.Conv2d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, - nn.Conv3d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, - # activations - nn.ReLU: relu_flops_counter_hook, - nn.PReLU: relu_flops_counter_hook, - nn.ELU: relu_flops_counter_hook, - nn.LeakyReLU: relu_flops_counter_hook, - nn.ReLU6: relu_flops_counter_hook, - # poolings - nn.MaxPool1d: pool_flops_counter_hook, - nn.AvgPool1d: pool_flops_counter_hook, - nn.AvgPool2d: pool_flops_counter_hook, - nn.MaxPool2d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, - nn.MaxPool3d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, - nn.AvgPool3d: pool_flops_counter_hook, - nn.AdaptiveMaxPool1d: pool_flops_counter_hook, - nn.AdaptiveAvgPool1d: pool_flops_counter_hook, - nn.AdaptiveMaxPool2d: pool_flops_counter_hook, - nn.AdaptiveAvgPool2d: pool_flops_counter_hook, - nn.AdaptiveMaxPool3d: pool_flops_counter_hook, - nn.AdaptiveAvgPool3d: pool_flops_counter_hook, - # normalizations - nn.BatchNorm1d: norm_flops_counter_hook, - nn.BatchNorm2d: norm_flops_counter_hook, - nn.BatchNorm3d: norm_flops_counter_hook, - nn.GroupNorm: norm_flops_counter_hook, - nn.InstanceNorm1d: norm_flops_counter_hook, - nn.InstanceNorm2d: norm_flops_counter_hook, - nn.InstanceNorm3d: norm_flops_counter_hook, - nn.LayerNorm: norm_flops_counter_hook, - # FC - nn.Linear: linear_flops_counter_hook, - mmcv.cnn.bricks.Linear: linear_flops_counter_hook, - # Upscale - nn.Upsample: upsample_flops_counter_hook, - # Deconvolution - nn.ConvTranspose2d: deconv_flops_counter_hook, - mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, - } diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/fuse_conv_bn.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/fuse_conv_bn.py deleted file mode 100644 index cb7076f80..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/fuse_conv_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def _fuse_conv_bn(conv, bn): - """Fuse conv and bn into one module. - - Args: - conv (nn.Module): Conv to be fused. - bn (nn.Module): BN to be fused. - - Returns: - nn.Module: Fused module. - """ - conv_w = conv.weight - conv_b = conv.bias if conv.bias is not None else torch.zeros_like( - bn.running_mean) - - factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) - conv.weight = nn.Parameter(conv_w * - factor.reshape([conv.out_channels, 1, 1, 1])) - conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) - return conv - - -def fuse_conv_bn(module): - """Recursively fuse conv and bn in a module. - - During inference, the functionary of batch norm layers is turned off - but only the mean and var alone channels are used, which exposes the - chance to fuse it with the preceding conv layers to save computations and - simplify network structures. - - Args: - module (nn.Module): Module to be fused. - - Returns: - nn.Module: Fused module. - """ - last_conv = None - last_conv_name = None - - for name, child in module.named_children(): - if isinstance(child, - (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): - if last_conv is None: # only fuse BN that is after Conv - continue - fused_conv = _fuse_conv_bn(last_conv, child) - module._modules[last_conv_name] = fused_conv - # To reduce changes, set BN as Identity instead of deleting it. - module._modules[name] = nn.Identity() - last_conv = None - elif isinstance(child, nn.Conv2d): - last_conv = child - last_conv_name = name - else: - fuse_conv_bn(child) - return module diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/sync_bn.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/sync_bn.py deleted file mode 100644 index 0c52526e9..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/sync_bn.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -import mmcv - - -class _BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): - """A general BatchNorm layer without input dimension check. - - Reproduced from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc - is `_check_input_dim` that is designed for tensor sanity checks. - The check has been bypassed in this class for the convenience of converting - SyncBatchNorm. - """ - - def _check_input_dim(self, input): - return - - -def revert_sync_batchnorm(module): - """Helper function to convert all `SyncBatchNorm` (SyncBN) and - `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to - `BatchNormXd` layers. - - Adapted from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - - Args: - module (nn.Module): The module containing `SyncBatchNorm` layers. - - Returns: - module_output: The converted module with `BatchNormXd` layers. - """ - module_output = module - module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] - if hasattr(mmcv, 'ops'): - module_checklist.append(mmcv.ops.SyncBatchNorm) - if isinstance(module, tuple(module_checklist)): - module_output = _BatchNormXd(module.num_features, module.eps, - module.momentum, module.affine, - module.track_running_stats) - if module.affine: - # no_grad() may not be needed here but - # just to be consistent with `convert_sync_batchnorm()` - with torch.no_grad(): - module_output.weight = module.weight - module_output.bias = module.bias - module_output.running_mean = module.running_mean - module_output.running_var = module.running_var - module_output.num_batches_tracked = module.num_batches_tracked - module_output.training = module.training - # qconfig exists in quantized models - if hasattr(module, 'qconfig'): - module_output.qconfig = module.qconfig - for name, child in module.named_children(): - module_output.add_module(name, revert_sync_batchnorm(child)) - del module - return module_output diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/weight_init.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/weight_init.py deleted file mode 100644 index 0ac08c87f..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/cnn/utils/weight_init.py +++ /dev/null @@ -1,685 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings - -import numpy as np -import torch -import torch.nn as nn -from torch import Tensor - -from mmcv.utils import Registry, build_from_cfg, get_logger, print_log - -INITIALIZERS = Registry('initializer') - - -def update_init_info(module, init_info): - """Update the `_params_init_info` in the module if the value of parameters - are changed. - - Args: - module (obj:`nn.Module`): The module of PyTorch with a user-defined - attribute `_params_init_info` which records the initialization - information. - init_info (str): The string that describes the initialization. - """ - assert hasattr( - module, - '_params_init_info'), f'Can not find `_params_init_info` in {module}' - for name, param in module.named_parameters(): - - assert param in module._params_init_info, ( - f'Find a new :obj:`Parameter` ' - f'named `{name}` during executing the ' - f'`init_weights` of ' - f'`{module.__class__.__name__}`. ' - f'Please do not add or ' - f'replace parameters during executing ' - f'the `init_weights`. ') - - # The parameter has been changed during executing the - # `init_weights` of module - mean_value = param.data.mean() - if module._params_init_info[param]['tmp_mean_value'] != mean_value: - module._params_init_info[param]['init_info'] = init_info - module._params_init_info[param]['tmp_mean_value'] = mean_value - - -def constant_init(module, val, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.constant_(module.weight, val) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def xavier_init(module, gain=1, bias=0, distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.xavier_uniform_(module.weight, gain=gain) - else: - nn.init.xavier_normal_(module.weight, gain=gain) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def normal_init(module, mean=0, std=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.normal_(module.weight, mean, std) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def trunc_normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - trunc_normal_(module.weight, mean, std, a, b) # type: ignore - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) # type: ignore - - -def uniform_init(module, a=0, b=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.uniform_(module.weight, a, b) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def kaiming_init(module, - a=0, - mode='fan_out', - nonlinearity='relu', - bias=0, - distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.kaiming_uniform_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - else: - nn.init.kaiming_normal_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def caffe2_xavier_init(module, bias=0): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - kaiming_init( - module, - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - bias=bias, - distribution='uniform') - - -def bias_init_with_prob(prior_prob): - """initialize conv/fc bias value according to a given probability value.""" - bias_init = float(-np.log((1 - prior_prob) / prior_prob)) - return bias_init - - -def _get_bases_name(m): - return [b.__name__ for b in m.__class__.__bases__] - - -class BaseInit(object): - - def __init__(self, *, bias=0, bias_prob=None, layer=None): - self.wholemodule = False - if not isinstance(bias, (int, float)): - raise TypeError(f'bias must be a number, but got a {type(bias)}') - - if bias_prob is not None: - if not isinstance(bias_prob, float): - raise TypeError(f'bias_prob type must be float, \ - but got {type(bias_prob)}') - - if layer is not None: - if not isinstance(layer, (str, list)): - raise TypeError(f'layer must be a str or a list of str, \ - but got a {type(layer)}') - else: - layer = [] - - if bias_prob is not None: - self.bias = bias_init_with_prob(bias_prob) - else: - self.bias = bias - self.layer = [layer] if isinstance(layer, str) else layer - - def _get_init_info(self): - info = f'{self.__class__.__name__}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Constant') -class ConstantInit(BaseInit): - """Initialize module parameters with constant values. - - Args: - val (int | float): the value to fill the weights in the module with - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, val, **kwargs): - super().__init__(**kwargs) - self.val = val - - def __call__(self, module): - - def init(m): - if self.wholemodule: - constant_init(m, self.val, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - constant_init(m, self.val, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Xavier') -class XavierInit(BaseInit): - r"""Initialize module parameters with values according to the method - described in `Understanding the difficulty of training deep feedforward - neural networks - Glorot, X. & Bengio, Y. (2010). - `_ - - Args: - gain (int | float): an optional scaling factor. Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` - or ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, gain=1, distribution='normal', **kwargs): - super().__init__(**kwargs) - self.gain = gain - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - xavier_init(m, self.gain, self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - xavier_init(m, self.gain, self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: gain={self.gain}, ' \ - f'distribution={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Normal') -class NormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. - - Args: - mean (int | float):the mean of the normal distribution. Defaults to 0. - std (int | float): the standard deviation of the normal distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, mean=0, std=1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.std = std - - def __call__(self, module): - - def init(m): - if self.wholemodule: - normal_init(m, self.mean, self.std, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - normal_init(m, self.mean, self.std, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: mean={self.mean},' \ - f' std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='TruncNormal') -class TruncNormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values - outside :math:`[a, b]`. - - Args: - mean (float): the mean of the normal distribution. Defaults to 0. - std (float): the standard deviation of the normal distribution. - Defaults to 1. - a (float): The minimum cutoff value. - b ( float): The maximum cutoff value. - bias (float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - **kwargs) -> None: - super().__init__(**kwargs) - self.mean = mean - self.std = std - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ - f' mean={self.mean}, std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Uniform') -class UniformInit(BaseInit): - r"""Initialize module parameters with values drawn from the uniform - distribution :math:`\mathcal{U}(a, b)`. - - Args: - a (int | float): the lower bound of the uniform distribution. - Defaults to 0. - b (int | float): the upper bound of the uniform distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, a=0, b=1, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def __call__(self, module): - - def init(m): - if self.wholemodule: - uniform_init(m, self.a, self.b, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - uniform_init(m, self.a, self.b, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a},' \ - f' b={self.b}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Kaiming') -class KaimingInit(BaseInit): - r"""Initialize module parameters with the values according to the method - described in `Delving deep into rectifiers: Surpassing human-level - performance on ImageNet classification - He, K. et al. (2015). - `_ - - Args: - a (int | float): the negative slope of the rectifier used after this - layer (only used with ``'leaky_relu'``). Defaults to 0. - mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing - ``'fan_in'`` preserves the magnitude of the variance of the weights - in the forward pass. Choosing ``'fan_out'`` preserves the - magnitudes in the backwards pass. Defaults to ``'fan_out'``. - nonlinearity (str): the non-linear function (`nn.functional` name), - recommended to use only with ``'relu'`` or ``'leaky_relu'`` . - Defaults to 'relu'. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` or - ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - a=0, - mode='fan_out', - nonlinearity='relu', - distribution='normal', - **kwargs): - super().__init__(**kwargs) - self.a = a - self.mode = mode - self.nonlinearity = nonlinearity - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ - f'nonlinearity={self.nonlinearity}, ' \ - f'distribution ={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Caffe2Xavier') -class Caffe2XavierInit(KaimingInit): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - def __init__(self, **kwargs): - super().__init__( - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='uniform', - **kwargs) - - def __call__(self, module): - super().__call__(module) - - -@INITIALIZERS.register_module(name='Pretrained') -class PretrainedInit(object): - """Initialize module by loading a pretrained model. - - Args: - checkpoint (str): the checkpoint file of the pretrained model should - be load. - prefix (str, optional): the prefix of a sub-module in the pretrained - model. it is for loading a part of the pretrained model to - initialize. For example, if we would like to only load the - backbone of a detector model, we can set ``prefix='backbone.'``. - Defaults to None. - map_location (str): map tensors into proper locations. - """ - - def __init__(self, checkpoint, prefix=None, map_location=None): - self.checkpoint = checkpoint - self.prefix = prefix - self.map_location = map_location - - def __call__(self, module): - from mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, - load_state_dict) - logger = get_logger('mmcv') - if self.prefix is None: - print_log(f'load model from: {self.checkpoint}', logger=logger) - load_checkpoint( - module, - self.checkpoint, - map_location=self.map_location, - strict=False, - logger=logger) - else: - print_log( - f'load {self.prefix} in model from: {self.checkpoint}', - logger=logger) - state_dict = _load_checkpoint_with_prefix( - self.prefix, self.checkpoint, map_location=self.map_location) - load_state_dict(module, state_dict, strict=False, logger=logger) - - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: load from {self.checkpoint}' - return info - - -def _initialize(module, cfg, wholemodule=False): - func = build_from_cfg(cfg, INITIALIZERS) - # wholemodule flag is for override mode, there is no layer key in override - # and initializer will give init values for the whole module with the name - # in override. - func.wholemodule = wholemodule - func(module) - - -def _initialize_override(module, override, cfg): - if not isinstance(override, (dict, list)): - raise TypeError(f'override must be a dict or a list of dict, \ - but got {type(override)}') - - override = [override] if isinstance(override, dict) else override - - for override_ in override: - - cp_override = copy.deepcopy(override_) - name = cp_override.pop('name', None) - if name is None: - raise ValueError('`override` must contain the key "name",' - f'but got {cp_override}') - # if override only has name key, it means use args in init_cfg - if not cp_override: - cp_override.update(cfg) - # if override has name key and other args except type key, it will - # raise error - elif 'type' not in cp_override.keys(): - raise ValueError( - f'`override` need "type" key, but got {cp_override}') - - if hasattr(module, name): - _initialize(getattr(module, name), cp_override, wholemodule=True) - else: - raise RuntimeError(f'module did not have attribute {name}, ' - f'but init_cfg is {cp_override}.') - - -def initialize(module, init_cfg): - r"""Initialize a module. - - Args: - module (``torch.nn.Module``): the module will be initialized. - init_cfg (dict | list[dict]): initialization configuration dict to - define initializer. OpenMMLab has implemented 6 initializers - including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, - ``Kaiming``, and ``Pretrained``. - - Example: - >>> module = nn.Linear(2, 3, bias=True) - >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) - >>> initialize(module, init_cfg) - - >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) - >>> # define key ``'layer'`` for initializing layer with different - >>> # configuration - >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), - dict(type='Constant', layer='Linear', val=2)] - >>> initialize(module, init_cfg) - - >>> # define key``'override'`` to initialize some specific part in - >>> # module - >>> class FooNet(nn.Module): - >>> def __init__(self): - >>> super().__init__() - >>> self.feat = nn.Conv2d(3, 16, 3) - >>> self.reg = nn.Conv2d(16, 10, 3) - >>> self.cls = nn.Conv2d(16, 5, 3) - >>> model = FooNet() - >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', - >>> override=dict(type='Constant', name='reg', val=3, bias=4)) - >>> initialize(model, init_cfg) - - >>> model = ResNet(depth=50) - >>> # Initialize weights with the pretrained model. - >>> init_cfg = dict(type='Pretrained', - checkpoint='torchvision://resnet50') - >>> initialize(model, init_cfg) - - >>> # Initialize weights of a sub-module with the specific part of - >>> # a pretrained model by using "prefix". - >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ - >>> 'retinanet_r50_fpn_1x_coco/'\ - >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' - >>> init_cfg = dict(type='Pretrained', - checkpoint=url, prefix='backbone.') - """ - if not isinstance(init_cfg, (dict, list)): - raise TypeError(f'init_cfg must be a dict or a list of dict, \ - but got {type(init_cfg)}') - - if isinstance(init_cfg, dict): - init_cfg = [init_cfg] - - for cfg in init_cfg: - # should deeply copy the original config because cfg may be used by - # other modules, e.g., one init_cfg shared by multiple bottleneck - # blocks, the expected cfg will be changed after pop and will change - # the initialization behavior of other modules - cp_cfg = copy.deepcopy(cfg) - override = cp_cfg.pop('override', None) - _initialize(module, cp_cfg) - - if override is not None: - cp_cfg.pop('layer', None) - _initialize_override(module, override, cp_cfg) - else: - # All attributes in module have same initialization. - pass - - -def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, - b: float) -> Tensor: - # Method based on - # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf - # Modified from - # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - def norm_cdf(x): - # Computes standard normal cumulative distribution function - return (1. + math.erf(x / math.sqrt(2.))) / 2. - - if (mean < a - 2 * std) or (mean > b + 2 * std): - warnings.warn( - 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' - 'The distribution of values may be incorrect.', - stacklevel=2) - - with torch.no_grad(): - # Values are generated by using a truncated uniform distribution and - # then using the inverse CDF for the normal distribution. - # Get upper and lower cdf values - lower = norm_cdf((a - mean) / std) - upper = norm_cdf((b - mean) / std) - - # Uniformly fill tensor with values from [lower, upper], then translate - # to [2lower-1, 2upper-1]. - tensor.uniform_(2 * lower - 1, 2 * upper - 1) - - # Use inverse cdf transform for normal distribution to get truncated - # standard normal - tensor.erfinv_() - - # Transform to proper mean, std - tensor.mul_(std * math.sqrt(2.)) - tensor.add_(mean) - - # Clamp to ensure it's in the proper range - tensor.clamp_(min=a, max=b) - return tensor - - -def trunc_normal_(tensor: Tensor, - mean: float = 0., - std: float = 1., - a: float = -2., - b: float = 2.) -> Tensor: - r"""Fills the input Tensor with values drawn from a truncated - normal distribution. The values are effectively drawn from the - normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` - with values outside :math:`[a, b]` redrawn until they are within - the bounds. The method used for generating the random values works - best when :math:`a \leq \text{mean} \leq b`. - - Modified from - https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - - Args: - tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. - mean (float): the mean of the normal distribution. - std (float): the standard deviation of the normal distribution. - a (float): the minimum cutoff value. - b (float): the maximum cutoff value. - """ - return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/device/__init__.py deleted file mode 100644 index 6ac55e63b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from . import ipu, mlu - -__all__ = ['mlu', 'ipu'] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/__init__.py deleted file mode 100755 index d550865ad..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import IPUFp16OptimizerHook - from .model_wrapper import ipu_model_wrapper - from .runner import IPUBaseRunner, IPUEpochBasedRunner, IPUIterBasedRunner - from .utils import cfg2options - __all__ = [ - 'cfg2options', 'ipu_model_wrapper', 'IPUFp16OptimizerHook', - 'IPUDataLoader', 'IPUBaseRunner', 'IPUEpochBasedRunner', - 'IPUIterBasedRunner' - ] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/dataloader.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/dataloader.py deleted file mode 100755 index 1485df2f3..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/dataloader.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence -from functools import partial - -import poptorch -from torch.utils.data.dataloader import default_collate - -from mmcv.parallel import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Put each data field into a tensor/DataContainer with outer dimension - batch size. - - TODO support for - :type:`~mmcv.parallel.DataContainer`. Currently, it will be ignored. - There are 3 cases. - - 1. cpu_only = True, e.g., meta data. - 2. cpu_only = False, stack = True, e.g., images tensors. - 3. cpu_only = False, stack = False, e.g., gt bboxes. - """ - - if not isinstance(batch, Sequence): - raise TypeError( - f'`batch` should be a sequence, but got {type(batch)}.') - - if isinstance(batch[0], DataContainer): - # TODO `DataContainer` will be supported in the future. - raise TypeError('DataContainer is not supported in ipu data loader.') - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - collated_batch = [] - for samples in transposed: - if not isinstance(samples[0], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch.append(collate(samples, samples_per_gpu)) - return collated_batch - elif isinstance(batch[0], Mapping): - collated_batch = {} - for key in batch[0]: - if not isinstance(batch[0][key], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch[key] = collate([d[key] for d in batch]) - return collated_batch - else: - return default_collate(batch) - - -class IPUDataLoader(poptorch.DataLoader): - """Thin wrapper of `torch.utils.data.DataLoader`. - - Compared with the pytorch DataLoder, this DataLoder changes the way of - calculation of batch size and adds the AsynchronousDataAccessor to - load and release data faster in cpu mode. - - If this data loader is used in a distributed execution environment, it will - ensure that each process uses a different subset of the dataset, providing - you first call ``options.randomSeed(N)`` with an integer N which is the - same across all hosts. - - Args: - dataset (torch.utils.data.Dataset): The dataset to get the data from. - options (poptorch.Options): Options that will be used to compile - and run the model. - batch_size (int, optional): This is the batch size in the conventional - sense of being the size that runs through an operation in the model - at any given time. - shuffle (bool, optional): set to ``True`` to have the data reshuffled - at every epoch (default: ``False``). - num_workers (int, optional): how many subprocesses to use for data - loading. ``0`` means that the data will be loaded in the main - process. (default: ``0``) - drop_last (bool, optional): If True and the number of elements in the - dataset is not a multiple of the combined batch size then the - incomplete batch at the end will be dropped. - persistent_workers (bool, optional): Re-use workers between - iterations if True. - auto_distributed_partitioning (bool, optional): If True, partitions the - dataset for distributed execution automatically. Otherwise, it is - assumed that partitioning has been handled manually. - mode (poptorch.DataLoaderMode, optional): If `DataLoaderMode.Async`, - uses an :py:class:`~poptorch.AsynchronousDataAccessor` to access - the dataset. If `DataLoaderMode.Sync`, accesses the dataset - synchronously. - async_options (Dict[str, Any], optional): Options to pass to - :py:class:`~poptorch.AsynchronousDataAccessor`. - rebatched_worker_size (int, optional): When using AsyncRebatched: batch - size of the tensors loaded by the workers. - Default to the combined batch size. - If specified the ``rebatched_worker_size`` must be less than - or equal to the combined batch size. - kwargs (Dict[str, Any], optional): Other options to pass to PyTorch's - ``DataLoader`` constructor. - """ - - def __init__(self, - dataset, - options, - batch_size=1, - shuffle=False, - num_workers=0, - drop_last=True, - persistent_workers=True, - auto_distributed_partitioning=True, - mode='sync', - async_options=None, - rebatched_worker_size=None, - **kwargs): - """Lazy init: - - In many frameworks, the dataloader will be constructed before the - initialization of the ipu options, so the lazy init method is used - here, and the real initialization will not be done until the dataloader - needs to be used and the options are input. - """ - # lazy init: sometimes, we cannot get IPU options when build data - # loader - self.kwargs = { - 'dataset': dataset, - 'batch_size': batch_size, - 'shuffle': shuffle, - 'num_workers': num_workers, - 'drop_last': drop_last, - 'persistent_workers': persistent_workers, - 'auto_distributed_partitioning': auto_distributed_partitioning, - 'mode': mode, - 'collate_fn': partial(collate, samples_per_gpu=batch_size), - 'async_options': async_options, - 'rebatched_worker_size': rebatched_worker_size, - **kwargs - } - self.dataset = dataset - self.initialized = False - if options: - self.init(options=options) - - def init(self, options, **kwargs): - if not self.initialized: - kwargs = {**self.kwargs, **kwargs, 'options': options} - if kwargs['mode'] == 'sync': - kwargs['mode'] = poptorch.DataLoaderMode.Sync - elif kwargs['mode'] == 'async': - kwargs['mode'] = poptorch.DataLoaderMode.AsyncRebatched - if kwargs['async_options'] is None: - kwargs['async_options'] = { - 'load_indefinitely': True, - 'buffer_size': 8 - } - if kwargs['rebatched_worker_size'] is None: - kwargs['rebatched_worker_size'] = 128 - super().__init__(**kwargs) - self.initialized = True - - return self diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/hierarchical_data_manager.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/hierarchical_data_manager.py deleted file mode 100755 index a6f3b3cd2..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/hierarchical_data_manager.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import numpy as np -import torch - -from mmcv.parallel import DataContainer - -# A customized None type for HierarchicalDataManager -HierarchicalDataNone = object() - - -class HierarchicalDataManager: - """A class manage all the tensors in the hierarchical data. - - At present, the input data structure accepted by IPU is limited, - when the input data structure of mmcv varies. - Here, an intermediate class is needed to get and update tensors - from the original data. - - HierarchicalDataManager will record a hierarchical input/output data in - self._hierarchical_data. For example, we have an input data: - {'img': tensorA, 'label': tensorB, 'img_metas': [tensorC, tensorD]} - To enable IPU to use the input, HierarchicalDataManager will collect - the torch tensors from self._hierarchical_data into a tuple like: - (tensorA, tensorB, tensorC, tensorD). - Meanwhile, the return of IPU is a tuple of tensors, HierarchicalDataManager - also have a function named update_all_tensors to update tensors in - self._hierarchical_data which is the output for upper calls. - - Args: - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - """ - - def __init__(self, logger=None): - self.atomic_types = (int, str, float, np.ndarray, type(None)) - self.warning = warnings.warn if logger is None else logger.warning - # enable or disable input data's shape and value check - self.quick_mode = False - self._hierarchical_data = None - - def quick(self): - self.quick_mode = True - - def compare_atomic_type(self, a, b): - """Compare data, supported datatypes are numpy array and python basic - types.""" - if isinstance(a, np.ndarray): - return np.all(a == b) - else: - return a == b - - def record_hierarchical_data(self, data): - """Record a hierarchical data.""" - if self._hierarchical_data is not None: - if isinstance(data, torch.Tensor): - assert isinstance(self._hierarchical_data, torch.Tensor), \ - 'original hierarchical data is not torch.tensor' - self._hierarchical_data = data - else: - self.update_hierarchical_data(data) - else: - self._hierarchical_data = data - - @property - def hierarchical_data(self): - return self._hierarchical_data - - def update_hierarchical_data(self, - dataA, - dataB=HierarchicalDataNone, - strict=True, - address='data'): - """Update dataB with dataA in-place. - - Args: - dataA (list or dict or tuple): New hierarchical data. - dataB (list or dict or tuple): hierarchical data to update. - if not specified, self.hierarchical_data will be updated then. - strict (bool, optional): If true, an error will be reported - when the following conditions occur: - 1. Non-torch.Tensor data changed. - 2. Torch.Tensor data shape changed. - address (str): Record the address of current data to be updated. - Default: 'data'. - """ - if dataB is HierarchicalDataNone: - dataB = self.hierarchical_data - - # Update with a da ta with the same structure - # but different values(tensors and basic python data types) - if isinstance(dataA, (tuple, list)): - for idx, node in enumerate(dataA): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(idx)}]' - assert isinstance(node, type(dataB[idx])),\ - f'data structure changed: {new_address}' - if isinstance(node, torch.Tensor): - dataB[idx] = node - else: - self.update_hierarchical_data( - node, dataB[idx], strict, address=new_address) - elif isinstance(dataA, dict): - for k, v in dataA.items(): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(k)}]' - assert isinstance(v, type(dataB[k])),\ - f'data structure changed: {new_address}' - if isinstance(v, torch.Tensor): - dataB[k] = v - else: - self.update_hierarchical_data( - v, dataB[k], strict, address=new_address) - elif isinstance(dataA, self.atomic_types): - if not self.quick_mode: - is_equal = self.compare_atomic_type(dataA, dataB) - if not is_equal: - if strict: - raise ValueError( - 'all data except torch.Tensor should be same, ' - f'but data({address}) is changed.') - else: - self.warning( - f'find a non-torch.Tensor data({type(dataA)}) ' - f'changed, and the address is {address}') - elif isinstance(dataA, DataContainer): - if not self.quick_mode: - assert isinstance(dataB, DataContainer) - new_address = address + '.data' - self.update_hierarchical_data( - dataA.data, dataB.data, False, address=new_address) - else: - raise NotImplementedError( - f'not supported datatype:{type(dataA)}, address is {address}') - - def collect_all_tensors(self, hierarchical_data=None): - """Collect torch.Tensor data from self.hierarchical_data to a list and - return.""" - # get a list of tensor from self._hierarchical_data - if hierarchical_data is None: - hierarchical_data = self._hierarchical_data - tensors = [] - if isinstance(hierarchical_data, torch.Tensor): - tensors = [hierarchical_data] - else: - self._collect_tensors(hierarchical_data, tensors) - return tensors - - def _collect_tensors(self, data, tensors): - if isinstance(data, (tuple, list)): - for node in data: - if isinstance(node, torch.Tensor): - tensors.append(node) - else: - self._collect_tensors(node, tensors) - elif isinstance(data, dict): - for v in data.values(): - if isinstance(v, torch.Tensor): - tensors.append(v) - else: - self._collect_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._collect_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def update_all_tensors(self, tensors): - """Put tensors from tuple back to self.hierarchical_data.""" - if isinstance(self._hierarchical_data, torch.Tensor): - print(tensors, len(tensors)) - assert len(tensors) == 1 - assert isinstance(tensors[0], torch.Tensor) - self._hierarchical_data = tensors[0] - else: - # convert to list if tensors is tuple - tensors = list(tensors) - self._set_tensors(self._hierarchical_data, tensors) - return self.hierarchical_data - - def _set_tensors(self, data, tensors): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = tensors.pop(0) - else: - self._set_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._set_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def clean_all_tensors(self): - """Delete tensors from self.hierarchical_data.""" - self._clean_tensors(self._hierarchical_data) - - def _clean_tensors(self, data): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = None - else: - self._clean_tensors(v) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._clean_tensors(data.data) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/hook_wrapper.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/hook_wrapper.py deleted file mode 100755 index 141afb86d..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/hook_wrapper.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import HOOKS, LrUpdaterHook, OptimizerHook -from mmcv.utils import TORCH_VERSION, digit_version - - -def wrap_lr_updater_hook(lr_hook_class): - """A wrapper function to wrap any subclass of LrUpdaterHook. - - IPU needs extra operations to upload optimizer settings. This wrapper will - override function(_set_lr) of a subclass of LrUpdaterHook. - """ - assert issubclass(lr_hook_class, LrUpdaterHook) - - class ipu_lr_hook_class(lr_hook_class): - - def _set_lr(self, runner, *args, **kwargs): - super()._set_lr(runner, *args, **kwargs) - # convert torch optimizer to poptorch optimizer - runner.model.setOptimizer(runner.optimizer) - - return ipu_lr_hook_class - - -def wrap_optimizer_hook(optimizer_hook_class): - """A wrapper function to wrap OptimizerHook. - - This is an non-intrusive implementation of wrapping optimizer hook (or you - need to change every config file to use IPU optimizer hook) IPU's clip-norm - implementation is different from pytorch, so there should be an error - raised when using clip-norm. - """ - - class ipu_optimizer_hook_class(OptimizerHook): - - def __init__(self, **kwargs): - super().__init__(**kwargs) - if self.grad_clip is not None: - raise NotImplementedError('IPU does not support gradient clip') - - return ipu_optimizer_hook_class - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class IPUFp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - assert grad_clip is None,\ - 'IPU mode does not support `grad_clip` currently' - assert coalesce,\ - 'implemented all reduce in distributed training currently' - assert bucket_size_mb == -1,\ - '`bucket_size_mb` should not be set in IPU mode' - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - raise NotImplementedError( - 'IPU mode does not support dynamic loss scale currently') - elif isinstance(loss_scale, float): - self.loss_scale = loss_scale - elif isinstance(loss_scale, dict): - raise NotImplementedError( - 'IPU mode supports single scale currently') - else: - raise ValueError( - f'loss_scale should be float, but got {loss_scale} ') - - def after_train_iter(self, runner): - pass - -else: - raise RuntimeError('The IPU mode only supports torch 1.6 and above') diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/model_wrapper.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/model_wrapper.py deleted file mode 100755 index c345537e2..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/model_wrapper.py +++ /dev/null @@ -1,721 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect -from collections import OrderedDict -from typing import Optional, Union - -import poptorch -import torch -import torch.nn as nn -from poptorch import PoplarExecutor, __version__, identity_loss -from poptorch._args_parser import ArgsParser - -from mmcv.runner import auto_fp16 -from .hierarchical_data_manager import HierarchicalDataManager -from .utils import compare_ndarray, model_sharding, recomputation_checkpoint - - -class DictArgsParser(ArgsParser): - """A helper class for handling model input. - - Args: - inputs (list): Inputs of model. - """ - - def __init__(self, inputs): - # Combine args and kwargs: - self._has_variadic_arguments = True - self._varnames = list(inputs.keys()) - self._defaults = [inspect.Parameter.empty for _ in self._varnames] - self._warned_not_contiguous_input = False - - -class WrappedNet(nn.Module): - """A net wrapper for model conversion. - - This wrapper will make some changes and add some extra functions to - training/inference model. - - Args: - model (:obj:`nn.Module`): The model to run. - inputs_manager (:obj:`HierarchicalDataManager`): A parser - converting inputs from tuple to dictionary. - outputs_manager (:obj:`HierarchicalDataManager`): A parser - converting outputs from dictionary to tuple. - inter_outputs_in_cpu (dict): Specify the features to be - recorded. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - model, - inputs_manager, - outputs_manager, - inter_outputs_in_cpu, - modules_to_record=None): - super().__init__() - self.model = model - self.inputs_manager = inputs_manager - self.outputs_manager = outputs_manager - self.training = model.training - # Register a hook function to capture the intermediate features - # generated by the network to align the outputs between ipu and cpu - # Used to confirm whether the implementation of CPU is consistent - # with the implementation of IPU - self.inter_outputs_in_cpu = inter_outputs_in_cpu - if modules_to_record is None: - modules_to_record = [] - - for idx, (name, module) in enumerate(model.named_modules()): - if name in modules_to_record or idx in modules_to_record: - features_hook = self.get_input_output_hook( - name, idx, self.inter_outputs_in_cpu) - module.register_forward_hook(hook=features_hook) - - def get_input_output_hook(self, name, idx, save_dict): - - def input_output_hook(module, fea_in, fea_out): - if isinstance(fea_in, tuple): - fea_in = list(fea_in) - if isinstance(fea_out, tuple): - fea_out = list(fea_out) - save_dict[name] = { - 'fea_in': fea_in, - 'fea_out': fea_out, - 'idx': idx - } - return None - - return input_output_hook - - def forward(self, inputs_tuple): - """This function is used to be compiled to ipu, the inputs and outputs - need to be tuples, so here we need to restore the input back to a - dictionary and convert the output to a tuple.""" - self.inputs_manager.update_all_tensors(inputs_tuple) - kwargs = {**(self.inputs_manager.hierarchical_data)} - if self.training: - outputs = self.forward_train(kwargs) - # tell poptorch which loss will be used finally - identity_loss(outputs['loss'], reduction='none') - else: - outputs = self.forward_eval(kwargs) - - if isinstance(outputs, torch.Tensor): - # currently not support single tensor output, - # need to wrap it with a dictionary, - # use a keyword to identify this case - outputs = {'output of WrappedNet: single tensor': outputs} - - # if there are some features need to be record, add extra outputs - for name in self.inter_outputs_in_cpu: - outputs[name] = self.inter_outputs_in_cpu[name] - - # record all the places of return tensors in the converting stage - # while in the real run stage, all the tensor are changed in-place - # that means the output can be obtained directly outside this function - self.outputs_manager.record_hierarchical_data(outputs) - plain_outputs = self.outputs_manager.collect_all_tensors() - return plain_outputs - - def forward_train(self, kwargs): - optimizer = kwargs.pop('optimizer') - outputs = self.train_step(kwargs, optimizer) - return outputs - - def train_step(self, data, optimizer=None, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating are also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer`, optional): The - optimizer of runner is passed to ``train_step()``. This - argument is unused and reserved. - - Returns: - dict: Dict of outputs. The following fields are contained. - - loss (torch.Tensor): A tensor for back propagation, which \ - can be a weighted sum of multiple losses. - - log_vars (dict): Dict contains all the variables to be sent \ - to the logger. - - num_samples (int): Indicates the batch size (when the model \ - is DDP, it means the batch size on each GPU), which is \ - used for averaging the logs. - """ - losses = self.model(**data) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, log_vars=log_vars, num_samples=len(data['img'].data)) - - return outputs - - def _parse_losses(self, losses): - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(loss.mean() for loss in loss_value) - elif isinstance(loss_value, dict): - for name, value in loss_value.items(): - log_vars[name] = value - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(value for key, value in log_vars.items() if 'loss' in key) - log_vars['loss'] = loss - - return loss, log_vars - - def forward_eval(self, kwargs): - img = kwargs.pop('img') - img_metas = kwargs.pop('img_metas', None) - return_loss = kwargs.pop('return_loss') - assert not return_loss - # TODO Temporarily hard-code to close post_process, - # otherwise, in the third trace(_check_trace), - # post_process will convert output tensor to numpy array automatically, - # resulting in _check_trace failure - outputs = self.model( - img, - img_metas=img_metas, - return_loss=return_loss, - post_process=False) - return outputs - - -class MMPoplarExecutor(PoplarExecutor): - """An executor for inputs/outputs parsing, model compilation, data - alignment and IPU upload/download. - - Args: - model (:obj:`nn.Module`): The model to be compiled. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - training (bool): Model in training mode or eval mode. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - args (argument list): Arguments passed to the `__init__` - method of PoplarExecutor. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of PoplarExecutor. - """ - - def __init__(self, - model, - logger=None, - training=True, - modules_to_record=None, - *args, - **kwargs): - # self.model == self._user_model: input pytorch model - # self._model: wrapped model which is used to compile - # and update weights, these two models use same weights - # wrapped model only accept and output tuple, so - # HierarchicalDataManager will convert dictionary - # to tuple and convert them back - self.inputs_manager = HierarchicalDataManager(logger=logger) - self.outputs_manager = HierarchicalDataManager(logger=logger) - self.logger = logger - # the features calculated by CPU - self.inter_outputs_in_cpu = {} - # the features calculated by IPU - self.inter_outputs_in_ipu = {} - if modules_to_record is None: - # It is possible that the IPU implementation of some operators - # is inconsistent with the expected (CPU), here you can use - # this method to confirm whether there is a problem - self.compare_with_cpu = False - else: - self.compare_with_cpu = True - # move model.fp16_enabled to self.fp16_enabled, - # modify the position where the input is automatically casted to half - if getattr(model, 'fp16_enabled', False): - model.fp16_enabled = False - self.fp16_enabled = True - # make torch.jit.trace convert self._model - model = WrappedNet( - model, - self.inputs_manager, - self.outputs_manager, - self.inter_outputs_in_cpu, - modules_to_record=modules_to_record) - super().__init__(model, training=training, *args, **kwargs) - # overwrite self._args_parser in train_step or val_step - self._args_parser = None - if training: - assert self.training - else: - assert not self.training - - @property - def training(self): - # If trying to get the attribute(training) of self, - # since the class has no training attribute, - # it will automatically look for the training attribute of self.model. - # However, the real attribute we want to check is self._training, - # self.model.training and self._training are often inconsistent. - # It is not clear whether it is a Poptorch bug or a special design, - # temporarily use this function to fix the problem - return self._training # comes from self.model._training - - @auto_fp16(supported_types=(PoplarExecutor, )) - def run_model(self, data_dict): - # this function is used to parse input_dict - # and convert to output_dict - if self.isCompiled(): - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - else: - # get tensors out of data and put them in a tuple - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - # turn logger in data manager off after compilation - self.inputs_manager.quick() - self.outputs_manager.quick() - - # parser args in the first iter - if self._args_parser is None: - self._args_parser = DictArgsParser({'args': inputs_tuple}) - - # run or convert model - # the plain_outputs will be used in converting stage - plain_outputs = self(inputs_tuple) - - self.inputs_manager.clean_all_tensors() - - # put list of tensors back to the output dict - # according to the same order - self.outputs_manager.update_all_tensors(plain_outputs) - # get the real output dictionary from self.outputs_manager - output_dict = self.outputs_manager.hierarchical_data - - # split output_dict into inter_outputs_in_ipu - # and output of the torch model - torch_model_output = {} - for name in output_dict: - if name in self.inter_outputs_in_cpu: - self.inter_outputs_in_ipu[name] = output_dict[name] - else: - torch_model_output[name] = output_dict[name] - - if 'output of WrappedNet: single tensor' in output_dict: - assert len(torch_model_output) == 1 - assert isinstance( - torch_model_output['output of WrappedNet: single tensor'], - torch.Tensor) - torch_model_output = \ - torch_model_output['output of WrappedNet: single tensor'] - - return torch_model_output - - def train_step(self, data, optimizer=None, **kwargs): - # arguments from mmcls/models/classifiers/base.py: - # BaseClassifier.train_step - assert self.training - assert len(kwargs) == 0 # TODO, support later if necessary - - # TODO support datacontainer as input - # currently, auto_fp16 and HierarchicalDataManager take too much - # time on traversing datacontainer - data['img_metas'] = None - num_samples = len(data['img'].data) - - # TODO we will ignore optimizer because it will not be used in model, - # support later if necessary - data['optimizer'] = None - output_dict = self.run_model(data) - - # outputs contained loss, log_vars, num_samples, - # only loss(torch.tensor) has been updated - # remove all unchanged vars, left torch.tensor - neat_output_dict = {'loss': output_dict['loss']} - - # re-parse outputs, get back log_vars and num_samples - loss, log_vars = self.model._parse_losses(neat_output_dict) - final_output_dict = dict( - loss=loss, log_vars=log_vars, num_samples=num_samples) - return final_output_dict - - def eval_call(self, img, img_metas=None, return_loss=True, **kwargs): - # arguments from mmdet/models/detectors/base.py:BaseDetector.forward - # tmp usssage for eval mode - assert not self.training - assert len(kwargs) == 0 # TODO, support later if necessary - assert not return_loss - data = {'img': img, 'img_metas': img_metas, 'return_loss': return_loss} - - output_dict = self.run_model(data) - - return output_dict - - def detachFromDevice(self): - if self.isCompiled() and self._is_attached: - super().detachFromDevice() - - def attachToDevice(self): - if self.isCompiled() and not self._is_attached: - super().attachToDevice() - - -class TrainEvalModel: - """A class maintaining training MMPoplarExecutor and inference - MMPoplarExecutor. - - Args: - train_model (:obj:`nn.Module`): The training model to be compiled. - ``train_model`` can be None if only executing validation. - eval_model (:obj:`nn.Module`): The inference model to be compiled. - options (mmcv.Config, dict): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - train_model, - eval_model, - options, - optimizer, - modules_to_record=None, - logger=None): - if train_model is None: - self._train_executor = None - self.training = False - else: - self._train_executor = get_training_model( - train_model, - options=options['training'], - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - self.training = True - self._eval_executor = get_inference_model( - eval_model, options=options['inference'], logger=logger) - - @property - def executor(self): - if self.training: - return self._train_executor - else: - return self._eval_executor - - def train(self, mode: bool = True): - """Sets the module in training mode. - - This has any effect only on certain modules. See documentations of - particular modules for details of their behaviors in - training/evaluation mode, if they are affected, - e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - Args: - mode (bool): whether to set training mode (``True``) or evaluation - mode (``False``). Default: ``True``. - - Returns: - Module: self - """ - if not isinstance(mode, bool): - raise ValueError('training mode is expected to be boolean, ' - f'but got {type(mode)}') - if self._train_executor is None and mode: - raise RuntimeError( - 'The train_executor is not initialized.' - 'If you want to initialize train_executor,' - 'you need to input optimizer when converting pytorch model') - - if mode == self.training: - self.model.train(mode) - return self - else: - if self.isCompiled(): - # copy weights from IPU to cpu before off-load current session - self.copyWeightsToHost() - # detach the current session before change the mode, - # if is training mode and weights are updated, - # poptorch will copy weights from IPU to host - self.detachFromDevice() - - self.training = mode # session will changed with mode changing - self.model.train(mode) - - # after changing mode, attach the current new session, - # and this function will copy weights of model to device - self.attachToDevice() - return self - - def eval(self): - """Sets the module in evaluation mode. - - This has any effect only on certain modules. - See documentations of particular modules - for details of their behaviors in training/evaluation mode, - if they are affected, e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - This is equivalent with :meth:`self.train(False) - `. - - See :ref:`locally-disable-grad-doc` for a comparison between - `.eval()` and several similar mechanisms that may be confused with it. - - Returns: - Module: self - """ - return self.train(False) - - def compare_data_between_ipu_and_cpu(self, inter_outputs_in_cpu, - inter_outputs_in_ipu): - for key, val in inter_outputs_in_cpu.items(): - is_tensor = isinstance(val['fea_in'], torch.Tensor) - fea_in_cpu = val['fea_in'] - fea_in_cpu_list = [fea_in_cpu] if is_tensor else fea_in_cpu - fea_in_ipu = inter_outputs_in_ipu[key]['fea_in'] - fea_in_ipu_list = [fea_in_ipu] if is_tensor else fea_in_ipu - - is_tensor = isinstance(val['fea_out'], torch.Tensor) - fea_out_cpu = val['fea_out'] - fea_out_cpu_list = [fea_out_cpu] if is_tensor else fea_out_cpu - fea_out_ipu = inter_outputs_in_ipu[key]['fea_out'] - fea_out_ipu_list = [fea_out_ipu] if is_tensor else fea_out_ipu - - print('comparing layer:', key) - for idx, (featA, featB) in \ - enumerate(zip(fea_in_cpu_list, fea_in_ipu_list)): - print('fea_in, tensor ', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - for idx, (featA, featB) in \ - enumerate(zip(fea_out_cpu_list, fea_out_ipu_list)): - print('fea_out, tensor', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def train_step(self, data, optimizer=None, **kwargs): - assert self.training, 'not supported train_step on eval mode' - inter_outputs_in_cpu = {} - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu): - self.copyWeightsToHost() - # run in CPU mode - self._train_executor.model.train_step(data, optimizer, **kwargs) - inter_outputs_in_cpu = { - **(self._train_executor.inter_outputs_in_cpu) - } - # run in IPU mode - result = self._train_executor.train_step(data, optimizer, **kwargs) - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu - and len(inter_outputs_in_cpu) > 0): - self.compare_data_between_ipu_and_cpu( - inter_outputs_in_cpu, - self._train_executor.inter_outputs_in_ipu) - return result - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def __call__(self, *args, **kwargs): - if self.training: - raise NotImplementedError('use train_step rather than __call__') - else: - return self._eval_executor.eval_call(*args, **kwargs) - - def __getattr__(self, attr): - return getattr(self.executor, attr) - - -def get_training_model(model: nn.Module, - options: Optional[poptorch.Options] = None, - optimizer: Optional[torch.optim.Optimizer] = None, - logger=None, - modules_to_record=None) -> poptorch.PoplarExecutor: - """Create a PopTorch training model from a PyTorch model, running on IPU - hardware in training mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned training model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.train()`` on the original model, which - changes the ``training`` bool of the model instance, will not alter the - model returned by this function. You may need to call ``model.train()`` - on your model before you call this function for correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): The optimizers - to apply during training. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place - of ``model``. - """ - # Create a copy of the original model in case it needs to be wrapped - maybe_wrapped_model = copy.copy(model) - - return MMPoplarExecutor( - model=maybe_wrapped_model, - logger=logger, - options=options, - training=True, - optimizer=optimizer, - user_model=model, - modules_to_record=modules_to_record, - poptorch_version=__version__) - - -def get_inference_model(model: Union[nn.Module, poptorch.PoplarExecutor], - options: Optional[poptorch.Options] = None, - logger=None) -> poptorch.PoplarExecutor: - """Create a PopTorch inference model from a PyTorch model, running on IPU - hardware in inference mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned inference model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.eval()`` on the original model will not alter - the model returned by this function. You may need to call - ``model.eval()`` on your model before you call this function for - correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place of - ``model``. - """ - - return MMPoplarExecutor( - model=copy.copy(model), - logger=logger, - options=options, - training=False, - poptorch_version=__version__) - - -def ipu_model_wrapper(model, - options, - optimizer=None, - logger=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None): - """Convert torch model to IPU model. - - Args: - model (nn.Module): The target model to be converted. - options (dict[str, poptorch.Options]): IPU options, generated - by :func:`cfg2options`. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during training. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (dict): A dictionary contains train_split_edges and - train_ckpt_nodes, See details in :func:`model_sharding` and - :func:`recomputation_checkpoint` functions. - fp16_cfg (dict): Config for IPU fp16 training. Currently supports - configs: `loss_scale`, `velocity_accum_type` and `accum_type`. - See details in - https://docs.graphcore.ai/projects/poptorch-user-guide/en/latest/index.html - - Returns: - TrainEvalModel: IPU wrapped model. - """ - if ipu_model_cfg is None: - ipu_model_cfg = {} - training = model.training if optimizer is not None else False - # set mixed-precision - if fp16_cfg is not None: - from mmcv.runner import wrap_fp16_model - loss_scale = fp16_cfg['loss_scale'] - wrap_fp16_model(model) - model.half() - # TODO tmp ussage to set loss scaling for torch original optimizer - if optimizer is not None: - optimizer.loss_scaling = loss_scale - if fp16_cfg.get('velocity_accum_type', False): - if fp16_cfg['velocity_accum_type'] == 'half': - optimizer.velocity_accum_type = torch.half - else: - optimizer.velocity_accum_type = torch.float32 - if fp16_cfg.get('accum_type', False): - if fp16_cfg['accum_type'] == 'half': - optimizer.accum_type = torch.half - else: - optimizer.accum_type = torch.float32 - # TODO support feature alignment for fp16 - if modules_to_record is not None: - raise NotImplementedError( - 'Feature alignment for fp16 is not implemented') - - # set model partition - if optimizer is None: - train_model = None - else: - # split model into multi-IPUs if specified - train_model = model_sharding( - copy.copy(model).train(), - ipu_model_cfg.get('train_split_edges', [])) - - recomputation_checkpoint(train_model, - ipu_model_cfg.get('train_ckpt_nodes', [])) - - # TODO support feature alignment for gradient accumulation mode - gradient_accumulation = \ - getattr(options['training'].Training, 'gradient_accumulation', 1) - if gradient_accumulation > 1: - assert modules_to_record is None, \ - 'Feature alignment for grad-accumulation mode not implemented' - - # TODO support feature alignment for multi-replica mode - replication_factor = \ - getattr(options['training'], 'replication_factor', 1) - if replication_factor > 1: - assert modules_to_record is None, \ - 'Feature alignment for multi-replica mode not implemented' - - # TODO supports different model partitions between train and eval mode - assert len(ipu_model_cfg.get('eval_split_edges', [])) == 0,\ - 'Currently, BeginBlock can only be used once on the same model' - eval_model = copy.copy(model).eval() - - # wrap model for compilation - model = TrainEvalModel( - train_model, - eval_model, - options=options, - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - model.train(training) - return model diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/runner.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/runner.py deleted file mode 100755 index e2d492267..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/runner.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.runner import (HOOKS, RUNNERS, BaseRunner, EpochBasedRunner, - IterBasedRunner) -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import (IPUFp16OptimizerHook, wrap_lr_updater_hook, - wrap_optimizer_hook) - from .model_wrapper import ipu_model_wrapper - from .utils import build_from_cfg_with_wrapper, cfg2options - - -class IPUBaseRunner(BaseRunner): - """A base runner for IPU. - - This runner has some extra processes for IPU which are shown below: - - 1. Parse options for IPU - 2. wrap pytorch model for IPU - 3. Raise errors while encountering illegal usage - 4. Input IPU options and initialize dataloader if finding an instance - of IPUDataLoader - - Args: - model (:obj:`nn.Module`): The model to run. - options_cfg (mmcv.Config, dict): Options that will be used to compile - and run the model. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (mmcv.Config, dict): Config of model partition and - recomputing checkpoint - fp16_cfg (mmcv.Config): Config for fp16 training. - batch_processor (callable): A callable method that process a data - batch. Should be None for IPU runner - kwargs (Dict[str, Any], optional): Keyword arguments will be passed to - ``base_runner.BaseRunner``. - """ - - def __init__(self, - model, - options_cfg=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None, - batch_processor=None, - **kwargs): - assert hasattr(model, 'train_step') and batch_processor is None,\ - 'only support model with train_step' - - if options_cfg is None: - options_cfg = {} - # call BaseRunner.__init__() here - super().__init__(model, **kwargs) - - # process options of ipu - if IS_IPU_AVAILABLE: - self.options = cfg2options(options_cfg) - self.model = ipu_model_wrapper( - self.model, - self.options, - self.optimizer, - self.logger, - modules_to_record=modules_to_record, - ipu_model_cfg=ipu_model_cfg, - fp16_cfg=fp16_cfg) - else: - raise NotImplementedError('cpu mode on IPURunner is not supported') - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - assert isinstance(lr_config, dict) - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, - # e.g., 'cyclic', then its first letter will be capitalized, - # e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, the string will not be changed - # if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = build_from_cfg_with_wrapper(lr_config, HOOKS, - wrap_lr_updater_hook) - self.register_hook(hook, priority='VERY_HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - assert isinstance(optimizer_config, (dict, IPUFp16OptimizerHook)) - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = build_from_cfg_with_wrapper(optimizer_config, HOOKS, - wrap_optimizer_hook) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def run(self, data_loaders, workflow, *args, **kwargs): - for i, flow in enumerate(workflow): - mode, _ = flow - # initialize IPU dataloader if not initialized - assert isinstance(data_loaders[i], IPUDataLoader),\ - 'IPU runner can only work with `IPUDataLoader`' - data_loaders[i].init(options=self.get_options(mode)) - - super().run(data_loaders, workflow, *args, **kwargs) - - def get_options(self, mode): - if mode == 'train': - return self.options['training'] - elif mode == 'val': - return self.options['inference'] - else: - raise ValueError(f'mode should be train or val but got {mode}') - - -@RUNNERS.register_module() -class IPUEpochBasedRunner(IPUBaseRunner, EpochBasedRunner): - """Epoch-based Runner for IPU. - - The Inheritance order(MRO) is: IPUEpochBasedRunner -> IPUBaseRunner -> - EpochBasedRunner -> BaseRunner This runner train models epoch by epoch. - """ - pass - - -@RUNNERS.register_module() -class IPUIterBasedRunner(IPUBaseRunner, IterBasedRunner): - """Iteration-based Runner for IPU. - - The Inheritance order(MRO) is: IPUIterBasedRunner -> IPUBaseRunner -> - IterBasedRunner -> BaseRunner This runner train models iteration by - iteration. - """ - pass diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/utils.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/utils.py deleted file mode 100755 index 79709db1e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/ipu/utils.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import numpy as np -import popart -import poptorch -import torch -import torch.nn as nn - -from mmcv.utils import Registry - - -def _options_assigner(cfg, options_node): - # set popart.options by config - # cfg: dict, python data type - # options_node: python module or function - if isinstance(cfg, dict): - for key in cfg: - _options_assigner(cfg[key], getattr(options_node, key)) - elif isinstance(cfg, (int, float, str, list)): - if callable(options_node): - options_node(cfg) - else: - error_msg = f'options_node type {type(options_node)} not supported' - raise NotImplementedError(error_msg) - else: - error_msg = f'cfg type {type(cfg)} not supported' - raise NotImplementedError(error_msg) - - -def cfg2options(cfg): - """Parse dictionary to ipu options. - - Args: - cfg (dict): A dictionary of ipu settings. - - Returns: - dict[str, poptorch.Options]: Training options and inference options - of IPU. - """ - # set ipu options for inference and training by config - train_cfg = cfg.pop('train_cfg', {}) - eval_cfg = cfg.pop('eval_cfg', {}) - eval_cfg['replicationFactor'] = 1 # eval mode only use one replica - eval_cfg['executionStrategy'] = 'ShardedExecution' - # overwrite default ipu cfg with specified train cfgs - training_ipu_cfg = {**cfg, **train_cfg} - # overwrite default ipu cfg with specified eval cfgs - inference_ipu_cfg = {**cfg, **eval_cfg} - - ipu_options = { - 'training': _cast_to_options(training_ipu_cfg), - 'inference': _cast_to_options(inference_ipu_cfg) - } - - # TODO configure these codes - ipu_options['training']._Popart.set('disableGradAccumulationTensorStreams', - True) - ipu_options['training']._Popart.set( - 'accumulateOuterFragmentSettings.schedule', - int(popart.AccumulateOuterFragmentSchedule.OverlapMemoryOptimized)) - ipu_options['training'].Precision.enableStochasticRounding(True) - - return ipu_options - - -def _cast_to_options(cfg): - # If it cannot be directly assigned, use if statement to parse it, - # and if it can be directly assigned, use _options_assigner to assign - options = poptorch.Options() - - if 'availableMemoryProportion' in cfg: - available_memory_proportion = cfg.pop('availableMemoryProportion') - mem_props = {} - for i, mem_prop in enumerate(available_memory_proportion): - mem_props[f'IPU{i}'] = mem_prop - options.setAvailableMemoryProportion(mem_props) - - if 'executionStrategy' in cfg: - execution_strategy = cfg.pop('executionStrategy') - if execution_strategy == 'SameAsIpu': - options.setExecutionStrategy( - poptorch.PipelinedExecution( - getattr(poptorch.AutoStage, execution_strategy))) - elif execution_strategy == 'ShardedExecution': - options.setExecutionStrategy(poptorch.ShardedExecution()) - else: - raise NotImplementedError( - 'executionStrategy should be "SameAsIpu" or "ShardedExecution"' - f', but got {execution_strategy}') - - if 'partialsType' in cfg: - partials_type = cfg.pop('partialsType') - options.Precision.setPartialsType(getattr( - torch, partials_type)) # half or float - - _options_assigner(cfg, options) - return options - - -def model_sharding(model, split_edges): - """split models in-place into multi-IPUs. - - Args: - model (nn.Module): The target model to be split. - split_edges (list of dict): Model layer names or layer numbers - of split edge. Each item of ``split_edges`` is a dictionary, - which may contain the following key-pairs: - - - layer_to_call: PyTorch module to assign to the block - - user_id (optional): A user defined identifier for the block. - - ipu_id: The id of the IPU to run on. - - Examples: - >>> split_edges = [ - ... dict(layer_to_call='model.conv1', ipu_id=0), - ... dict(layer_to_call='model.conv3', ipu_id=1)] - >>> sharding_model = model_sharding(torch_model, split_edges) - - Returns: - nn.Module: Split model. - """ - if len(split_edges) == 0: - return model - assert isinstance(split_edges, list) - spilt_edges_dict = {edge['layer_to_call']: edge for edge in split_edges} - - for idx, (name, module) in enumerate(model.named_modules()): - if idx in spilt_edges_dict and name in spilt_edges_dict: - raise ValueError( - 'The same layer is referenced twice while doing model' - f' partition: idx is {idx} and name is {name}') - - edge = spilt_edges_dict.pop(name, None) - edge = spilt_edges_dict.pop(idx, edge) - if edge is not None: - poptorch.BeginBlock(module, edge.get('user_id', name), - edge['ipu_id']) - - # ensure all split_edges are used - if len(spilt_edges_dict) > 0: - split_edge_names = list(spilt_edges_dict.keys()) - raise RuntimeError( - f'split_edges: {split_edge_names} are not contained in the model') - return model - - -def recomputation_checkpoint(model: nn.Module, module_names: list): - """Annotates the output of a module to be checkpointed instead of - recomputed. - - If recomputation mode is enabled, ipu will release the activations of - the middle layers to save memory. During the backward of gradient, - the activation of the middle layer will be recalculated again. - This function is used to declare the activations of some intermediate - layers that need to be saved in order to skip the recomputation of - some layers. - - Args: - model (nn.Module): The target model to apply recomputation - checkpoint. - module_names (list): Layer names of module. - """ - - def recompute_outputs(module, inputs, outputs): - if isinstance(outputs, tuple): - return tuple(poptorch.recomputationCheckpoint(y) for y in outputs) - else: - return poptorch.recomputationCheckpoint(outputs) - - for name, module in model.named_modules(): - if name in module_names: - module.register_forward_hook(recompute_outputs) - module_names.remove(name) - - # check all module_names are used - assert len(module_names) == 0,\ - f'recomputed nodes: {module_names} are not contained in the model' - - -def compare_ndarray(featA, featB, rtol=1e-3, atol=1e-5): - """Align data between two activations or weights.""" - try: - np.testing.assert_allclose(featA, featB, rtol=rtol, atol=atol) - except AssertionError as e: - print(e) - - -def build_from_cfg_with_wrapper(cfg, - registry, - wrapper_func=None, - default_args=None): - """Build a module from config dict and wrap module with "wrapper_func". - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - wrapper_func (function): Used to wrap class - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - - if wrapper_func is None: - wrapped_obj_cls = obj_cls - else: - wrapped_obj_cls = wrapper_func(obj_cls) - try: - return wrapped_obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{wrapped_obj_cls.__name__}: {e}') diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/__init__.py deleted file mode 100644 index 572c4da7e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .data_parallel import MLUDataParallel -from .distributed import MLUDistributedDataParallel -from .scatter_gather import scatter, scatter_kwargs - -__all__ = [ - 'MLUDataParallel', 'MLUDistributedDataParallel', 'scatter', - 'scatter_kwargs' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/_functions.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/_functions.py deleted file mode 100644 index 7c35e65a2..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/_functions.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def scatter(input, devices): - """scatter copies tensor to MLU directly.""" - if isinstance(input, list): - outputs = [scatter(_input, devices) for _input in input] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - return output.to('mlu') if devices != [-1] else output - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_mlus, input): - outputs = scatter(input, target_mlus) - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/data_parallel.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/data_parallel.py deleted file mode 100644 index b2d09d0b0..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/data_parallel.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -import torch - -from mmcv.parallel import MMDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDataParallel(MMDataParallel): - """The MLUDataParallel module that supports DataContainer. - - MLUDataParallel is a class inherited from MMDataParall, which supports - MLU training and inference only. - - The main differences with MMDataParallel: - - - It only supports single-card of MLU, and only use first card to - run training and inference. - - - It uses direct host-to-device copy instead of stream-background - scatter. - - .. warning:: - MLUDataParallel only supports single MLU training, if you need to - train with multiple MLUs, please use MLUDistributedDataParallel - instead. If you have multiple MLUs, you can set the environment - variable ``MLU_VISIBLE_DEVICES=0`` (or any other card number(s)) - to specify the running device. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MLUDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.device_ids = [0] - self.src_device_obj = torch.device('mlu:0') - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/distributed.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/distributed.py deleted file mode 100644 index 3768c754c..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/distributed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.parallel import MMDistributedDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDistributedDataParallel(MMDistributedDataParallel): - """The DDP module supports DataContainer. - - MLUDDP has one difference from MMDDP which moves data to MLU with coping - instead of scattering. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/scatter_gather.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/scatter_gather.py deleted file mode 100644 index 0b0c9b96f..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/device/mlu/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmcv.parallel.data_container import DataContainer -from ._functions import Scatter - - -def scatter(inputs, target_mlus, dim=0): - """Scatter inputs to target mlu. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_mlus != [-1]: - obj = obj.to('mlu') - return [obj] - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_mlus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_mlus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_mlus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_mlus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_mlus, dim) if inputs else [] - kwargs = scatter(kwargs, target_mlus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/engine/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/engine/__init__.py deleted file mode 100644 index 3193b7f66..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, - single_gpu_test) - -__all__ = [ - 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', - 'single_gpu_test' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/engine/test.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/engine/test.py deleted file mode 100644 index f236b1cda..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/engine/test.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, data_loader): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - # Assume result has the same length of batch_size - # refer to https://github.com/open-mmlab/mmcv/issues/985 - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting - ``gpu_collect=True``, it encodes results to gpu tensors and use gpu - communication for results collection. On cpu mode it saves the results on - different gpus to ``tmpdir`` and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - if rank == 0: - batch_size = len(result) - batch_size_all = batch_size * world_size - if batch_size_all + prog_bar.completed > len(dataset): - batch_size_all = len(dataset) - prog_bar.completed - for _ in range(batch_size_all): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results under cpu mode. - - On cpu mode, this function will save the results on different gpus to - ``tmpdir`` and collect them by the rank 0 worker. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - tmpdir (str | None): temporal directory for collected results to - store. If set to None, it will create a random temporal directory - for it. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_result = mmcv.load(part_file) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results under gpu mode. - - On gpu mode, this function will encode results to gpu tensors and use gpu - communication for results collection. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/__init__.py deleted file mode 100644 index 2051b85f7..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .file_client import BaseStorageBackend, FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler -from .io import dump, load, register_handler -from .parse import dict_from_file, list_from_file - -__all__ = [ - 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', - 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', - 'list_from_file', 'dict_from_file' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/file_client.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/file_client.py deleted file mode 100644 index e7fd7cdfa..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/file_client.py +++ /dev/null @@ -1,1163 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import os -import os.path as osp -import re -import tempfile -import warnings -from abc import ABCMeta, abstractmethod -from contextlib import contextmanager -from pathlib import Path -from typing import Iterable, Iterator, Optional, Tuple, Union -from urllib.request import urlopen - -import mmcv -from mmcv.utils.misc import has_method -from mmcv.utils.path import is_filepath - - -class BaseStorageBackend(metaclass=ABCMeta): - """Abstract class of storage backends. - - All backends need to implement two apis: ``get()`` and ``get_text()``. - ``get()`` reads the file as a byte stream and ``get_text()`` reads the file - as texts. - """ - - # a flag to indicate whether the backend can create a symlink for a file - _allow_symlink = False - - @property - def name(self): - return self.__class__.__name__ - - @property - def allow_symlink(self): - return self._allow_symlink - - @abstractmethod - def get(self, filepath): - pass - - @abstractmethod - def get_text(self, filepath): - pass - - -class CephBackend(BaseStorageBackend): - """Ceph storage backend (for internal use). - - Args: - path_mapping (dict|None): path mapping dict from local path to Petrel - path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` - will be replaced by ``dst``. Default: None. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - """ - - def __init__(self, path_mapping=None): - try: - import ceph - except ImportError: - raise ImportError('Please install ceph to enable CephBackend.') - - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - self._client = ceph.S3Client() - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def get(self, filepath): - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class PetrelBackend(BaseStorageBackend): - """Petrel storage backend (for internal use). - - PetrelBackend supports reading and writing data to multiple clusters. - If the file path contains the cluster name, PetrelBackend will read data - from specified cluster or write data to it. Otherwise, PetrelBackend will - access the default cluster. - - Args: - path_mapping (dict, optional): Path mapping dict from local path to - Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in - ``filepath`` will be replaced by ``dst``. Default: None. - enable_mc (bool, optional): Whether to enable memcached support. - Default: True. - - Examples: - >>> filepath1 = 's3://path/of/file' - >>> filepath2 = 'cluster-name:s3://path/of/file' - >>> client = PetrelBackend() - >>> client.get(filepath1) # get data from default cluster - >>> client.get(filepath2) # get data from 'cluster-name' cluster - """ - - def __init__(self, - path_mapping: Optional[dict] = None, - enable_mc: bool = True): - try: - from petrel_client import client - except ImportError: - raise ImportError('Please install petrel_client to enable ' - 'PetrelBackend.') - - self._client = client.Client(enable_mc=enable_mc) - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def _map_path(self, filepath: Union[str, Path]) -> str: - """Map ``filepath`` to a string path whose prefix will be replaced by - :attr:`self.path_mapping`. - - Args: - filepath (str): Path to be mapped. - """ - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - return filepath - - def _format_path(self, filepath: str) -> str: - """Convert a ``filepath`` to standard format of petrel oss. - - If the ``filepath`` is concatenated by ``os.path.join``, in a Windows - environment, the ``filepath`` will be the format of - 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the - above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. - - Args: - filepath (str): Path to be formatted. - """ - return re.sub(r'\\+', '/', filepath) - - def get(self, filepath: Union[str, Path]) -> memoryview: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - memoryview: A memory view of expected bytes object to avoid - copying. The memoryview object can be converted to bytes by - ``value_buf.tobytes()``. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return str(self.get(filepath), encoding=encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Save data to a given ``filepath``. - - Args: - obj (bytes): Data to be saved. - filepath (str or Path): Path to write data. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.put(filepath, obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Save data to a given ``filepath``. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to encode the ``obj``. - Default: 'utf-8'. - """ - self.put(bytes(obj, encoding=encoding), filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - if not has_method(self._client, 'delete'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `delete` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.delete(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - if not (has_method(self._client, 'contains') - and has_method(self._client, 'isdir')): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` and `isdir` methods, please use a higher' - 'version or dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) or self._client.isdir(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - if not has_method(self._client, 'isdir'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `isdir` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - if not has_method(self._client, 'contains'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` method, please use a higher version or ' - 'dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result after concatenation. - """ - filepath = self._format_path(self._map_path(filepath)) - if filepath.endswith('/'): - filepath = filepath[:-1] - formatted_paths = [filepath] - for path in filepaths: - formatted_paths.append(self._format_path(self._map_path(path))) - return '/'.join(formatted_paths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download a file from ``filepath`` and return a temporary path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str | Path): Download a file from ``filepath``. - - Examples: - >>> client = PetrelBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('s3://path/of/your/file') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one temporary path. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - assert self.isfile(filepath) - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - Petrel has no concept of directories but it simulates the directory - hierarchy in the filesystem through public prefixes. In addition, - if the returned path ends with '/', it means the path is a public - prefix which is a logical directory. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - In addition, the returned path of directory will not contains the - suffix '/' which is consistent with other backends. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if not has_method(self._client, 'list'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `list` method, please use a higher version or dev' - ' branch instead.')) - - dir_path = self._map_path(dir_path) - dir_path = self._format_path(dir_path) - if list_dir and suffix is not None: - raise TypeError( - '`list_dir` should be False when `suffix` is not None') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - # Petrel's simulated directory hierarchy assumes that directory paths - # should end with `/` - if not dir_path.endswith('/'): - dir_path += '/' - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for path in self._client.list(dir_path): - # the `self.isdir` is not used here to determine whether path - # is a directory, because `self.isdir` relies on - # `self._client.list` - if path.endswith('/'): # a directory path - next_dir_path = self.join_path(dir_path, path) - if list_dir: - # get the relative path and exclude the last - # character '/' - rel_dir = next_dir_path[len(root):-1] - yield rel_dir - if recursive: - yield from _list_dir_or_file(next_dir_path, list_dir, - list_file, suffix, - recursive) - else: # a file path - absolute_path = self.join_path(dir_path, path) - rel_path = absolute_path[len(root):] - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class MemcachedBackend(BaseStorageBackend): - """Memcached storage backend. - - Attributes: - server_list_cfg (str): Config file for memcached server list. - client_cfg (str): Config file for memcached client. - sys_path (str | None): Additional path to be appended to `sys.path`. - Default: None. - """ - - def __init__(self, server_list_cfg, client_cfg, sys_path=None): - if sys_path is not None: - import sys - sys.path.append(sys_path) - try: - import mc - except ImportError: - raise ImportError( - 'Please install memcached to enable MemcachedBackend.') - - self.server_list_cfg = server_list_cfg - self.client_cfg = client_cfg - self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, - self.client_cfg) - # mc.pyvector servers as a point which points to a memory cache - self._mc_buffer = mc.pyvector() - - def get(self, filepath): - filepath = str(filepath) - import mc - self._client.Get(filepath, self._mc_buffer) - value_buf = mc.ConvertBuffer(self._mc_buffer) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class LmdbBackend(BaseStorageBackend): - """Lmdb storage backend. - - Args: - db_path (str): Lmdb database path. - readonly (bool, optional): Lmdb environment parameter. If True, - disallow any write operations. Default: True. - lock (bool, optional): Lmdb environment parameter. If False, when - concurrent access occurs, do not lock the database. Default: False. - readahead (bool, optional): Lmdb environment parameter. If False, - disable the OS filesystem readahead mechanism, which may improve - random read performance when a database is larger than RAM. - Default: False. - - Attributes: - db_path (str): Lmdb database path. - """ - - def __init__(self, - db_path, - readonly=True, - lock=False, - readahead=False, - **kwargs): - try: - import lmdb # NOQA - except ImportError: - raise ImportError('Please install lmdb to enable LmdbBackend.') - - self.db_path = str(db_path) - self.readonly = readonly - self.lock = lock - self.readahead = readahead - self.kwargs = kwargs - self._client = None - - def get(self, filepath): - """Get values according to the filepath. - - Args: - filepath (str | obj:`Path`): Here, filepath is the lmdb key. - """ - if self._client is None: - self._client = self._get_client() - - with self._client.begin(write=False) as txn: - value_buf = txn.get(str(filepath).encode('utf-8')) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - def _get_client(self): - import lmdb - - return lmdb.open( - self.db_path, - readonly=self.readonly, - lock=self.lock, - readahead=self.readahead, - **self.kwargs) - - def __del__(self): - self._client.close() - - -class HardDiskBackend(BaseStorageBackend): - """Raw hard disks storage backend.""" - - _allow_symlink = True - - def get(self, filepath: Union[str, Path]) -> bytes: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes: Expected bytes object. - """ - with open(filepath, 'rb') as f: - value_buf = f.read() - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - with open(filepath, 'r', encoding=encoding) as f: - value_buf = f.read() - return value_buf - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` will create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'wb') as f: - f.write(obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` will create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'w', encoding=encoding) as f: - f.write(obj) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - os.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return osp.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return osp.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return osp.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return osp.join(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, filepath: Union[str, Path]) -> Iterable[Union[str, Path]]: - """Only for unified API and do nothing.""" - yield filepath - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if list_dir and suffix is not None: - raise TypeError('`suffix` should be None when `list_dir` is True') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - elif osp.isdir(entry.path): - if list_dir: - rel_dir = osp.relpath(entry.path, root) - yield rel_dir - if recursive: - yield from _list_dir_or_file(entry.path, list_dir, - list_file, suffix, - recursive) - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class HTTPBackend(BaseStorageBackend): - """HTTP and HTTPS storage bachend.""" - - def get(self, filepath): - value_buf = urlopen(filepath).read() - return value_buf - - def get_text(self, filepath, encoding='utf-8'): - value_buf = urlopen(filepath).read() - return value_buf.decode(encoding) - - @contextmanager - def get_local_path(self, filepath: str) -> Iterable[str]: - """Download a file from ``filepath``. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str): Download a file from ``filepath``. - - Examples: - >>> client = HTTPBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('http://path/of/your/file') as path: - ... # do something here - """ - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - -class FileClient: - """A general file client to access files in different backends. - - The client loads a file or text in a specified backend from its path - and returns it as a binary or text file. There are two ways to choose a - backend, the name of backend and the prefix of path. Although both of them - can be used to choose a storage backend, ``backend`` has a higher priority - that is if they are all set, the storage backend will be chosen by the - backend argument. If they are all `None`, the disk backend will be chosen. - Note that It can also register other backend accessor with a given name, - prefixes, and backend class. In addition, We use the singleton pattern to - avoid repeated object creation. If the arguments are the same, the same - object will be returned. - - Args: - backend (str, optional): The storage backend type. Options are "disk", - "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. - prefix (str, optional): The prefix of the registered storage backend. - Options are "s3", "http", "https". Default: None. - - Examples: - >>> # only set backend - >>> file_client = FileClient(backend='petrel') - >>> # only set prefix - >>> file_client = FileClient(prefix='s3') - >>> # set both backend and prefix but use backend to choose client - >>> file_client = FileClient(backend='petrel', prefix='s3') - >>> # if the arguments are the same, the same object is returned - >>> file_client1 = FileClient(backend='petrel') - >>> file_client1 is file_client - True - - Attributes: - client (:obj:`BaseStorageBackend`): The backend object. - """ - - _backends = { - 'disk': HardDiskBackend, - 'ceph': CephBackend, - 'memcached': MemcachedBackend, - 'lmdb': LmdbBackend, - 'petrel': PetrelBackend, - 'http': HTTPBackend, - } - # This collection is used to record the overridden backends, and when a - # backend appears in the collection, the singleton pattern is disabled for - # that backend, because if the singleton pattern is used, then the object - # returned will be the backend before overwriting - _overridden_backends = set() - _prefix_to_backends = { - 's3': PetrelBackend, - 'http': HTTPBackend, - 'https': HTTPBackend, - } - _overridden_prefixes = set() - - _instances = {} - - def __new__(cls, backend=None, prefix=None, **kwargs): - if backend is None and prefix is None: - backend = 'disk' - if backend is not None and backend not in cls._backends: - raise ValueError( - f'Backend {backend} is not supported. Currently supported ones' - f' are {list(cls._backends.keys())}') - if prefix is not None and prefix not in cls._prefix_to_backends: - raise ValueError( - f'prefix {prefix} is not supported. Currently supported ones ' - f'are {list(cls._prefix_to_backends.keys())}') - - # concatenate the arguments to a unique key for determining whether - # objects with the same arguments were created - arg_key = f'{backend}:{prefix}' - for key, value in kwargs.items(): - arg_key += f':{key}:{value}' - - # if a backend was overridden, it will create a new object - if (arg_key in cls._instances - and backend not in cls._overridden_backends - and prefix not in cls._overridden_prefixes): - _instance = cls._instances[arg_key] - else: - # create a new object and put it to _instance - _instance = super().__new__(cls) - if backend is not None: - _instance.client = cls._backends[backend](**kwargs) - else: - _instance.client = cls._prefix_to_backends[prefix](**kwargs) - - cls._instances[arg_key] = _instance - - return _instance - - @property - def name(self): - return self.client.name - - @property - def allow_symlink(self): - return self.client.allow_symlink - - @staticmethod - def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: - """Parse the prefix of a uri. - - Args: - uri (str | Path): Uri to be parsed that contains the file prefix. - - Examples: - >>> FileClient.parse_uri_prefix('s3://path/of/your/file') - 's3' - - Returns: - str | None: Return the prefix of uri if the uri contains '://' else - ``None``. - """ - assert is_filepath(uri) - uri = str(uri) - if '://' not in uri: - return None - else: - prefix, _ = uri.split('://') - # In the case of PetrelBackend, the prefix may contains the cluster - # name like clusterName:s3 - if ':' in prefix: - _, prefix = prefix.split(':') - return prefix - - @classmethod - def infer_client(cls, - file_client_args: Optional[dict] = None, - uri: Optional[Union[str, Path]] = None) -> 'FileClient': - """Infer a suitable file client based on the URI and arguments. - - Args: - file_client_args (dict, optional): Arguments to instantiate a - FileClient. Default: None. - uri (str | Path, optional): Uri to be parsed that contains the file - prefix. Default: None. - - Examples: - >>> uri = 's3://path/of/your/file' - >>> file_client = FileClient.infer_client(uri=uri) - >>> file_client_args = {'backend': 'petrel'} - >>> file_client = FileClient.infer_client(file_client_args) - - Returns: - FileClient: Instantiated FileClient object. - """ - assert file_client_args is not None or uri is not None - if file_client_args is None: - file_prefix = cls.parse_uri_prefix(uri) # type: ignore - return cls(prefix=file_prefix) - else: - return cls(**file_client_args) - - @classmethod - def _register_backend(cls, name, backend, force=False, prefixes=None): - if not isinstance(name, str): - raise TypeError('the backend name should be a string, ' - f'but got {type(name)}') - if not inspect.isclass(backend): - raise TypeError( - f'backend should be a class but got {type(backend)}') - if not issubclass(backend, BaseStorageBackend): - raise TypeError( - f'backend {backend} is not a subclass of BaseStorageBackend') - if not force and name in cls._backends: - raise KeyError( - f'{name} is already registered as a storage backend, ' - 'add "force=True" if you want to override it') - - if name in cls._backends and force: - cls._overridden_backends.add(name) - cls._backends[name] = backend - - if prefixes is not None: - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if prefix not in cls._prefix_to_backends: - cls._prefix_to_backends[prefix] = backend - elif (prefix in cls._prefix_to_backends) and force: - cls._overridden_prefixes.add(prefix) - cls._prefix_to_backends[prefix] = backend - else: - raise KeyError( - f'{prefix} is already registered as a storage backend,' - ' add "force=True" if you want to override it') - - @classmethod - def register_backend(cls, name, backend=None, force=False, prefixes=None): - """Register a backend to FileClient. - - This method can be used as a normal class method or a decorator. - - .. code-block:: python - - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - FileClient.register_backend('new', NewBackend) - - or - - .. code-block:: python - - @FileClient.register_backend('new') - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - Args: - name (str): The name of the registered backend. - backend (class, optional): The backend class to be registered, - which must be a subclass of :class:`BaseStorageBackend`. - When this method is used as a decorator, backend is None. - Defaults to None. - force (bool, optional): Whether to override the backend if the name - has already been registered. Defaults to False. - prefixes (str or list[str] or tuple[str], optional): The prefixes - of the registered storage backend. Default: None. - `New in version 1.3.15.` - """ - if backend is not None: - cls._register_backend( - name, backend, force=force, prefixes=prefixes) - return - - def _register(backend_cls): - cls._register_backend( - name, backend_cls, force=force, prefixes=prefixes) - return backend_cls - - return _register - - def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: - """Read data from a given ``filepath`` with 'rb' mode. - - Note: - There are two types of return values for ``get``, one is ``bytes`` - and the other is ``memoryview``. The advantage of using memoryview - is that you can avoid copying, and if you want to convert it to - ``bytes``, you can use ``.tobytes()``. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes | memoryview: Expected bytes object or a memory view of the - bytes object. - """ - return self.client.get(filepath) - - def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return self.client.get_text(filepath, encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` should create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - self.client.put(obj, filepath) - - def put_text(self, obj: str, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` should create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str, optional): The encoding format used to open the - `filepath`. Default: 'utf-8'. - """ - self.client.put_text(obj, filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str, Path): Path to be removed. - """ - self.client.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return self.client.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return self.client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return self.client.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return self.client.join_path(filepath, *filepaths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download data from ``filepath`` and write the data to local path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Note: - If the ``filepath`` is a local path, just return itself. - - .. warning:: - ``get_local_path`` is an experimental interface that may change in - the future. - - Args: - filepath (str or Path): Path to be read data. - - Examples: - >>> file_client = FileClient(prefix='s3') - >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one path. - """ - with self.client.get_local_path(str(filepath)) as local_path: - yield local_path - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, - suffix, recursive) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/__init__.py deleted file mode 100644 index aa24d9197..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseFileHandler -from .json_handler import JsonHandler -from .pickle_handler import PickleHandler -from .yaml_handler import YamlHandler - -__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/base.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/base.py deleted file mode 100644 index 288878bc5..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseFileHandler(metaclass=ABCMeta): - # `str_like` is a flag to indicate whether the type of file object is - # str-like object or bytes-like object. Pickle only processes bytes-like - # objects but json only processes str-like object. If it is str-like - # object, `StringIO` will be used to process the buffer. - str_like = True - - @abstractmethod - def load_from_fileobj(self, file, **kwargs): - pass - - @abstractmethod - def dump_to_fileobj(self, obj, file, **kwargs): - pass - - @abstractmethod - def dump_to_str(self, obj, **kwargs): - pass - - def load_from_path(self, filepath, mode='r', **kwargs): - with open(filepath, mode) as f: - return self.load_from_fileobj(f, **kwargs) - - def dump_to_path(self, obj, filepath, mode='w', **kwargs): - with open(filepath, mode) as f: - self.dump_to_fileobj(obj, f, **kwargs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/json_handler.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/json_handler.py deleted file mode 100644 index 18d4f15f7..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/json_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -import numpy as np - -from .base import BaseFileHandler - - -def set_default(obj): - """Set default json values for non-serializable values. - - It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. - It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, - etc.) into plain numbers of plain python built-in types. - """ - if isinstance(obj, (set, range)): - return list(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, np.generic): - return obj.item() - raise TypeError(f'{type(obj)} is unsupported for json dump') - - -class JsonHandler(BaseFileHandler): - - def load_from_fileobj(self, file): - return json.load(file) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('default', set_default) - json.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('default', set_default) - return json.dumps(obj, **kwargs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/pickle_handler.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/pickle_handler.py deleted file mode 100644 index b37c79bed..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/pickle_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import pickle - -from .base import BaseFileHandler - - -class PickleHandler(BaseFileHandler): - - str_like = False - - def load_from_fileobj(self, file, **kwargs): - return pickle.load(file, **kwargs) - - def load_from_path(self, filepath, **kwargs): - return super(PickleHandler, self).load_from_path( - filepath, mode='rb', **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('protocol', 2) - return pickle.dumps(obj, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('protocol', 2) - pickle.dump(obj, file, **kwargs) - - def dump_to_path(self, obj, filepath, **kwargs): - super(PickleHandler, self).dump_to_path( - obj, filepath, mode='wb', **kwargs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/yaml_handler.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/yaml_handler.py deleted file mode 100644 index 60911e7e6..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/handlers/yaml_handler.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import yaml - -try: - from yaml import CDumper as Dumper - from yaml import CLoader as Loader -except ImportError: - from yaml import Loader, Dumper - -from .base import BaseFileHandler # isort:skip - - -class YamlHandler(BaseFileHandler): - - def load_from_fileobj(self, file, **kwargs): - kwargs.setdefault('Loader', Loader) - return yaml.load(file, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('Dumper', Dumper) - yaml.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('Dumper', Dumper) - return yaml.dump(obj, **kwargs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/io.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/io.py deleted file mode 100644 index aaefde58a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/io.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from io import BytesIO, StringIO -from pathlib import Path - -from ..utils import is_list_of, is_str -from .file_client import FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler - -file_handlers = { - 'json': JsonHandler(), - 'yaml': YamlHandler(), - 'yml': YamlHandler(), - 'pickle': PickleHandler(), - 'pkl': PickleHandler() -} - - -def load(file, file_format=None, file_client_args=None, **kwargs): - """Load data from json/yaml/pickle files. - - This method provides a unified api for loading data from serialized files. - - Note: - In v1.3.16 and later, ``load`` supports loading data from serialized - files those can be storaged in different backends. - - Args: - file (str or :obj:`Path` or file-like object): Filename or a file-like - object. - file_format (str, optional): If not specified, the file format will be - inferred from the file extension, otherwise use the specified one. - Currently supported formats include "json", "yaml/yml" and - "pickle/pkl". - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> load('/path/of/your/file') # file is storaged in disk - >>> load('https://path/of/your/file') # file is storaged in Internet - >>> load('s3://path/of/your/file') # file is storaged in petrel - - Returns: - The content from the file. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None and is_str(file): - file_format = file.split('.')[-1] - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO(file_client.get_text(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - else: - with BytesIO(file_client.get(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - elif hasattr(file, 'read'): - obj = handler.load_from_fileobj(file, **kwargs) - else: - raise TypeError('"file" must be a filepath str or a file-object') - return obj - - -def dump(obj, file=None, file_format=None, file_client_args=None, **kwargs): - """Dump data to json/yaml/pickle strings or files. - - This method provides a unified api for dumping data as strings or to files, - and also supports custom arguments for each file format. - - Note: - In v1.3.16 and later, ``dump`` supports dumping data as strings or to - files which is saved to different backends. - - Args: - obj (any): The python object to be dumped. - file (str or :obj:`Path` or file-like object, optional): If not - specified, then the object is dumped to a str, otherwise to a file - specified by the filename or file-like object. - file_format (str, optional): Same as :func:`load`. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dump('hello world', '/path/of/your/file') # disk - >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel - - Returns: - bool: True for success, False otherwise. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None: - if is_str(file): - file_format = file.split('.')[-1] - elif file is None: - raise ValueError( - 'file_format must be specified since file is None') - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if file is None: - return handler.dump_to_str(obj, **kwargs) - elif is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put_text(f.getvalue(), file) - else: - with BytesIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put(f.getvalue(), file) - elif hasattr(file, 'write'): - handler.dump_to_fileobj(obj, file, **kwargs) - else: - raise TypeError('"file" must be a filename str or a file-object') - - -def _register_handler(handler, file_formats): - """Register a handler for some file extensions. - - Args: - handler (:obj:`BaseFileHandler`): Handler to be registered. - file_formats (str or list[str]): File formats to be handled by this - handler. - """ - if not isinstance(handler, BaseFileHandler): - raise TypeError( - f'handler must be a child of BaseFileHandler, not {type(handler)}') - if isinstance(file_formats, str): - file_formats = [file_formats] - if not is_list_of(file_formats, str): - raise TypeError('file_formats must be a str or a list of str') - for ext in file_formats: - file_handlers[ext] = handler - - -def register_handler(file_formats, **kwargs): - - def wrap(cls): - _register_handler(cls(**kwargs), file_formats) - return cls - - return wrap diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/parse.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/parse.py deleted file mode 100644 index f60f0d611..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/fileio/parse.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from io import StringIO - -from .file_client import FileClient - - -def list_from_file(filename, - prefix='', - offset=0, - max_num=0, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a list of strings. - - Note: - In v1.3.16 and later, ``list_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a list for strings. - - Args: - filename (str): Filename. - prefix (str): The prefix to be inserted to the beginning of each item. - offset (int): The offset of lines. - max_num (int): The maximum number of lines to be read, - zeros and negatives mean no limitation. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> list_from_file('/path/of/your/file') # disk - ['hello', 'world'] - >>> list_from_file('s3://path/of/your/file') # ceph or petrel - ['hello', 'world'] - - Returns: - list[str]: A list of strings. - """ - cnt = 0 - item_list = [] - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for _ in range(offset): - f.readline() - for line in f: - if 0 < max_num <= cnt: - break - item_list.append(prefix + line.rstrip('\n\r')) - cnt += 1 - return item_list - - -def dict_from_file(filename, - key_type=str, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a dict. - - Each line of the text file will be two or more columns split by - whitespaces or tabs. The first column will be parsed as dict keys, and - the following columns will be parsed as dict values. - - Note: - In v1.3.16 and later, ``dict_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a dict. - - Args: - filename(str): Filename. - key_type(type): Type of the dict keys. str is user by default and - type conversion will be performed if specified. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dict_from_file('/path/of/your/file') # disk - {'key1': 'value1', 'key2': 'value2'} - >>> dict_from_file('s3://path/of/your/file') # ceph or petrel - {'key1': 'value1', 'key2': 'value2'} - - Returns: - dict: The parsed contents. - """ - mapping = {} - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for line in f: - items = line.rstrip('\n').split() - assert len(items) >= 2 - key = key_type(items[0]) - val = items[1:] if len(items) > 2 else items[1] - mapping[key] = val - return mapping diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/image/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/image/__init__.py deleted file mode 100644 index d0051d609..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/image/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, - gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, - rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) -from .geometric import (cutout, imcrop, imflip, imflip_, impad, - impad_to_multiple, imrescale, imresize, imresize_like, - imresize_to_multiple, imrotate, imshear, imtranslate, - rescale_size) -from .io import imfrombytes, imread, imwrite, supported_backends, use_backend -from .misc import tensor2imgs -from .photometric import (adjust_brightness, adjust_color, adjust_contrast, - adjust_lighting, adjust_sharpness, auto_contrast, - clahe, imdenormalize, imequalize, iminvert, - imnormalize, imnormalize_, lut_transform, posterize, - solarize) - -__all__ = [ - 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', - 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', - 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', - 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', - 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', - 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', - 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', - 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', - 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', - 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/image/colorspace.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/image/colorspace.py deleted file mode 100644 index 4337720ea..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/image/colorspace.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def imconvert(img, src, dst): - """Convert an image from the src colorspace to dst colorspace. - - Args: - img (ndarray): The input image. - src (str): The source colorspace, e.g., 'rgb', 'hsv'. - dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. - - Returns: - ndarray: The converted image. - """ - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - out_img = cv2.cvtColor(img, code) - return out_img - - -def bgr2gray(img, keepdim=False): - """Convert a BGR image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def rgb2gray(img, keepdim=False): - """Convert a RGB image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def gray2bgr(img): - """Convert a grayscale image to BGR image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted BGR image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return out_img - - -def gray2rgb(img): - """Convert a grayscale image to RGB image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted RGB image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) - return out_img - - -def _convert_input_type_range(img): - """Convert the type and range of the input image. - - It converts the input image to np.float32 type and range of [0, 1]. - It is mainly used for pre-processing the input image in colorspace - conversion functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - (ndarray): The converted image with type of np.float32 and range of - [0, 1]. - """ - img_type = img.dtype - img = img.astype(np.float32) - if img_type == np.float32: - pass - elif img_type == np.uint8: - img /= 255. - else: - raise TypeError('The img type should be np.float32 or np.uint8, ' - f'but got {img_type}') - return img - - -def _convert_output_type_range(img, dst_type): - """Convert the type and range of the image according to dst_type. - - It converts the image to desired type and range. If `dst_type` is np.uint8, - images will be converted to np.uint8 type with range [0, 255]. If - `dst_type` is np.float32, it converts the image to np.float32 type with - range [0, 1]. - It is mainly used for post-processing images in colorspace conversion - functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The image to be converted with np.float32 type and - range [0, 255]. - dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it - converts the image to np.uint8 type with range [0, 255]. If - dst_type is np.float32, it converts the image to np.float32 type - with range [0, 1]. - - Returns: - (ndarray): The converted image with desired type and range. - """ - if dst_type not in (np.uint8, np.float32): - raise TypeError('The dst_type should be np.float32 or np.uint8, ' - f'but got {dst_type}') - if dst_type == np.uint8: - img = img.round() - else: - img /= 255. - return img.astype(dst_type) - - -def rgb2ycbcr(img, y_only=False): - """Convert a RGB image to YCbCr image. - - This function produces the same results as Matlab's `rgb2ycbcr` function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 - else: - out_img = np.matmul( - img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], - [24.966, 112.0, -18.214]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def bgr2ycbcr(img, y_only=False): - """Convert a BGR image to YCbCr image. - - The bgr version of rgb2ycbcr. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 - else: - out_img = np.matmul( - img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], - [65.481, -37.797, 112.0]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2rgb(img): - """Convert a YCbCr image to RGB image. - - This function produces the same results as Matlab's ycbcr2rgb function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted RGB image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0, -0.00153632, 0.00791071], - [0.00625893, -0.00318811, 0]]) * 255.0 + [ - -222.921, 135.576, -276.836 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2bgr(img): - """Convert a YCbCr image to BGR image. - - The bgr version of ycbcr2rgb. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted BGR image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0.00791071, -0.00153632, 0], - [0, -0.00318811, 0.00625893]]) * 255.0 + [ - -276.836, 135.576, -222.921 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def convert_color_factory(src, dst): - - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - - def convert_color(img): - out_img = cv2.cvtColor(img, code) - return out_img - - convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} - image. - - Args: - img (ndarray or str): The input image. - - Returns: - ndarray: The converted {dst.upper()} image. - """ - - return convert_color - - -bgr2rgb = convert_color_factory('bgr', 'rgb') - -rgb2bgr = convert_color_factory('rgb', 'bgr') - -bgr2hsv = convert_color_factory('bgr', 'hsv') - -hsv2bgr = convert_color_factory('hsv', 'bgr') - -bgr2hls = convert_color_factory('bgr', 'hls') - -hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/image/geometric.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/image/geometric.py deleted file mode 100644 index 4c423bf2a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/image/geometric.py +++ /dev/null @@ -1,741 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers - -import cv2 -import numpy as np - -from ..utils import to_2tuple -from .io import imread_backend - -try: - from PIL import Image -except ImportError: - Image = None - - -def _scale_size(size, scale): - """Rescale a size by a ratio. - - Args: - size (tuple[int]): (w, h). - scale (float | tuple(float)): Scaling factor. - - Returns: - tuple[int]: scaled size. - """ - if isinstance(scale, (float, int)): - scale = (scale, scale) - w, h = size - return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) - - -cv2_interp_codes = { - 'nearest': cv2.INTER_NEAREST, - 'bilinear': cv2.INTER_LINEAR, - 'bicubic': cv2.INTER_CUBIC, - 'area': cv2.INTER_AREA, - 'lanczos': cv2.INTER_LANCZOS4 -} - -# Pillow >=v9.1.0 use a slightly different naming scheme for filters. -# Set pillow_interp_codes according to the naming scheme used. -if Image is not None: - if hasattr(Image, 'Resampling'): - pillow_interp_codes = { - 'nearest': Image.Resampling.NEAREST, - 'bilinear': Image.Resampling.BILINEAR, - 'bicubic': Image.Resampling.BICUBIC, - 'box': Image.Resampling.BOX, - 'lanczos': Image.Resampling.LANCZOS, - 'hamming': Image.Resampling.HAMMING - } - else: - pillow_interp_codes = { - 'nearest': Image.NEAREST, - 'bilinear': Image.BILINEAR, - 'bicubic': Image.BICUBIC, - 'box': Image.BOX, - 'lanczos': Image.LANCZOS, - 'hamming': Image.HAMMING - } - - -def imresize(img, - size, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image to a given size. - - Args: - img (ndarray): The input image. - size (tuple[int]): Target size (w, h). - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if backend is None: - backend = imread_backend - if backend not in ['cv2', 'pillow']: - raise ValueError(f'backend: {backend} is not supported for resize.' - f"Supported backends are 'cv2', 'pillow'") - - if backend == 'pillow': - assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' - pil_image = Image.fromarray(img) - pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) - resized_img = np.array(pil_image) - else: - resized_img = cv2.resize( - img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) - if not return_scale: - return resized_img - else: - w_scale = size[0] / w - h_scale = size[1] / h - return resized_img, w_scale, h_scale - - -def imresize_to_multiple(img, - divisor, - size=None, - scale_factor=None, - keep_ratio=False, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image according to a given size or scale factor and then rounds - up the the resized or rescaled image size to the nearest value that can be - divided by the divisor. - - Args: - img (ndarray): The input image. - divisor (int | tuple): Resized image size will be a multiple of - divisor. If divisor is a tuple, divisor should be - (w_divisor, h_divisor). - size (None | int | tuple[int]): Target size (w, h). Default: None. - scale_factor (None | float | tuple[float]): Multiplier for spatial - size. Should match input size if it is a tuple and the 2D style is - (w_scale_factor, h_scale_factor). Default: None. - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: False. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if size is not None and scale_factor is not None: - raise ValueError('only one of size or scale_factor should be defined') - elif size is None and scale_factor is None: - raise ValueError('one of size or scale_factor should be defined') - elif size is not None: - size = to_2tuple(size) - if keep_ratio: - size = rescale_size((w, h), size, return_scale=False) - else: - size = _scale_size((w, h), scale_factor) - - divisor = to_2tuple(divisor) - size = tuple([int(np.ceil(s / d)) * d for s, d in zip(size, divisor)]) - resized_img, w_scale, h_scale = imresize( - img, - size, - return_scale=True, - interpolation=interpolation, - out=out, - backend=backend) - if return_scale: - return resized_img, w_scale, h_scale - else: - return resized_img - - -def imresize_like(img, - dst_img, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image to the same size of a given image. - - Args: - img (ndarray): The input image. - dst_img (ndarray): The target image. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = dst_img.shape[:2] - return imresize(img, (w, h), return_scale, interpolation, backend=backend) - - -def rescale_size(old_size, scale, return_scale=False): - """Calculate the new size to be rescaled to. - - Args: - old_size (tuple[int]): The old size (w, h) of image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image size. - - Returns: - tuple[int]: The new rescaled image size. - """ - w, h = old_size - if isinstance(scale, (float, int)): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - scale_factor = scale - elif isinstance(scale, tuple): - max_long_edge = max(scale) - max_short_edge = min(scale) - scale_factor = min(max_long_edge / max(h, w), - max_short_edge / min(h, w)) - else: - raise TypeError( - f'Scale must be a number or tuple of int, but got {type(scale)}') - - new_size = _scale_size((w, h), scale_factor) - - if return_scale: - return new_size, scale_factor - else: - return new_size - - -def imrescale(img, - scale, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image while keeping the aspect ratio. - - Args: - img (ndarray): The input image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - ndarray: The rescaled image. - """ - h, w = img.shape[:2] - new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) - rescaled_img = imresize( - img, new_size, interpolation=interpolation, backend=backend) - if return_scale: - return rescaled_img, scale_factor - else: - return rescaled_img - - -def imflip(img, direction='horizontal'): - """Flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image. - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return np.flip(img, axis=1) - elif direction == 'vertical': - return np.flip(img, axis=0) - else: - return np.flip(img, axis=(0, 1)) - - -def imflip_(img, direction='horizontal'): - """Inplace flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image (inplace). - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return cv2.flip(img, 1, img) - elif direction == 'vertical': - return cv2.flip(img, 0, img) - else: - return cv2.flip(img, -1, img) - - -def imrotate(img, - angle, - center=None, - scale=1.0, - border_value=0, - interpolation='bilinear', - auto_bound=False): - """Rotate an image. - - Args: - img (ndarray): Image to be rotated. - angle (float): Rotation angle in degrees, positive values mean - clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. - scale (float): Isotropic scale factor. - border_value (int): Border value. - interpolation (str): Same as :func:`resize`. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. - - Returns: - ndarray: The rotated image. - """ - if center is not None and auto_bound: - raise ValueError('`auto_bound` conflicts with `center`') - h, w = img.shape[:2] - if center is None: - center = ((w - 1) * 0.5, (h - 1) * 0.5) - assert isinstance(center, tuple) - - matrix = cv2.getRotationMatrix2D(center, -angle, scale) - if auto_bound: - cos = np.abs(matrix[0, 0]) - sin = np.abs(matrix[0, 1]) - new_w = h * sin + w * cos - new_h = h * cos + w * sin - matrix[0, 2] += (new_w - w) * 0.5 - matrix[1, 2] += (new_h - h) * 0.5 - w = int(np.round(new_w)) - h = int(np.round(new_h)) - rotated = cv2.warpAffine( - img, - matrix, (w, h), - flags=cv2_interp_codes[interpolation], - borderValue=border_value) - return rotated - - -def bbox_clip(bboxes, img_shape): - """Clip bboxes to fit the image shape. - - Args: - bboxes (ndarray): Shape (..., 4*k) - img_shape (tuple[int]): (height, width) of the image. - - Returns: - ndarray: Clipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) - cmin[0::2] = img_shape[1] - 1 - cmin[1::2] = img_shape[0] - 1 - clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) - return clipped_bboxes - - -def bbox_scaling(bboxes, scale, clip_shape=None): - """Scaling bboxes w.r.t the box center. - - Args: - bboxes (ndarray): Shape(..., 4). - scale (float): Scaling factor. - clip_shape (tuple[int], optional): If specified, bboxes that exceed the - boundary will be clipped according to the given shape (h, w). - - Returns: - ndarray: Scaled bboxes. - """ - if float(scale) == 1.0: - scaled_bboxes = bboxes.copy() - else: - w = bboxes[..., 2] - bboxes[..., 0] + 1 - h = bboxes[..., 3] - bboxes[..., 1] + 1 - dw = (w * (scale - 1)) * 0.5 - dh = (h * (scale - 1)) * 0.5 - scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) - if clip_shape is not None: - return bbox_clip(scaled_bboxes, clip_shape) - else: - return scaled_bboxes - - -def imcrop(img, bboxes, scale=1.0, pad_fill=None): - """Crop image patches. - - 3 steps: scale the bboxes -> clip bboxes -> crop and pad. - - Args: - img (ndarray): Image to be cropped. - bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. - scale (float, optional): Scale ratio of bboxes, the default value - 1.0 means no padding. - pad_fill (Number | list[Number]): Value to be filled for padding. - Default: None, which means no padding. - - Returns: - list[ndarray] | ndarray: The cropped image patches. - """ - chn = 1 if img.ndim == 2 else img.shape[2] - if pad_fill is not None: - if isinstance(pad_fill, (int, float)): - pad_fill = [pad_fill for _ in range(chn)] - assert len(pad_fill) == chn - - _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes - scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) - clipped_bbox = bbox_clip(scaled_bboxes, img.shape) - - patches = [] - for i in range(clipped_bbox.shape[0]): - x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) - if pad_fill is None: - patch = img[y1:y2 + 1, x1:x2 + 1, ...] - else: - _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) - if chn == 1: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) - else: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) - patch = np.array( - pad_fill, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - x_start = 0 if _x1 >= 0 else -_x1 - y_start = 0 if _y1 >= 0 else -_y1 - w = x2 - x1 + 1 - h = y2 - y1 + 1 - patch[y_start:y_start + h, x_start:x_start + w, - ...] = img[y1:y1 + h, x1:x1 + w, ...] - patches.append(patch) - - if bboxes.ndim == 1: - return patches[0] - else: - return patches - - -def impad(img, - *, - shape=None, - padding=None, - pad_val=0, - padding_mode='constant'): - """Pad the given image to a certain shape or pad on all sides with - specified padding mode and padding value. - - Args: - img (ndarray): Image to be padded. - shape (tuple[int]): Expected padding shape (h, w). Default: None. - padding (int or tuple[int]): Padding on each border. If a single int is - provided this is used to pad all borders. If tuple of length 2 is - provided this is the padding on left/right and top/bottom - respectively. If a tuple of length 4 is provided this is the - padding for the left, top, right and bottom borders respectively. - Default: None. Note that `shape` and `padding` can not be both - set. - pad_val (Number | Sequence[Number]): Values to be filled in padding - areas when padding_mode is 'constant'. Default: 0. - padding_mode (str): Type of padding. Should be: constant, edge, - reflect or symmetric. Default: constant. - - constant: pads with a constant value, this value is specified - with pad_val. - - edge: pads with the last value at the edge of the image. - - reflect: pads with reflection of image without repeating the last - value on the edge. For example, padding [1, 2, 3, 4] with 2 - elements on both sides in reflect mode will result in - [3, 2, 1, 2, 3, 4, 3, 2]. - - symmetric: pads with reflection of image repeating the last value - on the edge. For example, padding [1, 2, 3, 4] with 2 elements on - both sides in symmetric mode will result in - [2, 1, 1, 2, 3, 4, 4, 3] - - Returns: - ndarray: The padded image. - """ - - assert (shape is not None) ^ (padding is not None) - if shape is not None: - width = max(shape[1] - img.shape[1], 0) - height = max(shape[0] - img.shape[0], 0) - padding = (0, 0, width, height) - - # check pad_val - if isinstance(pad_val, tuple): - assert len(pad_val) == img.shape[-1] - elif not isinstance(pad_val, numbers.Number): - raise TypeError('pad_val must be a int or a tuple. ' - f'But received {type(pad_val)}') - - # check padding - if isinstance(padding, tuple) and len(padding) in [2, 4]: - if len(padding) == 2: - padding = (padding[0], padding[1], padding[0], padding[1]) - elif isinstance(padding, numbers.Number): - padding = (padding, padding, padding, padding) - else: - raise ValueError('Padding must be a int or a 2, or 4 element tuple.' - f'But received {padding}') - - # check padding mode - assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] - - border_type = { - 'constant': cv2.BORDER_CONSTANT, - 'edge': cv2.BORDER_REPLICATE, - 'reflect': cv2.BORDER_REFLECT_101, - 'symmetric': cv2.BORDER_REFLECT - } - img = cv2.copyMakeBorder( - img, - padding[1], - padding[3], - padding[0], - padding[2], - border_type[padding_mode], - value=pad_val) - - return img - - -def impad_to_multiple(img, divisor, pad_val=0): - """Pad an image to ensure each edge to be multiple to some number. - - Args: - img (ndarray): Image to be padded. - divisor (int): Padded image edges will be multiple to divisor. - pad_val (Number | Sequence[Number]): Same as :func:`impad`. - - Returns: - ndarray: The padded image. - """ - pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor - pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor - return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) - - -def cutout(img, shape, pad_val=0): - """Randomly cut out a rectangle from the original img. - - Args: - img (ndarray): Image to be cutout. - shape (int | tuple[int]): Expected cutout shape (h, w). If given as a - int, the value will be used for both h and w. - pad_val (int | float | tuple[int | float]): Values to be filled in the - cut area. Defaults to 0. - - Returns: - ndarray: The cutout image. - """ - - channels = 1 if img.ndim == 2 else img.shape[2] - if isinstance(shape, int): - cut_h, cut_w = shape, shape - else: - assert isinstance(shape, tuple) and len(shape) == 2, \ - f'shape must be a int or a tuple with length 2, but got type ' \ - f'{type(shape)} instead.' - cut_h, cut_w = shape - if isinstance(pad_val, (int, float)): - pad_val = tuple([pad_val] * channels) - elif isinstance(pad_val, tuple): - assert len(pad_val) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(pad_val), channels) - else: - raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') - - img_h, img_w = img.shape[:2] - y0 = np.random.uniform(img_h) - x0 = np.random.uniform(img_w) - - y1 = int(max(0, y0 - cut_h / 2.)) - x1 = int(max(0, x0 - cut_w / 2.)) - y2 = min(img_h, y1 + cut_h) - x2 = min(img_w, x1 + cut_w) - - if img.ndim == 2: - patch_shape = (y2 - y1, x2 - x1) - else: - patch_shape = (y2 - y1, x2 - x1, channels) - - img_cutout = img.copy() - patch = np.array( - pad_val, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - img_cutout[y1:y2, x1:x2, ...] = patch - - return img_cutout - - -def _get_shear_matrix(magnitude, direction='horizontal'): - """Generate the shear matrix for transformation. - - Args: - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - - Returns: - ndarray: The shear matrix with dtype float32. - """ - if direction == 'horizontal': - shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) - elif direction == 'vertical': - shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) - return shear_matrix - - -def imshear(img, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear an image. - - Args: - img (ndarray): Image to be sheared with format (h, w) - or (h, w, c). - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The sheared image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`') - shear_matrix = _get_shear_matrix(magnitude, direction) - sheared = cv2.warpAffine( - img, - shear_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. shearing masks whose channels large - # than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return sheared - - -def _get_translate_matrix(offset, direction='horizontal'): - """Generate the translate matrix. - - Args: - offset (int | float): The offset used for translate. - direction (str): The translate direction, either - "horizontal" or "vertical". - - Returns: - ndarray: The translate matrix with dtype float32. - """ - if direction == 'horizontal': - translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) - elif direction == 'vertical': - translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) - return translate_matrix - - -def imtranslate(img, - offset, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Translate an image. - - Args: - img (ndarray): Image to be translated with format - (h, w) or (h, w, c). - offset (int | float): The offset used for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The translated image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`.') - translate_matrix = _get_translate_matrix(offset, direction) - translated = cv2.warpAffine( - img, - translate_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. translating masks whose channels - # large than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return translated diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/image/io.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/image/io.py deleted file mode 100644 index ae81b561a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/image/io.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os.path as osp -import warnings -from pathlib import Path - -import cv2 -import numpy as np -from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, - IMREAD_UNCHANGED) - -from mmcv.fileio import FileClient -from mmcv.utils import is_filepath, is_str - -try: - from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG -except ImportError: - TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None - -try: - from PIL import Image, ImageOps -except ImportError: - Image = None - -try: - import tifffile -except ImportError: - tifffile = None - -jpeg = None -supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] - -imread_flags = { - 'color': IMREAD_COLOR, - 'grayscale': IMREAD_GRAYSCALE, - 'unchanged': IMREAD_UNCHANGED, - 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, - 'grayscale_ignore_orientation': - IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE -} - -imread_backend = 'cv2' - - -def use_backend(backend): - """Select a backend for image decoding. - - Args: - backend (str): The image decoding backend type. Options are `cv2`, - `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) - and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` - file format. - """ - assert backend in supported_backends - global imread_backend - imread_backend = backend - if imread_backend == 'turbojpeg': - if TurboJPEG is None: - raise ImportError('`PyTurboJPEG` is not installed') - global jpeg - if jpeg is None: - jpeg = TurboJPEG() - elif imread_backend == 'pillow': - if Image is None: - raise ImportError('`Pillow` is not installed') - elif imread_backend == 'tifffile': - if tifffile is None: - raise ImportError('`tifffile` is not installed') - - -def _jpegflag(flag='color', channel_order='bgr'): - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'color': - if channel_order == 'bgr': - return TJPF_BGR - elif channel_order == 'rgb': - return TJCS_RGB - elif flag == 'grayscale': - return TJPF_GRAY - else: - raise ValueError('flag must be "color" or "grayscale"') - - -def _pillow2array(img, flag='color', channel_order='bgr'): - """Convert a pillow image to numpy array. - - Args: - img (:obj:`PIL.Image.Image`): The image loaded using PIL - flag (str): Flags specifying the color type of a loaded image, - candidates are 'color', 'grayscale' and 'unchanged'. - Default to 'color'. - channel_order (str): The channel order of the output image array, - candidates are 'bgr' and 'rgb'. Default to 'bgr'. - - Returns: - np.ndarray: The converted numpy array - """ - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'unchanged': - array = np.array(img) - if array.ndim >= 3 and array.shape[2] >= 3: # color image - array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR - else: - # Handle exif orientation tag - if flag in ['color', 'grayscale']: - img = ImageOps.exif_transpose(img) - # If the image mode is not 'RGB', convert it to 'RGB' first. - if img.mode != 'RGB': - if img.mode != 'LA': - # Most formats except 'LA' can be directly converted to RGB - img = img.convert('RGB') - else: - # When the mode is 'LA', the default conversion will fill in - # the canvas with black, which sometimes shadows black objects - # in the foreground. - # - # Therefore, a random color (124, 117, 104) is used for canvas - img_rgba = img.convert('RGBA') - img = Image.new('RGB', img_rgba.size, (124, 117, 104)) - img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha - if flag in ['color', 'color_ignore_orientation']: - array = np.array(img) - if channel_order != 'rgb': - array = array[:, :, ::-1] # RGB to BGR - elif flag in ['grayscale', 'grayscale_ignore_orientation']: - img = img.convert('L') - array = np.array(img) - else: - raise ValueError( - 'flag must be "color", "grayscale", "unchanged", ' - f'"color_ignore_orientation" or "grayscale_ignore_orientation"' - f' but got {flag}') - return array - - -def imread(img_or_path, - flag='color', - channel_order='bgr', - backend=None, - file_client_args=None): - """Read an image. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Args: - img_or_path (ndarray or str or Path): Either a numpy array or str or - pathlib.Path. If it is a numpy array (loaded image), then - it will be returned as is. - flag (str): Flags specifying the color type of a loaded image, - candidates are `color`, `grayscale`, `unchanged`, - `color_ignore_orientation` and `grayscale_ignore_orientation`. - By default, `cv2` and `pillow` backend would rotate the image - according to its EXIF info unless called with `unchanged` or - `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend - always ignore image's EXIF info regardless of the flag. - The `turbojpeg` backend only supports `color` and `grayscale`. - channel_order (str): Order of channel, candidates are `bgr` and `rgb`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. - If backend is None, the global imread_backend specified by - ``mmcv.use_backend()`` will be used. Default: None. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> import mmcv - >>> img_path = '/path/to/img.jpg' - >>> img = mmcv.imread(img_path) - >>> img = mmcv.imread(img_path, flag='color', channel_order='rgb', - ... backend='cv2') - >>> img = mmcv.imread(img_path, flag='color', channel_order='bgr', - ... backend='pillow') - >>> s3_img_path = 's3://bucket/img.jpg' - >>> # infer the file backend by the prefix s3 - >>> img = mmcv.imread(s3_img_path) - >>> # manually set the file backend petrel - >>> img = mmcv.imread(s3_img_path, file_client_args={ - ... 'backend': 'petrel'}) - >>> http_img_path = 'http://path/to/img.jpg' - >>> img = mmcv.imread(http_img_path) - >>> img = mmcv.imread(http_img_path, file_client_args={ - ... 'backend': 'http'}) - """ - - if isinstance(img_or_path, Path): - img_or_path = str(img_or_path) - - if isinstance(img_or_path, np.ndarray): - return img_or_path - elif is_str(img_or_path): - file_client = FileClient.infer_client(file_client_args, img_or_path) - img_bytes = file_client.get(img_or_path) - return imfrombytes(img_bytes, flag, channel_order, backend) - else: - raise TypeError('"img" must be a numpy array or a str or ' - 'a pathlib.Path object') - - -def imfrombytes(content, flag='color', channel_order='bgr', backend=None): - """Read an image from bytes. - - Args: - content (bytes): Image bytes got from files or other streams. - flag (str): Same as :func:`imread`. - channel_order (str): The channel order of the output, candidates - are 'bgr' and 'rgb'. Default to 'bgr'. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. If backend is - None, the global imread_backend specified by ``mmcv.use_backend()`` - will be used. Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> img_path = '/path/to/img.jpg' - >>> with open(img_path, 'rb') as f: - >>> img_buff = f.read() - >>> img = mmcv.imfrombytes(img_buff) - >>> img = mmcv.imfrombytes(img_buff, flag='color', channel_order='rgb') - >>> img = mmcv.imfrombytes(img_buff, backend='pillow') - >>> img = mmcv.imfrombytes(img_buff, backend='cv2') - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError( - f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow', 'tifffile'") - if backend == 'turbojpeg': - img = jpeg.decode(content, _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - with io.BytesIO(content) as buff: - img = Image.open(buff) - img = _pillow2array(img, flag, channel_order) - return img - elif backend == 'tifffile': - with io.BytesIO(content) as buff: - img = tifffile.imread(buff) - return img - else: - img_np = np.frombuffer(content, np.uint8) - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imdecode(img_np, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - - -def imwrite(img, - file_path, - params=None, - auto_mkdir=None, - file_client_args=None): - """Write image to file. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Warning: - The parameter `auto_mkdir` will be deprecated in the future and every - file clients will make directory automatically. - - Args: - img (ndarray): Image array to be written. - file_path (str): Image file path. - params (None or list): Same as opencv :func:`imwrite` interface. - auto_mkdir (bool): If the parent folder of `file_path` does not exist, - whether to create it automatically. It will be deprecated. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - bool: Successful or not. - - Examples: - >>> # write to hard disk client - >>> ret = mmcv.imwrite(img, '/path/to/img.jpg') - >>> # infer the file backend by the prefix s3 - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg') - >>> # manually set the file backend petrel - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg', file_client_args={ - ... 'backend': 'petrel'}) - """ - assert is_filepath(file_path) - file_path = str(file_path) - if auto_mkdir is not None: - warnings.warn( - 'The parameter `auto_mkdir` will be deprecated in the future and ' - 'every file clients will make directory automatically.') - file_client = FileClient.infer_client(file_client_args, file_path) - img_ext = osp.splitext(file_path)[-1] - # Encode image according to image suffix. - # For example, if image path is '/path/your/img.jpg', the encode - # format is '.jpg'. - flag, img_buff = cv2.imencode(img_ext, img, params) - file_client.put(img_buff.tobytes(), file_path) - return flag diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/image/misc.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/image/misc.py deleted file mode 100644 index 43934a689..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/image/misc.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - -import mmcv - -try: - import torch -except ImportError: - torch = None - - -def tensor2imgs(tensor, mean=None, std=None, to_rgb=True): - """Convert tensor to 3-channel images or 1-channel gray images. - - Args: - tensor (torch.Tensor): Tensor that contains multiple images, shape ( - N, C, H, W). :math:`C` can be either 3 or 1. - mean (tuple[float], optional): Mean of images. If None, - (0, 0, 0) will be used for tensor with 3-channel, - while (0, ) for tensor with 1-channel. Defaults to None. - std (tuple[float], optional): Standard deviation of images. If None, - (1, 1, 1) will be used for tensor with 3-channel, - while (1, ) for tensor with 1-channel. Defaults to None. - to_rgb (bool, optional): Whether the tensor was converted to RGB - format in the first place. If so, convert it back to BGR. - For the tensor with 1 channel, it must be False. Defaults to True. - - Returns: - list[np.ndarray]: A list that contains multiple images. - """ - - if torch is None: - raise RuntimeError('pytorch is not installed') - assert torch.is_tensor(tensor) and tensor.ndim == 4 - channels = tensor.size(1) - assert channels in [1, 3] - if mean is None: - mean = (0, ) * channels - if std is None: - std = (1, ) * channels - assert (channels == len(mean) == len(std) == 3) or \ - (channels == len(mean) == len(std) == 1 and not to_rgb) - - num_imgs = tensor.size(0) - mean = np.array(mean, dtype=np.float32) - std = np.array(std, dtype=np.float32) - imgs = [] - for img_id in range(num_imgs): - img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) - img = mmcv.imdenormalize( - img, mean, std, to_bgr=to_rgb).astype(np.uint8) - imgs.append(np.ascontiguousarray(img)) - return imgs diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/image/photometric.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/image/photometric.py deleted file mode 100644 index 5085d0120..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/image/photometric.py +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - -from ..utils import is_tuple_of -from .colorspace import bgr2gray, gray2bgr - - -def imnormalize(img, mean, std, to_rgb=True): - """Normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - img = img.copy().astype(np.float32) - return imnormalize_(img, mean, std, to_rgb) - - -def imnormalize_(img, mean, std, to_rgb=True): - """Inplace normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - # cv2 inplace normalization does not accept uint8 - assert img.dtype != np.uint8 - mean = np.float64(mean.reshape(1, -1)) - stdinv = 1 / np.float64(std.reshape(1, -1)) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - cv2.subtract(img, mean, img) # inplace - cv2.multiply(img, stdinv, img) # inplace - return img - - -def imdenormalize(img, mean, std, to_bgr=True): - assert img.dtype != np.uint8 - mean = mean.reshape(1, -1).astype(np.float64) - std = std.reshape(1, -1).astype(np.float64) - img = cv2.multiply(img, std) # make a copy - cv2.add(img, mean, img) # inplace - if to_bgr: - cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace - return img - - -def iminvert(img): - """Invert (negate) an image. - - Args: - img (ndarray): Image to be inverted. - - Returns: - ndarray: The inverted image. - """ - return np.full_like(img, 255) - img - - -def solarize(img, thr=128): - """Solarize an image (invert all pixel values above a threshold) - - Args: - img (ndarray): Image to be solarized. - thr (int): Threshold for solarizing (0 - 255). - - Returns: - ndarray: The solarized image. - """ - img = np.where(img < thr, img, 255 - img) - return img - - -def posterize(img, bits): - """Posterize an image (reduce the number of bits for each color channel) - - Args: - img (ndarray): Image to be posterized. - bits (int): Number of bits (1 to 8) to use for posterizing. - - Returns: - ndarray: The posterized image. - """ - shift = 8 - bits - img = np.left_shift(np.right_shift(img, shift), shift) - return img - - -def adjust_color(img, alpha=1, beta=None, gamma=0): - r"""It blends the source image and its gray image: - - .. math:: - output = img * alpha + gray\_img * beta + gamma - - Args: - img (ndarray): The input source image. - alpha (int | float): Weight for the source image. Default 1. - beta (int | float): Weight for the converted gray image. - If None, it's assigned the value (1 - `alpha`). - gamma (int | float): Scalar added to each sum. - Same as :func:`cv2.addWeighted`. Default 0. - - Returns: - ndarray: Colored image which has the same size and dtype as input. - """ - gray_img = bgr2gray(img) - gray_img = np.tile(gray_img[..., None], [1, 1, 3]) - if beta is None: - beta = 1 - alpha - colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) - if not colored_img.dtype == np.uint8: - # Note when the dtype of `img` is not the default `np.uint8` - # (e.g. np.float32), the value in `colored_img` got from cv2 - # is not guaranteed to be in range [0, 255], so here clip - # is needed. - colored_img = np.clip(colored_img, 0, 255) - return colored_img - - -def imequalize(img): - """Equalize the image histogram. - - This function applies a non-linear mapping to the input image, - in order to create a uniform distribution of grayscale values - in the output image. - - Args: - img (ndarray): Image to be equalized. - - Returns: - ndarray: The equalized image. - """ - - def _scale_channel(im, c): - """Scale the data in the corresponding channel.""" - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # For computing the step, filter out the nonzeros. - nonzero_histo = histo[histo > 0] - step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 - if not step: - lut = np.array(range(256)) - else: - # Compute the cumulative sum, shifted by step // 2 - # and then normalized by step. - lut = (np.cumsum(histo) + (step // 2)) // step - # Shift lut, prepending with 0. - lut = np.concatenate([[0], lut[:-1]], 0) - # handle potential integer overflow - lut[lut > 255] = 255 - # If step is zero, return the original image. - # Otherwise, index from lut. - return np.where(np.equal(step, 0), im, lut[im]) - - # Scales each channel independently and then stacks - # the result. - s1 = _scale_channel(img, 0) - s2 = _scale_channel(img, 1) - s3 = _scale_channel(img, 2) - equalized_img = np.stack([s1, s2, s3], axis=-1) - return equalized_img.astype(img.dtype) - - -def adjust_brightness(img, factor=1.): - """Adjust image brightness. - - This function controls the brightness of an image. An - enhancement factor of 0.0 gives a black image. - A factor of 1.0 gives the original image. This function - blends the source image and the degenerated black image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be brightened. - factor (float): A value controls the enhancement. - Factor 1.0 returns the original image, lower - factors mean less color (brightness, contrast, - etc), and higher values more. Default 1. - - Returns: - ndarray: The brightened image. - """ - degenerated = np.zeros_like(img) - # Note manually convert the dtype to np.float32, to - # achieve as close results as PIL.ImageEnhance.Brightness. - # Set beta=1-factor, and gamma=0 - brightened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - brightened_img = np.clip(brightened_img, 0, 255) - return brightened_img.astype(img.dtype) - - -def adjust_contrast(img, factor=1.): - """Adjust image contrast. - - This function controls the contrast of an image. An - enhancement factor of 0.0 gives a solid grey - image. A factor of 1.0 gives the original image. It - blends the source image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be contrasted. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - - Returns: - ndarray: The contrasted image. - """ - gray_img = bgr2gray(img) - hist = np.histogram(gray_img, 256, (0, 255))[0] - mean = round(np.sum(gray_img) / np.sum(hist)) - degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) - degenerated = gray2bgr(degenerated) - contrasted_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - contrasted_img = np.clip(contrasted_img, 0, 255) - return contrasted_img.astype(img.dtype) - - -def auto_contrast(img, cutoff=0): - """Auto adjust image contrast. - - This function maximize (normalize) image contrast by first removing cutoff - percent of the lightest and darkest pixels from the histogram and remapping - the image so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - Args: - img (ndarray): Image to be contrasted. BGR order. - cutoff (int | float | tuple): The cutoff percent of the lightest and - darkest pixels to be removed. If given as tuple, it shall be - (low, high). Otherwise, the single value will be used for both. - Defaults to 0. - - Returns: - ndarray: The contrasted image. - """ - - def _auto_contrast_channel(im, c, cutoff): - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # Remove cut-off percent pixels from histo - histo_sum = np.cumsum(histo) - cut_low = histo_sum[-1] * cutoff[0] // 100 - cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 - histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low - histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) - - # Compute mapping - low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] - # If all the values have been cut off, return the origin img - if low >= high: - return im - scale = 255.0 / (high - low) - offset = -low * scale - lut = np.array(range(256)) - lut = lut * scale + offset - lut = np.clip(lut, 0, 255) - return lut[im] - - if isinstance(cutoff, (int, float)): - cutoff = (cutoff, cutoff) - else: - assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ - f'float or tuple, but got {type(cutoff)} instead.' - # Auto adjusts contrast for each channel independently and then stacks - # the result. - s1 = _auto_contrast_channel(img, 0, cutoff) - s2 = _auto_contrast_channel(img, 1, cutoff) - s3 = _auto_contrast_channel(img, 2, cutoff) - contrasted_img = np.stack([s1, s2, s3], axis=-1) - return contrasted_img.astype(img.dtype) - - -def adjust_sharpness(img, factor=1., kernel=None): - """Adjust image sharpness. - - This function controls the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image. A - factor of 1.0 gives the original image. And a factor - of 2.0 gives a sharpened image. It blends the source - image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be sharpened. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - kernel (np.ndarray, optional): Filter kernel to be applied on the img - to obtain the degenerated img. Defaults to None. - - Note: - No value sanity check is enforced on the kernel set by users. So with - an inappropriate kernel, the ``adjust_sharpness`` may fail to perform - the function its name indicates but end up performing whatever - transform determined by the kernel. - - Returns: - ndarray: The sharpened image. - """ - - if kernel is None: - # adopted from PIL.ImageFilter.SMOOTH - kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 - assert isinstance(kernel, np.ndarray), \ - f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' - assert kernel.ndim == 2, \ - f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' - - degenerated = cv2.filter2D(img, -1, kernel) - sharpened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - sharpened_img = np.clip(sharpened_img, 0, 255) - return sharpened_img.astype(img.dtype) - - -def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): - """AlexNet-style PCA jitter. - - This data augmentation is proposed in `ImageNet Classification with Deep - Convolutional Neural Networks - `_. - - Args: - img (ndarray): Image to be adjusted lighting. BGR order. - eigval (ndarray): the eigenvalue of the convariance matrix of pixel - values, respectively. - eigvec (ndarray): the eigenvector of the convariance matrix of pixel - values, respectively. - alphastd (float): The standard deviation for distribution of alpha. - Defaults to 0.1 - to_rgb (bool): Whether to convert img to rgb. - - Returns: - ndarray: The adjusted image. - """ - assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ - f'eigval and eigvec should both be of type np.ndarray, got ' \ - f'{type(eigval)} and {type(eigvec)} instead.' - - assert eigval.ndim == 1 and eigvec.ndim == 2 - assert eigvec.shape == (3, eigval.shape[0]) - n_eigval = eigval.shape[0] - assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ - f'got {type(alphastd)} instead.' - - img = img.copy().astype(np.float32) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - - alpha = np.random.normal(0, alphastd, n_eigval) - alter = eigvec \ - * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ - * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) - alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) - img_adjusted = img + alter - return img_adjusted - - -def lut_transform(img, lut_table): - """Transform array by look-up table. - - The function lut_transform fills the output array with values from the - look-up table. Indices of the entries are taken from the input array. - - Args: - img (ndarray): Image to be transformed. - lut_table (ndarray): look-up table of 256 elements; in case of - multi-channel input array, the table should either have a single - channel (in this case the same table is used for all channels) or - the same number of channels as in the input array. - - Returns: - ndarray: The transformed image. - """ - assert isinstance(img, np.ndarray) - assert 0 <= np.min(img) and np.max(img) <= 255 - assert isinstance(lut_table, np.ndarray) - assert lut_table.shape == (256, ) - - return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) - - -def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - img (ndarray): Image to be processed. - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - - Returns: - ndarray: The processed image. - """ - assert isinstance(img, np.ndarray) - assert img.ndim == 2 - assert isinstance(clip_limit, (float, int)) - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - - clahe = cv2.createCLAHE(clip_limit, tile_grid_size) - return clahe.apply(np.array(img, dtype=np.uint8)) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/deprecated.json b/cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/deprecated.json deleted file mode 100644 index 25cf6f28c..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/deprecated.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "resnet50_caffe": "detectron/resnet50_caffe", - "resnet50_caffe_bgr": "detectron2/resnet50_caffe_bgr", - "resnet101_caffe": "detectron/resnet101_caffe", - "resnet101_caffe_bgr": "detectron2/resnet101_caffe_bgr" -} diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/mmcls.json b/cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/mmcls.json deleted file mode 100644 index c073a41d0..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/mmcls.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "vgg11": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_batch256_imagenet_20210208-4271cd6c.pth", - "vgg13": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_batch256_imagenet_20210208-4d1d6080.pth", - "vgg16": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_batch256_imagenet_20210208-db26f1a5.pth", - "vgg19": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_batch256_imagenet_20210208-e6920e4a.pth", - "vgg11_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_bn_batch256_imagenet_20210207-f244902c.pth", - "vgg13_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_bn_batch256_imagenet_20210207-1a8b7864.pth", - "vgg16_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_bn_batch256_imagenet_20210208-7e55cd29.pth", - "vgg19_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_bn_batch256_imagenet_20210208-da620c4f.pth", - "resnet18": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet18_8xb32_in1k_20210831-fbbb1da6.pth", - "resnet34": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet34_8xb32_in1k_20210831-f257d4e6.pth", - "resnet50": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_8xb32_in1k_20210831-ea4938fc.pth", - "resnet101": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet101_8xb32_in1k_20210831-539c63f8.pth", - "resnet152": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet152_8xb32_in1k_20210901-4d7582fa.pth", - "resnet50_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d50_b32x8_imagenet_20210531-db14775a.pth", - "resnet101_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d101_b32x8_imagenet_20210531-6e13bcd3.pth", - "resnet152_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d152_b32x8_imagenet_20210531-278cf22a.pth", - "resnext50_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext50_32x4d_b32x8_imagenet_20210429-56066e27.pth", - "resnext101_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x4d_b32x8_imagenet_20210506-e0fa3dd5.pth", - "resnext101_32x8d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x8d_b32x8_imagenet_20210506-23a247d5.pth", - "resnext152_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext152_32x4d_b32x8_imagenet_20210524-927787be.pth", - "se-resnet50": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet50_batch256_imagenet_20200804-ae206104.pth", - "se-resnet101": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet101_batch256_imagenet_20200804-ba5b51d4.pth", - "resnest50": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest50_imagenet_converted-1ebf0afe.pth", - "resnest101": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest101_imagenet_converted-032caa52.pth", - "resnest200": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest200_imagenet_converted-581a60f2.pth", - "resnest269": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest269_imagenet_converted-59930960.pth", - "shufflenet_v1": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v1/shufflenet_v1_batch1024_imagenet_20200804-5d6cec73.pth", - "shufflenet_v2": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v2/shufflenet_v2_batch1024_imagenet_20200812-5bf4721e.pth", - "mobilenet_v2": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v2/mobilenet_v2_batch256_imagenet_20200708-3b2dc3af.pth", - "mobilenet_v3_small": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_small-8427ecf0.pth", - "mobilenet_v3_large": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_large-3ea3c186.pth", - "repvgg_A0": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A0_3rdparty_4xb64-coslr-120e_in1k_20210909-883ab98c.pth", - "repvgg_A1": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A1_3rdparty_4xb64-coslr-120e_in1k_20210909-24003a24.pth", - "repvgg_A2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-A2_3rdparty_4xb64-coslr-120e_in1k_20210909-97d7695a.pth", - "repvgg_B0": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B0_3rdparty_4xb64-coslr-120e_in1k_20210909-446375f4.pth", - "repvgg_B1": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1_3rdparty_4xb64-coslr-120e_in1k_20210909-750cdf67.pth", - "repvgg_B1g2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1g2_3rdparty_4xb64-coslr-120e_in1k_20210909-344f6422.pth", - "repvgg_B1g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B1g4_3rdparty_4xb64-coslr-120e_in1k_20210909-d4c1a642.pth", - "repvgg_B2": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B2_3rdparty_4xb64-coslr-120e_in1k_20210909-bd6b937c.pth", - "repvgg_B2g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B2g4_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-7b7955f0.pth", - "repvgg_B3": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B3_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-dda968bf.pth", - "repvgg_B3g4": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-B3g4_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-4e54846a.pth", - "repvgg_D2se": "https://download.openmmlab.com/mmclassification/v0/repvgg/repvgg-D2se_3rdparty_4xb64-autoaug-lbs-mixup-coslr-200e_in1k_20210909-cf3139b7.pth", - "res2net101_w26": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net101-w26-s4_3rdparty_8xb32_in1k_20210927-870b6c36.pth", - "res2net50_w14": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net50-w14-s8_3rdparty_8xb32_in1k_20210927-bc967bf1.pth", - "res2net50_w26": "https://download.openmmlab.com/mmclassification/v0/res2net/res2net50-w26-s8_3rdparty_8xb32_in1k_20210927-f547a94b.pth", - "swin_tiny": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_tiny_224_b16x64_300e_imagenet_20210616_090925-66df6be6.pth", - "swin_small": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_small_224_b16x64_300e_imagenet_20210615_110219-7f9d988b.pth", - "swin_base": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/convert/swin_base_patch4_window7_224_22kto1k-f967f799.pth", - "swin_large": "https://download.openmmlab.com/mmclassification/v0/swin-transformer/convert/swin_large_patch4_window7_224_22kto1k-5f0996db.pth", - "t2t_vit_t_14": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-14_3rdparty_8xb64_in1k_20210928-b7c09b62.pth", - "t2t_vit_t_19": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-19_3rdparty_8xb64_in1k_20210928-7f1478d5.pth", - "t2t_vit_t_24": "https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-24_3rdparty_8xb64_in1k_20210928-fe95a61b.pth", - "tnt_small": "https://download.openmmlab.com/mmclassification/v0/tnt/tnt-small-p16_3rdparty_in1k_20210903-c56ee7df.pth", - "vit_base_p16": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-98e8652b.pth", - "vit_base_p32": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p32_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-9cea8599.pth", - "vit_large_p16": "https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-large-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-b20ba619.pth" -} diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/open_mmlab.json b/cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/open_mmlab.json deleted file mode 100644 index 8311db4fe..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/open_mmlab.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "vgg16_caffe": "https://download.openmmlab.com/pretrain/third_party/vgg16_caffe-292e1171.pth", - "detectron/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_caffe-788b5fa3.pth", - "detectron2/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_msra-5891d200.pth", - "detectron/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_caffe-3ad79236.pth", - "detectron2/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_msra-6cc46731.pth", - "detectron2/resnext101_32x8d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x8d-1516f1aa.pth", - "resnext50_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext50-32x4d-0ab1a123.pth", - "resnext101_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d-a5af3160.pth", - "resnext101_64x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_64x4d-ee2c6f71.pth", - "contrib/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_thangvubk-ad1730dd.pth", - "detectron/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn-9186a21c.pth", - "detectron/resnet101_gn": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn-cac0ab98.pth", - "jhu/resnet50_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_ws-15beedd8.pth", - "jhu/resnet101_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn_ws-3e3c308c.pth", - "jhu/resnext50_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn_ws-0d87ac85.pth", - "jhu/resnext101_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn_ws-34ac1a9e.pth", - "jhu/resnext50_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn-c7e8b754.pth", - "jhu/resnext101_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn-ac3bb84e.pth", - "msra/hrnetv2_w18_small": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18_small-b5a04e21.pth", - "msra/hrnetv2_w18": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18-00eb2006.pth", - "msra/hrnetv2_w32": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w32-dc9eeb4f.pth", - "msra/hrnetv2_w40": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w40-ed0b031c.pth", - "msra/hrnetv2_w48": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w48-d2186c55.pth", - "bninception_caffe": "https://download.openmmlab.com/pretrain/third_party/bn_inception_caffe-ed2e8665.pth", - "kin400/i3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/i3d_r50_f32s2_k400-2c57e077.pth", - "kin400/nl3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/nl3d_r50_f32s2_k400-fa7e7caa.pth", - "res2net101_v1d_26w_4s": "https://download.openmmlab.com/pretrain/third_party/res2net101_v1d_26w_4s_mmdetv2-f0a600f9.pth", - "regnetx_400mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_400mf-a5b10d96.pth", - "regnetx_800mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_800mf-1f4be4c7.pth", - "regnetx_1.6gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_1.6gf-5791c176.pth", - "regnetx_3.2gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_3.2gf-c2599b0f.pth", - "regnetx_4.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_4.0gf-a88f671e.pth", - "regnetx_6.4gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_6.4gf-006af45d.pth", - "regnetx_8.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_8.0gf-3c68abe7.pth", - "regnetx_12gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_12gf-4c2a3350.pth", - "resnet18_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet18_v1c-b5776b93.pth", - "resnet50_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet50_v1c-2cccc1ad.pth", - "resnet101_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet101_v1c-e67eebb6.pth", - "mmedit/vgg16": "https://download.openmmlab.com/mmediting/third_party/vgg_state_dict.pth", - "mmedit/res34_en_nomixup": "https://download.openmmlab.com/mmediting/third_party/model_best_resnet34_En_nomixup.pth", - "mmedit/mobilenet_v2": "https://download.openmmlab.com/mmediting/third_party/mobilenet_v2.pth", - "contrib/mobilenet_v3_large": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_large-bc2c3fd3.pth", - "contrib/mobilenet_v3_small": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_small-47085aa1.pth", - "resnest50": "https://download.openmmlab.com/pretrain/third_party/resnest50_d2-7497a55b.pth", - "resnest101": "https://download.openmmlab.com/pretrain/third_party/resnest101_d2-f3b931b2.pth", - "resnest200": "https://download.openmmlab.com/pretrain/third_party/resnest200_d2-ca88e41f.pth", - "darknet53": "https://download.openmmlab.com/pretrain/third_party/darknet53-a628ea1b.pth", - "mmdet/mobilenet_v2": "https://download.openmmlab.com/mmdetection/v2.0/third_party/mobilenet_v2_batch256_imagenet-ff34753d.pth" -} diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/torchvision_0.12.json b/cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/torchvision_0.12.json deleted file mode 100644 index 06defe674..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/model_zoo/torchvision_0.12.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "alexnet": "https://download.pytorch.org/models/alexnet-owt-7be5be79.pth", - "densenet121": "https://download.pytorch.org/models/densenet121-a639ec97.pth", - "densenet169": "https://download.pytorch.org/models/densenet169-b2777c0a.pth", - "densenet201": "https://download.pytorch.org/models/densenet201-c1103571.pth", - "densenet161": "https://download.pytorch.org/models/densenet161-8d451a50.pth", - "efficientnet_b0": "https://download.pytorch.org/models/efficientnet_b0_rwightman-3dd342df.pth", - "efficientnet_b1": "https://download.pytorch.org/models/efficientnet_b1_rwightman-533bc792.pth", - "efficientnet_b2": "https://download.pytorch.org/models/efficientnet_b2_rwightman-bcdf34b7.pth", - "efficientnet_b3": "https://download.pytorch.org/models/efficientnet_b3_rwightman-cf984f9c.pth", - "efficientnet_b4": "https://download.pytorch.org/models/efficientnet_b4_rwightman-7eb33cd5.pth", - "efficientnet_b5": "https://download.pytorch.org/models/efficientnet_b5_lukemelas-b6417697.pth", - "efficientnet_b6": "https://download.pytorch.org/models/efficientnet_b6_lukemelas-c76e70fd.pth", - "efficientnet_b7": "https://download.pytorch.org/models/efficientnet_b7_lukemelas-dcc49843.pth", - "googlenet": "https://download.pytorch.org/models/googlenet-1378be20.pth", - "inception_v3_google": "https://download.pytorch.org/models/inception_v3_google-0cc3c7bd.pth", - "mobilenet_v2": "https://download.pytorch.org/models/mobilenet_v2-b0353104.pth", - "mobilenet_v3_large": "https://download.pytorch.org/models/mobilenet_v3_large-8738ca79.pth", - "mobilenet_v3_small": "https://download.pytorch.org/models/mobilenet_v3_small-047dcff4.pth", - "regnet_y_400mf": "https://download.pytorch.org/models/regnet_y_400mf-c65dace8.pth", - "regnet_y_800mf": "https://download.pytorch.org/models/regnet_y_800mf-1b27b58c.pth", - "regnet_y_1_6gf": "https://download.pytorch.org/models/regnet_y_1_6gf-b11a554e.pth", - "regnet_y_3_2gf": "https://download.pytorch.org/models/regnet_y_3_2gf-b5a9779c.pth", - "regnet_y_8gf": "https://download.pytorch.org/models/regnet_y_8gf-d0d0e4a8.pth", - "regnet_y_16gf": "https://download.pytorch.org/models/regnet_y_16gf-9e6ed7dd.pth", - "regnet_y_32gf": "https://download.pytorch.org/models/regnet_y_32gf-4dee3f7a.pth", - "regnet_x_400mf": "https://download.pytorch.org/models/regnet_x_400mf-adf1edd5.pth", - "regnet_x_800mf": "https://download.pytorch.org/models/regnet_x_800mf-ad17e45c.pth", - "regnet_x_1_6gf": "https://download.pytorch.org/models/regnet_x_1_6gf-e3633e7f.pth", - "regnet_x_3_2gf": "https://download.pytorch.org/models/regnet_x_3_2gf-f342aeae.pth", - "regnet_x_8gf": "https://download.pytorch.org/models/regnet_x_8gf-03ceed89.pth", - "regnet_x_16gf": "https://download.pytorch.org/models/regnet_x_16gf-2007eb11.pth", - "regnet_x_32gf": "https://download.pytorch.org/models/regnet_x_32gf-9d47f8d0.pth", - "resnet18": "https://download.pytorch.org/models/resnet18-f37072fd.pth", - "resnet34": "https://download.pytorch.org/models/resnet34-b627a593.pth", - "resnet50": "https://download.pytorch.org/models/resnet50-0676ba61.pth", - "resnet101": "https://download.pytorch.org/models/resnet101-63fe2227.pth", - "resnet152": "https://download.pytorch.org/models/resnet152-394f9c45.pth", - "resnext50_32x4d": "https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth", - "resnext101_32x8d": "https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth", - "wide_resnet50_2": "https://download.pytorch.org/models/wide_resnet50_2-95faca4d.pth", - "wide_resnet101_2": "https://download.pytorch.org/models/wide_resnet101_2-32ee1156.pth", - "shufflenetv2_x0.5": "https://download.pytorch.org/models/shufflenetv2_x0.5-f707e7126e.pth", - "shufflenetv2_x1.0": "https://download.pytorch.org/models/shufflenetv2_x1-5666bf0f80.pth", - "shufflenetv2_x1.5": null, - "shufflenetv2_x2.0": null, - "squeezenet1_0": "https://download.pytorch.org/models/squeezenet1_0-b66bff10.pth", - "squeezenet1_1": "https://download.pytorch.org/models/squeezenet1_1-b8a52dc0.pth", - "vgg11": "https://download.pytorch.org/models/vgg11-8a719046.pth", - "vgg13": "https://download.pytorch.org/models/vgg13-19584684.pth", - "vgg16": "https://download.pytorch.org/models/vgg16-397923af.pth", - "vgg19": "https://download.pytorch.org/models/vgg19-dcbb9e9d.pth", - "vgg11_bn": "https://download.pytorch.org/models/vgg11_bn-6002323d.pth", - "vgg13_bn": "https://download.pytorch.org/models/vgg13_bn-abd245e5.pth", - "vgg16_bn": "https://download.pytorch.org/models/vgg16_bn-6c64b313.pth", - "vgg19_bn": "https://download.pytorch.org/models/vgg19_bn-c79401a0.pth" -} diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/__init__.py deleted file mode 100644 index a12b79cef..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .deprecated_wrappers import Conv2d_deprecated as Conv2d -from .deprecated_wrappers import ConvTranspose2d_deprecated as ConvTranspose2d -from .deprecated_wrappers import Linear_deprecated as Linear -from .deprecated_wrappers import MaxPool2d_deprecated as MaxPool2d -from .focal_loss import (SigmoidFocalLoss, SoftmaxFocalLoss, - sigmoid_focal_loss, softmax_focal_loss) -from .sync_bn import SyncBatchNorm -from .cc_attention import CrissCrossAttention -from .point_sample import * -from .psa_mask import PSAMask, PSAMaskFunction -from .info import * \ No newline at end of file diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/cc_attention.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/cc_attention.py deleted file mode 100644 index 3fd83fcb9..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/cc_attention.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.cnn import PLUGIN_LAYERS, Scale - - -def NEG_INF_DIAG(n, device): - """Returns a diagonal matrix of size [n, n]. - - The diagonal are all "-inf". This is for avoiding calculating the - overlapped element in the Criss-Cross twice. - """ - return torch.diag(torch.tensor(float('-inf')).to(device).repeat(n), 0) - - -@PLUGIN_LAYERS.register_module() -class CrissCrossAttention(nn.Module): - """Criss-Cross Attention Module. - - .. note:: - Before v1.3.13, we use a CUDA op. Since v1.3.13, we switch - to a pure PyTorch and equivalent implementation. For more - details, please refer to https://github.com/open-mmlab/mmcv/pull/1201. - - Speed comparison for one forward pass - - - Input size: [2,512,97,97] - - Device: 1 NVIDIA GeForce RTX 2080 Ti - - +-----------------------+---------------+------------+---------------+ - | |PyTorch version|CUDA version|Relative speed | - +=======================+===============+============+===============+ - |with torch.no_grad() |0.00554402 s |0.0299619 s |5.4x | - +-----------------------+---------------+------------+---------------+ - |no with torch.no_grad()|0.00562803 s |0.0301349 s |5.4x | - +-----------------------+---------------+------------+---------------+ - - Args: - in_channels (int): Channels of the input feature map. - """ - - def __init__(self, in_channels): - super().__init__() - self.query_conv = nn.Conv2d(in_channels, in_channels // 8, 1) - self.key_conv = nn.Conv2d(in_channels, in_channels // 8, 1) - self.value_conv = nn.Conv2d(in_channels, in_channels, 1) - self.gamma = Scale(0.) - self.in_channels = in_channels - - def forward(self, x): - """forward function of Criss-Cross Attention. - - Args: - x (torch.Tensor): Input feature with the shape of - (batch_size, in_channels, height, width). - - Returns: - torch.Tensor: Output of the layer, with the shape of - (batch_size, in_channels, height, width) - """ - B, C, H, W = x.size() - query = self.query_conv(x) - key = self.key_conv(x) - value = self.value_conv(x) - energy_H = torch.einsum('bchw,bciw->bwhi', query, key) + NEG_INF_DIAG( - H, query.device) - energy_H = energy_H.transpose(1, 2) - energy_W = torch.einsum('bchw,bchj->bhwj', query, key) - attn = F.softmax( - torch.cat([energy_H, energy_W], dim=-1), dim=-1) # [B,H,W,(H+W)] - out = torch.einsum('bciw,bhwi->bchw', value, attn[..., :H]) - out += torch.einsum('bchj,bhwj->bchw', value, attn[..., H:]) - - out = self.gamma(out) + x - out = out.contiguous() - - return out - - def __repr__(self): - s = self.__class__.__name__ - s += f'(in_channels={self.in_channels})' - return s diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/README.md b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/README.md deleted file mode 100644 index 3bc020040..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/README.md +++ /dev/null @@ -1,170 +0,0 @@ -# Code Structure of CUDA operators - -This folder contains all non-python code for MMCV custom ops. Please follow the same architecture if you want to add new ops. - -## Directories Tree - -```folder -. -├── common -│ ├── box_iou_rotated_utils.hpp -│ ├── parrots_cpp_helper.hpp -│ ├── parrots_cuda_helper.hpp -│ ├── pytorch_cpp_helper.hpp -│ ├── pytorch_cuda_helper.hpp -│ ├── pytorch_device_registry.hpp -│   └── cuda -│   ├── common_cuda_helper.hpp -│   ├── parrots_cudawarpfunction.cuh -│   ├── ... -│   └── ops_cuda_kernel.cuh -├── onnxruntime -│   ├── onnxruntime_register.h -│   ├── onnxruntime_session_options_config_keys.h -│   ├── ort_mmcv_utils.h -│   ├── ... -│   ├── onnx_ops.h -│   └── cpu -│ ├── onnxruntime_register.cpp -│      ├── ... -│      └── onnx_ops_impl.cpp -├── parrots -│   ├── ... -│   ├── ops.cpp -│   ├── ops_parrots.cpp -│   └── ops_pytorch.h -├── pytorch -│   ├── info.cpp -│   ├── pybind.cpp -│   ├── ... -│   ├── ops.cpp -│   ├── cuda -│   │   ├── ... -│   │   └── ops_cuda.cu -│   └── cpu -│      ├── ... -│      └── ops.cpp -└── tensorrt - ├── trt_cuda_helper.cuh - ├── trt_plugin_helper.hpp - ├── trt_plugin.hpp - ├── trt_serialize.hpp - ├── ... - ├── trt_ops.hpp - └── plugins -    ├── trt_cuda_helper.cu -    ├── trt_plugin.cpp -    ├── ... -    ├── trt_ops.cpp -    └── trt_ops_kernel.cu -``` - -## Components - -- `common`: This directory contains all tools and shared codes. - - `cuda`: The cuda kernels which can be shared by all backends. **HIP** kernel is also here since they have similar syntax. -- `onnxruntime`: **ONNX Runtime** support for custom ops. - - `cpu`: CPU implementation of supported ops. -- `parrots`: **Parrots** is a deep learning frame for model training and inference. Parrots custom ops are placed in this directory. -- `pytorch`: **PyTorch** custom ops are supported by binding C++ to Python with **pybind11**. The ops implementation and binding codes are placed in this directory. - - `cuda`: This directory contains cuda kernel launchers, which feed memory pointers of tensor to the cuda kernel in `common/cuda`. The launchers provide c++ interface of cuda implementation of corresponding custom ops. - - `cpu`: This directory contain cpu implementations of corresponding custom ops. -- `tensorrt`: **TensorRT** support for custom ops. - - `plugins`: This directory contains the implementation of the supported custom ops. Some ops might also use shared cuda kernel in `common/cuda`. - -## How to add new PyTorch ops? - -1. (Optional) Add shared kernel in `common` to support special hardware platform. - - ```c++ - // src/common/cuda/new_ops_cuda_kernel.cuh - - template - __global__ void new_ops_forward_cuda_kernel(const T* input, T* output, ...) { - // forward here - } - - ``` - - Add cuda kernel launcher in `pytorch/cuda`. - - ```c++ - // src/pytorch/cuda - #include - - void NewOpsForwardCUDAKernelLauncher(Tensor input, Tensor output, ...){ - // initialize - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - ... - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "new_ops_forward_cuda_kernel", ([&] { - new_ops_forward_cuda_kernel - <<>>( - input.data_ptr(), output.data_ptr(),...); - })); - AT_CUDA_CHECK(cudaGetLastError()); - } - ``` - -2. Register implementation for different devices. - - ```c++ - // src/pytorch/cuda/cudabind.cpp - ... - - Tensor new_ops_forward_cuda(Tensor input, Tensor output, ...){ - // implement cuda forward here - // use `NewOpsForwardCUDAKernelLauncher` here - } - // declare interface here. - Tensor new_ops_forward_impl(Tensor input, Tensor output, ...); - // register the implementation for given device (CUDA here). - REGISTER_DEVICE_IMPL(new_ops_forward_impl, CUDA, new_ops_forward_cuda); - ``` - -3. Add ops implementation in `pytorch` directory. Select different implementations according to device type. - - ```c++ - // src/pytorch/new_ops.cpp - Tensor new_ops_forward_impl(Tensor input, Tensor output, ...){ - // dispatch the implementation according to the device type of input. - DISPATCH_DEVICE_IMPL(new_ops_forward_impl, input, output, ...); - } - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...){ - return new_ops_forward_impl(input, output, ...); - } - ``` - -4. Binding the implementation in `pytorch/pybind.cpp` - - ```c++ - // src/pytorch/pybind.cpp - - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...); - - ... - - // bind with pybind11 - m.def("new_ops_forward", &new_ops_forward, "new_ops_forward", - py::arg("input"), py::arg("output"), ...); - - ... - - ``` - -5. Build MMCV again. Enjoy new ops in python - - ```python - from ..utils import ext_loader - ext_module = ext_loader.load_ext('_ext', ['new_ops_forward']) - - ... - - ext_module.new_ops_forward(input, output, ...) - - ``` diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp deleted file mode 100644 index e18036bac..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef COMMON_CUDA_HELPER -#define COMMON_CUDA_HELPER - -#include - -#define CUDA_1D_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) - -#define CUDA_2D_KERNEL_LOOP(i, n, j, m) \ - for (size_t i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) \ - for (size_t j = blockIdx.y * blockDim.y + threadIdx.y; j < (m); \ - j += blockDim.y * gridDim.y) - -#define CUDA_2D_KERNEL_BLOCK_LOOP(i, n, j, m) \ - for (size_t i = blockIdx.x; i < (n); i += gridDim.x) \ - for (size_t j = blockIdx.y; j < (m); j += gridDim.y) - -#define THREADS_PER_BLOCK 512 - -inline int GET_BLOCKS(const int N, const int num_threads = THREADS_PER_BLOCK) { - int optimal_block_num = (N + num_threads - 1) / num_threads; - int max_block_num = 4096; - return std::min(optimal_block_num, max_block_num); -} - -template -__device__ T bilinear_interpolate(const T* input, const int height, - const int width, T y, T x, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) return 0; - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - int y_low = (int)y; - int x_low = (int)x; - int y_high; - int x_high; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - // do bilinear interpolation - T v1 = input[y_low * width + x_low]; - T v2 = input[y_low * width + x_high]; - T v3 = input[y_high * width + x_low]; - T v4 = input[y_high * width + x_high]; - T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - return val; -} - -template -__device__ void bilinear_interpolate_gradient( - const int height, const int width, T y, T x, T& w1, T& w2, T& w3, T& w4, - int& x_low, int& x_high, int& y_low, int& y_high, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - w1 = w2 = w3 = w4 = 0.; - x_low = x_high = y_low = y_high = -1; - return; - } - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - y_low = (int)y; - x_low = (int)x; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - - // reference in forward - // T v1 = input[y_low * width + x_low]; - // T v2 = input[y_low * width + x_high]; - // T v3 = input[y_high * width + x_low]; - // T v4 = input[y_high * width + x_high]; - // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - return; -} -#endif // COMMON_CUDA_HELPER diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh deleted file mode 100644 index 5d946686b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/psamask_cuda_kernel.cuh +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef PSAMASK_CUDA_KERNEL_CUH -#define PSAMASK_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -// CUDA: grid stride looping -#ifndef CUDA_KERNEL_LOOP -#define CUDA_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) -#endif - -template -__global__ void psamask_collect_forward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* mask_data, T* buffer_data) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - buffer_data[(n * h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)) * - h_feature * w_feature + - h * w_feature + w] = mask_data - [((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + h) * - w_feature + - w]; - } - } - } -} - -template -__global__ void psamask_distribute_forward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* mask_data, T* buffer_data) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - buffer_data[(n * h_feature * w_feature + h * w_feature + w) * - h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)] = mask_data - [((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + h) * - w_feature + - w]; - } - } - } -} - -template -__global__ void psamask_collect_backward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* buffer_diff, T* mask_diff) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - mask_diff[((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + - h) * - w_feature + - w] = buffer_diff[(n * h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)) * - h_feature * w_feature + - h * w_feature + w]; - } - } - } -} - -template -__global__ void psamask_distribute_backward_cuda( - const int nthreads, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask, const T* buffer_diff, T* mask_diff) { - CUDA_KERNEL_LOOP(index, nthreads) { - const int w = index % w_feature; - const int h = (index / w_feature) % h_feature; - const int n = index / w_feature / h_feature; - // effective mask region : [hstart, hend) x [wstart, wend) with mask-indexed - const int hstart = max(0, half_h_mask - h); - const int hend = min(h_mask, h_feature + half_h_mask - h); - const int wstart = max(0, half_w_mask - w); - const int wend = min(w_mask, w_feature + half_w_mask - w); - // (hidx, widx ) with mask-indexed - // (hidx + h - half_h_mask, widx + w - half_w_mask) with feature-indexed - for (int hidx = hstart; hidx < hend; hidx++) { - for (int widx = wstart; widx < wend; widx++) { - mask_diff[((n * h_mask * w_mask + hidx * w_mask + widx) * h_feature + - h) * - w_feature + - w] = - buffer_diff[(n * h_feature * w_feature + h * w_feature + w) * - h_feature * w_feature + - (hidx + h - half_h_mask) * w_feature + - (widx + w - half_w_mask)]; - } - } - } -} - -#endif // PSAMASK_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 1eb5f8fcc..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sigmoid_focal_loss_forward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* output, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + expf(-input[index])); - - // (1 - p)**gamma * log(p) - T term_p = pow(((T)1. - p), gamma) * log(max(p, (T)FLT_MIN)); - // p**gamma * log(1 - p) - T term_n = pow(p, gamma) * log(max((T)1. - p, (T)FLT_MIN)); - - output[index] = (T)0.; - output[index] += -flag_p * alpha * term_p; - output[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - output[index] *= weight[t]; - } - } -} - -template -__global__ void sigmoid_focal_loss_backward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* grad_input, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + exp(-input[index])); - - // (1 - p)**gamma * (1 - p - gamma*p*log(p)) - T term_p = pow(((T)1. - p), gamma) * - ((T)1. - p - (gamma * p * log(max(p, (T)FLT_MIN)))); - // p**gamma * (gamma * (1 - p) * log(1 - p) - p) - T term_n = pow(p, gamma) * - (gamma * ((T)1. - p) * log(max((T)1. - p, (T)FLT_MIN)) - p); - - grad_input[index] = (T)0.; - grad_input[index] += -flag_p * alpha * term_p; - grad_input[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - grad_input[index] *= weight[t]; - } - } -} - -#endif // SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh deleted file mode 100644 index 631b2c617..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void softmax_focal_loss_forward_cuda_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* output, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - output[index] = - -alpha * pow((T)1. - pred, gamma) * log(max(pred, (T)FLT_MIN)); - } else { - output[index] = 0; - } - if (weight != NULL) { - output[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda1_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* buff, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - buff[index] = alpha * (-pow((T)1. - pred, gamma) + - gamma * pow((T)1. - pred, gamma - 1) * pred * - log(max(pred, (T)FLT_MIN))); - } else { - buff[index] = 0; - } - if (weight != NULL) { - buff[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda2_kernel( - const int nthreads, const T* softmax, const int64_t* target, const T* buff, - T* grad_input, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - int64_t label = target[n]; - - if (label >= 0) { - T flag = (label == c ? (T)1. : (T)0.); - grad_input[index] = buff[n] * (flag - softmax[index]); - } else { - grad_input[index] = 0; - } - } -} - -#endif // SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh deleted file mode 100644 index 4ec6a4668..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SYNCBN_CUDA_KERNEL_CUH -#define SYNCBN_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sync_bn_forward_mean_cuda_kernel(const T *input, float *mean, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += input[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_mean_cuda_kernel(const phalf *input, - float *mean, int num, - int channels, int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += static_cast(input[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_var_cuda_kernel(const T *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = input[index] - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_var_cuda_kernel(const phalf *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = static_cast(input[index]) - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_output_cuda_kernel( - const T *input, const float *mean, const float *var, float *running_mean, - float *running_var, const float *weight, const float *bias, float *norm, - float *std, T *output, int num, int channels, int spatial, float eps, - float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = (input[index] - mean_value) / std_value; - output[index] = norm[index] * weight_value + bias_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - (input[index] - mean_value) / std_value * weight_value + bias_value; - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = norm[index] = (input[index] - mean_value) / std_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = (input[index] - mean_value) / std_value; - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template <> -__global__ void sync_bn_forward_output_cuda_kernel( - const phalf *input, const float *mean, const float *var, - float *running_mean, float *running_var, const float *weight, - const float *bias, float *norm, float *std, phalf *output, int num, - int channels, int spatial, float eps, float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = - static_cast(norm[index] * weight_value + bias_value); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - static_cast((static_cast(input[index]) - mean_value) / - std_value * weight_value + - bias_value); - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = static_cast(norm[index]); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = static_cast( - (static_cast(input[index]) - mean_value) / std_value); - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template -__global__ void sync_bn_backward_param_cuda_kernel(const T *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += grad_output[index] * norm[index]; - buffer2[tid] += grad_output[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template <> -__global__ void sync_bn_backward_param_cuda_kernel(const phalf *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += static_cast(grad_output[index]) * norm[index]; - buffer2[tid] += static_cast(grad_output[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const T *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, T *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = - weight[c] * - (grad_output[index] - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]; - } -} - -template <> -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const phalf *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, phalf *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = static_cast( - weight[c] * - (static_cast(grad_output[index]) - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]); - } -} - -#endif // SYNCBN_CUDA_KERNEL_CUH diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp deleted file mode 100644 index f68e87405..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef PYTORCH_CPP_HELPER -#define PYTORCH_CPP_HELPER -#include - -#include - -using namespace at; - -#define CHECK_CUDA(x) \ - TORCH_CHECK(x.device().is_cuda(), #x " must be a CUDA tensor") -#define CHECK_MLU(x) \ - TORCH_CHECK(x.device().type() == at::kMLU, #x " must be a MLU tensor") -#define CHECK_CPU(x) \ - TORCH_CHECK(x.device().type() == at::kCPU, #x " must be a CPU tensor") -#define CHECK_CONTIGUOUS(x) \ - TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") -#define CHECK_CUDA_INPUT(x) \ - CHECK_CUDA(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_MLU_INPUT(x) \ - CHECK_MLU(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_CPU_INPUT(x) \ - CHECK_CPU(x); \ - CHECK_CONTIGUOUS(x) - -#endif // PYTORCH_CPP_HELPER diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp deleted file mode 100644 index 9869b535f..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PYTORCH_CUDA_HELPER -#define PYTORCH_CUDA_HELPER - -#include -#include -#include - -#include -#include - -#include "common_cuda_helper.hpp" - -using at::Half; -using at::Tensor; -using phalf = at::Half; - -#define __PHALF(x) (x) - -#endif // PYTORCH_CUDA_HELPER diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp deleted file mode 100644 index 2a32b7270..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/common/pytorch_device_registry.hpp +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef PYTORCH_DEVICE_REGISTRY_H -#define PYTORCH_DEVICE_REGISTRY_H - -// Using is recommended in the official documentation in -// https://pytorch.org/tutorials/advanced/cpp_extension.html#writing-the-c-op. -// However, we use for compatibility with CUDA 9.0 -// Read https://github.com/pytorch/extension-cpp/issues/35 for more details. -#include - -#include -#include -#include -#include - -inline std::string GetDeviceStr(const at::Device& device) { - std::string str = DeviceTypeName(device.type(), true); - if (device.has_index()) { - str.push_back(':'); - str.append(std::to_string(device.index())); - } - return str; -} - -// Registry -template -class DeviceRegistry; - -template -class DeviceRegistry { - public: - using FunctionType = Ret (*)(Args...); - static const int MAX_DEVICE_TYPES = - int8_t(at::DeviceType::COMPILE_TIME_MAX_DEVICE_TYPES); - - void Register(at::DeviceType device, FunctionType function) { - funcs_[int8_t(device)] = function; - } - - FunctionType Find(at::DeviceType device) const { - return funcs_[int8_t(device)]; - } - - static DeviceRegistry& instance() { - static DeviceRegistry inst; - return inst; - } - - private: - DeviceRegistry() { - for (size_t i = 0; i < MAX_DEVICE_TYPES; ++i) { - funcs_[i] = nullptr; - } - }; - FunctionType funcs_[MAX_DEVICE_TYPES]; -}; - -// get device of first tensor param - -template , at::Tensor>::value, - bool> = true> -at::Device GetFirstTensorDevice(T&& t, Args&&... args) { - return std::forward(t).device(); -} -template , at::Tensor>::value, - bool> = true> -at::Device GetFirstTensorDevice(T&& t, Args&&... args) { - return GetFirstTensorDevice(std::forward(args)...); -} - -// check device consistency - -inline std::pair CheckDeviceConsistency( - const at::Device& device, int index) { - return {index, device}; -} - -template , at::Tensor>::value, - bool> = true> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args); - -template , at::Tensor>::value, - bool> = true> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args) { - auto new_device = std::forward(t).device(); - if (new_device.type() != device.type() || - new_device.index() != device.index()) { - return {index, new_device}; - } - return CheckDeviceConsistency(device, index + 1, std::forward(args)...); -} - -template < - typename T, typename... Args, - std::enable_if_t, at::Tensor>::value, bool>> -std::pair CheckDeviceConsistency(const at::Device& device, - int index, T&& t, - Args&&... args) { - return CheckDeviceConsistency(device, index + 1, std::forward(args)...); -} - -// dispatch - -template -auto Dispatch(const R& registry, const char* name, Args&&... args) { - auto device = GetFirstTensorDevice(std::forward(args)...); - auto inconsist = - CheckDeviceConsistency(device, 0, std::forward(args)...); - TORCH_CHECK(inconsist.first >= int(sizeof...(Args)), name, ": at param ", - inconsist.first, - ", inconsistent device: ", GetDeviceStr(inconsist.second).c_str(), - " vs ", GetDeviceStr(device).c_str(), "\n") - auto f_ptr = registry.Find(device.type()); - TORCH_CHECK(f_ptr != nullptr, name, ": implementation for device ", - GetDeviceStr(device).c_str(), " not found.\n") - return f_ptr(std::forward(args)...); -} - -// helper macro - -#define DEVICE_REGISTRY(key) DeviceRegistry::instance() - -#define REGISTER_DEVICE_IMPL(key, device, value) \ - struct key##_##device##_registerer { \ - key##_##device##_registerer() { \ - DEVICE_REGISTRY(key).Register(at::k##device, value); \ - } \ - }; \ - static key##_##device##_registerer _##key##_##device##_registerer; - -#define DISPATCH_DEVICE_IMPL(key, ...) \ - Dispatch(DEVICE_REGISTRY(key), #key, __VA_ARGS__) - -#endif // PYTORCH_DEVICE_REGISTRY diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp deleted file mode 100644 index dc376bb51..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/cudabind.cpp +++ /dev/null @@ -1,210 +0,0 @@ -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha); - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha); - -void sigmoid_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SigmoidFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void sigmoid_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - SigmoidFocalLossBackwardCUDAKernelLauncher(input, target, weight, grad_input, - gamma, alpha); -} - -void softmax_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SoftmaxFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void softmax_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - SoftmaxFocalLossBackwardCUDAKernelLauncher(input, target, weight, buff, - grad_input, gamma, alpha); -} - -void sigmoid_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha); - -void softmax_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha); - -REGISTER_DEVICE_IMPL(sigmoid_focal_loss_forward_impl, CUDA, - sigmoid_focal_loss_forward_cuda); -REGISTER_DEVICE_IMPL(sigmoid_focal_loss_backward_impl, CUDA, - sigmoid_focal_loss_backward_cuda); -REGISTER_DEVICE_IMPL(softmax_focal_loss_forward_impl, CUDA, - softmax_focal_loss_forward_cuda); -REGISTER_DEVICE_IMPL(softmax_focal_loss_backward_impl, CUDA, - softmax_focal_loss_backward_cuda); - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean); - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var); - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size); - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias); - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -void sync_bn_forward_mean_cuda(const Tensor input, Tensor mean) { - SyncBNForwardMeanCUDAKernelLauncher(input, mean); -} - -void sync_bn_forward_var_cuda(const Tensor input, const Tensor mean, - Tensor var) { - SyncBNForwardVarCUDAKernelLauncher(input, mean, var); -} - -void sync_bn_forward_output_cuda(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - SyncBNForwardOutputCUDAKernelLauncher(input, mean, var, running_mean, - running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_cuda(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - SyncBNBackwardParamCUDAKernelLauncher(grad_output, norm, grad_weight, - grad_bias); -} - -void sync_bn_backward_data_cuda(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - SyncBNBackwardDataCUDAKernelLauncher(grad_output, weight, grad_weight, - grad_bias, norm, std, grad_input); -} - -void sync_bn_forward_mean_impl(const Tensor input, Tensor mean); - -void sync_bn_forward_var_impl(const Tensor input, const Tensor mean, - Tensor var); - -void sync_bn_forward_output_impl(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param_impl(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data_impl(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input); - -REGISTER_DEVICE_IMPL(sync_bn_forward_mean_impl, CUDA, - sync_bn_forward_mean_cuda); -REGISTER_DEVICE_IMPL(sync_bn_forward_var_impl, CUDA, sync_bn_forward_var_cuda); -REGISTER_DEVICE_IMPL(sync_bn_forward_output_impl, CUDA, - sync_bn_forward_output_cuda); -REGISTER_DEVICE_IMPL(sync_bn_backward_param_impl, CUDA, - sync_bn_backward_param_cuda); -REGISTER_DEVICE_IMPL(sync_bn_backward_data_impl, CUDA, - sync_bn_backward_data_cuda); - - - -void PSAMaskForwardCUDAKernelLauncher(const int psa_type, const Tensor input, - Tensor output, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, - const int half_w_mask); - -void PSAMaskBackwardCUDAKernelLauncher( - const int psa_type, const Tensor grad_output, Tensor grad_input, - const int num_, const int h_feature, const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, const int half_w_mask); - -void psamask_forward_cuda(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask) { - PSAMaskForwardCUDAKernelLauncher(psa_type, input, output, num_, h_feature, - w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_backward_cuda(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - PSAMaskBackwardCUDAKernelLauncher(psa_type, grad_output, grad_input, num_, - h_feature, w_feature, h_mask, w_mask, - half_h_mask, half_w_mask); -} - -void psamask_forward_impl(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask); - -void psamask_backward_impl(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask); -REGISTER_DEVICE_IMPL(psamask_forward_impl, CUDA, psamask_forward_cuda); -REGISTER_DEVICE_IMPL(psamask_backward_impl, CUDA, psamask_backward_cuda); diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu deleted file mode 100644 index cb899f954..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sigmoid_focal_loss_cuda_kernel.cuh" -#include "softmax_focal_loss_cuda_kernel.cuh" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = input.size(1); - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_forward_cuda_kernel", [&] { - sigmoid_focal_loss_forward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha) { - int output_size = grad_input.numel(); - int num_classes = input.size(1); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_backward_cuda_kernel", [&] { - sigmoid_focal_loss_backward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - grad_input.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = softmax.size(1); - - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(softmax.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - softmax.scalar_type(), "softmax_focal_loss_forward_cuda_kernel", [&] { - softmax_focal_loss_forward_cuda_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha) { - int num_classes = softmax.size(1); - - int output_size = buff.numel(); - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda1_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda1_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - buff.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); - - output_size = grad_input.numel(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda2_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda2_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), buff.data_ptr(), - grad_input.data_ptr(), num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu deleted file mode 100644 index a0bdfa60c..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/psamask_cuda.cu +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// Modified from -// https://github.com/hszhao/semseg/blob/master/lib/psa/src - -#include - -#include "psamask_cuda_kernel.cuh" -#include "pytorch_cuda_helper.hpp" - -void PSAMaskForwardCUDAKernelLauncher(const int psa_type, const Tensor input, - Tensor output, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, - const int half_w_mask) { - int nthreads = num_ * h_feature * w_feature; - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - if (psa_type == 0) - AT_DISPATCH_FLOATING_TYPES( - input.scalar_type(), "psamask_collect_forward_cuda", [&] { - psamask_collect_forward_cuda<<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, input.data_ptr(), - output.data_ptr()); - }); - else - AT_DISPATCH_FLOATING_TYPES( - input.scalar_type(), "psamask_distribute_forward_cuda", [&] { - psamask_distribute_forward_cuda - <<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, input.data_ptr(), - output.data_ptr()); - }); -} - -void PSAMaskBackwardCUDAKernelLauncher( - const int psa_type, const Tensor grad_output, Tensor grad_input, - const int num_, const int h_feature, const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, const int half_w_mask) { - int nthreads = num_ * h_feature * w_feature; - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - if (psa_type == 0) - AT_DISPATCH_FLOATING_TYPES( - grad_input.scalar_type(), "psamask_collect_backward_cuda", [&] { - psamask_collect_backward_cuda<<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, grad_output.data_ptr(), - grad_input.data_ptr()); - }); - else - AT_DISPATCH_FLOATING_TYPES( - grad_input.scalar_type(), "psamask_distribute_backward_cuda", [&] { - psamask_distribute_backward_cuda - <<>>( - nthreads, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask, grad_output.data_ptr(), - grad_input.data_ptr()); - }); -} diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu deleted file mode 100644 index 657c81701..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sync_bn_cuda_kernel.cuh" - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_mean_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_var_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), num, channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_output_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), running_mean.data_ptr(), - running_var.data_ptr(), weight.data_ptr(), - bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), output.data_ptr(), num, - channels, spatial, eps, momentum, group_size); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias) { - int num = grad_output.size(0); - int channels = grad_output.size(1); - int spatial = grad_output.size(2); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_param_cuda_kernel", [&] { - sync_bn_backward_param_cuda_kernel - <<>>( - grad_output.data_ptr(), norm.data_ptr(), - grad_weight.data_ptr(), grad_bias.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - int output_size = grad_input.numel(); - int num = grad_input.size(0); - int channels = grad_input.size(1); - int spatial = grad_input.size(2); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_data_cuda_kernel", [&] { - sync_bn_backward_data_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - weight.data_ptr(), grad_weight.data_ptr(), - grad_bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), grad_input.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp deleted file mode 100644 index ed0e21865..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void sigmoid_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(sigmoid_focal_loss_forward_impl, input, target, weight, - output, gamma, alpha); -} - -void sigmoid_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(sigmoid_focal_loss_backward_impl, input, target, weight, - grad_input, gamma, alpha); -} - -void softmax_focal_loss_forward_impl(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - DISPATCH_DEVICE_IMPL(softmax_focal_loss_forward_impl, input, target, weight, - output, gamma, alpha); -} - -void softmax_focal_loss_backward_impl(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - DISPATCH_DEVICE_IMPL(softmax_focal_loss_backward_impl, input, target, weight, - buff, grad_input, gamma, alpha); -} - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - sigmoid_focal_loss_forward_impl(input, target, weight, output, gamma, alpha); -} - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha) { - sigmoid_focal_loss_backward_impl(input, target, weight, grad_input, gamma, - alpha); -} - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - softmax_focal_loss_forward_impl(input, target, weight, output, gamma, alpha); -} - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha) { - softmax_focal_loss_backward_impl(input, target, weight, buff, grad_input, - gamma, alpha); -} diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/info.cpp b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/info.cpp deleted file mode 100644 index a08d227d4..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/info.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// modified from -// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/vision.cpp -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF -#include -int get_cudart_version() { return CUDART_VERSION; } -#endif -#endif - -std::string get_compiling_cuda_version() { -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF - std::ostringstream oss; - // copied from - // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/cuda/detail/CUDAHooks.cpp#L231 - auto printCudaStyleVersion = [&](int v) { - oss << (v / 1000) << "." << (v / 10 % 100); - if (v % 10 != 0) { - oss << "." << (v % 10); - } - }; - printCudaStyleVersion(get_cudart_version()); - return oss.str(); -#else - return std::string("rocm not available"); -#endif -#else - return std::string("not available"); -#endif -} - -// similar to -// https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/Version.cpp -std::string get_compiler_version() { - std::ostringstream ss; -#if defined(__GNUC__) -#ifndef __clang__ - { ss << "GCC " << __GNUC__ << "." << __GNUC_MINOR__; } -#endif -#endif - -#if defined(__clang_major__) - { - ss << "clang " << __clang_major__ << "." << __clang_minor__ << "." - << __clang_patchlevel__; - } -#endif - -#if defined(_MSC_VER) - { ss << "MSVC " << _MSC_FULL_VER; } -#endif - return ss.str(); -} diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp deleted file mode 100644 index 6064c9ba5..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/psamask.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// Modified from -// https://github.com/hszhao/semseg/blob/master/lib/psa/src -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void psamask_forward_impl(const int psa_type, const Tensor input, Tensor output, - const int num_, const int h_feature, - const int w_feature, const int h_mask, - const int w_mask, const int half_h_mask, - const int half_w_mask) { - DISPATCH_DEVICE_IMPL(psamask_forward_impl, psa_type, input, output, num_, - h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_backward_impl(const int psa_type, const Tensor grad_output, - Tensor grad_input, const int num_, - const int h_feature, const int w_feature, - const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - DISPATCH_DEVICE_IMPL(psamask_backward_impl, psa_type, grad_output, grad_input, - num_, h_feature, w_feature, h_mask, w_mask, half_h_mask, - half_w_mask); -} - -void psamask_forward(const Tensor input, Tensor output, const int psa_type, - const int num_, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask) { - psamask_forward_impl(psa_type, input, output, num_, h_feature, w_feature, - h_mask, w_mask, half_h_mask, half_w_mask); -} - -void psamask_backward(Tensor grad_output, const Tensor grad_input, - const int psa_type, const int num_, const int h_feature, - const int w_feature, const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask) { - psamask_backward_impl(psa_type, grad_output, grad_input, num_, h_feature, - w_feature, h_mask, w_mask, half_h_mask, half_w_mask); -} diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp deleted file mode 100644 index 1e99174d8..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include - -#include "pytorch_cpp_helper.hpp" - -std::string get_compiler_version(); -std::string get_compiling_cuda_version(); - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha); - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha); - -void sync_bn_forward_mean(const Tensor input, Tensor mean); - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var); - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - - -void psamask_forward(const Tensor input, Tensor output, const int psa_type, - const int num_, const int h_feature, const int w_feature, - const int h_mask, const int w_mask, const int half_h_mask, - const int half_w_mask); - -void psamask_backward(Tensor grad_output, const Tensor grad_input, - const int psa_type, const int num_, const int h_feature, - const int w_feature, const int h_mask, const int w_mask, - const int half_h_mask, const int half_w_mask); - - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - m.def("get_compiler_version", &get_compiler_version, "get_compiler_version"); - m.def("get_compiling_cuda_version", &get_compiling_cuda_version, - "get_compiling_cuda_version"); - - - m.def("sigmoid_focal_loss_forward", &sigmoid_focal_loss_forward, - "sigmoid_focal_loss_forward ", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("sigmoid_focal_loss_backward", &sigmoid_focal_loss_backward, - "sigmoid_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("grad_input"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_forward", &softmax_focal_loss_forward, - "softmax_focal_loss_forward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_backward", &softmax_focal_loss_backward, - "softmax_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("buff"), py::arg("grad_input"), - py::arg("gamma"), py::arg("alpha")); - - // SyncBN - m.def("sync_bn_forward_mean", &sync_bn_forward_mean, "sync_bn forward_mean", - py::arg("input"), py::arg("mean")); - m.def("sync_bn_forward_var", &sync_bn_forward_var, "sync_bn forward_var", - py::arg("input"), py::arg("mean"), py::arg("var")); - m.def("sync_bn_forward_output", &sync_bn_forward_output, - "sync_bn forward_output", py::arg("input"), py::arg("mean"), - py::arg("var"), py::arg("weight"), py::arg("bias"), - py::arg("running_mean"), py::arg("running_var"), py::arg("norm"), - py::arg("std"), py::arg("output"), py::arg("eps"), py::arg("momentum"), - py::arg("group_size")); - m.def("sync_bn_backward_param", &sync_bn_backward_param, - "sync_bn backward_param", py::arg("grad_output"), py::arg("norm"), - py::arg("grad_weight"), py::arg("grad_bias")); - m.def("sync_bn_backward_data", &sync_bn_backward_data, - "sync_bn backward_data", py::arg("grad_output"), py::arg("weight"), - py::arg("grad_weight"), py::arg("grad_bias"), py::arg("norm"), - py::arg("std"), py::arg("grad_input")); - - // PASMask - m.def("psamask_forward", &psamask_forward, "PSAMASK forward (CPU/CUDA)", - py::arg("input"), py::arg("output"), py::arg("psa_type"), - py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), - py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), - py::arg("half_w_mask")); - m.def("psamask_backward", &psamask_backward, "PSAMASK backward (CPU/CUDA)", - py::arg("grad_output"), py::arg("grad_input"), py::arg("psa_type"), - py::arg("num_"), py::arg("h_feature"), py::arg("w_feature"), - py::arg("h_mask"), py::arg("w_mask"), py::arg("half_h_mask"), - py::arg("half_w_mask")); -} diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp deleted file mode 100644 index fd5a51327..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" -#include "pytorch_device_registry.hpp" - -void sync_bn_forward_mean_impl(const Tensor input, Tensor mean) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_mean_impl, input, mean); -} - -void sync_bn_forward_var_impl(const Tensor input, const Tensor mean, - Tensor var) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_var_impl, input, mean, var); -} - -void sync_bn_forward_output_impl(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - DISPATCH_DEVICE_IMPL(sync_bn_forward_output_impl, input, mean, var, - running_mean, running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_impl(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - DISPATCH_DEVICE_IMPL(sync_bn_backward_param_impl, grad_output, norm, - grad_weight, grad_bias); -} - -void sync_bn_backward_data_impl(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - DISPATCH_DEVICE_IMPL(sync_bn_backward_data_impl, grad_output, weight, - grad_weight, grad_bias, norm, std, grad_input); -} - -void sync_bn_forward_mean(const Tensor input, Tensor mean) { - sync_bn_forward_mean_impl(input, mean); -} - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var) { - sync_bn_forward_var_impl(input, mean, var); -} - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - sync_bn_forward_output_impl(input, mean, var, running_mean, running_var, - weight, bias, norm, std, output, eps, momentum, - group_size); -} - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - sync_bn_backward_param_impl(grad_output, norm, grad_weight, grad_bias); -} - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - sync_bn_backward_data_impl(grad_output, weight, grad_weight, grad_bias, norm, - std, grad_input); -} diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/deprecated_wrappers.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/deprecated_wrappers.py deleted file mode 100644 index 629a8033f..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/deprecated_wrappers.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# This file is for backward compatibility. -# Module wrappers for empty tensor have been moved to mmcv.cnn.bricks. -import warnings - -from ..cnn.bricks.wrappers import Conv2d, ConvTranspose2d, Linear, MaxPool2d - - -class Conv2d_deprecated(Conv2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Conv2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) - - -class ConvTranspose2d_deprecated(ConvTranspose2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing ConvTranspose2d wrapper from "mmcv.ops" will be ' - 'deprecated in the future. Please import them from "mmcv.cnn" ' - 'instead', DeprecationWarning) - - -class MaxPool2d_deprecated(MaxPool2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing MaxPool2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) - - -class Linear_deprecated(Linear): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Linear wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead', - DeprecationWarning) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/focal_loss.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/focal_loss.py deleted file mode 100644 index 805860516..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/focal_loss.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sigmoid_focal_loss_forward', 'sigmoid_focal_loss_backward', - 'softmax_focal_loss_forward', 'softmax_focal_loss_backward' -]) - - -class SigmoidFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSigmoidFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance( - target, (torch.Tensor, torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - output = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_forward( - input, target, weight, output, gamma=ctx.gamma, alpha=ctx.alpha) - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input, target, weight) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - input, target, weight = ctx.saved_tensors - - grad_input = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_backward( - input, - target, - weight, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input.size(0) - return grad_input, None, None, None, None, None - - -sigmoid_focal_loss = SigmoidFocalLossFunction.apply - - -class SigmoidFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SigmoidFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return sigmoid_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s - - -class SoftmaxFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSoftmaxFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - channel_stats, _ = torch.max(input, dim=1) - input_softmax = input - channel_stats.unsqueeze(1).expand_as(input) - input_softmax.exp_() - - channel_stats = input_softmax.sum(dim=1) - input_softmax /= channel_stats.unsqueeze(1).expand_as(input) - - output = input.new_zeros(input.size(0)) - ext_module.softmax_focal_loss_forward( - input_softmax, - target, - weight, - output, - gamma=ctx.gamma, - alpha=ctx.alpha) - - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input_softmax, target, weight) - return output - - @staticmethod - def backward(ctx, grad_output): - input_softmax, target, weight = ctx.saved_tensors - buff = input_softmax.new_zeros(input_softmax.size(0)) - grad_input = input_softmax.new_zeros(input_softmax.size()) - - ext_module.softmax_focal_loss_backward( - input_softmax, - target, - weight, - buff, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input_softmax.size(0) - return grad_input, None, None, None, None, None - - -softmax_focal_loss = SoftmaxFocalLossFunction.apply - - -class SoftmaxFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SoftmaxFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return softmax_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/info.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/info.py deleted file mode 100644 index 29f2e5598..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/info.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os - -import torch - -if torch.__version__ == 'parrots': - import parrots - - def get_compiler_version(): - return 'GCC ' + parrots.version.compiler - - def get_compiling_cuda_version(): - return parrots.version.cuda -else: - from ..utils import ext_loader - ext_module = ext_loader.load_ext( - '_ext', ['get_compiler_version', 'get_compiling_cuda_version']) - - def get_compiler_version(): - return ext_module.get_compiler_version() - - def get_compiling_cuda_version(): - return ext_module.get_compiling_cuda_version() - - -def get_onnxruntime_op_path(): - wildcard = os.path.join( - os.path.abspath(os.path.dirname(os.path.dirname(__file__))), - '_ext_ort.*.so') - - paths = glob.glob(wildcard) - if len(paths) > 0: - return paths[0] - else: - return '' diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/point_sample.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/point_sample.py deleted file mode 100644 index 9a70b28e2..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/point_sample.py +++ /dev/null @@ -1,346 +0,0 @@ -# Modified from https://github.com/facebookresearch/detectron2/tree/master/projects/PointRend # noqa - -from os import path as osp - -import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.nn.modules.utils import _pair -from torch.onnx.operators import shape_as_tensor - - -def bilinear_grid_sample(im, grid, align_corners=False): - """Given an input and a flow-field grid, computes the output using input - values and pixel locations from grid. Supported only bilinear interpolation - method to sample the input pixels. - - Args: - im (torch.Tensor): Input feature map, shape (N, C, H, W) - grid (torch.Tensor): Point coordinates, shape (N, Hg, Wg, 2) - align_corners {bool}: If set to True, the extrema (-1 and 1) are - considered as referring to the center points of the input’s - corner pixels. If set to False, they are instead considered as - referring to the corner points of the input’s corner pixels, - making the sampling more resolution agnostic. - - Returns: - torch.Tensor: A tensor with sampled points, shape (N, C, Hg, Wg) - """ - n, c, h, w = im.shape - gn, gh, gw, _ = grid.shape - assert n == gn - - x = grid[:, :, :, 0] - y = grid[:, :, :, 1] - - if align_corners: - x = ((x + 1) / 2) * (w - 1) - y = ((y + 1) / 2) * (h - 1) - else: - x = ((x + 1) * w - 1) / 2 - y = ((y + 1) * h - 1) / 2 - - x = x.view(n, -1) - y = y.view(n, -1) - - x0 = torch.floor(x).long() - y0 = torch.floor(y).long() - x1 = x0 + 1 - y1 = y0 + 1 - - wa = ((x1 - x) * (y1 - y)).unsqueeze(1) - wb = ((x1 - x) * (y - y0)).unsqueeze(1) - wc = ((x - x0) * (y1 - y)).unsqueeze(1) - wd = ((x - x0) * (y - y0)).unsqueeze(1) - - # Apply default for grid_sample function zero padding - im_padded = F.pad(im, pad=[1, 1, 1, 1], mode='constant', value=0) - padded_h = h + 2 - padded_w = w + 2 - # save points positions after padding - x0, x1, y0, y1 = x0 + 1, x1 + 1, y0 + 1, y1 + 1 - - # Clip coordinates to padded image size - x0 = torch.where(x0 < 0, torch.tensor(0), x0) - x0 = torch.where(x0 > padded_w - 1, torch.tensor(padded_w - 1), x0) - x1 = torch.where(x1 < 0, torch.tensor(0), x1) - x1 = torch.where(x1 > padded_w - 1, torch.tensor(padded_w - 1), x1) - y0 = torch.where(y0 < 0, torch.tensor(0), y0) - y0 = torch.where(y0 > padded_h - 1, torch.tensor(padded_h - 1), y0) - y1 = torch.where(y1 < 0, torch.tensor(0), y1) - y1 = torch.where(y1 > padded_h - 1, torch.tensor(padded_h - 1), y1) - - im_padded = im_padded.view(n, c, -1) - - x0_y0 = (x0 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x0_y1 = (x0 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y0 = (x1 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y1 = (x1 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - - Ia = torch.gather(im_padded, 2, x0_y0) - Ib = torch.gather(im_padded, 2, x0_y1) - Ic = torch.gather(im_padded, 2, x1_y0) - Id = torch.gather(im_padded, 2, x1_y1) - - return (Ia * wa + Ib * wb + Ic * wc + Id * wd).reshape(n, c, gh, gw) - - -def is_in_onnx_export_without_custom_ops(): - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - return torch.onnx.is_in_onnx_export( - ) and not osp.exists(ort_custom_op_path) - - -def normalize(grid): - """Normalize input grid from [-1, 1] to [0, 1] - - Args: - grid (torch.Tensor): The grid to be normalize, range [-1, 1]. - - Returns: - torch.Tensor: Normalized grid, range [0, 1]. - """ - - return (grid + 1.0) / 2.0 - - -def denormalize(grid): - """Denormalize input grid from range [0, 1] to [-1, 1] - - Args: - grid (torch.Tensor): The grid to be denormalize, range [0, 1]. - - Returns: - torch.Tensor: Denormalized grid, range [-1, 1]. - """ - - return grid * 2.0 - 1.0 - - -def generate_grid(num_grid, size, device): - """Generate regular square grid of points in [0, 1] x [0, 1] coordinate - space. - - Args: - num_grid (int): The number of grids to sample, one for each region. - size (tuple[int, int]): The side size of the regular grid. - device (torch.device): Desired device of returned tensor. - - Returns: - torch.Tensor: A tensor of shape (num_grid, size[0]*size[1], 2) that - contains coordinates for the regular grids. - """ - - affine_trans = torch.tensor([[[1., 0., 0.], [0., 1., 0.]]], device=device) - grid = F.affine_grid( - affine_trans, torch.Size((1, 1, *size)), align_corners=False) - grid = normalize(grid) - return grid.view(1, -1, 2).expand(num_grid, -1, -1) - - -def rel_roi_point_to_abs_img_point(rois, rel_roi_points): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (torch.Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (torch.Tensor): Point coordinates inside RoI, relative - to RoI, location, range (0, 1), shape (N, P, 2) - Returns: - torch.Tensor: Image based absolute point coordinates, shape (N, P, 2) - """ - - with torch.no_grad(): - assert rel_roi_points.size(0) == rois.size(0) - assert rois.dim() == 2 - assert rel_roi_points.dim() == 3 - assert rel_roi_points.size(2) == 2 - # remove batch idx - if rois.size(1) == 5: - rois = rois[:, 1:] - abs_img_points = rel_roi_points.clone() - # To avoid an error during exporting to onnx use independent - # variables instead inplace computation - xs = abs_img_points[:, :, 0] * (rois[:, None, 2] - rois[:, None, 0]) - ys = abs_img_points[:, :, 1] * (rois[:, None, 3] - rois[:, None, 1]) - xs += rois[:, None, 0] - ys += rois[:, None, 1] - abs_img_points = torch.stack([xs, ys], dim=2) - return abs_img_points - - -def get_shape_from_feature_map(x): - """Get spatial resolution of input feature map considering exporting to - onnx mode. - - Args: - x (torch.Tensor): Input tensor, shape (N, C, H, W) - - Returns: - torch.Tensor: Spatial resolution (width, height), shape (1, 1, 2) - """ - if torch.onnx.is_in_onnx_export(): - img_shape = shape_as_tensor(x)[2:].flip(0).view(1, 1, 2).to( - x.device).float() - else: - img_shape = torch.tensor(x.shape[2:]).flip(0).view(1, 1, 2).to( - x.device).float() - return img_shape - - -def abs_img_point_to_rel_img_point(abs_img_points, img, spatial_scale=1.): - """Convert image based absolute point coordinates to image based relative - coordinates for sampling. - - Args: - abs_img_points (torch.Tensor): Image based absolute point coordinates, - shape (N, P, 2) - img (tuple or torch.Tensor): (height, width) of image or feature map. - spatial_scale (float, optional): Scale points by this factor. - Default: 1. - - Returns: - Tensor: Image based relative point coordinates for sampling, shape - (N, P, 2). - """ - - assert (isinstance(img, tuple) and len(img) == 2) or \ - (isinstance(img, torch.Tensor) and len(img.shape) == 4) - - if isinstance(img, tuple): - h, w = img - scale = torch.tensor([w, h], - dtype=torch.float, - device=abs_img_points.device) - scale = scale.view(1, 1, 2) - else: - scale = get_shape_from_feature_map(img) - - return abs_img_points / scale * spatial_scale - - -def rel_roi_point_to_rel_img_point(rois, - rel_roi_points, - img, - spatial_scale=1.): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (torch.Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (torch.Tensor): Point coordinates inside RoI, relative - to RoI, location, range (0, 1), shape (N, P, 2) - img (tuple or torch.Tensor): (height, width) of image or feature map. - spatial_scale (float, optional): Scale points by this factor. - Default: 1. - - Returns: - torch.Tensor: Image based relative point coordinates for sampling, - shape (N, P, 2). - """ - - abs_img_point = rel_roi_point_to_abs_img_point(rois, rel_roi_points) - rel_img_point = abs_img_point_to_rel_img_point(abs_img_point, img, - spatial_scale) - - return rel_img_point - - -def point_sample(input, points, align_corners=False, **kwargs): - """A wrapper around :func:`grid_sample` to support 3D point_coords tensors - Unlike :func:`torch.nn.functional.grid_sample` it assumes point_coords to - lie inside ``[0, 1] x [0, 1]`` square. - - Args: - input (torch.Tensor): Feature map, shape (N, C, H, W). - points (torch.Tensor): Image based absolute point coordinates - (normalized), range [0, 1] x [0, 1], shape (N, P, 2) or - (N, Hgrid, Wgrid, 2). - align_corners (bool, optional): Whether align_corners. - Default: False - - Returns: - torch.Tensor: Features of `point` on `input`, shape (N, C, P) or - (N, C, Hgrid, Wgrid). - """ - - add_dim = False - if points.dim() == 3: - add_dim = True - points = points.unsqueeze(2) - if is_in_onnx_export_without_custom_ops(): - # If custom ops for onnx runtime not compiled use python - # implementation of grid_sample function to make onnx graph - # with supported nodes - output = bilinear_grid_sample( - input, denormalize(points), align_corners=align_corners) - else: - output = F.grid_sample( - input, denormalize(points), align_corners=align_corners, **kwargs) - if add_dim: - output = output.squeeze(3) - return output - - -class SimpleRoIAlign(nn.Module): - - def __init__(self, output_size, spatial_scale, aligned=True): - """Simple RoI align in PointRend, faster than standard RoIAlign. - - Args: - output_size (tuple[int]): h, w - spatial_scale (float): scale the input boxes by this number - aligned (bool): if False, use the legacy implementation in - MMDetection, align_corners=True will be used in F.grid_sample. - If True, align the results more perfectly. - """ - - super(SimpleRoIAlign, self).__init__() - self.output_size = _pair(output_size) - self.spatial_scale = float(spatial_scale) - # to be consistent with other RoI ops - self.use_torchvision = False - self.aligned = aligned - - def forward(self, features, rois): - num_imgs = features.size(0) - num_rois = rois.size(0) - rel_roi_points = generate_grid( - num_rois, self.output_size, device=rois.device) - - if torch.onnx.is_in_onnx_export(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois, rel_roi_points, features, self.spatial_scale) - rel_img_points = rel_img_points.reshape(num_imgs, -1, - *rel_img_points.shape[1:]) - point_feats = point_sample( - features, rel_img_points, align_corners=not self.aligned) - point_feats = point_feats.transpose(1, 2) - else: - point_feats = [] - for batch_ind in range(num_imgs): - # unravel batch dim - feat = features[batch_ind].unsqueeze(0) - inds = (rois[:, 0].long() == batch_ind) - if inds.any(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois[inds], rel_roi_points[inds], feat, - self.spatial_scale).unsqueeze(0) - point_feat = point_sample( - feat, rel_img_points, align_corners=not self.aligned) - point_feat = point_feat.squeeze(0).transpose(0, 1) - point_feats.append(point_feat) - - point_feats = torch.cat(point_feats, dim=0) - - channels = features.size(1) - roi_feats = point_feats.reshape(num_rois, channels, *self.output_size) - - return roi_feats - - def __repr__(self): - format_str = self.__class__.__name__ - format_str += '(output_size={}, spatial_scale={}'.format( - self.output_size, self.spatial_scale) - return format_str diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/psa_mask.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/psa_mask.py deleted file mode 100644 index cdf14e62b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/psa_mask.py +++ /dev/null @@ -1,92 +0,0 @@ -# Modified from https://github.com/hszhao/semseg/blob/master/lib/psa -from torch import nn -from torch.autograd import Function -from torch.nn.modules.utils import _pair - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', - ['psamask_forward', 'psamask_backward']) - - -class PSAMaskFunction(Function): - - @staticmethod - def symbolic(g, input, psa_type, mask_size): - return g.op( - 'mmcv::MMCVPSAMask', - input, - psa_type_i=psa_type, - mask_size_i=mask_size) - - @staticmethod - def forward(ctx, input, psa_type, mask_size): - ctx.psa_type = psa_type - ctx.mask_size = _pair(mask_size) - ctx.save_for_backward(input) - - h_mask, w_mask = ctx.mask_size - batch_size, channels, h_feature, w_feature = input.size() - assert channels == h_mask * w_mask - output = input.new_zeros( - (batch_size, h_feature * w_feature, h_feature, w_feature)) - - ext_module.psamask_forward( - input, - output, - psa_type=psa_type, - num_=batch_size, - h_feature=h_feature, - w_feature=w_feature, - h_mask=h_mask, - w_mask=w_mask, - half_h_mask=(h_mask - 1) // 2, - half_w_mask=(w_mask - 1) // 2) - return output - - @staticmethod - def backward(ctx, grad_output): - input = ctx.saved_tensors[0] - psa_type = ctx.psa_type - h_mask, w_mask = ctx.mask_size - batch_size, channels, h_feature, w_feature = input.size() - grad_input = grad_output.new_zeros( - (batch_size, channels, h_feature, w_feature)) - ext_module.psamask_backward( - grad_output, - grad_input, - psa_type=psa_type, - num_=batch_size, - h_feature=h_feature, - w_feature=w_feature, - h_mask=h_mask, - w_mask=w_mask, - half_h_mask=(h_mask - 1) // 2, - half_w_mask=(w_mask - 1) // 2) - return grad_input, None, None, None - - -psa_mask = PSAMaskFunction.apply - - -class PSAMask(nn.Module): - - def __init__(self, psa_type, mask_size=None): - super(PSAMask, self).__init__() - assert psa_type in ['collect', 'distribute'] - if psa_type == 'collect': - psa_type_enum = 0 - else: - psa_type_enum = 1 - self.psa_type_enum = psa_type_enum - self.mask_size = mask_size - self.psa_type = psa_type - - def forward(self, input): - return psa_mask(input, self.psa_type_enum, self.mask_size) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(psa_type={self.psa_type}, ' - s += f'mask_size={self.mask_size})' - return s diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/sync_bn.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/sync_bn.py deleted file mode 100644 index 04302f031..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/ops/sync_bn.py +++ /dev/null @@ -1,279 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn.functional as F -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.module import Module -from torch.nn.parameter import Parameter - -from mmcv.cnn import NORM_LAYERS -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sync_bn_forward_mean', 'sync_bn_forward_var', 'sync_bn_forward_output', - 'sync_bn_backward_param', 'sync_bn_backward_data' -]) - - -class SyncBatchNormFunction(Function): - - @staticmethod - def symbolic(g, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - return g.op( - 'mmcv::MMCVSyncBatchNorm', - input, - running_mean, - running_var, - weight, - bias, - momentum_f=momentum, - eps_f=eps, - group_i=group, - group_size_i=group_size, - stats_mode=stats_mode) - - @staticmethod - def forward(self, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - self.momentum = momentum - self.eps = eps - self.group = group - self.group_size = group_size - self.stats_mode = stats_mode - - assert isinstance( - input, (torch.HalfTensor, torch.FloatTensor, - torch.cuda.HalfTensor, torch.cuda.FloatTensor)), \ - f'only support Half or Float Tensor, but {input.type()}' - output = torch.zeros_like(input) - input3d = input.flatten(start_dim=2) - output3d = output.view_as(input3d) - num_channels = input3d.size(1) - - # ensure mean/var/norm/std are initialized as zeros - # ``torch.empty()`` does not guarantee that - mean = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - var = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - norm = torch.zeros_like( - input3d, dtype=torch.float, device=input3d.device) - std = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - - batch_size = input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_forward_mean(input3d, mean) - batch_flag = torch.ones([1], device=mean.device, dtype=mean.dtype) - else: - # skip updating mean and leave it as zeros when the input is empty - batch_flag = torch.zeros([1], device=mean.device, dtype=mean.dtype) - - # synchronize mean and the batch flag - vec = torch.cat([mean, batch_flag]) - if self.stats_mode == 'N': - vec *= batch_size - if self.group_size > 1: - dist.all_reduce(vec, group=self.group) - total_batch = vec[-1].detach() - mean = vec[:num_channels] - - if self.stats_mode == 'default': - mean = mean / self.group_size - elif self.stats_mode == 'N': - mean = mean / total_batch.clamp(min=1) - else: - raise NotImplementedError - - # leave var as zeros when the input is empty - if batch_size > 0: - ext_module.sync_bn_forward_var(input3d, mean, var) - - if self.stats_mode == 'N': - var *= batch_size - if self.group_size > 1: - dist.all_reduce(var, group=self.group) - - if self.stats_mode == 'default': - var /= self.group_size - elif self.stats_mode == 'N': - var /= total_batch.clamp(min=1) - else: - raise NotImplementedError - - # if the total batch size over all the ranks is zero, - # we should not update the statistics in the current batch - update_flag = total_batch.clamp(max=1) - momentum = update_flag * self.momentum - ext_module.sync_bn_forward_output( - input3d, - mean, - var, - weight, - bias, - running_mean, - running_var, - norm, - std, - output3d, - eps=self.eps, - momentum=momentum, - group_size=self.group_size) - self.save_for_backward(norm, std, weight) - return output - - @staticmethod - @once_differentiable - def backward(self, grad_output): - norm, std, weight = self.saved_tensors - grad_weight = torch.zeros_like(weight) - grad_bias = torch.zeros_like(weight) - grad_input = torch.zeros_like(grad_output) - grad_output3d = grad_output.flatten(start_dim=2) - grad_input3d = grad_input.view_as(grad_output3d) - - batch_size = grad_input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_backward_param(grad_output3d, norm, grad_weight, - grad_bias) - - # all reduce - if self.group_size > 1: - dist.all_reduce(grad_weight, group=self.group) - dist.all_reduce(grad_bias, group=self.group) - grad_weight /= self.group_size - grad_bias /= self.group_size - - if batch_size > 0: - ext_module.sync_bn_backward_data(grad_output3d, weight, - grad_weight, grad_bias, norm, std, - grad_input3d) - - return grad_input, None, None, grad_weight, grad_bias, \ - None, None, None, None, None - - -@NORM_LAYERS.register_module(name='MMSyncBN') -class SyncBatchNorm(Module): - """Synchronized Batch Normalization. - - Args: - num_features (int): number of features/chennels in input tensor - eps (float, optional): a value added to the denominator for numerical - stability. Defaults to 1e-5. - momentum (float, optional): the value used for the running_mean and - running_var computation. Defaults to 0.1. - affine (bool, optional): whether to use learnable affine parameters. - Defaults to True. - track_running_stats (bool, optional): whether to track the running - mean and variance during training. When set to False, this - module does not track such statistics, and initializes statistics - buffers ``running_mean`` and ``running_var`` as ``None``. When - these buffers are ``None``, this module always uses batch - statistics in both training and eval modes. Defaults to True. - group (int, optional): synchronization of stats happen within - each process group individually. By default it is synchronization - across the whole world. Defaults to None. - stats_mode (str, optional): The statistical mode. Available options - includes ``'default'`` and ``'N'``. Defaults to 'default'. - When ``stats_mode=='default'``, it computes the overall statistics - using those from each worker with equal weight, i.e., the - statistics are synchronized and simply divied by ``group``. This - mode will produce inaccurate statistics when empty tensors occur. - When ``stats_mode=='N'``, it compute the overall statistics using - the total number of batches in each worker ignoring the number of - group, i.e., the statistics are synchronized and then divied by - the total batch ``N``. This mode is beneficial when empty tensors - occur during training, as it average the total mean by the real - number of batch. - """ - - def __init__(self, - num_features, - eps=1e-5, - momentum=0.1, - affine=True, - track_running_stats=True, - group=None, - stats_mode='default'): - super(SyncBatchNorm, self).__init__() - self.num_features = num_features - self.eps = eps - self.momentum = momentum - self.affine = affine - self.track_running_stats = track_running_stats - group = dist.group.WORLD if group is None else group - self.group = group - self.group_size = dist.get_world_size(group) - assert stats_mode in ['default', 'N'], \ - f'"stats_mode" only accepts "default" and "N", got "{stats_mode}"' - self.stats_mode = stats_mode - if self.affine: - self.weight = Parameter(torch.Tensor(num_features)) - self.bias = Parameter(torch.Tensor(num_features)) - else: - self.register_parameter('weight', None) - self.register_parameter('bias', None) - if self.track_running_stats: - self.register_buffer('running_mean', torch.zeros(num_features)) - self.register_buffer('running_var', torch.ones(num_features)) - self.register_buffer('num_batches_tracked', - torch.tensor(0, dtype=torch.long)) - else: - self.register_buffer('running_mean', None) - self.register_buffer('running_var', None) - self.register_buffer('num_batches_tracked', None) - self.reset_parameters() - - def reset_running_stats(self): - if self.track_running_stats: - self.running_mean.zero_() - self.running_var.fill_(1) - self.num_batches_tracked.zero_() - - def reset_parameters(self): - self.reset_running_stats() - if self.affine: - self.weight.data.uniform_() # pytorch use ones_() - self.bias.data.zero_() - - def forward(self, input): - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input, got {input.dim()}D input') - if self.momentum is None: - exponential_average_factor = 0.0 - else: - exponential_average_factor = self.momentum - - if self.training and self.track_running_stats: - if self.num_batches_tracked is not None: - self.num_batches_tracked += 1 - if self.momentum is None: # use cumulative moving average - exponential_average_factor = 1.0 / float( - self.num_batches_tracked) - else: # use exponential moving average - exponential_average_factor = self.momentum - - if self.training or not self.track_running_stats: - return SyncBatchNormFunction.apply( - input, self.running_mean, self.running_var, self.weight, - self.bias, exponential_average_factor, self.eps, self.group, - self.group_size, self.stats_mode) - else: - return F.batch_norm(input, self.running_mean, self.running_var, - self.weight, self.bias, False, - exponential_average_factor, self.eps) - - def __repr__(self): - s = self.__class__.__name__ - s += f'({self.num_features}, ' - s += f'eps={self.eps}, ' - s += f'momentum={self.momentum}, ' - s += f'affine={self.affine}, ' - s += f'track_running_stats={self.track_running_stats}, ' - s += f'group_size={self.group_size},' - s += f'stats_mode={self.stats_mode})' - return s diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/__init__.py deleted file mode 100644 index 2ed2c17ad..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collate import collate -from .data_container import DataContainer -from .data_parallel import MMDataParallel -from .distributed import MMDistributedDataParallel -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter, scatter_kwargs -from .utils import is_module_wrapper - -__all__ = [ - 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', - 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/_functions.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/_functions.py deleted file mode 100644 index 95c58bf1a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/_functions.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import _get_stream - - -def scatter(input, devices, streams=None): - """Scatters tensor across multiple GPUs.""" - if streams is None: - streams = [None] * len(devices) - - if isinstance(input, list): - chunk_size = (len(input) - 1) // len(devices) + 1 - outputs = [ - scatter(input[i], [devices[i // chunk_size]], - [streams[i // chunk_size]]) for i in range(len(input)) - ] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - # TODO: copy to a pinned buffer first (if copying from CPU) - stream = streams[0] if output.numel() > 0 else None - if devices != [-1]: - with torch.cuda.device(devices[0]), torch.cuda.stream(stream): - output = output.cuda(devices[0], non_blocking=True) - - return output - else: - raise Exception(f'Unknown type {type(input)}.') - - -def synchronize_stream(output, devices, streams): - if isinstance(output, list): - chunk_size = len(output) // len(devices) - for i in range(len(devices)): - for j in range(chunk_size): - synchronize_stream(output[i * chunk_size + j], [devices[i]], - [streams[i]]) - elif isinstance(output, torch.Tensor): - if output.numel() != 0: - with torch.cuda.device(devices[0]): - main_stream = torch.cuda.current_stream() - main_stream.wait_stream(streams[0]) - output.record_stream(main_stream) - else: - raise Exception(f'Unknown type {type(output)}.') - - -def get_input_device(input): - if isinstance(input, list): - for item in input: - input_device = get_input_device(item) - if input_device != -1: - return input_device - return -1 - elif isinstance(input, torch.Tensor): - return input.get_device() if input.is_cuda else -1 - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_gpus, input): - input_device = get_input_device(input) - streams = None - if input_device == -1 and target_gpus != [-1]: - # Perform CPU to GPU copies in a background stream - streams = [_get_stream(device) for device in target_gpus] - - outputs = scatter(input, target_gpus, streams) - # Synchronize with the copy stream - if streams is not None: - synchronize_stream(outputs, target_gpus, streams) - - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/collate.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/collate.py deleted file mode 100644 index ad749197d..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/collate.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence - -import torch -import torch.nn.functional as F -from torch.utils.data.dataloader import default_collate - -from .data_container import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Puts each data field into a tensor/DataContainer with outer dimension - batch size. - - Extend default_collate to add support for - :type:`~mmcv.parallel.DataContainer`. There are 3 cases. - - 1. cpu_only = True, e.g., meta data - 2. cpu_only = False, stack = True, e.g., images tensors - 3. cpu_only = False, stack = False, e.g., gt bboxes - """ - - if not isinstance(batch, Sequence): - raise TypeError(f'{batch.dtype} is not supported.') - - if isinstance(batch[0], DataContainer): - stacked = [] - if batch[0].cpu_only: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer( - stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) - elif batch[0].stack: - for i in range(0, len(batch), samples_per_gpu): - assert isinstance(batch[i].data, torch.Tensor) - - if batch[i].pad_dims is not None: - ndim = batch[i].dim() - assert ndim > batch[i].pad_dims - max_shape = [0 for _ in range(batch[i].pad_dims)] - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = batch[i].size(-dim) - for sample in batch[i:i + samples_per_gpu]: - for dim in range(0, ndim - batch[i].pad_dims): - assert batch[i].size(dim) == sample.size(dim) - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = max(max_shape[dim - 1], - sample.size(-dim)) - padded_samples = [] - for sample in batch[i:i + samples_per_gpu]: - pad = [0 for _ in range(batch[i].pad_dims * 2)] - for dim in range(1, batch[i].pad_dims + 1): - pad[2 * dim - - 1] = max_shape[dim - 1] - sample.size(-dim) - padded_samples.append( - F.pad( - sample.data, pad, value=sample.padding_value)) - stacked.append(default_collate(padded_samples)) - elif batch[i].pad_dims is None: - stacked.append( - default_collate([ - sample.data - for sample in batch[i:i + samples_per_gpu] - ])) - else: - raise ValueError( - 'pad_dims should be either None or integers (1-3)') - - else: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer(stacked, batch[0].stack, batch[0].padding_value) - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - return [collate(samples, samples_per_gpu) for samples in transposed] - elif isinstance(batch[0], Mapping): - return { - key: collate([d[key] for d in batch], samples_per_gpu) - for key in batch[0] - } - else: - return default_collate(batch) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/data_container.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/data_container.py deleted file mode 100644 index cedb0d32a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/data_container.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch - - -def assert_tensor_type(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not isinstance(args[0].data, torch.Tensor): - raise AttributeError( - f'{args[0].__class__.__name__} has no attribute ' - f'{func.__name__} for type {args[0].datatype}') - return func(*args, **kwargs) - - return wrapper - - -class DataContainer: - """A container for any type of objects. - - Typically tensors will be stacked in the collate function and sliced along - some dimension in the scatter function. This behavior has some limitations. - 1. All tensors have to be the same size. - 2. Types are limited (numpy array or Tensor). - - We design `DataContainer` and `MMDataParallel` to overcome these - limitations. The behavior can be either of the following. - - - copy to GPU, pad all tensors to the same size and stack them - - copy to GPU without stacking - - leave the objects as is and pass it to the model - - pad_dims specifies the number of last few dimensions to do padding - """ - - def __init__(self, - data, - stack=False, - padding_value=0, - cpu_only=False, - pad_dims=2): - self._data = data - self._cpu_only = cpu_only - self._stack = stack - self._padding_value = padding_value - assert pad_dims in [None, 1, 2, 3] - self._pad_dims = pad_dims - - def __repr__(self): - return f'{self.__class__.__name__}({repr(self.data)})' - - def __len__(self): - return len(self._data) - - @property - def data(self): - return self._data - - @property - def datatype(self): - if isinstance(self.data, torch.Tensor): - return self.data.type() - else: - return type(self.data) - - @property - def cpu_only(self): - return self._cpu_only - - @property - def stack(self): - return self._stack - - @property - def padding_value(self): - return self._padding_value - - @property - def pad_dims(self): - return self._pad_dims - - @assert_tensor_type - def size(self, *args, **kwargs): - return self.data.size(*args, **kwargs) - - @assert_tensor_type - def dim(self): - return self.data.dim() diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/data_parallel.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/data_parallel.py deleted file mode 100644 index 7a5abeb6e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/data_parallel.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from itertools import chain - -from torch.nn.parallel import DataParallel - -from .scatter_gather import scatter_kwargs - - -class MMDataParallel(DataParallel): - """The DataParallel module that supports DataContainer. - - MMDataParallel has two main differences with PyTorch DataParallel: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data during both GPU and CPU inference. - - It implement two more APIs ``train_step()`` and ``val_step()``. - - .. warning:: - MMDataParallel only supports single GPU training, if you need to - train with multiple GPUs, please use MMDistributedDataParallel - instead. If you have multiple GPUs and you just want to use - MMDataParallel, you can set the environment variable - ``CUDA_VISIBLE_DEVICES=0`` or instantiate ``MMDataParallel`` with - ``device_ids=[0]``. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - device_ids (list[int]): Device IDS of modules to be scattered to. - Defaults to None when GPU is not available. - output_device (str | int): Device ID for output. Defaults to None. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MMDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.dim = dim - - def forward(self, *inputs, **kwargs): - """Override the original forward function. - - The main difference lies in the CPU inference where the data in - :class:`DataContainers` will still be gathered. - """ - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module(*inputs[0], **kwargs[0]) - else: - return super().forward(*inputs, **kwargs) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.train_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.train_step(*inputs[0], **kwargs[0]) - - def val_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.val_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/distributed.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/distributed.py deleted file mode 100644 index 0188ca4ab..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/distributed.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel.distributed import (DistributedDataParallel, - _find_tensors) - -from mmcv import print_log -from mmcv.utils import TORCH_VERSION, digit_version -from .scatter_gather import scatter_kwargs - - -class MMDistributedDataParallel(DistributedDataParallel): - """The DDP module that supports DataContainer. - - MMDDP has two main differences with PyTorch DDP: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data. - - It implement two APIs ``train_step()`` and ``val_step()``. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - """train_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.train_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.train_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.train_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output - - def val_step(self, *inputs, **kwargs): - """val_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.val_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.val_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.val_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/distributed_deprecated.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/distributed_deprecated.py deleted file mode 100644 index b593d4a9e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/distributed_deprecated.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn as nn -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter_kwargs - - -@MODULE_WRAPPERS.register_module() -class MMDistributedDataParallel(nn.Module): - - def __init__(self, - module, - dim=0, - broadcast_buffers=True, - bucket_cap_mb=25): - super(MMDistributedDataParallel, self).__init__() - self.module = module - self.dim = dim - self.broadcast_buffers = broadcast_buffers - - self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 - self._sync_params() - - def _dist_broadcast_coalesced(self, tensors, buffer_size): - for tensors in _take_tensors(tensors, buffer_size): - flat_tensors = _flatten_dense_tensors(tensors) - dist.broadcast(flat_tensors, 0) - for tensor, synced in zip( - tensors, _unflatten_dense_tensors(flat_tensors, tensors)): - tensor.copy_(synced) - - def _sync_params(self): - module_states = list(self.module.state_dict().values()) - if len(module_states) > 0: - self._dist_broadcast_coalesced(module_states, - self.broadcast_bucket_size) - if self.broadcast_buffers: - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) < digit_version('1.0')): - buffers = [b.data for b in self.module._all_buffers()] - else: - buffers = [b.data for b in self.module.buffers()] - if len(buffers) > 0: - self._dist_broadcast_coalesced(buffers, - self.broadcast_bucket_size) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/registry.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/registry.py deleted file mode 100644 index 144f9fb16..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel, DistributedDataParallel - -from mmcv.utils import Registry - -MODULE_WRAPPERS = Registry('module wrapper') -MODULE_WRAPPERS.register_module(module=DataParallel) -MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/scatter_gather.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/scatter_gather.py deleted file mode 100644 index 900ff8856..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import Scatter as OrigScatter - -from ._functions import Scatter -from .data_container import DataContainer - - -def scatter(inputs, target_gpus, dim=0): - """Scatter inputs to target gpus. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_gpus != [-1]: - return OrigScatter.apply(target_gpus, None, dim, obj) - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_gpus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_gpus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_gpus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_gpus, dim) if inputs else [] - kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/utils.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/utils.py deleted file mode 100644 index 0f5712cb4..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/parallel/utils.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import MODULE_WRAPPERS - - -def is_module_wrapper(module): - """Check if a module is a module wrapper. - - The following 3 modules in MMCV (and their subclasses) are regarded as - module wrappers: DataParallel, DistributedDataParallel, - MMDistributedDataParallel (the deprecated version). You may add you own - module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: True if the input module is a module wrapper. - """ - module_wrappers = tuple(MODULE_WRAPPERS.module_dict.values()) - return isinstance(module, module_wrappers) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/__init__.py deleted file mode 100644 index 183d53672..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_module import BaseModule, ModuleDict, ModuleList, Sequential -from .base_runner import BaseRunner -from .builder import RUNNERS, build_runner -from .checkpoint import (CheckpointLoader, _load_checkpoint, - _load_checkpoint_with_prefix, load_checkpoint, - load_state_dict, save_checkpoint, weights_to_cpu) -from .default_constructor import DefaultRunnerConstructor -from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, - init_dist, master_only) -from .epoch_based_runner import EpochBasedRunner, Runner -from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model -from .hooks import (HOOKS, CheckpointHook, ClearMLLoggerHook, ClosureHook, - DistEvalHook, DistSamplerSeedHook, DvcliveLoggerHook, - EMAHook, EvalHook, Fp16OptimizerHook, - GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, Hook, IterTimerHook, - LoggerHook, MlflowLoggerHook, NeptuneLoggerHook, - OptimizerHook, PaviLoggerHook, SegmindLoggerHook, - SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .hooks.lr_updater import StepLrUpdaterHook # noqa -from .hooks.lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, - InvLrUpdaterHook, LinearAnnealingLrUpdaterHook, - LrUpdaterHook, OneCycleLrUpdaterHook, - PolyLrUpdaterHook) -from .hooks.momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .iter_based_runner import IterBasedRunner, IterLoader -from .log_buffer import LogBuffer -from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, - DefaultOptimizerConstructor, build_optimizer, - build_optimizer_constructor) -from .priority import Priority, get_priority -from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed - -# initialize ipu to registor ipu runner to RUNNERS -from mmcv.device import ipu # isort:skip # noqa - -__all__ = [ - 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', - 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', - 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', - 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', - 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', - 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', - 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', - 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', - 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', - 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', - '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', - 'ModuleDict', 'ModuleList', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor', - 'SegmindLoggerHook', 'LinearAnnealingMomentumUpdaterHook', - 'LinearAnnealingLrUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/base_module.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/base_module.py deleted file mode 100644 index 7937eca37..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/base_module.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings -from abc import ABCMeta -from collections import defaultdict -from logging import FileHandler - -import torch.nn as nn - -from mmcv.runner.dist_utils import master_only -from mmcv.utils.logging import get_logger, logger_initialized, print_log - - -class BaseModule(nn.Module, metaclass=ABCMeta): - """Base module for all modules in openmmlab. - - ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional - functionality of parameter initialization. Compared with - ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. - - - ``init_cfg``: the config to control the initialization. - - ``init_weights``: The function of parameter initialization and recording - initialization information. - - ``_params_init_info``: Used to track the parameter initialization - information. This attribute only exists during executing the - ``init_weights``. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, init_cfg=None): - """Initialize BaseModule, inherited from `torch.nn.Module`""" - - # NOTE init_cfg can be defined in different levels, but init_cfg - # in low levels has a higher priority. - - super(BaseModule, self).__init__() - # define default value of init_cfg instead of hard code - # in init_weights() function - self._is_init = False - - self.init_cfg = copy.deepcopy(init_cfg) - - # Backward compatibility in derived classes - # if pretrained is not None: - # warnings.warn('DeprecationWarning: pretrained is a deprecated \ - # key, please consider using init_cfg') - # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - - @property - def is_init(self): - return self._is_init - - def init_weights(self): - """Initialize the weights.""" - - is_top_level_module = False - # check if it is top-level module - if not hasattr(self, '_params_init_info'): - # The `_params_init_info` is used to record the initialization - # information of the parameters - # the key should be the obj:`nn.Parameter` of model and the value - # should be a dict containing - # - init_info (str): The string that describes the initialization. - # - tmp_mean_value (FloatTensor): The mean of the parameter, - # which indicates whether the parameter has been modified. - # this attribute would be deleted after all parameters - # is initialized. - self._params_init_info = defaultdict(dict) - is_top_level_module = True - - # Initialize the `_params_init_info`, - # When detecting the `tmp_mean_value` of - # the corresponding parameter is changed, update related - # initialization information - for name, param in self.named_parameters(): - self._params_init_info[param][ - 'init_info'] = f'The value is the same before and ' \ - f'after calling `init_weights` ' \ - f'of {self.__class__.__name__} ' - self._params_init_info[param][ - 'tmp_mean_value'] = param.data.mean() - - # pass `params_init_info` to all submodules - # All submodules share the same `params_init_info`, - # so it will be updated when parameters are - # modified at any level of the model. - for sub_module in self.modules(): - sub_module._params_init_info = self._params_init_info - - # Get the initialized logger, if not exist, - # create a logger named `mmcv` - logger_names = list(logger_initialized.keys()) - logger_name = logger_names[0] if logger_names else 'mmcv' - - from ..cnn import initialize - from ..cnn.utils.weight_init import update_init_info - module_name = self.__class__.__name__ - if not self._is_init: - if self.init_cfg: - print_log( - f'initialize {module_name} with init_cfg {self.init_cfg}', - logger=logger_name) - initialize(self, self.init_cfg) - if isinstance(self.init_cfg, dict): - # prevent the parameters of - # the pre-trained model - # from being overwritten by - # the `init_weights` - if self.init_cfg['type'] == 'Pretrained': - return - - for m in self.children(): - if hasattr(m, 'init_weights'): - m.init_weights() - # users may overload the `init_weights` - update_init_info( - m, - init_info=f'Initialized by ' - f'user-defined `init_weights`' - f' in {m.__class__.__name__} ') - - self._is_init = True - else: - warnings.warn(f'init_weights of {self.__class__.__name__} has ' - f'been called more than once.') - - if is_top_level_module: - self._dump_init_info(logger_name) - - for sub_module in self.modules(): - del sub_module._params_init_info - - @master_only - def _dump_init_info(self, logger_name): - """Dump the initialization information to a file named - `initialization.log.json` in workdir. - - Args: - logger_name (str): The name of logger. - """ - - logger = get_logger(logger_name) - - with_file_handler = False - # dump the information to the logger file if there is a `FileHandler` - for handler in logger.handlers: - if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True - if not with_file_handler: - for name, param in self.named_parameters(): - print_log( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n ", - logger=logger_name) - - def __repr__(self): - s = super().__repr__() - if self.init_cfg: - s += f'\ninit_cfg={self.init_cfg}' - return s - - -class Sequential(BaseModule, nn.Sequential): - """Sequential module in openmmlab. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, *args, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.Sequential.__init__(self, *args) - - -class ModuleList(BaseModule, nn.ModuleList): - """ModuleList in openmmlab. - - Args: - modules (iterable, optional): an iterable of modules to add. - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleList.__init__(self, modules) - - -class ModuleDict(BaseModule, nn.ModuleDict): - """ModuleDict in openmmlab. - - Args: - modules (dict, optional): a mapping (dictionary) of (string: module) - or an iterable of key-value pairs of type (string, module). - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleDict.__init__(self, modules) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/base_runner.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/base_runner.py deleted file mode 100644 index 12a0025f8..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/base_runner.py +++ /dev/null @@ -1,544 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -import os.path as osp -import warnings -from abc import ABCMeta, abstractmethod - -import torch -from torch.optim import Optimizer - -import mmcv -from ..parallel import is_module_wrapper -from .checkpoint import load_checkpoint -from .dist_utils import get_dist_info -from .hooks import HOOKS, Hook -from .log_buffer import LogBuffer -from .priority import Priority, get_priority -from .utils import get_time_str - - -class BaseRunner(metaclass=ABCMeta): - """The base class of Runner, a training helper for PyTorch. - - All subclasses should implement the following APIs: - - - ``run()`` - - ``train()`` - - ``val()`` - - ``save_checkpoint()`` - - Args: - model (:obj:`torch.nn.Module`): The model to be run. - batch_processor (callable): A callable method that process a data - batch. The interface of this method should be - `batch_processor(model, data, train_mode) -> dict` - optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an - optimizer (in most cases) or a dict of optimizers (in models that - requires more than one optimizer, e.g., GAN). - work_dir (str, optional): The working directory to save checkpoints - and logs. Defaults to None. - logger (:obj:`logging.Logger`): Logger used during training. - Defaults to None. (The default value is just for backward - compatibility) - meta (dict | None): A dict records some import information such as - environment info and seed, which will be logged in logger hook. - Defaults to None. - max_epochs (int, optional): Total training epochs. - max_iters (int, optional): Total training iterations. - """ - - def __init__(self, - model, - batch_processor=None, - optimizer=None, - work_dir=None, - logger=None, - meta=None, - max_iters=None, - max_epochs=None): - if batch_processor is not None: - if not callable(batch_processor): - raise TypeError('batch_processor must be callable, ' - f'but got {type(batch_processor)}') - warnings.warn( - 'batch_processor is deprecated, please implement ' - 'train_step() and val_step() in the model instead.', - DeprecationWarning) - # raise an error is `batch_processor` is not None and - # `model.train_step()` exists. - if is_module_wrapper(model): - _model = model.module - else: - _model = model - if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): - raise RuntimeError( - 'batch_processor and model.train_step()/model.val_step() ' - 'cannot be both available.') - else: - assert hasattr(model, 'train_step') - - # check the type of `optimizer` - if isinstance(optimizer, dict): - for name, optim in optimizer.items(): - if not isinstance(optim, Optimizer): - raise TypeError( - f'optimizer must be a dict of torch.optim.Optimizers, ' - f'but optimizer["{name}"] is a {type(optim)}') - elif not isinstance(optimizer, Optimizer) and optimizer is not None: - raise TypeError( - f'optimizer must be a torch.optim.Optimizer object ' - f'or dict or None, but got {type(optimizer)}') - - # check the type of `logger` - if not isinstance(logger, logging.Logger): - raise TypeError(f'logger must be a logging.Logger object, ' - f'but got {type(logger)}') - - # check the type of `meta` - if meta is not None and not isinstance(meta, dict): - raise TypeError( - f'meta must be a dict or None, but got {type(meta)}') - - self.model = model - self.batch_processor = batch_processor - self.optimizer = optimizer - self.logger = logger - self.meta = meta - # create work_dir - if mmcv.is_str(work_dir): - self.work_dir = osp.abspath(work_dir) - mmcv.mkdir_or_exist(self.work_dir) - elif work_dir is None: - self.work_dir = None - else: - raise TypeError('"work_dir" must be a str or None') - - # get model name from the model class - if hasattr(self.model, 'module'): - self._model_name = self.model.module.__class__.__name__ - else: - self._model_name = self.model.__class__.__name__ - - self._rank, self._world_size = get_dist_info() - self.timestamp = get_time_str() - self.mode = None - self._hooks = [] - self._epoch = 0 - self._iter = 0 - self._inner_iter = 0 - - if max_epochs is not None and max_iters is not None: - raise ValueError( - 'Only one of `max_epochs` or `max_iters` can be set.') - - self._max_epochs = max_epochs - self._max_iters = max_iters - # TODO: Redesign LogBuffer, it is not flexible and elegant enough - self.log_buffer = LogBuffer() - - @property - def model_name(self): - """str: Name of the model, usually the module class name.""" - return self._model_name - - @property - def rank(self): - """int: Rank of current process. (distributed training)""" - return self._rank - - @property - def world_size(self): - """int: Number of processes participating in the job. - (distributed training)""" - return self._world_size - - @property - def hooks(self): - """list[:obj:`Hook`]: A list of registered hooks.""" - return self._hooks - - @property - def epoch(self): - """int: Current epoch.""" - return self._epoch - - @property - def iter(self): - """int: Current iteration.""" - return self._iter - - @property - def inner_iter(self): - """int: Iteration in an epoch.""" - return self._inner_iter - - @property - def max_epochs(self): - """int: Maximum training epochs.""" - return self._max_epochs - - @property - def max_iters(self): - """int: Maximum training iterations.""" - return self._max_iters - - @abstractmethod - def train(self): - pass - - @abstractmethod - def val(self): - pass - - @abstractmethod - def run(self, data_loaders, workflow, **kwargs): - pass - - @abstractmethod - def save_checkpoint(self, - out_dir, - filename_tmpl, - save_optimizer=True, - meta=None, - create_symlink=True): - pass - - def current_lr(self): - """Get current learning rates. - - Returns: - list[float] | dict[str, list[float]]: Current learning rates of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - if isinstance(self.optimizer, torch.optim.Optimizer): - lr = [group['lr'] for group in self.optimizer.param_groups] - elif isinstance(self.optimizer, dict): - lr = dict() - for name, optim in self.optimizer.items(): - lr[name] = [group['lr'] for group in optim.param_groups] - else: - raise RuntimeError( - 'lr is not applicable because optimizer does not exist.') - return lr - - def current_momentum(self): - """Get current momentums. - - Returns: - list[float] | dict[str, list[float]]: Current momentums of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - - def _get_momentum(optimizer): - momentums = [] - for group in optimizer.param_groups: - if 'momentum' in group.keys(): - momentums.append(group['momentum']) - elif 'betas' in group.keys(): - momentums.append(group['betas'][0]) - else: - momentums.append(0) - return momentums - - if self.optimizer is None: - raise RuntimeError( - 'momentum is not applicable because optimizer does not exist.') - elif isinstance(self.optimizer, torch.optim.Optimizer): - momentums = _get_momentum(self.optimizer) - elif isinstance(self.optimizer, dict): - momentums = dict() - for name, optim in self.optimizer.items(): - momentums[name] = _get_momentum(optim) - return momentums - - def register_hook(self, hook, priority='NORMAL'): - """Register a hook into the hook list. - - The hook will be inserted into a priority queue, with the specified - priority (See :class:`Priority` for details of priorities). - For hooks with the same priority, they will be triggered in the same - order as they are registered. - - Args: - hook (:obj:`Hook`): The hook to be registered. - priority (int or str or :obj:`Priority`): Hook priority. - Lower value means higher priority. - """ - assert isinstance(hook, Hook) - if hasattr(hook, 'priority'): - raise ValueError('"priority" is a reserved attribute for hooks') - priority = get_priority(priority) - hook.priority = priority - # insert the hook to a sorted list - inserted = False - for i in range(len(self._hooks) - 1, -1, -1): - if priority >= self._hooks[i].priority: - self._hooks.insert(i + 1, hook) - inserted = True - break - if not inserted: - self._hooks.insert(0, hook) - - def register_hook_from_cfg(self, hook_cfg): - """Register a hook from its cfg. - - Args: - hook_cfg (dict): Hook config. It should have at least keys 'type' - and 'priority' indicating its type and priority. - - Note: - The specific hook class to register should not use 'type' and - 'priority' arguments during initialization. - """ - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = mmcv.build_from_cfg(hook_cfg, HOOKS) - self.register_hook(hook, priority=priority) - - def call_hook(self, fn_name): - """Call all hooks. - - Args: - fn_name (str): The function name in each hook to be called, such as - "before_train_epoch". - """ - for hook in self._hooks: - getattr(hook, fn_name)(self) - - def get_hook_info(self): - # Get hooks info in each stage - stage_hook_map = {stage: [] for stage in Hook.stages} - for hook in self.hooks: - try: - priority = Priority(hook.priority).name - except ValueError: - priority = hook.priority - classname = hook.__class__.__name__ - hook_info = f'({priority:<12}) {classname:<35}' - for trigger_stage in hook.get_triggered_stages(): - stage_hook_map[trigger_stage].append(hook_info) - - stage_hook_infos = [] - for stage in Hook.stages: - hook_infos = stage_hook_map[stage] - if len(hook_infos) > 0: - info = f'{stage}:\n' - info += '\n'.join(hook_infos) - info += '\n -------------------- ' - stage_hook_infos.append(info) - return '\n'.join(stage_hook_infos) - - def load_checkpoint(self, - filename, - map_location='cpu', - strict=False, - revise_keys=[(r'^module.', '')]): - return load_checkpoint( - self.model, - filename, - map_location, - strict, - self.logger, - revise_keys=revise_keys) - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - if map_location == 'default': - if torch.cuda.is_available(): - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint(checkpoint) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - if self.meta is None: - self.meta = {} - self.meta.setdefault('hook_msgs', {}) - # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages - self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) - - # Re-calculate the number of iterations when resuming - # models with different number of GPUs - if 'config' in checkpoint['meta']: - config = mmcv.Config.fromstring( - checkpoint['meta']['config'], file_format='.py') - previous_gpu_ids = config.get('gpu_ids', None) - if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( - previous_gpu_ids) != self.world_size: - self._iter = int(self._iter * len(previous_gpu_ids) / - self.world_size) - self.logger.info('the iteration number is changed due to ' - 'change of GPU number') - - # resume meta information meta - self.meta = checkpoint['meta'] - - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - elif isinstance(lr_config, dict): - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = mmcv.build_from_cfg(lr_config, HOOKS) - else: - hook = lr_config - self.register_hook(hook, priority='VERY_HIGH') - - def register_momentum_hook(self, momentum_config): - if momentum_config is None: - return - if isinstance(momentum_config, dict): - assert 'policy' in momentum_config - policy_type = momentum_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of momentum updater. - # Since this is not applicable for - # `CosineAnnealingMomentumUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'MomentumUpdaterHook' - momentum_config['type'] = hook_type - hook = mmcv.build_from_cfg(momentum_config, HOOKS) - else: - hook = momentum_config - self.register_hook(hook, priority='HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = mmcv.build_from_cfg(optimizer_config, HOOKS) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def register_checkpoint_hook(self, checkpoint_config): - if checkpoint_config is None: - return - if isinstance(checkpoint_config, dict): - checkpoint_config.setdefault('type', 'CheckpointHook') - hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) - else: - hook = checkpoint_config - self.register_hook(hook, priority='NORMAL') - - def register_logger_hooks(self, log_config): - if log_config is None: - return - log_interval = log_config['interval'] - for info in log_config['hooks']: - logger_hook = mmcv.build_from_cfg( - info, HOOKS, default_args=dict(interval=log_interval)) - self.register_hook(logger_hook, priority='VERY_LOW') - - def register_timer_hook(self, timer_config): - if timer_config is None: - return - if isinstance(timer_config, dict): - timer_config_ = copy.deepcopy(timer_config) - hook = mmcv.build_from_cfg(timer_config_, HOOKS) - else: - hook = timer_config - self.register_hook(hook, priority='LOW') - - def register_custom_hooks(self, custom_config): - if custom_config is None: - return - - if not isinstance(custom_config, list): - custom_config = [custom_config] - - for item in custom_config: - if isinstance(item, dict): - self.register_hook_from_cfg(item) - else: - self.register_hook(item, priority='NORMAL') - - def register_profiler_hook(self, profiler_config): - if profiler_config is None: - return - if isinstance(profiler_config, dict): - profiler_config.setdefault('type', 'ProfilerHook') - hook = mmcv.build_from_cfg(profiler_config, HOOKS) - else: - hook = profiler_config - self.register_hook(hook) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - timer_config=dict(type='IterTimerHook'), - custom_hooks_config=None): - """Register default and custom hooks for training. - - Default and custom hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - self.register_lr_hook(lr_config) - self.register_momentum_hook(momentum_config) - self.register_optimizer_hook(optimizer_config) - self.register_checkpoint_hook(checkpoint_config) - self.register_timer_hook(timer_config) - self.register_logger_hooks(log_config) - self.register_custom_hooks(custom_hooks_config) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/builder.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/builder.py deleted file mode 100644 index 77c96ba0b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/builder.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from ..utils import Registry - -RUNNERS = Registry('runner') -RUNNER_BUILDERS = Registry('runner builder') - - -def build_runner_constructor(cfg): - return RUNNER_BUILDERS.build(cfg) - - -def build_runner(cfg, default_args=None): - runner_cfg = copy.deepcopy(cfg) - constructor_type = runner_cfg.pop('constructor', - 'DefaultRunnerConstructor') - runner_constructor = build_runner_constructor( - dict( - type=constructor_type, - runner_cfg=runner_cfg, - default_args=default_args)) - runner = runner_constructor() - return runner diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/checkpoint.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/checkpoint.py deleted file mode 100644 index 835ee725a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/checkpoint.py +++ /dev/null @@ -1,759 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os -import os.path as osp -import pkgutil -import re -import time -import warnings -from collections import OrderedDict -from importlib import import_module -from tempfile import TemporaryDirectory - -import torch -import torchvision -from torch.optim import Optimizer - -import mmcv -from ..fileio import FileClient -from ..fileio import load as load_file -from ..parallel import is_module_wrapper -from ..utils import digit_version, load_url, mkdir_or_exist -from .dist_utils import get_dist_info - -ENV_MMCV_HOME = 'MMCV_HOME' -ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' -DEFAULT_CACHE_DIR = '~/.cache' - - -def _get_mmcv_home(): - mmcv_home = os.path.expanduser( - os.getenv( - ENV_MMCV_HOME, - os.path.join( - os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) - - mkdir_or_exist(mmcv_home) - return mmcv_home - - -def load_state_dict(module, state_dict, strict=False, logger=None): - """Load state_dict to a module. - - This method is modified from :meth:`torch.nn.Module.load_state_dict`. - Default value for ``strict`` is set to ``False`` and the message for - param mismatch will be shown even if strict is False. - - Args: - module (Module): Module that receives the state_dict. - state_dict (OrderedDict): Weights. - strict (bool): whether to strictly enforce that the keys - in :attr:`state_dict` match the keys returned by this module's - :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. - logger (:obj:`logging.Logger`, optional): Logger to log the error - message. If not specified, print function will be used. - """ - unexpected_keys = [] - all_missing_keys = [] - err_msg = [] - - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata - - # use _load_from_state_dict to enable checkpoint version control - def load(module, prefix=''): - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - local_metadata = {} if metadata is None else metadata.get( - prefix[:-1], {}) - module._load_from_state_dict(state_dict, prefix, local_metadata, True, - all_missing_keys, unexpected_keys, - err_msg) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(module) - load = None # break load->load reference cycle - - # ignore "num_batches_tracked" of BN layers - missing_keys = [ - key for key in all_missing_keys if 'num_batches_tracked' not in key - ] - - if unexpected_keys: - err_msg.append('unexpected key in source ' - f'state_dict: {", ".join(unexpected_keys)}\n') - if missing_keys: - err_msg.append( - f'missing keys in source state_dict: {", ".join(missing_keys)}\n') - - rank, _ = get_dist_info() - if len(err_msg) > 0 and rank == 0: - err_msg.insert( - 0, 'The model and loaded state dict do not match exactly\n') - err_msg = '\n'.join(err_msg) - if strict: - raise RuntimeError(err_msg) - elif logger is not None: - logger.warning(err_msg) - else: - print(err_msg) - - -def get_torchvision_models(): - if digit_version(torchvision.__version__) < digit_version('0.13.0a0'): - model_urls = dict() - # When the version of torchvision is lower than 0.13, the model url is - # not declared in `torchvision.model.__init__.py`, so we need to - # iterate through `torchvision.models.__path__` to get the url for each - # model. - for _, name, ispkg in pkgutil.walk_packages( - torchvision.models.__path__): - if ispkg: - continue - _zoo = import_module(f'torchvision.models.{name}') - if hasattr(_zoo, 'model_urls'): - _urls = getattr(_zoo, 'model_urls') - model_urls.update(_urls) - else: - # Since torchvision bumps to v0.13, the weight loading logic, - # model keys and model urls have been changed. Here the URLs of old - # version is loaded to avoid breaking back compatibility. If the - # torchvision version>=0.13.0, new URLs will be added. Users can get - # the resnet50 checkpoint by setting 'resnet50.imagent1k_v1', - # 'resnet50' or 'ResNet50_Weights.IMAGENET1K_V1' in the config. - json_path = osp.join(mmcv.__path__[0], - 'model_zoo/torchvision_0.12.json') - model_urls = mmcv.load(json_path) - for cls_name, cls in torchvision.models.__dict__.items(): - # The name of torchvision model weights classes ends with - # `_Weights` such as `ResNet18_Weights`. However, some model weight - # classes, such as `MNASNet0_75_Weights` does not have any urls in - # torchvision 0.13.0 and cannot be iterated. Here we simply check - # `DEFAULT` attribute to ensure the class is not empty. - if (not cls_name.endswith('_Weights') - or not hasattr(cls, 'DEFAULT')): - continue - # Since `cls.DEFAULT` can not be accessed by iterating cls, we set - # default urls explicitly. - cls_key = cls_name.replace('_Weights', '').lower() - model_urls[f'{cls_key}.default'] = cls.DEFAULT.url - for weight_enum in cls: - cls_key = cls_name.replace('_Weights', '').lower() - cls_key = f'{cls_key}.{weight_enum.name.lower()}' - model_urls[cls_key] = weight_enum.url - - return model_urls - - -def get_external_models(): - mmcv_home = _get_mmcv_home() - default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') - default_urls = load_file(default_json_path) - assert isinstance(default_urls, dict) - external_json_path = osp.join(mmcv_home, 'open_mmlab.json') - if osp.exists(external_json_path): - external_urls = load_file(external_json_path) - assert isinstance(external_urls, dict) - default_urls.update(external_urls) - - return default_urls - - -def get_mmcls_models(): - mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') - mmcls_urls = load_file(mmcls_json_path) - - return mmcls_urls - - -def get_deprecated_model_names(): - deprecate_json_path = osp.join(mmcv.__path__[0], - 'model_zoo/deprecated.json') - deprecate_urls = load_file(deprecate_json_path) - assert isinstance(deprecate_urls, dict) - - return deprecate_urls - - -def _process_mmcls_checkpoint(checkpoint): - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - # Some checkpoints converted from 3rd-party repo don't - # have the "state_dict" key. - state_dict = checkpoint - new_state_dict = OrderedDict() - for k, v in state_dict.items(): - if k.startswith('backbone.'): - new_state_dict[k[9:]] = v - new_checkpoint = dict(state_dict=new_state_dict) - - return new_checkpoint - - -class CheckpointLoader: - """A general checkpoint loader to manage all schemes.""" - - _schemes = {} - - @classmethod - def _register_scheme(cls, prefixes, loader, force=False): - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if (prefix not in cls._schemes) or force: - cls._schemes[prefix] = loader - else: - raise KeyError( - f'{prefix} is already registered as a loader backend, ' - 'add "force=True" if you want to override it') - # sort, longer prefixes take priority - cls._schemes = OrderedDict( - sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) - - @classmethod - def register_scheme(cls, prefixes, loader=None, force=False): - """Register a loader to CheckpointLoader. - - This method can be used as a normal class method or a decorator. - - Args: - prefixes (str or list[str] or tuple[str]): - The prefix of the registered loader. - loader (function, optional): The loader function to be registered. - When this method is used as a decorator, loader is None. - Defaults to None. - force (bool, optional): Whether to override the loader - if the prefix has already been registered. Defaults to False. - """ - - if loader is not None: - cls._register_scheme(prefixes, loader, force=force) - return - - def _register(loader_cls): - cls._register_scheme(prefixes, loader_cls, force=force) - return loader_cls - - return _register - - @classmethod - def _get_checkpoint_loader(cls, path): - """Finds a loader that supports the given path. Falls back to the local - loader if no other loader is found. - - Args: - path (str): checkpoint path - - Returns: - callable: checkpoint loader - """ - for p in cls._schemes: - # use regular match to handle some cases that where the prefix of - # loader has a prefix. For example, both 's3://path' and - # 'open-mmlab:s3://path' should return `load_from_ceph` - if re.match(p, path) is not None: - return cls._schemes[p] - - @classmethod - def load_checkpoint(cls, filename, map_location=None, logger=None): - """load checkpoint through URL scheme path. - - Args: - filename (str): checkpoint file name with given prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - logger (:mod:`logging.Logger`, optional): The logger for message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint_loader = cls._get_checkpoint_loader(filename) - class_name = checkpoint_loader.__name__ - mmcv.print_log( - f'load checkpoint from {class_name[10:]} path: {filename}', logger) - return checkpoint_loader(filename, map_location) - - -@CheckpointLoader.register_scheme(prefixes='') -def load_from_local(filename, map_location): - """load checkpoint by local file path. - - Args: - filename (str): local checkpoint file path - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - filename = osp.expanduser(filename) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) -def load_from_http(filename, map_location=None, model_dir=None): - """load checkpoint through HTTP or HTTPS scheme path. In distributed - setting, this function only download checkpoint at local rank 0. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - model_dir (string, optional): directory in which to save the object, - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - rank, world_size = get_dist_info() - if rank == 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - if world_size > 1: - torch.distributed.barrier() - if rank > 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='pavi://') -def load_from_pavi(filename, map_location=None): - """load checkpoint through the file path prefixed with pavi. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with pavi prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - assert filename.startswith('pavi://'), \ - f'Expected filename startswith `pavi://`, but get {filename}' - model_path = filename[7:] - - try: - from pavi import modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - - model = modelcloud.get(model_path) - with TemporaryDirectory() as tmp_dir: - downloaded_file = osp.join(tmp_dir, model.name) - model.download(downloaded_file) - checkpoint = torch.load(downloaded_file, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=r'(\S+\:)?s3://') -def load_from_ceph(filename, map_location=None, backend='petrel'): - """load checkpoint through the file path prefixed with s3. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Note: - Since v1.4.1, the registered scheme prefixes have been enhanced to - support bucket names in the path prefix, e.g. 's3://xx.xx/xx.path', - 'bucket1:s3://xx.xx/xx.path'. - - Args: - filename (str): checkpoint file path with s3 prefix - map_location (str, optional): Same as :func:`torch.load`. - backend (str, optional): The storage backend type. Options are 'ceph', - 'petrel'. Default: 'petrel'. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - allowed_backends = ['ceph', 'petrel'] - if backend not in allowed_backends: - raise ValueError(f'Load from Backend {backend} is not supported.') - - if backend == 'ceph': - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - - # CephClient and PetrelBackend have the same prefix 's3://' and the latter - # will be chosen as default. If PetrelBackend can not be instantiated - # successfully, the CephClient will be chosen. - try: - file_client = FileClient(backend=backend) - except ImportError: - allowed_backends.remove(backend) - file_client = FileClient(backend=allowed_backends[0]) - - with io.BytesIO(file_client.get(filename)) as buffer: - checkpoint = torch.load(buffer, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) -def load_from_torchvision(filename, map_location=None): - """load checkpoint through the file path prefixed with modelzoo or - torchvision. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - model_urls = get_torchvision_models() - if filename.startswith('modelzoo://'): - warnings.warn( - 'The URL scheme of "modelzoo://" is deprecated, please ' - 'use "torchvision://" instead', DeprecationWarning) - model_name = filename[11:] - else: - model_name = filename[14:] - - # Support getting model urls in the same way as torchvision - # `ResNet50_Weights.IMAGENET1K_V1` will be mapped to - # resnet50.imagenet1k_v1. - model_name = model_name.lower().replace('_weights', '') - return load_from_http(model_urls[model_name], map_location=map_location) - - -@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) -def load_from_openmmlab(filename, map_location=None): - """load checkpoint through the file path prefixed with open-mmlab or - openmmlab. - - Args: - filename (str): checkpoint file path with open-mmlab or - openmmlab prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_external_models() - prefix_str = 'open-mmlab://' - if filename.startswith(prefix_str): - model_name = filename[13:] - else: - model_name = filename[12:] - prefix_str = 'openmmlab://' - - deprecated_urls = get_deprecated_model_names() - if model_name in deprecated_urls: - warnings.warn( - f'{prefix_str}{model_name} is deprecated in favor ' - f'of {prefix_str}{deprecated_urls[model_name]}', - DeprecationWarning) - model_name = deprecated_urls[model_name] - model_url = model_urls[model_name] - # check if is url - if model_url.startswith(('http://', 'https://')): - checkpoint = load_from_http(model_url, map_location=map_location) - else: - filename = osp.join(_get_mmcv_home(), model_url) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='mmcls://') -def load_from_mmcls(filename, map_location=None): - """load checkpoint through the file path prefixed with mmcls. - - Args: - filename (str): checkpoint file path with mmcls prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_mmcls_models() - model_name = filename[8:] - checkpoint = load_from_http( - model_urls[model_name], map_location=map_location) - checkpoint = _process_mmcls_checkpoint(checkpoint) - return checkpoint - - -def _load_checkpoint(filename, map_location=None, logger=None): - """Load checkpoint from somewhere (modelzoo, file, url). - - Args: - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str, optional): Same as :func:`torch.load`. - Default: None. - logger (:mod:`logging.Logger`, optional): The logger for error message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. It can be either an - OrderedDict storing model weights or a dict containing other - information, which depends on the checkpoint. - """ - return CheckpointLoader.load_checkpoint(filename, map_location, logger) - - -def _load_checkpoint_with_prefix(prefix, filename, map_location=None): - """Load partial pretrained model with specific prefix. - - Args: - prefix (str): The prefix of sub-module. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str | None): Same as :func:`torch.load`. Default: None. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint = _load_checkpoint(filename, map_location=map_location) - - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - if not prefix.endswith('.'): - prefix += '.' - prefix_len = len(prefix) - - state_dict = { - k[prefix_len:]: v - for k, v in state_dict.items() if k.startswith(prefix) - } - - assert state_dict, f'{prefix} is not in the pretrained model' - return state_dict - - -def load_checkpoint(model, - filename, - map_location=None, - strict=False, - logger=None, - revise_keys=[(r'^module\.', '')]): - """Load checkpoint from a file or URI. - - Args: - model (Module): Module to load checkpoint. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str): Same as :func:`torch.load`. - strict (bool): Whether to allow different params for the model and - checkpoint. - logger (:mod:`logging.Logger` or None): The logger for error message. - revise_keys (list): A list of customized keywords to modify the - state_dict in checkpoint. Each item is a (pattern, replacement) - pair of the regular expression operations. Default: strip - the prefix 'module.' by [(r'^module\\.', '')]. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - checkpoint = _load_checkpoint(filename, map_location, logger) - # OrderedDict is a subclass of dict - if not isinstance(checkpoint, dict): - raise RuntimeError( - f'No state_dict found in checkpoint file {filename}') - # get state_dict from checkpoint - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - - # strip prefix of state_dict - metadata = getattr(state_dict, '_metadata', OrderedDict()) - for p, r in revise_keys: - state_dict = OrderedDict( - {re.sub(p, r, k): v - for k, v in state_dict.items()}) - # Keep metadata in state_dict - state_dict._metadata = metadata - - # load state_dict - load_state_dict(model, state_dict, strict, logger) - return checkpoint - - -def weights_to_cpu(state_dict): - """Copy a model state_dict to cpu. - - Args: - state_dict (OrderedDict): Model weights on GPU. - - Returns: - OrderedDict: Model weights on GPU. - """ - state_dict_cpu = OrderedDict() - for key, val in state_dict.items(): - state_dict_cpu[key] = val.cpu() - # Keep metadata in state_dict - state_dict_cpu._metadata = getattr(state_dict, '_metadata', OrderedDict()) - return state_dict_cpu - - -def _save_to_state_dict(module, destination, prefix, keep_vars): - """Saves module state to `destination` dictionary. - - This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. - - Args: - module (nn.Module): The module to generate state_dict. - destination (dict): A dict where state will be stored. - prefix (str): The prefix for parameters and buffers used in this - module. - """ - for name, param in module._parameters.items(): - if param is not None: - destination[prefix + name] = param if keep_vars else param.detach() - for name, buf in module._buffers.items(): - # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d - if buf is not None: - destination[prefix + name] = buf if keep_vars else buf.detach() - - -def get_state_dict(module, destination=None, prefix='', keep_vars=False): - """Returns a dictionary containing a whole state of the module. - - Both parameters and persistent buffers (e.g. running averages) are - included. Keys are corresponding parameter and buffer names. - - This method is modified from :meth:`torch.nn.Module.state_dict` to - recursively check parallel module in case that the model has a complicated - structure, e.g., nn.Module(nn.Module(DDP)). - - Args: - module (nn.Module): The module to generate state_dict. - destination (OrderedDict): Returned dict for the state of the - module. - prefix (str): Prefix of the key. - keep_vars (bool): Whether to keep the variable property of the - parameters. Default: False. - - Returns: - dict: A dictionary containing a whole state of the module. - """ - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - - # below is the same as torch.nn.Module.state_dict() - if destination is None: - destination = OrderedDict() - destination._metadata = OrderedDict() - destination._metadata[prefix[:-1]] = local_metadata = dict( - version=module._version) - _save_to_state_dict(module, destination, prefix, keep_vars) - for name, child in module._modules.items(): - if child is not None: - get_state_dict( - child, destination, prefix + name + '.', keep_vars=keep_vars) - for hook in module._state_dict_hooks.values(): - hook_result = hook(module, destination, prefix, local_metadata) - if hook_result is not None: - destination = hook_result - return destination - - -def save_checkpoint(model, - filename, - optimizer=None, - meta=None, - file_client_args=None): - """Save checkpoint to file. - - The checkpoint will have 3 fields: ``meta``, ``state_dict`` and - ``optimizer``. By default ``meta`` will contain version and time info. - - Args: - model (Module): Module whose params are to be saved. - filename (str): Checkpoint filename. - optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. - meta (dict, optional): Metadata to be saved in checkpoint. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError(f'meta must be a dict or None, but got {type(meta)}') - meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) - - if is_module_wrapper(model): - model = model.module - - if hasattr(model, 'CLASSES') and model.CLASSES is not None: - # save class name to the meta - meta.update(CLASSES=model.CLASSES) - - checkpoint = { - 'meta': meta, - 'state_dict': weights_to_cpu(get_state_dict(model)) - } - # save optimizer state dict in the checkpoint - if isinstance(optimizer, Optimizer): - checkpoint['optimizer'] = optimizer.state_dict() - elif isinstance(optimizer, dict): - checkpoint['optimizer'] = {} - for name, optim in optimizer.items(): - checkpoint['optimizer'][name] = optim.state_dict() - - if filename.startswith('pavi://'): - if file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" if filename starts with' - f'"pavi://", but got {file_client_args}') - try: - from pavi import exception, modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - model_path = filename[7:] - root = modelcloud.Folder() - model_dir, model_name = osp.split(model_path) - try: - model = modelcloud.get(model_dir) - except exception.NodeNotFoundError: - model = root.create_training_model(model_dir) - with TemporaryDirectory() as tmp_dir: - checkpoint_file = osp.join(tmp_dir, model_name) - with open(checkpoint_file, 'wb') as f: - torch.save(checkpoint, f) - f.flush() - model.create_file(checkpoint_file, name=model_name) - else: - file_client = FileClient.infer_client(file_client_args, filename) - with io.BytesIO() as f: - torch.save(checkpoint, f) - file_client.put(f.getvalue(), filename) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/default_constructor.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/default_constructor.py deleted file mode 100644 index 4a4f2cc64..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/default_constructor.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import RUNNER_BUILDERS, RUNNERS - - -@RUNNER_BUILDERS.register_module() -class DefaultRunnerConstructor: - """Default constructor for runners. - - Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. - For example, We can inject some new properties and functions for `Runner`. - - Example: - >>> from mmcv.runner import RUNNER_BUILDERS, build_runner - >>> # Define a new RunnerReconstructor - >>> @RUNNER_BUILDERS.register_module() - >>> class MyRunnerConstructor: - ... def __init__(self, runner_cfg, default_args=None): - ... if not isinstance(runner_cfg, dict): - ... raise TypeError('runner_cfg should be a dict', - ... f'but got {type(runner_cfg)}') - ... self.runner_cfg = runner_cfg - ... self.default_args = default_args - ... - ... def __call__(self): - ... runner = RUNNERS.build(self.runner_cfg, - ... default_args=self.default_args) - ... # Add new properties for existing runner - ... runner.my_name = 'my_runner' - ... runner.my_function = lambda self: print(self.my_name) - ... ... - >>> # build your runner - >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, - ... constructor='MyRunnerConstructor') - >>> runner = build_runner(runner_cfg) - """ - - def __init__(self, runner_cfg, default_args=None): - if not isinstance(runner_cfg, dict): - raise TypeError('runner_cfg should be a dict', - f'but got {type(runner_cfg)}') - self.runner_cfg = runner_cfg - self.default_args = default_args - - def __call__(self): - return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/dist_utils.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/dist_utils.py deleted file mode 100644 index 26d77a8f9..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/dist_utils.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved -import functools -import os -import socket -import subprocess -from collections import OrderedDict - -import torch -import torch.multiprocessing as mp -from torch import distributed as dist -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import IS_MLU_AVAILABLE - - -def _find_free_port(): - # Copied from https://github.com/facebookresearch/detectron2/blob/main/detectron2/engine/launch.py # noqa: E501 - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - # Binding to port 0 will cause the OS to find an available port for us - sock.bind(('', 0)) - port = sock.getsockname()[1] - sock.close() - # NOTE: there is still a chance the port could be taken by other processes. - return port - - -def _is_free_port(port): - ips = socket.gethostbyname_ex(socket.gethostname())[-1] - ips.append('localhost') - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - return all(s.connect_ex((ip, port)) != 0 for ip in ips) - - -def init_dist(launcher, backend='nccl', **kwargs): - if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') - if launcher == 'pytorch': - _init_dist_pytorch(backend, **kwargs) - elif launcher == 'mpi': - _init_dist_mpi(backend, **kwargs) - elif launcher == 'slurm': - _init_dist_slurm(backend, **kwargs) - else: - raise ValueError(f'Invalid launcher type: {launcher}') - - -def _init_dist_pytorch(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['RANK']) - if IS_MLU_AVAILABLE: - import torch_mlu # noqa: F401 - torch.mlu.set_device(rank) - dist.init_process_group( - backend='cncl', - rank=rank, - world_size=int(os.environ['WORLD_SIZE']), - **kwargs) - else: - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_mpi(backend, **kwargs): - local_rank = int(os.environ['OMPI_COMM_WORLD_LOCAL_RANK']) - torch.cuda.set_device(local_rank) - if 'MASTER_PORT' not in os.environ: - # 29500 is torch.distributed default port - os.environ['MASTER_PORT'] = '29500' - if 'MASTER_ADDR' not in os.environ: - raise KeyError('The environment variable MASTER_ADDR is not set') - os.environ['WORLD_SIZE'] = os.environ['OMPI_COMM_WORLD_SIZE'] - os.environ['RANK'] = os.environ['OMPI_COMM_WORLD_RANK'] - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_slurm(backend, port=None): - """Initialize slurm distributed training environment. - - If argument ``port`` is not specified, then the master port will be system - environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system - environment variable, then a default port ``29500`` will be used. - - Args: - backend (str): Backend of torch.distributed. - port (int, optional): Master port. Defaults to None. - """ - proc_id = int(os.environ['SLURM_PROCID']) - ntasks = int(os.environ['SLURM_NTASKS']) - node_list = os.environ['SLURM_NODELIST'] - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(proc_id % num_gpus) - addr = subprocess.getoutput( - f'scontrol show hostname {node_list} | head -n1') - # specify master port - if port is not None: - os.environ['MASTER_PORT'] = str(port) - elif 'MASTER_PORT' in os.environ: - pass # use MASTER_PORT in the environment variable - else: - # if torch.distributed default port(29500) is available - # then use it, else find a free port - if _is_free_port(29500): - os.environ['MASTER_PORT'] = '29500' - else: - os.environ['MASTER_PORT'] = str(_find_free_port()) - # use MASTER_ADDR in the environment variable if it already exists - if 'MASTER_ADDR' not in os.environ: - os.environ['MASTER_ADDR'] = addr - os.environ['WORLD_SIZE'] = str(ntasks) - os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) - os.environ['RANK'] = str(proc_id) - dist.init_process_group(backend=backend) - - -def get_dist_info(): - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - world_size = dist.get_world_size() - else: - rank = 0 - world_size = 1 - return rank, world_size - - -def master_only(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - rank, _ = get_dist_info() - if rank == 0: - return func(*args, **kwargs) - - return wrapper - - -def allreduce_params(params, coalesce=True, bucket_size_mb=-1): - """Allreduce parameters. - - Args: - params (list[torch.Parameters]): List of parameters or buffers of a - model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - _, world_size = get_dist_info() - if world_size == 1: - return - params = [param.data for param in params] - if coalesce: - _allreduce_coalesced(params, world_size, bucket_size_mb) - else: - for tensor in params: - dist.all_reduce(tensor.div_(world_size)) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - _, world_size = get_dist_info() - if world_size == 1: - return - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/epoch_based_runner.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/epoch_based_runner.py deleted file mode 100644 index 078e91df3..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/epoch_based_runner.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .utils import get_host_info - - -@RUNNERS.register_module() -class EpochBasedRunner(BaseRunner): - """Epoch-based Runner. - - This runner train models epoch by epoch. - """ - - def run_iter(self, data_batch, train_mode, **kwargs): - if self.batch_processor is not None: - outputs = self.batch_processor( - self.model, data_batch, train_mode=train_mode, **kwargs) - elif train_mode: - outputs = self.model.train_step(data_batch, self.optimizer, - **kwargs) - else: - outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('"batch_processor()" or "model.train_step()"' - 'and "model.val_step()" must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - self._iter += 1 - - self.call_hook('after_train_epoch') - self._epoch += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - self.call_hook('before_val_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_val_iter') - self.run_iter(data_batch, train_mode=False) - self.call_hook('after_val_iter') - - self.call_hook('after_val_epoch') - - def run(self, data_loaders, workflow, max_epochs=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, epochs) to specify the - running order and epochs. E.g, [('train', 2), ('val', 1)] means - running 2 epochs for training and 1 epoch for validation, - iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_epochs is not None: - warnings.warn( - 'setting max_epochs in run is deprecated, ' - 'please set max_epochs in runner_config', DeprecationWarning) - self._max_epochs = max_epochs - - assert self._max_epochs is not None, ( - 'max_epochs must be specified during instantiation') - - for i, flow in enumerate(workflow): - mode, epochs = flow - if mode == 'train': - self._max_iters = self._max_epochs * len(data_loaders[i]) - break - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d epochs', workflow, - self._max_epochs) - self.call_hook('before_run') - - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - mode, epochs = flow - if isinstance(mode, str): # self.train() - if not hasattr(self, mode): - raise ValueError( - f'runner has no method named "{mode}" to run an ' - 'epoch') - epoch_runner = getattr(self, mode) - else: - raise TypeError( - 'mode in workflow must be a str, but got {}'.format( - type(mode))) - - for _ in range(epochs): - if mode == 'train' and self.epoch >= self._max_epochs: - break - epoch_runner(data_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_run') - - def save_checkpoint(self, - out_dir, - filename_tmpl='epoch_{}.pth', - save_optimizer=True, - meta=None, - create_symlink=True): - """Save the checkpoint. - - Args: - out_dir (str): The directory that checkpoints are saved. - filename_tmpl (str, optional): The checkpoint filename template, - which contains a placeholder for the epoch number. - Defaults to 'epoch_{}.pth'. - save_optimizer (bool, optional): Whether to save the optimizer to - the checkpoint. Defaults to True. - meta (dict, optional): The meta information to be saved in the - checkpoint. Defaults to None. - create_symlink (bool, optional): Whether to create a symlink - "latest.pth" to point to the latest checkpoint. - Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.epoch + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - -@RUNNERS.register_module() -class Runner(EpochBasedRunner): - """Deprecated name of EpochBasedRunner.""" - - def __init__(self, *args, **kwargs): - warnings.warn( - 'Runner was deprecated, please use EpochBasedRunner instead', - DeprecationWarning) - super().__init__(*args, **kwargs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/fp16_utils.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/fp16_utils.py deleted file mode 100644 index be3ac3a31..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/fp16_utils.py +++ /dev/null @@ -1,423 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import warnings -from collections import abc -from inspect import getfullargspec - -import numpy as np -import torch -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .dist_utils import allreduce_grads as _allreduce_grads - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 - # manually, so the behavior may not be consistent with real amp. - from torch.cuda.amp import autocast -except ImportError: - pass - - -def cast_tensor_type(inputs, src_type, dst_type): - """Recursively convert Tensor in inputs from src_type to dst_type. - - Note: - In v1.4.4 and later, ``cast_tersor_type`` will only convert the - torch.Tensor which is consistent with ``src_type`` to the ``dst_type``. - Before v1.4.4, it ignores the ``src_type`` argument, leading to some - potential problems. For example, - ``cast_tensor_type(inputs, torch.float, torch.half)`` will convert all - tensors in inputs to ``torch.half`` including those originally in - ``torch.Int`` or other types, which is not expected. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype): Source type.. - dst_type (torch.dtype): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - if isinstance(inputs, nn.Module): - return inputs - elif isinstance(inputs, torch.Tensor): - # we need to ensure that the type of inputs to be casted are the same - # as the argument `src_type`. - return inputs.to(dst_type) if inputs.dtype == src_type else inputs - elif isinstance(inputs, str): - return inputs - elif isinstance(inputs, np.ndarray): - return inputs - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type, dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type, dst_type) for item in inputs) - else: - return inputs - - -def auto_fp16(apply_to=None, out_fp32=False, supported_types=(nn.Module, )): - """Decorator to enable fp16 training automatically. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If inputs arguments are fp32 tensors, they will - be converted to fp16 automatically. Arguments other than fp32 tensors are - ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp32 (bool): Whether to convert the output back to fp32. - supported_types (tuple): Classes can be decorated by ``auto_fp16``. - `New in version 1.5.0.` - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp16 - >>> @auto_fp16() - >>> def forward(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp16 - >>> @auto_fp16(apply_to=('pred', )) - >>> def do_something(self, pred, others): - >>> pass - """ - - def auto_fp16_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], supported_types): - raise TypeError('@auto_fp16 can only be used to decorate the ' - f'method of those classes {supported_types}') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - # NOTE: default args are not taken into consideration - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.float, torch.half)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = {} - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.float, torch.half) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=True): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp32: - output = cast_tensor_type(output, torch.half, torch.float) - return output - - return new_func - - return auto_fp16_wrapper - - -def force_fp32(apply_to=None, out_fp16=False): - """Decorator to convert input arguments to fp32 in force. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If there are some inputs that must be processed - in fp32 mode, then this decorator can handle it. If inputs arguments are - fp16 tensors, they will be converted to fp32 automatically. Arguments other - than fp16 tensors are ignored. If you are using PyTorch >= 1.6, - torch.cuda.amp is used as the backend, otherwise, original mmcv - implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp16 (bool): Whether to convert the output back to fp16. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp32 - >>> @force_fp32() - >>> def loss(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp32 - >>> @force_fp32(apply_to=('pred', )) - >>> def post_process(self, pred, others): - >>> pass - """ - - def force_fp32_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@force_fp32 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.half, torch.float)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = dict() - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.half, torch.float) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=False): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp16: - output = cast_tensor_type(output, torch.float, torch.half) - return output - - return new_func - - return force_fp32_wrapper - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - warnings.warning( - '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' - 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads', - DeprecationWarning) - _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) - - -def wrap_fp16_model(model): - """Wrap the FP32 model to FP16. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - For PyTorch >= 1.6, this function will - 1. Set fp16 flag inside the model to True. - - Otherwise: - 1. Convert FP32 model to FP16. - 2. Remain some necessary layers to be FP32, e.g., normalization layers. - 3. Set `fp16_enabled` flag inside the model to True. - - Args: - model (nn.Module): Model in FP32. - """ - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.6.0')): - # convert model to fp16 - model.half() - # patch the normalization layers to make it work in fp32 mode - patch_norm_fp32(model) - # set `fp16_enabled` flag - for m in model.modules(): - if hasattr(m, 'fp16_enabled'): - m.fp16_enabled = True - - -def patch_norm_fp32(module): - """Recursively convert normalization layers from FP16 to FP32. - - Args: - module (nn.Module): The modules to be converted in FP16. - - Returns: - nn.Module: The converted module, the normalization layers have been - converted to FP32. - """ - if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): - module.float() - if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': - module.forward = patch_forward_method(module.forward, torch.half, - torch.float) - for child in module.children(): - patch_norm_fp32(child) - return module - - -def patch_forward_method(func, src_type, dst_type, convert_output=True): - """Patch the forward method of a module. - - Args: - func (callable): The original forward method. - src_type (torch.dtype): Type of input arguments to be converted from. - dst_type (torch.dtype): Type of input arguments to be converted to. - convert_output (bool): Whether to convert the output back to src_type. - - Returns: - callable: The patched forward method. - """ - - def new_forward(*args, **kwargs): - output = func(*cast_tensor_type(args, src_type, dst_type), - **cast_tensor_type(kwargs, src_type, dst_type)) - if convert_output: - output = cast_tensor_type(output, dst_type, src_type) - return output - - return new_forward - - -class LossScaler: - """Class that manages loss scaling in mixed precision training which - supports both dynamic or static mode. - - The implementation refers to - https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. - Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. - It's important to understand how :class:`LossScaler` operates. - Loss scaling is designed to combat the problem of underflowing - gradients encountered at long times when training fp16 networks. - Dynamic loss scaling begins by attempting a very high loss - scale. Ironically, this may result in OVERflowing gradients. - If overflowing gradients are encountered, :class:`FP16_Optimizer` then - skips the update step for this particular iteration/minibatch, - and :class:`LossScaler` adjusts the loss scale to a lower value. - If a certain number of iterations occur without overflowing gradients - detected,:class:`LossScaler` increases the loss scale once more. - In this way :class:`LossScaler` attempts to "ride the edge" of always - using the highest loss scale possible without incurring overflow. - - Args: - init_scale (float): Initial loss scale value, default: 2**32. - scale_factor (float): Factor used when adjusting the loss scale. - Default: 2. - mode (str): Loss scaling mode. 'dynamic' or 'static' - scale_window (int): Number of consecutive iterations without an - overflow to wait before increasing the loss scale. Default: 1000. - """ - - def __init__(self, - init_scale=2**32, - mode='dynamic', - scale_factor=2., - scale_window=1000): - self.cur_scale = init_scale - self.cur_iter = 0 - assert mode in ('dynamic', - 'static'), 'mode can only be dynamic or static' - self.mode = mode - self.last_overflow_iter = -1 - self.scale_factor = scale_factor - self.scale_window = scale_window - - def has_overflow(self, params): - """Check if params contain overflow.""" - if self.mode != 'dynamic': - return False - for p in params: - if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): - return True - return False - - def _has_inf_or_nan(x): - """Check if params contain NaN.""" - try: - cpu_sum = float(x.float().sum()) - except RuntimeError as instance: - if 'value cannot be converted' not in instance.args[0]: - raise - return True - else: - if cpu_sum == float('inf') or cpu_sum == -float('inf') \ - or cpu_sum != cpu_sum: - return True - return False - - def update_scale(self, overflow): - """update the current loss scale value when overflow happens.""" - if self.mode != 'dynamic': - return - if overflow: - self.cur_scale = max(self.cur_scale / self.scale_factor, 1) - self.last_overflow_iter = self.cur_iter - else: - if (self.cur_iter - self.last_overflow_iter) % \ - self.scale_window == 0: - self.cur_scale *= self.scale_factor - self.cur_iter += 1 - - def state_dict(self): - """Returns the state of the scaler as a :class:`dict`.""" - return dict( - cur_scale=self.cur_scale, - cur_iter=self.cur_iter, - mode=self.mode, - last_overflow_iter=self.last_overflow_iter, - scale_factor=self.scale_factor, - scale_window=self.scale_window) - - def load_state_dict(self, state_dict): - """Loads the loss_scaler state dict. - - Args: - state_dict (dict): scaler state. - """ - self.cur_scale = state_dict['cur_scale'] - self.cur_iter = state_dict['cur_iter'] - self.mode = state_dict['mode'] - self.last_overflow_iter = state_dict['last_overflow_iter'] - self.scale_factor = state_dict['scale_factor'] - self.scale_window = state_dict['scale_window'] - - @property - def loss_scale(self): - return self.cur_scale diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/__init__.py deleted file mode 100644 index 03e2a619e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkpoint import CheckpointHook -from .closure import ClosureHook -from .ema import EMAHook -from .evaluation import DistEvalHook, EvalHook -from .hook import HOOKS, Hook -from .iter_timer import IterTimerHook -from .logger import (ClearMLLoggerHook, DvcliveLoggerHook, LoggerHook, - MlflowLoggerHook, NeptuneLoggerHook, PaviLoggerHook, - SegmindLoggerHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, InvLrUpdaterHook, - LinearAnnealingLrUpdaterHook, LrUpdaterHook, - OneCycleLrUpdaterHook, PolyLrUpdaterHook, - StepLrUpdaterHook) -from .memory import EmptyCacheHook -from .momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, OptimizerHook) -from .profiler import ProfilerHook -from .sampler_seed import DistSamplerSeedHook -from .sync_buffer import SyncBuffersHook - -__all__ = [ - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'OptimizerHook', - 'Fp16OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', - 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TextLoggerHook', 'TensorboardLoggerHook', 'NeptuneLoggerHook', - 'WandbLoggerHook', 'DvcliveLoggerHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'SyncBuffersHook', 'EMAHook', 'EvalHook', 'DistEvalHook', 'ProfilerHook', - 'GradientCumulativeOptimizerHook', 'GradientCumulativeFp16OptimizerHook', - 'SegmindLoggerHook', 'LinearAnnealingLrUpdaterHook', - 'LinearAnnealingMomentumUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/checkpoint.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/checkpoint.py deleted file mode 100644 index 7bb75f402..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/checkpoint.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -from mmcv.fileio import FileClient -from ..dist_utils import allreduce_params, master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class CheckpointHook(Hook): - """Save checkpoints periodically. - - Args: - interval (int): The saving period. If ``by_epoch=True``, interval - indicates epochs, otherwise it indicates iterations. - Default: -1, which means "never". - by_epoch (bool): Saving checkpoints by epoch or by iteration. - Default: True. - save_optimizer (bool): Whether to save optimizer state_dict in the - checkpoint. It is usually used for resuming experiments. - Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, ``runner.work_dir`` will be used by default. If - specified, the ``out_dir`` will be the concatenation of ``out_dir`` - and the last level directory of ``runner.work_dir``. - `Changed in version 1.3.16.` - max_keep_ckpts (int, optional): The maximum checkpoints to keep. - In some cases we want only the latest few checkpoints and would - like to delete old ones to save the disk space. - Default: -1, which means unlimited. - save_last (bool, optional): Whether to force the last checkpoint to be - saved regardless of interval. Default: True. - sync_buffer (bool, optional): Whether to synchronize buffers in - different gpus. Default: False. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - - .. warning:: - Before v1.3.16, the ``out_dir`` argument indicates the path where the - checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the - root directory and the final path to save checkpoint is the - concatenation of ``out_dir`` and the last level directory of - ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" - and the value of ``runner.work_dir`` is "/path/of/B", then the final - path will be "/path/of/A/B". - """ - - def __init__(self, - interval=-1, - by_epoch=True, - save_optimizer=True, - out_dir=None, - max_keep_ckpts=-1, - save_last=True, - sync_buffer=False, - file_client_args=None, - **kwargs): - self.interval = interval - self.by_epoch = by_epoch - self.save_optimizer = save_optimizer - self.out_dir = out_dir - self.max_keep_ckpts = max_keep_ckpts - self.save_last = save_last - self.args = kwargs - self.sync_buffer = sync_buffer - self.file_client_args = file_client_args - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - - runner.logger.info((f'Checkpoints will be saved to {self.out_dir} by ' - f'{self.file_client.name}.')) - - # disable the create_symlink option because some file backends do not - # allow to create a symlink - if 'create_symlink' in self.args: - if self.args[ - 'create_symlink'] and not self.file_client.allow_symlink: - self.args['create_symlink'] = False - warnings.warn( - ('create_symlink is set as True by the user but is changed' - 'to be False because creating symbolic link is not ' - f'allowed in {self.file_client.name}')) - else: - self.args['create_symlink'] = self.file_client.allow_symlink - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` epochs - # 2. reach the last epoch of training - if self.every_n_epochs( - runner, self.interval) or (self.save_last - and self.is_last_epoch(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.epoch + 1} epochs') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) - - @master_only - def _save_checkpoint(self, runner): - """Save the current checkpoint and delete unwanted checkpoint.""" - runner.save_checkpoint( - self.out_dir, save_optimizer=self.save_optimizer, **self.args) - if runner.meta is not None: - if self.by_epoch: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) - else: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) - runner.meta.setdefault('hook_msgs', dict()) - runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( - self.out_dir, cur_ckpt_filename) - # remove other checkpoints - if self.max_keep_ckpts > 0: - if self.by_epoch: - name = 'epoch_{}.pth' - current_ckpt = runner.epoch + 1 - else: - name = 'iter_{}.pth' - current_ckpt = runner.iter + 1 - redundant_ckpts = range( - current_ckpt - self.max_keep_ckpts * self.interval, 0, - -self.interval) - filename_tmpl = self.args.get('filename_tmpl', name) - for _step in redundant_ckpts: - ckpt_path = self.file_client.join_path( - self.out_dir, filename_tmpl.format(_step)) - if self.file_client.isfile(ckpt_path): - self.file_client.remove(ckpt_path) - else: - break - - def after_train_iter(self, runner): - if self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` iterations - # 2. reach the last iteration of training - if self.every_n_iters( - runner, self.interval) or (self.save_last - and self.is_last_iter(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.iter + 1} iterations') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/closure.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/closure.py deleted file mode 100644 index b955f81f4..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/closure.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ClosureHook(Hook): - - def __init__(self, fn_name, fn): - assert hasattr(self, fn_name) - assert callable(fn) - setattr(self, fn_name, fn) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/ema.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/ema.py deleted file mode 100644 index 6ed77b84e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/ema.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...parallel import is_module_wrapper -from ..hooks.hook import HOOKS, Hook - - -@HOOKS.register_module() -class EMAHook(Hook): - r"""Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. - - .. math:: - - Xema\_{t+1} = (1 - \text{momentum}) \times - Xema\_{t} + \text{momentum} \times X_t - - Args: - momentum (float): The momentum used for updating ema parameter. - Defaults to 0.0002. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - warm_up (int): During first warm_up steps, we may use smaller momentum - to update ema parameters more slowly. Defaults to 100. - resume_from (str): The checkpoint path. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - warm_up=100, - resume_from=None): - assert isinstance(interval, int) and interval > 0 - self.warm_up = warm_up - self.interval = interval - assert momentum > 0 and momentum < 1 - self.momentum = momentum**interval - self.checkpoint = resume_from - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - self.model_parameters = dict(model.named_parameters(recurse=True)) - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers(recurse=True)) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - curr_step = runner.iter - # We warm up the momentum considering the instability at beginning - momentum = min(self.momentum, - (1 + curr_step) / (self.warm_up + curr_step)) - if curr_step % self.interval != 0: - return - for name, parameter in self.model_parameters.items(): - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/evaluation.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/evaluation.py deleted file mode 100644 index 2a57d535e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/evaluation.py +++ /dev/null @@ -1,511 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from math import inf - -import torch.distributed as dist -from torch.nn.modules.batchnorm import _BatchNorm -from torch.utils.data import DataLoader - -from mmcv.fileio import FileClient -from mmcv.utils import is_seq_of -from .hook import Hook -from .logger import LoggerHook - - -class EvalHook(Hook): - """Non-Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader, and return the test results. If ``None``, the default - test function ``mmcv.engine.single_gpu_test`` will be used. - (default: ``None``) - greater_keys (List[str] | None, optional): Metric keys that will be - inferred by 'greater' comparison rule. If ``None``, - _default_greater_keys will be used. (default: ``None``) - less_keys (List[str] | None, optional): Metric keys that will be - inferred by 'less' comparison rule. If ``None``, _default_less_keys - will be used. (default: ``None``) - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - `New in version 1.3.16.` - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - `New in version 1.3.16.` - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - - Note: - If new arguments are added for EvalHook, tools/test.py, - tools/eval_metric.py may be affected. - """ - - # Since the key for determine greater or less is related to the downstream - # tasks, downstream repos may need to overwrite the following inner - # variable accordingly. - - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - _default_greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - _default_less_keys = ['loss'] - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - out_dir=None, - file_client_args=None, - **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError(f'dataloader must be a pytorch DataLoader, ' - f'but got {type(dataloader)}') - - if interval <= 0: - raise ValueError(f'interval must be a positive number, ' - f'but got {interval}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' - - if start is not None and start < 0: - raise ValueError(f'The evaluation start epoch {start} is smaller ' - f'than 0') - - self.dataloader = dataloader - self.interval = interval - self.start = start - self.by_epoch = by_epoch - - assert isinstance(save_best, str) or save_best is None, \ - '""save_best"" should be a str or None ' \ - f'rather than {type(save_best)}' - self.save_best = save_best - self.eval_kwargs = eval_kwargs - self.initial_flag = True - - if test_fn is None: - from mmcv.engine import single_gpu_test - self.test_fn = single_gpu_test - else: - self.test_fn = test_fn - - if greater_keys is None: - self.greater_keys = self._default_greater_keys - else: - if not isinstance(greater_keys, (list, tuple)): - greater_keys = (greater_keys, ) - assert is_seq_of(greater_keys, str) - self.greater_keys = greater_keys - - if less_keys is None: - self.less_keys = self._default_less_keys - else: - if not isinstance(less_keys, (list, tuple)): - less_keys = (less_keys, ) - assert is_seq_of(less_keys, str) - self.less_keys = less_keys - - if self.save_best is not None: - self.best_ckpt_path = None - self._init_rule(rule, self.save_best) - - self.out_dir = out_dir - self.file_client_args = file_client_args - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific (note that the key indicator matching - is case-insensitive): - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator != 'auto': - # `_lc` here means we use the lower case of keys for - # case-insensitive matching - key_indicator_lc = key_indicator.lower() - greater_keys = [key.lower() for key in self.greater_keys] - less_keys = [key.lower() for key in self.less_keys] - - if key_indicator_lc in greater_keys: - rule = 'greater' - elif key_indicator_lc in less_keys: - rule = 'less' - elif any(key in key_indicator_lc for key in greater_keys): - rule = 'greater' - elif any(key in key_indicator_lc for key in less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - if self.rule is not None: - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'The best checkpoint will be saved to {self.out_dir} by ' - f'{self.file_client.name}')) - - if self.save_best is not None: - if runner.meta is None: - warnings.warn('runner.meta is None. Creating an empty one.') - runner.meta = dict() - runner.meta.setdefault('hook_msgs', dict()) - self.best_ckpt_path = runner.meta['hook_msgs'].get( - 'best_ckpt', None) - - def before_train_iter(self, runner): - """Evaluate the model only at the start of training by iteration.""" - if self.by_epoch or not self.initial_flag: - return - if self.start is not None and runner.iter >= self.start: - self.after_train_iter(runner) - self.initial_flag = False - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - if not (self.by_epoch and self.initial_flag): - return - if self.start is not None and runner.epoch >= self.start: - self.after_train_epoch(runner) - self.initial_flag = False - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch and self._should_evaluate(runner): - # Because the priority of EvalHook is higher than LoggerHook, the - # training log and the evaluating log are mixed. Therefore, - # we need to dump the training log and clear it before evaluating - # log is generated. In addition, this problem will only appear in - # `IterBasedRunner` whose `self.by_epoch` is False, because - # `EpochBasedRunner` whose `self.by_epoch` is True calls - # `_do_evaluate` in `after_train_epoch` stage, and at this stage - # the training log has been printed, so it will not cause any - # problem. more details at - # https://github.com/open-mmlab/mmsegmentation/issues/694 - for hook in runner._hooks: - if isinstance(hook, LoggerHook): - hook.after_train_iter(runner) - runner.log_buffer.clear() - - self._do_evaluate(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch and self._should_evaluate(runner): - self._do_evaluate(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - results = self.test_fn(runner.model, self.dataloader) - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - def _should_evaluate(self, runner): - """Judge whether to perform evaluation. - - Here is the rule to judge whether to perform evaluation: - 1. It will not perform evaluation during the epoch/iteration interval, - which is determined by ``self.interval``. - 2. It will not perform evaluation if the start time is larger than - current time. - 3. It will not perform evaluation when current time is larger than - the start time but during epoch/iteration interval. - - Returns: - bool: The flag indicating whether to perform evaluation. - """ - if self.by_epoch: - current = runner.epoch - check_time = self.every_n_epochs - else: - current = runner.iter - check_time = self.every_n_iters - - if self.start is None: - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - elif (current + 1) < self.start: - # No evaluation if start is larger than the current time. - return False - else: - # Evaluation only at epochs/iters 3, 5, 7... - # if start==3 and interval==2 - if (current + 1 - self.start) % self.interval: - return False - return True - - def _save_ckpt(self, runner, key_score): - """Save the best checkpoint. - - It will compare the score according to the compare function, write - related information (best score, best checkpoint path) and save the - best checkpoint into ``work_dir``. - """ - if self.by_epoch: - current = f'epoch_{runner.epoch + 1}' - cur_type, cur_time = 'epoch', runner.epoch + 1 - else: - current = f'iter_{runner.iter + 1}' - cur_type, cur_time = 'iter', runner.iter + 1 - - best_score = runner.meta['hook_msgs'].get( - 'best_score', self.init_value_map[self.rule]) - if self.compare_func(key_score, best_score): - best_score = key_score - runner.meta['hook_msgs']['best_score'] = best_score - - if self.best_ckpt_path and self.file_client.isfile( - self.best_ckpt_path): - self.file_client.remove(self.best_ckpt_path) - runner.logger.info( - (f'The previous best checkpoint {self.best_ckpt_path} was ' - 'removed')) - - best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' - self.best_ckpt_path = self.file_client.join_path( - self.out_dir, best_ckpt_name) - runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path - - runner.save_checkpoint( - self.out_dir, - filename_tmpl=best_ckpt_name, - create_symlink=False) - runner.logger.info( - f'Now best checkpoint is saved as {best_ckpt_name}.') - runner.logger.info( - f'Best {self.key_indicator} is {best_score:0.4f} ' - f'at {cur_time} {cur_type}.') - - def evaluate(self, runner, results): - """Evaluate the results. - - Args: - runner (:obj:`mmcv.Runner`): The underlined training runner. - results (list): Output results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - - if self.save_best is not None: - # If the performance of model is pool, the `eval_res` may be an - # empty dict and it will raise exception when `self.save_best` is - # not None. More details at - # https://github.com/open-mmlab/mmdetection/issues/6265. - if not eval_res: - warnings.warn( - 'Since `eval_res` is an empty dict, the behavior to save ' - 'the best checkpoint will be skipped in this evaluation.') - return None - - if self.key_indicator == 'auto': - # infer from eval_results - self._init_rule(self.rule, list(eval_res.keys())[0]) - return eval_res[self.key_indicator] - - return None - - -class DistEvalHook(EvalHook): - """Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader in a multi-gpu manner, and return the test results. If - ``None``, the default test function ``mmcv.engine.multi_gpu_test`` - will be used. (default: ``None``) - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - broadcast_bn_buffer (bool): Whether to broadcast the - buffer(running_mean and running_var) of rank 0 to other rank - before evaluation. Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - """ - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - broadcast_bn_buffer=True, - tmpdir=None, - gpu_collect=False, - out_dir=None, - file_client_args=None, - **eval_kwargs): - - if test_fn is None: - from mmcv.engine import multi_gpu_test - test_fn = multi_gpu_test - - super().__init__( - dataloader, - start=start, - interval=interval, - by_epoch=by_epoch, - save_best=save_best, - rule=rule, - test_fn=test_fn, - greater_keys=greater_keys, - less_keys=less_keys, - out_dir=out_dir, - file_client_args=file_client_args, - **eval_kwargs) - - self.broadcast_bn_buffer = broadcast_bn_buffer - self.tmpdir = tmpdir - self.gpu_collect = gpu_collect - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - results = self.test_fn( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to - # save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/hook.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/hook.py deleted file mode 100644 index f2d1c9865..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/hook.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, is_method_overridden - -HOOKS = Registry('hook') - - -class Hook: - stages = ('before_run', 'before_train_epoch', 'before_train_iter', - 'after_train_iter', 'after_train_epoch', 'before_val_epoch', - 'before_val_iter', 'after_val_iter', 'after_val_epoch', - 'after_run') - - def before_run(self, runner): - pass - - def after_run(self, runner): - pass - - def before_epoch(self, runner): - pass - - def after_epoch(self, runner): - pass - - def before_iter(self, runner): - pass - - def after_iter(self, runner): - pass - - def before_train_epoch(self, runner): - self.before_epoch(runner) - - def before_val_epoch(self, runner): - self.before_epoch(runner) - - def after_train_epoch(self, runner): - self.after_epoch(runner) - - def after_val_epoch(self, runner): - self.after_epoch(runner) - - def before_train_iter(self, runner): - self.before_iter(runner) - - def before_val_iter(self, runner): - self.before_iter(runner) - - def after_train_iter(self, runner): - self.after_iter(runner) - - def after_val_iter(self, runner): - self.after_iter(runner) - - def every_n_epochs(self, runner, n): - return (runner.epoch + 1) % n == 0 if n > 0 else False - - def every_n_inner_iters(self, runner, n): - return (runner.inner_iter + 1) % n == 0 if n > 0 else False - - def every_n_iters(self, runner, n): - return (runner.iter + 1) % n == 0 if n > 0 else False - - def end_of_epoch(self, runner): - return runner.inner_iter + 1 == len(runner.data_loader) - - def is_last_epoch(self, runner): - return runner.epoch + 1 == runner._max_epochs - - def is_last_iter(self, runner): - return runner.iter + 1 == runner._max_iters - - def get_triggered_stages(self): - trigger_stages = set() - for stage in Hook.stages: - if is_method_overridden(stage, Hook, self): - trigger_stages.add(stage) - - # some methods will be triggered in multi stages - # use this dict to map method to stages. - method_stages_map = { - 'before_epoch': ['before_train_epoch', 'before_val_epoch'], - 'after_epoch': ['after_train_epoch', 'after_val_epoch'], - 'before_iter': ['before_train_iter', 'before_val_iter'], - 'after_iter': ['after_train_iter', 'after_val_iter'], - } - - for method, map_stages in method_stages_map.items(): - if is_method_overridden(method, Hook, self): - trigger_stages.update(map_stages) - - return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/iter_timer.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/iter_timer.py deleted file mode 100644 index 16531be96..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/iter_timer.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import time -import torch.distributed as dist - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class IterTimerHook(Hook): - - def before_epoch(self, runner): - self.t = time.time() - - def before_iter(self, runner): - self.batch_size = runner.cfg.data.samples_per_gpu - runner.log_buffer.update({'data_time': time.time() - self.t}) - - def after_iter(self, runner): - iter_info = {'time': time.time() - self.t} - fps = self.batch_size / iter_info["time"] - - if dist.is_initialized(): - fps = fps * dist.get_world_size() - - iter_info["fps"] = fps - - runner.log_buffer.update(iter_info) - self.t = time.time() diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/__init__.py deleted file mode 100644 index 062709e70..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import LoggerHook -from .clearml import ClearMLLoggerHook -from .dvclive import DvcliveLoggerHook -from .mlflow import MlflowLoggerHook -from .neptune import NeptuneLoggerHook -from .pavi import PaviLoggerHook -from .segmind import SegmindLoggerHook -from .tensorboard import TensorboardLoggerHook -from .text import TextLoggerHook -from .wandb import WandbLoggerHook - -__all__ = [ - 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', - 'NeptuneLoggerHook', 'DvcliveLoggerHook', 'SegmindLoggerHook', - 'ClearMLLoggerHook' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/base.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/base.py deleted file mode 100644 index 9f1a51b31..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/base.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from abc import ABCMeta, abstractmethod - -import numpy as np -import torch - -from ..hook import Hook - - -class LoggerHook(Hook): - """Base class for logger hooks. - - Args: - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - """ - - __metaclass__ = ABCMeta - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - self.interval = interval - self.ignore_last = ignore_last - self.reset_flag = reset_flag - self.by_epoch = by_epoch - - @abstractmethod - def log(self, runner): - pass - - @staticmethod - def is_scalar(val, include_np=True, include_torch=True): - """Tell the input variable is a scalar or not. - - Args: - val: Input variable. - include_np (bool): Whether include 0-d np.ndarray as a scalar. - include_torch (bool): Whether include 0-d torch.Tensor as a scalar. - - Returns: - bool: True or False. - """ - if isinstance(val, numbers.Number): - return True - elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: - return True - elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: - return True - else: - return False - - def get_mode(self, runner): - if runner.mode == 'train': - if 'time' in runner.log_buffer.output: - mode = 'train' - else: - mode = 'val' - elif runner.mode == 'val': - mode = 'val' - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return mode - - def get_epoch(self, runner): - if runner.mode == 'train': - epoch = runner.epoch + 1 - elif runner.mode == 'val': - # normal val mode - # runner.epoch += 1 has been done before val workflow - epoch = runner.epoch - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return epoch - - def get_iter(self, runner, inner_iter=False): - """Get the current training iteration step.""" - if self.by_epoch and inner_iter: - current_iter = runner.inner_iter + 1 - else: - current_iter = runner.iter + 1 - return current_iter - - def get_lr_tags(self, runner): - tags = {} - lrs = runner.current_lr() - if isinstance(lrs, dict): - for name, value in lrs.items(): - tags[f'learning_rate/{name}'] = value[0] - else: - tags['learning_rate'] = lrs[0] - return tags - - def get_momentum_tags(self, runner): - tags = {} - momentums = runner.current_momentum() - if isinstance(momentums, dict): - for name, value in momentums.items(): - tags[f'momentum/{name}'] = value[0] - else: - tags['momentum'] = momentums[0] - return tags - - def get_loggable_tags(self, - runner, - allow_scalar=True, - allow_text=False, - add_mode=True, - tags_to_skip=('time', 'data_time')): - tags = {} - for var, val in runner.log_buffer.output.items(): - if var in tags_to_skip: - continue - if self.is_scalar(val) and not allow_scalar: - continue - if isinstance(val, str) and not allow_text: - continue - if add_mode: - var = f'{self.get_mode(runner)}/{var}' - tags[var] = val - tags.update(self.get_lr_tags(runner)) - tags.update(self.get_momentum_tags(runner)) - return tags - - def before_run(self, runner): - for hook in runner.hooks[::-1]: - if isinstance(hook, LoggerHook): - hook.reset_flag = True - break - - def before_epoch(self, runner): - runner.log_buffer.clear() # clear logs of last epoch - - def after_train_iter(self, runner): - if self.by_epoch and self.every_n_inner_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif not self.by_epoch and self.every_n_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif self.end_of_epoch(runner) and not self.ignore_last: - # not precise but more stable - runner.log_buffer.average(self.interval) - - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_train_epoch(self, runner): - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_val_epoch(self, runner): - runner.log_buffer.average() - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/clearml.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/clearml.py deleted file mode 100644 index 1de71e152..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/clearml.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class ClearMLLoggerHook(LoggerHook): - """Class to log metrics with clearml. - - It requires `clearml`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the `clearml.Task.init` - initialization keys. See `taskinit`_ for more details. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _clearml: - https://clear.ml/docs/latest/docs/ - .. _taskinit: - https://clear.ml/docs/latest/docs/references/sdk/task/#taskinit - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(ClearMLLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_clearml() - self.init_kwargs = init_kwargs - - def import_clearml(self): - try: - import clearml - except ImportError: - raise ImportError( - 'Please run "pip install clearml" to install clearml') - self.clearml = clearml - - @master_only - def before_run(self, runner): - super(ClearMLLoggerHook, self).before_run(runner) - task_kwargs = self.init_kwargs if self.init_kwargs else {} - self.task = self.clearml.Task.init(**task_kwargs) - self.task_logger = self.task.get_logger() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - for tag, val in tags.items(): - self.task_logger.report_scalar(tag, tag, val, - self.get_iter(runner)) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/dvclive.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/dvclive.py deleted file mode 100644 index c79eefa75..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/dvclive.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from pathlib import Path - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class DvcliveLoggerHook(LoggerHook): - """Class to log metrics with dvclive. - - It requires `dvclive`_ to be installed. - - Args: - model_file (str): Default None. If not None, after each epoch the - model will be saved to {model_file}. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - kwargs: Arguments for instantiating `Live`_. - - .. _dvclive: - https://dvc.org/doc/dvclive - - .. _Live: - https://dvc.org/doc/dvclive/api-reference/live#parameters - """ - - def __init__(self, - model_file=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - **kwargs): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.model_file = model_file - self.import_dvclive(**kwargs) - - def import_dvclive(self, **kwargs): - try: - from dvclive import Live - except ImportError: - raise ImportError( - 'Please run "pip install dvclive" to install dvclive') - self.dvclive = Live(**kwargs) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.dvclive.set_step(self.get_iter(runner)) - for k, v in tags.items(): - self.dvclive.log(k, v) - - @master_only - def after_train_epoch(self, runner): - super().after_train_epoch(runner) - if self.model_file is not None: - runner.save_checkpoint( - Path(self.model_file).parent, - filename_tmpl=Path(self.model_file).name, - create_symlink=False, - ) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/mlflow.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/mlflow.py deleted file mode 100644 index dcd87bcb5..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/mlflow.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import TORCH_VERSION -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class MlflowLoggerHook(LoggerHook): - """Class to log metrics and (optionally) a trained model to MLflow. - - It requires `MLflow`_ to be installed. - - Args: - exp_name (str, optional): Name of the experiment to be used. - Default None. If not None, set the active experiment. - If experiment does not exist, an experiment with provided name - will be created. - tags (Dict[str], optional): Tags for the current run. - Default None. If not None, set tags for the current run. - log_model (bool, optional): Whether to log an MLflow artifact. - Default True. If True, log runner.model as an MLflow artifact - for the current run. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _MLflow: - https://www.mlflow.org/docs/latest/index.html - """ - - def __init__(self, - exp_name=None, - tags=None, - log_model=True, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(MlflowLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_mlflow() - self.exp_name = exp_name - self.tags = tags - self.log_model = log_model - - def import_mlflow(self): - try: - import mlflow - import mlflow.pytorch as mlflow_pytorch - except ImportError: - raise ImportError( - 'Please run "pip install mlflow" to install mlflow') - self.mlflow = mlflow - self.mlflow_pytorch = mlflow_pytorch - - @master_only - def before_run(self, runner): - super(MlflowLoggerHook, self).before_run(runner) - if self.exp_name is not None: - self.mlflow.set_experiment(self.exp_name) - if self.tags is not None: - self.mlflow.set_tags(self.tags) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.mlflow.log_metrics(tags, step=self.get_iter(runner)) - - @master_only - def after_run(self, runner): - if self.log_model: - self.mlflow_pytorch.log_model( - runner.model, - 'models', - pip_requirements=[f'torch=={TORCH_VERSION}']) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/neptune.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/neptune.py deleted file mode 100644 index e0aafe91d..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/neptune.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class NeptuneLoggerHook(LoggerHook): - """Class to log metrics to NeptuneAI. - - It requires `Neptune`_ to be installed. - - Args: - init_kwargs (dict): a dict contains the initialization keys as below: - - - project (str): Name of a project in a form of - namespace/project_name. If None, the value of NEPTUNE_PROJECT - environment variable will be taken. - - api_token (str): User’s API token. If None, the value of - NEPTUNE_API_TOKEN environment variable will be taken. Note: It is - strongly recommended to use NEPTUNE_API_TOKEN environment - variable rather than placing your API token in plain text in your - source code. - - name (str, optional, default is 'Untitled'): Editable name of the - run. Name is displayed in the run's Details and in Runs table as - a column. - - Check https://docs.neptune.ai/api-reference/neptune#init for more - init arguments. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than ``interval``. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _Neptune: - https://docs.neptune.ai - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=True, - with_step=True, - by_epoch=True): - - super(NeptuneLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_neptune() - self.init_kwargs = init_kwargs - self.with_step = with_step - - def import_neptune(self): - try: - import neptune.new as neptune - except ImportError: - raise ImportError( - 'Please run "pip install neptune-client" to install neptune') - self.neptune = neptune - self.run = None - - @master_only - def before_run(self, runner): - if self.init_kwargs: - self.run = self.neptune.init(**self.init_kwargs) - else: - self.run = self.neptune.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for tag_name, tag_value in tags.items(): - if self.with_step: - self.run[tag_name].log( - tag_value, step=self.get_iter(runner)) - else: - tags['global_step'] = self.get_iter(runner) - self.run[tag_name].log(tags) - - @master_only - def after_run(self, runner): - self.run.stop() diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/pavi.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/pavi.py deleted file mode 100644 index d5d61f9e5..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/pavi.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp - -import torch -import yaml - -import mmcv -from ....parallel.utils import is_module_wrapper -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class PaviLoggerHook(LoggerHook): - """Class to visual model, log metrics (for internal use). - - Args: - init_kwargs (dict): A dict contains the initialization keys. - add_graph (bool): Whether to visual model. Default: False. - add_last_ckpt (bool): Whether to save checkpoint after run. - Default: False. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - img_key (string): Get image data from Dataset. Default: 'img_info'. - """ - - def __init__(self, - init_kwargs=None, - add_graph=False, - add_last_ckpt=False, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - img_key='img_info'): - super(PaviLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.init_kwargs = init_kwargs - self.add_graph = add_graph - self.add_last_ckpt = add_last_ckpt - self.img_key = img_key - - @master_only - def before_run(self, runner): - super(PaviLoggerHook, self).before_run(runner) - try: - from pavi import SummaryWriter - except ImportError: - raise ImportError('Please run "pip install pavi" to install pavi.') - - self.run_name = runner.work_dir.split('/')[-1] - - if not self.init_kwargs: - self.init_kwargs = dict() - self.init_kwargs['name'] = self.run_name - self.init_kwargs['model'] = runner._model_name - if runner.meta is not None: - if 'config_dict' in runner.meta: - config_dict = runner.meta['config_dict'] - assert isinstance( - config_dict, - dict), ('meta["config_dict"] has to be of a dict, ' - f'but got {type(config_dict)}') - elif 'config_file' in runner.meta: - config_file = runner.meta['config_file'] - config_dict = dict(mmcv.Config.fromfile(config_file)) - else: - config_dict = None - if config_dict is not None: - # 'max_.*iter' is parsed in pavi sdk as the maximum iterations - # to properly set up the progress bar. - config_dict = config_dict.copy() - config_dict.setdefault('max_iter', runner.max_iters) - # non-serializable values are first converted in - # mmcv.dump to json - config_dict = json.loads( - mmcv.dump(config_dict, file_format='json')) - session_text = yaml.dump(config_dict) - self.init_kwargs['session_text'] = session_text - self.writer = SummaryWriter(**self.init_kwargs) - - def get_step(self, runner): - """Get the total training step/epoch.""" - if self.get_mode(runner) == 'val' and self.by_epoch: - return self.get_epoch(runner) - else: - return self.get_iter(runner) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, add_mode=False) - if tags: - self.writer.add_scalars( - self.get_mode(runner), tags, self.get_step(runner)) - - @master_only - def after_run(self, runner): - if self.add_last_ckpt: - ckpt_path = osp.join(runner.work_dir, 'latest.pth') - if osp.islink(ckpt_path): - ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) - - if osp.isfile(ckpt_path): - # runner.epoch += 1 has been done before `after_run`. - iteration = runner.epoch if self.by_epoch else runner.iter - return self.writer.add_snapshot_file( - tag=self.run_name, - snapshot_file_path=ckpt_path, - iteration=iteration) - - # flush the buffer and send a task ending signal to Pavi - self.writer.close() - - @master_only - def before_epoch(self, runner): - if runner.epoch == 0 and self.add_graph: - if is_module_wrapper(runner.model): - _model = runner.model.module - else: - _model = runner.model - device = next(_model.parameters()).device - data = next(iter(runner.data_loader)) - image = data[self.img_key][0:1].to(device) - with torch.no_grad(): - self.writer.add_graph(_model, image) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/segmind.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/segmind.py deleted file mode 100644 index e262c7c1a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/segmind.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class SegmindLoggerHook(LoggerHook): - """Class to log metrics to Segmind. - - It requires `Segmind`_ to be installed. - - Args: - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - - .. _Segmind: - https://docs.segmind.com/python-library - """ - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(SegmindLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_segmind() - - def import_segmind(self): - try: - import segmind - except ImportError: - raise ImportError( - "Please run 'pip install segmind' to install segmind") - self.log_metrics = segmind.tracking.fluent.log_metrics - self.mlflow_log = segmind.utils.logging_utils.try_mlflow_log - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - # logging metrics to segmind - self.mlflow_log( - self.log_metrics, tags, step=runner.epoch, epoch=runner.epoch) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/tensorboard.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/tensorboard.py deleted file mode 100644 index bf00d5742..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/tensorboard.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import TORCH_VERSION, digit_version -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TensorboardLoggerHook(LoggerHook): - """Class to log metrics to Tensorboard. - - Args: - log_dir (string): Save directory location. Default: None. If default - values are used, directory location is ``runner.work_dir``/tf_logs. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - """ - - def __init__(self, - log_dir=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(TensorboardLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.log_dir = log_dir - - @master_only - def before_run(self, runner): - super(TensorboardLoggerHook, self).before_run(runner) - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.1')): - try: - from tensorboardX import SummaryWriter - except ImportError: - raise ImportError('Please install tensorboardX to use ' - 'TensorboardLoggerHook.') - else: - try: - from torch.utils.tensorboard import SummaryWriter - except ImportError: - raise ImportError( - 'Please run "pip install future tensorboard" to install ' - 'the dependencies to use torch.utils.tensorboard ' - '(applicable to PyTorch 1.1 or higher)') - - if self.log_dir is None: - self.log_dir = osp.join(runner.work_dir, 'tf_logs') - self.writer = SummaryWriter(self.log_dir) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, allow_text=True) - for tag, val in tags.items(): - if isinstance(val, str): - self.writer.add_text(tag, val, self.get_iter(runner)) - else: - self.writer.add_scalar(tag, val, self.get_iter(runner)) - - @master_only - def after_run(self, runner): - self.writer.close() diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/text.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/text.py deleted file mode 100644 index 644ced2c9..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/text.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import datetime -import os -import os.path as osp -from collections import OrderedDict - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.fileio.file_client import FileClient -from mmcv.utils import is_tuple_of, scandir -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TextLoggerHook(LoggerHook): - """Logger hook in text. - - In this logger hook, the information will be printed on terminal and - saved in json file. - - Args: - by_epoch (bool, optional): Whether EpochBasedRunner is used. - Default: True. - interval (int, optional): Logging interval (every k iterations). - Default: 10. - ignore_last (bool, optional): Ignore the log of last iterations in each - epoch if less than :attr:`interval`. Default: True. - reset_flag (bool, optional): Whether to clear the output buffer after - logging. Default: False. - interval_exp_name (int, optional): Logging interval for experiment - name. This feature is to help users conveniently get the experiment - information from screen or log file. Default: 1000. - out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. - If ``out_dir`` is specified, logs will be copied to a new directory - which is the concatenation of ``out_dir`` and the last level - directory of ``runner.work_dir``. Default: None. - `New in version 1.3.16.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be copied to ``out_dir``. - Default: ('.log.json', '.log', '.py'). - `New in version 1.3.16.` - keep_local (bool, optional): Whether to keep local log when - :attr:`out_dir` is specified. If False, the local log will be - removed. Default: True. - `New in version 1.3.16.` - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - - def __init__(self, - by_epoch=True, - interval=10, - ignore_last=True, - reset_flag=False, - interval_exp_name=1000, - out_dir=None, - out_suffix=('.log.json', '.log', '.py'), - keep_local=True, - file_client_args=None): - super(TextLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.by_epoch = by_epoch - self.time_sec_tot = 0 - self.interval_exp_name = interval_exp_name - - if out_dir is None and file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" when `out_dir` is not' - 'specified.') - self.out_dir = out_dir - - if not (out_dir is None or isinstance(out_dir, str) - or is_tuple_of(out_dir, str)): - raise TypeError('out_dir should be "None" or string or tuple of ' - 'string, but got {out_dir}') - self.out_suffix = out_suffix - - self.keep_local = keep_local - self.file_client_args = file_client_args - if self.out_dir is not None: - self.file_client = FileClient.infer_client(file_client_args, - self.out_dir) - - def before_run(self, runner): - super(TextLoggerHook, self).before_run(runner) - - if self.out_dir is not None: - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - # The final `self.out_dir` is the concatenation of `self.out_dir` - # and the last level directory of `runner.work_dir` - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'Text logs will be saved to {self.out_dir} by ' - f'{self.file_client.name} after the training process.')) - - self.start_iter = runner.iter - self.json_log_path = osp.join(runner.work_dir, - f'{runner.timestamp}.log.json') - if runner.meta is not None: - self._dump_log(runner.meta, runner) - - def _get_max_memory(self, runner): - device = getattr(runner.model, 'output_device', None) - mem = torch.cuda.max_memory_allocated(device=device) - mem_mb = torch.tensor([int(mem) // (1024 * 1024)], - dtype=torch.int, - device=device) - if runner.world_size > 1: - dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) - return mem_mb.item() - - def _log_info(self, log_dict, runner): - # print exp name for users to distinguish experiments - # at every ``interval_exp_name`` iterations and the end of each epoch - if runner.meta is not None and 'exp_name' in runner.meta: - if (self.every_n_iters(runner, self.interval_exp_name)) or ( - self.by_epoch and self.end_of_epoch(runner)): - exp_info = f'Exp name: {runner.meta["exp_name"]}' - runner.logger.info(exp_info) - - if log_dict['mode'] == 'train': - if isinstance(log_dict['lr'], dict): - lr_str = [] - for k, val in log_dict['lr'].items(): - lr_str.append(f'lr_{k}: {val:.3e}') - lr_str = ' '.join(lr_str) - else: - lr_str = f'lr: {log_dict["lr"]:.3e}' - - # by epoch: Epoch [4][100/1000] - # by iter: Iter [100/100000] - if self.by_epoch: - log_str = f'Epoch [{log_dict["epoch"]}]' \ - f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' - else: - log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' - log_str += f'{lr_str}, ' - - if 'time' in log_dict.keys(): - self.time_sec_tot += (log_dict['time'] * self.interval) - time_sec_avg = self.time_sec_tot / ( - runner.iter - self.start_iter + 1) - eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) - eta_str = str(datetime.timedelta(seconds=int(eta_sec))) - log_str += f'eta: {eta_str}, ' - log_str += f'time: {log_dict["time"]:.3f}, ' \ - f'data_time: {log_dict["data_time"]:.3f}, ' - # statistic memory - if torch.cuda.is_available(): - log_str += f'memory: {log_dict["memory"]}, ' - else: - # val/test time - # here 1000 is the length of the val dataloader - # by epoch: Epoch[val] [4][1000] - # by iter: Iter[val] [1000] - if self.by_epoch: - log_str = f'Epoch({log_dict["mode"]}) ' \ - f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' - else: - log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' - - log_items = [] - for name, val in log_dict.items(): - # TODO: resolve this hack - # these items have been in log_str - if name in [ - 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', - 'memory', 'epoch' - ]: - continue - if isinstance(val, float): - val = f'{val:.4f}' - log_items.append(f'{name}: {val}') - log_str += ', '.join(log_items) - - runner.logger.info(log_str) - - def _dump_log(self, log_dict, runner): - # dump log in json format - json_log = OrderedDict() - for k, v in log_dict.items(): - json_log[k] = self._round_float(v) - # only append log at last line - if runner.rank == 0: - with open(self.json_log_path, 'a+') as f: - mmcv.dump(json_log, f, file_format='json') - f.write('\n') - - def _round_float(self, items): - if isinstance(items, list): - return [self._round_float(item) for item in items] - elif isinstance(items, float): - return round(items, 5) - else: - return items - - def log(self, runner): - if 'eval_iter_num' in runner.log_buffer.output: - # this doesn't modify runner.iter and is regardless of by_epoch - cur_iter = runner.log_buffer.output.pop('eval_iter_num') - else: - cur_iter = self.get_iter(runner, inner_iter=True) - - log_dict = OrderedDict( - mode=self.get_mode(runner), - epoch=self.get_epoch(runner), - iter=cur_iter) - - # only record lr of the first param group - cur_lr = runner.current_lr() - if isinstance(cur_lr, list): - log_dict['lr'] = cur_lr[0] - else: - assert isinstance(cur_lr, dict) - log_dict['lr'] = {} - for k, lr_ in cur_lr.items(): - assert isinstance(lr_, list) - log_dict['lr'].update({k: lr_[0]}) - - if 'time' in runner.log_buffer.output: - # statistic memory - if torch.cuda.is_available(): - log_dict['memory'] = self._get_max_memory(runner) - - log_dict = dict(log_dict, **runner.log_buffer.output) - - self._log_info(log_dict, runner) - self._dump_log(log_dict, runner) - return log_dict - - def after_run(self, runner): - # copy or upload logs to self.out_dir - if self.out_dir is not None: - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - out_filepath = self.file_client.join_path( - self.out_dir, filename) - with open(local_filepath, 'r') as f: - self.file_client.put_text(f.read(), out_filepath) - - runner.logger.info( - (f'The file {local_filepath} has been uploaded to ' - f'{out_filepath}.')) - - if not self.keep_local: - os.remove(local_filepath) - runner.logger.info( - (f'{local_filepath} was removed due to the ' - '`self.keep_local=False`')) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/wandb.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/wandb.py deleted file mode 100644 index 78b890ee1..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/logger/wandb.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import scandir -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class WandbLoggerHook(LoggerHook): - """Class to log metrics with wandb. - - It requires `wandb`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the initialization keys. Check - https://docs.wandb.ai/ref/python/init for more init arguments. - interval (int): Logging interval (every k iterations). - Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - commit (bool): Save the metrics dict to the wandb server and increment - the step. If false ``wandb.log`` just updates the current metrics - dict with the row argument and metrics won't be saved until - ``wandb.log`` is called with ``commit=True``. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - log_artifact (bool): If True, artifacts in {work_dir} will be uploaded - to wandb after training ends. - Default: True - `New in version 1.4.3.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be uploaded to wandb. - Default: ('.log.json', '.log', '.py'). - `New in version 1.4.3.` - - .. _wandb: - https://docs.wandb.ai - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - commit=True, - by_epoch=True, - with_step=True, - log_artifact=True, - out_suffix=('.log.json', '.log', '.py')): - super(WandbLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_wandb() - self.init_kwargs = init_kwargs - self.commit = commit - self.with_step = with_step - self.log_artifact = log_artifact - self.out_suffix = out_suffix - - def import_wandb(self): - try: - import wandb - except ImportError: - raise ImportError( - 'Please run "pip install wandb" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(WandbLoggerHook, self).before_run(runner) - if self.wandb is None: - self.import_wandb() - if self.init_kwargs: - self.wandb.init(**self.init_kwargs) - else: - self.wandb.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - if self.with_step: - self.wandb.log( - tags, step=self.get_iter(runner), commit=self.commit) - else: - tags['global_step'] = self.get_iter(runner) - self.wandb.log(tags, commit=self.commit) - - @master_only - def after_run(self, runner): - if self.log_artifact: - wandb_artifact = self.wandb.Artifact( - name='artifacts', type='model') - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - wandb_artifact.add_file(local_filepath) - self.wandb.log_artifact(wandb_artifact) - self.wandb.join() diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/lr_updater.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/lr_updater.py deleted file mode 100644 index ee2a53a65..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/lr_updater.py +++ /dev/null @@ -1,730 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from math import cos, pi - -import mmcv -from .hook import HOOKS, Hook - - -class LrUpdaterHook(Hook): - """LR Scheduler in MMCV. - - Args: - by_epoch (bool): LR changes epoch by epoch - warmup (string): Type of warmup used. It can be None(use no warmup), - 'constant', 'linear' or 'exp' - warmup_iters (int): The number of iterations or epochs that warmup - lasts - warmup_ratio (float): LR used at the beginning of warmup equals to - warmup_ratio * initial_lr - warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters - means the number of epochs that warmup lasts, otherwise means the - number of iteration that warmup lasts - """ - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.1, - warmup_by_epoch=False): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_ratio" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - self.warmup_by_epoch = warmup_by_epoch - - if self.warmup_by_epoch: - self.warmup_epochs = self.warmup_iters - self.warmup_iters = None - else: - self.warmup_epochs = None - - self.base_lr = [] # initial lr for all param groups - self.regular_lr = [] # expected lr if no warming up is performed - - def _set_lr(self, runner, lr_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, lr in zip(optim.param_groups, lr_groups[k]): - param_group['lr'] = lr - else: - for param_group, lr in zip(runner.optimizer.param_groups, - lr_groups): - param_group['lr'] = lr - - def get_lr(self, runner, base_lr): - raise NotImplementedError - - def get_regular_lr(self, runner): - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(runner, _base_lr) - for _base_lr in self.base_lr[k] - ] - lr_groups.update({k: _lr_group}) - - return lr_groups - else: - return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - if self.warmup == 'constant': - warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_lr = [_lr * (1 - k) for _lr in regular_lr] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.regular_lr, dict): - lr_groups = {} - for key, regular_lr in self.regular_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.regular_lr) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - group.setdefault('initial_lr', group['lr']) - _base_lr = [ - group['initial_lr'] for group in optim.param_groups - ] - self.base_lr.update({k: _base_lr}) - else: - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['initial_lr'] for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if self.warmup_iters is None: - epoch_len = len(runner.data_loader) - self.warmup_iters = self.warmup_epochs * epoch_len - - if not self.by_epoch: - return - - self.regular_lr = self.get_regular_lr(runner) - self._set_lr(runner, self.regular_lr) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_lr = self.get_regular_lr(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - - -@HOOKS.register_module() -class FixedLrUpdaterHook(LrUpdaterHook): - - def __init__(self, **kwargs): - super(FixedLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - return base_lr - - -@HOOKS.register_module() -class StepLrUpdaterHook(LrUpdaterHook): - """Step LR scheduler with min_lr clipping. - - Args: - step (int | list[int]): Step to decay the LR. If an int value is given, - regard it as the decay interval. If a list is given, decay LR at - these steps. - gamma (float, optional): Decay LR ratio. Default: 0.1. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. If None - is given, we don't perform lr clipping. Default: None. - """ - - def __init__(self, step, gamma=0.1, min_lr=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_lr = min_lr - super(StepLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - lr = base_lr * (self.gamma**exp) - if self.min_lr is not None: - # clip to a minimum value - lr = max(lr, self.min_lr) - return lr - - -@HOOKS.register_module() -class ExpLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, **kwargs): - self.gamma = gamma - super(ExpLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * self.gamma**progress - - -@HOOKS.register_module() -class PolyLrUpdaterHook(LrUpdaterHook): - - def __init__(self, power=1., min_lr=0., **kwargs): - self.power = power - self.min_lr = min_lr - super(PolyLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - coeff = (1 - progress / max_progress)**self.power - return (base_lr - self.min_lr) * coeff + self.min_lr - - -@HOOKS.register_module() -class InvLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, power=1., **kwargs): - self.gamma = gamma - self.power = power - super(InvLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * (1 + self.gamma * progress)**(-self.power) - - -@HOOKS.register_module() -class CosineAnnealingLrUpdaterHook(LrUpdaterHook): - """CosineAnnealing LR scheduler. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(CosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): - """Flat + Cosine lr schedule. - - Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 - - Args: - start_percent (float): When to start annealing the learning rate - after the percentage of the total training steps. - The value should be in range [0, 1). - Default: 0.75 - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - start_percent=0.75, - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - if start_percent < 0 or start_percent > 1 or not isinstance( - start_percent, float): - raise ValueError( - 'expected float between 0 and 1 start_percent, but ' - f'got {start_percent}') - self.start_percent = start_percent - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(FlatCosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - start = round(runner.max_epochs * self.start_percent) - progress = runner.epoch - start - max_progress = runner.max_epochs - start - else: - start = round(runner.max_iters * self.start_percent) - progress = runner.iter - start - max_progress = runner.max_iters - start - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress < 0: - return base_lr - else: - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class CosineRestartLrUpdaterHook(LrUpdaterHook): - """Cosine annealing with restarts learning rate scheme. - - Args: - periods (list[int]): Periods for each cosine anneling cycle. - restart_weights (list[float], optional): Restart weights at each - restart iteration. Default: [1]. - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - periods, - restart_weights=[1], - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.periods = periods - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - self.restart_weights = restart_weights - assert (len(self.periods) == len(self.restart_weights) - ), 'periods and restart_weights should have the same length.' - super(CosineRestartLrUpdaterHook, self).__init__(**kwargs) - - self.cumulative_periods = [ - sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) - ] - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - else: - progress = runner.iter - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - idx = get_position_from_periods(progress, self.cumulative_periods) - current_weight = self.restart_weights[idx] - nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] - current_periods = self.periods[idx] - - alpha = min((progress - nearest_restart) / current_periods, 1) - return annealing_cos(base_lr, target_lr, alpha, current_weight) - - -def get_position_from_periods(iteration, cumulative_periods): - """Get the position from a period list. - - It will return the index of the right-closest number in the period list. - For example, the cumulative_periods = [100, 200, 300, 400], - if iteration == 50, return 0; - if iteration == 210, return 2; - if iteration == 300, return 3. - - Args: - iteration (int): Current iteration. - cumulative_periods (list[int]): Cumulative period list. - - Returns: - int: The position of the right-closest number in the period list. - """ - for i, period in enumerate(cumulative_periods): - if iteration < period: - return i - raise ValueError(f'Current iteration {iteration} exceeds ' - f'cumulative_periods {cumulative_periods}') - - -@HOOKS.register_module() -class CyclicLrUpdaterHook(LrUpdaterHook): - """Cyclic LR Scheduler. - - Implement the cyclical learning rate policy (CLR) described in - https://arxiv.org/pdf/1506.01186.pdf - - Different from the original paper, we use cosine annealing rather than - triangular policy inside a cycle. This improves the performance in the - 3D detection area. - - Args: - by_epoch (bool, optional): Whether to update LR by epoch. - target_ratio (tuple[float], optional): Relative ratio of the highest LR - and the lowest LR to the initial LR. - cyclic_times (int, optional): Number of cycles during training - step_ratio_up (float, optional): The ratio of the increasing process of - LR in the total cycle. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch=False, - target_ratio=(10, 1e-4), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - gamma=1, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - assert 0 < gamma <= 1, \ - '"gamma" must be in range (0, 1]' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.max_iter_per_phase = None - self.lr_phases = [] # init lr_phases - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicLrUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicLrUpdaterHook, self).before_run(runner) - # initiate lr_phases - # total lr_phases are separated as up and down - self.max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * self.max_iter_per_phase) - self.lr_phases.append([0, iter_up_phase, 1, self.target_ratio[0]]) - self.lr_phases.append([ - iter_up_phase, self.max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - # Update weight decay - scale = self.gamma**curr_cycle - - for (start_iter, end_iter, start_ratio, end_ratio) in self.lr_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_lr and base lr. The target end_ratio can be - # expressed as: - # end_ratio = (base_lr + scale * (max_lr - base_lr)) / base_lr - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_lr * start_ratio, - base_lr * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleLrUpdaterHook(LrUpdaterHook): - """One Cycle LR Scheduler. - - The 1cycle learning rate policy changes the learning rate after every - batch. The one cycle learning rate policy is described in - https://arxiv.org/pdf/1708.07120.pdf - - Args: - max_lr (float or list): Upper learning rate boundaries in the cycle - for each parameter group. - total_steps (int, optional): The total number of steps in the cycle. - Note that if a value is not provided here, it will be the max_iter - of runner. Default: None. - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - div_factor (float): Determines the initial learning rate via - initial_lr = max_lr/div_factor - Default: 25 - final_div_factor (float): Determines the minimum learning rate via - min_lr = initial_lr/final_div_factor - Default: 1e4 - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - max_lr, - total_steps=None, - pct_start=0.3, - anneal_strategy='cos', - div_factor=25, - final_div_factor=1e4, - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch = False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(max_lr, (numbers.Number, list, dict)): - raise ValueError('the type of max_lr must be the one of list or ' - f'dict, but got {type(max_lr)}') - self._max_lr = max_lr - if total_steps is not None: - if not isinstance(total_steps, int): - raise ValueError('the type of total_steps must be int, but' - f'got {type(total_steps)}') - self.total_steps = total_steps - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.div_factor = div_factor - self.final_div_factor = final_div_factor - self.three_phase = three_phase - self.lr_phases = [] # init lr_phases - super(OneCycleLrUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if hasattr(self, 'total_steps'): - total_steps = self.total_steps - else: - total_steps = runner.max_iters - if total_steps < runner.max_iters: - raise ValueError( - 'The total steps must be greater than or equal to max ' - f'iterations {runner.max_iters} of runner, but total steps ' - f'is {total_steps}.') - - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - _max_lr = format_param(k, optim, self._max_lr) - self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(optim.param_groups, self.base_lr[k]): - group.setdefault('initial_lr', lr) - else: - k = type(runner.optimizer).__name__ - _max_lr = format_param(k, runner.optimizer, self._max_lr) - self.base_lr = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(runner.optimizer.param_groups, self.base_lr): - group.setdefault('initial_lr', lr) - - if self.three_phase: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append([ - float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 - ]) - self.lr_phases.append( - [total_steps - 1, 1, 1 / self.final_div_factor]) - else: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append( - [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - start_iter = 0 - for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): - if curr_iter <= end_iter: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, - pct) - break - start_iter = end_iter - return lr - - -@HOOKS.register_module() -class LinearAnnealingLrUpdaterHook(LrUpdaterHook): - """Linear annealing LR Scheduler decays the learning rate of each parameter - group linearly. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(LinearAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_linear(base_lr, target_lr, progress / max_progress) - - -def annealing_cos(start, end, factor, weight=1): - """Calculate annealing cos learning rate. - - Cosine anneal from `weight * start + (1 - weight) * end` to `end` as - percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the cosine annealing. - end (float): The ending learing rate of the cosine annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - weight (float, optional): The combination factor of `start` and `end` - when calculating the actual starting learning rate. Default to 1. - """ - cos_out = cos(pi * factor) + 1 - return end + 0.5 * weight * (start - end) * cos_out - - -def annealing_linear(start, end, factor): - """Calculate annealing linear learning rate. - - Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the linear annealing. - end (float): The ending learing rate of the linear annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - """ - return start + (end - start) * factor - - -def format_param(name, optim, param): - if isinstance(param, numbers.Number): - return [param] * len(optim.param_groups) - elif isinstance(param, (list, tuple)): # multi param groups - if len(param) != len(optim.param_groups): - raise ValueError(f'expected {len(optim.param_groups)} ' - f'values for {name}, got {len(param)}') - return param - else: # multi optimizers - if name not in param: - raise KeyError(f'{name} is not found in {param.keys()}') - return param[name] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/memory.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/memory.py deleted file mode 100644 index 70cf9a838..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class EmptyCacheHook(Hook): - - def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): - self._before_epoch = before_epoch - self._after_epoch = after_epoch - self._after_iter = after_iter - - def after_iter(self, runner): - if self._after_iter: - torch.cuda.empty_cache() - - def before_epoch(self, runner): - if self._before_epoch: - torch.cuda.empty_cache() - - def after_epoch(self, runner): - if self._after_epoch: - torch.cuda.empty_cache() diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/momentum_updater.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/momentum_updater.py deleted file mode 100644 index aa15fe23c..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/momentum_updater.py +++ /dev/null @@ -1,566 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -from .hook import HOOKS, Hook -from .lr_updater import annealing_cos, annealing_linear, format_param - - -class MomentumUpdaterHook(Hook): - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.9): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_momentum" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - - self.base_momentum = [] # initial momentum for all param groups - self.regular_momentum = [ - ] # expected momentum if no warming up is performed - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, base_momentum): - raise NotImplementedError - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k in runner.optimizer.keys(): - _momentum_group = [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum[k] - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - return [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum - ] - - def get_warmup_momentum(self, cur_iters): - - def _get_warmup_momentum(cur_iters, regular_momentum): - if self.warmup == 'constant': - warmup_momentum = [ - _momentum / self.warmup_ratio - for _momentum in regular_momentum - ] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_momentum = [ - _momentum / (1 - k) for _momentum in regular_momentum - ] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_momentum = [ - _momentum / k for _momentum in regular_momentum - ] - return warmup_momentum - - if isinstance(self.regular_momentum, dict): - momentum_groups = {} - for key, regular_momentum in self.regular_momentum.items(): - momentum_groups[key] = _get_warmup_momentum( - cur_iters, regular_momentum) - return momentum_groups - else: - return _get_warmup_momentum(cur_iters, self.regular_momentum) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, - # if 'initial_momentum' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_momentum = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - _base_momentum = [ - group['initial_momentum'] for group in optim.param_groups - ] - self.base_momentum.update({k: _base_momentum}) - else: - for group in runner.optimizer.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - self.base_momentum = [ - group['initial_momentum'] - for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if not self.by_epoch: - return - self.regular_momentum = self.get_regular_momentum(runner) - self._set_momentum(runner, self.regular_momentum) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_momentum = self.get_regular_momentum(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - - -@HOOKS.register_module() -class StepMomentumUpdaterHook(MomentumUpdaterHook): - """Step momentum scheduler with min value clipping. - - Args: - step (int | list[int]): Step to decay the momentum. If an int value is - given, regard it as the decay interval. If a list is given, decay - momentum at these steps. - gamma (float, optional): Decay momentum ratio. Default: 0.5. - min_momentum (float, optional): Minimum momentum value to keep. If - momentum after decay is lower than this value, it will be clipped - accordingly. If None is given, we don't perform lr clipping. - Default: None. - """ - - def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_momentum = min_momentum - super(StepMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - momentum = base_momentum * (self.gamma**exp) - if self.min_momentum is not None: - # clip to a minimum value - momentum = max(momentum, self.min_momentum) - return momentum - - -@HOOKS.register_module() -class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Cosine annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(CosineAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_cos(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class LinearAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Linear annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(LinearAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_linear(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class CyclicMomentumUpdaterHook(MomentumUpdaterHook): - """Cyclic momentum Scheduler. - - Implement the cyclical momentum scheduler policy described in - https://arxiv.org/pdf/1708.07120.pdf - - This momentum scheduler usually used together with the CyclicLRUpdater - to improve the performance in the 3D detection area. - - Args: - target_ratio (tuple[float]): Relative ratio of the lowest momentum and - the highest momentum to the initial momentum. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of momentum - in the total cycle. - by_epoch (bool): Whether to update momentum by epoch. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch=False, - target_ratio=(0.85 / 0.95, 1), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - gamma=1, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.momentum_phases = [] # init momentum_phases - - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - # currently only support by_epoch=False - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicMomentumUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicMomentumUpdaterHook, self).before_run(runner) - # initiate momentum_phases - # total momentum_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.max_iter_per_phase = max_iter_per_phase - self.momentum_phases.append( - [0, iter_up_phase, 1, self.target_ratio[0]]) - self.momentum_phases.append([ - iter_up_phase, max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_momentum(self, runner, base_momentum): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - scale = self.gamma**curr_cycle - for (start_iter, end_iter, start_ratio, end_ratio) \ - in self.momentum_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_momentum and base momentum. The target end_ratio - # can be expressed as: - # end_ratio = (base_momentum + scale * \ - # (max_momentum - base_momentum)) / base_momentum - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_momentum * start_ratio, - base_momentum * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): - """OneCycle momentum Scheduler. - - This momentum scheduler usually used together with the OneCycleLrUpdater - to improve the performance. - - Args: - base_momentum (float or list): Lower momentum boundaries in the cycle - for each parameter group. Note that momentum is cycled inversely - to learning rate; at the peak of a cycle, momentum is - 'base_momentum' and learning rate is 'max_lr'. - Default: 0.85 - max_momentum (float or list): Upper momentum boundaries in the cycle - for each parameter group. Functionally, - it defines the cycle amplitude (max_momentum - base_momentum). - Note that momentum is cycled inversely - to learning rate; at the start of a cycle, momentum is - 'max_momentum' and learning rate is 'base_lr' - Default: 0.95 - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - base_momentum=0.85, - max_momentum=0.95, - pct_start=0.3, - anneal_strategy='cos', - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch=False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(base_momentum, (float, list, dict)): - raise ValueError('base_momentum must be the type among of float,' - 'list or dict.') - self._base_momentum = base_momentum - if not isinstance(max_momentum, (float, list, dict)): - raise ValueError('max_momentum must be the type among of float,' - 'list or dict.') - self._max_momentum = max_momentum - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('Expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must by one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.three_phase = three_phase - self.momentum_phases = [] # init momentum_phases - super(OneCycleMomentumUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip( - optim.param_groups, _base_momentum, _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - else: - optim = runner.optimizer - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - k = type(optim).__name__ - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip(optim.param_groups, - _base_momentum, - _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - - if self.three_phase: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': - float(2 * self.pct_start * runner.max_iters) - 2, - 'start_momentum': - 'base_momentum', - 'end_momentum': - 'max_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'max_momentum', - 'end_momentum': 'max_momentum' - }) - else: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'base_momentum', - 'end_momentum': 'max_momentum' - }) - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, param_group): - curr_iter = runner.iter - start_iter = 0 - for i, phase in enumerate(self.momentum_phases): - end_iter = phase['end_iter'] - if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - momentum = self.anneal_func( - param_group[phase['start_momentum']], - param_group[phase['end_momentum']], pct) - break - start_iter = end_iter - return momentum - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k, optim in runner.optimizer.items(): - _momentum_group = [ - self.get_momentum(runner, param_group) - for param_group in optim.param_groups - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - momentum_groups = [] - for param_group in runner.optimizer.param_groups: - momentum_groups.append(self.get_momentum(runner, param_group)) - return momentum_groups diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/optimizer.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/optimizer.py deleted file mode 100644 index 12c885183..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/optimizer.py +++ /dev/null @@ -1,556 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -from collections import defaultdict -from itertools import chain - -from torch.nn.utils import clip_grad - -from mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version -from ..dist_utils import allreduce_grads -from ..fp16_utils import LossScaler, wrap_fp16_model -from .hook import HOOKS, Hook - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - from torch.cuda.amp import GradScaler -except ImportError: - pass - - -@HOOKS.register_module() -class OptimizerHook(Hook): - """A hook contains custom operations for the optimizer. - - Args: - grad_clip (dict, optional): A config dict to control the clip_grad. - Default: None. - detect_anomalous_params (bool): This option is only used for - debugging which will slow down the training speed. - Detect anomalous parameters that are not included in - the computational graph with `loss` as the root. - There are two cases - - - Parameters were not used during - forward pass. - - Parameters were not used to produce - loss. - Default: False. - """ - - def __init__(self, grad_clip=None, detect_anomalous_params=False): - self.grad_clip = grad_clip - self.detect_anomalous_params = detect_anomalous_params - - def clip_grads(self, params): - params = list( - filter(lambda p: p.requires_grad and p.grad is not None, params)) - if len(params) > 0: - return clip_grad.clip_grad_norm_(params, **self.grad_clip) - - def after_train_iter(self, runner): - runner.optimizer.zero_grad() - if self.detect_anomalous_params: - self.detect_anomalous_parameters(runner.outputs['loss'], runner) - runner.outputs['loss'].backward() - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - - def detect_anomalous_parameters(self, loss, runner): - logger = runner.logger - parameters_in_graph = set() - visited = set() - - def traverse(grad_fn): - if grad_fn is None: - return - if grad_fn not in visited: - visited.add(grad_fn) - if hasattr(grad_fn, 'variable'): - parameters_in_graph.add(grad_fn.variable) - parents = grad_fn.next_functions - if parents is not None: - for parent in parents: - grad_fn = parent[0] - traverse(grad_fn) - - traverse(loss.grad_fn) - for n, p in runner.model.named_parameters(): - if p not in parameters_in_graph and p.requires_grad: - logger.log( - level=logging.ERROR, - msg=f'{n} with shape {p.size()} is not ' - f'in the computational graph \n') - - -@HOOKS.register_module() -class GradientCumulativeOptimizerHook(OptimizerHook): - """Optimizer Hook implements multi-iters gradient cumulating. - - Args: - cumulative_iters (int, optional): Num of gradient cumulative iters. - The optimizer will step every `cumulative_iters` iters. - Defaults to 1. - - Examples: - >>> # Use cumulative_iters to simulate a large batch size - >>> # It is helpful when the hardware cannot handle a large batch size. - >>> loader = DataLoader(data, batch_size=64) - >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) - >>> # almost equals to - >>> loader = DataLoader(data, batch_size=256) - >>> optim_hook = OptimizerHook() - """ - - def __init__(self, cumulative_iters=1, **kwargs): - super(GradientCumulativeOptimizerHook, self).__init__(**kwargs) - - assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ - f'cumulative_iters only accepts positive int, but got ' \ - f'{type(cumulative_iters)} instead.' - - self.cumulative_iters = cumulative_iters - self.divisible_iters = 0 - self.remainder_iters = 0 - self.initialized = False - - def has_batch_norm(self, module): - if isinstance(module, _BatchNorm): - return True - for m in module.children(): - if self.has_batch_norm(m): - return True - return False - - def _init(self, runner): - if runner.iter % self.cumulative_iters != 0: - runner.logger.warning( - 'Resume iter number is not divisible by cumulative_iters in ' - 'GradientCumulativeOptimizerHook, which means the gradient of ' - 'some iters is lost and the result may be influenced slightly.' - ) - - if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: - runner.logger.warning( - 'GradientCumulativeOptimizerHook may slightly decrease ' - 'performance if the model has BatchNorm layers.') - - residual_iters = runner.max_iters - runner.iter - - self.divisible_iters = ( - residual_iters // self.cumulative_iters * self.cumulative_iters) - self.remainder_iters = residual_iters - self.divisible_iters - - self.initialized = True - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - runner.optimizer.zero_grad() - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - self.loss_scaler = GradScaler() - elif isinstance(loss_scale, float): - self._scale_update_param = loss_scale - self.loss_scaler = GradScaler(init_scale=loss_scale) - elif isinstance(loss_scale, dict): - self.loss_scaler = GradScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training.""" - # wrap model mode to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer to - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients. - 3. Unscale the optimizer’s gradient tensors. - 4. Call optimizer.step() and update scale factor. - 5. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - - self.loss_scaler.scale(runner.outputs['loss']).backward() - self.loss_scaler.unscale_(runner.optimizer) - # grad clip - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using PyTorch's implementation) implements - multi-iters gradient cumulating. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - """ - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - - self.loss_scaler.scale(loss).backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - self.loss_scaler.unscale_(runner.optimizer) - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() - -else: - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (mmcv's implementation). - - The steps of fp16 optimizer is as follows. - 1. Scale the loss value. - 2. BP in the fp16 model. - 2. Copy gradients from fp16 model to fp32 weights. - 3. Update fp32 weights. - 4. Copy updated parameters from fp32 weights to fp16 model. - - Refer to https://arxiv.org/abs/1710.03740 for more details. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of LossScaler. - Defaults to 512. - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - if loss_scale == 'dynamic': - self.loss_scaler = LossScaler(mode='dynamic') - elif isinstance(loss_scale, float): - self.loss_scaler = LossScaler( - init_scale=loss_scale, mode='static') - elif isinstance(loss_scale, dict): - self.loss_scaler = LossScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training. - - 1. Make a master copy of fp32 weights for optimization. - 2. Convert the main model from fp32 to fp16. - """ - # keep a copy of fp32 weights - old_groups = runner.optimizer.param_groups - runner.optimizer.param_groups = copy.deepcopy( - runner.optimizer.param_groups) - state = defaultdict(dict) - p_map = { - old_p: p - for old_p, p in zip( - chain(*(g['params'] for g in old_groups)), - chain(*(g['params'] - for g in runner.optimizer.param_groups))) - } - for k, v in runner.optimizer.state.items(): - state[p_map[k]] = v - runner.optimizer.state = state - # convert model to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer `loss_scalar.py` - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients (fp16). - 3. Copy gradients from the model to the fp32 weight copy. - 4. Scale the gradients back and update the fp32 weight copy. - 5. Copy back the params from fp32 weight copy to the fp16 model. - 6. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - # scale the loss value - scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale - scaled_loss.backward() - # copy fp16 grads in the model to fp32 params in the optimizer - - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - self.loss_scaler.update_scale(has_overflow) - if has_overflow: - runner.logger.warning('Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using mmcv implementation) implements multi- - iters gradient cumulating.""" - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - - loss = runner.outputs['loss'] - loss = loss / loss_factor - - # scale the loss value - scaled_loss = loss * self.loss_scaler.loss_scale - scaled_loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - else: - runner.logger.warning( - 'Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - self.loss_scaler.update_scale(has_overflow) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/profiler.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/profiler.py deleted file mode 100644 index fef9adc13..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/profiler.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Callable, List, Optional, Union - -import torch - -from ..dist_utils import master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ProfilerHook(Hook): - """Profiler to analyze performance during training. - - PyTorch Profiler is a tool that allows the collection of the performance - metrics during the training. More details on Profiler can be found at - https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile - - Args: - by_epoch (bool): Profile performance by epoch or by iteration. - Default: True. - profile_iters (int): Number of iterations for profiling. - If ``by_epoch=True``, profile_iters indicates that they are the - first profile_iters epochs at the beginning of the - training, otherwise it indicates the first profile_iters - iterations. Default: 1. - activities (list[str]): List of activity groups (CPU, CUDA) to use in - profiling. Default: ['cpu', 'cuda']. - schedule (dict, optional): Config of generating the callable schedule. - if schedule is None, profiler will not add step markers into the - trace and table view. Default: None. - on_trace_ready (callable, dict): Either a handler or a dict of generate - handler. Default: None. - record_shapes (bool): Save information about operator's input shapes. - Default: False. - profile_memory (bool): Track tensor memory allocation/deallocation. - Default: False. - with_stack (bool): Record source information (file and line number) - for the ops. Default: False. - with_flops (bool): Use formula to estimate the FLOPS of specific - operators (matrix multiplication and 2D convolution). - Default: False. - json_trace_path (str, optional): Exports the collected trace in Chrome - JSON format. Default: None. - - Example: - >>> runner = ... # instantiate a Runner - >>> # tensorboard trace - >>> trace_config = dict(type='tb_trace', dir_name='work_dir') - >>> profiler_config = dict(on_trace_ready=trace_config) - >>> runner.register_profiler_hook(profiler_config) - >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) - """ - - def __init__(self, - by_epoch: bool = True, - profile_iters: int = 1, - activities: List[str] = ['cpu', 'cuda'], - schedule: Optional[dict] = None, - on_trace_ready: Optional[Union[Callable, dict]] = None, - record_shapes: bool = False, - profile_memory: bool = False, - with_stack: bool = False, - with_flops: bool = False, - json_trace_path: Optional[str] = None) -> None: - try: - from torch import profiler # torch version >= 1.8.1 - except ImportError: - raise ImportError('profiler is the new feature of torch1.8.1, ' - f'but your version is {torch.__version__}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' - self.by_epoch = by_epoch - - if profile_iters < 1: - raise ValueError('profile_iters should be greater than 0, but got ' - f'{profile_iters}') - self.profile_iters = profile_iters - - if not isinstance(activities, list): - raise ValueError( - f'activities should be list, but got {type(activities)}') - self.activities = [] - for activity in activities: - activity = activity.lower() - if activity == 'cpu': - self.activities.append(profiler.ProfilerActivity.CPU) - elif activity == 'cuda': - self.activities.append(profiler.ProfilerActivity.CUDA) - else: - raise ValueError( - f'activity should be "cpu" or "cuda", but got {activity}') - - if schedule is not None: - self.schedule = profiler.schedule(**schedule) - else: - self.schedule = None - - self.on_trace_ready = on_trace_ready - self.record_shapes = record_shapes - self.profile_memory = profile_memory - self.with_stack = with_stack - self.with_flops = with_flops - self.json_trace_path = json_trace_path - - @master_only - def before_run(self, runner): - if self.by_epoch and runner.max_epochs < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_epochs}') - - if not self.by_epoch and runner.max_iters < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_iters}') - - if callable(self.on_trace_ready): # handler - _on_trace_ready = self.on_trace_ready - elif isinstance(self.on_trace_ready, dict): # config of handler - trace_cfg = self.on_trace_ready.copy() - trace_type = trace_cfg.pop('type') # log_trace handler - if trace_type == 'log_trace': - - def _log_handler(prof): - print(prof.key_averages().table(**trace_cfg)) - - _on_trace_ready = _log_handler - elif trace_type == 'tb_trace': # tensorboard_trace handler - try: - import torch_tb_profiler # noqa: F401 - except ImportError: - raise ImportError('please run "pip install ' - 'torch-tb-profiler" to install ' - 'torch_tb_profiler') - _on_trace_ready = torch.profiler.tensorboard_trace_handler( - **trace_cfg) - else: - raise ValueError('trace_type should be "log_trace" or ' - f'"tb_trace", but got {trace_type}') - elif self.on_trace_ready is None: - _on_trace_ready = None # type: ignore - else: - raise ValueError('on_trace_ready should be handler, dict or None, ' - f'but got {type(self.on_trace_ready)}') - - if self.by_epoch and runner.max_epochs > 1: - warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' - 'instead of 1 epoch. Since profiler will slow down ' - 'the training, it is recommended to train 1 epoch ' - 'with ProfilerHook and adjust your setting according' - ' to the profiler summary. During normal training ' - '(epoch > 1), you may disable the ProfilerHook.') - - self.profiler = torch.profiler.profile( - activities=self.activities, - schedule=self.schedule, - on_trace_ready=_on_trace_ready, - record_shapes=self.record_shapes, - profile_memory=self.profile_memory, - with_stack=self.with_stack, - with_flops=self.with_flops) - - self.profiler.__enter__() - runner.logger.info('profiler is profiling...') - - @master_only - def after_train_epoch(self, runner): - if self.by_epoch and runner.epoch == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) - - @master_only - def after_train_iter(self, runner): - self.profiler.step() - if not self.by_epoch and runner.iter == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/sampler_seed.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/sampler_seed.py deleted file mode 100644 index ee0dc6bdd..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/sampler_seed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class DistSamplerSeedHook(Hook): - """Data-loading sampler for distributed training. - - When distributed training, it is only useful in conjunction with - :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same - purpose with :obj:`IterLoader`. - """ - - def before_epoch(self, runner): - if hasattr(runner.data_loader.sampler, 'set_epoch'): - # in case the data loader uses `SequentialSampler` in Pytorch - runner.data_loader.sampler.set_epoch(runner.epoch) - elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): - # batch sampler in pytorch warps the sampler as its attributes. - runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/sync_buffer.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/sync_buffer.py deleted file mode 100644 index 6376b7ff8..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/hooks/sync_buffer.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..dist_utils import allreduce_params -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class SyncBuffersHook(Hook): - """Synchronize model buffers such as running_mean and running_var in BN at - the end of each epoch. - - Args: - distributed (bool): Whether distributed training is used. It is - effective only for distributed training. Defaults to True. - """ - - def __init__(self, distributed=True): - self.distributed = distributed - - def after_epoch(self, runner): - """All-reduce model buffers at the end of each epoch.""" - if self.distributed: - allreduce_params(runner.model.buffers()) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/iter_based_runner.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/iter_based_runner.py deleted file mode 100644 index 9892b07a4..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/iter_based_runner.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch -from torch.optim import Optimizer - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .hooks import IterTimerHook -from .utils import get_host_info - - -class IterLoader: - - def __init__(self, dataloader): - self._dataloader = dataloader - self.iter_loader = iter(self._dataloader) - self._epoch = 0 - - @property - def epoch(self): - return self._epoch - - def __next__(self): - try: - data = next(self.iter_loader) - except StopIteration: - self._epoch += 1 - if hasattr(self._dataloader.sampler, 'set_epoch'): - self._dataloader.sampler.set_epoch(self._epoch) - time.sleep(2) # Prevent possible deadlock during epoch transition - self.iter_loader = iter(self._dataloader) - data = next(self.iter_loader) - - return data - - def __len__(self): - return len(self._dataloader) - - -@RUNNERS.register_module() -class IterBasedRunner(BaseRunner): - """Iteration-based Runner. - - This runner train models iteration by iteration. - """ - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._epoch = data_loader.epoch - data_batch = next(data_loader) - self.call_hook('before_train_iter') - outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.train_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_train_iter') - self._inner_iter += 1 - self._iter += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - data_batch = next(data_loader) - self.call_hook('before_val_iter') - outputs = self.model.val_step(data_batch, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.val_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_val_iter') - self._inner_iter += 1 - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, iters) to specify the - running order and iterations. E.g, [('train', 10000), - ('val', 1000)] means running 10000 iterations for training and - 1000 iterations for validation, iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - """Resume model from checkpoint. - - Args: - checkpoint (str): Checkpoint to resume from. - resume_optimizer (bool, optional): Whether resume the optimizer(s) - if the checkpoint file includes optimizer(s). Default to True. - map_location (str, optional): Same as :func:`torch.load`. - Default to 'default'. - """ - if map_location == 'default': - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - self._inner_iter = checkpoint['meta']['iter'] - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') - - def save_checkpoint(self, - out_dir, - filename_tmpl='iter_{}.pth', - meta=None, - save_optimizer=True, - create_symlink=True): - """Save checkpoint to file. - - Args: - out_dir (str): Directory to save checkpoint files. - filename_tmpl (str, optional): Checkpoint file template. - Defaults to 'iter_{}.pth'. - meta (dict, optional): Metadata to be saved in checkpoint. - Defaults to None. - save_optimizer (bool, optional): Whether save optimizer. - Defaults to True. - create_symlink (bool, optional): Whether create symlink to the - latest checkpoint file. Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.iter + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - custom_hooks_config=None): - """Register default hooks for iter-based training. - - Checkpoint hook, optimizer stepper hook and logger hooks will be set to - `by_epoch=False` by default. - - Default hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - if checkpoint_config is not None: - checkpoint_config.setdefault('by_epoch', False) - if lr_config is not None: - lr_config.setdefault('by_epoch', False) - if log_config is not None: - for info in log_config['hooks']: - info.setdefault('by_epoch', False) - super(IterBasedRunner, self).register_training_hooks( - lr_config=lr_config, - momentum_config=momentum_config, - optimizer_config=optimizer_config, - checkpoint_config=checkpoint_config, - log_config=log_config, - timer_config=IterTimerHook(), - custom_hooks_config=custom_hooks_config) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/log_buffer.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/log_buffer.py deleted file mode 100644 index d949e2941..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/log_buffer.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import numpy as np - - -class LogBuffer: - - def __init__(self): - self.val_history = OrderedDict() - self.n_history = OrderedDict() - self.output = OrderedDict() - self.ready = False - - def clear(self): - self.val_history.clear() - self.n_history.clear() - self.clear_output() - - def clear_output(self): - self.output.clear() - self.ready = False - - def update(self, vars, count=1): - assert isinstance(vars, dict) - for key, var in vars.items(): - if key not in self.val_history: - self.val_history[key] = [] - self.n_history[key] = [] - self.val_history[key].append(var) - self.n_history[key].append(count) - - def average(self, n=0): - """Average latest n values or all values.""" - assert n >= 0 - for key in self.val_history: - values = np.array(self.val_history[key][-n:]) - nums = np.array(self.n_history[key][-n:]) - avg = np.sum(values * nums) / np.sum(nums) - self.output[key] = avg - self.ready = True diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/optimizer/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/optimizer/__init__.py deleted file mode 100644 index 53c34d047..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/optimizer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, - build_optimizer_constructor) -from .default_constructor import DefaultOptimizerConstructor - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', - 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/optimizer/builder.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/optimizer/builder.py deleted file mode 100644 index f9234eed8..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/optimizer/builder.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect - -import torch - -from ...utils import Registry, build_from_cfg - -OPTIMIZERS = Registry('optimizer') -OPTIMIZER_BUILDERS = Registry('optimizer builder') - - -def register_torch_optimizers(): - torch_optimizers = [] - for module_name in dir(torch.optim): - if module_name.startswith('__'): - continue - _optim = getattr(torch.optim, module_name) - if inspect.isclass(_optim) and issubclass(_optim, - torch.optim.Optimizer): - OPTIMIZERS.register_module()(_optim) - torch_optimizers.append(module_name) - return torch_optimizers - - -TORCH_OPTIMIZERS = register_torch_optimizers() - - -def build_optimizer_constructor(cfg): - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/optimizer/default_constructor.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/optimizer/default_constructor.py deleted file mode 100644 index ae97db880..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/optimizer/default_constructor.py +++ /dev/null @@ -1,250 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -from torch.nn import GroupNorm, LayerNorm - -from mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of -from mmcv.utils.ext_loader import check_ops_exist -from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS - - -@OPTIMIZER_BUILDERS.register_module() -class DefaultOptimizerConstructor: - """Default constructor for optimizers. - - By default each parameter share the same optimizer settings, and we - provide an argument ``paramwise_cfg`` to specify parameter-wise settings. - It is a dict and may contain the following fields: - - - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If - one of the keys in ``custom_keys`` is a substring of the name of one - parameter, then the setting of the parameter will be specified by - ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will - be ignored. It should be noted that the aforementioned ``key`` is the - longest key that is a substring of the name of the parameter. If there - are multiple matched keys with the same length, then the key with lower - alphabet order will be chosen. - ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` - and ``decay_mult``. See Example 2 below. - - ``bias_lr_mult`` (float): It will be multiplied to the learning - rate for all bias parameters (except for those in normalization - layers and offset layers of DCN). - - ``bias_decay_mult`` (float): It will be multiplied to the weight - decay for all bias parameters (except for those in - normalization layers, depthwise conv layers, offset layers of DCN). - - ``norm_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of normalization - layers. - - ``dwconv_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of depthwise conv - layers. - - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning - rate for parameters of offset layer in the deformable convs - of a model. - - ``bypass_duplicate`` (bool): If true, the duplicate parameters - would not be added into optimizer. Default: False. - - Note: - - 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will - override the effect of ``bias_lr_mult`` in the bias of offset layer. - So be careful when using both ``bias_lr_mult`` and - ``dcn_offset_lr_mult``. If you wish to apply both of them to the offset - layer in deformable convs, set ``dcn_offset_lr_mult`` to the original - ``dcn_offset_lr_mult`` * ``bias_lr_mult``. - - 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will - apply it to all the DCN layers in the model. So be careful when the - model contains multiple DCN layers in places other than backbone. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - optimizer_cfg (dict): The config dict of the optimizer. - Positional fields are - - - `type`: class name of the optimizer. - - Optional fields are - - - any arguments of the corresponding optimizer type, e.g., - lr, weight_decay, momentum, etc. - paramwise_cfg (dict, optional): Parameter-wise options. - - Example 1: - >>> model = torch.nn.modules.Conv1d(1, 1, 1) - >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, - >>> weight_decay=0.0001) - >>> paramwise_cfg = dict(norm_decay_mult=0.) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - - Example 2: - >>> # assume model have attribute model.backbone and model.cls_head - >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) - >>> paramwise_cfg = dict(custom_keys={ - 'backbone': dict(lr_mult=0.1, decay_mult=0.9)}) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - >>> # Then the `lr` and `weight_decay` for model.backbone is - >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for - >>> # model.cls_head is (0.01, 0.95). - """ - - def __init__(self, optimizer_cfg, paramwise_cfg=None): - if not isinstance(optimizer_cfg, dict): - raise TypeError('optimizer_cfg should be a dict', - f'but got {type(optimizer_cfg)}') - self.optimizer_cfg = optimizer_cfg - self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg - self.base_lr = optimizer_cfg.get('lr', None) - self.base_wd = optimizer_cfg.get('weight_decay', None) - self._validate_cfg() - - def _validate_cfg(self): - if not isinstance(self.paramwise_cfg, dict): - raise TypeError('paramwise_cfg should be None or a dict, ' - f'but got {type(self.paramwise_cfg)}') - - if 'custom_keys' in self.paramwise_cfg: - if not isinstance(self.paramwise_cfg['custom_keys'], dict): - raise TypeError( - 'If specified, custom_keys must be a dict, ' - f'but got {type(self.paramwise_cfg["custom_keys"])}') - if self.base_wd is None: - for key in self.paramwise_cfg['custom_keys']: - if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: - raise ValueError('base_wd should not be None') - - # get base lr and weight decay - # weight_decay must be explicitly specified if mult is specified - if ('bias_decay_mult' in self.paramwise_cfg - or 'norm_decay_mult' in self.paramwise_cfg - or 'dwconv_decay_mult' in self.paramwise_cfg): - if self.base_wd is None: - raise ValueError('base_wd should not be None') - - def _is_in(self, param_group, param_group_list): - assert is_list_of(param_group_list, dict) - param = set(param_group['params']) - param_set = set() - for group in param_group_list: - param_set.update(set(group['params'])) - - return not param.isdisjoint(param_set) - - def add_params(self, params, module, prefix='', is_dcn_module=None): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - prefix (str): The prefix of the module - is_dcn_module (int|float|None): If the current module is a - submodule of DCN, `is_dcn_module` will be passed to - control conv_offset layer's learning rate. Defaults to None. - """ - # get param-wise options - custom_keys = self.paramwise_cfg.get('custom_keys', {}) - # first sort with alphabet order and then sort with reversed len of str - sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) - - bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) - bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) - norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) - dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) - bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) - dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) - - # special rules for norm layers and depth-wise conv layers - is_norm = isinstance(module, - (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) - is_dwconv = ( - isinstance(module, torch.nn.Conv2d) - and module.in_channels == module.groups) - - for name, param in module.named_parameters(recurse=False): - param_group = {'params': [param]} - if not param.requires_grad: - params.append(param_group) - continue - if bypass_duplicate and self._is_in(param_group, params): - warnings.warn(f'{prefix} is duplicate. It is skipped since ' - f'bypass_duplicate={bypass_duplicate}') - continue - # if the parameter match one of the custom keys, ignore other rules - is_custom = False - for key in sorted_keys: - if key in f'{prefix}.{name}': - is_custom = True - lr_mult = custom_keys[key].get('lr_mult', 1.) - param_group['lr'] = self.base_lr * lr_mult - if self.base_wd is not None: - decay_mult = custom_keys[key].get('decay_mult', 1.) - param_group['weight_decay'] = self.base_wd * decay_mult - break - - if not is_custom: - # bias_lr_mult affects all bias parameters - # except for norm.bias dcn.conv_offset.bias - if name == 'bias' and not (is_norm or is_dcn_module): - param_group['lr'] = self.base_lr * bias_lr_mult - - if (prefix.find('conv_offset') != -1 and is_dcn_module - and isinstance(module, torch.nn.Conv2d)): - # deal with both dcn_offset's bias & weight - param_group['lr'] = self.base_lr * dcn_offset_lr_mult - - # apply weight decay policies - if self.base_wd is not None: - # norm decay - if is_norm: - param_group[ - 'weight_decay'] = self.base_wd * norm_decay_mult - # depth-wise conv - elif is_dwconv: - param_group[ - 'weight_decay'] = self.base_wd * dwconv_decay_mult - # bias lr and decay - elif name == 'bias' and not is_dcn_module: - # TODO: current bias_decay_mult will have affect on DCN - param_group[ - 'weight_decay'] = self.base_wd * bias_decay_mult - params.append(param_group) - - if check_ops_exist(): - from mmcv.ops import DeformConv2d, ModulatedDeformConv2d - is_dcn_module = isinstance(module, - (DeformConv2d, ModulatedDeformConv2d)) - else: - is_dcn_module = False - for child_name, child_mod in module.named_children(): - child_prefix = f'{prefix}.{child_name}' if prefix else child_name - self.add_params( - params, - child_mod, - prefix=child_prefix, - is_dcn_module=is_dcn_module) - - def __call__(self, model): - if hasattr(model, 'module'): - model = model.module - - optimizer_cfg = self.optimizer_cfg.copy() - # if no paramwise option is specified, just use the global setting - if not self.paramwise_cfg: - optimizer_cfg['params'] = model.parameters() - return build_from_cfg(optimizer_cfg, OPTIMIZERS) - - # set param-wise lr and weight decay recursively - params = [] - self.add_params(params, model) - optimizer_cfg['params'] = params - - return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/priority.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/priority.py deleted file mode 100644 index 64cc4e3a0..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/priority.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from enum import Enum - - -class Priority(Enum): - """Hook priority levels. - - +--------------+------------+ - | Level | Value | - +==============+============+ - | HIGHEST | 0 | - +--------------+------------+ - | VERY_HIGH | 10 | - +--------------+------------+ - | HIGH | 30 | - +--------------+------------+ - | ABOVE_NORMAL | 40 | - +--------------+------------+ - | NORMAL | 50 | - +--------------+------------+ - | BELOW_NORMAL | 60 | - +--------------+------------+ - | LOW | 70 | - +--------------+------------+ - | VERY_LOW | 90 | - +--------------+------------+ - | LOWEST | 100 | - +--------------+------------+ - """ - - HIGHEST = 0 - VERY_HIGH = 10 - HIGH = 30 - ABOVE_NORMAL = 40 - NORMAL = 50 - BELOW_NORMAL = 60 - LOW = 70 - VERY_LOW = 90 - LOWEST = 100 - - -def get_priority(priority): - """Get priority value. - - Args: - priority (int or str or :obj:`Priority`): Priority. - - Returns: - int: The priority value. - """ - if isinstance(priority, int): - if priority < 0 or priority > 100: - raise ValueError('priority must be between 0 and 100') - return priority - elif isinstance(priority, Priority): - return priority.value - elif isinstance(priority, str): - return Priority[priority.upper()].value - else: - raise TypeError('priority must be an integer or Priority enum value') diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/utils.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/utils.py deleted file mode 100644 index 144d11e1a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/runner/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random -import sys -import time -import warnings -from getpass import getuser -from socket import gethostname - -import numpy as np -import torch - -import mmcv - - -def get_host_info(): - """Get hostname and username. - - Return empty string if exception raised, e.g. ``getpass.getuser()`` will - lead to error in docker container - """ - host = '' - try: - host = f'{getuser()}@{gethostname()}' - except Exception as e: - warnings.warn(f'Host or user not found: {str(e)}') - finally: - return host - - -def get_time_str(): - return time.strftime('%Y%m%d_%H%M%S', time.localtime()) - - -def obj_from_dict(info, parent=None, default_args=None): - """Initialize an object from dict. - - The dict must contain the key "type", which indicates the object type, it - can be either a string or type, such as "list" or ``list``. Remaining - fields are treated as the arguments for constructing the object. - - Args: - info (dict): Object types and arguments. - parent (:class:`module`): Module which may containing expected object - classes. - default_args (dict, optional): Default arguments for initializing the - object. - - Returns: - any type: Object built from the dict. - """ - assert isinstance(info, dict) and 'type' in info - assert isinstance(default_args, dict) or default_args is None - args = info.copy() - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if parent is not None: - obj_type = getattr(parent, obj_type) - else: - obj_type = sys.modules[obj_type] - elif not isinstance(obj_type, type): - raise TypeError('type must be a str or valid type, but ' - f'got {type(obj_type)}') - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - return obj_type(**args) - - -def set_random_seed(seed, deterministic=False, use_rank_shift=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - rank_shift (bool): Whether to add rank number to the random seed to - have different random seed in different threads. Default: False. - """ - if use_rank_shift: - rank, _ = mmcv.runner.get_dist_info() - seed += rank - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/__init__.py deleted file mode 100644 index e0825ed67..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/__init__.py +++ /dev/null @@ -1,78 +0,0 @@ -# flake8: noqa -# Copyright (c) OpenMMLab. All rights reserved. -from .config import Config, ConfigDict, DictAction -from .misc import (check_prerequisites, concat_list, deprecated_api_warning, - has_method, import_modules_from_strings, is_list_of, - is_method_overridden, is_seq_of, is_str, is_tuple_of, - iter_cast, list_cast, requires_executable, requires_package, - slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, - to_ntuple, tuple_cast) -from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, - scandir, symlink) -from .progressbar import (ProgressBar, track_iter_progress, - track_parallel_progress, track_progress) -from .testing import (assert_attrs_equal, assert_dict_contains_subset, - assert_dict_has_keys, assert_is_norm_layer, - assert_keys_equal, assert_params_all_zeros, - check_python_script) -from .timer import Timer, TimerError, check_time -from .version_utils import digit_version, get_git_hash - -try: - import torch -except ImportError: - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', - 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', - 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', - 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', - 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', - 'track_progress', 'track_iter_progress', 'track_parallel_progress', - 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', - 'digit_version', 'get_git_hash', 'import_modules_from_strings', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', - 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', - 'is_method_overridden', 'has_method' - ] -else: - from .device_type import IS_IPU_AVAILABLE, IS_MLU_AVAILABLE - from .env import collect_env - from .hub import load_url - from .logging import get_logger, print_log - from .parrots_jit import jit, skip_no_elena - # yapf: disable - from .parrots_wrapper import (IS_CUDA_AVAILABLE, TORCH_VERSION, - BuildExtension, CppExtension, CUDAExtension, - DataLoader, PoolDataLoader, SyncBatchNorm, - _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, - _AvgPoolNd, _BatchNorm, _ConvNd, - _ConvTransposeMixin, _get_cuda_home, - _InstanceNorm, _MaxPoolNd, get_build_config, - is_rocm_pytorch) - # yapf: enable - from .registry import Registry, build_from_cfg - from .seed import worker_init_fn - from .trace import is_jit_tracing - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', - 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', - 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', - 'check_prerequisites', 'requires_package', 'requires_executable', - 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', - 'symlink', 'scandir', 'ProgressBar', 'track_progress', - 'track_iter_progress', 'track_parallel_progress', 'Registry', - 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', - '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', - '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', - 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', - 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', - 'deprecated_api_warning', 'digit_version', 'get_git_hash', - 'import_modules_from_strings', 'jit', 'skip_no_elena', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', - 'assert_params_all_zeros', 'check_python_script', - 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', - '_get_cuda_home', 'load_url', 'has_method', 'IS_CUDA_AVAILABLE', - 'worker_init_fn', 'IS_MLU_AVAILABLE', 'IS_IPU_AVAILABLE' - ] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/config.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/config.py deleted file mode 100644 index 8efbc24e2..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/config.py +++ /dev/null @@ -1,719 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import ast -import copy -import os -import os.path as osp -import platform -import shutil -import sys -import tempfile -import types -import uuid -import warnings -from argparse import Action, ArgumentParser -from collections import abc -from importlib import import_module -from pathlib import Path - -from addict import Dict -from yapf.yapflib.yapf_api import FormatCode - -from .misc import import_modules_from_strings -from .path import check_file_exist - -if platform.system() == 'Windows': - import regex as re -else: - import re - -BASE_KEY = '_base_' -DELETE_KEY = '_delete_' -DEPRECATION_KEY = '_deprecation_' -RESERVED_KEYS = ['filename', 'text', 'pretty_text'] - - -class ConfigDict(Dict): - - def __missing__(self, name): - raise KeyError(name) - - def __getattr__(self, name): - try: - value = super(ConfigDict, self).__getattr__(name) - except KeyError: - ex = AttributeError(f"'{self.__class__.__name__}' object has no " - f"attribute '{name}'") - except Exception as e: - ex = e - else: - return value - raise ex - - -def add_args(parser, cfg, prefix=''): - for k, v in cfg.items(): - if isinstance(v, str): - parser.add_argument('--' + prefix + k) - elif isinstance(v, int): - parser.add_argument('--' + prefix + k, type=int) - elif isinstance(v, float): - parser.add_argument('--' + prefix + k, type=float) - elif isinstance(v, bool): - parser.add_argument('--' + prefix + k, action='store_true') - elif isinstance(v, dict): - add_args(parser, v, prefix + k + '.') - elif isinstance(v, abc.Iterable): - parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') - else: - print(f'cannot parse key {prefix + k} of type {type(v)}') - return parser - - -class Config: - """A facility for config and config files. - - It supports common file formats as configs: python/json/yaml. The interface - is the same as a dict object and also allows access config values as - attributes. - - Example: - >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - >>> cfg.a - 1 - >>> cfg.b - {'b1': [0, 1]} - >>> cfg.b.b1 - [0, 1] - >>> cfg = Config.fromfile('tests/data/config/a.py') - >>> cfg.filename - "/home/kchen/projects/mmcv/tests/data/config/a.py" - >>> cfg.item4 - 'test' - >>> cfg - "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " - "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" - """ - - @staticmethod - def _validate_py_syntax(filename): - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - content = f.read() - try: - ast.parse(content) - except SyntaxError as e: - raise SyntaxError('There are syntax errors in config ' - f'file {filename}: {e}') - - @staticmethod - def _substitute_predefined_vars(filename, temp_config_name): - file_dirname = osp.dirname(filename) - file_basename = osp.basename(filename) - file_basename_no_extension = osp.splitext(file_basename)[0] - file_extname = osp.splitext(filename)[1] - support_templates = dict( - fileDirname=file_dirname, - fileBasename=file_basename, - fileBasenameNoExtension=file_basename_no_extension, - fileExtname=file_extname) - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - for key, value in support_templates.items(): - regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' - value = value.replace('\\', '/') - config_file = re.sub(regexp, value, config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - - @staticmethod - def _pre_substitute_base_vars(filename, temp_config_name): - """Substitute base variable placehoders to string, so that parsing - would work.""" - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - base_var_dict = {} - regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' - base_vars = set(re.findall(regexp, config_file)) - for base_var in base_vars: - randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' - base_var_dict[randstr] = base_var - regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' - config_file = re.sub(regexp, f'"{randstr}"', config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - return base_var_dict - - @staticmethod - def _substitute_base_vars(cfg, base_var_dict, base_cfg): - """Substitute variable strings to their actual values.""" - cfg = copy.deepcopy(cfg) - - if isinstance(cfg, dict): - for k, v in cfg.items(): - if isinstance(v, str) and v in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[v].split('.'): - new_v = new_v[new_k] - cfg[k] = new_v - elif isinstance(v, (list, tuple, dict)): - cfg[k] = Config._substitute_base_vars( - v, base_var_dict, base_cfg) - elif isinstance(cfg, tuple): - cfg = tuple( - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg) - elif isinstance(cfg, list): - cfg = [ - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg - ] - elif isinstance(cfg, str) and cfg in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[cfg].split('.'): - new_v = new_v[new_k] - cfg = new_v - - return cfg - - @staticmethod - def _file2dict(filename, use_predefined_variables=True): - filename = osp.abspath(osp.expanduser(filename)) - check_file_exist(filename) - fileExtname = osp.splitext(filename)[1] - if fileExtname not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - - with tempfile.TemporaryDirectory() as temp_config_dir: - temp_config_file = tempfile.NamedTemporaryFile( - dir=temp_config_dir, suffix=fileExtname) - if platform.system() == 'Windows': - temp_config_file.close() - temp_config_name = osp.basename(temp_config_file.name) - # Substitute predefined variables - if use_predefined_variables: - Config._substitute_predefined_vars(filename, - temp_config_file.name) - else: - shutil.copyfile(filename, temp_config_file.name) - # Substitute base variables from placeholders to strings - base_var_dict = Config._pre_substitute_base_vars( - temp_config_file.name, temp_config_file.name) - - if filename.endswith('.py'): - temp_module_name = osp.splitext(temp_config_name)[0] - sys.path.insert(0, temp_config_dir) - Config._validate_py_syntax(filename) - mod = import_module(temp_module_name) - sys.path.pop(0) - cfg_dict = { - name: value - for name, value in mod.__dict__.items() - if not name.startswith('__') - and not isinstance(value, types.ModuleType) - and not isinstance(value, types.FunctionType) - } - # delete imported module - del sys.modules[temp_module_name] - elif filename.endswith(('.yml', '.yaml', '.json')): - import mmcv - cfg_dict = mmcv.load(temp_config_file.name) - # close temp file - temp_config_file.close() - - # check deprecation information - if DEPRECATION_KEY in cfg_dict: - deprecation_info = cfg_dict.pop(DEPRECATION_KEY) - warning_msg = f'The config file {filename} will be deprecated ' \ - 'in the future.' - if 'expected' in deprecation_info: - warning_msg += f' Please use {deprecation_info["expected"]} ' \ - 'instead.' - if 'reference' in deprecation_info: - warning_msg += ' More information can be found at ' \ - f'{deprecation_info["reference"]}' - warnings.warn(warning_msg, DeprecationWarning) - - cfg_text = filename + '\n' - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - cfg_text += f.read() - - if BASE_KEY in cfg_dict: - cfg_dir = osp.dirname(filename) - base_filename = cfg_dict.pop(BASE_KEY) - base_filename = base_filename if isinstance( - base_filename, list) else [base_filename] - - cfg_dict_list = list() - cfg_text_list = list() - for f in base_filename: - _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) - cfg_dict_list.append(_cfg_dict) - cfg_text_list.append(_cfg_text) - - base_cfg_dict = dict() - for c in cfg_dict_list: - duplicate_keys = base_cfg_dict.keys() & c.keys() - if len(duplicate_keys) > 0: - raise KeyError('Duplicate key is not allowed among bases. ' - f'Duplicate keys: {duplicate_keys}') - base_cfg_dict.update(c) - - # Substitute base variables from strings to their actual values - cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, - base_cfg_dict) - - base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) - cfg_dict = base_cfg_dict - - # merge cfg_text - cfg_text_list.append(cfg_text) - cfg_text = '\n'.join(cfg_text_list) - - return cfg_dict, cfg_text - - @staticmethod - def _merge_a_into_b(a, b, allow_list_keys=False): - """merge dict ``a`` into dict ``b`` (non-inplace). - - Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid - in-place modifications. - - Args: - a (dict): The source dict to be merged into ``b``. - b (dict): The origin dict to be fetch keys from ``a``. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in source ``a`` and will replace the element of the - corresponding index in b if b is a list. Default: False. - - Returns: - dict: The modified dict of ``b`` using ``a``. - - Examples: - # Normally merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # Delete b first and merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # b is a list - >>> Config._merge_a_into_b( - ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) - [{'a': 2}, {'b': 2}] - """ - b = b.copy() - for k, v in a.items(): - if allow_list_keys and k.isdigit() and isinstance(b, list): - k = int(k) - if len(b) <= k: - raise KeyError(f'Index {k} exceeds the length of list {b}') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - elif isinstance(v, dict): - if k in b and not v.pop(DELETE_KEY, False): - allowed_types = (dict, list) if allow_list_keys else dict - if not isinstance(b[k], allowed_types): - raise TypeError( - f'{k}={v} in child config cannot inherit from ' - f'base because {k} is a dict in the child config ' - f'but is of type {type(b[k])} in base config. ' - f'You may set `{DELETE_KEY}=True` to ignore the ' - f'base config.') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - else: - b[k] = ConfigDict(v) - else: - b[k] = v - return b - - @staticmethod - def fromfile(filename, - use_predefined_variables=True, - import_custom_modules=True): - if isinstance(filename, Path): - filename = str(filename) - cfg_dict, cfg_text = Config._file2dict(filename, - use_predefined_variables) - if import_custom_modules and cfg_dict.get('custom_imports', None): - import_modules_from_strings(**cfg_dict['custom_imports']) - return Config(cfg_dict, cfg_text=cfg_text, filename=filename) - - @staticmethod - def fromstring(cfg_str, file_format): - """Generate config from config str. - - Args: - cfg_str (str): Config str. - file_format (str): Config file format corresponding to the - config str. Only py/yml/yaml/json type are supported now! - - Returns: - :obj:`Config`: Config obj. - """ - if file_format not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - if file_format != '.py' and 'dict(' in cfg_str: - # check if users specify a wrong suffix for python - warnings.warn( - 'Please check "file_format", the file format may be .py') - with tempfile.NamedTemporaryFile( - 'w', encoding='utf-8', suffix=file_format, - delete=False) as temp_file: - temp_file.write(cfg_str) - # on windows, previous implementation cause error - # see PR 1077 for details - cfg = Config.fromfile(temp_file.name) - os.remove(temp_file.name) - return cfg - - @staticmethod - def auto_argparser(description=None): - """Generate argparser from config file automatically (experimental)""" - partial_parser = ArgumentParser(description=description) - partial_parser.add_argument('config', help='config file path') - cfg_file = partial_parser.parse_known_args()[0].config - cfg = Config.fromfile(cfg_file) - parser = ArgumentParser(description=description) - parser.add_argument('config', help='config file path') - add_args(parser, cfg) - return parser, cfg - - def __init__(self, cfg_dict=None, cfg_text=None, filename=None): - if cfg_dict is None: - cfg_dict = dict() - elif not isinstance(cfg_dict, dict): - raise TypeError('cfg_dict must be a dict, but ' - f'got {type(cfg_dict)}') - for key in cfg_dict: - if key in RESERVED_KEYS: - raise KeyError(f'{key} is reserved for config file') - - if isinstance(filename, Path): - filename = str(filename) - - super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) - super(Config, self).__setattr__('_filename', filename) - if cfg_text: - text = cfg_text - elif filename: - with open(filename, 'r') as f: - text = f.read() - else: - text = '' - super(Config, self).__setattr__('_text', text) - - @property - def filename(self): - return self._filename - - @property - def text(self): - return self._text - - @property - def pretty_text(self): - - indent = 4 - - def _indent(s_, num_spaces): - s = s_.split('\n') - if len(s) == 1: - return s_ - first = s.pop(0) - s = [(num_spaces * ' ') + line for line in s] - s = '\n'.join(s) - s = first + '\n' + s - return s - - def _format_basic_types(k, v, use_mapping=False): - if isinstance(v, str): - v_str = f"'{v}'" - else: - v_str = str(v) - - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) - - return attr_str - - def _format_list(k, v, use_mapping=False): - # check if all items in the list are dict - if all(isinstance(_, dict) for _ in v): - v_str = '[\n' - v_str += '\n'.join( - f'dict({_indent(_format_dict(v_), indent)}),' - for v_ in v).rstrip(',') - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) + ']' - else: - attr_str = _format_basic_types(k, v, use_mapping) - return attr_str - - def _contain_invalid_identifier(dict_str): - contain_invalid_identifier = False - for key_name in dict_str: - contain_invalid_identifier |= \ - (not str(key_name).isidentifier()) - return contain_invalid_identifier - - def _format_dict(input_dict, outest_level=False): - r = '' - s = [] - - use_mapping = _contain_invalid_identifier(input_dict) - if use_mapping: - r += '{' - for idx, (k, v) in enumerate(input_dict.items()): - is_last = idx >= len(input_dict) - 1 - end = '' if outest_level or is_last else ',' - if isinstance(v, dict): - v_str = '\n' + _format_dict(v) - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: dict({v_str}' - else: - attr_str = f'{str(k)}=dict({v_str}' - attr_str = _indent(attr_str, indent) + ')' + end - elif isinstance(v, list): - attr_str = _format_list(k, v, use_mapping) + end - else: - attr_str = _format_basic_types(k, v, use_mapping) + end - - s.append(attr_str) - r += '\n'.join(s) - if use_mapping: - r += '}' - return r - - cfg_dict = self._cfg_dict.to_dict() - text = _format_dict(cfg_dict, outest_level=True) - # copied from setup.cfg - yapf_style = dict( - based_on_style='pep8', - blank_line_before_nested_class_or_def=True, - split_before_expression_after_opening_paren=True) - text, _ = FormatCode(text, style_config=yapf_style, verify=True) - - return text - - def __repr__(self): - return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' - - def __len__(self): - return len(self._cfg_dict) - - def __getattr__(self, name): - return getattr(self._cfg_dict, name) - - def __getitem__(self, name): - return self._cfg_dict.__getitem__(name) - - def __setattr__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setattr__(name, value) - - def __setitem__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setitem__(name, value) - - def __iter__(self): - return iter(self._cfg_dict) - - def __getstate__(self): - return (self._cfg_dict, self._filename, self._text) - - def __copy__(self): - cls = self.__class__ - other = cls.__new__(cls) - other.__dict__.update(self.__dict__) - - return other - - def __deepcopy__(self, memo): - cls = self.__class__ - other = cls.__new__(cls) - memo[id(self)] = other - - for key, value in self.__dict__.items(): - super(Config, other).__setattr__(key, copy.deepcopy(value, memo)) - - return other - - def __setstate__(self, state): - _cfg_dict, _filename, _text = state - super(Config, self).__setattr__('_cfg_dict', _cfg_dict) - super(Config, self).__setattr__('_filename', _filename) - super(Config, self).__setattr__('_text', _text) - - def dump(self, file=None): - cfg_dict = super(Config, self).__getattribute__('_cfg_dict').to_dict() - if self.filename.endswith('.py'): - if file is None: - return self.pretty_text - else: - with open(file, 'w', encoding='utf-8') as f: - f.write(self.pretty_text) - else: - import mmcv - if file is None: - file_format = self.filename.split('.')[-1] - return mmcv.dump(cfg_dict, file_format=file_format) - else: - mmcv.dump(cfg_dict, file) - - def merge_from_dict(self, options, allow_list_keys=True): - """Merge list into cfg_dict. - - Merge the dict parsed by MultipleKVAction into this cfg. - - Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} - >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) - >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - - >>> # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) - >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) - >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - - Args: - options (dict): dict of configs to merge from. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in ``options`` and will replace the element of the - corresponding index in the config if the config is a list. - Default: True. - """ - option_cfg_dict = {} - for full_key, v in options.items(): - d = option_cfg_dict - key_list = full_key.split('.') - for subkey in key_list[:-1]: - d.setdefault(subkey, ConfigDict()) - d = d[subkey] - subkey = key_list[-1] - d[subkey] = v - - cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - super(Config, self).__setattr__( - '_cfg_dict', - Config._merge_a_into_b( - option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) - - -class DictAction(Action): - """ - argparse action to split an argument into KEY=VALUE form - on the first = and append to a dictionary. List options can - be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit - brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build - list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' - """ - - @staticmethod - def _parse_int_float_bool(val): - try: - return int(val) - except ValueError: - pass - try: - return float(val) - except ValueError: - pass - if val.lower() in ['true', 'false']: - return True if val.lower() == 'true' else False - if val == 'None': - return None - return val - - @staticmethod - def _parse_iterable(val): - """Parse iterable values in the string. - - All elements inside '()' or '[]' are treated as iterable values. - - Args: - val (str): Value string. - - Returns: - list | tuple: The expanded list or tuple from the string. - - Examples: - >>> DictAction._parse_iterable('1,2,3') - [1, 2, 3] - >>> DictAction._parse_iterable('[a, b, c]') - ['a', 'b', 'c'] - >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') - [(1, 2, 3), ['a', 'b'], 'c'] - """ - - def find_next_comma(string): - """Find the position of next comma in the string. - - If no ',' is found in the string, return the string length. All - chars inside '()' and '[]' are treated as one element and thus ',' - inside these brackets are ignored. - """ - assert (string.count('(') == string.count(')')) and ( - string.count('[') == string.count(']')), \ - f'Imbalanced brackets exist in {string}' - end = len(string) - for idx, char in enumerate(string): - pre = string[:idx] - # The string before this ',' is balanced - if ((char == ',') and (pre.count('(') == pre.count(')')) - and (pre.count('[') == pre.count(']'))): - end = idx - break - return end - - # Strip ' and " characters and replace whitespace. - val = val.strip('\'\"').replace(' ', '') - is_tuple = False - if val.startswith('(') and val.endswith(')'): - is_tuple = True - val = val[1:-1] - elif val.startswith('[') and val.endswith(']'): - val = val[1:-1] - elif ',' not in val: - # val is a single value - return DictAction._parse_int_float_bool(val) - - values = [] - while len(val) > 0: - comma_idx = find_next_comma(val) - element = DictAction._parse_iterable(val[:comma_idx]) - values.append(element) - val = val[comma_idx + 1:] - if is_tuple: - values = tuple(values) - return values - - def __call__(self, parser, namespace, values, option_string=None): - options = {} - for kv in values: - key, val = kv.split('=', maxsplit=1) - options[key] = self._parse_iterable(val) - setattr(namespace, self.dest, options) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/device_type.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/device_type.py deleted file mode 100644 index c29d944ab..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/device_type.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - - -def is_ipu_available(): - try: - import poptorch - return poptorch.ipuHardwareIsAvailable() - except ImportError: - return False - - -IS_IPU_AVAILABLE = is_ipu_available() - - -def is_mlu_available(): - try: - import torch - return (hasattr(torch, 'is_mlu_available') - and torch.is_mlu_available()) - except Exception: - return False - - -IS_MLU_AVAILABLE = is_mlu_available() diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/env.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/env.py deleted file mode 100644 index 511332506..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/env.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This file holding some environment constant for sharing by other files.""" - -import os.path as osp -import subprocess -import sys -from collections import defaultdict - -import cv2 -import torch - -import mmcv -from .parrots_wrapper import get_build_config - - -def collect_env(): - """Collect the information of the running environments. - - Returns: - dict: The environment information. The following fields are contained. - - - sys.platform: The variable of ``sys.platform``. - - Python: Python version. - - CUDA available: Bool, indicating if CUDA is available. - - GPU devices: Device type of each GPU. - - CUDA_HOME (optional): The env var ``CUDA_HOME``. - - NVCC (optional): NVCC version. - - GCC: GCC version, "n/a" if GCC is not installed. - - MSVC: Microsoft Virtual C++ Compiler version, Windows only. - - PyTorch: PyTorch version. - - PyTorch compiling details: The output of \ - ``torch.__config__.show()``. - - TorchVision (optional): TorchVision version. - - OpenCV: OpenCV version. - - MMCV: MMCV version. - - MMCV Compiler: The GCC version for compiling MMCV ops. - - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. - """ - env_info = {} - env_info['sys.platform'] = sys.platform - env_info['Python'] = sys.version.replace('\n', '') - - cuda_available = torch.cuda.is_available() - env_info['CUDA available'] = cuda_available - - if cuda_available: - devices = defaultdict(list) - for k in range(torch.cuda.device_count()): - devices[torch.cuda.get_device_name(k)].append(str(k)) - for name, device_ids in devices.items(): - env_info['GPU ' + ','.join(device_ids)] = name - - from mmcv.utils.parrots_wrapper import _get_cuda_home - CUDA_HOME = _get_cuda_home() - env_info['CUDA_HOME'] = CUDA_HOME - - if CUDA_HOME is not None and osp.isdir(CUDA_HOME): - try: - nvcc = osp.join(CUDA_HOME, 'bin/nvcc') - nvcc = subprocess.check_output(f'"{nvcc}" -V', shell=True) - nvcc = nvcc.decode('utf-8').strip() - release = nvcc.rfind('Cuda compilation tools') - build = nvcc.rfind('Build ') - nvcc = nvcc[release:build].strip() - except subprocess.SubprocessError: - nvcc = 'Not Available' - env_info['NVCC'] = nvcc - - try: - # Check C++ Compiler. - # For Unix-like, sysconfig has 'CC' variable like 'gcc -pthread ...', - # indicating the compiler used, we use this to get the compiler name - import sysconfig - cc = sysconfig.get_config_var('CC') - if cc: - cc = osp.basename(cc.split()[0]) - cc_info = subprocess.check_output(f'{cc} --version', shell=True) - env_info['GCC'] = cc_info.decode('utf-8').partition( - '\n')[0].strip() - else: - # on Windows, cl.exe is not in PATH. We need to find the path. - # distutils.ccompiler.new_compiler() returns a msvccompiler - # object and after initialization, path to cl.exe is found. - import locale - import os - from distutils.ccompiler import new_compiler - ccompiler = new_compiler() - ccompiler.initialize() - cc = subprocess.check_output( - f'{ccompiler.cc}', stderr=subprocess.STDOUT, shell=True) - encoding = os.device_encoding( - sys.stdout.fileno()) or locale.getpreferredencoding() - env_info['MSVC'] = cc.decode(encoding).partition('\n')[0].strip() - env_info['GCC'] = 'n/a' - except subprocess.CalledProcessError: - env_info['GCC'] = 'n/a' - - env_info['PyTorch'] = torch.__version__ - env_info['PyTorch compiling details'] = get_build_config() - - try: - import torchvision - env_info['TorchVision'] = torchvision.__version__ - except ModuleNotFoundError: - pass - - env_info['OpenCV'] = cv2.__version__ - - env_info['MMCV'] = mmcv.__version__ - - try: - from mmcv.ops import get_compiler_version, get_compiling_cuda_version - except ModuleNotFoundError: - env_info['MMCV Compiler'] = 'n/a' - env_info['MMCV CUDA Compiler'] = 'n/a' - else: - env_info['MMCV Compiler'] = get_compiler_version() - env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() - - return env_info diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/ext_loader.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/ext_loader.py deleted file mode 100644 index f82c6d568..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/ext_loader.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os -import pkgutil -import warnings -from collections import namedtuple - -import torch - -if torch.__version__ != 'parrots': - - def load_ext(name, funcs): - ext = importlib.import_module('mmcv.' + name) - for fun in funcs: - assert hasattr(ext, fun), f'{fun} miss in module {name}' - return ext -else: - from parrots import extension - from parrots.base import ParrotsException - - has_return_value_ops = [ - 'nms', - 'softnms', - 'nms_match', - 'nms_rotated', - 'top_pool_forward', - 'top_pool_backward', - 'bottom_pool_forward', - 'bottom_pool_backward', - 'left_pool_forward', - 'left_pool_backward', - 'right_pool_forward', - 'right_pool_backward', - 'fused_bias_leakyrelu', - 'upfirdn2d', - 'ms_deform_attn_forward', - 'pixel_group', - 'contour_expand', - 'diff_iou_rotated_sort_vertices_forward', - ] - - def get_fake_func(name, e): - - def fake_func(*args, **kwargs): - warnings.warn(f'{name} is not supported in parrots now') - raise e - - return fake_func - - def load_ext(name, funcs): - ExtModule = namedtuple('ExtModule', funcs) - ext_list = [] - lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - for fun in funcs: - try: - ext_fun = extension.load(fun, name, lib_dir=lib_root) - except ParrotsException as e: - if 'No element registered' not in e.message: - warnings.warn(e.message) - ext_fun = get_fake_func(fun, e) - ext_list.append(ext_fun) - else: - if fun in has_return_value_ops: - ext_list.append(ext_fun.op) - else: - ext_list.append(ext_fun.op_) - return ExtModule(*ext_list) - - -def check_ops_exist(): - ext_loader = pkgutil.find_loader('mmcv._ext') - return ext_loader is not None diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/hub.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/hub.py deleted file mode 100644 index 12fbff2ee..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/hub.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# The 1.6 release of PyTorch switched torch.save to use a new zipfile-based -# file format. It will cause RuntimeError when a checkpoint was saved in -# torch >= 1.6.0 but loaded in torch < 1.7.0. -# More details at https://github.com/open-mmlab/mmpose/issues/904 -from .parrots_wrapper import TORCH_VERSION -from .path import mkdir_or_exist -from .version_utils import digit_version - -if TORCH_VERSION != 'parrots' and digit_version(TORCH_VERSION) < digit_version( - '1.7.0'): - # Modified from https://github.com/pytorch/pytorch/blob/master/torch/hub.py - import os - import sys - import warnings - import zipfile - from urllib.parse import urlparse - - import torch - from torch.hub import HASH_REGEX, _get_torch_home, download_url_to_file - - # Hub used to support automatically extracts from zipfile manually - # compressed by users. The legacy zip format expects only one file from - # torch.save() < 1.6 in the zip. We should remove this support since - # zipfile is now default zipfile format for torch.save(). - def _is_legacy_zip_format(filename): - if zipfile.is_zipfile(filename): - infolist = zipfile.ZipFile(filename).infolist() - return len(infolist) == 1 and not infolist[0].is_dir() - return False - - def _legacy_zip_load(filename, model_dir, map_location): - warnings.warn( - 'Falling back to the old format < 1.6. This support will' - ' be deprecated in favor of default zipfile format ' - 'introduced in 1.6. Please redo torch.save() to save it ' - 'in the new zipfile format.', DeprecationWarning) - # Note: extractall() defaults to overwrite file if exists. No need to - # clean up beforehand. We deliberately don't handle tarfile here - # since our legacy serialization format was in tar. - # E.g. resnet18-5c106cde.pth which is widely used. - with zipfile.ZipFile(filename) as f: - members = f.infolist() - if len(members) != 1: - raise RuntimeError( - 'Only one file(not dir) is allowed in the zipfile') - f.extractall(model_dir) - extraced_name = members[0].filename - extracted_file = os.path.join(model_dir, extraced_name) - return torch.load(extracted_file, map_location=map_location) - - def load_url(url, - model_dir=None, - map_location=None, - progress=True, - check_hash=False, - file_name=None): - r"""Loads the Torch serialized object at the given URL. - - If downloaded file is a zip file, it will be automatically decompressed - - If the object is already present in `model_dir`, it's deserialized and - returned. - The default value of ``model_dir`` is ``/checkpoints`` where - ``hub_dir`` is the directory returned by :func:`~torch.hub.get_dir`. - - Args: - url (str): URL of the object to download - model_dir (str, optional): directory in which to save the object - map_location (optional): a function or a dict specifying how to - remap storage locations (see torch.load) - progress (bool, optional): whether or not to display a progress bar - to stderr. Default: True - check_hash(bool, optional): If True, the filename part of the URL - should follow the naming convention ``filename-.ext`` - where ```` is the first eight or more digits of the - SHA256 hash of the contents of the file. The hash is used to - ensure unique names and to verify the contents of the file. - Default: False - file_name (str, optional): name for the downloaded file. Filename - from ``url`` will be used if not set. Default: None. - - Example: - >>> url = ('https://s3.amazonaws.com/pytorch/models/resnet18-5c106' - ... 'cde.pth') - >>> state_dict = torch.hub.load_state_dict_from_url(url) - """ - # Issue warning to move data if old env is set - if os.getenv('TORCH_MODEL_ZOO'): - warnings.warn( - 'TORCH_MODEL_ZOO is deprecated, please use env ' - 'TORCH_HOME instead', DeprecationWarning) - - if model_dir is None: - torch_home = _get_torch_home() - model_dir = os.path.join(torch_home, 'checkpoints') - - mkdir_or_exist(model_dir) - - parts = urlparse(url) - filename = os.path.basename(parts.path) - if file_name is not None: - filename = file_name - cached_file = os.path.join(model_dir, filename) - if not os.path.exists(cached_file): - sys.stderr.write('Downloading: "{}" to {}\n'.format( - url, cached_file)) - hash_prefix = None - if check_hash: - r = HASH_REGEX.search(filename) # r is Optional[Match[str]] - hash_prefix = r.group(1) if r else None - download_url_to_file( - url, cached_file, hash_prefix, progress=progress) - - if _is_legacy_zip_format(cached_file): - return _legacy_zip_load(cached_file, model_dir, map_location) - - try: - return torch.load(cached_file, map_location=map_location) - except RuntimeError as error: - if digit_version(TORCH_VERSION) < digit_version('1.5.0'): - warnings.warn( - f'If the error is the same as "{cached_file} is a zip ' - 'archive (did you mean to use torch.jit.load()?)", you can' - ' upgrade your torch to 1.5.0 or higher (current torch ' - f'version is {TORCH_VERSION}). The error was raised ' - ' because the checkpoint was saved in torch>=1.6.0 but ' - 'loaded in torch<1.5.') - raise error -else: - from torch.utils.model_zoo import load_url # noqa: F401 diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/logging.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/logging.py deleted file mode 100644 index c4c7025f0..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/logging.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.distributed as dist - -logger_initialized = {} - - -def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): - """Initialize and get a logger by name. - - If the logger has not been initialized, this method will initialize the - logger by adding one or two handlers, otherwise the initialized logger will - be directly returned. During initialization, a StreamHandler will always be - added. If `log_file` is specified and the process rank is 0, a FileHandler - will also be added. - - Args: - name (str): Logger name. - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the logger. - log_level (int): The logger level. Note that only the process of - rank 0 is affected, and other processes will set the level to - "Error" thus be silent most of the time. - file_mode (str): The file mode used in opening log file. - Defaults to 'w'. - - Returns: - logging.Logger: The expected logger. - """ - logger = logging.getLogger(name) - if name in logger_initialized: - return logger - # handle hierarchical names - # e.g., logger "a" is initialized, then logger "a.b" will skip the - # initialization since it is a child of "a". - for logger_name in logger_initialized: - if name.startswith(logger_name): - return logger - - # handle duplicate logs to the console - # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) - # to the root logger. As logger.propagate is True by default, this root - # level handler causes logging messages from rank>0 processes to - # unexpectedly show up on the console, creating much unwanted clutter. - # To fix this issue, we set the root logger's StreamHandler, if any, to log - # at the ERROR level. - for handler in logger.root.handlers: - if type(handler) is logging.StreamHandler: - handler.setLevel(logging.ERROR) - - stream_handler = logging.StreamHandler() - handlers = [stream_handler] - - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - else: - rank = 0 - - # only rank 0 will add a FileHandler - if rank == 0 and log_file is not None: - # Here, the default behaviour of the official logger is 'a'. Thus, we - # provide an interface to change the file mode to the default - # behaviour. - file_handler = logging.FileHandler(log_file, file_mode) - handlers.append(file_handler) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - for handler in handlers: - handler.setFormatter(formatter) - handler.setLevel(log_level) - logger.addHandler(handler) - - if rank == 0: - logger.setLevel(log_level) - else: - logger.setLevel(logging.ERROR) - - logger_initialized[name] = True - - return logger - - -def print_log(msg, logger=None, level=logging.INFO): - """Print a log message. - - Args: - msg (str): The message to be logged. - logger (logging.Logger | str | None): The logger to be used. - Some special loggers are: - - - "silent": no message will be printed. - - other str: the logger obtained with `get_root_logger(logger)`. - - None: The `print()` method will be used to print log messages. - level (int): Logging level. Only available when `logger` is a Logger - object or "root". - """ - if logger is None: - print(msg) - elif isinstance(logger, logging.Logger): - logger.log(level, msg) - elif logger == 'silent': - pass - elif isinstance(logger, str): - _logger = get_logger(logger) - _logger.log(level, msg) - else: - raise TypeError( - 'logger should be either a logging.Logger object, str, ' - f'"silent" or None, but got {type(logger)}') diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/misc.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/misc.py deleted file mode 100644 index 7957ea89b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/misc.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections.abc -import functools -import itertools -import subprocess -import warnings -from collections import abc -from importlib import import_module -from inspect import getfullargspec -from itertools import repeat - - -# From PyTorch internals -def _ntuple(n): - - def parse(x): - if isinstance(x, collections.abc.Iterable): - return x - return tuple(repeat(x, n)) - - return parse - - -to_1tuple = _ntuple(1) -to_2tuple = _ntuple(2) -to_3tuple = _ntuple(3) -to_4tuple = _ntuple(4) -to_ntuple = _ntuple - - -def is_str(x): - """Whether the input is an string instance. - - Note: This method is deprecated since python 2 is no longer supported. - """ - return isinstance(x, str) - - -def import_modules_from_strings(imports, allow_failed_imports=False): - """Import modules from the given list of strings. - - Args: - imports (list | str | None): The given module names to be imported. - allow_failed_imports (bool): If True, the failed imports will return - None. Otherwise, an ImportError is raise. Default: False. - - Returns: - list[module] | module | None: The imported modules. - - Examples: - >>> osp, sys = import_modules_from_strings( - ... ['os.path', 'sys']) - >>> import os.path as osp_ - >>> import sys as sys_ - >>> assert osp == osp_ - >>> assert sys == sys_ - """ - if not imports: - return - single_import = False - if isinstance(imports, str): - single_import = True - imports = [imports] - if not isinstance(imports, list): - raise TypeError( - f'custom_imports must be a list but got type {type(imports)}') - imported = [] - for imp in imports: - if not isinstance(imp, str): - raise TypeError( - f'{imp} is of type {type(imp)} and cannot be imported.') - try: - imported_tmp = import_module(imp) - except ImportError: - if allow_failed_imports: - warnings.warn(f'{imp} failed to import and is ignored.', - UserWarning) - imported_tmp = None - else: - raise ImportError - imported.append(imported_tmp) - if single_import: - imported = imported[0] - return imported - - -def iter_cast(inputs, dst_type, return_type=None): - """Cast elements of an iterable object into some type. - - Args: - inputs (Iterable): The input object. - dst_type (type): Destination type. - return_type (type, optional): If specified, the output object will be - converted to this type, otherwise an iterator. - - Returns: - iterator or specified type: The converted object. - """ - if not isinstance(inputs, abc.Iterable): - raise TypeError('inputs must be an iterable object') - if not isinstance(dst_type, type): - raise TypeError('"dst_type" must be a valid type') - - out_iterable = map(dst_type, inputs) - - if return_type is None: - return out_iterable - else: - return return_type(out_iterable) - - -def list_cast(inputs, dst_type): - """Cast elements of an iterable object into a list of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=list) - - -def tuple_cast(inputs, dst_type): - """Cast elements of an iterable object into a tuple of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=tuple) - - -def is_seq_of(seq, expected_type, seq_type=None): - """Check whether it is a sequence of some type. - - Args: - seq (Sequence): The sequence to be checked. - expected_type (type): Expected type of sequence items. - seq_type (type, optional): Expected sequence type. - - Returns: - bool: Whether the sequence is valid. - """ - if seq_type is None: - exp_seq_type = abc.Sequence - else: - assert isinstance(seq_type, type) - exp_seq_type = seq_type - if not isinstance(seq, exp_seq_type): - return False - for item in seq: - if not isinstance(item, expected_type): - return False - return True - - -def is_list_of(seq, expected_type): - """Check whether it is a list of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=list) - - -def is_tuple_of(seq, expected_type): - """Check whether it is a tuple of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=tuple) - - -def slice_list(in_list, lens): - """Slice a list into several sub lists by a list of given length. - - Args: - in_list (list): The list to be sliced. - lens(int or list): The expected length of each out list. - - Returns: - list: A list of sliced list. - """ - if isinstance(lens, int): - assert len(in_list) % lens == 0 - lens = [lens] * int(len(in_list) / lens) - if not isinstance(lens, list): - raise TypeError('"indices" must be an integer or a list of integers') - elif sum(lens) != len(in_list): - raise ValueError('sum of lens and list length does not ' - f'match: {sum(lens)} != {len(in_list)}') - out_list = [] - idx = 0 - for i in range(len(lens)): - out_list.append(in_list[idx:idx + lens[i]]) - idx += lens[i] - return out_list - - -def concat_list(in_list): - """Concatenate a list of list into a single list. - - Args: - in_list (list): The list of list to be merged. - - Returns: - list: The concatenated flat list. - """ - return list(itertools.chain(*in_list)) - - -def check_prerequisites( - prerequisites, - checker, - msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' - 'found, please install them first.'): # yapf: disable - """A decorator factory to check if prerequisites are satisfied. - - Args: - prerequisites (str of list[str]): Prerequisites to be checked. - checker (callable): The checker method that returns True if a - prerequisite is meet, False otherwise. - msg_tmpl (str): The message template with two variables. - - Returns: - decorator: A specific decorator. - """ - - def wrap(func): - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - requirements = [prerequisites] if isinstance( - prerequisites, str) else prerequisites - missing = [] - for item in requirements: - if not checker(item): - missing.append(item) - if missing: - print(msg_tmpl.format(', '.join(missing), func.__name__)) - raise RuntimeError('Prerequisites not meet.') - else: - return func(*args, **kwargs) - - return wrapped_func - - return wrap - - -def _check_py_package(package): - try: - import_module(package) - except ImportError: - return False - else: - return True - - -def _check_executable(cmd): - if subprocess.call(f'which {cmd}', shell=True) != 0: - return False - else: - return True - - -def requires_package(prerequisites): - """A decorator to check if some python packages are installed. - - Example: - >>> @requires_package('numpy') - >>> func(arg1, args): - >>> return numpy.zeros(1) - array([0.]) - >>> @requires_package(['numpy', 'non_package']) - >>> func(arg1, args): - >>> return numpy.zeros(1) - ImportError - """ - return check_prerequisites(prerequisites, checker=_check_py_package) - - -def requires_executable(prerequisites): - """A decorator to check if some executable files are installed. - - Example: - >>> @requires_executable('ffmpeg') - >>> func(arg1, args): - >>> print(1) - 1 - """ - return check_prerequisites(prerequisites, checker=_check_executable) - - -def deprecated_api_warning(name_dict, cls_name=None): - """A decorator to check if some arguments are deprecate and try to replace - deprecate src_arg_name to dst_arg_name. - - Args: - name_dict(dict): - key (str): Deprecate argument names. - val (str): Expected argument names. - - Returns: - func: New function. - """ - - def api_warning_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get name of the function - func_name = old_func.__name__ - if cls_name is not None: - func_name = f'{cls_name}.{func_name}' - if args: - arg_names = args_info.args[:len(args)] - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in arg_names: - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - arg_names[arg_names.index(src_arg_name)] = dst_arg_name - if kwargs: - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in kwargs: - - assert dst_arg_name not in kwargs, ( - f'The expected behavior is to replace ' - f'the deprecated key `{src_arg_name}` to ' - f'new key `{dst_arg_name}`, but got them ' - f'in the arguments at the same time, which ' - f'is confusing. `{src_arg_name} will be ' - f'deprecated in the future, please ' - f'use `{dst_arg_name}` instead.') - - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - kwargs[dst_arg_name] = kwargs.pop(src_arg_name) - - # apply converted arguments to the decorated method - output = old_func(*args, **kwargs) - return output - - return new_func - - return api_warning_wrapper - - -def is_method_overridden(method, base_class, derived_class): - """Check if a method of base class is overridden in derived class. - - Args: - method (str): the method name to check. - base_class (type): the class of the base class. - derived_class (type | Any): the class or instance of the derived class. - """ - assert isinstance(base_class, type), \ - "base_class doesn't accept instance, Please pass class instead." - - if not isinstance(derived_class, type): - derived_class = derived_class.__class__ - - base_method = getattr(base_class, method) - derived_method = getattr(derived_class, method) - return derived_method != base_method - - -def has_method(obj: object, method: str) -> bool: - """Check whether the object has a method. - - Args: - method (str): The method name to check. - obj (object): The object to check. - - Returns: - bool: True if the object has the method else False. - """ - return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/parrots_jit.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/parrots_jit.py deleted file mode 100644 index 61873f6db..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/parrots_jit.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -from .parrots_wrapper import TORCH_VERSION - -parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') - -if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': - from parrots.jit import pat as jit -else: - - def jit(func=None, - check_input=None, - full_shape=True, - derivate=False, - coderize=False, - optimize=False): - - def wrapper(func): - - def wrapper_inner(*args, **kargs): - return func(*args, **kargs) - - return wrapper_inner - - if func is None: - return wrapper - else: - return func - - -if TORCH_VERSION == 'parrots': - from parrots.utils.tester import skip_no_elena -else: - - def skip_no_elena(func): - - def wrapper(*args, **kargs): - return func(*args, **kargs) - - return wrapper diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/parrots_wrapper.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/parrots_wrapper.py deleted file mode 100644 index 7e657b561..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/parrots_wrapper.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch - -TORCH_VERSION = torch.__version__ - - -def is_cuda_available() -> bool: - return torch.cuda.is_available() - - -IS_CUDA_AVAILABLE = is_cuda_available() - - -def is_rocm_pytorch() -> bool: - is_rocm = False - if TORCH_VERSION != 'parrots': - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - return is_rocm - - -def _get_cuda_home(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import CUDA_HOME - else: - if is_rocm_pytorch(): - from torch.utils.cpp_extension import ROCM_HOME - CUDA_HOME = ROCM_HOME - else: - from torch.utils.cpp_extension import CUDA_HOME - return CUDA_HOME - - -def get_build_config(): - if TORCH_VERSION == 'parrots': - from parrots.config import get_build_info - return get_build_info() - else: - return torch.__config__.show() - - -def _get_conv(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin - else: - from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin - return _ConvNd, _ConvTransposeMixin - - -def _get_dataloader(): - if TORCH_VERSION == 'parrots': - from torch.utils.data import DataLoader, PoolDataLoader - else: - from torch.utils.data import DataLoader - PoolDataLoader = DataLoader - return DataLoader, PoolDataLoader - - -def _get_extension(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import BuildExtension, Extension - CppExtension = partial(Extension, cuda=False) - CUDAExtension = partial(Extension, cuda=True) - else: - from torch.utils.cpp_extension import (BuildExtension, CppExtension, - CUDAExtension) - return BuildExtension, CppExtension, CUDAExtension - - -def _get_pool(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - else: - from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd - - -def _get_norm(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm2d - else: - from torch.nn.modules.batchnorm import _BatchNorm - from torch.nn.modules.instancenorm import _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm - return _BatchNorm, _InstanceNorm, SyncBatchNorm_ - - -_ConvNd, _ConvTransposeMixin = _get_conv() -DataLoader, PoolDataLoader = _get_dataloader() -BuildExtension, CppExtension, CUDAExtension = _get_extension() -_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() -_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() - - -class SyncBatchNorm(SyncBatchNorm_): - - def _check_input_dim(self, input): - if TORCH_VERSION == 'parrots': - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input (got {input.dim()}D input)') - else: - super()._check_input_dim(input) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/path.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/path.py deleted file mode 100644 index 568081837..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/path.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -from pathlib import Path - -from .misc import is_str - - -def is_filepath(x): - return is_str(x) or isinstance(x, Path) - - -def fopen(filepath, *args, **kwargs): - if is_str(filepath): - return open(filepath, *args, **kwargs) - elif isinstance(filepath, Path): - return filepath.open(*args, **kwargs) - raise ValueError('`filepath` should be a string or a Path') - - -def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): - if not osp.isfile(filename): - raise FileNotFoundError(msg_tmpl.format(filename)) - - -def mkdir_or_exist(dir_name, mode=0o777): - if dir_name == '': - return - dir_name = osp.expanduser(dir_name) - os.makedirs(dir_name, mode=mode, exist_ok=True) - - -def symlink(src, dst, overwrite=True, **kwargs): - if os.path.lexists(dst) and overwrite: - os.remove(dst) - os.symlink(src, dst, **kwargs) - - -def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): - """Scan a directory to find the interested files. - - Args: - dir_path (str | :obj:`Path`): Path of the directory. - suffix (str | tuple(str), optional): File suffix that we are - interested in. Default: None. - recursive (bool, optional): If set to True, recursively scan the - directory. Default: False. - case_sensitive (bool, optional) : If set to False, ignore the case of - suffix. Default: True. - - Returns: - A generator for all the interested files with relative paths. - """ - if isinstance(dir_path, (str, Path)): - dir_path = str(dir_path) - else: - raise TypeError('"dir_path" must be a string or Path object') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('"suffix" must be a string or tuple of strings') - - if suffix is not None and not case_sensitive: - suffix = suffix.lower() if isinstance(suffix, str) else tuple( - item.lower() for item in suffix) - - root = dir_path - - def _scandir(dir_path, suffix, recursive, case_sensitive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - _rel_path = rel_path if case_sensitive else rel_path.lower() - if suffix is None or _rel_path.endswith(suffix): - yield rel_path - elif recursive and os.path.isdir(entry.path): - # scan recursively if entry.path is a directory - yield from _scandir(entry.path, suffix, recursive, - case_sensitive) - - return _scandir(dir_path, suffix, recursive, case_sensitive) - - -def find_vcs_root(path, markers=('.git', )): - """Finds the root directory (including itself) of specified markers. - - Args: - path (str): Path of directory or file. - markers (list[str], optional): List of file or directory names. - - Returns: - The directory contained one of the markers or None if not found. - """ - if osp.isfile(path): - path = osp.dirname(path) - - prev, cur = None, osp.abspath(osp.expanduser(path)) - while cur != prev: - if any(osp.exists(osp.join(cur, marker)) for marker in markers): - return cur - prev, cur = cur, osp.split(cur)[0] - return None diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/progressbar.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/progressbar.py deleted file mode 100644 index 0062f670d..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/progressbar.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from collections.abc import Iterable -from multiprocessing import Pool -from shutil import get_terminal_size - -from .timer import Timer - - -class ProgressBar: - """A progress bar which can print the progress.""" - - def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): - self.task_num = task_num - self.bar_width = bar_width - self.completed = 0 - self.file = file - if start: - self.start() - - @property - def terminal_width(self): - width, _ = get_terminal_size() - return width - - def start(self): - if self.task_num > 0: - self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' - 'elapsed: 0s, ETA:') - else: - self.file.write('completed: 0, elapsed: 0s') - self.file.flush() - self.timer = Timer() - - def update(self, num_tasks=1): - assert num_tasks > 0 - self.completed += num_tasks - elapsed = self.timer.since_start() - if elapsed > 0: - fps = self.completed / elapsed - else: - fps = float('inf') - if self.task_num > 0: - percentage = self.completed / float(self.task_num) - eta = int(elapsed * (1 - percentage) / percentage + 0.5) - msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ - f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ - f'ETA: {eta:5}s' - - bar_width = min(self.bar_width, - int(self.terminal_width - len(msg)) + 2, - int(self.terminal_width * 0.6)) - bar_width = max(2, bar_width) - mark_width = int(bar_width * percentage) - bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) - self.file.write(msg.format(bar_chars)) - else: - self.file.write( - f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' - f' {fps:.1f} tasks/s') - self.file.flush() - - -def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): - """Track the progress of tasks execution with a progress bar. - - Tasks are done with a simple for-loop. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - results = [] - for task in tasks: - results.append(func(task, **kwargs)) - prog_bar.update() - prog_bar.file.write('\n') - return results - - -def init_pool(process_num, initializer=None, initargs=None): - if initializer is None: - return Pool(process_num) - elif initargs is None: - return Pool(process_num, initializer) - else: - if not isinstance(initargs, tuple): - raise TypeError('"initargs" must be a tuple') - return Pool(process_num, initializer, initargs) - - -def track_parallel_progress(func, - tasks, - nproc, - initializer=None, - initargs=None, - bar_width=50, - chunksize=1, - skip_first=False, - keep_order=True, - file=sys.stdout): - """Track the progress of parallel task execution with a progress bar. - - The built-in :mod:`multiprocessing` module is used for process pools and - tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - nproc (int): Process (worker) number. - initializer (None or callable): Refer to :class:`multiprocessing.Pool` - for details. - initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for - details. - chunksize (int): Refer to :class:`multiprocessing.Pool` for details. - bar_width (int): Width of progress bar. - skip_first (bool): Whether to skip the first sample for each worker - when estimating fps, since the initialization step may takes - longer. - keep_order (bool): If True, :func:`Pool.imap` is used, otherwise - :func:`Pool.imap_unordered` is used. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - pool = init_pool(nproc, initializer, initargs) - start = not skip_first - task_num -= nproc * chunksize * int(skip_first) - prog_bar = ProgressBar(task_num, bar_width, start, file=file) - results = [] - if keep_order: - gen = pool.imap(func, tasks, chunksize) - else: - gen = pool.imap_unordered(func, tasks, chunksize) - for result in gen: - results.append(result) - if skip_first: - if len(results) < nproc * chunksize: - continue - elif len(results) == nproc * chunksize: - prog_bar.start() - continue - prog_bar.update() - prog_bar.file.write('\n') - pool.close() - pool.join() - return results - - -def track_iter_progress(tasks, bar_width=50, file=sys.stdout): - """Track the progress of tasks iteration or enumeration with a progress - bar. - - Tasks are yielded with a simple for-loop. - - Args: - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Yields: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - for task in tasks: - yield task - prog_bar.update() - prog_bar.file.write('\n') diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/registry.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/registry.py deleted file mode 100644 index a6d92b68b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/registry.py +++ /dev/null @@ -1,337 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import warnings -from functools import partial - -from .misc import deprecated_api_warning, is_seq_of - - -def build_from_cfg(cfg, registry, default_args=None): - """Build a module from config dict when it is a class configuration, or - call a function from config dict when it is a function configuration. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = build_from_cfg(dict(type='Resnet'), MODELS) - >>> # Returns an instantiated object - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = build_from_cfg(dict(type='resnet50'), MODELS) - >>> # Return a result of the calling function - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type) or inspect.isfunction(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - try: - return obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{obj_cls.__name__}: {e}') - - -class Registry: - """A registry to map strings to classes or functions. - - Registered object could be built from registry. Meanwhile, registered - functions could be called from registry. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = MODELS.build(dict(type='ResNet')) - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = MODELS.build(dict(type='resnet50')) - - Please refer to - https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for - advanced usage. - - Args: - name (str): Registry name. - build_func(func, optional): Build function to construct instance from - Registry, func:`build_from_cfg` is used if neither ``parent`` or - ``build_func`` is specified. If ``parent`` is specified and - ``build_func`` is not given, ``build_func`` will be inherited - from ``parent``. Default: None. - parent (Registry, optional): Parent registry. The class registered in - children registry could be built from parent. Default: None. - scope (str, optional): The scope of registry. It is the key to search - for children registry. If not specified, scope will be the name of - the package where class is defined, e.g. mmdet, mmcls, mmseg. - Default: None. - """ - - def __init__(self, name, build_func=None, parent=None, scope=None): - self._name = name - self._module_dict = dict() - self._children = dict() - self._scope = self.infer_scope() if scope is None else scope - - # self.build_func will be set with the following priority: - # 1. build_func - # 2. parent.build_func - # 3. build_from_cfg - if build_func is None: - if parent is not None: - self.build_func = parent.build_func - else: - self.build_func = build_from_cfg - else: - self.build_func = build_func - if parent is not None: - assert isinstance(parent, Registry) - parent._add_children(self) - self.parent = parent - else: - self.parent = None - - def __len__(self): - return len(self._module_dict) - - def __contains__(self, key): - return self.get(key) is not None - - def __repr__(self): - format_str = self.__class__.__name__ + \ - f'(name={self._name}, ' \ - f'items={self._module_dict})' - return format_str - - @staticmethod - def infer_scope(): - """Infer the scope of registry. - - The name of the package where registry is defined will be returned. - - Example: - >>> # in mmdet/models/backbone/resnet.py - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - The scope of ``ResNet`` will be ``mmdet``. - - Returns: - str: The inferred scope name. - """ - # We access the caller using inspect.currentframe() instead of - # inspect.stack() for performance reasons. See details in PR #1844 - frame = inspect.currentframe() - # get the frame where `infer_scope()` is called - infer_scope_caller = frame.f_back.f_back - filename = inspect.getmodule(infer_scope_caller).__name__ - split_filename = filename.split('.') - return split_filename[0] - - @staticmethod - def split_scope_key(key): - """Split scope and key. - - The first scope will be split from key. - - Examples: - >>> Registry.split_scope_key('mmdet.ResNet') - 'mmdet', 'ResNet' - >>> Registry.split_scope_key('ResNet') - None, 'ResNet' - - Return: - tuple[str | None, str]: The former element is the first scope of - the key, which can be ``None``. The latter is the remaining key. - """ - split_index = key.find('.') - if split_index != -1: - return key[:split_index], key[split_index + 1:] - else: - return None, key - - @property - def name(self): - return self._name - - @property - def scope(self): - return self._scope - - @property - def module_dict(self): - return self._module_dict - - @property - def children(self): - return self._children - - def get(self, key): - """Get the registry record. - - Args: - key (str): The class name in string format. - - Returns: - class: The corresponding class. - """ - scope, real_key = self.split_scope_key(key) - if scope is None or scope == self._scope: - # get from self - if real_key in self._module_dict: - return self._module_dict[real_key] - else: - # get from self._children - if scope in self._children: - return self._children[scope].get(real_key) - else: - # goto root - parent = self.parent - while parent.parent is not None: - parent = parent.parent - return parent.get(key) - - def build(self, *args, **kwargs): - return self.build_func(*args, **kwargs, registry=self) - - def _add_children(self, registry): - """Add children for a registry. - - The ``registry`` will be added as children based on its scope. - The parent registry could build objects from children registry. - - Example: - >>> models = Registry('models') - >>> mmdet_models = Registry('models', parent=models) - >>> @mmdet_models.register_module() - >>> class ResNet: - >>> pass - >>> resnet = models.build(dict(type='mmdet.ResNet')) - """ - - assert isinstance(registry, Registry) - assert registry.scope is not None - assert registry.scope not in self.children, \ - f'scope {registry.scope} exists in {self.name} registry' - self.children[registry.scope] = registry - - @deprecated_api_warning(name_dict=dict(module_class='module')) - def _register_module(self, module, module_name=None, force=False): - if not inspect.isclass(module) and not inspect.isfunction(module): - raise TypeError('module must be a class or a function, ' - f'but got {type(module)}') - - if module_name is None: - module_name = module.__name__ - if isinstance(module_name, str): - module_name = [module_name] - for name in module_name: - if not force and name in self._module_dict: - raise KeyError(f'{name} is already registered ' - f'in {self.name}') - self._module_dict[name] = module - - def deprecated_register_module(self, cls=None, force=False): - warnings.warn( - 'The old API of register_module(module, force=False) ' - 'is deprecated and will be removed, please use the new API ' - 'register_module(name=None, force=False, module=None) instead.', - DeprecationWarning) - if cls is None: - return partial(self.deprecated_register_module, force=force) - self._register_module(cls, force=force) - return cls - - def register_module(self, name=None, force=False, module=None): - """Register a module. - - A record will be added to `self._module_dict`, whose key is the class - name or the specified name, and value is the class itself. - It can be used as a decorator or a normal function. - - Example: - >>> backbones = Registry('backbone') - >>> @backbones.register_module() - >>> class ResNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> @backbones.register_module(name='mnet') - >>> class MobileNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> class ResNet: - >>> pass - >>> backbones.register_module(ResNet) - - Args: - name (str | None): The module name to be registered. If not - specified, the class name will be used. - force (bool, optional): Whether to override an existing class with - the same name. Default: False. - module (type): Module class or function to be registered. - """ - if not isinstance(force, bool): - raise TypeError(f'force must be a boolean, but got {type(force)}') - # NOTE: This is a walkaround to be compatible with the old api, - # while it may introduce unexpected bugs. - if isinstance(name, type): - return self.deprecated_register_module(name, force=force) - - # raise the error ahead of time - if not (name is None or isinstance(name, str) or is_seq_of(name, str)): - raise TypeError( - 'name must be either of None, an instance of str or a sequence' - f' of str, but got {type(name)}') - - # use it as a normal method: x.register_module(module=SomeClass) - if module is not None: - self._register_module(module=module, module_name=name, force=force) - return module - - # use it as a decorator: @x.register_module() - def _register(module): - self._register_module(module=module, module_name=name, force=force) - return module - - return _register diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/seed.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/seed.py deleted file mode 100644 index 003f92367..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/seed.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import random - -import numpy as np -import torch - - -def worker_init_fn(worker_id: int, num_workers: int, rank: int, seed: int): - """Function to initialize each worker. - - The seed of each worker equals to - ``num_worker * rank + worker_id + user_seed``. - - Args: - worker_id (int): Id for each worker. - num_workers (int): Number of workers. - rank (int): Rank in distributed training. - seed (int): Random seed. - """ - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/testing.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/testing.py deleted file mode 100644 index 7b64e8fae..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/testing.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (c) Open-MMLab. -import sys -from collections.abc import Iterable -from runpy import run_path -from shlex import split -from typing import Any, Dict, List -from unittest.mock import patch - - -def check_python_script(cmd): - """Run the python cmd script with `__main__`. The difference between - `os.system` is that, this function exectues code in the current process, so - that it can be tracked by coverage tools. Currently it supports two forms: - - - ./tests/data/scripts/hello.py zz - - python tests/data/scripts/hello.py zz - """ - args = split(cmd) - if args[0] == 'python': - args = args[1:] - with patch.object(sys, 'argv', args): - run_path(args[0], run_name='__main__') - - -def _any(judge_result): - """Since built-in ``any`` works only when the element of iterable is not - iterable, implement the function.""" - if not isinstance(judge_result, Iterable): - return judge_result - - try: - for element in judge_result: - if _any(element): - return True - except TypeError: - # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) - if judge_result: - return True - return False - - -def assert_dict_contains_subset(dict_obj: Dict[Any, Any], - expected_subset: Dict[Any, Any]) -> bool: - """Check if the dict_obj contains the expected_subset. - - Args: - dict_obj (Dict[Any, Any]): Dict object to be checked. - expected_subset (Dict[Any, Any]): Subset expected to be contained in - dict_obj. - - Returns: - bool: Whether the dict_obj contains the expected_subset. - """ - - for key, value in expected_subset.items(): - if key not in dict_obj.keys() or _any(dict_obj[key] != value): - return False - return True - - -def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: - """Check if attribute of class object is correct. - - Args: - obj (object): Class object to be checked. - expected_attrs (Dict[str, Any]): Dict of the expected attrs. - - Returns: - bool: Whether the attribute of class object is correct. - """ - for attr, value in expected_attrs.items(): - if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): - return False - return True - - -def assert_dict_has_keys(obj: Dict[str, Any], - expected_keys: List[str]) -> bool: - """Check if the obj has all the expected_keys. - - Args: - obj (Dict[str, Any]): Object to be checked. - expected_keys (List[str]): Keys expected to contained in the keys of - the obj. - - Returns: - bool: Whether the obj has the expected keys. - """ - return set(expected_keys).issubset(set(obj.keys())) - - -def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: - """Check if target_keys is equal to result_keys. - - Args: - result_keys (List[str]): Result keys to be checked. - target_keys (List[str]): Target keys to be checked. - - Returns: - bool: Whether target_keys is equal to result_keys. - """ - return set(result_keys) == set(target_keys) - - -def assert_is_norm_layer(module) -> bool: - """Check if the module is a norm layer. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the module is a norm layer. - """ - from torch.nn import GroupNorm, LayerNorm - - from .parrots_wrapper import _BatchNorm, _InstanceNorm - norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) - return isinstance(module, norm_layer_candidates) - - -def assert_params_all_zeros(module) -> bool: - """Check if the parameters of the module is all zeros. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the parameters of the module is all zeros. - """ - weight_data = module.weight.data - is_weight_zero = weight_data.allclose( - weight_data.new_zeros(weight_data.size())) - - if hasattr(module, 'bias') and module.bias is not None: - bias_data = module.bias.data - is_bias_zero = bias_data.allclose( - bias_data.new_zeros(bias_data.size())) - else: - is_bias_zero = True - - return is_weight_zero and is_bias_zero diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/timer.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/timer.py deleted file mode 100644 index 02e96e537..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/timer.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from time import time - - -class TimerError(Exception): - - def __init__(self, message): - self.message = message - super(TimerError, self).__init__(message) - - -class Timer: - """A flexible Timer class. - - Examples: - >>> import time - >>> import mmcv - >>> with mmcv.Timer(): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - 1.000 - >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - it takes 1.0 seconds - >>> timer = mmcv.Timer() - >>> time.sleep(0.5) - >>> print(timer.since_start()) - 0.500 - >>> time.sleep(0.5) - >>> print(timer.since_last_check()) - 0.500 - >>> print(timer.since_start()) - 1.000 - """ - - def __init__(self, start=True, print_tmpl=None): - self._is_running = False - self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' - if start: - self.start() - - @property - def is_running(self): - """bool: indicate whether the timer is running""" - return self._is_running - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - print(self.print_tmpl.format(self.since_last_check())) - self._is_running = False - - def start(self): - """Start the timer.""" - if not self._is_running: - self._t_start = time() - self._is_running = True - self._t_last = time() - - def since_start(self): - """Total time since the timer is started. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - self._t_last = time() - return self._t_last - self._t_start - - def since_last_check(self): - """Time since the last checking. - - Either :func:`since_start` or :func:`since_last_check` is a checking - operation. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - dur = time() - self._t_last - self._t_last = time() - return dur - - -_g_timers = {} # global timers - - -def check_time(timer_id): - """Add check points in a single line. - - This method is suitable for running a task on a list of items. A timer will - be registered when the method is called for the first time. - - Examples: - >>> import time - >>> import mmcv - >>> for i in range(1, 6): - >>> # simulate a code block - >>> time.sleep(i) - >>> mmcv.check_time('task1') - 2.000 - 3.000 - 4.000 - 5.000 - - Args: - str: Timer identifier. - """ - if timer_id not in _g_timers: - _g_timers[timer_id] = Timer() - return 0 - else: - return _g_timers[timer_id].since_last_check() diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/trace.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/trace.py deleted file mode 100644 index 45423bd05..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/trace.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch - -from mmcv.utils import digit_version - - -def is_jit_tracing() -> bool: - if (torch.__version__ != 'parrots' - and digit_version(torch.__version__) >= digit_version('1.6.0')): - on_trace = torch.jit.is_tracing() - # In PyTorch 1.6, torch.jit.is_tracing has a bug. - # Refers to https://github.com/pytorch/pytorch/issues/42448 - if isinstance(on_trace, bool): - return on_trace - else: - return torch._C._is_tracing() - else: - warnings.warn( - 'torch.jit.is_tracing is only supported after v1.6.0. ' - 'Therefore is_tracing returns False automatically. Please ' - 'set on_trace manually if you are using trace.', UserWarning) - return False diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/version_utils.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/version_utils.py deleted file mode 100644 index 963c45a2e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/utils/version_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import subprocess -import warnings - -from packaging.version import parse - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - assert 'parrots' not in version_str - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - -def get_git_hash(fallback='unknown', digits=None): - """Get the git hash of the current repo. - - Args: - fallback (str, optional): The fallback string when git hash is - unavailable. Defaults to 'unknown'. - digits (int, optional): kept digits of the hash. Defaults to None, - meaning all digits are kept. - - Returns: - str: Git commit hash. - """ - - if digits is not None and not isinstance(digits, int): - raise TypeError('digits must be None or an integer') - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - sha = out.strip().decode('ascii') - if digits is not None: - sha = sha[:digits] - except OSError: - sha = fallback - - return sha diff --git a/cv/semantic_segmentation/stdc/pytorch/mmcv/version.py b/cv/semantic_segmentation/stdc/pytorch/mmcv/version.py deleted file mode 100644 index a97ffc2dd..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmcv/version.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -__version__ = '1.5.0' - - -def parse_version_info(version_str: str, length: int = 4) -> tuple: - """Parse a version string into a tuple. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int | str]: The version info, e.g., "1.3.0" is parsed into - (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into - (2, 0, 0, 0, 'rc', 1) (when length is set to 4). - """ - from packaging.version import parse - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - release.extend(list(version.pre)) - elif version.is_postrelease: - release.extend(list(version.post)) - else: - release.extend([0, 0]) - return tuple(release) - - -version_info = tuple(int(x) for x in __version__.split('.')[:3]) - -__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/__init__.py deleted file mode 100644 index 360abfc85..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv -from packaging.version import parse - -from .version import __version__, version_info - -MMCV_MIN = '1.3.13' -MMCV_MAX = '1.6.0' - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -mmcv_min_version = digit_version(MMCV_MIN) -mmcv_max_version = digit_version(MMCV_MAX) -mmcv_version = digit_version(mmcv.__version__) - - -assert (mmcv_min_version <= mmcv_version <= mmcv_max_version), \ - f'MMCV=={mmcv.__version__} is used but incompatible. ' \ - f'Please install mmcv>={mmcv_min_version}, <={mmcv_max_version}.' - -__all__ = ['__version__', 'version_info', 'digit_version'] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/apis/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/apis/__init__.py deleted file mode 100644 index c68818053..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/apis/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .inference import inference_segmentor, init_segmentor, show_result_pyplot -from .test import multi_gpu_test, single_gpu_test -from .train import (get_root_logger, init_random_seed, set_random_seed, - train_segmentor) - -__all__ = [ - 'get_root_logger', 'set_random_seed', 'train_segmentor', 'init_segmentor', - 'inference_segmentor', 'multi_gpu_test', 'single_gpu_test', - 'show_result_pyplot', 'init_random_seed' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/apis/inference.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/apis/inference.py deleted file mode 100644 index 906943804..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/apis/inference.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import matplotlib.pyplot as plt -import mmcv -import torch -from mmcv.parallel import collate, scatter -from mmcv.runner import load_checkpoint - -from mmseg.datasets.pipelines import Compose -from mmseg.models import build_segmentor - - -def init_segmentor(config, checkpoint=None, device='cuda:0'): - """Initialize a segmentor from config file. - - Args: - config (str or :obj:`mmcv.Config`): Config file path or the config - object. - checkpoint (str, optional): Checkpoint path. If left as None, the model - will not load any weights. - device (str, optional) CPU/CUDA device option. Default 'cuda:0'. - Use 'cpu' for loading model on CPU. - Returns: - nn.Module: The constructed segmentor. - """ - if isinstance(config, str): - config = mmcv.Config.fromfile(config) - elif not isinstance(config, mmcv.Config): - raise TypeError('config must be a filename or Config object, ' - 'but got {}'.format(type(config))) - config.model.pretrained = None - config.model.train_cfg = None - model = build_segmentor(config.model, test_cfg=config.get('test_cfg')) - if checkpoint is not None: - checkpoint = load_checkpoint(model, checkpoint, map_location='cpu') - model.CLASSES = checkpoint['meta']['CLASSES'] - model.PALETTE = checkpoint['meta']['PALETTE'] - model.cfg = config # save the config in the model for convenience - model.to(device) - model.eval() - return model - - -class LoadImage: - """A simple pipeline to load image.""" - - def __call__(self, results): - """Call function to load images into results. - - Args: - results (dict): A result dict contains the file name - of the image to be read. - - Returns: - dict: ``results`` will be returned containing loaded image. - """ - - if isinstance(results['img'], str): - results['filename'] = results['img'] - results['ori_filename'] = results['img'] - else: - results['filename'] = None - results['ori_filename'] = None - img = mmcv.imread(results['img']) - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - return results - - -def inference_segmentor(model, img): - """Inference image(s) with the segmentor. - - Args: - model (nn.Module): The loaded segmentor. - imgs (str/ndarray or list[str/ndarray]): Either image files or loaded - images. - - Returns: - (list[Tensor]): The segmentation result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # build the data pipeline - test_pipeline = [LoadImage()] + cfg.data.test.pipeline[1:] - test_pipeline = Compose(test_pipeline) - # prepare data - data = dict(img=img) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if next(model.parameters()).is_cuda: - # scatter to specified GPU - data = scatter(data, [device])[0] - else: - data['img_metas'] = [i.data[0] for i in data['img_metas']] - - # forward the model - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - return result - - -def show_result_pyplot(model, - img, - result, - palette=None, - fig_size=(15, 10), - opacity=0.5, - title='', - block=True): - """Visualize the segmentation results on the image. - - Args: - model (nn.Module): The loaded segmentor. - img (str or np.ndarray): Image filename or loaded image. - result (list): The segmentation result. - palette (list[list[int]]] | None): The palette of segmentation - map. If None is given, random palette will be generated. - Default: None - fig_size (tuple): Figure size of the pyplot figure. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - title (str): The title of pyplot figure. - Default is ''. - block (bool): Whether to block the pyplot figure. - Default is True. - """ - if hasattr(model, 'module'): - model = model.module - img = model.show_result( - img, result, palette=palette, show=False, opacity=opacity) - plt.figure(figsize=fig_size) - plt.imshow(mmcv.bgr2rgb(img)) - plt.title(title) - plt.tight_layout() - plt.show(block=block) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/apis/test.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/apis/test.py deleted file mode 100644 index cc4fcc979..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/apis/test.py +++ /dev/null @@ -1,233 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import tempfile -import warnings - -import mmcv -import numpy as np -import torch -from mmcv.engine import collect_results_cpu, collect_results_gpu -from mmcv.image import tensor2imgs -from mmcv.runner import get_dist_info - - -def np2tmp(array, temp_file_name=None, tmpdir=None): - """Save ndarray to local numpy file. - - Args: - array (ndarray): Ndarray to save. - temp_file_name (str): Numpy file name. If 'temp_file_name=None', this - function will generate a file name with tempfile.NamedTemporaryFile - to save ndarray. Default: None. - tmpdir (str): Temporary directory to save Ndarray files. Default: None. - Returns: - str: The numpy file name. - """ - - if temp_file_name is None: - temp_file_name = tempfile.NamedTemporaryFile( - suffix='.npy', delete=False, dir=tmpdir).name - np.save(temp_file_name, array) - return temp_file_name - - -def single_gpu_test(model, - data_loader, - show=False, - out_dir=None, - efficient_test=False, - opacity=0.5, - pre_eval=False, - format_only=False, - format_args={}): - """Test with single GPU by progressive mode. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - show (bool): Whether show results during inference. Default: False. - out_dir (str, optional): If specified, the results will be dumped into - the directory to save output results. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - loader_indices = data_loader.batch_sampler - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - - if show or out_dir: - img_tensor = data['img'][0] - img_metas = data['img_metas'][0].data[0] - imgs = tensor2imgs(img_tensor, **img_metas[0]['img_norm_cfg']) - assert len(imgs) == len(img_metas) - - for img, img_meta in zip(imgs, img_metas): - h, w, _ = img_meta['img_shape'] - img_show = img[:h, :w, :] - - ori_h, ori_w = img_meta['ori_shape'][:-1] - img_show = mmcv.imresize(img_show, (ori_w, ori_h)) - - if out_dir: - out_file = osp.join(out_dir, img_meta['ori_filename']) - else: - out_file = None - - model.module.show_result( - img_show, - result, - palette=dataset.PALETTE, - show=show, - out_file=out_file, - opacity=opacity) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - results.extend(result) - else: - results.extend(result) - - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - - return results - - -def multi_gpu_test(model, - data_loader, - tmpdir=None, - gpu_collect=False, - efficient_test=False, - pre_eval=False, - format_only=False, - format_args={}): - """Test model with multiple gpus by progressive mode. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' - it encodes results to gpu tensors and use gpu communication for results - collection. On cpu mode it saves the results on different gpus to 'tmpdir' - and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (utils.data.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. The same path is used for efficient - test. Default: None. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Mutually exclusive with - pre_eval and format_results. Default: False. - pre_eval (bool): Use dataset.pre_eval() function to generate - pre_results for metric evaluation. Mutually exclusive with - efficient_test and format_results. Default: False. - format_only (bool): Only format result for results commit. - Mutually exclusive with pre_eval and efficient_test. - Default: False. - format_args (dict): The args for format_results. Default: {}. - - Returns: - list: list of evaluation pre-results or list of save file names. - """ - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` will be deprecated, the ' - 'evaluation is CPU memory friendly with pre_eval=True') - mmcv.mkdir_or_exist('.efficient_test') - # when none of them is set true, return segmentation results as - # a list of np.array. - assert [efficient_test, pre_eval, format_only].count(True) <= 1, \ - '``efficient_test``, ``pre_eval`` and ``format_only`` are mutually ' \ - 'exclusive, only one of them could be true .' - - model.eval() - results = [] - dataset = data_loader.dataset - # The pipeline about how the data_loader retrieval samples from dataset: - # sampler -> batch_sampler -> indices - # The indices are passed to dataset_fetcher to get data from dataset. - # data_fetcher -> collate_fn(dataset[index]) -> data_sample - # we use batch_sampler to get correct data idx - - # batch_sampler based on DistributedSampler, the indices only point to data - # samples of related machine. - loader_indices = data_loader.batch_sampler - - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - - for batch_indices, data in zip(loader_indices, data_loader): - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - - if efficient_test: - result = [np2tmp(_, tmpdir='.efficient_test') for _ in result] - - if format_only: - result = dataset.format_results( - result, indices=batch_indices, **format_args) - if pre_eval: - # TODO: adapt samples_per_gpu > 1. - # only samples_per_gpu=1 valid now - result = dataset.pre_eval(result, indices=batch_indices) - - results.extend(result) - - if rank == 0: - batch_size = len(result) * world_size - for _ in range(batch_size): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/apis/train.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/apis/train.py deleted file mode 100644 index 9be063785..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/apis/train.py +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import random -import warnings - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (HOOKS, DistSamplerSeedHook, EpochBasedRunner, - build_runner, get_dist_info) -from mmcv.utils import build_from_cfg - -from mmseg import digit_version -from mmseg.core import DistEvalHook, EvalHook, build_optimizer -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.utils import find_latest_checkpoint, get_root_logger - - -def init_random_seed(seed=None, device='cuda'): - """Initialize random seed. - - If the seed is not set, the seed will be automatically randomized, - and then broadcast to all processes to prevent some potential bugs. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is not None: - return seed - - # Make sure all ranks share the same random seed to prevent - # some potential bugs. Please refer to - # https://github.com/open-mmlab/mmdetection/issues/6339 - rank, world_size = get_dist_info() - seed = np.random.randint(2**31) - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() - - -def set_random_seed(seed, deterministic=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - """ - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - - -def train_segmentor(model, - dataset, - cfg, - distributed=False, - validate=False, - timestamp=None, - meta=None): - """Launch segmentor training.""" - logger = get_root_logger(cfg.log_level) - - # prepare data loaders - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - # The default loader config - loader_cfg = dict( - # cfg.gpus will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - seed=cfg.seed, - drop_last=True) - # The overall dataloader settings - loader_cfg.update({ - k: v - for k, v in cfg.data.items() if k not in [ - 'train', 'val', 'test', 'train_dataloader', 'val_dataloader', - 'test_dataloader' - ] - }) - - # The specific dataloader settings - train_loader_cfg = {**loader_cfg, **cfg.data.get('train_dataloader', {})} - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus - if distributed: - find_unused_parameters = cfg.get('find_unused_parameters', False) - # Sets the `find_unused_parameters` parameter in - # torch.nn.parallel.DistributedDataParallel - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - else: - if not torch.cuda.is_available(): - assert digit_version(mmcv.__version__) >= digit_version('1.4.4'), \ - 'Please use MMCV >= 1.4.4 for CPU training!' - model = MMDataParallel(model, device_ids=cfg.gpu_ids) - # build runner - optimizer = build_optimizer(model, cfg.optimizer) - - if cfg.get('runner') is None: - cfg.runner = {'type': 'IterBasedRunner', 'max_iters': cfg.total_iters} - warnings.warn( - 'config is now expected to have a `runner` section, ' - 'please set `runner` in your config.', UserWarning) - - runner = build_runner( - cfg.runner, - default_args=dict( - model=model, - batch_processor=None, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta)) - - runner.cfg = cfg - - # register hooks - runner.register_training_hooks(cfg.lr_config, cfg.optimizer_config, - cfg.checkpoint_config, cfg.log_config, - cfg.get('momentum_config', None)) - if distributed: - # when distributed training by epoch, using`DistSamplerSeedHook` to set - # the different seed to distributed sampler for each epoch, it will - # shuffle dataset at each epoch and avoid overfitting. - if isinstance(runner, EpochBasedRunner): - runner.register_hook(DistSamplerSeedHook()) - - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register eval hooks - if validate: - val_dataset = build_dataset(cfg.data.val, dict(test_mode=True)) - # The specific dataloader settings - val_loader_cfg = { - **loader_cfg, - 'samples_per_gpu': 1, - 'shuffle': False, # Not shuffle by default - **cfg.data.get('val_dataloader', {}), - } - val_dataloader = build_dataloader(val_dataset, **val_loader_cfg) - eval_cfg = cfg.get('evaluation', {}) - eval_cfg['by_epoch'] = cfg.runner['type'] != 'IterBasedRunner' - eval_hook = DistEvalHook if distributed else EvalHook - # In this PR (https://github.com/open-mmlab/mmcv/pull/1193), the - # priority of IterTimerHook has been modified from 'NORMAL' to 'LOW'. - runner.register_hook( - eval_hook(val_dataloader, **eval_cfg), priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from is None and cfg.get('auto_resume'): - resume_from = find_latest_checkpoint(cfg.work_dir) - if resume_from is not None: - cfg.resume_from = resume_from - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/__init__.py deleted file mode 100644 index 1a077d2f1..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, build_optimizer, - build_optimizer_constructor) -from .evaluation import * # noqa: F401, F403 -from .optimizers import * # noqa: F401, F403 -from .seg import * # noqa: F401, F403 -from .utils import * # noqa: F401, F403 - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/builder.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/builder.py deleted file mode 100644 index 406dd9b4b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/builder.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from mmcv.runner.optimizer import OPTIMIZER_BUILDERS as MMCV_OPTIMIZER_BUILDERS -from mmcv.utils import Registry, build_from_cfg - -OPTIMIZER_BUILDERS = Registry( - 'optimizer builder', parent=MMCV_OPTIMIZER_BUILDERS) - - -def build_optimizer_constructor(cfg): - constructor_type = cfg.get('type') - if constructor_type in OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - elif constructor_type in MMCV_OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, MMCV_OPTIMIZER_BUILDERS) - else: - raise KeyError(f'{constructor_type} is not registered ' - 'in the optimizer builder registry.') - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/__init__.py deleted file mode 100644 index 3d16d17e5..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .class_names import get_classes, get_palette -from .eval_hooks import DistEvalHook, EvalHook -from .metrics import (eval_metrics, intersect_and_union, mean_dice, - mean_fscore, mean_iou, pre_eval_to_metrics) - -__all__ = [ - 'EvalHook', 'DistEvalHook', 'mean_dice', 'mean_iou', 'mean_fscore', - 'eval_metrics', 'get_classes', 'get_palette', 'pre_eval_to_metrics', - 'intersect_and_union' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/class_names.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/class_names.py deleted file mode 100644 index e3bff6231..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/class_names.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - - -def cityscapes_classes(): - """Cityscapes class names for external use.""" - return [ - 'road', 'sidewalk', 'building', 'wall', 'fence', 'pole', - 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle' - ] - - -def ade_classes(): - """ADE20K class names for external use.""" - return [ - 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', - 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', - 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', - 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', - 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', - 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', - 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', - 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', - 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', - 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', - 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', - 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', - 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', - 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', - 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', - 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', - 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', - 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', - 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', - 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', - 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', - 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', - 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', - 'clock', 'flag' - ] - - -def voc_classes(): - """Pascal VOC class names for external use.""" - return [ - 'background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', - 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', - 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', - 'tvmonitor' - ] - - -def cocostuff_classes(): - """CocoStuff class names for external use.""" - return [ - 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', - 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', - 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', - 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', - 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', - 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', - 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', - 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', - 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', - 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', - 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', - 'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner', - 'blanket', 'branch', 'bridge', 'building-other', 'bush', 'cabinet', - 'cage', 'cardboard', 'carpet', 'ceiling-other', 'ceiling-tile', - 'cloth', 'clothes', 'clouds', 'counter', 'cupboard', 'curtain', - 'desk-stuff', 'dirt', 'door-stuff', 'fence', 'floor-marble', - 'floor-other', 'floor-stone', 'floor-tile', 'floor-wood', 'flower', - 'fog', 'food-other', 'fruit', 'furniture-other', 'grass', 'gravel', - 'ground-other', 'hill', 'house', 'leaves', 'light', 'mat', 'metal', - 'mirror-stuff', 'moss', 'mountain', 'mud', 'napkin', 'net', 'paper', - 'pavement', 'pillow', 'plant-other', 'plastic', 'platform', - 'playingfield', 'railing', 'railroad', 'river', 'road', 'rock', 'roof', - 'rug', 'salad', 'sand', 'sea', 'shelf', 'sky-other', 'skyscraper', - 'snow', 'solid-other', 'stairs', 'stone', 'straw', 'structural-other', - 'table', 'tent', 'textile-other', 'towel', 'tree', 'vegetable', - 'wall-brick', 'wall-concrete', 'wall-other', 'wall-panel', - 'wall-stone', 'wall-tile', 'wall-wood', 'water-other', 'waterdrops', - 'window-blind', 'window-other', 'wood' - ] - - -def loveda_classes(): - """LoveDA class names for external use.""" - return [ - 'background', 'building', 'road', 'water', 'barren', 'forest', - 'agricultural' - ] - - -def potsdam_classes(): - """Potsdam class names for external use.""" - return [ - 'impervious_surface', 'building', 'low_vegetation', 'tree', 'car', - 'clutter' - ] - - -def vaihingen_classes(): - """Vaihingen class names for external use.""" - return [ - 'impervious_surface', 'building', 'low_vegetation', 'tree', 'car', - 'clutter' - ] - - -def isaid_classes(): - """iSAID class names for external use.""" - return [ - 'background', 'ship', 'store_tank', 'baseball_diamond', 'tennis_court', - 'basketball_court', 'Ground_Track_Field', 'Bridge', 'Large_Vehicle', - 'Small_Vehicle', 'Helicopter', 'Swimming_pool', 'Roundabout', - 'Soccer_ball_field', 'plane', 'Harbor' - ] - - -def stare_classes(): - """stare class names for external use.""" - return ['background', 'vessel'] - - -def cityscapes_palette(): - """Cityscapes palette for external use.""" - return [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], - [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], - [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], - [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], [0, 80, 100], - [0, 0, 230], [119, 11, 32]] - - -def ade_palette(): - """ADE20K palette for external use.""" - return [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], - [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], - [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], - [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], - [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], - [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], - [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], - [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], - [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], - [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], - [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], - [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], - [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], - [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], - [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], - [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], - [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], - [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], - [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], - [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], - [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], - [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], - [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], - [102, 255, 0], [92, 0, 255]] - - -def voc_palette(): - """Pascal VOC palette for external use.""" - return [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], - [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], - [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], - [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], - [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] - - -def cocostuff_palette(): - """CocoStuff palette for external use.""" - return [[0, 192, 64], [0, 192, 64], [0, 64, 96], [128, 192, 192], - [0, 64, 64], [0, 192, 224], [0, 192, 192], [128, 192, 64], - [0, 192, 96], [128, 192, 64], [128, 32, 192], [0, 0, 224], - [0, 0, 64], [0, 160, 192], [128, 0, 96], [128, 0, 192], - [0, 32, 192], [128, 128, 224], [0, 0, 192], [128, 160, 192], - [128, 128, 0], [128, 0, 32], [128, 32, 0], [128, 0, 128], - [64, 128, 32], [0, 160, 0], [0, 0, 0], [192, 128, 160], [0, 32, 0], - [0, 128, 128], [64, 128, 160], [128, 160, 0], [0, 128, 0], - [192, 128, 32], [128, 96, 128], [0, 0, 128], [64, 0, 32], - [0, 224, 128], [128, 0, 0], [192, 0, 160], [0, 96, 128], - [128, 128, 128], [64, 0, 160], [128, 224, 128], [128, 128, 64], - [192, 0, 32], [128, 96, 0], [128, 0, 192], [0, 128, 32], - [64, 224, 0], [0, 0, 64], [128, 128, 160], [64, 96, 0], - [0, 128, 192], [0, 128, 160], [192, 224, 0], [0, 128, 64], - [128, 128, 32], [192, 32, 128], [0, 64, 192], [0, 0, 32], - [64, 160, 128], [128, 64, 64], [128, 0, 160], [64, 32, 128], - [128, 192, 192], [0, 0, 160], [192, 160, 128], [128, 192, 0], - [128, 0, 96], [192, 32, 0], [128, 64, 128], [64, 128, 96], - [64, 160, 0], [0, 64, 0], [192, 128, 224], [64, 32, 0], - [0, 192, 128], [64, 128, 224], [192, 160, 0], [0, 192, 0], - [192, 128, 96], [192, 96, 128], [0, 64, 128], [64, 0, 96], - [64, 224, 128], [128, 64, 0], [192, 0, 224], [64, 96, 128], - [128, 192, 128], [64, 0, 224], [192, 224, 128], [128, 192, 64], - [192, 0, 96], [192, 96, 0], [128, 64, 192], [0, 128, 96], - [0, 224, 0], [64, 64, 64], [128, 128, 224], [0, 96, 0], - [64, 192, 192], [0, 128, 224], [128, 224, 0], [64, 192, 64], - [128, 128, 96], [128, 32, 128], [64, 0, 192], [0, 64, 96], - [0, 160, 128], [192, 0, 64], [128, 64, 224], [0, 32, 128], - [192, 128, 192], [0, 64, 224], [128, 160, 128], [192, 128, 0], - [128, 64, 32], [128, 32, 64], [192, 0, 128], [64, 192, 32], - [0, 160, 64], [64, 0, 0], [192, 192, 160], [0, 32, 64], - [64, 128, 128], [64, 192, 160], [128, 160, 64], [64, 128, 0], - [192, 192, 32], [128, 96, 192], [64, 0, 128], [64, 64, 32], - [0, 224, 192], [192, 0, 0], [192, 64, 160], [0, 96, 192], - [192, 128, 128], [64, 64, 160], [128, 224, 192], [192, 128, 64], - [192, 64, 32], [128, 96, 64], [192, 0, 192], [0, 192, 32], - [64, 224, 64], [64, 0, 64], [128, 192, 160], [64, 96, 64], - [64, 128, 192], [0, 192, 160], [192, 224, 64], [64, 128, 64], - [128, 192, 32], [192, 32, 192], [64, 64, 192], [0, 64, 32], - [64, 160, 192], [192, 64, 64], [128, 64, 160], [64, 32, 192], - [192, 192, 192], [0, 64, 160], [192, 160, 192], [192, 192, 0], - [128, 64, 96], [192, 32, 64], [192, 64, 128], [64, 192, 96], - [64, 160, 64], [64, 64, 0]] - - -def loveda_palette(): - """LoveDA palette for external use.""" - return [[255, 255, 255], [255, 0, 0], [255, 255, 0], [0, 0, 255], - [159, 129, 183], [0, 255, 0], [255, 195, 128]] - - -def potsdam_palette(): - """Potsdam palette for external use.""" - return [[255, 255, 255], [0, 0, 255], [0, 255, 255], [0, 255, 0], - [255, 255, 0], [255, 0, 0]] - - -def vaihingen_palette(): - """Vaihingen palette for external use.""" - return [[255, 255, 255], [0, 0, 255], [0, 255, 255], [0, 255, 0], - [255, 255, 0], [255, 0, 0]] - - -def isaid_palette(): - """iSAID palette for external use.""" - return [[0, 0, 0], [0, 0, 63], [0, 63, 63], [0, 63, 0], [0, 63, 127], - [0, 63, 191], [0, 63, 255], [0, 127, 63], [0, 127, - 127], [0, 0, 127], - [0, 0, 191], [0, 0, 255], [0, 191, 127], [0, 127, 191], - [0, 127, 255], [0, 100, 155]] - - -def stare_palette(): - """STARE palette for external use.""" - return [[120, 120, 120], [6, 230, 230]] - - -dataset_aliases = { - 'cityscapes': ['cityscapes'], - 'ade': ['ade', 'ade20k'], - 'voc': ['voc', 'pascal_voc', 'voc12', 'voc12aug'], - 'loveda': ['loveda'], - 'potsdam': ['potsdam'], - 'vaihingen': ['vaihingen'], - 'cocostuff': [ - 'cocostuff', 'cocostuff10k', 'cocostuff164k', 'coco-stuff', - 'coco-stuff10k', 'coco-stuff164k', 'coco_stuff', 'coco_stuff10k', - 'coco_stuff164k' - ], - 'isaid': ['isaid', 'iSAID'], - 'stare': ['stare', 'STARE'] -} - - -def get_classes(dataset): - """Get class names of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_classes()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels - - -def get_palette(dataset): - """Get class palette (RGB) of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_palette()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/eval_hooks.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/eval_hooks.py deleted file mode 100644 index 952db3b0b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/eval_hooks.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -import torch.distributed as dist -from mmcv.runner import DistEvalHook as _DistEvalHook -from mmcv.runner import EvalHook as _EvalHook -from torch.nn.modules.batchnorm import _BatchNorm - - -class EvalHook(_EvalHook): - """Single GPU EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``single_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - if not self._should_evaluate(runner): - return - - from mmseg.apis import single_gpu_test - results = single_gpu_test( - runner.model, self.dataloader, show=False, pre_eval=self.pre_eval) - runner.log_buffer.clear() - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - if self.save_best: - self._save_ckpt(runner, key_score) - - -class DistEvalHook(_DistEvalHook): - """Distributed EvalHook, with efficient test support. - - Args: - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: False. - efficient_test (bool): Whether save the results as local numpy files to - save CPU memory during evaluation. Default: False. - pre_eval (bool): Whether to use progressive mode to evaluate model. - Default: False. - Returns: - list: The prediction results. - """ - - greater_keys = ['mIoU', 'mAcc', 'aAcc'] - - def __init__(self, - *args, - by_epoch=False, - efficient_test=False, - pre_eval=False, - **kwargs): - super().__init__(*args, by_epoch=by_epoch, **kwargs) - self.pre_eval = pre_eval - if efficient_test: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` for evaluation hook ' - 'is deprecated, the evaluation hook is CPU memory friendly ' - 'with ``pre_eval=True`` as argument for ``multi_gpu_test()`` ' - 'function') - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - if not self._should_evaluate(runner): - return - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - from mmseg.apis import multi_gpu_test - results = multi_gpu_test( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect, - pre_eval=self.pre_eval) - - runner.log_buffer.clear() - - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - - if self.save_best: - self._save_ckpt(runner, key_score) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/metrics.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/metrics.py deleted file mode 100644 index a1c0908e1..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/evaluation/metrics.py +++ /dev/null @@ -1,395 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import mmcv -import numpy as np -import torch - - -def f_score(precision, recall, beta=1): - """calculate the f-score value. - - Args: - precision (float | torch.Tensor): The precision value. - recall (float | torch.Tensor): The recall value. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - Returns: - [torch.tensor]: The f-score value. - """ - score = (1 + beta**2) * (precision * recall) / ( - (beta**2 * precision) + recall) - return score - - -def intersect_and_union(pred_label, - label, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate intersection and Union. - - Args: - pred_label (ndarray | str): Prediction segmentation map - or predict result filename. - label (ndarray | str): Ground truth segmentation map - or label filename. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. The parameter will - work only when label is str. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. The parameter will - work only when label is str. Default: False. - - Returns: - torch.Tensor: The intersection of prediction and ground truth - histogram on all classes. - torch.Tensor: The union of prediction and ground truth histogram on - all classes. - torch.Tensor: The prediction histogram on all classes. - torch.Tensor: The ground truth histogram on all classes. - """ - - if isinstance(pred_label, str): - pred_label = torch.from_numpy(np.load(pred_label)) - else: - pred_label = torch.from_numpy((pred_label)) - - if isinstance(label, str): - label = torch.from_numpy( - mmcv.imread(label, flag='unchanged', backend='pillow')) - else: - label = torch.from_numpy(label) - - if label_map is not None: - for old_id, new_id in label_map.items(): - label[label == old_id] = new_id - if reduce_zero_label: - label[label == 0] = 255 - label = label - 1 - label[label == 254] = 255 - - mask = (label != ignore_index) - pred_label = pred_label[mask] - label = label[mask] - - intersect = pred_label[pred_label == label] - area_intersect = torch.histc( - intersect.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_pred_label = torch.histc( - pred_label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_label = torch.histc( - label.float(), bins=(num_classes), min=0, max=num_classes - 1) - area_union = area_pred_label + area_label - area_intersect - return area_intersect, area_union, area_pred_label, area_label - - -def total_intersect_and_union(results, - gt_seg_maps, - num_classes, - ignore_index, - label_map=dict(), - reduce_zero_label=False): - """Calculate Total Intersection and Union. - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - ndarray: The intersection of prediction and ground truth histogram - on all classes. - ndarray: The union of prediction and ground truth histogram on all - classes. - ndarray: The prediction histogram on all classes. - ndarray: The ground truth histogram on all classes. - """ - total_area_intersect = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_union = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_pred_label = torch.zeros((num_classes, ), dtype=torch.float64) - total_area_label = torch.zeros((num_classes, ), dtype=torch.float64) - for result, gt_seg_map in zip(results, gt_seg_maps): - area_intersect, area_union, area_pred_label, area_label = \ - intersect_and_union( - result, gt_seg_map, num_classes, ignore_index, - label_map, reduce_zero_label) - total_area_intersect += area_intersect - total_area_union += area_union - total_area_pred_label += area_pred_label - total_area_label += area_label - return total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label - - -def mean_iou(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category IoU, shape (num_classes, ). - """ - iou_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mIoU'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return iou_result - - -def mean_dice(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False): - """Calculate Mean Dice (mDice) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category dice, shape (num_classes, ). - """ - - dice_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mDice'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label) - return dice_result - - -def mean_fscore(results, - gt_seg_maps, - num_classes, - ignore_index, - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate Mean Intersection and Union (mIoU) - - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str]): list of ground truth - segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - beta (int): Determines the weight of recall in the combined score. - Default: False. - - - Returns: - dict[str, float | ndarray]: Default metrics. - float: Overall accuracy on all images. - ndarray: Per category recall, shape (num_classes, ). - ndarray: Per category precision, shape (num_classes, ). - ndarray: Per category f-score, shape (num_classes, ). - """ - fscore_result = eval_metrics( - results=results, - gt_seg_maps=gt_seg_maps, - num_classes=num_classes, - ignore_index=ignore_index, - metrics=['mFscore'], - nan_to_num=nan_to_num, - label_map=label_map, - reduce_zero_label=reduce_zero_label, - beta=beta) - return fscore_result - - -def eval_metrics(results, - gt_seg_maps, - num_classes, - ignore_index, - metrics=['mIoU'], - nan_to_num=None, - label_map=dict(), - reduce_zero_label=False, - beta=1): - """Calculate evaluation metrics - Args: - results (list[ndarray] | list[str]): List of prediction segmentation - maps or list of prediction result filenames. - gt_seg_maps (list[ndarray] | list[str] | Iterables): list of ground - truth segmentation maps or list of label filenames. - num_classes (int): Number of categories. - ignore_index (int): Index that will be ignored in evaluation. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - label_map (dict): Mapping old labels to new labels. Default: dict(). - reduce_zero_label (bool): Whether ignore zero label. Default: False. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - total_area_intersect, total_area_union, total_area_pred_label, \ - total_area_label = total_intersect_and_union( - results, gt_seg_maps, num_classes, ignore_index, label_map, - reduce_zero_label) - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def pre_eval_to_metrics(pre_eval_results, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Convert pre-eval results to metrics. - - Args: - pre_eval_results (list[tuple[torch.Tensor]]): per image eval results - for computing evaluation metric - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - - # convert list of tuples to tuple of lists, e.g. - # [(A_1, B_1, C_1, D_1), ..., (A_n, B_n, C_n, D_n)] to - # ([A_1, ..., A_n], ..., [D_1, ..., D_n]) - pre_eval_results = tuple(zip(*pre_eval_results)) - assert len(pre_eval_results) == 4 - - total_area_intersect = sum(pre_eval_results[0]) - total_area_union = sum(pre_eval_results[1]) - total_area_pred_label = sum(pre_eval_results[2]) - total_area_label = sum(pre_eval_results[3]) - - ret_metrics = total_area_to_metrics(total_area_intersect, total_area_union, - total_area_pred_label, - total_area_label, metrics, nan_to_num, - beta) - - return ret_metrics - - -def total_area_to_metrics(total_area_intersect, - total_area_union, - total_area_pred_label, - total_area_label, - metrics=['mIoU'], - nan_to_num=None, - beta=1): - """Calculate evaluation metrics - Args: - total_area_intersect (ndarray): The intersection of prediction and - ground truth histogram on all classes. - total_area_union (ndarray): The union of prediction and ground truth - histogram on all classes. - total_area_pred_label (ndarray): The prediction histogram on all - classes. - total_area_label (ndarray): The ground truth histogram on all classes. - metrics (list[str] | str): Metrics to be evaluated, 'mIoU' and 'mDice'. - nan_to_num (int, optional): If specified, NaN values will be replaced - by the numbers defined by the user. Default: None. - Returns: - float: Overall accuracy on all images. - ndarray: Per category accuracy, shape (num_classes, ). - ndarray: Per category evaluation metrics, shape (num_classes, ). - """ - if isinstance(metrics, str): - metrics = [metrics] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metrics).issubset(set(allowed_metrics)): - raise KeyError('metrics {} is not supported'.format(metrics)) - - all_acc = total_area_intersect.sum() / total_area_label.sum() - ret_metrics = OrderedDict({'aAcc': all_acc}) - for metric in metrics: - if metric == 'mIoU': - iou = total_area_intersect / total_area_union - acc = total_area_intersect / total_area_label - ret_metrics['IoU'] = iou - ret_metrics['Acc'] = acc - elif metric == 'mDice': - dice = 2 * total_area_intersect / ( - total_area_pred_label + total_area_label) - acc = total_area_intersect / total_area_label - ret_metrics['Dice'] = dice - ret_metrics['Acc'] = acc - elif metric == 'mFscore': - precision = total_area_intersect / total_area_pred_label - recall = total_area_intersect / total_area_label - f_value = torch.tensor( - [f_score(x[0], x[1], beta) for x in zip(precision, recall)]) - ret_metrics['Fscore'] = f_value - ret_metrics['Precision'] = precision - ret_metrics['Recall'] = recall - - ret_metrics = { - metric: value.numpy() - for metric, value in ret_metrics.items() - } - if nan_to_num is not None: - ret_metrics = OrderedDict({ - metric: np.nan_to_num(metric_value, nan=nan_to_num) - for metric, metric_value in ret_metrics.items() - }) - return ret_metrics diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/optimizers/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/optimizers/__init__.py deleted file mode 100644 index 4fbf4ecfc..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/optimizers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .layer_decay_optimizer_constructor import ( - LayerDecayOptimizerConstructor, LearningRateDecayOptimizerConstructor) - -__all__ = [ - 'LearningRateDecayOptimizerConstructor', 'LayerDecayOptimizerConstructor' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py deleted file mode 100644 index 2b6b8ff9c..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/optimizers/layer_decay_optimizer_constructor.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import warnings - -from mmcv.runner import DefaultOptimizerConstructor, get_dist_info - -from mmseg.utils import get_root_logger -from ..builder import OPTIMIZER_BUILDERS - - -def get_layer_id_for_convnext(var_name, max_layer_id): - """Get the layer id to set the different learning rates in ``layer_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_layer_id (int): Maximum number of backbone layers. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - stage_id = int(var_name.split('.')[2]) - if stage_id == 0: - layer_id = 0 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - block_id = int(var_name.split('.')[3]) - if stage_id == 0: - layer_id = 1 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 + block_id // 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - else: - return max_layer_id + 1 - - -def get_stage_id_for_convnext(var_name, max_stage_id): - """Get the stage id to set the different learning rates in ``stage_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_stage_id (int): Maximum number of backbone layers. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - return 0 - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - return stage_id + 1 - else: - return max_stage_id - 1 - - -def get_layer_id_for_vit(var_name, max_layer_id): - """Get the layer id to set the different learning rates. - - Args: - var_name (str): The key of the model. - num_max_layer (int): Maximum number of backbone layers. - - Returns: - int: Returns the layer id of the key. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.patch_embed'): - return 0 - elif var_name.startswith('backbone.layers'): - layer_id = int(var_name.split('.')[2]) - return layer_id + 1 - else: - return max_layer_id - 1 - - -@OPTIMIZER_BUILDERS.register_module() -class LearningRateDecayOptimizerConstructor(DefaultOptimizerConstructor): - """Different learning rates are set for different layers of backbone. - - Note: Currently, this optimizer constructor is built for ConvNeXt, - BEiT and MAE. - """ - - def add_params(self, params, module, **kwargs): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - """ - logger = get_root_logger() - - parameter_groups = {} - logger.info(f'self.paramwise_cfg is {self.paramwise_cfg}') - num_layers = self.paramwise_cfg.get('num_layers') + 2 - decay_rate = self.paramwise_cfg.get('decay_rate') - decay_type = self.paramwise_cfg.get('decay_type', 'layer_wise') - logger.info('Build LearningRateDecayOptimizerConstructor ' - f'{decay_type} {decay_rate} - {num_layers}') - weight_decay = self.base_wd - for name, param in module.named_parameters(): - if not param.requires_grad: - continue # frozen weights - if len(param.shape) == 1 or name.endswith('.bias') or name in ( - 'pos_embed', 'cls_token'): - group_name = 'no_decay' - this_weight_decay = 0. - else: - group_name = 'decay' - this_weight_decay = weight_decay - if 'layer_wise' in decay_type: - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_convnext( - name, self.paramwise_cfg.get('num_layers')) - logger.info(f'set param {name} as id {layer_id}') - elif 'BEiT' in module.backbone.__class__.__name__ or \ - 'MAE' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_vit(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - elif decay_type == 'stage_wise': - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_stage_id_for_convnext(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - group_name = f'layer_{layer_id}_{group_name}' - - if group_name not in parameter_groups: - scale = decay_rate**(num_layers - layer_id - 1) - - parameter_groups[group_name] = { - 'weight_decay': this_weight_decay, - 'params': [], - 'param_names': [], - 'lr_scale': scale, - 'group_name': group_name, - 'lr': scale * self.base_lr, - } - - parameter_groups[group_name]['params'].append(param) - parameter_groups[group_name]['param_names'].append(name) - rank, _ = get_dist_info() - if rank == 0: - to_display = {} - for key in parameter_groups: - to_display[key] = { - 'param_names': parameter_groups[key]['param_names'], - 'lr_scale': parameter_groups[key]['lr_scale'], - 'lr': parameter_groups[key]['lr'], - 'weight_decay': parameter_groups[key]['weight_decay'], - } - logger.info(f'Param groups = {json.dumps(to_display, indent=2)}') - params.extend(parameter_groups.values()) - - -@OPTIMIZER_BUILDERS.register_module() -class LayerDecayOptimizerConstructor(LearningRateDecayOptimizerConstructor): - """Different learning rates are set for different layers of backbone. - - Note: Currently, this optimizer constructor is built for BEiT, - and it will be deprecated. - Please use ``LearningRateDecayOptimizerConstructor`` instead. - """ - - def __init__(self, optimizer_cfg, paramwise_cfg): - warnings.warn('DeprecationWarning: Original ' - 'LayerDecayOptimizerConstructor of BEiT ' - 'will be deprecated. Please use ' - 'LearningRateDecayOptimizerConstructor instead, ' - 'and set decay_type = layer_wise_vit in paramwise_cfg.') - paramwise_cfg.update({'decay_type': 'layer_wise_vit'}) - warnings.warn('DeprecationWarning: Layer_decay_rate will ' - 'be deleted, please use decay_rate instead.') - paramwise_cfg['decay_rate'] = paramwise_cfg.pop('layer_decay_rate') - super(LayerDecayOptimizerConstructor, - self).__init__(optimizer_cfg, paramwise_cfg) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/__init__.py deleted file mode 100644 index 5206b96be..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_pixel_sampler -from .sampler import BasePixelSampler, OHEMPixelSampler - -__all__ = ['build_pixel_sampler', 'BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/builder.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/builder.py deleted file mode 100644 index 1cecd347b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/builder.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, build_from_cfg - -PIXEL_SAMPLERS = Registry('pixel sampler') - - -def build_pixel_sampler(cfg, **default_args): - """Build pixel sampler for segmentation map.""" - return build_from_cfg(cfg, PIXEL_SAMPLERS, default_args) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/sampler/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/sampler/__init__.py deleted file mode 100644 index 5a7648564..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/sampler/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_pixel_sampler import BasePixelSampler -from .ohem_pixel_sampler import OHEMPixelSampler - -__all__ = ['BasePixelSampler', 'OHEMPixelSampler'] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py deleted file mode 100644 index 03672cd47..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/sampler/base_pixel_sampler.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BasePixelSampler(metaclass=ABCMeta): - """Base class of pixel sampler.""" - - def __init__(self, **kwargs): - pass - - @abstractmethod - def sample(self, seg_logit, seg_label): - """Placeholder for sample function.""" diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py deleted file mode 100644 index 833a28768..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/seg/sampler/ohem_pixel_sampler.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import PIXEL_SAMPLERS -from .base_pixel_sampler import BasePixelSampler - - -@PIXEL_SAMPLERS.register_module() -class OHEMPixelSampler(BasePixelSampler): - """Online Hard Example Mining Sampler for segmentation. - - Args: - context (nn.Module): The context of sampler, subclass of - :obj:`BaseDecodeHead`. - thresh (float, optional): The threshold for hard example selection. - Below which, are prediction with low confidence. If not - specified, the hard examples will be pixels of top ``min_kept`` - loss. Default: None. - min_kept (int, optional): The minimum number of predictions to keep. - Default: 100000. - """ - - def __init__(self, context, thresh=None, min_kept=100000): - super(OHEMPixelSampler, self).__init__() - self.context = context - assert min_kept > 1 - self.thresh = thresh - self.min_kept = min_kept - - def sample(self, seg_logit, seg_label): - """Sample pixels that have high loss or with low prediction confidence. - - Args: - seg_logit (torch.Tensor): segmentation logits, shape (N, C, H, W) - seg_label (torch.Tensor): segmentation label, shape (N, 1, H, W) - - Returns: - torch.Tensor: segmentation weight, shape (N, H, W) - """ - with torch.no_grad(): - assert seg_logit.shape[2:] == seg_label.shape[2:] - assert seg_label.shape[1] == 1 - seg_label = seg_label.squeeze(1).long() - batch_kept = self.min_kept * seg_label.size(0) - valid_mask = seg_label != self.context.ignore_index - seg_weight = seg_logit.new_zeros(size=seg_label.size()) - valid_seg_weight = seg_weight[valid_mask] - if self.thresh is not None: - seg_prob = F.softmax(seg_logit, dim=1) - - tmp_seg_label = seg_label.clone().unsqueeze(1) - tmp_seg_label[tmp_seg_label == self.context.ignore_index] = 0 - seg_prob = seg_prob.gather(1, tmp_seg_label).squeeze(1) - sort_prob, sort_indices = seg_prob[valid_mask].sort() - - if sort_prob.numel() > 0: - min_threshold = sort_prob[min(batch_kept, - sort_prob.numel() - 1)] - else: - min_threshold = 0.0 - threshold = max(min_threshold, self.thresh) - valid_seg_weight[seg_prob[valid_mask] < threshold] = 1. - else: - if not isinstance(self.context.loss_decode, nn.ModuleList): - losses_decode = [self.context.loss_decode] - else: - losses_decode = self.context.loss_decode - losses = 0.0 - for loss_module in losses_decode: - losses += loss_module( - seg_logit, - seg_label, - weight=None, - ignore_index=self.context.ignore_index, - reduction_override='none') - - # faster than topk according to https://github.com/pytorch/pytorch/issues/22812 # noqa - _, sort_indices = losses[valid_mask].sort(descending=True) - valid_seg_weight[sort_indices[:batch_kept]] = 1. - - seg_weight[valid_mask] = valid_seg_weight - - return seg_weight diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/utils/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/utils/__init__.py deleted file mode 100644 index 28882893a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/utils/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .dist_util import check_dist_init, sync_random_seed -from .misc import add_prefix - -__all__ = ['add_prefix', 'check_dist_init', 'sync_random_seed'] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/utils/dist_util.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/utils/dist_util.py deleted file mode 100644 index b3288519d..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/utils/dist_util.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def check_dist_init(): - return dist.is_available() and dist.is_initialized() - - -def sync_random_seed(seed=None, device='cuda'): - """Make sure different ranks share the same seed. All workers must call - this function, otherwise it will deadlock. This method is generally used in - `DistributedSampler`, because the seed should be identical across all - processes in the distributed group. - - In distributed sampling, different ranks should sample non-overlapped - data in the dataset. Therefore, this function is used to make sure that - each rank shuffles the data indices in the same order based - on the same seed. Then different ranks could use different indices - to select non-overlapped data from the same data list. - - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - - if seed is None: - seed = np.random.randint(2**31) - assert isinstance(seed, int) - - rank, world_size = get_dist_info() - - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/utils/misc.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/core/utils/misc.py deleted file mode 100644 index 282bb8d96..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/core/utils/misc.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def add_prefix(inputs, prefix): - """Add prefix for dict. - - Args: - inputs (dict): The input dict with str keys. - prefix (str): The prefix to add. - - Returns: - - dict: The dict with keys updated with ``prefix``. - """ - - outputs = dict() - for name, value in inputs.items(): - outputs[f'{prefix}.{name}'] = value - - return outputs diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/__init__.py deleted file mode 100644 index 3366f0aec..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .ade import ADE20KDataset -from .builder import DATASETS, PIPELINES, build_dataloader, build_dataset -from .cityscapes import CityscapesDataset -from .coco_stuff import COCOStuffDataset -from .custom import CustomDataset -from .dataset_wrappers import (ConcatDataset, MultiImageMixDataset, - RepeatDataset) -from .pascal_context import PascalContextDataset, PascalContextDataset59 -from .voc import PascalVOCDataset diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/ade.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/ade.py deleted file mode 100644 index db94cebd3..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/ade.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np -from PIL import Image - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class ADE20KDataset(CustomDataset): - """ADE20K dataset. - - In segmentation map annotation for ADE20K, 0 stands for background, which - is not included in 150 categories. ``reduce_zero_label`` is fixed to True. - The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is fixed to - '.png'. - """ - CLASSES = ( - 'wall', 'building', 'sky', 'floor', 'tree', 'ceiling', 'road', 'bed ', - 'windowpane', 'grass', 'cabinet', 'sidewalk', 'person', 'earth', - 'door', 'table', 'mountain', 'plant', 'curtain', 'chair', 'car', - 'water', 'painting', 'sofa', 'shelf', 'house', 'sea', 'mirror', 'rug', - 'field', 'armchair', 'seat', 'fence', 'desk', 'rock', 'wardrobe', - 'lamp', 'bathtub', 'railing', 'cushion', 'base', 'box', 'column', - 'signboard', 'chest of drawers', 'counter', 'sand', 'sink', - 'skyscraper', 'fireplace', 'refrigerator', 'grandstand', 'path', - 'stairs', 'runway', 'case', 'pool table', 'pillow', 'screen door', - 'stairway', 'river', 'bridge', 'bookcase', 'blind', 'coffee table', - 'toilet', 'flower', 'book', 'hill', 'bench', 'countertop', 'stove', - 'palm', 'kitchen island', 'computer', 'swivel chair', 'boat', 'bar', - 'arcade machine', 'hovel', 'bus', 'towel', 'light', 'truck', 'tower', - 'chandelier', 'awning', 'streetlight', 'booth', 'television receiver', - 'airplane', 'dirt track', 'apparel', 'pole', 'land', 'bannister', - 'escalator', 'ottoman', 'bottle', 'buffet', 'poster', 'stage', 'van', - 'ship', 'fountain', 'conveyer belt', 'canopy', 'washer', 'plaything', - 'swimming pool', 'stool', 'barrel', 'basket', 'waterfall', 'tent', - 'bag', 'minibike', 'cradle', 'oven', 'ball', 'food', 'step', 'tank', - 'trade name', 'microwave', 'pot', 'animal', 'bicycle', 'lake', - 'dishwasher', 'screen', 'blanket', 'sculpture', 'hood', 'sconce', - 'vase', 'traffic light', 'tray', 'ashcan', 'fan', 'pier', 'crt screen', - 'plate', 'monitor', 'bulletin board', 'shower', 'radiator', 'glass', - 'clock', 'flag') - - PALETTE = [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255], - [11, 200, 200], [255, 82, 0], [0, 255, 245], [0, 61, 255], - [0, 255, 112], [0, 255, 133], [255, 0, 0], [255, 163, 0], - [255, 102, 0], [194, 255, 0], [0, 143, 255], [51, 255, 0], - [0, 82, 255], [0, 255, 41], [0, 255, 173], [10, 0, 255], - [173, 255, 0], [0, 255, 153], [255, 92, 0], [255, 0, 255], - [255, 0, 245], [255, 0, 102], [255, 173, 0], [255, 0, 20], - [255, 184, 184], [0, 31, 255], [0, 255, 61], [0, 71, 255], - [255, 0, 204], [0, 255, 194], [0, 255, 82], [0, 10, 255], - [0, 112, 255], [51, 0, 255], [0, 194, 255], [0, 122, 255], - [0, 255, 163], [255, 153, 0], [0, 255, 10], [255, 112, 0], - [143, 255, 0], [82, 0, 255], [163, 255, 0], [255, 235, 0], - [8, 184, 170], [133, 0, 255], [0, 255, 92], [184, 0, 255], - [255, 0, 31], [0, 184, 255], [0, 214, 255], [255, 0, 112], - [92, 255, 0], [0, 224, 255], [112, 224, 255], [70, 184, 160], - [163, 0, 255], [153, 0, 255], [71, 255, 0], [255, 0, 163], - [255, 204, 0], [255, 0, 143], [0, 255, 235], [133, 255, 0], - [255, 0, 235], [245, 0, 255], [255, 0, 122], [255, 245, 0], - [10, 190, 212], [214, 255, 0], [0, 204, 255], [20, 0, 255], - [255, 255, 0], [0, 153, 255], [0, 41, 255], [0, 255, 204], - [41, 0, 255], [41, 255, 0], [173, 0, 255], [0, 245, 255], - [71, 0, 255], [122, 0, 255], [0, 255, 184], [0, 92, 255], - [184, 255, 0], [0, 133, 255], [255, 214, 0], [25, 194, 194], - [102, 255, 0], [92, 0, 255]] - - def __init__(self, **kwargs): - super(ADE20KDataset, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - reduce_zero_label=True, - **kwargs) - - def results2img(self, results, imgfile_prefix, to_label_id, indices=None): - """Write the segmentation results to images. - - Args: - results (list[ndarray]): Testing results of the - dataset. - imgfile_prefix (str): The filename prefix of the png files. - If the prefix is "somepath/xxx", - the png files will be named "somepath/xxx.png". - to_label_id (bool): whether convert output to label_id for - submission. - indices (list[int], optional): Indices of input results, if not - set, all the indices of the dataset will be used. - Default: None. - - Returns: - list[str: str]: result txt files which contains corresponding - semantic segmentation images. - """ - if indices is None: - indices = list(range(len(self))) - - mmcv.mkdir_or_exist(imgfile_prefix) - result_files = [] - for result, idx in zip(results, indices): - - filename = self.img_infos[idx]['filename'] - basename = osp.splitext(osp.basename(filename))[0] - - png_filename = osp.join(imgfile_prefix, f'{basename}.png') - - # The index range of official requirement is from 0 to 150. - # But the index range of output is from 0 to 149. - # That is because we set reduce_zero_label=True. - result = result + 1 - - output = Image.fromarray(result.astype(np.uint8)) - output.save(png_filename) - result_files.append(png_filename) - - return result_files - - def format_results(self, - results, - imgfile_prefix, - to_label_id=True, - indices=None): - """Format the results into dir (standard format for ade20k evaluation). - - Args: - results (list): Testing results of the dataset. - imgfile_prefix (str | None): The prefix of images files. It - includes the file path and the prefix of filename, e.g., - "a/b/prefix". - to_label_id (bool): whether convert output to label_id for - submission. Default: False - indices (list[int], optional): Indices of input results, if not - set, all the indices of the dataset will be used. - Default: None. - - Returns: - tuple: (result_files, tmp_dir), result_files is a list containing - the image paths, tmp_dir is the temporal directory created - for saving json/png files when img_prefix is not specified. - """ - - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - result_files = self.results2img(results, imgfile_prefix, to_label_id, - indices) - return result_files diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/builder.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/builder.py deleted file mode 100644 index 4d852d365..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/builder.py +++ /dev/null @@ -1,191 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import platform -import random -from functools import partial - -import numpy as np -import torch -from mmcv.parallel import collate -from mmcv.runner import get_dist_info -from mmcv.utils import Registry, build_from_cfg, digit_version -from torch.utils.data import DataLoader - -from .samplers import DistributedSampler - -if platform.system() != 'Windows': - # https://github.com/pytorch/pytorch/issues/973 - import resource - rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) - base_soft_limit = rlimit[0] - hard_limit = rlimit[1] - soft_limit = min(max(4096, base_soft_limit), hard_limit) - resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) - -DATASETS = Registry('dataset') -PIPELINES = Registry('pipeline') - - -def _concat_dataset(cfg, default_args=None): - """Build :obj:`ConcatDataset by.""" - from .dataset_wrappers import ConcatDataset - img_dir = cfg['img_dir'] - ann_dir = cfg.get('ann_dir', None) - split = cfg.get('split', None) - # pop 'separate_eval' since it is not a valid key for common datasets. - separate_eval = cfg.pop('separate_eval', True) - num_img_dir = len(img_dir) if isinstance(img_dir, (list, tuple)) else 1 - if ann_dir is not None: - num_ann_dir = len(ann_dir) if isinstance(ann_dir, (list, tuple)) else 1 - else: - num_ann_dir = 0 - if split is not None: - num_split = len(split) if isinstance(split, (list, tuple)) else 1 - else: - num_split = 0 - if num_img_dir > 1: - assert num_img_dir == num_ann_dir or num_ann_dir == 0 - assert num_img_dir == num_split or num_split == 0 - else: - assert num_split == num_ann_dir or num_ann_dir <= 1 - num_dset = max(num_split, num_img_dir) - - datasets = [] - for i in range(num_dset): - data_cfg = copy.deepcopy(cfg) - if isinstance(img_dir, (list, tuple)): - data_cfg['img_dir'] = img_dir[i] - if isinstance(ann_dir, (list, tuple)): - data_cfg['ann_dir'] = ann_dir[i] - if isinstance(split, (list, tuple)): - data_cfg['split'] = split[i] - datasets.append(build_dataset(data_cfg, default_args)) - - return ConcatDataset(datasets, separate_eval) - - -def build_dataset(cfg, default_args=None): - """Build datasets.""" - from .dataset_wrappers import (ConcatDataset, MultiImageMixDataset, - RepeatDataset) - if isinstance(cfg, (list, tuple)): - dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) - elif cfg['type'] == 'RepeatDataset': - dataset = RepeatDataset( - build_dataset(cfg['dataset'], default_args), cfg['times']) - elif cfg['type'] == 'MultiImageMixDataset': - cp_cfg = copy.deepcopy(cfg) - cp_cfg['dataset'] = build_dataset(cp_cfg['dataset']) - cp_cfg.pop('type') - dataset = MultiImageMixDataset(**cp_cfg) - elif isinstance(cfg.get('img_dir'), (list, tuple)) or isinstance( - cfg.get('split', None), (list, tuple)): - dataset = _concat_dataset(cfg, default_args) - else: - dataset = build_from_cfg(cfg, DATASETS, default_args) - - return dataset - - -def build_dataloader(dataset, - samples_per_gpu, - workers_per_gpu, - num_gpus=1, - dist=True, - shuffle=True, - seed=None, - drop_last=False, - pin_memory=True, - persistent_workers=True, - **kwargs): - """Build PyTorch DataLoader. - - In distributed training, each GPU/process has a dataloader. - In non-distributed training, there is only one dataloader for all GPUs. - - Args: - dataset (Dataset): A PyTorch dataset. - samples_per_gpu (int): Number of training samples on each GPU, i.e., - batch size of each GPU. - workers_per_gpu (int): How many subprocesses to use for data loading - for each GPU. - num_gpus (int): Number of GPUs. Only used in non-distributed training. - dist (bool): Distributed training/test or not. Default: True. - shuffle (bool): Whether to shuffle the data at every epoch. - Default: True. - seed (int | None): Seed to be used. Default: None. - drop_last (bool): Whether to drop the last incomplete batch in epoch. - Default: False - pin_memory (bool): Whether to use pin_memory in DataLoader. - Default: True - persistent_workers (bool): If True, the data loader will not shutdown - the worker processes after a dataset has been consumed once. - This allows to maintain the workers Dataset instances alive. - The argument also has effect in PyTorch>=1.7.0. - Default: True - kwargs: any keyword argument to be used to initialize DataLoader - - Returns: - DataLoader: A PyTorch dataloader. - """ - rank, world_size = get_dist_info() - if dist: - sampler = DistributedSampler( - dataset, world_size, rank, shuffle=shuffle, seed=seed) - shuffle = False - batch_size = samples_per_gpu - num_workers = workers_per_gpu - else: - sampler = None - batch_size = num_gpus * samples_per_gpu - num_workers = num_gpus * workers_per_gpu - - init_fn = partial( - worker_init_fn, num_workers=num_workers, rank=rank, - seed=seed) if seed is not None else None - - if digit_version(torch.__version__) >= digit_version('1.8.0'): - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - persistent_workers=persistent_workers, - **kwargs) - else: - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - **kwargs) - - return data_loader - - -def worker_init_fn(worker_id, num_workers, rank, seed): - """Worker init func for dataloader. - - The seed of each worker equals to num_worker * rank + worker_id + user_seed - - Args: - worker_id (int): Worker id. - num_workers (int): Number of workers. - rank (int): The rank of current process. - seed (int): The random seed to use. - """ - - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/cityscapes.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/cityscapes.py deleted file mode 100644 index ed633d00d..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/cityscapes.py +++ /dev/null @@ -1,214 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np -from mmcv.utils import print_log -from PIL import Image - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class CityscapesDataset(CustomDataset): - """Cityscapes dataset. - - The ``img_suffix`` is fixed to '_leftImg8bit.png' and ``seg_map_suffix`` is - fixed to '_gtFine_labelTrainIds.png' for Cityscapes dataset. - """ - - CLASSES = ('road', 'sidewalk', 'building', 'wall', 'fence', 'pole', - 'traffic light', 'traffic sign', 'vegetation', 'terrain', 'sky', - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle') - - PALETTE = [[128, 64, 128], [244, 35, 232], [70, 70, 70], [102, 102, 156], - [190, 153, 153], [153, 153, 153], [250, 170, 30], [220, 220, 0], - [107, 142, 35], [152, 251, 152], [70, 130, 180], [220, 20, 60], - [255, 0, 0], [0, 0, 142], [0, 0, 70], [0, 60, 100], - [0, 80, 100], [0, 0, 230], [119, 11, 32]] - - def __init__(self, - img_suffix='_leftImg8bit.png', - seg_map_suffix='_gtFine_labelTrainIds.png', - **kwargs): - super(CityscapesDataset, self).__init__( - img_suffix=img_suffix, seg_map_suffix=seg_map_suffix, **kwargs) - - @staticmethod - def _convert_to_label_id(result): - """Convert trainId to id for cityscapes.""" - if isinstance(result, str): - result = np.load(result) - import cityscapesscripts.helpers.labels as CSLabels - result_copy = result.copy() - for trainId, label in CSLabels.trainId2label.items(): - result_copy[result == trainId] = label.id - - return result_copy - - def results2img(self, results, imgfile_prefix, to_label_id, indices=None): - """Write the segmentation results to images. - - Args: - results (list[ndarray]): Testing results of the - dataset. - imgfile_prefix (str): The filename prefix of the png files. - If the prefix is "somepath/xxx", - the png files will be named "somepath/xxx.png". - to_label_id (bool): whether convert output to label_id for - submission. - indices (list[int], optional): Indices of input results, - if not set, all the indices of the dataset will be used. - Default: None. - - Returns: - list[str: str]: result txt files which contains corresponding - semantic segmentation images. - """ - if indices is None: - indices = list(range(len(self))) - - mmcv.mkdir_or_exist(imgfile_prefix) - result_files = [] - for result, idx in zip(results, indices): - if to_label_id: - result = self._convert_to_label_id(result) - filename = self.img_infos[idx]['filename'] - basename = osp.splitext(osp.basename(filename))[0] - - png_filename = osp.join(imgfile_prefix, f'{basename}.png') - - output = Image.fromarray(result.astype(np.uint8)).convert('P') - import cityscapesscripts.helpers.labels as CSLabels - palette = np.zeros((len(CSLabels.id2label), 3), dtype=np.uint8) - for label_id, label in CSLabels.id2label.items(): - palette[label_id] = label.color - - output.putpalette(palette) - output.save(png_filename) - result_files.append(png_filename) - - return result_files - - def format_results(self, - results, - imgfile_prefix, - to_label_id=True, - indices=None): - """Format the results into dir (standard format for Cityscapes - evaluation). - - Args: - results (list): Testing results of the dataset. - imgfile_prefix (str): The prefix of images files. It - includes the file path and the prefix of filename, e.g., - "a/b/prefix". - to_label_id (bool): whether convert output to label_id for - submission. Default: False - indices (list[int], optional): Indices of input results, - if not set, all the indices of the dataset will be used. - Default: None. - - Returns: - tuple: (result_files, tmp_dir), result_files is a list containing - the image paths, tmp_dir is the temporal directory created - for saving json/png files when img_prefix is not specified. - """ - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - result_files = self.results2img(results, imgfile_prefix, to_label_id, - indices) - - return result_files - - def evaluate(self, - results, - metric='mIoU', - logger=None, - imgfile_prefix=None): - """Evaluation in Cityscapes/default protocol. - - Args: - results (list): Testing results of the dataset. - metric (str | list[str]): Metrics to be evaluated. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - imgfile_prefix (str | None): The prefix of output image file, - for cityscapes evaluation only. It includes the file path and - the prefix of filename, e.g., "a/b/prefix". - If results are evaluated with cityscapes protocol, it would be - the prefix of output png files. The output files would be - png images under folder "a/b/prefix/xxx.png", where "xxx" is - the image name of cityscapes. If not specified, a temp file - will be created for evaluation. - Default: None. - - Returns: - dict[str, float]: Cityscapes/default metrics. - """ - - eval_results = dict() - metrics = metric.copy() if isinstance(metric, list) else [metric] - if 'cityscapes' in metrics: - eval_results.update( - self._evaluate_cityscapes(results, logger, imgfile_prefix)) - metrics.remove('cityscapes') - if len(metrics) > 0: - eval_results.update( - super(CityscapesDataset, - self).evaluate(results, metrics, logger)) - - return eval_results - - def _evaluate_cityscapes(self, results, logger, imgfile_prefix): - """Evaluation in Cityscapes protocol. - - Args: - results (list): Testing results of the dataset. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - imgfile_prefix (str | None): The prefix of output image file - - Returns: - dict[str: float]: Cityscapes evaluation results. - """ - try: - import cityscapesscripts.evaluation.evalPixelLevelSemanticLabeling as CSEval # noqa - except ImportError: - raise ImportError('Please run "pip install cityscapesscripts" to ' - 'install cityscapesscripts first.') - msg = 'Evaluating in Cityscapes style' - if logger is None: - msg = '\n' + msg - print_log(msg, logger=logger) - - result_dir = imgfile_prefix - - eval_results = dict() - print_log(f'Evaluating results under {result_dir} ...', logger=logger) - - CSEval.args.evalInstLevelScore = True - CSEval.args.predictionPath = osp.abspath(result_dir) - CSEval.args.evalPixelAccuracy = True - CSEval.args.JSONOutput = False - - seg_map_list = [] - pred_list = [] - - # when evaluating with official cityscapesscripts, - # **_gtFine_labelIds.png is used - for seg_map in mmcv.scandir( - self.ann_dir, 'gtFine_labelIds.png', recursive=True): - seg_map_list.append(osp.join(self.ann_dir, seg_map)) - pred_list.append(CSEval.getPrediction(CSEval.args, seg_map)) - - eval_results.update( - CSEval.evaluateImgLists(pred_list, seg_map_list, CSEval.args)) - - return eval_results diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/coco_stuff.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/coco_stuff.py deleted file mode 100644 index 24d089556..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/coco_stuff.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class COCOStuffDataset(CustomDataset): - """COCO-Stuff dataset. - - In segmentation map annotation for COCO-Stuff, Train-IDs of the 10k version - are from 1 to 171, where 0 is the ignore index, and Train-ID of COCO Stuff - 164k is from 0 to 170, where 255 is the ignore index. So, they are all 171 - semantic categories. ``reduce_zero_label`` is set to True and False for the - 10k and 164k versions, respectively. The ``img_suffix`` is fixed to '.jpg', - and ``seg_map_suffix`` is fixed to '.png'. - """ - CLASSES = ( - 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', - 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', - 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', - 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', - 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', - 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', - 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', - 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', - 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', - 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', - 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', - 'scissors', 'teddy bear', 'hair drier', 'toothbrush', 'banner', - 'blanket', 'branch', 'bridge', 'building-other', 'bush', 'cabinet', - 'cage', 'cardboard', 'carpet', 'ceiling-other', 'ceiling-tile', - 'cloth', 'clothes', 'clouds', 'counter', 'cupboard', 'curtain', - 'desk-stuff', 'dirt', 'door-stuff', 'fence', 'floor-marble', - 'floor-other', 'floor-stone', 'floor-tile', 'floor-wood', - 'flower', 'fog', 'food-other', 'fruit', 'furniture-other', 'grass', - 'gravel', 'ground-other', 'hill', 'house', 'leaves', 'light', 'mat', - 'metal', 'mirror-stuff', 'moss', 'mountain', 'mud', 'napkin', 'net', - 'paper', 'pavement', 'pillow', 'plant-other', 'plastic', 'platform', - 'playingfield', 'railing', 'railroad', 'river', 'road', 'rock', 'roof', - 'rug', 'salad', 'sand', 'sea', 'shelf', 'sky-other', 'skyscraper', - 'snow', 'solid-other', 'stairs', 'stone', 'straw', 'structural-other', - 'table', 'tent', 'textile-other', 'towel', 'tree', 'vegetable', - 'wall-brick', 'wall-concrete', 'wall-other', 'wall-panel', - 'wall-stone', 'wall-tile', 'wall-wood', 'water-other', 'waterdrops', - 'window-blind', 'window-other', 'wood') - - PALETTE = [[0, 192, 64], [0, 192, 64], [0, 64, 96], [128, 192, 192], - [0, 64, 64], [0, 192, 224], [0, 192, 192], [128, 192, 64], - [0, 192, 96], [128, 192, 64], [128, 32, 192], [0, 0, 224], - [0, 0, 64], [0, 160, 192], [128, 0, 96], [128, 0, 192], - [0, 32, 192], [128, 128, 224], [0, 0, 192], [128, 160, 192], - [128, 128, 0], [128, 0, 32], [128, 32, 0], [128, 0, 128], - [64, 128, 32], [0, 160, 0], [0, 0, 0], [192, 128, 160], - [0, 32, 0], [0, 128, 128], [64, 128, 160], [128, 160, 0], - [0, 128, 0], [192, 128, 32], [128, 96, 128], [0, 0, 128], - [64, 0, 32], [0, 224, 128], [128, 0, 0], [192, 0, 160], - [0, 96, 128], [128, 128, 128], [64, 0, 160], [128, 224, 128], - [128, 128, 64], [192, 0, 32], [128, 96, 0], [128, 0, 192], - [0, 128, 32], [64, 224, 0], [0, 0, 64], [128, 128, 160], - [64, 96, 0], [0, 128, 192], [0, 128, 160], [192, 224, 0], - [0, 128, 64], [128, 128, 32], [192, 32, 128], [0, 64, 192], - [0, 0, 32], [64, 160, 128], [128, 64, 64], [128, 0, 160], - [64, 32, 128], [128, 192, 192], [0, 0, 160], [192, 160, 128], - [128, 192, 0], [128, 0, 96], [192, 32, 0], [128, 64, 128], - [64, 128, 96], [64, 160, 0], [0, 64, 0], [192, 128, 224], - [64, 32, 0], [0, 192, 128], [64, 128, 224], [192, 160, 0], - [0, 192, 0], [192, 128, 96], [192, 96, 128], [0, 64, 128], - [64, 0, 96], [64, 224, 128], [128, 64, 0], [192, 0, 224], - [64, 96, 128], [128, 192, 128], [64, 0, 224], [192, 224, 128], - [128, 192, 64], [192, 0, 96], [192, 96, 0], [128, 64, 192], - [0, 128, 96], [0, 224, 0], [64, 64, 64], [128, 128, 224], - [0, 96, 0], [64, 192, 192], [0, 128, 224], [128, 224, 0], - [64, 192, 64], [128, 128, 96], [128, 32, 128], [64, 0, 192], - [0, 64, 96], [0, 160, 128], [192, 0, 64], [128, 64, 224], - [0, 32, 128], [192, 128, 192], [0, 64, 224], [128, 160, 128], - [192, 128, 0], [128, 64, 32], [128, 32, 64], [192, 0, 128], - [64, 192, 32], [0, 160, 64], [64, 0, 0], [192, 192, 160], - [0, 32, 64], [64, 128, 128], [64, 192, 160], [128, 160, 64], - [64, 128, 0], [192, 192, 32], [128, 96, 192], [64, 0, 128], - [64, 64, 32], [0, 224, 192], [192, 0, 0], [192, 64, 160], - [0, 96, 192], [192, 128, 128], [64, 64, 160], [128, 224, 192], - [192, 128, 64], [192, 64, 32], [128, 96, 64], [192, 0, 192], - [0, 192, 32], [64, 224, 64], [64, 0, 64], [128, 192, 160], - [64, 96, 64], [64, 128, 192], [0, 192, 160], [192, 224, 64], - [64, 128, 64], [128, 192, 32], [192, 32, 192], [64, 64, 192], - [0, 64, 32], [64, 160, 192], [192, 64, 64], [128, 64, 160], - [64, 32, 192], [192, 192, 192], [0, 64, 160], [192, 160, 192], - [192, 192, 0], [128, 64, 96], [192, 32, 64], [192, 64, 128], - [64, 192, 96], [64, 160, 64], [64, 64, 0]] - - def __init__(self, **kwargs): - super(COCOStuffDataset, self).__init__( - img_suffix='.jpg', seg_map_suffix='_labelTrainIds.png', **kwargs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/custom.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/custom.py deleted file mode 100644 index 4615d4114..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/custom.py +++ /dev/null @@ -1,487 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from collections import OrderedDict - -import mmcv -import numpy as np -from mmcv.utils import print_log -from prettytable import PrettyTable -from torch.utils.data import Dataset - -from mmseg.core import eval_metrics, intersect_and_union, pre_eval_to_metrics -from mmseg.utils import get_root_logger -from .builder import DATASETS -from .pipelines import Compose, LoadAnnotations - - -@DATASETS.register_module() -class CustomDataset(Dataset): - """Custom dataset for semantic segmentation. An example of file structure - is as followed. - - .. code-block:: none - - ├── data - │ ├── my_dataset - │ │ ├── img_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{img_suffix} - │ │ │ │ ├── yyy{img_suffix} - │ │ │ │ ├── zzz{img_suffix} - │ │ │ ├── val - │ │ ├── ann_dir - │ │ │ ├── train - │ │ │ │ ├── xxx{seg_map_suffix} - │ │ │ │ ├── yyy{seg_map_suffix} - │ │ │ │ ├── zzz{seg_map_suffix} - │ │ │ ├── val - - The img/gt_semantic_seg pair of CustomDataset should be of the same - except suffix. A valid img/gt_semantic_seg filename pair should be like - ``xxx{img_suffix}`` and ``xxx{seg_map_suffix}`` (extension is also included - in the suffix). If split is given, then ``xxx`` is specified in txt file. - Otherwise, all files in ``img_dir/``and ``ann_dir`` will be loaded. - Please refer to ``docs/en/tutorials/new_dataset.md`` for more details. - - - Args: - pipeline (list[dict]): Processing pipeline - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. Default: '.jpg' - ann_dir (str, optional): Path to annotation directory. Default: None - seg_map_suffix (str): Suffix of segmentation maps. Default: '.png' - split (str, optional): Split txt file. If split is specified, only - file with suffix in the splits will be loaded. Otherwise, all - images in img_dir/ann_dir will be loaded. Default: None - data_root (str, optional): Data root for img_dir/ann_dir. Default: - None. - test_mode (bool): If test_mode=True, gt wouldn't be loaded. - ignore_index (int): The label index to be ignored. Default: 255 - reduce_zero_label (bool): Whether to mark label zero as ignored. - Default: False - classes (str | Sequence[str], optional): Specify classes to load. - If is None, ``cls.CLASSES`` will be used. Default: None. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, and - self.PALETTE is None, random palette will be generated. - Default: None - gt_seg_map_loader_cfg (dict, optional): build LoadAnnotations to - load gt for evaluation, load from disk by default. Default: None. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - CLASSES = None - - PALETTE = None - - def __init__(self, - pipeline, - img_dir, - img_suffix='.jpg', - ann_dir=None, - seg_map_suffix='.png', - split=None, - data_root=None, - test_mode=False, - ignore_index=255, - reduce_zero_label=False, - classes=None, - palette=None, - gt_seg_map_loader_cfg=None, - file_client_args=dict(backend='disk')): - self.pipeline = Compose(pipeline) - self.img_dir = img_dir - self.img_suffix = img_suffix - self.ann_dir = ann_dir - self.seg_map_suffix = seg_map_suffix - self.split = split - self.data_root = data_root - self.test_mode = test_mode - self.ignore_index = ignore_index - self.reduce_zero_label = reduce_zero_label - self.label_map = None - self.CLASSES, self.PALETTE = self.get_classes_and_palette( - classes, palette) - self.gt_seg_map_loader = LoadAnnotations( - ) if gt_seg_map_loader_cfg is None else LoadAnnotations( - **gt_seg_map_loader_cfg) - - self.file_client_args = file_client_args - self.file_client = mmcv.FileClient.infer_client(self.file_client_args) - - if test_mode: - assert self.CLASSES is not None, \ - '`cls.CLASSES` or `classes` should be specified when testing' - - # join paths if data_root is specified - if self.data_root is not None: - if not osp.isabs(self.img_dir): - self.img_dir = osp.join(self.data_root, self.img_dir) - if not (self.ann_dir is None or osp.isabs(self.ann_dir)): - self.ann_dir = osp.join(self.data_root, self.ann_dir) - if not (self.split is None or osp.isabs(self.split)): - self.split = osp.join(self.data_root, self.split) - - # load annotations - self.img_infos = self.load_annotations(self.img_dir, self.img_suffix, - self.ann_dir, - self.seg_map_suffix, self.split) - - def __len__(self): - """Total number of samples of data.""" - return len(self.img_infos) - - def load_annotations(self, img_dir, img_suffix, ann_dir, seg_map_suffix, - split): - """Load annotation from directory. - - Args: - img_dir (str): Path to image directory - img_suffix (str): Suffix of images. - ann_dir (str|None): Path to annotation directory. - seg_map_suffix (str|None): Suffix of segmentation maps. - split (str|None): Split txt file. If split is specified, only file - with suffix in the splits will be loaded. Otherwise, all images - in img_dir/ann_dir will be loaded. Default: None - - Returns: - list[dict]: All image info of dataset. - """ - - img_infos = [] - if split is not None: - lines = mmcv.list_from_file( - split, file_client_args=self.file_client_args) - for line in lines: - img_name = line.strip() - img_info = dict(filename=img_name + img_suffix) - if ann_dir is not None: - seg_map = img_name + seg_map_suffix - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - else: - for img in self.file_client.list_dir_or_file( - dir_path=img_dir, - list_dir=False, - suffix=img_suffix, - recursive=True): - img_info = dict(filename=img) - if ann_dir is not None: - seg_map = img.replace(img_suffix, seg_map_suffix) - img_info['ann'] = dict(seg_map=seg_map) - img_infos.append(img_info) - img_infos = sorted(img_infos, key=lambda x: x['filename']) - - print_log(f'Loaded {len(img_infos)} images', logger=get_root_logger()) - return img_infos - - def get_ann_info(self, idx): - """Get annotation by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - return self.img_infos[idx]['ann'] - - def pre_pipeline(self, results): - """Prepare results dict for pipeline.""" - results['seg_fields'] = [] - results['img_prefix'] = self.img_dir - results['seg_prefix'] = self.ann_dir - if self.custom_classes: - results['label_map'] = self.label_map - - def __getitem__(self, idx): - """Get training/test data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training/test data (with annotation if `test_mode` is set - False). - """ - - if self.test_mode: - return self.prepare_test_img(idx) - else: - return self.prepare_train_img(idx) - - def prepare_train_img(self, idx): - """Get training data and annotations after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training data and annotation after pipeline with new keys - introduced by pipeline. - """ - - img_info = self.img_infos[idx] - ann_info = self.get_ann_info(idx) - results = dict(img_info=img_info, ann_info=ann_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def prepare_test_img(self, idx): - """Get testing data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Testing data after pipeline with new keys introduced by - pipeline. - """ - - img_info = self.img_infos[idx] - results = dict(img_info=img_info) - self.pre_pipeline(results) - return self.pipeline(results) - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """Place holder to format result to dataset specific output.""" - raise NotImplementedError - - def get_gt_seg_map_by_idx(self, index): - """Get one ground truth segmentation map for evaluation.""" - ann_info = self.get_ann_info(index) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - return results['gt_semantic_seg'] - - def get_gt_seg_maps(self, efficient_test=None): - """Get ground truth segmentation maps for evaluation.""" - if efficient_test is not None: - warnings.warn( - 'DeprecationWarning: ``efficient_test`` has been deprecated ' - 'since MMSeg v0.16, the ``get_gt_seg_maps()`` is CPU memory ' - 'friendly by default. ') - - for idx in range(len(self)): - ann_info = self.get_ann_info(idx) - results = dict(ann_info=ann_info) - self.pre_pipeline(results) - self.gt_seg_map_loader(results) - yield results['gt_semantic_seg'] - - def pre_eval(self, preds, indices): - """Collect eval result from each iteration. - - Args: - preds (list[torch.Tensor] | torch.Tensor): the segmentation logit - after argmax, shape (N, H, W). - indices (list[int] | int): the prediction related ground truth - indices. - - Returns: - list[torch.Tensor]: (area_intersect, area_union, area_prediction, - area_ground_truth). - """ - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - - pre_eval_results = [] - - for pred, index in zip(preds, indices): - seg_map = self.get_gt_seg_map_by_idx(index) - pre_eval_results.append( - intersect_and_union( - pred, - seg_map, - len(self.CLASSES), - self.ignore_index, - # as the labels has been converted when dataset initialized - # in `get_palette_for_custom_classes ` this `label_map` - # should be `dict()`, see - # https://github.com/open-mmlab/mmsegmentation/issues/1415 - # for more ditails - label_map=dict(), - reduce_zero_label=self.reduce_zero_label)) - - return pre_eval_results - - def get_classes_and_palette(self, classes=None, palette=None): - """Get class names of current dataset. - - Args: - classes (Sequence[str] | str | None): If classes is None, use - default CLASSES defined by builtin dataset. If classes is a - string, take it as a file name. The file contains the name of - classes where each line contains one class name. If classes is - a tuple or list, override the CLASSES defined by the dataset. - palette (Sequence[Sequence[int]]] | np.ndarray | None): - The palette of segmentation map. If None is given, random - palette will be generated. Default: None - """ - if classes is None: - self.custom_classes = False - return self.CLASSES, self.PALETTE - - self.custom_classes = True - if isinstance(classes, str): - # take it as a file path - class_names = mmcv.list_from_file(classes) - elif isinstance(classes, (tuple, list)): - class_names = classes - else: - raise ValueError(f'Unsupported type {type(classes)} of classes.') - - if self.CLASSES: - if not set(class_names).issubset(self.CLASSES): - raise ValueError('classes is not a subset of CLASSES.') - - # dictionary, its keys are the old label ids and its values - # are the new label ids. - # used for changing pixel labels in load_annotations. - self.label_map = {} - for i, c in enumerate(self.CLASSES): - if c not in class_names: - self.label_map[i] = -1 - else: - self.label_map[i] = class_names.index(c) - - palette = self.get_palette_for_custom_classes(class_names, palette) - - return class_names, palette - - def get_palette_for_custom_classes(self, class_names, palette=None): - - if self.label_map is not None: - # return subset of palette - palette = [] - for old_id, new_id in sorted( - self.label_map.items(), key=lambda x: x[1]): - if new_id != -1: - palette.append(self.PALETTE[old_id]) - palette = type(self.PALETTE)(palette) - - elif palette is None: - if self.PALETTE is None: - # Get random state before set seed, and restore - # random state later. - # It will prevent loss of randomness, as the palette - # may be different in each iteration if not specified. - # See: https://github.com/open-mmlab/mmdetection/issues/5844 - state = np.random.get_state() - np.random.seed(42) - # random palette - palette = np.random.randint(0, 255, size=(len(class_names), 3)) - np.random.set_state(state) - else: - palette = self.PALETTE - - return palette - - def evaluate(self, - results, - metric='mIoU', - logger=None, - gt_seg_maps=None, - **kwargs): - """Evaluate the dataset. - - Args: - results (list[tuple[torch.Tensor]] | list[str]): per image pre_eval - results or predict segmentation map for computing evaluation - metric. - metric (str | list[str]): Metrics to be evaluated. 'mIoU', - 'mDice' and 'mFscore' are supported. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - gt_seg_maps (generator[ndarray]): Custom gt seg maps as input, - used in ConcatDataset - - Returns: - dict[str, float]: Default metrics. - """ - if isinstance(metric, str): - metric = [metric] - allowed_metrics = ['mIoU', 'mDice', 'mFscore'] - if not set(metric).issubset(set(allowed_metrics)): - raise KeyError('metric {} is not supported'.format(metric)) - - eval_results = {} - # test a list of files - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - if gt_seg_maps is None: - gt_seg_maps = self.get_gt_seg_maps() - num_classes = len(self.CLASSES) - ret_metrics = eval_metrics( - results, - gt_seg_maps, - num_classes, - self.ignore_index, - metric, - label_map=dict(), - reduce_zero_label=self.reduce_zero_label) - # test a list of pre_eval_results - else: - ret_metrics = pre_eval_to_metrics(results, metric) - - # Because dataset.CLASSES is required for per-eval. - if self.CLASSES is None: - class_names = tuple(range(num_classes)) - else: - class_names = self.CLASSES - - # summary table - ret_metrics_summary = OrderedDict({ - ret_metric: np.round(np.nanmean(ret_metric_value) * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - - # each class table - ret_metrics.pop('aAcc', None) - ret_metrics_class = OrderedDict({ - ret_metric: np.round(ret_metric_value * 100, 2) - for ret_metric, ret_metric_value in ret_metrics.items() - }) - ret_metrics_class.update({'Class': class_names}) - ret_metrics_class.move_to_end('Class', last=False) - - # for logger - class_table_data = PrettyTable() - for key, val in ret_metrics_class.items(): - class_table_data.add_column(key, val) - - summary_table_data = PrettyTable() - for key, val in ret_metrics_summary.items(): - if key == 'aAcc': - summary_table_data.add_column(key, [val]) - else: - summary_table_data.add_column('m' + key, [val]) - - print_log('per class results:', logger) - print_log('\n' + class_table_data.get_string(), logger=logger) - print_log('Summary:', logger) - print_log('\n' + summary_table_data.get_string(), logger=logger) - - # each metric dict - for key, value in ret_metrics_summary.items(): - if key == 'aAcc': - eval_results[key] = value / 100.0 - else: - eval_results['m' + key] = value / 100.0 - - ret_metrics_class.pop('Class', None) - for key, value in ret_metrics_class.items(): - eval_results.update({ - key + '.' + str(name): value[idx] / 100.0 - for idx, name in enumerate(class_names) - }) - - return eval_results diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/dataset_wrappers.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/dataset_wrappers.py deleted file mode 100644 index 1fb089f9f..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/dataset_wrappers.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import bisect -import collections -import copy -from itertools import chain - -import mmcv -import numpy as np -from mmcv.utils import build_from_cfg, print_log -from torch.utils.data.dataset import ConcatDataset as _ConcatDataset - -from .builder import DATASETS, PIPELINES -from .cityscapes import CityscapesDataset - - -@DATASETS.register_module() -class ConcatDataset(_ConcatDataset): - """A wrapper of concatenated dataset. - - Same as :obj:`torch.utils.data.dataset.ConcatDataset`, but - support evaluation and formatting results - - Args: - datasets (list[:obj:`Dataset`]): A list of datasets. - separate_eval (bool): Whether to evaluate the concatenated - dataset results separately, Defaults to True. - """ - - def __init__(self, datasets, separate_eval=True): - super(ConcatDataset, self).__init__(datasets) - self.CLASSES = datasets[0].CLASSES - self.PALETTE = datasets[0].PALETTE - self.separate_eval = separate_eval - assert separate_eval in [True, False], \ - f'separate_eval can only be True or False,' \ - f'but get {separate_eval}' - if any([isinstance(ds, CityscapesDataset) for ds in datasets]): - raise NotImplementedError( - 'Evaluating ConcatDataset containing CityscapesDataset' - 'is not supported!') - - def evaluate(self, results, logger=None, **kwargs): - """Evaluate the results. - - Args: - results (list[tuple[torch.Tensor]] | list[str]]): per image - pre_eval results or predict segmentation map for - computing evaluation metric. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - - Returns: - dict[str: float]: evaluate results of the total dataset - or each separate - dataset if `self.separate_eval=True`. - """ - assert len(results) == self.cumulative_sizes[-1], \ - ('Dataset and results have different sizes: ' - f'{self.cumulative_sizes[-1]} v.s. {len(results)}') - - # Check whether all the datasets support evaluation - for dataset in self.datasets: - assert hasattr(dataset, 'evaluate'), \ - f'{type(dataset)} does not implement evaluate function' - - if self.separate_eval: - dataset_idx = -1 - total_eval_results = dict() - for size, dataset in zip(self.cumulative_sizes, self.datasets): - start_idx = 0 if dataset_idx == -1 else \ - self.cumulative_sizes[dataset_idx] - end_idx = self.cumulative_sizes[dataset_idx + 1] - - results_per_dataset = results[start_idx:end_idx] - print_log( - f'\nEvaluateing {dataset.img_dir} with ' - f'{len(results_per_dataset)} images now', - logger=logger) - - eval_results_per_dataset = dataset.evaluate( - results_per_dataset, logger=logger, **kwargs) - dataset_idx += 1 - for k, v in eval_results_per_dataset.items(): - total_eval_results.update({f'{dataset_idx}_{k}': v}) - - return total_eval_results - - if len(set([type(ds) for ds in self.datasets])) != 1: - raise NotImplementedError( - 'All the datasets should have same types when ' - 'self.separate_eval=False') - else: - if mmcv.is_list_of(results, np.ndarray) or mmcv.is_list_of( - results, str): - # merge the generators of gt_seg_maps - gt_seg_maps = chain( - *[dataset.get_gt_seg_maps() for dataset in self.datasets]) - else: - # if the results are `pre_eval` results, - # we do not need gt_seg_maps to evaluate - gt_seg_maps = None - eval_results = self.datasets[0].evaluate( - results, gt_seg_maps=gt_seg_maps, logger=logger, **kwargs) - return eval_results - - def get_dataset_idx_and_sample_idx(self, indice): - """Return dataset and sample index when given an indice of - ConcatDataset. - - Args: - indice (int): indice of sample in ConcatDataset - - Returns: - int: the index of sub dataset the sample belong to - int: the index of sample in its corresponding subset - """ - if indice < 0: - if -indice > len(self): - raise ValueError( - 'absolute value of index should not exceed dataset length') - indice = len(self) + indice - dataset_idx = bisect.bisect_right(self.cumulative_sizes, indice) - if dataset_idx == 0: - sample_idx = indice - else: - sample_idx = indice - self.cumulative_sizes[dataset_idx - 1] - return dataset_idx, sample_idx - - def format_results(self, results, imgfile_prefix, indices=None, **kwargs): - """format result for every sample of ConcatDataset.""" - if indices is None: - indices = list(range(len(self))) - - assert isinstance(results, list), 'results must be a list.' - assert isinstance(indices, list), 'indices must be a list.' - - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].format_results( - [results[i]], - imgfile_prefix + f'/{dataset_idx}', - indices=[sample_idx], - **kwargs) - ret_res.append(res) - return sum(ret_res, []) - - def pre_eval(self, preds, indices): - """do pre eval for every sample of ConcatDataset.""" - # In order to compat with batch inference - if not isinstance(indices, list): - indices = [indices] - if not isinstance(preds, list): - preds = [preds] - ret_res = [] - for i, indice in enumerate(indices): - dataset_idx, sample_idx = self.get_dataset_idx_and_sample_idx( - indice) - res = self.datasets[dataset_idx].pre_eval(preds[i], sample_idx) - ret_res.append(res) - return sum(ret_res, []) - - -@DATASETS.register_module() -class RepeatDataset(object): - """A wrapper of repeated dataset. - - The length of repeated dataset will be `times` larger than the original - dataset. This is useful when the data loading time is long but the dataset - is small. Using RepeatDataset can reduce the data loading time between - epochs. - - Args: - dataset (:obj:`Dataset`): The dataset to be repeated. - times (int): Repeat times. - """ - - def __init__(self, dataset, times): - self.dataset = dataset - self.times = times - self.CLASSES = dataset.CLASSES - self.PALETTE = dataset.PALETTE - self._ori_len = len(self.dataset) - - def __getitem__(self, idx): - """Get item from original dataset.""" - return self.dataset[idx % self._ori_len] - - def __len__(self): - """The length is multiplied by ``times``""" - return self.times * self._ori_len - - -@DATASETS.register_module() -class MultiImageMixDataset: - """A wrapper of multiple images mixed dataset. - - Suitable for training on multiple images mixed data augmentation like - mosaic and mixup. For the augmentation pipeline of mixed image data, - the `get_indexes` method needs to be provided to obtain the image - indexes, and you can set `skip_flags` to change the pipeline running - process. - - - Args: - dataset (:obj:`CustomDataset`): The dataset to be mixed. - pipeline (Sequence[dict]): Sequence of transform object or - config dict to be composed. - skip_type_keys (list[str], optional): Sequence of type string to - be skip pipeline. Default to None. - """ - - def __init__(self, dataset, pipeline, skip_type_keys=None): - assert isinstance(pipeline, collections.abc.Sequence) - if skip_type_keys is not None: - assert all([ - isinstance(skip_type_key, str) - for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys - - self.pipeline = [] - self.pipeline_types = [] - for transform in pipeline: - if isinstance(transform, dict): - self.pipeline_types.append(transform['type']) - transform = build_from_cfg(transform, PIPELINES) - self.pipeline.append(transform) - else: - raise TypeError('pipeline must be a dict') - - self.dataset = dataset - self.CLASSES = dataset.CLASSES - self.PALETTE = dataset.PALETTE - self.num_samples = len(dataset) - - def __len__(self): - return self.num_samples - - def __getitem__(self, idx): - results = copy.deepcopy(self.dataset[idx]) - for (transform, transform_type) in zip(self.pipeline, - self.pipeline_types): - if self._skip_type_keys is not None and \ - transform_type in self._skip_type_keys: - continue - - if hasattr(transform, 'get_indexes'): - indexes = transform.get_indexes(self.dataset) - if not isinstance(indexes, collections.abc.Sequence): - indexes = [indexes] - mix_results = [ - copy.deepcopy(self.dataset[index]) for index in indexes - ] - results['mix_results'] = mix_results - - results = transform(results) - - if 'mix_results' in results: - results.pop('mix_results') - - return results - - def update_skip_type_keys(self, skip_type_keys): - """Update skip_type_keys. - - It is called by an external hook. - - Args: - skip_type_keys (list[str], optional): Sequence of type - string to be skip pipeline. - """ - assert all([ - isinstance(skip_type_key, str) for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pascal_context.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pascal_context.py deleted file mode 100644 index efacee0f3..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pascal_context.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class PascalContextDataset(CustomDataset): - """PascalContext dataset. - - In segmentation map annotation for PascalContext, 0 stands for background, - which is included in 60 categories. ``reduce_zero_label`` is fixed to - False. The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is - fixed to '.png'. - - Args: - split (str): Split txt file for PascalContext. - """ - - CLASSES = ('background', 'aeroplane', 'bag', 'bed', 'bedclothes', 'bench', - 'bicycle', 'bird', 'boat', 'book', 'bottle', 'building', 'bus', - 'cabinet', 'car', 'cat', 'ceiling', 'chair', 'cloth', - 'computer', 'cow', 'cup', 'curtain', 'dog', 'door', 'fence', - 'floor', 'flower', 'food', 'grass', 'ground', 'horse', - 'keyboard', 'light', 'motorbike', 'mountain', 'mouse', 'person', - 'plate', 'platform', 'pottedplant', 'road', 'rock', 'sheep', - 'shelves', 'sidewalk', 'sign', 'sky', 'snow', 'sofa', 'table', - 'track', 'train', 'tree', 'truck', 'tvmonitor', 'wall', 'water', - 'window', 'wood') - - PALETTE = [[120, 120, 120], [180, 120, 120], [6, 230, 230], [80, 50, 50], - [4, 200, 3], [120, 120, 80], [140, 140, 140], [204, 5, 255], - [230, 230, 230], [4, 250, 7], [224, 5, 255], [235, 255, 7], - [150, 5, 61], [120, 120, 70], [8, 255, 51], [255, 6, 82], - [143, 255, 140], [204, 255, 4], [255, 51, 7], [204, 70, 3], - [0, 102, 200], [61, 230, 250], [255, 6, 51], [11, 102, 255], - [255, 7, 71], [255, 9, 224], [9, 7, 230], [220, 220, 220], - [255, 9, 92], [112, 9, 255], [8, 255, 214], [7, 255, 224], - [255, 184, 6], [10, 255, 71], [255, 41, 10], [7, 255, 255], - [224, 255, 8], [102, 8, 255], [255, 61, 6], [255, 194, 7], - [255, 122, 8], [0, 255, 20], [255, 8, 41], [255, 5, 153], - [6, 51, 255], [235, 12, 255], [160, 150, 20], [0, 163, 255], - [140, 140, 140], [250, 10, 15], [20, 255, 0], [31, 255, 0], - [255, 31, 0], [255, 224, 0], [153, 255, 0], [0, 0, 255], - [255, 71, 0], [0, 235, 255], [0, 173, 255], [31, 0, 255]] - - def __init__(self, split, **kwargs): - super(PascalContextDataset, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - split=split, - reduce_zero_label=False, - **kwargs) - assert self.file_client.exists(self.img_dir) and self.split is not None - - -@DATASETS.register_module() -class PascalContextDataset59(CustomDataset): - """PascalContext dataset. - - In segmentation map annotation for PascalContext, 0 stands for background, - which is included in 60 categories. ``reduce_zero_label`` is fixed to - False. The ``img_suffix`` is fixed to '.jpg' and ``seg_map_suffix`` is - fixed to '.png'. - - Args: - split (str): Split txt file for PascalContext. - """ - - CLASSES = ('aeroplane', 'bag', 'bed', 'bedclothes', 'bench', 'bicycle', - 'bird', 'boat', 'book', 'bottle', 'building', 'bus', 'cabinet', - 'car', 'cat', 'ceiling', 'chair', 'cloth', 'computer', 'cow', - 'cup', 'curtain', 'dog', 'door', 'fence', 'floor', 'flower', - 'food', 'grass', 'ground', 'horse', 'keyboard', 'light', - 'motorbike', 'mountain', 'mouse', 'person', 'plate', 'platform', - 'pottedplant', 'road', 'rock', 'sheep', 'shelves', 'sidewalk', - 'sign', 'sky', 'snow', 'sofa', 'table', 'track', 'train', - 'tree', 'truck', 'tvmonitor', 'wall', 'water', 'window', 'wood') - - PALETTE = [[180, 120, 120], [6, 230, 230], [80, 50, 50], [4, 200, 3], - [120, 120, 80], [140, 140, 140], [204, 5, 255], [230, 230, 230], - [4, 250, 7], [224, 5, 255], [235, 255, 7], [150, 5, 61], - [120, 120, 70], [8, 255, 51], [255, 6, 82], [143, 255, 140], - [204, 255, 4], [255, 51, 7], [204, 70, 3], [0, 102, 200], - [61, 230, 250], [255, 6, 51], [11, 102, 255], [255, 7, 71], - [255, 9, 224], [9, 7, 230], [220, 220, 220], [255, 9, 92], - [112, 9, 255], [8, 255, 214], [7, 255, 224], [255, 184, 6], - [10, 255, 71], [255, 41, 10], [7, 255, 255], [224, 255, 8], - [102, 8, 255], [255, 61, 6], [255, 194, 7], [255, 122, 8], - [0, 255, 20], [255, 8, 41], [255, 5, 153], [6, 51, 255], - [235, 12, 255], [160, 150, 20], [0, 163, 255], [140, 140, 140], - [250, 10, 15], [20, 255, 0], [31, 255, 0], [255, 31, 0], - [255, 224, 0], [153, 255, 0], [0, 0, 255], [255, 71, 0], - [0, 235, 255], [0, 173, 255], [31, 0, 255]] - - def __init__(self, split, **kwargs): - super(PascalContextDataset59, self).__init__( - img_suffix='.jpg', - seg_map_suffix='.png', - split=split, - reduce_zero_label=True, - **kwargs) - assert self.file_client.exists(self.img_dir) and self.split is not None diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/__init__.py deleted file mode 100644 index 8256a6fe2..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .compose import Compose -from .formatting import (Collect, ImageToTensor, ToDataContainer, ToTensor, - Transpose, to_tensor) -from .loading import LoadAnnotations, LoadImageFromFile -from .test_time_aug import MultiScaleFlipAug -from .transforms import (CLAHE, AdjustGamma, Normalize, Pad, - PhotoMetricDistortion, RandomCrop, RandomCutOut, - RandomFlip, RandomMosaic, RandomRotate, Rerange, - Resize, RGB2Gray, SegRescale) - -__all__ = [ - 'Compose', 'to_tensor', 'ToTensor', 'ImageToTensor', 'ToDataContainer', - 'Transpose', 'Collect', 'LoadAnnotations', 'LoadImageFromFile', - 'MultiScaleFlipAug', 'Resize', 'RandomFlip', 'Pad', 'RandomCrop', - 'Normalize', 'SegRescale', 'PhotoMetricDistortion', 'RandomRotate', - 'AdjustGamma', 'CLAHE', 'Rerange', 'RGB2Gray', 'RandomCutOut', - 'RandomMosaic' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/compose.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/compose.py deleted file mode 100644 index 30280c133..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/compose.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections - -from mmcv.utils import build_from_cfg - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class Compose(object): - """Compose multiple transforms sequentially. - - Args: - transforms (Sequence[dict | callable]): Sequence of transform object or - config dict to be composed. - """ - - def __init__(self, transforms): - assert isinstance(transforms, collections.abc.Sequence) - self.transforms = [] - for transform in transforms: - if isinstance(transform, dict): - transform = build_from_cfg(transform, PIPELINES) - self.transforms.append(transform) - elif callable(transform): - self.transforms.append(transform) - else: - raise TypeError('transform must be callable or a dict') - - def __call__(self, data): - """Call function to apply transforms sequentially. - - Args: - data (dict): A result dict contains the data to transform. - - Returns: - dict: Transformed data. - """ - - for t in self.transforms: - data = t(data) - if data is None: - return None - return data - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - format_string += '\n' - format_string += f' {t}' - format_string += '\n)' - return format_string diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/formating.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/formating.py deleted file mode 100644 index f6e53bfeb..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/formating.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -import warnings - -from .formatting import * - -warnings.warn('DeprecationWarning: mmseg.datasets.pipelines.formating will be ' - 'deprecated in 2021, please replace it with ' - 'mmseg.datasets.pipelines.formatting.') diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/formatting.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/formatting.py deleted file mode 100644 index 4e057c1b8..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/formatting.py +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import mmcv -import numpy as np -import torch -from mmcv.parallel import DataContainer as DC - -from ..builder import PIPELINES - - -def to_tensor(data): - """Convert objects of various python types to :obj:`torch.Tensor`. - - Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, - :class:`Sequence`, :class:`int` and :class:`float`. - - Args: - data (torch.Tensor | numpy.ndarray | Sequence | int | float): Data to - be converted. - """ - - if isinstance(data, torch.Tensor): - return data - elif isinstance(data, np.ndarray): - return torch.from_numpy(data) - elif isinstance(data, Sequence) and not mmcv.is_str(data): - return torch.tensor(data) - elif isinstance(data, int): - return torch.LongTensor([data]) - elif isinstance(data, float): - return torch.FloatTensor([data]) - else: - raise TypeError(f'type {type(data)} cannot be converted to tensor.') - - -@PIPELINES.register_module() -class ToTensor(object): - """Convert some results to :obj:`torch.Tensor` by given keys. - - Args: - keys (Sequence[str]): Keys that need to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert data in results to :obj:`torch.Tensor`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted - to :obj:`torch.Tensor`. - """ - - for key in self.keys: - results[key] = to_tensor(results[key]) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class ImageToTensor(object): - """Convert image to :obj:`torch.Tensor` by given keys. - - The dimension order of input image is (H, W, C). The pipeline will convert - it to (C, H, W). If only 2 dimension (H, W) is given, the output would be - (1, H, W). - - Args: - keys (Sequence[str]): Key of images to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - img = results[key] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - results[key] = to_tensor(img.transpose(2, 0, 1)) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class Transpose(object): - """Transpose some results by given keys. - - Args: - keys (Sequence[str]): Keys of results to be transposed. - order (Sequence[int]): Order of transpose. - """ - - def __init__(self, keys, order): - self.keys = keys - self.order = order - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - - for key in self.keys: - results[key] = results[key].transpose(self.order) - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, order={self.order})' - - -@PIPELINES.register_module() -class ToDataContainer(object): - """Convert results to :obj:`mmcv.DataContainer` by given fields. - - Args: - fields (Sequence[dict]): Each field is a dict like - ``dict(key='xxx', **kwargs)``. The ``key`` in result will - be converted to :obj:`mmcv.DataContainer` with ``**kwargs``. - Default: ``(dict(key='img', stack=True), - dict(key='gt_semantic_seg'))``. - """ - - def __init__(self, - fields=(dict(key='img', - stack=True), dict(key='gt_semantic_seg'))): - self.fields = fields - - def __call__(self, results): - """Call function to convert data in results to - :obj:`mmcv.DataContainer`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted to - :obj:`mmcv.DataContainer`. - """ - - for field in self.fields: - field = field.copy() - key = field.pop('key') - results[key] = DC(results[key], **field) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(fields={self.fields})' - - -@PIPELINES.register_module() -class DefaultFormatBundle(object): - """Default formatting bundle. - - It simplifies the pipeline of formatting common fields, including "img" - and "gt_semantic_seg". These fields are formatted as follows. - - - img: (1)transpose, (2)to tensor, (3)to DataContainer (stack=True) - - gt_semantic_seg: (1)unsqueeze dim-0 (2)to tensor, - (3)to DataContainer (stack=True) - """ - - def __call__(self, results): - """Call function to transform and format common fields in results. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data that is formatted with - default bundle. - """ - - if 'img' in results: - img = results['img'] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - img = np.ascontiguousarray(img.transpose(2, 0, 1)) - results['img'] = DC(to_tensor(img), stack=True) - if 'gt_semantic_seg' in results: - # convert to long - results['gt_semantic_seg'] = DC( - to_tensor(results['gt_semantic_seg'][None, - ...].astype(np.int64)), - stack=True) - return results - - def __repr__(self): - return self.__class__.__name__ - - -@PIPELINES.register_module() -class Collect(object): - """Collect data from the loader relevant to the specific task. - - This is usually the last stage of the data loader pipeline. Typically keys - is set to some subset of "img", "gt_semantic_seg". - - The "img_meta" item is always populated. The contents of the "img_meta" - dictionary depends on "meta_keys". By default this includes: - - - "img_shape": shape of the image input to the network as a tuple - (h, w, c). Note that images may be zero padded on the bottom/right - if the batch tensor is larger than this shape. - - - "scale_factor": a float indicating the preprocessing scale - - - "flip": a boolean indicating if image flip transform was used - - - "filename": path to the image file - - - "ori_shape": original shape of the image as a tuple (h, w, c) - - - "pad_shape": image shape after padding - - - "img_norm_cfg": a dict of normalization information: - - mean - per channel mean subtraction - - std - per channel std divisor - - to_rgb - bool indicating if bgr was converted to rgb - - Args: - keys (Sequence[str]): Keys of results to be collected in ``data``. - meta_keys (Sequence[str], optional): Meta keys to be converted to - ``mmcv.DataContainer`` and collected in ``data[img_metas]``. - Default: (``filename``, ``ori_filename``, ``ori_shape``, - ``img_shape``, ``pad_shape``, ``scale_factor``, ``flip``, - ``flip_direction``, ``img_norm_cfg``) - """ - - def __init__(self, - keys, - meta_keys=('filename', 'ori_filename', 'ori_shape', - 'img_shape', 'pad_shape', 'scale_factor', 'flip', - 'flip_direction', 'img_norm_cfg')): - self.keys = keys - self.meta_keys = meta_keys - - def __call__(self, results): - """Call function to collect keys in results. The keys in ``meta_keys`` - will be converted to :obj:mmcv.DataContainer. - - Args: - results (dict): Result dict contains the data to collect. - - Returns: - dict: The result dict contains the following keys - - keys in``self.keys`` - - ``img_metas`` - """ - - data = {} - img_meta = {} - for key in self.meta_keys: - img_meta[key] = results[key] - data['img_metas'] = DC(img_meta, cpu_only=True) - for key in self.keys: - data[key] = results[key] - return data - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, meta_keys={self.meta_keys})' diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/loading.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/loading.py deleted file mode 100644 index 572e43431..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/loading.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class LoadImageFromFile(object): - """Load an image from file. - - Required keys are "img_prefix" and "img_info" (a dict that must contain the - key "filename"). Added or updated keys are "filename", "img", "img_shape", - "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`), - "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1). - - Args: - to_float32 (bool): Whether to convert the loaded image to a float32 - numpy array. If set to False, the loaded image is an uint8 array. - Defaults to False. - color_type (str): The flag argument for :func:`mmcv.imfrombytes`. - Defaults to 'color'. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'cv2' - """ - - def __init__(self, - to_float32=False, - color_type='color', - file_client_args=dict(backend='disk'), - imdecode_backend='cv2'): - self.to_float32 = to_float32 - self.color_type = color_type - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call functions to load image and get image meta information. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded image and meta information. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('img_prefix') is not None: - filename = osp.join(results['img_prefix'], - results['img_info']['filename']) - else: - filename = results['img_info']['filename'] - img_bytes = self.file_client.get(filename) - img = mmcv.imfrombytes( - img_bytes, flag=self.color_type, backend=self.imdecode_backend) - if self.to_float32: - img = img.astype(np.float32) - - results['filename'] = filename - results['ori_filename'] = results['img_info']['filename'] - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - # Set initial values for default meta_keys - results['pad_shape'] = img.shape - results['scale_factor'] = 1.0 - num_channels = 1 if len(img.shape) < 3 else img.shape[2] - results['img_norm_cfg'] = dict( - mean=np.zeros(num_channels, dtype=np.float32), - std=np.ones(num_channels, dtype=np.float32), - to_rgb=False) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(to_float32={self.to_float32},' - repr_str += f"color_type='{self.color_type}'," - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str - - -@PIPELINES.register_module() -class LoadAnnotations(object): - """Load annotations for semantic segmentation. - - Args: - reduce_zero_label (bool): Whether reduce all label value by 1. - Usually used for datasets where 0 is background label. - Default: False. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - imdecode_backend (str): Backend for :func:`mmcv.imdecode`. Default: - 'pillow' - """ - - def __init__(self, - reduce_zero_label=False, - file_client_args=dict(backend='disk'), - imdecode_backend='pillow'): - self.reduce_zero_label = reduce_zero_label - self.file_client_args = file_client_args.copy() - self.file_client = None - self.imdecode_backend = imdecode_backend - - def __call__(self, results): - """Call function to load multiple types annotations. - - Args: - results (dict): Result dict from :obj:`mmseg.CustomDataset`. - - Returns: - dict: The dict contains loaded semantic segmentation annotations. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results.get('seg_prefix', None) is not None: - filename = osp.join(results['seg_prefix'], - results['ann_info']['seg_map']) - else: - filename = results['ann_info']['seg_map'] - img_bytes = self.file_client.get(filename) - gt_semantic_seg = mmcv.imfrombytes( - img_bytes, flag='unchanged', - backend=self.imdecode_backend).squeeze().astype(np.uint8) - # modify if custom classes - if results.get('label_map', None) is not None: - # Add deep copy to solve bug of repeatedly - # replace `gt_semantic_seg`, which is reported in - # https://github.com/open-mmlab/mmsegmentation/pull/1445/ - gt_semantic_seg_copy = gt_semantic_seg.copy() - for old_id, new_id in results['label_map'].items(): - gt_semantic_seg[gt_semantic_seg_copy == old_id] = new_id - # reduce zero_label - if self.reduce_zero_label: - # avoid using underflow conversion - gt_semantic_seg[gt_semantic_seg == 0] = 255 - gt_semantic_seg = gt_semantic_seg - 1 - gt_semantic_seg[gt_semantic_seg == 254] = 255 - results['gt_semantic_seg'] = gt_semantic_seg - results['seg_fields'].append('gt_semantic_seg') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(reduce_zero_label={self.reduce_zero_label},' - repr_str += f"imdecode_backend='{self.imdecode_backend}')" - return repr_str diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/test_time_aug.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/test_time_aug.py deleted file mode 100644 index 5c17cbbba..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/test_time_aug.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv - -from ..builder import PIPELINES -from .compose import Compose - - -@PIPELINES.register_module() -class MultiScaleFlipAug(object): - """Test-time augmentation with multiple scales and flipping. - - An example configuration is as followed: - - .. code-block:: - - img_scale=(2048, 1024), - img_ratios=[0.5, 1.0], - flip=True, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ] - - After MultiScaleFLipAug with above configuration, the results are wrapped - into lists of the same length as followed: - - .. code-block:: - - dict( - img=[...], - img_shape=[...], - scale=[(1024, 512), (1024, 512), (2048, 1024), (2048, 1024)] - flip=[False, True, False, True] - ... - ) - - Args: - transforms (list[dict]): Transforms to apply in each augmentation. - img_scale (None | tuple | list[tuple]): Images scales for resizing. - img_ratios (float | list[float]): Image ratios for resizing - flip (bool): Whether apply flip augmentation. Default: False. - flip_direction (str | list[str]): Flip augmentation directions, - options are "horizontal" and "vertical". If flip_direction is list, - multiple flip augmentations will be applied. - It has no effect when flip == False. Default: "horizontal". - """ - - def __init__(self, - transforms, - img_scale, - img_ratios=None, - flip=False, - flip_direction='horizontal'): - self.transforms = Compose(transforms) - if img_ratios is not None: - img_ratios = img_ratios if isinstance(img_ratios, - list) else [img_ratios] - assert mmcv.is_list_of(img_ratios, float) - if img_scale is None: - # mode 1: given img_scale=None and a range of image ratio - self.img_scale = None - assert mmcv.is_list_of(img_ratios, float) - elif isinstance(img_scale, tuple) and mmcv.is_list_of( - img_ratios, float): - assert len(img_scale) == 2 - # mode 2: given a scale and a range of image ratio - self.img_scale = [(int(img_scale[0] * ratio), - int(img_scale[1] * ratio)) - for ratio in img_ratios] - else: - # mode 3: given multiple scales - self.img_scale = img_scale if isinstance(img_scale, - list) else [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) or self.img_scale is None - self.flip = flip - self.img_ratios = img_ratios - self.flip_direction = flip_direction if isinstance( - flip_direction, list) else [flip_direction] - assert mmcv.is_list_of(self.flip_direction, str) - if not self.flip and self.flip_direction != ['horizontal']: - warnings.warn( - 'flip_direction has no effect when flip is set to False') - if (self.flip - and not any([t['type'] == 'RandomFlip' for t in transforms])): - warnings.warn( - 'flip has no effect when RandomFlip is not in transforms') - - def __call__(self, results): - """Call function to apply test time augment transforms on results. - - Args: - results (dict): Result dict contains the data to transform. - - Returns: - dict[str: list]: The augmented data, where each value is wrapped - into a list. - """ - - aug_data = [] - if self.img_scale is None and mmcv.is_list_of(self.img_ratios, float): - h, w = results['img'].shape[:2] - img_scale = [(int(w * ratio), int(h * ratio)) - for ratio in self.img_ratios] - else: - img_scale = self.img_scale - flip_aug = [False, True] if self.flip else [False] - for scale in img_scale: - for flip in flip_aug: - for direction in self.flip_direction: - _results = results.copy() - _results['scale'] = scale - _results['flip'] = flip - _results['flip_direction'] = direction - data = self.transforms(_results) - aug_data.append(data) - # list of dict to dict of list - aug_data_dict = {key: [] for key in aug_data[0]} - for data in aug_data: - for key, val in data.items(): - aug_data_dict[key].append(val) - return aug_data_dict - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(transforms={self.transforms}, ' - repr_str += f'img_scale={self.img_scale}, flip={self.flip})' - repr_str += f'flip_direction={self.flip_direction}' - return repr_str diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/transforms.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/transforms.py deleted file mode 100644 index 5673b646f..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/pipelines/transforms.py +++ /dev/null @@ -1,1335 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -import mmcv -import numpy as np -from mmcv.utils import deprecated_api_warning, is_tuple_of -from numpy import random - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class ResizeToMultiple(object): - """Resize images & seg to multiple of divisor. - - Args: - size_divisor (int): images and gt seg maps need to resize to multiple - of size_divisor. Default: 32. - interpolation (str, optional): The interpolation mode of image resize. - Default: None - """ - - def __init__(self, size_divisor=32, interpolation=None): - self.size_divisor = size_divisor - self.interpolation = interpolation - - def __call__(self, results): - """Call function to resize images, semantic segmentation map to - multiple of size divisor. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape' keys are updated. - """ - # Align image to multiple of size divisor. - img = results['img'] - img = mmcv.imresize_to_multiple( - img, - self.size_divisor, - scale_factor=1, - interpolation=self.interpolation - if self.interpolation else 'bilinear') - - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape - - # Align segmentation map to multiple of size divisor. - for key in results.get('seg_fields', []): - gt_seg = results[key] - gt_seg = mmcv.imresize_to_multiple( - gt_seg, - self.size_divisor, - scale_factor=1, - interpolation='nearest') - results[key] = gt_seg - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(size_divisor={self.size_divisor}, ' - f'interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class Resize(object): - """Resize images & seg. - - This transform resizes the input image to some scale. If the input dict - contains the key "scale", then the scale in the input dict is used, - otherwise the specified scale in the init method is used. - - ``img_scale`` can be None, a tuple (single-scale) or a list of tuple - (multi-scale). There are 4 multiscale modes: - - - ``ratio_range is not None``: - 1. When img_scale is None, img_scale is the shape of image in results - (img_scale = results['img'].shape[:2]) and the image is resized based - on the original size. (mode 1) - 2. When img_scale is a tuple (single-scale), randomly sample a ratio from - the ratio range and multiply it with the image scale. (mode 2) - - - ``ratio_range is None and multiscale_mode == "range"``: randomly sample a - scale from the a range. (mode 3) - - - ``ratio_range is None and multiscale_mode == "value"``: randomly sample a - scale from multiple scales. (mode 4) - - Args: - img_scale (tuple or list[tuple]): Images scales for resizing. - Default:None. - multiscale_mode (str): Either "range" or "value". - Default: 'range' - ratio_range (tuple[float]): (min_ratio, max_ratio). - Default: None - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: True - min_size (int, optional): The minimum size for input and the shape - of the image and seg map will not be less than ``min_size``. - As the shape of model input is fixed like 'SETR' and 'BEiT'. - Following the setting in these models, resized images must be - bigger than the crop size in ``slide_inference``. Default: None - """ - - def __init__(self, - img_scale=None, - multiscale_mode='range', - ratio_range=None, - keep_ratio=True, - min_size=None): - if img_scale is None: - self.img_scale = None - else: - if isinstance(img_scale, list): - self.img_scale = img_scale - else: - self.img_scale = [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) - - if ratio_range is not None: - # mode 1: given img_scale=None and a range of image ratio - # mode 2: given a scale and a range of image ratio - assert self.img_scale is None or len(self.img_scale) == 1 - else: - # mode 3 and 4: given multiple scales or a range of scales - assert multiscale_mode in ['value', 'range'] - - self.multiscale_mode = multiscale_mode - self.ratio_range = ratio_range - self.keep_ratio = keep_ratio - self.min_size = min_size - - @staticmethod - def random_select(img_scales): - """Randomly select an img_scale from given candidates. - - Args: - img_scales (list[tuple]): Images scales for selection. - - Returns: - (tuple, int): Returns a tuple ``(img_scale, scale_dix)``, - where ``img_scale`` is the selected image scale and - ``scale_idx`` is the selected index in the given candidates. - """ - - assert mmcv.is_list_of(img_scales, tuple) - scale_idx = np.random.randint(len(img_scales)) - img_scale = img_scales[scale_idx] - return img_scale, scale_idx - - @staticmethod - def random_sample(img_scales): - """Randomly sample an img_scale when ``multiscale_mode=='range'``. - - Args: - img_scales (list[tuple]): Images scale range for sampling. - There must be two tuples in img_scales, which specify the lower - and upper bound of image scales. - - Returns: - (tuple, None): Returns a tuple ``(img_scale, None)``, where - ``img_scale`` is sampled scale and None is just a placeholder - to be consistent with :func:`random_select`. - """ - - assert mmcv.is_list_of(img_scales, tuple) and len(img_scales) == 2 - img_scale_long = [max(s) for s in img_scales] - img_scale_short = [min(s) for s in img_scales] - long_edge = np.random.randint( - min(img_scale_long), - max(img_scale_long) + 1) - short_edge = np.random.randint( - min(img_scale_short), - max(img_scale_short) + 1) - img_scale = (long_edge, short_edge) - return img_scale, None - - @staticmethod - def random_sample_ratio(img_scale, ratio_range): - """Randomly sample an img_scale when ``ratio_range`` is specified. - - A ratio will be randomly sampled from the range specified by - ``ratio_range``. Then it would be multiplied with ``img_scale`` to - generate sampled scale. - - Args: - img_scale (tuple): Images scale base to multiply with ratio. - ratio_range (tuple[float]): The minimum and maximum ratio to scale - the ``img_scale``. - - Returns: - (tuple, None): Returns a tuple ``(scale, None)``, where - ``scale`` is sampled ratio multiplied with ``img_scale`` and - None is just a placeholder to be consistent with - :func:`random_select`. - """ - - assert isinstance(img_scale, tuple) and len(img_scale) == 2 - min_ratio, max_ratio = ratio_range - assert min_ratio <= max_ratio - ratio = np.random.random_sample() * (max_ratio - min_ratio) + min_ratio - scale = int(img_scale[0] * ratio), int(img_scale[1] * ratio) - return scale, None - - def _random_scale(self, results): - """Randomly sample an img_scale according to ``ratio_range`` and - ``multiscale_mode``. - - If ``ratio_range`` is specified, a ratio will be sampled and be - multiplied with ``img_scale``. - If multiple scales are specified by ``img_scale``, a scale will be - sampled according to ``multiscale_mode``. - Otherwise, single scale will be used. - - Args: - results (dict): Result dict from :obj:`dataset`. - - Returns: - dict: Two new keys 'scale` and 'scale_idx` are added into - ``results``, which would be used by subsequent pipelines. - """ - - if self.ratio_range is not None: - if self.img_scale is None: - h, w = results['img'].shape[:2] - scale, scale_idx = self.random_sample_ratio((w, h), - self.ratio_range) - else: - scale, scale_idx = self.random_sample_ratio( - self.img_scale[0], self.ratio_range) - elif len(self.img_scale) == 1: - scale, scale_idx = self.img_scale[0], 0 - elif self.multiscale_mode == 'range': - scale, scale_idx = self.random_sample(self.img_scale) - elif self.multiscale_mode == 'value': - scale, scale_idx = self.random_select(self.img_scale) - else: - raise NotImplementedError - - results['scale'] = scale - results['scale_idx'] = scale_idx - - def _resize_img(self, results): - """Resize images with ``results['scale']``.""" - if self.keep_ratio: - if self.min_size is not None: - # TODO: Now 'min_size' is an 'int' which means the minimum - # shape of images is (min_size, min_size, 3). 'min_size' - # with tuple type will be supported, i.e. the width and - # height are not equal. - if min(results['scale']) < self.min_size: - new_short = self.min_size - else: - new_short = min(results['scale']) - - h, w = results['img'].shape[:2] - if h > w: - new_h, new_w = new_short * h / w, new_short - else: - new_h, new_w = new_short, new_short * w / h - results['scale'] = (new_h, new_w) - - img, scale_factor = mmcv.imrescale( - results['img'], results['scale'], return_scale=True) - # the w_scale and h_scale has minor difference - # a real fix should be done in the mmcv.imrescale in the future - new_h, new_w = img.shape[:2] - h, w = results['img'].shape[:2] - w_scale = new_w / w - h_scale = new_h / h - else: - img, w_scale, h_scale = mmcv.imresize( - results['img'], results['scale'], return_scale=True) - scale_factor = np.array([w_scale, h_scale, w_scale, h_scale], - dtype=np.float32) - results['img'] = img - results['img_shape'] = img.shape - results['pad_shape'] = img.shape # in case that there is no padding - results['scale_factor'] = scale_factor - results['keep_ratio'] = self.keep_ratio - - def _resize_seg(self, results): - """Resize semantic segmentation map with ``results['scale']``.""" - for key in results.get('seg_fields', []): - if self.keep_ratio: - gt_seg = mmcv.imrescale( - results[key], results['scale'], interpolation='nearest') - else: - gt_seg = mmcv.imresize( - results[key], results['scale'], interpolation='nearest') - results[key] = gt_seg - - def __call__(self, results): - """Call function to resize images, bounding boxes, masks, semantic - segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape', 'scale_factor', - 'keep_ratio' keys are added into result dict. - """ - - if 'scale' not in results: - self._random_scale(results) - self._resize_img(results) - self._resize_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(img_scale={self.img_scale}, ' - f'multiscale_mode={self.multiscale_mode}, ' - f'ratio_range={self.ratio_range}, ' - f'keep_ratio={self.keep_ratio})') - return repr_str - - -@PIPELINES.register_module() -class RandomFlip(object): - """Flip the image & seg. - - If the input dict contains the key "flip", then the flag will be used, - otherwise it will be randomly decided by a ratio specified in the init - method. - - Args: - prob (float, optional): The flipping probability. Default: None. - direction(str, optional): The flipping direction. Options are - 'horizontal' and 'vertical'. Default: 'horizontal'. - """ - - @deprecated_api_warning({'flip_ratio': 'prob'}, cls_name='RandomFlip') - def __init__(self, prob=None, direction='horizontal'): - self.prob = prob - self.direction = direction - if prob is not None: - assert prob >= 0 and prob <= 1 - assert direction in ['horizontal', 'vertical'] - - def __call__(self, results): - """Call function to flip bounding boxes, masks, semantic segmentation - maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Flipped results, 'flip', 'flip_direction' keys are added into - result dict. - """ - - if 'flip' not in results: - flip = True if np.random.rand() < self.prob else False - results['flip'] = flip - if 'flip_direction' not in results: - results['flip_direction'] = self.direction - if results['flip']: - # flip image - results['img'] = mmcv.imflip( - results['img'], direction=results['flip_direction']) - - # flip segs - for key in results.get('seg_fields', []): - # use copy() to make numpy stride positive - results[key] = mmcv.imflip( - results[key], direction=results['flip_direction']).copy() - return results - - def __repr__(self): - return self.__class__.__name__ + f'(prob={self.prob})' - - -@PIPELINES.register_module() -class Pad(object): - """Pad the image & mask. - - There are two padding modes: (1) pad to a fixed size and (2) pad to the - minimum size that is divisible by some number. - Added keys are "pad_shape", "pad_fixed_size", "pad_size_divisor", - - Args: - size (tuple, optional): Fixed padding size. - size_divisor (int, optional): The divisor of padded size. - pad_val (float, optional): Padding value. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - """ - - def __init__(self, - size=None, - size_divisor=None, - pad_val=0, - seg_pad_val=255): - self.size = size - self.size_divisor = size_divisor - self.pad_val = pad_val - self.seg_pad_val = seg_pad_val - # only one of size and size_divisor should be valid - assert size is not None or size_divisor is not None - assert size is None or size_divisor is None - - def _pad_img(self, results): - """Pad images according to ``self.size``.""" - if self.size is not None: - padded_img = mmcv.impad( - results['img'], shape=self.size, pad_val=self.pad_val) - elif self.size_divisor is not None: - padded_img = mmcv.impad_to_multiple( - results['img'], self.size_divisor, pad_val=self.pad_val) - results['img'] = padded_img - results['pad_shape'] = padded_img.shape - results['pad_fixed_size'] = self.size - results['pad_size_divisor'] = self.size_divisor - - def _pad_seg(self, results): - """Pad masks according to ``results['pad_shape']``.""" - for key in results.get('seg_fields', []): - results[key] = mmcv.impad( - results[key], - shape=results['pad_shape'][:2], - pad_val=self.seg_pad_val) - - def __call__(self, results): - """Call function to pad images, masks, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Updated result dict. - """ - - self._pad_img(results) - self._pad_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(size={self.size}, size_divisor={self.size_divisor}, ' \ - f'pad_val={self.pad_val})' - return repr_str - - -@PIPELINES.register_module() -class Normalize(object): - """Normalize the image. - - Added key is "img_norm_cfg". - - Args: - mean (sequence): Mean values of 3 channels. - std (sequence): Std values of 3 channels. - to_rgb (bool): Whether to convert the image from BGR to RGB, - default is true. - """ - - def __init__(self, mean, std, to_rgb=True): - self.mean = np.array(mean, dtype=np.float32) - self.std = np.array(std, dtype=np.float32) - self.to_rgb = to_rgb - - def __call__(self, results): - """Call function to normalize images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Normalized results, 'img_norm_cfg' key is added into - result dict. - """ - - results['img'] = mmcv.imnormalize(results['img'], self.mean, self.std, - self.to_rgb) - results['img_norm_cfg'] = dict( - mean=self.mean, std=self.std, to_rgb=self.to_rgb) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(mean={self.mean}, std={self.std}, to_rgb=' \ - f'{self.to_rgb})' - return repr_str - - -@PIPELINES.register_module() -class Rerange(object): - """Rerange the image pixel value. - - Args: - min_value (float or int): Minimum value of the reranged image. - Default: 0. - max_value (float or int): Maximum value of the reranged image. - Default: 255. - """ - - def __init__(self, min_value=0, max_value=255): - assert isinstance(min_value, float) or isinstance(min_value, int) - assert isinstance(max_value, float) or isinstance(max_value, int) - assert min_value < max_value - self.min_value = min_value - self.max_value = max_value - - def __call__(self, results): - """Call function to rerange images. - - Args: - results (dict): Result dict from loading pipeline. - Returns: - dict: Reranged results. - """ - - img = results['img'] - img_min_value = np.min(img) - img_max_value = np.max(img) - - assert img_min_value < img_max_value - # rerange to [0, 1] - img = (img - img_min_value) / (img_max_value - img_min_value) - # rerange to [min_value, max_value] - img = img * (self.max_value - self.min_value) + self.min_value - results['img'] = img - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(min_value={self.min_value}, max_value={self.max_value})' - return repr_str - - -@PIPELINES.register_module() -class CLAHE(object): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - """ - - def __init__(self, clip_limit=40.0, tile_grid_size=(8, 8)): - assert isinstance(clip_limit, (float, int)) - self.clip_limit = clip_limit - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - self.tile_grid_size = tile_grid_size - - def __call__(self, results): - """Call function to Use CLAHE method process images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - for i in range(results['img'].shape[2]): - results['img'][:, :, i] = mmcv.clahe( - np.array(results['img'][:, :, i], dtype=np.uint8), - self.clip_limit, self.tile_grid_size) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(clip_limit={self.clip_limit}, '\ - f'tile_grid_size={self.tile_grid_size})' - return repr_str - - -@PIPELINES.register_module() -class RandomCrop(object): - """Random crop the image & seg. - - Args: - crop_size (tuple): Expected size after cropping, (h, w). - cat_max_ratio (float): The maximum ratio that single category could - occupy. - """ - - def __init__(self, crop_size, cat_max_ratio=1., ignore_index=255): - assert crop_size[0] > 0 and crop_size[1] > 0 - self.crop_size = crop_size - self.cat_max_ratio = cat_max_ratio - self.ignore_index = ignore_index - - def get_crop_bbox(self, img): - """Randomly get a crop bounding box.""" - margin_h = max(img.shape[0] - self.crop_size[0], 0) - margin_w = max(img.shape[1] - self.crop_size[1], 0) - offset_h = np.random.randint(0, margin_h + 1) - offset_w = np.random.randint(0, margin_w + 1) - crop_y1, crop_y2 = offset_h, offset_h + self.crop_size[0] - crop_x1, crop_x2 = offset_w, offset_w + self.crop_size[1] - - return crop_y1, crop_y2, crop_x1, crop_x2 - - def crop(self, img, crop_bbox): - """Crop from ``img``""" - crop_y1, crop_y2, crop_x1, crop_x2 = crop_bbox - img = img[crop_y1:crop_y2, crop_x1:crop_x2, ...] - return img - - def __call__(self, results): - """Call function to randomly crop images, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Randomly cropped results, 'img_shape' key in result dict is - updated according to crop size. - """ - - img = results['img'] - crop_bbox = self.get_crop_bbox(img) - if self.cat_max_ratio < 1.: - # Repeat 10 times - for _ in range(10): - seg_temp = self.crop(results['gt_semantic_seg'], crop_bbox) - labels, cnt = np.unique(seg_temp, return_counts=True) - cnt = cnt[labels != self.ignore_index] - if len(cnt) > 1 and np.max(cnt) / np.sum( - cnt) < self.cat_max_ratio: - break - crop_bbox = self.get_crop_bbox(img) - - # crop the image - img = self.crop(img, crop_bbox) - img_shape = img.shape - results['img'] = img - results['img_shape'] = img_shape - - # crop semantic seg - for key in results.get('seg_fields', []): - results[key] = self.crop(results[key], crop_bbox) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(crop_size={self.crop_size})' - - -@PIPELINES.register_module() -class RandomRotate(object): - """Rotate the image & seg. - - Args: - prob (float): The rotation probability. - degree (float, tuple[float]): Range of degrees to select from. If - degree is a number instead of tuple like (min, max), - the range of degree will be (``-degree``, ``+degree``) - pad_val (float, optional): Padding value of image. Default: 0. - seg_pad_val (float, optional): Padding value of segmentation map. - Default: 255. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. Default: None. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. Default: False - """ - - def __init__(self, - prob, - degree, - pad_val=0, - seg_pad_val=255, - center=None, - auto_bound=False): - self.prob = prob - assert prob >= 0 and prob <= 1 - if isinstance(degree, (float, int)): - assert degree > 0, f'degree {degree} should be positive' - self.degree = (-degree, degree) - else: - self.degree = degree - assert len(self.degree) == 2, f'degree {self.degree} should be a ' \ - f'tuple of (min, max)' - self.pal_val = pad_val - self.seg_pad_val = seg_pad_val - self.center = center - self.auto_bound = auto_bound - - def __call__(self, results): - """Call function to rotate image, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Rotated results. - """ - - rotate = True if np.random.rand() < self.prob else False - degree = np.random.uniform(min(*self.degree), max(*self.degree)) - if rotate: - # rotate image - results['img'] = mmcv.imrotate( - results['img'], - angle=degree, - border_value=self.pal_val, - center=self.center, - auto_bound=self.auto_bound) - - # rotate segs - for key in results.get('seg_fields', []): - results[key] = mmcv.imrotate( - results[key], - angle=degree, - border_value=self.seg_pad_val, - center=self.center, - auto_bound=self.auto_bound, - interpolation='nearest') - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' \ - f'degree={self.degree}, ' \ - f'pad_val={self.pal_val}, ' \ - f'seg_pad_val={self.seg_pad_val}, ' \ - f'center={self.center}, ' \ - f'auto_bound={self.auto_bound})' - return repr_str - - -@PIPELINES.register_module() -class RGB2Gray(object): - """Convert RGB image to grayscale image. - - This transform calculate the weighted mean of input image channels with - ``weights`` and then expand the channels to ``out_channels``. When - ``out_channels`` is None, the number of output channels is the same as - input channels. - - Args: - out_channels (int): Expected number of output channels after - transforming. Default: None. - weights (tuple[float]): The weights to calculate the weighted mean. - Default: (0.299, 0.587, 0.114). - """ - - def __init__(self, out_channels=None, weights=(0.299, 0.587, 0.114)): - assert out_channels is None or out_channels > 0 - self.out_channels = out_channels - assert isinstance(weights, tuple) - for item in weights: - assert isinstance(item, (float, int)) - self.weights = weights - - def __call__(self, results): - """Call function to convert RGB image to grayscale image. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with grayscale image. - """ - img = results['img'] - assert len(img.shape) == 3 - assert img.shape[2] == len(self.weights) - weights = np.array(self.weights).reshape((1, 1, -1)) - img = (img * weights).sum(2, keepdims=True) - if self.out_channels is None: - img = img.repeat(weights.shape[2], axis=2) - else: - img = img.repeat(self.out_channels, axis=2) - - results['img'] = img - results['img_shape'] = img.shape - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(out_channels={self.out_channels}, ' \ - f'weights={self.weights})' - return repr_str - - -@PIPELINES.register_module() -class AdjustGamma(object): - """Using gamma correction to process the image. - - Args: - gamma (float or int): Gamma value used in gamma correction. - Default: 1.0. - """ - - def __init__(self, gamma=1.0): - assert isinstance(gamma, float) or isinstance(gamma, int) - assert gamma > 0 - self.gamma = gamma - inv_gamma = 1.0 / gamma - self.table = np.array([(i / 255.0)**inv_gamma * 255 - for i in np.arange(256)]).astype('uint8') - - def __call__(self, results): - """Call function to process the image with gamma correction. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Processed results. - """ - - results['img'] = mmcv.lut_transform( - np.array(results['img'], dtype=np.uint8), self.table) - - return results - - def __repr__(self): - return self.__class__.__name__ + f'(gamma={self.gamma})' - - -@PIPELINES.register_module() -class SegRescale(object): - """Rescale semantic segmentation maps. - - Args: - scale_factor (float): The scale factor of the final output. - """ - - def __init__(self, scale_factor=1): - self.scale_factor = scale_factor - - def __call__(self, results): - """Call function to scale the semantic segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with semantic segmentation map scaled. - """ - for key in results.get('seg_fields', []): - if self.scale_factor != 1: - results[key] = mmcv.imrescale( - results[key], self.scale_factor, interpolation='nearest') - return results - - def __repr__(self): - return self.__class__.__name__ + f'(scale_factor={self.scale_factor})' - - -@PIPELINES.register_module() -class PhotoMetricDistortion(object): - """Apply photometric distortion to image sequentially, every transformation - is applied with a probability of 0.5. The position of random contrast is in - second or second to last. - - 1. random brightness - 2. random contrast (mode 0) - 3. convert color from BGR to HSV - 4. random saturation - 5. random hue - 6. convert color from HSV to BGR - 7. random contrast (mode 1) - - Args: - brightness_delta (int): delta of brightness. - contrast_range (tuple): range of contrast. - saturation_range (tuple): range of saturation. - hue_delta (int): delta of hue. - """ - - def __init__(self, - brightness_delta=32, - contrast_range=(0.5, 1.5), - saturation_range=(0.5, 1.5), - hue_delta=18): - self.brightness_delta = brightness_delta - self.contrast_lower, self.contrast_upper = contrast_range - self.saturation_lower, self.saturation_upper = saturation_range - self.hue_delta = hue_delta - - def convert(self, img, alpha=1, beta=0): - """Multiple with alpha and add beat with clip.""" - img = img.astype(np.float32) * alpha + beta - img = np.clip(img, 0, 255) - return img.astype(np.uint8) - - def brightness(self, img): - """Brightness distortion.""" - if random.randint(2): - return self.convert( - img, - beta=random.uniform(-self.brightness_delta, - self.brightness_delta)) - return img - - def contrast(self, img): - """Contrast distortion.""" - if random.randint(2): - return self.convert( - img, - alpha=random.uniform(self.contrast_lower, self.contrast_upper)) - return img - - def saturation(self, img): - """Saturation distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, 1] = self.convert( - img[:, :, 1], - alpha=random.uniform(self.saturation_lower, - self.saturation_upper)) - img = mmcv.hsv2bgr(img) - return img - - def hue(self, img): - """Hue distortion.""" - if random.randint(2): - img = mmcv.bgr2hsv(img) - img[:, :, - 0] = (img[:, :, 0].astype(int) + - random.randint(-self.hue_delta, self.hue_delta)) % 180 - img = mmcv.hsv2bgr(img) - return img - - def __call__(self, results): - """Call function to perform photometric distortion on images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images distorted. - """ - - img = results['img'] - # random brightness - img = self.brightness(img) - - # mode == 0 --> do random contrast first - # mode == 1 --> do random contrast last - mode = random.randint(2) - if mode == 1: - img = self.contrast(img) - - # random saturation - img = self.saturation(img) - - # random hue - img = self.hue(img) - - # random contrast - if mode == 0: - img = self.contrast(img) - - results['img'] = img - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(brightness_delta={self.brightness_delta}, ' - f'contrast_range=({self.contrast_lower}, ' - f'{self.contrast_upper}), ' - f'saturation_range=({self.saturation_lower}, ' - f'{self.saturation_upper}), ' - f'hue_delta={self.hue_delta})') - return repr_str - - -@PIPELINES.register_module() -class RandomCutOut(object): - """CutOut operation. - - Randomly drop some regions of image used in - `Cutout `_. - Args: - prob (float): cutout probability. - n_holes (int | tuple[int, int]): Number of regions to be dropped. - If it is given as a list, number of holes will be randomly - selected from the closed interval [`n_holes[0]`, `n_holes[1]`]. - cutout_shape (tuple[int, int] | list[tuple[int, int]]): The candidate - shape of dropped regions. It can be `tuple[int, int]` to use a - fixed cutout shape, or `list[tuple[int, int]]` to randomly choose - shape from the list. - cutout_ratio (tuple[float, float] | list[tuple[float, float]]): The - candidate ratio of dropped regions. It can be `tuple[float, float]` - to use a fixed ratio or `list[tuple[float, float]]` to randomly - choose ratio from the list. Please note that `cutout_shape` - and `cutout_ratio` cannot be both given at the same time. - fill_in (tuple[float, float, float] | tuple[int, int, int]): The value - of pixel to fill in the dropped regions. Default: (0, 0, 0). - seg_fill_in (int): The labels of pixel to fill in the dropped regions. - If seg_fill_in is None, skip. Default: None. - """ - - def __init__(self, - prob, - n_holes, - cutout_shape=None, - cutout_ratio=None, - fill_in=(0, 0, 0), - seg_fill_in=None): - - assert 0 <= prob and prob <= 1 - assert (cutout_shape is None) ^ (cutout_ratio is None), \ - 'Either cutout_shape or cutout_ratio should be specified.' - assert (isinstance(cutout_shape, (list, tuple)) - or isinstance(cutout_ratio, (list, tuple))) - if isinstance(n_holes, tuple): - assert len(n_holes) == 2 and 0 <= n_holes[0] < n_holes[1] - else: - n_holes = (n_holes, n_holes) - if seg_fill_in is not None: - assert (isinstance(seg_fill_in, int) and 0 <= seg_fill_in - and seg_fill_in <= 255) - self.prob = prob - self.n_holes = n_holes - self.fill_in = fill_in - self.seg_fill_in = seg_fill_in - self.with_ratio = cutout_ratio is not None - self.candidates = cutout_ratio if self.with_ratio else cutout_shape - if not isinstance(self.candidates, list): - self.candidates = [self.candidates] - - def __call__(self, results): - """Call function to drop some regions of image.""" - cutout = True if np.random.rand() < self.prob else False - if cutout: - h, w, c = results['img'].shape - n_holes = np.random.randint(self.n_holes[0], self.n_holes[1] + 1) - for _ in range(n_holes): - x1 = np.random.randint(0, w) - y1 = np.random.randint(0, h) - index = np.random.randint(0, len(self.candidates)) - if not self.with_ratio: - cutout_w, cutout_h = self.candidates[index] - else: - cutout_w = int(self.candidates[index][0] * w) - cutout_h = int(self.candidates[index][1] * h) - - x2 = np.clip(x1 + cutout_w, 0, w) - y2 = np.clip(y1 + cutout_h, 0, h) - results['img'][y1:y2, x1:x2, :] = self.fill_in - - if self.seg_fill_in is not None: - for key in results.get('seg_fields', []): - results[key][y1:y2, x1:x2] = self.seg_fill_in - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' - repr_str += f'n_holes={self.n_holes}, ' - repr_str += (f'cutout_ratio={self.candidates}, ' if self.with_ratio - else f'cutout_shape={self.candidates}, ') - repr_str += f'fill_in={self.fill_in}, ' - repr_str += f'seg_fill_in={self.seg_fill_in})' - return repr_str - - -@PIPELINES.register_module() -class RandomMosaic(object): - """Mosaic augmentation. Given 4 images, mosaic transform combines them into - one output image. The output image is composed of the parts from each sub- - image. - - .. code:: text - - mosaic transform - center_x - +------------------------------+ - | pad | pad | - | +-----------+ | - | | | | - | | image1 |--------+ | - | | | | | - | | | image2 | | - center_y |----+-------------+-----------| - | | cropped | | - |pad | image3 | image4 | - | | | | - +----|-------------+-----------+ - | | - +-------------+ - - The mosaic transform steps are as follows: - 1. Choose the mosaic center as the intersections of 4 images - 2. Get the left top image according to the index, and randomly - sample another 3 images from the custom dataset. - 3. Sub image will be cropped if image is larger than mosaic patch - - Args: - prob (float): mosaic probability. - img_scale (Sequence[int]): Image size after mosaic pipeline of - a single image. The size of the output image is four times - that of a single image. The output image comprises 4 single images. - Default: (640, 640). - center_ratio_range (Sequence[float]): Center ratio range of mosaic - output. Default: (0.5, 1.5). - pad_val (int): Pad value. Default: 0. - seg_pad_val (int): Pad value of segmentation map. Default: 255. - """ - - def __init__(self, - prob, - img_scale=(640, 640), - center_ratio_range=(0.5, 1.5), - pad_val=0, - seg_pad_val=255): - assert 0 <= prob and prob <= 1 - assert isinstance(img_scale, tuple) - self.prob = prob - self.img_scale = img_scale - self.center_ratio_range = center_ratio_range - self.pad_val = pad_val - self.seg_pad_val = seg_pad_val - - def __call__(self, results): - """Call function to make a mosaic of image. - - Args: - results (dict): Result dict. - - Returns: - dict: Result dict with mosaic transformed. - """ - mosaic = True if np.random.rand() < self.prob else False - if mosaic: - results = self._mosaic_transform_img(results) - results = self._mosaic_transform_seg(results) - return results - - def get_indexes(self, dataset): - """Call function to collect indexes. - - Args: - dataset (:obj:`MultiImageMixDataset`): The dataset. - - Returns: - list: indexes. - """ - - indexes = [random.randint(0, len(dataset)) for _ in range(3)] - return indexes - - def _mosaic_transform_img(self, results): - """Mosaic transform function. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - if len(results['img'].shape) == 3: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2), 3), - self.pad_val, - dtype=results['img'].dtype) - else: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.pad_val, - dtype=results['img'].dtype) - - # mosaic center x, y - self.center_x = int( - random.uniform(*self.center_ratio_range) * self.img_scale[1]) - self.center_y = int( - random.uniform(*self.center_ratio_range) * self.img_scale[0]) - center_position = (self.center_x, self.center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - result_patch = copy.deepcopy(results) - else: - result_patch = copy.deepcopy(results['mix_results'][i - 1]) - - img_i = result_patch['img'] - h_i, w_i = img_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - img_i = mmcv.imresize( - img_i, (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i))) - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, img_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_img[y1_p:y2_p, x1_p:x2_p] = img_i[y1_c:y2_c, x1_c:x2_c] - - results['img'] = mosaic_img - results['img_shape'] = mosaic_img.shape - results['ori_shape'] = mosaic_img.shape - - return results - - def _mosaic_transform_seg(self, results): - """Mosaic transform function for label annotations. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - for key in results.get('seg_fields', []): - mosaic_seg = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.seg_pad_val, - dtype=results[key].dtype) - - # mosaic center x, y - center_position = (self.center_x, self.center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - result_patch = copy.deepcopy(results) - else: - result_patch = copy.deepcopy(results['mix_results'][i - 1]) - - gt_seg_i = result_patch[key] - h_i, w_i = gt_seg_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - gt_seg_i = mmcv.imresize( - gt_seg_i, - (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i)), - interpolation='nearest') - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, gt_seg_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_seg[y1_p:y2_p, x1_p:x2_p] = gt_seg_i[y1_c:y2_c, - x1_c:x2_c] - - results[key] = mosaic_seg - - return results - - def _mosaic_combine(self, loc, center_position_xy, img_shape_wh): - """Calculate global coordinate of mosaic image and local coordinate of - cropped sub-image. - - Args: - loc (str): Index for the sub-image, loc in ('top_left', - 'top_right', 'bottom_left', 'bottom_right'). - center_position_xy (Sequence[float]): Mixing center for 4 images, - (x, y). - img_shape_wh (Sequence[int]): Width and height of sub-image - - Returns: - tuple[tuple[float]]: Corresponding coordinate of pasting and - cropping - - paste_coord (tuple): paste corner coordinate in mosaic image. - - crop_coord (tuple): crop corner coordinate in mosaic image. - """ - - assert loc in ('top_left', 'top_right', 'bottom_left', 'bottom_right') - if loc == 'top_left': - # index0 to top left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - center_position_xy[0], \ - center_position_xy[1] - crop_coord = img_shape_wh[0] - (x2 - x1), img_shape_wh[1] - ( - y2 - y1), img_shape_wh[0], img_shape_wh[1] - - elif loc == 'top_right': - # index1 to top right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - center_position_xy[1] - crop_coord = 0, img_shape_wh[1] - (y2 - y1), min( - img_shape_wh[0], x2 - x1), img_shape_wh[1] - - elif loc == 'bottom_left': - # index2 to bottom left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - center_position_xy[1], \ - center_position_xy[0], \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = img_shape_wh[0] - (x2 - x1), 0, img_shape_wh[0], min( - y2 - y1, img_shape_wh[1]) - - else: - # index3 to bottom right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - center_position_xy[1], \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = 0, 0, min(img_shape_wh[0], - x2 - x1), min(y2 - y1, img_shape_wh[1]) - - paste_coord = x1, y1, x2, y2 - return paste_coord, crop_coord - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(prob={self.prob}, ' - repr_str += f'img_scale={self.img_scale}, ' - repr_str += f'center_ratio_range={self.center_ratio_range}, ' - repr_str += f'pad_val={self.pad_val}, ' - repr_str += f'seg_pad_val={self.pad_val})' - return repr_str diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/samplers/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/samplers/__init__.py deleted file mode 100644 index da09effaf..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/samplers/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .distributed_sampler import DistributedSampler - -__all__ = ['DistributedSampler'] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/samplers/distributed_sampler.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/samplers/distributed_sampler.py deleted file mode 100644 index d1a13c716..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/samplers/distributed_sampler.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from __future__ import division -from typing import Iterator, Optional - -import torch -from torch.utils.data import Dataset -from torch.utils.data import DistributedSampler as _DistributedSampler - -from mmseg.core.utils import sync_random_seed - - -class DistributedSampler(_DistributedSampler): - """DistributedSampler inheriting from - `torch.utils.data.DistributedSampler`. - - Args: - datasets (Dataset): the dataset will be loaded. - num_replicas (int, optional): Number of processes participating in - distributed training. By default, world_size is retrieved from the - current distributed group. - rank (int, optional): Rank of the current process within num_replicas. - By default, rank is retrieved from the current distributed group. - shuffle (bool): If True (default), sampler will shuffle the indices. - seed (int): random seed used to shuffle the sampler if - :attr:`shuffle=True`. This number should be identical across all - processes in the distributed group. Default: ``0``. - """ - - def __init__(self, - dataset: Dataset, - num_replicas: Optional[int] = None, - rank: Optional[int] = None, - shuffle: bool = True, - seed=0) -> None: - super().__init__( - dataset, num_replicas=num_replicas, rank=rank, shuffle=shuffle) - - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - - def __iter__(self) -> Iterator: - """ - Yields: - Iterator: iterator of indices for rank. - """ - # deterministically shuffle based on epoch - if self.shuffle: - g = torch.Generator() - # When :attr:`shuffle=True`, this ensures all replicas - # use a different random ordering for each epoch. - # Otherwise, the next iteration of this sampler will - # yield the same ordering. - g.manual_seed(self.epoch + self.seed) - indices = torch.randperm(len(self.dataset), generator=g).tolist() - else: - indices = torch.arange(len(self.dataset)).tolist() - - # add extra samples to make it evenly divisible - indices += indices[:(self.total_size - len(indices))] - assert len(indices) == self.total_size - - # subsample - indices = indices[self.rank:self.total_size:self.num_replicas] - assert len(indices) == self.num_samples - - return iter(indices) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/voc.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/voc.py deleted file mode 100644 index 9eecc344f..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/datasets/voc.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class PascalVOCDataset(CustomDataset): - """Pascal VOC dataset. - - Args: - split (str): Split txt file for Pascal VOC. - """ - - CLASSES = ('background', 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', - 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', - 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', - 'train', 'tvmonitor') - - PALETTE = [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0], [0, 0, 128], - [128, 0, 128], [0, 128, 128], [128, 128, 128], [64, 0, 0], - [192, 0, 0], [64, 128, 0], [192, 128, 0], [64, 0, 128], - [192, 0, 128], [64, 128, 128], [192, 128, 128], [0, 64, 0], - [128, 64, 0], [0, 192, 0], [128, 192, 0], [0, 64, 128]] - - def __init__(self, split, **kwargs): - - if "img_dir" in kwargs: - image_dir = kwargs["img_dir"] - if not osp.join(kwargs['data_root'], image_dir): - image_dir = "images" - if osp.join(kwargs['data_root'], image_dir): - kwargs["img_dir"] = image_dir - - super(PascalVOCDataset, self).__init__( - img_suffix='.jpg', seg_map_suffix='.png', split=split, **kwargs) - - assert osp.exists(self.img_dir) and self.split is not None diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/__init__.py deleted file mode 100644 index 87d8108e3..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .backbones import * # noqa: F401,F403 -from .builder import (BACKBONES, HEADS, LOSSES, SEGMENTORS, build_backbone, - build_head, build_loss, build_segmentor) -from .decode_heads import * # noqa: F401,F403 -from .losses import * # noqa: F401,F403 -from .necks import * # noqa: F401,F403 -from .segmentors import * # noqa: F401,F403 - -__all__ = [ - 'BACKBONES', 'HEADS', 'LOSSES', 'SEGMENTORS', 'build_backbone', - 'build_head', 'build_loss', 'build_segmentor' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/__init__.py deleted file mode 100644 index 249f0d5df..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .resnest import ResNeSt -from .resnet import ResNet, ResNetV1c, ResNetV1d -from .resnext import ResNeXt -from .bisenetv1 import AttentionRefinementModule -from .stdc import STDCNet, STDCContextPathNet diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/bisenetv1.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/bisenetv1.py deleted file mode 100644 index 4beb7b394..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/bisenetv1.py +++ /dev/null @@ -1,332 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import BACKBONES, build_backbone - - -class SpatialPath(BaseModule): - """Spatial Path to preserve the spatial size of the original input image - and encode affluent spatial information. - - Args: - in_channels(int): The number of channels of input - image. Default: 3. - num_channels (Tuple[int]): The number of channels of - each layers in Spatial Path. - Default: (64, 64, 64, 128). - Returns: - x (torch.Tensor): Feature map for Feature Fusion Module. - """ - - def __init__(self, - in_channels=3, - num_channels=(64, 64, 64, 128), - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(SpatialPath, self).__init__(init_cfg=init_cfg) - assert len(num_channels) == 4, 'Length of input channels \ - of Spatial Path must be 4!' - - self.layers = [] - for i in range(len(num_channels)): - layer_name = f'layer{i + 1}' - self.layers.append(layer_name) - if i == 0: - self.add_module( - layer_name, - ConvModule( - in_channels=in_channels, - out_channels=num_channels[i], - kernel_size=7, - stride=2, - padding=3, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - elif i == len(num_channels) - 1: - self.add_module( - layer_name, - ConvModule( - in_channels=num_channels[i - 1], - out_channels=num_channels[i], - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - else: - self.add_module( - layer_name, - ConvModule( - in_channels=num_channels[i - 1], - out_channels=num_channels[i], - kernel_size=3, - stride=2, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, x): - for i, layer_name in enumerate(self.layers): - layer_stage = getattr(self, layer_name) - x = layer_stage(x) - return x - - -class AttentionRefinementModule(BaseModule): - """Attention Refinement Module (ARM) to refine the features of each stage. - - Args: - in_channels (int): The number of input channels. - out_channels (int): The number of output channels. - Returns: - x_out (torch.Tensor): Feature map of Attention Refinement Module. - """ - - def __init__(self, - in_channels, - out_channel, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(AttentionRefinementModule, self).__init__(init_cfg=init_cfg) - self.conv_layer = ConvModule( - in_channels=in_channels, - out_channels=out_channel, - kernel_size=3, - stride=1, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.atten_conv_layer = nn.Sequential( - nn.AdaptiveAvgPool2d((1, 1)), - ConvModule( - in_channels=out_channel, - out_channels=out_channel, - kernel_size=1, - bias=False, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None), nn.Sigmoid()) - - def forward(self, x): - x = self.conv_layer(x) - x_atten = self.atten_conv_layer(x) - x_out = x * x_atten - return x_out - - -class ContextPath(BaseModule): - """Context Path to provide sufficient receptive field. - - Args: - backbone_cfg:(dict): Config of backbone of - Context Path. - context_channels (Tuple[int]): The number of channel numbers - of various modules in Context Path. - Default: (128, 256, 512). - align_corners (bool, optional): The align_corners argument of - resize operation. Default: False. - Returns: - x_16_up, x_32_up (torch.Tensor, torch.Tensor): Two feature maps - undergoing upsampling from 1/16 and 1/32 downsampling - feature maps. These two feature maps are used for Feature - Fusion Module and Auxiliary Head. - """ - - def __init__(self, - backbone_cfg, - context_channels=(128, 256, 512), - align_corners=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(ContextPath, self).__init__(init_cfg=init_cfg) - assert len(context_channels) == 3, 'Length of input channels \ - of Context Path must be 3!' - - self.backbone = build_backbone(backbone_cfg) - - self.align_corners = align_corners - self.arm16 = AttentionRefinementModule(context_channels[1], - context_channels[0]) - self.arm32 = AttentionRefinementModule(context_channels[2], - context_channels[0]) - self.conv_head32 = ConvModule( - in_channels=context_channels[0], - out_channels=context_channels[0], - kernel_size=3, - stride=1, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.conv_head16 = ConvModule( - in_channels=context_channels[0], - out_channels=context_channels[0], - kernel_size=3, - stride=1, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.gap_conv = nn.Sequential( - nn.AdaptiveAvgPool2d((1, 1)), - ConvModule( - in_channels=context_channels[2], - out_channels=context_channels[0], - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, x): - x_4, x_8, x_16, x_32 = self.backbone(x) - x_gap = self.gap_conv(x_32) - - x_32_arm = self.arm32(x_32) - x_32_sum = x_32_arm + x_gap - x_32_up = resize(input=x_32_sum, size=x_16.shape[2:], mode='nearest') - x_32_up = self.conv_head32(x_32_up) - - x_16_arm = self.arm16(x_16) - x_16_sum = x_16_arm + x_32_up - x_16_up = resize(input=x_16_sum, size=x_8.shape[2:], mode='nearest') - x_16_up = self.conv_head16(x_16_up) - - return x_16_up, x_32_up - - -class FeatureFusionModule(BaseModule): - """Feature Fusion Module to fuse low level output feature of Spatial Path - and high level output feature of Context Path. - - Args: - in_channels (int): The number of input channels. - out_channels (int): The number of output channels. - Returns: - x_out (torch.Tensor): Feature map of Feature Fusion Module. - """ - - def __init__(self, - in_channels, - out_channels, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(FeatureFusionModule, self).__init__(init_cfg=init_cfg) - self.conv1 = ConvModule( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.gap = nn.AdaptiveAvgPool2d((1, 1)) - self.conv_atten = nn.Sequential( - ConvModule( - in_channels=out_channels, - out_channels=out_channels, - kernel_size=1, - stride=1, - padding=0, - bias=False, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg), nn.Sigmoid()) - - def forward(self, x_sp, x_cp): - x_concat = torch.cat([x_sp, x_cp], dim=1) - x_fuse = self.conv1(x_concat) - x_atten = self.gap(x_fuse) - # Note: No BN and more 1x1 conv in paper. - x_atten = self.conv_atten(x_atten) - x_atten = x_fuse * x_atten - x_out = x_atten + x_fuse - return x_out - - -@BACKBONES.register_module() -class BiSeNetV1(BaseModule): - """BiSeNetV1 backbone. - - This backbone is the implementation of `BiSeNet: Bilateral - Segmentation Network for Real-time Semantic - Segmentation `_. - - Args: - backbone_cfg:(dict): Config of backbone of - Context Path. - in_channels (int): The number of channels of input - image. Default: 3. - spatial_channels (Tuple[int]): Size of channel numbers of - various layers in Spatial Path. - Default: (64, 64, 64, 128). - context_channels (Tuple[int]): Size of channel numbers of - various modules in Context Path. - Default: (128, 256, 512). - out_indices (Tuple[int] | int, optional): Output from which stages. - Default: (0, 1, 2). - align_corners (bool, optional): The align_corners argument of - resize operation in Bilateral Guided Aggregation Layer. - Default: False. - out_channels(int): The number of channels of output. - It must be the same with `in_channels` of decode_head. - Default: 256. - """ - - def __init__(self, - backbone_cfg, - in_channels=3, - spatial_channels=(64, 64, 64, 128), - context_channels=(128, 256, 512), - out_indices=(0, 1, 2), - align_corners=False, - out_channels=256, - conv_cfg=None, - norm_cfg=dict(type='BN', requires_grad=True), - act_cfg=dict(type='ReLU'), - init_cfg=None): - - super(BiSeNetV1, self).__init__(init_cfg=init_cfg) - assert len(spatial_channels) == 4, 'Length of input channels \ - of Spatial Path must be 4!' - - assert len(context_channels) == 3, 'Length of input channels \ - of Context Path must be 3!' - - self.out_indices = out_indices - self.align_corners = align_corners - self.context_path = ContextPath(backbone_cfg, context_channels, - self.align_corners) - self.spatial_path = SpatialPath(in_channels, spatial_channels) - self.ffm = FeatureFusionModule(context_channels[1], out_channels) - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - - def forward(self, x): - # stole refactoring code from Coin Cheung, thanks - x_context8, x_context16 = self.context_path(x) - x_spatial = self.spatial_path(x) - x_fuse = self.ffm(x_spatial, x_context8) - - outs = [x_fuse, x_context8, x_context16] - outs = [outs[i] for i in self.out_indices] - return tuple(outs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/resnest.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/resnest.py deleted file mode 100644 index 91952c2ca..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/resnest.py +++ /dev/null @@ -1,318 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import torch -import torch.nn as nn -import torch.nn.functional as F -import torch.utils.checkpoint as cp -from mmcv.cnn import build_conv_layer, build_norm_layer - -from ..builder import BACKBONES -from ..utils import ResLayer -from .resnet import Bottleneck as _Bottleneck -from .resnet import ResNetV1d - - -class RSoftmax(nn.Module): - """Radix Softmax module in ``SplitAttentionConv2d``. - - Args: - radix (int): Radix of input. - groups (int): Groups of input. - """ - - def __init__(self, radix, groups): - super().__init__() - self.radix = radix - self.groups = groups - - def forward(self, x): - batch = x.size(0) - if self.radix > 1: - x = x.view(batch, self.groups, self.radix, -1).transpose(1, 2) - x = F.softmax(x, dim=1) - x = x.reshape(batch, -1) - else: - x = torch.sigmoid(x) - return x - - -class SplitAttentionConv2d(nn.Module): - """Split-Attention Conv2d in ResNeSt. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int | tuple[int]): Same as nn.Conv2d. - stride (int | tuple[int]): Same as nn.Conv2d. - padding (int | tuple[int]): Same as nn.Conv2d. - dilation (int | tuple[int]): Same as nn.Conv2d. - groups (int): Same as nn.Conv2d. - radix (int): Radix of SpltAtConv2d. Default: 2 - reduction_factor (int): Reduction factor of inter_channels. Default: 4. - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - dcn (dict): Config dict for DCN. Default: None. - """ - - def __init__(self, - in_channels, - channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - radix=2, - reduction_factor=4, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None): - super(SplitAttentionConv2d, self).__init__() - inter_channels = max(in_channels * radix // reduction_factor, 32) - self.radix = radix - self.groups = groups - self.channels = channels - self.with_dcn = dcn is not None - self.dcn = dcn - fallback_on_stride = False - if self.with_dcn: - fallback_on_stride = self.dcn.pop('fallback_on_stride', False) - if self.with_dcn and not fallback_on_stride: - assert conv_cfg is None, 'conv_cfg must be None for DCN' - conv_cfg = dcn - self.conv = build_conv_layer( - conv_cfg, - in_channels, - channels * radix, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups * radix, - bias=False) - self.norm0_name, norm0 = build_norm_layer( - norm_cfg, channels * radix, postfix=0) - self.add_module(self.norm0_name, norm0) - self.relu = nn.ReLU(inplace=True) - self.fc1 = build_conv_layer( - None, channels, inter_channels, 1, groups=self.groups) - self.norm1_name, norm1 = build_norm_layer( - norm_cfg, inter_channels, postfix=1) - self.add_module(self.norm1_name, norm1) - self.fc2 = build_conv_layer( - None, inter_channels, channels * radix, 1, groups=self.groups) - self.rsoftmax = RSoftmax(radix, groups) - - @property - def norm0(self): - """nn.Module: the normalization layer named "norm0" """ - return getattr(self, self.norm0_name) - - @property - def norm1(self): - """nn.Module: the normalization layer named "norm1" """ - return getattr(self, self.norm1_name) - - def forward(self, x): - x = self.conv(x) - x = self.norm0(x) - x = self.relu(x) - - batch, rchannel = x.shape[:2] - batch = x.size(0) - if self.radix > 1: - splits = x.view(batch, self.radix, -1, *x.shape[2:]) - gap = splits.sum(dim=1) - else: - gap = x - gap = F.adaptive_avg_pool2d(gap, 1) - gap = self.fc1(gap) - - gap = self.norm1(gap) - gap = self.relu(gap) - - atten = self.fc2(gap) - atten = self.rsoftmax(atten).view(batch, -1, 1, 1) - - if self.radix > 1: - attens = atten.view(batch, self.radix, -1, *atten.shape[2:]) - out = torch.sum(attens * splits, dim=1) - else: - out = atten * x - return out.contiguous() - - -class Bottleneck(_Bottleneck): - """Bottleneck block for ResNeSt. - - Args: - inplane (int): Input planes of this block. - planes (int): Middle planes of this block. - groups (int): Groups of conv2. - width_per_group (int): Width per group of conv2. 64x4d indicates - ``groups=64, width_per_group=4`` and 32x8d indicates - ``groups=32, width_per_group=8``. - radix (int): Radix of SpltAtConv2d. Default: 2 - reduction_factor (int): Reduction factor of inter_channels in - SplitAttentionConv2d. Default: 4. - avg_down_stride (bool): Whether to use average pool for stride in - Bottleneck. Default: True. - kwargs (dict): Key word arguments for base class. - """ - expansion = 4 - - def __init__(self, - inplanes, - planes, - groups=1, - base_width=4, - base_channels=64, - radix=2, - reduction_factor=4, - avg_down_stride=True, - **kwargs): - """Bottleneck block for ResNeSt.""" - super(Bottleneck, self).__init__(inplanes, planes, **kwargs) - - if groups == 1: - width = self.planes - else: - width = math.floor(self.planes * - (base_width / base_channels)) * groups - - self.avg_down_stride = avg_down_stride and self.conv2_stride > 1 - - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, width, postfix=1) - self.norm3_name, norm3 = build_norm_layer( - self.norm_cfg, self.planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - self.conv_cfg, - self.inplanes, - width, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - self.with_modulated_dcn = False - self.conv2 = SplitAttentionConv2d( - width, - width, - kernel_size=3, - stride=1 if self.avg_down_stride else self.conv2_stride, - padding=self.dilation, - dilation=self.dilation, - groups=groups, - radix=radix, - reduction_factor=reduction_factor, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - dcn=self.dcn) - delattr(self, self.norm2_name) - - if self.avg_down_stride: - self.avd_layer = nn.AvgPool2d(3, self.conv2_stride, padding=1) - - self.conv3 = build_conv_layer( - self.conv_cfg, - width, - self.planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - def forward(self, x): - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv1_plugin_names) - - out = self.conv2(out) - - if self.avg_down_stride: - out = self.avd_layer(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv2_plugin_names) - - out = self.conv3(out) - out = self.norm3(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv3_plugin_names) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -@BACKBONES.register_module() -class ResNeSt(ResNetV1d): - """ResNeSt backbone. - - This backbone is the implementation of `ResNeSt: - Split-Attention Networks `_. - - Args: - groups (int): Number of groups of Bottleneck. Default: 1 - base_width (int): Base width of Bottleneck. Default: 4 - radix (int): Radix of SpltAtConv2d. Default: 2 - reduction_factor (int): Reduction factor of inter_channels in - SplitAttentionConv2d. Default: 4. - avg_down_stride (bool): Whether to use average pool for stride in - Bottleneck. Default: True. - kwargs (dict): Keyword arguments for ResNet. - """ - - arch_settings = { - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)), - 200: (Bottleneck, (3, 24, 36, 3)) - } - - def __init__(self, - groups=1, - base_width=4, - radix=2, - reduction_factor=4, - avg_down_stride=True, - **kwargs): - self.groups = groups - self.base_width = base_width - self.radix = radix - self.reduction_factor = reduction_factor - self.avg_down_stride = avg_down_stride - super(ResNeSt, self).__init__(**kwargs) - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``.""" - return ResLayer( - groups=self.groups, - base_width=self.base_width, - base_channels=self.base_channels, - radix=self.radix, - reduction_factor=self.reduction_factor, - avg_down_stride=self.avg_down_stride, - **kwargs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/resnet.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/resnet.py deleted file mode 100644 index e8b961d5f..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/resnet.py +++ /dev/null @@ -1,714 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.utils.checkpoint as cp -from mmcv.cnn import build_conv_layer, build_norm_layer, build_plugin_layer -from mmcv.runner import BaseModule -from mmcv.utils.parrots_wrapper import _BatchNorm - -from ..builder import BACKBONES -from ..utils import ResLayer - - -class BasicBlock(BaseModule): - """Basic block for ResNet.""" - - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(BasicBlock, self).__init__(init_cfg) - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - 3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - self.add_module(self.norm1_name, norm1) - self.conv2 = build_conv_layer( - conv_cfg, planes, planes, 3, padding=1, bias=False) - self.add_module(self.norm2_name, norm2) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.norm2(out) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -class Bottleneck(BaseModule): - """Bottleneck block for ResNet. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if it is - "caffe", the stride-two layer is the first 1x1 conv layer. - """ - - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(Bottleneck, self).__init__(init_cfg) - assert style in ['pytorch', 'caffe'] - assert dcn is None or isinstance(dcn, dict) - assert plugins is None or isinstance(plugins, list) - if plugins is not None: - allowed_position = ['after_conv1', 'after_conv2', 'after_conv3'] - assert all(p['position'] in allowed_position for p in plugins) - - self.inplanes = inplanes - self.planes = planes - self.stride = stride - self.dilation = dilation - self.style = style - self.with_cp = with_cp - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.dcn = dcn - self.with_dcn = dcn is not None - self.plugins = plugins - self.with_plugins = plugins is not None - - if self.with_plugins: - # collect plugins for conv1/conv2/conv3 - self.after_conv1_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv1' - ] - self.after_conv2_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv2' - ] - self.after_conv3_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv3' - ] - - if self.style == 'pytorch': - self.conv1_stride = 1 - self.conv2_stride = stride - else: - self.conv1_stride = stride - self.conv2_stride = 1 - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - self.norm3_name, norm3 = build_norm_layer( - norm_cfg, planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - fallback_on_stride = False - if self.with_dcn: - fallback_on_stride = dcn.pop('fallback_on_stride', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = build_conv_layer( - conv_cfg, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - else: - assert self.conv_cfg is None, 'conv_cfg must be None for DCN' - self.conv2 = build_conv_layer( - dcn, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.add_module(self.norm2_name, norm2) - self.conv3 = build_conv_layer( - conv_cfg, - planes, - planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - - if self.with_plugins: - self.after_conv1_plugin_names = self.make_block_plugins( - planes, self.after_conv1_plugins) - self.after_conv2_plugin_names = self.make_block_plugins( - planes, self.after_conv2_plugins) - self.after_conv3_plugin_names = self.make_block_plugins( - planes * self.expansion, self.after_conv3_plugins) - - def make_block_plugins(self, in_channels, plugins): - """make plugins for block. - - Args: - in_channels (int): Input channels of plugin. - plugins (list[dict]): List of plugins cfg to build. - - Returns: - list[str]: List of the names of plugin. - """ - assert isinstance(plugins, list) - plugin_names = [] - for plugin in plugins: - plugin = plugin.copy() - name, layer = build_plugin_layer( - plugin, - in_channels=in_channels, - postfix=plugin.pop('postfix', '')) - assert not hasattr(self, name), f'duplicate plugin {name}' - self.add_module(name, layer) - plugin_names.append(name) - return plugin_names - - def forward_plugin(self, x, plugin_names): - """Forward function for plugins.""" - out = x - for name in plugin_names: - out = getattr(self, name)(x) - return out - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - @property - def norm3(self): - """nn.Module: normalization layer after the third convolution layer""" - return getattr(self, self.norm3_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv1_plugin_names) - - out = self.conv2(out) - out = self.norm2(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv2_plugin_names) - - out = self.conv3(out) - out = self.norm3(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv3_plugin_names) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -@BACKBONES.register_module() -class ResNet(BaseModule): - """ResNet backbone. - - This backbone is the improved implementation of `Deep Residual Learning - for Image Recognition `_. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - in_channels (int): Number of input image channels. Default: 3. - stem_channels (int): Number of stem channels. Default: 64. - base_channels (int): Number of base channels of res layer. Default: 64. - num_stages (int): Resnet stages, normally 4. Default: 4. - strides (Sequence[int]): Strides of the first block of each stage. - Default: (1, 2, 2, 2). - dilations (Sequence[int]): Dilation of each stage. - Default: (1, 1, 1, 1). - out_indices (Sequence[int]): Output from which stages. - Default: (0, 1, 2, 3). - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. Default: 'pytorch'. - deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv. - Default: False. - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False. - frozen_stages (int): Stages to be frozen (stop grad and set eval mode). - -1 means not freezing any parameters. Default: -1. - conv_cfg (dict | None): Dictionary to construct and config conv layer. - When conv_cfg is None, cfg will be set to dict(type='Conv2d'). - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN', requires_grad=True). - norm_eval (bool): Whether to set norm layers to eval mode, namely, - freeze running stats (mean and var). Note: Effect on Batch Norm - and its variants only. Default: False. - dcn (dict | None): Dictionary to construct and config DCN conv layer. - When dcn is not None, conv_cfg must be None. Default: None. - stage_with_dcn (Sequence[bool]): Whether to set DCN conv for each - stage. The length of stage_with_dcn is equal to num_stages. - Default: (False, False, False, False). - plugins (list[dict]): List of plugins for stages, each dict contains: - - - cfg (dict, required): Cfg dict to build plugin. - - - position (str, required): Position inside block to insert plugin, - options: 'after_conv1', 'after_conv2', 'after_conv3'. - - - stages (tuple[bool], optional): Stages to apply plugin, length - should be same as 'num_stages'. - Default: None. - multi_grid (Sequence[int]|None): Multi grid dilation rates of last - stage. Default: None. - contract_dilation (bool): Whether contract first dilation of each layer - Default: False. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - zero_init_residual (bool): Whether to use zero init for last norm layer - in resblocks to let them behave as identity. Default: True. - pretrained (str, optional): model pretrained path. Default: None. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Example: - >>> from mmseg.models import ResNet - >>> import torch - >>> self = ResNet(depth=18) - >>> self.eval() - >>> inputs = torch.rand(1, 3, 32, 32) - >>> level_outputs = self.forward(inputs) - >>> for level_out in level_outputs: - ... print(tuple(level_out.shape)) - (1, 64, 8, 8) - (1, 128, 4, 4) - (1, 256, 2, 2) - (1, 512, 1, 1) - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - in_channels=3, - stem_channels=64, - base_channels=64, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - deep_stem=False, - avg_down=False, - frozen_stages=-1, - conv_cfg=None, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=False, - dcn=None, - stage_with_dcn=(False, False, False, False), - plugins=None, - multi_grid=None, - contract_dilation=False, - with_cp=False, - zero_init_residual=True, - pretrained=None, - init_cfg=None): - super(ResNet, self).__init__(init_cfg) - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - - self.pretrained = pretrained - self.zero_init_residual = zero_init_residual - block_init_cfg = None - assert not (init_cfg and pretrained), \ - 'init_cfg and pretrained cannot be setting at the same time' - if isinstance(pretrained, str): - warnings.warn('DeprecationWarning: pretrained is a deprecated, ' - 'please use "init_cfg" instead') - self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - elif pretrained is None: - if init_cfg is None: - self.init_cfg = [ - dict(type='Kaiming', layer='Conv2d'), - dict( - type='Constant', - val=1, - layer=['_BatchNorm', 'GroupNorm']) - ] - block = self.arch_settings[depth][0] - if self.zero_init_residual: - if block is BasicBlock: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm2')) - elif block is Bottleneck: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm3')) - else: - raise TypeError('pretrained must be a str or None') - - self.depth = depth - self.stem_channels = stem_channels - self.base_channels = base_channels - self.num_stages = num_stages - assert num_stages >= 1 and num_stages <= 4 - self.strides = strides - self.dilations = dilations - assert len(strides) == len(dilations) == num_stages - self.out_indices = out_indices - assert max(out_indices) < num_stages - self.style = style - self.deep_stem = deep_stem - self.avg_down = avg_down - self.frozen_stages = frozen_stages - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.with_cp = with_cp - self.norm_eval = norm_eval - self.dcn = dcn - self.stage_with_dcn = stage_with_dcn - if dcn is not None: - assert len(stage_with_dcn) == num_stages - self.plugins = plugins - self.multi_grid = multi_grid - self.contract_dilation = contract_dilation - self.block, stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - self.inplanes = stem_channels - - self._make_stem_layer(in_channels, stem_channels) - - self.res_layers = [] - for i, num_blocks in enumerate(self.stage_blocks): - stride = strides[i] - dilation = dilations[i] - dcn = self.dcn if self.stage_with_dcn[i] else None - if plugins is not None: - stage_plugins = self.make_stage_plugins(plugins, i) - else: - stage_plugins = None - # multi grid is applied to last layer only - stage_multi_grid = multi_grid if i == len( - self.stage_blocks) - 1 else None - planes = base_channels * 2**i - res_layer = self.make_res_layer( - block=self.block, - inplanes=self.inplanes, - planes=planes, - num_blocks=num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - avg_down=self.avg_down, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - dcn=dcn, - plugins=stage_plugins, - multi_grid=stage_multi_grid, - contract_dilation=contract_dilation, - init_cfg=block_init_cfg) - self.inplanes = planes * self.block.expansion - layer_name = f'layer{i+1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self._freeze_stages() - - self.feat_dim = self.block.expansion * base_channels * 2**( - len(self.stage_blocks) - 1) - - def make_stage_plugins(self, plugins, stage_idx): - """make plugins for ResNet 'stage_idx'th stage . - - Currently we support to insert 'context_block', - 'empirical_attention_block', 'nonlocal_block' into the backbone like - ResNet/ResNeXt. They could be inserted after conv1/conv2/conv3 of - Bottleneck. - - An example of plugins format could be : - >>> plugins=[ - ... dict(cfg=dict(type='xxx', arg1='xxx'), - ... stages=(False, True, True, True), - ... position='after_conv2'), - ... dict(cfg=dict(type='yyy'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='1'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='2'), - ... stages=(True, True, True, True), - ... position='after_conv3') - ... ] - >>> self = ResNet(depth=18) - >>> stage_plugins = self.make_stage_plugins(plugins, 0) - >>> assert len(stage_plugins) == 3 - - Suppose 'stage_idx=0', the structure of blocks in the stage would be: - conv1-> conv2->conv3->yyy->zzz1->zzz2 - Suppose 'stage_idx=1', the structure of blocks in the stage would be: - conv1-> conv2->xxx->conv3->yyy->zzz1->zzz2 - - If stages is missing, the plugin would be applied to all stages. - - Args: - plugins (list[dict]): List of plugins cfg to build. The postfix is - required if multiple same type plugins are inserted. - stage_idx (int): Index of stage to build - - Returns: - list[dict]: Plugins for current stage - """ - stage_plugins = [] - for plugin in plugins: - plugin = plugin.copy() - stages = plugin.pop('stages', None) - assert stages is None or len(stages) == self.num_stages - # whether to insert plugin into current stage - if stages is None or stages[stage_idx]: - stage_plugins.append(plugin) - - return stage_plugins - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``.""" - return ResLayer(**kwargs) - - @property - def norm1(self): - """nn.Module: the normalization layer named "norm1" """ - return getattr(self, self.norm1_name) - - def _make_stem_layer(self, in_channels, stem_channels): - """Make stem layer for ResNet.""" - if self.deep_stem: - self.stem = nn.Sequential( - build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels // 2, - kernel_size=3, - stride=2, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels // 2, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels)[1], - nn.ReLU(inplace=True)) - else: - self.conv1 = build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels, - kernel_size=7, - stride=2, - padding=3, - bias=False) - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, stem_channels, postfix=1) - self.add_module(self.norm1_name, norm1) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - def _freeze_stages(self): - """Freeze stages param and norm stats.""" - if self.frozen_stages >= 0: - if self.deep_stem: - self.stem.eval() - for param in self.stem.parameters(): - param.requires_grad = False - else: - self.norm1.eval() - for m in [self.conv1, self.norm1]: - for param in m.parameters(): - param.requires_grad = False - - for i in range(1, self.frozen_stages + 1): - m = getattr(self, f'layer{i}') - m.eval() - for param in m.parameters(): - param.requires_grad = False - - def forward(self, x): - """Forward function.""" - if self.deep_stem: - x = self.stem(x) - else: - x = self.conv1(x) - x = self.norm1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - return tuple(outs) - - def train(self, mode=True): - """Convert the model into training mode while keep normalization layer - freezed.""" - super(ResNet, self).train(mode) - self._freeze_stages() - if mode and self.norm_eval: - for m in self.modules(): - # trick: eval have effect on BatchNorm only - if isinstance(m, _BatchNorm): - m.eval() - - -@BACKBONES.register_module() -class ResNetV1c(ResNet): - """ResNetV1c variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1c replaces the 7x7 conv in - the input stem with three 3x3 convs. For more details please refer to `Bag - of Tricks for Image Classification with Convolutional Neural Networks - `_. - """ - - def __init__(self, **kwargs): - super(ResNetV1c, self).__init__( - deep_stem=True, avg_down=False, **kwargs) - - -@BACKBONES.register_module() -class ResNetV1d(ResNet): - """ResNetV1d variant described in [1]_. - - Compared with default ResNet(ResNetV1b), ResNetV1d replaces the 7x7 conv in - the input stem with three 3x3 convs. And in the downsampling block, a 2x2 - avg_pool with stride 2 is added before conv, whose stride is changed to 1. - """ - - def __init__(self, **kwargs): - super(ResNetV1d, self).__init__( - deep_stem=True, avg_down=True, **kwargs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/resnext.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/resnext.py deleted file mode 100644 index 805c27bf3..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/resnext.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from mmcv.cnn import build_conv_layer, build_norm_layer - -from ..builder import BACKBONES -from ..utils import ResLayer -from .resnet import Bottleneck as _Bottleneck -from .resnet import ResNet - - -class Bottleneck(_Bottleneck): - """Bottleneck block for ResNeXt. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if it is - "caffe", the stride-two layer is the first 1x1 conv layer. - """ - - def __init__(self, - inplanes, - planes, - groups=1, - base_width=4, - base_channels=64, - **kwargs): - super(Bottleneck, self).__init__(inplanes, planes, **kwargs) - - if groups == 1: - width = self.planes - else: - width = math.floor(self.planes * - (base_width / base_channels)) * groups - - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, width, postfix=1) - self.norm2_name, norm2 = build_norm_layer( - self.norm_cfg, width, postfix=2) - self.norm3_name, norm3 = build_norm_layer( - self.norm_cfg, self.planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - self.conv_cfg, - self.inplanes, - width, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - fallback_on_stride = False - self.with_modulated_dcn = False - if self.with_dcn: - fallback_on_stride = self.dcn.pop('fallback_on_stride', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = build_conv_layer( - self.conv_cfg, - width, - width, - kernel_size=3, - stride=self.conv2_stride, - padding=self.dilation, - dilation=self.dilation, - groups=groups, - bias=False) - else: - assert self.conv_cfg is None, 'conv_cfg must be None for DCN' - self.conv2 = build_conv_layer( - self.dcn, - width, - width, - kernel_size=3, - stride=self.conv2_stride, - padding=self.dilation, - dilation=self.dilation, - groups=groups, - bias=False) - - self.add_module(self.norm2_name, norm2) - self.conv3 = build_conv_layer( - self.conv_cfg, - width, - self.planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - -@BACKBONES.register_module() -class ResNeXt(ResNet): - """ResNeXt backbone. - - This backbone is the implementation of `Aggregated - Residual Transformations for Deep Neural - Networks `_. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - in_channels (int): Number of input image channels. Normally 3. - num_stages (int): Resnet stages, normally 4. - groups (int): Group of resnext. - base_width (int): Base width of resnext. - strides (Sequence[int]): Strides of the first block of each stage. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - norm_cfg (dict): dictionary to construct and config norm layer. - norm_eval (bool): Whether to set norm layers to eval mode, namely, - freeze running stats (mean and var). Note: Effect on Batch Norm - and its variants only. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. - zero_init_residual (bool): whether to use zero init for last norm layer - in resblocks to let them behave as identity. - - Example: - >>> from mmseg.models import ResNeXt - >>> import torch - >>> self = ResNeXt(depth=50) - >>> self.eval() - >>> inputs = torch.rand(1, 3, 32, 32) - >>> level_outputs = self.forward(inputs) - >>> for level_out in level_outputs: - ... print(tuple(level_out.shape)) - (1, 256, 8, 8) - (1, 512, 4, 4) - (1, 1024, 2, 2) - (1, 2048, 1, 1) - """ - - arch_settings = { - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, groups=1, base_width=4, **kwargs): - self.groups = groups - self.base_width = base_width - super(ResNeXt, self).__init__(**kwargs) - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``""" - return ResLayer( - groups=self.groups, - base_width=self.base_width, - base_channels=self.base_channels, - **kwargs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/stdc.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/stdc.py deleted file mode 100644 index 04f2f7a2a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/stdc.py +++ /dev/null @@ -1,422 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/MichaelFan01/STDC-Seg.""" -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential - -from mmseg.ops import resize -from ..builder import BACKBONES, build_backbone -from .bisenetv1 import AttentionRefinementModule - - -class STDCModule(BaseModule): - """STDCModule. - - Args: - in_channels (int): The number of input channels. - out_channels (int): The number of output channels before scaling. - stride (int): The number of stride for the first conv layer. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): The activation config for conv layers. - num_convs (int): Numbers of conv layers. - fusion_type (str): Type of fusion operation. Default: 'add'. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - stride, - norm_cfg=None, - act_cfg=None, - num_convs=4, - fusion_type='add', - init_cfg=None): - super(STDCModule, self).__init__(init_cfg=init_cfg) - assert num_convs > 1 - assert fusion_type in ['add', 'cat'] - self.stride = stride - self.with_downsample = True if self.stride == 2 else False - self.fusion_type = fusion_type - - self.layers = ModuleList() - conv_0 = ConvModule( - in_channels, out_channels // 2, kernel_size=1, norm_cfg=norm_cfg) - - if self.with_downsample: - self.downsample = ConvModule( - out_channels // 2, - out_channels // 2, - kernel_size=3, - stride=2, - padding=1, - groups=out_channels // 2, - norm_cfg=norm_cfg, - act_cfg=None) - - if self.fusion_type == 'add': - self.layers.append(nn.Sequential(conv_0, self.downsample)) - self.skip = Sequential( - ConvModule( - in_channels, - in_channels, - kernel_size=3, - stride=2, - padding=1, - groups=in_channels, - norm_cfg=norm_cfg, - act_cfg=None), - ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=norm_cfg, - act_cfg=None)) - else: - self.layers.append(conv_0) - self.skip = nn.AvgPool2d(kernel_size=3, stride=2, padding=1) - else: - self.layers.append(conv_0) - - for i in range(1, num_convs): - out_factor = 2**(i + 1) if i != num_convs - 1 else 2**i - self.layers.append( - ConvModule( - out_channels // 2**i, - out_channels // out_factor, - kernel_size=3, - stride=1, - padding=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, inputs): - if self.fusion_type == 'add': - out = self.forward_add(inputs) - else: - out = self.forward_cat(inputs) - return out - - def forward_add(self, inputs): - layer_outputs = [] - x = inputs.clone() - for layer in self.layers: - x = layer(x) - layer_outputs.append(x) - if self.with_downsample: - inputs = self.skip(inputs) - - return torch.cat(layer_outputs, dim=1) + inputs - - def forward_cat(self, inputs): - x0 = self.layers[0](inputs) - layer_outputs = [x0] - for i, layer in enumerate(self.layers[1:]): - if i == 0: - if self.with_downsample: - x = layer(self.downsample(x0)) - else: - x = layer(x0) - else: - x = layer(x) - layer_outputs.append(x) - if self.with_downsample: - layer_outputs[0] = self.skip(x0) - return torch.cat(layer_outputs, dim=1) - - -class FeatureFusionModule(BaseModule): - """Feature Fusion Module. This module is different from FeatureFusionModule - in BiSeNetV1. It uses two ConvModules in `self.attention` whose inter - channel number is calculated by given `scale_factor`, while - FeatureFusionModule in BiSeNetV1 only uses one ConvModule in - `self.conv_atten`. - - Args: - in_channels (int): The number of input channels. - out_channels (int): The number of output channels. - scale_factor (int): The number of channel scale factor. - Default: 4. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): The activation config for conv layers. - Default: dict(type='ReLU'). - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - scale_factor=4, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(FeatureFusionModule, self).__init__(init_cfg=init_cfg) - channels = out_channels // scale_factor - self.conv0 = ConvModule( - in_channels, out_channels, 1, norm_cfg=norm_cfg, act_cfg=act_cfg) - self.attention = nn.Sequential( - nn.AdaptiveAvgPool2d((1, 1)), - ConvModule( - out_channels, - channels, - 1, - norm_cfg=None, - bias=False, - act_cfg=act_cfg), - ConvModule( - channels, - out_channels, - 1, - norm_cfg=None, - bias=False, - act_cfg=None), nn.Sigmoid()) - - def forward(self, spatial_inputs, context_inputs): - inputs = torch.cat([spatial_inputs, context_inputs], dim=1) - x = self.conv0(inputs) - attn = self.attention(x) - x_attn = x * attn - return x_attn + x - - -@BACKBONES.register_module() -class STDCNet(BaseModule): - """This backbone is the implementation of `Rethinking BiSeNet For Real-time - Semantic Segmentation `_. - - Args: - stdc_type (int): The type of backbone structure, - `STDCNet1` and`STDCNet2` denotes two main backbones in paper, - whose FLOPs is 813M and 1446M, respectively. - in_channels (int): The num of input_channels. - channels (tuple[int]): The output channels for each stage. - bottleneck_type (str): The type of STDC Module type, the value must - be 'add' or 'cat'. - norm_cfg (dict): Config dict for normalization layer. - act_cfg (dict): The activation config for conv layers. - num_convs (int): Numbers of conv layer at each STDC Module. - Default: 4. - with_final_conv (bool): Whether add a conv layer at the Module output. - Default: True. - pretrained (str, optional): Model pretrained path. Default: None. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Example: - >>> import torch - >>> stdc_type = 'STDCNet1' - >>> in_channels = 3 - >>> channels = (32, 64, 256, 512, 1024) - >>> bottleneck_type = 'cat' - >>> inputs = torch.rand(1, 3, 1024, 2048) - >>> self = STDCNet(stdc_type, in_channels, - ... channels, bottleneck_type).eval() - >>> outputs = self.forward(inputs) - >>> for i in range(len(outputs)): - ... print(f'outputs[{i}].shape = {outputs[i].shape}') - outputs[0].shape = torch.Size([1, 256, 128, 256]) - outputs[1].shape = torch.Size([1, 512, 64, 128]) - outputs[2].shape = torch.Size([1, 1024, 32, 64]) - """ - - arch_settings = { - 'STDCNet1': [(2, 1), (2, 1), (2, 1)], - 'STDCNet2': [(2, 1, 1, 1), (2, 1, 1, 1, 1), (2, 1, 1)] - } - - def __init__(self, - stdc_type, - in_channels, - channels, - bottleneck_type, - norm_cfg, - act_cfg, - num_convs=4, - with_final_conv=False, - pretrained=None, - init_cfg=None): - super(STDCNet, self).__init__(init_cfg=init_cfg) - assert stdc_type in self.arch_settings, \ - f'invalid structure {stdc_type} for STDCNet.' - assert bottleneck_type in ['add', 'cat'],\ - f'bottleneck_type must be `add` or `cat`, got {bottleneck_type}' - - assert len(channels) == 5,\ - f'invalid channels length {len(channels)} for STDCNet.' - - self.in_channels = in_channels - self.channels = channels - self.stage_strides = self.arch_settings[stdc_type] - self.prtrained = pretrained - self.num_convs = num_convs - self.with_final_conv = with_final_conv - - self.stages = ModuleList([ - ConvModule( - self.in_channels, - self.channels[0], - kernel_size=3, - stride=2, - padding=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg), - ConvModule( - self.channels[0], - self.channels[1], - kernel_size=3, - stride=2, - padding=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - ]) - # `self.num_shallow_features` is the number of shallow modules in - # `STDCNet`, which is noted as `Stage1` and `Stage2` in original paper. - # They are both not used for following modules like Attention - # Refinement Module and Feature Fusion Module. - # Thus they would be cut from `outs`. Please refer to Figure 4 - # of original paper for more details. - self.num_shallow_features = len(self.stages) - - for strides in self.stage_strides: - idx = len(self.stages) - 1 - self.stages.append( - self._make_stage(self.channels[idx], self.channels[idx + 1], - strides, norm_cfg, act_cfg, bottleneck_type)) - # After appending, `self.stages` is a ModuleList including several - # shallow modules and STDCModules. - # (len(self.stages) == - # self.num_shallow_features + len(self.stage_strides)) - if self.with_final_conv: - self.final_conv = ConvModule( - self.channels[-1], - max(1024, self.channels[-1]), - 1, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def _make_stage(self, in_channels, out_channels, strides, norm_cfg, - act_cfg, bottleneck_type): - layers = [] - for i, stride in enumerate(strides): - layers.append( - STDCModule( - in_channels if i == 0 else out_channels, - out_channels, - stride, - norm_cfg, - act_cfg, - num_convs=self.num_convs, - fusion_type=bottleneck_type)) - return Sequential(*layers) - - def forward(self, x): - outs = [] - for stage in self.stages: - x = stage(x) - outs.append(x) - if self.with_final_conv: - outs[-1] = self.final_conv(outs[-1]) - outs = outs[self.num_shallow_features:] - return tuple(outs) - - -@BACKBONES.register_module() -class STDCContextPathNet(BaseModule): - """STDCNet with Context Path. The `outs` below is a list of three feature - maps from deep to shallow, whose height and width is from small to big, - respectively. The biggest feature map of `outs` is outputted for - `STDCHead`, where Detail Loss would be calculated by Detail Ground-truth. - The other two feature maps are used for Attention Refinement Module, - respectively. Besides, the biggest feature map of `outs` and the last - output of Attention Refinement Module are concatenated for Feature Fusion - Module. Then, this fusion feature map `feat_fuse` would be outputted for - `decode_head`. More details please refer to Figure 4 of original paper. - - Args: - backbone_cfg (dict): Config dict for stdc backbone. - last_in_channels (tuple(int)), The number of channels of last - two feature maps from stdc backbone. Default: (1024, 512). - out_channels (int): The channels of output feature maps. - Default: 128. - ffm_cfg (dict): Config dict for Feature Fusion Module. Default: - `dict(in_channels=512, out_channels=256, scale_factor=4)`. - upsample_mode (str): Algorithm used for upsampling: - ``'nearest'`` | ``'linear'`` | ``'bilinear'`` | ``'bicubic'`` | - ``'trilinear'``. Default: ``'nearest'``. - align_corners (str): align_corners argument of F.interpolate. It - must be `None` if upsample_mode is ``'nearest'``. Default: None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Return: - outputs (tuple): The tuple of list of output feature map for - auxiliary heads and decoder head. - """ - - def __init__(self, - backbone_cfg, - last_in_channels=(1024, 512), - out_channels=128, - ffm_cfg=dict( - in_channels=512, out_channels=256, scale_factor=4), - upsample_mode='nearest', - align_corners=None, - norm_cfg=dict(type='BN'), - init_cfg=None): - super(STDCContextPathNet, self).__init__(init_cfg=init_cfg) - self.backbone = build_backbone(backbone_cfg) - self.arms = ModuleList() - self.convs = ModuleList() - for channels in last_in_channels: - self.arms.append(AttentionRefinementModule(channels, out_channels)) - self.convs.append( - ConvModule( - out_channels, - out_channels, - 3, - padding=1, - norm_cfg=norm_cfg)) - self.conv_avg = ConvModule( - last_in_channels[0], out_channels, 1, norm_cfg=norm_cfg) - - self.ffm = FeatureFusionModule(**ffm_cfg) - - self.upsample_mode = upsample_mode - self.align_corners = align_corners - - def forward(self, x): - outs = list(self.backbone(x)) - avg = F.adaptive_avg_pool2d(outs[-1], 1) - avg_feat = self.conv_avg(avg) - - feature_up = resize( - avg_feat, - size=outs[-1].shape[2:], - mode=self.upsample_mode, - align_corners=self.align_corners) - arms_out = [] - for i in range(len(self.arms)): - x_arm = self.arms[i](outs[len(outs) - 1 - i]) + feature_up - feature_up = resize( - x_arm, - size=outs[len(outs) - 1 - i - 1].shape[2:], - mode=self.upsample_mode, - align_corners=self.align_corners) - feature_up = self.convs[i](feature_up) - arms_out.append(feature_up) - - feat_fuse = self.ffm(outs[0], arms_out[1]) - - # The `outputs` has four feature maps. - # `outs[0]` is outputted for `STDCHead` auxiliary head. - # Two feature maps of `arms_out` are outputted for auxiliary head. - # `feat_fuse` is outputted for decoder head. - outputs = [outs[0]] + list(arms_out) + [feat_fuse] - return tuple(outputs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/timm_backbone.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/timm_backbone.py deleted file mode 100644 index 01b29fc5e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/backbones/timm_backbone.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -try: - import timm -except ImportError: - timm = None - -from mmcv.cnn.bricks.registry import NORM_LAYERS -from mmcv.runner import BaseModule - -from ..builder import BACKBONES - - -@BACKBONES.register_module() -class TIMMBackbone(BaseModule): - """Wrapper to use backbones from timm library. More details can be found in - `timm `_ . - - Args: - model_name (str): Name of timm model to instantiate. - pretrained (bool): Load pretrained weights if True. - checkpoint_path (str): Path of checkpoint to load after - model is initialized. - in_channels (int): Number of input image channels. Default: 3. - init_cfg (dict, optional): Initialization config dict - **kwargs: Other timm & model specific arguments. - """ - - def __init__( - self, - model_name, - features_only=True, - pretrained=True, - checkpoint_path='', - in_channels=3, - init_cfg=None, - **kwargs, - ): - if timm is None: - raise RuntimeError('timm is not installed') - super(TIMMBackbone, self).__init__(init_cfg) - if 'norm_layer' in kwargs: - kwargs['norm_layer'] = NORM_LAYERS.get(kwargs['norm_layer']) - self.timm_model = timm.create_model( - model_name=model_name, - features_only=features_only, - pretrained=pretrained, - in_chans=in_channels, - checkpoint_path=checkpoint_path, - **kwargs, - ) - - # Make unused parameters None - self.timm_model.global_pool = None - self.timm_model.fc = None - self.timm_model.classifier = None - - # Hack to use pretrained weights from timm - if pretrained or checkpoint_path: - self._is_init = True - - def forward(self, x): - features = self.timm_model(x) - return features diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/builder.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/builder.py deleted file mode 100644 index 5e18e4e64..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/builder.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -from mmcv.cnn import MODELS as MMCV_MODELS -from mmcv.cnn.bricks.registry import ATTENTION as MMCV_ATTENTION -from mmcv.utils import Registry - -MODELS = Registry('models', parent=MMCV_MODELS) -ATTENTION = Registry('attention', parent=MMCV_ATTENTION) - -BACKBONES = MODELS -NECKS = MODELS -HEADS = MODELS -LOSSES = MODELS -SEGMENTORS = MODELS - - -def build_backbone(cfg): - """Build backbone.""" - return BACKBONES.build(cfg) - - -def build_neck(cfg): - """Build neck.""" - return NECKS.build(cfg) - - -def build_head(cfg): - """Build head.""" - return HEADS.build(cfg) - - -def build_loss(cfg): - """Build loss.""" - return LOSSES.build(cfg) - - -def build_segmentor(cfg, train_cfg=None, test_cfg=None): - """Build segmentor.""" - if train_cfg is not None or test_cfg is not None: - warnings.warn( - 'train_cfg and test_cfg is deprecated, ' - 'please specify them in model', UserWarning) - assert cfg.get('train_cfg') is None or train_cfg is None, \ - 'train_cfg specified in both outer field and model field ' - assert cfg.get('test_cfg') is None or test_cfg is None, \ - 'test_cfg specified in both outer field and model field ' - return SEGMENTORS.build( - cfg, default_args=dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/__init__.py deleted file mode 100644 index f12706a11..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .aspp_head import ASPPHead -from .cc_head import CCHead -from .decode_head import BaseDecodeHead -from .fcn_head import FCNHead -from .stdc_head import STDCHead \ No newline at end of file diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/aspp_head.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/aspp_head.py deleted file mode 100644 index 7059aee96..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/aspp_head.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule - -from mmseg.ops import resize -from ..builder import HEADS -from .decode_head import BaseDecodeHead - - -class ASPPModule(nn.ModuleList): - """Atrous Spatial Pyramid Pooling (ASPP) Module. - - Args: - dilations (tuple[int]): Dilation rate of each layer. - in_channels (int): Input channels. - channels (int): Channels after modules, before conv_seg. - conv_cfg (dict|None): Config of conv layers. - norm_cfg (dict|None): Config of norm layers. - act_cfg (dict): Config of activation layers. - """ - - def __init__(self, dilations, in_channels, channels, conv_cfg, norm_cfg, - act_cfg): - super(ASPPModule, self).__init__() - self.dilations = dilations - self.in_channels = in_channels - self.channels = channels - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - for dilation in dilations: - self.append( - ConvModule( - self.in_channels, - self.channels, - 1 if dilation == 1 else 3, - dilation=dilation, - padding=0 if dilation == 1 else dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - - def forward(self, x): - """Forward function.""" - aspp_outs = [] - for aspp_module in self: - aspp_outs.append(aspp_module(x)) - - return aspp_outs - - -@HEADS.register_module() -class ASPPHead(BaseDecodeHead): - """Rethinking Atrous Convolution for Semantic Image Segmentation. - - This head is the implementation of `DeepLabV3 - `_. - - Args: - dilations (tuple[int]): Dilation rates for ASPP module. - Default: (1, 6, 12, 18). - """ - - def __init__(self, dilations=(1, 6, 12, 18), **kwargs): - super(ASPPHead, self).__init__(**kwargs) - assert isinstance(dilations, (list, tuple)) - self.dilations = dilations - self.image_pool = nn.Sequential( - nn.AdaptiveAvgPool2d(1), - ConvModule( - self.in_channels, - self.channels, - 1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - self.aspp_modules = ASPPModule( - dilations, - self.in_channels, - self.channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - self.bottleneck = ConvModule( - (len(dilations) + 1) * self.channels, - self.channels, - 3, - padding=1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - def _forward_feature(self, inputs): - """Forward function for feature maps before classifying each pixel with - ``self.cls_seg`` fc. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - feats (Tensor): A tensor of shape (batch_size, self.channels, - H, W) which is feature map for last layer of decoder head. - """ - x = self._transform_inputs(inputs) - aspp_outs = [ - resize( - self.image_pool(x), - size=x.size()[2:], - mode='bilinear', - align_corners=self.align_corners) - ] - aspp_outs.extend(self.aspp_modules(x)) - aspp_outs = torch.cat(aspp_outs, dim=1) - feats = self.bottleneck(aspp_outs) - return feats - - def forward(self, inputs): - """Forward function.""" - output = self._forward_feature(inputs) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/cc_head.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/cc_head.py deleted file mode 100644 index ed19eb46d..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/cc_head.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import HEADS -from .fcn_head import FCNHead - -try: - from mmcv.ops import CrissCrossAttention -except ModuleNotFoundError: - CrissCrossAttention = None - - -@HEADS.register_module() -class CCHead(FCNHead): - """CCNet: Criss-Cross Attention for Semantic Segmentation. - - This head is the implementation of `CCNet - `_. - - Args: - recurrence (int): Number of recurrence of Criss Cross Attention - module. Default: 2. - """ - - def __init__(self, recurrence=2, **kwargs): - if CrissCrossAttention is None: - raise RuntimeError('Please install mmcv-full for ' - 'CrissCrossAttention ops') - super(CCHead, self).__init__(num_convs=2, **kwargs) - self.recurrence = recurrence - self.cca = CrissCrossAttention(self.channels) - - def forward(self, inputs): - """Forward function.""" - x = self._transform_inputs(inputs) - output = self.convs[0](x) - for _ in range(self.recurrence): - output = self.cca(output) - output = self.convs[1](output) - if self.concat_input: - output = self.conv_cat(torch.cat([x, output], dim=1)) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/decode_head.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/decode_head.py deleted file mode 100644 index d08b1d0b6..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/decode_head.py +++ /dev/null @@ -1,266 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -import torch -import torch.nn as nn -from mmcv.runner import BaseModule, auto_fp16, force_fp32 - -from mmseg.core import build_pixel_sampler -from mmseg.ops import resize -from ..builder import build_loss -from ..losses import accuracy - - -class BaseDecodeHead(BaseModule, metaclass=ABCMeta): - """Base class for BaseDecodeHead. - - Args: - in_channels (int|Sequence[int]): Input channels. - channels (int): Channels after modules, before conv_seg. - num_classes (int): Number of classes. - dropout_ratio (float): Ratio of dropout layer. Default: 0.1. - conv_cfg (dict|None): Config of conv layers. Default: None. - norm_cfg (dict|None): Config of norm layers. Default: None. - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU') - in_index (int|Sequence[int]): Input feature index. Default: -1 - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - Default: None. - loss_decode (dict | Sequence[dict]): Config of decode loss. - The `loss_name` is property of corresponding loss function which - could be shown in training log. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - e.g. dict(type='CrossEntropyLoss'), - [dict(type='CrossEntropyLoss', loss_name='loss_ce'), - dict(type='DiceLoss', loss_name='loss_dice')] - Default: dict(type='CrossEntropyLoss'). - ignore_index (int | None): The label index to be ignored. When using - masked BCE loss, ignore_index should be set to None. Default: 255. - sampler (dict|None): The config of segmentation map sampler. - Default: None. - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - """ - - def __init__(self, - in_channels, - channels, - *, - num_classes, - dropout_ratio=0.1, - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - in_index=-1, - input_transform=None, - loss_decode=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0), - ignore_index=255, - sampler=None, - align_corners=False, - init_cfg=dict( - type='Normal', std=0.01, override=dict(name='conv_seg'))): - super(BaseDecodeHead, self).__init__(init_cfg) - self._init_inputs(in_channels, in_index, input_transform) - self.channels = channels - self.num_classes = num_classes - self.dropout_ratio = dropout_ratio - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.in_index = in_index - - self.ignore_index = ignore_index - self.align_corners = align_corners - - if isinstance(loss_decode, dict): - self.loss_decode = build_loss(loss_decode) - elif isinstance(loss_decode, (list, tuple)): - self.loss_decode = nn.ModuleList() - for loss in loss_decode: - self.loss_decode.append(build_loss(loss)) - else: - raise TypeError(f'loss_decode must be a dict or sequence of dict,\ - but got {type(loss_decode)}') - - if sampler is not None: - self.sampler = build_pixel_sampler(sampler, context=self) - else: - self.sampler = None - - self.conv_seg = nn.Conv2d(channels, num_classes, kernel_size=1) - if dropout_ratio > 0: - self.dropout = nn.Dropout2d(dropout_ratio) - else: - self.dropout = None - self.fp16_enabled = False - - def extra_repr(self): - """Extra repr.""" - s = f'input_transform={self.input_transform}, ' \ - f'ignore_index={self.ignore_index}, ' \ - f'align_corners={self.align_corners}' - return s - - def _init_inputs(self, in_channels, in_index, input_transform): - """Check and initialize input transforms. - - The in_channels, in_index and input_transform must match. - Specifically, when input_transform is None, only single feature map - will be selected. So in_channels and in_index must be of type int. - When input_transform - - Args: - in_channels (int|Sequence[int]): Input channels. - in_index (int|Sequence[int]): Input feature index. - input_transform (str|None): Transformation type of input features. - Options: 'resize_concat', 'multiple_select', None. - 'resize_concat': Multiple feature maps will be resize to the - same size as first one and than concat together. - Usually used in FCN head of HRNet. - 'multiple_select': Multiple feature maps will be bundle into - a list and passed into decode head. - None: Only one select feature map is allowed. - """ - - if input_transform is not None: - assert input_transform in ['resize_concat', 'multiple_select'] - self.input_transform = input_transform - self.in_index = in_index - if input_transform is not None: - assert isinstance(in_channels, (list, tuple)) - assert isinstance(in_index, (list, tuple)) - assert len(in_channels) == len(in_index) - if input_transform == 'resize_concat': - self.in_channels = sum(in_channels) - else: - self.in_channels = in_channels - else: - assert isinstance(in_channels, int) - assert isinstance(in_index, int) - self.in_channels = in_channels - - def _transform_inputs(self, inputs): - """Transform inputs for decoder. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - Tensor: The transformed inputs - """ - - if self.input_transform == 'resize_concat': - inputs = [inputs[i] for i in self.in_index] - upsampled_inputs = [ - resize( - input=x, - size=inputs[0].shape[2:], - mode='bilinear', - align_corners=self.align_corners) for x in inputs - ] - inputs = torch.cat(upsampled_inputs, dim=1) - elif self.input_transform == 'multiple_select': - inputs = [inputs[i] for i in self.in_index] - else: - inputs = inputs[self.in_index] - - return inputs - - @auto_fp16() - @abstractmethod - def forward(self, inputs): - """Placeholder of forward function.""" - pass - - def forward_train(self, inputs, img_metas, gt_semantic_seg, train_cfg): - """Forward function for training. - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - train_cfg (dict): The training config. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - seg_logits = self.forward(inputs) - losses = self.losses(seg_logits, gt_semantic_seg) - return losses - - def forward_test(self, inputs, img_metas, test_cfg): - """Forward function for testing. - - Args: - inputs (list[Tensor]): List of multi-level img features. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - test_cfg (dict): The testing config. - - Returns: - Tensor: Output segmentation map. - """ - return self.forward(inputs) - - def cls_seg(self, feat): - """Classify each pixel.""" - if self.dropout is not None: - feat = self.dropout(feat) - output = self.conv_seg(feat) - return output - - @force_fp32(apply_to=('seg_logit', )) - def losses(self, seg_logit, seg_label): - """Compute segmentation loss.""" - loss = dict() - seg_logit = resize( - input=seg_logit, - size=seg_label.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - if self.sampler is not None: - seg_weight = self.sampler.sample(seg_logit, seg_label) - else: - seg_weight = None - seg_label = seg_label.squeeze(1) - - if not isinstance(self.loss_decode, nn.ModuleList): - losses_decode = [self.loss_decode] - else: - losses_decode = self.loss_decode - for loss_decode in losses_decode: - if loss_decode.loss_name not in loss: - loss[loss_decode.loss_name] = loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - else: - loss[loss_decode.loss_name] += loss_decode( - seg_logit, - seg_label, - weight=seg_weight, - ignore_index=self.ignore_index) - - loss['acc_seg'] = accuracy( - seg_logit, seg_label, ignore_index=self.ignore_index) - return loss diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/fcn_head.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/fcn_head.py deleted file mode 100644 index fb79a0d7c..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/fcn_head.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule - -from ..builder import HEADS -from .decode_head import BaseDecodeHead - - -@HEADS.register_module() -class FCNHead(BaseDecodeHead): - """Fully Convolution Networks for Semantic Segmentation. - - This head is implemented of `FCNNet `_. - - Args: - num_convs (int): Number of convs in the head. Default: 2. - kernel_size (int): The kernel size for convs in the head. Default: 3. - concat_input (bool): Whether concat the input and output of convs - before classification layer. - dilation (int): The dilation rate for convs in the head. Default: 1. - """ - - def __init__(self, - num_convs=2, - kernel_size=3, - concat_input=True, - dilation=1, - **kwargs): - assert num_convs >= 0 and dilation > 0 and isinstance(dilation, int) - self.num_convs = num_convs - self.concat_input = concat_input - self.kernel_size = kernel_size - super(FCNHead, self).__init__(**kwargs) - if num_convs == 0: - assert self.in_channels == self.channels - - conv_padding = (kernel_size // 2) * dilation - convs = [] - convs.append( - ConvModule( - self.in_channels, - self.channels, - kernel_size=kernel_size, - padding=conv_padding, - dilation=dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - for i in range(num_convs - 1): - convs.append( - ConvModule( - self.channels, - self.channels, - kernel_size=kernel_size, - padding=conv_padding, - dilation=dilation, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg)) - if num_convs == 0: - self.convs = nn.Identity() - else: - self.convs = nn.Sequential(*convs) - if self.concat_input: - self.conv_cat = ConvModule( - self.in_channels + self.channels, - self.channels, - kernel_size=kernel_size, - padding=kernel_size // 2, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg) - - def _forward_feature(self, inputs): - """Forward function for feature maps before classifying each pixel with - ``self.cls_seg`` fc. - - Args: - inputs (list[Tensor]): List of multi-level img features. - - Returns: - feats (Tensor): A tensor of shape (batch_size, self.channels, - H, W) which is feature map for last layer of decoder head. - """ - x = self._transform_inputs(inputs) - feats = self.convs(x) - if self.concat_input: - feats = self.conv_cat(torch.cat([x, feats], dim=1)) - return feats - - def forward(self, inputs): - """Forward function.""" - output = self._forward_feature(inputs) - output = self.cls_seg(output) - return output diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/stdc_head.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/stdc_head.py deleted file mode 100644 index bddf1eb47..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/decode_heads/stdc_head.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn.functional as F - -from ..builder import HEADS -from .fcn_head import FCNHead - - -@HEADS.register_module() -class STDCHead(FCNHead): - """This head is the implementation of `Rethinking BiSeNet For Real-time - Semantic Segmentation `_. - - Args: - boundary_threshold (float): The threshold of calculating boundary. - Default: 0.1. - """ - - def __init__(self, boundary_threshold=0.1, **kwargs): - super(STDCHead, self).__init__(**kwargs) - self.boundary_threshold = boundary_threshold - # Using register buffer to make laplacian kernel on the same - # device of `seg_label`. - self.register_buffer( - 'laplacian_kernel', - torch.tensor([-1, -1, -1, -1, 8, -1, -1, -1, -1], - dtype=torch.float32, - requires_grad=False).reshape((1, 1, 3, 3))) - self.fusion_kernel = torch.nn.Parameter( - torch.tensor([[6. / 10], [3. / 10], [1. / 10]], - dtype=torch.float32).reshape(1, 3, 1, 1), - requires_grad=False) - - def losses(self, seg_logit, seg_label): - """Compute Detail Aggregation Loss.""" - # Note: The paper claims `fusion_kernel` is a trainable 1x1 conv - # parameters. However, it is a constant in original repo and other - # codebase because it would not be added into computation graph - # after threshold operation. - seg_label = seg_label.to(self.laplacian_kernel) - boundary_targets = F.conv2d( - seg_label, self.laplacian_kernel, padding=1) - boundary_targets = boundary_targets.clamp(min=0) - boundary_targets[boundary_targets > self.boundary_threshold] = 1 - boundary_targets[boundary_targets <= self.boundary_threshold] = 0 - - boundary_targets_x2 = F.conv2d( - seg_label, self.laplacian_kernel, stride=2, padding=1) - boundary_targets_x2 = boundary_targets_x2.clamp(min=0) - - boundary_targets_x4 = F.conv2d( - seg_label, self.laplacian_kernel, stride=4, padding=1) - boundary_targets_x4 = boundary_targets_x4.clamp(min=0) - - boundary_targets_x4_up = F.interpolate( - boundary_targets_x4, boundary_targets.shape[2:], mode='nearest') - boundary_targets_x2_up = F.interpolate( - boundary_targets_x2, boundary_targets.shape[2:], mode='nearest') - - boundary_targets_x2_up[ - boundary_targets_x2_up > self.boundary_threshold] = 1 - boundary_targets_x2_up[ - boundary_targets_x2_up <= self.boundary_threshold] = 0 - - boundary_targets_x4_up[ - boundary_targets_x4_up > self.boundary_threshold] = 1 - boundary_targets_x4_up[ - boundary_targets_x4_up <= self.boundary_threshold] = 0 - - boundary_targets_pyramids = torch.stack( - (boundary_targets, boundary_targets_x2_up, boundary_targets_x4_up), - dim=1) - - boundary_targets_pyramids = boundary_targets_pyramids.squeeze(2) - boundary_targets_pyramid = F.conv2d(boundary_targets_pyramids, - self.fusion_kernel) - - boundary_targets_pyramid[ - boundary_targets_pyramid > self.boundary_threshold] = 1 - boundary_targets_pyramid[ - boundary_targets_pyramid <= self.boundary_threshold] = 0 - - loss = super(STDCHead, self).losses(seg_logit, - boundary_targets_pyramid.long()) - return loss diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/__init__.py deleted file mode 100644 index fbc5b2d1b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .accuracy import Accuracy, accuracy -from .cross_entropy_loss import (CrossEntropyLoss, binary_cross_entropy, - cross_entropy, mask_cross_entropy) -from .dice_loss import DiceLoss -from .focal_loss import FocalLoss -from .lovasz_loss import LovaszLoss -from .utils import reduce_loss, weight_reduce_loss, weighted_loss - -__all__ = [ - 'accuracy', 'Accuracy', 'cross_entropy', 'binary_cross_entropy', - 'mask_cross_entropy', 'CrossEntropyLoss', 'reduce_loss', - 'weight_reduce_loss', 'weighted_loss', 'LovaszLoss', 'DiceLoss', - 'FocalLoss' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/accuracy.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/accuracy.py deleted file mode 100644 index 1d9e2d770..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/accuracy.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def accuracy(pred, target, topk=1, thresh=None, ignore_index=None): - """Calculate accuracy according to the prediction and target. - - Args: - pred (torch.Tensor): The model prediction, shape (N, num_class, ...) - target (torch.Tensor): The target of each prediction, shape (N, , ...) - ignore_index (int | None): The label index to be ignored. Default: None - topk (int | tuple[int], optional): If the predictions in ``topk`` - matches the target, the predictions will be regarded as - correct ones. Defaults to 1. - thresh (float, optional): If not None, predictions with scores under - this threshold are considered incorrect. Default to None. - - Returns: - float | tuple[float]: If the input ``topk`` is a single integer, - the function will return a single float as accuracy. If - ``topk`` is a tuple containing multiple integers, the - function will return a tuple containing accuracies of - each ``topk`` number. - """ - assert isinstance(topk, (int, tuple)) - if isinstance(topk, int): - topk = (topk, ) - return_single = True - else: - return_single = False - - maxk = max(topk) - if pred.size(0) == 0: - accu = [pred.new_tensor(0.) for i in range(len(topk))] - return accu[0] if return_single else accu - assert pred.ndim == target.ndim + 1 - assert pred.size(0) == target.size(0) - assert maxk <= pred.size(1), \ - f'maxk {maxk} exceeds pred dimension {pred.size(1)}' - pred_value, pred_label = pred.topk(maxk, dim=1) - # transpose to shape (maxk, N, ...) - pred_label = pred_label.transpose(0, 1) - correct = pred_label.eq(target.unsqueeze(0).expand_as(pred_label)) - if thresh is not None: - # Only prediction values larger than thresh are counted as correct - correct = correct & (pred_value > thresh).t() - if ignore_index is not None: - correct = correct[:, target != ignore_index] - res = [] - eps = torch.finfo(torch.float32).eps - for k in topk: - # Avoid causing ZeroDivisionError when all pixels - # of an image are ignored - correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) + eps - if ignore_index is not None: - total_num = target[target != ignore_index].numel() + eps - else: - total_num = target.numel() + eps - res.append(correct_k.mul_(100.0 / total_num)) - return res[0] if return_single else res - - -class Accuracy(nn.Module): - """Accuracy calculation module.""" - - def __init__(self, topk=(1, ), thresh=None, ignore_index=None): - """Module to calculate the accuracy. - - Args: - topk (tuple, optional): The criterion used to calculate the - accuracy. Defaults to (1,). - thresh (float, optional): If not None, predictions with scores - under this threshold are considered incorrect. Default to None. - """ - super().__init__() - self.topk = topk - self.thresh = thresh - self.ignore_index = ignore_index - - def forward(self, pred, target): - """Forward function to calculate accuracy. - - Args: - pred (torch.Tensor): Prediction of models. - target (torch.Tensor): Target for each prediction. - - Returns: - tuple[float]: The accuracies under different topk criterions. - """ - return accuracy(pred, target, self.topk, self.thresh, - self.ignore_index) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/cross_entropy_loss.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/cross_entropy_loss.py deleted file mode 100644 index 623fd58db..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/cross_entropy_loss.py +++ /dev/null @@ -1,296 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weight_reduce_loss - - -def cross_entropy(pred, - label, - weight=None, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=-100, - avg_non_ignore=False): - """cross_entropy. The wrapper function for :func:`F.cross_entropy` - - Args: - pred (torch.Tensor): The prediction with shape (N, 1). - label (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - Default: None. - class_weight (list[float], optional): The weight for each class. - Default: None. - reduction (str, optional): The method used to reduce the loss. - Options are 'none', 'mean' and 'sum'. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Default: None. - ignore_index (int): Specifies a target value that is ignored and - does not contribute to the input gradients. When - ``avg_non_ignore `` is ``True``, and the ``reduction`` is - ``''mean''``, the loss is averaged over non-ignored targets. - Defaults: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - """ - - # class_weight is a manual rescaling weight given to each class. - # If given, has to be a Tensor of size C element-wise losses - loss = F.cross_entropy( - pred, - label, - weight=class_weight, - reduction='none', - ignore_index=ignore_index) - - # apply weights and do the reduction - # average loss over non-ignored elements - # pytorch's official cross_entropy average loss over non-ignored elements - # refer to https://github.com/pytorch/pytorch/blob/56b43f4fec1f76953f15a627694d4bba34588969/torch/nn/functional.py#L2660 # noqa - if (avg_factor is None) and avg_non_ignore and reduction == 'mean': - avg_factor = label.numel() - (label == ignore_index).sum().item() - if weight is not None: - weight = weight.float() - loss = weight_reduce_loss( - loss, weight=weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def _expand_onehot_labels(labels, label_weights, target_shape, ignore_index): - """Expand onehot labels to match the size of prediction.""" - bin_labels = labels.new_zeros(target_shape) - valid_mask = (labels >= 0) & (labels != ignore_index) - inds = torch.nonzero(valid_mask, as_tuple=True) - - if inds[0].numel() > 0: - if labels.dim() == 3: - bin_labels[inds[0], labels[valid_mask], inds[1], inds[2]] = 1 - else: - bin_labels[inds[0], labels[valid_mask]] = 1 - - valid_mask = valid_mask.unsqueeze(1).expand(target_shape).float() - - if label_weights is None: - bin_label_weights = valid_mask - else: - bin_label_weights = label_weights.unsqueeze(1).expand(target_shape) - bin_label_weights = bin_label_weights * valid_mask - - return bin_labels, bin_label_weights, valid_mask - - -def binary_cross_entropy(pred, - label, - weight=None, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=-100, - avg_non_ignore=False, - **kwargs): - """Calculate the binary CrossEntropy loss. - - Args: - pred (torch.Tensor): The prediction with shape (N, 1). - label (torch.Tensor): The learning label of the prediction. - Note: In bce loss, label < 0 is invalid. - weight (torch.Tensor, optional): Sample-wise loss weight. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (int): The label index to be ignored. Default: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - - Returns: - torch.Tensor: The calculated loss - """ - if pred.size(1) == 1: - # For binary class segmentation, the shape of pred is - # [N, 1, H, W] and that of label is [N, H, W]. - # As the ignore_index often set as 255, so the - # binary class label check should mask out - # ignore_index - assert label[label != ignore_index].max() <= 1, \ - 'For pred with shape [N, 1, H, W], its label must have at ' \ - 'most 2 classes' - pred = pred.squeeze() - if pred.dim() != label.dim(): - assert (pred.dim() == 2 and label.dim() == 1) or ( - pred.dim() == 4 and label.dim() == 3), \ - 'Only pred shape [N, C], label shape [N] or pred shape [N, C, ' \ - 'H, W], label shape [N, H, W] are supported' - # `weight` returned from `_expand_onehot_labels` - # has been treated for valid (non-ignore) pixels - label, weight, valid_mask = _expand_onehot_labels( - label, weight, pred.shape, ignore_index) - else: - # should mask out the ignored elements - valid_mask = ((label >= 0) & (label != ignore_index)).float() - if weight is not None: - weight = weight * valid_mask - else: - weight = valid_mask - # average loss over non-ignored and valid elements - if reduction == 'mean' and avg_factor is None and avg_non_ignore: - avg_factor = valid_mask.sum().item() - - loss = F.binary_cross_entropy_with_logits( - pred, label.float(), pos_weight=class_weight, reduction='none') - # do the reduction for the weighted loss - loss = weight_reduce_loss( - loss, weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def mask_cross_entropy(pred, - target, - label, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=None, - **kwargs): - """Calculate the CrossEntropy loss for masks. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. - label (torch.Tensor): ``label`` indicates the class label of the mask' - corresponding object. This will be used to select the mask in the - of the class which the object belongs to when the mask prediction - if not class-agnostic. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (None): Placeholder, to be consistent with other loss. - Default: None. - - Returns: - torch.Tensor: The calculated loss - """ - assert ignore_index is None, 'BCE loss does not support ignore_index' - # TODO: handle these two reserved arguments - assert reduction == 'mean' and avg_factor is None - num_rois = pred.size()[0] - inds = torch.arange(0, num_rois, dtype=torch.long, device=pred.device) - pred_slice = pred[inds, label].squeeze(1) - return F.binary_cross_entropy_with_logits( - pred_slice, target, weight=class_weight, reduction='mean')[None] - - -@LOSSES.register_module() -class CrossEntropyLoss(nn.Module): - """CrossEntropyLoss. - - Args: - use_sigmoid (bool, optional): Whether the prediction uses sigmoid - of softmax. Defaults to False. - use_mask (bool, optional): Whether to use mask cross entropy loss. - Defaults to False. - reduction (str, optional): . Defaults to 'mean'. - Options are "none", "mean" and "sum". - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_ce'. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - `New in version 0.23.0.` - """ - - def __init__(self, - use_sigmoid=False, - use_mask=False, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_ce', - avg_non_ignore=False): - super(CrossEntropyLoss, self).__init__() - assert (use_sigmoid is False) or (use_mask is False) - self.use_sigmoid = use_sigmoid - self.use_mask = use_mask - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = get_class_weight(class_weight) - self.avg_non_ignore = avg_non_ignore - if not self.avg_non_ignore and self.reduction == 'mean': - warnings.warn( - 'Default ``avg_non_ignore`` is False, if you would like to ' - 'ignore the certain label and average loss over non-ignore ' - 'labels, which is the same with PyTorch official ' - 'cross_entropy, set ``avg_non_ignore=True``.') - - if self.use_sigmoid: - self.cls_criterion = binary_cross_entropy - elif self.use_mask: - self.cls_criterion = mask_cross_entropy - else: - self.cls_criterion = cross_entropy - self._loss_name = loss_name - - def extra_repr(self): - """Extra repr.""" - s = f'avg_non_ignore={self.avg_non_ignore}' - return s - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - ignore_index=-100, - **kwargs): - """Forward function.""" - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = cls_score.new_tensor(self.class_weight) - else: - class_weight = None - # Note: for BCE loss, label < 0 is invalid. - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - weight, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - avg_non_ignore=self.avg_non_ignore, - ignore_index=ignore_index, - **kwargs) - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/dice_loss.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/dice_loss.py deleted file mode 100644 index 79a3abfc2..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/dice_loss.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/LikeLy-Journey/SegmenTron/blob/master/ -segmentron/solver/loss.py (Apache-2.0 License)""" -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weighted_loss - - -@weighted_loss -def dice_loss(pred, - target, - valid_mask, - smooth=1, - exponent=2, - class_weight=None, - ignore_index=255): - assert pred.shape[0] == target.shape[0] - total_loss = 0 - num_classes = pred.shape[1] - for i in range(num_classes): - if i != ignore_index: - dice_loss = binary_dice_loss( - pred[:, i], - target[..., i], - valid_mask=valid_mask, - smooth=smooth, - exponent=exponent) - if class_weight is not None: - dice_loss *= class_weight[i] - total_loss += dice_loss - return total_loss / num_classes - - -@weighted_loss -def binary_dice_loss(pred, target, valid_mask, smooth=1, exponent=2, **kwards): - assert pred.shape[0] == target.shape[0] - pred = pred.reshape(pred.shape[0], -1) - target = target.reshape(target.shape[0], -1) - valid_mask = valid_mask.reshape(valid_mask.shape[0], -1) - - num = torch.sum(torch.mul(pred, target) * valid_mask, dim=1) * 2 + smooth - den = torch.sum(pred.pow(exponent) + target.pow(exponent), dim=1) + smooth - - return 1 - num / den - - -@LOSSES.register_module() -class DiceLoss(nn.Module): - """DiceLoss. - - This loss is proposed in `V-Net: Fully Convolutional Neural Networks for - Volumetric Medical Image Segmentation `_. - - Args: - smooth (float): A float number to smooth loss, and avoid NaN error. - Default: 1 - exponent (float): An float number to calculate denominator - value: \\sum{x^exponent} + \\sum{y^exponent}. Default: 2. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Default to 1.0. - ignore_index (int | None): The label index to be ignored. Default: 255. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_dice'. - """ - - def __init__(self, - smooth=1, - exponent=2, - reduction='mean', - class_weight=None, - loss_weight=1.0, - ignore_index=255, - loss_name='loss_dice', - **kwards): - super(DiceLoss, self).__init__() - self.smooth = smooth - self.exponent = exponent - self.reduction = reduction - self.class_weight = get_class_weight(class_weight) - self.loss_weight = loss_weight - self.ignore_index = ignore_index - self._loss_name = loss_name - - def forward(self, - pred, - target, - avg_factor=None, - reduction_override=None, - **kwards): - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = pred.new_tensor(self.class_weight) - else: - class_weight = None - - pred = F.softmax(pred, dim=1) - num_classes = pred.shape[1] - one_hot_target = F.one_hot( - torch.clamp(target.long(), 0, num_classes - 1), - num_classes=num_classes) - valid_mask = (target != self.ignore_index).long() - - loss = self.loss_weight * dice_loss( - pred, - one_hot_target, - valid_mask=valid_mask, - reduction=reduction, - avg_factor=avg_factor, - smooth=self.smooth, - exponent=self.exponent, - class_weight=class_weight, - ignore_index=self.ignore_index) - return loss - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/focal_loss.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/focal_loss.py deleted file mode 100644 index af1c711df..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/focal_loss.py +++ /dev/null @@ -1,327 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Modified from https://github.com/open-mmlab/mmdetection -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.ops import sigmoid_focal_loss as _sigmoid_focal_loss - -from ..builder import LOSSES -from .utils import weight_reduce_loss - - -# This method is used when cuda is not available -def py_sigmoid_focal_loss(pred, - target, - one_hot_target=None, - weight=None, - gamma=2.0, - alpha=0.5, - class_weight=None, - valid_mask=None, - reduction='mean', - avg_factor=None): - """PyTorch version of `Focal Loss `_. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the - number of classes - target (torch.Tensor): The learning label of the prediction with - shape (N, C) - one_hot_target (None): Placeholder. It should be None. - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal Loss. - Defaults to 0.5. - class_weight (list[float], optional): Weight of each class. - Defaults to None. - valid_mask (torch.Tensor, optional): A mask uses 1 to mark the valid - samples and uses 0 to mark the ignored samples. Default: None. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - if isinstance(alpha, list): - alpha = pred.new_tensor(alpha) - pred_sigmoid = pred.sigmoid() - target = target.type_as(pred) - one_minus_pt = (1 - pred_sigmoid) * target + pred_sigmoid * (1 - target) - focal_weight = (alpha * target + (1 - alpha) * - (1 - target)) * one_minus_pt.pow(gamma) - - loss = F.binary_cross_entropy_with_logits( - pred, target, reduction='none') * focal_weight - final_weight = torch.ones(1, pred.size(1)).type_as(loss) - if weight is not None: - if weight.shape != loss.shape and weight.size(0) == loss.size(0): - # For most cases, weight is of shape (N, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - assert weight.dim() == loss.dim() - final_weight = final_weight * weight - if class_weight is not None: - final_weight = final_weight * pred.new_tensor(class_weight) - if valid_mask is not None: - final_weight = final_weight * valid_mask - loss = weight_reduce_loss(loss, final_weight, reduction, avg_factor) - return loss - - -def sigmoid_focal_loss(pred, - target, - one_hot_target, - weight=None, - gamma=2.0, - alpha=0.5, - class_weight=None, - valid_mask=None, - reduction='mean', - avg_factor=None): - r"""A warpper of cuda version `Focal Loss - `_. - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. It's shape - should be (N, ) - one_hot_target (torch.Tensor): The learning label with shape (N, C) - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal Loss. - Defaults to 0.5. - class_weight (list[float], optional): Weight of each class. - Defaults to None. - valid_mask (torch.Tensor, optional): A mask uses 1 to mark the valid - samples and uses 0 to mark the ignored samples. Default: None. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - # Function.apply does not accept keyword arguments, so the decorator - # "weighted_loss" is not applicable - final_weight = torch.ones(1, pred.size(1)).type_as(pred) - if isinstance(alpha, list): - # _sigmoid_focal_loss doesn't accept alpha of list type. Therefore, if - # a list is given, we set the input alpha as 0.5. This means setting - # equal weight for foreground class and background class. By - # multiplying the loss by 2, the effect of setting alpha as 0.5 is - # undone. The alpha of type list is used to regulate the loss in the - # post-processing process. - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), - gamma, 0.5, None, 'none') * 2 - alpha = pred.new_tensor(alpha) - final_weight = final_weight * ( - alpha * one_hot_target + (1 - alpha) * (1 - one_hot_target)) - else: - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), - gamma, alpha, None, 'none') - if weight is not None: - if weight.shape != loss.shape and weight.size(0) == loss.size(0): - # For most cases, weight is of shape (N, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - assert weight.dim() == loss.dim() - final_weight = final_weight * weight - if class_weight is not None: - final_weight = final_weight * pred.new_tensor(class_weight) - if valid_mask is not None: - final_weight = final_weight * valid_mask - loss = weight_reduce_loss(loss, final_weight, reduction, avg_factor) - return loss - - -@LOSSES.register_module() -class FocalLoss(nn.Module): - - def __init__(self, - use_sigmoid=True, - gamma=2.0, - alpha=0.5, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_focal'): - """`Focal Loss `_ - Args: - use_sigmoid (bool, optional): Whether to the prediction is - used for sigmoid or softmax. Defaults to True. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float | list[float], optional): A balanced form for Focal - Loss. Defaults to 0.5. When a list is provided, the length - of the list should be equal to the number of classes. - Please be careful that this parameter is not the - class-wise weight but the weight of a binary classification - problem. This binary classification problem regards the - pixels which belong to one class as the foreground - and the other pixels as the background, each element in - the list is the weight of the corresponding foreground class. - The value of alpha or each element of alpha should be a float - in the interval [0, 1]. If you want to specify the class-wise - weight, please use `class_weight` parameter. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and - "sum". - class_weight (list[float], optional): Weight of each class. - Defaults to None. - loss_weight (float, optional): Weight of loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this - loss item to be included into the backward graph, `loss_` must - be the prefix of the name. Defaults to 'loss_focal'. - """ - super(FocalLoss, self).__init__() - assert use_sigmoid is True, \ - 'AssertionError: Only sigmoid focal loss supported now.' - assert reduction in ('none', 'mean', 'sum'), \ - "AssertionError: reduction should be 'none', 'mean' or " \ - "'sum'" - assert isinstance(alpha, (float, list)), \ - 'AssertionError: alpha should be of type float' - assert isinstance(gamma, float), \ - 'AssertionError: gamma should be of type float' - assert isinstance(loss_weight, float), \ - 'AssertionError: loss_weight should be of type float' - assert isinstance(loss_name, str), \ - 'AssertionError: loss_name should be of type str' - assert isinstance(class_weight, list) or class_weight is None, \ - 'AssertionError: class_weight must be None or of type list' - self.use_sigmoid = use_sigmoid - self.gamma = gamma - self.alpha = alpha - self.reduction = reduction - self.class_weight = class_weight - self.loss_weight = loss_weight - self._loss_name = loss_name - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - ignore_index=255, - **kwargs): - """Forward function. - - Args: - pred (torch.Tensor): The prediction with shape - (N, C) where C = number of classes, or - (N, C, d_1, d_2, ..., d_K) with K≥1 in the - case of K-dimensional loss. - target (torch.Tensor): The ground truth. If containing class - indices, shape (N) where each value is 0≤targets[i]≤C−1, - or (N, d_1, d_2, ..., d_K) with K≥1 in the case of - K-dimensional loss. If containing class probabilities, - same shape as the input. - weight (torch.Tensor, optional): The weight of loss for each - prediction. Defaults to None. - avg_factor (int, optional): Average factor that is used to - average the loss. Defaults to None. - reduction_override (str, optional): The reduction method used - to override the original reduction method of the loss. - Options are "none", "mean" and "sum". - ignore_index (int, optional): The label index to be ignored. - Default: 255 - Returns: - torch.Tensor: The calculated loss - """ - assert isinstance(ignore_index, int), \ - 'ignore_index must be of type int' - assert reduction_override in (None, 'none', 'mean', 'sum'), \ - "AssertionError: reduction should be 'none', 'mean' or " \ - "'sum'" - assert pred.shape == target.shape or \ - (pred.size(0) == target.size(0) and - pred.shape[2:] == target.shape[1:]), \ - "The shape of pred doesn't match the shape of target" - - original_shape = pred.shape - - # [B, C, d_1, d_2, ..., d_k] -> [C, B, d_1, d_2, ..., d_k] - pred = pred.transpose(0, 1) - # [C, B, d_1, d_2, ..., d_k] -> [C, N] - pred = pred.reshape(pred.size(0), -1) - # [C, N] -> [N, C] - pred = pred.transpose(0, 1).contiguous() - - if original_shape == target.shape: - # target with shape [B, C, d_1, d_2, ...] - # transform it's shape into [N, C] - # [B, C, d_1, d_2, ...] -> [C, B, d_1, d_2, ..., d_k] - target = target.transpose(0, 1) - # [C, B, d_1, d_2, ..., d_k] -> [C, N] - target = target.reshape(target.size(0), -1) - # [C, N] -> [N, C] - target = target.transpose(0, 1).contiguous() - else: - # target with shape [B, d_1, d_2, ...] - # transform it's shape into [N, ] - target = target.view(-1).contiguous() - valid_mask = (target != ignore_index).view(-1, 1) - # avoid raising error when using F.one_hot() - target = torch.where(target == ignore_index, target.new_tensor(0), - target) - - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.use_sigmoid: - num_classes = pred.size(1) - if torch.cuda.is_available() and pred.is_cuda: - if target.dim() == 1: - one_hot_target = F.one_hot(target, num_classes=num_classes) - else: - one_hot_target = target - target = target.argmax(dim=1) - valid_mask = (target != ignore_index).view(-1, 1) - calculate_loss_func = sigmoid_focal_loss - else: - one_hot_target = None - if target.dim() == 1: - target = F.one_hot(target, num_classes=num_classes) - else: - valid_mask = (target.argmax(dim=1) != ignore_index).view( - -1, 1) - calculate_loss_func = py_sigmoid_focal_loss - - loss_cls = self.loss_weight * calculate_loss_func( - pred, - target, - one_hot_target, - weight, - gamma=self.gamma, - alpha=self.alpha, - class_weight=self.class_weight, - valid_mask=valid_mask, - reduction=reduction, - avg_factor=avg_factor) - - if reduction == 'none': - # [N, C] -> [C, N] - loss_cls = loss_cls.transpose(0, 1) - # [C, N] -> [C, B, d1, d2, ...] - # original_shape: [B, C, d1, d2, ...] - loss_cls = loss_cls.reshape(original_shape[1], - original_shape[0], - *original_shape[2:]) - # [C, B, d1, d2, ...] -> [B, C, d1, d2, ...] - loss_cls = loss_cls.transpose(0, 1).contiguous() - else: - raise NotImplementedError - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/lovasz_loss.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/lovasz_loss.py deleted file mode 100644 index 2bb0fad39..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/lovasz_loss.py +++ /dev/null @@ -1,323 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/bermanmaxim/LovaszSoftmax/blob/master/pytor -ch/lovasz_losses.py Lovasz-Softmax and Jaccard hinge loss in PyTorch Maxim -Berman 2018 ESAT-PSI KU Leuven (MIT License)""" - -import mmcv -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import get_class_weight, weight_reduce_loss - - -def lovasz_grad(gt_sorted): - """Computes gradient of the Lovasz extension w.r.t sorted errors. - - See Alg. 1 in paper. - """ - p = len(gt_sorted) - gts = gt_sorted.sum() - intersection = gts - gt_sorted.float().cumsum(0) - union = gts + (1 - gt_sorted).float().cumsum(0) - jaccard = 1. - intersection / union - if p > 1: # cover 1-pixel case - jaccard[1:p] = jaccard[1:p] - jaccard[0:-1] - return jaccard - - -def flatten_binary_logits(logits, labels, ignore_index=None): - """Flattens predictions in the batch (binary case) Remove labels equal to - 'ignore_index'.""" - logits = logits.view(-1) - labels = labels.view(-1) - if ignore_index is None: - return logits, labels - valid = (labels != ignore_index) - vlogits = logits[valid] - vlabels = labels[valid] - return vlogits, vlabels - - -def flatten_probs(probs, labels, ignore_index=None): - """Flattens predictions in the batch.""" - if probs.dim() == 3: - # assumes output of a sigmoid layer - B, H, W = probs.size() - probs = probs.view(B, 1, H, W) - B, C, H, W = probs.size() - probs = probs.permute(0, 2, 3, 1).contiguous().view(-1, C) # B*H*W, C=P,C - labels = labels.view(-1) - if ignore_index is None: - return probs, labels - valid = (labels != ignore_index) - vprobs = probs[valid.nonzero().squeeze()] - vlabels = labels[valid] - return vprobs, vlabels - - -def lovasz_hinge_flat(logits, labels): - """Binary Lovasz hinge loss. - - Args: - logits (torch.Tensor): [P], logits at each prediction - (between -infty and +infty). - labels (torch.Tensor): [P], binary ground truth labels (0 or 1). - - Returns: - torch.Tensor: The calculated loss. - """ - if len(labels) == 0: - # only void pixels, the gradients should be 0 - return logits.sum() * 0. - signs = 2. * labels.float() - 1. - errors = (1. - logits * signs) - errors_sorted, perm = torch.sort(errors, dim=0, descending=True) - perm = perm.data - gt_sorted = labels[perm] - grad = lovasz_grad(gt_sorted) - loss = torch.dot(F.relu(errors_sorted), grad) - return loss - - -def lovasz_hinge(logits, - labels, - classes='present', - per_image=False, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=255): - """Binary Lovasz hinge loss. - - Args: - logits (torch.Tensor): [B, H, W], logits at each pixel - (between -infty and +infty). - labels (torch.Tensor): [B, H, W], binary ground truth masks (0 or 1). - classes (str | list[int], optional): Placeholder, to be consistent with - other loss. Default: None. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - class_weight (list[float], optional): Placeholder, to be consistent - with other loss. Default: None. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. This parameter only works when per_image is True. - Default: None. - ignore_index (int | None): The label index to be ignored. Default: 255. - - Returns: - torch.Tensor: The calculated loss. - """ - if per_image: - loss = [ - lovasz_hinge_flat(*flatten_binary_logits( - logit.unsqueeze(0), label.unsqueeze(0), ignore_index)) - for logit, label in zip(logits, labels) - ] - loss = weight_reduce_loss( - torch.stack(loss), None, reduction, avg_factor) - else: - loss = lovasz_hinge_flat( - *flatten_binary_logits(logits, labels, ignore_index)) - return loss - - -def lovasz_softmax_flat(probs, labels, classes='present', class_weight=None): - """Multi-class Lovasz-Softmax loss. - - Args: - probs (torch.Tensor): [P, C], class probabilities at each prediction - (between 0 and 1). - labels (torch.Tensor): [P], ground truth labels (between 0 and C - 1). - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - class_weight (list[float], optional): The weight for each class. - Default: None. - - Returns: - torch.Tensor: The calculated loss. - """ - if probs.numel() == 0: - # only void pixels, the gradients should be 0 - return probs * 0. - C = probs.size(1) - losses = [] - class_to_sum = list(range(C)) if classes in ['all', 'present'] else classes - for c in class_to_sum: - fg = (labels == c).float() # foreground for class c - if (classes == 'present' and fg.sum() == 0): - continue - if C == 1: - if len(classes) > 1: - raise ValueError('Sigmoid output possible only with 1 class') - class_pred = probs[:, 0] - else: - class_pred = probs[:, c] - errors = (fg - class_pred).abs() - errors_sorted, perm = torch.sort(errors, 0, descending=True) - perm = perm.data - fg_sorted = fg[perm] - loss = torch.dot(errors_sorted, lovasz_grad(fg_sorted)) - if class_weight is not None: - loss *= class_weight[c] - losses.append(loss) - return torch.stack(losses).mean() - - -def lovasz_softmax(probs, - labels, - classes='present', - per_image=False, - class_weight=None, - reduction='mean', - avg_factor=None, - ignore_index=255): - """Multi-class Lovasz-Softmax loss. - - Args: - probs (torch.Tensor): [B, C, H, W], class probabilities at each - prediction (between 0 and 1). - labels (torch.Tensor): [B, H, W], ground truth labels (between 0 and - C - 1). - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - class_weight (list[float], optional): The weight for each class. - Default: None. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. This parameter only works when per_image is True. - Default: None. - ignore_index (int | None): The label index to be ignored. Default: 255. - - Returns: - torch.Tensor: The calculated loss. - """ - - if per_image: - loss = [ - lovasz_softmax_flat( - *flatten_probs( - prob.unsqueeze(0), label.unsqueeze(0), ignore_index), - classes=classes, - class_weight=class_weight) - for prob, label in zip(probs, labels) - ] - loss = weight_reduce_loss( - torch.stack(loss), None, reduction, avg_factor) - else: - loss = lovasz_softmax_flat( - *flatten_probs(probs, labels, ignore_index), - classes=classes, - class_weight=class_weight) - return loss - - -@LOSSES.register_module() -class LovaszLoss(nn.Module): - """LovaszLoss. - - This loss is proposed in `The Lovasz-Softmax loss: A tractable surrogate - for the optimization of the intersection-over-union measure in neural - networks `_. - - Args: - loss_type (str, optional): Binary or multi-class loss. - Default: 'multi_class'. Options are "binary" and "multi_class". - classes (str | list[int], optional): Classes chosen to calculate loss. - 'all' for all classes, 'present' for classes present in labels, or - a list of classes to average. Default: 'present'. - per_image (bool, optional): If per_image is True, compute the loss per - image instead of per batch. Default: False. - reduction (str, optional): The method used to reduce the loss. Options - are "none", "mean" and "sum". This parameter only works when - per_image is True. Default: 'mean'. - class_weight (list[float] | str, optional): Weight of each class. If in - str format, read them from a file. Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - loss_name (str, optional): Name of the loss item. If you want this loss - item to be included into the backward graph, `loss_` must be the - prefix of the name. Defaults to 'loss_lovasz'. - """ - - def __init__(self, - loss_type='multi_class', - classes='present', - per_image=False, - reduction='mean', - class_weight=None, - loss_weight=1.0, - loss_name='loss_lovasz'): - super(LovaszLoss, self).__init__() - assert loss_type in ('binary', 'multi_class'), "loss_type should be \ - 'binary' or 'multi_class'." - - if loss_type == 'binary': - self.cls_criterion = lovasz_hinge - else: - self.cls_criterion = lovasz_softmax - assert classes in ('all', 'present') or mmcv.is_list_of(classes, int) - if not per_image: - assert reduction == 'none', "reduction should be 'none' when \ - per_image is False." - - self.classes = classes - self.per_image = per_image - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = get_class_weight(class_weight) - self._loss_name = loss_name - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - """Forward function.""" - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.class_weight is not None: - class_weight = cls_score.new_tensor(self.class_weight) - else: - class_weight = None - - # if multi-class loss, transform logits to probs - if self.cls_criterion == lovasz_softmax: - cls_score = F.softmax(cls_score, dim=1) - - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - self.classes, - self.per_image, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss_cls - - @property - def loss_name(self): - """Loss Name. - - This function must be implemented and will return the name of this - loss function. This name will be used to combine different loss items - by simple sum operation. In addition, if you want this loss item to be - included into the backward graph, `loss_` must be the prefix of the - name. - Returns: - str: The name of this loss item. - """ - return self._loss_name diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/utils.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/utils.py deleted file mode 100644 index 621f57c74..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/losses/utils.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import mmcv -import numpy as np -import torch -import torch.nn.functional as F - - -def get_class_weight(class_weight): - """Get class weight for loss function. - - Args: - class_weight (list[float] | str | None): If class_weight is a str, - take it as a file name and read from it. - """ - if isinstance(class_weight, str): - # take it as a file path - if class_weight.endswith('.npy'): - class_weight = np.load(class_weight) - else: - # pkl, json or yaml - class_weight = mmcv.load(class_weight) - - return class_weight - - -def reduce_loss(loss, reduction): - """Reduce loss as specified. - - Args: - loss (Tensor): Elementwise loss tensor. - reduction (str): Options are "none", "mean" and "sum". - - Return: - Tensor: Reduced loss tensor. - """ - reduction_enum = F._Reduction.get_enum(reduction) - # none: 0, elementwise_mean:1, sum: 2 - if reduction_enum == 0: - return loss - elif reduction_enum == 1: - return loss.mean() - elif reduction_enum == 2: - return loss.sum() - - -def weight_reduce_loss(loss, weight=None, reduction='mean', avg_factor=None): - """Apply element-wise weight and reduce loss. - - Args: - loss (Tensor): Element-wise loss. - weight (Tensor): Element-wise weights. - reduction (str): Same as built-in losses of PyTorch. - avg_factor (float): Average factor when computing the mean of losses. - - Returns: - Tensor: Processed loss values. - """ - # if weight is specified, apply element-wise weight - if weight is not None: - assert weight.dim() == loss.dim() - if weight.dim() > 1: - assert weight.size(1) == 1 or weight.size(1) == loss.size(1) - loss = loss * weight - - # if avg_factor is not specified, just reduce the loss - if avg_factor is None: - loss = reduce_loss(loss, reduction) - else: - # if reduction is mean, then average the loss by avg_factor - if reduction == 'mean': - # Avoid causing ZeroDivisionError when avg_factor is 0.0, - # i.e., all labels of an image belong to ignore index. - eps = torch.finfo(torch.float32).eps - loss = loss.sum() / (avg_factor + eps) - # if reduction is 'none', then do nothing, otherwise raise an error - elif reduction != 'none': - raise ValueError('avg_factor can not be used with reduction="sum"') - return loss - - -def weighted_loss(loss_func): - """Create a weighted version of a given loss function. - - To use this decorator, the loss function must have the signature like - `loss_func(pred, target, **kwargs)`. The function only needs to compute - element-wise loss without any reduction. This decorator will add weight - and reduction arguments to the function. The decorated function will have - the signature like `loss_func(pred, target, weight=None, reduction='mean', - avg_factor=None, **kwargs)`. - - :Example: - - >>> import torch - >>> @weighted_loss - >>> def l1_loss(pred, target): - >>> return (pred - target).abs() - - >>> pred = torch.Tensor([0, 2, 3]) - >>> target = torch.Tensor([1, 1, 1]) - >>> weight = torch.Tensor([1, 0, 1]) - - >>> l1_loss(pred, target) - tensor(1.3333) - >>> l1_loss(pred, target, weight) - tensor(1.) - >>> l1_loss(pred, target, reduction='none') - tensor([1., 1., 2.]) - >>> l1_loss(pred, target, weight, avg_factor=2) - tensor(1.5000) - """ - - @functools.wraps(loss_func) - def wrapper(pred, - target, - weight=None, - reduction='mean', - avg_factor=None, - **kwargs): - # get element-wise loss - loss = loss_func(pred, target, **kwargs) - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - return wrapper diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/__init__.py deleted file mode 100644 index ff03186a9..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .featurepyramid import Feature2Pyramid -from .fpn import FPN -from .ic_neck import ICNeck -from .jpu import JPU -from .mla_neck import MLANeck -from .multilevel_neck import MultiLevelNeck - -__all__ = [ - 'FPN', 'MultiLevelNeck', 'MLANeck', 'ICNeck', 'JPU', 'Feature2Pyramid' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/featurepyramid.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/featurepyramid.py deleted file mode 100644 index 82a00ceb1..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/featurepyramid.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import build_norm_layer - -from ..builder import NECKS - - -@NECKS.register_module() -class Feature2Pyramid(nn.Module): - """Feature2Pyramid. - - A neck structure connect ViT backbone and decoder_heads. - - Args: - embed_dims (int): Embedding dimension. - rescales (list[float]): Different sampling multiples were - used to obtain pyramid features. Default: [4, 2, 1, 0.5]. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='SyncBN', requires_grad=True). - """ - - def __init__(self, - embed_dim, - rescales=[4, 2, 1, 0.5], - norm_cfg=dict(type='SyncBN', requires_grad=True)): - super(Feature2Pyramid, self).__init__() - self.rescales = rescales - self.upsample_4x = None - for k in self.rescales: - if k == 4: - self.upsample_4x = nn.Sequential( - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2), - build_norm_layer(norm_cfg, embed_dim)[1], - nn.GELU(), - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2), - ) - elif k == 2: - self.upsample_2x = nn.Sequential( - nn.ConvTranspose2d( - embed_dim, embed_dim, kernel_size=2, stride=2)) - elif k == 1: - self.identity = nn.Identity() - elif k == 0.5: - self.downsample_2x = nn.MaxPool2d(kernel_size=2, stride=2) - elif k == 0.25: - self.downsample_4x = nn.MaxPool2d(kernel_size=4, stride=4) - else: - raise KeyError(f'invalid {k} for feature2pyramid') - - def forward(self, inputs): - assert len(inputs) == len(self.rescales) - outputs = [] - if self.upsample_4x is not None: - ops = [ - self.upsample_4x, self.upsample_2x, self.identity, - self.downsample_2x - ] - else: - ops = [ - self.upsample_2x, self.identity, self.downsample_2x, - self.downsample_4x - ] - for i in range(len(inputs)): - outputs.append(ops[i](inputs[i])) - return tuple(outputs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/fpn.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/fpn.py deleted file mode 100644 index 6997de9d4..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/fpn.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule, auto_fp16 - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class FPN(BaseModule): - """Feature Pyramid Network. - - This neck is the implementation of `Feature Pyramid Networks for Object - Detection `_. - - Args: - in_channels (list[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - num_outs (int): Number of output scales. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - add_extra_convs (bool | str): If bool, it decides whether to add conv - layers on top of the original feature maps. Default to False. - If True, its actual mode is specified by `extra_convs_on_inputs`. - If str, it specifies the source feature map of the extra convs. - Only the following options are allowed - - - 'on_input': Last feat map of neck inputs (i.e. backbone feature). - - 'on_lateral': Last feature map after lateral convs. - - 'on_output': The last output feature map after fpn convs. - extra_convs_on_inputs (bool, deprecated): Whether to apply extra convs - on the original feature from the backbone. If True, - it is equivalent to `add_extra_convs='on_input'`. If False, it is - equivalent to set `add_extra_convs='on_output'`. Default to True. - relu_before_extra_convs (bool): Whether to apply relu before the extra - conv. Default: False. - no_norm_on_lateral (bool): Whether to apply norm on lateral. - Default: False. - conv_cfg (dict): Config dict for convolution layer. Default: None. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - upsample_cfg (dict): Config dict for interpolate layer. - Default: dict(mode='nearest'). - init_cfg (dict or list[dict], optional): Initialization config dict. - - Example: - >>> import torch - >>> in_channels = [2, 3, 5, 7] - >>> scales = [340, 170, 84, 43] - >>> inputs = [torch.rand(1, c, s, s) - ... for c, s in zip(in_channels, scales)] - >>> self = FPN(in_channels, 11, len(in_channels)).eval() - >>> outputs = self.forward(inputs) - >>> for i in range(len(outputs)): - ... print(f'outputs[{i}].shape = {outputs[i].shape}') - outputs[0].shape = torch.Size([1, 11, 340, 340]) - outputs[1].shape = torch.Size([1, 11, 170, 170]) - outputs[2].shape = torch.Size([1, 11, 84, 84]) - outputs[3].shape = torch.Size([1, 11, 43, 43]) - """ - - def __init__(self, - in_channels, - out_channels, - num_outs, - start_level=0, - end_level=-1, - add_extra_convs=False, - extra_convs_on_inputs=False, - relu_before_extra_convs=False, - no_norm_on_lateral=False, - conv_cfg=None, - norm_cfg=None, - act_cfg=None, - upsample_cfg=dict(mode='nearest'), - init_cfg=dict( - type='Xavier', layer='Conv2d', distribution='uniform')): - super(FPN, self).__init__(init_cfg) - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.num_ins = len(in_channels) - self.num_outs = num_outs - self.relu_before_extra_convs = relu_before_extra_convs - self.no_norm_on_lateral = no_norm_on_lateral - self.fp16_enabled = False - self.upsample_cfg = upsample_cfg.copy() - - if end_level == -1: - self.backbone_end_level = self.num_ins - assert num_outs >= self.num_ins - start_level - else: - # if end_level < inputs, no extra level is allowed - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - assert num_outs == end_level - start_level - self.start_level = start_level - self.end_level = end_level - self.add_extra_convs = add_extra_convs - assert isinstance(add_extra_convs, (str, bool)) - if isinstance(add_extra_convs, str): - # Extra_convs_source choices: 'on_input', 'on_lateral', 'on_output' - assert add_extra_convs in ('on_input', 'on_lateral', 'on_output') - elif add_extra_convs: # True - if extra_convs_on_inputs: - # For compatibility with previous release - # TODO: deprecate `extra_convs_on_inputs` - self.add_extra_convs = 'on_input' - else: - self.add_extra_convs = 'on_output' - - self.lateral_convs = nn.ModuleList() - self.fpn_convs = nn.ModuleList() - - for i in range(self.start_level, self.backbone_end_level): - l_conv = ConvModule( - in_channels[i], - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg if not self.no_norm_on_lateral else None, - act_cfg=act_cfg, - inplace=False) - fpn_conv = ConvModule( - out_channels, - out_channels, - 3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - - self.lateral_convs.append(l_conv) - self.fpn_convs.append(fpn_conv) - - # add extra conv layers (e.g., RetinaNet) - extra_levels = num_outs - self.backbone_end_level + self.start_level - if self.add_extra_convs and extra_levels >= 1: - for i in range(extra_levels): - if i == 0 and self.add_extra_convs == 'on_input': - in_channels = self.in_channels[self.backbone_end_level - 1] - else: - in_channels = out_channels - extra_fpn_conv = ConvModule( - in_channels, - out_channels, - 3, - stride=2, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - self.fpn_convs.append(extra_fpn_conv) - - @auto_fp16() - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # build laterals - laterals = [ - lateral_conv(inputs[i + self.start_level]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - - # build top-down path - used_backbone_levels = len(laterals) - for i in range(used_backbone_levels - 1, 0, -1): - # In some cases, fixing `scale factor` (e.g. 2) is preferred, but - # it cannot co-exist with `size` in `F.interpolate`. - if 'scale_factor' in self.upsample_cfg: - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], **self.upsample_cfg) - else: - prev_shape = laterals[i - 1].shape[2:] - laterals[i - 1] = laterals[i - 1] + resize( - laterals[i], size=prev_shape, **self.upsample_cfg) - - # build outputs - # part 1: from original levels - outs = [ - self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels) - ] - # part 2: add extra levels - if self.num_outs > len(outs): - # use max pool to get more levels on top of outputs - # (e.g., Faster R-CNN, Mask R-CNN) - if not self.add_extra_convs: - for i in range(self.num_outs - used_backbone_levels): - outs.append(F.max_pool2d(outs[-1], 1, stride=2)) - # add conv layers on top of original feature maps (RetinaNet) - else: - if self.add_extra_convs == 'on_input': - extra_source = inputs[self.backbone_end_level - 1] - elif self.add_extra_convs == 'on_lateral': - extra_source = laterals[-1] - elif self.add_extra_convs == 'on_output': - extra_source = outs[-1] - else: - raise NotImplementedError - outs.append(self.fpn_convs[used_backbone_levels](extra_source)) - for i in range(used_backbone_levels + 1, self.num_outs): - if self.relu_before_extra_convs: - outs.append(self.fpn_convs[i](F.relu(outs[-1]))) - else: - outs.append(self.fpn_convs[i](outs[-1])) - return tuple(outs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/ic_neck.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/ic_neck.py deleted file mode 100644 index a5d81cef8..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/ic_neck.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -class CascadeFeatureFusion(BaseModule): - """Cascade Feature Fusion Unit in ICNet. - - Args: - low_channels (int): The number of input channels for - low resolution feature map. - high_channels (int): The number of input channels for - high resolution feature map. - out_channels (int): The number of output channels. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - - Returns: - x (Tensor): The output tensor of shape (N, out_channels, H, W). - x_low (Tensor): The output tensor of shape (N, out_channels, H, W) - for Cascade Label Guidance in auxiliary heads. - """ - - def __init__(self, - low_channels, - high_channels, - out_channels, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(CascadeFeatureFusion, self).__init__(init_cfg=init_cfg) - self.align_corners = align_corners - self.conv_low = ConvModule( - low_channels, - out_channels, - 3, - padding=2, - dilation=2, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.conv_high = ConvModule( - high_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, x_low, x_high): - x_low = resize( - x_low, - size=x_high.size()[2:], - mode='bilinear', - align_corners=self.align_corners) - # Note: Different from original paper, `x_low` is underwent - # `self.conv_low` rather than another 1x1 conv classifier - # before being used for auxiliary head. - x_low = self.conv_low(x_low) - x_high = self.conv_high(x_high) - x = x_low + x_high - x = F.relu(x, inplace=True) - return x, x_low - - -@NECKS.register_module() -class ICNeck(BaseModule): - """ICNet for Real-Time Semantic Segmentation on High-Resolution Images. - - This head is the implementation of `ICHead - `_. - - Args: - in_channels (int): The number of input image channels. Default: 3. - out_channels (int): The numbers of output feature channels. - Default: 128. - conv_cfg (dict): Dictionary to construct and config conv layer. - Default: None. - norm_cfg (dict): Dictionary to construct and config norm layer. - Default: dict(type='BN'). - act_cfg (dict): Dictionary to construct and config act layer. - Default: dict(type='ReLU'). - align_corners (bool): align_corners argument of F.interpolate. - Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(64, 256, 256), - out_channels=128, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - align_corners=False, - init_cfg=None): - super(ICNeck, self).__init__(init_cfg=init_cfg) - assert len(in_channels) == 3, 'Length of input channels \ - must be 3!' - - self.in_channels = in_channels - self.out_channels = out_channels - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.align_corners = align_corners - self.cff_24 = CascadeFeatureFusion( - self.in_channels[2], - self.in_channels[1], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - self.cff_12 = CascadeFeatureFusion( - self.out_channels, - self.in_channels[0], - self.out_channels, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg, - act_cfg=self.act_cfg, - align_corners=self.align_corners) - - def forward(self, inputs): - assert len(inputs) == 3, 'Length of input feature \ - maps must be 3!' - - x_sub1, x_sub2, x_sub4 = inputs - x_cff_24, x_24 = self.cff_24(x_sub4, x_sub2) - x_cff_12, x_12 = self.cff_12(x_cff_24, x_sub1) - # Note: `x_cff_12` is used for decode_head, - # `x_24` and `x_12` are used for auxiliary head. - return x_24, x_12, x_cff_12 diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/jpu.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/jpu.py deleted file mode 100644 index 3cc6b9f42..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/jpu.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, DepthwiseSeparableConvModule -from mmcv.runner import BaseModule - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class JPU(BaseModule): - """FastFCN: Rethinking Dilated Convolution in the Backbone - for Semantic Segmentation. - - This Joint Pyramid Upsampling (JPU) neck is the implementation of - `FastFCN `_. - - Args: - in_channels (Tuple[int], optional): The number of input channels - for each convolution operations before upsampling. - Default: (512, 1024, 2048). - mid_channels (int): The number of output channels of JPU. - Default: 512. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - dilations (tuple[int]): Dilation rate of each Depthwise - Separable ConvModule. Default: (1, 2, 4, 8). - align_corners (bool, optional): The align_corners argument of - resize operation. Default: False. - conv_cfg (dict | None): Config of conv layers. - Default: None. - norm_cfg (dict | None): Config of norm layers. - Default: dict(type='BN'). - act_cfg (dict): Config of activation layers. - Default: dict(type='ReLU'). - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None. - """ - - def __init__(self, - in_channels=(512, 1024, 2048), - mid_channels=512, - start_level=0, - end_level=-1, - dilations=(1, 2, 4, 8), - align_corners=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - init_cfg=None): - super(JPU, self).__init__(init_cfg=init_cfg) - assert isinstance(in_channels, tuple) - assert isinstance(dilations, tuple) - self.in_channels = in_channels - self.mid_channels = mid_channels - self.start_level = start_level - self.num_ins = len(in_channels) - if end_level == -1: - self.backbone_end_level = self.num_ins - else: - self.backbone_end_level = end_level - assert end_level <= len(in_channels) - - self.dilations = dilations - self.align_corners = align_corners - - self.conv_layers = nn.ModuleList() - self.dilation_layers = nn.ModuleList() - for i in range(self.start_level, self.backbone_end_level): - conv_layer = nn.Sequential( - ConvModule( - self.in_channels[i], - self.mid_channels, - kernel_size=3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.conv_layers.append(conv_layer) - for i in range(len(dilations)): - dilation_layer = nn.Sequential( - DepthwiseSeparableConvModule( - in_channels=(self.backbone_end_level - self.start_level) * - self.mid_channels, - out_channels=self.mid_channels, - kernel_size=3, - stride=1, - padding=dilations[i], - dilation=dilations[i], - dw_norm_cfg=norm_cfg, - dw_act_cfg=None, - pw_norm_cfg=norm_cfg, - pw_act_cfg=act_cfg)) - self.dilation_layers.append(dilation_layer) - - def forward(self, inputs): - """Forward function.""" - assert len(inputs) == len(self.in_channels), 'Length of inputs must \ - be the same with self.in_channels!' - - feats = [ - self.conv_layers[i - self.start_level](inputs[i]) - for i in range(self.start_level, self.backbone_end_level) - ] - - h, w = feats[0].shape[2:] - for i in range(1, len(feats)): - feats[i] = resize( - feats[i], - size=(h, w), - mode='bilinear', - align_corners=self.align_corners) - - feat = torch.cat(feats, dim=1) - concat_feat = torch.cat([ - self.dilation_layers[i](feat) for i in range(len(self.dilations)) - ], - dim=1) - - outs = [] - - # Default: outs[2] is the output of JPU for decoder head, outs[1] is - # the feature map from backbone for auxiliary head. Additionally, - # outs[0] can also be used for auxiliary head. - for i in range(self.start_level, self.backbone_end_level - 1): - outs.append(inputs[i]) - outs.append(concat_feat) - return tuple(outs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/mla_neck.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/mla_neck.py deleted file mode 100644 index 1513e296d..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/mla_neck.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, build_norm_layer - -from ..builder import NECKS - - -class MLAModule(nn.Module): - - def __init__(self, - in_channels=[1024, 1024, 1024, 1024], - out_channels=256, - norm_cfg=None, - act_cfg=None): - super(MLAModule, self).__init__() - self.channel_proj = nn.ModuleList() - for i in range(len(in_channels)): - self.channel_proj.append( - ConvModule( - in_channels=in_channels[i], - out_channels=out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - self.feat_extract = nn.ModuleList() - for i in range(len(in_channels)): - self.feat_extract.append( - ConvModule( - in_channels=out_channels, - out_channels=out_channels, - kernel_size=3, - padding=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, inputs): - - # feat_list -> [p2, p3, p4, p5] - feat_list = [] - for x, conv in zip(inputs, self.channel_proj): - feat_list.append(conv(x)) - - # feat_list -> [p5, p4, p3, p2] - # mid_list -> [m5, m4, m3, m2] - feat_list = feat_list[::-1] - mid_list = [] - for feat in feat_list: - if len(mid_list) == 0: - mid_list.append(feat) - else: - mid_list.append(mid_list[-1] + feat) - - # mid_list -> [m5, m4, m3, m2] - # out_list -> [o2, o3, o4, o5] - out_list = [] - for mid, conv in zip(mid_list, self.feat_extract): - out_list.append(conv(mid)) - - return tuple(out_list) - - -@NECKS.register_module() -class MLANeck(nn.Module): - """Multi-level Feature Aggregation. - - This neck is `The Multi-level Feature Aggregation construction of - SETR `_. - - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - norm_layer (dict): Config dict for input normalization. - Default: norm_layer=dict(type='LN', eps=1e-6, requires_grad=True). - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - norm_layer=dict(type='LN', eps=1e-6, requires_grad=True), - norm_cfg=None, - act_cfg=None): - super(MLANeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - - # In order to build general vision transformer backbone, we have to - # move MLA to neck. - self.norm = nn.ModuleList([ - build_norm_layer(norm_layer, in_channels[i])[1] - for i in range(len(in_channels)) - ]) - - self.mla = MLAModule( - in_channels=in_channels, - out_channels=out_channels, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - - # Convert from nchw to nlc - outs = [] - for i in range(len(inputs)): - x = inputs[i] - n, c, h, w = x.shape - x = x.reshape(n, c, h * w).transpose(2, 1).contiguous() - x = self.norm[i](x) - x = x.transpose(1, 2).reshape(n, c, h, w).contiguous() - outs.append(x) - - outs = self.mla(outs) - return tuple(outs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/multilevel_neck.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/multilevel_neck.py deleted file mode 100644 index 5151f8762..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/necks/multilevel_neck.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule, xavier_init - -from mmseg.ops import resize -from ..builder import NECKS - - -@NECKS.register_module() -class MultiLevelNeck(nn.Module): - """MultiLevelNeck. - - A neck structure connect vit backbone and decoder_heads. - - Args: - in_channels (List[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - scales (List[float]): Scale factors for each input feature map. - Default: [0.5, 1, 2, 4] - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - scales=[0.5, 1, 2, 4], - norm_cfg=None, - act_cfg=None): - super(MultiLevelNeck, self).__init__() - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.scales = scales - self.num_outs = len(scales) - self.lateral_convs = nn.ModuleList() - self.convs = nn.ModuleList() - for in_channel in in_channels: - self.lateral_convs.append( - ConvModule( - in_channel, - out_channels, - kernel_size=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - for _ in range(self.num_outs): - self.convs.append( - ConvModule( - out_channels, - out_channels, - kernel_size=3, - padding=1, - stride=1, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - # default init_weights for conv(msra) and norm in ConvModule - def init_weights(self): - for m in self.modules(): - if isinstance(m, nn.Conv2d): - xavier_init(m, distribution='uniform') - - def forward(self, inputs): - assert len(inputs) == len(self.in_channels) - inputs = [ - lateral_conv(inputs[i]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - # for len(inputs) not equal to self.num_outs - if len(inputs) == 1: - inputs = [inputs[0] for _ in range(self.num_outs)] - outs = [] - for i in range(self.num_outs): - x_resize = resize( - inputs[i], scale_factor=self.scales[i], mode='bilinear') - outs.append(self.convs[i](x_resize)) - return tuple(outs) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/__init__.py deleted file mode 100644 index 387c858bd..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseSegmentor -from .cascade_encoder_decoder import CascadeEncoderDecoder -from .encoder_decoder import EncoderDecoder - -__all__ = ['BaseSegmentor', 'EncoderDecoder', 'CascadeEncoderDecoder'] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/base.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/base.py deleted file mode 100644 index 76dc8f075..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/base.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from abc import ABCMeta, abstractmethod -from collections import OrderedDict - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import BaseModule, auto_fp16 - - -class BaseSegmentor(BaseModule, metaclass=ABCMeta): - """Base class for segmentors.""" - - def __init__(self, init_cfg=None): - super(BaseSegmentor, self).__init__(init_cfg) - self.fp16_enabled = False - - @property - def with_neck(self): - """bool: whether the segmentor has neck""" - return hasattr(self, 'neck') and self.neck is not None - - @property - def with_auxiliary_head(self): - """bool: whether the segmentor has auxiliary head""" - return hasattr(self, - 'auxiliary_head') and self.auxiliary_head is not None - - @property - def with_decode_head(self): - """bool: whether the segmentor has decode head""" - return hasattr(self, 'decode_head') and self.decode_head is not None - - @abstractmethod - def extract_feat(self, imgs): - """Placeholder for extract features from images.""" - pass - - @abstractmethod - def encode_decode(self, img, img_metas): - """Placeholder for encode images with backbone and decode into a - semantic segmentation map of the same size as input.""" - pass - - @abstractmethod - def forward_train(self, imgs, img_metas, **kwargs): - """Placeholder for Forward function for training.""" - pass - - @abstractmethod - def simple_test(self, img, img_meta, **kwargs): - """Placeholder for single image test.""" - pass - - @abstractmethod - def aug_test(self, imgs, img_metas, **kwargs): - """Placeholder for augmentation test.""" - pass - - def forward_test(self, imgs, img_metas, **kwargs): - """ - Args: - imgs (List[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains all images in the batch. - img_metas (List[List[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. - """ - for var, name in [(imgs, 'imgs'), (img_metas, 'img_metas')]: - if not isinstance(var, list): - raise TypeError(f'{name} must be a list, but got ' - f'{type(var)}') - - num_augs = len(imgs) - if num_augs != len(img_metas): - raise ValueError(f'num of augmentations ({len(imgs)}) != ' - f'num of image meta ({len(img_metas)})') - # all images in the same aug batch all of the same ori_shape and pad - # shape - for img_meta in img_metas: - ori_shapes = [_['ori_shape'] for _ in img_meta] - assert all(shape == ori_shapes[0] for shape in ori_shapes) - img_shapes = [_['img_shape'] for _ in img_meta] - assert all(shape == img_shapes[0] for shape in img_shapes) - pad_shapes = [_['pad_shape'] for _ in img_meta] - assert all(shape == pad_shapes[0] for shape in pad_shapes) - - if num_augs == 1: - return self.simple_test(imgs[0], img_metas[0], **kwargs) - else: - return self.aug_test(imgs, img_metas, **kwargs) - - @auto_fp16(apply_to=('img', )) - def forward(self, img, img_metas, return_loss=True, **kwargs): - """Calls either :func:`forward_train` or :func:`forward_test` depending - on whether ``return_loss`` is ``True``. - - Note this setting will change the expected inputs. When - ``return_loss=True``, img and img_meta are single-nested (i.e. Tensor - and List[dict]), and when ``resturn_loss=False``, img and img_meta - should be double nested (i.e. List[Tensor], List[List[dict]]), with - the outer list indicating test time augmentations. - """ - if return_loss: - return self.forward_train(img, img_metas, **kwargs) - else: - return self.forward_test(img, img_metas, **kwargs) - - def train_step(self, data_batch, optimizer, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating is also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer` | dict): The optimizer of - runner is passed to ``train_step()``. This argument is unused - and reserved. - - Returns: - dict: It should contain at least 3 keys: ``loss``, ``log_vars``, - ``num_samples``. - ``loss`` is a tensor for back propagation, which can be a - weighted sum of multiple losses. - ``log_vars`` contains all the variables to be sent to the - logger. - ``num_samples`` indicates the batch size (when the model is - DDP, it means the batch size on each GPU), which is used for - averaging the logs. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, - log_vars=log_vars, - num_samples=len(data_batch['img_metas'])) - - return outputs - - def val_step(self, data_batch, optimizer=None, **kwargs): - """The iteration step during validation. - - This method shares the same signature as :func:`train_step`, but used - during val epochs. Note that the evaluation after training epochs is - not implemented with this method, but an evaluation hook. - """ - losses = self(**data_batch) - loss, log_vars = self._parse_losses(losses) - - log_vars_ = dict() - for loss_name, loss_value in log_vars.items(): - k = loss_name + '_val' - log_vars_[k] = loss_value - - outputs = dict( - loss=loss, - log_vars=log_vars_, - num_samples=len(data_batch['img_metas'])) - - return outputs - - @staticmethod - def _parse_losses(losses): - """Parse the raw outputs (losses) of the network. - - Args: - losses (dict): Raw output of the network, which usually contain - losses and other necessary information. - - Returns: - tuple[Tensor, dict]: (loss, log_vars), loss is the loss tensor - which may be a weighted sum of all losses, log_vars contains - all the variables to be sent to the logger. - """ - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(_value for _key, _value in log_vars.items() - if 'loss' in _key) - - # If the loss_vars has different length, raise assertion error - # to prevent GPUs from infinite waiting. - if dist.is_available() and dist.is_initialized(): - log_var_length = torch.tensor(len(log_vars), device=loss.device) - dist.all_reduce(log_var_length) - message = (f'rank {dist.get_rank()}' + - f' len(log_vars): {len(log_vars)}' + ' keys: ' + - ','.join(log_vars.keys()) + '\n') - assert log_var_length == len(log_vars) * dist.get_world_size(), \ - 'loss log variables are different across GPUs!\n' + message - - log_vars['loss'] = loss - for loss_name, loss_value in log_vars.items(): - # reduce loss when distributed training - if dist.is_available() and dist.is_initialized(): - loss_value = loss_value.data.clone() - dist.all_reduce(loss_value.div_(dist.get_world_size())) - log_vars[loss_name] = loss_value.item() - - return loss, log_vars - - def show_result(self, - img, - result, - palette=None, - win_name='', - show=False, - wait_time=0, - out_file=None, - opacity=0.5): - """Draw `result` over `img`. - - Args: - img (str or Tensor): The image to be displayed. - result (Tensor): The semantic segmentation results to draw over - `img`. - palette (list[list[int]]] | np.ndarray | None): The palette of - segmentation map. If None is given, random palette will be - generated. Default: None - win_name (str): The window name. - wait_time (int): Value of waitKey param. - Default: 0. - show (bool): Whether to show the image. - Default: False. - out_file (str or None): The filename to write the image. - Default: None. - opacity(float): Opacity of painted segmentation map. - Default 0.5. - Must be in (0, 1] range. - Returns: - img (Tensor): Only if not `show` or `out_file` - """ - img = mmcv.imread(img) - img = img.copy() - seg = result[0] - if palette is None: - if self.PALETTE is None: - # Get random state before set seed, - # and restore random state later. - # It will prevent loss of randomness, as the palette - # may be different in each iteration if not specified. - # See: https://github.com/open-mmlab/mmdetection/issues/5844 - state = np.random.get_state() - np.random.seed(42) - # random palette - palette = np.random.randint( - 0, 255, size=(len(self.CLASSES), 3)) - np.random.set_state(state) - else: - palette = self.PALETTE - palette = np.array(palette) - assert palette.shape[0] == len(self.CLASSES) - assert palette.shape[1] == 3 - assert len(palette.shape) == 2 - assert 0 < opacity <= 1.0 - color_seg = np.zeros((seg.shape[0], seg.shape[1], 3), dtype=np.uint8) - for label, color in enumerate(palette): - color_seg[seg == label, :] = color - # convert to BGR - color_seg = color_seg[..., ::-1] - - img = img * (1 - opacity) + color_seg * opacity - img = img.astype(np.uint8) - # if out_file specified, do not show image in window - if out_file is not None: - show = False - - if show: - mmcv.imshow(img, win_name, wait_time) - if out_file is not None: - mmcv.imwrite(img, out_file) - - if not (show or out_file): - warnings.warn('show==False and out_file is not specified, only ' - 'result image will be returned') - return img diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py deleted file mode 100644 index 1913a22e2..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/cascade_encoder_decoder.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from mmseg.core import add_prefix -from mmseg.ops import resize -from .. import builder -from ..builder import SEGMENTORS -from .encoder_decoder import EncoderDecoder - - -@SEGMENTORS.register_module() -class CascadeEncoderDecoder(EncoderDecoder): - """Cascade Encoder Decoder segmentors. - - CascadeEncoderDecoder almost the same as EncoderDecoder, while decoders of - CascadeEncoderDecoder are cascaded. The output of previous decoder_head - will be the input of next decoder_head. - """ - - def __init__(self, - num_stages, - backbone, - decode_head, - neck=None, - auxiliary_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - self.num_stages = num_stages - super(CascadeEncoderDecoder, self).__init__( - backbone=backbone, - decode_head=decode_head, - neck=neck, - auxiliary_head=auxiliary_head, - train_cfg=train_cfg, - test_cfg=test_cfg, - pretrained=pretrained, - init_cfg=init_cfg) - - def _init_decode_head(self, decode_head): - """Initialize ``decode_head``""" - assert isinstance(decode_head, list) - assert len(decode_head) == self.num_stages - self.decode_head = nn.ModuleList() - for i in range(self.num_stages): - self.decode_head.append(builder.build_head(decode_head[i])) - self.align_corners = self.decode_head[-1].align_corners - self.num_classes = self.decode_head[-1].num_classes - - def encode_decode(self, img, img_metas): - """Encode images with backbone and decode into a semantic segmentation - map of the same size as input.""" - x = self.extract_feat(img) - out = self.decode_head[0].forward_test(x, img_metas, self.test_cfg) - for i in range(1, self.num_stages): - out = self.decode_head[i].forward_test(x, out, img_metas, - self.test_cfg) - out = resize( - input=out, - size=img.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - return out - - def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for decode head in - training.""" - losses = dict() - - loss_decode = self.decode_head[0].forward_train( - x, img_metas, gt_semantic_seg, self.train_cfg) - - losses.update(add_prefix(loss_decode, 'decode_0')) - - for i in range(1, self.num_stages): - # forward test again, maybe unnecessary for most methods. - if i == 1: - prev_outputs = self.decode_head[0].forward_test( - x, img_metas, self.test_cfg) - else: - prev_outputs = self.decode_head[i - 1].forward_test( - x, prev_outputs, img_metas, self.test_cfg) - loss_decode = self.decode_head[i].forward_train( - x, prev_outputs, img_metas, gt_semantic_seg, self.train_cfg) - losses.update(add_prefix(loss_decode, f'decode_{i}')) - - return losses diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/encoder_decoder.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/encoder_decoder.py deleted file mode 100644 index 72467b469..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/segmentors/encoder_decoder.py +++ /dev/null @@ -1,284 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmseg.core import add_prefix -from mmseg.ops import resize -from .. import builder -from ..builder import SEGMENTORS -from .base import BaseSegmentor - - -@SEGMENTORS.register_module() -class EncoderDecoder(BaseSegmentor): - """Encoder Decoder segmentors. - - EncoderDecoder typically consists of backbone, decode_head, auxiliary_head. - Note that auxiliary_head is only used for deep supervision during training, - which could be dumped during inference. - """ - - def __init__(self, - backbone, - decode_head, - neck=None, - auxiliary_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - super(EncoderDecoder, self).__init__(init_cfg) - if pretrained is not None: - assert backbone.get('pretrained') is None, \ - 'both backbone and segmentor set pretrained weight' - backbone.pretrained = pretrained - self.backbone = builder.build_backbone(backbone) - if neck is not None: - self.neck = builder.build_neck(neck) - self._init_decode_head(decode_head) - self._init_auxiliary_head(auxiliary_head) - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - assert self.with_decode_head - - def _init_decode_head(self, decode_head): - """Initialize ``decode_head``""" - self.decode_head = builder.build_head(decode_head) - self.align_corners = self.decode_head.align_corners - self.num_classes = self.decode_head.num_classes - - def _init_auxiliary_head(self, auxiliary_head): - """Initialize ``auxiliary_head``""" - if auxiliary_head is not None: - if isinstance(auxiliary_head, list): - self.auxiliary_head = nn.ModuleList() - for head_cfg in auxiliary_head: - self.auxiliary_head.append(builder.build_head(head_cfg)) - else: - self.auxiliary_head = builder.build_head(auxiliary_head) - - def extract_feat(self, img): - """Extract features from images.""" - x = self.backbone(img) - if self.with_neck: - x = self.neck(x) - return x - - def encode_decode(self, img, img_metas): - """Encode images with backbone and decode into a semantic segmentation - map of the same size as input.""" - x = self.extract_feat(img) - out = self._decode_head_forward_test(x, img_metas) - out = resize( - input=out, - size=img.shape[2:], - mode='bilinear', - align_corners=self.align_corners) - return out - - def _decode_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for decode head in - training.""" - losses = dict() - loss_decode = self.decode_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - - losses.update(add_prefix(loss_decode, 'decode')) - return losses - - def _decode_head_forward_test(self, x, img_metas): - """Run forward function and calculate loss for decode head in - inference.""" - seg_logits = self.decode_head.forward_test(x, img_metas, self.test_cfg) - return seg_logits - - def _auxiliary_head_forward_train(self, x, img_metas, gt_semantic_seg): - """Run forward function and calculate loss for auxiliary head in - training.""" - losses = dict() - if isinstance(self.auxiliary_head, nn.ModuleList): - for idx, aux_head in enumerate(self.auxiliary_head): - loss_aux = aux_head.forward_train(x, img_metas, - gt_semantic_seg, - self.train_cfg) - losses.update(add_prefix(loss_aux, f'aux_{idx}')) - else: - loss_aux = self.auxiliary_head.forward_train( - x, img_metas, gt_semantic_seg, self.train_cfg) - losses.update(add_prefix(loss_aux, 'aux')) - - return losses - - def forward_dummy(self, img): - """Dummy forward function.""" - seg_logit = self.encode_decode(img, None) - - return seg_logit - - def forward_train(self, img, img_metas, gt_semantic_seg): - """Forward function for training. - - Args: - img (Tensor): Input images. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - gt_semantic_seg (Tensor): Semantic segmentation masks - used if the architecture supports semantic segmentation task. - - Returns: - dict[str, Tensor]: a dictionary of loss components - """ - - x = self.extract_feat(img) - - losses = dict() - - loss_decode = self._decode_head_forward_train(x, img_metas, - gt_semantic_seg) - losses.update(loss_decode) - - if self.with_auxiliary_head: - loss_aux = self._auxiliary_head_forward_train( - x, img_metas, gt_semantic_seg) - losses.update(loss_aux) - - return losses - - # TODO refactor - def slide_inference(self, img, img_meta, rescale): - """Inference by sliding-window with overlap. - - If h_crop > h_img or w_crop > w_img, the small patch will be used to - decode without padding. - """ - - h_stride, w_stride = self.test_cfg.stride - h_crop, w_crop = self.test_cfg.crop_size - batch_size, _, h_img, w_img = img.size() - num_classes = self.num_classes - h_grids = max(h_img - h_crop + h_stride - 1, 0) // h_stride + 1 - w_grids = max(w_img - w_crop + w_stride - 1, 0) // w_stride + 1 - preds = img.new_zeros((batch_size, num_classes, h_img, w_img)) - count_mat = img.new_zeros((batch_size, 1, h_img, w_img)) - for h_idx in range(h_grids): - for w_idx in range(w_grids): - y1 = h_idx * h_stride - x1 = w_idx * w_stride - y2 = min(y1 + h_crop, h_img) - x2 = min(x1 + w_crop, w_img) - y1 = max(y2 - h_crop, 0) - x1 = max(x2 - w_crop, 0) - crop_img = img[:, :, y1:y2, x1:x2] - crop_seg_logit = self.encode_decode(crop_img, img_meta) - preds += F.pad(crop_seg_logit, - (int(x1), int(preds.shape[3] - x2), int(y1), - int(preds.shape[2] - y2))) - - count_mat[:, :, y1:y2, x1:x2] += 1 - assert (count_mat == 0).sum() == 0 - if torch.onnx.is_in_onnx_export(): - # cast count_mat to constant while exporting to ONNX - count_mat = torch.from_numpy( - count_mat.cpu().detach().numpy()).to(device=img.device) - preds = preds / count_mat - if rescale: - preds = resize( - preds, - size=img_meta[0]['ori_shape'][:2], - mode='bilinear', - align_corners=self.align_corners, - warning=False) - return preds - - def whole_inference(self, img, img_meta, rescale): - """Inference with full image.""" - - seg_logit = self.encode_decode(img, img_meta) - if rescale: - # support dynamic shape for onnx - if torch.onnx.is_in_onnx_export(): - size = img.shape[2:] - else: - size = img_meta[0]['ori_shape'][:2] - seg_logit = resize( - seg_logit, - size=size, - mode='bilinear', - align_corners=self.align_corners, - warning=False) - - return seg_logit - - def inference(self, img, img_meta, rescale): - """Inference with slide/whole style. - - Args: - img (Tensor): The input image of shape (N, 3, H, W). - img_meta (dict): Image info dict where each dict has: 'img_shape', - 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmseg/datasets/pipelines/formatting.py:Collect`. - rescale (bool): Whether rescale back to original shape. - - Returns: - Tensor: The output segmentation map. - """ - - assert self.test_cfg.mode in ['slide', 'whole'] - ori_shape = img_meta[0]['ori_shape'] - assert all(_['ori_shape'] == ori_shape for _ in img_meta) - if self.test_cfg.mode == 'slide': - seg_logit = self.slide_inference(img, img_meta, rescale) - else: - seg_logit = self.whole_inference(img, img_meta, rescale) - output = F.softmax(seg_logit, dim=1) - flip = img_meta[0]['flip'] - if flip: - flip_direction = img_meta[0]['flip_direction'] - assert flip_direction in ['horizontal', 'vertical'] - if flip_direction == 'horizontal': - output = output.flip(dims=(3, )) - elif flip_direction == 'vertical': - output = output.flip(dims=(2, )) - - return output - - def simple_test(self, img, img_meta, rescale=True): - """Simple test with single image.""" - seg_logit = self.inference(img, img_meta, rescale) - seg_pred = seg_logit.argmax(dim=1) - if torch.onnx.is_in_onnx_export(): - # our inference backend only support 4D output - seg_pred = seg_pred.unsqueeze(0) - return seg_pred - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred - - def aug_test(self, imgs, img_metas, rescale=True): - """Test with augmentations. - - Only rescale=True is supported. - """ - # aug_test rescale all imgs back to ori_shape for now - assert rescale - # to save memory, we get augmented seg logit inplace - seg_logit = self.inference(imgs[0], img_metas[0], rescale) - for i in range(1, len(imgs)): - cur_seg_logit = self.inference(imgs[i], img_metas[i], rescale) - seg_logit += cur_seg_logit - seg_logit /= len(imgs) - seg_pred = seg_logit.argmax(dim=1) - seg_pred = seg_pred.cpu().numpy() - # unravel batch dim - seg_pred = list(seg_pred) - return seg_pred diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/__init__.py deleted file mode 100644 index fc281e3d6..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .embed import PatchEmbed -from .inverted_residual import InvertedResidual, InvertedResidualV3 -from .make_divisible import make_divisible -from .res_layer import ResLayer -from .se_layer import SELayer -from .self_attention_block import SelfAttentionBlock -from .shape_convert import (nchw2nlc2nchw, nchw_to_nlc, nlc2nchw2nlc, - nlc_to_nchw) -from .up_conv_block import UpConvBlock -from .wrappers import resize, Upsample - -__all__ = [ - 'ResLayer', 'SelfAttentionBlock', 'make_divisible', 'InvertedResidual', - 'UpConvBlock', 'InvertedResidualV3', 'SELayer', 'PatchEmbed', - 'nchw_to_nlc', 'nlc_to_nchw', 'nchw2nlc2nchw', 'nlc2nchw2nlc', - 'resize', 'Upsample' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/embed.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/embed.py deleted file mode 100644 index 1515675e1..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/embed.py +++ /dev/null @@ -1,330 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -from typing import Sequence - -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner.base_module import BaseModule -from mmcv.utils import to_2tuple - - -class AdaptivePadding(nn.Module): - """Applies padding to input (if needed) so that input can get fully covered - by filter you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad zero around - input. The "corner" mode would pad zero to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel: - stride (int | tuple): Stride of the filter. Default: 1: - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - - super(AdaptivePadding, self).__init__() - - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The config dict for embedding - conv layer type selection. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int, optional): The slide stride of embedding conv. - Default: None (Would be set as `kernel_size`). - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only work when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=None, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super(PatchEmbed, self).__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adap_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adap_padding: - pad_h, pad_w = self.adap_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adap_padding: - x = self.adap_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map. Our implementation uses `nn.Unfold` to - merge patch, which is about 25% faster than original implementation. - Instead, we need to modify pretrained models for compatibility. - - Args: - in_channels (int): The num of input channels. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adap_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adap_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - - if self.adap_padding: - x = self.adap_padding(x) - H, W = x.shape[-2:] - - x = self.sampler(x) - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/inverted_residual.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/inverted_residual.py deleted file mode 100644 index c9cda7682..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/inverted_residual.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import ConvModule -from torch import nn -from torch.utils import checkpoint as cp - -from .se_layer import SELayer - - -class InvertedResidual(nn.Module): - """InvertedResidual block for MobileNetV2. - - Args: - in_channels (int): The input channels of the InvertedResidual block. - out_channels (int): The output channels of the InvertedResidual block. - stride (int): Stride of the middle (first) 3x3 convolution. - expand_ratio (int): Adjusts number of channels of the hidden layer - in InvertedResidual by this amount. - dilation (int): Dilation rate of depthwise conv. Default: 1 - conv_cfg (dict): Config dict for convolution layer. - Default: None, which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU6'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - stride, - expand_ratio, - dilation=1, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU6'), - with_cp=False, - **kwargs): - super(InvertedResidual, self).__init__() - self.stride = stride - assert stride in [1, 2], f'stride must in [1, 2]. ' \ - f'But received {stride}.' - self.with_cp = with_cp - self.use_res_connect = self.stride == 1 and in_channels == out_channels - hidden_dim = int(round(in_channels * expand_ratio)) - - layers = [] - if expand_ratio != 1: - layers.append( - ConvModule( - in_channels=in_channels, - out_channels=hidden_dim, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs)) - layers.extend([ - ConvModule( - in_channels=hidden_dim, - out_channels=hidden_dim, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - groups=hidden_dim, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - **kwargs), - ConvModule( - in_channels=hidden_dim, - out_channels=out_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None, - **kwargs) - ]) - self.conv = nn.Sequential(*layers) - - def forward(self, x): - - def _inner_forward(x): - if self.use_res_connect: - return x + self.conv(x) - else: - return self.conv(x) - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out - - -class InvertedResidualV3(nn.Module): - """Inverted Residual Block for MobileNetV3. - - Args: - in_channels (int): The input channels of this Module. - out_channels (int): The output channels of this Module. - mid_channels (int): The input channels of the depthwise convolution. - kernel_size (int): The kernel size of the depthwise convolution. - Default: 3. - stride (int): The stride of the depthwise convolution. Default: 1. - se_cfg (dict): Config dict for se layer. Default: None, which means no - se layer. - with_expand_conv (bool): Use expand conv or not. If set False, - mid_channels must be the same with in_channels. Default: True. - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - mid_channels, - kernel_size=3, - stride=1, - se_cfg=None, - with_expand_conv=True, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - with_cp=False): - super(InvertedResidualV3, self).__init__() - self.with_res_shortcut = (stride == 1 and in_channels == out_channels) - assert stride in [1, 2] - self.with_cp = with_cp - self.with_se = se_cfg is not None - self.with_expand_conv = with_expand_conv - - if self.with_se: - assert isinstance(se_cfg, dict) - if not self.with_expand_conv: - assert mid_channels == in_channels - - if self.with_expand_conv: - self.expand_conv = ConvModule( - in_channels=in_channels, - out_channels=mid_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.depthwise_conv = ConvModule( - in_channels=mid_channels, - out_channels=mid_channels, - kernel_size=kernel_size, - stride=stride, - padding=kernel_size // 2, - groups=mid_channels, - conv_cfg=dict( - type='Conv2dAdaptivePadding') if stride == 2 else conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - if self.with_se: - self.se = SELayer(**se_cfg) - - self.linear_conv = ConvModule( - in_channels=mid_channels, - out_channels=out_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - def forward(self, x): - - def _inner_forward(x): - out = x - - if self.with_expand_conv: - out = self.expand_conv(out) - - out = self.depthwise_conv(out) - - if self.with_se: - out = self.se(out) - - out = self.linear_conv(out) - - if self.with_res_shortcut: - return x + out - else: - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/make_divisible.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/make_divisible.py deleted file mode 100644 index ed42c2eee..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/make_divisible.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def make_divisible(value, divisor, min_value=None, min_ratio=0.9): - """Make divisible function. - - This function rounds the channel number to the nearest value that can be - divisible by the divisor. It is taken from the original tf repo. It ensures - that all layers have a channel number that is divisible by divisor. It can - be seen here: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py # noqa - - Args: - value (int): The original channel number. - divisor (int): The divisor to fully divide the channel number. - min_value (int): The minimum value of the output channel. - Default: None, means that the minimum value equal to the divisor. - min_ratio (float): The minimum ratio of the rounded channel number to - the original channel number. Default: 0.9. - - Returns: - int: The modified output channel number. - """ - - if min_value is None: - min_value = divisor - new_value = max(min_value, int(value + divisor / 2) // divisor * divisor) - # Make sure that round down does not go down by more than (1-min_ratio). - if new_value < min_ratio * value: - new_value += divisor - return new_value diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/res_layer.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/res_layer.py deleted file mode 100644 index 190a0c5d5..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/res_layer.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner import Sequential -from torch import nn as nn - - -class ResLayer(Sequential): - """ResLayer to build ResNet style backbone. - - Args: - block (nn.Module): block used to build ResLayer. - inplanes (int): inplanes of block. - planes (int): planes of block. - num_blocks (int): number of blocks. - stride (int): stride of the first block. Default: 1 - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False - conv_cfg (dict): dictionary to construct and config conv layer. - Default: None - norm_cfg (dict): dictionary to construct and config norm layer. - Default: dict(type='BN') - multi_grid (int | None): Multi grid dilation rates of last - stage. Default: None - contract_dilation (bool): Whether contract first dilation of each layer - Default: False - """ - - def __init__(self, - block, - inplanes, - planes, - num_blocks, - stride=1, - dilation=1, - avg_down=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - multi_grid=None, - contract_dilation=False, - **kwargs): - self.block = block - - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = [] - conv_stride = stride - if avg_down: - conv_stride = 1 - downsample.append( - nn.AvgPool2d( - kernel_size=stride, - stride=stride, - ceil_mode=True, - count_include_pad=False)) - downsample.extend([ - build_conv_layer( - conv_cfg, - inplanes, - planes * block.expansion, - kernel_size=1, - stride=conv_stride, - bias=False), - build_norm_layer(norm_cfg, planes * block.expansion)[1] - ]) - downsample = nn.Sequential(*downsample) - - layers = [] - if multi_grid is None: - if dilation > 1 and contract_dilation: - first_dilation = dilation // 2 - else: - first_dilation = dilation - else: - first_dilation = multi_grid[0] - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=stride, - dilation=first_dilation, - downsample=downsample, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - inplanes = planes * block.expansion - for i in range(1, num_blocks): - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=1, - dilation=dilation if multi_grid is None else multi_grid[i], - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - super(ResLayer, self).__init__(*layers) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/se_layer.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/se_layer.py deleted file mode 100644 index 16f52aa5c..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/se_layer.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch.nn as nn -from mmcv.cnn import ConvModule - -from .make_divisible import make_divisible - - -class SELayer(nn.Module): - """Squeeze-and-Excitation Module. - - Args: - channels (int): The input (and output) channels of the SE layer. - ratio (int): Squeeze ratio in SELayer, the intermediate channel will be - ``int(channels/ratio)``. Default: 16. - conv_cfg (None or dict): Config dict for convolution layer. - Default: None, which means using conv2d. - act_cfg (dict or Sequence[dict]): Config dict for activation layer. - If act_cfg is a dict, two activation layers will be configured - by this dict. If act_cfg is a sequence of dicts, the first - activation layer will be configured by the first dict and the - second activation layer will be configured by the second dict. - Default: (dict(type='ReLU'), dict(type='HSigmoid', bias=3.0, - divisor=6.0)). - """ - - def __init__(self, - channels, - ratio=16, - conv_cfg=None, - act_cfg=(dict(type='ReLU'), - dict(type='HSigmoid', bias=3.0, divisor=6.0))): - super(SELayer, self).__init__() - if isinstance(act_cfg, dict): - act_cfg = (act_cfg, act_cfg) - assert len(act_cfg) == 2 - assert mmcv.is_tuple_of(act_cfg, dict) - self.global_avgpool = nn.AdaptiveAvgPool2d(1) - self.conv1 = ConvModule( - in_channels=channels, - out_channels=make_divisible(channels // ratio, 8), - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[0]) - self.conv2 = ConvModule( - in_channels=make_divisible(channels // ratio, 8), - out_channels=channels, - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[1]) - - def forward(self, x): - out = self.global_avgpool(x) - out = self.conv1(out) - out = self.conv2(out) - return x * out diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/self_attention_block.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/self_attention_block.py deleted file mode 100644 index c945fa716..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/self_attention_block.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.cnn import ConvModule, constant_init -from torch import nn as nn -from torch.nn import functional as F - - -class SelfAttentionBlock(nn.Module): - """General self-attention block/non-local block. - - Please refer to https://arxiv.org/abs/1706.03762 for details about key, - query and value. - - Args: - key_in_channels (int): Input channels of key feature. - query_in_channels (int): Input channels of query feature. - channels (int): Output channels of key/query transform. - out_channels (int): Output channels. - share_key_query (bool): Whether share projection weight between key - and query projection. - query_downsample (nn.Module): Query downsample module. - key_downsample (nn.Module): Key downsample module. - key_query_num_convs (int): Number of convs for key/query projection. - value_num_convs (int): Number of convs for value projection. - matmul_norm (bool): Whether normalize attention map with sqrt of - channels - with_out (bool): Whether use out projection. - conv_cfg (dict|None): Config of conv layers. - norm_cfg (dict|None): Config of norm layers. - act_cfg (dict|None): Config of activation layers. - """ - - def __init__(self, key_in_channels, query_in_channels, channels, - out_channels, share_key_query, query_downsample, - key_downsample, key_query_num_convs, value_out_num_convs, - key_query_norm, value_out_norm, matmul_norm, with_out, - conv_cfg, norm_cfg, act_cfg): - super(SelfAttentionBlock, self).__init__() - if share_key_query: - assert key_in_channels == query_in_channels - self.key_in_channels = key_in_channels - self.query_in_channels = query_in_channels - self.out_channels = out_channels - self.channels = channels - self.share_key_query = share_key_query - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.key_project = self.build_project( - key_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if share_key_query: - self.query_project = self.key_project - else: - self.query_project = self.build_project( - query_in_channels, - channels, - num_convs=key_query_num_convs, - use_conv_module=key_query_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.value_project = self.build_project( - key_in_channels, - channels if with_out else out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - if with_out: - self.out_project = self.build_project( - channels, - out_channels, - num_convs=value_out_num_convs, - use_conv_module=value_out_norm, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.out_project = None - - self.query_downsample = query_downsample - self.key_downsample = key_downsample - self.matmul_norm = matmul_norm - - self.init_weights() - - def init_weights(self): - """Initialize weight of later layer.""" - if self.out_project is not None: - if not isinstance(self.out_project, ConvModule): - constant_init(self.out_project, 0) - - def build_project(self, in_channels, channels, num_convs, use_conv_module, - conv_cfg, norm_cfg, act_cfg): - """Build projection layer for key/query/value/out.""" - if use_conv_module: - convs = [ - ConvModule( - in_channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - ] - for _ in range(num_convs - 1): - convs.append( - ConvModule( - channels, - channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - else: - convs = [nn.Conv2d(in_channels, channels, 1)] - for _ in range(num_convs - 1): - convs.append(nn.Conv2d(channels, channels, 1)) - if len(convs) > 1: - convs = nn.Sequential(*convs) - else: - convs = convs[0] - return convs - - def forward(self, query_feats, key_feats): - """Forward function.""" - batch_size = query_feats.size(0) - query = self.query_project(query_feats) - if self.query_downsample is not None: - query = self.query_downsample(query) - query = query.reshape(*query.shape[:2], -1) - query = query.permute(0, 2, 1).contiguous() - - key = self.key_project(key_feats) - value = self.value_project(key_feats) - if self.key_downsample is not None: - key = self.key_downsample(key) - value = self.key_downsample(value) - key = key.reshape(*key.shape[:2], -1) - value = value.reshape(*value.shape[:2], -1) - value = value.permute(0, 2, 1).contiguous() - - sim_map = torch.matmul(query, key) - if self.matmul_norm: - sim_map = (self.channels**-.5) * sim_map - sim_map = F.softmax(sim_map, dim=-1) - - context = torch.matmul(sim_map, value) - context = context.permute(0, 2, 1).contiguous() - context = context.reshape(batch_size, -1, *query_feats.shape[2:]) - if self.out_project is not None: - context = self.out_project(context) - return context diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/shape_convert.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/shape_convert.py deleted file mode 100644 index cce1e220b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/shape_convert.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def nlc_to_nchw(x, hw_shape): - """Convert [N, L, C] shape tensor to [N, C, H, W] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, L, C] before conversion. - hw_shape (Sequence[int]): The height and width of output feature map. - - Returns: - Tensor: The output tensor of shape [N, C, H, W] after conversion. - """ - H, W = hw_shape - assert len(x.shape) == 3 - B, L, C = x.shape - assert L == H * W, 'The seq_len doesn\'t match H, W' - return x.transpose(1, 2).reshape(B, C, H, W) - - -def nchw_to_nlc(x): - """Flatten [N, C, H, W] shape tensor to [N, L, C] shape tensor. - - Args: - x (Tensor): The input tensor of shape [N, C, H, W] before conversion. - - Returns: - Tensor: The output tensor of shape [N, L, C] after conversion. - """ - assert len(x.shape) == 4 - return x.flatten(2).transpose(1, 2).contiguous() - - -def nchw2nlc2nchw(module, x, contiguous=False, **kwargs): - """Flatten [N, C, H, W] shape tensor `x` to [N, L, C] shape tensor. Use the - reshaped tensor as the input of `module`, and the convert the output of - `module`, whose shape is. - - [N, L, C], to [N, C, H, W]. - - Args: - module (Callable): A callable object the takes a tensor - with shape [N, L, C] as input. - x (Tensor): The input tensor of shape [N, C, H, W]. - contiguous: - contiguous (Bool): Whether to make the tensor contiguous - after each shape transform. - - Returns: - Tensor: The output tensor of shape [N, C, H, W]. - - Example: - >>> import torch - >>> import torch.nn as nn - >>> norm = nn.LayerNorm(4) - >>> feature_map = torch.rand(4, 4, 5, 5) - >>> output = nchw2nlc2nchw(norm, feature_map) - """ - B, C, H, W = x.shape - if not contiguous: - x = x.flatten(2).transpose(1, 2) - x = module(x, **kwargs) - x = x.transpose(1, 2).reshape(B, C, H, W) - else: - x = x.flatten(2).transpose(1, 2).contiguous() - x = module(x, **kwargs) - x = x.transpose(1, 2).reshape(B, C, H, W).contiguous() - return x - - -def nlc2nchw2nlc(module, x, hw_shape, contiguous=False, **kwargs): - """Convert [N, L, C] shape tensor `x` to [N, C, H, W] shape tensor. Use the - reshaped tensor as the input of `module`, and convert the output of - `module`, whose shape is. - - [N, C, H, W], to [N, L, C]. - - Args: - module (Callable): A callable object the takes a tensor - with shape [N, C, H, W] as input. - x (Tensor): The input tensor of shape [N, L, C]. - hw_shape: (Sequence[int]): The height and width of the - feature map with shape [N, C, H, W]. - contiguous (Bool): Whether to make the tensor contiguous - after each shape transform. - - Returns: - Tensor: The output tensor of shape [N, L, C]. - - Example: - >>> import torch - >>> import torch.nn as nn - >>> conv = nn.Conv2d(16, 16, 3, 1, 1) - >>> feature_map = torch.rand(4, 25, 16) - >>> output = nlc2nchw2nlc(conv, feature_map, (5, 5)) - """ - H, W = hw_shape - assert len(x.shape) == 3 - B, L, C = x.shape - assert L == H * W, 'The seq_len doesn\'t match H, W' - if not contiguous: - x = x.transpose(1, 2).reshape(B, C, H, W) - x = module(x, **kwargs) - x = x.flatten(2).transpose(1, 2) - else: - x = x.transpose(1, 2).reshape(B, C, H, W).contiguous() - x = module(x, **kwargs) - x = x.flatten(2).transpose(1, 2).contiguous() - return x diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/up_conv_block.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/up_conv_block.py deleted file mode 100644 index d8396d9c2..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/up_conv_block.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, build_upsample_layer - - -class UpConvBlock(nn.Module): - """Upsample convolution block in decoder for UNet. - - This upsample convolution block consists of one upsample module - followed by one convolution block. The upsample module expands the - high-level low-resolution feature map and the convolution block fuses - the upsampled high-level low-resolution feature map and the low-level - high-resolution feature map from encoder. - - Args: - conv_block (nn.Sequential): Sequential of convolutional layers. - in_channels (int): Number of input channels of the high-level - skip_channels (int): Number of input channels of the low-level - high-resolution feature map from encoder. - out_channels (int): Number of output channels. - num_convs (int): Number of convolutional layers in the conv_block. - Default: 2. - stride (int): Stride of convolutional layer in conv_block. Default: 1. - dilation (int): Dilation rate of convolutional layer in conv_block. - Default: 1. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - conv_cfg (dict | None): Config dict for convolution layer. - Default: None. - norm_cfg (dict | None): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict | None): Config dict for activation layer in ConvModule. - Default: dict(type='ReLU'). - upsample_cfg (dict): The upsample config of the upsample module in - decoder. Default: dict(type='InterpConv'). If the size of - high-level feature map is the same as that of skip feature map - (low-level feature map from encoder), it does not need upsample the - high-level feature map and the upsample_cfg is None. - dcn (bool): Use deformable convolution in convolutional layer or not. - Default: None. - plugins (dict): plugins for convolutional layers. Default: None. - """ - - def __init__(self, - conv_block, - in_channels, - skip_channels, - out_channels, - num_convs=2, - stride=1, - dilation=1, - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - upsample_cfg=dict(type='InterpConv'), - dcn=None, - plugins=None): - super(UpConvBlock, self).__init__() - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.conv_block = conv_block( - in_channels=2 * skip_channels, - out_channels=out_channels, - num_convs=num_convs, - stride=stride, - dilation=dilation, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - dcn=None, - plugins=None) - if upsample_cfg is not None: - self.upsample = build_upsample_layer( - cfg=upsample_cfg, - in_channels=in_channels, - out_channels=skip_channels, - with_cp=with_cp, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - else: - self.upsample = ConvModule( - in_channels, - skip_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - def forward(self, skip, x): - """Forward function.""" - - x = self.upsample(x) - out = torch.cat([skip, x], dim=1) - out = self.conv_block(out) - - return out diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/wrappers.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/wrappers.py deleted file mode 100644 index abbd0c029..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/models/utils/wrappers.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.nn.functional as F - - -def resize(input, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None, - warning=True): - if warning: - if size is not None and align_corners: - input_h, input_w = tuple(int(x) for x in input.shape[2:]) - output_h, output_w = tuple(int(x) for x in size) - if output_h > input_h or output_w > output_h: - if ((output_h > 1 and output_w > 1 and input_h > 1 - and input_w > 1) and (output_h - 1) % (input_h - 1) - and (output_w - 1) % (input_w - 1)): - warnings.warn( - f'When align_corners={align_corners}, ' - 'the output would more aligned if ' - f'input size {(input_h, input_w)} is `x+1` and ' - f'out size {(output_h, output_w)} is `nx+1`') - return F.interpolate(input, size, scale_factor, mode, align_corners) - - -class Upsample(nn.Module): - - def __init__(self, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None): - super().__init__() - self.size = size - if isinstance(scale_factor, tuple): - self.scale_factor = tuple(float(factor) for factor in scale_factor) - else: - self.scale_factor = float(scale_factor) if scale_factor else None - self.mode = mode - self.align_corners = align_corners - - def forward(self, x): - if not self.size: - size = [int(t * self.scale_factor) for t in x.shape[-2:]] - else: - size = self.size - return resize(x, size, None, self.mode, self.align_corners) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/ops/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/ops/__init__.py deleted file mode 100644 index bc075cd4e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/ops/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .encoding import Encoding -from .wrappers import Upsample, resize - -__all__ = ['Upsample', 'resize', 'Encoding'] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/ops/encoding.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/ops/encoding.py deleted file mode 100644 index f397cc54e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/ops/encoding.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn -from torch.nn import functional as F - - -class Encoding(nn.Module): - """Encoding Layer: a learnable residual encoder. - - Input is of shape (batch_size, channels, height, width). - Output is of shape (batch_size, num_codes, channels). - - Args: - channels: dimension of the features or feature channels - num_codes: number of code words - """ - - def __init__(self, channels, num_codes): - super(Encoding, self).__init__() - # init codewords and smoothing factor - self.channels, self.num_codes = channels, num_codes - std = 1. / ((num_codes * channels)**0.5) - # [num_codes, channels] - self.codewords = nn.Parameter( - torch.empty(num_codes, channels, - dtype=torch.float).uniform_(-std, std), - requires_grad=True) - # [num_codes] - self.scale = nn.Parameter( - torch.empty(num_codes, dtype=torch.float).uniform_(-1, 0), - requires_grad=True) - - @staticmethod - def scaled_l2(x, codewords, scale): - num_codes, channels = codewords.size() - batch_size = x.size(0) - reshaped_scale = scale.view((1, 1, num_codes)) - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - - scaled_l2_norm = reshaped_scale * ( - expanded_x - reshaped_codewords).pow(2).sum(dim=3) - return scaled_l2_norm - - @staticmethod - def aggregate(assignment_weights, x, codewords): - num_codes, channels = codewords.size() - reshaped_codewords = codewords.view((1, 1, num_codes, channels)) - batch_size = x.size(0) - - expanded_x = x.unsqueeze(2).expand( - (batch_size, x.size(1), num_codes, channels)) - encoded_feat = (assignment_weights.unsqueeze(3) * - (expanded_x - reshaped_codewords)).sum(dim=1) - return encoded_feat - - def forward(self, x): - assert x.dim() == 4 and x.size(1) == self.channels - # [batch_size, channels, height, width] - batch_size = x.size(0) - # [batch_size, height x width, channels] - x = x.view(batch_size, self.channels, -1).transpose(1, 2).contiguous() - # assignment_weights: [batch_size, channels, num_codes] - assignment_weights = F.softmax( - self.scaled_l2(x, self.codewords, self.scale), dim=2) - # aggregate - encoded_feat = self.aggregate(assignment_weights, x, self.codewords) - return encoded_feat - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(Nx{self.channels}xHxW =>Nx{self.num_codes}' \ - f'x{self.channels})' - return repr_str diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/ops/wrappers.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/ops/wrappers.py deleted file mode 100644 index ce67e4beb..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/ops/wrappers.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.nn.functional as F - - -def resize(input, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None, - warning=True): - if warning: - if size is not None and align_corners: - input_h, input_w = tuple(int(x) for x in input.shape[2:]) - output_h, output_w = tuple(int(x) for x in size) - if output_h > input_h or output_w > output_h: - if ((output_h > 1 and output_w > 1 and input_h > 1 - and input_w > 1) and (output_h - 1) % (input_h - 1) - and (output_w - 1) % (input_w - 1)): - warnings.warn( - f'When align_corners={align_corners}, ' - 'the output would more aligned if ' - f'input size {(input_h, input_w)} is `x+1` and ' - f'out size {(output_h, output_w)} is `nx+1`') - return F.interpolate(input, size, scale_factor, mode, align_corners) - - -class Upsample(nn.Module): - - def __init__(self, - size=None, - scale_factor=None, - mode='nearest', - align_corners=None): - super(Upsample, self).__init__() - self.size = size - if isinstance(scale_factor, tuple): - self.scale_factor = tuple(float(factor) for factor in scale_factor) - else: - self.scale_factor = float(scale_factor) if scale_factor else None - self.mode = mode - self.align_corners = align_corners - - def forward(self, x): - if not self.size: - size = [int(t * self.scale_factor) for t in x.shape[-2:]] - else: - size = self.size - return resize(x, size, None, self.mode, self.align_corners) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/registry/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/registry/__init__.py deleted file mode 100644 index ee514d1a2..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/registry/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import (DATA_SAMPLERS, DATASETS, EVALUATOR, HOOKS, INFERENCERS, - LOG_PROCESSORS, LOOPS, METRICS, MODEL_WRAPPERS, MODELS, - OPTIM_WRAPPER_CONSTRUCTORS, OPTIM_WRAPPERS, OPTIMIZERS, - PARAM_SCHEDULERS, RUNNER_CONSTRUCTORS, RUNNERS, - TASK_UTILS, TRANSFORMS, VISBACKENDS, VISUALIZERS, - WEIGHT_INITIALIZERS) - -__all__ = [ - 'HOOKS', 'DATASETS', 'DATA_SAMPLERS', 'TRANSFORMS', 'MODELS', - 'WEIGHT_INITIALIZERS', 'OPTIMIZERS', 'OPTIM_WRAPPER_CONSTRUCTORS', - 'TASK_UTILS', 'PARAM_SCHEDULERS', 'METRICS', 'MODEL_WRAPPERS', - 'VISBACKENDS', 'VISUALIZERS', 'RUNNERS', 'RUNNER_CONSTRUCTORS', 'LOOPS', - 'EVALUATOR', 'LOG_PROCESSORS', 'OPTIM_WRAPPERS', 'INFERENCERS' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/registry/registry.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/registry/registry.py deleted file mode 100644 index 1e423980d..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/registry/registry.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""MMSegmentation provides 21 registry nodes to support using modules across -projects. Each node is a child of the root registry in MMEngine. - -More details can be found at -https://mmengine.readthedocs.io/en/latest/advanced_tutorials/registry.html. -""" - -from mmengine.registry import DATA_SAMPLERS as MMENGINE_DATA_SAMPLERS -from mmengine.registry import DATASETS as MMENGINE_DATASETS -from mmengine.registry import EVALUATOR as MMENGINE_EVALUATOR -from mmengine.registry import HOOKS as MMENGINE_HOOKS -from mmengine.registry import INFERENCERS as MMENGINE_INFERENCERS -from mmengine.registry import LOG_PROCESSORS as MMENGINE_LOG_PROCESSORS -from mmengine.registry import LOOPS as MMENGINE_LOOPS -from mmengine.registry import METRICS as MMENGINE_METRICS -from mmengine.registry import MODEL_WRAPPERS as MMENGINE_MODEL_WRAPPERS -from mmengine.registry import MODELS as MMENGINE_MODELS -from mmengine.registry import \ - OPTIM_WRAPPER_CONSTRUCTORS as MMENGINE_OPTIM_WRAPPER_CONSTRUCTORS -from mmengine.registry import OPTIM_WRAPPERS as MMENGINE_OPTIM_WRAPPERS -from mmengine.registry import OPTIMIZERS as MMENGINE_OPTIMIZERS -from mmengine.registry import PARAM_SCHEDULERS as MMENGINE_PARAM_SCHEDULERS -from mmengine.registry import \ - RUNNER_CONSTRUCTORS as MMENGINE_RUNNER_CONSTRUCTORS -from mmengine.registry import RUNNERS as MMENGINE_RUNNERS -from mmengine.registry import TASK_UTILS as MMENGINE_TASK_UTILS -from mmengine.registry import TRANSFORMS as MMENGINE_TRANSFORMS -from mmengine.registry import VISBACKENDS as MMENGINE_VISBACKENDS -from mmengine.registry import VISUALIZERS as MMENGINE_VISUALIZERS -from mmengine.registry import \ - WEIGHT_INITIALIZERS as MMENGINE_WEIGHT_INITIALIZERS -from mmengine.registry import Registry - -# manage all kinds of runners like `EpochBasedRunner` and `IterBasedRunner` -RUNNERS = Registry('runner', parent=MMENGINE_RUNNERS) -# manage runner constructors that define how to initialize runners -RUNNER_CONSTRUCTORS = Registry( - 'runner constructor', parent=MMENGINE_RUNNER_CONSTRUCTORS) -# manage all kinds of loops like `EpochBasedTrainLoop` -LOOPS = Registry('loop', parent=MMENGINE_LOOPS) -# manage all kinds of hooks like `CheckpointHook` -HOOKS = Registry( - 'hook', parent=MMENGINE_HOOKS, locations=['mmseg.engine.hooks']) - -# manage data-related modules -DATASETS = Registry( - 'dataset', parent=MMENGINE_DATASETS, locations=['mmseg.datasets']) -DATA_SAMPLERS = Registry('data sampler', parent=MMENGINE_DATA_SAMPLERS) -TRANSFORMS = Registry( - 'transform', - parent=MMENGINE_TRANSFORMS, - locations=['mmseg.datasets.transforms']) - -# mangage all kinds of modules inheriting `nn.Module` -MODELS = Registry('model', parent=MMENGINE_MODELS, locations=['mmseg.models']) -# mangage all kinds of model wrappers like 'MMDistributedDataParallel' -MODEL_WRAPPERS = Registry( - 'model_wrapper', - parent=MMENGINE_MODEL_WRAPPERS, - locations=['mmseg.models']) -# mangage all kinds of weight initialization modules like `Uniform` -WEIGHT_INITIALIZERS = Registry( - 'weight initializer', - parent=MMENGINE_WEIGHT_INITIALIZERS, - locations=['mmseg.models']) - -# mangage all kinds of optimizers like `SGD` and `Adam` -OPTIMIZERS = Registry( - 'optimizer', - parent=MMENGINE_OPTIMIZERS, - locations=['mmseg.engine.optimizers']) -# manage optimizer wrapper -OPTIM_WRAPPERS = Registry( - 'optim_wrapper', - parent=MMENGINE_OPTIM_WRAPPERS, - locations=['mmseg.engine.optimizers']) -# manage constructors that customize the optimization hyperparameters. -OPTIM_WRAPPER_CONSTRUCTORS = Registry( - 'optimizer wrapper constructor', - parent=MMENGINE_OPTIM_WRAPPER_CONSTRUCTORS, - locations=['mmseg.engine.optimizers']) -# mangage all kinds of parameter schedulers like `MultiStepLR` -PARAM_SCHEDULERS = Registry( - 'parameter scheduler', parent=MMENGINE_PARAM_SCHEDULERS) - -# manage all kinds of metrics -METRICS = Registry( - 'metric', parent=MMENGINE_METRICS, locations=['mmseg.evaluation']) -# manage evaluator -EVALUATOR = Registry( - 'evaluator', parent=MMENGINE_EVALUATOR, locations=['mmseg.evaluation']) - -# manage task-specific modules like ohem pixel sampler -TASK_UTILS = Registry( - 'task util', parent=MMENGINE_TASK_UTILS, locations=['mmseg.models']) - -# manage visualizer -VISUALIZERS = Registry( - 'visualizer', - parent=MMENGINE_VISUALIZERS, - locations=['mmseg.visualization']) -# manage visualizer backend -VISBACKENDS = Registry( - 'vis_backend', - parent=MMENGINE_VISBACKENDS, - locations=['mmseg.visualization']) - -# manage logprocessor -LOG_PROCESSORS = Registry( - 'log_processor', - parent=MMENGINE_LOG_PROCESSORS, - locations=['mmseg.visualization']) - -# manage inferencer -INFERENCERS = Registry('inferencer', parent=MMENGINE_INFERENCERS) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/__init__.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/__init__.py deleted file mode 100644 index ed002c7de..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collect_env import collect_env -from .logger import get_root_logger -from .misc import find_latest_checkpoint -from .set_env import setup_multi_processes - -__all__ = [ - 'get_root_logger', 'collect_env', 'find_latest_checkpoint', - 'setup_multi_processes' -] diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/collect_env.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/collect_env.py deleted file mode 100644 index 3379ecb06..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/collect_env.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash - -import mmseg - - -def collect_env(): - """Collect the information of the running environments.""" - env_info = collect_base_env() - env_info['MMSegmentation'] = f'{mmseg.__version__}+{get_git_hash()[:7]}' - - return env_info - - -if __name__ == '__main__': - for name, val in collect_env().items(): - print('{}: {}'.format(name, val)) diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/logger.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/logger.py deleted file mode 100644 index 0cb3c78d6..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/logger.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -from mmcv.utils import get_logger - - -def get_root_logger(log_file=None, log_level=logging.INFO): - """Get the root logger. - - The logger will be initialized if it has not been initialized. By default a - StreamHandler will be added. If `log_file` is specified, a FileHandler will - also be added. The name of the root logger is the top-level package name, - e.g., "mmseg". - - Args: - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the root logger. - log_level (int): The root logger level. Note that only the process of - rank 0 is affected, while other processes will set the level to - "Error" and be silent most of the time. - - Returns: - logging.Logger: The root logger. - """ - - logger = get_logger(name='mmseg', log_file=log_file, log_level=log_level) - - return logger diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/misc.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/misc.py deleted file mode 100644 index bd1b6b163..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/misc.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os.path as osp -import warnings - - -def find_latest_checkpoint(path, suffix='pth'): - """This function is for finding the latest checkpoint. - - It will be used when automatically resume, modified from - https://github.com/open-mmlab/mmdetection/blob/dev-v2.20.0/mmdet/utils/misc.py - - Args: - path (str): The path to find checkpoints. - suffix (str): File extension for the checkpoint. Defaults to pth. - - Returns: - latest_path(str | None): File path of the latest checkpoint. - """ - if not osp.exists(path): - warnings.warn("The path of the checkpoints doesn't exist.") - return None - if osp.exists(osp.join(path, f'latest.{suffix}')): - return osp.join(path, f'latest.{suffix}') - - checkpoints = glob.glob(osp.join(path, f'*.{suffix}')) - if len(checkpoints) == 0: - warnings.warn('The are no checkpoints in the path') - return None - latest = -1 - latest_path = '' - for checkpoint in checkpoints: - if len(checkpoint) < len(latest_path): - continue - # `count` is iteration number, as checkpoints are saved as - # 'iter_xx.pth' or 'epoch_xx.pth' and xx is iteration number. - count = int(osp.basename(checkpoint).split('_')[-1].split('.')[0]) - if count > latest: - latest = count - latest_path = checkpoint - return latest_path diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/set_env.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/set_env.py deleted file mode 100644 index b2d3aaf14..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/utils/set_env.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import platform - -import cv2 -import torch.multiprocessing as mp - -from ..utils import get_root_logger - - -def setup_multi_processes(cfg): - """Setup multi-processing environment variables.""" - logger = get_root_logger() - - # set multi-process start method - if platform.system() != 'Windows': - mp_start_method = cfg.get('mp_start_method', None) - current_method = mp.get_start_method(allow_none=True) - if mp_start_method in ('fork', 'spawn', 'forkserver'): - logger.info( - f'Multi-processing start method `{mp_start_method}` is ' - f'different from the previous setting `{current_method}`.' - f'It will be force set to `{mp_start_method}`.') - mp.set_start_method(mp_start_method, force=True) - else: - logger.info( - f'Multi-processing start method is `{mp_start_method}`') - - # disable opencv multithreading to avoid system being overloaded - opencv_num_threads = cfg.get('opencv_num_threads', None) - if isinstance(opencv_num_threads, int): - logger.info(f'OpenCV num_threads is `{opencv_num_threads}`') - cv2.setNumThreads(opencv_num_threads) - else: - logger.info(f'OpenCV num_threads is `{cv2.getNumThreads}') - - if cfg.data.workers_per_gpu > 1: - # setup OMP threads - # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa - omp_num_threads = cfg.get('omp_num_threads', None) - if 'OMP_NUM_THREADS' not in os.environ: - if isinstance(omp_num_threads, int): - logger.info(f'OMP num threads is {omp_num_threads}') - os.environ['OMP_NUM_THREADS'] = str(omp_num_threads) - else: - logger.info(f'OMP num threads is {os.environ["OMP_NUM_THREADS"] }') - - # setup MKL threads - if 'MKL_NUM_THREADS' not in os.environ: - mkl_num_threads = cfg.get('mkl_num_threads', None) - if isinstance(mkl_num_threads, int): - logger.info(f'MKL num threads is {mkl_num_threads}') - os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads) - else: - logger.info(f'MKL num threads is {os.environ["MKL_NUM_THREADS"]}') diff --git a/cv/semantic_segmentation/stdc/pytorch/mmseg/version.py b/cv/semantic_segmentation/stdc/pytorch/mmseg/version.py deleted file mode 100644 index e05146f0a..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/mmseg/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) Open-MMLab. All rights reserved. - -__version__ = '0.24.1' - - -def parse_version_info(version_str): - version_info = [] - for x in version_str.split('.'): - if x.isdigit(): - version_info.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - version_info.append(int(patch_version[0])) - version_info.append(f'rc{patch_version[1]}') - return tuple(version_info) - - -version_info = parse_version_info(__version__) diff --git a/cv/semantic_segmentation/stdc/pytorch/requirements.txt b/cv/semantic_segmentation/stdc/pytorch/requirements.txt deleted file mode 100644 index 16b558673..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ --r requirements/mmcv/runtime.txt --r requirements/apcnet.txt -opencv-python3 -cityscapesscripts \ No newline at end of file diff --git a/cv/semantic_segmentation/stdc/pytorch/requirements/apcnet.txt b/cv/semantic_segmentation/stdc/pytorch/requirements/apcnet.txt deleted file mode 100644 index 2712f504c..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/requirements/apcnet.txt +++ /dev/null @@ -1,4 +0,0 @@ -matplotlib -numpy -packaging -prettytable diff --git a/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/build.txt b/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/build.txt deleted file mode 100644 index abf514853..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/build.txt +++ /dev/null @@ -1 +0,0 @@ -pytest-runner diff --git a/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/docs.txt b/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/docs.txt deleted file mode 100644 index 6a5319af3..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/docs.txt +++ /dev/null @@ -1,8 +0,0 @@ -docutils==0.16.0 -myst-parser -opencv-python --e git+https://github.com/open-mmlab/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme -sphinx==4.0.2 -sphinx-copybutton -sphinx_markdown_tables -torch diff --git a/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/optional.txt b/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/optional.txt deleted file mode 100644 index 63730036f..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/optional.txt +++ /dev/null @@ -1 +0,0 @@ -ninja diff --git a/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/requirements.txt b/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/requirements.txt deleted file mode 100644 index 6f00e6303..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ --r requirements/mmcv/build.txt --r requirements/mmcv/optional.txt --r requirements/mmcv/runtime.txt --r requirements/mmcv/test.txt diff --git a/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/runtime.txt b/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/runtime.txt deleted file mode 100644 index 66e90d674..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/runtime.txt +++ /dev/null @@ -1,7 +0,0 @@ -addict -numpy -packaging -Pillow -pyyaml -regex;sys_platform=='win32' -yapf diff --git a/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/test.txt b/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/test.txt deleted file mode 100644 index 6d9d17b98..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/requirements/mmcv/test.txt +++ /dev/null @@ -1,9 +0,0 @@ -coverage -lmdb -onnx==1.7.0; python_version < '3.10' -onnxoptimizer; python_version < '3.10' -onnxruntime>=1.8.0; python_version < '3.10' -pytest -PyTurboJPEG -scipy -tifffile diff --git a/cv/semantic_segmentation/stdc/pytorch/requirements/mminstall.txt b/cv/semantic_segmentation/stdc/pytorch/requirements/mminstall.txt deleted file mode 100644 index bd43faf87..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/requirements/mminstall.txt +++ /dev/null @@ -1,2 +0,0 @@ -mmcls>=0.20.1 -mmcv-full>=1.4.4,<=1.6.0 diff --git a/cv/semantic_segmentation/stdc/pytorch/requirements/optional.txt b/cv/semantic_segmentation/stdc/pytorch/requirements/optional.txt deleted file mode 100644 index 47fa59331..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/requirements/optional.txt +++ /dev/null @@ -1 +0,0 @@ -cityscapesscripts diff --git a/cv/semantic_segmentation/stdc/pytorch/requirements/runtime.txt b/cv/semantic_segmentation/stdc/pytorch/requirements/runtime.txt deleted file mode 100644 index 520408fe8..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/requirements/runtime.txt +++ /dev/null @@ -1,5 +0,0 @@ -matplotlib -mmcls>=0.20.1 -numpy -packaging -prettytable diff --git a/cv/semantic_segmentation/stdc/pytorch/setup.py b/cv/semantic_segmentation/stdc/pytorch/setup.py deleted file mode 100644 index d409997db..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/setup.py +++ /dev/null @@ -1,258 +0,0 @@ -import glob -import os -import platform -import re -import warnings -from pkg_resources import DistributionNotFound, get_distribution -from setuptools import find_packages, setup - -EXT_TYPE = '' -try: - import torch - if torch.__version__ == 'parrots': - from parrots.utils.build_extension import BuildExtension - EXT_TYPE = 'parrots' - elif (hasattr(torch, 'is_mlu_available') and torch.is_mlu_available()) or \ - os.getenv('FORCE_MLU', '0') == '1': - from torch_mlu.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - else: - from torch.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - cmd_class = {'build_ext': BuildExtension} -except ModuleNotFoundError: - cmd_class = {} - print('Skip building ext ops due to the absence of torch.') - - -def choose_requirement(primary, secondary): - """If some version of primary requirement installed, return primary, else - return secondary.""" - try: - name = re.split(r'[!<>=]', primary)[0] - get_distribution(name) - except DistributionNotFound: - return secondary - - return str(primary) - - -def get_version(): - version_file = 'mmcv/version.py' - with open(version_file, 'r', encoding='utf-8') as f: - exec(compile(f.read(), version_file, 'exec')) - return locals()['__version__'] - - -def parse_requirements(fname='requirements/mmcv/runtime.txt', with_version=True): - """Parse the package dependencies listed in a requirements file but strips - specific versioning information. - - Args: - fname (str): path to requirements file - with_version (bool, default=False): if True include version specs - - Returns: - List[str]: list of requirements items - - CommandLine: - python -c "import setup; print(setup.parse_requirements())" - """ - import sys - from os.path import exists - require_fpath = fname - - def parse_line(line): - """Parse information from a line in a requirements text file.""" - if line.startswith('-r '): - # Allow specifying requirements in other files - target = line.split(' ')[1] - for info in parse_require_file(target): - yield info - else: - info = {'line': line} - if line.startswith('-e '): - info['package'] = line.split('#egg=')[1] - else: - # Remove versioning from the package - pat = '(' + '|'.join(['>=', '==', '>']) + ')' - parts = re.split(pat, line, maxsplit=1) - parts = [p.strip() for p in parts] - - info['package'] = parts[0] - if len(parts) > 1: - op, rest = parts[1:] - if ';' in rest: - # Handle platform specific dependencies - # http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies - version, platform_deps = map(str.strip, - rest.split(';')) - info['platform_deps'] = platform_deps - else: - version = rest # NOQA - info['version'] = (op, version) - yield info - - def parse_require_file(fpath): - with open(fpath, 'r') as f: - for line in f.readlines(): - line = line.strip() - if line and not line.startswith('#'): - for info in parse_line(line): - yield info - - def gen_packages_items(): - if exists(require_fpath): - for info in parse_require_file(require_fpath): - parts = [info['package']] - if with_version and 'version' in info: - parts.extend(info['version']) - if not sys.version.startswith('3.4'): - # apparently package_deps are broken in 3.4 - platform_deps = info.get('platform_deps') - if platform_deps is not None: - parts.append(';' + platform_deps) - item = ''.join(parts) - yield item - - packages = list(gen_packages_items()) - return packages - - -install_requires = parse_requirements() - -try: - # OpenCV installed via conda. - import cv2 # NOQA: F401 - major, minor, *rest = cv2.__version__.split('.') - if int(major) < 3: - raise RuntimeError( - f'OpenCV >=3 is required but {cv2.__version__} is installed') -except ImportError: - # If first not installed install second package - CHOOSE_INSTALL_REQUIRES = [('opencv-python-headless>=3', - 'opencv-python>=3')] - for main, secondary in CHOOSE_INSTALL_REQUIRES: - install_requires.append(choose_requirement(main, secondary)) - - -def get_extensions(): - extensions = [] - - if EXT_TYPE == 'pytorch': - ext_name = 'mmcv._ext' - from torch.utils.cpp_extension import CppExtension, CUDAExtension - - # prevent ninja from using too many resources - try: - import psutil - num_cpu = len(psutil.Process().cpu_affinity()) - cpu_use = max(4, num_cpu - 1) - except (ModuleNotFoundError, AttributeError): - cpu_use = 4 - - os.environ.setdefault('MAX_JOBS', str(cpu_use)) - define_macros = [] - - # Before PyTorch1.8.0, when compiling CUDA code, `cxx` is a - # required key passed to PyTorch. Even if there is no flag passed - # to cxx, users also need to pass an empty list to PyTorch. - # Since PyTorch1.8.0, it has a default value so users do not need - # to pass an empty list anymore. - # More details at https://github.com/pytorch/pytorch/pull/45956 - extra_compile_args = {'cxx': []} - - # Since the PR (https://github.com/open-mmlab/mmcv/pull/1463) uses - # c++14 features, the argument ['std=c++14'] must be added here. - # However, in the windows environment, some standard libraries - # will depend on c++17 or higher. In fact, for the windows - # environment, the compiler will choose the appropriate compiler - # to compile those cpp files, so there is no need to add the - # argument - if platform.system() != 'Windows': - extra_compile_args['cxx'] = ['-std=c++14'] - - include_dirs = [] - - is_rocm_pytorch = False - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm_pytorch = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - - if is_rocm_pytorch or torch.cuda.is_available() or os.getenv( - 'FORCE_CUDA', '0') == '1': - if is_rocm_pytorch: - define_macros += [('HIP_DIFF', None)] - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cpp') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - else: - print(f'Compiling {ext_name} only with CPU') - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cpu/*.cpp') - extension = CppExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - - # Since the PR (https://github.com/open-mmlab/mmcv/pull/1463) uses - # c++14 features, the argument ['std=c++14'] must be added here. - # However, in the windows environment, some standard libraries - # will depend on c++17 or higher. In fact, for the windows - # environment, the compiler will choose the appropriate compiler - # to compile those cpp files, so there is no need to add the - # argument - if 'nvcc' in extra_compile_args and platform.system() != 'Windows': - extra_compile_args['nvcc'] += ['-std=c++14'] - - ext_ops = extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args) - extensions.append(ext_ops) - - return extensions - - -setup( - name='mmcv' if os.getenv('MMCV_WITH_OPS', '0') == '0' else 'mmcv-full', - version=get_version(), - description='OpenMMLab Computer Vision Foundation', - keywords='computer vision', - packages=find_packages(), - include_package_data=True, - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Topic :: Utilities', - ], - url='https://github.com/open-mmlab/mmcv', - author='MMCV Contributors', - author_email='openmmlab@gmail.com', - install_requires=install_requires, - extras_require={ - 'all': parse_requirements('requirements/mmcv/requirements.txt'), - 'tests': parse_requirements('requirements/mmcv/test.txt'), - 'build': parse_requirements('requirements/mmcv/build.txt'), - 'optional': parse_requirements('requirements/mmcv/optional.txt'), - }, - ext_modules=get_extensions(), - cmdclass=cmd_class, - zip_safe=False) diff --git a/cv/semantic_segmentation/stdc/pytorch/tools/analyze_logs.py b/cv/semantic_segmentation/stdc/pytorch/tools/analyze_logs.py deleted file mode 100644 index e2127d4d6..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/tools/analyze_logs.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Modified from https://github.com/open- -mmlab/mmdetection/blob/master/tools/analysis_tools/analyze_logs.py.""" -import argparse -import json -from collections import defaultdict - -import matplotlib.pyplot as plt -import seaborn as sns - - -def plot_curve(log_dicts, args): - if args.backend is not None: - plt.switch_backend(args.backend) - sns.set_style(args.style) - # if legend is None, use {filename}_{key} as legend - legend = args.legend - if legend is None: - legend = [] - for json_log in args.json_logs: - for metric in args.keys: - legend.append(f'{json_log}_{metric}') - assert len(legend) == (len(args.json_logs) * len(args.keys)) - metrics = args.keys - - num_metrics = len(metrics) - for i, log_dict in enumerate(log_dicts): - epochs = list(log_dict.keys()) - for j, metric in enumerate(metrics): - print(f'plot curve of {args.json_logs[i]}, metric is {metric}') - plot_epochs = [] - plot_iters = [] - plot_values = [] - # In some log files exist lines of validation, - # `mode` list is used to only collect iter number - # of training line. - for epoch in epochs: - epoch_logs = log_dict[epoch] - if metric not in epoch_logs.keys(): - continue - if metric in ['mIoU', 'mAcc', 'aAcc']: - plot_epochs.append(epoch) - plot_values.append(epoch_logs[metric][0]) - else: - for idx in range(len(epoch_logs[metric])): - if epoch_logs['mode'][idx] == 'train': - plot_iters.append(epoch_logs['iter'][idx]) - plot_values.append(epoch_logs[metric][idx]) - ax = plt.gca() - label = legend[i * num_metrics + j] - if metric in ['mIoU', 'mAcc', 'aAcc']: - ax.set_xticks(plot_epochs) - plt.xlabel('epoch') - plt.plot(plot_epochs, plot_values, label=label, marker='o') - else: - plt.xlabel('iter') - plt.plot(plot_iters, plot_values, label=label, linewidth=0.5) - plt.legend() - if args.title is not None: - plt.title(args.title) - if args.out is None: - plt.show() - else: - print(f'save curve to: {args.out}') - plt.savefig(args.out) - plt.cla() - - -def parse_args(): - parser = argparse.ArgumentParser(description='Analyze Json Log') - parser.add_argument( - 'json_logs', - type=str, - nargs='+', - help='path of train log in json format') - parser.add_argument( - '--keys', - type=str, - nargs='+', - default=['mIoU'], - help='the metric that you want to plot') - parser.add_argument('--title', type=str, help='title of figure') - parser.add_argument( - '--legend', - type=str, - nargs='+', - default=None, - help='legend of each plot') - parser.add_argument( - '--backend', type=str, default=None, help='backend of plt') - parser.add_argument( - '--style', type=str, default='dark', help='style of plt') - parser.add_argument('--out', type=str, default=None) - args = parser.parse_args() - return args - - -def load_json_logs(json_logs): - # load and convert json_logs to log_dict, key is epoch, value is a sub dict - # keys of sub dict is different metrics - # value of sub dict is a list of corresponding values of all iterations - log_dicts = [dict() for _ in json_logs] - for json_log, log_dict in zip(json_logs, log_dicts): - with open(json_log, 'r') as log_file: - for line in log_file: - log = json.loads(line.strip()) - # skip lines without `epoch` field - if 'epoch' not in log: - continue - epoch = log.pop('epoch') - if epoch not in log_dict: - log_dict[epoch] = defaultdict(list) - for k, v in log.items(): - log_dict[epoch][k].append(v) - return log_dicts - - -def main(): - args = parse_args() - json_logs = args.json_logs - for json_log in json_logs: - assert json_log.endswith('.json') - log_dicts = load_json_logs(json_logs) - plot_curve(log_dicts, args) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/stdc/pytorch/tools/benchmark.py b/cv/semantic_segmentation/stdc/pytorch/tools/benchmark.py deleted file mode 100644 index f6d688848..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/tools/benchmark.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import time - -import mmcv -import numpy as np -import torch -from mmcv import Config -from mmcv.parallel import MMDataParallel -from mmcv.runner import load_checkpoint, wrap_fp16_model - -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='MMSeg benchmark a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--log-interval', type=int, default=50, help='interval of logging') - parser.add_argument( - '--work-dir', - help=('if specified, the results will be dumped ' - 'into the directory as json')) - parser.add_argument('--repeat-times', type=int, default=1) - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.work_dir is not None: - mmcv.mkdir_or_exist(osp.abspath(args.work_dir)) - json_file = osp.join(args.work_dir, f'fps_{timestamp}.json') - else: - # use config filename as default work_dir if cfg.work_dir is None - work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - mmcv.mkdir_or_exist(osp.abspath(work_dir)) - json_file = osp.join(work_dir, f'fps_{timestamp}.json') - - repeat_times = args.repeat_times - # set cudnn_benchmark - torch.backends.cudnn.benchmark = False - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - benchmark_dict = dict(config=args.config, unit='img / s') - overall_fps_list = [] - for time_index in range(repeat_times): - print(f'Run {time_index + 1}:') - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - data_loader = build_dataloader( - dataset, - samples_per_gpu=1, - workers_per_gpu=cfg.data.workers_per_gpu, - dist=False, - shuffle=False) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - if 'checkpoint' in args and osp.exists(args.checkpoint): - load_checkpoint(model, args.checkpoint, map_location='cpu') - - model = MMDataParallel(model, device_ids=[0]) - - model.eval() - - # the first several iterations may be very slow so skip them - num_warmup = 5 - pure_inf_time = 0 - total_iters = 200 - - # benchmark with 200 image and take the average - for i, data in enumerate(data_loader): - - torch.cuda.synchronize() - start_time = time.perf_counter() - - with torch.no_grad(): - model(return_loss=False, rescale=True, **data) - - torch.cuda.synchronize() - elapsed = time.perf_counter() - start_time - - if i >= num_warmup: - pure_inf_time += elapsed - if (i + 1) % args.log_interval == 0: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Done image [{i + 1:<3}/ {total_iters}], ' - f'fps: {fps:.2f} img / s') - - if (i + 1) == total_iters: - fps = (i + 1 - num_warmup) / pure_inf_time - print(f'Overall fps: {fps:.2f} img / s\n') - benchmark_dict[f'overall_fps_{time_index + 1}'] = round(fps, 2) - overall_fps_list.append(fps) - break - benchmark_dict['average_fps'] = round(np.mean(overall_fps_list), 2) - benchmark_dict['fps_variance'] = round(np.var(overall_fps_list), 4) - print(f'Average fps of {repeat_times} evaluations: ' - f'{benchmark_dict["average_fps"]}') - print(f'The variance of {repeat_times} evaluations: ' - f'{benchmark_dict["fps_variance"]}') - mmcv.dump(benchmark_dict, json_file, indent=4) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/stdc/pytorch/tools/confusion_matrix.py b/cv/semantic_segmentation/stdc/pytorch/tools/confusion_matrix.py deleted file mode 100644 index 2c5b64cf4..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/tools/confusion_matrix.py +++ /dev/null @@ -1,184 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os - -import matplotlib.pyplot as plt -import mmcv -import numpy as np -from matplotlib.ticker import MultipleLocator -from mmcv import Config, DictAction - -from mmseg.datasets import build_dataset - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Generate confusion matrix from segmentation results') - parser.add_argument('config', help='test config file path') - parser.add_argument( - 'prediction_path', help='prediction path where test .pkl result') - parser.add_argument( - 'save_dir', help='directory where confusion matrix will be saved') - parser.add_argument( - '--show', action='store_true', help='show confusion matrix') - parser.add_argument( - '--color-theme', - default='winter', - help='theme of the matrix color map') - parser.add_argument( - '--title', - default='Normalized Confusion Matrix', - help='title of the matrix color map') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - args = parser.parse_args() - return args - - -def calculate_confusion_matrix(dataset, results): - """Calculate the confusion matrix. - - Args: - dataset (Dataset): Test or val dataset. - results (list[ndarray]): A list of segmentation results in each image. - """ - n = len(dataset.CLASSES) - confusion_matrix = np.zeros(shape=[n, n]) - assert len(dataset) == len(results) - prog_bar = mmcv.ProgressBar(len(results)) - for idx, per_img_res in enumerate(results): - res_segm = per_img_res - gt_segm = dataset.get_gt_seg_map_by_idx(idx) - inds = n * gt_segm + res_segm - inds = inds.flatten() - mat = np.bincount(inds, minlength=n**2).reshape(n, n) - confusion_matrix += mat - prog_bar.update() - return confusion_matrix - - -def plot_confusion_matrix(confusion_matrix, - labels, - save_dir=None, - show=True, - title='Normalized Confusion Matrix', - color_theme='winter'): - """Draw confusion matrix with matplotlib. - - Args: - confusion_matrix (ndarray): The confusion matrix. - labels (list[str]): List of class names. - save_dir (str|optional): If set, save the confusion matrix plot to the - given path. Default: None. - show (bool): Whether to show the plot. Default: True. - title (str): Title of the plot. Default: `Normalized Confusion Matrix`. - color_theme (str): Theme of the matrix color map. Default: `winter`. - """ - # normalize the confusion matrix - per_label_sums = confusion_matrix.sum(axis=1)[:, np.newaxis] - confusion_matrix = \ - confusion_matrix.astype(np.float32) / per_label_sums * 100 - - num_classes = len(labels) - fig, ax = plt.subplots( - figsize=(2 * num_classes, 2 * num_classes * 0.8), dpi=180) - cmap = plt.get_cmap(color_theme) - im = ax.imshow(confusion_matrix, cmap=cmap) - plt.colorbar(mappable=im, ax=ax) - - title_font = {'weight': 'bold', 'size': 12} - ax.set_title(title, fontdict=title_font) - label_font = {'size': 10} - plt.ylabel('Ground Truth Label', fontdict=label_font) - plt.xlabel('Prediction Label', fontdict=label_font) - - # draw locator - xmajor_locator = MultipleLocator(1) - xminor_locator = MultipleLocator(0.5) - ax.xaxis.set_major_locator(xmajor_locator) - ax.xaxis.set_minor_locator(xminor_locator) - ymajor_locator = MultipleLocator(1) - yminor_locator = MultipleLocator(0.5) - ax.yaxis.set_major_locator(ymajor_locator) - ax.yaxis.set_minor_locator(yminor_locator) - - # draw grid - ax.grid(True, which='minor', linestyle='-') - - # draw label - ax.set_xticks(np.arange(num_classes)) - ax.set_yticks(np.arange(num_classes)) - ax.set_xticklabels(labels) - ax.set_yticklabels(labels) - - ax.tick_params( - axis='x', bottom=False, top=True, labelbottom=False, labeltop=True) - plt.setp( - ax.get_xticklabels(), rotation=45, ha='left', rotation_mode='anchor') - - # draw confusion matrix value - for i in range(num_classes): - for j in range(num_classes): - ax.text( - j, - i, - '{}%'.format( - round(confusion_matrix[i, j], 2 - ) if not np.isnan(confusion_matrix[i, j]) else -1), - ha='center', - va='center', - color='w', - size=7) - - ax.set_ylim(len(confusion_matrix) - 0.5, -0.5) # matplotlib>3.1.1 - - fig.tight_layout() - if save_dir is not None: - plt.savefig( - os.path.join(save_dir, 'confusion_matrix.png'), format='png') - if show: - plt.show() - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - results = mmcv.load(args.prediction_path) - - assert isinstance(results, list) - if isinstance(results[0], np.ndarray): - pass - else: - raise TypeError('invalid type of prediction results') - - if isinstance(cfg.data.test, dict): - cfg.data.test.test_mode = True - elif isinstance(cfg.data.test, list): - for ds_cfg in cfg.data.test: - ds_cfg.test_mode = True - - dataset = build_dataset(cfg.data.test) - confusion_matrix = calculate_confusion_matrix(dataset, results) - plot_confusion_matrix( - confusion_matrix, - dataset.CLASSES, - save_dir=args.save_dir, - show=args.show, - title=args.title, - color_theme=args.color_theme) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/cityscapes.py b/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/cityscapes.py deleted file mode 100644 index 17b616847..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/cityscapes.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp - -import mmcv -from cityscapesscripts.preparation.json2labelImg import json2labelImg - - -def convert_json_to_label(json_file): - label_file = json_file.replace('_polygons.json', '_labelTrainIds.png') - json2labelImg(json_file, label_file, 'trainIds') - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert Cityscapes annotations to TrainIds') - parser.add_argument('cityscapes_path', help='cityscapes data path') - parser.add_argument('--gt-dir', default='gtFine', type=str) - parser.add_argument('-o', '--out-dir', help='output path') - parser.add_argument( - '--nproc', default=1, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - cityscapes_path = args.cityscapes_path - out_dir = args.out_dir if args.out_dir else cityscapes_path - mmcv.mkdir_or_exist(out_dir) - - gt_dir = osp.join(cityscapes_path, args.gt_dir) - - poly_files = [] - for poly in mmcv.scandir(gt_dir, '_polygons.json', recursive=True): - poly_file = osp.join(gt_dir, poly) - poly_files.append(poly_file) - if args.nproc > 1: - mmcv.track_parallel_progress(convert_json_to_label, poly_files, - args.nproc) - else: - mmcv.track_progress(convert_json_to_label, poly_files) - - split_names = ['train', 'val', 'test'] - - for split in split_names: - filenames = [] - for poly in mmcv.scandir( - osp.join(gt_dir, split), '_polygons.json', recursive=True): - filenames.append(poly.replace('_gtFine_polygons.json', '')) - with open(osp.join(out_dir, f'{split}.txt'), 'w') as f: - f.writelines(f + '\n' for f in filenames) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/coco_stuff10k.py b/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/coco_stuff10k.py deleted file mode 100644 index 374f81970..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/coco_stuff10k.py +++ /dev/null @@ -1,307 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import shutil -from functools import partial - -import mmcv -import numpy as np -from PIL import Image -from scipy.io import loadmat - -COCO_LEN = 10000 - -clsID_to_trID = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 13: 12, - 14: 13, - 15: 14, - 16: 15, - 17: 16, - 18: 17, - 19: 18, - 20: 19, - 21: 20, - 22: 21, - 23: 22, - 24: 23, - 25: 24, - 27: 25, - 28: 26, - 31: 27, - 32: 28, - 33: 29, - 34: 30, - 35: 31, - 36: 32, - 37: 33, - 38: 34, - 39: 35, - 40: 36, - 41: 37, - 42: 38, - 43: 39, - 44: 40, - 46: 41, - 47: 42, - 48: 43, - 49: 44, - 50: 45, - 51: 46, - 52: 47, - 53: 48, - 54: 49, - 55: 50, - 56: 51, - 57: 52, - 58: 53, - 59: 54, - 60: 55, - 61: 56, - 62: 57, - 63: 58, - 64: 59, - 65: 60, - 67: 61, - 70: 62, - 72: 63, - 73: 64, - 74: 65, - 75: 66, - 76: 67, - 77: 68, - 78: 69, - 79: 70, - 80: 71, - 81: 72, - 82: 73, - 84: 74, - 85: 75, - 86: 76, - 87: 77, - 88: 78, - 89: 79, - 90: 80, - 92: 81, - 93: 82, - 94: 83, - 95: 84, - 96: 85, - 97: 86, - 98: 87, - 99: 88, - 100: 89, - 101: 90, - 102: 91, - 103: 92, - 104: 93, - 105: 94, - 106: 95, - 107: 96, - 108: 97, - 109: 98, - 110: 99, - 111: 100, - 112: 101, - 113: 102, - 114: 103, - 115: 104, - 116: 105, - 117: 106, - 118: 107, - 119: 108, - 120: 109, - 121: 110, - 122: 111, - 123: 112, - 124: 113, - 125: 114, - 126: 115, - 127: 116, - 128: 117, - 129: 118, - 130: 119, - 131: 120, - 132: 121, - 133: 122, - 134: 123, - 135: 124, - 136: 125, - 137: 126, - 138: 127, - 139: 128, - 140: 129, - 141: 130, - 142: 131, - 143: 132, - 144: 133, - 145: 134, - 146: 135, - 147: 136, - 148: 137, - 149: 138, - 150: 139, - 151: 140, - 152: 141, - 153: 142, - 154: 143, - 155: 144, - 156: 145, - 157: 146, - 158: 147, - 159: 148, - 160: 149, - 161: 150, - 162: 151, - 163: 152, - 164: 153, - 165: 154, - 166: 155, - 167: 156, - 168: 157, - 169: 158, - 170: 159, - 171: 160, - 172: 161, - 173: 162, - 174: 163, - 175: 164, - 176: 165, - 177: 166, - 178: 167, - 179: 168, - 180: 169, - 181: 170, - 182: 171 -} - - -def convert_to_trainID(tuple_path, in_img_dir, in_ann_dir, out_img_dir, - out_mask_dir, is_train): - imgpath, maskpath = tuple_path - shutil.copyfile( - osp.join(in_img_dir, imgpath), - osp.join(out_img_dir, 'train2014', imgpath) if is_train else osp.join( - out_img_dir, 'test2014', imgpath)) - annotate = loadmat(osp.join(in_ann_dir, maskpath)) - mask = annotate['S'].astype(np.uint8) - mask_copy = mask.copy() - for clsID, trID in clsID_to_trID.items(): - mask_copy[mask == clsID] = trID - seg_filename = osp.join(out_mask_dir, 'train2014', - maskpath.split('.')[0] + - '_labelTrainIds.png') if is_train else osp.join( - out_mask_dir, 'test2014', - maskpath.split('.')[0] + '_labelTrainIds.png') - Image.fromarray(mask_copy).save(seg_filename, 'PNG') - - -def generate_coco_list(folder): - train_list = osp.join(folder, 'imageLists', 'train.txt') - test_list = osp.join(folder, 'imageLists', 'test.txt') - train_paths = [] - test_paths = [] - - with open(train_list) as f: - for filename in f: - basename = filename.strip() - imgpath = basename + '.jpg' - maskpath = basename + '.mat' - train_paths.append((imgpath, maskpath)) - - with open(test_list) as f: - for filename in f: - basename = filename.strip() - imgpath = basename + '.jpg' - maskpath = basename + '.mat' - test_paths.append((imgpath, maskpath)) - - return train_paths, test_paths - - -def parse_args(): - parser = argparse.ArgumentParser( - description=\ - 'Convert COCO Stuff 10k annotations to mmsegmentation format') # noqa - parser.add_argument('coco_path', help='coco stuff path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=16, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - coco_path = args.coco_path - nproc = args.nproc - - out_dir = args.out_dir or coco_path - out_img_dir = osp.join(out_dir, 'images') - out_mask_dir = osp.join(out_dir, 'annotations') - - mmcv.mkdir_or_exist(osp.join(out_img_dir, 'train2014')) - mmcv.mkdir_or_exist(osp.join(out_img_dir, 'test2014')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'train2014')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'test2014')) - - train_list, test_list = generate_coco_list(coco_path) - assert (len(train_list) + - len(test_list)) == COCO_LEN, 'Wrong length of list {} & {}'.format( - len(train_list), len(test_list)) - - if args.nproc > 1: - mmcv.track_parallel_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=True), - train_list, - nproc=nproc) - mmcv.track_parallel_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=False), - test_list, - nproc=nproc) - else: - mmcv.track_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=True), train_list) - mmcv.track_progress( - partial( - convert_to_trainID, - in_img_dir=osp.join(coco_path, 'images'), - in_ann_dir=osp.join(coco_path, 'annotations'), - out_img_dir=out_img_dir, - out_mask_dir=out_mask_dir, - is_train=False), test_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/coco_stuff164k.py b/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/coco_stuff164k.py deleted file mode 100644 index 6d8e2f2a3..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/coco_stuff164k.py +++ /dev/null @@ -1,264 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -import shutil -from functools import partial -from glob import glob - -import mmcv -import numpy as np -from PIL import Image - -COCO_LEN = 123287 - -clsID_to_trID = { - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 12: 11, - 13: 12, - 14: 13, - 15: 14, - 16: 15, - 17: 16, - 18: 17, - 19: 18, - 20: 19, - 21: 20, - 22: 21, - 23: 22, - 24: 23, - 26: 24, - 27: 25, - 30: 26, - 31: 27, - 32: 28, - 33: 29, - 34: 30, - 35: 31, - 36: 32, - 37: 33, - 38: 34, - 39: 35, - 40: 36, - 41: 37, - 42: 38, - 43: 39, - 45: 40, - 46: 41, - 47: 42, - 48: 43, - 49: 44, - 50: 45, - 51: 46, - 52: 47, - 53: 48, - 54: 49, - 55: 50, - 56: 51, - 57: 52, - 58: 53, - 59: 54, - 60: 55, - 61: 56, - 62: 57, - 63: 58, - 64: 59, - 66: 60, - 69: 61, - 71: 62, - 72: 63, - 73: 64, - 74: 65, - 75: 66, - 76: 67, - 77: 68, - 78: 69, - 79: 70, - 80: 71, - 81: 72, - 83: 73, - 84: 74, - 85: 75, - 86: 76, - 87: 77, - 88: 78, - 89: 79, - 91: 80, - 92: 81, - 93: 82, - 94: 83, - 95: 84, - 96: 85, - 97: 86, - 98: 87, - 99: 88, - 100: 89, - 101: 90, - 102: 91, - 103: 92, - 104: 93, - 105: 94, - 106: 95, - 107: 96, - 108: 97, - 109: 98, - 110: 99, - 111: 100, - 112: 101, - 113: 102, - 114: 103, - 115: 104, - 116: 105, - 117: 106, - 118: 107, - 119: 108, - 120: 109, - 121: 110, - 122: 111, - 123: 112, - 124: 113, - 125: 114, - 126: 115, - 127: 116, - 128: 117, - 129: 118, - 130: 119, - 131: 120, - 132: 121, - 133: 122, - 134: 123, - 135: 124, - 136: 125, - 137: 126, - 138: 127, - 139: 128, - 140: 129, - 141: 130, - 142: 131, - 143: 132, - 144: 133, - 145: 134, - 146: 135, - 147: 136, - 148: 137, - 149: 138, - 150: 139, - 151: 140, - 152: 141, - 153: 142, - 154: 143, - 155: 144, - 156: 145, - 157: 146, - 158: 147, - 159: 148, - 160: 149, - 161: 150, - 162: 151, - 163: 152, - 164: 153, - 165: 154, - 166: 155, - 167: 156, - 168: 157, - 169: 158, - 170: 159, - 171: 160, - 172: 161, - 173: 162, - 174: 163, - 175: 164, - 176: 165, - 177: 166, - 178: 167, - 179: 168, - 180: 169, - 181: 170, - 255: 255 -} - - -def convert_to_trainID(maskpath, out_mask_dir, is_train): - mask = np.array(Image.open(maskpath)) - mask_copy = mask.copy() - for clsID, trID in clsID_to_trID.items(): - mask_copy[mask == clsID] = trID - seg_filename = osp.join( - out_mask_dir, 'train2017', - osp.basename(maskpath).split('.')[0] + - '_labelTrainIds.png') if is_train else osp.join( - out_mask_dir, 'val2017', - osp.basename(maskpath).split('.')[0] + '_labelTrainIds.png') - Image.fromarray(mask_copy).save(seg_filename, 'PNG') - - -def parse_args(): - parser = argparse.ArgumentParser( - description=\ - 'Convert COCO Stuff 164k annotations to mmsegmentation format') # noqa - parser.add_argument('coco_path', help='coco stuff path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=16, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - coco_path = args.coco_path - nproc = args.nproc - - out_dir = args.out_dir or coco_path - out_img_dir = osp.join(out_dir, 'images') - out_mask_dir = osp.join(out_dir, 'annotations') - - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'train2017')) - mmcv.mkdir_or_exist(osp.join(out_mask_dir, 'val2017')) - - if out_dir != coco_path: - shutil.copytree(osp.join(coco_path, 'images'), out_img_dir) - - train_list = glob(osp.join(coco_path, 'annotations', 'train2017', '*.png')) - train_list = [file for file in train_list if '_labelTrainIds' not in file] - test_list = glob(osp.join(coco_path, 'annotations', 'val2017', '*.png')) - test_list = [file for file in test_list if '_labelTrainIds' not in file] - assert (len(train_list) + - len(test_list)) == COCO_LEN, 'Wrong length of list {} & {}'.format( - len(train_list), len(test_list)) - - if args.nproc > 1: - mmcv.track_parallel_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=True), - train_list, - nproc=nproc) - mmcv.track_parallel_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=False), - test_list, - nproc=nproc) - else: - mmcv.track_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=True), - train_list) - mmcv.track_progress( - partial( - convert_to_trainID, out_mask_dir=out_mask_dir, is_train=False), - test_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/pascal_context.py b/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/pascal_context.py deleted file mode 100644 index 03b79d518..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/pascal_context.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -from functools import partial - -import mmcv -import numpy as np -from detail import Detail -from PIL import Image - -_mapping = np.sort( - np.array([ - 0, 2, 259, 260, 415, 324, 9, 258, 144, 18, 19, 22, 23, 397, 25, 284, - 158, 159, 416, 33, 162, 420, 454, 295, 296, 427, 44, 45, 46, 308, 59, - 440, 445, 31, 232, 65, 354, 424, 68, 326, 72, 458, 34, 207, 80, 355, - 85, 347, 220, 349, 360, 98, 187, 104, 105, 366, 189, 368, 113, 115 - ])) -_key = np.array(range(len(_mapping))).astype('uint8') - - -def generate_labels(img_id, detail, out_dir): - - def _class_to_index(mask, _mapping, _key): - # assert the values - values = np.unique(mask) - for i in range(len(values)): - assert (values[i] in _mapping) - index = np.digitize(mask.ravel(), _mapping, right=True) - return _key[index].reshape(mask.shape) - - mask = Image.fromarray( - _class_to_index(detail.getMask(img_id), _mapping=_mapping, _key=_key)) - filename = img_id['file_name'] - mask.save(osp.join(out_dir, filename.replace('jpg', 'png'))) - return osp.splitext(osp.basename(filename))[0] - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert PASCAL VOC annotations to mmsegmentation format') - parser.add_argument('devkit_path', help='pascal voc devkit path') - parser.add_argument('json_path', help='annoation json filepath') - parser.add_argument('-o', '--out_dir', help='output path') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - devkit_path = args.devkit_path - if args.out_dir is None: - out_dir = osp.join(devkit_path, 'VOC2010', 'SegmentationClassContext') - else: - out_dir = args.out_dir - json_path = args.json_path - mmcv.mkdir_or_exist(out_dir) - img_dir = osp.join(devkit_path, 'VOC2010', 'JPEGImages') - - train_detail = Detail(json_path, img_dir, 'train') - train_ids = train_detail.getImgs() - - val_detail = Detail(json_path, img_dir, 'val') - val_ids = val_detail.getImgs() - - mmcv.mkdir_or_exist( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext')) - - train_list = mmcv.track_progress( - partial(generate_labels, detail=train_detail, out_dir=out_dir), - train_ids) - with open( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext', - 'train.txt'), 'w') as f: - f.writelines(line + '\n' for line in sorted(train_list)) - - val_list = mmcv.track_progress( - partial(generate_labels, detail=val_detail, out_dir=out_dir), val_ids) - with open( - osp.join(devkit_path, 'VOC2010/ImageSets/SegmentationContext', - 'val.txt'), 'w') as f: - f.writelines(line + '\n' for line in sorted(val_list)) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/voc_aug.py b/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/voc_aug.py deleted file mode 100644 index 1d42c2704..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/tools/convert_datasets/voc_aug.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os.path as osp -from functools import partial - -import mmcv -import numpy as np -from PIL import Image -from scipy.io import loadmat - -AUG_LEN = 10582 - - -def convert_mat(mat_file, in_dir, out_dir): - data = loadmat(osp.join(in_dir, mat_file)) - mask = data['GTcls'][0]['Segmentation'][0].astype(np.uint8) - seg_filename = osp.join(out_dir, mat_file.replace('.mat', '.png')) - Image.fromarray(mask).save(seg_filename, 'PNG') - - -def generate_aug_list(merged_list, excluded_list): - return list(set(merged_list) - set(excluded_list)) - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Convert PASCAL VOC annotations to mmsegmentation format') - parser.add_argument('devkit_path', help='pascal voc devkit path') - parser.add_argument('aug_path', help='pascal voc aug path') - parser.add_argument('-o', '--out_dir', help='output path') - parser.add_argument( - '--nproc', default=1, type=int, help='number of process') - args = parser.parse_args() - return args - - -def main(): - args = parse_args() - devkit_path = args.devkit_path - aug_path = args.aug_path - nproc = args.nproc - if args.out_dir is None: - out_dir = osp.join(devkit_path, 'VOC2012', 'SegmentationClassAug') - else: - out_dir = args.out_dir - mmcv.mkdir_or_exist(out_dir) - in_dir = osp.join(aug_path, 'dataset', 'cls') - - mmcv.track_parallel_progress( - partial(convert_mat, in_dir=in_dir, out_dir=out_dir), - list(mmcv.scandir(in_dir, suffix='.mat')), - nproc=nproc) - - full_aug_list = [] - with open(osp.join(aug_path, 'dataset', 'train.txt')) as f: - full_aug_list += [line.strip() for line in f] - with open(osp.join(aug_path, 'dataset', 'val.txt')) as f: - full_aug_list += [line.strip() for line in f] - - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'train.txt')) as f: - ori_train_list = [line.strip() for line in f] - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'val.txt')) as f: - val_list = [line.strip() for line in f] - - aug_train_list = generate_aug_list(ori_train_list + full_aug_list, - val_list) - assert len(aug_train_list) == AUG_LEN, 'len(aug_train_list) != {}'.format( - AUG_LEN) - - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', - 'trainaug.txt'), 'w') as f: - f.writelines(line + '\n' for line in aug_train_list) - - aug_list = generate_aug_list(full_aug_list, ori_train_list + val_list) - assert len(aug_list) == AUG_LEN - len( - ori_train_list), 'len(aug_list) != {}'.format(AUG_LEN - - len(ori_train_list)) - with open( - osp.join(devkit_path, 'VOC2012/ImageSets/Segmentation', 'aug.txt'), - 'w') as f: - f.writelines(line + '\n' for line in aug_list) - - print('Done!') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/stdc/pytorch/tools/get_flops.py b/cv/semantic_segmentation/stdc/pytorch/tools/get_flops.py deleted file mode 100644 index e30c36fdf..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/tools/get_flops.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse - -from mmcv import Config -from mmcv.cnn import get_model_complexity_info - -from mmseg.models import build_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Get the FLOPs of a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument( - '--shape', - type=int, - nargs='+', - default=[2048, 1024], - help='input image size') - args = parser.parse_args() - return args - - -def main(): - - args = parse_args() - - if len(args.shape) == 1: - input_shape = (3, args.shape[0], args.shape[0]) - elif len(args.shape) == 2: - input_shape = (3, ) + tuple(args.shape) - else: - raise ValueError('invalid input shape') - - cfg = Config.fromfile(args.config) - cfg.model.pretrained = None - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')).cuda() - model.eval() - - if hasattr(model, 'forward_dummy'): - model.forward = model.forward_dummy - else: - raise NotImplementedError( - 'FLOPs counter is currently not currently supported with {}'. - format(model.__class__.__name__)) - - flops, params = get_model_complexity_info(model, input_shape) - split_line = '=' * 30 - print('{0}\nInput shape: {1}\nFlops: {2}\nParams: {3}\n{0}'.format( - split_line, input_shape, flops, params)) - print('!!!Please be cautious if you use the results in papers. ' - 'You may need to check if all ops are supported and verify that the ' - 'flops computation is correct.') - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/stdc/pytorch/tools/print_config.py b/cv/semantic_segmentation/stdc/pytorch/tools/print_config.py deleted file mode 100644 index 3f9c08dd9..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/tools/print_config.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import warnings - -from mmcv import Config, DictAction - -from mmseg.apis import init_segmentor - - -def parse_args(): - parser = argparse.ArgumentParser(description='Print the whole config') - parser.add_argument('config', help='config file path') - parser.add_argument( - '--graph', action='store_true', help='print the models graph') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - args = parser.parse_args() - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options, ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - print(f'Config:\n{cfg.pretty_text}') - # dump config - cfg.dump('example.py') - # dump models graph - if args.graph: - model = init_segmentor(args.config, device='cpu') - print(f'Model graph:\n{str(model)}') - with open('example-graph.txt', 'w') as f: - f.writelines(str(model)) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/stdc/pytorch/tools/slurm_test.sh b/cv/semantic_segmentation/stdc/pytorch/tools/slurm_test.sh deleted file mode 100755 index 4e6f7bf4e..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/tools/slurm_test.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -set -x - -PARTITION=$1 -JOB_NAME=$2 -CONFIG=$3 -CHECKPOINT=$4 -GPUS=${GPUS:-4} -GPUS_PER_NODE=${GPUS_PER_NODE:-4} -CPUS_PER_TASK=${CPUS_PER_TASK:-5} -PY_ARGS=${@:5} -SRUN_ARGS=${SRUN_ARGS:-""} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -srun -p ${PARTITION} \ - --job-name=${JOB_NAME} \ - --gres=gpu:${GPUS_PER_NODE} \ - --ntasks=${GPUS} \ - --ntasks-per-node=${GPUS_PER_NODE} \ - --cpus-per-task=${CPUS_PER_TASK} \ - --kill-on-bad-exit=1 \ - ${SRUN_ARGS} \ - python -u tools/test.py ${CONFIG} ${CHECKPOINT} --launcher="slurm" ${PY_ARGS} diff --git a/cv/semantic_segmentation/stdc/pytorch/tools/slurm_train.sh b/cv/semantic_segmentation/stdc/pytorch/tools/slurm_train.sh deleted file mode 100755 index ab232105f..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/tools/slurm_train.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -set -x - -PARTITION=$1 -JOB_NAME=$2 -CONFIG=$3 -GPUS=${GPUS:-4} -GPUS_PER_NODE=${GPUS_PER_NODE:-4} -CPUS_PER_TASK=${CPUS_PER_TASK:-5} -SRUN_ARGS=${SRUN_ARGS:-""} -PY_ARGS=${@:4} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -srun -p ${PARTITION} \ - --job-name=${JOB_NAME} \ - --gres=gpu:${GPUS_PER_NODE} \ - --ntasks=${GPUS} \ - --ntasks-per-node=${GPUS_PER_NODE} \ - --cpus-per-task=${CPUS_PER_TASK} \ - --kill-on-bad-exit=1 \ - ${SRUN_ARGS} \ - python -u tools/train.py ${CONFIG} --launcher="slurm" ${PY_ARGS} diff --git a/cv/semantic_segmentation/stdc/pytorch/tools/test.py b/cv/semantic_segmentation/stdc/pytorch/tools/test.py deleted file mode 100644 index 12892ec9b..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/tools/test.py +++ /dev/null @@ -1,319 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import os -import os.path as osp -import shutil -import time -import warnings - -import mmcv -import torch -from mmcv.cnn.utils import revert_sync_batchnorm -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (get_dist_info, init_dist, load_checkpoint, - wrap_fp16_model) -from mmcv.utils import DictAction - -from mmseg import digit_version -from mmseg.apis import multi_gpu_test, single_gpu_test -from mmseg.datasets import build_dataloader, build_dataset -from mmseg.models import build_segmentor -from mmseg.utils import setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser( - description='mmseg test (and eval) a model') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument( - '--work-dir', - help=('if specified, the evaluation metric results will be dumped' - 'into the directory as json')) - parser.add_argument( - '--aug-test', action='store_true', help='Use Flip and Multi scale aug') - parser.add_argument('--out', help='output result file in pickle format') - parser.add_argument( - '--format-only', - action='store_true', - help='Format the output results without perform evaluation. It is' - 'useful when you want to format the result to a specific format and ' - 'submit it to the test server') - parser.add_argument( - '--eval', - type=str, - nargs='+', - help='evaluation metrics, which depends on the dataset, e.g., "mIoU"' - ' for generic datasets, and "cityscapes" for Cityscapes') - parser.add_argument('--show', action='store_true', help='show results') - parser.add_argument( - '--show-dir', help='directory where painted images will be saved') - parser.add_argument( - '--gpu-collect', - action='store_true', - help='whether to use gpu to collect results.') - parser.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed testing)') - parser.add_argument( - '--tmpdir', - help='tmp directory used for collecting results from multiple ' - 'workers, available when gpu_collect is not specified') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--eval-options', - nargs='+', - action=DictAction, - help='custom options for evaluation') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument( - '--opacity', - type=float, - default=0.5, - help='Opacity of painted segmentation map. In (0, 1] range.') - parser.add_argument('--local_rank', type=int, default=0) - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - assert args.out or args.eval or args.format_only or args.show \ - or args.show_dir, \ - ('Please specify at least one operation (save/eval/format/show the ' - 'results / save the results) with the argument "--out", "--eval"' - ', "--format-only", "--show" or "--show-dir"') - - if args.eval and args.format_only: - raise ValueError('--eval and --format_only cannot be both specified') - - if args.out is not None and not args.out.endswith(('.pkl', '.pickle')): - raise ValueError('The output file must be a pkl file.') - - cfg = mmcv.Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - if args.aug_test: - # hard code index - cfg.data.test.pipeline[1].img_ratios = [ - 0.5, 0.75, 1.0, 1.25, 1.5, 1.75 - ] - cfg.data.test.pipeline[1].flip = True - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - if args.gpu_id is not None: - cfg.gpu_ids = [args.gpu_id] - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - cfg.gpu_ids = [args.gpu_id] - distributed = False - if len(cfg.gpu_ids) > 1: - warnings.warn(f'The gpu-ids is reset from {cfg.gpu_ids} to ' - f'{cfg.gpu_ids[0:1]} to avoid potential error in ' - 'non-distribute testing time.') - cfg.gpu_ids = cfg.gpu_ids[0:1] - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - - rank, _ = get_dist_info() - # allows not to create - if args.work_dir is not None and rank == 0: - mmcv.mkdir_or_exist(osp.abspath(args.work_dir)) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.aug_test: - json_file = osp.join(args.work_dir, - f'eval_multi_scale_{timestamp}.json') - else: - json_file = osp.join(args.work_dir, - f'eval_single_scale_{timestamp}.json') - elif rank == 0: - work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - mmcv.mkdir_or_exist(osp.abspath(work_dir)) - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - if args.aug_test: - json_file = osp.join(work_dir, - f'eval_multi_scale_{timestamp}.json') - else: - json_file = osp.join(work_dir, - f'eval_single_scale_{timestamp}.json') - - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - # The default loader config - loader_cfg = dict( - # cfg.gpus will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - shuffle=False) - # The overall dataloader settings - loader_cfg.update({ - k: v - for k, v in cfg.data.items() if k not in [ - 'train', 'val', 'test', 'train_dataloader', 'val_dataloader', - 'test_dataloader' - ] - }) - test_loader_cfg = { - **loader_cfg, - 'samples_per_gpu': 1, - 'shuffle': False, # Not shuffle by default - **cfg.data.get('test_dataloader', {}) - } - # build the dataloader - data_loader = build_dataloader(dataset, **test_loader_cfg) - - # build the model and load checkpoint - cfg.model.train_cfg = None - model = build_segmentor(cfg.model, test_cfg=cfg.get('test_cfg')) - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) - checkpoint = load_checkpoint(model, args.checkpoint, map_location='cpu') - if 'CLASSES' in checkpoint.get('meta', {}): - model.CLASSES = checkpoint['meta']['CLASSES'] - else: - print('"CLASSES" not found in meta, use dataset.CLASSES instead') - model.CLASSES = dataset.CLASSES - if 'PALETTE' in checkpoint.get('meta', {}): - model.PALETTE = checkpoint['meta']['PALETTE'] - else: - print('"PALETTE" not found in meta, use dataset.PALETTE instead') - model.PALETTE = dataset.PALETTE - - # clean gpu memory when starting a new evaluation. - torch.cuda.empty_cache() - eval_kwargs = {} if args.eval_options is None else args.eval_options - - # Deprecated - efficient_test = eval_kwargs.get('efficient_test', False) - if efficient_test: - warnings.warn( - '``efficient_test=True`` does not have effect in tools/test.py, ' - 'the evaluation and format results are CPU memory efficient by ' - 'default') - - eval_on_format_results = ( - args.eval is not None and 'cityscapes' in args.eval) - if eval_on_format_results: - assert len(args.eval) == 1, 'eval on format results is not ' \ - 'applicable for metrics other than ' \ - 'cityscapes' - if args.format_only or eval_on_format_results: - if 'imgfile_prefix' in eval_kwargs: - tmpdir = eval_kwargs['imgfile_prefix'] - else: - tmpdir = '.format_cityscapes' - eval_kwargs.setdefault('imgfile_prefix', tmpdir) - mmcv.mkdir_or_exist(tmpdir) - else: - tmpdir = None - - if not distributed: - warnings.warn( - 'SyncBN is only supported with DDP. To be compatible with DP, ' - 'we convert SyncBN to BN. Please use dist_train.sh which can ' - 'avoid this error.') - if not torch.cuda.is_available(): - assert digit_version(mmcv.__version__) >= digit_version('1.4.4'), \ - 'Please use MMCV >= 1.4.4 for CPU training!' - model = revert_sync_batchnorm(model) - model = MMDataParallel(model, device_ids=cfg.gpu_ids) - results = single_gpu_test( - model, - data_loader, - args.show, - args.show_dir, - False, - args.opacity, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - else: - model = MMDistributedDataParallel( - model.cuda(), - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False) - results = multi_gpu_test( - model, - data_loader, - args.tmpdir, - args.gpu_collect, - False, - pre_eval=args.eval is not None and not eval_on_format_results, - format_only=args.format_only or eval_on_format_results, - format_args=eval_kwargs) - - rank, _ = get_dist_info() - if rank == 0: - if args.out: - warnings.warn( - 'The behavior of ``args.out`` has been changed since MMSeg ' - 'v0.16, the pickled outputs could be seg map as type of ' - 'np.array, pre-eval results or file paths for ' - '``dataset.format_results()``.') - print(f'\nwriting results to {args.out}') - mmcv.dump(results, args.out) - if args.eval: - eval_kwargs.update(metric=args.eval) - metric = dataset.evaluate(results, **eval_kwargs) - metric_dict = dict(config=args.config, metric=metric) - mmcv.dump(metric_dict, json_file, indent=4) - if tmpdir is not None and eval_on_format_results: - # remove tmp dir when cityscapes evaluation - shutil.rmtree(tmpdir) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/stdc/pytorch/train.py b/cv/semantic_segmentation/stdc/pytorch/train.py deleted file mode 100644 index e198dd60f..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/train.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import copy -import os -import os.path as osp -import time -import warnings - -import mmcv -import torch -import torch.distributed as dist -from mmcv.cnn.utils import revert_sync_batchnorm -from mmcv.runner import get_dist_info, init_dist -from mmcv.utils import Config, DictAction, get_git_hash - -from mmseg import __version__ -from mmseg.apis import init_random_seed, set_random_seed, train_segmentor -from mmseg.datasets import build_dataset -from mmseg.models import build_segmentor -from mmseg.utils import collect_env, get_root_logger, setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train a segmentor') - parser.add_argument('config', help='train config file path') - parser.add_argument('--work-dir', help='the dir to save logs and models') - parser.add_argument( - '--load-from', help='the checkpoint file to load weights from') - parser.add_argument( - '--resume-from', help='the checkpoint file to resume from') - parser.add_argument( - '--no-validate', - action='store_true', - help='whether not to evaluate the checkpoint during training') - group_gpus = parser.add_mutually_exclusive_group() - group_gpus.add_argument( - '--gpus', - type=int, - help='(Deprecated, please use --gpu-id) number of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-ids', - type=int, - nargs='+', - help='(Deprecated, please use --gpu-id) ids of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed training)') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--diff_seed', - action='store_true', - help='Whether or not set different seeds for different ranks') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help="--options is deprecated in favor of --cfg_options' and it will " - 'not be supported in version v0.22.0. Override some settings in the ' - 'used config, the key-value pair in xxx=yyy format will be merged ' - 'into config file. If the value to be overwritten is a list, it ' - 'should be like key="[a,b]" or key=a,b It also allows nested ' - 'list/tuple values, e.g. key="[(a,b),(c,d)]" Note that the quotation ' - 'marks are necessary and that no white space is allowed.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - parser.add_argument('--dist_backend', type=str, default=None) - parser.add_argument( - '--auto-resume', - action='store_true', - help='resume from the latest checkpoint automatically.') - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options. ' - '--options will not be supported in version v0.22.0.') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - - # work_dir is determined in this priority: CLI > segment in file > filename - if args.work_dir is not None: - # update configs according to CLI args if args.work_dir is not None - cfg.work_dir = args.work_dir - elif cfg.get('work_dir', None) is None: - # use config filename as default work_dir if cfg.work_dir is None - cfg.work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - if args.load_from is not None: - cfg.load_from = args.load_from - if args.resume_from is not None: - cfg.resume_from = args.resume_from - if args.gpus is not None: - cfg.gpu_ids = range(1) - warnings.warn('`--gpus` is deprecated because we only support ' - 'single GPU mode in non-distributed training. ' - 'Use `gpus=1` now.') - if args.gpu_ids is not None: - cfg.gpu_ids = args.gpu_ids[0:1] - warnings.warn('`--gpu-ids` is deprecated, please use `--gpu-id`. ' - 'Because we only support single GPU mode in ' - 'non-distributed training. Use the first GPU ' - 'in `gpu_ids` now.') - if args.gpus is None and args.gpu_ids is None: - cfg.gpu_ids = [args.gpu_id] - - cfg.auto_resume = args.auto_resume - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - if args.dist_backend is not None: - cfg.dist_params.backend = args.dist_backend - init_dist(args.launcher, **cfg.dist_params) - # gpu_ids is used to calculate iter when resuming checkpoint - _, world_size = get_dist_info() - cfg.gpu_ids = range(world_size) - - # create work_dir - mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) - # dump config - cfg.dump(osp.join(cfg.work_dir, osp.basename(args.config))) - # init the logger before other steps - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - log_file = osp.join(cfg.work_dir, f'{timestamp}.log') - logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) - - # set multi-process settings - setup_multi_processes(cfg) - - # init the meta dict to record some important information such as - # environment info and seed, which will be logged - meta = dict() - # log env info - env_info_dict = collect_env() - env_info = '\n'.join([f'{k}: {v}' for k, v in env_info_dict.items()]) - dash_line = '-' * 60 + '\n' - logger.info('Environment info:\n' + dash_line + env_info + '\n' + - dash_line) - meta['env_info'] = env_info - - # log some basic info - logger.info(f'Distributed training: {distributed}') - logger.info(f'Config:\n{cfg.pretty_text}') - - # set random seeds - seed = init_random_seed(args.seed) - seed = seed + dist.get_rank() if args.diff_seed else seed - logger.info(f'Set random seed to {seed}, ' - f'deterministic: {args.deterministic}') - set_random_seed(seed, deterministic=args.deterministic) - cfg.seed = seed - meta['seed'] = seed - meta['exp_name'] = osp.basename(args.config) - - model = build_segmentor( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')) - model.init_weights() - - # SyncBN is not support for DP - if not distributed: - warnings.warn( - 'SyncBN is only supported with DDP. To be compatible with DP, ' - 'we convert SyncBN to BN. Please use dist_train.sh which can ' - 'avoid this error.') - model = revert_sync_batchnorm(model) - - logger.info(model) - - datasets = [build_dataset(cfg.data.train)] - if len(cfg.workflow) == 2: - val_dataset = copy.deepcopy(cfg.data.val) - val_dataset.pipeline = cfg.data.train.pipeline - datasets.append(build_dataset(val_dataset)) - if cfg.checkpoint_config is not None: - # save mmseg version, config file content and class names in - # checkpoints as meta data - cfg.checkpoint_config.meta = dict( - mmseg_version=f'{__version__}+{get_git_hash()[:7]}', - config=cfg.pretty_text, - CLASSES=datasets[0].CLASSES, - PALETTE=datasets[0].PALETTE) - # add an attribute for visualization convenience - model.CLASSES = datasets[0].CLASSES - # passing checkpoint meta for saving best checkpoint - meta.update(cfg.checkpoint_config.meta) - train_segmentor( - model, - datasets, - cfg, - distributed=distributed, - validate=(not args.no_validate), - timestamp=timestamp, - meta=meta) - - -if __name__ == '__main__': - main() diff --git a/cv/semantic_segmentation/stdc/pytorch/train_dist.sh b/cv/semantic_segmentation/stdc/pytorch/train_dist.sh deleted file mode 100755 index d09675129..000000000 --- a/cv/semantic_segmentation/stdc/pytorch/train_dist.sh +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - -CONFIG=$1 -GPUS=$2 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/train.py \ - $CONFIG \ - --launcher pytorch ${@:3} -- Gitee From bc9f54968d323200c5811b9c9aba0a197090f112 Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Thu, 6 Mar 2025 09:45:46 +0800 Subject: [PATCH 09/15] update basicvsr++ --- .../basicvsr++/pytorch/.gitignore | 139 -- .../basicvsr++/pytorch/LICENSE | 203 --- .../basicvsr++/pytorch/README.md | 25 +- .../basicvsr++/pytorch/build_env.sh | 14 - .../basicvsr_plusplus_c64n7_8x1_600k_reds4.py | 155 -- .../basicvsr++/pytorch/dist_train.sh | 21 - .../basicvsr++/pytorch/docker/Dockerfile | 24 - .../basicvsr++/pytorch/docker/README.md | 19 - .../basicvsr++/pytorch/mmcv/__init__.py | 13 - .../pytorch/mmcv/arraymisc/__init__.py | 4 - .../pytorch/mmcv/arraymisc/quantization.py | 55 - .../basicvsr++/pytorch/mmcv/cnn/__init__.py | 41 - .../basicvsr++/pytorch/mmcv/cnn/alexnet.py | 61 - .../pytorch/mmcv/cnn/bricks/__init__.py | 35 - .../pytorch/mmcv/cnn/bricks/activation.py | 92 -- .../pytorch/mmcv/cnn/bricks/context_block.py | 125 -- .../pytorch/mmcv/cnn/bricks/conv.py | 44 - .../cnn/bricks/conv2d_adaptive_padding.py | 62 - .../pytorch/mmcv/cnn/bricks/conv_module.py | 206 --- .../pytorch/mmcv/cnn/bricks/conv_ws.py | 148 -- .../bricks/depthwise_separable_conv_module.py | 96 -- .../pytorch/mmcv/cnn/bricks/drop.py | 65 - .../mmcv/cnn/bricks/generalized_attention.py | 412 ----- .../pytorch/mmcv/cnn/bricks/hsigmoid.py | 34 - .../pytorch/mmcv/cnn/bricks/hswish.py | 29 - .../pytorch/mmcv/cnn/bricks/non_local.py | 306 ---- .../pytorch/mmcv/cnn/bricks/norm.py | 144 -- .../pytorch/mmcv/cnn/bricks/padding.py | 36 - .../pytorch/mmcv/cnn/bricks/plugin.py | 88 -- .../pytorch/mmcv/cnn/bricks/registry.py | 16 - .../pytorch/mmcv/cnn/bricks/scale.py | 21 - .../pytorch/mmcv/cnn/bricks/swish.py | 25 - .../pytorch/mmcv/cnn/bricks/transformer.py | 595 -------- .../pytorch/mmcv/cnn/bricks/upsample.py | 84 -- .../pytorch/mmcv/cnn/bricks/wrappers.py | 180 --- .../basicvsr++/pytorch/mmcv/cnn/builder.py | 30 - .../basicvsr++/pytorch/mmcv/cnn/resnet.py | 316 ---- .../pytorch/mmcv/cnn/utils/__init__.py | 19 - .../pytorch/mmcv/cnn/utils/flops_counter.py | 599 -------- .../pytorch/mmcv/cnn/utils/fuse_conv_bn.py | 59 - .../pytorch/mmcv/cnn/utils/sync_bn.py | 59 - .../pytorch/mmcv/cnn/utils/weight_init.py | 684 --------- .../basicvsr++/pytorch/mmcv/cnn/vgg.py | 175 --- .../pytorch/mmcv/engine/__init__.py | 8 - .../basicvsr++/pytorch/mmcv/engine/test.py | 202 --- .../pytorch/mmcv/fileio/__init__.py | 11 - .../pytorch/mmcv/fileio/file_client.py | 1148 -------------- .../pytorch/mmcv/fileio/handlers/__init__.py | 7 - .../pytorch/mmcv/fileio/handlers/base.py | 30 - .../mmcv/fileio/handlers/json_handler.py | 36 - .../mmcv/fileio/handlers/pickle_handler.py | 28 - .../mmcv/fileio/handlers/yaml_handler.py | 24 - .../basicvsr++/pytorch/mmcv/fileio/io.py | 151 -- .../basicvsr++/pytorch/mmcv/fileio/parse.py | 97 -- .../basicvsr++/pytorch/mmcv/image/__init__.py | 28 - .../pytorch/mmcv/image/colorspace.py | 306 ---- .../pytorch/mmcv/image/geometric.py | 728 --------- .../basicvsr++/pytorch/mmcv/image/io.py | 258 ---- .../basicvsr++/pytorch/mmcv/image/misc.py | 44 - .../pytorch/mmcv/image/photometric.py | 428 ------ .../basicvsr++/pytorch/mmcv/ops/__init__.py | 15 - .../csrc/common/cuda/common_cuda_helper.hpp | 112 -- .../modulated_deform_conv_cuda_kernel.cuh | 400 ----- .../csrc/common/cuda/sync_bn_cuda_kernel.cuh | 332 ---- .../ops/csrc/common/pytorch_cpp_helper.hpp | 24 - .../ops/csrc/common/pytorch_cuda_helper.hpp | 19 - .../cuda/modulated_deform_conv_cuda.cu | 97 -- .../ops/csrc/pytorch/cuda/sync_bn_cuda.cu | 111 -- .../pytorch/mmcv/ops/csrc/pytorch/info.cpp | 56 - .../csrc/pytorch/modulated_deform_conv.cpp | 338 ----- .../pytorch/modulated_deform_conv_cpu.cpp | 403 ----- .../pytorch/mmcv/ops/csrc/pytorch/pybind.cpp | 82 - .../pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp | 159 -- .../pytorch/mmcv/ops/deprecated_wrappers.py | 43 - .../basicvsr++/pytorch/mmcv/ops/info.py | 36 - .../pytorch/mmcv/ops/modulated_deform_conv.py | 282 ---- .../basicvsr++/pytorch/mmcv/ops/sync_bn.py | 279 ---- .../pytorch/mmcv/parallel/__init__.py | 13 - .../pytorch/mmcv/parallel/_functions.py | 79 - .../pytorch/mmcv/parallel/collate.py | 84 -- .../pytorch/mmcv/parallel/data_container.py | 89 -- .../pytorch/mmcv/parallel/data_parallel.py | 89 -- .../pytorch/mmcv/parallel/distributed.py | 112 -- .../mmcv/parallel/distributed_deprecated.py | 70 - .../pytorch/mmcv/parallel/registry.py | 8 - .../pytorch/mmcv/parallel/scatter_gather.py | 59 - .../basicvsr++/pytorch/mmcv/parallel/utils.py | 20 - .../pytorch/mmcv/runner/__init__.py | 47 - .../pytorch/mmcv/runner/base_module.py | 195 --- .../pytorch/mmcv/runner/base_runner.py | 542 ------- .../basicvsr++/pytorch/mmcv/runner/builder.py | 24 - .../pytorch/mmcv/runner/checkpoint.py | 707 --------- .../mmcv/runner/default_constructor.py | 44 - .../pytorch/mmcv/runner/dist_utils.py | 164 -- .../pytorch/mmcv/runner/epoch_based_runner.py | 187 --- .../pytorch/mmcv/runner/fp16_utils.py | 410 ----- .../pytorch/mmcv/runner/hooks/__init__.py | 29 - .../pytorch/mmcv/runner/hooks/checkpoint.py | 167 --- .../pytorch/mmcv/runner/hooks/closure.py | 11 - .../pytorch/mmcv/runner/hooks/ema.py | 89 -- .../pytorch/mmcv/runner/hooks/evaluation.py | 509 ------- .../pytorch/mmcv/runner/hooks/hook.py | 92 -- .../pytorch/mmcv/runner/hooks/iter_timer.py | 30 - .../mmcv/runner/hooks/logger/__init__.py | 15 - .../pytorch/mmcv/runner/hooks/logger/base.py | 166 -- .../mmcv/runner/hooks/logger/dvclive.py | 58 - .../mmcv/runner/hooks/logger/mlflow.py | 78 - .../mmcv/runner/hooks/logger/neptune.py | 82 - .../pytorch/mmcv/runner/hooks/logger/pavi.py | 117 -- .../mmcv/runner/hooks/logger/tensorboard.py | 57 - .../pytorch/mmcv/runner/hooks/logger/text.py | 256 ---- .../pytorch/mmcv/runner/hooks/logger/wandb.py | 56 - .../pytorch/mmcv/runner/hooks/lr_updater.py | 670 --------- .../pytorch/mmcv/runner/hooks/memory.py | 25 - .../mmcv/runner/hooks/momentum_updater.py | 493 ------ .../pytorch/mmcv/runner/hooks/optimizer.py | 508 ------- .../pytorch/mmcv/runner/hooks/profiler.py | 180 --- .../pytorch/mmcv/runner/hooks/sampler_seed.py | 20 - .../pytorch/mmcv/runner/hooks/sync_buffer.py | 22 - .../pytorch/mmcv/runner/iter_based_runner.py | 273 ---- .../pytorch/mmcv/runner/log_buffer.py | 41 - .../pytorch/mmcv/runner/optimizer/__init__.py | 9 - .../pytorch/mmcv/runner/optimizer/builder.py | 44 - .../runner/optimizer/default_constructor.py | 249 --- .../pytorch/mmcv/runner/priority.py | 60 - .../basicvsr++/pytorch/mmcv/runner/utils.py | 93 -- .../basicvsr++/pytorch/mmcv/utils/__init__.py | 69 - .../basicvsr++/pytorch/mmcv/utils/config.py | 688 --------- .../basicvsr++/pytorch/mmcv/utils/env.py | 95 -- .../pytorch/mmcv/utils/ext_loader.py | 71 - .../basicvsr++/pytorch/mmcv/utils/logging.py | 110 -- .../basicvsr++/pytorch/mmcv/utils/misc.py | 377 ----- .../pytorch/mmcv/utils/parrots_jit.py | 41 - .../pytorch/mmcv/utils/parrots_wrapper.py | 107 -- .../basicvsr++/pytorch/mmcv/utils/path.py | 101 -- .../pytorch/mmcv/utils/progressbar.py | 208 --- .../basicvsr++/pytorch/mmcv/utils/registry.py | 315 ---- .../basicvsr++/pytorch/mmcv/utils/testing.py | 140 -- .../basicvsr++/pytorch/mmcv/utils/timer.py | 118 -- .../basicvsr++/pytorch/mmcv/utils/trace.py | 23 - .../pytorch/mmcv/utils/version_utils.py | 90 -- .../basicvsr++/pytorch/mmcv/version.py | 35 - .../basicvsr++/pytorch/mmedit/__init__.py | 34 - .../pytorch/mmedit/apis/__init__.py | 18 - .../mmedit/apis/generation_inference.py | 63 - .../mmedit/apis/inpainting_inference.py | 53 - .../pytorch/mmedit/apis/matting_inference.py | 78 - .../mmedit/apis/restoration_face_inference.py | 90 -- .../mmedit/apis/restoration_inference.py | 48 - .../apis/restoration_video_inference.py | 129 -- .../basicvsr++/pytorch/mmedit/apis/test.py | 234 --- .../basicvsr++/pytorch/mmedit/apis/train.py | 361 ----- .../apis/video_interpolation_inference.py | 204 --- .../pytorch/mmedit/core/__init__.py | 13 - .../mmedit/core/distributed_wrapper.py | 139 -- .../mmedit/core/evaluation/__init__.py | 10 - .../mmedit/core/evaluation/eval_hooks.py | 114 -- .../mmedit/core/evaluation/metric_utils.py | 81 - .../pytorch/mmedit/core/evaluation/metrics.py | 572 ------- .../core/evaluation/niqe_pris_params.npz | Bin 11850 -> 0 bytes .../pytorch/mmedit/core/export/__init__.py | 4 - .../pytorch/mmedit/core/export/wrappers.py | 134 -- .../pytorch/mmedit/core/hooks/__init__.py | 5 - .../pytorch/mmedit/core/hooks/ema.py | 113 -- .../mmedit/core/hooks/visualization.py | 84 -- .../basicvsr++/pytorch/mmedit/core/mask.py | 316 ---- .../basicvsr++/pytorch/mmedit/core/misc.py | 74 - .../pytorch/mmedit/core/optimizer/__init__.py | 4 - .../pytorch/mmedit/core/optimizer/builder.py | 58 - .../pytorch/mmedit/core/scheduler/__init__.py | 4 - .../mmedit/core/scheduler/lr_updater.py | 304 ---- .../pytorch/mmedit/core/utils/__init__.py | 4 - .../pytorch/mmedit/core/utils/dist_utils.py | 35 - .../pytorch/mmedit/datasets/__init__.py | 11 - .../pytorch/mmedit/datasets/base_dataset.py | 78 - .../mmedit/datasets/base_sr_dataset.py | 87 -- .../pytorch/mmedit/datasets/builder.py | 181 --- .../mmedit/datasets/dataset_wrappers.py | 39 - .../mmedit/datasets/pipelines/__init__.py | 22 - .../mmedit/datasets/pipelines/augmentation.py | 1332 ----------------- .../mmedit/datasets/pipelines/compose.py | 53 - .../pytorch/mmedit/datasets/pipelines/crop.py | 749 --------- .../mmedit/datasets/pipelines/formating.py | 263 ---- .../mmedit/datasets/pipelines/loading.py | 562 ------- .../datasets/pipelines/matlab_like_resize.py | 275 ---- .../datasets/pipelines/normalization.py | 103 -- .../mmedit/datasets/pipelines/utils.py | 154 -- .../pytorch/mmedit/datasets/registry.py | 5 - .../mmedit/datasets/samplers/__init__.py | 4 - .../datasets/samplers/distributed_sampler.py | 71 - .../datasets/sr_reds_multiple_gt_dataset.py | 85 -- .../pytorch/mmedit/models/__init__.py | 13 - .../mmedit/models/backbones/__init__.py | 8 - .../models/backbones/sr_backbones/__init__.py | 6 - .../backbones/sr_backbones/basicvsr_net.py | 420 ------ .../backbones/sr_backbones/basicvsr_pp.py | 434 ------ .../basicvsr++/pytorch/mmedit/models/base.py | 105 -- .../pytorch/mmedit/models/builder.py | 60 - .../pytorch/mmedit/models/common/__init__.py | 32 - .../pytorch/mmedit/models/common/aspp.py | 125 -- .../models/common/contextual_attention.py | 379 ----- .../pytorch/mmedit/models/common/conv.py | 6 - .../mmedit/models/common/downsample.py | 22 - .../pytorch/mmedit/models/common/ensemble.py | 105 -- .../pytorch/mmedit/models/common/flow_warp.py | 50 - .../mmedit/models/common/gated_conv_module.py | 72 - .../mmedit/models/common/gca_module.py | 358 ----- .../models/common/generation_model_utils.py | 301 ---- .../mmedit/models/common/img_normalize.py | 32 - .../mmedit/models/common/linear_module.py | 89 -- .../mmedit/models/common/mask_conv_module.py | 88 -- .../mmedit/models/common/model_utils.py | 136 -- .../mmedit/models/common/partial_conv.py | 102 -- .../models/common/separable_conv_module.py | 97 -- .../mmedit/models/common/sr_backbone_utils.py | 97 -- .../pytorch/mmedit/models/common/upsample.py | 51 - .../pytorch/mmedit/models/losses/__init__.py | 7 - .../mmedit/models/losses/pixelwise_loss.py | 221 --- .../pytorch/mmedit/models/losses/utils.py | 115 -- .../pytorch/mmedit/models/registry.py | 8 - .../mmedit/models/restorers/__init__.py | 6 - .../mmedit/models/restorers/basic_restorer.py | 210 --- .../mmedit/models/restorers/basicvsr.py | 224 --- .../pytorch/mmedit/utils/__init__.py | 6 - .../basicvsr++/pytorch/mmedit/utils/cli.py | 18 - .../pytorch/mmedit/utils/collect_env.py | 18 - .../basicvsr++/pytorch/mmedit/utils/logger.py | 27 - .../pytorch/mmedit/utils/setup_env.py | 47 - .../basicvsr++/pytorch/mmedit/version.py | 18 - .../basicvsr++/pytorch/requirements.txt | 3 - .../basicvsr++/pytorch/setup.py | 354 ----- .../basicvsr++/pytorch/train.py | 172 --- 232 files changed, 16 insertions(+), 33990 deletions(-) delete mode 100755 cv/super_resolution/basicvsr++/pytorch/.gitignore delete mode 100755 cv/super_resolution/basicvsr++/pytorch/LICENSE delete mode 100755 cv/super_resolution/basicvsr++/pytorch/build_env.sh delete mode 100755 cv/super_resolution/basicvsr++/pytorch/configs/basicvsr_plusplus/basicvsr_plusplus_c64n7_8x1_600k_reds4.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/dist_train.sh delete mode 100755 cv/super_resolution/basicvsr++/pytorch/docker/Dockerfile delete mode 100755 cv/super_resolution/basicvsr++/pytorch/docker/README.md delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/arraymisc/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/arraymisc/quantization.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/alexnet.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/activation.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/context_block.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv_module.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv_ws.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/drop.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/generalized_attention.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/hsigmoid.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/hswish.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/non_local.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/norm.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/padding.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/plugin.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/registry.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/scale.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/swish.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/transformer.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/upsample.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/wrappers.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/builder.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/resnet.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/flops_counter.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/fuse_conv_bn.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/sync_bn.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/weight_init.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/vgg.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/engine/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/engine/test.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/file_client.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/base.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/json_handler.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/pickle_handler.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/yaml_handler.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/io.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/parse.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/image/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/image/colorspace.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/image/geometric.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/image/io.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/image/misc.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/image/photometric.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/cuda/modulated_deform_conv_cuda_kernel.cuh delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/cuda/modulated_deform_conv_cuda.cu delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/info.cpp delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv.cpp delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv_cpu.cpp delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/deprecated_wrappers.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/info.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/modulated_deform_conv.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/ops/sync_bn.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/_functions.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/collate.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/data_container.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/data_parallel.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/distributed.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/distributed_deprecated.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/registry.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/scatter_gather.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/utils.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/base_module.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/base_runner.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/builder.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/checkpoint.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/default_constructor.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/dist_utils.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/epoch_based_runner.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/fp16_utils.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/checkpoint.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/closure.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/ema.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/evaluation.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/hook.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/iter_timer.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/base.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/dvclive.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/mlflow.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/neptune.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/pavi.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/tensorboard.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/text.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/wandb.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/lr_updater.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/memory.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/momentum_updater.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/optimizer.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/profiler.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/sampler_seed.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/sync_buffer.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/iter_based_runner.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/log_buffer.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/optimizer/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/optimizer/builder.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/optimizer/default_constructor.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/priority.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/runner/utils.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/config.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/env.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/ext_loader.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/logging.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/misc.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/parrots_jit.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/parrots_wrapper.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/path.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/progressbar.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/registry.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/testing.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/timer.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/trace.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/utils/version_utils.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmcv/version.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/apis/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/apis/generation_inference.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/apis/inpainting_inference.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/apis/matting_inference.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/apis/restoration_face_inference.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/apis/restoration_inference.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/apis/restoration_video_inference.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/apis/test.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/apis/train.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/apis/video_interpolation_inference.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/distributed_wrapper.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/eval_hooks.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/metric_utils.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/metrics.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/niqe_pris_params.npz delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/export/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/export/wrappers.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/hooks/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/hooks/ema.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/hooks/visualization.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/mask.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/misc.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/optimizer/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/optimizer/builder.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/scheduler/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/scheduler/lr_updater.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/utils/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/core/utils/dist_utils.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/base_dataset.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/base_sr_dataset.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/builder.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/dataset_wrappers.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/augmentation.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/compose.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/crop.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/formating.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/loading.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/matlab_like_resize.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/normalization.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/utils.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/registry.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/samplers/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/samplers/distributed_sampler.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/sr_reds_multiple_gt_dataset.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/sr_backbones/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/sr_backbones/basicvsr_net.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/sr_backbones/basicvsr_pp.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/base.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/builder.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/aspp.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/contextual_attention.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/conv.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/downsample.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/ensemble.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/flow_warp.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/gated_conv_module.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/gca_module.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/generation_model_utils.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/img_normalize.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/linear_module.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/mask_conv_module.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/model_utils.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/partial_conv.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/separable_conv_module.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/sr_backbone_utils.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/upsample.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/losses/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/losses/pixelwise_loss.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/losses/utils.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/registry.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/restorers/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/restorers/basic_restorer.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/models/restorers/basicvsr.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/utils/__init__.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/utils/cli.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/utils/collect_env.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/utils/logger.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/utils/setup_env.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/mmedit/version.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/requirements.txt delete mode 100755 cv/super_resolution/basicvsr++/pytorch/setup.py delete mode 100755 cv/super_resolution/basicvsr++/pytorch/train.py diff --git a/cv/super_resolution/basicvsr++/pytorch/.gitignore b/cv/super_resolution/basicvsr++/pytorch/.gitignore deleted file mode 100755 index 3fff415f6..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/.gitignore +++ /dev/null @@ -1,139 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class -**/*.pyc - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/en/_build/ -docs/en/_tmp/ -docs/zh_cn/_build/ -docs/zh_cn/_tmp/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -# custom -.vscode -.idea -*.pkl -*.pkl.json -*.log.json -work_dirs/ -/data/ -/data -mmedit/.mim - -# Pytorch -*.pth - -# onnx and tensorrt -*.onnx -*.trt - -# local history -.history/** - -# Pytorch Server -*.mar - -# MacOS -.DS_Store -work_dirs/ -tests/ -demo/ -tools/ diff --git a/cv/super_resolution/basicvsr++/pytorch/LICENSE b/cv/super_resolution/basicvsr++/pytorch/LICENSE deleted file mode 100755 index 94d620d25..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Copyright (c) MMEditing Authors. All rights reserved. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020 MMEditing Authors. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cv/super_resolution/basicvsr++/pytorch/README.md b/cv/super_resolution/basicvsr++/pytorch/README.md index 82126a07f..c5058120b 100755 --- a/cv/super_resolution/basicvsr++/pytorch/README.md +++ b/cv/super_resolution/basicvsr++/pytorch/README.md @@ -7,7 +7,18 @@ A recurrent structure is a popular framework choice for the task of video super- ## Step 1: Installing packages ```shell -sh build_env.sh +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +git clone https://github.com/open-mmlab/mmagic.git -b v1.2.0 --depth=1 +cd mmagic/ +pip3 install -e . -v + +sed -i 's/diffusers.models.unet_2d_condition/diffusers.models.unets.unet_2d_condition/g' mmagic/models/editors/vico/vico_utils.py +pip install albumentations ``` ## Step 2: Preparing datasets @@ -22,18 +33,14 @@ ln -s ${REDS_DATASET_PATH} data/REDS ### One single GPU ```shell -python3 train.py [training args] # config file can be found in the configs directory +python3 tools/train.py configs/basicvsr_pp/basicvsr-pp_c64n7_8xb1-600k_reds4.py ``` ### Mutiple GPUs on one machine ```shell -bash dist_train.sh [training args] # config file can be found in the configs directory -``` -### Example - -```shell -bash dist_train.sh configs/basicvsr_plusplus/basicvsr_plusplus_c64n7_8x1_600k_reds4.py 8 +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/basicvsr_pp/basicvsr-pp_c64n7_8xb1-600k_reds4.py 8 ``` ## Reference -https://github.com/open-mmlab/mmediting +[mmagic](https://github.com/open-mmlab/mmagic) diff --git a/cv/super_resolution/basicvsr++/pytorch/build_env.sh b/cv/super_resolution/basicvsr++/pytorch/build_env.sh deleted file mode 100755 index 5c77a38df..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/build_env.sh +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. - - -PIPCMD=pip3 - -MMCV_WITH_OPS=1 python3 setup.py build && cp build/lib.linux*/mmcv/_ext.cpython* mmcv -$PIPCMD install -r requirements.txt diff --git a/cv/super_resolution/basicvsr++/pytorch/configs/basicvsr_plusplus/basicvsr_plusplus_c64n7_8x1_600k_reds4.py b/cv/super_resolution/basicvsr++/pytorch/configs/basicvsr_plusplus/basicvsr_plusplus_c64n7_8x1_600k_reds4.py deleted file mode 100755 index 2db94b926..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/configs/basicvsr_plusplus/basicvsr_plusplus_c64n7_8x1_600k_reds4.py +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -exp_name = 'basicvsr_plusplus_c64n7_8x1_600k_reds4' - -# model settings -model = dict( - type='BasicVSR', - generator=dict( - type='BasicVSRPlusPlus', - mid_channels=64, - num_blocks=7, - is_low_res_input=True, - spynet_pretrained='https://download.openmmlab.com/mmediting/restorers/basicvsr/spynet_20210409-c6c1bd09.pth'), - pixel_loss=dict(type='CharbonnierLoss', loss_weight=1.0, reduction='mean')) -# model training and testing settings -train_cfg = dict(fix_iter=5000) -test_cfg = dict(metrics=['PSNR'], crop_border=0) - -# dataset settings -train_dataset_type = 'SRREDSMultipleGTDataset' -val_dataset_type = 'SRREDSMultipleGTDataset' - -train_pipeline = [ - dict(type='GenerateSegmentIndices', interval_list=[1]), - dict( - type='LoadImageFromFileList', - io_backend='disk', - key='lq', - channel_order='rgb'), - dict( - type='LoadImageFromFileList', - io_backend='disk', - key='gt', - channel_order='rgb'), - dict(type='RescaleToZeroOne', keys=['lq', 'gt']), - dict(type='PairedRandomCrop', gt_patch_size=256), - dict( - type='Flip', keys=['lq', 'gt'], flip_ratio=0.5, - direction='horizontal'), - dict(type='Flip', keys=['lq', 'gt'], flip_ratio=0.5, direction='vertical'), - dict(type='RandomTransposeHW', keys=['lq', 'gt'], transpose_ratio=0.5), - dict(type='FramesToTensor', keys=['lq', 'gt']), - dict(type='Collect', keys=['lq', 'gt'], meta_keys=['lq_path', 'gt_path']) -] - -test_pipeline = [ - dict(type='GenerateSegmentIndices', interval_list=[1]), - dict( - type='LoadImageFromFileList', - io_backend='disk', - key='lq', - channel_order='rgb'), - dict( - type='LoadImageFromFileList', - io_backend='disk', - key='gt', - channel_order='rgb'), - dict(type='RescaleToZeroOne', keys=['lq', 'gt']), - dict(type='FramesToTensor', keys=['lq', 'gt']), - dict( - type='Collect', - keys=['lq', 'gt'], - meta_keys=['lq_path', 'gt_path', 'key']) -] - -demo_pipeline = [ - dict(type='GenerateSegmentIndices', interval_list=[1]), - dict( - type='LoadImageFromFileList', - io_backend='disk', - key='lq', - channel_order='rgb'), - dict(type='RescaleToZeroOne', keys=['lq']), - dict(type='FramesToTensor', keys=['lq']), - dict(type='Collect', keys=['lq'], meta_keys=['lq_path', 'key']) -] - -data = dict( - workers_per_gpu=6, - train_dataloader=dict(samples_per_gpu=1, drop_last=True), # 8 gpus - val_dataloader=dict(samples_per_gpu=1), - test_dataloader=dict(samples_per_gpu=1, workers_per_gpu=1), - - # train - train=dict( - type='RepeatDataset', - times=1000, - dataset=dict( - type=train_dataset_type, - lq_folder='data/REDS/train/train_sharp_bicubic/X4', - gt_folder='data/REDS/train/train_sharp', - num_input_frames=30, - pipeline=train_pipeline, - scale=4, - val_partition='official', - test_mode=False)), - # val - val=dict( - type=val_dataset_type, - lq_folder='data/REDS/train/train_sharp_bicubic/X4', - gt_folder='data/REDS/train/train_sharp', - num_input_frames=100, - pipeline=test_pipeline, - scale=4, - val_partition='official', - repeat=2, - test_mode=True), - # test - test=dict( - type=val_dataset_type, - lq_folder='data/REDS/train_sharp_bicubic/X4', - gt_folder='data/REDS/train_sharp', - num_input_frames=100, - pipeline=test_pipeline, - scale=4, - val_partition='REDS4', - test_mode=True), -) - -# optimizer -optimizers = dict( - generator=dict( - type='Adam', - lr=1e-4, - betas=(0.9, 0.99), - paramwise_cfg=dict(custom_keys={'spynet': dict(lr_mult=0.25)}))) - -# learning policy -total_iters = 600000 -lr_config = dict( - policy='CosineRestart', - by_epoch=False, - periods=[600000], - restart_weights=[1], - min_lr=1e-7) - -checkpoint_config = dict(interval=5000, save_optimizer=True, by_epoch=False) -# remove gpu_collect=True in non distributed training -evaluation = dict(interval=5000, save_image=False, gpu_collect=True) -log_config = dict( - interval=1, - hooks=[ - dict(type='TextLoggerHook', by_epoch=False), - # dict(type='TensorboardLoggerHook'), - ]) -visual_config = None - -# runtime settings -dist_params = dict(backend='nccl') -log_level = 'INFO' -work_dir = f'./work_dirs/{exp_name}' -load_from = None -resume_from = None -workflow = [('train', 1)] -find_unused_parameters = True -cudnn_benchmark = True diff --git a/cv/super_resolution/basicvsr++/pytorch/dist_train.sh b/cv/super_resolution/basicvsr++/pytorch/dist_train.sh deleted file mode 100755 index 87c687cd7..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/dist_train.sh +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -#!/usr/bin/env bash - -CONFIG=$1 -GPUS=$2 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/train.py \ - $CONFIG \ - --seed 0 \ - --launcher pytorch ${@:3} diff --git a/cv/super_resolution/basicvsr++/pytorch/docker/Dockerfile b/cv/super_resolution/basicvsr++/pytorch/docker/Dockerfile deleted file mode 100755 index 651287043..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/docker/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -ARG PYTORCH="1.6.0" -ARG CUDA="10.1" -ARG CUDA_ALIAS="101" -ARG CUDNN="7" -ARG MMCV="1.3.1" - -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ENV TORCH_CUDA_ARCH_LIST="6.0 6.1 7.0+PTX" -ENV TORCH_NVCC_FLAGS="-Xfatbin -compress-all" -ENV CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" - -RUN apt-get update && apt-get install -y git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 libgl1-mesa-glx \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Install mmediting -RUN conda clean --all -RUN git clone https://github.com/open-mmlab/mmediting.git /mmediting -WORKDIR /mmediting -ENV FORCE_CUDA="1" -RUN pip install mmcv-full==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA_ALIAS}/torch${PYTORCH}/index.html -RUN pip install -r requirements.txt -RUN pip install --no-cache-dir -e . diff --git a/cv/super_resolution/basicvsr++/pytorch/docker/README.md b/cv/super_resolution/basicvsr++/pytorch/docker/README.md deleted file mode 100755 index 851c28b0b..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/docker/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Docker Image - -We provide a [Dockerfile](Dockerfile) to build an image. - -```shell -# build an image with PyTorch 1.6, CUDA 10.1 -docker build -t mmediting docker/ -``` - -Run it with - -```shell -docker run --gpus all --shm-size=8g -it -v {DATA_DIR}:/mmediting/data mmediting -``` - -**Note**: -Versions defined in this [Dockerfile](Dockerfile) is not up-to-date. -If you use this Dockerfile in your project, you probably want to make some updates. -Feel free to submit an issue or PR for the update. diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/__init__.py deleted file mode 100755 index 30b661fb5..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -from .arraymisc import * -from .fileio import * -from .image import * -from .utils import * -from .version import * - -# The following modules are not imported to this level, so mmcv may be used -# without PyTorch. -# - runner -# - parallel -# - op diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/arraymisc/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/arraymisc/__init__.py deleted file mode 100755 index 4b4700d61..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/arraymisc/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .quantization import dequantize, quantize - -__all__ = ['quantize', 'dequantize'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/arraymisc/quantization.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/arraymisc/quantization.py deleted file mode 100755 index 8e47a3545..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/arraymisc/quantization.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - - -def quantize(arr, min_val, max_val, levels, dtype=np.int64): - """Quantize an array of (-inf, inf) to [0, levels-1]. - - Args: - arr (ndarray): Input array. - min_val (scalar): Minimum value to be clipped. - max_val (scalar): Maximum value to be clipped. - levels (int): Quantization levels. - dtype (np.type): The type of the quantized array. - - Returns: - tuple: Quantized array. - """ - if not (isinstance(levels, int) and levels > 1): - raise ValueError( - f'levels must be a positive integer, but got {levels}') - if min_val >= max_val: - raise ValueError( - f'min_val ({min_val}) must be smaller than max_val ({max_val})') - - arr = np.clip(arr, min_val, max_val) - min_val - quantized_arr = np.minimum( - np.floor(levels * arr / (max_val - min_val)).astype(dtype), levels - 1) - - return quantized_arr - - -def dequantize(arr, min_val, max_val, levels, dtype=np.float64): - """Dequantize an array. - - Args: - arr (ndarray): Input array. - min_val (scalar): Minimum value to be clipped. - max_val (scalar): Maximum value to be clipped. - levels (int): Quantization levels. - dtype (np.type): The type of the dequantized array. - - Returns: - tuple: Dequantized array. - """ - if not (isinstance(levels, int) and levels > 1): - raise ValueError( - f'levels must be a positive integer, but got {levels}') - if min_val >= max_val: - raise ValueError( - f'min_val ({min_val}) must be smaller than max_val ({max_val})') - - dequantized_arr = (arr + 0.5).astype(dtype) * (max_val - - min_val) / levels + min_val - - return dequantized_arr diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/__init__.py deleted file mode 100755 index 7246c8974..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .alexnet import AlexNet -# yapf: disable -from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, - ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, - ConvTranspose2d, ConvTranspose3d, ConvWS2d, - DepthwiseSeparableConvModule, GeneralizedAttention, - HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, - NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, - build_activation_layer, build_conv_layer, - build_norm_layer, build_padding_layer, build_plugin_layer, - build_upsample_layer, conv_ws_2d, is_norm) -from .builder import MODELS, build_model_from_cfg -# yapf: enable -from .resnet import ResNet, make_res_layer -from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, - NormalInit, PretrainedInit, TruncNormalInit, UniformInit, - XavierInit, bias_init_with_prob, caffe2_xavier_init, - constant_init, fuse_conv_bn, get_model_complexity_info, - initialize, kaiming_init, normal_init, trunc_normal_init, - uniform_init, xavier_init) -from .vgg import VGG, make_vgg_layer - -__all__ = [ - 'AlexNet', 'VGG', 'make_vgg_layer', 'ResNet', 'make_res_layer', - 'constant_init', 'xavier_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'kaiming_init', 'caffe2_xavier_init', - 'bias_init_with_prob', 'ConvModule', 'build_activation_layer', - 'build_conv_layer', 'build_norm_layer', 'build_padding_layer', - 'build_upsample_layer', 'build_plugin_layer', 'is_norm', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'HSigmoid', 'Swish', 'HSwish', - 'GeneralizedAttention', 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', - 'PADDING_LAYERS', 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', - 'get_model_complexity_info', 'conv_ws_2d', 'ConvAWS2d', 'ConvWS2d', - 'fuse_conv_bn', 'DepthwiseSeparableConvModule', 'Linear', 'Conv2d', - 'ConvTranspose2d', 'MaxPool2d', 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', - 'initialize', 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'MODELS', 'build_model_from_cfg' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/alexnet.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/alexnet.py deleted file mode 100755 index 89e36b8c7..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/alexnet.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn - - -class AlexNet(nn.Module): - """AlexNet backbone. - - Args: - num_classes (int): number of classes for classification. - """ - - def __init__(self, num_classes=-1): - super(AlexNet, self).__init__() - self.num_classes = num_classes - self.features = nn.Sequential( - nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - nn.Conv2d(64, 192, kernel_size=5, padding=2), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - nn.Conv2d(192, 384, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.Conv2d(384, 256, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.Conv2d(256, 256, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - ) - if self.num_classes > 0: - self.classifier = nn.Sequential( - nn.Dropout(), - nn.Linear(256 * 6 * 6, 4096), - nn.ReLU(inplace=True), - nn.Dropout(), - nn.Linear(4096, 4096), - nn.ReLU(inplace=True), - nn.Linear(4096, num_classes), - ) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - # use default initializer - pass - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - - x = self.features(x) - if self.num_classes > 0: - x = x.view(x.size(0), 256 * 6 * 6) - x = self.classifier(x) - - return x diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/__init__.py deleted file mode 100755 index 0f33124ed..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .activation import build_activation_layer -from .context_block import ContextBlock -from .conv import build_conv_layer -from .conv2d_adaptive_padding import Conv2dAdaptivePadding -from .conv_module import ConvModule -from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d -from .depthwise_separable_conv_module import DepthwiseSeparableConvModule -from .drop import Dropout, DropPath -from .generalized_attention import GeneralizedAttention -from .hsigmoid import HSigmoid -from .hswish import HSwish -from .non_local import NonLocal1d, NonLocal2d, NonLocal3d -from .norm import build_norm_layer, is_norm -from .padding import build_padding_layer -from .plugin import build_plugin_layer -from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) -from .scale import Scale -from .swish import Swish -from .upsample import build_upsample_layer -from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, - Linear, MaxPool2d, MaxPool3d) - -__all__ = [ - 'ConvModule', 'build_activation_layer', 'build_conv_layer', - 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', - 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', - 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', - 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', - 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', - 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', - 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/activation.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/activation.py deleted file mode 100755 index 79f198838..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/activation.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version -from .registry import ACTIVATION_LAYERS - -for module in [ - nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, - nn.Sigmoid, nn.Tanh -]: - ACTIVATION_LAYERS.register_module(module=module) - - -@ACTIVATION_LAYERS.register_module(name='Clip') -@ACTIVATION_LAYERS.register_module() -class Clamp(nn.Module): - """Clamp activation layer. - - This activation function is to clamp the feature map value within - :math:`[min, max]`. More details can be found in ``torch.clamp()``. - - Args: - min (Number | optional): Lower-bound of the range to be clamped to. - Default to -1. - max (Number | optional): Upper-bound of the range to be clamped to. - Default to 1. - """ - - def __init__(self, min=-1., max=1.): - super(Clamp, self).__init__() - self.min = min - self.max = max - - def forward(self, x): - """Forward function. - - Args: - x (torch.Tensor): The input tensor. - - Returns: - torch.Tensor: Clamped tensor. - """ - return torch.clamp(x, min=self.min, max=self.max) - - -class GELU(nn.Module): - r"""Applies the Gaussian Error Linear Units function: - - .. math:: - \text{GELU}(x) = x * \Phi(x) - where :math:`\Phi(x)` is the Cumulative Distribution Function for - Gaussian Distribution. - - Shape: - - Input: :math:`(N, *)` where `*` means, any number of additional - dimensions - - Output: :math:`(N, *)`, same shape as the input - - .. image:: scripts/activation_images/GELU.png - - Examples:: - - >>> m = nn.GELU() - >>> input = torch.randn(2) - >>> output = m(input) - """ - - def forward(self, input): - return F.gelu(input) - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.4')): - ACTIVATION_LAYERS.register_module(module=GELU) -else: - ACTIVATION_LAYERS.register_module(module=nn.GELU) - - -def build_activation_layer(cfg): - """Build activation layer. - - Args: - cfg (dict): The activation layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an activation layer. - - Returns: - nn.Module: Created activation layer. - """ - return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/context_block.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/context_block.py deleted file mode 100755 index d60fdb904..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/context_block.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn - -from ..utils import constant_init, kaiming_init -from .registry import PLUGIN_LAYERS - - -def last_zero_init(m): - if isinstance(m, nn.Sequential): - constant_init(m[-1], val=0) - else: - constant_init(m, val=0) - - -@PLUGIN_LAYERS.register_module() -class ContextBlock(nn.Module): - """ContextBlock module in GCNet. - - See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' - (https://arxiv.org/abs/1904.11492) for details. - - Args: - in_channels (int): Channels of the input feature map. - ratio (float): Ratio of channels of transform bottleneck - pooling_type (str): Pooling method for context modeling. - Options are 'att' and 'avg', stand for attention pooling and - average pooling respectively. Default: 'att'. - fusion_types (Sequence[str]): Fusion method for feature fusion, - Options are 'channels_add', 'channel_mul', stand for channelwise - addition and multiplication respectively. Default: ('channel_add',) - """ - - _abbr_ = 'context_block' - - def __init__(self, - in_channels, - ratio, - pooling_type='att', - fusion_types=('channel_add', )): - super(ContextBlock, self).__init__() - assert pooling_type in ['avg', 'att'] - assert isinstance(fusion_types, (list, tuple)) - valid_fusion_types = ['channel_add', 'channel_mul'] - assert all([f in valid_fusion_types for f in fusion_types]) - assert len(fusion_types) > 0, 'at least one fusion should be used' - self.in_channels = in_channels - self.ratio = ratio - self.planes = int(in_channels * ratio) - self.pooling_type = pooling_type - self.fusion_types = fusion_types - if pooling_type == 'att': - self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) - self.softmax = nn.Softmax(dim=2) - else: - self.avg_pool = nn.AdaptiveAvgPool2d(1) - if 'channel_add' in fusion_types: - self.channel_add_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_add_conv = None - if 'channel_mul' in fusion_types: - self.channel_mul_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_mul_conv = None - self.reset_parameters() - - def reset_parameters(self): - if self.pooling_type == 'att': - kaiming_init(self.conv_mask, mode='fan_in') - self.conv_mask.inited = True - - if self.channel_add_conv is not None: - last_zero_init(self.channel_add_conv) - if self.channel_mul_conv is not None: - last_zero_init(self.channel_mul_conv) - - def spatial_pool(self, x): - batch, channel, height, width = x.size() - if self.pooling_type == 'att': - input_x = x - # [N, C, H * W] - input_x = input_x.view(batch, channel, height * width) - # [N, 1, C, H * W] - input_x = input_x.unsqueeze(1) - # [N, 1, H, W] - context_mask = self.conv_mask(x) - # [N, 1, H * W] - context_mask = context_mask.view(batch, 1, height * width) - # [N, 1, H * W] - context_mask = self.softmax(context_mask) - # [N, 1, H * W, 1] - context_mask = context_mask.unsqueeze(-1) - # [N, 1, C, 1] - context = torch.matmul(input_x, context_mask) - # [N, C, 1, 1] - context = context.view(batch, channel, 1, 1) - else: - # [N, C, 1, 1] - context = self.avg_pool(x) - - return context - - def forward(self, x): - # [N, C, 1, 1] - context = self.spatial_pool(x) - - out = x - if self.channel_mul_conv is not None: - # [N, C, 1, 1] - channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) - out = out * channel_mul_term - if self.channel_add_conv is not None: - # [N, C, 1, 1] - channel_add_term = self.channel_add_conv(context) - out = out + channel_add_term - - return out diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv.py deleted file mode 100755 index cf5449199..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from .registry import CONV_LAYERS - -CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) -CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) -CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) -CONV_LAYERS.register_module('Conv', module=nn.Conv2d) - - -def build_conv_layer(cfg, *args, **kwargs): - """Build convolution layer. - - Args: - cfg (None or dict): The conv layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an conv layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding conv layer. - - Returns: - nn.Module: Created conv layer. - """ - if cfg is None: - cfg_ = dict(type='Conv2d') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in CONV_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - else: - conv_layer = CONV_LAYERS.get(layer_type) - - layer = conv_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py deleted file mode 100755 index b45e758ac..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from torch import nn -from torch.nn import functional as F - -from .registry import CONV_LAYERS - - -@CONV_LAYERS.register_module() -class Conv2dAdaptivePadding(nn.Conv2d): - """Implementation of 2D convolution in tensorflow with `padding` as "same", - which applies padding to input (if needed) so that input image gets fully - covered by filter and stride you specified. For stride 1, this will ensure - that output image size is same as input. For stride of 2, output dimensions - will be half, for example. - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the convolving kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If ``True``, adds a learnable bias to the - output. Default: ``True`` - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__(in_channels, out_channels, kernel_size, stride, 0, - dilation, groups, bias) - - def forward(self, x): - img_h, img_w = x.size()[-2:] - kernel_h, kernel_w = self.weight.size()[-2:] - stride_h, stride_w = self.stride - output_h = math.ceil(img_h / stride_h) - output_w = math.ceil(img_w / stride_w) - pad_h = ( - max((output_h - 1) * self.stride[0] + - (kernel_h - 1) * self.dilation[0] + 1 - img_h, 0)) - pad_w = ( - max((output_w - 1) * self.stride[1] + - (kernel_w - 1) * self.dilation[1] + 1 - img_w, 0)) - if pad_h > 0 or pad_w > 0: - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2 - ]) - return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv_module.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv_module.py deleted file mode 100755 index 4f19f1d0c..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv_module.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from mmcv.utils import _BatchNorm, _InstanceNorm -from ..utils import constant_init, kaiming_init -from .activation import build_activation_layer -from .conv import build_conv_layer -from .norm import build_norm_layer -from .padding import build_padding_layer -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class ConvModule(nn.Module): - """A conv block that bundles conv/norm/activation layers. - - This block simplifies the usage of convolution layers, which are commonly - used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). - It is based upon three build methods: `build_conv_layer()`, - `build_norm_layer()` and `build_activation_layer()`. - - Besides, we add some additional features in this module. - 1. Automatically set `bias` of the conv layer. - 2. Spectral norm is supported. - 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only - supports zero and circular padding, and we add "reflect" padding mode. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. - groups (int): Number of blocked connections from input channels to - output channels. Same as that in ``nn._ConvNd``. - bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - inplace (bool): Whether to use inplace mode for activation. - Default: True. - with_spectral_norm (bool): Whether use spectral norm in conv module. - Default: False. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in PyTorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Common examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - Default: ('conv', 'norm', 'act'). - """ - - _abbr_ = 'conv_block' - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias='auto', - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - padding_mode='zeros', - order=('conv', 'norm', 'act')): - super(ConvModule, self).__init__() - assert conv_cfg is None or isinstance(conv_cfg, dict) - assert norm_cfg is None or isinstance(norm_cfg, dict) - assert act_cfg is None or isinstance(act_cfg, dict) - official_padding_mode = ['zeros', 'circular'] - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.with_explicit_padding = padding_mode not in official_padding_mode - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 3 - assert set(order) == set(['conv', 'norm', 'act']) - - self.with_norm = norm_cfg is not None - self.with_activation = act_cfg is not None - # if the conv layer is before a norm layer, bias is unnecessary. - if bias == 'auto': - bias = not self.with_norm - self.with_bias = bias - - if self.with_explicit_padding: - pad_cfg = dict(type=padding_mode) - self.padding_layer = build_padding_layer(pad_cfg, padding) - - # reset padding to 0 for conv module - conv_padding = 0 if self.with_explicit_padding else padding - # build convolution layer - self.conv = build_conv_layer( - conv_cfg, - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=conv_padding, - dilation=dilation, - groups=groups, - bias=bias) - # export the attributes of self.conv to a higher level for convenience - self.in_channels = self.conv.in_channels - self.out_channels = self.conv.out_channels - self.kernel_size = self.conv.kernel_size - self.stride = self.conv.stride - self.padding = padding - self.dilation = self.conv.dilation - self.transposed = self.conv.transposed - self.output_padding = self.conv.output_padding - self.groups = self.conv.groups - - if self.with_spectral_norm: - self.conv = nn.utils.spectral_norm(self.conv) - - # build normalization layers - if self.with_norm: - # norm layer is after conv layer - if order.index('norm') > order.index('conv'): - norm_channels = out_channels - else: - norm_channels = in_channels - self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) - self.add_module(self.norm_name, norm) - if self.with_bias: - if isinstance(norm, (_BatchNorm, _InstanceNorm)): - warnings.warn( - 'Unnecessary conv bias before batch/instance norm') - else: - self.norm_name = None - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - # nn.Tanh has no 'inplace' argument - if act_cfg_['type'] not in [ - 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish' - ]: - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - @property - def norm(self): - if self.norm_name: - return getattr(self, self.norm_name) - else: - return None - - def init_weights(self): - # 1. It is mainly for customized conv layers with their own - # initialization manners by calling their own ``init_weights()``, - # and we do not want ConvModule to override the initialization. - # 2. For customized conv layers without their own initialization - # manners (that is, they don't have their own ``init_weights()``) - # and PyTorch's conv layers, they will be initialized by - # this method with default ``kaiming_init``. - # Note: For PyTorch's conv layers, they will be overwritten by our - # initialization implementation using default ``kaiming_init``. - if not hasattr(self.conv, 'init_weights'): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) - if self.with_norm: - constant_init(self.norm, 1, bias=0) - - def forward(self, x, activate=True, norm=True): - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - x = self.conv(x) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv_ws.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv_ws.py deleted file mode 100755 index a3941e278..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/conv_ws.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from .registry import CONV_LAYERS - - -def conv_ws_2d(input, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - eps=1e-5): - c_in = weight.size(0) - weight_flat = weight.view(c_in, -1) - mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1) - std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1) - weight = (weight - mean) / (std + eps) - return F.conv2d(input, weight, bias, stride, padding, dilation, groups) - - -@CONV_LAYERS.register_module('ConvWS') -class ConvWS2d(nn.Conv2d): - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True, - eps=1e-5): - super(ConvWS2d, self).__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.eps = eps - - def forward(self, x): - return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups, self.eps) - - -@CONV_LAYERS.register_module(name='ConvAWS') -class ConvAWS2d(nn.Conv2d): - """AWS (Adaptive Weight Standardization) - - This is a variant of Weight Standardization - (https://arxiv.org/pdf/1903.10520.pdf) - It is used in DetectoRS to avoid NaN - (https://arxiv.org/pdf/2006.02334.pdf) - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the conv kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If set True, adds a learnable bias to the - output. Default: True - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.register_buffer('weight_gamma', - torch.ones(self.out_channels, 1, 1, 1)) - self.register_buffer('weight_beta', - torch.zeros(self.out_channels, 1, 1, 1)) - - def _get_weight(self, weight): - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - weight = (weight - mean) / std - weight = self.weight_gamma * weight + self.weight_beta - return weight - - def forward(self, x): - weight = self._get_weight(self.weight) - return F.conv2d(x, weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - """Override default load function. - - AWS overrides the function _load_from_state_dict to recover - weight_gamma and weight_beta if they are missing. If weight_gamma and - weight_beta are found in the checkpoint, this function will return - after super()._load_from_state_dict. Otherwise, it will compute the - mean and std of the pretrained weights and store them in weight_beta - and weight_gamma. - """ - - self.weight_gamma.data.fill_(-1) - local_missing_keys = [] - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, local_missing_keys, - unexpected_keys, error_msgs) - if self.weight_gamma.data.mean() > 0: - for k in local_missing_keys: - missing_keys.append(k) - return - weight = self.weight.data - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - self.weight_beta.data.copy_(mean) - self.weight_gamma.data.copy_(std) - missing_gamma_beta = [ - k for k in local_missing_keys - if k.endswith('weight_gamma') or k.endswith('weight_beta') - ] - for k in missing_gamma_beta: - local_missing_keys.remove(k) - for k in local_missing_keys: - missing_keys.append(k) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py deleted file mode 100755 index 722d5d8d7..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .conv_module import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if `norm_cfg` and `act_cfg` are specified. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. Default: 1. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. Default: 0. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super(DepthwiseSeparableConvModule, self).__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/drop.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/drop.py deleted file mode 100755 index b0a026654..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/drop.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from mmcv import build_from_cfg -from .registry import DROPOUT_LAYERS - - -def drop_path(x, drop_prob=0., training=False): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - """ - if drop_prob == 0. or not training: - return x - keep_prob = 1 - drop_prob - # handle tensors with different dimensions, not just 4D tensors. - shape = (x.shape[0], ) + (1, ) * (x.ndim - 1) - random_tensor = keep_prob + torch.rand( - shape, dtype=x.dtype, device=x.device) - output = x.div(keep_prob) * random_tensor.floor() - return output - - -@DROPOUT_LAYERS.register_module() -class DropPath(nn.Module): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - - Args: - drop_prob (float): Probability of the path to be zeroed. Default: 0.1 - """ - - def __init__(self, drop_prob=0.1): - super(DropPath, self).__init__() - self.drop_prob = drop_prob - - def forward(self, x): - return drop_path(x, self.drop_prob, self.training) - - -@DROPOUT_LAYERS.register_module() -class Dropout(nn.Dropout): - """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of - ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with - ``DropPath`` - - Args: - drop_prob (float): Probability of the elements to be - zeroed. Default: 0.5. - inplace (bool): Do the operation inplace or not. Default: False. - """ - - def __init__(self, drop_prob=0.5, inplace=False): - super().__init__(p=drop_prob, inplace=inplace) - - -def build_dropout(cfg, default_args=None): - """Builder for drop out layers.""" - return build_from_cfg(cfg, DROPOUT_LAYERS, default_args) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/generalized_attention.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/generalized_attention.py deleted file mode 100755 index 988d9adf2..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/generalized_attention.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import kaiming_init -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class GeneralizedAttention(nn.Module): - """GeneralizedAttention module. - - See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' - (https://arxiv.org/abs/1711.07971) for details. - - Args: - in_channels (int): Channels of the input feature map. - spatial_range (int): The spatial range. -1 indicates no spatial range - constraint. Default: -1. - num_heads (int): The head number of empirical_attention module. - Default: 9. - position_embedding_dim (int): The position embedding dimension. - Default: -1. - position_magnitude (int): A multiplier acting on coord difference. - Default: 1. - kv_stride (int): The feature stride acting on key/value feature map. - Default: 2. - q_stride (int): The feature stride acting on query feature map. - Default: 1. - attention_type (str): A binary indicator string for indicating which - items in generalized empirical_attention module are used. - Default: '1111'. - - - '1000' indicates 'query and key content' (appr - appr) item, - - '0100' indicates 'query content and relative position' - (appr - position) item, - - '0010' indicates 'key content only' (bias - appr) item, - - '0001' indicates 'relative position only' (bias - position) item. - """ - - _abbr_ = 'gen_attention_block' - - def __init__(self, - in_channels, - spatial_range=-1, - num_heads=9, - position_embedding_dim=-1, - position_magnitude=1, - kv_stride=2, - q_stride=1, - attention_type='1111'): - - super(GeneralizedAttention, self).__init__() - - # hard range means local range for non-local operation - self.position_embedding_dim = ( - position_embedding_dim - if position_embedding_dim > 0 else in_channels) - - self.position_magnitude = position_magnitude - self.num_heads = num_heads - self.in_channels = in_channels - self.spatial_range = spatial_range - self.kv_stride = kv_stride - self.q_stride = q_stride - self.attention_type = [bool(int(_)) for _ in attention_type] - self.qk_embed_dim = in_channels // num_heads - out_c = self.qk_embed_dim * num_heads - - if self.attention_type[0] or self.attention_type[1]: - self.query_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.query_conv.kaiming_init = True - - if self.attention_type[0] or self.attention_type[2]: - self.key_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.key_conv.kaiming_init = True - - self.v_dim = in_channels // num_heads - self.value_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=self.v_dim * num_heads, - kernel_size=1, - bias=False) - self.value_conv.kaiming_init = True - - if self.attention_type[1] or self.attention_type[3]: - self.appr_geom_fc_x = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_x.kaiming_init = True - - self.appr_geom_fc_y = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_y.kaiming_init = True - - if self.attention_type[2]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.appr_bias = nn.Parameter(appr_bias_value) - - if self.attention_type[3]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.geom_bias = nn.Parameter(geom_bias_value) - - self.proj_conv = nn.Conv2d( - in_channels=self.v_dim * num_heads, - out_channels=in_channels, - kernel_size=1, - bias=True) - self.proj_conv.kaiming_init = True - self.gamma = nn.Parameter(torch.zeros(1)) - - if self.spatial_range >= 0: - # only works when non local is after 3*3 conv - if in_channels == 256: - max_len = 84 - elif in_channels == 512: - max_len = 42 - - max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) - local_constraint_map = np.ones( - (max_len, max_len, max_len_kv, max_len_kv), dtype=np.int) - for iy in range(max_len): - for ix in range(max_len): - local_constraint_map[ - iy, ix, - max((iy - self.spatial_range) // - self.kv_stride, 0):min((iy + self.spatial_range + - 1) // self.kv_stride + - 1, max_len), - max((ix - self.spatial_range) // - self.kv_stride, 0):min((ix + self.spatial_range + - 1) // self.kv_stride + - 1, max_len)] = 0 - - self.local_constraint_map = nn.Parameter( - torch.from_numpy(local_constraint_map).byte(), - requires_grad=False) - - if self.q_stride > 1: - self.q_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.q_stride) - else: - self.q_downsample = None - - if self.kv_stride > 1: - self.kv_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.kv_stride) - else: - self.kv_downsample = None - - self.init_weights() - - def get_position_embedding(self, - h, - w, - h_kv, - w_kv, - q_stride, - kv_stride, - device, - dtype, - feat_dim, - wave_length=1000): - # the default type of Tensor is float32, leading to type mismatch - # in fp16 mode. Cast it to support fp16 mode. - h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) - h_idxs = h_idxs.view((h, 1)) * q_stride - - w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) - w_idxs = w_idxs.view((w, 1)) * q_stride - - h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( - device=device, dtype=dtype) - h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride - - w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( - device=device, dtype=dtype) - w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride - - # (h, h_kv, 1) - h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) - h_diff *= self.position_magnitude - - # (w, w_kv, 1) - w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) - w_diff *= self.position_magnitude - - feat_range = torch.arange(0, feat_dim / 4).to( - device=device, dtype=dtype) - - dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) - dim_mat = dim_mat**((4. / feat_dim) * feat_range) - dim_mat = dim_mat.view((1, 1, -1)) - - embedding_x = torch.cat( - ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) - - embedding_y = torch.cat( - ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) - - return embedding_x, embedding_y - - def forward(self, x_input): - num_heads = self.num_heads - - # use empirical_attention - if self.q_downsample is not None: - x_q = self.q_downsample(x_input) - else: - x_q = x_input - n, _, h, w = x_q.shape - - if self.kv_downsample is not None: - x_kv = self.kv_downsample(x_input) - else: - x_kv = x_input - _, _, h_kv, w_kv = x_kv.shape - - if self.attention_type[0] or self.attention_type[1]: - proj_query = self.query_conv(x_q).view( - (n, num_heads, self.qk_embed_dim, h * w)) - proj_query = proj_query.permute(0, 1, 3, 2) - - if self.attention_type[0] or self.attention_type[2]: - proj_key = self.key_conv(x_kv).view( - (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) - - if self.attention_type[1] or self.attention_type[3]: - position_embed_x, position_embed_y = self.get_position_embedding( - h, w, h_kv, w_kv, self.q_stride, self.kv_stride, - x_input.device, x_input.dtype, self.position_embedding_dim) - # (n, num_heads, w, w_kv, dim) - position_feat_x = self.appr_geom_fc_x(position_embed_x).\ - view(1, w, w_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - # (n, num_heads, h, h_kv, dim) - position_feat_y = self.appr_geom_fc_y(position_embed_y).\ - view(1, h, h_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - position_feat_x /= math.sqrt(2) - position_feat_y /= math.sqrt(2) - - # accelerate for saliency only - if (np.sum(self.attention_type) == 1) and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy = torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, h_kv * w_kv) - - h = 1 - w = 1 - else: - # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for - if not self.attention_type[0]: - energy = torch.zeros( - n, - num_heads, - h, - w, - h_kv, - w_kv, - dtype=x_input.dtype, - device=x_input.device) - - # attention_type[0]: appr - appr - # attention_type[1]: appr - position - # attention_type[2]: bias - appr - # attention_type[3]: bias - position - if self.attention_type[0] or self.attention_type[2]: - if self.attention_type[0] and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - energy = torch.matmul(proj_query + appr_bias, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[0]: - energy = torch.matmul(proj_query, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy += torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, 1, h_kv, w_kv) - - if self.attention_type[1] or self.attention_type[3]: - if self.attention_type[1] and self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - - proj_query_reshape = (proj_query + geom_bias).\ - view(n, num_heads, h, w, self.qk_embed_dim) - - energy_x = torch.matmul( - proj_query_reshape.permute(0, 1, 3, 2, 4), - position_feat_x.permute(0, 1, 2, 4, 3)) - energy_x = energy_x.\ - permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul( - proj_query_reshape, - position_feat_y.permute(0, 1, 2, 4, 3)) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[1]: - proj_query_reshape = proj_query.\ - view(n, num_heads, h, w, self.qk_embed_dim) - proj_query_reshape = proj_query_reshape.\ - permute(0, 1, 3, 2, 4) - position_feat_x_reshape = position_feat_x.\ - permute(0, 1, 2, 4, 3) - position_feat_y_reshape = position_feat_y.\ - permute(0, 1, 2, 4, 3) - - energy_x = torch.matmul(proj_query_reshape, - position_feat_x_reshape) - energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul(proj_query_reshape, - position_feat_y_reshape) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, self.qk_embed_dim, 1).\ - repeat(n, 1, 1, 1) - - position_feat_x_reshape = position_feat_x.\ - view(n, num_heads, w*w_kv, self.qk_embed_dim) - - position_feat_y_reshape = position_feat_y.\ - view(n, num_heads, h * h_kv, self.qk_embed_dim) - - energy_x = torch.matmul(position_feat_x_reshape, geom_bias) - energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) - - energy_y = torch.matmul(position_feat_y_reshape, geom_bias) - energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) - - energy += energy_x + energy_y - - energy = energy.view(n, num_heads, h * w, h_kv * w_kv) - - if self.spatial_range >= 0: - cur_local_constraint_map = \ - self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ - contiguous().\ - view(1, 1, h*w, h_kv*w_kv) - - energy = energy.masked_fill_(cur_local_constraint_map, - float('-inf')) - - attention = F.softmax(energy, 3) - - proj_value = self.value_conv(x_kv) - proj_value_reshape = proj_value.\ - view((n, num_heads, self.v_dim, h_kv * w_kv)).\ - permute(0, 1, 3, 2) - - out = torch.matmul(attention, proj_value_reshape).\ - permute(0, 1, 3, 2).\ - contiguous().\ - view(n, self.v_dim * self.num_heads, h, w) - - out = self.proj_conv(out) - - # output is downsampled, upsample back to input size - if self.q_downsample is not None: - out = F.interpolate( - out, - size=x_input.shape[2:], - mode='bilinear', - align_corners=False) - - out = self.gamma * out + x_input - return out - - def init_weights(self): - for m in self.modules(): - if hasattr(m, 'kaiming_init') and m.kaiming_init: - kaiming_init( - m, - mode='fan_in', - nonlinearity='leaky_relu', - bias=0, - distribution='uniform', - a=1) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/hsigmoid.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/hsigmoid.py deleted file mode 100755 index 30b1a3d65..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/hsigmoid.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSigmoid(nn.Module): - """Hard Sigmoid Module. Apply the hard sigmoid function: - Hsigmoid(x) = min(max((x + bias) / divisor, min_value), max_value) - Default: Hsigmoid(x) = min(max((x + 1) / 2, 0), 1) - - Args: - bias (float): Bias of the input feature map. Default: 1.0. - divisor (float): Divisor of the input feature map. Default: 2.0. - min_value (float): Lower bound value. Default: 0.0. - max_value (float): Upper bound value. Default: 1.0. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, bias=1.0, divisor=2.0, min_value=0.0, max_value=1.0): - super(HSigmoid, self).__init__() - self.bias = bias - self.divisor = divisor - assert self.divisor != 0 - self.min_value = min_value - self.max_value = max_value - - def forward(self, x): - x = (x + self.bias) / self.divisor - - return x.clamp_(self.min_value, self.max_value) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/hswish.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/hswish.py deleted file mode 100755 index 7e0c090ff..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/hswish.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSwish(nn.Module): - """Hard Swish Module. - - This module applies the hard swish function: - - .. math:: - Hswish(x) = x * ReLU6(x + 3) / 6 - - Args: - inplace (bool): can optionally do the operation in-place. - Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.act = nn.ReLU6(inplace) - - def forward(self, x): - return x * self.act(x + 3) / 6 diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/non_local.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/non_local.py deleted file mode 100755 index 92d00155e..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/non_local.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta - -import torch -import torch.nn as nn - -from ..utils import constant_init, normal_init -from .conv_module import ConvModule -from .registry import PLUGIN_LAYERS - - -class _NonLocalNd(nn.Module, metaclass=ABCMeta): - """Basic Non-local module. - - This module is proposed in - "Non-local Neural Networks" - Paper reference: https://arxiv.org/abs/1711.07971 - Code reference: https://github.com/AlexHex7/Non-local_pytorch - - Args: - in_channels (int): Channels of the input feature map. - reduction (int): Channel reduction ratio. Default: 2. - use_scale (bool): Whether to scale pairwise_weight by - `1/sqrt(inter_channels)` when the mode is `embedded_gaussian`. - Default: True. - conv_cfg (None | dict): The config dict for convolution layers. - If not specified, it will use `nn.Conv2d` for convolution layers. - Default: None. - norm_cfg (None | dict): The config dict for normalization layers. - Default: None. (This parameter is only applicable to conv_out.) - mode (str): Options are `gaussian`, `concatenation`, - `embedded_gaussian` and `dot_product`. Default: embedded_gaussian. - """ - - def __init__(self, - in_channels, - reduction=2, - use_scale=True, - conv_cfg=None, - norm_cfg=None, - mode='embedded_gaussian', - **kwargs): - super(_NonLocalNd, self).__init__() - self.in_channels = in_channels - self.reduction = reduction - self.use_scale = use_scale - self.inter_channels = max(in_channels // reduction, 1) - self.mode = mode - - if mode not in [ - 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation' - ]: - raise ValueError("Mode should be in 'gaussian', 'concatenation', " - f"'embedded_gaussian' or 'dot_product', but got " - f'{mode} instead.') - - # g, theta, phi are defaulted as `nn.ConvNd`. - # Here we use ConvModule for potential usage. - self.g = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.conv_out = ConvModule( - self.inter_channels, - self.in_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - if self.mode != 'gaussian': - self.theta = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.phi = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - - if self.mode == 'concatenation': - self.concat_project = ConvModule( - self.inter_channels * 2, - 1, - kernel_size=1, - stride=1, - padding=0, - bias=False, - act_cfg=dict(type='ReLU')) - - self.init_weights(**kwargs) - - def init_weights(self, std=0.01, zeros_init=True): - if self.mode != 'gaussian': - for m in [self.g, self.theta, self.phi]: - normal_init(m.conv, std=std) - else: - normal_init(self.g.conv, std=std) - if zeros_init: - if self.conv_out.norm_cfg is None: - constant_init(self.conv_out.conv, 0) - else: - constant_init(self.conv_out.norm, 0) - else: - if self.conv_out.norm_cfg is None: - normal_init(self.conv_out.conv, std=std) - else: - normal_init(self.conv_out.norm, std=std) - - def gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def embedded_gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - if self.use_scale: - # theta_x.shape[-1] is `self.inter_channels` - pairwise_weight /= theta_x.shape[-1]**0.5 - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def dot_product(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight /= pairwise_weight.shape[-1] - return pairwise_weight - - def concatenation(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - h = theta_x.size(2) - w = phi_x.size(3) - theta_x = theta_x.repeat(1, 1, 1, w) - phi_x = phi_x.repeat(1, 1, h, 1) - - concat_feature = torch.cat([theta_x, phi_x], dim=1) - pairwise_weight = self.concat_project(concat_feature) - n, _, h, w = pairwise_weight.size() - pairwise_weight = pairwise_weight.view(n, h, w) - pairwise_weight /= pairwise_weight.shape[-1] - - return pairwise_weight - - def forward(self, x): - # Assume `reduction = 1`, then `inter_channels = C` - # or `inter_channels = C` when `mode="gaussian"` - - # NonLocal1d x: [N, C, H] - # NonLocal2d x: [N, C, H, W] - # NonLocal3d x: [N, C, T, H, W] - n = x.size(0) - - # NonLocal1d g_x: [N, H, C] - # NonLocal2d g_x: [N, HxW, C] - # NonLocal3d g_x: [N, TxHxW, C] - g_x = self.g(x).view(n, self.inter_channels, -1) - g_x = g_x.permute(0, 2, 1) - - # NonLocal1d theta_x: [N, H, C], phi_x: [N, C, H] - # NonLocal2d theta_x: [N, HxW, C], phi_x: [N, C, HxW] - # NonLocal3d theta_x: [N, TxHxW, C], phi_x: [N, C, TxHxW] - if self.mode == 'gaussian': - theta_x = x.view(n, self.in_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - if self.sub_sample: - phi_x = self.phi(x).view(n, self.in_channels, -1) - else: - phi_x = x.view(n, self.in_channels, -1) - elif self.mode == 'concatenation': - theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) - phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) - else: - theta_x = self.theta(x).view(n, self.inter_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - phi_x = self.phi(x).view(n, self.inter_channels, -1) - - pairwise_func = getattr(self, self.mode) - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = pairwise_func(theta_x, phi_x) - - # NonLocal1d y: [N, H, C] - # NonLocal2d y: [N, HxW, C] - # NonLocal3d y: [N, TxHxW, C] - y = torch.matmul(pairwise_weight, g_x) - # NonLocal1d y: [N, C, H] - # NonLocal2d y: [N, C, H, W] - # NonLocal3d y: [N, C, T, H, W] - y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, - *x.size()[2:]) - - output = x + self.conv_out(y) - - return output - - -class NonLocal1d(_NonLocalNd): - """1D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv1d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv1d'), - **kwargs): - super(NonLocal1d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool1d(kernel_size=2) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -@PLUGIN_LAYERS.register_module() -class NonLocal2d(_NonLocalNd): - """2D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv2d'). - """ - - _abbr_ = 'nonlocal_block' - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv2d'), - **kwargs): - super(NonLocal2d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -class NonLocal3d(_NonLocalNd): - """3D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv3d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv3d'), - **kwargs): - super(NonLocal3d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/norm.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/norm.py deleted file mode 100755 index cfb326bdb..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/norm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import torch.nn as nn - -from mmcv.utils import is_tuple_of -from mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm -from .registry import NORM_LAYERS - -NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) -NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) -NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) -NORM_LAYERS.register_module('GN', module=nn.GroupNorm) -NORM_LAYERS.register_module('LN', module=nn.LayerNorm) -NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) -NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - When we build a norm layer with `build_norm_layer()`, we want to preserve - the norm type in variable names, e.g, self.bn1, self.gn. This method will - infer the abbreviation to map class types to abbreviations. - - Rule 1: If the class has the property "_abbr_", return the property. - Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or - InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and - "in" respectively. - Rule 3: If the class name contains "batch", "group", "layer" or "instance", - the abbreviation of this layer will be "bn", "gn", "ln" and "in" - respectively. - Rule 4: Otherwise, the abbreviation falls back to "norm". - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN - return 'in' - elif issubclass(class_type, _BatchNorm): - return 'bn' - elif issubclass(class_type, nn.GroupNorm): - return 'gn' - elif issubclass(class_type, nn.LayerNorm): - return 'ln' - else: - class_name = class_type.__name__.lower() - if 'batch' in class_name: - return 'bn' - elif 'group' in class_name: - return 'gn' - elif 'layer' in class_name: - return 'ln' - elif 'instance' in class_name: - return 'in' - else: - return 'norm_layer' - - -def build_norm_layer(cfg, num_features, postfix=''): - """Build normalization layer. - - Args: - cfg (dict): The norm layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate a norm layer. - - requires_grad (bool, optional): Whether stop gradient updates. - num_features (int): Number of input channels. - postfix (int | str): The postfix to be appended into norm abbreviation - to create named layer. - - Returns: - (str, nn.Module): The first element is the layer name consisting of - abbreviation and postfix, e.g., bn1, gn. The second element is the - created norm layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in NORM_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - - norm_layer = NORM_LAYERS.get(layer_type) - abbr = infer_abbr(norm_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - requires_grad = cfg_.pop('requires_grad', True) - cfg_.setdefault('eps', 1e-5) - if layer_type != 'GN': - layer = norm_layer(num_features, **cfg_) - if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): - layer._specify_ddp_gpu_num(1) - else: - assert 'num_groups' in cfg_ - layer = norm_layer(num_channels=num_features, **cfg_) - - for param in layer.parameters(): - param.requires_grad = requires_grad - - return name, layer - - -def is_norm(layer, exclude=None): - """Check if a layer is a normalization layer. - - Args: - layer (nn.Module): The layer to be checked. - exclude (type | tuple[type]): Types to be excluded. - - Returns: - bool: Whether the layer is a norm layer. - """ - if exclude is not None: - if not isinstance(exclude, tuple): - exclude = (exclude, ) - if not is_tuple_of(exclude, type): - raise TypeError( - f'"exclude" must be either None or type or a tuple of types, ' - f'but got {type(exclude)}: {exclude}') - - if exclude and isinstance(layer, exclude): - return False - - all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) - return isinstance(layer, all_norm_bases) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/padding.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/padding.py deleted file mode 100755 index e4ac6b28a..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/padding.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import PADDING_LAYERS - -PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) -PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) -PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) - - -def build_padding_layer(cfg, *args, **kwargs): - """Build padding layer. - - Args: - cfg (None or dict): The padding layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate a padding layer. - - Returns: - nn.Module: Created padding layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - - cfg_ = cfg.copy() - padding_type = cfg_.pop('type') - if padding_type not in PADDING_LAYERS: - raise KeyError(f'Unrecognized padding type {padding_type}.') - else: - padding_layer = PADDING_LAYERS.get(padding_type) - - layer = padding_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/plugin.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/plugin.py deleted file mode 100755 index 07c010d40..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/plugin.py +++ /dev/null @@ -1,88 +0,0 @@ -import inspect -import platform - -from .registry import PLUGIN_LAYERS - -if platform.system() == 'Windows': - import regex as re -else: - import re - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - This method will infer the abbreviation to map class types to - abbreviations. - - Rule 1: If the class has the property "abbr", return the property. - Rule 2: Otherwise, the abbreviation falls back to snake case of class - name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - - def camel2snack(word): - """Convert camel case word into snack case. - - Modified from `inflection lib - `_. - - Example:: - - >>> camel2snack("FancyBlock") - 'fancy_block' - """ - - word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) - word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) - word = word.replace('-', '_') - return word.lower() - - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - else: - return camel2snack(class_type.__name__) - - -def build_plugin_layer(cfg, postfix='', **kwargs): - """Build plugin layer. - - Args: - cfg (None or dict): cfg should contain: - type (str): identify plugin layer type. - layer args: args needed to instantiate a plugin layer. - postfix (int, str): appended into norm abbreviation to - create named layer. Default: ''. - - Returns: - tuple[str, nn.Module]: - name (str): abbreviation + postfix - layer (nn.Module): created plugin layer - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in PLUGIN_LAYERS: - raise KeyError(f'Unrecognized plugin type {layer_type}') - - plugin_layer = PLUGIN_LAYERS.get(layer_type) - abbr = infer_abbr(plugin_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - layer = plugin_layer(**kwargs, **cfg_) - - return name, layer diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/registry.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/registry.py deleted file mode 100755 index c29279776..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/registry.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -CONV_LAYERS = Registry('conv layer') -NORM_LAYERS = Registry('norm layer') -ACTIVATION_LAYERS = Registry('activation layer') -PADDING_LAYERS = Registry('padding layer') -UPSAMPLE_LAYERS = Registry('upsample layer') -PLUGIN_LAYERS = Registry('plugin layer') - -DROPOUT_LAYERS = Registry('drop out layers') -POSITIONAL_ENCODING = Registry('position encoding') -ATTENTION = Registry('attention') -FEEDFORWARD_NETWORK = Registry('feed-forward Network') -TRANSFORMER_LAYER = Registry('transformerLayer') -TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/scale.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/scale.py deleted file mode 100755 index c905fffcc..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/scale.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class Scale(nn.Module): - """A learnable scale parameter. - - This layer scales the input by a learnable factor. It multiplies a - learnable scale parameter of shape (1,) with input of any shape. - - Args: - scale (float): Initial value of scale factor. Default: 1.0 - """ - - def __init__(self, scale=1.0): - super(Scale, self).__init__() - self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) - - def forward(self, x): - return x * self.scale diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/swish.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/swish.py deleted file mode 100755 index e2ca8ed7b..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/swish.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class Swish(nn.Module): - """Swish Module. - - This module applies the swish function: - - .. math:: - Swish(x) = x * Sigmoid(x) - - Returns: - Tensor: The output tensor. - """ - - def __init__(self): - super(Swish, self).__init__() - - def forward(self, x): - return x * torch.sigmoid(x) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/transformer.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/transformer.py deleted file mode 100755 index ed32688af..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/transformer.py +++ /dev/null @@ -1,595 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings - -import torch -import torch.nn as nn - -from mmcv import ConfigDict, deprecated_api_warning -from mmcv.cnn import Linear, build_activation_layer, build_norm_layer -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential -from mmcv.utils import build_from_cfg -from .drop import build_dropout -from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, - TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) - -# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file -try: - from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention # noqa F401 - warnings.warn( - ImportWarning( - '``MultiScaleDeformableAttention`` has been moved to ' - '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 - '``from mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 - 'to ``from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 - )) - -except ImportError: - warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' - '``mmcv.ops.multi_scale_deform_attn``, ' - 'You should install ``mmcv-full`` if you need this module. ') - - -def build_positional_encoding(cfg, default_args=None): - """Builder for Position Encoding.""" - return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) - - -def build_attention(cfg, default_args=None): - """Builder for attention.""" - return build_from_cfg(cfg, ATTENTION, default_args) - - -def build_feedforward_network(cfg, default_args=None): - """Builder for feed-forward network (FFN).""" - return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) - - -def build_transformer_layer(cfg, default_args=None): - """Builder for transformer layer.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) - - -def build_transformer_layer_sequence(cfg, default_args=None): - """Builder for transformer encoder and transformer decoder.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) - - -@ATTENTION.register_module() -class MultiheadAttention(BaseModule): - """A wrapper for ``torch.nn.MultiheadAttention``. - - This module implements MultiheadAttention with identity connection, - and positional encoding is also passed as input. - - Args: - embed_dims (int): The embedding dimension. - num_heads (int): Parallel attention heads. - attn_drop (float): A Dropout layer on attn_output_weights. - Default: 0.0. - proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. - Default: 0.0. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): When it is True, Key, Query and Value are shape of - (batch, n, embed_dim), otherwise (n, batch, embed_dim). - Default to False. - """ - - def __init__(self, - embed_dims, - num_heads, - attn_drop=0., - proj_drop=0., - dropout_layer=dict(type='Dropout', drop_prob=0.), - init_cfg=None, - batch_first=False, - **kwargs): - super(MultiheadAttention, self).__init__(init_cfg) - if 'dropout' in kwargs: - warnings.warn('The arguments `dropout` in MultiheadAttention ' - 'has been deprecated, now you can separately ' - 'set `attn_drop`(float), proj_drop(float), ' - 'and `dropout_layer`(dict) ') - attn_drop = kwargs['dropout'] - dropout_layer['drop_prob'] = kwargs.pop('dropout') - - self.embed_dims = embed_dims - self.num_heads = num_heads - self.batch_first = batch_first - - self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, - **kwargs) - - self.proj_drop = nn.Dropout(proj_drop) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else nn.Identity() - - @deprecated_api_warning({'residual': 'identity'}, - cls_name='MultiheadAttention') - def forward(self, - query, - key=None, - value=None, - identity=None, - query_pos=None, - key_pos=None, - attn_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `MultiheadAttention`. - - **kwargs allow passing a more general data flow when combining - with other operations in `transformerlayer`. - - Args: - query (Tensor): The input query with shape [num_queries, bs, - embed_dims] if self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - If None, the ``query`` will be used. Defaults to None. - value (Tensor): The value tensor with same shape as `key`. - Same in `nn.MultiheadAttention.forward`. Defaults to None. - If None, the `key` will be used. - identity (Tensor): This tensor, with the same shape as x, - will be used for the identity link. - If None, `x` will be used. Defaults to None. - query_pos (Tensor): The positional encoding for query, with - the same shape as `x`. If not None, it will - be added to `x` before forward function. Defaults to None. - key_pos (Tensor): The positional encoding for `key`, with the - same shape as `key`. Defaults to None. If not None, it will - be added to `key` before forward function. If None, and - `query_pos` has the same shape as `key`, then `query_pos` - will be used for `key_pos`. Defaults to None. - attn_mask (Tensor): ByteTensor mask with shape [num_queries, - num_keys]. Same in `nn.MultiheadAttention.forward`. - Defaults to None. - key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. - Defaults to None. - - Returns: - Tensor: forwarded results with shape - [num_queries, bs, embed_dims] - if self.batch_first is False, else - [bs, num_queries embed_dims]. - """ - - if key is None: - key = query - if value is None: - value = key - if identity is None: - identity = query - if key_pos is None: - if query_pos is not None: - # use query_pos if key_pos is not available - if query_pos.shape == key.shape: - key_pos = query_pos - else: - warnings.warn(f'position encoding of key is' - f'missing in {self.__class__.__name__}.') - if query_pos is not None: - query = query + query_pos - if key_pos is not None: - key = key + key_pos - - # Because the dataflow('key', 'query', 'value') of - # ``torch.nn.MultiheadAttention`` is (num_query, batch, - # embed_dims), We should adjust the shape of dataflow from - # batch_first (batch, num_query, embed_dims) to num_query_first - # (num_query ,batch, embed_dims), and recover ``attn_output`` - # from num_query_first to batch_first. - if self.batch_first: - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - out = self.attn( - query=query, - key=key, - value=value, - attn_mask=attn_mask, - key_padding_mask=key_padding_mask)[0] - - if self.batch_first: - out = out.transpose(0, 1) - - return identity + self.dropout_layer(self.proj_drop(out)) - - -@FEEDFORWARD_NETWORK.register_module() -class FFN(BaseModule): - """Implements feed-forward networks (FFNs) with identity connection. - - Args: - embed_dims (int): The feature dimension. Same as - `MultiheadAttention`. Defaults: 256. - feedforward_channels (int): The hidden dimension of FFNs. - Defaults: 1024. - num_fcs (int, optional): The number of fully-connected layers in - FFNs. Default: 2. - act_cfg (dict, optional): The activation config for FFNs. - Default: dict(type='ReLU') - ffn_drop (float, optional): Probability of an element to be - zeroed in FFN. Default 0.0. - add_identity (bool, optional): Whether to add the - identity connection. Default: `True`. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - @deprecated_api_warning( - { - 'dropout': 'ffn_drop', - 'add_residual': 'add_identity' - }, - cls_name='FFN') - def __init__(self, - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - act_cfg=dict(type='ReLU', inplace=True), - ffn_drop=0., - dropout_layer=None, - add_identity=True, - init_cfg=None, - **kwargs): - super(FFN, self).__init__(init_cfg) - assert num_fcs >= 2, 'num_fcs should be no less ' \ - f'than 2. got {num_fcs}.' - self.embed_dims = embed_dims - self.feedforward_channels = feedforward_channels - self.num_fcs = num_fcs - self.act_cfg = act_cfg - self.activate = build_activation_layer(act_cfg) - - layers = [] - in_channels = embed_dims - for _ in range(num_fcs - 1): - layers.append( - Sequential( - Linear(in_channels, feedforward_channels), self.activate, - nn.Dropout(ffn_drop))) - in_channels = feedforward_channels - layers.append(Linear(feedforward_channels, embed_dims)) - layers.append(nn.Dropout(ffn_drop)) - self.layers = Sequential(*layers) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else torch.nn.Identity() - self.add_identity = add_identity - - @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') - def forward(self, x, identity=None): - """Forward function for `FFN`. - - The function would add x to the output tensor if residue is None. - """ - out = self.layers(x) - if not self.add_identity: - return self.dropout_layer(out) - if identity is None: - identity = x - return identity + self.dropout_layer(out) - - -@TRANSFORMER_LAYER.register_module() -class BaseTransformerLayer(BaseModule): - """Base `TransformerLayer` for vision transformer. - - It can be built from `mmcv.ConfigDict` and support more flexible - customization, for example, using any number of `FFN or LN ` and - use different kinds of `attention` by specifying a list of `ConfigDict` - named `attn_cfgs`. It is worth mentioning that it supports `prenorm` - when you specifying `norm` as the first element of `operation_order`. - More details about the `prenorm`: `On Layer Normalization in the - Transformer Architecture `_ . - - Args: - attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for `self_attention` or `cross_attention` modules, - The order of the configs in the list should be consistent with - corresponding attentions in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. Default: None. - ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for FFN, The order of the configs in the list should be - consistent with corresponding ffn in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. - operation_order (tuple[str]): The execution order of operation - in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). - Support `prenorm` when you specifying first element as `norm`. - Default:None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): Key, Query and Value are shape - of (batch, n, embed_dim) - or (n, batch, embed_dim). Default to False. - """ - - def __init__(self, - attn_cfgs=None, - ffn_cfgs=dict( - type='FFN', - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - ffn_drop=0., - act_cfg=dict(type='ReLU', inplace=True), - ), - operation_order=None, - norm_cfg=dict(type='LN'), - init_cfg=None, - batch_first=False, - **kwargs): - - deprecated_args = dict( - feedforward_channels='feedforward_channels', - ffn_dropout='ffn_drop', - ffn_num_fcs='num_fcs') - for ori_name, new_name in deprecated_args.items(): - if ori_name in kwargs: - warnings.warn( - f'The arguments `{ori_name}` in BaseTransformerLayer ' - f'has been deprecated, now you should set `{new_name}` ' - f'and other FFN related arguments ' - f'to a dict named `ffn_cfgs`. ') - ffn_cfgs[new_name] = kwargs[ori_name] - - super(BaseTransformerLayer, self).__init__(init_cfg) - - self.batch_first = batch_first - - assert set(operation_order) & set( - ['self_attn', 'norm', 'ffn', 'cross_attn']) == \ - set(operation_order), f'The operation_order of' \ - f' {self.__class__.__name__} should ' \ - f'contains all four operation type ' \ - f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" - - num_attn = operation_order.count('self_attn') + operation_order.count( - 'cross_attn') - if isinstance(attn_cfgs, dict): - attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] - else: - assert num_attn == len(attn_cfgs), f'The length ' \ - f'of attn_cfg {num_attn} is ' \ - f'not consistent with the number of attention' \ - f'in operation_order {operation_order}.' - - self.num_attn = num_attn - self.operation_order = operation_order - self.norm_cfg = norm_cfg - self.pre_norm = operation_order[0] == 'norm' - self.attentions = ModuleList() - - index = 0 - for operation_name in operation_order: - if operation_name in ['self_attn', 'cross_attn']: - if 'batch_first' in attn_cfgs[index]: - assert self.batch_first == attn_cfgs[index]['batch_first'] - else: - attn_cfgs[index]['batch_first'] = self.batch_first - attention = build_attention(attn_cfgs[index]) - # Some custom attentions used as `self_attn` - # or `cross_attn` can have different behavior. - attention.operation_name = operation_name - self.attentions.append(attention) - index += 1 - - self.embed_dims = self.attentions[0].embed_dims - - self.ffns = ModuleList() - num_ffns = operation_order.count('ffn') - if isinstance(ffn_cfgs, dict): - ffn_cfgs = ConfigDict(ffn_cfgs) - if isinstance(ffn_cfgs, dict): - ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] - assert len(ffn_cfgs) == num_ffns - for ffn_index in range(num_ffns): - if 'embed_dims' not in ffn_cfgs[ffn_index]: - ffn_cfgs['embed_dims'] = self.embed_dims - else: - assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims - self.ffns.append( - build_feedforward_network(ffn_cfgs[ffn_index], - dict(type='FFN'))) - - self.norms = ModuleList() - num_norms = operation_order.count('norm') - for _ in range(num_norms): - self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) - - def forward(self, - query, - key=None, - value=None, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerDecoderLayer`. - - **kwargs contains some specific arguments of attentions. - - Args: - query (Tensor): The input query with shape - [num_queries, bs, embed_dims] if - self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - value (Tensor): The value tensor with same shape as `key`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor] | None): 2D Tensor used in - calculation of corresponding attention. The length of - it should equal to the number of `attention` in - `operation_order`. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in `self_attn` layer. - Defaults to None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: forwarded results with shape [num_queries, bs, embed_dims]. - """ - - norm_index = 0 - attn_index = 0 - ffn_index = 0 - identity = query - if attn_masks is None: - attn_masks = [None for _ in range(self.num_attn)] - elif isinstance(attn_masks, torch.Tensor): - attn_masks = [ - copy.deepcopy(attn_masks) for _ in range(self.num_attn) - ] - warnings.warn(f'Use same attn_mask in all attentions in ' - f'{self.__class__.__name__} ') - else: - assert len(attn_masks) == self.num_attn, f'The length of ' \ - f'attn_masks {len(attn_masks)} must be equal ' \ - f'to the number of attention in ' \ - f'operation_order {self.num_attn}' - - for layer in self.operation_order: - if layer == 'self_attn': - temp_key = temp_value = query - query = self.attentions[attn_index]( - query, - temp_key, - temp_value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=query_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=query_key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'norm': - query = self.norms[norm_index](query) - norm_index += 1 - - elif layer == 'cross_attn': - query = self.attentions[attn_index]( - query, - key, - value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=key_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'ffn': - query = self.ffns[ffn_index]( - query, identity if self.pre_norm else None) - ffn_index += 1 - - return query - - -@TRANSFORMER_LAYER_SEQUENCE.register_module() -class TransformerLayerSequence(BaseModule): - """Base class for TransformerEncoder and TransformerDecoder in vision - transformer. - - As base-class of Encoder and Decoder in vision transformer. - Support customization such as specifying different kind - of `transformer_layer` in `transformer_coder`. - - Args: - transformerlayer (list[obj:`mmcv.ConfigDict`] | - obj:`mmcv.ConfigDict`): Config of transformerlayer - in TransformerCoder. If it is obj:`mmcv.ConfigDict`, - it would be repeated `num_layer` times to a - list[`mmcv.ConfigDict`]. Default: None. - num_layers (int): The number of `TransformerLayer`. Default: None. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): - super(TransformerLayerSequence, self).__init__(init_cfg) - if isinstance(transformerlayers, dict): - transformerlayers = [ - copy.deepcopy(transformerlayers) for _ in range(num_layers) - ] - else: - assert isinstance(transformerlayers, list) and \ - len(transformerlayers) == num_layers - self.num_layers = num_layers - self.layers = ModuleList() - for i in range(num_layers): - self.layers.append(build_transformer_layer(transformerlayers[i])) - self.embed_dims = self.layers[0].embed_dims - self.pre_norm = self.layers[0].pre_norm - - def forward(self, - query, - key, - value, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerCoder`. - - Args: - query (Tensor): Input query with shape - `(num_queries, bs, embed_dims)`. - key (Tensor): The key tensor with shape - `(num_keys, bs, embed_dims)`. - value (Tensor): The value tensor with shape - `(num_keys, bs, embed_dims)`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor], optional): Each element is 2D Tensor - which is used in calculation of corresponding attention in - operation_order. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in self-attention - Default: None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: results with shape [num_queries, bs, embed_dims]. - """ - for layer in self.layers: - query = layer( - query, - key, - value, - query_pos=query_pos, - key_pos=key_pos, - attn_masks=attn_masks, - query_key_padding_mask=query_key_padding_mask, - key_padding_mask=key_padding_mask, - **kwargs) - return query diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/upsample.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/upsample.py deleted file mode 100755 index a1a353767..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/upsample.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import xavier_init -from .registry import UPSAMPLE_LAYERS - -UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) -UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) - - -@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') -class PixelShufflePack(nn.Module): - """Pixel Shuffle upsample layer. - - This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to - achieve a simple upsampling with pixel shuffle. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of the conv layer to expand the - channels. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super(PixelShufflePack, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - xavier_init(self.upsample_conv, distribution='uniform') - - def forward(self, x): - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x - - -def build_upsample_layer(cfg, *args, **kwargs): - """Build upsample layer. - - Args: - cfg (dict): The upsample layer config, which should contain: - - - type (str): Layer type. - - scale_factor (int): Upsample ratio, which is not applicable to - deconv. - - layer args: Args needed to instantiate a upsample layer. - args (argument list): Arguments passed to the ``__init__`` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the - ``__init__`` method of the corresponding conv layer. - - Returns: - nn.Module: Created upsample layer. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - raise KeyError( - f'the cfg dict must contain the key "type", but got {cfg}') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in UPSAMPLE_LAYERS: - raise KeyError(f'Unrecognized upsample type {layer_type}') - else: - upsample = UPSAMPLE_LAYERS.get(layer_type) - - if upsample is nn.Upsample: - cfg_['mode'] = layer_type - layer = upsample(*args, **kwargs, **cfg_) - return layer diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/wrappers.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/wrappers.py deleted file mode 100755 index 8aebf67bf..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/bricks/wrappers.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 - -Wrap some nn modules to support empty tensor input. Currently, these wrappers -are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask -heads are trained on only positive RoIs. -""" -import math - -import torch -import torch.nn as nn -from torch.nn.modules.utils import _pair, _triple - -from .registry import CONV_LAYERS, UPSAMPLE_LAYERS - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def obsolete_torch_version(torch_version, version_threshold): - return torch_version == 'parrots' or torch_version <= version_threshold - - -class NewEmptyTensorOp(torch.autograd.Function): - - @staticmethod - def forward(ctx, x, new_shape): - ctx.shape = x.shape - return x.new_empty(new_shape) - - @staticmethod - def backward(ctx, grad): - shape = ctx.shape - return NewEmptyTensorOp.apply(grad, shape), None - - -@CONV_LAYERS.register_module('Conv', force=True) -class Conv2d(nn.Conv2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module('Conv3d', force=True) -class Conv3d(nn.Conv3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv') -@UPSAMPLE_LAYERS.register_module('deconv', force=True) -class ConvTranspose2d(nn.ConvTranspose2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv3d') -@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) -class ConvTranspose3d(nn.ConvTranspose3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -class MaxPool2d(nn.MaxPool2d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), - _pair(self.padding), _pair(self.stride), - _pair(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class MaxPool3d(nn.MaxPool3d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), - _triple(self.padding), - _triple(self.stride), - _triple(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class Linear(torch.nn.Linear): - - def forward(self, x): - # empty tensor forward of Linear layer is supported in Pytorch 1.6 - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): - out_shape = [x.shape[0], self.out_features] - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/builder.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/builder.py deleted file mode 100755 index 7567316c5..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/builder.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..runner import Sequential -from ..utils import Registry, build_from_cfg - - -def build_model_from_cfg(cfg, registry, default_args=None): - """Build a PyTorch model from config dict(s). Different from - ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. - - Args: - cfg (dict, list[dict]): The config of modules, is is either a config - dict or a list of config dicts. If cfg is a list, a - the built modules will be wrapped with ``nn.Sequential``. - registry (:obj:`Registry`): A registry the module belongs to. - default_args (dict, optional): Default arguments to build the module. - Defaults to None. - - Returns: - nn.Module: A built nn module. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return Sequential(*modules) - else: - return build_from_cfg(cfg, registry, default_args) - - -MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/resnet.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/resnet.py deleted file mode 100755 index 1cb3ac057..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/resnet.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn -import torch.utils.checkpoint as cp - -from .utils import constant_init, kaiming_init - - -def conv3x3(in_planes, out_planes, stride=1, dilation=1): - """3x3 convolution with padding.""" - return nn.Conv2d( - in_planes, - out_planes, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False): - super(BasicBlock, self).__init__() - assert style in ['pytorch', 'caffe'] - self.conv1 = conv3x3(inplanes, planes, stride, dilation) - self.bn1 = nn.BatchNorm2d(planes) - self.relu = nn.ReLU(inplace=True) - self.conv2 = conv3x3(planes, planes) - self.bn2 = nn.BatchNorm2d(planes) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - assert not with_cp - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Bottleneck(nn.Module): - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False): - """Bottleneck block. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if - it is "caffe", the stride-two layer is the first 1x1 conv layer. - """ - super(Bottleneck, self).__init__() - assert style in ['pytorch', 'caffe'] - if style == 'pytorch': - conv1_stride = 1 - conv2_stride = stride - else: - conv1_stride = stride - conv2_stride = 1 - self.conv1 = nn.Conv2d( - inplanes, planes, kernel_size=1, stride=conv1_stride, bias=False) - self.conv2 = nn.Conv2d( - planes, - planes, - kernel_size=3, - stride=conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.bn1 = nn.BatchNorm2d(planes) - self.bn2 = nn.BatchNorm2d(planes) - self.conv3 = nn.Conv2d( - planes, planes * self.expansion, kernel_size=1, bias=False) - self.bn3 = nn.BatchNorm2d(planes * self.expansion) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - def forward(self, x): - - def _inner_forward(x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -def make_res_layer(block, - inplanes, - planes, - blocks, - stride=1, - dilation=1, - style='pytorch', - with_cp=False): - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = nn.Sequential( - nn.Conv2d( - inplanes, - planes * block.expansion, - kernel_size=1, - stride=stride, - bias=False), - nn.BatchNorm2d(planes * block.expansion), - ) - - layers = [] - layers.append( - block( - inplanes, - planes, - stride, - dilation, - downsample, - style=style, - with_cp=with_cp)) - inplanes = planes * block.expansion - for _ in range(1, blocks): - layers.append( - block(inplanes, planes, 1, dilation, style=style, with_cp=with_cp)) - - return nn.Sequential(*layers) - - -class ResNet(nn.Module): - """ResNet backbone. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - num_stages (int): Resnet stages, normally 4. - strides (Sequence[int]): Strides of the first block of each stage. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze - running stats (mean and var). - bn_frozen (bool): Whether to freeze weight and bias of BN layers. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - frozen_stages=-1, - bn_eval=True, - bn_frozen=False, - with_cp=False): - super(ResNet, self).__init__() - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - assert num_stages >= 1 and num_stages <= 4 - block, stage_blocks = self.arch_settings[depth] - stage_blocks = stage_blocks[:num_stages] - assert len(strides) == len(dilations) == num_stages - assert max(out_indices) < num_stages - - self.out_indices = out_indices - self.style = style - self.frozen_stages = frozen_stages - self.bn_eval = bn_eval - self.bn_frozen = bn_frozen - self.with_cp = with_cp - - self.inplanes = 64 - self.conv1 = nn.Conv2d( - 3, 64, kernel_size=7, stride=2, padding=3, bias=False) - self.bn1 = nn.BatchNorm2d(64) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - self.res_layers = [] - for i, num_blocks in enumerate(stage_blocks): - stride = strides[i] - dilation = dilations[i] - planes = 64 * 2**i - res_layer = make_res_layer( - block, - self.inplanes, - planes, - num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - with_cp=with_cp) - self.inplanes = planes * block.expansion - layer_name = f'layer{i + 1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self.feat_dim = block.expansion * 64 * 2**(len(stage_blocks) - 1) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m) - elif isinstance(m, nn.BatchNorm2d): - constant_init(m, 1) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - if len(outs) == 1: - return outs[0] - else: - return tuple(outs) - - def train(self, mode=True): - super(ResNet, self).train(mode) - if self.bn_eval: - for m in self.modules(): - if isinstance(m, nn.BatchNorm2d): - m.eval() - if self.bn_frozen: - for params in m.parameters(): - params.requires_grad = False - if mode and self.frozen_stages >= 0: - for param in self.conv1.parameters(): - param.requires_grad = False - for param in self.bn1.parameters(): - param.requires_grad = False - self.bn1.eval() - self.bn1.weight.requires_grad = False - self.bn1.bias.requires_grad = False - for i in range(1, self.frozen_stages + 1): - mod = getattr(self, f'layer{i}') - mod.eval() - for param in mod.parameters(): - param.requires_grad = False diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/__init__.py deleted file mode 100755 index a263e31c1..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .flops_counter import get_model_complexity_info -from .fuse_conv_bn import fuse_conv_bn -from .sync_bn import revert_sync_batchnorm -from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, - KaimingInit, NormalInit, PretrainedInit, - TruncNormalInit, UniformInit, XavierInit, - bias_init_with_prob, caffe2_xavier_init, - constant_init, initialize, kaiming_init, normal_init, - trunc_normal_init, uniform_init, xavier_init) - -__all__ = [ - 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', - 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', - 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'revert_sync_batchnorm' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/flops_counter.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/flops_counter.py deleted file mode 100755 index dceeb398b..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/flops_counter.py +++ /dev/null @@ -1,599 +0,0 @@ -# Modified from flops-counter.pytorch by Vladislav Sovrasov -# original repo: https://github.com/sovrasov/flops-counter.pytorch - -# MIT License - -# Copyright (c) 2018 Vladislav Sovrasov - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -from functools import partial - -import numpy as np -import torch -import torch.nn as nn - -import mmcv - - -def get_model_complexity_info(model, - input_shape, - print_per_layer_stat=True, - as_strings=True, - input_constructor=None, - flush=False, - ost=sys.stdout): - """Get complexity information of a model. - - This method can calculate FLOPs and parameter counts of a model with - corresponding input shape. It can also print complexity information for - each layer in a model. - - Supported layers are listed as below: - - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. - - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, ``nn.LeakyReLU``, - ``nn.ReLU6``. - - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, - ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, - ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, - ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, - ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. - - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, - ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, - ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. - - Linear: ``nn.Linear``. - - Deconvolution: ``nn.ConvTranspose2d``. - - Upsample: ``nn.Upsample``. - - Args: - model (nn.Module): The model for complexity calculation. - input_shape (tuple): Input shape used for calculation. - print_per_layer_stat (bool): Whether to print complexity information - for each layer in a model. Default: True. - as_strings (bool): Output FLOPs and params counts in a string form. - Default: True. - input_constructor (None | callable): If specified, it takes a callable - method that generates input. otherwise, it will generate a random - tensor with input shape to calculate FLOPs. Default: None. - flush (bool): same as that in :func:`print`. Default: False. - ost (stream): same as ``file`` param in :func:`print`. - Default: sys.stdout. - - Returns: - tuple[float | str]: If ``as_strings`` is set to True, it will return - FLOPs and parameter counts in a string format. otherwise, it will - return those in a float number format. - """ - assert type(input_shape) is tuple - assert len(input_shape) >= 1 - assert isinstance(model, nn.Module) - flops_model = add_flops_counting_methods(model) - flops_model.eval() - flops_model.start_flops_count() - if input_constructor: - input = input_constructor(input_shape) - _ = flops_model(**input) - else: - try: - batch = torch.ones(()).new_empty( - (1, *input_shape), - dtype=next(flops_model.parameters()).dtype, - device=next(flops_model.parameters()).device) - except StopIteration: - # Avoid StopIteration for models which have no parameters, - # like `nn.Relu()`, `nn.AvgPool2d`, etc. - batch = torch.ones(()).new_empty((1, *input_shape)) - - _ = flops_model(batch) - - flops_count, params_count = flops_model.compute_average_flops_cost() - if print_per_layer_stat: - print_model_with_flops( - flops_model, flops_count, params_count, ost=ost, flush=flush) - flops_model.stop_flops_count() - - if as_strings: - return flops_to_string(flops_count), params_to_string(params_count) - - return flops_count, params_count - - -def flops_to_string(flops, units='GFLOPs', precision=2): - """Convert FLOPs number into a string. - - Note that Here we take a multiply-add counts as one FLOP. - - Args: - flops (float): FLOPs number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', - 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically - choose the most suitable unit for FLOPs. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted FLOPs number with units. - - Examples: - >>> flops_to_string(1e9) - '1.0 GFLOPs' - >>> flops_to_string(2e5, 'MFLOPs') - '0.2 MFLOPs' - >>> flops_to_string(3e-9, None) - '3e-09 FLOPs' - """ - if units is None: - if flops // 10**9 > 0: - return str(round(flops / 10.**9, precision)) + ' GFLOPs' - elif flops // 10**6 > 0: - return str(round(flops / 10.**6, precision)) + ' MFLOPs' - elif flops // 10**3 > 0: - return str(round(flops / 10.**3, precision)) + ' KFLOPs' - else: - return str(flops) + ' FLOPs' - else: - if units == 'GFLOPs': - return str(round(flops / 10.**9, precision)) + ' ' + units - elif units == 'MFLOPs': - return str(round(flops / 10.**6, precision)) + ' ' + units - elif units == 'KFLOPs': - return str(round(flops / 10.**3, precision)) + ' ' + units - else: - return str(flops) + ' FLOPs' - - -def params_to_string(num_params, units=None, precision=2): - """Convert parameter number into a string. - - Args: - num_params (float): Parameter number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'M', - 'K' and ''. If set to None, it will automatically choose the most - suitable unit for Parameter number. Default: None. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted parameter number with units. - - Examples: - >>> params_to_string(1e9) - '1000.0 M' - >>> params_to_string(2e5) - '200.0 k' - >>> params_to_string(3e-9) - '3e-09' - """ - if units is None: - if num_params // 10**6 > 0: - return str(round(num_params / 10**6, precision)) + ' M' - elif num_params // 10**3: - return str(round(num_params / 10**3, precision)) + ' k' - else: - return str(num_params) - else: - if units == 'M': - return str(round(num_params / 10.**6, precision)) + ' ' + units - elif units == 'K': - return str(round(num_params / 10.**3, precision)) + ' ' + units - else: - return str(num_params) - - -def print_model_with_flops(model, - total_flops, - total_params, - units='GFLOPs', - precision=3, - ost=sys.stdout, - flush=False): - """Print a model with FLOPs for each layer. - - Args: - model (nn.Module): The model to be printed. - total_flops (float): Total FLOPs of the model. - total_params (float): Total parameter counts of the model. - units (str | None): Converted FLOPs units. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 3. - ost (stream): same as `file` param in :func:`print`. - Default: sys.stdout. - flush (bool): same as that in :func:`print`. Default: False. - - Example: - >>> class ExampleModel(nn.Module): - - >>> def __init__(self): - >>> super().__init__() - >>> self.conv1 = nn.Conv2d(3, 8, 3) - >>> self.conv2 = nn.Conv2d(8, 256, 3) - >>> self.conv3 = nn.Conv2d(256, 8, 3) - >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) - >>> self.flatten = nn.Flatten() - >>> self.fc = nn.Linear(8, 1) - - >>> def forward(self, x): - >>> x = self.conv1(x) - >>> x = self.conv2(x) - >>> x = self.conv3(x) - >>> x = self.avg_pool(x) - >>> x = self.flatten(x) - >>> x = self.fc(x) - >>> return x - - >>> model = ExampleModel() - >>> x = (3, 16, 16) - to print the complexity information state for each layer, you can use - >>> get_model_complexity_info(model, x) - or directly use - >>> print_model_with_flops(model, 4579784.0, 37361) - ExampleModel( - 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, - (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 - (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) - (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) - (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) - (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) - (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) - ) - """ - - def accumulate_params(self): - if is_supported_instance(self): - return self.__params__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_params() - return sum - - def accumulate_flops(self): - if is_supported_instance(self): - return self.__flops__ / model.__batch_counter__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_flops() - return sum - - def flops_repr(self): - accumulated_num_params = self.accumulate_params() - accumulated_flops_cost = self.accumulate_flops() - return ', '.join([ - params_to_string( - accumulated_num_params, units='M', precision=precision), - '{:.3%} Params'.format(accumulated_num_params / total_params), - flops_to_string( - accumulated_flops_cost, units=units, precision=precision), - '{:.3%} FLOPs'.format(accumulated_flops_cost / total_flops), - self.original_extra_repr() - ]) - - def add_extra_repr(m): - m.accumulate_flops = accumulate_flops.__get__(m) - m.accumulate_params = accumulate_params.__get__(m) - flops_extra_repr = flops_repr.__get__(m) - if m.extra_repr != flops_extra_repr: - m.original_extra_repr = m.extra_repr - m.extra_repr = flops_extra_repr - assert m.extra_repr != m.original_extra_repr - - def del_extra_repr(m): - if hasattr(m, 'original_extra_repr'): - m.extra_repr = m.original_extra_repr - del m.original_extra_repr - if hasattr(m, 'accumulate_flops'): - del m.accumulate_flops - - model.apply(add_extra_repr) - print(model, file=ost, flush=flush) - model.apply(del_extra_repr) - - -def get_model_parameters_number(model): - """Calculate parameter number of a model. - - Args: - model (nn.module): The model for parameter number calculation. - - Returns: - float: Parameter number of the model. - """ - num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - return num_params - - -def add_flops_counting_methods(net_main_module): - # adding additional methods to the existing module object, - # this is done this way so that each function has access to self object - net_main_module.start_flops_count = start_flops_count.__get__( - net_main_module) - net_main_module.stop_flops_count = stop_flops_count.__get__( - net_main_module) - net_main_module.reset_flops_count = reset_flops_count.__get__( - net_main_module) - net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # noqa: E501 - net_main_module) - - net_main_module.reset_flops_count() - - return net_main_module - - -def compute_average_flops_cost(self): - """Compute average FLOPs cost. - - A method to compute average FLOPs cost, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - - Returns: - float: Current mean flops consumption per image. - """ - batches_count = self.__batch_counter__ - flops_sum = 0 - for module in self.modules(): - if is_supported_instance(module): - flops_sum += module.__flops__ - params_sum = get_model_parameters_number(self) - return flops_sum / batches_count, params_sum - - -def start_flops_count(self): - """Activate the computation of mean flops consumption per image. - - A method to activate the computation of mean flops consumption per image. - which will be available after ``add_flops_counting_methods()`` is called on - a desired net object. It should be called before running the network. - """ - add_batch_counter_hook_function(self) - - def add_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - return - - else: - handle = module.register_forward_hook( - get_modules_mapping()[type(module)]) - - module.__flops_handle__ = handle - - self.apply(partial(add_flops_counter_hook_function)) - - -def stop_flops_count(self): - """Stop computing the mean flops consumption per image. - - A method to stop computing the mean flops consumption per image, which will - be available after ``add_flops_counting_methods()`` is called on a desired - net object. It can be called to pause the computation whenever. - """ - remove_batch_counter_hook_function(self) - self.apply(remove_flops_counter_hook_function) - - -def reset_flops_count(self): - """Reset statistics computed so far. - - A method to Reset computed statistics, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - """ - add_batch_counter_variables_or_reset(self) - self.apply(add_flops_counter_variable_or_reset) - - -# ---- Internal functions -def empty_flops_counter_hook(module, input, output): - module.__flops__ += 0 - - -def upsample_flops_counter_hook(module, input, output): - output_size = output[0] - batch_size = output_size.shape[0] - output_elements_count = batch_size - for val in output_size.shape[1:]: - output_elements_count *= val - module.__flops__ += int(output_elements_count) - - -def relu_flops_counter_hook(module, input, output): - active_elements_count = output.numel() - module.__flops__ += int(active_elements_count) - - -def linear_flops_counter_hook(module, input, output): - input = input[0] - output_last_dim = output.shape[ - -1] # pytorch checks dimensions, so here we don't care much - module.__flops__ += int(np.prod(input.shape) * output_last_dim) - - -def pool_flops_counter_hook(module, input, output): - input = input[0] - module.__flops__ += int(np.prod(input.shape)) - - -def norm_flops_counter_hook(module, input, output): - input = input[0] - - batch_flops = np.prod(input.shape) - if (getattr(module, 'affine', False) - or getattr(module, 'elementwise_affine', False)): - batch_flops *= 2 - module.__flops__ += int(batch_flops) - - -def deconv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - input_height, input_width = input.shape[2:] - - kernel_height, kernel_width = conv_module.kernel_size - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = ( - kernel_height * kernel_width * in_channels * filters_per_channel) - - active_elements_count = batch_size * input_height * input_width - overall_conv_flops = conv_per_position_flops * active_elements_count - bias_flops = 0 - if conv_module.bias is not None: - output_height, output_width = output.shape[2:] - bias_flops = out_channels * batch_size * output_height * output_height - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def conv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - output_dims = list(output.shape[2:]) - - kernel_dims = list(conv_module.kernel_size) - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = int( - np.prod(kernel_dims)) * in_channels * filters_per_channel - - active_elements_count = batch_size * int(np.prod(output_dims)) - - overall_conv_flops = conv_per_position_flops * active_elements_count - - bias_flops = 0 - - if conv_module.bias is not None: - - bias_flops = out_channels * active_elements_count - - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def batch_counter_hook(module, input, output): - batch_size = 1 - if len(input) > 0: - # Can have multiple inputs, getting the first one - input = input[0] - batch_size = len(input) - else: - pass - print('Warning! No positional inputs found for a module, ' - 'assuming batch size is 1.') - module.__batch_counter__ += batch_size - - -def add_batch_counter_variables_or_reset(module): - - module.__batch_counter__ = 0 - - -def add_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - return - - handle = module.register_forward_hook(batch_counter_hook) - module.__batch_counter_handle__ = handle - - -def remove_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - module.__batch_counter_handle__.remove() - del module.__batch_counter_handle__ - - -def add_flops_counter_variable_or_reset(module): - if is_supported_instance(module): - if hasattr(module, '__flops__') or hasattr(module, '__params__'): - print('Warning: variables __flops__ or __params__ are already ' - 'defined for the module' + type(module).__name__ + - ' ptflops can affect your code!') - module.__flops__ = 0 - module.__params__ = get_model_parameters_number(module) - - -def is_supported_instance(module): - if type(module) in get_modules_mapping(): - return True - return False - - -def remove_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - module.__flops_handle__.remove() - del module.__flops_handle__ - - -def get_modules_mapping(): - return { - # convolutions - nn.Conv1d: conv_flops_counter_hook, - nn.Conv2d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, - nn.Conv3d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, - # activations - nn.ReLU: relu_flops_counter_hook, - nn.PReLU: relu_flops_counter_hook, - nn.ELU: relu_flops_counter_hook, - nn.LeakyReLU: relu_flops_counter_hook, - nn.ReLU6: relu_flops_counter_hook, - # poolings - nn.MaxPool1d: pool_flops_counter_hook, - nn.AvgPool1d: pool_flops_counter_hook, - nn.AvgPool2d: pool_flops_counter_hook, - nn.MaxPool2d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, - nn.MaxPool3d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, - nn.AvgPool3d: pool_flops_counter_hook, - nn.AdaptiveMaxPool1d: pool_flops_counter_hook, - nn.AdaptiveAvgPool1d: pool_flops_counter_hook, - nn.AdaptiveMaxPool2d: pool_flops_counter_hook, - nn.AdaptiveAvgPool2d: pool_flops_counter_hook, - nn.AdaptiveMaxPool3d: pool_flops_counter_hook, - nn.AdaptiveAvgPool3d: pool_flops_counter_hook, - # normalizations - nn.BatchNorm1d: norm_flops_counter_hook, - nn.BatchNorm2d: norm_flops_counter_hook, - nn.BatchNorm3d: norm_flops_counter_hook, - nn.GroupNorm: norm_flops_counter_hook, - nn.InstanceNorm1d: norm_flops_counter_hook, - nn.InstanceNorm2d: norm_flops_counter_hook, - nn.InstanceNorm3d: norm_flops_counter_hook, - nn.LayerNorm: norm_flops_counter_hook, - # FC - nn.Linear: linear_flops_counter_hook, - mmcv.cnn.bricks.Linear: linear_flops_counter_hook, - # Upscale - nn.Upsample: upsample_flops_counter_hook, - # Deconvolution - nn.ConvTranspose2d: deconv_flops_counter_hook, - mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, - } diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/fuse_conv_bn.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/fuse_conv_bn.py deleted file mode 100755 index cb7076f80..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/fuse_conv_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def _fuse_conv_bn(conv, bn): - """Fuse conv and bn into one module. - - Args: - conv (nn.Module): Conv to be fused. - bn (nn.Module): BN to be fused. - - Returns: - nn.Module: Fused module. - """ - conv_w = conv.weight - conv_b = conv.bias if conv.bias is not None else torch.zeros_like( - bn.running_mean) - - factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) - conv.weight = nn.Parameter(conv_w * - factor.reshape([conv.out_channels, 1, 1, 1])) - conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) - return conv - - -def fuse_conv_bn(module): - """Recursively fuse conv and bn in a module. - - During inference, the functionary of batch norm layers is turned off - but only the mean and var alone channels are used, which exposes the - chance to fuse it with the preceding conv layers to save computations and - simplify network structures. - - Args: - module (nn.Module): Module to be fused. - - Returns: - nn.Module: Fused module. - """ - last_conv = None - last_conv_name = None - - for name, child in module.named_children(): - if isinstance(child, - (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): - if last_conv is None: # only fuse BN that is after Conv - continue - fused_conv = _fuse_conv_bn(last_conv, child) - module._modules[last_conv_name] = fused_conv - # To reduce changes, set BN as Identity instead of deleting it. - module._modules[name] = nn.Identity() - last_conv = None - elif isinstance(child, nn.Conv2d): - last_conv = child - last_conv_name = name - else: - fuse_conv_bn(child) - return module diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/sync_bn.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/sync_bn.py deleted file mode 100755 index 8a79ff4a4..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/sync_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -import torch - -import mmcv - - -class _BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): - """A general BatchNorm layer without input dimension check. - - Reproduced from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc - is `_check_input_dim` that is designed for tensor sanity checks. - The check has been bypassed in this class for the convenience of converting - SyncBatchNorm. - """ - - def _check_input_dim(self, input): - return - - -def revert_sync_batchnorm(module): - """Helper function to convert all `SyncBatchNorm` (SyncBN) and - `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to - `BatchNormXd` layers. - - Adapted from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - - Args: - module (nn.Module): The module containing `SyncBatchNorm` layers. - - Returns: - module_output: The converted module with `BatchNormXd` layers. - """ - module_output = module - module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] - if hasattr(mmcv, 'ops'): - module_checklist.append(mmcv.ops.SyncBatchNorm) - if isinstance(module, tuple(module_checklist)): - module_output = _BatchNormXd(module.num_features, module.eps, - module.momentum, module.affine, - module.track_running_stats) - if module.affine: - # no_grad() may not be needed here but - # just to be consistent with `convert_sync_batchnorm()` - with torch.no_grad(): - module_output.weight = module.weight - module_output.bias = module.bias - module_output.running_mean = module.running_mean - module_output.running_var = module.running_var - module_output.num_batches_tracked = module.num_batches_tracked - module_output.training = module.training - # qconfig exists in quantized models - if hasattr(module, 'qconfig'): - module_output.qconfig = module.qconfig - for name, child in module.named_children(): - module_output.add_module(name, revert_sync_batchnorm(child)) - del module - return module_output diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/weight_init.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/weight_init.py deleted file mode 100755 index e1ac999e2..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/utils/weight_init.py +++ /dev/null @@ -1,684 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings - -import numpy as np -import torch -import torch.nn as nn -from torch import Tensor - -from mmcv.utils import Registry, build_from_cfg, get_logger, print_log - -INITIALIZERS = Registry('initializer') - - -def update_init_info(module, init_info): - """Update the `_params_init_info` in the module if the value of parameters - are changed. - - Args: - module (obj:`nn.Module`): The module of PyTorch with a user-defined - attribute `_params_init_info` which records the initialization - information. - init_info (str): The string that describes the initialization. - """ - assert hasattr( - module, - '_params_init_info'), f'Can not find `_params_init_info` in {module}' - for name, param in module.named_parameters(): - - assert param in module._params_init_info, ( - f'Find a new :obj:`Parameter` ' - f'named `{name}` during executing the ' - f'`init_weights` of ' - f'`{module.__class__.__name__}`. ' - f'Please do not add or ' - f'replace parameters during executing ' - f'the `init_weights`. ') - - # The parameter has been changed during executing the - # `init_weights` of module - mean_value = param.data.mean() - if module._params_init_info[param]['tmp_mean_value'] != mean_value: - module._params_init_info[param]['init_info'] = init_info - module._params_init_info[param]['tmp_mean_value'] = mean_value - - -def constant_init(module, val, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.constant_(module.weight, val) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def xavier_init(module, gain=1, bias=0, distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.xavier_uniform_(module.weight, gain=gain) - else: - nn.init.xavier_normal_(module.weight, gain=gain) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def normal_init(module, mean=0, std=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.normal_(module.weight, mean, std) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def trunc_normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - trunc_normal_(module.weight, mean, std, a, b) # type: ignore - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) # type: ignore - - -def uniform_init(module, a=0, b=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.uniform_(module.weight, a, b) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def kaiming_init(module, - a=0, - mode='fan_out', - nonlinearity='relu', - bias=0, - distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.kaiming_uniform_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - else: - nn.init.kaiming_normal_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def caffe2_xavier_init(module, bias=0): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - kaiming_init( - module, - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - bias=bias, - distribution='uniform') - - -def bias_init_with_prob(prior_prob): - """initialize conv/fc bias value according to a given probability value.""" - bias_init = float(-np.log((1 - prior_prob) / prior_prob)) - return bias_init - - -def _get_bases_name(m): - return [b.__name__ for b in m.__class__.__bases__] - - -class BaseInit(object): - - def __init__(self, *, bias=0, bias_prob=None, layer=None): - self.wholemodule = False - if not isinstance(bias, (int, float)): - raise TypeError(f'bias must be a number, but got a {type(bias)}') - - if bias_prob is not None: - if not isinstance(bias_prob, float): - raise TypeError(f'bias_prob type must be float, \ - but got {type(bias_prob)}') - - if layer is not None: - if not isinstance(layer, (str, list)): - raise TypeError(f'layer must be a str or a list of str, \ - but got a {type(layer)}') - else: - layer = [] - - if bias_prob is not None: - self.bias = bias_init_with_prob(bias_prob) - else: - self.bias = bias - self.layer = [layer] if isinstance(layer, str) else layer - - def _get_init_info(self): - info = f'{self.__class__.__name__}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Constant') -class ConstantInit(BaseInit): - """Initialize module parameters with constant values. - - Args: - val (int | float): the value to fill the weights in the module with - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, val, **kwargs): - super().__init__(**kwargs) - self.val = val - - def __call__(self, module): - - def init(m): - if self.wholemodule: - constant_init(m, self.val, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - constant_init(m, self.val, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Xavier') -class XavierInit(BaseInit): - r"""Initialize module parameters with values according to the method - described in `Understanding the difficulty of training deep feedforward - neural networks - Glorot, X. & Bengio, Y. (2010). - `_ - - Args: - gain (int | float): an optional scaling factor. Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` - or ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, gain=1, distribution='normal', **kwargs): - super().__init__(**kwargs) - self.gain = gain - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - xavier_init(m, self.gain, self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - xavier_init(m, self.gain, self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: gain={self.gain}, ' \ - f'distribution={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Normal') -class NormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. - - Args: - mean (int | float):the mean of the normal distribution. Defaults to 0. - std (int | float): the standard deviation of the normal distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, mean=0, std=1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.std = std - - def __call__(self, module): - - def init(m): - if self.wholemodule: - normal_init(m, self.mean, self.std, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - normal_init(m, self.mean, self.std, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: mean={self.mean},' \ - f' std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='TruncNormal') -class TruncNormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values - outside :math:`[a, b]`. - - Args: - mean (float): the mean of the normal distribution. Defaults to 0. - std (float): the standard deviation of the normal distribution. - Defaults to 1. - a (float): The minimum cutoff value. - b ( float): The maximum cutoff value. - bias (float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - **kwargs) -> None: - super().__init__(**kwargs) - self.mean = mean - self.std = std - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ - f' mean={self.mean}, std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Uniform') -class UniformInit(BaseInit): - r"""Initialize module parameters with values drawn from the uniform - distribution :math:`\mathcal{U}(a, b)`. - - Args: - a (int | float): the lower bound of the uniform distribution. - Defaults to 0. - b (int | float): the upper bound of the uniform distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, a=0, b=1, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def __call__(self, module): - - def init(m): - if self.wholemodule: - uniform_init(m, self.a, self.b, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - uniform_init(m, self.a, self.b, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a},' \ - f' b={self.b}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Kaiming') -class KaimingInit(BaseInit): - r"""Initialize module parameters with the values according to the method - described in `Delving deep into rectifiers: Surpassing human-level - performance on ImageNet classification - He, K. et al. (2015). - `_ - - Args: - a (int | float): the negative slope of the rectifier used after this - layer (only used with ``'leaky_relu'``). Defaults to 0. - mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing - ``'fan_in'`` preserves the magnitude of the variance of the weights - in the forward pass. Choosing ``'fan_out'`` preserves the - magnitudes in the backwards pass. Defaults to ``'fan_out'``. - nonlinearity (str): the non-linear function (`nn.functional` name), - recommended to use only with ``'relu'`` or ``'leaky_relu'`` . - Defaults to 'relu'. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` or - ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - a=0, - mode='fan_out', - nonlinearity='relu', - distribution='normal', - **kwargs): - super().__init__(**kwargs) - self.a = a - self.mode = mode - self.nonlinearity = nonlinearity - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ - f'nonlinearity={self.nonlinearity}, ' \ - f'distribution ={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Caffe2Xavier') -class Caffe2XavierInit(KaimingInit): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - def __init__(self, **kwargs): - super().__init__( - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='uniform', - **kwargs) - - def __call__(self, module): - super().__call__(module) - - -@INITIALIZERS.register_module(name='Pretrained') -class PretrainedInit(object): - """Initialize module by loading a pretrained model. - - Args: - checkpoint (str): the checkpoint file of the pretrained model should - be load. - prefix (str, optional): the prefix of a sub-module in the pretrained - model. it is for loading a part of the pretrained model to - initialize. For example, if we would like to only load the - backbone of a detector model, we can set ``prefix='backbone.'``. - Defaults to None. - map_location (str): map tensors into proper locations. - """ - - def __init__(self, checkpoint, prefix=None, map_location=None): - self.checkpoint = checkpoint - self.prefix = prefix - self.map_location = map_location - - def __call__(self, module): - from mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, - load_state_dict) - logger = get_logger('mmcv') - if self.prefix is None: - print_log(f'load model from: {self.checkpoint}', logger=logger) - load_checkpoint( - module, - self.checkpoint, - map_location=self.map_location, - strict=False, - logger=logger) - else: - print_log( - f'load {self.prefix} in model from: {self.checkpoint}', - logger=logger) - state_dict = _load_checkpoint_with_prefix( - self.prefix, self.checkpoint, map_location=self.map_location) - load_state_dict(module, state_dict, strict=False, logger=logger) - - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: load from {self.checkpoint}' - return info - - -def _initialize(module, cfg, wholemodule=False): - func = build_from_cfg(cfg, INITIALIZERS) - # wholemodule flag is for override mode, there is no layer key in override - # and initializer will give init values for the whole module with the name - # in override. - func.wholemodule = wholemodule - func(module) - - -def _initialize_override(module, override, cfg): - if not isinstance(override, (dict, list)): - raise TypeError(f'override must be a dict or a list of dict, \ - but got {type(override)}') - - override = [override] if isinstance(override, dict) else override - - for override_ in override: - - cp_override = copy.deepcopy(override_) - name = cp_override.pop('name', None) - if name is None: - raise ValueError('`override` must contain the key "name",' - f'but got {cp_override}') - # if override only has name key, it means use args in init_cfg - if not cp_override: - cp_override.update(cfg) - # if override has name key and other args except type key, it will - # raise error - elif 'type' not in cp_override.keys(): - raise ValueError( - f'`override` need "type" key, but got {cp_override}') - - if hasattr(module, name): - _initialize(getattr(module, name), cp_override, wholemodule=True) - else: - raise RuntimeError(f'module did not have attribute {name}, ' - f'but init_cfg is {cp_override}.') - - -def initialize(module, init_cfg): - """Initialize a module. - - Args: - module (``torch.nn.Module``): the module will be initialized. - init_cfg (dict | list[dict]): initialization configuration dict to - define initializer. OpenMMLab has implemented 6 initializers - including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, - ``Kaiming``, and ``Pretrained``. - Example: - >>> module = nn.Linear(2, 3, bias=True) - >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) - >>> initialize(module, init_cfg) - - >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) - >>> # define key ``'layer'`` for initializing layer with different - >>> # configuration - >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), - dict(type='Constant', layer='Linear', val=2)] - >>> initialize(module, init_cfg) - - >>> # define key``'override'`` to initialize some specific part in - >>> # module - >>> class FooNet(nn.Module): - >>> def __init__(self): - >>> super().__init__() - >>> self.feat = nn.Conv2d(3, 16, 3) - >>> self.reg = nn.Conv2d(16, 10, 3) - >>> self.cls = nn.Conv2d(16, 5, 3) - >>> model = FooNet() - >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', - >>> override=dict(type='Constant', name='reg', val=3, bias=4)) - >>> initialize(model, init_cfg) - - >>> model = ResNet(depth=50) - >>> # Initialize weights with the pretrained model. - >>> init_cfg = dict(type='Pretrained', - checkpoint='torchvision://resnet50') - >>> initialize(model, init_cfg) - - >>> # Initialize weights of a sub-module with the specific part of - >>> # a pretrained model by using "prefix". - >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ - >>> 'retinanet_r50_fpn_1x_coco/'\ - >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' - >>> init_cfg = dict(type='Pretrained', - checkpoint=url, prefix='backbone.') - """ - if not isinstance(init_cfg, (dict, list)): - raise TypeError(f'init_cfg must be a dict or a list of dict, \ - but got {type(init_cfg)}') - - if isinstance(init_cfg, dict): - init_cfg = [init_cfg] - - for cfg in init_cfg: - # should deeply copy the original config because cfg may be used by - # other modules, e.g., one init_cfg shared by multiple bottleneck - # blocks, the expected cfg will be changed after pop and will change - # the initialization behavior of other modules - cp_cfg = copy.deepcopy(cfg) - override = cp_cfg.pop('override', None) - _initialize(module, cp_cfg) - - if override is not None: - cp_cfg.pop('layer', None) - _initialize_override(module, override, cp_cfg) - else: - # All attributes in module have same initialization. - pass - - -def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, - b: float) -> Tensor: - # Method based on - # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf - # Modified from - # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - def norm_cdf(x): - # Computes standard normal cumulative distribution function - return (1. + math.erf(x / math.sqrt(2.))) / 2. - - if (mean < a - 2 * std) or (mean > b + 2 * std): - warnings.warn( - 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' - 'The distribution of values may be incorrect.', - stacklevel=2) - - with torch.no_grad(): - # Values are generated by using a truncated uniform distribution and - # then using the inverse CDF for the normal distribution. - # Get upper and lower cdf values - lower = norm_cdf((a - mean) / std) - upper = norm_cdf((b - mean) / std) - - # Uniformly fill tensor with values from [lower, upper], then translate - # to [2lower-1, 2upper-1]. - tensor.uniform_(2 * lower - 1, 2 * upper - 1) - - # Use inverse cdf transform for normal distribution to get truncated - # standard normal - tensor.erfinv_() - - # Transform to proper mean, std - tensor.mul_(std * math.sqrt(2.)) - tensor.add_(mean) - - # Clamp to ensure it's in the proper range - tensor.clamp_(min=a, max=b) - return tensor - - -def trunc_normal_(tensor: Tensor, - mean: float = 0., - std: float = 1., - a: float = -2., - b: float = 2.) -> Tensor: - r"""Fills the input Tensor with values drawn from a truncated - normal distribution. The values are effectively drawn from the - normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` - with values outside :math:`[a, b]` redrawn until they are within - the bounds. The method used for generating the random values works - best when :math:`a \leq \text{mean} \leq b`. - - Modified from - https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - - Args: - tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. - mean (float): the mean of the normal distribution. - std (float): the standard deviation of the normal distribution. - a (float): the minimum cutoff value. - b (float): the maximum cutoff value. - """ - return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/vgg.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/vgg.py deleted file mode 100755 index 8778b6495..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/cnn/vgg.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn - -from .utils import constant_init, kaiming_init, normal_init - - -def conv3x3(in_planes, out_planes, dilation=1): - """3x3 convolution with padding.""" - return nn.Conv2d( - in_planes, - out_planes, - kernel_size=3, - padding=dilation, - dilation=dilation) - - -def make_vgg_layer(inplanes, - planes, - num_blocks, - dilation=1, - with_bn=False, - ceil_mode=False): - layers = [] - for _ in range(num_blocks): - layers.append(conv3x3(inplanes, planes, dilation)) - if with_bn: - layers.append(nn.BatchNorm2d(planes)) - layers.append(nn.ReLU(inplace=True)) - inplanes = planes - layers.append(nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=ceil_mode)) - - return layers - - -class VGG(nn.Module): - """VGG backbone. - - Args: - depth (int): Depth of vgg, from {11, 13, 16, 19}. - with_bn (bool): Use BatchNorm or not. - num_classes (int): number of classes for classification. - num_stages (int): VGG stages, normally 5. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze - running stats (mean and var). - bn_frozen (bool): Whether to freeze weight and bias of BN layers. - """ - - arch_settings = { - 11: (1, 1, 2, 2, 2), - 13: (2, 2, 2, 2, 2), - 16: (2, 2, 3, 3, 3), - 19: (2, 2, 4, 4, 4) - } - - def __init__(self, - depth, - with_bn=False, - num_classes=-1, - num_stages=5, - dilations=(1, 1, 1, 1, 1), - out_indices=(0, 1, 2, 3, 4), - frozen_stages=-1, - bn_eval=True, - bn_frozen=False, - ceil_mode=False, - with_last_pool=True): - super(VGG, self).__init__() - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for vgg') - assert num_stages >= 1 and num_stages <= 5 - stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - assert len(dilations) == num_stages - assert max(out_indices) <= num_stages - - self.num_classes = num_classes - self.out_indices = out_indices - self.frozen_stages = frozen_stages - self.bn_eval = bn_eval - self.bn_frozen = bn_frozen - - self.inplanes = 3 - start_idx = 0 - vgg_layers = [] - self.range_sub_modules = [] - for i, num_blocks in enumerate(self.stage_blocks): - num_modules = num_blocks * (2 + with_bn) + 1 - end_idx = start_idx + num_modules - dilation = dilations[i] - planes = 64 * 2**i if i < 4 else 512 - vgg_layer = make_vgg_layer( - self.inplanes, - planes, - num_blocks, - dilation=dilation, - with_bn=with_bn, - ceil_mode=ceil_mode) - vgg_layers.extend(vgg_layer) - self.inplanes = planes - self.range_sub_modules.append([start_idx, end_idx]) - start_idx = end_idx - if not with_last_pool: - vgg_layers.pop(-1) - self.range_sub_modules[-1][1] -= 1 - self.module_name = 'features' - self.add_module(self.module_name, nn.Sequential(*vgg_layers)) - - if self.num_classes > 0: - self.classifier = nn.Sequential( - nn.Linear(512 * 7 * 7, 4096), - nn.ReLU(True), - nn.Dropout(), - nn.Linear(4096, 4096), - nn.ReLU(True), - nn.Dropout(), - nn.Linear(4096, num_classes), - ) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m) - elif isinstance(m, nn.BatchNorm2d): - constant_init(m, 1) - elif isinstance(m, nn.Linear): - normal_init(m, std=0.01) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - outs = [] - vgg_layers = getattr(self, self.module_name) - for i in range(len(self.stage_blocks)): - for j in range(*self.range_sub_modules[i]): - vgg_layer = vgg_layers[j] - x = vgg_layer(x) - if i in self.out_indices: - outs.append(x) - if self.num_classes > 0: - x = x.view(x.size(0), -1) - x = self.classifier(x) - outs.append(x) - if len(outs) == 1: - return outs[0] - else: - return tuple(outs) - - def train(self, mode=True): - super(VGG, self).train(mode) - if self.bn_eval: - for m in self.modules(): - if isinstance(m, nn.BatchNorm2d): - m.eval() - if self.bn_frozen: - for params in m.parameters(): - params.requires_grad = False - vgg_layers = getattr(self, self.module_name) - if mode and self.frozen_stages >= 0: - for i in range(self.frozen_stages): - for j in range(*self.range_sub_modules[i]): - mod = vgg_layers[j] - mod.eval() - for param in mod.parameters(): - param.requires_grad = False diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/engine/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/engine/__init__.py deleted file mode 100755 index 3193b7f66..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, - single_gpu_test) - -__all__ = [ - 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', - 'single_gpu_test' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/engine/test.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/engine/test.py deleted file mode 100755 index f236b1cda..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/engine/test.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, data_loader): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - # Assume result has the same length of batch_size - # refer to https://github.com/open-mmlab/mmcv/issues/985 - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting - ``gpu_collect=True``, it encodes results to gpu tensors and use gpu - communication for results collection. On cpu mode it saves the results on - different gpus to ``tmpdir`` and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - if rank == 0: - batch_size = len(result) - batch_size_all = batch_size * world_size - if batch_size_all + prog_bar.completed > len(dataset): - batch_size_all = len(dataset) - prog_bar.completed - for _ in range(batch_size_all): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results under cpu mode. - - On cpu mode, this function will save the results on different gpus to - ``tmpdir`` and collect them by the rank 0 worker. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - tmpdir (str | None): temporal directory for collected results to - store. If set to None, it will create a random temporal directory - for it. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_result = mmcv.load(part_file) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results under gpu mode. - - On gpu mode, this function will encode results to gpu tensors and use gpu - communication for results collection. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/__init__.py deleted file mode 100755 index 2051b85f7..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .file_client import BaseStorageBackend, FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler -from .io import dump, load, register_handler -from .parse import dict_from_file, list_from_file - -__all__ = [ - 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', - 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', - 'list_from_file', 'dict_from_file' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/file_client.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/file_client.py deleted file mode 100755 index b2d622868..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/file_client.py +++ /dev/null @@ -1,1148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import os -import os.path as osp -import re -import tempfile -import warnings -from abc import ABCMeta, abstractmethod -from contextlib import contextmanager -from pathlib import Path -from typing import Iterable, Iterator, Optional, Tuple, Union -from urllib.request import urlopen - -import mmcv -from mmcv.utils.misc import has_method -from mmcv.utils.path import is_filepath - - -class BaseStorageBackend(metaclass=ABCMeta): - """Abstract class of storage backends. - - All backends need to implement two apis: ``get()`` and ``get_text()``. - ``get()`` reads the file as a byte stream and ``get_text()`` reads the file - as texts. - """ - - # a flag to indicate whether the backend can create a symlink for a file - _allow_symlink = False - - @property - def name(self): - return self.__class__.__name__ - - @property - def allow_symlink(self): - return self._allow_symlink - - @abstractmethod - def get(self, filepath): - pass - - @abstractmethod - def get_text(self, filepath): - pass - - -class CephBackend(BaseStorageBackend): - """Ceph storage backend (for internal use). - - Args: - path_mapping (dict|None): path mapping dict from local path to Petrel - path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` - will be replaced by ``dst``. Default: None. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - """ - - def __init__(self, path_mapping=None): - try: - import ceph - except ImportError: - raise ImportError('Please install ceph to enable CephBackend.') - - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead') - self._client = ceph.S3Client() - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def get(self, filepath): - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class PetrelBackend(BaseStorageBackend): - """Petrel storage backend (for internal use). - - PetrelBackend supports reading and writing data to multiple clusters. - If the file path contains the cluster name, PetrelBackend will read data - from specified cluster or write data to it. Otherwise, PetrelBackend will - access the default cluster. - - Args: - path_mapping (dict, optional): Path mapping dict from local path to - Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in - ``filepath`` will be replaced by ``dst``. Default: None. - enable_mc (bool, optional): Whether to enable memcached support. - Default: True. - - Examples: - >>> filepath1 = 's3://path/of/file' - >>> filepath2 = 'cluster-name:s3://path/of/file' - >>> client = PetrelBackend() - >>> client.get(filepath1) # get data from default cluster - >>> client.get(filepath2) # get data from 'cluster-name' cluster - """ - - def __init__(self, - path_mapping: Optional[dict] = None, - enable_mc: bool = True): - try: - from petrel_client import client - except ImportError: - raise ImportError('Please install petrel_client to enable ' - 'PetrelBackend.') - - self._client = client.Client(enable_mc=enable_mc) - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def _map_path(self, filepath: Union[str, Path]) -> str: - """Map ``filepath`` to a string path whose prefix will be replaced by - :attr:`self.path_mapping`. - - Args: - filepath (str): Path to be mapped. - """ - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - return filepath - - def _format_path(self, filepath: str) -> str: - """Convert a ``filepath`` to standard format of petrel oss. - - If the ``filepath`` is concatenated by ``os.path.join``, in a Windows - environment, the ``filepath`` will be the format of - 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the - above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. - - Args: - filepath (str): Path to be formatted. - """ - return re.sub(r'\\+', '/', filepath) - - def get(self, filepath: Union[str, Path]) -> memoryview: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - memoryview: A memory view of expected bytes object to avoid - copying. The memoryview object can be converted to bytes by - ``value_buf.tobytes()``. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return str(self.get(filepath), encoding=encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Save data to a given ``filepath``. - - Args: - obj (bytes): Data to be saved. - filepath (str or Path): Path to write data. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.put(filepath, obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Save data to a given ``filepath``. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to encode the ``obj``. - Default: 'utf-8'. - """ - self.put(bytes(obj, encoding=encoding), filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - if not has_method(self._client, 'delete'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `delete` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.delete(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - if not (has_method(self._client, 'contains') - and has_method(self._client, 'isdir')): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` and `isdir` methods, please use a higher' - 'version or dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) or self._client.isdir(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - if not has_method(self._client, 'isdir'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `isdir` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - if not has_method(self._client, 'contains'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` method, please use a higher version or ' - 'dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result after concatenation. - """ - filepath = self._format_path(self._map_path(filepath)) - if filepath.endswith('/'): - filepath = filepath[:-1] - formatted_paths = [filepath] - for path in filepaths: - formatted_paths.append(self._format_path(self._map_path(path))) - return '/'.join(formatted_paths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download a file from ``filepath`` and return a temporary path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str | Path): Download a file from ``filepath``. - - Examples: - >>> client = PetrelBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('s3://path/of/your/file') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one temporary path. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - assert self.isfile(filepath) - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - Petrel has no concept of directories but it simulates the directory - hierarchy in the filesystem through public prefixes. In addition, - if the returned path ends with '/', it means the path is a public - prefix which is a logical directory. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - In addition, the returned path of directory will not contains the - suffix '/' which is consistent with other backends. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if not has_method(self._client, 'list'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `list` method, please use a higher version or dev' - ' branch instead.')) - - dir_path = self._map_path(dir_path) - dir_path = self._format_path(dir_path) - if list_dir and suffix is not None: - raise TypeError( - '`list_dir` should be False when `suffix` is not None') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - # Petrel's simulated directory hierarchy assumes that directory paths - # should end with `/` - if not dir_path.endswith('/'): - dir_path += '/' - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for path in self._client.list(dir_path): - # the `self.isdir` is not used here to determine whether path - # is a directory, because `self.isdir` relies on - # `self._client.list` - if path.endswith('/'): # a directory path - next_dir_path = self.join_path(dir_path, path) - if list_dir: - # get the relative path and exclude the last - # character '/' - rel_dir = next_dir_path[len(root):-1] - yield rel_dir - if recursive: - yield from _list_dir_or_file(next_dir_path, list_dir, - list_file, suffix, - recursive) - else: # a file path - absolute_path = self.join_path(dir_path, path) - rel_path = absolute_path[len(root):] - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class MemcachedBackend(BaseStorageBackend): - """Memcached storage backend. - - Attributes: - server_list_cfg (str): Config file for memcached server list. - client_cfg (str): Config file for memcached client. - sys_path (str | None): Additional path to be appended to `sys.path`. - Default: None. - """ - - def __init__(self, server_list_cfg, client_cfg, sys_path=None): - if sys_path is not None: - import sys - sys.path.append(sys_path) - try: - import mc - except ImportError: - raise ImportError( - 'Please install memcached to enable MemcachedBackend.') - - self.server_list_cfg = server_list_cfg - self.client_cfg = client_cfg - self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, - self.client_cfg) - # mc.pyvector servers as a point which points to a memory cache - self._mc_buffer = mc.pyvector() - - def get(self, filepath): - filepath = str(filepath) - import mc - self._client.Get(filepath, self._mc_buffer) - value_buf = mc.ConvertBuffer(self._mc_buffer) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class LmdbBackend(BaseStorageBackend): - """Lmdb storage backend. - - Args: - db_path (str): Lmdb database path. - readonly (bool, optional): Lmdb environment parameter. If True, - disallow any write operations. Default: True. - lock (bool, optional): Lmdb environment parameter. If False, when - concurrent access occurs, do not lock the database. Default: False. - readahead (bool, optional): Lmdb environment parameter. If False, - disable the OS filesystem readahead mechanism, which may improve - random read performance when a database is larger than RAM. - Default: False. - - Attributes: - db_path (str): Lmdb database path. - """ - - def __init__(self, - db_path, - readonly=True, - lock=False, - readahead=False, - **kwargs): - try: - import lmdb - except ImportError: - raise ImportError('Please install lmdb to enable LmdbBackend.') - - self.db_path = str(db_path) - self._client = lmdb.open( - self.db_path, - readonly=readonly, - lock=lock, - readahead=readahead, - **kwargs) - - def get(self, filepath): - """Get values according to the filepath. - - Args: - filepath (str | obj:`Path`): Here, filepath is the lmdb key. - """ - filepath = str(filepath) - with self._client.begin(write=False) as txn: - value_buf = txn.get(filepath.encode('ascii')) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class HardDiskBackend(BaseStorageBackend): - """Raw hard disks storage backend.""" - - _allow_symlink = True - - def get(self, filepath: Union[str, Path]) -> bytes: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes: Expected bytes object. - """ - with open(filepath, 'rb') as f: - value_buf = f.read() - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - with open(filepath, 'r', encoding=encoding) as f: - value_buf = f.read() - return value_buf - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` will create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'wb') as f: - f.write(obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` will create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'w', encoding=encoding) as f: - f.write(obj) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - os.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return osp.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return osp.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return osp.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return osp.join(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, filepath: Union[str, Path]) -> Iterable[Union[str, Path]]: - """Only for unified API and do nothing.""" - yield filepath - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if list_dir and suffix is not None: - raise TypeError('`suffix` should be None when `list_dir` is True') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - elif osp.isdir(entry.path): - if list_dir: - rel_dir = osp.relpath(entry.path, root) - yield rel_dir - if recursive: - yield from _list_dir_or_file(entry.path, list_dir, - list_file, suffix, - recursive) - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class HTTPBackend(BaseStorageBackend): - """HTTP and HTTPS storage bachend.""" - - def get(self, filepath): - value_buf = urlopen(filepath).read() - return value_buf - - def get_text(self, filepath, encoding='utf-8'): - value_buf = urlopen(filepath).read() - return value_buf.decode(encoding) - - @contextmanager - def get_local_path(self, filepath: str) -> Iterable[str]: - """Download a file from ``filepath``. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str): Download a file from ``filepath``. - - Examples: - >>> client = HTTPBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('http://path/of/your/file') as path: - ... # do something here - """ - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - -class FileClient: - """A general file client to access files in different backends. - - The client loads a file or text in a specified backend from its path - and returns it as a binary or text file. There are two ways to choose a - backend, the name of backend and the prefix of path. Although both of them - can be used to choose a storage backend, ``backend`` has a higher priority - that is if they are all set, the storage backend will be chosen by the - backend argument. If they are all `None`, the disk backend will be chosen. - Note that It can also register other backend accessor with a given name, - prefixes, and backend class. In addition, We use the singleton pattern to - avoid repeated object creation. If the arguments are the same, the same - object will be returned. - - Args: - backend (str, optional): The storage backend type. Options are "disk", - "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. - prefix (str, optional): The prefix of the registered storage backend. - Options are "s3", "http", "https". Default: None. - - Examples: - >>> # only set backend - >>> file_client = FileClient(backend='petrel') - >>> # only set prefix - >>> file_client = FileClient(prefix='s3') - >>> # set both backend and prefix but use backend to choose client - >>> file_client = FileClient(backend='petrel', prefix='s3') - >>> # if the arguments are the same, the same object is returned - >>> file_client1 = FileClient(backend='petrel') - >>> file_client1 is file_client - True - - Attributes: - client (:obj:`BaseStorageBackend`): The backend object. - """ - - _backends = { - 'disk': HardDiskBackend, - 'ceph': CephBackend, - 'memcached': MemcachedBackend, - 'lmdb': LmdbBackend, - 'petrel': PetrelBackend, - 'http': HTTPBackend, - } - # This collection is used to record the overridden backends, and when a - # backend appears in the collection, the singleton pattern is disabled for - # that backend, because if the singleton pattern is used, then the object - # returned will be the backend before overwriting - _overridden_backends = set() - _prefix_to_backends = { - 's3': PetrelBackend, - 'http': HTTPBackend, - 'https': HTTPBackend, - } - _overridden_prefixes = set() - - _instances = {} - - def __new__(cls, backend=None, prefix=None, **kwargs): - if backend is None and prefix is None: - backend = 'disk' - if backend is not None and backend not in cls._backends: - raise ValueError( - f'Backend {backend} is not supported. Currently supported ones' - f' are {list(cls._backends.keys())}') - if prefix is not None and prefix not in cls._prefix_to_backends: - raise ValueError( - f'prefix {prefix} is not supported. Currently supported ones ' - f'are {list(cls._prefix_to_backends.keys())}') - - # concatenate the arguments to a unique key for determining whether - # objects with the same arguments were created - arg_key = f'{backend}:{prefix}' - for key, value in kwargs.items(): - arg_key += f':{key}:{value}' - - # if a backend was overridden, it will create a new object - if (arg_key in cls._instances - and backend not in cls._overridden_backends - and prefix not in cls._overridden_prefixes): - _instance = cls._instances[arg_key] - else: - # create a new object and put it to _instance - _instance = super().__new__(cls) - if backend is not None: - _instance.client = cls._backends[backend](**kwargs) - else: - _instance.client = cls._prefix_to_backends[prefix](**kwargs) - - cls._instances[arg_key] = _instance - - return _instance - - @property - def name(self): - return self.client.name - - @property - def allow_symlink(self): - return self.client.allow_symlink - - @staticmethod - def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: - """Parse the prefix of a uri. - - Args: - uri (str | Path): Uri to be parsed that contains the file prefix. - - Examples: - >>> FileClient.parse_uri_prefix('s3://path/of/your/file') - 's3' - - Returns: - str | None: Return the prefix of uri if the uri contains '://' - else ``None``. - """ - assert is_filepath(uri) - uri = str(uri) - if '://' not in uri: - return None - else: - prefix, _ = uri.split('://') - # In the case of PetrelBackend, the prefix may contains the cluster - # name like clusterName:s3 - if ':' in prefix: - _, prefix = prefix.split(':') - return prefix - - @classmethod - def infer_client(cls, - file_client_args: Optional[dict] = None, - uri: Optional[Union[str, Path]] = None) -> 'FileClient': - """Infer a suitable file client based on the URI and arguments. - - Args: - file_client_args (dict, optional): Arguments to instantiate a - FileClient. Default: None. - uri (str | Path, optional): Uri to be parsed that contains the file - prefix. Default: None. - - Examples: - >>> uri = 's3://path/of/your/file' - >>> file_client = FileClient.infer_client(uri=uri) - >>> file_client_args = {'backend': 'petrel'} - >>> file_client = FileClient.infer_client(file_client_args) - - Returns: - FileClient: Instantiated FileClient object. - """ - assert file_client_args is not None or uri is not None - if file_client_args is None: - file_prefix = cls.parse_uri_prefix(uri) # type: ignore - return cls(prefix=file_prefix) - else: - return cls(**file_client_args) - - @classmethod - def _register_backend(cls, name, backend, force=False, prefixes=None): - if not isinstance(name, str): - raise TypeError('the backend name should be a string, ' - f'but got {type(name)}') - if not inspect.isclass(backend): - raise TypeError( - f'backend should be a class but got {type(backend)}') - if not issubclass(backend, BaseStorageBackend): - raise TypeError( - f'backend {backend} is not a subclass of BaseStorageBackend') - if not force and name in cls._backends: - raise KeyError( - f'{name} is already registered as a storage backend, ' - 'add "force=True" if you want to override it') - - if name in cls._backends and force: - cls._overridden_backends.add(name) - cls._backends[name] = backend - - if prefixes is not None: - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if prefix not in cls._prefix_to_backends: - cls._prefix_to_backends[prefix] = backend - elif (prefix in cls._prefix_to_backends) and force: - cls._overridden_prefixes.add(prefix) - cls._prefix_to_backends[prefix] = backend - else: - raise KeyError( - f'{prefix} is already registered as a storage backend,' - ' add "force=True" if you want to override it') - - @classmethod - def register_backend(cls, name, backend=None, force=False, prefixes=None): - """Register a backend to FileClient. - - This method can be used as a normal class method or a decorator. - - .. code-block:: python - - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - FileClient.register_backend('new', NewBackend) - - or - - .. code-block:: python - - @FileClient.register_backend('new') - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - Args: - name (str): The name of the registered backend. - backend (class, optional): The backend class to be registered, - which must be a subclass of :class:`BaseStorageBackend`. - When this method is used as a decorator, backend is None. - Defaults to None. - force (bool, optional): Whether to override the backend if the name - has already been registered. Defaults to False. - prefixes (str or list[str] or tuple[str], optional): The prefixes - of the registered storage backend. Default: None. - `New in version 1.3.15.` - """ - if backend is not None: - cls._register_backend( - name, backend, force=force, prefixes=prefixes) - return - - def _register(backend_cls): - cls._register_backend( - name, backend_cls, force=force, prefixes=prefixes) - return backend_cls - - return _register - - def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: - """Read data from a given ``filepath`` with 'rb' mode. - - Note: - There are two types of return values for ``get``, one is ``bytes`` - and the other is ``memoryview``. The advantage of using memoryview - is that you can avoid copying, and if you want to convert it to - ``bytes``, you can use ``.tobytes()``. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes | memoryview: Expected bytes object or a memory view of the - bytes object. - """ - return self.client.get(filepath) - - def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return self.client.get_text(filepath, encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` should create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - self.client.put(obj, filepath) - - def put_text(self, obj: str, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` should create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str, optional): The encoding format used to open the - `filepath`. Default: 'utf-8'. - """ - self.client.put_text(obj, filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str, Path): Path to be removed. - """ - self.client.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return self.client.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return self.client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return self.client.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return self.client.join_path(filepath, *filepaths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download data from ``filepath`` and write the data to local path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Note: - If the ``filepath`` is a local path, just return itself. - - .. warning:: - ``get_local_path`` is an experimental interface that may change in - the future. - - Args: - filepath (str or Path): Path to be read data. - - Examples: - >>> file_client = FileClient(prefix='s3') - >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one path. - """ - with self.client.get_local_path(str(filepath)) as local_path: - yield local_path - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, - suffix, recursive) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/__init__.py deleted file mode 100755 index aa24d9197..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseFileHandler -from .json_handler import JsonHandler -from .pickle_handler import PickleHandler -from .yaml_handler import YamlHandler - -__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/base.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/base.py deleted file mode 100755 index 288878bc5..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseFileHandler(metaclass=ABCMeta): - # `str_like` is a flag to indicate whether the type of file object is - # str-like object or bytes-like object. Pickle only processes bytes-like - # objects but json only processes str-like object. If it is str-like - # object, `StringIO` will be used to process the buffer. - str_like = True - - @abstractmethod - def load_from_fileobj(self, file, **kwargs): - pass - - @abstractmethod - def dump_to_fileobj(self, obj, file, **kwargs): - pass - - @abstractmethod - def dump_to_str(self, obj, **kwargs): - pass - - def load_from_path(self, filepath, mode='r', **kwargs): - with open(filepath, mode) as f: - return self.load_from_fileobj(f, **kwargs) - - def dump_to_path(self, obj, filepath, mode='w', **kwargs): - with open(filepath, mode) as f: - self.dump_to_fileobj(obj, f, **kwargs) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/json_handler.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/json_handler.py deleted file mode 100755 index 18d4f15f7..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/json_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -import numpy as np - -from .base import BaseFileHandler - - -def set_default(obj): - """Set default json values for non-serializable values. - - It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. - It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, - etc.) into plain numbers of plain python built-in types. - """ - if isinstance(obj, (set, range)): - return list(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, np.generic): - return obj.item() - raise TypeError(f'{type(obj)} is unsupported for json dump') - - -class JsonHandler(BaseFileHandler): - - def load_from_fileobj(self, file): - return json.load(file) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('default', set_default) - json.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('default', set_default) - return json.dumps(obj, **kwargs) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/pickle_handler.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/pickle_handler.py deleted file mode 100755 index b37c79bed..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/pickle_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import pickle - -from .base import BaseFileHandler - - -class PickleHandler(BaseFileHandler): - - str_like = False - - def load_from_fileobj(self, file, **kwargs): - return pickle.load(file, **kwargs) - - def load_from_path(self, filepath, **kwargs): - return super(PickleHandler, self).load_from_path( - filepath, mode='rb', **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('protocol', 2) - return pickle.dumps(obj, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('protocol', 2) - pickle.dump(obj, file, **kwargs) - - def dump_to_path(self, obj, filepath, **kwargs): - super(PickleHandler, self).dump_to_path( - obj, filepath, mode='wb', **kwargs) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/yaml_handler.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/yaml_handler.py deleted file mode 100755 index c5aa2eea1..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/handlers/yaml_handler.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import yaml - -try: - from yaml import CLoader as Loader, CDumper as Dumper -except ImportError: - from yaml import Loader, Dumper - -from .base import BaseFileHandler # isort:skip - - -class YamlHandler(BaseFileHandler): - - def load_from_fileobj(self, file, **kwargs): - kwargs.setdefault('Loader', Loader) - return yaml.load(file, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('Dumper', Dumper) - yaml.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('Dumper', Dumper) - return yaml.dump(obj, **kwargs) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/io.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/io.py deleted file mode 100755 index aaefde58a..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/io.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from io import BytesIO, StringIO -from pathlib import Path - -from ..utils import is_list_of, is_str -from .file_client import FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler - -file_handlers = { - 'json': JsonHandler(), - 'yaml': YamlHandler(), - 'yml': YamlHandler(), - 'pickle': PickleHandler(), - 'pkl': PickleHandler() -} - - -def load(file, file_format=None, file_client_args=None, **kwargs): - """Load data from json/yaml/pickle files. - - This method provides a unified api for loading data from serialized files. - - Note: - In v1.3.16 and later, ``load`` supports loading data from serialized - files those can be storaged in different backends. - - Args: - file (str or :obj:`Path` or file-like object): Filename or a file-like - object. - file_format (str, optional): If not specified, the file format will be - inferred from the file extension, otherwise use the specified one. - Currently supported formats include "json", "yaml/yml" and - "pickle/pkl". - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> load('/path/of/your/file') # file is storaged in disk - >>> load('https://path/of/your/file') # file is storaged in Internet - >>> load('s3://path/of/your/file') # file is storaged in petrel - - Returns: - The content from the file. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None and is_str(file): - file_format = file.split('.')[-1] - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO(file_client.get_text(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - else: - with BytesIO(file_client.get(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - elif hasattr(file, 'read'): - obj = handler.load_from_fileobj(file, **kwargs) - else: - raise TypeError('"file" must be a filepath str or a file-object') - return obj - - -def dump(obj, file=None, file_format=None, file_client_args=None, **kwargs): - """Dump data to json/yaml/pickle strings or files. - - This method provides a unified api for dumping data as strings or to files, - and also supports custom arguments for each file format. - - Note: - In v1.3.16 and later, ``dump`` supports dumping data as strings or to - files which is saved to different backends. - - Args: - obj (any): The python object to be dumped. - file (str or :obj:`Path` or file-like object, optional): If not - specified, then the object is dumped to a str, otherwise to a file - specified by the filename or file-like object. - file_format (str, optional): Same as :func:`load`. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dump('hello world', '/path/of/your/file') # disk - >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel - - Returns: - bool: True for success, False otherwise. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None: - if is_str(file): - file_format = file.split('.')[-1] - elif file is None: - raise ValueError( - 'file_format must be specified since file is None') - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if file is None: - return handler.dump_to_str(obj, **kwargs) - elif is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put_text(f.getvalue(), file) - else: - with BytesIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put(f.getvalue(), file) - elif hasattr(file, 'write'): - handler.dump_to_fileobj(obj, file, **kwargs) - else: - raise TypeError('"file" must be a filename str or a file-object') - - -def _register_handler(handler, file_formats): - """Register a handler for some file extensions. - - Args: - handler (:obj:`BaseFileHandler`): Handler to be registered. - file_formats (str or list[str]): File formats to be handled by this - handler. - """ - if not isinstance(handler, BaseFileHandler): - raise TypeError( - f'handler must be a child of BaseFileHandler, not {type(handler)}') - if isinstance(file_formats, str): - file_formats = [file_formats] - if not is_list_of(file_formats, str): - raise TypeError('file_formats must be a str or a list of str') - for ext in file_formats: - file_handlers[ext] = handler - - -def register_handler(file_formats, **kwargs): - - def wrap(cls): - _register_handler(cls(**kwargs), file_formats) - return cls - - return wrap diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/parse.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/parse.py deleted file mode 100755 index f60f0d611..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/fileio/parse.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from io import StringIO - -from .file_client import FileClient - - -def list_from_file(filename, - prefix='', - offset=0, - max_num=0, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a list of strings. - - Note: - In v1.3.16 and later, ``list_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a list for strings. - - Args: - filename (str): Filename. - prefix (str): The prefix to be inserted to the beginning of each item. - offset (int): The offset of lines. - max_num (int): The maximum number of lines to be read, - zeros and negatives mean no limitation. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> list_from_file('/path/of/your/file') # disk - ['hello', 'world'] - >>> list_from_file('s3://path/of/your/file') # ceph or petrel - ['hello', 'world'] - - Returns: - list[str]: A list of strings. - """ - cnt = 0 - item_list = [] - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for _ in range(offset): - f.readline() - for line in f: - if 0 < max_num <= cnt: - break - item_list.append(prefix + line.rstrip('\n\r')) - cnt += 1 - return item_list - - -def dict_from_file(filename, - key_type=str, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a dict. - - Each line of the text file will be two or more columns split by - whitespaces or tabs. The first column will be parsed as dict keys, and - the following columns will be parsed as dict values. - - Note: - In v1.3.16 and later, ``dict_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a dict. - - Args: - filename(str): Filename. - key_type(type): Type of the dict keys. str is user by default and - type conversion will be performed if specified. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dict_from_file('/path/of/your/file') # disk - {'key1': 'value1', 'key2': 'value2'} - >>> dict_from_file('s3://path/of/your/file') # ceph or petrel - {'key1': 'value1', 'key2': 'value2'} - - Returns: - dict: The parsed contents. - """ - mapping = {} - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for line in f: - items = line.rstrip('\n').split() - assert len(items) >= 2 - key = key_type(items[0]) - val = items[1:] if len(items) > 2 else items[1] - mapping[key] = val - return mapping diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/image/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/image/__init__.py deleted file mode 100755 index d0051d609..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/image/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, - gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, - rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) -from .geometric import (cutout, imcrop, imflip, imflip_, impad, - impad_to_multiple, imrescale, imresize, imresize_like, - imresize_to_multiple, imrotate, imshear, imtranslate, - rescale_size) -from .io import imfrombytes, imread, imwrite, supported_backends, use_backend -from .misc import tensor2imgs -from .photometric import (adjust_brightness, adjust_color, adjust_contrast, - adjust_lighting, adjust_sharpness, auto_contrast, - clahe, imdenormalize, imequalize, iminvert, - imnormalize, imnormalize_, lut_transform, posterize, - solarize) - -__all__ = [ - 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', - 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', - 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', - 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', - 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', - 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', - 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', - 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', - 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', - 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/image/colorspace.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/image/colorspace.py deleted file mode 100755 index 814533952..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/image/colorspace.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def imconvert(img, src, dst): - """Convert an image from the src colorspace to dst colorspace. - - Args: - img (ndarray): The input image. - src (str): The source colorspace, e.g., 'rgb', 'hsv'. - dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. - - Returns: - ndarray: The converted image. - """ - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - out_img = cv2.cvtColor(img, code) - return out_img - - -def bgr2gray(img, keepdim=False): - """Convert a BGR image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def rgb2gray(img, keepdim=False): - """Convert a RGB image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def gray2bgr(img): - """Convert a grayscale image to BGR image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted BGR image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return out_img - - -def gray2rgb(img): - """Convert a grayscale image to RGB image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted RGB image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) - return out_img - - -def _convert_input_type_range(img): - """Convert the type and range of the input image. - - It converts the input image to np.float32 type and range of [0, 1]. - It is mainly used for pre-processing the input image in colorspace - conversion functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - (ndarray): The converted image with type of np.float32 and range of - [0, 1]. - """ - img_type = img.dtype - img = img.astype(np.float32) - if img_type == np.float32: - pass - elif img_type == np.uint8: - img /= 255. - else: - raise TypeError('The img type should be np.float32 or np.uint8, ' - f'but got {img_type}') - return img - - -def _convert_output_type_range(img, dst_type): - """Convert the type and range of the image according to dst_type. - - It converts the image to desired type and range. If `dst_type` is np.uint8, - images will be converted to np.uint8 type with range [0, 255]. If - `dst_type` is np.float32, it converts the image to np.float32 type with - range [0, 1]. - It is mainly used for post-processing images in colorspace conversion - functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The image to be converted with np.float32 type and - range [0, 255]. - dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it - converts the image to np.uint8 type with range [0, 255]. If - dst_type is np.float32, it converts the image to np.float32 type - with range [0, 1]. - - Returns: - (ndarray): The converted image with desired type and range. - """ - if dst_type not in (np.uint8, np.float32): - raise TypeError('The dst_type should be np.float32 or np.uint8, ' - f'but got {dst_type}') - if dst_type == np.uint8: - img = img.round() - else: - img /= 255. - return img.astype(dst_type) - - -def rgb2ycbcr(img, y_only=False): - """Convert a RGB image to YCbCr image. - - This function produces the same results as Matlab's `rgb2ycbcr` function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 - else: - out_img = np.matmul( - img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], - [24.966, 112.0, -18.214]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def bgr2ycbcr(img, y_only=False): - """Convert a BGR image to YCbCr image. - - The bgr version of rgb2ycbcr. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 - else: - out_img = np.matmul( - img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], - [65.481, -37.797, 112.0]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2rgb(img): - """Convert a YCbCr image to RGB image. - - This function produces the same results as Matlab's ycbcr2rgb function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted RGB image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0, -0.00153632, 0.00791071], - [0.00625893, -0.00318811, 0]]) * 255.0 + [ - -222.921, 135.576, -276.836 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2bgr(img): - """Convert a YCbCr image to BGR image. - - The bgr version of ycbcr2rgb. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted BGR image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0.00791071, -0.00153632, 0], - [0, -0.00318811, 0.00625893]]) * 255.0 + [ - -276.836, 135.576, -222.921 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def convert_color_factory(src, dst): - - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - - def convert_color(img): - out_img = cv2.cvtColor(img, code) - return out_img - - convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} - image. - - Args: - img (ndarray or str): The input image. - - Returns: - ndarray: The converted {dst.upper()} image. - """ - - return convert_color - - -bgr2rgb = convert_color_factory('bgr', 'rgb') - -rgb2bgr = convert_color_factory('rgb', 'bgr') - -bgr2hsv = convert_color_factory('bgr', 'hsv') - -hsv2bgr = convert_color_factory('hsv', 'bgr') - -bgr2hls = convert_color_factory('bgr', 'hls') - -hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/image/geometric.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/image/geometric.py deleted file mode 100755 index cf97c201c..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/image/geometric.py +++ /dev/null @@ -1,728 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers - -import cv2 -import numpy as np - -from ..utils import to_2tuple -from .io import imread_backend - -try: - from PIL import Image -except ImportError: - Image = None - - -def _scale_size(size, scale): - """Rescale a size by a ratio. - - Args: - size (tuple[int]): (w, h). - scale (float | tuple(float)): Scaling factor. - - Returns: - tuple[int]: scaled size. - """ - if isinstance(scale, (float, int)): - scale = (scale, scale) - w, h = size - return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) - - -cv2_interp_codes = { - 'nearest': cv2.INTER_NEAREST, - 'bilinear': cv2.INTER_LINEAR, - 'bicubic': cv2.INTER_CUBIC, - 'area': cv2.INTER_AREA, - 'lanczos': cv2.INTER_LANCZOS4 -} - -if Image is not None: - pillow_interp_codes = { - 'nearest': Image.NEAREST, - 'bilinear': Image.BILINEAR, - 'bicubic': Image.BICUBIC, - 'box': Image.BOX, - 'lanczos': Image.LANCZOS, - 'hamming': Image.HAMMING - } - - -def imresize(img, - size, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image to a given size. - - Args: - img (ndarray): The input image. - size (tuple[int]): Target size (w, h). - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if backend is None: - backend = imread_backend - if backend not in ['cv2', 'pillow']: - raise ValueError(f'backend: {backend} is not supported for resize.' - f"Supported backends are 'cv2', 'pillow'") - - if backend == 'pillow': - assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' - pil_image = Image.fromarray(img) - pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) - resized_img = np.array(pil_image) - else: - resized_img = cv2.resize( - img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) - if not return_scale: - return resized_img - else: - w_scale = size[0] / w - h_scale = size[1] / h - return resized_img, w_scale, h_scale - - -def imresize_to_multiple(img, - divisor, - size=None, - scale_factor=None, - keep_ratio=False, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image according to a given size or scale factor and then rounds - up the the resized or rescaled image size to the nearest value that can be - divided by the divisor. - - Args: - img (ndarray): The input image. - divisor (int | tuple): Resized image size will be a multiple of - divisor. If divisor is a tuple, divisor should be - (w_divisor, h_divisor). - size (None | int | tuple[int]): Target size (w, h). Default: None. - scale_factor (None | float | tuple[float]): Multiplier for spatial - size. Should match input size if it is a tuple and the 2D style is - (w_scale_factor, h_scale_factor). Default: None. - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: False. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if size is not None and scale_factor is not None: - raise ValueError('only one of size or scale_factor should be defined') - elif size is None and scale_factor is None: - raise ValueError('one of size or scale_factor should be defined') - elif size is not None: - size = to_2tuple(size) - if keep_ratio: - size = rescale_size((w, h), size, return_scale=False) - else: - size = _scale_size((w, h), scale_factor) - - divisor = to_2tuple(divisor) - size = tuple([int(np.ceil(s / d)) * d for s, d in zip(size, divisor)]) - resized_img, w_scale, h_scale = imresize( - img, - size, - return_scale=True, - interpolation=interpolation, - out=out, - backend=backend) - if return_scale: - return resized_img, w_scale, h_scale - else: - return resized_img - - -def imresize_like(img, - dst_img, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image to the same size of a given image. - - Args: - img (ndarray): The input image. - dst_img (ndarray): The target image. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = dst_img.shape[:2] - return imresize(img, (w, h), return_scale, interpolation, backend=backend) - - -def rescale_size(old_size, scale, return_scale=False): - """Calculate the new size to be rescaled to. - - Args: - old_size (tuple[int]): The old size (w, h) of image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image size. - - Returns: - tuple[int]: The new rescaled image size. - """ - w, h = old_size - if isinstance(scale, (float, int)): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - scale_factor = scale - elif isinstance(scale, tuple): - max_long_edge = max(scale) - max_short_edge = min(scale) - scale_factor = min(max_long_edge / max(h, w), - max_short_edge / min(h, w)) - else: - raise TypeError( - f'Scale must be a number or tuple of int, but got {type(scale)}') - - new_size = _scale_size((w, h), scale_factor) - - if return_scale: - return new_size, scale_factor - else: - return new_size - - -def imrescale(img, - scale, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image while keeping the aspect ratio. - - Args: - img (ndarray): The input image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - ndarray: The rescaled image. - """ - h, w = img.shape[:2] - new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) - rescaled_img = imresize( - img, new_size, interpolation=interpolation, backend=backend) - if return_scale: - return rescaled_img, scale_factor - else: - return rescaled_img - - -def imflip(img, direction='horizontal'): - """Flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image. - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return np.flip(img, axis=1) - elif direction == 'vertical': - return np.flip(img, axis=0) - else: - return np.flip(img, axis=(0, 1)) - - -def imflip_(img, direction='horizontal'): - """Inplace flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image (inplace). - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return cv2.flip(img, 1, img) - elif direction == 'vertical': - return cv2.flip(img, 0, img) - else: - return cv2.flip(img, -1, img) - - -def imrotate(img, - angle, - center=None, - scale=1.0, - border_value=0, - interpolation='bilinear', - auto_bound=False): - """Rotate an image. - - Args: - img (ndarray): Image to be rotated. - angle (float): Rotation angle in degrees, positive values mean - clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. - scale (float): Isotropic scale factor. - border_value (int): Border value. - interpolation (str): Same as :func:`resize`. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. - - Returns: - ndarray: The rotated image. - """ - if center is not None and auto_bound: - raise ValueError('`auto_bound` conflicts with `center`') - h, w = img.shape[:2] - if center is None: - center = ((w - 1) * 0.5, (h - 1) * 0.5) - assert isinstance(center, tuple) - - matrix = cv2.getRotationMatrix2D(center, -angle, scale) - if auto_bound: - cos = np.abs(matrix[0, 0]) - sin = np.abs(matrix[0, 1]) - new_w = h * sin + w * cos - new_h = h * cos + w * sin - matrix[0, 2] += (new_w - w) * 0.5 - matrix[1, 2] += (new_h - h) * 0.5 - w = int(np.round(new_w)) - h = int(np.round(new_h)) - rotated = cv2.warpAffine( - img, - matrix, (w, h), - flags=cv2_interp_codes[interpolation], - borderValue=border_value) - return rotated - - -def bbox_clip(bboxes, img_shape): - """Clip bboxes to fit the image shape. - - Args: - bboxes (ndarray): Shape (..., 4*k) - img_shape (tuple[int]): (height, width) of the image. - - Returns: - ndarray: Clipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) - cmin[0::2] = img_shape[1] - 1 - cmin[1::2] = img_shape[0] - 1 - clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) - return clipped_bboxes - - -def bbox_scaling(bboxes, scale, clip_shape=None): - """Scaling bboxes w.r.t the box center. - - Args: - bboxes (ndarray): Shape(..., 4). - scale (float): Scaling factor. - clip_shape (tuple[int], optional): If specified, bboxes that exceed the - boundary will be clipped according to the given shape (h, w). - - Returns: - ndarray: Scaled bboxes. - """ - if float(scale) == 1.0: - scaled_bboxes = bboxes.copy() - else: - w = bboxes[..., 2] - bboxes[..., 0] + 1 - h = bboxes[..., 3] - bboxes[..., 1] + 1 - dw = (w * (scale - 1)) * 0.5 - dh = (h * (scale - 1)) * 0.5 - scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) - if clip_shape is not None: - return bbox_clip(scaled_bboxes, clip_shape) - else: - return scaled_bboxes - - -def imcrop(img, bboxes, scale=1.0, pad_fill=None): - """Crop image patches. - - 3 steps: scale the bboxes -> clip bboxes -> crop and pad. - - Args: - img (ndarray): Image to be cropped. - bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. - scale (float, optional): Scale ratio of bboxes, the default value - 1.0 means no padding. - pad_fill (Number | list[Number]): Value to be filled for padding. - Default: None, which means no padding. - - Returns: - list[ndarray] | ndarray: The cropped image patches. - """ - chn = 1 if img.ndim == 2 else img.shape[2] - if pad_fill is not None: - if isinstance(pad_fill, (int, float)): - pad_fill = [pad_fill for _ in range(chn)] - assert len(pad_fill) == chn - - _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes - scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) - clipped_bbox = bbox_clip(scaled_bboxes, img.shape) - - patches = [] - for i in range(clipped_bbox.shape[0]): - x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) - if pad_fill is None: - patch = img[y1:y2 + 1, x1:x2 + 1, ...] - else: - _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) - if chn == 1: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) - else: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) - patch = np.array( - pad_fill, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - x_start = 0 if _x1 >= 0 else -_x1 - y_start = 0 if _y1 >= 0 else -_y1 - w = x2 - x1 + 1 - h = y2 - y1 + 1 - patch[y_start:y_start + h, x_start:x_start + w, - ...] = img[y1:y1 + h, x1:x1 + w, ...] - patches.append(patch) - - if bboxes.ndim == 1: - return patches[0] - else: - return patches - - -def impad(img, - *, - shape=None, - padding=None, - pad_val=0, - padding_mode='constant'): - """Pad the given image to a certain shape or pad on all sides with - specified padding mode and padding value. - - Args: - img (ndarray): Image to be padded. - shape (tuple[int]): Expected padding shape (h, w). Default: None. - padding (int or tuple[int]): Padding on each border. If a single int is - provided this is used to pad all borders. If tuple of length 2 is - provided this is the padding on left/right and top/bottom - respectively. If a tuple of length 4 is provided this is the - padding for the left, top, right and bottom borders respectively. - Default: None. Note that `shape` and `padding` can not be both - set. - pad_val (Number | Sequence[Number]): Values to be filled in padding - areas when padding_mode is 'constant'. Default: 0. - padding_mode (str): Type of padding. Should be: constant, edge, - reflect or symmetric. Default: constant. - - - constant: pads with a constant value, this value is specified - with pad_val. - - edge: pads with the last value at the edge of the image. - - reflect: pads with reflection of image without repeating the - last value on the edge. For example, padding [1, 2, 3, 4] - with 2 elements on both sides in reflect mode will result - in [3, 2, 1, 2, 3, 4, 3, 2]. - - symmetric: pads with reflection of image repeating the last - value on the edge. For example, padding [1, 2, 3, 4] with - 2 elements on both sides in symmetric mode will result in - [2, 1, 1, 2, 3, 4, 4, 3] - - Returns: - ndarray: The padded image. - """ - - assert (shape is not None) ^ (padding is not None) - if shape is not None: - padding = (0, 0, shape[1] - img.shape[1], shape[0] - img.shape[0]) - - # check pad_val - if isinstance(pad_val, tuple): - assert len(pad_val) == img.shape[-1] - elif not isinstance(pad_val, numbers.Number): - raise TypeError('pad_val must be a int or a tuple. ' - f'But received {type(pad_val)}') - - # check padding - if isinstance(padding, tuple) and len(padding) in [2, 4]: - if len(padding) == 2: - padding = (padding[0], padding[1], padding[0], padding[1]) - elif isinstance(padding, numbers.Number): - padding = (padding, padding, padding, padding) - else: - raise ValueError('Padding must be a int or a 2, or 4 element tuple.' - f'But received {padding}') - - # check padding mode - assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] - - border_type = { - 'constant': cv2.BORDER_CONSTANT, - 'edge': cv2.BORDER_REPLICATE, - 'reflect': cv2.BORDER_REFLECT_101, - 'symmetric': cv2.BORDER_REFLECT - } - img = cv2.copyMakeBorder( - img, - padding[1], - padding[3], - padding[0], - padding[2], - border_type[padding_mode], - value=pad_val) - - return img - - -def impad_to_multiple(img, divisor, pad_val=0): - """Pad an image to ensure each edge to be multiple to some number. - - Args: - img (ndarray): Image to be padded. - divisor (int): Padded image edges will be multiple to divisor. - pad_val (Number | Sequence[Number]): Same as :func:`impad`. - - Returns: - ndarray: The padded image. - """ - pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor - pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor - return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) - - -def cutout(img, shape, pad_val=0): - """Randomly cut out a rectangle from the original img. - - Args: - img (ndarray): Image to be cutout. - shape (int | tuple[int]): Expected cutout shape (h, w). If given as a - int, the value will be used for both h and w. - pad_val (int | float | tuple[int | float]): Values to be filled in the - cut area. Defaults to 0. - - Returns: - ndarray: The cutout image. - """ - - channels = 1 if img.ndim == 2 else img.shape[2] - if isinstance(shape, int): - cut_h, cut_w = shape, shape - else: - assert isinstance(shape, tuple) and len(shape) == 2, \ - f'shape must be a int or a tuple with length 2, but got type ' \ - f'{type(shape)} instead.' - cut_h, cut_w = shape - if isinstance(pad_val, (int, float)): - pad_val = tuple([pad_val] * channels) - elif isinstance(pad_val, tuple): - assert len(pad_val) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(pad_val), channels) - else: - raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') - - img_h, img_w = img.shape[:2] - y0 = np.random.uniform(img_h) - x0 = np.random.uniform(img_w) - - y1 = int(max(0, y0 - cut_h / 2.)) - x1 = int(max(0, x0 - cut_w / 2.)) - y2 = min(img_h, y1 + cut_h) - x2 = min(img_w, x1 + cut_w) - - if img.ndim == 2: - patch_shape = (y2 - y1, x2 - x1) - else: - patch_shape = (y2 - y1, x2 - x1, channels) - - img_cutout = img.copy() - patch = np.array( - pad_val, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - img_cutout[y1:y2, x1:x2, ...] = patch - - return img_cutout - - -def _get_shear_matrix(magnitude, direction='horizontal'): - """Generate the shear matrix for transformation. - - Args: - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - - Returns: - ndarray: The shear matrix with dtype float32. - """ - if direction == 'horizontal': - shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) - elif direction == 'vertical': - shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) - return shear_matrix - - -def imshear(img, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear an image. - - Args: - img (ndarray): Image to be sheared with format (h, w) - or (h, w, c). - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The sheared image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`') - shear_matrix = _get_shear_matrix(magnitude, direction) - sheared = cv2.warpAffine( - img, - shear_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. shearing masks whose channels large - # than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return sheared - - -def _get_translate_matrix(offset, direction='horizontal'): - """Generate the translate matrix. - - Args: - offset (int | float): The offset used for translate. - direction (str): The translate direction, either - "horizontal" or "vertical". - - Returns: - ndarray: The translate matrix with dtype float32. - """ - if direction == 'horizontal': - translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) - elif direction == 'vertical': - translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) - return translate_matrix - - -def imtranslate(img, - offset, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Translate an image. - - Args: - img (ndarray): Image to be translated with format - (h, w) or (h, w, c). - offset (int | float): The offset used for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The translated image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`.') - translate_matrix = _get_translate_matrix(offset, direction) - translated = cv2.warpAffine( - img, - translate_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. translating masks whose channels - # large than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return translated diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/image/io.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/image/io.py deleted file mode 100755 index d47aaa845..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/image/io.py +++ /dev/null @@ -1,258 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os.path as osp -from pathlib import Path - -import cv2 -import numpy as np -from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, - IMREAD_UNCHANGED) - -from mmcv.utils import check_file_exist, is_str, mkdir_or_exist - -try: - from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG -except ImportError: - TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None - -try: - from PIL import Image, ImageOps -except ImportError: - Image = None - -try: - import tifffile -except ImportError: - tifffile = None - -jpeg = None -supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] - -imread_flags = { - 'color': IMREAD_COLOR, - 'grayscale': IMREAD_GRAYSCALE, - 'unchanged': IMREAD_UNCHANGED, - 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, - 'grayscale_ignore_orientation': - IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE -} - -imread_backend = 'cv2' - - -def use_backend(backend): - """Select a backend for image decoding. - - Args: - backend (str): The image decoding backend type. Options are `cv2`, - `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) - and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` - file format. - """ - assert backend in supported_backends - global imread_backend - imread_backend = backend - if imread_backend == 'turbojpeg': - if TurboJPEG is None: - raise ImportError('`PyTurboJPEG` is not installed') - global jpeg - if jpeg is None: - jpeg = TurboJPEG() - elif imread_backend == 'pillow': - if Image is None: - raise ImportError('`Pillow` is not installed') - elif imread_backend == 'tifffile': - if tifffile is None: - raise ImportError('`tifffile` is not installed') - - -def _jpegflag(flag='color', channel_order='bgr'): - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'color': - if channel_order == 'bgr': - return TJPF_BGR - elif channel_order == 'rgb': - return TJCS_RGB - elif flag == 'grayscale': - return TJPF_GRAY - else: - raise ValueError('flag must be "color" or "grayscale"') - - -def _pillow2array(img, flag='color', channel_order='bgr'): - """Convert a pillow image to numpy array. - - Args: - img (:obj:`PIL.Image.Image`): The image loaded using PIL - flag (str): Flags specifying the color type of a loaded image, - candidates are 'color', 'grayscale' and 'unchanged'. - Default to 'color'. - channel_order (str): The channel order of the output image array, - candidates are 'bgr' and 'rgb'. Default to 'bgr'. - - Returns: - np.ndarray: The converted numpy array - """ - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'unchanged': - array = np.array(img) - if array.ndim >= 3 and array.shape[2] >= 3: # color image - array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR - else: - # Handle exif orientation tag - if flag in ['color', 'grayscale']: - img = ImageOps.exif_transpose(img) - # If the image mode is not 'RGB', convert it to 'RGB' first. - if img.mode != 'RGB': - if img.mode != 'LA': - # Most formats except 'LA' can be directly converted to RGB - img = img.convert('RGB') - else: - # When the mode is 'LA', the default conversion will fill in - # the canvas with black, which sometimes shadows black objects - # in the foreground. - # - # Therefore, a random color (124, 117, 104) is used for canvas - img_rgba = img.convert('RGBA') - img = Image.new('RGB', img_rgba.size, (124, 117, 104)) - img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha - if flag in ['color', 'color_ignore_orientation']: - array = np.array(img) - if channel_order != 'rgb': - array = array[:, :, ::-1] # RGB to BGR - elif flag in ['grayscale', 'grayscale_ignore_orientation']: - img = img.convert('L') - array = np.array(img) - else: - raise ValueError( - 'flag must be "color", "grayscale", "unchanged", ' - f'"color_ignore_orientation" or "grayscale_ignore_orientation"' - f' but got {flag}') - return array - - -def imread(img_or_path, flag='color', channel_order='bgr', backend=None): - """Read an image. - - Args: - img_or_path (ndarray or str or Path): Either a numpy array or str or - pathlib.Path. If it is a numpy array (loaded image), then - it will be returned as is. - flag (str): Flags specifying the color type of a loaded image, - candidates are `color`, `grayscale`, `unchanged`, - `color_ignore_orientation` and `grayscale_ignore_orientation`. - By default, `cv2` and `pillow` backend would rotate the image - according to its EXIF info unless called with `unchanged` or - `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend - always ignore image's EXIF info regardless of the flag. - The `turbojpeg` backend only supports `color` and `grayscale`. - channel_order (str): Order of channel, candidates are `bgr` and `rgb`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. - If backend is None, the global imread_backend specified by - ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - ndarray: Loaded image array. - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError(f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow'") - if isinstance(img_or_path, Path): - img_or_path = str(img_or_path) - - if isinstance(img_or_path, np.ndarray): - return img_or_path - elif is_str(img_or_path): - check_file_exist(img_or_path, - f'img file does not exist: {img_or_path}') - if backend == 'turbojpeg': - with open(img_or_path, 'rb') as in_file: - img = jpeg.decode(in_file.read(), - _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - img = Image.open(img_or_path) - img = _pillow2array(img, flag, channel_order) - return img - elif backend == 'tifffile': - img = tifffile.imread(img_or_path) - return img - else: - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imread(img_or_path, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - else: - raise TypeError('"img" must be a numpy array or a str or ' - 'a pathlib.Path object') - - -def imfrombytes(content, flag='color', channel_order='bgr', backend=None): - """Read an image from bytes. - - Args: - content (bytes): Image bytes got from files or other streams. - flag (str): Same as :func:`imread`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `None`. If backend is None, the - global imread_backend specified by ``mmcv.use_backend()`` will be - used. Default: None. - - Returns: - ndarray: Loaded image array. - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError(f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow'") - if backend == 'turbojpeg': - img = jpeg.decode(content, _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - buff = io.BytesIO(content) - img = Image.open(buff) - img = _pillow2array(img, flag, channel_order) - return img - else: - img_np = np.frombuffer(content, np.uint8) - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imdecode(img_np, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - - -def imwrite(img, file_path, params=None, auto_mkdir=True): - """Write image to file. - - Args: - img (ndarray): Image array to be written. - file_path (str): Image file path. - params (None or list): Same as opencv :func:`imwrite` interface. - auto_mkdir (bool): If the parent folder of `file_path` does not exist, - whether to create it automatically. - - Returns: - bool: Successful or not. - """ - if auto_mkdir: - dir_name = osp.abspath(osp.dirname(file_path)) - mkdir_or_exist(dir_name) - return cv2.imwrite(file_path, img, params) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/image/misc.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/image/misc.py deleted file mode 100755 index dfc4a9c6e..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/image/misc.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - -import mmcv - -try: - import torch -except ImportError: - torch = None - - -def tensor2imgs(tensor, mean=(0, 0, 0), std=(1, 1, 1), to_rgb=True): - """Convert tensor to 3-channel images. - - Args: - tensor (torch.Tensor): Tensor that contains multiple images, shape ( - N, C, H, W). - mean (tuple[float], optional): Mean of images. Defaults to (0, 0, 0). - std (tuple[float], optional): Standard deviation of images. - Defaults to (1, 1, 1). - to_rgb (bool, optional): Whether the tensor was converted to RGB - format in the first place. If so, convert it back to BGR. - Defaults to True. - - Returns: - list[np.ndarray]: A list that contains multiple images. - """ - - if torch is None: - raise RuntimeError('pytorch is not installed') - assert torch.is_tensor(tensor) and tensor.ndim == 4 - assert len(mean) == 3 - assert len(std) == 3 - - num_imgs = tensor.size(0) - mean = np.array(mean, dtype=np.float32) - std = np.array(std, dtype=np.float32) - imgs = [] - for img_id in range(num_imgs): - img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) - img = mmcv.imdenormalize( - img, mean, std, to_bgr=to_rgb).astype(np.uint8) - imgs.append(np.ascontiguousarray(img)) - return imgs diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/image/photometric.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/image/photometric.py deleted file mode 100755 index 5085d0120..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/image/photometric.py +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - -from ..utils import is_tuple_of -from .colorspace import bgr2gray, gray2bgr - - -def imnormalize(img, mean, std, to_rgb=True): - """Normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - img = img.copy().astype(np.float32) - return imnormalize_(img, mean, std, to_rgb) - - -def imnormalize_(img, mean, std, to_rgb=True): - """Inplace normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - # cv2 inplace normalization does not accept uint8 - assert img.dtype != np.uint8 - mean = np.float64(mean.reshape(1, -1)) - stdinv = 1 / np.float64(std.reshape(1, -1)) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - cv2.subtract(img, mean, img) # inplace - cv2.multiply(img, stdinv, img) # inplace - return img - - -def imdenormalize(img, mean, std, to_bgr=True): - assert img.dtype != np.uint8 - mean = mean.reshape(1, -1).astype(np.float64) - std = std.reshape(1, -1).astype(np.float64) - img = cv2.multiply(img, std) # make a copy - cv2.add(img, mean, img) # inplace - if to_bgr: - cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace - return img - - -def iminvert(img): - """Invert (negate) an image. - - Args: - img (ndarray): Image to be inverted. - - Returns: - ndarray: The inverted image. - """ - return np.full_like(img, 255) - img - - -def solarize(img, thr=128): - """Solarize an image (invert all pixel values above a threshold) - - Args: - img (ndarray): Image to be solarized. - thr (int): Threshold for solarizing (0 - 255). - - Returns: - ndarray: The solarized image. - """ - img = np.where(img < thr, img, 255 - img) - return img - - -def posterize(img, bits): - """Posterize an image (reduce the number of bits for each color channel) - - Args: - img (ndarray): Image to be posterized. - bits (int): Number of bits (1 to 8) to use for posterizing. - - Returns: - ndarray: The posterized image. - """ - shift = 8 - bits - img = np.left_shift(np.right_shift(img, shift), shift) - return img - - -def adjust_color(img, alpha=1, beta=None, gamma=0): - r"""It blends the source image and its gray image: - - .. math:: - output = img * alpha + gray\_img * beta + gamma - - Args: - img (ndarray): The input source image. - alpha (int | float): Weight for the source image. Default 1. - beta (int | float): Weight for the converted gray image. - If None, it's assigned the value (1 - `alpha`). - gamma (int | float): Scalar added to each sum. - Same as :func:`cv2.addWeighted`. Default 0. - - Returns: - ndarray: Colored image which has the same size and dtype as input. - """ - gray_img = bgr2gray(img) - gray_img = np.tile(gray_img[..., None], [1, 1, 3]) - if beta is None: - beta = 1 - alpha - colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) - if not colored_img.dtype == np.uint8: - # Note when the dtype of `img` is not the default `np.uint8` - # (e.g. np.float32), the value in `colored_img` got from cv2 - # is not guaranteed to be in range [0, 255], so here clip - # is needed. - colored_img = np.clip(colored_img, 0, 255) - return colored_img - - -def imequalize(img): - """Equalize the image histogram. - - This function applies a non-linear mapping to the input image, - in order to create a uniform distribution of grayscale values - in the output image. - - Args: - img (ndarray): Image to be equalized. - - Returns: - ndarray: The equalized image. - """ - - def _scale_channel(im, c): - """Scale the data in the corresponding channel.""" - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # For computing the step, filter out the nonzeros. - nonzero_histo = histo[histo > 0] - step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 - if not step: - lut = np.array(range(256)) - else: - # Compute the cumulative sum, shifted by step // 2 - # and then normalized by step. - lut = (np.cumsum(histo) + (step // 2)) // step - # Shift lut, prepending with 0. - lut = np.concatenate([[0], lut[:-1]], 0) - # handle potential integer overflow - lut[lut > 255] = 255 - # If step is zero, return the original image. - # Otherwise, index from lut. - return np.where(np.equal(step, 0), im, lut[im]) - - # Scales each channel independently and then stacks - # the result. - s1 = _scale_channel(img, 0) - s2 = _scale_channel(img, 1) - s3 = _scale_channel(img, 2) - equalized_img = np.stack([s1, s2, s3], axis=-1) - return equalized_img.astype(img.dtype) - - -def adjust_brightness(img, factor=1.): - """Adjust image brightness. - - This function controls the brightness of an image. An - enhancement factor of 0.0 gives a black image. - A factor of 1.0 gives the original image. This function - blends the source image and the degenerated black image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be brightened. - factor (float): A value controls the enhancement. - Factor 1.0 returns the original image, lower - factors mean less color (brightness, contrast, - etc), and higher values more. Default 1. - - Returns: - ndarray: The brightened image. - """ - degenerated = np.zeros_like(img) - # Note manually convert the dtype to np.float32, to - # achieve as close results as PIL.ImageEnhance.Brightness. - # Set beta=1-factor, and gamma=0 - brightened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - brightened_img = np.clip(brightened_img, 0, 255) - return brightened_img.astype(img.dtype) - - -def adjust_contrast(img, factor=1.): - """Adjust image contrast. - - This function controls the contrast of an image. An - enhancement factor of 0.0 gives a solid grey - image. A factor of 1.0 gives the original image. It - blends the source image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be contrasted. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - - Returns: - ndarray: The contrasted image. - """ - gray_img = bgr2gray(img) - hist = np.histogram(gray_img, 256, (0, 255))[0] - mean = round(np.sum(gray_img) / np.sum(hist)) - degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) - degenerated = gray2bgr(degenerated) - contrasted_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - contrasted_img = np.clip(contrasted_img, 0, 255) - return contrasted_img.astype(img.dtype) - - -def auto_contrast(img, cutoff=0): - """Auto adjust image contrast. - - This function maximize (normalize) image contrast by first removing cutoff - percent of the lightest and darkest pixels from the histogram and remapping - the image so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - Args: - img (ndarray): Image to be contrasted. BGR order. - cutoff (int | float | tuple): The cutoff percent of the lightest and - darkest pixels to be removed. If given as tuple, it shall be - (low, high). Otherwise, the single value will be used for both. - Defaults to 0. - - Returns: - ndarray: The contrasted image. - """ - - def _auto_contrast_channel(im, c, cutoff): - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # Remove cut-off percent pixels from histo - histo_sum = np.cumsum(histo) - cut_low = histo_sum[-1] * cutoff[0] // 100 - cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 - histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low - histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) - - # Compute mapping - low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] - # If all the values have been cut off, return the origin img - if low >= high: - return im - scale = 255.0 / (high - low) - offset = -low * scale - lut = np.array(range(256)) - lut = lut * scale + offset - lut = np.clip(lut, 0, 255) - return lut[im] - - if isinstance(cutoff, (int, float)): - cutoff = (cutoff, cutoff) - else: - assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ - f'float or tuple, but got {type(cutoff)} instead.' - # Auto adjusts contrast for each channel independently and then stacks - # the result. - s1 = _auto_contrast_channel(img, 0, cutoff) - s2 = _auto_contrast_channel(img, 1, cutoff) - s3 = _auto_contrast_channel(img, 2, cutoff) - contrasted_img = np.stack([s1, s2, s3], axis=-1) - return contrasted_img.astype(img.dtype) - - -def adjust_sharpness(img, factor=1., kernel=None): - """Adjust image sharpness. - - This function controls the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image. A - factor of 1.0 gives the original image. And a factor - of 2.0 gives a sharpened image. It blends the source - image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be sharpened. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - kernel (np.ndarray, optional): Filter kernel to be applied on the img - to obtain the degenerated img. Defaults to None. - - Note: - No value sanity check is enforced on the kernel set by users. So with - an inappropriate kernel, the ``adjust_sharpness`` may fail to perform - the function its name indicates but end up performing whatever - transform determined by the kernel. - - Returns: - ndarray: The sharpened image. - """ - - if kernel is None: - # adopted from PIL.ImageFilter.SMOOTH - kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 - assert isinstance(kernel, np.ndarray), \ - f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' - assert kernel.ndim == 2, \ - f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' - - degenerated = cv2.filter2D(img, -1, kernel) - sharpened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - sharpened_img = np.clip(sharpened_img, 0, 255) - return sharpened_img.astype(img.dtype) - - -def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): - """AlexNet-style PCA jitter. - - This data augmentation is proposed in `ImageNet Classification with Deep - Convolutional Neural Networks - `_. - - Args: - img (ndarray): Image to be adjusted lighting. BGR order. - eigval (ndarray): the eigenvalue of the convariance matrix of pixel - values, respectively. - eigvec (ndarray): the eigenvector of the convariance matrix of pixel - values, respectively. - alphastd (float): The standard deviation for distribution of alpha. - Defaults to 0.1 - to_rgb (bool): Whether to convert img to rgb. - - Returns: - ndarray: The adjusted image. - """ - assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ - f'eigval and eigvec should both be of type np.ndarray, got ' \ - f'{type(eigval)} and {type(eigvec)} instead.' - - assert eigval.ndim == 1 and eigvec.ndim == 2 - assert eigvec.shape == (3, eigval.shape[0]) - n_eigval = eigval.shape[0] - assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ - f'got {type(alphastd)} instead.' - - img = img.copy().astype(np.float32) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - - alpha = np.random.normal(0, alphastd, n_eigval) - alter = eigvec \ - * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ - * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) - alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) - img_adjusted = img + alter - return img_adjusted - - -def lut_transform(img, lut_table): - """Transform array by look-up table. - - The function lut_transform fills the output array with values from the - look-up table. Indices of the entries are taken from the input array. - - Args: - img (ndarray): Image to be transformed. - lut_table (ndarray): look-up table of 256 elements; in case of - multi-channel input array, the table should either have a single - channel (in this case the same table is used for all channels) or - the same number of channels as in the input array. - - Returns: - ndarray: The transformed image. - """ - assert isinstance(img, np.ndarray) - assert 0 <= np.min(img) and np.max(img) <= 255 - assert isinstance(lut_table, np.ndarray) - assert lut_table.shape == (256, ) - - return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) - - -def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - img (ndarray): Image to be processed. - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - - Returns: - ndarray: The processed image. - """ - assert isinstance(img, np.ndarray) - assert img.ndim == 2 - assert isinstance(clip_limit, (float, int)) - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - - clahe = cv2.createCLAHE(clip_limit, tile_grid_size) - return clahe.apply(np.array(img, dtype=np.uint8)) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/__init__.py deleted file mode 100755 index 8976a6654..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .deprecated_wrappers import Conv2d_deprecated as Conv2d -from .deprecated_wrappers import ConvTranspose2d_deprecated as ConvTranspose2d -from .deprecated_wrappers import Linear_deprecated as Linear -from .deprecated_wrappers import MaxPool2d_deprecated as MaxPool2d -from .info import (get_compiler_version, get_compiling_cuda_version, - get_onnxruntime_op_path) - -from .modulated_deform_conv import (ModulatedDeformConv2d, - ModulatedDeformConv2dPack, - modulated_deform_conv2d) - -from .sync_bn import SyncBatchNorm - - diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp deleted file mode 100755 index a1e926adb..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef COMMON_CUDA_HELPER -#define COMMON_CUDA_HELPER - -#include - -#define CUDA_1D_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) - -#define THREADS_PER_BLOCK 512 - -#define DIVUP(m, n) ((m) / (n) + ((m) % (n) > 0)) - -inline int GET_BLOCKS(const int N) { - int optimal_block_num = (N + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; - int max_block_num = 4096; - return std::min(optimal_block_num, max_block_num); -} - -template -__device__ T bilinear_interpolate(const T* input, const int height, - const int width, T y, T x, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) return 0; - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - int y_low = (int)y; - int x_low = (int)x; - int y_high; - int x_high; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - // do bilinear interpolation - T v1 = input[y_low * width + x_low]; - T v2 = input[y_low * width + x_high]; - T v3 = input[y_high * width + x_low]; - T v4 = input[y_high * width + x_high]; - T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - return val; -} - -template -__device__ void bilinear_interpolate_gradient( - const int height, const int width, T y, T x, T& w1, T& w2, T& w3, T& w4, - int& x_low, int& x_high, int& y_low, int& y_high, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - w1 = w2 = w3 = w4 = 0.; - x_low = x_high = y_low = y_high = -1; - return; - } - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - y_low = (int)y; - x_low = (int)x; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - - // reference in forward - // T v1 = input[y_low * width + x_low]; - // T v2 = input[y_low * width + x_high]; - // T v3 = input[y_high * width + x_low]; - // T v4 = input[y_high * width + x_high]; - // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - return; -} -#endif // COMMON_CUDA_HELPER diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/cuda/modulated_deform_conv_cuda_kernel.cuh b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/cuda/modulated_deform_conv_cuda_kernel.cuh deleted file mode 100755 index bd03df260..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/cuda/modulated_deform_conv_cuda_kernel.cuh +++ /dev/null @@ -1,400 +0,0 @@ -// Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -/*! - ******************* BEGIN Caffe Copyright Notice and Disclaimer - ***************** - * - * COPYRIGHT - * - * All contributions by the University of California: - * Copyright (c) 2014-2017 The Regents of the University of California (Regents) - * All rights reserved. - * - * All other contributions: - * Copyright (c) 2014-2017, the respective contributors - * All rights reserved. - * - * Caffe uses a shared copyright model: each contributor holds copyright over - * their contributions to Caffe. The project versioning records all such - * contribution and copyright details. If a contributor wants to further mark - * their specific copyright on a particular contribution, they should indicate - * their copyright solely in the commit message of the change when it is - * committed. - * - * LICENSE - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - *this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - *AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - *IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - *FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - *DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - *SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - *CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - *OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - *OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * CONTRIBUTION AGREEMENT - * - * By contributing to the BVLC/caffe repository through pull-request, comment, - * or otherwise, the contributor releases their content to the - * license and copyright terms herein. - * - ***************** END Caffe Copyright Notice and Disclaimer - ********************* - * - * Copyright (c) 2018 Microsoft - * Licensed under The MIT License [see LICENSE for details] - * \file modulated_deformable_im2col.cuh - * \brief Function definitions of converting an image to - * column matrix based on kernel, padding, dilation, and offset. - * These functions are mainly used in deformable convolution operators. - * \ref: https://arxiv.org/abs/1703.06211 - * \author Yuwen Xiong, Haozhi Qi, Jifeng Dai, Xizhou Zhu, Han Hu, Dazhi Cheng - */ - -// modified from -// https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/blob/mmdetection/mmdet/ops/dcn/src/deform_conv_cuda_kernel.cu - -#ifndef MODULATED_DEFORM_CONV_CUDA_KERNEL_CUH -#define MODULATED_DEFORM_CONV_CUDA_KERNEL_CUH - -#include -#ifdef MMCV_WITH_TRT -#include "common_cuda_helper.hpp" -#else // MMCV_WITH_TRT -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else // MMCV_USE_PARROTS -#include "pytorch_cuda_helper.hpp" -#endif // MMCV_USE_PARROTS -#endif // MMCV_WITH_TRT - -template -__device__ T dmcn_im2col_bilinear(const T *input, const int data_width, - const int height, const int width, T h, T w) { - int h_low = floorf(h); - int w_low = floorf(w); - int h_high = h_low + 1; - int w_high = w_low + 1; - - T lh = h - h_low; - T lw = w - w_low; - T hh = 1 - lh, hw = 1 - lw; - - T v1 = 0; - if (h_low >= 0 && w_low >= 0) v1 = input[h_low * data_width + w_low]; - T v2 = 0; - if (h_low >= 0 && w_high <= width - 1) - v2 = input[h_low * data_width + w_high]; - T v3 = 0; - if (h_high <= height - 1 && w_low >= 0) - v3 = input[h_high * data_width + w_low]; - T v4 = 0; - if (h_high <= height - 1 && w_high <= width - 1) - v4 = input[h_high * data_width + w_high]; - - T w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - return val; -} - -template -__device__ T dmcn_get_gradient_weight(T argmax_h, T argmax_w, const int h, - const int w, const int height, - const int width) { - if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || - argmax_w >= width) { - // empty - return 0; - } - - int argmax_h_low = floorf(argmax_h); - int argmax_w_low = floorf(argmax_w); - int argmax_h_high = argmax_h_low + 1; - int argmax_w_high = argmax_w_low + 1; - - T weight = 0; - if (h == argmax_h_low && w == argmax_w_low) - weight = (h + 1 - argmax_h) * (w + 1 - argmax_w); - if (h == argmax_h_low && w == argmax_w_high) - weight = (h + 1 - argmax_h) * (argmax_w + 1 - w); - if (h == argmax_h_high && w == argmax_w_low) - weight = (argmax_h + 1 - h) * (w + 1 - argmax_w); - if (h == argmax_h_high && w == argmax_w_high) - weight = (argmax_h + 1 - h) * (argmax_w + 1 - w); - return weight; -} - -template -__device__ T dmcn_get_coordinate_weight(T argmax_h, T argmax_w, - const int height, const int width, - const T *im_data, const int data_width, - const int bp_dir) { - if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || - argmax_w >= width) { - // empty - return 0; - } - - int argmax_h_low = floorf(argmax_h); - int argmax_w_low = floorf(argmax_w); - int argmax_h_high = argmax_h_low + 1; - int argmax_w_high = argmax_w_low + 1; - - T weight = 0; - - if (bp_dir == 0) { - if (argmax_h_low >= 0 && argmax_w_low >= 0) - weight += -1 * (argmax_w_low + 1 - argmax_w) * - im_data[argmax_h_low * data_width + argmax_w_low]; - if (argmax_h_low >= 0 && argmax_w_high <= width - 1) - weight += -1 * (argmax_w - argmax_w_low) * - im_data[argmax_h_low * data_width + argmax_w_high]; - if (argmax_h_high <= height - 1 && argmax_w_low >= 0) - weight += (argmax_w_low + 1 - argmax_w) * - im_data[argmax_h_high * data_width + argmax_w_low]; - if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) - weight += (argmax_w - argmax_w_low) * - im_data[argmax_h_high * data_width + argmax_w_high]; - } else if (bp_dir == 1) { - if (argmax_h_low >= 0 && argmax_w_low >= 0) - weight += -1 * (argmax_h_low + 1 - argmax_h) * - im_data[argmax_h_low * data_width + argmax_w_low]; - if (argmax_h_low >= 0 && argmax_w_high <= width - 1) - weight += (argmax_h_low + 1 - argmax_h) * - im_data[argmax_h_low * data_width + argmax_w_high]; - if (argmax_h_high <= height - 1 && argmax_w_low >= 0) - weight += -1 * (argmax_h - argmax_h_low) * - im_data[argmax_h_high * data_width + argmax_w_low]; - if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) - weight += (argmax_h - argmax_h_low) * - im_data[argmax_h_high * data_width + argmax_w_high]; - } - - return weight; -} - -template -__global__ void modulated_deformable_im2col_gpu_kernel( - const int n, const T *data_im, const T *data_offset, const T *data_mask, - const int height, const int width, const int kernel_h, const int kernel_w, - const int pad_h, const int pad_w, const int stride_h, const int stride_w, - const int dilation_h, const int dilation_w, - const int channel_per_deformable_group, const int batch_size, - const int num_channels, const int deformable_group, const int height_col, - const int width_col, T *data_col) { - CUDA_1D_KERNEL_LOOP(index, n) { - // index index of output matrix - const int w_col = index % width_col; - const int h_col = (index / width_col) % height_col; - const int b_col = (index / width_col / height_col) % batch_size; - const int c_im = (index / width_col / height_col) / batch_size; - const int c_col = c_im * kernel_h * kernel_w; - - // compute deformable group index - const int deformable_group_index = c_im / channel_per_deformable_group; - - const int h_in = h_col * stride_h - pad_h; - const int w_in = w_col * stride_w - pad_w; - - T *data_col_ptr = - data_col + - ((c_col * batch_size + b_col) * height_col + h_col) * width_col + w_col; - const T *data_im_ptr = - data_im + (b_col * num_channels + c_im) * height * width; - const T *data_offset_ptr = - data_offset + (b_col * deformable_group + deformable_group_index) * 2 * - kernel_h * kernel_w * height_col * width_col; - - const T *data_mask_ptr = - data_mask + (b_col * deformable_group + deformable_group_index) * - kernel_h * kernel_w * height_col * width_col; - - for (int i = 0; i < kernel_h; ++i) { - for (int j = 0; j < kernel_w; ++j) { - const int data_offset_h_ptr = - ((2 * (i * kernel_w + j)) * height_col + h_col) * width_col + w_col; - const int data_offset_w_ptr = - ((2 * (i * kernel_w + j) + 1) * height_col + h_col) * width_col + - w_col; - const int data_mask_hw_ptr = - ((i * kernel_w + j) * height_col + h_col) * width_col + w_col; - const T offset_h = data_offset_ptr[data_offset_h_ptr]; - const T offset_w = data_offset_ptr[data_offset_w_ptr]; - const T mask = data_mask_ptr[data_mask_hw_ptr]; - T val = static_cast(0); - const T h_im = h_in + i * dilation_h + offset_h; - const T w_im = w_in + j * dilation_w + offset_w; - if (h_im > -1 && w_im > -1 && h_im < height && w_im < width) - val = dmcn_im2col_bilinear(data_im_ptr, width, height, width, h_im, - w_im); - *data_col_ptr = val * mask; - data_col_ptr += batch_size * height_col * width_col; - } - } - } -} - -template -__global__ void modulated_deformable_col2im_gpu_kernel( - const int n, const T *data_col, const T *data_offset, const T *data_mask, - const int channels, const int height, const int width, const int kernel_h, - const int kernel_w, const int pad_h, const int pad_w, const int stride_h, - const int stride_w, const int dilation_h, const int dilation_w, - const int channel_per_deformable_group, const int batch_size, - const int deformable_group, const int height_col, const int width_col, - T *grad_im) { - CUDA_1D_KERNEL_LOOP(index, n) { - const int j = (index / width_col / height_col / batch_size) % kernel_w; - const int i = - (index / width_col / height_col / batch_size / kernel_w) % kernel_h; - const int c = - index / width_col / height_col / batch_size / kernel_w / kernel_h; - // compute the start and end of the output - - const int deformable_group_index = c / channel_per_deformable_group; - - int w_out = index % width_col; - int h_out = (index / width_col) % height_col; - int b = (index / width_col / height_col) % batch_size; - int w_in = w_out * stride_w - pad_w; - int h_in = h_out * stride_h - pad_h; - - const T *data_offset_ptr = - data_offset + (b * deformable_group + deformable_group_index) * 2 * - kernel_h * kernel_w * height_col * width_col; - const T *data_mask_ptr = - data_mask + (b * deformable_group + deformable_group_index) * kernel_h * - kernel_w * height_col * width_col; - const int data_offset_h_ptr = - ((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out; - const int data_offset_w_ptr = - ((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out; - const int data_mask_hw_ptr = - ((i * kernel_w + j) * height_col + h_out) * width_col + w_out; - const T offset_h = data_offset_ptr[data_offset_h_ptr]; - const T offset_w = data_offset_ptr[data_offset_w_ptr]; - const T mask = data_mask_ptr[data_mask_hw_ptr]; - const T cur_inv_h_data = h_in + i * dilation_h + offset_h; - const T cur_inv_w_data = w_in + j * dilation_w + offset_w; - - const T cur_top_grad = data_col[index] * mask; - const int cur_h = (int)cur_inv_h_data; - const int cur_w = (int)cur_inv_w_data; - for (int dy = -2; dy <= 2; dy++) { - for (int dx = -2; dx <= 2; dx++) { - if (cur_h + dy >= 0 && cur_h + dy < height && cur_w + dx >= 0 && - cur_w + dx < width && abs(cur_inv_h_data - (cur_h + dy)) < 1 && - abs(cur_inv_w_data - (cur_w + dx)) < 1) { - int cur_bottom_grad_pos = - ((b * channels + c) * height + cur_h + dy) * width + cur_w + dx; - T weight = - dmcn_get_gradient_weight(cur_inv_h_data, cur_inv_w_data, - cur_h + dy, cur_w + dx, height, width); - atomicAdd(grad_im + cur_bottom_grad_pos, weight * cur_top_grad); - } - } - } - } -} - -template -__global__ void modulated_deformable_col2im_coord_gpu_kernel( - const int n, const T *data_col, const T *data_im, const T *data_offset, - const T *data_mask, const int channels, const int height, const int width, - const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int channel_per_deformable_group, - const int batch_size, const int offset_channels, const int deformable_group, - const int height_col, const int width_col, T *grad_offset, T *grad_mask) { - CUDA_1D_KERNEL_LOOP(index, n) { - T val = 0, mval = 0; - int w = index % width_col; - int h = (index / width_col) % height_col; - int c = (index / width_col / height_col) % offset_channels; - int b = (index / width_col / height_col) / offset_channels; - // compute the start and end of the output - - const int deformable_group_index = c / (2 * kernel_h * kernel_w); - const int col_step = kernel_h * kernel_w; - int cnt = 0; - const T *data_col_ptr = data_col + deformable_group_index * - channel_per_deformable_group * - batch_size * width_col * height_col; - const T *data_im_ptr = - data_im + (b * deformable_group + deformable_group_index) * - channel_per_deformable_group / kernel_h / kernel_w * - height * width; - const T *data_offset_ptr = - data_offset + (b * deformable_group + deformable_group_index) * 2 * - kernel_h * kernel_w * height_col * width_col; - const T *data_mask_ptr = - data_mask + (b * deformable_group + deformable_group_index) * kernel_h * - kernel_w * height_col * width_col; - - const int offset_c = c - deformable_group_index * 2 * kernel_h * kernel_w; - - for (int col_c = (offset_c / 2); col_c < channel_per_deformable_group; - col_c += col_step) { - const int col_pos = - (((col_c * batch_size + b) * height_col) + h) * width_col + w; - const int bp_dir = offset_c % 2; - - int j = (col_pos / width_col / height_col / batch_size) % kernel_w; - int i = - (col_pos / width_col / height_col / batch_size / kernel_w) % kernel_h; - int w_out = col_pos % width_col; - int h_out = (col_pos / width_col) % height_col; - int w_in = w_out * stride_w - pad_w; - int h_in = h_out * stride_h - pad_h; - const int data_offset_h_ptr = - (((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out); - const int data_offset_w_ptr = - (((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + - w_out); - const int data_mask_hw_ptr = - (((i * kernel_w + j) * height_col + h_out) * width_col + w_out); - const T offset_h = data_offset_ptr[data_offset_h_ptr]; - const T offset_w = data_offset_ptr[data_offset_w_ptr]; - const T mask = data_mask_ptr[data_mask_hw_ptr]; - T inv_h = h_in + i * dilation_h + offset_h; - T inv_w = w_in + j * dilation_w + offset_w; - if (inv_h <= -1 || inv_w <= -1 || inv_h >= height || inv_w >= width) - inv_h = inv_w = -2; - else - mval += data_col_ptr[col_pos] * - dmcn_im2col_bilinear(data_im_ptr + cnt * height * width, width, - height, width, inv_h, inv_w); - const T weight = dmcn_get_coordinate_weight( - inv_h, inv_w, height, width, data_im_ptr + cnt * height * width, - width, bp_dir); - val += weight * data_col_ptr[col_pos] * mask; - cnt += 1; - } - // KERNEL_ASSIGN(grad_offset[index], offset_req, val); - grad_offset[index] = val; - if (offset_c % 2 == 0) - // KERNEL_ASSIGN(grad_mask[(((b * deformable_group + - // deformable_group_index) * kernel_h * kernel_w + offset_c / 2) * - // height_col + h) * width_col + w], mask_req, mval); - grad_mask[(((b * deformable_group + deformable_group_index) * kernel_h * - kernel_w + - offset_c / 2) * - height_col + - h) * - width_col + - w] = mval; - } -} - -#endif // MODULATED_DEFORM_CONV_CUDA_KERNEL_CUH diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh deleted file mode 100755 index da6ab4f00..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh +++ /dev/null @@ -1,332 +0,0 @@ -// Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SYNCBN_CUDA_KERNEL_CUH -#define SYNCBN_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sync_bn_forward_mean_cuda_kernel(const T *input, float *mean, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += input[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_mean_cuda_kernel(const phalf *input, - float *mean, int num, - int channels, int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += static_cast(input[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_var_cuda_kernel(const T *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = input[index] - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_var_cuda_kernel(const phalf *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = static_cast(input[index]) - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_output_cuda_kernel( - const T *input, const float *mean, const float *var, float *running_mean, - float *running_var, const float *weight, const float *bias, float *norm, - float *std, T *output, int num, int channels, int spatial, float eps, - float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = (input[index] - mean_value) / std_value; - output[index] = norm[index] * weight_value + bias_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - (input[index] - mean_value) / std_value * weight_value + bias_value; - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = norm[index] = (input[index] - mean_value) / std_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = (input[index] - mean_value) / std_value; - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template <> -__global__ void sync_bn_forward_output_cuda_kernel( - const phalf *input, const float *mean, const float *var, - float *running_mean, float *running_var, const float *weight, - const float *bias, float *norm, float *std, phalf *output, int num, - int channels, int spatial, float eps, float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = - static_cast(norm[index] * weight_value + bias_value); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - static_cast((static_cast(input[index]) - mean_value) / - std_value * weight_value + - bias_value); - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = static_cast(norm[index]); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = static_cast( - (static_cast(input[index]) - mean_value) / std_value); - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template -__global__ void sync_bn_backward_param_cuda_kernel(const T *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += grad_output[index] * norm[index]; - buffer2[tid] += grad_output[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template <> -__global__ void sync_bn_backward_param_cuda_kernel(const phalf *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += static_cast(grad_output[index]) * norm[index]; - buffer2[tid] += static_cast(grad_output[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const T *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, T *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = - weight[c] * - (grad_output[index] - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]; - } -} - -template <> -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const phalf *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, phalf *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = static_cast( - weight[c] * - (static_cast(grad_output[index]) - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]); - } -} - -#endif // SYNCBN_CUDA_KERNEL_CUH diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp deleted file mode 100755 index c7f9f35b7..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef PYTORCH_CPP_HELPER -#define PYTORCH_CPP_HELPER -#include - -#include - -using namespace at; - -#define DIVUP(m, n) ((m) / (n) + ((m) % (n) > 0)) - -#define CHECK_CUDA(x) \ - TORCH_CHECK(x.device().is_cuda(), #x " must be a CUDA tensor") -#define CHECK_CPU(x) \ - TORCH_CHECK(!x.device().is_cuda(), #x " must be a CPU tensor") -#define CHECK_CONTIGUOUS(x) \ - TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") -#define CHECK_CUDA_INPUT(x) \ - CHECK_CUDA(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_CPU_INPUT(x) \ - CHECK_CPU(x); \ - CHECK_CONTIGUOUS(x) - -#endif // PYTORCH_CPP_HELPER diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp deleted file mode 100755 index 9869b535f..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PYTORCH_CUDA_HELPER -#define PYTORCH_CUDA_HELPER - -#include -#include -#include - -#include -#include - -#include "common_cuda_helper.hpp" - -using at::Half; -using at::Tensor; -using phalf = at::Half; - -#define __PHALF(x) (x) - -#endif // PYTORCH_CUDA_HELPER diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/cuda/modulated_deform_conv_cuda.cu b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/cuda/modulated_deform_conv_cuda.cu deleted file mode 100755 index 63b5140e5..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/cuda/modulated_deform_conv_cuda.cu +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -// Copyright (c) OpenMMLab. All rights reserved -#include "modulated_deform_conv_cuda_kernel.cuh" -#include "pytorch_cuda_helper.hpp" - -void modulated_deformable_im2col_cuda( - const Tensor data_im, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor data_col) { - // num_axes should be smaller than block size - const int channel_per_deformable_group = channels / deformable_group; - const int num_kernels = channels * batch_size * height_col * width_col; - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - data_im.scalar_type(), "modulated_deformable_im2col_gpu", ([&] { - const scalar_t *data_im_ = data_im.data_ptr(); - const scalar_t *data_offset_ = data_offset.data_ptr(); - const scalar_t *data_mask_ = data_mask.data_ptr(); - scalar_t *data_col_ = data_col.data_ptr(); - - modulated_deformable_im2col_gpu_kernel<<< - GET_BLOCKS(num_kernels), THREADS_PER_BLOCK, 0, - at::cuda::getCurrentCUDAStream()>>>( - num_kernels, data_im_, data_offset_, data_mask_, height_im, - width_im, kernel_h, kenerl_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, channel_per_deformable_group, batch_size, - channels, deformable_group, height_col, width_col, data_col_); - })); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void modulated_deformable_col2im_cuda( - const Tensor data_col, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor grad_im) { - const int channel_per_deformable_group = channels / deformable_group; - const int num_kernels = - channels * kernel_h * kernel_w * batch_size * height_col * width_col; - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - data_col.scalar_type(), "modulated_deformable_col2im_gpu", ([&] { - const scalar_t *data_col_ = data_col.data_ptr(); - const scalar_t *data_offset_ = data_offset.data_ptr(); - const scalar_t *data_mask_ = data_mask.data_ptr(); - scalar_t *grad_im_ = grad_im.data_ptr(); - - modulated_deformable_col2im_gpu_kernel<<< - GET_BLOCKS(num_kernels), THREADS_PER_BLOCK, 0, - at::cuda::getCurrentCUDAStream()>>>( - num_kernels, data_col_, data_offset_, data_mask_, channels, - height_im, width_im, kernel_h, kernel_w, pad_h, pad_w, stride_h, - stride_w, dilation_h, dilation_w, channel_per_deformable_group, - batch_size, deformable_group, height_col, width_col, grad_im_); - })); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void modulated_deformable_col2im_coord_cuda( - const Tensor data_col, const Tensor data_im, const Tensor data_offset, - const Tensor data_mask, const int batch_size, const int channels, - const int height_im, const int width_im, const int height_col, - const int width_col, const int kernel_h, const int kernel_w, - const int pad_h, const int pad_w, const int stride_h, const int stride_w, - const int dilation_h, const int dilation_w, const int deformable_group, - Tensor grad_offset, Tensor grad_mask) { - const int num_kernels = batch_size * height_col * width_col * 2 * kernel_h * - kernel_w * deformable_group; - const int channel_per_deformable_group = - channels * kernel_h * kernel_w / deformable_group; - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - data_col.scalar_type(), "modulated_deformable_col2im_coord_gpu", ([&] { - const scalar_t *data_col_ = data_col.data_ptr(); - const scalar_t *data_im_ = data_im.data_ptr(); - const scalar_t *data_offset_ = data_offset.data_ptr(); - const scalar_t *data_mask_ = data_mask.data_ptr(); - scalar_t *grad_offset_ = grad_offset.data_ptr(); - scalar_t *grad_mask_ = grad_mask.data_ptr(); - - modulated_deformable_col2im_coord_gpu_kernel<<< - GET_BLOCKS(num_kernels), THREADS_PER_BLOCK, 0, - at::cuda::getCurrentCUDAStream()>>>( - num_kernels, data_col_, data_im_, data_offset_, data_mask_, - channels, height_im, width_im, kernel_h, kernel_w, pad_h, pad_w, - stride_h, stride_w, dilation_h, dilation_w, - channel_per_deformable_group, batch_size, - 2 * kernel_h * kernel_w * deformable_group, deformable_group, - height_col, width_col, grad_offset_, grad_mask_); - })); - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu deleted file mode 100755 index 19b9f149c..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sync_bn_cuda_kernel.cuh" - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_mean_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_var_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), num, channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_output_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), running_mean.data_ptr(), - running_var.data_ptr(), weight.data_ptr(), - bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), output.data_ptr(), num, - channels, spatial, eps, momentum, group_size); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias) { - int num = grad_output.size(0); - int channels = grad_output.size(1); - int spatial = grad_output.size(2); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_param_cuda_kernel", [&] { - sync_bn_backward_param_cuda_kernel - <<>>( - grad_output.data_ptr(), norm.data_ptr(), - grad_weight.data_ptr(), grad_bias.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - int output_size = grad_input.numel(); - int num = grad_input.size(0); - int channels = grad_input.size(1); - int spatial = grad_input.size(2); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_data_cuda_kernel", [&] { - sync_bn_backward_data_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - weight.data_ptr(), grad_weight.data_ptr(), - grad_bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), grad_input.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/info.cpp b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/info.cpp deleted file mode 100755 index a08d227d4..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/info.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// modified from -// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/vision.cpp -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF -#include -int get_cudart_version() { return CUDART_VERSION; } -#endif -#endif - -std::string get_compiling_cuda_version() { -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF - std::ostringstream oss; - // copied from - // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/cuda/detail/CUDAHooks.cpp#L231 - auto printCudaStyleVersion = [&](int v) { - oss << (v / 1000) << "." << (v / 10 % 100); - if (v % 10 != 0) { - oss << "." << (v % 10); - } - }; - printCudaStyleVersion(get_cudart_version()); - return oss.str(); -#else - return std::string("rocm not available"); -#endif -#else - return std::string("not available"); -#endif -} - -// similar to -// https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/Version.cpp -std::string get_compiler_version() { - std::ostringstream ss; -#if defined(__GNUC__) -#ifndef __clang__ - { ss << "GCC " << __GNUC__ << "." << __GNUC_MINOR__; } -#endif -#endif - -#if defined(__clang_major__) - { - ss << "clang " << __clang_major__ << "." << __clang_minor__ << "." - << __clang_patchlevel__; - } -#endif - -#if defined(_MSC_VER) - { ss << "MSVC " << _MSC_FULL_VER; } -#endif - return ss.str(); -} diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv.cpp b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv.cpp deleted file mode 100755 index c5e78c3a3..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv.cpp +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA - -void modulated_deformable_im2col_cuda( - const Tensor data_im, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor data_col); - -void modulated_deformable_col2im_cuda( - const Tensor data_col, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor grad_im); - -void modulated_deformable_col2im_coord_cuda( - const Tensor data_col, const Tensor data_im, const Tensor data_offset, - const Tensor data_mask, const int batch_size, const int channels, - const int height_im, const int width_im, const int height_col, - const int width_col, const int kernel_h, const int kernel_w, - const int pad_h, const int pad_w, const int stride_h, const int stride_w, - const int dilation_h, const int dilation_w, const int deformable_group, - Tensor grad_offset, Tensor grad_mask); - -#endif - -void modulated_deformable_im2col_cpu( - const Tensor data_im, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor data_col); - -void modulated_deformable_col2im_cpu( - const Tensor data_col, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor grad_im); - -void modulated_deformable_col2im_coord_cpu( - const Tensor data_col, const Tensor data_im, const Tensor data_offset, - const Tensor data_mask, const int batch_size, const int channels, - const int height_im, const int width_im, const int height_col, - const int width_col, const int kernel_h, const int kernel_w, - const int pad_h, const int pad_w, const int stride_h, const int stride_w, - const int dilation_h, const int dilation_w, const int deformable_group, - Tensor grad_offset, Tensor grad_mask); - -void modulated_deform_conv_forward( - Tensor input, Tensor weight, Tensor bias, Tensor ones, Tensor offset, - Tensor mask, Tensor output, Tensor columns, int kernel_h, int kernel_w, - const int stride_h, const int stride_w, const int pad_h, const int pad_w, - const int dilation_h, const int dilation_w, const int group, - const int deformable_group, const bool with_bias) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(bias); - CHECK_CUDA_INPUT(ones); - CHECK_CUDA_INPUT(offset); - CHECK_CUDA_INPUT(mask); - CHECK_CUDA_INPUT(output); - CHECK_CUDA_INPUT(columns); - -#else - AT_ERROR("ModulatedDeformConv is not compiled with GPU support"); -#endif - } else { - CHECK_CPU_INPUT(input); - CHECK_CPU_INPUT(weight); - CHECK_CPU_INPUT(bias); - CHECK_CPU_INPUT(ones); - CHECK_CPU_INPUT(offset); - CHECK_CPU_INPUT(mask); - CHECK_CPU_INPUT(output); - CHECK_CPU_INPUT(columns); - } - - at::DeviceGuard guard(input.device()); - - const int batch = input.size(0); - const int channels = input.size(1); - const int height = input.size(2); - const int width = input.size(3); - - const int channels_out = weight.size(0); - const int channels_kernel = weight.size(1); - const int kernel_h_ = weight.size(2); - const int kernel_w_ = weight.size(3); - - if (kernel_h_ != kernel_h || kernel_w_ != kernel_w) - AT_ERROR("Input shape and kernel shape won't match: (%d x %d vs %d x %d).", - kernel_h_, kernel_w, kernel_h_, kernel_w_); - if (channels != channels_kernel * group) - AT_ERROR("Input shape and kernel channels won't match: (%d vs %d).", - channels, channels_kernel * group); - - const int height_out = - (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1; - const int width_out = - (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1; - - if (ones.ndimension() != 2 || - ones.size(0) * ones.size(1) < height_out * width_out) { - // Resize plane and fill with ones... - ones = at::ones({height_out, width_out}, input.options()); - } - - // resize output - output = output.view({batch, channels_out, height_out, width_out}).zero_(); - // resize temporary columns - columns = - at::zeros({channels * kernel_h * kernel_w, 1 * height_out * width_out}, - input.options()); - - output = output.view({output.size(0), group, output.size(1) / group, - output.size(2), output.size(3)}); - - for (int b = 0; b < batch; b++) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - modulated_deformable_im2col_cuda( - input[b], offset[b], mask[b], 1, channels, height, width, height_out, - width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, deformable_group, columns); -#endif - } else { - modulated_deformable_im2col_cpu( - input[b], offset[b], mask[b], 1, channels, height, width, height_out, - width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, deformable_group, columns); - } - - // divide into group - weight = weight.view({group, weight.size(0) / group, weight.size(1), - weight.size(2), weight.size(3)}); - columns = columns.view({group, columns.size(0) / group, columns.size(1)}); - - for (int g = 0; g < group; g++) { - output[b][g] = output[b][g] - .flatten(1) - .addmm_(weight[g].flatten(1), columns[g]) - .view_as(output[b][g]); - } - - weight = weight.view({weight.size(0) * weight.size(1), weight.size(2), - weight.size(3), weight.size(4)}); - columns = - columns.view({columns.size(0) * columns.size(1), columns.size(2)}); - } - - output = output.view({output.size(0), output.size(1) * output.size(2), - output.size(3), output.size(4)}); - - if (with_bias) { - output += bias.view({1, bias.size(0), 1, 1}); - } -} - -void modulated_deform_conv_backward( - Tensor input, Tensor weight, Tensor bias, Tensor ones, Tensor offset, - Tensor mask, Tensor columns, Tensor grad_input, Tensor grad_weight, - Tensor grad_bias, Tensor grad_offset, Tensor grad_mask, Tensor grad_output, - int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, - int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, - const bool with_bias) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(bias); - CHECK_CUDA_INPUT(ones); - CHECK_CUDA_INPUT(offset); - CHECK_CUDA_INPUT(mask); - CHECK_CUDA_INPUT(columns); - CHECK_CUDA_INPUT(grad_input); - CHECK_CUDA_INPUT(grad_weight); - CHECK_CUDA_INPUT(grad_bias); - CHECK_CUDA_INPUT(grad_offset); - CHECK_CUDA_INPUT(grad_mask); - CHECK_CUDA_INPUT(grad_output); - -#else - AT_ERROR("ModulatedDeformConv is not compiled with GPU support"); -#endif - } else { - CHECK_CPU_INPUT(input); - CHECK_CPU_INPUT(weight); - CHECK_CPU_INPUT(bias); - CHECK_CPU_INPUT(ones); - CHECK_CPU_INPUT(offset); - CHECK_CPU_INPUT(mask); - CHECK_CPU_INPUT(columns); - CHECK_CPU_INPUT(grad_input); - CHECK_CPU_INPUT(grad_weight); - CHECK_CPU_INPUT(grad_bias); - CHECK_CPU_INPUT(grad_offset); - CHECK_CPU_INPUT(grad_mask); - CHECK_CPU_INPUT(grad_output); - } - - at::DeviceGuard guard(input.device()); - - const int batch = input.size(0); - const int channels = input.size(1); - const int height = input.size(2); - const int width = input.size(3); - - const int channels_kernel = weight.size(1); - const int kernel_h_ = weight.size(2); - const int kernel_w_ = weight.size(3); - if (kernel_h_ != kernel_h || kernel_w_ != kernel_w) - AT_ERROR("Input shape and kernel shape won't match: (%d x %d vs %d x %d).", - kernel_h_, kernel_w, kernel_h_, kernel_w_); - if (channels != channels_kernel * group) - AT_ERROR("Input shape and kernel channels won't match: (%d vs %d).", - channels, channels_kernel * group); - - const int height_out = - (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1; - const int width_out = - (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1; - - if (ones.ndimension() != 2 || - ones.size(0) * ones.size(1) < height_out * width_out) { - // Resize plane and fill with ones... - ones = at::ones({height_out, width_out}, input.options()); - } - - grad_input = grad_input.view({batch, channels, height, width}); - columns = at::zeros({channels * kernel_h * kernel_w, height_out * width_out}, - input.options()); - - grad_output = - grad_output.view({grad_output.size(0), group, grad_output.size(1) / group, - grad_output.size(2), grad_output.size(3)}); - - for (int b = 0; b < batch; b++) { - // divide int group - columns = columns.view({group, columns.size(0) / group, columns.size(1)}); - weight = weight.view({group, weight.size(0) / group, weight.size(1), - weight.size(2), weight.size(3)}); - - for (int g = 0; g < group; g++) { - columns[g].addmm_(weight[g].flatten(1).transpose(0, 1), - grad_output[b][g].flatten(1), 0.0f, 1.0f); - } - - columns = - columns.view({columns.size(0) * columns.size(1), columns.size(2)}); - weight = weight.view({weight.size(0) * weight.size(1), weight.size(2), - weight.size(3), weight.size(4)}); - - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - // gradient w.r.t. input coordinate data - modulated_deformable_col2im_coord_cuda( - columns, input[b], offset[b], mask[b], 1, channels, height, width, - height_out, width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, - stride_w, dilation_h, dilation_w, deformable_group, grad_offset[b], - grad_mask[b]); - // gradient w.r.t. input data - modulated_deformable_col2im_cuda( - columns, offset[b], mask[b], 1, channels, height, width, height_out, - width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, deformable_group, grad_input[b]); - - // gradient w.r.t. weight, dWeight should accumulate across the batch and - // group - modulated_deformable_im2col_cuda( - input[b], offset[b], mask[b], 1, channels, height, width, height_out, - width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, deformable_group, columns); -#endif - } else { - // gradient w.r.t. input coordinate data - modulated_deformable_col2im_coord_cpu( - columns, input[b], offset[b], mask[b], 1, channels, height, width, - height_out, width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, - stride_w, dilation_h, dilation_w, deformable_group, grad_offset[b], - grad_mask[b]); - // gradient w.r.t. input data - modulated_deformable_col2im_cpu( - columns, offset[b], mask[b], 1, channels, height, width, height_out, - width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, deformable_group, grad_input[b]); - // gradient w.r.t. weight, dWeight should accumulate across the batch and - // group - modulated_deformable_im2col_cpu( - input[b], offset[b], mask[b], 1, channels, height, width, height_out, - width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, deformable_group, columns); - } - - columns = columns.view({group, columns.size(0) / group, columns.size(1)}); - grad_weight = grad_weight.view({group, grad_weight.size(0) / group, - grad_weight.size(1), grad_weight.size(2), - grad_weight.size(3)}); - if (with_bias) - grad_bias = grad_bias.view({group, grad_bias.size(0) / group}); - - for (int g = 0; g < group; g++) { - grad_weight[g] = - grad_weight[g] - .flatten(1) - .addmm_(grad_output[b][g].flatten(1), columns[g].transpose(0, 1)) - .view_as(grad_weight[g]); - if (with_bias) { - grad_bias[g] = - grad_bias[g] - .view({-1, 1}) - .addmm_(grad_output[b][g].flatten(1), ones.view({-1, 1})) - .view(-1); - } - } - - columns = - columns.view({columns.size(0) * columns.size(1), columns.size(2)}); - grad_weight = grad_weight.view({grad_weight.size(0) * grad_weight.size(1), - grad_weight.size(2), grad_weight.size(3), - grad_weight.size(4)}); - if (with_bias) - grad_bias = grad_bias.view({grad_bias.size(0) * grad_bias.size(1)}); - } - grad_output = grad_output.view({grad_output.size(0) * grad_output.size(1), - grad_output.size(2), grad_output.size(3), - grad_output.size(4)}); -} diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv_cpu.cpp b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv_cpu.cpp deleted file mode 100755 index 89a81d733..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv_cpu.cpp +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -template -T dmcn_im2col_bilinear_cpu(const T *input, const int data_width, - const int height, const int width, T h, T w) { - int h_low = floorf(h); - int w_low = floorf(w); - int h_high = h_low + 1; - int w_high = w_low + 1; - - T lh = h - h_low; - T lw = w - w_low; - T hh = 1 - lh, hw = 1 - lw; - - T v1 = 0; - if (h_low >= 0 && w_low >= 0) v1 = input[h_low * data_width + w_low]; - T v2 = 0; - if (h_low >= 0 && w_high <= width - 1) - v2 = input[h_low * data_width + w_high]; - T v3 = 0; - if (h_high <= height - 1 && w_low >= 0) - v3 = input[h_high * data_width + w_low]; - T v4 = 0; - if (h_high <= height - 1 && w_high <= width - 1) - v4 = input[h_high * data_width + w_high]; - - T w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - return val; -} - -template -T dmcn_get_gradient_weight_cpu(T argmax_h, T argmax_w, const int h, const int w, - const int height, const int width) { - if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || - argmax_w >= width) { - // empty - return 0; - } - - int argmax_h_low = floorf(argmax_h); - int argmax_w_low = floorf(argmax_w); - int argmax_h_high = argmax_h_low + 1; - int argmax_w_high = argmax_w_low + 1; - - T weight = 0; - if (h == argmax_h_low && w == argmax_w_low) - weight = (h + 1 - argmax_h) * (w + 1 - argmax_w); - if (h == argmax_h_low && w == argmax_w_high) - weight = (h + 1 - argmax_h) * (argmax_w + 1 - w); - if (h == argmax_h_high && w == argmax_w_low) - weight = (argmax_h + 1 - h) * (w + 1 - argmax_w); - if (h == argmax_h_high && w == argmax_w_high) - weight = (argmax_h + 1 - h) * (argmax_w + 1 - w); - return weight; -} - -template -T dmcn_get_coordinate_weight_cpu(T argmax_h, T argmax_w, const int height, - const int width, const T *im_data, - const int data_width, const int bp_dir) { - if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || - argmax_w >= width) { - // empty - return 0; - } - - int argmax_h_low = floorf(argmax_h); - int argmax_w_low = floorf(argmax_w); - int argmax_h_high = argmax_h_low + 1; - int argmax_w_high = argmax_w_low + 1; - - T weight = 0; - - if (bp_dir == 0) { - if (argmax_h_low >= 0 && argmax_w_low >= 0) - weight += -1 * (argmax_w_low + 1 - argmax_w) * - im_data[argmax_h_low * data_width + argmax_w_low]; - if (argmax_h_low >= 0 && argmax_w_high <= width - 1) - weight += -1 * (argmax_w - argmax_w_low) * - im_data[argmax_h_low * data_width + argmax_w_high]; - if (argmax_h_high <= height - 1 && argmax_w_low >= 0) - weight += (argmax_w_low + 1 - argmax_w) * - im_data[argmax_h_high * data_width + argmax_w_low]; - if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) - weight += (argmax_w - argmax_w_low) * - im_data[argmax_h_high * data_width + argmax_w_high]; - } else if (bp_dir == 1) { - if (argmax_h_low >= 0 && argmax_w_low >= 0) - weight += -1 * (argmax_h_low + 1 - argmax_h) * - im_data[argmax_h_low * data_width + argmax_w_low]; - if (argmax_h_low >= 0 && argmax_w_high <= width - 1) - weight += (argmax_h_low + 1 - argmax_h) * - im_data[argmax_h_low * data_width + argmax_w_high]; - if (argmax_h_high <= height - 1 && argmax_w_low >= 0) - weight += -1 * (argmax_h - argmax_h_low) * - im_data[argmax_h_high * data_width + argmax_w_low]; - if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) - weight += (argmax_h - argmax_h_low) * - im_data[argmax_h_high * data_width + argmax_w_high]; - } - - return weight; -} - -template -void modulated_deformable_im2col_cpu_kernel( - const int n, const T *data_im, const T *data_offset, const T *data_mask, - const int height, const int width, const int kernel_h, const int kernel_w, - const int pad_h, const int pad_w, const int stride_h, const int stride_w, - const int dilation_h, const int dilation_w, - const int channel_per_deformable_group, const int batch_size, - const int num_channels, const int deformable_group, const int height_col, - const int width_col, T *data_col) { - for (int index = 0; index < n; index++) { - // index index of output matrix - const int w_col = index % width_col; - const int h_col = (index / width_col) % height_col; - const int b_col = (index / width_col / height_col) % batch_size; - const int c_im = (index / width_col / height_col) / batch_size; - const int c_col = c_im * kernel_h * kernel_w; - - // compute deformable group index - const int deformable_group_index = c_im / channel_per_deformable_group; - - const int h_in = h_col * stride_h - pad_h; - const int w_in = w_col * stride_w - pad_w; - - T *data_col_ptr = - data_col + - ((c_col * batch_size + b_col) * height_col + h_col) * width_col + w_col; - const T *data_im_ptr = - data_im + (b_col * num_channels + c_im) * height * width; - const T *data_offset_ptr = - data_offset + (b_col * deformable_group + deformable_group_index) * 2 * - kernel_h * kernel_w * height_col * width_col; - - const T *data_mask_ptr = - data_mask + (b_col * deformable_group + deformable_group_index) * - kernel_h * kernel_w * height_col * width_col; - - for (int i = 0; i < kernel_h; ++i) { - for (int j = 0; j < kernel_w; ++j) { - const int data_offset_h_ptr = - ((2 * (i * kernel_w + j)) * height_col + h_col) * width_col + w_col; - const int data_offset_w_ptr = - ((2 * (i * kernel_w + j) + 1) * height_col + h_col) * width_col + - w_col; - const int data_mask_hw_ptr = - ((i * kernel_w + j) * height_col + h_col) * width_col + w_col; - const T offset_h = data_offset_ptr[data_offset_h_ptr]; - const T offset_w = data_offset_ptr[data_offset_w_ptr]; - const T mask = data_mask_ptr[data_mask_hw_ptr]; - T val = static_cast(0); - const T h_im = h_in + i * dilation_h + offset_h; - const T w_im = w_in + j * dilation_w + offset_w; - if (h_im > -1 && w_im > -1 && h_im < height && w_im < width) - val = dmcn_im2col_bilinear_cpu(data_im_ptr, width, height, width, - h_im, w_im); - *data_col_ptr = val * mask; - data_col_ptr += batch_size * height_col * width_col; - } - } - } -} - -template -void modulated_deformable_col2im_cpu_kernel( - const int n, const T *data_col, const T *data_offset, const T *data_mask, - const int channels, const int height, const int width, const int kernel_h, - const int kernel_w, const int pad_h, const int pad_w, const int stride_h, - const int stride_w, const int dilation_h, const int dilation_w, - const int channel_per_deformable_group, const int batch_size, - const int deformable_group, const int height_col, const int width_col, - T *grad_im) { - for (int index = 0; index < n; index++) { - const int j = (index / width_col / height_col / batch_size) % kernel_w; - const int i = - (index / width_col / height_col / batch_size / kernel_w) % kernel_h; - const int c = - index / width_col / height_col / batch_size / kernel_w / kernel_h; - // compute the start and end of the output - - const int deformable_group_index = c / channel_per_deformable_group; - - int w_out = index % width_col; - int h_out = (index / width_col) % height_col; - int b = (index / width_col / height_col) % batch_size; - int w_in = w_out * stride_w - pad_w; - int h_in = h_out * stride_h - pad_h; - - const T *data_offset_ptr = - data_offset + (b * deformable_group + deformable_group_index) * 2 * - kernel_h * kernel_w * height_col * width_col; - const T *data_mask_ptr = - data_mask + (b * deformable_group + deformable_group_index) * kernel_h * - kernel_w * height_col * width_col; - const int data_offset_h_ptr = - ((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out; - const int data_offset_w_ptr = - ((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out; - const int data_mask_hw_ptr = - ((i * kernel_w + j) * height_col + h_out) * width_col + w_out; - const T offset_h = data_offset_ptr[data_offset_h_ptr]; - const T offset_w = data_offset_ptr[data_offset_w_ptr]; - const T mask = data_mask_ptr[data_mask_hw_ptr]; - const T cur_inv_h_data = h_in + i * dilation_h + offset_h; - const T cur_inv_w_data = w_in + j * dilation_w + offset_w; - - const T cur_top_grad = data_col[index] * mask; - const int cur_h = (int)cur_inv_h_data; - const int cur_w = (int)cur_inv_w_data; - for (int dy = -2; dy <= 2; dy++) { - for (int dx = -2; dx <= 2; dx++) { - if (cur_h + dy >= 0 && cur_h + dy < height && cur_w + dx >= 0 && - cur_w + dx < width && abs(cur_inv_h_data - (cur_h + dy)) < 1 && - abs(cur_inv_w_data - (cur_w + dx)) < 1) { - int cur_bottom_grad_pos = - ((b * channels + c) * height + cur_h + dy) * width + cur_w + dx; - T weight = dmcn_get_gradient_weight_cpu(cur_inv_h_data, - cur_inv_w_data, cur_h + dy, - cur_w + dx, height, width); - *(grad_im + cur_bottom_grad_pos) += weight * cur_top_grad; - } - } - } - } -} - -template -void modulated_deformable_col2im_coord_cpu_kernel( - const int n, const T *data_col, const T *data_im, const T *data_offset, - const T *data_mask, const int channels, const int height, const int width, - const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int channel_per_deformable_group, - const int batch_size, const int offset_channels, const int deformable_group, - const int height_col, const int width_col, T *grad_offset, T *grad_mask) { - for (int index = 0; index < n; index++) { - T val = 0, mval = 0; - int w = index % width_col; - int h = (index / width_col) % height_col; - int c = (index / width_col / height_col) % offset_channels; - int b = (index / width_col / height_col) / offset_channels; - // compute the start and end of the output - - const int deformable_group_index = c / (2 * kernel_h * kernel_w); - const int col_step = kernel_h * kernel_w; - int cnt = 0; - const T *data_col_ptr = data_col + deformable_group_index * - channel_per_deformable_group * - batch_size * width_col * height_col; - const T *data_im_ptr = - data_im + (b * deformable_group + deformable_group_index) * - channel_per_deformable_group / kernel_h / kernel_w * - height * width; - const T *data_offset_ptr = - data_offset + (b * deformable_group + deformable_group_index) * 2 * - kernel_h * kernel_w * height_col * width_col; - const T *data_mask_ptr = - data_mask + (b * deformable_group + deformable_group_index) * kernel_h * - kernel_w * height_col * width_col; - - const int offset_c = c - deformable_group_index * 2 * kernel_h * kernel_w; - - for (int col_c = (offset_c / 2); col_c < channel_per_deformable_group; - col_c += col_step) { - const int col_pos = - (((col_c * batch_size + b) * height_col) + h) * width_col + w; - const int bp_dir = offset_c % 2; - - int j = (col_pos / width_col / height_col / batch_size) % kernel_w; - int i = - (col_pos / width_col / height_col / batch_size / kernel_w) % kernel_h; - int w_out = col_pos % width_col; - int h_out = (col_pos / width_col) % height_col; - int w_in = w_out * stride_w - pad_w; - int h_in = h_out * stride_h - pad_h; - const int data_offset_h_ptr = - (((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out); - const int data_offset_w_ptr = - (((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + - w_out); - const int data_mask_hw_ptr = - (((i * kernel_w + j) * height_col + h_out) * width_col + w_out); - const T offset_h = data_offset_ptr[data_offset_h_ptr]; - const T offset_w = data_offset_ptr[data_offset_w_ptr]; - const T mask = data_mask_ptr[data_mask_hw_ptr]; - T inv_h = h_in + i * dilation_h + offset_h; - T inv_w = w_in + j * dilation_w + offset_w; - if (inv_h <= -1 || inv_w <= -1 || inv_h >= height || inv_w >= width) - inv_h = inv_w = -2; - else - mval += data_col_ptr[col_pos] * - dmcn_im2col_bilinear_cpu(data_im_ptr + cnt * height * width, - width, height, width, inv_h, inv_w); - const T weight = dmcn_get_coordinate_weight_cpu( - inv_h, inv_w, height, width, data_im_ptr + cnt * height * width, - width, bp_dir); - val += weight * data_col_ptr[col_pos] * mask; - cnt += 1; - } - // KERNEL_ASSIGN(grad_offset[index], offset_req, val); - grad_offset[index] = val; - if (offset_c % 2 == 0) - // KERNEL_ASSIGN(grad_mask[(((b * deformable_group + - // deformable_group_index) * kernel_h * kernel_w + offset_c / 2) * - // height_col + h) * width_col + w], mask_req, mval); - grad_mask[(((b * deformable_group + deformable_group_index) * kernel_h * - kernel_w + - offset_c / 2) * - height_col + - h) * - width_col + - w] = mval; - } -} - -void modulated_deformable_im2col_cpu( - const Tensor data_im, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor data_col) { - // num_axes should be smaller than block size - const int channel_per_deformable_group = channels / deformable_group; - const int num_kernels = channels * batch_size * height_col * width_col; - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - data_im.scalar_type(), "modulated_deformable_im2col_cpu", ([&] { - const scalar_t *data_im_ = data_im.data_ptr(); - const scalar_t *data_offset_ = data_offset.data_ptr(); - const scalar_t *data_mask_ = data_mask.data_ptr(); - scalar_t *data_col_ = data_col.data_ptr(); - - modulated_deformable_im2col_cpu_kernel( - num_kernels, data_im_, data_offset_, data_mask_, height_im, - width_im, kernel_h, kenerl_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, channel_per_deformable_group, batch_size, - channels, deformable_group, height_col, width_col, data_col_); - })); -} - -void modulated_deformable_col2im_cpu( - const Tensor data_col, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor grad_im) { - const int channel_per_deformable_group = channels / deformable_group; - const int num_kernels = - channels * kernel_h * kernel_w * batch_size * height_col * width_col; - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - data_col.scalar_type(), "modulated_deformable_col2im_cpu", ([&] { - const scalar_t *data_col_ = data_col.data_ptr(); - const scalar_t *data_offset_ = data_offset.data_ptr(); - const scalar_t *data_mask_ = data_mask.data_ptr(); - scalar_t *grad_im_ = grad_im.data_ptr(); - - modulated_deformable_col2im_cpu_kernel( - num_kernels, data_col_, data_offset_, data_mask_, channels, - height_im, width_im, kernel_h, kernel_w, pad_h, pad_w, stride_h, - stride_w, dilation_h, dilation_w, channel_per_deformable_group, - batch_size, deformable_group, height_col, width_col, grad_im_); - })); -} - -void modulated_deformable_col2im_coord_cpu( - const Tensor data_col, const Tensor data_im, const Tensor data_offset, - const Tensor data_mask, const int batch_size, const int channels, - const int height_im, const int width_im, const int height_col, - const int width_col, const int kernel_h, const int kernel_w, - const int pad_h, const int pad_w, const int stride_h, const int stride_w, - const int dilation_h, const int dilation_w, const int deformable_group, - Tensor grad_offset, Tensor grad_mask) { - const int num_kernels = batch_size * height_col * width_col * 2 * kernel_h * - kernel_w * deformable_group; - const int channel_per_deformable_group = - channels * kernel_h * kernel_w / deformable_group; - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - data_col.scalar_type(), "modulated_deformable_col2im_coord_cpu", ([&] { - const scalar_t *data_col_ = data_col.data_ptr(); - const scalar_t *data_im_ = data_im.data_ptr(); - const scalar_t *data_offset_ = data_offset.data_ptr(); - const scalar_t *data_mask_ = data_mask.data_ptr(); - scalar_t *grad_offset_ = grad_offset.data_ptr(); - scalar_t *grad_mask_ = grad_mask.data_ptr(); - - modulated_deformable_col2im_coord_cpu_kernel( - num_kernels, data_col_, data_im_, data_offset_, data_mask_, - channels, height_im, width_im, kernel_h, kernel_w, pad_h, pad_w, - stride_h, stride_w, dilation_h, dilation_w, - channel_per_deformable_group, batch_size, - 2 * kernel_h * kernel_w * deformable_group, deformable_group, - height_col, width_col, grad_offset_, grad_mask_); - })); -} diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp deleted file mode 100755 index e9273ecd2..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -std::string get_compiler_version(); -std::string get_compiling_cuda_version(); - -void modulated_deform_conv_forward( - Tensor input, Tensor weight, Tensor bias, Tensor ones, Tensor offset, - Tensor mask, Tensor output, Tensor columns, int kernel_h, int kernel_w, - const int stride_h, const int stride_w, const int pad_h, const int pad_w, - const int dilation_h, const int dilation_w, const int group, - const int deformable_group, const bool with_bias); - -void modulated_deform_conv_backward( - Tensor input, Tensor weight, Tensor bias, Tensor ones, Tensor offset, - Tensor mask, Tensor columns, Tensor grad_input, Tensor grad_weight, - Tensor grad_bias, Tensor grad_offset, Tensor grad_mask, Tensor grad_output, - int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, - int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, - const bool with_bias); - -void sync_bn_forward_mean(const Tensor input, Tensor mean); - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var); - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - - m.def("get_compiler_version", &get_compiler_version, "get_compiler_version"); - m.def("get_compiling_cuda_version", &get_compiling_cuda_version, - "get_compiling_cuda_version"); - - m.def("modulated_deform_conv_forward", &modulated_deform_conv_forward, - "modulated deform conv forward", py::arg("input"), py::arg("weight"), - py::arg("bias"), py::arg("ones"), py::arg("offset"), py::arg("mask"), - py::arg("output"), py::arg("columns"), py::arg("kernel_h"), - py::arg("kernel_w"), py::arg("stride_h"), py::arg("stride_w"), - py::arg("pad_h"), py::arg("pad_w"), py::arg("dilation_h"), - py::arg("dilation_w"), py::arg("group"), py::arg("deformable_group"), - py::arg("with_bias")); - m.def("modulated_deform_conv_backward", &modulated_deform_conv_backward, - "modulated deform conv backward", py::arg("input"), py::arg("weight"), - py::arg("bias"), py::arg("ones"), py::arg("offset"), py::arg("mask"), - py::arg("columns"), py::arg("grad_input"), py::arg("grad_weight"), - py::arg("grad_bias"), py::arg("grad_offset"), py::arg("grad_mask"), - py::arg("grad_output"), py::arg("kernel_h"), py::arg("kernel_w"), - py::arg("stride_h"), py::arg("stride_w"), py::arg("pad_h"), - py::arg("pad_w"), py::arg("dilation_h"), py::arg("dilation_w"), - py::arg("group"), py::arg("deformable_group"), py::arg("with_bias")); - - m.def("sync_bn_forward_mean", &sync_bn_forward_mean, "sync_bn forward_mean", - py::arg("input"), py::arg("mean")); - m.def("sync_bn_forward_var", &sync_bn_forward_var, "sync_bn forward_var", - py::arg("input"), py::arg("mean"), py::arg("var")); - m.def("sync_bn_forward_output", &sync_bn_forward_output, - "sync_bn forward_output", py::arg("input"), py::arg("mean"), - py::arg("var"), py::arg("weight"), py::arg("bias"), - py::arg("running_mean"), py::arg("running_var"), py::arg("norm"), - py::arg("std"), py::arg("output"), py::arg("eps"), py::arg("momentum"), - py::arg("group_size")); - m.def("sync_bn_backward_param", &sync_bn_backward_param, - "sync_bn backward_param", py::arg("grad_output"), py::arg("norm"), - py::arg("grad_weight"), py::arg("grad_bias")); - m.def("sync_bn_backward_data", &sync_bn_backward_data, - "sync_bn backward_data", py::arg("grad_output"), py::arg("weight"), - py::arg("grad_weight"), py::arg("grad_bias"), py::arg("norm"), - py::arg("std"), py::arg("grad_input")); -} diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp deleted file mode 100755 index 2e023a859..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean); - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var); - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size); - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias); - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -void sync_bn_forward_mean_cuda(const Tensor input, Tensor mean) { - SyncBNForwardMeanCUDAKernelLauncher(input, mean); -} - -void sync_bn_forward_var_cuda(const Tensor input, const Tensor mean, - Tensor var) { - SyncBNForwardVarCUDAKernelLauncher(input, mean, var); -} - -void sync_bn_forward_output_cuda(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - SyncBNForwardOutputCUDAKernelLauncher(input, mean, var, running_mean, - running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_cuda(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - SyncBNBackwardParamCUDAKernelLauncher(grad_output, norm, grad_weight, - grad_bias); -} - -void sync_bn_backward_data_cuda(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - SyncBNBackwardDataCUDAKernelLauncher(grad_output, weight, grad_weight, - grad_bias, norm, std, grad_input); -} -#endif - -void sync_bn_forward_mean(const Tensor input, Tensor mean) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - sync_bn_forward_mean_cuda(input, mean); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - CHECK_CUDA_INPUT(var); - sync_bn_forward_var_cuda(input, mean, var); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - CHECK_CUDA_INPUT(var); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(bias); - CHECK_CUDA_INPUT(running_mean); - CHECK_CUDA_INPUT(running_var); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(std); - CHECK_CUDA_INPUT(output); - sync_bn_forward_output_cuda(input, mean, var, running_mean, running_var, - weight, bias, norm, std, output, eps, momentum, - group_size); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(grad_weight); - CHECK_CUDA_INPUT(grad_bias); - sync_bn_backward_param_cuda(grad_output, norm, grad_weight, grad_bias); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(grad_weight); - CHECK_CUDA_INPUT(grad_bias); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(std); - CHECK_CUDA_INPUT(grad_input); - sync_bn_backward_data_cuda(grad_output, weight, grad_weight, grad_bias, - norm, std, grad_input); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/deprecated_wrappers.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/deprecated_wrappers.py deleted file mode 100755 index a2e593df9..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/deprecated_wrappers.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# This file is for backward compatibility. -# Module wrappers for empty tensor have been moved to mmcv.cnn.bricks. -import warnings - -from ..cnn.bricks.wrappers import Conv2d, ConvTranspose2d, Linear, MaxPool2d - - -class Conv2d_deprecated(Conv2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Conv2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') - - -class ConvTranspose2d_deprecated(ConvTranspose2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing ConvTranspose2d wrapper from "mmcv.ops" will be ' - 'deprecated in the future. Please import them from "mmcv.cnn" ' - 'instead') - - -class MaxPool2d_deprecated(MaxPool2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing MaxPool2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') - - -class Linear_deprecated(Linear): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Linear wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/info.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/info.py deleted file mode 100755 index 29f2e5598..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/info.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os - -import torch - -if torch.__version__ == 'parrots': - import parrots - - def get_compiler_version(): - return 'GCC ' + parrots.version.compiler - - def get_compiling_cuda_version(): - return parrots.version.cuda -else: - from ..utils import ext_loader - ext_module = ext_loader.load_ext( - '_ext', ['get_compiler_version', 'get_compiling_cuda_version']) - - def get_compiler_version(): - return ext_module.get_compiler_version() - - def get_compiling_cuda_version(): - return ext_module.get_compiling_cuda_version() - - -def get_onnxruntime_op_path(): - wildcard = os.path.join( - os.path.abspath(os.path.dirname(os.path.dirname(__file__))), - '_ext_ort.*.so') - - paths = glob.glob(wildcard) - if len(paths) > 0: - return paths[0] - else: - return '' diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/modulated_deform_conv.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/modulated_deform_conv.py deleted file mode 100755 index 341798059..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/modulated_deform_conv.py +++ /dev/null @@ -1,282 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.utils import _pair, _single - -from mmcv.utils import deprecated_api_warning -from ..cnn import CONV_LAYERS -from ..utils import ext_loader, print_log - -ext_module = ext_loader.load_ext( - '_ext', - ['modulated_deform_conv_forward', 'modulated_deform_conv_backward']) - - -class ModulatedDeformConv2dFunction(Function): - - @staticmethod - def symbolic(g, input, offset, mask, weight, bias, stride, padding, - dilation, groups, deform_groups): - input_tensors = [input, offset, mask, weight] - if bias is not None: - input_tensors.append(bias) - return g.op( - 'mmcv::MMCVModulatedDeformConv2d', - *input_tensors, - stride_i=stride, - padding_i=padding, - dilation_i=dilation, - groups_i=groups, - deform_groups_i=deform_groups) - - @staticmethod - def forward(ctx, - input, - offset, - mask, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - deform_groups=1): - if input is not None and input.dim() != 4: - raise ValueError( - f'Expected 4D tensor as input, got {input.dim()}D tensor \ - instead.') - ctx.stride = _pair(stride) - ctx.padding = _pair(padding) - ctx.dilation = _pair(dilation) - ctx.groups = groups - ctx.deform_groups = deform_groups - ctx.with_bias = bias is not None - if not ctx.with_bias: - bias = input.new_empty(0) # fake tensor - # When pytorch version >= 1.6.0, amp is adopted for fp16 mode; - # amp won't cast the type of model (float32), but "offset" is cast - # to float16 by nn.Conv2d automatically, leading to the type - # mismatch with input (when it is float32) or weight. - # The flag for whether to use fp16 or amp is the type of "offset", - # we cast weight and input to temporarily support fp16 and amp - # whatever the pytorch version is. - input = input.type_as(offset) - weight = weight.type_as(input) - ctx.save_for_backward(input, offset, mask, weight, bias) - output = input.new_empty( - ModulatedDeformConv2dFunction._output_size(ctx, input, weight)) - ctx._bufs = [input.new_empty(0), input.new_empty(0)] - ext_module.modulated_deform_conv_forward( - input, - weight, - bias, - ctx._bufs[0], - offset, - mask, - output, - ctx._bufs[1], - kernel_h=weight.size(2), - kernel_w=weight.size(3), - stride_h=ctx.stride[0], - stride_w=ctx.stride[1], - pad_h=ctx.padding[0], - pad_w=ctx.padding[1], - dilation_h=ctx.dilation[0], - dilation_w=ctx.dilation[1], - group=ctx.groups, - deformable_group=ctx.deform_groups, - with_bias=ctx.with_bias) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - input, offset, mask, weight, bias = ctx.saved_tensors - grad_input = torch.zeros_like(input) - grad_offset = torch.zeros_like(offset) - grad_mask = torch.zeros_like(mask) - grad_weight = torch.zeros_like(weight) - grad_bias = torch.zeros_like(bias) - grad_output = grad_output.contiguous() - ext_module.modulated_deform_conv_backward( - input, - weight, - bias, - ctx._bufs[0], - offset, - mask, - ctx._bufs[1], - grad_input, - grad_weight, - grad_bias, - grad_offset, - grad_mask, - grad_output, - kernel_h=weight.size(2), - kernel_w=weight.size(3), - stride_h=ctx.stride[0], - stride_w=ctx.stride[1], - pad_h=ctx.padding[0], - pad_w=ctx.padding[1], - dilation_h=ctx.dilation[0], - dilation_w=ctx.dilation[1], - group=ctx.groups, - deformable_group=ctx.deform_groups, - with_bias=ctx.with_bias) - if not ctx.with_bias: - grad_bias = None - - return (grad_input, grad_offset, grad_mask, grad_weight, grad_bias, - None, None, None, None, None) - - @staticmethod - def _output_size(ctx, input, weight): - channels = weight.size(0) - output_size = (input.size(0), channels) - for d in range(input.dim() - 2): - in_size = input.size(d + 2) - pad = ctx.padding[d] - kernel = ctx.dilation[d] * (weight.size(d + 2) - 1) + 1 - stride_ = ctx.stride[d] - output_size += ((in_size + (2 * pad) - kernel) // stride_ + 1, ) - if not all(map(lambda s: s > 0, output_size)): - raise ValueError( - 'convolution input is too small (output would be ' + - 'x'.join(map(str, output_size)) + ')') - return output_size - - -modulated_deform_conv2d = ModulatedDeformConv2dFunction.apply - - -class ModulatedDeformConv2d(nn.Module): - - @deprecated_api_warning({'deformable_groups': 'deform_groups'}, - cls_name='ModulatedDeformConv2d') - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - deform_groups=1, - bias=True): - super(ModulatedDeformConv2d, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.kernel_size = _pair(kernel_size) - self.stride = _pair(stride) - self.padding = _pair(padding) - self.dilation = _pair(dilation) - self.groups = groups - self.deform_groups = deform_groups - # enable compatibility with nn.Conv2d - self.transposed = False - self.output_padding = _single(0) - - self.weight = nn.Parameter( - torch.Tensor(out_channels, in_channels // groups, - *self.kernel_size)) - if bias: - self.bias = nn.Parameter(torch.Tensor(out_channels)) - else: - self.register_parameter('bias', None) - self.init_weights() - - def init_weights(self): - n = self.in_channels - for k in self.kernel_size: - n *= k - stdv = 1. / math.sqrt(n) - self.weight.data.uniform_(-stdv, stdv) - if self.bias is not None: - self.bias.data.zero_() - - def forward(self, x, offset, mask): - return modulated_deform_conv2d(x, offset, mask, self.weight, self.bias, - self.stride, self.padding, - self.dilation, self.groups, - self.deform_groups) - - -@CONV_LAYERS.register_module('DCNv2') -class ModulatedDeformConv2dPack(ModulatedDeformConv2d): - """A ModulatedDeformable Conv Encapsulation that acts as normal Conv - layers. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - stride (int): Same as nn.Conv2d, while tuple is not supported. - padding (int): Same as nn.Conv2d, while tuple is not supported. - dilation (int): Same as nn.Conv2d, while tuple is not supported. - groups (int): Same as nn.Conv2d. - bias (bool or str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if norm_cfg is None, otherwise - False. - """ - - _version = 2 - - def __init__(self, *args, **kwargs): - super(ModulatedDeformConv2dPack, self).__init__(*args, **kwargs) - self.conv_offset = nn.Conv2d( - self.in_channels, - self.deform_groups * 3 * self.kernel_size[0] * self.kernel_size[1], - kernel_size=self.kernel_size, - stride=self.stride, - padding=self.padding, - dilation=self.dilation, - bias=True) - self.init_weights() - - def init_weights(self): - super(ModulatedDeformConv2dPack, self).init_weights() - if hasattr(self, 'conv_offset'): - self.conv_offset.weight.data.zero_() - self.conv_offset.bias.data.zero_() - - def forward(self, x): - out = self.conv_offset(x) - o1, o2, mask = torch.chunk(out, 3, dim=1) - offset = torch.cat((o1, o2), dim=1) - mask = torch.sigmoid(mask) - return modulated_deform_conv2d(x, offset, mask, self.weight, self.bias, - self.stride, self.padding, - self.dilation, self.groups, - self.deform_groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - version = local_metadata.get('version', None) - - if version is None or version < 2: - # the key is different in early versions - # In version < 2, ModulatedDeformConvPack - # loads previous benchmark models. - if (prefix + 'conv_offset.weight' not in state_dict - and prefix[:-1] + '_offset.weight' in state_dict): - state_dict[prefix + 'conv_offset.weight'] = state_dict.pop( - prefix[:-1] + '_offset.weight') - if (prefix + 'conv_offset.bias' not in state_dict - and prefix[:-1] + '_offset.bias' in state_dict): - state_dict[prefix + - 'conv_offset.bias'] = state_dict.pop(prefix[:-1] + - '_offset.bias') - - if version is not None and version > 1: - print_log( - f'ModulatedDeformConvPack {prefix.rstrip(".")} is upgraded to ' - 'version 2.', - logger='root') - - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, missing_keys, unexpected_keys, - error_msgs) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/sync_bn.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/sync_bn.py deleted file mode 100755 index 04302f031..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/ops/sync_bn.py +++ /dev/null @@ -1,279 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn.functional as F -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.module import Module -from torch.nn.parameter import Parameter - -from mmcv.cnn import NORM_LAYERS -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sync_bn_forward_mean', 'sync_bn_forward_var', 'sync_bn_forward_output', - 'sync_bn_backward_param', 'sync_bn_backward_data' -]) - - -class SyncBatchNormFunction(Function): - - @staticmethod - def symbolic(g, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - return g.op( - 'mmcv::MMCVSyncBatchNorm', - input, - running_mean, - running_var, - weight, - bias, - momentum_f=momentum, - eps_f=eps, - group_i=group, - group_size_i=group_size, - stats_mode=stats_mode) - - @staticmethod - def forward(self, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - self.momentum = momentum - self.eps = eps - self.group = group - self.group_size = group_size - self.stats_mode = stats_mode - - assert isinstance( - input, (torch.HalfTensor, torch.FloatTensor, - torch.cuda.HalfTensor, torch.cuda.FloatTensor)), \ - f'only support Half or Float Tensor, but {input.type()}' - output = torch.zeros_like(input) - input3d = input.flatten(start_dim=2) - output3d = output.view_as(input3d) - num_channels = input3d.size(1) - - # ensure mean/var/norm/std are initialized as zeros - # ``torch.empty()`` does not guarantee that - mean = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - var = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - norm = torch.zeros_like( - input3d, dtype=torch.float, device=input3d.device) - std = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - - batch_size = input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_forward_mean(input3d, mean) - batch_flag = torch.ones([1], device=mean.device, dtype=mean.dtype) - else: - # skip updating mean and leave it as zeros when the input is empty - batch_flag = torch.zeros([1], device=mean.device, dtype=mean.dtype) - - # synchronize mean and the batch flag - vec = torch.cat([mean, batch_flag]) - if self.stats_mode == 'N': - vec *= batch_size - if self.group_size > 1: - dist.all_reduce(vec, group=self.group) - total_batch = vec[-1].detach() - mean = vec[:num_channels] - - if self.stats_mode == 'default': - mean = mean / self.group_size - elif self.stats_mode == 'N': - mean = mean / total_batch.clamp(min=1) - else: - raise NotImplementedError - - # leave var as zeros when the input is empty - if batch_size > 0: - ext_module.sync_bn_forward_var(input3d, mean, var) - - if self.stats_mode == 'N': - var *= batch_size - if self.group_size > 1: - dist.all_reduce(var, group=self.group) - - if self.stats_mode == 'default': - var /= self.group_size - elif self.stats_mode == 'N': - var /= total_batch.clamp(min=1) - else: - raise NotImplementedError - - # if the total batch size over all the ranks is zero, - # we should not update the statistics in the current batch - update_flag = total_batch.clamp(max=1) - momentum = update_flag * self.momentum - ext_module.sync_bn_forward_output( - input3d, - mean, - var, - weight, - bias, - running_mean, - running_var, - norm, - std, - output3d, - eps=self.eps, - momentum=momentum, - group_size=self.group_size) - self.save_for_backward(norm, std, weight) - return output - - @staticmethod - @once_differentiable - def backward(self, grad_output): - norm, std, weight = self.saved_tensors - grad_weight = torch.zeros_like(weight) - grad_bias = torch.zeros_like(weight) - grad_input = torch.zeros_like(grad_output) - grad_output3d = grad_output.flatten(start_dim=2) - grad_input3d = grad_input.view_as(grad_output3d) - - batch_size = grad_input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_backward_param(grad_output3d, norm, grad_weight, - grad_bias) - - # all reduce - if self.group_size > 1: - dist.all_reduce(grad_weight, group=self.group) - dist.all_reduce(grad_bias, group=self.group) - grad_weight /= self.group_size - grad_bias /= self.group_size - - if batch_size > 0: - ext_module.sync_bn_backward_data(grad_output3d, weight, - grad_weight, grad_bias, norm, std, - grad_input3d) - - return grad_input, None, None, grad_weight, grad_bias, \ - None, None, None, None, None - - -@NORM_LAYERS.register_module(name='MMSyncBN') -class SyncBatchNorm(Module): - """Synchronized Batch Normalization. - - Args: - num_features (int): number of features/chennels in input tensor - eps (float, optional): a value added to the denominator for numerical - stability. Defaults to 1e-5. - momentum (float, optional): the value used for the running_mean and - running_var computation. Defaults to 0.1. - affine (bool, optional): whether to use learnable affine parameters. - Defaults to True. - track_running_stats (bool, optional): whether to track the running - mean and variance during training. When set to False, this - module does not track such statistics, and initializes statistics - buffers ``running_mean`` and ``running_var`` as ``None``. When - these buffers are ``None``, this module always uses batch - statistics in both training and eval modes. Defaults to True. - group (int, optional): synchronization of stats happen within - each process group individually. By default it is synchronization - across the whole world. Defaults to None. - stats_mode (str, optional): The statistical mode. Available options - includes ``'default'`` and ``'N'``. Defaults to 'default'. - When ``stats_mode=='default'``, it computes the overall statistics - using those from each worker with equal weight, i.e., the - statistics are synchronized and simply divied by ``group``. This - mode will produce inaccurate statistics when empty tensors occur. - When ``stats_mode=='N'``, it compute the overall statistics using - the total number of batches in each worker ignoring the number of - group, i.e., the statistics are synchronized and then divied by - the total batch ``N``. This mode is beneficial when empty tensors - occur during training, as it average the total mean by the real - number of batch. - """ - - def __init__(self, - num_features, - eps=1e-5, - momentum=0.1, - affine=True, - track_running_stats=True, - group=None, - stats_mode='default'): - super(SyncBatchNorm, self).__init__() - self.num_features = num_features - self.eps = eps - self.momentum = momentum - self.affine = affine - self.track_running_stats = track_running_stats - group = dist.group.WORLD if group is None else group - self.group = group - self.group_size = dist.get_world_size(group) - assert stats_mode in ['default', 'N'], \ - f'"stats_mode" only accepts "default" and "N", got "{stats_mode}"' - self.stats_mode = stats_mode - if self.affine: - self.weight = Parameter(torch.Tensor(num_features)) - self.bias = Parameter(torch.Tensor(num_features)) - else: - self.register_parameter('weight', None) - self.register_parameter('bias', None) - if self.track_running_stats: - self.register_buffer('running_mean', torch.zeros(num_features)) - self.register_buffer('running_var', torch.ones(num_features)) - self.register_buffer('num_batches_tracked', - torch.tensor(0, dtype=torch.long)) - else: - self.register_buffer('running_mean', None) - self.register_buffer('running_var', None) - self.register_buffer('num_batches_tracked', None) - self.reset_parameters() - - def reset_running_stats(self): - if self.track_running_stats: - self.running_mean.zero_() - self.running_var.fill_(1) - self.num_batches_tracked.zero_() - - def reset_parameters(self): - self.reset_running_stats() - if self.affine: - self.weight.data.uniform_() # pytorch use ones_() - self.bias.data.zero_() - - def forward(self, input): - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input, got {input.dim()}D input') - if self.momentum is None: - exponential_average_factor = 0.0 - else: - exponential_average_factor = self.momentum - - if self.training and self.track_running_stats: - if self.num_batches_tracked is not None: - self.num_batches_tracked += 1 - if self.momentum is None: # use cumulative moving average - exponential_average_factor = 1.0 / float( - self.num_batches_tracked) - else: # use exponential moving average - exponential_average_factor = self.momentum - - if self.training or not self.track_running_stats: - return SyncBatchNormFunction.apply( - input, self.running_mean, self.running_var, self.weight, - self.bias, exponential_average_factor, self.eps, self.group, - self.group_size, self.stats_mode) - else: - return F.batch_norm(input, self.running_mean, self.running_var, - self.weight, self.bias, False, - exponential_average_factor, self.eps) - - def __repr__(self): - s = self.__class__.__name__ - s += f'({self.num_features}, ' - s += f'eps={self.eps}, ' - s += f'momentum={self.momentum}, ' - s += f'affine={self.affine}, ' - s += f'track_running_stats={self.track_running_stats}, ' - s += f'group_size={self.group_size},' - s += f'stats_mode={self.stats_mode})' - return s diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/__init__.py deleted file mode 100755 index 2ed2c17ad..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collate import collate -from .data_container import DataContainer -from .data_parallel import MMDataParallel -from .distributed import MMDistributedDataParallel -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter, scatter_kwargs -from .utils import is_module_wrapper - -__all__ = [ - 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', - 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/_functions.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/_functions.py deleted file mode 100755 index 9b5a8a444..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/_functions.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import _get_stream - - -def scatter(input, devices, streams=None): - """Scatters tensor across multiple GPUs.""" - if streams is None: - streams = [None] * len(devices) - - if isinstance(input, list): - chunk_size = (len(input) - 1) // len(devices) + 1 - outputs = [ - scatter(input[i], [devices[i // chunk_size]], - [streams[i // chunk_size]]) for i in range(len(input)) - ] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - # TODO: copy to a pinned buffer first (if copying from CPU) - stream = streams[0] if output.numel() > 0 else None - if devices != [-1]: - with torch.cuda.device(devices[0]), torch.cuda.stream(stream): - output = output.cuda(devices[0], non_blocking=True) - else: - # unsqueeze the first dimension thus the tensor's shape is the - # same as those scattered with GPU. - output = output.unsqueeze(0) - return output - else: - raise Exception(f'Unknown type {type(input)}.') - - -def synchronize_stream(output, devices, streams): - if isinstance(output, list): - chunk_size = len(output) // len(devices) - for i in range(len(devices)): - for j in range(chunk_size): - synchronize_stream(output[i * chunk_size + j], [devices[i]], - [streams[i]]) - elif isinstance(output, torch.Tensor): - if output.numel() != 0: - with torch.cuda.device(devices[0]): - main_stream = torch.cuda.current_stream() - main_stream.wait_stream(streams[0]) - output.record_stream(main_stream) - else: - raise Exception(f'Unknown type {type(output)}.') - - -def get_input_device(input): - if isinstance(input, list): - for item in input: - input_device = get_input_device(item) - if input_device != -1: - return input_device - return -1 - elif isinstance(input, torch.Tensor): - return input.get_device() if input.is_cuda else -1 - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_gpus, input): - input_device = get_input_device(input) - streams = None - if input_device == -1 and target_gpus != [-1]: - # Perform CPU to GPU copies in a background stream - streams = [_get_stream(device) for device in target_gpus] - - outputs = scatter(input, target_gpus, streams) - # Synchronize with the copy stream - if streams is not None: - synchronize_stream(outputs, target_gpus, streams) - - return tuple(outputs) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/collate.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/collate.py deleted file mode 100755 index ad749197d..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/collate.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence - -import torch -import torch.nn.functional as F -from torch.utils.data.dataloader import default_collate - -from .data_container import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Puts each data field into a tensor/DataContainer with outer dimension - batch size. - - Extend default_collate to add support for - :type:`~mmcv.parallel.DataContainer`. There are 3 cases. - - 1. cpu_only = True, e.g., meta data - 2. cpu_only = False, stack = True, e.g., images tensors - 3. cpu_only = False, stack = False, e.g., gt bboxes - """ - - if not isinstance(batch, Sequence): - raise TypeError(f'{batch.dtype} is not supported.') - - if isinstance(batch[0], DataContainer): - stacked = [] - if batch[0].cpu_only: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer( - stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) - elif batch[0].stack: - for i in range(0, len(batch), samples_per_gpu): - assert isinstance(batch[i].data, torch.Tensor) - - if batch[i].pad_dims is not None: - ndim = batch[i].dim() - assert ndim > batch[i].pad_dims - max_shape = [0 for _ in range(batch[i].pad_dims)] - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = batch[i].size(-dim) - for sample in batch[i:i + samples_per_gpu]: - for dim in range(0, ndim - batch[i].pad_dims): - assert batch[i].size(dim) == sample.size(dim) - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = max(max_shape[dim - 1], - sample.size(-dim)) - padded_samples = [] - for sample in batch[i:i + samples_per_gpu]: - pad = [0 for _ in range(batch[i].pad_dims * 2)] - for dim in range(1, batch[i].pad_dims + 1): - pad[2 * dim - - 1] = max_shape[dim - 1] - sample.size(-dim) - padded_samples.append( - F.pad( - sample.data, pad, value=sample.padding_value)) - stacked.append(default_collate(padded_samples)) - elif batch[i].pad_dims is None: - stacked.append( - default_collate([ - sample.data - for sample in batch[i:i + samples_per_gpu] - ])) - else: - raise ValueError( - 'pad_dims should be either None or integers (1-3)') - - else: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer(stacked, batch[0].stack, batch[0].padding_value) - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - return [collate(samples, samples_per_gpu) for samples in transposed] - elif isinstance(batch[0], Mapping): - return { - key: collate([d[key] for d in batch], samples_per_gpu) - for key in batch[0] - } - else: - return default_collate(batch) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/data_container.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/data_container.py deleted file mode 100755 index cedb0d32a..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/data_container.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch - - -def assert_tensor_type(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not isinstance(args[0].data, torch.Tensor): - raise AttributeError( - f'{args[0].__class__.__name__} has no attribute ' - f'{func.__name__} for type {args[0].datatype}') - return func(*args, **kwargs) - - return wrapper - - -class DataContainer: - """A container for any type of objects. - - Typically tensors will be stacked in the collate function and sliced along - some dimension in the scatter function. This behavior has some limitations. - 1. All tensors have to be the same size. - 2. Types are limited (numpy array or Tensor). - - We design `DataContainer` and `MMDataParallel` to overcome these - limitations. The behavior can be either of the following. - - - copy to GPU, pad all tensors to the same size and stack them - - copy to GPU without stacking - - leave the objects as is and pass it to the model - - pad_dims specifies the number of last few dimensions to do padding - """ - - def __init__(self, - data, - stack=False, - padding_value=0, - cpu_only=False, - pad_dims=2): - self._data = data - self._cpu_only = cpu_only - self._stack = stack - self._padding_value = padding_value - assert pad_dims in [None, 1, 2, 3] - self._pad_dims = pad_dims - - def __repr__(self): - return f'{self.__class__.__name__}({repr(self.data)})' - - def __len__(self): - return len(self._data) - - @property - def data(self): - return self._data - - @property - def datatype(self): - if isinstance(self.data, torch.Tensor): - return self.data.type() - else: - return type(self.data) - - @property - def cpu_only(self): - return self._cpu_only - - @property - def stack(self): - return self._stack - - @property - def padding_value(self): - return self._padding_value - - @property - def pad_dims(self): - return self._pad_dims - - @assert_tensor_type - def size(self, *args, **kwargs): - return self.data.size(*args, **kwargs) - - @assert_tensor_type - def dim(self): - return self.data.dim() diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/data_parallel.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/data_parallel.py deleted file mode 100755 index 79b5f69b6..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/data_parallel.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from itertools import chain - -from torch.nn.parallel import DataParallel - -from .scatter_gather import scatter_kwargs - - -class MMDataParallel(DataParallel): - """The DataParallel module that supports DataContainer. - - MMDataParallel has two main differences with PyTorch DataParallel: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data during both GPU and CPU inference. - - It implement two more APIs ``train_step()`` and ``val_step()``. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - device_ids (list[int]): Device IDS of modules to be scattered to. - Defaults to None when GPU is not available. - output_device (str | int): Device ID for output. Defaults to None. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MMDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.dim = dim - - def forward(self, *inputs, **kwargs): - """Override the original forward function. - - The main difference lies in the CPU inference where the data in - :class:`DataContainers` will still be gathered. - """ - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module(*inputs[0], **kwargs[0]) - else: - return super().forward(*inputs, **kwargs) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.train_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - 'instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.train_step(*inputs[0], **kwargs[0]) - - def val_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.val_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/distributed.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/distributed.py deleted file mode 100755 index b799a213d..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/distributed.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel.distributed import (DistributedDataParallel, - _find_tensors) - -from mmcv import print_log -from mmcv.utils import TORCH_VERSION, digit_version -from .scatter_gather import scatter_kwargs - - -class MMDistributedDataParallel(DistributedDataParallel): - """The DDP module that supports DataContainer. - - MMDDP has two main differences with PyTorch DDP: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data. - - It implement two APIs ``train_step()`` and ``val_step()``. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - """train_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.train_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if getattr(self, 'require_forward_param_sync', True): - self._sync_params() - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.train_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.train_step(*inputs, **kwargs) - - if torch.is_grad_enabled() and getattr( - self, 'require_backward_grad_sync', True): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output - - def val_step(self, *inputs, **kwargs): - """val_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.val_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if getattr(self, 'require_forward_param_sync', True): - self._sync_params() - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.val_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.val_step(*inputs, **kwargs) - - if torch.is_grad_enabled() and getattr( - self, 'require_backward_grad_sync', True): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/distributed_deprecated.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/distributed_deprecated.py deleted file mode 100755 index b593d4a9e..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/distributed_deprecated.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn as nn -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter_kwargs - - -@MODULE_WRAPPERS.register_module() -class MMDistributedDataParallel(nn.Module): - - def __init__(self, - module, - dim=0, - broadcast_buffers=True, - bucket_cap_mb=25): - super(MMDistributedDataParallel, self).__init__() - self.module = module - self.dim = dim - self.broadcast_buffers = broadcast_buffers - - self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 - self._sync_params() - - def _dist_broadcast_coalesced(self, tensors, buffer_size): - for tensors in _take_tensors(tensors, buffer_size): - flat_tensors = _flatten_dense_tensors(tensors) - dist.broadcast(flat_tensors, 0) - for tensor, synced in zip( - tensors, _unflatten_dense_tensors(flat_tensors, tensors)): - tensor.copy_(synced) - - def _sync_params(self): - module_states = list(self.module.state_dict().values()) - if len(module_states) > 0: - self._dist_broadcast_coalesced(module_states, - self.broadcast_bucket_size) - if self.broadcast_buffers: - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) < digit_version('1.0')): - buffers = [b.data for b in self.module._all_buffers()] - else: - buffers = [b.data for b in self.module.buffers()] - if len(buffers) > 0: - self._dist_broadcast_coalesced(buffers, - self.broadcast_bucket_size) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/registry.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/registry.py deleted file mode 100755 index 144f9fb16..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel, DistributedDataParallel - -from mmcv.utils import Registry - -MODULE_WRAPPERS = Registry('module wrapper') -MODULE_WRAPPERS.register_module(module=DataParallel) -MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/scatter_gather.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/scatter_gather.py deleted file mode 100755 index 900ff8856..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import Scatter as OrigScatter - -from ._functions import Scatter -from .data_container import DataContainer - - -def scatter(inputs, target_gpus, dim=0): - """Scatter inputs to target gpus. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_gpus != [-1]: - return OrigScatter.apply(target_gpus, None, dim, obj) - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_gpus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_gpus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_gpus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_gpus, dim) if inputs else [] - kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/utils.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/utils.py deleted file mode 100755 index 0f5712cb4..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/parallel/utils.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import MODULE_WRAPPERS - - -def is_module_wrapper(module): - """Check if a module is a module wrapper. - - The following 3 modules in MMCV (and their subclasses) are regarded as - module wrappers: DataParallel, DistributedDataParallel, - MMDistributedDataParallel (the deprecated version). You may add you own - module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: True if the input module is a module wrapper. - """ - module_wrappers = tuple(MODULE_WRAPPERS.module_dict.values()) - return isinstance(module, module_wrappers) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/__init__.py deleted file mode 100755 index 52e4b48d3..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_module import BaseModule, ModuleList, Sequential -from .base_runner import BaseRunner -from .builder import RUNNERS, build_runner -from .checkpoint import (CheckpointLoader, _load_checkpoint, - _load_checkpoint_with_prefix, load_checkpoint, - load_state_dict, save_checkpoint, weights_to_cpu) -from .default_constructor import DefaultRunnerConstructor -from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, - init_dist, master_only) -from .epoch_based_runner import EpochBasedRunner, Runner -from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model -from .hooks import (HOOKS, CheckpointHook, ClosureHook, DistEvalHook, - DistSamplerSeedHook, DvcliveLoggerHook, EMAHook, EvalHook, - Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, Hook, IterTimerHook, - LoggerHook, LrUpdaterHook, MlflowLoggerHook, - NeptuneLoggerHook, OptimizerHook, PaviLoggerHook, - SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .iter_based_runner import IterBasedRunner, IterLoader -from .log_buffer import LogBuffer -from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, - DefaultOptimizerConstructor, build_optimizer, - build_optimizer_constructor) -from .priority import Priority, get_priority -from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed - -__all__ = [ - 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', - 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', - 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', - 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', - 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', - 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', - 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', - 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', - 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', - 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', - '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', - 'ModuleList', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/base_module.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/base_module.py deleted file mode 100755 index 529575b81..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/base_module.py +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings -from abc import ABCMeta -from collections import defaultdict -from logging import FileHandler - -import torch.nn as nn - -from mmcv.runner.dist_utils import master_only -from mmcv.utils.logging import get_logger, logger_initialized, print_log - - -class BaseModule(nn.Module, metaclass=ABCMeta): - """Base module for all modules in openmmlab. - - ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional - functionality of parameter initialization. Compared with - ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. - - - ``init_cfg``: the config to control the initialization. - - ``init_weights``: The function of parameter - initialization and recording initialization - information. - - ``_params_init_info``: Used to track the parameter - initialization information. This attribute only - exists during executing the ``init_weights``. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, init_cfg=None): - """Initialize BaseModule, inherited from `torch.nn.Module`""" - - # NOTE init_cfg can be defined in different levels, but init_cfg - # in low levels has a higher priority. - - super(BaseModule, self).__init__() - # define default value of init_cfg instead of hard code - # in init_weights() function - self._is_init = False - - self.init_cfg = copy.deepcopy(init_cfg) - - # Backward compatibility in derived classes - # if pretrained is not None: - # warnings.warn('DeprecationWarning: pretrained is a deprecated \ - # key, please consider using init_cfg') - # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - - @property - def is_init(self): - return self._is_init - - def init_weights(self): - """Initialize the weights.""" - - is_top_level_module = False - # check if it is top-level module - if not hasattr(self, '_params_init_info'): - # The `_params_init_info` is used to record the initialization - # information of the parameters - # the key should be the obj:`nn.Parameter` of model and the value - # should be a dict containing - # - init_info (str): The string that describes the initialization. - # - tmp_mean_value (FloatTensor): The mean of the parameter, - # which indicates whether the parameter has been modified. - # this attribute would be deleted after all parameters - # is initialized. - self._params_init_info = defaultdict(dict) - is_top_level_module = True - - # Initialize the `_params_init_info`, - # When detecting the `tmp_mean_value` of - # the corresponding parameter is changed, update related - # initialization information - for name, param in self.named_parameters(): - self._params_init_info[param][ - 'init_info'] = f'The value is the same before and ' \ - f'after calling `init_weights` ' \ - f'of {self.__class__.__name__} ' - self._params_init_info[param][ - 'tmp_mean_value'] = param.data.mean() - - # pass `params_init_info` to all submodules - # All submodules share the same `params_init_info`, - # so it will be updated when parameters are - # modified at any level of the model. - for sub_module in self.modules(): - sub_module._params_init_info = self._params_init_info - - # Get the initialized logger, if not exist, - # create a logger named `mmcv` - logger_names = list(logger_initialized.keys()) - logger_name = logger_names[0] if logger_names else 'mmcv' - - from ..cnn import initialize - from ..cnn.utils.weight_init import update_init_info - module_name = self.__class__.__name__ - if not self._is_init: - if self.init_cfg: - print_log( - f'initialize {module_name} with init_cfg {self.init_cfg}', - logger=logger_name) - initialize(self, self.init_cfg) - if isinstance(self.init_cfg, dict): - # prevent the parameters of - # the pre-trained model - # from being overwritten by - # the `init_weights` - if self.init_cfg['type'] == 'Pretrained': - return - - for m in self.children(): - if hasattr(m, 'init_weights'): - m.init_weights() - # users may overload the `init_weights` - update_init_info( - m, - init_info=f'Initialized by ' - f'user-defined `init_weights`' - f' in {m.__class__.__name__} ') - - self._is_init = True - else: - warnings.warn(f'init_weights of {self.__class__.__name__} has ' - f'been called more than once.') - - if is_top_level_module: - self._dump_init_info(logger_name) - - for sub_module in self.modules(): - del sub_module._params_init_info - - @master_only - def _dump_init_info(self, logger_name): - """Dump the initialization information to a file named - `initialization.log.json` in workdir. - - Args: - logger_name (str): The name of logger. - """ - - logger = get_logger(logger_name) - - with_file_handler = False - # dump the information to the logger file if there is a `FileHandler` - for handler in logger.handlers: - if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True - if not with_file_handler: - for name, param in self.named_parameters(): - print_log( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n ", - logger=logger_name) - - def __repr__(self): - s = super().__repr__() - if self.init_cfg: - s += f'\ninit_cfg={self.init_cfg}' - return s - - -class Sequential(BaseModule, nn.Sequential): - """Sequential module in openmmlab. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, *args, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.Sequential.__init__(self, *args) - - -class ModuleList(BaseModule, nn.ModuleList): - """ModuleList in openmmlab. - - Args: - modules (iterable, optional): an iterable of modules to add. - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleList.__init__(self, modules) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/base_runner.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/base_runner.py deleted file mode 100755 index 25cd98f51..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/base_runner.py +++ /dev/null @@ -1,542 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -import os.path as osp -import warnings -from abc import ABCMeta, abstractmethod - -import torch -from torch.optim import Optimizer - -import mmcv -from ..parallel import is_module_wrapper -from .checkpoint import load_checkpoint -from .dist_utils import get_dist_info -from .hooks import HOOKS, Hook -from .log_buffer import LogBuffer -from .priority import Priority, get_priority -from .utils import get_time_str - - -class BaseRunner(metaclass=ABCMeta): - """The base class of Runner, a training helper for PyTorch. - - All subclasses should implement the following APIs: - - - ``run()`` - - ``train()`` - - ``val()`` - - ``save_checkpoint()`` - - Args: - model (:obj:`torch.nn.Module`): The model to be run. - batch_processor (callable): A callable method that process a data - batch. The interface of this method should be - `batch_processor(model, data, train_mode) -> dict` - optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an - optimizer (in most cases) or a dict of optimizers (in models that - requires more than one optimizer, e.g., GAN). - work_dir (str, optional): The working directory to save checkpoints - and logs. Defaults to None. - logger (:obj:`logging.Logger`): Logger used during training. - Defaults to None. (The default value is just for backward - compatibility) - meta (dict | None): A dict records some import information such as - environment info and seed, which will be logged in logger hook. - Defaults to None. - max_epochs (int, optional): Total training epochs. - max_iters (int, optional): Total training iterations. - """ - - def __init__(self, - model, - batch_processor=None, - optimizer=None, - work_dir=None, - logger=None, - meta=None, - max_iters=None, - max_epochs=None): - if batch_processor is not None: - if not callable(batch_processor): - raise TypeError('batch_processor must be callable, ' - f'but got {type(batch_processor)}') - warnings.warn('batch_processor is deprecated, please implement ' - 'train_step() and val_step() in the model instead.') - # raise an error is `batch_processor` is not None and - # `model.train_step()` exists. - if is_module_wrapper(model): - _model = model.module - else: - _model = model - if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): - raise RuntimeError( - 'batch_processor and model.train_step()/model.val_step() ' - 'cannot be both available.') - else: - assert hasattr(model, 'train_step') - - # check the type of `optimizer` - if isinstance(optimizer, dict): - for name, optim in optimizer.items(): - if not isinstance(optim, Optimizer): - raise TypeError( - f'optimizer must be a dict of torch.optim.Optimizers, ' - f'but optimizer["{name}"] is a {type(optim)}') - elif not isinstance(optimizer, Optimizer) and optimizer is not None: - raise TypeError( - f'optimizer must be a torch.optim.Optimizer object ' - f'or dict or None, but got {type(optimizer)}') - - # check the type of `logger` - if not isinstance(logger, logging.Logger): - raise TypeError(f'logger must be a logging.Logger object, ' - f'but got {type(logger)}') - - # check the type of `meta` - if meta is not None and not isinstance(meta, dict): - raise TypeError( - f'meta must be a dict or None, but got {type(meta)}') - - self.model = model - self.batch_processor = batch_processor - self.optimizer = optimizer - self.logger = logger - self.meta = meta - # create work_dir - if mmcv.is_str(work_dir): - self.work_dir = osp.abspath(work_dir) - mmcv.mkdir_or_exist(self.work_dir) - elif work_dir is None: - self.work_dir = None - else: - raise TypeError('"work_dir" must be a str or None') - - # get model name from the model class - if hasattr(self.model, 'module'): - self._model_name = self.model.module.__class__.__name__ - else: - self._model_name = self.model.__class__.__name__ - - self._rank, self._world_size = get_dist_info() - self.timestamp = get_time_str() - self.mode = None - self._hooks = [] - self._epoch = 0 - self._iter = 0 - self._inner_iter = 0 - - if max_epochs is not None and max_iters is not None: - raise ValueError( - 'Only one of `max_epochs` or `max_iters` can be set.') - - self._max_epochs = max_epochs - self._max_iters = max_iters - # TODO: Redesign LogBuffer, it is not flexible and elegant enough - self.log_buffer = LogBuffer() - - @property - def model_name(self): - """str: Name of the model, usually the module class name.""" - return self._model_name - - @property - def rank(self): - """int: Rank of current process. (distributed training)""" - return self._rank - - @property - def world_size(self): - """int: Number of processes participating in the job. - (distributed training)""" - return self._world_size - - @property - def hooks(self): - """list[:obj:`Hook`]: A list of registered hooks.""" - return self._hooks - - @property - def epoch(self): - """int: Current epoch.""" - return self._epoch - - @property - def iter(self): - """int: Current iteration.""" - return self._iter - - @property - def inner_iter(self): - """int: Iteration in an epoch.""" - return self._inner_iter - - @property - def max_epochs(self): - """int: Maximum training epochs.""" - return self._max_epochs - - @property - def max_iters(self): - """int: Maximum training iterations.""" - return self._max_iters - - @abstractmethod - def train(self): - pass - - @abstractmethod - def val(self): - pass - - @abstractmethod - def run(self, data_loaders, workflow, **kwargs): - pass - - @abstractmethod - def save_checkpoint(self, - out_dir, - filename_tmpl, - save_optimizer=True, - meta=None, - create_symlink=True): - pass - - def current_lr(self): - """Get current learning rates. - - Returns: - list[float] | dict[str, list[float]]: Current learning rates of all - param groups. If the runner has a dict of optimizers, this - method will return a dict. - """ - if isinstance(self.optimizer, torch.optim.Optimizer): - lr = [group['lr'] for group in self.optimizer.param_groups] - elif isinstance(self.optimizer, dict): - lr = dict() - for name, optim in self.optimizer.items(): - lr[name] = [group['lr'] for group in optim.param_groups] - else: - raise RuntimeError( - 'lr is not applicable because optimizer does not exist.') - return lr - - def current_momentum(self): - """Get current momentums. - - Returns: - list[float] | dict[str, list[float]]: Current momentums of all - param groups. If the runner has a dict of optimizers, this - method will return a dict. - """ - - def _get_momentum(optimizer): - momentums = [] - for group in optimizer.param_groups: - if 'momentum' in group.keys(): - momentums.append(group['momentum']) - elif 'betas' in group.keys(): - momentums.append(group['betas'][0]) - else: - momentums.append(0) - return momentums - - if self.optimizer is None: - raise RuntimeError( - 'momentum is not applicable because optimizer does not exist.') - elif isinstance(self.optimizer, torch.optim.Optimizer): - momentums = _get_momentum(self.optimizer) - elif isinstance(self.optimizer, dict): - momentums = dict() - for name, optim in self.optimizer.items(): - momentums[name] = _get_momentum(optim) - return momentums - - def register_hook(self, hook, priority='NORMAL'): - """Register a hook into the hook list. - - The hook will be inserted into a priority queue, with the specified - priority (See :class:`Priority` for details of priorities). - For hooks with the same priority, they will be triggered in the same - order as they are registered. - - Args: - hook (:obj:`Hook`): The hook to be registered. - priority (int or str or :obj:`Priority`): Hook priority. - Lower value means higher priority. - """ - assert isinstance(hook, Hook) - if hasattr(hook, 'priority'): - raise ValueError('"priority" is a reserved attribute for hooks') - priority = get_priority(priority) - hook.priority = priority - # insert the hook to a sorted list - inserted = False - for i in range(len(self._hooks) - 1, -1, -1): - if priority >= self._hooks[i].priority: - self._hooks.insert(i + 1, hook) - inserted = True - break - if not inserted: - self._hooks.insert(0, hook) - - def register_hook_from_cfg(self, hook_cfg): - """Register a hook from its cfg. - - Args: - hook_cfg (dict): Hook config. It should have at least keys 'type' - and 'priority' indicating its type and priority. - - Notes: - The specific hook class to register should not use 'type' and - 'priority' arguments during initialization. - """ - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = mmcv.build_from_cfg(hook_cfg, HOOKS) - self.register_hook(hook, priority=priority) - - def call_hook(self, fn_name): - """Call all hooks. - - Args: - fn_name (str): The function name in each hook to be called, such as - "before_train_epoch". - """ - for hook in self._hooks: - getattr(hook, fn_name)(self) - - def get_hook_info(self): - # Get hooks info in each stage - stage_hook_map = {stage: [] for stage in Hook.stages} - for hook in self.hooks: - try: - priority = Priority(hook.priority).name - except ValueError: - priority = hook.priority - classname = hook.__class__.__name__ - hook_info = f'({priority:<12}) {classname:<35}' - for trigger_stage in hook.get_triggered_stages(): - stage_hook_map[trigger_stage].append(hook_info) - - stage_hook_infos = [] - for stage in Hook.stages: - hook_infos = stage_hook_map[stage] - if len(hook_infos) > 0: - info = f'{stage}:\n' - info += '\n'.join(hook_infos) - info += '\n -------------------- ' - stage_hook_infos.append(info) - return '\n'.join(stage_hook_infos) - - def load_checkpoint(self, - filename, - map_location='cpu', - strict=False, - revise_keys=[(r'^module.', '')]): - return load_checkpoint( - self.model, - filename, - map_location, - strict, - self.logger, - revise_keys=revise_keys) - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - if map_location == 'default': - if torch.cuda.is_available(): - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint(checkpoint) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - if self.meta is None: - self.meta = {} - self.meta.setdefault('hook_msgs', {}) - # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages - self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) - - # Re-calculate the number of iterations when resuming - # models with different number of GPUs - if 'config' in checkpoint['meta']: - config = mmcv.Config.fromstring( - checkpoint['meta']['config'], file_format='.py') - previous_gpu_ids = config.get('gpu_ids', None) - if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( - previous_gpu_ids) != self.world_size: - self._iter = int(self._iter * len(previous_gpu_ids) / - self.world_size) - self.logger.info('the iteration number is changed due to ' - 'change of GPU number') - - # resume meta information meta - self.meta = checkpoint['meta'] - - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - elif isinstance(lr_config, dict): - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = mmcv.build_from_cfg(lr_config, HOOKS) - else: - hook = lr_config - self.register_hook(hook, priority='VERY_HIGH') - - def register_momentum_hook(self, momentum_config): - if momentum_config is None: - return - if isinstance(momentum_config, dict): - assert 'policy' in momentum_config - policy_type = momentum_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of momentum updater. - # Since this is not applicable for - # `CosineAnnealingMomentumUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'MomentumUpdaterHook' - momentum_config['type'] = hook_type - hook = mmcv.build_from_cfg(momentum_config, HOOKS) - else: - hook = momentum_config - self.register_hook(hook, priority='HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = mmcv.build_from_cfg(optimizer_config, HOOKS) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def register_checkpoint_hook(self, checkpoint_config): - if checkpoint_config is None: - return - if isinstance(checkpoint_config, dict): - checkpoint_config.setdefault('type', 'CheckpointHook') - hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) - else: - hook = checkpoint_config - self.register_hook(hook, priority='NORMAL') - - def register_logger_hooks(self, log_config): - if log_config is None: - return - log_interval = log_config['interval'] - for info in log_config['hooks']: - logger_hook = mmcv.build_from_cfg( - info, HOOKS, default_args=dict(interval=log_interval)) - self.register_hook(logger_hook, priority='VERY_LOW') - - def register_timer_hook(self, timer_config): - if timer_config is None: - return - if isinstance(timer_config, dict): - timer_config_ = copy.deepcopy(timer_config) - hook = mmcv.build_from_cfg(timer_config_, HOOKS) - else: - hook = timer_config - self.register_hook(hook, priority='LOW') - - def register_custom_hooks(self, custom_config): - if custom_config is None: - return - - if not isinstance(custom_config, list): - custom_config = [custom_config] - - for item in custom_config: - if isinstance(item, dict): - self.register_hook_from_cfg(item) - else: - self.register_hook(item, priority='NORMAL') - - def register_profiler_hook(self, profiler_config): - if profiler_config is None: - return - if isinstance(profiler_config, dict): - profiler_config.setdefault('type', 'ProfilerHook') - hook = mmcv.build_from_cfg(profiler_config, HOOKS) - else: - hook = profiler_config - self.register_hook(hook) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - timer_config=dict(type='IterTimerHook'), - custom_hooks_config=None): - """Register default and custom hooks for training. - - Default and custom hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - self.register_lr_hook(lr_config) - self.register_momentum_hook(momentum_config) - self.register_optimizer_hook(optimizer_config) - self.register_checkpoint_hook(checkpoint_config) - self.register_timer_hook(timer_config) - self.register_logger_hooks(log_config) - self.register_custom_hooks(custom_hooks_config) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/builder.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/builder.py deleted file mode 100755 index 77c96ba0b..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/builder.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from ..utils import Registry - -RUNNERS = Registry('runner') -RUNNER_BUILDERS = Registry('runner builder') - - -def build_runner_constructor(cfg): - return RUNNER_BUILDERS.build(cfg) - - -def build_runner(cfg, default_args=None): - runner_cfg = copy.deepcopy(cfg) - constructor_type = runner_cfg.pop('constructor', - 'DefaultRunnerConstructor') - runner_constructor = build_runner_constructor( - dict( - type=constructor_type, - runner_cfg=runner_cfg, - default_args=default_args)) - runner = runner_constructor() - return runner diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/checkpoint.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/checkpoint.py deleted file mode 100755 index 6ad605b85..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/checkpoint.py +++ /dev/null @@ -1,707 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os -import os.path as osp -import pkgutil -import re -import time -import warnings -from collections import OrderedDict -from importlib import import_module -from tempfile import TemporaryDirectory - -import torch -import torchvision -from torch.optim import Optimizer -from torch.utils import model_zoo - -import mmcv -from ..fileio import FileClient -from ..fileio import load as load_file -from ..parallel import is_module_wrapper -from ..utils import mkdir_or_exist -from .dist_utils import get_dist_info - -ENV_MMCV_HOME = 'MMCV_HOME' -ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' -DEFAULT_CACHE_DIR = '~/.cache' - - -def _get_mmcv_home(): - mmcv_home = os.path.expanduser( - os.getenv( - ENV_MMCV_HOME, - os.path.join( - os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) - - mkdir_or_exist(mmcv_home) - return mmcv_home - - -def load_state_dict(module, state_dict, strict=False, logger=None): - """Load state_dict to a module. - - This method is modified from :meth:`torch.nn.Module.load_state_dict`. - Default value for ``strict`` is set to ``False`` and the message for - param mismatch will be shown even if strict is False. - - Args: - module (Module): Module that receives the state_dict. - state_dict (OrderedDict): Weights. - strict (bool): whether to strictly enforce that the keys - in :attr:`state_dict` match the keys returned by this module's - :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. - logger (:obj:`logging.Logger`, optional): Logger to log the error - message. If not specified, print function will be used. - """ - unexpected_keys = [] - all_missing_keys = [] - err_msg = [] - - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata - - # use _load_from_state_dict to enable checkpoint version control - def load(module, prefix=''): - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - local_metadata = {} if metadata is None else metadata.get( - prefix[:-1], {}) - module._load_from_state_dict(state_dict, prefix, local_metadata, True, - all_missing_keys, unexpected_keys, - err_msg) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(module) - load = None # break load->load reference cycle - - # ignore "num_batches_tracked" of BN layers - missing_keys = [ - key for key in all_missing_keys if 'num_batches_tracked' not in key - ] - - if unexpected_keys: - err_msg.append('unexpected key in source ' - f'state_dict: {", ".join(unexpected_keys)}\n') - if missing_keys: - err_msg.append( - f'missing keys in source state_dict: {", ".join(missing_keys)}\n') - - rank, _ = get_dist_info() - if len(err_msg) > 0 and rank == 0: - err_msg.insert( - 0, 'The model and loaded state dict do not match exactly\n') - err_msg = '\n'.join(err_msg) - if strict: - raise RuntimeError(err_msg) - elif logger is not None: - logger.warning(err_msg) - else: - print(err_msg) - - -def get_torchvision_models(): - model_urls = dict() - for _, name, ispkg in pkgutil.walk_packages(torchvision.models.__path__): - if ispkg: - continue - _zoo = import_module(f'torchvision.models.{name}') - if hasattr(_zoo, 'model_urls'): - _urls = getattr(_zoo, 'model_urls') - model_urls.update(_urls) - return model_urls - - -def get_external_models(): - mmcv_home = _get_mmcv_home() - default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') - default_urls = load_file(default_json_path) - assert isinstance(default_urls, dict) - external_json_path = osp.join(mmcv_home, 'open_mmlab.json') - if osp.exists(external_json_path): - external_urls = load_file(external_json_path) - assert isinstance(external_urls, dict) - default_urls.update(external_urls) - - return default_urls - - -def get_mmcls_models(): - mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') - mmcls_urls = load_file(mmcls_json_path) - - return mmcls_urls - - -def get_deprecated_model_names(): - deprecate_json_path = osp.join(mmcv.__path__[0], - 'model_zoo/deprecated.json') - deprecate_urls = load_file(deprecate_json_path) - assert isinstance(deprecate_urls, dict) - - return deprecate_urls - - -def _process_mmcls_checkpoint(checkpoint): - state_dict = checkpoint['state_dict'] - new_state_dict = OrderedDict() - for k, v in state_dict.items(): - if k.startswith('backbone.'): - new_state_dict[k[9:]] = v - new_checkpoint = dict(state_dict=new_state_dict) - - return new_checkpoint - - -class CheckpointLoader: - """A general checkpoint loader to manage all schemes.""" - - _schemes = {} - - @classmethod - def _register_scheme(cls, prefixes, loader, force=False): - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if (prefix not in cls._schemes) or force: - cls._schemes[prefix] = loader - else: - raise KeyError( - f'{prefix} is already registered as a loader backend, ' - 'add "force=True" if you want to override it') - # sort, longer prefixes take priority - cls._schemes = OrderedDict( - sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) - - @classmethod - def register_scheme(cls, prefixes, loader=None, force=False): - """Register a loader to CheckpointLoader. - - This method can be used as a normal class method or a decorator. - - Args: - prefixes (str or list[str] or tuple[str]): - The prefix of the registered loader. - loader (function, optional): The loader function to be registered. - When this method is used as a decorator, loader is None. - Defaults to None. - force (bool, optional): Whether to override the loader - if the prefix has already been registered. Defaults to False. - """ - - if loader is not None: - cls._register_scheme(prefixes, loader, force=force) - return - - def _register(loader_cls): - cls._register_scheme(prefixes, loader_cls, force=force) - return loader_cls - - return _register - - @classmethod - def _get_checkpoint_loader(cls, path): - """Finds a loader that supports the given path. Falls back to the local - loader if no other loader is found. - - Args: - path (str): checkpoint path - - Returns: - loader (function): checkpoint loader - """ - - for p in cls._schemes: - if path.startswith(p): - return cls._schemes[p] - - @classmethod - def load_checkpoint(cls, filename, map_location=None, logger=None): - """load checkpoint through URL scheme path. - - Args: - filename (str): checkpoint file name with given prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - logger (:mod:`logging.Logger`, optional): The logger for message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint_loader = cls._get_checkpoint_loader(filename) - class_name = checkpoint_loader.__name__ - mmcv.print_log( - f'load checkpoint from {class_name[10:]} path: {filename}', logger) - return checkpoint_loader(filename, map_location) - - -@CheckpointLoader.register_scheme(prefixes='') -def load_from_local(filename, map_location): - """load checkpoint by local file path. - - Args: - filename (str): local checkpoint file path - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - if not osp.isfile(filename): - raise IOError(f'{filename} is not a checkpoint file') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) -def load_from_http(filename, map_location=None, model_dir=None): - """load checkpoint through HTTP or HTTPS scheme path. In distributed - setting, this function only download checkpoint at local rank 0. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - model_dir (string, optional): directory in which to save the object, - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - rank, world_size = get_dist_info() - rank = int(os.environ.get('LOCAL_RANK', rank)) - if rank == 0: - checkpoint = model_zoo.load_url( - filename, model_dir=model_dir, map_location=map_location) - if world_size > 1: - torch.distributed.barrier() - if rank > 0: - checkpoint = model_zoo.load_url( - filename, model_dir=model_dir, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='pavi://') -def load_from_pavi(filename, map_location=None): - """load checkpoint through the file path prefixed with pavi. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with pavi prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - assert filename.startswith('pavi://'), \ - f'Expected filename startswith `pavi://`, but get {filename}' - model_path = filename[7:] - - try: - from pavi import modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - - model = modelcloud.get(model_path) - with TemporaryDirectory() as tmp_dir: - downloaded_file = osp.join(tmp_dir, model.name) - model.download(downloaded_file) - checkpoint = torch.load(downloaded_file, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='s3://') -def load_from_ceph(filename, map_location=None, backend='petrel'): - """load checkpoint through the file path prefixed with s3. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with s3 prefix - map_location (str, optional): Same as :func:`torch.load`. - backend (str, optional): The storage backend type. Options are 'ceph', - 'petrel'. Default: 'petrel'. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - allowed_backends = ['ceph', 'petrel'] - if backend not in allowed_backends: - raise ValueError(f'Load from Backend {backend} is not supported.') - - if backend == 'ceph': - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead') - - # CephClient and PetrelBackend have the same prefix 's3://' and the latter - # will be chosen as default. If PetrelBackend can not be instantiated - # successfully, the CephClient will be chosen. - try: - file_client = FileClient(backend=backend) - except ImportError: - allowed_backends.remove(backend) - file_client = FileClient(backend=allowed_backends[0]) - - with io.BytesIO(file_client.get(filename)) as buffer: - checkpoint = torch.load(buffer, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) -def load_from_torchvision(filename, map_location=None): - """load checkpoint through the file path prefixed with modelzoo or - torchvision. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - model_urls = get_torchvision_models() - if filename.startswith('modelzoo://'): - warnings.warn('The URL scheme of "modelzoo://" is deprecated, please ' - 'use "torchvision://" instead') - model_name = filename[11:] - else: - model_name = filename[14:] - return load_from_http(model_urls[model_name], map_location=map_location) - - -@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) -def load_from_openmmlab(filename, map_location=None): - """load checkpoint through the file path prefixed with open-mmlab or - openmmlab. - - Args: - filename (str): checkpoint file path with open-mmlab or - openmmlab prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_external_models() - prefix_str = 'open-mmlab://' - if filename.startswith(prefix_str): - model_name = filename[13:] - else: - model_name = filename[12:] - prefix_str = 'openmmlab://' - - deprecated_urls = get_deprecated_model_names() - if model_name in deprecated_urls: - warnings.warn(f'{prefix_str}{model_name} is deprecated in favor ' - f'of {prefix_str}{deprecated_urls[model_name]}') - model_name = deprecated_urls[model_name] - model_url = model_urls[model_name] - # check if is url - if model_url.startswith(('http://', 'https://')): - checkpoint = load_from_http(model_url, map_location=map_location) - else: - filename = osp.join(_get_mmcv_home(), model_url) - if not osp.isfile(filename): - raise IOError(f'{filename} is not a checkpoint file') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='mmcls://') -def load_from_mmcls(filename, map_location=None): - """load checkpoint through the file path prefixed with mmcls. - - Args: - filename (str): checkpoint file path with mmcls prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_mmcls_models() - model_name = filename[8:] - checkpoint = load_from_http( - model_urls[model_name], map_location=map_location) - checkpoint = _process_mmcls_checkpoint(checkpoint) - return checkpoint - - -def _load_checkpoint(filename, map_location=None, logger=None): - """Load checkpoint from somewhere (modelzoo, file, url). - - Args: - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str, optional): Same as :func:`torch.load`. - Default: None. - logger (:mod:`logging.Logger`, optional): The logger for error message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. It can be either an - OrderedDict storing model weights or a dict containing other - information, which depends on the checkpoint. - """ - return CheckpointLoader.load_checkpoint(filename, map_location, logger) - - -def _load_checkpoint_with_prefix(prefix, filename, map_location=None): - """Load partial pretrained model with specific prefix. - - Args: - prefix (str): The prefix of sub-module. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str | None): Same as :func:`torch.load`. Default: None. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint = _load_checkpoint(filename, map_location=map_location) - - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - if not prefix.endswith('.'): - prefix += '.' - prefix_len = len(prefix) - - state_dict = { - k[prefix_len:]: v - for k, v in state_dict.items() if k.startswith(prefix) - } - - assert state_dict, f'{prefix} is not in the pretrained model' - return state_dict - - -def load_checkpoint(model, - filename, - map_location=None, - strict=False, - logger=None, - revise_keys=[(r'^module\.', '')]): - """Load checkpoint from a file or URI. - - Args: - model (Module): Module to load checkpoint. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str): Same as :func:`torch.load`. - strict (bool): Whether to allow different params for the model and - checkpoint. - logger (:mod:`logging.Logger` or None): The logger for error message. - revise_keys (list): A list of customized keywords to modify the - state_dict in checkpoint. Each item is a (pattern, replacement) - pair of the regular expression operations. Default: strip - the prefix 'module.' by [(r'^module\\.', '')]. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - checkpoint = _load_checkpoint(filename, map_location, logger) - # OrderedDict is a subclass of dict - if not isinstance(checkpoint, dict): - raise RuntimeError( - f'No state_dict found in checkpoint file {filename}') - # get state_dict from checkpoint - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - - # strip prefix of state_dict - metadata = getattr(state_dict, '_metadata', OrderedDict()) - for p, r in revise_keys: - state_dict = OrderedDict( - {re.sub(p, r, k): v - for k, v in state_dict.items()}) - # Keep metadata in state_dict - state_dict._metadata = metadata - - # load state_dict - load_state_dict(model, state_dict, strict, logger) - return checkpoint - - -def weights_to_cpu(state_dict): - """Copy a model state_dict to cpu. - - Args: - state_dict (OrderedDict): Model weights on GPU. - - Returns: - OrderedDict: Model weights on GPU. - """ - state_dict_cpu = OrderedDict() - for key, val in state_dict.items(): - state_dict_cpu[key] = val.cpu() - # Keep metadata in state_dict - state_dict_cpu._metadata = getattr(state_dict, '_metadata', OrderedDict()) - return state_dict_cpu - - -def _save_to_state_dict(module, destination, prefix, keep_vars): - """Saves module state to `destination` dictionary. - - This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. - - Args: - module (nn.Module): The module to generate state_dict. - destination (dict): A dict where state will be stored. - prefix (str): The prefix for parameters and buffers used in this - module. - """ - for name, param in module._parameters.items(): - if param is not None: - destination[prefix + name] = param if keep_vars else param.detach() - for name, buf in module._buffers.items(): - # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d - if buf is not None: - destination[prefix + name] = buf if keep_vars else buf.detach() - - -def get_state_dict(module, destination=None, prefix='', keep_vars=False): - """Returns a dictionary containing a whole state of the module. - - Both parameters and persistent buffers (e.g. running averages) are - included. Keys are corresponding parameter and buffer names. - - This method is modified from :meth:`torch.nn.Module.state_dict` to - recursively check parallel module in case that the model has a complicated - structure, e.g., nn.Module(nn.Module(DDP)). - - Args: - module (nn.Module): The module to generate state_dict. - destination (OrderedDict): Returned dict for the state of the - module. - prefix (str): Prefix of the key. - keep_vars (bool): Whether to keep the variable property of the - parameters. Default: False. - - Returns: - dict: A dictionary containing a whole state of the module. - """ - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - - # below is the same as torch.nn.Module.state_dict() - if destination is None: - destination = OrderedDict() - destination._metadata = OrderedDict() - destination._metadata[prefix[:-1]] = local_metadata = dict( - version=module._version) - _save_to_state_dict(module, destination, prefix, keep_vars) - for name, child in module._modules.items(): - if child is not None: - get_state_dict( - child, destination, prefix + name + '.', keep_vars=keep_vars) - for hook in module._state_dict_hooks.values(): - hook_result = hook(module, destination, prefix, local_metadata) - if hook_result is not None: - destination = hook_result - return destination - - -def save_checkpoint(model, - filename, - optimizer=None, - meta=None, - file_client_args=None): - """Save checkpoint to file. - - The checkpoint will have 3 fields: ``meta``, ``state_dict`` and - ``optimizer``. By default ``meta`` will contain version and time info. - - Args: - model (Module): Module whose params are to be saved. - filename (str): Checkpoint filename. - optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. - meta (dict, optional): Metadata to be saved in checkpoint. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError(f'meta must be a dict or None, but got {type(meta)}') - meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) - - if is_module_wrapper(model): - model = model.module - - if hasattr(model, 'CLASSES') and model.CLASSES is not None: - # save class name to the meta - meta.update(CLASSES=model.CLASSES) - - checkpoint = { - 'meta': meta, - 'state_dict': weights_to_cpu(get_state_dict(model)) - } - # save optimizer state dict in the checkpoint - if isinstance(optimizer, Optimizer): - checkpoint['optimizer'] = optimizer.state_dict() - elif isinstance(optimizer, dict): - checkpoint['optimizer'] = {} - for name, optim in optimizer.items(): - checkpoint['optimizer'][name] = optim.state_dict() - - if filename.startswith('pavi://'): - if file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" if filename starts with' - f'"pavi://", but got {file_client_args}') - try: - from pavi import modelcloud - from pavi import exception - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - model_path = filename[7:] - root = modelcloud.Folder() - model_dir, model_name = osp.split(model_path) - try: - model = modelcloud.get(model_dir) - except exception.NodeNotFoundError: - model = root.create_training_model(model_dir) - with TemporaryDirectory() as tmp_dir: - checkpoint_file = osp.join(tmp_dir, model_name) - with open(checkpoint_file, 'wb') as f: - torch.save(checkpoint, f) - f.flush() - model.create_file(checkpoint_file, name=model_name) - else: - file_client = FileClient.infer_client(file_client_args, filename) - with io.BytesIO() as f: - torch.save(checkpoint, f) - file_client.put(f.getvalue(), filename) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/default_constructor.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/default_constructor.py deleted file mode 100755 index 0bad847f2..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/default_constructor.py +++ /dev/null @@ -1,44 +0,0 @@ -from .builder import RUNNER_BUILDERS, RUNNERS - - -@RUNNER_BUILDERS.register_module() -class DefaultRunnerConstructor: - """Default constructor for runners. - - Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. - For example, We can inject some new properties and functions for `Runner`. - - Example: - >>> from mmcv.runner import RUNNER_BUILDERS, build_runner - >>> # Define a new RunnerReconstructor - >>> @RUNNER_BUILDERS.register_module() - >>> class MyRunnerConstructor: - ... def __init__(self, runner_cfg, default_args=None): - ... if not isinstance(runner_cfg, dict): - ... raise TypeError('runner_cfg should be a dict', - ... f'but got {type(runner_cfg)}') - ... self.runner_cfg = runner_cfg - ... self.default_args = default_args - ... - ... def __call__(self): - ... runner = RUNNERS.build(self.runner_cfg, - ... default_args=self.default_args) - ... # Add new properties for existing runner - ... runner.my_name = 'my_runner' - ... runner.my_function = lambda self: print(self.my_name) - ... ... - >>> # build your runner - >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, - ... constructor='MyRunnerConstructor') - >>> runner = build_runner(runner_cfg) - """ - - def __init__(self, runner_cfg, default_args=None): - if not isinstance(runner_cfg, dict): - raise TypeError('runner_cfg should be a dict', - f'but got {type(runner_cfg)}') - self.runner_cfg = runner_cfg - self.default_args = default_args - - def __call__(self): - return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/dist_utils.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/dist_utils.py deleted file mode 100755 index d3a1ef3fd..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/dist_utils.py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import os -import subprocess -from collections import OrderedDict - -import torch -import torch.multiprocessing as mp -from torch import distributed as dist -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - - -def init_dist(launcher, backend='nccl', **kwargs): - if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') - if launcher == 'pytorch': - _init_dist_pytorch(backend, **kwargs) - elif launcher == 'mpi': - _init_dist_mpi(backend, **kwargs) - elif launcher == 'slurm': - _init_dist_slurm(backend, **kwargs) - else: - raise ValueError(f'Invalid launcher type: {launcher}') - - -def _init_dist_pytorch(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['RANK']) - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_mpi(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['OMPI_COMM_WORLD_RANK']) - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_slurm(backend, port=None): - """Initialize slurm distributed training environment. - - If argument ``port`` is not specified, then the master port will be system - environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system - environment variable, then a default port ``29500`` will be used. - - Args: - backend (str): Backend of torch.distributed. - port (int, optional): Master port. Defaults to None. - """ - proc_id = int(os.environ['SLURM_PROCID']) - ntasks = int(os.environ['SLURM_NTASKS']) - node_list = os.environ['SLURM_NODELIST'] - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(proc_id % num_gpus) - addr = subprocess.getoutput( - f'scontrol show hostname {node_list} | head -n1') - # specify master port - if port is not None: - os.environ['MASTER_PORT'] = str(port) - elif 'MASTER_PORT' in os.environ: - pass # use MASTER_PORT in the environment variable - else: - # 29500 is torch.distributed default port - os.environ['MASTER_PORT'] = '29500' - # use MASTER_ADDR in the environment variable if it already exists - if 'MASTER_ADDR' not in os.environ: - os.environ['MASTER_ADDR'] = addr - os.environ['WORLD_SIZE'] = str(ntasks) - os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) - os.environ['RANK'] = str(proc_id) - dist.init_process_group(backend=backend) - - -def get_dist_info(): - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - world_size = dist.get_world_size() - else: - rank = 0 - world_size = 1 - return rank, world_size - - -def master_only(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - rank, _ = get_dist_info() - if rank == 0: - return func(*args, **kwargs) - - return wrapper - - -def allreduce_params(params, coalesce=True, bucket_size_mb=-1): - """Allreduce parameters. - - Args: - params (list[torch.Parameters]): List of parameters or buffers of a - model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - _, world_size = get_dist_info() - if world_size == 1: - return - params = [param.data for param in params] - if coalesce: - _allreduce_coalesced(params, world_size, bucket_size_mb) - else: - for tensor in params: - dist.all_reduce(tensor.div_(world_size)) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - _, world_size = get_dist_info() - if world_size == 1: - return - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/epoch_based_runner.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/epoch_based_runner.py deleted file mode 100755 index 2dd29357a..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/epoch_based_runner.py +++ /dev/null @@ -1,187 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .utils import get_host_info - - -@RUNNERS.register_module() -class EpochBasedRunner(BaseRunner): - """Epoch-based Runner. - - This runner train models epoch by epoch. - """ - - def run_iter(self, data_batch, train_mode, **kwargs): - if self.batch_processor is not None: - outputs = self.batch_processor( - self.model, data_batch, train_mode=train_mode, **kwargs) - elif train_mode: - outputs = self.model.train_step(data_batch, self.optimizer, - **kwargs) - else: - outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('"batch_processor()" or "model.train_step()"' - 'and "model.val_step()" must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - self._iter += 1 - - self.call_hook('after_train_epoch') - self._epoch += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - self.call_hook('before_val_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_val_iter') - self.run_iter(data_batch, train_mode=False) - self.call_hook('after_val_iter') - - self.call_hook('after_val_epoch') - - def run(self, data_loaders, workflow, max_epochs=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, epochs) to specify the - running order and epochs. E.g, [('train', 2), ('val', 1)] means - running 2 epochs for training and 1 epoch for validation, - iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_epochs is not None: - warnings.warn( - 'setting max_epochs in run is deprecated, ' - 'please set max_epochs in runner_config', DeprecationWarning) - self._max_epochs = max_epochs - - assert self._max_epochs is not None, ( - 'max_epochs must be specified during instantiation') - - for i, flow in enumerate(workflow): - mode, epochs = flow - if mode == 'train': - self._max_iters = self._max_epochs * len(data_loaders[i]) - break - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d epochs', workflow, - self._max_epochs) - self.call_hook('before_run') - - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - mode, epochs = flow - if isinstance(mode, str): # self.train() - if not hasattr(self, mode): - raise ValueError( - f'runner has no method named "{mode}" to run an ' - 'epoch') - epoch_runner = getattr(self, mode) - else: - raise TypeError( - 'mode in workflow must be a str, but got {}'.format( - type(mode))) - - for _ in range(epochs): - if mode == 'train' and self.epoch >= self._max_epochs: - break - epoch_runner(data_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_run') - - def save_checkpoint(self, - out_dir, - filename_tmpl='epoch_{}.pth', - save_optimizer=True, - meta=None, - create_symlink=True): - """Save the checkpoint. - - Args: - out_dir (str): The directory that checkpoints are saved. - filename_tmpl (str, optional): The checkpoint filename template, - which contains a placeholder for the epoch number. - Defaults to 'epoch_{}.pth'. - save_optimizer (bool, optional): Whether to save the optimizer to - the checkpoint. Defaults to True. - meta (dict, optional): The meta information to be saved in the - checkpoint. Defaults to None. - create_symlink (bool, optional): Whether to create a symlink - "latest.pth" to point to the latest checkpoint. - Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.epoch + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - -@RUNNERS.register_module() -class Runner(EpochBasedRunner): - """Deprecated name of EpochBasedRunner.""" - - def __init__(self, *args, **kwargs): - warnings.warn( - 'Runner was deprecated, please use EpochBasedRunner instead') - super().__init__(*args, **kwargs) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/fp16_utils.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/fp16_utils.py deleted file mode 100755 index 4baab939a..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/fp16_utils.py +++ /dev/null @@ -1,410 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import warnings -from collections import abc -from inspect import getfullargspec - -import numpy as np -import torch -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .dist_utils import allreduce_grads as _allreduce_grads - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 - # manually, so the behavior may not be consistent with real amp. - from torch.cuda.amp import autocast -except ImportError: - pass - - -def cast_tensor_type(inputs, src_type, dst_type): - """Recursively convert Tensor in inputs from src_type to dst_type. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype): Source type.. - dst_type (torch.dtype): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - if isinstance(inputs, nn.Module): - return inputs - elif isinstance(inputs, torch.Tensor): - return inputs.to(dst_type) - elif isinstance(inputs, str): - return inputs - elif isinstance(inputs, np.ndarray): - return inputs - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type, dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type, dst_type) for item in inputs) - else: - return inputs - - -def auto_fp16(apply_to=None, out_fp32=False): - """Decorator to enable fp16 training automatically. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If inputs arguments are fp32 tensors, they will - be converted to fp16 automatically. Arguments other than fp32 tensors are - ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp32 (bool): Whether to convert the output back to fp32. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp16 - >>> @auto_fp16() - >>> def forward(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp16 - >>> @auto_fp16(apply_to=('pred', )) - >>> def do_something(self, pred, others): - >>> pass - """ - - def auto_fp16_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@auto_fp16 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - # NOTE: default args are not taken into consideration - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.float, torch.half)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = {} - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.float, torch.half) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=True): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp32: - output = cast_tensor_type(output, torch.half, torch.float) - return output - - return new_func - - return auto_fp16_wrapper - - -def force_fp32(apply_to=None, out_fp16=False): - """Decorator to convert input arguments to fp32 in force. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If there are some inputs that must be processed - in fp32 mode, then this decorator can handle it. If inputs arguments are - fp16 tensors, they will be converted to fp32 automatically. Arguments other - than fp16 tensors are ignored. If you are using PyTorch >= 1.6, - torch.cuda.amp is used as the backend, otherwise, original mmcv - implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp16 (bool): Whether to convert the output back to fp16. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp32 - >>> @force_fp32() - >>> def loss(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp32 - >>> @force_fp32(apply_to=('pred', )) - >>> def post_process(self, pred, others): - >>> pass - """ - - def force_fp32_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@force_fp32 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.half, torch.float)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = dict() - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.half, torch.float) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=False): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp16: - output = cast_tensor_type(output, torch.float, torch.half) - return output - - return new_func - - return force_fp32_wrapper - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - warnings.warning( - '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' - 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads') - _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) - - -def wrap_fp16_model(model): - """Wrap the FP32 model to FP16. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - For PyTorch >= 1.6, this function will - 1. Set fp16 flag inside the model to True. - - Otherwise: - 1. Convert FP32 model to FP16. - 2. Remain some necessary layers to be FP32, e.g., normalization layers. - 3. Set `fp16_enabled` flag inside the model to True. - - Args: - model (nn.Module): Model in FP32. - """ - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.6.0')): - # convert model to fp16 - model.half() - # patch the normalization layers to make it work in fp32 mode - patch_norm_fp32(model) - # set `fp16_enabled` flag - for m in model.modules(): - if hasattr(m, 'fp16_enabled'): - m.fp16_enabled = True - - -def patch_norm_fp32(module): - """Recursively convert normalization layers from FP16 to FP32. - - Args: - module (nn.Module): The modules to be converted in FP16. - - Returns: - nn.Module: The converted module, the normalization layers have been - converted to FP32. - """ - if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): - module.float() - if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': - module.forward = patch_forward_method(module.forward, torch.half, - torch.float) - for child in module.children(): - patch_norm_fp32(child) - return module - - -def patch_forward_method(func, src_type, dst_type, convert_output=True): - """Patch the forward method of a module. - - Args: - func (callable): The original forward method. - src_type (torch.dtype): Type of input arguments to be converted from. - dst_type (torch.dtype): Type of input arguments to be converted to. - convert_output (bool): Whether to convert the output back to src_type. - - Returns: - callable: The patched forward method. - """ - - def new_forward(*args, **kwargs): - output = func(*cast_tensor_type(args, src_type, dst_type), - **cast_tensor_type(kwargs, src_type, dst_type)) - if convert_output: - output = cast_tensor_type(output, dst_type, src_type) - return output - - return new_forward - - -class LossScaler: - """Class that manages loss scaling in mixed precision training which - supports both dynamic or static mode. - - The implementation refers to - https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. - Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. - It's important to understand how :class:`LossScaler` operates. - Loss scaling is designed to combat the problem of underflowing - gradients encountered at long times when training fp16 networks. - Dynamic loss scaling begins by attempting a very high loss - scale. Ironically, this may result in OVERflowing gradients. - If overflowing gradients are encountered, :class:`FP16_Optimizer` then - skips the update step for this particular iteration/minibatch, - and :class:`LossScaler` adjusts the loss scale to a lower value. - If a certain number of iterations occur without overflowing gradients - detected,:class:`LossScaler` increases the loss scale once more. - In this way :class:`LossScaler` attempts to "ride the edge" of always - using the highest loss scale possible without incurring overflow. - - Args: - init_scale (float): Initial loss scale value, default: 2**32. - scale_factor (float): Factor used when adjusting the loss scale. - Default: 2. - mode (str): Loss scaling mode. 'dynamic' or 'static' - scale_window (int): Number of consecutive iterations without an - overflow to wait before increasing the loss scale. Default: 1000. - """ - - def __init__(self, - init_scale=2**32, - mode='dynamic', - scale_factor=2., - scale_window=1000): - self.cur_scale = init_scale - self.cur_iter = 0 - assert mode in ('dynamic', - 'static'), 'mode can only be dynamic or static' - self.mode = mode - self.last_overflow_iter = -1 - self.scale_factor = scale_factor - self.scale_window = scale_window - - def has_overflow(self, params): - """Check if params contain overflow.""" - if self.mode != 'dynamic': - return False - for p in params: - if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): - return True - return False - - def _has_inf_or_nan(x): - """Check if params contain NaN.""" - try: - cpu_sum = float(x.float().sum()) - except RuntimeError as instance: - if 'value cannot be converted' not in instance.args[0]: - raise - return True - else: - if cpu_sum == float('inf') or cpu_sum == -float('inf') \ - or cpu_sum != cpu_sum: - return True - return False - - def update_scale(self, overflow): - """update the current loss scale value when overflow happens.""" - if self.mode != 'dynamic': - return - if overflow: - self.cur_scale = max(self.cur_scale / self.scale_factor, 1) - self.last_overflow_iter = self.cur_iter - else: - if (self.cur_iter - self.last_overflow_iter) % \ - self.scale_window == 0: - self.cur_scale *= self.scale_factor - self.cur_iter += 1 - - def state_dict(self): - """Returns the state of the scaler as a :class:`dict`.""" - return dict( - cur_scale=self.cur_scale, - cur_iter=self.cur_iter, - mode=self.mode, - last_overflow_iter=self.last_overflow_iter, - scale_factor=self.scale_factor, - scale_window=self.scale_window) - - def load_state_dict(self, state_dict): - """Loads the loss_scaler state dict. - - Args: - state_dict (dict): scaler state. - """ - self.cur_scale = state_dict['cur_scale'] - self.cur_iter = state_dict['cur_iter'] - self.mode = state_dict['mode'] - self.last_overflow_iter = state_dict['last_overflow_iter'] - self.scale_factor = state_dict['scale_factor'] - self.scale_window = state_dict['scale_window'] - - @property - def loss_scale(self): - return self.cur_scale diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/__init__.py deleted file mode 100755 index 915af28ce..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkpoint import CheckpointHook -from .closure import ClosureHook -from .ema import EMAHook -from .evaluation import DistEvalHook, EvalHook -from .hook import HOOKS, Hook -from .iter_timer import IterTimerHook -from .logger import (DvcliveLoggerHook, LoggerHook, MlflowLoggerHook, - NeptuneLoggerHook, PaviLoggerHook, TensorboardLoggerHook, - TextLoggerHook, WandbLoggerHook) -from .lr_updater import LrUpdaterHook -from .memory import EmptyCacheHook -from .momentum_updater import MomentumUpdaterHook -from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, OptimizerHook) -from .profiler import ProfilerHook -from .sampler_seed import DistSamplerSeedHook -from .sync_buffer import SyncBuffersHook - -__all__ = [ - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'OptimizerHook', 'Fp16OptimizerHook', 'IterTimerHook', - 'DistSamplerSeedHook', 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'DvcliveLoggerHook', - 'MomentumUpdaterHook', 'SyncBuffersHook', 'EMAHook', 'EvalHook', - 'DistEvalHook', 'ProfilerHook', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/checkpoint.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/checkpoint.py deleted file mode 100755 index 7bb75f402..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/checkpoint.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -from mmcv.fileio import FileClient -from ..dist_utils import allreduce_params, master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class CheckpointHook(Hook): - """Save checkpoints periodically. - - Args: - interval (int): The saving period. If ``by_epoch=True``, interval - indicates epochs, otherwise it indicates iterations. - Default: -1, which means "never". - by_epoch (bool): Saving checkpoints by epoch or by iteration. - Default: True. - save_optimizer (bool): Whether to save optimizer state_dict in the - checkpoint. It is usually used for resuming experiments. - Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, ``runner.work_dir`` will be used by default. If - specified, the ``out_dir`` will be the concatenation of ``out_dir`` - and the last level directory of ``runner.work_dir``. - `Changed in version 1.3.16.` - max_keep_ckpts (int, optional): The maximum checkpoints to keep. - In some cases we want only the latest few checkpoints and would - like to delete old ones to save the disk space. - Default: -1, which means unlimited. - save_last (bool, optional): Whether to force the last checkpoint to be - saved regardless of interval. Default: True. - sync_buffer (bool, optional): Whether to synchronize buffers in - different gpus. Default: False. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - - .. warning:: - Before v1.3.16, the ``out_dir`` argument indicates the path where the - checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the - root directory and the final path to save checkpoint is the - concatenation of ``out_dir`` and the last level directory of - ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" - and the value of ``runner.work_dir`` is "/path/of/B", then the final - path will be "/path/of/A/B". - """ - - def __init__(self, - interval=-1, - by_epoch=True, - save_optimizer=True, - out_dir=None, - max_keep_ckpts=-1, - save_last=True, - sync_buffer=False, - file_client_args=None, - **kwargs): - self.interval = interval - self.by_epoch = by_epoch - self.save_optimizer = save_optimizer - self.out_dir = out_dir - self.max_keep_ckpts = max_keep_ckpts - self.save_last = save_last - self.args = kwargs - self.sync_buffer = sync_buffer - self.file_client_args = file_client_args - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - - runner.logger.info((f'Checkpoints will be saved to {self.out_dir} by ' - f'{self.file_client.name}.')) - - # disable the create_symlink option because some file backends do not - # allow to create a symlink - if 'create_symlink' in self.args: - if self.args[ - 'create_symlink'] and not self.file_client.allow_symlink: - self.args['create_symlink'] = False - warnings.warn( - ('create_symlink is set as True by the user but is changed' - 'to be False because creating symbolic link is not ' - f'allowed in {self.file_client.name}')) - else: - self.args['create_symlink'] = self.file_client.allow_symlink - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` epochs - # 2. reach the last epoch of training - if self.every_n_epochs( - runner, self.interval) or (self.save_last - and self.is_last_epoch(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.epoch + 1} epochs') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) - - @master_only - def _save_checkpoint(self, runner): - """Save the current checkpoint and delete unwanted checkpoint.""" - runner.save_checkpoint( - self.out_dir, save_optimizer=self.save_optimizer, **self.args) - if runner.meta is not None: - if self.by_epoch: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) - else: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) - runner.meta.setdefault('hook_msgs', dict()) - runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( - self.out_dir, cur_ckpt_filename) - # remove other checkpoints - if self.max_keep_ckpts > 0: - if self.by_epoch: - name = 'epoch_{}.pth' - current_ckpt = runner.epoch + 1 - else: - name = 'iter_{}.pth' - current_ckpt = runner.iter + 1 - redundant_ckpts = range( - current_ckpt - self.max_keep_ckpts * self.interval, 0, - -self.interval) - filename_tmpl = self.args.get('filename_tmpl', name) - for _step in redundant_ckpts: - ckpt_path = self.file_client.join_path( - self.out_dir, filename_tmpl.format(_step)) - if self.file_client.isfile(ckpt_path): - self.file_client.remove(ckpt_path) - else: - break - - def after_train_iter(self, runner): - if self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` iterations - # 2. reach the last iteration of training - if self.every_n_iters( - runner, self.interval) or (self.save_last - and self.is_last_iter(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.iter + 1} iterations') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/closure.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/closure.py deleted file mode 100755 index b955f81f4..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/closure.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ClosureHook(Hook): - - def __init__(self, fn_name, fn): - assert hasattr(self, fn_name) - assert callable(fn) - setattr(self, fn_name, fn) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/ema.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/ema.py deleted file mode 100755 index 15c7e6808..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/ema.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...parallel import is_module_wrapper -from ..hooks.hook import HOOKS, Hook - - -@HOOKS.register_module() -class EMAHook(Hook): - r"""Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. - - .. math:: - - \text{Xema\_{t+1}} = (1 - \text{momentum}) \times - \text{Xema\_{t}} + \text{momentum} \times X_t - - Args: - momentum (float): The momentum used for updating ema parameter. - Defaults to 0.0002. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - warm_up (int): During first warm_up steps, we may use smaller momentum - to update ema parameters more slowly. Defaults to 100. - resume_from (str): The checkpoint path. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - warm_up=100, - resume_from=None): - assert isinstance(interval, int) and interval > 0 - self.warm_up = warm_up - self.interval = interval - assert momentum > 0 and momentum < 1 - self.momentum = momentum**interval - self.checkpoint = resume_from - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - self.model_parameters = dict(model.named_parameters(recurse=True)) - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers(recurse=True)) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - curr_step = runner.iter - # We warm up the momentum considering the instability at beginning - momentum = min(self.momentum, - (1 + curr_step) / (self.warm_up + curr_step)) - if curr_step % self.interval != 0: - return - for name, parameter in self.model_parameters.items(): - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/evaluation.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/evaluation.py deleted file mode 100755 index 1eeb44650..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/evaluation.py +++ /dev/null @@ -1,509 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from math import inf - -import torch.distributed as dist -from torch.nn.modules.batchnorm import _BatchNorm -from torch.utils.data import DataLoader - -from mmcv.fileio import FileClient -from mmcv.utils import is_seq_of -from .hook import Hook -from .logger import LoggerHook - - -class EvalHook(Hook): - """Non-Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader, and return the test results. If ``None``, the default - test function ``mmcv.engine.single_gpu_test`` will be used. - (default: ``None``) - greater_keys (List[str] | None, optional): Metric keys that will be - inferred by 'greater' comparison rule. If ``None``, - _default_greater_keys will be used. (default: ``None``) - less_keys (List[str] | None, optional): Metric keys that will be - inferred by 'less' comparison rule. If ``None``, _default_less_keys - will be used. (default: ``None``) - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - `New in version 1.3.16.` - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - `New in version 1.3.16.` - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - - Notes: - If new arguments are added for EvalHook, tools/test.py, - tools/eval_metric.py may be affected. - """ - - # Since the key for determine greater or less is related to the downstream - # tasks, downstream repos may need to overwrite the following inner - # variable accordingly. - - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - _default_greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - _default_less_keys = ['loss'] - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - out_dir=None, - file_client_args=None, - **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError(f'dataloader must be a pytorch DataLoader, ' - f'but got {type(dataloader)}') - - if interval <= 0: - raise ValueError(f'interval must be a positive number, ' - f'but got {interval}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' - - if start is not None and start < 0: - raise ValueError(f'The evaluation start epoch {start} is smaller ' - f'than 0') - - self.dataloader = dataloader - self.interval = interval - self.start = start - self.by_epoch = by_epoch - - assert isinstance(save_best, str) or save_best is None, \ - '""save_best"" should be a str or None ' \ - f'rather than {type(save_best)}' - self.save_best = save_best - self.eval_kwargs = eval_kwargs - self.initial_flag = True - - if test_fn is None: - from mmcv.engine import single_gpu_test - self.test_fn = single_gpu_test - else: - self.test_fn = test_fn - - if greater_keys is None: - self.greater_keys = self._default_greater_keys - else: - if not isinstance(greater_keys, (list, tuple)): - greater_keys = (greater_keys, ) - assert is_seq_of(greater_keys, str) - self.greater_keys = greater_keys - - if less_keys is None: - self.less_keys = self._default_less_keys - else: - if not isinstance(less_keys, (list, tuple)): - less_keys = (less_keys, ) - assert is_seq_of(less_keys, str) - self.less_keys = less_keys - - if self.save_best is not None: - self.best_ckpt_path = None - self._init_rule(rule, self.save_best) - - self.out_dir = out_dir - self.file_client_args = file_client_args - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific (note that the key indicator matching - is case-insensitive): - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator != 'auto': - # `_lc` here means we use the lower case of keys for - # case-insensitive matching - key_indicator_lc = key_indicator.lower() - greater_keys = [key.lower() for key in self.greater_keys] - less_keys = [key.lower() for key in self.less_keys] - - if key_indicator_lc in greater_keys: - rule = 'greater' - elif key_indicator_lc in less_keys: - rule = 'less' - elif any(key in key_indicator_lc for key in greater_keys): - rule = 'greater' - elif any(key in key_indicator_lc for key in less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - if self.rule is not None: - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'The best checkpoint will be saved to {self.out_dir} by ' - f'{self.file_client.name}')) - - if self.save_best is not None: - if runner.meta is None: - warnings.warn('runner.meta is None. Creating an empty one.') - runner.meta = dict() - runner.meta.setdefault('hook_msgs', dict()) - self.best_ckpt_path = runner.meta['hook_msgs'].get( - 'best_ckpt', None) - - def before_train_iter(self, runner): - """Evaluate the model only at the start of training by iteration.""" - if self.by_epoch or not self.initial_flag: - return - if self.start is not None and runner.iter >= self.start: - self.after_train_iter(runner) - self.initial_flag = False - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - if not (self.by_epoch and self.initial_flag): - return - if self.start is not None and runner.epoch >= self.start: - self.after_train_epoch(runner) - self.initial_flag = False - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch and self._should_evaluate(runner): - # Because the priority of EvalHook is higher than LoggerHook, the - # training log and the evaluating log are mixed. Therefore, - # we need to dump the training log and clear it before evaluating - # log is generated. In addition, this problem will only appear in - # `IterBasedRunner` whose `self.by_epoch` is False, because - # `EpochBasedRunner` whose `self.by_epoch` is True calls - # `_do_evaluate` in `after_train_epoch` stage, and at this stage - # the training log has been printed, so it will not cause any - # problem. more details at - # https://github.com/open-mmlab/mmsegmentation/issues/694 - for hook in runner._hooks: - if isinstance(hook, LoggerHook): - hook.after_train_iter(runner) - runner.log_buffer.clear() - - self._do_evaluate(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch and self._should_evaluate(runner): - self._do_evaluate(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - results = self.test_fn(runner.model, self.dataloader) - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - def _should_evaluate(self, runner): - """Judge whether to perform evaluation. - - Here is the rule to judge whether to perform evaluation: - 1. It will not perform evaluation during the epoch/iteration interval, - which is determined by ``self.interval``. - 2. It will not perform evaluation if the start time is larger than - current time. - 3. It will not perform evaluation when current time is larger than - the start time but during epoch/iteration interval. - - Returns: - bool: The flag indicating whether to perform evaluation. - """ - if self.by_epoch: - current = runner.epoch - check_time = self.every_n_epochs - else: - current = runner.iter - check_time = self.every_n_iters - - if self.start is None: - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - elif (current + 1) < self.start: - # No evaluation if start is larger than the current time. - return False - else: - # Evaluation only at epochs/iters 3, 5, 7... - # if start==3 and interval==2 - if (current + 1 - self.start) % self.interval: - return False - return True - - def _save_ckpt(self, runner, key_score): - """Save the best checkpoint. - - It will compare the score according to the compare function, write - related information (best score, best checkpoint path) and save the - best checkpoint into ``work_dir``. - """ - if self.by_epoch: - current = f'epoch_{runner.epoch + 1}' - cur_type, cur_time = 'epoch', runner.epoch + 1 - else: - current = f'iter_{runner.iter + 1}' - cur_type, cur_time = 'iter', runner.iter + 1 - - best_score = runner.meta['hook_msgs'].get( - 'best_score', self.init_value_map[self.rule]) - if self.compare_func(key_score, best_score): - best_score = key_score - runner.meta['hook_msgs']['best_score'] = best_score - - if self.best_ckpt_path and self.file_client.isfile( - self.best_ckpt_path): - self.file_client.remove(self.best_ckpt_path) - runner.logger.info( - (f'The previous best checkpoint {self.best_ckpt_path} was ' - 'removed')) - - best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' - self.best_ckpt_path = self.file_client.join_path( - self.out_dir, best_ckpt_name) - runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path - - runner.save_checkpoint( - self.out_dir, best_ckpt_name, create_symlink=False) - runner.logger.info( - f'Now best checkpoint is saved as {best_ckpt_name}.') - runner.logger.info( - f'Best {self.key_indicator} is {best_score:0.4f} ' - f'at {cur_time} {cur_type}.') - - def evaluate(self, runner, results): - """Evaluate the results. - - Args: - runner (:obj:`mmcv.Runner`): The underlined training runner. - results (list): Output results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - - if self.save_best is not None: - # If the performance of model is pool, the `eval_res` may be an - # empty dict and it will raise exception when `self.save_best` is - # not None. More details at - # https://github.com/open-mmlab/mmdetection/issues/6265. - if not eval_res: - warnings.warn( - 'Since `eval_res` is an empty dict, the behavior to save ' - 'the best checkpoint will be skipped in this evaluation.') - return None - - if self.key_indicator == 'auto': - # infer from eval_results - self._init_rule(self.rule, list(eval_res.keys())[0]) - return eval_res[self.key_indicator] - - return None - - -class DistEvalHook(EvalHook): - """Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader in a multi-gpu manner, and return the test results. If - ``None``, the default test function ``mmcv.engine.multi_gpu_test`` - will be used. (default: ``None``) - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - broadcast_bn_buffer (bool): Whether to broadcast the - buffer(running_mean and running_var) of rank 0 to other rank - before evaluation. Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - """ - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - broadcast_bn_buffer=True, - tmpdir=None, - gpu_collect=False, - out_dir=None, - file_client_args=None, - **eval_kwargs): - - if test_fn is None: - from mmcv.engine import multi_gpu_test - test_fn = multi_gpu_test - - super().__init__( - dataloader, - start=start, - interval=interval, - by_epoch=by_epoch, - save_best=save_best, - rule=rule, - test_fn=test_fn, - greater_keys=greater_keys, - less_keys=less_keys, - out_dir=out_dir, - file_client_args=file_client_args, - **eval_kwargs) - - self.broadcast_bn_buffer = broadcast_bn_buffer - self.tmpdir = tmpdir - self.gpu_collect = gpu_collect - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - results = self.test_fn( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to - # save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/hook.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/hook.py deleted file mode 100755 index f2d1c9865..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/hook.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, is_method_overridden - -HOOKS = Registry('hook') - - -class Hook: - stages = ('before_run', 'before_train_epoch', 'before_train_iter', - 'after_train_iter', 'after_train_epoch', 'before_val_epoch', - 'before_val_iter', 'after_val_iter', 'after_val_epoch', - 'after_run') - - def before_run(self, runner): - pass - - def after_run(self, runner): - pass - - def before_epoch(self, runner): - pass - - def after_epoch(self, runner): - pass - - def before_iter(self, runner): - pass - - def after_iter(self, runner): - pass - - def before_train_epoch(self, runner): - self.before_epoch(runner) - - def before_val_epoch(self, runner): - self.before_epoch(runner) - - def after_train_epoch(self, runner): - self.after_epoch(runner) - - def after_val_epoch(self, runner): - self.after_epoch(runner) - - def before_train_iter(self, runner): - self.before_iter(runner) - - def before_val_iter(self, runner): - self.before_iter(runner) - - def after_train_iter(self, runner): - self.after_iter(runner) - - def after_val_iter(self, runner): - self.after_iter(runner) - - def every_n_epochs(self, runner, n): - return (runner.epoch + 1) % n == 0 if n > 0 else False - - def every_n_inner_iters(self, runner, n): - return (runner.inner_iter + 1) % n == 0 if n > 0 else False - - def every_n_iters(self, runner, n): - return (runner.iter + 1) % n == 0 if n > 0 else False - - def end_of_epoch(self, runner): - return runner.inner_iter + 1 == len(runner.data_loader) - - def is_last_epoch(self, runner): - return runner.epoch + 1 == runner._max_epochs - - def is_last_iter(self, runner): - return runner.iter + 1 == runner._max_iters - - def get_triggered_stages(self): - trigger_stages = set() - for stage in Hook.stages: - if is_method_overridden(stage, Hook, self): - trigger_stages.add(stage) - - # some methods will be triggered in multi stages - # use this dict to map method to stages. - method_stages_map = { - 'before_epoch': ['before_train_epoch', 'before_val_epoch'], - 'after_epoch': ['after_train_epoch', 'after_val_epoch'], - 'before_iter': ['before_train_iter', 'before_val_iter'], - 'after_iter': ['after_train_iter', 'after_val_iter'], - } - - for method, map_stages in method_stages_map.items(): - if is_method_overridden(method, Hook, self): - trigger_stages.update(map_stages) - - return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/iter_timer.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/iter_timer.py deleted file mode 100755 index f91d2492d..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/iter_timer.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright (c) OpenMMLab. All rights reserved. - -import time -import torch.distributed as dist - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class IterTimerHook(Hook): - - def before_epoch(self, runner): - self.t = time.time() - - def before_iter(self, runner): - self.batch_size = runner.data_loader._dataloader.batch_size - runner.log_buffer.update({'data_time': time.time() - self.t}) - - def after_iter(self, runner): - iter_info = {'time': time.time() - self.t} - fps = self.batch_size / iter_info["time"] - - if dist.is_initialized(): - fps = fps * dist.get_world_size() - - iter_info["fps"] = fps - - runner.log_buffer.update(iter_info) - self.t = time.time() diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/__init__.py deleted file mode 100755 index a0b6b3456..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import LoggerHook -from .dvclive import DvcliveLoggerHook -from .mlflow import MlflowLoggerHook -from .neptune import NeptuneLoggerHook -from .pavi import PaviLoggerHook -from .tensorboard import TensorboardLoggerHook -from .text import TextLoggerHook -from .wandb import WandbLoggerHook - -__all__ = [ - 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', - 'NeptuneLoggerHook', 'DvcliveLoggerHook' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/base.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/base.py deleted file mode 100755 index f84525672..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/base.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from abc import ABCMeta, abstractmethod - -import numpy as np -import torch - -from ..hook import Hook - - -class LoggerHook(Hook): - """Base class for logger hooks. - - Args: - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging. - by_epoch (bool): Whether EpochBasedRunner is used. - """ - - __metaclass__ = ABCMeta - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - self.interval = interval - self.ignore_last = ignore_last - self.reset_flag = reset_flag - self.by_epoch = by_epoch - - @abstractmethod - def log(self, runner): - pass - - @staticmethod - def is_scalar(val, include_np=True, include_torch=True): - """Tell the input variable is a scalar or not. - - Args: - val: Input variable. - include_np (bool): Whether include 0-d np.ndarray as a scalar. - include_torch (bool): Whether include 0-d torch.Tensor as a scalar. - - Returns: - bool: True or False. - """ - if isinstance(val, numbers.Number): - return True - elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: - return True - elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: - return True - else: - return False - - def get_mode(self, runner): - if runner.mode == 'train': - if 'time' in runner.log_buffer.output: - mode = 'train' - else: - mode = 'val' - elif runner.mode == 'val': - mode = 'val' - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return mode - - def get_epoch(self, runner): - if runner.mode == 'train': - epoch = runner.epoch + 1 - elif runner.mode == 'val': - # normal val mode - # runner.epoch += 1 has been done before val workflow - epoch = runner.epoch - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return epoch - - def get_iter(self, runner, inner_iter=False): - """Get the current training iteration step.""" - if self.by_epoch and inner_iter: - current_iter = runner.inner_iter + 1 - else: - current_iter = runner.iter + 1 - return current_iter - - def get_lr_tags(self, runner): - tags = {} - lrs = runner.current_lr() - if isinstance(lrs, dict): - for name, value in lrs.items(): - tags[f'learning_rate/{name}'] = value[0] - else: - tags['learning_rate'] = lrs[0] - return tags - - def get_momentum_tags(self, runner): - tags = {} - momentums = runner.current_momentum() - if isinstance(momentums, dict): - for name, value in momentums.items(): - tags[f'momentum/{name}'] = value[0] - else: - tags['momentum'] = momentums[0] - return tags - - def get_loggable_tags(self, - runner, - allow_scalar=True, - allow_text=False, - add_mode=True, - tags_to_skip=('time', 'data_time')): - tags = {} - for var, val in runner.log_buffer.output.items(): - if var in tags_to_skip: - continue - if self.is_scalar(val) and not allow_scalar: - continue - if isinstance(val, str) and not allow_text: - continue - if add_mode: - var = f'{self.get_mode(runner)}/{var}' - tags[var] = val - tags.update(self.get_lr_tags(runner)) - tags.update(self.get_momentum_tags(runner)) - return tags - - def before_run(self, runner): - for hook in runner.hooks[::-1]: - if isinstance(hook, LoggerHook): - hook.reset_flag = True - break - - def before_epoch(self, runner): - runner.log_buffer.clear() # clear logs of last epoch - - def after_train_iter(self, runner): - if self.by_epoch and self.every_n_inner_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif not self.by_epoch and self.every_n_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif self.end_of_epoch(runner) and not self.ignore_last: - # not precise but more stable - runner.log_buffer.average(self.interval) - - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_train_epoch(self, runner): - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_val_epoch(self, runner): - runner.log_buffer.average() - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/dvclive.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/dvclive.py deleted file mode 100755 index 687cdc58c..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/dvclive.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class DvcliveLoggerHook(LoggerHook): - """Class to log metrics with dvclive. - - It requires `dvclive`_ to be installed. - - Args: - path (str): Directory where dvclive will write TSV log files. - interval (int): Logging interval (every k iterations). - Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. - Default: True. - - .. _dvclive: - https://dvc.org/doc/dvclive - """ - - def __init__(self, - path, - interval=10, - ignore_last=True, - reset_flag=True, - by_epoch=True): - - super(DvcliveLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.path = path - self.import_dvclive() - - def import_dvclive(self): - try: - import dvclive - except ImportError: - raise ImportError( - 'Please run "pip install dvclive" to install dvclive') - self.dvclive = dvclive - - @master_only - def before_run(self, runner): - self.dvclive.init(self.path) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for k, v in tags.items(): - self.dvclive.log(k, v, step=self.get_iter(runner)) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/mlflow.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/mlflow.py deleted file mode 100755 index f9a72592b..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/mlflow.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class MlflowLoggerHook(LoggerHook): - - def __init__(self, - exp_name=None, - tags=None, - log_model=True, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - """Class to log metrics and (optionally) a trained model to MLflow. - - It requires `MLflow`_ to be installed. - - Args: - exp_name (str, optional): Name of the experiment to be used. - Default None. - If not None, set the active experiment. - If experiment does not exist, an experiment with provided name - will be created. - tags (dict of str: str, optional): Tags for the current run. - Default None. - If not None, set tags for the current run. - log_model (bool, optional): Whether to log an MLflow artifact. - Default True. - If True, log runner.model as an MLflow artifact - for the current run. - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging - by_epoch (bool): Whether EpochBasedRunner is used. - - .. _MLflow: - https://www.mlflow.org/docs/latest/index.html - """ - super(MlflowLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_mlflow() - self.exp_name = exp_name - self.tags = tags - self.log_model = log_model - - def import_mlflow(self): - try: - import mlflow - import mlflow.pytorch as mlflow_pytorch - except ImportError: - raise ImportError( - 'Please run "pip install mlflow" to install mlflow') - self.mlflow = mlflow - self.mlflow_pytorch = mlflow_pytorch - - @master_only - def before_run(self, runner): - super(MlflowLoggerHook, self).before_run(runner) - if self.exp_name is not None: - self.mlflow.set_experiment(self.exp_name) - if self.tags is not None: - self.mlflow.set_tags(self.tags) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.mlflow.log_metrics(tags, step=self.get_iter(runner)) - - @master_only - def after_run(self, runner): - if self.log_model: - self.mlflow_pytorch.log_model(runner.model, 'models') diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/neptune.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/neptune.py deleted file mode 100755 index 7a38772b0..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/neptune.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class NeptuneLoggerHook(LoggerHook): - """Class to log metrics to NeptuneAI. - - It requires `neptune-client` to be installed. - - Args: - init_kwargs (dict): a dict contains the initialization keys as below: - - project (str): Name of a project in a form of - namespace/project_name. If None, the value of - NEPTUNE_PROJECT environment variable will be taken. - - api_token (str): User’s API token. - If None, the value of NEPTUNE_API_TOKEN environment - variable will be taken. Note: It is strongly recommended - to use NEPTUNE_API_TOKEN environment variable rather than - placing your API token in plain text in your source code. - - name (str, optional, default is 'Untitled'): Editable name of - the run. Name is displayed in the run's Details and in - Runs table as a column. - Check https://docs.neptune.ai/api-reference/neptune#init for - more init arguments. - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging - by_epoch (bool): Whether EpochBasedRunner is used. - - .. _NeptuneAI: - https://docs.neptune.ai/you-should-know/logging-metadata - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=True, - with_step=True, - by_epoch=True): - - super(NeptuneLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_neptune() - self.init_kwargs = init_kwargs - self.with_step = with_step - - def import_neptune(self): - try: - import neptune.new as neptune - except ImportError: - raise ImportError( - 'Please run "pip install neptune-client" to install neptune') - self.neptune = neptune - self.run = None - - @master_only - def before_run(self, runner): - if self.init_kwargs: - self.run = self.neptune.init(**self.init_kwargs) - else: - self.run = self.neptune.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for tag_name, tag_value in tags.items(): - if self.with_step: - self.run[tag_name].log( - tag_value, step=self.get_iter(runner)) - else: - tags['global_step'] = self.get_iter(runner) - self.run[tag_name].log(tags) - - @master_only - def after_run(self, runner): - self.run.stop() diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/pavi.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/pavi.py deleted file mode 100755 index ba2f6e8df..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/pavi.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp - -import torch -import yaml - -import mmcv -from ....parallel.utils import is_module_wrapper -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class PaviLoggerHook(LoggerHook): - - def __init__(self, - init_kwargs=None, - add_graph=False, - add_last_ckpt=False, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - img_key='img_info'): - super(PaviLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.init_kwargs = init_kwargs - self.add_graph = add_graph - self.add_last_ckpt = add_last_ckpt - self.img_key = img_key - - @master_only - def before_run(self, runner): - super(PaviLoggerHook, self).before_run(runner) - try: - from pavi import SummaryWriter - except ImportError: - raise ImportError('Please run "pip install pavi" to install pavi.') - - self.run_name = runner.work_dir.split('/')[-1] - - if not self.init_kwargs: - self.init_kwargs = dict() - self.init_kwargs['name'] = self.run_name - self.init_kwargs['model'] = runner._model_name - if runner.meta is not None: - if 'config_dict' in runner.meta: - config_dict = runner.meta['config_dict'] - assert isinstance( - config_dict, - dict), ('meta["config_dict"] has to be of a dict, ' - f'but got {type(config_dict)}') - elif 'config_file' in runner.meta: - config_file = runner.meta['config_file'] - config_dict = dict(mmcv.Config.fromfile(config_file)) - else: - config_dict = None - if config_dict is not None: - # 'max_.*iter' is parsed in pavi sdk as the maximum iterations - # to properly set up the progress bar. - config_dict = config_dict.copy() - config_dict.setdefault('max_iter', runner.max_iters) - # non-serializable values are first converted in - # mmcv.dump to json - config_dict = json.loads( - mmcv.dump(config_dict, file_format='json')) - session_text = yaml.dump(config_dict) - self.init_kwargs['session_text'] = session_text - self.writer = SummaryWriter(**self.init_kwargs) - - def get_step(self, runner): - """Get the total training step/epoch.""" - if self.get_mode(runner) == 'val' and self.by_epoch: - return self.get_epoch(runner) - else: - return self.get_iter(runner) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, add_mode=False) - if tags: - self.writer.add_scalars( - self.get_mode(runner), tags, self.get_step(runner)) - - @master_only - def after_run(self, runner): - if self.add_last_ckpt: - ckpt_path = osp.join(runner.work_dir, 'latest.pth') - if osp.islink(ckpt_path): - ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) - - if osp.isfile(ckpt_path): - # runner.epoch += 1 has been done before `after_run`. - iteration = runner.epoch if self.by_epoch else runner.iter - return self.writer.add_snapshot_file( - tag=self.run_name, - snapshot_file_path=ckpt_path, - iteration=iteration) - - # flush the buffer and send a task ending signal to Pavi - self.writer.close() - - @master_only - def before_epoch(self, runner): - if runner.epoch == 0 and self.add_graph: - if is_module_wrapper(runner.model): - _model = runner.model.module - else: - _model = runner.model - device = next(_model.parameters()).device - data = next(iter(runner.data_loader)) - image = data[self.img_key][0:1].to(device) - with torch.no_grad(): - self.writer.add_graph(_model, image) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/tensorboard.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/tensorboard.py deleted file mode 100755 index a8d50366f..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/tensorboard.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import TORCH_VERSION, digit_version -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TensorboardLoggerHook(LoggerHook): - - def __init__(self, - log_dir=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(TensorboardLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.log_dir = log_dir - - @master_only - def before_run(self, runner): - super(TensorboardLoggerHook, self).before_run(runner) - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.1')): - try: - from tensorboardX import SummaryWriter - except ImportError: - raise ImportError('Please install tensorboardX to use ' - 'TensorboardLoggerHook.') - else: - try: - from torch.utils.tensorboard import SummaryWriter - except ImportError: - raise ImportError( - 'Please run "pip install future tensorboard" to install ' - 'the dependencies to use torch.utils.tensorboard ' - '(applicable to PyTorch 1.1 or higher)') - - if self.log_dir is None: - self.log_dir = osp.join(runner.work_dir, 'tf_logs') - self.writer = SummaryWriter(self.log_dir) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, allow_text=True) - for tag, val in tags.items(): - if isinstance(val, str): - self.writer.add_text(tag, val, self.get_iter(runner)) - else: - self.writer.add_scalar(tag, val, self.get_iter(runner)) - - @master_only - def after_run(self, runner): - self.writer.close() diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/text.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/text.py deleted file mode 100755 index 043c7bf20..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/text.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import datetime -import os -import os.path as osp -from collections import OrderedDict - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.fileio.file_client import FileClient -from mmcv.utils import is_tuple_of, scandir -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TextLoggerHook(LoggerHook): - """Logger hook in text. - - In this logger hook, the information will be printed on terminal and - saved in json file. - - Args: - by_epoch (bool, optional): Whether EpochBasedRunner is used. - Default: True. - interval (int, optional): Logging interval (every k iterations). - Default: 10. - ignore_last (bool, optional): Ignore the log of last iterations in each - epoch if less than :attr:`interval`. Default: True. - reset_flag (bool, optional): Whether to clear the output buffer after - logging. Default: False. - interval_exp_name (int, optional): Logging interval for experiment - name. This feature is to help users conveniently get the experiment - information from screen or log file. Default: 1000. - out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. - If ``out_dir`` is specified, logs will be copied to a new directory - which is the concatenation of ``out_dir`` and the last level - directory of ``runner.work_dir``. Default: None. - `New in version 1.3.16.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be copied to ``out_dir``. - Default: ('.log.json', '.log', '.py'). - `New in version 1.3.16.` - keep_local (bool, optional): Whether to keep local log when - :attr:`out_dir` is specified. If False, the local log will be - removed. Default: True. - `New in version 1.3.16.` - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - - def __init__(self, - by_epoch=True, - interval=10, - ignore_last=True, - reset_flag=False, - interval_exp_name=1000, - out_dir=None, - out_suffix=('.log.json', '.log', '.py'), - keep_local=True, - file_client_args=None): - super(TextLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.by_epoch = by_epoch - self.time_sec_tot = 0 - self.interval_exp_name = interval_exp_name - - if out_dir is None and file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" when `out_dir` is not' - 'specified.') - self.out_dir = out_dir - - if not (out_dir is None or isinstance(out_dir, str) - or is_tuple_of(out_dir, str)): - raise TypeError('out_dir should be "None" or string or tuple of ' - 'string, but got {out_dir}') - self.out_suffix = out_suffix - - self.keep_local = keep_local - self.file_client_args = file_client_args - if self.out_dir is not None: - self.file_client = FileClient.infer_client(file_client_args, - self.out_dir) - - def before_run(self, runner): - super(TextLoggerHook, self).before_run(runner) - - if self.out_dir is not None: - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - # The final `self.out_dir` is the concatenation of `self.out_dir` - # and the last level directory of `runner.work_dir` - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'Text logs will be saved to {self.out_dir} by ' - f'{self.file_client.name} after the training process.')) - - self.start_iter = runner.iter - self.json_log_path = osp.join(runner.work_dir, - f'{runner.timestamp}.log.json') - if runner.meta is not None: - self._dump_log(runner.meta, runner) - - def _get_max_memory(self, runner): - device = getattr(runner.model, 'output_device', None) - mem = torch.cuda.max_memory_allocated(device=device) - mem_mb = torch.tensor([mem / (1024 * 1024)], - dtype=torch.int, - device=device) - if runner.world_size > 1: - dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) - return mem_mb.item() - - def _log_info(self, log_dict, runner): - # print exp name for users to distinguish experiments - # at every ``interval_exp_name`` iterations and the end of each epoch - if runner.meta is not None and 'exp_name' in runner.meta: - if (self.every_n_iters(runner, self.interval_exp_name)) or ( - self.by_epoch and self.end_of_epoch(runner)): - exp_info = f'Exp name: {runner.meta["exp_name"]}' - runner.logger.info(exp_info) - - if log_dict['mode'] == 'train': - if isinstance(log_dict['lr'], dict): - lr_str = [] - for k, val in log_dict['lr'].items(): - lr_str.append(f'lr_{k}: {val:.3e}') - lr_str = ' '.join(lr_str) - else: - lr_str = f'lr: {log_dict["lr"]:.3e}' - - # by epoch: Epoch [4][100/1000] - # by iter: Iter [100/100000] - if self.by_epoch: - log_str = f'Epoch [{log_dict["epoch"]}]' \ - f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' - else: - log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' - log_str += f'{lr_str}, ' - - if 'time' in log_dict.keys(): - self.time_sec_tot += (log_dict['time'] * self.interval) - time_sec_avg = self.time_sec_tot / ( - runner.iter - self.start_iter + 1) - eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) - eta_str = str(datetime.timedelta(seconds=int(eta_sec))) - log_str += f'eta: {eta_str}, ' - log_str += f'time: {log_dict["time"]:.3f}, ' \ - f'data_time: {log_dict["data_time"]:.3f}, ' - # statistic memory - if torch.cuda.is_available(): - log_str += f'memory: {log_dict["memory"]}, ' - else: - # val/test time - # here 1000 is the length of the val dataloader - # by epoch: Epoch[val] [4][1000] - # by iter: Iter[val] [1000] - if self.by_epoch: - log_str = f'Epoch({log_dict["mode"]}) ' \ - f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' - else: - log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' - - log_items = [] - for name, val in log_dict.items(): - # TODO: resolve this hack - # these items have been in log_str - if name in [ - 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', - 'memory', 'epoch' - ]: - continue - if isinstance(val, float): - val = f'{val:.4f}' - log_items.append(f'{name}: {val}') - log_str += ', '.join(log_items) - - runner.logger.info(log_str) - - def _dump_log(self, log_dict, runner): - # dump log in json format - json_log = OrderedDict() - for k, v in log_dict.items(): - json_log[k] = self._round_float(v) - # only append log at last line - if runner.rank == 0: - with open(self.json_log_path, 'a+') as f: - mmcv.dump(json_log, f, file_format='json') - f.write('\n') - - def _round_float(self, items): - if isinstance(items, list): - return [self._round_float(item) for item in items] - elif isinstance(items, float): - return round(items, 5) - else: - return items - - def log(self, runner): - if 'eval_iter_num' in runner.log_buffer.output: - # this doesn't modify runner.iter and is regardless of by_epoch - cur_iter = runner.log_buffer.output.pop('eval_iter_num') - else: - cur_iter = self.get_iter(runner, inner_iter=True) - - log_dict = OrderedDict( - mode=self.get_mode(runner), - epoch=self.get_epoch(runner), - iter=cur_iter) - - # only record lr of the first param group - cur_lr = runner.current_lr() - if isinstance(cur_lr, list): - log_dict['lr'] = cur_lr[0] - else: - assert isinstance(cur_lr, dict) - log_dict['lr'] = {} - for k, lr_ in cur_lr.items(): - assert isinstance(lr_, list) - log_dict['lr'].update({k: lr_[0]}) - - if 'time' in runner.log_buffer.output: - # statistic memory - if torch.cuda.is_available(): - log_dict['memory'] = self._get_max_memory(runner) - - log_dict = dict(log_dict, **runner.log_buffer.output) - - self._log_info(log_dict, runner) - self._dump_log(log_dict, runner) - return log_dict - - def after_run(self, runner): - # copy or upload logs to self.out_dir - if self.out_dir is not None: - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - out_filepath = self.file_client.join_path( - self.out_dir, filename) - with open(local_filepath, 'r') as f: - self.file_client.put_text(f.read(), out_filepath) - - runner.logger.info( - (f'The file {local_filepath} has been uploaded to ' - f'{out_filepath}.')) - - if not self.keep_local: - os.remove(local_filepath) - runner.logger.info( - (f'{local_filepath} was removed due to the ' - '`self.keep_local=False`')) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/wandb.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/wandb.py deleted file mode 100755 index 9f6808462..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/logger/wandb.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class WandbLoggerHook(LoggerHook): - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - commit=True, - by_epoch=True, - with_step=True): - super(WandbLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_wandb() - self.init_kwargs = init_kwargs - self.commit = commit - self.with_step = with_step - - def import_wandb(self): - try: - import wandb - except ImportError: - raise ImportError( - 'Please run "pip install wandb" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(WandbLoggerHook, self).before_run(runner) - if self.wandb is None: - self.import_wandb() - if self.init_kwargs: - self.wandb.init(**self.init_kwargs) - else: - self.wandb.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - if self.with_step: - self.wandb.log( - tags, step=self.get_iter(runner), commit=self.commit) - else: - tags['global_step'] = self.get_iter(runner) - self.wandb.log(tags, commit=self.commit) - - @master_only - def after_run(self, runner): - self.wandb.join() diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/lr_updater.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/lr_updater.py deleted file mode 100755 index e5a124157..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/lr_updater.py +++ /dev/null @@ -1,670 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from math import cos, pi - -import mmcv -from .hook import HOOKS, Hook - - -class LrUpdaterHook(Hook): - """LR Scheduler in MMCV. - - Args: - by_epoch (bool): LR changes epoch by epoch - warmup (string): Type of warmup used. It can be None(use no warmup), - 'constant', 'linear' or 'exp' - warmup_iters (int): The number of iterations or epochs that warmup - lasts - warmup_ratio (float): LR used at the beginning of warmup equals to - warmup_ratio * initial_lr - warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters - means the number of epochs that warmup lasts, otherwise means the - number of iteration that warmup lasts - """ - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.1, - warmup_by_epoch=False): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_ratio" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - self.warmup_by_epoch = warmup_by_epoch - - if self.warmup_by_epoch: - self.warmup_epochs = self.warmup_iters - self.warmup_iters = None - else: - self.warmup_epochs = None - - self.base_lr = [] # initial lr for all param groups - self.regular_lr = [] # expected lr if no warming up is performed - - def _set_lr(self, runner, lr_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, lr in zip(optim.param_groups, lr_groups[k]): - param_group['lr'] = lr - else: - for param_group, lr in zip(runner.optimizer.param_groups, - lr_groups): - param_group['lr'] = lr - - def get_lr(self, runner, base_lr): - raise NotImplementedError - - def get_regular_lr(self, runner): - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(runner, _base_lr) - for _base_lr in self.base_lr[k] - ] - lr_groups.update({k: _lr_group}) - - return lr_groups - else: - return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - if self.warmup == 'constant': - warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_lr = [_lr * (1 - k) for _lr in regular_lr] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.regular_lr, dict): - lr_groups = {} - for key, regular_lr in self.regular_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.regular_lr) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - group.setdefault('initial_lr', group['lr']) - _base_lr = [ - group['initial_lr'] for group in optim.param_groups - ] - self.base_lr.update({k: _base_lr}) - else: - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['initial_lr'] for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if self.warmup_iters is None: - epoch_len = len(runner.data_loader) - self.warmup_iters = self.warmup_epochs * epoch_len - - if not self.by_epoch: - return - - self.regular_lr = self.get_regular_lr(runner) - self._set_lr(runner, self.regular_lr) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_lr = self.get_regular_lr(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - - -@HOOKS.register_module() -class FixedLrUpdaterHook(LrUpdaterHook): - - def __init__(self, **kwargs): - super(FixedLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - return base_lr - - -@HOOKS.register_module() -class StepLrUpdaterHook(LrUpdaterHook): - """Step LR scheduler with min_lr clipping. - - Args: - step (int | list[int]): Step to decay the LR. If an int value is given, - regard it as the decay interval. If a list is given, decay LR at - these steps. - gamma (float, optional): Decay LR ratio. Default: 0.1. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. If None - is given, we don't perform lr clipping. Default: None. - """ - - def __init__(self, step, gamma=0.1, min_lr=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_lr = min_lr - super(StepLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - lr = base_lr * (self.gamma**exp) - if self.min_lr is not None: - # clip to a minimum value - lr = max(lr, self.min_lr) - return lr - - -@HOOKS.register_module() -class ExpLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, **kwargs): - self.gamma = gamma - super(ExpLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * self.gamma**progress - - -@HOOKS.register_module() -class PolyLrUpdaterHook(LrUpdaterHook): - - def __init__(self, power=1., min_lr=0., **kwargs): - self.power = power - self.min_lr = min_lr - super(PolyLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - coeff = (1 - progress / max_progress)**self.power - return (base_lr - self.min_lr) * coeff + self.min_lr - - -@HOOKS.register_module() -class InvLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, power=1., **kwargs): - self.gamma = gamma - self.power = power - super(InvLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * (1 + self.gamma * progress)**(-self.power) - - -@HOOKS.register_module() -class CosineAnnealingLrUpdaterHook(LrUpdaterHook): - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(CosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): - """Flat + Cosine lr schedule. - - Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 - - Args: - start_percent (float): When to start annealing the learning rate - after the percentage of the total training steps. - The value should be in range [0, 1). - Default: 0.75 - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - start_percent=0.75, - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - if start_percent < 0 or start_percent > 1 or not isinstance( - start_percent, float): - raise ValueError( - 'expected float between 0 and 1 start_percent, but ' - f'got {start_percent}') - self.start_percent = start_percent - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(FlatCosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - start = round(runner.max_epochs * self.start_percent) - progress = runner.epoch - start - max_progress = runner.max_epochs - start - else: - start = round(runner.max_iters * self.start_percent) - progress = runner.iter - start - max_progress = runner.max_iters - start - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress < 0: - return base_lr - else: - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class CosineRestartLrUpdaterHook(LrUpdaterHook): - """Cosine annealing with restarts learning rate scheme. - - Args: - periods (list[int]): Periods for each cosine anneling cycle. - restart_weights (list[float], optional): Restart weights at each - restart iteration. Default: [1]. - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - periods, - restart_weights=[1], - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.periods = periods - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - self.restart_weights = restart_weights - assert (len(self.periods) == len(self.restart_weights) - ), 'periods and restart_weights should have the same length.' - super(CosineRestartLrUpdaterHook, self).__init__(**kwargs) - - self.cumulative_periods = [ - sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) - ] - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - else: - progress = runner.iter - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - idx = get_position_from_periods(progress, self.cumulative_periods) - current_weight = self.restart_weights[idx] - nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] - current_periods = self.periods[idx] - - alpha = min((progress - nearest_restart) / current_periods, 1) - return annealing_cos(base_lr, target_lr, alpha, current_weight) - - -def get_position_from_periods(iteration, cumulative_periods): - """Get the position from a period list. - - It will return the index of the right-closest number in the period list. - For example, the cumulative_periods = [100, 200, 300, 400], - if iteration == 50, return 0; - if iteration == 210, return 2; - if iteration == 300, return 3. - - Args: - iteration (int): Current iteration. - cumulative_periods (list[int]): Cumulative period list. - - Returns: - int: The position of the right-closest number in the period list. - """ - for i, period in enumerate(cumulative_periods): - if iteration < period: - return i - raise ValueError(f'Current iteration {iteration} exceeds ' - f'cumulative_periods {cumulative_periods}') - - -@HOOKS.register_module() -class CyclicLrUpdaterHook(LrUpdaterHook): - """Cyclic LR Scheduler. - - Implement the cyclical learning rate policy (CLR) described in - https://arxiv.org/pdf/1506.01186.pdf - - Different from the original paper, we use cosine annealing rather than - triangular policy inside a cycle. This improves the performance in the - 3D detection area. - - Args: - by_epoch (bool): Whether to update LR by epoch. - target_ratio (tuple[float]): Relative ratio of the highest LR and the - lowest LR to the initial LR. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of LR in - the total cycle. - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - """ - - def __init__(self, - by_epoch=False, - target_ratio=(10, 1e-4), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.lr_phases = [] # init lr_phases - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicLrUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicLrUpdaterHook, self).before_run(runner) - # initiate lr_phases - # total lr_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.lr_phases.append( - [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) - self.lr_phases.append([ - iter_up_phase, max_iter_per_phase, max_iter_per_phase, - self.target_ratio[0], self.target_ratio[1] - ]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - for (start_iter, end_iter, max_iter_per_phase, start_ratio, - end_ratio) in self.lr_phases: - curr_iter %= max_iter_per_phase - if start_iter <= curr_iter < end_iter: - progress = curr_iter - start_iter - return self.anneal_func(base_lr * start_ratio, - base_lr * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleLrUpdaterHook(LrUpdaterHook): - """One Cycle LR Scheduler. - - The 1cycle learning rate policy changes the learning rate after every - batch. The one cycle learning rate policy is described in - https://arxiv.org/pdf/1708.07120.pdf - - Args: - max_lr (float or list): Upper learning rate boundaries in the cycle - for each parameter group. - total_steps (int, optional): The total number of steps in the cycle. - Note that if a value is not provided here, it will be the max_iter - of runner. Default: None. - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - div_factor (float): Determines the initial learning rate via - initial_lr = max_lr/div_factor - Default: 25 - final_div_factor (float): Determines the minimum learning rate via - min_lr = initial_lr/final_div_factor - Default: 1e4 - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - max_lr, - total_steps=None, - pct_start=0.3, - anneal_strategy='cos', - div_factor=25, - final_div_factor=1e4, - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch = False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(max_lr, (numbers.Number, list, dict)): - raise ValueError('the type of max_lr must be the one of list or ' - f'dict, but got {type(max_lr)}') - self._max_lr = max_lr - if total_steps is not None: - if not isinstance(total_steps, int): - raise ValueError('the type of total_steps must be int, but' - f'got {type(total_steps)}') - self.total_steps = total_steps - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.div_factor = div_factor - self.final_div_factor = final_div_factor - self.three_phase = three_phase - self.lr_phases = [] # init lr_phases - super(OneCycleLrUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if hasattr(self, 'total_steps'): - total_steps = self.total_steps - else: - total_steps = runner.max_iters - if total_steps < runner.max_iters: - raise ValueError( - 'The total steps must be greater than or equal to max ' - f'iterations {runner.max_iters} of runner, but total steps ' - f'is {total_steps}.') - - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - _max_lr = format_param(k, optim, self._max_lr) - self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(optim.param_groups, self.base_lr[k]): - group.setdefault('initial_lr', lr) - else: - k = type(runner.optimizer).__name__ - _max_lr = format_param(k, runner.optimizer, self._max_lr) - self.base_lr = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(runner.optimizer.param_groups, self.base_lr): - group.setdefault('initial_lr', lr) - - if self.three_phase: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append([ - float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 - ]) - self.lr_phases.append( - [total_steps - 1, 1, 1 / self.final_div_factor]) - else: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append( - [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - start_iter = 0 - for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): - if curr_iter <= end_iter: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, - pct) - break - start_iter = end_iter - return lr - - -def annealing_cos(start, end, factor, weight=1): - """Calculate annealing cos learning rate. - - Cosine anneal from `weight * start + (1 - weight) * end` to `end` as - percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the cosine annealing. - end (float): The ending learing rate of the cosine annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - weight (float, optional): The combination factor of `start` and `end` - when calculating the actual starting learning rate. Default to 1. - """ - cos_out = cos(pi * factor) + 1 - return end + 0.5 * weight * (start - end) * cos_out - - -def annealing_linear(start, end, factor): - """Calculate annealing linear learning rate. - - Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the linear annealing. - end (float): The ending learing rate of the linear annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - """ - return start + (end - start) * factor - - -def format_param(name, optim, param): - if isinstance(param, numbers.Number): - return [param] * len(optim.param_groups) - elif isinstance(param, (list, tuple)): # multi param groups - if len(param) != len(optim.param_groups): - raise ValueError(f'expected {len(optim.param_groups)} ' - f'values for {name}, got {len(param)}') - return param - else: # multi optimizers - if name not in param: - raise KeyError(f'{name} is not found in {param.keys()}') - return param[name] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/memory.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/memory.py deleted file mode 100755 index 70cf9a838..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class EmptyCacheHook(Hook): - - def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): - self._before_epoch = before_epoch - self._after_epoch = after_epoch - self._after_iter = after_iter - - def after_iter(self, runner): - if self._after_iter: - torch.cuda.empty_cache() - - def before_epoch(self, runner): - if self._before_epoch: - torch.cuda.empty_cache() - - def after_epoch(self, runner): - if self._after_epoch: - torch.cuda.empty_cache() diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/momentum_updater.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/momentum_updater.py deleted file mode 100755 index 13d0e2fab..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/momentum_updater.py +++ /dev/null @@ -1,493 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -from .hook import HOOKS, Hook -from .lr_updater import annealing_cos, annealing_linear, format_param - - -class MomentumUpdaterHook(Hook): - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.9): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_momentum" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - - self.base_momentum = [] # initial momentum for all param groups - self.regular_momentum = [ - ] # expected momentum if no warming up is performed - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, base_momentum): - raise NotImplementedError - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k in runner.optimizer.keys(): - _momentum_group = [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum[k] - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - return [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum - ] - - def get_warmup_momentum(self, cur_iters): - - def _get_warmup_momentum(cur_iters, regular_momentum): - if self.warmup == 'constant': - warmup_momentum = [ - _momentum / self.warmup_ratio - for _momentum in self.regular_momentum - ] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_momentum = [ - _momentum / (1 - k) for _momentum in self.regular_mom - ] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_momentum = [ - _momentum / k for _momentum in self.regular_mom - ] - return warmup_momentum - - if isinstance(self.regular_momentum, dict): - momentum_groups = {} - for key, regular_momentum in self.regular_momentum.items(): - momentum_groups[key] = _get_warmup_momentum( - cur_iters, regular_momentum) - return momentum_groups - else: - return _get_warmup_momentum(cur_iters, self.regular_momentum) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, - # if 'initial_momentum' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_momentum = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - _base_momentum = [ - group['initial_momentum'] for group in optim.param_groups - ] - self.base_momentum.update({k: _base_momentum}) - else: - for group in runner.optimizer.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - self.base_momentum = [ - group['initial_momentum'] - for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if not self.by_epoch: - return - self.regular_mom = self.get_regular_momentum(runner) - self._set_momentum(runner, self.regular_mom) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_mom = self.get_regular_momentum(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_momentum(runner, self.regular_mom) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_momentum(runner, self.regular_mom) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - - -@HOOKS.register_module() -class StepMomentumUpdaterHook(MomentumUpdaterHook): - """Step momentum scheduler with min value clipping. - - Args: - step (int | list[int]): Step to decay the momentum. If an int value is - given, regard it as the decay interval. If a list is given, decay - momentum at these steps. - gamma (float, optional): Decay momentum ratio. Default: 0.5. - min_momentum (float, optional): Minimum momentum value to keep. If - momentum after decay is lower than this value, it will be clipped - accordingly. If None is given, we don't perform lr clipping. - Default: None. - """ - - def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_momentum = min_momentum - super(StepMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - momentum = base_momentum * (self.gamma**exp) - if self.min_momentum is not None: - # clip to a minimum value - momentum = max(momentum, self.min_momentum) - return momentum - - -@HOOKS.register_module() -class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(CosineAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_cos(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class CyclicMomentumUpdaterHook(MomentumUpdaterHook): - """Cyclic momentum Scheduler. - - Implement the cyclical momentum scheduler policy described in - https://arxiv.org/pdf/1708.07120.pdf - - This momentum scheduler usually used together with the CyclicLRUpdater - to improve the performance in the 3D detection area. - - Attributes: - target_ratio (tuple[float]): Relative ratio of the lowest momentum and - the highest momentum to the initial momentum. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of momentum - in the total cycle. - by_epoch (bool): Whether to update momentum by epoch. - """ - - def __init__(self, - by_epoch=False, - target_ratio=(0.85 / 0.95, 1), - cyclic_times=1, - step_ratio_up=0.4, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.momentum_phases = [] # init momentum_phases - # currently only support by_epoch=False - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicMomentumUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicMomentumUpdaterHook, self).before_run(runner) - # initiate momentum_phases - # total momentum_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.momentum_phases.append( - [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) - self.momentum_phases.append([ - iter_up_phase, max_iter_per_phase, max_iter_per_phase, - self.target_ratio[0], self.target_ratio[1] - ]) - - def get_momentum(self, runner, base_momentum): - curr_iter = runner.iter - for (start_iter, end_iter, max_iter_per_phase, start_ratio, - end_ratio) in self.momentum_phases: - curr_iter %= max_iter_per_phase - if start_iter <= curr_iter < end_iter: - progress = curr_iter - start_iter - return annealing_cos(base_momentum * start_ratio, - base_momentum * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): - """OneCycle momentum Scheduler. - - This momentum scheduler usually used together with the OneCycleLrUpdater - to improve the performance. - - Args: - base_momentum (float or list): Lower momentum boundaries in the cycle - for each parameter group. Note that momentum is cycled inversely - to learning rate; at the peak of a cycle, momentum is - 'base_momentum' and learning rate is 'max_lr'. - Default: 0.85 - max_momentum (float or list): Upper momentum boundaries in the cycle - for each parameter group. Functionally, - it defines the cycle amplitude (max_momentum - base_momentum). - Note that momentum is cycled inversely - to learning rate; at the start of a cycle, momentum is - 'max_momentum' and learning rate is 'base_lr' - Default: 0.95 - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - base_momentum=0.85, - max_momentum=0.95, - pct_start=0.3, - anneal_strategy='cos', - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch=False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(base_momentum, (float, list, dict)): - raise ValueError('base_momentum must be the type among of float,' - 'list or dict.') - self._base_momentum = base_momentum - if not isinstance(max_momentum, (float, list, dict)): - raise ValueError('max_momentum must be the type among of float,' - 'list or dict.') - self._max_momentum = max_momentum - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('Expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must by one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.three_phase = three_phase - self.momentum_phases = [] # init momentum_phases - super(OneCycleMomentumUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip( - optim.param_groups, _base_momentum, _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - else: - optim = runner.optimizer - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - k = type(optim).__name__ - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip(optim.param_groups, - _base_momentum, - _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - - if self.three_phase: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': - float(2 * self.pct_start * runner.max_iters) - 2, - 'start_momentum': - 'base_momentum', - 'end_momentum': - 'max_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'max_momentum', - 'end_momentum': 'max_momentum' - }) - else: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'base_momentum', - 'end_momentum': 'max_momentum' - }) - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, param_group): - curr_iter = runner.iter - start_iter = 0 - for i, phase in enumerate(self.momentum_phases): - end_iter = phase['end_iter'] - if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - momentum = self.anneal_func( - param_group[phase['start_momentum']], - param_group[phase['end_momentum']], pct) - break - start_iter = end_iter - return momentum - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k, optim in runner.optimizer.items(): - _momentum_group = [ - self.get_momentum(runner, param_group) - for param_group in optim.param_groups - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - momentum_groups = [] - for param_group in runner.optimizer.param_groups: - momentum_groups.append(self.get_momentum(runner, param_group)) - return momentum_groups diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/optimizer.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/optimizer.py deleted file mode 100755 index f575ceda0..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/optimizer.py +++ /dev/null @@ -1,508 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -from collections import defaultdict -from itertools import chain - -from torch.nn.utils import clip_grad - -from mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version -from ..dist_utils import allreduce_grads -from ..fp16_utils import LossScaler, wrap_fp16_model -from .hook import HOOKS, Hook - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - from torch.cuda.amp import GradScaler -except ImportError: - pass - - -@HOOKS.register_module() -class OptimizerHook(Hook): - - def __init__(self, grad_clip=None): - self.grad_clip = grad_clip - - def clip_grads(self, params): - params = list( - filter(lambda p: p.requires_grad and p.grad is not None, params)) - if len(params) > 0: - return clip_grad.clip_grad_norm_(params, **self.grad_clip) - - def after_train_iter(self, runner): - runner.optimizer.zero_grad() - runner.outputs['loss'].backward() - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - - -@HOOKS.register_module() -class GradientCumulativeOptimizerHook(OptimizerHook): - """Optimizer Hook implements multi-iters gradient cumulating. - - Args: - cumulative_iters (int, optional): Num of gradient cumulative iters. - The optimizer will step every `cumulative_iters` iters. - Defaults to 1. - - Examples: - >>> # Use cumulative_iters to simulate a large batch size - >>> # It is helpful when the hardware cannot handle a large batch size. - >>> loader = DataLoader(data, batch_size=64) - >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) - >>> # almost equals to - >>> loader = DataLoader(data, batch_size=256) - >>> optim_hook = OptimizerHook() - """ - - def __init__(self, cumulative_iters=1, **kwargs): - super(GradientCumulativeOptimizerHook, self).__init__(**kwargs) - - assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ - f'cumulative_iters only accepts positive int, but got ' \ - f'{type(cumulative_iters)} instead.' - - self.cumulative_iters = cumulative_iters - self.divisible_iters = 0 - self.remainder_iters = 0 - self.initialized = False - - def has_batch_norm(self, module): - if isinstance(module, _BatchNorm): - return True - for m in module.children(): - if self.has_batch_norm(m): - return True - return False - - def _init(self, runner): - if runner.iter % self.cumulative_iters != 0: - runner.logger.warning( - 'Resume iter number is not divisible by cumulative_iters in ' - 'GradientCumulativeOptimizerHook, which means the gradient of ' - 'some iters is lost and the result may be influenced slightly.' - ) - - if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: - runner.logger.warning( - 'GradientCumulativeOptimizerHook may slightly decrease ' - 'performance if the model has BatchNorm layers.') - - residual_iters = runner.max_iters - runner.iter - - self.divisible_iters = ( - residual_iters // self.cumulative_iters * self.cumulative_iters) - self.remainder_iters = residual_iters - self.divisible_iters - - self.initialized = True - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - runner.optimizer.zero_grad() - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - self.loss_scaler = GradScaler() - elif isinstance(loss_scale, float): - self._scale_update_param = loss_scale - self.loss_scaler = GradScaler(init_scale=loss_scale) - elif isinstance(loss_scale, dict): - self.loss_scaler = GradScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training.""" - # wrap model mode to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer to - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients. - 3. Unscale the optimizer’s gradient tensors. - 4. Call optimizer.step() and update scale factor. - 5. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - - self.loss_scaler.scale(runner.outputs['loss']).backward() - self.loss_scaler.unscale_(runner.optimizer) - # grad clip - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using PyTorch's implementation) implements - multi-iters gradient cumulating. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - """ - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - - self.loss_scaler.scale(loss).backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - self.loss_scaler.unscale_(runner.optimizer) - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() - -else: - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (mmcv's implementation). - - The steps of fp16 optimizer is as follows. - 1. Scale the loss value. - 2. BP in the fp16 model. - 2. Copy gradients from fp16 model to fp32 weights. - 3. Update fp32 weights. - 4. Copy updated parameters from fp32 weights to fp16 model. - - Refer to https://arxiv.org/abs/1710.03740 for more details. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of LossScaler. - Defaults to 512. - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - if loss_scale == 'dynamic': - self.loss_scaler = LossScaler(mode='dynamic') - elif isinstance(loss_scale, float): - self.loss_scaler = LossScaler( - init_scale=loss_scale, mode='static') - elif isinstance(loss_scale, dict): - self.loss_scaler = LossScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training. - - 1. Make a master copy of fp32 weights for optimization. - 2. Convert the main model from fp32 to fp16. - """ - # keep a copy of fp32 weights - old_groups = runner.optimizer.param_groups - runner.optimizer.param_groups = copy.deepcopy( - runner.optimizer.param_groups) - state = defaultdict(dict) - p_map = { - old_p: p - for old_p, p in zip( - chain(*(g['params'] for g in old_groups)), - chain(*(g['params'] - for g in runner.optimizer.param_groups))) - } - for k, v in runner.optimizer.state.items(): - state[p_map[k]] = v - runner.optimizer.state = state - # convert model to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer `loss_scalar.py` - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients (fp16). - 3. Copy gradients from the model to the fp32 weight copy. - 4. Scale the gradients back and update the fp32 weight copy. - 5. Copy back the params from fp32 weight copy to the fp16 model. - 6. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - # scale the loss value - scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale - scaled_loss.backward() - # copy fp16 grads in the model to fp32 params in the optimizer - - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - self.loss_scaler.update_scale(has_overflow) - if has_overflow: - runner.logger.warning('Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using mmcv implementation) implements multi- - iters gradient cumulating.""" - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - - loss = runner.outputs['loss'] - loss = loss / loss_factor - - # scale the loss value - scaled_loss = loss * self.loss_scaler.loss_scale - scaled_loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - else: - runner.logger.warning( - 'Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - self.loss_scaler.update_scale(has_overflow) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/profiler.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/profiler.py deleted file mode 100755 index b70236997..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/profiler.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Callable, List, Optional, Union - -import torch - -from ..dist_utils import master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ProfilerHook(Hook): - """Profiler to analyze performance during training. - - PyTorch Profiler is a tool that allows the collection of the performance - metrics during the training. More details on Profiler can be found at - https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile - - Args: - by_epoch (bool): Profile performance by epoch or by iteration. - Default: True. - profile_iters (int): Number of iterations for profiling. - If ``by_epoch=True``, profile_iters indicates that they are the - first profile_iters epochs at the beginning of the - training, otherwise it indicates the first profile_iters - iterations. Default: 1. - activities (list[str]): List of activity groups (CPU, CUDA) to use in - profiling. Default: ['cpu', 'cuda']. - schedule (dict, optional): Config of generating the callable schedule. - if schedule is None, profiler will not add step markers into the - trace and table view. Default: None. - on_trace_ready (callable, dict): Either a handler or a dict of generate - handler. Default: None. - record_shapes (bool): Save information about operator's input shapes. - Default: False. - profile_memory (bool): Track tensor memory allocation/deallocation. - Default: False. - with_stack (bool): Record source information (file and line number) - for the ops. Default: False. - with_flops (bool): Use formula to estimate the FLOPS of specific - operators (matrix multiplication and 2D convolution). - Default: False. - json_trace_path (str, optional): Exports the collected trace in Chrome - JSON format. Default: None. - - Example: - >>> runner = ... # instantiate a Runner - >>> # tensorboard trace - >>> trace_config = dict(type='tb_trace', dir_name='work_dir') - >>> profiler_config = dict(on_trace_ready=trace_config) - >>> runner.register_profiler_hook(profiler_config) - >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) - """ - - def __init__(self, - by_epoch: bool = True, - profile_iters: int = 1, - activities: List[str] = ['cpu', 'cuda'], - schedule: Optional[dict] = None, - on_trace_ready: Optional[Union[Callable, dict]] = None, - record_shapes: bool = False, - profile_memory: bool = False, - with_stack: bool = False, - with_flops: bool = False, - json_trace_path: Optional[str] = None) -> None: - try: - from torch import profiler # torch version >= 1.8.1 - except ImportError: - raise ImportError('profiler is the new feature of torch1.8.1, ' - f'but your version is {torch.__version__}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' - self.by_epoch = by_epoch - - if profile_iters < 1: - raise ValueError('profile_iters should be greater than 0, but got ' - f'{profile_iters}') - self.profile_iters = profile_iters - - if not isinstance(activities, list): - raise ValueError( - f'activities should be list, but got {type(activities)}') - self.activities = [] - for activity in activities: - activity = activity.lower() - if activity == 'cpu': - self.activities.append(profiler.ProfilerActivity.CPU) - elif activity == 'cuda': - self.activities.append(profiler.ProfilerActivity.CUDA) - else: - raise ValueError( - f'activity should be "cpu" or "cuda", but got {activity}') - - if schedule is not None: - self.schedule = profiler.schedule(**schedule) - else: - self.schedule = None - - self.on_trace_ready = on_trace_ready - self.record_shapes = record_shapes - self.profile_memory = profile_memory - self.with_stack = with_stack - self.with_flops = with_flops - self.json_trace_path = json_trace_path - - @master_only - def before_run(self, runner): - if self.by_epoch and runner.max_epochs < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_epochs}') - - if not self.by_epoch and runner.max_iters < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_iters}') - - if callable(self.on_trace_ready): # handler - _on_trace_ready = self.on_trace_ready - elif isinstance(self.on_trace_ready, dict): # config of handler - trace_cfg = self.on_trace_ready.copy() - trace_type = trace_cfg.pop('type') # log_trace handler - if trace_type == 'log_trace': - - def _log_handler(prof): - print(prof.key_averages().table(**trace_cfg)) - - _on_trace_ready = _log_handler - elif trace_type == 'tb_trace': # tensorboard_trace handler - try: - import torch_tb_profiler # noqa: F401 - except ImportError: - raise ImportError('please run "pip install ' - 'torch-tb-profiler" to install ' - 'torch_tb_profiler') - _on_trace_ready = torch.profiler.tensorboard_trace_handler( - **trace_cfg) - else: - raise ValueError('trace_type should be "log_trace" or ' - f'"tb_trace", but got {trace_type}') - elif self.on_trace_ready is None: - _on_trace_ready = None # type: ignore - else: - raise ValueError('on_trace_ready should be handler, dict or None, ' - f'but got {type(self.on_trace_ready)}') - - if runner.max_epochs > 1: - warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' - 'instead of 1 epoch. Since profiler will slow down ' - 'the training, it is recommended to train 1 epoch ' - 'with ProfilerHook and adjust your setting according' - ' to the profiler summary. During normal training ' - '(epoch > 1), you may disable the ProfilerHook.') - - self.profiler = torch.profiler.profile( - activities=self.activities, - schedule=self.schedule, - on_trace_ready=_on_trace_ready, - record_shapes=self.record_shapes, - profile_memory=self.profile_memory, - with_stack=self.with_stack, - with_flops=self.with_flops) - - self.profiler.__enter__() - runner.logger.info('profiler is profiling...') - - @master_only - def after_train_epoch(self, runner): - if self.by_epoch and runner.epoch == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) - - @master_only - def after_train_iter(self, runner): - self.profiler.step() - if not self.by_epoch and runner.iter == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/sampler_seed.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/sampler_seed.py deleted file mode 100755 index ee0dc6bdd..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/sampler_seed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class DistSamplerSeedHook(Hook): - """Data-loading sampler for distributed training. - - When distributed training, it is only useful in conjunction with - :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same - purpose with :obj:`IterLoader`. - """ - - def before_epoch(self, runner): - if hasattr(runner.data_loader.sampler, 'set_epoch'): - # in case the data loader uses `SequentialSampler` in Pytorch - runner.data_loader.sampler.set_epoch(runner.epoch) - elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): - # batch sampler in pytorch warps the sampler as its attributes. - runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/sync_buffer.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/sync_buffer.py deleted file mode 100755 index 6376b7ff8..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/hooks/sync_buffer.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..dist_utils import allreduce_params -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class SyncBuffersHook(Hook): - """Synchronize model buffers such as running_mean and running_var in BN at - the end of each epoch. - - Args: - distributed (bool): Whether distributed training is used. It is - effective only for distributed training. Defaults to True. - """ - - def __init__(self, distributed=True): - self.distributed = distributed - - def after_epoch(self, runner): - """All-reduce model buffers at the end of each epoch.""" - if self.distributed: - allreduce_params(runner.model.buffers()) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/iter_based_runner.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/iter_based_runner.py deleted file mode 100755 index 9892b07a4..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/iter_based_runner.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch -from torch.optim import Optimizer - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .hooks import IterTimerHook -from .utils import get_host_info - - -class IterLoader: - - def __init__(self, dataloader): - self._dataloader = dataloader - self.iter_loader = iter(self._dataloader) - self._epoch = 0 - - @property - def epoch(self): - return self._epoch - - def __next__(self): - try: - data = next(self.iter_loader) - except StopIteration: - self._epoch += 1 - if hasattr(self._dataloader.sampler, 'set_epoch'): - self._dataloader.sampler.set_epoch(self._epoch) - time.sleep(2) # Prevent possible deadlock during epoch transition - self.iter_loader = iter(self._dataloader) - data = next(self.iter_loader) - - return data - - def __len__(self): - return len(self._dataloader) - - -@RUNNERS.register_module() -class IterBasedRunner(BaseRunner): - """Iteration-based Runner. - - This runner train models iteration by iteration. - """ - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._epoch = data_loader.epoch - data_batch = next(data_loader) - self.call_hook('before_train_iter') - outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.train_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_train_iter') - self._inner_iter += 1 - self._iter += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - data_batch = next(data_loader) - self.call_hook('before_val_iter') - outputs = self.model.val_step(data_batch, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.val_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_val_iter') - self._inner_iter += 1 - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, iters) to specify the - running order and iterations. E.g, [('train', 10000), - ('val', 1000)] means running 10000 iterations for training and - 1000 iterations for validation, iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - """Resume model from checkpoint. - - Args: - checkpoint (str): Checkpoint to resume from. - resume_optimizer (bool, optional): Whether resume the optimizer(s) - if the checkpoint file includes optimizer(s). Default to True. - map_location (str, optional): Same as :func:`torch.load`. - Default to 'default'. - """ - if map_location == 'default': - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - self._inner_iter = checkpoint['meta']['iter'] - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') - - def save_checkpoint(self, - out_dir, - filename_tmpl='iter_{}.pth', - meta=None, - save_optimizer=True, - create_symlink=True): - """Save checkpoint to file. - - Args: - out_dir (str): Directory to save checkpoint files. - filename_tmpl (str, optional): Checkpoint file template. - Defaults to 'iter_{}.pth'. - meta (dict, optional): Metadata to be saved in checkpoint. - Defaults to None. - save_optimizer (bool, optional): Whether save optimizer. - Defaults to True. - create_symlink (bool, optional): Whether create symlink to the - latest checkpoint file. Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.iter + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - custom_hooks_config=None): - """Register default hooks for iter-based training. - - Checkpoint hook, optimizer stepper hook and logger hooks will be set to - `by_epoch=False` by default. - - Default hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - if checkpoint_config is not None: - checkpoint_config.setdefault('by_epoch', False) - if lr_config is not None: - lr_config.setdefault('by_epoch', False) - if log_config is not None: - for info in log_config['hooks']: - info.setdefault('by_epoch', False) - super(IterBasedRunner, self).register_training_hooks( - lr_config=lr_config, - momentum_config=momentum_config, - optimizer_config=optimizer_config, - checkpoint_config=checkpoint_config, - log_config=log_config, - timer_config=IterTimerHook(), - custom_hooks_config=custom_hooks_config) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/log_buffer.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/log_buffer.py deleted file mode 100755 index d949e2941..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/log_buffer.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import numpy as np - - -class LogBuffer: - - def __init__(self): - self.val_history = OrderedDict() - self.n_history = OrderedDict() - self.output = OrderedDict() - self.ready = False - - def clear(self): - self.val_history.clear() - self.n_history.clear() - self.clear_output() - - def clear_output(self): - self.output.clear() - self.ready = False - - def update(self, vars, count=1): - assert isinstance(vars, dict) - for key, var in vars.items(): - if key not in self.val_history: - self.val_history[key] = [] - self.n_history[key] = [] - self.val_history[key].append(var) - self.n_history[key].append(count) - - def average(self, n=0): - """Average latest n values or all values.""" - assert n >= 0 - for key in self.val_history: - values = np.array(self.val_history[key][-n:]) - nums = np.array(self.n_history[key][-n:]) - avg = np.sum(values * nums) / np.sum(nums) - self.output[key] = avg - self.ready = True diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/optimizer/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/optimizer/__init__.py deleted file mode 100755 index 53c34d047..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/optimizer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, - build_optimizer_constructor) -from .default_constructor import DefaultOptimizerConstructor - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', - 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/optimizer/builder.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/optimizer/builder.py deleted file mode 100755 index f9234eed8..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/optimizer/builder.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect - -import torch - -from ...utils import Registry, build_from_cfg - -OPTIMIZERS = Registry('optimizer') -OPTIMIZER_BUILDERS = Registry('optimizer builder') - - -def register_torch_optimizers(): - torch_optimizers = [] - for module_name in dir(torch.optim): - if module_name.startswith('__'): - continue - _optim = getattr(torch.optim, module_name) - if inspect.isclass(_optim) and issubclass(_optim, - torch.optim.Optimizer): - OPTIMIZERS.register_module()(_optim) - torch_optimizers.append(module_name) - return torch_optimizers - - -TORCH_OPTIMIZERS = register_torch_optimizers() - - -def build_optimizer_constructor(cfg): - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/optimizer/default_constructor.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/optimizer/default_constructor.py deleted file mode 100755 index 64d06847b..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/optimizer/default_constructor.py +++ /dev/null @@ -1,249 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -from torch.nn import GroupNorm, LayerNorm - -from mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of -from mmcv.utils.ext_loader import check_ops_exist -from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS - - -@OPTIMIZER_BUILDERS.register_module() -class DefaultOptimizerConstructor: - """Default constructor for optimizers. - - By default each parameter share the same optimizer settings, and we - provide an argument ``paramwise_cfg`` to specify parameter-wise settings. - It is a dict and may contain the following fields: - - - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If - one of the keys in ``custom_keys`` is a substring of the name of one - parameter, then the setting of the parameter will be specified by - ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will - be ignored. It should be noted that the aforementioned ``key`` is the - longest key that is a substring of the name of the parameter. If there - are multiple matched keys with the same length, then the key with lower - alphabet order will be chosen. - ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` - and ``decay_mult``. See Example 2 below. - - ``bias_lr_mult`` (float): It will be multiplied to the learning - rate for all bias parameters (except for those in normalization - layers and offset layers of DCN). - - ``bias_decay_mult`` (float): It will be multiplied to the weight - decay for all bias parameters (except for those in - normalization layers, depthwise conv layers, offset layers of DCN). - - ``norm_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of normalization - layers. - - ``dwconv_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of depthwise conv - layers. - - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning - rate for parameters of offset layer in the deformable convs - of a model. - - ``bypass_duplicate`` (bool): If true, the duplicate parameters - would not be added into optimizer. Default: False. - - Note: - 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will - override the effect of ``bias_lr_mult`` in the bias of offset - layer. So be careful when using both ``bias_lr_mult`` and - ``dcn_offset_lr_mult``. If you wish to apply both of them to the - offset layer in deformable convs, set ``dcn_offset_lr_mult`` - to the original ``dcn_offset_lr_mult`` * ``bias_lr_mult``. - 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will - apply it to all the DCN layers in the model. So be careful when - the model contains multiple DCN layers in places other than - backbone. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - optimizer_cfg (dict): The config dict of the optimizer. - Positional fields are - - - `type`: class name of the optimizer. - - Optional fields are - - - any arguments of the corresponding optimizer type, e.g., - lr, weight_decay, momentum, etc. - paramwise_cfg (dict, optional): Parameter-wise options. - - Example 1: - >>> model = torch.nn.modules.Conv1d(1, 1, 1) - >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, - >>> weight_decay=0.0001) - >>> paramwise_cfg = dict(norm_decay_mult=0.) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - - Example 2: - >>> # assume model have attribute model.backbone and model.cls_head - >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) - >>> paramwise_cfg = dict(custom_keys={ - '.backbone': dict(lr_mult=0.1, decay_mult=0.9)}) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - >>> # Then the `lr` and `weight_decay` for model.backbone is - >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for - >>> # model.cls_head is (0.01, 0.95). - """ - - def __init__(self, optimizer_cfg, paramwise_cfg=None): - if not isinstance(optimizer_cfg, dict): - raise TypeError('optimizer_cfg should be a dict', - f'but got {type(optimizer_cfg)}') - self.optimizer_cfg = optimizer_cfg - self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg - self.base_lr = optimizer_cfg.get('lr', None) - self.base_wd = optimizer_cfg.get('weight_decay', None) - self._validate_cfg() - - def _validate_cfg(self): - if not isinstance(self.paramwise_cfg, dict): - raise TypeError('paramwise_cfg should be None or a dict, ' - f'but got {type(self.paramwise_cfg)}') - - if 'custom_keys' in self.paramwise_cfg: - if not isinstance(self.paramwise_cfg['custom_keys'], dict): - raise TypeError( - 'If specified, custom_keys must be a dict, ' - f'but got {type(self.paramwise_cfg["custom_keys"])}') - if self.base_wd is None: - for key in self.paramwise_cfg['custom_keys']: - if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: - raise ValueError('base_wd should not be None') - - # get base lr and weight decay - # weight_decay must be explicitly specified if mult is specified - if ('bias_decay_mult' in self.paramwise_cfg - or 'norm_decay_mult' in self.paramwise_cfg - or 'dwconv_decay_mult' in self.paramwise_cfg): - if self.base_wd is None: - raise ValueError('base_wd should not be None') - - def _is_in(self, param_group, param_group_list): - assert is_list_of(param_group_list, dict) - param = set(param_group['params']) - param_set = set() - for group in param_group_list: - param_set.update(set(group['params'])) - - return not param.isdisjoint(param_set) - - def add_params(self, params, module, prefix='', is_dcn_module=None): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - prefix (str): The prefix of the module - is_dcn_module (int|float|None): If the current module is a - submodule of DCN, `is_dcn_module` will be passed to - control conv_offset layer's learning rate. Defaults to None. - """ - # get param-wise options - custom_keys = self.paramwise_cfg.get('custom_keys', {}) - # first sort with alphabet order and then sort with reversed len of str - sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) - - bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) - bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) - norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) - dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) - bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) - dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) - - # special rules for norm layers and depth-wise conv layers - is_norm = isinstance(module, - (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) - is_dwconv = ( - isinstance(module, torch.nn.Conv2d) - and module.in_channels == module.groups) - - for name, param in module.named_parameters(recurse=False): - param_group = {'params': [param]} - if not param.requires_grad: - params.append(param_group) - continue - if bypass_duplicate and self._is_in(param_group, params): - warnings.warn(f'{prefix} is duplicate. It is skipped since ' - f'bypass_duplicate={bypass_duplicate}') - continue - # if the parameter match one of the custom keys, ignore other rules - is_custom = False - for key in sorted_keys: - if key in f'{prefix}.{name}': - is_custom = True - lr_mult = custom_keys[key].get('lr_mult', 1.) - param_group['lr'] = self.base_lr * lr_mult - if self.base_wd is not None: - decay_mult = custom_keys[key].get('decay_mult', 1.) - param_group['weight_decay'] = self.base_wd * decay_mult - break - - if not is_custom: - # bias_lr_mult affects all bias parameters - # except for norm.bias dcn.conv_offset.bias - if name == 'bias' and not (is_norm or is_dcn_module): - param_group['lr'] = self.base_lr * bias_lr_mult - - if (prefix.find('conv_offset') != -1 and is_dcn_module - and isinstance(module, torch.nn.Conv2d)): - # deal with both dcn_offset's bias & weight - param_group['lr'] = self.base_lr * dcn_offset_lr_mult - - # apply weight decay policies - if self.base_wd is not None: - # norm decay - if is_norm: - param_group[ - 'weight_decay'] = self.base_wd * norm_decay_mult - # depth-wise conv - elif is_dwconv: - param_group[ - 'weight_decay'] = self.base_wd * dwconv_decay_mult - # bias lr and decay - elif name == 'bias' and not is_dcn_module: - # TODO: current bias_decay_mult will have affect on DCN - param_group[ - 'weight_decay'] = self.base_wd * bias_decay_mult - params.append(param_group) - - if check_ops_exist(): - from mmcv.ops import ModulatedDeformConv2d - is_dcn_module = isinstance(module, - (ModulatedDeformConv2d)) - else: - is_dcn_module = False - for child_name, child_mod in module.named_children(): - child_prefix = f'{prefix}.{child_name}' if prefix else child_name - self.add_params( - params, - child_mod, - prefix=child_prefix, - is_dcn_module=is_dcn_module) - - def __call__(self, model): - if hasattr(model, 'module'): - model = model.module - - optimizer_cfg = self.optimizer_cfg.copy() - # if no paramwise option is specified, just use the global setting - if not self.paramwise_cfg: - optimizer_cfg['params'] = model.parameters() - return build_from_cfg(optimizer_cfg, OPTIMIZERS) - - # set param-wise lr and weight decay recursively - params = [] - self.add_params(params, model) - optimizer_cfg['params'] = params - - return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/priority.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/priority.py deleted file mode 100755 index 64cc4e3a0..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/priority.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from enum import Enum - - -class Priority(Enum): - """Hook priority levels. - - +--------------+------------+ - | Level | Value | - +==============+============+ - | HIGHEST | 0 | - +--------------+------------+ - | VERY_HIGH | 10 | - +--------------+------------+ - | HIGH | 30 | - +--------------+------------+ - | ABOVE_NORMAL | 40 | - +--------------+------------+ - | NORMAL | 50 | - +--------------+------------+ - | BELOW_NORMAL | 60 | - +--------------+------------+ - | LOW | 70 | - +--------------+------------+ - | VERY_LOW | 90 | - +--------------+------------+ - | LOWEST | 100 | - +--------------+------------+ - """ - - HIGHEST = 0 - VERY_HIGH = 10 - HIGH = 30 - ABOVE_NORMAL = 40 - NORMAL = 50 - BELOW_NORMAL = 60 - LOW = 70 - VERY_LOW = 90 - LOWEST = 100 - - -def get_priority(priority): - """Get priority value. - - Args: - priority (int or str or :obj:`Priority`): Priority. - - Returns: - int: The priority value. - """ - if isinstance(priority, int): - if priority < 0 or priority > 100: - raise ValueError('priority must be between 0 and 100') - return priority - elif isinstance(priority, Priority): - return priority.value - elif isinstance(priority, str): - return Priority[priority.upper()].value - else: - raise TypeError('priority must be an integer or Priority enum value') diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/utils.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/utils.py deleted file mode 100755 index 144d11e1a..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/runner/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random -import sys -import time -import warnings -from getpass import getuser -from socket import gethostname - -import numpy as np -import torch - -import mmcv - - -def get_host_info(): - """Get hostname and username. - - Return empty string if exception raised, e.g. ``getpass.getuser()`` will - lead to error in docker container - """ - host = '' - try: - host = f'{getuser()}@{gethostname()}' - except Exception as e: - warnings.warn(f'Host or user not found: {str(e)}') - finally: - return host - - -def get_time_str(): - return time.strftime('%Y%m%d_%H%M%S', time.localtime()) - - -def obj_from_dict(info, parent=None, default_args=None): - """Initialize an object from dict. - - The dict must contain the key "type", which indicates the object type, it - can be either a string or type, such as "list" or ``list``. Remaining - fields are treated as the arguments for constructing the object. - - Args: - info (dict): Object types and arguments. - parent (:class:`module`): Module which may containing expected object - classes. - default_args (dict, optional): Default arguments for initializing the - object. - - Returns: - any type: Object built from the dict. - """ - assert isinstance(info, dict) and 'type' in info - assert isinstance(default_args, dict) or default_args is None - args = info.copy() - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if parent is not None: - obj_type = getattr(parent, obj_type) - else: - obj_type = sys.modules[obj_type] - elif not isinstance(obj_type, type): - raise TypeError('type must be a str or valid type, but ' - f'got {type(obj_type)}') - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - return obj_type(**args) - - -def set_random_seed(seed, deterministic=False, use_rank_shift=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - rank_shift (bool): Whether to add rank number to the random seed to - have different random seed in different threads. Default: False. - """ - if use_rank_shift: - rank, _ = mmcv.runner.get_dist_info() - seed += rank - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/__init__.py deleted file mode 100755 index 378a00684..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/__init__.py +++ /dev/null @@ -1,69 +0,0 @@ -# flake8: noqa -# Copyright (c) OpenMMLab. All rights reserved. -from .config import Config, ConfigDict, DictAction -from .misc import (check_prerequisites, concat_list, deprecated_api_warning, - has_method, import_modules_from_strings, is_list_of, - is_method_overridden, is_seq_of, is_str, is_tuple_of, - iter_cast, list_cast, requires_executable, requires_package, - slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, - to_ntuple, tuple_cast) -from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, - scandir, symlink) -from .progressbar import (ProgressBar, track_iter_progress, - track_parallel_progress, track_progress) -from .testing import (assert_attrs_equal, assert_dict_contains_subset, - assert_dict_has_keys, assert_is_norm_layer, - assert_keys_equal, assert_params_all_zeros, - check_python_script) -from .timer import Timer, TimerError, check_time -from .version_utils import digit_version, get_git_hash - -try: - import torch -except ImportError: - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', - 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', - 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', - 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', - 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', - 'track_progress', 'track_iter_progress', 'track_parallel_progress', - 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', - 'digit_version', 'get_git_hash', 'import_modules_from_strings', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', - 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', - 'is_method_overridden', 'has_method' - ] -else: - from .env import collect_env - from .logging import get_logger, print_log - from .parrots_jit import jit, skip_no_elena - from .parrots_wrapper import ( - TORCH_VERSION, BuildExtension, CppExtension, CUDAExtension, DataLoader, - PoolDataLoader, SyncBatchNorm, _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, - _AvgPoolNd, _BatchNorm, _ConvNd, _ConvTransposeMixin, _InstanceNorm, - _MaxPoolNd, get_build_config, is_rocm_pytorch, _get_cuda_home) - from .registry import Registry, build_from_cfg - from .trace import is_jit_tracing - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', - 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', - 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', - 'check_prerequisites', 'requires_package', 'requires_executable', - 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', - 'symlink', 'scandir', 'ProgressBar', 'track_progress', - 'track_iter_progress', 'track_parallel_progress', 'Registry', - 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', - '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', - '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', - 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', - 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', - 'deprecated_api_warning', 'digit_version', 'get_git_hash', - 'import_modules_from_strings', 'jit', 'skip_no_elena', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', - 'assert_params_all_zeros', 'check_python_script', - 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', - '_get_cuda_home', 'has_method' - ] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/config.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/config.py deleted file mode 100755 index c71377c07..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/config.py +++ /dev/null @@ -1,688 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import ast -import copy -import os -import os.path as osp -import platform -import shutil -import sys -import tempfile -import uuid -import warnings -from argparse import Action, ArgumentParser -from collections import abc -from importlib import import_module - -from addict import Dict -from yapf.yapflib.yapf_api import FormatCode - -from .misc import import_modules_from_strings -from .path import check_file_exist - -if platform.system() == 'Windows': - import regex as re -else: - import re - -BASE_KEY = '_base_' -DELETE_KEY = '_delete_' -DEPRECATION_KEY = '_deprecation_' -RESERVED_KEYS = ['filename', 'text', 'pretty_text'] - - -class ConfigDict(Dict): - - def __missing__(self, name): - raise KeyError(name) - - def __getattr__(self, name): - try: - value = super(ConfigDict, self).__getattr__(name) - except KeyError: - ex = AttributeError(f"'{self.__class__.__name__}' object has no " - f"attribute '{name}'") - except Exception as e: - ex = e - else: - return value - raise ex - - -def add_args(parser, cfg, prefix=''): - for k, v in cfg.items(): - if isinstance(v, str): - parser.add_argument('--' + prefix + k) - elif isinstance(v, int): - parser.add_argument('--' + prefix + k, type=int) - elif isinstance(v, float): - parser.add_argument('--' + prefix + k, type=float) - elif isinstance(v, bool): - parser.add_argument('--' + prefix + k, action='store_true') - elif isinstance(v, dict): - add_args(parser, v, prefix + k + '.') - elif isinstance(v, abc.Iterable): - parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') - else: - print(f'cannot parse key {prefix + k} of type {type(v)}') - return parser - - -class Config: - """A facility for config and config files. - - It supports common file formats as configs: python/json/yaml. The interface - is the same as a dict object and also allows access config values as - attributes. - - Example: - >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - >>> cfg.a - 1 - >>> cfg.b - {'b1': [0, 1]} - >>> cfg.b.b1 - [0, 1] - >>> cfg = Config.fromfile('tests/data/config/a.py') - >>> cfg.filename - "/home/kchen/projects/mmcv/tests/data/config/a.py" - >>> cfg.item4 - 'test' - >>> cfg - "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " - "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" - """ - - @staticmethod - def _validate_py_syntax(filename): - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - content = f.read() - try: - ast.parse(content) - except SyntaxError as e: - raise SyntaxError('There are syntax errors in config ' - f'file {filename}: {e}') - - @staticmethod - def _substitute_predefined_vars(filename, temp_config_name): - file_dirname = osp.dirname(filename) - file_basename = osp.basename(filename) - file_basename_no_extension = osp.splitext(file_basename)[0] - file_extname = osp.splitext(filename)[1] - support_templates = dict( - fileDirname=file_dirname, - fileBasename=file_basename, - fileBasenameNoExtension=file_basename_no_extension, - fileExtname=file_extname) - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - for key, value in support_templates.items(): - regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' - value = value.replace('\\', '/') - config_file = re.sub(regexp, value, config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - - @staticmethod - def _pre_substitute_base_vars(filename, temp_config_name): - """Substitute base variable placehoders to string, so that parsing - would work.""" - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - base_var_dict = {} - regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' - base_vars = set(re.findall(regexp, config_file)) - for base_var in base_vars: - randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' - base_var_dict[randstr] = base_var - regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' - config_file = re.sub(regexp, f'"{randstr}"', config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - return base_var_dict - - @staticmethod - def _substitute_base_vars(cfg, base_var_dict, base_cfg): - """Substitute variable strings to their actual values.""" - cfg = copy.deepcopy(cfg) - - if isinstance(cfg, dict): - for k, v in cfg.items(): - if isinstance(v, str) and v in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[v].split('.'): - new_v = new_v[new_k] - cfg[k] = new_v - elif isinstance(v, (list, tuple, dict)): - cfg[k] = Config._substitute_base_vars( - v, base_var_dict, base_cfg) - elif isinstance(cfg, tuple): - cfg = tuple( - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg) - elif isinstance(cfg, list): - cfg = [ - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg - ] - elif isinstance(cfg, str) and cfg in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[cfg].split('.'): - new_v = new_v[new_k] - cfg = new_v - - return cfg - - @staticmethod - def _file2dict(filename, use_predefined_variables=True): - filename = osp.abspath(osp.expanduser(filename)) - check_file_exist(filename) - fileExtname = osp.splitext(filename)[1] - if fileExtname not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - - with tempfile.TemporaryDirectory() as temp_config_dir: - temp_config_file = tempfile.NamedTemporaryFile( - dir=temp_config_dir, suffix=fileExtname) - if platform.system() == 'Windows': - temp_config_file.close() - temp_config_name = osp.basename(temp_config_file.name) - # Substitute predefined variables - if use_predefined_variables: - Config._substitute_predefined_vars(filename, - temp_config_file.name) - else: - shutil.copyfile(filename, temp_config_file.name) - # Substitute base variables from placeholders to strings - base_var_dict = Config._pre_substitute_base_vars( - temp_config_file.name, temp_config_file.name) - - if filename.endswith('.py'): - temp_module_name = osp.splitext(temp_config_name)[0] - sys.path.insert(0, temp_config_dir) - Config._validate_py_syntax(filename) - mod = import_module(temp_module_name) - sys.path.pop(0) - cfg_dict = { - name: value - for name, value in mod.__dict__.items() - if not name.startswith('__') - } - # delete imported module - del sys.modules[temp_module_name] - elif filename.endswith(('.yml', '.yaml', '.json')): - import mmcv - cfg_dict = mmcv.load(temp_config_file.name) - # close temp file - temp_config_file.close() - - # check deprecation information - if DEPRECATION_KEY in cfg_dict: - deprecation_info = cfg_dict.pop(DEPRECATION_KEY) - warning_msg = f'The config file {filename} will be deprecated ' \ - 'in the future.' - if 'expected' in deprecation_info: - warning_msg += f' Please use {deprecation_info["expected"]} ' \ - 'instead.' - if 'reference' in deprecation_info: - warning_msg += ' More information can be found at ' \ - f'{deprecation_info["reference"]}' - warnings.warn(warning_msg) - - cfg_text = filename + '\n' - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - cfg_text += f.read() - - if BASE_KEY in cfg_dict: - cfg_dir = osp.dirname(filename) - base_filename = cfg_dict.pop(BASE_KEY) - base_filename = base_filename if isinstance( - base_filename, list) else [base_filename] - - cfg_dict_list = list() - cfg_text_list = list() - for f in base_filename: - _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) - cfg_dict_list.append(_cfg_dict) - cfg_text_list.append(_cfg_text) - - base_cfg_dict = dict() - for c in cfg_dict_list: - duplicate_keys = base_cfg_dict.keys() & c.keys() - if len(duplicate_keys) > 0: - raise KeyError('Duplicate key is not allowed among bases. ' - f'Duplicate keys: {duplicate_keys}') - base_cfg_dict.update(c) - - # Substitute base variables from strings to their actual values - cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, - base_cfg_dict) - - base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) - cfg_dict = base_cfg_dict - - # merge cfg_text - cfg_text_list.append(cfg_text) - cfg_text = '\n'.join(cfg_text_list) - - return cfg_dict, cfg_text - - @staticmethod - def _merge_a_into_b(a, b, allow_list_keys=False): - """merge dict ``a`` into dict ``b`` (non-inplace). - - Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid - in-place modifications. - - Args: - a (dict): The source dict to be merged into ``b``. - b (dict): The origin dict to be fetch keys from ``a``. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in source ``a`` and will replace the element of the - corresponding index in b if b is a list. Default: False. - - Returns: - dict: The modified dict of ``b`` using ``a``. - - Examples: - # Normally merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # Delete b first and merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # b is a list - >>> Config._merge_a_into_b( - ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) - [{'a': 2}, {'b': 2}] - """ - b = b.copy() - for k, v in a.items(): - if allow_list_keys and k.isdigit() and isinstance(b, list): - k = int(k) - if len(b) <= k: - raise KeyError(f'Index {k} exceeds the length of list {b}') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - elif isinstance(v, - dict) and k in b and not v.pop(DELETE_KEY, False): - allowed_types = (dict, list) if allow_list_keys else dict - if not isinstance(b[k], allowed_types): - raise TypeError( - f'{k}={v} in child config cannot inherit from base ' - f'because {k} is a dict in the child config but is of ' - f'type {type(b[k])} in base config. You may set ' - f'`{DELETE_KEY}=True` to ignore the base config') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - else: - b[k] = v - return b - - @staticmethod - def fromfile(filename, - use_predefined_variables=True, - import_custom_modules=True): - cfg_dict, cfg_text = Config._file2dict(filename, - use_predefined_variables) - if import_custom_modules and cfg_dict.get('custom_imports', None): - import_modules_from_strings(**cfg_dict['custom_imports']) - return Config(cfg_dict, cfg_text=cfg_text, filename=filename) - - @staticmethod - def fromstring(cfg_str, file_format): - """Generate config from config str. - - Args: - cfg_str (str): Config str. - file_format (str): Config file format corresponding to the - config str. Only py/yml/yaml/json type are supported now! - - Returns: - obj:`Config`: Config obj. - """ - if file_format not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - if file_format != '.py' and 'dict(' in cfg_str: - # check if users specify a wrong suffix for python - warnings.warn( - 'Please check "file_format", the file format may be .py') - with tempfile.NamedTemporaryFile( - 'w', encoding='utf-8', suffix=file_format, - delete=False) as temp_file: - temp_file.write(cfg_str) - # on windows, previous implementation cause error - # see PR 1077 for details - cfg = Config.fromfile(temp_file.name) - os.remove(temp_file.name) - return cfg - - @staticmethod - def auto_argparser(description=None): - """Generate argparser from config file automatically (experimental)""" - partial_parser = ArgumentParser(description=description) - partial_parser.add_argument('config', help='config file path') - cfg_file = partial_parser.parse_known_args()[0].config - cfg = Config.fromfile(cfg_file) - parser = ArgumentParser(description=description) - parser.add_argument('config', help='config file path') - add_args(parser, cfg) - return parser, cfg - - def __init__(self, cfg_dict=None, cfg_text=None, filename=None): - if cfg_dict is None: - cfg_dict = dict() - elif not isinstance(cfg_dict, dict): - raise TypeError('cfg_dict must be a dict, but ' - f'got {type(cfg_dict)}') - for key in cfg_dict: - if key in RESERVED_KEYS: - raise KeyError(f'{key} is reserved for config file') - - super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) - super(Config, self).__setattr__('_filename', filename) - if cfg_text: - text = cfg_text - elif filename: - with open(filename, 'r') as f: - text = f.read() - else: - text = '' - super(Config, self).__setattr__('_text', text) - - @property - def filename(self): - return self._filename - - @property - def text(self): - return self._text - - @property - def pretty_text(self): - - indent = 4 - - def _indent(s_, num_spaces): - s = s_.split('\n') - if len(s) == 1: - return s_ - first = s.pop(0) - s = [(num_spaces * ' ') + line for line in s] - s = '\n'.join(s) - s = first + '\n' + s - return s - - def _format_basic_types(k, v, use_mapping=False): - if isinstance(v, str): - v_str = f"'{v}'" - else: - v_str = str(v) - - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) - - return attr_str - - def _format_list(k, v, use_mapping=False): - # check if all items in the list are dict - if all(isinstance(_, dict) for _ in v): - v_str = '[\n' - v_str += '\n'.join( - f'dict({_indent(_format_dict(v_), indent)}),' - for v_ in v).rstrip(',') - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) + ']' - else: - attr_str = _format_basic_types(k, v, use_mapping) - return attr_str - - def _contain_invalid_identifier(dict_str): - contain_invalid_identifier = False - for key_name in dict_str: - contain_invalid_identifier |= \ - (not str(key_name).isidentifier()) - return contain_invalid_identifier - - def _format_dict(input_dict, outest_level=False): - r = '' - s = [] - - use_mapping = _contain_invalid_identifier(input_dict) - if use_mapping: - r += '{' - for idx, (k, v) in enumerate(input_dict.items()): - is_last = idx >= len(input_dict) - 1 - end = '' if outest_level or is_last else ',' - if isinstance(v, dict): - v_str = '\n' + _format_dict(v) - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: dict({v_str}' - else: - attr_str = f'{str(k)}=dict({v_str}' - attr_str = _indent(attr_str, indent) + ')' + end - elif isinstance(v, list): - attr_str = _format_list(k, v, use_mapping) + end - else: - attr_str = _format_basic_types(k, v, use_mapping) + end - - s.append(attr_str) - r += '\n'.join(s) - if use_mapping: - r += '}' - return r - - cfg_dict = self._cfg_dict.to_dict() - text = _format_dict(cfg_dict, outest_level=True) - # copied from setup.cfg - yapf_style = dict( - based_on_style='pep8', - blank_line_before_nested_class_or_def=True, - split_before_expression_after_opening_paren=True) - text, _ = FormatCode(text, style_config=yapf_style, verify=True) - - return text - - def __repr__(self): - return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' - - def __len__(self): - return len(self._cfg_dict) - - def __getattr__(self, name): - return getattr(self._cfg_dict, name) - - def __getitem__(self, name): - return self._cfg_dict.__getitem__(name) - - def __setattr__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setattr__(name, value) - - def __setitem__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setitem__(name, value) - - def __iter__(self): - return iter(self._cfg_dict) - - def __getstate__(self): - return (self._cfg_dict, self._filename, self._text) - - def __setstate__(self, state): - _cfg_dict, _filename, _text = state - super(Config, self).__setattr__('_cfg_dict', _cfg_dict) - super(Config, self).__setattr__('_filename', _filename) - super(Config, self).__setattr__('_text', _text) - - def dump(self, file=None): - cfg_dict = super(Config, self).__getattribute__('_cfg_dict').to_dict() - if self.filename.endswith('.py'): - if file is None: - return self.pretty_text - else: - with open(file, 'w', encoding='utf-8') as f: - f.write(self.pretty_text) - else: - import mmcv - if file is None: - file_format = self.filename.split('.')[-1] - return mmcv.dump(cfg_dict, file_format=file_format) - else: - mmcv.dump(cfg_dict, file) - - def merge_from_dict(self, options, allow_list_keys=True): - """Merge list into cfg_dict. - - Merge the dict parsed by MultipleKVAction into this cfg. - - Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} - >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) - >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - - # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) - >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) - >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - - Args: - options (dict): dict of configs to merge from. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in ``options`` and will replace the element of the - corresponding index in the config if the config is a list. - Default: True. - """ - option_cfg_dict = {} - for full_key, v in options.items(): - d = option_cfg_dict - key_list = full_key.split('.') - for subkey in key_list[:-1]: - d.setdefault(subkey, ConfigDict()) - d = d[subkey] - subkey = key_list[-1] - d[subkey] = v - - cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - super(Config, self).__setattr__( - '_cfg_dict', - Config._merge_a_into_b( - option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) - - -class DictAction(Action): - """ - argparse action to split an argument into KEY=VALUE form - on the first = and append to a dictionary. List options can - be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit - brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build - list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' - """ - - @staticmethod - def _parse_int_float_bool(val): - try: - return int(val) - except ValueError: - pass - try: - return float(val) - except ValueError: - pass - if val.lower() in ['true', 'false']: - return True if val.lower() == 'true' else False - return val - - @staticmethod - def _parse_iterable(val): - """Parse iterable values in the string. - - All elements inside '()' or '[]' are treated as iterable values. - - Args: - val (str): Value string. - - Returns: - list | tuple: The expanded list or tuple from the string. - - Examples: - >>> DictAction._parse_iterable('1,2,3') - [1, 2, 3] - >>> DictAction._parse_iterable('[a, b, c]') - ['a', 'b', 'c'] - >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') - [(1, 2, 3), ['a', 'b'], 'c'] - """ - - def find_next_comma(string): - """Find the position of next comma in the string. - - If no ',' is found in the string, return the string length. All - chars inside '()' and '[]' are treated as one element and thus ',' - inside these brackets are ignored. - """ - assert (string.count('(') == string.count(')')) and ( - string.count('[') == string.count(']')), \ - f'Imbalanced brackets exist in {string}' - end = len(string) - for idx, char in enumerate(string): - pre = string[:idx] - # The string before this ',' is balanced - if ((char == ',') and (pre.count('(') == pre.count(')')) - and (pre.count('[') == pre.count(']'))): - end = idx - break - return end - - # Strip ' and " characters and replace whitespace. - val = val.strip('\'\"').replace(' ', '') - is_tuple = False - if val.startswith('(') and val.endswith(')'): - is_tuple = True - val = val[1:-1] - elif val.startswith('[') and val.endswith(']'): - val = val[1:-1] - elif ',' not in val: - # val is a single value - return DictAction._parse_int_float_bool(val) - - values = [] - while len(val) > 0: - comma_idx = find_next_comma(val) - element = DictAction._parse_iterable(val[:comma_idx]) - values.append(element) - val = val[comma_idx + 1:] - if is_tuple: - values = tuple(values) - return values - - def __call__(self, parser, namespace, values, option_string=None): - options = {} - for kv in values: - key, val = kv.split('=', maxsplit=1) - options[key] = self._parse_iterable(val) - setattr(namespace, self.dest, options) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/env.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/env.py deleted file mode 100755 index e46a1094f..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/env.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This file holding some environment constant for sharing by other files.""" - -import os.path as osp -import subprocess -import sys -from collections import defaultdict - -import cv2 -import torch - -import mmcv -from .parrots_wrapper import get_build_config - - -def collect_env(): - """Collect the information of the running environments. - - Returns: - dict: The environment information. The following fields are contained. - - - sys.platform: The variable of ``sys.platform``. - - Python: Python version. - - CUDA available: Bool, indicating if CUDA is available. - - GPU devices: Device type of each GPU. - - CUDA_HOME (optional): The env var ``CUDA_HOME``. - - NVCC (optional): NVCC version. - - GCC: GCC version, "n/a" if GCC is not installed. - - PyTorch: PyTorch version. - - PyTorch compiling details: The output of \ - ``torch.__config__.show()``. - - TorchVision (optional): TorchVision version. - - OpenCV: OpenCV version. - - MMCV: MMCV version. - - MMCV Compiler: The GCC version for compiling MMCV ops. - - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. - """ - env_info = {} - env_info['sys.platform'] = sys.platform - env_info['Python'] = sys.version.replace('\n', '') - - cuda_available = torch.cuda.is_available() - env_info['CUDA available'] = cuda_available - - if cuda_available: - devices = defaultdict(list) - for k in range(torch.cuda.device_count()): - devices[torch.cuda.get_device_name(k)].append(str(k)) - for name, device_ids in devices.items(): - env_info['GPU ' + ','.join(device_ids)] = name - - from mmcv.utils.parrots_wrapper import _get_cuda_home - CUDA_HOME = _get_cuda_home() - env_info['CUDA_HOME'] = CUDA_HOME - - if CUDA_HOME is not None and osp.isdir(CUDA_HOME): - try: - nvcc = osp.join(CUDA_HOME, 'bin/nvcc') - nvcc = subprocess.check_output( - f'"{nvcc}" -V | tail -n1', shell=True) - nvcc = nvcc.decode('utf-8').strip() - except subprocess.SubprocessError: - nvcc = 'Not Available' - env_info['NVCC'] = nvcc - - try: - gcc = subprocess.check_output('gcc --version | head -n1', shell=True) - gcc = gcc.decode('utf-8').strip() - env_info['GCC'] = gcc - except subprocess.CalledProcessError: # gcc is unavailable - env_info['GCC'] = 'n/a' - - env_info['PyTorch'] = torch.__version__ - env_info['PyTorch compiling details'] = get_build_config() - - try: - import torchvision - env_info['TorchVision'] = torchvision.__version__ - except ModuleNotFoundError: - pass - - env_info['OpenCV'] = cv2.__version__ - - env_info['MMCV'] = mmcv.__version__ - - try: - from mmcv.ops import get_compiler_version, get_compiling_cuda_version - except ModuleNotFoundError: - env_info['MMCV Compiler'] = 'n/a' - env_info['MMCV CUDA Compiler'] = 'n/a' - else: - env_info['MMCV Compiler'] = get_compiler_version() - env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() - - return env_info diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/ext_loader.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/ext_loader.py deleted file mode 100755 index 08132d2c1..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/ext_loader.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os -import pkgutil -import warnings -from collections import namedtuple - -import torch - -if torch.__version__ != 'parrots': - - def load_ext(name, funcs): - ext = importlib.import_module('mmcv.' + name) - for fun in funcs: - assert hasattr(ext, fun), f'{fun} miss in module {name}' - return ext -else: - from parrots import extension - from parrots.base import ParrotsException - - has_return_value_ops = [ - 'nms', - 'softnms', - 'nms_match', - 'nms_rotated', - 'top_pool_forward', - 'top_pool_backward', - 'bottom_pool_forward', - 'bottom_pool_backward', - 'left_pool_forward', - 'left_pool_backward', - 'right_pool_forward', - 'right_pool_backward', - 'fused_bias_leakyrelu', - 'upfirdn2d', - 'ms_deform_attn_forward', - 'pixel_group', - 'contour_expand', - ] - - def get_fake_func(name, e): - - def fake_func(*args, **kwargs): - warnings.warn(f'{name} is not supported in parrots now') - raise e - - return fake_func - - def load_ext(name, funcs): - ExtModule = namedtuple('ExtModule', funcs) - ext_list = [] - lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - for fun in funcs: - try: - ext_fun = extension.load(fun, name, lib_dir=lib_root) - except ParrotsException as e: - if 'No element registered' not in e.message: - warnings.warn(e.message) - ext_fun = get_fake_func(fun, e) - ext_list.append(ext_fun) - else: - if fun in has_return_value_ops: - ext_list.append(ext_fun.op) - else: - ext_list.append(ext_fun.op_) - return ExtModule(*ext_list) - - -def check_ops_exist(): - ext_loader = pkgutil.find_loader('mmcv._ext') - return ext_loader is not None diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/logging.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/logging.py deleted file mode 100755 index 4aa0e04bb..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/logging.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.distributed as dist - -logger_initialized = {} - - -def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): - """Initialize and get a logger by name. - - If the logger has not been initialized, this method will initialize the - logger by adding one or two handlers, otherwise the initialized logger will - be directly returned. During initialization, a StreamHandler will always be - added. If `log_file` is specified and the process rank is 0, a FileHandler - will also be added. - - Args: - name (str): Logger name. - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the logger. - log_level (int): The logger level. Note that only the process of - rank 0 is affected, and other processes will set the level to - "Error" thus be silent most of the time. - file_mode (str): The file mode used in opening log file. - Defaults to 'w'. - - Returns: - logging.Logger: The expected logger. - """ - logger = logging.getLogger(name) - if name in logger_initialized: - return logger - # handle hierarchical names - # e.g., logger "a" is initialized, then logger "a.b" will skip the - # initialization since it is a child of "a". - for logger_name in logger_initialized: - if name.startswith(logger_name): - return logger - - # handle duplicate logs to the console - # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) - # to the root logger. As logger.propagate is True by default, this root - # level handler causes logging messages from rank>0 processes to - # unexpectedly show up on the console, creating much unwanted clutter. - # To fix this issue, we set the root logger's StreamHandler, if any, to log - # at the ERROR level. - for handler in logger.root.handlers: - if type(handler) is logging.StreamHandler: - handler.setLevel(logging.ERROR) - - stream_handler = logging.StreamHandler() - handlers = [stream_handler] - - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - else: - rank = 0 - - # only rank 0 will add a FileHandler - if rank == 0 and log_file is not None: - # Here, the default behaviour of the official logger is 'a'. Thus, we - # provide an interface to change the file mode to the default - # behaviour. - file_handler = logging.FileHandler(log_file, file_mode) - handlers.append(file_handler) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - for handler in handlers: - handler.setFormatter(formatter) - handler.setLevel(log_level) - logger.addHandler(handler) - - if rank == 0: - logger.setLevel(log_level) - else: - logger.setLevel(logging.ERROR) - - logger_initialized[name] = True - - return logger - - -def print_log(msg, logger=None, level=logging.INFO): - """Print a log message. - - Args: - msg (str): The message to be logged. - logger (logging.Logger | str | None): The logger to be used. - Some special loggers are: - - "silent": no message will be printed. - - other str: the logger obtained with `get_root_logger(logger)`. - - None: The `print()` method will be used to print log messages. - level (int): Logging level. Only available when `logger` is a Logger - object or "root". - """ - if logger is None: - print(msg) - elif isinstance(logger, logging.Logger): - logger.log(level, msg) - elif logger == 'silent': - pass - elif isinstance(logger, str): - _logger = get_logger(logger) - _logger.log(level, msg) - else: - raise TypeError( - 'logger should be either a logging.Logger object, str, ' - f'"silent" or None, but got {type(logger)}') diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/misc.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/misc.py deleted file mode 100755 index 2c58d0d7f..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/misc.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections.abc -import functools -import itertools -import subprocess -import warnings -from collections import abc -from importlib import import_module -from inspect import getfullargspec -from itertools import repeat - - -# From PyTorch internals -def _ntuple(n): - - def parse(x): - if isinstance(x, collections.abc.Iterable): - return x - return tuple(repeat(x, n)) - - return parse - - -to_1tuple = _ntuple(1) -to_2tuple = _ntuple(2) -to_3tuple = _ntuple(3) -to_4tuple = _ntuple(4) -to_ntuple = _ntuple - - -def is_str(x): - """Whether the input is an string instance. - - Note: This method is deprecated since python 2 is no longer supported. - """ - return isinstance(x, str) - - -def import_modules_from_strings(imports, allow_failed_imports=False): - """Import modules from the given list of strings. - - Args: - imports (list | str | None): The given module names to be imported. - allow_failed_imports (bool): If True, the failed imports will return - None. Otherwise, an ImportError is raise. Default: False. - - Returns: - list[module] | module | None: The imported modules. - - Examples: - >>> osp, sys = import_modules_from_strings( - ... ['os.path', 'sys']) - >>> import os.path as osp_ - >>> import sys as sys_ - >>> assert osp == osp_ - >>> assert sys == sys_ - """ - if not imports: - return - single_import = False - if isinstance(imports, str): - single_import = True - imports = [imports] - if not isinstance(imports, list): - raise TypeError( - f'custom_imports must be a list but got type {type(imports)}') - imported = [] - for imp in imports: - if not isinstance(imp, str): - raise TypeError( - f'{imp} is of type {type(imp)} and cannot be imported.') - try: - imported_tmp = import_module(imp) - except ImportError: - if allow_failed_imports: - warnings.warn(f'{imp} failed to import and is ignored.', - UserWarning) - imported_tmp = None - else: - raise ImportError - imported.append(imported_tmp) - if single_import: - imported = imported[0] - return imported - - -def iter_cast(inputs, dst_type, return_type=None): - """Cast elements of an iterable object into some type. - - Args: - inputs (Iterable): The input object. - dst_type (type): Destination type. - return_type (type, optional): If specified, the output object will be - converted to this type, otherwise an iterator. - - Returns: - iterator or specified type: The converted object. - """ - if not isinstance(inputs, abc.Iterable): - raise TypeError('inputs must be an iterable object') - if not isinstance(dst_type, type): - raise TypeError('"dst_type" must be a valid type') - - out_iterable = map(dst_type, inputs) - - if return_type is None: - return out_iterable - else: - return return_type(out_iterable) - - -def list_cast(inputs, dst_type): - """Cast elements of an iterable object into a list of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=list) - - -def tuple_cast(inputs, dst_type): - """Cast elements of an iterable object into a tuple of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=tuple) - - -def is_seq_of(seq, expected_type, seq_type=None): - """Check whether it is a sequence of some type. - - Args: - seq (Sequence): The sequence to be checked. - expected_type (type): Expected type of sequence items. - seq_type (type, optional): Expected sequence type. - - Returns: - bool: Whether the sequence is valid. - """ - if seq_type is None: - exp_seq_type = abc.Sequence - else: - assert isinstance(seq_type, type) - exp_seq_type = seq_type - if not isinstance(seq, exp_seq_type): - return False - for item in seq: - if not isinstance(item, expected_type): - return False - return True - - -def is_list_of(seq, expected_type): - """Check whether it is a list of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=list) - - -def is_tuple_of(seq, expected_type): - """Check whether it is a tuple of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=tuple) - - -def slice_list(in_list, lens): - """Slice a list into several sub lists by a list of given length. - - Args: - in_list (list): The list to be sliced. - lens(int or list): The expected length of each out list. - - Returns: - list: A list of sliced list. - """ - if isinstance(lens, int): - assert len(in_list) % lens == 0 - lens = [lens] * int(len(in_list) / lens) - if not isinstance(lens, list): - raise TypeError('"indices" must be an integer or a list of integers') - elif sum(lens) != len(in_list): - raise ValueError('sum of lens and list length does not ' - f'match: {sum(lens)} != {len(in_list)}') - out_list = [] - idx = 0 - for i in range(len(lens)): - out_list.append(in_list[idx:idx + lens[i]]) - idx += lens[i] - return out_list - - -def concat_list(in_list): - """Concatenate a list of list into a single list. - - Args: - in_list (list): The list of list to be merged. - - Returns: - list: The concatenated flat list. - """ - return list(itertools.chain(*in_list)) - - -def check_prerequisites( - prerequisites, - checker, - msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' - 'found, please install them first.'): # yapf: disable - """A decorator factory to check if prerequisites are satisfied. - - Args: - prerequisites (str of list[str]): Prerequisites to be checked. - checker (callable): The checker method that returns True if a - prerequisite is meet, False otherwise. - msg_tmpl (str): The message template with two variables. - - Returns: - decorator: A specific decorator. - """ - - def wrap(func): - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - requirements = [prerequisites] if isinstance( - prerequisites, str) else prerequisites - missing = [] - for item in requirements: - if not checker(item): - missing.append(item) - if missing: - print(msg_tmpl.format(', '.join(missing), func.__name__)) - raise RuntimeError('Prerequisites not meet.') - else: - return func(*args, **kwargs) - - return wrapped_func - - return wrap - - -def _check_py_package(package): - try: - import_module(package) - except ImportError: - return False - else: - return True - - -def _check_executable(cmd): - if subprocess.call(f'which {cmd}', shell=True) != 0: - return False - else: - return True - - -def requires_package(prerequisites): - """A decorator to check if some python packages are installed. - - Example: - >>> @requires_package('numpy') - >>> func(arg1, args): - >>> return numpy.zeros(1) - array([0.]) - >>> @requires_package(['numpy', 'non_package']) - >>> func(arg1, args): - >>> return numpy.zeros(1) - ImportError - """ - return check_prerequisites(prerequisites, checker=_check_py_package) - - -def requires_executable(prerequisites): - """A decorator to check if some executable files are installed. - - Example: - >>> @requires_executable('ffmpeg') - >>> func(arg1, args): - >>> print(1) - 1 - """ - return check_prerequisites(prerequisites, checker=_check_executable) - - -def deprecated_api_warning(name_dict, cls_name=None): - """A decorator to check if some arguments are deprecate and try to replace - deprecate src_arg_name to dst_arg_name. - - Args: - name_dict(dict): - key (str): Deprecate argument names. - val (str): Expected argument names. - - Returns: - func: New function. - """ - - def api_warning_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get name of the function - func_name = old_func.__name__ - if cls_name is not None: - func_name = f'{cls_name}.{func_name}' - if args: - arg_names = args_info.args[:len(args)] - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in arg_names: - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead') - arg_names[arg_names.index(src_arg_name)] = dst_arg_name - if kwargs: - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in kwargs: - - assert dst_arg_name not in kwargs, ( - f'The expected behavior is to replace ' - f'the deprecated key `{src_arg_name}` to ' - f'new key `{dst_arg_name}`, but got them ' - f'in the arguments at the same time, which ' - f'is confusing. `{src_arg_name} will be ' - f'deprecated in the future, please ' - f'use `{dst_arg_name}` instead.') - - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead') - kwargs[dst_arg_name] = kwargs.pop(src_arg_name) - - # apply converted arguments to the decorated method - output = old_func(*args, **kwargs) - return output - - return new_func - - return api_warning_wrapper - - -def is_method_overridden(method, base_class, derived_class): - """Check if a method of base class is overridden in derived class. - - Args: - method (str): the method name to check. - base_class (type): the class of the base class. - derived_class (type | Any): the class or instance of the derived class. - """ - assert isinstance(base_class, type), \ - "base_class doesn't accept instance, Please pass class instead." - - if not isinstance(derived_class, type): - derived_class = derived_class.__class__ - - base_method = getattr(base_class, method) - derived_method = getattr(derived_class, method) - return derived_method != base_method - - -def has_method(obj: object, method: str) -> bool: - """Check whether the object has a method. - - Args: - method (str): The method name to check. - obj (object): The object to check. - - Returns: - bool: True if the object has the method else False. - """ - return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/parrots_jit.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/parrots_jit.py deleted file mode 100755 index 61873f6db..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/parrots_jit.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -from .parrots_wrapper import TORCH_VERSION - -parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') - -if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': - from parrots.jit import pat as jit -else: - - def jit(func=None, - check_input=None, - full_shape=True, - derivate=False, - coderize=False, - optimize=False): - - def wrapper(func): - - def wrapper_inner(*args, **kargs): - return func(*args, **kargs) - - return wrapper_inner - - if func is None: - return wrapper - else: - return func - - -if TORCH_VERSION == 'parrots': - from parrots.utils.tester import skip_no_elena -else: - - def skip_no_elena(func): - - def wrapper(*args, **kargs): - return func(*args, **kargs) - - return wrapper diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/parrots_wrapper.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/parrots_wrapper.py deleted file mode 100755 index 93c97640d..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/parrots_wrapper.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch - -TORCH_VERSION = torch.__version__ - - -def is_rocm_pytorch() -> bool: - is_rocm = False - if TORCH_VERSION != 'parrots': - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - return is_rocm - - -def _get_cuda_home(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import CUDA_HOME - else: - if is_rocm_pytorch(): - from torch.utils.cpp_extension import ROCM_HOME - CUDA_HOME = ROCM_HOME - else: - from torch.utils.cpp_extension import CUDA_HOME - return CUDA_HOME - - -def get_build_config(): - if TORCH_VERSION == 'parrots': - from parrots.config import get_build_info - return get_build_info() - else: - return torch.__config__.show() - - -def _get_conv(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin - else: - from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin - return _ConvNd, _ConvTransposeMixin - - -def _get_dataloader(): - if TORCH_VERSION == 'parrots': - from torch.utils.data import DataLoader, PoolDataLoader - else: - from torch.utils.data import DataLoader - PoolDataLoader = DataLoader - return DataLoader, PoolDataLoader - - -def _get_extension(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import BuildExtension, Extension - CppExtension = partial(Extension, cuda=False) - CUDAExtension = partial(Extension, cuda=True) - else: - from torch.utils.cpp_extension import (BuildExtension, CppExtension, - CUDAExtension) - return BuildExtension, CppExtension, CUDAExtension - - -def _get_pool(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - else: - from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd - - -def _get_norm(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm2d - else: - from torch.nn.modules.instancenorm import _InstanceNorm - from torch.nn.modules.batchnorm import _BatchNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm - return _BatchNorm, _InstanceNorm, SyncBatchNorm_ - - -_ConvNd, _ConvTransposeMixin = _get_conv() -DataLoader, PoolDataLoader = _get_dataloader() -BuildExtension, CppExtension, CUDAExtension = _get_extension() -_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() -_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() - - -class SyncBatchNorm(SyncBatchNorm_): - - def _check_input_dim(self, input): - if TORCH_VERSION == 'parrots': - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input (got {input.dim()}D input)') - else: - super()._check_input_dim(input) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/path.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/path.py deleted file mode 100755 index 7dab4b304..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/path.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -from pathlib import Path - -from .misc import is_str - - -def is_filepath(x): - return is_str(x) or isinstance(x, Path) - - -def fopen(filepath, *args, **kwargs): - if is_str(filepath): - return open(filepath, *args, **kwargs) - elif isinstance(filepath, Path): - return filepath.open(*args, **kwargs) - raise ValueError('`filepath` should be a string or a Path') - - -def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): - if not osp.isfile(filename): - raise FileNotFoundError(msg_tmpl.format(filename)) - - -def mkdir_or_exist(dir_name, mode=0o777): - if dir_name == '': - return - dir_name = osp.expanduser(dir_name) - os.makedirs(dir_name, mode=mode, exist_ok=True) - - -def symlink(src, dst, overwrite=True, **kwargs): - if os.path.lexists(dst) and overwrite: - os.remove(dst) - os.symlink(src, dst, **kwargs) - - -def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): - """Scan a directory to find the interested files. - - Args: - dir_path (str | obj:`Path`): Path of the directory. - suffix (str | tuple(str), optional): File suffix that we are - interested in. Default: None. - recursive (bool, optional): If set to True, recursively scan the - directory. Default: False. - case_sensitive (bool, optional) : If set to False, ignore the case of - suffix. Default: True. - - Returns: - A generator for all the interested files with relative paths. - """ - if isinstance(dir_path, (str, Path)): - dir_path = str(dir_path) - else: - raise TypeError('"dir_path" must be a string or Path object') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('"suffix" must be a string or tuple of strings') - - if suffix is not None and not case_sensitive: - suffix = suffix.lower() if isinstance(suffix, str) else tuple( - item.lower() for item in suffix) - - root = dir_path - - def _scandir(dir_path, suffix, recursive, case_sensitive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - _rel_path = rel_path if case_sensitive else rel_path.lower() - if suffix is None or _rel_path.endswith(suffix): - yield rel_path - elif recursive and os.path.isdir(entry.path): - # scan recursively if entry.path is a directory - yield from _scandir(entry.path, suffix, recursive, - case_sensitive) - - return _scandir(dir_path, suffix, recursive, case_sensitive) - - -def find_vcs_root(path, markers=('.git', )): - """Finds the root directory (including itself) of specified markers. - - Args: - path (str): Path of directory or file. - markers (list[str], optional): List of file or directory names. - - Returns: - The directory contained one of the markers or None if not found. - """ - if osp.isfile(path): - path = osp.dirname(path) - - prev, cur = None, osp.abspath(osp.expanduser(path)) - while cur != prev: - if any(osp.exists(osp.join(cur, marker)) for marker in markers): - return cur - prev, cur = cur, osp.split(cur)[0] - return None diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/progressbar.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/progressbar.py deleted file mode 100755 index 0062f670d..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/progressbar.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from collections.abc import Iterable -from multiprocessing import Pool -from shutil import get_terminal_size - -from .timer import Timer - - -class ProgressBar: - """A progress bar which can print the progress.""" - - def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): - self.task_num = task_num - self.bar_width = bar_width - self.completed = 0 - self.file = file - if start: - self.start() - - @property - def terminal_width(self): - width, _ = get_terminal_size() - return width - - def start(self): - if self.task_num > 0: - self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' - 'elapsed: 0s, ETA:') - else: - self.file.write('completed: 0, elapsed: 0s') - self.file.flush() - self.timer = Timer() - - def update(self, num_tasks=1): - assert num_tasks > 0 - self.completed += num_tasks - elapsed = self.timer.since_start() - if elapsed > 0: - fps = self.completed / elapsed - else: - fps = float('inf') - if self.task_num > 0: - percentage = self.completed / float(self.task_num) - eta = int(elapsed * (1 - percentage) / percentage + 0.5) - msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ - f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ - f'ETA: {eta:5}s' - - bar_width = min(self.bar_width, - int(self.terminal_width - len(msg)) + 2, - int(self.terminal_width * 0.6)) - bar_width = max(2, bar_width) - mark_width = int(bar_width * percentage) - bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) - self.file.write(msg.format(bar_chars)) - else: - self.file.write( - f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' - f' {fps:.1f} tasks/s') - self.file.flush() - - -def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): - """Track the progress of tasks execution with a progress bar. - - Tasks are done with a simple for-loop. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - results = [] - for task in tasks: - results.append(func(task, **kwargs)) - prog_bar.update() - prog_bar.file.write('\n') - return results - - -def init_pool(process_num, initializer=None, initargs=None): - if initializer is None: - return Pool(process_num) - elif initargs is None: - return Pool(process_num, initializer) - else: - if not isinstance(initargs, tuple): - raise TypeError('"initargs" must be a tuple') - return Pool(process_num, initializer, initargs) - - -def track_parallel_progress(func, - tasks, - nproc, - initializer=None, - initargs=None, - bar_width=50, - chunksize=1, - skip_first=False, - keep_order=True, - file=sys.stdout): - """Track the progress of parallel task execution with a progress bar. - - The built-in :mod:`multiprocessing` module is used for process pools and - tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - nproc (int): Process (worker) number. - initializer (None or callable): Refer to :class:`multiprocessing.Pool` - for details. - initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for - details. - chunksize (int): Refer to :class:`multiprocessing.Pool` for details. - bar_width (int): Width of progress bar. - skip_first (bool): Whether to skip the first sample for each worker - when estimating fps, since the initialization step may takes - longer. - keep_order (bool): If True, :func:`Pool.imap` is used, otherwise - :func:`Pool.imap_unordered` is used. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - pool = init_pool(nproc, initializer, initargs) - start = not skip_first - task_num -= nproc * chunksize * int(skip_first) - prog_bar = ProgressBar(task_num, bar_width, start, file=file) - results = [] - if keep_order: - gen = pool.imap(func, tasks, chunksize) - else: - gen = pool.imap_unordered(func, tasks, chunksize) - for result in gen: - results.append(result) - if skip_first: - if len(results) < nproc * chunksize: - continue - elif len(results) == nproc * chunksize: - prog_bar.start() - continue - prog_bar.update() - prog_bar.file.write('\n') - pool.close() - pool.join() - return results - - -def track_iter_progress(tasks, bar_width=50, file=sys.stdout): - """Track the progress of tasks iteration or enumeration with a progress - bar. - - Tasks are yielded with a simple for-loop. - - Args: - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Yields: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - for task in tasks: - yield task - prog_bar.update() - prog_bar.file.write('\n') diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/registry.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/registry.py deleted file mode 100755 index fa9df39bc..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/registry.py +++ /dev/null @@ -1,315 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import warnings -from functools import partial - -from .misc import is_seq_of - - -def build_from_cfg(cfg, registry, default_args=None): - """Build a module from config dict. - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - try: - return obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{obj_cls.__name__}: {e}') - - -class Registry: - """A registry to map strings to classes. - - Registered object could be built from registry. - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = MODELS.build(dict(type='ResNet')) - - Please refer to - https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for - advanced usage. - - Args: - name (str): Registry name. - build_func(func, optional): Build function to construct instance from - Registry, func:`build_from_cfg` is used if neither ``parent`` or - ``build_func`` is specified. If ``parent`` is specified and - ``build_func`` is not given, ``build_func`` will be inherited - from ``parent``. Default: None. - parent (Registry, optional): Parent registry. The class registered in - children registry could be built from parent. Default: None. - scope (str, optional): The scope of registry. It is the key to search - for children registry. If not specified, scope will be the name of - the package where class is defined, e.g. mmdet, mmcls, mmseg. - Default: None. - """ - - def __init__(self, name, build_func=None, parent=None, scope=None): - self._name = name - self._module_dict = dict() - self._children = dict() - self._scope = self.infer_scope() if scope is None else scope - - # self.build_func will be set with the following priority: - # 1. build_func - # 2. parent.build_func - # 3. build_from_cfg - if build_func is None: - if parent is not None: - self.build_func = parent.build_func - else: - self.build_func = build_from_cfg - else: - self.build_func = build_func - if parent is not None: - assert isinstance(parent, Registry) - parent._add_children(self) - self.parent = parent - else: - self.parent = None - - def __len__(self): - return len(self._module_dict) - - def __contains__(self, key): - return self.get(key) is not None - - def __repr__(self): - format_str = self.__class__.__name__ + \ - f'(name={self._name}, ' \ - f'items={self._module_dict})' - return format_str - - @staticmethod - def infer_scope(): - """Infer the scope of registry. - - The name of the package where registry is defined will be returned. - - Example: - # in mmdet/models/backbone/resnet.py - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - The scope of ``ResNet`` will be ``mmdet``. - - - Returns: - scope (str): The inferred scope name. - """ - # inspect.stack() trace where this function is called, the index-2 - # indicates the frame where `infer_scope()` is called - filename = inspect.getmodule(inspect.stack()[2][0]).__name__ - split_filename = filename.split('.') - return split_filename[0] - - @staticmethod - def split_scope_key(key): - """Split scope and key. - - The first scope will be split from key. - - Examples: - >>> Registry.split_scope_key('mmdet.ResNet') - 'mmdet', 'ResNet' - >>> Registry.split_scope_key('ResNet') - None, 'ResNet' - - Return: - scope (str, None): The first scope. - key (str): The remaining key. - """ - split_index = key.find('.') - if split_index != -1: - return key[:split_index], key[split_index + 1:] - else: - return None, key - - @property - def name(self): - return self._name - - @property - def scope(self): - return self._scope - - @property - def module_dict(self): - return self._module_dict - - @property - def children(self): - return self._children - - def get(self, key): - """Get the registry record. - - Args: - key (str): The class name in string format. - - Returns: - class: The corresponding class. - """ - scope, real_key = self.split_scope_key(key) - if scope is None or scope == self._scope: - # get from self - if real_key in self._module_dict: - return self._module_dict[real_key] - else: - # get from self._children - if scope in self._children: - return self._children[scope].get(real_key) - else: - # goto root - parent = self.parent - while parent.parent is not None: - parent = parent.parent - return parent.get(key) - - def build(self, *args, **kwargs): - return self.build_func(*args, **kwargs, registry=self) - - def _add_children(self, registry): - """Add children for a registry. - - The ``registry`` will be added as children based on its scope. - The parent registry could build objects from children registry. - - Example: - >>> models = Registry('models') - >>> mmdet_models = Registry('models', parent=models) - >>> @mmdet_models.register_module() - >>> class ResNet: - >>> pass - >>> resnet = models.build(dict(type='mmdet.ResNet')) - """ - - assert isinstance(registry, Registry) - assert registry.scope is not None - assert registry.scope not in self.children, \ - f'scope {registry.scope} exists in {self.name} registry' - self.children[registry.scope] = registry - - def _register_module(self, module_class, module_name=None, force=False): - if not inspect.isclass(module_class): - raise TypeError('module must be a class, ' - f'but got {type(module_class)}') - - if module_name is None: - module_name = module_class.__name__ - if isinstance(module_name, str): - module_name = [module_name] - for name in module_name: - if not force and name in self._module_dict: - raise KeyError(f'{name} is already registered ' - f'in {self.name}') - self._module_dict[name] = module_class - - def deprecated_register_module(self, cls=None, force=False): - warnings.warn( - 'The old API of register_module(module, force=False) ' - 'is deprecated and will be removed, please use the new API ' - 'register_module(name=None, force=False, module=None) instead.') - if cls is None: - return partial(self.deprecated_register_module, force=force) - self._register_module(cls, force=force) - return cls - - def register_module(self, name=None, force=False, module=None): - """Register a module. - - A record will be added to `self._module_dict`, whose key is the class - name or the specified name, and value is the class itself. - It can be used as a decorator or a normal function. - - Example: - >>> backbones = Registry('backbone') - >>> @backbones.register_module() - >>> class ResNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> @backbones.register_module(name='mnet') - >>> class MobileNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> class ResNet: - >>> pass - >>> backbones.register_module(ResNet) - - Args: - name (str | None): The module name to be registered. If not - specified, the class name will be used. - force (bool, optional): Whether to override an existing class with - the same name. Default: False. - module (type): Module class to be registered. - """ - if not isinstance(force, bool): - raise TypeError(f'force must be a boolean, but got {type(force)}') - # NOTE: This is a walkaround to be compatible with the old api, - # while it may introduce unexpected bugs. - if isinstance(name, type): - return self.deprecated_register_module(name, force=force) - - # raise the error ahead of time - if not (name is None or isinstance(name, str) or is_seq_of(name, str)): - raise TypeError( - 'name must be either of None, an instance of str or a sequence' - f' of str, but got {type(name)}') - - # use it as a normal method: x.register_module(module=SomeClass) - if module is not None: - self._register_module( - module_class=module, module_name=name, force=force) - return module - - # use it as a decorator: @x.register_module() - def _register(cls): - self._register_module( - module_class=cls, module_name=name, force=force) - return cls - - return _register diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/testing.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/testing.py deleted file mode 100755 index a27f936da..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/testing.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) Open-MMLab. -import sys -from collections.abc import Iterable -from runpy import run_path -from shlex import split -from typing import Any, Dict, List -from unittest.mock import patch - - -def check_python_script(cmd): - """Run the python cmd script with `__main__`. The difference between - `os.system` is that, this function exectues code in the current process, so - that it can be tracked by coverage tools. Currently it supports two forms: - - - ./tests/data/scripts/hello.py zz - - python tests/data/scripts/hello.py zz - """ - args = split(cmd) - if args[0] == 'python': - args = args[1:] - with patch.object(sys, 'argv', args): - run_path(args[0], run_name='__main__') - - -def _any(judge_result): - """Since built-in ``any`` works only when the element of iterable is not - iterable, implement the function.""" - if not isinstance(judge_result, Iterable): - return judge_result - - try: - for element in judge_result: - if _any(element): - return True - except TypeError: - # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) - if judge_result: - return True - return False - - -def assert_dict_contains_subset(dict_obj: Dict[Any, Any], - expected_subset: Dict[Any, Any]) -> bool: - """Check if the dict_obj contains the expected_subset. - - Args: - dict_obj (Dict[Any, Any]): Dict object to be checked. - expected_subset (Dict[Any, Any]): Subset expected to be contained in - dict_obj. - - Returns: - bool: Whether the dict_obj contains the expected_subset. - """ - - for key, value in expected_subset.items(): - if key not in dict_obj.keys() or _any(dict_obj[key] != value): - return False - return True - - -def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: - """Check if attribute of class object is correct. - - Args: - obj (object): Class object to be checked. - expected_attrs (Dict[str, Any]): Dict of the expected attrs. - - Returns: - bool: Whether the attribute of class object is correct. - """ - for attr, value in expected_attrs.items(): - if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): - return False - return True - - -def assert_dict_has_keys(obj: Dict[str, Any], - expected_keys: List[str]) -> bool: - """Check if the obj has all the expected_keys. - - Args: - obj (Dict[str, Any]): Object to be checked. - expected_keys (List[str]): Keys expected to contained in the keys of - the obj. - - Returns: - bool: Whether the obj has the expected keys. - """ - return set(expected_keys).issubset(set(obj.keys())) - - -def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: - """Check if target_keys is equal to result_keys. - - Args: - result_keys (List[str]): Result keys to be checked. - target_keys (List[str]): Target keys to be checked. - - Returns: - bool: Whether target_keys is equal to result_keys. - """ - return set(result_keys) == set(target_keys) - - -def assert_is_norm_layer(module) -> bool: - """Check if the module is a norm layer. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the module is a norm layer. - """ - from .parrots_wrapper import _BatchNorm, _InstanceNorm - from torch.nn import GroupNorm, LayerNorm - norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) - return isinstance(module, norm_layer_candidates) - - -def assert_params_all_zeros(module) -> bool: - """Check if the parameters of the module is all zeros. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the parameters of the module is all zeros. - """ - weight_data = module.weight.data - is_weight_zero = weight_data.allclose( - weight_data.new_zeros(weight_data.size())) - - if hasattr(module, 'bias') and module.bias is not None: - bias_data = module.bias.data - is_bias_zero = bias_data.allclose( - bias_data.new_zeros(bias_data.size())) - else: - is_bias_zero = True - - return is_weight_zero and is_bias_zero diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/timer.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/timer.py deleted file mode 100755 index 66d4a78a8..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/timer.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from time import time - - -class TimerError(Exception): - - def __init__(self, message): - self.message = message - super(TimerError, self).__init__(message) - - -class Timer: - """A flexible Timer class. - - :Example: - - >>> import time - >>> import mmcv - >>> with mmcv.Timer(): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - 1.000 - >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - it takes 1.0 seconds - >>> timer = mmcv.Timer() - >>> time.sleep(0.5) - >>> print(timer.since_start()) - 0.500 - >>> time.sleep(0.5) - >>> print(timer.since_last_check()) - 0.500 - >>> print(timer.since_start()) - 1.000 - """ - - def __init__(self, start=True, print_tmpl=None): - self._is_running = False - self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' - if start: - self.start() - - @property - def is_running(self): - """bool: indicate whether the timer is running""" - return self._is_running - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - print(self.print_tmpl.format(self.since_last_check())) - self._is_running = False - - def start(self): - """Start the timer.""" - if not self._is_running: - self._t_start = time() - self._is_running = True - self._t_last = time() - - def since_start(self): - """Total time since the timer is started. - - Returns (float): Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - self._t_last = time() - return self._t_last - self._t_start - - def since_last_check(self): - """Time since the last checking. - - Either :func:`since_start` or :func:`since_last_check` is a checking - operation. - - Returns (float): Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - dur = time() - self._t_last - self._t_last = time() - return dur - - -_g_timers = {} # global timers - - -def check_time(timer_id): - """Add check points in a single line. - - This method is suitable for running a task on a list of items. A timer will - be registered when the method is called for the first time. - - :Example: - - >>> import time - >>> import mmcv - >>> for i in range(1, 6): - >>> # simulate a code block - >>> time.sleep(i) - >>> mmcv.check_time('task1') - 2.000 - 3.000 - 4.000 - 5.000 - - Args: - timer_id (str): Timer identifier. - """ - if timer_id not in _g_timers: - _g_timers[timer_id] = Timer() - return 0 - else: - return _g_timers[timer_id].since_last_check() diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/trace.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/trace.py deleted file mode 100755 index 8e49bfd38..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/trace.py +++ /dev/null @@ -1,23 +0,0 @@ -import warnings - -import torch - -from mmcv.utils import digit_version - - -def is_jit_tracing() -> bool: - if (torch.__version__ != 'parrots' - and digit_version(torch.__version__) >= digit_version('1.6.0')): - on_trace = torch.jit.is_tracing() - # In PyTorch 1.6, torch.jit.is_tracing has a bug. - # Refers to https://github.com/pytorch/pytorch/issues/42448 - if isinstance(on_trace, bool): - return on_trace - else: - return torch._C._is_tracing() - else: - warnings.warn( - 'torch.jit.is_tracing is only supported after v1.6.0. ' - 'Therefore is_tracing returns False automatically. Please ' - 'set on_trace manually if you are using trace.', UserWarning) - return False diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/version_utils.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/version_utils.py deleted file mode 100755 index 963c45a2e..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/utils/version_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import subprocess -import warnings - -from packaging.version import parse - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - assert 'parrots' not in version_str - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - -def get_git_hash(fallback='unknown', digits=None): - """Get the git hash of the current repo. - - Args: - fallback (str, optional): The fallback string when git hash is - unavailable. Defaults to 'unknown'. - digits (int, optional): kept digits of the hash. Defaults to None, - meaning all digits are kept. - - Returns: - str: Git commit hash. - """ - - if digits is not None and not isinstance(digits, int): - raise TypeError('digits must be None or an integer') - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - sha = out.strip().decode('ascii') - if digits is not None: - sha = sha[:digits] - except OSError: - sha = fallback - - return sha diff --git a/cv/super_resolution/basicvsr++/pytorch/mmcv/version.py b/cv/super_resolution/basicvsr++/pytorch/mmcv/version.py deleted file mode 100755 index 1cce4e50b..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmcv/version.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -__version__ = '1.3.17' - - -def parse_version_info(version_str: str, length: int = 4) -> tuple: - """Parse a version string into a tuple. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int | str]: The version info, e.g., "1.3.0" is parsed into - (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into - (2, 0, 0, 0, 'rc', 1) (when length is set to 4). - """ - from packaging.version import parse - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - release.extend(list(version.pre)) - elif version.is_postrelease: - release.extend(list(version.post)) - else: - release.extend([0, 0]) - return tuple(release) - - -version_info = tuple(int(x) for x in __version__.split('.')[:3]) - -__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/__init__.py deleted file mode 100755 index 05d4c20b7..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - -from .version import __version__, version_info - -try: - from mmcv.utils import digit_version -except ImportError: - - def digit_version(version_str): - digit_ver = [] - for x in version_str.split('.'): - if x.isdigit(): - digit_ver.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - digit_ver.append(int(patch_version[0]) - 1) - digit_ver.append(int(patch_version[1])) - return digit_ver - - -MMCV_MIN = '1.3.13' -MMCV_MAX = '1.6' - -mmcv_min_version = digit_version(MMCV_MIN) -mmcv_max_version = digit_version(MMCV_MAX) -mmcv_version = digit_version(mmcv.__version__) - - -assert (mmcv_min_version <= mmcv_version <= mmcv_max_version), \ - f'mmcv=={mmcv.__version__} is used but incompatible. ' \ - f'Please install mmcv-full>={mmcv_min_version}, <={mmcv_max_version}.' - -__all__ = ['__version__', 'version_info'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/__init__.py deleted file mode 100755 index 1ae70035e..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .generation_inference import generation_inference -from .inpainting_inference import inpainting_inference -from .matting_inference import init_model, matting_inference -from .restoration_face_inference import restoration_face_inference -from .restoration_inference import restoration_inference -from .restoration_video_inference import restoration_video_inference -from .test import multi_gpu_test, single_gpu_test -from .train import init_random_seed, set_random_seed, train_model -from .video_interpolation_inference import video_interpolation_inference - -__all__ = [ - 'train_model', 'set_random_seed', 'init_model', 'matting_inference', - 'inpainting_inference', 'restoration_inference', 'generation_inference', - 'multi_gpu_test', 'single_gpu_test', 'restoration_video_inference', - 'restoration_face_inference', 'video_interpolation_inference', - 'init_random_seed' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/generation_inference.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/generation_inference.py deleted file mode 100755 index c8e829bb1..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/generation_inference.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -from mmcv.parallel import collate, scatter - -from mmedit.core import tensor2img -from mmedit.datasets.pipelines import Compose - - -def generation_inference(model, img, img_unpaired=None): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img (str): File path of input image. - img_unpaired (str, optional): File path of the unpaired image. - If not None, perform unpaired image generation. Default: None. - - Returns: - np.ndarray: The predicted generation result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # build the data pipeline - test_pipeline = Compose(cfg.test_pipeline) - # prepare data - if img_unpaired is None: - data = dict(pair_path=img) - else: - data = dict(img_a_path=img, img_b_path=img_unpaired) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - # forward the model - with torch.no_grad(): - results = model(test_mode=True, **data) - # process generation shown mode - if img_unpaired is None: - if model.show_input: - output = np.concatenate([ - tensor2img(results['real_a'], min_max=(-1, 1)), - tensor2img(results['fake_b'], min_max=(-1, 1)), - tensor2img(results['real_b'], min_max=(-1, 1)) - ], - axis=1) - else: - output = tensor2img(results['fake_b'], min_max=(-1, 1)) - else: - if model.show_input: - output = np.concatenate([ - tensor2img(results['real_a'], min_max=(-1, 1)), - tensor2img(results['fake_b'], min_max=(-1, 1)), - tensor2img(results['real_b'], min_max=(-1, 1)), - tensor2img(results['fake_a'], min_max=(-1, 1)) - ], - axis=1) - else: - if model.test_direction == 'a2b': - output = tensor2img(results['fake_b'], min_max=(-1, 1)) - else: - output = tensor2img(results['fake_a'], min_max=(-1, 1)) - return output diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/inpainting_inference.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/inpainting_inference.py deleted file mode 100755 index f80650b74..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/inpainting_inference.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.parallel import collate, scatter - -from mmedit.datasets.pipelines import Compose - - -def inpainting_inference(model, masked_img, mask): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - masked_img (str): File path of image with mask. - mask (str): Mask file path. - - Returns: - Tensor: The predicted inpainting result. - """ - device = next(model.parameters()).device # model device - - infer_pipeline = [ - dict(type='LoadImageFromFile', key='masked_img'), - dict(type='LoadMask', mask_mode='file', mask_config=dict()), - dict(type='Pad', keys=['masked_img', 'mask'], mode='reflect'), - dict( - type='Normalize', - keys=['masked_img'], - mean=[127.5] * 3, - std=[127.5] * 3, - to_rgb=False), - dict(type='GetMaskedImage', img_name='masked_img'), - dict( - type='Collect', - keys=['masked_img', 'mask'], - meta_keys=['masked_img_path']), - dict(type='ImageToTensor', keys=['masked_img', 'mask']) - ] - - # build the data pipeline - test_pipeline = Compose(infer_pipeline) - # prepare data - data = dict(masked_img_path=masked_img, mask_path=mask) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - else: - data.pop('meta') - # forward the model - with torch.no_grad(): - result = model(test_mode=True, **data) - - return result['fake_img'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/matting_inference.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/matting_inference.py deleted file mode 100755 index 5446afe73..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/matting_inference.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch -from mmcv.parallel import collate, scatter -from mmcv.runner import load_checkpoint - -from mmedit.datasets.pipelines import Compose -from mmedit.models import build_model - - -def init_model(config, checkpoint=None, device='cuda:0'): - """Initialize a model from config file. - - Args: - config (str or :obj:`mmcv.Config`): Config file path or the config - object. - checkpoint (str, optional): Checkpoint path. If left as None, the model - will not load any weights. - device (str): Which device the model will deploy. Default: 'cuda:0'. - - Returns: - nn.Module: The constructed model. - """ - if isinstance(config, str): - config = mmcv.Config.fromfile(config) - elif not isinstance(config, mmcv.Config): - raise TypeError('config must be a filename or Config object, ' - f'but got {type(config)}') - config.model.pretrained = None - config.test_cfg.metrics = None - model = build_model(config.model, test_cfg=config.test_cfg) - if checkpoint is not None: - checkpoint = load_checkpoint(model, checkpoint) - - model.cfg = config # save the config in the model for convenience - model.to(device) - model.eval() - return model - - -def matting_inference(model, img, trimap): - """Inference image(s) with the model. - - Args: - model (nn.Module): The loaded model. - img (str): Image file path. - trimap (str): Trimap file path. - - Returns: - np.ndarray: The predicted alpha matte. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # remove alpha from test_pipeline - keys_to_remove = ['alpha', 'ori_alpha'] - for key in keys_to_remove: - for pipeline in list(cfg.test_pipeline): - if 'key' in pipeline and key == pipeline['key']: - cfg.test_pipeline.remove(pipeline) - if 'keys' in pipeline and key in pipeline['keys']: - pipeline['keys'].remove(key) - if len(pipeline['keys']) == 0: - cfg.test_pipeline.remove(pipeline) - if 'meta_keys' in pipeline and key in pipeline['meta_keys']: - pipeline['meta_keys'].remove(key) - # build the data pipeline - test_pipeline = Compose(cfg.test_pipeline) - # prepare data - data = dict(merged_path=img, trimap_path=trimap) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - # forward the model - with torch.no_grad(): - result = model(test_mode=True, **data) - - return result['pred_alpha'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/restoration_face_inference.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/restoration_face_inference.py deleted file mode 100755 index dbe12e11a..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/restoration_face_inference.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -from mmcv.parallel import collate, scatter - -from mmedit.datasets.pipelines import Compose - -try: - from facexlib.utils.face_restoration_helper import FaceRestoreHelper - has_facexlib = True -except ImportError: - has_facexlib = False - - -def restoration_face_inference(model, img, upscale_factor=1, face_size=1024): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img (str): File path of input image. - upscale_factor (int, optional): The number of times the input image - is upsampled. Default: 1. - face_size (int, optional): The size of the cropped and aligned faces. - Default: 1024. - - Returns: - Tensor: The predicted restoration result. - """ - device = next(model.parameters()).device # model device - - # build the data pipeline - if model.cfg.get('demo_pipeline', None): - test_pipeline = model.cfg.demo_pipeline - elif model.cfg.get('test_pipeline', None): - test_pipeline = model.cfg.test_pipeline - else: - test_pipeline = model.cfg.val_pipeline - - # remove gt from test_pipeline - keys_to_remove = ['gt', 'gt_path'] - for key in keys_to_remove: - for pipeline in list(test_pipeline): - if 'key' in pipeline and key == pipeline['key']: - test_pipeline.remove(pipeline) - if 'keys' in pipeline and key in pipeline['keys']: - pipeline['keys'].remove(key) - if len(pipeline['keys']) == 0: - test_pipeline.remove(pipeline) - if 'meta_keys' in pipeline and key in pipeline['meta_keys']: - pipeline['meta_keys'].remove(key) - # build the data pipeline - test_pipeline = Compose(test_pipeline) - - # face helper for detecting and aligning faces - assert has_facexlib, 'Please install FaceXLib to use the demo.' - face_helper = FaceRestoreHelper( - upscale_factor, - face_size=face_size, - crop_ratio=(1, 1), - det_model='retinaface_resnet50', - template_3points=True, - save_ext='png', - device=device) - - face_helper.read_image(img) - # get face landmarks for each face - face_helper.get_face_landmarks_5( - only_center_face=False, eye_dist_threshold=None) - # align and warp each face - face_helper.align_warp_face() - - for i, img in enumerate(face_helper.cropped_faces): - # prepare data - data = dict(lq=img.astype(np.float32)) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - - with torch.no_grad(): - output = model(test_mode=True, **data)['output'].clip_(0, 1) - - output = output.squeeze(0).permute(1, 2, 0)[:, :, [2, 1, 0]] - output = output.cpu().numpy() * 255 # (0, 255) - face_helper.add_restored_face(output) - - face_helper.get_inverse_affine(None) - restored_img = face_helper.paste_faces_to_input_image(upsample_img=None) - - return restored_img diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/restoration_inference.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/restoration_inference.py deleted file mode 100755 index 98c08abc8..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/restoration_inference.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.parallel import collate, scatter - -from mmedit.datasets.pipelines import Compose - - -def restoration_inference(model, img, ref=None): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img (str): File path of input image. - ref (str | None): File path of reference image. Default: None. - - Returns: - Tensor: The predicted restoration result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # remove gt from test_pipeline - keys_to_remove = ['gt', 'gt_path'] - for key in keys_to_remove: - for pipeline in list(cfg.test_pipeline): - if 'key' in pipeline and key == pipeline['key']: - cfg.test_pipeline.remove(pipeline) - if 'keys' in pipeline and key in pipeline['keys']: - pipeline['keys'].remove(key) - if len(pipeline['keys']) == 0: - cfg.test_pipeline.remove(pipeline) - if 'meta_keys' in pipeline and key in pipeline['meta_keys']: - pipeline['meta_keys'].remove(key) - # build the data pipeline - test_pipeline = Compose(cfg.test_pipeline) - # prepare data - if ref: # Ref-SR - data = dict(lq_path=img, ref_path=ref) - else: # SISR - data = dict(lq_path=img) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - # forward the model - with torch.no_grad(): - result = model(test_mode=True, **data) - - return result['output'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/restoration_video_inference.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/restoration_video_inference.py deleted file mode 100755 index ca6369dfc..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/restoration_video_inference.py +++ /dev/null @@ -1,129 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os.path as osp -import re -from functools import reduce - -import mmcv -import numpy as np -import torch - -from mmedit.datasets.pipelines import Compose - -VIDEO_EXTENSIONS = ('.mp4', '.mov') - - -def pad_sequence(data, window_size): - padding = window_size // 2 - - data = torch.cat([ - data[:, 1 + padding:1 + 2 * padding].flip(1), data, - data[:, -1 - 2 * padding:-1 - padding].flip(1) - ], - dim=1) - - return data - - -def restoration_video_inference(model, - img_dir, - window_size, - start_idx, - filename_tmpl, - max_seq_len=None): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img_dir (str): Directory of the input video. - window_size (int): The window size used in sliding-window framework. - This value should be set according to the settings of the network. - A value smaller than 0 means using recurrent framework. - start_idx (int): The index corresponds to the first frame in the - sequence. - filename_tmpl (str): Template for file name. - max_seq_len (int | None): The maximum sequence length that the model - processes. If the sequence length is larger than this number, - the sequence is split into multiple segments. If it is None, - the entire sequence is processed at once. - - Returns: - Tensor: The predicted restoration result. - """ - - device = next(model.parameters()).device # model device - - # build the data pipeline - if model.cfg.get('demo_pipeline', None): - test_pipeline = model.cfg.demo_pipeline - elif model.cfg.get('test_pipeline', None): - test_pipeline = model.cfg.test_pipeline - else: - test_pipeline = model.cfg.val_pipeline - - # check if the input is a video - file_extension = osp.splitext(img_dir)[1] - if file_extension in VIDEO_EXTENSIONS: - video_reader = mmcv.VideoReader(img_dir) - # load the images - data = dict(lq=[], lq_path=None, key=img_dir) - for frame in video_reader: - data['lq'].append(np.flip(frame, axis=2)) - - # remove the data loading pipeline - tmp_pipeline = [] - for pipeline in test_pipeline: - if pipeline['type'] not in [ - 'GenerateSegmentIndices', 'LoadImageFromFileList' - ]: - tmp_pipeline.append(pipeline) - test_pipeline = tmp_pipeline - else: - # the first element in the pipeline must be 'GenerateSegmentIndices' - if test_pipeline[0]['type'] != 'GenerateSegmentIndices': - raise TypeError('The first element in the pipeline must be ' - f'"GenerateSegmentIndices", but got ' - f'"{test_pipeline[0]["type"]}".') - - # specify start_idx and filename_tmpl - test_pipeline[0]['start_idx'] = start_idx - test_pipeline[0]['filename_tmpl'] = filename_tmpl - - # prepare data - sequence_length = len(glob.glob(osp.join(img_dir, '*'))) - img_dir_split = re.split(r'[\\/]', img_dir) - key = img_dir_split[-1] - lq_folder = reduce(osp.join, img_dir_split[:-1]) - data = dict( - lq_path=lq_folder, - gt_path='', - key=key, - sequence_length=sequence_length) - - # compose the pipeline - test_pipeline = Compose(test_pipeline) - data = test_pipeline(data) - data = data['lq'].unsqueeze(0) # in cpu - - # forward the model - with torch.no_grad(): - if window_size > 0: # sliding window framework - data = pad_sequence(data, window_size) - result = [] - for i in range(0, data.size(1) - 2 * (window_size // 2)): - data_i = data[:, i:i + window_size].to(device) - result.append(model(lq=data_i, test_mode=True)['output'].cpu()) - result = torch.stack(result, dim=1) - else: # recurrent framework - if max_seq_len is None: - result = model( - lq=data.to(device), test_mode=True)['output'].cpu() - else: - result = [] - for i in range(0, data.size(1), max_seq_len): - result.append( - model( - lq=data[:, i:i + max_seq_len].to(device), - test_mode=True)['output'].cpu()) - result = torch.cat(result, dim=1) - return result diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/test.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/test.py deleted file mode 100755 index 535511eee..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/test.py +++ /dev/null @@ -1,234 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile - -import mmcv -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, - data_loader, - save_image=False, - save_path=None, - iteration=None): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - save_image (bool): Whether save image. Default: False. - save_path (str): The path to save image. Default: None. - iteration (int): Iteration number. It is used for the save image name. - Default: None. - - Returns: - list: The prediction results. - """ - if save_image and save_path is None: - raise ValueError( - "When 'save_image' is True, you should also set 'save_path'.") - - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model( - test_mode=True, - save_image=save_image, - save_path=save_path, - iteration=iteration, - **data) - results.append(result) - - # get batch size - for _, v in data.items(): - if isinstance(v, torch.Tensor): - batch_size = v.size(0) - break - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, - data_loader, - tmpdir=None, - gpu_collect=False, - save_image=False, - save_path=None, - iteration=None, - empty_cache=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' - it encodes results to gpu tensors and use gpu communication for results - collection. On cpu mode it saves the results on different gpus to 'tmpdir' - and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - save_image (bool): Whether save image. Default: False. - save_path (str): The path to save image. Default: None. - iteration (int): Iteration number. It is used for the save image name. - Default: None. - empty_cache (bool): empty cache in every iteration. Default: False. - - Returns: - list: The prediction results. - """ - - if save_image and save_path is None: - raise ValueError( - "When 'save_image' is True, you should also set 'save_path'.") - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model( - test_mode=True, - save_image=save_image, - save_path=save_path, - iteration=iteration, - **data) - results.append(result) - if empty_cache: - torch.cuda.empty_cache() - if rank == 0: - # get batch size - for _, v in data.items(): - if isinstance(v, torch.Tensor): - batch_size = v.size(0) - break - for _ in range(batch_size * world_size): - prog_bar.update() - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results in cpu mode. - - It saves the results on different gpus to 'tmpdir' and collects - them by the rank 0 worker. - - Args: - result_part (list): Results to be collected - size (int): Result size. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. Default: None - - Returns: - list: Ordered results. - """ - - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # synchronizes all processes to make sure tmpdir exist - dist.barrier() - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, 'part_{}.pkl'.format(rank))) - # synchronizes all processes for loading pickle file - dist.barrier() - # collect all parts - if rank != 0: - return None - - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, 'part_{}.pkl'.format(i)) - part_list.append(mmcv.load(part_file)) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results in gpu mode. - - It encodes results to gpu tensors and use gpu communication for results - collection. - - Args: - result_part (list): Results to be collected - size (int): Result size. - - Returns: - list: Ordered results. - """ - - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank != 0: - return None - - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_list.append(pickle.loads(recv[:shape[0]].cpu().numpy().tobytes())) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/train.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/train.py deleted file mode 100755 index 359943ca8..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/train.py +++ /dev/null @@ -1,361 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -import random -import warnings - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.parallel import MMDataParallel -from mmcv.runner import HOOKS, IterBasedRunner, get_dist_info -from mmcv.utils import build_from_cfg - -from mmedit.core import DistEvalIterHook, EvalIterHook, build_optimizers -from mmedit.core.distributed_wrapper import DistributedDataParallelWrapper -from mmedit.datasets.builder import build_dataloader, build_dataset -from mmedit.utils import get_root_logger - - -def init_random_seed(seed=None, device='cuda'): - """Initialize random seed. - If the seed is not set, the seed will be automatically randomized, - and then broadcast to all processes to prevent some potential bugs. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is not None: - return seed - - # Make sure all ranks share the same random seed to prevent - # some potential bugs. Please refer to - # https://github.com/open-mmlab/mmdetection/issues/6339 - rank, world_size = get_dist_info() - seed = np.random.randint(2**31) - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() - - -def set_random_seed(seed, deterministic=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - """ - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - - -def train_model(model, - dataset, - cfg, - distributed=False, - validate=False, - timestamp=None, - meta=None): - """Train model entry function. - - Args: - model (nn.Module): The model to be trained. - dataset (:obj:`Dataset`): Train dataset. - cfg (dict): The config dict for training. - distributed (bool): Whether to use distributed training. - Default: False. - validate (bool): Whether to do evaluation. Default: False. - timestamp (str | None): Local time for runner. Default: None. - meta (dict | None): Meta dict to record some important information. - Default: None - """ - logger = get_root_logger(log_level=cfg.log_level) - - # start training - if distributed: - _dist_train( - model, - dataset, - cfg, - validate=validate, - logger=logger, - timestamp=timestamp, - meta=meta) - else: - _non_dist_train( - model, - dataset, - cfg, - validate=validate, - logger=logger, - timestamp=timestamp, - meta=meta) - - -def _dist_train(model, - dataset, - cfg, - validate=False, - logger=None, - timestamp=None, - meta=None): - """Distributed training function. - - Args: - model (nn.Module): The model to be trained. - dataset (:obj:`Dataset`): Train dataset. - cfg (dict): The config dict for training. - validate (bool): Whether to do evaluation. Default: False. - logger (logging.Logger | None): Logger for training. Default: None. - timestamp (str | None): Local time for runner. Default: None. - meta (dict | None): Meta dict to record some important information. - Default: None. - """ - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - - # step 1: give default values and override (if exist) from cfg.data - loader_cfg = { - **dict(seed=cfg.get('seed'), drop_last=False, dist=True), - **({} if torch.__version__ != 'parrots' else dict( - prefetch_num=2, - pin_memory=False, - )), - **dict((k, cfg.data[k]) for k in [ - 'samples_per_gpu', - 'workers_per_gpu', - 'shuffle', - 'seed', - 'drop_last', - 'prefetch_num', - 'pin_memory', - ] if k in cfg.data) - } - - # step 2: cfg.data.train_dataloader has highest priority - train_loader_cfg = dict(loader_cfg, **cfg.data.get('train_dataloader', {})) - - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus - find_unused_parameters = cfg.get('find_unused_parameters', False) - model = DistributedDataParallelWrapper( - model, - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - - # build runner - optimizer = build_optimizers(model, cfg.optimizers) - runner = IterBasedRunner( - model, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta) - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register hooks - runner.register_training_hooks( - cfg.lr_config, - checkpoint_config=cfg.checkpoint_config, - log_config=cfg.log_config) - - # visual hook - if cfg.get('visual_config', None) is not None: - cfg.visual_config['output_dir'] = os.path.join( - cfg.work_dir, cfg.visual_config['output_dir']) - runner.register_hook(mmcv.build_from_cfg(cfg.visual_config, HOOKS)) - - # evaluation hook - if validate and cfg.get('evaluation', None) is not None: - dataset = build_dataset(cfg.data.val) - - if ('val_samples_per_gpu' in cfg.data - or 'val_workers_per_gpu' in cfg.data): - warnings.warn('"val_samples_per_gpu/val_workers_per_gpu" have ' - 'been deprecated. Please use ' - '"val_dataloader=dict(samples_per_gpu=1)" instead. ' - 'Details see ' - 'https://github.com/open-mmlab/mmediting/pull/201') - - val_loader_cfg = { - **loader_cfg, - **dict(shuffle=False, drop_last=False), - **dict((newk, cfg.data[oldk]) for oldk, newk in [ - ('val_samples_per_gpu', 'samples_per_gpu'), - ('val_workers_per_gpu', 'workers_per_gpu'), - ] if oldk in cfg.data), - **cfg.data.get('val_dataloader', {}) - } - - data_loader = build_dataloader(dataset, **val_loader_cfg) - save_path = osp.join(cfg.work_dir, 'val_visuals') - runner.register_hook( - DistEvalIterHook( - data_loader, save_path=save_path, **cfg.evaluation), - priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow, cfg.total_iters) - - -def _non_dist_train(model, - dataset, - cfg, - validate=False, - logger=None, - timestamp=None, - meta=None): - """Non-Distributed training function. - - Args: - model (nn.Module): The model to be trained. - dataset (:obj:`Dataset`): Train dataset. - cfg (dict): The config dict for training. - validate (bool): Whether to do evaluation. Default: False. - logger (logging.Logger | None): Logger for training. Default: None. - timestamp (str | None): Local time for runner. Default: None. - meta (dict | None): Meta dict to record some important information. - Default: None. - """ - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - - # step 1: give default values and override (if exist) from cfg.data - loader_cfg = { - **dict( - seed=cfg.get('seed'), - drop_last=False, - dist=False, - num_gpus=cfg.gpus), - **({} if torch.__version__ != 'parrots' else dict( - prefetch_num=2, - pin_memory=False, - )), - **dict((k, cfg.data[k]) for k in [ - 'samples_per_gpu', - 'workers_per_gpu', - 'shuffle', - 'seed', - 'drop_last', - 'prefetch_num', - 'pin_memory', - ] if k in cfg.data) - } - - # step 2: cfg.data.train_dataloader has highest priority - train_loader_cfg = dict(loader_cfg, **cfg.data.get('train_dataloader', {})) - - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus/cpus - model = MMDataParallel(model, device_ids=range(cfg.gpus)) - - # build runner - optimizer = build_optimizers(model, cfg.optimizers) - runner = IterBasedRunner( - model, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta) - - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register hooks - runner.register_training_hooks( - cfg.lr_config, - checkpoint_config=cfg.checkpoint_config, - log_config=cfg.log_config) - - # visual hook - if cfg.get('visual_config', None) is not None: - cfg.visual_config['output_dir'] = os.path.join( - cfg.work_dir, cfg.visual_config['output_dir']) - runner.register_hook(mmcv.build_from_cfg(cfg.visual_config, HOOKS)) - - # evaluation hook - if validate and cfg.get('evaluation', None) is not None: - dataset = build_dataset(cfg.data.val) - - if ('val_samples_per_gpu' in cfg.data - or 'val_workers_per_gpu' in cfg.data): - warnings.warn('"val_samples_per_gpu/val_workers_per_gpu" have ' - 'been deprecated. Please use ' - '"val_dataloader=dict(samples_per_gpu=1)" instead. ' - 'Details see ' - 'https://github.com/open-mmlab/mmediting/pull/201') - - val_loader_cfg = { - **loader_cfg, - **dict(shuffle=False, drop_last=False), - **dict((newk, cfg.data[oldk]) for oldk, newk in [ - ('val_samples_per_gpu', 'samples_per_gpu'), - ('val_workers_per_gpu', 'workers_per_gpu'), - ] if oldk in cfg.data), - **cfg.data.get('val_dataloader', {}) - } - - data_loader = build_dataloader(dataset, **val_loader_cfg) - save_path = osp.join(cfg.work_dir, 'val_visuals') - runner.register_hook( - EvalIterHook(data_loader, save_path=save_path, **cfg.evaluation), - priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow, cfg.total_iters) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/video_interpolation_inference.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/video_interpolation_inference.py deleted file mode 100755 index eaabb4308..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/apis/video_interpolation_inference.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -import os -import os.path as osp - -import cv2 -import mmcv -import numpy as np -import torch -from mmcv.fileio import FileClient -from mmcv.parallel import collate - -from mmedit.datasets.pipelines import Compose - -VIDEO_EXTENSIONS = ('.mp4', '.mov', '.avi') -FILE_CLIENT = FileClient('disk') - - -def read_image(filepath): - """Read image from file. - - Args: - filepath (str): File path. - - Returns: - image (np.array): Image. - """ - img_bytes = FILE_CLIENT.get(filepath) - image = mmcv.imfrombytes( - img_bytes, flag='color', channel_order='rgb', backend='pillow') - return image - - -def read_frames(source, start_index, num_frames, from_video, end_index): - """Read frames from file or video. - - Args: - source (list | mmcv.VideoReader): Source of frames. - start_index (int): Start index of frames. - num_frames (int): frames number to be read. - from_video (bool): Weather read frames from video. - end_index (int): The end index of frames. - - Returns: - images (np.array): Images. - """ - images = [] - last_index = min(start_index + num_frames, end_index) - # read frames from video - if from_video: - for index in range(start_index, last_index): - if index >= source.frame_cnt: - break - images.append(np.flip(source.get_frame(index), axis=2)) - else: - files = source[start_index:last_index] - images = [read_image(f) for f in files] - return images - - -def video_interpolation_inference(model, - input_dir, - output_dir, - start_idx=0, - end_idx=None, - batch_size=4, - fps_multiplier=0, - fps=0, - filename_tmpl='{:08d}.png'): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - input_dir (str): Directory of the input video. - output_dir (str): Directory of the output video. - start_idx (int): The index corresponding to the first frame in the - sequence. Default: 0 - end_idx (int | None): The index corresponding to the last interpolated - frame in the sequence. If it is None, interpolate to the last - frame of video or sequence. Default: None - batch_size (int): Batch size. Default: 4 - fps_multiplier (float): multiply the fps based on the input video. - Default: 0. - fps (float): frame rate of the output video. Default: 0. - filename_tmpl (str): template of the file names. Default: '{:08d}.png' - - Returns: - output (list[numpy.array]): The predicted interpolation result. - It is an image sequence. - input_fps (float): The fps of input video. If the input is an image - sequence, input_fps=0.0 - """ - - device = next(model.parameters()).device # model device - - # build the data pipeline - if model.cfg.get('demo_pipeline', None): - test_pipeline = model.cfg.demo_pipeline - elif model.cfg.get('test_pipeline', None): - test_pipeline = model.cfg.test_pipeline - else: - test_pipeline = model.cfg.val_pipeline - - # remove the data loading pipeline - tmp_pipeline = [] - for pipeline in test_pipeline: - if pipeline['type'] not in [ - 'GenerateSegmentIndices', 'LoadImageFromFileList', - 'LoadImageFromFile' - ]: - tmp_pipeline.append(pipeline) - test_pipeline = tmp_pipeline - - # compose the pipeline - test_pipeline = Compose(test_pipeline) - - # check if the input is a video - input_file_extension = os.path.splitext(input_dir)[1] - if input_file_extension in VIDEO_EXTENSIONS: - source = mmcv.VideoReader(input_dir) - input_fps = source.fps - length = source.frame_cnt - from_video = True - h, w = source.height, source.width - if fps_multiplier: - assert fps_multiplier > 0, '`fps_multiplier` cannot be negative' - output_fps = fps_multiplier * input_fps - else: - output_fps = fps if fps > 0 else input_fps * 2 - else: - files = os.listdir(input_dir) - files = [osp.join(input_dir, f) for f in files] - files.sort() - source = files - length = files.__len__() - from_video = False - example_frame = read_image(files[0]) - h, w = example_frame.shape[:2] - output_fps = fps - - # check if the output is a video - output_file_extension = os.path.splitext(output_dir)[1] - if output_file_extension in VIDEO_EXTENSIONS: - fourcc = cv2.VideoWriter_fourcc(*'mp4v') - target = cv2.VideoWriter(output_dir, fourcc, output_fps, (w, h)) - to_video = True - else: - to_video = False - - end_idx = min(end_idx, length) if end_idx is not None else length - - # calculate step args - step_size = model.step_frames * batch_size - lenth_per_step = model.required_frames + model.step_frames * ( - batch_size - 1) - repeat_frame = model.required_frames - model.step_frames - - prog_bar = mmcv.ProgressBar( - math.ceil( - (end_idx + step_size - lenth_per_step - start_idx) / step_size)) - output_index = start_idx - for start_index in range(start_idx, end_idx, step_size): - images = read_frames( - source, start_index, lenth_per_step, from_video, end_index=end_idx) - - # data prepare - data = dict(inputs=images, inputs_path=None, key=input_dir) - data = [test_pipeline(data)] - data = collate(data, samples_per_gpu=1)['inputs'] - # data.shape: [1, t, c, h, w] - - # forward the model - data = model.split_frames(data) - input_tensors = data.clone().detach() - with torch.no_grad(): - output = model(data.to(device), test_mode=True)['output'] - if len(output.shape) == 4: - output = output.unsqueeze(1) - output_tensors = output.cpu() - if len(output_tensors.shape) == 4: - output_tensors = output_tensors.unsqueeze(1) - result = model.merge_frames(input_tensors, output_tensors) - if not start_idx == start_index: - result = result[repeat_frame:] - prog_bar.update() - - # save frames - if to_video: - for frame in result: - target.write(frame) - else: - for frame in result: - save_path = osp.join(output_dir, - filename_tmpl.format(output_index)) - mmcv.imwrite(frame, save_path) - output_index += 1 - - if start_index + lenth_per_step >= end_idx: - break - - print() - print(f'Output dir: {output_dir}') - if to_video: - target.release() diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/__init__.py deleted file mode 100755 index 2b24ce348..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .evaluation import (DistEvalIterHook, EvalIterHook, L1Evaluation, mae, - mse, psnr, reorder_image, sad, ssim) -from .hooks import VisualizationHook -from .misc import tensor2img -from .optimizer import build_optimizers -from .scheduler import LinearLrUpdaterHook, ReduceLrUpdaterHook - -__all__ = [ - 'build_optimizers', 'tensor2img', 'EvalIterHook', 'DistEvalIterHook', - 'mse', 'psnr', 'reorder_image', 'sad', 'ssim', 'LinearLrUpdaterHook', - 'VisualizationHook', 'L1Evaluation', 'ReduceLrUpdaterHook', 'mae' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/distributed_wrapper.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/distributed_wrapper.py deleted file mode 100755 index 660f41f3f..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/distributed_wrapper.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.parallel import MODULE_WRAPPERS, MMDistributedDataParallel -from mmcv.parallel.scatter_gather import scatter_kwargs -from torch.cuda._utils import _get_device_index - - -@MODULE_WRAPPERS.register_module() -class DistributedDataParallelWrapper(nn.Module): - """A DistributedDataParallel wrapper for models in MMediting. - - In MMedting, there is a need to wrap different modules in the models - with separate DistributedDataParallel. Otherwise, it will cause - errors for GAN training. - More specific, the GAN model, usually has two sub-modules: - generator and discriminator. If we wrap both of them in one - standard DistributedDataParallel, it will cause errors during training, - because when we update the parameters of the generator (or discriminator), - the parameters of the discriminator (or generator) is not updated, which is - not allowed for DistributedDataParallel. - So we design this wrapper to separately wrap DistributedDataParallel - for generator and discriminator. - - In this wrapper, we perform two operations: - 1. Wrap the modules in the models with separate MMDistributedDataParallel. - Note that only modules with parameters will be wrapped. - 2. Do scatter operation for 'forward', 'train_step' and 'val_step'. - - Note that the arguments of this wrapper is the same as those in - `torch.nn.parallel.distributed.DistributedDataParallel`. - - Args: - module (nn.Module): Module that needs to be wrapped. - device_ids (list[int | `torch.device`]): Same as that in - `torch.nn.parallel.distributed.DistributedDataParallel`. - dim (int, optional): Same as that in the official scatter function in - pytorch. Defaults to 0. - broadcast_buffers (bool): Same as that in - `torch.nn.parallel.distributed.DistributedDataParallel`. - Defaults to False. - find_unused_parameters (bool, optional): Same as that in - `torch.nn.parallel.distributed.DistributedDataParallel`. - Traverse the autograd graph of all tensors contained in returned - value of the wrapped module’s forward function. Defaults to False. - kwargs (dict): Other arguments used in - `torch.nn.parallel.distributed.DistributedDataParallel`. - """ - - def __init__(self, - module, - device_ids, - dim=0, - broadcast_buffers=False, - find_unused_parameters=False, - **kwargs): - super().__init__() - assert len(device_ids) == 1, ( - 'Currently, DistributedDataParallelWrapper only supports one' - 'single CUDA device for each process.' - f'The length of device_ids must be 1, but got {len(device_ids)}.') - self.module = module - self.dim = dim - self.to_ddp( - device_ids=device_ids, - dim=dim, - broadcast_buffers=broadcast_buffers, - find_unused_parameters=find_unused_parameters, - **kwargs) - self.output_device = _get_device_index(device_ids[0], True) - - def to_ddp(self, device_ids, dim, broadcast_buffers, - find_unused_parameters, **kwargs): - """Wrap models with separate MMDistributedDataParallel. - - It only wraps the modules with parameters. - """ - for name, module in self.module._modules.items(): - if next(module.parameters(), None) is None: - module = module.cuda() - elif all(not p.requires_grad for p in module.parameters()): - module = module.cuda() - else: - module = MMDistributedDataParallel( - module.cuda(), - device_ids=device_ids, - dim=dim, - broadcast_buffers=broadcast_buffers, - find_unused_parameters=find_unused_parameters, - **kwargs) - self.module._modules[name] = module - - def scatter(self, inputs, kwargs, device_ids): - """Scatter function. - - Args: - inputs (Tensor): Input Tensor. - kwargs (dict): Args for - ``mmcv.parallel.scatter_gather.scatter_kwargs``. - device_ids (int): Device id. - """ - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - """Forward function. - - Args: - inputs (tuple): Input data. - kwargs (dict): Args for - ``mmcv.parallel.scatter_gather.scatter_kwargs``. - """ - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - """Train step function. - - Args: - inputs (Tensor): Input Tensor. - kwargs (dict): Args for - ``mmcv.parallel.scatter_gather.scatter_kwargs``. - """ - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - """Validation step function. - - Args: - inputs (tuple): Input data. - kwargs (dict): Args for ``scatter_kwargs``. - """ - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/__init__.py deleted file mode 100755 index 5294618cf..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .eval_hooks import DistEvalIterHook, EvalIterHook -from .metrics import (L1Evaluation, connectivity, gradient_error, mae, mse, - niqe, psnr, reorder_image, sad, ssim) - -__all__ = [ - 'mse', 'sad', 'psnr', 'reorder_image', 'ssim', 'EvalIterHook', - 'DistEvalIterHook', 'L1Evaluation', 'gradient_error', 'connectivity', - 'niqe', 'mae' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/eval_hooks.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/eval_hooks.py deleted file mode 100755 index bda0f846c..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/eval_hooks.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.runner import Hook -from torch.utils.data import DataLoader - - -class EvalIterHook(Hook): - """Non-Distributed evaluation hook for iteration-based runner. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader. - interval (int): Evaluation interval. Default: 1. - eval_kwargs (dict): Other eval kwargs. It contains: - save_image (bool): Whether to save image. - save_path (str): The path to save image. - """ - - def __init__(self, dataloader, interval=1, **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError('dataloader must be a pytorch DataLoader, ' - f'but got { type(dataloader)}') - self.dataloader = dataloader - self.interval = interval - self.eval_kwargs = eval_kwargs - self.save_image = self.eval_kwargs.pop('save_image', False) - self.save_path = self.eval_kwargs.pop('save_path', None) - - def after_train_iter(self, runner): - """The behavior after each train iteration. - - Args: - runner (``mmcv.runner.BaseRunner``): The runner. - """ - if not self.every_n_iters(runner, self.interval): - return - runner.log_buffer.clear() - from mmedit.apis import single_gpu_test - results = single_gpu_test( - runner.model, - self.dataloader, - save_image=self.save_image, - save_path=self.save_path, - iteration=runner.iter) - self.evaluate(runner, results) - - def evaluate(self, runner, results): - """Evaluation function. - - Args: - runner (``mmcv.runner.BaseRunner``): The runner. - results (dict): Model forward results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - # call `after_val_epoch` after evaluation. - # This is a hack. - # Because epoch does not naturally exist In IterBasedRunner, - # thus we consider the end of an evluation as the end of an epoch. - # With this hack , we can support epoch based hooks. - if 'iter' in runner.__class__.__name__.lower(): - runner.call_hook('after_val_epoch') - - -class DistEvalIterHook(EvalIterHook): - """Distributed evaluation hook. - - Args: - dataloader (DataLoader): A PyTorch dataloader. - interval (int): Evaluation interval. Default: 1. - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - eval_kwargs (dict): Other eval kwargs. It may contain: - save_image (bool): Whether save image. - save_path (str): The path to save image. - """ - - def __init__(self, - dataloader, - interval=1, - gpu_collect=False, - **eval_kwargs): - super().__init__(dataloader, interval, **eval_kwargs) - self.gpu_collect = gpu_collect - - def after_train_iter(self, runner): - """The behavior after each train iteration. - - Args: - runner (``mmcv.runner.BaseRunner``): The runner. - """ - if not self.every_n_iters(runner, self.interval): - return - runner.log_buffer.clear() - from mmedit.apis import multi_gpu_test - results = multi_gpu_test( - runner.model, - self.dataloader, - tmpdir=osp.join(runner.work_dir, '.eval_hook'), - gpu_collect=self.gpu_collect, - save_image=self.save_image, - save_path=self.save_path, - iteration=runner.iter) - if runner.rank == 0: - print('\n') - self.evaluate(runner, results) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/metric_utils.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/metric_utils.py deleted file mode 100755 index ec6017678..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/metric_utils.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def gaussian(x, sigma): - """Gaussian function. - - Args: - x (array_like): The independent variable. - sigma (float): Standard deviation of the gaussian function. - - Return: - ndarray or scalar: Gaussian value of `x`. - """ - return np.exp(-x**2 / (2 * sigma**2)) / (sigma * np.sqrt(2 * np.pi)) - - -def dgaussian(x, sigma): - """Gradient of gaussian. - - Args: - x (array_like): The independent variable. - sigma (float): Standard deviation of the gaussian function. - - Return: - ndarray or scalar: Gradient of gaussian of `x`. - """ - return -x * gaussian(x, sigma) / sigma**2 - - -def gauss_filter(sigma, epsilon=1e-2): - """Gradient of gaussian. - - Args: - sigma (float): Standard deviation of the gaussian kernel. - epsilon (float): Small value used when calculating kernel size. - Default: 1e-2. - - Return: - tuple[ndarray]: Gaussian filter along x and y axis. - """ - half_size = np.ceil( - sigma * np.sqrt(-2 * np.log(np.sqrt(2 * np.pi) * sigma * epsilon))) - size = int(2 * half_size + 1) - - # create filter in x axis - filter_x = np.zeros((size, size)) - for i in range(size): - for j in range(size): - filter_x[i, j] = gaussian(i - half_size, sigma) * dgaussian( - j - half_size, sigma) - - # normalize filter - norm = np.sqrt((filter_x**2).sum()) - filter_x = filter_x / norm - filter_y = np.transpose(filter_x) - - return filter_x, filter_y - - -def gauss_gradient(img, sigma): - """Gaussian gradient. - - From https://www.mathworks.com/matlabcentral/mlc-downloads/downloads/ - submissions/8060/versions/2/previews/gaussgradient/gaussgradient.m/ - index.html - - Args: - img (ndarray): Input image. - sigma (float): Standard deviation of the gaussian kernel. - - Return: - ndarray: Gaussian gradient of input `img`. - """ - filter_x, filter_y = gauss_filter(sigma) - img_filtered_x = cv2.filter2D( - img, -1, filter_x, borderType=cv2.BORDER_REPLICATE) - img_filtered_y = cv2.filter2D( - img, -1, filter_y, borderType=cv2.BORDER_REPLICATE) - return np.sqrt(img_filtered_x**2 + img_filtered_y**2) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/metrics.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/metrics.py deleted file mode 100755 index 9e37dbe1a..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/metrics.py +++ /dev/null @@ -1,572 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import cv2 -import mmcv -import numpy as np -from scipy.ndimage import convolve -from scipy.special import gamma - -from mmedit.datasets.pipelines.matlab_like_resize import MATLABLikeResize -from .metric_utils import gauss_gradient - - -def sad(alpha, trimap, pred_alpha): - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - assert (pred_alpha[trimap == 0] == 0).all() - assert (pred_alpha[trimap == 255] == 255).all() - alpha = alpha.astype(np.float64) / 255 - pred_alpha = pred_alpha.astype(np.float64) / 255 - sad_result = np.abs(pred_alpha - alpha).sum() / 1000 - return sad_result - - -def mse(alpha, trimap, pred_alpha): - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - assert (pred_alpha[trimap == 0] == 0).all() - assert (pred_alpha[trimap == 255] == 255).all() - alpha = alpha.astype(np.float64) / 255 - pred_alpha = pred_alpha.astype(np.float64) / 255 - weight_sum = (trimap == 128).sum() - if weight_sum != 0: - mse_result = ((pred_alpha - alpha)**2).sum() / weight_sum - else: - mse_result = 0 - return mse_result - - -def gradient_error(alpha, trimap, pred_alpha, sigma=1.4): - """Gradient error for evaluating alpha matte prediction. - - Args: - alpha (ndarray): Ground-truth alpha matte. - trimap (ndarray): Input trimap with its value in {0, 128, 255}. - pred_alpha (ndarray): Predicted alpha matte. - sigma (float): Standard deviation of the gaussian kernel. Default: 1.4. - """ - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - if not ((pred_alpha[trimap == 0] == 0).all() and - (pred_alpha[trimap == 255] == 255).all()): - raise ValueError( - 'pred_alpha should be masked by trimap before evaluation') - alpha = alpha.astype(np.float64) - pred_alpha = pred_alpha.astype(np.float64) - alpha_normed = np.zeros_like(alpha) - pred_alpha_normed = np.zeros_like(pred_alpha) - cv2.normalize(alpha, alpha_normed, 1., 0., cv2.NORM_MINMAX) - cv2.normalize(pred_alpha, pred_alpha_normed, 1., 0., cv2.NORM_MINMAX) - - alpha_grad = gauss_gradient(alpha_normed, sigma).astype(np.float32) - pred_alpha_grad = gauss_gradient(pred_alpha_normed, - sigma).astype(np.float32) - - grad_loss = ((alpha_grad - pred_alpha_grad)**2 * (trimap == 128)).sum() - # same as SAD, divide by 1000 to reduce the magnitude of the result - return grad_loss / 1000 - - -def connectivity(alpha, trimap, pred_alpha, step=0.1): - """Connectivity error for evaluating alpha matte prediction. - - Args: - alpha (ndarray): Ground-truth alpha matte with shape (height, width). - Value range of alpha is [0, 255]. - trimap (ndarray): Input trimap with shape (height, width). Elements - in trimap are one of {0, 128, 255}. - pred_alpha (ndarray): Predicted alpha matte with shape (height, width). - Value range of pred_alpha is [0, 255]. - step (float): Step of threshold when computing intersection between - `alpha` and `pred_alpha`. - """ - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - if not ((pred_alpha[trimap == 0] == 0).all() and - (pred_alpha[trimap == 255] == 255).all()): - raise ValueError( - 'pred_alpha should be masked by trimap before evaluation') - alpha = alpha.astype(np.float32) / 255 - pred_alpha = pred_alpha.astype(np.float32) / 255 - - thresh_steps = np.arange(0, 1 + step, step) - round_down_map = -np.ones_like(alpha) - for i in range(1, len(thresh_steps)): - alpha_thresh = alpha >= thresh_steps[i] - pred_alpha_thresh = pred_alpha >= thresh_steps[i] - intersection = (alpha_thresh & pred_alpha_thresh).astype(np.uint8) - - # connected components - _, output, stats, _ = cv2.connectedComponentsWithStats( - intersection, connectivity=4) - # start from 1 in dim 0 to exclude background - size = stats[1:, -1] - - # largest connected component of the intersection - omega = np.zeros_like(alpha) - if len(size) != 0: - max_id = np.argmax(size) - # plus one to include background - omega[output == max_id + 1] = 1 - - mask = (round_down_map == -1) & (omega == 0) - round_down_map[mask] = thresh_steps[i - 1] - round_down_map[round_down_map == -1] = 1 - - alpha_diff = alpha - round_down_map - pred_alpha_diff = pred_alpha - round_down_map - # only calculate difference larger than or equal to 0.15 - alpha_phi = 1 - alpha_diff * (alpha_diff >= 0.15) - pred_alpha_phi = 1 - pred_alpha_diff * (pred_alpha_diff >= 0.15) - - connectivity_error = np.sum( - np.abs(alpha_phi - pred_alpha_phi) * (trimap == 128)) - # same as SAD, divide by 1000 to reduce the magnitude of the result - return connectivity_error / 1000 - - -def reorder_image(img, input_order='HWC'): - """Reorder images to 'HWC' order. - - If the input_order is (h, w), return (h, w, 1); - If the input_order is (c, h, w), return (h, w, c); - If the input_order is (h, w, c), return as it is. - - Args: - img (ndarray): Input image. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - If the input image shape is (h, w), input_order will not have - effects. Default: 'HWC'. - - Returns: - ndarray: reordered image. - """ - - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - if len(img.shape) == 2: - img = img[..., None] - return img - if input_order == 'CHW': - img = img.transpose(1, 2, 0) - return img - - -def psnr(img1, img2, crop_border=0, input_order='HWC', convert_to=None): - """Calculate PSNR (Peak Signal-to-Noise Ratio). - - Ref: https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio - - Args: - img1 (ndarray): Images with range [0, 255]. - img2 (ndarray): Images with range [0, 255]. - crop_border (int): Cropped pixels in each edges of an image. These - pixels are not involved in the PSNR calculation. Default: 0. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether to convert the images to other color models. - If None, the images are not altered. When computing for 'Y', - the images are assumed to be in BGR order. Options are 'Y' and - None. Default: None. - - Returns: - float: psnr result. - """ - - assert img1.shape == img2.shape, ( - f'Image shapes are different: {img1.shape}, {img2.shape}.') - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - img1 = reorder_image(img1, input_order=input_order) - img2 = reorder_image(img2, input_order=input_order) - - img1, img2 = img1.astype(np.float32), img2.astype(np.float32) - if isinstance(convert_to, str) and convert_to.lower() == 'y': - img1 = mmcv.bgr2ycbcr(img1 / 255., y_only=True) * 255. - img2 = mmcv.bgr2ycbcr(img2 / 255., y_only=True) * 255. - elif convert_to is not None: - raise ValueError('Wrong color model. Supported values are ' - '"Y" and None.') - - if crop_border != 0: - img1 = img1[crop_border:-crop_border, crop_border:-crop_border, None] - img2 = img2[crop_border:-crop_border, crop_border:-crop_border, None] - - mse_value = np.mean((img1 - img2)**2) - if mse_value == 0: - return float('inf') - return 20. * np.log10(255. / np.sqrt(mse_value)) - - -def mae(img1, img2, crop_border=0, input_order='HWC', convert_to=None): - """Calculate mean average error for evaluation. - - Args: - img1 (ndarray): Images with range [0, 255]. - img2 (ndarray): Images with range [0, 255]. - crop_border (int): Cropped pixels in each edges of an image. These - pixels are not involved in the PSNR calculation. Default: 0. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether to convert the images to other color models. - If None, the images are not altered. Options are 'RGB2Y', 'BGR2Y' - and None. Default: None. - - Returns: - float: mae result. - """ - - assert img1.shape == img2.shape, ( - f'Image shapes are different: {img1.shape}, {img2.shape}.') - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - img1 = reorder_image(img1, input_order=input_order) - img2 = reorder_image(img2, input_order=input_order) - - img1, img2 = img1.astype(np.float32), img2.astype(np.float32) - img1, img2 = img1 / 255., img2 / 255. - if isinstance(convert_to, str) and convert_to.lower() == 'rgb2y': - img1 = mmcv.rgb2ycbcr(img1, y_only=True) - img2 = mmcv.rgb2ycbcr(img2, y_only=True) - elif isinstance(convert_to, str) and convert_to.lower() == 'bgr2y': - img1 = mmcv.bgr2ycbcr(img1, y_only=True) - img2 = mmcv.bgr2ycbcr(img2, y_only=True) - elif convert_to is not None: - raise ValueError('Wrong color model. Supported values are ' - '"RGB2Y", "BGR2Y" and None.') - - if crop_border != 0: - img1 = img1[crop_border:-crop_border, crop_border:-crop_border, None] - img2 = img2[crop_border:-crop_border, crop_border:-crop_border, None] - - l1_value = np.mean(np.abs(img1 - img2)) - - return l1_value - - -def _ssim(img1, img2): - """Calculate SSIM (structural similarity) for one channel images. - - It is called by func:`calculate_ssim`. - - Args: - img1, img2 (ndarray): Images with range [0, 255] with order 'HWC'. - - Returns: - float: ssim result. - """ - - C1 = (0.01 * 255)**2 - C2 = (0.03 * 255)**2 - - img1 = img1.astype(np.float64) - img2 = img2.astype(np.float64) - kernel = cv2.getGaussianKernel(11, 1.5) - window = np.outer(kernel, kernel.transpose()) - - mu1 = cv2.filter2D(img1, -1, window)[5:-5, 5:-5] - mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5] - mu1_sq = mu1**2 - mu2_sq = mu2**2 - mu1_mu2 = mu1 * mu2 - sigma1_sq = cv2.filter2D(img1**2, -1, window)[5:-5, 5:-5] - mu1_sq - sigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sq - sigma12 = cv2.filter2D(img1 * img2, -1, window)[5:-5, 5:-5] - mu1_mu2 - - ssim_map = ((2 * mu1_mu2 + C1) * - (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) * - (sigma1_sq + sigma2_sq + C2)) - return ssim_map.mean() - - -def ssim(img1, img2, crop_border=0, input_order='HWC', convert_to=None): - """Calculate SSIM (structural similarity). - - Ref: - Image quality assessment: From error visibility to structural similarity - - The results are the same as that of the official released MATLAB code in - https://ece.uwaterloo.ca/~z70wang/research/ssim/. - - For three-channel images, SSIM is calculated for each channel and then - averaged. - - Args: - img1 (ndarray): Images with range [0, 255]. - img2 (ndarray): Images with range [0, 255]. - crop_border (int): Cropped pixels in each edges of an image. These - pixels are not involved in the SSIM calculation. Default: 0. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether to convert the images to other color models. - If None, the images are not altered. When computing for 'Y', - the images are assumed to be in BGR order. Options are 'Y' and - None. Default: None. - - Returns: - float: ssim result. - """ - - assert img1.shape == img2.shape, ( - f'Image shapes are different: {img1.shape}, {img2.shape}.') - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - img1 = reorder_image(img1, input_order=input_order) - img2 = reorder_image(img2, input_order=input_order) - - if isinstance(convert_to, str) and convert_to.lower() == 'y': - img1, img2 = img1.astype(np.float32), img2.astype(np.float32) - img1 = mmcv.bgr2ycbcr(img1 / 255., y_only=True) * 255. - img2 = mmcv.bgr2ycbcr(img2 / 255., y_only=True) * 255. - img1 = np.expand_dims(img1, axis=2) - img2 = np.expand_dims(img2, axis=2) - elif convert_to is not None: - raise ValueError('Wrong color model. Supported values are ' - '"Y" and None') - - if crop_border != 0: - img1 = img1[crop_border:-crop_border, crop_border:-crop_border, None] - img2 = img2[crop_border:-crop_border, crop_border:-crop_border, None] - - ssims = [] - for i in range(img1.shape[2]): - ssims.append(_ssim(img1[..., i], img2[..., i])) - return np.array(ssims).mean() - - -class L1Evaluation: - """L1 evaluation metric. - - Args: - data_dict (dict): Must contain keys of 'gt_img' and 'fake_res'. If - 'mask' is given, the results will be computed with mask as weight. - """ - - def __call__(self, data_dict): - gt = data_dict['gt_img'] - if 'fake_img' in data_dict: - pred = data_dict.get('fake_img') - else: - pred = data_dict.get('fake_res') - mask = data_dict.get('mask', None) - - from mmedit.models.losses.pixelwise_loss import l1_loss - l1_error = l1_loss(pred, gt, weight=mask, reduction='mean') - - return l1_error - - -def estimate_aggd_param(block): - """Estimate AGGD (Asymmetric Generalized Gaussian Distribution) parameters. - - Args: - block (ndarray): 2D Image block. - - Returns: - tuple: alpha (float), beta_l (float) and beta_r (float) for the AGGD - distribution (Estimating the parames in Equation 7 in the paper). - """ - block = block.flatten() - gam = np.arange(0.2, 10.001, 0.001) # len = 9801 - gam_reciprocal = np.reciprocal(gam) - r_gam = np.square(gamma(gam_reciprocal * 2)) / ( - gamma(gam_reciprocal) * gamma(gam_reciprocal * 3)) - - left_std = np.sqrt(np.mean(block[block < 0]**2)) - right_std = np.sqrt(np.mean(block[block > 0]**2)) - gammahat = left_std / right_std - rhat = (np.mean(np.abs(block)))**2 / np.mean(block**2) - rhatnorm = (rhat * (gammahat**3 + 1) * - (gammahat + 1)) / ((gammahat**2 + 1)**2) - array_position = np.argmin((r_gam - rhatnorm)**2) - - alpha = gam[array_position] - beta_l = left_std * np.sqrt(gamma(1 / alpha) / gamma(3 / alpha)) - beta_r = right_std * np.sqrt(gamma(1 / alpha) / gamma(3 / alpha)) - return (alpha, beta_l, beta_r) - - -def compute_feature(block): - """Compute features. - - Args: - block (ndarray): 2D Image block. - - Returns: - list: Features with length of 18. - """ - feat = [] - alpha, beta_l, beta_r = estimate_aggd_param(block) - feat.extend([alpha, (beta_l + beta_r) / 2]) - - # distortions disturb the fairly regular structure of natural images. - # This deviation can be captured by analyzing the sample distribution of - # the products of pairs of adjacent coefficients computed along - # horizontal, vertical and diagonal orientations. - shifts = [[0, 1], [1, 0], [1, 1], [1, -1]] - for shift in shifts: - shifted_block = np.roll(block, shift, axis=(0, 1)) - alpha, beta_l, beta_r = estimate_aggd_param(block * shifted_block) - mean = (beta_r - beta_l) * (gamma(2 / alpha) / gamma(1 / alpha)) - feat.extend([alpha, mean, beta_l, beta_r]) - return feat - - -def niqe_core(img, - mu_pris_param, - cov_pris_param, - gaussian_window, - block_size_h=96, - block_size_w=96): - """Calculate NIQE (Natural Image Quality Evaluator) metric. - - Ref: Making a "Completely Blind" Image Quality Analyzer. - This implementation could produce almost the same results as the official - MATLAB codes: http://live.ece.utexas.edu/research/quality/niqe_release.zip - - Note that we do not include block overlap height and width, since they are - always 0 in the official implementation. - - For good performance, it is advisable by the official implementation to - divide the distorted image in to the same size patched as used for the - construction of multivariate Gaussian model. - - Args: - img (ndarray): Input image whose quality needs to be computed. The - image must be a gray or Y (of YCbCr) image with shape (h, w). - Range [0, 255] with float type. - mu_pris_param (ndarray): Mean of a pre-defined multivariate Gaussian - model calculated on the pristine dataset. - cov_pris_param (ndarray): Covariance of a pre-defined multivariate - Gaussian model calculated on the pristine dataset. - gaussian_window (ndarray): A 7x7 Gaussian window used for smoothing the - image. - block_size_h (int): Height of the blocks in to which image is divided. - Default: 96 (the official recommended value). - block_size_w (int): Width of the blocks in to which image is divided. - Default: 96 (the official recommended value). - """ - # crop image - h, w = img.shape - num_block_h = math.floor(h / block_size_h) - num_block_w = math.floor(w / block_size_w) - img = img[0:num_block_h * block_size_h, 0:num_block_w * block_size_w] - - distparam = [] # dist param is actually the multiscale features - for scale in (1, 2): # perform on two scales (1, 2) - mu = convolve(img, gaussian_window, mode='nearest') - - sigma = np.sqrt( - np.abs( - convolve(np.square(img), gaussian_window, mode='nearest') - - np.square(mu))) - # normalize, as in Eq. 1 in the paper - img_nomalized = (img - mu) / (sigma + 1) - - feat = [] - for idx_w in range(num_block_w): - for idx_h in range(num_block_h): - # process each block - block = img_nomalized[idx_h * block_size_h // - scale:(idx_h + 1) * block_size_h // - scale, idx_w * block_size_w // - scale:(idx_w + 1) * block_size_w // - scale] - feat.append(compute_feature(block)) - - distparam.append(np.array(feat)) - - # matlab-like bicubic downsample with anti-aliasing - if scale == 1: - resize = MATLABLikeResize(keys=None, scale=0.5) - img = resize._resize(img[:, :, np.newaxis] / 255.)[:, :, 0] * 255. - - distparam = np.concatenate(distparam, axis=1) - - # fit a MVG (multivariate Gaussian) model to distorted patch features - mu_distparam = np.nanmean(distparam, axis=0) - distparam_no_nan = distparam[~np.isnan(distparam).any(axis=1)] - cov_distparam = np.cov(distparam_no_nan, rowvar=False) - - # compute niqe quality, Eq. 10 in the paper - invcov_param = np.linalg.pinv((cov_pris_param + cov_distparam) / 2) - quality = np.matmul( - np.matmul((mu_pris_param - mu_distparam), invcov_param), - np.transpose((mu_pris_param - mu_distparam))) - - return np.squeeze(np.sqrt(quality)) - - -def niqe(img, crop_border, input_order='HWC', convert_to='y'): - """Calculate NIQE (Natural Image Quality Evaluator) metric. - - Ref: Making a "Completely Blind" Image Quality Analyzer. - This implementation could produce almost the same results as the official - MATLAB codes: http://live.ece.utexas.edu/research/quality/niqe_release.zip - - We use the official params estimated from the pristine dataset. - We use the recommended block size (96, 96) without overlaps. - - Args: - img (ndarray): Input image whose quality needs to be computed. - The input image must be in range [0, 255] with float/int type. - The input_order of image can be 'HW' or 'HWC' or 'CHW'. (BGR order) - If the input order is 'HWC' or 'CHW', it will be converted to gray - or Y (of YCbCr) image according to the ``convert_to`` argument. - crop_border (int): Cropped pixels in each edge of an image. These - pixels are not involved in the metric calculation. - input_order (str): Whether the input order is 'HW', 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether converted to 'y' (of MATLAB YCbCr) or 'gray'. - Default: 'y'. - - Returns: - float: NIQE result. - """ - - # we use the official params estimated from the pristine dataset. - niqe_pris_params = np.load('mmedit/core/evaluation/niqe_pris_params.npz') - mu_pris_param = niqe_pris_params['mu_pris_param'] - cov_pris_param = niqe_pris_params['cov_pris_param'] - gaussian_window = niqe_pris_params['gaussian_window'] - - img = img.astype(np.float32) - if input_order != 'HW': - img = reorder_image(img, input_order=input_order) - if convert_to == 'y': - img = mmcv.bgr2ycbcr(img / 255., y_only=True) * 255. - elif convert_to == 'gray': - img = mmcv.bgr2gray(img / 255., cv2.COLOR_BGR2GRAY) * 255. - img = np.squeeze(img) - - if crop_border != 0: - img = img[crop_border:-crop_border, crop_border:-crop_border] - - # round to follow official implementation - img = img.round() - - niqe_result = niqe_core(img, mu_pris_param, cov_pris_param, - gaussian_window) - - return niqe_result diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/niqe_pris_params.npz b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/evaluation/niqe_pris_params.npz deleted file mode 100755 index 204ddcee87c4cd39aca04a42b539f0a5bfccecc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11850 zcmbt)cUaH;+jr3=DyzX|m!hnqC9AVZMABZ`oA%y&X;D&HMUqmXgpjuqEi+{lC3KOH zvO@CYcVDlL`*+><^T+f2zK%G)KA+F`=s3UU`8r?Yt*tqhOOWHgULqW0eZL;d<>cV_ z{WFzAki*5t!rjx!%fj8t)5=BC)jfcdW6HlS{(j*1O}}61TKZeGjX61dIfBG(?YwL} z#a9c9ueDbYUn(qa@8;?4Y2|9+=4os9`~TNl?ewx6`F}4*D|fq*_Yy0X3d_hZS}GhO z{QvvnnI`=;G2{-HlBB`x_0d1xQg?O8QO%(qnsX|_U3TGla@yMO{j0i~EZ13=saQ19 zs$Ija!?JJad3blt(Dzf6o4IXBA@>?hy~l6iD$qc}-_4DDJ>QUV{rspT(fzE#WmAQm zXg&R`|LdE$!gbQGerum;`G!JI?(dQr%w|tgvUZDpipczx02yA zzneR2-cxJ-d%+jWdTD-fMhoBc3VJ4AyhiGHEy=i5$!~4ENn?ficteAG$>j5Eo?kM> z7DnTQ4dnDn}rvW|bKT@{wg)=4h9w>YDT)_R!DHJe^f&obA< z&S~zYiU8|hAwA=4Gn!hTB&h@&)bYvErN)GvQ)U*g1Q7XExe z`||h_;>fYBHaT{jE^V&f*>I(q1pUq&HXJ%l3bpR}?TtlL(%QMb;EOAj42_wmE_j5Z zzq$3!OU|crzZRu)brn%H*9C_gnx5>Nq>WIGaUR*m8H6hMy-ACe6_nob+v?2Z{d&fCCCX-~=>-`b8)5*cpIw-|{AK5+ssC)6O zHjDYp-|o(}hvYsxu5Ep{hf3y`n3})LpnXdgrfRSDVG~}QKY9FUIyv9nAbNXQ3eDdT zIVRaJgBt62+Gx2q>)e~!W0jXq^6zz8-qao^qUwx1Tz&JvfW@Muvu5_DrA+ zYE)2}t;}wYskAO|iKCrWaV^1W33Q;O^XJW1@pM%_F6o7lC7U$l;pF)>o|GbLoSyBA zqdQYSM>+?`Qq7c#7RfMI=Fy=zJT$b6Ottn~?b{tin;$<+N{opi<-v||%^}Y0WQC0` zx5X|};(RT-<9O6)zo0JXBUWs_Cz3{eb2sIOggVl?MWxC!<044-*h_UCi6klv5i7D< zn?@mbhP?H19w_q1U;cEyCWe;ry=+Tf5>Mqyp+|)>_fX`=-Q!)KTCrPxX}J=-2^24& z{?`oCXeys5D6X?@4-K{^j&HnR$r}E2-QB~VK)!tH7iN`3(U8*u{qgaIwENYZzhu6r zke8f_cJ)v!#hq5V6w0xZPI;7f&3GL~d0DesjV44<(Ljr4eM2k_E>PU);Ix(ro#^KH zaVwncn)j`Y?v0=)`?jd@B}UWxguH6?Jafj+OYOYJB8d&Nw5*z78WU5pslO?TWIh?M zTyx)q#ogkm;NB5Q8`bv9-Ea>cog?%M>T=gfan)XNciJMYQhO{+iP>0r{ygC9L`Hmj z+a zRO8@wT?r+YV>ZiD-`bLPzPR2uvE5)4Uoc1L7u4l@k}uE2i$V1Eokhd&+fXV!w(8E5 z;&7UoR;OH5CQV5j3vRBi3M9`ftM{dz3Z*p`vRPL@hS8hij1TuSELrmBl*BWf;WR6M zwab`ve;O0k*p{>;jGWJ>UD=~(#Rl$3HBS69oW8t|s+n2uN9&BObogQuX?#hyTgYGp z-HcW9zTo3Ucg#3)=O!AkU~awG)8*bYba|b0!5^N~JI!YHy&vv$@PNk8_0mF2!?p0i zj4BU0cEa&xy{H$}Z%)Z*H*}%bO=%|&>FBa^y4g7<_r2&@+P6tk(ymnPZ6Nh|)=m-= zOLe}Vs>_t-_8q)b=S4S~PKb(LcNxW9;0xvm{err*1(qwmS56>}{*TLZHIhjnFj&Fg zL7&}Q(#dmYNeuZVC?-k7M3a`7v&}q@M2d9LeyEsX%dD%$*>rx2rL2UWxus_!XhW#v z<(JbF$t?Ze_Q}5X?959+9{w$HRQ9qzTj)hNO}B3iwbnjNio+9lhh2Bk%JN5KW)Mz; zwUaHpG}TElS7=`Tl>kzEc6sRT^)PZ-6m2MSJ)CAASSOv|V#1V;@mqV{4JDDqA~O9+ zLG-|&D%yKZ2;I!w`qO`#1>=#oD-ul%BP-pD1FO{h$c`md8b}Az`PViF+qf*)(gtts z@AhHDUEC_8`ptKA9|3oPFPJ0r3+l4ly^M23VLH8OA5W7e=&;ig3l<-}8b@Qce=py< zHJ-RmnV9)mr%}P3v{K$$8x~jA@RaXk0%fjX3!TnHQ=7-td0z9+`S=m zyMni2+Wjaxr7>n1w|zV<ztwS z#*!sQI~K|Q8AUyF-I@gpg6Vyo`Z%$cXsWGK`1Qv-6Q(h3N~!qqNV0a85P$kOcyv!= z9|3oPFPJ0r3+gh?`a55WryFID^>?_t!H)e|74IRXrA|jWX1DY`Q)PquXKejxXh2gc zO+&dZ>#?(ACYx+j7iFn2PlmQ}sWa2unA1(oMr1Yq_A$PELzY;S^q{JXgRZFvL(Cgv--B0q3*xjU8QHJ)$^zKBG7MF|+1<1_|U38n1^(}d6 zK3>$0_UZ;icgwA3)9vE(g$GTj^zf7K-jz!!;dtT4#j94)W(NhX*6wu_KaaoUhVW+k zk$mEZkeL{r*c_s(OpD3aS*rZ#3>6xvJF6QIw1L21u&1$)fV;pK%n|wpbvfMQFme0M zC<^GYdnFeVPAv-S+>|0?NP#n~v_js2ndV#Z7R?{|e$1_F+9!n2pOPz&ipa*$?DB3$ zttrl|M|(zzcWyM9Z%Hy-xiN@*B>U_pf7wgk<_m>4eTbqBj*{Fbl>O+sLQ3bQ32M~y zre@;ubDm_kTKCHGbAGh?+`7Cqd;O`ua`vuC=dIb|{_ch6^aIG|M8(9mr=H|JQBtMx zPaj%dJ9mT0cr(@&-rBD>)t|zR%a%>K=uRg&jMg^0dDGezYnGayH(?)}CO^}@;78*^ zo4faQjO?esc^&u*_B8epa2NQ3IYPgnE(TZPxSpm(QK*2-`-)j{G}P2K5M*q@?hJTu zPRWa++3^B7Z*#(E+@l;{xx=xf!6UUpwA!34b`L6(IS@rrDmrZkuZ2?1nyMVdMf<78 zLvDNZqj;)Uy%Jhb6+#jxpYKZEs>bfiDn&@_3Z(3TF9Sk5f{1vkLZ|BllWz6B%AsI! z_O5QBVWv+2&5$1I-x3o<&OzaVYp(gzd0yq-Z{0?0VK~nlKhHzRv@H+4p>}l*H z;4bh5bA*0DUCPuI<8=lcsl)r4@;EM6cJ9fV!yjYySjorspc9qW%w9u(gr=xbmC~SB z)(Sh8y1m}fsLz5mxw%$enxV#yC_0E)R_n74)6_RgCl#5v%L)2=Re}t1e*M~dMx9Nz zY<;L3tj>Ok9BNobs~E3A^FXKEHcERg*ekYNmra(>+1fO)gweuU!Ak!1Y)Zti`Mad; z^gV{_(5X*GOuNv_cG3g^sm8#Rm_}uaBppFej!y#-6p_ zW-}w5VyWQ9t)FKd!pUNJf1lKzY`VXv-aB!5JoSB!IPzlJ$oyodUG%NzVcMCyBo@i~ zQ_v=Z(gdR*+9zEmBbgjTiY0qD|MA_BO}<J`9)7VGAUEmAm2>pV(jGeyOQ>8A5#w))cQH{}5vj6l{F$XI)!F`rk z`j!Y1nxqx07#KjG45oxjf61hx$dcY+7EOP>OKEog=}&5_jVnLy=4X4#yZ>-f_n|V& z2d{P>_al>+UtfA%_NQi*o=0ZZD$Ff?z0v)pzGP|N;wl#EOL=cwuIwG_OKZ*+9^WLi zjdhhbc6{0GM^^I{(p~@XB730@5!W~$Dt~pkkDJSkiO!h*)%mSIopV%gPY?1K)zi?8 z&_~b#;Je^;;4j$I*hj!!;0xvm{errrW^`Duu}!Bb-$V6FPusDvs@*P{PZLO9d*)e* zgeZ#Zo?dm`qnH$=8@4$dO{NF_UzW-G#gf6<#%YFb`lK}Tp7x9pe0`bG`1taTND|ob zl~+$Bj>dgBG01M95VjN9{rS|&wN^vNV{RiB_yp89a21tOGKcUhqE z>%ITsnuavHaqy~b!QdvE za%ZvUn>1~v(7i>>Jx`A1d)|9HTw!ul7!}@ z<)+$9aIIa;uOAkazdCDE@FP>^p6b4K(~cik;J?6UfS!hKgg$}}0N(|#1AoDu#y$e> z0$(sk=oi$b@x5eceXfs#Y5}QrSFLP(SeI7?*iBcO2_mALAwpzi0 zpQ7y6RgQJ7V*d2RP~Y}ceIN0mD&Ghq#A2O*gy=>(=e~QiXU%anQh;;6Y^e)r#CKaQBs-GkJNyzY*-{3bt zirxNxw~eDJqgS6_+En?FPVj)$Qdk$7BOA9;v|0xo*3(- zVc|g!L&rZ%+u%(L3xC;*S8QRbyjh8t_V`l7jm;Z-R(g*5b?~L&zrbgJo`!COK7tMa z-vzG&f5D!{J_7CnUoc1L7t}?(jYG@$c@ODX_&qUvS4M}z{If)C&ePAT&a>+AzLX%v zAKCW(7-b6GQ`+8jl4LUu)$-52Kr7wf284|0@@9U&>73K@$+@b@!Km*feOk7a*W+0w z4embNzg5JH`J_rOiRdbVR)jYn(ZKBppgMZ-Cgsemo>gxR1cPN(nUfLcp@=l`Eg{Nz- zPPJpvfrp~?Y75hA zorZXlNi;=jvff)w=8*M{Q)I*!FzFT0Xx0uN^}XTm!pDSP2VV;Q3w#FXY3N4iBj^C| zUGO^a7wl>5Bj7IZ1#^UcL0t-R<0RDQxKi-^sVBM~nJ}^Ps`F#Xm?U0&ZO!+xr3OK< zIq5Rn*$c<6(2PxnCrj4Q_rN#PW`@a9g5}w}(|puM^8onX@OR;3!moob1^)#;1N1a> zBlHn;0QfF=9rz3OH1-j27x;oXLcgFc9|{DTJXJ#ILF-IA>%>5s>t;Du-6e!Lnm;S) z_2`gK^6n)mjXre9H?~VcKZNcV^`88EEO^8}xB8y?K$8XhjN>=l7DO|LFY%l?=tn<1 z&PK+L?7wrR8v6DHM(owZX{)>If@x-1lUDXBpV6EIc>sKG_`C2i;n%^Jg8u@a0eTv` z5&8%^0DKp`4*Ugs8v6*i3w*&Gp+{ z0KN-e2mXRRjeP{%1-@X8&@ZSteU>F@VA)BZI-92-BFFqz!B+>@s0k*59ileRJ{beVhX%tMWu*EwDe5#&lV7MGDEBrk3@bz(YMczWyW&Tn^-iTZc`4Msw z3RCX2bBp_q=CH^ck*gs;LQaA_0KPZ;UHF*r>)=ble}T^cJw2)$ z|JFy)0pPpfb>J`9)7VGAUEmAm2>pV(G-XKNk0L-jl#(~YCZ@$s#nPui6jJ-^d5 zPb!QgCuSsWa|@>YO^-J0jgO=iT8+{0KN-e2mXRRjeP{%1-@X8&@ZUVJ6$og zj;1}d;_{o*9VOb##`;*1cyKgDiND}};wISKLr_}=h$;bX$DgD(aD1wI4xG;|~M5p)3fE_faI z3-&bj5pWmyf;mFJpf0-?2TTu=wRj50 z-@LcvwbKC|_ONk&c!S?a9$7JGfrD${=s6FZXFzU`d>T0{@>0l6WgJ-8 zkNzWRS}RA-OW|A)&cEQC2hKAfw?{sW92R*aay8^f$Vrd~!1so~3m+4H9egSHFYpB4G3!8O}@LToBH` z;G74}Ga$D|K8+j}c_VT)%d>I zr?HQKyTBLB5&8vnF}$Ux-Yw_*k293zU$gwikKy3>{Ub1PhSI^x$IHuUz!e_h!Xm^{PL9%!{9nR`LwC2Qt(f7EH=kfWtKW>sn*S?lwvMqV! z=XCch;XSV7e*8Z8eB9svaO&986R(UukL$P}&*Ss(`FI}haUJ*L_r>pz>-c=U$93HQ z&-&o={`vd-^L}kj&M|^x|8J+R|Ep(y{~i9X1J?iJ4E8@C|MwHU|NZe7BOmZT&-ecG k`G4=`|NgxA5|00^x3x9@829_Ou_J%3j{NJr?DxC>1Ij;2^Z)<= diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/export/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/export/__init__.py deleted file mode 100755 index 6cf757d83..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/export/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .wrappers import ONNXRuntimeEditing - -__all__ = ['ONNXRuntimeEditing'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/export/wrappers.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/export/wrappers.py deleted file mode 100755 index 5b4d55fbc..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/export/wrappers.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -import numpy as np -import onnxruntime as ort -import torch -from torch import nn - -from mmedit.models import BaseMattor, BasicRestorer, build_model - - -def inference_with_session(sess, io_binding, output_names, input_tensor): - device_type = input_tensor.device.type - device_id = input_tensor.device.index - device_id = 0 if device_id is None else device_id - io_binding.bind_input( - name='input', - device_type=device_type, - device_id=device_id, - element_type=np.float32, - shape=input_tensor.shape, - buffer_ptr=input_tensor.data_ptr()) - for name in output_names: - io_binding.bind_output(name) - sess.run_with_iobinding(io_binding) - pred = io_binding.copy_outputs_to_cpu() - return pred - - -class ONNXRuntimeMattor(nn.Module): - - def __init__(self, sess, io_binding, output_names, base_model): - super(ONNXRuntimeMattor, self).__init__() - self.sess = sess - self.io_binding = io_binding - self.output_names = output_names - self.base_model = base_model - - def forward(self, - merged, - trimap, - meta, - test_mode=False, - save_image=False, - save_path=None, - iteration=None): - input_tensor = torch.cat((merged, trimap), 1).contiguous() - pred_alpha = inference_with_session(self.sess, self.io_binding, - self.output_names, input_tensor)[0] - - pred_alpha = pred_alpha.squeeze() - pred_alpha = self.base_model.restore_shape(pred_alpha, meta) - eval_result = self.base_model.evaluate(pred_alpha, meta) - - if save_image: - self.base_model.save_image(pred_alpha, meta, save_path, iteration) - - return {'pred_alpha': pred_alpha, 'eval_result': eval_result} - - -class RestorerGenerator(nn.Module): - - def __init__(self, sess, io_binding, output_names): - super(RestorerGenerator, self).__init__() - self.sess = sess - self.io_binding = io_binding - self.output_names = output_names - - def forward(self, x): - pred = inference_with_session(self.sess, self.io_binding, - self.output_names, x)[0] - pred = torch.from_numpy(pred) - return pred - - -class ONNXRuntimeRestorer(nn.Module): - - def __init__(self, sess, io_binding, output_names, base_model): - super(ONNXRuntimeRestorer, self).__init__() - self.sess = sess - self.io_binding = io_binding - self.output_names = output_names - self.base_model = base_model - restorer_generator = RestorerGenerator(self.sess, self.io_binding, - self.output_names) - base_model.generator = restorer_generator - - def forward(self, lq, gt=None, test_mode=False, **kwargs): - return self.base_model(lq, gt=gt, test_mode=test_mode, **kwargs) - - -class ONNXRuntimeEditing(nn.Module): - - def __init__(self, onnx_file, cfg, device_id): - super(ONNXRuntimeEditing, self).__init__() - ort_custom_op_path = '' - try: - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - except (ImportError, ModuleNotFoundError): - warnings.warn('If input model has custom op from mmcv, \ - you may have to build mmcv with ONNXRuntime from source.') - session_options = ort.SessionOptions() - # register custom op for onnxruntime - if osp.exists(ort_custom_op_path): - session_options.register_custom_ops_library(ort_custom_op_path) - sess = ort.InferenceSession(onnx_file, session_options) - providers = ['CPUExecutionProvider'] - options = [{}] - is_cuda_available = ort.get_device() == 'GPU' - if is_cuda_available: - providers.insert(0, 'CUDAExecutionProvider') - options.insert(0, {'device_id': device_id}) - - sess.set_providers(providers, options) - - self.sess = sess - self.device_id = device_id - self.io_binding = sess.io_binding() - self.output_names = [_.name for _ in sess.get_outputs()] - - base_model = build_model( - cfg.model, train_cfg=None, test_cfg=cfg.test_cfg) - - if isinstance(base_model, BaseMattor): - WrapperClass = ONNXRuntimeMattor - elif isinstance(base_model, BasicRestorer): - WrapperClass = ONNXRuntimeRestorer - self.wrapper = WrapperClass(self.sess, self.io_binding, - self.output_names, base_model) - - def forward(self, **kwargs): - return self.wrapper(**kwargs) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/hooks/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/hooks/__init__.py deleted file mode 100755 index 575c43b35..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/hooks/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .ema import ExponentialMovingAverageHook -from .visualization import VisualizationHook - -__all__ = ['VisualizationHook', 'ExponentialMovingAverageHook'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/hooks/ema.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/hooks/ema.py deleted file mode 100755 index 0e7f0b2e8..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/hooks/ema.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from copy import deepcopy -from functools import partial - -import mmcv -import torch -from mmcv.parallel import is_module_wrapper -from mmcv.runner import HOOKS, Hook - - -@HOOKS.register_module() -class ExponentialMovingAverageHook(Hook): - """Exponential Moving Average Hook. - - Exponential moving average is a trick that widely used in current GAN - literature, e.g., PGGAN, StyleGAN, and BigGAN. This general idea of it is - maintaining a model with the same architecture, but its parameters are - updated as a moving average of the trained weights in the original model. - In general, the model with moving averaged weights achieves better - performance. - - Args: - module_keys (str | tuple[str]): The name of the ema model. Note that we - require these keys are followed by '_ema' so that we can easily - find the original model by discarding the last four characters. - interp_mode (str, optional): Mode of the interpolation method. - Defaults to 'lerp'. - interp_cfg (dict | None, optional): Set arguments of the interpolation - function. Defaults to None. - interval (int, optional): Evaluation interval (by iterations). - Default: -1. - start_iter (int, optional): Start iteration for ema. If the start - iteration is not reached, the weights of ema model will maintain - the same as the original one. Otherwise, its parameters are updated - as a moving average of the trained weights in the original model. - Default: 0. - """ - - def __init__(self, - module_keys, - interp_mode='lerp', - interp_cfg=None, - interval=-1, - start_iter=0): - super().__init__() - assert isinstance(module_keys, str) or mmcv.is_tuple_of( - module_keys, str) - self.module_keys = (module_keys, ) if isinstance(module_keys, - str) else module_keys - # sanity check for the format of module keys - for k in self.module_keys: - assert k.endswith( - '_ema'), 'You should give keys that end with "_ema".' - self.interp_mode = interp_mode - self.interp_cfg = dict() if interp_cfg is None else deepcopy( - interp_cfg) - self.interval = interval - self.start_iter = start_iter - - assert hasattr( - self, interp_mode - ), f'Currently, we do not support {self.interp_mode} for EMA.' - self.interp_func = partial( - getattr(self, interp_mode), **self.interp_cfg) - - @staticmethod - def lerp(a, b, momentum=0.999, momentum_nontrainable=0., trainable=True): - m = momentum if trainable else momentum_nontrainable - return a + (b - a) * m - - def every_n_iters(self, runner, n): - if runner.iter < self.start_iter: - return True - return (runner.iter + 1 - self.start_iter) % n == 0 if n > 0 else False - - @torch.no_grad() - def after_train_iter(self, runner): - if not self.every_n_iters(runner, self.interval): - return - - model = runner.model.module if is_module_wrapper( - runner.model) else runner.model - - for key in self.module_keys: - # get current ema states - ema_net = getattr(model, key) - states_ema = ema_net.state_dict(keep_vars=False) - # get currently original states - net = getattr(model, key[:-4]) - states_orig = net.state_dict(keep_vars=True) - - for k, v in states_orig.items(): - if runner.iter < self.start_iter: - states_ema[k].data.copy_(v.data) - else: - states_ema[k] = self.interp_func( - v, states_ema[k], trainable=v.requires_grad).detach() - ema_net.load_state_dict(states_ema, strict=True) - - def before_run(self, runner): - model = runner.model.module if is_module_wrapper( - runner.model) else runner.model - # sanity check for ema model - for k in self.module_keys: - if not hasattr(model, k) and not hasattr(model, k[:-4]): - raise RuntimeError( - f'Cannot find both {k[:-4]} and {k} network for EMA hook.') - if not hasattr(model, k) and hasattr(model, k[:-4]): - setattr(model, k, deepcopy(getattr(model, k[:-4]))) - warnings.warn( - f'We do not suggest construct and initialize EMA model {k}' - ' in hook. You may explicitly define it by yourself.') diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/hooks/visualization.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/hooks/visualization.py deleted file mode 100755 index 63c8ee7d8..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/hooks/visualization.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import torch -from mmcv.runner import HOOKS, Hook -from mmcv.runner.dist_utils import master_only -from torchvision.utils import save_image - - -@HOOKS.register_module() -class VisualizationHook(Hook): - """Visualization hook. - - In this hook, we use the official api `save_image` in torchvision to save - the visualization results. - - Args: - output_dir (str): The file path to store visualizations. - res_name_list (str): The list contains the name of results in outputs - dict. The results in outputs dict must be a torch.Tensor with shape - (n, c, h, w). - interval (int): The interval of calling this hook. If set to -1, - the visualization hook will not be called. Default: -1. - filename_tmpl (str): Format string used to save images. The output file - name will be formatted as this args. Default: 'iter_{}.png'. - rerange (bool): Whether to rerange the output value from [-1, 1] to - [0, 1]. We highly recommend users should preprocess the - visualization results on their own. Here, we just provide a simple - interface. Default: True. - bgr2rgb (bool): Whether to reformat the channel dimension from BGR to - RGB. The final image we will save is following RGB style. - Default: True. - nrow (int): The number of samples in a row. Default: 1. - padding (int): The number of padding pixels between each samples. - Default: 4. - """ - - def __init__(self, - output_dir, - res_name_list, - interval=-1, - filename_tmpl='iter_{}.png', - rerange=True, - bgr2rgb=True, - nrow=1, - padding=4): - assert mmcv.is_list_of(res_name_list, str) - self.output_dir = output_dir - self.res_name_list = res_name_list - self.interval = interval - self.filename_tmpl = filename_tmpl - self.bgr2rgb = bgr2rgb - self.rerange = rerange - self.nrow = nrow - self.padding = padding - - mmcv.mkdir_or_exist(self.output_dir) - - @master_only - def after_train_iter(self, runner): - """The behavior after each train iteration. - - Args: - runner (object): The runner. - """ - if not self.every_n_iters(runner, self.interval): - return - results = runner.outputs['results'] - - filename = self.filename_tmpl.format(runner.iter + 1) - - img_list = [x for k, x in results.items() if k in self.res_name_list] - img_cat = torch.cat(img_list, dim=3).detach() - if self.rerange: - img_cat = ((img_cat + 1) / 2) - if self.bgr2rgb: - img_cat = img_cat[:, [2, 1, 0], ...] - img_cat = img_cat.clamp_(0, 1) - save_image( - img_cat, - osp.join(self.output_dir, filename), - nrow=self.nrow, - padding=self.padding) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/mask.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/mask.py deleted file mode 100755 index 51486111c..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/mask.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import cv2 -import mmcv -import numpy as np -from PIL import Image, ImageDraw - - -def random_bbox(img_shape, max_bbox_shape, max_bbox_delta=40, min_margin=20): - """Generate a random bbox for the mask on a given image. - - In our implementation, the max value cannot be obtained since we use - `np.random.randint`. And this may be different with other standard scripts - in the community. - - Args: - img_shape (tuple[int]): The size of a image, in the form of (h, w). - max_bbox_shape (int | tuple[int]): Maximum shape of the mask box, - in the form of (h, w). If it is an integer, the mask box will be - square. - max_bbox_delta (int | tuple[int]): Maximum delta of the mask box, - in the form of (delta_h, delta_w). If it is an integer, delta_h - and delta_w will be the same. Mask shape will be randomly sampled - from the range of `max_bbox_shape - max_bbox_delta` and - `max_bbox_shape`. Default: (40, 40). - min_margin (int | tuple[int]): The minimum margin size from the - edges of mask box to the image boarder, in the form of - (margin_h, margin_w). If it is an integer, margin_h and margin_w - will be the same. Default: (20, 20). - - Returns: - tuple[int]: The generated box, (top, left, h, w). - """ - if not isinstance(max_bbox_shape, tuple): - max_bbox_shape = (max_bbox_shape, max_bbox_shape) - if not isinstance(max_bbox_delta, tuple): - max_bbox_delta = (max_bbox_delta, max_bbox_delta) - if not isinstance(min_margin, tuple): - min_margin = (min_margin, min_margin) - assert mmcv.is_tuple_of(max_bbox_shape, int) - assert mmcv.is_tuple_of(max_bbox_delta, int) - assert mmcv.is_tuple_of(min_margin, int) - - img_h, img_w = img_shape[:2] - max_mask_h, max_mask_w = max_bbox_shape - max_delta_h, max_delta_w = max_bbox_delta - margin_h, margin_w = min_margin - - if max_mask_h > img_h or max_mask_w > img_w: - raise ValueError(f'mask shape {max_bbox_shape} should be smaller than ' - f'image shape {img_shape}') - if (max_delta_h // 2 * 2 >= max_mask_h - or max_delta_w // 2 * 2 >= max_mask_w): - raise ValueError(f'mask delta {max_bbox_delta} should be smaller than' - f'mask shape {max_bbox_shape}') - if img_h - max_mask_h < 2 * margin_h or img_w - max_mask_w < 2 * margin_w: - raise ValueError(f'Margin {min_margin} cannot be satisfied for img' - f'shape {img_shape} and mask shape {max_bbox_shape}') - - # get the max value of (top, left) - max_top = img_h - margin_h - max_mask_h - max_left = img_w - margin_w - max_mask_w - # randomly select a (top, left) - top = np.random.randint(margin_h, max_top) - left = np.random.randint(margin_w, max_left) - # randomly shrink the shape of mask box according to `max_bbox_delta` - # the center of box is fixed - delta_top = np.random.randint(0, max_delta_h // 2 + 1) - delta_left = np.random.randint(0, max_delta_w // 2 + 1) - top = top + delta_top - left = left + delta_left - h = max_mask_h - delta_top - w = max_mask_w - delta_left - return (top, left, h, w) - - -def bbox2mask(img_shape, bbox, dtype='uint8'): - """Generate mask in ndarray from bbox. - - The returned mask has the shape of (h, w, 1). '1' indicates the - hole and '0' indicates the valid regions. - - We prefer to use `uint8` as the data type of masks, which may be different - from other codes in the community. - - Args: - img_shape (tuple[int]): The size of the image. - bbox (tuple[int]): Configuration tuple, (top, left, height, width) - dtype (str): Indicate the data type of returned masks. Default: 'uint8' - - Return: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - height, width = img_shape[:2] - - mask = np.zeros((height, width, 1), dtype=dtype) - mask[bbox[0]:bbox[0] + bbox[2], bbox[1]:bbox[1] + bbox[3], :] = 1 - - return mask - - -def brush_stroke_mask(img_shape, - num_vertices=(4, 12), - mean_angle=2 * math.pi / 5, - angle_range=2 * math.pi / 15, - brush_width=(12, 40), - max_loops=4, - dtype='uint8'): - """Generate free-form mask. - - The method of generating free-form mask is in the following paper: - Free-Form Image Inpainting with Gated Convolution. - - When you set the config of this type of mask. You may note the usage of - `np.random.randint` and the range of `np.random.randint` is [left, right). - - We prefer to use `uint8` as the data type of masks, which may be different - from other codes in the community. - - TODO: Rewrite the implementation of this function. - - Args: - img_shape (tuple[int]): Size of the image. - num_vertices (int | tuple[int]): Min and max number of vertices. If - only give an integer, we will fix the number of vertices. - Default: (4, 12). - mean_angle (float): Mean value of the angle in each vertex. The angle - is measured in radians. Default: 2 * math.pi / 5. - angle_range (float): Range of the random angle. - Default: 2 * math.pi / 15. - brush_width (int | tuple[int]): (min_width, max_width). If only give - an integer, we will fix the width of brush. Default: (12, 40). - max_loops (int): The max number of for loops of drawing strokes. - dtype (str): Indicate the data type of returned masks. - Default: 'uint8'. - - Returns: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - img_h, img_w = img_shape[:2] - if isinstance(num_vertices, int): - min_num_vertices, max_num_vertices = num_vertices, num_vertices + 1 - elif isinstance(num_vertices, tuple): - min_num_vertices, max_num_vertices = num_vertices - else: - raise TypeError('The type of num_vertices should be int' - f'or tuple[int], but got type: {num_vertices}') - - if isinstance(brush_width, tuple): - min_width, max_width = brush_width - elif isinstance(brush_width, int): - min_width, max_width = brush_width, brush_width + 1 - else: - raise TypeError('The type of brush_width should be int' - f'or tuple[int], but got type: {brush_width}') - - average_radius = math.sqrt(img_h * img_h + img_w * img_w) / 8 - mask = Image.new('L', (img_w, img_h), 0) - - loop_num = np.random.randint(1, max_loops) - num_vertex_list = np.random.randint( - min_num_vertices, max_num_vertices, size=loop_num) - angle_min_list = np.random.uniform(0, angle_range, size=loop_num) - angle_max_list = np.random.uniform(0, angle_range, size=loop_num) - - for loop_n in range(loop_num): - num_vertex = num_vertex_list[loop_n] - angle_min = mean_angle - angle_min_list[loop_n] - angle_max = mean_angle + angle_max_list[loop_n] - angles = [] - vertex = [] - - # set random angle on each vertex - angles = np.random.uniform(angle_min, angle_max, size=num_vertex) - reverse_mask = (np.arange(num_vertex, dtype=np.float32) % 2) == 0 - angles[reverse_mask] = 2 * math.pi - angles[reverse_mask] - - h, w = mask.size - - # set random vertices - vertex.append((np.random.randint(0, w), np.random.randint(0, h))) - r_list = np.random.normal( - loc=average_radius, scale=average_radius // 2, size=num_vertex) - for i in range(num_vertex): - r = np.clip(r_list[i], 0, 2 * average_radius) - new_x = np.clip(vertex[-1][0] + r * math.cos(angles[i]), 0, w) - new_y = np.clip(vertex[-1][1] + r * math.sin(angles[i]), 0, h) - vertex.append((int(new_x), int(new_y))) - # draw brush strokes according to the vertex and angle list - draw = ImageDraw.Draw(mask) - width = np.random.randint(min_width, max_width) - draw.line(vertex, fill=1, width=width) - for v in vertex: - draw.ellipse((v[0] - width // 2, v[1] - width // 2, - v[0] + width // 2, v[1] + width // 2), - fill=1) - # randomly flip the mask - if np.random.normal() > 0: - mask.transpose(Image.FLIP_LEFT_RIGHT) - if np.random.normal() > 0: - mask.transpose(Image.FLIP_TOP_BOTTOM) - mask = np.array(mask).astype(dtype=getattr(np, dtype)) - mask = mask[:, :, None] - return mask - - -def random_irregular_mask(img_shape, - num_vertices=(4, 8), - max_angle=4, - length_range=(10, 100), - brush_width=(10, 40), - dtype='uint8'): - """Generate random irregular masks. - - This is a modified version of free-form mask implemented in - 'brush_stroke_mask'. - - We prefer to use `uint8` as the data type of masks, which may be different - from other codes in the community. - - TODO: Rewrite the implementation of this function. - - Args: - img_shape (tuple[int]): Size of the image. - num_vertices (int | tuple[int]): Min and max number of vertices. If - only give an integer, we will fix the number of vertices. - Default: (4, 8). - max_angle (float): Max value of angle at each vertex. Default 4.0. - length_range (int | tuple[int]): (min_length, max_length). If only give - an integer, we will fix the length of brush. Default: (10, 100). - brush_width (int | tuple[int]): (min_width, max_width). If only give - an integer, we will fix the width of brush. Default: (10, 40). - dtype (str): Indicate the data type of returned masks. Default: 'uint8' - - Returns: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - h, w = img_shape[:2] - - mask = np.zeros((h, w), dtype=dtype) - if isinstance(length_range, int): - min_length, max_length = length_range, length_range + 1 - elif isinstance(length_range, tuple): - min_length, max_length = length_range - else: - raise TypeError('The type of length_range should be int' - f'or tuple[int], but got type: {length_range}') - if isinstance(num_vertices, int): - min_num_vertices, max_num_vertices = num_vertices, num_vertices + 1 - elif isinstance(num_vertices, tuple): - min_num_vertices, max_num_vertices = num_vertices - else: - raise TypeError('The type of num_vertices should be int' - f'or tuple[int], but got type: {num_vertices}') - - if isinstance(brush_width, int): - min_brush_width, max_brush_width = brush_width, brush_width + 1 - elif isinstance(brush_width, tuple): - min_brush_width, max_brush_width = brush_width - else: - raise TypeError('The type of brush_width should be int' - f'or tuple[int], but got type: {brush_width}') - - num_v = np.random.randint(min_num_vertices, max_num_vertices) - - for i in range(num_v): - start_x = np.random.randint(w) - start_y = np.random.randint(h) - # from the start point, randomly setlect n \in [1, 6] directions. - direction_num = np.random.randint(1, 6) - angle_list = np.random.randint(0, max_angle, size=direction_num) - length_list = np.random.randint( - min_length, max_length, size=direction_num) - brush_width_list = np.random.randint( - min_brush_width, max_brush_width, size=direction_num) - for direct_n in range(direction_num): - angle = 0.01 + angle_list[direct_n] - if i % 2 == 0: - angle = 2 * math.pi - angle - length = length_list[direct_n] - brush_w = brush_width_list[direct_n] - # compute end point according to the random angle - end_x = (start_x + length * np.sin(angle)).astype(np.int32) - end_y = (start_y + length * np.cos(angle)).astype(np.int32) - - cv2.line(mask, (start_y, start_x), (end_y, end_x), 1, brush_w) - start_x, start_y = end_x, end_y - mask = np.expand_dims(mask, axis=2) - - return mask - - -def get_irregular_mask(img_shape, area_ratio_range=(0.15, 0.5), **kwargs): - """Get irregular mask with the constraints in mask ratio - - Args: - img_shape (tuple[int]): Size of the image. - area_ratio_range (tuple(float)): Contain the minimum and maximum area - ratio. Default: (0.15, 0.5). - - Returns: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - mask = random_irregular_mask(img_shape, **kwargs) - min_ratio, max_ratio = area_ratio_range - - while not min_ratio < (np.sum(mask) / - (img_shape[0] * img_shape[1])) < max_ratio: - mask = random_irregular_mask(img_shape, **kwargs) - - return mask diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/misc.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/misc.py deleted file mode 100755 index 21c5d4770..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/misc.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -from torchvision.utils import make_grid - - -def tensor2img(tensor, out_type=np.uint8, min_max=(0, 1)): - """Convert torch Tensors into image numpy arrays. - - After clamping to (min, max), image values will be normalized to [0, 1]. - - For different tensor shapes, this function will have different behaviors: - - 1. 4D mini-batch Tensor of shape (N x 3/1 x H x W): - Use `make_grid` to stitch images in the batch dimension, and then - convert it to numpy array. - 2. 3D Tensor of shape (3/1 x H x W) and 2D Tensor of shape (H x W): - Directly change to numpy array. - - Note that the image channel in input tensors should be RGB order. This - function will convert it to cv2 convention, i.e., (H x W x C) with BGR - order. - - Args: - tensor (Tensor | list[Tensor]): Input tensors. - out_type (numpy type): Output types. If ``np.uint8``, transform outputs - to uint8 type with range [0, 255]; otherwise, float type with - range [0, 1]. Default: ``np.uint8``. - min_max (tuple): min and max values for clamp. - - Returns: - (Tensor | list[Tensor]): 3D ndarray of shape (H x W x C) or 2D ndarray - of shape (H x W). - """ - if not (torch.is_tensor(tensor) or - (isinstance(tensor, list) - and all(torch.is_tensor(t) for t in tensor))): - raise TypeError( - f'tensor or list of tensors expected, got {type(tensor)}') - - if torch.is_tensor(tensor): - tensor = [tensor] - result = [] - for _tensor in tensor: - # Squeeze two times so that: - # 1. (1, 1, h, w) -> (h, w) or - # 3. (1, 3, h, w) -> (3, h, w) or - # 2. (n>1, 3/1, h, w) -> (n>1, 3/1, h, w) - _tensor = _tensor.squeeze(0).squeeze(0) - _tensor = _tensor.float().detach().cpu().clamp_(*min_max) - _tensor = (_tensor - min_max[0]) / (min_max[1] - min_max[0]) - n_dim = _tensor.dim() - if n_dim == 4: - img_np = make_grid( - _tensor, nrow=int(math.sqrt(_tensor.size(0))), - normalize=False).numpy() - img_np = np.transpose(img_np[[2, 1, 0], :, :], (1, 2, 0)) - elif n_dim == 3: - img_np = _tensor.numpy() - img_np = np.transpose(img_np[[2, 1, 0], :, :], (1, 2, 0)) - elif n_dim == 2: - img_np = _tensor.numpy() - else: - raise ValueError('Only support 4D, 3D or 2D tensor. ' - f'But received with dimension: {n_dim}') - if out_type == np.uint8: - # Unlike MATLAB, numpy.unit8() WILL NOT round by default. - img_np = (img_np * 255.0).round() - img_np = img_np.astype(out_type) - result.append(img_np) - result = result[0] if len(result) == 1 else result - return result diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/optimizer/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/optimizer/__init__.py deleted file mode 100755 index e1c477dd1..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/optimizer/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_optimizers - -__all__ = ['build_optimizers'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/optimizer/builder.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/optimizer/builder.py deleted file mode 100755 index 2edf94dad..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/optimizer/builder.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import build_optimizer - - -def build_optimizers(model, cfgs): - """Build multiple optimizers from configs. - - If `cfgs` contains several dicts for optimizers, then a dict for each - constructed optimizers will be returned. - If `cfgs` only contains one optimizer config, the constructed optimizer - itself will be returned. - - For example, - - 1) Multiple optimizer configs: - - .. code-block:: python - - optimizer_cfg = dict( - model1=dict(type='SGD', lr=lr), - model2=dict(type='SGD', lr=lr)) - - The return dict is - ``dict('model1': torch.optim.Optimizer, 'model2': torch.optim.Optimizer)`` - - 2) Single optimizer config: - - .. code-block:: python - - optimizer_cfg = dict(type='SGD', lr=lr) - - The return is ``torch.optim.Optimizer``. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - cfgs (dict): The config dict of the optimizer. - - Returns: - dict[:obj:`torch.optim.Optimizer`] | :obj:`torch.optim.Optimizer`: - The initialized optimizers. - """ - optimizers = {} - if hasattr(model, 'module'): - model = model.module - # determine whether 'cfgs' has several dicts for optimizers - is_dict_of_dict = True - for key, cfg in cfgs.items(): - if not isinstance(cfg, dict): - is_dict_of_dict = False - - if is_dict_of_dict: - for key, cfg in cfgs.items(): - cfg_ = cfg.copy() - module = getattr(model, key) - optimizers[key] = build_optimizer(module, cfg_) - return optimizers - - return build_optimizer(model, cfgs) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/scheduler/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/scheduler/__init__.py deleted file mode 100755 index af0458206..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/scheduler/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .lr_updater import LinearLrUpdaterHook, ReduceLrUpdaterHook - -__all__ = ['LinearLrUpdaterHook', 'ReduceLrUpdaterHook'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/scheduler/lr_updater.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/scheduler/lr_updater.py deleted file mode 100755 index 0677373c6..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/scheduler/lr_updater.py +++ /dev/null @@ -1,304 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import HOOKS, LrUpdaterHook - - -@HOOKS.register_module() -class LinearLrUpdaterHook(LrUpdaterHook): - """Linear learning rate scheduler for image generation. - - In the beginning, the learning rate is 'base_lr' defined in mmcv. - We give a target learning rate 'target_lr' and a start point 'start' - (iteration / epoch). Before 'start', we fix learning rate as 'base_lr'; - After 'start', we linearly update learning rate to 'target_lr'. - - Args: - target_lr (float): The target learning rate. Default: 0. - start (int): The start point (iteration / epoch, specified by args - 'by_epoch' in its parent class in mmcv) to update learning rate. - Default: 0. - interval (int): The interval to update the learning rate. Default: 1. - """ - - def __init__(self, target_lr=0, start=0, interval=1, **kwargs): - super().__init__(**kwargs) - self.target_lr = target_lr - self.start = start - self.interval = interval - - def get_lr(self, runner, base_lr): - """Calculates the learning rate. - - Args: - runner (object): The passed runner. - base_lr (float): Base learning rate. - - Returns: - float: Current learning rate. - """ - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - assert max_progress >= self.start - - if max_progress == self.start: - return base_lr - - # Before 'start', fix lr; After 'start', linearly update lr. - factor = (max(0, progress - self.start) // self.interval) / ( - (max_progress - self.start) // self.interval) - return base_lr + (self.target_lr - base_lr) * factor - - -@HOOKS.register_module() -class ReduceLrUpdaterHook(LrUpdaterHook): - """ReduceLROnPlateau Scheduler. - - Reduce learning rate when a metric has stopped improving. This scheduler - reads a metrics quantity and if no improvement is seen for a 'patience' - number of epochs, the learning rate is reduced. - - Args: - val_metric (str, optional): Metrics to be evaluated. If val_metric is - None, the metrics will be loss value. Default: None. - mode (str, optional): One of `min`, `max`. In `min` mode, lr will - be reduced when the quantity monitored has stopped - decreasing; in `max` mode it will be reduced when the - quantity monitored has stopped increasing. Default: 'min'. - factor (float, optional): Factor by which the learning rate will be - reduced. new_lr = lr * factor. Default: 0.1. - patience (int, optional): Number of epochs with no improvement after - which learning rate will be reduced. For example, if - `patience = 2`, then we will ignore the first 2 epochs - with no improvement, and will only decrease the LR after the - 3rd epoch if the loss still hasn't improved then. - Default: 10. - threshold (float, optional): Threshold for measuring the new optimum, - to only focus on significant changes. Default: 1e-4. - threshold_mode (str, optional): One of `rel`, `abs`. In `rel` mode, - dynamic_threshold = best * ( 1 + threshold ) in 'max' - mode or best * ( 1 - threshold ) in `min` mode. - In `abs` mode, dynamic_threshold = best + threshold in - `max` mode or best - threshold in `min` mode. Default: 'rel'. - cooldown (int, optional): Number of epochs to wait before resuming - normal operation after lr has been reduced. Default: 0. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. - Default: 0. - eps (float, optional): Minimal decay applied to lr. If the difference - between new and old lr is smaller than eps, the update is - ignored. Default: 1e-8. - verbose (bool): If ``True``, prints a message to stdout for - each update. Default: ``False``. - epoch_base_valid (None | Bool): Whether use epoch base valid. - If `None`, follow `by_epoch` (inherited from `LrUpdaterHook`). - Default: None. - """ - - def __init__(self, - val_metric: str = None, - mode: str = 'min', - factor: float = 0.1, - patience: int = 10, - threshold: float = 1e-4, - threshold_mode: str = 'rel', - cooldown: int = 0, - min_lr: float = 0., - eps: float = 1e-8, - verbose: bool = False, - epoch_base_valid=None, - **kwargs): - - self.val_metric = val_metric - - if mode not in ['min', 'max']: - raise ValueError( - 'mode must be one of "min" or "max", instead got {mode}') - self.mode = mode - - if factor >= 1.0 or factor < 0: - raise ValueError('Factor should be < 1.0 and >=0') - self.factor = factor - - self.patience = patience - self.threshold = threshold - - if threshold_mode not in ['rel', 'abs']: - raise ValueError('thresh_mode must be one of "rel" or "abs",' - f'instead got {threshold_mode}') - self.threshold_mode = threshold_mode - - self.cooldown = cooldown - self.cooldown_counter = 0 - self.best = None - self.num_bad_epochs = None - self.mode_worse = None # the worse value for the chosen mode - self.min_lr = min_lr - self.eps = eps - self.verbose = verbose - self.last_epoch = 0 - self._init_is_better(self.mode) - self._reset() - - super().__init__(**kwargs) - if epoch_base_valid is None: - self.epoch_base_valid = self.by_epoch - else: - self.epoch_base_valid = epoch_base_valid - - def get_lr(self, regular_lr, optimizer_name): - if self.num_bad_epochs > self.patience: - self.cooldown_counter = self.cooldown - self.num_bad_epochs = 0 - if regular_lr - regular_lr * self.factor > self.eps: - new_lr = max(regular_lr * self.factor, self.min_lr) - if self.verbose: - print(f'Reducing learning rate of {optimizer_name} from ' - f'{regular_lr:.4e} to {new_lr:.4e}.') - else: - new_lr = regular_lr - return new_lr - else: - return regular_lr - - def get_regular_lr(self, runner): - if not self.regular_lr: - self.regular_lr = self.base_lr - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(_regular_lr, k) - for _regular_lr in self.regular_lr[k] - ] - lr_groups.update({k: _lr_group}) - else: - lr_groups = [ - self.get_lr(_regular_lr, 'generator') - for _regular_lr in self.regular_lr - ] - self.regular_lr = lr_groups - return lr_groups - - def _init_is_better(self, mode): - if mode == 'min': - self.mode_worse = float('inf') - else: - self.mode_worse = float('-inf') - - def _reset(self): - self.best = self.mode_worse - self.cooldown_counter = 0 - self.num_bad_epochs = 0 - - def is_better(self, a, best): - if self.mode == 'min' and self.threshold_mode == 'rel': - rel_epsilon = 1. - self.threshold - return a < best * rel_epsilon - elif self.mode == 'min' and self.threshold_mode == 'abs': - return a < best - self.threshold - elif self.mode == 'max' and self.threshold_mode == 'rel': - rel_epsilon = 1. + self.threshold - return a > best * rel_epsilon - else: - return a > best + self.threshold - - @property - def in_cooldown(self): - return self.cooldown_counter > 0 - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - cur_epoch = runner.epoch - if self.warmup is not None and self.warmup_by_epoch: - if cur_epoch <= self.warmup_epochs: - return - # If val_metric is None, we check training loss to reduce learning - # rate. - if self.val_metric is None: - current = runner.outputs['log_vars']['loss'] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'train_epoch--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') - - def after_train_iter(self, runner): - if self.by_epoch: - return - cur_iter = runner.iter - if self.warmup_epochs is not None and cur_iter <= self.warmup_iters: - return - # If val_metric is None, we check training loss to reduce learning - # rate. - if self.val_metric is None: - current = runner.outputs['log_vars']['loss'] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'train_iter--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') - - def after_val_epoch(self, runner): - if not self.by_epoch and not self.epoch_base_valid: - return - cur_epoch = runner.epoch - if self.warmup is not None and self.warmup_by_epoch: - if cur_epoch <= self.warmup_epochs: - return - # If val_metric is not None, we check its value to reduce learning - # rate. - if self.val_metric is not None: - current = runner.log_buffer.output[self.val_metric] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'val_epoch--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') - - def after_val_iter(self, runner): - if self.by_epoch or self.epoch_base_valid: - return - cur_iter = runner.iter - if self.warmup_epochs is not None and cur_iter <= self.warmup_iters: - return - # If val_metric is not None, we check its value to reduce learning - # rate. - if self.val_metric is not None: - current = runner.eval_result[self.val_metric] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'val_iter--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/utils/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/utils/__init__.py deleted file mode 100755 index f894e827c..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/utils/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .dist_utils import sync_random_seed - -__all__ = ['sync_random_seed'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/utils/dist_utils.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/core/utils/dist_utils.py deleted file mode 100755 index 8a3af5bb0..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/core/utils/dist_utils.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def sync_random_seed(seed=None, device='cuda'): - """Make sure different ranks share the same seed. - All workers must call this function, otherwise it will deadlock. - This method is generally used in `DistributedSampler`, - because the seed should be identical across all processes - in the distributed group. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is None: - seed = np.random.randint(2**31) - assert isinstance(seed, int) - - rank, world_size = get_dist_info() - - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/__init__.py deleted file mode 100755 index c364917d6..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .base_dataset import BaseDataset -from .base_sr_dataset import BaseSRDataset -from .builder import build_dataloader, build_dataset -from .dataset_wrappers import RepeatDataset -from .registry import DATASETS, PIPELINES -from .sr_reds_multiple_gt_dataset import SRREDSMultipleGTDataset - diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/base_dataset.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/base_dataset.py deleted file mode 100755 index c8ffea6eb..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/base_dataset.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -from abc import ABCMeta, abstractmethod - -from torch.utils.data import Dataset - -from .pipelines import Compose - - -class BaseDataset(Dataset, metaclass=ABCMeta): - """Base class for datasets. - - All datasets should subclass it. - All subclasses should overwrite: - - ``load_annotations``, supporting to load information and generate - image lists. - - Args: - pipeline (list[dict | callable]): A sequence of data transforms. - test_mode (bool): If True, the dataset will work in test mode. - Otherwise, in train mode. - """ - - def __init__(self, pipeline, test_mode=False): - super().__init__() - self.test_mode = test_mode - self.pipeline = Compose(pipeline) - - @abstractmethod - def load_annotations(self): - """Abstract function for loading annotation. - - All subclasses should overwrite this function - """ - - def prepare_train_data(self, idx): - """Prepare training data. - - Args: - idx (int): Index of the training batch data. - - Returns: - dict: Returned training batch. - """ - results = copy.deepcopy(self.data_infos[idx]) - return self.pipeline(results) - - def prepare_test_data(self, idx): - """Prepare testing data. - - Args: - idx (int): Index for getting each testing batch. - - Returns: - Tensor: Returned testing batch. - """ - results = copy.deepcopy(self.data_infos[idx]) - return self.pipeline(results) - - def __len__(self): - """Length of the dataset. - - Returns: - int: Length of the dataset. - """ - return len(self.data_infos) - - def __getitem__(self, idx): - """Get item at each call. - - Args: - idx (int): Index for getting each item. - """ - if self.test_mode: - return self.prepare_test_data(idx) - - return self.prepare_train_data(idx) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/base_sr_dataset.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/base_sr_dataset.py deleted file mode 100755 index 6e8115056..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/base_sr_dataset.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import os.path as osp -from collections import defaultdict -from pathlib import Path - -from mmcv import scandir - -from .base_dataset import BaseDataset - -IMG_EXTENSIONS = ('.jpg', '.JPG', '.jpeg', '.JPEG', '.png', '.PNG', '.ppm', - '.PPM', '.bmp', '.BMP', '.tif', '.TIF', '.tiff', '.TIFF') - - -class BaseSRDataset(BaseDataset): - """Base class for super resolution datasets. - """ - - def __init__(self, pipeline, scale, test_mode=False): - super().__init__(pipeline, test_mode) - self.scale = scale - - @staticmethod - def scan_folder(path): - """Obtain image path list (including sub-folders) from a given folder. - - Args: - path (str | :obj:`Path`): Folder path. - - Returns: - list[str]: image list obtained form given folder. - """ - - if isinstance(path, (str, Path)): - path = str(path) - else: - raise TypeError("'path' must be a str or a Path object, " - f'but received {type(path)}.') - - images = list(scandir(path, suffix=IMG_EXTENSIONS, recursive=True)) - images = [osp.join(path, v) for v in images] - assert images, f'{path} has no valid image file.' - return images - - def __getitem__(self, idx): - """Get item at each call. - - Args: - idx (int): Index for getting each item. - """ - results = copy.deepcopy(self.data_infos[idx]) - results['scale'] = self.scale - return self.pipeline(results) - - def evaluate(self, results, logger=None): - """Evaluate with different metrics. - - Args: - results (list[tuple]): The output of forward_test() of the model. - - Return: - dict: Evaluation results dict. - """ - if not isinstance(results, list): - raise TypeError(f'results must be a list, but got {type(results)}') - assert len(results) == len(self), ( - 'The length of results is not equal to the dataset len: ' - f'{len(results)} != {len(self)}') - - results = [res['eval_result'] for res in results] # a list of dict - eval_result = defaultdict(list) # a dict of list - - for res in results: - for metric, val in res.items(): - eval_result[metric].append(val) - for metric, val_list in eval_result.items(): - assert len(val_list) == len(self), ( - f'Length of evaluation result of {metric} is {len(val_list)}, ' - f'should be {len(self)}') - - # average the results - eval_result = { - metric: sum(values) / len(self) - for metric, values in eval_result.items() - } - - return eval_result diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/builder.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/builder.py deleted file mode 100755 index 4414d375e..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/builder.py +++ /dev/null @@ -1,181 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import platform -import random -from functools import partial - -import numpy as np -import torch -from mmcv.parallel import collate -from mmcv.runner import get_dist_info -from mmcv.utils import build_from_cfg -from packaging import version -from torch.utils.data import ConcatDataset, DataLoader - -from .dataset_wrappers import RepeatDataset -from .registry import DATASETS -from .samplers import DistributedSampler - -if platform.system() != 'Windows': - # https://github.com/pytorch/pytorch/issues/973 - import resource - rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) - base_soft_limit = rlimit[0] - hard_limit = rlimit[1] - soft_limit = min(max(4096, base_soft_limit), hard_limit) - resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) - - -def _concat_dataset(cfg, default_args=None): - """Concat datasets with different ann_file but the same type. - - Args: - cfg (dict): The config of dataset. - default_args (dict, optional): Default initialization arguments. - Default: None. - - Returns: - Dataset: The concatenated dataset. - """ - ann_files = cfg['ann_file'] - - datasets = [] - num_dset = len(ann_files) - for i in range(num_dset): - data_cfg = copy.deepcopy(cfg) - data_cfg['ann_file'] = ann_files[i] - datasets.append(build_dataset(data_cfg, default_args)) - - return ConcatDataset(datasets) - - -def build_dataset(cfg, default_args=None): - """Build a dataset from config dict. - - It supports a variety of dataset config. If ``cfg`` is a Sequential (list - or dict), it will be a concatenated dataset of the datasets specified by - the Sequential. If it is a ``RepeatDataset``, then it will repeat the - dataset ``cfg['dataset']`` for ``cfg['times']`` times. If the ``ann_file`` - of the dataset is a Sequential, then it will build a concatenated dataset - with the same dataset type but different ``ann_file``. - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - default_args (dict, optional): Default initialization arguments. - Default: None. - - Returns: - Dataset: The constructed dataset. - """ - if isinstance(cfg, (list, tuple)): - dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) - elif cfg['type'] == 'RepeatDataset': - dataset = RepeatDataset( - build_dataset(cfg['dataset'], default_args), cfg['times']) - elif isinstance(cfg.get('ann_file'), (list, tuple)): - dataset = _concat_dataset(cfg, default_args) - else: - dataset = build_from_cfg(cfg, DATASETS, default_args) - - return dataset - - -def build_dataloader(dataset, - samples_per_gpu, - workers_per_gpu, - num_gpus=1, - dist=True, - shuffle=True, - seed=None, - drop_last=False, - pin_memory=True, - persistent_workers=True, - **kwargs): - """Build PyTorch DataLoader. - - In distributed training, each GPU/process has a dataloader. - In non-distributed training, there is only one dataloader for all GPUs. - - Args: - dataset (:obj:`Dataset`): A PyTorch dataset. - samples_per_gpu (int): Number of samples on each GPU, i.e., - batch size of each GPU. - workers_per_gpu (int): How many subprocesses to use for data - loading for each GPU. - num_gpus (int): Number of GPUs. Only used in non-distributed - training. Default: 1. - dist (bool): Distributed training/test or not. Default: True. - shuffle (bool): Whether to shuffle the data at every epoch. - Default: True. - seed (int | None): Seed to be used. Default: None. - drop_last (bool): Whether to drop the last incomplete batch in epoch. - Default: False - pin_memory (bool): Whether to use pin_memory in DataLoader. - Default: True - persistent_workers (bool): If True, the data loader will not shutdown - the worker processes after a dataset has been consumed once. - This allows to maintain the workers Dataset instances alive. - The argument also has effect in PyTorch>=1.7.0. - Default: True - kwargs (dict, optional): Any keyword argument to be used to initialize - DataLoader. - - Returns: - DataLoader: A PyTorch dataloader. - """ - rank, world_size = get_dist_info() - if dist: - sampler = DistributedSampler( - dataset, - world_size, - rank, - shuffle=shuffle, - samples_per_gpu=samples_per_gpu, - seed=seed) - shuffle = False - batch_size = samples_per_gpu - num_workers = workers_per_gpu - else: - sampler = None - batch_size = num_gpus * samples_per_gpu - num_workers = num_gpus * workers_per_gpu - - init_fn = partial( - worker_init_fn, num_workers=num_workers, rank=rank, - seed=seed) if seed is not None else None - - if version.parse(torch.__version__) >= version.parse('1.7.0'): - kwargs['persistent_workers'] = persistent_workers - - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - **kwargs) - - return data_loader - - -def worker_init_fn(worker_id, num_workers, rank, seed): - """Function to initialize each worker. - - The seed of each worker equals to - ``num_worker * rank + worker_id + user_seed``. - - Args: - worker_id (int): Id for each worker. - num_workers (int): Number of workers. - rank (int): Rank in distributed training. - seed (int): Random seed. - """ - - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/dataset_wrappers.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/dataset_wrappers.py deleted file mode 100755 index 3dbca31ea..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/dataset_wrappers.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import DATASETS - - -@DATASETS.register_module() -class RepeatDataset: - """A wrapper of repeated dataset. - - The length of repeated dataset will be `times` larger than the original - dataset. This is useful when the data loading time is long but the dataset - is small. Using RepeatDataset can reduce the data loading time between - epochs. - - Args: - dataset (:obj:`Dataset`): The dataset to be repeated. - times (int): Repeat times. - """ - - def __init__(self, dataset, times): - self.dataset = dataset - self.times = times - - self._ori_len = len(self.dataset) - - def __getitem__(self, idx): - """Get item at each call. - - Args: - idx (int): Index for getting each item. - """ - return self.dataset[idx % self._ori_len] - - def __len__(self): - """Length of the dataset. - - Returns: - int: Length of the dataset. - """ - return self.times * self._ori_len diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/__init__.py deleted file mode 100755 index e903c0403..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .augmentation import (BinarizeImage, ColorJitter, CopyValues, Flip, - GenerateFrameIndices, - GenerateFrameIndiceswithPadding, - GenerateSegmentIndices, MirrorSequence, Pad, - Quantize, RandomAffine, RandomJitter, - RandomMaskDilation, RandomTransposeHW, Resize, - TemporalReverse, UnsharpMasking) -from .compose import Compose -from .crop import (Crop, CropAroundCenter, CropAroundFg, CropAroundUnknown, - CropLike, FixedCrop, ModCrop, PairedRandomCrop, - RandomResizedCrop) -from .formating import (Collect, FormatTrimap, GetMaskedImage, ImageToTensor, - ToTensor) -from .loading import (GetSpatialDiscountMask, LoadImageFromFile, - LoadImageFromFileList, LoadMask, LoadPairedImageFromFile, - RandomLoadResizeBg) -from .normalization import Normalize, RescaleToZeroOne - diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/augmentation.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/augmentation.py deleted file mode 100755 index e16f80a1c..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/augmentation.py +++ /dev/null @@ -1,1332 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import numbers -import os -import os.path as osp -import random - -import cv2 -import mmcv -import numpy as np -import torch -import torchvision.transforms as transforms -from PIL import Image - -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class Resize: - """Resize data to a specific size for training or resize the images to fit - the network input regulation for testing. - - When used for resizing images to fit network input regulation, the case is - that a network may have several downsample and then upsample operation, - then the input height and width should be divisible by the downsample - factor of the network. - For example, the network would downsample the input for 5 times with - stride 2, then the downsample factor is 2^5 = 32 and the height - and width should be divisible by 32. - - Required keys are the keys in attribute "keys", added or modified keys are - "keep_ratio", "scale_factor", "interpolation" and the - keys in attribute "keys". - - All keys in "keys" should have the same shape. "test_trans" is used to - record the test transformation to align the input's shape. - - Args: - keys (list[str]): The images to be resized. - scale (float | tuple[int]): If scale is tuple[int], target spatial - size (h, w). Otherwise, target spatial size is scaled by input - size. - Note that when it is used, `size_factor` and `max_size` are - useless. Default: None - keep_ratio (bool): If set to True, images will be resized without - changing the aspect ratio. Otherwise, it will resize images to a - given size. Default: False. - Note that it is used togher with `scale`. - size_factor (int): Let the output shape be a multiple of size_factor. - Default:None. - Note that when it is used, `scale` should be set to None and - `keep_ratio` should be set to False. - max_size (int): The maximum size of the longest side of the output. - Default:None. - Note that it is used togher with `size_factor`. - interpolation (str): Algorithm used for interpolation: - "nearest" | "bilinear" | "bicubic" | "area" | "lanczos". - Default: "bilinear". - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. - Default: None. - output_keys (list[str] | None): The resized images. Default: None - Note that if it is not `None`, its length should be equal to keys. - """ - - def __init__(self, - keys, - scale=None, - keep_ratio=False, - size_factor=None, - max_size=None, - interpolation='bilinear', - backend=None, - output_keys=None): - assert keys, 'Keys should not be empty.' - if output_keys: - assert len(output_keys) == len(keys) - else: - output_keys = keys - if size_factor: - assert scale is None, ('When size_factor is used, scale should ', - f'be None. But received {scale}.') - assert keep_ratio is False, ('When size_factor is used, ' - 'keep_ratio should be False.') - if max_size: - assert size_factor is not None, ( - 'When max_size is used, ' - f'size_factor should also be set. But received {size_factor}.') - if isinstance(scale, float): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - elif mmcv.is_tuple_of(scale, int): - max_long_edge = max(scale) - max_short_edge = min(scale) - if max_short_edge == -1: - # assign np.inf to long edge for rescaling short edge later. - scale = (np.inf, max_long_edge) - elif scale is not None: - raise TypeError( - f'Scale must be None, float or tuple of int, but got ' - f'{type(scale)}.') - self.keys = keys - self.output_keys = output_keys - self.scale = scale - self.size_factor = size_factor - self.max_size = max_size - self.keep_ratio = keep_ratio - self.interpolation = interpolation - self.backend = backend - - def _resize(self, img): - if self.keep_ratio: - img, self.scale_factor = mmcv.imrescale( - img, - self.scale, - return_scale=True, - interpolation=self.interpolation, - backend=self.backend) - else: - img, w_scale, h_scale = mmcv.imresize( - img, - self.scale, - return_scale=True, - interpolation=self.interpolation, - backend=self.backend) - self.scale_factor = np.array((w_scale, h_scale), dtype=np.float32) - return img - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - if self.size_factor: - h, w = results[self.keys[0]].shape[:2] - new_h = h - (h % self.size_factor) - new_w = w - (w % self.size_factor) - if self.max_size: - new_h = min(self.max_size - (self.max_size % self.size_factor), - new_h) - new_w = min(self.max_size - (self.max_size % self.size_factor), - new_w) - self.scale = (new_w, new_h) - for key, out_key in zip(self.keys, self.output_keys): - results[out_key] = self._resize(results[key]) - if len(results[out_key].shape) == 2: - results[out_key] = np.expand_dims(results[out_key], axis=2) - - results['scale_factor'] = self.scale_factor - results['keep_ratio'] = self.keep_ratio - results['interpolation'] = self.interpolation - results['backend'] = self.backend - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(keys={self.keys}, output_keys={self.output_keys}, ' - f'scale={self.scale}, ' - f'keep_ratio={self.keep_ratio}, size_factor={self.size_factor}, ' - f'max_size={self.max_size}, interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class RandomRotation: - """Rotate the image by a randomly-chosen angle, measured in degree. - - Args: - keys (list[str]): The images to be rotated. - degrees (tuple[float] | tuple[int] | float | int): If it is a tuple, - it represents a range (min, max). If it is a float or int, - the range is constructed as (-degrees, degrees). - """ - - def __init__(self, keys, degrees): - if isinstance(degrees, (int, float)): - if degrees < 0.0: - raise ValueError('Degrees must be positive if it is a number.') - else: - degrees = (-degrees, degrees) - elif not mmcv.is_tuple_of(degrees, (int, float)): - raise TypeError(f'Degrees must be float | int or tuple of float | ' - 'int, but got ' - f'{type(degrees)}.') - - self.keys = keys - self.degrees = degrees - - def __call__(self, results): - angle = random.uniform(self.degrees[0], self.degrees[1]) - - for k in self.keys: - results[k] = mmcv.imrotate(results[k], angle) - if results[k].ndim == 2: - results[k] = np.expand_dims(results[k], axis=2) - results['degrees'] = self.degrees - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, degrees={self.degrees})') - return repr_str - - -@PIPELINES.register_module() -class Flip: - """Flip the input data with a probability. - - Reverse the order of elements in the given data with a specific direction. - The shape of the data is preserved, but the elements are reordered. - Required keys are the keys in attributes "keys", added or modified keys are - "flip", "flip_direction" and the keys in attributes "keys". - It also supports flipping a list of images with the same flip. - - Args: - keys (list[str]): The images to be flipped. - flip_ratio (float): The propability to flip the images. - direction (str): Flip images horizontally or vertically. Options are - "horizontal" | "vertical". Default: "horizontal". - """ - _directions = ['horizontal', 'vertical'] - - def __init__(self, keys, flip_ratio=0.5, direction='horizontal'): - if direction not in self._directions: - raise ValueError(f'Direction {direction} is not supported.' - f'Currently support ones are {self._directions}') - self.keys = keys - self.flip_ratio = flip_ratio - self.direction = direction - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - flip = np.random.random() < self.flip_ratio - - if flip: - for key in self.keys: - if isinstance(results[key], list): - for v in results[key]: - mmcv.imflip_(v, self.direction) - else: - mmcv.imflip_(results[key], self.direction) - - results['flip'] = flip - results['flip_direction'] = self.direction - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, flip_ratio={self.flip_ratio}, ' - f'direction={self.direction})') - return repr_str - - -@PIPELINES.register_module() -class Pad: - """Pad the images to align with network downsample factor for testing. - - See `Reshape` for more explanation. `numpy.pad` is used for the pad - operation. - Required keys are the keys in attribute "keys", added or - modified keys are "test_trans" and the keys in attribute - "keys". All keys in "keys" should have the same shape. "test_trans" is used - to record the test transformation to align the input's shape. - - Args: - keys (list[str]): The images to be padded. - ds_factor (int): Downsample factor of the network. The height and - weight will be padded to a multiple of ds_factor. Default: 32. - kwargs (option): any keyword arguments to be passed to `numpy.pad`. - """ - - def __init__(self, keys, ds_factor=32, **kwargs): - self.keys = keys - self.ds_factor = ds_factor - self.kwargs = kwargs - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - h, w = results[self.keys[0]].shape[:2] - - new_h = self.ds_factor * ((h - 1) // self.ds_factor + 1) - new_w = self.ds_factor * ((w - 1) // self.ds_factor + 1) - - pad_h = new_h - h - pad_w = new_w - w - if new_h != h or new_w != w: - pad_width = ((0, pad_h), (0, pad_w), (0, 0)) - for key in self.keys: - results[key] = np.pad(results[key], - pad_width[:results[key].ndim], - **self.kwargs) - results['pad'] = (pad_h, pad_w) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - kwargs_str = ', '.join( - [f'{key}={val}' for key, val in self.kwargs.items()]) - repr_str += (f'(keys={self.keys}, ds_factor={self.ds_factor}, ' - f'{kwargs_str})') - return repr_str - - -@PIPELINES.register_module() -class RandomAffine: - """Apply random affine to input images. - - This class is adopted from - https://github.com/pytorch/vision/blob/v0.5.0/torchvision/transforms/ - transforms.py#L1015 - It should be noted that in - https://github.com/Yaoyi-Li/GCA-Matting/blob/master/dataloader/ - data_generator.py#L70 - random flip is added. See explanation of `flip_ratio` below. - Required keys are the keys in attribute "keys", modified keys - are keys in attribute "keys". - - Args: - keys (Sequence[str]): The images to be affined. - degrees (float | tuple[float]): Range of degrees to select from. If it - is a float instead of a tuple like (min, max), the range of degrees - will be (-degrees, +degrees). Set to 0 to deactivate rotations. - translate (tuple, optional): Tuple of maximum absolute fraction for - horizontal and vertical translations. For example translate=(a, b), - then horizontal shift is randomly sampled in the range - -img_width * a < dx < img_width * a and vertical shift is randomly - sampled in the range -img_height * b < dy < img_height * b. - Default: None. - scale (tuple, optional): Scaling factor interval, e.g (a, b), then - scale is randomly sampled from the range a <= scale <= b. - Default: None. - shear (float | tuple[float], optional): Range of shear degrees to - select from. If shear is a float, a shear parallel to the x axis - and a shear parallel to the y axis in the range (-shear, +shear) - will be applied. Else if shear is a tuple of 2 values, a x-axis - shear and a y-axis shear in (shear[0], shear[1]) will be applied. - Default: None. - flip_ratio (float, optional): Probability of the image being flipped. - The flips in horizontal direction and vertical direction are - independent. The image may be flipped in both directions. - Default: None. - """ - - def __init__(self, - keys, - degrees, - translate=None, - scale=None, - shear=None, - flip_ratio=None): - self.keys = keys - if isinstance(degrees, numbers.Number): - assert degrees >= 0, ('If degrees is a single number, ' - 'it must be positive.') - self.degrees = (-degrees, degrees) - else: - assert isinstance(degrees, tuple) and len(degrees) == 2, \ - 'degrees should be a tuple and it must be of length 2.' - self.degrees = degrees - - if translate is not None: - assert isinstance(translate, tuple) and len(translate) == 2, \ - 'translate should be a tuple and it must be of length 2.' - for t in translate: - assert 0.0 <= t <= 1.0, ('translation values should be ' - 'between 0 and 1.') - self.translate = translate - - if scale is not None: - assert isinstance(scale, tuple) and len(scale) == 2, \ - 'scale should be a tuple and it must be of length 2.' - for s in scale: - assert s > 0, 'scale values should be positive.' - self.scale = scale - - if shear is not None: - if isinstance(shear, numbers.Number): - assert shear >= 0, ('If shear is a single number, ' - 'it must be positive.') - self.shear = (-shear, shear) - else: - assert isinstance(shear, tuple) and len(shear) == 2, \ - 'shear should be a tuple and it must be of length 2.' - # X-Axis and Y-Axis shear with (min, max) - self.shear = shear - else: - self.shear = shear - - if flip_ratio is not None: - assert isinstance(flip_ratio, - float), 'flip_ratio should be a float.' - self.flip_ratio = flip_ratio - else: - self.flip_ratio = 0 - - @staticmethod - def _get_params(degrees, translate, scale_ranges, shears, flip_ratio, - img_size): - """Get parameters for affine transformation. - - Returns: - paras (tuple): Params to be passed to the affine transformation. - """ - angle = np.random.uniform(degrees[0], degrees[1]) - if translate is not None: - max_dx = translate[0] * img_size[0] - max_dy = translate[1] * img_size[1] - translations = (np.round(np.random.uniform(-max_dx, max_dx)), - np.round(np.random.uniform(-max_dy, max_dy))) - else: - translations = (0, 0) - - if scale_ranges is not None: - scale = (np.random.uniform(scale_ranges[0], scale_ranges[1]), - np.random.uniform(scale_ranges[0], scale_ranges[1])) - else: - scale = (1.0, 1.0) - - if shears is not None: - shear = np.random.uniform(shears[0], shears[1]) - else: - shear = 0.0 - - # Because `flip` is used as a multiplier in line 479 and 480, - # so -1 stands for flip and 1 stands for no flip. Thus `flip` - # should be an 'inverse' flag as the result of the comparison. - # See https://github.com/open-mmlab/mmediting/pull/799 for more detail - flip = (np.random.rand(2) > flip_ratio).astype(np.int32) * 2 - 1 - - return angle, translations, scale, shear, flip - - @staticmethod - def _get_inverse_affine_matrix(center, angle, translate, scale, shear, - flip): - """Helper method to compute inverse matrix for affine transformation. - - As it is explained in PIL.Image.rotate, we need compute INVERSE of - affine transformation matrix: M = T * C * RSS * C^-1 where - T is translation matrix: - [1, 0, tx | 0, 1, ty | 0, 0, 1]; - C is translation matrix to keep center: - [1, 0, cx | 0, 1, cy | 0, 0, 1]; - RSS is rotation with scale and shear matrix. - - It is different from the original function in torchvision. - 1. The order are changed to flip -> scale -> rotation -> shear. - 2. x and y have different scale factors. - RSS(shear, a, scale, f) = - [ cos(a + shear)*scale_x*f -sin(a + shear)*scale_y 0] - [ sin(a)*scale_x*f cos(a)*scale_y 0] - [ 0 0 1] - Thus, the inverse is M^-1 = C * RSS^-1 * C^-1 * T^-1. - """ - - angle = math.radians(angle) - shear = math.radians(shear) - scale_x = 1.0 / scale[0] * flip[0] - scale_y = 1.0 / scale[1] * flip[1] - - # Inverted rotation matrix with scale and shear - d = math.cos(angle + shear) * math.cos(angle) + math.sin( - angle + shear) * math.sin(angle) - matrix = [ - math.cos(angle) * scale_x, - math.sin(angle + shear) * scale_x, 0, -math.sin(angle) * scale_y, - math.cos(angle + shear) * scale_y, 0 - ] - matrix = [m / d for m in matrix] - - # Apply inverse of translation and of center translation: - # RSS^-1 * C^-1 * T^-1 - matrix[2] += matrix[0] * (-center[0] - translate[0]) + matrix[1] * ( - -center[1] - translate[1]) - matrix[5] += matrix[3] * (-center[0] - translate[0]) + matrix[4] * ( - -center[1] - translate[1]) - - # Apply center translation: C * RSS^-1 * C^-1 * T^-1 - matrix[2] += center[0] - matrix[5] += center[1] - - return matrix - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - h, w = results[self.keys[0]].shape[:2] - # if image is too small, set degree to 0 to reduce introduced dark area - if np.maximum(h, w) < 1024: - params = self._get_params((0, 0), self.translate, self.scale, - self.shear, self.flip_ratio, (h, w)) - else: - params = self._get_params(self.degrees, self.translate, self.scale, - self.shear, self.flip_ratio, (h, w)) - - center = (w * 0.5 - 0.5, h * 0.5 - 0.5) - M = self._get_inverse_affine_matrix(center, *params) - M = np.array(M).reshape((2, 3)) - - for key in self.keys: - results[key] = cv2.warpAffine( - results[key], - M, (w, h), - flags=cv2.INTER_NEAREST + cv2.WARP_INVERSE_MAP) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, degrees={self.degrees}, ' - f'translate={self.translate}, scale={self.scale}, ' - f'shear={self.shear}, flip_ratio={self.flip_ratio})') - return repr_str - - -@PIPELINES.register_module() -class RandomJitter: - """Randomly jitter the foreground in hsv space. - - The jitter range of hue is adjustable while the jitter ranges of saturation - and value are adaptive to the images. Side effect: the "fg" image will be - converted to `np.float32`. - Required keys are "fg" and "alpha", modified key is "fg". - - Args: - hue_range (float | tuple[float]): Range of hue jittering. If it is a - float instead of a tuple like (min, max), the range of hue - jittering will be (-hue_range, +hue_range). Default: 40. - """ - - def __init__(self, hue_range=40): - if isinstance(hue_range, numbers.Number): - assert hue_range >= 0, ('If hue_range is a single number, ' - 'it must be positive.') - self.hue_range = (-hue_range, hue_range) - else: - assert isinstance(hue_range, tuple) and len(hue_range) == 2, \ - 'hue_range should be a tuple and it must be of length 2.' - self.hue_range = hue_range - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - fg, alpha = results['fg'], results['alpha'] - - # convert to HSV space; - # convert to float32 image to keep precision during space conversion. - fg = mmcv.bgr2hsv(fg.astype(np.float32) / 255) - # Hue noise - hue_jitter = np.random.randint(self.hue_range[0], self.hue_range[1]) - fg[:, :, 0] = np.remainder(fg[:, :, 0] + hue_jitter, 360) - - # Saturation noise - sat_mean = fg[:, :, 1][alpha > 0].mean() - # jitter saturation within range (1.1 - sat_mean) * [-0.1, 0.1] - sat_jitter = (1.1 - sat_mean) * (np.random.rand() * 0.2 - 0.1) - sat = fg[:, :, 1] - sat = np.abs(sat + sat_jitter) - sat[sat > 1] = 2 - sat[sat > 1] - fg[:, :, 1] = sat - - # Value noise - val_mean = fg[:, :, 2][alpha > 0].mean() - # jitter value within range (1.1 - val_mean) * [-0.1, 0.1] - val_jitter = (1.1 - val_mean) * (np.random.rand() * 0.2 - 0.1) - val = fg[:, :, 2] - val = np.abs(val + val_jitter) - val[val > 1] = 2 - val[val > 1] - fg[:, :, 2] = val - # convert back to BGR space - fg = mmcv.hsv2bgr(fg) - results['fg'] = fg * 255 - - return results - - def __repr__(self): - return self.__class__.__name__ + f'hue_range={self.hue_range}' - - -@PIPELINES.register_module() -class ColorJitter: - """An interface for torch color jitter so that it can be invoked in - mmediting pipeline. - - Randomly change the brightness, contrast and saturation of an image. - Modified keys are the attributes specified in "keys". - - Args: - keys (list[str]): The images to be resized. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'rgb'. - - Notes: ``**kwards`` follows the args list of - ``torchvision.transforms.ColorJitter``. - - brightness (float or tuple of float (min, max)): How much to jitter - brightness. brightness_factor is chosen uniformly from - [max(0, 1 - brightness), 1 + brightness] or the given [min, max]. - Should be non negative numbers. - contrast (float or tuple of float (min, max)): How much to jitter - contrast. contrast_factor is chosen uniformly from - [max(0, 1 - contrast), 1 + contrast] or the given [min, max]. - Should be non negative numbers. - saturation (float or tuple of float (min, max)): How much to jitter - saturation. saturation_factor is chosen uniformly from - [max(0, 1 - saturation), 1 + saturation] or the given [min, max]. - Should be non negative numbers. - hue (float or tuple of float (min, max)): How much to jitter hue. - hue_factor is chosen uniformly from [-hue, hue] or the given - [min, max]. - Should have 0<= hue <= 0.5 or -0.5 <= min <= max <= 0.5. - """ - - def __init__(self, keys, channel_order='rgb', **kwargs): - assert keys, 'Keys should not be empty.' - assert 'to_rgb' not in kwargs, ( - '`to_rgb` is not support in ColorJitter, ' - "which is replaced by `channel_order` ('rgb' or 'bgr')") - - self.keys = keys - self.channel_order = channel_order - self.transform = transforms.ColorJitter(**kwargs) - - def _color_jitter(self, image, this_seed): - - if self.channel_order.lower() == 'bgr': - image = image[..., ::-1] - - image = Image.fromarray(image) - torch.manual_seed(this_seed) - image = self.transform(image) - image = np.asarray(image) - - if self.channel_order.lower() == 'bgr': - image = image[..., ::-1] - - return image - - def __call__(self, results): - - this_seed = random.randint(0, 2**32) - - for k in self.keys: - if isinstance(results[k], list): - results[k] = [ - self._color_jitter(v, this_seed) for v in results[k] - ] - else: - results[k] = self._color_jitter(results[k], this_seed) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, channel_order={self.channel_order}, ' - f'brightness={self.transform.brightness}, ' - f'contrast={self.transform.contrast}, ' - f'saturation={self.transform.saturation}, ' - f'hue={self.transform.hue})') - - return repr_str - - -class BinarizeImage: - """Binarize image. - - Args: - keys (Sequence[str]): The images to be binarized. - binary_thr (float): Threshold for binarization. - to_int (bool): If True, return image as int32, otherwise - return image as float32. - """ - - def __init__(self, keys, binary_thr, to_int=False): - self.keys = keys - self.binary_thr = binary_thr - self.to_int = to_int - - def _binarize(self, img): - type_ = np.float32 if not self.to_int else np.int32 - img = (img[..., :] > self.binary_thr).astype(type_) - - return img - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - results[k] = self._binarize(results[k]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, binary_thr={self.binary_thr}, ' - f'to_int={self.to_int})') - - return repr_str - - -@PIPELINES.register_module() -class RandomMaskDilation: - """Randomly dilate binary masks. - - Args: - keys (Sequence[str]): The images to be resized. - get_binary (bool): If True, according to binary_thr, reset final - output as binary mask. Otherwise, return masks directly. - binary_thr (float): Threshold for obtaining binary mask. - kernel_min (int): Min size of dilation kernel. - kernel_max (int): Max size of dilation kernel. - """ - - def __init__(self, keys, binary_thr=0., kernel_min=9, kernel_max=49): - self.keys = keys - self.kernel_min = kernel_min - self.kernel_max = kernel_max - self.binary_thr = binary_thr - - def _random_dilate(self, img): - kernel_size = np.random.randint(self.kernel_min, self.kernel_max + 1) - kernel = np.ones((kernel_size, kernel_size), dtype=np.uint8) - dilate_kernel_size = kernel_size - img_ = cv2.dilate(img, kernel, iterations=1) - - img_ = (img_ > self.binary_thr).astype(np.float32) - - return img_, dilate_kernel_size - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - results[k], d_kernel = self._random_dilate(results[k]) - if len(results[k].shape) == 2: - results[k] = np.expand_dims(results[k], axis=2) - results[k + '_dilate_kernel_size'] = d_kernel - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, kernel_min={self.kernel_min}, ' - f'kernel_max={self.kernel_max})') - - return repr_str - - -@PIPELINES.register_module() -class RandomTransposeHW: - """Randomly transpose images in H and W dimensions with a probability. - - (TransposeHW = horizontal flip + anti-clockwise rotatation by 90 degrees) - When used with horizontal/vertical flips, it serves as a way of rotation - augmentation. - It also supports randomly transposing a list of images. - - Required keys are the keys in attributes "keys", added or modified keys are - "transpose" and the keys in attributes "keys". - - Args: - keys (list[str]): The images to be transposed. - transpose_ratio (float): The propability to transpose the images. - """ - - def __init__(self, keys, transpose_ratio=0.5): - self.keys = keys - self.transpose_ratio = transpose_ratio - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - transpose = np.random.random() < self.transpose_ratio - - if transpose: - for key in self.keys: - if isinstance(results[key], list): - results[key] = [v.transpose(1, 0, 2) for v in results[key]] - else: - results[key] = results[key].transpose(1, 0, 2) - - results['transpose'] = transpose - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(keys={self.keys}, transpose_ratio={self.transpose_ratio})') - return repr_str - - -@PIPELINES.register_module() -class GenerateFrameIndiceswithPadding: - """Generate frame index with padding for REDS dataset and Vid4 dataset - during testing. - - Required keys: lq_path, gt_path, key, num_input_frames, max_frame_num - Added or modified keys: lq_path, gt_path - - Args: - padding (str): padding mode, one of - 'replicate' | 'reflection' | 'reflection_circle' | 'circle'. - - Examples: current_idx = 0, num_input_frames = 5 - The generated frame indices under different padding mode: - - replicate: [0, 0, 0, 1, 2] - reflection: [2, 1, 0, 1, 2] - reflection_circle: [4, 3, 0, 1, 2] - circle: [3, 4, 0, 1, 2] - - filename_tmpl (str): Template for file name. Default: '{:08d}'. - """ - - def __init__(self, padding, filename_tmpl='{:08d}'): - if padding not in ('replicate', 'reflection', 'reflection_circle', - 'circle'): - raise ValueError(f'Wrong padding mode {padding}.' - 'Should be "replicate", "reflection", ' - '"reflection_circle", "circle"') - self.padding = padding - self.filename_tmpl = filename_tmpl - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - clip_name, frame_name = results['key'].split(os.sep) - current_idx = int(frame_name) - max_frame_num = results['max_frame_num'] - 1 # start from 0 - num_input_frames = results['num_input_frames'] - num_pad = num_input_frames // 2 - - frame_list = [] - for i in range(current_idx - num_pad, current_idx + num_pad + 1): - if i < 0: - if self.padding == 'replicate': - pad_idx = 0 - elif self.padding == 'reflection': - pad_idx = -i - elif self.padding == 'reflection_circle': - pad_idx = current_idx + num_pad - i - else: - pad_idx = num_input_frames + i - elif i > max_frame_num: - if self.padding == 'replicate': - pad_idx = max_frame_num - elif self.padding == 'reflection': - pad_idx = max_frame_num * 2 - i - elif self.padding == 'reflection_circle': - pad_idx = (current_idx - num_pad) - (i - max_frame_num) - else: - pad_idx = i - num_input_frames - else: - pad_idx = i - frame_list.append(pad_idx) - - lq_path_root = results['lq_path'] - gt_path_root = results['gt_path'] - lq_paths = [ - osp.join(lq_path_root, clip_name, - f'{self.filename_tmpl.format(idx)}.png') - for idx in frame_list - ] - gt_paths = [osp.join(gt_path_root, clip_name, f'{frame_name}.png')] - results['lq_path'] = lq_paths - results['gt_path'] = gt_paths - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ + f"(padding='{self.padding}')" - return repr_str - - -@PIPELINES.register_module() -class GenerateFrameIndices: - """Generate frame index for REDS datasets. It also performs - temporal augmention with random interval. - - Required keys: lq_path, gt_path, key, num_input_frames - Added or modified keys: lq_path, gt_path, interval, reverse - - Args: - interval_list (list[int]): Interval list for temporal augmentation. - It will randomly pick an interval from interval_list and sample - frame index with the interval. - frames_per_clip(int): Number of frames per clips. Default: 99 for - REDS dataset. - """ - - def __init__(self, interval_list, frames_per_clip=99): - self.interval_list = interval_list - self.frames_per_clip = frames_per_clip - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - clip_name, frame_name = results['key'].split( - os.sep) # key example: 000/00000000 - center_frame_idx = int(frame_name) - num_half_frames = results['num_input_frames'] // 2 - - max_frame_num = results.get('max_frame_num', self.frames_per_clip + 1) - frames_per_clip = min(self.frames_per_clip, max_frame_num - 1) - - interval = np.random.choice(self.interval_list) - # ensure not exceeding the borders - start_frame_idx = center_frame_idx - num_half_frames * interval - end_frame_idx = center_frame_idx + num_half_frames * interval - while (start_frame_idx < 0) or (end_frame_idx > frames_per_clip): - center_frame_idx = np.random.randint(0, frames_per_clip + 1) - start_frame_idx = center_frame_idx - num_half_frames * interval - end_frame_idx = center_frame_idx + num_half_frames * interval - frame_name = f'{center_frame_idx:08d}' - neighbor_list = list( - range(center_frame_idx - num_half_frames * interval, - center_frame_idx + num_half_frames * interval + 1, interval)) - - lq_path_root = results['lq_path'] - gt_path_root = results['gt_path'] - lq_path = [ - osp.join(lq_path_root, clip_name, f'{v:08d}.png') - for v in neighbor_list - ] - gt_path = [osp.join(gt_path_root, clip_name, f'{frame_name}.png')] - results['lq_path'] = lq_path - results['gt_path'] = gt_path - results['interval'] = interval - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(interval_list={self.interval_list}, ' - f'frames_per_clip={self.frames_per_clip})') - return repr_str - - -@PIPELINES.register_module() -class TemporalReverse: - """Reverse frame lists for temporal augmentation. - - Required keys are the keys in attributes "lq" and "gt", - added or modified keys are "lq", "gt" and "reverse". - - Args: - keys (list[str]): The frame lists to be reversed. - reverse_ratio (float): The propability to reverse the frame lists. - Default: 0.5. - """ - - def __init__(self, keys, reverse_ratio=0.5): - self.keys = keys - self.reverse_ratio = reverse_ratio - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - reverse = np.random.random() < self.reverse_ratio - - if reverse: - for key in self.keys: - results[key].reverse() - - results['reverse'] = reverse - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(keys={self.keys}, reverse_ratio={self.reverse_ratio})' - return repr_str - - -@PIPELINES.register_module() -class GenerateSegmentIndices: - """Generate frame indices for a segment. It also performs temporal - augmention with random interval. - - Required keys: lq_path, gt_path, key, num_input_frames, sequence_length - Added or modified keys: lq_path, gt_path, interval, reverse - - Args: - interval_list (list[int]): Interval list for temporal augmentation. - It will randomly pick an interval from interval_list and sample - frame index with the interval. - start_idx (int): The index corresponds to the first frame in the - sequence. Default: 0. - filename_tmpl (str): Template for file name. Default: '{:08d}.png'. - """ - - def __init__(self, interval_list, start_idx=0, filename_tmpl='{:08d}.png'): - self.interval_list = interval_list - self.filename_tmpl = filename_tmpl - self.start_idx = start_idx - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - # key example: '000', 'calendar' (sequence name) - clip_name = results['key'] - interval = np.random.choice(self.interval_list) - - self.sequence_length = results['sequence_length'] - num_input_frames = results.get('num_input_frames', - self.sequence_length) - - # randomly select a frame as start - if self.sequence_length - num_input_frames * interval < 0: - raise ValueError('The input sequence is not long enough to ' - 'support the current choice of [interval] or ' - '[num_input_frames].') - start_frame_idx = np.random.randint( - 0, self.sequence_length - num_input_frames * interval + 1) - end_frame_idx = start_frame_idx + num_input_frames * interval - neighbor_list = list(range(start_frame_idx, end_frame_idx, interval)) - neighbor_list = [v + self.start_idx for v in neighbor_list] - - # add the corresponding file paths - lq_path_root = results['lq_path'] - gt_path_root = results['gt_path'] - lq_path = [ - osp.join(lq_path_root, clip_name, self.filename_tmpl.format(v)) - for v in neighbor_list - ] - gt_path = [ - osp.join(gt_path_root, clip_name, self.filename_tmpl.format(v)) - for v in neighbor_list - ] - - results['lq_path'] = lq_path - results['gt_path'] = gt_path - results['interval'] = interval - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(interval_list={self.interval_list})') - return repr_str - - -@PIPELINES.register_module() -class MirrorSequence: - """Extend short sequences (e.g. Vimeo-90K) by mirroring the sequences - - Given a sequence with N frames (x1, ..., xN), extend the sequence to - (x1, ..., xN, xN, ..., x1). - - Args: - keys (list[str]): The frame lists to be extended. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if isinstance(results[key], list): - results[key] = results[key] + results[key][::-1] - else: - raise TypeError('The input must be of class list[nparray]. ' - f'Got {type(results[key])}.') - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys})') - return repr_str - - -@PIPELINES.register_module() -class CopyValues: - """Copy the value of a source key to a destination key. - - - It does the following: results[dst_key] = results[src_key] for - (src_key, dst_key) in zip(src_keys, dst_keys). - - Added keys are the keys in the attribute "dst_keys". - - Args: - src_keys (list[str]): The source keys. - dst_keys (list[str]): The destination keys. - """ - - def __init__(self, src_keys, dst_keys): - - if not isinstance(src_keys, list) or not isinstance(dst_keys, list): - raise AssertionError('"src_keys" and "dst_keys" must be lists.') - - if len(src_keys) != len(dst_keys): - raise ValueError('"src_keys" and "dst_keys" should have the same' - 'number of elements.') - - self.src_keys = src_keys - self.dst_keys = dst_keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict with a key added/modified. - """ - for (src_key, dst_key) in zip(self.src_keys, self.dst_keys): - results[dst_key] = copy.deepcopy(results[src_key]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(src_keys={self.src_keys})') - repr_str += (f'(dst_keys={self.dst_keys})') - return repr_str - - -@PIPELINES.register_module() -class Quantize: - """Quantize and clip the image to [0, 1]. - - It is assumed that the the input has range [0, 1]. - - Modified keys are the attributes specified in "keys". - - Args: - keys (list[str]): The keys whose values are clipped. - """ - - def __init__(self, keys): - self.keys = keys - - def _quantize_clip(self, input_): - is_single_image = False - if isinstance(input_, np.ndarray): - is_single_image = True - input_ = [input_] - - # quantize and clip - input_ = [np.clip((v * 255.0).round(), 0, 255) / 255. for v in input_] - - if is_single_image: - input_ = input_[0] - - return input_ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict with the values of the specified keys are rounded - and clipped. - """ - - for key in self.keys: - results[key] = self._quantize_clip(results[key]) - - return results - - def __repr__(self): - return self.__class__.__name__ - - -@PIPELINES.register_module() -class UnsharpMasking: - """Apply unsharp masking to an image or a sequence of images. - - Args: - kernel_size (int): The kernel_size of the Gaussian kernel. - sigma (float): The standard deviation of the Gaussian. - weight (float): The weight of the "details" in the final output. - threshold (float): Pixel differences larger than this value are - regarded as "details". - keys (list[str]): The keys whose values are processed. - - Added keys are "xxx_unsharp", where "xxx" are the attributes specified - in "keys". - - """ - - def __init__(self, kernel_size, sigma, weight, threshold, keys): - if kernel_size % 2 == 0: - raise ValueError('kernel_size must be an odd number, but ' - f'got {kernel_size}.') - - self.kernel_size = kernel_size - self.sigma = sigma - self.weight = weight - self.threshold = threshold - self.keys = keys - - kernel = cv2.getGaussianKernel(kernel_size, sigma) - self.kernel = np.matmul(kernel, kernel.transpose()) - - def _unsharp_masking(self, imgs): - is_single_image = False - if isinstance(imgs, np.ndarray): - is_single_image = True - imgs = [imgs] - - outputs = [] - for img in imgs: - residue = img - cv2.filter2D(img, -1, self.kernel) - mask = np.float32(np.abs(residue) * 255 > self.threshold) - soft_mask = cv2.filter2D(mask, -1, self.kernel) - sharpened = np.clip(img + self.weight * residue, 0, 1) - - outputs.append(soft_mask * sharpened + (1 - soft_mask) * img) - - if is_single_image: - outputs = outputs[0] - - return outputs - - def __call__(self, results): - for key in self.keys: - results[f'{key}_unsharp'] = self._unsharp_masking(results[key]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, kernel_size={self.kernel_size}, ' - f'sigma={self.sigma}, weight={self.weight}, ' - f'threshold={self.threshold})') - return repr_str diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/compose.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/compose.py deleted file mode 100755 index 0ffb0fd57..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/compose.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -from mmcv.utils import build_from_cfg - -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class Compose: - """Compose a data pipeline with a sequence of transforms. - - Args: - transforms (list[dict | callable]): - Either config dicts of transforms or transform objects. - """ - - def __init__(self, transforms): - assert isinstance(transforms, Sequence) - self.transforms = [] - for transform in transforms: - if isinstance(transform, dict): - transform = build_from_cfg(transform, PIPELINES) - self.transforms.append(transform) - elif callable(transform): - self.transforms.append(transform) - else: - raise TypeError(f'transform must be callable or a dict, ' - f'but got {type(transform)}') - - def __call__(self, data): - """Call function. - - Args: - data (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for t in self.transforms: - data = t(data) - if data is None: - return None - return data - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - format_string += '\n' - format_string += f' {t}' - format_string += '\n)' - return format_string diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/crop.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/crop.py deleted file mode 100755 index 51fa24253..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/crop.py +++ /dev/null @@ -1,749 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -import random - -import mmcv -import numpy as np -from torch.nn.modules.utils import _pair - -from ..registry import PIPELINES -from .utils import random_choose_unknown - - -@PIPELINES.register_module() -class Crop: - """Crop data to specific size for training. - - Args: - keys (Sequence[str]): The images to be cropped. - crop_size (Tuple[int]): Target spatial size (h, w). - random_crop (bool): If set to True, it will random crop - image. Otherwise, it will work as center crop. - is_pad_zeros (bool, optional): Whether to pad the image with 0 if - crop_size is greater than image size. Default: False. - """ - - def __init__(self, keys, crop_size, random_crop=True, is_pad_zeros=False): - if not mmcv.is_tuple_of(crop_size, int): - raise TypeError( - 'Elements of crop_size must be int and crop_size must be' - f' tuple, but got {type(crop_size[0])} in {type(crop_size)}') - - self.keys = keys - self.crop_size = crop_size - self.random_crop = random_crop - self.is_pad_zeros = is_pad_zeros - - def _crop(self, data): - if not isinstance(data, list): - data_list = [data] - else: - data_list = data - - crop_bbox_list = [] - data_list_ = [] - - for item in data_list: - data_h, data_w = item.shape[:2] - crop_h, crop_w = self.crop_size - - if self.is_pad_zeros: - - crop_y_offset, crop_x_offset = 0, 0 - - if crop_h > data_h: - crop_y_offset = (crop_h - data_h) // 2 - if crop_w > data_w: - crop_x_offset = (crop_w - data_w) // 2 - - if crop_y_offset > 0 or crop_x_offset > 0: - pad_width = [(2 * crop_y_offset, 2 * crop_y_offset), - (2 * crop_x_offset, 2 * crop_x_offset)] - if item.ndim == 3: - pad_width.append((0, 0)) - item = np.pad( - item, - tuple(pad_width), - mode='constant', - constant_values=0) - - data_h, data_w = item.shape[:2] - - crop_h = min(data_h, crop_h) - crop_w = min(data_w, crop_w) - - if self.random_crop: - x_offset = np.random.randint(0, data_w - crop_w + 1) - y_offset = np.random.randint(0, data_h - crop_h + 1) - else: - x_offset = max(0, (data_w - crop_w)) // 2 - y_offset = max(0, (data_h - crop_h)) // 2 - - crop_bbox = [x_offset, y_offset, crop_w, crop_h] - item_ = item[y_offset:y_offset + crop_h, - x_offset:x_offset + crop_w, ...] - crop_bbox_list.append(crop_bbox) - data_list_.append(item_) - - if not isinstance(data, list): - return data_list_[0], crop_bbox_list[0] - return data_list_, crop_bbox_list - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - data_, crop_bbox = self._crop(results[k]) - results[k] = data_ - results[k + '_crop_bbox'] = crop_bbox - results['crop_size'] = self.crop_size - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'keys={self.keys}, crop_size={self.crop_size}, ' - f'random_crop={self.random_crop}') - - return repr_str - - -@PIPELINES.register_module() -class RandomResizedCrop(object): - """Crop data to random size and aspect ratio. - - A crop of a random proportion of the original image - and a random aspect ratio of the original aspect ratio is made. - The cropped image is finally resized to a given size specified - by 'crop_size'. Modified keys are the attributes specified in "keys". - - This code is partially adopted from - torchvision.transforms.RandomResizedCrop: - [https://pytorch.org/vision/stable/_modules/torchvision/transforms/\ - transforms.html#RandomResizedCrop]. - - Args: - keys (list[str]): The images to be resized and random-cropped. - crop_size (int | tuple[int]): Target spatial size (h, w). - scale (tuple[float], optional): Range of the proportion of the original - image to be cropped. Default: (0.08, 1.0). - ratio (tuple[float], optional): Range of aspect ratio of the crop. - Default: (3. / 4., 4. / 3.). - interpolation (str, optional): Algorithm used for interpolation. - It can be only either one of the following: - "nearest" | "bilinear" | "bicubic" | "area" | "lanczos". - Default: "bilinear". - """ - - def __init__(self, - keys, - crop_size, - scale=(0.08, 1.0), - ratio=(3. / 4., 4. / 3.), - interpolation='bilinear'): - assert keys, 'Keys should not be empty.' - if isinstance(crop_size, int): - crop_size = (crop_size, crop_size) - elif not mmcv.is_tuple_of(crop_size, int): - raise TypeError('"crop_size" must be an integer ' - 'or a tuple of integers, but got ' - f'{type(crop_size)}') - if not mmcv.is_tuple_of(scale, float): - raise TypeError('"scale" must be a tuple of float, ' - f'but got {type(scale)}') - if not mmcv.is_tuple_of(ratio, float): - raise TypeError('"ratio" must be a tuple of float, ' - f'but got {type(ratio)}') - - self.keys = keys - self.crop_size = crop_size - self.scale = scale - self.ratio = ratio - self.interpolation = interpolation - - def get_params(self, data): - """Get parameters for a random sized crop. - - Args: - data (np.ndarray): Image of type numpy array to be cropped. - - Returns: - A tuple containing the coordinates of the top left corner - and the chosen crop size. - """ - data_h, data_w = data.shape[:2] - area = data_h * data_w - - for _ in range(10): - target_area = random.uniform(*self.scale) * area - log_ratio = (math.log(self.ratio[0]), math.log(self.ratio[1])) - aspect_ratio = math.exp(random.uniform(*log_ratio)) - - crop_w = int(round(math.sqrt(target_area * aspect_ratio))) - crop_h = int(round(math.sqrt(target_area / aspect_ratio))) - - if 0 < crop_w <= data_w and 0 < crop_h <= data_h: - top = random.randint(0, data_h - crop_h) - left = random.randint(0, data_w - crop_w) - return top, left, crop_h, crop_w - - # Fall back to center crop - in_ratio = float(data_w) / float(data_h) - if (in_ratio < min(self.ratio)): - crop_w = data_w - crop_h = int(round(crop_w / min(self.ratio))) - elif (in_ratio > max(self.ratio)): - crop_h = data_h - crop_w = int(round(crop_h * max(self.ratio))) - else: # whole image - crop_w = data_w - crop_h = data_h - top = (data_h - crop_h) // 2 - left = (data_w - crop_w) // 2 - return top, left, crop_h, crop_w - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - top, left, crop_h, crop_w = self.get_params(results[k]) - crop_bbox = [top, left, crop_w, crop_h] - results[k] = results[k][top:top + crop_h, left:left + crop_w, ...] - results[k] = mmcv.imresize( - results[k], - self.crop_size, - return_scale=False, - interpolation=self.interpolation) - results[k + '_crop_bbox'] = crop_bbox - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, crop_size={self.crop_size}, ' - f'scale={self.scale}, ratio={self.ratio}, ' - f'interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class FixedCrop: - """Crop paired data (at a specific position) to specific size for training. - - Args: - keys (Sequence[str]): The images to be cropped. - crop_size (Tuple[int]): Target spatial size (h, w). - crop_pos (Tuple[int]): Specific position (x, y). If set to None, - random initialize the position to crop paired data batch. - """ - - def __init__(self, keys, crop_size, crop_pos=None): - if not mmcv.is_tuple_of(crop_size, int): - raise TypeError( - 'Elements of crop_size must be int and crop_size must be' - f' tuple, but got {type(crop_size[0])} in {type(crop_size)}') - if not mmcv.is_tuple_of(crop_pos, int) and (crop_pos is not None): - raise TypeError( - 'Elements of crop_pos must be int and crop_pos must be' - f' tuple or None, but got {type(crop_pos[0])} in ' - f'{type(crop_pos)}') - - self.keys = keys - self.crop_size = crop_size - self.crop_pos = crop_pos - - def _crop(self, data, x_offset, y_offset, crop_w, crop_h): - crop_bbox = [x_offset, y_offset, crop_w, crop_h] - data_ = data[y_offset:y_offset + crop_h, x_offset:x_offset + crop_w, - ...] - return data_, crop_bbox - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - if isinstance(results[self.keys[0]], list): - data_h, data_w = results[self.keys[0]][0].shape[:2] - else: - data_h, data_w = results[self.keys[0]].shape[:2] - crop_h, crop_w = self.crop_size - crop_h = min(data_h, crop_h) - crop_w = min(data_w, crop_w) - - if self.crop_pos is None: - x_offset = np.random.randint(0, data_w - crop_w + 1) - y_offset = np.random.randint(0, data_h - crop_h + 1) - else: - x_offset, y_offset = self.crop_pos - crop_w = min(data_w - x_offset, crop_w) - crop_h = min(data_h - y_offset, crop_h) - - for k in self.keys: - images = results[k] - is_list = isinstance(images, list) - if not is_list: - images = [images] - cropped_images = [] - crop_bbox = None - for image in images: - # In fixed crop for paired images, sizes should be the same - if (image.shape[0] != data_h or image.shape[1] != data_w): - raise ValueError( - 'The sizes of paired images should be the same. ' - f'Expected ({data_h}, {data_w}), ' - f'but got ({image.shape[0]}, ' - f'{image.shape[1]}).') - data_, crop_bbox = self._crop(image, x_offset, y_offset, - crop_w, crop_h) - cropped_images.append(data_) - results[k + '_crop_bbox'] = crop_bbox - if not is_list: - cropped_images = cropped_images[0] - results[k] = cropped_images - results['crop_size'] = self.crop_size - results['crop_pos'] = self.crop_pos - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'keys={self.keys}, crop_size={self.crop_size}, ' - f'crop_pos={self.crop_pos}') - return repr_str - - -@PIPELINES.register_module() -class PairedRandomCrop: - """Paried random crop. - - It crops a pair of lq and gt images with corresponding locations. - It also supports accepting lq list and gt list. - Required keys are "scale", "lq", and "gt", - added or modified keys are "lq" and "gt". - - Args: - gt_patch_size (int): cropped gt patch size. - """ - - def __init__(self, gt_patch_size): - self.gt_patch_size = gt_patch_size - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - scale = results['scale'] - lq_patch_size = self.gt_patch_size // scale - - lq_is_list = isinstance(results['lq'], list) - if not lq_is_list: - results['lq'] = [results['lq']] - gt_is_list = isinstance(results['gt'], list) - if not gt_is_list: - results['gt'] = [results['gt']] - - h_lq, w_lq, _ = results['lq'][0].shape - h_gt, w_gt, _ = results['gt'][0].shape - - if h_gt != h_lq * scale or w_gt != w_lq * scale: - raise ValueError( - f'Scale mismatches. GT ({h_gt}, {w_gt}) is not {scale}x ' - f'multiplication of LQ ({h_lq}, {w_lq}).') - if h_lq < lq_patch_size or w_lq < lq_patch_size: - raise ValueError( - f'LQ ({h_lq}, {w_lq}) is smaller than patch size ' - f'({lq_patch_size}, {lq_patch_size}). Please check ' - f'{results["lq_path"][0]} and {results["gt_path"][0]}.') - - # randomly choose top and left coordinates for lq patch - top = np.random.randint(h_lq - lq_patch_size + 1) - left = np.random.randint(w_lq - lq_patch_size + 1) - # crop lq patch - results['lq'] = [ - v[top:top + lq_patch_size, left:left + lq_patch_size, ...] - for v in results['lq'] - ] - # crop corresponding gt patch - top_gt, left_gt = int(top * scale), int(left * scale) - results['gt'] = [ - v[top_gt:top_gt + self.gt_patch_size, - left_gt:left_gt + self.gt_patch_size, ...] for v in results['gt'] - ] - - if not lq_is_list: - results['lq'] = results['lq'][0] - if not gt_is_list: - results['gt'] = results['gt'][0] - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(gt_patch_size={self.gt_patch_size})' - return repr_str - - -@PIPELINES.register_module() -class CropAroundCenter: - """Randomly crop the images around unknown area in the center 1/4 images. - - This cropping strategy is adopted in GCA matting. The `unknown area` is the - same as `semi-transparent area`. - https://arxiv.org/pdf/2001.04069.pdf - - It retains the center 1/4 images and resizes the images to 'crop_size'. - Required keys are "fg", "bg", "trimap" and "alpha", added or modified keys - are "crop_bbox", "fg", "bg", "trimap" and "alpha". - - Args: - crop_size (int | tuple): Desired output size. If int, square crop is - applied. - """ - - def __init__(self, crop_size): - if mmcv.is_tuple_of(crop_size, int): - assert len(crop_size) == 2, 'length of crop_size must be 2.' - elif not isinstance(crop_size, int): - raise TypeError('crop_size must be int or a tuple of int, but got ' - f'{type(crop_size)}') - self.crop_size = _pair(crop_size) - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - fg = results['fg'] - alpha = results['alpha'] - trimap = results['trimap'] - bg = results['bg'] - h, w = fg.shape[:2] - assert bg.shape == fg.shape, (f'shape of bg {bg.shape} should be the ' - f'same as fg {fg.shape}.') - - crop_h, crop_w = self.crop_size - # Make sure h >= crop_h, w >= crop_w. If not, rescale imgs - rescale_ratio = max(crop_h / h, crop_w / w) - if rescale_ratio > 1: - new_h = max(int(h * rescale_ratio), crop_h) - new_w = max(int(w * rescale_ratio), crop_w) - fg = mmcv.imresize(fg, (new_w, new_h), interpolation='nearest') - alpha = mmcv.imresize( - alpha, (new_w, new_h), interpolation='nearest') - trimap = mmcv.imresize( - trimap, (new_w, new_h), interpolation='nearest') - bg = mmcv.imresize(bg, (new_w, new_h), interpolation='bicubic') - h, w = new_h, new_w - - # resize to 1/4 to ignore small unknown patches - small_trimap = mmcv.imresize( - trimap, (w // 4, h // 4), interpolation='nearest') - # find unknown area in center 1/4 region - margin_h, margin_w = crop_h // 2, crop_w // 2 - sample_area = small_trimap[margin_h // 4:(h - margin_h) // 4, - margin_w // 4:(w - margin_w) // 4] - unknown_xs, unknown_ys = np.where(sample_area == 128) - unknown_num = len(unknown_xs) - if unknown_num < 10: - # too few unknown area in the center, crop from the whole image - top = np.random.randint(0, h - crop_h + 1) - left = np.random.randint(0, w - crop_w + 1) - else: - idx = np.random.randint(unknown_num) - top = unknown_xs[idx] * 4 - left = unknown_ys[idx] * 4 - bottom = top + crop_h - right = left + crop_w - - results['fg'] = fg[top:bottom, left:right] - results['alpha'] = alpha[top:bottom, left:right] - results['trimap'] = trimap[top:bottom, left:right] - results['bg'] = bg[top:bottom, left:right] - results['crop_bbox'] = (left, top, right, bottom) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(crop_size={self.crop_size})' - - -@PIPELINES.register_module() -class CropAroundUnknown: - """Crop around unknown area with a randomly selected scale. - - Randomly select the w and h from a list of (w, h). - Required keys are the keys in argument `keys`, added or - modified keys are "crop_bbox" and the keys in argument `keys`. - This class assumes value of "alpha" ranges from 0 to 255. - - Args: - keys (Sequence[str]): The images to be cropped. It must contain - 'alpha'. If unknown_source is set to 'trimap', then it must also - contain 'trimap'. - crop_sizes (list[int | tuple[int]]): List of (w, h) to be selected. - unknown_source (str, optional): Unknown area to select from. It must be - 'alpha' or 'tirmap'. Default to 'alpha'. - interpolations (str | list[str], optional): Interpolation method of - mmcv.imresize. The interpolation operation will be applied when - image size is smaller than the crop_size. If given as a list of - str, it should have the same length as `keys`. Or if given as a - str all the keys will be resized with the same method. - Default to 'bilinear'. - """ - - def __init__(self, - keys, - crop_sizes, - unknown_source='alpha', - interpolations='bilinear'): - if 'alpha' not in keys: - raise ValueError(f'"alpha" must be in keys, but got {keys}') - self.keys = keys - - if not isinstance(crop_sizes, list): - raise TypeError( - f'Crop sizes must be list, but got {type(crop_sizes)}.') - self.crop_sizes = [_pair(crop_size) for crop_size in crop_sizes] - if not mmcv.is_tuple_of(self.crop_sizes[0], int): - raise TypeError('Elements of crop_sizes must be int or tuple of ' - f'int, but got {type(self.crop_sizes[0][0])}.') - - if unknown_source not in ['alpha', 'trimap']: - raise ValueError('unknown_source must be "alpha" or "trimap", ' - f'but got {unknown_source}') - if unknown_source not in keys: - # it could only be trimap, since alpha is checked before - raise ValueError( - 'if unknown_source is "trimap", it must also be set in keys') - self.unknown_source = unknown_source - - if isinstance(interpolations, str): - self.interpolations = [interpolations] * len(self.keys) - elif mmcv.is_list_of(interpolations, - str) and len(interpolations) == len(self.keys): - self.interpolations = interpolations - else: - raise TypeError( - 'interpolations must be a str or list of str with ' - f'the same length as keys, but got {interpolations}') - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - h, w = results[self.keys[0]].shape[:2] - - rand_ind = np.random.randint(len(self.crop_sizes)) - crop_h, crop_w = self.crop_sizes[rand_ind] - - # Make sure h >= crop_h, w >= crop_w. If not, rescale imgs - rescale_ratio = max(crop_h / h, crop_w / w) - if rescale_ratio > 1: - h = max(int(h * rescale_ratio), crop_h) - w = max(int(w * rescale_ratio), crop_w) - for key, interpolation in zip(self.keys, self.interpolations): - results[key] = mmcv.imresize( - results[key], (w, h), interpolation=interpolation) - - # Select the cropping top-left point which is an unknown pixel - if self.unknown_source == 'alpha': - unknown = (results['alpha'] > 0) & (results['alpha'] < 255) - else: - unknown = results['trimap'] == 128 - top, left = random_choose_unknown(unknown.squeeze(), (crop_h, crop_w)) - - bottom = top + crop_h - right = left + crop_w - - for key in self.keys: - results[key] = results[key][top:bottom, left:right] - results['crop_bbox'] = (left, top, right, bottom) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, crop_sizes={self.crop_sizes}, ' - f"unknown_source='{self.unknown_source}', " - f'interpolations={self.interpolations})') - return repr_str - - -@PIPELINES.register_module() -class CropAroundFg: - """Crop around the whole foreground in the segmentation mask. - - Required keys are "seg" and the keys in argument `keys`. - Meanwhile, "seg" must be in argument `keys`. Added or modified keys are - "crop_bbox" and the keys in argument `keys`. - - Args: - keys (Sequence[str]): The images to be cropped. It must contain - 'seg'. - bd_ratio_range (tuple, optional): The range of the boundary (bd) ratio - to select from. The boundary ratio is the ratio of the boundary to - the minimal bbox that contains the whole foreground given by - segmentation. Default to (0.1, 0.4). - test_mode (bool): Whether use test mode. In test mode, the tight crop - area of foreground will be extended to the a square. - Default to False. - """ - - def __init__(self, keys, bd_ratio_range=(0.1, 0.4), test_mode=False): - if 'seg' not in keys: - raise ValueError(f'"seg" must be in keys, but got {keys}') - if (not mmcv.is_tuple_of(bd_ratio_range, float) - or len(bd_ratio_range) != 2): - raise TypeError('bd_ratio_range must be a tuple of 2 int, but got ' - f'{bd_ratio_range}') - self.keys = keys - self.bd_ratio_range = bd_ratio_range - self.test_mode = test_mode - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - seg = results['seg'] - height, width = seg.shape[:2] - - # get foreground bbox - fg_coor = np.array(np.where(seg)) - top, left = np.amin(fg_coor, axis=1) - bottom, right = np.amax(fg_coor, axis=1) - - # enlarge bbox - long_side = np.maximum(bottom - top, right - left) - if self.test_mode: - bottom = top + long_side - right = left + long_side - boundary_ratio = np.random.uniform(*self.bd_ratio_range) - boundary = int(np.round(boundary_ratio * long_side)) - # NOTE: Different from the original repo, we keep track of the four - # corners of the bbox (left, top, right, bottom) while the original - # repo use (top, left, height, width) to represent bbox. This may - # introduce an difference of 1 pixel. - top = max(top - boundary, 0) - left = max(left - boundary, 0) - bottom = min(bottom + boundary, height) - right = min(right + boundary, width) - - for key in self.keys: - results[key] = results[key][top:bottom, left:right] - results['crop_bbox'] = (left, top, right, bottom) - return results - - -@PIPELINES.register_module() -class ModCrop: - """Mod crop gt images, used during testing. - - Required keys are "scale" and "gt", - added or modified keys are "gt". - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - img = results['gt'].copy() - scale = results['scale'] - if img.ndim in [2, 3]: - h, w = img.shape[0], img.shape[1] - h_remainder, w_remainder = h % scale, w % scale - img = img[:h - h_remainder, :w - w_remainder, ...] - else: - raise ValueError(f'Wrong img ndim: {img.ndim}.') - results['gt'] = img - return results - - -@PIPELINES.register_module() -class CropLike: - """Crop/pad the image in the target_key according to the size of image - in the reference_key . - - Args: - target_key (str): The key needs to be cropped. - reference_key (str | None): The reference key, need its size. - Default: None. - """ - - def __init__(self, target_key, reference_key=None): - - assert reference_key and target_key - self.target_key = target_key - self.reference_key = reference_key - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - Require self.target_key and self.reference_key. - - Returns: - dict: A dict containing the processed data and information. - Modify self.target_key. - """ - size = results[self.reference_key].shape - old_image = results[self.target_key] - old_size = old_image.shape - h, w = old_size[:2] - new_size = size[:2] + old_size[2:] - h_cover, w_cover = min(h, size[0]), min(w, size[1]) - - format_image = np.zeros(new_size, dtype=old_image.dtype) - format_image[:h_cover, :w_cover] = old_image[:h_cover, :w_cover] - results[self.target_key] = format_image - - return results - - def __repr__(self): - return (self.__class__.__name__ + f' target_key={self.target_key}, ' + - f'reference_key={self.reference_key}') diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/formating.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/formating.py deleted file mode 100755 index 6ae98c050..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/formating.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import mmcv -import numpy as np -import torch -from mmcv.parallel import DataContainer as DC -from torch.nn import functional as F - -from ..registry import PIPELINES - - -def to_tensor(data): - """Convert objects of various python types to :obj:`torch.Tensor`. - - Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, - :class:`Sequence`, :class:`int` and :class:`float`. - """ - if isinstance(data, torch.Tensor): - return data - if isinstance(data, np.ndarray): - return torch.from_numpy(data) - if isinstance(data, Sequence) and not mmcv.is_str(data): - return torch.tensor(data) - if isinstance(data, int): - return torch.LongTensor([data]) - if isinstance(data, float): - return torch.FloatTensor([data]) - - raise TypeError(f'type {type(data)} cannot be converted to tensor.') - - -@PIPELINES.register_module() -class ToTensor: - """Convert some values in results dict to `torch.Tensor` type - in data loader pipeline. - - Args: - keys (Sequence[str]): Required keys to be converted. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - results[key] = to_tensor(results[key]) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class ImageToTensor: - """Convert image type to `torch.Tensor` type. - - Args: - keys (Sequence[str]): Required keys to be converted. - to_float32 (bool): Whether convert numpy image array to np.float32 - before converted to tensor. Default: True. - """ - - def __init__(self, keys, to_float32=True): - self.keys = keys - self.to_float32 = to_float32 - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - # deal with gray scale img: expand a color channel - if len(results[key].shape) == 2: - results[key] = results[key][..., None] - if self.to_float32 and not isinstance(results[key], np.float32): - results[key] = results[key].astype(np.float32) - results[key] = to_tensor(results[key].transpose(2, 0, 1)) - return results - - def __repr__(self): - return self.__class__.__name__ + ( - f'(keys={self.keys}, to_float32={self.to_float32})') - - -@PIPELINES.register_module() -class FramesToTensor(ImageToTensor): - """Convert frames type to `torch.Tensor` type. - - It accepts a list of frames, converts each to `torch.Tensor` type and then - concatenates in a new dimension (dim=0). - - Args: - keys (Sequence[str]): Required keys to be converted. - to_float32 (bool): Whether convert numpy image array to np.float32 - before converted to tensor. Default: True. - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if not isinstance(results[key], list): - raise TypeError(f'results["{key}"] should be a list, ' - f'but got {type(results[key])}') - for idx, v in enumerate(results[key]): - # deal with gray scale img: expand a color channel - if len(v.shape) == 2: - v = v[..., None] - if self.to_float32 and not isinstance(v, np.float32): - v = v.astype(np.float32) - results[key][idx] = to_tensor(v.transpose(2, 0, 1)) - results[key] = torch.stack(results[key], dim=0) - if results[key].size(0) == 1: - results[key].squeeze_() - return results - - -@PIPELINES.register_module() -class GetMaskedImage: - """Get masked image. - - Args: - img_name (str): Key for clean image. - mask_name (str): Key for mask image. The mask shape should be - (h, w, 1) while '1' indicate holes and '0' indicate valid - regions. - """ - - def __init__(self, img_name='gt_img', mask_name='mask'): - self.img_name = img_name - self.mask_name = mask_name - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - clean_img = results[self.img_name] - mask = results[self.mask_name] - - masked_img = clean_img * (1. - mask) - results['masked_img'] = masked_img - - return results - - def __repr__(self): - return self.__class__.__name__ + ( - f"(img_name='{self.img_name}', mask_name='{self.mask_name}')") - - -@PIPELINES.register_module() -class FormatTrimap: - """Convert trimap (tensor) to one-hot representation. - - It transforms the trimap label from (0, 128, 255) to (0, 1, 2). If - ``to_onehot`` is set to True, the trimap will convert to one-hot tensor of - shape (3, H, W). Required key is "trimap", added or modified key are - "trimap" and "to_onehot". - - Args: - to_onehot (bool): whether convert trimap to one-hot tensor. Default: - ``False``. - """ - - def __init__(self, to_onehot=False): - self.to_onehot = to_onehot - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - trimap = results['trimap'].squeeze() - trimap[trimap == 128] = 1 - trimap[trimap == 255] = 2 - if self.to_onehot: - trimap = F.one_hot(trimap.to(torch.long), num_classes=3) - trimap = trimap.permute(2, 0, 1) - else: - trimap = trimap[None, ...] # expand the channels dimension - results['trimap'] = trimap.float() - results['meta'].data['to_onehot'] = self.to_onehot - return results - - def __repr__(self): - return self.__class__.__name__ + f'(to_onehot={self.to_onehot})' - - -@PIPELINES.register_module() -class Collect: - """Collect data from the loader relevant to the specific task. - - This is usually the last stage of the data loader pipeline. Typically keys - is set to some subset of "img", "gt_labels". - - The "img_meta" item is always populated. The contents of the "meta" - dictionary depends on "meta_keys". - - Args: - keys (Sequence[str]): Required keys to be collected. - meta_keys (Sequence[str]): Required keys to be collected to "meta". - Default: None. - """ - - def __init__(self, keys, meta_keys=None): - self.keys = keys - self.meta_keys = meta_keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - data = {} - img_meta = {} - for key in self.meta_keys: - img_meta[key] = results[key] - data['meta'] = DC(img_meta, cpu_only=True) - for key in self.keys: - data[key] = results[key] - return data - - def __repr__(self): - return self.__class__.__name__ + ( - f'(keys={self.keys}, meta_keys={self.meta_keys})') diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/loading.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/loading.py deleted file mode 100755 index c7f162732..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/loading.py +++ /dev/null @@ -1,562 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from pathlib import Path - -import mmcv -import numpy as np -from mmcv.fileio import FileClient - -from mmedit.core.mask import (bbox2mask, brush_stroke_mask, get_irregular_mask, - random_bbox) -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class LoadImageFromFile: - """Load image from file. - - Args: - io_backend (str): io backend where images are store. Default: 'disk'. - key (str): Keys in results to find corresponding path. Default: 'gt'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - convert_to (str | None): The color space of the output image. If None, - no conversion is conducted. Default: None. - save_original_img (bool): If True, maintain a copy of the image in - `results` dict with name of `f'ori_{key}'`. Default: False. - use_cache (bool): If True, load all images at once. Default: False. - backend (str): The image loading backend type. Options are `cv2`, - `pillow`, and 'turbojpeg'. Default: None. - kwargs (dict): Args for file client. - """ - - def __init__(self, - io_backend='disk', - key='gt', - flag='color', - channel_order='bgr', - convert_to=None, - save_original_img=False, - use_cache=False, - backend=None, - **kwargs): - - self.io_backend = io_backend - self.key = key - self.flag = flag - self.save_original_img = save_original_img - self.channel_order = channel_order - self.convert_to = convert_to - self.kwargs = kwargs - self.file_client = None - self.use_cache = use_cache - self.cache = dict() if use_cache else None - self.backend = backend - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - filepath = str(results[f'{self.key}_path']) - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - if self.use_cache: - if filepath in self.cache: - img = self.cache[filepath] - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - self.cache[filepath] = img - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - - if self.convert_to is not None: - if self.channel_order == 'bgr' and self.convert_to.lower() == 'y': - img = mmcv.bgr2ycbcr(img, y_only=True) - elif self.channel_order == 'rgb': - img = mmcv.rgb2ycbcr(img, y_only=True) - else: - raise ValueError('Currently support only "bgr2ycbcr" or ' - '"bgr2ycbcr".') - if img.ndim == 2: - img = np.expand_dims(img, axis=2) - - results[self.key] = img - results[f'{self.key}_path'] = filepath - results[f'{self.key}_ori_shape'] = img.shape - if self.save_original_img: - results[f'ori_{self.key}'] = img.copy() - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(io_backend={self.io_backend}, key={self.key}, ' - f'flag={self.flag}, save_original_img={self.save_original_img}, ' - f'channel_order={self.channel_order}, use_cache={self.use_cache})') - return repr_str - - -@PIPELINES.register_module() -class LoadImageFromFileList(LoadImageFromFile): - """Load image from file list. - - It accepts a list of path and read each frame from each path. A list - of frames will be returned. - - Args: - io_backend (str): io backend where images are store. Default: 'disk'. - key (str): Keys in results to find corresponding path. Default: 'gt'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - convert_to (str | None): The color space of the output image. If None, - no conversion is conducted. Default: None. - save_original_img (bool): If True, maintain a copy of the image in - `results` dict with name of `f'ori_{key}'`. Default: False. - use_cache (bool): If True, load all images at once. Default: False. - backend (str): The image loading backend type. Options are `cv2`, - `pillow`, and 'turbojpeg'. Default: None. - kwargs (dict): Args for file client. - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - filepaths = results[f'{self.key}_path'] - if not isinstance(filepaths, list): - raise TypeError( - f'filepath should be list, but got {type(filepaths)}') - - filepaths = [str(v) for v in filepaths] - - imgs = [] - shapes = [] - if self.save_original_img: - ori_imgs = [] - for filepath in filepaths: - if self.use_cache: - if filepath in self.cache: - img = self.cache[filepath] - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - self.cache[filepath] = img - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - - # convert to y-channel, if specified - if self.convert_to is not None: - if self.channel_order == 'bgr' and self.convert_to.lower( - ) == 'y': - img = mmcv.bgr2ycbcr(img, y_only=True) - elif self.channel_order == 'rgb': - img = mmcv.rgb2ycbcr(img, y_only=True) - else: - raise ValueError('Currently support only "bgr2ycbcr" or ' - '"bgr2ycbcr".') - - if img.ndim == 2: - img = np.expand_dims(img, axis=2) - - imgs.append(img) - shapes.append(img.shape) - if self.save_original_img: - ori_imgs.append(img.copy()) - - results[self.key] = imgs - results[f'{self.key}_path'] = filepaths - results[f'{self.key}_ori_shape'] = shapes - if self.save_original_img: - results[f'ori_{self.key}'] = ori_imgs - - return results - - -@PIPELINES.register_module() -class RandomLoadResizeBg: - """Randomly load a background image and resize it. - - Required key is "fg", added key is "bg". - - Args: - bg_dir (str): Path of directory to load background images from. - io_backend (str): io backend where images are store. Default: 'disk'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - kwargs (dict): Args for file client. - """ - - def __init__(self, - bg_dir, - io_backend='disk', - flag='color', - channel_order='bgr', - **kwargs): - self.bg_dir = bg_dir - self.bg_list = list(mmcv.scandir(bg_dir)) - self.io_backend = io_backend - self.flag = flag - self.channel_order = channel_order - self.kwargs = kwargs - self.file_client = None - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - h, w = results['fg'].shape[:2] - idx = np.random.randint(len(self.bg_list)) - filepath = Path(self.bg_dir).joinpath(self.bg_list[idx]) - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, flag=self.flag, channel_order=self.channel_order) # HWC - bg = mmcv.imresize(img, (w, h), interpolation='bicubic') - results['bg'] = bg - return results - - def __repr__(self): - return self.__class__.__name__ + f"(bg_dir='{self.bg_dir}')" - - -@PIPELINES.register_module() -class LoadMask: - """Load Mask for multiple types. - - For different types of mask, users need to provide the corresponding - config dict. - - Example config for bbox: - - .. code-block:: python - - config = dict(img_shape=(256, 256), max_bbox_shape=128) - - Example config for irregular: - - .. code-block:: python - - config = dict( - img_shape=(256, 256), - num_vertices=(4, 12), - max_angle=4., - length_range=(10, 100), - brush_width=(10, 40), - area_ratio_range=(0.15, 0.5)) - - Example config for ff: - - .. code-block:: python - - config = dict( - img_shape=(256, 256), - num_vertices=(4, 12), - mean_angle=1.2, - angle_range=0.4, - brush_width=(12, 40)) - - Example config for set: - - .. code-block:: python - - config = dict( - mask_list_file='xxx/xxx/ooxx.txt', - prefix='/xxx/xxx/ooxx/', - io_backend='disk', - flag='unchanged', - file_client_kwargs=dict() - ) - - The mask_list_file contains the list of mask file name like this: - test1.jpeg - test2.jpeg - ... - ... - - The prefix gives the data path. - - Args: - mask_mode (str): Mask mode in ['bbox', 'irregular', 'ff', 'set', - 'file']. - * bbox: square bounding box masks. - * irregular: irregular holes. - * ff: free-form holes from DeepFillv2. - * set: randomly get a mask from a mask set. - * file: get mask from 'mask_path' in results. - mask_config (dict): Params for creating masks. Each type of mask needs - different configs. - """ - - def __init__(self, mask_mode='bbox', mask_config=None): - self.mask_mode = mask_mode - self.mask_config = dict() if mask_config is None else mask_config - assert isinstance(self.mask_config, dict) - - # set init info if needed in some modes - self._init_info() - - def _init_info(self): - if self.mask_mode == 'set': - # get mask list information - self.mask_list = [] - mask_list_file = self.mask_config['mask_list_file'] - with open(mask_list_file, 'r') as f: - for line in f: - line_split = line.strip().split(' ') - mask_name = line_split[0] - self.mask_list.append( - Path(self.mask_config['prefix']).joinpath(mask_name)) - self.mask_set_size = len(self.mask_list) - self.io_backend = self.mask_config['io_backend'] - self.flag = self.mask_config['flag'] - self.file_client_kwargs = self.mask_config['file_client_kwargs'] - self.file_client = None - elif self.mask_mode == 'file': - self.io_backend = 'disk' - self.flag = 'unchanged' - self.file_client_kwargs = dict() - self.file_client = None - - def _get_random_mask_from_set(self): - if self.file_client is None: - self.file_client = FileClient(self.io_backend, - **self.file_client_kwargs) - # minus 1 to avoid out of range error - mask_idx = np.random.randint(0, self.mask_set_size) - mask_bytes = self.file_client.get(self.mask_list[mask_idx]) - mask = mmcv.imfrombytes(mask_bytes, flag=self.flag) # HWC, BGR - if mask.ndim == 2: - mask = np.expand_dims(mask, axis=2) - else: - mask = mask[:, :, 0:1] - - mask[mask > 0] = 1. - return mask - - def _get_mask_from_file(self, path): - if self.file_client is None: - self.file_client = FileClient(self.io_backend, - **self.file_client_kwargs) - mask_bytes = self.file_client.get(path) - mask = mmcv.imfrombytes(mask_bytes, flag=self.flag) # HWC, BGR - if mask.ndim == 2: - mask = np.expand_dims(mask, axis=2) - else: - mask = mask[:, :, 0:1] - - mask[mask > 0] = 1. - return mask - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - if self.mask_mode == 'bbox': - mask_bbox = random_bbox(**self.mask_config) - mask = bbox2mask(self.mask_config['img_shape'], mask_bbox) - results['mask_bbox'] = mask_bbox - elif self.mask_mode == 'irregular': - mask = get_irregular_mask(**self.mask_config) - elif self.mask_mode == 'set': - mask = self._get_random_mask_from_set() - elif self.mask_mode == 'ff': - mask = brush_stroke_mask(**self.mask_config) - elif self.mask_mode == 'file': - mask = self._get_mask_from_file(results['mask_path']) - else: - raise NotImplementedError( - f'Mask mode {self.mask_mode} has not been implemented.') - results['mask'] = mask - return results - - def __repr__(self): - return self.__class__.__name__ + f"(mask_mode='{self.mask_mode}')" - - -@PIPELINES.register_module() -class GetSpatialDiscountMask: - """Get spatial discounting mask constant. - - Spatial discounting mask is first introduced in: - Generative Image Inpainting with Contextual Attention. - - Args: - gamma (float, optional): Gamma for computing spatial discounting. - Defaults to 0.99. - beta (float, optional): Beta for computing spatial discounting. - Defaults to 1.5. - """ - - def __init__(self, gamma=0.99, beta=1.5): - self.gamma = gamma - self.beta = beta - - def spatial_discount_mask(self, mask_width, mask_height): - """Generate spatial discounting mask constant. - - Args: - mask_width (int): The width of bbox hole. - mask_height (int): The height of bbox height. - - Returns: - np.ndarray: Spatial discounting mask. - """ - w, h = np.meshgrid(np.arange(mask_width), np.arange(mask_height)) - grid_stack = np.stack([h, w], axis=2) - mask_values = (self.gamma**(np.minimum( - grid_stack, [mask_height - 1, mask_width - 1] - grid_stack) * - self.beta)).max( - axis=2, keepdims=True) - - return mask_values - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - mask_bbox = results['mask_bbox'] - mask = results['mask'] - mask_height, mask_width = mask_bbox[-2:] - discount_hole = self.spatial_discount_mask(mask_width, mask_height) - discount_mask = np.zeros_like(mask) - discount_mask[mask_bbox[0]:mask_bbox[0] + mask_height, - mask_bbox[1]:mask_bbox[1] + mask_width, - ...] = discount_hole - - results['discount_mask'] = discount_mask - - return results - - def __repr__(self): - return self.__class__.__name__ + (f'(gamma={self.gamma}, ' - f'beta={self.beta})') - - -@PIPELINES.register_module() -class LoadPairedImageFromFile(LoadImageFromFile): - """Load a pair of images from file. - - Each sample contains a pair of images, which are concatenated in the w - dimension (a|b). This is a special loading class for generation paired - dataset. It loads a pair of images as the common loader does and crops - it into two images with the same shape in different domains. - - Required key is "pair_path". Added or modified keys are "pair", - "pair_ori_shape", "ori_pair", "img_a", "img_b", "img_a_path", - "img_b_path", "img_a_ori_shape", "img_b_ori_shape", "ori_img_a" and - "ori_img_b". - - Args: - io_backend (str): io backend where images are store. Default: 'disk'. - key (str): Keys in results to find corresponding path. Default: 'gt'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - save_original_img (bool): If True, maintain a copy of the image in - `results` dict with name of `f'ori_{key}'`. Default: False. - kwargs (dict): Args for file client. - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - filepath = str(results[f'{self.key}_path']) - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, flag=self.flag, channel_order=self.channel_order) # HWC - if img.ndim == 2: - img = np.expand_dims(img, axis=2) - - results[self.key] = img - results[f'{self.key}_path'] = filepath - results[f'{self.key}_ori_shape'] = img.shape - if self.save_original_img: - results[f'ori_{self.key}'] = img.copy() - - # crop pair into a and b - w = img.shape[1] - if w % 2 != 0: - raise ValueError( - f'The width of image pair must be even number, but got {w}.') - new_w = w // 2 - img_a = img[:, :new_w, :] - img_b = img[:, new_w:, :] - - results['img_a'] = img_a - results['img_b'] = img_b - results['img_a_path'] = filepath - results['img_b_path'] = filepath - results['img_a_ori_shape'] = img_a.shape - results['img_b_ori_shape'] = img_b.shape - if self.save_original_img: - results['ori_img_a'] = img_a.copy() - results['ori_img_b'] = img_b.copy() - - return results diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/matlab_like_resize.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/matlab_like_resize.py deleted file mode 100755 index b6f63102a..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/matlab_like_resize.py +++ /dev/null @@ -1,275 +0,0 @@ -# This code is referenced from matlab_imresize with modifications -# Reference: https://github.com/fatheral/matlab_imresize/blob/master/imresize.py # noqa -# Original licence: Copyright (c) 2020 fatheral, under the MIT License. -import numpy as np - -from ..registry import PIPELINES - - -def get_size_from_scale(input_size, scale_factor): - """Get the output size given input size and scale factor. - - Args: - input_size (tuple): The size of the input image. - scale_factor (float): The resize factor. - - Returns: - list[int]: The size of the output image. - """ - - output_shape = [ - int(np.ceil(scale * shape)) - for (scale, shape) in zip(scale_factor, input_size) - ] - - return output_shape - - -def get_scale_from_size(input_size, output_size): - """Get the scale factor given input size and output size. - - Args: - input_size (tuple(int)): The size of the input image. - output_size (tuple(int)): The size of the output image. - - Returns: - list[float]: The scale factor of each dimension. - """ - - scale = [ - 1.0 * output_shape / input_shape - for (input_shape, output_shape) in zip(input_size, output_size) - ] - - return scale - - -def _cubic(x): - """ Cubic function. - - Args: - x (ndarray): The distance from the center position. - - Returns: - ndarray: The weight corresponding to a particular distance. - - """ - - x = np.array(x, dtype=np.float32) - x_abs = np.abs(x) - x_abs_sq = x_abs**2 - x_abs_cu = x_abs_sq * x_abs - - # if |x| <= 1: y = 1.5|x|^3 - 2.5|x|^2 + 1 - # if 1 < |x| <= 2: -0.5|x|^3 + 2.5|x|^2 - 4|x| + 2 - f = (1.5 * x_abs_cu - 2.5 * x_abs_sq + 1) * (x_abs <= 1) + ( - -0.5 * x_abs_cu + 2.5 * x_abs_sq - 4 * x_abs + 2) * ((1 < x_abs) & - (x_abs <= 2)) - - return f - - -def get_weights_indices(input_length, output_length, scale, kernel, - kernel_width): - """Get weights and indices for interpolation. - - Args: - input_length (int): Length of the input sequence. - output_length (int): Length of the output sequence. - scale (float): Scale factor. - kernel (func): The kernel used for resizing. - kernel_width (int): The width of the kernel. - - Returns: - list[ndarray]: The weights and the indices for interpolation. - - - """ - if scale < 1: # modified kernel for antialiasing - - def h(x): - return scale * kernel(scale * x) - - kernel_width = 1.0 * kernel_width / scale - else: - h = kernel - kernel_width = kernel_width - - # coordinates of output - x = np.arange(1, output_length + 1).astype(np.float32) - - # coordinates of input - u = x / scale + 0.5 * (1 - 1 / scale) - left = np.floor(u - kernel_width / 2) # leftmost pixel - p = int(np.ceil(kernel_width)) + 2 # maximum number of pixels - - # indices of input pixels - ind = left[:, np.newaxis, ...] + np.arange(p) - indices = ind.astype(np.int32) - - # weights of input pixels - weights = h(u[:, np.newaxis, ...] - indices - 1) - - weights = weights / np.sum(weights, axis=1)[:, np.newaxis, ...] - - # remove all-zero columns - aux = np.concatenate( - (np.arange(input_length), np.arange(input_length - 1, -1, - step=-1))).astype(np.int32) - indices = aux[np.mod(indices, aux.size)] - ind2store = np.nonzero(np.any(weights, axis=0)) - weights = weights[:, ind2store] - indices = indices[:, ind2store] - - return weights, indices - - -def resize_along_dim(img_in, weights, indices, dim): - """Resize along a specific dimension. - - Args: - img_in (ndarray): The input image. - weights (ndarray): The weights used for interpolation, computed from - [get_weights_indices]. - indices (ndarray): The indices used for interpolation, computed from - [get_weights_indices]. - dim (int): Which dimension to undergo interpolation. - - Returns: - ndarray: Interpolated (along one dimension) image. - """ - - img_in = img_in.astype(np.float32) - w_shape = weights.shape - output_shape = list(img_in.shape) - output_shape[dim] = w_shape[0] - img_out = np.zeros(output_shape) - - if dim == 0: - for i in range(w_shape[0]): - w = weights[i, :][np.newaxis, ...] - ind = indices[i, :] - img_slice = img_in[ind, :] - img_out[i] = np.sum(np.squeeze(img_slice, axis=0) * w.T, axis=0) - elif dim == 1: - for i in range(w_shape[0]): - w = weights[i, :][:, :, np.newaxis] - ind = indices[i, :] - img_slice = img_in[:, ind] - img_out[:, i] = np.sum(np.squeeze(img_slice, axis=1) * w.T, axis=1) - - if img_in.dtype == np.uint8: - img_out = np.clip(img_out, 0, 255) - return np.around(img_out).astype(np.uint8) - else: - return img_out - - -@PIPELINES.register_module() -class MATLABLikeResize: - """Resize the input image using MATLAB-like downsampling. - - Currently support bicubic interpolation only. Note that the output of - this function is slightly different from the official MATLAB function. - - Required keys are the keys in attribute "keys". Added or modified keys - are "scale" and "output_shape", and the keys in attribute "keys". - - Args: - keys (list[str]): A list of keys whose values are modified. - scale (float | None, optional): The scale factor of the resize - operation. If None, it will be determined by output_shape. - Default: None. - output_shape (tuple(int) | None, optional): The size of the output - image. If None, it will be determined by scale. Note that if - scale is provided, output_shape will not be used. - Default: None. - kernel (str, optional): The kernel for the resize operation. - Currently support 'bicubic' only. Default: 'bicubic'. - kernel_width (float): The kernel width. Currently support 4.0 only. - Default: 4.0. - """ - - def __init__(self, - keys, - scale=None, - output_shape=None, - kernel='bicubic', - kernel_width=4.0): - - if kernel.lower() != 'bicubic': - raise ValueError('Currently support bicubic kernel only.') - - if float(kernel_width) != 4.0: - raise ValueError('Current support only width=4 only.') - - if scale is None and output_shape is None: - raise ValueError('"scale" and "output_shape" cannot be both None') - - self.kernel_func = _cubic - self.keys = keys - self.scale = scale - self.output_shape = output_shape - self.kernel = kernel - self.kernel_width = kernel_width - - def _resize(self, img): - weights = {} - indices = {} - - # compute scale and output_size - if self.scale is not None: - scale = float(self.scale) - scale = [scale, scale] - output_size = get_size_from_scale(img.shape, scale) - else: - scale = get_scale_from_size(img.shape, self.output_shape) - output_size = list(self.output_shape) - - # apply cubic interpolation along two dimensions - order = np.argsort(np.array(scale)) - for k in range(2): - key = (img.shape[k], output_size[k], scale[k], self.kernel_func, - self.kernel_width) - weight, index = get_weights_indices(img.shape[k], output_size[k], - scale[k], self.kernel_func, - self.kernel_width) - weights[key] = weight - indices[key] = index - - output = np.copy(img) - if output.ndim == 2: # grayscale image - output = output[:, :, np.newaxis] - - for k in range(2): - dim = order[k] - key = (img.shape[dim], output_size[dim], scale[dim], - self.kernel_func, self.kernel_width) - output = resize_along_dim(output, weights[key], indices[key], dim) - - return output - - def __call__(self, results): - for key in self.keys: - is_single_image = False - if isinstance(results[key], np.ndarray): - is_single_image = True - results[key] = [results[key]] - - results[key] = [self._resize(img) for img in results[key]] - - if is_single_image: - results[key] = results[key][0] - - results['scale'] = self.scale - results['output_shape'] = self.output_shape - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(keys={self.keys}, scale={self.scale}, ' - f'output_shape={self.output_shape}, ' - f'kernel={self.kernel}, kernel_width={self.kernel_width})') - return repr_str diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/normalization.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/normalization.py deleted file mode 100755 index 8ff774d7f..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/normalization.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np - -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class Normalize: - """Normalize images with the given mean and std value. - - Required keys are the keys in attribute "keys", added or modified keys are - the keys in attribute "keys" and these keys with postfix '_norm_cfg'. - It also supports normalizing a list of images. - - Args: - keys (Sequence[str]): The images to be normalized. - mean (np.ndarray): Mean values of different channels. - std (np.ndarray): Std values of different channels. - to_rgb (bool): Whether to convert channels from BGR to RGB. - """ - - def __init__(self, keys, mean, std, to_rgb=False, save_original=False): - self.keys = keys - self.mean = np.array(mean, dtype=np.float32) - self.std = np.array(std, dtype=np.float32) - self.to_rgb = to_rgb - self.save_original = save_original - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if isinstance(results[key], list): - if self.save_original: - results[key + '_unnormalised'] = [ - v.copy() for v in results[key] - ] - results[key] = [ - mmcv.imnormalize(v, self.mean, self.std, self.to_rgb) - for v in results[key] - ] - else: - if self.save_original: - results[key + '_unnormalised'] = results[key].copy() - results[key] = mmcv.imnormalize(results[key], self.mean, - self.std, self.to_rgb) - - results['img_norm_cfg'] = dict( - mean=self.mean, std=self.std, to_rgb=self.to_rgb) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, mean={self.mean}, std={self.std}, ' - f'to_rgb={self.to_rgb})') - - return repr_str - - -@PIPELINES.register_module() -class RescaleToZeroOne: - """Transform the images into a range between 0 and 1. - - Required keys are the keys in attribute "keys", added or modified keys are - the keys in attribute "keys". - It also supports rescaling a list of images. - - Args: - keys (Sequence[str]): The images to be transformed. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if isinstance(results[key], list): - results[key] = [ - v.astype(np.float32) / 255. for v in results[key] - ] - else: - results[key] = results[key].astype(np.float32) / 255. - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/utils.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/utils.py deleted file mode 100755 index 42d470c34..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/pipelines/utils.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import numpy as np -import torch -from mmcv.utils import print_log - -_integer_types = ( - np.byte, - np.ubyte, # 8 bits - np.short, - np.ushort, # 16 bits - np.intc, - np.uintc, # 16 or 32 or 64 bits - np.int_, - np.uint, # 32 or 64 bits - np.longlong, - np.ulonglong) # 64 bits - -_integer_ranges = { - t: (np.iinfo(t).min, np.iinfo(t).max) - for t in _integer_types -} - -dtype_range = { - np.bool_: (False, True), - np.bool8: (False, True), - np.float16: (-1, 1), - np.float32: (-1, 1), - np.float64: (-1, 1) -} -dtype_range.update(_integer_ranges) - - -def dtype_limits(image, clip_negative=False): - """Return intensity limits, i.e. (min, max) tuple, of the image's dtype. - - This function is adopted from skimage: - https://github.com/scikit-image/scikit-image/blob/ - 7e4840bd9439d1dfb6beaf549998452c99f97fdd/skimage/util/dtype.py#L35 - - Args: - image (ndarray): Input image. - clip_negative (bool, optional): If True, clip the negative range - (i.e. return 0 for min intensity) even if the image dtype allows - negative values. - - Returns - tuple: Lower and upper intensity limits. - """ - imin, imax = dtype_range[image.dtype.type] - if clip_negative: - imin = 0 - return imin, imax - - -def adjust_gamma(image, gamma=1, gain=1): - """Performs Gamma Correction on the input image. - - This function is adopted from skimage: - https://github.com/scikit-image/scikit-image/blob/ - 7e4840bd9439d1dfb6beaf549998452c99f97fdd/skimage/exposure/ - exposure.py#L439-L494 - - Also known as Power Law Transform. - This function transforms the input image pixelwise according to the - equation ``O = I**gamma`` after scaling each pixel to the range 0 to 1. - - Args: - image (ndarray): Input image. - gamma (float, optional): Non negative real number. Defaults to 1. - gain (float, optional): The constant multiplier. Defaults to 1. - - Returns: - ndarray: Gamma corrected output image. - """ - if np.any(image < 0): - raise ValueError('Image Correction methods work correctly only on ' - 'images with non-negative values. Use ' - 'skimage.exposure.rescale_intensity.') - - dtype = image.dtype.type - - if gamma < 0: - raise ValueError('Gamma should be a non-negative real number.') - - scale = float(dtype_limits(image, True)[1] - dtype_limits(image, True)[0]) - - out = ((image / scale)**gamma) * scale * gain - return out.astype(dtype) - - -def random_choose_unknown(unknown, crop_size): - """Randomly choose an unknown start (top-left) point for a given crop_size. - - Args: - unknown (np.ndarray): The binary unknown mask. - crop_size (tuple[int]): The given crop size. - - Returns: - tuple[int]: The top-left point of the chosen bbox. - """ - h, w = unknown.shape - crop_h, crop_w = crop_size - delta_h = center_h = crop_h // 2 - delta_w = center_w = crop_w // 2 - - # mask out the validate area for selecting the cropping center - mask = np.zeros_like(unknown) - mask[delta_h:h - delta_h, delta_w:w - delta_w] = 1 - if np.any(unknown & mask): - center_h_list, center_w_list = np.where(unknown & mask) - elif np.any(unknown): - center_h_list, center_w_list = np.where(unknown) - else: - print_log('No unknown pixels found!', level=logging.WARNING) - center_h_list = [center_h] - center_w_list = [center_w] - num_unknowns = len(center_h_list) - rand_ind = np.random.randint(num_unknowns) - center_h = center_h_list[rand_ind] - center_w = center_w_list[rand_ind] - - # make sure the top-left point is valid - top = np.clip(center_h - delta_h, 0, h - crop_h) - left = np.clip(center_w - delta_w, 0, w - crop_w) - - return top, left - - -def make_coord(shape, ranges=None, flatten=True): - """ Make coordinates at grid centers. - - Args: - shape (tuple): shape of image. - ranges (tuple): range of coordinate value. Default: None. - flatten (bool): flatten to (n, 2) or Not. Default: True. - - return: - coord (Tensor): coordinates. - """ - coord_seqs = [] - for i, n in enumerate(shape): - if ranges is None: - v0, v1 = -1, 1 - else: - v0, v1 = ranges[i] - r = (v1 - v0) / (2 * n) - seq = v0 + r + (2 * r) * torch.arange(n).float() - coord_seqs.append(seq) - coord = torch.stack(torch.meshgrid(*coord_seqs), dim=-1) - if flatten: - coord = coord.view(-1, coord.shape[-1]) - return coord diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/registry.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/registry.py deleted file mode 100755 index 984580ef2..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/registry.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -DATASETS = Registry('dataset') -PIPELINES = Registry('pipeline') diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/samplers/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/samplers/__init__.py deleted file mode 100755 index da09effaf..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/samplers/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .distributed_sampler import DistributedSampler - -__all__ = ['DistributedSampler'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/samplers/distributed_sampler.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/samplers/distributed_sampler.py deleted file mode 100755 index 7e800c813..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/samplers/distributed_sampler.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from __future__ import division -import math - -import torch -from torch.utils.data import DistributedSampler as _DistributedSampler - -from mmedit.core.utils import sync_random_seed - - -class DistributedSampler(_DistributedSampler): - """DistributedSampler inheriting from `torch.utils.data.DistributedSampler`. - - In pytorch of lower versions, there is no `shuffle` argument. This child - class will port one to DistributedSampler. - """ - - def __init__(self, - dataset, - num_replicas=None, - rank=None, - shuffle=True, - samples_per_gpu=1, - seed=0): - super().__init__(dataset, num_replicas=num_replicas, rank=rank) - self.shuffle = shuffle - self.samples_per_gpu = samples_per_gpu - # fix the bug of the official implementation - self.num_samples_per_replica = int( - math.ceil( - len(self.dataset) * 1.0 / self.num_replicas / samples_per_gpu)) - self.num_samples = self.num_samples_per_replica * self.samples_per_gpu - self.total_size = self.num_samples * self.num_replicas - - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - - # to avoid padding bug when meeting too small dataset - if len(dataset) < self.num_replicas * samples_per_gpu: - raise ValueError( - 'You may use too small dataset and our distributed ' - 'sampler cannot pad your dataset correctly. We highly ' - 'recommend you to use fewer GPUs to finish your work') - - def __iter__(self): - # deterministically shuffle based on epoch - if self.shuffle: - g = torch.Generator() - # When :attr:`shuffle=True`, this ensures all replicas - # use a different random ordering for each epoch. - # Otherwise, the next iteration of this sampler will - # yield the same ordering. - g.manual_seed(self.epoch + self.seed) - indices = torch.randperm(len(self.dataset), generator=g).tolist() - else: - indices = torch.arange(len(self.dataset)).tolist() - - # add extra samples to make it evenly divisible - indices += indices[:(self.total_size - len(indices))] - assert len(indices) == self.total_size - - # subsample - indices = indices[self.rank:self.total_size:self.num_replicas] - assert len(indices) == self.num_samples - - return iter(indices) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/sr_reds_multiple_gt_dataset.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/sr_reds_multiple_gt_dataset.py deleted file mode 100755 index 1653f9de5..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/datasets/sr_reds_multiple_gt_dataset.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_sr_dataset import BaseSRDataset -from .registry import DATASETS - - -@DATASETS.register_module() -class SRREDSMultipleGTDataset(BaseSRDataset): - """REDS dataset for video super resolution for recurrent networks. - - The dataset loads several LQ (Low-Quality) frames and GT (Ground-Truth) - frames. Then it applies specified transforms and finally returns a dict - containing paired data and other information. - - Args: - lq_folder (str | :obj:`Path`): Path to a lq folder. - gt_folder (str | :obj:`Path`): Path to a gt folder. - num_input_frames (int): Number of input frames. - pipeline (list[dict | callable]): A sequence of data transformations. - scale (int): Upsampling scale ratio. - val_partition (str): Validation partition mode. Choices ['official' or - 'REDS4']. Default: 'official'. - repeat (int): Number of replication of the validation set. This is used - to allow training REDS4 with more than 4 GPUs. For example, if - 8 GPUs are used, this number can be set to 2. Default: 1. - test_mode (bool): Store `True` when building test dataset. - Default: `False`. - """ - - def __init__(self, - lq_folder, - gt_folder, - num_input_frames, - pipeline, - scale, - val_partition='official', - repeat=1, - test_mode=False): - - self.repeat = repeat - if not isinstance(repeat, int): - raise TypeError('"repeat" must be an integer, but got ' - f'{type(repeat)}.') - - super().__init__(pipeline, scale, test_mode) - self.lq_folder = str(lq_folder) - self.gt_folder = str(gt_folder) - self.num_input_frames = num_input_frames - self.val_partition = val_partition - self.data_infos = self.load_annotations() - - def load_annotations(self): - """Load annotations for REDS dataset. - - Returns: - list[dict]: A list of dicts for paired paths and other information. - """ - # generate keys - keys = [f'{i:03d}' for i in range(0, 270)] - - if self.val_partition == 'REDS4': - val_partition = ['000', '011', '015', '020'] - elif self.val_partition == 'official': - val_partition = [f'{i:03d}' for i in range(240, 270)] - else: - raise ValueError( - f'Wrong validation partition {self.val_partition}.' - f'Supported ones are ["official", "REDS4"]') - - if self.test_mode: - keys = [v for v in keys if v in val_partition] - keys *= self.repeat - else: - keys = [v for v in keys if v not in val_partition] - - data_infos = [] - for key in keys: - data_infos.append( - dict( - lq_path=self.lq_folder, - gt_path=self.gt_folder, - key=key, - sequence_length=100, # REDS has 100 frames for each clip - num_input_frames=self.num_input_frames)) - - return data_infos diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/__init__.py deleted file mode 100755 index 2c71bad9b..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .backbones import * # noqa: F401, F403 -from .base import BaseModel -from .builder import (build, build_backbone, build_component, build_loss, - build_model) -from .common import * # noqa: F401, F403 -from .losses import * # noqa: F401, F403 -from .registry import BACKBONES, COMPONENTS, LOSSES, MODELS -from .restorers import BasicRestorer - diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/__init__.py deleted file mode 100755 index 378f89997..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .sr_backbones import (BasicVSRNet, - BasicVSRPlusPlus, - ) - diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/sr_backbones/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/sr_backbones/__init__.py deleted file mode 100755 index 3f19614c3..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/sr_backbones/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .basicvsr_net import BasicVSRNet -from .basicvsr_pp import BasicVSRPlusPlus diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/sr_backbones/basicvsr_net.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/sr_backbones/basicvsr_net.py deleted file mode 100755 index 1c98c4c73..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/sr_backbones/basicvsr_net.py +++ /dev/null @@ -1,420 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import load_checkpoint - -from mmedit.models.common import (PixelShufflePack, ResidualBlockNoBN, - flow_warp, make_layer) -from mmedit.models.registry import BACKBONES -from mmedit.utils import get_root_logger - - -@BACKBONES.register_module() -class BasicVSRNet(nn.Module): - """BasicVSR network structure for video super-resolution. - - Support only x4 upsampling. - Paper: - BasicVSR: The Search for Essential Components in Video Super-Resolution - and Beyond, CVPR, 2021 - - Args: - mid_channels (int): Channel number of the intermediate features. - Default: 64. - num_blocks (int): Number of residual blocks in each propagation branch. - Default: 30. - spynet_pretrained (str): Pre-trained model path of SPyNet. - Default: None. - """ - - def __init__(self, mid_channels=64, num_blocks=30, spynet_pretrained=None): - - super().__init__() - - self.mid_channels = mid_channels - - # optical flow network for feature alignment - self.spynet = SPyNet(pretrained=spynet_pretrained) - - # propagation branches - self.backward_resblocks = ResidualBlocksWithInputConv( - mid_channels + 3, mid_channels, num_blocks) - self.forward_resblocks = ResidualBlocksWithInputConv( - mid_channels + 3, mid_channels, num_blocks) - - # upsample - self.fusion = nn.Conv2d( - mid_channels * 2, mid_channels, 1, 1, 0, bias=True) - self.upsample1 = PixelShufflePack( - mid_channels, mid_channels, 2, upsample_kernel=3) - self.upsample2 = PixelShufflePack( - mid_channels, 64, 2, upsample_kernel=3) - self.conv_hr = nn.Conv2d(64, 64, 3, 1, 1) - self.conv_last = nn.Conv2d(64, 3, 3, 1, 1) - self.img_upsample = nn.Upsample( - scale_factor=4, mode='bilinear', align_corners=False) - - # activation function - self.lrelu = nn.LeakyReLU(negative_slope=0.1, inplace=True) - - def check_if_mirror_extended(self, lrs): - """Check whether the input is a mirror-extended sequence. - - If mirror-extended, the i-th (i=0, ..., t-1) frame is equal to the - (t-1-i)-th frame. - - Args: - lrs (tensor): Input LR images with shape (n, t, c, h, w) - """ - - self.is_mirror_extended = False - if lrs.size(1) % 2 == 0: - lrs_1, lrs_2 = torch.chunk(lrs, 2, dim=1) - if torch.norm(lrs_1 - lrs_2.flip(1)) == 0: - self.is_mirror_extended = True - - def compute_flow(self, lrs): - """Compute optical flow using SPyNet for feature warping. - - Note that if the input is an mirror-extended sequence, 'flows_forward' - is not needed, since it is equal to 'flows_backward.flip(1)'. - - Args: - lrs (tensor): Input LR images with shape (n, t, c, h, w) - - Return: - tuple(Tensor): Optical flow. 'flows_forward' corresponds to the - flows used for forward-time propagation (current to previous). - 'flows_backward' corresponds to the flows used for - backward-time propagation (current to next). - """ - - n, t, c, h, w = lrs.size() - lrs_1 = lrs[:, :-1, :, :, :].reshape(-1, c, h, w) - lrs_2 = lrs[:, 1:, :, :, :].reshape(-1, c, h, w) - - flows_backward = self.spynet(lrs_1, lrs_2).view(n, t - 1, 2, h, w) - - if self.is_mirror_extended: # flows_forward = flows_backward.flip(1) - flows_forward = None - else: - flows_forward = self.spynet(lrs_2, lrs_1).view(n, t - 1, 2, h, w) - - return flows_forward, flows_backward - - def forward(self, lrs): - """Forward function for BasicVSR. - - Args: - lrs (Tensor): Input LR sequence with shape (n, t, c, h, w). - - Returns: - Tensor: Output HR sequence with shape (n, t, c, 4h, 4w). - """ - - n, t, c, h, w = lrs.size() - assert h >= 64 and w >= 64, ( - 'The height and width of inputs should be at least 64, ' - f'but got {h} and {w}.') - - # check whether the input is an extended sequence - self.check_if_mirror_extended(lrs) - - # compute optical flow - flows_forward, flows_backward = self.compute_flow(lrs) - - # backward-time propagation - outputs = [] - feat_prop = lrs.new_zeros(n, self.mid_channels, h, w) - for i in range(t - 1, -1, -1): - if i < t - 1: # no warping required for the last timestep - flow = flows_backward[:, i, :, :, :] - feat_prop = flow_warp(feat_prop, flow.permute(0, 2, 3, 1)) - - feat_prop = torch.cat([lrs[:, i, :, :, :], feat_prop], dim=1) - feat_prop = self.backward_resblocks(feat_prop) - - outputs.append(feat_prop) - outputs = outputs[::-1] - - # forward-time propagation and upsampling - feat_prop = torch.zeros_like(feat_prop) - for i in range(0, t): - lr_curr = lrs[:, i, :, :, :] - if i > 0: # no warping required for the first timestep - if flows_forward is not None: - flow = flows_forward[:, i - 1, :, :, :] - else: - flow = flows_backward[:, -i, :, :, :] - feat_prop = flow_warp(feat_prop, flow.permute(0, 2, 3, 1)) - - feat_prop = torch.cat([lr_curr, feat_prop], dim=1) - feat_prop = self.forward_resblocks(feat_prop) - - # upsampling given the backward and forward features - out = torch.cat([outputs[i], feat_prop], dim=1) - out = self.lrelu(self.fusion(out)) - out = self.lrelu(self.upsample1(out)) - out = self.lrelu(self.upsample2(out)) - out = self.lrelu(self.conv_hr(out)) - out = self.conv_last(out) - base = self.img_upsample(lr_curr) - out += base - outputs[i] = out - - return torch.stack(outputs, dim=1) - - def init_weights(self, pretrained=None, strict=True): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults: None. - strict (boo, optional): Whether strictly load the pretrained model. - Defaults to True. - """ - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=strict, logger=logger) - elif pretrained is not None: - raise TypeError(f'"pretrained" must be a str or None. ' - f'But received {type(pretrained)}.') - - -class ResidualBlocksWithInputConv(nn.Module): - """Residual blocks with a convolution in front. - - Args: - in_channels (int): Number of input channels of the first conv. - out_channels (int): Number of channels of the residual blocks. - Default: 64. - num_blocks (int): Number of residual blocks. Default: 30. - """ - - def __init__(self, in_channels, out_channels=64, num_blocks=30): - super().__init__() - - main = [] - - # a convolution used to match the channels of the residual blocks - main.append(nn.Conv2d(in_channels, out_channels, 3, 1, 1, bias=True)) - main.append(nn.LeakyReLU(negative_slope=0.1, inplace=True)) - - # residual blocks - main.append( - make_layer( - ResidualBlockNoBN, num_blocks, mid_channels=out_channels)) - - self.main = nn.Sequential(*main) - - def forward(self, feat): - """ - Forward function for ResidualBlocksWithInputConv. - - Args: - feat (Tensor): Input feature with shape (n, in_channels, h, w) - - Returns: - Tensor: Output feature with shape (n, out_channels, h, w) - """ - return self.main(feat) - - -class SPyNet(nn.Module): - """SPyNet network structure. - - The difference to the SPyNet in [tof.py] is that - 1. more SPyNetBasicModule is used in this version, and - 2. no batch normalization is used in this version. - - Paper: - Optical Flow Estimation using a Spatial Pyramid Network, CVPR, 2017 - - Args: - pretrained (str): path for pre-trained SPyNet. Default: None. - """ - - def __init__(self, pretrained): - super().__init__() - - self.basic_module = nn.ModuleList( - [SPyNetBasicModule() for _ in range(6)]) - - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=True, logger=logger) - elif pretrained is not None: - raise TypeError('[pretrained] should be str or None, ' - f'but got {type(pretrained)}.') - - self.register_buffer( - 'mean', - torch.Tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1)) - self.register_buffer( - 'std', - torch.Tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1)) - - def compute_flow(self, ref, supp): - """Compute flow from ref to supp. - - Note that in this function, the images are already resized to a - multiple of 32. - - Args: - ref (Tensor): Reference image with shape of (n, 3, h, w). - supp (Tensor): Supporting image with shape of (n, 3, h, w). - - Returns: - Tensor: Estimated optical flow: (n, 2, h, w). - """ - n, _, h, w = ref.size() - - # normalize the input images - ref = [(ref - self.mean) / self.std] - supp = [(supp - self.mean) / self.std] - - # generate downsampled frames - for level in range(5): - ref.append( - F.avg_pool2d( - input=ref[-1], - kernel_size=2, - stride=2, - count_include_pad=False)) - supp.append( - F.avg_pool2d( - input=supp[-1], - kernel_size=2, - stride=2, - count_include_pad=False)) - ref = ref[::-1] - supp = supp[::-1] - - # flow computation - flow = ref[0].new_zeros(n, 2, h // 32, w // 32) - for level in range(len(ref)): - if level == 0: - flow_up = flow - else: - flow_up = F.interpolate( - input=flow, - scale_factor=2, - mode='bilinear', - align_corners=True) * 2.0 - - # add the residue to the upsampled flow - flow = flow_up + self.basic_module[level]( - torch.cat([ - ref[level], - flow_warp( - supp[level], - flow_up.permute(0, 2, 3, 1), - padding_mode='border'), flow_up - ], 1)) - - return flow - - def forward(self, ref, supp): - """Forward function of SPyNet. - - This function computes the optical flow from ref to supp. - - Args: - ref (Tensor): Reference image with shape of (n, 3, h, w). - supp (Tensor): Supporting image with shape of (n, 3, h, w). - - Returns: - Tensor: Estimated optical flow: (n, 2, h, w). - """ - - # upsize to a multiple of 32 - h, w = ref.shape[2:4] - w_up = w if (w % 32) == 0 else 32 * (w // 32 + 1) - h_up = h if (h % 32) == 0 else 32 * (h // 32 + 1) - ref = F.interpolate( - input=ref, size=(h_up, w_up), mode='bilinear', align_corners=False) - supp = F.interpolate( - input=supp, - size=(h_up, w_up), - mode='bilinear', - align_corners=False) - - # compute flow, and resize back to the original resolution - flow = F.interpolate( - input=self.compute_flow(ref, supp), - size=(h, w), - mode='bilinear', - align_corners=False) - - # adjust the flow values - flow[:, 0, :, :] *= float(w) / float(w_up) - flow[:, 1, :, :] *= float(h) / float(h_up) - - return flow - - -class SPyNetBasicModule(nn.Module): - """Basic Module for SPyNet. - - Paper: - Optical Flow Estimation using a Spatial Pyramid Network, CVPR, 2017 - """ - - def __init__(self): - super().__init__() - - self.basic_module = nn.Sequential( - ConvModule( - in_channels=8, - out_channels=32, - kernel_size=7, - stride=1, - padding=3, - norm_cfg=None, - act_cfg=dict(type='ReLU')), - ConvModule( - in_channels=32, - out_channels=64, - kernel_size=7, - stride=1, - padding=3, - norm_cfg=None, - act_cfg=dict(type='ReLU')), - ConvModule( - in_channels=64, - out_channels=32, - kernel_size=7, - stride=1, - padding=3, - norm_cfg=None, - act_cfg=dict(type='ReLU')), - ConvModule( - in_channels=32, - out_channels=16, - kernel_size=7, - stride=1, - padding=3, - norm_cfg=None, - act_cfg=dict(type='ReLU')), - ConvModule( - in_channels=16, - out_channels=2, - kernel_size=7, - stride=1, - padding=3, - norm_cfg=None, - act_cfg=None)) - - def forward(self, tensor_input): - """ - Args: - tensor_input (Tensor): Input tensor with shape (b, 8, h, w). - 8 channels contain: - [reference image (3), neighbor image (3), initial flow (2)]. - - Returns: - Tensor: Refined flow with shape (b, 2, h, w) - """ - return self.basic_module(tensor_input) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/sr_backbones/basicvsr_pp.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/sr_backbones/basicvsr_pp.py deleted file mode 100755 index 42e23fda8..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/backbones/sr_backbones/basicvsr_pp.py +++ /dev/null @@ -1,434 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import constant_init -from mmcv.ops import ModulatedDeformConv2d, modulated_deform_conv2d -from mmcv.runner import load_checkpoint - -from mmedit.models.backbones.sr_backbones.basicvsr_net import ( - ResidualBlocksWithInputConv, SPyNet) -from mmedit.models.common import PixelShufflePack, flow_warp -from mmedit.models.registry import BACKBONES -from mmedit.utils import get_root_logger - - -@BACKBONES.register_module() -class BasicVSRPlusPlus(nn.Module): - """BasicVSR++ network structure. - - Support either x4 upsampling or same size output. - - Paper: - BasicVSR++: Improving Video Super-Resolution with Enhanced Propagation - and Alignment - - Args: - mid_channels (int, optional): Channel number of the intermediate - features. Default: 64. - num_blocks (int, optional): The number of residual blocks in each - propagation branch. Default: 7. - max_residue_magnitude (int): The maximum magnitude of the offset - residue (Eq. 6 in paper). Default: 10. - is_low_res_input (bool, optional): Whether the input is low-resolution - or not. If False, the output resolution is equal to the input - resolution. Default: True. - spynet_pretrained (str, optional): Pre-trained model path of SPyNet. - Default: None. - cpu_cache_length (int, optional): When the length of sequence is larger - than this value, the intermediate features are sent to CPU. This - saves GPU memory, but slows down the inference speed. You can - increase this number if you have a GPU with large memory. - Default: 100. - """ - - def __init__(self, - mid_channels=64, - num_blocks=7, - max_residue_magnitude=10, - is_low_res_input=True, - spynet_pretrained=None, - cpu_cache_length=100): - - super().__init__() - self.mid_channels = mid_channels - self.is_low_res_input = is_low_res_input - self.cpu_cache_length = cpu_cache_length - - # optical flow - self.spynet = SPyNet(pretrained=spynet_pretrained) - - # feature extraction module - if is_low_res_input: - self.feat_extract = ResidualBlocksWithInputConv(3, mid_channels, 5) - else: - self.feat_extract = nn.Sequential( - nn.Conv2d(3, mid_channels, 3, 2, 1), - nn.LeakyReLU(negative_slope=0.1, inplace=True), - nn.Conv2d(mid_channels, mid_channels, 3, 2, 1), - nn.LeakyReLU(negative_slope=0.1, inplace=True), - ResidualBlocksWithInputConv(mid_channels, mid_channels, 5)) - - # propagation branches - self.deform_align = nn.ModuleDict() - self.backbone = nn.ModuleDict() - modules = ['backward_1', 'forward_1', 'backward_2', 'forward_2'] - for i, module in enumerate(modules): - self.deform_align[module] = SecondOrderDeformableAlignment( - 2 * mid_channels, - mid_channels, - 3, - padding=1, - deform_groups=16, - max_residue_magnitude=max_residue_magnitude) - self.backbone[module] = ResidualBlocksWithInputConv( - (2 + i) * mid_channels, mid_channels, num_blocks) - - # upsampling module - self.reconstruction = ResidualBlocksWithInputConv( - 5 * mid_channels, mid_channels, 5) - self.upsample1 = PixelShufflePack( - mid_channels, mid_channels, 2, upsample_kernel=3) - self.upsample2 = PixelShufflePack( - mid_channels, 64, 2, upsample_kernel=3) - self.conv_hr = nn.Conv2d(64, 64, 3, 1, 1) - self.conv_last = nn.Conv2d(64, 3, 3, 1, 1) - self.img_upsample = nn.Upsample( - scale_factor=4, mode='bilinear', align_corners=False) - - # activation function - self.lrelu = nn.LeakyReLU(negative_slope=0.1, inplace=True) - - # check if the sequence is augmented by flipping - self.is_mirror_extended = False - - def check_if_mirror_extended(self, lqs): - """Check whether the input is a mirror-extended sequence. - - If mirror-extended, the i-th (i=0, ..., t-1) frame is equal to the - (t-1-i)-th frame. - - Args: - lqs (tensor): Input low quality (LQ) sequence with - shape (n, t, c, h, w). - """ - - if lqs.size(1) % 2 == 0: - lqs_1, lqs_2 = torch.chunk(lqs, 2, dim=1) - if torch.norm(lqs_1 - lqs_2.flip(1)) == 0: - self.is_mirror_extended = True - - def compute_flow(self, lqs): - """Compute optical flow using SPyNet for feature alignment. - - Note that if the input is an mirror-extended sequence, 'flows_forward' - is not needed, since it is equal to 'flows_backward.flip(1)'. - - Args: - lqs (tensor): Input low quality (LQ) sequence with - shape (n, t, c, h, w). - - Return: - tuple(Tensor): Optical flow. 'flows_forward' corresponds to the - flows used for forward-time propagation (current to previous). - 'flows_backward' corresponds to the flows used for - backward-time propagation (current to next). - """ - - n, t, c, h, w = lqs.size() - lqs_1 = lqs[:, :-1, :, :, :].reshape(-1, c, h, w) - lqs_2 = lqs[:, 1:, :, :, :].reshape(-1, c, h, w) - - flows_backward = self.spynet(lqs_1, lqs_2).view(n, t - 1, 2, h, w) - - if self.is_mirror_extended: # flows_forward = flows_backward.flip(1) - flows_forward = None - else: - flows_forward = self.spynet(lqs_2, lqs_1).view(n, t - 1, 2, h, w) - - if self.cpu_cache: - flows_backward = flows_backward.cpu() - flows_forward = flows_forward.cpu() - - return flows_forward, flows_backward - - def propagate(self, feats, flows, module_name): - """Propagate the latent features throughout the sequence. - - Args: - feats dict(list[tensor]): Features from previous branches. Each - component is a list of tensors with shape (n, c, h, w). - flows (tensor): Optical flows with shape (n, t - 1, 2, h, w). - module_name (str): The name of the propagation branches. Can either - be 'backward_1', 'forward_1', 'backward_2', 'forward_2'. - - Return: - dict(list[tensor]): A dictionary containing all the propagated - features. Each key in the dictionary corresponds to a - propagation branch, which is represented by a list of tensors. - """ - - n, t, _, h, w = flows.size() - - frame_idx = range(0, t + 1) - flow_idx = range(-1, t) - mapping_idx = list(range(0, len(feats['spatial']))) - mapping_idx += mapping_idx[::-1] - - if 'backward' in module_name: - frame_idx = frame_idx[::-1] - flow_idx = frame_idx - - feat_prop = flows.new_zeros(n, self.mid_channels, h, w) - for i, idx in enumerate(frame_idx): - feat_current = feats['spatial'][mapping_idx[idx]] - if self.cpu_cache: - feat_current = feat_current.cuda() - feat_prop = feat_prop.cuda() - # second-order deformable alignment - if i > 0: - flow_n1 = flows[:, flow_idx[i], :, :, :] - if self.cpu_cache: - flow_n1 = flow_n1.cuda() - - cond_n1 = flow_warp(feat_prop, flow_n1.permute(0, 2, 3, 1)) - - # initialize second-order features - feat_n2 = torch.zeros_like(feat_prop) - flow_n2 = torch.zeros_like(flow_n1) - cond_n2 = torch.zeros_like(cond_n1) - - if i > 1: # second-order features - feat_n2 = feats[module_name][-2] - if self.cpu_cache: - feat_n2 = feat_n2.cuda() - - flow_n2 = flows[:, flow_idx[i - 1], :, :, :] - if self.cpu_cache: - flow_n2 = flow_n2.cuda() - - flow_n2 = flow_n1 + flow_warp(flow_n2, - flow_n1.permute(0, 2, 3, 1)) - cond_n2 = flow_warp(feat_n2, flow_n2.permute(0, 2, 3, 1)) - - # flow-guided deformable convolution - cond = torch.cat([cond_n1, feat_current, cond_n2], dim=1) - feat_prop = torch.cat([feat_prop, feat_n2], dim=1) - feat_prop = self.deform_align[module_name](feat_prop, cond, - flow_n1, flow_n2) - - # concatenate and residual blocks - feat = [feat_current] + [ - feats[k][idx] - for k in feats if k not in ['spatial', module_name] - ] + [feat_prop] - if self.cpu_cache: - feat = [f.cuda() for f in feat] - - feat = torch.cat(feat, dim=1) - feat_prop = feat_prop + self.backbone[module_name](feat) - feats[module_name].append(feat_prop) - - if self.cpu_cache: - feats[module_name][-1] = feats[module_name][-1].cpu() - torch.cuda.empty_cache() - - if 'backward' in module_name: - feats[module_name] = feats[module_name][::-1] - - return feats - - def upsample(self, lqs, feats): - """Compute the output image given the features. - - Args: - lqs (tensor): Input low quality (LQ) sequence with - shape (n, t, c, h, w). - feats (dict): The features from the propagation branches. - - Returns: - Tensor: Output HR sequence with shape (n, t, c, 4h, 4w). - - """ - - outputs = [] - num_outputs = len(feats['spatial']) - - mapping_idx = list(range(0, num_outputs)) - mapping_idx += mapping_idx[::-1] - - for i in range(0, lqs.size(1)): - hr = [feats[k].pop(0) for k in feats if k != 'spatial'] - hr.insert(0, feats['spatial'][mapping_idx[i]]) - hr = torch.cat(hr, dim=1) - if self.cpu_cache: - hr = hr.cuda() - - hr = self.reconstruction(hr) - hr = self.lrelu(self.upsample1(hr)) - hr = self.lrelu(self.upsample2(hr)) - hr = self.lrelu(self.conv_hr(hr)) - hr = self.conv_last(hr) - if self.is_low_res_input: - hr += self.img_upsample(lqs[:, i, :, :, :]) - else: - hr += lqs[:, i, :, :, :] - - if self.cpu_cache: - hr = hr.cpu() - torch.cuda.empty_cache() - - outputs.append(hr) - - return torch.stack(outputs, dim=1) - - def forward(self, lqs): - """Forward function for BasicVSR++. - - Args: - lqs (tensor): Input low quality (LQ) sequence with - shape (n, t, c, h, w). - - Returns: - Tensor: Output HR sequence with shape (n, t, c, 4h, 4w). - """ - - n, t, c, h, w = lqs.size() - - # whether to cache the features in CPU (no effect if using CPU) - if t > self.cpu_cache_length and lqs.is_cuda: - self.cpu_cache = True - else: - self.cpu_cache = False - - if self.is_low_res_input: - lqs_downsample = lqs.clone() - else: - lqs_downsample = F.interpolate( - lqs.view(-1, c, h, w), scale_factor=0.25, - mode='bicubic').view(n, t, c, h // 4, w // 4) - - # check whether the input is an extended sequence - self.check_if_mirror_extended(lqs) - - feats = {} - # compute spatial features - if self.cpu_cache: - feats['spatial'] = [] - for i in range(0, t): - feat = self.feat_extract(lqs[:, i, :, :, :]).cpu() - feats['spatial'].append(feat) - torch.cuda.empty_cache() - else: - feats_ = self.feat_extract(lqs.view(-1, c, h, w)) - h, w = feats_.shape[2:] - feats_ = feats_.view(n, t, -1, h, w) - feats['spatial'] = [feats_[:, i, :, :, :] for i in range(0, t)] - - # compute optical flow using the low-res inputs - assert lqs_downsample.size(3) >= 64 and lqs_downsample.size(4) >= 64, ( - 'The height and width of low-res inputs must be at least 64, ' - f'but got {h} and {w}.') - flows_forward, flows_backward = self.compute_flow(lqs_downsample) - - # feature propagation - for iter_ in [1, 2]: - for direction in ['backward', 'forward']: - module = f'{direction}_{iter_}' - - feats[module] = [] - - if direction == 'backward': - flows = flows_backward - elif flows_forward is not None: - flows = flows_forward - else: - flows = flows_backward.flip(1) - - feats = self.propagate(feats, flows, module) - if self.cpu_cache: - del flows - torch.cuda.empty_cache() - - return self.upsample(lqs, feats) - - def init_weights(self, pretrained=None, strict=True): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Default: None. - strict (bool, optional): Whether strictly load the pretrained - model. Default: True. - """ - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=strict, logger=logger) - elif pretrained is not None: - raise TypeError(f'"pretrained" must be a str or None. ' - f'But received {type(pretrained)}.') - - -class SecondOrderDeformableAlignment(ModulatedDeformConv2d): - """Second-order deformable alignment module. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - stride (int or tuple[int]): Same as nn.Conv2d. - padding (int or tuple[int]): Same as nn.Conv2d. - dilation (int or tuple[int]): Same as nn.Conv2d. - groups (int): Same as nn.Conv2d. - bias (bool or str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if norm_cfg is None, otherwise - False. - max_residue_magnitude (int): The maximum magnitude of the offset - residue (Eq. 6 in paper). Default: 10. - - """ - - def __init__(self, *args, **kwargs): - self.max_residue_magnitude = kwargs.pop('max_residue_magnitude', 10) - - super(SecondOrderDeformableAlignment, self).__init__(*args, **kwargs) - - self.conv_offset = nn.Sequential( - nn.Conv2d(3 * self.out_channels + 4, self.out_channels, 3, 1, 1), - nn.LeakyReLU(negative_slope=0.1, inplace=True), - nn.Conv2d(self.out_channels, self.out_channels, 3, 1, 1), - nn.LeakyReLU(negative_slope=0.1, inplace=True), - nn.Conv2d(self.out_channels, self.out_channels, 3, 1, 1), - nn.LeakyReLU(negative_slope=0.1, inplace=True), - nn.Conv2d(self.out_channels, 27 * self.deform_groups, 3, 1, 1), - ) - - self.init_offset() - - def init_offset(self): - constant_init(self.conv_offset[-1], val=0, bias=0) - - def forward(self, x, extra_feat, flow_1, flow_2): - extra_feat = torch.cat([extra_feat, flow_1, flow_2], dim=1) - out = self.conv_offset(extra_feat) - o1, o2, mask = torch.chunk(out, 3, dim=1) - - # offset - offset = self.max_residue_magnitude * torch.tanh( - torch.cat((o1, o2), dim=1)) - offset_1, offset_2 = torch.chunk(offset, 2, dim=1) - offset_1 = offset_1 + flow_1.flip(1).repeat(1, - offset_1.size(1) // 2, 1, - 1) - offset_2 = offset_2 + flow_2.flip(1).repeat(1, - offset_2.size(1) // 2, 1, - 1) - offset = torch.cat([offset_1, offset_2], dim=1) - - # mask - mask = torch.sigmoid(mask) - - return modulated_deform_conv2d(x, offset, mask, self.weight, self.bias, - self.stride, self.padding, - self.dilation, self.groups, - self.deform_groups) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/base.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/base.py deleted file mode 100755 index 02327e283..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/base.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod -from collections import OrderedDict - -import torch -import torch.nn as nn - - -class BaseModel(nn.Module, metaclass=ABCMeta): - """Base model. - - All models should subclass it. - All subclass should overwrite: - - ``init_weights``, supporting to initialize models. - - ``forward_train``, supporting to forward when training. - - ``forward_test``, supporting to forward when testing. - - ``train_step``, supporting to train one step when training. - """ - - @abstractmethod - def init_weights(self): - """Abstract method for initializing weight. - - All subclass should overwrite it. - """ - - @abstractmethod - def forward_train(self, imgs, labels): - """Abstract method for training forward. - - All subclass should overwrite it. - """ - - @abstractmethod - def forward_test(self, imgs): - """Abstract method for testing forward. - - All subclass should overwrite it. - """ - - def forward(self, imgs, labels, test_mode, **kwargs): - """Forward function for base model. - - Args: - imgs (Tensor): Input image(s). - labels (Tensor): Ground-truth label(s). - test_mode (bool): Whether in test mode. - kwargs (dict): Other arguments. - - Returns: - Tensor: Forward results. - """ - - if test_mode: - return self.forward_test(imgs, **kwargs) - - return self.forward_train(imgs, labels, **kwargs) - - @abstractmethod - def train_step(self, data_batch, optimizer): - """Abstract method for one training step. - - All subclass should overwrite it. - """ - - def val_step(self, data_batch, **kwargs): - """Abstract method for one validation step. - - All subclass should overwrite it. - """ - output = self.forward_test(**data_batch, **kwargs) - return output - - def parse_losses(self, losses): - """Parse losses dict for different loss variants. - - Args: - losses (dict): Loss dict. - - Returns: - loss (float): Sum of the total loss. - log_vars (dict): loss dict for different variants. - """ - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(_value for _key, _value in log_vars.items() - if 'loss' in _key) - - log_vars['loss'] = loss - for name in log_vars: - log_vars[name] = log_vars[name].item() - - return loss, log_vars diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/builder.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/builder.py deleted file mode 100755 index 8606225aa..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/builder.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv import build_from_cfg - -from .registry import BACKBONES, COMPONENTS, LOSSES, MODELS - - -def build(cfg, registry, default_args=None): - """Build module function. - - Args: - cfg (dict): Configuration for building modules. - registry (obj): ``registry`` object. - default_args (dict, optional): Default arguments. Defaults to None. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return nn.Sequential(*modules) - - return build_from_cfg(cfg, registry, default_args) - - -def build_backbone(cfg): - """Build backbone. - - Args: - cfg (dict): Configuration for building backbone. - """ - return build(cfg, BACKBONES) - - -def build_component(cfg): - """Build component. - - Args: - cfg (dict): Configuration for building component. - """ - return build(cfg, COMPONENTS) - - -def build_loss(cfg): - """Build loss. - - Args: - cfg (dict): Configuration for building loss. - """ - return build(cfg, LOSSES) - - -def build_model(cfg, train_cfg=None, test_cfg=None): - """Build model. - - Args: - cfg (dict): Configuration for building model. - train_cfg (dict): Training configuration. Default: None. - test_cfg (dict): Testing configuration. Default: None. - """ - return build(cfg, MODELS, dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/__init__.py deleted file mode 100755 index 7ebeb4fba..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .aspp import ASPP -from .contextual_attention import ContextualAttentionModule -from .conv import * # noqa: F401, F403 -from .downsample import pixel_unshuffle -from .ensemble import SpatialTemporalEnsemble -from .flow_warp import flow_warp -from .gated_conv_module import SimpleGatedConvModule -from .gca_module import GCAModule -from .generation_model_utils import (GANImageBuffer, ResidualBlockWithDropout, - UnetSkipConnectionBlock, - generation_init_weights) -from .img_normalize import ImgNormalize -from .linear_module import LinearModule -from .mask_conv_module import MaskConvModule -from .model_utils import (extract_around_bbox, extract_bbox_patch, scale_bbox, - set_requires_grad) -from .partial_conv import PartialConv2d -from .separable_conv_module import DepthwiseSeparableConvModule -from .sr_backbone_utils import (ResidualBlockNoBN, default_init_weights, - make_layer) -from .upsample import PixelShufflePack - -__all__ = [ - 'ASPP', 'PartialConv2d', 'PixelShufflePack', 'default_init_weights', - 'ResidualBlockNoBN', 'make_layer', 'MaskConvModule', 'extract_bbox_patch', - 'extract_around_bbox', 'set_requires_grad', 'scale_bbox', - 'DepthwiseSeparableConvModule', 'ContextualAttentionModule', 'GCAModule', - 'SimpleGatedConvModule', 'LinearModule', 'flow_warp', 'ImgNormalize', - 'generation_init_weights', 'GANImageBuffer', 'UnetSkipConnectionBlock', - 'ResidualBlockWithDropout', 'pixel_unshuffle', 'SpatialTemporalEnsemble' -] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/aspp.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/aspp.py deleted file mode 100755 index c1e58e858..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/aspp.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.cnn import ConvModule -from torch import nn -from torch.nn import functional as F - -from .separable_conv_module import DepthwiseSeparableConvModule - - -class ASPPPooling(nn.Sequential): - - def __init__(self, in_channels, out_channels, conv_cfg, norm_cfg, act_cfg): - super().__init__( - nn.AdaptiveAvgPool2d(1), - ConvModule( - in_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, x): - size = x.shape[-2:] - for mod in self: - x = mod(x) - return F.interpolate( - x, size=size, mode='bilinear', align_corners=False) - - -class ASPP(nn.Module): - """ASPP module from DeepLabV3. - - The code is adopted from - https://github.com/pytorch/vision/blob/master/torchvision/models/ - segmentation/deeplabv3.py - - For more information about the module: - `"Rethinking Atrous Convolution for Semantic Image Segmentation" - `_. - - Args: - in_channels (int): Input channels of the module. - out_channels (int): Output channels of the module. - mid_channels (int): Output channels of the intermediate ASPP conv - modules. - dilations (Sequence[int]): Dilation rate of three ASPP conv module. - Default: [12, 24, 36]. - conv_cfg (dict): Config dict for convolution layer. If "None", - nn.Conv2d will be applied. Default: None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - separable_conv (bool): Whether replace normal conv with depthwise - separable conv which is faster. Default: False. - """ - - def __init__(self, - in_channels, - out_channels=256, - mid_channels=256, - dilations=(12, 24, 36), - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - separable_conv=False): - super().__init__() - - if separable_conv: - conv_module = DepthwiseSeparableConvModule - else: - conv_module = ConvModule - - modules = [] - modules.append( - ConvModule( - in_channels, - mid_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - for dilation in dilations: - modules.append( - conv_module( - in_channels, - mid_channels, - 3, - padding=dilation, - dilation=dilation, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - modules.append( - ASPPPooling(in_channels, mid_channels, conv_cfg, norm_cfg, - act_cfg)) - - self.convs = nn.ModuleList(modules) - - self.project = nn.Sequential( - ConvModule( - 5 * mid_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg), nn.Dropout(0.5)) - - def forward(self, x): - """Forward function for ASPP module. - - Args: - x (Tensor): Input tensor with shape (N, C, H, W). - - Returns: - Tensor: Output tensor. - """ - res = [] - for conv in self.convs: - res.append(conv(x)) - res = torch.cat(res, dim=1) - return self.project(res) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/contextual_attention.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/contextual_attention.py deleted file mode 100755 index 7dcf4099e..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/contextual_attention.py +++ /dev/null @@ -1,379 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch -import torch.nn as nn -import torch.nn.functional as F - - -class ContextualAttentionModule(nn.Module): - """Contexture attention module. - - The details of this module can be found in: - Generative Image Inpainting with Contextual Attention - - Args: - unfold_raw_kernel_size (int): Kernel size used in unfolding raw - feature. Default: 4. - unfold_raw_stride (int): Stride used in unfolding raw feature. Default: - 2. - unfold_raw_padding (int): Padding used in unfolding raw feature. - Default: 1. - unfold_corr_kernel_size (int): Kernel size used in unfolding - context for computing correlation maps. Default: 3. - unfold_corr_stride (int): Stride used in unfolding context for - computing correlation maps. Default: 1. - unfold_corr_dilation (int): Dilation used in unfolding context for - computing correlation maps. Default: 1. - unfold_corr_padding (int): Padding used in unfolding context for - computing correlation maps. Default: 1. - scale (float): The resale factor used in resize input features. - Default: 0.5. - fuse_kernel_size (int): The kernel size used in fusion module. - Default: 3. - softmax_scale (float): The scale factor for softmax function. - Default: 10. - return_attention_score (bool): If True, the attention score will be - returned. Default: True. - """ - - def __init__(self, - unfold_raw_kernel_size=4, - unfold_raw_stride=2, - unfold_raw_padding=1, - unfold_corr_kernel_size=3, - unfold_corr_stride=1, - unfold_corr_dilation=1, - unfold_corr_padding=1, - scale=0.5, - fuse_kernel_size=3, - softmax_scale=10, - return_attention_score=True): - super().__init__() - self.unfold_raw_kernel_size = unfold_raw_kernel_size - self.unfold_raw_stride = unfold_raw_stride - self.unfold_raw_padding = unfold_raw_padding - self.unfold_corr_kernel_size = unfold_corr_kernel_size - self.unfold_corr_stride = unfold_corr_stride - self.unfold_corr_dilation = unfold_corr_dilation - self.unfold_corr_padding = unfold_corr_padding - self.scale = scale - self.fuse_kernel_size = fuse_kernel_size - self.with_fuse_correlation = fuse_kernel_size > 1 - self.softmax_scale = softmax_scale - self.return_attention_score = return_attention_score - - if self.with_fuse_correlation: - assert fuse_kernel_size % 2 == 1 - fuse_kernel = torch.eye(fuse_kernel_size).view( - 1, 1, fuse_kernel_size, fuse_kernel_size) - self.register_buffer('fuse_kernel', fuse_kernel) - padding = int((fuse_kernel_size - 1) // 2) - self.fuse_conv = partial(F.conv2d, padding=padding, stride=1) - self.softmax = nn.Softmax(dim=1) - - def forward(self, x, context, mask=None): - """Forward Function. - - Args: - x (torch.Tensor): Tensor with shape (n, c, h, w). - context (torch.Tensor): Tensor with shape (n, c, h, w). - mask (torch.Tensor): Tensor with shape (n, 1, h, w). Default: None. - - Returns: - tuple(torch.Tensor): Features after contextural attention. - """ - # raw features to be used in copy (deconv) - raw_context = context - raw_context_cols = self.im2col( - raw_context, - kernel_size=self.unfold_raw_kernel_size, - stride=self.unfold_raw_stride, - padding=self.unfold_raw_padding, - normalize=False, - return_cols=True) - # resize the feature to reduce computational cost - x = F.interpolate(x, scale_factor=self.scale) - context = F.interpolate(context, scale_factor=self.scale) - - context_cols = self.im2col( - context, - kernel_size=self.unfold_corr_kernel_size, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation, - normalize=True, - return_cols=True) - h_unfold, w_unfold = self.calculate_unfold_hw( - context.size()[-2:], - kernel_size=self.unfold_corr_kernel_size, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation, - ) - # reshape context_cols to - # (n*h_unfold*w_unfold, c, unfold_mks, unfold_mks) - # 'mks' is short for 'mask_kernel_size' - context_cols = context_cols.reshape(-1, *context_cols.shape[2:]) - - # the shape of correlation map should be: - # (n, h_unfold*w_unfold, h', w') - correlation_map = self.patch_correlation(x, context_cols) - # fuse correlation map to enlarge consistent attention region. - if self.with_fuse_correlation: - correlation_map = self.fuse_correlation_map( - correlation_map, h_unfold, w_unfold) - - correlation_map = self.mask_correlation_map(correlation_map, mask=mask) - - attention_score = self.softmax(correlation_map * self.softmax_scale) - - raw_context_filter = raw_context_cols.reshape( - -1, *raw_context_cols.shape[2:]) - output = self.patch_copy_deconv(attention_score, raw_context_filter) - # deconv will cause overlap and we need to remove the effects of that - overlap_factor = self.calculate_overlap_factor(attention_score) - output /= overlap_factor - - if self.return_attention_score: - n, _, h_s, w_s = attention_score.size() - attention_score = attention_score.view(n, h_unfold, w_unfold, h_s, - w_s) - return output, attention_score - - return output - - def patch_correlation(self, x, kernel): - """Calculate patch correlation. - - Args: - x (torch.Tensor): Input tensor. - kernel (torch.Tensor): Kernel tensor. - - Returns: - torch.Tensor: Tensor with shape of (n, l, h, w). - """ - n, _, h_in, w_in = x.size() - - patch_corr = F.conv2d( - x.view(1, -1, h_in, w_in), - kernel, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation, - groups=n) - h_out, w_out = patch_corr.size()[-2:] - return patch_corr.view(n, -1, h_out, w_out) - - def patch_copy_deconv(self, attention_score, context_filter): - """Copy patches using deconv. - - Args: - attention_score (torch.Tensor): Tensor with shape of (n, l , h, w). - context_filter (torch.Tensor): Filter kernel. - - Returns: - torch.Tensor: Tensor with shape of (n, c, h, w). - """ - n, _, h, w = attention_score.size() - attention_score = attention_score.view(1, -1, h, w) - output = F.conv_transpose2d( - attention_score, - context_filter, - stride=self.unfold_raw_stride, - padding=self.unfold_raw_padding, - groups=n) - h_out, w_out = output.size()[-2:] - return output.view(n, -1, h_out, w_out) - - def fuse_correlation_map(self, correlation_map, h_unfold, w_unfold): - """Fuse correlation map. - - This operation is to fuse correlation map for increasing large - consistent correlation regions. - - The mechanism behind this op is simple and easy to understand. A - standard 'Eye' matrix will be applied as a filter on the correlation - map in horizontal and vertical direction. - - The shape of input correlation map is (n, h_unfold*w_unfold, h, w). - When adopting fusing, we will apply convolutional filter in the - reshaped feature map with shape of (n, 1, h_unfold*w_fold, h*w). - - A simple specification for horizontal direction is shown below: - - .. code-block:: python - - (h, (h, (h, (h, - 0) 1) 2) 3) ... - (h, 0) - (h, 1) 1 - (h, 2) 1 - (h, 3) 1 - ... - - """ - # horizontal direction - n, _, h_map, w_map = correlation_map.size() - map_ = correlation_map.permute(0, 2, 3, 1) - map_ = map_.reshape(n, h_map * w_map, h_unfold * w_unfold, 1) - map_ = map_.permute(0, 3, 1, 2).contiguous() - map_ = self.fuse_conv(map_, self.fuse_kernel) - - correlation_map = map_.view(n, h_unfold, w_unfold, h_map, w_map) - - # vertical direction - map_ = correlation_map.permute(0, 2, 1, 4, - 3).reshape(n, 1, h_unfold * w_unfold, - h_map * w_map) - map_ = self.fuse_conv(map_, self.fuse_kernel) - - # Note that the dimension should be transposed since the convolution of - # eye matrix will put the normed scores into the last several dimension - correlation_map = map_.view(n, w_unfold, h_unfold, w_map, - h_map).permute(0, 4, 3, 2, 1) - correlation_map = correlation_map.reshape(n, -1, h_unfold, w_unfold) - - return correlation_map - - def calculate_unfold_hw(self, - input_size, - kernel_size=3, - stride=1, - dilation=1, - padding=0): - """Calculate (h, w) after unfolding - - The official implementation of `unfold` in pytorch will put the - dimension (h, w) into `L`. Thus, this function is just to calculate the - (h, w) according to the equation in: - https://pytorch.org/docs/stable/nn.html#torch.nn.Unfold - """ - h_in, w_in = input_size - - h_unfold = int((h_in + 2 * padding - dilation * - (kernel_size - 1) - 1) / stride + 1) - - w_unfold = int((w_in + 2 * padding - dilation * - (kernel_size - 1) - 1) / stride + 1) - return h_unfold, w_unfold - - def calculate_overlap_factor(self, attention_score): - """Calculate the overlap factor after applying deconv. - - Args: - attention_score (torch.Tensor): The attention score with shape of - (n, c, h, w). - - Returns: - torch.Tensor: The overlap factor will be returned. - """ - h, w = attention_score.shape[-2:] - kernel_size = self.unfold_raw_kernel_size - - ones_input = torch.ones(1, 1, h, w).to(attention_score) - ones_filter = torch.ones(1, 1, kernel_size, - kernel_size).to(attention_score) - overlap = F.conv_transpose2d( - ones_input, - ones_filter, - stride=self.unfold_raw_stride, - padding=self.unfold_raw_padding) - - # avoid division by zero - overlap[overlap == 0] = 1. - return overlap - - def mask_correlation_map(self, correlation_map, mask): - """Add mask weight for correlation map. - - Add a negative infinity number to the masked regions so that softmax - function will result in 'zero' in those regions. - - Args: - correlation_map (torch.Tensor): Correlation map with shape of - (n, h_unfold*w_unfold, h_map, w_map). - mask (torch.Tensor): Mask tensor with shape of (n, c, h, w). '1' - in the mask indicates masked region while '0' indicates valid - region. - - Returns: - torch.Tensor: Updated correlation map with mask. - """ - if mask is not None: - mask = F.interpolate(mask, scale_factor=self.scale) - # if any pixel is masked in patch, the patch is considered to be - # masked - mask_cols = self.im2col( - mask, - kernel_size=self.unfold_corr_kernel_size, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation) - mask_cols = (mask_cols.sum(dim=1, keepdim=True) > 0).float() - mask_cols = mask_cols.permute(0, 2, - 1).reshape(mask.size(0), -1, 1, 1) - # add negative inf will bring zero in softmax - mask_cols[mask_cols == 1] = -float('inf') - correlation_map += mask_cols - return correlation_map - - def im2col(self, - img, - kernel_size, - stride=1, - padding=0, - dilation=1, - normalize=False, - return_cols=False): - """Reshape image-style feature to columns. - - This function is used for unfold feature maps to columns. The - details of this function can be found in: - https://pytorch.org/docs/1.1.0/nn.html?highlight=unfold#torch.nn.Unfold - - Args: - img (torch.Tensor): Features to be unfolded. The shape of this - feature should be (n, c, h, w). - kernel_size (int): In this function, we only support square kernel - with same height and width. - stride (int): Stride number in unfolding. Default: 1. - padding (int): Padding number in unfolding. Default: 0. - dilation (int): Dilation number in unfolding. Default: 1. - normalize (bool): If True, the unfolded feature will be normalized. - Default: False. - return_cols (bool): The official implementation in PyTorch of - unfolding will return features with shape of - (n, c*$prod{kernel_size}$, L). If True, the features will be - reshaped to (n, L, c, kernel_size, kernel_size). Otherwise, - the results will maintain the shape as the official - implementation. - - Returns: - torch.Tensor: Unfolded columns. If `return_cols` is True, the \ - shape of output tensor is \ - `(n, L, c, kernel_size, kernel_size)`. Otherwise, the shape \ - will be `(n, c*$prod{kernel_size}$, L)`. - """ - - # unfold img to columns with shape (n, c*kernel_size**2, num_cols) - img_unfold = F.unfold( - img, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation) - # normalize the feature map - if normalize: - norm = torch.sqrt((img_unfold**2).sum(dim=1, keepdim=True)) - eps = torch.tensor([1e-4]).to(img) - img_unfold = img_unfold / torch.max(norm, eps) - - if return_cols: - img_unfold_ = img_unfold.permute(0, 2, 1) - n, num_cols = img_unfold_.size()[:2] - img_cols = img_unfold_.view(n, num_cols, img.size(1), kernel_size, - kernel_size) - return img_cols - - return img_unfold diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/conv.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/conv.py deleted file mode 100755 index 8e03d0f7d..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/conv.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import CONV_LAYERS -from torch import nn - -CONV_LAYERS.register_module('Deconv', module=nn.ConvTranspose2d) -# TODO: octave conv diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/downsample.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/downsample.py deleted file mode 100755 index 7d51a5b55..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/downsample.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def pixel_unshuffle(x, scale): - """Down-sample by pixel unshuffle. - - Args: - x (Tensor): Input tensor. - scale (int): Scale factor. - - Returns: - Tensor: Output tensor. - """ - - b, c, h, w = x.shape - if h % scale != 0 or w % scale != 0: - raise AssertionError( - f'Invalid scale ({scale}) of pixel unshuffle for tensor ' - f'with shape: {x.shape}') - h = int(h / scale) - w = int(w / scale) - x = x.view(b, c, h, scale, w, scale) - x = x.permute(0, 1, 3, 5, 2, 4) - return x.reshape(b, -1, h, w) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/ensemble.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/ensemble.py deleted file mode 100755 index 019b6e0f0..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/ensemble.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class SpatialTemporalEnsemble(nn.Module): - """ Apply spatial and temporal ensemble and compute outputs - - Args: - is_temporal_ensemble (bool, optional): Whether to apply ensemble - temporally. If True, the sequence will also be flipped temporally. - If the input is an image, this argument must be set to False. - Default: False. - - """ - - def __init__(self, is_temporal_ensemble=False): - - super().__init__() - - self.is_temporal_ensemble = is_temporal_ensemble - - def _transform(self, imgs, mode): - """Apply spatial transform (flip, rotate) to the images. - - Args: - imgs (torch.Tensor): The images to be transformed/ - mode (str): The mode of transform. Supported values are 'vertical', - 'horizontal', and 'transpose', corresponding to vertical flip, - horizontal flip, and rotation, respectively. - - Returns: - torch.Tensor: Output of the model with spatial ensemble applied. - - """ - - is_single_image = False - if imgs.ndim == 4: - if self.is_temporal_ensemble: - raise ValueError('"is_temporal_ensemble" must be False if ' - 'the input is an image.') - is_single_image = True - imgs = imgs.unsqueeze(1) - - if mode == 'vertical': - imgs = imgs.flip(4).clone() - elif mode == 'horizontal': - imgs = imgs.flip(3).clone() - elif mode == 'transpose': - imgs = imgs.permute(0, 1, 2, 4, 3).clone() - - if is_single_image: - imgs = imgs.squeeze(1) - - return imgs - - def spatial_ensemble(self, imgs, model): - """Apply spatial ensemble. - - Args: - imgs (torch.Tensor): The images to be processed by the model. Its - size should be either (n, t, c, h, w) or (n, c, h, w). - model (nn.Module): The model to process the images. - - Returns: - torch.Tensor: Output of the model with spatial ensemble applied. - - """ - - img_list = [imgs.cpu()] - for mode in ['vertical', 'horizontal', 'transpose']: - img_list.extend([self._transform(t, mode) for t in img_list]) - - output_list = [model(t.to(imgs.device)).cpu() for t in img_list] - for i in range(len(output_list)): - if i > 3: - output_list[i] = self._transform(output_list[i], 'transpose') - if i % 4 > 1: - output_list[i] = self._transform(output_list[i], 'horizontal') - if (i % 4) % 2 == 1: - output_list[i] = self._transform(output_list[i], 'vertical') - - outputs = torch.stack(output_list, dim=0) - outputs = outputs.mean(dim=0, keepdim=False) - - return outputs.to(imgs.device) - - def forward(self, imgs, model): - """Apply spatial and temporal ensemble. - - Args: - imgs (torch.Tensor): The images to be processed by the model. Its - size should be either (n, t, c, h, w) or (n, c, h, w). - model (nn.Module): The model to process the images. - - Returns: - torch.Tensor: Output of the model with spatial ensemble applied. - - """ - outputs = self.spatial_ensemble(imgs, model) - if self.is_temporal_ensemble: - outputs += self.spatial_ensemble(imgs.flip(1), model).flip(1) - outputs *= 0.5 - - return outputs diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/flow_warp.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/flow_warp.py deleted file mode 100755 index 7083230db..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/flow_warp.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn.functional as F - - -def flow_warp(x, - flow, - interpolation='bilinear', - padding_mode='zeros', - align_corners=True): - """Warp an image or a feature map with optical flow. - - Args: - x (Tensor): Tensor with size (n, c, h, w). - flow (Tensor): Tensor with size (n, h, w, 2). The last dimension is - a two-channel, denoting the width and height relative offsets. - Note that the values are not normalized to [-1, 1]. - interpolation (str): Interpolation mode: 'nearest' or 'bilinear'. - Default: 'bilinear'. - padding_mode (str): Padding mode: 'zeros' or 'border' or 'reflection'. - Default: 'zeros'. - align_corners (bool): Whether align corners. Default: True. - - Returns: - Tensor: Warped image or feature map. - """ - if x.size()[-2:] != flow.size()[1:3]: - raise ValueError(f'The spatial sizes of input ({x.size()[-2:]}) and ' - f'flow ({flow.size()[1:3]}) are not the same.') - _, _, h, w = x.size() - # create mesh grid - device = flow.device - grid_y, grid_x = torch.meshgrid( - torch.arange(0, h, device=device, dtype=x.dtype), - torch.arange(0, w, device=device, dtype=x.dtype)) - grid = torch.stack((grid_x, grid_y), 2) # h, w, 2 - grid.requires_grad = False - - grid_flow = grid + flow - # scale grid_flow to [-1,1] - grid_flow_x = 2.0 * grid_flow[:, :, :, 0] / max(w - 1, 1) - 1.0 - grid_flow_y = 2.0 * grid_flow[:, :, :, 1] / max(h - 1, 1) - 1.0 - grid_flow = torch.stack((grid_flow_x, grid_flow_y), dim=3) - output = F.grid_sample( - x, - grid_flow, - mode=interpolation, - padding_mode=padding_mode, - align_corners=align_corners) - return output diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/gated_conv_module.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/gated_conv_module.py deleted file mode 100755 index fed22c4f5..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/gated_conv_module.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, build_activation_layer - - -class SimpleGatedConvModule(nn.Module): - """Simple Gated Convolutional Module. - - This module is a simple gated convolutional module. The detailed formula - is: - - .. math:: - y = \\phi(conv1(x)) * \\sigma(conv2(x)), - - where `phi` is the feature activation function and `sigma` is the gate - activation function. In default, the gate activation function is sigmoid. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): The number of channels of the output feature. Note - that `out_channels` in the conv module is doubled since this module - contains two convolutions for feature and gate separately. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - feat_act_cfg (dict): Config dict for feature activation layer. - gate_act_cfg (dict): Config dict for gate activation layer. - kwargs (keyword arguments): Same as `ConvModule`. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - feat_act_cfg=dict(type='ELU'), - gate_act_cfg=dict(type='Sigmoid'), - **kwargs): - super().__init__() - # the activation function should specified outside conv module - kwargs_ = copy.deepcopy(kwargs) - kwargs_['act_cfg'] = None - self.with_feat_act = feat_act_cfg is not None - self.with_gate_act = gate_act_cfg is not None - - self.conv = ConvModule(in_channels, out_channels * 2, kernel_size, - **kwargs_) - - if self.with_feat_act: - self.feat_act = build_activation_layer(feat_act_cfg) - - if self.with_gate_act: - self.gate_act = build_activation_layer(gate_act_cfg) - - def forward(self, x): - """Forward Function. - - Args: - x (torch.Tensor): Input tensor with shape of (n, c, h, w). - - Returns: - torch.Tensor: Output tensor with shape of (n, c, h', w'). - """ - x = self.conv(x) - x, gate = torch.split(x, x.size(1) // 2, dim=1) - if self.with_feat_act: - x = self.feat_act(x) - if self.with_gate_act: - gate = self.gate_act(gate) - x = x * gate - - return x diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/gca_module.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/gca_module.py deleted file mode 100755 index 75ab64e5b..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/gca_module.py +++ /dev/null @@ -1,358 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, constant_init, xavier_init -from torch.nn import functional as F - - -class GCAModule(nn.Module): - """Guided Contextual Attention Module. - - From https://arxiv.org/pdf/2001.04069.pdf. - Based on https://github.com/nbei/Deep-Flow-Guided-Video-Inpainting. - This module use image feature map to augment the alpha feature map with - guided contextual attention score. - - Image feature and alpha feature are unfolded to small patches and later - used as conv kernel. Thus, we refer the unfolding size as kernel size. - Image feature patches have a default kernel size 3 while the kernel size of - alpha feature patches could be specified by `rate` (see `rate` below). The - image feature patches are used to convolve with the image feature itself - to calculate the contextual attention. Then the attention feature map is - convolved by alpha feature patches to obtain the attention alpha feature. - At last, the attention alpha feature is added to the input alpha feature. - - Args: - in_channels (int): Input channels of the guided contextual attention - module. - out_channels (int): Output channels of the guided contextual attention - module. - kernel_size (int): Kernel size of image feature patches. Default 3. - stride (int): Stride when unfolding the image feature. Default 1. - rate (int): The downsample rate of image feature map. The corresponding - kernel size and stride of alpha feature patches will be `rate x 2` - and `rate`. It could be regarded as the granularity of the gca - module. Default: 2. - pad_args (dict): Parameters of padding when convolve image feature with - image feature patches or alpha feature patches. Allowed keys are - `mode` and `value`. See torch.nn.functional.pad() for more - information. Default: dict(mode='reflect'). - interpolation (str): Interpolation method in upsampling and - downsampling. - penalty (float): Punishment hyperparameter to avoid a large correlation - between each unknown patch and itself. - eps (float): A small number to avoid dividing by 0 when calculating - the normed image feature patch. Default: 1e-4. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=3, - stride=1, - rate=2, - pad_args=dict(mode='reflect'), - interpolation='nearest', - penalty=-1e4, - eps=1e-4): - super().__init__() - self.kernel_size = kernel_size - self.stride = stride - self.rate = rate - self.pad_args = pad_args - self.interpolation = interpolation - self.penalty = penalty - self.eps = eps - - # reduced the channels of input image feature. - self.guidance_conv = nn.Conv2d(in_channels, in_channels // 2, 1) - - # convolution after the attention alpha feature - self.out_conv = ConvModule( - out_channels, - out_channels, - 1, - norm_cfg=dict(type='BN'), - act_cfg=None) - - self.init_weights() - - def init_weights(self): - xavier_init(self.guidance_conv, distribution='uniform') - xavier_init(self.out_conv.conv, distribution='uniform') - constant_init(self.out_conv.norm, 1e-3) - - def forward(self, img_feat, alpha_feat, unknown=None, softmax_scale=1.): - """Forward function of GCAModule. - - Args: - img_feat (Tensor): Image feature map of shape - (N, ori_c, ori_h, ori_w). - alpha_feat (Tensor): Alpha feature map of shape - (N, alpha_c, ori_h, ori_w). - unknown (Tensor, optional): Unknown area map generated by trimap. - If specified, this tensor should have shape - (N, 1, ori_h, ori_w). - softmax_scale (float, optional): The softmax scale of the attention - if unknown area is not provided in forward. Default: 1. - - Returns: - Tensor: The augmented alpha feature. - """ - - if alpha_feat.shape[2:4] != img_feat.shape[2:4]: - raise ValueError( - 'image feature size does not align with alpha feature size: ' - f'image feature size {img_feat.shape[2:4]}, ' - f'alpha feature size {alpha_feat.shape[2:4]}') - - if unknown is not None and unknown.shape[2:4] != img_feat.shape[2:4]: - raise ValueError( - 'image feature size does not align with unknown mask size: ' - f'image feature size {img_feat.shape[2:4]}, ' - f'unknown mask size {unknown.shape[2:4]}') - - # preprocess image feature - img_feat = self.guidance_conv(img_feat) - img_feat = F.interpolate( - img_feat, scale_factor=1 / self.rate, mode=self.interpolation) - - # preprocess unknown mask - unknown, softmax_scale = self.process_unknown_mask( - unknown, img_feat, softmax_scale) - - img_ps, alpha_ps, unknown_ps = self.extract_feature_maps_patches( - img_feat, alpha_feat, unknown) - - # create self correlation mask with shape: - # (N, img_h*img_w, img_h, img_w) - self_mask = self.get_self_correlation_mask(img_feat) - - # split tensors by batch dimension; tuple is returned - img_groups = torch.split(img_feat, 1, dim=0) - img_ps_groups = torch.split(img_ps, 1, dim=0) - alpha_ps_groups = torch.split(alpha_ps, 1, dim=0) - unknown_ps_groups = torch.split(unknown_ps, 1, dim=0) - scale_groups = torch.split(softmax_scale, 1, dim=0) - groups = (img_groups, img_ps_groups, alpha_ps_groups, - unknown_ps_groups, scale_groups) - - out = [] - # i is the virtual index of the sample in the current batch - for img_i, img_ps_i, alpha_ps_i, unknown_ps_i, scale_i in zip(*groups): - similarity_map = self.compute_similarity_map(img_i, img_ps_i) - - gca_score = self.compute_guided_attention_score( - similarity_map, unknown_ps_i, scale_i, self_mask) - - out_i = self.propagate_alpha_feature(gca_score, alpha_ps_i) - - out.append(out_i) - - out = torch.cat(out, dim=0) - out.reshape_as(alpha_feat) - - out = self.out_conv(out) + alpha_feat - return out - - def extract_feature_maps_patches(self, img_feat, alpha_feat, unknown): - """Extract image feature, alpha feature unknown patches. - - Args: - img_feat (Tensor): Image feature map of shape - (N, img_c, img_h, img_w). - alpha_feat (Tensor): Alpha feature map of shape - (N, alpha_c, ori_h, ori_w). - unknown (Tensor, optional): Unknown area map generated by trimap of - shape (N, 1, img_h, img_w). - - Returns: - tuple: 3-tuple of - - ``Tensor``: Image feature patches of shape \ - (N, img_h*img_w, img_c, img_ks, img_ks). - - ``Tensor``: Guided contextual attention alpha feature map. \ - (N, img_h*img_w, alpha_c, alpha_ks, alpha_ks). - - ``Tensor``: Unknown mask of shape (N, img_h*img_w, 1, 1). - """ - # extract image feature patches with shape: - # (N, img_h*img_w, img_c, img_ks, img_ks) - img_ks = self.kernel_size - img_ps = self.extract_patches(img_feat, img_ks, self.stride) - - # extract alpha feature patches with shape: - # (N, img_h*img_w, alpha_c, alpha_ks, alpha_ks) - alpha_ps = self.extract_patches(alpha_feat, self.rate * 2, self.rate) - - # extract unknown mask patches with shape: (N, img_h*img_w, 1, 1) - unknown_ps = self.extract_patches(unknown, img_ks, self.stride) - unknown_ps = unknown_ps.squeeze(dim=2) # squeeze channel dimension - unknown_ps = unknown_ps.mean(dim=[2, 3], keepdim=True) - - return img_ps, alpha_ps, unknown_ps - - def compute_similarity_map(self, img_feat, img_ps): - """Compute similarity between image feature patches. - - Args: - img_feat (Tensor): Image feature map of shape - (1, img_c, img_h, img_w). - img_ps (Tensor): Image feature patches tensor of shape - (1, img_h*img_w, img_c, img_ks, img_ks). - - Returns: - Tensor: Similarity map between image feature patches with shape \ - (1, img_h*img_w, img_h, img_w). - """ - img_ps = img_ps[0] # squeeze dim 0 - # convolve the feature to get correlation (similarity) map - escape_NaN = torch.FloatTensor([self.eps]).to(img_feat) - img_ps_normed = img_ps / torch.max(self.l2_norm(img_ps), escape_NaN) - img_feat = self.pad(img_feat, self.kernel_size, self.stride) - similarity_map = F.conv2d(img_feat, img_ps_normed) - - return similarity_map - - def compute_guided_attention_score(self, similarity_map, unknown_ps, scale, - self_mask): - """Compute guided attention score. - - Args: - similarity_map (Tensor): Similarity map of image feature with shape - (1, img_h*img_w, img_h, img_w). - unknown_ps (Tensor): Unknown area patches tensor of shape - (1, img_h*img_w, 1, 1). - scale (Tensor): Softmax scale of known and unknown area: - [unknown_scale, known_scale]. - self_mask (Tensor): Self correlation mask of shape - (1, img_h*img_w, img_h, img_w). At (1, i*i, i, i) mask value - equals -1e4 for i in [1, img_h*img_w] and other area is all - zero. - - Returns: - Tensor: Similarity map between image feature patches with shape \ - (1, img_h*img_w, img_h, img_w). - """ - # scale the correlation with predicted scale factor for known and - # unknown area - unknown_scale, known_scale = scale[0] - out = similarity_map * ( - unknown_scale * unknown_ps.gt(0.).float() + - known_scale * unknown_ps.le(0.).float()) - # mask itself, self-mask only applied to unknown area - out = out + self_mask * unknown_ps - gca_score = F.softmax(out, dim=1) - - return gca_score - - def propagate_alpha_feature(self, gca_score, alpha_ps): - """Propagate alpha feature based on guided attention score. - - Args: - gca_score (Tensor): Guided attention score map of shape - (1, img_h*img_w, img_h, img_w). - alpha_ps (Tensor): Alpha feature patches tensor of shape - (1, img_h*img_w, alpha_c, alpha_ks, alpha_ks). - - Returns: - Tensor: Propagated alpha feature map of shape \ - (1, alpha_c, alpha_h, alpha_w). - """ - alpha_ps = alpha_ps[0] # squeeze dim 0 - if self.rate == 1: - gca_score = self.pad(gca_score, kernel_size=2, stride=1) - alpha_ps = alpha_ps.permute(1, 0, 2, 3) - out = F.conv2d(gca_score, alpha_ps) / 4. - else: - out = F.conv_transpose2d( - gca_score, alpha_ps, stride=self.rate, padding=1) / 4. - - return out - - def process_unknown_mask(self, unknown, img_feat, softmax_scale): - """Process unknown mask. - - Args: - unknown (Tensor, optional): Unknown area map generated by trimap of - shape (N, 1, ori_h, ori_w) - img_feat (Tensor): The interpolated image feature map of shape - (N, img_c, img_h, img_w). - softmax_scale (float, optional): The softmax scale of the attention - if unknown area is not provided in forward. Default: 1. - - Returns: - tuple: 2-tuple of - - ``Tensor``: Interpolated unknown area map of shape \ - (N, img_h*img_w, img_h, img_w). - - ``Tensor``: Softmax scale tensor of known and unknown area of \ - shape (N, 2). - """ - n, _, h, w = img_feat.shape - - if unknown is not None: - unknown = unknown.clone() - unknown = F.interpolate( - unknown, scale_factor=1 / self.rate, mode=self.interpolation) - unknown_mean = unknown.mean(dim=[2, 3]) - known_mean = 1 - unknown_mean - unknown_scale = torch.clamp( - torch.sqrt(unknown_mean / known_mean), 0.1, 10).to(img_feat) - known_scale = torch.clamp( - torch.sqrt(known_mean / unknown_mean), 0.1, 10).to(img_feat) - softmax_scale = torch.cat([unknown_scale, known_scale], dim=1) - else: - unknown = torch.ones((n, 1, h, w)).to(img_feat) - softmax_scale = torch.FloatTensor( - [softmax_scale, - softmax_scale]).view(1, 2).repeat(n, 1).to(img_feat) - - return unknown, softmax_scale - - def extract_patches(self, x, kernel_size, stride): - """Extract feature patches. - - The feature map will be padded automatically to make sure the number of - patches is equal to `(H / stride) * (W / stride)`. - - Args: - x (Tensor): Feature map of shape (N, C, H, W). - kernel_size (int): Size of each patches. - stride (int): Stride between patches. - - Returns: - Tensor: Extracted patches of shape \ - (N, (H / stride) * (W / stride) , C, kernel_size, kernel_size). - """ - n, c, _, _ = x.shape - x = self.pad(x, kernel_size, stride) - x = F.unfold(x, (kernel_size, kernel_size), stride=(stride, stride)) - x = x.permute(0, 2, 1) - x = x.reshape(n, -1, c, kernel_size, kernel_size) - return x - - def pad(self, x, kernel_size, stride): - left = (kernel_size - stride + 1) // 2 - right = (kernel_size - stride) // 2 - pad = (left, right, left, right) - return F.pad(x, pad, **self.pad_args) - - def get_self_correlation_mask(self, img_feat): - _, _, h, w = img_feat.shape - # As ONNX does not support dynamic num_classes, we have to convert it - # into an integer - self_mask = F.one_hot( - torch.arange(h * w).view(h, w), num_classes=int(h * w)) - self_mask = self_mask.permute(2, 0, 1).view(1, h * w, h, w) - # use large negative value to mask out self-correlation before softmax - self_mask = self_mask * self.penalty - return self_mask.to(img_feat) - - @staticmethod - def l2_norm(x): - x = x**2 - x = x.sum(dim=[1, 2, 3], keepdim=True) - return torch.sqrt(x) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/generation_model_utils.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/generation_model_utils.py deleted file mode 100755 index 21d45fbe3..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/generation_model_utils.py +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, kaiming_init, normal_init, xavier_init -from torch.nn import init - - -def generation_init_weights(module, init_type='normal', init_gain=0.02): - """Default initialization of network weights for image generation. - - By default, we use normal init, but xavier and kaiming might work - better for some applications. - - Args: - module (nn.Module): Module to be initialized. - init_type (str): The name of an initialization method: - normal | xavier | kaiming | orthogonal. - init_gain (float): Scaling factor for normal, xavier and - orthogonal. - """ - - def init_func(m): - """Initialization function. - - Args: - m (nn.Module): Module to be initialized. - """ - classname = m.__class__.__name__ - if hasattr(m, 'weight') and (classname.find('Conv') != -1 - or classname.find('Linear') != -1): - if init_type == 'normal': - normal_init(m, 0.0, init_gain) - elif init_type == 'xavier': - xavier_init(m, gain=init_gain, distribution='normal') - elif init_type == 'kaiming': - kaiming_init( - m, - a=0, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='normal') - elif init_type == 'orthogonal': - init.orthogonal_(m.weight, gain=init_gain) - init.constant_(m.bias.data, 0.0) - else: - raise NotImplementedError( - f"Initialization method '{init_type}' is not implemented") - elif classname.find('BatchNorm2d') != -1: - # BatchNorm Layer's weight is not a matrix; - # only normal distribution applies. - normal_init(m, 1.0, init_gain) - - module.apply(init_func) - - -class GANImageBuffer: - """This class implements an image buffer that stores previously - generated images. - - This buffer allows us to update the discriminator using a history of - generated images rather than the ones produced by the latest generator - to reduce model oscillation. - - Args: - buffer_size (int): The size of image buffer. If buffer_size = 0, - no buffer will be created. - buffer_ratio (float): The chance / possibility to use the images - previously stored in the buffer. - """ - - def __init__(self, buffer_size, buffer_ratio=0.5): - self.buffer_size = buffer_size - # create an empty buffer - if self.buffer_size > 0: - self.img_num = 0 - self.image_buffer = [] - self.buffer_ratio = buffer_ratio - - def query(self, images): - """Query current image batch using a history of generated images. - - Args: - images (Tensor): Current image batch without history information. - """ - if self.buffer_size == 0: # if the buffer size is 0, do nothing - return images - return_images = [] - for image in images: - image = torch.unsqueeze(image.data, 0) - # if the buffer is not full, keep inserting current images - if self.img_num < self.buffer_size: - self.img_num = self.img_num + 1 - self.image_buffer.append(image) - return_images.append(image) - else: - use_buffer = np.random.random() < self.buffer_ratio - # by self.buffer_ratio, the buffer will return a previously - # stored image, and insert the current image into the buffer - if use_buffer: - random_id = np.random.randint(0, self.buffer_size) - image_tmp = self.image_buffer[random_id].clone() - self.image_buffer[random_id] = image - return_images.append(image_tmp) - # by (1 - self.buffer_ratio), the buffer will return the - # current image - else: - return_images.append(image) - # collect all the images and return - return_images = torch.cat(return_images, 0) - return return_images - - -class UnetSkipConnectionBlock(nn.Module): - """Construct a Unet submodule with skip connections, with the following - structure: downsampling - `submodule` - upsampling. - - Args: - outer_channels (int): Number of channels at the outer conv layer. - inner_channels (int): Number of channels at the inner conv layer. - in_channels (int): Number of channels in input images/features. If is - None, equals to `outer_channels`. Default: None. - submodule (UnetSkipConnectionBlock): Previously constructed submodule. - Default: None. - is_outermost (bool): Whether this module is the outermost module. - Default: False. - is_innermost (bool): Whether this module is the innermost module. - Default: False. - norm_cfg (dict): Config dict to build norm layer. Default: - `dict(type='BN')`. - use_dropout (bool): Whether to use dropout layers. Default: False. - """ - - def __init__(self, - outer_channels, - inner_channels, - in_channels=None, - submodule=None, - is_outermost=False, - is_innermost=False, - norm_cfg=dict(type='BN'), - use_dropout=False): - super().__init__() - # cannot be both outermost and innermost - assert not (is_outermost and is_innermost), ( - "'is_outermost' and 'is_innermost' cannot be True" - 'at the same time.') - self.is_outermost = is_outermost - assert isinstance(norm_cfg, dict), ("'norm_cfg' should be dict, but" - f'got {type(norm_cfg)}') - assert 'type' in norm_cfg, "'norm_cfg' must have key 'type'" - # We use norm layers in the unet skip connection block. - # Only for IN, use bias since it does not have affine parameters. - use_bias = norm_cfg['type'] == 'IN' - - kernel_size = 4 - stride = 2 - padding = 1 - if in_channels is None: - in_channels = outer_channels - down_conv_cfg = dict(type='Conv2d') - down_norm_cfg = norm_cfg - down_act_cfg = dict(type='LeakyReLU', negative_slope=0.2) - up_conv_cfg = dict(type='Deconv') - up_norm_cfg = norm_cfg - up_act_cfg = dict(type='ReLU') - up_in_channels = inner_channels * 2 - up_bias = use_bias - middle = [submodule] - upper = [] - - if is_outermost: - down_act_cfg = None - down_norm_cfg = None - up_bias = True - up_norm_cfg = None - upper = [nn.Tanh()] - elif is_innermost: - down_norm_cfg = None - up_in_channels = inner_channels - middle = [] - else: - upper = [nn.Dropout(0.5)] if use_dropout else [] - - down = [ - ConvModule( - in_channels=in_channels, - out_channels=inner_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - bias=use_bias, - conv_cfg=down_conv_cfg, - norm_cfg=down_norm_cfg, - act_cfg=down_act_cfg, - order=('act', 'conv', 'norm')) - ] - up = [ - ConvModule( - in_channels=up_in_channels, - out_channels=outer_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - bias=up_bias, - conv_cfg=up_conv_cfg, - norm_cfg=up_norm_cfg, - act_cfg=up_act_cfg, - order=('act', 'conv', 'norm')) - ] - - model = down + middle + up + upper - - self.model = nn.Sequential(*model) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - if self.is_outermost: - return self.model(x) - - # add skip connections - return torch.cat([x, self.model(x)], 1) - - -class ResidualBlockWithDropout(nn.Module): - """Define a Residual Block with dropout layers. - - Ref: - Deep Residual Learning for Image Recognition - - A residual block is a conv block with skip connections. A dropout layer is - added between two common conv modules. - - Args: - channels (int): Number of channels in the conv layer. - padding_mode (str): The name of padding layer: - 'reflect' | 'replicate' | 'zeros'. - norm_cfg (dict): Config dict to build norm layer. Default: - `dict(type='IN')`. - use_dropout (bool): Whether to use dropout layers. Default: True. - """ - - def __init__(self, - channels, - padding_mode, - norm_cfg=dict(type='BN'), - use_dropout=True): - super().__init__() - assert isinstance(norm_cfg, dict), ("'norm_cfg' should be dict, but" - f'got {type(norm_cfg)}') - assert 'type' in norm_cfg, "'norm_cfg' must have key 'type'" - # We use norm layers in the residual block with dropout layers. - # Only for IN, use bias since it does not have affine parameters. - use_bias = norm_cfg['type'] == 'IN' - - block = [ - ConvModule( - in_channels=channels, - out_channels=channels, - kernel_size=3, - padding=1, - bias=use_bias, - norm_cfg=norm_cfg, - padding_mode=padding_mode) - ] - - if use_dropout: - block += [nn.Dropout(0.5)] - - block += [ - ConvModule( - in_channels=channels, - out_channels=channels, - kernel_size=3, - padding=1, - bias=use_bias, - norm_cfg=norm_cfg, - act_cfg=None, - padding_mode=padding_mode) - ] - - self.block = nn.Sequential(*block) - - def forward(self, x): - """Forward function. Add skip connections without final ReLU. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - out = x + self.block(x) - return out diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/img_normalize.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/img_normalize.py deleted file mode 100755 index 1bd8f76aa..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/img_normalize.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class ImgNormalize(nn.Conv2d): - """Normalize images with the given mean and std value. - - Based on Conv2d layer, can work in GPU. - - Args: - pixel_range (float): Pixel range of feature. - img_mean (Tuple[float]): Image mean of each channel. - img_std (Tuple[float]): Image std of each channel. - sign (int): Sign of bias. Default -1. - """ - - def __init__(self, pixel_range, img_mean, img_std, sign=-1): - - assert len(img_mean) == len(img_std) - num_channels = len(img_mean) - super().__init__(num_channels, num_channels, kernel_size=1) - - std = torch.Tensor(img_std) - self.weight.data = torch.eye(num_channels).view( - num_channels, num_channels, 1, 1) - self.weight.data.div_(std.view(num_channels, 1, 1, 1)) - self.bias.data = sign * pixel_range * torch.Tensor(img_mean) - self.bias.data.div_(std) - - self.weight.requires_grad = False - self.bias.requires_grad = False diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/linear_module.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/linear_module.py deleted file mode 100755 index 5101ad164..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/linear_module.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import build_activation_layer, kaiming_init - - -class LinearModule(nn.Module): - """A linear block that contains linear/norm/activation layers. - - For low level vision, we add spectral norm and padding layer. - - Args: - in_features (int): Same as nn.Linear. - out_features (int): Same as nn.Linear. - bias (bool): Same as nn.Linear. - act_cfg (dict): Config dict for activation layer, "relu" by default. - inplace (bool): Whether to use inplace mode for activation. - with_spectral_norm (bool): Whether use spectral norm in linear module. - order (tuple[str]): The order of linear/activation layers. It is a - sequence of "linear", "norm" and "act". Examples are - ("linear", "act") and ("act", "linear"). - """ - - def __init__(self, - in_features, - out_features, - bias=True, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - order=('linear', 'act')): - super().__init__() - assert act_cfg is None or isinstance(act_cfg, dict) - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 2 - assert set(order) == set(['linear', 'act']) - - self.with_activation = act_cfg is not None - self.with_bias = bias - - # build linear layer - self.linear = nn.Linear(in_features, out_features, bias=bias) - # export the attributes of self.linear to a higher level for - # convenience - self.in_features = self.linear.in_features - self.out_features = self.linear.out_features - - if self.with_spectral_norm: - self.linear = nn.utils.spectral_norm(self.linear) - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - def init_weights(self): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - - kaiming_init(self.linear, a=a, nonlinearity=nonlinearity) - - def forward(self, x, activate=True): - """Forward Function. - - Args: - x (torch.Tensor): Input tensor with shape of :math:`(n, *, c)`. - Same as ``torch.nn.Linear``. - activate (bool, optional): Whether to use activation layer. - Defaults to True. - - Returns: - torch.Tensor: Same as ``torch.nn.Linear``. - """ - for layer in self.order: - if layer == 'linear': - x = self.linear(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/mask_conv_module.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/mask_conv_module.py deleted file mode 100755 index e70868686..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/mask_conv_module.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import ConvModule - - -class MaskConvModule(ConvModule): - """Mask convolution module. - - This is a simple wrapper for mask convolution like: 'partial conv'. - Convolutions in this module always need a mask as extra input. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - stride (int or tuple[int]): Same as nn.Conv2d. - padding (int or tuple[int]): Same as nn.Conv2d. - dilation (int or tuple[int]): Same as nn.Conv2d. - groups (int): Same as nn.Conv2d. - bias (bool or str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if norm_cfg is None, otherwise - False. - conv_cfg (dict): Config dict for convolution layer. - norm_cfg (dict): Config dict for normalization layer. - act_cfg (dict): Config dict for activation layer, "relu" by default. - inplace (bool): Whether to use inplace mode for activation. - with_spectral_norm (bool): Whether use spectral norm in conv module. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in Pytorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - """ - supported_conv_list = ['PConv'] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.conv_cfg['type'] in self.supported_conv_list - - self.init_weights() - - def forward(self, - x, - mask=None, - activate=True, - norm=True, - return_mask=True): - """Forward function for partial conv2d. - - Args: - input (torch.Tensor): Tensor with shape of (n, c, h, w). - mask (torch.Tensor): Tensor with shape of (n, c, h, w) or - (n, 1, h, w). If mask is not given, the function will - work as standard conv2d. Default: None. - activate (bool): Whether use activation layer. - norm (bool): Whether use norm layer. - return_mask (bool): If True and mask is not None, the updated - mask will be returned. Default: True. - - Returns: - Tensor or tuple: Result Tensor or 2-tuple of - - ``Tensor``: Results after partial conv. - - ``Tensor``: Updated mask will be returned if mask is given \ - and `return_mask` is True. - """ - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - mask = self.padding_layer(mask) - if return_mask: - x, updated_mask = self.conv( - x, mask, return_mask=return_mask) - else: - x = self.conv(x, mask, return_mask=False) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - - if return_mask: - return x, updated_mask - - return x diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/model_utils.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/model_utils.py deleted file mode 100755 index 62135c6e6..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/model_utils.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch - - -def set_requires_grad(nets, requires_grad=False): - """Set requires_grad for all the networks. - - Args: - nets (nn.Module | list[nn.Module]): A list of networks or a single - network. - requires_grad (bool): Whether the networks require gradients or not - """ - if not isinstance(nets, list): - nets = [nets] - for net in nets: - if net is not None: - for param in net.parameters(): - param.requires_grad = requires_grad - - -def extract_bbox_patch(bbox, img, channel_first=True): - """Extract patch from a given bbox - - Args: - bbox (torch.Tensor | numpy.array): Bbox with (top, left, h, w). If - `img` has batch dimension, the `bbox` must be stacked at first - dimension. The shape should be (4,) or (n, 4). - img (torch.Tensor | numpy.array): Image data to be extracted. If - organized in batch dimension, the batch dimension must be the first - order like (n, h, w, c) or (n, c, h, w). - channel_first (bool): If True, the channel dimension of img is before - height and width, e.g. (c, h, w). Otherwise, the img shape (samples - in the batch) is like (h, w, c). - - Returns: - (torch.Tensor | numpy.array): Extracted patches. The dimension of the \ - output should be the same as `img`. - """ - - def _extract(bbox, img): - assert len(bbox) == 4 - t, l, h, w = bbox - if channel_first: - img_patch = img[..., t:t + h, l:l + w] - else: - img_patch = img[t:t + h, l:l + w, ...] - - return img_patch - - input_size = img.shape - assert len(input_size) == 3 or len(input_size) == 4 - bbox_size = bbox.shape - assert bbox_size == (4, ) or (len(bbox_size) == 2 - and bbox_size[0] == input_size[0]) - - # images with batch dimension - if len(input_size) == 4: - output_list = [] - for i in range(input_size[0]): - img_patch_ = _extract(bbox[i], img[i:i + 1, ...]) - output_list.append(img_patch_) - if isinstance(img, torch.Tensor): - img_patch = torch.cat(output_list, dim=0) - else: - img_patch = np.concatenate(output_list, axis=0) - # standardize image - else: - img_patch = _extract(bbox, img) - - return img_patch - - -def scale_bbox(bbox, target_size): - """Modify bbox to target size. - - The original bbox will be enlarged to the target size with the original - bbox in the center of the new bbox. - - Args: - bbox (np.ndarray | torch.Tensor): Bboxes to be modified. Bbox can - be in batch or not. The shape should be (4,) or (n, 4). - target_size (tuple[int]): Target size of final bbox. - - Returns: - (np.ndarray | torch.Tensor): Modified bboxes. - """ - - def _mod(bbox, target_size): - top_ori, left_ori, h_ori, w_ori = bbox - h, w = target_size - assert h >= h_ori and w >= w_ori - top = int(max(0, top_ori - (h - h_ori) // 2)) - left = int(max(0, left_ori - (w - w_ori) // 2)) - - if isinstance(bbox, torch.Tensor): - bbox_new = torch.Tensor([top, left, h, w]).type_as(bbox) - else: - bbox_new = np.asarray([top, left, h, w]) - - return bbox_new - - if isinstance(bbox, torch.Tensor): - bbox_new = torch.zeros_like(bbox) - elif isinstance(bbox, np.ndarray): - bbox_new = np.zeros_like(bbox) - else: - raise TypeError('bbox mush be torch.Tensor or numpy.ndarray' - f'but got type {type(bbox)}') - bbox_shape = list(bbox.shape) - - if len(bbox_shape) == 2: - for i in range(bbox_shape[0]): - bbox_new[i, :] = _mod(bbox[i], target_size) - else: - bbox_new = _mod(bbox, target_size) - - return bbox_new - - -def extract_around_bbox(img, bbox, target_size, channel_first=True): - """Extract patches around the given bbox. - - Args: - bbox (np.ndarray | torch.Tensor): Bboxes to be modified. Bbox can - be in batch or not. - target_size (List(int)): Target size of final bbox. - - Returns: - (torch.Tensor | numpy.array): Extracted patches. The dimension of the \ - output should be the same as `img`. - """ - bbox_new = scale_bbox(bbox, target_size) - img_patch = extract_bbox_patch(bbox_new, img, channel_first=channel_first) - - return img_patch, bbox_new diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/partial_conv.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/partial_conv.py deleted file mode 100755 index 51b50feca..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/partial_conv.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import CONV_LAYERS - - -@CONV_LAYERS.register_module(name='PConv') -class PartialConv2d(nn.Conv2d): - """Implementation for partial convolution. - - Image Inpainting for Irregular Holes Using Partial Convolutions - [https://arxiv.org/abs/1804.07723] - - Args: - multi_channel (bool): If True, the mask is multi-channel. Otherwise, - the mask is single-channel. - eps (float): Need to be changed for mixed precision training. - For mixed precision training, you need change 1e-8 to 1e-6. - """ - - def __init__(self, *args, multi_channel=False, eps=1e-8, **kwargs): - super().__init__(*args, **kwargs) - - # whether the mask is multi-channel or not - self.multi_channel = multi_channel - self.eps = eps - - if self.multi_channel: - out_channels, in_channels = self.out_channels, self.in_channels - else: - out_channels, in_channels = 1, 1 - - self.register_buffer( - 'weight_mask_updater', - torch.ones(out_channels, in_channels, self.kernel_size[0], - self.kernel_size[1])) - - self.mask_kernel_numel = np.prod(self.weight_mask_updater.shape[1:4]) - self.mask_kernel_numel = (self.mask_kernel_numel).item() - - def forward(self, input, mask=None, return_mask=True): - """Forward function for partial conv2d. - - Args: - input (torch.Tensor): Tensor with shape of (n, c, h, w). - mask (torch.Tensor): Tensor with shape of (n, c, h, w) or - (n, 1, h, w). If mask is not given, the function will - work as standard conv2d. Default: None. - return_mask (bool): If True and mask is not None, the updated - mask will be returned. Default: True. - - Returns: - torch.Tensor : Results after partial conv.\ - torch.Tensor : Updated mask will be returned if mask is given and \ - ``return_mask`` is True. - """ - assert input.dim() == 4 - if mask is not None: - assert mask.dim() == 4 - if self.multi_channel: - assert mask.shape[1] == input.shape[1] - else: - assert mask.shape[1] == 1 - - # update mask and compute mask ratio - if mask is not None: - with torch.no_grad(): - - updated_mask = F.conv2d( - mask, - self.weight_mask_updater, - bias=None, - stride=self.stride, - padding=self.padding, - dilation=self.dilation) - mask_ratio = self.mask_kernel_numel / (updated_mask + self.eps) - - updated_mask = torch.clamp(updated_mask, 0, 1) - mask_ratio = mask_ratio * updated_mask - - # standard conv2d - if mask is not None: - input = input * mask - raw_out = super().forward(input) - - if mask is not None: - if self.bias is None: - output = raw_out * mask_ratio - else: - # compute new bias when mask is given - bias_view = self.bias.view(1, self.out_channels, 1, 1) - output = (raw_out - bias_view) * mask_ratio + bias_view - output = output * updated_mask - else: - output = raw_out - - if return_mask and mask is not None: - return output, updated_mask - - return output diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/separable_conv_module.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/separable_conv_module.py deleted file mode 100755 index 139a54e58..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/separable_conv_module.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if ``norm_cfg`` and ``act_cfg`` are specified. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - stride (int or tuple[int]): Same as nn.Conv2d. Default: 1. - padding (int or tuple[int]): Same as nn.Conv2d. Default: 0. - dilation (int or tuple[int]): Same as nn.Conv2d. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as ``norm_cfg``. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as ``act_cfg``. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as ``act_cfg``. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super().__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (N, C, H, W). - - Returns: - Tensor: Output tensor. - """ - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/sr_backbone_utils.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/sr_backbone_utils.py deleted file mode 100755 index b4b0aad91..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/sr_backbone_utils.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import constant_init, kaiming_init -from mmcv.utils.parrots_wrapper import _BatchNorm - - -def default_init_weights(module, scale=1): - """Initialize network weights. - - Args: - modules (nn.Module): Modules to be initialized. - scale (float): Scale initialized weights, especially for residual - blocks. - """ - for m in module.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m, a=0, mode='fan_in', bias=0) - m.weight.data *= scale - elif isinstance(m, nn.Linear): - kaiming_init(m, a=0, mode='fan_in', bias=0) - m.weight.data *= scale - elif isinstance(m, _BatchNorm): - constant_init(m.weight, val=1, bias=0) - - -def make_layer(block, num_blocks, **kwarg): - """Make layers by stacking the same blocks. - - Args: - block (nn.module): nn.module class for basic block. - num_blocks (int): number of blocks. - - Returns: - nn.Sequential: Stacked blocks in nn.Sequential. - """ - layers = [] - for _ in range(num_blocks): - layers.append(block(**kwarg)) - return nn.Sequential(*layers) - - -class ResidualBlockNoBN(nn.Module): - """Residual block without BN. - - It has a style of: - - :: - - ---Conv-ReLU-Conv-+- - |________________| - - Args: - mid_channels (int): Channel number of intermediate features. - Default: 64. - res_scale (float): Used to scale the residual before addition. - Default: 1.0. - """ - - def __init__(self, mid_channels=64, res_scale=1.0): - super().__init__() - self.res_scale = res_scale - self.conv1 = nn.Conv2d(mid_channels, mid_channels, 3, 1, 1, bias=True) - self.conv2 = nn.Conv2d(mid_channels, mid_channels, 3, 1, 1, bias=True) - - self.relu = nn.ReLU(inplace=True) - - # if res_scale < 1.0, use the default initialization, as in EDSR. - # if res_scale = 1.0, use scaled kaiming_init, as in MSRResNet. - if res_scale == 1.0: - self.init_weights() - - def init_weights(self): - """Initialize weights for ResidualBlockNoBN. - - Initialization methods like `kaiming_init` are for VGG-style - modules. For modules with residual paths, using smaller std is - better for stability and performance. We empirically use 0.1. - See more details in "ESRGAN: Enhanced Super-Resolution Generative - Adversarial Networks" - """ - - for m in [self.conv1, self.conv2]: - default_init_weights(m, 0.1) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - identity = x - out = self.conv2(self.relu(self.conv1(x))) - return identity + out * self.res_scale diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/upsample.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/upsample.py deleted file mode 100755 index f39ec1a9e..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/common/upsample.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from .sr_backbone_utils import default_init_weights - - -class PixelShufflePack(nn.Module): - """ Pixel Shuffle upsample layer. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of Conv layer to expand channels. - - Returns: - Upsampled feature map. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super().__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - """Initialize weights for PixelShufflePack. - """ - default_init_weights(self, 1) - - def forward(self, x): - """Forward function for PixelShufflePack. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/losses/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/losses/__init__.py deleted file mode 100755 index 67fe30665..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/losses/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .pixelwise_loss import CharbonnierLoss, L1Loss, MaskedTVLoss, MSELoss -from .utils import mask_reduce_loss, reduce_loss - diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/losses/pixelwise_loss.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/losses/pixelwise_loss.py deleted file mode 100755 index 2f2435b8d..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/losses/pixelwise_loss.py +++ /dev/null @@ -1,221 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..registry import LOSSES -from .utils import masked_loss - -_reduction_modes = ['none', 'mean', 'sum'] - - -@masked_loss -def l1_loss(pred, target): - """L1 loss. - - Args: - pred (Tensor): Prediction Tensor with shape (n, c, h, w). - target ([type]): Target Tensor with shape (n, c, h, w). - - Returns: - Tensor: Calculated L1 loss. - """ - return F.l1_loss(pred, target, reduction='none') - - -@masked_loss -def mse_loss(pred, target): - """MSE loss. - - Args: - pred (Tensor): Prediction Tensor with shape (n, c, h, w). - target ([type]): Target Tensor with shape (n, c, h, w). - - Returns: - Tensor: Calculated MSE loss. - """ - return F.mse_loss(pred, target, reduction='none') - - -@masked_loss -def charbonnier_loss(pred, target, eps=1e-12): - """Charbonnier loss. - - Args: - pred (Tensor): Prediction Tensor with shape (n, c, h, w). - target ([type]): Target Tensor with shape (n, c, h, w). - - Returns: - Tensor: Calculated Charbonnier loss. - """ - return torch.sqrt((pred - target)**2 + eps) - - -@LOSSES.register_module() -class L1Loss(nn.Module): - """L1 (mean absolute error, MAE) loss. - - Args: - loss_weight (float): Loss weight for L1 loss. Default: 1.0. - reduction (str): Specifies the reduction to apply to the output. - Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduce loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - """ - - def __init__(self, loss_weight=1.0, reduction='mean', sample_wise=False): - super().__init__() - if reduction not in ['none', 'mean', 'sum']: - raise ValueError(f'Unsupported reduction mode: {reduction}. ' - f'Supported ones are: {_reduction_modes}') - - self.loss_weight = loss_weight - self.reduction = reduction - self.sample_wise = sample_wise - - def forward(self, pred, target, weight=None, **kwargs): - """Forward Function. - - Args: - pred (Tensor): of shape (N, C, H, W). Predicted tensor. - target (Tensor): of shape (N, C, H, W). Ground truth tensor. - weight (Tensor, optional): of shape (N, C, H, W). Element-wise - weights. Default: None. - """ - return self.loss_weight * l1_loss( - pred, - target, - weight, - reduction=self.reduction, - sample_wise=self.sample_wise) - - -@LOSSES.register_module() -class MSELoss(nn.Module): - """MSE (L2) loss. - - Args: - loss_weight (float): Loss weight for MSE loss. Default: 1.0. - reduction (str): Specifies the reduction to apply to the output. - Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduces loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - """ - - def __init__(self, loss_weight=1.0, reduction='mean', sample_wise=False): - super().__init__() - if reduction not in ['none', 'mean', 'sum']: - raise ValueError(f'Unsupported reduction mode: {reduction}. ' - f'Supported ones are: {_reduction_modes}') - - self.loss_weight = loss_weight - self.reduction = reduction - self.sample_wise = sample_wise - - def forward(self, pred, target, weight=None, **kwargs): - """Forward Function. - - Args: - pred (Tensor): of shape (N, C, H, W). Predicted tensor. - target (Tensor): of shape (N, C, H, W). Ground truth tensor. - weight (Tensor, optional): of shape (N, C, H, W). Element-wise - weights. Default: None. - """ - return self.loss_weight * mse_loss( - pred, - target, - weight, - reduction=self.reduction, - sample_wise=self.sample_wise) - - -@LOSSES.register_module() -class CharbonnierLoss(nn.Module): - """Charbonnier loss (one variant of Robust L1Loss, a differentiable - variant of L1Loss). - - Described in "Deep Laplacian Pyramid Networks for Fast and Accurate - Super-Resolution". - - Args: - loss_weight (float): Loss weight for L1 loss. Default: 1.0. - reduction (str): Specifies the reduction to apply to the output. - Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduces loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - eps (float): A value used to control the curvature near zero. - Default: 1e-12. - """ - - def __init__(self, - loss_weight=1.0, - reduction='mean', - sample_wise=False, - eps=1e-12): - super().__init__() - if reduction not in ['none', 'mean', 'sum']: - raise ValueError(f'Unsupported reduction mode: {reduction}. ' - f'Supported ones are: {_reduction_modes}') - - self.loss_weight = loss_weight - self.reduction = reduction - self.sample_wise = sample_wise - self.eps = eps - - def forward(self, pred, target, weight=None, **kwargs): - """Forward Function. - - Args: - pred (Tensor): of shape (N, C, H, W). Predicted tensor. - target (Tensor): of shape (N, C, H, W). Ground truth tensor. - weight (Tensor, optional): of shape (N, C, H, W). Element-wise - weights. Default: None. - """ - return self.loss_weight * charbonnier_loss( - pred, - target, - weight, - eps=self.eps, - reduction=self.reduction, - sample_wise=self.sample_wise) - - -@LOSSES.register_module() -class MaskedTVLoss(L1Loss): - """Masked TV loss. - - Args: - loss_weight (float, optional): Loss weight. Defaults to 1.0. - """ - - def __init__(self, loss_weight=1.0): - super().__init__(loss_weight=loss_weight) - - def forward(self, pred, mask=None): - """Forward function. - - Args: - pred (torch.Tensor): Tensor with shape of (n, c, h, w). - mask (torch.Tensor, optional): Tensor with shape of (n, 1, h, w). - Defaults to None. - - Returns: - [type]: [description] - """ - y_diff = super().forward( - pred[:, :, :-1, :], pred[:, :, 1:, :], weight=mask[:, :, :-1, :]) - x_diff = super().forward( - pred[:, :, :, :-1], pred[:, :, :, 1:], weight=mask[:, :, :, :-1]) - - loss = x_diff + y_diff - - return loss diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/losses/utils.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/losses/utils.py deleted file mode 100755 index 2f536d924..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/losses/utils.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch.nn.functional as F - - -def reduce_loss(loss, reduction): - """Reduce loss as specified. - - Args: - loss (Tensor): Elementwise loss tensor. - reduction (str): Options are "none", "mean" and "sum". - - Returns: - Tensor: Reduced loss tensor. - """ - reduction_enum = F._Reduction.get_enum(reduction) - # none: 0, elementwise_mean:1, sum: 2 - if reduction_enum == 0: - return loss - if reduction_enum == 1: - return loss.mean() - - return loss.sum() - - -def mask_reduce_loss(loss, weight=None, reduction='mean', sample_wise=False): - """Apply element-wise weight and reduce loss. - - Args: - loss (Tensor): Element-wise loss. - weight (Tensor): Element-wise weights. Default: None. - reduction (str): Same as built-in losses of PyTorch. Options are - "none", "mean" and "sum". Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduces loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - - Returns: - Tensor: Processed loss values. - """ - # if weight is specified, apply element-wise weight - if weight is not None: - assert weight.dim() == loss.dim() - assert weight.size(1) == 1 or weight.size(1) == loss.size(1) - loss = loss * weight - - # if weight is not specified or reduction is sum, just reduce the loss - if weight is None or reduction == 'sum': - loss = reduce_loss(loss, reduction) - # if reduction is mean, then compute mean over masked region - elif reduction == 'mean': - # expand weight from N1HW to NCHW - if weight.size(1) == 1: - weight = weight.expand_as(loss) - # small value to prevent division by zero - eps = 1e-12 - - # perform sample-wise mean - if sample_wise: - weight = weight.sum(dim=[1, 2, 3], keepdim=True) # NCHW to N111 - loss = (loss / (weight + eps)).sum() / weight.size(0) - # perform pixel-wise mean - else: - loss = loss.sum() / (weight.sum() + eps) - - return loss - - -def masked_loss(loss_func): - """Create a masked version of a given loss function. - - To use this decorator, the loss function must have the signature like - `loss_func(pred, target, **kwargs)`. The function only needs to compute - element-wise loss without any reduction. This decorator will add weight - and reduction arguments to the function. The decorated function will have - the signature like `loss_func(pred, target, weight=None, reduction='mean', - avg_factor=None, **kwargs)`. - - :Example: - - >>> import torch - >>> @masked_loss - >>> def l1_loss(pred, target): - >>> return (pred - target).abs() - - >>> pred = torch.Tensor([0, 2, 3]) - >>> target = torch.Tensor([1, 1, 1]) - >>> weight = torch.Tensor([1, 0, 1]) - - >>> l1_loss(pred, target) - tensor(1.3333) - >>> l1_loss(pred, target, weight) - tensor(1.5000) - >>> l1_loss(pred, target, reduction='none') - tensor([1., 1., 2.]) - >>> l1_loss(pred, target, weight, reduction='sum') - tensor(3.) - """ - - @functools.wraps(loss_func) - def wrapper(pred, - target, - weight=None, - reduction='mean', - sample_wise=False, - **kwargs): - # get element-wise loss - loss = loss_func(pred, target, **kwargs) - loss = mask_reduce_loss(loss, weight, reduction, sample_wise) - return loss - - return wrapper diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/registry.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/registry.py deleted file mode 100755 index 0a574b667..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import MODELS as MMCV_MODELS -from mmcv.utils import Registry - -MODELS = Registry('model', parent=MMCV_MODELS) -BACKBONES = MODELS -COMPONENTS = MODELS -LOSSES = MODELS diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/restorers/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/restorers/__init__.py deleted file mode 100755 index a39a17a6c..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/restorers/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .basic_restorer import BasicRestorer -from .basicvsr import BasicVSR diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/restorers/basic_restorer.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/restorers/basic_restorer.py deleted file mode 100755 index 5114b8e9a..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/restorers/basic_restorer.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -import os.path as osp - -import mmcv -from mmcv.runner import auto_fp16 - -from mmedit.core import psnr, ssim, tensor2img -from ..base import BaseModel -from ..builder import build_backbone, build_loss -from ..registry import MODELS - - -@MODELS.register_module() -class BasicRestorer(BaseModel): - """Basic model for image restoration. - - It must contain a generator that takes an image as inputs and outputs a - restored image. It also has a pixel-wise loss for training. - - The subclasses should overwrite the function `forward_train`, - `forward_test` and `train_step`. - - Args: - generator (dict): Config for the generator structure. - pixel_loss (dict): Config for pixel-wise loss. - train_cfg (dict): Config for training. Default: None. - test_cfg (dict): Config for testing. Default: None. - pretrained (str): Path for pretrained model. Default: None. - """ - allowed_metrics = {'PSNR': psnr, 'SSIM': ssim} - - def __init__(self, - generator, - pixel_loss, - train_cfg=None, - test_cfg=None, - pretrained=None): - super().__init__() - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - # support fp16 - self.fp16_enabled = False - - # generator - self.generator = build_backbone(generator) - self.init_weights(pretrained) - - # loss - self.pixel_loss = build_loss(pixel_loss) - - def init_weights(self, pretrained=None): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - """ - self.generator.init_weights(pretrained) - - @auto_fp16(apply_to=('lq', )) - def forward(self, lq, gt=None, test_mode=False, **kwargs): - """Forward function. - - Args: - lq (Tensor): Input lq images. - gt (Tensor): Ground-truth image. Default: None. - test_mode (bool): Whether in test mode or not. Default: False. - kwargs (dict): Other arguments. - """ - - if test_mode: - return self.forward_test(lq, gt, **kwargs) - - return self.forward_train(lq, gt) - - def forward_train(self, lq, gt): - """Training forward function. - - Args: - lq (Tensor): LQ Tensor with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). - - Returns: - Tensor: Output tensor. - """ - losses = dict() - output = self.generator(lq) - loss_pix = self.pixel_loss(output, gt) - losses['loss_pix'] = loss_pix - outputs = dict( - losses=losses, - num_samples=len(gt.data), - results=dict(lq=lq.cpu(), gt=gt.cpu(), output=output.cpu())) - return outputs - - def evaluate(self, output, gt): - """Evaluation function. - - Args: - output (Tensor): Model output with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). - - Returns: - dict: Evaluation results. - """ - crop_border = self.test_cfg.crop_border - - output = tensor2img(output) - gt = tensor2img(gt) - - eval_result = dict() - for metric in self.test_cfg.metrics: - eval_result[metric] = self.allowed_metrics[metric](output, gt, - crop_border) - return eval_result - - def forward_test(self, - lq, - gt=None, - meta=None, - save_image=False, - save_path=None, - iteration=None): - """Testing forward function. - - Args: - lq (Tensor): LQ Tensor with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). Default: None. - save_image (bool): Whether to save image. Default: False. - save_path (str): Path to save image. Default: None. - iteration (int): Iteration for the saving image name. - Default: None. - - Returns: - dict: Output results. - """ - output = self.generator(lq) - if self.test_cfg is not None and self.test_cfg.get('metrics', None): - assert gt is not None, ( - 'evaluation with metrics must have gt images.') - results = dict(eval_result=self.evaluate(output, gt)) - else: - results = dict(lq=lq.cpu(), output=output.cpu()) - if gt is not None: - results['gt'] = gt.cpu() - - # save image - if save_image: - lq_path = meta[0]['lq_path'] - folder_name = osp.splitext(osp.basename(lq_path))[0] - if isinstance(iteration, numbers.Number): - save_path = osp.join(save_path, folder_name, - f'{folder_name}-{iteration + 1:06d}.png') - elif iteration is None: - save_path = osp.join(save_path, f'{folder_name}.png') - else: - raise ValueError('iteration should be number or None, ' - f'but got {type(iteration)}') - mmcv.imwrite(tensor2img(output), save_path) - - return results - - def forward_dummy(self, img): - """Used for computing network FLOPs. - - Args: - img (Tensor): Input image. - - Returns: - Tensor: Output image. - """ - out = self.generator(img) - return out - - def train_step(self, data_batch, optimizer): - """Train step. - - Args: - data_batch (dict): A batch of data. - optimizer (obj): Optimizer. - - Returns: - dict: Returned output. - """ - outputs = self(**data_batch, test_mode=False) - loss, log_vars = self.parse_losses(outputs.pop('losses')) - - # optimize - optimizer['generator'].zero_grad() - loss.backward() - optimizer['generator'].step() - - outputs.update({'log_vars': log_vars}) - return outputs - - def val_step(self, data_batch, **kwargs): - """Validation step. - - Args: - data_batch (dict): A batch of data. - kwargs (dict): Other arguments for ``val_step``. - - Returns: - dict: Returned output. - """ - output = self.forward_test(**data_batch, **kwargs) - return output diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/restorers/basicvsr.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/models/restorers/basicvsr.py deleted file mode 100755 index 1510c55e7..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/models/restorers/basicvsr.py +++ /dev/null @@ -1,224 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -import os.path as osp - -import mmcv -import numpy as np -import torch - -from mmedit.core import tensor2img -from ..registry import MODELS -from .basic_restorer import BasicRestorer - - -@MODELS.register_module() -class BasicVSR(BasicRestorer): - """BasicVSR model for video super-resolution. - - Note that this model is used for IconVSR. - - Paper: - BasicVSR: The Search for Essential Components in Video Super-Resolution - and Beyond, CVPR, 2021 - - Args: - generator (dict): Config for the generator structure. - pixel_loss (dict): Config for pixel-wise loss. - ensemble (dict): Config for ensemble. Default: None. - train_cfg (dict): Config for training. Default: None. - test_cfg (dict): Config for testing. Default: None. - pretrained (str): Path for pretrained model. Default: None. - """ - - def __init__(self, - generator, - pixel_loss, - ensemble=None, - train_cfg=None, - test_cfg=None, - pretrained=None): - super().__init__(generator, pixel_loss, train_cfg, test_cfg, - pretrained) - - # fix pre-trained networks - self.fix_iter = train_cfg.get('fix_iter', 0) if train_cfg else 0 - self.is_weight_fixed = False - - # count training steps - self.register_buffer('step_counter', torch.zeros(1)) - - # ensemble - self.forward_ensemble = None - if ensemble is not None: - if ensemble['type'] == 'SpatialTemporalEnsemble': - from mmedit.models.common.ensemble import \ - SpatialTemporalEnsemble - is_temporal = ensemble.get('is_temporal_ensemble', False) - self.forward_ensemble = SpatialTemporalEnsemble(is_temporal) - else: - raise NotImplementedError( - 'Currently support only ' - '"SpatialTemporalEnsemble", but got type ' - f'[{ensemble["type"]}]') - - def check_if_mirror_extended(self, lrs): - """Check whether the input is a mirror-extended sequence. - - If mirror-extended, the i-th (i=0, ..., t-1) frame is equal to the - (t-1-i)-th frame. - - Args: - lrs (tensor): Input LR images with shape (n, t, c, h, w) - """ - - is_mirror_extended = False - if lrs.size(1) % 2 == 0: - lrs_1, lrs_2 = torch.chunk(lrs, 2, dim=1) - if torch.norm(lrs_1 - lrs_2.flip(1)) == 0: - is_mirror_extended = True - - return is_mirror_extended - - def train_step(self, data_batch, optimizer): - """Train step. - - Args: - data_batch (dict): A batch of data. - optimizer (obj): Optimizer. - - Returns: - dict: Returned output. - """ - # fix SPyNet and EDVR at the beginning - if self.step_counter < self.fix_iter: - if not self.is_weight_fixed: - self.is_weight_fixed = True - for k, v in self.generator.named_parameters(): - if 'spynet' in k or 'edvr' in k: - v.requires_grad_(False) - elif self.step_counter == self.fix_iter: - # train all the parameters - self.generator.requires_grad_(True) - - outputs = self(**data_batch, test_mode=False) - loss, log_vars = self.parse_losses(outputs.pop('losses')) - - # optimize - optimizer['generator'].zero_grad() - loss.backward() - optimizer['generator'].step() - - self.step_counter += 1 - - outputs.update({'log_vars': log_vars}) - return outputs - - def evaluate(self, output, gt): - """Evaluation function. - - If the output contains multiple frames, we compute the metric - one by one and take an average. - - Args: - output (Tensor): Model output with shape (n, t, c, h, w). - gt (Tensor): GT Tensor with shape (n, t, c, h, w). - - Returns: - dict: Evaluation results. - """ - crop_border = self.test_cfg.crop_border - convert_to = self.test_cfg.get('convert_to', None) - - eval_result = dict() - for metric in self.test_cfg.metrics: - if output.ndim == 5: # a sequence: (n, t, c, h, w) - avg = [] - for i in range(0, output.size(1)): - output_i = tensor2img(output[:, i, :, :, :]) - gt_i = tensor2img(gt[:, i, :, :, :]) - avg.append(self.allowed_metrics[metric]( - output_i, gt_i, crop_border, convert_to=convert_to)) - eval_result[metric] = np.mean(avg) - elif output.ndim == 4: # an image: (n, c, t, w), for Vimeo-90K-T - output_img = tensor2img(output) - gt_img = tensor2img(gt) - value = self.allowed_metrics[metric]( - output_img, gt_img, crop_border, convert_to=convert_to) - eval_result[metric] = value - - return eval_result - - def forward_test(self, - lq, - gt=None, - meta=None, - save_image=False, - save_path=None, - iteration=None): - """Testing forward function. - - Args: - lq (Tensor): LQ Tensor with shape (n, t, c, h, w). - gt (Tensor): GT Tensor with shape (n, t, c, h, w). Default: None. - save_image (bool): Whether to save image. Default: False. - save_path (str): Path to save image. Default: None. - iteration (int): Iteration for the saving image name. - Default: None. - - Returns: - dict: Output results. - """ - with torch.no_grad(): - if self.forward_ensemble is not None: - output = self.forward_ensemble(lq, self.generator) - else: - output = self.generator(lq) - - # If the GT is an image (i.e. the center frame), the output sequence is - # turned to an image. - if gt is not None and gt.ndim == 4: - t = output.size(1) - if self.check_if_mirror_extended(lq): # with mirror extension - output = 0.5 * (output[:, t // 4] + output[:, -1 - t // 4]) - else: # without mirror extension - output = output[:, t // 2] - - if self.test_cfg is not None and self.test_cfg.get('metrics', None): - assert gt is not None, ( - 'evaluation with metrics must have gt images.') - results = dict(eval_result=self.evaluate(output, gt)) - else: - results = dict(lq=lq.cpu(), output=output.cpu()) - if gt is not None: - results['gt'] = gt.cpu() - - # save image - if save_image: - if output.ndim == 4: # an image, key = 000001/0000 (Vimeo-90K) - img_name = meta[0]['key'].replace('/', '_') - if isinstance(iteration, numbers.Number): - save_path = osp.join( - save_path, f'{img_name}-{iteration + 1:06d}.png') - elif iteration is None: - save_path = osp.join(save_path, f'{img_name}.png') - else: - raise ValueError('iteration should be number or None, ' - f'but got {type(iteration)}') - mmcv.imwrite(tensor2img(output), save_path) - elif output.ndim == 5: # a sequence, key = 000 - folder_name = meta[0]['key'].split('/')[0] - for i in range(0, output.size(1)): - if isinstance(iteration, numbers.Number): - save_path_i = osp.join( - save_path, folder_name, - f'{i:08d}-{iteration + 1:06d}.png') - elif iteration is None: - save_path_i = osp.join(save_path, folder_name, - f'{i:08d}.png') - else: - raise ValueError('iteration should be number or None, ' - f'but got {type(iteration)}') - mmcv.imwrite( - tensor2img(output[:, i, :, :, :]), save_path_i) - - return results diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/__init__.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/__init__.py deleted file mode 100755 index 0521b4a1c..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .cli import modify_args -from .logger import get_root_logger -from .setup_env import setup_multi_processes - -__all__ = ['get_root_logger', 'setup_multi_processes', 'modify_args'] diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/cli.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/cli.py deleted file mode 100755 index a78b32712..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/cli.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import re -import sys -import warnings - - -def modify_args(): - for i, v in enumerate(sys.argv): - if i == 0: - assert v.endswith('.py') - elif re.match(r'--\w+_.*', v): - new_arg = v.replace('_', '-') - warnings.warn( - f'command line argument {v} is deprecated, ' - f'please use {new_arg} instead.', - category=DeprecationWarning, - ) - sys.argv[i] = new_arg diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/collect_env.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/collect_env.py deleted file mode 100755 index af22082c7..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/collect_env.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash - -import mmedit - - -def collect_env(): - """Collect the information of the running environments.""" - env_info = collect_base_env() - env_info['MMEditing'] = f'{mmedit.__version__}+{get_git_hash()[:7]}' - - return env_info - - -if __name__ == '__main__': - for name, val in collect_env().items(): - print('{}: {}'.format(name, val)) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/logger.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/logger.py deleted file mode 100755 index cdc340f18..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/logger.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -from mmcv.utils import get_logger - - -def get_root_logger(log_file=None, log_level=logging.INFO): - """Get the root logger. - - The logger will be initialized if it has not been initialized. By default a - StreamHandler will be added. If `log_file` is specified, a FileHandler will - also be added. The name of the root logger is the top-level package name, - e.g., "mmedit". - - Args: - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the root logger. - log_level (int): The root logger level. Note that only the process of - rank 0 is affected, while other processes will set the level to - "Error" and be silent most of the time. - - Returns: - logging.Logger: The root logger. - """ - # root logger name: mmedit - logger = get_logger(__name__.split('.')[0], log_file, log_level) - return logger diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/setup_env.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/setup_env.py deleted file mode 100755 index 21def2f08..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/utils/setup_env.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import platform -import warnings - -import cv2 -import torch.multiprocessing as mp - - -def setup_multi_processes(cfg): - """Setup multi-processing environment variables.""" - # set multi-process start method as `fork` to speed up the training - if platform.system() != 'Windows': - mp_start_method = cfg.get('mp_start_method', 'fork') - current_method = mp.get_start_method(allow_none=True) - if current_method is not None and current_method != mp_start_method: - warnings.warn( - f'Multi-processing start method `{mp_start_method}` is ' - f'different from the previous setting `{current_method}`.' - f'It will be force set to `{mp_start_method}`. You can change ' - f'this behavior by changing `mp_start_method` in your config.') - mp.set_start_method(mp_start_method, force=True) - - # disable opencv multithreading to avoid system being overloaded - opencv_num_threads = cfg.get('opencv_num_threads', 0) - cv2.setNumThreads(opencv_num_threads) - - # setup OMP threads - # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa - if 'OMP_NUM_THREADS' not in os.environ and cfg.data.workers_per_gpu > 1: - omp_num_threads = 1 - warnings.warn( - f'Setting OMP_NUM_THREADS environment variable for each process ' - f'to be {omp_num_threads} in default, to avoid your system being ' - f'overloaded, please further tune the variable for optimal ' - f'performance in your application as needed.') - os.environ['OMP_NUM_THREADS'] = str(omp_num_threads) - - # setup MKL threads - if 'MKL_NUM_THREADS' not in os.environ and cfg.data.workers_per_gpu > 1: - mkl_num_threads = 1 - warnings.warn( - f'Setting MKL_NUM_THREADS environment variable for each process ' - f'to be {mkl_num_threads} in default, to avoid your system being ' - f'overloaded, please further tune the variable for optimal ' - f'performance in your application as needed.') - os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads) diff --git a/cv/super_resolution/basicvsr++/pytorch/mmedit/version.py b/cv/super_resolution/basicvsr++/pytorch/mmedit/version.py deleted file mode 100755 index ffec9df73..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/mmedit/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) Open-MMLab. All rights reserved. - -__version__ = '0.15.0' - - -def parse_version_info(version_str): - ver_info = [] - for x in version_str.split('.'): - if x.isdigit(): - ver_info.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - ver_info.append(int(patch_version[0])) - ver_info.append(f'rc{patch_version[1]}') - return tuple(ver_info) - - -version_info = parse_version_info(__version__) diff --git a/cv/super_resolution/basicvsr++/pytorch/requirements.txt b/cv/super_resolution/basicvsr++/pytorch/requirements.txt deleted file mode 100755 index 21300ccfa..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -yapf -addict -opencv-python diff --git a/cv/super_resolution/basicvsr++/pytorch/setup.py b/cv/super_resolution/basicvsr++/pytorch/setup.py deleted file mode 100755 index 3cc9f7a46..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/setup.py +++ /dev/null @@ -1,354 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -import glob -import os -import re -from pkg_resources import DistributionNotFound, get_distribution -from setuptools import find_packages, setup - -EXT_TYPE = '' -try: - import torch - if torch.__version__ == 'parrots': - from parrots.utils.build_extension import BuildExtension - EXT_TYPE = 'parrots' - else: - from torch.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - cmd_class = {'build_ext': BuildExtension} -except ModuleNotFoundError: - cmd_class = {} - print('Skip building ext ops due to the absence of torch.') - - -def choose_requirement(primary, secondary): - """If some version of primary requirement installed, return primary, else - return secondary.""" - try: - name = re.split(r'[!<>=]', primary)[0] - get_distribution(name) - except DistributionNotFound: - return secondary - - return str(primary) - - -def get_version(): - version_file = 'mmcv/version.py' - with open(version_file, 'r', encoding='utf-8') as f: - exec(compile(f.read(), version_file, 'exec')) - version = locals()['__version__'] - local_version_identifier = os.environ.get('MMCV_LOCAL_VERSION_IDENTIFIER', '') - if local_version_identifier != '': - version += '+' + local_version_identifier - return version - - -def parse_requirements(fname='requirements/runtime.txt', with_version=True): - """Parse the package dependencies listed in a requirements file but strips - specific versioning information. - - Args: - fname (str): path to requirements file - with_version (bool, default=False): if True include version specs - - Returns: - List[str]: list of requirements items - - CommandLine: - python -c "import setup; print(setup.parse_requirements())" - """ - import sys - from os.path import exists - require_fpath = fname - - def parse_line(line): - """Parse information from a line in a requirements text file.""" - if line.startswith('-r '): - # Allow specifying requirements in other files - target = line.split(' ')[1] - for info in parse_require_file(target): - yield info - else: - info = {'line': line} - if line.startswith('-e '): - info['package'] = line.split('#egg=')[1] - else: - # Remove versioning from the package - pat = '(' + '|'.join(['>=', '==', '>']) + ')' - parts = re.split(pat, line, maxsplit=1) - parts = [p.strip() for p in parts] - - info['package'] = parts[0] - if len(parts) > 1: - op, rest = parts[1:] - if ';' in rest: - # Handle platform specific dependencies - # http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies - version, platform_deps = map(str.strip, - rest.split(';')) - info['platform_deps'] = platform_deps - else: - version = rest # NOQA - info['version'] = (op, version) - yield info - - def parse_require_file(fpath): - with open(fpath, 'r') as f: - for line in f.readlines(): - line = line.strip() - if line and not line.startswith('#'): - for info in parse_line(line): - yield info - - def gen_packages_items(): - if exists(require_fpath): - for info in parse_require_file(require_fpath): - parts = [info['package']] - if with_version and 'version' in info: - parts.extend(info['version']) - if not sys.version.startswith('3.4'): - # apparently package_deps are broken in 3.4 - platform_deps = info.get('platform_deps') - if platform_deps is not None: - parts.append(';' + platform_deps) - item = ''.join(parts) - yield item - - packages = list(gen_packages_items()) - return packages - - -install_requires = parse_requirements() - -try: - # OpenCV installed via conda. - import cv2 # NOQA: F401 - major, minor, *rest = cv2.__version__.split('.') - if int(major) < 3: - raise RuntimeError( - f'OpenCV >=3 is required but {cv2.__version__} is installed') -except ImportError: - # If first not installed install second package - CHOOSE_INSTALL_REQUIRES = [('opencv-python-headless>=3', - 'opencv-python>=3')] - for main, secondary in CHOOSE_INSTALL_REQUIRES: - install_requires.append(choose_requirement(main, secondary)) - - -def get_extensions(): - extensions = [] - - if os.getenv('MMCV_WITH_TRT', '0') != '0': - ext_name = 'mmcv._ext_trt' - from torch.utils.cpp_extension import include_paths, library_paths - library_dirs = [] - libraries = [] - include_dirs = [] - tensorrt_path = os.getenv('TENSORRT_DIR', '0') - tensorrt_lib_path = glob.glob( - os.path.join(tensorrt_path, 'targets', '*', 'lib'))[0] - library_dirs += [tensorrt_lib_path] - libraries += ['nvinfer', 'nvparsers', 'nvinfer_plugin'] - libraries += ['cudart'] - define_macros = [] - extra_compile_args = {'cxx': []} - - include_path = os.path.abspath('./mmcv/ops/csrc/common/cuda') - include_trt_path = os.path.abspath('./mmcv/ops/csrc/tensorrt') - include_dirs.append(include_path) - include_dirs.append(include_trt_path) - include_dirs.append(os.path.join(tensorrt_path, 'include')) - include_dirs += include_paths(cuda=True) - - op_files = glob.glob('./mmcv/ops/csrc/tensorrt/plugins/*') - define_macros += [('MMCV_WITH_CUDA', None)] - define_macros += [('MMCV_WITH_TRT', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - library_dirs += library_paths(cuda=True) - - from setuptools import Extension - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - language='c++', - library_dirs=library_dirs, - libraries=libraries) - extensions.append(ext_ops) - - if os.getenv('MMCV_WITH_OPS', '0') == '0': - return extensions - - if EXT_TYPE == 'parrots': - ext_name = 'mmcv._ext' - from parrots.utils.build_extension import Extension - # new parrots op impl do not use MMCV_USE_PARROTS - # define_macros = [('MMCV_USE_PARROTS', None)] - define_macros = [] - include_dirs = [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') +\ - glob.glob('./mmcv/ops/csrc/parrots/*.cpp') - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args = { - 'nvcc': [cuda_args] if cuda_args else [], - 'cxx': [], - } - if torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - extra_compile_args['nvcc'] += [ - '-D__CUDA_NO_HALF_OPERATORS__', - '-D__CUDA_NO_HALF_CONVERSIONS__', - '-D__CUDA_NO_HALF2_OPERATORS__', - ] - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - cuda=True, - pytorch=True) - extensions.append(ext_ops) - elif EXT_TYPE == 'pytorch': - ext_name = 'mmcv._ext' - from torch.utils.cpp_extension import CppExtension, CUDAExtension - - # prevent ninja from using too many resources - try: - import psutil - num_cpu = len(psutil.Process().cpu_affinity()) - cpu_use = max(4, num_cpu - 1) - except (ModuleNotFoundError, AttributeError): - cpu_use = 4 - - os.environ.setdefault('MAX_JOBS', str(cpu_use)) - define_macros = [] - extra_compile_args = {'cxx': []} - include_dirs = [] - - is_rocm_pytorch = False - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm_pytorch = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - - project_dir = 'mmcv/ops/csrc/' - if is_rocm_pytorch: - from torch.utils.hipify import hipify_python - - hipify_python.hipify( - project_directory=project_dir, - output_directory=project_dir, - includes='mmcv/ops/csrc/*', - show_detailed=True, - is_pytorch_extension=True, - ) - define_macros += [('MMCV_WITH_CUDA', None)] - define_macros += [('HIP_DIFF', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/hip/*') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/hip')) - elif torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - else: - print(f'Compiling {ext_name} without CUDA') - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') - extension = CppExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - - ext_ops = extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args) - extensions.append(ext_ops) - - if EXT_TYPE == 'pytorch' and os.getenv('MMCV_WITH_ORT', '0') != '0': - ext_name = 'mmcv._ext_ort' - from torch.utils.cpp_extension import library_paths, include_paths - import onnxruntime - library_dirs = [] - libraries = [] - include_dirs = [] - ort_path = os.getenv('ONNXRUNTIME_DIR', '0') - library_dirs += [os.path.join(ort_path, 'lib')] - libraries.append('onnxruntime') - define_macros = [] - extra_compile_args = {'cxx': []} - - include_path = os.path.abspath('./mmcv/ops/csrc/onnxruntime') - include_dirs.append(include_path) - include_dirs.append(os.path.join(ort_path, 'include')) - - op_files = glob.glob('./mmcv/ops/csrc/onnxruntime/cpu/*') - if onnxruntime.get_device() == 'GPU' or os.getenv('FORCE_CUDA', - '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files += glob.glob('./mmcv/ops/csrc/onnxruntime/gpu/*') - include_dirs += include_paths(cuda=True) - library_dirs += library_paths(cuda=True) - else: - include_dirs += include_paths(cuda=False) - library_dirs += library_paths(cuda=False) - - from setuptools import Extension - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - language='c++', - library_dirs=library_dirs, - libraries=libraries) - extensions.append(ext_ops) - - return extensions - - -setup( - name='mmcv' if os.getenv('MMCV_WITH_OPS', '0') == '0' else 'mmcv-full', - version=get_version(), - description='OpenMMLab Computer Vision Foundation', - keywords='computer vision', - packages=find_packages(), - include_package_data=True, - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Topic :: Utilities', - ], - url='https://github.com/open-mmlab/mmcv', - author='MMCV Contributors', - author_email='openmmlab@gmail.com', - setup_requires=[], - tests_require=['pytest'], - install_requires=install_requires, - ext_modules=get_extensions(), - cmdclass=cmd_class, - zip_safe=False) diff --git a/cv/super_resolution/basicvsr++/pytorch/train.py b/cv/super_resolution/basicvsr++/pytorch/train.py deleted file mode 100755 index d85aabcad..000000000 --- a/cv/super_resolution/basicvsr++/pytorch/train.py +++ /dev/null @@ -1,172 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import argparse -import copy -import os -import os.path as osp -import time - -import mmcv -import torch -import torch.distributed as dist -from mmcv import Config, DictAction -from mmcv.runner import init_dist - -from mmedit import __version__ -from mmedit.apis import init_random_seed, set_random_seed, train_model -from mmedit.datasets import build_dataset -from mmedit.models import build_model -from mmedit.utils import collect_env, get_root_logger, setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train an editor') - parser.add_argument('config', help='train config file path') - parser.add_argument('--work-dir', help='the dir to save logs and models') - parser.add_argument( - '--resume-from', help='the checkpoint file to resume from') - parser.add_argument( - '--no-validate', - action='store_true', - help='whether not to evaluate the checkpoint during training') - parser.add_argument( - '--gpus', - type=int, - default=1, - help='number of gpus to use ' - '(only applicable to non-distributed training)') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--diff_seed', - action='store_true', - help='Whether or not set different seeds for different ranks') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - parser.add_argument( - '--autoscale-lr', - action='store_true', - help='automatically scale lr with the number of gpus') - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - # update configs according to CLI args - if args.work_dir is not None: - cfg.work_dir = args.work_dir - if args.resume_from is not None: - cfg.resume_from = args.resume_from - cfg.gpus = args.gpus - - if args.autoscale_lr: - # apply the linear scaling rule (https://arxiv.org/abs/1706.02677) - cfg.optimizer['lr'] = cfg.optimizer['lr'] * cfg.gpus / 8 - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - - # create work_dir - mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) - # init the logger before other steps - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - log_file = osp.join(cfg.work_dir, f'{timestamp}.log') - logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) - - # log env info - env_info_dict = collect_env.collect_env() - env_info = '\n'.join([f'{k}: {v}' for k, v in env_info_dict.items()]) - dash_line = '-' * 60 + '\n' - logger.info('Environment info:\n' + dash_line + env_info + '\n' + - dash_line) - - # log some basic info - logger.info('Distributed training: {}'.format(distributed)) - logger.info('mmedit Version: {}'.format(__version__)) - logger.info('Config:\n{}'.format(cfg.text)) - - # set random seeds - seed = init_random_seed(args.seed) - seed = seed + dist.get_rank() if args.diff_seed else seed - logger.info('Set random seed to {}, deterministic: {}'.format( - seed, args.deterministic)) - set_random_seed(seed, deterministic=args.deterministic) - cfg.seed = seed - - model = build_model( - cfg.model, train_cfg=cfg.train_cfg, test_cfg=cfg.test_cfg) - - datasets = [build_dataset(cfg.data.train)] - if len(cfg.workflow) == 2: - val_dataset = copy.deepcopy(cfg.data.val) - val_dataset.pipeline = cfg.data.train.pipeline - datasets.append(build_dataset(val_dataset)) - if cfg.checkpoint_config is not None: - # save version, config file content and class names in - # checkpoints as meta data - cfg.checkpoint_config.meta = dict( - mmedit_version=__version__, - config=cfg.text, - ) - - # meta information - meta = dict() - if cfg.get('exp_name', None) is None: - cfg['exp_name'] = osp.splitext(osp.basename(cfg.work_dir))[0] - meta['exp_name'] = cfg.exp_name - meta['mmedit Version'] = __version__ - meta['seed'] = seed - meta['env_info'] = env_info - - # add an attribute for visualization convenience - train_model( - model, - datasets, - cfg, - distributed=distributed, - validate=(not args.no_validate), - timestamp=timestamp, - meta=meta) - - -if __name__ == '__main__': - main() -- Gitee From efd115e3d5a66d1d30806ffe8b9b94b96f121f0a Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Thu, 6 Mar 2025 10:10:54 +0800 Subject: [PATCH 10/15] update esrgan --- cv/super_resolution/esrgan/pytorch/.gitignore | 138 -- cv/super_resolution/esrgan/pytorch/LICENSE | 203 --- cv/super_resolution/esrgan/pytorch/README.md | 42 +- .../esrgan_psnr_x4c64b23g32_g1_1000k_div2k.py | 134 -- .../esrgan/pytorch/dist_train.sh | 21 - .../esrgan/pytorch/docker/Dockerfile | 24 - .../esrgan/pytorch/docker/README.md | 19 - .../esrgan/pytorch/get_div2k_anno.py | 34 - .../esrgan/pytorch/mmcv/__init__.py | 12 - .../esrgan/pytorch/mmcv/cnn/__init__.py | 41 - .../esrgan/pytorch/mmcv/cnn/alexnet.py | 61 - .../pytorch/mmcv/cnn/bricks/__init__.py | 35 - .../pytorch/mmcv/cnn/bricks/activation.py | 92 -- .../pytorch/mmcv/cnn/bricks/context_block.py | 125 -- .../esrgan/pytorch/mmcv/cnn/bricks/conv.py | 44 - .../cnn/bricks/conv2d_adaptive_padding.py | 62 - .../pytorch/mmcv/cnn/bricks/conv_module.py | 206 --- .../esrgan/pytorch/mmcv/cnn/bricks/conv_ws.py | 148 -- .../bricks/depthwise_separable_conv_module.py | 96 -- .../esrgan/pytorch/mmcv/cnn/bricks/drop.py | 65 - .../mmcv/cnn/bricks/generalized_attention.py | 412 ----- .../pytorch/mmcv/cnn/bricks/hsigmoid.py | 34 - .../esrgan/pytorch/mmcv/cnn/bricks/hswish.py | 29 - .../pytorch/mmcv/cnn/bricks/non_local.py | 306 ---- .../esrgan/pytorch/mmcv/cnn/bricks/norm.py | 144 -- .../esrgan/pytorch/mmcv/cnn/bricks/padding.py | 36 - .../esrgan/pytorch/mmcv/cnn/bricks/plugin.py | 88 -- .../pytorch/mmcv/cnn/bricks/registry.py | 16 - .../esrgan/pytorch/mmcv/cnn/bricks/scale.py | 21 - .../esrgan/pytorch/mmcv/cnn/bricks/swish.py | 25 - .../pytorch/mmcv/cnn/bricks/transformer.py | 595 -------- .../pytorch/mmcv/cnn/bricks/upsample.py | 84 -- .../pytorch/mmcv/cnn/bricks/wrappers.py | 180 --- .../esrgan/pytorch/mmcv/cnn/builder.py | 30 - .../esrgan/pytorch/mmcv/cnn/resnet.py | 316 ---- .../esrgan/pytorch/mmcv/cnn/utils/__init__.py | 19 - .../pytorch/mmcv/cnn/utils/flops_counter.py | 599 -------- .../pytorch/mmcv/cnn/utils/fuse_conv_bn.py | 59 - .../esrgan/pytorch/mmcv/cnn/utils/sync_bn.py | 59 - .../pytorch/mmcv/cnn/utils/weight_init.py | 684 --------- .../esrgan/pytorch/mmcv/cnn/vgg.py | 175 --- .../esrgan/pytorch/mmcv/engine/__init__.py | 8 - .../esrgan/pytorch/mmcv/engine/test.py | 202 --- .../esrgan/pytorch/mmcv/fileio/__init__.py | 11 - .../esrgan/pytorch/mmcv/fileio/file_client.py | 1148 -------------- .../pytorch/mmcv/fileio/handlers/__init__.py | 7 - .../pytorch/mmcv/fileio/handlers/base.py | 30 - .../mmcv/fileio/handlers/json_handler.py | 36 - .../mmcv/fileio/handlers/pickle_handler.py | 28 - .../mmcv/fileio/handlers/yaml_handler.py | 24 - .../esrgan/pytorch/mmcv/fileio/io.py | 151 -- .../esrgan/pytorch/mmcv/fileio/parse.py | 97 -- .../esrgan/pytorch/mmcv/image/__init__.py | 28 - .../esrgan/pytorch/mmcv/image/colorspace.py | 306 ---- .../esrgan/pytorch/mmcv/image/geometric.py | 728 --------- .../esrgan/pytorch/mmcv/image/io.py | 258 ---- .../esrgan/pytorch/mmcv/image/misc.py | 44 - .../esrgan/pytorch/mmcv/image/photometric.py | 428 ------ .../esrgan/pytorch/mmcv/parallel/__init__.py | 13 - .../pytorch/mmcv/parallel/_functions.py | 79 - .../esrgan/pytorch/mmcv/parallel/collate.py | 84 -- .../pytorch/mmcv/parallel/data_container.py | 89 -- .../pytorch/mmcv/parallel/data_parallel.py | 89 -- .../pytorch/mmcv/parallel/distributed.py | 112 -- .../mmcv/parallel/distributed_deprecated.py | 70 - .../esrgan/pytorch/mmcv/parallel/registry.py | 8 - .../pytorch/mmcv/parallel/scatter_gather.py | 59 - .../esrgan/pytorch/mmcv/parallel/utils.py | 20 - .../esrgan/pytorch/mmcv/runner/__init__.py | 47 - .../esrgan/pytorch/mmcv/runner/base_module.py | 195 --- .../esrgan/pytorch/mmcv/runner/base_runner.py | 542 ------- .../esrgan/pytorch/mmcv/runner/builder.py | 24 - .../esrgan/pytorch/mmcv/runner/checkpoint.py | 707 --------- .../mmcv/runner/default_constructor.py | 44 - .../esrgan/pytorch/mmcv/runner/dist_utils.py | 164 -- .../pytorch/mmcv/runner/epoch_based_runner.py | 187 --- .../esrgan/pytorch/mmcv/runner/fp16_utils.py | 410 ----- .../pytorch/mmcv/runner/hooks/__init__.py | 29 - .../pytorch/mmcv/runner/hooks/checkpoint.py | 167 --- .../pytorch/mmcv/runner/hooks/closure.py | 11 - .../esrgan/pytorch/mmcv/runner/hooks/ema.py | 89 -- .../pytorch/mmcv/runner/hooks/evaluation.py | 509 ------- .../esrgan/pytorch/mmcv/runner/hooks/hook.py | 92 -- .../pytorch/mmcv/runner/hooks/iter_timer.py | 30 - .../mmcv/runner/hooks/logger/__init__.py | 15 - .../pytorch/mmcv/runner/hooks/logger/base.py | 166 -- .../mmcv/runner/hooks/logger/dvclive.py | 58 - .../mmcv/runner/hooks/logger/mlflow.py | 78 - .../mmcv/runner/hooks/logger/neptune.py | 82 - .../pytorch/mmcv/runner/hooks/logger/pavi.py | 117 -- .../mmcv/runner/hooks/logger/tensorboard.py | 57 - .../pytorch/mmcv/runner/hooks/logger/text.py | 256 ---- .../pytorch/mmcv/runner/hooks/logger/wandb.py | 56 - .../pytorch/mmcv/runner/hooks/lr_updater.py | 670 --------- .../pytorch/mmcv/runner/hooks/memory.py | 25 - .../mmcv/runner/hooks/momentum_updater.py | 493 ------ .../pytorch/mmcv/runner/hooks/optimizer.py | 508 ------- .../pytorch/mmcv/runner/hooks/profiler.py | 180 --- .../pytorch/mmcv/runner/hooks/sampler_seed.py | 20 - .../pytorch/mmcv/runner/hooks/sync_buffer.py | 22 - .../pytorch/mmcv/runner/iter_based_runner.py | 273 ---- .../esrgan/pytorch/mmcv/runner/log_buffer.py | 41 - .../pytorch/mmcv/runner/optimizer/__init__.py | 9 - .../pytorch/mmcv/runner/optimizer/builder.py | 44 - .../runner/optimizer/default_constructor.py | 249 --- .../esrgan/pytorch/mmcv/runner/priority.py | 60 - .../esrgan/pytorch/mmcv/runner/utils.py | 93 -- .../esrgan/pytorch/mmcv/utils/__init__.py | 69 - .../esrgan/pytorch/mmcv/utils/config.py | 688 --------- .../esrgan/pytorch/mmcv/utils/env.py | 95 -- .../esrgan/pytorch/mmcv/utils/ext_loader.py | 71 - .../esrgan/pytorch/mmcv/utils/logging.py | 110 -- .../esrgan/pytorch/mmcv/utils/misc.py | 377 ----- .../esrgan/pytorch/mmcv/utils/parrots_jit.py | 41 - .../pytorch/mmcv/utils/parrots_wrapper.py | 107 -- .../esrgan/pytorch/mmcv/utils/path.py | 101 -- .../esrgan/pytorch/mmcv/utils/progressbar.py | 208 --- .../esrgan/pytorch/mmcv/utils/registry.py | 315 ---- .../esrgan/pytorch/mmcv/utils/testing.py | 140 -- .../esrgan/pytorch/mmcv/utils/timer.py | 118 -- .../esrgan/pytorch/mmcv/utils/trace.py | 23 - .../pytorch/mmcv/utils/version_utils.py | 90 -- .../esrgan/pytorch/mmcv/version.py | 35 - .../esrgan/pytorch/mmedit/__init__.py | 34 - .../esrgan/pytorch/mmedit/apis/__init__.py | 18 - .../mmedit/apis/generation_inference.py | 63 - .../mmedit/apis/inpainting_inference.py | 53 - .../pytorch/mmedit/apis/matting_inference.py | 78 - .../mmedit/apis/restoration_face_inference.py | 90 -- .../mmedit/apis/restoration_inference.py | 48 - .../apis/restoration_video_inference.py | 129 -- .../esrgan/pytorch/mmedit/apis/test.py | 234 --- .../esrgan/pytorch/mmedit/apis/train.py | 361 ----- .../apis/video_interpolation_inference.py | 204 --- .../esrgan/pytorch/mmedit/core/__init__.py | 13 - .../mmedit/core/distributed_wrapper.py | 139 -- .../mmedit/core/evaluation/__init__.py | 10 - .../mmedit/core/evaluation/eval_hooks.py | 114 -- .../mmedit/core/evaluation/metric_utils.py | 81 - .../pytorch/mmedit/core/evaluation/metrics.py | 572 ------- .../core/evaluation/niqe_pris_params.npz | Bin 11850 -> 0 bytes .../pytorch/mmedit/core/export/__init__.py | 4 - .../pytorch/mmedit/core/export/wrappers.py | 134 -- .../pytorch/mmedit/core/hooks/__init__.py | 5 - .../esrgan/pytorch/mmedit/core/hooks/ema.py | 113 -- .../mmedit/core/hooks/visualization.py | 84 -- .../esrgan/pytorch/mmedit/core/mask.py | 316 ---- .../esrgan/pytorch/mmedit/core/misc.py | 74 - .../pytorch/mmedit/core/optimizer/__init__.py | 4 - .../pytorch/mmedit/core/optimizer/builder.py | 58 - .../pytorch/mmedit/core/scheduler/__init__.py | 4 - .../mmedit/core/scheduler/lr_updater.py | 304 ---- .../pytorch/mmedit/core/utils/__init__.py | 4 - .../pytorch/mmedit/core/utils/dist_utils.py | 35 - .../pytorch/mmedit/datasets/__init__.py | 11 - .../pytorch/mmedit/datasets/base_dataset.py | 78 - .../mmedit/datasets/base_sr_dataset.py | 87 -- .../esrgan/pytorch/mmedit/datasets/builder.py | 181 --- .../pytorch/mmedit/datasets/comp1k_dataset.py | 70 - .../mmedit/datasets/dataset_wrappers.py | 39 - .../mmedit/datasets/pipelines/__init__.py | 23 - .../mmedit/datasets/pipelines/augmentation.py | 1332 ----------------- .../mmedit/datasets/pipelines/compose.py | 53 - .../pytorch/mmedit/datasets/pipelines/crop.py | 749 --------- .../mmedit/datasets/pipelines/formating.py | 263 ---- .../mmedit/datasets/pipelines/loading.py | 562 ------- .../datasets/pipelines/matlab_like_resize.py | 275 ---- .../datasets/pipelines/normalization.py | 103 -- .../mmedit/datasets/pipelines/utils.py | 154 -- .../pytorch/mmedit/datasets/registry.py | 5 - .../mmedit/datasets/samplers/__init__.py | 4 - .../datasets/samplers/distributed_sampler.py | 71 - .../mmedit/datasets/sr_annotation_dataset.py | 75 - .../mmedit/datasets/sr_folder_dataset.py | 86 -- .../esrgan/pytorch/mmedit/models/__init__.py | 14 - .../mmedit/models/backbones/__init__.py | 5 - .../models/backbones/sr_backbones/__init__.py | 5 - .../models/backbones/sr_backbones/rrdb_net.py | 197 --- .../esrgan/pytorch/mmedit/models/base.py | 105 -- .../esrgan/pytorch/mmedit/models/builder.py | 60 - .../pytorch/mmedit/models/common/__init__.py | 32 - .../pytorch/mmedit/models/common/aspp.py | 125 -- .../models/common/contextual_attention.py | 379 ----- .../pytorch/mmedit/models/common/conv.py | 6 - .../mmedit/models/common/downsample.py | 22 - .../pytorch/mmedit/models/common/ensemble.py | 105 -- .../pytorch/mmedit/models/common/flow_warp.py | 50 - .../mmedit/models/common/gated_conv_module.py | 72 - .../mmedit/models/common/gca_module.py | 358 ----- .../models/common/generation_model_utils.py | 301 ---- .../mmedit/models/common/img_normalize.py | 32 - .../mmedit/models/common/linear_module.py | 89 -- .../mmedit/models/common/mask_conv_module.py | 88 -- .../mmedit/models/common/model_utils.py | 136 -- .../mmedit/models/common/partial_conv.py | 102 -- .../models/common/separable_conv_module.py | 97 -- .../mmedit/models/common/sr_backbone_utils.py | 97 -- .../pytorch/mmedit/models/common/upsample.py | 51 - .../mmedit/models/components/__init__.py | 5 - .../components/discriminators/__init__.py | 6 - .../components/discriminators/modified_vgg.py | 118 -- .../pytorch/mmedit/models/losses/__init__.py | 9 - .../pytorch/mmedit/models/losses/gan_loss.py | 344 ----- .../mmedit/models/losses/perceptual_loss.py | 287 ---- .../mmedit/models/losses/pixelwise_loss.py | 221 --- .../pytorch/mmedit/models/losses/utils.py | 115 -- .../esrgan/pytorch/mmedit/models/registry.py | 8 - .../mmedit/models/restorers/__init__.py | 7 - .../mmedit/models/restorers/basic_restorer.py | 210 --- .../pytorch/mmedit/models/restorers/esrgan.py | 128 -- .../pytorch/mmedit/models/restorers/srgan.py | 176 --- .../esrgan/pytorch/mmedit/utils/__init__.py | 6 - .../esrgan/pytorch/mmedit/utils/cli.py | 18 - .../pytorch/mmedit/utils/collect_env.py | 18 - .../esrgan/pytorch/mmedit/utils/logger.py | 27 - .../esrgan/pytorch/mmedit/utils/setup_env.py | 47 - .../esrgan/pytorch/mmedit/version.py | 18 - .../esrgan/pytorch/requirements.txt | 13 - cv/super_resolution/esrgan/pytorch/train.py | 172 --- 219 files changed, 17 insertions(+), 31142 deletions(-) delete mode 100755 cv/super_resolution/esrgan/pytorch/.gitignore delete mode 100755 cv/super_resolution/esrgan/pytorch/LICENSE delete mode 100755 cv/super_resolution/esrgan/pytorch/configs/esrgan_psnr_x4c64b23g32_g1_1000k_div2k.py delete mode 100755 cv/super_resolution/esrgan/pytorch/dist_train.sh delete mode 100755 cv/super_resolution/esrgan/pytorch/docker/Dockerfile delete mode 100755 cv/super_resolution/esrgan/pytorch/docker/README.md delete mode 100755 cv/super_resolution/esrgan/pytorch/get_div2k_anno.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/alexnet.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/activation.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/context_block.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv_module.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv_ws.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/drop.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/generalized_attention.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/hsigmoid.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/hswish.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/non_local.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/norm.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/padding.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/plugin.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/registry.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/scale.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/swish.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/transformer.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/upsample.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/wrappers.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/builder.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/resnet.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/flops_counter.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/fuse_conv_bn.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/sync_bn.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/weight_init.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/cnn/vgg.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/engine/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/engine/test.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/fileio/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/fileio/file_client.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/base.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/json_handler.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/pickle_handler.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/yaml_handler.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/fileio/io.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/fileio/parse.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/image/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/image/colorspace.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/image/geometric.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/image/io.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/image/misc.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/image/photometric.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/parallel/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/parallel/_functions.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/parallel/collate.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/parallel/data_container.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/parallel/data_parallel.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/parallel/distributed.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/parallel/distributed_deprecated.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/parallel/registry.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/parallel/scatter_gather.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/parallel/utils.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/base_module.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/base_runner.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/builder.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/checkpoint.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/default_constructor.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/dist_utils.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/epoch_based_runner.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/fp16_utils.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/checkpoint.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/closure.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/ema.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/evaluation.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/hook.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/iter_timer.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/base.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/dvclive.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/mlflow.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/neptune.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/pavi.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/tensorboard.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/text.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/wandb.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/lr_updater.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/memory.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/momentum_updater.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/optimizer.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/profiler.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/sampler_seed.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/sync_buffer.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/iter_based_runner.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/log_buffer.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/optimizer/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/optimizer/builder.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/optimizer/default_constructor.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/priority.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/runner/utils.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/config.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/env.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/ext_loader.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/logging.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/misc.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/parrots_jit.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/parrots_wrapper.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/path.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/progressbar.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/registry.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/testing.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/timer.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/trace.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/utils/version_utils.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmcv/version.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/apis/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/apis/generation_inference.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/apis/inpainting_inference.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/apis/matting_inference.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/apis/restoration_face_inference.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/apis/restoration_inference.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/apis/restoration_video_inference.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/apis/test.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/apis/train.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/apis/video_interpolation_inference.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/distributed_wrapper.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/eval_hooks.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/metric_utils.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/metrics.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/niqe_pris_params.npz delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/export/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/export/wrappers.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/hooks/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/hooks/ema.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/hooks/visualization.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/mask.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/misc.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/optimizer/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/optimizer/builder.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/scheduler/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/scheduler/lr_updater.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/utils/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/core/utils/dist_utils.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/base_dataset.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/base_sr_dataset.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/builder.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/comp1k_dataset.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/dataset_wrappers.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/augmentation.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/compose.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/crop.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/formating.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/loading.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/matlab_like_resize.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/normalization.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/utils.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/registry.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/samplers/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/samplers/distributed_sampler.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/sr_annotation_dataset.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/datasets/sr_folder_dataset.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/backbones/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/backbones/sr_backbones/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/backbones/sr_backbones/rrdb_net.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/base.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/builder.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/aspp.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/contextual_attention.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/conv.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/downsample.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/ensemble.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/flow_warp.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/gated_conv_module.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/gca_module.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/generation_model_utils.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/img_normalize.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/linear_module.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/mask_conv_module.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/model_utils.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/partial_conv.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/separable_conv_module.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/sr_backbone_utils.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/common/upsample.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/components/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/components/discriminators/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/components/discriminators/modified_vgg.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/losses/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/losses/gan_loss.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/losses/perceptual_loss.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/losses/pixelwise_loss.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/losses/utils.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/registry.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/basic_restorer.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/esrgan.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/srgan.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/utils/__init__.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/utils/cli.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/utils/collect_env.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/utils/logger.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/utils/setup_env.py delete mode 100755 cv/super_resolution/esrgan/pytorch/mmedit/version.py delete mode 100755 cv/super_resolution/esrgan/pytorch/requirements.txt delete mode 100755 cv/super_resolution/esrgan/pytorch/train.py diff --git a/cv/super_resolution/esrgan/pytorch/.gitignore b/cv/super_resolution/esrgan/pytorch/.gitignore deleted file mode 100755 index 5fff6429d..000000000 --- a/cv/super_resolution/esrgan/pytorch/.gitignore +++ /dev/null @@ -1,138 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class -**/*.pyc - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/en/_build/ -docs/en/_tmp/ -docs/zh_cn/_build/ -docs/zh_cn/_tmp/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -# custom -.vscode -.idea -*.pkl -*.pkl.json -*.log.json -work_dirs/ -/data/ -/data -mmedit/.mim - -# Pytorch -*.pth - -# onnx and tensorrt -*.onnx -*.trt - -# local history -.history/** - -# Pytorch Server -*.mar - -# MacOS -.DS_Store -work_dirs/ -data/ -mmcv_ori/ diff --git a/cv/super_resolution/esrgan/pytorch/LICENSE b/cv/super_resolution/esrgan/pytorch/LICENSE deleted file mode 100755 index 94d620d25..000000000 --- a/cv/super_resolution/esrgan/pytorch/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Copyright (c) MMEditing Authors. All rights reserved. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020 MMEditing Authors. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cv/super_resolution/esrgan/pytorch/README.md b/cv/super_resolution/esrgan/pytorch/README.md index 10d6b759b..7f9028993 100755 --- a/cv/super_resolution/esrgan/pytorch/README.md +++ b/cv/super_resolution/esrgan/pytorch/README.md @@ -7,47 +7,39 @@ The Super-Resolution Generative Adversarial Network (SRGAN) is a seminal work th ## Step 1: Installing packages ```shell -$ pip3 install -r requirements.txt +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +git clone https://github.com/open-mmlab/mmagic.git -b v1.2.0 --depth=1 +cd mmagic/ +pip3 install -e . -v + +sed -i 's/diffusers.models.unet_2d_condition/diffusers.models.unets.unet_2d_condition/g' mmagic/models/editors/vico/vico_utils.py +pip install albumentations ``` ## Step 2: Preparing datasets ```shell -$ cd /path/to/modelzoo/cv/super_resolution/esrgan/pytorch - -# Download DIV2K +# Download DIV2K: https://data.vision.ee.ethz.ch/cvl/DIV2K/ or you can follow this tools/dataset_converters/div2k/README.md $ mkdir -p data/DIV2K -$ cd data/DIV2K -# Homepage of DIV2K: https://data.vision.ee.ethz.ch/cvl/DIV2K/ - -# Download validation samples -$ cd ../.. -$ mkdir -p data/test -$ cd data/test -# Home page of Set5: http://people.rennes.inria.fr/Aline.Roumy/results/SR_BMVC12.html -# Home page of Set14: https://github.com/jbhuang0604/SelfExSR - -$ python3 get_div2k_anno.py ``` ## Step 3: Training ### One single GPU - ```shell -$ python3 train.py [training args] # config file can be found in the configs directory +python3 tools/train.py configs/esrgan/esrgan_psnr-x4c64b23g32_1xb16-1000k_div2k.py ``` ### Mutiple GPUs on one machine - -```shell -$ bash dist_train.sh [training args] # config file can be found in the configs directory -``` -### Example - ```shell -bash dist_train.sh configs/esrgan_psnr_x4c64b23g32_g1_1000k_div2k.py 8 +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/esrgan/esrgan_psnr-x4c64b23g32_1xb16-1000k_div2k.py 8 ``` ## Reference -https://github.com/open-mmlab/mmediting +[mmagic](https://github.com/open-mmlab/mmagic) diff --git a/cv/super_resolution/esrgan/pytorch/configs/esrgan_psnr_x4c64b23g32_g1_1000k_div2k.py b/cv/super_resolution/esrgan/pytorch/configs/esrgan_psnr_x4c64b23g32_g1_1000k_div2k.py deleted file mode 100755 index bf53fe07b..000000000 --- a/cv/super_resolution/esrgan/pytorch/configs/esrgan_psnr_x4c64b23g32_g1_1000k_div2k.py +++ /dev/null @@ -1,134 +0,0 @@ - # Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -exp_name = 'esrgan_psnr_x4c64b23g32_g1_1000k_div2k' - -scale = 4 -# model settings -model = dict( - type='BasicRestorer', - generator=dict( - type='RRDBNet', - in_channels=3, - out_channels=3, - mid_channels=64, - num_blocks=23, - growth_channels=32, - upscale_factor=scale), - pixel_loss=dict(type='L1Loss', loss_weight=1.0, reduction='mean')) -# model training and testing settings -train_cfg = None -test_cfg = dict(metrics=['PSNR', 'SSIM'], crop_border=scale) - -# dataset settings -train_dataset_type = 'SRAnnotationDataset' -val_dataset_type = 'SRFolderDataset' -train_pipeline = [ - dict( - type='LoadImageFromFile', - io_backend='disk', - key='lq', - flag='unchanged'), - dict( - type='LoadImageFromFile', - io_backend='disk', - key='gt', - flag='unchanged'), - dict(type='RescaleToZeroOne', keys=['lq', 'gt']), - dict( - type='Normalize', - keys=['lq', 'gt'], - mean=[0, 0, 0], - std=[1, 1, 1], - to_rgb=True), - dict(type='PairedRandomCrop', gt_patch_size=128), - dict( - type='Flip', keys=['lq', 'gt'], flip_ratio=0.5, - direction='horizontal'), - dict(type='Flip', keys=['lq', 'gt'], flip_ratio=0.5, direction='vertical'), - dict(type='RandomTransposeHW', keys=['lq', 'gt'], transpose_ratio=0.5), - dict(type='Collect', keys=['lq', 'gt'], meta_keys=['lq_path', 'gt_path']), - dict(type='ImageToTensor', keys=['lq', 'gt']) -] -test_pipeline = [ - dict( - type='LoadImageFromFile', - io_backend='disk', - key='lq', - flag='unchanged'), - dict( - type='LoadImageFromFile', - io_backend='disk', - key='gt', - flag='unchanged'), - dict(type='RescaleToZeroOne', keys=['lq', 'gt']), - dict( - type='Normalize', - keys=['lq', 'gt'], - mean=[0, 0, 0], - std=[1, 1, 1], - to_rgb=True), - dict(type='Collect', keys=['lq', 'gt'], meta_keys=['lq_path', 'gt_path']), - dict(type='ImageToTensor', keys=['lq', 'gt']) -] - -data = dict( - workers_per_gpu=8, - train_dataloader=dict(samples_per_gpu=16, drop_last=True), - val_dataloader=dict(samples_per_gpu=1), - test_dataloader=dict(samples_per_gpu=1), - train=dict( - type='RepeatDataset', - times=1000, - dataset=dict( - type=train_dataset_type, - lq_folder='data/DIV2K/DIV2K_train_LR_bicubic/X4', - gt_folder='data/DIV2K/DIV2K_train_HR', - ann_file='data/DIV2K/meta_info_DIV2K800sub_GT.txt', - pipeline=train_pipeline, - filename_tmpl='{}x4', - scale=scale)), - val=dict( - type=val_dataset_type, - lq_folder='data/test/Set14/LRbicx4', - gt_folder='data/test/Set14/GTmod12', - pipeline=test_pipeline, - scale=scale, - filename_tmpl='{}'), - test=dict( - type=val_dataset_type, - lq_folder='data/val_set14/Set14_bicLRx4', - gt_folder='data/val_set14/Set14', - pipeline=test_pipeline, - scale=scale, - filename_tmpl='{}')) - -# optimizer -optimizers = dict(generator=dict(type='Adam', lr=2e-4, betas=(0.9, 0.999))) - -# learning policy -total_iters = 1000000 -lr_config = dict( - policy='CosineRestart', - by_epoch=False, - periods=[250000, 250000, 250000, 250000], - restart_weights=[1, 1, 1, 1], - min_lr=1e-7) - -checkpoint_config = dict(interval=5000, save_optimizer=True, by_epoch=False) -evaluation = dict(interval=5000, save_image=True, gpu_collect=True) -log_config = dict( - interval=10, - hooks=[ - dict(type='TextLoggerHook', by_epoch=False), - dict(type='TensorboardLoggerHook'), - # dict(type='PaviLoggerHook', init_kwargs=dict(project='mmedit-sr')) - ]) -visual_config = None - -# runtime settings -dist_params = dict(backend='nccl') -log_level = 'INFO' -work_dir = f'./work_dirs/{exp_name}' -load_from = None -resume_from = None -workflow = [('train', 1)] -cudnn_benchmark = True diff --git a/cv/super_resolution/esrgan/pytorch/dist_train.sh b/cv/super_resolution/esrgan/pytorch/dist_train.sh deleted file mode 100755 index 87c687cd7..000000000 --- a/cv/super_resolution/esrgan/pytorch/dist_train.sh +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -#!/usr/bin/env bash - -CONFIG=$1 -GPUS=$2 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/train.py \ - $CONFIG \ - --seed 0 \ - --launcher pytorch ${@:3} diff --git a/cv/super_resolution/esrgan/pytorch/docker/Dockerfile b/cv/super_resolution/esrgan/pytorch/docker/Dockerfile deleted file mode 100755 index 651287043..000000000 --- a/cv/super_resolution/esrgan/pytorch/docker/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -ARG PYTORCH="1.6.0" -ARG CUDA="10.1" -ARG CUDA_ALIAS="101" -ARG CUDNN="7" -ARG MMCV="1.3.1" - -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ENV TORCH_CUDA_ARCH_LIST="6.0 6.1 7.0+PTX" -ENV TORCH_NVCC_FLAGS="-Xfatbin -compress-all" -ENV CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" - -RUN apt-get update && apt-get install -y git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 libgl1-mesa-glx \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Install mmediting -RUN conda clean --all -RUN git clone https://github.com/open-mmlab/mmediting.git /mmediting -WORKDIR /mmediting -ENV FORCE_CUDA="1" -RUN pip install mmcv-full==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA_ALIAS}/torch${PYTORCH}/index.html -RUN pip install -r requirements.txt -RUN pip install --no-cache-dir -e . diff --git a/cv/super_resolution/esrgan/pytorch/docker/README.md b/cv/super_resolution/esrgan/pytorch/docker/README.md deleted file mode 100755 index 851c28b0b..000000000 --- a/cv/super_resolution/esrgan/pytorch/docker/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Docker Image - -We provide a [Dockerfile](Dockerfile) to build an image. - -```shell -# build an image with PyTorch 1.6, CUDA 10.1 -docker build -t mmediting docker/ -``` - -Run it with - -```shell -docker run --gpus all --shm-size=8g -it -v {DATA_DIR}:/mmediting/data mmediting -``` - -**Note**: -Versions defined in this [Dockerfile](Dockerfile) is not up-to-date. -If you use this Dockerfile in your project, you probably want to make some updates. -Feel free to submit an issue or PR for the update. diff --git a/cv/super_resolution/esrgan/pytorch/get_div2k_anno.py b/cv/super_resolution/esrgan/pytorch/get_div2k_anno.py deleted file mode 100755 index 45665d7d6..000000000 --- a/cv/super_resolution/esrgan/pytorch/get_div2k_anno.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -import mmcv -from os import path as osp -from PIL import Image - - -def generate_meta_info_div2k(): - """Generate meta info for DIV2K dataset. - """ - - gt_folder = 'data/DIV2K/DIV2K_train_HR' - meta_info_txt = 'data/DIV2K/meta_info_DIV2K800sub_GT.txt' - - img_list = sorted(list(mmcv.scandir(gt_folder))) - - with open(meta_info_txt, 'w') as f: - for idx, img_path in enumerate(img_list): - img = Image.open(osp.join(gt_folder, img_path)) # lazy load - width, height = img.size - mode = img.mode - if mode == 'RGB': - n_channel = 3 - elif mode == 'L': - n_channel = 1 - else: - raise ValueError(f'Unsupported mode {mode}.') - - info = f'{img_path} ({height},{width},{n_channel})' - print(idx + 1, info) - f.write(f'{info}\n') - - -if __name__ == '__main__': - generate_meta_info_div2k() diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/__init__.py deleted file mode 100755 index bf07d0924..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -from .fileio import * -from .image import * -from .utils import * -from .version import * - -# The following modules are not imported to this level, so mmcv may be used -# without PyTorch. -# - runner -# - parallel -# - op diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/__init__.py deleted file mode 100755 index 7246c8974..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .alexnet import AlexNet -# yapf: disable -from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, - ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, - ConvTranspose2d, ConvTranspose3d, ConvWS2d, - DepthwiseSeparableConvModule, GeneralizedAttention, - HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, - NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, - build_activation_layer, build_conv_layer, - build_norm_layer, build_padding_layer, build_plugin_layer, - build_upsample_layer, conv_ws_2d, is_norm) -from .builder import MODELS, build_model_from_cfg -# yapf: enable -from .resnet import ResNet, make_res_layer -from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, - NormalInit, PretrainedInit, TruncNormalInit, UniformInit, - XavierInit, bias_init_with_prob, caffe2_xavier_init, - constant_init, fuse_conv_bn, get_model_complexity_info, - initialize, kaiming_init, normal_init, trunc_normal_init, - uniform_init, xavier_init) -from .vgg import VGG, make_vgg_layer - -__all__ = [ - 'AlexNet', 'VGG', 'make_vgg_layer', 'ResNet', 'make_res_layer', - 'constant_init', 'xavier_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'kaiming_init', 'caffe2_xavier_init', - 'bias_init_with_prob', 'ConvModule', 'build_activation_layer', - 'build_conv_layer', 'build_norm_layer', 'build_padding_layer', - 'build_upsample_layer', 'build_plugin_layer', 'is_norm', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'HSigmoid', 'Swish', 'HSwish', - 'GeneralizedAttention', 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', - 'PADDING_LAYERS', 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', - 'get_model_complexity_info', 'conv_ws_2d', 'ConvAWS2d', 'ConvWS2d', - 'fuse_conv_bn', 'DepthwiseSeparableConvModule', 'Linear', 'Conv2d', - 'ConvTranspose2d', 'MaxPool2d', 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', - 'initialize', 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'MODELS', 'build_model_from_cfg' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/alexnet.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/alexnet.py deleted file mode 100755 index 89e36b8c7..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/alexnet.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn - - -class AlexNet(nn.Module): - """AlexNet backbone. - - Args: - num_classes (int): number of classes for classification. - """ - - def __init__(self, num_classes=-1): - super(AlexNet, self).__init__() - self.num_classes = num_classes - self.features = nn.Sequential( - nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - nn.Conv2d(64, 192, kernel_size=5, padding=2), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - nn.Conv2d(192, 384, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.Conv2d(384, 256, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.Conv2d(256, 256, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - ) - if self.num_classes > 0: - self.classifier = nn.Sequential( - nn.Dropout(), - nn.Linear(256 * 6 * 6, 4096), - nn.ReLU(inplace=True), - nn.Dropout(), - nn.Linear(4096, 4096), - nn.ReLU(inplace=True), - nn.Linear(4096, num_classes), - ) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - # use default initializer - pass - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - - x = self.features(x) - if self.num_classes > 0: - x = x.view(x.size(0), 256 * 6 * 6) - x = self.classifier(x) - - return x diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/__init__.py deleted file mode 100755 index 0f33124ed..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .activation import build_activation_layer -from .context_block import ContextBlock -from .conv import build_conv_layer -from .conv2d_adaptive_padding import Conv2dAdaptivePadding -from .conv_module import ConvModule -from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d -from .depthwise_separable_conv_module import DepthwiseSeparableConvModule -from .drop import Dropout, DropPath -from .generalized_attention import GeneralizedAttention -from .hsigmoid import HSigmoid -from .hswish import HSwish -from .non_local import NonLocal1d, NonLocal2d, NonLocal3d -from .norm import build_norm_layer, is_norm -from .padding import build_padding_layer -from .plugin import build_plugin_layer -from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) -from .scale import Scale -from .swish import Swish -from .upsample import build_upsample_layer -from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, - Linear, MaxPool2d, MaxPool3d) - -__all__ = [ - 'ConvModule', 'build_activation_layer', 'build_conv_layer', - 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', - 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', - 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', - 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', - 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', - 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', - 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/activation.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/activation.py deleted file mode 100755 index 79f198838..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/activation.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version -from .registry import ACTIVATION_LAYERS - -for module in [ - nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, - nn.Sigmoid, nn.Tanh -]: - ACTIVATION_LAYERS.register_module(module=module) - - -@ACTIVATION_LAYERS.register_module(name='Clip') -@ACTIVATION_LAYERS.register_module() -class Clamp(nn.Module): - """Clamp activation layer. - - This activation function is to clamp the feature map value within - :math:`[min, max]`. More details can be found in ``torch.clamp()``. - - Args: - min (Number | optional): Lower-bound of the range to be clamped to. - Default to -1. - max (Number | optional): Upper-bound of the range to be clamped to. - Default to 1. - """ - - def __init__(self, min=-1., max=1.): - super(Clamp, self).__init__() - self.min = min - self.max = max - - def forward(self, x): - """Forward function. - - Args: - x (torch.Tensor): The input tensor. - - Returns: - torch.Tensor: Clamped tensor. - """ - return torch.clamp(x, min=self.min, max=self.max) - - -class GELU(nn.Module): - r"""Applies the Gaussian Error Linear Units function: - - .. math:: - \text{GELU}(x) = x * \Phi(x) - where :math:`\Phi(x)` is the Cumulative Distribution Function for - Gaussian Distribution. - - Shape: - - Input: :math:`(N, *)` where `*` means, any number of additional - dimensions - - Output: :math:`(N, *)`, same shape as the input - - .. image:: scripts/activation_images/GELU.png - - Examples:: - - >>> m = nn.GELU() - >>> input = torch.randn(2) - >>> output = m(input) - """ - - def forward(self, input): - return F.gelu(input) - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.4')): - ACTIVATION_LAYERS.register_module(module=GELU) -else: - ACTIVATION_LAYERS.register_module(module=nn.GELU) - - -def build_activation_layer(cfg): - """Build activation layer. - - Args: - cfg (dict): The activation layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an activation layer. - - Returns: - nn.Module: Created activation layer. - """ - return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/context_block.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/context_block.py deleted file mode 100755 index d60fdb904..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/context_block.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn - -from ..utils import constant_init, kaiming_init -from .registry import PLUGIN_LAYERS - - -def last_zero_init(m): - if isinstance(m, nn.Sequential): - constant_init(m[-1], val=0) - else: - constant_init(m, val=0) - - -@PLUGIN_LAYERS.register_module() -class ContextBlock(nn.Module): - """ContextBlock module in GCNet. - - See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' - (https://arxiv.org/abs/1904.11492) for details. - - Args: - in_channels (int): Channels of the input feature map. - ratio (float): Ratio of channels of transform bottleneck - pooling_type (str): Pooling method for context modeling. - Options are 'att' and 'avg', stand for attention pooling and - average pooling respectively. Default: 'att'. - fusion_types (Sequence[str]): Fusion method for feature fusion, - Options are 'channels_add', 'channel_mul', stand for channelwise - addition and multiplication respectively. Default: ('channel_add',) - """ - - _abbr_ = 'context_block' - - def __init__(self, - in_channels, - ratio, - pooling_type='att', - fusion_types=('channel_add', )): - super(ContextBlock, self).__init__() - assert pooling_type in ['avg', 'att'] - assert isinstance(fusion_types, (list, tuple)) - valid_fusion_types = ['channel_add', 'channel_mul'] - assert all([f in valid_fusion_types for f in fusion_types]) - assert len(fusion_types) > 0, 'at least one fusion should be used' - self.in_channels = in_channels - self.ratio = ratio - self.planes = int(in_channels * ratio) - self.pooling_type = pooling_type - self.fusion_types = fusion_types - if pooling_type == 'att': - self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) - self.softmax = nn.Softmax(dim=2) - else: - self.avg_pool = nn.AdaptiveAvgPool2d(1) - if 'channel_add' in fusion_types: - self.channel_add_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_add_conv = None - if 'channel_mul' in fusion_types: - self.channel_mul_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_mul_conv = None - self.reset_parameters() - - def reset_parameters(self): - if self.pooling_type == 'att': - kaiming_init(self.conv_mask, mode='fan_in') - self.conv_mask.inited = True - - if self.channel_add_conv is not None: - last_zero_init(self.channel_add_conv) - if self.channel_mul_conv is not None: - last_zero_init(self.channel_mul_conv) - - def spatial_pool(self, x): - batch, channel, height, width = x.size() - if self.pooling_type == 'att': - input_x = x - # [N, C, H * W] - input_x = input_x.view(batch, channel, height * width) - # [N, 1, C, H * W] - input_x = input_x.unsqueeze(1) - # [N, 1, H, W] - context_mask = self.conv_mask(x) - # [N, 1, H * W] - context_mask = context_mask.view(batch, 1, height * width) - # [N, 1, H * W] - context_mask = self.softmax(context_mask) - # [N, 1, H * W, 1] - context_mask = context_mask.unsqueeze(-1) - # [N, 1, C, 1] - context = torch.matmul(input_x, context_mask) - # [N, C, 1, 1] - context = context.view(batch, channel, 1, 1) - else: - # [N, C, 1, 1] - context = self.avg_pool(x) - - return context - - def forward(self, x): - # [N, C, 1, 1] - context = self.spatial_pool(x) - - out = x - if self.channel_mul_conv is not None: - # [N, C, 1, 1] - channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) - out = out * channel_mul_term - if self.channel_add_conv is not None: - # [N, C, 1, 1] - channel_add_term = self.channel_add_conv(context) - out = out + channel_add_term - - return out diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv.py deleted file mode 100755 index cf5449199..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from .registry import CONV_LAYERS - -CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) -CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) -CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) -CONV_LAYERS.register_module('Conv', module=nn.Conv2d) - - -def build_conv_layer(cfg, *args, **kwargs): - """Build convolution layer. - - Args: - cfg (None or dict): The conv layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an conv layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding conv layer. - - Returns: - nn.Module: Created conv layer. - """ - if cfg is None: - cfg_ = dict(type='Conv2d') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in CONV_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - else: - conv_layer = CONV_LAYERS.get(layer_type) - - layer = conv_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py deleted file mode 100755 index b45e758ac..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from torch import nn -from torch.nn import functional as F - -from .registry import CONV_LAYERS - - -@CONV_LAYERS.register_module() -class Conv2dAdaptivePadding(nn.Conv2d): - """Implementation of 2D convolution in tensorflow with `padding` as "same", - which applies padding to input (if needed) so that input image gets fully - covered by filter and stride you specified. For stride 1, this will ensure - that output image size is same as input. For stride of 2, output dimensions - will be half, for example. - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the convolving kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If ``True``, adds a learnable bias to the - output. Default: ``True`` - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__(in_channels, out_channels, kernel_size, stride, 0, - dilation, groups, bias) - - def forward(self, x): - img_h, img_w = x.size()[-2:] - kernel_h, kernel_w = self.weight.size()[-2:] - stride_h, stride_w = self.stride - output_h = math.ceil(img_h / stride_h) - output_w = math.ceil(img_w / stride_w) - pad_h = ( - max((output_h - 1) * self.stride[0] + - (kernel_h - 1) * self.dilation[0] + 1 - img_h, 0)) - pad_w = ( - max((output_w - 1) * self.stride[1] + - (kernel_w - 1) * self.dilation[1] + 1 - img_w, 0)) - if pad_h > 0 or pad_w > 0: - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2 - ]) - return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv_module.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv_module.py deleted file mode 100755 index 4f19f1d0c..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv_module.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from mmcv.utils import _BatchNorm, _InstanceNorm -from ..utils import constant_init, kaiming_init -from .activation import build_activation_layer -from .conv import build_conv_layer -from .norm import build_norm_layer -from .padding import build_padding_layer -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class ConvModule(nn.Module): - """A conv block that bundles conv/norm/activation layers. - - This block simplifies the usage of convolution layers, which are commonly - used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). - It is based upon three build methods: `build_conv_layer()`, - `build_norm_layer()` and `build_activation_layer()`. - - Besides, we add some additional features in this module. - 1. Automatically set `bias` of the conv layer. - 2. Spectral norm is supported. - 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only - supports zero and circular padding, and we add "reflect" padding mode. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. - groups (int): Number of blocked connections from input channels to - output channels. Same as that in ``nn._ConvNd``. - bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - inplace (bool): Whether to use inplace mode for activation. - Default: True. - with_spectral_norm (bool): Whether use spectral norm in conv module. - Default: False. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in PyTorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Common examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - Default: ('conv', 'norm', 'act'). - """ - - _abbr_ = 'conv_block' - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias='auto', - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - padding_mode='zeros', - order=('conv', 'norm', 'act')): - super(ConvModule, self).__init__() - assert conv_cfg is None or isinstance(conv_cfg, dict) - assert norm_cfg is None or isinstance(norm_cfg, dict) - assert act_cfg is None or isinstance(act_cfg, dict) - official_padding_mode = ['zeros', 'circular'] - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.with_explicit_padding = padding_mode not in official_padding_mode - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 3 - assert set(order) == set(['conv', 'norm', 'act']) - - self.with_norm = norm_cfg is not None - self.with_activation = act_cfg is not None - # if the conv layer is before a norm layer, bias is unnecessary. - if bias == 'auto': - bias = not self.with_norm - self.with_bias = bias - - if self.with_explicit_padding: - pad_cfg = dict(type=padding_mode) - self.padding_layer = build_padding_layer(pad_cfg, padding) - - # reset padding to 0 for conv module - conv_padding = 0 if self.with_explicit_padding else padding - # build convolution layer - self.conv = build_conv_layer( - conv_cfg, - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=conv_padding, - dilation=dilation, - groups=groups, - bias=bias) - # export the attributes of self.conv to a higher level for convenience - self.in_channels = self.conv.in_channels - self.out_channels = self.conv.out_channels - self.kernel_size = self.conv.kernel_size - self.stride = self.conv.stride - self.padding = padding - self.dilation = self.conv.dilation - self.transposed = self.conv.transposed - self.output_padding = self.conv.output_padding - self.groups = self.conv.groups - - if self.with_spectral_norm: - self.conv = nn.utils.spectral_norm(self.conv) - - # build normalization layers - if self.with_norm: - # norm layer is after conv layer - if order.index('norm') > order.index('conv'): - norm_channels = out_channels - else: - norm_channels = in_channels - self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) - self.add_module(self.norm_name, norm) - if self.with_bias: - if isinstance(norm, (_BatchNorm, _InstanceNorm)): - warnings.warn( - 'Unnecessary conv bias before batch/instance norm') - else: - self.norm_name = None - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - # nn.Tanh has no 'inplace' argument - if act_cfg_['type'] not in [ - 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish' - ]: - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - @property - def norm(self): - if self.norm_name: - return getattr(self, self.norm_name) - else: - return None - - def init_weights(self): - # 1. It is mainly for customized conv layers with their own - # initialization manners by calling their own ``init_weights()``, - # and we do not want ConvModule to override the initialization. - # 2. For customized conv layers without their own initialization - # manners (that is, they don't have their own ``init_weights()``) - # and PyTorch's conv layers, they will be initialized by - # this method with default ``kaiming_init``. - # Note: For PyTorch's conv layers, they will be overwritten by our - # initialization implementation using default ``kaiming_init``. - if not hasattr(self.conv, 'init_weights'): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) - if self.with_norm: - constant_init(self.norm, 1, bias=0) - - def forward(self, x, activate=True, norm=True): - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - x = self.conv(x) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv_ws.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv_ws.py deleted file mode 100755 index a3941e278..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/conv_ws.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from .registry import CONV_LAYERS - - -def conv_ws_2d(input, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - eps=1e-5): - c_in = weight.size(0) - weight_flat = weight.view(c_in, -1) - mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1) - std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1) - weight = (weight - mean) / (std + eps) - return F.conv2d(input, weight, bias, stride, padding, dilation, groups) - - -@CONV_LAYERS.register_module('ConvWS') -class ConvWS2d(nn.Conv2d): - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True, - eps=1e-5): - super(ConvWS2d, self).__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.eps = eps - - def forward(self, x): - return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups, self.eps) - - -@CONV_LAYERS.register_module(name='ConvAWS') -class ConvAWS2d(nn.Conv2d): - """AWS (Adaptive Weight Standardization) - - This is a variant of Weight Standardization - (https://arxiv.org/pdf/1903.10520.pdf) - It is used in DetectoRS to avoid NaN - (https://arxiv.org/pdf/2006.02334.pdf) - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the conv kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If set True, adds a learnable bias to the - output. Default: True - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.register_buffer('weight_gamma', - torch.ones(self.out_channels, 1, 1, 1)) - self.register_buffer('weight_beta', - torch.zeros(self.out_channels, 1, 1, 1)) - - def _get_weight(self, weight): - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - weight = (weight - mean) / std - weight = self.weight_gamma * weight + self.weight_beta - return weight - - def forward(self, x): - weight = self._get_weight(self.weight) - return F.conv2d(x, weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - """Override default load function. - - AWS overrides the function _load_from_state_dict to recover - weight_gamma and weight_beta if they are missing. If weight_gamma and - weight_beta are found in the checkpoint, this function will return - after super()._load_from_state_dict. Otherwise, it will compute the - mean and std of the pretrained weights and store them in weight_beta - and weight_gamma. - """ - - self.weight_gamma.data.fill_(-1) - local_missing_keys = [] - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, local_missing_keys, - unexpected_keys, error_msgs) - if self.weight_gamma.data.mean() > 0: - for k in local_missing_keys: - missing_keys.append(k) - return - weight = self.weight.data - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - self.weight_beta.data.copy_(mean) - self.weight_gamma.data.copy_(std) - missing_gamma_beta = [ - k for k in local_missing_keys - if k.endswith('weight_gamma') or k.endswith('weight_beta') - ] - for k in missing_gamma_beta: - local_missing_keys.remove(k) - for k in local_missing_keys: - missing_keys.append(k) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py deleted file mode 100755 index 722d5d8d7..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .conv_module import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if `norm_cfg` and `act_cfg` are specified. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. Default: 1. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. Default: 0. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super(DepthwiseSeparableConvModule, self).__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/drop.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/drop.py deleted file mode 100755 index b0a026654..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/drop.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from mmcv import build_from_cfg -from .registry import DROPOUT_LAYERS - - -def drop_path(x, drop_prob=0., training=False): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - """ - if drop_prob == 0. or not training: - return x - keep_prob = 1 - drop_prob - # handle tensors with different dimensions, not just 4D tensors. - shape = (x.shape[0], ) + (1, ) * (x.ndim - 1) - random_tensor = keep_prob + torch.rand( - shape, dtype=x.dtype, device=x.device) - output = x.div(keep_prob) * random_tensor.floor() - return output - - -@DROPOUT_LAYERS.register_module() -class DropPath(nn.Module): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - - Args: - drop_prob (float): Probability of the path to be zeroed. Default: 0.1 - """ - - def __init__(self, drop_prob=0.1): - super(DropPath, self).__init__() - self.drop_prob = drop_prob - - def forward(self, x): - return drop_path(x, self.drop_prob, self.training) - - -@DROPOUT_LAYERS.register_module() -class Dropout(nn.Dropout): - """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of - ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with - ``DropPath`` - - Args: - drop_prob (float): Probability of the elements to be - zeroed. Default: 0.5. - inplace (bool): Do the operation inplace or not. Default: False. - """ - - def __init__(self, drop_prob=0.5, inplace=False): - super().__init__(p=drop_prob, inplace=inplace) - - -def build_dropout(cfg, default_args=None): - """Builder for drop out layers.""" - return build_from_cfg(cfg, DROPOUT_LAYERS, default_args) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/generalized_attention.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/generalized_attention.py deleted file mode 100755 index 988d9adf2..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/generalized_attention.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import kaiming_init -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class GeneralizedAttention(nn.Module): - """GeneralizedAttention module. - - See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' - (https://arxiv.org/abs/1711.07971) for details. - - Args: - in_channels (int): Channels of the input feature map. - spatial_range (int): The spatial range. -1 indicates no spatial range - constraint. Default: -1. - num_heads (int): The head number of empirical_attention module. - Default: 9. - position_embedding_dim (int): The position embedding dimension. - Default: -1. - position_magnitude (int): A multiplier acting on coord difference. - Default: 1. - kv_stride (int): The feature stride acting on key/value feature map. - Default: 2. - q_stride (int): The feature stride acting on query feature map. - Default: 1. - attention_type (str): A binary indicator string for indicating which - items in generalized empirical_attention module are used. - Default: '1111'. - - - '1000' indicates 'query and key content' (appr - appr) item, - - '0100' indicates 'query content and relative position' - (appr - position) item, - - '0010' indicates 'key content only' (bias - appr) item, - - '0001' indicates 'relative position only' (bias - position) item. - """ - - _abbr_ = 'gen_attention_block' - - def __init__(self, - in_channels, - spatial_range=-1, - num_heads=9, - position_embedding_dim=-1, - position_magnitude=1, - kv_stride=2, - q_stride=1, - attention_type='1111'): - - super(GeneralizedAttention, self).__init__() - - # hard range means local range for non-local operation - self.position_embedding_dim = ( - position_embedding_dim - if position_embedding_dim > 0 else in_channels) - - self.position_magnitude = position_magnitude - self.num_heads = num_heads - self.in_channels = in_channels - self.spatial_range = spatial_range - self.kv_stride = kv_stride - self.q_stride = q_stride - self.attention_type = [bool(int(_)) for _ in attention_type] - self.qk_embed_dim = in_channels // num_heads - out_c = self.qk_embed_dim * num_heads - - if self.attention_type[0] or self.attention_type[1]: - self.query_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.query_conv.kaiming_init = True - - if self.attention_type[0] or self.attention_type[2]: - self.key_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.key_conv.kaiming_init = True - - self.v_dim = in_channels // num_heads - self.value_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=self.v_dim * num_heads, - kernel_size=1, - bias=False) - self.value_conv.kaiming_init = True - - if self.attention_type[1] or self.attention_type[3]: - self.appr_geom_fc_x = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_x.kaiming_init = True - - self.appr_geom_fc_y = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_y.kaiming_init = True - - if self.attention_type[2]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.appr_bias = nn.Parameter(appr_bias_value) - - if self.attention_type[3]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.geom_bias = nn.Parameter(geom_bias_value) - - self.proj_conv = nn.Conv2d( - in_channels=self.v_dim * num_heads, - out_channels=in_channels, - kernel_size=1, - bias=True) - self.proj_conv.kaiming_init = True - self.gamma = nn.Parameter(torch.zeros(1)) - - if self.spatial_range >= 0: - # only works when non local is after 3*3 conv - if in_channels == 256: - max_len = 84 - elif in_channels == 512: - max_len = 42 - - max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) - local_constraint_map = np.ones( - (max_len, max_len, max_len_kv, max_len_kv), dtype=np.int) - for iy in range(max_len): - for ix in range(max_len): - local_constraint_map[ - iy, ix, - max((iy - self.spatial_range) // - self.kv_stride, 0):min((iy + self.spatial_range + - 1) // self.kv_stride + - 1, max_len), - max((ix - self.spatial_range) // - self.kv_stride, 0):min((ix + self.spatial_range + - 1) // self.kv_stride + - 1, max_len)] = 0 - - self.local_constraint_map = nn.Parameter( - torch.from_numpy(local_constraint_map).byte(), - requires_grad=False) - - if self.q_stride > 1: - self.q_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.q_stride) - else: - self.q_downsample = None - - if self.kv_stride > 1: - self.kv_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.kv_stride) - else: - self.kv_downsample = None - - self.init_weights() - - def get_position_embedding(self, - h, - w, - h_kv, - w_kv, - q_stride, - kv_stride, - device, - dtype, - feat_dim, - wave_length=1000): - # the default type of Tensor is float32, leading to type mismatch - # in fp16 mode. Cast it to support fp16 mode. - h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) - h_idxs = h_idxs.view((h, 1)) * q_stride - - w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) - w_idxs = w_idxs.view((w, 1)) * q_stride - - h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( - device=device, dtype=dtype) - h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride - - w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( - device=device, dtype=dtype) - w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride - - # (h, h_kv, 1) - h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) - h_diff *= self.position_magnitude - - # (w, w_kv, 1) - w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) - w_diff *= self.position_magnitude - - feat_range = torch.arange(0, feat_dim / 4).to( - device=device, dtype=dtype) - - dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) - dim_mat = dim_mat**((4. / feat_dim) * feat_range) - dim_mat = dim_mat.view((1, 1, -1)) - - embedding_x = torch.cat( - ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) - - embedding_y = torch.cat( - ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) - - return embedding_x, embedding_y - - def forward(self, x_input): - num_heads = self.num_heads - - # use empirical_attention - if self.q_downsample is not None: - x_q = self.q_downsample(x_input) - else: - x_q = x_input - n, _, h, w = x_q.shape - - if self.kv_downsample is not None: - x_kv = self.kv_downsample(x_input) - else: - x_kv = x_input - _, _, h_kv, w_kv = x_kv.shape - - if self.attention_type[0] or self.attention_type[1]: - proj_query = self.query_conv(x_q).view( - (n, num_heads, self.qk_embed_dim, h * w)) - proj_query = proj_query.permute(0, 1, 3, 2) - - if self.attention_type[0] or self.attention_type[2]: - proj_key = self.key_conv(x_kv).view( - (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) - - if self.attention_type[1] or self.attention_type[3]: - position_embed_x, position_embed_y = self.get_position_embedding( - h, w, h_kv, w_kv, self.q_stride, self.kv_stride, - x_input.device, x_input.dtype, self.position_embedding_dim) - # (n, num_heads, w, w_kv, dim) - position_feat_x = self.appr_geom_fc_x(position_embed_x).\ - view(1, w, w_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - # (n, num_heads, h, h_kv, dim) - position_feat_y = self.appr_geom_fc_y(position_embed_y).\ - view(1, h, h_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - position_feat_x /= math.sqrt(2) - position_feat_y /= math.sqrt(2) - - # accelerate for saliency only - if (np.sum(self.attention_type) == 1) and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy = torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, h_kv * w_kv) - - h = 1 - w = 1 - else: - # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for - if not self.attention_type[0]: - energy = torch.zeros( - n, - num_heads, - h, - w, - h_kv, - w_kv, - dtype=x_input.dtype, - device=x_input.device) - - # attention_type[0]: appr - appr - # attention_type[1]: appr - position - # attention_type[2]: bias - appr - # attention_type[3]: bias - position - if self.attention_type[0] or self.attention_type[2]: - if self.attention_type[0] and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - energy = torch.matmul(proj_query + appr_bias, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[0]: - energy = torch.matmul(proj_query, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy += torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, 1, h_kv, w_kv) - - if self.attention_type[1] or self.attention_type[3]: - if self.attention_type[1] and self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - - proj_query_reshape = (proj_query + geom_bias).\ - view(n, num_heads, h, w, self.qk_embed_dim) - - energy_x = torch.matmul( - proj_query_reshape.permute(0, 1, 3, 2, 4), - position_feat_x.permute(0, 1, 2, 4, 3)) - energy_x = energy_x.\ - permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul( - proj_query_reshape, - position_feat_y.permute(0, 1, 2, 4, 3)) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[1]: - proj_query_reshape = proj_query.\ - view(n, num_heads, h, w, self.qk_embed_dim) - proj_query_reshape = proj_query_reshape.\ - permute(0, 1, 3, 2, 4) - position_feat_x_reshape = position_feat_x.\ - permute(0, 1, 2, 4, 3) - position_feat_y_reshape = position_feat_y.\ - permute(0, 1, 2, 4, 3) - - energy_x = torch.matmul(proj_query_reshape, - position_feat_x_reshape) - energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul(proj_query_reshape, - position_feat_y_reshape) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, self.qk_embed_dim, 1).\ - repeat(n, 1, 1, 1) - - position_feat_x_reshape = position_feat_x.\ - view(n, num_heads, w*w_kv, self.qk_embed_dim) - - position_feat_y_reshape = position_feat_y.\ - view(n, num_heads, h * h_kv, self.qk_embed_dim) - - energy_x = torch.matmul(position_feat_x_reshape, geom_bias) - energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) - - energy_y = torch.matmul(position_feat_y_reshape, geom_bias) - energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) - - energy += energy_x + energy_y - - energy = energy.view(n, num_heads, h * w, h_kv * w_kv) - - if self.spatial_range >= 0: - cur_local_constraint_map = \ - self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ - contiguous().\ - view(1, 1, h*w, h_kv*w_kv) - - energy = energy.masked_fill_(cur_local_constraint_map, - float('-inf')) - - attention = F.softmax(energy, 3) - - proj_value = self.value_conv(x_kv) - proj_value_reshape = proj_value.\ - view((n, num_heads, self.v_dim, h_kv * w_kv)).\ - permute(0, 1, 3, 2) - - out = torch.matmul(attention, proj_value_reshape).\ - permute(0, 1, 3, 2).\ - contiguous().\ - view(n, self.v_dim * self.num_heads, h, w) - - out = self.proj_conv(out) - - # output is downsampled, upsample back to input size - if self.q_downsample is not None: - out = F.interpolate( - out, - size=x_input.shape[2:], - mode='bilinear', - align_corners=False) - - out = self.gamma * out + x_input - return out - - def init_weights(self): - for m in self.modules(): - if hasattr(m, 'kaiming_init') and m.kaiming_init: - kaiming_init( - m, - mode='fan_in', - nonlinearity='leaky_relu', - bias=0, - distribution='uniform', - a=1) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/hsigmoid.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/hsigmoid.py deleted file mode 100755 index 30b1a3d65..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/hsigmoid.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSigmoid(nn.Module): - """Hard Sigmoid Module. Apply the hard sigmoid function: - Hsigmoid(x) = min(max((x + bias) / divisor, min_value), max_value) - Default: Hsigmoid(x) = min(max((x + 1) / 2, 0), 1) - - Args: - bias (float): Bias of the input feature map. Default: 1.0. - divisor (float): Divisor of the input feature map. Default: 2.0. - min_value (float): Lower bound value. Default: 0.0. - max_value (float): Upper bound value. Default: 1.0. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, bias=1.0, divisor=2.0, min_value=0.0, max_value=1.0): - super(HSigmoid, self).__init__() - self.bias = bias - self.divisor = divisor - assert self.divisor != 0 - self.min_value = min_value - self.max_value = max_value - - def forward(self, x): - x = (x + self.bias) / self.divisor - - return x.clamp_(self.min_value, self.max_value) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/hswish.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/hswish.py deleted file mode 100755 index 7e0c090ff..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/hswish.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSwish(nn.Module): - """Hard Swish Module. - - This module applies the hard swish function: - - .. math:: - Hswish(x) = x * ReLU6(x + 3) / 6 - - Args: - inplace (bool): can optionally do the operation in-place. - Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.act = nn.ReLU6(inplace) - - def forward(self, x): - return x * self.act(x + 3) / 6 diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/non_local.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/non_local.py deleted file mode 100755 index 92d00155e..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/non_local.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta - -import torch -import torch.nn as nn - -from ..utils import constant_init, normal_init -from .conv_module import ConvModule -from .registry import PLUGIN_LAYERS - - -class _NonLocalNd(nn.Module, metaclass=ABCMeta): - """Basic Non-local module. - - This module is proposed in - "Non-local Neural Networks" - Paper reference: https://arxiv.org/abs/1711.07971 - Code reference: https://github.com/AlexHex7/Non-local_pytorch - - Args: - in_channels (int): Channels of the input feature map. - reduction (int): Channel reduction ratio. Default: 2. - use_scale (bool): Whether to scale pairwise_weight by - `1/sqrt(inter_channels)` when the mode is `embedded_gaussian`. - Default: True. - conv_cfg (None | dict): The config dict for convolution layers. - If not specified, it will use `nn.Conv2d` for convolution layers. - Default: None. - norm_cfg (None | dict): The config dict for normalization layers. - Default: None. (This parameter is only applicable to conv_out.) - mode (str): Options are `gaussian`, `concatenation`, - `embedded_gaussian` and `dot_product`. Default: embedded_gaussian. - """ - - def __init__(self, - in_channels, - reduction=2, - use_scale=True, - conv_cfg=None, - norm_cfg=None, - mode='embedded_gaussian', - **kwargs): - super(_NonLocalNd, self).__init__() - self.in_channels = in_channels - self.reduction = reduction - self.use_scale = use_scale - self.inter_channels = max(in_channels // reduction, 1) - self.mode = mode - - if mode not in [ - 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation' - ]: - raise ValueError("Mode should be in 'gaussian', 'concatenation', " - f"'embedded_gaussian' or 'dot_product', but got " - f'{mode} instead.') - - # g, theta, phi are defaulted as `nn.ConvNd`. - # Here we use ConvModule for potential usage. - self.g = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.conv_out = ConvModule( - self.inter_channels, - self.in_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - if self.mode != 'gaussian': - self.theta = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.phi = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - - if self.mode == 'concatenation': - self.concat_project = ConvModule( - self.inter_channels * 2, - 1, - kernel_size=1, - stride=1, - padding=0, - bias=False, - act_cfg=dict(type='ReLU')) - - self.init_weights(**kwargs) - - def init_weights(self, std=0.01, zeros_init=True): - if self.mode != 'gaussian': - for m in [self.g, self.theta, self.phi]: - normal_init(m.conv, std=std) - else: - normal_init(self.g.conv, std=std) - if zeros_init: - if self.conv_out.norm_cfg is None: - constant_init(self.conv_out.conv, 0) - else: - constant_init(self.conv_out.norm, 0) - else: - if self.conv_out.norm_cfg is None: - normal_init(self.conv_out.conv, std=std) - else: - normal_init(self.conv_out.norm, std=std) - - def gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def embedded_gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - if self.use_scale: - # theta_x.shape[-1] is `self.inter_channels` - pairwise_weight /= theta_x.shape[-1]**0.5 - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def dot_product(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight /= pairwise_weight.shape[-1] - return pairwise_weight - - def concatenation(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - h = theta_x.size(2) - w = phi_x.size(3) - theta_x = theta_x.repeat(1, 1, 1, w) - phi_x = phi_x.repeat(1, 1, h, 1) - - concat_feature = torch.cat([theta_x, phi_x], dim=1) - pairwise_weight = self.concat_project(concat_feature) - n, _, h, w = pairwise_weight.size() - pairwise_weight = pairwise_weight.view(n, h, w) - pairwise_weight /= pairwise_weight.shape[-1] - - return pairwise_weight - - def forward(self, x): - # Assume `reduction = 1`, then `inter_channels = C` - # or `inter_channels = C` when `mode="gaussian"` - - # NonLocal1d x: [N, C, H] - # NonLocal2d x: [N, C, H, W] - # NonLocal3d x: [N, C, T, H, W] - n = x.size(0) - - # NonLocal1d g_x: [N, H, C] - # NonLocal2d g_x: [N, HxW, C] - # NonLocal3d g_x: [N, TxHxW, C] - g_x = self.g(x).view(n, self.inter_channels, -1) - g_x = g_x.permute(0, 2, 1) - - # NonLocal1d theta_x: [N, H, C], phi_x: [N, C, H] - # NonLocal2d theta_x: [N, HxW, C], phi_x: [N, C, HxW] - # NonLocal3d theta_x: [N, TxHxW, C], phi_x: [N, C, TxHxW] - if self.mode == 'gaussian': - theta_x = x.view(n, self.in_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - if self.sub_sample: - phi_x = self.phi(x).view(n, self.in_channels, -1) - else: - phi_x = x.view(n, self.in_channels, -1) - elif self.mode == 'concatenation': - theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) - phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) - else: - theta_x = self.theta(x).view(n, self.inter_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - phi_x = self.phi(x).view(n, self.inter_channels, -1) - - pairwise_func = getattr(self, self.mode) - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = pairwise_func(theta_x, phi_x) - - # NonLocal1d y: [N, H, C] - # NonLocal2d y: [N, HxW, C] - # NonLocal3d y: [N, TxHxW, C] - y = torch.matmul(pairwise_weight, g_x) - # NonLocal1d y: [N, C, H] - # NonLocal2d y: [N, C, H, W] - # NonLocal3d y: [N, C, T, H, W] - y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, - *x.size()[2:]) - - output = x + self.conv_out(y) - - return output - - -class NonLocal1d(_NonLocalNd): - """1D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv1d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv1d'), - **kwargs): - super(NonLocal1d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool1d(kernel_size=2) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -@PLUGIN_LAYERS.register_module() -class NonLocal2d(_NonLocalNd): - """2D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv2d'). - """ - - _abbr_ = 'nonlocal_block' - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv2d'), - **kwargs): - super(NonLocal2d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -class NonLocal3d(_NonLocalNd): - """3D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv3d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv3d'), - **kwargs): - super(NonLocal3d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/norm.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/norm.py deleted file mode 100755 index cfb326bdb..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/norm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import torch.nn as nn - -from mmcv.utils import is_tuple_of -from mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm -from .registry import NORM_LAYERS - -NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) -NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) -NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) -NORM_LAYERS.register_module('GN', module=nn.GroupNorm) -NORM_LAYERS.register_module('LN', module=nn.LayerNorm) -NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) -NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - When we build a norm layer with `build_norm_layer()`, we want to preserve - the norm type in variable names, e.g, self.bn1, self.gn. This method will - infer the abbreviation to map class types to abbreviations. - - Rule 1: If the class has the property "_abbr_", return the property. - Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or - InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and - "in" respectively. - Rule 3: If the class name contains "batch", "group", "layer" or "instance", - the abbreviation of this layer will be "bn", "gn", "ln" and "in" - respectively. - Rule 4: Otherwise, the abbreviation falls back to "norm". - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN - return 'in' - elif issubclass(class_type, _BatchNorm): - return 'bn' - elif issubclass(class_type, nn.GroupNorm): - return 'gn' - elif issubclass(class_type, nn.LayerNorm): - return 'ln' - else: - class_name = class_type.__name__.lower() - if 'batch' in class_name: - return 'bn' - elif 'group' in class_name: - return 'gn' - elif 'layer' in class_name: - return 'ln' - elif 'instance' in class_name: - return 'in' - else: - return 'norm_layer' - - -def build_norm_layer(cfg, num_features, postfix=''): - """Build normalization layer. - - Args: - cfg (dict): The norm layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate a norm layer. - - requires_grad (bool, optional): Whether stop gradient updates. - num_features (int): Number of input channels. - postfix (int | str): The postfix to be appended into norm abbreviation - to create named layer. - - Returns: - (str, nn.Module): The first element is the layer name consisting of - abbreviation and postfix, e.g., bn1, gn. The second element is the - created norm layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in NORM_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - - norm_layer = NORM_LAYERS.get(layer_type) - abbr = infer_abbr(norm_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - requires_grad = cfg_.pop('requires_grad', True) - cfg_.setdefault('eps', 1e-5) - if layer_type != 'GN': - layer = norm_layer(num_features, **cfg_) - if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): - layer._specify_ddp_gpu_num(1) - else: - assert 'num_groups' in cfg_ - layer = norm_layer(num_channels=num_features, **cfg_) - - for param in layer.parameters(): - param.requires_grad = requires_grad - - return name, layer - - -def is_norm(layer, exclude=None): - """Check if a layer is a normalization layer. - - Args: - layer (nn.Module): The layer to be checked. - exclude (type | tuple[type]): Types to be excluded. - - Returns: - bool: Whether the layer is a norm layer. - """ - if exclude is not None: - if not isinstance(exclude, tuple): - exclude = (exclude, ) - if not is_tuple_of(exclude, type): - raise TypeError( - f'"exclude" must be either None or type or a tuple of types, ' - f'but got {type(exclude)}: {exclude}') - - if exclude and isinstance(layer, exclude): - return False - - all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) - return isinstance(layer, all_norm_bases) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/padding.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/padding.py deleted file mode 100755 index e4ac6b28a..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/padding.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import PADDING_LAYERS - -PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) -PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) -PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) - - -def build_padding_layer(cfg, *args, **kwargs): - """Build padding layer. - - Args: - cfg (None or dict): The padding layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate a padding layer. - - Returns: - nn.Module: Created padding layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - - cfg_ = cfg.copy() - padding_type = cfg_.pop('type') - if padding_type not in PADDING_LAYERS: - raise KeyError(f'Unrecognized padding type {padding_type}.') - else: - padding_layer = PADDING_LAYERS.get(padding_type) - - layer = padding_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/plugin.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/plugin.py deleted file mode 100755 index 07c010d40..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/plugin.py +++ /dev/null @@ -1,88 +0,0 @@ -import inspect -import platform - -from .registry import PLUGIN_LAYERS - -if platform.system() == 'Windows': - import regex as re -else: - import re - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - This method will infer the abbreviation to map class types to - abbreviations. - - Rule 1: If the class has the property "abbr", return the property. - Rule 2: Otherwise, the abbreviation falls back to snake case of class - name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - - def camel2snack(word): - """Convert camel case word into snack case. - - Modified from `inflection lib - `_. - - Example:: - - >>> camel2snack("FancyBlock") - 'fancy_block' - """ - - word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) - word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) - word = word.replace('-', '_') - return word.lower() - - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - else: - return camel2snack(class_type.__name__) - - -def build_plugin_layer(cfg, postfix='', **kwargs): - """Build plugin layer. - - Args: - cfg (None or dict): cfg should contain: - type (str): identify plugin layer type. - layer args: args needed to instantiate a plugin layer. - postfix (int, str): appended into norm abbreviation to - create named layer. Default: ''. - - Returns: - tuple[str, nn.Module]: - name (str): abbreviation + postfix - layer (nn.Module): created plugin layer - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in PLUGIN_LAYERS: - raise KeyError(f'Unrecognized plugin type {layer_type}') - - plugin_layer = PLUGIN_LAYERS.get(layer_type) - abbr = infer_abbr(plugin_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - layer = plugin_layer(**kwargs, **cfg_) - - return name, layer diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/registry.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/registry.py deleted file mode 100755 index c29279776..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/registry.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -CONV_LAYERS = Registry('conv layer') -NORM_LAYERS = Registry('norm layer') -ACTIVATION_LAYERS = Registry('activation layer') -PADDING_LAYERS = Registry('padding layer') -UPSAMPLE_LAYERS = Registry('upsample layer') -PLUGIN_LAYERS = Registry('plugin layer') - -DROPOUT_LAYERS = Registry('drop out layers') -POSITIONAL_ENCODING = Registry('position encoding') -ATTENTION = Registry('attention') -FEEDFORWARD_NETWORK = Registry('feed-forward Network') -TRANSFORMER_LAYER = Registry('transformerLayer') -TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/scale.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/scale.py deleted file mode 100755 index c905fffcc..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/scale.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class Scale(nn.Module): - """A learnable scale parameter. - - This layer scales the input by a learnable factor. It multiplies a - learnable scale parameter of shape (1,) with input of any shape. - - Args: - scale (float): Initial value of scale factor. Default: 1.0 - """ - - def __init__(self, scale=1.0): - super(Scale, self).__init__() - self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) - - def forward(self, x): - return x * self.scale diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/swish.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/swish.py deleted file mode 100755 index e2ca8ed7b..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/swish.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class Swish(nn.Module): - """Swish Module. - - This module applies the swish function: - - .. math:: - Swish(x) = x * Sigmoid(x) - - Returns: - Tensor: The output tensor. - """ - - def __init__(self): - super(Swish, self).__init__() - - def forward(self, x): - return x * torch.sigmoid(x) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/transformer.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/transformer.py deleted file mode 100755 index ed32688af..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/transformer.py +++ /dev/null @@ -1,595 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings - -import torch -import torch.nn as nn - -from mmcv import ConfigDict, deprecated_api_warning -from mmcv.cnn import Linear, build_activation_layer, build_norm_layer -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential -from mmcv.utils import build_from_cfg -from .drop import build_dropout -from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, - TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) - -# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file -try: - from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention # noqa F401 - warnings.warn( - ImportWarning( - '``MultiScaleDeformableAttention`` has been moved to ' - '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 - '``from mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 - 'to ``from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 - )) - -except ImportError: - warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' - '``mmcv.ops.multi_scale_deform_attn``, ' - 'You should install ``mmcv-full`` if you need this module. ') - - -def build_positional_encoding(cfg, default_args=None): - """Builder for Position Encoding.""" - return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) - - -def build_attention(cfg, default_args=None): - """Builder for attention.""" - return build_from_cfg(cfg, ATTENTION, default_args) - - -def build_feedforward_network(cfg, default_args=None): - """Builder for feed-forward network (FFN).""" - return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) - - -def build_transformer_layer(cfg, default_args=None): - """Builder for transformer layer.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) - - -def build_transformer_layer_sequence(cfg, default_args=None): - """Builder for transformer encoder and transformer decoder.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) - - -@ATTENTION.register_module() -class MultiheadAttention(BaseModule): - """A wrapper for ``torch.nn.MultiheadAttention``. - - This module implements MultiheadAttention with identity connection, - and positional encoding is also passed as input. - - Args: - embed_dims (int): The embedding dimension. - num_heads (int): Parallel attention heads. - attn_drop (float): A Dropout layer on attn_output_weights. - Default: 0.0. - proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. - Default: 0.0. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): When it is True, Key, Query and Value are shape of - (batch, n, embed_dim), otherwise (n, batch, embed_dim). - Default to False. - """ - - def __init__(self, - embed_dims, - num_heads, - attn_drop=0., - proj_drop=0., - dropout_layer=dict(type='Dropout', drop_prob=0.), - init_cfg=None, - batch_first=False, - **kwargs): - super(MultiheadAttention, self).__init__(init_cfg) - if 'dropout' in kwargs: - warnings.warn('The arguments `dropout` in MultiheadAttention ' - 'has been deprecated, now you can separately ' - 'set `attn_drop`(float), proj_drop(float), ' - 'and `dropout_layer`(dict) ') - attn_drop = kwargs['dropout'] - dropout_layer['drop_prob'] = kwargs.pop('dropout') - - self.embed_dims = embed_dims - self.num_heads = num_heads - self.batch_first = batch_first - - self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, - **kwargs) - - self.proj_drop = nn.Dropout(proj_drop) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else nn.Identity() - - @deprecated_api_warning({'residual': 'identity'}, - cls_name='MultiheadAttention') - def forward(self, - query, - key=None, - value=None, - identity=None, - query_pos=None, - key_pos=None, - attn_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `MultiheadAttention`. - - **kwargs allow passing a more general data flow when combining - with other operations in `transformerlayer`. - - Args: - query (Tensor): The input query with shape [num_queries, bs, - embed_dims] if self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - If None, the ``query`` will be used. Defaults to None. - value (Tensor): The value tensor with same shape as `key`. - Same in `nn.MultiheadAttention.forward`. Defaults to None. - If None, the `key` will be used. - identity (Tensor): This tensor, with the same shape as x, - will be used for the identity link. - If None, `x` will be used. Defaults to None. - query_pos (Tensor): The positional encoding for query, with - the same shape as `x`. If not None, it will - be added to `x` before forward function. Defaults to None. - key_pos (Tensor): The positional encoding for `key`, with the - same shape as `key`. Defaults to None. If not None, it will - be added to `key` before forward function. If None, and - `query_pos` has the same shape as `key`, then `query_pos` - will be used for `key_pos`. Defaults to None. - attn_mask (Tensor): ByteTensor mask with shape [num_queries, - num_keys]. Same in `nn.MultiheadAttention.forward`. - Defaults to None. - key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. - Defaults to None. - - Returns: - Tensor: forwarded results with shape - [num_queries, bs, embed_dims] - if self.batch_first is False, else - [bs, num_queries embed_dims]. - """ - - if key is None: - key = query - if value is None: - value = key - if identity is None: - identity = query - if key_pos is None: - if query_pos is not None: - # use query_pos if key_pos is not available - if query_pos.shape == key.shape: - key_pos = query_pos - else: - warnings.warn(f'position encoding of key is' - f'missing in {self.__class__.__name__}.') - if query_pos is not None: - query = query + query_pos - if key_pos is not None: - key = key + key_pos - - # Because the dataflow('key', 'query', 'value') of - # ``torch.nn.MultiheadAttention`` is (num_query, batch, - # embed_dims), We should adjust the shape of dataflow from - # batch_first (batch, num_query, embed_dims) to num_query_first - # (num_query ,batch, embed_dims), and recover ``attn_output`` - # from num_query_first to batch_first. - if self.batch_first: - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - out = self.attn( - query=query, - key=key, - value=value, - attn_mask=attn_mask, - key_padding_mask=key_padding_mask)[0] - - if self.batch_first: - out = out.transpose(0, 1) - - return identity + self.dropout_layer(self.proj_drop(out)) - - -@FEEDFORWARD_NETWORK.register_module() -class FFN(BaseModule): - """Implements feed-forward networks (FFNs) with identity connection. - - Args: - embed_dims (int): The feature dimension. Same as - `MultiheadAttention`. Defaults: 256. - feedforward_channels (int): The hidden dimension of FFNs. - Defaults: 1024. - num_fcs (int, optional): The number of fully-connected layers in - FFNs. Default: 2. - act_cfg (dict, optional): The activation config for FFNs. - Default: dict(type='ReLU') - ffn_drop (float, optional): Probability of an element to be - zeroed in FFN. Default 0.0. - add_identity (bool, optional): Whether to add the - identity connection. Default: `True`. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - @deprecated_api_warning( - { - 'dropout': 'ffn_drop', - 'add_residual': 'add_identity' - }, - cls_name='FFN') - def __init__(self, - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - act_cfg=dict(type='ReLU', inplace=True), - ffn_drop=0., - dropout_layer=None, - add_identity=True, - init_cfg=None, - **kwargs): - super(FFN, self).__init__(init_cfg) - assert num_fcs >= 2, 'num_fcs should be no less ' \ - f'than 2. got {num_fcs}.' - self.embed_dims = embed_dims - self.feedforward_channels = feedforward_channels - self.num_fcs = num_fcs - self.act_cfg = act_cfg - self.activate = build_activation_layer(act_cfg) - - layers = [] - in_channels = embed_dims - for _ in range(num_fcs - 1): - layers.append( - Sequential( - Linear(in_channels, feedforward_channels), self.activate, - nn.Dropout(ffn_drop))) - in_channels = feedforward_channels - layers.append(Linear(feedforward_channels, embed_dims)) - layers.append(nn.Dropout(ffn_drop)) - self.layers = Sequential(*layers) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else torch.nn.Identity() - self.add_identity = add_identity - - @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') - def forward(self, x, identity=None): - """Forward function for `FFN`. - - The function would add x to the output tensor if residue is None. - """ - out = self.layers(x) - if not self.add_identity: - return self.dropout_layer(out) - if identity is None: - identity = x - return identity + self.dropout_layer(out) - - -@TRANSFORMER_LAYER.register_module() -class BaseTransformerLayer(BaseModule): - """Base `TransformerLayer` for vision transformer. - - It can be built from `mmcv.ConfigDict` and support more flexible - customization, for example, using any number of `FFN or LN ` and - use different kinds of `attention` by specifying a list of `ConfigDict` - named `attn_cfgs`. It is worth mentioning that it supports `prenorm` - when you specifying `norm` as the first element of `operation_order`. - More details about the `prenorm`: `On Layer Normalization in the - Transformer Architecture `_ . - - Args: - attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for `self_attention` or `cross_attention` modules, - The order of the configs in the list should be consistent with - corresponding attentions in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. Default: None. - ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for FFN, The order of the configs in the list should be - consistent with corresponding ffn in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. - operation_order (tuple[str]): The execution order of operation - in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). - Support `prenorm` when you specifying first element as `norm`. - Default:None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): Key, Query and Value are shape - of (batch, n, embed_dim) - or (n, batch, embed_dim). Default to False. - """ - - def __init__(self, - attn_cfgs=None, - ffn_cfgs=dict( - type='FFN', - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - ffn_drop=0., - act_cfg=dict(type='ReLU', inplace=True), - ), - operation_order=None, - norm_cfg=dict(type='LN'), - init_cfg=None, - batch_first=False, - **kwargs): - - deprecated_args = dict( - feedforward_channels='feedforward_channels', - ffn_dropout='ffn_drop', - ffn_num_fcs='num_fcs') - for ori_name, new_name in deprecated_args.items(): - if ori_name in kwargs: - warnings.warn( - f'The arguments `{ori_name}` in BaseTransformerLayer ' - f'has been deprecated, now you should set `{new_name}` ' - f'and other FFN related arguments ' - f'to a dict named `ffn_cfgs`. ') - ffn_cfgs[new_name] = kwargs[ori_name] - - super(BaseTransformerLayer, self).__init__(init_cfg) - - self.batch_first = batch_first - - assert set(operation_order) & set( - ['self_attn', 'norm', 'ffn', 'cross_attn']) == \ - set(operation_order), f'The operation_order of' \ - f' {self.__class__.__name__} should ' \ - f'contains all four operation type ' \ - f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" - - num_attn = operation_order.count('self_attn') + operation_order.count( - 'cross_attn') - if isinstance(attn_cfgs, dict): - attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] - else: - assert num_attn == len(attn_cfgs), f'The length ' \ - f'of attn_cfg {num_attn} is ' \ - f'not consistent with the number of attention' \ - f'in operation_order {operation_order}.' - - self.num_attn = num_attn - self.operation_order = operation_order - self.norm_cfg = norm_cfg - self.pre_norm = operation_order[0] == 'norm' - self.attentions = ModuleList() - - index = 0 - for operation_name in operation_order: - if operation_name in ['self_attn', 'cross_attn']: - if 'batch_first' in attn_cfgs[index]: - assert self.batch_first == attn_cfgs[index]['batch_first'] - else: - attn_cfgs[index]['batch_first'] = self.batch_first - attention = build_attention(attn_cfgs[index]) - # Some custom attentions used as `self_attn` - # or `cross_attn` can have different behavior. - attention.operation_name = operation_name - self.attentions.append(attention) - index += 1 - - self.embed_dims = self.attentions[0].embed_dims - - self.ffns = ModuleList() - num_ffns = operation_order.count('ffn') - if isinstance(ffn_cfgs, dict): - ffn_cfgs = ConfigDict(ffn_cfgs) - if isinstance(ffn_cfgs, dict): - ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] - assert len(ffn_cfgs) == num_ffns - for ffn_index in range(num_ffns): - if 'embed_dims' not in ffn_cfgs[ffn_index]: - ffn_cfgs['embed_dims'] = self.embed_dims - else: - assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims - self.ffns.append( - build_feedforward_network(ffn_cfgs[ffn_index], - dict(type='FFN'))) - - self.norms = ModuleList() - num_norms = operation_order.count('norm') - for _ in range(num_norms): - self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) - - def forward(self, - query, - key=None, - value=None, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerDecoderLayer`. - - **kwargs contains some specific arguments of attentions. - - Args: - query (Tensor): The input query with shape - [num_queries, bs, embed_dims] if - self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - value (Tensor): The value tensor with same shape as `key`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor] | None): 2D Tensor used in - calculation of corresponding attention. The length of - it should equal to the number of `attention` in - `operation_order`. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in `self_attn` layer. - Defaults to None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: forwarded results with shape [num_queries, bs, embed_dims]. - """ - - norm_index = 0 - attn_index = 0 - ffn_index = 0 - identity = query - if attn_masks is None: - attn_masks = [None for _ in range(self.num_attn)] - elif isinstance(attn_masks, torch.Tensor): - attn_masks = [ - copy.deepcopy(attn_masks) for _ in range(self.num_attn) - ] - warnings.warn(f'Use same attn_mask in all attentions in ' - f'{self.__class__.__name__} ') - else: - assert len(attn_masks) == self.num_attn, f'The length of ' \ - f'attn_masks {len(attn_masks)} must be equal ' \ - f'to the number of attention in ' \ - f'operation_order {self.num_attn}' - - for layer in self.operation_order: - if layer == 'self_attn': - temp_key = temp_value = query - query = self.attentions[attn_index]( - query, - temp_key, - temp_value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=query_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=query_key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'norm': - query = self.norms[norm_index](query) - norm_index += 1 - - elif layer == 'cross_attn': - query = self.attentions[attn_index]( - query, - key, - value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=key_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'ffn': - query = self.ffns[ffn_index]( - query, identity if self.pre_norm else None) - ffn_index += 1 - - return query - - -@TRANSFORMER_LAYER_SEQUENCE.register_module() -class TransformerLayerSequence(BaseModule): - """Base class for TransformerEncoder and TransformerDecoder in vision - transformer. - - As base-class of Encoder and Decoder in vision transformer. - Support customization such as specifying different kind - of `transformer_layer` in `transformer_coder`. - - Args: - transformerlayer (list[obj:`mmcv.ConfigDict`] | - obj:`mmcv.ConfigDict`): Config of transformerlayer - in TransformerCoder. If it is obj:`mmcv.ConfigDict`, - it would be repeated `num_layer` times to a - list[`mmcv.ConfigDict`]. Default: None. - num_layers (int): The number of `TransformerLayer`. Default: None. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): - super(TransformerLayerSequence, self).__init__(init_cfg) - if isinstance(transformerlayers, dict): - transformerlayers = [ - copy.deepcopy(transformerlayers) for _ in range(num_layers) - ] - else: - assert isinstance(transformerlayers, list) and \ - len(transformerlayers) == num_layers - self.num_layers = num_layers - self.layers = ModuleList() - for i in range(num_layers): - self.layers.append(build_transformer_layer(transformerlayers[i])) - self.embed_dims = self.layers[0].embed_dims - self.pre_norm = self.layers[0].pre_norm - - def forward(self, - query, - key, - value, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerCoder`. - - Args: - query (Tensor): Input query with shape - `(num_queries, bs, embed_dims)`. - key (Tensor): The key tensor with shape - `(num_keys, bs, embed_dims)`. - value (Tensor): The value tensor with shape - `(num_keys, bs, embed_dims)`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor], optional): Each element is 2D Tensor - which is used in calculation of corresponding attention in - operation_order. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in self-attention - Default: None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: results with shape [num_queries, bs, embed_dims]. - """ - for layer in self.layers: - query = layer( - query, - key, - value, - query_pos=query_pos, - key_pos=key_pos, - attn_masks=attn_masks, - query_key_padding_mask=query_key_padding_mask, - key_padding_mask=key_padding_mask, - **kwargs) - return query diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/upsample.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/upsample.py deleted file mode 100755 index a1a353767..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/upsample.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import xavier_init -from .registry import UPSAMPLE_LAYERS - -UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) -UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) - - -@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') -class PixelShufflePack(nn.Module): - """Pixel Shuffle upsample layer. - - This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to - achieve a simple upsampling with pixel shuffle. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of the conv layer to expand the - channels. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super(PixelShufflePack, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - xavier_init(self.upsample_conv, distribution='uniform') - - def forward(self, x): - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x - - -def build_upsample_layer(cfg, *args, **kwargs): - """Build upsample layer. - - Args: - cfg (dict): The upsample layer config, which should contain: - - - type (str): Layer type. - - scale_factor (int): Upsample ratio, which is not applicable to - deconv. - - layer args: Args needed to instantiate a upsample layer. - args (argument list): Arguments passed to the ``__init__`` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the - ``__init__`` method of the corresponding conv layer. - - Returns: - nn.Module: Created upsample layer. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - raise KeyError( - f'the cfg dict must contain the key "type", but got {cfg}') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in UPSAMPLE_LAYERS: - raise KeyError(f'Unrecognized upsample type {layer_type}') - else: - upsample = UPSAMPLE_LAYERS.get(layer_type) - - if upsample is nn.Upsample: - cfg_['mode'] = layer_type - layer = upsample(*args, **kwargs, **cfg_) - return layer diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/wrappers.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/wrappers.py deleted file mode 100755 index 8aebf67bf..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/bricks/wrappers.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 - -Wrap some nn modules to support empty tensor input. Currently, these wrappers -are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask -heads are trained on only positive RoIs. -""" -import math - -import torch -import torch.nn as nn -from torch.nn.modules.utils import _pair, _triple - -from .registry import CONV_LAYERS, UPSAMPLE_LAYERS - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def obsolete_torch_version(torch_version, version_threshold): - return torch_version == 'parrots' or torch_version <= version_threshold - - -class NewEmptyTensorOp(torch.autograd.Function): - - @staticmethod - def forward(ctx, x, new_shape): - ctx.shape = x.shape - return x.new_empty(new_shape) - - @staticmethod - def backward(ctx, grad): - shape = ctx.shape - return NewEmptyTensorOp.apply(grad, shape), None - - -@CONV_LAYERS.register_module('Conv', force=True) -class Conv2d(nn.Conv2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module('Conv3d', force=True) -class Conv3d(nn.Conv3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv') -@UPSAMPLE_LAYERS.register_module('deconv', force=True) -class ConvTranspose2d(nn.ConvTranspose2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv3d') -@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) -class ConvTranspose3d(nn.ConvTranspose3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -class MaxPool2d(nn.MaxPool2d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), - _pair(self.padding), _pair(self.stride), - _pair(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class MaxPool3d(nn.MaxPool3d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), - _triple(self.padding), - _triple(self.stride), - _triple(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class Linear(torch.nn.Linear): - - def forward(self, x): - # empty tensor forward of Linear layer is supported in Pytorch 1.6 - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): - out_shape = [x.shape[0], self.out_features] - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/builder.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/builder.py deleted file mode 100755 index 7567316c5..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/builder.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..runner import Sequential -from ..utils import Registry, build_from_cfg - - -def build_model_from_cfg(cfg, registry, default_args=None): - """Build a PyTorch model from config dict(s). Different from - ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. - - Args: - cfg (dict, list[dict]): The config of modules, is is either a config - dict or a list of config dicts. If cfg is a list, a - the built modules will be wrapped with ``nn.Sequential``. - registry (:obj:`Registry`): A registry the module belongs to. - default_args (dict, optional): Default arguments to build the module. - Defaults to None. - - Returns: - nn.Module: A built nn module. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return Sequential(*modules) - else: - return build_from_cfg(cfg, registry, default_args) - - -MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/resnet.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/resnet.py deleted file mode 100755 index 1cb3ac057..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/resnet.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn -import torch.utils.checkpoint as cp - -from .utils import constant_init, kaiming_init - - -def conv3x3(in_planes, out_planes, stride=1, dilation=1): - """3x3 convolution with padding.""" - return nn.Conv2d( - in_planes, - out_planes, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False): - super(BasicBlock, self).__init__() - assert style in ['pytorch', 'caffe'] - self.conv1 = conv3x3(inplanes, planes, stride, dilation) - self.bn1 = nn.BatchNorm2d(planes) - self.relu = nn.ReLU(inplace=True) - self.conv2 = conv3x3(planes, planes) - self.bn2 = nn.BatchNorm2d(planes) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - assert not with_cp - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Bottleneck(nn.Module): - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False): - """Bottleneck block. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if - it is "caffe", the stride-two layer is the first 1x1 conv layer. - """ - super(Bottleneck, self).__init__() - assert style in ['pytorch', 'caffe'] - if style == 'pytorch': - conv1_stride = 1 - conv2_stride = stride - else: - conv1_stride = stride - conv2_stride = 1 - self.conv1 = nn.Conv2d( - inplanes, planes, kernel_size=1, stride=conv1_stride, bias=False) - self.conv2 = nn.Conv2d( - planes, - planes, - kernel_size=3, - stride=conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.bn1 = nn.BatchNorm2d(planes) - self.bn2 = nn.BatchNorm2d(planes) - self.conv3 = nn.Conv2d( - planes, planes * self.expansion, kernel_size=1, bias=False) - self.bn3 = nn.BatchNorm2d(planes * self.expansion) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - def forward(self, x): - - def _inner_forward(x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -def make_res_layer(block, - inplanes, - planes, - blocks, - stride=1, - dilation=1, - style='pytorch', - with_cp=False): - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = nn.Sequential( - nn.Conv2d( - inplanes, - planes * block.expansion, - kernel_size=1, - stride=stride, - bias=False), - nn.BatchNorm2d(planes * block.expansion), - ) - - layers = [] - layers.append( - block( - inplanes, - planes, - stride, - dilation, - downsample, - style=style, - with_cp=with_cp)) - inplanes = planes * block.expansion - for _ in range(1, blocks): - layers.append( - block(inplanes, planes, 1, dilation, style=style, with_cp=with_cp)) - - return nn.Sequential(*layers) - - -class ResNet(nn.Module): - """ResNet backbone. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - num_stages (int): Resnet stages, normally 4. - strides (Sequence[int]): Strides of the first block of each stage. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze - running stats (mean and var). - bn_frozen (bool): Whether to freeze weight and bias of BN layers. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - frozen_stages=-1, - bn_eval=True, - bn_frozen=False, - with_cp=False): - super(ResNet, self).__init__() - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - assert num_stages >= 1 and num_stages <= 4 - block, stage_blocks = self.arch_settings[depth] - stage_blocks = stage_blocks[:num_stages] - assert len(strides) == len(dilations) == num_stages - assert max(out_indices) < num_stages - - self.out_indices = out_indices - self.style = style - self.frozen_stages = frozen_stages - self.bn_eval = bn_eval - self.bn_frozen = bn_frozen - self.with_cp = with_cp - - self.inplanes = 64 - self.conv1 = nn.Conv2d( - 3, 64, kernel_size=7, stride=2, padding=3, bias=False) - self.bn1 = nn.BatchNorm2d(64) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - self.res_layers = [] - for i, num_blocks in enumerate(stage_blocks): - stride = strides[i] - dilation = dilations[i] - planes = 64 * 2**i - res_layer = make_res_layer( - block, - self.inplanes, - planes, - num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - with_cp=with_cp) - self.inplanes = planes * block.expansion - layer_name = f'layer{i + 1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self.feat_dim = block.expansion * 64 * 2**(len(stage_blocks) - 1) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m) - elif isinstance(m, nn.BatchNorm2d): - constant_init(m, 1) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - if len(outs) == 1: - return outs[0] - else: - return tuple(outs) - - def train(self, mode=True): - super(ResNet, self).train(mode) - if self.bn_eval: - for m in self.modules(): - if isinstance(m, nn.BatchNorm2d): - m.eval() - if self.bn_frozen: - for params in m.parameters(): - params.requires_grad = False - if mode and self.frozen_stages >= 0: - for param in self.conv1.parameters(): - param.requires_grad = False - for param in self.bn1.parameters(): - param.requires_grad = False - self.bn1.eval() - self.bn1.weight.requires_grad = False - self.bn1.bias.requires_grad = False - for i in range(1, self.frozen_stages + 1): - mod = getattr(self, f'layer{i}') - mod.eval() - for param in mod.parameters(): - param.requires_grad = False diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/__init__.py deleted file mode 100755 index a263e31c1..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .flops_counter import get_model_complexity_info -from .fuse_conv_bn import fuse_conv_bn -from .sync_bn import revert_sync_batchnorm -from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, - KaimingInit, NormalInit, PretrainedInit, - TruncNormalInit, UniformInit, XavierInit, - bias_init_with_prob, caffe2_xavier_init, - constant_init, initialize, kaiming_init, normal_init, - trunc_normal_init, uniform_init, xavier_init) - -__all__ = [ - 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', - 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', - 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'revert_sync_batchnorm' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/flops_counter.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/flops_counter.py deleted file mode 100755 index dceeb398b..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/flops_counter.py +++ /dev/null @@ -1,599 +0,0 @@ -# Modified from flops-counter.pytorch by Vladislav Sovrasov -# original repo: https://github.com/sovrasov/flops-counter.pytorch - -# MIT License - -# Copyright (c) 2018 Vladislav Sovrasov - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -from functools import partial - -import numpy as np -import torch -import torch.nn as nn - -import mmcv - - -def get_model_complexity_info(model, - input_shape, - print_per_layer_stat=True, - as_strings=True, - input_constructor=None, - flush=False, - ost=sys.stdout): - """Get complexity information of a model. - - This method can calculate FLOPs and parameter counts of a model with - corresponding input shape. It can also print complexity information for - each layer in a model. - - Supported layers are listed as below: - - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. - - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, ``nn.LeakyReLU``, - ``nn.ReLU6``. - - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, - ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, - ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, - ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, - ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. - - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, - ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, - ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. - - Linear: ``nn.Linear``. - - Deconvolution: ``nn.ConvTranspose2d``. - - Upsample: ``nn.Upsample``. - - Args: - model (nn.Module): The model for complexity calculation. - input_shape (tuple): Input shape used for calculation. - print_per_layer_stat (bool): Whether to print complexity information - for each layer in a model. Default: True. - as_strings (bool): Output FLOPs and params counts in a string form. - Default: True. - input_constructor (None | callable): If specified, it takes a callable - method that generates input. otherwise, it will generate a random - tensor with input shape to calculate FLOPs. Default: None. - flush (bool): same as that in :func:`print`. Default: False. - ost (stream): same as ``file`` param in :func:`print`. - Default: sys.stdout. - - Returns: - tuple[float | str]: If ``as_strings`` is set to True, it will return - FLOPs and parameter counts in a string format. otherwise, it will - return those in a float number format. - """ - assert type(input_shape) is tuple - assert len(input_shape) >= 1 - assert isinstance(model, nn.Module) - flops_model = add_flops_counting_methods(model) - flops_model.eval() - flops_model.start_flops_count() - if input_constructor: - input = input_constructor(input_shape) - _ = flops_model(**input) - else: - try: - batch = torch.ones(()).new_empty( - (1, *input_shape), - dtype=next(flops_model.parameters()).dtype, - device=next(flops_model.parameters()).device) - except StopIteration: - # Avoid StopIteration for models which have no parameters, - # like `nn.Relu()`, `nn.AvgPool2d`, etc. - batch = torch.ones(()).new_empty((1, *input_shape)) - - _ = flops_model(batch) - - flops_count, params_count = flops_model.compute_average_flops_cost() - if print_per_layer_stat: - print_model_with_flops( - flops_model, flops_count, params_count, ost=ost, flush=flush) - flops_model.stop_flops_count() - - if as_strings: - return flops_to_string(flops_count), params_to_string(params_count) - - return flops_count, params_count - - -def flops_to_string(flops, units='GFLOPs', precision=2): - """Convert FLOPs number into a string. - - Note that Here we take a multiply-add counts as one FLOP. - - Args: - flops (float): FLOPs number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', - 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically - choose the most suitable unit for FLOPs. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted FLOPs number with units. - - Examples: - >>> flops_to_string(1e9) - '1.0 GFLOPs' - >>> flops_to_string(2e5, 'MFLOPs') - '0.2 MFLOPs' - >>> flops_to_string(3e-9, None) - '3e-09 FLOPs' - """ - if units is None: - if flops // 10**9 > 0: - return str(round(flops / 10.**9, precision)) + ' GFLOPs' - elif flops // 10**6 > 0: - return str(round(flops / 10.**6, precision)) + ' MFLOPs' - elif flops // 10**3 > 0: - return str(round(flops / 10.**3, precision)) + ' KFLOPs' - else: - return str(flops) + ' FLOPs' - else: - if units == 'GFLOPs': - return str(round(flops / 10.**9, precision)) + ' ' + units - elif units == 'MFLOPs': - return str(round(flops / 10.**6, precision)) + ' ' + units - elif units == 'KFLOPs': - return str(round(flops / 10.**3, precision)) + ' ' + units - else: - return str(flops) + ' FLOPs' - - -def params_to_string(num_params, units=None, precision=2): - """Convert parameter number into a string. - - Args: - num_params (float): Parameter number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'M', - 'K' and ''. If set to None, it will automatically choose the most - suitable unit for Parameter number. Default: None. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted parameter number with units. - - Examples: - >>> params_to_string(1e9) - '1000.0 M' - >>> params_to_string(2e5) - '200.0 k' - >>> params_to_string(3e-9) - '3e-09' - """ - if units is None: - if num_params // 10**6 > 0: - return str(round(num_params / 10**6, precision)) + ' M' - elif num_params // 10**3: - return str(round(num_params / 10**3, precision)) + ' k' - else: - return str(num_params) - else: - if units == 'M': - return str(round(num_params / 10.**6, precision)) + ' ' + units - elif units == 'K': - return str(round(num_params / 10.**3, precision)) + ' ' + units - else: - return str(num_params) - - -def print_model_with_flops(model, - total_flops, - total_params, - units='GFLOPs', - precision=3, - ost=sys.stdout, - flush=False): - """Print a model with FLOPs for each layer. - - Args: - model (nn.Module): The model to be printed. - total_flops (float): Total FLOPs of the model. - total_params (float): Total parameter counts of the model. - units (str | None): Converted FLOPs units. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 3. - ost (stream): same as `file` param in :func:`print`. - Default: sys.stdout. - flush (bool): same as that in :func:`print`. Default: False. - - Example: - >>> class ExampleModel(nn.Module): - - >>> def __init__(self): - >>> super().__init__() - >>> self.conv1 = nn.Conv2d(3, 8, 3) - >>> self.conv2 = nn.Conv2d(8, 256, 3) - >>> self.conv3 = nn.Conv2d(256, 8, 3) - >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) - >>> self.flatten = nn.Flatten() - >>> self.fc = nn.Linear(8, 1) - - >>> def forward(self, x): - >>> x = self.conv1(x) - >>> x = self.conv2(x) - >>> x = self.conv3(x) - >>> x = self.avg_pool(x) - >>> x = self.flatten(x) - >>> x = self.fc(x) - >>> return x - - >>> model = ExampleModel() - >>> x = (3, 16, 16) - to print the complexity information state for each layer, you can use - >>> get_model_complexity_info(model, x) - or directly use - >>> print_model_with_flops(model, 4579784.0, 37361) - ExampleModel( - 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, - (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 - (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) - (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) - (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) - (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) - (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) - ) - """ - - def accumulate_params(self): - if is_supported_instance(self): - return self.__params__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_params() - return sum - - def accumulate_flops(self): - if is_supported_instance(self): - return self.__flops__ / model.__batch_counter__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_flops() - return sum - - def flops_repr(self): - accumulated_num_params = self.accumulate_params() - accumulated_flops_cost = self.accumulate_flops() - return ', '.join([ - params_to_string( - accumulated_num_params, units='M', precision=precision), - '{:.3%} Params'.format(accumulated_num_params / total_params), - flops_to_string( - accumulated_flops_cost, units=units, precision=precision), - '{:.3%} FLOPs'.format(accumulated_flops_cost / total_flops), - self.original_extra_repr() - ]) - - def add_extra_repr(m): - m.accumulate_flops = accumulate_flops.__get__(m) - m.accumulate_params = accumulate_params.__get__(m) - flops_extra_repr = flops_repr.__get__(m) - if m.extra_repr != flops_extra_repr: - m.original_extra_repr = m.extra_repr - m.extra_repr = flops_extra_repr - assert m.extra_repr != m.original_extra_repr - - def del_extra_repr(m): - if hasattr(m, 'original_extra_repr'): - m.extra_repr = m.original_extra_repr - del m.original_extra_repr - if hasattr(m, 'accumulate_flops'): - del m.accumulate_flops - - model.apply(add_extra_repr) - print(model, file=ost, flush=flush) - model.apply(del_extra_repr) - - -def get_model_parameters_number(model): - """Calculate parameter number of a model. - - Args: - model (nn.module): The model for parameter number calculation. - - Returns: - float: Parameter number of the model. - """ - num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - return num_params - - -def add_flops_counting_methods(net_main_module): - # adding additional methods to the existing module object, - # this is done this way so that each function has access to self object - net_main_module.start_flops_count = start_flops_count.__get__( - net_main_module) - net_main_module.stop_flops_count = stop_flops_count.__get__( - net_main_module) - net_main_module.reset_flops_count = reset_flops_count.__get__( - net_main_module) - net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # noqa: E501 - net_main_module) - - net_main_module.reset_flops_count() - - return net_main_module - - -def compute_average_flops_cost(self): - """Compute average FLOPs cost. - - A method to compute average FLOPs cost, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - - Returns: - float: Current mean flops consumption per image. - """ - batches_count = self.__batch_counter__ - flops_sum = 0 - for module in self.modules(): - if is_supported_instance(module): - flops_sum += module.__flops__ - params_sum = get_model_parameters_number(self) - return flops_sum / batches_count, params_sum - - -def start_flops_count(self): - """Activate the computation of mean flops consumption per image. - - A method to activate the computation of mean flops consumption per image. - which will be available after ``add_flops_counting_methods()`` is called on - a desired net object. It should be called before running the network. - """ - add_batch_counter_hook_function(self) - - def add_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - return - - else: - handle = module.register_forward_hook( - get_modules_mapping()[type(module)]) - - module.__flops_handle__ = handle - - self.apply(partial(add_flops_counter_hook_function)) - - -def stop_flops_count(self): - """Stop computing the mean flops consumption per image. - - A method to stop computing the mean flops consumption per image, which will - be available after ``add_flops_counting_methods()`` is called on a desired - net object. It can be called to pause the computation whenever. - """ - remove_batch_counter_hook_function(self) - self.apply(remove_flops_counter_hook_function) - - -def reset_flops_count(self): - """Reset statistics computed so far. - - A method to Reset computed statistics, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - """ - add_batch_counter_variables_or_reset(self) - self.apply(add_flops_counter_variable_or_reset) - - -# ---- Internal functions -def empty_flops_counter_hook(module, input, output): - module.__flops__ += 0 - - -def upsample_flops_counter_hook(module, input, output): - output_size = output[0] - batch_size = output_size.shape[0] - output_elements_count = batch_size - for val in output_size.shape[1:]: - output_elements_count *= val - module.__flops__ += int(output_elements_count) - - -def relu_flops_counter_hook(module, input, output): - active_elements_count = output.numel() - module.__flops__ += int(active_elements_count) - - -def linear_flops_counter_hook(module, input, output): - input = input[0] - output_last_dim = output.shape[ - -1] # pytorch checks dimensions, so here we don't care much - module.__flops__ += int(np.prod(input.shape) * output_last_dim) - - -def pool_flops_counter_hook(module, input, output): - input = input[0] - module.__flops__ += int(np.prod(input.shape)) - - -def norm_flops_counter_hook(module, input, output): - input = input[0] - - batch_flops = np.prod(input.shape) - if (getattr(module, 'affine', False) - or getattr(module, 'elementwise_affine', False)): - batch_flops *= 2 - module.__flops__ += int(batch_flops) - - -def deconv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - input_height, input_width = input.shape[2:] - - kernel_height, kernel_width = conv_module.kernel_size - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = ( - kernel_height * kernel_width * in_channels * filters_per_channel) - - active_elements_count = batch_size * input_height * input_width - overall_conv_flops = conv_per_position_flops * active_elements_count - bias_flops = 0 - if conv_module.bias is not None: - output_height, output_width = output.shape[2:] - bias_flops = out_channels * batch_size * output_height * output_height - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def conv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - output_dims = list(output.shape[2:]) - - kernel_dims = list(conv_module.kernel_size) - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = int( - np.prod(kernel_dims)) * in_channels * filters_per_channel - - active_elements_count = batch_size * int(np.prod(output_dims)) - - overall_conv_flops = conv_per_position_flops * active_elements_count - - bias_flops = 0 - - if conv_module.bias is not None: - - bias_flops = out_channels * active_elements_count - - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def batch_counter_hook(module, input, output): - batch_size = 1 - if len(input) > 0: - # Can have multiple inputs, getting the first one - input = input[0] - batch_size = len(input) - else: - pass - print('Warning! No positional inputs found for a module, ' - 'assuming batch size is 1.') - module.__batch_counter__ += batch_size - - -def add_batch_counter_variables_or_reset(module): - - module.__batch_counter__ = 0 - - -def add_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - return - - handle = module.register_forward_hook(batch_counter_hook) - module.__batch_counter_handle__ = handle - - -def remove_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - module.__batch_counter_handle__.remove() - del module.__batch_counter_handle__ - - -def add_flops_counter_variable_or_reset(module): - if is_supported_instance(module): - if hasattr(module, '__flops__') or hasattr(module, '__params__'): - print('Warning: variables __flops__ or __params__ are already ' - 'defined for the module' + type(module).__name__ + - ' ptflops can affect your code!') - module.__flops__ = 0 - module.__params__ = get_model_parameters_number(module) - - -def is_supported_instance(module): - if type(module) in get_modules_mapping(): - return True - return False - - -def remove_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - module.__flops_handle__.remove() - del module.__flops_handle__ - - -def get_modules_mapping(): - return { - # convolutions - nn.Conv1d: conv_flops_counter_hook, - nn.Conv2d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, - nn.Conv3d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, - # activations - nn.ReLU: relu_flops_counter_hook, - nn.PReLU: relu_flops_counter_hook, - nn.ELU: relu_flops_counter_hook, - nn.LeakyReLU: relu_flops_counter_hook, - nn.ReLU6: relu_flops_counter_hook, - # poolings - nn.MaxPool1d: pool_flops_counter_hook, - nn.AvgPool1d: pool_flops_counter_hook, - nn.AvgPool2d: pool_flops_counter_hook, - nn.MaxPool2d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, - nn.MaxPool3d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, - nn.AvgPool3d: pool_flops_counter_hook, - nn.AdaptiveMaxPool1d: pool_flops_counter_hook, - nn.AdaptiveAvgPool1d: pool_flops_counter_hook, - nn.AdaptiveMaxPool2d: pool_flops_counter_hook, - nn.AdaptiveAvgPool2d: pool_flops_counter_hook, - nn.AdaptiveMaxPool3d: pool_flops_counter_hook, - nn.AdaptiveAvgPool3d: pool_flops_counter_hook, - # normalizations - nn.BatchNorm1d: norm_flops_counter_hook, - nn.BatchNorm2d: norm_flops_counter_hook, - nn.BatchNorm3d: norm_flops_counter_hook, - nn.GroupNorm: norm_flops_counter_hook, - nn.InstanceNorm1d: norm_flops_counter_hook, - nn.InstanceNorm2d: norm_flops_counter_hook, - nn.InstanceNorm3d: norm_flops_counter_hook, - nn.LayerNorm: norm_flops_counter_hook, - # FC - nn.Linear: linear_flops_counter_hook, - mmcv.cnn.bricks.Linear: linear_flops_counter_hook, - # Upscale - nn.Upsample: upsample_flops_counter_hook, - # Deconvolution - nn.ConvTranspose2d: deconv_flops_counter_hook, - mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, - } diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/fuse_conv_bn.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/fuse_conv_bn.py deleted file mode 100755 index cb7076f80..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/fuse_conv_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def _fuse_conv_bn(conv, bn): - """Fuse conv and bn into one module. - - Args: - conv (nn.Module): Conv to be fused. - bn (nn.Module): BN to be fused. - - Returns: - nn.Module: Fused module. - """ - conv_w = conv.weight - conv_b = conv.bias if conv.bias is not None else torch.zeros_like( - bn.running_mean) - - factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) - conv.weight = nn.Parameter(conv_w * - factor.reshape([conv.out_channels, 1, 1, 1])) - conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) - return conv - - -def fuse_conv_bn(module): - """Recursively fuse conv and bn in a module. - - During inference, the functionary of batch norm layers is turned off - but only the mean and var alone channels are used, which exposes the - chance to fuse it with the preceding conv layers to save computations and - simplify network structures. - - Args: - module (nn.Module): Module to be fused. - - Returns: - nn.Module: Fused module. - """ - last_conv = None - last_conv_name = None - - for name, child in module.named_children(): - if isinstance(child, - (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): - if last_conv is None: # only fuse BN that is after Conv - continue - fused_conv = _fuse_conv_bn(last_conv, child) - module._modules[last_conv_name] = fused_conv - # To reduce changes, set BN as Identity instead of deleting it. - module._modules[name] = nn.Identity() - last_conv = None - elif isinstance(child, nn.Conv2d): - last_conv = child - last_conv_name = name - else: - fuse_conv_bn(child) - return module diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/sync_bn.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/sync_bn.py deleted file mode 100755 index 8a79ff4a4..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/sync_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -import torch - -import mmcv - - -class _BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): - """A general BatchNorm layer without input dimension check. - - Reproduced from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc - is `_check_input_dim` that is designed for tensor sanity checks. - The check has been bypassed in this class for the convenience of converting - SyncBatchNorm. - """ - - def _check_input_dim(self, input): - return - - -def revert_sync_batchnorm(module): - """Helper function to convert all `SyncBatchNorm` (SyncBN) and - `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to - `BatchNormXd` layers. - - Adapted from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - - Args: - module (nn.Module): The module containing `SyncBatchNorm` layers. - - Returns: - module_output: The converted module with `BatchNormXd` layers. - """ - module_output = module - module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] - if hasattr(mmcv, 'ops'): - module_checklist.append(mmcv.ops.SyncBatchNorm) - if isinstance(module, tuple(module_checklist)): - module_output = _BatchNormXd(module.num_features, module.eps, - module.momentum, module.affine, - module.track_running_stats) - if module.affine: - # no_grad() may not be needed here but - # just to be consistent with `convert_sync_batchnorm()` - with torch.no_grad(): - module_output.weight = module.weight - module_output.bias = module.bias - module_output.running_mean = module.running_mean - module_output.running_var = module.running_var - module_output.num_batches_tracked = module.num_batches_tracked - module_output.training = module.training - # qconfig exists in quantized models - if hasattr(module, 'qconfig'): - module_output.qconfig = module.qconfig - for name, child in module.named_children(): - module_output.add_module(name, revert_sync_batchnorm(child)) - del module - return module_output diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/weight_init.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/weight_init.py deleted file mode 100755 index e1ac999e2..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/utils/weight_init.py +++ /dev/null @@ -1,684 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings - -import numpy as np -import torch -import torch.nn as nn -from torch import Tensor - -from mmcv.utils import Registry, build_from_cfg, get_logger, print_log - -INITIALIZERS = Registry('initializer') - - -def update_init_info(module, init_info): - """Update the `_params_init_info` in the module if the value of parameters - are changed. - - Args: - module (obj:`nn.Module`): The module of PyTorch with a user-defined - attribute `_params_init_info` which records the initialization - information. - init_info (str): The string that describes the initialization. - """ - assert hasattr( - module, - '_params_init_info'), f'Can not find `_params_init_info` in {module}' - for name, param in module.named_parameters(): - - assert param in module._params_init_info, ( - f'Find a new :obj:`Parameter` ' - f'named `{name}` during executing the ' - f'`init_weights` of ' - f'`{module.__class__.__name__}`. ' - f'Please do not add or ' - f'replace parameters during executing ' - f'the `init_weights`. ') - - # The parameter has been changed during executing the - # `init_weights` of module - mean_value = param.data.mean() - if module._params_init_info[param]['tmp_mean_value'] != mean_value: - module._params_init_info[param]['init_info'] = init_info - module._params_init_info[param]['tmp_mean_value'] = mean_value - - -def constant_init(module, val, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.constant_(module.weight, val) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def xavier_init(module, gain=1, bias=0, distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.xavier_uniform_(module.weight, gain=gain) - else: - nn.init.xavier_normal_(module.weight, gain=gain) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def normal_init(module, mean=0, std=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.normal_(module.weight, mean, std) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def trunc_normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - trunc_normal_(module.weight, mean, std, a, b) # type: ignore - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) # type: ignore - - -def uniform_init(module, a=0, b=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.uniform_(module.weight, a, b) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def kaiming_init(module, - a=0, - mode='fan_out', - nonlinearity='relu', - bias=0, - distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.kaiming_uniform_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - else: - nn.init.kaiming_normal_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def caffe2_xavier_init(module, bias=0): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - kaiming_init( - module, - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - bias=bias, - distribution='uniform') - - -def bias_init_with_prob(prior_prob): - """initialize conv/fc bias value according to a given probability value.""" - bias_init = float(-np.log((1 - prior_prob) / prior_prob)) - return bias_init - - -def _get_bases_name(m): - return [b.__name__ for b in m.__class__.__bases__] - - -class BaseInit(object): - - def __init__(self, *, bias=0, bias_prob=None, layer=None): - self.wholemodule = False - if not isinstance(bias, (int, float)): - raise TypeError(f'bias must be a number, but got a {type(bias)}') - - if bias_prob is not None: - if not isinstance(bias_prob, float): - raise TypeError(f'bias_prob type must be float, \ - but got {type(bias_prob)}') - - if layer is not None: - if not isinstance(layer, (str, list)): - raise TypeError(f'layer must be a str or a list of str, \ - but got a {type(layer)}') - else: - layer = [] - - if bias_prob is not None: - self.bias = bias_init_with_prob(bias_prob) - else: - self.bias = bias - self.layer = [layer] if isinstance(layer, str) else layer - - def _get_init_info(self): - info = f'{self.__class__.__name__}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Constant') -class ConstantInit(BaseInit): - """Initialize module parameters with constant values. - - Args: - val (int | float): the value to fill the weights in the module with - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, val, **kwargs): - super().__init__(**kwargs) - self.val = val - - def __call__(self, module): - - def init(m): - if self.wholemodule: - constant_init(m, self.val, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - constant_init(m, self.val, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Xavier') -class XavierInit(BaseInit): - r"""Initialize module parameters with values according to the method - described in `Understanding the difficulty of training deep feedforward - neural networks - Glorot, X. & Bengio, Y. (2010). - `_ - - Args: - gain (int | float): an optional scaling factor. Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` - or ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, gain=1, distribution='normal', **kwargs): - super().__init__(**kwargs) - self.gain = gain - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - xavier_init(m, self.gain, self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - xavier_init(m, self.gain, self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: gain={self.gain}, ' \ - f'distribution={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Normal') -class NormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. - - Args: - mean (int | float):the mean of the normal distribution. Defaults to 0. - std (int | float): the standard deviation of the normal distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, mean=0, std=1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.std = std - - def __call__(self, module): - - def init(m): - if self.wholemodule: - normal_init(m, self.mean, self.std, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - normal_init(m, self.mean, self.std, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: mean={self.mean},' \ - f' std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='TruncNormal') -class TruncNormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values - outside :math:`[a, b]`. - - Args: - mean (float): the mean of the normal distribution. Defaults to 0. - std (float): the standard deviation of the normal distribution. - Defaults to 1. - a (float): The minimum cutoff value. - b ( float): The maximum cutoff value. - bias (float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - **kwargs) -> None: - super().__init__(**kwargs) - self.mean = mean - self.std = std - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ - f' mean={self.mean}, std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Uniform') -class UniformInit(BaseInit): - r"""Initialize module parameters with values drawn from the uniform - distribution :math:`\mathcal{U}(a, b)`. - - Args: - a (int | float): the lower bound of the uniform distribution. - Defaults to 0. - b (int | float): the upper bound of the uniform distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, a=0, b=1, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def __call__(self, module): - - def init(m): - if self.wholemodule: - uniform_init(m, self.a, self.b, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - uniform_init(m, self.a, self.b, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a},' \ - f' b={self.b}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Kaiming') -class KaimingInit(BaseInit): - r"""Initialize module parameters with the values according to the method - described in `Delving deep into rectifiers: Surpassing human-level - performance on ImageNet classification - He, K. et al. (2015). - `_ - - Args: - a (int | float): the negative slope of the rectifier used after this - layer (only used with ``'leaky_relu'``). Defaults to 0. - mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing - ``'fan_in'`` preserves the magnitude of the variance of the weights - in the forward pass. Choosing ``'fan_out'`` preserves the - magnitudes in the backwards pass. Defaults to ``'fan_out'``. - nonlinearity (str): the non-linear function (`nn.functional` name), - recommended to use only with ``'relu'`` or ``'leaky_relu'`` . - Defaults to 'relu'. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` or - ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - a=0, - mode='fan_out', - nonlinearity='relu', - distribution='normal', - **kwargs): - super().__init__(**kwargs) - self.a = a - self.mode = mode - self.nonlinearity = nonlinearity - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ - f'nonlinearity={self.nonlinearity}, ' \ - f'distribution ={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Caffe2Xavier') -class Caffe2XavierInit(KaimingInit): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - def __init__(self, **kwargs): - super().__init__( - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='uniform', - **kwargs) - - def __call__(self, module): - super().__call__(module) - - -@INITIALIZERS.register_module(name='Pretrained') -class PretrainedInit(object): - """Initialize module by loading a pretrained model. - - Args: - checkpoint (str): the checkpoint file of the pretrained model should - be load. - prefix (str, optional): the prefix of a sub-module in the pretrained - model. it is for loading a part of the pretrained model to - initialize. For example, if we would like to only load the - backbone of a detector model, we can set ``prefix='backbone.'``. - Defaults to None. - map_location (str): map tensors into proper locations. - """ - - def __init__(self, checkpoint, prefix=None, map_location=None): - self.checkpoint = checkpoint - self.prefix = prefix - self.map_location = map_location - - def __call__(self, module): - from mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, - load_state_dict) - logger = get_logger('mmcv') - if self.prefix is None: - print_log(f'load model from: {self.checkpoint}', logger=logger) - load_checkpoint( - module, - self.checkpoint, - map_location=self.map_location, - strict=False, - logger=logger) - else: - print_log( - f'load {self.prefix} in model from: {self.checkpoint}', - logger=logger) - state_dict = _load_checkpoint_with_prefix( - self.prefix, self.checkpoint, map_location=self.map_location) - load_state_dict(module, state_dict, strict=False, logger=logger) - - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: load from {self.checkpoint}' - return info - - -def _initialize(module, cfg, wholemodule=False): - func = build_from_cfg(cfg, INITIALIZERS) - # wholemodule flag is for override mode, there is no layer key in override - # and initializer will give init values for the whole module with the name - # in override. - func.wholemodule = wholemodule - func(module) - - -def _initialize_override(module, override, cfg): - if not isinstance(override, (dict, list)): - raise TypeError(f'override must be a dict or a list of dict, \ - but got {type(override)}') - - override = [override] if isinstance(override, dict) else override - - for override_ in override: - - cp_override = copy.deepcopy(override_) - name = cp_override.pop('name', None) - if name is None: - raise ValueError('`override` must contain the key "name",' - f'but got {cp_override}') - # if override only has name key, it means use args in init_cfg - if not cp_override: - cp_override.update(cfg) - # if override has name key and other args except type key, it will - # raise error - elif 'type' not in cp_override.keys(): - raise ValueError( - f'`override` need "type" key, but got {cp_override}') - - if hasattr(module, name): - _initialize(getattr(module, name), cp_override, wholemodule=True) - else: - raise RuntimeError(f'module did not have attribute {name}, ' - f'but init_cfg is {cp_override}.') - - -def initialize(module, init_cfg): - """Initialize a module. - - Args: - module (``torch.nn.Module``): the module will be initialized. - init_cfg (dict | list[dict]): initialization configuration dict to - define initializer. OpenMMLab has implemented 6 initializers - including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, - ``Kaiming``, and ``Pretrained``. - Example: - >>> module = nn.Linear(2, 3, bias=True) - >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) - >>> initialize(module, init_cfg) - - >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) - >>> # define key ``'layer'`` for initializing layer with different - >>> # configuration - >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), - dict(type='Constant', layer='Linear', val=2)] - >>> initialize(module, init_cfg) - - >>> # define key``'override'`` to initialize some specific part in - >>> # module - >>> class FooNet(nn.Module): - >>> def __init__(self): - >>> super().__init__() - >>> self.feat = nn.Conv2d(3, 16, 3) - >>> self.reg = nn.Conv2d(16, 10, 3) - >>> self.cls = nn.Conv2d(16, 5, 3) - >>> model = FooNet() - >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', - >>> override=dict(type='Constant', name='reg', val=3, bias=4)) - >>> initialize(model, init_cfg) - - >>> model = ResNet(depth=50) - >>> # Initialize weights with the pretrained model. - >>> init_cfg = dict(type='Pretrained', - checkpoint='torchvision://resnet50') - >>> initialize(model, init_cfg) - - >>> # Initialize weights of a sub-module with the specific part of - >>> # a pretrained model by using "prefix". - >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ - >>> 'retinanet_r50_fpn_1x_coco/'\ - >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' - >>> init_cfg = dict(type='Pretrained', - checkpoint=url, prefix='backbone.') - """ - if not isinstance(init_cfg, (dict, list)): - raise TypeError(f'init_cfg must be a dict or a list of dict, \ - but got {type(init_cfg)}') - - if isinstance(init_cfg, dict): - init_cfg = [init_cfg] - - for cfg in init_cfg: - # should deeply copy the original config because cfg may be used by - # other modules, e.g., one init_cfg shared by multiple bottleneck - # blocks, the expected cfg will be changed after pop and will change - # the initialization behavior of other modules - cp_cfg = copy.deepcopy(cfg) - override = cp_cfg.pop('override', None) - _initialize(module, cp_cfg) - - if override is not None: - cp_cfg.pop('layer', None) - _initialize_override(module, override, cp_cfg) - else: - # All attributes in module have same initialization. - pass - - -def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, - b: float) -> Tensor: - # Method based on - # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf - # Modified from - # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - def norm_cdf(x): - # Computes standard normal cumulative distribution function - return (1. + math.erf(x / math.sqrt(2.))) / 2. - - if (mean < a - 2 * std) or (mean > b + 2 * std): - warnings.warn( - 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' - 'The distribution of values may be incorrect.', - stacklevel=2) - - with torch.no_grad(): - # Values are generated by using a truncated uniform distribution and - # then using the inverse CDF for the normal distribution. - # Get upper and lower cdf values - lower = norm_cdf((a - mean) / std) - upper = norm_cdf((b - mean) / std) - - # Uniformly fill tensor with values from [lower, upper], then translate - # to [2lower-1, 2upper-1]. - tensor.uniform_(2 * lower - 1, 2 * upper - 1) - - # Use inverse cdf transform for normal distribution to get truncated - # standard normal - tensor.erfinv_() - - # Transform to proper mean, std - tensor.mul_(std * math.sqrt(2.)) - tensor.add_(mean) - - # Clamp to ensure it's in the proper range - tensor.clamp_(min=a, max=b) - return tensor - - -def trunc_normal_(tensor: Tensor, - mean: float = 0., - std: float = 1., - a: float = -2., - b: float = 2.) -> Tensor: - r"""Fills the input Tensor with values drawn from a truncated - normal distribution. The values are effectively drawn from the - normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` - with values outside :math:`[a, b]` redrawn until they are within - the bounds. The method used for generating the random values works - best when :math:`a \leq \text{mean} \leq b`. - - Modified from - https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - - Args: - tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. - mean (float): the mean of the normal distribution. - std (float): the standard deviation of the normal distribution. - a (float): the minimum cutoff value. - b (float): the maximum cutoff value. - """ - return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/vgg.py b/cv/super_resolution/esrgan/pytorch/mmcv/cnn/vgg.py deleted file mode 100755 index 8778b6495..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/cnn/vgg.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn - -from .utils import constant_init, kaiming_init, normal_init - - -def conv3x3(in_planes, out_planes, dilation=1): - """3x3 convolution with padding.""" - return nn.Conv2d( - in_planes, - out_planes, - kernel_size=3, - padding=dilation, - dilation=dilation) - - -def make_vgg_layer(inplanes, - planes, - num_blocks, - dilation=1, - with_bn=False, - ceil_mode=False): - layers = [] - for _ in range(num_blocks): - layers.append(conv3x3(inplanes, planes, dilation)) - if with_bn: - layers.append(nn.BatchNorm2d(planes)) - layers.append(nn.ReLU(inplace=True)) - inplanes = planes - layers.append(nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=ceil_mode)) - - return layers - - -class VGG(nn.Module): - """VGG backbone. - - Args: - depth (int): Depth of vgg, from {11, 13, 16, 19}. - with_bn (bool): Use BatchNorm or not. - num_classes (int): number of classes for classification. - num_stages (int): VGG stages, normally 5. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze - running stats (mean and var). - bn_frozen (bool): Whether to freeze weight and bias of BN layers. - """ - - arch_settings = { - 11: (1, 1, 2, 2, 2), - 13: (2, 2, 2, 2, 2), - 16: (2, 2, 3, 3, 3), - 19: (2, 2, 4, 4, 4) - } - - def __init__(self, - depth, - with_bn=False, - num_classes=-1, - num_stages=5, - dilations=(1, 1, 1, 1, 1), - out_indices=(0, 1, 2, 3, 4), - frozen_stages=-1, - bn_eval=True, - bn_frozen=False, - ceil_mode=False, - with_last_pool=True): - super(VGG, self).__init__() - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for vgg') - assert num_stages >= 1 and num_stages <= 5 - stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - assert len(dilations) == num_stages - assert max(out_indices) <= num_stages - - self.num_classes = num_classes - self.out_indices = out_indices - self.frozen_stages = frozen_stages - self.bn_eval = bn_eval - self.bn_frozen = bn_frozen - - self.inplanes = 3 - start_idx = 0 - vgg_layers = [] - self.range_sub_modules = [] - for i, num_blocks in enumerate(self.stage_blocks): - num_modules = num_blocks * (2 + with_bn) + 1 - end_idx = start_idx + num_modules - dilation = dilations[i] - planes = 64 * 2**i if i < 4 else 512 - vgg_layer = make_vgg_layer( - self.inplanes, - planes, - num_blocks, - dilation=dilation, - with_bn=with_bn, - ceil_mode=ceil_mode) - vgg_layers.extend(vgg_layer) - self.inplanes = planes - self.range_sub_modules.append([start_idx, end_idx]) - start_idx = end_idx - if not with_last_pool: - vgg_layers.pop(-1) - self.range_sub_modules[-1][1] -= 1 - self.module_name = 'features' - self.add_module(self.module_name, nn.Sequential(*vgg_layers)) - - if self.num_classes > 0: - self.classifier = nn.Sequential( - nn.Linear(512 * 7 * 7, 4096), - nn.ReLU(True), - nn.Dropout(), - nn.Linear(4096, 4096), - nn.ReLU(True), - nn.Dropout(), - nn.Linear(4096, num_classes), - ) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m) - elif isinstance(m, nn.BatchNorm2d): - constant_init(m, 1) - elif isinstance(m, nn.Linear): - normal_init(m, std=0.01) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - outs = [] - vgg_layers = getattr(self, self.module_name) - for i in range(len(self.stage_blocks)): - for j in range(*self.range_sub_modules[i]): - vgg_layer = vgg_layers[j] - x = vgg_layer(x) - if i in self.out_indices: - outs.append(x) - if self.num_classes > 0: - x = x.view(x.size(0), -1) - x = self.classifier(x) - outs.append(x) - if len(outs) == 1: - return outs[0] - else: - return tuple(outs) - - def train(self, mode=True): - super(VGG, self).train(mode) - if self.bn_eval: - for m in self.modules(): - if isinstance(m, nn.BatchNorm2d): - m.eval() - if self.bn_frozen: - for params in m.parameters(): - params.requires_grad = False - vgg_layers = getattr(self, self.module_name) - if mode and self.frozen_stages >= 0: - for i in range(self.frozen_stages): - for j in range(*self.range_sub_modules[i]): - mod = vgg_layers[j] - mod.eval() - for param in mod.parameters(): - param.requires_grad = False diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/engine/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/engine/__init__.py deleted file mode 100755 index 3193b7f66..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, - single_gpu_test) - -__all__ = [ - 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', - 'single_gpu_test' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/engine/test.py b/cv/super_resolution/esrgan/pytorch/mmcv/engine/test.py deleted file mode 100755 index f236b1cda..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/engine/test.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, data_loader): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - # Assume result has the same length of batch_size - # refer to https://github.com/open-mmlab/mmcv/issues/985 - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting - ``gpu_collect=True``, it encodes results to gpu tensors and use gpu - communication for results collection. On cpu mode it saves the results on - different gpus to ``tmpdir`` and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - if rank == 0: - batch_size = len(result) - batch_size_all = batch_size * world_size - if batch_size_all + prog_bar.completed > len(dataset): - batch_size_all = len(dataset) - prog_bar.completed - for _ in range(batch_size_all): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results under cpu mode. - - On cpu mode, this function will save the results on different gpus to - ``tmpdir`` and collect them by the rank 0 worker. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - tmpdir (str | None): temporal directory for collected results to - store. If set to None, it will create a random temporal directory - for it. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_result = mmcv.load(part_file) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results under gpu mode. - - On gpu mode, this function will encode results to gpu tensors and use gpu - communication for results collection. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/fileio/__init__.py deleted file mode 100755 index 2051b85f7..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .file_client import BaseStorageBackend, FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler -from .io import dump, load, register_handler -from .parse import dict_from_file, list_from_file - -__all__ = [ - 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', - 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', - 'list_from_file', 'dict_from_file' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/file_client.py b/cv/super_resolution/esrgan/pytorch/mmcv/fileio/file_client.py deleted file mode 100755 index b2d622868..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/file_client.py +++ /dev/null @@ -1,1148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import os -import os.path as osp -import re -import tempfile -import warnings -from abc import ABCMeta, abstractmethod -from contextlib import contextmanager -from pathlib import Path -from typing import Iterable, Iterator, Optional, Tuple, Union -from urllib.request import urlopen - -import mmcv -from mmcv.utils.misc import has_method -from mmcv.utils.path import is_filepath - - -class BaseStorageBackend(metaclass=ABCMeta): - """Abstract class of storage backends. - - All backends need to implement two apis: ``get()`` and ``get_text()``. - ``get()`` reads the file as a byte stream and ``get_text()`` reads the file - as texts. - """ - - # a flag to indicate whether the backend can create a symlink for a file - _allow_symlink = False - - @property - def name(self): - return self.__class__.__name__ - - @property - def allow_symlink(self): - return self._allow_symlink - - @abstractmethod - def get(self, filepath): - pass - - @abstractmethod - def get_text(self, filepath): - pass - - -class CephBackend(BaseStorageBackend): - """Ceph storage backend (for internal use). - - Args: - path_mapping (dict|None): path mapping dict from local path to Petrel - path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` - will be replaced by ``dst``. Default: None. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - """ - - def __init__(self, path_mapping=None): - try: - import ceph - except ImportError: - raise ImportError('Please install ceph to enable CephBackend.') - - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead') - self._client = ceph.S3Client() - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def get(self, filepath): - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class PetrelBackend(BaseStorageBackend): - """Petrel storage backend (for internal use). - - PetrelBackend supports reading and writing data to multiple clusters. - If the file path contains the cluster name, PetrelBackend will read data - from specified cluster or write data to it. Otherwise, PetrelBackend will - access the default cluster. - - Args: - path_mapping (dict, optional): Path mapping dict from local path to - Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in - ``filepath`` will be replaced by ``dst``. Default: None. - enable_mc (bool, optional): Whether to enable memcached support. - Default: True. - - Examples: - >>> filepath1 = 's3://path/of/file' - >>> filepath2 = 'cluster-name:s3://path/of/file' - >>> client = PetrelBackend() - >>> client.get(filepath1) # get data from default cluster - >>> client.get(filepath2) # get data from 'cluster-name' cluster - """ - - def __init__(self, - path_mapping: Optional[dict] = None, - enable_mc: bool = True): - try: - from petrel_client import client - except ImportError: - raise ImportError('Please install petrel_client to enable ' - 'PetrelBackend.') - - self._client = client.Client(enable_mc=enable_mc) - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def _map_path(self, filepath: Union[str, Path]) -> str: - """Map ``filepath`` to a string path whose prefix will be replaced by - :attr:`self.path_mapping`. - - Args: - filepath (str): Path to be mapped. - """ - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - return filepath - - def _format_path(self, filepath: str) -> str: - """Convert a ``filepath`` to standard format of petrel oss. - - If the ``filepath`` is concatenated by ``os.path.join``, in a Windows - environment, the ``filepath`` will be the format of - 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the - above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. - - Args: - filepath (str): Path to be formatted. - """ - return re.sub(r'\\+', '/', filepath) - - def get(self, filepath: Union[str, Path]) -> memoryview: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - memoryview: A memory view of expected bytes object to avoid - copying. The memoryview object can be converted to bytes by - ``value_buf.tobytes()``. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return str(self.get(filepath), encoding=encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Save data to a given ``filepath``. - - Args: - obj (bytes): Data to be saved. - filepath (str or Path): Path to write data. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.put(filepath, obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Save data to a given ``filepath``. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to encode the ``obj``. - Default: 'utf-8'. - """ - self.put(bytes(obj, encoding=encoding), filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - if not has_method(self._client, 'delete'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `delete` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.delete(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - if not (has_method(self._client, 'contains') - and has_method(self._client, 'isdir')): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` and `isdir` methods, please use a higher' - 'version or dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) or self._client.isdir(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - if not has_method(self._client, 'isdir'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `isdir` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - if not has_method(self._client, 'contains'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` method, please use a higher version or ' - 'dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result after concatenation. - """ - filepath = self._format_path(self._map_path(filepath)) - if filepath.endswith('/'): - filepath = filepath[:-1] - formatted_paths = [filepath] - for path in filepaths: - formatted_paths.append(self._format_path(self._map_path(path))) - return '/'.join(formatted_paths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download a file from ``filepath`` and return a temporary path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str | Path): Download a file from ``filepath``. - - Examples: - >>> client = PetrelBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('s3://path/of/your/file') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one temporary path. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - assert self.isfile(filepath) - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - Petrel has no concept of directories but it simulates the directory - hierarchy in the filesystem through public prefixes. In addition, - if the returned path ends with '/', it means the path is a public - prefix which is a logical directory. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - In addition, the returned path of directory will not contains the - suffix '/' which is consistent with other backends. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if not has_method(self._client, 'list'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `list` method, please use a higher version or dev' - ' branch instead.')) - - dir_path = self._map_path(dir_path) - dir_path = self._format_path(dir_path) - if list_dir and suffix is not None: - raise TypeError( - '`list_dir` should be False when `suffix` is not None') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - # Petrel's simulated directory hierarchy assumes that directory paths - # should end with `/` - if not dir_path.endswith('/'): - dir_path += '/' - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for path in self._client.list(dir_path): - # the `self.isdir` is not used here to determine whether path - # is a directory, because `self.isdir` relies on - # `self._client.list` - if path.endswith('/'): # a directory path - next_dir_path = self.join_path(dir_path, path) - if list_dir: - # get the relative path and exclude the last - # character '/' - rel_dir = next_dir_path[len(root):-1] - yield rel_dir - if recursive: - yield from _list_dir_or_file(next_dir_path, list_dir, - list_file, suffix, - recursive) - else: # a file path - absolute_path = self.join_path(dir_path, path) - rel_path = absolute_path[len(root):] - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class MemcachedBackend(BaseStorageBackend): - """Memcached storage backend. - - Attributes: - server_list_cfg (str): Config file for memcached server list. - client_cfg (str): Config file for memcached client. - sys_path (str | None): Additional path to be appended to `sys.path`. - Default: None. - """ - - def __init__(self, server_list_cfg, client_cfg, sys_path=None): - if sys_path is not None: - import sys - sys.path.append(sys_path) - try: - import mc - except ImportError: - raise ImportError( - 'Please install memcached to enable MemcachedBackend.') - - self.server_list_cfg = server_list_cfg - self.client_cfg = client_cfg - self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, - self.client_cfg) - # mc.pyvector servers as a point which points to a memory cache - self._mc_buffer = mc.pyvector() - - def get(self, filepath): - filepath = str(filepath) - import mc - self._client.Get(filepath, self._mc_buffer) - value_buf = mc.ConvertBuffer(self._mc_buffer) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class LmdbBackend(BaseStorageBackend): - """Lmdb storage backend. - - Args: - db_path (str): Lmdb database path. - readonly (bool, optional): Lmdb environment parameter. If True, - disallow any write operations. Default: True. - lock (bool, optional): Lmdb environment parameter. If False, when - concurrent access occurs, do not lock the database. Default: False. - readahead (bool, optional): Lmdb environment parameter. If False, - disable the OS filesystem readahead mechanism, which may improve - random read performance when a database is larger than RAM. - Default: False. - - Attributes: - db_path (str): Lmdb database path. - """ - - def __init__(self, - db_path, - readonly=True, - lock=False, - readahead=False, - **kwargs): - try: - import lmdb - except ImportError: - raise ImportError('Please install lmdb to enable LmdbBackend.') - - self.db_path = str(db_path) - self._client = lmdb.open( - self.db_path, - readonly=readonly, - lock=lock, - readahead=readahead, - **kwargs) - - def get(self, filepath): - """Get values according to the filepath. - - Args: - filepath (str | obj:`Path`): Here, filepath is the lmdb key. - """ - filepath = str(filepath) - with self._client.begin(write=False) as txn: - value_buf = txn.get(filepath.encode('ascii')) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class HardDiskBackend(BaseStorageBackend): - """Raw hard disks storage backend.""" - - _allow_symlink = True - - def get(self, filepath: Union[str, Path]) -> bytes: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes: Expected bytes object. - """ - with open(filepath, 'rb') as f: - value_buf = f.read() - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - with open(filepath, 'r', encoding=encoding) as f: - value_buf = f.read() - return value_buf - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` will create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'wb') as f: - f.write(obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` will create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'w', encoding=encoding) as f: - f.write(obj) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - os.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return osp.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return osp.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return osp.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return osp.join(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, filepath: Union[str, Path]) -> Iterable[Union[str, Path]]: - """Only for unified API and do nothing.""" - yield filepath - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if list_dir and suffix is not None: - raise TypeError('`suffix` should be None when `list_dir` is True') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - elif osp.isdir(entry.path): - if list_dir: - rel_dir = osp.relpath(entry.path, root) - yield rel_dir - if recursive: - yield from _list_dir_or_file(entry.path, list_dir, - list_file, suffix, - recursive) - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class HTTPBackend(BaseStorageBackend): - """HTTP and HTTPS storage bachend.""" - - def get(self, filepath): - value_buf = urlopen(filepath).read() - return value_buf - - def get_text(self, filepath, encoding='utf-8'): - value_buf = urlopen(filepath).read() - return value_buf.decode(encoding) - - @contextmanager - def get_local_path(self, filepath: str) -> Iterable[str]: - """Download a file from ``filepath``. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str): Download a file from ``filepath``. - - Examples: - >>> client = HTTPBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('http://path/of/your/file') as path: - ... # do something here - """ - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - -class FileClient: - """A general file client to access files in different backends. - - The client loads a file or text in a specified backend from its path - and returns it as a binary or text file. There are two ways to choose a - backend, the name of backend and the prefix of path. Although both of them - can be used to choose a storage backend, ``backend`` has a higher priority - that is if they are all set, the storage backend will be chosen by the - backend argument. If they are all `None`, the disk backend will be chosen. - Note that It can also register other backend accessor with a given name, - prefixes, and backend class. In addition, We use the singleton pattern to - avoid repeated object creation. If the arguments are the same, the same - object will be returned. - - Args: - backend (str, optional): The storage backend type. Options are "disk", - "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. - prefix (str, optional): The prefix of the registered storage backend. - Options are "s3", "http", "https". Default: None. - - Examples: - >>> # only set backend - >>> file_client = FileClient(backend='petrel') - >>> # only set prefix - >>> file_client = FileClient(prefix='s3') - >>> # set both backend and prefix but use backend to choose client - >>> file_client = FileClient(backend='petrel', prefix='s3') - >>> # if the arguments are the same, the same object is returned - >>> file_client1 = FileClient(backend='petrel') - >>> file_client1 is file_client - True - - Attributes: - client (:obj:`BaseStorageBackend`): The backend object. - """ - - _backends = { - 'disk': HardDiskBackend, - 'ceph': CephBackend, - 'memcached': MemcachedBackend, - 'lmdb': LmdbBackend, - 'petrel': PetrelBackend, - 'http': HTTPBackend, - } - # This collection is used to record the overridden backends, and when a - # backend appears in the collection, the singleton pattern is disabled for - # that backend, because if the singleton pattern is used, then the object - # returned will be the backend before overwriting - _overridden_backends = set() - _prefix_to_backends = { - 's3': PetrelBackend, - 'http': HTTPBackend, - 'https': HTTPBackend, - } - _overridden_prefixes = set() - - _instances = {} - - def __new__(cls, backend=None, prefix=None, **kwargs): - if backend is None and prefix is None: - backend = 'disk' - if backend is not None and backend not in cls._backends: - raise ValueError( - f'Backend {backend} is not supported. Currently supported ones' - f' are {list(cls._backends.keys())}') - if prefix is not None and prefix not in cls._prefix_to_backends: - raise ValueError( - f'prefix {prefix} is not supported. Currently supported ones ' - f'are {list(cls._prefix_to_backends.keys())}') - - # concatenate the arguments to a unique key for determining whether - # objects with the same arguments were created - arg_key = f'{backend}:{prefix}' - for key, value in kwargs.items(): - arg_key += f':{key}:{value}' - - # if a backend was overridden, it will create a new object - if (arg_key in cls._instances - and backend not in cls._overridden_backends - and prefix not in cls._overridden_prefixes): - _instance = cls._instances[arg_key] - else: - # create a new object and put it to _instance - _instance = super().__new__(cls) - if backend is not None: - _instance.client = cls._backends[backend](**kwargs) - else: - _instance.client = cls._prefix_to_backends[prefix](**kwargs) - - cls._instances[arg_key] = _instance - - return _instance - - @property - def name(self): - return self.client.name - - @property - def allow_symlink(self): - return self.client.allow_symlink - - @staticmethod - def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: - """Parse the prefix of a uri. - - Args: - uri (str | Path): Uri to be parsed that contains the file prefix. - - Examples: - >>> FileClient.parse_uri_prefix('s3://path/of/your/file') - 's3' - - Returns: - str | None: Return the prefix of uri if the uri contains '://' - else ``None``. - """ - assert is_filepath(uri) - uri = str(uri) - if '://' not in uri: - return None - else: - prefix, _ = uri.split('://') - # In the case of PetrelBackend, the prefix may contains the cluster - # name like clusterName:s3 - if ':' in prefix: - _, prefix = prefix.split(':') - return prefix - - @classmethod - def infer_client(cls, - file_client_args: Optional[dict] = None, - uri: Optional[Union[str, Path]] = None) -> 'FileClient': - """Infer a suitable file client based on the URI and arguments. - - Args: - file_client_args (dict, optional): Arguments to instantiate a - FileClient. Default: None. - uri (str | Path, optional): Uri to be parsed that contains the file - prefix. Default: None. - - Examples: - >>> uri = 's3://path/of/your/file' - >>> file_client = FileClient.infer_client(uri=uri) - >>> file_client_args = {'backend': 'petrel'} - >>> file_client = FileClient.infer_client(file_client_args) - - Returns: - FileClient: Instantiated FileClient object. - """ - assert file_client_args is not None or uri is not None - if file_client_args is None: - file_prefix = cls.parse_uri_prefix(uri) # type: ignore - return cls(prefix=file_prefix) - else: - return cls(**file_client_args) - - @classmethod - def _register_backend(cls, name, backend, force=False, prefixes=None): - if not isinstance(name, str): - raise TypeError('the backend name should be a string, ' - f'but got {type(name)}') - if not inspect.isclass(backend): - raise TypeError( - f'backend should be a class but got {type(backend)}') - if not issubclass(backend, BaseStorageBackend): - raise TypeError( - f'backend {backend} is not a subclass of BaseStorageBackend') - if not force and name in cls._backends: - raise KeyError( - f'{name} is already registered as a storage backend, ' - 'add "force=True" if you want to override it') - - if name in cls._backends and force: - cls._overridden_backends.add(name) - cls._backends[name] = backend - - if prefixes is not None: - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if prefix not in cls._prefix_to_backends: - cls._prefix_to_backends[prefix] = backend - elif (prefix in cls._prefix_to_backends) and force: - cls._overridden_prefixes.add(prefix) - cls._prefix_to_backends[prefix] = backend - else: - raise KeyError( - f'{prefix} is already registered as a storage backend,' - ' add "force=True" if you want to override it') - - @classmethod - def register_backend(cls, name, backend=None, force=False, prefixes=None): - """Register a backend to FileClient. - - This method can be used as a normal class method or a decorator. - - .. code-block:: python - - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - FileClient.register_backend('new', NewBackend) - - or - - .. code-block:: python - - @FileClient.register_backend('new') - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - Args: - name (str): The name of the registered backend. - backend (class, optional): The backend class to be registered, - which must be a subclass of :class:`BaseStorageBackend`. - When this method is used as a decorator, backend is None. - Defaults to None. - force (bool, optional): Whether to override the backend if the name - has already been registered. Defaults to False. - prefixes (str or list[str] or tuple[str], optional): The prefixes - of the registered storage backend. Default: None. - `New in version 1.3.15.` - """ - if backend is not None: - cls._register_backend( - name, backend, force=force, prefixes=prefixes) - return - - def _register(backend_cls): - cls._register_backend( - name, backend_cls, force=force, prefixes=prefixes) - return backend_cls - - return _register - - def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: - """Read data from a given ``filepath`` with 'rb' mode. - - Note: - There are two types of return values for ``get``, one is ``bytes`` - and the other is ``memoryview``. The advantage of using memoryview - is that you can avoid copying, and if you want to convert it to - ``bytes``, you can use ``.tobytes()``. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes | memoryview: Expected bytes object or a memory view of the - bytes object. - """ - return self.client.get(filepath) - - def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return self.client.get_text(filepath, encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` should create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - self.client.put(obj, filepath) - - def put_text(self, obj: str, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` should create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str, optional): The encoding format used to open the - `filepath`. Default: 'utf-8'. - """ - self.client.put_text(obj, filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str, Path): Path to be removed. - """ - self.client.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return self.client.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return self.client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return self.client.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return self.client.join_path(filepath, *filepaths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download data from ``filepath`` and write the data to local path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Note: - If the ``filepath`` is a local path, just return itself. - - .. warning:: - ``get_local_path`` is an experimental interface that may change in - the future. - - Args: - filepath (str or Path): Path to be read data. - - Examples: - >>> file_client = FileClient(prefix='s3') - >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one path. - """ - with self.client.get_local_path(str(filepath)) as local_path: - yield local_path - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, - suffix, recursive) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/__init__.py deleted file mode 100755 index aa24d9197..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseFileHandler -from .json_handler import JsonHandler -from .pickle_handler import PickleHandler -from .yaml_handler import YamlHandler - -__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/base.py b/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/base.py deleted file mode 100755 index 288878bc5..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseFileHandler(metaclass=ABCMeta): - # `str_like` is a flag to indicate whether the type of file object is - # str-like object or bytes-like object. Pickle only processes bytes-like - # objects but json only processes str-like object. If it is str-like - # object, `StringIO` will be used to process the buffer. - str_like = True - - @abstractmethod - def load_from_fileobj(self, file, **kwargs): - pass - - @abstractmethod - def dump_to_fileobj(self, obj, file, **kwargs): - pass - - @abstractmethod - def dump_to_str(self, obj, **kwargs): - pass - - def load_from_path(self, filepath, mode='r', **kwargs): - with open(filepath, mode) as f: - return self.load_from_fileobj(f, **kwargs) - - def dump_to_path(self, obj, filepath, mode='w', **kwargs): - with open(filepath, mode) as f: - self.dump_to_fileobj(obj, f, **kwargs) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/json_handler.py b/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/json_handler.py deleted file mode 100755 index 18d4f15f7..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/json_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -import numpy as np - -from .base import BaseFileHandler - - -def set_default(obj): - """Set default json values for non-serializable values. - - It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. - It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, - etc.) into plain numbers of plain python built-in types. - """ - if isinstance(obj, (set, range)): - return list(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, np.generic): - return obj.item() - raise TypeError(f'{type(obj)} is unsupported for json dump') - - -class JsonHandler(BaseFileHandler): - - def load_from_fileobj(self, file): - return json.load(file) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('default', set_default) - json.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('default', set_default) - return json.dumps(obj, **kwargs) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/pickle_handler.py b/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/pickle_handler.py deleted file mode 100755 index b37c79bed..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/pickle_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import pickle - -from .base import BaseFileHandler - - -class PickleHandler(BaseFileHandler): - - str_like = False - - def load_from_fileobj(self, file, **kwargs): - return pickle.load(file, **kwargs) - - def load_from_path(self, filepath, **kwargs): - return super(PickleHandler, self).load_from_path( - filepath, mode='rb', **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('protocol', 2) - return pickle.dumps(obj, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('protocol', 2) - pickle.dump(obj, file, **kwargs) - - def dump_to_path(self, obj, filepath, **kwargs): - super(PickleHandler, self).dump_to_path( - obj, filepath, mode='wb', **kwargs) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/yaml_handler.py b/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/yaml_handler.py deleted file mode 100755 index c5aa2eea1..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/handlers/yaml_handler.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import yaml - -try: - from yaml import CLoader as Loader, CDumper as Dumper -except ImportError: - from yaml import Loader, Dumper - -from .base import BaseFileHandler # isort:skip - - -class YamlHandler(BaseFileHandler): - - def load_from_fileobj(self, file, **kwargs): - kwargs.setdefault('Loader', Loader) - return yaml.load(file, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('Dumper', Dumper) - yaml.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('Dumper', Dumper) - return yaml.dump(obj, **kwargs) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/io.py b/cv/super_resolution/esrgan/pytorch/mmcv/fileio/io.py deleted file mode 100755 index aaefde58a..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/io.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from io import BytesIO, StringIO -from pathlib import Path - -from ..utils import is_list_of, is_str -from .file_client import FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler - -file_handlers = { - 'json': JsonHandler(), - 'yaml': YamlHandler(), - 'yml': YamlHandler(), - 'pickle': PickleHandler(), - 'pkl': PickleHandler() -} - - -def load(file, file_format=None, file_client_args=None, **kwargs): - """Load data from json/yaml/pickle files. - - This method provides a unified api for loading data from serialized files. - - Note: - In v1.3.16 and later, ``load`` supports loading data from serialized - files those can be storaged in different backends. - - Args: - file (str or :obj:`Path` or file-like object): Filename or a file-like - object. - file_format (str, optional): If not specified, the file format will be - inferred from the file extension, otherwise use the specified one. - Currently supported formats include "json", "yaml/yml" and - "pickle/pkl". - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> load('/path/of/your/file') # file is storaged in disk - >>> load('https://path/of/your/file') # file is storaged in Internet - >>> load('s3://path/of/your/file') # file is storaged in petrel - - Returns: - The content from the file. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None and is_str(file): - file_format = file.split('.')[-1] - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO(file_client.get_text(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - else: - with BytesIO(file_client.get(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - elif hasattr(file, 'read'): - obj = handler.load_from_fileobj(file, **kwargs) - else: - raise TypeError('"file" must be a filepath str or a file-object') - return obj - - -def dump(obj, file=None, file_format=None, file_client_args=None, **kwargs): - """Dump data to json/yaml/pickle strings or files. - - This method provides a unified api for dumping data as strings or to files, - and also supports custom arguments for each file format. - - Note: - In v1.3.16 and later, ``dump`` supports dumping data as strings or to - files which is saved to different backends. - - Args: - obj (any): The python object to be dumped. - file (str or :obj:`Path` or file-like object, optional): If not - specified, then the object is dumped to a str, otherwise to a file - specified by the filename or file-like object. - file_format (str, optional): Same as :func:`load`. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dump('hello world', '/path/of/your/file') # disk - >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel - - Returns: - bool: True for success, False otherwise. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None: - if is_str(file): - file_format = file.split('.')[-1] - elif file is None: - raise ValueError( - 'file_format must be specified since file is None') - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if file is None: - return handler.dump_to_str(obj, **kwargs) - elif is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put_text(f.getvalue(), file) - else: - with BytesIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put(f.getvalue(), file) - elif hasattr(file, 'write'): - handler.dump_to_fileobj(obj, file, **kwargs) - else: - raise TypeError('"file" must be a filename str or a file-object') - - -def _register_handler(handler, file_formats): - """Register a handler for some file extensions. - - Args: - handler (:obj:`BaseFileHandler`): Handler to be registered. - file_formats (str or list[str]): File formats to be handled by this - handler. - """ - if not isinstance(handler, BaseFileHandler): - raise TypeError( - f'handler must be a child of BaseFileHandler, not {type(handler)}') - if isinstance(file_formats, str): - file_formats = [file_formats] - if not is_list_of(file_formats, str): - raise TypeError('file_formats must be a str or a list of str') - for ext in file_formats: - file_handlers[ext] = handler - - -def register_handler(file_formats, **kwargs): - - def wrap(cls): - _register_handler(cls(**kwargs), file_formats) - return cls - - return wrap diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/parse.py b/cv/super_resolution/esrgan/pytorch/mmcv/fileio/parse.py deleted file mode 100755 index f60f0d611..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/fileio/parse.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from io import StringIO - -from .file_client import FileClient - - -def list_from_file(filename, - prefix='', - offset=0, - max_num=0, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a list of strings. - - Note: - In v1.3.16 and later, ``list_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a list for strings. - - Args: - filename (str): Filename. - prefix (str): The prefix to be inserted to the beginning of each item. - offset (int): The offset of lines. - max_num (int): The maximum number of lines to be read, - zeros and negatives mean no limitation. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> list_from_file('/path/of/your/file') # disk - ['hello', 'world'] - >>> list_from_file('s3://path/of/your/file') # ceph or petrel - ['hello', 'world'] - - Returns: - list[str]: A list of strings. - """ - cnt = 0 - item_list = [] - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for _ in range(offset): - f.readline() - for line in f: - if 0 < max_num <= cnt: - break - item_list.append(prefix + line.rstrip('\n\r')) - cnt += 1 - return item_list - - -def dict_from_file(filename, - key_type=str, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a dict. - - Each line of the text file will be two or more columns split by - whitespaces or tabs. The first column will be parsed as dict keys, and - the following columns will be parsed as dict values. - - Note: - In v1.3.16 and later, ``dict_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a dict. - - Args: - filename(str): Filename. - key_type(type): Type of the dict keys. str is user by default and - type conversion will be performed if specified. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dict_from_file('/path/of/your/file') # disk - {'key1': 'value1', 'key2': 'value2'} - >>> dict_from_file('s3://path/of/your/file') # ceph or petrel - {'key1': 'value1', 'key2': 'value2'} - - Returns: - dict: The parsed contents. - """ - mapping = {} - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for line in f: - items = line.rstrip('\n').split() - assert len(items) >= 2 - key = key_type(items[0]) - val = items[1:] if len(items) > 2 else items[1] - mapping[key] = val - return mapping diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/image/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/image/__init__.py deleted file mode 100755 index d0051d609..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/image/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, - gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, - rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) -from .geometric import (cutout, imcrop, imflip, imflip_, impad, - impad_to_multiple, imrescale, imresize, imresize_like, - imresize_to_multiple, imrotate, imshear, imtranslate, - rescale_size) -from .io import imfrombytes, imread, imwrite, supported_backends, use_backend -from .misc import tensor2imgs -from .photometric import (adjust_brightness, adjust_color, adjust_contrast, - adjust_lighting, adjust_sharpness, auto_contrast, - clahe, imdenormalize, imequalize, iminvert, - imnormalize, imnormalize_, lut_transform, posterize, - solarize) - -__all__ = [ - 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', - 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', - 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', - 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', - 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', - 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', - 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', - 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', - 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', - 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/image/colorspace.py b/cv/super_resolution/esrgan/pytorch/mmcv/image/colorspace.py deleted file mode 100755 index 814533952..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/image/colorspace.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def imconvert(img, src, dst): - """Convert an image from the src colorspace to dst colorspace. - - Args: - img (ndarray): The input image. - src (str): The source colorspace, e.g., 'rgb', 'hsv'. - dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. - - Returns: - ndarray: The converted image. - """ - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - out_img = cv2.cvtColor(img, code) - return out_img - - -def bgr2gray(img, keepdim=False): - """Convert a BGR image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def rgb2gray(img, keepdim=False): - """Convert a RGB image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def gray2bgr(img): - """Convert a grayscale image to BGR image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted BGR image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return out_img - - -def gray2rgb(img): - """Convert a grayscale image to RGB image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted RGB image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) - return out_img - - -def _convert_input_type_range(img): - """Convert the type and range of the input image. - - It converts the input image to np.float32 type and range of [0, 1]. - It is mainly used for pre-processing the input image in colorspace - conversion functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - (ndarray): The converted image with type of np.float32 and range of - [0, 1]. - """ - img_type = img.dtype - img = img.astype(np.float32) - if img_type == np.float32: - pass - elif img_type == np.uint8: - img /= 255. - else: - raise TypeError('The img type should be np.float32 or np.uint8, ' - f'but got {img_type}') - return img - - -def _convert_output_type_range(img, dst_type): - """Convert the type and range of the image according to dst_type. - - It converts the image to desired type and range. If `dst_type` is np.uint8, - images will be converted to np.uint8 type with range [0, 255]. If - `dst_type` is np.float32, it converts the image to np.float32 type with - range [0, 1]. - It is mainly used for post-processing images in colorspace conversion - functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The image to be converted with np.float32 type and - range [0, 255]. - dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it - converts the image to np.uint8 type with range [0, 255]. If - dst_type is np.float32, it converts the image to np.float32 type - with range [0, 1]. - - Returns: - (ndarray): The converted image with desired type and range. - """ - if dst_type not in (np.uint8, np.float32): - raise TypeError('The dst_type should be np.float32 or np.uint8, ' - f'but got {dst_type}') - if dst_type == np.uint8: - img = img.round() - else: - img /= 255. - return img.astype(dst_type) - - -def rgb2ycbcr(img, y_only=False): - """Convert a RGB image to YCbCr image. - - This function produces the same results as Matlab's `rgb2ycbcr` function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 - else: - out_img = np.matmul( - img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], - [24.966, 112.0, -18.214]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def bgr2ycbcr(img, y_only=False): - """Convert a BGR image to YCbCr image. - - The bgr version of rgb2ycbcr. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 - else: - out_img = np.matmul( - img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], - [65.481, -37.797, 112.0]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2rgb(img): - """Convert a YCbCr image to RGB image. - - This function produces the same results as Matlab's ycbcr2rgb function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted RGB image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0, -0.00153632, 0.00791071], - [0.00625893, -0.00318811, 0]]) * 255.0 + [ - -222.921, 135.576, -276.836 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2bgr(img): - """Convert a YCbCr image to BGR image. - - The bgr version of ycbcr2rgb. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted BGR image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0.00791071, -0.00153632, 0], - [0, -0.00318811, 0.00625893]]) * 255.0 + [ - -276.836, 135.576, -222.921 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def convert_color_factory(src, dst): - - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - - def convert_color(img): - out_img = cv2.cvtColor(img, code) - return out_img - - convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} - image. - - Args: - img (ndarray or str): The input image. - - Returns: - ndarray: The converted {dst.upper()} image. - """ - - return convert_color - - -bgr2rgb = convert_color_factory('bgr', 'rgb') - -rgb2bgr = convert_color_factory('rgb', 'bgr') - -bgr2hsv = convert_color_factory('bgr', 'hsv') - -hsv2bgr = convert_color_factory('hsv', 'bgr') - -bgr2hls = convert_color_factory('bgr', 'hls') - -hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/image/geometric.py b/cv/super_resolution/esrgan/pytorch/mmcv/image/geometric.py deleted file mode 100755 index cf97c201c..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/image/geometric.py +++ /dev/null @@ -1,728 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers - -import cv2 -import numpy as np - -from ..utils import to_2tuple -from .io import imread_backend - -try: - from PIL import Image -except ImportError: - Image = None - - -def _scale_size(size, scale): - """Rescale a size by a ratio. - - Args: - size (tuple[int]): (w, h). - scale (float | tuple(float)): Scaling factor. - - Returns: - tuple[int]: scaled size. - """ - if isinstance(scale, (float, int)): - scale = (scale, scale) - w, h = size - return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) - - -cv2_interp_codes = { - 'nearest': cv2.INTER_NEAREST, - 'bilinear': cv2.INTER_LINEAR, - 'bicubic': cv2.INTER_CUBIC, - 'area': cv2.INTER_AREA, - 'lanczos': cv2.INTER_LANCZOS4 -} - -if Image is not None: - pillow_interp_codes = { - 'nearest': Image.NEAREST, - 'bilinear': Image.BILINEAR, - 'bicubic': Image.BICUBIC, - 'box': Image.BOX, - 'lanczos': Image.LANCZOS, - 'hamming': Image.HAMMING - } - - -def imresize(img, - size, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image to a given size. - - Args: - img (ndarray): The input image. - size (tuple[int]): Target size (w, h). - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if backend is None: - backend = imread_backend - if backend not in ['cv2', 'pillow']: - raise ValueError(f'backend: {backend} is not supported for resize.' - f"Supported backends are 'cv2', 'pillow'") - - if backend == 'pillow': - assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' - pil_image = Image.fromarray(img) - pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) - resized_img = np.array(pil_image) - else: - resized_img = cv2.resize( - img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) - if not return_scale: - return resized_img - else: - w_scale = size[0] / w - h_scale = size[1] / h - return resized_img, w_scale, h_scale - - -def imresize_to_multiple(img, - divisor, - size=None, - scale_factor=None, - keep_ratio=False, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image according to a given size or scale factor and then rounds - up the the resized or rescaled image size to the nearest value that can be - divided by the divisor. - - Args: - img (ndarray): The input image. - divisor (int | tuple): Resized image size will be a multiple of - divisor. If divisor is a tuple, divisor should be - (w_divisor, h_divisor). - size (None | int | tuple[int]): Target size (w, h). Default: None. - scale_factor (None | float | tuple[float]): Multiplier for spatial - size. Should match input size if it is a tuple and the 2D style is - (w_scale_factor, h_scale_factor). Default: None. - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: False. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if size is not None and scale_factor is not None: - raise ValueError('only one of size or scale_factor should be defined') - elif size is None and scale_factor is None: - raise ValueError('one of size or scale_factor should be defined') - elif size is not None: - size = to_2tuple(size) - if keep_ratio: - size = rescale_size((w, h), size, return_scale=False) - else: - size = _scale_size((w, h), scale_factor) - - divisor = to_2tuple(divisor) - size = tuple([int(np.ceil(s / d)) * d for s, d in zip(size, divisor)]) - resized_img, w_scale, h_scale = imresize( - img, - size, - return_scale=True, - interpolation=interpolation, - out=out, - backend=backend) - if return_scale: - return resized_img, w_scale, h_scale - else: - return resized_img - - -def imresize_like(img, - dst_img, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image to the same size of a given image. - - Args: - img (ndarray): The input image. - dst_img (ndarray): The target image. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = dst_img.shape[:2] - return imresize(img, (w, h), return_scale, interpolation, backend=backend) - - -def rescale_size(old_size, scale, return_scale=False): - """Calculate the new size to be rescaled to. - - Args: - old_size (tuple[int]): The old size (w, h) of image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image size. - - Returns: - tuple[int]: The new rescaled image size. - """ - w, h = old_size - if isinstance(scale, (float, int)): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - scale_factor = scale - elif isinstance(scale, tuple): - max_long_edge = max(scale) - max_short_edge = min(scale) - scale_factor = min(max_long_edge / max(h, w), - max_short_edge / min(h, w)) - else: - raise TypeError( - f'Scale must be a number or tuple of int, but got {type(scale)}') - - new_size = _scale_size((w, h), scale_factor) - - if return_scale: - return new_size, scale_factor - else: - return new_size - - -def imrescale(img, - scale, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image while keeping the aspect ratio. - - Args: - img (ndarray): The input image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - ndarray: The rescaled image. - """ - h, w = img.shape[:2] - new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) - rescaled_img = imresize( - img, new_size, interpolation=interpolation, backend=backend) - if return_scale: - return rescaled_img, scale_factor - else: - return rescaled_img - - -def imflip(img, direction='horizontal'): - """Flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image. - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return np.flip(img, axis=1) - elif direction == 'vertical': - return np.flip(img, axis=0) - else: - return np.flip(img, axis=(0, 1)) - - -def imflip_(img, direction='horizontal'): - """Inplace flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image (inplace). - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return cv2.flip(img, 1, img) - elif direction == 'vertical': - return cv2.flip(img, 0, img) - else: - return cv2.flip(img, -1, img) - - -def imrotate(img, - angle, - center=None, - scale=1.0, - border_value=0, - interpolation='bilinear', - auto_bound=False): - """Rotate an image. - - Args: - img (ndarray): Image to be rotated. - angle (float): Rotation angle in degrees, positive values mean - clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. - scale (float): Isotropic scale factor. - border_value (int): Border value. - interpolation (str): Same as :func:`resize`. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. - - Returns: - ndarray: The rotated image. - """ - if center is not None and auto_bound: - raise ValueError('`auto_bound` conflicts with `center`') - h, w = img.shape[:2] - if center is None: - center = ((w - 1) * 0.5, (h - 1) * 0.5) - assert isinstance(center, tuple) - - matrix = cv2.getRotationMatrix2D(center, -angle, scale) - if auto_bound: - cos = np.abs(matrix[0, 0]) - sin = np.abs(matrix[0, 1]) - new_w = h * sin + w * cos - new_h = h * cos + w * sin - matrix[0, 2] += (new_w - w) * 0.5 - matrix[1, 2] += (new_h - h) * 0.5 - w = int(np.round(new_w)) - h = int(np.round(new_h)) - rotated = cv2.warpAffine( - img, - matrix, (w, h), - flags=cv2_interp_codes[interpolation], - borderValue=border_value) - return rotated - - -def bbox_clip(bboxes, img_shape): - """Clip bboxes to fit the image shape. - - Args: - bboxes (ndarray): Shape (..., 4*k) - img_shape (tuple[int]): (height, width) of the image. - - Returns: - ndarray: Clipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) - cmin[0::2] = img_shape[1] - 1 - cmin[1::2] = img_shape[0] - 1 - clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) - return clipped_bboxes - - -def bbox_scaling(bboxes, scale, clip_shape=None): - """Scaling bboxes w.r.t the box center. - - Args: - bboxes (ndarray): Shape(..., 4). - scale (float): Scaling factor. - clip_shape (tuple[int], optional): If specified, bboxes that exceed the - boundary will be clipped according to the given shape (h, w). - - Returns: - ndarray: Scaled bboxes. - """ - if float(scale) == 1.0: - scaled_bboxes = bboxes.copy() - else: - w = bboxes[..., 2] - bboxes[..., 0] + 1 - h = bboxes[..., 3] - bboxes[..., 1] + 1 - dw = (w * (scale - 1)) * 0.5 - dh = (h * (scale - 1)) * 0.5 - scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) - if clip_shape is not None: - return bbox_clip(scaled_bboxes, clip_shape) - else: - return scaled_bboxes - - -def imcrop(img, bboxes, scale=1.0, pad_fill=None): - """Crop image patches. - - 3 steps: scale the bboxes -> clip bboxes -> crop and pad. - - Args: - img (ndarray): Image to be cropped. - bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. - scale (float, optional): Scale ratio of bboxes, the default value - 1.0 means no padding. - pad_fill (Number | list[Number]): Value to be filled for padding. - Default: None, which means no padding. - - Returns: - list[ndarray] | ndarray: The cropped image patches. - """ - chn = 1 if img.ndim == 2 else img.shape[2] - if pad_fill is not None: - if isinstance(pad_fill, (int, float)): - pad_fill = [pad_fill for _ in range(chn)] - assert len(pad_fill) == chn - - _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes - scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) - clipped_bbox = bbox_clip(scaled_bboxes, img.shape) - - patches = [] - for i in range(clipped_bbox.shape[0]): - x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) - if pad_fill is None: - patch = img[y1:y2 + 1, x1:x2 + 1, ...] - else: - _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) - if chn == 1: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) - else: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) - patch = np.array( - pad_fill, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - x_start = 0 if _x1 >= 0 else -_x1 - y_start = 0 if _y1 >= 0 else -_y1 - w = x2 - x1 + 1 - h = y2 - y1 + 1 - patch[y_start:y_start + h, x_start:x_start + w, - ...] = img[y1:y1 + h, x1:x1 + w, ...] - patches.append(patch) - - if bboxes.ndim == 1: - return patches[0] - else: - return patches - - -def impad(img, - *, - shape=None, - padding=None, - pad_val=0, - padding_mode='constant'): - """Pad the given image to a certain shape or pad on all sides with - specified padding mode and padding value. - - Args: - img (ndarray): Image to be padded. - shape (tuple[int]): Expected padding shape (h, w). Default: None. - padding (int or tuple[int]): Padding on each border. If a single int is - provided this is used to pad all borders. If tuple of length 2 is - provided this is the padding on left/right and top/bottom - respectively. If a tuple of length 4 is provided this is the - padding for the left, top, right and bottom borders respectively. - Default: None. Note that `shape` and `padding` can not be both - set. - pad_val (Number | Sequence[Number]): Values to be filled in padding - areas when padding_mode is 'constant'. Default: 0. - padding_mode (str): Type of padding. Should be: constant, edge, - reflect or symmetric. Default: constant. - - - constant: pads with a constant value, this value is specified - with pad_val. - - edge: pads with the last value at the edge of the image. - - reflect: pads with reflection of image without repeating the - last value on the edge. For example, padding [1, 2, 3, 4] - with 2 elements on both sides in reflect mode will result - in [3, 2, 1, 2, 3, 4, 3, 2]. - - symmetric: pads with reflection of image repeating the last - value on the edge. For example, padding [1, 2, 3, 4] with - 2 elements on both sides in symmetric mode will result in - [2, 1, 1, 2, 3, 4, 4, 3] - - Returns: - ndarray: The padded image. - """ - - assert (shape is not None) ^ (padding is not None) - if shape is not None: - padding = (0, 0, shape[1] - img.shape[1], shape[0] - img.shape[0]) - - # check pad_val - if isinstance(pad_val, tuple): - assert len(pad_val) == img.shape[-1] - elif not isinstance(pad_val, numbers.Number): - raise TypeError('pad_val must be a int or a tuple. ' - f'But received {type(pad_val)}') - - # check padding - if isinstance(padding, tuple) and len(padding) in [2, 4]: - if len(padding) == 2: - padding = (padding[0], padding[1], padding[0], padding[1]) - elif isinstance(padding, numbers.Number): - padding = (padding, padding, padding, padding) - else: - raise ValueError('Padding must be a int or a 2, or 4 element tuple.' - f'But received {padding}') - - # check padding mode - assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] - - border_type = { - 'constant': cv2.BORDER_CONSTANT, - 'edge': cv2.BORDER_REPLICATE, - 'reflect': cv2.BORDER_REFLECT_101, - 'symmetric': cv2.BORDER_REFLECT - } - img = cv2.copyMakeBorder( - img, - padding[1], - padding[3], - padding[0], - padding[2], - border_type[padding_mode], - value=pad_val) - - return img - - -def impad_to_multiple(img, divisor, pad_val=0): - """Pad an image to ensure each edge to be multiple to some number. - - Args: - img (ndarray): Image to be padded. - divisor (int): Padded image edges will be multiple to divisor. - pad_val (Number | Sequence[Number]): Same as :func:`impad`. - - Returns: - ndarray: The padded image. - """ - pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor - pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor - return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) - - -def cutout(img, shape, pad_val=0): - """Randomly cut out a rectangle from the original img. - - Args: - img (ndarray): Image to be cutout. - shape (int | tuple[int]): Expected cutout shape (h, w). If given as a - int, the value will be used for both h and w. - pad_val (int | float | tuple[int | float]): Values to be filled in the - cut area. Defaults to 0. - - Returns: - ndarray: The cutout image. - """ - - channels = 1 if img.ndim == 2 else img.shape[2] - if isinstance(shape, int): - cut_h, cut_w = shape, shape - else: - assert isinstance(shape, tuple) and len(shape) == 2, \ - f'shape must be a int or a tuple with length 2, but got type ' \ - f'{type(shape)} instead.' - cut_h, cut_w = shape - if isinstance(pad_val, (int, float)): - pad_val = tuple([pad_val] * channels) - elif isinstance(pad_val, tuple): - assert len(pad_val) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(pad_val), channels) - else: - raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') - - img_h, img_w = img.shape[:2] - y0 = np.random.uniform(img_h) - x0 = np.random.uniform(img_w) - - y1 = int(max(0, y0 - cut_h / 2.)) - x1 = int(max(0, x0 - cut_w / 2.)) - y2 = min(img_h, y1 + cut_h) - x2 = min(img_w, x1 + cut_w) - - if img.ndim == 2: - patch_shape = (y2 - y1, x2 - x1) - else: - patch_shape = (y2 - y1, x2 - x1, channels) - - img_cutout = img.copy() - patch = np.array( - pad_val, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - img_cutout[y1:y2, x1:x2, ...] = patch - - return img_cutout - - -def _get_shear_matrix(magnitude, direction='horizontal'): - """Generate the shear matrix for transformation. - - Args: - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - - Returns: - ndarray: The shear matrix with dtype float32. - """ - if direction == 'horizontal': - shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) - elif direction == 'vertical': - shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) - return shear_matrix - - -def imshear(img, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear an image. - - Args: - img (ndarray): Image to be sheared with format (h, w) - or (h, w, c). - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The sheared image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`') - shear_matrix = _get_shear_matrix(magnitude, direction) - sheared = cv2.warpAffine( - img, - shear_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. shearing masks whose channels large - # than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return sheared - - -def _get_translate_matrix(offset, direction='horizontal'): - """Generate the translate matrix. - - Args: - offset (int | float): The offset used for translate. - direction (str): The translate direction, either - "horizontal" or "vertical". - - Returns: - ndarray: The translate matrix with dtype float32. - """ - if direction == 'horizontal': - translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) - elif direction == 'vertical': - translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) - return translate_matrix - - -def imtranslate(img, - offset, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Translate an image. - - Args: - img (ndarray): Image to be translated with format - (h, w) or (h, w, c). - offset (int | float): The offset used for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The translated image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`.') - translate_matrix = _get_translate_matrix(offset, direction) - translated = cv2.warpAffine( - img, - translate_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. translating masks whose channels - # large than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return translated diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/image/io.py b/cv/super_resolution/esrgan/pytorch/mmcv/image/io.py deleted file mode 100755 index d47aaa845..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/image/io.py +++ /dev/null @@ -1,258 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os.path as osp -from pathlib import Path - -import cv2 -import numpy as np -from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, - IMREAD_UNCHANGED) - -from mmcv.utils import check_file_exist, is_str, mkdir_or_exist - -try: - from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG -except ImportError: - TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None - -try: - from PIL import Image, ImageOps -except ImportError: - Image = None - -try: - import tifffile -except ImportError: - tifffile = None - -jpeg = None -supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] - -imread_flags = { - 'color': IMREAD_COLOR, - 'grayscale': IMREAD_GRAYSCALE, - 'unchanged': IMREAD_UNCHANGED, - 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, - 'grayscale_ignore_orientation': - IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE -} - -imread_backend = 'cv2' - - -def use_backend(backend): - """Select a backend for image decoding. - - Args: - backend (str): The image decoding backend type. Options are `cv2`, - `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) - and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` - file format. - """ - assert backend in supported_backends - global imread_backend - imread_backend = backend - if imread_backend == 'turbojpeg': - if TurboJPEG is None: - raise ImportError('`PyTurboJPEG` is not installed') - global jpeg - if jpeg is None: - jpeg = TurboJPEG() - elif imread_backend == 'pillow': - if Image is None: - raise ImportError('`Pillow` is not installed') - elif imread_backend == 'tifffile': - if tifffile is None: - raise ImportError('`tifffile` is not installed') - - -def _jpegflag(flag='color', channel_order='bgr'): - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'color': - if channel_order == 'bgr': - return TJPF_BGR - elif channel_order == 'rgb': - return TJCS_RGB - elif flag == 'grayscale': - return TJPF_GRAY - else: - raise ValueError('flag must be "color" or "grayscale"') - - -def _pillow2array(img, flag='color', channel_order='bgr'): - """Convert a pillow image to numpy array. - - Args: - img (:obj:`PIL.Image.Image`): The image loaded using PIL - flag (str): Flags specifying the color type of a loaded image, - candidates are 'color', 'grayscale' and 'unchanged'. - Default to 'color'. - channel_order (str): The channel order of the output image array, - candidates are 'bgr' and 'rgb'. Default to 'bgr'. - - Returns: - np.ndarray: The converted numpy array - """ - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'unchanged': - array = np.array(img) - if array.ndim >= 3 and array.shape[2] >= 3: # color image - array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR - else: - # Handle exif orientation tag - if flag in ['color', 'grayscale']: - img = ImageOps.exif_transpose(img) - # If the image mode is not 'RGB', convert it to 'RGB' first. - if img.mode != 'RGB': - if img.mode != 'LA': - # Most formats except 'LA' can be directly converted to RGB - img = img.convert('RGB') - else: - # When the mode is 'LA', the default conversion will fill in - # the canvas with black, which sometimes shadows black objects - # in the foreground. - # - # Therefore, a random color (124, 117, 104) is used for canvas - img_rgba = img.convert('RGBA') - img = Image.new('RGB', img_rgba.size, (124, 117, 104)) - img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha - if flag in ['color', 'color_ignore_orientation']: - array = np.array(img) - if channel_order != 'rgb': - array = array[:, :, ::-1] # RGB to BGR - elif flag in ['grayscale', 'grayscale_ignore_orientation']: - img = img.convert('L') - array = np.array(img) - else: - raise ValueError( - 'flag must be "color", "grayscale", "unchanged", ' - f'"color_ignore_orientation" or "grayscale_ignore_orientation"' - f' but got {flag}') - return array - - -def imread(img_or_path, flag='color', channel_order='bgr', backend=None): - """Read an image. - - Args: - img_or_path (ndarray or str or Path): Either a numpy array or str or - pathlib.Path. If it is a numpy array (loaded image), then - it will be returned as is. - flag (str): Flags specifying the color type of a loaded image, - candidates are `color`, `grayscale`, `unchanged`, - `color_ignore_orientation` and `grayscale_ignore_orientation`. - By default, `cv2` and `pillow` backend would rotate the image - according to its EXIF info unless called with `unchanged` or - `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend - always ignore image's EXIF info regardless of the flag. - The `turbojpeg` backend only supports `color` and `grayscale`. - channel_order (str): Order of channel, candidates are `bgr` and `rgb`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. - If backend is None, the global imread_backend specified by - ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - ndarray: Loaded image array. - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError(f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow'") - if isinstance(img_or_path, Path): - img_or_path = str(img_or_path) - - if isinstance(img_or_path, np.ndarray): - return img_or_path - elif is_str(img_or_path): - check_file_exist(img_or_path, - f'img file does not exist: {img_or_path}') - if backend == 'turbojpeg': - with open(img_or_path, 'rb') as in_file: - img = jpeg.decode(in_file.read(), - _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - img = Image.open(img_or_path) - img = _pillow2array(img, flag, channel_order) - return img - elif backend == 'tifffile': - img = tifffile.imread(img_or_path) - return img - else: - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imread(img_or_path, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - else: - raise TypeError('"img" must be a numpy array or a str or ' - 'a pathlib.Path object') - - -def imfrombytes(content, flag='color', channel_order='bgr', backend=None): - """Read an image from bytes. - - Args: - content (bytes): Image bytes got from files or other streams. - flag (str): Same as :func:`imread`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `None`. If backend is None, the - global imread_backend specified by ``mmcv.use_backend()`` will be - used. Default: None. - - Returns: - ndarray: Loaded image array. - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError(f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow'") - if backend == 'turbojpeg': - img = jpeg.decode(content, _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - buff = io.BytesIO(content) - img = Image.open(buff) - img = _pillow2array(img, flag, channel_order) - return img - else: - img_np = np.frombuffer(content, np.uint8) - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imdecode(img_np, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - - -def imwrite(img, file_path, params=None, auto_mkdir=True): - """Write image to file. - - Args: - img (ndarray): Image array to be written. - file_path (str): Image file path. - params (None or list): Same as opencv :func:`imwrite` interface. - auto_mkdir (bool): If the parent folder of `file_path` does not exist, - whether to create it automatically. - - Returns: - bool: Successful or not. - """ - if auto_mkdir: - dir_name = osp.abspath(osp.dirname(file_path)) - mkdir_or_exist(dir_name) - return cv2.imwrite(file_path, img, params) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/image/misc.py b/cv/super_resolution/esrgan/pytorch/mmcv/image/misc.py deleted file mode 100755 index dfc4a9c6e..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/image/misc.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - -import mmcv - -try: - import torch -except ImportError: - torch = None - - -def tensor2imgs(tensor, mean=(0, 0, 0), std=(1, 1, 1), to_rgb=True): - """Convert tensor to 3-channel images. - - Args: - tensor (torch.Tensor): Tensor that contains multiple images, shape ( - N, C, H, W). - mean (tuple[float], optional): Mean of images. Defaults to (0, 0, 0). - std (tuple[float], optional): Standard deviation of images. - Defaults to (1, 1, 1). - to_rgb (bool, optional): Whether the tensor was converted to RGB - format in the first place. If so, convert it back to BGR. - Defaults to True. - - Returns: - list[np.ndarray]: A list that contains multiple images. - """ - - if torch is None: - raise RuntimeError('pytorch is not installed') - assert torch.is_tensor(tensor) and tensor.ndim == 4 - assert len(mean) == 3 - assert len(std) == 3 - - num_imgs = tensor.size(0) - mean = np.array(mean, dtype=np.float32) - std = np.array(std, dtype=np.float32) - imgs = [] - for img_id in range(num_imgs): - img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) - img = mmcv.imdenormalize( - img, mean, std, to_bgr=to_rgb).astype(np.uint8) - imgs.append(np.ascontiguousarray(img)) - return imgs diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/image/photometric.py b/cv/super_resolution/esrgan/pytorch/mmcv/image/photometric.py deleted file mode 100755 index 5085d0120..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/image/photometric.py +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - -from ..utils import is_tuple_of -from .colorspace import bgr2gray, gray2bgr - - -def imnormalize(img, mean, std, to_rgb=True): - """Normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - img = img.copy().astype(np.float32) - return imnormalize_(img, mean, std, to_rgb) - - -def imnormalize_(img, mean, std, to_rgb=True): - """Inplace normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - # cv2 inplace normalization does not accept uint8 - assert img.dtype != np.uint8 - mean = np.float64(mean.reshape(1, -1)) - stdinv = 1 / np.float64(std.reshape(1, -1)) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - cv2.subtract(img, mean, img) # inplace - cv2.multiply(img, stdinv, img) # inplace - return img - - -def imdenormalize(img, mean, std, to_bgr=True): - assert img.dtype != np.uint8 - mean = mean.reshape(1, -1).astype(np.float64) - std = std.reshape(1, -1).astype(np.float64) - img = cv2.multiply(img, std) # make a copy - cv2.add(img, mean, img) # inplace - if to_bgr: - cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace - return img - - -def iminvert(img): - """Invert (negate) an image. - - Args: - img (ndarray): Image to be inverted. - - Returns: - ndarray: The inverted image. - """ - return np.full_like(img, 255) - img - - -def solarize(img, thr=128): - """Solarize an image (invert all pixel values above a threshold) - - Args: - img (ndarray): Image to be solarized. - thr (int): Threshold for solarizing (0 - 255). - - Returns: - ndarray: The solarized image. - """ - img = np.where(img < thr, img, 255 - img) - return img - - -def posterize(img, bits): - """Posterize an image (reduce the number of bits for each color channel) - - Args: - img (ndarray): Image to be posterized. - bits (int): Number of bits (1 to 8) to use for posterizing. - - Returns: - ndarray: The posterized image. - """ - shift = 8 - bits - img = np.left_shift(np.right_shift(img, shift), shift) - return img - - -def adjust_color(img, alpha=1, beta=None, gamma=0): - r"""It blends the source image and its gray image: - - .. math:: - output = img * alpha + gray\_img * beta + gamma - - Args: - img (ndarray): The input source image. - alpha (int | float): Weight for the source image. Default 1. - beta (int | float): Weight for the converted gray image. - If None, it's assigned the value (1 - `alpha`). - gamma (int | float): Scalar added to each sum. - Same as :func:`cv2.addWeighted`. Default 0. - - Returns: - ndarray: Colored image which has the same size and dtype as input. - """ - gray_img = bgr2gray(img) - gray_img = np.tile(gray_img[..., None], [1, 1, 3]) - if beta is None: - beta = 1 - alpha - colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) - if not colored_img.dtype == np.uint8: - # Note when the dtype of `img` is not the default `np.uint8` - # (e.g. np.float32), the value in `colored_img` got from cv2 - # is not guaranteed to be in range [0, 255], so here clip - # is needed. - colored_img = np.clip(colored_img, 0, 255) - return colored_img - - -def imequalize(img): - """Equalize the image histogram. - - This function applies a non-linear mapping to the input image, - in order to create a uniform distribution of grayscale values - in the output image. - - Args: - img (ndarray): Image to be equalized. - - Returns: - ndarray: The equalized image. - """ - - def _scale_channel(im, c): - """Scale the data in the corresponding channel.""" - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # For computing the step, filter out the nonzeros. - nonzero_histo = histo[histo > 0] - step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 - if not step: - lut = np.array(range(256)) - else: - # Compute the cumulative sum, shifted by step // 2 - # and then normalized by step. - lut = (np.cumsum(histo) + (step // 2)) // step - # Shift lut, prepending with 0. - lut = np.concatenate([[0], lut[:-1]], 0) - # handle potential integer overflow - lut[lut > 255] = 255 - # If step is zero, return the original image. - # Otherwise, index from lut. - return np.where(np.equal(step, 0), im, lut[im]) - - # Scales each channel independently and then stacks - # the result. - s1 = _scale_channel(img, 0) - s2 = _scale_channel(img, 1) - s3 = _scale_channel(img, 2) - equalized_img = np.stack([s1, s2, s3], axis=-1) - return equalized_img.astype(img.dtype) - - -def adjust_brightness(img, factor=1.): - """Adjust image brightness. - - This function controls the brightness of an image. An - enhancement factor of 0.0 gives a black image. - A factor of 1.0 gives the original image. This function - blends the source image and the degenerated black image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be brightened. - factor (float): A value controls the enhancement. - Factor 1.0 returns the original image, lower - factors mean less color (brightness, contrast, - etc), and higher values more. Default 1. - - Returns: - ndarray: The brightened image. - """ - degenerated = np.zeros_like(img) - # Note manually convert the dtype to np.float32, to - # achieve as close results as PIL.ImageEnhance.Brightness. - # Set beta=1-factor, and gamma=0 - brightened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - brightened_img = np.clip(brightened_img, 0, 255) - return brightened_img.astype(img.dtype) - - -def adjust_contrast(img, factor=1.): - """Adjust image contrast. - - This function controls the contrast of an image. An - enhancement factor of 0.0 gives a solid grey - image. A factor of 1.0 gives the original image. It - blends the source image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be contrasted. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - - Returns: - ndarray: The contrasted image. - """ - gray_img = bgr2gray(img) - hist = np.histogram(gray_img, 256, (0, 255))[0] - mean = round(np.sum(gray_img) / np.sum(hist)) - degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) - degenerated = gray2bgr(degenerated) - contrasted_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - contrasted_img = np.clip(contrasted_img, 0, 255) - return contrasted_img.astype(img.dtype) - - -def auto_contrast(img, cutoff=0): - """Auto adjust image contrast. - - This function maximize (normalize) image contrast by first removing cutoff - percent of the lightest and darkest pixels from the histogram and remapping - the image so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - Args: - img (ndarray): Image to be contrasted. BGR order. - cutoff (int | float | tuple): The cutoff percent of the lightest and - darkest pixels to be removed. If given as tuple, it shall be - (low, high). Otherwise, the single value will be used for both. - Defaults to 0. - - Returns: - ndarray: The contrasted image. - """ - - def _auto_contrast_channel(im, c, cutoff): - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # Remove cut-off percent pixels from histo - histo_sum = np.cumsum(histo) - cut_low = histo_sum[-1] * cutoff[0] // 100 - cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 - histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low - histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) - - # Compute mapping - low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] - # If all the values have been cut off, return the origin img - if low >= high: - return im - scale = 255.0 / (high - low) - offset = -low * scale - lut = np.array(range(256)) - lut = lut * scale + offset - lut = np.clip(lut, 0, 255) - return lut[im] - - if isinstance(cutoff, (int, float)): - cutoff = (cutoff, cutoff) - else: - assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ - f'float or tuple, but got {type(cutoff)} instead.' - # Auto adjusts contrast for each channel independently and then stacks - # the result. - s1 = _auto_contrast_channel(img, 0, cutoff) - s2 = _auto_contrast_channel(img, 1, cutoff) - s3 = _auto_contrast_channel(img, 2, cutoff) - contrasted_img = np.stack([s1, s2, s3], axis=-1) - return contrasted_img.astype(img.dtype) - - -def adjust_sharpness(img, factor=1., kernel=None): - """Adjust image sharpness. - - This function controls the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image. A - factor of 1.0 gives the original image. And a factor - of 2.0 gives a sharpened image. It blends the source - image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be sharpened. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - kernel (np.ndarray, optional): Filter kernel to be applied on the img - to obtain the degenerated img. Defaults to None. - - Note: - No value sanity check is enforced on the kernel set by users. So with - an inappropriate kernel, the ``adjust_sharpness`` may fail to perform - the function its name indicates but end up performing whatever - transform determined by the kernel. - - Returns: - ndarray: The sharpened image. - """ - - if kernel is None: - # adopted from PIL.ImageFilter.SMOOTH - kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 - assert isinstance(kernel, np.ndarray), \ - f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' - assert kernel.ndim == 2, \ - f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' - - degenerated = cv2.filter2D(img, -1, kernel) - sharpened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - sharpened_img = np.clip(sharpened_img, 0, 255) - return sharpened_img.astype(img.dtype) - - -def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): - """AlexNet-style PCA jitter. - - This data augmentation is proposed in `ImageNet Classification with Deep - Convolutional Neural Networks - `_. - - Args: - img (ndarray): Image to be adjusted lighting. BGR order. - eigval (ndarray): the eigenvalue of the convariance matrix of pixel - values, respectively. - eigvec (ndarray): the eigenvector of the convariance matrix of pixel - values, respectively. - alphastd (float): The standard deviation for distribution of alpha. - Defaults to 0.1 - to_rgb (bool): Whether to convert img to rgb. - - Returns: - ndarray: The adjusted image. - """ - assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ - f'eigval and eigvec should both be of type np.ndarray, got ' \ - f'{type(eigval)} and {type(eigvec)} instead.' - - assert eigval.ndim == 1 and eigvec.ndim == 2 - assert eigvec.shape == (3, eigval.shape[0]) - n_eigval = eigval.shape[0] - assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ - f'got {type(alphastd)} instead.' - - img = img.copy().astype(np.float32) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - - alpha = np.random.normal(0, alphastd, n_eigval) - alter = eigvec \ - * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ - * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) - alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) - img_adjusted = img + alter - return img_adjusted - - -def lut_transform(img, lut_table): - """Transform array by look-up table. - - The function lut_transform fills the output array with values from the - look-up table. Indices of the entries are taken from the input array. - - Args: - img (ndarray): Image to be transformed. - lut_table (ndarray): look-up table of 256 elements; in case of - multi-channel input array, the table should either have a single - channel (in this case the same table is used for all channels) or - the same number of channels as in the input array. - - Returns: - ndarray: The transformed image. - """ - assert isinstance(img, np.ndarray) - assert 0 <= np.min(img) and np.max(img) <= 255 - assert isinstance(lut_table, np.ndarray) - assert lut_table.shape == (256, ) - - return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) - - -def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - img (ndarray): Image to be processed. - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - - Returns: - ndarray: The processed image. - """ - assert isinstance(img, np.ndarray) - assert img.ndim == 2 - assert isinstance(clip_limit, (float, int)) - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - - clahe = cv2.createCLAHE(clip_limit, tile_grid_size) - return clahe.apply(np.array(img, dtype=np.uint8)) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/parallel/__init__.py deleted file mode 100755 index 2ed2c17ad..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collate import collate -from .data_container import DataContainer -from .data_parallel import MMDataParallel -from .distributed import MMDistributedDataParallel -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter, scatter_kwargs -from .utils import is_module_wrapper - -__all__ = [ - 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', - 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/_functions.py b/cv/super_resolution/esrgan/pytorch/mmcv/parallel/_functions.py deleted file mode 100755 index 9b5a8a444..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/_functions.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import _get_stream - - -def scatter(input, devices, streams=None): - """Scatters tensor across multiple GPUs.""" - if streams is None: - streams = [None] * len(devices) - - if isinstance(input, list): - chunk_size = (len(input) - 1) // len(devices) + 1 - outputs = [ - scatter(input[i], [devices[i // chunk_size]], - [streams[i // chunk_size]]) for i in range(len(input)) - ] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - # TODO: copy to a pinned buffer first (if copying from CPU) - stream = streams[0] if output.numel() > 0 else None - if devices != [-1]: - with torch.cuda.device(devices[0]), torch.cuda.stream(stream): - output = output.cuda(devices[0], non_blocking=True) - else: - # unsqueeze the first dimension thus the tensor's shape is the - # same as those scattered with GPU. - output = output.unsqueeze(0) - return output - else: - raise Exception(f'Unknown type {type(input)}.') - - -def synchronize_stream(output, devices, streams): - if isinstance(output, list): - chunk_size = len(output) // len(devices) - for i in range(len(devices)): - for j in range(chunk_size): - synchronize_stream(output[i * chunk_size + j], [devices[i]], - [streams[i]]) - elif isinstance(output, torch.Tensor): - if output.numel() != 0: - with torch.cuda.device(devices[0]): - main_stream = torch.cuda.current_stream() - main_stream.wait_stream(streams[0]) - output.record_stream(main_stream) - else: - raise Exception(f'Unknown type {type(output)}.') - - -def get_input_device(input): - if isinstance(input, list): - for item in input: - input_device = get_input_device(item) - if input_device != -1: - return input_device - return -1 - elif isinstance(input, torch.Tensor): - return input.get_device() if input.is_cuda else -1 - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_gpus, input): - input_device = get_input_device(input) - streams = None - if input_device == -1 and target_gpus != [-1]: - # Perform CPU to GPU copies in a background stream - streams = [_get_stream(device) for device in target_gpus] - - outputs = scatter(input, target_gpus, streams) - # Synchronize with the copy stream - if streams is not None: - synchronize_stream(outputs, target_gpus, streams) - - return tuple(outputs) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/collate.py b/cv/super_resolution/esrgan/pytorch/mmcv/parallel/collate.py deleted file mode 100755 index ad749197d..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/collate.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence - -import torch -import torch.nn.functional as F -from torch.utils.data.dataloader import default_collate - -from .data_container import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Puts each data field into a tensor/DataContainer with outer dimension - batch size. - - Extend default_collate to add support for - :type:`~mmcv.parallel.DataContainer`. There are 3 cases. - - 1. cpu_only = True, e.g., meta data - 2. cpu_only = False, stack = True, e.g., images tensors - 3. cpu_only = False, stack = False, e.g., gt bboxes - """ - - if not isinstance(batch, Sequence): - raise TypeError(f'{batch.dtype} is not supported.') - - if isinstance(batch[0], DataContainer): - stacked = [] - if batch[0].cpu_only: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer( - stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) - elif batch[0].stack: - for i in range(0, len(batch), samples_per_gpu): - assert isinstance(batch[i].data, torch.Tensor) - - if batch[i].pad_dims is not None: - ndim = batch[i].dim() - assert ndim > batch[i].pad_dims - max_shape = [0 for _ in range(batch[i].pad_dims)] - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = batch[i].size(-dim) - for sample in batch[i:i + samples_per_gpu]: - for dim in range(0, ndim - batch[i].pad_dims): - assert batch[i].size(dim) == sample.size(dim) - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = max(max_shape[dim - 1], - sample.size(-dim)) - padded_samples = [] - for sample in batch[i:i + samples_per_gpu]: - pad = [0 for _ in range(batch[i].pad_dims * 2)] - for dim in range(1, batch[i].pad_dims + 1): - pad[2 * dim - - 1] = max_shape[dim - 1] - sample.size(-dim) - padded_samples.append( - F.pad( - sample.data, pad, value=sample.padding_value)) - stacked.append(default_collate(padded_samples)) - elif batch[i].pad_dims is None: - stacked.append( - default_collate([ - sample.data - for sample in batch[i:i + samples_per_gpu] - ])) - else: - raise ValueError( - 'pad_dims should be either None or integers (1-3)') - - else: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer(stacked, batch[0].stack, batch[0].padding_value) - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - return [collate(samples, samples_per_gpu) for samples in transposed] - elif isinstance(batch[0], Mapping): - return { - key: collate([d[key] for d in batch], samples_per_gpu) - for key in batch[0] - } - else: - return default_collate(batch) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/data_container.py b/cv/super_resolution/esrgan/pytorch/mmcv/parallel/data_container.py deleted file mode 100755 index cedb0d32a..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/data_container.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch - - -def assert_tensor_type(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not isinstance(args[0].data, torch.Tensor): - raise AttributeError( - f'{args[0].__class__.__name__} has no attribute ' - f'{func.__name__} for type {args[0].datatype}') - return func(*args, **kwargs) - - return wrapper - - -class DataContainer: - """A container for any type of objects. - - Typically tensors will be stacked in the collate function and sliced along - some dimension in the scatter function. This behavior has some limitations. - 1. All tensors have to be the same size. - 2. Types are limited (numpy array or Tensor). - - We design `DataContainer` and `MMDataParallel` to overcome these - limitations. The behavior can be either of the following. - - - copy to GPU, pad all tensors to the same size and stack them - - copy to GPU without stacking - - leave the objects as is and pass it to the model - - pad_dims specifies the number of last few dimensions to do padding - """ - - def __init__(self, - data, - stack=False, - padding_value=0, - cpu_only=False, - pad_dims=2): - self._data = data - self._cpu_only = cpu_only - self._stack = stack - self._padding_value = padding_value - assert pad_dims in [None, 1, 2, 3] - self._pad_dims = pad_dims - - def __repr__(self): - return f'{self.__class__.__name__}({repr(self.data)})' - - def __len__(self): - return len(self._data) - - @property - def data(self): - return self._data - - @property - def datatype(self): - if isinstance(self.data, torch.Tensor): - return self.data.type() - else: - return type(self.data) - - @property - def cpu_only(self): - return self._cpu_only - - @property - def stack(self): - return self._stack - - @property - def padding_value(self): - return self._padding_value - - @property - def pad_dims(self): - return self._pad_dims - - @assert_tensor_type - def size(self, *args, **kwargs): - return self.data.size(*args, **kwargs) - - @assert_tensor_type - def dim(self): - return self.data.dim() diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/data_parallel.py b/cv/super_resolution/esrgan/pytorch/mmcv/parallel/data_parallel.py deleted file mode 100755 index 79b5f69b6..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/data_parallel.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from itertools import chain - -from torch.nn.parallel import DataParallel - -from .scatter_gather import scatter_kwargs - - -class MMDataParallel(DataParallel): - """The DataParallel module that supports DataContainer. - - MMDataParallel has two main differences with PyTorch DataParallel: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data during both GPU and CPU inference. - - It implement two more APIs ``train_step()`` and ``val_step()``. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - device_ids (list[int]): Device IDS of modules to be scattered to. - Defaults to None when GPU is not available. - output_device (str | int): Device ID for output. Defaults to None. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MMDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.dim = dim - - def forward(self, *inputs, **kwargs): - """Override the original forward function. - - The main difference lies in the CPU inference where the data in - :class:`DataContainers` will still be gathered. - """ - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module(*inputs[0], **kwargs[0]) - else: - return super().forward(*inputs, **kwargs) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.train_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - 'instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.train_step(*inputs[0], **kwargs[0]) - - def val_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.val_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/distributed.py b/cv/super_resolution/esrgan/pytorch/mmcv/parallel/distributed.py deleted file mode 100755 index b799a213d..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/distributed.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel.distributed import (DistributedDataParallel, - _find_tensors) - -from mmcv import print_log -from mmcv.utils import TORCH_VERSION, digit_version -from .scatter_gather import scatter_kwargs - - -class MMDistributedDataParallel(DistributedDataParallel): - """The DDP module that supports DataContainer. - - MMDDP has two main differences with PyTorch DDP: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data. - - It implement two APIs ``train_step()`` and ``val_step()``. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - """train_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.train_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if getattr(self, 'require_forward_param_sync', True): - self._sync_params() - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.train_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.train_step(*inputs, **kwargs) - - if torch.is_grad_enabled() and getattr( - self, 'require_backward_grad_sync', True): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output - - def val_step(self, *inputs, **kwargs): - """val_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.val_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if getattr(self, 'require_forward_param_sync', True): - self._sync_params() - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.val_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.val_step(*inputs, **kwargs) - - if torch.is_grad_enabled() and getattr( - self, 'require_backward_grad_sync', True): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/distributed_deprecated.py b/cv/super_resolution/esrgan/pytorch/mmcv/parallel/distributed_deprecated.py deleted file mode 100755 index b593d4a9e..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/distributed_deprecated.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn as nn -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter_kwargs - - -@MODULE_WRAPPERS.register_module() -class MMDistributedDataParallel(nn.Module): - - def __init__(self, - module, - dim=0, - broadcast_buffers=True, - bucket_cap_mb=25): - super(MMDistributedDataParallel, self).__init__() - self.module = module - self.dim = dim - self.broadcast_buffers = broadcast_buffers - - self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 - self._sync_params() - - def _dist_broadcast_coalesced(self, tensors, buffer_size): - for tensors in _take_tensors(tensors, buffer_size): - flat_tensors = _flatten_dense_tensors(tensors) - dist.broadcast(flat_tensors, 0) - for tensor, synced in zip( - tensors, _unflatten_dense_tensors(flat_tensors, tensors)): - tensor.copy_(synced) - - def _sync_params(self): - module_states = list(self.module.state_dict().values()) - if len(module_states) > 0: - self._dist_broadcast_coalesced(module_states, - self.broadcast_bucket_size) - if self.broadcast_buffers: - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) < digit_version('1.0')): - buffers = [b.data for b in self.module._all_buffers()] - else: - buffers = [b.data for b in self.module.buffers()] - if len(buffers) > 0: - self._dist_broadcast_coalesced(buffers, - self.broadcast_bucket_size) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/registry.py b/cv/super_resolution/esrgan/pytorch/mmcv/parallel/registry.py deleted file mode 100755 index 144f9fb16..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel, DistributedDataParallel - -from mmcv.utils import Registry - -MODULE_WRAPPERS = Registry('module wrapper') -MODULE_WRAPPERS.register_module(module=DataParallel) -MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/scatter_gather.py b/cv/super_resolution/esrgan/pytorch/mmcv/parallel/scatter_gather.py deleted file mode 100755 index 900ff8856..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import Scatter as OrigScatter - -from ._functions import Scatter -from .data_container import DataContainer - - -def scatter(inputs, target_gpus, dim=0): - """Scatter inputs to target gpus. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_gpus != [-1]: - return OrigScatter.apply(target_gpus, None, dim, obj) - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_gpus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_gpus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_gpus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_gpus, dim) if inputs else [] - kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/utils.py b/cv/super_resolution/esrgan/pytorch/mmcv/parallel/utils.py deleted file mode 100755 index 0f5712cb4..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/parallel/utils.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import MODULE_WRAPPERS - - -def is_module_wrapper(module): - """Check if a module is a module wrapper. - - The following 3 modules in MMCV (and their subclasses) are regarded as - module wrappers: DataParallel, DistributedDataParallel, - MMDistributedDataParallel (the deprecated version). You may add you own - module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: True if the input module is a module wrapper. - """ - module_wrappers = tuple(MODULE_WRAPPERS.module_dict.values()) - return isinstance(module, module_wrappers) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/__init__.py deleted file mode 100755 index 52e4b48d3..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_module import BaseModule, ModuleList, Sequential -from .base_runner import BaseRunner -from .builder import RUNNERS, build_runner -from .checkpoint import (CheckpointLoader, _load_checkpoint, - _load_checkpoint_with_prefix, load_checkpoint, - load_state_dict, save_checkpoint, weights_to_cpu) -from .default_constructor import DefaultRunnerConstructor -from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, - init_dist, master_only) -from .epoch_based_runner import EpochBasedRunner, Runner -from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model -from .hooks import (HOOKS, CheckpointHook, ClosureHook, DistEvalHook, - DistSamplerSeedHook, DvcliveLoggerHook, EMAHook, EvalHook, - Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, Hook, IterTimerHook, - LoggerHook, LrUpdaterHook, MlflowLoggerHook, - NeptuneLoggerHook, OptimizerHook, PaviLoggerHook, - SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .iter_based_runner import IterBasedRunner, IterLoader -from .log_buffer import LogBuffer -from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, - DefaultOptimizerConstructor, build_optimizer, - build_optimizer_constructor) -from .priority import Priority, get_priority -from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed - -__all__ = [ - 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', - 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', - 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', - 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', - 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', - 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', - 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', - 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', - 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', - 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', - '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', - 'ModuleList', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/base_module.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/base_module.py deleted file mode 100755 index 529575b81..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/base_module.py +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings -from abc import ABCMeta -from collections import defaultdict -from logging import FileHandler - -import torch.nn as nn - -from mmcv.runner.dist_utils import master_only -from mmcv.utils.logging import get_logger, logger_initialized, print_log - - -class BaseModule(nn.Module, metaclass=ABCMeta): - """Base module for all modules in openmmlab. - - ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional - functionality of parameter initialization. Compared with - ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. - - - ``init_cfg``: the config to control the initialization. - - ``init_weights``: The function of parameter - initialization and recording initialization - information. - - ``_params_init_info``: Used to track the parameter - initialization information. This attribute only - exists during executing the ``init_weights``. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, init_cfg=None): - """Initialize BaseModule, inherited from `torch.nn.Module`""" - - # NOTE init_cfg can be defined in different levels, but init_cfg - # in low levels has a higher priority. - - super(BaseModule, self).__init__() - # define default value of init_cfg instead of hard code - # in init_weights() function - self._is_init = False - - self.init_cfg = copy.deepcopy(init_cfg) - - # Backward compatibility in derived classes - # if pretrained is not None: - # warnings.warn('DeprecationWarning: pretrained is a deprecated \ - # key, please consider using init_cfg') - # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - - @property - def is_init(self): - return self._is_init - - def init_weights(self): - """Initialize the weights.""" - - is_top_level_module = False - # check if it is top-level module - if not hasattr(self, '_params_init_info'): - # The `_params_init_info` is used to record the initialization - # information of the parameters - # the key should be the obj:`nn.Parameter` of model and the value - # should be a dict containing - # - init_info (str): The string that describes the initialization. - # - tmp_mean_value (FloatTensor): The mean of the parameter, - # which indicates whether the parameter has been modified. - # this attribute would be deleted after all parameters - # is initialized. - self._params_init_info = defaultdict(dict) - is_top_level_module = True - - # Initialize the `_params_init_info`, - # When detecting the `tmp_mean_value` of - # the corresponding parameter is changed, update related - # initialization information - for name, param in self.named_parameters(): - self._params_init_info[param][ - 'init_info'] = f'The value is the same before and ' \ - f'after calling `init_weights` ' \ - f'of {self.__class__.__name__} ' - self._params_init_info[param][ - 'tmp_mean_value'] = param.data.mean() - - # pass `params_init_info` to all submodules - # All submodules share the same `params_init_info`, - # so it will be updated when parameters are - # modified at any level of the model. - for sub_module in self.modules(): - sub_module._params_init_info = self._params_init_info - - # Get the initialized logger, if not exist, - # create a logger named `mmcv` - logger_names = list(logger_initialized.keys()) - logger_name = logger_names[0] if logger_names else 'mmcv' - - from ..cnn import initialize - from ..cnn.utils.weight_init import update_init_info - module_name = self.__class__.__name__ - if not self._is_init: - if self.init_cfg: - print_log( - f'initialize {module_name} with init_cfg {self.init_cfg}', - logger=logger_name) - initialize(self, self.init_cfg) - if isinstance(self.init_cfg, dict): - # prevent the parameters of - # the pre-trained model - # from being overwritten by - # the `init_weights` - if self.init_cfg['type'] == 'Pretrained': - return - - for m in self.children(): - if hasattr(m, 'init_weights'): - m.init_weights() - # users may overload the `init_weights` - update_init_info( - m, - init_info=f'Initialized by ' - f'user-defined `init_weights`' - f' in {m.__class__.__name__} ') - - self._is_init = True - else: - warnings.warn(f'init_weights of {self.__class__.__name__} has ' - f'been called more than once.') - - if is_top_level_module: - self._dump_init_info(logger_name) - - for sub_module in self.modules(): - del sub_module._params_init_info - - @master_only - def _dump_init_info(self, logger_name): - """Dump the initialization information to a file named - `initialization.log.json` in workdir. - - Args: - logger_name (str): The name of logger. - """ - - logger = get_logger(logger_name) - - with_file_handler = False - # dump the information to the logger file if there is a `FileHandler` - for handler in logger.handlers: - if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True - if not with_file_handler: - for name, param in self.named_parameters(): - print_log( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n ", - logger=logger_name) - - def __repr__(self): - s = super().__repr__() - if self.init_cfg: - s += f'\ninit_cfg={self.init_cfg}' - return s - - -class Sequential(BaseModule, nn.Sequential): - """Sequential module in openmmlab. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, *args, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.Sequential.__init__(self, *args) - - -class ModuleList(BaseModule, nn.ModuleList): - """ModuleList in openmmlab. - - Args: - modules (iterable, optional): an iterable of modules to add. - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleList.__init__(self, modules) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/base_runner.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/base_runner.py deleted file mode 100755 index 25cd98f51..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/base_runner.py +++ /dev/null @@ -1,542 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -import os.path as osp -import warnings -from abc import ABCMeta, abstractmethod - -import torch -from torch.optim import Optimizer - -import mmcv -from ..parallel import is_module_wrapper -from .checkpoint import load_checkpoint -from .dist_utils import get_dist_info -from .hooks import HOOKS, Hook -from .log_buffer import LogBuffer -from .priority import Priority, get_priority -from .utils import get_time_str - - -class BaseRunner(metaclass=ABCMeta): - """The base class of Runner, a training helper for PyTorch. - - All subclasses should implement the following APIs: - - - ``run()`` - - ``train()`` - - ``val()`` - - ``save_checkpoint()`` - - Args: - model (:obj:`torch.nn.Module`): The model to be run. - batch_processor (callable): A callable method that process a data - batch. The interface of this method should be - `batch_processor(model, data, train_mode) -> dict` - optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an - optimizer (in most cases) or a dict of optimizers (in models that - requires more than one optimizer, e.g., GAN). - work_dir (str, optional): The working directory to save checkpoints - and logs. Defaults to None. - logger (:obj:`logging.Logger`): Logger used during training. - Defaults to None. (The default value is just for backward - compatibility) - meta (dict | None): A dict records some import information such as - environment info and seed, which will be logged in logger hook. - Defaults to None. - max_epochs (int, optional): Total training epochs. - max_iters (int, optional): Total training iterations. - """ - - def __init__(self, - model, - batch_processor=None, - optimizer=None, - work_dir=None, - logger=None, - meta=None, - max_iters=None, - max_epochs=None): - if batch_processor is not None: - if not callable(batch_processor): - raise TypeError('batch_processor must be callable, ' - f'but got {type(batch_processor)}') - warnings.warn('batch_processor is deprecated, please implement ' - 'train_step() and val_step() in the model instead.') - # raise an error is `batch_processor` is not None and - # `model.train_step()` exists. - if is_module_wrapper(model): - _model = model.module - else: - _model = model - if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): - raise RuntimeError( - 'batch_processor and model.train_step()/model.val_step() ' - 'cannot be both available.') - else: - assert hasattr(model, 'train_step') - - # check the type of `optimizer` - if isinstance(optimizer, dict): - for name, optim in optimizer.items(): - if not isinstance(optim, Optimizer): - raise TypeError( - f'optimizer must be a dict of torch.optim.Optimizers, ' - f'but optimizer["{name}"] is a {type(optim)}') - elif not isinstance(optimizer, Optimizer) and optimizer is not None: - raise TypeError( - f'optimizer must be a torch.optim.Optimizer object ' - f'or dict or None, but got {type(optimizer)}') - - # check the type of `logger` - if not isinstance(logger, logging.Logger): - raise TypeError(f'logger must be a logging.Logger object, ' - f'but got {type(logger)}') - - # check the type of `meta` - if meta is not None and not isinstance(meta, dict): - raise TypeError( - f'meta must be a dict or None, but got {type(meta)}') - - self.model = model - self.batch_processor = batch_processor - self.optimizer = optimizer - self.logger = logger - self.meta = meta - # create work_dir - if mmcv.is_str(work_dir): - self.work_dir = osp.abspath(work_dir) - mmcv.mkdir_or_exist(self.work_dir) - elif work_dir is None: - self.work_dir = None - else: - raise TypeError('"work_dir" must be a str or None') - - # get model name from the model class - if hasattr(self.model, 'module'): - self._model_name = self.model.module.__class__.__name__ - else: - self._model_name = self.model.__class__.__name__ - - self._rank, self._world_size = get_dist_info() - self.timestamp = get_time_str() - self.mode = None - self._hooks = [] - self._epoch = 0 - self._iter = 0 - self._inner_iter = 0 - - if max_epochs is not None and max_iters is not None: - raise ValueError( - 'Only one of `max_epochs` or `max_iters` can be set.') - - self._max_epochs = max_epochs - self._max_iters = max_iters - # TODO: Redesign LogBuffer, it is not flexible and elegant enough - self.log_buffer = LogBuffer() - - @property - def model_name(self): - """str: Name of the model, usually the module class name.""" - return self._model_name - - @property - def rank(self): - """int: Rank of current process. (distributed training)""" - return self._rank - - @property - def world_size(self): - """int: Number of processes participating in the job. - (distributed training)""" - return self._world_size - - @property - def hooks(self): - """list[:obj:`Hook`]: A list of registered hooks.""" - return self._hooks - - @property - def epoch(self): - """int: Current epoch.""" - return self._epoch - - @property - def iter(self): - """int: Current iteration.""" - return self._iter - - @property - def inner_iter(self): - """int: Iteration in an epoch.""" - return self._inner_iter - - @property - def max_epochs(self): - """int: Maximum training epochs.""" - return self._max_epochs - - @property - def max_iters(self): - """int: Maximum training iterations.""" - return self._max_iters - - @abstractmethod - def train(self): - pass - - @abstractmethod - def val(self): - pass - - @abstractmethod - def run(self, data_loaders, workflow, **kwargs): - pass - - @abstractmethod - def save_checkpoint(self, - out_dir, - filename_tmpl, - save_optimizer=True, - meta=None, - create_symlink=True): - pass - - def current_lr(self): - """Get current learning rates. - - Returns: - list[float] | dict[str, list[float]]: Current learning rates of all - param groups. If the runner has a dict of optimizers, this - method will return a dict. - """ - if isinstance(self.optimizer, torch.optim.Optimizer): - lr = [group['lr'] for group in self.optimizer.param_groups] - elif isinstance(self.optimizer, dict): - lr = dict() - for name, optim in self.optimizer.items(): - lr[name] = [group['lr'] for group in optim.param_groups] - else: - raise RuntimeError( - 'lr is not applicable because optimizer does not exist.') - return lr - - def current_momentum(self): - """Get current momentums. - - Returns: - list[float] | dict[str, list[float]]: Current momentums of all - param groups. If the runner has a dict of optimizers, this - method will return a dict. - """ - - def _get_momentum(optimizer): - momentums = [] - for group in optimizer.param_groups: - if 'momentum' in group.keys(): - momentums.append(group['momentum']) - elif 'betas' in group.keys(): - momentums.append(group['betas'][0]) - else: - momentums.append(0) - return momentums - - if self.optimizer is None: - raise RuntimeError( - 'momentum is not applicable because optimizer does not exist.') - elif isinstance(self.optimizer, torch.optim.Optimizer): - momentums = _get_momentum(self.optimizer) - elif isinstance(self.optimizer, dict): - momentums = dict() - for name, optim in self.optimizer.items(): - momentums[name] = _get_momentum(optim) - return momentums - - def register_hook(self, hook, priority='NORMAL'): - """Register a hook into the hook list. - - The hook will be inserted into a priority queue, with the specified - priority (See :class:`Priority` for details of priorities). - For hooks with the same priority, they will be triggered in the same - order as they are registered. - - Args: - hook (:obj:`Hook`): The hook to be registered. - priority (int or str or :obj:`Priority`): Hook priority. - Lower value means higher priority. - """ - assert isinstance(hook, Hook) - if hasattr(hook, 'priority'): - raise ValueError('"priority" is a reserved attribute for hooks') - priority = get_priority(priority) - hook.priority = priority - # insert the hook to a sorted list - inserted = False - for i in range(len(self._hooks) - 1, -1, -1): - if priority >= self._hooks[i].priority: - self._hooks.insert(i + 1, hook) - inserted = True - break - if not inserted: - self._hooks.insert(0, hook) - - def register_hook_from_cfg(self, hook_cfg): - """Register a hook from its cfg. - - Args: - hook_cfg (dict): Hook config. It should have at least keys 'type' - and 'priority' indicating its type and priority. - - Notes: - The specific hook class to register should not use 'type' and - 'priority' arguments during initialization. - """ - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = mmcv.build_from_cfg(hook_cfg, HOOKS) - self.register_hook(hook, priority=priority) - - def call_hook(self, fn_name): - """Call all hooks. - - Args: - fn_name (str): The function name in each hook to be called, such as - "before_train_epoch". - """ - for hook in self._hooks: - getattr(hook, fn_name)(self) - - def get_hook_info(self): - # Get hooks info in each stage - stage_hook_map = {stage: [] for stage in Hook.stages} - for hook in self.hooks: - try: - priority = Priority(hook.priority).name - except ValueError: - priority = hook.priority - classname = hook.__class__.__name__ - hook_info = f'({priority:<12}) {classname:<35}' - for trigger_stage in hook.get_triggered_stages(): - stage_hook_map[trigger_stage].append(hook_info) - - stage_hook_infos = [] - for stage in Hook.stages: - hook_infos = stage_hook_map[stage] - if len(hook_infos) > 0: - info = f'{stage}:\n' - info += '\n'.join(hook_infos) - info += '\n -------------------- ' - stage_hook_infos.append(info) - return '\n'.join(stage_hook_infos) - - def load_checkpoint(self, - filename, - map_location='cpu', - strict=False, - revise_keys=[(r'^module.', '')]): - return load_checkpoint( - self.model, - filename, - map_location, - strict, - self.logger, - revise_keys=revise_keys) - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - if map_location == 'default': - if torch.cuda.is_available(): - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint(checkpoint) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - if self.meta is None: - self.meta = {} - self.meta.setdefault('hook_msgs', {}) - # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages - self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) - - # Re-calculate the number of iterations when resuming - # models with different number of GPUs - if 'config' in checkpoint['meta']: - config = mmcv.Config.fromstring( - checkpoint['meta']['config'], file_format='.py') - previous_gpu_ids = config.get('gpu_ids', None) - if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( - previous_gpu_ids) != self.world_size: - self._iter = int(self._iter * len(previous_gpu_ids) / - self.world_size) - self.logger.info('the iteration number is changed due to ' - 'change of GPU number') - - # resume meta information meta - self.meta = checkpoint['meta'] - - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - elif isinstance(lr_config, dict): - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = mmcv.build_from_cfg(lr_config, HOOKS) - else: - hook = lr_config - self.register_hook(hook, priority='VERY_HIGH') - - def register_momentum_hook(self, momentum_config): - if momentum_config is None: - return - if isinstance(momentum_config, dict): - assert 'policy' in momentum_config - policy_type = momentum_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of momentum updater. - # Since this is not applicable for - # `CosineAnnealingMomentumUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'MomentumUpdaterHook' - momentum_config['type'] = hook_type - hook = mmcv.build_from_cfg(momentum_config, HOOKS) - else: - hook = momentum_config - self.register_hook(hook, priority='HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = mmcv.build_from_cfg(optimizer_config, HOOKS) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def register_checkpoint_hook(self, checkpoint_config): - if checkpoint_config is None: - return - if isinstance(checkpoint_config, dict): - checkpoint_config.setdefault('type', 'CheckpointHook') - hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) - else: - hook = checkpoint_config - self.register_hook(hook, priority='NORMAL') - - def register_logger_hooks(self, log_config): - if log_config is None: - return - log_interval = log_config['interval'] - for info in log_config['hooks']: - logger_hook = mmcv.build_from_cfg( - info, HOOKS, default_args=dict(interval=log_interval)) - self.register_hook(logger_hook, priority='VERY_LOW') - - def register_timer_hook(self, timer_config): - if timer_config is None: - return - if isinstance(timer_config, dict): - timer_config_ = copy.deepcopy(timer_config) - hook = mmcv.build_from_cfg(timer_config_, HOOKS) - else: - hook = timer_config - self.register_hook(hook, priority='LOW') - - def register_custom_hooks(self, custom_config): - if custom_config is None: - return - - if not isinstance(custom_config, list): - custom_config = [custom_config] - - for item in custom_config: - if isinstance(item, dict): - self.register_hook_from_cfg(item) - else: - self.register_hook(item, priority='NORMAL') - - def register_profiler_hook(self, profiler_config): - if profiler_config is None: - return - if isinstance(profiler_config, dict): - profiler_config.setdefault('type', 'ProfilerHook') - hook = mmcv.build_from_cfg(profiler_config, HOOKS) - else: - hook = profiler_config - self.register_hook(hook) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - timer_config=dict(type='IterTimerHook'), - custom_hooks_config=None): - """Register default and custom hooks for training. - - Default and custom hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - self.register_lr_hook(lr_config) - self.register_momentum_hook(momentum_config) - self.register_optimizer_hook(optimizer_config) - self.register_checkpoint_hook(checkpoint_config) - self.register_timer_hook(timer_config) - self.register_logger_hooks(log_config) - self.register_custom_hooks(custom_hooks_config) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/builder.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/builder.py deleted file mode 100755 index 77c96ba0b..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/builder.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from ..utils import Registry - -RUNNERS = Registry('runner') -RUNNER_BUILDERS = Registry('runner builder') - - -def build_runner_constructor(cfg): - return RUNNER_BUILDERS.build(cfg) - - -def build_runner(cfg, default_args=None): - runner_cfg = copy.deepcopy(cfg) - constructor_type = runner_cfg.pop('constructor', - 'DefaultRunnerConstructor') - runner_constructor = build_runner_constructor( - dict( - type=constructor_type, - runner_cfg=runner_cfg, - default_args=default_args)) - runner = runner_constructor() - return runner diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/checkpoint.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/checkpoint.py deleted file mode 100755 index 6ad605b85..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/checkpoint.py +++ /dev/null @@ -1,707 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os -import os.path as osp -import pkgutil -import re -import time -import warnings -from collections import OrderedDict -from importlib import import_module -from tempfile import TemporaryDirectory - -import torch -import torchvision -from torch.optim import Optimizer -from torch.utils import model_zoo - -import mmcv -from ..fileio import FileClient -from ..fileio import load as load_file -from ..parallel import is_module_wrapper -from ..utils import mkdir_or_exist -from .dist_utils import get_dist_info - -ENV_MMCV_HOME = 'MMCV_HOME' -ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' -DEFAULT_CACHE_DIR = '~/.cache' - - -def _get_mmcv_home(): - mmcv_home = os.path.expanduser( - os.getenv( - ENV_MMCV_HOME, - os.path.join( - os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) - - mkdir_or_exist(mmcv_home) - return mmcv_home - - -def load_state_dict(module, state_dict, strict=False, logger=None): - """Load state_dict to a module. - - This method is modified from :meth:`torch.nn.Module.load_state_dict`. - Default value for ``strict`` is set to ``False`` and the message for - param mismatch will be shown even if strict is False. - - Args: - module (Module): Module that receives the state_dict. - state_dict (OrderedDict): Weights. - strict (bool): whether to strictly enforce that the keys - in :attr:`state_dict` match the keys returned by this module's - :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. - logger (:obj:`logging.Logger`, optional): Logger to log the error - message. If not specified, print function will be used. - """ - unexpected_keys = [] - all_missing_keys = [] - err_msg = [] - - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata - - # use _load_from_state_dict to enable checkpoint version control - def load(module, prefix=''): - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - local_metadata = {} if metadata is None else metadata.get( - prefix[:-1], {}) - module._load_from_state_dict(state_dict, prefix, local_metadata, True, - all_missing_keys, unexpected_keys, - err_msg) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(module) - load = None # break load->load reference cycle - - # ignore "num_batches_tracked" of BN layers - missing_keys = [ - key for key in all_missing_keys if 'num_batches_tracked' not in key - ] - - if unexpected_keys: - err_msg.append('unexpected key in source ' - f'state_dict: {", ".join(unexpected_keys)}\n') - if missing_keys: - err_msg.append( - f'missing keys in source state_dict: {", ".join(missing_keys)}\n') - - rank, _ = get_dist_info() - if len(err_msg) > 0 and rank == 0: - err_msg.insert( - 0, 'The model and loaded state dict do not match exactly\n') - err_msg = '\n'.join(err_msg) - if strict: - raise RuntimeError(err_msg) - elif logger is not None: - logger.warning(err_msg) - else: - print(err_msg) - - -def get_torchvision_models(): - model_urls = dict() - for _, name, ispkg in pkgutil.walk_packages(torchvision.models.__path__): - if ispkg: - continue - _zoo = import_module(f'torchvision.models.{name}') - if hasattr(_zoo, 'model_urls'): - _urls = getattr(_zoo, 'model_urls') - model_urls.update(_urls) - return model_urls - - -def get_external_models(): - mmcv_home = _get_mmcv_home() - default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') - default_urls = load_file(default_json_path) - assert isinstance(default_urls, dict) - external_json_path = osp.join(mmcv_home, 'open_mmlab.json') - if osp.exists(external_json_path): - external_urls = load_file(external_json_path) - assert isinstance(external_urls, dict) - default_urls.update(external_urls) - - return default_urls - - -def get_mmcls_models(): - mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') - mmcls_urls = load_file(mmcls_json_path) - - return mmcls_urls - - -def get_deprecated_model_names(): - deprecate_json_path = osp.join(mmcv.__path__[0], - 'model_zoo/deprecated.json') - deprecate_urls = load_file(deprecate_json_path) - assert isinstance(deprecate_urls, dict) - - return deprecate_urls - - -def _process_mmcls_checkpoint(checkpoint): - state_dict = checkpoint['state_dict'] - new_state_dict = OrderedDict() - for k, v in state_dict.items(): - if k.startswith('backbone.'): - new_state_dict[k[9:]] = v - new_checkpoint = dict(state_dict=new_state_dict) - - return new_checkpoint - - -class CheckpointLoader: - """A general checkpoint loader to manage all schemes.""" - - _schemes = {} - - @classmethod - def _register_scheme(cls, prefixes, loader, force=False): - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if (prefix not in cls._schemes) or force: - cls._schemes[prefix] = loader - else: - raise KeyError( - f'{prefix} is already registered as a loader backend, ' - 'add "force=True" if you want to override it') - # sort, longer prefixes take priority - cls._schemes = OrderedDict( - sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) - - @classmethod - def register_scheme(cls, prefixes, loader=None, force=False): - """Register a loader to CheckpointLoader. - - This method can be used as a normal class method or a decorator. - - Args: - prefixes (str or list[str] or tuple[str]): - The prefix of the registered loader. - loader (function, optional): The loader function to be registered. - When this method is used as a decorator, loader is None. - Defaults to None. - force (bool, optional): Whether to override the loader - if the prefix has already been registered. Defaults to False. - """ - - if loader is not None: - cls._register_scheme(prefixes, loader, force=force) - return - - def _register(loader_cls): - cls._register_scheme(prefixes, loader_cls, force=force) - return loader_cls - - return _register - - @classmethod - def _get_checkpoint_loader(cls, path): - """Finds a loader that supports the given path. Falls back to the local - loader if no other loader is found. - - Args: - path (str): checkpoint path - - Returns: - loader (function): checkpoint loader - """ - - for p in cls._schemes: - if path.startswith(p): - return cls._schemes[p] - - @classmethod - def load_checkpoint(cls, filename, map_location=None, logger=None): - """load checkpoint through URL scheme path. - - Args: - filename (str): checkpoint file name with given prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - logger (:mod:`logging.Logger`, optional): The logger for message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint_loader = cls._get_checkpoint_loader(filename) - class_name = checkpoint_loader.__name__ - mmcv.print_log( - f'load checkpoint from {class_name[10:]} path: {filename}', logger) - return checkpoint_loader(filename, map_location) - - -@CheckpointLoader.register_scheme(prefixes='') -def load_from_local(filename, map_location): - """load checkpoint by local file path. - - Args: - filename (str): local checkpoint file path - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - if not osp.isfile(filename): - raise IOError(f'{filename} is not a checkpoint file') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) -def load_from_http(filename, map_location=None, model_dir=None): - """load checkpoint through HTTP or HTTPS scheme path. In distributed - setting, this function only download checkpoint at local rank 0. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - model_dir (string, optional): directory in which to save the object, - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - rank, world_size = get_dist_info() - rank = int(os.environ.get('LOCAL_RANK', rank)) - if rank == 0: - checkpoint = model_zoo.load_url( - filename, model_dir=model_dir, map_location=map_location) - if world_size > 1: - torch.distributed.barrier() - if rank > 0: - checkpoint = model_zoo.load_url( - filename, model_dir=model_dir, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='pavi://') -def load_from_pavi(filename, map_location=None): - """load checkpoint through the file path prefixed with pavi. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with pavi prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - assert filename.startswith('pavi://'), \ - f'Expected filename startswith `pavi://`, but get {filename}' - model_path = filename[7:] - - try: - from pavi import modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - - model = modelcloud.get(model_path) - with TemporaryDirectory() as tmp_dir: - downloaded_file = osp.join(tmp_dir, model.name) - model.download(downloaded_file) - checkpoint = torch.load(downloaded_file, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='s3://') -def load_from_ceph(filename, map_location=None, backend='petrel'): - """load checkpoint through the file path prefixed with s3. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with s3 prefix - map_location (str, optional): Same as :func:`torch.load`. - backend (str, optional): The storage backend type. Options are 'ceph', - 'petrel'. Default: 'petrel'. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - allowed_backends = ['ceph', 'petrel'] - if backend not in allowed_backends: - raise ValueError(f'Load from Backend {backend} is not supported.') - - if backend == 'ceph': - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead') - - # CephClient and PetrelBackend have the same prefix 's3://' and the latter - # will be chosen as default. If PetrelBackend can not be instantiated - # successfully, the CephClient will be chosen. - try: - file_client = FileClient(backend=backend) - except ImportError: - allowed_backends.remove(backend) - file_client = FileClient(backend=allowed_backends[0]) - - with io.BytesIO(file_client.get(filename)) as buffer: - checkpoint = torch.load(buffer, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) -def load_from_torchvision(filename, map_location=None): - """load checkpoint through the file path prefixed with modelzoo or - torchvision. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - model_urls = get_torchvision_models() - if filename.startswith('modelzoo://'): - warnings.warn('The URL scheme of "modelzoo://" is deprecated, please ' - 'use "torchvision://" instead') - model_name = filename[11:] - else: - model_name = filename[14:] - return load_from_http(model_urls[model_name], map_location=map_location) - - -@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) -def load_from_openmmlab(filename, map_location=None): - """load checkpoint through the file path prefixed with open-mmlab or - openmmlab. - - Args: - filename (str): checkpoint file path with open-mmlab or - openmmlab prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_external_models() - prefix_str = 'open-mmlab://' - if filename.startswith(prefix_str): - model_name = filename[13:] - else: - model_name = filename[12:] - prefix_str = 'openmmlab://' - - deprecated_urls = get_deprecated_model_names() - if model_name in deprecated_urls: - warnings.warn(f'{prefix_str}{model_name} is deprecated in favor ' - f'of {prefix_str}{deprecated_urls[model_name]}') - model_name = deprecated_urls[model_name] - model_url = model_urls[model_name] - # check if is url - if model_url.startswith(('http://', 'https://')): - checkpoint = load_from_http(model_url, map_location=map_location) - else: - filename = osp.join(_get_mmcv_home(), model_url) - if not osp.isfile(filename): - raise IOError(f'{filename} is not a checkpoint file') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='mmcls://') -def load_from_mmcls(filename, map_location=None): - """load checkpoint through the file path prefixed with mmcls. - - Args: - filename (str): checkpoint file path with mmcls prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_mmcls_models() - model_name = filename[8:] - checkpoint = load_from_http( - model_urls[model_name], map_location=map_location) - checkpoint = _process_mmcls_checkpoint(checkpoint) - return checkpoint - - -def _load_checkpoint(filename, map_location=None, logger=None): - """Load checkpoint from somewhere (modelzoo, file, url). - - Args: - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str, optional): Same as :func:`torch.load`. - Default: None. - logger (:mod:`logging.Logger`, optional): The logger for error message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. It can be either an - OrderedDict storing model weights or a dict containing other - information, which depends on the checkpoint. - """ - return CheckpointLoader.load_checkpoint(filename, map_location, logger) - - -def _load_checkpoint_with_prefix(prefix, filename, map_location=None): - """Load partial pretrained model with specific prefix. - - Args: - prefix (str): The prefix of sub-module. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str | None): Same as :func:`torch.load`. Default: None. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint = _load_checkpoint(filename, map_location=map_location) - - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - if not prefix.endswith('.'): - prefix += '.' - prefix_len = len(prefix) - - state_dict = { - k[prefix_len:]: v - for k, v in state_dict.items() if k.startswith(prefix) - } - - assert state_dict, f'{prefix} is not in the pretrained model' - return state_dict - - -def load_checkpoint(model, - filename, - map_location=None, - strict=False, - logger=None, - revise_keys=[(r'^module\.', '')]): - """Load checkpoint from a file or URI. - - Args: - model (Module): Module to load checkpoint. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str): Same as :func:`torch.load`. - strict (bool): Whether to allow different params for the model and - checkpoint. - logger (:mod:`logging.Logger` or None): The logger for error message. - revise_keys (list): A list of customized keywords to modify the - state_dict in checkpoint. Each item is a (pattern, replacement) - pair of the regular expression operations. Default: strip - the prefix 'module.' by [(r'^module\\.', '')]. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - checkpoint = _load_checkpoint(filename, map_location, logger) - # OrderedDict is a subclass of dict - if not isinstance(checkpoint, dict): - raise RuntimeError( - f'No state_dict found in checkpoint file {filename}') - # get state_dict from checkpoint - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - - # strip prefix of state_dict - metadata = getattr(state_dict, '_metadata', OrderedDict()) - for p, r in revise_keys: - state_dict = OrderedDict( - {re.sub(p, r, k): v - for k, v in state_dict.items()}) - # Keep metadata in state_dict - state_dict._metadata = metadata - - # load state_dict - load_state_dict(model, state_dict, strict, logger) - return checkpoint - - -def weights_to_cpu(state_dict): - """Copy a model state_dict to cpu. - - Args: - state_dict (OrderedDict): Model weights on GPU. - - Returns: - OrderedDict: Model weights on GPU. - """ - state_dict_cpu = OrderedDict() - for key, val in state_dict.items(): - state_dict_cpu[key] = val.cpu() - # Keep metadata in state_dict - state_dict_cpu._metadata = getattr(state_dict, '_metadata', OrderedDict()) - return state_dict_cpu - - -def _save_to_state_dict(module, destination, prefix, keep_vars): - """Saves module state to `destination` dictionary. - - This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. - - Args: - module (nn.Module): The module to generate state_dict. - destination (dict): A dict where state will be stored. - prefix (str): The prefix for parameters and buffers used in this - module. - """ - for name, param in module._parameters.items(): - if param is not None: - destination[prefix + name] = param if keep_vars else param.detach() - for name, buf in module._buffers.items(): - # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d - if buf is not None: - destination[prefix + name] = buf if keep_vars else buf.detach() - - -def get_state_dict(module, destination=None, prefix='', keep_vars=False): - """Returns a dictionary containing a whole state of the module. - - Both parameters and persistent buffers (e.g. running averages) are - included. Keys are corresponding parameter and buffer names. - - This method is modified from :meth:`torch.nn.Module.state_dict` to - recursively check parallel module in case that the model has a complicated - structure, e.g., nn.Module(nn.Module(DDP)). - - Args: - module (nn.Module): The module to generate state_dict. - destination (OrderedDict): Returned dict for the state of the - module. - prefix (str): Prefix of the key. - keep_vars (bool): Whether to keep the variable property of the - parameters. Default: False. - - Returns: - dict: A dictionary containing a whole state of the module. - """ - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - - # below is the same as torch.nn.Module.state_dict() - if destination is None: - destination = OrderedDict() - destination._metadata = OrderedDict() - destination._metadata[prefix[:-1]] = local_metadata = dict( - version=module._version) - _save_to_state_dict(module, destination, prefix, keep_vars) - for name, child in module._modules.items(): - if child is not None: - get_state_dict( - child, destination, prefix + name + '.', keep_vars=keep_vars) - for hook in module._state_dict_hooks.values(): - hook_result = hook(module, destination, prefix, local_metadata) - if hook_result is not None: - destination = hook_result - return destination - - -def save_checkpoint(model, - filename, - optimizer=None, - meta=None, - file_client_args=None): - """Save checkpoint to file. - - The checkpoint will have 3 fields: ``meta``, ``state_dict`` and - ``optimizer``. By default ``meta`` will contain version and time info. - - Args: - model (Module): Module whose params are to be saved. - filename (str): Checkpoint filename. - optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. - meta (dict, optional): Metadata to be saved in checkpoint. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError(f'meta must be a dict or None, but got {type(meta)}') - meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) - - if is_module_wrapper(model): - model = model.module - - if hasattr(model, 'CLASSES') and model.CLASSES is not None: - # save class name to the meta - meta.update(CLASSES=model.CLASSES) - - checkpoint = { - 'meta': meta, - 'state_dict': weights_to_cpu(get_state_dict(model)) - } - # save optimizer state dict in the checkpoint - if isinstance(optimizer, Optimizer): - checkpoint['optimizer'] = optimizer.state_dict() - elif isinstance(optimizer, dict): - checkpoint['optimizer'] = {} - for name, optim in optimizer.items(): - checkpoint['optimizer'][name] = optim.state_dict() - - if filename.startswith('pavi://'): - if file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" if filename starts with' - f'"pavi://", but got {file_client_args}') - try: - from pavi import modelcloud - from pavi import exception - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - model_path = filename[7:] - root = modelcloud.Folder() - model_dir, model_name = osp.split(model_path) - try: - model = modelcloud.get(model_dir) - except exception.NodeNotFoundError: - model = root.create_training_model(model_dir) - with TemporaryDirectory() as tmp_dir: - checkpoint_file = osp.join(tmp_dir, model_name) - with open(checkpoint_file, 'wb') as f: - torch.save(checkpoint, f) - f.flush() - model.create_file(checkpoint_file, name=model_name) - else: - file_client = FileClient.infer_client(file_client_args, filename) - with io.BytesIO() as f: - torch.save(checkpoint, f) - file_client.put(f.getvalue(), filename) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/default_constructor.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/default_constructor.py deleted file mode 100755 index 0bad847f2..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/default_constructor.py +++ /dev/null @@ -1,44 +0,0 @@ -from .builder import RUNNER_BUILDERS, RUNNERS - - -@RUNNER_BUILDERS.register_module() -class DefaultRunnerConstructor: - """Default constructor for runners. - - Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. - For example, We can inject some new properties and functions for `Runner`. - - Example: - >>> from mmcv.runner import RUNNER_BUILDERS, build_runner - >>> # Define a new RunnerReconstructor - >>> @RUNNER_BUILDERS.register_module() - >>> class MyRunnerConstructor: - ... def __init__(self, runner_cfg, default_args=None): - ... if not isinstance(runner_cfg, dict): - ... raise TypeError('runner_cfg should be a dict', - ... f'but got {type(runner_cfg)}') - ... self.runner_cfg = runner_cfg - ... self.default_args = default_args - ... - ... def __call__(self): - ... runner = RUNNERS.build(self.runner_cfg, - ... default_args=self.default_args) - ... # Add new properties for existing runner - ... runner.my_name = 'my_runner' - ... runner.my_function = lambda self: print(self.my_name) - ... ... - >>> # build your runner - >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, - ... constructor='MyRunnerConstructor') - >>> runner = build_runner(runner_cfg) - """ - - def __init__(self, runner_cfg, default_args=None): - if not isinstance(runner_cfg, dict): - raise TypeError('runner_cfg should be a dict', - f'but got {type(runner_cfg)}') - self.runner_cfg = runner_cfg - self.default_args = default_args - - def __call__(self): - return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/dist_utils.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/dist_utils.py deleted file mode 100755 index d3a1ef3fd..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/dist_utils.py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import os -import subprocess -from collections import OrderedDict - -import torch -import torch.multiprocessing as mp -from torch import distributed as dist -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - - -def init_dist(launcher, backend='nccl', **kwargs): - if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') - if launcher == 'pytorch': - _init_dist_pytorch(backend, **kwargs) - elif launcher == 'mpi': - _init_dist_mpi(backend, **kwargs) - elif launcher == 'slurm': - _init_dist_slurm(backend, **kwargs) - else: - raise ValueError(f'Invalid launcher type: {launcher}') - - -def _init_dist_pytorch(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['RANK']) - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_mpi(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['OMPI_COMM_WORLD_RANK']) - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_slurm(backend, port=None): - """Initialize slurm distributed training environment. - - If argument ``port`` is not specified, then the master port will be system - environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system - environment variable, then a default port ``29500`` will be used. - - Args: - backend (str): Backend of torch.distributed. - port (int, optional): Master port. Defaults to None. - """ - proc_id = int(os.environ['SLURM_PROCID']) - ntasks = int(os.environ['SLURM_NTASKS']) - node_list = os.environ['SLURM_NODELIST'] - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(proc_id % num_gpus) - addr = subprocess.getoutput( - f'scontrol show hostname {node_list} | head -n1') - # specify master port - if port is not None: - os.environ['MASTER_PORT'] = str(port) - elif 'MASTER_PORT' in os.environ: - pass # use MASTER_PORT in the environment variable - else: - # 29500 is torch.distributed default port - os.environ['MASTER_PORT'] = '29500' - # use MASTER_ADDR in the environment variable if it already exists - if 'MASTER_ADDR' not in os.environ: - os.environ['MASTER_ADDR'] = addr - os.environ['WORLD_SIZE'] = str(ntasks) - os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) - os.environ['RANK'] = str(proc_id) - dist.init_process_group(backend=backend) - - -def get_dist_info(): - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - world_size = dist.get_world_size() - else: - rank = 0 - world_size = 1 - return rank, world_size - - -def master_only(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - rank, _ = get_dist_info() - if rank == 0: - return func(*args, **kwargs) - - return wrapper - - -def allreduce_params(params, coalesce=True, bucket_size_mb=-1): - """Allreduce parameters. - - Args: - params (list[torch.Parameters]): List of parameters or buffers of a - model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - _, world_size = get_dist_info() - if world_size == 1: - return - params = [param.data for param in params] - if coalesce: - _allreduce_coalesced(params, world_size, bucket_size_mb) - else: - for tensor in params: - dist.all_reduce(tensor.div_(world_size)) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - _, world_size = get_dist_info() - if world_size == 1: - return - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/epoch_based_runner.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/epoch_based_runner.py deleted file mode 100755 index 2dd29357a..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/epoch_based_runner.py +++ /dev/null @@ -1,187 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .utils import get_host_info - - -@RUNNERS.register_module() -class EpochBasedRunner(BaseRunner): - """Epoch-based Runner. - - This runner train models epoch by epoch. - """ - - def run_iter(self, data_batch, train_mode, **kwargs): - if self.batch_processor is not None: - outputs = self.batch_processor( - self.model, data_batch, train_mode=train_mode, **kwargs) - elif train_mode: - outputs = self.model.train_step(data_batch, self.optimizer, - **kwargs) - else: - outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('"batch_processor()" or "model.train_step()"' - 'and "model.val_step()" must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - self._iter += 1 - - self.call_hook('after_train_epoch') - self._epoch += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - self.call_hook('before_val_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_val_iter') - self.run_iter(data_batch, train_mode=False) - self.call_hook('after_val_iter') - - self.call_hook('after_val_epoch') - - def run(self, data_loaders, workflow, max_epochs=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, epochs) to specify the - running order and epochs. E.g, [('train', 2), ('val', 1)] means - running 2 epochs for training and 1 epoch for validation, - iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_epochs is not None: - warnings.warn( - 'setting max_epochs in run is deprecated, ' - 'please set max_epochs in runner_config', DeprecationWarning) - self._max_epochs = max_epochs - - assert self._max_epochs is not None, ( - 'max_epochs must be specified during instantiation') - - for i, flow in enumerate(workflow): - mode, epochs = flow - if mode == 'train': - self._max_iters = self._max_epochs * len(data_loaders[i]) - break - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d epochs', workflow, - self._max_epochs) - self.call_hook('before_run') - - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - mode, epochs = flow - if isinstance(mode, str): # self.train() - if not hasattr(self, mode): - raise ValueError( - f'runner has no method named "{mode}" to run an ' - 'epoch') - epoch_runner = getattr(self, mode) - else: - raise TypeError( - 'mode in workflow must be a str, but got {}'.format( - type(mode))) - - for _ in range(epochs): - if mode == 'train' and self.epoch >= self._max_epochs: - break - epoch_runner(data_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_run') - - def save_checkpoint(self, - out_dir, - filename_tmpl='epoch_{}.pth', - save_optimizer=True, - meta=None, - create_symlink=True): - """Save the checkpoint. - - Args: - out_dir (str): The directory that checkpoints are saved. - filename_tmpl (str, optional): The checkpoint filename template, - which contains a placeholder for the epoch number. - Defaults to 'epoch_{}.pth'. - save_optimizer (bool, optional): Whether to save the optimizer to - the checkpoint. Defaults to True. - meta (dict, optional): The meta information to be saved in the - checkpoint. Defaults to None. - create_symlink (bool, optional): Whether to create a symlink - "latest.pth" to point to the latest checkpoint. - Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.epoch + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - -@RUNNERS.register_module() -class Runner(EpochBasedRunner): - """Deprecated name of EpochBasedRunner.""" - - def __init__(self, *args, **kwargs): - warnings.warn( - 'Runner was deprecated, please use EpochBasedRunner instead') - super().__init__(*args, **kwargs) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/fp16_utils.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/fp16_utils.py deleted file mode 100755 index 4baab939a..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/fp16_utils.py +++ /dev/null @@ -1,410 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import warnings -from collections import abc -from inspect import getfullargspec - -import numpy as np -import torch -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .dist_utils import allreduce_grads as _allreduce_grads - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 - # manually, so the behavior may not be consistent with real amp. - from torch.cuda.amp import autocast -except ImportError: - pass - - -def cast_tensor_type(inputs, src_type, dst_type): - """Recursively convert Tensor in inputs from src_type to dst_type. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype): Source type.. - dst_type (torch.dtype): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - if isinstance(inputs, nn.Module): - return inputs - elif isinstance(inputs, torch.Tensor): - return inputs.to(dst_type) - elif isinstance(inputs, str): - return inputs - elif isinstance(inputs, np.ndarray): - return inputs - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type, dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type, dst_type) for item in inputs) - else: - return inputs - - -def auto_fp16(apply_to=None, out_fp32=False): - """Decorator to enable fp16 training automatically. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If inputs arguments are fp32 tensors, they will - be converted to fp16 automatically. Arguments other than fp32 tensors are - ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp32 (bool): Whether to convert the output back to fp32. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp16 - >>> @auto_fp16() - >>> def forward(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp16 - >>> @auto_fp16(apply_to=('pred', )) - >>> def do_something(self, pred, others): - >>> pass - """ - - def auto_fp16_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@auto_fp16 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - # NOTE: default args are not taken into consideration - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.float, torch.half)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = {} - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.float, torch.half) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=True): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp32: - output = cast_tensor_type(output, torch.half, torch.float) - return output - - return new_func - - return auto_fp16_wrapper - - -def force_fp32(apply_to=None, out_fp16=False): - """Decorator to convert input arguments to fp32 in force. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If there are some inputs that must be processed - in fp32 mode, then this decorator can handle it. If inputs arguments are - fp16 tensors, they will be converted to fp32 automatically. Arguments other - than fp16 tensors are ignored. If you are using PyTorch >= 1.6, - torch.cuda.amp is used as the backend, otherwise, original mmcv - implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp16 (bool): Whether to convert the output back to fp16. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp32 - >>> @force_fp32() - >>> def loss(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp32 - >>> @force_fp32(apply_to=('pred', )) - >>> def post_process(self, pred, others): - >>> pass - """ - - def force_fp32_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@force_fp32 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.half, torch.float)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = dict() - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.half, torch.float) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=False): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp16: - output = cast_tensor_type(output, torch.float, torch.half) - return output - - return new_func - - return force_fp32_wrapper - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - warnings.warning( - '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' - 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads') - _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) - - -def wrap_fp16_model(model): - """Wrap the FP32 model to FP16. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - For PyTorch >= 1.6, this function will - 1. Set fp16 flag inside the model to True. - - Otherwise: - 1. Convert FP32 model to FP16. - 2. Remain some necessary layers to be FP32, e.g., normalization layers. - 3. Set `fp16_enabled` flag inside the model to True. - - Args: - model (nn.Module): Model in FP32. - """ - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.6.0')): - # convert model to fp16 - model.half() - # patch the normalization layers to make it work in fp32 mode - patch_norm_fp32(model) - # set `fp16_enabled` flag - for m in model.modules(): - if hasattr(m, 'fp16_enabled'): - m.fp16_enabled = True - - -def patch_norm_fp32(module): - """Recursively convert normalization layers from FP16 to FP32. - - Args: - module (nn.Module): The modules to be converted in FP16. - - Returns: - nn.Module: The converted module, the normalization layers have been - converted to FP32. - """ - if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): - module.float() - if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': - module.forward = patch_forward_method(module.forward, torch.half, - torch.float) - for child in module.children(): - patch_norm_fp32(child) - return module - - -def patch_forward_method(func, src_type, dst_type, convert_output=True): - """Patch the forward method of a module. - - Args: - func (callable): The original forward method. - src_type (torch.dtype): Type of input arguments to be converted from. - dst_type (torch.dtype): Type of input arguments to be converted to. - convert_output (bool): Whether to convert the output back to src_type. - - Returns: - callable: The patched forward method. - """ - - def new_forward(*args, **kwargs): - output = func(*cast_tensor_type(args, src_type, dst_type), - **cast_tensor_type(kwargs, src_type, dst_type)) - if convert_output: - output = cast_tensor_type(output, dst_type, src_type) - return output - - return new_forward - - -class LossScaler: - """Class that manages loss scaling in mixed precision training which - supports both dynamic or static mode. - - The implementation refers to - https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. - Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. - It's important to understand how :class:`LossScaler` operates. - Loss scaling is designed to combat the problem of underflowing - gradients encountered at long times when training fp16 networks. - Dynamic loss scaling begins by attempting a very high loss - scale. Ironically, this may result in OVERflowing gradients. - If overflowing gradients are encountered, :class:`FP16_Optimizer` then - skips the update step for this particular iteration/minibatch, - and :class:`LossScaler` adjusts the loss scale to a lower value. - If a certain number of iterations occur without overflowing gradients - detected,:class:`LossScaler` increases the loss scale once more. - In this way :class:`LossScaler` attempts to "ride the edge" of always - using the highest loss scale possible without incurring overflow. - - Args: - init_scale (float): Initial loss scale value, default: 2**32. - scale_factor (float): Factor used when adjusting the loss scale. - Default: 2. - mode (str): Loss scaling mode. 'dynamic' or 'static' - scale_window (int): Number of consecutive iterations without an - overflow to wait before increasing the loss scale. Default: 1000. - """ - - def __init__(self, - init_scale=2**32, - mode='dynamic', - scale_factor=2., - scale_window=1000): - self.cur_scale = init_scale - self.cur_iter = 0 - assert mode in ('dynamic', - 'static'), 'mode can only be dynamic or static' - self.mode = mode - self.last_overflow_iter = -1 - self.scale_factor = scale_factor - self.scale_window = scale_window - - def has_overflow(self, params): - """Check if params contain overflow.""" - if self.mode != 'dynamic': - return False - for p in params: - if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): - return True - return False - - def _has_inf_or_nan(x): - """Check if params contain NaN.""" - try: - cpu_sum = float(x.float().sum()) - except RuntimeError as instance: - if 'value cannot be converted' not in instance.args[0]: - raise - return True - else: - if cpu_sum == float('inf') or cpu_sum == -float('inf') \ - or cpu_sum != cpu_sum: - return True - return False - - def update_scale(self, overflow): - """update the current loss scale value when overflow happens.""" - if self.mode != 'dynamic': - return - if overflow: - self.cur_scale = max(self.cur_scale / self.scale_factor, 1) - self.last_overflow_iter = self.cur_iter - else: - if (self.cur_iter - self.last_overflow_iter) % \ - self.scale_window == 0: - self.cur_scale *= self.scale_factor - self.cur_iter += 1 - - def state_dict(self): - """Returns the state of the scaler as a :class:`dict`.""" - return dict( - cur_scale=self.cur_scale, - cur_iter=self.cur_iter, - mode=self.mode, - last_overflow_iter=self.last_overflow_iter, - scale_factor=self.scale_factor, - scale_window=self.scale_window) - - def load_state_dict(self, state_dict): - """Loads the loss_scaler state dict. - - Args: - state_dict (dict): scaler state. - """ - self.cur_scale = state_dict['cur_scale'] - self.cur_iter = state_dict['cur_iter'] - self.mode = state_dict['mode'] - self.last_overflow_iter = state_dict['last_overflow_iter'] - self.scale_factor = state_dict['scale_factor'] - self.scale_window = state_dict['scale_window'] - - @property - def loss_scale(self): - return self.cur_scale diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/__init__.py deleted file mode 100755 index 915af28ce..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkpoint import CheckpointHook -from .closure import ClosureHook -from .ema import EMAHook -from .evaluation import DistEvalHook, EvalHook -from .hook import HOOKS, Hook -from .iter_timer import IterTimerHook -from .logger import (DvcliveLoggerHook, LoggerHook, MlflowLoggerHook, - NeptuneLoggerHook, PaviLoggerHook, TensorboardLoggerHook, - TextLoggerHook, WandbLoggerHook) -from .lr_updater import LrUpdaterHook -from .memory import EmptyCacheHook -from .momentum_updater import MomentumUpdaterHook -from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, OptimizerHook) -from .profiler import ProfilerHook -from .sampler_seed import DistSamplerSeedHook -from .sync_buffer import SyncBuffersHook - -__all__ = [ - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'OptimizerHook', 'Fp16OptimizerHook', 'IterTimerHook', - 'DistSamplerSeedHook', 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'DvcliveLoggerHook', - 'MomentumUpdaterHook', 'SyncBuffersHook', 'EMAHook', 'EvalHook', - 'DistEvalHook', 'ProfilerHook', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/checkpoint.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/checkpoint.py deleted file mode 100755 index 7bb75f402..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/checkpoint.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -from mmcv.fileio import FileClient -from ..dist_utils import allreduce_params, master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class CheckpointHook(Hook): - """Save checkpoints periodically. - - Args: - interval (int): The saving period. If ``by_epoch=True``, interval - indicates epochs, otherwise it indicates iterations. - Default: -1, which means "never". - by_epoch (bool): Saving checkpoints by epoch or by iteration. - Default: True. - save_optimizer (bool): Whether to save optimizer state_dict in the - checkpoint. It is usually used for resuming experiments. - Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, ``runner.work_dir`` will be used by default. If - specified, the ``out_dir`` will be the concatenation of ``out_dir`` - and the last level directory of ``runner.work_dir``. - `Changed in version 1.3.16.` - max_keep_ckpts (int, optional): The maximum checkpoints to keep. - In some cases we want only the latest few checkpoints and would - like to delete old ones to save the disk space. - Default: -1, which means unlimited. - save_last (bool, optional): Whether to force the last checkpoint to be - saved regardless of interval. Default: True. - sync_buffer (bool, optional): Whether to synchronize buffers in - different gpus. Default: False. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - - .. warning:: - Before v1.3.16, the ``out_dir`` argument indicates the path where the - checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the - root directory and the final path to save checkpoint is the - concatenation of ``out_dir`` and the last level directory of - ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" - and the value of ``runner.work_dir`` is "/path/of/B", then the final - path will be "/path/of/A/B". - """ - - def __init__(self, - interval=-1, - by_epoch=True, - save_optimizer=True, - out_dir=None, - max_keep_ckpts=-1, - save_last=True, - sync_buffer=False, - file_client_args=None, - **kwargs): - self.interval = interval - self.by_epoch = by_epoch - self.save_optimizer = save_optimizer - self.out_dir = out_dir - self.max_keep_ckpts = max_keep_ckpts - self.save_last = save_last - self.args = kwargs - self.sync_buffer = sync_buffer - self.file_client_args = file_client_args - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - - runner.logger.info((f'Checkpoints will be saved to {self.out_dir} by ' - f'{self.file_client.name}.')) - - # disable the create_symlink option because some file backends do not - # allow to create a symlink - if 'create_symlink' in self.args: - if self.args[ - 'create_symlink'] and not self.file_client.allow_symlink: - self.args['create_symlink'] = False - warnings.warn( - ('create_symlink is set as True by the user but is changed' - 'to be False because creating symbolic link is not ' - f'allowed in {self.file_client.name}')) - else: - self.args['create_symlink'] = self.file_client.allow_symlink - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` epochs - # 2. reach the last epoch of training - if self.every_n_epochs( - runner, self.interval) or (self.save_last - and self.is_last_epoch(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.epoch + 1} epochs') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) - - @master_only - def _save_checkpoint(self, runner): - """Save the current checkpoint and delete unwanted checkpoint.""" - runner.save_checkpoint( - self.out_dir, save_optimizer=self.save_optimizer, **self.args) - if runner.meta is not None: - if self.by_epoch: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) - else: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) - runner.meta.setdefault('hook_msgs', dict()) - runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( - self.out_dir, cur_ckpt_filename) - # remove other checkpoints - if self.max_keep_ckpts > 0: - if self.by_epoch: - name = 'epoch_{}.pth' - current_ckpt = runner.epoch + 1 - else: - name = 'iter_{}.pth' - current_ckpt = runner.iter + 1 - redundant_ckpts = range( - current_ckpt - self.max_keep_ckpts * self.interval, 0, - -self.interval) - filename_tmpl = self.args.get('filename_tmpl', name) - for _step in redundant_ckpts: - ckpt_path = self.file_client.join_path( - self.out_dir, filename_tmpl.format(_step)) - if self.file_client.isfile(ckpt_path): - self.file_client.remove(ckpt_path) - else: - break - - def after_train_iter(self, runner): - if self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` iterations - # 2. reach the last iteration of training - if self.every_n_iters( - runner, self.interval) or (self.save_last - and self.is_last_iter(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.iter + 1} iterations') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/closure.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/closure.py deleted file mode 100755 index b955f81f4..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/closure.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ClosureHook(Hook): - - def __init__(self, fn_name, fn): - assert hasattr(self, fn_name) - assert callable(fn) - setattr(self, fn_name, fn) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/ema.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/ema.py deleted file mode 100755 index 15c7e6808..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/ema.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...parallel import is_module_wrapper -from ..hooks.hook import HOOKS, Hook - - -@HOOKS.register_module() -class EMAHook(Hook): - r"""Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. - - .. math:: - - \text{Xema\_{t+1}} = (1 - \text{momentum}) \times - \text{Xema\_{t}} + \text{momentum} \times X_t - - Args: - momentum (float): The momentum used for updating ema parameter. - Defaults to 0.0002. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - warm_up (int): During first warm_up steps, we may use smaller momentum - to update ema parameters more slowly. Defaults to 100. - resume_from (str): The checkpoint path. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - warm_up=100, - resume_from=None): - assert isinstance(interval, int) and interval > 0 - self.warm_up = warm_up - self.interval = interval - assert momentum > 0 and momentum < 1 - self.momentum = momentum**interval - self.checkpoint = resume_from - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - self.model_parameters = dict(model.named_parameters(recurse=True)) - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers(recurse=True)) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - curr_step = runner.iter - # We warm up the momentum considering the instability at beginning - momentum = min(self.momentum, - (1 + curr_step) / (self.warm_up + curr_step)) - if curr_step % self.interval != 0: - return - for name, parameter in self.model_parameters.items(): - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/evaluation.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/evaluation.py deleted file mode 100755 index 1eeb44650..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/evaluation.py +++ /dev/null @@ -1,509 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from math import inf - -import torch.distributed as dist -from torch.nn.modules.batchnorm import _BatchNorm -from torch.utils.data import DataLoader - -from mmcv.fileio import FileClient -from mmcv.utils import is_seq_of -from .hook import Hook -from .logger import LoggerHook - - -class EvalHook(Hook): - """Non-Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader, and return the test results. If ``None``, the default - test function ``mmcv.engine.single_gpu_test`` will be used. - (default: ``None``) - greater_keys (List[str] | None, optional): Metric keys that will be - inferred by 'greater' comparison rule. If ``None``, - _default_greater_keys will be used. (default: ``None``) - less_keys (List[str] | None, optional): Metric keys that will be - inferred by 'less' comparison rule. If ``None``, _default_less_keys - will be used. (default: ``None``) - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - `New in version 1.3.16.` - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - `New in version 1.3.16.` - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - - Notes: - If new arguments are added for EvalHook, tools/test.py, - tools/eval_metric.py may be affected. - """ - - # Since the key for determine greater or less is related to the downstream - # tasks, downstream repos may need to overwrite the following inner - # variable accordingly. - - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - _default_greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - _default_less_keys = ['loss'] - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - out_dir=None, - file_client_args=None, - **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError(f'dataloader must be a pytorch DataLoader, ' - f'but got {type(dataloader)}') - - if interval <= 0: - raise ValueError(f'interval must be a positive number, ' - f'but got {interval}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' - - if start is not None and start < 0: - raise ValueError(f'The evaluation start epoch {start} is smaller ' - f'than 0') - - self.dataloader = dataloader - self.interval = interval - self.start = start - self.by_epoch = by_epoch - - assert isinstance(save_best, str) or save_best is None, \ - '""save_best"" should be a str or None ' \ - f'rather than {type(save_best)}' - self.save_best = save_best - self.eval_kwargs = eval_kwargs - self.initial_flag = True - - if test_fn is None: - from mmcv.engine import single_gpu_test - self.test_fn = single_gpu_test - else: - self.test_fn = test_fn - - if greater_keys is None: - self.greater_keys = self._default_greater_keys - else: - if not isinstance(greater_keys, (list, tuple)): - greater_keys = (greater_keys, ) - assert is_seq_of(greater_keys, str) - self.greater_keys = greater_keys - - if less_keys is None: - self.less_keys = self._default_less_keys - else: - if not isinstance(less_keys, (list, tuple)): - less_keys = (less_keys, ) - assert is_seq_of(less_keys, str) - self.less_keys = less_keys - - if self.save_best is not None: - self.best_ckpt_path = None - self._init_rule(rule, self.save_best) - - self.out_dir = out_dir - self.file_client_args = file_client_args - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific (note that the key indicator matching - is case-insensitive): - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator != 'auto': - # `_lc` here means we use the lower case of keys for - # case-insensitive matching - key_indicator_lc = key_indicator.lower() - greater_keys = [key.lower() for key in self.greater_keys] - less_keys = [key.lower() for key in self.less_keys] - - if key_indicator_lc in greater_keys: - rule = 'greater' - elif key_indicator_lc in less_keys: - rule = 'less' - elif any(key in key_indicator_lc for key in greater_keys): - rule = 'greater' - elif any(key in key_indicator_lc for key in less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - if self.rule is not None: - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'The best checkpoint will be saved to {self.out_dir} by ' - f'{self.file_client.name}')) - - if self.save_best is not None: - if runner.meta is None: - warnings.warn('runner.meta is None. Creating an empty one.') - runner.meta = dict() - runner.meta.setdefault('hook_msgs', dict()) - self.best_ckpt_path = runner.meta['hook_msgs'].get( - 'best_ckpt', None) - - def before_train_iter(self, runner): - """Evaluate the model only at the start of training by iteration.""" - if self.by_epoch or not self.initial_flag: - return - if self.start is not None and runner.iter >= self.start: - self.after_train_iter(runner) - self.initial_flag = False - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - if not (self.by_epoch and self.initial_flag): - return - if self.start is not None and runner.epoch >= self.start: - self.after_train_epoch(runner) - self.initial_flag = False - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch and self._should_evaluate(runner): - # Because the priority of EvalHook is higher than LoggerHook, the - # training log and the evaluating log are mixed. Therefore, - # we need to dump the training log and clear it before evaluating - # log is generated. In addition, this problem will only appear in - # `IterBasedRunner` whose `self.by_epoch` is False, because - # `EpochBasedRunner` whose `self.by_epoch` is True calls - # `_do_evaluate` in `after_train_epoch` stage, and at this stage - # the training log has been printed, so it will not cause any - # problem. more details at - # https://github.com/open-mmlab/mmsegmentation/issues/694 - for hook in runner._hooks: - if isinstance(hook, LoggerHook): - hook.after_train_iter(runner) - runner.log_buffer.clear() - - self._do_evaluate(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch and self._should_evaluate(runner): - self._do_evaluate(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - results = self.test_fn(runner.model, self.dataloader) - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - def _should_evaluate(self, runner): - """Judge whether to perform evaluation. - - Here is the rule to judge whether to perform evaluation: - 1. It will not perform evaluation during the epoch/iteration interval, - which is determined by ``self.interval``. - 2. It will not perform evaluation if the start time is larger than - current time. - 3. It will not perform evaluation when current time is larger than - the start time but during epoch/iteration interval. - - Returns: - bool: The flag indicating whether to perform evaluation. - """ - if self.by_epoch: - current = runner.epoch - check_time = self.every_n_epochs - else: - current = runner.iter - check_time = self.every_n_iters - - if self.start is None: - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - elif (current + 1) < self.start: - # No evaluation if start is larger than the current time. - return False - else: - # Evaluation only at epochs/iters 3, 5, 7... - # if start==3 and interval==2 - if (current + 1 - self.start) % self.interval: - return False - return True - - def _save_ckpt(self, runner, key_score): - """Save the best checkpoint. - - It will compare the score according to the compare function, write - related information (best score, best checkpoint path) and save the - best checkpoint into ``work_dir``. - """ - if self.by_epoch: - current = f'epoch_{runner.epoch + 1}' - cur_type, cur_time = 'epoch', runner.epoch + 1 - else: - current = f'iter_{runner.iter + 1}' - cur_type, cur_time = 'iter', runner.iter + 1 - - best_score = runner.meta['hook_msgs'].get( - 'best_score', self.init_value_map[self.rule]) - if self.compare_func(key_score, best_score): - best_score = key_score - runner.meta['hook_msgs']['best_score'] = best_score - - if self.best_ckpt_path and self.file_client.isfile( - self.best_ckpt_path): - self.file_client.remove(self.best_ckpt_path) - runner.logger.info( - (f'The previous best checkpoint {self.best_ckpt_path} was ' - 'removed')) - - best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' - self.best_ckpt_path = self.file_client.join_path( - self.out_dir, best_ckpt_name) - runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path - - runner.save_checkpoint( - self.out_dir, best_ckpt_name, create_symlink=False) - runner.logger.info( - f'Now best checkpoint is saved as {best_ckpt_name}.') - runner.logger.info( - f'Best {self.key_indicator} is {best_score:0.4f} ' - f'at {cur_time} {cur_type}.') - - def evaluate(self, runner, results): - """Evaluate the results. - - Args: - runner (:obj:`mmcv.Runner`): The underlined training runner. - results (list): Output results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - - if self.save_best is not None: - # If the performance of model is pool, the `eval_res` may be an - # empty dict and it will raise exception when `self.save_best` is - # not None. More details at - # https://github.com/open-mmlab/mmdetection/issues/6265. - if not eval_res: - warnings.warn( - 'Since `eval_res` is an empty dict, the behavior to save ' - 'the best checkpoint will be skipped in this evaluation.') - return None - - if self.key_indicator == 'auto': - # infer from eval_results - self._init_rule(self.rule, list(eval_res.keys())[0]) - return eval_res[self.key_indicator] - - return None - - -class DistEvalHook(EvalHook): - """Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader in a multi-gpu manner, and return the test results. If - ``None``, the default test function ``mmcv.engine.multi_gpu_test`` - will be used. (default: ``None``) - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - broadcast_bn_buffer (bool): Whether to broadcast the - buffer(running_mean and running_var) of rank 0 to other rank - before evaluation. Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - """ - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - broadcast_bn_buffer=True, - tmpdir=None, - gpu_collect=False, - out_dir=None, - file_client_args=None, - **eval_kwargs): - - if test_fn is None: - from mmcv.engine import multi_gpu_test - test_fn = multi_gpu_test - - super().__init__( - dataloader, - start=start, - interval=interval, - by_epoch=by_epoch, - save_best=save_best, - rule=rule, - test_fn=test_fn, - greater_keys=greater_keys, - less_keys=less_keys, - out_dir=out_dir, - file_client_args=file_client_args, - **eval_kwargs) - - self.broadcast_bn_buffer = broadcast_bn_buffer - self.tmpdir = tmpdir - self.gpu_collect = gpu_collect - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - results = self.test_fn( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to - # save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/hook.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/hook.py deleted file mode 100755 index f2d1c9865..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/hook.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, is_method_overridden - -HOOKS = Registry('hook') - - -class Hook: - stages = ('before_run', 'before_train_epoch', 'before_train_iter', - 'after_train_iter', 'after_train_epoch', 'before_val_epoch', - 'before_val_iter', 'after_val_iter', 'after_val_epoch', - 'after_run') - - def before_run(self, runner): - pass - - def after_run(self, runner): - pass - - def before_epoch(self, runner): - pass - - def after_epoch(self, runner): - pass - - def before_iter(self, runner): - pass - - def after_iter(self, runner): - pass - - def before_train_epoch(self, runner): - self.before_epoch(runner) - - def before_val_epoch(self, runner): - self.before_epoch(runner) - - def after_train_epoch(self, runner): - self.after_epoch(runner) - - def after_val_epoch(self, runner): - self.after_epoch(runner) - - def before_train_iter(self, runner): - self.before_iter(runner) - - def before_val_iter(self, runner): - self.before_iter(runner) - - def after_train_iter(self, runner): - self.after_iter(runner) - - def after_val_iter(self, runner): - self.after_iter(runner) - - def every_n_epochs(self, runner, n): - return (runner.epoch + 1) % n == 0 if n > 0 else False - - def every_n_inner_iters(self, runner, n): - return (runner.inner_iter + 1) % n == 0 if n > 0 else False - - def every_n_iters(self, runner, n): - return (runner.iter + 1) % n == 0 if n > 0 else False - - def end_of_epoch(self, runner): - return runner.inner_iter + 1 == len(runner.data_loader) - - def is_last_epoch(self, runner): - return runner.epoch + 1 == runner._max_epochs - - def is_last_iter(self, runner): - return runner.iter + 1 == runner._max_iters - - def get_triggered_stages(self): - trigger_stages = set() - for stage in Hook.stages: - if is_method_overridden(stage, Hook, self): - trigger_stages.add(stage) - - # some methods will be triggered in multi stages - # use this dict to map method to stages. - method_stages_map = { - 'before_epoch': ['before_train_epoch', 'before_val_epoch'], - 'after_epoch': ['after_train_epoch', 'after_val_epoch'], - 'before_iter': ['before_train_iter', 'before_val_iter'], - 'after_iter': ['after_train_iter', 'after_val_iter'], - } - - for method, map_stages in method_stages_map.items(): - if is_method_overridden(method, Hook, self): - trigger_stages.update(map_stages) - - return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/iter_timer.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/iter_timer.py deleted file mode 100755 index a072f4c51..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/iter_timer.py +++ /dev/null @@ -1,30 +0,0 @@ -# copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright (c) OpenMMLab. All rights reserved. - -import time -import torch.distributed as dist - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class IterTimerHook(Hook): - - def before_epoch(self, runner): - self.t = time.time() - - def before_iter(self, runner): - self.batch_size = runner.data_loader._dataloader.batch_size - runner.log_buffer.update({'data_time': time.time() - self.t}) - - def after_iter(self, runner): - iter_info = {'time': time.time() - self.t} - fps = self.batch_size / iter_info["time"] - - if dist.is_initialized(): - fps = fps * dist.get_world_size() - - iter_info["fps"] = fps - - runner.log_buffer.update(iter_info) - self.t = time.time() diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/__init__.py deleted file mode 100755 index a0b6b3456..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import LoggerHook -from .dvclive import DvcliveLoggerHook -from .mlflow import MlflowLoggerHook -from .neptune import NeptuneLoggerHook -from .pavi import PaviLoggerHook -from .tensorboard import TensorboardLoggerHook -from .text import TextLoggerHook -from .wandb import WandbLoggerHook - -__all__ = [ - 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', - 'NeptuneLoggerHook', 'DvcliveLoggerHook' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/base.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/base.py deleted file mode 100755 index f84525672..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/base.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from abc import ABCMeta, abstractmethod - -import numpy as np -import torch - -from ..hook import Hook - - -class LoggerHook(Hook): - """Base class for logger hooks. - - Args: - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging. - by_epoch (bool): Whether EpochBasedRunner is used. - """ - - __metaclass__ = ABCMeta - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - self.interval = interval - self.ignore_last = ignore_last - self.reset_flag = reset_flag - self.by_epoch = by_epoch - - @abstractmethod - def log(self, runner): - pass - - @staticmethod - def is_scalar(val, include_np=True, include_torch=True): - """Tell the input variable is a scalar or not. - - Args: - val: Input variable. - include_np (bool): Whether include 0-d np.ndarray as a scalar. - include_torch (bool): Whether include 0-d torch.Tensor as a scalar. - - Returns: - bool: True or False. - """ - if isinstance(val, numbers.Number): - return True - elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: - return True - elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: - return True - else: - return False - - def get_mode(self, runner): - if runner.mode == 'train': - if 'time' in runner.log_buffer.output: - mode = 'train' - else: - mode = 'val' - elif runner.mode == 'val': - mode = 'val' - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return mode - - def get_epoch(self, runner): - if runner.mode == 'train': - epoch = runner.epoch + 1 - elif runner.mode == 'val': - # normal val mode - # runner.epoch += 1 has been done before val workflow - epoch = runner.epoch - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return epoch - - def get_iter(self, runner, inner_iter=False): - """Get the current training iteration step.""" - if self.by_epoch and inner_iter: - current_iter = runner.inner_iter + 1 - else: - current_iter = runner.iter + 1 - return current_iter - - def get_lr_tags(self, runner): - tags = {} - lrs = runner.current_lr() - if isinstance(lrs, dict): - for name, value in lrs.items(): - tags[f'learning_rate/{name}'] = value[0] - else: - tags['learning_rate'] = lrs[0] - return tags - - def get_momentum_tags(self, runner): - tags = {} - momentums = runner.current_momentum() - if isinstance(momentums, dict): - for name, value in momentums.items(): - tags[f'momentum/{name}'] = value[0] - else: - tags['momentum'] = momentums[0] - return tags - - def get_loggable_tags(self, - runner, - allow_scalar=True, - allow_text=False, - add_mode=True, - tags_to_skip=('time', 'data_time')): - tags = {} - for var, val in runner.log_buffer.output.items(): - if var in tags_to_skip: - continue - if self.is_scalar(val) and not allow_scalar: - continue - if isinstance(val, str) and not allow_text: - continue - if add_mode: - var = f'{self.get_mode(runner)}/{var}' - tags[var] = val - tags.update(self.get_lr_tags(runner)) - tags.update(self.get_momentum_tags(runner)) - return tags - - def before_run(self, runner): - for hook in runner.hooks[::-1]: - if isinstance(hook, LoggerHook): - hook.reset_flag = True - break - - def before_epoch(self, runner): - runner.log_buffer.clear() # clear logs of last epoch - - def after_train_iter(self, runner): - if self.by_epoch and self.every_n_inner_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif not self.by_epoch and self.every_n_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif self.end_of_epoch(runner) and not self.ignore_last: - # not precise but more stable - runner.log_buffer.average(self.interval) - - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_train_epoch(self, runner): - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_val_epoch(self, runner): - runner.log_buffer.average() - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/dvclive.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/dvclive.py deleted file mode 100755 index 687cdc58c..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/dvclive.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class DvcliveLoggerHook(LoggerHook): - """Class to log metrics with dvclive. - - It requires `dvclive`_ to be installed. - - Args: - path (str): Directory where dvclive will write TSV log files. - interval (int): Logging interval (every k iterations). - Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. - Default: True. - - .. _dvclive: - https://dvc.org/doc/dvclive - """ - - def __init__(self, - path, - interval=10, - ignore_last=True, - reset_flag=True, - by_epoch=True): - - super(DvcliveLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.path = path - self.import_dvclive() - - def import_dvclive(self): - try: - import dvclive - except ImportError: - raise ImportError( - 'Please run "pip install dvclive" to install dvclive') - self.dvclive = dvclive - - @master_only - def before_run(self, runner): - self.dvclive.init(self.path) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for k, v in tags.items(): - self.dvclive.log(k, v, step=self.get_iter(runner)) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/mlflow.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/mlflow.py deleted file mode 100755 index f9a72592b..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/mlflow.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class MlflowLoggerHook(LoggerHook): - - def __init__(self, - exp_name=None, - tags=None, - log_model=True, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - """Class to log metrics and (optionally) a trained model to MLflow. - - It requires `MLflow`_ to be installed. - - Args: - exp_name (str, optional): Name of the experiment to be used. - Default None. - If not None, set the active experiment. - If experiment does not exist, an experiment with provided name - will be created. - tags (dict of str: str, optional): Tags for the current run. - Default None. - If not None, set tags for the current run. - log_model (bool, optional): Whether to log an MLflow artifact. - Default True. - If True, log runner.model as an MLflow artifact - for the current run. - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging - by_epoch (bool): Whether EpochBasedRunner is used. - - .. _MLflow: - https://www.mlflow.org/docs/latest/index.html - """ - super(MlflowLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_mlflow() - self.exp_name = exp_name - self.tags = tags - self.log_model = log_model - - def import_mlflow(self): - try: - import mlflow - import mlflow.pytorch as mlflow_pytorch - except ImportError: - raise ImportError( - 'Please run "pip install mlflow" to install mlflow') - self.mlflow = mlflow - self.mlflow_pytorch = mlflow_pytorch - - @master_only - def before_run(self, runner): - super(MlflowLoggerHook, self).before_run(runner) - if self.exp_name is not None: - self.mlflow.set_experiment(self.exp_name) - if self.tags is not None: - self.mlflow.set_tags(self.tags) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.mlflow.log_metrics(tags, step=self.get_iter(runner)) - - @master_only - def after_run(self, runner): - if self.log_model: - self.mlflow_pytorch.log_model(runner.model, 'models') diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/neptune.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/neptune.py deleted file mode 100755 index 7a38772b0..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/neptune.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class NeptuneLoggerHook(LoggerHook): - """Class to log metrics to NeptuneAI. - - It requires `neptune-client` to be installed. - - Args: - init_kwargs (dict): a dict contains the initialization keys as below: - - project (str): Name of a project in a form of - namespace/project_name. If None, the value of - NEPTUNE_PROJECT environment variable will be taken. - - api_token (str): User’s API token. - If None, the value of NEPTUNE_API_TOKEN environment - variable will be taken. Note: It is strongly recommended - to use NEPTUNE_API_TOKEN environment variable rather than - placing your API token in plain text in your source code. - - name (str, optional, default is 'Untitled'): Editable name of - the run. Name is displayed in the run's Details and in - Runs table as a column. - Check https://docs.neptune.ai/api-reference/neptune#init for - more init arguments. - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging - by_epoch (bool): Whether EpochBasedRunner is used. - - .. _NeptuneAI: - https://docs.neptune.ai/you-should-know/logging-metadata - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=True, - with_step=True, - by_epoch=True): - - super(NeptuneLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_neptune() - self.init_kwargs = init_kwargs - self.with_step = with_step - - def import_neptune(self): - try: - import neptune.new as neptune - except ImportError: - raise ImportError( - 'Please run "pip install neptune-client" to install neptune') - self.neptune = neptune - self.run = None - - @master_only - def before_run(self, runner): - if self.init_kwargs: - self.run = self.neptune.init(**self.init_kwargs) - else: - self.run = self.neptune.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for tag_name, tag_value in tags.items(): - if self.with_step: - self.run[tag_name].log( - tag_value, step=self.get_iter(runner)) - else: - tags['global_step'] = self.get_iter(runner) - self.run[tag_name].log(tags) - - @master_only - def after_run(self, runner): - self.run.stop() diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/pavi.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/pavi.py deleted file mode 100755 index ba2f6e8df..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/pavi.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp - -import torch -import yaml - -import mmcv -from ....parallel.utils import is_module_wrapper -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class PaviLoggerHook(LoggerHook): - - def __init__(self, - init_kwargs=None, - add_graph=False, - add_last_ckpt=False, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - img_key='img_info'): - super(PaviLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.init_kwargs = init_kwargs - self.add_graph = add_graph - self.add_last_ckpt = add_last_ckpt - self.img_key = img_key - - @master_only - def before_run(self, runner): - super(PaviLoggerHook, self).before_run(runner) - try: - from pavi import SummaryWriter - except ImportError: - raise ImportError('Please run "pip install pavi" to install pavi.') - - self.run_name = runner.work_dir.split('/')[-1] - - if not self.init_kwargs: - self.init_kwargs = dict() - self.init_kwargs['name'] = self.run_name - self.init_kwargs['model'] = runner._model_name - if runner.meta is not None: - if 'config_dict' in runner.meta: - config_dict = runner.meta['config_dict'] - assert isinstance( - config_dict, - dict), ('meta["config_dict"] has to be of a dict, ' - f'but got {type(config_dict)}') - elif 'config_file' in runner.meta: - config_file = runner.meta['config_file'] - config_dict = dict(mmcv.Config.fromfile(config_file)) - else: - config_dict = None - if config_dict is not None: - # 'max_.*iter' is parsed in pavi sdk as the maximum iterations - # to properly set up the progress bar. - config_dict = config_dict.copy() - config_dict.setdefault('max_iter', runner.max_iters) - # non-serializable values are first converted in - # mmcv.dump to json - config_dict = json.loads( - mmcv.dump(config_dict, file_format='json')) - session_text = yaml.dump(config_dict) - self.init_kwargs['session_text'] = session_text - self.writer = SummaryWriter(**self.init_kwargs) - - def get_step(self, runner): - """Get the total training step/epoch.""" - if self.get_mode(runner) == 'val' and self.by_epoch: - return self.get_epoch(runner) - else: - return self.get_iter(runner) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, add_mode=False) - if tags: - self.writer.add_scalars( - self.get_mode(runner), tags, self.get_step(runner)) - - @master_only - def after_run(self, runner): - if self.add_last_ckpt: - ckpt_path = osp.join(runner.work_dir, 'latest.pth') - if osp.islink(ckpt_path): - ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) - - if osp.isfile(ckpt_path): - # runner.epoch += 1 has been done before `after_run`. - iteration = runner.epoch if self.by_epoch else runner.iter - return self.writer.add_snapshot_file( - tag=self.run_name, - snapshot_file_path=ckpt_path, - iteration=iteration) - - # flush the buffer and send a task ending signal to Pavi - self.writer.close() - - @master_only - def before_epoch(self, runner): - if runner.epoch == 0 and self.add_graph: - if is_module_wrapper(runner.model): - _model = runner.model.module - else: - _model = runner.model - device = next(_model.parameters()).device - data = next(iter(runner.data_loader)) - image = data[self.img_key][0:1].to(device) - with torch.no_grad(): - self.writer.add_graph(_model, image) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/tensorboard.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/tensorboard.py deleted file mode 100755 index a8d50366f..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/tensorboard.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import TORCH_VERSION, digit_version -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TensorboardLoggerHook(LoggerHook): - - def __init__(self, - log_dir=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(TensorboardLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.log_dir = log_dir - - @master_only - def before_run(self, runner): - super(TensorboardLoggerHook, self).before_run(runner) - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.1')): - try: - from tensorboardX import SummaryWriter - except ImportError: - raise ImportError('Please install tensorboardX to use ' - 'TensorboardLoggerHook.') - else: - try: - from torch.utils.tensorboard import SummaryWriter - except ImportError: - raise ImportError( - 'Please run "pip install future tensorboard" to install ' - 'the dependencies to use torch.utils.tensorboard ' - '(applicable to PyTorch 1.1 or higher)') - - if self.log_dir is None: - self.log_dir = osp.join(runner.work_dir, 'tf_logs') - self.writer = SummaryWriter(self.log_dir) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, allow_text=True) - for tag, val in tags.items(): - if isinstance(val, str): - self.writer.add_text(tag, val, self.get_iter(runner)) - else: - self.writer.add_scalar(tag, val, self.get_iter(runner)) - - @master_only - def after_run(self, runner): - self.writer.close() diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/text.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/text.py deleted file mode 100755 index 043c7bf20..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/text.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import datetime -import os -import os.path as osp -from collections import OrderedDict - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.fileio.file_client import FileClient -from mmcv.utils import is_tuple_of, scandir -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TextLoggerHook(LoggerHook): - """Logger hook in text. - - In this logger hook, the information will be printed on terminal and - saved in json file. - - Args: - by_epoch (bool, optional): Whether EpochBasedRunner is used. - Default: True. - interval (int, optional): Logging interval (every k iterations). - Default: 10. - ignore_last (bool, optional): Ignore the log of last iterations in each - epoch if less than :attr:`interval`. Default: True. - reset_flag (bool, optional): Whether to clear the output buffer after - logging. Default: False. - interval_exp_name (int, optional): Logging interval for experiment - name. This feature is to help users conveniently get the experiment - information from screen or log file. Default: 1000. - out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. - If ``out_dir`` is specified, logs will be copied to a new directory - which is the concatenation of ``out_dir`` and the last level - directory of ``runner.work_dir``. Default: None. - `New in version 1.3.16.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be copied to ``out_dir``. - Default: ('.log.json', '.log', '.py'). - `New in version 1.3.16.` - keep_local (bool, optional): Whether to keep local log when - :attr:`out_dir` is specified. If False, the local log will be - removed. Default: True. - `New in version 1.3.16.` - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - - def __init__(self, - by_epoch=True, - interval=10, - ignore_last=True, - reset_flag=False, - interval_exp_name=1000, - out_dir=None, - out_suffix=('.log.json', '.log', '.py'), - keep_local=True, - file_client_args=None): - super(TextLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.by_epoch = by_epoch - self.time_sec_tot = 0 - self.interval_exp_name = interval_exp_name - - if out_dir is None and file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" when `out_dir` is not' - 'specified.') - self.out_dir = out_dir - - if not (out_dir is None or isinstance(out_dir, str) - or is_tuple_of(out_dir, str)): - raise TypeError('out_dir should be "None" or string or tuple of ' - 'string, but got {out_dir}') - self.out_suffix = out_suffix - - self.keep_local = keep_local - self.file_client_args = file_client_args - if self.out_dir is not None: - self.file_client = FileClient.infer_client(file_client_args, - self.out_dir) - - def before_run(self, runner): - super(TextLoggerHook, self).before_run(runner) - - if self.out_dir is not None: - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - # The final `self.out_dir` is the concatenation of `self.out_dir` - # and the last level directory of `runner.work_dir` - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'Text logs will be saved to {self.out_dir} by ' - f'{self.file_client.name} after the training process.')) - - self.start_iter = runner.iter - self.json_log_path = osp.join(runner.work_dir, - f'{runner.timestamp}.log.json') - if runner.meta is not None: - self._dump_log(runner.meta, runner) - - def _get_max_memory(self, runner): - device = getattr(runner.model, 'output_device', None) - mem = torch.cuda.max_memory_allocated(device=device) - mem_mb = torch.tensor([mem / (1024 * 1024)], - dtype=torch.int, - device=device) - if runner.world_size > 1: - dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) - return mem_mb.item() - - def _log_info(self, log_dict, runner): - # print exp name for users to distinguish experiments - # at every ``interval_exp_name`` iterations and the end of each epoch - if runner.meta is not None and 'exp_name' in runner.meta: - if (self.every_n_iters(runner, self.interval_exp_name)) or ( - self.by_epoch and self.end_of_epoch(runner)): - exp_info = f'Exp name: {runner.meta["exp_name"]}' - runner.logger.info(exp_info) - - if log_dict['mode'] == 'train': - if isinstance(log_dict['lr'], dict): - lr_str = [] - for k, val in log_dict['lr'].items(): - lr_str.append(f'lr_{k}: {val:.3e}') - lr_str = ' '.join(lr_str) - else: - lr_str = f'lr: {log_dict["lr"]:.3e}' - - # by epoch: Epoch [4][100/1000] - # by iter: Iter [100/100000] - if self.by_epoch: - log_str = f'Epoch [{log_dict["epoch"]}]' \ - f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' - else: - log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' - log_str += f'{lr_str}, ' - - if 'time' in log_dict.keys(): - self.time_sec_tot += (log_dict['time'] * self.interval) - time_sec_avg = self.time_sec_tot / ( - runner.iter - self.start_iter + 1) - eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) - eta_str = str(datetime.timedelta(seconds=int(eta_sec))) - log_str += f'eta: {eta_str}, ' - log_str += f'time: {log_dict["time"]:.3f}, ' \ - f'data_time: {log_dict["data_time"]:.3f}, ' - # statistic memory - if torch.cuda.is_available(): - log_str += f'memory: {log_dict["memory"]}, ' - else: - # val/test time - # here 1000 is the length of the val dataloader - # by epoch: Epoch[val] [4][1000] - # by iter: Iter[val] [1000] - if self.by_epoch: - log_str = f'Epoch({log_dict["mode"]}) ' \ - f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' - else: - log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' - - log_items = [] - for name, val in log_dict.items(): - # TODO: resolve this hack - # these items have been in log_str - if name in [ - 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', - 'memory', 'epoch' - ]: - continue - if isinstance(val, float): - val = f'{val:.4f}' - log_items.append(f'{name}: {val}') - log_str += ', '.join(log_items) - - runner.logger.info(log_str) - - def _dump_log(self, log_dict, runner): - # dump log in json format - json_log = OrderedDict() - for k, v in log_dict.items(): - json_log[k] = self._round_float(v) - # only append log at last line - if runner.rank == 0: - with open(self.json_log_path, 'a+') as f: - mmcv.dump(json_log, f, file_format='json') - f.write('\n') - - def _round_float(self, items): - if isinstance(items, list): - return [self._round_float(item) for item in items] - elif isinstance(items, float): - return round(items, 5) - else: - return items - - def log(self, runner): - if 'eval_iter_num' in runner.log_buffer.output: - # this doesn't modify runner.iter and is regardless of by_epoch - cur_iter = runner.log_buffer.output.pop('eval_iter_num') - else: - cur_iter = self.get_iter(runner, inner_iter=True) - - log_dict = OrderedDict( - mode=self.get_mode(runner), - epoch=self.get_epoch(runner), - iter=cur_iter) - - # only record lr of the first param group - cur_lr = runner.current_lr() - if isinstance(cur_lr, list): - log_dict['lr'] = cur_lr[0] - else: - assert isinstance(cur_lr, dict) - log_dict['lr'] = {} - for k, lr_ in cur_lr.items(): - assert isinstance(lr_, list) - log_dict['lr'].update({k: lr_[0]}) - - if 'time' in runner.log_buffer.output: - # statistic memory - if torch.cuda.is_available(): - log_dict['memory'] = self._get_max_memory(runner) - - log_dict = dict(log_dict, **runner.log_buffer.output) - - self._log_info(log_dict, runner) - self._dump_log(log_dict, runner) - return log_dict - - def after_run(self, runner): - # copy or upload logs to self.out_dir - if self.out_dir is not None: - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - out_filepath = self.file_client.join_path( - self.out_dir, filename) - with open(local_filepath, 'r') as f: - self.file_client.put_text(f.read(), out_filepath) - - runner.logger.info( - (f'The file {local_filepath} has been uploaded to ' - f'{out_filepath}.')) - - if not self.keep_local: - os.remove(local_filepath) - runner.logger.info( - (f'{local_filepath} was removed due to the ' - '`self.keep_local=False`')) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/wandb.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/wandb.py deleted file mode 100755 index 9f6808462..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/logger/wandb.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class WandbLoggerHook(LoggerHook): - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - commit=True, - by_epoch=True, - with_step=True): - super(WandbLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_wandb() - self.init_kwargs = init_kwargs - self.commit = commit - self.with_step = with_step - - def import_wandb(self): - try: - import wandb - except ImportError: - raise ImportError( - 'Please run "pip install wandb" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(WandbLoggerHook, self).before_run(runner) - if self.wandb is None: - self.import_wandb() - if self.init_kwargs: - self.wandb.init(**self.init_kwargs) - else: - self.wandb.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - if self.with_step: - self.wandb.log( - tags, step=self.get_iter(runner), commit=self.commit) - else: - tags['global_step'] = self.get_iter(runner) - self.wandb.log(tags, commit=self.commit) - - @master_only - def after_run(self, runner): - self.wandb.join() diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/lr_updater.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/lr_updater.py deleted file mode 100755 index e5a124157..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/lr_updater.py +++ /dev/null @@ -1,670 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from math import cos, pi - -import mmcv -from .hook import HOOKS, Hook - - -class LrUpdaterHook(Hook): - """LR Scheduler in MMCV. - - Args: - by_epoch (bool): LR changes epoch by epoch - warmup (string): Type of warmup used. It can be None(use no warmup), - 'constant', 'linear' or 'exp' - warmup_iters (int): The number of iterations or epochs that warmup - lasts - warmup_ratio (float): LR used at the beginning of warmup equals to - warmup_ratio * initial_lr - warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters - means the number of epochs that warmup lasts, otherwise means the - number of iteration that warmup lasts - """ - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.1, - warmup_by_epoch=False): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_ratio" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - self.warmup_by_epoch = warmup_by_epoch - - if self.warmup_by_epoch: - self.warmup_epochs = self.warmup_iters - self.warmup_iters = None - else: - self.warmup_epochs = None - - self.base_lr = [] # initial lr for all param groups - self.regular_lr = [] # expected lr if no warming up is performed - - def _set_lr(self, runner, lr_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, lr in zip(optim.param_groups, lr_groups[k]): - param_group['lr'] = lr - else: - for param_group, lr in zip(runner.optimizer.param_groups, - lr_groups): - param_group['lr'] = lr - - def get_lr(self, runner, base_lr): - raise NotImplementedError - - def get_regular_lr(self, runner): - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(runner, _base_lr) - for _base_lr in self.base_lr[k] - ] - lr_groups.update({k: _lr_group}) - - return lr_groups - else: - return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - if self.warmup == 'constant': - warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_lr = [_lr * (1 - k) for _lr in regular_lr] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.regular_lr, dict): - lr_groups = {} - for key, regular_lr in self.regular_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.regular_lr) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - group.setdefault('initial_lr', group['lr']) - _base_lr = [ - group['initial_lr'] for group in optim.param_groups - ] - self.base_lr.update({k: _base_lr}) - else: - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['initial_lr'] for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if self.warmup_iters is None: - epoch_len = len(runner.data_loader) - self.warmup_iters = self.warmup_epochs * epoch_len - - if not self.by_epoch: - return - - self.regular_lr = self.get_regular_lr(runner) - self._set_lr(runner, self.regular_lr) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_lr = self.get_regular_lr(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - - -@HOOKS.register_module() -class FixedLrUpdaterHook(LrUpdaterHook): - - def __init__(self, **kwargs): - super(FixedLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - return base_lr - - -@HOOKS.register_module() -class StepLrUpdaterHook(LrUpdaterHook): - """Step LR scheduler with min_lr clipping. - - Args: - step (int | list[int]): Step to decay the LR. If an int value is given, - regard it as the decay interval. If a list is given, decay LR at - these steps. - gamma (float, optional): Decay LR ratio. Default: 0.1. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. If None - is given, we don't perform lr clipping. Default: None. - """ - - def __init__(self, step, gamma=0.1, min_lr=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_lr = min_lr - super(StepLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - lr = base_lr * (self.gamma**exp) - if self.min_lr is not None: - # clip to a minimum value - lr = max(lr, self.min_lr) - return lr - - -@HOOKS.register_module() -class ExpLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, **kwargs): - self.gamma = gamma - super(ExpLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * self.gamma**progress - - -@HOOKS.register_module() -class PolyLrUpdaterHook(LrUpdaterHook): - - def __init__(self, power=1., min_lr=0., **kwargs): - self.power = power - self.min_lr = min_lr - super(PolyLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - coeff = (1 - progress / max_progress)**self.power - return (base_lr - self.min_lr) * coeff + self.min_lr - - -@HOOKS.register_module() -class InvLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, power=1., **kwargs): - self.gamma = gamma - self.power = power - super(InvLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * (1 + self.gamma * progress)**(-self.power) - - -@HOOKS.register_module() -class CosineAnnealingLrUpdaterHook(LrUpdaterHook): - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(CosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): - """Flat + Cosine lr schedule. - - Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 - - Args: - start_percent (float): When to start annealing the learning rate - after the percentage of the total training steps. - The value should be in range [0, 1). - Default: 0.75 - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - start_percent=0.75, - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - if start_percent < 0 or start_percent > 1 or not isinstance( - start_percent, float): - raise ValueError( - 'expected float between 0 and 1 start_percent, but ' - f'got {start_percent}') - self.start_percent = start_percent - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(FlatCosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - start = round(runner.max_epochs * self.start_percent) - progress = runner.epoch - start - max_progress = runner.max_epochs - start - else: - start = round(runner.max_iters * self.start_percent) - progress = runner.iter - start - max_progress = runner.max_iters - start - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress < 0: - return base_lr - else: - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class CosineRestartLrUpdaterHook(LrUpdaterHook): - """Cosine annealing with restarts learning rate scheme. - - Args: - periods (list[int]): Periods for each cosine anneling cycle. - restart_weights (list[float], optional): Restart weights at each - restart iteration. Default: [1]. - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - periods, - restart_weights=[1], - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.periods = periods - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - self.restart_weights = restart_weights - assert (len(self.periods) == len(self.restart_weights) - ), 'periods and restart_weights should have the same length.' - super(CosineRestartLrUpdaterHook, self).__init__(**kwargs) - - self.cumulative_periods = [ - sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) - ] - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - else: - progress = runner.iter - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - idx = get_position_from_periods(progress, self.cumulative_periods) - current_weight = self.restart_weights[idx] - nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] - current_periods = self.periods[idx] - - alpha = min((progress - nearest_restart) / current_periods, 1) - return annealing_cos(base_lr, target_lr, alpha, current_weight) - - -def get_position_from_periods(iteration, cumulative_periods): - """Get the position from a period list. - - It will return the index of the right-closest number in the period list. - For example, the cumulative_periods = [100, 200, 300, 400], - if iteration == 50, return 0; - if iteration == 210, return 2; - if iteration == 300, return 3. - - Args: - iteration (int): Current iteration. - cumulative_periods (list[int]): Cumulative period list. - - Returns: - int: The position of the right-closest number in the period list. - """ - for i, period in enumerate(cumulative_periods): - if iteration < period: - return i - raise ValueError(f'Current iteration {iteration} exceeds ' - f'cumulative_periods {cumulative_periods}') - - -@HOOKS.register_module() -class CyclicLrUpdaterHook(LrUpdaterHook): - """Cyclic LR Scheduler. - - Implement the cyclical learning rate policy (CLR) described in - https://arxiv.org/pdf/1506.01186.pdf - - Different from the original paper, we use cosine annealing rather than - triangular policy inside a cycle. This improves the performance in the - 3D detection area. - - Args: - by_epoch (bool): Whether to update LR by epoch. - target_ratio (tuple[float]): Relative ratio of the highest LR and the - lowest LR to the initial LR. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of LR in - the total cycle. - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - """ - - def __init__(self, - by_epoch=False, - target_ratio=(10, 1e-4), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.lr_phases = [] # init lr_phases - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicLrUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicLrUpdaterHook, self).before_run(runner) - # initiate lr_phases - # total lr_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.lr_phases.append( - [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) - self.lr_phases.append([ - iter_up_phase, max_iter_per_phase, max_iter_per_phase, - self.target_ratio[0], self.target_ratio[1] - ]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - for (start_iter, end_iter, max_iter_per_phase, start_ratio, - end_ratio) in self.lr_phases: - curr_iter %= max_iter_per_phase - if start_iter <= curr_iter < end_iter: - progress = curr_iter - start_iter - return self.anneal_func(base_lr * start_ratio, - base_lr * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleLrUpdaterHook(LrUpdaterHook): - """One Cycle LR Scheduler. - - The 1cycle learning rate policy changes the learning rate after every - batch. The one cycle learning rate policy is described in - https://arxiv.org/pdf/1708.07120.pdf - - Args: - max_lr (float or list): Upper learning rate boundaries in the cycle - for each parameter group. - total_steps (int, optional): The total number of steps in the cycle. - Note that if a value is not provided here, it will be the max_iter - of runner. Default: None. - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - div_factor (float): Determines the initial learning rate via - initial_lr = max_lr/div_factor - Default: 25 - final_div_factor (float): Determines the minimum learning rate via - min_lr = initial_lr/final_div_factor - Default: 1e4 - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - max_lr, - total_steps=None, - pct_start=0.3, - anneal_strategy='cos', - div_factor=25, - final_div_factor=1e4, - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch = False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(max_lr, (numbers.Number, list, dict)): - raise ValueError('the type of max_lr must be the one of list or ' - f'dict, but got {type(max_lr)}') - self._max_lr = max_lr - if total_steps is not None: - if not isinstance(total_steps, int): - raise ValueError('the type of total_steps must be int, but' - f'got {type(total_steps)}') - self.total_steps = total_steps - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.div_factor = div_factor - self.final_div_factor = final_div_factor - self.three_phase = three_phase - self.lr_phases = [] # init lr_phases - super(OneCycleLrUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if hasattr(self, 'total_steps'): - total_steps = self.total_steps - else: - total_steps = runner.max_iters - if total_steps < runner.max_iters: - raise ValueError( - 'The total steps must be greater than or equal to max ' - f'iterations {runner.max_iters} of runner, but total steps ' - f'is {total_steps}.') - - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - _max_lr = format_param(k, optim, self._max_lr) - self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(optim.param_groups, self.base_lr[k]): - group.setdefault('initial_lr', lr) - else: - k = type(runner.optimizer).__name__ - _max_lr = format_param(k, runner.optimizer, self._max_lr) - self.base_lr = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(runner.optimizer.param_groups, self.base_lr): - group.setdefault('initial_lr', lr) - - if self.three_phase: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append([ - float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 - ]) - self.lr_phases.append( - [total_steps - 1, 1, 1 / self.final_div_factor]) - else: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append( - [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - start_iter = 0 - for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): - if curr_iter <= end_iter: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, - pct) - break - start_iter = end_iter - return lr - - -def annealing_cos(start, end, factor, weight=1): - """Calculate annealing cos learning rate. - - Cosine anneal from `weight * start + (1 - weight) * end` to `end` as - percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the cosine annealing. - end (float): The ending learing rate of the cosine annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - weight (float, optional): The combination factor of `start` and `end` - when calculating the actual starting learning rate. Default to 1. - """ - cos_out = cos(pi * factor) + 1 - return end + 0.5 * weight * (start - end) * cos_out - - -def annealing_linear(start, end, factor): - """Calculate annealing linear learning rate. - - Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the linear annealing. - end (float): The ending learing rate of the linear annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - """ - return start + (end - start) * factor - - -def format_param(name, optim, param): - if isinstance(param, numbers.Number): - return [param] * len(optim.param_groups) - elif isinstance(param, (list, tuple)): # multi param groups - if len(param) != len(optim.param_groups): - raise ValueError(f'expected {len(optim.param_groups)} ' - f'values for {name}, got {len(param)}') - return param - else: # multi optimizers - if name not in param: - raise KeyError(f'{name} is not found in {param.keys()}') - return param[name] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/memory.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/memory.py deleted file mode 100755 index 70cf9a838..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class EmptyCacheHook(Hook): - - def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): - self._before_epoch = before_epoch - self._after_epoch = after_epoch - self._after_iter = after_iter - - def after_iter(self, runner): - if self._after_iter: - torch.cuda.empty_cache() - - def before_epoch(self, runner): - if self._before_epoch: - torch.cuda.empty_cache() - - def after_epoch(self, runner): - if self._after_epoch: - torch.cuda.empty_cache() diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/momentum_updater.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/momentum_updater.py deleted file mode 100755 index 13d0e2fab..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/momentum_updater.py +++ /dev/null @@ -1,493 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -from .hook import HOOKS, Hook -from .lr_updater import annealing_cos, annealing_linear, format_param - - -class MomentumUpdaterHook(Hook): - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.9): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_momentum" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - - self.base_momentum = [] # initial momentum for all param groups - self.regular_momentum = [ - ] # expected momentum if no warming up is performed - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, base_momentum): - raise NotImplementedError - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k in runner.optimizer.keys(): - _momentum_group = [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum[k] - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - return [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum - ] - - def get_warmup_momentum(self, cur_iters): - - def _get_warmup_momentum(cur_iters, regular_momentum): - if self.warmup == 'constant': - warmup_momentum = [ - _momentum / self.warmup_ratio - for _momentum in self.regular_momentum - ] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_momentum = [ - _momentum / (1 - k) for _momentum in self.regular_mom - ] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_momentum = [ - _momentum / k for _momentum in self.regular_mom - ] - return warmup_momentum - - if isinstance(self.regular_momentum, dict): - momentum_groups = {} - for key, regular_momentum in self.regular_momentum.items(): - momentum_groups[key] = _get_warmup_momentum( - cur_iters, regular_momentum) - return momentum_groups - else: - return _get_warmup_momentum(cur_iters, self.regular_momentum) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, - # if 'initial_momentum' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_momentum = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - _base_momentum = [ - group['initial_momentum'] for group in optim.param_groups - ] - self.base_momentum.update({k: _base_momentum}) - else: - for group in runner.optimizer.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - self.base_momentum = [ - group['initial_momentum'] - for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if not self.by_epoch: - return - self.regular_mom = self.get_regular_momentum(runner) - self._set_momentum(runner, self.regular_mom) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_mom = self.get_regular_momentum(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_momentum(runner, self.regular_mom) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_momentum(runner, self.regular_mom) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - - -@HOOKS.register_module() -class StepMomentumUpdaterHook(MomentumUpdaterHook): - """Step momentum scheduler with min value clipping. - - Args: - step (int | list[int]): Step to decay the momentum. If an int value is - given, regard it as the decay interval. If a list is given, decay - momentum at these steps. - gamma (float, optional): Decay momentum ratio. Default: 0.5. - min_momentum (float, optional): Minimum momentum value to keep. If - momentum after decay is lower than this value, it will be clipped - accordingly. If None is given, we don't perform lr clipping. - Default: None. - """ - - def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_momentum = min_momentum - super(StepMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - momentum = base_momentum * (self.gamma**exp) - if self.min_momentum is not None: - # clip to a minimum value - momentum = max(momentum, self.min_momentum) - return momentum - - -@HOOKS.register_module() -class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(CosineAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_cos(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class CyclicMomentumUpdaterHook(MomentumUpdaterHook): - """Cyclic momentum Scheduler. - - Implement the cyclical momentum scheduler policy described in - https://arxiv.org/pdf/1708.07120.pdf - - This momentum scheduler usually used together with the CyclicLRUpdater - to improve the performance in the 3D detection area. - - Attributes: - target_ratio (tuple[float]): Relative ratio of the lowest momentum and - the highest momentum to the initial momentum. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of momentum - in the total cycle. - by_epoch (bool): Whether to update momentum by epoch. - """ - - def __init__(self, - by_epoch=False, - target_ratio=(0.85 / 0.95, 1), - cyclic_times=1, - step_ratio_up=0.4, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.momentum_phases = [] # init momentum_phases - # currently only support by_epoch=False - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicMomentumUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicMomentumUpdaterHook, self).before_run(runner) - # initiate momentum_phases - # total momentum_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.momentum_phases.append( - [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) - self.momentum_phases.append([ - iter_up_phase, max_iter_per_phase, max_iter_per_phase, - self.target_ratio[0], self.target_ratio[1] - ]) - - def get_momentum(self, runner, base_momentum): - curr_iter = runner.iter - for (start_iter, end_iter, max_iter_per_phase, start_ratio, - end_ratio) in self.momentum_phases: - curr_iter %= max_iter_per_phase - if start_iter <= curr_iter < end_iter: - progress = curr_iter - start_iter - return annealing_cos(base_momentum * start_ratio, - base_momentum * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): - """OneCycle momentum Scheduler. - - This momentum scheduler usually used together with the OneCycleLrUpdater - to improve the performance. - - Args: - base_momentum (float or list): Lower momentum boundaries in the cycle - for each parameter group. Note that momentum is cycled inversely - to learning rate; at the peak of a cycle, momentum is - 'base_momentum' and learning rate is 'max_lr'. - Default: 0.85 - max_momentum (float or list): Upper momentum boundaries in the cycle - for each parameter group. Functionally, - it defines the cycle amplitude (max_momentum - base_momentum). - Note that momentum is cycled inversely - to learning rate; at the start of a cycle, momentum is - 'max_momentum' and learning rate is 'base_lr' - Default: 0.95 - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - base_momentum=0.85, - max_momentum=0.95, - pct_start=0.3, - anneal_strategy='cos', - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch=False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(base_momentum, (float, list, dict)): - raise ValueError('base_momentum must be the type among of float,' - 'list or dict.') - self._base_momentum = base_momentum - if not isinstance(max_momentum, (float, list, dict)): - raise ValueError('max_momentum must be the type among of float,' - 'list or dict.') - self._max_momentum = max_momentum - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('Expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must by one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.three_phase = three_phase - self.momentum_phases = [] # init momentum_phases - super(OneCycleMomentumUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip( - optim.param_groups, _base_momentum, _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - else: - optim = runner.optimizer - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - k = type(optim).__name__ - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip(optim.param_groups, - _base_momentum, - _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - - if self.three_phase: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': - float(2 * self.pct_start * runner.max_iters) - 2, - 'start_momentum': - 'base_momentum', - 'end_momentum': - 'max_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'max_momentum', - 'end_momentum': 'max_momentum' - }) - else: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'base_momentum', - 'end_momentum': 'max_momentum' - }) - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, param_group): - curr_iter = runner.iter - start_iter = 0 - for i, phase in enumerate(self.momentum_phases): - end_iter = phase['end_iter'] - if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - momentum = self.anneal_func( - param_group[phase['start_momentum']], - param_group[phase['end_momentum']], pct) - break - start_iter = end_iter - return momentum - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k, optim in runner.optimizer.items(): - _momentum_group = [ - self.get_momentum(runner, param_group) - for param_group in optim.param_groups - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - momentum_groups = [] - for param_group in runner.optimizer.param_groups: - momentum_groups.append(self.get_momentum(runner, param_group)) - return momentum_groups diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/optimizer.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/optimizer.py deleted file mode 100755 index f575ceda0..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/optimizer.py +++ /dev/null @@ -1,508 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -from collections import defaultdict -from itertools import chain - -from torch.nn.utils import clip_grad - -from mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version -from ..dist_utils import allreduce_grads -from ..fp16_utils import LossScaler, wrap_fp16_model -from .hook import HOOKS, Hook - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - from torch.cuda.amp import GradScaler -except ImportError: - pass - - -@HOOKS.register_module() -class OptimizerHook(Hook): - - def __init__(self, grad_clip=None): - self.grad_clip = grad_clip - - def clip_grads(self, params): - params = list( - filter(lambda p: p.requires_grad and p.grad is not None, params)) - if len(params) > 0: - return clip_grad.clip_grad_norm_(params, **self.grad_clip) - - def after_train_iter(self, runner): - runner.optimizer.zero_grad() - runner.outputs['loss'].backward() - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - - -@HOOKS.register_module() -class GradientCumulativeOptimizerHook(OptimizerHook): - """Optimizer Hook implements multi-iters gradient cumulating. - - Args: - cumulative_iters (int, optional): Num of gradient cumulative iters. - The optimizer will step every `cumulative_iters` iters. - Defaults to 1. - - Examples: - >>> # Use cumulative_iters to simulate a large batch size - >>> # It is helpful when the hardware cannot handle a large batch size. - >>> loader = DataLoader(data, batch_size=64) - >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) - >>> # almost equals to - >>> loader = DataLoader(data, batch_size=256) - >>> optim_hook = OptimizerHook() - """ - - def __init__(self, cumulative_iters=1, **kwargs): - super(GradientCumulativeOptimizerHook, self).__init__(**kwargs) - - assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ - f'cumulative_iters only accepts positive int, but got ' \ - f'{type(cumulative_iters)} instead.' - - self.cumulative_iters = cumulative_iters - self.divisible_iters = 0 - self.remainder_iters = 0 - self.initialized = False - - def has_batch_norm(self, module): - if isinstance(module, _BatchNorm): - return True - for m in module.children(): - if self.has_batch_norm(m): - return True - return False - - def _init(self, runner): - if runner.iter % self.cumulative_iters != 0: - runner.logger.warning( - 'Resume iter number is not divisible by cumulative_iters in ' - 'GradientCumulativeOptimizerHook, which means the gradient of ' - 'some iters is lost and the result may be influenced slightly.' - ) - - if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: - runner.logger.warning( - 'GradientCumulativeOptimizerHook may slightly decrease ' - 'performance if the model has BatchNorm layers.') - - residual_iters = runner.max_iters - runner.iter - - self.divisible_iters = ( - residual_iters // self.cumulative_iters * self.cumulative_iters) - self.remainder_iters = residual_iters - self.divisible_iters - - self.initialized = True - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - runner.optimizer.zero_grad() - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - self.loss_scaler = GradScaler() - elif isinstance(loss_scale, float): - self._scale_update_param = loss_scale - self.loss_scaler = GradScaler(init_scale=loss_scale) - elif isinstance(loss_scale, dict): - self.loss_scaler = GradScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training.""" - # wrap model mode to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer to - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients. - 3. Unscale the optimizer’s gradient tensors. - 4. Call optimizer.step() and update scale factor. - 5. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - - self.loss_scaler.scale(runner.outputs['loss']).backward() - self.loss_scaler.unscale_(runner.optimizer) - # grad clip - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using PyTorch's implementation) implements - multi-iters gradient cumulating. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - """ - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - - self.loss_scaler.scale(loss).backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - self.loss_scaler.unscale_(runner.optimizer) - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() - -else: - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (mmcv's implementation). - - The steps of fp16 optimizer is as follows. - 1. Scale the loss value. - 2. BP in the fp16 model. - 2. Copy gradients from fp16 model to fp32 weights. - 3. Update fp32 weights. - 4. Copy updated parameters from fp32 weights to fp16 model. - - Refer to https://arxiv.org/abs/1710.03740 for more details. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of LossScaler. - Defaults to 512. - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - if loss_scale == 'dynamic': - self.loss_scaler = LossScaler(mode='dynamic') - elif isinstance(loss_scale, float): - self.loss_scaler = LossScaler( - init_scale=loss_scale, mode='static') - elif isinstance(loss_scale, dict): - self.loss_scaler = LossScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training. - - 1. Make a master copy of fp32 weights for optimization. - 2. Convert the main model from fp32 to fp16. - """ - # keep a copy of fp32 weights - old_groups = runner.optimizer.param_groups - runner.optimizer.param_groups = copy.deepcopy( - runner.optimizer.param_groups) - state = defaultdict(dict) - p_map = { - old_p: p - for old_p, p in zip( - chain(*(g['params'] for g in old_groups)), - chain(*(g['params'] - for g in runner.optimizer.param_groups))) - } - for k, v in runner.optimizer.state.items(): - state[p_map[k]] = v - runner.optimizer.state = state - # convert model to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer `loss_scalar.py` - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients (fp16). - 3. Copy gradients from the model to the fp32 weight copy. - 4. Scale the gradients back and update the fp32 weight copy. - 5. Copy back the params from fp32 weight copy to the fp16 model. - 6. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - # scale the loss value - scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale - scaled_loss.backward() - # copy fp16 grads in the model to fp32 params in the optimizer - - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - self.loss_scaler.update_scale(has_overflow) - if has_overflow: - runner.logger.warning('Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using mmcv implementation) implements multi- - iters gradient cumulating.""" - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - - loss = runner.outputs['loss'] - loss = loss / loss_factor - - # scale the loss value - scaled_loss = loss * self.loss_scaler.loss_scale - scaled_loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - else: - runner.logger.warning( - 'Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - self.loss_scaler.update_scale(has_overflow) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/profiler.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/profiler.py deleted file mode 100755 index b70236997..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/profiler.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Callable, List, Optional, Union - -import torch - -from ..dist_utils import master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ProfilerHook(Hook): - """Profiler to analyze performance during training. - - PyTorch Profiler is a tool that allows the collection of the performance - metrics during the training. More details on Profiler can be found at - https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile - - Args: - by_epoch (bool): Profile performance by epoch or by iteration. - Default: True. - profile_iters (int): Number of iterations for profiling. - If ``by_epoch=True``, profile_iters indicates that they are the - first profile_iters epochs at the beginning of the - training, otherwise it indicates the first profile_iters - iterations. Default: 1. - activities (list[str]): List of activity groups (CPU, CUDA) to use in - profiling. Default: ['cpu', 'cuda']. - schedule (dict, optional): Config of generating the callable schedule. - if schedule is None, profiler will not add step markers into the - trace and table view. Default: None. - on_trace_ready (callable, dict): Either a handler or a dict of generate - handler. Default: None. - record_shapes (bool): Save information about operator's input shapes. - Default: False. - profile_memory (bool): Track tensor memory allocation/deallocation. - Default: False. - with_stack (bool): Record source information (file and line number) - for the ops. Default: False. - with_flops (bool): Use formula to estimate the FLOPS of specific - operators (matrix multiplication and 2D convolution). - Default: False. - json_trace_path (str, optional): Exports the collected trace in Chrome - JSON format. Default: None. - - Example: - >>> runner = ... # instantiate a Runner - >>> # tensorboard trace - >>> trace_config = dict(type='tb_trace', dir_name='work_dir') - >>> profiler_config = dict(on_trace_ready=trace_config) - >>> runner.register_profiler_hook(profiler_config) - >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) - """ - - def __init__(self, - by_epoch: bool = True, - profile_iters: int = 1, - activities: List[str] = ['cpu', 'cuda'], - schedule: Optional[dict] = None, - on_trace_ready: Optional[Union[Callable, dict]] = None, - record_shapes: bool = False, - profile_memory: bool = False, - with_stack: bool = False, - with_flops: bool = False, - json_trace_path: Optional[str] = None) -> None: - try: - from torch import profiler # torch version >= 1.8.1 - except ImportError: - raise ImportError('profiler is the new feature of torch1.8.1, ' - f'but your version is {torch.__version__}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' - self.by_epoch = by_epoch - - if profile_iters < 1: - raise ValueError('profile_iters should be greater than 0, but got ' - f'{profile_iters}') - self.profile_iters = profile_iters - - if not isinstance(activities, list): - raise ValueError( - f'activities should be list, but got {type(activities)}') - self.activities = [] - for activity in activities: - activity = activity.lower() - if activity == 'cpu': - self.activities.append(profiler.ProfilerActivity.CPU) - elif activity == 'cuda': - self.activities.append(profiler.ProfilerActivity.CUDA) - else: - raise ValueError( - f'activity should be "cpu" or "cuda", but got {activity}') - - if schedule is not None: - self.schedule = profiler.schedule(**schedule) - else: - self.schedule = None - - self.on_trace_ready = on_trace_ready - self.record_shapes = record_shapes - self.profile_memory = profile_memory - self.with_stack = with_stack - self.with_flops = with_flops - self.json_trace_path = json_trace_path - - @master_only - def before_run(self, runner): - if self.by_epoch and runner.max_epochs < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_epochs}') - - if not self.by_epoch and runner.max_iters < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_iters}') - - if callable(self.on_trace_ready): # handler - _on_trace_ready = self.on_trace_ready - elif isinstance(self.on_trace_ready, dict): # config of handler - trace_cfg = self.on_trace_ready.copy() - trace_type = trace_cfg.pop('type') # log_trace handler - if trace_type == 'log_trace': - - def _log_handler(prof): - print(prof.key_averages().table(**trace_cfg)) - - _on_trace_ready = _log_handler - elif trace_type == 'tb_trace': # tensorboard_trace handler - try: - import torch_tb_profiler # noqa: F401 - except ImportError: - raise ImportError('please run "pip install ' - 'torch-tb-profiler" to install ' - 'torch_tb_profiler') - _on_trace_ready = torch.profiler.tensorboard_trace_handler( - **trace_cfg) - else: - raise ValueError('trace_type should be "log_trace" or ' - f'"tb_trace", but got {trace_type}') - elif self.on_trace_ready is None: - _on_trace_ready = None # type: ignore - else: - raise ValueError('on_trace_ready should be handler, dict or None, ' - f'but got {type(self.on_trace_ready)}') - - if runner.max_epochs > 1: - warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' - 'instead of 1 epoch. Since profiler will slow down ' - 'the training, it is recommended to train 1 epoch ' - 'with ProfilerHook and adjust your setting according' - ' to the profiler summary. During normal training ' - '(epoch > 1), you may disable the ProfilerHook.') - - self.profiler = torch.profiler.profile( - activities=self.activities, - schedule=self.schedule, - on_trace_ready=_on_trace_ready, - record_shapes=self.record_shapes, - profile_memory=self.profile_memory, - with_stack=self.with_stack, - with_flops=self.with_flops) - - self.profiler.__enter__() - runner.logger.info('profiler is profiling...') - - @master_only - def after_train_epoch(self, runner): - if self.by_epoch and runner.epoch == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) - - @master_only - def after_train_iter(self, runner): - self.profiler.step() - if not self.by_epoch and runner.iter == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/sampler_seed.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/sampler_seed.py deleted file mode 100755 index ee0dc6bdd..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/sampler_seed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class DistSamplerSeedHook(Hook): - """Data-loading sampler for distributed training. - - When distributed training, it is only useful in conjunction with - :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same - purpose with :obj:`IterLoader`. - """ - - def before_epoch(self, runner): - if hasattr(runner.data_loader.sampler, 'set_epoch'): - # in case the data loader uses `SequentialSampler` in Pytorch - runner.data_loader.sampler.set_epoch(runner.epoch) - elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): - # batch sampler in pytorch warps the sampler as its attributes. - runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/sync_buffer.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/sync_buffer.py deleted file mode 100755 index 6376b7ff8..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/hooks/sync_buffer.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..dist_utils import allreduce_params -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class SyncBuffersHook(Hook): - """Synchronize model buffers such as running_mean and running_var in BN at - the end of each epoch. - - Args: - distributed (bool): Whether distributed training is used. It is - effective only for distributed training. Defaults to True. - """ - - def __init__(self, distributed=True): - self.distributed = distributed - - def after_epoch(self, runner): - """All-reduce model buffers at the end of each epoch.""" - if self.distributed: - allreduce_params(runner.model.buffers()) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/iter_based_runner.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/iter_based_runner.py deleted file mode 100755 index 9892b07a4..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/iter_based_runner.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch -from torch.optim import Optimizer - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .hooks import IterTimerHook -from .utils import get_host_info - - -class IterLoader: - - def __init__(self, dataloader): - self._dataloader = dataloader - self.iter_loader = iter(self._dataloader) - self._epoch = 0 - - @property - def epoch(self): - return self._epoch - - def __next__(self): - try: - data = next(self.iter_loader) - except StopIteration: - self._epoch += 1 - if hasattr(self._dataloader.sampler, 'set_epoch'): - self._dataloader.sampler.set_epoch(self._epoch) - time.sleep(2) # Prevent possible deadlock during epoch transition - self.iter_loader = iter(self._dataloader) - data = next(self.iter_loader) - - return data - - def __len__(self): - return len(self._dataloader) - - -@RUNNERS.register_module() -class IterBasedRunner(BaseRunner): - """Iteration-based Runner. - - This runner train models iteration by iteration. - """ - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._epoch = data_loader.epoch - data_batch = next(data_loader) - self.call_hook('before_train_iter') - outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.train_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_train_iter') - self._inner_iter += 1 - self._iter += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - data_batch = next(data_loader) - self.call_hook('before_val_iter') - outputs = self.model.val_step(data_batch, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.val_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_val_iter') - self._inner_iter += 1 - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, iters) to specify the - running order and iterations. E.g, [('train', 10000), - ('val', 1000)] means running 10000 iterations for training and - 1000 iterations for validation, iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - """Resume model from checkpoint. - - Args: - checkpoint (str): Checkpoint to resume from. - resume_optimizer (bool, optional): Whether resume the optimizer(s) - if the checkpoint file includes optimizer(s). Default to True. - map_location (str, optional): Same as :func:`torch.load`. - Default to 'default'. - """ - if map_location == 'default': - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - self._inner_iter = checkpoint['meta']['iter'] - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') - - def save_checkpoint(self, - out_dir, - filename_tmpl='iter_{}.pth', - meta=None, - save_optimizer=True, - create_symlink=True): - """Save checkpoint to file. - - Args: - out_dir (str): Directory to save checkpoint files. - filename_tmpl (str, optional): Checkpoint file template. - Defaults to 'iter_{}.pth'. - meta (dict, optional): Metadata to be saved in checkpoint. - Defaults to None. - save_optimizer (bool, optional): Whether save optimizer. - Defaults to True. - create_symlink (bool, optional): Whether create symlink to the - latest checkpoint file. Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.iter + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - custom_hooks_config=None): - """Register default hooks for iter-based training. - - Checkpoint hook, optimizer stepper hook and logger hooks will be set to - `by_epoch=False` by default. - - Default hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - if checkpoint_config is not None: - checkpoint_config.setdefault('by_epoch', False) - if lr_config is not None: - lr_config.setdefault('by_epoch', False) - if log_config is not None: - for info in log_config['hooks']: - info.setdefault('by_epoch', False) - super(IterBasedRunner, self).register_training_hooks( - lr_config=lr_config, - momentum_config=momentum_config, - optimizer_config=optimizer_config, - checkpoint_config=checkpoint_config, - log_config=log_config, - timer_config=IterTimerHook(), - custom_hooks_config=custom_hooks_config) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/log_buffer.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/log_buffer.py deleted file mode 100755 index d949e2941..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/log_buffer.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import numpy as np - - -class LogBuffer: - - def __init__(self): - self.val_history = OrderedDict() - self.n_history = OrderedDict() - self.output = OrderedDict() - self.ready = False - - def clear(self): - self.val_history.clear() - self.n_history.clear() - self.clear_output() - - def clear_output(self): - self.output.clear() - self.ready = False - - def update(self, vars, count=1): - assert isinstance(vars, dict) - for key, var in vars.items(): - if key not in self.val_history: - self.val_history[key] = [] - self.n_history[key] = [] - self.val_history[key].append(var) - self.n_history[key].append(count) - - def average(self, n=0): - """Average latest n values or all values.""" - assert n >= 0 - for key in self.val_history: - values = np.array(self.val_history[key][-n:]) - nums = np.array(self.n_history[key][-n:]) - avg = np.sum(values * nums) / np.sum(nums) - self.output[key] = avg - self.ready = True diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/optimizer/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/optimizer/__init__.py deleted file mode 100755 index 53c34d047..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/optimizer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, - build_optimizer_constructor) -from .default_constructor import DefaultOptimizerConstructor - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', - 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/optimizer/builder.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/optimizer/builder.py deleted file mode 100755 index f9234eed8..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/optimizer/builder.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect - -import torch - -from ...utils import Registry, build_from_cfg - -OPTIMIZERS = Registry('optimizer') -OPTIMIZER_BUILDERS = Registry('optimizer builder') - - -def register_torch_optimizers(): - torch_optimizers = [] - for module_name in dir(torch.optim): - if module_name.startswith('__'): - continue - _optim = getattr(torch.optim, module_name) - if inspect.isclass(_optim) and issubclass(_optim, - torch.optim.Optimizer): - OPTIMIZERS.register_module()(_optim) - torch_optimizers.append(module_name) - return torch_optimizers - - -TORCH_OPTIMIZERS = register_torch_optimizers() - - -def build_optimizer_constructor(cfg): - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/optimizer/default_constructor.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/optimizer/default_constructor.py deleted file mode 100755 index effd1e170..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/optimizer/default_constructor.py +++ /dev/null @@ -1,249 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -from torch.nn import GroupNorm, LayerNorm - -from mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of -from mmcv.utils.ext_loader import check_ops_exist -from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS - - -@OPTIMIZER_BUILDERS.register_module() -class DefaultOptimizerConstructor: - """Default constructor for optimizers. - - By default each parameter share the same optimizer settings, and we - provide an argument ``paramwise_cfg`` to specify parameter-wise settings. - It is a dict and may contain the following fields: - - - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If - one of the keys in ``custom_keys`` is a substring of the name of one - parameter, then the setting of the parameter will be specified by - ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will - be ignored. It should be noted that the aforementioned ``key`` is the - longest key that is a substring of the name of the parameter. If there - are multiple matched keys with the same length, then the key with lower - alphabet order will be chosen. - ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` - and ``decay_mult``. See Example 2 below. - - ``bias_lr_mult`` (float): It will be multiplied to the learning - rate for all bias parameters (except for those in normalization - layers and offset layers of DCN). - - ``bias_decay_mult`` (float): It will be multiplied to the weight - decay for all bias parameters (except for those in - normalization layers, depthwise conv layers, offset layers of DCN). - - ``norm_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of normalization - layers. - - ``dwconv_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of depthwise conv - layers. - - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning - rate for parameters of offset layer in the deformable convs - of a model. - - ``bypass_duplicate`` (bool): If true, the duplicate parameters - would not be added into optimizer. Default: False. - - Note: - 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will - override the effect of ``bias_lr_mult`` in the bias of offset - layer. So be careful when using both ``bias_lr_mult`` and - ``dcn_offset_lr_mult``. If you wish to apply both of them to the - offset layer in deformable convs, set ``dcn_offset_lr_mult`` - to the original ``dcn_offset_lr_mult`` * ``bias_lr_mult``. - 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will - apply it to all the DCN layers in the model. So be careful when - the model contains multiple DCN layers in places other than - backbone. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - optimizer_cfg (dict): The config dict of the optimizer. - Positional fields are - - - `type`: class name of the optimizer. - - Optional fields are - - - any arguments of the corresponding optimizer type, e.g., - lr, weight_decay, momentum, etc. - paramwise_cfg (dict, optional): Parameter-wise options. - - Example 1: - >>> model = torch.nn.modules.Conv1d(1, 1, 1) - >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, - >>> weight_decay=0.0001) - >>> paramwise_cfg = dict(norm_decay_mult=0.) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - - Example 2: - >>> # assume model have attribute model.backbone and model.cls_head - >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) - >>> paramwise_cfg = dict(custom_keys={ - '.backbone': dict(lr_mult=0.1, decay_mult=0.9)}) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - >>> # Then the `lr` and `weight_decay` for model.backbone is - >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for - >>> # model.cls_head is (0.01, 0.95). - """ - - def __init__(self, optimizer_cfg, paramwise_cfg=None): - if not isinstance(optimizer_cfg, dict): - raise TypeError('optimizer_cfg should be a dict', - f'but got {type(optimizer_cfg)}') - self.optimizer_cfg = optimizer_cfg - self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg - self.base_lr = optimizer_cfg.get('lr', None) - self.base_wd = optimizer_cfg.get('weight_decay', None) - self._validate_cfg() - - def _validate_cfg(self): - if not isinstance(self.paramwise_cfg, dict): - raise TypeError('paramwise_cfg should be None or a dict, ' - f'but got {type(self.paramwise_cfg)}') - - if 'custom_keys' in self.paramwise_cfg: - if not isinstance(self.paramwise_cfg['custom_keys'], dict): - raise TypeError( - 'If specified, custom_keys must be a dict, ' - f'but got {type(self.paramwise_cfg["custom_keys"])}') - if self.base_wd is None: - for key in self.paramwise_cfg['custom_keys']: - if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: - raise ValueError('base_wd should not be None') - - # get base lr and weight decay - # weight_decay must be explicitly specified if mult is specified - if ('bias_decay_mult' in self.paramwise_cfg - or 'norm_decay_mult' in self.paramwise_cfg - or 'dwconv_decay_mult' in self.paramwise_cfg): - if self.base_wd is None: - raise ValueError('base_wd should not be None') - - def _is_in(self, param_group, param_group_list): - assert is_list_of(param_group_list, dict) - param = set(param_group['params']) - param_set = set() - for group in param_group_list: - param_set.update(set(group['params'])) - - return not param.isdisjoint(param_set) - - def add_params(self, params, module, prefix='', is_dcn_module=None): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - prefix (str): The prefix of the module - is_dcn_module (int|float|None): If the current module is a - submodule of DCN, `is_dcn_module` will be passed to - control conv_offset layer's learning rate. Defaults to None. - """ - # get param-wise options - custom_keys = self.paramwise_cfg.get('custom_keys', {}) - # first sort with alphabet order and then sort with reversed len of str - sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) - - bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) - bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) - norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) - dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) - bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) - dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) - - # special rules for norm layers and depth-wise conv layers - is_norm = isinstance(module, - (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) - is_dwconv = ( - isinstance(module, torch.nn.Conv2d) - and module.in_channels == module.groups) - - for name, param in module.named_parameters(recurse=False): - param_group = {'params': [param]} - if not param.requires_grad: - params.append(param_group) - continue - if bypass_duplicate and self._is_in(param_group, params): - warnings.warn(f'{prefix} is duplicate. It is skipped since ' - f'bypass_duplicate={bypass_duplicate}') - continue - # if the parameter match one of the custom keys, ignore other rules - is_custom = False - for key in sorted_keys: - if key in f'{prefix}.{name}': - is_custom = True - lr_mult = custom_keys[key].get('lr_mult', 1.) - param_group['lr'] = self.base_lr * lr_mult - if self.base_wd is not None: - decay_mult = custom_keys[key].get('decay_mult', 1.) - param_group['weight_decay'] = self.base_wd * decay_mult - break - - if not is_custom: - # bias_lr_mult affects all bias parameters - # except for norm.bias dcn.conv_offset.bias - if name == 'bias' and not (is_norm or is_dcn_module): - param_group['lr'] = self.base_lr * bias_lr_mult - - if (prefix.find('conv_offset') != -1 and is_dcn_module - and isinstance(module, torch.nn.Conv2d)): - # deal with both dcn_offset's bias & weight - param_group['lr'] = self.base_lr * dcn_offset_lr_mult - - # apply weight decay policies - if self.base_wd is not None: - # norm decay - if is_norm: - param_group[ - 'weight_decay'] = self.base_wd * norm_decay_mult - # depth-wise conv - elif is_dwconv: - param_group[ - 'weight_decay'] = self.base_wd * dwconv_decay_mult - # bias lr and decay - elif name == 'bias' and not is_dcn_module: - # TODO: current bias_decay_mult will have affect on DCN - param_group[ - 'weight_decay'] = self.base_wd * bias_decay_mult - params.append(param_group) - - if check_ops_exist(): - from mmcv.ops import DeformConv2d, ModulatedDeformConv2d - is_dcn_module = isinstance(module, - (DeformConv2d, ModulatedDeformConv2d)) - else: - is_dcn_module = False - for child_name, child_mod in module.named_children(): - child_prefix = f'{prefix}.{child_name}' if prefix else child_name - self.add_params( - params, - child_mod, - prefix=child_prefix, - is_dcn_module=is_dcn_module) - - def __call__(self, model): - if hasattr(model, 'module'): - model = model.module - - optimizer_cfg = self.optimizer_cfg.copy() - # if no paramwise option is specified, just use the global setting - if not self.paramwise_cfg: - optimizer_cfg['params'] = model.parameters() - return build_from_cfg(optimizer_cfg, OPTIMIZERS) - - # set param-wise lr and weight decay recursively - params = [] - self.add_params(params, model) - optimizer_cfg['params'] = params - - return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/priority.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/priority.py deleted file mode 100755 index 64cc4e3a0..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/priority.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from enum import Enum - - -class Priority(Enum): - """Hook priority levels. - - +--------------+------------+ - | Level | Value | - +==============+============+ - | HIGHEST | 0 | - +--------------+------------+ - | VERY_HIGH | 10 | - +--------------+------------+ - | HIGH | 30 | - +--------------+------------+ - | ABOVE_NORMAL | 40 | - +--------------+------------+ - | NORMAL | 50 | - +--------------+------------+ - | BELOW_NORMAL | 60 | - +--------------+------------+ - | LOW | 70 | - +--------------+------------+ - | VERY_LOW | 90 | - +--------------+------------+ - | LOWEST | 100 | - +--------------+------------+ - """ - - HIGHEST = 0 - VERY_HIGH = 10 - HIGH = 30 - ABOVE_NORMAL = 40 - NORMAL = 50 - BELOW_NORMAL = 60 - LOW = 70 - VERY_LOW = 90 - LOWEST = 100 - - -def get_priority(priority): - """Get priority value. - - Args: - priority (int or str or :obj:`Priority`): Priority. - - Returns: - int: The priority value. - """ - if isinstance(priority, int): - if priority < 0 or priority > 100: - raise ValueError('priority must be between 0 and 100') - return priority - elif isinstance(priority, Priority): - return priority.value - elif isinstance(priority, str): - return Priority[priority.upper()].value - else: - raise TypeError('priority must be an integer or Priority enum value') diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/runner/utils.py b/cv/super_resolution/esrgan/pytorch/mmcv/runner/utils.py deleted file mode 100755 index 144d11e1a..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/runner/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random -import sys -import time -import warnings -from getpass import getuser -from socket import gethostname - -import numpy as np -import torch - -import mmcv - - -def get_host_info(): - """Get hostname and username. - - Return empty string if exception raised, e.g. ``getpass.getuser()`` will - lead to error in docker container - """ - host = '' - try: - host = f'{getuser()}@{gethostname()}' - except Exception as e: - warnings.warn(f'Host or user not found: {str(e)}') - finally: - return host - - -def get_time_str(): - return time.strftime('%Y%m%d_%H%M%S', time.localtime()) - - -def obj_from_dict(info, parent=None, default_args=None): - """Initialize an object from dict. - - The dict must contain the key "type", which indicates the object type, it - can be either a string or type, such as "list" or ``list``. Remaining - fields are treated as the arguments for constructing the object. - - Args: - info (dict): Object types and arguments. - parent (:class:`module`): Module which may containing expected object - classes. - default_args (dict, optional): Default arguments for initializing the - object. - - Returns: - any type: Object built from the dict. - """ - assert isinstance(info, dict) and 'type' in info - assert isinstance(default_args, dict) or default_args is None - args = info.copy() - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if parent is not None: - obj_type = getattr(parent, obj_type) - else: - obj_type = sys.modules[obj_type] - elif not isinstance(obj_type, type): - raise TypeError('type must be a str or valid type, but ' - f'got {type(obj_type)}') - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - return obj_type(**args) - - -def set_random_seed(seed, deterministic=False, use_rank_shift=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - rank_shift (bool): Whether to add rank number to the random seed to - have different random seed in different threads. Default: False. - """ - if use_rank_shift: - rank, _ = mmcv.runner.get_dist_info() - seed += rank - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/__init__.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/__init__.py deleted file mode 100755 index 378a00684..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/__init__.py +++ /dev/null @@ -1,69 +0,0 @@ -# flake8: noqa -# Copyright (c) OpenMMLab. All rights reserved. -from .config import Config, ConfigDict, DictAction -from .misc import (check_prerequisites, concat_list, deprecated_api_warning, - has_method, import_modules_from_strings, is_list_of, - is_method_overridden, is_seq_of, is_str, is_tuple_of, - iter_cast, list_cast, requires_executable, requires_package, - slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, - to_ntuple, tuple_cast) -from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, - scandir, symlink) -from .progressbar import (ProgressBar, track_iter_progress, - track_parallel_progress, track_progress) -from .testing import (assert_attrs_equal, assert_dict_contains_subset, - assert_dict_has_keys, assert_is_norm_layer, - assert_keys_equal, assert_params_all_zeros, - check_python_script) -from .timer import Timer, TimerError, check_time -from .version_utils import digit_version, get_git_hash - -try: - import torch -except ImportError: - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', - 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', - 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', - 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', - 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', - 'track_progress', 'track_iter_progress', 'track_parallel_progress', - 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', - 'digit_version', 'get_git_hash', 'import_modules_from_strings', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', - 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', - 'is_method_overridden', 'has_method' - ] -else: - from .env import collect_env - from .logging import get_logger, print_log - from .parrots_jit import jit, skip_no_elena - from .parrots_wrapper import ( - TORCH_VERSION, BuildExtension, CppExtension, CUDAExtension, DataLoader, - PoolDataLoader, SyncBatchNorm, _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, - _AvgPoolNd, _BatchNorm, _ConvNd, _ConvTransposeMixin, _InstanceNorm, - _MaxPoolNd, get_build_config, is_rocm_pytorch, _get_cuda_home) - from .registry import Registry, build_from_cfg - from .trace import is_jit_tracing - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', - 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', - 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', - 'check_prerequisites', 'requires_package', 'requires_executable', - 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', - 'symlink', 'scandir', 'ProgressBar', 'track_progress', - 'track_iter_progress', 'track_parallel_progress', 'Registry', - 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', - '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', - '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', - 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', - 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', - 'deprecated_api_warning', 'digit_version', 'get_git_hash', - 'import_modules_from_strings', 'jit', 'skip_no_elena', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', - 'assert_params_all_zeros', 'check_python_script', - 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', - '_get_cuda_home', 'has_method' - ] diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/config.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/config.py deleted file mode 100755 index c71377c07..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/config.py +++ /dev/null @@ -1,688 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import ast -import copy -import os -import os.path as osp -import platform -import shutil -import sys -import tempfile -import uuid -import warnings -from argparse import Action, ArgumentParser -from collections import abc -from importlib import import_module - -from addict import Dict -from yapf.yapflib.yapf_api import FormatCode - -from .misc import import_modules_from_strings -from .path import check_file_exist - -if platform.system() == 'Windows': - import regex as re -else: - import re - -BASE_KEY = '_base_' -DELETE_KEY = '_delete_' -DEPRECATION_KEY = '_deprecation_' -RESERVED_KEYS = ['filename', 'text', 'pretty_text'] - - -class ConfigDict(Dict): - - def __missing__(self, name): - raise KeyError(name) - - def __getattr__(self, name): - try: - value = super(ConfigDict, self).__getattr__(name) - except KeyError: - ex = AttributeError(f"'{self.__class__.__name__}' object has no " - f"attribute '{name}'") - except Exception as e: - ex = e - else: - return value - raise ex - - -def add_args(parser, cfg, prefix=''): - for k, v in cfg.items(): - if isinstance(v, str): - parser.add_argument('--' + prefix + k) - elif isinstance(v, int): - parser.add_argument('--' + prefix + k, type=int) - elif isinstance(v, float): - parser.add_argument('--' + prefix + k, type=float) - elif isinstance(v, bool): - parser.add_argument('--' + prefix + k, action='store_true') - elif isinstance(v, dict): - add_args(parser, v, prefix + k + '.') - elif isinstance(v, abc.Iterable): - parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') - else: - print(f'cannot parse key {prefix + k} of type {type(v)}') - return parser - - -class Config: - """A facility for config and config files. - - It supports common file formats as configs: python/json/yaml. The interface - is the same as a dict object and also allows access config values as - attributes. - - Example: - >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - >>> cfg.a - 1 - >>> cfg.b - {'b1': [0, 1]} - >>> cfg.b.b1 - [0, 1] - >>> cfg = Config.fromfile('tests/data/config/a.py') - >>> cfg.filename - "/home/kchen/projects/mmcv/tests/data/config/a.py" - >>> cfg.item4 - 'test' - >>> cfg - "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " - "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" - """ - - @staticmethod - def _validate_py_syntax(filename): - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - content = f.read() - try: - ast.parse(content) - except SyntaxError as e: - raise SyntaxError('There are syntax errors in config ' - f'file {filename}: {e}') - - @staticmethod - def _substitute_predefined_vars(filename, temp_config_name): - file_dirname = osp.dirname(filename) - file_basename = osp.basename(filename) - file_basename_no_extension = osp.splitext(file_basename)[0] - file_extname = osp.splitext(filename)[1] - support_templates = dict( - fileDirname=file_dirname, - fileBasename=file_basename, - fileBasenameNoExtension=file_basename_no_extension, - fileExtname=file_extname) - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - for key, value in support_templates.items(): - regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' - value = value.replace('\\', '/') - config_file = re.sub(regexp, value, config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - - @staticmethod - def _pre_substitute_base_vars(filename, temp_config_name): - """Substitute base variable placehoders to string, so that parsing - would work.""" - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - base_var_dict = {} - regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' - base_vars = set(re.findall(regexp, config_file)) - for base_var in base_vars: - randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' - base_var_dict[randstr] = base_var - regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' - config_file = re.sub(regexp, f'"{randstr}"', config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - return base_var_dict - - @staticmethod - def _substitute_base_vars(cfg, base_var_dict, base_cfg): - """Substitute variable strings to their actual values.""" - cfg = copy.deepcopy(cfg) - - if isinstance(cfg, dict): - for k, v in cfg.items(): - if isinstance(v, str) and v in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[v].split('.'): - new_v = new_v[new_k] - cfg[k] = new_v - elif isinstance(v, (list, tuple, dict)): - cfg[k] = Config._substitute_base_vars( - v, base_var_dict, base_cfg) - elif isinstance(cfg, tuple): - cfg = tuple( - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg) - elif isinstance(cfg, list): - cfg = [ - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg - ] - elif isinstance(cfg, str) and cfg in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[cfg].split('.'): - new_v = new_v[new_k] - cfg = new_v - - return cfg - - @staticmethod - def _file2dict(filename, use_predefined_variables=True): - filename = osp.abspath(osp.expanduser(filename)) - check_file_exist(filename) - fileExtname = osp.splitext(filename)[1] - if fileExtname not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - - with tempfile.TemporaryDirectory() as temp_config_dir: - temp_config_file = tempfile.NamedTemporaryFile( - dir=temp_config_dir, suffix=fileExtname) - if platform.system() == 'Windows': - temp_config_file.close() - temp_config_name = osp.basename(temp_config_file.name) - # Substitute predefined variables - if use_predefined_variables: - Config._substitute_predefined_vars(filename, - temp_config_file.name) - else: - shutil.copyfile(filename, temp_config_file.name) - # Substitute base variables from placeholders to strings - base_var_dict = Config._pre_substitute_base_vars( - temp_config_file.name, temp_config_file.name) - - if filename.endswith('.py'): - temp_module_name = osp.splitext(temp_config_name)[0] - sys.path.insert(0, temp_config_dir) - Config._validate_py_syntax(filename) - mod = import_module(temp_module_name) - sys.path.pop(0) - cfg_dict = { - name: value - for name, value in mod.__dict__.items() - if not name.startswith('__') - } - # delete imported module - del sys.modules[temp_module_name] - elif filename.endswith(('.yml', '.yaml', '.json')): - import mmcv - cfg_dict = mmcv.load(temp_config_file.name) - # close temp file - temp_config_file.close() - - # check deprecation information - if DEPRECATION_KEY in cfg_dict: - deprecation_info = cfg_dict.pop(DEPRECATION_KEY) - warning_msg = f'The config file {filename} will be deprecated ' \ - 'in the future.' - if 'expected' in deprecation_info: - warning_msg += f' Please use {deprecation_info["expected"]} ' \ - 'instead.' - if 'reference' in deprecation_info: - warning_msg += ' More information can be found at ' \ - f'{deprecation_info["reference"]}' - warnings.warn(warning_msg) - - cfg_text = filename + '\n' - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - cfg_text += f.read() - - if BASE_KEY in cfg_dict: - cfg_dir = osp.dirname(filename) - base_filename = cfg_dict.pop(BASE_KEY) - base_filename = base_filename if isinstance( - base_filename, list) else [base_filename] - - cfg_dict_list = list() - cfg_text_list = list() - for f in base_filename: - _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) - cfg_dict_list.append(_cfg_dict) - cfg_text_list.append(_cfg_text) - - base_cfg_dict = dict() - for c in cfg_dict_list: - duplicate_keys = base_cfg_dict.keys() & c.keys() - if len(duplicate_keys) > 0: - raise KeyError('Duplicate key is not allowed among bases. ' - f'Duplicate keys: {duplicate_keys}') - base_cfg_dict.update(c) - - # Substitute base variables from strings to their actual values - cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, - base_cfg_dict) - - base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) - cfg_dict = base_cfg_dict - - # merge cfg_text - cfg_text_list.append(cfg_text) - cfg_text = '\n'.join(cfg_text_list) - - return cfg_dict, cfg_text - - @staticmethod - def _merge_a_into_b(a, b, allow_list_keys=False): - """merge dict ``a`` into dict ``b`` (non-inplace). - - Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid - in-place modifications. - - Args: - a (dict): The source dict to be merged into ``b``. - b (dict): The origin dict to be fetch keys from ``a``. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in source ``a`` and will replace the element of the - corresponding index in b if b is a list. Default: False. - - Returns: - dict: The modified dict of ``b`` using ``a``. - - Examples: - # Normally merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # Delete b first and merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # b is a list - >>> Config._merge_a_into_b( - ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) - [{'a': 2}, {'b': 2}] - """ - b = b.copy() - for k, v in a.items(): - if allow_list_keys and k.isdigit() and isinstance(b, list): - k = int(k) - if len(b) <= k: - raise KeyError(f'Index {k} exceeds the length of list {b}') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - elif isinstance(v, - dict) and k in b and not v.pop(DELETE_KEY, False): - allowed_types = (dict, list) if allow_list_keys else dict - if not isinstance(b[k], allowed_types): - raise TypeError( - f'{k}={v} in child config cannot inherit from base ' - f'because {k} is a dict in the child config but is of ' - f'type {type(b[k])} in base config. You may set ' - f'`{DELETE_KEY}=True` to ignore the base config') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - else: - b[k] = v - return b - - @staticmethod - def fromfile(filename, - use_predefined_variables=True, - import_custom_modules=True): - cfg_dict, cfg_text = Config._file2dict(filename, - use_predefined_variables) - if import_custom_modules and cfg_dict.get('custom_imports', None): - import_modules_from_strings(**cfg_dict['custom_imports']) - return Config(cfg_dict, cfg_text=cfg_text, filename=filename) - - @staticmethod - def fromstring(cfg_str, file_format): - """Generate config from config str. - - Args: - cfg_str (str): Config str. - file_format (str): Config file format corresponding to the - config str. Only py/yml/yaml/json type are supported now! - - Returns: - obj:`Config`: Config obj. - """ - if file_format not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - if file_format != '.py' and 'dict(' in cfg_str: - # check if users specify a wrong suffix for python - warnings.warn( - 'Please check "file_format", the file format may be .py') - with tempfile.NamedTemporaryFile( - 'w', encoding='utf-8', suffix=file_format, - delete=False) as temp_file: - temp_file.write(cfg_str) - # on windows, previous implementation cause error - # see PR 1077 for details - cfg = Config.fromfile(temp_file.name) - os.remove(temp_file.name) - return cfg - - @staticmethod - def auto_argparser(description=None): - """Generate argparser from config file automatically (experimental)""" - partial_parser = ArgumentParser(description=description) - partial_parser.add_argument('config', help='config file path') - cfg_file = partial_parser.parse_known_args()[0].config - cfg = Config.fromfile(cfg_file) - parser = ArgumentParser(description=description) - parser.add_argument('config', help='config file path') - add_args(parser, cfg) - return parser, cfg - - def __init__(self, cfg_dict=None, cfg_text=None, filename=None): - if cfg_dict is None: - cfg_dict = dict() - elif not isinstance(cfg_dict, dict): - raise TypeError('cfg_dict must be a dict, but ' - f'got {type(cfg_dict)}') - for key in cfg_dict: - if key in RESERVED_KEYS: - raise KeyError(f'{key} is reserved for config file') - - super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) - super(Config, self).__setattr__('_filename', filename) - if cfg_text: - text = cfg_text - elif filename: - with open(filename, 'r') as f: - text = f.read() - else: - text = '' - super(Config, self).__setattr__('_text', text) - - @property - def filename(self): - return self._filename - - @property - def text(self): - return self._text - - @property - def pretty_text(self): - - indent = 4 - - def _indent(s_, num_spaces): - s = s_.split('\n') - if len(s) == 1: - return s_ - first = s.pop(0) - s = [(num_spaces * ' ') + line for line in s] - s = '\n'.join(s) - s = first + '\n' + s - return s - - def _format_basic_types(k, v, use_mapping=False): - if isinstance(v, str): - v_str = f"'{v}'" - else: - v_str = str(v) - - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) - - return attr_str - - def _format_list(k, v, use_mapping=False): - # check if all items in the list are dict - if all(isinstance(_, dict) for _ in v): - v_str = '[\n' - v_str += '\n'.join( - f'dict({_indent(_format_dict(v_), indent)}),' - for v_ in v).rstrip(',') - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) + ']' - else: - attr_str = _format_basic_types(k, v, use_mapping) - return attr_str - - def _contain_invalid_identifier(dict_str): - contain_invalid_identifier = False - for key_name in dict_str: - contain_invalid_identifier |= \ - (not str(key_name).isidentifier()) - return contain_invalid_identifier - - def _format_dict(input_dict, outest_level=False): - r = '' - s = [] - - use_mapping = _contain_invalid_identifier(input_dict) - if use_mapping: - r += '{' - for idx, (k, v) in enumerate(input_dict.items()): - is_last = idx >= len(input_dict) - 1 - end = '' if outest_level or is_last else ',' - if isinstance(v, dict): - v_str = '\n' + _format_dict(v) - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: dict({v_str}' - else: - attr_str = f'{str(k)}=dict({v_str}' - attr_str = _indent(attr_str, indent) + ')' + end - elif isinstance(v, list): - attr_str = _format_list(k, v, use_mapping) + end - else: - attr_str = _format_basic_types(k, v, use_mapping) + end - - s.append(attr_str) - r += '\n'.join(s) - if use_mapping: - r += '}' - return r - - cfg_dict = self._cfg_dict.to_dict() - text = _format_dict(cfg_dict, outest_level=True) - # copied from setup.cfg - yapf_style = dict( - based_on_style='pep8', - blank_line_before_nested_class_or_def=True, - split_before_expression_after_opening_paren=True) - text, _ = FormatCode(text, style_config=yapf_style, verify=True) - - return text - - def __repr__(self): - return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' - - def __len__(self): - return len(self._cfg_dict) - - def __getattr__(self, name): - return getattr(self._cfg_dict, name) - - def __getitem__(self, name): - return self._cfg_dict.__getitem__(name) - - def __setattr__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setattr__(name, value) - - def __setitem__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setitem__(name, value) - - def __iter__(self): - return iter(self._cfg_dict) - - def __getstate__(self): - return (self._cfg_dict, self._filename, self._text) - - def __setstate__(self, state): - _cfg_dict, _filename, _text = state - super(Config, self).__setattr__('_cfg_dict', _cfg_dict) - super(Config, self).__setattr__('_filename', _filename) - super(Config, self).__setattr__('_text', _text) - - def dump(self, file=None): - cfg_dict = super(Config, self).__getattribute__('_cfg_dict').to_dict() - if self.filename.endswith('.py'): - if file is None: - return self.pretty_text - else: - with open(file, 'w', encoding='utf-8') as f: - f.write(self.pretty_text) - else: - import mmcv - if file is None: - file_format = self.filename.split('.')[-1] - return mmcv.dump(cfg_dict, file_format=file_format) - else: - mmcv.dump(cfg_dict, file) - - def merge_from_dict(self, options, allow_list_keys=True): - """Merge list into cfg_dict. - - Merge the dict parsed by MultipleKVAction into this cfg. - - Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} - >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) - >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - - # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) - >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) - >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - - Args: - options (dict): dict of configs to merge from. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in ``options`` and will replace the element of the - corresponding index in the config if the config is a list. - Default: True. - """ - option_cfg_dict = {} - for full_key, v in options.items(): - d = option_cfg_dict - key_list = full_key.split('.') - for subkey in key_list[:-1]: - d.setdefault(subkey, ConfigDict()) - d = d[subkey] - subkey = key_list[-1] - d[subkey] = v - - cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - super(Config, self).__setattr__( - '_cfg_dict', - Config._merge_a_into_b( - option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) - - -class DictAction(Action): - """ - argparse action to split an argument into KEY=VALUE form - on the first = and append to a dictionary. List options can - be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit - brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build - list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' - """ - - @staticmethod - def _parse_int_float_bool(val): - try: - return int(val) - except ValueError: - pass - try: - return float(val) - except ValueError: - pass - if val.lower() in ['true', 'false']: - return True if val.lower() == 'true' else False - return val - - @staticmethod - def _parse_iterable(val): - """Parse iterable values in the string. - - All elements inside '()' or '[]' are treated as iterable values. - - Args: - val (str): Value string. - - Returns: - list | tuple: The expanded list or tuple from the string. - - Examples: - >>> DictAction._parse_iterable('1,2,3') - [1, 2, 3] - >>> DictAction._parse_iterable('[a, b, c]') - ['a', 'b', 'c'] - >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') - [(1, 2, 3), ['a', 'b'], 'c'] - """ - - def find_next_comma(string): - """Find the position of next comma in the string. - - If no ',' is found in the string, return the string length. All - chars inside '()' and '[]' are treated as one element and thus ',' - inside these brackets are ignored. - """ - assert (string.count('(') == string.count(')')) and ( - string.count('[') == string.count(']')), \ - f'Imbalanced brackets exist in {string}' - end = len(string) - for idx, char in enumerate(string): - pre = string[:idx] - # The string before this ',' is balanced - if ((char == ',') and (pre.count('(') == pre.count(')')) - and (pre.count('[') == pre.count(']'))): - end = idx - break - return end - - # Strip ' and " characters and replace whitespace. - val = val.strip('\'\"').replace(' ', '') - is_tuple = False - if val.startswith('(') and val.endswith(')'): - is_tuple = True - val = val[1:-1] - elif val.startswith('[') and val.endswith(']'): - val = val[1:-1] - elif ',' not in val: - # val is a single value - return DictAction._parse_int_float_bool(val) - - values = [] - while len(val) > 0: - comma_idx = find_next_comma(val) - element = DictAction._parse_iterable(val[:comma_idx]) - values.append(element) - val = val[comma_idx + 1:] - if is_tuple: - values = tuple(values) - return values - - def __call__(self, parser, namespace, values, option_string=None): - options = {} - for kv in values: - key, val = kv.split('=', maxsplit=1) - options[key] = self._parse_iterable(val) - setattr(namespace, self.dest, options) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/env.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/env.py deleted file mode 100755 index e46a1094f..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/env.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This file holding some environment constant for sharing by other files.""" - -import os.path as osp -import subprocess -import sys -from collections import defaultdict - -import cv2 -import torch - -import mmcv -from .parrots_wrapper import get_build_config - - -def collect_env(): - """Collect the information of the running environments. - - Returns: - dict: The environment information. The following fields are contained. - - - sys.platform: The variable of ``sys.platform``. - - Python: Python version. - - CUDA available: Bool, indicating if CUDA is available. - - GPU devices: Device type of each GPU. - - CUDA_HOME (optional): The env var ``CUDA_HOME``. - - NVCC (optional): NVCC version. - - GCC: GCC version, "n/a" if GCC is not installed. - - PyTorch: PyTorch version. - - PyTorch compiling details: The output of \ - ``torch.__config__.show()``. - - TorchVision (optional): TorchVision version. - - OpenCV: OpenCV version. - - MMCV: MMCV version. - - MMCV Compiler: The GCC version for compiling MMCV ops. - - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. - """ - env_info = {} - env_info['sys.platform'] = sys.platform - env_info['Python'] = sys.version.replace('\n', '') - - cuda_available = torch.cuda.is_available() - env_info['CUDA available'] = cuda_available - - if cuda_available: - devices = defaultdict(list) - for k in range(torch.cuda.device_count()): - devices[torch.cuda.get_device_name(k)].append(str(k)) - for name, device_ids in devices.items(): - env_info['GPU ' + ','.join(device_ids)] = name - - from mmcv.utils.parrots_wrapper import _get_cuda_home - CUDA_HOME = _get_cuda_home() - env_info['CUDA_HOME'] = CUDA_HOME - - if CUDA_HOME is not None and osp.isdir(CUDA_HOME): - try: - nvcc = osp.join(CUDA_HOME, 'bin/nvcc') - nvcc = subprocess.check_output( - f'"{nvcc}" -V | tail -n1', shell=True) - nvcc = nvcc.decode('utf-8').strip() - except subprocess.SubprocessError: - nvcc = 'Not Available' - env_info['NVCC'] = nvcc - - try: - gcc = subprocess.check_output('gcc --version | head -n1', shell=True) - gcc = gcc.decode('utf-8').strip() - env_info['GCC'] = gcc - except subprocess.CalledProcessError: # gcc is unavailable - env_info['GCC'] = 'n/a' - - env_info['PyTorch'] = torch.__version__ - env_info['PyTorch compiling details'] = get_build_config() - - try: - import torchvision - env_info['TorchVision'] = torchvision.__version__ - except ModuleNotFoundError: - pass - - env_info['OpenCV'] = cv2.__version__ - - env_info['MMCV'] = mmcv.__version__ - - try: - from mmcv.ops import get_compiler_version, get_compiling_cuda_version - except ModuleNotFoundError: - env_info['MMCV Compiler'] = 'n/a' - env_info['MMCV CUDA Compiler'] = 'n/a' - else: - env_info['MMCV Compiler'] = get_compiler_version() - env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() - - return env_info diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/ext_loader.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/ext_loader.py deleted file mode 100755 index 08132d2c1..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/ext_loader.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os -import pkgutil -import warnings -from collections import namedtuple - -import torch - -if torch.__version__ != 'parrots': - - def load_ext(name, funcs): - ext = importlib.import_module('mmcv.' + name) - for fun in funcs: - assert hasattr(ext, fun), f'{fun} miss in module {name}' - return ext -else: - from parrots import extension - from parrots.base import ParrotsException - - has_return_value_ops = [ - 'nms', - 'softnms', - 'nms_match', - 'nms_rotated', - 'top_pool_forward', - 'top_pool_backward', - 'bottom_pool_forward', - 'bottom_pool_backward', - 'left_pool_forward', - 'left_pool_backward', - 'right_pool_forward', - 'right_pool_backward', - 'fused_bias_leakyrelu', - 'upfirdn2d', - 'ms_deform_attn_forward', - 'pixel_group', - 'contour_expand', - ] - - def get_fake_func(name, e): - - def fake_func(*args, **kwargs): - warnings.warn(f'{name} is not supported in parrots now') - raise e - - return fake_func - - def load_ext(name, funcs): - ExtModule = namedtuple('ExtModule', funcs) - ext_list = [] - lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - for fun in funcs: - try: - ext_fun = extension.load(fun, name, lib_dir=lib_root) - except ParrotsException as e: - if 'No element registered' not in e.message: - warnings.warn(e.message) - ext_fun = get_fake_func(fun, e) - ext_list.append(ext_fun) - else: - if fun in has_return_value_ops: - ext_list.append(ext_fun.op) - else: - ext_list.append(ext_fun.op_) - return ExtModule(*ext_list) - - -def check_ops_exist(): - ext_loader = pkgutil.find_loader('mmcv._ext') - return ext_loader is not None diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/logging.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/logging.py deleted file mode 100755 index 4aa0e04bb..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/logging.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.distributed as dist - -logger_initialized = {} - - -def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): - """Initialize and get a logger by name. - - If the logger has not been initialized, this method will initialize the - logger by adding one or two handlers, otherwise the initialized logger will - be directly returned. During initialization, a StreamHandler will always be - added. If `log_file` is specified and the process rank is 0, a FileHandler - will also be added. - - Args: - name (str): Logger name. - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the logger. - log_level (int): The logger level. Note that only the process of - rank 0 is affected, and other processes will set the level to - "Error" thus be silent most of the time. - file_mode (str): The file mode used in opening log file. - Defaults to 'w'. - - Returns: - logging.Logger: The expected logger. - """ - logger = logging.getLogger(name) - if name in logger_initialized: - return logger - # handle hierarchical names - # e.g., logger "a" is initialized, then logger "a.b" will skip the - # initialization since it is a child of "a". - for logger_name in logger_initialized: - if name.startswith(logger_name): - return logger - - # handle duplicate logs to the console - # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) - # to the root logger. As logger.propagate is True by default, this root - # level handler causes logging messages from rank>0 processes to - # unexpectedly show up on the console, creating much unwanted clutter. - # To fix this issue, we set the root logger's StreamHandler, if any, to log - # at the ERROR level. - for handler in logger.root.handlers: - if type(handler) is logging.StreamHandler: - handler.setLevel(logging.ERROR) - - stream_handler = logging.StreamHandler() - handlers = [stream_handler] - - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - else: - rank = 0 - - # only rank 0 will add a FileHandler - if rank == 0 and log_file is not None: - # Here, the default behaviour of the official logger is 'a'. Thus, we - # provide an interface to change the file mode to the default - # behaviour. - file_handler = logging.FileHandler(log_file, file_mode) - handlers.append(file_handler) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - for handler in handlers: - handler.setFormatter(formatter) - handler.setLevel(log_level) - logger.addHandler(handler) - - if rank == 0: - logger.setLevel(log_level) - else: - logger.setLevel(logging.ERROR) - - logger_initialized[name] = True - - return logger - - -def print_log(msg, logger=None, level=logging.INFO): - """Print a log message. - - Args: - msg (str): The message to be logged. - logger (logging.Logger | str | None): The logger to be used. - Some special loggers are: - - "silent": no message will be printed. - - other str: the logger obtained with `get_root_logger(logger)`. - - None: The `print()` method will be used to print log messages. - level (int): Logging level. Only available when `logger` is a Logger - object or "root". - """ - if logger is None: - print(msg) - elif isinstance(logger, logging.Logger): - logger.log(level, msg) - elif logger == 'silent': - pass - elif isinstance(logger, str): - _logger = get_logger(logger) - _logger.log(level, msg) - else: - raise TypeError( - 'logger should be either a logging.Logger object, str, ' - f'"silent" or None, but got {type(logger)}') diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/misc.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/misc.py deleted file mode 100755 index 2c58d0d7f..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/misc.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections.abc -import functools -import itertools -import subprocess -import warnings -from collections import abc -from importlib import import_module -from inspect import getfullargspec -from itertools import repeat - - -# From PyTorch internals -def _ntuple(n): - - def parse(x): - if isinstance(x, collections.abc.Iterable): - return x - return tuple(repeat(x, n)) - - return parse - - -to_1tuple = _ntuple(1) -to_2tuple = _ntuple(2) -to_3tuple = _ntuple(3) -to_4tuple = _ntuple(4) -to_ntuple = _ntuple - - -def is_str(x): - """Whether the input is an string instance. - - Note: This method is deprecated since python 2 is no longer supported. - """ - return isinstance(x, str) - - -def import_modules_from_strings(imports, allow_failed_imports=False): - """Import modules from the given list of strings. - - Args: - imports (list | str | None): The given module names to be imported. - allow_failed_imports (bool): If True, the failed imports will return - None. Otherwise, an ImportError is raise. Default: False. - - Returns: - list[module] | module | None: The imported modules. - - Examples: - >>> osp, sys = import_modules_from_strings( - ... ['os.path', 'sys']) - >>> import os.path as osp_ - >>> import sys as sys_ - >>> assert osp == osp_ - >>> assert sys == sys_ - """ - if not imports: - return - single_import = False - if isinstance(imports, str): - single_import = True - imports = [imports] - if not isinstance(imports, list): - raise TypeError( - f'custom_imports must be a list but got type {type(imports)}') - imported = [] - for imp in imports: - if not isinstance(imp, str): - raise TypeError( - f'{imp} is of type {type(imp)} and cannot be imported.') - try: - imported_tmp = import_module(imp) - except ImportError: - if allow_failed_imports: - warnings.warn(f'{imp} failed to import and is ignored.', - UserWarning) - imported_tmp = None - else: - raise ImportError - imported.append(imported_tmp) - if single_import: - imported = imported[0] - return imported - - -def iter_cast(inputs, dst_type, return_type=None): - """Cast elements of an iterable object into some type. - - Args: - inputs (Iterable): The input object. - dst_type (type): Destination type. - return_type (type, optional): If specified, the output object will be - converted to this type, otherwise an iterator. - - Returns: - iterator or specified type: The converted object. - """ - if not isinstance(inputs, abc.Iterable): - raise TypeError('inputs must be an iterable object') - if not isinstance(dst_type, type): - raise TypeError('"dst_type" must be a valid type') - - out_iterable = map(dst_type, inputs) - - if return_type is None: - return out_iterable - else: - return return_type(out_iterable) - - -def list_cast(inputs, dst_type): - """Cast elements of an iterable object into a list of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=list) - - -def tuple_cast(inputs, dst_type): - """Cast elements of an iterable object into a tuple of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=tuple) - - -def is_seq_of(seq, expected_type, seq_type=None): - """Check whether it is a sequence of some type. - - Args: - seq (Sequence): The sequence to be checked. - expected_type (type): Expected type of sequence items. - seq_type (type, optional): Expected sequence type. - - Returns: - bool: Whether the sequence is valid. - """ - if seq_type is None: - exp_seq_type = abc.Sequence - else: - assert isinstance(seq_type, type) - exp_seq_type = seq_type - if not isinstance(seq, exp_seq_type): - return False - for item in seq: - if not isinstance(item, expected_type): - return False - return True - - -def is_list_of(seq, expected_type): - """Check whether it is a list of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=list) - - -def is_tuple_of(seq, expected_type): - """Check whether it is a tuple of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=tuple) - - -def slice_list(in_list, lens): - """Slice a list into several sub lists by a list of given length. - - Args: - in_list (list): The list to be sliced. - lens(int or list): The expected length of each out list. - - Returns: - list: A list of sliced list. - """ - if isinstance(lens, int): - assert len(in_list) % lens == 0 - lens = [lens] * int(len(in_list) / lens) - if not isinstance(lens, list): - raise TypeError('"indices" must be an integer or a list of integers') - elif sum(lens) != len(in_list): - raise ValueError('sum of lens and list length does not ' - f'match: {sum(lens)} != {len(in_list)}') - out_list = [] - idx = 0 - for i in range(len(lens)): - out_list.append(in_list[idx:idx + lens[i]]) - idx += lens[i] - return out_list - - -def concat_list(in_list): - """Concatenate a list of list into a single list. - - Args: - in_list (list): The list of list to be merged. - - Returns: - list: The concatenated flat list. - """ - return list(itertools.chain(*in_list)) - - -def check_prerequisites( - prerequisites, - checker, - msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' - 'found, please install them first.'): # yapf: disable - """A decorator factory to check if prerequisites are satisfied. - - Args: - prerequisites (str of list[str]): Prerequisites to be checked. - checker (callable): The checker method that returns True if a - prerequisite is meet, False otherwise. - msg_tmpl (str): The message template with two variables. - - Returns: - decorator: A specific decorator. - """ - - def wrap(func): - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - requirements = [prerequisites] if isinstance( - prerequisites, str) else prerequisites - missing = [] - for item in requirements: - if not checker(item): - missing.append(item) - if missing: - print(msg_tmpl.format(', '.join(missing), func.__name__)) - raise RuntimeError('Prerequisites not meet.') - else: - return func(*args, **kwargs) - - return wrapped_func - - return wrap - - -def _check_py_package(package): - try: - import_module(package) - except ImportError: - return False - else: - return True - - -def _check_executable(cmd): - if subprocess.call(f'which {cmd}', shell=True) != 0: - return False - else: - return True - - -def requires_package(prerequisites): - """A decorator to check if some python packages are installed. - - Example: - >>> @requires_package('numpy') - >>> func(arg1, args): - >>> return numpy.zeros(1) - array([0.]) - >>> @requires_package(['numpy', 'non_package']) - >>> func(arg1, args): - >>> return numpy.zeros(1) - ImportError - """ - return check_prerequisites(prerequisites, checker=_check_py_package) - - -def requires_executable(prerequisites): - """A decorator to check if some executable files are installed. - - Example: - >>> @requires_executable('ffmpeg') - >>> func(arg1, args): - >>> print(1) - 1 - """ - return check_prerequisites(prerequisites, checker=_check_executable) - - -def deprecated_api_warning(name_dict, cls_name=None): - """A decorator to check if some arguments are deprecate and try to replace - deprecate src_arg_name to dst_arg_name. - - Args: - name_dict(dict): - key (str): Deprecate argument names. - val (str): Expected argument names. - - Returns: - func: New function. - """ - - def api_warning_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get name of the function - func_name = old_func.__name__ - if cls_name is not None: - func_name = f'{cls_name}.{func_name}' - if args: - arg_names = args_info.args[:len(args)] - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in arg_names: - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead') - arg_names[arg_names.index(src_arg_name)] = dst_arg_name - if kwargs: - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in kwargs: - - assert dst_arg_name not in kwargs, ( - f'The expected behavior is to replace ' - f'the deprecated key `{src_arg_name}` to ' - f'new key `{dst_arg_name}`, but got them ' - f'in the arguments at the same time, which ' - f'is confusing. `{src_arg_name} will be ' - f'deprecated in the future, please ' - f'use `{dst_arg_name}` instead.') - - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead') - kwargs[dst_arg_name] = kwargs.pop(src_arg_name) - - # apply converted arguments to the decorated method - output = old_func(*args, **kwargs) - return output - - return new_func - - return api_warning_wrapper - - -def is_method_overridden(method, base_class, derived_class): - """Check if a method of base class is overridden in derived class. - - Args: - method (str): the method name to check. - base_class (type): the class of the base class. - derived_class (type | Any): the class or instance of the derived class. - """ - assert isinstance(base_class, type), \ - "base_class doesn't accept instance, Please pass class instead." - - if not isinstance(derived_class, type): - derived_class = derived_class.__class__ - - base_method = getattr(base_class, method) - derived_method = getattr(derived_class, method) - return derived_method != base_method - - -def has_method(obj: object, method: str) -> bool: - """Check whether the object has a method. - - Args: - method (str): The method name to check. - obj (object): The object to check. - - Returns: - bool: True if the object has the method else False. - """ - return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/parrots_jit.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/parrots_jit.py deleted file mode 100755 index 61873f6db..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/parrots_jit.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -from .parrots_wrapper import TORCH_VERSION - -parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') - -if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': - from parrots.jit import pat as jit -else: - - def jit(func=None, - check_input=None, - full_shape=True, - derivate=False, - coderize=False, - optimize=False): - - def wrapper(func): - - def wrapper_inner(*args, **kargs): - return func(*args, **kargs) - - return wrapper_inner - - if func is None: - return wrapper - else: - return func - - -if TORCH_VERSION == 'parrots': - from parrots.utils.tester import skip_no_elena -else: - - def skip_no_elena(func): - - def wrapper(*args, **kargs): - return func(*args, **kargs) - - return wrapper diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/parrots_wrapper.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/parrots_wrapper.py deleted file mode 100755 index 93c97640d..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/parrots_wrapper.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch - -TORCH_VERSION = torch.__version__ - - -def is_rocm_pytorch() -> bool: - is_rocm = False - if TORCH_VERSION != 'parrots': - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - return is_rocm - - -def _get_cuda_home(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import CUDA_HOME - else: - if is_rocm_pytorch(): - from torch.utils.cpp_extension import ROCM_HOME - CUDA_HOME = ROCM_HOME - else: - from torch.utils.cpp_extension import CUDA_HOME - return CUDA_HOME - - -def get_build_config(): - if TORCH_VERSION == 'parrots': - from parrots.config import get_build_info - return get_build_info() - else: - return torch.__config__.show() - - -def _get_conv(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin - else: - from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin - return _ConvNd, _ConvTransposeMixin - - -def _get_dataloader(): - if TORCH_VERSION == 'parrots': - from torch.utils.data import DataLoader, PoolDataLoader - else: - from torch.utils.data import DataLoader - PoolDataLoader = DataLoader - return DataLoader, PoolDataLoader - - -def _get_extension(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import BuildExtension, Extension - CppExtension = partial(Extension, cuda=False) - CUDAExtension = partial(Extension, cuda=True) - else: - from torch.utils.cpp_extension import (BuildExtension, CppExtension, - CUDAExtension) - return BuildExtension, CppExtension, CUDAExtension - - -def _get_pool(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - else: - from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd - - -def _get_norm(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm2d - else: - from torch.nn.modules.instancenorm import _InstanceNorm - from torch.nn.modules.batchnorm import _BatchNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm - return _BatchNorm, _InstanceNorm, SyncBatchNorm_ - - -_ConvNd, _ConvTransposeMixin = _get_conv() -DataLoader, PoolDataLoader = _get_dataloader() -BuildExtension, CppExtension, CUDAExtension = _get_extension() -_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() -_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() - - -class SyncBatchNorm(SyncBatchNorm_): - - def _check_input_dim(self, input): - if TORCH_VERSION == 'parrots': - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input (got {input.dim()}D input)') - else: - super()._check_input_dim(input) diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/path.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/path.py deleted file mode 100755 index 7dab4b304..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/path.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -from pathlib import Path - -from .misc import is_str - - -def is_filepath(x): - return is_str(x) or isinstance(x, Path) - - -def fopen(filepath, *args, **kwargs): - if is_str(filepath): - return open(filepath, *args, **kwargs) - elif isinstance(filepath, Path): - return filepath.open(*args, **kwargs) - raise ValueError('`filepath` should be a string or a Path') - - -def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): - if not osp.isfile(filename): - raise FileNotFoundError(msg_tmpl.format(filename)) - - -def mkdir_or_exist(dir_name, mode=0o777): - if dir_name == '': - return - dir_name = osp.expanduser(dir_name) - os.makedirs(dir_name, mode=mode, exist_ok=True) - - -def symlink(src, dst, overwrite=True, **kwargs): - if os.path.lexists(dst) and overwrite: - os.remove(dst) - os.symlink(src, dst, **kwargs) - - -def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): - """Scan a directory to find the interested files. - - Args: - dir_path (str | obj:`Path`): Path of the directory. - suffix (str | tuple(str), optional): File suffix that we are - interested in. Default: None. - recursive (bool, optional): If set to True, recursively scan the - directory. Default: False. - case_sensitive (bool, optional) : If set to False, ignore the case of - suffix. Default: True. - - Returns: - A generator for all the interested files with relative paths. - """ - if isinstance(dir_path, (str, Path)): - dir_path = str(dir_path) - else: - raise TypeError('"dir_path" must be a string or Path object') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('"suffix" must be a string or tuple of strings') - - if suffix is not None and not case_sensitive: - suffix = suffix.lower() if isinstance(suffix, str) else tuple( - item.lower() for item in suffix) - - root = dir_path - - def _scandir(dir_path, suffix, recursive, case_sensitive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - _rel_path = rel_path if case_sensitive else rel_path.lower() - if suffix is None or _rel_path.endswith(suffix): - yield rel_path - elif recursive and os.path.isdir(entry.path): - # scan recursively if entry.path is a directory - yield from _scandir(entry.path, suffix, recursive, - case_sensitive) - - return _scandir(dir_path, suffix, recursive, case_sensitive) - - -def find_vcs_root(path, markers=('.git', )): - """Finds the root directory (including itself) of specified markers. - - Args: - path (str): Path of directory or file. - markers (list[str], optional): List of file or directory names. - - Returns: - The directory contained one of the markers or None if not found. - """ - if osp.isfile(path): - path = osp.dirname(path) - - prev, cur = None, osp.abspath(osp.expanduser(path)) - while cur != prev: - if any(osp.exists(osp.join(cur, marker)) for marker in markers): - return cur - prev, cur = cur, osp.split(cur)[0] - return None diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/progressbar.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/progressbar.py deleted file mode 100755 index 0062f670d..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/progressbar.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from collections.abc import Iterable -from multiprocessing import Pool -from shutil import get_terminal_size - -from .timer import Timer - - -class ProgressBar: - """A progress bar which can print the progress.""" - - def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): - self.task_num = task_num - self.bar_width = bar_width - self.completed = 0 - self.file = file - if start: - self.start() - - @property - def terminal_width(self): - width, _ = get_terminal_size() - return width - - def start(self): - if self.task_num > 0: - self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' - 'elapsed: 0s, ETA:') - else: - self.file.write('completed: 0, elapsed: 0s') - self.file.flush() - self.timer = Timer() - - def update(self, num_tasks=1): - assert num_tasks > 0 - self.completed += num_tasks - elapsed = self.timer.since_start() - if elapsed > 0: - fps = self.completed / elapsed - else: - fps = float('inf') - if self.task_num > 0: - percentage = self.completed / float(self.task_num) - eta = int(elapsed * (1 - percentage) / percentage + 0.5) - msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ - f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ - f'ETA: {eta:5}s' - - bar_width = min(self.bar_width, - int(self.terminal_width - len(msg)) + 2, - int(self.terminal_width * 0.6)) - bar_width = max(2, bar_width) - mark_width = int(bar_width * percentage) - bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) - self.file.write(msg.format(bar_chars)) - else: - self.file.write( - f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' - f' {fps:.1f} tasks/s') - self.file.flush() - - -def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): - """Track the progress of tasks execution with a progress bar. - - Tasks are done with a simple for-loop. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - results = [] - for task in tasks: - results.append(func(task, **kwargs)) - prog_bar.update() - prog_bar.file.write('\n') - return results - - -def init_pool(process_num, initializer=None, initargs=None): - if initializer is None: - return Pool(process_num) - elif initargs is None: - return Pool(process_num, initializer) - else: - if not isinstance(initargs, tuple): - raise TypeError('"initargs" must be a tuple') - return Pool(process_num, initializer, initargs) - - -def track_parallel_progress(func, - tasks, - nproc, - initializer=None, - initargs=None, - bar_width=50, - chunksize=1, - skip_first=False, - keep_order=True, - file=sys.stdout): - """Track the progress of parallel task execution with a progress bar. - - The built-in :mod:`multiprocessing` module is used for process pools and - tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - nproc (int): Process (worker) number. - initializer (None or callable): Refer to :class:`multiprocessing.Pool` - for details. - initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for - details. - chunksize (int): Refer to :class:`multiprocessing.Pool` for details. - bar_width (int): Width of progress bar. - skip_first (bool): Whether to skip the first sample for each worker - when estimating fps, since the initialization step may takes - longer. - keep_order (bool): If True, :func:`Pool.imap` is used, otherwise - :func:`Pool.imap_unordered` is used. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - pool = init_pool(nproc, initializer, initargs) - start = not skip_first - task_num -= nproc * chunksize * int(skip_first) - prog_bar = ProgressBar(task_num, bar_width, start, file=file) - results = [] - if keep_order: - gen = pool.imap(func, tasks, chunksize) - else: - gen = pool.imap_unordered(func, tasks, chunksize) - for result in gen: - results.append(result) - if skip_first: - if len(results) < nproc * chunksize: - continue - elif len(results) == nproc * chunksize: - prog_bar.start() - continue - prog_bar.update() - prog_bar.file.write('\n') - pool.close() - pool.join() - return results - - -def track_iter_progress(tasks, bar_width=50, file=sys.stdout): - """Track the progress of tasks iteration or enumeration with a progress - bar. - - Tasks are yielded with a simple for-loop. - - Args: - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Yields: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - for task in tasks: - yield task - prog_bar.update() - prog_bar.file.write('\n') diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/registry.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/registry.py deleted file mode 100755 index fa9df39bc..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/registry.py +++ /dev/null @@ -1,315 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import warnings -from functools import partial - -from .misc import is_seq_of - - -def build_from_cfg(cfg, registry, default_args=None): - """Build a module from config dict. - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - try: - return obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{obj_cls.__name__}: {e}') - - -class Registry: - """A registry to map strings to classes. - - Registered object could be built from registry. - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = MODELS.build(dict(type='ResNet')) - - Please refer to - https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for - advanced usage. - - Args: - name (str): Registry name. - build_func(func, optional): Build function to construct instance from - Registry, func:`build_from_cfg` is used if neither ``parent`` or - ``build_func`` is specified. If ``parent`` is specified and - ``build_func`` is not given, ``build_func`` will be inherited - from ``parent``. Default: None. - parent (Registry, optional): Parent registry. The class registered in - children registry could be built from parent. Default: None. - scope (str, optional): The scope of registry. It is the key to search - for children registry. If not specified, scope will be the name of - the package where class is defined, e.g. mmdet, mmcls, mmseg. - Default: None. - """ - - def __init__(self, name, build_func=None, parent=None, scope=None): - self._name = name - self._module_dict = dict() - self._children = dict() - self._scope = self.infer_scope() if scope is None else scope - - # self.build_func will be set with the following priority: - # 1. build_func - # 2. parent.build_func - # 3. build_from_cfg - if build_func is None: - if parent is not None: - self.build_func = parent.build_func - else: - self.build_func = build_from_cfg - else: - self.build_func = build_func - if parent is not None: - assert isinstance(parent, Registry) - parent._add_children(self) - self.parent = parent - else: - self.parent = None - - def __len__(self): - return len(self._module_dict) - - def __contains__(self, key): - return self.get(key) is not None - - def __repr__(self): - format_str = self.__class__.__name__ + \ - f'(name={self._name}, ' \ - f'items={self._module_dict})' - return format_str - - @staticmethod - def infer_scope(): - """Infer the scope of registry. - - The name of the package where registry is defined will be returned. - - Example: - # in mmdet/models/backbone/resnet.py - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - The scope of ``ResNet`` will be ``mmdet``. - - - Returns: - scope (str): The inferred scope name. - """ - # inspect.stack() trace where this function is called, the index-2 - # indicates the frame where `infer_scope()` is called - filename = inspect.getmodule(inspect.stack()[2][0]).__name__ - split_filename = filename.split('.') - return split_filename[0] - - @staticmethod - def split_scope_key(key): - """Split scope and key. - - The first scope will be split from key. - - Examples: - >>> Registry.split_scope_key('mmdet.ResNet') - 'mmdet', 'ResNet' - >>> Registry.split_scope_key('ResNet') - None, 'ResNet' - - Return: - scope (str, None): The first scope. - key (str): The remaining key. - """ - split_index = key.find('.') - if split_index != -1: - return key[:split_index], key[split_index + 1:] - else: - return None, key - - @property - def name(self): - return self._name - - @property - def scope(self): - return self._scope - - @property - def module_dict(self): - return self._module_dict - - @property - def children(self): - return self._children - - def get(self, key): - """Get the registry record. - - Args: - key (str): The class name in string format. - - Returns: - class: The corresponding class. - """ - scope, real_key = self.split_scope_key(key) - if scope is None or scope == self._scope: - # get from self - if real_key in self._module_dict: - return self._module_dict[real_key] - else: - # get from self._children - if scope in self._children: - return self._children[scope].get(real_key) - else: - # goto root - parent = self.parent - while parent.parent is not None: - parent = parent.parent - return parent.get(key) - - def build(self, *args, **kwargs): - return self.build_func(*args, **kwargs, registry=self) - - def _add_children(self, registry): - """Add children for a registry. - - The ``registry`` will be added as children based on its scope. - The parent registry could build objects from children registry. - - Example: - >>> models = Registry('models') - >>> mmdet_models = Registry('models', parent=models) - >>> @mmdet_models.register_module() - >>> class ResNet: - >>> pass - >>> resnet = models.build(dict(type='mmdet.ResNet')) - """ - - assert isinstance(registry, Registry) - assert registry.scope is not None - assert registry.scope not in self.children, \ - f'scope {registry.scope} exists in {self.name} registry' - self.children[registry.scope] = registry - - def _register_module(self, module_class, module_name=None, force=False): - if not inspect.isclass(module_class): - raise TypeError('module must be a class, ' - f'but got {type(module_class)}') - - if module_name is None: - module_name = module_class.__name__ - if isinstance(module_name, str): - module_name = [module_name] - for name in module_name: - if not force and name in self._module_dict: - raise KeyError(f'{name} is already registered ' - f'in {self.name}') - self._module_dict[name] = module_class - - def deprecated_register_module(self, cls=None, force=False): - warnings.warn( - 'The old API of register_module(module, force=False) ' - 'is deprecated and will be removed, please use the new API ' - 'register_module(name=None, force=False, module=None) instead.') - if cls is None: - return partial(self.deprecated_register_module, force=force) - self._register_module(cls, force=force) - return cls - - def register_module(self, name=None, force=False, module=None): - """Register a module. - - A record will be added to `self._module_dict`, whose key is the class - name or the specified name, and value is the class itself. - It can be used as a decorator or a normal function. - - Example: - >>> backbones = Registry('backbone') - >>> @backbones.register_module() - >>> class ResNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> @backbones.register_module(name='mnet') - >>> class MobileNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> class ResNet: - >>> pass - >>> backbones.register_module(ResNet) - - Args: - name (str | None): The module name to be registered. If not - specified, the class name will be used. - force (bool, optional): Whether to override an existing class with - the same name. Default: False. - module (type): Module class to be registered. - """ - if not isinstance(force, bool): - raise TypeError(f'force must be a boolean, but got {type(force)}') - # NOTE: This is a walkaround to be compatible with the old api, - # while it may introduce unexpected bugs. - if isinstance(name, type): - return self.deprecated_register_module(name, force=force) - - # raise the error ahead of time - if not (name is None or isinstance(name, str) or is_seq_of(name, str)): - raise TypeError( - 'name must be either of None, an instance of str or a sequence' - f' of str, but got {type(name)}') - - # use it as a normal method: x.register_module(module=SomeClass) - if module is not None: - self._register_module( - module_class=module, module_name=name, force=force) - return module - - # use it as a decorator: @x.register_module() - def _register(cls): - self._register_module( - module_class=cls, module_name=name, force=force) - return cls - - return _register diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/testing.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/testing.py deleted file mode 100755 index a27f936da..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/testing.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) Open-MMLab. -import sys -from collections.abc import Iterable -from runpy import run_path -from shlex import split -from typing import Any, Dict, List -from unittest.mock import patch - - -def check_python_script(cmd): - """Run the python cmd script with `__main__`. The difference between - `os.system` is that, this function exectues code in the current process, so - that it can be tracked by coverage tools. Currently it supports two forms: - - - ./tests/data/scripts/hello.py zz - - python tests/data/scripts/hello.py zz - """ - args = split(cmd) - if args[0] == 'python': - args = args[1:] - with patch.object(sys, 'argv', args): - run_path(args[0], run_name='__main__') - - -def _any(judge_result): - """Since built-in ``any`` works only when the element of iterable is not - iterable, implement the function.""" - if not isinstance(judge_result, Iterable): - return judge_result - - try: - for element in judge_result: - if _any(element): - return True - except TypeError: - # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) - if judge_result: - return True - return False - - -def assert_dict_contains_subset(dict_obj: Dict[Any, Any], - expected_subset: Dict[Any, Any]) -> bool: - """Check if the dict_obj contains the expected_subset. - - Args: - dict_obj (Dict[Any, Any]): Dict object to be checked. - expected_subset (Dict[Any, Any]): Subset expected to be contained in - dict_obj. - - Returns: - bool: Whether the dict_obj contains the expected_subset. - """ - - for key, value in expected_subset.items(): - if key not in dict_obj.keys() or _any(dict_obj[key] != value): - return False - return True - - -def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: - """Check if attribute of class object is correct. - - Args: - obj (object): Class object to be checked. - expected_attrs (Dict[str, Any]): Dict of the expected attrs. - - Returns: - bool: Whether the attribute of class object is correct. - """ - for attr, value in expected_attrs.items(): - if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): - return False - return True - - -def assert_dict_has_keys(obj: Dict[str, Any], - expected_keys: List[str]) -> bool: - """Check if the obj has all the expected_keys. - - Args: - obj (Dict[str, Any]): Object to be checked. - expected_keys (List[str]): Keys expected to contained in the keys of - the obj. - - Returns: - bool: Whether the obj has the expected keys. - """ - return set(expected_keys).issubset(set(obj.keys())) - - -def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: - """Check if target_keys is equal to result_keys. - - Args: - result_keys (List[str]): Result keys to be checked. - target_keys (List[str]): Target keys to be checked. - - Returns: - bool: Whether target_keys is equal to result_keys. - """ - return set(result_keys) == set(target_keys) - - -def assert_is_norm_layer(module) -> bool: - """Check if the module is a norm layer. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the module is a norm layer. - """ - from .parrots_wrapper import _BatchNorm, _InstanceNorm - from torch.nn import GroupNorm, LayerNorm - norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) - return isinstance(module, norm_layer_candidates) - - -def assert_params_all_zeros(module) -> bool: - """Check if the parameters of the module is all zeros. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the parameters of the module is all zeros. - """ - weight_data = module.weight.data - is_weight_zero = weight_data.allclose( - weight_data.new_zeros(weight_data.size())) - - if hasattr(module, 'bias') and module.bias is not None: - bias_data = module.bias.data - is_bias_zero = bias_data.allclose( - bias_data.new_zeros(bias_data.size())) - else: - is_bias_zero = True - - return is_weight_zero and is_bias_zero diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/timer.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/timer.py deleted file mode 100755 index 66d4a78a8..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/timer.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from time import time - - -class TimerError(Exception): - - def __init__(self, message): - self.message = message - super(TimerError, self).__init__(message) - - -class Timer: - """A flexible Timer class. - - :Example: - - >>> import time - >>> import mmcv - >>> with mmcv.Timer(): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - 1.000 - >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - it takes 1.0 seconds - >>> timer = mmcv.Timer() - >>> time.sleep(0.5) - >>> print(timer.since_start()) - 0.500 - >>> time.sleep(0.5) - >>> print(timer.since_last_check()) - 0.500 - >>> print(timer.since_start()) - 1.000 - """ - - def __init__(self, start=True, print_tmpl=None): - self._is_running = False - self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' - if start: - self.start() - - @property - def is_running(self): - """bool: indicate whether the timer is running""" - return self._is_running - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - print(self.print_tmpl.format(self.since_last_check())) - self._is_running = False - - def start(self): - """Start the timer.""" - if not self._is_running: - self._t_start = time() - self._is_running = True - self._t_last = time() - - def since_start(self): - """Total time since the timer is started. - - Returns (float): Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - self._t_last = time() - return self._t_last - self._t_start - - def since_last_check(self): - """Time since the last checking. - - Either :func:`since_start` or :func:`since_last_check` is a checking - operation. - - Returns (float): Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - dur = time() - self._t_last - self._t_last = time() - return dur - - -_g_timers = {} # global timers - - -def check_time(timer_id): - """Add check points in a single line. - - This method is suitable for running a task on a list of items. A timer will - be registered when the method is called for the first time. - - :Example: - - >>> import time - >>> import mmcv - >>> for i in range(1, 6): - >>> # simulate a code block - >>> time.sleep(i) - >>> mmcv.check_time('task1') - 2.000 - 3.000 - 4.000 - 5.000 - - Args: - timer_id (str): Timer identifier. - """ - if timer_id not in _g_timers: - _g_timers[timer_id] = Timer() - return 0 - else: - return _g_timers[timer_id].since_last_check() diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/trace.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/trace.py deleted file mode 100755 index 8e49bfd38..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/trace.py +++ /dev/null @@ -1,23 +0,0 @@ -import warnings - -import torch - -from mmcv.utils import digit_version - - -def is_jit_tracing() -> bool: - if (torch.__version__ != 'parrots' - and digit_version(torch.__version__) >= digit_version('1.6.0')): - on_trace = torch.jit.is_tracing() - # In PyTorch 1.6, torch.jit.is_tracing has a bug. - # Refers to https://github.com/pytorch/pytorch/issues/42448 - if isinstance(on_trace, bool): - return on_trace - else: - return torch._C._is_tracing() - else: - warnings.warn( - 'torch.jit.is_tracing is only supported after v1.6.0. ' - 'Therefore is_tracing returns False automatically. Please ' - 'set on_trace manually if you are using trace.', UserWarning) - return False diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/utils/version_utils.py b/cv/super_resolution/esrgan/pytorch/mmcv/utils/version_utils.py deleted file mode 100755 index 963c45a2e..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/utils/version_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import subprocess -import warnings - -from packaging.version import parse - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - assert 'parrots' not in version_str - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - -def get_git_hash(fallback='unknown', digits=None): - """Get the git hash of the current repo. - - Args: - fallback (str, optional): The fallback string when git hash is - unavailable. Defaults to 'unknown'. - digits (int, optional): kept digits of the hash. Defaults to None, - meaning all digits are kept. - - Returns: - str: Git commit hash. - """ - - if digits is not None and not isinstance(digits, int): - raise TypeError('digits must be None or an integer') - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - sha = out.strip().decode('ascii') - if digits is not None: - sha = sha[:digits] - except OSError: - sha = fallback - - return sha diff --git a/cv/super_resolution/esrgan/pytorch/mmcv/version.py b/cv/super_resolution/esrgan/pytorch/mmcv/version.py deleted file mode 100755 index 1cce4e50b..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmcv/version.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -__version__ = '1.3.17' - - -def parse_version_info(version_str: str, length: int = 4) -> tuple: - """Parse a version string into a tuple. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int | str]: The version info, e.g., "1.3.0" is parsed into - (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into - (2, 0, 0, 0, 'rc', 1) (when length is set to 4). - """ - from packaging.version import parse - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - release.extend(list(version.pre)) - elif version.is_postrelease: - release.extend(list(version.post)) - else: - release.extend([0, 0]) - return tuple(release) - - -version_info = tuple(int(x) for x in __version__.split('.')[:3]) - -__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/__init__.py deleted file mode 100755 index 05d4c20b7..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - -from .version import __version__, version_info - -try: - from mmcv.utils import digit_version -except ImportError: - - def digit_version(version_str): - digit_ver = [] - for x in version_str.split('.'): - if x.isdigit(): - digit_ver.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - digit_ver.append(int(patch_version[0]) - 1) - digit_ver.append(int(patch_version[1])) - return digit_ver - - -MMCV_MIN = '1.3.13' -MMCV_MAX = '1.6' - -mmcv_min_version = digit_version(MMCV_MIN) -mmcv_max_version = digit_version(MMCV_MAX) -mmcv_version = digit_version(mmcv.__version__) - - -assert (mmcv_min_version <= mmcv_version <= mmcv_max_version), \ - f'mmcv=={mmcv.__version__} is used but incompatible. ' \ - f'Please install mmcv-full>={mmcv_min_version}, <={mmcv_max_version}.' - -__all__ = ['__version__', 'version_info'] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/apis/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/apis/__init__.py deleted file mode 100755 index 1ae70035e..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/apis/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .generation_inference import generation_inference -from .inpainting_inference import inpainting_inference -from .matting_inference import init_model, matting_inference -from .restoration_face_inference import restoration_face_inference -from .restoration_inference import restoration_inference -from .restoration_video_inference import restoration_video_inference -from .test import multi_gpu_test, single_gpu_test -from .train import init_random_seed, set_random_seed, train_model -from .video_interpolation_inference import video_interpolation_inference - -__all__ = [ - 'train_model', 'set_random_seed', 'init_model', 'matting_inference', - 'inpainting_inference', 'restoration_inference', 'generation_inference', - 'multi_gpu_test', 'single_gpu_test', 'restoration_video_inference', - 'restoration_face_inference', 'video_interpolation_inference', - 'init_random_seed' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/apis/generation_inference.py b/cv/super_resolution/esrgan/pytorch/mmedit/apis/generation_inference.py deleted file mode 100755 index c8e829bb1..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/apis/generation_inference.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -from mmcv.parallel import collate, scatter - -from mmedit.core import tensor2img -from mmedit.datasets.pipelines import Compose - - -def generation_inference(model, img, img_unpaired=None): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img (str): File path of input image. - img_unpaired (str, optional): File path of the unpaired image. - If not None, perform unpaired image generation. Default: None. - - Returns: - np.ndarray: The predicted generation result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # build the data pipeline - test_pipeline = Compose(cfg.test_pipeline) - # prepare data - if img_unpaired is None: - data = dict(pair_path=img) - else: - data = dict(img_a_path=img, img_b_path=img_unpaired) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - # forward the model - with torch.no_grad(): - results = model(test_mode=True, **data) - # process generation shown mode - if img_unpaired is None: - if model.show_input: - output = np.concatenate([ - tensor2img(results['real_a'], min_max=(-1, 1)), - tensor2img(results['fake_b'], min_max=(-1, 1)), - tensor2img(results['real_b'], min_max=(-1, 1)) - ], - axis=1) - else: - output = tensor2img(results['fake_b'], min_max=(-1, 1)) - else: - if model.show_input: - output = np.concatenate([ - tensor2img(results['real_a'], min_max=(-1, 1)), - tensor2img(results['fake_b'], min_max=(-1, 1)), - tensor2img(results['real_b'], min_max=(-1, 1)), - tensor2img(results['fake_a'], min_max=(-1, 1)) - ], - axis=1) - else: - if model.test_direction == 'a2b': - output = tensor2img(results['fake_b'], min_max=(-1, 1)) - else: - output = tensor2img(results['fake_a'], min_max=(-1, 1)) - return output diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/apis/inpainting_inference.py b/cv/super_resolution/esrgan/pytorch/mmedit/apis/inpainting_inference.py deleted file mode 100755 index f80650b74..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/apis/inpainting_inference.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.parallel import collate, scatter - -from mmedit.datasets.pipelines import Compose - - -def inpainting_inference(model, masked_img, mask): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - masked_img (str): File path of image with mask. - mask (str): Mask file path. - - Returns: - Tensor: The predicted inpainting result. - """ - device = next(model.parameters()).device # model device - - infer_pipeline = [ - dict(type='LoadImageFromFile', key='masked_img'), - dict(type='LoadMask', mask_mode='file', mask_config=dict()), - dict(type='Pad', keys=['masked_img', 'mask'], mode='reflect'), - dict( - type='Normalize', - keys=['masked_img'], - mean=[127.5] * 3, - std=[127.5] * 3, - to_rgb=False), - dict(type='GetMaskedImage', img_name='masked_img'), - dict( - type='Collect', - keys=['masked_img', 'mask'], - meta_keys=['masked_img_path']), - dict(type='ImageToTensor', keys=['masked_img', 'mask']) - ] - - # build the data pipeline - test_pipeline = Compose(infer_pipeline) - # prepare data - data = dict(masked_img_path=masked_img, mask_path=mask) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - else: - data.pop('meta') - # forward the model - with torch.no_grad(): - result = model(test_mode=True, **data) - - return result['fake_img'] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/apis/matting_inference.py b/cv/super_resolution/esrgan/pytorch/mmedit/apis/matting_inference.py deleted file mode 100755 index 5446afe73..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/apis/matting_inference.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch -from mmcv.parallel import collate, scatter -from mmcv.runner import load_checkpoint - -from mmedit.datasets.pipelines import Compose -from mmedit.models import build_model - - -def init_model(config, checkpoint=None, device='cuda:0'): - """Initialize a model from config file. - - Args: - config (str or :obj:`mmcv.Config`): Config file path or the config - object. - checkpoint (str, optional): Checkpoint path. If left as None, the model - will not load any weights. - device (str): Which device the model will deploy. Default: 'cuda:0'. - - Returns: - nn.Module: The constructed model. - """ - if isinstance(config, str): - config = mmcv.Config.fromfile(config) - elif not isinstance(config, mmcv.Config): - raise TypeError('config must be a filename or Config object, ' - f'but got {type(config)}') - config.model.pretrained = None - config.test_cfg.metrics = None - model = build_model(config.model, test_cfg=config.test_cfg) - if checkpoint is not None: - checkpoint = load_checkpoint(model, checkpoint) - - model.cfg = config # save the config in the model for convenience - model.to(device) - model.eval() - return model - - -def matting_inference(model, img, trimap): - """Inference image(s) with the model. - - Args: - model (nn.Module): The loaded model. - img (str): Image file path. - trimap (str): Trimap file path. - - Returns: - np.ndarray: The predicted alpha matte. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # remove alpha from test_pipeline - keys_to_remove = ['alpha', 'ori_alpha'] - for key in keys_to_remove: - for pipeline in list(cfg.test_pipeline): - if 'key' in pipeline and key == pipeline['key']: - cfg.test_pipeline.remove(pipeline) - if 'keys' in pipeline and key in pipeline['keys']: - pipeline['keys'].remove(key) - if len(pipeline['keys']) == 0: - cfg.test_pipeline.remove(pipeline) - if 'meta_keys' in pipeline and key in pipeline['meta_keys']: - pipeline['meta_keys'].remove(key) - # build the data pipeline - test_pipeline = Compose(cfg.test_pipeline) - # prepare data - data = dict(merged_path=img, trimap_path=trimap) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - # forward the model - with torch.no_grad(): - result = model(test_mode=True, **data) - - return result['pred_alpha'] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/apis/restoration_face_inference.py b/cv/super_resolution/esrgan/pytorch/mmedit/apis/restoration_face_inference.py deleted file mode 100755 index dbe12e11a..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/apis/restoration_face_inference.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -from mmcv.parallel import collate, scatter - -from mmedit.datasets.pipelines import Compose - -try: - from facexlib.utils.face_restoration_helper import FaceRestoreHelper - has_facexlib = True -except ImportError: - has_facexlib = False - - -def restoration_face_inference(model, img, upscale_factor=1, face_size=1024): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img (str): File path of input image. - upscale_factor (int, optional): The number of times the input image - is upsampled. Default: 1. - face_size (int, optional): The size of the cropped and aligned faces. - Default: 1024. - - Returns: - Tensor: The predicted restoration result. - """ - device = next(model.parameters()).device # model device - - # build the data pipeline - if model.cfg.get('demo_pipeline', None): - test_pipeline = model.cfg.demo_pipeline - elif model.cfg.get('test_pipeline', None): - test_pipeline = model.cfg.test_pipeline - else: - test_pipeline = model.cfg.val_pipeline - - # remove gt from test_pipeline - keys_to_remove = ['gt', 'gt_path'] - for key in keys_to_remove: - for pipeline in list(test_pipeline): - if 'key' in pipeline and key == pipeline['key']: - test_pipeline.remove(pipeline) - if 'keys' in pipeline and key in pipeline['keys']: - pipeline['keys'].remove(key) - if len(pipeline['keys']) == 0: - test_pipeline.remove(pipeline) - if 'meta_keys' in pipeline and key in pipeline['meta_keys']: - pipeline['meta_keys'].remove(key) - # build the data pipeline - test_pipeline = Compose(test_pipeline) - - # face helper for detecting and aligning faces - assert has_facexlib, 'Please install FaceXLib to use the demo.' - face_helper = FaceRestoreHelper( - upscale_factor, - face_size=face_size, - crop_ratio=(1, 1), - det_model='retinaface_resnet50', - template_3points=True, - save_ext='png', - device=device) - - face_helper.read_image(img) - # get face landmarks for each face - face_helper.get_face_landmarks_5( - only_center_face=False, eye_dist_threshold=None) - # align and warp each face - face_helper.align_warp_face() - - for i, img in enumerate(face_helper.cropped_faces): - # prepare data - data = dict(lq=img.astype(np.float32)) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - - with torch.no_grad(): - output = model(test_mode=True, **data)['output'].clip_(0, 1) - - output = output.squeeze(0).permute(1, 2, 0)[:, :, [2, 1, 0]] - output = output.cpu().numpy() * 255 # (0, 255) - face_helper.add_restored_face(output) - - face_helper.get_inverse_affine(None) - restored_img = face_helper.paste_faces_to_input_image(upsample_img=None) - - return restored_img diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/apis/restoration_inference.py b/cv/super_resolution/esrgan/pytorch/mmedit/apis/restoration_inference.py deleted file mode 100755 index 98c08abc8..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/apis/restoration_inference.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.parallel import collate, scatter - -from mmedit.datasets.pipelines import Compose - - -def restoration_inference(model, img, ref=None): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img (str): File path of input image. - ref (str | None): File path of reference image. Default: None. - - Returns: - Tensor: The predicted restoration result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # remove gt from test_pipeline - keys_to_remove = ['gt', 'gt_path'] - for key in keys_to_remove: - for pipeline in list(cfg.test_pipeline): - if 'key' in pipeline and key == pipeline['key']: - cfg.test_pipeline.remove(pipeline) - if 'keys' in pipeline and key in pipeline['keys']: - pipeline['keys'].remove(key) - if len(pipeline['keys']) == 0: - cfg.test_pipeline.remove(pipeline) - if 'meta_keys' in pipeline and key in pipeline['meta_keys']: - pipeline['meta_keys'].remove(key) - # build the data pipeline - test_pipeline = Compose(cfg.test_pipeline) - # prepare data - if ref: # Ref-SR - data = dict(lq_path=img, ref_path=ref) - else: # SISR - data = dict(lq_path=img) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - # forward the model - with torch.no_grad(): - result = model(test_mode=True, **data) - - return result['output'] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/apis/restoration_video_inference.py b/cv/super_resolution/esrgan/pytorch/mmedit/apis/restoration_video_inference.py deleted file mode 100755 index ca6369dfc..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/apis/restoration_video_inference.py +++ /dev/null @@ -1,129 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os.path as osp -import re -from functools import reduce - -import mmcv -import numpy as np -import torch - -from mmedit.datasets.pipelines import Compose - -VIDEO_EXTENSIONS = ('.mp4', '.mov') - - -def pad_sequence(data, window_size): - padding = window_size // 2 - - data = torch.cat([ - data[:, 1 + padding:1 + 2 * padding].flip(1), data, - data[:, -1 - 2 * padding:-1 - padding].flip(1) - ], - dim=1) - - return data - - -def restoration_video_inference(model, - img_dir, - window_size, - start_idx, - filename_tmpl, - max_seq_len=None): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img_dir (str): Directory of the input video. - window_size (int): The window size used in sliding-window framework. - This value should be set according to the settings of the network. - A value smaller than 0 means using recurrent framework. - start_idx (int): The index corresponds to the first frame in the - sequence. - filename_tmpl (str): Template for file name. - max_seq_len (int | None): The maximum sequence length that the model - processes. If the sequence length is larger than this number, - the sequence is split into multiple segments. If it is None, - the entire sequence is processed at once. - - Returns: - Tensor: The predicted restoration result. - """ - - device = next(model.parameters()).device # model device - - # build the data pipeline - if model.cfg.get('demo_pipeline', None): - test_pipeline = model.cfg.demo_pipeline - elif model.cfg.get('test_pipeline', None): - test_pipeline = model.cfg.test_pipeline - else: - test_pipeline = model.cfg.val_pipeline - - # check if the input is a video - file_extension = osp.splitext(img_dir)[1] - if file_extension in VIDEO_EXTENSIONS: - video_reader = mmcv.VideoReader(img_dir) - # load the images - data = dict(lq=[], lq_path=None, key=img_dir) - for frame in video_reader: - data['lq'].append(np.flip(frame, axis=2)) - - # remove the data loading pipeline - tmp_pipeline = [] - for pipeline in test_pipeline: - if pipeline['type'] not in [ - 'GenerateSegmentIndices', 'LoadImageFromFileList' - ]: - tmp_pipeline.append(pipeline) - test_pipeline = tmp_pipeline - else: - # the first element in the pipeline must be 'GenerateSegmentIndices' - if test_pipeline[0]['type'] != 'GenerateSegmentIndices': - raise TypeError('The first element in the pipeline must be ' - f'"GenerateSegmentIndices", but got ' - f'"{test_pipeline[0]["type"]}".') - - # specify start_idx and filename_tmpl - test_pipeline[0]['start_idx'] = start_idx - test_pipeline[0]['filename_tmpl'] = filename_tmpl - - # prepare data - sequence_length = len(glob.glob(osp.join(img_dir, '*'))) - img_dir_split = re.split(r'[\\/]', img_dir) - key = img_dir_split[-1] - lq_folder = reduce(osp.join, img_dir_split[:-1]) - data = dict( - lq_path=lq_folder, - gt_path='', - key=key, - sequence_length=sequence_length) - - # compose the pipeline - test_pipeline = Compose(test_pipeline) - data = test_pipeline(data) - data = data['lq'].unsqueeze(0) # in cpu - - # forward the model - with torch.no_grad(): - if window_size > 0: # sliding window framework - data = pad_sequence(data, window_size) - result = [] - for i in range(0, data.size(1) - 2 * (window_size // 2)): - data_i = data[:, i:i + window_size].to(device) - result.append(model(lq=data_i, test_mode=True)['output'].cpu()) - result = torch.stack(result, dim=1) - else: # recurrent framework - if max_seq_len is None: - result = model( - lq=data.to(device), test_mode=True)['output'].cpu() - else: - result = [] - for i in range(0, data.size(1), max_seq_len): - result.append( - model( - lq=data[:, i:i + max_seq_len].to(device), - test_mode=True)['output'].cpu()) - result = torch.cat(result, dim=1) - return result diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/apis/test.py b/cv/super_resolution/esrgan/pytorch/mmedit/apis/test.py deleted file mode 100755 index 535511eee..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/apis/test.py +++ /dev/null @@ -1,234 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile - -import mmcv -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, - data_loader, - save_image=False, - save_path=None, - iteration=None): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - save_image (bool): Whether save image. Default: False. - save_path (str): The path to save image. Default: None. - iteration (int): Iteration number. It is used for the save image name. - Default: None. - - Returns: - list: The prediction results. - """ - if save_image and save_path is None: - raise ValueError( - "When 'save_image' is True, you should also set 'save_path'.") - - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model( - test_mode=True, - save_image=save_image, - save_path=save_path, - iteration=iteration, - **data) - results.append(result) - - # get batch size - for _, v in data.items(): - if isinstance(v, torch.Tensor): - batch_size = v.size(0) - break - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, - data_loader, - tmpdir=None, - gpu_collect=False, - save_image=False, - save_path=None, - iteration=None, - empty_cache=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' - it encodes results to gpu tensors and use gpu communication for results - collection. On cpu mode it saves the results on different gpus to 'tmpdir' - and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - save_image (bool): Whether save image. Default: False. - save_path (str): The path to save image. Default: None. - iteration (int): Iteration number. It is used for the save image name. - Default: None. - empty_cache (bool): empty cache in every iteration. Default: False. - - Returns: - list: The prediction results. - """ - - if save_image and save_path is None: - raise ValueError( - "When 'save_image' is True, you should also set 'save_path'.") - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model( - test_mode=True, - save_image=save_image, - save_path=save_path, - iteration=iteration, - **data) - results.append(result) - if empty_cache: - torch.cuda.empty_cache() - if rank == 0: - # get batch size - for _, v in data.items(): - if isinstance(v, torch.Tensor): - batch_size = v.size(0) - break - for _ in range(batch_size * world_size): - prog_bar.update() - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results in cpu mode. - - It saves the results on different gpus to 'tmpdir' and collects - them by the rank 0 worker. - - Args: - result_part (list): Results to be collected - size (int): Result size. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. Default: None - - Returns: - list: Ordered results. - """ - - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # synchronizes all processes to make sure tmpdir exist - dist.barrier() - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, 'part_{}.pkl'.format(rank))) - # synchronizes all processes for loading pickle file - dist.barrier() - # collect all parts - if rank != 0: - return None - - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, 'part_{}.pkl'.format(i)) - part_list.append(mmcv.load(part_file)) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results in gpu mode. - - It encodes results to gpu tensors and use gpu communication for results - collection. - - Args: - result_part (list): Results to be collected - size (int): Result size. - - Returns: - list: Ordered results. - """ - - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank != 0: - return None - - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_list.append(pickle.loads(recv[:shape[0]].cpu().numpy().tobytes())) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/apis/train.py b/cv/super_resolution/esrgan/pytorch/mmedit/apis/train.py deleted file mode 100755 index 359943ca8..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/apis/train.py +++ /dev/null @@ -1,361 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -import random -import warnings - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.parallel import MMDataParallel -from mmcv.runner import HOOKS, IterBasedRunner, get_dist_info -from mmcv.utils import build_from_cfg - -from mmedit.core import DistEvalIterHook, EvalIterHook, build_optimizers -from mmedit.core.distributed_wrapper import DistributedDataParallelWrapper -from mmedit.datasets.builder import build_dataloader, build_dataset -from mmedit.utils import get_root_logger - - -def init_random_seed(seed=None, device='cuda'): - """Initialize random seed. - If the seed is not set, the seed will be automatically randomized, - and then broadcast to all processes to prevent some potential bugs. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is not None: - return seed - - # Make sure all ranks share the same random seed to prevent - # some potential bugs. Please refer to - # https://github.com/open-mmlab/mmdetection/issues/6339 - rank, world_size = get_dist_info() - seed = np.random.randint(2**31) - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() - - -def set_random_seed(seed, deterministic=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - """ - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - - -def train_model(model, - dataset, - cfg, - distributed=False, - validate=False, - timestamp=None, - meta=None): - """Train model entry function. - - Args: - model (nn.Module): The model to be trained. - dataset (:obj:`Dataset`): Train dataset. - cfg (dict): The config dict for training. - distributed (bool): Whether to use distributed training. - Default: False. - validate (bool): Whether to do evaluation. Default: False. - timestamp (str | None): Local time for runner. Default: None. - meta (dict | None): Meta dict to record some important information. - Default: None - """ - logger = get_root_logger(log_level=cfg.log_level) - - # start training - if distributed: - _dist_train( - model, - dataset, - cfg, - validate=validate, - logger=logger, - timestamp=timestamp, - meta=meta) - else: - _non_dist_train( - model, - dataset, - cfg, - validate=validate, - logger=logger, - timestamp=timestamp, - meta=meta) - - -def _dist_train(model, - dataset, - cfg, - validate=False, - logger=None, - timestamp=None, - meta=None): - """Distributed training function. - - Args: - model (nn.Module): The model to be trained. - dataset (:obj:`Dataset`): Train dataset. - cfg (dict): The config dict for training. - validate (bool): Whether to do evaluation. Default: False. - logger (logging.Logger | None): Logger for training. Default: None. - timestamp (str | None): Local time for runner. Default: None. - meta (dict | None): Meta dict to record some important information. - Default: None. - """ - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - - # step 1: give default values and override (if exist) from cfg.data - loader_cfg = { - **dict(seed=cfg.get('seed'), drop_last=False, dist=True), - **({} if torch.__version__ != 'parrots' else dict( - prefetch_num=2, - pin_memory=False, - )), - **dict((k, cfg.data[k]) for k in [ - 'samples_per_gpu', - 'workers_per_gpu', - 'shuffle', - 'seed', - 'drop_last', - 'prefetch_num', - 'pin_memory', - ] if k in cfg.data) - } - - # step 2: cfg.data.train_dataloader has highest priority - train_loader_cfg = dict(loader_cfg, **cfg.data.get('train_dataloader', {})) - - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus - find_unused_parameters = cfg.get('find_unused_parameters', False) - model = DistributedDataParallelWrapper( - model, - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - - # build runner - optimizer = build_optimizers(model, cfg.optimizers) - runner = IterBasedRunner( - model, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta) - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register hooks - runner.register_training_hooks( - cfg.lr_config, - checkpoint_config=cfg.checkpoint_config, - log_config=cfg.log_config) - - # visual hook - if cfg.get('visual_config', None) is not None: - cfg.visual_config['output_dir'] = os.path.join( - cfg.work_dir, cfg.visual_config['output_dir']) - runner.register_hook(mmcv.build_from_cfg(cfg.visual_config, HOOKS)) - - # evaluation hook - if validate and cfg.get('evaluation', None) is not None: - dataset = build_dataset(cfg.data.val) - - if ('val_samples_per_gpu' in cfg.data - or 'val_workers_per_gpu' in cfg.data): - warnings.warn('"val_samples_per_gpu/val_workers_per_gpu" have ' - 'been deprecated. Please use ' - '"val_dataloader=dict(samples_per_gpu=1)" instead. ' - 'Details see ' - 'https://github.com/open-mmlab/mmediting/pull/201') - - val_loader_cfg = { - **loader_cfg, - **dict(shuffle=False, drop_last=False), - **dict((newk, cfg.data[oldk]) for oldk, newk in [ - ('val_samples_per_gpu', 'samples_per_gpu'), - ('val_workers_per_gpu', 'workers_per_gpu'), - ] if oldk in cfg.data), - **cfg.data.get('val_dataloader', {}) - } - - data_loader = build_dataloader(dataset, **val_loader_cfg) - save_path = osp.join(cfg.work_dir, 'val_visuals') - runner.register_hook( - DistEvalIterHook( - data_loader, save_path=save_path, **cfg.evaluation), - priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow, cfg.total_iters) - - -def _non_dist_train(model, - dataset, - cfg, - validate=False, - logger=None, - timestamp=None, - meta=None): - """Non-Distributed training function. - - Args: - model (nn.Module): The model to be trained. - dataset (:obj:`Dataset`): Train dataset. - cfg (dict): The config dict for training. - validate (bool): Whether to do evaluation. Default: False. - logger (logging.Logger | None): Logger for training. Default: None. - timestamp (str | None): Local time for runner. Default: None. - meta (dict | None): Meta dict to record some important information. - Default: None. - """ - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - - # step 1: give default values and override (if exist) from cfg.data - loader_cfg = { - **dict( - seed=cfg.get('seed'), - drop_last=False, - dist=False, - num_gpus=cfg.gpus), - **({} if torch.__version__ != 'parrots' else dict( - prefetch_num=2, - pin_memory=False, - )), - **dict((k, cfg.data[k]) for k in [ - 'samples_per_gpu', - 'workers_per_gpu', - 'shuffle', - 'seed', - 'drop_last', - 'prefetch_num', - 'pin_memory', - ] if k in cfg.data) - } - - # step 2: cfg.data.train_dataloader has highest priority - train_loader_cfg = dict(loader_cfg, **cfg.data.get('train_dataloader', {})) - - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus/cpus - model = MMDataParallel(model, device_ids=range(cfg.gpus)) - - # build runner - optimizer = build_optimizers(model, cfg.optimizers) - runner = IterBasedRunner( - model, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta) - - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register hooks - runner.register_training_hooks( - cfg.lr_config, - checkpoint_config=cfg.checkpoint_config, - log_config=cfg.log_config) - - # visual hook - if cfg.get('visual_config', None) is not None: - cfg.visual_config['output_dir'] = os.path.join( - cfg.work_dir, cfg.visual_config['output_dir']) - runner.register_hook(mmcv.build_from_cfg(cfg.visual_config, HOOKS)) - - # evaluation hook - if validate and cfg.get('evaluation', None) is not None: - dataset = build_dataset(cfg.data.val) - - if ('val_samples_per_gpu' in cfg.data - or 'val_workers_per_gpu' in cfg.data): - warnings.warn('"val_samples_per_gpu/val_workers_per_gpu" have ' - 'been deprecated. Please use ' - '"val_dataloader=dict(samples_per_gpu=1)" instead. ' - 'Details see ' - 'https://github.com/open-mmlab/mmediting/pull/201') - - val_loader_cfg = { - **loader_cfg, - **dict(shuffle=False, drop_last=False), - **dict((newk, cfg.data[oldk]) for oldk, newk in [ - ('val_samples_per_gpu', 'samples_per_gpu'), - ('val_workers_per_gpu', 'workers_per_gpu'), - ] if oldk in cfg.data), - **cfg.data.get('val_dataloader', {}) - } - - data_loader = build_dataloader(dataset, **val_loader_cfg) - save_path = osp.join(cfg.work_dir, 'val_visuals') - runner.register_hook( - EvalIterHook(data_loader, save_path=save_path, **cfg.evaluation), - priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow, cfg.total_iters) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/apis/video_interpolation_inference.py b/cv/super_resolution/esrgan/pytorch/mmedit/apis/video_interpolation_inference.py deleted file mode 100755 index eaabb4308..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/apis/video_interpolation_inference.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -import os -import os.path as osp - -import cv2 -import mmcv -import numpy as np -import torch -from mmcv.fileio import FileClient -from mmcv.parallel import collate - -from mmedit.datasets.pipelines import Compose - -VIDEO_EXTENSIONS = ('.mp4', '.mov', '.avi') -FILE_CLIENT = FileClient('disk') - - -def read_image(filepath): - """Read image from file. - - Args: - filepath (str): File path. - - Returns: - image (np.array): Image. - """ - img_bytes = FILE_CLIENT.get(filepath) - image = mmcv.imfrombytes( - img_bytes, flag='color', channel_order='rgb', backend='pillow') - return image - - -def read_frames(source, start_index, num_frames, from_video, end_index): - """Read frames from file or video. - - Args: - source (list | mmcv.VideoReader): Source of frames. - start_index (int): Start index of frames. - num_frames (int): frames number to be read. - from_video (bool): Weather read frames from video. - end_index (int): The end index of frames. - - Returns: - images (np.array): Images. - """ - images = [] - last_index = min(start_index + num_frames, end_index) - # read frames from video - if from_video: - for index in range(start_index, last_index): - if index >= source.frame_cnt: - break - images.append(np.flip(source.get_frame(index), axis=2)) - else: - files = source[start_index:last_index] - images = [read_image(f) for f in files] - return images - - -def video_interpolation_inference(model, - input_dir, - output_dir, - start_idx=0, - end_idx=None, - batch_size=4, - fps_multiplier=0, - fps=0, - filename_tmpl='{:08d}.png'): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - input_dir (str): Directory of the input video. - output_dir (str): Directory of the output video. - start_idx (int): The index corresponding to the first frame in the - sequence. Default: 0 - end_idx (int | None): The index corresponding to the last interpolated - frame in the sequence. If it is None, interpolate to the last - frame of video or sequence. Default: None - batch_size (int): Batch size. Default: 4 - fps_multiplier (float): multiply the fps based on the input video. - Default: 0. - fps (float): frame rate of the output video. Default: 0. - filename_tmpl (str): template of the file names. Default: '{:08d}.png' - - Returns: - output (list[numpy.array]): The predicted interpolation result. - It is an image sequence. - input_fps (float): The fps of input video. If the input is an image - sequence, input_fps=0.0 - """ - - device = next(model.parameters()).device # model device - - # build the data pipeline - if model.cfg.get('demo_pipeline', None): - test_pipeline = model.cfg.demo_pipeline - elif model.cfg.get('test_pipeline', None): - test_pipeline = model.cfg.test_pipeline - else: - test_pipeline = model.cfg.val_pipeline - - # remove the data loading pipeline - tmp_pipeline = [] - for pipeline in test_pipeline: - if pipeline['type'] not in [ - 'GenerateSegmentIndices', 'LoadImageFromFileList', - 'LoadImageFromFile' - ]: - tmp_pipeline.append(pipeline) - test_pipeline = tmp_pipeline - - # compose the pipeline - test_pipeline = Compose(test_pipeline) - - # check if the input is a video - input_file_extension = os.path.splitext(input_dir)[1] - if input_file_extension in VIDEO_EXTENSIONS: - source = mmcv.VideoReader(input_dir) - input_fps = source.fps - length = source.frame_cnt - from_video = True - h, w = source.height, source.width - if fps_multiplier: - assert fps_multiplier > 0, '`fps_multiplier` cannot be negative' - output_fps = fps_multiplier * input_fps - else: - output_fps = fps if fps > 0 else input_fps * 2 - else: - files = os.listdir(input_dir) - files = [osp.join(input_dir, f) for f in files] - files.sort() - source = files - length = files.__len__() - from_video = False - example_frame = read_image(files[0]) - h, w = example_frame.shape[:2] - output_fps = fps - - # check if the output is a video - output_file_extension = os.path.splitext(output_dir)[1] - if output_file_extension in VIDEO_EXTENSIONS: - fourcc = cv2.VideoWriter_fourcc(*'mp4v') - target = cv2.VideoWriter(output_dir, fourcc, output_fps, (w, h)) - to_video = True - else: - to_video = False - - end_idx = min(end_idx, length) if end_idx is not None else length - - # calculate step args - step_size = model.step_frames * batch_size - lenth_per_step = model.required_frames + model.step_frames * ( - batch_size - 1) - repeat_frame = model.required_frames - model.step_frames - - prog_bar = mmcv.ProgressBar( - math.ceil( - (end_idx + step_size - lenth_per_step - start_idx) / step_size)) - output_index = start_idx - for start_index in range(start_idx, end_idx, step_size): - images = read_frames( - source, start_index, lenth_per_step, from_video, end_index=end_idx) - - # data prepare - data = dict(inputs=images, inputs_path=None, key=input_dir) - data = [test_pipeline(data)] - data = collate(data, samples_per_gpu=1)['inputs'] - # data.shape: [1, t, c, h, w] - - # forward the model - data = model.split_frames(data) - input_tensors = data.clone().detach() - with torch.no_grad(): - output = model(data.to(device), test_mode=True)['output'] - if len(output.shape) == 4: - output = output.unsqueeze(1) - output_tensors = output.cpu() - if len(output_tensors.shape) == 4: - output_tensors = output_tensors.unsqueeze(1) - result = model.merge_frames(input_tensors, output_tensors) - if not start_idx == start_index: - result = result[repeat_frame:] - prog_bar.update() - - # save frames - if to_video: - for frame in result: - target.write(frame) - else: - for frame in result: - save_path = osp.join(output_dir, - filename_tmpl.format(output_index)) - mmcv.imwrite(frame, save_path) - output_index += 1 - - if start_index + lenth_per_step >= end_idx: - break - - print() - print(f'Output dir: {output_dir}') - if to_video: - target.release() diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/__init__.py deleted file mode 100755 index 2b24ce348..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .evaluation import (DistEvalIterHook, EvalIterHook, L1Evaluation, mae, - mse, psnr, reorder_image, sad, ssim) -from .hooks import VisualizationHook -from .misc import tensor2img -from .optimizer import build_optimizers -from .scheduler import LinearLrUpdaterHook, ReduceLrUpdaterHook - -__all__ = [ - 'build_optimizers', 'tensor2img', 'EvalIterHook', 'DistEvalIterHook', - 'mse', 'psnr', 'reorder_image', 'sad', 'ssim', 'LinearLrUpdaterHook', - 'VisualizationHook', 'L1Evaluation', 'ReduceLrUpdaterHook', 'mae' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/distributed_wrapper.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/distributed_wrapper.py deleted file mode 100755 index 660f41f3f..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/distributed_wrapper.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.parallel import MODULE_WRAPPERS, MMDistributedDataParallel -from mmcv.parallel.scatter_gather import scatter_kwargs -from torch.cuda._utils import _get_device_index - - -@MODULE_WRAPPERS.register_module() -class DistributedDataParallelWrapper(nn.Module): - """A DistributedDataParallel wrapper for models in MMediting. - - In MMedting, there is a need to wrap different modules in the models - with separate DistributedDataParallel. Otherwise, it will cause - errors for GAN training. - More specific, the GAN model, usually has two sub-modules: - generator and discriminator. If we wrap both of them in one - standard DistributedDataParallel, it will cause errors during training, - because when we update the parameters of the generator (or discriminator), - the parameters of the discriminator (or generator) is not updated, which is - not allowed for DistributedDataParallel. - So we design this wrapper to separately wrap DistributedDataParallel - for generator and discriminator. - - In this wrapper, we perform two operations: - 1. Wrap the modules in the models with separate MMDistributedDataParallel. - Note that only modules with parameters will be wrapped. - 2. Do scatter operation for 'forward', 'train_step' and 'val_step'. - - Note that the arguments of this wrapper is the same as those in - `torch.nn.parallel.distributed.DistributedDataParallel`. - - Args: - module (nn.Module): Module that needs to be wrapped. - device_ids (list[int | `torch.device`]): Same as that in - `torch.nn.parallel.distributed.DistributedDataParallel`. - dim (int, optional): Same as that in the official scatter function in - pytorch. Defaults to 0. - broadcast_buffers (bool): Same as that in - `torch.nn.parallel.distributed.DistributedDataParallel`. - Defaults to False. - find_unused_parameters (bool, optional): Same as that in - `torch.nn.parallel.distributed.DistributedDataParallel`. - Traverse the autograd graph of all tensors contained in returned - value of the wrapped module’s forward function. Defaults to False. - kwargs (dict): Other arguments used in - `torch.nn.parallel.distributed.DistributedDataParallel`. - """ - - def __init__(self, - module, - device_ids, - dim=0, - broadcast_buffers=False, - find_unused_parameters=False, - **kwargs): - super().__init__() - assert len(device_ids) == 1, ( - 'Currently, DistributedDataParallelWrapper only supports one' - 'single CUDA device for each process.' - f'The length of device_ids must be 1, but got {len(device_ids)}.') - self.module = module - self.dim = dim - self.to_ddp( - device_ids=device_ids, - dim=dim, - broadcast_buffers=broadcast_buffers, - find_unused_parameters=find_unused_parameters, - **kwargs) - self.output_device = _get_device_index(device_ids[0], True) - - def to_ddp(self, device_ids, dim, broadcast_buffers, - find_unused_parameters, **kwargs): - """Wrap models with separate MMDistributedDataParallel. - - It only wraps the modules with parameters. - """ - for name, module in self.module._modules.items(): - if next(module.parameters(), None) is None: - module = module.cuda() - elif all(not p.requires_grad for p in module.parameters()): - module = module.cuda() - else: - module = MMDistributedDataParallel( - module.cuda(), - device_ids=device_ids, - dim=dim, - broadcast_buffers=broadcast_buffers, - find_unused_parameters=find_unused_parameters, - **kwargs) - self.module._modules[name] = module - - def scatter(self, inputs, kwargs, device_ids): - """Scatter function. - - Args: - inputs (Tensor): Input Tensor. - kwargs (dict): Args for - ``mmcv.parallel.scatter_gather.scatter_kwargs``. - device_ids (int): Device id. - """ - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - """Forward function. - - Args: - inputs (tuple): Input data. - kwargs (dict): Args for - ``mmcv.parallel.scatter_gather.scatter_kwargs``. - """ - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - """Train step function. - - Args: - inputs (Tensor): Input Tensor. - kwargs (dict): Args for - ``mmcv.parallel.scatter_gather.scatter_kwargs``. - """ - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - """Validation step function. - - Args: - inputs (tuple): Input data. - kwargs (dict): Args for ``scatter_kwargs``. - """ - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/__init__.py deleted file mode 100755 index 5294618cf..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .eval_hooks import DistEvalIterHook, EvalIterHook -from .metrics import (L1Evaluation, connectivity, gradient_error, mae, mse, - niqe, psnr, reorder_image, sad, ssim) - -__all__ = [ - 'mse', 'sad', 'psnr', 'reorder_image', 'ssim', 'EvalIterHook', - 'DistEvalIterHook', 'L1Evaluation', 'gradient_error', 'connectivity', - 'niqe', 'mae' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/eval_hooks.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/eval_hooks.py deleted file mode 100755 index bda0f846c..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/eval_hooks.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.runner import Hook -from torch.utils.data import DataLoader - - -class EvalIterHook(Hook): - """Non-Distributed evaluation hook for iteration-based runner. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader. - interval (int): Evaluation interval. Default: 1. - eval_kwargs (dict): Other eval kwargs. It contains: - save_image (bool): Whether to save image. - save_path (str): The path to save image. - """ - - def __init__(self, dataloader, interval=1, **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError('dataloader must be a pytorch DataLoader, ' - f'but got { type(dataloader)}') - self.dataloader = dataloader - self.interval = interval - self.eval_kwargs = eval_kwargs - self.save_image = self.eval_kwargs.pop('save_image', False) - self.save_path = self.eval_kwargs.pop('save_path', None) - - def after_train_iter(self, runner): - """The behavior after each train iteration. - - Args: - runner (``mmcv.runner.BaseRunner``): The runner. - """ - if not self.every_n_iters(runner, self.interval): - return - runner.log_buffer.clear() - from mmedit.apis import single_gpu_test - results = single_gpu_test( - runner.model, - self.dataloader, - save_image=self.save_image, - save_path=self.save_path, - iteration=runner.iter) - self.evaluate(runner, results) - - def evaluate(self, runner, results): - """Evaluation function. - - Args: - runner (``mmcv.runner.BaseRunner``): The runner. - results (dict): Model forward results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - # call `after_val_epoch` after evaluation. - # This is a hack. - # Because epoch does not naturally exist In IterBasedRunner, - # thus we consider the end of an evluation as the end of an epoch. - # With this hack , we can support epoch based hooks. - if 'iter' in runner.__class__.__name__.lower(): - runner.call_hook('after_val_epoch') - - -class DistEvalIterHook(EvalIterHook): - """Distributed evaluation hook. - - Args: - dataloader (DataLoader): A PyTorch dataloader. - interval (int): Evaluation interval. Default: 1. - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - eval_kwargs (dict): Other eval kwargs. It may contain: - save_image (bool): Whether save image. - save_path (str): The path to save image. - """ - - def __init__(self, - dataloader, - interval=1, - gpu_collect=False, - **eval_kwargs): - super().__init__(dataloader, interval, **eval_kwargs) - self.gpu_collect = gpu_collect - - def after_train_iter(self, runner): - """The behavior after each train iteration. - - Args: - runner (``mmcv.runner.BaseRunner``): The runner. - """ - if not self.every_n_iters(runner, self.interval): - return - runner.log_buffer.clear() - from mmedit.apis import multi_gpu_test - results = multi_gpu_test( - runner.model, - self.dataloader, - tmpdir=osp.join(runner.work_dir, '.eval_hook'), - gpu_collect=self.gpu_collect, - save_image=self.save_image, - save_path=self.save_path, - iteration=runner.iter) - if runner.rank == 0: - print('\n') - self.evaluate(runner, results) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/metric_utils.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/metric_utils.py deleted file mode 100755 index ec6017678..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/metric_utils.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def gaussian(x, sigma): - """Gaussian function. - - Args: - x (array_like): The independent variable. - sigma (float): Standard deviation of the gaussian function. - - Return: - ndarray or scalar: Gaussian value of `x`. - """ - return np.exp(-x**2 / (2 * sigma**2)) / (sigma * np.sqrt(2 * np.pi)) - - -def dgaussian(x, sigma): - """Gradient of gaussian. - - Args: - x (array_like): The independent variable. - sigma (float): Standard deviation of the gaussian function. - - Return: - ndarray or scalar: Gradient of gaussian of `x`. - """ - return -x * gaussian(x, sigma) / sigma**2 - - -def gauss_filter(sigma, epsilon=1e-2): - """Gradient of gaussian. - - Args: - sigma (float): Standard deviation of the gaussian kernel. - epsilon (float): Small value used when calculating kernel size. - Default: 1e-2. - - Return: - tuple[ndarray]: Gaussian filter along x and y axis. - """ - half_size = np.ceil( - sigma * np.sqrt(-2 * np.log(np.sqrt(2 * np.pi) * sigma * epsilon))) - size = int(2 * half_size + 1) - - # create filter in x axis - filter_x = np.zeros((size, size)) - for i in range(size): - for j in range(size): - filter_x[i, j] = gaussian(i - half_size, sigma) * dgaussian( - j - half_size, sigma) - - # normalize filter - norm = np.sqrt((filter_x**2).sum()) - filter_x = filter_x / norm - filter_y = np.transpose(filter_x) - - return filter_x, filter_y - - -def gauss_gradient(img, sigma): - """Gaussian gradient. - - From https://www.mathworks.com/matlabcentral/mlc-downloads/downloads/ - submissions/8060/versions/2/previews/gaussgradient/gaussgradient.m/ - index.html - - Args: - img (ndarray): Input image. - sigma (float): Standard deviation of the gaussian kernel. - - Return: - ndarray: Gaussian gradient of input `img`. - """ - filter_x, filter_y = gauss_filter(sigma) - img_filtered_x = cv2.filter2D( - img, -1, filter_x, borderType=cv2.BORDER_REPLICATE) - img_filtered_y = cv2.filter2D( - img, -1, filter_y, borderType=cv2.BORDER_REPLICATE) - return np.sqrt(img_filtered_x**2 + img_filtered_y**2) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/metrics.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/metrics.py deleted file mode 100755 index 9e37dbe1a..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/metrics.py +++ /dev/null @@ -1,572 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import cv2 -import mmcv -import numpy as np -from scipy.ndimage import convolve -from scipy.special import gamma - -from mmedit.datasets.pipelines.matlab_like_resize import MATLABLikeResize -from .metric_utils import gauss_gradient - - -def sad(alpha, trimap, pred_alpha): - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - assert (pred_alpha[trimap == 0] == 0).all() - assert (pred_alpha[trimap == 255] == 255).all() - alpha = alpha.astype(np.float64) / 255 - pred_alpha = pred_alpha.astype(np.float64) / 255 - sad_result = np.abs(pred_alpha - alpha).sum() / 1000 - return sad_result - - -def mse(alpha, trimap, pred_alpha): - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - assert (pred_alpha[trimap == 0] == 0).all() - assert (pred_alpha[trimap == 255] == 255).all() - alpha = alpha.astype(np.float64) / 255 - pred_alpha = pred_alpha.astype(np.float64) / 255 - weight_sum = (trimap == 128).sum() - if weight_sum != 0: - mse_result = ((pred_alpha - alpha)**2).sum() / weight_sum - else: - mse_result = 0 - return mse_result - - -def gradient_error(alpha, trimap, pred_alpha, sigma=1.4): - """Gradient error for evaluating alpha matte prediction. - - Args: - alpha (ndarray): Ground-truth alpha matte. - trimap (ndarray): Input trimap with its value in {0, 128, 255}. - pred_alpha (ndarray): Predicted alpha matte. - sigma (float): Standard deviation of the gaussian kernel. Default: 1.4. - """ - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - if not ((pred_alpha[trimap == 0] == 0).all() and - (pred_alpha[trimap == 255] == 255).all()): - raise ValueError( - 'pred_alpha should be masked by trimap before evaluation') - alpha = alpha.astype(np.float64) - pred_alpha = pred_alpha.astype(np.float64) - alpha_normed = np.zeros_like(alpha) - pred_alpha_normed = np.zeros_like(pred_alpha) - cv2.normalize(alpha, alpha_normed, 1., 0., cv2.NORM_MINMAX) - cv2.normalize(pred_alpha, pred_alpha_normed, 1., 0., cv2.NORM_MINMAX) - - alpha_grad = gauss_gradient(alpha_normed, sigma).astype(np.float32) - pred_alpha_grad = gauss_gradient(pred_alpha_normed, - sigma).astype(np.float32) - - grad_loss = ((alpha_grad - pred_alpha_grad)**2 * (trimap == 128)).sum() - # same as SAD, divide by 1000 to reduce the magnitude of the result - return grad_loss / 1000 - - -def connectivity(alpha, trimap, pred_alpha, step=0.1): - """Connectivity error for evaluating alpha matte prediction. - - Args: - alpha (ndarray): Ground-truth alpha matte with shape (height, width). - Value range of alpha is [0, 255]. - trimap (ndarray): Input trimap with shape (height, width). Elements - in trimap are one of {0, 128, 255}. - pred_alpha (ndarray): Predicted alpha matte with shape (height, width). - Value range of pred_alpha is [0, 255]. - step (float): Step of threshold when computing intersection between - `alpha` and `pred_alpha`. - """ - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - if not ((pred_alpha[trimap == 0] == 0).all() and - (pred_alpha[trimap == 255] == 255).all()): - raise ValueError( - 'pred_alpha should be masked by trimap before evaluation') - alpha = alpha.astype(np.float32) / 255 - pred_alpha = pred_alpha.astype(np.float32) / 255 - - thresh_steps = np.arange(0, 1 + step, step) - round_down_map = -np.ones_like(alpha) - for i in range(1, len(thresh_steps)): - alpha_thresh = alpha >= thresh_steps[i] - pred_alpha_thresh = pred_alpha >= thresh_steps[i] - intersection = (alpha_thresh & pred_alpha_thresh).astype(np.uint8) - - # connected components - _, output, stats, _ = cv2.connectedComponentsWithStats( - intersection, connectivity=4) - # start from 1 in dim 0 to exclude background - size = stats[1:, -1] - - # largest connected component of the intersection - omega = np.zeros_like(alpha) - if len(size) != 0: - max_id = np.argmax(size) - # plus one to include background - omega[output == max_id + 1] = 1 - - mask = (round_down_map == -1) & (omega == 0) - round_down_map[mask] = thresh_steps[i - 1] - round_down_map[round_down_map == -1] = 1 - - alpha_diff = alpha - round_down_map - pred_alpha_diff = pred_alpha - round_down_map - # only calculate difference larger than or equal to 0.15 - alpha_phi = 1 - alpha_diff * (alpha_diff >= 0.15) - pred_alpha_phi = 1 - pred_alpha_diff * (pred_alpha_diff >= 0.15) - - connectivity_error = np.sum( - np.abs(alpha_phi - pred_alpha_phi) * (trimap == 128)) - # same as SAD, divide by 1000 to reduce the magnitude of the result - return connectivity_error / 1000 - - -def reorder_image(img, input_order='HWC'): - """Reorder images to 'HWC' order. - - If the input_order is (h, w), return (h, w, 1); - If the input_order is (c, h, w), return (h, w, c); - If the input_order is (h, w, c), return as it is. - - Args: - img (ndarray): Input image. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - If the input image shape is (h, w), input_order will not have - effects. Default: 'HWC'. - - Returns: - ndarray: reordered image. - """ - - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - if len(img.shape) == 2: - img = img[..., None] - return img - if input_order == 'CHW': - img = img.transpose(1, 2, 0) - return img - - -def psnr(img1, img2, crop_border=0, input_order='HWC', convert_to=None): - """Calculate PSNR (Peak Signal-to-Noise Ratio). - - Ref: https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio - - Args: - img1 (ndarray): Images with range [0, 255]. - img2 (ndarray): Images with range [0, 255]. - crop_border (int): Cropped pixels in each edges of an image. These - pixels are not involved in the PSNR calculation. Default: 0. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether to convert the images to other color models. - If None, the images are not altered. When computing for 'Y', - the images are assumed to be in BGR order. Options are 'Y' and - None. Default: None. - - Returns: - float: psnr result. - """ - - assert img1.shape == img2.shape, ( - f'Image shapes are different: {img1.shape}, {img2.shape}.') - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - img1 = reorder_image(img1, input_order=input_order) - img2 = reorder_image(img2, input_order=input_order) - - img1, img2 = img1.astype(np.float32), img2.astype(np.float32) - if isinstance(convert_to, str) and convert_to.lower() == 'y': - img1 = mmcv.bgr2ycbcr(img1 / 255., y_only=True) * 255. - img2 = mmcv.bgr2ycbcr(img2 / 255., y_only=True) * 255. - elif convert_to is not None: - raise ValueError('Wrong color model. Supported values are ' - '"Y" and None.') - - if crop_border != 0: - img1 = img1[crop_border:-crop_border, crop_border:-crop_border, None] - img2 = img2[crop_border:-crop_border, crop_border:-crop_border, None] - - mse_value = np.mean((img1 - img2)**2) - if mse_value == 0: - return float('inf') - return 20. * np.log10(255. / np.sqrt(mse_value)) - - -def mae(img1, img2, crop_border=0, input_order='HWC', convert_to=None): - """Calculate mean average error for evaluation. - - Args: - img1 (ndarray): Images with range [0, 255]. - img2 (ndarray): Images with range [0, 255]. - crop_border (int): Cropped pixels in each edges of an image. These - pixels are not involved in the PSNR calculation. Default: 0. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether to convert the images to other color models. - If None, the images are not altered. Options are 'RGB2Y', 'BGR2Y' - and None. Default: None. - - Returns: - float: mae result. - """ - - assert img1.shape == img2.shape, ( - f'Image shapes are different: {img1.shape}, {img2.shape}.') - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - img1 = reorder_image(img1, input_order=input_order) - img2 = reorder_image(img2, input_order=input_order) - - img1, img2 = img1.astype(np.float32), img2.astype(np.float32) - img1, img2 = img1 / 255., img2 / 255. - if isinstance(convert_to, str) and convert_to.lower() == 'rgb2y': - img1 = mmcv.rgb2ycbcr(img1, y_only=True) - img2 = mmcv.rgb2ycbcr(img2, y_only=True) - elif isinstance(convert_to, str) and convert_to.lower() == 'bgr2y': - img1 = mmcv.bgr2ycbcr(img1, y_only=True) - img2 = mmcv.bgr2ycbcr(img2, y_only=True) - elif convert_to is not None: - raise ValueError('Wrong color model. Supported values are ' - '"RGB2Y", "BGR2Y" and None.') - - if crop_border != 0: - img1 = img1[crop_border:-crop_border, crop_border:-crop_border, None] - img2 = img2[crop_border:-crop_border, crop_border:-crop_border, None] - - l1_value = np.mean(np.abs(img1 - img2)) - - return l1_value - - -def _ssim(img1, img2): - """Calculate SSIM (structural similarity) for one channel images. - - It is called by func:`calculate_ssim`. - - Args: - img1, img2 (ndarray): Images with range [0, 255] with order 'HWC'. - - Returns: - float: ssim result. - """ - - C1 = (0.01 * 255)**2 - C2 = (0.03 * 255)**2 - - img1 = img1.astype(np.float64) - img2 = img2.astype(np.float64) - kernel = cv2.getGaussianKernel(11, 1.5) - window = np.outer(kernel, kernel.transpose()) - - mu1 = cv2.filter2D(img1, -1, window)[5:-5, 5:-5] - mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5] - mu1_sq = mu1**2 - mu2_sq = mu2**2 - mu1_mu2 = mu1 * mu2 - sigma1_sq = cv2.filter2D(img1**2, -1, window)[5:-5, 5:-5] - mu1_sq - sigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sq - sigma12 = cv2.filter2D(img1 * img2, -1, window)[5:-5, 5:-5] - mu1_mu2 - - ssim_map = ((2 * mu1_mu2 + C1) * - (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) * - (sigma1_sq + sigma2_sq + C2)) - return ssim_map.mean() - - -def ssim(img1, img2, crop_border=0, input_order='HWC', convert_to=None): - """Calculate SSIM (structural similarity). - - Ref: - Image quality assessment: From error visibility to structural similarity - - The results are the same as that of the official released MATLAB code in - https://ece.uwaterloo.ca/~z70wang/research/ssim/. - - For three-channel images, SSIM is calculated for each channel and then - averaged. - - Args: - img1 (ndarray): Images with range [0, 255]. - img2 (ndarray): Images with range [0, 255]. - crop_border (int): Cropped pixels in each edges of an image. These - pixels are not involved in the SSIM calculation. Default: 0. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether to convert the images to other color models. - If None, the images are not altered. When computing for 'Y', - the images are assumed to be in BGR order. Options are 'Y' and - None. Default: None. - - Returns: - float: ssim result. - """ - - assert img1.shape == img2.shape, ( - f'Image shapes are different: {img1.shape}, {img2.shape}.') - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - img1 = reorder_image(img1, input_order=input_order) - img2 = reorder_image(img2, input_order=input_order) - - if isinstance(convert_to, str) and convert_to.lower() == 'y': - img1, img2 = img1.astype(np.float32), img2.astype(np.float32) - img1 = mmcv.bgr2ycbcr(img1 / 255., y_only=True) * 255. - img2 = mmcv.bgr2ycbcr(img2 / 255., y_only=True) * 255. - img1 = np.expand_dims(img1, axis=2) - img2 = np.expand_dims(img2, axis=2) - elif convert_to is not None: - raise ValueError('Wrong color model. Supported values are ' - '"Y" and None') - - if crop_border != 0: - img1 = img1[crop_border:-crop_border, crop_border:-crop_border, None] - img2 = img2[crop_border:-crop_border, crop_border:-crop_border, None] - - ssims = [] - for i in range(img1.shape[2]): - ssims.append(_ssim(img1[..., i], img2[..., i])) - return np.array(ssims).mean() - - -class L1Evaluation: - """L1 evaluation metric. - - Args: - data_dict (dict): Must contain keys of 'gt_img' and 'fake_res'. If - 'mask' is given, the results will be computed with mask as weight. - """ - - def __call__(self, data_dict): - gt = data_dict['gt_img'] - if 'fake_img' in data_dict: - pred = data_dict.get('fake_img') - else: - pred = data_dict.get('fake_res') - mask = data_dict.get('mask', None) - - from mmedit.models.losses.pixelwise_loss import l1_loss - l1_error = l1_loss(pred, gt, weight=mask, reduction='mean') - - return l1_error - - -def estimate_aggd_param(block): - """Estimate AGGD (Asymmetric Generalized Gaussian Distribution) parameters. - - Args: - block (ndarray): 2D Image block. - - Returns: - tuple: alpha (float), beta_l (float) and beta_r (float) for the AGGD - distribution (Estimating the parames in Equation 7 in the paper). - """ - block = block.flatten() - gam = np.arange(0.2, 10.001, 0.001) # len = 9801 - gam_reciprocal = np.reciprocal(gam) - r_gam = np.square(gamma(gam_reciprocal * 2)) / ( - gamma(gam_reciprocal) * gamma(gam_reciprocal * 3)) - - left_std = np.sqrt(np.mean(block[block < 0]**2)) - right_std = np.sqrt(np.mean(block[block > 0]**2)) - gammahat = left_std / right_std - rhat = (np.mean(np.abs(block)))**2 / np.mean(block**2) - rhatnorm = (rhat * (gammahat**3 + 1) * - (gammahat + 1)) / ((gammahat**2 + 1)**2) - array_position = np.argmin((r_gam - rhatnorm)**2) - - alpha = gam[array_position] - beta_l = left_std * np.sqrt(gamma(1 / alpha) / gamma(3 / alpha)) - beta_r = right_std * np.sqrt(gamma(1 / alpha) / gamma(3 / alpha)) - return (alpha, beta_l, beta_r) - - -def compute_feature(block): - """Compute features. - - Args: - block (ndarray): 2D Image block. - - Returns: - list: Features with length of 18. - """ - feat = [] - alpha, beta_l, beta_r = estimate_aggd_param(block) - feat.extend([alpha, (beta_l + beta_r) / 2]) - - # distortions disturb the fairly regular structure of natural images. - # This deviation can be captured by analyzing the sample distribution of - # the products of pairs of adjacent coefficients computed along - # horizontal, vertical and diagonal orientations. - shifts = [[0, 1], [1, 0], [1, 1], [1, -1]] - for shift in shifts: - shifted_block = np.roll(block, shift, axis=(0, 1)) - alpha, beta_l, beta_r = estimate_aggd_param(block * shifted_block) - mean = (beta_r - beta_l) * (gamma(2 / alpha) / gamma(1 / alpha)) - feat.extend([alpha, mean, beta_l, beta_r]) - return feat - - -def niqe_core(img, - mu_pris_param, - cov_pris_param, - gaussian_window, - block_size_h=96, - block_size_w=96): - """Calculate NIQE (Natural Image Quality Evaluator) metric. - - Ref: Making a "Completely Blind" Image Quality Analyzer. - This implementation could produce almost the same results as the official - MATLAB codes: http://live.ece.utexas.edu/research/quality/niqe_release.zip - - Note that we do not include block overlap height and width, since they are - always 0 in the official implementation. - - For good performance, it is advisable by the official implementation to - divide the distorted image in to the same size patched as used for the - construction of multivariate Gaussian model. - - Args: - img (ndarray): Input image whose quality needs to be computed. The - image must be a gray or Y (of YCbCr) image with shape (h, w). - Range [0, 255] with float type. - mu_pris_param (ndarray): Mean of a pre-defined multivariate Gaussian - model calculated on the pristine dataset. - cov_pris_param (ndarray): Covariance of a pre-defined multivariate - Gaussian model calculated on the pristine dataset. - gaussian_window (ndarray): A 7x7 Gaussian window used for smoothing the - image. - block_size_h (int): Height of the blocks in to which image is divided. - Default: 96 (the official recommended value). - block_size_w (int): Width of the blocks in to which image is divided. - Default: 96 (the official recommended value). - """ - # crop image - h, w = img.shape - num_block_h = math.floor(h / block_size_h) - num_block_w = math.floor(w / block_size_w) - img = img[0:num_block_h * block_size_h, 0:num_block_w * block_size_w] - - distparam = [] # dist param is actually the multiscale features - for scale in (1, 2): # perform on two scales (1, 2) - mu = convolve(img, gaussian_window, mode='nearest') - - sigma = np.sqrt( - np.abs( - convolve(np.square(img), gaussian_window, mode='nearest') - - np.square(mu))) - # normalize, as in Eq. 1 in the paper - img_nomalized = (img - mu) / (sigma + 1) - - feat = [] - for idx_w in range(num_block_w): - for idx_h in range(num_block_h): - # process each block - block = img_nomalized[idx_h * block_size_h // - scale:(idx_h + 1) * block_size_h // - scale, idx_w * block_size_w // - scale:(idx_w + 1) * block_size_w // - scale] - feat.append(compute_feature(block)) - - distparam.append(np.array(feat)) - - # matlab-like bicubic downsample with anti-aliasing - if scale == 1: - resize = MATLABLikeResize(keys=None, scale=0.5) - img = resize._resize(img[:, :, np.newaxis] / 255.)[:, :, 0] * 255. - - distparam = np.concatenate(distparam, axis=1) - - # fit a MVG (multivariate Gaussian) model to distorted patch features - mu_distparam = np.nanmean(distparam, axis=0) - distparam_no_nan = distparam[~np.isnan(distparam).any(axis=1)] - cov_distparam = np.cov(distparam_no_nan, rowvar=False) - - # compute niqe quality, Eq. 10 in the paper - invcov_param = np.linalg.pinv((cov_pris_param + cov_distparam) / 2) - quality = np.matmul( - np.matmul((mu_pris_param - mu_distparam), invcov_param), - np.transpose((mu_pris_param - mu_distparam))) - - return np.squeeze(np.sqrt(quality)) - - -def niqe(img, crop_border, input_order='HWC', convert_to='y'): - """Calculate NIQE (Natural Image Quality Evaluator) metric. - - Ref: Making a "Completely Blind" Image Quality Analyzer. - This implementation could produce almost the same results as the official - MATLAB codes: http://live.ece.utexas.edu/research/quality/niqe_release.zip - - We use the official params estimated from the pristine dataset. - We use the recommended block size (96, 96) without overlaps. - - Args: - img (ndarray): Input image whose quality needs to be computed. - The input image must be in range [0, 255] with float/int type. - The input_order of image can be 'HW' or 'HWC' or 'CHW'. (BGR order) - If the input order is 'HWC' or 'CHW', it will be converted to gray - or Y (of YCbCr) image according to the ``convert_to`` argument. - crop_border (int): Cropped pixels in each edge of an image. These - pixels are not involved in the metric calculation. - input_order (str): Whether the input order is 'HW', 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether converted to 'y' (of MATLAB YCbCr) or 'gray'. - Default: 'y'. - - Returns: - float: NIQE result. - """ - - # we use the official params estimated from the pristine dataset. - niqe_pris_params = np.load('mmedit/core/evaluation/niqe_pris_params.npz') - mu_pris_param = niqe_pris_params['mu_pris_param'] - cov_pris_param = niqe_pris_params['cov_pris_param'] - gaussian_window = niqe_pris_params['gaussian_window'] - - img = img.astype(np.float32) - if input_order != 'HW': - img = reorder_image(img, input_order=input_order) - if convert_to == 'y': - img = mmcv.bgr2ycbcr(img / 255., y_only=True) * 255. - elif convert_to == 'gray': - img = mmcv.bgr2gray(img / 255., cv2.COLOR_BGR2GRAY) * 255. - img = np.squeeze(img) - - if crop_border != 0: - img = img[crop_border:-crop_border, crop_border:-crop_border] - - # round to follow official implementation - img = img.round() - - niqe_result = niqe_core(img, mu_pris_param, cov_pris_param, - gaussian_window) - - return niqe_result diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/niqe_pris_params.npz b/cv/super_resolution/esrgan/pytorch/mmedit/core/evaluation/niqe_pris_params.npz deleted file mode 100755 index 204ddcee87c4cd39aca04a42b539f0a5bfccecc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11850 zcmbt)cUaH;+jr3=DyzX|m!hnqC9AVZMABZ`oA%y&X;D&HMUqmXgpjuqEi+{lC3KOH zvO@CYcVDlL`*+><^T+f2zK%G)KA+F`=s3UU`8r?Yt*tqhOOWHgULqW0eZL;d<>cV_ z{WFzAki*5t!rjx!%fj8t)5=BC)jfcdW6HlS{(j*1O}}61TKZeGjX61dIfBG(?YwL} z#a9c9ueDbYUn(qa@8;?4Y2|9+=4os9`~TNl?ewx6`F}4*D|fq*_Yy0X3d_hZS}GhO z{QvvnnI`=;G2{-HlBB`x_0d1xQg?O8QO%(qnsX|_U3TGla@yMO{j0i~EZ13=saQ19 zs$Ija!?JJad3blt(Dzf6o4IXBA@>?hy~l6iD$qc}-_4DDJ>QUV{rspT(fzE#WmAQm zXg&R`|LdE$!gbQGerum;`G!JI?(dQr%w|tgvUZDpipczx02yA zzneR2-cxJ-d%+jWdTD-fMhoBc3VJ4AyhiGHEy=i5$!~4ENn?ficteAG$>j5Eo?kM> z7DnTQ4dnDn}rvW|bKT@{wg)=4h9w>YDT)_R!DHJe^f&obA< z&S~zYiU8|hAwA=4Gn!hTB&h@&)bYvErN)GvQ)U*g1Q7XExe z`||h_;>fYBHaT{jE^V&f*>I(q1pUq&HXJ%l3bpR}?TtlL(%QMb;EOAj42_wmE_j5Z zzq$3!OU|crzZRu)brn%H*9C_gnx5>Nq>WIGaUR*m8H6hMy-ACe6_nob+v?2Z{d&fCCCX-~=>-`b8)5*cpIw-|{AK5+ssC)6O zHjDYp-|o(}hvYsxu5Ep{hf3y`n3})LpnXdgrfRSDVG~}QKY9FUIyv9nAbNXQ3eDdT zIVRaJgBt62+Gx2q>)e~!W0jXq^6zz8-qao^qUwx1Tz&JvfW@Muvu5_DrA+ zYE)2}t;}wYskAO|iKCrWaV^1W33Q;O^XJW1@pM%_F6o7lC7U$l;pF)>o|GbLoSyBA zqdQYSM>+?`Qq7c#7RfMI=Fy=zJT$b6Ottn~?b{tin;$<+N{opi<-v||%^}Y0WQC0` zx5X|};(RT-<9O6)zo0JXBUWs_Cz3{eb2sIOggVl?MWxC!<044-*h_UCi6klv5i7D< zn?@mbhP?H19w_q1U;cEyCWe;ry=+Tf5>Mqyp+|)>_fX`=-Q!)KTCrPxX}J=-2^24& z{?`oCXeys5D6X?@4-K{^j&HnR$r}E2-QB~VK)!tH7iN`3(U8*u{qgaIwENYZzhu6r zke8f_cJ)v!#hq5V6w0xZPI;7f&3GL~d0DesjV44<(Ljr4eM2k_E>PU);Ix(ro#^KH zaVwncn)j`Y?v0=)`?jd@B}UWxguH6?Jafj+OYOYJB8d&Nw5*z78WU5pslO?TWIh?M zTyx)q#ogkm;NB5Q8`bv9-Ea>cog?%M>T=gfan)XNciJMYQhO{+iP>0r{ygC9L`Hmj z+a zRO8@wT?r+YV>ZiD-`bLPzPR2uvE5)4Uoc1L7u4l@k}uE2i$V1Eokhd&+fXV!w(8E5 z;&7UoR;OH5CQV5j3vRBi3M9`ftM{dz3Z*p`vRPL@hS8hij1TuSELrmBl*BWf;WR6M zwab`ve;O0k*p{>;jGWJ>UD=~(#Rl$3HBS69oW8t|s+n2uN9&BObogQuX?#hyTgYGp z-HcW9zTo3Ucg#3)=O!AkU~awG)8*bYba|b0!5^N~JI!YHy&vv$@PNk8_0mF2!?p0i zj4BU0cEa&xy{H$}Z%)Z*H*}%bO=%|&>FBa^y4g7<_r2&@+P6tk(ymnPZ6Nh|)=m-= zOLe}Vs>_t-_8q)b=S4S~PKb(LcNxW9;0xvm{err*1(qwmS56>}{*TLZHIhjnFj&Fg zL7&}Q(#dmYNeuZVC?-k7M3a`7v&}q@M2d9LeyEsX%dD%$*>rx2rL2UWxus_!XhW#v z<(JbF$t?Ze_Q}5X?959+9{w$HRQ9qzTj)hNO}B3iwbnjNio+9lhh2Bk%JN5KW)Mz; zwUaHpG}TElS7=`Tl>kzEc6sRT^)PZ-6m2MSJ)CAASSOv|V#1V;@mqV{4JDDqA~O9+ zLG-|&D%yKZ2;I!w`qO`#1>=#oD-ul%BP-pD1FO{h$c`md8b}Az`PViF+qf*)(gtts z@AhHDUEC_8`ptKA9|3oPFPJ0r3+l4ly^M23VLH8OA5W7e=&;ig3l<-}8b@Qce=py< zHJ-RmnV9)mr%}P3v{K$$8x~jA@RaXk0%fjX3!TnHQ=7-td0z9+`S=m zyMni2+Wjaxr7>n1w|zV<ztwS z#*!sQI~K|Q8AUyF-I@gpg6Vyo`Z%$cXsWGK`1Qv-6Q(h3N~!qqNV0a85P$kOcyv!= z9|3oPFPJ0r3+gh?`a55WryFID^>?_t!H)e|74IRXrA|jWX1DY`Q)PquXKejxXh2gc zO+&dZ>#?(ACYx+j7iFn2PlmQ}sWa2unA1(oMr1Yq_A$PELzY;S^q{JXgRZFvL(Cgv--B0q3*xjU8QHJ)$^zKBG7MF|+1<1_|U38n1^(}d6 zK3>$0_UZ;icgwA3)9vE(g$GTj^zf7K-jz!!;dtT4#j94)W(NhX*6wu_KaaoUhVW+k zk$mEZkeL{r*c_s(OpD3aS*rZ#3>6xvJF6QIw1L21u&1$)fV;pK%n|wpbvfMQFme0M zC<^GYdnFeVPAv-S+>|0?NP#n~v_js2ndV#Z7R?{|e$1_F+9!n2pOPz&ipa*$?DB3$ zttrl|M|(zzcWyM9Z%Hy-xiN@*B>U_pf7wgk<_m>4eTbqBj*{Fbl>O+sLQ3bQ32M~y zre@;ubDm_kTKCHGbAGh?+`7Cqd;O`ua`vuC=dIb|{_ch6^aIG|M8(9mr=H|JQBtMx zPaj%dJ9mT0cr(@&-rBD>)t|zR%a%>K=uRg&jMg^0dDGezYnGayH(?)}CO^}@;78*^ zo4faQjO?esc^&u*_B8epa2NQ3IYPgnE(TZPxSpm(QK*2-`-)j{G}P2K5M*q@?hJTu zPRWa++3^B7Z*#(E+@l;{xx=xf!6UUpwA!34b`L6(IS@rrDmrZkuZ2?1nyMVdMf<78 zLvDNZqj;)Uy%Jhb6+#jxpYKZEs>bfiDn&@_3Z(3TF9Sk5f{1vkLZ|BllWz6B%AsI! z_O5QBVWv+2&5$1I-x3o<&OzaVYp(gzd0yq-Z{0?0VK~nlKhHzRv@H+4p>}l*H z;4bh5bA*0DUCPuI<8=lcsl)r4@;EM6cJ9fV!yjYySjorspc9qW%w9u(gr=xbmC~SB z)(Sh8y1m}fsLz5mxw%$enxV#yC_0E)R_n74)6_RgCl#5v%L)2=Re}t1e*M~dMx9Nz zY<;L3tj>Ok9BNobs~E3A^FXKEHcERg*ekYNmra(>+1fO)gweuU!Ak!1Y)Zti`Mad; z^gV{_(5X*GOuNv_cG3g^sm8#Rm_}uaBppFej!y#-6p_ zW-}w5VyWQ9t)FKd!pUNJf1lKzY`VXv-aB!5JoSB!IPzlJ$oyodUG%NzVcMCyBo@i~ zQ_v=Z(gdR*+9zEmBbgjTiY0qD|MA_BO}<J`9)7VGAUEmAm2>pV(jGeyOQ>8A5#w))cQH{}5vj6l{F$XI)!F`rk z`j!Y1nxqx07#KjG45oxjf61hx$dcY+7EOP>OKEog=}&5_jVnLy=4X4#yZ>-f_n|V& z2d{P>_al>+UtfA%_NQi*o=0ZZD$Ff?z0v)pzGP|N;wl#EOL=cwuIwG_OKZ*+9^WLi zjdhhbc6{0GM^^I{(p~@XB730@5!W~$Dt~pkkDJSkiO!h*)%mSIopV%gPY?1K)zi?8 z&_~b#;Je^;;4j$I*hj!!;0xvm{errrW^`Duu}!Bb-$V6FPusDvs@*P{PZLO9d*)e* zgeZ#Zo?dm`qnH$=8@4$dO{NF_UzW-G#gf6<#%YFb`lK}Tp7x9pe0`bG`1taTND|ob zl~+$Bj>dgBG01M95VjN9{rS|&wN^vNV{RiB_yp89a21tOGKcUhqE z>%ITsnuavHaqy~b!QdvE za%ZvUn>1~v(7i>>Jx`A1d)|9HTw!ul7!}@ z<)+$9aIIa;uOAkazdCDE@FP>^p6b4K(~cik;J?6UfS!hKgg$}}0N(|#1AoDu#y$e> z0$(sk=oi$b@x5eceXfs#Y5}QrSFLP(SeI7?*iBcO2_mALAwpzi0 zpQ7y6RgQJ7V*d2RP~Y}ceIN0mD&Ghq#A2O*gy=>(=e~QiXU%anQh;;6Y^e)r#CKaQBs-GkJNyzY*-{3bt zirxNxw~eDJqgS6_+En?FPVj)$Qdk$7BOA9;v|0xo*3(- zVc|g!L&rZ%+u%(L3xC;*S8QRbyjh8t_V`l7jm;Z-R(g*5b?~L&zrbgJo`!COK7tMa z-vzG&f5D!{J_7CnUoc1L7t}?(jYG@$c@ODX_&qUvS4M}z{If)C&ePAT&a>+AzLX%v zAKCW(7-b6GQ`+8jl4LUu)$-52Kr7wf284|0@@9U&>73K@$+@b@!Km*feOk7a*W+0w z4embNzg5JH`J_rOiRdbVR)jYn(ZKBppgMZ-Cgsemo>gxR1cPN(nUfLcp@=l`Eg{Nz- zPPJpvfrp~?Y75hA zorZXlNi;=jvff)w=8*M{Q)I*!FzFT0Xx0uN^}XTm!pDSP2VV;Q3w#FXY3N4iBj^C| zUGO^a7wl>5Bj7IZ1#^UcL0t-R<0RDQxKi-^sVBM~nJ}^Ps`F#Xm?U0&ZO!+xr3OK< zIq5Rn*$c<6(2PxnCrj4Q_rN#PW`@a9g5}w}(|puM^8onX@OR;3!moob1^)#;1N1a> zBlHn;0QfF=9rz3OH1-j27x;oXLcgFc9|{DTJXJ#ILF-IA>%>5s>t;Du-6e!Lnm;S) z_2`gK^6n)mjXre9H?~VcKZNcV^`88EEO^8}xB8y?K$8XhjN>=l7DO|LFY%l?=tn<1 z&PK+L?7wrR8v6DHM(owZX{)>If@x-1lUDXBpV6EIc>sKG_`C2i;n%^Jg8u@a0eTv` z5&8%^0DKp`4*Ugs8v6*i3w*&Gp+{ z0KN-e2mXRRjeP{%1-@X8&@ZSteU>F@VA)BZI-92-BFFqz!B+>@s0k*59ileRJ{beVhX%tMWu*EwDe5#&lV7MGDEBrk3@bz(YMczWyW&Tn^-iTZc`4Msw z3RCX2bBp_q=CH^ck*gs;LQaA_0KPZ;UHF*r>)=ble}T^cJw2)$ z|JFy)0pPpfb>J`9)7VGAUEmAm2>pV(G-XKNk0L-jl#(~YCZ@$s#nPui6jJ-^d5 zPb!QgCuSsWa|@>YO^-J0jgO=iT8+{0KN-e2mXRRjeP{%1-@X8&@ZUVJ6$og zj;1}d;_{o*9VOb##`;*1cyKgDiND}};wISKLr_}=h$;bX$DgD(aD1wI4xG;|~M5p)3fE_faI z3-&bj5pWmyf;mFJpf0-?2TTu=wRj50 z-@LcvwbKC|_ONk&c!S?a9$7JGfrD${=s6FZXFzU`d>T0{@>0l6WgJ-8 zkNzWRS}RA-OW|A)&cEQC2hKAfw?{sW92R*aay8^f$Vrd~!1so~3m+4H9egSHFYpB4G3!8O}@LToBH` z;G74}Ga$D|K8+j}c_VT)%d>I zr?HQKyTBLB5&8vnF}$Ux-Yw_*k293zU$gwikKy3>{Ub1PhSI^x$IHuUz!e_h!Xm^{PL9%!{9nR`LwC2Qt(f7EH=kfWtKW>sn*S?lwvMqV! z=XCch;XSV7e*8Z8eB9svaO&986R(UukL$P}&*Ss(`FI}haUJ*L_r>pz>-c=U$93HQ z&-&o={`vd-^L}kj&M|^x|8J+R|Ep(y{~i9X1J?iJ4E8@C|MwHU|NZe7BOmZT&-ecG k`G4=`|NgxA5|00^x3x9@829_Ou_J%3j{NJr?DxC>1Ij;2^Z)<= diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/export/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/export/__init__.py deleted file mode 100755 index 6cf757d83..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/export/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .wrappers import ONNXRuntimeEditing - -__all__ = ['ONNXRuntimeEditing'] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/export/wrappers.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/export/wrappers.py deleted file mode 100755 index 5b4d55fbc..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/export/wrappers.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -import numpy as np -import onnxruntime as ort -import torch -from torch import nn - -from mmedit.models import BaseMattor, BasicRestorer, build_model - - -def inference_with_session(sess, io_binding, output_names, input_tensor): - device_type = input_tensor.device.type - device_id = input_tensor.device.index - device_id = 0 if device_id is None else device_id - io_binding.bind_input( - name='input', - device_type=device_type, - device_id=device_id, - element_type=np.float32, - shape=input_tensor.shape, - buffer_ptr=input_tensor.data_ptr()) - for name in output_names: - io_binding.bind_output(name) - sess.run_with_iobinding(io_binding) - pred = io_binding.copy_outputs_to_cpu() - return pred - - -class ONNXRuntimeMattor(nn.Module): - - def __init__(self, sess, io_binding, output_names, base_model): - super(ONNXRuntimeMattor, self).__init__() - self.sess = sess - self.io_binding = io_binding - self.output_names = output_names - self.base_model = base_model - - def forward(self, - merged, - trimap, - meta, - test_mode=False, - save_image=False, - save_path=None, - iteration=None): - input_tensor = torch.cat((merged, trimap), 1).contiguous() - pred_alpha = inference_with_session(self.sess, self.io_binding, - self.output_names, input_tensor)[0] - - pred_alpha = pred_alpha.squeeze() - pred_alpha = self.base_model.restore_shape(pred_alpha, meta) - eval_result = self.base_model.evaluate(pred_alpha, meta) - - if save_image: - self.base_model.save_image(pred_alpha, meta, save_path, iteration) - - return {'pred_alpha': pred_alpha, 'eval_result': eval_result} - - -class RestorerGenerator(nn.Module): - - def __init__(self, sess, io_binding, output_names): - super(RestorerGenerator, self).__init__() - self.sess = sess - self.io_binding = io_binding - self.output_names = output_names - - def forward(self, x): - pred = inference_with_session(self.sess, self.io_binding, - self.output_names, x)[0] - pred = torch.from_numpy(pred) - return pred - - -class ONNXRuntimeRestorer(nn.Module): - - def __init__(self, sess, io_binding, output_names, base_model): - super(ONNXRuntimeRestorer, self).__init__() - self.sess = sess - self.io_binding = io_binding - self.output_names = output_names - self.base_model = base_model - restorer_generator = RestorerGenerator(self.sess, self.io_binding, - self.output_names) - base_model.generator = restorer_generator - - def forward(self, lq, gt=None, test_mode=False, **kwargs): - return self.base_model(lq, gt=gt, test_mode=test_mode, **kwargs) - - -class ONNXRuntimeEditing(nn.Module): - - def __init__(self, onnx_file, cfg, device_id): - super(ONNXRuntimeEditing, self).__init__() - ort_custom_op_path = '' - try: - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - except (ImportError, ModuleNotFoundError): - warnings.warn('If input model has custom op from mmcv, \ - you may have to build mmcv with ONNXRuntime from source.') - session_options = ort.SessionOptions() - # register custom op for onnxruntime - if osp.exists(ort_custom_op_path): - session_options.register_custom_ops_library(ort_custom_op_path) - sess = ort.InferenceSession(onnx_file, session_options) - providers = ['CPUExecutionProvider'] - options = [{}] - is_cuda_available = ort.get_device() == 'GPU' - if is_cuda_available: - providers.insert(0, 'CUDAExecutionProvider') - options.insert(0, {'device_id': device_id}) - - sess.set_providers(providers, options) - - self.sess = sess - self.device_id = device_id - self.io_binding = sess.io_binding() - self.output_names = [_.name for _ in sess.get_outputs()] - - base_model = build_model( - cfg.model, train_cfg=None, test_cfg=cfg.test_cfg) - - if isinstance(base_model, BaseMattor): - WrapperClass = ONNXRuntimeMattor - elif isinstance(base_model, BasicRestorer): - WrapperClass = ONNXRuntimeRestorer - self.wrapper = WrapperClass(self.sess, self.io_binding, - self.output_names, base_model) - - def forward(self, **kwargs): - return self.wrapper(**kwargs) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/hooks/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/hooks/__init__.py deleted file mode 100755 index 575c43b35..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/hooks/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .ema import ExponentialMovingAverageHook -from .visualization import VisualizationHook - -__all__ = ['VisualizationHook', 'ExponentialMovingAverageHook'] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/hooks/ema.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/hooks/ema.py deleted file mode 100755 index 0e7f0b2e8..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/hooks/ema.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from copy import deepcopy -from functools import partial - -import mmcv -import torch -from mmcv.parallel import is_module_wrapper -from mmcv.runner import HOOKS, Hook - - -@HOOKS.register_module() -class ExponentialMovingAverageHook(Hook): - """Exponential Moving Average Hook. - - Exponential moving average is a trick that widely used in current GAN - literature, e.g., PGGAN, StyleGAN, and BigGAN. This general idea of it is - maintaining a model with the same architecture, but its parameters are - updated as a moving average of the trained weights in the original model. - In general, the model with moving averaged weights achieves better - performance. - - Args: - module_keys (str | tuple[str]): The name of the ema model. Note that we - require these keys are followed by '_ema' so that we can easily - find the original model by discarding the last four characters. - interp_mode (str, optional): Mode of the interpolation method. - Defaults to 'lerp'. - interp_cfg (dict | None, optional): Set arguments of the interpolation - function. Defaults to None. - interval (int, optional): Evaluation interval (by iterations). - Default: -1. - start_iter (int, optional): Start iteration for ema. If the start - iteration is not reached, the weights of ema model will maintain - the same as the original one. Otherwise, its parameters are updated - as a moving average of the trained weights in the original model. - Default: 0. - """ - - def __init__(self, - module_keys, - interp_mode='lerp', - interp_cfg=None, - interval=-1, - start_iter=0): - super().__init__() - assert isinstance(module_keys, str) or mmcv.is_tuple_of( - module_keys, str) - self.module_keys = (module_keys, ) if isinstance(module_keys, - str) else module_keys - # sanity check for the format of module keys - for k in self.module_keys: - assert k.endswith( - '_ema'), 'You should give keys that end with "_ema".' - self.interp_mode = interp_mode - self.interp_cfg = dict() if interp_cfg is None else deepcopy( - interp_cfg) - self.interval = interval - self.start_iter = start_iter - - assert hasattr( - self, interp_mode - ), f'Currently, we do not support {self.interp_mode} for EMA.' - self.interp_func = partial( - getattr(self, interp_mode), **self.interp_cfg) - - @staticmethod - def lerp(a, b, momentum=0.999, momentum_nontrainable=0., trainable=True): - m = momentum if trainable else momentum_nontrainable - return a + (b - a) * m - - def every_n_iters(self, runner, n): - if runner.iter < self.start_iter: - return True - return (runner.iter + 1 - self.start_iter) % n == 0 if n > 0 else False - - @torch.no_grad() - def after_train_iter(self, runner): - if not self.every_n_iters(runner, self.interval): - return - - model = runner.model.module if is_module_wrapper( - runner.model) else runner.model - - for key in self.module_keys: - # get current ema states - ema_net = getattr(model, key) - states_ema = ema_net.state_dict(keep_vars=False) - # get currently original states - net = getattr(model, key[:-4]) - states_orig = net.state_dict(keep_vars=True) - - for k, v in states_orig.items(): - if runner.iter < self.start_iter: - states_ema[k].data.copy_(v.data) - else: - states_ema[k] = self.interp_func( - v, states_ema[k], trainable=v.requires_grad).detach() - ema_net.load_state_dict(states_ema, strict=True) - - def before_run(self, runner): - model = runner.model.module if is_module_wrapper( - runner.model) else runner.model - # sanity check for ema model - for k in self.module_keys: - if not hasattr(model, k) and not hasattr(model, k[:-4]): - raise RuntimeError( - f'Cannot find both {k[:-4]} and {k} network for EMA hook.') - if not hasattr(model, k) and hasattr(model, k[:-4]): - setattr(model, k, deepcopy(getattr(model, k[:-4]))) - warnings.warn( - f'We do not suggest construct and initialize EMA model {k}' - ' in hook. You may explicitly define it by yourself.') diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/hooks/visualization.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/hooks/visualization.py deleted file mode 100755 index 63c8ee7d8..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/hooks/visualization.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import torch -from mmcv.runner import HOOKS, Hook -from mmcv.runner.dist_utils import master_only -from torchvision.utils import save_image - - -@HOOKS.register_module() -class VisualizationHook(Hook): - """Visualization hook. - - In this hook, we use the official api `save_image` in torchvision to save - the visualization results. - - Args: - output_dir (str): The file path to store visualizations. - res_name_list (str): The list contains the name of results in outputs - dict. The results in outputs dict must be a torch.Tensor with shape - (n, c, h, w). - interval (int): The interval of calling this hook. If set to -1, - the visualization hook will not be called. Default: -1. - filename_tmpl (str): Format string used to save images. The output file - name will be formatted as this args. Default: 'iter_{}.png'. - rerange (bool): Whether to rerange the output value from [-1, 1] to - [0, 1]. We highly recommend users should preprocess the - visualization results on their own. Here, we just provide a simple - interface. Default: True. - bgr2rgb (bool): Whether to reformat the channel dimension from BGR to - RGB. The final image we will save is following RGB style. - Default: True. - nrow (int): The number of samples in a row. Default: 1. - padding (int): The number of padding pixels between each samples. - Default: 4. - """ - - def __init__(self, - output_dir, - res_name_list, - interval=-1, - filename_tmpl='iter_{}.png', - rerange=True, - bgr2rgb=True, - nrow=1, - padding=4): - assert mmcv.is_list_of(res_name_list, str) - self.output_dir = output_dir - self.res_name_list = res_name_list - self.interval = interval - self.filename_tmpl = filename_tmpl - self.bgr2rgb = bgr2rgb - self.rerange = rerange - self.nrow = nrow - self.padding = padding - - mmcv.mkdir_or_exist(self.output_dir) - - @master_only - def after_train_iter(self, runner): - """The behavior after each train iteration. - - Args: - runner (object): The runner. - """ - if not self.every_n_iters(runner, self.interval): - return - results = runner.outputs['results'] - - filename = self.filename_tmpl.format(runner.iter + 1) - - img_list = [x for k, x in results.items() if k in self.res_name_list] - img_cat = torch.cat(img_list, dim=3).detach() - if self.rerange: - img_cat = ((img_cat + 1) / 2) - if self.bgr2rgb: - img_cat = img_cat[:, [2, 1, 0], ...] - img_cat = img_cat.clamp_(0, 1) - save_image( - img_cat, - osp.join(self.output_dir, filename), - nrow=self.nrow, - padding=self.padding) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/mask.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/mask.py deleted file mode 100755 index 51486111c..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/mask.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import cv2 -import mmcv -import numpy as np -from PIL import Image, ImageDraw - - -def random_bbox(img_shape, max_bbox_shape, max_bbox_delta=40, min_margin=20): - """Generate a random bbox for the mask on a given image. - - In our implementation, the max value cannot be obtained since we use - `np.random.randint`. And this may be different with other standard scripts - in the community. - - Args: - img_shape (tuple[int]): The size of a image, in the form of (h, w). - max_bbox_shape (int | tuple[int]): Maximum shape of the mask box, - in the form of (h, w). If it is an integer, the mask box will be - square. - max_bbox_delta (int | tuple[int]): Maximum delta of the mask box, - in the form of (delta_h, delta_w). If it is an integer, delta_h - and delta_w will be the same. Mask shape will be randomly sampled - from the range of `max_bbox_shape - max_bbox_delta` and - `max_bbox_shape`. Default: (40, 40). - min_margin (int | tuple[int]): The minimum margin size from the - edges of mask box to the image boarder, in the form of - (margin_h, margin_w). If it is an integer, margin_h and margin_w - will be the same. Default: (20, 20). - - Returns: - tuple[int]: The generated box, (top, left, h, w). - """ - if not isinstance(max_bbox_shape, tuple): - max_bbox_shape = (max_bbox_shape, max_bbox_shape) - if not isinstance(max_bbox_delta, tuple): - max_bbox_delta = (max_bbox_delta, max_bbox_delta) - if not isinstance(min_margin, tuple): - min_margin = (min_margin, min_margin) - assert mmcv.is_tuple_of(max_bbox_shape, int) - assert mmcv.is_tuple_of(max_bbox_delta, int) - assert mmcv.is_tuple_of(min_margin, int) - - img_h, img_w = img_shape[:2] - max_mask_h, max_mask_w = max_bbox_shape - max_delta_h, max_delta_w = max_bbox_delta - margin_h, margin_w = min_margin - - if max_mask_h > img_h or max_mask_w > img_w: - raise ValueError(f'mask shape {max_bbox_shape} should be smaller than ' - f'image shape {img_shape}') - if (max_delta_h // 2 * 2 >= max_mask_h - or max_delta_w // 2 * 2 >= max_mask_w): - raise ValueError(f'mask delta {max_bbox_delta} should be smaller than' - f'mask shape {max_bbox_shape}') - if img_h - max_mask_h < 2 * margin_h or img_w - max_mask_w < 2 * margin_w: - raise ValueError(f'Margin {min_margin} cannot be satisfied for img' - f'shape {img_shape} and mask shape {max_bbox_shape}') - - # get the max value of (top, left) - max_top = img_h - margin_h - max_mask_h - max_left = img_w - margin_w - max_mask_w - # randomly select a (top, left) - top = np.random.randint(margin_h, max_top) - left = np.random.randint(margin_w, max_left) - # randomly shrink the shape of mask box according to `max_bbox_delta` - # the center of box is fixed - delta_top = np.random.randint(0, max_delta_h // 2 + 1) - delta_left = np.random.randint(0, max_delta_w // 2 + 1) - top = top + delta_top - left = left + delta_left - h = max_mask_h - delta_top - w = max_mask_w - delta_left - return (top, left, h, w) - - -def bbox2mask(img_shape, bbox, dtype='uint8'): - """Generate mask in ndarray from bbox. - - The returned mask has the shape of (h, w, 1). '1' indicates the - hole and '0' indicates the valid regions. - - We prefer to use `uint8` as the data type of masks, which may be different - from other codes in the community. - - Args: - img_shape (tuple[int]): The size of the image. - bbox (tuple[int]): Configuration tuple, (top, left, height, width) - dtype (str): Indicate the data type of returned masks. Default: 'uint8' - - Return: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - height, width = img_shape[:2] - - mask = np.zeros((height, width, 1), dtype=dtype) - mask[bbox[0]:bbox[0] + bbox[2], bbox[1]:bbox[1] + bbox[3], :] = 1 - - return mask - - -def brush_stroke_mask(img_shape, - num_vertices=(4, 12), - mean_angle=2 * math.pi / 5, - angle_range=2 * math.pi / 15, - brush_width=(12, 40), - max_loops=4, - dtype='uint8'): - """Generate free-form mask. - - The method of generating free-form mask is in the following paper: - Free-Form Image Inpainting with Gated Convolution. - - When you set the config of this type of mask. You may note the usage of - `np.random.randint` and the range of `np.random.randint` is [left, right). - - We prefer to use `uint8` as the data type of masks, which may be different - from other codes in the community. - - TODO: Rewrite the implementation of this function. - - Args: - img_shape (tuple[int]): Size of the image. - num_vertices (int | tuple[int]): Min and max number of vertices. If - only give an integer, we will fix the number of vertices. - Default: (4, 12). - mean_angle (float): Mean value of the angle in each vertex. The angle - is measured in radians. Default: 2 * math.pi / 5. - angle_range (float): Range of the random angle. - Default: 2 * math.pi / 15. - brush_width (int | tuple[int]): (min_width, max_width). If only give - an integer, we will fix the width of brush. Default: (12, 40). - max_loops (int): The max number of for loops of drawing strokes. - dtype (str): Indicate the data type of returned masks. - Default: 'uint8'. - - Returns: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - img_h, img_w = img_shape[:2] - if isinstance(num_vertices, int): - min_num_vertices, max_num_vertices = num_vertices, num_vertices + 1 - elif isinstance(num_vertices, tuple): - min_num_vertices, max_num_vertices = num_vertices - else: - raise TypeError('The type of num_vertices should be int' - f'or tuple[int], but got type: {num_vertices}') - - if isinstance(brush_width, tuple): - min_width, max_width = brush_width - elif isinstance(brush_width, int): - min_width, max_width = brush_width, brush_width + 1 - else: - raise TypeError('The type of brush_width should be int' - f'or tuple[int], but got type: {brush_width}') - - average_radius = math.sqrt(img_h * img_h + img_w * img_w) / 8 - mask = Image.new('L', (img_w, img_h), 0) - - loop_num = np.random.randint(1, max_loops) - num_vertex_list = np.random.randint( - min_num_vertices, max_num_vertices, size=loop_num) - angle_min_list = np.random.uniform(0, angle_range, size=loop_num) - angle_max_list = np.random.uniform(0, angle_range, size=loop_num) - - for loop_n in range(loop_num): - num_vertex = num_vertex_list[loop_n] - angle_min = mean_angle - angle_min_list[loop_n] - angle_max = mean_angle + angle_max_list[loop_n] - angles = [] - vertex = [] - - # set random angle on each vertex - angles = np.random.uniform(angle_min, angle_max, size=num_vertex) - reverse_mask = (np.arange(num_vertex, dtype=np.float32) % 2) == 0 - angles[reverse_mask] = 2 * math.pi - angles[reverse_mask] - - h, w = mask.size - - # set random vertices - vertex.append((np.random.randint(0, w), np.random.randint(0, h))) - r_list = np.random.normal( - loc=average_radius, scale=average_radius // 2, size=num_vertex) - for i in range(num_vertex): - r = np.clip(r_list[i], 0, 2 * average_radius) - new_x = np.clip(vertex[-1][0] + r * math.cos(angles[i]), 0, w) - new_y = np.clip(vertex[-1][1] + r * math.sin(angles[i]), 0, h) - vertex.append((int(new_x), int(new_y))) - # draw brush strokes according to the vertex and angle list - draw = ImageDraw.Draw(mask) - width = np.random.randint(min_width, max_width) - draw.line(vertex, fill=1, width=width) - for v in vertex: - draw.ellipse((v[0] - width // 2, v[1] - width // 2, - v[0] + width // 2, v[1] + width // 2), - fill=1) - # randomly flip the mask - if np.random.normal() > 0: - mask.transpose(Image.FLIP_LEFT_RIGHT) - if np.random.normal() > 0: - mask.transpose(Image.FLIP_TOP_BOTTOM) - mask = np.array(mask).astype(dtype=getattr(np, dtype)) - mask = mask[:, :, None] - return mask - - -def random_irregular_mask(img_shape, - num_vertices=(4, 8), - max_angle=4, - length_range=(10, 100), - brush_width=(10, 40), - dtype='uint8'): - """Generate random irregular masks. - - This is a modified version of free-form mask implemented in - 'brush_stroke_mask'. - - We prefer to use `uint8` as the data type of masks, which may be different - from other codes in the community. - - TODO: Rewrite the implementation of this function. - - Args: - img_shape (tuple[int]): Size of the image. - num_vertices (int | tuple[int]): Min and max number of vertices. If - only give an integer, we will fix the number of vertices. - Default: (4, 8). - max_angle (float): Max value of angle at each vertex. Default 4.0. - length_range (int | tuple[int]): (min_length, max_length). If only give - an integer, we will fix the length of brush. Default: (10, 100). - brush_width (int | tuple[int]): (min_width, max_width). If only give - an integer, we will fix the width of brush. Default: (10, 40). - dtype (str): Indicate the data type of returned masks. Default: 'uint8' - - Returns: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - h, w = img_shape[:2] - - mask = np.zeros((h, w), dtype=dtype) - if isinstance(length_range, int): - min_length, max_length = length_range, length_range + 1 - elif isinstance(length_range, tuple): - min_length, max_length = length_range - else: - raise TypeError('The type of length_range should be int' - f'or tuple[int], but got type: {length_range}') - if isinstance(num_vertices, int): - min_num_vertices, max_num_vertices = num_vertices, num_vertices + 1 - elif isinstance(num_vertices, tuple): - min_num_vertices, max_num_vertices = num_vertices - else: - raise TypeError('The type of num_vertices should be int' - f'or tuple[int], but got type: {num_vertices}') - - if isinstance(brush_width, int): - min_brush_width, max_brush_width = brush_width, brush_width + 1 - elif isinstance(brush_width, tuple): - min_brush_width, max_brush_width = brush_width - else: - raise TypeError('The type of brush_width should be int' - f'or tuple[int], but got type: {brush_width}') - - num_v = np.random.randint(min_num_vertices, max_num_vertices) - - for i in range(num_v): - start_x = np.random.randint(w) - start_y = np.random.randint(h) - # from the start point, randomly setlect n \in [1, 6] directions. - direction_num = np.random.randint(1, 6) - angle_list = np.random.randint(0, max_angle, size=direction_num) - length_list = np.random.randint( - min_length, max_length, size=direction_num) - brush_width_list = np.random.randint( - min_brush_width, max_brush_width, size=direction_num) - for direct_n in range(direction_num): - angle = 0.01 + angle_list[direct_n] - if i % 2 == 0: - angle = 2 * math.pi - angle - length = length_list[direct_n] - brush_w = brush_width_list[direct_n] - # compute end point according to the random angle - end_x = (start_x + length * np.sin(angle)).astype(np.int32) - end_y = (start_y + length * np.cos(angle)).astype(np.int32) - - cv2.line(mask, (start_y, start_x), (end_y, end_x), 1, brush_w) - start_x, start_y = end_x, end_y - mask = np.expand_dims(mask, axis=2) - - return mask - - -def get_irregular_mask(img_shape, area_ratio_range=(0.15, 0.5), **kwargs): - """Get irregular mask with the constraints in mask ratio - - Args: - img_shape (tuple[int]): Size of the image. - area_ratio_range (tuple(float)): Contain the minimum and maximum area - ratio. Default: (0.15, 0.5). - - Returns: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - mask = random_irregular_mask(img_shape, **kwargs) - min_ratio, max_ratio = area_ratio_range - - while not min_ratio < (np.sum(mask) / - (img_shape[0] * img_shape[1])) < max_ratio: - mask = random_irregular_mask(img_shape, **kwargs) - - return mask diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/misc.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/misc.py deleted file mode 100755 index 21c5d4770..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/misc.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -from torchvision.utils import make_grid - - -def tensor2img(tensor, out_type=np.uint8, min_max=(0, 1)): - """Convert torch Tensors into image numpy arrays. - - After clamping to (min, max), image values will be normalized to [0, 1]. - - For different tensor shapes, this function will have different behaviors: - - 1. 4D mini-batch Tensor of shape (N x 3/1 x H x W): - Use `make_grid` to stitch images in the batch dimension, and then - convert it to numpy array. - 2. 3D Tensor of shape (3/1 x H x W) and 2D Tensor of shape (H x W): - Directly change to numpy array. - - Note that the image channel in input tensors should be RGB order. This - function will convert it to cv2 convention, i.e., (H x W x C) with BGR - order. - - Args: - tensor (Tensor | list[Tensor]): Input tensors. - out_type (numpy type): Output types. If ``np.uint8``, transform outputs - to uint8 type with range [0, 255]; otherwise, float type with - range [0, 1]. Default: ``np.uint8``. - min_max (tuple): min and max values for clamp. - - Returns: - (Tensor | list[Tensor]): 3D ndarray of shape (H x W x C) or 2D ndarray - of shape (H x W). - """ - if not (torch.is_tensor(tensor) or - (isinstance(tensor, list) - and all(torch.is_tensor(t) for t in tensor))): - raise TypeError( - f'tensor or list of tensors expected, got {type(tensor)}') - - if torch.is_tensor(tensor): - tensor = [tensor] - result = [] - for _tensor in tensor: - # Squeeze two times so that: - # 1. (1, 1, h, w) -> (h, w) or - # 3. (1, 3, h, w) -> (3, h, w) or - # 2. (n>1, 3/1, h, w) -> (n>1, 3/1, h, w) - _tensor = _tensor.squeeze(0).squeeze(0) - _tensor = _tensor.float().detach().cpu().clamp_(*min_max) - _tensor = (_tensor - min_max[0]) / (min_max[1] - min_max[0]) - n_dim = _tensor.dim() - if n_dim == 4: - img_np = make_grid( - _tensor, nrow=int(math.sqrt(_tensor.size(0))), - normalize=False).numpy() - img_np = np.transpose(img_np[[2, 1, 0], :, :], (1, 2, 0)) - elif n_dim == 3: - img_np = _tensor.numpy() - img_np = np.transpose(img_np[[2, 1, 0], :, :], (1, 2, 0)) - elif n_dim == 2: - img_np = _tensor.numpy() - else: - raise ValueError('Only support 4D, 3D or 2D tensor. ' - f'But received with dimension: {n_dim}') - if out_type == np.uint8: - # Unlike MATLAB, numpy.unit8() WILL NOT round by default. - img_np = (img_np * 255.0).round() - img_np = img_np.astype(out_type) - result.append(img_np) - result = result[0] if len(result) == 1 else result - return result diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/optimizer/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/optimizer/__init__.py deleted file mode 100755 index e1c477dd1..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/optimizer/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_optimizers - -__all__ = ['build_optimizers'] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/optimizer/builder.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/optimizer/builder.py deleted file mode 100755 index 2edf94dad..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/optimizer/builder.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import build_optimizer - - -def build_optimizers(model, cfgs): - """Build multiple optimizers from configs. - - If `cfgs` contains several dicts for optimizers, then a dict for each - constructed optimizers will be returned. - If `cfgs` only contains one optimizer config, the constructed optimizer - itself will be returned. - - For example, - - 1) Multiple optimizer configs: - - .. code-block:: python - - optimizer_cfg = dict( - model1=dict(type='SGD', lr=lr), - model2=dict(type='SGD', lr=lr)) - - The return dict is - ``dict('model1': torch.optim.Optimizer, 'model2': torch.optim.Optimizer)`` - - 2) Single optimizer config: - - .. code-block:: python - - optimizer_cfg = dict(type='SGD', lr=lr) - - The return is ``torch.optim.Optimizer``. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - cfgs (dict): The config dict of the optimizer. - - Returns: - dict[:obj:`torch.optim.Optimizer`] | :obj:`torch.optim.Optimizer`: - The initialized optimizers. - """ - optimizers = {} - if hasattr(model, 'module'): - model = model.module - # determine whether 'cfgs' has several dicts for optimizers - is_dict_of_dict = True - for key, cfg in cfgs.items(): - if not isinstance(cfg, dict): - is_dict_of_dict = False - - if is_dict_of_dict: - for key, cfg in cfgs.items(): - cfg_ = cfg.copy() - module = getattr(model, key) - optimizers[key] = build_optimizer(module, cfg_) - return optimizers - - return build_optimizer(model, cfgs) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/scheduler/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/scheduler/__init__.py deleted file mode 100755 index af0458206..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/scheduler/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .lr_updater import LinearLrUpdaterHook, ReduceLrUpdaterHook - -__all__ = ['LinearLrUpdaterHook', 'ReduceLrUpdaterHook'] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/scheduler/lr_updater.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/scheduler/lr_updater.py deleted file mode 100755 index 0677373c6..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/scheduler/lr_updater.py +++ /dev/null @@ -1,304 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import HOOKS, LrUpdaterHook - - -@HOOKS.register_module() -class LinearLrUpdaterHook(LrUpdaterHook): - """Linear learning rate scheduler for image generation. - - In the beginning, the learning rate is 'base_lr' defined in mmcv. - We give a target learning rate 'target_lr' and a start point 'start' - (iteration / epoch). Before 'start', we fix learning rate as 'base_lr'; - After 'start', we linearly update learning rate to 'target_lr'. - - Args: - target_lr (float): The target learning rate. Default: 0. - start (int): The start point (iteration / epoch, specified by args - 'by_epoch' in its parent class in mmcv) to update learning rate. - Default: 0. - interval (int): The interval to update the learning rate. Default: 1. - """ - - def __init__(self, target_lr=0, start=0, interval=1, **kwargs): - super().__init__(**kwargs) - self.target_lr = target_lr - self.start = start - self.interval = interval - - def get_lr(self, runner, base_lr): - """Calculates the learning rate. - - Args: - runner (object): The passed runner. - base_lr (float): Base learning rate. - - Returns: - float: Current learning rate. - """ - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - assert max_progress >= self.start - - if max_progress == self.start: - return base_lr - - # Before 'start', fix lr; After 'start', linearly update lr. - factor = (max(0, progress - self.start) // self.interval) / ( - (max_progress - self.start) // self.interval) - return base_lr + (self.target_lr - base_lr) * factor - - -@HOOKS.register_module() -class ReduceLrUpdaterHook(LrUpdaterHook): - """ReduceLROnPlateau Scheduler. - - Reduce learning rate when a metric has stopped improving. This scheduler - reads a metrics quantity and if no improvement is seen for a 'patience' - number of epochs, the learning rate is reduced. - - Args: - val_metric (str, optional): Metrics to be evaluated. If val_metric is - None, the metrics will be loss value. Default: None. - mode (str, optional): One of `min`, `max`. In `min` mode, lr will - be reduced when the quantity monitored has stopped - decreasing; in `max` mode it will be reduced when the - quantity monitored has stopped increasing. Default: 'min'. - factor (float, optional): Factor by which the learning rate will be - reduced. new_lr = lr * factor. Default: 0.1. - patience (int, optional): Number of epochs with no improvement after - which learning rate will be reduced. For example, if - `patience = 2`, then we will ignore the first 2 epochs - with no improvement, and will only decrease the LR after the - 3rd epoch if the loss still hasn't improved then. - Default: 10. - threshold (float, optional): Threshold for measuring the new optimum, - to only focus on significant changes. Default: 1e-4. - threshold_mode (str, optional): One of `rel`, `abs`. In `rel` mode, - dynamic_threshold = best * ( 1 + threshold ) in 'max' - mode or best * ( 1 - threshold ) in `min` mode. - In `abs` mode, dynamic_threshold = best + threshold in - `max` mode or best - threshold in `min` mode. Default: 'rel'. - cooldown (int, optional): Number of epochs to wait before resuming - normal operation after lr has been reduced. Default: 0. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. - Default: 0. - eps (float, optional): Minimal decay applied to lr. If the difference - between new and old lr is smaller than eps, the update is - ignored. Default: 1e-8. - verbose (bool): If ``True``, prints a message to stdout for - each update. Default: ``False``. - epoch_base_valid (None | Bool): Whether use epoch base valid. - If `None`, follow `by_epoch` (inherited from `LrUpdaterHook`). - Default: None. - """ - - def __init__(self, - val_metric: str = None, - mode: str = 'min', - factor: float = 0.1, - patience: int = 10, - threshold: float = 1e-4, - threshold_mode: str = 'rel', - cooldown: int = 0, - min_lr: float = 0., - eps: float = 1e-8, - verbose: bool = False, - epoch_base_valid=None, - **kwargs): - - self.val_metric = val_metric - - if mode not in ['min', 'max']: - raise ValueError( - 'mode must be one of "min" or "max", instead got {mode}') - self.mode = mode - - if factor >= 1.0 or factor < 0: - raise ValueError('Factor should be < 1.0 and >=0') - self.factor = factor - - self.patience = patience - self.threshold = threshold - - if threshold_mode not in ['rel', 'abs']: - raise ValueError('thresh_mode must be one of "rel" or "abs",' - f'instead got {threshold_mode}') - self.threshold_mode = threshold_mode - - self.cooldown = cooldown - self.cooldown_counter = 0 - self.best = None - self.num_bad_epochs = None - self.mode_worse = None # the worse value for the chosen mode - self.min_lr = min_lr - self.eps = eps - self.verbose = verbose - self.last_epoch = 0 - self._init_is_better(self.mode) - self._reset() - - super().__init__(**kwargs) - if epoch_base_valid is None: - self.epoch_base_valid = self.by_epoch - else: - self.epoch_base_valid = epoch_base_valid - - def get_lr(self, regular_lr, optimizer_name): - if self.num_bad_epochs > self.patience: - self.cooldown_counter = self.cooldown - self.num_bad_epochs = 0 - if regular_lr - regular_lr * self.factor > self.eps: - new_lr = max(regular_lr * self.factor, self.min_lr) - if self.verbose: - print(f'Reducing learning rate of {optimizer_name} from ' - f'{regular_lr:.4e} to {new_lr:.4e}.') - else: - new_lr = regular_lr - return new_lr - else: - return regular_lr - - def get_regular_lr(self, runner): - if not self.regular_lr: - self.regular_lr = self.base_lr - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(_regular_lr, k) - for _regular_lr in self.regular_lr[k] - ] - lr_groups.update({k: _lr_group}) - else: - lr_groups = [ - self.get_lr(_regular_lr, 'generator') - for _regular_lr in self.regular_lr - ] - self.regular_lr = lr_groups - return lr_groups - - def _init_is_better(self, mode): - if mode == 'min': - self.mode_worse = float('inf') - else: - self.mode_worse = float('-inf') - - def _reset(self): - self.best = self.mode_worse - self.cooldown_counter = 0 - self.num_bad_epochs = 0 - - def is_better(self, a, best): - if self.mode == 'min' and self.threshold_mode == 'rel': - rel_epsilon = 1. - self.threshold - return a < best * rel_epsilon - elif self.mode == 'min' and self.threshold_mode == 'abs': - return a < best - self.threshold - elif self.mode == 'max' and self.threshold_mode == 'rel': - rel_epsilon = 1. + self.threshold - return a > best * rel_epsilon - else: - return a > best + self.threshold - - @property - def in_cooldown(self): - return self.cooldown_counter > 0 - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - cur_epoch = runner.epoch - if self.warmup is not None and self.warmup_by_epoch: - if cur_epoch <= self.warmup_epochs: - return - # If val_metric is None, we check training loss to reduce learning - # rate. - if self.val_metric is None: - current = runner.outputs['log_vars']['loss'] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'train_epoch--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') - - def after_train_iter(self, runner): - if self.by_epoch: - return - cur_iter = runner.iter - if self.warmup_epochs is not None and cur_iter <= self.warmup_iters: - return - # If val_metric is None, we check training loss to reduce learning - # rate. - if self.val_metric is None: - current = runner.outputs['log_vars']['loss'] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'train_iter--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') - - def after_val_epoch(self, runner): - if not self.by_epoch and not self.epoch_base_valid: - return - cur_epoch = runner.epoch - if self.warmup is not None and self.warmup_by_epoch: - if cur_epoch <= self.warmup_epochs: - return - # If val_metric is not None, we check its value to reduce learning - # rate. - if self.val_metric is not None: - current = runner.log_buffer.output[self.val_metric] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'val_epoch--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') - - def after_val_iter(self, runner): - if self.by_epoch or self.epoch_base_valid: - return - cur_iter = runner.iter - if self.warmup_epochs is not None and cur_iter <= self.warmup_iters: - return - # If val_metric is not None, we check its value to reduce learning - # rate. - if self.val_metric is not None: - current = runner.eval_result[self.val_metric] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'val_iter--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/utils/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/utils/__init__.py deleted file mode 100755 index f894e827c..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/utils/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .dist_utils import sync_random_seed - -__all__ = ['sync_random_seed'] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/core/utils/dist_utils.py b/cv/super_resolution/esrgan/pytorch/mmedit/core/utils/dist_utils.py deleted file mode 100755 index 8a3af5bb0..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/core/utils/dist_utils.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def sync_random_seed(seed=None, device='cuda'): - """Make sure different ranks share the same seed. - All workers must call this function, otherwise it will deadlock. - This method is generally used in `DistributedSampler`, - because the seed should be identical across all processes - in the distributed group. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is None: - seed = np.random.randint(2**31) - assert isinstance(seed, int) - - rank, world_size = get_dist_info() - - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/__init__.py deleted file mode 100755 index 72805084f..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .base_dataset import BaseDataset -from .base_sr_dataset import BaseSRDataset -from .builder import build_dataloader, build_dataset -from .dataset_wrappers import RepeatDataset -from .registry import DATASETS, PIPELINES -from .sr_annotation_dataset import SRAnnotationDataset -from .sr_folder_dataset import SRFolderDataset diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/base_dataset.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/base_dataset.py deleted file mode 100755 index c8ffea6eb..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/base_dataset.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -from abc import ABCMeta, abstractmethod - -from torch.utils.data import Dataset - -from .pipelines import Compose - - -class BaseDataset(Dataset, metaclass=ABCMeta): - """Base class for datasets. - - All datasets should subclass it. - All subclasses should overwrite: - - ``load_annotations``, supporting to load information and generate - image lists. - - Args: - pipeline (list[dict | callable]): A sequence of data transforms. - test_mode (bool): If True, the dataset will work in test mode. - Otherwise, in train mode. - """ - - def __init__(self, pipeline, test_mode=False): - super().__init__() - self.test_mode = test_mode - self.pipeline = Compose(pipeline) - - @abstractmethod - def load_annotations(self): - """Abstract function for loading annotation. - - All subclasses should overwrite this function - """ - - def prepare_train_data(self, idx): - """Prepare training data. - - Args: - idx (int): Index of the training batch data. - - Returns: - dict: Returned training batch. - """ - results = copy.deepcopy(self.data_infos[idx]) - return self.pipeline(results) - - def prepare_test_data(self, idx): - """Prepare testing data. - - Args: - idx (int): Index for getting each testing batch. - - Returns: - Tensor: Returned testing batch. - """ - results = copy.deepcopy(self.data_infos[idx]) - return self.pipeline(results) - - def __len__(self): - """Length of the dataset. - - Returns: - int: Length of the dataset. - """ - return len(self.data_infos) - - def __getitem__(self, idx): - """Get item at each call. - - Args: - idx (int): Index for getting each item. - """ - if self.test_mode: - return self.prepare_test_data(idx) - - return self.prepare_train_data(idx) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/base_sr_dataset.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/base_sr_dataset.py deleted file mode 100755 index 6e8115056..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/base_sr_dataset.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import os.path as osp -from collections import defaultdict -from pathlib import Path - -from mmcv import scandir - -from .base_dataset import BaseDataset - -IMG_EXTENSIONS = ('.jpg', '.JPG', '.jpeg', '.JPEG', '.png', '.PNG', '.ppm', - '.PPM', '.bmp', '.BMP', '.tif', '.TIF', '.tiff', '.TIFF') - - -class BaseSRDataset(BaseDataset): - """Base class for super resolution datasets. - """ - - def __init__(self, pipeline, scale, test_mode=False): - super().__init__(pipeline, test_mode) - self.scale = scale - - @staticmethod - def scan_folder(path): - """Obtain image path list (including sub-folders) from a given folder. - - Args: - path (str | :obj:`Path`): Folder path. - - Returns: - list[str]: image list obtained form given folder. - """ - - if isinstance(path, (str, Path)): - path = str(path) - else: - raise TypeError("'path' must be a str or a Path object, " - f'but received {type(path)}.') - - images = list(scandir(path, suffix=IMG_EXTENSIONS, recursive=True)) - images = [osp.join(path, v) for v in images] - assert images, f'{path} has no valid image file.' - return images - - def __getitem__(self, idx): - """Get item at each call. - - Args: - idx (int): Index for getting each item. - """ - results = copy.deepcopy(self.data_infos[idx]) - results['scale'] = self.scale - return self.pipeline(results) - - def evaluate(self, results, logger=None): - """Evaluate with different metrics. - - Args: - results (list[tuple]): The output of forward_test() of the model. - - Return: - dict: Evaluation results dict. - """ - if not isinstance(results, list): - raise TypeError(f'results must be a list, but got {type(results)}') - assert len(results) == len(self), ( - 'The length of results is not equal to the dataset len: ' - f'{len(results)} != {len(self)}') - - results = [res['eval_result'] for res in results] # a list of dict - eval_result = defaultdict(list) # a dict of list - - for res in results: - for metric, val in res.items(): - eval_result[metric].append(val) - for metric, val_list in eval_result.items(): - assert len(val_list) == len(self), ( - f'Length of evaluation result of {metric} is {len(val_list)}, ' - f'should be {len(self)}') - - # average the results - eval_result = { - metric: sum(values) / len(self) - for metric, values in eval_result.items() - } - - return eval_result diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/builder.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/builder.py deleted file mode 100755 index 4414d375e..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/builder.py +++ /dev/null @@ -1,181 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import platform -import random -from functools import partial - -import numpy as np -import torch -from mmcv.parallel import collate -from mmcv.runner import get_dist_info -from mmcv.utils import build_from_cfg -from packaging import version -from torch.utils.data import ConcatDataset, DataLoader - -from .dataset_wrappers import RepeatDataset -from .registry import DATASETS -from .samplers import DistributedSampler - -if platform.system() != 'Windows': - # https://github.com/pytorch/pytorch/issues/973 - import resource - rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) - base_soft_limit = rlimit[0] - hard_limit = rlimit[1] - soft_limit = min(max(4096, base_soft_limit), hard_limit) - resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) - - -def _concat_dataset(cfg, default_args=None): - """Concat datasets with different ann_file but the same type. - - Args: - cfg (dict): The config of dataset. - default_args (dict, optional): Default initialization arguments. - Default: None. - - Returns: - Dataset: The concatenated dataset. - """ - ann_files = cfg['ann_file'] - - datasets = [] - num_dset = len(ann_files) - for i in range(num_dset): - data_cfg = copy.deepcopy(cfg) - data_cfg['ann_file'] = ann_files[i] - datasets.append(build_dataset(data_cfg, default_args)) - - return ConcatDataset(datasets) - - -def build_dataset(cfg, default_args=None): - """Build a dataset from config dict. - - It supports a variety of dataset config. If ``cfg`` is a Sequential (list - or dict), it will be a concatenated dataset of the datasets specified by - the Sequential. If it is a ``RepeatDataset``, then it will repeat the - dataset ``cfg['dataset']`` for ``cfg['times']`` times. If the ``ann_file`` - of the dataset is a Sequential, then it will build a concatenated dataset - with the same dataset type but different ``ann_file``. - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - default_args (dict, optional): Default initialization arguments. - Default: None. - - Returns: - Dataset: The constructed dataset. - """ - if isinstance(cfg, (list, tuple)): - dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) - elif cfg['type'] == 'RepeatDataset': - dataset = RepeatDataset( - build_dataset(cfg['dataset'], default_args), cfg['times']) - elif isinstance(cfg.get('ann_file'), (list, tuple)): - dataset = _concat_dataset(cfg, default_args) - else: - dataset = build_from_cfg(cfg, DATASETS, default_args) - - return dataset - - -def build_dataloader(dataset, - samples_per_gpu, - workers_per_gpu, - num_gpus=1, - dist=True, - shuffle=True, - seed=None, - drop_last=False, - pin_memory=True, - persistent_workers=True, - **kwargs): - """Build PyTorch DataLoader. - - In distributed training, each GPU/process has a dataloader. - In non-distributed training, there is only one dataloader for all GPUs. - - Args: - dataset (:obj:`Dataset`): A PyTorch dataset. - samples_per_gpu (int): Number of samples on each GPU, i.e., - batch size of each GPU. - workers_per_gpu (int): How many subprocesses to use for data - loading for each GPU. - num_gpus (int): Number of GPUs. Only used in non-distributed - training. Default: 1. - dist (bool): Distributed training/test or not. Default: True. - shuffle (bool): Whether to shuffle the data at every epoch. - Default: True. - seed (int | None): Seed to be used. Default: None. - drop_last (bool): Whether to drop the last incomplete batch in epoch. - Default: False - pin_memory (bool): Whether to use pin_memory in DataLoader. - Default: True - persistent_workers (bool): If True, the data loader will not shutdown - the worker processes after a dataset has been consumed once. - This allows to maintain the workers Dataset instances alive. - The argument also has effect in PyTorch>=1.7.0. - Default: True - kwargs (dict, optional): Any keyword argument to be used to initialize - DataLoader. - - Returns: - DataLoader: A PyTorch dataloader. - """ - rank, world_size = get_dist_info() - if dist: - sampler = DistributedSampler( - dataset, - world_size, - rank, - shuffle=shuffle, - samples_per_gpu=samples_per_gpu, - seed=seed) - shuffle = False - batch_size = samples_per_gpu - num_workers = workers_per_gpu - else: - sampler = None - batch_size = num_gpus * samples_per_gpu - num_workers = num_gpus * workers_per_gpu - - init_fn = partial( - worker_init_fn, num_workers=num_workers, rank=rank, - seed=seed) if seed is not None else None - - if version.parse(torch.__version__) >= version.parse('1.7.0'): - kwargs['persistent_workers'] = persistent_workers - - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - **kwargs) - - return data_loader - - -def worker_init_fn(worker_id, num_workers, rank, seed): - """Function to initialize each worker. - - The seed of each worker equals to - ``num_worker * rank + worker_id + user_seed``. - - Args: - worker_id (int): Id for each worker. - num_workers (int): Number of workers. - rank (int): Rank in distributed training. - seed (int): Random seed. - """ - - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/comp1k_dataset.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/comp1k_dataset.py deleted file mode 100755 index a778a9463..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/comp1k_dataset.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv - -from .base_matting_dataset import BaseMattingDataset -from .registry import DATASETS - - -@DATASETS.register_module() -class AdobeComp1kDataset(BaseMattingDataset): - """Adobe composition-1k dataset. - - The dataset loads (alpha, fg, bg) data and apply specified transforms to - the data. You could specify whether composite merged image online or load - composited merged image in pipeline. - - Example for online comp-1k dataset: - - :: - - [ - { - "alpha": 'alpha/000.png', - "fg": 'fg/000.png', - "bg": 'bg/000.png' - }, - { - "alpha": 'alpha/001.png', - "fg": 'fg/001.png', - "bg": 'bg/001.png' - }, - ] - - Example for offline comp-1k dataset: - - :: - - [ - { - "alpha": 'alpha/000.png', - "merged": 'merged/000.png', - "fg": 'fg/000.png', - "bg": 'bg/000.png' - }, - { - "alpha": 'alpha/001.png', - "merged": 'merged/001.png', - "fg": 'fg/001.png', - "bg": 'bg/001.png' - }, - ] - - """ - - def load_annotations(self): - """Load annotations for Adobe Composition-1k dataset. - - It loads image paths from json file. - - Returns: - dict: Loaded dict. - """ - data_infos = mmcv.load(self.ann_file) - - for data_info in data_infos: - for key in data_info: - data_info[key] = osp.join(self.data_prefix, data_info[key]) - - return data_infos diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/dataset_wrappers.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/dataset_wrappers.py deleted file mode 100755 index 3dbca31ea..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/dataset_wrappers.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import DATASETS - - -@DATASETS.register_module() -class RepeatDataset: - """A wrapper of repeated dataset. - - The length of repeated dataset will be `times` larger than the original - dataset. This is useful when the data loading time is long but the dataset - is small. Using RepeatDataset can reduce the data loading time between - epochs. - - Args: - dataset (:obj:`Dataset`): The dataset to be repeated. - times (int): Repeat times. - """ - - def __init__(self, dataset, times): - self.dataset = dataset - self.times = times - - self._ori_len = len(self.dataset) - - def __getitem__(self, idx): - """Get item at each call. - - Args: - idx (int): Index for getting each item. - """ - return self.dataset[idx % self._ori_len] - - def __len__(self): - """Length of the dataset. - - Returns: - int: Length of the dataset. - """ - return self.times * self._ori_len diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/__init__.py deleted file mode 100755 index 07d4e48c3..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .augmentation import (BinarizeImage, ColorJitter, CopyValues, Flip, - GenerateFrameIndices, - GenerateFrameIndiceswithPadding, - GenerateSegmentIndices, MirrorSequence, Pad, - Quantize, RandomAffine, RandomJitter, - RandomMaskDilation, RandomTransposeHW, Resize, - TemporalReverse, UnsharpMasking) -from .compose import Compose -from .crop import (Crop, CropAroundCenter, CropAroundFg, CropAroundUnknown, - CropLike, FixedCrop, ModCrop, PairedRandomCrop, - RandomResizedCrop) -from .formating import (Collect, FormatTrimap, GetMaskedImage, ImageToTensor, - ToTensor) -from .loading import (GetSpatialDiscountMask, LoadImageFromFile, - LoadImageFromFileList, LoadMask, LoadPairedImageFromFile, - RandomLoadResizeBg) -from .normalization import Normalize, RescaleToZeroOne - - diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/augmentation.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/augmentation.py deleted file mode 100755 index e16f80a1c..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/augmentation.py +++ /dev/null @@ -1,1332 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import numbers -import os -import os.path as osp -import random - -import cv2 -import mmcv -import numpy as np -import torch -import torchvision.transforms as transforms -from PIL import Image - -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class Resize: - """Resize data to a specific size for training or resize the images to fit - the network input regulation for testing. - - When used for resizing images to fit network input regulation, the case is - that a network may have several downsample and then upsample operation, - then the input height and width should be divisible by the downsample - factor of the network. - For example, the network would downsample the input for 5 times with - stride 2, then the downsample factor is 2^5 = 32 and the height - and width should be divisible by 32. - - Required keys are the keys in attribute "keys", added or modified keys are - "keep_ratio", "scale_factor", "interpolation" and the - keys in attribute "keys". - - All keys in "keys" should have the same shape. "test_trans" is used to - record the test transformation to align the input's shape. - - Args: - keys (list[str]): The images to be resized. - scale (float | tuple[int]): If scale is tuple[int], target spatial - size (h, w). Otherwise, target spatial size is scaled by input - size. - Note that when it is used, `size_factor` and `max_size` are - useless. Default: None - keep_ratio (bool): If set to True, images will be resized without - changing the aspect ratio. Otherwise, it will resize images to a - given size. Default: False. - Note that it is used togher with `scale`. - size_factor (int): Let the output shape be a multiple of size_factor. - Default:None. - Note that when it is used, `scale` should be set to None and - `keep_ratio` should be set to False. - max_size (int): The maximum size of the longest side of the output. - Default:None. - Note that it is used togher with `size_factor`. - interpolation (str): Algorithm used for interpolation: - "nearest" | "bilinear" | "bicubic" | "area" | "lanczos". - Default: "bilinear". - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. - Default: None. - output_keys (list[str] | None): The resized images. Default: None - Note that if it is not `None`, its length should be equal to keys. - """ - - def __init__(self, - keys, - scale=None, - keep_ratio=False, - size_factor=None, - max_size=None, - interpolation='bilinear', - backend=None, - output_keys=None): - assert keys, 'Keys should not be empty.' - if output_keys: - assert len(output_keys) == len(keys) - else: - output_keys = keys - if size_factor: - assert scale is None, ('When size_factor is used, scale should ', - f'be None. But received {scale}.') - assert keep_ratio is False, ('When size_factor is used, ' - 'keep_ratio should be False.') - if max_size: - assert size_factor is not None, ( - 'When max_size is used, ' - f'size_factor should also be set. But received {size_factor}.') - if isinstance(scale, float): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - elif mmcv.is_tuple_of(scale, int): - max_long_edge = max(scale) - max_short_edge = min(scale) - if max_short_edge == -1: - # assign np.inf to long edge for rescaling short edge later. - scale = (np.inf, max_long_edge) - elif scale is not None: - raise TypeError( - f'Scale must be None, float or tuple of int, but got ' - f'{type(scale)}.') - self.keys = keys - self.output_keys = output_keys - self.scale = scale - self.size_factor = size_factor - self.max_size = max_size - self.keep_ratio = keep_ratio - self.interpolation = interpolation - self.backend = backend - - def _resize(self, img): - if self.keep_ratio: - img, self.scale_factor = mmcv.imrescale( - img, - self.scale, - return_scale=True, - interpolation=self.interpolation, - backend=self.backend) - else: - img, w_scale, h_scale = mmcv.imresize( - img, - self.scale, - return_scale=True, - interpolation=self.interpolation, - backend=self.backend) - self.scale_factor = np.array((w_scale, h_scale), dtype=np.float32) - return img - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - if self.size_factor: - h, w = results[self.keys[0]].shape[:2] - new_h = h - (h % self.size_factor) - new_w = w - (w % self.size_factor) - if self.max_size: - new_h = min(self.max_size - (self.max_size % self.size_factor), - new_h) - new_w = min(self.max_size - (self.max_size % self.size_factor), - new_w) - self.scale = (new_w, new_h) - for key, out_key in zip(self.keys, self.output_keys): - results[out_key] = self._resize(results[key]) - if len(results[out_key].shape) == 2: - results[out_key] = np.expand_dims(results[out_key], axis=2) - - results['scale_factor'] = self.scale_factor - results['keep_ratio'] = self.keep_ratio - results['interpolation'] = self.interpolation - results['backend'] = self.backend - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(keys={self.keys}, output_keys={self.output_keys}, ' - f'scale={self.scale}, ' - f'keep_ratio={self.keep_ratio}, size_factor={self.size_factor}, ' - f'max_size={self.max_size}, interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class RandomRotation: - """Rotate the image by a randomly-chosen angle, measured in degree. - - Args: - keys (list[str]): The images to be rotated. - degrees (tuple[float] | tuple[int] | float | int): If it is a tuple, - it represents a range (min, max). If it is a float or int, - the range is constructed as (-degrees, degrees). - """ - - def __init__(self, keys, degrees): - if isinstance(degrees, (int, float)): - if degrees < 0.0: - raise ValueError('Degrees must be positive if it is a number.') - else: - degrees = (-degrees, degrees) - elif not mmcv.is_tuple_of(degrees, (int, float)): - raise TypeError(f'Degrees must be float | int or tuple of float | ' - 'int, but got ' - f'{type(degrees)}.') - - self.keys = keys - self.degrees = degrees - - def __call__(self, results): - angle = random.uniform(self.degrees[0], self.degrees[1]) - - for k in self.keys: - results[k] = mmcv.imrotate(results[k], angle) - if results[k].ndim == 2: - results[k] = np.expand_dims(results[k], axis=2) - results['degrees'] = self.degrees - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, degrees={self.degrees})') - return repr_str - - -@PIPELINES.register_module() -class Flip: - """Flip the input data with a probability. - - Reverse the order of elements in the given data with a specific direction. - The shape of the data is preserved, but the elements are reordered. - Required keys are the keys in attributes "keys", added or modified keys are - "flip", "flip_direction" and the keys in attributes "keys". - It also supports flipping a list of images with the same flip. - - Args: - keys (list[str]): The images to be flipped. - flip_ratio (float): The propability to flip the images. - direction (str): Flip images horizontally or vertically. Options are - "horizontal" | "vertical". Default: "horizontal". - """ - _directions = ['horizontal', 'vertical'] - - def __init__(self, keys, flip_ratio=0.5, direction='horizontal'): - if direction not in self._directions: - raise ValueError(f'Direction {direction} is not supported.' - f'Currently support ones are {self._directions}') - self.keys = keys - self.flip_ratio = flip_ratio - self.direction = direction - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - flip = np.random.random() < self.flip_ratio - - if flip: - for key in self.keys: - if isinstance(results[key], list): - for v in results[key]: - mmcv.imflip_(v, self.direction) - else: - mmcv.imflip_(results[key], self.direction) - - results['flip'] = flip - results['flip_direction'] = self.direction - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, flip_ratio={self.flip_ratio}, ' - f'direction={self.direction})') - return repr_str - - -@PIPELINES.register_module() -class Pad: - """Pad the images to align with network downsample factor for testing. - - See `Reshape` for more explanation. `numpy.pad` is used for the pad - operation. - Required keys are the keys in attribute "keys", added or - modified keys are "test_trans" and the keys in attribute - "keys". All keys in "keys" should have the same shape. "test_trans" is used - to record the test transformation to align the input's shape. - - Args: - keys (list[str]): The images to be padded. - ds_factor (int): Downsample factor of the network. The height and - weight will be padded to a multiple of ds_factor. Default: 32. - kwargs (option): any keyword arguments to be passed to `numpy.pad`. - """ - - def __init__(self, keys, ds_factor=32, **kwargs): - self.keys = keys - self.ds_factor = ds_factor - self.kwargs = kwargs - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - h, w = results[self.keys[0]].shape[:2] - - new_h = self.ds_factor * ((h - 1) // self.ds_factor + 1) - new_w = self.ds_factor * ((w - 1) // self.ds_factor + 1) - - pad_h = new_h - h - pad_w = new_w - w - if new_h != h or new_w != w: - pad_width = ((0, pad_h), (0, pad_w), (0, 0)) - for key in self.keys: - results[key] = np.pad(results[key], - pad_width[:results[key].ndim], - **self.kwargs) - results['pad'] = (pad_h, pad_w) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - kwargs_str = ', '.join( - [f'{key}={val}' for key, val in self.kwargs.items()]) - repr_str += (f'(keys={self.keys}, ds_factor={self.ds_factor}, ' - f'{kwargs_str})') - return repr_str - - -@PIPELINES.register_module() -class RandomAffine: - """Apply random affine to input images. - - This class is adopted from - https://github.com/pytorch/vision/blob/v0.5.0/torchvision/transforms/ - transforms.py#L1015 - It should be noted that in - https://github.com/Yaoyi-Li/GCA-Matting/blob/master/dataloader/ - data_generator.py#L70 - random flip is added. See explanation of `flip_ratio` below. - Required keys are the keys in attribute "keys", modified keys - are keys in attribute "keys". - - Args: - keys (Sequence[str]): The images to be affined. - degrees (float | tuple[float]): Range of degrees to select from. If it - is a float instead of a tuple like (min, max), the range of degrees - will be (-degrees, +degrees). Set to 0 to deactivate rotations. - translate (tuple, optional): Tuple of maximum absolute fraction for - horizontal and vertical translations. For example translate=(a, b), - then horizontal shift is randomly sampled in the range - -img_width * a < dx < img_width * a and vertical shift is randomly - sampled in the range -img_height * b < dy < img_height * b. - Default: None. - scale (tuple, optional): Scaling factor interval, e.g (a, b), then - scale is randomly sampled from the range a <= scale <= b. - Default: None. - shear (float | tuple[float], optional): Range of shear degrees to - select from. If shear is a float, a shear parallel to the x axis - and a shear parallel to the y axis in the range (-shear, +shear) - will be applied. Else if shear is a tuple of 2 values, a x-axis - shear and a y-axis shear in (shear[0], shear[1]) will be applied. - Default: None. - flip_ratio (float, optional): Probability of the image being flipped. - The flips in horizontal direction and vertical direction are - independent. The image may be flipped in both directions. - Default: None. - """ - - def __init__(self, - keys, - degrees, - translate=None, - scale=None, - shear=None, - flip_ratio=None): - self.keys = keys - if isinstance(degrees, numbers.Number): - assert degrees >= 0, ('If degrees is a single number, ' - 'it must be positive.') - self.degrees = (-degrees, degrees) - else: - assert isinstance(degrees, tuple) and len(degrees) == 2, \ - 'degrees should be a tuple and it must be of length 2.' - self.degrees = degrees - - if translate is not None: - assert isinstance(translate, tuple) and len(translate) == 2, \ - 'translate should be a tuple and it must be of length 2.' - for t in translate: - assert 0.0 <= t <= 1.0, ('translation values should be ' - 'between 0 and 1.') - self.translate = translate - - if scale is not None: - assert isinstance(scale, tuple) and len(scale) == 2, \ - 'scale should be a tuple and it must be of length 2.' - for s in scale: - assert s > 0, 'scale values should be positive.' - self.scale = scale - - if shear is not None: - if isinstance(shear, numbers.Number): - assert shear >= 0, ('If shear is a single number, ' - 'it must be positive.') - self.shear = (-shear, shear) - else: - assert isinstance(shear, tuple) and len(shear) == 2, \ - 'shear should be a tuple and it must be of length 2.' - # X-Axis and Y-Axis shear with (min, max) - self.shear = shear - else: - self.shear = shear - - if flip_ratio is not None: - assert isinstance(flip_ratio, - float), 'flip_ratio should be a float.' - self.flip_ratio = flip_ratio - else: - self.flip_ratio = 0 - - @staticmethod - def _get_params(degrees, translate, scale_ranges, shears, flip_ratio, - img_size): - """Get parameters for affine transformation. - - Returns: - paras (tuple): Params to be passed to the affine transformation. - """ - angle = np.random.uniform(degrees[0], degrees[1]) - if translate is not None: - max_dx = translate[0] * img_size[0] - max_dy = translate[1] * img_size[1] - translations = (np.round(np.random.uniform(-max_dx, max_dx)), - np.round(np.random.uniform(-max_dy, max_dy))) - else: - translations = (0, 0) - - if scale_ranges is not None: - scale = (np.random.uniform(scale_ranges[0], scale_ranges[1]), - np.random.uniform(scale_ranges[0], scale_ranges[1])) - else: - scale = (1.0, 1.0) - - if shears is not None: - shear = np.random.uniform(shears[0], shears[1]) - else: - shear = 0.0 - - # Because `flip` is used as a multiplier in line 479 and 480, - # so -1 stands for flip and 1 stands for no flip. Thus `flip` - # should be an 'inverse' flag as the result of the comparison. - # See https://github.com/open-mmlab/mmediting/pull/799 for more detail - flip = (np.random.rand(2) > flip_ratio).astype(np.int32) * 2 - 1 - - return angle, translations, scale, shear, flip - - @staticmethod - def _get_inverse_affine_matrix(center, angle, translate, scale, shear, - flip): - """Helper method to compute inverse matrix for affine transformation. - - As it is explained in PIL.Image.rotate, we need compute INVERSE of - affine transformation matrix: M = T * C * RSS * C^-1 where - T is translation matrix: - [1, 0, tx | 0, 1, ty | 0, 0, 1]; - C is translation matrix to keep center: - [1, 0, cx | 0, 1, cy | 0, 0, 1]; - RSS is rotation with scale and shear matrix. - - It is different from the original function in torchvision. - 1. The order are changed to flip -> scale -> rotation -> shear. - 2. x and y have different scale factors. - RSS(shear, a, scale, f) = - [ cos(a + shear)*scale_x*f -sin(a + shear)*scale_y 0] - [ sin(a)*scale_x*f cos(a)*scale_y 0] - [ 0 0 1] - Thus, the inverse is M^-1 = C * RSS^-1 * C^-1 * T^-1. - """ - - angle = math.radians(angle) - shear = math.radians(shear) - scale_x = 1.0 / scale[0] * flip[0] - scale_y = 1.0 / scale[1] * flip[1] - - # Inverted rotation matrix with scale and shear - d = math.cos(angle + shear) * math.cos(angle) + math.sin( - angle + shear) * math.sin(angle) - matrix = [ - math.cos(angle) * scale_x, - math.sin(angle + shear) * scale_x, 0, -math.sin(angle) * scale_y, - math.cos(angle + shear) * scale_y, 0 - ] - matrix = [m / d for m in matrix] - - # Apply inverse of translation and of center translation: - # RSS^-1 * C^-1 * T^-1 - matrix[2] += matrix[0] * (-center[0] - translate[0]) + matrix[1] * ( - -center[1] - translate[1]) - matrix[5] += matrix[3] * (-center[0] - translate[0]) + matrix[4] * ( - -center[1] - translate[1]) - - # Apply center translation: C * RSS^-1 * C^-1 * T^-1 - matrix[2] += center[0] - matrix[5] += center[1] - - return matrix - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - h, w = results[self.keys[0]].shape[:2] - # if image is too small, set degree to 0 to reduce introduced dark area - if np.maximum(h, w) < 1024: - params = self._get_params((0, 0), self.translate, self.scale, - self.shear, self.flip_ratio, (h, w)) - else: - params = self._get_params(self.degrees, self.translate, self.scale, - self.shear, self.flip_ratio, (h, w)) - - center = (w * 0.5 - 0.5, h * 0.5 - 0.5) - M = self._get_inverse_affine_matrix(center, *params) - M = np.array(M).reshape((2, 3)) - - for key in self.keys: - results[key] = cv2.warpAffine( - results[key], - M, (w, h), - flags=cv2.INTER_NEAREST + cv2.WARP_INVERSE_MAP) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, degrees={self.degrees}, ' - f'translate={self.translate}, scale={self.scale}, ' - f'shear={self.shear}, flip_ratio={self.flip_ratio})') - return repr_str - - -@PIPELINES.register_module() -class RandomJitter: - """Randomly jitter the foreground in hsv space. - - The jitter range of hue is adjustable while the jitter ranges of saturation - and value are adaptive to the images. Side effect: the "fg" image will be - converted to `np.float32`. - Required keys are "fg" and "alpha", modified key is "fg". - - Args: - hue_range (float | tuple[float]): Range of hue jittering. If it is a - float instead of a tuple like (min, max), the range of hue - jittering will be (-hue_range, +hue_range). Default: 40. - """ - - def __init__(self, hue_range=40): - if isinstance(hue_range, numbers.Number): - assert hue_range >= 0, ('If hue_range is a single number, ' - 'it must be positive.') - self.hue_range = (-hue_range, hue_range) - else: - assert isinstance(hue_range, tuple) and len(hue_range) == 2, \ - 'hue_range should be a tuple and it must be of length 2.' - self.hue_range = hue_range - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - fg, alpha = results['fg'], results['alpha'] - - # convert to HSV space; - # convert to float32 image to keep precision during space conversion. - fg = mmcv.bgr2hsv(fg.astype(np.float32) / 255) - # Hue noise - hue_jitter = np.random.randint(self.hue_range[0], self.hue_range[1]) - fg[:, :, 0] = np.remainder(fg[:, :, 0] + hue_jitter, 360) - - # Saturation noise - sat_mean = fg[:, :, 1][alpha > 0].mean() - # jitter saturation within range (1.1 - sat_mean) * [-0.1, 0.1] - sat_jitter = (1.1 - sat_mean) * (np.random.rand() * 0.2 - 0.1) - sat = fg[:, :, 1] - sat = np.abs(sat + sat_jitter) - sat[sat > 1] = 2 - sat[sat > 1] - fg[:, :, 1] = sat - - # Value noise - val_mean = fg[:, :, 2][alpha > 0].mean() - # jitter value within range (1.1 - val_mean) * [-0.1, 0.1] - val_jitter = (1.1 - val_mean) * (np.random.rand() * 0.2 - 0.1) - val = fg[:, :, 2] - val = np.abs(val + val_jitter) - val[val > 1] = 2 - val[val > 1] - fg[:, :, 2] = val - # convert back to BGR space - fg = mmcv.hsv2bgr(fg) - results['fg'] = fg * 255 - - return results - - def __repr__(self): - return self.__class__.__name__ + f'hue_range={self.hue_range}' - - -@PIPELINES.register_module() -class ColorJitter: - """An interface for torch color jitter so that it can be invoked in - mmediting pipeline. - - Randomly change the brightness, contrast and saturation of an image. - Modified keys are the attributes specified in "keys". - - Args: - keys (list[str]): The images to be resized. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'rgb'. - - Notes: ``**kwards`` follows the args list of - ``torchvision.transforms.ColorJitter``. - - brightness (float or tuple of float (min, max)): How much to jitter - brightness. brightness_factor is chosen uniformly from - [max(0, 1 - brightness), 1 + brightness] or the given [min, max]. - Should be non negative numbers. - contrast (float or tuple of float (min, max)): How much to jitter - contrast. contrast_factor is chosen uniformly from - [max(0, 1 - contrast), 1 + contrast] or the given [min, max]. - Should be non negative numbers. - saturation (float or tuple of float (min, max)): How much to jitter - saturation. saturation_factor is chosen uniformly from - [max(0, 1 - saturation), 1 + saturation] or the given [min, max]. - Should be non negative numbers. - hue (float or tuple of float (min, max)): How much to jitter hue. - hue_factor is chosen uniformly from [-hue, hue] or the given - [min, max]. - Should have 0<= hue <= 0.5 or -0.5 <= min <= max <= 0.5. - """ - - def __init__(self, keys, channel_order='rgb', **kwargs): - assert keys, 'Keys should not be empty.' - assert 'to_rgb' not in kwargs, ( - '`to_rgb` is not support in ColorJitter, ' - "which is replaced by `channel_order` ('rgb' or 'bgr')") - - self.keys = keys - self.channel_order = channel_order - self.transform = transforms.ColorJitter(**kwargs) - - def _color_jitter(self, image, this_seed): - - if self.channel_order.lower() == 'bgr': - image = image[..., ::-1] - - image = Image.fromarray(image) - torch.manual_seed(this_seed) - image = self.transform(image) - image = np.asarray(image) - - if self.channel_order.lower() == 'bgr': - image = image[..., ::-1] - - return image - - def __call__(self, results): - - this_seed = random.randint(0, 2**32) - - for k in self.keys: - if isinstance(results[k], list): - results[k] = [ - self._color_jitter(v, this_seed) for v in results[k] - ] - else: - results[k] = self._color_jitter(results[k], this_seed) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, channel_order={self.channel_order}, ' - f'brightness={self.transform.brightness}, ' - f'contrast={self.transform.contrast}, ' - f'saturation={self.transform.saturation}, ' - f'hue={self.transform.hue})') - - return repr_str - - -class BinarizeImage: - """Binarize image. - - Args: - keys (Sequence[str]): The images to be binarized. - binary_thr (float): Threshold for binarization. - to_int (bool): If True, return image as int32, otherwise - return image as float32. - """ - - def __init__(self, keys, binary_thr, to_int=False): - self.keys = keys - self.binary_thr = binary_thr - self.to_int = to_int - - def _binarize(self, img): - type_ = np.float32 if not self.to_int else np.int32 - img = (img[..., :] > self.binary_thr).astype(type_) - - return img - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - results[k] = self._binarize(results[k]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, binary_thr={self.binary_thr}, ' - f'to_int={self.to_int})') - - return repr_str - - -@PIPELINES.register_module() -class RandomMaskDilation: - """Randomly dilate binary masks. - - Args: - keys (Sequence[str]): The images to be resized. - get_binary (bool): If True, according to binary_thr, reset final - output as binary mask. Otherwise, return masks directly. - binary_thr (float): Threshold for obtaining binary mask. - kernel_min (int): Min size of dilation kernel. - kernel_max (int): Max size of dilation kernel. - """ - - def __init__(self, keys, binary_thr=0., kernel_min=9, kernel_max=49): - self.keys = keys - self.kernel_min = kernel_min - self.kernel_max = kernel_max - self.binary_thr = binary_thr - - def _random_dilate(self, img): - kernel_size = np.random.randint(self.kernel_min, self.kernel_max + 1) - kernel = np.ones((kernel_size, kernel_size), dtype=np.uint8) - dilate_kernel_size = kernel_size - img_ = cv2.dilate(img, kernel, iterations=1) - - img_ = (img_ > self.binary_thr).astype(np.float32) - - return img_, dilate_kernel_size - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - results[k], d_kernel = self._random_dilate(results[k]) - if len(results[k].shape) == 2: - results[k] = np.expand_dims(results[k], axis=2) - results[k + '_dilate_kernel_size'] = d_kernel - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, kernel_min={self.kernel_min}, ' - f'kernel_max={self.kernel_max})') - - return repr_str - - -@PIPELINES.register_module() -class RandomTransposeHW: - """Randomly transpose images in H and W dimensions with a probability. - - (TransposeHW = horizontal flip + anti-clockwise rotatation by 90 degrees) - When used with horizontal/vertical flips, it serves as a way of rotation - augmentation. - It also supports randomly transposing a list of images. - - Required keys are the keys in attributes "keys", added or modified keys are - "transpose" and the keys in attributes "keys". - - Args: - keys (list[str]): The images to be transposed. - transpose_ratio (float): The propability to transpose the images. - """ - - def __init__(self, keys, transpose_ratio=0.5): - self.keys = keys - self.transpose_ratio = transpose_ratio - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - transpose = np.random.random() < self.transpose_ratio - - if transpose: - for key in self.keys: - if isinstance(results[key], list): - results[key] = [v.transpose(1, 0, 2) for v in results[key]] - else: - results[key] = results[key].transpose(1, 0, 2) - - results['transpose'] = transpose - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(keys={self.keys}, transpose_ratio={self.transpose_ratio})') - return repr_str - - -@PIPELINES.register_module() -class GenerateFrameIndiceswithPadding: - """Generate frame index with padding for REDS dataset and Vid4 dataset - during testing. - - Required keys: lq_path, gt_path, key, num_input_frames, max_frame_num - Added or modified keys: lq_path, gt_path - - Args: - padding (str): padding mode, one of - 'replicate' | 'reflection' | 'reflection_circle' | 'circle'. - - Examples: current_idx = 0, num_input_frames = 5 - The generated frame indices under different padding mode: - - replicate: [0, 0, 0, 1, 2] - reflection: [2, 1, 0, 1, 2] - reflection_circle: [4, 3, 0, 1, 2] - circle: [3, 4, 0, 1, 2] - - filename_tmpl (str): Template for file name. Default: '{:08d}'. - """ - - def __init__(self, padding, filename_tmpl='{:08d}'): - if padding not in ('replicate', 'reflection', 'reflection_circle', - 'circle'): - raise ValueError(f'Wrong padding mode {padding}.' - 'Should be "replicate", "reflection", ' - '"reflection_circle", "circle"') - self.padding = padding - self.filename_tmpl = filename_tmpl - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - clip_name, frame_name = results['key'].split(os.sep) - current_idx = int(frame_name) - max_frame_num = results['max_frame_num'] - 1 # start from 0 - num_input_frames = results['num_input_frames'] - num_pad = num_input_frames // 2 - - frame_list = [] - for i in range(current_idx - num_pad, current_idx + num_pad + 1): - if i < 0: - if self.padding == 'replicate': - pad_idx = 0 - elif self.padding == 'reflection': - pad_idx = -i - elif self.padding == 'reflection_circle': - pad_idx = current_idx + num_pad - i - else: - pad_idx = num_input_frames + i - elif i > max_frame_num: - if self.padding == 'replicate': - pad_idx = max_frame_num - elif self.padding == 'reflection': - pad_idx = max_frame_num * 2 - i - elif self.padding == 'reflection_circle': - pad_idx = (current_idx - num_pad) - (i - max_frame_num) - else: - pad_idx = i - num_input_frames - else: - pad_idx = i - frame_list.append(pad_idx) - - lq_path_root = results['lq_path'] - gt_path_root = results['gt_path'] - lq_paths = [ - osp.join(lq_path_root, clip_name, - f'{self.filename_tmpl.format(idx)}.png') - for idx in frame_list - ] - gt_paths = [osp.join(gt_path_root, clip_name, f'{frame_name}.png')] - results['lq_path'] = lq_paths - results['gt_path'] = gt_paths - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ + f"(padding='{self.padding}')" - return repr_str - - -@PIPELINES.register_module() -class GenerateFrameIndices: - """Generate frame index for REDS datasets. It also performs - temporal augmention with random interval. - - Required keys: lq_path, gt_path, key, num_input_frames - Added or modified keys: lq_path, gt_path, interval, reverse - - Args: - interval_list (list[int]): Interval list for temporal augmentation. - It will randomly pick an interval from interval_list and sample - frame index with the interval. - frames_per_clip(int): Number of frames per clips. Default: 99 for - REDS dataset. - """ - - def __init__(self, interval_list, frames_per_clip=99): - self.interval_list = interval_list - self.frames_per_clip = frames_per_clip - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - clip_name, frame_name = results['key'].split( - os.sep) # key example: 000/00000000 - center_frame_idx = int(frame_name) - num_half_frames = results['num_input_frames'] // 2 - - max_frame_num = results.get('max_frame_num', self.frames_per_clip + 1) - frames_per_clip = min(self.frames_per_clip, max_frame_num - 1) - - interval = np.random.choice(self.interval_list) - # ensure not exceeding the borders - start_frame_idx = center_frame_idx - num_half_frames * interval - end_frame_idx = center_frame_idx + num_half_frames * interval - while (start_frame_idx < 0) or (end_frame_idx > frames_per_clip): - center_frame_idx = np.random.randint(0, frames_per_clip + 1) - start_frame_idx = center_frame_idx - num_half_frames * interval - end_frame_idx = center_frame_idx + num_half_frames * interval - frame_name = f'{center_frame_idx:08d}' - neighbor_list = list( - range(center_frame_idx - num_half_frames * interval, - center_frame_idx + num_half_frames * interval + 1, interval)) - - lq_path_root = results['lq_path'] - gt_path_root = results['gt_path'] - lq_path = [ - osp.join(lq_path_root, clip_name, f'{v:08d}.png') - for v in neighbor_list - ] - gt_path = [osp.join(gt_path_root, clip_name, f'{frame_name}.png')] - results['lq_path'] = lq_path - results['gt_path'] = gt_path - results['interval'] = interval - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(interval_list={self.interval_list}, ' - f'frames_per_clip={self.frames_per_clip})') - return repr_str - - -@PIPELINES.register_module() -class TemporalReverse: - """Reverse frame lists for temporal augmentation. - - Required keys are the keys in attributes "lq" and "gt", - added or modified keys are "lq", "gt" and "reverse". - - Args: - keys (list[str]): The frame lists to be reversed. - reverse_ratio (float): The propability to reverse the frame lists. - Default: 0.5. - """ - - def __init__(self, keys, reverse_ratio=0.5): - self.keys = keys - self.reverse_ratio = reverse_ratio - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - reverse = np.random.random() < self.reverse_ratio - - if reverse: - for key in self.keys: - results[key].reverse() - - results['reverse'] = reverse - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(keys={self.keys}, reverse_ratio={self.reverse_ratio})' - return repr_str - - -@PIPELINES.register_module() -class GenerateSegmentIndices: - """Generate frame indices for a segment. It also performs temporal - augmention with random interval. - - Required keys: lq_path, gt_path, key, num_input_frames, sequence_length - Added or modified keys: lq_path, gt_path, interval, reverse - - Args: - interval_list (list[int]): Interval list for temporal augmentation. - It will randomly pick an interval from interval_list and sample - frame index with the interval. - start_idx (int): The index corresponds to the first frame in the - sequence. Default: 0. - filename_tmpl (str): Template for file name. Default: '{:08d}.png'. - """ - - def __init__(self, interval_list, start_idx=0, filename_tmpl='{:08d}.png'): - self.interval_list = interval_list - self.filename_tmpl = filename_tmpl - self.start_idx = start_idx - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - # key example: '000', 'calendar' (sequence name) - clip_name = results['key'] - interval = np.random.choice(self.interval_list) - - self.sequence_length = results['sequence_length'] - num_input_frames = results.get('num_input_frames', - self.sequence_length) - - # randomly select a frame as start - if self.sequence_length - num_input_frames * interval < 0: - raise ValueError('The input sequence is not long enough to ' - 'support the current choice of [interval] or ' - '[num_input_frames].') - start_frame_idx = np.random.randint( - 0, self.sequence_length - num_input_frames * interval + 1) - end_frame_idx = start_frame_idx + num_input_frames * interval - neighbor_list = list(range(start_frame_idx, end_frame_idx, interval)) - neighbor_list = [v + self.start_idx for v in neighbor_list] - - # add the corresponding file paths - lq_path_root = results['lq_path'] - gt_path_root = results['gt_path'] - lq_path = [ - osp.join(lq_path_root, clip_name, self.filename_tmpl.format(v)) - for v in neighbor_list - ] - gt_path = [ - osp.join(gt_path_root, clip_name, self.filename_tmpl.format(v)) - for v in neighbor_list - ] - - results['lq_path'] = lq_path - results['gt_path'] = gt_path - results['interval'] = interval - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(interval_list={self.interval_list})') - return repr_str - - -@PIPELINES.register_module() -class MirrorSequence: - """Extend short sequences (e.g. Vimeo-90K) by mirroring the sequences - - Given a sequence with N frames (x1, ..., xN), extend the sequence to - (x1, ..., xN, xN, ..., x1). - - Args: - keys (list[str]): The frame lists to be extended. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if isinstance(results[key], list): - results[key] = results[key] + results[key][::-1] - else: - raise TypeError('The input must be of class list[nparray]. ' - f'Got {type(results[key])}.') - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys})') - return repr_str - - -@PIPELINES.register_module() -class CopyValues: - """Copy the value of a source key to a destination key. - - - It does the following: results[dst_key] = results[src_key] for - (src_key, dst_key) in zip(src_keys, dst_keys). - - Added keys are the keys in the attribute "dst_keys". - - Args: - src_keys (list[str]): The source keys. - dst_keys (list[str]): The destination keys. - """ - - def __init__(self, src_keys, dst_keys): - - if not isinstance(src_keys, list) or not isinstance(dst_keys, list): - raise AssertionError('"src_keys" and "dst_keys" must be lists.') - - if len(src_keys) != len(dst_keys): - raise ValueError('"src_keys" and "dst_keys" should have the same' - 'number of elements.') - - self.src_keys = src_keys - self.dst_keys = dst_keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict with a key added/modified. - """ - for (src_key, dst_key) in zip(self.src_keys, self.dst_keys): - results[dst_key] = copy.deepcopy(results[src_key]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(src_keys={self.src_keys})') - repr_str += (f'(dst_keys={self.dst_keys})') - return repr_str - - -@PIPELINES.register_module() -class Quantize: - """Quantize and clip the image to [0, 1]. - - It is assumed that the the input has range [0, 1]. - - Modified keys are the attributes specified in "keys". - - Args: - keys (list[str]): The keys whose values are clipped. - """ - - def __init__(self, keys): - self.keys = keys - - def _quantize_clip(self, input_): - is_single_image = False - if isinstance(input_, np.ndarray): - is_single_image = True - input_ = [input_] - - # quantize and clip - input_ = [np.clip((v * 255.0).round(), 0, 255) / 255. for v in input_] - - if is_single_image: - input_ = input_[0] - - return input_ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict with the values of the specified keys are rounded - and clipped. - """ - - for key in self.keys: - results[key] = self._quantize_clip(results[key]) - - return results - - def __repr__(self): - return self.__class__.__name__ - - -@PIPELINES.register_module() -class UnsharpMasking: - """Apply unsharp masking to an image or a sequence of images. - - Args: - kernel_size (int): The kernel_size of the Gaussian kernel. - sigma (float): The standard deviation of the Gaussian. - weight (float): The weight of the "details" in the final output. - threshold (float): Pixel differences larger than this value are - regarded as "details". - keys (list[str]): The keys whose values are processed. - - Added keys are "xxx_unsharp", where "xxx" are the attributes specified - in "keys". - - """ - - def __init__(self, kernel_size, sigma, weight, threshold, keys): - if kernel_size % 2 == 0: - raise ValueError('kernel_size must be an odd number, but ' - f'got {kernel_size}.') - - self.kernel_size = kernel_size - self.sigma = sigma - self.weight = weight - self.threshold = threshold - self.keys = keys - - kernel = cv2.getGaussianKernel(kernel_size, sigma) - self.kernel = np.matmul(kernel, kernel.transpose()) - - def _unsharp_masking(self, imgs): - is_single_image = False - if isinstance(imgs, np.ndarray): - is_single_image = True - imgs = [imgs] - - outputs = [] - for img in imgs: - residue = img - cv2.filter2D(img, -1, self.kernel) - mask = np.float32(np.abs(residue) * 255 > self.threshold) - soft_mask = cv2.filter2D(mask, -1, self.kernel) - sharpened = np.clip(img + self.weight * residue, 0, 1) - - outputs.append(soft_mask * sharpened + (1 - soft_mask) * img) - - if is_single_image: - outputs = outputs[0] - - return outputs - - def __call__(self, results): - for key in self.keys: - results[f'{key}_unsharp'] = self._unsharp_masking(results[key]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, kernel_size={self.kernel_size}, ' - f'sigma={self.sigma}, weight={self.weight}, ' - f'threshold={self.threshold})') - return repr_str diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/compose.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/compose.py deleted file mode 100755 index 0ffb0fd57..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/compose.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -from mmcv.utils import build_from_cfg - -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class Compose: - """Compose a data pipeline with a sequence of transforms. - - Args: - transforms (list[dict | callable]): - Either config dicts of transforms or transform objects. - """ - - def __init__(self, transforms): - assert isinstance(transforms, Sequence) - self.transforms = [] - for transform in transforms: - if isinstance(transform, dict): - transform = build_from_cfg(transform, PIPELINES) - self.transforms.append(transform) - elif callable(transform): - self.transforms.append(transform) - else: - raise TypeError(f'transform must be callable or a dict, ' - f'but got {type(transform)}') - - def __call__(self, data): - """Call function. - - Args: - data (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for t in self.transforms: - data = t(data) - if data is None: - return None - return data - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - format_string += '\n' - format_string += f' {t}' - format_string += '\n)' - return format_string diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/crop.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/crop.py deleted file mode 100755 index 51fa24253..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/crop.py +++ /dev/null @@ -1,749 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -import random - -import mmcv -import numpy as np -from torch.nn.modules.utils import _pair - -from ..registry import PIPELINES -from .utils import random_choose_unknown - - -@PIPELINES.register_module() -class Crop: - """Crop data to specific size for training. - - Args: - keys (Sequence[str]): The images to be cropped. - crop_size (Tuple[int]): Target spatial size (h, w). - random_crop (bool): If set to True, it will random crop - image. Otherwise, it will work as center crop. - is_pad_zeros (bool, optional): Whether to pad the image with 0 if - crop_size is greater than image size. Default: False. - """ - - def __init__(self, keys, crop_size, random_crop=True, is_pad_zeros=False): - if not mmcv.is_tuple_of(crop_size, int): - raise TypeError( - 'Elements of crop_size must be int and crop_size must be' - f' tuple, but got {type(crop_size[0])} in {type(crop_size)}') - - self.keys = keys - self.crop_size = crop_size - self.random_crop = random_crop - self.is_pad_zeros = is_pad_zeros - - def _crop(self, data): - if not isinstance(data, list): - data_list = [data] - else: - data_list = data - - crop_bbox_list = [] - data_list_ = [] - - for item in data_list: - data_h, data_w = item.shape[:2] - crop_h, crop_w = self.crop_size - - if self.is_pad_zeros: - - crop_y_offset, crop_x_offset = 0, 0 - - if crop_h > data_h: - crop_y_offset = (crop_h - data_h) // 2 - if crop_w > data_w: - crop_x_offset = (crop_w - data_w) // 2 - - if crop_y_offset > 0 or crop_x_offset > 0: - pad_width = [(2 * crop_y_offset, 2 * crop_y_offset), - (2 * crop_x_offset, 2 * crop_x_offset)] - if item.ndim == 3: - pad_width.append((0, 0)) - item = np.pad( - item, - tuple(pad_width), - mode='constant', - constant_values=0) - - data_h, data_w = item.shape[:2] - - crop_h = min(data_h, crop_h) - crop_w = min(data_w, crop_w) - - if self.random_crop: - x_offset = np.random.randint(0, data_w - crop_w + 1) - y_offset = np.random.randint(0, data_h - crop_h + 1) - else: - x_offset = max(0, (data_w - crop_w)) // 2 - y_offset = max(0, (data_h - crop_h)) // 2 - - crop_bbox = [x_offset, y_offset, crop_w, crop_h] - item_ = item[y_offset:y_offset + crop_h, - x_offset:x_offset + crop_w, ...] - crop_bbox_list.append(crop_bbox) - data_list_.append(item_) - - if not isinstance(data, list): - return data_list_[0], crop_bbox_list[0] - return data_list_, crop_bbox_list - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - data_, crop_bbox = self._crop(results[k]) - results[k] = data_ - results[k + '_crop_bbox'] = crop_bbox - results['crop_size'] = self.crop_size - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'keys={self.keys}, crop_size={self.crop_size}, ' - f'random_crop={self.random_crop}') - - return repr_str - - -@PIPELINES.register_module() -class RandomResizedCrop(object): - """Crop data to random size and aspect ratio. - - A crop of a random proportion of the original image - and a random aspect ratio of the original aspect ratio is made. - The cropped image is finally resized to a given size specified - by 'crop_size'. Modified keys are the attributes specified in "keys". - - This code is partially adopted from - torchvision.transforms.RandomResizedCrop: - [https://pytorch.org/vision/stable/_modules/torchvision/transforms/\ - transforms.html#RandomResizedCrop]. - - Args: - keys (list[str]): The images to be resized and random-cropped. - crop_size (int | tuple[int]): Target spatial size (h, w). - scale (tuple[float], optional): Range of the proportion of the original - image to be cropped. Default: (0.08, 1.0). - ratio (tuple[float], optional): Range of aspect ratio of the crop. - Default: (3. / 4., 4. / 3.). - interpolation (str, optional): Algorithm used for interpolation. - It can be only either one of the following: - "nearest" | "bilinear" | "bicubic" | "area" | "lanczos". - Default: "bilinear". - """ - - def __init__(self, - keys, - crop_size, - scale=(0.08, 1.0), - ratio=(3. / 4., 4. / 3.), - interpolation='bilinear'): - assert keys, 'Keys should not be empty.' - if isinstance(crop_size, int): - crop_size = (crop_size, crop_size) - elif not mmcv.is_tuple_of(crop_size, int): - raise TypeError('"crop_size" must be an integer ' - 'or a tuple of integers, but got ' - f'{type(crop_size)}') - if not mmcv.is_tuple_of(scale, float): - raise TypeError('"scale" must be a tuple of float, ' - f'but got {type(scale)}') - if not mmcv.is_tuple_of(ratio, float): - raise TypeError('"ratio" must be a tuple of float, ' - f'but got {type(ratio)}') - - self.keys = keys - self.crop_size = crop_size - self.scale = scale - self.ratio = ratio - self.interpolation = interpolation - - def get_params(self, data): - """Get parameters for a random sized crop. - - Args: - data (np.ndarray): Image of type numpy array to be cropped. - - Returns: - A tuple containing the coordinates of the top left corner - and the chosen crop size. - """ - data_h, data_w = data.shape[:2] - area = data_h * data_w - - for _ in range(10): - target_area = random.uniform(*self.scale) * area - log_ratio = (math.log(self.ratio[0]), math.log(self.ratio[1])) - aspect_ratio = math.exp(random.uniform(*log_ratio)) - - crop_w = int(round(math.sqrt(target_area * aspect_ratio))) - crop_h = int(round(math.sqrt(target_area / aspect_ratio))) - - if 0 < crop_w <= data_w and 0 < crop_h <= data_h: - top = random.randint(0, data_h - crop_h) - left = random.randint(0, data_w - crop_w) - return top, left, crop_h, crop_w - - # Fall back to center crop - in_ratio = float(data_w) / float(data_h) - if (in_ratio < min(self.ratio)): - crop_w = data_w - crop_h = int(round(crop_w / min(self.ratio))) - elif (in_ratio > max(self.ratio)): - crop_h = data_h - crop_w = int(round(crop_h * max(self.ratio))) - else: # whole image - crop_w = data_w - crop_h = data_h - top = (data_h - crop_h) // 2 - left = (data_w - crop_w) // 2 - return top, left, crop_h, crop_w - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - top, left, crop_h, crop_w = self.get_params(results[k]) - crop_bbox = [top, left, crop_w, crop_h] - results[k] = results[k][top:top + crop_h, left:left + crop_w, ...] - results[k] = mmcv.imresize( - results[k], - self.crop_size, - return_scale=False, - interpolation=self.interpolation) - results[k + '_crop_bbox'] = crop_bbox - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, crop_size={self.crop_size}, ' - f'scale={self.scale}, ratio={self.ratio}, ' - f'interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class FixedCrop: - """Crop paired data (at a specific position) to specific size for training. - - Args: - keys (Sequence[str]): The images to be cropped. - crop_size (Tuple[int]): Target spatial size (h, w). - crop_pos (Tuple[int]): Specific position (x, y). If set to None, - random initialize the position to crop paired data batch. - """ - - def __init__(self, keys, crop_size, crop_pos=None): - if not mmcv.is_tuple_of(crop_size, int): - raise TypeError( - 'Elements of crop_size must be int and crop_size must be' - f' tuple, but got {type(crop_size[0])} in {type(crop_size)}') - if not mmcv.is_tuple_of(crop_pos, int) and (crop_pos is not None): - raise TypeError( - 'Elements of crop_pos must be int and crop_pos must be' - f' tuple or None, but got {type(crop_pos[0])} in ' - f'{type(crop_pos)}') - - self.keys = keys - self.crop_size = crop_size - self.crop_pos = crop_pos - - def _crop(self, data, x_offset, y_offset, crop_w, crop_h): - crop_bbox = [x_offset, y_offset, crop_w, crop_h] - data_ = data[y_offset:y_offset + crop_h, x_offset:x_offset + crop_w, - ...] - return data_, crop_bbox - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - if isinstance(results[self.keys[0]], list): - data_h, data_w = results[self.keys[0]][0].shape[:2] - else: - data_h, data_w = results[self.keys[0]].shape[:2] - crop_h, crop_w = self.crop_size - crop_h = min(data_h, crop_h) - crop_w = min(data_w, crop_w) - - if self.crop_pos is None: - x_offset = np.random.randint(0, data_w - crop_w + 1) - y_offset = np.random.randint(0, data_h - crop_h + 1) - else: - x_offset, y_offset = self.crop_pos - crop_w = min(data_w - x_offset, crop_w) - crop_h = min(data_h - y_offset, crop_h) - - for k in self.keys: - images = results[k] - is_list = isinstance(images, list) - if not is_list: - images = [images] - cropped_images = [] - crop_bbox = None - for image in images: - # In fixed crop for paired images, sizes should be the same - if (image.shape[0] != data_h or image.shape[1] != data_w): - raise ValueError( - 'The sizes of paired images should be the same. ' - f'Expected ({data_h}, {data_w}), ' - f'but got ({image.shape[0]}, ' - f'{image.shape[1]}).') - data_, crop_bbox = self._crop(image, x_offset, y_offset, - crop_w, crop_h) - cropped_images.append(data_) - results[k + '_crop_bbox'] = crop_bbox - if not is_list: - cropped_images = cropped_images[0] - results[k] = cropped_images - results['crop_size'] = self.crop_size - results['crop_pos'] = self.crop_pos - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'keys={self.keys}, crop_size={self.crop_size}, ' - f'crop_pos={self.crop_pos}') - return repr_str - - -@PIPELINES.register_module() -class PairedRandomCrop: - """Paried random crop. - - It crops a pair of lq and gt images with corresponding locations. - It also supports accepting lq list and gt list. - Required keys are "scale", "lq", and "gt", - added or modified keys are "lq" and "gt". - - Args: - gt_patch_size (int): cropped gt patch size. - """ - - def __init__(self, gt_patch_size): - self.gt_patch_size = gt_patch_size - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - scale = results['scale'] - lq_patch_size = self.gt_patch_size // scale - - lq_is_list = isinstance(results['lq'], list) - if not lq_is_list: - results['lq'] = [results['lq']] - gt_is_list = isinstance(results['gt'], list) - if not gt_is_list: - results['gt'] = [results['gt']] - - h_lq, w_lq, _ = results['lq'][0].shape - h_gt, w_gt, _ = results['gt'][0].shape - - if h_gt != h_lq * scale or w_gt != w_lq * scale: - raise ValueError( - f'Scale mismatches. GT ({h_gt}, {w_gt}) is not {scale}x ' - f'multiplication of LQ ({h_lq}, {w_lq}).') - if h_lq < lq_patch_size or w_lq < lq_patch_size: - raise ValueError( - f'LQ ({h_lq}, {w_lq}) is smaller than patch size ' - f'({lq_patch_size}, {lq_patch_size}). Please check ' - f'{results["lq_path"][0]} and {results["gt_path"][0]}.') - - # randomly choose top and left coordinates for lq patch - top = np.random.randint(h_lq - lq_patch_size + 1) - left = np.random.randint(w_lq - lq_patch_size + 1) - # crop lq patch - results['lq'] = [ - v[top:top + lq_patch_size, left:left + lq_patch_size, ...] - for v in results['lq'] - ] - # crop corresponding gt patch - top_gt, left_gt = int(top * scale), int(left * scale) - results['gt'] = [ - v[top_gt:top_gt + self.gt_patch_size, - left_gt:left_gt + self.gt_patch_size, ...] for v in results['gt'] - ] - - if not lq_is_list: - results['lq'] = results['lq'][0] - if not gt_is_list: - results['gt'] = results['gt'][0] - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(gt_patch_size={self.gt_patch_size})' - return repr_str - - -@PIPELINES.register_module() -class CropAroundCenter: - """Randomly crop the images around unknown area in the center 1/4 images. - - This cropping strategy is adopted in GCA matting. The `unknown area` is the - same as `semi-transparent area`. - https://arxiv.org/pdf/2001.04069.pdf - - It retains the center 1/4 images and resizes the images to 'crop_size'. - Required keys are "fg", "bg", "trimap" and "alpha", added or modified keys - are "crop_bbox", "fg", "bg", "trimap" and "alpha". - - Args: - crop_size (int | tuple): Desired output size. If int, square crop is - applied. - """ - - def __init__(self, crop_size): - if mmcv.is_tuple_of(crop_size, int): - assert len(crop_size) == 2, 'length of crop_size must be 2.' - elif not isinstance(crop_size, int): - raise TypeError('crop_size must be int or a tuple of int, but got ' - f'{type(crop_size)}') - self.crop_size = _pair(crop_size) - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - fg = results['fg'] - alpha = results['alpha'] - trimap = results['trimap'] - bg = results['bg'] - h, w = fg.shape[:2] - assert bg.shape == fg.shape, (f'shape of bg {bg.shape} should be the ' - f'same as fg {fg.shape}.') - - crop_h, crop_w = self.crop_size - # Make sure h >= crop_h, w >= crop_w. If not, rescale imgs - rescale_ratio = max(crop_h / h, crop_w / w) - if rescale_ratio > 1: - new_h = max(int(h * rescale_ratio), crop_h) - new_w = max(int(w * rescale_ratio), crop_w) - fg = mmcv.imresize(fg, (new_w, new_h), interpolation='nearest') - alpha = mmcv.imresize( - alpha, (new_w, new_h), interpolation='nearest') - trimap = mmcv.imresize( - trimap, (new_w, new_h), interpolation='nearest') - bg = mmcv.imresize(bg, (new_w, new_h), interpolation='bicubic') - h, w = new_h, new_w - - # resize to 1/4 to ignore small unknown patches - small_trimap = mmcv.imresize( - trimap, (w // 4, h // 4), interpolation='nearest') - # find unknown area in center 1/4 region - margin_h, margin_w = crop_h // 2, crop_w // 2 - sample_area = small_trimap[margin_h // 4:(h - margin_h) // 4, - margin_w // 4:(w - margin_w) // 4] - unknown_xs, unknown_ys = np.where(sample_area == 128) - unknown_num = len(unknown_xs) - if unknown_num < 10: - # too few unknown area in the center, crop from the whole image - top = np.random.randint(0, h - crop_h + 1) - left = np.random.randint(0, w - crop_w + 1) - else: - idx = np.random.randint(unknown_num) - top = unknown_xs[idx] * 4 - left = unknown_ys[idx] * 4 - bottom = top + crop_h - right = left + crop_w - - results['fg'] = fg[top:bottom, left:right] - results['alpha'] = alpha[top:bottom, left:right] - results['trimap'] = trimap[top:bottom, left:right] - results['bg'] = bg[top:bottom, left:right] - results['crop_bbox'] = (left, top, right, bottom) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(crop_size={self.crop_size})' - - -@PIPELINES.register_module() -class CropAroundUnknown: - """Crop around unknown area with a randomly selected scale. - - Randomly select the w and h from a list of (w, h). - Required keys are the keys in argument `keys`, added or - modified keys are "crop_bbox" and the keys in argument `keys`. - This class assumes value of "alpha" ranges from 0 to 255. - - Args: - keys (Sequence[str]): The images to be cropped. It must contain - 'alpha'. If unknown_source is set to 'trimap', then it must also - contain 'trimap'. - crop_sizes (list[int | tuple[int]]): List of (w, h) to be selected. - unknown_source (str, optional): Unknown area to select from. It must be - 'alpha' or 'tirmap'. Default to 'alpha'. - interpolations (str | list[str], optional): Interpolation method of - mmcv.imresize. The interpolation operation will be applied when - image size is smaller than the crop_size. If given as a list of - str, it should have the same length as `keys`. Or if given as a - str all the keys will be resized with the same method. - Default to 'bilinear'. - """ - - def __init__(self, - keys, - crop_sizes, - unknown_source='alpha', - interpolations='bilinear'): - if 'alpha' not in keys: - raise ValueError(f'"alpha" must be in keys, but got {keys}') - self.keys = keys - - if not isinstance(crop_sizes, list): - raise TypeError( - f'Crop sizes must be list, but got {type(crop_sizes)}.') - self.crop_sizes = [_pair(crop_size) for crop_size in crop_sizes] - if not mmcv.is_tuple_of(self.crop_sizes[0], int): - raise TypeError('Elements of crop_sizes must be int or tuple of ' - f'int, but got {type(self.crop_sizes[0][0])}.') - - if unknown_source not in ['alpha', 'trimap']: - raise ValueError('unknown_source must be "alpha" or "trimap", ' - f'but got {unknown_source}') - if unknown_source not in keys: - # it could only be trimap, since alpha is checked before - raise ValueError( - 'if unknown_source is "trimap", it must also be set in keys') - self.unknown_source = unknown_source - - if isinstance(interpolations, str): - self.interpolations = [interpolations] * len(self.keys) - elif mmcv.is_list_of(interpolations, - str) and len(interpolations) == len(self.keys): - self.interpolations = interpolations - else: - raise TypeError( - 'interpolations must be a str or list of str with ' - f'the same length as keys, but got {interpolations}') - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - h, w = results[self.keys[0]].shape[:2] - - rand_ind = np.random.randint(len(self.crop_sizes)) - crop_h, crop_w = self.crop_sizes[rand_ind] - - # Make sure h >= crop_h, w >= crop_w. If not, rescale imgs - rescale_ratio = max(crop_h / h, crop_w / w) - if rescale_ratio > 1: - h = max(int(h * rescale_ratio), crop_h) - w = max(int(w * rescale_ratio), crop_w) - for key, interpolation in zip(self.keys, self.interpolations): - results[key] = mmcv.imresize( - results[key], (w, h), interpolation=interpolation) - - # Select the cropping top-left point which is an unknown pixel - if self.unknown_source == 'alpha': - unknown = (results['alpha'] > 0) & (results['alpha'] < 255) - else: - unknown = results['trimap'] == 128 - top, left = random_choose_unknown(unknown.squeeze(), (crop_h, crop_w)) - - bottom = top + crop_h - right = left + crop_w - - for key in self.keys: - results[key] = results[key][top:bottom, left:right] - results['crop_bbox'] = (left, top, right, bottom) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, crop_sizes={self.crop_sizes}, ' - f"unknown_source='{self.unknown_source}', " - f'interpolations={self.interpolations})') - return repr_str - - -@PIPELINES.register_module() -class CropAroundFg: - """Crop around the whole foreground in the segmentation mask. - - Required keys are "seg" and the keys in argument `keys`. - Meanwhile, "seg" must be in argument `keys`. Added or modified keys are - "crop_bbox" and the keys in argument `keys`. - - Args: - keys (Sequence[str]): The images to be cropped. It must contain - 'seg'. - bd_ratio_range (tuple, optional): The range of the boundary (bd) ratio - to select from. The boundary ratio is the ratio of the boundary to - the minimal bbox that contains the whole foreground given by - segmentation. Default to (0.1, 0.4). - test_mode (bool): Whether use test mode. In test mode, the tight crop - area of foreground will be extended to the a square. - Default to False. - """ - - def __init__(self, keys, bd_ratio_range=(0.1, 0.4), test_mode=False): - if 'seg' not in keys: - raise ValueError(f'"seg" must be in keys, but got {keys}') - if (not mmcv.is_tuple_of(bd_ratio_range, float) - or len(bd_ratio_range) != 2): - raise TypeError('bd_ratio_range must be a tuple of 2 int, but got ' - f'{bd_ratio_range}') - self.keys = keys - self.bd_ratio_range = bd_ratio_range - self.test_mode = test_mode - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - seg = results['seg'] - height, width = seg.shape[:2] - - # get foreground bbox - fg_coor = np.array(np.where(seg)) - top, left = np.amin(fg_coor, axis=1) - bottom, right = np.amax(fg_coor, axis=1) - - # enlarge bbox - long_side = np.maximum(bottom - top, right - left) - if self.test_mode: - bottom = top + long_side - right = left + long_side - boundary_ratio = np.random.uniform(*self.bd_ratio_range) - boundary = int(np.round(boundary_ratio * long_side)) - # NOTE: Different from the original repo, we keep track of the four - # corners of the bbox (left, top, right, bottom) while the original - # repo use (top, left, height, width) to represent bbox. This may - # introduce an difference of 1 pixel. - top = max(top - boundary, 0) - left = max(left - boundary, 0) - bottom = min(bottom + boundary, height) - right = min(right + boundary, width) - - for key in self.keys: - results[key] = results[key][top:bottom, left:right] - results['crop_bbox'] = (left, top, right, bottom) - return results - - -@PIPELINES.register_module() -class ModCrop: - """Mod crop gt images, used during testing. - - Required keys are "scale" and "gt", - added or modified keys are "gt". - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - img = results['gt'].copy() - scale = results['scale'] - if img.ndim in [2, 3]: - h, w = img.shape[0], img.shape[1] - h_remainder, w_remainder = h % scale, w % scale - img = img[:h - h_remainder, :w - w_remainder, ...] - else: - raise ValueError(f'Wrong img ndim: {img.ndim}.') - results['gt'] = img - return results - - -@PIPELINES.register_module() -class CropLike: - """Crop/pad the image in the target_key according to the size of image - in the reference_key . - - Args: - target_key (str): The key needs to be cropped. - reference_key (str | None): The reference key, need its size. - Default: None. - """ - - def __init__(self, target_key, reference_key=None): - - assert reference_key and target_key - self.target_key = target_key - self.reference_key = reference_key - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - Require self.target_key and self.reference_key. - - Returns: - dict: A dict containing the processed data and information. - Modify self.target_key. - """ - size = results[self.reference_key].shape - old_image = results[self.target_key] - old_size = old_image.shape - h, w = old_size[:2] - new_size = size[:2] + old_size[2:] - h_cover, w_cover = min(h, size[0]), min(w, size[1]) - - format_image = np.zeros(new_size, dtype=old_image.dtype) - format_image[:h_cover, :w_cover] = old_image[:h_cover, :w_cover] - results[self.target_key] = format_image - - return results - - def __repr__(self): - return (self.__class__.__name__ + f' target_key={self.target_key}, ' + - f'reference_key={self.reference_key}') diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/formating.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/formating.py deleted file mode 100755 index 6ae98c050..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/formating.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import mmcv -import numpy as np -import torch -from mmcv.parallel import DataContainer as DC -from torch.nn import functional as F - -from ..registry import PIPELINES - - -def to_tensor(data): - """Convert objects of various python types to :obj:`torch.Tensor`. - - Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, - :class:`Sequence`, :class:`int` and :class:`float`. - """ - if isinstance(data, torch.Tensor): - return data - if isinstance(data, np.ndarray): - return torch.from_numpy(data) - if isinstance(data, Sequence) and not mmcv.is_str(data): - return torch.tensor(data) - if isinstance(data, int): - return torch.LongTensor([data]) - if isinstance(data, float): - return torch.FloatTensor([data]) - - raise TypeError(f'type {type(data)} cannot be converted to tensor.') - - -@PIPELINES.register_module() -class ToTensor: - """Convert some values in results dict to `torch.Tensor` type - in data loader pipeline. - - Args: - keys (Sequence[str]): Required keys to be converted. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - results[key] = to_tensor(results[key]) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class ImageToTensor: - """Convert image type to `torch.Tensor` type. - - Args: - keys (Sequence[str]): Required keys to be converted. - to_float32 (bool): Whether convert numpy image array to np.float32 - before converted to tensor. Default: True. - """ - - def __init__(self, keys, to_float32=True): - self.keys = keys - self.to_float32 = to_float32 - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - # deal with gray scale img: expand a color channel - if len(results[key].shape) == 2: - results[key] = results[key][..., None] - if self.to_float32 and not isinstance(results[key], np.float32): - results[key] = results[key].astype(np.float32) - results[key] = to_tensor(results[key].transpose(2, 0, 1)) - return results - - def __repr__(self): - return self.__class__.__name__ + ( - f'(keys={self.keys}, to_float32={self.to_float32})') - - -@PIPELINES.register_module() -class FramesToTensor(ImageToTensor): - """Convert frames type to `torch.Tensor` type. - - It accepts a list of frames, converts each to `torch.Tensor` type and then - concatenates in a new dimension (dim=0). - - Args: - keys (Sequence[str]): Required keys to be converted. - to_float32 (bool): Whether convert numpy image array to np.float32 - before converted to tensor. Default: True. - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if not isinstance(results[key], list): - raise TypeError(f'results["{key}"] should be a list, ' - f'but got {type(results[key])}') - for idx, v in enumerate(results[key]): - # deal with gray scale img: expand a color channel - if len(v.shape) == 2: - v = v[..., None] - if self.to_float32 and not isinstance(v, np.float32): - v = v.astype(np.float32) - results[key][idx] = to_tensor(v.transpose(2, 0, 1)) - results[key] = torch.stack(results[key], dim=0) - if results[key].size(0) == 1: - results[key].squeeze_() - return results - - -@PIPELINES.register_module() -class GetMaskedImage: - """Get masked image. - - Args: - img_name (str): Key for clean image. - mask_name (str): Key for mask image. The mask shape should be - (h, w, 1) while '1' indicate holes and '0' indicate valid - regions. - """ - - def __init__(self, img_name='gt_img', mask_name='mask'): - self.img_name = img_name - self.mask_name = mask_name - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - clean_img = results[self.img_name] - mask = results[self.mask_name] - - masked_img = clean_img * (1. - mask) - results['masked_img'] = masked_img - - return results - - def __repr__(self): - return self.__class__.__name__ + ( - f"(img_name='{self.img_name}', mask_name='{self.mask_name}')") - - -@PIPELINES.register_module() -class FormatTrimap: - """Convert trimap (tensor) to one-hot representation. - - It transforms the trimap label from (0, 128, 255) to (0, 1, 2). If - ``to_onehot`` is set to True, the trimap will convert to one-hot tensor of - shape (3, H, W). Required key is "trimap", added or modified key are - "trimap" and "to_onehot". - - Args: - to_onehot (bool): whether convert trimap to one-hot tensor. Default: - ``False``. - """ - - def __init__(self, to_onehot=False): - self.to_onehot = to_onehot - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - trimap = results['trimap'].squeeze() - trimap[trimap == 128] = 1 - trimap[trimap == 255] = 2 - if self.to_onehot: - trimap = F.one_hot(trimap.to(torch.long), num_classes=3) - trimap = trimap.permute(2, 0, 1) - else: - trimap = trimap[None, ...] # expand the channels dimension - results['trimap'] = trimap.float() - results['meta'].data['to_onehot'] = self.to_onehot - return results - - def __repr__(self): - return self.__class__.__name__ + f'(to_onehot={self.to_onehot})' - - -@PIPELINES.register_module() -class Collect: - """Collect data from the loader relevant to the specific task. - - This is usually the last stage of the data loader pipeline. Typically keys - is set to some subset of "img", "gt_labels". - - The "img_meta" item is always populated. The contents of the "meta" - dictionary depends on "meta_keys". - - Args: - keys (Sequence[str]): Required keys to be collected. - meta_keys (Sequence[str]): Required keys to be collected to "meta". - Default: None. - """ - - def __init__(self, keys, meta_keys=None): - self.keys = keys - self.meta_keys = meta_keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - data = {} - img_meta = {} - for key in self.meta_keys: - img_meta[key] = results[key] - data['meta'] = DC(img_meta, cpu_only=True) - for key in self.keys: - data[key] = results[key] - return data - - def __repr__(self): - return self.__class__.__name__ + ( - f'(keys={self.keys}, meta_keys={self.meta_keys})') diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/loading.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/loading.py deleted file mode 100755 index c7f162732..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/loading.py +++ /dev/null @@ -1,562 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from pathlib import Path - -import mmcv -import numpy as np -from mmcv.fileio import FileClient - -from mmedit.core.mask import (bbox2mask, brush_stroke_mask, get_irregular_mask, - random_bbox) -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class LoadImageFromFile: - """Load image from file. - - Args: - io_backend (str): io backend where images are store. Default: 'disk'. - key (str): Keys in results to find corresponding path. Default: 'gt'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - convert_to (str | None): The color space of the output image. If None, - no conversion is conducted. Default: None. - save_original_img (bool): If True, maintain a copy of the image in - `results` dict with name of `f'ori_{key}'`. Default: False. - use_cache (bool): If True, load all images at once. Default: False. - backend (str): The image loading backend type. Options are `cv2`, - `pillow`, and 'turbojpeg'. Default: None. - kwargs (dict): Args for file client. - """ - - def __init__(self, - io_backend='disk', - key='gt', - flag='color', - channel_order='bgr', - convert_to=None, - save_original_img=False, - use_cache=False, - backend=None, - **kwargs): - - self.io_backend = io_backend - self.key = key - self.flag = flag - self.save_original_img = save_original_img - self.channel_order = channel_order - self.convert_to = convert_to - self.kwargs = kwargs - self.file_client = None - self.use_cache = use_cache - self.cache = dict() if use_cache else None - self.backend = backend - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - filepath = str(results[f'{self.key}_path']) - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - if self.use_cache: - if filepath in self.cache: - img = self.cache[filepath] - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - self.cache[filepath] = img - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - - if self.convert_to is not None: - if self.channel_order == 'bgr' and self.convert_to.lower() == 'y': - img = mmcv.bgr2ycbcr(img, y_only=True) - elif self.channel_order == 'rgb': - img = mmcv.rgb2ycbcr(img, y_only=True) - else: - raise ValueError('Currently support only "bgr2ycbcr" or ' - '"bgr2ycbcr".') - if img.ndim == 2: - img = np.expand_dims(img, axis=2) - - results[self.key] = img - results[f'{self.key}_path'] = filepath - results[f'{self.key}_ori_shape'] = img.shape - if self.save_original_img: - results[f'ori_{self.key}'] = img.copy() - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(io_backend={self.io_backend}, key={self.key}, ' - f'flag={self.flag}, save_original_img={self.save_original_img}, ' - f'channel_order={self.channel_order}, use_cache={self.use_cache})') - return repr_str - - -@PIPELINES.register_module() -class LoadImageFromFileList(LoadImageFromFile): - """Load image from file list. - - It accepts a list of path and read each frame from each path. A list - of frames will be returned. - - Args: - io_backend (str): io backend where images are store. Default: 'disk'. - key (str): Keys in results to find corresponding path. Default: 'gt'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - convert_to (str | None): The color space of the output image. If None, - no conversion is conducted. Default: None. - save_original_img (bool): If True, maintain a copy of the image in - `results` dict with name of `f'ori_{key}'`. Default: False. - use_cache (bool): If True, load all images at once. Default: False. - backend (str): The image loading backend type. Options are `cv2`, - `pillow`, and 'turbojpeg'. Default: None. - kwargs (dict): Args for file client. - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - filepaths = results[f'{self.key}_path'] - if not isinstance(filepaths, list): - raise TypeError( - f'filepath should be list, but got {type(filepaths)}') - - filepaths = [str(v) for v in filepaths] - - imgs = [] - shapes = [] - if self.save_original_img: - ori_imgs = [] - for filepath in filepaths: - if self.use_cache: - if filepath in self.cache: - img = self.cache[filepath] - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - self.cache[filepath] = img - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - - # convert to y-channel, if specified - if self.convert_to is not None: - if self.channel_order == 'bgr' and self.convert_to.lower( - ) == 'y': - img = mmcv.bgr2ycbcr(img, y_only=True) - elif self.channel_order == 'rgb': - img = mmcv.rgb2ycbcr(img, y_only=True) - else: - raise ValueError('Currently support only "bgr2ycbcr" or ' - '"bgr2ycbcr".') - - if img.ndim == 2: - img = np.expand_dims(img, axis=2) - - imgs.append(img) - shapes.append(img.shape) - if self.save_original_img: - ori_imgs.append(img.copy()) - - results[self.key] = imgs - results[f'{self.key}_path'] = filepaths - results[f'{self.key}_ori_shape'] = shapes - if self.save_original_img: - results[f'ori_{self.key}'] = ori_imgs - - return results - - -@PIPELINES.register_module() -class RandomLoadResizeBg: - """Randomly load a background image and resize it. - - Required key is "fg", added key is "bg". - - Args: - bg_dir (str): Path of directory to load background images from. - io_backend (str): io backend where images are store. Default: 'disk'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - kwargs (dict): Args for file client. - """ - - def __init__(self, - bg_dir, - io_backend='disk', - flag='color', - channel_order='bgr', - **kwargs): - self.bg_dir = bg_dir - self.bg_list = list(mmcv.scandir(bg_dir)) - self.io_backend = io_backend - self.flag = flag - self.channel_order = channel_order - self.kwargs = kwargs - self.file_client = None - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - h, w = results['fg'].shape[:2] - idx = np.random.randint(len(self.bg_list)) - filepath = Path(self.bg_dir).joinpath(self.bg_list[idx]) - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, flag=self.flag, channel_order=self.channel_order) # HWC - bg = mmcv.imresize(img, (w, h), interpolation='bicubic') - results['bg'] = bg - return results - - def __repr__(self): - return self.__class__.__name__ + f"(bg_dir='{self.bg_dir}')" - - -@PIPELINES.register_module() -class LoadMask: - """Load Mask for multiple types. - - For different types of mask, users need to provide the corresponding - config dict. - - Example config for bbox: - - .. code-block:: python - - config = dict(img_shape=(256, 256), max_bbox_shape=128) - - Example config for irregular: - - .. code-block:: python - - config = dict( - img_shape=(256, 256), - num_vertices=(4, 12), - max_angle=4., - length_range=(10, 100), - brush_width=(10, 40), - area_ratio_range=(0.15, 0.5)) - - Example config for ff: - - .. code-block:: python - - config = dict( - img_shape=(256, 256), - num_vertices=(4, 12), - mean_angle=1.2, - angle_range=0.4, - brush_width=(12, 40)) - - Example config for set: - - .. code-block:: python - - config = dict( - mask_list_file='xxx/xxx/ooxx.txt', - prefix='/xxx/xxx/ooxx/', - io_backend='disk', - flag='unchanged', - file_client_kwargs=dict() - ) - - The mask_list_file contains the list of mask file name like this: - test1.jpeg - test2.jpeg - ... - ... - - The prefix gives the data path. - - Args: - mask_mode (str): Mask mode in ['bbox', 'irregular', 'ff', 'set', - 'file']. - * bbox: square bounding box masks. - * irregular: irregular holes. - * ff: free-form holes from DeepFillv2. - * set: randomly get a mask from a mask set. - * file: get mask from 'mask_path' in results. - mask_config (dict): Params for creating masks. Each type of mask needs - different configs. - """ - - def __init__(self, mask_mode='bbox', mask_config=None): - self.mask_mode = mask_mode - self.mask_config = dict() if mask_config is None else mask_config - assert isinstance(self.mask_config, dict) - - # set init info if needed in some modes - self._init_info() - - def _init_info(self): - if self.mask_mode == 'set': - # get mask list information - self.mask_list = [] - mask_list_file = self.mask_config['mask_list_file'] - with open(mask_list_file, 'r') as f: - for line in f: - line_split = line.strip().split(' ') - mask_name = line_split[0] - self.mask_list.append( - Path(self.mask_config['prefix']).joinpath(mask_name)) - self.mask_set_size = len(self.mask_list) - self.io_backend = self.mask_config['io_backend'] - self.flag = self.mask_config['flag'] - self.file_client_kwargs = self.mask_config['file_client_kwargs'] - self.file_client = None - elif self.mask_mode == 'file': - self.io_backend = 'disk' - self.flag = 'unchanged' - self.file_client_kwargs = dict() - self.file_client = None - - def _get_random_mask_from_set(self): - if self.file_client is None: - self.file_client = FileClient(self.io_backend, - **self.file_client_kwargs) - # minus 1 to avoid out of range error - mask_idx = np.random.randint(0, self.mask_set_size) - mask_bytes = self.file_client.get(self.mask_list[mask_idx]) - mask = mmcv.imfrombytes(mask_bytes, flag=self.flag) # HWC, BGR - if mask.ndim == 2: - mask = np.expand_dims(mask, axis=2) - else: - mask = mask[:, :, 0:1] - - mask[mask > 0] = 1. - return mask - - def _get_mask_from_file(self, path): - if self.file_client is None: - self.file_client = FileClient(self.io_backend, - **self.file_client_kwargs) - mask_bytes = self.file_client.get(path) - mask = mmcv.imfrombytes(mask_bytes, flag=self.flag) # HWC, BGR - if mask.ndim == 2: - mask = np.expand_dims(mask, axis=2) - else: - mask = mask[:, :, 0:1] - - mask[mask > 0] = 1. - return mask - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - if self.mask_mode == 'bbox': - mask_bbox = random_bbox(**self.mask_config) - mask = bbox2mask(self.mask_config['img_shape'], mask_bbox) - results['mask_bbox'] = mask_bbox - elif self.mask_mode == 'irregular': - mask = get_irregular_mask(**self.mask_config) - elif self.mask_mode == 'set': - mask = self._get_random_mask_from_set() - elif self.mask_mode == 'ff': - mask = brush_stroke_mask(**self.mask_config) - elif self.mask_mode == 'file': - mask = self._get_mask_from_file(results['mask_path']) - else: - raise NotImplementedError( - f'Mask mode {self.mask_mode} has not been implemented.') - results['mask'] = mask - return results - - def __repr__(self): - return self.__class__.__name__ + f"(mask_mode='{self.mask_mode}')" - - -@PIPELINES.register_module() -class GetSpatialDiscountMask: - """Get spatial discounting mask constant. - - Spatial discounting mask is first introduced in: - Generative Image Inpainting with Contextual Attention. - - Args: - gamma (float, optional): Gamma for computing spatial discounting. - Defaults to 0.99. - beta (float, optional): Beta for computing spatial discounting. - Defaults to 1.5. - """ - - def __init__(self, gamma=0.99, beta=1.5): - self.gamma = gamma - self.beta = beta - - def spatial_discount_mask(self, mask_width, mask_height): - """Generate spatial discounting mask constant. - - Args: - mask_width (int): The width of bbox hole. - mask_height (int): The height of bbox height. - - Returns: - np.ndarray: Spatial discounting mask. - """ - w, h = np.meshgrid(np.arange(mask_width), np.arange(mask_height)) - grid_stack = np.stack([h, w], axis=2) - mask_values = (self.gamma**(np.minimum( - grid_stack, [mask_height - 1, mask_width - 1] - grid_stack) * - self.beta)).max( - axis=2, keepdims=True) - - return mask_values - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - mask_bbox = results['mask_bbox'] - mask = results['mask'] - mask_height, mask_width = mask_bbox[-2:] - discount_hole = self.spatial_discount_mask(mask_width, mask_height) - discount_mask = np.zeros_like(mask) - discount_mask[mask_bbox[0]:mask_bbox[0] + mask_height, - mask_bbox[1]:mask_bbox[1] + mask_width, - ...] = discount_hole - - results['discount_mask'] = discount_mask - - return results - - def __repr__(self): - return self.__class__.__name__ + (f'(gamma={self.gamma}, ' - f'beta={self.beta})') - - -@PIPELINES.register_module() -class LoadPairedImageFromFile(LoadImageFromFile): - """Load a pair of images from file. - - Each sample contains a pair of images, which are concatenated in the w - dimension (a|b). This is a special loading class for generation paired - dataset. It loads a pair of images as the common loader does and crops - it into two images with the same shape in different domains. - - Required key is "pair_path". Added or modified keys are "pair", - "pair_ori_shape", "ori_pair", "img_a", "img_b", "img_a_path", - "img_b_path", "img_a_ori_shape", "img_b_ori_shape", "ori_img_a" and - "ori_img_b". - - Args: - io_backend (str): io backend where images are store. Default: 'disk'. - key (str): Keys in results to find corresponding path. Default: 'gt'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - save_original_img (bool): If True, maintain a copy of the image in - `results` dict with name of `f'ori_{key}'`. Default: False. - kwargs (dict): Args for file client. - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - filepath = str(results[f'{self.key}_path']) - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, flag=self.flag, channel_order=self.channel_order) # HWC - if img.ndim == 2: - img = np.expand_dims(img, axis=2) - - results[self.key] = img - results[f'{self.key}_path'] = filepath - results[f'{self.key}_ori_shape'] = img.shape - if self.save_original_img: - results[f'ori_{self.key}'] = img.copy() - - # crop pair into a and b - w = img.shape[1] - if w % 2 != 0: - raise ValueError( - f'The width of image pair must be even number, but got {w}.') - new_w = w // 2 - img_a = img[:, :new_w, :] - img_b = img[:, new_w:, :] - - results['img_a'] = img_a - results['img_b'] = img_b - results['img_a_path'] = filepath - results['img_b_path'] = filepath - results['img_a_ori_shape'] = img_a.shape - results['img_b_ori_shape'] = img_b.shape - if self.save_original_img: - results['ori_img_a'] = img_a.copy() - results['ori_img_b'] = img_b.copy() - - return results diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/matlab_like_resize.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/matlab_like_resize.py deleted file mode 100755 index 5de48f6f2..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/matlab_like_resize.py +++ /dev/null @@ -1,275 +0,0 @@ -# This code is referenced from matlab_imresize with modifications -# Reference: https://github.com/fatheral/matlab_imresize/blob/master/imresize.py # noqa -# Original licence: Copyright (c) 2020 fatheral, under the MIT License. -import numpy as np - -from ..registry import PIPELINES - - -def get_size_from_scale(input_size, scale_factor): - """Get the output size given input size and scale factor. - - Args: - input_size (tuple): The size of the input image. - scale_factor (float): The resize factor. - - Returns: - list[int]: The size of the output image. - """ - - output_shape = [ - int(np.ceil(scale * shape)) - for (scale, shape) in zip(scale_factor, input_size) - ] - - return output_shape - - -def get_scale_from_size(input_size, output_size): - """Get the scale factor given input size and output size. - - Args: - input_size (tuple(int)): The size of the input image. - output_size (tuple(int)): The size of the output image. - - Returns: - list[float]: The scale factor of each dimension. - """ - - scale = [ - 1.0 * output_shape / input_shape - for (input_shape, output_shape) in zip(input_size, output_size) - ] - - return scale - - -def _cubic(x): - """ Cubic function. - - Args: - x (ndarray): The distance from the center position. - - Returns: - ndarray: The weight corresponding to a particular distance. - - """ - - x = np.array(x, dtype=np.float32) - x_abs = np.abs(x) - x_abs_sq = x_abs**2 - x_abs_cu = x_abs_sq * x_abs - - # if |x| <= 1: y = 1.5|x|^3 - 2.5|x|^2 + 1 - # if 1 < |x| <= 2: -0.5|x|^3 + 2.5|x|^2 - 4|x| + 2 - f = (1.5 * x_abs_cu - 2.5 * x_abs_sq + 1) * (x_abs <= 1) + ( - -0.5 * x_abs_cu + 2.5 * x_abs_sq - 4 * x_abs + 2) * ((1 < x_abs) & - (x_abs <= 2)) - - return f - - -def get_weights_indices(input_length, output_length, scale, kernel, - kernel_width): - """Get weights and indices for interpolation. - - Args: - input_length (int): Length of the input sequence. - output_length (int): Length of the output sequence. - scale (float): Scale factor. - kernel (func): The kernel used for resizing. - kernel_width (int): The width of the kernel. - - Returns: - list[ndarray]: The weights and the indices for interpolation. - - - """ - if scale < 1: # modified kernel for antialiasing - - def h(x): - return scale * kernel(scale * x) - - kernel_width = 1.0 * kernel_width / scale - else: - h = kernel - kernel_width = kernel_width - - # coordinates of output - x = np.arange(1, output_length + 1).astype(np.float32) - - # coordinates of input - u = x / scale + 0.5 * (1 - 1 / scale) - left = np.floor(u - kernel_width / 2) # leftmost pixel - p = int(np.ceil(kernel_width)) + 2 # maximum number of pixels - - # indices of input pixels - ind = left[:, np.newaxis, ...] + np.arange(p) - indices = ind.astype(np.int32) - - # weights of input pixels - weights = h(u[:, np.newaxis, ...] - indices - 1) - - weights = weights / np.sum(weights, axis=1)[:, np.newaxis, ...] - - # remove all-zero columns - aux = np.concatenate( - (np.arange(input_length), np.arange(input_length - 1, -1, - step=-1))).astype(np.int32) - indices = aux[np.mod(indices, aux.size)] - ind2store = np.nonzero(np.any(weights, axis=0)) - weights = weights[:, ind2store] - indices = indices[:, ind2store] - - return weights, indices - - -def resize_along_dim(img_in, weights, indices, dim): - """Resize along a specific dimension. - - Args: - img_in (ndarray): The input image. - weights (ndarray): The weights used for interpolation, computed from - [get_weights_indices]. - indices (ndarray): The indices used for interpolation, computed from - [get_weights_indices]. - dim (int): Which dimension to undergo interpolation. - - Returns: - ndarray: Interpolated (along one dimension) image. - """ - - img_in = img_in.astype(np.float32) - w_shape = weights.shape - output_shape = list(img_in.shape) - output_shape[dim] = w_shape[0] - img_out = np.zeros(output_shape) - - if dim == 0: - for i in range(w_shape[0]): - w = weights[i, :][np.newaxis, ...] - ind = indices[i, :] - img_slice = img_in[ind, :] - img_out[i] = np.sum(np.squeeze(img_slice, axis=0) * w.T, axis=0) - elif dim == 1: - for i in range(w_shape[0]): - w = weights[i, :][:, :, np.newaxis] - ind = indices[i, :] - img_slice = img_in[:, ind] - img_out[:, i] = np.sum(np.squeeze(img_slice, axis=1) * w.T, axis=1) - - if img_in.dtype == np.uint8: - img_out = np.clip(img_out, 0, 255) - return np.around(img_out).astype(np.uint8) - else: - return img_out - - -@PIPELINES.register_module() -class MATLABLikeResize: - """Resize the input image using MATLAB-like downsampling. - - Currently support bicubic interpolation only. Note that the output of - this function is slightly different from the official MATLAB function. - - Required keys are the keys in attribute "keys". Added or modified keys - are "scale" and "output_shape", and the keys in attribute "keys". - - Args: - keys (list[str]): A list of keys whose values are modified. - scale (float | None, optional): The scale factor of the resize - operation. If None, it will be determined by output_shape. - Default: None. - output_shape (tuple(int) | None, optional): The size of the output - image. If None, it will be determined by scale. Note that if - scale is provided, output_shape will not be used. - Default: None. - kernel (str, optional): The kernel for the resize operation. - Currently support 'bicubic' only. Default: 'bicubic'. - kernel_width (float): The kernel width. Currently support 4.0 only. - Default: 4.0. - """ - - def __init__(self, - keys, - scale=None, - output_shape=None, - kernel='bicubic', - kernel_width=4.0): - - if kernel.lower() != 'bicubic': - raise ValueError('Currently support bicubic kernel only.') - - if float(kernel_width) != 4.0: - raise ValueError('Current support only width=4 only.') - - if scale is None and output_shape is None: - raise ValueError('"scale" and "output_shape" cannot be both None') - - self.kernel_func = _cubic - self.keys = keys - self.scale = scale - self.output_shape = output_shape - self.kernel = kernel - self.kernel_width = kernel_width - - def _resize(self, img): - weights = {} - indices = {} - - # compute scale and output_size - if self.scale is not None: - scale = float(self.scale) - scale = [scale, scale] - output_size = get_size_from_scale(img.shape, scale) - else: - scale = get_scale_from_size(img.shape, self.output_shape) - output_size = list(self.output_shape) - - # apply cubic interpolation along two dimensions - order = np.argsort(np.array(scale)) - for k in range(2): - key = (img.shape[k], output_size[k], scale[k], self.kernel_func, - self.kernel_width) - weight, index = get_weights_indices(img.shape[k], output_size[k], - scale[k], self.kernel_func, - self.kernel_width) - weights[key] = weight - indices[key] = index - - output = np.copy(img) - if output.ndim == 2: # grayscale image - output = output[:, :, np.newaxis] - - for k in range(2): - dim = order[k] - key = (img.shape[dim], output_size[dim], scale[dim], - self.kernel_func, self.kernel_width) - output = resize_along_dim(output, weights[key], indices[key], dim) - - return output - - def __call__(self, results): - for key in self.keys: - is_single_image = False - if isinstance(results[key], np.ndarray): - is_single_image = True - results[key] = [results[key]] - - results[key] = [self._resize(img) for img in results[key]] - - if is_single_image: - results[key] = results[key][0] - - results['scale'] = self.scale - results['output_shape'] = self.output_shape - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(keys={self.keys}, scale={self.scale}, ' - f'output_shape={self.output_shape}, ' - f'kernel={self.kernel}, kernel_width={self.kernel_width})') - return repr_str diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/normalization.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/normalization.py deleted file mode 100755 index 8ff774d7f..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/normalization.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np - -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class Normalize: - """Normalize images with the given mean and std value. - - Required keys are the keys in attribute "keys", added or modified keys are - the keys in attribute "keys" and these keys with postfix '_norm_cfg'. - It also supports normalizing a list of images. - - Args: - keys (Sequence[str]): The images to be normalized. - mean (np.ndarray): Mean values of different channels. - std (np.ndarray): Std values of different channels. - to_rgb (bool): Whether to convert channels from BGR to RGB. - """ - - def __init__(self, keys, mean, std, to_rgb=False, save_original=False): - self.keys = keys - self.mean = np.array(mean, dtype=np.float32) - self.std = np.array(std, dtype=np.float32) - self.to_rgb = to_rgb - self.save_original = save_original - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if isinstance(results[key], list): - if self.save_original: - results[key + '_unnormalised'] = [ - v.copy() for v in results[key] - ] - results[key] = [ - mmcv.imnormalize(v, self.mean, self.std, self.to_rgb) - for v in results[key] - ] - else: - if self.save_original: - results[key + '_unnormalised'] = results[key].copy() - results[key] = mmcv.imnormalize(results[key], self.mean, - self.std, self.to_rgb) - - results['img_norm_cfg'] = dict( - mean=self.mean, std=self.std, to_rgb=self.to_rgb) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, mean={self.mean}, std={self.std}, ' - f'to_rgb={self.to_rgb})') - - return repr_str - - -@PIPELINES.register_module() -class RescaleToZeroOne: - """Transform the images into a range between 0 and 1. - - Required keys are the keys in attribute "keys", added or modified keys are - the keys in attribute "keys". - It also supports rescaling a list of images. - - Args: - keys (Sequence[str]): The images to be transformed. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if isinstance(results[key], list): - results[key] = [ - v.astype(np.float32) / 255. for v in results[key] - ] - else: - results[key] = results[key].astype(np.float32) / 255. - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/utils.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/utils.py deleted file mode 100755 index 42d470c34..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/pipelines/utils.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import numpy as np -import torch -from mmcv.utils import print_log - -_integer_types = ( - np.byte, - np.ubyte, # 8 bits - np.short, - np.ushort, # 16 bits - np.intc, - np.uintc, # 16 or 32 or 64 bits - np.int_, - np.uint, # 32 or 64 bits - np.longlong, - np.ulonglong) # 64 bits - -_integer_ranges = { - t: (np.iinfo(t).min, np.iinfo(t).max) - for t in _integer_types -} - -dtype_range = { - np.bool_: (False, True), - np.bool8: (False, True), - np.float16: (-1, 1), - np.float32: (-1, 1), - np.float64: (-1, 1) -} -dtype_range.update(_integer_ranges) - - -def dtype_limits(image, clip_negative=False): - """Return intensity limits, i.e. (min, max) tuple, of the image's dtype. - - This function is adopted from skimage: - https://github.com/scikit-image/scikit-image/blob/ - 7e4840bd9439d1dfb6beaf549998452c99f97fdd/skimage/util/dtype.py#L35 - - Args: - image (ndarray): Input image. - clip_negative (bool, optional): If True, clip the negative range - (i.e. return 0 for min intensity) even if the image dtype allows - negative values. - - Returns - tuple: Lower and upper intensity limits. - """ - imin, imax = dtype_range[image.dtype.type] - if clip_negative: - imin = 0 - return imin, imax - - -def adjust_gamma(image, gamma=1, gain=1): - """Performs Gamma Correction on the input image. - - This function is adopted from skimage: - https://github.com/scikit-image/scikit-image/blob/ - 7e4840bd9439d1dfb6beaf549998452c99f97fdd/skimage/exposure/ - exposure.py#L439-L494 - - Also known as Power Law Transform. - This function transforms the input image pixelwise according to the - equation ``O = I**gamma`` after scaling each pixel to the range 0 to 1. - - Args: - image (ndarray): Input image. - gamma (float, optional): Non negative real number. Defaults to 1. - gain (float, optional): The constant multiplier. Defaults to 1. - - Returns: - ndarray: Gamma corrected output image. - """ - if np.any(image < 0): - raise ValueError('Image Correction methods work correctly only on ' - 'images with non-negative values. Use ' - 'skimage.exposure.rescale_intensity.') - - dtype = image.dtype.type - - if gamma < 0: - raise ValueError('Gamma should be a non-negative real number.') - - scale = float(dtype_limits(image, True)[1] - dtype_limits(image, True)[0]) - - out = ((image / scale)**gamma) * scale * gain - return out.astype(dtype) - - -def random_choose_unknown(unknown, crop_size): - """Randomly choose an unknown start (top-left) point for a given crop_size. - - Args: - unknown (np.ndarray): The binary unknown mask. - crop_size (tuple[int]): The given crop size. - - Returns: - tuple[int]: The top-left point of the chosen bbox. - """ - h, w = unknown.shape - crop_h, crop_w = crop_size - delta_h = center_h = crop_h // 2 - delta_w = center_w = crop_w // 2 - - # mask out the validate area for selecting the cropping center - mask = np.zeros_like(unknown) - mask[delta_h:h - delta_h, delta_w:w - delta_w] = 1 - if np.any(unknown & mask): - center_h_list, center_w_list = np.where(unknown & mask) - elif np.any(unknown): - center_h_list, center_w_list = np.where(unknown) - else: - print_log('No unknown pixels found!', level=logging.WARNING) - center_h_list = [center_h] - center_w_list = [center_w] - num_unknowns = len(center_h_list) - rand_ind = np.random.randint(num_unknowns) - center_h = center_h_list[rand_ind] - center_w = center_w_list[rand_ind] - - # make sure the top-left point is valid - top = np.clip(center_h - delta_h, 0, h - crop_h) - left = np.clip(center_w - delta_w, 0, w - crop_w) - - return top, left - - -def make_coord(shape, ranges=None, flatten=True): - """ Make coordinates at grid centers. - - Args: - shape (tuple): shape of image. - ranges (tuple): range of coordinate value. Default: None. - flatten (bool): flatten to (n, 2) or Not. Default: True. - - return: - coord (Tensor): coordinates. - """ - coord_seqs = [] - for i, n in enumerate(shape): - if ranges is None: - v0, v1 = -1, 1 - else: - v0, v1 = ranges[i] - r = (v1 - v0) / (2 * n) - seq = v0 + r + (2 * r) * torch.arange(n).float() - coord_seqs.append(seq) - coord = torch.stack(torch.meshgrid(*coord_seqs), dim=-1) - if flatten: - coord = coord.view(-1, coord.shape[-1]) - return coord diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/registry.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/registry.py deleted file mode 100755 index 984580ef2..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/registry.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -DATASETS = Registry('dataset') -PIPELINES = Registry('pipeline') diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/samplers/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/samplers/__init__.py deleted file mode 100755 index da09effaf..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/samplers/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .distributed_sampler import DistributedSampler - -__all__ = ['DistributedSampler'] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/samplers/distributed_sampler.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/samplers/distributed_sampler.py deleted file mode 100755 index 7e800c813..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/samplers/distributed_sampler.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from __future__ import division -import math - -import torch -from torch.utils.data import DistributedSampler as _DistributedSampler - -from mmedit.core.utils import sync_random_seed - - -class DistributedSampler(_DistributedSampler): - """DistributedSampler inheriting from `torch.utils.data.DistributedSampler`. - - In pytorch of lower versions, there is no `shuffle` argument. This child - class will port one to DistributedSampler. - """ - - def __init__(self, - dataset, - num_replicas=None, - rank=None, - shuffle=True, - samples_per_gpu=1, - seed=0): - super().__init__(dataset, num_replicas=num_replicas, rank=rank) - self.shuffle = shuffle - self.samples_per_gpu = samples_per_gpu - # fix the bug of the official implementation - self.num_samples_per_replica = int( - math.ceil( - len(self.dataset) * 1.0 / self.num_replicas / samples_per_gpu)) - self.num_samples = self.num_samples_per_replica * self.samples_per_gpu - self.total_size = self.num_samples * self.num_replicas - - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - - # to avoid padding bug when meeting too small dataset - if len(dataset) < self.num_replicas * samples_per_gpu: - raise ValueError( - 'You may use too small dataset and our distributed ' - 'sampler cannot pad your dataset correctly. We highly ' - 'recommend you to use fewer GPUs to finish your work') - - def __iter__(self): - # deterministically shuffle based on epoch - if self.shuffle: - g = torch.Generator() - # When :attr:`shuffle=True`, this ensures all replicas - # use a different random ordering for each epoch. - # Otherwise, the next iteration of this sampler will - # yield the same ordering. - g.manual_seed(self.epoch + self.seed) - indices = torch.randperm(len(self.dataset), generator=g).tolist() - else: - indices = torch.arange(len(self.dataset)).tolist() - - # add extra samples to make it evenly divisible - indices += indices[:(self.total_size - len(indices))] - assert len(indices) == self.total_size - - # subsample - indices = indices[self.rank:self.total_size:self.num_replicas] - assert len(indices) == self.num_samples - - return iter(indices) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/sr_annotation_dataset.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/sr_annotation_dataset.py deleted file mode 100755 index 318e0c6e3..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/sr_annotation_dataset.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from .base_sr_dataset import BaseSRDataset -from .registry import DATASETS - - -@DATASETS.register_module() -class SRAnnotationDataset(BaseSRDataset): - """General paired image dataset with an annotation file for image - restoration. - - The dataset loads lq (Low Quality) and gt (Ground-Truth) image pairs, - applies specified transforms and finally returns a dict containing paired - data and other information. - - This is the "annotation file mode": - Each line in the annotation file contains the image names and - image shape (usually for gt), separated by a white space. - - Example of an annotation file: - - :: - - 0001_s001.png (480,480,3) - 0001_s002.png (480,480,3) - - Args: - lq_folder (str | :obj:`Path`): Path to a lq folder. - gt_folder (str | :obj:`Path`): Path to a gt folder. - ann_file (str | :obj:`Path`): Path to the annotation file. - pipeline (list[dict | callable]): A sequence of data transformations. - scale (int): Upsampling scale ratio. - test_mode (bool): Store `True` when building test dataset. - Default: `False`. - filename_tmpl (str): Template for each filename. Note that the - template excludes the file extension. Default: '{}'. - """ - - def __init__(self, - lq_folder, - gt_folder, - ann_file, - pipeline, - scale, - test_mode=False, - filename_tmpl='{}'): - super().__init__(pipeline, scale, test_mode) - self.lq_folder = str(lq_folder) - self.gt_folder = str(gt_folder) - self.ann_file = str(ann_file) - self.filename_tmpl = filename_tmpl - self.data_infos = self.load_annotations() - - def load_annotations(self): - """Load annotations for SR dataset. - - It loads the LQ and GT image path from the annotation file. - Each line in the annotation file contains the image names and - image shape (usually for gt), separated by a white space. - - Returns: - list[dict]: A list of dicts for paired paths of LQ and GT. - """ - data_infos = [] - with open(self.ann_file, 'r') as fin: - for line in fin: - gt_name = line.split(' ')[0] - basename, ext = osp.splitext(osp.basename(gt_name)) - lq_name = f'{self.filename_tmpl.format(basename)}{ext}' - data_infos.append( - dict( - lq_path=osp.join(self.lq_folder, lq_name), - gt_path=osp.join(self.gt_folder, gt_name))) - return data_infos diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/sr_folder_dataset.py b/cv/super_resolution/esrgan/pytorch/mmedit/datasets/sr_folder_dataset.py deleted file mode 100755 index 3c76dd9ed..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/datasets/sr_folder_dataset.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from .base_sr_dataset import BaseSRDataset -from .registry import DATASETS - - -@DATASETS.register_module() -class SRFolderDataset(BaseSRDataset): - """General paired image folder dataset for image restoration. - - The dataset loads lq (Low Quality) and gt (Ground-Truth) image pairs, - applies specified transforms and finally returns a dict containing paired - data and other information. - - This is the "folder mode", which needs to specify the lq folder path and gt - folder path, each folder containing the corresponding images. - Image lists will be generated automatically. You can also specify the - filename template to match the lq and gt pairs. - - For example, we have two folders with the following structures: - - :: - - data_root - ├── lq - │ ├── 0001_x4.png - │ ├── 0002_x4.png - ├── gt - │ ├── 0001.png - │ ├── 0002.png - - then, you need to set: - - .. code-block:: python - - lq_folder = data_root/lq - gt_folder = data_root/gt - filename_tmpl = '{}_x4' - - Args: - lq_folder (str | :obj:`Path`): Path to a lq folder. - gt_folder (str | :obj:`Path`): Path to a gt folder. - pipeline (List[dict | callable]): A sequence of data transformations. - scale (int): Upsampling scale ratio. - test_mode (bool): Store `True` when building test dataset. - Default: `False`. - filename_tmpl (str): Template for each filename. Note that the - template excludes the file extension. Default: '{}'. - """ - - def __init__(self, - lq_folder, - gt_folder, - pipeline, - scale, - test_mode=False, - filename_tmpl='{}'): - super().__init__(pipeline, scale, test_mode) - self.lq_folder = str(lq_folder) - self.gt_folder = str(gt_folder) - self.filename_tmpl = filename_tmpl - self.data_infos = self.load_annotations() - - def load_annotations(self): - """Load annotations for SR dataset. - - It loads the LQ and GT image path from folders. - - Returns: - list[dict]: A list of dicts for paired paths of LQ and GT. - """ - data_infos = [] - lq_paths = self.scan_folder(self.lq_folder) - gt_paths = self.scan_folder(self.gt_folder) - assert len(lq_paths) == len(gt_paths), ( - f'gt and lq datasets have different number of images: ' - f'{len(lq_paths)}, {len(gt_paths)}.') - for gt_path in gt_paths: - basename, ext = osp.splitext(osp.basename(gt_path)) - lq_path = osp.join(self.lq_folder, - (f'{self.filename_tmpl.format(basename)}' - f'{ext}')) - assert lq_path in lq_paths, f'{lq_path} is not in lq_paths.' - data_infos.append(dict(lq_path=lq_path, gt_path=gt_path)) - return data_infos diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/__init__.py deleted file mode 100755 index afbd44b6d..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .backbones import * # noqa: F401, F403 -from .base import BaseModel -from .builder import (build, build_backbone, build_component, build_loss, - build_model) -from .common import * # noqa: F401, F403 -from .components import * # noqa: F401, F403 -from .losses import * # noqa: F401, F403 -from .registry import BACKBONES, COMPONENTS, LOSSES, MODELS -from .restorers import ESRGAN, SRGAN, BasicRestorer - diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/backbones/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/backbones/__init__.py deleted file mode 100755 index c838004ff..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/backbones/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .sr_backbones import RRDBNet diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/backbones/sr_backbones/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/backbones/sr_backbones/__init__.py deleted file mode 100755 index aaa64767b..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/backbones/sr_backbones/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .rrdb_net import RRDBNet diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/backbones/sr_backbones/rrdb_net.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/backbones/sr_backbones/rrdb_net.py deleted file mode 100755 index 577e142bc..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/backbones/sr_backbones/rrdb_net.py +++ /dev/null @@ -1,197 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.runner import load_checkpoint - -from mmedit.models.common import (default_init_weights, make_layer, - pixel_unshuffle) -from mmedit.models.registry import BACKBONES -from mmedit.utils import get_root_logger - - -class ResidualDenseBlock(nn.Module): - """Residual Dense Block. - - Used in RRDB block in ESRGAN. - - Args: - mid_channels (int): Channel number of intermediate features. - growth_channels (int): Channels for each growth. - """ - - def __init__(self, mid_channels=64, growth_channels=32): - super().__init__() - for i in range(5): - out_channels = mid_channels if i == 4 else growth_channels - self.add_module( - f'conv{i+1}', - nn.Conv2d(mid_channels + i * growth_channels, out_channels, 3, - 1, 1)) - self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) - - self.init_weights() - - def init_weights(self): - """Init weights for ResidualDenseBlock. - - Use smaller std for better stability and performance. We empirically - use 0.1. See more details in "ESRGAN: Enhanced Super-Resolution - Generative Adversarial Networks" - """ - for i in range(5): - default_init_weights(getattr(self, f'conv{i+1}'), 0.1) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - x1 = self.lrelu(self.conv1(x)) - x2 = self.lrelu(self.conv2(torch.cat((x, x1), 1))) - x3 = self.lrelu(self.conv3(torch.cat((x, x1, x2), 1))) - x4 = self.lrelu(self.conv4(torch.cat((x, x1, x2, x3), 1))) - x5 = self.conv5(torch.cat((x, x1, x2, x3, x4), 1)) - # Emperically, we use 0.2 to scale the residual for better performance - return x5 * 0.2 + x - - -class RRDB(nn.Module): - """Residual in Residual Dense Block. - - Used in RRDB-Net in ESRGAN. - - Args: - mid_channels (int): Channel number of intermediate features. - growth_channels (int): Channels for each growth. - """ - - def __init__(self, mid_channels, growth_channels=32): - super().__init__() - self.rdb1 = ResidualDenseBlock(mid_channels, growth_channels) - self.rdb2 = ResidualDenseBlock(mid_channels, growth_channels) - self.rdb3 = ResidualDenseBlock(mid_channels, growth_channels) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - out = self.rdb1(x) - out = self.rdb2(out) - out = self.rdb3(out) - # Emperically, we use 0.2 to scale the residual for better performance - return out * 0.2 + x - - -@BACKBONES.register_module() -class RRDBNet(nn.Module): - """Networks consisting of Residual in Residual Dense Block, which is used - in ESRGAN and Real-ESRGAN. - - ESRGAN: Enhanced Super-Resolution Generative Adversarial Networks. - Real-ESRGAN: Training Real-World Blind Super-Resolution with Pure Synthetic Data. # noqa: E501 - Currently, it supports [x1/x2/x4] upsampling scale factor. - - Args: - in_channels (int): Channel number of inputs. - out_channels (int): Channel number of outputs. - mid_channels (int): Channel number of intermediate features. - Default: 64 - num_blocks (int): Block number in the trunk network. Defaults: 23 - growth_channels (int): Channels for each growth. Default: 32. - upscale_factor (int): Upsampling factor. Support x1, x2 and x4. - Default: 4. - """ - _supported_upscale_factors = [1, 2, 4] - - def __init__(self, - in_channels, - out_channels, - mid_channels=64, - num_blocks=23, - growth_channels=32, - upscale_factor=4): - super().__init__() - if upscale_factor in self._supported_upscale_factors: - in_channels = in_channels * ((4 // upscale_factor)**2) - else: - raise ValueError(f'Unsupported scale factor {upscale_factor}. ' - f'Currently supported ones are ' - f'{self._supported_upscale_factors}.') - - self.upscale_factor = upscale_factor - self.conv_first = nn.Conv2d(in_channels, mid_channels, 3, 1, 1) - self.body = make_layer( - RRDB, - num_blocks, - mid_channels=mid_channels, - growth_channels=growth_channels) - self.conv_body = nn.Conv2d(mid_channels, mid_channels, 3, 1, 1) - # upsample - self.conv_up1 = nn.Conv2d(mid_channels, mid_channels, 3, 1, 1) - self.conv_up2 = nn.Conv2d(mid_channels, mid_channels, 3, 1, 1) - self.conv_hr = nn.Conv2d(mid_channels, mid_channels, 3, 1, 1) - self.conv_last = nn.Conv2d(mid_channels, out_channels, 3, 1, 1) - - self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - if self.upscale_factor in [1, 2]: - feat = pixel_unshuffle(x, scale=4 // self.upscale_factor) - else: - feat = x - - feat = self.conv_first(feat) - body_feat = self.conv_body(self.body(feat)) - feat = feat + body_feat - - # upsample - feat = self.lrelu( - self.conv_up1(F.interpolate(feat, scale_factor=2, mode='nearest'))) - feat = self.lrelu( - self.conv_up2(F.interpolate(feat, scale_factor=2, mode='nearest'))) - - out = self.conv_last(self.lrelu(self.conv_hr(feat))) - return out - - def init_weights(self, pretrained=None, strict=True): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - strict (boo, optional): Whether strictly load the pretrained model. - Defaults to True. - """ - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=strict, logger=logger) - elif pretrained is None: - # Use smaller std for better stability and performance. We - # use 0.1. See more details in "ESRGAN: Enhanced Super-Resolution - # Generative Adversarial Networks" - for m in [ - self.conv_first, self.conv_body, self.conv_up1, - self.conv_up2, self.conv_hr, self.conv_last - ]: - default_init_weights(m, 0.1) - else: - raise TypeError(f'"pretrained" must be a str or None. ' - f'But received {type(pretrained)}.') diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/base.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/base.py deleted file mode 100755 index 02327e283..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/base.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod -from collections import OrderedDict - -import torch -import torch.nn as nn - - -class BaseModel(nn.Module, metaclass=ABCMeta): - """Base model. - - All models should subclass it. - All subclass should overwrite: - - ``init_weights``, supporting to initialize models. - - ``forward_train``, supporting to forward when training. - - ``forward_test``, supporting to forward when testing. - - ``train_step``, supporting to train one step when training. - """ - - @abstractmethod - def init_weights(self): - """Abstract method for initializing weight. - - All subclass should overwrite it. - """ - - @abstractmethod - def forward_train(self, imgs, labels): - """Abstract method for training forward. - - All subclass should overwrite it. - """ - - @abstractmethod - def forward_test(self, imgs): - """Abstract method for testing forward. - - All subclass should overwrite it. - """ - - def forward(self, imgs, labels, test_mode, **kwargs): - """Forward function for base model. - - Args: - imgs (Tensor): Input image(s). - labels (Tensor): Ground-truth label(s). - test_mode (bool): Whether in test mode. - kwargs (dict): Other arguments. - - Returns: - Tensor: Forward results. - """ - - if test_mode: - return self.forward_test(imgs, **kwargs) - - return self.forward_train(imgs, labels, **kwargs) - - @abstractmethod - def train_step(self, data_batch, optimizer): - """Abstract method for one training step. - - All subclass should overwrite it. - """ - - def val_step(self, data_batch, **kwargs): - """Abstract method for one validation step. - - All subclass should overwrite it. - """ - output = self.forward_test(**data_batch, **kwargs) - return output - - def parse_losses(self, losses): - """Parse losses dict for different loss variants. - - Args: - losses (dict): Loss dict. - - Returns: - loss (float): Sum of the total loss. - log_vars (dict): loss dict for different variants. - """ - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(_value for _key, _value in log_vars.items() - if 'loss' in _key) - - log_vars['loss'] = loss - for name in log_vars: - log_vars[name] = log_vars[name].item() - - return loss, log_vars diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/builder.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/builder.py deleted file mode 100755 index 8606225aa..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/builder.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv import build_from_cfg - -from .registry import BACKBONES, COMPONENTS, LOSSES, MODELS - - -def build(cfg, registry, default_args=None): - """Build module function. - - Args: - cfg (dict): Configuration for building modules. - registry (obj): ``registry`` object. - default_args (dict, optional): Default arguments. Defaults to None. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return nn.Sequential(*modules) - - return build_from_cfg(cfg, registry, default_args) - - -def build_backbone(cfg): - """Build backbone. - - Args: - cfg (dict): Configuration for building backbone. - """ - return build(cfg, BACKBONES) - - -def build_component(cfg): - """Build component. - - Args: - cfg (dict): Configuration for building component. - """ - return build(cfg, COMPONENTS) - - -def build_loss(cfg): - """Build loss. - - Args: - cfg (dict): Configuration for building loss. - """ - return build(cfg, LOSSES) - - -def build_model(cfg, train_cfg=None, test_cfg=None): - """Build model. - - Args: - cfg (dict): Configuration for building model. - train_cfg (dict): Training configuration. Default: None. - test_cfg (dict): Testing configuration. Default: None. - """ - return build(cfg, MODELS, dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/__init__.py deleted file mode 100755 index 7ebeb4fba..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .aspp import ASPP -from .contextual_attention import ContextualAttentionModule -from .conv import * # noqa: F401, F403 -from .downsample import pixel_unshuffle -from .ensemble import SpatialTemporalEnsemble -from .flow_warp import flow_warp -from .gated_conv_module import SimpleGatedConvModule -from .gca_module import GCAModule -from .generation_model_utils import (GANImageBuffer, ResidualBlockWithDropout, - UnetSkipConnectionBlock, - generation_init_weights) -from .img_normalize import ImgNormalize -from .linear_module import LinearModule -from .mask_conv_module import MaskConvModule -from .model_utils import (extract_around_bbox, extract_bbox_patch, scale_bbox, - set_requires_grad) -from .partial_conv import PartialConv2d -from .separable_conv_module import DepthwiseSeparableConvModule -from .sr_backbone_utils import (ResidualBlockNoBN, default_init_weights, - make_layer) -from .upsample import PixelShufflePack - -__all__ = [ - 'ASPP', 'PartialConv2d', 'PixelShufflePack', 'default_init_weights', - 'ResidualBlockNoBN', 'make_layer', 'MaskConvModule', 'extract_bbox_patch', - 'extract_around_bbox', 'set_requires_grad', 'scale_bbox', - 'DepthwiseSeparableConvModule', 'ContextualAttentionModule', 'GCAModule', - 'SimpleGatedConvModule', 'LinearModule', 'flow_warp', 'ImgNormalize', - 'generation_init_weights', 'GANImageBuffer', 'UnetSkipConnectionBlock', - 'ResidualBlockWithDropout', 'pixel_unshuffle', 'SpatialTemporalEnsemble' -] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/aspp.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/aspp.py deleted file mode 100755 index c1e58e858..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/aspp.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.cnn import ConvModule -from torch import nn -from torch.nn import functional as F - -from .separable_conv_module import DepthwiseSeparableConvModule - - -class ASPPPooling(nn.Sequential): - - def __init__(self, in_channels, out_channels, conv_cfg, norm_cfg, act_cfg): - super().__init__( - nn.AdaptiveAvgPool2d(1), - ConvModule( - in_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, x): - size = x.shape[-2:] - for mod in self: - x = mod(x) - return F.interpolate( - x, size=size, mode='bilinear', align_corners=False) - - -class ASPP(nn.Module): - """ASPP module from DeepLabV3. - - The code is adopted from - https://github.com/pytorch/vision/blob/master/torchvision/models/ - segmentation/deeplabv3.py - - For more information about the module: - `"Rethinking Atrous Convolution for Semantic Image Segmentation" - `_. - - Args: - in_channels (int): Input channels of the module. - out_channels (int): Output channels of the module. - mid_channels (int): Output channels of the intermediate ASPP conv - modules. - dilations (Sequence[int]): Dilation rate of three ASPP conv module. - Default: [12, 24, 36]. - conv_cfg (dict): Config dict for convolution layer. If "None", - nn.Conv2d will be applied. Default: None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - separable_conv (bool): Whether replace normal conv with depthwise - separable conv which is faster. Default: False. - """ - - def __init__(self, - in_channels, - out_channels=256, - mid_channels=256, - dilations=(12, 24, 36), - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - separable_conv=False): - super().__init__() - - if separable_conv: - conv_module = DepthwiseSeparableConvModule - else: - conv_module = ConvModule - - modules = [] - modules.append( - ConvModule( - in_channels, - mid_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - for dilation in dilations: - modules.append( - conv_module( - in_channels, - mid_channels, - 3, - padding=dilation, - dilation=dilation, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - modules.append( - ASPPPooling(in_channels, mid_channels, conv_cfg, norm_cfg, - act_cfg)) - - self.convs = nn.ModuleList(modules) - - self.project = nn.Sequential( - ConvModule( - 5 * mid_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg), nn.Dropout(0.5)) - - def forward(self, x): - """Forward function for ASPP module. - - Args: - x (Tensor): Input tensor with shape (N, C, H, W). - - Returns: - Tensor: Output tensor. - """ - res = [] - for conv in self.convs: - res.append(conv(x)) - res = torch.cat(res, dim=1) - return self.project(res) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/contextual_attention.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/contextual_attention.py deleted file mode 100755 index 7dcf4099e..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/contextual_attention.py +++ /dev/null @@ -1,379 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch -import torch.nn as nn -import torch.nn.functional as F - - -class ContextualAttentionModule(nn.Module): - """Contexture attention module. - - The details of this module can be found in: - Generative Image Inpainting with Contextual Attention - - Args: - unfold_raw_kernel_size (int): Kernel size used in unfolding raw - feature. Default: 4. - unfold_raw_stride (int): Stride used in unfolding raw feature. Default: - 2. - unfold_raw_padding (int): Padding used in unfolding raw feature. - Default: 1. - unfold_corr_kernel_size (int): Kernel size used in unfolding - context for computing correlation maps. Default: 3. - unfold_corr_stride (int): Stride used in unfolding context for - computing correlation maps. Default: 1. - unfold_corr_dilation (int): Dilation used in unfolding context for - computing correlation maps. Default: 1. - unfold_corr_padding (int): Padding used in unfolding context for - computing correlation maps. Default: 1. - scale (float): The resale factor used in resize input features. - Default: 0.5. - fuse_kernel_size (int): The kernel size used in fusion module. - Default: 3. - softmax_scale (float): The scale factor for softmax function. - Default: 10. - return_attention_score (bool): If True, the attention score will be - returned. Default: True. - """ - - def __init__(self, - unfold_raw_kernel_size=4, - unfold_raw_stride=2, - unfold_raw_padding=1, - unfold_corr_kernel_size=3, - unfold_corr_stride=1, - unfold_corr_dilation=1, - unfold_corr_padding=1, - scale=0.5, - fuse_kernel_size=3, - softmax_scale=10, - return_attention_score=True): - super().__init__() - self.unfold_raw_kernel_size = unfold_raw_kernel_size - self.unfold_raw_stride = unfold_raw_stride - self.unfold_raw_padding = unfold_raw_padding - self.unfold_corr_kernel_size = unfold_corr_kernel_size - self.unfold_corr_stride = unfold_corr_stride - self.unfold_corr_dilation = unfold_corr_dilation - self.unfold_corr_padding = unfold_corr_padding - self.scale = scale - self.fuse_kernel_size = fuse_kernel_size - self.with_fuse_correlation = fuse_kernel_size > 1 - self.softmax_scale = softmax_scale - self.return_attention_score = return_attention_score - - if self.with_fuse_correlation: - assert fuse_kernel_size % 2 == 1 - fuse_kernel = torch.eye(fuse_kernel_size).view( - 1, 1, fuse_kernel_size, fuse_kernel_size) - self.register_buffer('fuse_kernel', fuse_kernel) - padding = int((fuse_kernel_size - 1) // 2) - self.fuse_conv = partial(F.conv2d, padding=padding, stride=1) - self.softmax = nn.Softmax(dim=1) - - def forward(self, x, context, mask=None): - """Forward Function. - - Args: - x (torch.Tensor): Tensor with shape (n, c, h, w). - context (torch.Tensor): Tensor with shape (n, c, h, w). - mask (torch.Tensor): Tensor with shape (n, 1, h, w). Default: None. - - Returns: - tuple(torch.Tensor): Features after contextural attention. - """ - # raw features to be used in copy (deconv) - raw_context = context - raw_context_cols = self.im2col( - raw_context, - kernel_size=self.unfold_raw_kernel_size, - stride=self.unfold_raw_stride, - padding=self.unfold_raw_padding, - normalize=False, - return_cols=True) - # resize the feature to reduce computational cost - x = F.interpolate(x, scale_factor=self.scale) - context = F.interpolate(context, scale_factor=self.scale) - - context_cols = self.im2col( - context, - kernel_size=self.unfold_corr_kernel_size, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation, - normalize=True, - return_cols=True) - h_unfold, w_unfold = self.calculate_unfold_hw( - context.size()[-2:], - kernel_size=self.unfold_corr_kernel_size, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation, - ) - # reshape context_cols to - # (n*h_unfold*w_unfold, c, unfold_mks, unfold_mks) - # 'mks' is short for 'mask_kernel_size' - context_cols = context_cols.reshape(-1, *context_cols.shape[2:]) - - # the shape of correlation map should be: - # (n, h_unfold*w_unfold, h', w') - correlation_map = self.patch_correlation(x, context_cols) - # fuse correlation map to enlarge consistent attention region. - if self.with_fuse_correlation: - correlation_map = self.fuse_correlation_map( - correlation_map, h_unfold, w_unfold) - - correlation_map = self.mask_correlation_map(correlation_map, mask=mask) - - attention_score = self.softmax(correlation_map * self.softmax_scale) - - raw_context_filter = raw_context_cols.reshape( - -1, *raw_context_cols.shape[2:]) - output = self.patch_copy_deconv(attention_score, raw_context_filter) - # deconv will cause overlap and we need to remove the effects of that - overlap_factor = self.calculate_overlap_factor(attention_score) - output /= overlap_factor - - if self.return_attention_score: - n, _, h_s, w_s = attention_score.size() - attention_score = attention_score.view(n, h_unfold, w_unfold, h_s, - w_s) - return output, attention_score - - return output - - def patch_correlation(self, x, kernel): - """Calculate patch correlation. - - Args: - x (torch.Tensor): Input tensor. - kernel (torch.Tensor): Kernel tensor. - - Returns: - torch.Tensor: Tensor with shape of (n, l, h, w). - """ - n, _, h_in, w_in = x.size() - - patch_corr = F.conv2d( - x.view(1, -1, h_in, w_in), - kernel, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation, - groups=n) - h_out, w_out = patch_corr.size()[-2:] - return patch_corr.view(n, -1, h_out, w_out) - - def patch_copy_deconv(self, attention_score, context_filter): - """Copy patches using deconv. - - Args: - attention_score (torch.Tensor): Tensor with shape of (n, l , h, w). - context_filter (torch.Tensor): Filter kernel. - - Returns: - torch.Tensor: Tensor with shape of (n, c, h, w). - """ - n, _, h, w = attention_score.size() - attention_score = attention_score.view(1, -1, h, w) - output = F.conv_transpose2d( - attention_score, - context_filter, - stride=self.unfold_raw_stride, - padding=self.unfold_raw_padding, - groups=n) - h_out, w_out = output.size()[-2:] - return output.view(n, -1, h_out, w_out) - - def fuse_correlation_map(self, correlation_map, h_unfold, w_unfold): - """Fuse correlation map. - - This operation is to fuse correlation map for increasing large - consistent correlation regions. - - The mechanism behind this op is simple and easy to understand. A - standard 'Eye' matrix will be applied as a filter on the correlation - map in horizontal and vertical direction. - - The shape of input correlation map is (n, h_unfold*w_unfold, h, w). - When adopting fusing, we will apply convolutional filter in the - reshaped feature map with shape of (n, 1, h_unfold*w_fold, h*w). - - A simple specification for horizontal direction is shown below: - - .. code-block:: python - - (h, (h, (h, (h, - 0) 1) 2) 3) ... - (h, 0) - (h, 1) 1 - (h, 2) 1 - (h, 3) 1 - ... - - """ - # horizontal direction - n, _, h_map, w_map = correlation_map.size() - map_ = correlation_map.permute(0, 2, 3, 1) - map_ = map_.reshape(n, h_map * w_map, h_unfold * w_unfold, 1) - map_ = map_.permute(0, 3, 1, 2).contiguous() - map_ = self.fuse_conv(map_, self.fuse_kernel) - - correlation_map = map_.view(n, h_unfold, w_unfold, h_map, w_map) - - # vertical direction - map_ = correlation_map.permute(0, 2, 1, 4, - 3).reshape(n, 1, h_unfold * w_unfold, - h_map * w_map) - map_ = self.fuse_conv(map_, self.fuse_kernel) - - # Note that the dimension should be transposed since the convolution of - # eye matrix will put the normed scores into the last several dimension - correlation_map = map_.view(n, w_unfold, h_unfold, w_map, - h_map).permute(0, 4, 3, 2, 1) - correlation_map = correlation_map.reshape(n, -1, h_unfold, w_unfold) - - return correlation_map - - def calculate_unfold_hw(self, - input_size, - kernel_size=3, - stride=1, - dilation=1, - padding=0): - """Calculate (h, w) after unfolding - - The official implementation of `unfold` in pytorch will put the - dimension (h, w) into `L`. Thus, this function is just to calculate the - (h, w) according to the equation in: - https://pytorch.org/docs/stable/nn.html#torch.nn.Unfold - """ - h_in, w_in = input_size - - h_unfold = int((h_in + 2 * padding - dilation * - (kernel_size - 1) - 1) / stride + 1) - - w_unfold = int((w_in + 2 * padding - dilation * - (kernel_size - 1) - 1) / stride + 1) - return h_unfold, w_unfold - - def calculate_overlap_factor(self, attention_score): - """Calculate the overlap factor after applying deconv. - - Args: - attention_score (torch.Tensor): The attention score with shape of - (n, c, h, w). - - Returns: - torch.Tensor: The overlap factor will be returned. - """ - h, w = attention_score.shape[-2:] - kernel_size = self.unfold_raw_kernel_size - - ones_input = torch.ones(1, 1, h, w).to(attention_score) - ones_filter = torch.ones(1, 1, kernel_size, - kernel_size).to(attention_score) - overlap = F.conv_transpose2d( - ones_input, - ones_filter, - stride=self.unfold_raw_stride, - padding=self.unfold_raw_padding) - - # avoid division by zero - overlap[overlap == 0] = 1. - return overlap - - def mask_correlation_map(self, correlation_map, mask): - """Add mask weight for correlation map. - - Add a negative infinity number to the masked regions so that softmax - function will result in 'zero' in those regions. - - Args: - correlation_map (torch.Tensor): Correlation map with shape of - (n, h_unfold*w_unfold, h_map, w_map). - mask (torch.Tensor): Mask tensor with shape of (n, c, h, w). '1' - in the mask indicates masked region while '0' indicates valid - region. - - Returns: - torch.Tensor: Updated correlation map with mask. - """ - if mask is not None: - mask = F.interpolate(mask, scale_factor=self.scale) - # if any pixel is masked in patch, the patch is considered to be - # masked - mask_cols = self.im2col( - mask, - kernel_size=self.unfold_corr_kernel_size, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation) - mask_cols = (mask_cols.sum(dim=1, keepdim=True) > 0).float() - mask_cols = mask_cols.permute(0, 2, - 1).reshape(mask.size(0), -1, 1, 1) - # add negative inf will bring zero in softmax - mask_cols[mask_cols == 1] = -float('inf') - correlation_map += mask_cols - return correlation_map - - def im2col(self, - img, - kernel_size, - stride=1, - padding=0, - dilation=1, - normalize=False, - return_cols=False): - """Reshape image-style feature to columns. - - This function is used for unfold feature maps to columns. The - details of this function can be found in: - https://pytorch.org/docs/1.1.0/nn.html?highlight=unfold#torch.nn.Unfold - - Args: - img (torch.Tensor): Features to be unfolded. The shape of this - feature should be (n, c, h, w). - kernel_size (int): In this function, we only support square kernel - with same height and width. - stride (int): Stride number in unfolding. Default: 1. - padding (int): Padding number in unfolding. Default: 0. - dilation (int): Dilation number in unfolding. Default: 1. - normalize (bool): If True, the unfolded feature will be normalized. - Default: False. - return_cols (bool): The official implementation in PyTorch of - unfolding will return features with shape of - (n, c*$prod{kernel_size}$, L). If True, the features will be - reshaped to (n, L, c, kernel_size, kernel_size). Otherwise, - the results will maintain the shape as the official - implementation. - - Returns: - torch.Tensor: Unfolded columns. If `return_cols` is True, the \ - shape of output tensor is \ - `(n, L, c, kernel_size, kernel_size)`. Otherwise, the shape \ - will be `(n, c*$prod{kernel_size}$, L)`. - """ - - # unfold img to columns with shape (n, c*kernel_size**2, num_cols) - img_unfold = F.unfold( - img, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation) - # normalize the feature map - if normalize: - norm = torch.sqrt((img_unfold**2).sum(dim=1, keepdim=True)) - eps = torch.tensor([1e-4]).to(img) - img_unfold = img_unfold / torch.max(norm, eps) - - if return_cols: - img_unfold_ = img_unfold.permute(0, 2, 1) - n, num_cols = img_unfold_.size()[:2] - img_cols = img_unfold_.view(n, num_cols, img.size(1), kernel_size, - kernel_size) - return img_cols - - return img_unfold diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/conv.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/conv.py deleted file mode 100755 index 8e03d0f7d..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/conv.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import CONV_LAYERS -from torch import nn - -CONV_LAYERS.register_module('Deconv', module=nn.ConvTranspose2d) -# TODO: octave conv diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/downsample.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/downsample.py deleted file mode 100755 index 7d51a5b55..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/downsample.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def pixel_unshuffle(x, scale): - """Down-sample by pixel unshuffle. - - Args: - x (Tensor): Input tensor. - scale (int): Scale factor. - - Returns: - Tensor: Output tensor. - """ - - b, c, h, w = x.shape - if h % scale != 0 or w % scale != 0: - raise AssertionError( - f'Invalid scale ({scale}) of pixel unshuffle for tensor ' - f'with shape: {x.shape}') - h = int(h / scale) - w = int(w / scale) - x = x.view(b, c, h, scale, w, scale) - x = x.permute(0, 1, 3, 5, 2, 4) - return x.reshape(b, -1, h, w) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/ensemble.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/ensemble.py deleted file mode 100755 index 019b6e0f0..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/ensemble.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class SpatialTemporalEnsemble(nn.Module): - """ Apply spatial and temporal ensemble and compute outputs - - Args: - is_temporal_ensemble (bool, optional): Whether to apply ensemble - temporally. If True, the sequence will also be flipped temporally. - If the input is an image, this argument must be set to False. - Default: False. - - """ - - def __init__(self, is_temporal_ensemble=False): - - super().__init__() - - self.is_temporal_ensemble = is_temporal_ensemble - - def _transform(self, imgs, mode): - """Apply spatial transform (flip, rotate) to the images. - - Args: - imgs (torch.Tensor): The images to be transformed/ - mode (str): The mode of transform. Supported values are 'vertical', - 'horizontal', and 'transpose', corresponding to vertical flip, - horizontal flip, and rotation, respectively. - - Returns: - torch.Tensor: Output of the model with spatial ensemble applied. - - """ - - is_single_image = False - if imgs.ndim == 4: - if self.is_temporal_ensemble: - raise ValueError('"is_temporal_ensemble" must be False if ' - 'the input is an image.') - is_single_image = True - imgs = imgs.unsqueeze(1) - - if mode == 'vertical': - imgs = imgs.flip(4).clone() - elif mode == 'horizontal': - imgs = imgs.flip(3).clone() - elif mode == 'transpose': - imgs = imgs.permute(0, 1, 2, 4, 3).clone() - - if is_single_image: - imgs = imgs.squeeze(1) - - return imgs - - def spatial_ensemble(self, imgs, model): - """Apply spatial ensemble. - - Args: - imgs (torch.Tensor): The images to be processed by the model. Its - size should be either (n, t, c, h, w) or (n, c, h, w). - model (nn.Module): The model to process the images. - - Returns: - torch.Tensor: Output of the model with spatial ensemble applied. - - """ - - img_list = [imgs.cpu()] - for mode in ['vertical', 'horizontal', 'transpose']: - img_list.extend([self._transform(t, mode) for t in img_list]) - - output_list = [model(t.to(imgs.device)).cpu() for t in img_list] - for i in range(len(output_list)): - if i > 3: - output_list[i] = self._transform(output_list[i], 'transpose') - if i % 4 > 1: - output_list[i] = self._transform(output_list[i], 'horizontal') - if (i % 4) % 2 == 1: - output_list[i] = self._transform(output_list[i], 'vertical') - - outputs = torch.stack(output_list, dim=0) - outputs = outputs.mean(dim=0, keepdim=False) - - return outputs.to(imgs.device) - - def forward(self, imgs, model): - """Apply spatial and temporal ensemble. - - Args: - imgs (torch.Tensor): The images to be processed by the model. Its - size should be either (n, t, c, h, w) or (n, c, h, w). - model (nn.Module): The model to process the images. - - Returns: - torch.Tensor: Output of the model with spatial ensemble applied. - - """ - outputs = self.spatial_ensemble(imgs, model) - if self.is_temporal_ensemble: - outputs += self.spatial_ensemble(imgs.flip(1), model).flip(1) - outputs *= 0.5 - - return outputs diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/flow_warp.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/flow_warp.py deleted file mode 100755 index 7083230db..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/flow_warp.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn.functional as F - - -def flow_warp(x, - flow, - interpolation='bilinear', - padding_mode='zeros', - align_corners=True): - """Warp an image or a feature map with optical flow. - - Args: - x (Tensor): Tensor with size (n, c, h, w). - flow (Tensor): Tensor with size (n, h, w, 2). The last dimension is - a two-channel, denoting the width and height relative offsets. - Note that the values are not normalized to [-1, 1]. - interpolation (str): Interpolation mode: 'nearest' or 'bilinear'. - Default: 'bilinear'. - padding_mode (str): Padding mode: 'zeros' or 'border' or 'reflection'. - Default: 'zeros'. - align_corners (bool): Whether align corners. Default: True. - - Returns: - Tensor: Warped image or feature map. - """ - if x.size()[-2:] != flow.size()[1:3]: - raise ValueError(f'The spatial sizes of input ({x.size()[-2:]}) and ' - f'flow ({flow.size()[1:3]}) are not the same.') - _, _, h, w = x.size() - # create mesh grid - device = flow.device - grid_y, grid_x = torch.meshgrid( - torch.arange(0, h, device=device, dtype=x.dtype), - torch.arange(0, w, device=device, dtype=x.dtype)) - grid = torch.stack((grid_x, grid_y), 2) # h, w, 2 - grid.requires_grad = False - - grid_flow = grid + flow - # scale grid_flow to [-1,1] - grid_flow_x = 2.0 * grid_flow[:, :, :, 0] / max(w - 1, 1) - 1.0 - grid_flow_y = 2.0 * grid_flow[:, :, :, 1] / max(h - 1, 1) - 1.0 - grid_flow = torch.stack((grid_flow_x, grid_flow_y), dim=3) - output = F.grid_sample( - x, - grid_flow, - mode=interpolation, - padding_mode=padding_mode, - align_corners=align_corners) - return output diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/gated_conv_module.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/gated_conv_module.py deleted file mode 100755 index fed22c4f5..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/gated_conv_module.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, build_activation_layer - - -class SimpleGatedConvModule(nn.Module): - """Simple Gated Convolutional Module. - - This module is a simple gated convolutional module. The detailed formula - is: - - .. math:: - y = \\phi(conv1(x)) * \\sigma(conv2(x)), - - where `phi` is the feature activation function and `sigma` is the gate - activation function. In default, the gate activation function is sigmoid. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): The number of channels of the output feature. Note - that `out_channels` in the conv module is doubled since this module - contains two convolutions for feature and gate separately. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - feat_act_cfg (dict): Config dict for feature activation layer. - gate_act_cfg (dict): Config dict for gate activation layer. - kwargs (keyword arguments): Same as `ConvModule`. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - feat_act_cfg=dict(type='ELU'), - gate_act_cfg=dict(type='Sigmoid'), - **kwargs): - super().__init__() - # the activation function should specified outside conv module - kwargs_ = copy.deepcopy(kwargs) - kwargs_['act_cfg'] = None - self.with_feat_act = feat_act_cfg is not None - self.with_gate_act = gate_act_cfg is not None - - self.conv = ConvModule(in_channels, out_channels * 2, kernel_size, - **kwargs_) - - if self.with_feat_act: - self.feat_act = build_activation_layer(feat_act_cfg) - - if self.with_gate_act: - self.gate_act = build_activation_layer(gate_act_cfg) - - def forward(self, x): - """Forward Function. - - Args: - x (torch.Tensor): Input tensor with shape of (n, c, h, w). - - Returns: - torch.Tensor: Output tensor with shape of (n, c, h', w'). - """ - x = self.conv(x) - x, gate = torch.split(x, x.size(1) // 2, dim=1) - if self.with_feat_act: - x = self.feat_act(x) - if self.with_gate_act: - gate = self.gate_act(gate) - x = x * gate - - return x diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/gca_module.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/gca_module.py deleted file mode 100755 index 75ab64e5b..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/gca_module.py +++ /dev/null @@ -1,358 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, constant_init, xavier_init -from torch.nn import functional as F - - -class GCAModule(nn.Module): - """Guided Contextual Attention Module. - - From https://arxiv.org/pdf/2001.04069.pdf. - Based on https://github.com/nbei/Deep-Flow-Guided-Video-Inpainting. - This module use image feature map to augment the alpha feature map with - guided contextual attention score. - - Image feature and alpha feature are unfolded to small patches and later - used as conv kernel. Thus, we refer the unfolding size as kernel size. - Image feature patches have a default kernel size 3 while the kernel size of - alpha feature patches could be specified by `rate` (see `rate` below). The - image feature patches are used to convolve with the image feature itself - to calculate the contextual attention. Then the attention feature map is - convolved by alpha feature patches to obtain the attention alpha feature. - At last, the attention alpha feature is added to the input alpha feature. - - Args: - in_channels (int): Input channels of the guided contextual attention - module. - out_channels (int): Output channels of the guided contextual attention - module. - kernel_size (int): Kernel size of image feature patches. Default 3. - stride (int): Stride when unfolding the image feature. Default 1. - rate (int): The downsample rate of image feature map. The corresponding - kernel size and stride of alpha feature patches will be `rate x 2` - and `rate`. It could be regarded as the granularity of the gca - module. Default: 2. - pad_args (dict): Parameters of padding when convolve image feature with - image feature patches or alpha feature patches. Allowed keys are - `mode` and `value`. See torch.nn.functional.pad() for more - information. Default: dict(mode='reflect'). - interpolation (str): Interpolation method in upsampling and - downsampling. - penalty (float): Punishment hyperparameter to avoid a large correlation - between each unknown patch and itself. - eps (float): A small number to avoid dividing by 0 when calculating - the normed image feature patch. Default: 1e-4. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=3, - stride=1, - rate=2, - pad_args=dict(mode='reflect'), - interpolation='nearest', - penalty=-1e4, - eps=1e-4): - super().__init__() - self.kernel_size = kernel_size - self.stride = stride - self.rate = rate - self.pad_args = pad_args - self.interpolation = interpolation - self.penalty = penalty - self.eps = eps - - # reduced the channels of input image feature. - self.guidance_conv = nn.Conv2d(in_channels, in_channels // 2, 1) - - # convolution after the attention alpha feature - self.out_conv = ConvModule( - out_channels, - out_channels, - 1, - norm_cfg=dict(type='BN'), - act_cfg=None) - - self.init_weights() - - def init_weights(self): - xavier_init(self.guidance_conv, distribution='uniform') - xavier_init(self.out_conv.conv, distribution='uniform') - constant_init(self.out_conv.norm, 1e-3) - - def forward(self, img_feat, alpha_feat, unknown=None, softmax_scale=1.): - """Forward function of GCAModule. - - Args: - img_feat (Tensor): Image feature map of shape - (N, ori_c, ori_h, ori_w). - alpha_feat (Tensor): Alpha feature map of shape - (N, alpha_c, ori_h, ori_w). - unknown (Tensor, optional): Unknown area map generated by trimap. - If specified, this tensor should have shape - (N, 1, ori_h, ori_w). - softmax_scale (float, optional): The softmax scale of the attention - if unknown area is not provided in forward. Default: 1. - - Returns: - Tensor: The augmented alpha feature. - """ - - if alpha_feat.shape[2:4] != img_feat.shape[2:4]: - raise ValueError( - 'image feature size does not align with alpha feature size: ' - f'image feature size {img_feat.shape[2:4]}, ' - f'alpha feature size {alpha_feat.shape[2:4]}') - - if unknown is not None and unknown.shape[2:4] != img_feat.shape[2:4]: - raise ValueError( - 'image feature size does not align with unknown mask size: ' - f'image feature size {img_feat.shape[2:4]}, ' - f'unknown mask size {unknown.shape[2:4]}') - - # preprocess image feature - img_feat = self.guidance_conv(img_feat) - img_feat = F.interpolate( - img_feat, scale_factor=1 / self.rate, mode=self.interpolation) - - # preprocess unknown mask - unknown, softmax_scale = self.process_unknown_mask( - unknown, img_feat, softmax_scale) - - img_ps, alpha_ps, unknown_ps = self.extract_feature_maps_patches( - img_feat, alpha_feat, unknown) - - # create self correlation mask with shape: - # (N, img_h*img_w, img_h, img_w) - self_mask = self.get_self_correlation_mask(img_feat) - - # split tensors by batch dimension; tuple is returned - img_groups = torch.split(img_feat, 1, dim=0) - img_ps_groups = torch.split(img_ps, 1, dim=0) - alpha_ps_groups = torch.split(alpha_ps, 1, dim=0) - unknown_ps_groups = torch.split(unknown_ps, 1, dim=0) - scale_groups = torch.split(softmax_scale, 1, dim=0) - groups = (img_groups, img_ps_groups, alpha_ps_groups, - unknown_ps_groups, scale_groups) - - out = [] - # i is the virtual index of the sample in the current batch - for img_i, img_ps_i, alpha_ps_i, unknown_ps_i, scale_i in zip(*groups): - similarity_map = self.compute_similarity_map(img_i, img_ps_i) - - gca_score = self.compute_guided_attention_score( - similarity_map, unknown_ps_i, scale_i, self_mask) - - out_i = self.propagate_alpha_feature(gca_score, alpha_ps_i) - - out.append(out_i) - - out = torch.cat(out, dim=0) - out.reshape_as(alpha_feat) - - out = self.out_conv(out) + alpha_feat - return out - - def extract_feature_maps_patches(self, img_feat, alpha_feat, unknown): - """Extract image feature, alpha feature unknown patches. - - Args: - img_feat (Tensor): Image feature map of shape - (N, img_c, img_h, img_w). - alpha_feat (Tensor): Alpha feature map of shape - (N, alpha_c, ori_h, ori_w). - unknown (Tensor, optional): Unknown area map generated by trimap of - shape (N, 1, img_h, img_w). - - Returns: - tuple: 3-tuple of - - ``Tensor``: Image feature patches of shape \ - (N, img_h*img_w, img_c, img_ks, img_ks). - - ``Tensor``: Guided contextual attention alpha feature map. \ - (N, img_h*img_w, alpha_c, alpha_ks, alpha_ks). - - ``Tensor``: Unknown mask of shape (N, img_h*img_w, 1, 1). - """ - # extract image feature patches with shape: - # (N, img_h*img_w, img_c, img_ks, img_ks) - img_ks = self.kernel_size - img_ps = self.extract_patches(img_feat, img_ks, self.stride) - - # extract alpha feature patches with shape: - # (N, img_h*img_w, alpha_c, alpha_ks, alpha_ks) - alpha_ps = self.extract_patches(alpha_feat, self.rate * 2, self.rate) - - # extract unknown mask patches with shape: (N, img_h*img_w, 1, 1) - unknown_ps = self.extract_patches(unknown, img_ks, self.stride) - unknown_ps = unknown_ps.squeeze(dim=2) # squeeze channel dimension - unknown_ps = unknown_ps.mean(dim=[2, 3], keepdim=True) - - return img_ps, alpha_ps, unknown_ps - - def compute_similarity_map(self, img_feat, img_ps): - """Compute similarity between image feature patches. - - Args: - img_feat (Tensor): Image feature map of shape - (1, img_c, img_h, img_w). - img_ps (Tensor): Image feature patches tensor of shape - (1, img_h*img_w, img_c, img_ks, img_ks). - - Returns: - Tensor: Similarity map between image feature patches with shape \ - (1, img_h*img_w, img_h, img_w). - """ - img_ps = img_ps[0] # squeeze dim 0 - # convolve the feature to get correlation (similarity) map - escape_NaN = torch.FloatTensor([self.eps]).to(img_feat) - img_ps_normed = img_ps / torch.max(self.l2_norm(img_ps), escape_NaN) - img_feat = self.pad(img_feat, self.kernel_size, self.stride) - similarity_map = F.conv2d(img_feat, img_ps_normed) - - return similarity_map - - def compute_guided_attention_score(self, similarity_map, unknown_ps, scale, - self_mask): - """Compute guided attention score. - - Args: - similarity_map (Tensor): Similarity map of image feature with shape - (1, img_h*img_w, img_h, img_w). - unknown_ps (Tensor): Unknown area patches tensor of shape - (1, img_h*img_w, 1, 1). - scale (Tensor): Softmax scale of known and unknown area: - [unknown_scale, known_scale]. - self_mask (Tensor): Self correlation mask of shape - (1, img_h*img_w, img_h, img_w). At (1, i*i, i, i) mask value - equals -1e4 for i in [1, img_h*img_w] and other area is all - zero. - - Returns: - Tensor: Similarity map between image feature patches with shape \ - (1, img_h*img_w, img_h, img_w). - """ - # scale the correlation with predicted scale factor for known and - # unknown area - unknown_scale, known_scale = scale[0] - out = similarity_map * ( - unknown_scale * unknown_ps.gt(0.).float() + - known_scale * unknown_ps.le(0.).float()) - # mask itself, self-mask only applied to unknown area - out = out + self_mask * unknown_ps - gca_score = F.softmax(out, dim=1) - - return gca_score - - def propagate_alpha_feature(self, gca_score, alpha_ps): - """Propagate alpha feature based on guided attention score. - - Args: - gca_score (Tensor): Guided attention score map of shape - (1, img_h*img_w, img_h, img_w). - alpha_ps (Tensor): Alpha feature patches tensor of shape - (1, img_h*img_w, alpha_c, alpha_ks, alpha_ks). - - Returns: - Tensor: Propagated alpha feature map of shape \ - (1, alpha_c, alpha_h, alpha_w). - """ - alpha_ps = alpha_ps[0] # squeeze dim 0 - if self.rate == 1: - gca_score = self.pad(gca_score, kernel_size=2, stride=1) - alpha_ps = alpha_ps.permute(1, 0, 2, 3) - out = F.conv2d(gca_score, alpha_ps) / 4. - else: - out = F.conv_transpose2d( - gca_score, alpha_ps, stride=self.rate, padding=1) / 4. - - return out - - def process_unknown_mask(self, unknown, img_feat, softmax_scale): - """Process unknown mask. - - Args: - unknown (Tensor, optional): Unknown area map generated by trimap of - shape (N, 1, ori_h, ori_w) - img_feat (Tensor): The interpolated image feature map of shape - (N, img_c, img_h, img_w). - softmax_scale (float, optional): The softmax scale of the attention - if unknown area is not provided in forward. Default: 1. - - Returns: - tuple: 2-tuple of - - ``Tensor``: Interpolated unknown area map of shape \ - (N, img_h*img_w, img_h, img_w). - - ``Tensor``: Softmax scale tensor of known and unknown area of \ - shape (N, 2). - """ - n, _, h, w = img_feat.shape - - if unknown is not None: - unknown = unknown.clone() - unknown = F.interpolate( - unknown, scale_factor=1 / self.rate, mode=self.interpolation) - unknown_mean = unknown.mean(dim=[2, 3]) - known_mean = 1 - unknown_mean - unknown_scale = torch.clamp( - torch.sqrt(unknown_mean / known_mean), 0.1, 10).to(img_feat) - known_scale = torch.clamp( - torch.sqrt(known_mean / unknown_mean), 0.1, 10).to(img_feat) - softmax_scale = torch.cat([unknown_scale, known_scale], dim=1) - else: - unknown = torch.ones((n, 1, h, w)).to(img_feat) - softmax_scale = torch.FloatTensor( - [softmax_scale, - softmax_scale]).view(1, 2).repeat(n, 1).to(img_feat) - - return unknown, softmax_scale - - def extract_patches(self, x, kernel_size, stride): - """Extract feature patches. - - The feature map will be padded automatically to make sure the number of - patches is equal to `(H / stride) * (W / stride)`. - - Args: - x (Tensor): Feature map of shape (N, C, H, W). - kernel_size (int): Size of each patches. - stride (int): Stride between patches. - - Returns: - Tensor: Extracted patches of shape \ - (N, (H / stride) * (W / stride) , C, kernel_size, kernel_size). - """ - n, c, _, _ = x.shape - x = self.pad(x, kernel_size, stride) - x = F.unfold(x, (kernel_size, kernel_size), stride=(stride, stride)) - x = x.permute(0, 2, 1) - x = x.reshape(n, -1, c, kernel_size, kernel_size) - return x - - def pad(self, x, kernel_size, stride): - left = (kernel_size - stride + 1) // 2 - right = (kernel_size - stride) // 2 - pad = (left, right, left, right) - return F.pad(x, pad, **self.pad_args) - - def get_self_correlation_mask(self, img_feat): - _, _, h, w = img_feat.shape - # As ONNX does not support dynamic num_classes, we have to convert it - # into an integer - self_mask = F.one_hot( - torch.arange(h * w).view(h, w), num_classes=int(h * w)) - self_mask = self_mask.permute(2, 0, 1).view(1, h * w, h, w) - # use large negative value to mask out self-correlation before softmax - self_mask = self_mask * self.penalty - return self_mask.to(img_feat) - - @staticmethod - def l2_norm(x): - x = x**2 - x = x.sum(dim=[1, 2, 3], keepdim=True) - return torch.sqrt(x) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/generation_model_utils.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/generation_model_utils.py deleted file mode 100755 index 21d45fbe3..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/generation_model_utils.py +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, kaiming_init, normal_init, xavier_init -from torch.nn import init - - -def generation_init_weights(module, init_type='normal', init_gain=0.02): - """Default initialization of network weights for image generation. - - By default, we use normal init, but xavier and kaiming might work - better for some applications. - - Args: - module (nn.Module): Module to be initialized. - init_type (str): The name of an initialization method: - normal | xavier | kaiming | orthogonal. - init_gain (float): Scaling factor for normal, xavier and - orthogonal. - """ - - def init_func(m): - """Initialization function. - - Args: - m (nn.Module): Module to be initialized. - """ - classname = m.__class__.__name__ - if hasattr(m, 'weight') and (classname.find('Conv') != -1 - or classname.find('Linear') != -1): - if init_type == 'normal': - normal_init(m, 0.0, init_gain) - elif init_type == 'xavier': - xavier_init(m, gain=init_gain, distribution='normal') - elif init_type == 'kaiming': - kaiming_init( - m, - a=0, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='normal') - elif init_type == 'orthogonal': - init.orthogonal_(m.weight, gain=init_gain) - init.constant_(m.bias.data, 0.0) - else: - raise NotImplementedError( - f"Initialization method '{init_type}' is not implemented") - elif classname.find('BatchNorm2d') != -1: - # BatchNorm Layer's weight is not a matrix; - # only normal distribution applies. - normal_init(m, 1.0, init_gain) - - module.apply(init_func) - - -class GANImageBuffer: - """This class implements an image buffer that stores previously - generated images. - - This buffer allows us to update the discriminator using a history of - generated images rather than the ones produced by the latest generator - to reduce model oscillation. - - Args: - buffer_size (int): The size of image buffer. If buffer_size = 0, - no buffer will be created. - buffer_ratio (float): The chance / possibility to use the images - previously stored in the buffer. - """ - - def __init__(self, buffer_size, buffer_ratio=0.5): - self.buffer_size = buffer_size - # create an empty buffer - if self.buffer_size > 0: - self.img_num = 0 - self.image_buffer = [] - self.buffer_ratio = buffer_ratio - - def query(self, images): - """Query current image batch using a history of generated images. - - Args: - images (Tensor): Current image batch without history information. - """ - if self.buffer_size == 0: # if the buffer size is 0, do nothing - return images - return_images = [] - for image in images: - image = torch.unsqueeze(image.data, 0) - # if the buffer is not full, keep inserting current images - if self.img_num < self.buffer_size: - self.img_num = self.img_num + 1 - self.image_buffer.append(image) - return_images.append(image) - else: - use_buffer = np.random.random() < self.buffer_ratio - # by self.buffer_ratio, the buffer will return a previously - # stored image, and insert the current image into the buffer - if use_buffer: - random_id = np.random.randint(0, self.buffer_size) - image_tmp = self.image_buffer[random_id].clone() - self.image_buffer[random_id] = image - return_images.append(image_tmp) - # by (1 - self.buffer_ratio), the buffer will return the - # current image - else: - return_images.append(image) - # collect all the images and return - return_images = torch.cat(return_images, 0) - return return_images - - -class UnetSkipConnectionBlock(nn.Module): - """Construct a Unet submodule with skip connections, with the following - structure: downsampling - `submodule` - upsampling. - - Args: - outer_channels (int): Number of channels at the outer conv layer. - inner_channels (int): Number of channels at the inner conv layer. - in_channels (int): Number of channels in input images/features. If is - None, equals to `outer_channels`. Default: None. - submodule (UnetSkipConnectionBlock): Previously constructed submodule. - Default: None. - is_outermost (bool): Whether this module is the outermost module. - Default: False. - is_innermost (bool): Whether this module is the innermost module. - Default: False. - norm_cfg (dict): Config dict to build norm layer. Default: - `dict(type='BN')`. - use_dropout (bool): Whether to use dropout layers. Default: False. - """ - - def __init__(self, - outer_channels, - inner_channels, - in_channels=None, - submodule=None, - is_outermost=False, - is_innermost=False, - norm_cfg=dict(type='BN'), - use_dropout=False): - super().__init__() - # cannot be both outermost and innermost - assert not (is_outermost and is_innermost), ( - "'is_outermost' and 'is_innermost' cannot be True" - 'at the same time.') - self.is_outermost = is_outermost - assert isinstance(norm_cfg, dict), ("'norm_cfg' should be dict, but" - f'got {type(norm_cfg)}') - assert 'type' in norm_cfg, "'norm_cfg' must have key 'type'" - # We use norm layers in the unet skip connection block. - # Only for IN, use bias since it does not have affine parameters. - use_bias = norm_cfg['type'] == 'IN' - - kernel_size = 4 - stride = 2 - padding = 1 - if in_channels is None: - in_channels = outer_channels - down_conv_cfg = dict(type='Conv2d') - down_norm_cfg = norm_cfg - down_act_cfg = dict(type='LeakyReLU', negative_slope=0.2) - up_conv_cfg = dict(type='Deconv') - up_norm_cfg = norm_cfg - up_act_cfg = dict(type='ReLU') - up_in_channels = inner_channels * 2 - up_bias = use_bias - middle = [submodule] - upper = [] - - if is_outermost: - down_act_cfg = None - down_norm_cfg = None - up_bias = True - up_norm_cfg = None - upper = [nn.Tanh()] - elif is_innermost: - down_norm_cfg = None - up_in_channels = inner_channels - middle = [] - else: - upper = [nn.Dropout(0.5)] if use_dropout else [] - - down = [ - ConvModule( - in_channels=in_channels, - out_channels=inner_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - bias=use_bias, - conv_cfg=down_conv_cfg, - norm_cfg=down_norm_cfg, - act_cfg=down_act_cfg, - order=('act', 'conv', 'norm')) - ] - up = [ - ConvModule( - in_channels=up_in_channels, - out_channels=outer_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - bias=up_bias, - conv_cfg=up_conv_cfg, - norm_cfg=up_norm_cfg, - act_cfg=up_act_cfg, - order=('act', 'conv', 'norm')) - ] - - model = down + middle + up + upper - - self.model = nn.Sequential(*model) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - if self.is_outermost: - return self.model(x) - - # add skip connections - return torch.cat([x, self.model(x)], 1) - - -class ResidualBlockWithDropout(nn.Module): - """Define a Residual Block with dropout layers. - - Ref: - Deep Residual Learning for Image Recognition - - A residual block is a conv block with skip connections. A dropout layer is - added between two common conv modules. - - Args: - channels (int): Number of channels in the conv layer. - padding_mode (str): The name of padding layer: - 'reflect' | 'replicate' | 'zeros'. - norm_cfg (dict): Config dict to build norm layer. Default: - `dict(type='IN')`. - use_dropout (bool): Whether to use dropout layers. Default: True. - """ - - def __init__(self, - channels, - padding_mode, - norm_cfg=dict(type='BN'), - use_dropout=True): - super().__init__() - assert isinstance(norm_cfg, dict), ("'norm_cfg' should be dict, but" - f'got {type(norm_cfg)}') - assert 'type' in norm_cfg, "'norm_cfg' must have key 'type'" - # We use norm layers in the residual block with dropout layers. - # Only for IN, use bias since it does not have affine parameters. - use_bias = norm_cfg['type'] == 'IN' - - block = [ - ConvModule( - in_channels=channels, - out_channels=channels, - kernel_size=3, - padding=1, - bias=use_bias, - norm_cfg=norm_cfg, - padding_mode=padding_mode) - ] - - if use_dropout: - block += [nn.Dropout(0.5)] - - block += [ - ConvModule( - in_channels=channels, - out_channels=channels, - kernel_size=3, - padding=1, - bias=use_bias, - norm_cfg=norm_cfg, - act_cfg=None, - padding_mode=padding_mode) - ] - - self.block = nn.Sequential(*block) - - def forward(self, x): - """Forward function. Add skip connections without final ReLU. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - out = x + self.block(x) - return out diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/img_normalize.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/img_normalize.py deleted file mode 100755 index 1bd8f76aa..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/img_normalize.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class ImgNormalize(nn.Conv2d): - """Normalize images with the given mean and std value. - - Based on Conv2d layer, can work in GPU. - - Args: - pixel_range (float): Pixel range of feature. - img_mean (Tuple[float]): Image mean of each channel. - img_std (Tuple[float]): Image std of each channel. - sign (int): Sign of bias. Default -1. - """ - - def __init__(self, pixel_range, img_mean, img_std, sign=-1): - - assert len(img_mean) == len(img_std) - num_channels = len(img_mean) - super().__init__(num_channels, num_channels, kernel_size=1) - - std = torch.Tensor(img_std) - self.weight.data = torch.eye(num_channels).view( - num_channels, num_channels, 1, 1) - self.weight.data.div_(std.view(num_channels, 1, 1, 1)) - self.bias.data = sign * pixel_range * torch.Tensor(img_mean) - self.bias.data.div_(std) - - self.weight.requires_grad = False - self.bias.requires_grad = False diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/linear_module.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/linear_module.py deleted file mode 100755 index 5101ad164..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/linear_module.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import build_activation_layer, kaiming_init - - -class LinearModule(nn.Module): - """A linear block that contains linear/norm/activation layers. - - For low level vision, we add spectral norm and padding layer. - - Args: - in_features (int): Same as nn.Linear. - out_features (int): Same as nn.Linear. - bias (bool): Same as nn.Linear. - act_cfg (dict): Config dict for activation layer, "relu" by default. - inplace (bool): Whether to use inplace mode for activation. - with_spectral_norm (bool): Whether use spectral norm in linear module. - order (tuple[str]): The order of linear/activation layers. It is a - sequence of "linear", "norm" and "act". Examples are - ("linear", "act") and ("act", "linear"). - """ - - def __init__(self, - in_features, - out_features, - bias=True, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - order=('linear', 'act')): - super().__init__() - assert act_cfg is None or isinstance(act_cfg, dict) - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 2 - assert set(order) == set(['linear', 'act']) - - self.with_activation = act_cfg is not None - self.with_bias = bias - - # build linear layer - self.linear = nn.Linear(in_features, out_features, bias=bias) - # export the attributes of self.linear to a higher level for - # convenience - self.in_features = self.linear.in_features - self.out_features = self.linear.out_features - - if self.with_spectral_norm: - self.linear = nn.utils.spectral_norm(self.linear) - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - def init_weights(self): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - - kaiming_init(self.linear, a=a, nonlinearity=nonlinearity) - - def forward(self, x, activate=True): - """Forward Function. - - Args: - x (torch.Tensor): Input tensor with shape of :math:`(n, *, c)`. - Same as ``torch.nn.Linear``. - activate (bool, optional): Whether to use activation layer. - Defaults to True. - - Returns: - torch.Tensor: Same as ``torch.nn.Linear``. - """ - for layer in self.order: - if layer == 'linear': - x = self.linear(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/mask_conv_module.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/mask_conv_module.py deleted file mode 100755 index e70868686..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/mask_conv_module.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import ConvModule - - -class MaskConvModule(ConvModule): - """Mask convolution module. - - This is a simple wrapper for mask convolution like: 'partial conv'. - Convolutions in this module always need a mask as extra input. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - stride (int or tuple[int]): Same as nn.Conv2d. - padding (int or tuple[int]): Same as nn.Conv2d. - dilation (int or tuple[int]): Same as nn.Conv2d. - groups (int): Same as nn.Conv2d. - bias (bool or str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if norm_cfg is None, otherwise - False. - conv_cfg (dict): Config dict for convolution layer. - norm_cfg (dict): Config dict for normalization layer. - act_cfg (dict): Config dict for activation layer, "relu" by default. - inplace (bool): Whether to use inplace mode for activation. - with_spectral_norm (bool): Whether use spectral norm in conv module. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in Pytorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - """ - supported_conv_list = ['PConv'] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.conv_cfg['type'] in self.supported_conv_list - - self.init_weights() - - def forward(self, - x, - mask=None, - activate=True, - norm=True, - return_mask=True): - """Forward function for partial conv2d. - - Args: - input (torch.Tensor): Tensor with shape of (n, c, h, w). - mask (torch.Tensor): Tensor with shape of (n, c, h, w) or - (n, 1, h, w). If mask is not given, the function will - work as standard conv2d. Default: None. - activate (bool): Whether use activation layer. - norm (bool): Whether use norm layer. - return_mask (bool): If True and mask is not None, the updated - mask will be returned. Default: True. - - Returns: - Tensor or tuple: Result Tensor or 2-tuple of - - ``Tensor``: Results after partial conv. - - ``Tensor``: Updated mask will be returned if mask is given \ - and `return_mask` is True. - """ - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - mask = self.padding_layer(mask) - if return_mask: - x, updated_mask = self.conv( - x, mask, return_mask=return_mask) - else: - x = self.conv(x, mask, return_mask=False) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - - if return_mask: - return x, updated_mask - - return x diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/model_utils.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/model_utils.py deleted file mode 100755 index 62135c6e6..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/model_utils.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch - - -def set_requires_grad(nets, requires_grad=False): - """Set requires_grad for all the networks. - - Args: - nets (nn.Module | list[nn.Module]): A list of networks or a single - network. - requires_grad (bool): Whether the networks require gradients or not - """ - if not isinstance(nets, list): - nets = [nets] - for net in nets: - if net is not None: - for param in net.parameters(): - param.requires_grad = requires_grad - - -def extract_bbox_patch(bbox, img, channel_first=True): - """Extract patch from a given bbox - - Args: - bbox (torch.Tensor | numpy.array): Bbox with (top, left, h, w). If - `img` has batch dimension, the `bbox` must be stacked at first - dimension. The shape should be (4,) or (n, 4). - img (torch.Tensor | numpy.array): Image data to be extracted. If - organized in batch dimension, the batch dimension must be the first - order like (n, h, w, c) or (n, c, h, w). - channel_first (bool): If True, the channel dimension of img is before - height and width, e.g. (c, h, w). Otherwise, the img shape (samples - in the batch) is like (h, w, c). - - Returns: - (torch.Tensor | numpy.array): Extracted patches. The dimension of the \ - output should be the same as `img`. - """ - - def _extract(bbox, img): - assert len(bbox) == 4 - t, l, h, w = bbox - if channel_first: - img_patch = img[..., t:t + h, l:l + w] - else: - img_patch = img[t:t + h, l:l + w, ...] - - return img_patch - - input_size = img.shape - assert len(input_size) == 3 or len(input_size) == 4 - bbox_size = bbox.shape - assert bbox_size == (4, ) or (len(bbox_size) == 2 - and bbox_size[0] == input_size[0]) - - # images with batch dimension - if len(input_size) == 4: - output_list = [] - for i in range(input_size[0]): - img_patch_ = _extract(bbox[i], img[i:i + 1, ...]) - output_list.append(img_patch_) - if isinstance(img, torch.Tensor): - img_patch = torch.cat(output_list, dim=0) - else: - img_patch = np.concatenate(output_list, axis=0) - # standardize image - else: - img_patch = _extract(bbox, img) - - return img_patch - - -def scale_bbox(bbox, target_size): - """Modify bbox to target size. - - The original bbox will be enlarged to the target size with the original - bbox in the center of the new bbox. - - Args: - bbox (np.ndarray | torch.Tensor): Bboxes to be modified. Bbox can - be in batch or not. The shape should be (4,) or (n, 4). - target_size (tuple[int]): Target size of final bbox. - - Returns: - (np.ndarray | torch.Tensor): Modified bboxes. - """ - - def _mod(bbox, target_size): - top_ori, left_ori, h_ori, w_ori = bbox - h, w = target_size - assert h >= h_ori and w >= w_ori - top = int(max(0, top_ori - (h - h_ori) // 2)) - left = int(max(0, left_ori - (w - w_ori) // 2)) - - if isinstance(bbox, torch.Tensor): - bbox_new = torch.Tensor([top, left, h, w]).type_as(bbox) - else: - bbox_new = np.asarray([top, left, h, w]) - - return bbox_new - - if isinstance(bbox, torch.Tensor): - bbox_new = torch.zeros_like(bbox) - elif isinstance(bbox, np.ndarray): - bbox_new = np.zeros_like(bbox) - else: - raise TypeError('bbox mush be torch.Tensor or numpy.ndarray' - f'but got type {type(bbox)}') - bbox_shape = list(bbox.shape) - - if len(bbox_shape) == 2: - for i in range(bbox_shape[0]): - bbox_new[i, :] = _mod(bbox[i], target_size) - else: - bbox_new = _mod(bbox, target_size) - - return bbox_new - - -def extract_around_bbox(img, bbox, target_size, channel_first=True): - """Extract patches around the given bbox. - - Args: - bbox (np.ndarray | torch.Tensor): Bboxes to be modified. Bbox can - be in batch or not. - target_size (List(int)): Target size of final bbox. - - Returns: - (torch.Tensor | numpy.array): Extracted patches. The dimension of the \ - output should be the same as `img`. - """ - bbox_new = scale_bbox(bbox, target_size) - img_patch = extract_bbox_patch(bbox_new, img, channel_first=channel_first) - - return img_patch, bbox_new diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/partial_conv.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/partial_conv.py deleted file mode 100755 index 51b50feca..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/partial_conv.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import CONV_LAYERS - - -@CONV_LAYERS.register_module(name='PConv') -class PartialConv2d(nn.Conv2d): - """Implementation for partial convolution. - - Image Inpainting for Irregular Holes Using Partial Convolutions - [https://arxiv.org/abs/1804.07723] - - Args: - multi_channel (bool): If True, the mask is multi-channel. Otherwise, - the mask is single-channel. - eps (float): Need to be changed for mixed precision training. - For mixed precision training, you need change 1e-8 to 1e-6. - """ - - def __init__(self, *args, multi_channel=False, eps=1e-8, **kwargs): - super().__init__(*args, **kwargs) - - # whether the mask is multi-channel or not - self.multi_channel = multi_channel - self.eps = eps - - if self.multi_channel: - out_channels, in_channels = self.out_channels, self.in_channels - else: - out_channels, in_channels = 1, 1 - - self.register_buffer( - 'weight_mask_updater', - torch.ones(out_channels, in_channels, self.kernel_size[0], - self.kernel_size[1])) - - self.mask_kernel_numel = np.prod(self.weight_mask_updater.shape[1:4]) - self.mask_kernel_numel = (self.mask_kernel_numel).item() - - def forward(self, input, mask=None, return_mask=True): - """Forward function for partial conv2d. - - Args: - input (torch.Tensor): Tensor with shape of (n, c, h, w). - mask (torch.Tensor): Tensor with shape of (n, c, h, w) or - (n, 1, h, w). If mask is not given, the function will - work as standard conv2d. Default: None. - return_mask (bool): If True and mask is not None, the updated - mask will be returned. Default: True. - - Returns: - torch.Tensor : Results after partial conv.\ - torch.Tensor : Updated mask will be returned if mask is given and \ - ``return_mask`` is True. - """ - assert input.dim() == 4 - if mask is not None: - assert mask.dim() == 4 - if self.multi_channel: - assert mask.shape[1] == input.shape[1] - else: - assert mask.shape[1] == 1 - - # update mask and compute mask ratio - if mask is not None: - with torch.no_grad(): - - updated_mask = F.conv2d( - mask, - self.weight_mask_updater, - bias=None, - stride=self.stride, - padding=self.padding, - dilation=self.dilation) - mask_ratio = self.mask_kernel_numel / (updated_mask + self.eps) - - updated_mask = torch.clamp(updated_mask, 0, 1) - mask_ratio = mask_ratio * updated_mask - - # standard conv2d - if mask is not None: - input = input * mask - raw_out = super().forward(input) - - if mask is not None: - if self.bias is None: - output = raw_out * mask_ratio - else: - # compute new bias when mask is given - bias_view = self.bias.view(1, self.out_channels, 1, 1) - output = (raw_out - bias_view) * mask_ratio + bias_view - output = output * updated_mask - else: - output = raw_out - - if return_mask and mask is not None: - return output, updated_mask - - return output diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/separable_conv_module.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/separable_conv_module.py deleted file mode 100755 index 139a54e58..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/separable_conv_module.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if ``norm_cfg`` and ``act_cfg`` are specified. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - stride (int or tuple[int]): Same as nn.Conv2d. Default: 1. - padding (int or tuple[int]): Same as nn.Conv2d. Default: 0. - dilation (int or tuple[int]): Same as nn.Conv2d. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as ``norm_cfg``. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as ``act_cfg``. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as ``act_cfg``. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super().__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (N, C, H, W). - - Returns: - Tensor: Output tensor. - """ - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/sr_backbone_utils.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/sr_backbone_utils.py deleted file mode 100755 index b4b0aad91..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/sr_backbone_utils.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import constant_init, kaiming_init -from mmcv.utils.parrots_wrapper import _BatchNorm - - -def default_init_weights(module, scale=1): - """Initialize network weights. - - Args: - modules (nn.Module): Modules to be initialized. - scale (float): Scale initialized weights, especially for residual - blocks. - """ - for m in module.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m, a=0, mode='fan_in', bias=0) - m.weight.data *= scale - elif isinstance(m, nn.Linear): - kaiming_init(m, a=0, mode='fan_in', bias=0) - m.weight.data *= scale - elif isinstance(m, _BatchNorm): - constant_init(m.weight, val=1, bias=0) - - -def make_layer(block, num_blocks, **kwarg): - """Make layers by stacking the same blocks. - - Args: - block (nn.module): nn.module class for basic block. - num_blocks (int): number of blocks. - - Returns: - nn.Sequential: Stacked blocks in nn.Sequential. - """ - layers = [] - for _ in range(num_blocks): - layers.append(block(**kwarg)) - return nn.Sequential(*layers) - - -class ResidualBlockNoBN(nn.Module): - """Residual block without BN. - - It has a style of: - - :: - - ---Conv-ReLU-Conv-+- - |________________| - - Args: - mid_channels (int): Channel number of intermediate features. - Default: 64. - res_scale (float): Used to scale the residual before addition. - Default: 1.0. - """ - - def __init__(self, mid_channels=64, res_scale=1.0): - super().__init__() - self.res_scale = res_scale - self.conv1 = nn.Conv2d(mid_channels, mid_channels, 3, 1, 1, bias=True) - self.conv2 = nn.Conv2d(mid_channels, mid_channels, 3, 1, 1, bias=True) - - self.relu = nn.ReLU(inplace=True) - - # if res_scale < 1.0, use the default initialization, as in EDSR. - # if res_scale = 1.0, use scaled kaiming_init, as in MSRResNet. - if res_scale == 1.0: - self.init_weights() - - def init_weights(self): - """Initialize weights for ResidualBlockNoBN. - - Initialization methods like `kaiming_init` are for VGG-style - modules. For modules with residual paths, using smaller std is - better for stability and performance. We empirically use 0.1. - See more details in "ESRGAN: Enhanced Super-Resolution Generative - Adversarial Networks" - """ - - for m in [self.conv1, self.conv2]: - default_init_weights(m, 0.1) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - identity = x - out = self.conv2(self.relu(self.conv1(x))) - return identity + out * self.res_scale diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/upsample.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/common/upsample.py deleted file mode 100755 index f39ec1a9e..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/common/upsample.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from .sr_backbone_utils import default_init_weights - - -class PixelShufflePack(nn.Module): - """ Pixel Shuffle upsample layer. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of Conv layer to expand channels. - - Returns: - Upsampled feature map. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super().__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - """Initialize weights for PixelShufflePack. - """ - default_init_weights(self, 1) - - def forward(self, x): - """Forward function for PixelShufflePack. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/components/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/components/__init__.py deleted file mode 100755 index 1184b5667..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/components/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .discriminators import ModifiedVGG diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/components/discriminators/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/components/discriminators/__init__.py deleted file mode 100755 index 81d7240bc..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/components/discriminators/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .modified_vgg import ModifiedVGG - diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/components/discriminators/modified_vgg.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/components/discriminators/modified_vgg.py deleted file mode 100755 index 3aecaf9a4..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/components/discriminators/modified_vgg.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.runner import load_checkpoint - -from mmedit.models.registry import COMPONENTS -from mmedit.utils import get_root_logger - - -@COMPONENTS.register_module() -class ModifiedVGG(nn.Module): - """A modified VGG discriminator with input size 128 x 128. - - It is used to train SRGAN and ESRGAN. - - Args: - in_channels (int): Channel number of inputs. Default: 3. - mid_channels (int): Channel number of base intermediate features. - Default: 64. - """ - - def __init__(self, in_channels, mid_channels): - super().__init__() - - self.conv0_0 = nn.Conv2d(in_channels, mid_channels, 3, 1, 1, bias=True) - self.conv0_1 = nn.Conv2d( - mid_channels, mid_channels, 4, 2, 1, bias=False) - self.bn0_1 = nn.BatchNorm2d(mid_channels, affine=True) - - self.conv1_0 = nn.Conv2d( - mid_channels, mid_channels * 2, 3, 1, 1, bias=False) - self.bn1_0 = nn.BatchNorm2d(mid_channels * 2, affine=True) - self.conv1_1 = nn.Conv2d( - mid_channels * 2, mid_channels * 2, 4, 2, 1, bias=False) - self.bn1_1 = nn.BatchNorm2d(mid_channels * 2, affine=True) - - self.conv2_0 = nn.Conv2d( - mid_channels * 2, mid_channels * 4, 3, 1, 1, bias=False) - self.bn2_0 = nn.BatchNorm2d(mid_channels * 4, affine=True) - self.conv2_1 = nn.Conv2d( - mid_channels * 4, mid_channels * 4, 4, 2, 1, bias=False) - self.bn2_1 = nn.BatchNorm2d(mid_channels * 4, affine=True) - - self.conv3_0 = nn.Conv2d( - mid_channels * 4, mid_channels * 8, 3, 1, 1, bias=False) - self.bn3_0 = nn.BatchNorm2d(mid_channels * 8, affine=True) - self.conv3_1 = nn.Conv2d( - mid_channels * 8, mid_channels * 8, 4, 2, 1, bias=False) - self.bn3_1 = nn.BatchNorm2d(mid_channels * 8, affine=True) - - self.conv4_0 = nn.Conv2d( - mid_channels * 8, mid_channels * 8, 3, 1, 1, bias=False) - self.bn4_0 = nn.BatchNorm2d(mid_channels * 8, affine=True) - self.conv4_1 = nn.Conv2d( - mid_channels * 8, mid_channels * 8, 4, 2, 1, bias=False) - self.bn4_1 = nn.BatchNorm2d(mid_channels * 8, affine=True) - - self.linear1 = nn.Linear(mid_channels * 8 * 4 * 4, 100) - self.linear2 = nn.Linear(100, 1) - - # activation function - self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - assert x.size(2) == 128 and x.size(3) == 128, ( - f'Input spatial size must be 128x128, ' - f'but received {x.size()}.') - - feat = self.lrelu(self.conv0_0(x)) - feat = self.lrelu(self.bn0_1( - self.conv0_1(feat))) # output spatial size: (64, 64) - - feat = self.lrelu(self.bn1_0(self.conv1_0(feat))) - feat = self.lrelu(self.bn1_1( - self.conv1_1(feat))) # output spatial size: (32, 32) - - feat = self.lrelu(self.bn2_0(self.conv2_0(feat))) - feat = self.lrelu(self.bn2_1( - self.conv2_1(feat))) # output spatial size: (16, 16) - - feat = self.lrelu(self.bn3_0(self.conv3_0(feat))) - feat = self.lrelu(self.bn3_1( - self.conv3_1(feat))) # output spatial size: (8, 8) - - feat = self.lrelu(self.bn4_0(self.conv4_0(feat))) - feat = self.lrelu(self.bn4_1( - self.conv4_1(feat))) # output spatial size: (4, 4) - - feat = feat.view(feat.size(0), -1) - feat = self.lrelu(self.linear1(feat)) - out = self.linear2(feat) - return out - - def init_weights(self, pretrained=None, strict=True): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - strict (boo, optional): Whether strictly load the pretrained model. - Defaults to True. - """ - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=strict, logger=logger) - elif pretrained is None: - pass # Use PyTorch default initialization. - else: - raise TypeError(f'"pretrained" must be a str or None. ' - f'But received {type(pretrained)}.') diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/__init__.py deleted file mode 100755 index 1ca5b5d6d..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .gan_loss import DiscShiftLoss, GANLoss, GaussianBlur, GradientPenaltyLoss -from .perceptual_loss import (PerceptualLoss, PerceptualVGG, - TransferalPerceptualLoss) -from .pixelwise_loss import CharbonnierLoss, L1Loss, MaskedTVLoss, MSELoss - diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/gan_loss.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/gan_loss.py deleted file mode 100755 index 35a6af812..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/gan_loss.py +++ /dev/null @@ -1,344 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.autograd as autograd -import torch.nn as nn -import torch.nn.functional as F -from torch.nn.functional import conv2d - -from ..registry import LOSSES - - -@LOSSES.register_module() -class GANLoss(nn.Module): - """Define GAN loss. - - Args: - gan_type (str): Support 'vanilla', 'lsgan', 'wgan', 'hinge'. - real_label_val (float): The value for real label. Default: 1.0. - fake_label_val (float): The value for fake label. Default: 0.0. - loss_weight (float): Loss weight. Default: 1.0. - Note that loss_weight is only for generators; and it is always 1.0 - for discriminators. - """ - - def __init__(self, - gan_type, - real_label_val=1.0, - fake_label_val=0.0, - loss_weight=1.0): - super().__init__() - self.gan_type = gan_type - self.real_label_val = real_label_val - self.fake_label_val = fake_label_val - self.loss_weight = loss_weight - if self.gan_type == 'smgan': - self.gaussian_blur = GaussianBlur() - - if self.gan_type == 'vanilla': - self.loss = nn.BCEWithLogitsLoss() - elif self.gan_type == 'lsgan' or self.gan_type == 'smgan': - self.loss = nn.MSELoss() - elif self.gan_type == 'wgan': - self.loss = self._wgan_loss - elif self.gan_type == 'hinge': - self.loss = nn.ReLU() - else: - raise NotImplementedError( - f'GAN type {self.gan_type} is not implemented.') - - def _wgan_loss(self, input, target): - """wgan loss. - - Args: - input (Tensor): Input tensor. - target (bool): Target label. - - Returns: - Tensor: wgan loss. - """ - - return -input.mean() if target else input.mean() - - def get_target_label(self, input, target_is_real): - """Get target label. - - Args: - input (Tensor): Input tensor. - target_is_real (bool): Whether the target is real or fake. - - Returns: - (bool | Tensor): Target tensor. Return bool for wgan, otherwise, - return Tensor. - """ - - if self.gan_type == 'wgan': - return target_is_real - target_val = ( - self.real_label_val if target_is_real else self.fake_label_val) - return input.new_ones(input.size()) * target_val - - def forward(self, input, target_is_real, is_disc=False, mask=None): - """ - Args: - input (Tensor): The input for the loss module, i.e., the network - prediction. - target_is_real (bool): Whether the target is real or fake. - is_disc (bool): Whether the loss for discriminators or not. - Default: False. - - Returns: - Tensor: GAN loss value. - """ - - target_label = self.get_target_label(input, target_is_real) - if self.gan_type == 'hinge': - if is_disc: # for discriminators in hinge-gan - input = -input if target_is_real else input - loss = self.loss(1 + input).mean() - else: # for generators in hinge-gan - loss = -input.mean() - elif self.gan_type == 'smgan': - - input_height, input_width = input.shape[2:] - mask_height, mask_width = mask.shape[2:] - - # Handle inconsistent size between outputs and masks - if input_height != mask_height or input_width != mask_width: - input = F.interpolate( - input, - size=(mask_height, mask_width), - mode='bilinear', - align_corners=True) - - target_label = self.get_target_label(input, target_is_real) - - if is_disc: - if target_is_real: - target_label = target_label - else: - target_label = self.gaussian_blur(mask).detach().cuda( - ) if mask.is_cuda else self.gaussian_blur( - mask).detach().cpu() - # target_label = self.gaussian_blur(mask).detach().cpu() - loss = self.loss(input, target_label) - else: - loss = self.loss(input, target_label) * mask / mask.mean() - loss = loss.mean() - else: # other gan types - loss = self.loss(input, target_label) - - # loss_weight is always 1.0 for discriminators - return loss if is_disc else loss * self.loss_weight - - -@LOSSES.register_module() -class GaussianBlur(nn.Module): - """A Gaussian filter which blurs a given tensor with a two-dimensional - gaussian kernel by convolving it along each channel. Batch operation - is supported. - - This function is modified from kornia.filters.gaussian: - ``. - - Args: - kernel_size (tuple[int]): The size of the kernel. Default: (71, 71). - sigma (tuple[float]): The standard deviation of the kernel. - Default (10.0, 10.0) - - Returns: - Tensor: The Gaussian-blurred tensor. - - Shape: - - input: Tensor with shape of (n, c, h, w) - - output: Tensor with shape of (n, c, h, w) - """ - - def __init__(self, kernel_size=(71, 71), sigma=(10.0, 10.0)): - super(GaussianBlur, self).__init__() - self.kernel_size = kernel_size - self.sigma = sigma - self.padding = self.compute_zero_padding(kernel_size) - self.kernel = self.get_2d_gaussian_kernel(kernel_size, sigma) - - @staticmethod - def compute_zero_padding(kernel_size): - """Compute zero padding tuple.""" - - padding = [(ks - 1) // 2 for ks in kernel_size] - - return padding[0], padding[1] - - def get_2d_gaussian_kernel(self, kernel_size, sigma): - """Get the two-dimensional Gaussian filter matrix coefficients. - - Args: - kernel_size (tuple[int]): Kernel filter size in the x and y - direction. The kernel sizes - should be odd and positive. - sigma (tuple[int]): Gaussian standard deviation in - the x and y direction. - - Returns: - kernel_2d (Tensor): A 2D torch tensor with gaussian filter - matrix coefficients. - """ - - if not isinstance(kernel_size, tuple) or len(kernel_size) != 2: - raise TypeError( - 'kernel_size must be a tuple of length two. Got {}'.format( - kernel_size)) - if not isinstance(sigma, tuple) or len(sigma) != 2: - raise TypeError( - 'sigma must be a tuple of length two. Got {}'.format(sigma)) - - kernel_size_x, kernel_size_y = kernel_size - sigma_x, sigma_y = sigma - - kernel_x = self.get_1d_gaussian_kernel(kernel_size_x, sigma_x) - kernel_y = self.get_1d_gaussian_kernel(kernel_size_y, sigma_y) - kernel_2d = torch.matmul( - kernel_x.unsqueeze(-1), - kernel_y.unsqueeze(-1).t()) - - return kernel_2d - - def get_1d_gaussian_kernel(self, kernel_size, sigma): - """Get the Gaussian filter coefficients in one dimension (x or y direction). - - Args: - kernel_size (int): Kernel filter size in x or y direction. - Should be odd and positive. - sigma (float): Gaussian standard deviation in x or y direction. - - Returns: - kernel_1d (Tensor): A 1D torch tensor with gaussian filter - coefficients in x or y direction. - """ - - if not isinstance(kernel_size, - int) or kernel_size % 2 == 0 or kernel_size <= 0: - raise TypeError( - 'kernel_size must be an odd positive integer. Got {}'.format( - kernel_size)) - - kernel_1d = self.gaussian(kernel_size, sigma) - return kernel_1d - - def gaussian(self, kernel_size, sigma): - - def gauss_arg(x): - return -(x - kernel_size // 2)**2 / float(2 * sigma**2) - - gauss = torch.stack([ - torch.exp(torch.tensor(gauss_arg(x))) for x in range(kernel_size) - ]) - return gauss / gauss.sum() - - def forward(self, x): - if not torch.is_tensor(x): - raise TypeError( - 'Input x type is not a torch.Tensor. Got {}'.format(type(x))) - if not len(x.shape) == 4: - raise ValueError( - 'Invalid input shape, we expect BxCxHxW. Got: {}'.format( - x.shape)) - _, c, _, _ = x.shape - tmp_kernel = self.kernel.to(x.device).to(x.dtype) - kernel = tmp_kernel.repeat(c, 1, 1, 1) - - return conv2d(x, kernel, padding=self.padding, stride=1, groups=c) - - -def gradient_penalty_loss(discriminator, real_data, fake_data, mask=None): - """Calculate gradient penalty for wgan-gp. - - Args: - discriminator (nn.Module): Network for the discriminator. - real_data (Tensor): Real input data. - fake_data (Tensor): Fake input data. - mask (Tensor): Masks for inpainting. Default: None. - - Returns: - Tensor: A tensor for gradient penalty. - """ - - batch_size = real_data.size(0) - alpha = torch.rand(batch_size, 1, 1, 1).to(real_data) - - # interpolate between real_data and fake_data - interpolates = alpha * real_data + (1. - alpha) * fake_data - interpolates = autograd.Variable(interpolates, requires_grad=True) - - disc_interpolates = discriminator(interpolates) - gradients = autograd.grad( - outputs=disc_interpolates, - inputs=interpolates, - grad_outputs=torch.ones_like(disc_interpolates), - create_graph=True, - retain_graph=True, - only_inputs=True)[0] - - if mask is not None: - gradients = gradients * mask - - gradients_penalty = ((gradients.norm(2, dim=1) - 1)**2).mean() - if mask is not None: - gradients_penalty /= torch.mean(mask) - - return gradients_penalty - - -@LOSSES.register_module() -class GradientPenaltyLoss(nn.Module): - """Gradient penalty loss for wgan-gp. - - Args: - loss_weight (float): Loss weight. Default: 1.0. - """ - - def __init__(self, loss_weight=1.): - super().__init__() - self.loss_weight = loss_weight - - def forward(self, discriminator, real_data, fake_data, mask=None): - """Forward function. - - Args: - discriminator (nn.Module): Network for the discriminator. - real_data (Tensor): Real input data. - fake_data (Tensor): Fake input data. - mask (Tensor): Masks for inpainting. Default: None. - - Returns: - Tensor: Loss. - """ - loss = gradient_penalty_loss( - discriminator, real_data, fake_data, mask=mask) - - return loss * self.loss_weight - - -@LOSSES.register_module() -class DiscShiftLoss(nn.Module): - """Disc shift loss. - - Args: - loss_weight (float, optional): Loss weight. Defaults to 1.0. - """ - - def __init__(self, loss_weight=0.1): - super().__init__() - self.loss_weight = loss_weight - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Tensor with shape (n, c, h, w) - - Returns: - Tensor: Loss. - """ - loss = torch.mean(x**2) - - return loss * self.loss_weight diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/perceptual_loss.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/perceptual_loss.py deleted file mode 100755 index 365790464..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/perceptual_loss.py +++ /dev/null @@ -1,287 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torchvision.models.vgg as vgg -from mmcv.runner import load_checkpoint -from torch.nn import functional as F - -from mmedit.utils import get_root_logger -from ..registry import LOSSES - - -class PerceptualVGG(nn.Module): - """VGG network used in calculating perceptual loss. - - In this implementation, we allow users to choose whether use normalization - in the input feature and the type of vgg network. Note that the pretrained - path must fit the vgg type. - - Args: - layer_name_list (list[str]): According to the name in this list, - forward function will return the corresponding features. This - list contains the name each layer in `vgg.feature`. An example - of this list is ['4', '10']. - vgg_type (str): Set the type of vgg network. Default: 'vgg19'. - use_input_norm (bool): If True, normalize the input image. - Importantly, the input feature must in the range [0, 1]. - Default: True. - pretrained (str): Path for pretrained weights. Default: - 'torchvision://vgg19' - """ - - def __init__(self, - layer_name_list, - vgg_type='vgg19', - use_input_norm=True, - pretrained='torchvision://vgg19'): - super().__init__() - if pretrained.startswith('torchvision://'): - assert vgg_type in pretrained - self.layer_name_list = layer_name_list - self.use_input_norm = use_input_norm - - # get vgg model and load pretrained vgg weight - # remove _vgg from attributes to avoid `find_unused_parameters` bug - _vgg = getattr(vgg, vgg_type)() - self.init_weights(_vgg, pretrained) - num_layers = max(map(int, layer_name_list)) + 1 - assert len(_vgg.features) >= num_layers - # only borrow layers that will be used from _vgg to avoid unused params - self.vgg_layers = _vgg.features[:num_layers] - - if self.use_input_norm: - # the mean is for image with range [0, 1] - self.register_buffer( - 'mean', - torch.Tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1)) - # the std is for image with range [-1, 1] - self.register_buffer( - 'std', - torch.Tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1)) - - for v in self.vgg_layers.parameters(): - v.requires_grad = False - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - if self.use_input_norm: - x = (x - self.mean) / self.std - output = {} - - for name, module in self.vgg_layers.named_children(): - x = module(x) - if name in self.layer_name_list: - output[name] = x.clone() - return output - - def init_weights(self, model, pretrained): - """Init weights. - - Args: - model (nn.Module): Models to be inited. - pretrained (str): Path for pretrained weights. - """ - logger = get_root_logger() - load_checkpoint(model, pretrained, logger=logger) - - -@LOSSES.register_module() -class PerceptualLoss(nn.Module): - """Perceptual loss with commonly used style loss. - - Args: - layers_weights (dict): The weight for each layer of vgg feature for - perceptual loss. Here is an example: {'4': 1., '9': 1., '18': 1.}, - which means the 5th, 10th and 18th feature layer will be - extracted with weight 1.0 in calculating losses. - layers_weights_style (dict): The weight for each layer of vgg feature - for style loss. If set to 'None', the weights are set equal to - the weights for perceptual loss. Default: None. - vgg_type (str): The type of vgg network used as feature extractor. - Default: 'vgg19'. - use_input_norm (bool): If True, normalize the input image in vgg. - Default: True. - perceptual_weight (float): If `perceptual_weight > 0`, the perceptual - loss will be calculated and the loss will multiplied by the - weight. Default: 1.0. - style_weight (float): If `style_weight > 0`, the style loss will be - calculated and the loss will multiplied by the weight. - Default: 1.0. - norm_img (bool): If True, the image will be normed to [0, 1]. Note that - this is different from the `use_input_norm` which norm the input in - in forward function of vgg according to the statistics of dataset. - Importantly, the input image must be in range [-1, 1]. - pretrained (str): Path for pretrained weights. Default: - 'torchvision://vgg19'. - criterion (str): Criterion type. Options are 'l1' and 'mse'. - Default: 'l1'. - """ - - def __init__(self, - layer_weights, - layer_weights_style=None, - vgg_type='vgg19', - use_input_norm=True, - perceptual_weight=1.0, - style_weight=1.0, - norm_img=True, - pretrained='torchvision://vgg19', - criterion='l1'): - super().__init__() - self.norm_img = norm_img - self.perceptual_weight = perceptual_weight - self.style_weight = style_weight - self.layer_weights = layer_weights - self.layer_weights_style = layer_weights_style - - self.vgg = PerceptualVGG( - layer_name_list=list(self.layer_weights.keys()), - vgg_type=vgg_type, - use_input_norm=use_input_norm, - pretrained=pretrained) - - if self.layer_weights_style is not None and \ - self.layer_weights_style != self.layer_weights: - self.vgg_style = PerceptualVGG( - layer_name_list=list(self.layer_weights_style.keys()), - vgg_type=vgg_type, - use_input_norm=use_input_norm, - pretrained=pretrained) - else: - self.layer_weights_style = self.layer_weights - self.vgg_style = None - - criterion = criterion.lower() - if criterion == 'l1': - self.criterion = torch.nn.L1Loss() - elif criterion == 'mse': - self.criterion = torch.nn.MSELoss() - else: - raise NotImplementedError( - f'{criterion} criterion has not been supported in' - ' this version.') - - def forward(self, x, gt): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - gt (Tensor): Ground-truth tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - if self.norm_img: - x = (x + 1.) * 0.5 - gt = (gt + 1.) * 0.5 - # extract vgg features - x_features = self.vgg(x) - gt_features = self.vgg(gt.detach()) - - # calculate perceptual loss - if self.perceptual_weight > 0: - percep_loss = 0 - for k in x_features.keys(): - percep_loss += self.criterion( - x_features[k], gt_features[k]) * self.layer_weights[k] - percep_loss *= self.perceptual_weight - else: - percep_loss = None - - # calculate style loss - if self.style_weight > 0: - if self.vgg_style is not None: - x_features = self.vgg_style(x) - gt_features = self.vgg_style(gt.detach()) - - style_loss = 0 - for k in x_features.keys(): - style_loss += self.criterion( - self._gram_mat(x_features[k]), - self._gram_mat( - gt_features[k])) * self.layer_weights_style[k] - style_loss *= self.style_weight - else: - style_loss = None - - return percep_loss, style_loss - - def _gram_mat(self, x): - """Calculate Gram matrix. - - Args: - x (torch.Tensor): Tensor with shape of (n, c, h, w). - - Returns: - torch.Tensor: Gram matrix. - """ - (n, c, h, w) = x.size() - features = x.view(n, c, w * h) - features_t = features.transpose(1, 2) - gram = features.bmm(features_t) / (c * h * w) - return gram - - -@LOSSES.register_module() -class TransferalPerceptualLoss(nn.Module): - """Transferal perceptual loss. - - Args: - loss_weight (float): Loss weight. Default: 1.0. - use_attention (bool): If True, use soft-attention tensor. Default: True - criterion (str): Criterion type. Options are 'l1' and 'mse'. - Default: 'l1'. - """ - - def __init__(self, loss_weight=1.0, use_attention=True, criterion='mse'): - super().__init__() - self.use_attention = use_attention - self.loss_weight = loss_weight - criterion = criterion.lower() - if criterion == 'l1': - self.loss_function = torch.nn.L1Loss() - elif criterion == 'mse': - self.loss_function = torch.nn.MSELoss() - else: - raise ValueError( - f"criterion should be 'l1' or 'mse', but got {criterion}") - - def forward(self, maps, soft_attention, textures): - """Forward function. - - Args: - maps (Tuple[Tensor]): Input tensors. - soft_attention (Tensor): Soft-attention tensor. - textures (Tuple[Tensor]): Ground-truth tensors. - - Returns: - Tensor: Forward results. - """ - - if self.use_attention: - h, w = soft_attention.shape[-2:] - softs = [torch.sigmoid(soft_attention)] - for i in range(1, len(maps)): - softs.append( - F.interpolate( - soft_attention, - size=(h * pow(2, i), w * pow(2, i)), - mode='bicubic', - align_corners=False)) - else: - softs = [1., 1., 1.] - - loss_texture = 0 - for map, soft, texture in zip(maps, softs, textures): - loss_texture += self.loss_function(map * soft, texture * soft) - - return loss_texture * self.loss_weight diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/pixelwise_loss.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/pixelwise_loss.py deleted file mode 100755 index 2f2435b8d..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/pixelwise_loss.py +++ /dev/null @@ -1,221 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..registry import LOSSES -from .utils import masked_loss - -_reduction_modes = ['none', 'mean', 'sum'] - - -@masked_loss -def l1_loss(pred, target): - """L1 loss. - - Args: - pred (Tensor): Prediction Tensor with shape (n, c, h, w). - target ([type]): Target Tensor with shape (n, c, h, w). - - Returns: - Tensor: Calculated L1 loss. - """ - return F.l1_loss(pred, target, reduction='none') - - -@masked_loss -def mse_loss(pred, target): - """MSE loss. - - Args: - pred (Tensor): Prediction Tensor with shape (n, c, h, w). - target ([type]): Target Tensor with shape (n, c, h, w). - - Returns: - Tensor: Calculated MSE loss. - """ - return F.mse_loss(pred, target, reduction='none') - - -@masked_loss -def charbonnier_loss(pred, target, eps=1e-12): - """Charbonnier loss. - - Args: - pred (Tensor): Prediction Tensor with shape (n, c, h, w). - target ([type]): Target Tensor with shape (n, c, h, w). - - Returns: - Tensor: Calculated Charbonnier loss. - """ - return torch.sqrt((pred - target)**2 + eps) - - -@LOSSES.register_module() -class L1Loss(nn.Module): - """L1 (mean absolute error, MAE) loss. - - Args: - loss_weight (float): Loss weight for L1 loss. Default: 1.0. - reduction (str): Specifies the reduction to apply to the output. - Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduce loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - """ - - def __init__(self, loss_weight=1.0, reduction='mean', sample_wise=False): - super().__init__() - if reduction not in ['none', 'mean', 'sum']: - raise ValueError(f'Unsupported reduction mode: {reduction}. ' - f'Supported ones are: {_reduction_modes}') - - self.loss_weight = loss_weight - self.reduction = reduction - self.sample_wise = sample_wise - - def forward(self, pred, target, weight=None, **kwargs): - """Forward Function. - - Args: - pred (Tensor): of shape (N, C, H, W). Predicted tensor. - target (Tensor): of shape (N, C, H, W). Ground truth tensor. - weight (Tensor, optional): of shape (N, C, H, W). Element-wise - weights. Default: None. - """ - return self.loss_weight * l1_loss( - pred, - target, - weight, - reduction=self.reduction, - sample_wise=self.sample_wise) - - -@LOSSES.register_module() -class MSELoss(nn.Module): - """MSE (L2) loss. - - Args: - loss_weight (float): Loss weight for MSE loss. Default: 1.0. - reduction (str): Specifies the reduction to apply to the output. - Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduces loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - """ - - def __init__(self, loss_weight=1.0, reduction='mean', sample_wise=False): - super().__init__() - if reduction not in ['none', 'mean', 'sum']: - raise ValueError(f'Unsupported reduction mode: {reduction}. ' - f'Supported ones are: {_reduction_modes}') - - self.loss_weight = loss_weight - self.reduction = reduction - self.sample_wise = sample_wise - - def forward(self, pred, target, weight=None, **kwargs): - """Forward Function. - - Args: - pred (Tensor): of shape (N, C, H, W). Predicted tensor. - target (Tensor): of shape (N, C, H, W). Ground truth tensor. - weight (Tensor, optional): of shape (N, C, H, W). Element-wise - weights. Default: None. - """ - return self.loss_weight * mse_loss( - pred, - target, - weight, - reduction=self.reduction, - sample_wise=self.sample_wise) - - -@LOSSES.register_module() -class CharbonnierLoss(nn.Module): - """Charbonnier loss (one variant of Robust L1Loss, a differentiable - variant of L1Loss). - - Described in "Deep Laplacian Pyramid Networks for Fast and Accurate - Super-Resolution". - - Args: - loss_weight (float): Loss weight for L1 loss. Default: 1.0. - reduction (str): Specifies the reduction to apply to the output. - Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduces loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - eps (float): A value used to control the curvature near zero. - Default: 1e-12. - """ - - def __init__(self, - loss_weight=1.0, - reduction='mean', - sample_wise=False, - eps=1e-12): - super().__init__() - if reduction not in ['none', 'mean', 'sum']: - raise ValueError(f'Unsupported reduction mode: {reduction}. ' - f'Supported ones are: {_reduction_modes}') - - self.loss_weight = loss_weight - self.reduction = reduction - self.sample_wise = sample_wise - self.eps = eps - - def forward(self, pred, target, weight=None, **kwargs): - """Forward Function. - - Args: - pred (Tensor): of shape (N, C, H, W). Predicted tensor. - target (Tensor): of shape (N, C, H, W). Ground truth tensor. - weight (Tensor, optional): of shape (N, C, H, W). Element-wise - weights. Default: None. - """ - return self.loss_weight * charbonnier_loss( - pred, - target, - weight, - eps=self.eps, - reduction=self.reduction, - sample_wise=self.sample_wise) - - -@LOSSES.register_module() -class MaskedTVLoss(L1Loss): - """Masked TV loss. - - Args: - loss_weight (float, optional): Loss weight. Defaults to 1.0. - """ - - def __init__(self, loss_weight=1.0): - super().__init__(loss_weight=loss_weight) - - def forward(self, pred, mask=None): - """Forward function. - - Args: - pred (torch.Tensor): Tensor with shape of (n, c, h, w). - mask (torch.Tensor, optional): Tensor with shape of (n, 1, h, w). - Defaults to None. - - Returns: - [type]: [description] - """ - y_diff = super().forward( - pred[:, :, :-1, :], pred[:, :, 1:, :], weight=mask[:, :, :-1, :]) - x_diff = super().forward( - pred[:, :, :, :-1], pred[:, :, :, 1:], weight=mask[:, :, :, :-1]) - - loss = x_diff + y_diff - - return loss diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/utils.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/utils.py deleted file mode 100755 index 2f536d924..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/losses/utils.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch.nn.functional as F - - -def reduce_loss(loss, reduction): - """Reduce loss as specified. - - Args: - loss (Tensor): Elementwise loss tensor. - reduction (str): Options are "none", "mean" and "sum". - - Returns: - Tensor: Reduced loss tensor. - """ - reduction_enum = F._Reduction.get_enum(reduction) - # none: 0, elementwise_mean:1, sum: 2 - if reduction_enum == 0: - return loss - if reduction_enum == 1: - return loss.mean() - - return loss.sum() - - -def mask_reduce_loss(loss, weight=None, reduction='mean', sample_wise=False): - """Apply element-wise weight and reduce loss. - - Args: - loss (Tensor): Element-wise loss. - weight (Tensor): Element-wise weights. Default: None. - reduction (str): Same as built-in losses of PyTorch. Options are - "none", "mean" and "sum". Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduces loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - - Returns: - Tensor: Processed loss values. - """ - # if weight is specified, apply element-wise weight - if weight is not None: - assert weight.dim() == loss.dim() - assert weight.size(1) == 1 or weight.size(1) == loss.size(1) - loss = loss * weight - - # if weight is not specified or reduction is sum, just reduce the loss - if weight is None or reduction == 'sum': - loss = reduce_loss(loss, reduction) - # if reduction is mean, then compute mean over masked region - elif reduction == 'mean': - # expand weight from N1HW to NCHW - if weight.size(1) == 1: - weight = weight.expand_as(loss) - # small value to prevent division by zero - eps = 1e-12 - - # perform sample-wise mean - if sample_wise: - weight = weight.sum(dim=[1, 2, 3], keepdim=True) # NCHW to N111 - loss = (loss / (weight + eps)).sum() / weight.size(0) - # perform pixel-wise mean - else: - loss = loss.sum() / (weight.sum() + eps) - - return loss - - -def masked_loss(loss_func): - """Create a masked version of a given loss function. - - To use this decorator, the loss function must have the signature like - `loss_func(pred, target, **kwargs)`. The function only needs to compute - element-wise loss without any reduction. This decorator will add weight - and reduction arguments to the function. The decorated function will have - the signature like `loss_func(pred, target, weight=None, reduction='mean', - avg_factor=None, **kwargs)`. - - :Example: - - >>> import torch - >>> @masked_loss - >>> def l1_loss(pred, target): - >>> return (pred - target).abs() - - >>> pred = torch.Tensor([0, 2, 3]) - >>> target = torch.Tensor([1, 1, 1]) - >>> weight = torch.Tensor([1, 0, 1]) - - >>> l1_loss(pred, target) - tensor(1.3333) - >>> l1_loss(pred, target, weight) - tensor(1.5000) - >>> l1_loss(pred, target, reduction='none') - tensor([1., 1., 2.]) - >>> l1_loss(pred, target, weight, reduction='sum') - tensor(3.) - """ - - @functools.wraps(loss_func) - def wrapper(pred, - target, - weight=None, - reduction='mean', - sample_wise=False, - **kwargs): - # get element-wise loss - loss = loss_func(pred, target, **kwargs) - loss = mask_reduce_loss(loss, weight, reduction, sample_wise) - return loss - - return wrapper diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/registry.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/registry.py deleted file mode 100755 index 0a574b667..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import MODELS as MMCV_MODELS -from mmcv.utils import Registry - -MODELS = Registry('model', parent=MMCV_MODELS) -BACKBONES = MODELS -COMPONENTS = MODELS -LOSSES = MODELS diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/__init__.py deleted file mode 100755 index f1e647f75..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .esrgan import ESRGAN -from .srgan import SRGAN -from .basic_restorer import BasicRestorer diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/basic_restorer.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/basic_restorer.py deleted file mode 100755 index 5114b8e9a..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/basic_restorer.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -import os.path as osp - -import mmcv -from mmcv.runner import auto_fp16 - -from mmedit.core import psnr, ssim, tensor2img -from ..base import BaseModel -from ..builder import build_backbone, build_loss -from ..registry import MODELS - - -@MODELS.register_module() -class BasicRestorer(BaseModel): - """Basic model for image restoration. - - It must contain a generator that takes an image as inputs and outputs a - restored image. It also has a pixel-wise loss for training. - - The subclasses should overwrite the function `forward_train`, - `forward_test` and `train_step`. - - Args: - generator (dict): Config for the generator structure. - pixel_loss (dict): Config for pixel-wise loss. - train_cfg (dict): Config for training. Default: None. - test_cfg (dict): Config for testing. Default: None. - pretrained (str): Path for pretrained model. Default: None. - """ - allowed_metrics = {'PSNR': psnr, 'SSIM': ssim} - - def __init__(self, - generator, - pixel_loss, - train_cfg=None, - test_cfg=None, - pretrained=None): - super().__init__() - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - # support fp16 - self.fp16_enabled = False - - # generator - self.generator = build_backbone(generator) - self.init_weights(pretrained) - - # loss - self.pixel_loss = build_loss(pixel_loss) - - def init_weights(self, pretrained=None): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - """ - self.generator.init_weights(pretrained) - - @auto_fp16(apply_to=('lq', )) - def forward(self, lq, gt=None, test_mode=False, **kwargs): - """Forward function. - - Args: - lq (Tensor): Input lq images. - gt (Tensor): Ground-truth image. Default: None. - test_mode (bool): Whether in test mode or not. Default: False. - kwargs (dict): Other arguments. - """ - - if test_mode: - return self.forward_test(lq, gt, **kwargs) - - return self.forward_train(lq, gt) - - def forward_train(self, lq, gt): - """Training forward function. - - Args: - lq (Tensor): LQ Tensor with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). - - Returns: - Tensor: Output tensor. - """ - losses = dict() - output = self.generator(lq) - loss_pix = self.pixel_loss(output, gt) - losses['loss_pix'] = loss_pix - outputs = dict( - losses=losses, - num_samples=len(gt.data), - results=dict(lq=lq.cpu(), gt=gt.cpu(), output=output.cpu())) - return outputs - - def evaluate(self, output, gt): - """Evaluation function. - - Args: - output (Tensor): Model output with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). - - Returns: - dict: Evaluation results. - """ - crop_border = self.test_cfg.crop_border - - output = tensor2img(output) - gt = tensor2img(gt) - - eval_result = dict() - for metric in self.test_cfg.metrics: - eval_result[metric] = self.allowed_metrics[metric](output, gt, - crop_border) - return eval_result - - def forward_test(self, - lq, - gt=None, - meta=None, - save_image=False, - save_path=None, - iteration=None): - """Testing forward function. - - Args: - lq (Tensor): LQ Tensor with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). Default: None. - save_image (bool): Whether to save image. Default: False. - save_path (str): Path to save image. Default: None. - iteration (int): Iteration for the saving image name. - Default: None. - - Returns: - dict: Output results. - """ - output = self.generator(lq) - if self.test_cfg is not None and self.test_cfg.get('metrics', None): - assert gt is not None, ( - 'evaluation with metrics must have gt images.') - results = dict(eval_result=self.evaluate(output, gt)) - else: - results = dict(lq=lq.cpu(), output=output.cpu()) - if gt is not None: - results['gt'] = gt.cpu() - - # save image - if save_image: - lq_path = meta[0]['lq_path'] - folder_name = osp.splitext(osp.basename(lq_path))[0] - if isinstance(iteration, numbers.Number): - save_path = osp.join(save_path, folder_name, - f'{folder_name}-{iteration + 1:06d}.png') - elif iteration is None: - save_path = osp.join(save_path, f'{folder_name}.png') - else: - raise ValueError('iteration should be number or None, ' - f'but got {type(iteration)}') - mmcv.imwrite(tensor2img(output), save_path) - - return results - - def forward_dummy(self, img): - """Used for computing network FLOPs. - - Args: - img (Tensor): Input image. - - Returns: - Tensor: Output image. - """ - out = self.generator(img) - return out - - def train_step(self, data_batch, optimizer): - """Train step. - - Args: - data_batch (dict): A batch of data. - optimizer (obj): Optimizer. - - Returns: - dict: Returned output. - """ - outputs = self(**data_batch, test_mode=False) - loss, log_vars = self.parse_losses(outputs.pop('losses')) - - # optimize - optimizer['generator'].zero_grad() - loss.backward() - optimizer['generator'].step() - - outputs.update({'log_vars': log_vars}) - return outputs - - def val_step(self, data_batch, **kwargs): - """Validation step. - - Args: - data_batch (dict): A batch of data. - kwargs (dict): Other arguments for ``val_step``. - - Returns: - dict: Returned output. - """ - output = self.forward_test(**data_batch, **kwargs) - return output diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/esrgan.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/esrgan.py deleted file mode 100755 index 4e787c0e4..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/esrgan.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..common import set_requires_grad -from ..registry import MODELS -from .srgan import SRGAN - - -@MODELS.register_module() -class ESRGAN(SRGAN): - """Enhanced SRGAN model for single image super-resolution. - - Ref: - ESRGAN: Enhanced Super-Resolution Generative Adversarial Networks. - It uses RaGAN for GAN updates: - The relativistic discriminator: a key element missing from standard GAN. - - Args: - generator (dict): Config for the generator. - discriminator (dict): Config for the discriminator. Default: None. - gan_loss (dict): Config for the gan loss. - Note that the loss weight in gan loss is only for the generator. - pixel_loss (dict): Config for the pixel loss. Default: None. - perceptual_loss (dict): Config for the perceptual loss. Default: None. - train_cfg (dict): Config for training. Default: None. - You may change the training of gan by setting: - `disc_steps`: how many discriminator updates after one generate - update; - `disc_init_steps`: how many discriminator updates at the start of - the training. - These two keys are useful when training with WGAN. - test_cfg (dict): Config for testing. Default: None. - pretrained (str): Path for pretrained model. Default: None. - """ - - def train_step(self, data_batch, optimizer): - """Train step. - - Args: - data_batch (dict): A batch of data. - optimizer (obj): Optimizer. - - Returns: - dict: Returned output. - """ - # data - lq = data_batch['lq'] - gt = data_batch['gt'] - - # generator - fake_g_output = self.generator(lq) - - losses = dict() - log_vars = dict() - - # no updates to discriminator parameters. - set_requires_grad(self.discriminator, False) - - if (self.step_counter % self.disc_steps == 0 - and self.step_counter >= self.disc_init_steps): - if self.pixel_loss: - losses['loss_pix'] = self.pixel_loss(fake_g_output, gt) - if self.perceptual_loss: - loss_percep, loss_style = self.perceptual_loss( - fake_g_output, gt) - if loss_percep is not None: - losses['loss_perceptual'] = loss_percep - if loss_style is not None: - losses['loss_style'] = loss_style - - # gan loss for generator - real_d_pred = self.discriminator(gt).detach() - fake_g_pred = self.discriminator(fake_g_output) - loss_gan_fake = self.gan_loss( - fake_g_pred - torch.mean(real_d_pred), - target_is_real=True, - is_disc=False) - loss_gan_real = self.gan_loss( - real_d_pred - torch.mean(fake_g_pred), - target_is_real=False, - is_disc=False) - losses['loss_gan'] = (loss_gan_fake + loss_gan_real) / 2 - - # parse loss - loss_g, log_vars_g = self.parse_losses(losses) - log_vars.update(log_vars_g) - - # optimize - optimizer['generator'].zero_grad() - loss_g.backward() - optimizer['generator'].step() - - # discriminator - set_requires_grad(self.discriminator, True) - # real - fake_d_pred = self.discriminator(fake_g_output).detach() - real_d_pred = self.discriminator(gt) - loss_d_real = self.gan_loss( - real_d_pred - torch.mean(fake_d_pred), - target_is_real=True, - is_disc=True - ) * 0.5 # 0.5 for averaging loss_d_real and loss_d_fake - loss_d, log_vars_d = self.parse_losses(dict(loss_d_real=loss_d_real)) - optimizer['discriminator'].zero_grad() - loss_d.backward() - log_vars.update(log_vars_d) - # fake - fake_d_pred = self.discriminator(fake_g_output.detach()) - loss_d_fake = self.gan_loss( - fake_d_pred - torch.mean(real_d_pred.detach()), - target_is_real=False, - is_disc=True - ) * 0.5 # 0.5 for averaging loss_d_real and loss_d_fake - loss_d, log_vars_d = self.parse_losses(dict(loss_d_fake=loss_d_fake)) - loss_d.backward() - log_vars.update(log_vars_d) - - optimizer['discriminator'].step() - - self.step_counter += 1 - - log_vars.pop('loss') # remove the unnecessary 'loss' - outputs = dict( - log_vars=log_vars, - num_samples=len(gt.data), - results=dict(lq=lq.cpu(), gt=gt.cpu(), output=fake_g_output.cpu())) - - return outputs diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/srgan.py b/cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/srgan.py deleted file mode 100755 index 6a568796f..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/models/restorers/srgan.py +++ /dev/null @@ -1,176 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import auto_fp16 - -from ..builder import build_backbone, build_component, build_loss -from ..common import set_requires_grad -from ..registry import MODELS -from .basic_restorer import BasicRestorer - - -@MODELS.register_module() -class SRGAN(BasicRestorer): - """SRGAN model for single image super-resolution. - - Ref: - Photo-Realistic Single Image Super-Resolution Using a Generative - Adversarial Network. - - Args: - generator (dict): Config for the generator. - discriminator (dict): Config for the discriminator. Default: None. - gan_loss (dict): Config for the gan loss. - Note that the loss weight in gan loss is only for the generator. - pixel_loss (dict): Config for the pixel loss. Default: None. - perceptual_loss (dict): Config for the perceptual loss. Default: None. - train_cfg (dict): Config for training. Default: None. - You may change the training of gan by setting: - `disc_steps`: how many discriminator updates after one generate - update; - `disc_init_steps`: how many discriminator updates at the start of - the training. - These two keys are useful when training with WGAN. - test_cfg (dict): Config for testing. Default: None. - pretrained (str): Path for pretrained model. Default: None. - """ - - def __init__(self, - generator, - discriminator=None, - gan_loss=None, - pixel_loss=None, - perceptual_loss=None, - train_cfg=None, - test_cfg=None, - pretrained=None): - super(BasicRestorer, self).__init__() - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - # generator - self.generator = build_backbone(generator) - # discriminator - self.discriminator = build_component( - discriminator) if discriminator else None - - # support fp16 - self.fp16_enabled = False - - # loss - self.gan_loss = build_loss(gan_loss) if gan_loss else None - self.pixel_loss = build_loss(pixel_loss) if pixel_loss else None - self.perceptual_loss = build_loss( - perceptual_loss) if perceptual_loss else None - - self.disc_steps = 1 if self.train_cfg is None else self.train_cfg.get( - 'disc_steps', 1) - self.disc_init_steps = (0 if self.train_cfg is None else - self.train_cfg.get('disc_init_steps', 0)) - self.step_counter = 0 # counting training steps - - self.init_weights(pretrained) - - def init_weights(self, pretrained=None): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - """ - self.generator.init_weights(pretrained=pretrained) - if self.discriminator: - self.discriminator.init_weights(pretrained=pretrained) - - @auto_fp16(apply_to=('lq', )) - def forward(self, lq, gt=None, test_mode=False, **kwargs): - """Forward function. - - Args: - lq (Tensor): Input lq images. - gt (Tensor): Ground-truth image. Default: None. - test_mode (bool): Whether in test mode or not. Default: False. - kwargs (dict): Other arguments. - """ - if test_mode: - return self.forward_test(lq, gt, **kwargs) - - raise ValueError( - 'SRGAN model does not support `forward_train` function.') - - def train_step(self, data_batch, optimizer): - """Train step. - - Args: - data_batch (dict): A batch of data. - optimizer (obj): Optimizer. - - Returns: - dict: Returned output. - """ - # data - lq = data_batch['lq'] - gt = data_batch['gt'] - - # generator - fake_g_output = self.generator(lq) - - losses = dict() - log_vars = dict() - - # no updates to discriminator parameters. - set_requires_grad(self.discriminator, False) - - if (self.step_counter % self.disc_steps == 0 - and self.step_counter >= self.disc_init_steps): - if self.pixel_loss: - losses['loss_pix'] = self.pixel_loss(fake_g_output, gt) - if self.perceptual_loss: - loss_percep, loss_style = self.perceptual_loss( - fake_g_output, gt) - if loss_percep is not None: - losses['loss_perceptual'] = loss_percep - if loss_style is not None: - losses['loss_style'] = loss_style - # gan loss for generator - fake_g_pred = self.discriminator(fake_g_output) - losses['loss_gan'] = self.gan_loss( - fake_g_pred, target_is_real=True, is_disc=False) - - # parse loss - loss_g, log_vars_g = self.parse_losses(losses) - log_vars.update(log_vars_g) - - # optimize - optimizer['generator'].zero_grad() - loss_g.backward() - optimizer['generator'].step() - - # discriminator - set_requires_grad(self.discriminator, True) - # real - real_d_pred = self.discriminator(gt) - loss_d_real = self.gan_loss( - real_d_pred, target_is_real=True, is_disc=True) - loss_d, log_vars_d = self.parse_losses(dict(loss_d_real=loss_d_real)) - optimizer['discriminator'].zero_grad() - loss_d.backward() - log_vars.update(log_vars_d) - # fake - fake_d_pred = self.discriminator(fake_g_output.detach()) - loss_d_fake = self.gan_loss( - fake_d_pred, target_is_real=False, is_disc=True) - loss_d, log_vars_d = self.parse_losses(dict(loss_d_fake=loss_d_fake)) - loss_d.backward() - log_vars.update(log_vars_d) - - optimizer['discriminator'].step() - - self.step_counter += 1 - - log_vars.pop('loss') # remove the unnecessary 'loss' - outputs = dict( - log_vars=log_vars, - num_samples=len(gt.data), - results=dict(lq=lq.cpu(), gt=gt.cpu(), output=fake_g_output.cpu())) - - return outputs diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/utils/__init__.py b/cv/super_resolution/esrgan/pytorch/mmedit/utils/__init__.py deleted file mode 100755 index 0521b4a1c..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/utils/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .cli import modify_args -from .logger import get_root_logger -from .setup_env import setup_multi_processes - -__all__ = ['get_root_logger', 'setup_multi_processes', 'modify_args'] diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/utils/cli.py b/cv/super_resolution/esrgan/pytorch/mmedit/utils/cli.py deleted file mode 100755 index a78b32712..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/utils/cli.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import re -import sys -import warnings - - -def modify_args(): - for i, v in enumerate(sys.argv): - if i == 0: - assert v.endswith('.py') - elif re.match(r'--\w+_.*', v): - new_arg = v.replace('_', '-') - warnings.warn( - f'command line argument {v} is deprecated, ' - f'please use {new_arg} instead.', - category=DeprecationWarning, - ) - sys.argv[i] = new_arg diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/utils/collect_env.py b/cv/super_resolution/esrgan/pytorch/mmedit/utils/collect_env.py deleted file mode 100755 index af22082c7..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/utils/collect_env.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash - -import mmedit - - -def collect_env(): - """Collect the information of the running environments.""" - env_info = collect_base_env() - env_info['MMEditing'] = f'{mmedit.__version__}+{get_git_hash()[:7]}' - - return env_info - - -if __name__ == '__main__': - for name, val in collect_env().items(): - print('{}: {}'.format(name, val)) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/utils/logger.py b/cv/super_resolution/esrgan/pytorch/mmedit/utils/logger.py deleted file mode 100755 index cdc340f18..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/utils/logger.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -from mmcv.utils import get_logger - - -def get_root_logger(log_file=None, log_level=logging.INFO): - """Get the root logger. - - The logger will be initialized if it has not been initialized. By default a - StreamHandler will be added. If `log_file` is specified, a FileHandler will - also be added. The name of the root logger is the top-level package name, - e.g., "mmedit". - - Args: - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the root logger. - log_level (int): The root logger level. Note that only the process of - rank 0 is affected, while other processes will set the level to - "Error" and be silent most of the time. - - Returns: - logging.Logger: The root logger. - """ - # root logger name: mmedit - logger = get_logger(__name__.split('.')[0], log_file, log_level) - return logger diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/utils/setup_env.py b/cv/super_resolution/esrgan/pytorch/mmedit/utils/setup_env.py deleted file mode 100755 index 21def2f08..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/utils/setup_env.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import platform -import warnings - -import cv2 -import torch.multiprocessing as mp - - -def setup_multi_processes(cfg): - """Setup multi-processing environment variables.""" - # set multi-process start method as `fork` to speed up the training - if platform.system() != 'Windows': - mp_start_method = cfg.get('mp_start_method', 'fork') - current_method = mp.get_start_method(allow_none=True) - if current_method is not None and current_method != mp_start_method: - warnings.warn( - f'Multi-processing start method `{mp_start_method}` is ' - f'different from the previous setting `{current_method}`.' - f'It will be force set to `{mp_start_method}`. You can change ' - f'this behavior by changing `mp_start_method` in your config.') - mp.set_start_method(mp_start_method, force=True) - - # disable opencv multithreading to avoid system being overloaded - opencv_num_threads = cfg.get('opencv_num_threads', 0) - cv2.setNumThreads(opencv_num_threads) - - # setup OMP threads - # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa - if 'OMP_NUM_THREADS' not in os.environ and cfg.data.workers_per_gpu > 1: - omp_num_threads = 1 - warnings.warn( - f'Setting OMP_NUM_THREADS environment variable for each process ' - f'to be {omp_num_threads} in default, to avoid your system being ' - f'overloaded, please further tune the variable for optimal ' - f'performance in your application as needed.') - os.environ['OMP_NUM_THREADS'] = str(omp_num_threads) - - # setup MKL threads - if 'MKL_NUM_THREADS' not in os.environ and cfg.data.workers_per_gpu > 1: - mkl_num_threads = 1 - warnings.warn( - f'Setting MKL_NUM_THREADS environment variable for each process ' - f'to be {mkl_num_threads} in default, to avoid your system being ' - f'overloaded, please further tune the variable for optimal ' - f'performance in your application as needed.') - os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads) diff --git a/cv/super_resolution/esrgan/pytorch/mmedit/version.py b/cv/super_resolution/esrgan/pytorch/mmedit/version.py deleted file mode 100755 index ffec9df73..000000000 --- a/cv/super_resolution/esrgan/pytorch/mmedit/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) Open-MMLab. All rights reserved. - -__version__ = '0.15.0' - - -def parse_version_info(version_str): - ver_info = [] - for x in version_str.split('.'): - if x.isdigit(): - ver_info.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - ver_info.append(int(patch_version[0])) - ver_info.append(f'rc{patch_version[1]}') - return tuple(ver_info) - - -version_info = parse_version_info(__version__) diff --git a/cv/super_resolution/esrgan/pytorch/requirements.txt b/cv/super_resolution/esrgan/pytorch/requirements.txt deleted file mode 100755 index 62ceaf90e..000000000 --- a/cv/super_resolution/esrgan/pytorch/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -opencv-python!=4.5.5.62,!=4.5.5.64 -# MMCV depends opencv-python instead of headless, thus we install opencv-python -# Due to a bug from upstream, we skip this two version -# https://github.com/opencv/opencv-python/issues/602 -# https://github.com/opencv/opencv/issues/21366 -# It seems to be fixed in https://github.com/opencv/opencv/pull/21382 -Pillow -addict -yapf -packaging -pyyaml -scipy -tensorboard diff --git a/cv/super_resolution/esrgan/pytorch/train.py b/cv/super_resolution/esrgan/pytorch/train.py deleted file mode 100755 index e04d89bc4..000000000 --- a/cv/super_resolution/esrgan/pytorch/train.py +++ /dev/null @@ -1,172 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import argparse -import copy -import os -import os.path as osp -import time - -import mmcv -import torch -import torch.distributed as dist -from mmcv import Config, DictAction -from mmcv.runner import init_dist - -from mmedit import __version__ -from mmedit.apis import init_random_seed, set_random_seed, train_model -from mmedit.datasets import build_dataset -from mmedit.models import build_model -from mmedit.utils import collect_env, get_root_logger, setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train an editor') - parser.add_argument('config', help='train config file path') - parser.add_argument('--work-dir', help='the dir to save logs and models') - parser.add_argument( - '--resume-from', help='the checkpoint file to resume from') - parser.add_argument( - '--no-validate', - action='store_true', - help='whether not to evaluate the checkpoint during training') - parser.add_argument( - '--gpus', - type=int, - default=1, - help='number of gpus to use ' - '(only applicable to non-distributed training)') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--diff_seed', - action='store_true', - help='Whether or not set different seeds for different ranks') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - parser.add_argument( - '--autoscale-lr', - action='store_true', - help='automatically scale lr with the number of gpus') - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - # update configs according to CLI args - if args.work_dir is not None: - cfg.work_dir = args.work_dir - if args.resume_from is not None: - cfg.resume_from = args.resume_from - cfg.gpus = args.gpus - - if args.autoscale_lr: - # apply the linear scaling rule (https://arxiv.org/abs/1706.02677) - cfg.optimizer['lr'] = cfg.optimizer['lr'] * cfg.gpus / 8 - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - - # create work_dir - mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) - # init the logger before other steps - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - log_file = osp.join(cfg.work_dir, f'{timestamp}.log') - logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) - - # log env info - env_info_dict = collect_env.collect_env() - env_info = '\n'.join([f'{k}: {v}' for k, v in env_info_dict.items()]) - dash_line = '-' * 60 + '\n' - logger.info('Environment info:\n' + dash_line + env_info + '\n' + - dash_line) - - # log some basic info - logger.info('Distributed training: {}'.format(distributed)) - logger.info('mmedit Version: {}'.format(__version__)) - logger.info('Config:\n{}'.format(cfg.text)) - - # set random seeds - seed = init_random_seed(args.seed) - seed = seed + dist.get_rank() if args.diff_seed else seed - logger.info('Set random seed to {}, deterministic: {}'.format( - seed, args.deterministic)) - set_random_seed(seed, deterministic=args.deterministic) - cfg.seed = seed - - model = build_model( - cfg.model, train_cfg=cfg.train_cfg, test_cfg=cfg.test_cfg) - - datasets = [build_dataset(cfg.data.train)] - if len(cfg.workflow) == 2: - val_dataset = copy.deepcopy(cfg.data.val) - val_dataset.pipeline = cfg.data.train.pipeline - datasets.append(build_dataset(val_dataset)) - if cfg.checkpoint_config is not None: - # save version, config file content and class names in - # checkpoints as meta data - cfg.checkpoint_config.meta = dict( - mmedit_version=__version__, - config=cfg.text, - ) - - # meta information - meta = dict() - if cfg.get('exp_name', None) is None: - cfg['exp_name'] = osp.splitext(osp.basename(cfg.work_dir))[0] - meta['exp_name'] = cfg.exp_name - meta['mmedit Version'] = __version__ - meta['seed'] = seed - meta['env_info'] = env_info - - # add an attribute for visualization convenience - train_model( - model, - datasets, - cfg, - distributed=distributed, - validate=(not args.no_validate), - timestamp=timestamp, - meta=meta) - - -if __name__ == '__main__': - main() -- Gitee From 3730cafedf65ca520fab418941b320aa71480d33 Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Thu, 6 Mar 2025 10:33:09 +0800 Subject: [PATCH 11/15] update autoassign --- cv/detection/atss_mmdet/pytorch/README.md | 2 +- cv/detection/autoassign/pytorch/.gitignore | 126 - cv/detection/autoassign/pytorch/LICENSE | 203 -- cv/detection/autoassign/pytorch/README.md | 29 +- .../_base_/datasets/cityscapes_detection.py | 56 - .../_base_/datasets/cityscapes_instance.py | 56 - .../configs/_base_/datasets/coco_detection.py | 49 - .../configs/_base_/datasets/coco_instance.py | 49 - .../_base_/datasets/coco_instance_semantic.py | 54 - .../configs/_base_/datasets/coco_panoptic.py | 59 - .../configs/_base_/datasets/deepfashion.py | 53 - .../_base_/datasets/lvis_v0.5_instance.py | 24 - .../_base_/datasets/lvis_v1_instance.py | 24 - .../_base_/datasets/openimages_detection.py | 65 - .../configs/_base_/datasets/voc0712.py | 55 - .../configs/_base_/datasets/wider_face.py | 63 - .../pytorch/configs/_base_/default_runtime.py | 27 - .../models/cascade_mask_rcnn_r50_fpn.py | 196 -- .../_base_/models/cascade_rcnn_r50_fpn.py | 179 - .../_base_/models/fast_rcnn_r50_fpn.py | 62 - .../_base_/models/faster_rcnn_r50_caffe_c4.py | 117 - .../models/faster_rcnn_r50_caffe_dc5.py | 105 - .../_base_/models/faster_rcnn_r50_fpn.py | 108 - .../_base_/models/mask_rcnn_r50_caffe_c4.py | 125 - .../_base_/models/mask_rcnn_r50_fpn.py | 120 - .../_base_/models/retinanet_r50_fpn.py | 60 - .../configs/_base_/models/rpn_r50_caffe_c4.py | 58 - .../configs/_base_/models/rpn_r50_fpn.py | 58 - .../pytorch/configs/_base_/models/ssd300.py | 56 - .../configs/_base_/schedules/schedule_1x.py | 11 - .../configs/_base_/schedules/schedule_20e.py | 11 - .../configs/_base_/schedules/schedule_2x.py | 11 - .../pytorch/configs/autoassign/README.md | 35 - .../autoassign_r50_fpn_8x2_1x_coco.py | 86 - .../pytorch/configs/autoassign/metafile.yml | 33 - cv/detection/autoassign/pytorch/dist_train.sh | 33 - .../autoassign/pytorch/docker/Dockerfile | 25 - .../pytorch/docker/serve/Dockerfile | 49 - .../pytorch/docker/serve/config.properties | 5 - .../pytorch/docker/serve/entrypoint.sh | 12 - .../autoassign/pytorch/mmcv/__init__.py | 11 - .../autoassign/pytorch/mmcv/cnn/__init__.py | 41 - .../autoassign/pytorch/mmcv/cnn/alexnet.py | 61 - .../pytorch/mmcv/cnn/bricks/__init__.py | 35 - .../pytorch/mmcv/cnn/bricks/activation.py | 92 - .../pytorch/mmcv/cnn/bricks/context_block.py | 125 - .../pytorch/mmcv/cnn/bricks/conv.py | 44 - .../cnn/bricks/conv2d_adaptive_padding.py | 62 - .../pytorch/mmcv/cnn/bricks/conv_module.py | 206 -- .../pytorch/mmcv/cnn/bricks/conv_ws.py | 148 - .../bricks/depthwise_separable_conv_module.py | 96 - .../pytorch/mmcv/cnn/bricks/drop.py | 65 - .../mmcv/cnn/bricks/generalized_attention.py | 412 --- .../pytorch/mmcv/cnn/bricks/hsigmoid.py | 34 - .../pytorch/mmcv/cnn/bricks/hswish.py | 29 - .../pytorch/mmcv/cnn/bricks/non_local.py | 306 -- .../pytorch/mmcv/cnn/bricks/norm.py | 144 - .../pytorch/mmcv/cnn/bricks/padding.py | 36 - .../pytorch/mmcv/cnn/bricks/plugin.py | 88 - .../pytorch/mmcv/cnn/bricks/registry.py | 16 - .../pytorch/mmcv/cnn/bricks/scale.py | 21 - .../pytorch/mmcv/cnn/bricks/swish.py | 25 - .../pytorch/mmcv/cnn/bricks/transformer.py | 595 ---- .../pytorch/mmcv/cnn/bricks/upsample.py | 84 - .../pytorch/mmcv/cnn/bricks/wrappers.py | 180 - .../autoassign/pytorch/mmcv/cnn/builder.py | 30 - .../autoassign/pytorch/mmcv/cnn/resnet.py | 316 -- .../pytorch/mmcv/cnn/utils/__init__.py | 19 - .../pytorch/mmcv/cnn/utils/flops_counter.py | 599 ---- .../pytorch/mmcv/cnn/utils/fuse_conv_bn.py | 59 - .../pytorch/mmcv/cnn/utils/sync_bn.py | 59 - .../pytorch/mmcv/cnn/utils/weight_init.py | 684 ---- .../autoassign/pytorch/mmcv/cnn/vgg.py | 175 - .../pytorch/mmcv/engine/__init__.py | 8 - .../autoassign/pytorch/mmcv/engine/test.py | 202 -- .../pytorch/mmcv/fileio/__init__.py | 11 - .../pytorch/mmcv/fileio/file_client.py | 1148 ------- .../pytorch/mmcv/fileio/handlers/__init__.py | 7 - .../pytorch/mmcv/fileio/handlers/base.py | 30 - .../mmcv/fileio/handlers/json_handler.py | 36 - .../mmcv/fileio/handlers/pickle_handler.py | 28 - .../mmcv/fileio/handlers/yaml_handler.py | 24 - .../autoassign/pytorch/mmcv/fileio/io.py | 151 - .../autoassign/pytorch/mmcv/fileio/parse.py | 97 - .../autoassign/pytorch/mmcv/image/__init__.py | 28 - .../pytorch/mmcv/image/colorspace.py | 306 -- .../pytorch/mmcv/image/geometric.py | 728 ---- .../autoassign/pytorch/mmcv/image/io.py | 258 -- .../autoassign/pytorch/mmcv/image/misc.py | 44 - .../pytorch/mmcv/image/photometric.py | 428 --- .../pytorch/mmcv/model_zoo/deprecated.json | 6 - .../pytorch/mmcv/model_zoo/mmcls.json | 31 - .../pytorch/mmcv/model_zoo/open_mmlab.json | 50 - .../autoassign/pytorch/mmcv/ops/__init__.py | 13 - .../pytorch/mmcv/ops/csrc/README.md | 169 - .../csrc/common/cuda/common_cuda_helper.hpp | 112 - .../ops/csrc/common/cuda/nms_cuda_kernel.cuh | 74 - .../ops/csrc/common/cuda/nms_rotated_cuda.cuh | 135 - .../common/cuda/roi_align_cuda_kernel.cuh | 212 -- .../csrc/common/cuda/roi_pool_cuda_kernel.cuh | 93 - .../cuda/sigmoid_focal_loss_cuda_kernel.cuh | 71 - .../cuda/softmax_focal_loss_cuda_kernel.cuh | 72 - .../csrc/common/cuda/sync_bn_cuda_kernel.cuh | 331 -- .../ops/csrc/common/pytorch_cpp_helper.hpp | 24 - .../ops/csrc/common/pytorch_cuda_helper.hpp | 19 - .../ops/csrc/pytorch/cuda/focal_loss_cuda.cu | 111 - .../mmcv/ops/csrc/pytorch/cuda/nms_cuda.cu | 53 - .../ops/csrc/pytorch/cuda/roi_align_cuda.cu | 58 - .../ops/csrc/pytorch/cuda/roi_pool_cuda.cu | 50 - .../ops/csrc/pytorch/cuda/sync_bn_cuda.cu | 110 - .../mmcv/ops/csrc/pytorch/focal_loss.cpp | 131 - .../pytorch/mmcv/ops/csrc/pytorch/info.cpp | 56 - .../pytorch/mmcv/ops/csrc/pytorch/nms.cpp | 261 -- .../pytorch/mmcv/ops/csrc/pytorch/pybind.cpp | 131 - .../mmcv/ops/csrc/pytorch/roi_align.cpp | 130 - .../mmcv/ops/csrc/pytorch/roi_align_cpu.cpp | 431 --- .../mmcv/ops/csrc/pytorch/roi_pool.cpp | 67 - .../pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp | 159 - .../pytorch/mmcv/ops/deprecated_wrappers.py | 43 - .../autoassign/pytorch/mmcv/ops/focal_loss.py | 212 -- .../autoassign/pytorch/mmcv/ops/info.py | 36 - .../autoassign/pytorch/mmcv/ops/nms.py | 417 --- .../pytorch/mmcv/ops/point_sample.py | 336 -- .../autoassign/pytorch/mmcv/ops/roi_align.py | 223 -- .../autoassign/pytorch/mmcv/ops/roi_pool.py | 86 - .../autoassign/pytorch/mmcv/ops/sync_bn.py | 279 -- .../pytorch/mmcv/parallel/__init__.py | 13 - .../pytorch/mmcv/parallel/_functions.py | 79 - .../pytorch/mmcv/parallel/collate.py | 84 - .../pytorch/mmcv/parallel/data_container.py | 89 - .../pytorch/mmcv/parallel/data_parallel.py | 89 - .../pytorch/mmcv/parallel/distributed.py | 112 - .../mmcv/parallel/distributed_deprecated.py | 70 - .../pytorch/mmcv/parallel/registry.py | 8 - .../pytorch/mmcv/parallel/scatter_gather.py | 59 - .../autoassign/pytorch/mmcv/parallel/utils.py | 20 - .../pytorch/mmcv/runner/__init__.py | 47 - .../pytorch/mmcv/runner/base_module.py | 195 -- .../pytorch/mmcv/runner/base_runner.py | 542 --- .../autoassign/pytorch/mmcv/runner/builder.py | 24 - .../pytorch/mmcv/runner/checkpoint.py | 707 ---- .../mmcv/runner/default_constructor.py | 44 - .../pytorch/mmcv/runner/dist_utils.py | 164 - .../pytorch/mmcv/runner/epoch_based_runner.py | 187 -- .../pytorch/mmcv/runner/fp16_utils.py | 410 --- .../pytorch/mmcv/runner/hooks/__init__.py | 29 - .../pytorch/mmcv/runner/hooks/checkpoint.py | 167 - .../pytorch/mmcv/runner/hooks/closure.py | 11 - .../pytorch/mmcv/runner/hooks/ema.py | 89 - .../pytorch/mmcv/runner/hooks/evaluation.py | 509 --- .../pytorch/mmcv/runner/hooks/hook.py | 92 - .../pytorch/mmcv/runner/hooks/iter_timer.py | 18 - .../mmcv/runner/hooks/logger/__init__.py | 15 - .../pytorch/mmcv/runner/hooks/logger/base.py | 166 - .../mmcv/runner/hooks/logger/dvclive.py | 58 - .../mmcv/runner/hooks/logger/mlflow.py | 78 - .../mmcv/runner/hooks/logger/neptune.py | 82 - .../pytorch/mmcv/runner/hooks/logger/pavi.py | 117 - .../mmcv/runner/hooks/logger/tensorboard.py | 57 - .../pytorch/mmcv/runner/hooks/logger/text.py | 256 -- .../pytorch/mmcv/runner/hooks/logger/wandb.py | 56 - .../pytorch/mmcv/runner/hooks/lr_updater.py | 670 ---- .../pytorch/mmcv/runner/hooks/memory.py | 25 - .../mmcv/runner/hooks/momentum_updater.py | 493 --- .../pytorch/mmcv/runner/hooks/optimizer.py | 508 --- .../pytorch/mmcv/runner/hooks/profiler.py | 180 - .../pytorch/mmcv/runner/hooks/sampler_seed.py | 20 - .../pytorch/mmcv/runner/hooks/sync_buffer.py | 22 - .../pytorch/mmcv/runner/iter_based_runner.py | 273 -- .../pytorch/mmcv/runner/log_buffer.py | 41 - .../pytorch/mmcv/runner/optimizer/__init__.py | 9 - .../pytorch/mmcv/runner/optimizer/builder.py | 44 - .../runner/optimizer/default_constructor.py | 247 -- .../pytorch/mmcv/runner/priority.py | 60 - .../autoassign/pytorch/mmcv/runner/utils.py | 93 - .../autoassign/pytorch/mmcv/utils/__init__.py | 69 - .../autoassign/pytorch/mmcv/utils/config.py | 688 ---- .../autoassign/pytorch/mmcv/utils/env.py | 95 - .../pytorch/mmcv/utils/ext_loader.py | 71 - .../autoassign/pytorch/mmcv/utils/logging.py | 110 - .../autoassign/pytorch/mmcv/utils/misc.py | 377 --- .../pytorch/mmcv/utils/parrots_jit.py | 41 - .../pytorch/mmcv/utils/parrots_wrapper.py | 107 - .../autoassign/pytorch/mmcv/utils/path.py | 101 - .../pytorch/mmcv/utils/progressbar.py | 208 -- .../autoassign/pytorch/mmcv/utils/registry.py | 315 -- .../autoassign/pytorch/mmcv/utils/testing.py | 140 - .../autoassign/pytorch/mmcv/utils/timer.py | 118 - .../autoassign/pytorch/mmcv/utils/trace.py | 23 - .../pytorch/mmcv/utils/version_utils.py | 90 - .../autoassign/pytorch/mmcv/version.py | 35 - .../autoassign/pytorch/mmdet/__init__.py | 29 - .../autoassign/pytorch/mmdet/apis/__init__.py | 12 - .../pytorch/mmdet/apis/inference.py | 251 -- .../autoassign/pytorch/mmdet/apis/test.py | 209 -- .../autoassign/pytorch/mmdet/apis/train.py | 244 -- .../autoassign/pytorch/mmdet/core/__init__.py | 10 - .../pytorch/mmdet/core/anchor/__init__.py | 14 - .../mmdet/core/anchor/anchor_generator.py | 866 ----- .../pytorch/mmdet/core/anchor/builder.py | 19 - .../mmdet/core/anchor/point_generator.py | 263 -- .../pytorch/mmdet/core/anchor/utils.py | 72 - .../pytorch/mmdet/core/bbox/__init__.py | 28 - .../mmdet/core/bbox/assigners/__init__.py | 22 - .../bbox/assigners/approx_max_iou_assigner.py | 146 - .../core/bbox/assigners/assign_result.py | 206 -- .../core/bbox/assigners/atss_assigner.py | 234 -- .../core/bbox/assigners/base_assigner.py | 10 - .../bbox/assigners/center_region_assigner.py | 336 -- .../core/bbox/assigners/grid_assigner.py | 156 - .../core/bbox/assigners/hungarian_assigner.py | 146 - .../bbox/assigners/mask_hungarian_assigner.py | 132 - .../core/bbox/assigners/max_iou_assigner.py | 218 -- .../core/bbox/assigners/point_assigner.py | 134 - .../core/bbox/assigners/region_assigner.py | 222 -- .../core/bbox/assigners/sim_ota_assigner.py | 257 -- .../bbox/assigners/task_aligned_assigner.py | 151 - .../core/bbox/assigners/uniform_assigner.py | 135 - .../pytorch/mmdet/core/bbox/builder.py | 21 - .../pytorch/mmdet/core/bbox/coder/__init__.py | 15 - .../mmdet/core/bbox/coder/base_bbox_coder.py | 18 - .../core/bbox/coder/bucketing_bbox_coder.py | 351 -- .../core/bbox/coder/delta_xywh_bbox_coder.py | 392 --- .../bbox/coder/distance_point_bbox_coder.py | 63 - .../coder/legacy_delta_xywh_bbox_coder.py | 216 -- .../core/bbox/coder/pseudo_bbox_coder.py | 19 - .../mmdet/core/bbox/coder/tblr_bbox_coder.py | 206 -- .../mmdet/core/bbox/coder/yolo_bbox_coder.py | 83 - .../pytorch/mmdet/core/bbox/demodata.py | 42 - .../core/bbox/iou_calculators/__init__.py | 5 - .../core/bbox/iou_calculators/builder.py | 9 - .../bbox/iou_calculators/iou2d_calculator.py | 261 -- .../mmdet/core/bbox/match_costs/__init__.py | 9 - .../mmdet/core/bbox/match_costs/builder.py | 9 - .../mmdet/core/bbox/match_costs/match_cost.py | 359 -- .../mmdet/core/bbox/samplers/__init__.py | 19 - .../mmdet/core/bbox/samplers/base_sampler.py | 102 - .../core/bbox/samplers/combined_sampler.py | 21 - .../samplers/instance_balanced_pos_sampler.py | 56 - .../bbox/samplers/iou_balanced_neg_sampler.py | 158 - .../core/bbox/samplers/mask_pseudo_sampler.py | 44 - .../bbox/samplers/mask_sampling_result.py | 60 - .../mmdet/core/bbox/samplers/ohem_sampler.py | 111 - .../core/bbox/samplers/pseudo_sampler.py | 42 - .../core/bbox/samplers/random_sampler.py | 82 - .../core/bbox/samplers/sampling_result.py | 153 - .../core/bbox/samplers/score_hlr_sampler.py | 265 -- .../pytorch/mmdet/core/bbox/transforms.py | 270 -- .../mmdet/core/data_structures/__init__.py | 5 - .../core/data_structures/general_data.py | 326 -- .../core/data_structures/instance_data.py | 188 -- .../pytorch/mmdet/core/evaluation/__init__.py | 19 - .../mmdet/core/evaluation/bbox_overlaps.py | 65 - .../mmdet/core/evaluation/class_names.py | 332 -- .../mmdet/core/evaluation/eval_hooks.py | 140 - .../pytorch/mmdet/core/evaluation/mean_ap.py | 782 ----- .../mmdet/core/evaluation/panoptic_utils.py | 6 - .../pytorch/mmdet/core/evaluation/recall.py | 197 -- .../pytorch/mmdet/core/export/__init__.py | 12 - .../mmdet/core/export/model_wrappers.py | 183 -- .../pytorch/mmdet/core/export/onnx_helper.py | 223 -- .../pytorch/mmdet/core/export/pytorch2onnx.py | 159 - .../pytorch/mmdet/core/hook/__init__.py | 17 - .../pytorch/mmdet/core/hook/checkloss_hook.py | 24 - .../autoassign/pytorch/mmdet/core/hook/ema.py | 130 - .../mmdet/core/hook/memory_profiler_hook.py | 55 - .../mmdet/core/hook/set_epoch_info_hook.py | 15 - .../pytorch/mmdet/core/hook/sync_norm_hook.py | 52 - .../mmdet/core/hook/sync_random_size_hook.py | 72 - .../mmdet/core/hook/wandblogger_hook.py | 587 ---- .../mmdet/core/hook/yolox_lrupdater_hook.py | 67 - .../mmdet/core/hook/yolox_mode_switch_hook.py | 52 - .../pytorch/mmdet/core/mask/__init__.py | 9 - .../pytorch/mmdet/core/mask/mask_target.py | 127 - .../pytorch/mmdet/core/mask/structures.py | 1102 ------- .../pytorch/mmdet/core/mask/utils.py | 89 - .../pytorch/mmdet/core/optimizers/__init__.py | 9 - .../pytorch/mmdet/core/optimizers/builder.py | 33 - .../layer_decay_optimizer_constructor.py | 154 - .../mmdet/core/post_processing/__init__.py | 10 - .../mmdet/core/post_processing/bbox_nms.py | 171 - .../mmdet/core/post_processing/matrix_nms.py | 121 - .../mmdet/core/post_processing/merge_augs.py | 154 - .../pytorch/mmdet/core/utils/__init__.py | 13 - .../pytorch/mmdet/core/utils/dist_utils.py | 193 -- .../pytorch/mmdet/core/utils/misc.py | 208 -- .../mmdet/core/visualization/__init__.py | 9 - .../pytorch/mmdet/core/visualization/image.py | 559 ---- .../mmdet/core/visualization/palette.py | 63 - .../pytorch/mmdet/datasets/__init__.py | 12 - .../mmdet/datasets/api_wrappers/__init__.py | 7 - .../mmdet/datasets/api_wrappers/coco_api.py | 47 - .../api_wrappers/panoptic_evaluation.py | 228 -- .../pytorch/mmdet/datasets/builder.py | 215 -- .../autoassign/pytorch/mmdet/datasets/coco.py | 649 ---- .../pytorch/mmdet/datasets/custom.py | 410 --- .../mmdet/datasets/dataset_wrappers.py | 456 --- .../mmdet/datasets/pipelines/__init__.py | 17 - .../mmdet/datasets/pipelines/compose.py | 55 - .../mmdet/datasets/pipelines/formating.py | 9 - .../mmdet/datasets/pipelines/formatting.py | 392 --- .../mmdet/datasets/pipelines/loading.py | 643 ---- .../mmdet/datasets/pipelines/test_time_aug.py | 121 - .../mmdet/datasets/pipelines/transforms.py | 2919 ----------------- .../mmdet/datasets/samplers/__init__.py | 10 - .../datasets/samplers/class_aware_sampler.py | 176 - .../datasets/samplers/distributed_sampler.py | 54 - .../mmdet/datasets/samplers/group_sampler.py | 148 - .../datasets/samplers/infinite_sampler.py | 186 -- .../pytorch/mmdet/datasets/utils.py | 164 - .../pytorch/mmdet/models/__init__.py | 10 - .../mmdet/models/backbones/__init__.py | 5 - .../pytorch/mmdet/models/backbones/resnet.py | 672 ---- .../pytorch/mmdet/models/builder.py | 59 - .../mmdet/models/dense_heads/__init__.py | 9 - .../models/dense_heads/anchor_free_head.py | 350 -- .../mmdet/models/dense_heads/anchor_head.py | 542 --- .../mmdet/models/dense_heads/atss_head.py | 501 --- .../models/dense_heads/autoassign_head.py | 527 --- .../models/dense_heads/base_dense_head.py | 526 --- .../models/dense_heads/dense_test_mixins.py | 206 -- .../mmdet/models/dense_heads/fcos_head.py | 455 --- .../mmdet/models/dense_heads/paa_head.py | 756 ----- .../mmdet/models/detectors/__init__.py | 5 - .../mmdet/models/detectors/autoassign.py | 19 - .../pytorch/mmdet/models/detectors/base.py | 360 -- .../mmdet/models/detectors/single_stage.py | 171 - .../pytorch/mmdet/models/losses/__init__.py | 10 - .../pytorch/mmdet/models/losses/accuracy.py | 79 - .../mmdet/models/losses/cross_entropy_loss.py | 301 -- .../pytorch/mmdet/models/losses/focal_loss.py | 244 -- .../pytorch/mmdet/models/losses/iou_loss.py | 474 --- .../pytorch/mmdet/models/losses/utils.py | 105 - .../pytorch/mmdet/models/necks/__init__.py | 5 - .../pytorch/mmdet/models/necks/fpn.py | 204 -- .../pytorch/mmdet/models/utils/__init__.py | 18 - .../mmdet/models/utils/brick_wrappers.py | 51 - .../pytorch/mmdet/models/utils/builder.py | 47 - .../mmdet/models/utils/ckpt_convert.py | 137 - .../mmdet/models/utils/conv_upsample.py | 67 - .../pytorch/mmdet/models/utils/csp_layer.py | 150 - .../mmdet/models/utils/gaussian_target.py | 268 -- .../mmdet/models/utils/inverted_residual.py | 130 - .../mmdet/models/utils/make_divisible.py | 28 - .../pytorch/mmdet/models/utils/misc.py | 72 - .../mmdet/models/utils/normed_predictor.py | 88 - .../models/utils/panoptic_gt_processing.py | 68 - .../mmdet/models/utils/point_sample.py | 87 - .../mmdet/models/utils/positional_encoding.py | 163 - .../pytorch/mmdet/models/utils/res_layer.py | 190 -- .../pytorch/mmdet/models/utils/se_layer.py | 127 - .../pytorch/mmdet/utils/__init__.py | 17 - .../pytorch/mmdet/utils/collect_env.py | 17 - .../pytorch/mmdet/utils/compat_config.py | 139 - .../pytorch/mmdet/utils/contextmanagers.py | 122 - .../autoassign/pytorch/mmdet/utils/logger.py | 65 - .../autoassign/pytorch/mmdet/utils/memory.py | 213 -- .../autoassign/pytorch/mmdet/utils/misc.py | 76 - .../pytorch/mmdet/utils/profiling.py | 40 - .../pytorch/mmdet/utils/replace_cfg_vals.py | 70 - .../pytorch/mmdet/utils/setup_env.py | 53 - .../pytorch/mmdet/utils/split_batch.py | 45 - .../pytorch/mmdet/utils/util_distribution.py | 74 - .../pytorch/mmdet/utils/util_mixins.py | 105 - .../pytorch/mmdet/utils/util_random.py | 34 - .../autoassign/pytorch/mmdet/version.py | 19 - cv/detection/autoassign/pytorch/pytest.ini | 7 - .../autoassign/pytorch/requirements.txt | 7 - cv/detection/autoassign/pytorch/setup.py | 354 -- cv/detection/autoassign/pytorch/train.py | 243 -- 370 files changed, 21 insertions(+), 57386 deletions(-) delete mode 100755 cv/detection/autoassign/pytorch/.gitignore delete mode 100755 cv/detection/autoassign/pytorch/LICENSE delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/datasets/cityscapes_detection.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/datasets/cityscapes_instance.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_detection.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_instance.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_instance_semantic.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_panoptic.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/datasets/deepfashion.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/datasets/lvis_v0.5_instance.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/datasets/lvis_v1_instance.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/datasets/openimages_detection.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/datasets/voc0712.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/datasets/wider_face.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/default_runtime.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/models/cascade_mask_rcnn_r50_fpn.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/models/cascade_rcnn_r50_fpn.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/models/fast_rcnn_r50_fpn.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/models/faster_rcnn_r50_caffe_c4.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/models/faster_rcnn_r50_caffe_dc5.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/models/faster_rcnn_r50_fpn.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/models/mask_rcnn_r50_caffe_c4.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/models/mask_rcnn_r50_fpn.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/models/retinanet_r50_fpn.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/models/rpn_r50_caffe_c4.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/models/rpn_r50_fpn.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/models/ssd300.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/schedules/schedule_1x.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/schedules/schedule_20e.py delete mode 100755 cv/detection/autoassign/pytorch/configs/_base_/schedules/schedule_2x.py delete mode 100755 cv/detection/autoassign/pytorch/configs/autoassign/README.md delete mode 100755 cv/detection/autoassign/pytorch/configs/autoassign/autoassign_r50_fpn_8x2_1x_coco.py delete mode 100755 cv/detection/autoassign/pytorch/configs/autoassign/metafile.yml delete mode 100755 cv/detection/autoassign/pytorch/dist_train.sh delete mode 100755 cv/detection/autoassign/pytorch/docker/Dockerfile delete mode 100755 cv/detection/autoassign/pytorch/docker/serve/Dockerfile delete mode 100755 cv/detection/autoassign/pytorch/docker/serve/config.properties delete mode 100755 cv/detection/autoassign/pytorch/docker/serve/entrypoint.sh delete mode 100755 cv/detection/autoassign/pytorch/mmcv/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/alexnet.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/activation.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/context_block.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv_module.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv_ws.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/drop.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/generalized_attention.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/hsigmoid.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/hswish.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/non_local.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/norm.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/padding.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/plugin.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/registry.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/scale.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/swish.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/transformer.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/upsample.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/bricks/wrappers.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/builder.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/resnet.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/utils/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/utils/flops_counter.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/utils/fuse_conv_bn.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/utils/sync_bn.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/utils/weight_init.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/cnn/vgg.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/engine/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/engine/test.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/fileio/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/fileio/file_client.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/fileio/handlers/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/fileio/handlers/base.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/fileio/handlers/json_handler.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/fileio/handlers/pickle_handler.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/fileio/handlers/yaml_handler.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/fileio/io.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/fileio/parse.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/image/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/image/colorspace.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/image/geometric.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/image/io.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/image/misc.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/image/photometric.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/model_zoo/deprecated.json delete mode 100755 cv/detection/autoassign/pytorch/mmcv/model_zoo/mmcls.json delete mode 100755 cv/detection/autoassign/pytorch/mmcv/model_zoo/open_mmlab.json delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/README.md delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/nms_cuda_kernel.cuh delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/nms_rotated_cuda.cuh delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/roi_align_cuda_kernel.cuh delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/roi_pool_cuda_kernel.cuh delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/nms_cuda.cu delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_align_cuda.cu delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_pool_cuda.cu delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/info.cpp delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/nms.cpp delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/roi_align.cpp delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/roi_align_cpu.cpp delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/roi_pool.cpp delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/deprecated_wrappers.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/focal_loss.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/info.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/nms.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/point_sample.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/roi_align.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/roi_pool.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/ops/sync_bn.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/parallel/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/parallel/_functions.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/parallel/collate.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/parallel/data_container.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/parallel/data_parallel.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/parallel/distributed.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/parallel/distributed_deprecated.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/parallel/registry.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/parallel/scatter_gather.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/parallel/utils.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/base_module.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/base_runner.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/builder.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/checkpoint.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/default_constructor.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/dist_utils.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/epoch_based_runner.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/fp16_utils.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/checkpoint.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/closure.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/ema.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/evaluation.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/hook.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/iter_timer.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/base.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/dvclive.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/mlflow.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/neptune.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/pavi.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/tensorboard.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/text.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/wandb.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/lr_updater.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/memory.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/momentum_updater.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/optimizer.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/profiler.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/sampler_seed.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/hooks/sync_buffer.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/iter_based_runner.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/log_buffer.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/optimizer/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/optimizer/builder.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/optimizer/default_constructor.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/priority.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/runner/utils.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/config.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/env.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/ext_loader.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/logging.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/misc.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/parrots_jit.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/parrots_wrapper.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/path.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/progressbar.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/registry.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/testing.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/timer.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/trace.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/utils/version_utils.py delete mode 100755 cv/detection/autoassign/pytorch/mmcv/version.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/apis/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/apis/inference.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/apis/test.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/apis/train.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/anchor/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/anchor/anchor_generator.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/anchor/builder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/anchor/point_generator.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/anchor/utils.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/approx_max_iou_assigner.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/assign_result.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/atss_assigner.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/base_assigner.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/center_region_assigner.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/grid_assigner.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/hungarian_assigner.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/mask_hungarian_assigner.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/max_iou_assigner.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/point_assigner.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/region_assigner.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/sim_ota_assigner.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/task_aligned_assigner.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/uniform_assigner.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/builder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/base_bbox_coder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/bucketing_bbox_coder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/delta_xywh_bbox_coder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/distance_point_bbox_coder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/legacy_delta_xywh_bbox_coder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/pseudo_bbox_coder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/tblr_bbox_coder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/yolo_bbox_coder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/demodata.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/iou_calculators/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/iou_calculators/builder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/iou_calculators/iou2d_calculator.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/match_costs/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/match_costs/builder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/match_costs/match_cost.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/base_sampler.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/combined_sampler.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/instance_balanced_pos_sampler.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/iou_balanced_neg_sampler.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/mask_pseudo_sampler.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/mask_sampling_result.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/ohem_sampler.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/pseudo_sampler.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/random_sampler.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/sampling_result.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/score_hlr_sampler.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/bbox/transforms.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/data_structures/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/data_structures/general_data.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/data_structures/instance_data.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/evaluation/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/evaluation/bbox_overlaps.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/evaluation/class_names.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/evaluation/eval_hooks.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/evaluation/mean_ap.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/evaluation/panoptic_utils.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/evaluation/recall.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/export/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/export/model_wrappers.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/export/onnx_helper.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/export/pytorch2onnx.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/hook/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/hook/checkloss_hook.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/hook/ema.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/hook/memory_profiler_hook.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/hook/set_epoch_info_hook.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/hook/sync_norm_hook.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/hook/sync_random_size_hook.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/hook/wandblogger_hook.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/hook/yolox_lrupdater_hook.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/hook/yolox_mode_switch_hook.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/mask/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/mask/mask_target.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/mask/structures.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/mask/utils.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/optimizers/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/optimizers/builder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/optimizers/layer_decay_optimizer_constructor.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/post_processing/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/post_processing/bbox_nms.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/post_processing/matrix_nms.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/post_processing/merge_augs.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/utils/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/utils/dist_utils.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/utils/misc.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/visualization/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/visualization/image.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/core/visualization/palette.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/api_wrappers/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/api_wrappers/coco_api.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/api_wrappers/panoptic_evaluation.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/builder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/coco.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/custom.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/dataset_wrappers.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/compose.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/formating.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/formatting.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/loading.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/test_time_aug.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/transforms.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/samplers/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/samplers/class_aware_sampler.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/samplers/distributed_sampler.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/samplers/group_sampler.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/samplers/infinite_sampler.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/datasets/utils.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/backbones/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/backbones/resnet.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/builder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/dense_heads/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/dense_heads/anchor_free_head.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/dense_heads/anchor_head.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/dense_heads/atss_head.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/dense_heads/autoassign_head.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/dense_heads/base_dense_head.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/dense_heads/dense_test_mixins.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/dense_heads/fcos_head.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/dense_heads/paa_head.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/detectors/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/detectors/autoassign.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/detectors/base.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/detectors/single_stage.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/losses/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/losses/accuracy.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/losses/cross_entropy_loss.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/losses/focal_loss.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/losses/iou_loss.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/losses/utils.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/necks/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/necks/fpn.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/brick_wrappers.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/builder.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/ckpt_convert.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/conv_upsample.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/csp_layer.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/gaussian_target.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/inverted_residual.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/make_divisible.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/misc.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/normed_predictor.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/panoptic_gt_processing.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/point_sample.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/positional_encoding.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/res_layer.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/models/utils/se_layer.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/__init__.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/collect_env.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/compat_config.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/contextmanagers.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/logger.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/memory.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/misc.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/profiling.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/replace_cfg_vals.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/setup_env.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/split_batch.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/util_distribution.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/util_mixins.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/utils/util_random.py delete mode 100755 cv/detection/autoassign/pytorch/mmdet/version.py delete mode 100755 cv/detection/autoassign/pytorch/pytest.ini delete mode 100755 cv/detection/autoassign/pytorch/requirements.txt delete mode 100755 cv/detection/autoassign/pytorch/setup.py delete mode 100755 cv/detection/autoassign/pytorch/train.py diff --git a/cv/detection/atss_mmdet/pytorch/README.md b/cv/detection/atss_mmdet/pytorch/README.md index 3b5af2d91..4c756bc6e 100644 --- a/cv/detection/atss_mmdet/pytorch/README.md +++ b/cv/detection/atss_mmdet/pytorch/README.md @@ -74,4 +74,4 @@ bash tools/dist_train.sh configs/atss/atss_r50_fpn_1x_coco.py 8 | BI-V100 x8 | MAP=39.5 | ## Reference -- [Bridging the Gap Between Anchor-based and Anchor-free Detection via Adaptive Training Sample Selection](https://arxiv.org/abs/1912.02424) +[mmdetection](https://github.com/open-mmlab/mmdetection) diff --git a/cv/detection/autoassign/pytorch/.gitignore b/cv/detection/autoassign/pytorch/.gitignore deleted file mode 100755 index 16af8baea..000000000 --- a/cv/detection/autoassign/pytorch/.gitignore +++ /dev/null @@ -1,126 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/en/_build/ -docs/zh_cn/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -data/ -data -.vscode -.idea -.DS_Store - -# custom -*.pkl -*.pkl.json -*.log.json -docs/modelzoo_statistics.md -mmdet/.mim -work_dirs/ - -# Pytorch -*.pth -*.py~ -*.sh~ -demo/ -docs/ diff --git a/cv/detection/autoassign/pytorch/LICENSE b/cv/detection/autoassign/pytorch/LICENSE deleted file mode 100755 index dd88d34e2..000000000 --- a/cv/detection/autoassign/pytorch/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Copyright 2018-2023 OpenMMLab. All rights reserved. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2023 OpenMMLab. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cv/detection/autoassign/pytorch/README.md b/cv/detection/autoassign/pytorch/README.md index 551ed1118..69e794236 100755 --- a/cv/detection/autoassign/pytorch/README.md +++ b/cv/detection/autoassign/pytorch/README.md @@ -8,16 +8,27 @@ Determining positive/negative samples for object detection is known as label ass ## Step 1: Installing packages ```bash -$ pip3 install -r requirements.txt -$ MMCV_WITH_OPS=1 python3 setup.py build && cp build/lib.linux*/mmcv/_ext.cpython* mmcv +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +# install MMDetection +git clone https://github.com/open-mmlab/mmdetection.git -b v3.3.0 --depth=1 +cd mmdetection +pip install -v -e . ``` ## Step 2: Preparing datasets ```bash -$ apt install dos2unix -$ mkdir -p data -$ ln -s /path/to/coco/ ./data +mkdir -p data +ln -s /path/to/coco/ ./data + +# Prepare resnet50_msra-5891d200.pth, skip this if fast network +mkdir -p /root/.cache/torch/hub/checkpoints/ +wget https://download.openmmlab.com/pretrain/third_party/resnet50_msra-5891d200.pth -O /root/.cache/torch/hub/checkpoints/resnet50_msra-5891d200.pth ``` Go to visit [COCO official website](https://cocodataset.org/#download), then select the COCO dataset you want to download. @@ -48,14 +59,14 @@ coco2017 ### One single GPU ```bash -$ python3 train.py [training args] # config file can be found in the configs directory +python3 tools/train.py configs/autoassign/autoassign_r50-caffe_fpn_1x_coco.py ``` ### Multiple GPUs on one machine ```bash -$ bash dist_train.sh [training args] # config file can be found in the configs directory +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/autoassign/autoassign_r50-caffe_fpn_1x_coco.py 8 ``` ## Reference - -https://github.com/Megvii-BaseDetection/AutoAssign +[mmdetection](https://github.com/open-mmlab/mmdetection) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/datasets/cityscapes_detection.py b/cv/detection/autoassign/pytorch/configs/_base_/datasets/cityscapes_detection.py deleted file mode 100755 index aec2e3e28..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/datasets/cityscapes_detection.py +++ /dev/null @@ -1,56 +0,0 @@ -# dataset settings -dataset_type = 'CityscapesDataset' -data_root = 'data/cityscapes/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', with_bbox=True), - dict( - type='Resize', img_scale=[(2048, 800), (2048, 1024)], keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=1, - workers_per_gpu=2, - train=dict( - type='RepeatDataset', - times=8, - dataset=dict( - type=dataset_type, - ann_file=data_root + - 'annotations/instancesonly_filtered_gtFine_train.json', - img_prefix=data_root + 'leftImg8bit/train/', - pipeline=train_pipeline)), - val=dict( - type=dataset_type, - ann_file=data_root + - 'annotations/instancesonly_filtered_gtFine_val.json', - img_prefix=data_root + 'leftImg8bit/val/', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - ann_file=data_root + - 'annotations/instancesonly_filtered_gtFine_test.json', - img_prefix=data_root + 'leftImg8bit/test/', - pipeline=test_pipeline)) -evaluation = dict(interval=1, metric='bbox') diff --git a/cv/detection/autoassign/pytorch/configs/_base_/datasets/cityscapes_instance.py b/cv/detection/autoassign/pytorch/configs/_base_/datasets/cityscapes_instance.py deleted file mode 100755 index 43f99803d..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/datasets/cityscapes_instance.py +++ /dev/null @@ -1,56 +0,0 @@ -# dataset settings -dataset_type = 'CityscapesDataset' -data_root = 'data/cityscapes/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', with_bbox=True, with_mask=True), - dict( - type='Resize', img_scale=[(2048, 800), (2048, 1024)], keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(2048, 1024), - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=1, - workers_per_gpu=2, - train=dict( - type='RepeatDataset', - times=8, - dataset=dict( - type=dataset_type, - ann_file=data_root + - 'annotations/instancesonly_filtered_gtFine_train.json', - img_prefix=data_root + 'leftImg8bit/train/', - pipeline=train_pipeline)), - val=dict( - type=dataset_type, - ann_file=data_root + - 'annotations/instancesonly_filtered_gtFine_val.json', - img_prefix=data_root + 'leftImg8bit/val/', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - ann_file=data_root + - 'annotations/instancesonly_filtered_gtFine_test.json', - img_prefix=data_root + 'leftImg8bit/test/', - pipeline=test_pipeline)) -evaluation = dict(metric=['bbox', 'segm']) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_detection.py b/cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_detection.py deleted file mode 100755 index 2e10886c1..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_detection.py +++ /dev/null @@ -1,49 +0,0 @@ -# dataset settings -dataset_type = 'CocoDataset' -data_root = 'data/coco/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', with_bbox=True), - dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(1333, 800), - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - type=dataset_type, - ann_file=data_root + 'annotations/instances_train2017.json', - img_prefix=data_root + 'train2017/', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - ann_file=data_root + 'annotations/instances_val2017.json', - img_prefix=data_root + 'val2017/', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - ann_file=data_root + 'annotations/instances_val2017.json', - img_prefix=data_root + 'val2017/', - pipeline=test_pipeline)) -evaluation = dict(interval=1, metric='bbox') diff --git a/cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_instance.py b/cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_instance.py deleted file mode 100755 index cd7995478..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_instance.py +++ /dev/null @@ -1,49 +0,0 @@ -# dataset settings -dataset_type = 'CocoDataset' -data_root = 'data/coco/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', with_bbox=True, with_mask=True), - dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(1333, 800), - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - type=dataset_type, - ann_file=data_root + 'annotations/instances_train2017.json', - img_prefix=data_root + 'train2017/', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - ann_file=data_root + 'annotations/instances_val2017.json', - img_prefix=data_root + 'val2017/', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - ann_file=data_root + 'annotations/instances_val2017.json', - img_prefix=data_root + 'val2017/', - pipeline=test_pipeline)) -evaluation = dict(metric=['bbox', 'segm']) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_instance_semantic.py b/cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_instance_semantic.py deleted file mode 100755 index 056f03d47..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_instance_semantic.py +++ /dev/null @@ -1,54 +0,0 @@ -# dataset settings -dataset_type = 'CocoDataset' -data_root = 'data/coco/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='LoadAnnotations', with_bbox=True, with_mask=True, with_seg=True), - dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='SegRescale', scale_factor=1 / 8), - dict(type='DefaultFormatBundle'), - dict( - type='Collect', - keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(1333, 800), - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - type=dataset_type, - ann_file=data_root + 'annotations/instances_train2017.json', - img_prefix=data_root + 'train2017/', - seg_prefix=data_root + 'stuffthingmaps/train2017/', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - ann_file=data_root + 'annotations/instances_val2017.json', - img_prefix=data_root + 'val2017/', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - ann_file=data_root + 'annotations/instances_val2017.json', - img_prefix=data_root + 'val2017/', - pipeline=test_pipeline)) -evaluation = dict(metric=['bbox', 'segm']) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_panoptic.py b/cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_panoptic.py deleted file mode 100755 index d49f722a6..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/datasets/coco_panoptic.py +++ /dev/null @@ -1,59 +0,0 @@ -# dataset settings -dataset_type = 'CocoPanopticDataset' -data_root = 'data/coco/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='LoadPanopticAnnotations', - with_bbox=True, - with_mask=True, - with_seg=True), - dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='SegRescale', scale_factor=1 / 4), - dict(type='DefaultFormatBundle'), - dict( - type='Collect', - keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks', 'gt_semantic_seg']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(1333, 800), - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - type=dataset_type, - ann_file=data_root + 'annotations/panoptic_train2017.json', - img_prefix=data_root + 'train2017/', - seg_prefix=data_root + 'annotations/panoptic_train2017/', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - ann_file=data_root + 'annotations/panoptic_val2017.json', - img_prefix=data_root + 'val2017/', - seg_prefix=data_root + 'annotations/panoptic_val2017/', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - ann_file=data_root + 'annotations/panoptic_val2017.json', - img_prefix=data_root + 'val2017/', - seg_prefix=data_root + 'annotations/panoptic_val2017/', - pipeline=test_pipeline)) -evaluation = dict(interval=1, metric=['PQ']) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/datasets/deepfashion.py b/cv/detection/autoassign/pytorch/configs/_base_/datasets/deepfashion.py deleted file mode 100755 index 9f9d00f58..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/datasets/deepfashion.py +++ /dev/null @@ -1,53 +0,0 @@ -# dataset settings -dataset_type = 'DeepFashionDataset' -data_root = 'data/DeepFashion/In-shop/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', with_bbox=True, with_mask=True), - dict(type='Resize', img_scale=(750, 1101), keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(750, 1101), - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - imgs_per_gpu=2, - workers_per_gpu=1, - train=dict( - type=dataset_type, - ann_file=data_root + 'annotations/DeepFashion_segmentation_query.json', - img_prefix=data_root + 'Img/', - pipeline=train_pipeline, - data_root=data_root), - val=dict( - type=dataset_type, - ann_file=data_root + 'annotations/DeepFashion_segmentation_query.json', - img_prefix=data_root + 'Img/', - pipeline=test_pipeline, - data_root=data_root), - test=dict( - type=dataset_type, - ann_file=data_root + - 'annotations/DeepFashion_segmentation_gallery.json', - img_prefix=data_root + 'Img/', - pipeline=test_pipeline, - data_root=data_root)) -evaluation = dict(interval=5, metric=['bbox', 'segm']) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/datasets/lvis_v0.5_instance.py b/cv/detection/autoassign/pytorch/configs/_base_/datasets/lvis_v0.5_instance.py deleted file mode 100755 index 2b3d444cc..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/datasets/lvis_v0.5_instance.py +++ /dev/null @@ -1,24 +0,0 @@ -# dataset settings -_base_ = 'coco_instance.py' -dataset_type = 'LVISV05Dataset' -data_root = 'data/lvis_v0.5/' -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - _delete_=True, - type='ClassBalancedDataset', - oversample_thr=1e-3, - dataset=dict( - type=dataset_type, - ann_file=data_root + 'annotations/lvis_v0.5_train.json', - img_prefix=data_root + 'train2017/')), - val=dict( - type=dataset_type, - ann_file=data_root + 'annotations/lvis_v0.5_val.json', - img_prefix=data_root + 'val2017/'), - test=dict( - type=dataset_type, - ann_file=data_root + 'annotations/lvis_v0.5_val.json', - img_prefix=data_root + 'val2017/')) -evaluation = dict(metric=['bbox', 'segm']) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/datasets/lvis_v1_instance.py b/cv/detection/autoassign/pytorch/configs/_base_/datasets/lvis_v1_instance.py deleted file mode 100755 index 1078d7cf5..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/datasets/lvis_v1_instance.py +++ /dev/null @@ -1,24 +0,0 @@ -# dataset settings -_base_ = 'coco_instance.py' -dataset_type = 'LVISV1Dataset' -data_root = 'data/lvis_v1/' -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - _delete_=True, - type='ClassBalancedDataset', - oversample_thr=1e-3, - dataset=dict( - type=dataset_type, - ann_file=data_root + 'annotations/lvis_v1_train.json', - img_prefix=data_root)), - val=dict( - type=dataset_type, - ann_file=data_root + 'annotations/lvis_v1_val.json', - img_prefix=data_root), - test=dict( - type=dataset_type, - ann_file=data_root + 'annotations/lvis_v1_val.json', - img_prefix=data_root)) -evaluation = dict(metric=['bbox', 'segm']) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/datasets/openimages_detection.py b/cv/detection/autoassign/pytorch/configs/_base_/datasets/openimages_detection.py deleted file mode 100755 index ca8c7b76a..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/datasets/openimages_detection.py +++ /dev/null @@ -1,65 +0,0 @@ -# dataset settings -dataset_type = 'OpenImagesDataset' -data_root = 'data/OpenImages/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', with_bbox=True, denorm_bbox=True), - dict(type='Resize', img_scale=(1024, 800), keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(1024, 800), - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ], - ), -] -data = dict( - samples_per_gpu=2, - workers_per_gpu=0, # workers_per_gpu > 0 may occur out of memory - train=dict( - type=dataset_type, - ann_file=data_root + 'annotations/oidv6-train-annotations-bbox.csv', - img_prefix=data_root + 'OpenImages/train/', - label_file=data_root + 'annotations/class-descriptions-boxable.csv', - hierarchy_file=data_root + - 'annotations/bbox_labels_600_hierarchy.json', - pipeline=train_pipeline), - val=dict( - type=dataset_type, - ann_file=data_root + 'annotations/validation-annotations-bbox.csv', - img_prefix=data_root + 'OpenImages/validation/', - label_file=data_root + 'annotations/class-descriptions-boxable.csv', - hierarchy_file=data_root + - 'annotations/bbox_labels_600_hierarchy.json', - meta_file=data_root + 'annotations/validation-image-metas.pkl', - image_level_ann_file=data_root + - 'annotations/validation-annotations-human-imagelabels-boxable.csv', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - ann_file=data_root + 'annotations/validation-annotations-bbox.csv', - img_prefix=data_root + 'OpenImages/validation/', - label_file=data_root + 'annotations/class-descriptions-boxable.csv', - hierarchy_file=data_root + - 'annotations/bbox_labels_600_hierarchy.json', - meta_file=data_root + 'annotations/validation-image-metas.pkl', - image_level_ann_file=data_root + - 'annotations/validation-annotations-human-imagelabels-boxable.csv', - pipeline=test_pipeline)) -evaluation = dict(interval=1, metric='mAP') diff --git a/cv/detection/autoassign/pytorch/configs/_base_/datasets/voc0712.py b/cv/detection/autoassign/pytorch/configs/_base_/datasets/voc0712.py deleted file mode 100755 index 564f3f189..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/datasets/voc0712.py +++ /dev/null @@ -1,55 +0,0 @@ -# dataset settings -dataset_type = 'VOCDataset' -data_root = 'data/VOCdevkit/' -img_norm_cfg = dict( - mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', with_bbox=True), - dict(type='Resize', img_scale=(1000, 600), keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(1000, 600), - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=2, - workers_per_gpu=2, - train=dict( - type='RepeatDataset', - times=3, - dataset=dict( - type=dataset_type, - ann_file=[ - data_root + 'VOC2007/ImageSets/Main/trainval.txt', - data_root + 'VOC2012/ImageSets/Main/trainval.txt' - ], - img_prefix=[data_root + 'VOC2007/', data_root + 'VOC2012/'], - pipeline=train_pipeline)), - val=dict( - type=dataset_type, - ann_file=data_root + 'VOC2007/ImageSets/Main/test.txt', - img_prefix=data_root + 'VOC2007/', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - ann_file=data_root + 'VOC2007/ImageSets/Main/test.txt', - img_prefix=data_root + 'VOC2007/', - pipeline=test_pipeline)) -evaluation = dict(interval=1, metric='mAP') diff --git a/cv/detection/autoassign/pytorch/configs/_base_/datasets/wider_face.py b/cv/detection/autoassign/pytorch/configs/_base_/datasets/wider_face.py deleted file mode 100755 index 5c6782b8a..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/datasets/wider_face.py +++ /dev/null @@ -1,63 +0,0 @@ -# dataset settings -dataset_type = 'WIDERFaceDataset' -data_root = 'data/WIDERFace/' -img_norm_cfg = dict(mean=[123.675, 116.28, 103.53], std=[1, 1, 1], to_rgb=True) -train_pipeline = [ - dict(type='LoadImageFromFile', to_float32=True), - dict(type='LoadAnnotations', with_bbox=True), - dict( - type='PhotoMetricDistortion', - brightness_delta=32, - contrast_range=(0.5, 1.5), - saturation_range=(0.5, 1.5), - hue_delta=18), - dict( - type='Expand', - mean=img_norm_cfg['mean'], - to_rgb=img_norm_cfg['to_rgb'], - ratio_range=(1, 4)), - dict( - type='MinIoURandomCrop', - min_ious=(0.1, 0.3, 0.5, 0.7, 0.9), - min_crop_size=0.3), - dict(type='Resize', img_scale=(300, 300), keep_ratio=False), - dict(type='Normalize', **img_norm_cfg), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']), -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(300, 300), - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=False), - dict(type='Normalize', **img_norm_cfg), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ]) -] -data = dict( - samples_per_gpu=60, - workers_per_gpu=2, - train=dict( - type='RepeatDataset', - times=2, - dataset=dict( - type=dataset_type, - ann_file=data_root + 'train.txt', - img_prefix=data_root + 'WIDER_train/', - min_size=17, - pipeline=train_pipeline)), - val=dict( - type=dataset_type, - ann_file=data_root + 'val.txt', - img_prefix=data_root + 'WIDER_val/', - pipeline=test_pipeline), - test=dict( - type=dataset_type, - ann_file=data_root + 'val.txt', - img_prefix=data_root + 'WIDER_val/', - pipeline=test_pipeline)) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/default_runtime.py b/cv/detection/autoassign/pytorch/configs/_base_/default_runtime.py deleted file mode 100755 index 16075f1c8..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/default_runtime.py +++ /dev/null @@ -1,27 +0,0 @@ -checkpoint_config = dict(interval=1) -# yapf:disable -log_config = dict( - interval=50, - hooks=[ - dict(type='TextLoggerHook'), - # dict(type='TensorboardLoggerHook') - ]) -# yapf:enable -custom_hooks = [dict(type='NumClassCheckHook')] - -dist_params = dict(backend='nccl') -log_level = 'INFO' -load_from = None -resume_from = None -workflow = [('train', 1)] - -# disable opencv multithreading to avoid system being overloaded -opencv_num_threads = 0 -# set multi-process start method as `fork` to speed up the training -mp_start_method = 'fork' - -# Default setting for scaling LR automatically -# - `enable` means enable scaling LR automatically -# or not by default. -# - `base_batch_size` = (8 GPUs) x (2 samples per GPU). -auto_scale_lr = dict(enable=False, base_batch_size=16) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/models/cascade_mask_rcnn_r50_fpn.py b/cv/detection/autoassign/pytorch/configs/_base_/models/cascade_mask_rcnn_r50_fpn.py deleted file mode 100755 index 4964b1b56..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/models/cascade_mask_rcnn_r50_fpn.py +++ /dev/null @@ -1,196 +0,0 @@ -# model settings -model = dict( - type='CascadeRCNN', - backbone=dict( - type='ResNet', - depth=50, - num_stages=4, - out_indices=(0, 1, 2, 3), - frozen_stages=1, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=True, - style='pytorch', - init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')), - neck=dict( - type='FPN', - in_channels=[256, 512, 1024, 2048], - out_channels=256, - num_outs=5), - rpn_head=dict( - type='RPNHead', - in_channels=256, - feat_channels=256, - anchor_generator=dict( - type='AnchorGenerator', - scales=[8], - ratios=[0.5, 1.0, 2.0], - strides=[4, 8, 16, 32, 64]), - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[.0, .0, .0, .0], - target_stds=[1.0, 1.0, 1.0, 1.0]), - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0), - loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)), - roi_head=dict( - type='CascadeRoIHead', - num_stages=3, - stage_loss_weights=[1, 0.5, 0.25], - bbox_roi_extractor=dict( - type='SingleRoIExtractor', - roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0), - out_channels=256, - featmap_strides=[4, 8, 16, 32]), - bbox_head=[ - dict( - type='Shared2FCBBoxHead', - in_channels=256, - fc_out_channels=1024, - roi_feat_size=7, - num_classes=80, - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[0., 0., 0., 0.], - target_stds=[0.1, 0.1, 0.2, 0.2]), - reg_class_agnostic=True, - loss_cls=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0), - loss_bbox=dict(type='SmoothL1Loss', beta=1.0, - loss_weight=1.0)), - dict( - type='Shared2FCBBoxHead', - in_channels=256, - fc_out_channels=1024, - roi_feat_size=7, - num_classes=80, - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[0., 0., 0., 0.], - target_stds=[0.05, 0.05, 0.1, 0.1]), - reg_class_agnostic=True, - loss_cls=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0), - loss_bbox=dict(type='SmoothL1Loss', beta=1.0, - loss_weight=1.0)), - dict( - type='Shared2FCBBoxHead', - in_channels=256, - fc_out_channels=1024, - roi_feat_size=7, - num_classes=80, - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[0., 0., 0., 0.], - target_stds=[0.033, 0.033, 0.067, 0.067]), - reg_class_agnostic=True, - loss_cls=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0), - loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)) - ], - mask_roi_extractor=dict( - type='SingleRoIExtractor', - roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0), - out_channels=256, - featmap_strides=[4, 8, 16, 32]), - mask_head=dict( - type='FCNMaskHead', - num_convs=4, - in_channels=256, - conv_out_channels=256, - num_classes=80, - loss_mask=dict( - type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))), - # model training and testing settings - train_cfg=dict( - rpn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.7, - neg_iou_thr=0.3, - min_pos_iou=0.3, - match_low_quality=True, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=256, - pos_fraction=0.5, - neg_pos_ub=-1, - add_gt_as_proposals=False), - allowed_border=0, - pos_weight=-1, - debug=False), - rpn_proposal=dict( - nms_pre=2000, - max_per_img=2000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0), - rcnn=[ - dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.5, - neg_iou_thr=0.5, - min_pos_iou=0.5, - match_low_quality=False, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=512, - pos_fraction=0.25, - neg_pos_ub=-1, - add_gt_as_proposals=True), - mask_size=28, - pos_weight=-1, - debug=False), - dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.6, - neg_iou_thr=0.6, - min_pos_iou=0.6, - match_low_quality=False, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=512, - pos_fraction=0.25, - neg_pos_ub=-1, - add_gt_as_proposals=True), - mask_size=28, - pos_weight=-1, - debug=False), - dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.7, - neg_iou_thr=0.7, - min_pos_iou=0.7, - match_low_quality=False, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=512, - pos_fraction=0.25, - neg_pos_ub=-1, - add_gt_as_proposals=True), - mask_size=28, - pos_weight=-1, - debug=False) - ]), - test_cfg=dict( - rpn=dict( - nms_pre=1000, - max_per_img=1000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0), - rcnn=dict( - score_thr=0.05, - nms=dict(type='nms', iou_threshold=0.5), - max_per_img=100, - mask_thr_binary=0.5))) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/models/cascade_rcnn_r50_fpn.py b/cv/detection/autoassign/pytorch/configs/_base_/models/cascade_rcnn_r50_fpn.py deleted file mode 100755 index e47c45f64..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/models/cascade_rcnn_r50_fpn.py +++ /dev/null @@ -1,179 +0,0 @@ -# model settings -model = dict( - type='CascadeRCNN', - backbone=dict( - type='ResNet', - depth=50, - num_stages=4, - out_indices=(0, 1, 2, 3), - frozen_stages=1, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=True, - style='pytorch', - init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')), - neck=dict( - type='FPN', - in_channels=[256, 512, 1024, 2048], - out_channels=256, - num_outs=5), - rpn_head=dict( - type='RPNHead', - in_channels=256, - feat_channels=256, - anchor_generator=dict( - type='AnchorGenerator', - scales=[8], - ratios=[0.5, 1.0, 2.0], - strides=[4, 8, 16, 32, 64]), - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[.0, .0, .0, .0], - target_stds=[1.0, 1.0, 1.0, 1.0]), - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0), - loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0)), - roi_head=dict( - type='CascadeRoIHead', - num_stages=3, - stage_loss_weights=[1, 0.5, 0.25], - bbox_roi_extractor=dict( - type='SingleRoIExtractor', - roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0), - out_channels=256, - featmap_strides=[4, 8, 16, 32]), - bbox_head=[ - dict( - type='Shared2FCBBoxHead', - in_channels=256, - fc_out_channels=1024, - roi_feat_size=7, - num_classes=80, - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[0., 0., 0., 0.], - target_stds=[0.1, 0.1, 0.2, 0.2]), - reg_class_agnostic=True, - loss_cls=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0), - loss_bbox=dict(type='SmoothL1Loss', beta=1.0, - loss_weight=1.0)), - dict( - type='Shared2FCBBoxHead', - in_channels=256, - fc_out_channels=1024, - roi_feat_size=7, - num_classes=80, - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[0., 0., 0., 0.], - target_stds=[0.05, 0.05, 0.1, 0.1]), - reg_class_agnostic=True, - loss_cls=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0), - loss_bbox=dict(type='SmoothL1Loss', beta=1.0, - loss_weight=1.0)), - dict( - type='Shared2FCBBoxHead', - in_channels=256, - fc_out_channels=1024, - roi_feat_size=7, - num_classes=80, - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[0., 0., 0., 0.], - target_stds=[0.033, 0.033, 0.067, 0.067]), - reg_class_agnostic=True, - loss_cls=dict( - type='CrossEntropyLoss', - use_sigmoid=False, - loss_weight=1.0), - loss_bbox=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1.0)) - ]), - # model training and testing settings - train_cfg=dict( - rpn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.7, - neg_iou_thr=0.3, - min_pos_iou=0.3, - match_low_quality=True, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=256, - pos_fraction=0.5, - neg_pos_ub=-1, - add_gt_as_proposals=False), - allowed_border=0, - pos_weight=-1, - debug=False), - rpn_proposal=dict( - nms_pre=2000, - max_per_img=2000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0), - rcnn=[ - dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.5, - neg_iou_thr=0.5, - min_pos_iou=0.5, - match_low_quality=False, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=512, - pos_fraction=0.25, - neg_pos_ub=-1, - add_gt_as_proposals=True), - pos_weight=-1, - debug=False), - dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.6, - neg_iou_thr=0.6, - min_pos_iou=0.6, - match_low_quality=False, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=512, - pos_fraction=0.25, - neg_pos_ub=-1, - add_gt_as_proposals=True), - pos_weight=-1, - debug=False), - dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.7, - neg_iou_thr=0.7, - min_pos_iou=0.7, - match_low_quality=False, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=512, - pos_fraction=0.25, - neg_pos_ub=-1, - add_gt_as_proposals=True), - pos_weight=-1, - debug=False) - ]), - test_cfg=dict( - rpn=dict( - nms_pre=1000, - max_per_img=1000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0), - rcnn=dict( - score_thr=0.05, - nms=dict(type='nms', iou_threshold=0.5), - max_per_img=100))) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/models/fast_rcnn_r50_fpn.py b/cv/detection/autoassign/pytorch/configs/_base_/models/fast_rcnn_r50_fpn.py deleted file mode 100755 index 04d6b8701..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/models/fast_rcnn_r50_fpn.py +++ /dev/null @@ -1,62 +0,0 @@ -# model settings -model = dict( - type='FastRCNN', - backbone=dict( - type='ResNet', - depth=50, - num_stages=4, - out_indices=(0, 1, 2, 3), - frozen_stages=1, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=True, - style='pytorch', - init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')), - neck=dict( - type='FPN', - in_channels=[256, 512, 1024, 2048], - out_channels=256, - num_outs=5), - roi_head=dict( - type='StandardRoIHead', - bbox_roi_extractor=dict( - type='SingleRoIExtractor', - roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0), - out_channels=256, - featmap_strides=[4, 8, 16, 32]), - bbox_head=dict( - type='Shared2FCBBoxHead', - in_channels=256, - fc_out_channels=1024, - roi_feat_size=7, - num_classes=80, - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[0., 0., 0., 0.], - target_stds=[0.1, 0.1, 0.2, 0.2]), - reg_class_agnostic=False, - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0))), - # model training and testing settings - train_cfg=dict( - rcnn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.5, - neg_iou_thr=0.5, - min_pos_iou=0.5, - match_low_quality=False, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=512, - pos_fraction=0.25, - neg_pos_ub=-1, - add_gt_as_proposals=True), - pos_weight=-1, - debug=False)), - test_cfg=dict( - rcnn=dict( - score_thr=0.05, - nms=dict(type='nms', iou_threshold=0.5), - max_per_img=100))) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/models/faster_rcnn_r50_caffe_c4.py b/cv/detection/autoassign/pytorch/configs/_base_/models/faster_rcnn_r50_caffe_c4.py deleted file mode 100755 index 673ce729a..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/models/faster_rcnn_r50_caffe_c4.py +++ /dev/null @@ -1,117 +0,0 @@ -# model settings -norm_cfg = dict(type='BN', requires_grad=False) -model = dict( - type='FasterRCNN', - backbone=dict( - type='ResNet', - depth=50, - num_stages=3, - strides=(1, 2, 2), - dilations=(1, 1, 1), - out_indices=(2, ), - frozen_stages=1, - norm_cfg=norm_cfg, - norm_eval=True, - style='caffe', - init_cfg=dict( - type='Pretrained', - checkpoint='open-mmlab://detectron2/resnet50_caffe')), - rpn_head=dict( - type='RPNHead', - in_channels=1024, - feat_channels=1024, - anchor_generator=dict( - type='AnchorGenerator', - scales=[2, 4, 8, 16, 32], - ratios=[0.5, 1.0, 2.0], - strides=[16]), - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[.0, .0, .0, .0], - target_stds=[1.0, 1.0, 1.0, 1.0]), - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0)), - roi_head=dict( - type='StandardRoIHead', - shared_head=dict( - type='ResLayer', - depth=50, - stage=3, - stride=2, - dilation=1, - style='caffe', - norm_cfg=norm_cfg, - norm_eval=True, - init_cfg=dict( - type='Pretrained', - checkpoint='open-mmlab://detectron2/resnet50_caffe')), - bbox_roi_extractor=dict( - type='SingleRoIExtractor', - roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0), - out_channels=1024, - featmap_strides=[16]), - bbox_head=dict( - type='BBoxHead', - with_avg_pool=True, - roi_feat_size=7, - in_channels=2048, - num_classes=80, - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[0., 0., 0., 0.], - target_stds=[0.1, 0.1, 0.2, 0.2]), - reg_class_agnostic=False, - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0))), - # model training and testing settings - train_cfg=dict( - rpn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.7, - neg_iou_thr=0.3, - min_pos_iou=0.3, - match_low_quality=True, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=256, - pos_fraction=0.5, - neg_pos_ub=-1, - add_gt_as_proposals=False), - allowed_border=-1, - pos_weight=-1, - debug=False), - rpn_proposal=dict( - nms_pre=12000, - max_per_img=2000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0), - rcnn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.5, - neg_iou_thr=0.5, - min_pos_iou=0.5, - match_low_quality=False, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=512, - pos_fraction=0.25, - neg_pos_ub=-1, - add_gt_as_proposals=True), - pos_weight=-1, - debug=False)), - test_cfg=dict( - rpn=dict( - nms_pre=6000, - max_per_img=1000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0), - rcnn=dict( - score_thr=0.05, - nms=dict(type='nms', iou_threshold=0.5), - max_per_img=100))) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/models/faster_rcnn_r50_caffe_dc5.py b/cv/detection/autoassign/pytorch/configs/_base_/models/faster_rcnn_r50_caffe_dc5.py deleted file mode 100755 index 5dca1f987..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/models/faster_rcnn_r50_caffe_dc5.py +++ /dev/null @@ -1,105 +0,0 @@ -# model settings -norm_cfg = dict(type='BN', requires_grad=False) -model = dict( - type='FasterRCNN', - backbone=dict( - type='ResNet', - depth=50, - num_stages=4, - strides=(1, 2, 2, 1), - dilations=(1, 1, 1, 2), - out_indices=(3, ), - frozen_stages=1, - norm_cfg=norm_cfg, - norm_eval=True, - style='caffe', - init_cfg=dict( - type='Pretrained', - checkpoint='open-mmlab://detectron2/resnet50_caffe')), - rpn_head=dict( - type='RPNHead', - in_channels=2048, - feat_channels=2048, - anchor_generator=dict( - type='AnchorGenerator', - scales=[2, 4, 8, 16, 32], - ratios=[0.5, 1.0, 2.0], - strides=[16]), - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[.0, .0, .0, .0], - target_stds=[1.0, 1.0, 1.0, 1.0]), - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0)), - roi_head=dict( - type='StandardRoIHead', - bbox_roi_extractor=dict( - type='SingleRoIExtractor', - roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0), - out_channels=2048, - featmap_strides=[16]), - bbox_head=dict( - type='Shared2FCBBoxHead', - in_channels=2048, - fc_out_channels=1024, - roi_feat_size=7, - num_classes=80, - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[0., 0., 0., 0.], - target_stds=[0.1, 0.1, 0.2, 0.2]), - reg_class_agnostic=False, - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0))), - # model training and testing settings - train_cfg=dict( - rpn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.7, - neg_iou_thr=0.3, - min_pos_iou=0.3, - match_low_quality=True, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=256, - pos_fraction=0.5, - neg_pos_ub=-1, - add_gt_as_proposals=False), - allowed_border=0, - pos_weight=-1, - debug=False), - rpn_proposal=dict( - nms_pre=12000, - max_per_img=2000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0), - rcnn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.5, - neg_iou_thr=0.5, - min_pos_iou=0.5, - match_low_quality=False, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=512, - pos_fraction=0.25, - neg_pos_ub=-1, - add_gt_as_proposals=True), - pos_weight=-1, - debug=False)), - test_cfg=dict( - rpn=dict( - nms=dict(type='nms', iou_threshold=0.7), - nms_pre=6000, - max_per_img=1000, - min_bbox_size=0), - rcnn=dict( - score_thr=0.05, - nms=dict(type='nms', iou_threshold=0.5), - max_per_img=100))) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/models/faster_rcnn_r50_fpn.py b/cv/detection/autoassign/pytorch/configs/_base_/models/faster_rcnn_r50_fpn.py deleted file mode 100755 index edff79655..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/models/faster_rcnn_r50_fpn.py +++ /dev/null @@ -1,108 +0,0 @@ -# model settings -model = dict( - type='FasterRCNN', - backbone=dict( - type='ResNet', - depth=50, - num_stages=4, - out_indices=(0, 1, 2, 3), - frozen_stages=1, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=True, - style='pytorch', - init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')), - neck=dict( - type='FPN', - in_channels=[256, 512, 1024, 2048], - out_channels=256, - num_outs=5), - rpn_head=dict( - type='RPNHead', - in_channels=256, - feat_channels=256, - anchor_generator=dict( - type='AnchorGenerator', - scales=[8], - ratios=[0.5, 1.0, 2.0], - strides=[4, 8, 16, 32, 64]), - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[.0, .0, .0, .0], - target_stds=[1.0, 1.0, 1.0, 1.0]), - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0)), - roi_head=dict( - type='StandardRoIHead', - bbox_roi_extractor=dict( - type='SingleRoIExtractor', - roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0), - out_channels=256, - featmap_strides=[4, 8, 16, 32]), - bbox_head=dict( - type='Shared2FCBBoxHead', - in_channels=256, - fc_out_channels=1024, - roi_feat_size=7, - num_classes=80, - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[0., 0., 0., 0.], - target_stds=[0.1, 0.1, 0.2, 0.2]), - reg_class_agnostic=False, - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0))), - # model training and testing settings - train_cfg=dict( - rpn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.7, - neg_iou_thr=0.3, - min_pos_iou=0.3, - match_low_quality=True, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=256, - pos_fraction=0.5, - neg_pos_ub=-1, - add_gt_as_proposals=False), - allowed_border=-1, - pos_weight=-1, - debug=False), - rpn_proposal=dict( - nms_pre=2000, - max_per_img=1000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0), - rcnn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.5, - neg_iou_thr=0.5, - min_pos_iou=0.5, - match_low_quality=False, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=512, - pos_fraction=0.25, - neg_pos_ub=-1, - add_gt_as_proposals=True), - pos_weight=-1, - debug=False)), - test_cfg=dict( - rpn=dict( - nms_pre=1000, - max_per_img=1000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0), - rcnn=dict( - score_thr=0.05, - nms=dict(type='nms', iou_threshold=0.5), - max_per_img=100) - # soft-nms is also supported for rcnn testing - # e.g., nms=dict(type='soft_nms', iou_threshold=0.5, min_score=0.05) - )) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/models/mask_rcnn_r50_caffe_c4.py b/cv/detection/autoassign/pytorch/configs/_base_/models/mask_rcnn_r50_caffe_c4.py deleted file mode 100755 index 486f9a5fd..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/models/mask_rcnn_r50_caffe_c4.py +++ /dev/null @@ -1,125 +0,0 @@ -# model settings -norm_cfg = dict(type='BN', requires_grad=False) -model = dict( - type='MaskRCNN', - backbone=dict( - type='ResNet', - depth=50, - num_stages=3, - strides=(1, 2, 2), - dilations=(1, 1, 1), - out_indices=(2, ), - frozen_stages=1, - norm_cfg=norm_cfg, - norm_eval=True, - style='caffe', - init_cfg=dict( - type='Pretrained', - checkpoint='open-mmlab://detectron2/resnet50_caffe')), - rpn_head=dict( - type='RPNHead', - in_channels=1024, - feat_channels=1024, - anchor_generator=dict( - type='AnchorGenerator', - scales=[2, 4, 8, 16, 32], - ratios=[0.5, 1.0, 2.0], - strides=[16]), - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[.0, .0, .0, .0], - target_stds=[1.0, 1.0, 1.0, 1.0]), - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0)), - roi_head=dict( - type='StandardRoIHead', - shared_head=dict( - type='ResLayer', - depth=50, - stage=3, - stride=2, - dilation=1, - style='caffe', - norm_cfg=norm_cfg, - norm_eval=True), - bbox_roi_extractor=dict( - type='SingleRoIExtractor', - roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0), - out_channels=1024, - featmap_strides=[16]), - bbox_head=dict( - type='BBoxHead', - with_avg_pool=True, - roi_feat_size=7, - in_channels=2048, - num_classes=80, - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[0., 0., 0., 0.], - target_stds=[0.1, 0.1, 0.2, 0.2]), - reg_class_agnostic=False, - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0)), - mask_roi_extractor=None, - mask_head=dict( - type='FCNMaskHead', - num_convs=0, - in_channels=2048, - conv_out_channels=256, - num_classes=80, - loss_mask=dict( - type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))), - # model training and testing settings - train_cfg=dict( - rpn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.7, - neg_iou_thr=0.3, - min_pos_iou=0.3, - match_low_quality=True, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=256, - pos_fraction=0.5, - neg_pos_ub=-1, - add_gt_as_proposals=False), - allowed_border=0, - pos_weight=-1, - debug=False), - rpn_proposal=dict( - nms_pre=12000, - max_per_img=2000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0), - rcnn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.5, - neg_iou_thr=0.5, - min_pos_iou=0.5, - match_low_quality=False, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=512, - pos_fraction=0.25, - neg_pos_ub=-1, - add_gt_as_proposals=True), - mask_size=14, - pos_weight=-1, - debug=False)), - test_cfg=dict( - rpn=dict( - nms_pre=6000, - nms=dict(type='nms', iou_threshold=0.7), - max_per_img=1000, - min_bbox_size=0), - rcnn=dict( - score_thr=0.05, - nms=dict(type='nms', iou_threshold=0.5), - max_per_img=100, - mask_thr_binary=0.5))) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/models/mask_rcnn_r50_fpn.py b/cv/detection/autoassign/pytorch/configs/_base_/models/mask_rcnn_r50_fpn.py deleted file mode 100755 index 4bb520daa..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/models/mask_rcnn_r50_fpn.py +++ /dev/null @@ -1,120 +0,0 @@ -# model settings -model = dict( - type='MaskRCNN', - backbone=dict( - type='ResNet', - depth=50, - num_stages=4, - out_indices=(0, 1, 2, 3), - frozen_stages=1, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=True, - style='pytorch', - init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')), - neck=dict( - type='FPN', - in_channels=[256, 512, 1024, 2048], - out_channels=256, - num_outs=5), - rpn_head=dict( - type='RPNHead', - in_channels=256, - feat_channels=256, - anchor_generator=dict( - type='AnchorGenerator', - scales=[8], - ratios=[0.5, 1.0, 2.0], - strides=[4, 8, 16, 32, 64]), - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[.0, .0, .0, .0], - target_stds=[1.0, 1.0, 1.0, 1.0]), - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0)), - roi_head=dict( - type='StandardRoIHead', - bbox_roi_extractor=dict( - type='SingleRoIExtractor', - roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0), - out_channels=256, - featmap_strides=[4, 8, 16, 32]), - bbox_head=dict( - type='Shared2FCBBoxHead', - in_channels=256, - fc_out_channels=1024, - roi_feat_size=7, - num_classes=80, - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[0., 0., 0., 0.], - target_stds=[0.1, 0.1, 0.2, 0.2]), - reg_class_agnostic=False, - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0)), - mask_roi_extractor=dict( - type='SingleRoIExtractor', - roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0), - out_channels=256, - featmap_strides=[4, 8, 16, 32]), - mask_head=dict( - type='FCNMaskHead', - num_convs=4, - in_channels=256, - conv_out_channels=256, - num_classes=80, - loss_mask=dict( - type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))), - # model training and testing settings - train_cfg=dict( - rpn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.7, - neg_iou_thr=0.3, - min_pos_iou=0.3, - match_low_quality=True, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=256, - pos_fraction=0.5, - neg_pos_ub=-1, - add_gt_as_proposals=False), - allowed_border=-1, - pos_weight=-1, - debug=False), - rpn_proposal=dict( - nms_pre=2000, - max_per_img=1000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0), - rcnn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.5, - neg_iou_thr=0.5, - min_pos_iou=0.5, - match_low_quality=True, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=512, - pos_fraction=0.25, - neg_pos_ub=-1, - add_gt_as_proposals=True), - mask_size=28, - pos_weight=-1, - debug=False)), - test_cfg=dict( - rpn=dict( - nms_pre=1000, - max_per_img=1000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0), - rcnn=dict( - score_thr=0.05, - nms=dict(type='nms', iou_threshold=0.5), - max_per_img=100, - mask_thr_binary=0.5))) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/models/retinanet_r50_fpn.py b/cv/detection/autoassign/pytorch/configs/_base_/models/retinanet_r50_fpn.py deleted file mode 100755 index 4857fc95e..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/models/retinanet_r50_fpn.py +++ /dev/null @@ -1,60 +0,0 @@ -# model settings -model = dict( - type='RetinaNet', - backbone=dict( - type='ResNet', - depth=50, - num_stages=4, - out_indices=(0, 1, 2, 3), - frozen_stages=1, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=True, - style='pytorch', - init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')), - neck=dict( - type='FPN', - in_channels=[256, 512, 1024, 2048], - out_channels=256, - start_level=1, - add_extra_convs='on_input', - num_outs=5), - bbox_head=dict( - type='RetinaHead', - num_classes=80, - in_channels=256, - stacked_convs=4, - feat_channels=256, - anchor_generator=dict( - type='AnchorGenerator', - octave_base_scale=4, - scales_per_octave=3, - ratios=[0.5, 1.0, 2.0], - strides=[8, 16, 32, 64, 128]), - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[.0, .0, .0, .0], - target_stds=[1.0, 1.0, 1.0, 1.0]), - loss_cls=dict( - type='FocalLoss', - use_sigmoid=True, - gamma=2.0, - alpha=0.25, - loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0)), - # model training and testing settings - train_cfg=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.5, - neg_iou_thr=0.4, - min_pos_iou=0, - ignore_iof_thr=-1), - allowed_border=-1, - pos_weight=-1, - debug=False), - test_cfg=dict( - nms_pre=1000, - min_bbox_size=0, - score_thr=0.05, - nms=dict(type='nms', iou_threshold=0.5), - max_per_img=100)) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/models/rpn_r50_caffe_c4.py b/cv/detection/autoassign/pytorch/configs/_base_/models/rpn_r50_caffe_c4.py deleted file mode 100755 index 1e2ee0bbc..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/models/rpn_r50_caffe_c4.py +++ /dev/null @@ -1,58 +0,0 @@ -# model settings -model = dict( - type='RPN', - backbone=dict( - type='ResNet', - depth=50, - num_stages=3, - strides=(1, 2, 2), - dilations=(1, 1, 1), - out_indices=(2, ), - frozen_stages=1, - norm_cfg=dict(type='BN', requires_grad=False), - norm_eval=True, - style='caffe', - init_cfg=dict( - type='Pretrained', - checkpoint='open-mmlab://detectron2/resnet50_caffe')), - neck=None, - rpn_head=dict( - type='RPNHead', - in_channels=1024, - feat_channels=1024, - anchor_generator=dict( - type='AnchorGenerator', - scales=[2, 4, 8, 16, 32], - ratios=[0.5, 1.0, 2.0], - strides=[16]), - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[.0, .0, .0, .0], - target_stds=[1.0, 1.0, 1.0, 1.0]), - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0)), - # model training and testing settings - train_cfg=dict( - rpn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.7, - neg_iou_thr=0.3, - min_pos_iou=0.3, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=256, - pos_fraction=0.5, - neg_pos_ub=-1, - add_gt_as_proposals=False), - allowed_border=0, - pos_weight=-1, - debug=False)), - test_cfg=dict( - rpn=dict( - nms_pre=12000, - max_per_img=2000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0))) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/models/rpn_r50_fpn.py b/cv/detection/autoassign/pytorch/configs/_base_/models/rpn_r50_fpn.py deleted file mode 100755 index 4133b292d..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/models/rpn_r50_fpn.py +++ /dev/null @@ -1,58 +0,0 @@ -# model settings -model = dict( - type='RPN', - backbone=dict( - type='ResNet', - depth=50, - num_stages=4, - out_indices=(0, 1, 2, 3), - frozen_stages=1, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=True, - style='pytorch', - init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')), - neck=dict( - type='FPN', - in_channels=[256, 512, 1024, 2048], - out_channels=256, - num_outs=5), - rpn_head=dict( - type='RPNHead', - in_channels=256, - feat_channels=256, - anchor_generator=dict( - type='AnchorGenerator', - scales=[8], - ratios=[0.5, 1.0, 2.0], - strides=[4, 8, 16, 32, 64]), - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[.0, .0, .0, .0], - target_stds=[1.0, 1.0, 1.0, 1.0]), - loss_cls=dict( - type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0), - loss_bbox=dict(type='L1Loss', loss_weight=1.0)), - # model training and testing settings - train_cfg=dict( - rpn=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.7, - neg_iou_thr=0.3, - min_pos_iou=0.3, - ignore_iof_thr=-1), - sampler=dict( - type='RandomSampler', - num=256, - pos_fraction=0.5, - neg_pos_ub=-1, - add_gt_as_proposals=False), - allowed_border=0, - pos_weight=-1, - debug=False)), - test_cfg=dict( - rpn=dict( - nms_pre=2000, - max_per_img=1000, - nms=dict(type='nms', iou_threshold=0.7), - min_bbox_size=0))) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/models/ssd300.py b/cv/detection/autoassign/pytorch/configs/_base_/models/ssd300.py deleted file mode 100755 index 6e9f67136..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/models/ssd300.py +++ /dev/null @@ -1,56 +0,0 @@ -# model settings -input_size = 300 -model = dict( - type='SingleStageDetector', - backbone=dict( - type='SSDVGG', - depth=16, - with_last_pool=False, - ceil_mode=True, - out_indices=(3, 4), - out_feature_indices=(22, 34), - init_cfg=dict( - type='Pretrained', checkpoint='open-mmlab://vgg16_caffe')), - neck=dict( - type='SSDNeck', - in_channels=(512, 1024), - out_channels=(512, 1024, 512, 256, 256, 256), - level_strides=(2, 2, 1, 1), - level_paddings=(1, 1, 0, 0), - l2_norm_scale=20), - bbox_head=dict( - type='SSDHead', - in_channels=(512, 1024, 512, 256, 256, 256), - num_classes=80, - anchor_generator=dict( - type='SSDAnchorGenerator', - scale_major=False, - input_size=input_size, - basesize_ratio_range=(0.15, 0.9), - strides=[8, 16, 32, 64, 100, 300], - ratios=[[2], [2, 3], [2, 3], [2, 3], [2], [2]]), - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - target_means=[.0, .0, .0, .0], - target_stds=[0.1, 0.1, 0.2, 0.2])), - # model training and testing settings - train_cfg=dict( - assigner=dict( - type='MaxIoUAssigner', - pos_iou_thr=0.5, - neg_iou_thr=0.5, - min_pos_iou=0., - ignore_iof_thr=-1, - gt_max_assign_all=False), - smoothl1_beta=1., - allowed_border=-1, - pos_weight=-1, - neg_pos_ratio=3, - debug=False), - test_cfg=dict( - nms_pre=1000, - nms=dict(type='nms', iou_threshold=0.45), - min_bbox_size=0, - score_thr=0.02, - max_per_img=200)) -cudnn_benchmark = True diff --git a/cv/detection/autoassign/pytorch/configs/_base_/schedules/schedule_1x.py b/cv/detection/autoassign/pytorch/configs/_base_/schedules/schedule_1x.py deleted file mode 100755 index a3fc8e340..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/schedules/schedule_1x.py +++ /dev/null @@ -1,11 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001) -optimizer_config = dict(grad_clip=None) -# learning policy -lr_config = dict( - policy='step', - warmup='linear', - warmup_iters=500, - warmup_ratio=0.001, - step=[8, 11]) -runner = dict(type='EpochBasedRunner', max_epochs=12) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/schedules/schedule_20e.py b/cv/detection/autoassign/pytorch/configs/_base_/schedules/schedule_20e.py deleted file mode 100755 index d11eee3f1..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/schedules/schedule_20e.py +++ /dev/null @@ -1,11 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001) -optimizer_config = dict(grad_clip=None) -# learning policy -lr_config = dict( - policy='step', - warmup='linear', - warmup_iters=500, - warmup_ratio=0.001, - step=[16, 19]) -runner = dict(type='EpochBasedRunner', max_epochs=20) diff --git a/cv/detection/autoassign/pytorch/configs/_base_/schedules/schedule_2x.py b/cv/detection/autoassign/pytorch/configs/_base_/schedules/schedule_2x.py deleted file mode 100755 index 6dbf94294..000000000 --- a/cv/detection/autoassign/pytorch/configs/_base_/schedules/schedule_2x.py +++ /dev/null @@ -1,11 +0,0 @@ -# optimizer -optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001) -optimizer_config = dict(grad_clip=None) -# learning policy -lr_config = dict( - policy='step', - warmup='linear', - warmup_iters=500, - warmup_ratio=0.001, - step=[16, 22]) -runner = dict(type='EpochBasedRunner', max_epochs=24) diff --git a/cv/detection/autoassign/pytorch/configs/autoassign/README.md b/cv/detection/autoassign/pytorch/configs/autoassign/README.md deleted file mode 100755 index 73b01301d..000000000 --- a/cv/detection/autoassign/pytorch/configs/autoassign/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# AutoAssign - -> [AutoAssign: Differentiable Label Assignment for Dense Object Detection](https://arxiv.org/abs/2007.03496) - - - -## Abstract - -Determining positive/negative samples for object detection is known as label assignment. Here we present an anchor-free detector named AutoAssign. It requires little human knowledge and achieves appearance-aware through a fully differentiable weighting mechanism. During training, to both satisfy the prior distribution of data and adapt to category characteristics, we present Center Weighting to adjust the category-specific prior distributions. To adapt to object appearances, Confidence Weighting is proposed to adjust the specific assign strategy of each instance. The two weighting modules are then combined to generate positive and negative weights to adjust each location's confidence. Extensive experiments on the MS COCO show that our method steadily surpasses other best sampling strategies by large margins with various backbones. Moreover, our best model achieves 52.1% AP, outperforming all existing one-stage detectors. Besides, experiments on other datasets, e.g., PASCAL VOC, Objects365, and WiderFace, demonstrate the broad applicability of AutoAssign. - -
- -
- -## Results and Models - -| Backbone | Style | Lr schd | Mem (GB) | box AP | Config | Download | -| :------: | :---: | :-----: | :------: | :----: | :------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| R-50 | caffe | 1x | 4.08 | 40.4 | [config](https://github.com/open-mmlab/mmdetection/tree/master/configs/autoassign/autoassign_r50_fpn_8x2_1x_coco.py) | [model](https://download.openmmlab.com/mmdetection/v2.0/autoassign/auto_assign_r50_fpn_1x_coco/auto_assign_r50_fpn_1x_coco_20210413_115540-5e17991f.pth) \| [log](https://download.openmmlab.com/mmdetection/v2.0/autoassign/auto_assign_r50_fpn_1x_coco/auto_assign_r50_fpn_1x_coco_20210413_115540-5e17991f.log.json) | - -**Note**: - -1. We find that the performance is unstable with 1x setting and may fluctuate by about 0.3 mAP. mAP 40.3 ~ 40.6 is acceptable. Such fluctuation can also be found in the original implementation. -2. You can get a more stable results ~ mAP 40.6 with a schedule total 13 epoch, and learning rate is divided by 10 at 10th and 13th epoch. - -## Citation - -```latex -@article{zhu2020autoassign, - title={AutoAssign: Differentiable Label Assignment for Dense Object Detection}, - author={Zhu, Benjin and Wang, Jianfeng and Jiang, Zhengkai and Zong, Fuhang and Liu, Songtao and Li, Zeming and Sun, Jian}, - journal={arXiv preprint arXiv:2007.03496}, - year={2020} -} -``` diff --git a/cv/detection/autoassign/pytorch/configs/autoassign/autoassign_r50_fpn_8x2_1x_coco.py b/cv/detection/autoassign/pytorch/configs/autoassign/autoassign_r50_fpn_8x2_1x_coco.py deleted file mode 100755 index e42e428e9..000000000 --- a/cv/detection/autoassign/pytorch/configs/autoassign/autoassign_r50_fpn_8x2_1x_coco.py +++ /dev/null @@ -1,86 +0,0 @@ -# We follow the original implementation which -# adopts the Caffe pre-trained backbone. -_base_ = [ - '../_base_/datasets/coco_detection.py', - '../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py' -] -model = dict( - type='AutoAssign', - backbone=dict( - type='ResNet', - depth=50, - num_stages=4, - out_indices=(0, 1, 2, 3), - frozen_stages=1, - norm_cfg=dict(type='BN', requires_grad=False), - norm_eval=True, - style='caffe', - init_cfg=dict( - type='Pretrained', - checkpoint='open-mmlab://detectron2/resnet50_caffe')), - neck=dict( - type='FPN', - in_channels=[256, 512, 1024, 2048], - out_channels=256, - start_level=1, - add_extra_convs=True, - num_outs=5, - relu_before_extra_convs=True, - init_cfg=dict(type='Caffe2Xavier', layer='Conv2d')), - bbox_head=dict( - type='AutoAssignHead', - num_classes=80, - in_channels=256, - stacked_convs=4, - feat_channels=256, - strides=[8, 16, 32, 64, 128], - loss_bbox=dict(type='GIoULoss', loss_weight=5.0)), - train_cfg=None, - test_cfg=dict( - nms_pre=1000, - min_bbox_size=0, - score_thr=0.05, - nms=dict(type='nms', iou_threshold=0.6), - max_per_img=100)) -img_norm_cfg = dict( - mean=[102.9801, 115.9465, 122.7717], std=[1.0, 1.0, 1.0], to_rgb=False) -train_pipeline = [ - dict(type='LoadImageFromFile'), - dict(type='LoadAnnotations', with_bbox=True), - dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), - dict(type='RandomFlip', flip_ratio=0.5), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='DefaultFormatBundle'), - dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']) -] -test_pipeline = [ - dict(type='LoadImageFromFile'), - dict( - type='MultiScaleFlipAug', - img_scale=(1333, 800), - flip=False, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']) - ]) -] -data = dict( - train=dict(pipeline=train_pipeline), - val=dict(pipeline=test_pipeline), - test=dict(pipeline=test_pipeline)) -# optimizer -optimizer = dict(lr=0.01, paramwise_cfg=dict(norm_decay_mult=0.)) -# learning policy -lr_config = dict( - policy='step', - warmup='linear', - warmup_iters=1000, - warmup_ratio=1.0 / 1000, - step=[8, 11]) -total_epochs = 12 -cudnn_benchmark = True diff --git a/cv/detection/autoassign/pytorch/configs/autoassign/metafile.yml b/cv/detection/autoassign/pytorch/configs/autoassign/metafile.yml deleted file mode 100755 index c3fdc9c6b..000000000 --- a/cv/detection/autoassign/pytorch/configs/autoassign/metafile.yml +++ /dev/null @@ -1,33 +0,0 @@ -Collections: - - Name: AutoAssign - Metadata: - Training Data: COCO - Training Techniques: - - SGD with Momentum - - Weight Decay - Training Resources: 8x V100 GPUs - Architecture: - - AutoAssign - - FPN - - ResNet - Paper: - URL: https://arxiv.org/abs/2007.03496 - Title: 'AutoAssign: Differentiable Label Assignment for Dense Object Detection' - README: configs/autoassign/README.md - Code: - URL: https://github.com/open-mmlab/mmdetection/blob/v2.12.0/mmdet/models/detectors/autoassign.py#L6 - Version: v2.12.0 - -Models: - - Name: autoassign_r50_fpn_8x2_1x_coco - In Collection: AutoAssign - Config: configs/autoassign/autoassign_r50_fpn_8x2_1x_coco.py - Metadata: - Training Memory (GB): 4.08 - Epochs: 12 - Results: - - Task: Object Detection - Dataset: COCO - Metrics: - box AP: 40.4 - Weights: https://download.openmmlab.com/mmdetection/v2.0/autoassign/auto_assign_r50_fpn_1x_coco/auto_assign_r50_fpn_1x_coco_20210413_115540-5e17991f.pth diff --git a/cv/detection/autoassign/pytorch/dist_train.sh b/cv/detection/autoassign/pytorch/dist_train.sh deleted file mode 100755 index 2d7dfad14..000000000 --- a/cv/detection/autoassign/pytorch/dist_train.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -CONFIG=$1 -GPUS=$2 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/train.py \ - $CONFIG \ - --seed 0 \ - --launcher pytorch ${@:3} diff --git a/cv/detection/autoassign/pytorch/docker/Dockerfile b/cv/detection/autoassign/pytorch/docker/Dockerfile deleted file mode 100755 index 0d904c2f6..000000000 --- a/cv/detection/autoassign/pytorch/docker/Dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -ARG PYTORCH="1.6.0" -ARG CUDA="10.1" -ARG CUDNN="7" - -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ENV TORCH_CUDA_ARCH_LIST="6.0 6.1 7.0+PTX" -ENV TORCH_NVCC_FLAGS="-Xfatbin -compress-all" -ENV CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" - -RUN apt-get update && apt-get install -y ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Install MMCV -RUN pip install --no-cache-dir --upgrade pip wheel setuptools -RUN pip install --no-cache-dir mmcv-full==1.3.17 -f https://download.openmmlab.com/mmcv/dist/cu101/torch1.6.0/index.html - -# Install MMDetection -RUN conda clean --all -RUN git clone https://github.com/open-mmlab/mmdetection.git /mmdetection -WORKDIR /mmdetection -ENV FORCE_CUDA="1" -RUN pip install --no-cache-dir -r requirements/build.txt -RUN pip install --no-cache-dir -e . diff --git a/cv/detection/autoassign/pytorch/docker/serve/Dockerfile b/cv/detection/autoassign/pytorch/docker/serve/Dockerfile deleted file mode 100755 index ab85e13dd..000000000 --- a/cv/detection/autoassign/pytorch/docker/serve/Dockerfile +++ /dev/null @@ -1,49 +0,0 @@ -ARG PYTORCH="1.6.0" -ARG CUDA="10.1" -ARG CUDNN="7" -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ARG MMCV="1.3.17" -ARG MMDET="2.25.0" - -ENV PYTHONUNBUFFERED TRUE - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ - ca-certificates \ - g++ \ - openjdk-11-jre-headless \ - # MMDet Requirements - ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 \ - && rm -rf /var/lib/apt/lists/* - -ENV PATH="/opt/conda/bin:$PATH" -RUN export FORCE_CUDA=1 - -# TORCHSEVER -RUN pip install torchserve torch-model-archiver - -# MMLAB -ARG PYTORCH -ARG CUDA -RUN ["/bin/bash", "-c", "pip install mmcv-full==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA//./}/torch${PYTORCH}/index.html"] -RUN pip install mmdet==${MMDET} - -RUN useradd -m model-server \ - && mkdir -p /home/model-server/tmp - -COPY entrypoint.sh /usr/local/bin/entrypoint.sh - -RUN chmod +x /usr/local/bin/entrypoint.sh \ - && chown -R model-server /home/model-server - -COPY config.properties /home/model-server/config.properties -RUN mkdir /home/model-server/model-store && chown -R model-server /home/model-server/model-store - -EXPOSE 8080 8081 8082 - -USER model-server -WORKDIR /home/model-server -ENV TEMP=/home/model-server/tmp -ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] -CMD ["serve"] diff --git a/cv/detection/autoassign/pytorch/docker/serve/config.properties b/cv/detection/autoassign/pytorch/docker/serve/config.properties deleted file mode 100755 index dd9a68515..000000000 --- a/cv/detection/autoassign/pytorch/docker/serve/config.properties +++ /dev/null @@ -1,5 +0,0 @@ -inference_address=http://0.0.0.0:8080 -management_address=http://0.0.0.0:8081 -metrics_address=http://0.0.0.0:8082 -model_store=/home/model-server/model-store -load_models=all diff --git a/cv/detection/autoassign/pytorch/docker/serve/entrypoint.sh b/cv/detection/autoassign/pytorch/docker/serve/entrypoint.sh deleted file mode 100755 index d9aedae68..000000000 --- a/cv/detection/autoassign/pytorch/docker/serve/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - -if [[ "$1" = "serve" ]]; then - shift 1 - torchserve --start --ts-config /home/model-server/config.properties -else - eval "$@" -fi - -# prevent docker exit -tail -f /dev/null diff --git a/cv/detection/autoassign/pytorch/mmcv/__init__.py b/cv/detection/autoassign/pytorch/mmcv/__init__.py deleted file mode 100755 index 87c01b07c..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -from .fileio import * -from .image import * -from .utils import * -from .version import * -# The following modules are not imported to this level, so mmcv may be used -# without PyTorch. -# - runner -# - parallel -# - op diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/__init__.py b/cv/detection/autoassign/pytorch/mmcv/cnn/__init__.py deleted file mode 100755 index 7246c8974..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .alexnet import AlexNet -# yapf: disable -from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, - ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, - ConvTranspose2d, ConvTranspose3d, ConvWS2d, - DepthwiseSeparableConvModule, GeneralizedAttention, - HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, - NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, - build_activation_layer, build_conv_layer, - build_norm_layer, build_padding_layer, build_plugin_layer, - build_upsample_layer, conv_ws_2d, is_norm) -from .builder import MODELS, build_model_from_cfg -# yapf: enable -from .resnet import ResNet, make_res_layer -from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, - NormalInit, PretrainedInit, TruncNormalInit, UniformInit, - XavierInit, bias_init_with_prob, caffe2_xavier_init, - constant_init, fuse_conv_bn, get_model_complexity_info, - initialize, kaiming_init, normal_init, trunc_normal_init, - uniform_init, xavier_init) -from .vgg import VGG, make_vgg_layer - -__all__ = [ - 'AlexNet', 'VGG', 'make_vgg_layer', 'ResNet', 'make_res_layer', - 'constant_init', 'xavier_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'kaiming_init', 'caffe2_xavier_init', - 'bias_init_with_prob', 'ConvModule', 'build_activation_layer', - 'build_conv_layer', 'build_norm_layer', 'build_padding_layer', - 'build_upsample_layer', 'build_plugin_layer', 'is_norm', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'HSigmoid', 'Swish', 'HSwish', - 'GeneralizedAttention', 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', - 'PADDING_LAYERS', 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', - 'get_model_complexity_info', 'conv_ws_2d', 'ConvAWS2d', 'ConvWS2d', - 'fuse_conv_bn', 'DepthwiseSeparableConvModule', 'Linear', 'Conv2d', - 'ConvTranspose2d', 'MaxPool2d', 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', - 'initialize', 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'MODELS', 'build_model_from_cfg' -] diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/alexnet.py b/cv/detection/autoassign/pytorch/mmcv/cnn/alexnet.py deleted file mode 100755 index 89e36b8c7..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/alexnet.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn - - -class AlexNet(nn.Module): - """AlexNet backbone. - - Args: - num_classes (int): number of classes for classification. - """ - - def __init__(self, num_classes=-1): - super(AlexNet, self).__init__() - self.num_classes = num_classes - self.features = nn.Sequential( - nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - nn.Conv2d(64, 192, kernel_size=5, padding=2), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - nn.Conv2d(192, 384, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.Conv2d(384, 256, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.Conv2d(256, 256, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - ) - if self.num_classes > 0: - self.classifier = nn.Sequential( - nn.Dropout(), - nn.Linear(256 * 6 * 6, 4096), - nn.ReLU(inplace=True), - nn.Dropout(), - nn.Linear(4096, 4096), - nn.ReLU(inplace=True), - nn.Linear(4096, num_classes), - ) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - # use default initializer - pass - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - - x = self.features(x) - if self.num_classes > 0: - x = x.view(x.size(0), 256 * 6 * 6) - x = self.classifier(x) - - return x diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/__init__.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/__init__.py deleted file mode 100755 index 0f33124ed..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .activation import build_activation_layer -from .context_block import ContextBlock -from .conv import build_conv_layer -from .conv2d_adaptive_padding import Conv2dAdaptivePadding -from .conv_module import ConvModule -from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d -from .depthwise_separable_conv_module import DepthwiseSeparableConvModule -from .drop import Dropout, DropPath -from .generalized_attention import GeneralizedAttention -from .hsigmoid import HSigmoid -from .hswish import HSwish -from .non_local import NonLocal1d, NonLocal2d, NonLocal3d -from .norm import build_norm_layer, is_norm -from .padding import build_padding_layer -from .plugin import build_plugin_layer -from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) -from .scale import Scale -from .swish import Swish -from .upsample import build_upsample_layer -from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, - Linear, MaxPool2d, MaxPool3d) - -__all__ = [ - 'ConvModule', 'build_activation_layer', 'build_conv_layer', - 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', - 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', - 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', - 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', - 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', - 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', - 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' -] diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/activation.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/activation.py deleted file mode 100755 index 79f198838..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/activation.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version -from .registry import ACTIVATION_LAYERS - -for module in [ - nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, - nn.Sigmoid, nn.Tanh -]: - ACTIVATION_LAYERS.register_module(module=module) - - -@ACTIVATION_LAYERS.register_module(name='Clip') -@ACTIVATION_LAYERS.register_module() -class Clamp(nn.Module): - """Clamp activation layer. - - This activation function is to clamp the feature map value within - :math:`[min, max]`. More details can be found in ``torch.clamp()``. - - Args: - min (Number | optional): Lower-bound of the range to be clamped to. - Default to -1. - max (Number | optional): Upper-bound of the range to be clamped to. - Default to 1. - """ - - def __init__(self, min=-1., max=1.): - super(Clamp, self).__init__() - self.min = min - self.max = max - - def forward(self, x): - """Forward function. - - Args: - x (torch.Tensor): The input tensor. - - Returns: - torch.Tensor: Clamped tensor. - """ - return torch.clamp(x, min=self.min, max=self.max) - - -class GELU(nn.Module): - r"""Applies the Gaussian Error Linear Units function: - - .. math:: - \text{GELU}(x) = x * \Phi(x) - where :math:`\Phi(x)` is the Cumulative Distribution Function for - Gaussian Distribution. - - Shape: - - Input: :math:`(N, *)` where `*` means, any number of additional - dimensions - - Output: :math:`(N, *)`, same shape as the input - - .. image:: scripts/activation_images/GELU.png - - Examples:: - - >>> m = nn.GELU() - >>> input = torch.randn(2) - >>> output = m(input) - """ - - def forward(self, input): - return F.gelu(input) - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.4')): - ACTIVATION_LAYERS.register_module(module=GELU) -else: - ACTIVATION_LAYERS.register_module(module=nn.GELU) - - -def build_activation_layer(cfg): - """Build activation layer. - - Args: - cfg (dict): The activation layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an activation layer. - - Returns: - nn.Module: Created activation layer. - """ - return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/context_block.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/context_block.py deleted file mode 100755 index d60fdb904..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/context_block.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn - -from ..utils import constant_init, kaiming_init -from .registry import PLUGIN_LAYERS - - -def last_zero_init(m): - if isinstance(m, nn.Sequential): - constant_init(m[-1], val=0) - else: - constant_init(m, val=0) - - -@PLUGIN_LAYERS.register_module() -class ContextBlock(nn.Module): - """ContextBlock module in GCNet. - - See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' - (https://arxiv.org/abs/1904.11492) for details. - - Args: - in_channels (int): Channels of the input feature map. - ratio (float): Ratio of channels of transform bottleneck - pooling_type (str): Pooling method for context modeling. - Options are 'att' and 'avg', stand for attention pooling and - average pooling respectively. Default: 'att'. - fusion_types (Sequence[str]): Fusion method for feature fusion, - Options are 'channels_add', 'channel_mul', stand for channelwise - addition and multiplication respectively. Default: ('channel_add',) - """ - - _abbr_ = 'context_block' - - def __init__(self, - in_channels, - ratio, - pooling_type='att', - fusion_types=('channel_add', )): - super(ContextBlock, self).__init__() - assert pooling_type in ['avg', 'att'] - assert isinstance(fusion_types, (list, tuple)) - valid_fusion_types = ['channel_add', 'channel_mul'] - assert all([f in valid_fusion_types for f in fusion_types]) - assert len(fusion_types) > 0, 'at least one fusion should be used' - self.in_channels = in_channels - self.ratio = ratio - self.planes = int(in_channels * ratio) - self.pooling_type = pooling_type - self.fusion_types = fusion_types - if pooling_type == 'att': - self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) - self.softmax = nn.Softmax(dim=2) - else: - self.avg_pool = nn.AdaptiveAvgPool2d(1) - if 'channel_add' in fusion_types: - self.channel_add_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_add_conv = None - if 'channel_mul' in fusion_types: - self.channel_mul_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_mul_conv = None - self.reset_parameters() - - def reset_parameters(self): - if self.pooling_type == 'att': - kaiming_init(self.conv_mask, mode='fan_in') - self.conv_mask.inited = True - - if self.channel_add_conv is not None: - last_zero_init(self.channel_add_conv) - if self.channel_mul_conv is not None: - last_zero_init(self.channel_mul_conv) - - def spatial_pool(self, x): - batch, channel, height, width = x.size() - if self.pooling_type == 'att': - input_x = x - # [N, C, H * W] - input_x = input_x.view(batch, channel, height * width) - # [N, 1, C, H * W] - input_x = input_x.unsqueeze(1) - # [N, 1, H, W] - context_mask = self.conv_mask(x) - # [N, 1, H * W] - context_mask = context_mask.view(batch, 1, height * width) - # [N, 1, H * W] - context_mask = self.softmax(context_mask) - # [N, 1, H * W, 1] - context_mask = context_mask.unsqueeze(-1) - # [N, 1, C, 1] - context = torch.matmul(input_x, context_mask) - # [N, C, 1, 1] - context = context.view(batch, channel, 1, 1) - else: - # [N, C, 1, 1] - context = self.avg_pool(x) - - return context - - def forward(self, x): - # [N, C, 1, 1] - context = self.spatial_pool(x) - - out = x - if self.channel_mul_conv is not None: - # [N, C, 1, 1] - channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) - out = out * channel_mul_term - if self.channel_add_conv is not None: - # [N, C, 1, 1] - channel_add_term = self.channel_add_conv(context) - out = out + channel_add_term - - return out diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv.py deleted file mode 100755 index cf5449199..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from .registry import CONV_LAYERS - -CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) -CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) -CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) -CONV_LAYERS.register_module('Conv', module=nn.Conv2d) - - -def build_conv_layer(cfg, *args, **kwargs): - """Build convolution layer. - - Args: - cfg (None or dict): The conv layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an conv layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding conv layer. - - Returns: - nn.Module: Created conv layer. - """ - if cfg is None: - cfg_ = dict(type='Conv2d') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in CONV_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - else: - conv_layer = CONV_LAYERS.get(layer_type) - - layer = conv_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py deleted file mode 100755 index b45e758ac..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from torch import nn -from torch.nn import functional as F - -from .registry import CONV_LAYERS - - -@CONV_LAYERS.register_module() -class Conv2dAdaptivePadding(nn.Conv2d): - """Implementation of 2D convolution in tensorflow with `padding` as "same", - which applies padding to input (if needed) so that input image gets fully - covered by filter and stride you specified. For stride 1, this will ensure - that output image size is same as input. For stride of 2, output dimensions - will be half, for example. - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the convolving kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If ``True``, adds a learnable bias to the - output. Default: ``True`` - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__(in_channels, out_channels, kernel_size, stride, 0, - dilation, groups, bias) - - def forward(self, x): - img_h, img_w = x.size()[-2:] - kernel_h, kernel_w = self.weight.size()[-2:] - stride_h, stride_w = self.stride - output_h = math.ceil(img_h / stride_h) - output_w = math.ceil(img_w / stride_w) - pad_h = ( - max((output_h - 1) * self.stride[0] + - (kernel_h - 1) * self.dilation[0] + 1 - img_h, 0)) - pad_w = ( - max((output_w - 1) * self.stride[1] + - (kernel_w - 1) * self.dilation[1] + 1 - img_w, 0)) - if pad_h > 0 or pad_w > 0: - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2 - ]) - return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv_module.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv_module.py deleted file mode 100755 index 4f19f1d0c..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv_module.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from mmcv.utils import _BatchNorm, _InstanceNorm -from ..utils import constant_init, kaiming_init -from .activation import build_activation_layer -from .conv import build_conv_layer -from .norm import build_norm_layer -from .padding import build_padding_layer -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class ConvModule(nn.Module): - """A conv block that bundles conv/norm/activation layers. - - This block simplifies the usage of convolution layers, which are commonly - used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). - It is based upon three build methods: `build_conv_layer()`, - `build_norm_layer()` and `build_activation_layer()`. - - Besides, we add some additional features in this module. - 1. Automatically set `bias` of the conv layer. - 2. Spectral norm is supported. - 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only - supports zero and circular padding, and we add "reflect" padding mode. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. - groups (int): Number of blocked connections from input channels to - output channels. Same as that in ``nn._ConvNd``. - bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - inplace (bool): Whether to use inplace mode for activation. - Default: True. - with_spectral_norm (bool): Whether use spectral norm in conv module. - Default: False. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in PyTorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Common examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - Default: ('conv', 'norm', 'act'). - """ - - _abbr_ = 'conv_block' - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias='auto', - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - padding_mode='zeros', - order=('conv', 'norm', 'act')): - super(ConvModule, self).__init__() - assert conv_cfg is None or isinstance(conv_cfg, dict) - assert norm_cfg is None or isinstance(norm_cfg, dict) - assert act_cfg is None or isinstance(act_cfg, dict) - official_padding_mode = ['zeros', 'circular'] - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.with_explicit_padding = padding_mode not in official_padding_mode - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 3 - assert set(order) == set(['conv', 'norm', 'act']) - - self.with_norm = norm_cfg is not None - self.with_activation = act_cfg is not None - # if the conv layer is before a norm layer, bias is unnecessary. - if bias == 'auto': - bias = not self.with_norm - self.with_bias = bias - - if self.with_explicit_padding: - pad_cfg = dict(type=padding_mode) - self.padding_layer = build_padding_layer(pad_cfg, padding) - - # reset padding to 0 for conv module - conv_padding = 0 if self.with_explicit_padding else padding - # build convolution layer - self.conv = build_conv_layer( - conv_cfg, - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=conv_padding, - dilation=dilation, - groups=groups, - bias=bias) - # export the attributes of self.conv to a higher level for convenience - self.in_channels = self.conv.in_channels - self.out_channels = self.conv.out_channels - self.kernel_size = self.conv.kernel_size - self.stride = self.conv.stride - self.padding = padding - self.dilation = self.conv.dilation - self.transposed = self.conv.transposed - self.output_padding = self.conv.output_padding - self.groups = self.conv.groups - - if self.with_spectral_norm: - self.conv = nn.utils.spectral_norm(self.conv) - - # build normalization layers - if self.with_norm: - # norm layer is after conv layer - if order.index('norm') > order.index('conv'): - norm_channels = out_channels - else: - norm_channels = in_channels - self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) - self.add_module(self.norm_name, norm) - if self.with_bias: - if isinstance(norm, (_BatchNorm, _InstanceNorm)): - warnings.warn( - 'Unnecessary conv bias before batch/instance norm') - else: - self.norm_name = None - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - # nn.Tanh has no 'inplace' argument - if act_cfg_['type'] not in [ - 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish' - ]: - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - @property - def norm(self): - if self.norm_name: - return getattr(self, self.norm_name) - else: - return None - - def init_weights(self): - # 1. It is mainly for customized conv layers with their own - # initialization manners by calling their own ``init_weights()``, - # and we do not want ConvModule to override the initialization. - # 2. For customized conv layers without their own initialization - # manners (that is, they don't have their own ``init_weights()``) - # and PyTorch's conv layers, they will be initialized by - # this method with default ``kaiming_init``. - # Note: For PyTorch's conv layers, they will be overwritten by our - # initialization implementation using default ``kaiming_init``. - if not hasattr(self.conv, 'init_weights'): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) - if self.with_norm: - constant_init(self.norm, 1, bias=0) - - def forward(self, x, activate=True, norm=True): - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - x = self.conv(x) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv_ws.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv_ws.py deleted file mode 100755 index a3941e278..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/conv_ws.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from .registry import CONV_LAYERS - - -def conv_ws_2d(input, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - eps=1e-5): - c_in = weight.size(0) - weight_flat = weight.view(c_in, -1) - mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1) - std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1) - weight = (weight - mean) / (std + eps) - return F.conv2d(input, weight, bias, stride, padding, dilation, groups) - - -@CONV_LAYERS.register_module('ConvWS') -class ConvWS2d(nn.Conv2d): - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True, - eps=1e-5): - super(ConvWS2d, self).__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.eps = eps - - def forward(self, x): - return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups, self.eps) - - -@CONV_LAYERS.register_module(name='ConvAWS') -class ConvAWS2d(nn.Conv2d): - """AWS (Adaptive Weight Standardization) - - This is a variant of Weight Standardization - (https://arxiv.org/pdf/1903.10520.pdf) - It is used in DetectoRS to avoid NaN - (https://arxiv.org/pdf/2006.02334.pdf) - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the conv kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If set True, adds a learnable bias to the - output. Default: True - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.register_buffer('weight_gamma', - torch.ones(self.out_channels, 1, 1, 1)) - self.register_buffer('weight_beta', - torch.zeros(self.out_channels, 1, 1, 1)) - - def _get_weight(self, weight): - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - weight = (weight - mean) / std - weight = self.weight_gamma * weight + self.weight_beta - return weight - - def forward(self, x): - weight = self._get_weight(self.weight) - return F.conv2d(x, weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - """Override default load function. - - AWS overrides the function _load_from_state_dict to recover - weight_gamma and weight_beta if they are missing. If weight_gamma and - weight_beta are found in the checkpoint, this function will return - after super()._load_from_state_dict. Otherwise, it will compute the - mean and std of the pretrained weights and store them in weight_beta - and weight_gamma. - """ - - self.weight_gamma.data.fill_(-1) - local_missing_keys = [] - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, local_missing_keys, - unexpected_keys, error_msgs) - if self.weight_gamma.data.mean() > 0: - for k in local_missing_keys: - missing_keys.append(k) - return - weight = self.weight.data - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - self.weight_beta.data.copy_(mean) - self.weight_gamma.data.copy_(std) - missing_gamma_beta = [ - k for k in local_missing_keys - if k.endswith('weight_gamma') or k.endswith('weight_beta') - ] - for k in missing_gamma_beta: - local_missing_keys.remove(k) - for k in local_missing_keys: - missing_keys.append(k) diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py deleted file mode 100755 index 722d5d8d7..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .conv_module import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if `norm_cfg` and `act_cfg` are specified. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. Default: 1. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. Default: 0. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super(DepthwiseSeparableConvModule, self).__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/drop.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/drop.py deleted file mode 100755 index b0a026654..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/drop.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from mmcv import build_from_cfg -from .registry import DROPOUT_LAYERS - - -def drop_path(x, drop_prob=0., training=False): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - """ - if drop_prob == 0. or not training: - return x - keep_prob = 1 - drop_prob - # handle tensors with different dimensions, not just 4D tensors. - shape = (x.shape[0], ) + (1, ) * (x.ndim - 1) - random_tensor = keep_prob + torch.rand( - shape, dtype=x.dtype, device=x.device) - output = x.div(keep_prob) * random_tensor.floor() - return output - - -@DROPOUT_LAYERS.register_module() -class DropPath(nn.Module): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - - Args: - drop_prob (float): Probability of the path to be zeroed. Default: 0.1 - """ - - def __init__(self, drop_prob=0.1): - super(DropPath, self).__init__() - self.drop_prob = drop_prob - - def forward(self, x): - return drop_path(x, self.drop_prob, self.training) - - -@DROPOUT_LAYERS.register_module() -class Dropout(nn.Dropout): - """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of - ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with - ``DropPath`` - - Args: - drop_prob (float): Probability of the elements to be - zeroed. Default: 0.5. - inplace (bool): Do the operation inplace or not. Default: False. - """ - - def __init__(self, drop_prob=0.5, inplace=False): - super().__init__(p=drop_prob, inplace=inplace) - - -def build_dropout(cfg, default_args=None): - """Builder for drop out layers.""" - return build_from_cfg(cfg, DROPOUT_LAYERS, default_args) diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/generalized_attention.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/generalized_attention.py deleted file mode 100755 index 988d9adf2..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/generalized_attention.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import kaiming_init -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class GeneralizedAttention(nn.Module): - """GeneralizedAttention module. - - See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' - (https://arxiv.org/abs/1711.07971) for details. - - Args: - in_channels (int): Channels of the input feature map. - spatial_range (int): The spatial range. -1 indicates no spatial range - constraint. Default: -1. - num_heads (int): The head number of empirical_attention module. - Default: 9. - position_embedding_dim (int): The position embedding dimension. - Default: -1. - position_magnitude (int): A multiplier acting on coord difference. - Default: 1. - kv_stride (int): The feature stride acting on key/value feature map. - Default: 2. - q_stride (int): The feature stride acting on query feature map. - Default: 1. - attention_type (str): A binary indicator string for indicating which - items in generalized empirical_attention module are used. - Default: '1111'. - - - '1000' indicates 'query and key content' (appr - appr) item, - - '0100' indicates 'query content and relative position' - (appr - position) item, - - '0010' indicates 'key content only' (bias - appr) item, - - '0001' indicates 'relative position only' (bias - position) item. - """ - - _abbr_ = 'gen_attention_block' - - def __init__(self, - in_channels, - spatial_range=-1, - num_heads=9, - position_embedding_dim=-1, - position_magnitude=1, - kv_stride=2, - q_stride=1, - attention_type='1111'): - - super(GeneralizedAttention, self).__init__() - - # hard range means local range for non-local operation - self.position_embedding_dim = ( - position_embedding_dim - if position_embedding_dim > 0 else in_channels) - - self.position_magnitude = position_magnitude - self.num_heads = num_heads - self.in_channels = in_channels - self.spatial_range = spatial_range - self.kv_stride = kv_stride - self.q_stride = q_stride - self.attention_type = [bool(int(_)) for _ in attention_type] - self.qk_embed_dim = in_channels // num_heads - out_c = self.qk_embed_dim * num_heads - - if self.attention_type[0] or self.attention_type[1]: - self.query_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.query_conv.kaiming_init = True - - if self.attention_type[0] or self.attention_type[2]: - self.key_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.key_conv.kaiming_init = True - - self.v_dim = in_channels // num_heads - self.value_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=self.v_dim * num_heads, - kernel_size=1, - bias=False) - self.value_conv.kaiming_init = True - - if self.attention_type[1] or self.attention_type[3]: - self.appr_geom_fc_x = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_x.kaiming_init = True - - self.appr_geom_fc_y = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_y.kaiming_init = True - - if self.attention_type[2]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.appr_bias = nn.Parameter(appr_bias_value) - - if self.attention_type[3]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.geom_bias = nn.Parameter(geom_bias_value) - - self.proj_conv = nn.Conv2d( - in_channels=self.v_dim * num_heads, - out_channels=in_channels, - kernel_size=1, - bias=True) - self.proj_conv.kaiming_init = True - self.gamma = nn.Parameter(torch.zeros(1)) - - if self.spatial_range >= 0: - # only works when non local is after 3*3 conv - if in_channels == 256: - max_len = 84 - elif in_channels == 512: - max_len = 42 - - max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) - local_constraint_map = np.ones( - (max_len, max_len, max_len_kv, max_len_kv), dtype=np.int) - for iy in range(max_len): - for ix in range(max_len): - local_constraint_map[ - iy, ix, - max((iy - self.spatial_range) // - self.kv_stride, 0):min((iy + self.spatial_range + - 1) // self.kv_stride + - 1, max_len), - max((ix - self.spatial_range) // - self.kv_stride, 0):min((ix + self.spatial_range + - 1) // self.kv_stride + - 1, max_len)] = 0 - - self.local_constraint_map = nn.Parameter( - torch.from_numpy(local_constraint_map).byte(), - requires_grad=False) - - if self.q_stride > 1: - self.q_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.q_stride) - else: - self.q_downsample = None - - if self.kv_stride > 1: - self.kv_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.kv_stride) - else: - self.kv_downsample = None - - self.init_weights() - - def get_position_embedding(self, - h, - w, - h_kv, - w_kv, - q_stride, - kv_stride, - device, - dtype, - feat_dim, - wave_length=1000): - # the default type of Tensor is float32, leading to type mismatch - # in fp16 mode. Cast it to support fp16 mode. - h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) - h_idxs = h_idxs.view((h, 1)) * q_stride - - w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) - w_idxs = w_idxs.view((w, 1)) * q_stride - - h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( - device=device, dtype=dtype) - h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride - - w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( - device=device, dtype=dtype) - w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride - - # (h, h_kv, 1) - h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) - h_diff *= self.position_magnitude - - # (w, w_kv, 1) - w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) - w_diff *= self.position_magnitude - - feat_range = torch.arange(0, feat_dim / 4).to( - device=device, dtype=dtype) - - dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) - dim_mat = dim_mat**((4. / feat_dim) * feat_range) - dim_mat = dim_mat.view((1, 1, -1)) - - embedding_x = torch.cat( - ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) - - embedding_y = torch.cat( - ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) - - return embedding_x, embedding_y - - def forward(self, x_input): - num_heads = self.num_heads - - # use empirical_attention - if self.q_downsample is not None: - x_q = self.q_downsample(x_input) - else: - x_q = x_input - n, _, h, w = x_q.shape - - if self.kv_downsample is not None: - x_kv = self.kv_downsample(x_input) - else: - x_kv = x_input - _, _, h_kv, w_kv = x_kv.shape - - if self.attention_type[0] or self.attention_type[1]: - proj_query = self.query_conv(x_q).view( - (n, num_heads, self.qk_embed_dim, h * w)) - proj_query = proj_query.permute(0, 1, 3, 2) - - if self.attention_type[0] or self.attention_type[2]: - proj_key = self.key_conv(x_kv).view( - (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) - - if self.attention_type[1] or self.attention_type[3]: - position_embed_x, position_embed_y = self.get_position_embedding( - h, w, h_kv, w_kv, self.q_stride, self.kv_stride, - x_input.device, x_input.dtype, self.position_embedding_dim) - # (n, num_heads, w, w_kv, dim) - position_feat_x = self.appr_geom_fc_x(position_embed_x).\ - view(1, w, w_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - # (n, num_heads, h, h_kv, dim) - position_feat_y = self.appr_geom_fc_y(position_embed_y).\ - view(1, h, h_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - position_feat_x /= math.sqrt(2) - position_feat_y /= math.sqrt(2) - - # accelerate for saliency only - if (np.sum(self.attention_type) == 1) and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy = torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, h_kv * w_kv) - - h = 1 - w = 1 - else: - # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for - if not self.attention_type[0]: - energy = torch.zeros( - n, - num_heads, - h, - w, - h_kv, - w_kv, - dtype=x_input.dtype, - device=x_input.device) - - # attention_type[0]: appr - appr - # attention_type[1]: appr - position - # attention_type[2]: bias - appr - # attention_type[3]: bias - position - if self.attention_type[0] or self.attention_type[2]: - if self.attention_type[0] and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - energy = torch.matmul(proj_query + appr_bias, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[0]: - energy = torch.matmul(proj_query, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy += torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, 1, h_kv, w_kv) - - if self.attention_type[1] or self.attention_type[3]: - if self.attention_type[1] and self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - - proj_query_reshape = (proj_query + geom_bias).\ - view(n, num_heads, h, w, self.qk_embed_dim) - - energy_x = torch.matmul( - proj_query_reshape.permute(0, 1, 3, 2, 4), - position_feat_x.permute(0, 1, 2, 4, 3)) - energy_x = energy_x.\ - permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul( - proj_query_reshape, - position_feat_y.permute(0, 1, 2, 4, 3)) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[1]: - proj_query_reshape = proj_query.\ - view(n, num_heads, h, w, self.qk_embed_dim) - proj_query_reshape = proj_query_reshape.\ - permute(0, 1, 3, 2, 4) - position_feat_x_reshape = position_feat_x.\ - permute(0, 1, 2, 4, 3) - position_feat_y_reshape = position_feat_y.\ - permute(0, 1, 2, 4, 3) - - energy_x = torch.matmul(proj_query_reshape, - position_feat_x_reshape) - energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul(proj_query_reshape, - position_feat_y_reshape) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, self.qk_embed_dim, 1).\ - repeat(n, 1, 1, 1) - - position_feat_x_reshape = position_feat_x.\ - view(n, num_heads, w*w_kv, self.qk_embed_dim) - - position_feat_y_reshape = position_feat_y.\ - view(n, num_heads, h * h_kv, self.qk_embed_dim) - - energy_x = torch.matmul(position_feat_x_reshape, geom_bias) - energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) - - energy_y = torch.matmul(position_feat_y_reshape, geom_bias) - energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) - - energy += energy_x + energy_y - - energy = energy.view(n, num_heads, h * w, h_kv * w_kv) - - if self.spatial_range >= 0: - cur_local_constraint_map = \ - self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ - contiguous().\ - view(1, 1, h*w, h_kv*w_kv) - - energy = energy.masked_fill_(cur_local_constraint_map, - float('-inf')) - - attention = F.softmax(energy, 3) - - proj_value = self.value_conv(x_kv) - proj_value_reshape = proj_value.\ - view((n, num_heads, self.v_dim, h_kv * w_kv)).\ - permute(0, 1, 3, 2) - - out = torch.matmul(attention, proj_value_reshape).\ - permute(0, 1, 3, 2).\ - contiguous().\ - view(n, self.v_dim * self.num_heads, h, w) - - out = self.proj_conv(out) - - # output is downsampled, upsample back to input size - if self.q_downsample is not None: - out = F.interpolate( - out, - size=x_input.shape[2:], - mode='bilinear', - align_corners=False) - - out = self.gamma * out + x_input - return out - - def init_weights(self): - for m in self.modules(): - if hasattr(m, 'kaiming_init') and m.kaiming_init: - kaiming_init( - m, - mode='fan_in', - nonlinearity='leaky_relu', - bias=0, - distribution='uniform', - a=1) diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/hsigmoid.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/hsigmoid.py deleted file mode 100755 index 30b1a3d65..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/hsigmoid.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSigmoid(nn.Module): - """Hard Sigmoid Module. Apply the hard sigmoid function: - Hsigmoid(x) = min(max((x + bias) / divisor, min_value), max_value) - Default: Hsigmoid(x) = min(max((x + 1) / 2, 0), 1) - - Args: - bias (float): Bias of the input feature map. Default: 1.0. - divisor (float): Divisor of the input feature map. Default: 2.0. - min_value (float): Lower bound value. Default: 0.0. - max_value (float): Upper bound value. Default: 1.0. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, bias=1.0, divisor=2.0, min_value=0.0, max_value=1.0): - super(HSigmoid, self).__init__() - self.bias = bias - self.divisor = divisor - assert self.divisor != 0 - self.min_value = min_value - self.max_value = max_value - - def forward(self, x): - x = (x + self.bias) / self.divisor - - return x.clamp_(self.min_value, self.max_value) diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/hswish.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/hswish.py deleted file mode 100755 index 7e0c090ff..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/hswish.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSwish(nn.Module): - """Hard Swish Module. - - This module applies the hard swish function: - - .. math:: - Hswish(x) = x * ReLU6(x + 3) / 6 - - Args: - inplace (bool): can optionally do the operation in-place. - Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.act = nn.ReLU6(inplace) - - def forward(self, x): - return x * self.act(x + 3) / 6 diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/non_local.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/non_local.py deleted file mode 100755 index 92d00155e..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/non_local.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta - -import torch -import torch.nn as nn - -from ..utils import constant_init, normal_init -from .conv_module import ConvModule -from .registry import PLUGIN_LAYERS - - -class _NonLocalNd(nn.Module, metaclass=ABCMeta): - """Basic Non-local module. - - This module is proposed in - "Non-local Neural Networks" - Paper reference: https://arxiv.org/abs/1711.07971 - Code reference: https://github.com/AlexHex7/Non-local_pytorch - - Args: - in_channels (int): Channels of the input feature map. - reduction (int): Channel reduction ratio. Default: 2. - use_scale (bool): Whether to scale pairwise_weight by - `1/sqrt(inter_channels)` when the mode is `embedded_gaussian`. - Default: True. - conv_cfg (None | dict): The config dict for convolution layers. - If not specified, it will use `nn.Conv2d` for convolution layers. - Default: None. - norm_cfg (None | dict): The config dict for normalization layers. - Default: None. (This parameter is only applicable to conv_out.) - mode (str): Options are `gaussian`, `concatenation`, - `embedded_gaussian` and `dot_product`. Default: embedded_gaussian. - """ - - def __init__(self, - in_channels, - reduction=2, - use_scale=True, - conv_cfg=None, - norm_cfg=None, - mode='embedded_gaussian', - **kwargs): - super(_NonLocalNd, self).__init__() - self.in_channels = in_channels - self.reduction = reduction - self.use_scale = use_scale - self.inter_channels = max(in_channels // reduction, 1) - self.mode = mode - - if mode not in [ - 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation' - ]: - raise ValueError("Mode should be in 'gaussian', 'concatenation', " - f"'embedded_gaussian' or 'dot_product', but got " - f'{mode} instead.') - - # g, theta, phi are defaulted as `nn.ConvNd`. - # Here we use ConvModule for potential usage. - self.g = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.conv_out = ConvModule( - self.inter_channels, - self.in_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - if self.mode != 'gaussian': - self.theta = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.phi = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - - if self.mode == 'concatenation': - self.concat_project = ConvModule( - self.inter_channels * 2, - 1, - kernel_size=1, - stride=1, - padding=0, - bias=False, - act_cfg=dict(type='ReLU')) - - self.init_weights(**kwargs) - - def init_weights(self, std=0.01, zeros_init=True): - if self.mode != 'gaussian': - for m in [self.g, self.theta, self.phi]: - normal_init(m.conv, std=std) - else: - normal_init(self.g.conv, std=std) - if zeros_init: - if self.conv_out.norm_cfg is None: - constant_init(self.conv_out.conv, 0) - else: - constant_init(self.conv_out.norm, 0) - else: - if self.conv_out.norm_cfg is None: - normal_init(self.conv_out.conv, std=std) - else: - normal_init(self.conv_out.norm, std=std) - - def gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def embedded_gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - if self.use_scale: - # theta_x.shape[-1] is `self.inter_channels` - pairwise_weight /= theta_x.shape[-1]**0.5 - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def dot_product(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight /= pairwise_weight.shape[-1] - return pairwise_weight - - def concatenation(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - h = theta_x.size(2) - w = phi_x.size(3) - theta_x = theta_x.repeat(1, 1, 1, w) - phi_x = phi_x.repeat(1, 1, h, 1) - - concat_feature = torch.cat([theta_x, phi_x], dim=1) - pairwise_weight = self.concat_project(concat_feature) - n, _, h, w = pairwise_weight.size() - pairwise_weight = pairwise_weight.view(n, h, w) - pairwise_weight /= pairwise_weight.shape[-1] - - return pairwise_weight - - def forward(self, x): - # Assume `reduction = 1`, then `inter_channels = C` - # or `inter_channels = C` when `mode="gaussian"` - - # NonLocal1d x: [N, C, H] - # NonLocal2d x: [N, C, H, W] - # NonLocal3d x: [N, C, T, H, W] - n = x.size(0) - - # NonLocal1d g_x: [N, H, C] - # NonLocal2d g_x: [N, HxW, C] - # NonLocal3d g_x: [N, TxHxW, C] - g_x = self.g(x).view(n, self.inter_channels, -1) - g_x = g_x.permute(0, 2, 1) - - # NonLocal1d theta_x: [N, H, C], phi_x: [N, C, H] - # NonLocal2d theta_x: [N, HxW, C], phi_x: [N, C, HxW] - # NonLocal3d theta_x: [N, TxHxW, C], phi_x: [N, C, TxHxW] - if self.mode == 'gaussian': - theta_x = x.view(n, self.in_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - if self.sub_sample: - phi_x = self.phi(x).view(n, self.in_channels, -1) - else: - phi_x = x.view(n, self.in_channels, -1) - elif self.mode == 'concatenation': - theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) - phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) - else: - theta_x = self.theta(x).view(n, self.inter_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - phi_x = self.phi(x).view(n, self.inter_channels, -1) - - pairwise_func = getattr(self, self.mode) - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = pairwise_func(theta_x, phi_x) - - # NonLocal1d y: [N, H, C] - # NonLocal2d y: [N, HxW, C] - # NonLocal3d y: [N, TxHxW, C] - y = torch.matmul(pairwise_weight, g_x) - # NonLocal1d y: [N, C, H] - # NonLocal2d y: [N, C, H, W] - # NonLocal3d y: [N, C, T, H, W] - y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, - *x.size()[2:]) - - output = x + self.conv_out(y) - - return output - - -class NonLocal1d(_NonLocalNd): - """1D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv1d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv1d'), - **kwargs): - super(NonLocal1d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool1d(kernel_size=2) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -@PLUGIN_LAYERS.register_module() -class NonLocal2d(_NonLocalNd): - """2D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv2d'). - """ - - _abbr_ = 'nonlocal_block' - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv2d'), - **kwargs): - super(NonLocal2d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -class NonLocal3d(_NonLocalNd): - """3D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv3d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv3d'), - **kwargs): - super(NonLocal3d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/norm.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/norm.py deleted file mode 100755 index cfb326bdb..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/norm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import torch.nn as nn - -from mmcv.utils import is_tuple_of -from mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm -from .registry import NORM_LAYERS - -NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) -NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) -NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) -NORM_LAYERS.register_module('GN', module=nn.GroupNorm) -NORM_LAYERS.register_module('LN', module=nn.LayerNorm) -NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) -NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - When we build a norm layer with `build_norm_layer()`, we want to preserve - the norm type in variable names, e.g, self.bn1, self.gn. This method will - infer the abbreviation to map class types to abbreviations. - - Rule 1: If the class has the property "_abbr_", return the property. - Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or - InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and - "in" respectively. - Rule 3: If the class name contains "batch", "group", "layer" or "instance", - the abbreviation of this layer will be "bn", "gn", "ln" and "in" - respectively. - Rule 4: Otherwise, the abbreviation falls back to "norm". - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN - return 'in' - elif issubclass(class_type, _BatchNorm): - return 'bn' - elif issubclass(class_type, nn.GroupNorm): - return 'gn' - elif issubclass(class_type, nn.LayerNorm): - return 'ln' - else: - class_name = class_type.__name__.lower() - if 'batch' in class_name: - return 'bn' - elif 'group' in class_name: - return 'gn' - elif 'layer' in class_name: - return 'ln' - elif 'instance' in class_name: - return 'in' - else: - return 'norm_layer' - - -def build_norm_layer(cfg, num_features, postfix=''): - """Build normalization layer. - - Args: - cfg (dict): The norm layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate a norm layer. - - requires_grad (bool, optional): Whether stop gradient updates. - num_features (int): Number of input channels. - postfix (int | str): The postfix to be appended into norm abbreviation - to create named layer. - - Returns: - (str, nn.Module): The first element is the layer name consisting of - abbreviation and postfix, e.g., bn1, gn. The second element is the - created norm layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in NORM_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - - norm_layer = NORM_LAYERS.get(layer_type) - abbr = infer_abbr(norm_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - requires_grad = cfg_.pop('requires_grad', True) - cfg_.setdefault('eps', 1e-5) - if layer_type != 'GN': - layer = norm_layer(num_features, **cfg_) - if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): - layer._specify_ddp_gpu_num(1) - else: - assert 'num_groups' in cfg_ - layer = norm_layer(num_channels=num_features, **cfg_) - - for param in layer.parameters(): - param.requires_grad = requires_grad - - return name, layer - - -def is_norm(layer, exclude=None): - """Check if a layer is a normalization layer. - - Args: - layer (nn.Module): The layer to be checked. - exclude (type | tuple[type]): Types to be excluded. - - Returns: - bool: Whether the layer is a norm layer. - """ - if exclude is not None: - if not isinstance(exclude, tuple): - exclude = (exclude, ) - if not is_tuple_of(exclude, type): - raise TypeError( - f'"exclude" must be either None or type or a tuple of types, ' - f'but got {type(exclude)}: {exclude}') - - if exclude and isinstance(layer, exclude): - return False - - all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) - return isinstance(layer, all_norm_bases) diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/padding.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/padding.py deleted file mode 100755 index e4ac6b28a..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/padding.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import PADDING_LAYERS - -PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) -PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) -PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) - - -def build_padding_layer(cfg, *args, **kwargs): - """Build padding layer. - - Args: - cfg (None or dict): The padding layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate a padding layer. - - Returns: - nn.Module: Created padding layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - - cfg_ = cfg.copy() - padding_type = cfg_.pop('type') - if padding_type not in PADDING_LAYERS: - raise KeyError(f'Unrecognized padding type {padding_type}.') - else: - padding_layer = PADDING_LAYERS.get(padding_type) - - layer = padding_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/plugin.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/plugin.py deleted file mode 100755 index 07c010d40..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/plugin.py +++ /dev/null @@ -1,88 +0,0 @@ -import inspect -import platform - -from .registry import PLUGIN_LAYERS - -if platform.system() == 'Windows': - import regex as re -else: - import re - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - This method will infer the abbreviation to map class types to - abbreviations. - - Rule 1: If the class has the property "abbr", return the property. - Rule 2: Otherwise, the abbreviation falls back to snake case of class - name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - - def camel2snack(word): - """Convert camel case word into snack case. - - Modified from `inflection lib - `_. - - Example:: - - >>> camel2snack("FancyBlock") - 'fancy_block' - """ - - word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) - word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) - word = word.replace('-', '_') - return word.lower() - - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - else: - return camel2snack(class_type.__name__) - - -def build_plugin_layer(cfg, postfix='', **kwargs): - """Build plugin layer. - - Args: - cfg (None or dict): cfg should contain: - type (str): identify plugin layer type. - layer args: args needed to instantiate a plugin layer. - postfix (int, str): appended into norm abbreviation to - create named layer. Default: ''. - - Returns: - tuple[str, nn.Module]: - name (str): abbreviation + postfix - layer (nn.Module): created plugin layer - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in PLUGIN_LAYERS: - raise KeyError(f'Unrecognized plugin type {layer_type}') - - plugin_layer = PLUGIN_LAYERS.get(layer_type) - abbr = infer_abbr(plugin_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - layer = plugin_layer(**kwargs, **cfg_) - - return name, layer diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/registry.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/registry.py deleted file mode 100755 index c29279776..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/registry.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -CONV_LAYERS = Registry('conv layer') -NORM_LAYERS = Registry('norm layer') -ACTIVATION_LAYERS = Registry('activation layer') -PADDING_LAYERS = Registry('padding layer') -UPSAMPLE_LAYERS = Registry('upsample layer') -PLUGIN_LAYERS = Registry('plugin layer') - -DROPOUT_LAYERS = Registry('drop out layers') -POSITIONAL_ENCODING = Registry('position encoding') -ATTENTION = Registry('attention') -FEEDFORWARD_NETWORK = Registry('feed-forward Network') -TRANSFORMER_LAYER = Registry('transformerLayer') -TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/scale.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/scale.py deleted file mode 100755 index c905fffcc..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/scale.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class Scale(nn.Module): - """A learnable scale parameter. - - This layer scales the input by a learnable factor. It multiplies a - learnable scale parameter of shape (1,) with input of any shape. - - Args: - scale (float): Initial value of scale factor. Default: 1.0 - """ - - def __init__(self, scale=1.0): - super(Scale, self).__init__() - self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) - - def forward(self, x): - return x * self.scale diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/swish.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/swish.py deleted file mode 100755 index e2ca8ed7b..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/swish.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class Swish(nn.Module): - """Swish Module. - - This module applies the swish function: - - .. math:: - Swish(x) = x * Sigmoid(x) - - Returns: - Tensor: The output tensor. - """ - - def __init__(self): - super(Swish, self).__init__() - - def forward(self, x): - return x * torch.sigmoid(x) diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/transformer.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/transformer.py deleted file mode 100755 index ed32688af..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/transformer.py +++ /dev/null @@ -1,595 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings - -import torch -import torch.nn as nn - -from mmcv import ConfigDict, deprecated_api_warning -from mmcv.cnn import Linear, build_activation_layer, build_norm_layer -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential -from mmcv.utils import build_from_cfg -from .drop import build_dropout -from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, - TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) - -# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file -try: - from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention # noqa F401 - warnings.warn( - ImportWarning( - '``MultiScaleDeformableAttention`` has been moved to ' - '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 - '``from mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 - 'to ``from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 - )) - -except ImportError: - warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' - '``mmcv.ops.multi_scale_deform_attn``, ' - 'You should install ``mmcv-full`` if you need this module. ') - - -def build_positional_encoding(cfg, default_args=None): - """Builder for Position Encoding.""" - return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) - - -def build_attention(cfg, default_args=None): - """Builder for attention.""" - return build_from_cfg(cfg, ATTENTION, default_args) - - -def build_feedforward_network(cfg, default_args=None): - """Builder for feed-forward network (FFN).""" - return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) - - -def build_transformer_layer(cfg, default_args=None): - """Builder for transformer layer.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) - - -def build_transformer_layer_sequence(cfg, default_args=None): - """Builder for transformer encoder and transformer decoder.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) - - -@ATTENTION.register_module() -class MultiheadAttention(BaseModule): - """A wrapper for ``torch.nn.MultiheadAttention``. - - This module implements MultiheadAttention with identity connection, - and positional encoding is also passed as input. - - Args: - embed_dims (int): The embedding dimension. - num_heads (int): Parallel attention heads. - attn_drop (float): A Dropout layer on attn_output_weights. - Default: 0.0. - proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. - Default: 0.0. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): When it is True, Key, Query and Value are shape of - (batch, n, embed_dim), otherwise (n, batch, embed_dim). - Default to False. - """ - - def __init__(self, - embed_dims, - num_heads, - attn_drop=0., - proj_drop=0., - dropout_layer=dict(type='Dropout', drop_prob=0.), - init_cfg=None, - batch_first=False, - **kwargs): - super(MultiheadAttention, self).__init__(init_cfg) - if 'dropout' in kwargs: - warnings.warn('The arguments `dropout` in MultiheadAttention ' - 'has been deprecated, now you can separately ' - 'set `attn_drop`(float), proj_drop(float), ' - 'and `dropout_layer`(dict) ') - attn_drop = kwargs['dropout'] - dropout_layer['drop_prob'] = kwargs.pop('dropout') - - self.embed_dims = embed_dims - self.num_heads = num_heads - self.batch_first = batch_first - - self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, - **kwargs) - - self.proj_drop = nn.Dropout(proj_drop) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else nn.Identity() - - @deprecated_api_warning({'residual': 'identity'}, - cls_name='MultiheadAttention') - def forward(self, - query, - key=None, - value=None, - identity=None, - query_pos=None, - key_pos=None, - attn_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `MultiheadAttention`. - - **kwargs allow passing a more general data flow when combining - with other operations in `transformerlayer`. - - Args: - query (Tensor): The input query with shape [num_queries, bs, - embed_dims] if self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - If None, the ``query`` will be used. Defaults to None. - value (Tensor): The value tensor with same shape as `key`. - Same in `nn.MultiheadAttention.forward`. Defaults to None. - If None, the `key` will be used. - identity (Tensor): This tensor, with the same shape as x, - will be used for the identity link. - If None, `x` will be used. Defaults to None. - query_pos (Tensor): The positional encoding for query, with - the same shape as `x`. If not None, it will - be added to `x` before forward function. Defaults to None. - key_pos (Tensor): The positional encoding for `key`, with the - same shape as `key`. Defaults to None. If not None, it will - be added to `key` before forward function. If None, and - `query_pos` has the same shape as `key`, then `query_pos` - will be used for `key_pos`. Defaults to None. - attn_mask (Tensor): ByteTensor mask with shape [num_queries, - num_keys]. Same in `nn.MultiheadAttention.forward`. - Defaults to None. - key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. - Defaults to None. - - Returns: - Tensor: forwarded results with shape - [num_queries, bs, embed_dims] - if self.batch_first is False, else - [bs, num_queries embed_dims]. - """ - - if key is None: - key = query - if value is None: - value = key - if identity is None: - identity = query - if key_pos is None: - if query_pos is not None: - # use query_pos if key_pos is not available - if query_pos.shape == key.shape: - key_pos = query_pos - else: - warnings.warn(f'position encoding of key is' - f'missing in {self.__class__.__name__}.') - if query_pos is not None: - query = query + query_pos - if key_pos is not None: - key = key + key_pos - - # Because the dataflow('key', 'query', 'value') of - # ``torch.nn.MultiheadAttention`` is (num_query, batch, - # embed_dims), We should adjust the shape of dataflow from - # batch_first (batch, num_query, embed_dims) to num_query_first - # (num_query ,batch, embed_dims), and recover ``attn_output`` - # from num_query_first to batch_first. - if self.batch_first: - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - out = self.attn( - query=query, - key=key, - value=value, - attn_mask=attn_mask, - key_padding_mask=key_padding_mask)[0] - - if self.batch_first: - out = out.transpose(0, 1) - - return identity + self.dropout_layer(self.proj_drop(out)) - - -@FEEDFORWARD_NETWORK.register_module() -class FFN(BaseModule): - """Implements feed-forward networks (FFNs) with identity connection. - - Args: - embed_dims (int): The feature dimension. Same as - `MultiheadAttention`. Defaults: 256. - feedforward_channels (int): The hidden dimension of FFNs. - Defaults: 1024. - num_fcs (int, optional): The number of fully-connected layers in - FFNs. Default: 2. - act_cfg (dict, optional): The activation config for FFNs. - Default: dict(type='ReLU') - ffn_drop (float, optional): Probability of an element to be - zeroed in FFN. Default 0.0. - add_identity (bool, optional): Whether to add the - identity connection. Default: `True`. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - @deprecated_api_warning( - { - 'dropout': 'ffn_drop', - 'add_residual': 'add_identity' - }, - cls_name='FFN') - def __init__(self, - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - act_cfg=dict(type='ReLU', inplace=True), - ffn_drop=0., - dropout_layer=None, - add_identity=True, - init_cfg=None, - **kwargs): - super(FFN, self).__init__(init_cfg) - assert num_fcs >= 2, 'num_fcs should be no less ' \ - f'than 2. got {num_fcs}.' - self.embed_dims = embed_dims - self.feedforward_channels = feedforward_channels - self.num_fcs = num_fcs - self.act_cfg = act_cfg - self.activate = build_activation_layer(act_cfg) - - layers = [] - in_channels = embed_dims - for _ in range(num_fcs - 1): - layers.append( - Sequential( - Linear(in_channels, feedforward_channels), self.activate, - nn.Dropout(ffn_drop))) - in_channels = feedforward_channels - layers.append(Linear(feedforward_channels, embed_dims)) - layers.append(nn.Dropout(ffn_drop)) - self.layers = Sequential(*layers) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else torch.nn.Identity() - self.add_identity = add_identity - - @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') - def forward(self, x, identity=None): - """Forward function for `FFN`. - - The function would add x to the output tensor if residue is None. - """ - out = self.layers(x) - if not self.add_identity: - return self.dropout_layer(out) - if identity is None: - identity = x - return identity + self.dropout_layer(out) - - -@TRANSFORMER_LAYER.register_module() -class BaseTransformerLayer(BaseModule): - """Base `TransformerLayer` for vision transformer. - - It can be built from `mmcv.ConfigDict` and support more flexible - customization, for example, using any number of `FFN or LN ` and - use different kinds of `attention` by specifying a list of `ConfigDict` - named `attn_cfgs`. It is worth mentioning that it supports `prenorm` - when you specifying `norm` as the first element of `operation_order`. - More details about the `prenorm`: `On Layer Normalization in the - Transformer Architecture `_ . - - Args: - attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for `self_attention` or `cross_attention` modules, - The order of the configs in the list should be consistent with - corresponding attentions in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. Default: None. - ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for FFN, The order of the configs in the list should be - consistent with corresponding ffn in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. - operation_order (tuple[str]): The execution order of operation - in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). - Support `prenorm` when you specifying first element as `norm`. - Default:None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): Key, Query and Value are shape - of (batch, n, embed_dim) - or (n, batch, embed_dim). Default to False. - """ - - def __init__(self, - attn_cfgs=None, - ffn_cfgs=dict( - type='FFN', - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - ffn_drop=0., - act_cfg=dict(type='ReLU', inplace=True), - ), - operation_order=None, - norm_cfg=dict(type='LN'), - init_cfg=None, - batch_first=False, - **kwargs): - - deprecated_args = dict( - feedforward_channels='feedforward_channels', - ffn_dropout='ffn_drop', - ffn_num_fcs='num_fcs') - for ori_name, new_name in deprecated_args.items(): - if ori_name in kwargs: - warnings.warn( - f'The arguments `{ori_name}` in BaseTransformerLayer ' - f'has been deprecated, now you should set `{new_name}` ' - f'and other FFN related arguments ' - f'to a dict named `ffn_cfgs`. ') - ffn_cfgs[new_name] = kwargs[ori_name] - - super(BaseTransformerLayer, self).__init__(init_cfg) - - self.batch_first = batch_first - - assert set(operation_order) & set( - ['self_attn', 'norm', 'ffn', 'cross_attn']) == \ - set(operation_order), f'The operation_order of' \ - f' {self.__class__.__name__} should ' \ - f'contains all four operation type ' \ - f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" - - num_attn = operation_order.count('self_attn') + operation_order.count( - 'cross_attn') - if isinstance(attn_cfgs, dict): - attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] - else: - assert num_attn == len(attn_cfgs), f'The length ' \ - f'of attn_cfg {num_attn} is ' \ - f'not consistent with the number of attention' \ - f'in operation_order {operation_order}.' - - self.num_attn = num_attn - self.operation_order = operation_order - self.norm_cfg = norm_cfg - self.pre_norm = operation_order[0] == 'norm' - self.attentions = ModuleList() - - index = 0 - for operation_name in operation_order: - if operation_name in ['self_attn', 'cross_attn']: - if 'batch_first' in attn_cfgs[index]: - assert self.batch_first == attn_cfgs[index]['batch_first'] - else: - attn_cfgs[index]['batch_first'] = self.batch_first - attention = build_attention(attn_cfgs[index]) - # Some custom attentions used as `self_attn` - # or `cross_attn` can have different behavior. - attention.operation_name = operation_name - self.attentions.append(attention) - index += 1 - - self.embed_dims = self.attentions[0].embed_dims - - self.ffns = ModuleList() - num_ffns = operation_order.count('ffn') - if isinstance(ffn_cfgs, dict): - ffn_cfgs = ConfigDict(ffn_cfgs) - if isinstance(ffn_cfgs, dict): - ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] - assert len(ffn_cfgs) == num_ffns - for ffn_index in range(num_ffns): - if 'embed_dims' not in ffn_cfgs[ffn_index]: - ffn_cfgs['embed_dims'] = self.embed_dims - else: - assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims - self.ffns.append( - build_feedforward_network(ffn_cfgs[ffn_index], - dict(type='FFN'))) - - self.norms = ModuleList() - num_norms = operation_order.count('norm') - for _ in range(num_norms): - self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) - - def forward(self, - query, - key=None, - value=None, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerDecoderLayer`. - - **kwargs contains some specific arguments of attentions. - - Args: - query (Tensor): The input query with shape - [num_queries, bs, embed_dims] if - self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - value (Tensor): The value tensor with same shape as `key`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor] | None): 2D Tensor used in - calculation of corresponding attention. The length of - it should equal to the number of `attention` in - `operation_order`. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in `self_attn` layer. - Defaults to None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: forwarded results with shape [num_queries, bs, embed_dims]. - """ - - norm_index = 0 - attn_index = 0 - ffn_index = 0 - identity = query - if attn_masks is None: - attn_masks = [None for _ in range(self.num_attn)] - elif isinstance(attn_masks, torch.Tensor): - attn_masks = [ - copy.deepcopy(attn_masks) for _ in range(self.num_attn) - ] - warnings.warn(f'Use same attn_mask in all attentions in ' - f'{self.__class__.__name__} ') - else: - assert len(attn_masks) == self.num_attn, f'The length of ' \ - f'attn_masks {len(attn_masks)} must be equal ' \ - f'to the number of attention in ' \ - f'operation_order {self.num_attn}' - - for layer in self.operation_order: - if layer == 'self_attn': - temp_key = temp_value = query - query = self.attentions[attn_index]( - query, - temp_key, - temp_value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=query_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=query_key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'norm': - query = self.norms[norm_index](query) - norm_index += 1 - - elif layer == 'cross_attn': - query = self.attentions[attn_index]( - query, - key, - value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=key_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'ffn': - query = self.ffns[ffn_index]( - query, identity if self.pre_norm else None) - ffn_index += 1 - - return query - - -@TRANSFORMER_LAYER_SEQUENCE.register_module() -class TransformerLayerSequence(BaseModule): - """Base class for TransformerEncoder and TransformerDecoder in vision - transformer. - - As base-class of Encoder and Decoder in vision transformer. - Support customization such as specifying different kind - of `transformer_layer` in `transformer_coder`. - - Args: - transformerlayer (list[obj:`mmcv.ConfigDict`] | - obj:`mmcv.ConfigDict`): Config of transformerlayer - in TransformerCoder. If it is obj:`mmcv.ConfigDict`, - it would be repeated `num_layer` times to a - list[`mmcv.ConfigDict`]. Default: None. - num_layers (int): The number of `TransformerLayer`. Default: None. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): - super(TransformerLayerSequence, self).__init__(init_cfg) - if isinstance(transformerlayers, dict): - transformerlayers = [ - copy.deepcopy(transformerlayers) for _ in range(num_layers) - ] - else: - assert isinstance(transformerlayers, list) and \ - len(transformerlayers) == num_layers - self.num_layers = num_layers - self.layers = ModuleList() - for i in range(num_layers): - self.layers.append(build_transformer_layer(transformerlayers[i])) - self.embed_dims = self.layers[0].embed_dims - self.pre_norm = self.layers[0].pre_norm - - def forward(self, - query, - key, - value, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerCoder`. - - Args: - query (Tensor): Input query with shape - `(num_queries, bs, embed_dims)`. - key (Tensor): The key tensor with shape - `(num_keys, bs, embed_dims)`. - value (Tensor): The value tensor with shape - `(num_keys, bs, embed_dims)`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor], optional): Each element is 2D Tensor - which is used in calculation of corresponding attention in - operation_order. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in self-attention - Default: None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: results with shape [num_queries, bs, embed_dims]. - """ - for layer in self.layers: - query = layer( - query, - key, - value, - query_pos=query_pos, - key_pos=key_pos, - attn_masks=attn_masks, - query_key_padding_mask=query_key_padding_mask, - key_padding_mask=key_padding_mask, - **kwargs) - return query diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/upsample.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/upsample.py deleted file mode 100755 index a1a353767..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/upsample.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import xavier_init -from .registry import UPSAMPLE_LAYERS - -UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) -UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) - - -@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') -class PixelShufflePack(nn.Module): - """Pixel Shuffle upsample layer. - - This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to - achieve a simple upsampling with pixel shuffle. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of the conv layer to expand the - channels. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super(PixelShufflePack, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - xavier_init(self.upsample_conv, distribution='uniform') - - def forward(self, x): - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x - - -def build_upsample_layer(cfg, *args, **kwargs): - """Build upsample layer. - - Args: - cfg (dict): The upsample layer config, which should contain: - - - type (str): Layer type. - - scale_factor (int): Upsample ratio, which is not applicable to - deconv. - - layer args: Args needed to instantiate a upsample layer. - args (argument list): Arguments passed to the ``__init__`` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the - ``__init__`` method of the corresponding conv layer. - - Returns: - nn.Module: Created upsample layer. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - raise KeyError( - f'the cfg dict must contain the key "type", but got {cfg}') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in UPSAMPLE_LAYERS: - raise KeyError(f'Unrecognized upsample type {layer_type}') - else: - upsample = UPSAMPLE_LAYERS.get(layer_type) - - if upsample is nn.Upsample: - cfg_['mode'] = layer_type - layer = upsample(*args, **kwargs, **cfg_) - return layer diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/wrappers.py b/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/wrappers.py deleted file mode 100755 index 8aebf67bf..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/bricks/wrappers.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 - -Wrap some nn modules to support empty tensor input. Currently, these wrappers -are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask -heads are trained on only positive RoIs. -""" -import math - -import torch -import torch.nn as nn -from torch.nn.modules.utils import _pair, _triple - -from .registry import CONV_LAYERS, UPSAMPLE_LAYERS - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def obsolete_torch_version(torch_version, version_threshold): - return torch_version == 'parrots' or torch_version <= version_threshold - - -class NewEmptyTensorOp(torch.autograd.Function): - - @staticmethod - def forward(ctx, x, new_shape): - ctx.shape = x.shape - return x.new_empty(new_shape) - - @staticmethod - def backward(ctx, grad): - shape = ctx.shape - return NewEmptyTensorOp.apply(grad, shape), None - - -@CONV_LAYERS.register_module('Conv', force=True) -class Conv2d(nn.Conv2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module('Conv3d', force=True) -class Conv3d(nn.Conv3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv') -@UPSAMPLE_LAYERS.register_module('deconv', force=True) -class ConvTranspose2d(nn.ConvTranspose2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv3d') -@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) -class ConvTranspose3d(nn.ConvTranspose3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -class MaxPool2d(nn.MaxPool2d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), - _pair(self.padding), _pair(self.stride), - _pair(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class MaxPool3d(nn.MaxPool3d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), - _triple(self.padding), - _triple(self.stride), - _triple(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class Linear(torch.nn.Linear): - - def forward(self, x): - # empty tensor forward of Linear layer is supported in Pytorch 1.6 - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): - out_shape = [x.shape[0], self.out_features] - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/builder.py b/cv/detection/autoassign/pytorch/mmcv/cnn/builder.py deleted file mode 100755 index 7567316c5..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/builder.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..runner import Sequential -from ..utils import Registry, build_from_cfg - - -def build_model_from_cfg(cfg, registry, default_args=None): - """Build a PyTorch model from config dict(s). Different from - ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. - - Args: - cfg (dict, list[dict]): The config of modules, is is either a config - dict or a list of config dicts. If cfg is a list, a - the built modules will be wrapped with ``nn.Sequential``. - registry (:obj:`Registry`): A registry the module belongs to. - default_args (dict, optional): Default arguments to build the module. - Defaults to None. - - Returns: - nn.Module: A built nn module. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return Sequential(*modules) - else: - return build_from_cfg(cfg, registry, default_args) - - -MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/resnet.py b/cv/detection/autoassign/pytorch/mmcv/cnn/resnet.py deleted file mode 100755 index 1cb3ac057..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/resnet.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn -import torch.utils.checkpoint as cp - -from .utils import constant_init, kaiming_init - - -def conv3x3(in_planes, out_planes, stride=1, dilation=1): - """3x3 convolution with padding.""" - return nn.Conv2d( - in_planes, - out_planes, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False): - super(BasicBlock, self).__init__() - assert style in ['pytorch', 'caffe'] - self.conv1 = conv3x3(inplanes, planes, stride, dilation) - self.bn1 = nn.BatchNorm2d(planes) - self.relu = nn.ReLU(inplace=True) - self.conv2 = conv3x3(planes, planes) - self.bn2 = nn.BatchNorm2d(planes) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - assert not with_cp - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Bottleneck(nn.Module): - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False): - """Bottleneck block. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if - it is "caffe", the stride-two layer is the first 1x1 conv layer. - """ - super(Bottleneck, self).__init__() - assert style in ['pytorch', 'caffe'] - if style == 'pytorch': - conv1_stride = 1 - conv2_stride = stride - else: - conv1_stride = stride - conv2_stride = 1 - self.conv1 = nn.Conv2d( - inplanes, planes, kernel_size=1, stride=conv1_stride, bias=False) - self.conv2 = nn.Conv2d( - planes, - planes, - kernel_size=3, - stride=conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.bn1 = nn.BatchNorm2d(planes) - self.bn2 = nn.BatchNorm2d(planes) - self.conv3 = nn.Conv2d( - planes, planes * self.expansion, kernel_size=1, bias=False) - self.bn3 = nn.BatchNorm2d(planes * self.expansion) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - def forward(self, x): - - def _inner_forward(x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -def make_res_layer(block, - inplanes, - planes, - blocks, - stride=1, - dilation=1, - style='pytorch', - with_cp=False): - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = nn.Sequential( - nn.Conv2d( - inplanes, - planes * block.expansion, - kernel_size=1, - stride=stride, - bias=False), - nn.BatchNorm2d(planes * block.expansion), - ) - - layers = [] - layers.append( - block( - inplanes, - planes, - stride, - dilation, - downsample, - style=style, - with_cp=with_cp)) - inplanes = planes * block.expansion - for _ in range(1, blocks): - layers.append( - block(inplanes, planes, 1, dilation, style=style, with_cp=with_cp)) - - return nn.Sequential(*layers) - - -class ResNet(nn.Module): - """ResNet backbone. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - num_stages (int): Resnet stages, normally 4. - strides (Sequence[int]): Strides of the first block of each stage. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze - running stats (mean and var). - bn_frozen (bool): Whether to freeze weight and bias of BN layers. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - frozen_stages=-1, - bn_eval=True, - bn_frozen=False, - with_cp=False): - super(ResNet, self).__init__() - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - assert num_stages >= 1 and num_stages <= 4 - block, stage_blocks = self.arch_settings[depth] - stage_blocks = stage_blocks[:num_stages] - assert len(strides) == len(dilations) == num_stages - assert max(out_indices) < num_stages - - self.out_indices = out_indices - self.style = style - self.frozen_stages = frozen_stages - self.bn_eval = bn_eval - self.bn_frozen = bn_frozen - self.with_cp = with_cp - - self.inplanes = 64 - self.conv1 = nn.Conv2d( - 3, 64, kernel_size=7, stride=2, padding=3, bias=False) - self.bn1 = nn.BatchNorm2d(64) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - self.res_layers = [] - for i, num_blocks in enumerate(stage_blocks): - stride = strides[i] - dilation = dilations[i] - planes = 64 * 2**i - res_layer = make_res_layer( - block, - self.inplanes, - planes, - num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - with_cp=with_cp) - self.inplanes = planes * block.expansion - layer_name = f'layer{i + 1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self.feat_dim = block.expansion * 64 * 2**(len(stage_blocks) - 1) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m) - elif isinstance(m, nn.BatchNorm2d): - constant_init(m, 1) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - if len(outs) == 1: - return outs[0] - else: - return tuple(outs) - - def train(self, mode=True): - super(ResNet, self).train(mode) - if self.bn_eval: - for m in self.modules(): - if isinstance(m, nn.BatchNorm2d): - m.eval() - if self.bn_frozen: - for params in m.parameters(): - params.requires_grad = False - if mode and self.frozen_stages >= 0: - for param in self.conv1.parameters(): - param.requires_grad = False - for param in self.bn1.parameters(): - param.requires_grad = False - self.bn1.eval() - self.bn1.weight.requires_grad = False - self.bn1.bias.requires_grad = False - for i in range(1, self.frozen_stages + 1): - mod = getattr(self, f'layer{i}') - mod.eval() - for param in mod.parameters(): - param.requires_grad = False diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/utils/__init__.py b/cv/detection/autoassign/pytorch/mmcv/cnn/utils/__init__.py deleted file mode 100755 index a263e31c1..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .flops_counter import get_model_complexity_info -from .fuse_conv_bn import fuse_conv_bn -from .sync_bn import revert_sync_batchnorm -from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, - KaimingInit, NormalInit, PretrainedInit, - TruncNormalInit, UniformInit, XavierInit, - bias_init_with_prob, caffe2_xavier_init, - constant_init, initialize, kaiming_init, normal_init, - trunc_normal_init, uniform_init, xavier_init) - -__all__ = [ - 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', - 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', - 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'revert_sync_batchnorm' -] diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/utils/flops_counter.py b/cv/detection/autoassign/pytorch/mmcv/cnn/utils/flops_counter.py deleted file mode 100755 index dceeb398b..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/utils/flops_counter.py +++ /dev/null @@ -1,599 +0,0 @@ -# Modified from flops-counter.pytorch by Vladislav Sovrasov -# original repo: https://github.com/sovrasov/flops-counter.pytorch - -# MIT License - -# Copyright (c) 2018 Vladislav Sovrasov - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -from functools import partial - -import numpy as np -import torch -import torch.nn as nn - -import mmcv - - -def get_model_complexity_info(model, - input_shape, - print_per_layer_stat=True, - as_strings=True, - input_constructor=None, - flush=False, - ost=sys.stdout): - """Get complexity information of a model. - - This method can calculate FLOPs and parameter counts of a model with - corresponding input shape. It can also print complexity information for - each layer in a model. - - Supported layers are listed as below: - - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. - - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, ``nn.LeakyReLU``, - ``nn.ReLU6``. - - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, - ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, - ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, - ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, - ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. - - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, - ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, - ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. - - Linear: ``nn.Linear``. - - Deconvolution: ``nn.ConvTranspose2d``. - - Upsample: ``nn.Upsample``. - - Args: - model (nn.Module): The model for complexity calculation. - input_shape (tuple): Input shape used for calculation. - print_per_layer_stat (bool): Whether to print complexity information - for each layer in a model. Default: True. - as_strings (bool): Output FLOPs and params counts in a string form. - Default: True. - input_constructor (None | callable): If specified, it takes a callable - method that generates input. otherwise, it will generate a random - tensor with input shape to calculate FLOPs. Default: None. - flush (bool): same as that in :func:`print`. Default: False. - ost (stream): same as ``file`` param in :func:`print`. - Default: sys.stdout. - - Returns: - tuple[float | str]: If ``as_strings`` is set to True, it will return - FLOPs and parameter counts in a string format. otherwise, it will - return those in a float number format. - """ - assert type(input_shape) is tuple - assert len(input_shape) >= 1 - assert isinstance(model, nn.Module) - flops_model = add_flops_counting_methods(model) - flops_model.eval() - flops_model.start_flops_count() - if input_constructor: - input = input_constructor(input_shape) - _ = flops_model(**input) - else: - try: - batch = torch.ones(()).new_empty( - (1, *input_shape), - dtype=next(flops_model.parameters()).dtype, - device=next(flops_model.parameters()).device) - except StopIteration: - # Avoid StopIteration for models which have no parameters, - # like `nn.Relu()`, `nn.AvgPool2d`, etc. - batch = torch.ones(()).new_empty((1, *input_shape)) - - _ = flops_model(batch) - - flops_count, params_count = flops_model.compute_average_flops_cost() - if print_per_layer_stat: - print_model_with_flops( - flops_model, flops_count, params_count, ost=ost, flush=flush) - flops_model.stop_flops_count() - - if as_strings: - return flops_to_string(flops_count), params_to_string(params_count) - - return flops_count, params_count - - -def flops_to_string(flops, units='GFLOPs', precision=2): - """Convert FLOPs number into a string. - - Note that Here we take a multiply-add counts as one FLOP. - - Args: - flops (float): FLOPs number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', - 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically - choose the most suitable unit for FLOPs. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted FLOPs number with units. - - Examples: - >>> flops_to_string(1e9) - '1.0 GFLOPs' - >>> flops_to_string(2e5, 'MFLOPs') - '0.2 MFLOPs' - >>> flops_to_string(3e-9, None) - '3e-09 FLOPs' - """ - if units is None: - if flops // 10**9 > 0: - return str(round(flops / 10.**9, precision)) + ' GFLOPs' - elif flops // 10**6 > 0: - return str(round(flops / 10.**6, precision)) + ' MFLOPs' - elif flops // 10**3 > 0: - return str(round(flops / 10.**3, precision)) + ' KFLOPs' - else: - return str(flops) + ' FLOPs' - else: - if units == 'GFLOPs': - return str(round(flops / 10.**9, precision)) + ' ' + units - elif units == 'MFLOPs': - return str(round(flops / 10.**6, precision)) + ' ' + units - elif units == 'KFLOPs': - return str(round(flops / 10.**3, precision)) + ' ' + units - else: - return str(flops) + ' FLOPs' - - -def params_to_string(num_params, units=None, precision=2): - """Convert parameter number into a string. - - Args: - num_params (float): Parameter number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'M', - 'K' and ''. If set to None, it will automatically choose the most - suitable unit for Parameter number. Default: None. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted parameter number with units. - - Examples: - >>> params_to_string(1e9) - '1000.0 M' - >>> params_to_string(2e5) - '200.0 k' - >>> params_to_string(3e-9) - '3e-09' - """ - if units is None: - if num_params // 10**6 > 0: - return str(round(num_params / 10**6, precision)) + ' M' - elif num_params // 10**3: - return str(round(num_params / 10**3, precision)) + ' k' - else: - return str(num_params) - else: - if units == 'M': - return str(round(num_params / 10.**6, precision)) + ' ' + units - elif units == 'K': - return str(round(num_params / 10.**3, precision)) + ' ' + units - else: - return str(num_params) - - -def print_model_with_flops(model, - total_flops, - total_params, - units='GFLOPs', - precision=3, - ost=sys.stdout, - flush=False): - """Print a model with FLOPs for each layer. - - Args: - model (nn.Module): The model to be printed. - total_flops (float): Total FLOPs of the model. - total_params (float): Total parameter counts of the model. - units (str | None): Converted FLOPs units. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 3. - ost (stream): same as `file` param in :func:`print`. - Default: sys.stdout. - flush (bool): same as that in :func:`print`. Default: False. - - Example: - >>> class ExampleModel(nn.Module): - - >>> def __init__(self): - >>> super().__init__() - >>> self.conv1 = nn.Conv2d(3, 8, 3) - >>> self.conv2 = nn.Conv2d(8, 256, 3) - >>> self.conv3 = nn.Conv2d(256, 8, 3) - >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) - >>> self.flatten = nn.Flatten() - >>> self.fc = nn.Linear(8, 1) - - >>> def forward(self, x): - >>> x = self.conv1(x) - >>> x = self.conv2(x) - >>> x = self.conv3(x) - >>> x = self.avg_pool(x) - >>> x = self.flatten(x) - >>> x = self.fc(x) - >>> return x - - >>> model = ExampleModel() - >>> x = (3, 16, 16) - to print the complexity information state for each layer, you can use - >>> get_model_complexity_info(model, x) - or directly use - >>> print_model_with_flops(model, 4579784.0, 37361) - ExampleModel( - 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, - (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 - (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) - (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) - (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) - (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) - (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) - ) - """ - - def accumulate_params(self): - if is_supported_instance(self): - return self.__params__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_params() - return sum - - def accumulate_flops(self): - if is_supported_instance(self): - return self.__flops__ / model.__batch_counter__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_flops() - return sum - - def flops_repr(self): - accumulated_num_params = self.accumulate_params() - accumulated_flops_cost = self.accumulate_flops() - return ', '.join([ - params_to_string( - accumulated_num_params, units='M', precision=precision), - '{:.3%} Params'.format(accumulated_num_params / total_params), - flops_to_string( - accumulated_flops_cost, units=units, precision=precision), - '{:.3%} FLOPs'.format(accumulated_flops_cost / total_flops), - self.original_extra_repr() - ]) - - def add_extra_repr(m): - m.accumulate_flops = accumulate_flops.__get__(m) - m.accumulate_params = accumulate_params.__get__(m) - flops_extra_repr = flops_repr.__get__(m) - if m.extra_repr != flops_extra_repr: - m.original_extra_repr = m.extra_repr - m.extra_repr = flops_extra_repr - assert m.extra_repr != m.original_extra_repr - - def del_extra_repr(m): - if hasattr(m, 'original_extra_repr'): - m.extra_repr = m.original_extra_repr - del m.original_extra_repr - if hasattr(m, 'accumulate_flops'): - del m.accumulate_flops - - model.apply(add_extra_repr) - print(model, file=ost, flush=flush) - model.apply(del_extra_repr) - - -def get_model_parameters_number(model): - """Calculate parameter number of a model. - - Args: - model (nn.module): The model for parameter number calculation. - - Returns: - float: Parameter number of the model. - """ - num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - return num_params - - -def add_flops_counting_methods(net_main_module): - # adding additional methods to the existing module object, - # this is done this way so that each function has access to self object - net_main_module.start_flops_count = start_flops_count.__get__( - net_main_module) - net_main_module.stop_flops_count = stop_flops_count.__get__( - net_main_module) - net_main_module.reset_flops_count = reset_flops_count.__get__( - net_main_module) - net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # noqa: E501 - net_main_module) - - net_main_module.reset_flops_count() - - return net_main_module - - -def compute_average_flops_cost(self): - """Compute average FLOPs cost. - - A method to compute average FLOPs cost, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - - Returns: - float: Current mean flops consumption per image. - """ - batches_count = self.__batch_counter__ - flops_sum = 0 - for module in self.modules(): - if is_supported_instance(module): - flops_sum += module.__flops__ - params_sum = get_model_parameters_number(self) - return flops_sum / batches_count, params_sum - - -def start_flops_count(self): - """Activate the computation of mean flops consumption per image. - - A method to activate the computation of mean flops consumption per image. - which will be available after ``add_flops_counting_methods()`` is called on - a desired net object. It should be called before running the network. - """ - add_batch_counter_hook_function(self) - - def add_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - return - - else: - handle = module.register_forward_hook( - get_modules_mapping()[type(module)]) - - module.__flops_handle__ = handle - - self.apply(partial(add_flops_counter_hook_function)) - - -def stop_flops_count(self): - """Stop computing the mean flops consumption per image. - - A method to stop computing the mean flops consumption per image, which will - be available after ``add_flops_counting_methods()`` is called on a desired - net object. It can be called to pause the computation whenever. - """ - remove_batch_counter_hook_function(self) - self.apply(remove_flops_counter_hook_function) - - -def reset_flops_count(self): - """Reset statistics computed so far. - - A method to Reset computed statistics, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - """ - add_batch_counter_variables_or_reset(self) - self.apply(add_flops_counter_variable_or_reset) - - -# ---- Internal functions -def empty_flops_counter_hook(module, input, output): - module.__flops__ += 0 - - -def upsample_flops_counter_hook(module, input, output): - output_size = output[0] - batch_size = output_size.shape[0] - output_elements_count = batch_size - for val in output_size.shape[1:]: - output_elements_count *= val - module.__flops__ += int(output_elements_count) - - -def relu_flops_counter_hook(module, input, output): - active_elements_count = output.numel() - module.__flops__ += int(active_elements_count) - - -def linear_flops_counter_hook(module, input, output): - input = input[0] - output_last_dim = output.shape[ - -1] # pytorch checks dimensions, so here we don't care much - module.__flops__ += int(np.prod(input.shape) * output_last_dim) - - -def pool_flops_counter_hook(module, input, output): - input = input[0] - module.__flops__ += int(np.prod(input.shape)) - - -def norm_flops_counter_hook(module, input, output): - input = input[0] - - batch_flops = np.prod(input.shape) - if (getattr(module, 'affine', False) - or getattr(module, 'elementwise_affine', False)): - batch_flops *= 2 - module.__flops__ += int(batch_flops) - - -def deconv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - input_height, input_width = input.shape[2:] - - kernel_height, kernel_width = conv_module.kernel_size - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = ( - kernel_height * kernel_width * in_channels * filters_per_channel) - - active_elements_count = batch_size * input_height * input_width - overall_conv_flops = conv_per_position_flops * active_elements_count - bias_flops = 0 - if conv_module.bias is not None: - output_height, output_width = output.shape[2:] - bias_flops = out_channels * batch_size * output_height * output_height - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def conv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - output_dims = list(output.shape[2:]) - - kernel_dims = list(conv_module.kernel_size) - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = int( - np.prod(kernel_dims)) * in_channels * filters_per_channel - - active_elements_count = batch_size * int(np.prod(output_dims)) - - overall_conv_flops = conv_per_position_flops * active_elements_count - - bias_flops = 0 - - if conv_module.bias is not None: - - bias_flops = out_channels * active_elements_count - - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def batch_counter_hook(module, input, output): - batch_size = 1 - if len(input) > 0: - # Can have multiple inputs, getting the first one - input = input[0] - batch_size = len(input) - else: - pass - print('Warning! No positional inputs found for a module, ' - 'assuming batch size is 1.') - module.__batch_counter__ += batch_size - - -def add_batch_counter_variables_or_reset(module): - - module.__batch_counter__ = 0 - - -def add_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - return - - handle = module.register_forward_hook(batch_counter_hook) - module.__batch_counter_handle__ = handle - - -def remove_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - module.__batch_counter_handle__.remove() - del module.__batch_counter_handle__ - - -def add_flops_counter_variable_or_reset(module): - if is_supported_instance(module): - if hasattr(module, '__flops__') or hasattr(module, '__params__'): - print('Warning: variables __flops__ or __params__ are already ' - 'defined for the module' + type(module).__name__ + - ' ptflops can affect your code!') - module.__flops__ = 0 - module.__params__ = get_model_parameters_number(module) - - -def is_supported_instance(module): - if type(module) in get_modules_mapping(): - return True - return False - - -def remove_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - module.__flops_handle__.remove() - del module.__flops_handle__ - - -def get_modules_mapping(): - return { - # convolutions - nn.Conv1d: conv_flops_counter_hook, - nn.Conv2d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, - nn.Conv3d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, - # activations - nn.ReLU: relu_flops_counter_hook, - nn.PReLU: relu_flops_counter_hook, - nn.ELU: relu_flops_counter_hook, - nn.LeakyReLU: relu_flops_counter_hook, - nn.ReLU6: relu_flops_counter_hook, - # poolings - nn.MaxPool1d: pool_flops_counter_hook, - nn.AvgPool1d: pool_flops_counter_hook, - nn.AvgPool2d: pool_flops_counter_hook, - nn.MaxPool2d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, - nn.MaxPool3d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, - nn.AvgPool3d: pool_flops_counter_hook, - nn.AdaptiveMaxPool1d: pool_flops_counter_hook, - nn.AdaptiveAvgPool1d: pool_flops_counter_hook, - nn.AdaptiveMaxPool2d: pool_flops_counter_hook, - nn.AdaptiveAvgPool2d: pool_flops_counter_hook, - nn.AdaptiveMaxPool3d: pool_flops_counter_hook, - nn.AdaptiveAvgPool3d: pool_flops_counter_hook, - # normalizations - nn.BatchNorm1d: norm_flops_counter_hook, - nn.BatchNorm2d: norm_flops_counter_hook, - nn.BatchNorm3d: norm_flops_counter_hook, - nn.GroupNorm: norm_flops_counter_hook, - nn.InstanceNorm1d: norm_flops_counter_hook, - nn.InstanceNorm2d: norm_flops_counter_hook, - nn.InstanceNorm3d: norm_flops_counter_hook, - nn.LayerNorm: norm_flops_counter_hook, - # FC - nn.Linear: linear_flops_counter_hook, - mmcv.cnn.bricks.Linear: linear_flops_counter_hook, - # Upscale - nn.Upsample: upsample_flops_counter_hook, - # Deconvolution - nn.ConvTranspose2d: deconv_flops_counter_hook, - mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, - } diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/utils/fuse_conv_bn.py b/cv/detection/autoassign/pytorch/mmcv/cnn/utils/fuse_conv_bn.py deleted file mode 100755 index cb7076f80..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/utils/fuse_conv_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def _fuse_conv_bn(conv, bn): - """Fuse conv and bn into one module. - - Args: - conv (nn.Module): Conv to be fused. - bn (nn.Module): BN to be fused. - - Returns: - nn.Module: Fused module. - """ - conv_w = conv.weight - conv_b = conv.bias if conv.bias is not None else torch.zeros_like( - bn.running_mean) - - factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) - conv.weight = nn.Parameter(conv_w * - factor.reshape([conv.out_channels, 1, 1, 1])) - conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) - return conv - - -def fuse_conv_bn(module): - """Recursively fuse conv and bn in a module. - - During inference, the functionary of batch norm layers is turned off - but only the mean and var alone channels are used, which exposes the - chance to fuse it with the preceding conv layers to save computations and - simplify network structures. - - Args: - module (nn.Module): Module to be fused. - - Returns: - nn.Module: Fused module. - """ - last_conv = None - last_conv_name = None - - for name, child in module.named_children(): - if isinstance(child, - (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): - if last_conv is None: # only fuse BN that is after Conv - continue - fused_conv = _fuse_conv_bn(last_conv, child) - module._modules[last_conv_name] = fused_conv - # To reduce changes, set BN as Identity instead of deleting it. - module._modules[name] = nn.Identity() - last_conv = None - elif isinstance(child, nn.Conv2d): - last_conv = child - last_conv_name = name - else: - fuse_conv_bn(child) - return module diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/utils/sync_bn.py b/cv/detection/autoassign/pytorch/mmcv/cnn/utils/sync_bn.py deleted file mode 100755 index 8a79ff4a4..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/utils/sync_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -import torch - -import mmcv - - -class _BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): - """A general BatchNorm layer without input dimension check. - - Reproduced from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc - is `_check_input_dim` that is designed for tensor sanity checks. - The check has been bypassed in this class for the convenience of converting - SyncBatchNorm. - """ - - def _check_input_dim(self, input): - return - - -def revert_sync_batchnorm(module): - """Helper function to convert all `SyncBatchNorm` (SyncBN) and - `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to - `BatchNormXd` layers. - - Adapted from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - - Args: - module (nn.Module): The module containing `SyncBatchNorm` layers. - - Returns: - module_output: The converted module with `BatchNormXd` layers. - """ - module_output = module - module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] - if hasattr(mmcv, 'ops'): - module_checklist.append(mmcv.ops.SyncBatchNorm) - if isinstance(module, tuple(module_checklist)): - module_output = _BatchNormXd(module.num_features, module.eps, - module.momentum, module.affine, - module.track_running_stats) - if module.affine: - # no_grad() may not be needed here but - # just to be consistent with `convert_sync_batchnorm()` - with torch.no_grad(): - module_output.weight = module.weight - module_output.bias = module.bias - module_output.running_mean = module.running_mean - module_output.running_var = module.running_var - module_output.num_batches_tracked = module.num_batches_tracked - module_output.training = module.training - # qconfig exists in quantized models - if hasattr(module, 'qconfig'): - module_output.qconfig = module.qconfig - for name, child in module.named_children(): - module_output.add_module(name, revert_sync_batchnorm(child)) - del module - return module_output diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/utils/weight_init.py b/cv/detection/autoassign/pytorch/mmcv/cnn/utils/weight_init.py deleted file mode 100755 index e1ac999e2..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/utils/weight_init.py +++ /dev/null @@ -1,684 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings - -import numpy as np -import torch -import torch.nn as nn -from torch import Tensor - -from mmcv.utils import Registry, build_from_cfg, get_logger, print_log - -INITIALIZERS = Registry('initializer') - - -def update_init_info(module, init_info): - """Update the `_params_init_info` in the module if the value of parameters - are changed. - - Args: - module (obj:`nn.Module`): The module of PyTorch with a user-defined - attribute `_params_init_info` which records the initialization - information. - init_info (str): The string that describes the initialization. - """ - assert hasattr( - module, - '_params_init_info'), f'Can not find `_params_init_info` in {module}' - for name, param in module.named_parameters(): - - assert param in module._params_init_info, ( - f'Find a new :obj:`Parameter` ' - f'named `{name}` during executing the ' - f'`init_weights` of ' - f'`{module.__class__.__name__}`. ' - f'Please do not add or ' - f'replace parameters during executing ' - f'the `init_weights`. ') - - # The parameter has been changed during executing the - # `init_weights` of module - mean_value = param.data.mean() - if module._params_init_info[param]['tmp_mean_value'] != mean_value: - module._params_init_info[param]['init_info'] = init_info - module._params_init_info[param]['tmp_mean_value'] = mean_value - - -def constant_init(module, val, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.constant_(module.weight, val) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def xavier_init(module, gain=1, bias=0, distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.xavier_uniform_(module.weight, gain=gain) - else: - nn.init.xavier_normal_(module.weight, gain=gain) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def normal_init(module, mean=0, std=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.normal_(module.weight, mean, std) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def trunc_normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - trunc_normal_(module.weight, mean, std, a, b) # type: ignore - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) # type: ignore - - -def uniform_init(module, a=0, b=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.uniform_(module.weight, a, b) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def kaiming_init(module, - a=0, - mode='fan_out', - nonlinearity='relu', - bias=0, - distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.kaiming_uniform_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - else: - nn.init.kaiming_normal_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def caffe2_xavier_init(module, bias=0): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - kaiming_init( - module, - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - bias=bias, - distribution='uniform') - - -def bias_init_with_prob(prior_prob): - """initialize conv/fc bias value according to a given probability value.""" - bias_init = float(-np.log((1 - prior_prob) / prior_prob)) - return bias_init - - -def _get_bases_name(m): - return [b.__name__ for b in m.__class__.__bases__] - - -class BaseInit(object): - - def __init__(self, *, bias=0, bias_prob=None, layer=None): - self.wholemodule = False - if not isinstance(bias, (int, float)): - raise TypeError(f'bias must be a number, but got a {type(bias)}') - - if bias_prob is not None: - if not isinstance(bias_prob, float): - raise TypeError(f'bias_prob type must be float, \ - but got {type(bias_prob)}') - - if layer is not None: - if not isinstance(layer, (str, list)): - raise TypeError(f'layer must be a str or a list of str, \ - but got a {type(layer)}') - else: - layer = [] - - if bias_prob is not None: - self.bias = bias_init_with_prob(bias_prob) - else: - self.bias = bias - self.layer = [layer] if isinstance(layer, str) else layer - - def _get_init_info(self): - info = f'{self.__class__.__name__}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Constant') -class ConstantInit(BaseInit): - """Initialize module parameters with constant values. - - Args: - val (int | float): the value to fill the weights in the module with - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, val, **kwargs): - super().__init__(**kwargs) - self.val = val - - def __call__(self, module): - - def init(m): - if self.wholemodule: - constant_init(m, self.val, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - constant_init(m, self.val, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Xavier') -class XavierInit(BaseInit): - r"""Initialize module parameters with values according to the method - described in `Understanding the difficulty of training deep feedforward - neural networks - Glorot, X. & Bengio, Y. (2010). - `_ - - Args: - gain (int | float): an optional scaling factor. Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` - or ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, gain=1, distribution='normal', **kwargs): - super().__init__(**kwargs) - self.gain = gain - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - xavier_init(m, self.gain, self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - xavier_init(m, self.gain, self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: gain={self.gain}, ' \ - f'distribution={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Normal') -class NormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. - - Args: - mean (int | float):the mean of the normal distribution. Defaults to 0. - std (int | float): the standard deviation of the normal distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, mean=0, std=1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.std = std - - def __call__(self, module): - - def init(m): - if self.wholemodule: - normal_init(m, self.mean, self.std, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - normal_init(m, self.mean, self.std, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: mean={self.mean},' \ - f' std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='TruncNormal') -class TruncNormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values - outside :math:`[a, b]`. - - Args: - mean (float): the mean of the normal distribution. Defaults to 0. - std (float): the standard deviation of the normal distribution. - Defaults to 1. - a (float): The minimum cutoff value. - b ( float): The maximum cutoff value. - bias (float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - **kwargs) -> None: - super().__init__(**kwargs) - self.mean = mean - self.std = std - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ - f' mean={self.mean}, std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Uniform') -class UniformInit(BaseInit): - r"""Initialize module parameters with values drawn from the uniform - distribution :math:`\mathcal{U}(a, b)`. - - Args: - a (int | float): the lower bound of the uniform distribution. - Defaults to 0. - b (int | float): the upper bound of the uniform distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, a=0, b=1, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def __call__(self, module): - - def init(m): - if self.wholemodule: - uniform_init(m, self.a, self.b, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - uniform_init(m, self.a, self.b, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a},' \ - f' b={self.b}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Kaiming') -class KaimingInit(BaseInit): - r"""Initialize module parameters with the values according to the method - described in `Delving deep into rectifiers: Surpassing human-level - performance on ImageNet classification - He, K. et al. (2015). - `_ - - Args: - a (int | float): the negative slope of the rectifier used after this - layer (only used with ``'leaky_relu'``). Defaults to 0. - mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing - ``'fan_in'`` preserves the magnitude of the variance of the weights - in the forward pass. Choosing ``'fan_out'`` preserves the - magnitudes in the backwards pass. Defaults to ``'fan_out'``. - nonlinearity (str): the non-linear function (`nn.functional` name), - recommended to use only with ``'relu'`` or ``'leaky_relu'`` . - Defaults to 'relu'. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` or - ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - a=0, - mode='fan_out', - nonlinearity='relu', - distribution='normal', - **kwargs): - super().__init__(**kwargs) - self.a = a - self.mode = mode - self.nonlinearity = nonlinearity - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ - f'nonlinearity={self.nonlinearity}, ' \ - f'distribution ={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Caffe2Xavier') -class Caffe2XavierInit(KaimingInit): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - def __init__(self, **kwargs): - super().__init__( - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='uniform', - **kwargs) - - def __call__(self, module): - super().__call__(module) - - -@INITIALIZERS.register_module(name='Pretrained') -class PretrainedInit(object): - """Initialize module by loading a pretrained model. - - Args: - checkpoint (str): the checkpoint file of the pretrained model should - be load. - prefix (str, optional): the prefix of a sub-module in the pretrained - model. it is for loading a part of the pretrained model to - initialize. For example, if we would like to only load the - backbone of a detector model, we can set ``prefix='backbone.'``. - Defaults to None. - map_location (str): map tensors into proper locations. - """ - - def __init__(self, checkpoint, prefix=None, map_location=None): - self.checkpoint = checkpoint - self.prefix = prefix - self.map_location = map_location - - def __call__(self, module): - from mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, - load_state_dict) - logger = get_logger('mmcv') - if self.prefix is None: - print_log(f'load model from: {self.checkpoint}', logger=logger) - load_checkpoint( - module, - self.checkpoint, - map_location=self.map_location, - strict=False, - logger=logger) - else: - print_log( - f'load {self.prefix} in model from: {self.checkpoint}', - logger=logger) - state_dict = _load_checkpoint_with_prefix( - self.prefix, self.checkpoint, map_location=self.map_location) - load_state_dict(module, state_dict, strict=False, logger=logger) - - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: load from {self.checkpoint}' - return info - - -def _initialize(module, cfg, wholemodule=False): - func = build_from_cfg(cfg, INITIALIZERS) - # wholemodule flag is for override mode, there is no layer key in override - # and initializer will give init values for the whole module with the name - # in override. - func.wholemodule = wholemodule - func(module) - - -def _initialize_override(module, override, cfg): - if not isinstance(override, (dict, list)): - raise TypeError(f'override must be a dict or a list of dict, \ - but got {type(override)}') - - override = [override] if isinstance(override, dict) else override - - for override_ in override: - - cp_override = copy.deepcopy(override_) - name = cp_override.pop('name', None) - if name is None: - raise ValueError('`override` must contain the key "name",' - f'but got {cp_override}') - # if override only has name key, it means use args in init_cfg - if not cp_override: - cp_override.update(cfg) - # if override has name key and other args except type key, it will - # raise error - elif 'type' not in cp_override.keys(): - raise ValueError( - f'`override` need "type" key, but got {cp_override}') - - if hasattr(module, name): - _initialize(getattr(module, name), cp_override, wholemodule=True) - else: - raise RuntimeError(f'module did not have attribute {name}, ' - f'but init_cfg is {cp_override}.') - - -def initialize(module, init_cfg): - """Initialize a module. - - Args: - module (``torch.nn.Module``): the module will be initialized. - init_cfg (dict | list[dict]): initialization configuration dict to - define initializer. OpenMMLab has implemented 6 initializers - including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, - ``Kaiming``, and ``Pretrained``. - Example: - >>> module = nn.Linear(2, 3, bias=True) - >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) - >>> initialize(module, init_cfg) - - >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) - >>> # define key ``'layer'`` for initializing layer with different - >>> # configuration - >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), - dict(type='Constant', layer='Linear', val=2)] - >>> initialize(module, init_cfg) - - >>> # define key``'override'`` to initialize some specific part in - >>> # module - >>> class FooNet(nn.Module): - >>> def __init__(self): - >>> super().__init__() - >>> self.feat = nn.Conv2d(3, 16, 3) - >>> self.reg = nn.Conv2d(16, 10, 3) - >>> self.cls = nn.Conv2d(16, 5, 3) - >>> model = FooNet() - >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', - >>> override=dict(type='Constant', name='reg', val=3, bias=4)) - >>> initialize(model, init_cfg) - - >>> model = ResNet(depth=50) - >>> # Initialize weights with the pretrained model. - >>> init_cfg = dict(type='Pretrained', - checkpoint='torchvision://resnet50') - >>> initialize(model, init_cfg) - - >>> # Initialize weights of a sub-module with the specific part of - >>> # a pretrained model by using "prefix". - >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ - >>> 'retinanet_r50_fpn_1x_coco/'\ - >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' - >>> init_cfg = dict(type='Pretrained', - checkpoint=url, prefix='backbone.') - """ - if not isinstance(init_cfg, (dict, list)): - raise TypeError(f'init_cfg must be a dict or a list of dict, \ - but got {type(init_cfg)}') - - if isinstance(init_cfg, dict): - init_cfg = [init_cfg] - - for cfg in init_cfg: - # should deeply copy the original config because cfg may be used by - # other modules, e.g., one init_cfg shared by multiple bottleneck - # blocks, the expected cfg will be changed after pop and will change - # the initialization behavior of other modules - cp_cfg = copy.deepcopy(cfg) - override = cp_cfg.pop('override', None) - _initialize(module, cp_cfg) - - if override is not None: - cp_cfg.pop('layer', None) - _initialize_override(module, override, cp_cfg) - else: - # All attributes in module have same initialization. - pass - - -def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, - b: float) -> Tensor: - # Method based on - # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf - # Modified from - # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - def norm_cdf(x): - # Computes standard normal cumulative distribution function - return (1. + math.erf(x / math.sqrt(2.))) / 2. - - if (mean < a - 2 * std) or (mean > b + 2 * std): - warnings.warn( - 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' - 'The distribution of values may be incorrect.', - stacklevel=2) - - with torch.no_grad(): - # Values are generated by using a truncated uniform distribution and - # then using the inverse CDF for the normal distribution. - # Get upper and lower cdf values - lower = norm_cdf((a - mean) / std) - upper = norm_cdf((b - mean) / std) - - # Uniformly fill tensor with values from [lower, upper], then translate - # to [2lower-1, 2upper-1]. - tensor.uniform_(2 * lower - 1, 2 * upper - 1) - - # Use inverse cdf transform for normal distribution to get truncated - # standard normal - tensor.erfinv_() - - # Transform to proper mean, std - tensor.mul_(std * math.sqrt(2.)) - tensor.add_(mean) - - # Clamp to ensure it's in the proper range - tensor.clamp_(min=a, max=b) - return tensor - - -def trunc_normal_(tensor: Tensor, - mean: float = 0., - std: float = 1., - a: float = -2., - b: float = 2.) -> Tensor: - r"""Fills the input Tensor with values drawn from a truncated - normal distribution. The values are effectively drawn from the - normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` - with values outside :math:`[a, b]` redrawn until they are within - the bounds. The method used for generating the random values works - best when :math:`a \leq \text{mean} \leq b`. - - Modified from - https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - - Args: - tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. - mean (float): the mean of the normal distribution. - std (float): the standard deviation of the normal distribution. - a (float): the minimum cutoff value. - b (float): the maximum cutoff value. - """ - return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/cv/detection/autoassign/pytorch/mmcv/cnn/vgg.py b/cv/detection/autoassign/pytorch/mmcv/cnn/vgg.py deleted file mode 100755 index 8778b6495..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/cnn/vgg.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn - -from .utils import constant_init, kaiming_init, normal_init - - -def conv3x3(in_planes, out_planes, dilation=1): - """3x3 convolution with padding.""" - return nn.Conv2d( - in_planes, - out_planes, - kernel_size=3, - padding=dilation, - dilation=dilation) - - -def make_vgg_layer(inplanes, - planes, - num_blocks, - dilation=1, - with_bn=False, - ceil_mode=False): - layers = [] - for _ in range(num_blocks): - layers.append(conv3x3(inplanes, planes, dilation)) - if with_bn: - layers.append(nn.BatchNorm2d(planes)) - layers.append(nn.ReLU(inplace=True)) - inplanes = planes - layers.append(nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=ceil_mode)) - - return layers - - -class VGG(nn.Module): - """VGG backbone. - - Args: - depth (int): Depth of vgg, from {11, 13, 16, 19}. - with_bn (bool): Use BatchNorm or not. - num_classes (int): number of classes for classification. - num_stages (int): VGG stages, normally 5. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze - running stats (mean and var). - bn_frozen (bool): Whether to freeze weight and bias of BN layers. - """ - - arch_settings = { - 11: (1, 1, 2, 2, 2), - 13: (2, 2, 2, 2, 2), - 16: (2, 2, 3, 3, 3), - 19: (2, 2, 4, 4, 4) - } - - def __init__(self, - depth, - with_bn=False, - num_classes=-1, - num_stages=5, - dilations=(1, 1, 1, 1, 1), - out_indices=(0, 1, 2, 3, 4), - frozen_stages=-1, - bn_eval=True, - bn_frozen=False, - ceil_mode=False, - with_last_pool=True): - super(VGG, self).__init__() - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for vgg') - assert num_stages >= 1 and num_stages <= 5 - stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - assert len(dilations) == num_stages - assert max(out_indices) <= num_stages - - self.num_classes = num_classes - self.out_indices = out_indices - self.frozen_stages = frozen_stages - self.bn_eval = bn_eval - self.bn_frozen = bn_frozen - - self.inplanes = 3 - start_idx = 0 - vgg_layers = [] - self.range_sub_modules = [] - for i, num_blocks in enumerate(self.stage_blocks): - num_modules = num_blocks * (2 + with_bn) + 1 - end_idx = start_idx + num_modules - dilation = dilations[i] - planes = 64 * 2**i if i < 4 else 512 - vgg_layer = make_vgg_layer( - self.inplanes, - planes, - num_blocks, - dilation=dilation, - with_bn=with_bn, - ceil_mode=ceil_mode) - vgg_layers.extend(vgg_layer) - self.inplanes = planes - self.range_sub_modules.append([start_idx, end_idx]) - start_idx = end_idx - if not with_last_pool: - vgg_layers.pop(-1) - self.range_sub_modules[-1][1] -= 1 - self.module_name = 'features' - self.add_module(self.module_name, nn.Sequential(*vgg_layers)) - - if self.num_classes > 0: - self.classifier = nn.Sequential( - nn.Linear(512 * 7 * 7, 4096), - nn.ReLU(True), - nn.Dropout(), - nn.Linear(4096, 4096), - nn.ReLU(True), - nn.Dropout(), - nn.Linear(4096, num_classes), - ) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m) - elif isinstance(m, nn.BatchNorm2d): - constant_init(m, 1) - elif isinstance(m, nn.Linear): - normal_init(m, std=0.01) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - outs = [] - vgg_layers = getattr(self, self.module_name) - for i in range(len(self.stage_blocks)): - for j in range(*self.range_sub_modules[i]): - vgg_layer = vgg_layers[j] - x = vgg_layer(x) - if i in self.out_indices: - outs.append(x) - if self.num_classes > 0: - x = x.view(x.size(0), -1) - x = self.classifier(x) - outs.append(x) - if len(outs) == 1: - return outs[0] - else: - return tuple(outs) - - def train(self, mode=True): - super(VGG, self).train(mode) - if self.bn_eval: - for m in self.modules(): - if isinstance(m, nn.BatchNorm2d): - m.eval() - if self.bn_frozen: - for params in m.parameters(): - params.requires_grad = False - vgg_layers = getattr(self, self.module_name) - if mode and self.frozen_stages >= 0: - for i in range(self.frozen_stages): - for j in range(*self.range_sub_modules[i]): - mod = vgg_layers[j] - mod.eval() - for param in mod.parameters(): - param.requires_grad = False diff --git a/cv/detection/autoassign/pytorch/mmcv/engine/__init__.py b/cv/detection/autoassign/pytorch/mmcv/engine/__init__.py deleted file mode 100755 index 3193b7f66..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, - single_gpu_test) - -__all__ = [ - 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', - 'single_gpu_test' -] diff --git a/cv/detection/autoassign/pytorch/mmcv/engine/test.py b/cv/detection/autoassign/pytorch/mmcv/engine/test.py deleted file mode 100755 index f236b1cda..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/engine/test.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, data_loader): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - # Assume result has the same length of batch_size - # refer to https://github.com/open-mmlab/mmcv/issues/985 - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting - ``gpu_collect=True``, it encodes results to gpu tensors and use gpu - communication for results collection. On cpu mode it saves the results on - different gpus to ``tmpdir`` and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - if rank == 0: - batch_size = len(result) - batch_size_all = batch_size * world_size - if batch_size_all + prog_bar.completed > len(dataset): - batch_size_all = len(dataset) - prog_bar.completed - for _ in range(batch_size_all): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results under cpu mode. - - On cpu mode, this function will save the results on different gpus to - ``tmpdir`` and collect them by the rank 0 worker. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - tmpdir (str | None): temporal directory for collected results to - store. If set to None, it will create a random temporal directory - for it. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_result = mmcv.load(part_file) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results under gpu mode. - - On gpu mode, this function will encode results to gpu tensors and use gpu - communication for results collection. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/detection/autoassign/pytorch/mmcv/fileio/__init__.py b/cv/detection/autoassign/pytorch/mmcv/fileio/__init__.py deleted file mode 100755 index 2051b85f7..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/fileio/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .file_client import BaseStorageBackend, FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler -from .io import dump, load, register_handler -from .parse import dict_from_file, list_from_file - -__all__ = [ - 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', - 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', - 'list_from_file', 'dict_from_file' -] diff --git a/cv/detection/autoassign/pytorch/mmcv/fileio/file_client.py b/cv/detection/autoassign/pytorch/mmcv/fileio/file_client.py deleted file mode 100755 index b2d622868..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/fileio/file_client.py +++ /dev/null @@ -1,1148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import os -import os.path as osp -import re -import tempfile -import warnings -from abc import ABCMeta, abstractmethod -from contextlib import contextmanager -from pathlib import Path -from typing import Iterable, Iterator, Optional, Tuple, Union -from urllib.request import urlopen - -import mmcv -from mmcv.utils.misc import has_method -from mmcv.utils.path import is_filepath - - -class BaseStorageBackend(metaclass=ABCMeta): - """Abstract class of storage backends. - - All backends need to implement two apis: ``get()`` and ``get_text()``. - ``get()`` reads the file as a byte stream and ``get_text()`` reads the file - as texts. - """ - - # a flag to indicate whether the backend can create a symlink for a file - _allow_symlink = False - - @property - def name(self): - return self.__class__.__name__ - - @property - def allow_symlink(self): - return self._allow_symlink - - @abstractmethod - def get(self, filepath): - pass - - @abstractmethod - def get_text(self, filepath): - pass - - -class CephBackend(BaseStorageBackend): - """Ceph storage backend (for internal use). - - Args: - path_mapping (dict|None): path mapping dict from local path to Petrel - path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` - will be replaced by ``dst``. Default: None. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - """ - - def __init__(self, path_mapping=None): - try: - import ceph - except ImportError: - raise ImportError('Please install ceph to enable CephBackend.') - - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead') - self._client = ceph.S3Client() - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def get(self, filepath): - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class PetrelBackend(BaseStorageBackend): - """Petrel storage backend (for internal use). - - PetrelBackend supports reading and writing data to multiple clusters. - If the file path contains the cluster name, PetrelBackend will read data - from specified cluster or write data to it. Otherwise, PetrelBackend will - access the default cluster. - - Args: - path_mapping (dict, optional): Path mapping dict from local path to - Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in - ``filepath`` will be replaced by ``dst``. Default: None. - enable_mc (bool, optional): Whether to enable memcached support. - Default: True. - - Examples: - >>> filepath1 = 's3://path/of/file' - >>> filepath2 = 'cluster-name:s3://path/of/file' - >>> client = PetrelBackend() - >>> client.get(filepath1) # get data from default cluster - >>> client.get(filepath2) # get data from 'cluster-name' cluster - """ - - def __init__(self, - path_mapping: Optional[dict] = None, - enable_mc: bool = True): - try: - from petrel_client import client - except ImportError: - raise ImportError('Please install petrel_client to enable ' - 'PetrelBackend.') - - self._client = client.Client(enable_mc=enable_mc) - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def _map_path(self, filepath: Union[str, Path]) -> str: - """Map ``filepath`` to a string path whose prefix will be replaced by - :attr:`self.path_mapping`. - - Args: - filepath (str): Path to be mapped. - """ - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - return filepath - - def _format_path(self, filepath: str) -> str: - """Convert a ``filepath`` to standard format of petrel oss. - - If the ``filepath`` is concatenated by ``os.path.join``, in a Windows - environment, the ``filepath`` will be the format of - 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the - above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. - - Args: - filepath (str): Path to be formatted. - """ - return re.sub(r'\\+', '/', filepath) - - def get(self, filepath: Union[str, Path]) -> memoryview: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - memoryview: A memory view of expected bytes object to avoid - copying. The memoryview object can be converted to bytes by - ``value_buf.tobytes()``. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return str(self.get(filepath), encoding=encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Save data to a given ``filepath``. - - Args: - obj (bytes): Data to be saved. - filepath (str or Path): Path to write data. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.put(filepath, obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Save data to a given ``filepath``. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to encode the ``obj``. - Default: 'utf-8'. - """ - self.put(bytes(obj, encoding=encoding), filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - if not has_method(self._client, 'delete'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `delete` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.delete(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - if not (has_method(self._client, 'contains') - and has_method(self._client, 'isdir')): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` and `isdir` methods, please use a higher' - 'version or dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) or self._client.isdir(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - if not has_method(self._client, 'isdir'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `isdir` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - if not has_method(self._client, 'contains'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` method, please use a higher version or ' - 'dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result after concatenation. - """ - filepath = self._format_path(self._map_path(filepath)) - if filepath.endswith('/'): - filepath = filepath[:-1] - formatted_paths = [filepath] - for path in filepaths: - formatted_paths.append(self._format_path(self._map_path(path))) - return '/'.join(formatted_paths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download a file from ``filepath`` and return a temporary path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str | Path): Download a file from ``filepath``. - - Examples: - >>> client = PetrelBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('s3://path/of/your/file') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one temporary path. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - assert self.isfile(filepath) - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - Petrel has no concept of directories but it simulates the directory - hierarchy in the filesystem through public prefixes. In addition, - if the returned path ends with '/', it means the path is a public - prefix which is a logical directory. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - In addition, the returned path of directory will not contains the - suffix '/' which is consistent with other backends. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if not has_method(self._client, 'list'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `list` method, please use a higher version or dev' - ' branch instead.')) - - dir_path = self._map_path(dir_path) - dir_path = self._format_path(dir_path) - if list_dir and suffix is not None: - raise TypeError( - '`list_dir` should be False when `suffix` is not None') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - # Petrel's simulated directory hierarchy assumes that directory paths - # should end with `/` - if not dir_path.endswith('/'): - dir_path += '/' - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for path in self._client.list(dir_path): - # the `self.isdir` is not used here to determine whether path - # is a directory, because `self.isdir` relies on - # `self._client.list` - if path.endswith('/'): # a directory path - next_dir_path = self.join_path(dir_path, path) - if list_dir: - # get the relative path and exclude the last - # character '/' - rel_dir = next_dir_path[len(root):-1] - yield rel_dir - if recursive: - yield from _list_dir_or_file(next_dir_path, list_dir, - list_file, suffix, - recursive) - else: # a file path - absolute_path = self.join_path(dir_path, path) - rel_path = absolute_path[len(root):] - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class MemcachedBackend(BaseStorageBackend): - """Memcached storage backend. - - Attributes: - server_list_cfg (str): Config file for memcached server list. - client_cfg (str): Config file for memcached client. - sys_path (str | None): Additional path to be appended to `sys.path`. - Default: None. - """ - - def __init__(self, server_list_cfg, client_cfg, sys_path=None): - if sys_path is not None: - import sys - sys.path.append(sys_path) - try: - import mc - except ImportError: - raise ImportError( - 'Please install memcached to enable MemcachedBackend.') - - self.server_list_cfg = server_list_cfg - self.client_cfg = client_cfg - self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, - self.client_cfg) - # mc.pyvector servers as a point which points to a memory cache - self._mc_buffer = mc.pyvector() - - def get(self, filepath): - filepath = str(filepath) - import mc - self._client.Get(filepath, self._mc_buffer) - value_buf = mc.ConvertBuffer(self._mc_buffer) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class LmdbBackend(BaseStorageBackend): - """Lmdb storage backend. - - Args: - db_path (str): Lmdb database path. - readonly (bool, optional): Lmdb environment parameter. If True, - disallow any write operations. Default: True. - lock (bool, optional): Lmdb environment parameter. If False, when - concurrent access occurs, do not lock the database. Default: False. - readahead (bool, optional): Lmdb environment parameter. If False, - disable the OS filesystem readahead mechanism, which may improve - random read performance when a database is larger than RAM. - Default: False. - - Attributes: - db_path (str): Lmdb database path. - """ - - def __init__(self, - db_path, - readonly=True, - lock=False, - readahead=False, - **kwargs): - try: - import lmdb - except ImportError: - raise ImportError('Please install lmdb to enable LmdbBackend.') - - self.db_path = str(db_path) - self._client = lmdb.open( - self.db_path, - readonly=readonly, - lock=lock, - readahead=readahead, - **kwargs) - - def get(self, filepath): - """Get values according to the filepath. - - Args: - filepath (str | obj:`Path`): Here, filepath is the lmdb key. - """ - filepath = str(filepath) - with self._client.begin(write=False) as txn: - value_buf = txn.get(filepath.encode('ascii')) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class HardDiskBackend(BaseStorageBackend): - """Raw hard disks storage backend.""" - - _allow_symlink = True - - def get(self, filepath: Union[str, Path]) -> bytes: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes: Expected bytes object. - """ - with open(filepath, 'rb') as f: - value_buf = f.read() - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - with open(filepath, 'r', encoding=encoding) as f: - value_buf = f.read() - return value_buf - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` will create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'wb') as f: - f.write(obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` will create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'w', encoding=encoding) as f: - f.write(obj) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - os.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return osp.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return osp.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return osp.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return osp.join(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, filepath: Union[str, Path]) -> Iterable[Union[str, Path]]: - """Only for unified API and do nothing.""" - yield filepath - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if list_dir and suffix is not None: - raise TypeError('`suffix` should be None when `list_dir` is True') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - elif osp.isdir(entry.path): - if list_dir: - rel_dir = osp.relpath(entry.path, root) - yield rel_dir - if recursive: - yield from _list_dir_or_file(entry.path, list_dir, - list_file, suffix, - recursive) - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class HTTPBackend(BaseStorageBackend): - """HTTP and HTTPS storage bachend.""" - - def get(self, filepath): - value_buf = urlopen(filepath).read() - return value_buf - - def get_text(self, filepath, encoding='utf-8'): - value_buf = urlopen(filepath).read() - return value_buf.decode(encoding) - - @contextmanager - def get_local_path(self, filepath: str) -> Iterable[str]: - """Download a file from ``filepath``. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str): Download a file from ``filepath``. - - Examples: - >>> client = HTTPBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('http://path/of/your/file') as path: - ... # do something here - """ - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - -class FileClient: - """A general file client to access files in different backends. - - The client loads a file or text in a specified backend from its path - and returns it as a binary or text file. There are two ways to choose a - backend, the name of backend and the prefix of path. Although both of them - can be used to choose a storage backend, ``backend`` has a higher priority - that is if they are all set, the storage backend will be chosen by the - backend argument. If they are all `None`, the disk backend will be chosen. - Note that It can also register other backend accessor with a given name, - prefixes, and backend class. In addition, We use the singleton pattern to - avoid repeated object creation. If the arguments are the same, the same - object will be returned. - - Args: - backend (str, optional): The storage backend type. Options are "disk", - "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. - prefix (str, optional): The prefix of the registered storage backend. - Options are "s3", "http", "https". Default: None. - - Examples: - >>> # only set backend - >>> file_client = FileClient(backend='petrel') - >>> # only set prefix - >>> file_client = FileClient(prefix='s3') - >>> # set both backend and prefix but use backend to choose client - >>> file_client = FileClient(backend='petrel', prefix='s3') - >>> # if the arguments are the same, the same object is returned - >>> file_client1 = FileClient(backend='petrel') - >>> file_client1 is file_client - True - - Attributes: - client (:obj:`BaseStorageBackend`): The backend object. - """ - - _backends = { - 'disk': HardDiskBackend, - 'ceph': CephBackend, - 'memcached': MemcachedBackend, - 'lmdb': LmdbBackend, - 'petrel': PetrelBackend, - 'http': HTTPBackend, - } - # This collection is used to record the overridden backends, and when a - # backend appears in the collection, the singleton pattern is disabled for - # that backend, because if the singleton pattern is used, then the object - # returned will be the backend before overwriting - _overridden_backends = set() - _prefix_to_backends = { - 's3': PetrelBackend, - 'http': HTTPBackend, - 'https': HTTPBackend, - } - _overridden_prefixes = set() - - _instances = {} - - def __new__(cls, backend=None, prefix=None, **kwargs): - if backend is None and prefix is None: - backend = 'disk' - if backend is not None and backend not in cls._backends: - raise ValueError( - f'Backend {backend} is not supported. Currently supported ones' - f' are {list(cls._backends.keys())}') - if prefix is not None and prefix not in cls._prefix_to_backends: - raise ValueError( - f'prefix {prefix} is not supported. Currently supported ones ' - f'are {list(cls._prefix_to_backends.keys())}') - - # concatenate the arguments to a unique key for determining whether - # objects with the same arguments were created - arg_key = f'{backend}:{prefix}' - for key, value in kwargs.items(): - arg_key += f':{key}:{value}' - - # if a backend was overridden, it will create a new object - if (arg_key in cls._instances - and backend not in cls._overridden_backends - and prefix not in cls._overridden_prefixes): - _instance = cls._instances[arg_key] - else: - # create a new object and put it to _instance - _instance = super().__new__(cls) - if backend is not None: - _instance.client = cls._backends[backend](**kwargs) - else: - _instance.client = cls._prefix_to_backends[prefix](**kwargs) - - cls._instances[arg_key] = _instance - - return _instance - - @property - def name(self): - return self.client.name - - @property - def allow_symlink(self): - return self.client.allow_symlink - - @staticmethod - def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: - """Parse the prefix of a uri. - - Args: - uri (str | Path): Uri to be parsed that contains the file prefix. - - Examples: - >>> FileClient.parse_uri_prefix('s3://path/of/your/file') - 's3' - - Returns: - str | None: Return the prefix of uri if the uri contains '://' - else ``None``. - """ - assert is_filepath(uri) - uri = str(uri) - if '://' not in uri: - return None - else: - prefix, _ = uri.split('://') - # In the case of PetrelBackend, the prefix may contains the cluster - # name like clusterName:s3 - if ':' in prefix: - _, prefix = prefix.split(':') - return prefix - - @classmethod - def infer_client(cls, - file_client_args: Optional[dict] = None, - uri: Optional[Union[str, Path]] = None) -> 'FileClient': - """Infer a suitable file client based on the URI and arguments. - - Args: - file_client_args (dict, optional): Arguments to instantiate a - FileClient. Default: None. - uri (str | Path, optional): Uri to be parsed that contains the file - prefix. Default: None. - - Examples: - >>> uri = 's3://path/of/your/file' - >>> file_client = FileClient.infer_client(uri=uri) - >>> file_client_args = {'backend': 'petrel'} - >>> file_client = FileClient.infer_client(file_client_args) - - Returns: - FileClient: Instantiated FileClient object. - """ - assert file_client_args is not None or uri is not None - if file_client_args is None: - file_prefix = cls.parse_uri_prefix(uri) # type: ignore - return cls(prefix=file_prefix) - else: - return cls(**file_client_args) - - @classmethod - def _register_backend(cls, name, backend, force=False, prefixes=None): - if not isinstance(name, str): - raise TypeError('the backend name should be a string, ' - f'but got {type(name)}') - if not inspect.isclass(backend): - raise TypeError( - f'backend should be a class but got {type(backend)}') - if not issubclass(backend, BaseStorageBackend): - raise TypeError( - f'backend {backend} is not a subclass of BaseStorageBackend') - if not force and name in cls._backends: - raise KeyError( - f'{name} is already registered as a storage backend, ' - 'add "force=True" if you want to override it') - - if name in cls._backends and force: - cls._overridden_backends.add(name) - cls._backends[name] = backend - - if prefixes is not None: - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if prefix not in cls._prefix_to_backends: - cls._prefix_to_backends[prefix] = backend - elif (prefix in cls._prefix_to_backends) and force: - cls._overridden_prefixes.add(prefix) - cls._prefix_to_backends[prefix] = backend - else: - raise KeyError( - f'{prefix} is already registered as a storage backend,' - ' add "force=True" if you want to override it') - - @classmethod - def register_backend(cls, name, backend=None, force=False, prefixes=None): - """Register a backend to FileClient. - - This method can be used as a normal class method or a decorator. - - .. code-block:: python - - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - FileClient.register_backend('new', NewBackend) - - or - - .. code-block:: python - - @FileClient.register_backend('new') - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - Args: - name (str): The name of the registered backend. - backend (class, optional): The backend class to be registered, - which must be a subclass of :class:`BaseStorageBackend`. - When this method is used as a decorator, backend is None. - Defaults to None. - force (bool, optional): Whether to override the backend if the name - has already been registered. Defaults to False. - prefixes (str or list[str] or tuple[str], optional): The prefixes - of the registered storage backend. Default: None. - `New in version 1.3.15.` - """ - if backend is not None: - cls._register_backend( - name, backend, force=force, prefixes=prefixes) - return - - def _register(backend_cls): - cls._register_backend( - name, backend_cls, force=force, prefixes=prefixes) - return backend_cls - - return _register - - def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: - """Read data from a given ``filepath`` with 'rb' mode. - - Note: - There are two types of return values for ``get``, one is ``bytes`` - and the other is ``memoryview``. The advantage of using memoryview - is that you can avoid copying, and if you want to convert it to - ``bytes``, you can use ``.tobytes()``. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes | memoryview: Expected bytes object or a memory view of the - bytes object. - """ - return self.client.get(filepath) - - def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return self.client.get_text(filepath, encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` should create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - self.client.put(obj, filepath) - - def put_text(self, obj: str, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` should create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str, optional): The encoding format used to open the - `filepath`. Default: 'utf-8'. - """ - self.client.put_text(obj, filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str, Path): Path to be removed. - """ - self.client.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return self.client.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return self.client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return self.client.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return self.client.join_path(filepath, *filepaths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download data from ``filepath`` and write the data to local path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Note: - If the ``filepath`` is a local path, just return itself. - - .. warning:: - ``get_local_path`` is an experimental interface that may change in - the future. - - Args: - filepath (str or Path): Path to be read data. - - Examples: - >>> file_client = FileClient(prefix='s3') - >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one path. - """ - with self.client.get_local_path(str(filepath)) as local_path: - yield local_path - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, - suffix, recursive) diff --git a/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/__init__.py b/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/__init__.py deleted file mode 100755 index aa24d9197..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseFileHandler -from .json_handler import JsonHandler -from .pickle_handler import PickleHandler -from .yaml_handler import YamlHandler - -__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/base.py b/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/base.py deleted file mode 100755 index 288878bc5..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseFileHandler(metaclass=ABCMeta): - # `str_like` is a flag to indicate whether the type of file object is - # str-like object or bytes-like object. Pickle only processes bytes-like - # objects but json only processes str-like object. If it is str-like - # object, `StringIO` will be used to process the buffer. - str_like = True - - @abstractmethod - def load_from_fileobj(self, file, **kwargs): - pass - - @abstractmethod - def dump_to_fileobj(self, obj, file, **kwargs): - pass - - @abstractmethod - def dump_to_str(self, obj, **kwargs): - pass - - def load_from_path(self, filepath, mode='r', **kwargs): - with open(filepath, mode) as f: - return self.load_from_fileobj(f, **kwargs) - - def dump_to_path(self, obj, filepath, mode='w', **kwargs): - with open(filepath, mode) as f: - self.dump_to_fileobj(obj, f, **kwargs) diff --git a/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/json_handler.py b/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/json_handler.py deleted file mode 100755 index 18d4f15f7..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/json_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -import numpy as np - -from .base import BaseFileHandler - - -def set_default(obj): - """Set default json values for non-serializable values. - - It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. - It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, - etc.) into plain numbers of plain python built-in types. - """ - if isinstance(obj, (set, range)): - return list(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, np.generic): - return obj.item() - raise TypeError(f'{type(obj)} is unsupported for json dump') - - -class JsonHandler(BaseFileHandler): - - def load_from_fileobj(self, file): - return json.load(file) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('default', set_default) - json.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('default', set_default) - return json.dumps(obj, **kwargs) diff --git a/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/pickle_handler.py b/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/pickle_handler.py deleted file mode 100755 index b37c79bed..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/pickle_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import pickle - -from .base import BaseFileHandler - - -class PickleHandler(BaseFileHandler): - - str_like = False - - def load_from_fileobj(self, file, **kwargs): - return pickle.load(file, **kwargs) - - def load_from_path(self, filepath, **kwargs): - return super(PickleHandler, self).load_from_path( - filepath, mode='rb', **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('protocol', 2) - return pickle.dumps(obj, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('protocol', 2) - pickle.dump(obj, file, **kwargs) - - def dump_to_path(self, obj, filepath, **kwargs): - super(PickleHandler, self).dump_to_path( - obj, filepath, mode='wb', **kwargs) diff --git a/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/yaml_handler.py b/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/yaml_handler.py deleted file mode 100755 index c5aa2eea1..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/fileio/handlers/yaml_handler.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import yaml - -try: - from yaml import CLoader as Loader, CDumper as Dumper -except ImportError: - from yaml import Loader, Dumper - -from .base import BaseFileHandler # isort:skip - - -class YamlHandler(BaseFileHandler): - - def load_from_fileobj(self, file, **kwargs): - kwargs.setdefault('Loader', Loader) - return yaml.load(file, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('Dumper', Dumper) - yaml.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('Dumper', Dumper) - return yaml.dump(obj, **kwargs) diff --git a/cv/detection/autoassign/pytorch/mmcv/fileio/io.py b/cv/detection/autoassign/pytorch/mmcv/fileio/io.py deleted file mode 100755 index aaefde58a..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/fileio/io.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from io import BytesIO, StringIO -from pathlib import Path - -from ..utils import is_list_of, is_str -from .file_client import FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler - -file_handlers = { - 'json': JsonHandler(), - 'yaml': YamlHandler(), - 'yml': YamlHandler(), - 'pickle': PickleHandler(), - 'pkl': PickleHandler() -} - - -def load(file, file_format=None, file_client_args=None, **kwargs): - """Load data from json/yaml/pickle files. - - This method provides a unified api for loading data from serialized files. - - Note: - In v1.3.16 and later, ``load`` supports loading data from serialized - files those can be storaged in different backends. - - Args: - file (str or :obj:`Path` or file-like object): Filename or a file-like - object. - file_format (str, optional): If not specified, the file format will be - inferred from the file extension, otherwise use the specified one. - Currently supported formats include "json", "yaml/yml" and - "pickle/pkl". - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> load('/path/of/your/file') # file is storaged in disk - >>> load('https://path/of/your/file') # file is storaged in Internet - >>> load('s3://path/of/your/file') # file is storaged in petrel - - Returns: - The content from the file. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None and is_str(file): - file_format = file.split('.')[-1] - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO(file_client.get_text(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - else: - with BytesIO(file_client.get(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - elif hasattr(file, 'read'): - obj = handler.load_from_fileobj(file, **kwargs) - else: - raise TypeError('"file" must be a filepath str or a file-object') - return obj - - -def dump(obj, file=None, file_format=None, file_client_args=None, **kwargs): - """Dump data to json/yaml/pickle strings or files. - - This method provides a unified api for dumping data as strings or to files, - and also supports custom arguments for each file format. - - Note: - In v1.3.16 and later, ``dump`` supports dumping data as strings or to - files which is saved to different backends. - - Args: - obj (any): The python object to be dumped. - file (str or :obj:`Path` or file-like object, optional): If not - specified, then the object is dumped to a str, otherwise to a file - specified by the filename or file-like object. - file_format (str, optional): Same as :func:`load`. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dump('hello world', '/path/of/your/file') # disk - >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel - - Returns: - bool: True for success, False otherwise. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None: - if is_str(file): - file_format = file.split('.')[-1] - elif file is None: - raise ValueError( - 'file_format must be specified since file is None') - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if file is None: - return handler.dump_to_str(obj, **kwargs) - elif is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put_text(f.getvalue(), file) - else: - with BytesIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put(f.getvalue(), file) - elif hasattr(file, 'write'): - handler.dump_to_fileobj(obj, file, **kwargs) - else: - raise TypeError('"file" must be a filename str or a file-object') - - -def _register_handler(handler, file_formats): - """Register a handler for some file extensions. - - Args: - handler (:obj:`BaseFileHandler`): Handler to be registered. - file_formats (str or list[str]): File formats to be handled by this - handler. - """ - if not isinstance(handler, BaseFileHandler): - raise TypeError( - f'handler must be a child of BaseFileHandler, not {type(handler)}') - if isinstance(file_formats, str): - file_formats = [file_formats] - if not is_list_of(file_formats, str): - raise TypeError('file_formats must be a str or a list of str') - for ext in file_formats: - file_handlers[ext] = handler - - -def register_handler(file_formats, **kwargs): - - def wrap(cls): - _register_handler(cls(**kwargs), file_formats) - return cls - - return wrap diff --git a/cv/detection/autoassign/pytorch/mmcv/fileio/parse.py b/cv/detection/autoassign/pytorch/mmcv/fileio/parse.py deleted file mode 100755 index f60f0d611..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/fileio/parse.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from io import StringIO - -from .file_client import FileClient - - -def list_from_file(filename, - prefix='', - offset=0, - max_num=0, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a list of strings. - - Note: - In v1.3.16 and later, ``list_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a list for strings. - - Args: - filename (str): Filename. - prefix (str): The prefix to be inserted to the beginning of each item. - offset (int): The offset of lines. - max_num (int): The maximum number of lines to be read, - zeros and negatives mean no limitation. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> list_from_file('/path/of/your/file') # disk - ['hello', 'world'] - >>> list_from_file('s3://path/of/your/file') # ceph or petrel - ['hello', 'world'] - - Returns: - list[str]: A list of strings. - """ - cnt = 0 - item_list = [] - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for _ in range(offset): - f.readline() - for line in f: - if 0 < max_num <= cnt: - break - item_list.append(prefix + line.rstrip('\n\r')) - cnt += 1 - return item_list - - -def dict_from_file(filename, - key_type=str, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a dict. - - Each line of the text file will be two or more columns split by - whitespaces or tabs. The first column will be parsed as dict keys, and - the following columns will be parsed as dict values. - - Note: - In v1.3.16 and later, ``dict_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a dict. - - Args: - filename(str): Filename. - key_type(type): Type of the dict keys. str is user by default and - type conversion will be performed if specified. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dict_from_file('/path/of/your/file') # disk - {'key1': 'value1', 'key2': 'value2'} - >>> dict_from_file('s3://path/of/your/file') # ceph or petrel - {'key1': 'value1', 'key2': 'value2'} - - Returns: - dict: The parsed contents. - """ - mapping = {} - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for line in f: - items = line.rstrip('\n').split() - assert len(items) >= 2 - key = key_type(items[0]) - val = items[1:] if len(items) > 2 else items[1] - mapping[key] = val - return mapping diff --git a/cv/detection/autoassign/pytorch/mmcv/image/__init__.py b/cv/detection/autoassign/pytorch/mmcv/image/__init__.py deleted file mode 100755 index d0051d609..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/image/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, - gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, - rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) -from .geometric import (cutout, imcrop, imflip, imflip_, impad, - impad_to_multiple, imrescale, imresize, imresize_like, - imresize_to_multiple, imrotate, imshear, imtranslate, - rescale_size) -from .io import imfrombytes, imread, imwrite, supported_backends, use_backend -from .misc import tensor2imgs -from .photometric import (adjust_brightness, adjust_color, adjust_contrast, - adjust_lighting, adjust_sharpness, auto_contrast, - clahe, imdenormalize, imequalize, iminvert, - imnormalize, imnormalize_, lut_transform, posterize, - solarize) - -__all__ = [ - 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', - 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', - 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', - 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', - 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', - 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', - 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', - 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', - 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', - 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting' -] diff --git a/cv/detection/autoassign/pytorch/mmcv/image/colorspace.py b/cv/detection/autoassign/pytorch/mmcv/image/colorspace.py deleted file mode 100755 index 814533952..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/image/colorspace.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def imconvert(img, src, dst): - """Convert an image from the src colorspace to dst colorspace. - - Args: - img (ndarray): The input image. - src (str): The source colorspace, e.g., 'rgb', 'hsv'. - dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. - - Returns: - ndarray: The converted image. - """ - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - out_img = cv2.cvtColor(img, code) - return out_img - - -def bgr2gray(img, keepdim=False): - """Convert a BGR image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def rgb2gray(img, keepdim=False): - """Convert a RGB image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def gray2bgr(img): - """Convert a grayscale image to BGR image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted BGR image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return out_img - - -def gray2rgb(img): - """Convert a grayscale image to RGB image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted RGB image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) - return out_img - - -def _convert_input_type_range(img): - """Convert the type and range of the input image. - - It converts the input image to np.float32 type and range of [0, 1]. - It is mainly used for pre-processing the input image in colorspace - conversion functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - (ndarray): The converted image with type of np.float32 and range of - [0, 1]. - """ - img_type = img.dtype - img = img.astype(np.float32) - if img_type == np.float32: - pass - elif img_type == np.uint8: - img /= 255. - else: - raise TypeError('The img type should be np.float32 or np.uint8, ' - f'but got {img_type}') - return img - - -def _convert_output_type_range(img, dst_type): - """Convert the type and range of the image according to dst_type. - - It converts the image to desired type and range. If `dst_type` is np.uint8, - images will be converted to np.uint8 type with range [0, 255]. If - `dst_type` is np.float32, it converts the image to np.float32 type with - range [0, 1]. - It is mainly used for post-processing images in colorspace conversion - functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The image to be converted with np.float32 type and - range [0, 255]. - dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it - converts the image to np.uint8 type with range [0, 255]. If - dst_type is np.float32, it converts the image to np.float32 type - with range [0, 1]. - - Returns: - (ndarray): The converted image with desired type and range. - """ - if dst_type not in (np.uint8, np.float32): - raise TypeError('The dst_type should be np.float32 or np.uint8, ' - f'but got {dst_type}') - if dst_type == np.uint8: - img = img.round() - else: - img /= 255. - return img.astype(dst_type) - - -def rgb2ycbcr(img, y_only=False): - """Convert a RGB image to YCbCr image. - - This function produces the same results as Matlab's `rgb2ycbcr` function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 - else: - out_img = np.matmul( - img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], - [24.966, 112.0, -18.214]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def bgr2ycbcr(img, y_only=False): - """Convert a BGR image to YCbCr image. - - The bgr version of rgb2ycbcr. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 - else: - out_img = np.matmul( - img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], - [65.481, -37.797, 112.0]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2rgb(img): - """Convert a YCbCr image to RGB image. - - This function produces the same results as Matlab's ycbcr2rgb function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted RGB image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0, -0.00153632, 0.00791071], - [0.00625893, -0.00318811, 0]]) * 255.0 + [ - -222.921, 135.576, -276.836 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2bgr(img): - """Convert a YCbCr image to BGR image. - - The bgr version of ycbcr2rgb. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted BGR image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0.00791071, -0.00153632, 0], - [0, -0.00318811, 0.00625893]]) * 255.0 + [ - -276.836, 135.576, -222.921 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def convert_color_factory(src, dst): - - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - - def convert_color(img): - out_img = cv2.cvtColor(img, code) - return out_img - - convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} - image. - - Args: - img (ndarray or str): The input image. - - Returns: - ndarray: The converted {dst.upper()} image. - """ - - return convert_color - - -bgr2rgb = convert_color_factory('bgr', 'rgb') - -rgb2bgr = convert_color_factory('rgb', 'bgr') - -bgr2hsv = convert_color_factory('bgr', 'hsv') - -hsv2bgr = convert_color_factory('hsv', 'bgr') - -bgr2hls = convert_color_factory('bgr', 'hls') - -hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/cv/detection/autoassign/pytorch/mmcv/image/geometric.py b/cv/detection/autoassign/pytorch/mmcv/image/geometric.py deleted file mode 100755 index cf97c201c..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/image/geometric.py +++ /dev/null @@ -1,728 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers - -import cv2 -import numpy as np - -from ..utils import to_2tuple -from .io import imread_backend - -try: - from PIL import Image -except ImportError: - Image = None - - -def _scale_size(size, scale): - """Rescale a size by a ratio. - - Args: - size (tuple[int]): (w, h). - scale (float | tuple(float)): Scaling factor. - - Returns: - tuple[int]: scaled size. - """ - if isinstance(scale, (float, int)): - scale = (scale, scale) - w, h = size - return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) - - -cv2_interp_codes = { - 'nearest': cv2.INTER_NEAREST, - 'bilinear': cv2.INTER_LINEAR, - 'bicubic': cv2.INTER_CUBIC, - 'area': cv2.INTER_AREA, - 'lanczos': cv2.INTER_LANCZOS4 -} - -if Image is not None: - pillow_interp_codes = { - 'nearest': Image.NEAREST, - 'bilinear': Image.BILINEAR, - 'bicubic': Image.BICUBIC, - 'box': Image.BOX, - 'lanczos': Image.LANCZOS, - 'hamming': Image.HAMMING - } - - -def imresize(img, - size, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image to a given size. - - Args: - img (ndarray): The input image. - size (tuple[int]): Target size (w, h). - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if backend is None: - backend = imread_backend - if backend not in ['cv2', 'pillow']: - raise ValueError(f'backend: {backend} is not supported for resize.' - f"Supported backends are 'cv2', 'pillow'") - - if backend == 'pillow': - assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' - pil_image = Image.fromarray(img) - pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) - resized_img = np.array(pil_image) - else: - resized_img = cv2.resize( - img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) - if not return_scale: - return resized_img - else: - w_scale = size[0] / w - h_scale = size[1] / h - return resized_img, w_scale, h_scale - - -def imresize_to_multiple(img, - divisor, - size=None, - scale_factor=None, - keep_ratio=False, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image according to a given size or scale factor and then rounds - up the the resized or rescaled image size to the nearest value that can be - divided by the divisor. - - Args: - img (ndarray): The input image. - divisor (int | tuple): Resized image size will be a multiple of - divisor. If divisor is a tuple, divisor should be - (w_divisor, h_divisor). - size (None | int | tuple[int]): Target size (w, h). Default: None. - scale_factor (None | float | tuple[float]): Multiplier for spatial - size. Should match input size if it is a tuple and the 2D style is - (w_scale_factor, h_scale_factor). Default: None. - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: False. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if size is not None and scale_factor is not None: - raise ValueError('only one of size or scale_factor should be defined') - elif size is None and scale_factor is None: - raise ValueError('one of size or scale_factor should be defined') - elif size is not None: - size = to_2tuple(size) - if keep_ratio: - size = rescale_size((w, h), size, return_scale=False) - else: - size = _scale_size((w, h), scale_factor) - - divisor = to_2tuple(divisor) - size = tuple([int(np.ceil(s / d)) * d for s, d in zip(size, divisor)]) - resized_img, w_scale, h_scale = imresize( - img, - size, - return_scale=True, - interpolation=interpolation, - out=out, - backend=backend) - if return_scale: - return resized_img, w_scale, h_scale - else: - return resized_img - - -def imresize_like(img, - dst_img, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image to the same size of a given image. - - Args: - img (ndarray): The input image. - dst_img (ndarray): The target image. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = dst_img.shape[:2] - return imresize(img, (w, h), return_scale, interpolation, backend=backend) - - -def rescale_size(old_size, scale, return_scale=False): - """Calculate the new size to be rescaled to. - - Args: - old_size (tuple[int]): The old size (w, h) of image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image size. - - Returns: - tuple[int]: The new rescaled image size. - """ - w, h = old_size - if isinstance(scale, (float, int)): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - scale_factor = scale - elif isinstance(scale, tuple): - max_long_edge = max(scale) - max_short_edge = min(scale) - scale_factor = min(max_long_edge / max(h, w), - max_short_edge / min(h, w)) - else: - raise TypeError( - f'Scale must be a number or tuple of int, but got {type(scale)}') - - new_size = _scale_size((w, h), scale_factor) - - if return_scale: - return new_size, scale_factor - else: - return new_size - - -def imrescale(img, - scale, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image while keeping the aspect ratio. - - Args: - img (ndarray): The input image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - ndarray: The rescaled image. - """ - h, w = img.shape[:2] - new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) - rescaled_img = imresize( - img, new_size, interpolation=interpolation, backend=backend) - if return_scale: - return rescaled_img, scale_factor - else: - return rescaled_img - - -def imflip(img, direction='horizontal'): - """Flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image. - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return np.flip(img, axis=1) - elif direction == 'vertical': - return np.flip(img, axis=0) - else: - return np.flip(img, axis=(0, 1)) - - -def imflip_(img, direction='horizontal'): - """Inplace flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image (inplace). - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return cv2.flip(img, 1, img) - elif direction == 'vertical': - return cv2.flip(img, 0, img) - else: - return cv2.flip(img, -1, img) - - -def imrotate(img, - angle, - center=None, - scale=1.0, - border_value=0, - interpolation='bilinear', - auto_bound=False): - """Rotate an image. - - Args: - img (ndarray): Image to be rotated. - angle (float): Rotation angle in degrees, positive values mean - clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. - scale (float): Isotropic scale factor. - border_value (int): Border value. - interpolation (str): Same as :func:`resize`. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. - - Returns: - ndarray: The rotated image. - """ - if center is not None and auto_bound: - raise ValueError('`auto_bound` conflicts with `center`') - h, w = img.shape[:2] - if center is None: - center = ((w - 1) * 0.5, (h - 1) * 0.5) - assert isinstance(center, tuple) - - matrix = cv2.getRotationMatrix2D(center, -angle, scale) - if auto_bound: - cos = np.abs(matrix[0, 0]) - sin = np.abs(matrix[0, 1]) - new_w = h * sin + w * cos - new_h = h * cos + w * sin - matrix[0, 2] += (new_w - w) * 0.5 - matrix[1, 2] += (new_h - h) * 0.5 - w = int(np.round(new_w)) - h = int(np.round(new_h)) - rotated = cv2.warpAffine( - img, - matrix, (w, h), - flags=cv2_interp_codes[interpolation], - borderValue=border_value) - return rotated - - -def bbox_clip(bboxes, img_shape): - """Clip bboxes to fit the image shape. - - Args: - bboxes (ndarray): Shape (..., 4*k) - img_shape (tuple[int]): (height, width) of the image. - - Returns: - ndarray: Clipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) - cmin[0::2] = img_shape[1] - 1 - cmin[1::2] = img_shape[0] - 1 - clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) - return clipped_bboxes - - -def bbox_scaling(bboxes, scale, clip_shape=None): - """Scaling bboxes w.r.t the box center. - - Args: - bboxes (ndarray): Shape(..., 4). - scale (float): Scaling factor. - clip_shape (tuple[int], optional): If specified, bboxes that exceed the - boundary will be clipped according to the given shape (h, w). - - Returns: - ndarray: Scaled bboxes. - """ - if float(scale) == 1.0: - scaled_bboxes = bboxes.copy() - else: - w = bboxes[..., 2] - bboxes[..., 0] + 1 - h = bboxes[..., 3] - bboxes[..., 1] + 1 - dw = (w * (scale - 1)) * 0.5 - dh = (h * (scale - 1)) * 0.5 - scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) - if clip_shape is not None: - return bbox_clip(scaled_bboxes, clip_shape) - else: - return scaled_bboxes - - -def imcrop(img, bboxes, scale=1.0, pad_fill=None): - """Crop image patches. - - 3 steps: scale the bboxes -> clip bboxes -> crop and pad. - - Args: - img (ndarray): Image to be cropped. - bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. - scale (float, optional): Scale ratio of bboxes, the default value - 1.0 means no padding. - pad_fill (Number | list[Number]): Value to be filled for padding. - Default: None, which means no padding. - - Returns: - list[ndarray] | ndarray: The cropped image patches. - """ - chn = 1 if img.ndim == 2 else img.shape[2] - if pad_fill is not None: - if isinstance(pad_fill, (int, float)): - pad_fill = [pad_fill for _ in range(chn)] - assert len(pad_fill) == chn - - _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes - scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) - clipped_bbox = bbox_clip(scaled_bboxes, img.shape) - - patches = [] - for i in range(clipped_bbox.shape[0]): - x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) - if pad_fill is None: - patch = img[y1:y2 + 1, x1:x2 + 1, ...] - else: - _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) - if chn == 1: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) - else: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) - patch = np.array( - pad_fill, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - x_start = 0 if _x1 >= 0 else -_x1 - y_start = 0 if _y1 >= 0 else -_y1 - w = x2 - x1 + 1 - h = y2 - y1 + 1 - patch[y_start:y_start + h, x_start:x_start + w, - ...] = img[y1:y1 + h, x1:x1 + w, ...] - patches.append(patch) - - if bboxes.ndim == 1: - return patches[0] - else: - return patches - - -def impad(img, - *, - shape=None, - padding=None, - pad_val=0, - padding_mode='constant'): - """Pad the given image to a certain shape or pad on all sides with - specified padding mode and padding value. - - Args: - img (ndarray): Image to be padded. - shape (tuple[int]): Expected padding shape (h, w). Default: None. - padding (int or tuple[int]): Padding on each border. If a single int is - provided this is used to pad all borders. If tuple of length 2 is - provided this is the padding on left/right and top/bottom - respectively. If a tuple of length 4 is provided this is the - padding for the left, top, right and bottom borders respectively. - Default: None. Note that `shape` and `padding` can not be both - set. - pad_val (Number | Sequence[Number]): Values to be filled in padding - areas when padding_mode is 'constant'. Default: 0. - padding_mode (str): Type of padding. Should be: constant, edge, - reflect or symmetric. Default: constant. - - - constant: pads with a constant value, this value is specified - with pad_val. - - edge: pads with the last value at the edge of the image. - - reflect: pads with reflection of image without repeating the - last value on the edge. For example, padding [1, 2, 3, 4] - with 2 elements on both sides in reflect mode will result - in [3, 2, 1, 2, 3, 4, 3, 2]. - - symmetric: pads with reflection of image repeating the last - value on the edge. For example, padding [1, 2, 3, 4] with - 2 elements on both sides in symmetric mode will result in - [2, 1, 1, 2, 3, 4, 4, 3] - - Returns: - ndarray: The padded image. - """ - - assert (shape is not None) ^ (padding is not None) - if shape is not None: - padding = (0, 0, shape[1] - img.shape[1], shape[0] - img.shape[0]) - - # check pad_val - if isinstance(pad_val, tuple): - assert len(pad_val) == img.shape[-1] - elif not isinstance(pad_val, numbers.Number): - raise TypeError('pad_val must be a int or a tuple. ' - f'But received {type(pad_val)}') - - # check padding - if isinstance(padding, tuple) and len(padding) in [2, 4]: - if len(padding) == 2: - padding = (padding[0], padding[1], padding[0], padding[1]) - elif isinstance(padding, numbers.Number): - padding = (padding, padding, padding, padding) - else: - raise ValueError('Padding must be a int or a 2, or 4 element tuple.' - f'But received {padding}') - - # check padding mode - assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] - - border_type = { - 'constant': cv2.BORDER_CONSTANT, - 'edge': cv2.BORDER_REPLICATE, - 'reflect': cv2.BORDER_REFLECT_101, - 'symmetric': cv2.BORDER_REFLECT - } - img = cv2.copyMakeBorder( - img, - padding[1], - padding[3], - padding[0], - padding[2], - border_type[padding_mode], - value=pad_val) - - return img - - -def impad_to_multiple(img, divisor, pad_val=0): - """Pad an image to ensure each edge to be multiple to some number. - - Args: - img (ndarray): Image to be padded. - divisor (int): Padded image edges will be multiple to divisor. - pad_val (Number | Sequence[Number]): Same as :func:`impad`. - - Returns: - ndarray: The padded image. - """ - pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor - pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor - return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) - - -def cutout(img, shape, pad_val=0): - """Randomly cut out a rectangle from the original img. - - Args: - img (ndarray): Image to be cutout. - shape (int | tuple[int]): Expected cutout shape (h, w). If given as a - int, the value will be used for both h and w. - pad_val (int | float | tuple[int | float]): Values to be filled in the - cut area. Defaults to 0. - - Returns: - ndarray: The cutout image. - """ - - channels = 1 if img.ndim == 2 else img.shape[2] - if isinstance(shape, int): - cut_h, cut_w = shape, shape - else: - assert isinstance(shape, tuple) and len(shape) == 2, \ - f'shape must be a int or a tuple with length 2, but got type ' \ - f'{type(shape)} instead.' - cut_h, cut_w = shape - if isinstance(pad_val, (int, float)): - pad_val = tuple([pad_val] * channels) - elif isinstance(pad_val, tuple): - assert len(pad_val) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(pad_val), channels) - else: - raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') - - img_h, img_w = img.shape[:2] - y0 = np.random.uniform(img_h) - x0 = np.random.uniform(img_w) - - y1 = int(max(0, y0 - cut_h / 2.)) - x1 = int(max(0, x0 - cut_w / 2.)) - y2 = min(img_h, y1 + cut_h) - x2 = min(img_w, x1 + cut_w) - - if img.ndim == 2: - patch_shape = (y2 - y1, x2 - x1) - else: - patch_shape = (y2 - y1, x2 - x1, channels) - - img_cutout = img.copy() - patch = np.array( - pad_val, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - img_cutout[y1:y2, x1:x2, ...] = patch - - return img_cutout - - -def _get_shear_matrix(magnitude, direction='horizontal'): - """Generate the shear matrix for transformation. - - Args: - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - - Returns: - ndarray: The shear matrix with dtype float32. - """ - if direction == 'horizontal': - shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) - elif direction == 'vertical': - shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) - return shear_matrix - - -def imshear(img, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear an image. - - Args: - img (ndarray): Image to be sheared with format (h, w) - or (h, w, c). - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The sheared image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`') - shear_matrix = _get_shear_matrix(magnitude, direction) - sheared = cv2.warpAffine( - img, - shear_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. shearing masks whose channels large - # than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return sheared - - -def _get_translate_matrix(offset, direction='horizontal'): - """Generate the translate matrix. - - Args: - offset (int | float): The offset used for translate. - direction (str): The translate direction, either - "horizontal" or "vertical". - - Returns: - ndarray: The translate matrix with dtype float32. - """ - if direction == 'horizontal': - translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) - elif direction == 'vertical': - translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) - return translate_matrix - - -def imtranslate(img, - offset, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Translate an image. - - Args: - img (ndarray): Image to be translated with format - (h, w) or (h, w, c). - offset (int | float): The offset used for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The translated image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`.') - translate_matrix = _get_translate_matrix(offset, direction) - translated = cv2.warpAffine( - img, - translate_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. translating masks whose channels - # large than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return translated diff --git a/cv/detection/autoassign/pytorch/mmcv/image/io.py b/cv/detection/autoassign/pytorch/mmcv/image/io.py deleted file mode 100755 index d47aaa845..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/image/io.py +++ /dev/null @@ -1,258 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os.path as osp -from pathlib import Path - -import cv2 -import numpy as np -from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, - IMREAD_UNCHANGED) - -from mmcv.utils import check_file_exist, is_str, mkdir_or_exist - -try: - from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG -except ImportError: - TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None - -try: - from PIL import Image, ImageOps -except ImportError: - Image = None - -try: - import tifffile -except ImportError: - tifffile = None - -jpeg = None -supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] - -imread_flags = { - 'color': IMREAD_COLOR, - 'grayscale': IMREAD_GRAYSCALE, - 'unchanged': IMREAD_UNCHANGED, - 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, - 'grayscale_ignore_orientation': - IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE -} - -imread_backend = 'cv2' - - -def use_backend(backend): - """Select a backend for image decoding. - - Args: - backend (str): The image decoding backend type. Options are `cv2`, - `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) - and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` - file format. - """ - assert backend in supported_backends - global imread_backend - imread_backend = backend - if imread_backend == 'turbojpeg': - if TurboJPEG is None: - raise ImportError('`PyTurboJPEG` is not installed') - global jpeg - if jpeg is None: - jpeg = TurboJPEG() - elif imread_backend == 'pillow': - if Image is None: - raise ImportError('`Pillow` is not installed') - elif imread_backend == 'tifffile': - if tifffile is None: - raise ImportError('`tifffile` is not installed') - - -def _jpegflag(flag='color', channel_order='bgr'): - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'color': - if channel_order == 'bgr': - return TJPF_BGR - elif channel_order == 'rgb': - return TJCS_RGB - elif flag == 'grayscale': - return TJPF_GRAY - else: - raise ValueError('flag must be "color" or "grayscale"') - - -def _pillow2array(img, flag='color', channel_order='bgr'): - """Convert a pillow image to numpy array. - - Args: - img (:obj:`PIL.Image.Image`): The image loaded using PIL - flag (str): Flags specifying the color type of a loaded image, - candidates are 'color', 'grayscale' and 'unchanged'. - Default to 'color'. - channel_order (str): The channel order of the output image array, - candidates are 'bgr' and 'rgb'. Default to 'bgr'. - - Returns: - np.ndarray: The converted numpy array - """ - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'unchanged': - array = np.array(img) - if array.ndim >= 3 and array.shape[2] >= 3: # color image - array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR - else: - # Handle exif orientation tag - if flag in ['color', 'grayscale']: - img = ImageOps.exif_transpose(img) - # If the image mode is not 'RGB', convert it to 'RGB' first. - if img.mode != 'RGB': - if img.mode != 'LA': - # Most formats except 'LA' can be directly converted to RGB - img = img.convert('RGB') - else: - # When the mode is 'LA', the default conversion will fill in - # the canvas with black, which sometimes shadows black objects - # in the foreground. - # - # Therefore, a random color (124, 117, 104) is used for canvas - img_rgba = img.convert('RGBA') - img = Image.new('RGB', img_rgba.size, (124, 117, 104)) - img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha - if flag in ['color', 'color_ignore_orientation']: - array = np.array(img) - if channel_order != 'rgb': - array = array[:, :, ::-1] # RGB to BGR - elif flag in ['grayscale', 'grayscale_ignore_orientation']: - img = img.convert('L') - array = np.array(img) - else: - raise ValueError( - 'flag must be "color", "grayscale", "unchanged", ' - f'"color_ignore_orientation" or "grayscale_ignore_orientation"' - f' but got {flag}') - return array - - -def imread(img_or_path, flag='color', channel_order='bgr', backend=None): - """Read an image. - - Args: - img_or_path (ndarray or str or Path): Either a numpy array or str or - pathlib.Path. If it is a numpy array (loaded image), then - it will be returned as is. - flag (str): Flags specifying the color type of a loaded image, - candidates are `color`, `grayscale`, `unchanged`, - `color_ignore_orientation` and `grayscale_ignore_orientation`. - By default, `cv2` and `pillow` backend would rotate the image - according to its EXIF info unless called with `unchanged` or - `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend - always ignore image's EXIF info regardless of the flag. - The `turbojpeg` backend only supports `color` and `grayscale`. - channel_order (str): Order of channel, candidates are `bgr` and `rgb`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. - If backend is None, the global imread_backend specified by - ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - ndarray: Loaded image array. - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError(f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow'") - if isinstance(img_or_path, Path): - img_or_path = str(img_or_path) - - if isinstance(img_or_path, np.ndarray): - return img_or_path - elif is_str(img_or_path): - check_file_exist(img_or_path, - f'img file does not exist: {img_or_path}') - if backend == 'turbojpeg': - with open(img_or_path, 'rb') as in_file: - img = jpeg.decode(in_file.read(), - _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - img = Image.open(img_or_path) - img = _pillow2array(img, flag, channel_order) - return img - elif backend == 'tifffile': - img = tifffile.imread(img_or_path) - return img - else: - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imread(img_or_path, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - else: - raise TypeError('"img" must be a numpy array or a str or ' - 'a pathlib.Path object') - - -def imfrombytes(content, flag='color', channel_order='bgr', backend=None): - """Read an image from bytes. - - Args: - content (bytes): Image bytes got from files or other streams. - flag (str): Same as :func:`imread`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `None`. If backend is None, the - global imread_backend specified by ``mmcv.use_backend()`` will be - used. Default: None. - - Returns: - ndarray: Loaded image array. - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError(f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow'") - if backend == 'turbojpeg': - img = jpeg.decode(content, _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - buff = io.BytesIO(content) - img = Image.open(buff) - img = _pillow2array(img, flag, channel_order) - return img - else: - img_np = np.frombuffer(content, np.uint8) - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imdecode(img_np, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - - -def imwrite(img, file_path, params=None, auto_mkdir=True): - """Write image to file. - - Args: - img (ndarray): Image array to be written. - file_path (str): Image file path. - params (None or list): Same as opencv :func:`imwrite` interface. - auto_mkdir (bool): If the parent folder of `file_path` does not exist, - whether to create it automatically. - - Returns: - bool: Successful or not. - """ - if auto_mkdir: - dir_name = osp.abspath(osp.dirname(file_path)) - mkdir_or_exist(dir_name) - return cv2.imwrite(file_path, img, params) diff --git a/cv/detection/autoassign/pytorch/mmcv/image/misc.py b/cv/detection/autoassign/pytorch/mmcv/image/misc.py deleted file mode 100755 index dfc4a9c6e..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/image/misc.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - -import mmcv - -try: - import torch -except ImportError: - torch = None - - -def tensor2imgs(tensor, mean=(0, 0, 0), std=(1, 1, 1), to_rgb=True): - """Convert tensor to 3-channel images. - - Args: - tensor (torch.Tensor): Tensor that contains multiple images, shape ( - N, C, H, W). - mean (tuple[float], optional): Mean of images. Defaults to (0, 0, 0). - std (tuple[float], optional): Standard deviation of images. - Defaults to (1, 1, 1). - to_rgb (bool, optional): Whether the tensor was converted to RGB - format in the first place. If so, convert it back to BGR. - Defaults to True. - - Returns: - list[np.ndarray]: A list that contains multiple images. - """ - - if torch is None: - raise RuntimeError('pytorch is not installed') - assert torch.is_tensor(tensor) and tensor.ndim == 4 - assert len(mean) == 3 - assert len(std) == 3 - - num_imgs = tensor.size(0) - mean = np.array(mean, dtype=np.float32) - std = np.array(std, dtype=np.float32) - imgs = [] - for img_id in range(num_imgs): - img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) - img = mmcv.imdenormalize( - img, mean, std, to_bgr=to_rgb).astype(np.uint8) - imgs.append(np.ascontiguousarray(img)) - return imgs diff --git a/cv/detection/autoassign/pytorch/mmcv/image/photometric.py b/cv/detection/autoassign/pytorch/mmcv/image/photometric.py deleted file mode 100755 index 5085d0120..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/image/photometric.py +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - -from ..utils import is_tuple_of -from .colorspace import bgr2gray, gray2bgr - - -def imnormalize(img, mean, std, to_rgb=True): - """Normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - img = img.copy().astype(np.float32) - return imnormalize_(img, mean, std, to_rgb) - - -def imnormalize_(img, mean, std, to_rgb=True): - """Inplace normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - # cv2 inplace normalization does not accept uint8 - assert img.dtype != np.uint8 - mean = np.float64(mean.reshape(1, -1)) - stdinv = 1 / np.float64(std.reshape(1, -1)) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - cv2.subtract(img, mean, img) # inplace - cv2.multiply(img, stdinv, img) # inplace - return img - - -def imdenormalize(img, mean, std, to_bgr=True): - assert img.dtype != np.uint8 - mean = mean.reshape(1, -1).astype(np.float64) - std = std.reshape(1, -1).astype(np.float64) - img = cv2.multiply(img, std) # make a copy - cv2.add(img, mean, img) # inplace - if to_bgr: - cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace - return img - - -def iminvert(img): - """Invert (negate) an image. - - Args: - img (ndarray): Image to be inverted. - - Returns: - ndarray: The inverted image. - """ - return np.full_like(img, 255) - img - - -def solarize(img, thr=128): - """Solarize an image (invert all pixel values above a threshold) - - Args: - img (ndarray): Image to be solarized. - thr (int): Threshold for solarizing (0 - 255). - - Returns: - ndarray: The solarized image. - """ - img = np.where(img < thr, img, 255 - img) - return img - - -def posterize(img, bits): - """Posterize an image (reduce the number of bits for each color channel) - - Args: - img (ndarray): Image to be posterized. - bits (int): Number of bits (1 to 8) to use for posterizing. - - Returns: - ndarray: The posterized image. - """ - shift = 8 - bits - img = np.left_shift(np.right_shift(img, shift), shift) - return img - - -def adjust_color(img, alpha=1, beta=None, gamma=0): - r"""It blends the source image and its gray image: - - .. math:: - output = img * alpha + gray\_img * beta + gamma - - Args: - img (ndarray): The input source image. - alpha (int | float): Weight for the source image. Default 1. - beta (int | float): Weight for the converted gray image. - If None, it's assigned the value (1 - `alpha`). - gamma (int | float): Scalar added to each sum. - Same as :func:`cv2.addWeighted`. Default 0. - - Returns: - ndarray: Colored image which has the same size and dtype as input. - """ - gray_img = bgr2gray(img) - gray_img = np.tile(gray_img[..., None], [1, 1, 3]) - if beta is None: - beta = 1 - alpha - colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) - if not colored_img.dtype == np.uint8: - # Note when the dtype of `img` is not the default `np.uint8` - # (e.g. np.float32), the value in `colored_img` got from cv2 - # is not guaranteed to be in range [0, 255], so here clip - # is needed. - colored_img = np.clip(colored_img, 0, 255) - return colored_img - - -def imequalize(img): - """Equalize the image histogram. - - This function applies a non-linear mapping to the input image, - in order to create a uniform distribution of grayscale values - in the output image. - - Args: - img (ndarray): Image to be equalized. - - Returns: - ndarray: The equalized image. - """ - - def _scale_channel(im, c): - """Scale the data in the corresponding channel.""" - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # For computing the step, filter out the nonzeros. - nonzero_histo = histo[histo > 0] - step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 - if not step: - lut = np.array(range(256)) - else: - # Compute the cumulative sum, shifted by step // 2 - # and then normalized by step. - lut = (np.cumsum(histo) + (step // 2)) // step - # Shift lut, prepending with 0. - lut = np.concatenate([[0], lut[:-1]], 0) - # handle potential integer overflow - lut[lut > 255] = 255 - # If step is zero, return the original image. - # Otherwise, index from lut. - return np.where(np.equal(step, 0), im, lut[im]) - - # Scales each channel independently and then stacks - # the result. - s1 = _scale_channel(img, 0) - s2 = _scale_channel(img, 1) - s3 = _scale_channel(img, 2) - equalized_img = np.stack([s1, s2, s3], axis=-1) - return equalized_img.astype(img.dtype) - - -def adjust_brightness(img, factor=1.): - """Adjust image brightness. - - This function controls the brightness of an image. An - enhancement factor of 0.0 gives a black image. - A factor of 1.0 gives the original image. This function - blends the source image and the degenerated black image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be brightened. - factor (float): A value controls the enhancement. - Factor 1.0 returns the original image, lower - factors mean less color (brightness, contrast, - etc), and higher values more. Default 1. - - Returns: - ndarray: The brightened image. - """ - degenerated = np.zeros_like(img) - # Note manually convert the dtype to np.float32, to - # achieve as close results as PIL.ImageEnhance.Brightness. - # Set beta=1-factor, and gamma=0 - brightened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - brightened_img = np.clip(brightened_img, 0, 255) - return brightened_img.astype(img.dtype) - - -def adjust_contrast(img, factor=1.): - """Adjust image contrast. - - This function controls the contrast of an image. An - enhancement factor of 0.0 gives a solid grey - image. A factor of 1.0 gives the original image. It - blends the source image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be contrasted. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - - Returns: - ndarray: The contrasted image. - """ - gray_img = bgr2gray(img) - hist = np.histogram(gray_img, 256, (0, 255))[0] - mean = round(np.sum(gray_img) / np.sum(hist)) - degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) - degenerated = gray2bgr(degenerated) - contrasted_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - contrasted_img = np.clip(contrasted_img, 0, 255) - return contrasted_img.astype(img.dtype) - - -def auto_contrast(img, cutoff=0): - """Auto adjust image contrast. - - This function maximize (normalize) image contrast by first removing cutoff - percent of the lightest and darkest pixels from the histogram and remapping - the image so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - Args: - img (ndarray): Image to be contrasted. BGR order. - cutoff (int | float | tuple): The cutoff percent of the lightest and - darkest pixels to be removed. If given as tuple, it shall be - (low, high). Otherwise, the single value will be used for both. - Defaults to 0. - - Returns: - ndarray: The contrasted image. - """ - - def _auto_contrast_channel(im, c, cutoff): - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # Remove cut-off percent pixels from histo - histo_sum = np.cumsum(histo) - cut_low = histo_sum[-1] * cutoff[0] // 100 - cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 - histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low - histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) - - # Compute mapping - low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] - # If all the values have been cut off, return the origin img - if low >= high: - return im - scale = 255.0 / (high - low) - offset = -low * scale - lut = np.array(range(256)) - lut = lut * scale + offset - lut = np.clip(lut, 0, 255) - return lut[im] - - if isinstance(cutoff, (int, float)): - cutoff = (cutoff, cutoff) - else: - assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ - f'float or tuple, but got {type(cutoff)} instead.' - # Auto adjusts contrast for each channel independently and then stacks - # the result. - s1 = _auto_contrast_channel(img, 0, cutoff) - s2 = _auto_contrast_channel(img, 1, cutoff) - s3 = _auto_contrast_channel(img, 2, cutoff) - contrasted_img = np.stack([s1, s2, s3], axis=-1) - return contrasted_img.astype(img.dtype) - - -def adjust_sharpness(img, factor=1., kernel=None): - """Adjust image sharpness. - - This function controls the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image. A - factor of 1.0 gives the original image. And a factor - of 2.0 gives a sharpened image. It blends the source - image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be sharpened. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - kernel (np.ndarray, optional): Filter kernel to be applied on the img - to obtain the degenerated img. Defaults to None. - - Note: - No value sanity check is enforced on the kernel set by users. So with - an inappropriate kernel, the ``adjust_sharpness`` may fail to perform - the function its name indicates but end up performing whatever - transform determined by the kernel. - - Returns: - ndarray: The sharpened image. - """ - - if kernel is None: - # adopted from PIL.ImageFilter.SMOOTH - kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 - assert isinstance(kernel, np.ndarray), \ - f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' - assert kernel.ndim == 2, \ - f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' - - degenerated = cv2.filter2D(img, -1, kernel) - sharpened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - sharpened_img = np.clip(sharpened_img, 0, 255) - return sharpened_img.astype(img.dtype) - - -def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): - """AlexNet-style PCA jitter. - - This data augmentation is proposed in `ImageNet Classification with Deep - Convolutional Neural Networks - `_. - - Args: - img (ndarray): Image to be adjusted lighting. BGR order. - eigval (ndarray): the eigenvalue of the convariance matrix of pixel - values, respectively. - eigvec (ndarray): the eigenvector of the convariance matrix of pixel - values, respectively. - alphastd (float): The standard deviation for distribution of alpha. - Defaults to 0.1 - to_rgb (bool): Whether to convert img to rgb. - - Returns: - ndarray: The adjusted image. - """ - assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ - f'eigval and eigvec should both be of type np.ndarray, got ' \ - f'{type(eigval)} and {type(eigvec)} instead.' - - assert eigval.ndim == 1 and eigvec.ndim == 2 - assert eigvec.shape == (3, eigval.shape[0]) - n_eigval = eigval.shape[0] - assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ - f'got {type(alphastd)} instead.' - - img = img.copy().astype(np.float32) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - - alpha = np.random.normal(0, alphastd, n_eigval) - alter = eigvec \ - * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ - * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) - alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) - img_adjusted = img + alter - return img_adjusted - - -def lut_transform(img, lut_table): - """Transform array by look-up table. - - The function lut_transform fills the output array with values from the - look-up table. Indices of the entries are taken from the input array. - - Args: - img (ndarray): Image to be transformed. - lut_table (ndarray): look-up table of 256 elements; in case of - multi-channel input array, the table should either have a single - channel (in this case the same table is used for all channels) or - the same number of channels as in the input array. - - Returns: - ndarray: The transformed image. - """ - assert isinstance(img, np.ndarray) - assert 0 <= np.min(img) and np.max(img) <= 255 - assert isinstance(lut_table, np.ndarray) - assert lut_table.shape == (256, ) - - return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) - - -def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - img (ndarray): Image to be processed. - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - - Returns: - ndarray: The processed image. - """ - assert isinstance(img, np.ndarray) - assert img.ndim == 2 - assert isinstance(clip_limit, (float, int)) - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - - clahe = cv2.createCLAHE(clip_limit, tile_grid_size) - return clahe.apply(np.array(img, dtype=np.uint8)) diff --git a/cv/detection/autoassign/pytorch/mmcv/model_zoo/deprecated.json b/cv/detection/autoassign/pytorch/mmcv/model_zoo/deprecated.json deleted file mode 100755 index 25cf6f28c..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/model_zoo/deprecated.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "resnet50_caffe": "detectron/resnet50_caffe", - "resnet50_caffe_bgr": "detectron2/resnet50_caffe_bgr", - "resnet101_caffe": "detectron/resnet101_caffe", - "resnet101_caffe_bgr": "detectron2/resnet101_caffe_bgr" -} diff --git a/cv/detection/autoassign/pytorch/mmcv/model_zoo/mmcls.json b/cv/detection/autoassign/pytorch/mmcv/model_zoo/mmcls.json deleted file mode 100755 index bdb311d9f..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/model_zoo/mmcls.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "vgg11": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_batch256_imagenet_20210208-4271cd6c.pth", - "vgg13": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_batch256_imagenet_20210208-4d1d6080.pth", - "vgg16": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_batch256_imagenet_20210208-db26f1a5.pth", - "vgg19": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_batch256_imagenet_20210208-e6920e4a.pth", - "vgg11_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg11_bn_batch256_imagenet_20210207-f244902c.pth", - "vgg13_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg13_bn_batch256_imagenet_20210207-1a8b7864.pth", - "vgg16_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg16_bn_batch256_imagenet_20210208-7e55cd29.pth", - "vgg19_bn": "https://download.openmmlab.com/mmclassification/v0/vgg/vgg19_bn_batch256_imagenet_20210208-da620c4f.pth", - "resnet18": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet18_batch256_imagenet_20200708-34ab8f90.pth", - "resnet34": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet34_batch256_imagenet_20200708-32ffb4f7.pth", - "resnet50": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_batch256_imagenet_20200708-cfb998bf.pth", - "resnet101": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet101_batch256_imagenet_20200708-753f3608.pth", - "resnet152": "https://download.openmmlab.com/mmclassification/v0/resnet/resnet152_batch256_imagenet_20200708-ec25b1f9.pth", - "resnet50_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d50_batch256_imagenet_20200708-1ad0ce94.pth", - "resnet101_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d101_batch256_imagenet_20200708-9cb302ef.pth", - "resnet152_v1d": "https://download.openmmlab.com/mmclassification/v0/resnet/resnetv1d152_batch256_imagenet_20200708-e79cb6a2.pth", - "resnext50_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext50_32x4d_b32x8_imagenet_20210429-56066e27.pth", - "resnext101_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x4d_b32x8_imagenet_20210506-e0fa3dd5.pth", - "resnext101_32x8d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext101_32x8d_b32x8_imagenet_20210506-23a247d5.pth", - "resnext152_32x4d": "https://download.openmmlab.com/mmclassification/v0/resnext/resnext152_32x4d_b32x8_imagenet_20210524-927787be.pth", - "se-resnet50": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet50_batch256_imagenet_20200804-ae206104.pth", - "se-resnet101": "https://download.openmmlab.com/mmclassification/v0/se-resnet/se-resnet101_batch256_imagenet_20200804-ba5b51d4.pth", - "resnest50": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest50_imagenet_converted-1ebf0afe.pth", - "resnest101": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest101_imagenet_converted-032caa52.pth", - "resnest200": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest200_imagenet_converted-581a60f2.pth", - "resnest269": "https://download.openmmlab.com/mmclassification/v0/resnest/resnest269_imagenet_converted-59930960.pth", - "shufflenet_v1": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v1/shufflenet_v1_batch1024_imagenet_20200804-5d6cec73.pth", - "shufflenet_v2": "https://download.openmmlab.com/mmclassification/v0/shufflenet_v2/shufflenet_v2_batch1024_imagenet_20200812-5bf4721e.pth", - "mobilenet_v2": "https://download.openmmlab.com/mmclassification/v0/mobilenet_v2/mobilenet_v2_batch256_imagenet_20200708-3b2dc3af.pth" -} diff --git a/cv/detection/autoassign/pytorch/mmcv/model_zoo/open_mmlab.json b/cv/detection/autoassign/pytorch/mmcv/model_zoo/open_mmlab.json deleted file mode 100755 index 8311db4fe..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/model_zoo/open_mmlab.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "vgg16_caffe": "https://download.openmmlab.com/pretrain/third_party/vgg16_caffe-292e1171.pth", - "detectron/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_caffe-788b5fa3.pth", - "detectron2/resnet50_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet50_msra-5891d200.pth", - "detectron/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_caffe-3ad79236.pth", - "detectron2/resnet101_caffe": "https://download.openmmlab.com/pretrain/third_party/resnet101_msra-6cc46731.pth", - "detectron2/resnext101_32x8d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x8d-1516f1aa.pth", - "resnext50_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext50-32x4d-0ab1a123.pth", - "resnext101_32x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d-a5af3160.pth", - "resnext101_64x4d": "https://download.openmmlab.com/pretrain/third_party/resnext101_64x4d-ee2c6f71.pth", - "contrib/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_thangvubk-ad1730dd.pth", - "detectron/resnet50_gn": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn-9186a21c.pth", - "detectron/resnet101_gn": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn-cac0ab98.pth", - "jhu/resnet50_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet50_gn_ws-15beedd8.pth", - "jhu/resnet101_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnet101_gn_ws-3e3c308c.pth", - "jhu/resnext50_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn_ws-0d87ac85.pth", - "jhu/resnext101_32x4d_gn_ws": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn_ws-34ac1a9e.pth", - "jhu/resnext50_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext50_32x4d_gn-c7e8b754.pth", - "jhu/resnext101_32x4d_gn": "https://download.openmmlab.com/pretrain/third_party/resnext101_32x4d_gn-ac3bb84e.pth", - "msra/hrnetv2_w18_small": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18_small-b5a04e21.pth", - "msra/hrnetv2_w18": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w18-00eb2006.pth", - "msra/hrnetv2_w32": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w32-dc9eeb4f.pth", - "msra/hrnetv2_w40": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w40-ed0b031c.pth", - "msra/hrnetv2_w48": "https://download.openmmlab.com/pretrain/third_party/hrnetv2_w48-d2186c55.pth", - "bninception_caffe": "https://download.openmmlab.com/pretrain/third_party/bn_inception_caffe-ed2e8665.pth", - "kin400/i3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/i3d_r50_f32s2_k400-2c57e077.pth", - "kin400/nl3d_r50_f32s2_k400": "https://download.openmmlab.com/pretrain/third_party/nl3d_r50_f32s2_k400-fa7e7caa.pth", - "res2net101_v1d_26w_4s": "https://download.openmmlab.com/pretrain/third_party/res2net101_v1d_26w_4s_mmdetv2-f0a600f9.pth", - "regnetx_400mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_400mf-a5b10d96.pth", - "regnetx_800mf": "https://download.openmmlab.com/pretrain/third_party/regnetx_800mf-1f4be4c7.pth", - "regnetx_1.6gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_1.6gf-5791c176.pth", - "regnetx_3.2gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_3.2gf-c2599b0f.pth", - "regnetx_4.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_4.0gf-a88f671e.pth", - "regnetx_6.4gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_6.4gf-006af45d.pth", - "regnetx_8.0gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_8.0gf-3c68abe7.pth", - "regnetx_12gf": "https://download.openmmlab.com/pretrain/third_party/regnetx_12gf-4c2a3350.pth", - "resnet18_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet18_v1c-b5776b93.pth", - "resnet50_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet50_v1c-2cccc1ad.pth", - "resnet101_v1c": "https://download.openmmlab.com/pretrain/third_party/resnet101_v1c-e67eebb6.pth", - "mmedit/vgg16": "https://download.openmmlab.com/mmediting/third_party/vgg_state_dict.pth", - "mmedit/res34_en_nomixup": "https://download.openmmlab.com/mmediting/third_party/model_best_resnet34_En_nomixup.pth", - "mmedit/mobilenet_v2": "https://download.openmmlab.com/mmediting/third_party/mobilenet_v2.pth", - "contrib/mobilenet_v3_large": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_large-bc2c3fd3.pth", - "contrib/mobilenet_v3_small": "https://download.openmmlab.com/pretrain/third_party/mobilenet_v3_small-47085aa1.pth", - "resnest50": "https://download.openmmlab.com/pretrain/third_party/resnest50_d2-7497a55b.pth", - "resnest101": "https://download.openmmlab.com/pretrain/third_party/resnest101_d2-f3b931b2.pth", - "resnest200": "https://download.openmmlab.com/pretrain/third_party/resnest200_d2-ca88e41f.pth", - "darknet53": "https://download.openmmlab.com/pretrain/third_party/darknet53-a628ea1b.pth", - "mmdet/mobilenet_v2": "https://download.openmmlab.com/mmdetection/v2.0/third_party/mobilenet_v2_batch256_imagenet-ff34753d.pth" -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/__init__.py b/cv/detection/autoassign/pytorch/mmcv/ops/__init__.py deleted file mode 100755 index 7a100d849..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .deprecated_wrappers import Conv2d_deprecated as Conv2d -from .deprecated_wrappers import ConvTranspose2d_deprecated as ConvTranspose2d -from .deprecated_wrappers import Linear_deprecated as Linear -from .deprecated_wrappers import MaxPool2d_deprecated as MaxPool2d -from .focal_loss import (SigmoidFocalLoss, SoftmaxFocalLoss, - sigmoid_focal_loss, softmax_focal_loss) -from .info import (get_compiler_version, get_compiling_cuda_version, - get_onnxruntime_op_path) -from .nms import batched_nms, nms, nms_match, soft_nms -from .roi_align import RoIAlign, roi_align -from .roi_pool import RoIPool, roi_pool -from .sync_bn import SyncBatchNorm \ No newline at end of file diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/README.md b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/README.md deleted file mode 100755 index 91c237f3d..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/README.md +++ /dev/null @@ -1,169 +0,0 @@ -# Code Structure of CUDA operators - -This folder contains all non-python code for MMCV custom ops. Please follow the same architecture if you want to add new ops. - -## Directories Tree - -```folder -. -├── common -│ ├── box_iou_rotated_utils.hpp -│ ├── parrots_cpp_helper.hpp -│ ├── parrots_cuda_helper.hpp -│ ├── pytorch_cpp_helper.hpp -│ ├── pytorch_cuda_helper.hpp -│   └── cuda -│   ├── common_cuda_helper.hpp -│   ├── parrots_cudawarpfunction.cuh -│   ├── ... -│   └── ops_cuda_kernel.cuh -├── onnxruntime -│   ├── onnxruntime_register.h -│   ├── onnxruntime_session_options_config_keys.h -│   ├── ort_mmcv_utils.h -│   ├── ... -│   ├── onnx_ops.h -│   └── cpu -│ ├── onnxruntime_register.cpp -│      ├── ... -│      └── onnx_ops_impl.cpp -├── parrots -│   ├── ... -│   ├── ops.cpp -│   ├── ops_parrots.cpp -│   └── ops_pytorch.h -├── pytorch -│   ├── info.cpp -│   ├── pybind.cpp -│   ├── ... -│   ├── ops.cpp -│   └── cuda -│      ├── ... -│      └── ops_cuda.cu -└── tensorrt - ├── trt_cuda_helper.cuh - ├── trt_plugin_helper.hpp - ├── trt_plugin.hpp - ├── trt_serialize.hpp - ├── ... - ├── trt_ops.hpp - └── plugins -    ├── trt_cuda_helper.cu -    ├── trt_plugin.cpp -    ├── ... -    ├── trt_ops.cpp -    └── trt_ops_kernel.cu -``` - -## Components - -- `common`: This directory contains all tools and shared codes. - - `cuda`: The cuda kernels which can be shared by all backends. **HIP** kernel is also here since they have similar syntax. -- `onnxruntime`: **ONNX Runtime** support for custom ops. - - `cpu`: CPU implementation of supported ops. -- `parrots`: **Parrots** is a deep learning frame for model training and inference. Parrots custom ops are placed in this directory. -- `pytorch`: **PyTorch** custom ops are supported by binding C++ to Python with **pybind11**. The ops implementation and binding codes are placed in this directory. - - `cuda`: This directory contains cuda kernel launchers, which feed memory pointers of tensor to the cuda kernel in `common/cuda`. The launchers provide c++ interface of cuda implementation of corresponding custom ops. -- `tensorrt`: **TensorRT** support for custom ops. - - `plugins`: This directory contains the implementation of the supported custom ops. Some ops might also use shared cuda kernel in `common/cuda`. - -## How to add new PyTorch ops? - -1. (Optional) Add shared kernel in `common` to support special hardware platform. - - ```c++ - // src/common/cuda/new_ops_cuda_kernel.cuh - - template - __global__ void new_ops_forward_cuda_kernel(const T* input, T* output, ...) { - // forward here - } - - ``` - - Add cuda kernel launcher in `pytorch/cuda`. - - ```c++ - // src/pytorch/cuda - #include - - void NewOpsForwardCUDAKernelLauncher(Tensor input, Tensor output, ...){ - // initialize - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - ... - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "new_ops_forward_cuda_kernel", ([&] { - new_ops_forward_cuda_kernel - <<>>( - input.data_ptr(), output.data_ptr(),...); - })); - AT_CUDA_CHECK(cudaGetLastError()); - } - ``` - -2. Add ops implementation in `pytorch` directory. Select different implementations according to device type. - - ```c++ - // src/pytorch/new_ops.cpp - #ifdef MMCV_WITH_CUDA - Tensor new_ops_forward_cuda(Tensor input, Tensor output, ...){ - // implement cuda forward here - // use `NewOpsForwardCUDAKernelLauncher` here - } - #else - - Tensor new_ops_forward_cpu(Tensor input, Tensor output, ...){ - // implement cpu forward here - } - - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...){ - // select implementation by input device type - if (boxes.device().is_cuda()) { - #ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(output); - return new_ops_forward_cuda(input, output, ...); - #else - AT_ERROR("new ops is not compiled with GPU support"); - #endif - } else { - CHECK_CPU_INPUT(input); - CHECK_CPU_INPUT(output); - return new_ops_forward_cpu(input, output, ...); - } - } - ``` - -3. Binding the implementation in `pytorch/pybind.cpp` - - ```c++ - // src/pytorch/pybind.cpp - - ... - - Tensor new_ops_forward(Tensor input, Tensor output, ...); - - ... - - // bind with pybind11 - m.def("new_ops_forward", &new_ops_forward, "new_ops_forward", - py::arg("input"), py::arg("output"), ...); - - ... - - ``` - -4. Build MMCV again. Enjoy new ops in python - - ```python - from ..utils import ext_loader - ext_module = ext_loader.load_ext('_ext', ['new_ops_forward']) - - ... - - ext_module.new_ops_forward(input, output, ...) - - ``` diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp deleted file mode 100755 index a1e926adb..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef COMMON_CUDA_HELPER -#define COMMON_CUDA_HELPER - -#include - -#define CUDA_1D_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) - -#define THREADS_PER_BLOCK 512 - -#define DIVUP(m, n) ((m) / (n) + ((m) % (n) > 0)) - -inline int GET_BLOCKS(const int N) { - int optimal_block_num = (N + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; - int max_block_num = 4096; - return std::min(optimal_block_num, max_block_num); -} - -template -__device__ T bilinear_interpolate(const T* input, const int height, - const int width, T y, T x, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) return 0; - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - int y_low = (int)y; - int x_low = (int)x; - int y_high; - int x_high; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - // do bilinear interpolation - T v1 = input[y_low * width + x_low]; - T v2 = input[y_low * width + x_high]; - T v3 = input[y_high * width + x_low]; - T v4 = input[y_high * width + x_high]; - T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - return val; -} - -template -__device__ void bilinear_interpolate_gradient( - const int height, const int width, T y, T x, T& w1, T& w2, T& w3, T& w4, - int& x_low, int& x_high, int& y_low, int& y_high, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - w1 = w2 = w3 = w4 = 0.; - x_low = x_high = y_low = y_high = -1; - return; - } - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - y_low = (int)y; - x_low = (int)x; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - - // reference in forward - // T v1 = input[y_low * width + x_low]; - // T v2 = input[y_low * width + x_high]; - // T v3 = input[y_high * width + x_low]; - // T v4 = input[y_high * width + x_high]; - // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - return; -} -#endif // COMMON_CUDA_HELPER diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/nms_cuda_kernel.cuh b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/nms_cuda_kernel.cuh deleted file mode 100755 index 40a2f4622..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/nms_cuda_kernel.cuh +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef NMS_CUDA_KERNEL_CUH -#define NMS_CUDA_KERNEL_CUH - -#include -#ifdef MMCV_WITH_TRT -#include "common_cuda_helper.hpp" -#else // MMCV_WITH_TRT -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else // MMCV_USE_PARROTS -#include "pytorch_cuda_helper.hpp" -#endif // MMCV_USE_PARROTS -#endif // MMCV_WITH_TRT - -int const threadsPerBlock = sizeof(unsigned long long int) * 8; - -__device__ inline bool devIoU(float const *const a, float const *const b, - const int offset, const float threshold) { - float left = fmaxf(a[0], b[0]), right = fminf(a[2], b[2]); - float top = fmaxf(a[1], b[1]), bottom = fminf(a[3], b[3]); - float width = fmaxf(right - left + offset, 0.f), - height = fmaxf(bottom - top + offset, 0.f); - float interS = width * height; - float Sa = (a[2] - a[0] + offset) * (a[3] - a[1] + offset); - float Sb = (b[2] - b[0] + offset) * (b[3] - b[1] + offset); - return interS > threshold * (Sa + Sb - interS); -} - -__global__ void nms_cuda(const int n_boxes, const float iou_threshold, - const int offset, const float *dev_boxes, - unsigned long long *dev_mask) { - const int row_start = blockIdx.y; - const int col_start = blockIdx.x; - const int tid = threadIdx.x; - - if (row_start > col_start) return; - - const int row_size = - fminf(n_boxes - row_start * threadsPerBlock, threadsPerBlock); - const int col_size = - fminf(n_boxes - col_start * threadsPerBlock, threadsPerBlock); - - __shared__ float block_boxes[threadsPerBlock * 4]; - if (tid < col_size) { - block_boxes[tid * 4 + 0] = - dev_boxes[(threadsPerBlock * col_start + tid) * 4 + 0]; - block_boxes[tid * 4 + 1] = - dev_boxes[(threadsPerBlock * col_start + tid) * 4 + 1]; - block_boxes[tid * 4 + 2] = - dev_boxes[(threadsPerBlock * col_start + tid) * 4 + 2]; - block_boxes[tid * 4 + 3] = - dev_boxes[(threadsPerBlock * col_start + tid) * 4 + 3]; - } - __syncthreads(); - - if (tid < row_size) { - const int cur_box_idx = threadsPerBlock * row_start + tid; - const float *cur_box = dev_boxes + cur_box_idx * 4; - int i = 0; - unsigned long long int t = 0; - int start = 0; - if (row_start == col_start) { - start = tid + 1; - } - for (i = start; i < col_size; i++) { - if (devIoU(cur_box, block_boxes + i * 4, offset, iou_threshold)) { - t |= 1ULL << i; - } - } - dev_mask[cur_box_idx * gridDim.y + col_start] = t; - } -} -#endif // NMS_CUDA_KERNEL_CUH diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/nms_rotated_cuda.cuh b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/nms_rotated_cuda.cuh deleted file mode 100755 index 80bed9681..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/nms_rotated_cuda.cuh +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved -// modified from -// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/nms_rotated/nms_rotated_cuda.cu -#ifndef NMS_ROTATED_CUDA_CUH -#define NMS_ROTATED_CUDA_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif -#include "box_iou_rotated_utils.hpp" - -__host__ __device__ inline int divideUP(const int x, const int y) { - return (((x) + (y)-1) / (y)); -} - -namespace { -int const threadsPerBlock = sizeof(unsigned long long) * 8; -} - -template -__global__ void nms_rotated_cuda_kernel(const int n_boxes, - const float iou_threshold, - const T* dev_boxes, - unsigned long long* dev_mask, - const int multi_label) { - // nms_rotated_cuda_kernel is modified from torchvision's nms_cuda_kernel - - if (multi_label == 1) { - const int row_start = blockIdx.y; - const int col_start = blockIdx.x; - - // if (row_start > col_start) return; - - const int row_size = - min(n_boxes - row_start * threadsPerBlock, threadsPerBlock); - const int col_size = - min(n_boxes - col_start * threadsPerBlock, threadsPerBlock); - - // Compared to nms_cuda_kernel, where each box is represented with 4 values - // (x1, y1, x2, y2), each rotated box is represented with 5 values - // (x_center, y_center, width, height, angle_degrees) here. - __shared__ T block_boxes[threadsPerBlock * 5]; - if (threadIdx.x < col_size) { - block_boxes[threadIdx.x * 6 + 0] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 6 + 0]; - block_boxes[threadIdx.x * 6 + 1] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 6 + 1]; - block_boxes[threadIdx.x * 6 + 2] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 6 + 2]; - block_boxes[threadIdx.x * 6 + 3] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 6 + 3]; - block_boxes[threadIdx.x * 6 + 4] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 6 + 4]; - block_boxes[threadIdx.x * 6 + 5] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 6 + 5]; - } - __syncthreads(); - - if (threadIdx.x < row_size) { - const int cur_box_idx = threadsPerBlock * row_start + threadIdx.x; - const T* cur_box = dev_boxes + cur_box_idx * 6; - int i = 0; - unsigned long long t = 0; - int start = 0; - if (row_start == col_start) { - start = threadIdx.x + 1; - } - for (i = start; i < col_size; i++) { - // Instead of devIoU used by original horizontal nms, here - // we use the single_box_iou_rotated function from - // box_iou_rotated_utils.h - if (single_box_iou_rotated(cur_box, block_boxes + i * 6, 0) > - iou_threshold) { - t |= 1ULL << i; - } - } - const int col_blocks = divideUP(n_boxes, threadsPerBlock); - dev_mask[cur_box_idx * col_blocks + col_start] = t; - } - } else { - const int row_start = blockIdx.y; - const int col_start = blockIdx.x; - - // if (row_start > col_start) return; - - const int row_size = - min(n_boxes - row_start * threadsPerBlock, threadsPerBlock); - const int col_size = - min(n_boxes - col_start * threadsPerBlock, threadsPerBlock); - - // Compared to nms_cuda_kernel, where each box is represented with 4 values - // (x1, y1, x2, y2), each rotated box is represented with 5 values - // (x_center, y_center, width, height, angle_degrees) here. - __shared__ T block_boxes[threadsPerBlock * 5]; - if (threadIdx.x < col_size) { - block_boxes[threadIdx.x * 5 + 0] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 0]; - block_boxes[threadIdx.x * 5 + 1] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 1]; - block_boxes[threadIdx.x * 5 + 2] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 2]; - block_boxes[threadIdx.x * 5 + 3] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 3]; - block_boxes[threadIdx.x * 5 + 4] = - dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 4]; - } - __syncthreads(); - - if (threadIdx.x < row_size) { - const int cur_box_idx = threadsPerBlock * row_start + threadIdx.x; - const T* cur_box = dev_boxes + cur_box_idx * 5; - int i = 0; - unsigned long long t = 0; - int start = 0; - if (row_start == col_start) { - start = threadIdx.x + 1; - } - for (i = start; i < col_size; i++) { - // Instead of devIoU used by original horizontal nms, here - // we use the single_box_iou_rotated function from - // box_iou_rotated_utils.h - if (single_box_iou_rotated(cur_box, block_boxes + i * 5, 0) > - iou_threshold) { - t |= 1ULL << i; - } - } - const int col_blocks = divideUP(n_boxes, threadsPerBlock); - dev_mask[cur_box_idx * col_blocks + col_start] = t; - } - } -} - -#endif diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/roi_align_cuda_kernel.cuh b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/roi_align_cuda_kernel.cuh deleted file mode 100755 index 4541462af..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/roi_align_cuda_kernel.cuh +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef ROI_ALIGN_CUDA_KERNEL_CUH -#define ROI_ALIGN_CUDA_KERNEL_CUH - -#include -#ifdef MMCV_WITH_TRT -#include "common_cuda_helper.hpp" -#else // MMCV_WITH_TRT -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else // MMCV_USE_PARROTS -#include "pytorch_cuda_helper.hpp" -#endif // MMCV_USE_PARROTS -#endif // MMCV_WITH_TRT - -/*** Forward ***/ -template -__global__ void roi_align_forward_cuda_kernel( - const int nthreads, const T* input, const T* rois, T* output, T* argmax_y, - T* argmax_x, const int pooled_height, const int pooled_width, - const T spatial_scale, const int sampling_ratio, - const int pool_mode, // 0 - max pool, 1 - avg pool - const bool aligned, const int channels, const int height, const int width) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - // (n, c, ph, pw) is an element in the pooled output - int pw = index % pooled_width; - int ph = (index / pooled_width) % pooled_height; - int c = (index / pooled_width / pooled_height) % channels; - int n = index / pooled_width / pooled_height / channels; - - const T* offset_rois = rois + n * 5; - int roi_batch_ind = offset_rois[0]; - - // Do not using rounding; this implementation detail is critical - T offset = aligned ? (T)0.5 : (T)0.0; - T roi_start_w = offset_rois[1] * spatial_scale - offset; - T roi_start_h = offset_rois[2] * spatial_scale - offset; - T roi_end_w = offset_rois[3] * spatial_scale - offset; - T roi_end_h = offset_rois[4] * spatial_scale - offset; - - T roi_width = roi_end_w - roi_start_w; - T roi_height = roi_end_h - roi_start_h; - if (!aligned) { // for backward-compatibility only - roi_width = max(roi_width, (T)1.); - roi_height = max(roi_height, (T)1.); - } - - T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); - T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); - - const T* offset_input = - input + (roi_batch_ind * channels + c) * height * width; - - // We use roi_bin_grid to sample the grid and mimic integral - int roi_bin_grid_h = - (sampling_ratio > 0) - ? sampling_ratio - : static_cast(ceilf(roi_height / pooled_height)); - int roi_bin_grid_w = - (sampling_ratio > 0) - ? sampling_ratio - : static_cast(ceilf(roi_width / pooled_width)); - - if (pool_mode == 0) { - // We do max pooling inside a bin - T maxval = -FLT_MAX; - T maxidx_y = -1.f, maxidx_x = -1.f; - for (int iy = 0; iy < roi_bin_grid_h; iy++) { - const T y = roi_start_h + ph * bin_size_h + - static_cast(iy + .5f) * bin_size_h / - static_cast(roi_bin_grid_h); - for (int ix = 0; ix < roi_bin_grid_w; ix++) { - const T x = roi_start_w + pw * bin_size_w + - static_cast(ix + .5f) * bin_size_w / - static_cast(roi_bin_grid_w); - T val = - bilinear_interpolate(offset_input, height, width, y, x, index); - if (val > maxval) { - maxval = val; - maxidx_y = y; - maxidx_x = x; - } - } - } - output[index] = maxval; - argmax_y[index] = maxidx_y; - argmax_x[index] = maxidx_x; - } else if (pool_mode == 1) { - // We do average pooling inside a bin - const T count = max(roi_bin_grid_h * roi_bin_grid_w, 1); - T output_val = 0.; - for (int iy = 0; iy < roi_bin_grid_h; iy++) { - const T y = roi_start_h + ph * bin_size_h + - static_cast(iy + .5f) * bin_size_h / - static_cast(roi_bin_grid_h); - for (int ix = 0; ix < roi_bin_grid_w; ix++) { - const T x = roi_start_w + pw * bin_size_w + - static_cast(ix + .5f) * bin_size_w / - static_cast(roi_bin_grid_w); - T val = - bilinear_interpolate(offset_input, height, width, y, x, index); - output_val += val; - } - } - output[index] = output_val / count; - } - } -} - -/*** Backward ***/ -template -__global__ void roi_align_backward_cuda_kernel( - const int nthreads, const T* grad_output, const T* rois, const T* argmax_y, - const T* argmax_x, T* grad_input, const int pooled_height, - const int pooled_width, const T spatial_scale, const int sampling_ratio, - const int pool_mode, // 0 - max pool, 1 - avg pool - const bool aligned, const int channels, const int height, const int width) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - // (n, c, ph, pw) is an element in the pooled output - int pw = index % pooled_width; - int ph = (index / pooled_width) % pooled_height; - int c = (index / pooled_width / pooled_height) % channels; - int n = index / pooled_width / pooled_height / channels; - - const T grad_output_this_bin = grad_output[index]; - - const T* offset_rois = rois + n * 5; - int roi_batch_ind = offset_rois[0]; - T* offset_grad_input = - grad_input + ((roi_batch_ind * channels + c) * height * width); - - if (pool_mode == 0) { - T y = argmax_y[index], x = argmax_x[index]; - if (y != -1.f) { - T w1, w2, w3, w4; - int x_low, x_high, y_low, y_high; - bilinear_interpolate_gradient(height, width, y, x, w1, w2, w3, w4, - x_low, x_high, y_low, y_high, index); - - if (x_low >= 0 && x_high >= 0 && y_low >= 0 && y_high >= 0) { - atomicAdd(offset_grad_input + y_low * width + x_low, - grad_output_this_bin * w1); - atomicAdd(offset_grad_input + y_low * width + x_high, - grad_output_this_bin * w2); - atomicAdd(offset_grad_input + y_high * width + x_low, - grad_output_this_bin * w3); - atomicAdd(offset_grad_input + y_high * width + x_high, - grad_output_this_bin * w4); - } - } - } else if (pool_mode == 1) { - // Do not using rounding; this implementation detail is critical - T offset = aligned ? (T)0.5 : (T)0.0; - T roi_start_w = offset_rois[1] * spatial_scale - offset; - T roi_start_h = offset_rois[2] * spatial_scale - offset; - T roi_end_w = offset_rois[3] * spatial_scale - offset; - T roi_end_h = offset_rois[4] * spatial_scale - offset; - - T roi_width = roi_end_w - roi_start_w; - T roi_height = roi_end_h - roi_start_h; - if (!aligned) { // for backward-compatibility only - roi_width = max(roi_width, (T)1.); - roi_height = max(roi_height, (T)1.); - } - - T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); - T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); - - // We use roi_bin_grid to sample the grid and mimic integral - int roi_bin_grid_h = - (sampling_ratio > 0) - ? sampling_ratio - : static_cast(ceilf(roi_height / pooled_height)); - int roi_bin_grid_w = - (sampling_ratio > 0) - ? sampling_ratio - : static_cast(ceilf(roi_width / pooled_width)); - - // We do average (integral) pooling inside a bin - const T count = roi_bin_grid_h * roi_bin_grid_w; // e.g. = 4 - - for (int iy = 0; iy < roi_bin_grid_h; iy++) { - const T y = roi_start_h + ph * bin_size_h + - static_cast(iy + .5f) * bin_size_h / - static_cast(roi_bin_grid_h); - for (int ix = 0; ix < roi_bin_grid_w; ix++) { - const T x = roi_start_w + pw * bin_size_w + - static_cast(ix + .5f) * bin_size_w / - static_cast(roi_bin_grid_w); - - T w1, w2, w3, w4; - int x_low, x_high, y_low, y_high; - bilinear_interpolate_gradient(height, width, y, x, w1, w2, w3, w4, - x_low, x_high, y_low, y_high, index); - - if (x_low >= 0 && x_high >= 0 && y_low >= 0 && y_high >= 0) { - atomicAdd(offset_grad_input + y_low * width + x_low, - grad_output_this_bin * w1 / count); - atomicAdd(offset_grad_input + y_low * width + x_high, - grad_output_this_bin * w2 / count); - atomicAdd(offset_grad_input + y_high * width + x_low, - grad_output_this_bin * w3 / count); - atomicAdd(offset_grad_input + y_high * width + x_high, - grad_output_this_bin * w4 / count); - } - } - } - } - } -} - -#endif // ROI_ALIGN_CUDA_KERNEL_CUH diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/roi_pool_cuda_kernel.cuh b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/roi_pool_cuda_kernel.cuh deleted file mode 100755 index 3d7eae66b..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/roi_pool_cuda_kernel.cuh +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef ROI_POOL_CUDA_KERNEL_CUH -#define ROI_POOL_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void roi_pool_forward_cuda_kernel( - const int nthreads, const T* input, const T* rois, T* output, int* argmax, - const int pooled_height, const int pooled_width, const T spatial_scale, - const int channels, const int height, const int width) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - // (n, c, ph, pw) is an element in the pooled output - int pw = index % pooled_width; - int ph = (index / pooled_width) % pooled_height; - int c = (index / pooled_width / pooled_height) % channels; - int n = index / pooled_width / pooled_height / channels; - - const T* offset_rois = rois + n * 5; - int roi_batch_ind = offset_rois[0]; - // calculate the roi region on feature maps - T roi_x1 = offset_rois[1] * spatial_scale; - T roi_y1 = offset_rois[2] * spatial_scale; - T roi_x2 = (offset_rois[3] + 1) * spatial_scale; - T roi_y2 = (offset_rois[4] + 1) * spatial_scale; - - // force malformed rois to be 1x1 - T roi_w = roi_x2 - roi_x1; - T roi_h = roi_y2 - roi_y1; - if (roi_w <= 0 || roi_h <= 0) continue; - - T bin_size_w = roi_w / static_cast(pooled_width); - T bin_size_h = roi_h / static_cast(pooled_height); - - // the corresponding bin region - int bin_x1 = floorf(static_cast(pw) * bin_size_w + roi_x1); - int bin_y1 = floorf(static_cast(ph) * bin_size_h + roi_y1); - int bin_x2 = ceilf(static_cast(pw + 1) * bin_size_w + roi_x1); - int bin_y2 = ceilf(static_cast(ph + 1) * bin_size_h + roi_y1); - - // add roi offsets and clip to input boundaries - bin_x1 = min(max(bin_x1, 0), width); - bin_y1 = min(max(bin_y1, 0), height); - bin_x2 = min(max(bin_x2, 0), width); - bin_y2 = min(max(bin_y2, 0), height); - bool is_empty = (bin_y2 <= bin_y1) || (bin_x2 <= bin_x1); - - const T* offset_input = - input + (roi_batch_ind * channels + c) * height * width; - // Define an empty pooling region to be zero - // If nothing is pooled, argmax = -1 causes nothing to be backprop'd - T max_val = is_empty ? 0 : -FLT_MAX; - int max_idx = -1; - for (int h = bin_y1; h < bin_y2; ++h) { - for (int w = bin_x1; w < bin_x2; ++w) { - int offset = h * width + w; - if (offset_input[offset] > max_val) { - max_val = offset_input[offset]; - max_idx = offset; - } - } - } - output[index] = max_val; - if (argmax != NULL) argmax[index] = max_idx; - } -} - -template -__global__ void roi_pool_backward_cuda_kernel( - const int nthreads, const T* grad_output, const T* rois, const int* argmax, - T* grad_input, const int pooled_height, const int pooled_width, - const int channels, const int height, const int width) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - // (n, c) is an element in the pooled output - int c = (index / pooled_width / pooled_height) % channels; - int n = index / pooled_width / pooled_height / channels; - - int roi_batch_ind = rois[n * 5]; - T* grad_input_offset = - grad_input + ((roi_batch_ind * channels + c) * height * width); - int argmax_index = argmax[index]; - - if (argmax_index != -1) { - atomicAdd(grad_input_offset + argmax_index, grad_output[index]); - } - } -} - -#endif // ROI_POOL_CUDA_KERNEL_CUH diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh deleted file mode 100755 index 1eb5f8fcc..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/sigmoid_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sigmoid_focal_loss_forward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* output, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + expf(-input[index])); - - // (1 - p)**gamma * log(p) - T term_p = pow(((T)1. - p), gamma) * log(max(p, (T)FLT_MIN)); - // p**gamma * log(1 - p) - T term_n = pow(p, gamma) * log(max((T)1. - p, (T)FLT_MIN)); - - output[index] = (T)0.; - output[index] += -flag_p * alpha * term_p; - output[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - output[index] *= weight[t]; - } - } -} - -template -__global__ void sigmoid_focal_loss_backward_cuda_kernel( - const int nthreads, const T* input, const int64_t* target, const T* weight, - T* grad_input, const T gamma, const T alpha, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - - int64_t t = target[n]; - T flag_p = (t == c); - T flag_n = (t != c); - - // p = sigmoid(x) = 1. / 1. + expf(-x) - T p = (T)1. / ((T)1. + exp(-input[index])); - - // (1 - p)**gamma * (1 - p - gamma*p*log(p)) - T term_p = pow(((T)1. - p), gamma) * - ((T)1. - p - (gamma * p * log(max(p, (T)FLT_MIN)))); - // p**gamma * (gamma * (1 - p) * log(1 - p) - p) - T term_n = pow(p, gamma) * - (gamma * ((T)1. - p) * log(max((T)1. - p, (T)FLT_MIN)) - p); - - grad_input[index] = (T)0.; - grad_input[index] += -flag_p * alpha * term_p; - grad_input[index] += -flag_n * ((T)1. - alpha) * term_n; - if (weight != NULL) { - grad_input[index] *= weight[t]; - } - } -} - -#endif // SIGMOID_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh deleted file mode 100755 index 631b2c617..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/softmax_focal_loss_cuda_kernel.cuh +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH -#define SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void softmax_focal_loss_forward_cuda_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* output, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - output[index] = - -alpha * pow((T)1. - pred, gamma) * log(max(pred, (T)FLT_MIN)); - } else { - output[index] = 0; - } - if (weight != NULL) { - output[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda1_kernel( - const int nthreads, const T* softmax, const int64_t* target, - const T* weight, T* buff, const T gamma, const T alpha, - const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int64_t label = target[index]; - T pred = softmax[index * num_classes + label]; - - if (label >= 0) { - buff[index] = alpha * (-pow((T)1. - pred, gamma) + - gamma * pow((T)1. - pred, gamma - 1) * pred * - log(max(pred, (T)FLT_MIN))); - } else { - buff[index] = 0; - } - if (weight != NULL) { - buff[index] *= weight[label]; - } - } -} - -template -__global__ void softmax_focal_loss_backward_cuda2_kernel( - const int nthreads, const T* softmax, const int64_t* target, const T* buff, - T* grad_input, const int num_classes) { - CUDA_1D_KERNEL_LOOP(index, nthreads) { - int n = index / num_classes; - int c = index % num_classes; - int64_t label = target[n]; - - if (label >= 0) { - T flag = (label == c ? (T)1. : (T)0.); - grad_input[index] = buff[n] * (flag - softmax[index]); - } else { - grad_input[index] = 0; - } - } -} - -#endif // SOFTMAX_FOCAL_LOSS_CUDA_KERNEL_CUH diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh deleted file mode 100755 index 4ec6a4668..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SYNCBN_CUDA_KERNEL_CUH -#define SYNCBN_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sync_bn_forward_mean_cuda_kernel(const T *input, float *mean, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += input[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_mean_cuda_kernel(const phalf *input, - float *mean, int num, - int channels, int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += static_cast(input[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_var_cuda_kernel(const T *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = input[index] - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_var_cuda_kernel(const phalf *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = static_cast(input[index]) - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_output_cuda_kernel( - const T *input, const float *mean, const float *var, float *running_mean, - float *running_var, const float *weight, const float *bias, float *norm, - float *std, T *output, int num, int channels, int spatial, float eps, - float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = (input[index] - mean_value) / std_value; - output[index] = norm[index] * weight_value + bias_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - (input[index] - mean_value) / std_value * weight_value + bias_value; - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = norm[index] = (input[index] - mean_value) / std_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = (input[index] - mean_value) / std_value; - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template <> -__global__ void sync_bn_forward_output_cuda_kernel( - const phalf *input, const float *mean, const float *var, - float *running_mean, float *running_var, const float *weight, - const float *bias, float *norm, float *std, phalf *output, int num, - int channels, int spatial, float eps, float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = - static_cast(norm[index] * weight_value + bias_value); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - static_cast((static_cast(input[index]) - mean_value) / - std_value * weight_value + - bias_value); - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = static_cast(norm[index]); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = static_cast( - (static_cast(input[index]) - mean_value) / std_value); - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template -__global__ void sync_bn_backward_param_cuda_kernel(const T *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += grad_output[index] * norm[index]; - buffer2[tid] += grad_output[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template <> -__global__ void sync_bn_backward_param_cuda_kernel(const phalf *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += static_cast(grad_output[index]) * norm[index]; - buffer2[tid] += static_cast(grad_output[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const T *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, T *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = - weight[c] * - (grad_output[index] - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]; - } -} - -template <> -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const phalf *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, phalf *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = static_cast( - weight[c] * - (static_cast(grad_output[index]) - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]); - } -} - -#endif // SYNCBN_CUDA_KERNEL_CUH diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp deleted file mode 100755 index c7f9f35b7..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef PYTORCH_CPP_HELPER -#define PYTORCH_CPP_HELPER -#include - -#include - -using namespace at; - -#define DIVUP(m, n) ((m) / (n) + ((m) % (n) > 0)) - -#define CHECK_CUDA(x) \ - TORCH_CHECK(x.device().is_cuda(), #x " must be a CUDA tensor") -#define CHECK_CPU(x) \ - TORCH_CHECK(!x.device().is_cuda(), #x " must be a CPU tensor") -#define CHECK_CONTIGUOUS(x) \ - TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") -#define CHECK_CUDA_INPUT(x) \ - CHECK_CUDA(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_CPU_INPUT(x) \ - CHECK_CPU(x); \ - CHECK_CONTIGUOUS(x) - -#endif // PYTORCH_CPP_HELPER diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp deleted file mode 100755 index 9869b535f..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PYTORCH_CUDA_HELPER -#define PYTORCH_CUDA_HELPER - -#include -#include -#include - -#include -#include - -#include "common_cuda_helper.hpp" - -using at::Half; -using at::Tensor; -using phalf = at::Half; - -#define __PHALF(x) (x) - -#endif // PYTORCH_CUDA_HELPER diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu deleted file mode 100755 index cb899f954..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/focal_loss_cuda.cu +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sigmoid_focal_loss_cuda_kernel.cuh" -#include "softmax_focal_loss_cuda_kernel.cuh" - -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = input.size(1); - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_forward_cuda_kernel", [&] { - sigmoid_focal_loss_forward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha) { - int output_size = grad_input.numel(); - int num_classes = input.size(1); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sigmoid_focal_loss_backward_cuda_kernel", [&] { - sigmoid_focal_loss_backward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - target.data_ptr(), weight.data_ptr(), - grad_input.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha) { - int output_size = output.numel(); - int num_classes = softmax.size(1); - - AT_ASSERTM(target.max().item() <= (int64_t)num_classes, - "target label should smaller or equal than num classes"); - at::cuda::CUDAGuard device_guard(softmax.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - softmax.scalar_type(), "softmax_focal_loss_forward_cuda_kernel", [&] { - softmax_focal_loss_forward_cuda_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - output.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor softmax, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha) { - int num_classes = softmax.size(1); - - int output_size = buff.numel(); - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda1_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda1_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), weight.data_ptr(), - buff.data_ptr(), gamma, alpha, num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); - - output_size = grad_input.numel(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_input.scalar_type(), - "softmax_focal_loss_backward_cuda2_" - "kernel", - [&] { - softmax_focal_loss_backward_cuda2_kernel - <<>>( - output_size, softmax.data_ptr(), - target.data_ptr(), buff.data_ptr(), - grad_input.data_ptr(), num_classes); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/nms_cuda.cu b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/nms_cuda.cu deleted file mode 100755 index 16cf64683..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/nms_cuda.cu +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "nms_cuda_kernel.cuh" -#include "pytorch_cuda_helper.hpp" - -Tensor NMSCUDAKernelLauncher(Tensor boxes, Tensor scores, float iou_threshold, - int offset) { - at::cuda::CUDAGuard device_guard(boxes.device()); - - if (boxes.numel() == 0) { - return at::empty({0}, boxes.options().dtype(at::kLong)); - } - auto order_t = std::get<1>(scores.sort(0, /*descending=*/true)); - auto boxes_sorted = boxes.index_select(0, order_t); - - int boxes_num = boxes.size(0); - const int col_blocks = DIVUP(boxes_num, threadsPerBlock); - Tensor mask = - at::empty({boxes_num, col_blocks}, boxes.options().dtype(at::kLong)); - dim3 blocks(col_blocks, col_blocks); - dim3 threads(threadsPerBlock); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - nms_cuda<<>>( - boxes_num, iou_threshold, offset, boxes_sorted.data_ptr(), - (unsigned long long*)mask.data_ptr()); - - at::Tensor mask_cpu = mask.to(at::kCPU); - unsigned long long* mask_host = - (unsigned long long*)mask_cpu.data_ptr(); - - std::vector remv(col_blocks); - memset(&remv[0], 0, sizeof(unsigned long long) * col_blocks); - - at::Tensor keep_t = - at::zeros({boxes_num}, boxes.options().dtype(at::kBool).device(at::kCPU)); - bool* keep = keep_t.data_ptr(); - - for (int i = 0; i < boxes_num; i++) { - int nblock = i / threadsPerBlock; - int inblock = i % threadsPerBlock; - - if (!(remv[nblock] & (1ULL << inblock))) { - keep[i] = true; - // set every overlap box with bit 1 in remv - unsigned long long* p = mask_host + i * col_blocks; - for (int j = nblock; j < col_blocks; j++) { - remv[j] |= p[j]; - } - } - } - - AT_CUDA_CHECK(cudaGetLastError()); - return order_t.masked_select(keep_t.to(at::kCUDA)); -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_align_cuda.cu b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_align_cuda.cu deleted file mode 100755 index 3d4f7614e..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_align_cuda.cu +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "roi_align_cuda_kernel.cuh" - -void ROIAlignForwardCUDAKernelLauncher(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned) { - int output_size = output.numel(); - int channels = input.size(1); - int height = input.size(2); - int width = input.size(3); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "roi_align_forward_cuda_kernel", [&] { - roi_align_forward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - rois.data_ptr(), output.data_ptr(), - argmax_y.data_ptr(), argmax_x.data_ptr(), - aligned_height, aligned_width, - static_cast(spatial_scale), sampling_ratio, pool_mode, - aligned, channels, height, width); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void ROIAlignBackwardCUDAKernelLauncher(Tensor grad_output, Tensor rois, - Tensor argmax_y, Tensor argmax_x, - Tensor grad_input, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, - bool aligned) { - int output_size = grad_output.numel(); - int channels = grad_input.size(1); - int height = grad_input.size(2); - int width = grad_input.size(3); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "roi_align_backward_cuda_kernel", [&] { - roi_align_backward_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - rois.data_ptr(), argmax_y.data_ptr(), - argmax_x.data_ptr(), grad_input.data_ptr(), - aligned_height, aligned_width, - static_cast(spatial_scale), sampling_ratio, pool_mode, - aligned, channels, height, width); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_pool_cuda.cu b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_pool_cuda.cu deleted file mode 100755 index d9cdf3050..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/roi_pool_cuda.cu +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "roi_pool_cuda_kernel.cuh" - -void ROIPoolForwardCUDAKernelLauncher(Tensor input, Tensor rois, Tensor output, - Tensor argmax, int pooled_height, - int pooled_width, float spatial_scale) { - int output_size = output.numel(); - int channels = input.size(1); - int height = input.size(2); - int width = input.size(3); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "roi_pool_forward_cuda_kernel", [&] { - roi_pool_forward_cuda_kernel - <<>>( - output_size, input.data_ptr(), - rois.data_ptr(), output.data_ptr(), - argmax.data_ptr(), pooled_height, pooled_width, - static_cast(spatial_scale), channels, height, width); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} - -void ROIPoolBackwardCUDAKernelLauncher(Tensor grad_output, Tensor rois, - Tensor argmax, Tensor grad_input, - int pooled_height, int pooled_width, - float spatial_scale) { - int output_size = grad_output.numel(); - int channels = grad_input.size(1); - int height = grad_input.size(2); - int width = grad_input.size(3); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "roi_pool_backward_cuda_kernel", [&] { - roi_pool_backward_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - rois.data_ptr(), argmax.data_ptr(), - grad_input.data_ptr(), pooled_height, pooled_width, - channels, height, width); - }); - - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu deleted file mode 100755 index 657c81701..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sync_bn_cuda_kernel.cuh" - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_mean_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_var_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), num, channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_output_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), running_mean.data_ptr(), - running_var.data_ptr(), weight.data_ptr(), - bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), output.data_ptr(), num, - channels, spatial, eps, momentum, group_size); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias) { - int num = grad_output.size(0); - int channels = grad_output.size(1); - int spatial = grad_output.size(2); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_param_cuda_kernel", [&] { - sync_bn_backward_param_cuda_kernel - <<>>( - grad_output.data_ptr(), norm.data_ptr(), - grad_weight.data_ptr(), grad_bias.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - int output_size = grad_input.numel(); - int num = grad_input.size(0); - int channels = grad_input.size(1); - int spatial = grad_input.size(2); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_data_cuda_kernel", [&] { - sync_bn_backward_data_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - weight.data_ptr(), grad_weight.data_ptr(), - grad_bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), grad_input.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp deleted file mode 100755 index 3e2c92b27..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/focal_loss.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -void SigmoidFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SigmoidFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, - Tensor grad_input, - const float gamma, - const float alpha); - -void SoftmaxFocalLossForwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor output, - const float gamma, - const float alpha); - -void SoftmaxFocalLossBackwardCUDAKernelLauncher(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, - const float gamma, - const float alpha); - -void sigmoid_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SigmoidFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void sigmoid_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor grad_input, - float gamma, float alpha) { - SigmoidFocalLossBackwardCUDAKernelLauncher(input, target, weight, grad_input, - gamma, alpha); -} - -void softmax_focal_loss_forward_cuda(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - SoftmaxFocalLossForwardCUDAKernelLauncher(input, target, weight, output, - gamma, alpha); -} - -void softmax_focal_loss_backward_cuda(Tensor input, Tensor target, - Tensor weight, Tensor buff, - Tensor grad_input, float gamma, - float alpha) { - SoftmaxFocalLossBackwardCUDAKernelLauncher(input, target, weight, buff, - grad_input, gamma, alpha); -} -#endif - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(target); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(output); - - sigmoid_focal_loss_forward_cuda(input, target, weight, output, gamma, - alpha); -#else - AT_ERROR("SigmoidFocalLoss is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SigmoidFocalLoss is not implemented on CPU"); - } -} - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(target); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(grad_input); - - sigmoid_focal_loss_backward_cuda(input, target, weight, grad_input, gamma, - alpha); -#else - AT_ERROR("SigmoidFocalLoss is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SigmoidFocalLoss is not implemented on CPU"); - } -} - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(target); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(output); - - softmax_focal_loss_forward_cuda(input, target, weight, output, gamma, - alpha); -#else - AT_ERROR("SoftmaxFocalLoss is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SoftmaxFocalLoss is not implemented on CPU"); - } -} - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(target); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(buff); - CHECK_CUDA_INPUT(grad_input); - - softmax_focal_loss_backward_cuda(input, target, weight, buff, grad_input, - gamma, alpha); -#else - AT_ERROR("SoftmaxFocalLoss is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SoftmaxFocalLoss is not implemented on CPU"); - } -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/info.cpp b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/info.cpp deleted file mode 100755 index a08d227d4..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/info.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// modified from -// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/vision.cpp -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF -#include -int get_cudart_version() { return CUDART_VERSION; } -#endif -#endif - -std::string get_compiling_cuda_version() { -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF - std::ostringstream oss; - // copied from - // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/cuda/detail/CUDAHooks.cpp#L231 - auto printCudaStyleVersion = [&](int v) { - oss << (v / 1000) << "." << (v / 10 % 100); - if (v % 10 != 0) { - oss << "." << (v % 10); - } - }; - printCudaStyleVersion(get_cudart_version()); - return oss.str(); -#else - return std::string("rocm not available"); -#endif -#else - return std::string("not available"); -#endif -} - -// similar to -// https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/Version.cpp -std::string get_compiler_version() { - std::ostringstream ss; -#if defined(__GNUC__) -#ifndef __clang__ - { ss << "GCC " << __GNUC__ << "." << __GNUC_MINOR__; } -#endif -#endif - -#if defined(__clang_major__) - { - ss << "clang " << __clang_major__ << "." << __clang_minor__ << "." - << __clang_patchlevel__; - } -#endif - -#if defined(_MSC_VER) - { ss << "MSVC " << _MSC_FULL_VER; } -#endif - return ss.str(); -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/nms.cpp b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/nms.cpp deleted file mode 100755 index e88208dc9..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/nms.cpp +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -Tensor NMSCUDAKernelLauncher(Tensor boxes, Tensor scores, float iou_threshold, - int offset); - -Tensor nms_cuda(Tensor boxes, Tensor scores, float iou_threshold, int offset) { - return NMSCUDAKernelLauncher(boxes, scores, iou_threshold, offset); -} -#endif - -Tensor nms_cpu(Tensor boxes, Tensor scores, float iou_threshold, int offset) { - if (boxes.numel() == 0) { - return at::empty({0}, boxes.options().dtype(at::kLong)); - } - auto x1_t = boxes.select(1, 0).contiguous(); - auto y1_t = boxes.select(1, 1).contiguous(); - auto x2_t = boxes.select(1, 2).contiguous(); - auto y2_t = boxes.select(1, 3).contiguous(); - - Tensor areas_t = (x2_t - x1_t + offset) * (y2_t - y1_t + offset); - - auto order_t = std::get<1>(scores.sort(0, /* descending=*/true)); - - auto nboxes = boxes.size(0); - Tensor select_t = at::ones({nboxes}, boxes.options().dtype(at::kBool)); - - auto select = select_t.data_ptr(); - auto order = order_t.data_ptr(); - auto x1 = x1_t.data_ptr(); - auto y1 = y1_t.data_ptr(); - auto x2 = x2_t.data_ptr(); - auto y2 = y2_t.data_ptr(); - auto areas = areas_t.data_ptr(); - - for (int64_t _i = 0; _i < nboxes; _i++) { - if (select[_i] == false) continue; - auto i = order[_i]; - auto ix1 = x1[i]; - auto iy1 = y1[i]; - auto ix2 = x2[i]; - auto iy2 = y2[i]; - auto iarea = areas[i]; - - for (int64_t _j = _i + 1; _j < nboxes; _j++) { - if (select[_j] == false) continue; - auto j = order[_j]; - auto xx1 = std::max(ix1, x1[j]); - auto yy1 = std::max(iy1, y1[j]); - auto xx2 = std::min(ix2, x2[j]); - auto yy2 = std::min(iy2, y2[j]); - - auto w = std::max(0.f, xx2 - xx1 + offset); - auto h = std::max(0.f, yy2 - yy1 + offset); - auto inter = w * h; - auto ovr = inter / (iarea + areas[j] - inter); - if (ovr > iou_threshold) select[_j] = false; - } - } - return order_t.masked_select(select_t); -} - -Tensor nms(Tensor boxes, Tensor scores, float iou_threshold, int offset) { - if (boxes.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(boxes); - CHECK_CUDA_INPUT(scores); - return nms_cuda(boxes, scores, iou_threshold, offset); -#else - AT_ERROR("nms is not compiled with GPU support"); -#endif - } else { - CHECK_CPU_INPUT(boxes); - CHECK_CPU_INPUT(scores); - return nms_cpu(boxes, scores, iou_threshold, offset); - } -} - -Tensor softnms_cpu(Tensor boxes, Tensor scores, Tensor dets, - float iou_threshold, float sigma, float min_score, - int method, int offset) { - if (boxes.numel() == 0) { - return at::empty({0}, boxes.options().dtype(at::kLong)); - } - - auto x1_t = boxes.select(1, 0).contiguous(); - auto y1_t = boxes.select(1, 1).contiguous(); - auto x2_t = boxes.select(1, 2).contiguous(); - auto y2_t = boxes.select(1, 3).contiguous(); - auto scores_t = scores.clone(); - - Tensor areas_t = (x2_t - x1_t + offset) * (y2_t - y1_t + offset); - - auto nboxes = boxes.size(0); - auto x1 = x1_t.data_ptr(); - auto y1 = y1_t.data_ptr(); - auto x2 = x2_t.data_ptr(); - auto y2 = y2_t.data_ptr(); - auto sc = scores_t.data_ptr(); - auto areas = areas_t.data_ptr(); - auto de = dets.data_ptr(); - - int64_t pos = 0; - Tensor inds_t = at::arange(nboxes, boxes.options().dtype(at::kLong)); - auto inds = inds_t.data_ptr(); - - for (int64_t i = 0; i < nboxes; i++) { - auto max_score = sc[i]; - auto max_pos = i; - - pos = i + 1; - // get max box - while (pos < nboxes) { - if (max_score < sc[pos]) { - max_score = sc[pos]; - max_pos = pos; - } - pos = pos + 1; - } - // swap - auto ix1 = de[i * 5 + 0] = x1[max_pos]; - auto iy1 = de[i * 5 + 1] = y1[max_pos]; - auto ix2 = de[i * 5 + 2] = x2[max_pos]; - auto iy2 = de[i * 5 + 3] = y2[max_pos]; - auto iscore = de[i * 5 + 4] = sc[max_pos]; - auto iarea = areas[max_pos]; - auto iind = inds[max_pos]; - x1[max_pos] = x1[i]; - y1[max_pos] = y1[i]; - x2[max_pos] = x2[i]; - y2[max_pos] = y2[i]; - sc[max_pos] = sc[i]; - areas[max_pos] = areas[i]; - inds[max_pos] = inds[i]; - x1[i] = ix1; - y1[i] = iy1; - x2[i] = ix2; - y2[i] = iy2; - sc[i] = iscore; - areas[i] = iarea; - inds[i] = iind; - - pos = i + 1; - while (pos < nboxes) { - auto xx1 = std::max(ix1, x1[pos]); - auto yy1 = std::max(iy1, y1[pos]); - auto xx2 = std::min(ix2, x2[pos]); - auto yy2 = std::min(iy2, y2[pos]); - - auto w = std::max(0.f, xx2 - xx1 + offset); - auto h = std::max(0.f, yy2 - yy1 + offset); - auto inter = w * h; - auto ovr = inter / (iarea + areas[pos] - inter); - - float weight = 1.; - if (method == 0) { - if (ovr >= iou_threshold) weight = 0; - } else if (method == 1) { - if (ovr >= iou_threshold) weight = 1 - ovr; - } else if (method == 2) { - weight = std::exp(-(ovr * ovr) / sigma); - } - sc[pos] *= weight; - // if box score falls below threshold, discard the box by - // swapping with last box update N - if (sc[pos] < min_score) { - x1[pos] = x1[nboxes - 1]; - y1[pos] = y1[nboxes - 1]; - x2[pos] = x2[nboxes - 1]; - y2[pos] = y2[nboxes - 1]; - sc[pos] = sc[nboxes - 1]; - areas[pos] = areas[nboxes - 1]; - inds[pos] = inds[nboxes - 1]; - nboxes = nboxes - 1; - pos = pos - 1; - } - pos = pos + 1; - } - } - return inds_t.slice(0, 0, nboxes); -} - -Tensor softnms(Tensor boxes, Tensor scores, Tensor dets, float iou_threshold, - float sigma, float min_score, int method, int offset) { - if (boxes.device().is_cuda()) { - AT_ERROR("softnms is not implemented on GPU"); - } else { - return softnms_cpu(boxes, scores, dets, iou_threshold, sigma, min_score, - method, offset); - } -} - -std::vector > nms_match_cpu(Tensor dets, float iou_threshold) { - auto x1_t = dets.select(1, 0).contiguous(); - auto y1_t = dets.select(1, 1).contiguous(); - auto x2_t = dets.select(1, 2).contiguous(); - auto y2_t = dets.select(1, 3).contiguous(); - auto scores = dets.select(1, 4).contiguous(); - - at::Tensor areas_t = (x2_t - x1_t) * (y2_t - y1_t); - - auto order_t = std::get<1>(scores.sort(0, /* descending=*/true)); - - auto ndets = dets.size(0); - at::Tensor suppressed_t = - at::zeros({ndets}, dets.options().dtype(at::kByte).device(at::kCPU)); - - auto suppressed = suppressed_t.data_ptr(); - auto order = order_t.data_ptr(); - auto x1 = x1_t.data_ptr(); - auto y1 = y1_t.data_ptr(); - auto x2 = x2_t.data_ptr(); - auto y2 = y2_t.data_ptr(); - auto areas = areas_t.data_ptr(); - - std::vector keep; - std::vector > matched; - - for (int64_t _i = 0; _i < ndets; _i++) { - auto i = order[_i]; - if (suppressed[i] == 1) continue; - keep.push_back(i); - std::vector v_i; - auto ix1 = x1[i]; - auto iy1 = y1[i]; - auto ix2 = x2[i]; - auto iy2 = y2[i]; - auto iarea = areas[i]; - - for (int64_t _j = _i + 1; _j < ndets; _j++) { - auto j = order[_j]; - if (suppressed[j] == 1) continue; - auto xx1 = std::max(ix1, x1[j]); - auto yy1 = std::max(iy1, y1[j]); - auto xx2 = std::min(ix2, x2[j]); - auto yy2 = std::min(iy2, y2[j]); - - auto w = std::max(static_cast(0), xx2 - xx1); - auto h = std::max(static_cast(0), yy2 - yy1); - auto inter = w * h; - auto ovr = inter / (iarea + areas[j] - inter); - if (ovr >= iou_threshold) { - suppressed[j] = 1; - v_i.push_back(j); - } - } - matched.push_back(v_i); - } - for (int i = 0; i < keep.size(); i++) - matched[i].insert(matched[i].begin(), keep[i]); - return matched; -} - -std::vector > nms_match(Tensor dets, float iou_threshold) { - if (dets.device().is_cuda()) { - AT_ERROR("nms_match is not implemented on GPU"); - } else { - return nms_match_cpu(dets, iou_threshold); - } -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp deleted file mode 100755 index de8e18c97..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -std::string get_compiler_version(); -std::string get_compiling_cuda_version(); - -void sigmoid_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void sigmoid_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor grad_input, float gamma, float alpha); - -void softmax_focal_loss_forward(Tensor input, Tensor target, Tensor weight, - Tensor output, float gamma, float alpha); - -void softmax_focal_loss_backward(Tensor input, Tensor target, Tensor weight, - Tensor buff, Tensor grad_input, float gamma, - float alpha); - -Tensor nms(Tensor boxes, Tensor scores, float iou_threshold, int offset); - -Tensor softnms(Tensor boxes, Tensor scores, Tensor dets, float iou_threshold, - float sigma, float min_score, int method, int offset); - -std::vector> nms_match(Tensor dets, float iou_threshold); - - - -void roi_align_forward(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, bool aligned); - -void roi_align_backward(Tensor grad_output, Tensor rois, Tensor argmax_y, - Tensor argmax_x, Tensor grad_input, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, bool aligned); - -void roi_pool_forward(Tensor input, Tensor rois, Tensor output, Tensor argmax, - int pooled_height, int pooled_width, float spatial_scale); - -void roi_pool_backward(Tensor grad_output, Tensor rois, Tensor argmax, - Tensor grad_input, int pooled_height, int pooled_width, - float spatial_scale); - -void sync_bn_forward_mean(const Tensor input, Tensor mean); - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var); - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - - m.def("get_compiler_version", &get_compiler_version, "get_compiler_version"); - m.def("get_compiling_cuda_version", &get_compiling_cuda_version, - "get_compiling_cuda_version"); - - m.def("sigmoid_focal_loss_forward", &sigmoid_focal_loss_forward, - "sigmoid_focal_loss_forward ", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("sigmoid_focal_loss_backward", &sigmoid_focal_loss_backward, - "sigmoid_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("grad_input"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_forward", &softmax_focal_loss_forward, - "softmax_focal_loss_forward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("output"), py::arg("gamma"), - py::arg("alpha")); - m.def("softmax_focal_loss_backward", &softmax_focal_loss_backward, - "softmax_focal_loss_backward", py::arg("input"), py::arg("target"), - py::arg("weight"), py::arg("buff"), py::arg("grad_input"), - py::arg("gamma"), py::arg("alpha")); - m.def("nms", &nms, "nms (CPU/CUDA) ", py::arg("boxes"), py::arg("scores"), - py::arg("iou_threshold"), py::arg("offset")); - m.def("softnms", &softnms, "softnms (CPU) ", py::arg("boxes"), - py::arg("scores"), py::arg("dets"), py::arg("iou_threshold"), - py::arg("sigma"), py::arg("min_score"), py::arg("method"), - py::arg("offset")); - m.def("nms_match", &nms_match, "nms_match (CPU) ", py::arg("dets"), - py::arg("iou_threshold")); - m.def("roi_align_forward", &roi_align_forward, "roi_align forward", - py::arg("input"), py::arg("rois"), py::arg("output"), - py::arg("argmax_y"), py::arg("argmax_x"), py::arg("aligned_height"), - py::arg("aligned_width"), py::arg("spatial_scale"), - py::arg("sampling_ratio"), py::arg("pool_mode"), py::arg("aligned")); - m.def("roi_align_backward", &roi_align_backward, "roi_align backward", - py::arg("grad_output"), py::arg("rois"), py::arg("argmax_y"), - py::arg("argmax_x"), py::arg("grad_input"), py::arg("aligned_height"), - py::arg("aligned_width"), py::arg("spatial_scale"), - py::arg("sampling_ratio"), py::arg("pool_mode"), py::arg("aligned")); - m.def("roi_pool_forward", &roi_pool_forward, "roi_pool forward", - py::arg("input"), py::arg("rois"), py::arg("output"), py::arg("argmax"), - py::arg("pooled_height"), py::arg("pooled_width"), - py::arg("spatial_scale")); - m.def("roi_pool_backward", &roi_pool_backward, "roi_pool backward", - py::arg("grad_output"), py::arg("rois"), py::arg("argmax"), - py::arg("grad_input"), py::arg("pooled_height"), - py::arg("pooled_width"), py::arg("spatial_scale")); - m.def("sync_bn_forward_mean", &sync_bn_forward_mean, "sync_bn forward_mean", - py::arg("input"), py::arg("mean")); - m.def("sync_bn_forward_var", &sync_bn_forward_var, "sync_bn forward_var", - py::arg("input"), py::arg("mean"), py::arg("var")); - m.def("sync_bn_forward_output", &sync_bn_forward_output, - "sync_bn forward_output", py::arg("input"), py::arg("mean"), - py::arg("var"), py::arg("weight"), py::arg("bias"), - py::arg("running_mean"), py::arg("running_var"), py::arg("norm"), - py::arg("std"), py::arg("output"), py::arg("eps"), py::arg("momentum"), - py::arg("group_size")); - m.def("sync_bn_backward_param", &sync_bn_backward_param, - "sync_bn backward_param", py::arg("grad_output"), py::arg("norm"), - py::arg("grad_weight"), py::arg("grad_bias")); - m.def("sync_bn_backward_data", &sync_bn_backward_data, - "sync_bn backward_data", py::arg("grad_output"), py::arg("weight"), - py::arg("grad_weight"), py::arg("grad_bias"), py::arg("norm"), - py::arg("std"), py::arg("grad_input")); -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/roi_align.cpp b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/roi_align.cpp deleted file mode 100755 index b44a742ce..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/roi_align.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -void ROIAlignForwardCUDAKernelLauncher(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned); - -void ROIAlignBackwardCUDAKernelLauncher(Tensor grad_output, Tensor rois, - Tensor argmax_y, Tensor argmax_x, - Tensor grad_input, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, - bool aligned); - -void roi_align_forward_cuda(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned) { - ROIAlignForwardCUDAKernelLauncher( - input, rois, output, argmax_y, argmax_x, aligned_height, aligned_width, - spatial_scale, sampling_ratio, pool_mode, aligned); -} - -void roi_align_backward_cuda(Tensor grad_output, Tensor rois, Tensor argmax_y, - Tensor argmax_x, Tensor grad_input, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned) { - ROIAlignBackwardCUDAKernelLauncher( - grad_output, rois, argmax_y, argmax_x, grad_input, aligned_height, - aligned_width, spatial_scale, sampling_ratio, pool_mode, aligned); -} -#endif - -void ROIAlignForwardCPULauncher(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned); - -void ROIAlignBackwardCPULauncher(Tensor grad_output, Tensor rois, - Tensor argmax_y, Tensor argmax_x, - Tensor grad_input, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, - bool aligned); - -void roi_align_forward_cpu(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, bool aligned) { - ROIAlignForwardCPULauncher(input, rois, output, argmax_y, argmax_x, - aligned_height, aligned_width, spatial_scale, - sampling_ratio, pool_mode, aligned); -} - -void roi_align_backward_cpu(Tensor grad_output, Tensor rois, Tensor argmax_y, - Tensor argmax_x, Tensor grad_input, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned) { - ROIAlignBackwardCPULauncher(grad_output, rois, argmax_y, argmax_x, grad_input, - aligned_height, aligned_width, spatial_scale, - sampling_ratio, pool_mode, aligned); -} - -void roi_align_forward(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, bool aligned) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(rois); - CHECK_CUDA_INPUT(output); - CHECK_CUDA_INPUT(argmax_y); - CHECK_CUDA_INPUT(argmax_x); - - roi_align_forward_cuda(input, rois, output, argmax_y, argmax_x, - aligned_height, aligned_width, spatial_scale, - sampling_ratio, pool_mode, aligned); -#else - AT_ERROR("RoIAlign is not compiled with GPU support"); -#endif - } else { - CHECK_CPU_INPUT(input); - CHECK_CPU_INPUT(rois); - CHECK_CPU_INPUT(output); - CHECK_CPU_INPUT(argmax_y); - CHECK_CPU_INPUT(argmax_x); - roi_align_forward_cpu(input, rois, output, argmax_y, argmax_x, - aligned_height, aligned_width, spatial_scale, - sampling_ratio, pool_mode, aligned); - } -} - -void roi_align_backward(Tensor grad_output, Tensor rois, Tensor argmax_y, - Tensor argmax_x, Tensor grad_input, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, bool aligned) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(rois); - CHECK_CUDA_INPUT(argmax_y); - CHECK_CUDA_INPUT(argmax_x); - CHECK_CUDA_INPUT(grad_input); - - roi_align_backward_cuda(grad_output, rois, argmax_y, argmax_x, grad_input, - aligned_height, aligned_width, spatial_scale, - sampling_ratio, pool_mode, aligned); -#else - AT_ERROR("RoIAlign is not compiled with GPU support"); -#endif - } else { - CHECK_CPU_INPUT(grad_output); - CHECK_CPU_INPUT(rois); - CHECK_CPU_INPUT(argmax_y); - CHECK_CPU_INPUT(argmax_x); - CHECK_CPU_INPUT(grad_input); - - roi_align_backward_cpu(grad_output, rois, argmax_y, argmax_x, grad_input, - aligned_height, aligned_width, spatial_scale, - sampling_ratio, pool_mode, aligned); - } -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/roi_align_cpu.cpp b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/roi_align_cpu.cpp deleted file mode 100755 index 3f797cb63..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/roi_align_cpu.cpp +++ /dev/null @@ -1,431 +0,0 @@ -// Modified from -// https://github.com/facebookresearch/detectron2/tree/master/detectron2/layers/csrc/ROIAlign -// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved -#include -#include - -#include "pytorch_cpp_helper.hpp" - -// implementation taken from Caffe2 -template -struct PreCalc { - int pos1; - int pos2; - int pos3; - int pos4; - T w1; - T w2; - T w3; - T w4; -}; - -template -void pre_calc_for_bilinear_interpolate( - const int height, const int width, const int pooled_height, - const int pooled_width, const int iy_upper, const int ix_upper, - T roi_start_h, T roi_start_w, T bin_size_h, T bin_size_w, - int roi_bin_grid_h, int roi_bin_grid_w, std::vector>& pre_calc) { - int pre_calc_index = 0; - for (int ph = 0; ph < pooled_height; ph++) { - for (int pw = 0; pw < pooled_width; pw++) { - for (int iy = 0; iy < iy_upper; iy++) { - const T yy = roi_start_h + ph * bin_size_h + - static_cast(iy + .5f) * bin_size_h / - static_cast(roi_bin_grid_h); // e.g., 0.5, 1.5 - for (int ix = 0; ix < ix_upper; ix++) { - const T xx = roi_start_w + pw * bin_size_w + - static_cast(ix + .5f) * bin_size_w / - static_cast(roi_bin_grid_w); - - T x = xx; - T y = yy; - // deal with: inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - PreCalc pc; - pc.pos1 = 0; - pc.pos2 = 0; - pc.pos3 = 0; - pc.pos4 = 0; - pc.w1 = 0; - pc.w2 = 0; - pc.w3 = 0; - pc.w4 = 0; - pre_calc[pre_calc_index] = pc; - pre_calc_index += 1; - continue; - } - - if (y <= 0) { - y = 0; - } - if (x <= 0) { - x = 0; - } - - int y_low = (int)y; - int x_low = (int)x; - int y_high; - int x_high; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - // save weights and indices - PreCalc pc; - pc.pos1 = y_low * width + x_low; - pc.pos2 = y_low * width + x_high; - pc.pos3 = y_high * width + x_low; - pc.pos4 = y_high * width + x_high; - pc.w1 = w1; - pc.w2 = w2; - pc.w3 = w3; - pc.w4 = w4; - pre_calc[pre_calc_index] = pc; - - pre_calc_index += 1; - } - } - } - } -} - -template -void ROIAlignForward(const int nthreads, const T* input, const T* rois, - T* output, T* argmax_y, T* argmax_x, - const int pooled_height, const int pooled_width, - const T spatial_scale, const int sampling_ratio, - const int pool_mode, // 0 - max pool, 1 - avg pool - const bool aligned, const int channels, const int height, - const int width) { - int n_rois = nthreads / channels / pooled_width / pooled_height; - // (n, c, ph, pw) is an element in the pooled output - // can be parallelized using omp - // #pragma omp parallel for num_threads(32) - for (int n = 0; n < n_rois; n++) { - int index_n = n * channels * pooled_width * pooled_height; - - const T* offset_rois = rois + n * 5; - int roi_batch_ind = offset_rois[0]; - - // Do not use rounding; this implementation detail is critical - T offset = aligned ? (T)0.5 : (T)0.0; - T roi_start_w = offset_rois[1] * spatial_scale - offset; - T roi_start_h = offset_rois[2] * spatial_scale - offset; - T roi_end_w = offset_rois[3] * spatial_scale - offset; - T roi_end_h = offset_rois[4] * spatial_scale - offset; - - T roi_width = roi_end_w - roi_start_w; - T roi_height = roi_end_h - roi_start_h; - if (aligned) { - AT_ASSERTM(roi_width >= 0 && roi_height >= 0, - "ROIs in ROIAlign cannot have non-negative size!"); - } else { // for backward-compatibility only - roi_width = std::max(roi_width, (T)1.); - roi_height = std::max(roi_height, (T)1.); - } - T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); - T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); - - // We use roi_bin_grid to sample the grid and mimic integral - int roi_bin_grid_h = (sampling_ratio > 0) - ? sampling_ratio - : ceilf(roi_height / pooled_height); // e.g., = 2 - int roi_bin_grid_w = - (sampling_ratio > 0) ? sampling_ratio : ceilf(roi_width / pooled_width); - - // When the grid is empty, output zeros == 0/1, instead of NaN. - const T count = std::max(roi_bin_grid_h * roi_bin_grid_w, 1); // e.g. = 4 - - // we want to precalculate indices and weights shared by all channels, - // this is the key point of optimization - std::vector> pre_calc(roi_bin_grid_h * roi_bin_grid_w * - pooled_width * pooled_height); - pre_calc_for_bilinear_interpolate( - height, width, pooled_height, pooled_width, roi_bin_grid_h, - roi_bin_grid_w, roi_start_h, roi_start_w, bin_size_h, bin_size_w, - roi_bin_grid_h, roi_bin_grid_w, pre_calc); - - for (int c = 0; c < channels; c++) { - int index_n_c = index_n + c * pooled_width * pooled_height; - const T* offset_input = - input + (roi_batch_ind * channels + c) * height * width; - int pre_calc_index = 0; - - for (int ph = 0; ph < pooled_height; ph++) { - for (int pw = 0; pw < pooled_width; pw++) { - int index = index_n_c + ph * pooled_width + pw; - - T output_val = 0.; - T maxval = -10000; - T maxidx_y = -1.f, maxidx_x = -1.f; - for (int iy = 0; iy < roi_bin_grid_h; iy++) { - const T y = roi_start_h + ph * bin_size_h + - static_cast(iy + .5f) * bin_size_h / - static_cast(roi_bin_grid_h); - for (int ix = 0; ix < roi_bin_grid_w; ix++) { - const T x = roi_start_w + pw * bin_size_w + - static_cast(ix + .5f) * bin_size_w / - static_cast(roi_bin_grid_w); - PreCalc pc = pre_calc[pre_calc_index]; - T val = pc.w1 * offset_input[pc.pos1] + - pc.w2 * offset_input[pc.pos2] + - pc.w3 * offset_input[pc.pos3] + - pc.w4 * offset_input[pc.pos4]; - if (val > maxval) { - maxval = val; - maxidx_y = y; - maxidx_x = x; - } - output_val += val; - pre_calc_index += 1; - } - } - if (pool_mode == 0) { - // We do max pooling inside a bin - output[index] = maxval; - argmax_y[index] = maxidx_y; - argmax_x[index] = maxidx_x; - } else if (pool_mode == 1) { - // We do average (integral) pooling inside a bin - output[index] = output_val / count; - } // if - } // for pw - } // for ph - } // for c - } // for n -} - -template -void bilinear_interpolate_gradient(const int height, const int width, T y, T x, - T& w1, T& w2, T& w3, T& w4, int& x_low, - int& x_high, int& y_low, int& y_high, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - w1 = w2 = w3 = w4 = 0.; - x_low = x_high = y_low = y_high = -1; - return; - } - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - y_low = (int)y; - x_low = (int)x; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - - // reference in forward - // T v1 = input[y_low * width + x_low]; - // T v2 = input[y_low * width + x_high]; - // T v3 = input[y_high * width + x_low]; - // T v4 = input[y_high * width + x_high]; - // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - return; -} - -template -inline void add(T* address, const T& val) { - *address += val; -} - -template -void ROIAlignBackward(const int nthreads, const T* grad_output, const T* rois, - const T* argmax_y, const T* argmax_x, T* grad_input, - const int pooled_height, const int pooled_width, - const T spatial_scale, const int sampling_ratio, - const int pool_mode, // 0 - max pool, 1 - avg pool - const bool aligned, const int channels, const int height, - const int width, const int n_stride, const int c_stride, - const int h_stride, const int w_stride) { - for (int index = 0; index < nthreads; index++) { - // (n, c, ph, pw) is an element in the pooled output - int pw = index % pooled_width; - int ph = (index / pooled_width) % pooled_height; - int c = (index / pooled_width / pooled_height) % channels; - int n = index / pooled_width / pooled_height / channels; - - const T* offset_rois = rois + n * 5; - int roi_batch_ind = offset_rois[0]; - - // Do not use rounding; this implementation detail is critical - T offset = aligned ? (T)0.5 : (T)0.0; - T roi_start_w = offset_rois[1] * spatial_scale - offset; - T roi_start_h = offset_rois[2] * spatial_scale - offset; - T roi_end_w = offset_rois[3] * spatial_scale - offset; - T roi_end_h = offset_rois[4] * spatial_scale - offset; - - T roi_width = roi_end_w - roi_start_w; - T roi_height = roi_end_h - roi_start_h; - if (aligned) { - AT_ASSERTM(roi_width >= 0 && roi_height >= 0, - "ROIs in ROIAlign do not have non-negative size!"); - } else { // for backward-compatibility only - roi_width = std::max(roi_width, (T)1.); - roi_height = std::max(roi_height, (T)1.); - } - T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); - T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); - - T* offset_grad_input = - grad_input + ((roi_batch_ind * channels + c) * height * width); - - int output_offset = n * n_stride + c * c_stride; - const T* offset_grad_output = grad_output + output_offset; - const T grad_output_this_bin = - offset_grad_output[ph * h_stride + pw * w_stride]; - - if (pool_mode == 0) { - // We do max pooling inside a bin - T y = argmax_y[index], x = argmax_x[index]; - if (y != -1.f) { - T w1, w2, w3, w4; - int x_low, x_high, y_low, y_high; - bilinear_interpolate_gradient(height, width, y, x, w1, w2, w3, w4, - x_low, x_high, y_low, y_high, index); - - T g1 = grad_output_this_bin * w1; - T g2 = grad_output_this_bin * w2; - T g3 = grad_output_this_bin * w3; - T g4 = grad_output_this_bin * w4; - - if (x_low >= 0 && x_high >= 0 && y_low >= 0 && y_high >= 0) { - // atomic add is not needed for now since it is single threaded - add(offset_grad_input + y_low * width + x_low, static_cast(g1)); - add(offset_grad_input + y_low * width + x_high, static_cast(g2)); - add(offset_grad_input + y_high * width + x_low, static_cast(g3)); - add(offset_grad_input + y_high * width + x_high, static_cast(g4)); - } // if - } // mode - } else if (pool_mode == 1) { - // We do average (integral) pooling inside a bin - // We use roi_bin_grid to sample the grid and mimic integral - int roi_bin_grid_h = - (sampling_ratio > 0) - ? sampling_ratio - : ceilf(roi_height / pooled_height); // e.g., = 2 - int roi_bin_grid_w = (sampling_ratio > 0) - ? sampling_ratio - : ceilf(roi_width / pooled_width); - - const T count = roi_bin_grid_h * roi_bin_grid_w; // e.g. = 4 - for (int iy = 0; iy < roi_bin_grid_h; iy++) { - const T y = roi_start_h + ph * bin_size_h + - static_cast(iy + .5f) * bin_size_h / - static_cast(roi_bin_grid_h); // e.g., 0.5, 1.5 - for (int ix = 0; ix < roi_bin_grid_w; ix++) { - const T x = roi_start_w + pw * bin_size_w + - static_cast(ix + .5f) * bin_size_w / - static_cast(roi_bin_grid_w); - - T w1, w2, w3, w4; - int x_low, x_high, y_low, y_high; - - bilinear_interpolate_gradient(height, width, y, x, w1, w2, w3, w4, - x_low, x_high, y_low, y_high, index); - - T g1 = grad_output_this_bin * w1 / count; - T g2 = grad_output_this_bin * w2 / count; - T g3 = grad_output_this_bin * w3 / count; - T g4 = grad_output_this_bin * w4 / count; - - if (x_low >= 0 && x_high >= 0 && y_low >= 0 && y_high >= 0) { - // atomic add is not needed for now since it is single threaded - add(offset_grad_input + y_low * width + x_low, static_cast(g1)); - add(offset_grad_input + y_low * width + x_high, static_cast(g2)); - add(offset_grad_input + y_high * width + x_low, static_cast(g3)); - add(offset_grad_input + y_high * width + x_high, - static_cast(g4)); - } // if - } // ix - } // iy - } // mode - } // for -} // ROIAlignBackward - -void ROIAlignForwardCPULauncher(Tensor input, Tensor rois, Tensor output, - Tensor argmax_y, Tensor argmax_x, - int aligned_height, int aligned_width, - float spatial_scale, int sampling_ratio, - int pool_mode, bool aligned) { - int output_size = output.numel(); - int channels = input.size(1); - int height = input.size(2); - int width = input.size(3); - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "ROIAlign_forward", [&] { - ROIAlignForward( - output_size, input.data_ptr(), rois.data_ptr(), - output.data_ptr(), argmax_y.data_ptr(), - argmax_x.data_ptr(), aligned_height, aligned_width, - static_cast(spatial_scale), sampling_ratio, pool_mode, - aligned, channels, height, width); - }); -} - -void ROIAlignBackwardCPULauncher(Tensor grad_output, Tensor rois, - Tensor argmax_y, Tensor argmax_x, - Tensor grad_input, int aligned_height, - int aligned_width, float spatial_scale, - int sampling_ratio, int pool_mode, - bool aligned) { - int output_size = grad_output.numel(); - int channels = grad_input.size(1); - int height = grad_input.size(2); - int width = grad_input.size(3); - - // get stride values to ensure indexing into gradients is correct. - int n_stride = grad_output.stride(0); - int c_stride = grad_output.stride(1); - int h_stride = grad_output.stride(2); - int w_stride = grad_output.stride(3); - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "ROIAlign_backward", [&] { - ROIAlignBackward( - output_size, grad_output.data_ptr(), - rois.data_ptr(), argmax_y.data_ptr(), - argmax_x.data_ptr(), grad_input.data_ptr(), - aligned_height, aligned_width, static_cast(spatial_scale), - sampling_ratio, pool_mode, aligned, channels, height, width, - n_stride, c_stride, h_stride, w_stride); - }); -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/roi_pool.cpp b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/roi_pool.cpp deleted file mode 100755 index 34c4b996b..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/roi_pool.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -void ROIPoolForwardCUDAKernelLauncher(Tensor input, Tensor rois, Tensor output, - Tensor argmax, int pooled_height, - int pooled_width, float spatial_scale); - -void ROIPoolBackwardCUDAKernelLauncher(Tensor grad_output, Tensor rois, - Tensor argmax, Tensor grad_input, - int pooled_height, int pooled_width, - float spatial_scale); - -void roi_pool_forward_cuda(Tensor input, Tensor rois, Tensor output, - Tensor argmax, int pooled_height, int pooled_width, - float spatial_scale) { - ROIPoolForwardCUDAKernelLauncher(input, rois, output, argmax, pooled_height, - pooled_width, spatial_scale); -} - -void roi_pool_backward_cuda(Tensor grad_output, Tensor rois, Tensor argmax, - Tensor grad_input, int pooled_height, - int pooled_width, float spatial_scale) { - ROIPoolBackwardCUDAKernelLauncher(grad_output, rois, argmax, grad_input, - pooled_height, pooled_width, spatial_scale); -} -#endif - -void roi_pool_forward(Tensor input, Tensor rois, Tensor output, Tensor argmax, - int pooled_height, int pooled_width, - float spatial_scale) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(rois); - CHECK_CUDA_INPUT(output); - CHECK_CUDA_INPUT(argmax); - - roi_pool_forward_cuda(input, rois, output, argmax, pooled_height, - pooled_width, spatial_scale); -#else - AT_ERROR("RoIPool is not compiled with GPU support"); -#endif - } else { - AT_ERROR("RoIPool is not implemented on CPU"); - } -} - -void roi_pool_backward(Tensor grad_output, Tensor rois, Tensor argmax, - Tensor grad_input, int pooled_height, int pooled_width, - float spatial_scale) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(rois); - CHECK_CUDA_INPUT(argmax); - CHECK_CUDA_INPUT(grad_input); - - roi_pool_backward_cuda(grad_output, rois, argmax, grad_input, pooled_height, - pooled_width, spatial_scale); -#else - AT_ERROR("RoIPool is not compiled with GPU support"); -#endif - } else { - AT_ERROR("RoIPool is not implemented on CPU"); - } -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp b/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp deleted file mode 100755 index 2e023a859..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean); - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var); - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size); - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias); - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -void sync_bn_forward_mean_cuda(const Tensor input, Tensor mean) { - SyncBNForwardMeanCUDAKernelLauncher(input, mean); -} - -void sync_bn_forward_var_cuda(const Tensor input, const Tensor mean, - Tensor var) { - SyncBNForwardVarCUDAKernelLauncher(input, mean, var); -} - -void sync_bn_forward_output_cuda(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - SyncBNForwardOutputCUDAKernelLauncher(input, mean, var, running_mean, - running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_cuda(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - SyncBNBackwardParamCUDAKernelLauncher(grad_output, norm, grad_weight, - grad_bias); -} - -void sync_bn_backward_data_cuda(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - SyncBNBackwardDataCUDAKernelLauncher(grad_output, weight, grad_weight, - grad_bias, norm, std, grad_input); -} -#endif - -void sync_bn_forward_mean(const Tensor input, Tensor mean) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - sync_bn_forward_mean_cuda(input, mean); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - CHECK_CUDA_INPUT(var); - sync_bn_forward_var_cuda(input, mean, var); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - CHECK_CUDA_INPUT(var); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(bias); - CHECK_CUDA_INPUT(running_mean); - CHECK_CUDA_INPUT(running_var); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(std); - CHECK_CUDA_INPUT(output); - sync_bn_forward_output_cuda(input, mean, var, running_mean, running_var, - weight, bias, norm, std, output, eps, momentum, - group_size); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(grad_weight); - CHECK_CUDA_INPUT(grad_bias); - sync_bn_backward_param_cuda(grad_output, norm, grad_weight, grad_bias); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(grad_weight); - CHECK_CUDA_INPUT(grad_bias); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(std); - CHECK_CUDA_INPUT(grad_input); - sync_bn_backward_data_cuda(grad_output, weight, grad_weight, grad_bias, - norm, std, grad_input); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/deprecated_wrappers.py b/cv/detection/autoassign/pytorch/mmcv/ops/deprecated_wrappers.py deleted file mode 100755 index a2e593df9..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/deprecated_wrappers.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# This file is for backward compatibility. -# Module wrappers for empty tensor have been moved to mmcv.cnn.bricks. -import warnings - -from ..cnn.bricks.wrappers import Conv2d, ConvTranspose2d, Linear, MaxPool2d - - -class Conv2d_deprecated(Conv2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Conv2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') - - -class ConvTranspose2d_deprecated(ConvTranspose2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing ConvTranspose2d wrapper from "mmcv.ops" will be ' - 'deprecated in the future. Please import them from "mmcv.cnn" ' - 'instead') - - -class MaxPool2d_deprecated(MaxPool2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing MaxPool2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') - - -class Linear_deprecated(Linear): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Linear wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/focal_loss.py b/cv/detection/autoassign/pytorch/mmcv/ops/focal_loss.py deleted file mode 100755 index 763bc93bd..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/focal_loss.py +++ /dev/null @@ -1,212 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sigmoid_focal_loss_forward', 'sigmoid_focal_loss_backward', - 'softmax_focal_loss_forward', 'softmax_focal_loss_backward' -]) - - -class SigmoidFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSigmoidFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - output = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_forward( - input, target, weight, output, gamma=ctx.gamma, alpha=ctx.alpha) - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input, target, weight) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - input, target, weight = ctx.saved_tensors - - grad_input = input.new_zeros(input.size()) - - ext_module.sigmoid_focal_loss_backward( - input, - target, - weight, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input.size(0) - return grad_input, None, None, None, None, None - - -sigmoid_focal_loss = SigmoidFocalLossFunction.apply - - -class SigmoidFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SigmoidFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return sigmoid_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s - - -class SoftmaxFocalLossFunction(Function): - - @staticmethod - def symbolic(g, input, target, gamma, alpha, weight, reduction): - return g.op( - 'mmcv::MMCVSoftmaxFocalLoss', - input, - target, - gamma_f=gamma, - alpha_f=alpha, - weight_f=weight, - reduction_s=reduction) - - @staticmethod - def forward(ctx, - input, - target, - gamma=2.0, - alpha=0.25, - weight=None, - reduction='mean'): - - assert isinstance(target, (torch.LongTensor, torch.cuda.LongTensor)) - assert input.dim() == 2 - assert target.dim() == 1 - assert input.size(0) == target.size(0) - if weight is None: - weight = input.new_empty(0) - else: - assert weight.dim() == 1 - assert input.size(1) == weight.size(0) - ctx.reduction_dict = {'none': 0, 'mean': 1, 'sum': 2} - assert reduction in ctx.reduction_dict.keys() - - ctx.gamma = float(gamma) - ctx.alpha = float(alpha) - ctx.reduction = ctx.reduction_dict[reduction] - - channel_stats, _ = torch.max(input, dim=1) - input_softmax = input - channel_stats.unsqueeze(1).expand_as(input) - input_softmax.exp_() - - channel_stats = input_softmax.sum(dim=1) - input_softmax /= channel_stats.unsqueeze(1).expand_as(input) - - output = input.new_zeros(input.size(0)) - ext_module.softmax_focal_loss_forward( - input_softmax, - target, - weight, - output, - gamma=ctx.gamma, - alpha=ctx.alpha) - - if ctx.reduction == ctx.reduction_dict['mean']: - output = output.sum() / input.size(0) - elif ctx.reduction == ctx.reduction_dict['sum']: - output = output.sum() - ctx.save_for_backward(input_softmax, target, weight) - return output - - @staticmethod - def backward(ctx, grad_output): - input_softmax, target, weight = ctx.saved_tensors - buff = input_softmax.new_zeros(input_softmax.size(0)) - grad_input = input_softmax.new_zeros(input_softmax.size()) - - ext_module.softmax_focal_loss_backward( - input_softmax, - target, - weight, - buff, - grad_input, - gamma=ctx.gamma, - alpha=ctx.alpha) - - grad_input *= grad_output - if ctx.reduction == ctx.reduction_dict['mean']: - grad_input /= input_softmax.size(0) - return grad_input, None, None, None, None, None - - -softmax_focal_loss = SoftmaxFocalLossFunction.apply - - -class SoftmaxFocalLoss(nn.Module): - - def __init__(self, gamma, alpha, weight=None, reduction='mean'): - super(SoftmaxFocalLoss, self).__init__() - self.gamma = gamma - self.alpha = alpha - self.register_buffer('weight', weight) - self.reduction = reduction - - def forward(self, input, target): - return softmax_focal_loss(input, target, self.gamma, self.alpha, - self.weight, self.reduction) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(gamma={self.gamma}, ' - s += f'alpha={self.alpha}, ' - s += f'reduction={self.reduction})' - return s diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/info.py b/cv/detection/autoassign/pytorch/mmcv/ops/info.py deleted file mode 100755 index 29f2e5598..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/info.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os - -import torch - -if torch.__version__ == 'parrots': - import parrots - - def get_compiler_version(): - return 'GCC ' + parrots.version.compiler - - def get_compiling_cuda_version(): - return parrots.version.cuda -else: - from ..utils import ext_loader - ext_module = ext_loader.load_ext( - '_ext', ['get_compiler_version', 'get_compiling_cuda_version']) - - def get_compiler_version(): - return ext_module.get_compiler_version() - - def get_compiling_cuda_version(): - return ext_module.get_compiling_cuda_version() - - -def get_onnxruntime_op_path(): - wildcard = os.path.join( - os.path.abspath(os.path.dirname(os.path.dirname(__file__))), - '_ext_ort.*.so') - - paths = glob.glob(wildcard) - if len(paths) > 0: - return paths[0] - else: - return '' diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/nms.py b/cv/detection/autoassign/pytorch/mmcv/ops/nms.py deleted file mode 100755 index 40ff7bee7..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/nms.py +++ /dev/null @@ -1,417 +0,0 @@ -import os - -import numpy as np -import torch - -from mmcv.utils import deprecated_api_warning -from ..utils import ext_loader - -ext_module = ext_loader.load_ext( - '_ext', ['nms', 'softnms', 'nms_match']) - - -# This function is modified from: https://github.com/pytorch/vision/ -class NMSop(torch.autograd.Function): - - @staticmethod - def forward(ctx, bboxes, scores, iou_threshold, offset, score_threshold, - max_num): - is_filtering_by_score = score_threshold > 0 - if is_filtering_by_score: - valid_mask = scores > score_threshold - bboxes, scores = bboxes[valid_mask], scores[valid_mask] - valid_inds = torch.nonzero( - valid_mask, as_tuple=False).squeeze(dim=1) - - inds = ext_module.nms( - bboxes, scores, iou_threshold=float(iou_threshold), offset=offset) - - if max_num > 0: - inds = inds[:max_num] - if is_filtering_by_score: - inds = valid_inds[inds] - return inds - - @staticmethod - def symbolic(g, bboxes, scores, iou_threshold, offset, score_threshold, - max_num): - from ..onnx import is_custom_op_loaded - has_custom_op = is_custom_op_loaded() - # TensorRT nms plugin is aligned with original nms in ONNXRuntime - is_trt_backend = os.environ.get('ONNX_BACKEND') == 'MMCVTensorRT' - if has_custom_op and (not is_trt_backend): - return g.op( - 'mmcv::NonMaxSuppression', - bboxes, - scores, - iou_threshold_f=float(iou_threshold), - offset_i=int(offset)) - else: - from torch.onnx.symbolic_opset9 import select, squeeze, unsqueeze - from ..onnx.onnx_utils.symbolic_helper import _size_helper - - boxes = unsqueeze(g, bboxes, 0) - scores = unsqueeze(g, unsqueeze(g, scores, 0), 0) - - if max_num > 0: - max_num = g.op( - 'Constant', - value_t=torch.tensor(max_num, dtype=torch.long)) - else: - dim = g.op('Constant', value_t=torch.tensor(0)) - max_num = _size_helper(g, bboxes, dim) - max_output_per_class = max_num - iou_threshold = g.op( - 'Constant', - value_t=torch.tensor([iou_threshold], dtype=torch.float)) - score_threshold = g.op( - 'Constant', - value_t=torch.tensor([score_threshold], dtype=torch.float)) - nms_out = g.op('NonMaxSuppression', boxes, scores, - max_output_per_class, iou_threshold, - score_threshold) - return squeeze( - g, - select( - g, nms_out, 1, - g.op( - 'Constant', - value_t=torch.tensor([2], dtype=torch.long))), 1) - - -class SoftNMSop(torch.autograd.Function): - - @staticmethod - def forward(ctx, boxes, scores, iou_threshold, sigma, min_score, method, - offset): - dets = boxes.new_empty((boxes.size(0), 5), device='cpu') - inds = ext_module.softnms( - boxes.cpu(), - scores.cpu(), - dets.cpu(), - iou_threshold=float(iou_threshold), - sigma=float(sigma), - min_score=float(min_score), - method=int(method), - offset=int(offset)) - return dets, inds - - @staticmethod - def symbolic(g, boxes, scores, iou_threshold, sigma, min_score, method, - offset): - from packaging import version - assert version.parse(torch.__version__) >= version.parse('1.7.0') - nms_out = g.op( - 'mmcv::SoftNonMaxSuppression', - boxes, - scores, - iou_threshold_f=float(iou_threshold), - sigma_f=float(sigma), - min_score_f=float(min_score), - method_i=int(method), - offset_i=int(offset), - outputs=2) - return nms_out - - -@deprecated_api_warning({'iou_thr': 'iou_threshold'}) -def nms(boxes, scores, iou_threshold, offset=0, score_threshold=0, max_num=-1): - """Dispatch to either CPU or GPU NMS implementations. - - The input can be either torch tensor or numpy array. GPU NMS will be used - if the input is gpu tensor, otherwise CPU NMS - will be used. The returned type will always be the same as inputs. - - Arguments: - boxes (torch.Tensor or np.ndarray): boxes in shape (N, 4). - scores (torch.Tensor or np.ndarray): scores in shape (N, ). - iou_threshold (float): IoU threshold for NMS. - offset (int, 0 or 1): boxes' width or height is (x2 - x1 + offset). - score_threshold (float): score threshold for NMS. - max_num (int): maximum number of boxes after NMS. - - Returns: - tuple: kept dets(boxes and scores) and indice, which is always the \ - same data type as the input. - - Example: - >>> boxes = np.array([[49.1, 32.4, 51.0, 35.9], - >>> [49.3, 32.9, 51.0, 35.3], - >>> [49.2, 31.8, 51.0, 35.4], - >>> [35.1, 11.5, 39.1, 15.7], - >>> [35.6, 11.8, 39.3, 14.2], - >>> [35.3, 11.5, 39.9, 14.5], - >>> [35.2, 11.7, 39.7, 15.7]], dtype=np.float32) - >>> scores = np.array([0.9, 0.9, 0.5, 0.5, 0.5, 0.4, 0.3],\ - dtype=np.float32) - >>> iou_threshold = 0.6 - >>> dets, inds = nms(boxes, scores, iou_threshold) - >>> assert len(inds) == len(dets) == 3 - """ - assert isinstance(boxes, (torch.Tensor, np.ndarray)) - assert isinstance(scores, (torch.Tensor, np.ndarray)) - is_numpy = False - if isinstance(boxes, np.ndarray): - is_numpy = True - boxes = torch.from_numpy(boxes) - if isinstance(scores, np.ndarray): - scores = torch.from_numpy(scores) - assert boxes.size(1) == 4 - assert boxes.size(0) == scores.size(0) - assert offset in (0, 1) - - if torch.__version__ == 'parrots': - indata_list = [boxes, scores] - indata_dict = { - 'iou_threshold': float(iou_threshold), - 'offset': int(offset) - } - inds = ext_module.nms(*indata_list, **indata_dict) - else: - inds = NMSop.apply(boxes, scores, iou_threshold, offset, - score_threshold, max_num) - dets = torch.cat((boxes[inds], scores[inds].reshape(-1, 1)), dim=1) - if is_numpy: - dets = dets.cpu().numpy() - inds = inds.cpu().numpy() - return dets, inds - - -@deprecated_api_warning({'iou_thr': 'iou_threshold'}) -def soft_nms(boxes, - scores, - iou_threshold=0.3, - sigma=0.5, - min_score=1e-3, - method='linear', - offset=0): - """Dispatch to only CPU Soft NMS implementations. - - The input can be either a torch tensor or numpy array. - The returned type will always be the same as inputs. - - Arguments: - boxes (torch.Tensor or np.ndarray): boxes in shape (N, 4). - scores (torch.Tensor or np.ndarray): scores in shape (N, ). - iou_threshold (float): IoU threshold for NMS. - sigma (float): hyperparameter for gaussian method - min_score (float): score filter threshold - method (str): either 'linear' or 'gaussian' - offset (int, 0 or 1): boxes' width or height is (x2 - x1 + offset). - - Returns: - tuple: kept dets(boxes and scores) and indice, which is always the \ - same data type as the input. - - Example: - >>> boxes = np.array([[4., 3., 5., 3.], - >>> [4., 3., 5., 4.], - >>> [3., 1., 3., 1.], - >>> [3., 1., 3., 1.], - >>> [3., 1., 3., 1.], - >>> [3., 1., 3., 1.]], dtype=np.float32) - >>> scores = np.array([0.9, 0.9, 0.5, 0.5, 0.4, 0.0], dtype=np.float32) - >>> iou_threshold = 0.6 - >>> dets, inds = soft_nms(boxes, scores, iou_threshold, sigma=0.5) - >>> assert len(inds) == len(dets) == 5 - """ - - assert isinstance(boxes, (torch.Tensor, np.ndarray)) - assert isinstance(scores, (torch.Tensor, np.ndarray)) - is_numpy = False - if isinstance(boxes, np.ndarray): - is_numpy = True - boxes = torch.from_numpy(boxes) - if isinstance(scores, np.ndarray): - scores = torch.from_numpy(scores) - assert boxes.size(1) == 4 - assert boxes.size(0) == scores.size(0) - assert offset in (0, 1) - method_dict = {'naive': 0, 'linear': 1, 'gaussian': 2} - assert method in method_dict.keys() - - if torch.__version__ == 'parrots': - dets = boxes.new_empty((boxes.size(0), 5), device='cpu') - indata_list = [boxes.cpu(), scores.cpu(), dets.cpu()] - indata_dict = { - 'iou_threshold': float(iou_threshold), - 'sigma': float(sigma), - 'min_score': min_score, - 'method': method_dict[method], - 'offset': int(offset) - } - inds = ext_module.softnms(*indata_list, **indata_dict) - else: - dets, inds = SoftNMSop.apply(boxes.cpu(), scores.cpu(), - float(iou_threshold), float(sigma), - float(min_score), method_dict[method], - int(offset)) - - dets = dets[:inds.size(0)] - - if is_numpy: - dets = dets.cpu().numpy() - inds = inds.cpu().numpy() - return dets, inds - else: - return dets.to(device=boxes.device), inds.to(device=boxes.device) - - -def batched_nms(boxes, scores, idxs, nms_cfg, class_agnostic=False): - """Performs non-maximum suppression in a batched fashion. - - Modified from https://github.com/pytorch/vision/blob - /505cd6957711af790211896d32b40291bea1bc21/torchvision/ops/boxes.py#L39. - In order to perform NMS independently per class, we add an offset to all - the boxes. The offset is dependent only on the class idx, and is large - enough so that boxes from different classes do not overlap. - - Arguments: - boxes (torch.Tensor): boxes in shape (N, 4). - scores (torch.Tensor): scores in shape (N, ). - idxs (torch.Tensor): each index value correspond to a bbox cluster, - and NMS will not be applied between elements of different idxs, - shape (N, ). - nms_cfg (dict): specify nms type and other parameters like iou_thr. - Possible keys includes the following. - - - iou_thr (float): IoU threshold used for NMS. - - split_thr (float): threshold number of boxes. In some cases the - number of boxes is large (e.g., 200k). To avoid OOM during - training, the users could set `split_thr` to a small value. - If the number of boxes is greater than the threshold, it will - perform NMS on each group of boxes separately and sequentially. - Defaults to 10000. - class_agnostic (bool): if true, nms is class agnostic, - i.e. IoU thresholding happens over all boxes, - regardless of the predicted class. - - Returns: - tuple: kept dets and indice. - """ - nms_cfg_ = nms_cfg.copy() - class_agnostic = nms_cfg_.pop('class_agnostic', class_agnostic) - if class_agnostic: - boxes_for_nms = boxes - else: - max_coordinate = boxes.max() - offsets = idxs.to(boxes) * (max_coordinate + torch.tensor(1).to(boxes)) - boxes_for_nms = boxes + offsets[:, None] - - nms_type = nms_cfg_.pop('type', 'nms') - nms_op = eval(nms_type) - - split_thr = nms_cfg_.pop('split_thr', 10000) - # Won't split to multiple nms nodes when exporting to onnx - if boxes_for_nms.shape[0] < split_thr or torch.onnx.is_in_onnx_export(): - dets, keep = nms_op(boxes_for_nms, scores, **nms_cfg_) - boxes = boxes[keep] - # -1 indexing works abnormal in TensorRT - # This assumes `dets` has 5 dimensions where - # the last dimension is score. - # TODO: more elegant way to handle the dimension issue. - # Some type of nms would reweight the score, such as SoftNMS - scores = dets[:, 4] - else: - max_num = nms_cfg_.pop('max_num', -1) - total_mask = scores.new_zeros(scores.size(), dtype=torch.bool) - # Some type of nms would reweight the score, such as SoftNMS - scores_after_nms = scores.new_zeros(scores.size()) - for id in torch.unique(idxs): - mask = (idxs == id).nonzero(as_tuple=False).view(-1) - dets, keep = nms_op(boxes_for_nms[mask], scores[mask], **nms_cfg_) - total_mask[mask[keep]] = True - scores_after_nms[mask[keep]] = dets[:, -1] - keep = total_mask.nonzero(as_tuple=False).view(-1) - - scores, inds = scores_after_nms[keep].sort(descending=True) - keep = keep[inds] - boxes = boxes[keep] - - if max_num > 0: - keep = keep[:max_num] - boxes = boxes[:max_num] - scores = scores[:max_num] - - return torch.cat([boxes, scores[:, None]], -1), keep - - -def nms_match(dets, iou_threshold): - """Matched dets into different groups by NMS. - - NMS match is Similar to NMS but when a bbox is suppressed, nms match will - record the indice of suppressed bbox and form a group with the indice of - kept bbox. In each group, indice is sorted as score order. - - Arguments: - dets (torch.Tensor | np.ndarray): Det boxes with scores, shape (N, 5). - iou_thr (float): IoU thresh for NMS. - - Returns: - List[torch.Tensor | np.ndarray]: The outer list corresponds different - matched group, the inner Tensor corresponds the indices for a group - in score order. - """ - if dets.shape[0] == 0: - matched = [] - else: - assert dets.shape[-1] == 5, 'inputs dets.shape should be (N, 5), ' \ - f'but get {dets.shape}' - if isinstance(dets, torch.Tensor): - dets_t = dets.detach().cpu() - else: - dets_t = torch.from_numpy(dets) - indata_list = [dets_t] - indata_dict = {'iou_threshold': float(iou_threshold)} - matched = ext_module.nms_match(*indata_list, **indata_dict) - if torch.__version__ == 'parrots': - matched = matched.tolist() - - if isinstance(dets, torch.Tensor): - return [dets.new_tensor(m, dtype=torch.long) for m in matched] - else: - return [np.array(m, dtype=np.int) for m in matched] - - -def nms_rotated(dets, scores, iou_threshold, labels=None): - """Performs non-maximum suppression (NMS) on the rotated boxes according to - their intersection-over-union (IoU). - - Rotated NMS iteratively removes lower scoring rotated boxes which have an - IoU greater than iou_threshold with another (higher scoring) rotated box. - - Args: - boxes (Tensor): Rotated boxes in shape (N, 5). They are expected to \ - be in (x_ctr, y_ctr, width, height, angle_radian) format. - scores (Tensor): scores in shape (N, ). - iou_threshold (float): IoU thresh for NMS. - labels (Tensor): boxes' label in shape (N,). - - Returns: - tuple: kept dets(boxes and scores) and indice, which is always the \ - same data type as the input. - """ - if dets.shape[0] == 0: - return dets, None - multi_label = labels is not None - if multi_label: - dets_wl = torch.cat((dets, labels.unsqueeze(1)), 1) - else: - dets_wl = dets - _, order = scores.sort(0, descending=True) - dets_sorted = dets_wl.index_select(0, order) - - if torch.__version__ == 'parrots': - keep_inds = ext_module.nms_rotated( - dets_wl, - scores, - order, - dets_sorted, - iou_threshold=iou_threshold, - multi_label=multi_label) - else: - keep_inds = ext_module.nms_rotated(dets_wl, scores, order, dets_sorted, - iou_threshold, multi_label) - dets = torch.cat((dets[keep_inds], scores[keep_inds].reshape(-1, 1)), - dim=1) - return dets, keep_inds diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/point_sample.py b/cv/detection/autoassign/pytorch/mmcv/ops/point_sample.py deleted file mode 100755 index c084a8c22..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/point_sample.py +++ /dev/null @@ -1,336 +0,0 @@ -# Modified from https://github.com/facebookresearch/detectron2/tree/master/projects/PointRend # noqa - -from os import path as osp - -import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.nn.modules.utils import _pair -from torch.onnx.operators import shape_as_tensor - - -def bilinear_grid_sample(im, grid, align_corners=False): - """Given an input and a flow-field grid, computes the output using input - values and pixel locations from grid. Supported only bilinear interpolation - method to sample the input pixels. - - Args: - im (torch.Tensor): Input feature map, shape (N, C, H, W) - grid (torch.Tensor): Point coordinates, shape (N, Hg, Wg, 2) - align_corners {bool}: If set to True, the extrema (-1 and 1) are - considered as referring to the center points of the input’s - corner pixels. If set to False, they are instead considered as - referring to the corner points of the input’s corner pixels, - making the sampling more resolution agnostic. - Returns: - torch.Tensor: A tensor with sampled points, shape (N, C, Hg, Wg) - """ - n, c, h, w = im.shape - gn, gh, gw, _ = grid.shape - assert n == gn - - x = grid[:, :, :, 0] - y = grid[:, :, :, 1] - - if align_corners: - x = ((x + 1) / 2) * (w - 1) - y = ((y + 1) / 2) * (h - 1) - else: - x = ((x + 1) * w - 1) / 2 - y = ((y + 1) * h - 1) / 2 - - x = x.view(n, -1) - y = y.view(n, -1) - - x0 = torch.floor(x).long() - y0 = torch.floor(y).long() - x1 = x0 + 1 - y1 = y0 + 1 - - wa = ((x1 - x) * (y1 - y)).unsqueeze(1) - wb = ((x1 - x) * (y - y0)).unsqueeze(1) - wc = ((x - x0) * (y1 - y)).unsqueeze(1) - wd = ((x - x0) * (y - y0)).unsqueeze(1) - - # Apply default for grid_sample function zero padding - im_padded = F.pad(im, pad=[1, 1, 1, 1], mode='constant', value=0) - padded_h = h + 2 - padded_w = w + 2 - # save points positions after padding - x0, x1, y0, y1 = x0 + 1, x1 + 1, y0 + 1, y1 + 1 - - # Clip coordinates to padded image size - x0 = torch.where(x0 < 0, torch.tensor(0), x0) - x0 = torch.where(x0 > padded_w - 1, torch.tensor(padded_w - 1), x0) - x1 = torch.where(x1 < 0, torch.tensor(0), x1) - x1 = torch.where(x1 > padded_w - 1, torch.tensor(padded_w - 1), x1) - y0 = torch.where(y0 < 0, torch.tensor(0), y0) - y0 = torch.where(y0 > padded_h - 1, torch.tensor(padded_h - 1), y0) - y1 = torch.where(y1 < 0, torch.tensor(0), y1) - y1 = torch.where(y1 > padded_h - 1, torch.tensor(padded_h - 1), y1) - - im_padded = im_padded.view(n, c, -1) - - x0_y0 = (x0 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x0_y1 = (x0 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y0 = (x1 + y0 * padded_w).unsqueeze(1).expand(-1, c, -1) - x1_y1 = (x1 + y1 * padded_w).unsqueeze(1).expand(-1, c, -1) - - Ia = torch.gather(im_padded, 2, x0_y0) - Ib = torch.gather(im_padded, 2, x0_y1) - Ic = torch.gather(im_padded, 2, x1_y0) - Id = torch.gather(im_padded, 2, x1_y1) - - return (Ia * wa + Ib * wb + Ic * wc + Id * wd).reshape(n, c, gh, gw) - - -def is_in_onnx_export_without_custom_ops(): - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - return torch.onnx.is_in_onnx_export( - ) and not osp.exists(ort_custom_op_path) - - -def normalize(grid): - """Normalize input grid from [-1, 1] to [0, 1] - Args: - grid (Tensor): The grid to be normalize, range [-1, 1]. - Returns: - Tensor: Normalized grid, range [0, 1]. - """ - - return (grid + 1.0) / 2.0 - - -def denormalize(grid): - """Denormalize input grid from range [0, 1] to [-1, 1] - Args: - grid (Tensor): The grid to be denormalize, range [0, 1]. - Returns: - Tensor: Denormalized grid, range [-1, 1]. - """ - - return grid * 2.0 - 1.0 - - -def generate_grid(num_grid, size, device): - """Generate regular square grid of points in [0, 1] x [0, 1] coordinate - space. - - Args: - num_grid (int): The number of grids to sample, one for each region. - size (tuple(int, int)): The side size of the regular grid. - device (torch.device): Desired device of returned tensor. - - Returns: - (torch.Tensor): A tensor of shape (num_grid, size[0]*size[1], 2) that - contains coordinates for the regular grids. - """ - - affine_trans = torch.tensor([[[1., 0., 0.], [0., 1., 0.]]], device=device) - grid = F.affine_grid( - affine_trans, torch.Size((1, 1, *size)), align_corners=False) - grid = normalize(grid) - return grid.view(1, -1, 2).expand(num_grid, -1, -1) - - -def rel_roi_point_to_abs_img_point(rois, rel_roi_points): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (Tensor): Point coordinates inside RoI, relative to - RoI, location, range (0, 1), shape (N, P, 2) - Returns: - Tensor: Image based absolute point coordinates, shape (N, P, 2) - """ - - with torch.no_grad(): - assert rel_roi_points.size(0) == rois.size(0) - assert rois.dim() == 2 - assert rel_roi_points.dim() == 3 - assert rel_roi_points.size(2) == 2 - # remove batch idx - if rois.size(1) == 5: - rois = rois[:, 1:] - abs_img_points = rel_roi_points.clone() - # To avoid an error during exporting to onnx use independent - # variables instead inplace computation - xs = abs_img_points[:, :, 0] * (rois[:, None, 2] - rois[:, None, 0]) - ys = abs_img_points[:, :, 1] * (rois[:, None, 3] - rois[:, None, 1]) - xs += rois[:, None, 0] - ys += rois[:, None, 1] - abs_img_points = torch.stack([xs, ys], dim=2) - return abs_img_points - - -def get_shape_from_feature_map(x): - """Get spatial resolution of input feature map considering exporting to - onnx mode. - - Args: - x (torch.Tensor): Input tensor, shape (N, C, H, W) - Returns: - torch.Tensor: Spatial resolution (width, height), shape (1, 1, 2) - """ - if torch.onnx.is_in_onnx_export(): - img_shape = shape_as_tensor(x)[2:].flip(0).view(1, 1, 2).to( - x.device).float() - else: - img_shape = torch.tensor(x.shape[2:]).flip(0).view(1, 1, 2).to( - x.device).float() - return img_shape - - -def abs_img_point_to_rel_img_point(abs_img_points, img, spatial_scale=1.): - """Convert image based absolute point coordinates to image based relative - coordinates for sampling. - - Args: - abs_img_points (Tensor): Image based absolute point coordinates, - shape (N, P, 2) - img (tuple/Tensor): (height, width) of image or feature map. - spatial_scale (float): Scale points by this factor. Default: 1. - - Returns: - Tensor: Image based relative point coordinates for sampling, - shape (N, P, 2) - """ - - assert (isinstance(img, tuple) and len(img) == 2) or \ - (isinstance(img, torch.Tensor) and len(img.shape) == 4) - - if isinstance(img, tuple): - h, w = img - scale = torch.tensor([w, h], - dtype=torch.float, - device=abs_img_points.device) - scale = scale.view(1, 1, 2) - else: - scale = get_shape_from_feature_map(img) - - return abs_img_points / scale * spatial_scale - - -def rel_roi_point_to_rel_img_point(rois, - rel_roi_points, - img, - spatial_scale=1.): - """Convert roi based relative point coordinates to image based absolute - point coordinates. - - Args: - rois (Tensor): RoIs or BBoxes, shape (N, 4) or (N, 5) - rel_roi_points (Tensor): Point coordinates inside RoI, relative to - RoI, location, range (0, 1), shape (N, P, 2) - img (tuple/Tensor): (height, width) of image or feature map. - spatial_scale (float): Scale points by this factor. Default: 1. - - Returns: - Tensor: Image based relative point coordinates for sampling, - shape (N, P, 2) - """ - - abs_img_point = rel_roi_point_to_abs_img_point(rois, rel_roi_points) - rel_img_point = abs_img_point_to_rel_img_point(abs_img_point, img, - spatial_scale) - - return rel_img_point - - -def point_sample(input, points, align_corners=False, **kwargs): - """A wrapper around :func:`grid_sample` to support 3D point_coords tensors - Unlike :func:`torch.nn.functional.grid_sample` it assumes point_coords to - lie inside ``[0, 1] x [0, 1]`` square. - - Args: - input (Tensor): Feature map, shape (N, C, H, W). - points (Tensor): Image based absolute point coordinates (normalized), - range [0, 1] x [0, 1], shape (N, P, 2) or (N, Hgrid, Wgrid, 2). - align_corners (bool): Whether align_corners. Default: False - - Returns: - Tensor: Features of `point` on `input`, shape (N, C, P) or - (N, C, Hgrid, Wgrid). - """ - - add_dim = False - if points.dim() == 3: - add_dim = True - points = points.unsqueeze(2) - if is_in_onnx_export_without_custom_ops(): - # If custom ops for onnx runtime not compiled use python - # implementation of grid_sample function to make onnx graph - # with supported nodes - output = bilinear_grid_sample( - input, denormalize(points), align_corners=align_corners) - else: - output = F.grid_sample( - input, denormalize(points), align_corners=align_corners, **kwargs) - if add_dim: - output = output.squeeze(3) - return output - - -class SimpleRoIAlign(nn.Module): - - def __init__(self, output_size, spatial_scale, aligned=True): - """Simple RoI align in PointRend, faster than standard RoIAlign. - - Args: - output_size (tuple[int]): h, w - spatial_scale (float): scale the input boxes by this number - aligned (bool): if False, use the legacy implementation in - MMDetection, align_corners=True will be used in F.grid_sample. - If True, align the results more perfectly. - """ - - super(SimpleRoIAlign, self).__init__() - self.output_size = _pair(output_size) - self.spatial_scale = float(spatial_scale) - # to be consistent with other RoI ops - self.use_torchvision = False - self.aligned = aligned - - def forward(self, features, rois): - num_imgs = features.size(0) - num_rois = rois.size(0) - rel_roi_points = generate_grid( - num_rois, self.output_size, device=rois.device) - - if torch.onnx.is_in_onnx_export(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois, rel_roi_points, features, self.spatial_scale) - rel_img_points = rel_img_points.reshape(num_imgs, -1, - *rel_img_points.shape[1:]) - point_feats = point_sample( - features, rel_img_points, align_corners=not self.aligned) - point_feats = point_feats.transpose(1, 2) - else: - point_feats = [] - for batch_ind in range(num_imgs): - # unravel batch dim - feat = features[batch_ind].unsqueeze(0) - inds = (rois[:, 0].long() == batch_ind) - if inds.any(): - rel_img_points = rel_roi_point_to_rel_img_point( - rois[inds], rel_roi_points[inds], feat, - self.spatial_scale).unsqueeze(0) - point_feat = point_sample( - feat, rel_img_points, align_corners=not self.aligned) - point_feat = point_feat.squeeze(0).transpose(0, 1) - point_feats.append(point_feat) - - point_feats = torch.cat(point_feats, dim=0) - - channels = features.size(1) - roi_feats = point_feats.reshape(num_rois, channels, *self.output_size) - - return roi_feats - - def __repr__(self): - format_str = self.__class__.__name__ - format_str += '(output_size={}, spatial_scale={}'.format( - self.output_size, self.spatial_scale) - return format_str diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/roi_align.py b/cv/detection/autoassign/pytorch/mmcv/ops/roi_align.py deleted file mode 100755 index 0755aefc6..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/roi_align.py +++ /dev/null @@ -1,223 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.utils import _pair - -from ..utils import deprecated_api_warning, ext_loader - -ext_module = ext_loader.load_ext('_ext', - ['roi_align_forward', 'roi_align_backward']) - - -class RoIAlignFunction(Function): - - @staticmethod - def symbolic(g, input, rois, output_size, spatial_scale, sampling_ratio, - pool_mode, aligned): - from ..onnx import is_custom_op_loaded - has_custom_op = is_custom_op_loaded() - if has_custom_op: - return g.op( - 'mmcv::MMCVRoiAlign', - input, - rois, - output_height_i=output_size[0], - output_width_i=output_size[1], - spatial_scale_f=spatial_scale, - sampling_ratio_i=sampling_ratio, - mode_s=pool_mode, - aligned_i=aligned) - else: - from torch.onnx.symbolic_opset9 import sub, squeeze - from torch.onnx.symbolic_helper import _slice_helper - from torch.onnx import TensorProtoDataType - # batch_indices = rois[:, 0].long() - batch_indices = _slice_helper( - g, rois, axes=[1], starts=[0], ends=[1]) - batch_indices = squeeze(g, batch_indices, 1) - batch_indices = g.op( - 'Cast', batch_indices, to_i=TensorProtoDataType.INT64) - # rois = rois[:, 1:] - rois = _slice_helper(g, rois, axes=[1], starts=[1], ends=[5]) - if aligned: - # rois -= 0.5/spatial_scale - aligned_offset = g.op( - 'Constant', - value_t=torch.tensor([0.5 / spatial_scale], - dtype=torch.float32)) - rois = sub(g, rois, aligned_offset) - # roi align - return g.op( - 'RoiAlign', - input, - rois, - batch_indices, - output_height_i=output_size[0], - output_width_i=output_size[1], - spatial_scale_f=spatial_scale, - sampling_ratio_i=max(0, sampling_ratio), - mode_s=pool_mode) - - @staticmethod - def forward(ctx, - input, - rois, - output_size, - spatial_scale=1.0, - sampling_ratio=0, - pool_mode='avg', - aligned=True): - ctx.output_size = _pair(output_size) - ctx.spatial_scale = spatial_scale - ctx.sampling_ratio = sampling_ratio - assert pool_mode in ('max', 'avg') - ctx.pool_mode = 0 if pool_mode == 'max' else 1 - ctx.aligned = aligned - ctx.input_shape = input.size() - - assert rois.size(1) == 5, 'RoI must be (idx, x1, y1, x2, y2)!' - - output_shape = (rois.size(0), input.size(1), ctx.output_size[0], - ctx.output_size[1]) - output = input.new_zeros(output_shape) - if ctx.pool_mode == 0: - argmax_y = input.new_zeros(output_shape) - argmax_x = input.new_zeros(output_shape) - else: - argmax_y = input.new_zeros(0) - argmax_x = input.new_zeros(0) - - ext_module.roi_align_forward( - input, - rois, - output, - argmax_y, - argmax_x, - aligned_height=ctx.output_size[0], - aligned_width=ctx.output_size[1], - spatial_scale=ctx.spatial_scale, - sampling_ratio=ctx.sampling_ratio, - pool_mode=ctx.pool_mode, - aligned=ctx.aligned) - - ctx.save_for_backward(rois, argmax_y, argmax_x) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - rois, argmax_y, argmax_x = ctx.saved_tensors - grad_input = grad_output.new_zeros(ctx.input_shape) - # complex head architecture may cause grad_output uncontiguous. - grad_output = grad_output.contiguous() - ext_module.roi_align_backward( - grad_output, - rois, - argmax_y, - argmax_x, - grad_input, - aligned_height=ctx.output_size[0], - aligned_width=ctx.output_size[1], - spatial_scale=ctx.spatial_scale, - sampling_ratio=ctx.sampling_ratio, - pool_mode=ctx.pool_mode, - aligned=ctx.aligned) - return grad_input, None, None, None, None, None, None - - -roi_align = RoIAlignFunction.apply - - -class RoIAlign(nn.Module): - """RoI align pooling layer. - - Args: - output_size (tuple): h, w - spatial_scale (float): scale the input boxes by this number - sampling_ratio (int): number of inputs samples to take for each - output sample. 0 to take samples densely for current models. - pool_mode (str, 'avg' or 'max'): pooling mode in each bin. - aligned (bool): if False, use the legacy implementation in - MMDetection. If True, align the results more perfectly. - use_torchvision (bool): whether to use roi_align from torchvision. - - Note: - The implementation of RoIAlign when aligned=True is modified from - https://github.com/facebookresearch/detectron2/ - - The meaning of aligned=True: - - Given a continuous coordinate c, its two neighboring pixel - indices (in our pixel model) are computed by floor(c - 0.5) and - ceil(c - 0.5). For example, c=1.3 has pixel neighbors with discrete - indices [0] and [1] (which are sampled from the underlying signal - at continuous coordinates 0.5 and 1.5). But the original roi_align - (aligned=False) does not subtract the 0.5 when computing - neighboring pixel indices and therefore it uses pixels with a - slightly incorrect alignment (relative to our pixel model) when - performing bilinear interpolation. - - With `aligned=True`, - we first appropriately scale the ROI and then shift it by -0.5 - prior to calling roi_align. This produces the correct neighbors; - - The difference does not make a difference to the model's - performance if ROIAlign is used together with conv layers. - """ - - @deprecated_api_warning( - { - 'out_size': 'output_size', - 'sample_num': 'sampling_ratio' - }, - cls_name='RoIAlign') - def __init__(self, - output_size, - spatial_scale=1.0, - sampling_ratio=0, - pool_mode='avg', - aligned=True, - use_torchvision=False): - super(RoIAlign, self).__init__() - - self.output_size = _pair(output_size) - self.spatial_scale = float(spatial_scale) - self.sampling_ratio = int(sampling_ratio) - self.pool_mode = pool_mode - self.aligned = aligned - self.use_torchvision = use_torchvision - - def forward(self, input, rois): - """ - Args: - input: NCHW images - rois: Bx5 boxes. First column is the index into N.\ - The other 4 columns are xyxy. - """ - if self.use_torchvision: - from torchvision.ops import roi_align as tv_roi_align - if 'aligned' in tv_roi_align.__code__.co_varnames: - return tv_roi_align(input, rois, self.output_size, - self.spatial_scale, self.sampling_ratio, - self.aligned) - else: - if self.aligned: - rois -= rois.new_tensor([0.] + - [0.5 / self.spatial_scale] * 4) - return tv_roi_align(input, rois, self.output_size, - self.spatial_scale, self.sampling_ratio) - else: - return roi_align(input, rois, self.output_size, self.spatial_scale, - self.sampling_ratio, self.pool_mode, self.aligned) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(output_size={self.output_size}, ' - s += f'spatial_scale={self.spatial_scale}, ' - s += f'sampling_ratio={self.sampling_ratio}, ' - s += f'pool_mode={self.pool_mode}, ' - s += f'aligned={self.aligned}, ' - s += f'use_torchvision={self.use_torchvision})' - return s diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/roi_pool.py b/cv/detection/autoassign/pytorch/mmcv/ops/roi_pool.py deleted file mode 100755 index d339d8f29..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/roi_pool.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.utils import _pair - -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', - ['roi_pool_forward', 'roi_pool_backward']) - - -class RoIPoolFunction(Function): - - @staticmethod - def symbolic(g, input, rois, output_size, spatial_scale): - return g.op( - 'MaxRoiPool', - input, - rois, - pooled_shape_i=output_size, - spatial_scale_f=spatial_scale) - - @staticmethod - def forward(ctx, input, rois, output_size, spatial_scale=1.0): - ctx.output_size = _pair(output_size) - ctx.spatial_scale = spatial_scale - ctx.input_shape = input.size() - - assert rois.size(1) == 5, 'RoI must be (idx, x1, y1, x2, y2)!' - - output_shape = (rois.size(0), input.size(1), ctx.output_size[0], - ctx.output_size[1]) - output = input.new_zeros(output_shape) - argmax = input.new_zeros(output_shape, dtype=torch.int) - - ext_module.roi_pool_forward( - input, - rois, - output, - argmax, - pooled_height=ctx.output_size[0], - pooled_width=ctx.output_size[1], - spatial_scale=ctx.spatial_scale) - - ctx.save_for_backward(rois, argmax) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - rois, argmax = ctx.saved_tensors - grad_input = grad_output.new_zeros(ctx.input_shape) - - ext_module.roi_pool_backward( - grad_output, - rois, - argmax, - grad_input, - pooled_height=ctx.output_size[0], - pooled_width=ctx.output_size[1], - spatial_scale=ctx.spatial_scale) - - return grad_input, None, None, None - - -roi_pool = RoIPoolFunction.apply - - -class RoIPool(nn.Module): - - def __init__(self, output_size, spatial_scale=1.0): - super(RoIPool, self).__init__() - - self.output_size = _pair(output_size) - self.spatial_scale = float(spatial_scale) - - def forward(self, input, rois): - return roi_pool(input, rois, self.output_size, self.spatial_scale) - - def __repr__(self): - s = self.__class__.__name__ - s += f'(output_size={self.output_size}, ' - s += f'spatial_scale={self.spatial_scale})' - return s diff --git a/cv/detection/autoassign/pytorch/mmcv/ops/sync_bn.py b/cv/detection/autoassign/pytorch/mmcv/ops/sync_bn.py deleted file mode 100755 index 04302f031..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/ops/sync_bn.py +++ /dev/null @@ -1,279 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn.functional as F -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.module import Module -from torch.nn.parameter import Parameter - -from mmcv.cnn import NORM_LAYERS -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sync_bn_forward_mean', 'sync_bn_forward_var', 'sync_bn_forward_output', - 'sync_bn_backward_param', 'sync_bn_backward_data' -]) - - -class SyncBatchNormFunction(Function): - - @staticmethod - def symbolic(g, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - return g.op( - 'mmcv::MMCVSyncBatchNorm', - input, - running_mean, - running_var, - weight, - bias, - momentum_f=momentum, - eps_f=eps, - group_i=group, - group_size_i=group_size, - stats_mode=stats_mode) - - @staticmethod - def forward(self, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - self.momentum = momentum - self.eps = eps - self.group = group - self.group_size = group_size - self.stats_mode = stats_mode - - assert isinstance( - input, (torch.HalfTensor, torch.FloatTensor, - torch.cuda.HalfTensor, torch.cuda.FloatTensor)), \ - f'only support Half or Float Tensor, but {input.type()}' - output = torch.zeros_like(input) - input3d = input.flatten(start_dim=2) - output3d = output.view_as(input3d) - num_channels = input3d.size(1) - - # ensure mean/var/norm/std are initialized as zeros - # ``torch.empty()`` does not guarantee that - mean = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - var = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - norm = torch.zeros_like( - input3d, dtype=torch.float, device=input3d.device) - std = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - - batch_size = input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_forward_mean(input3d, mean) - batch_flag = torch.ones([1], device=mean.device, dtype=mean.dtype) - else: - # skip updating mean and leave it as zeros when the input is empty - batch_flag = torch.zeros([1], device=mean.device, dtype=mean.dtype) - - # synchronize mean and the batch flag - vec = torch.cat([mean, batch_flag]) - if self.stats_mode == 'N': - vec *= batch_size - if self.group_size > 1: - dist.all_reduce(vec, group=self.group) - total_batch = vec[-1].detach() - mean = vec[:num_channels] - - if self.stats_mode == 'default': - mean = mean / self.group_size - elif self.stats_mode == 'N': - mean = mean / total_batch.clamp(min=1) - else: - raise NotImplementedError - - # leave var as zeros when the input is empty - if batch_size > 0: - ext_module.sync_bn_forward_var(input3d, mean, var) - - if self.stats_mode == 'N': - var *= batch_size - if self.group_size > 1: - dist.all_reduce(var, group=self.group) - - if self.stats_mode == 'default': - var /= self.group_size - elif self.stats_mode == 'N': - var /= total_batch.clamp(min=1) - else: - raise NotImplementedError - - # if the total batch size over all the ranks is zero, - # we should not update the statistics in the current batch - update_flag = total_batch.clamp(max=1) - momentum = update_flag * self.momentum - ext_module.sync_bn_forward_output( - input3d, - mean, - var, - weight, - bias, - running_mean, - running_var, - norm, - std, - output3d, - eps=self.eps, - momentum=momentum, - group_size=self.group_size) - self.save_for_backward(norm, std, weight) - return output - - @staticmethod - @once_differentiable - def backward(self, grad_output): - norm, std, weight = self.saved_tensors - grad_weight = torch.zeros_like(weight) - grad_bias = torch.zeros_like(weight) - grad_input = torch.zeros_like(grad_output) - grad_output3d = grad_output.flatten(start_dim=2) - grad_input3d = grad_input.view_as(grad_output3d) - - batch_size = grad_input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_backward_param(grad_output3d, norm, grad_weight, - grad_bias) - - # all reduce - if self.group_size > 1: - dist.all_reduce(grad_weight, group=self.group) - dist.all_reduce(grad_bias, group=self.group) - grad_weight /= self.group_size - grad_bias /= self.group_size - - if batch_size > 0: - ext_module.sync_bn_backward_data(grad_output3d, weight, - grad_weight, grad_bias, norm, std, - grad_input3d) - - return grad_input, None, None, grad_weight, grad_bias, \ - None, None, None, None, None - - -@NORM_LAYERS.register_module(name='MMSyncBN') -class SyncBatchNorm(Module): - """Synchronized Batch Normalization. - - Args: - num_features (int): number of features/chennels in input tensor - eps (float, optional): a value added to the denominator for numerical - stability. Defaults to 1e-5. - momentum (float, optional): the value used for the running_mean and - running_var computation. Defaults to 0.1. - affine (bool, optional): whether to use learnable affine parameters. - Defaults to True. - track_running_stats (bool, optional): whether to track the running - mean and variance during training. When set to False, this - module does not track such statistics, and initializes statistics - buffers ``running_mean`` and ``running_var`` as ``None``. When - these buffers are ``None``, this module always uses batch - statistics in both training and eval modes. Defaults to True. - group (int, optional): synchronization of stats happen within - each process group individually. By default it is synchronization - across the whole world. Defaults to None. - stats_mode (str, optional): The statistical mode. Available options - includes ``'default'`` and ``'N'``. Defaults to 'default'. - When ``stats_mode=='default'``, it computes the overall statistics - using those from each worker with equal weight, i.e., the - statistics are synchronized and simply divied by ``group``. This - mode will produce inaccurate statistics when empty tensors occur. - When ``stats_mode=='N'``, it compute the overall statistics using - the total number of batches in each worker ignoring the number of - group, i.e., the statistics are synchronized and then divied by - the total batch ``N``. This mode is beneficial when empty tensors - occur during training, as it average the total mean by the real - number of batch. - """ - - def __init__(self, - num_features, - eps=1e-5, - momentum=0.1, - affine=True, - track_running_stats=True, - group=None, - stats_mode='default'): - super(SyncBatchNorm, self).__init__() - self.num_features = num_features - self.eps = eps - self.momentum = momentum - self.affine = affine - self.track_running_stats = track_running_stats - group = dist.group.WORLD if group is None else group - self.group = group - self.group_size = dist.get_world_size(group) - assert stats_mode in ['default', 'N'], \ - f'"stats_mode" only accepts "default" and "N", got "{stats_mode}"' - self.stats_mode = stats_mode - if self.affine: - self.weight = Parameter(torch.Tensor(num_features)) - self.bias = Parameter(torch.Tensor(num_features)) - else: - self.register_parameter('weight', None) - self.register_parameter('bias', None) - if self.track_running_stats: - self.register_buffer('running_mean', torch.zeros(num_features)) - self.register_buffer('running_var', torch.ones(num_features)) - self.register_buffer('num_batches_tracked', - torch.tensor(0, dtype=torch.long)) - else: - self.register_buffer('running_mean', None) - self.register_buffer('running_var', None) - self.register_buffer('num_batches_tracked', None) - self.reset_parameters() - - def reset_running_stats(self): - if self.track_running_stats: - self.running_mean.zero_() - self.running_var.fill_(1) - self.num_batches_tracked.zero_() - - def reset_parameters(self): - self.reset_running_stats() - if self.affine: - self.weight.data.uniform_() # pytorch use ones_() - self.bias.data.zero_() - - def forward(self, input): - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input, got {input.dim()}D input') - if self.momentum is None: - exponential_average_factor = 0.0 - else: - exponential_average_factor = self.momentum - - if self.training and self.track_running_stats: - if self.num_batches_tracked is not None: - self.num_batches_tracked += 1 - if self.momentum is None: # use cumulative moving average - exponential_average_factor = 1.0 / float( - self.num_batches_tracked) - else: # use exponential moving average - exponential_average_factor = self.momentum - - if self.training or not self.track_running_stats: - return SyncBatchNormFunction.apply( - input, self.running_mean, self.running_var, self.weight, - self.bias, exponential_average_factor, self.eps, self.group, - self.group_size, self.stats_mode) - else: - return F.batch_norm(input, self.running_mean, self.running_var, - self.weight, self.bias, False, - exponential_average_factor, self.eps) - - def __repr__(self): - s = self.__class__.__name__ - s += f'({self.num_features}, ' - s += f'eps={self.eps}, ' - s += f'momentum={self.momentum}, ' - s += f'affine={self.affine}, ' - s += f'track_running_stats={self.track_running_stats}, ' - s += f'group_size={self.group_size},' - s += f'stats_mode={self.stats_mode})' - return s diff --git a/cv/detection/autoassign/pytorch/mmcv/parallel/__init__.py b/cv/detection/autoassign/pytorch/mmcv/parallel/__init__.py deleted file mode 100755 index 2ed2c17ad..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/parallel/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collate import collate -from .data_container import DataContainer -from .data_parallel import MMDataParallel -from .distributed import MMDistributedDataParallel -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter, scatter_kwargs -from .utils import is_module_wrapper - -__all__ = [ - 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', - 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' -] diff --git a/cv/detection/autoassign/pytorch/mmcv/parallel/_functions.py b/cv/detection/autoassign/pytorch/mmcv/parallel/_functions.py deleted file mode 100755 index 9b5a8a444..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/parallel/_functions.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import _get_stream - - -def scatter(input, devices, streams=None): - """Scatters tensor across multiple GPUs.""" - if streams is None: - streams = [None] * len(devices) - - if isinstance(input, list): - chunk_size = (len(input) - 1) // len(devices) + 1 - outputs = [ - scatter(input[i], [devices[i // chunk_size]], - [streams[i // chunk_size]]) for i in range(len(input)) - ] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - # TODO: copy to a pinned buffer first (if copying from CPU) - stream = streams[0] if output.numel() > 0 else None - if devices != [-1]: - with torch.cuda.device(devices[0]), torch.cuda.stream(stream): - output = output.cuda(devices[0], non_blocking=True) - else: - # unsqueeze the first dimension thus the tensor's shape is the - # same as those scattered with GPU. - output = output.unsqueeze(0) - return output - else: - raise Exception(f'Unknown type {type(input)}.') - - -def synchronize_stream(output, devices, streams): - if isinstance(output, list): - chunk_size = len(output) // len(devices) - for i in range(len(devices)): - for j in range(chunk_size): - synchronize_stream(output[i * chunk_size + j], [devices[i]], - [streams[i]]) - elif isinstance(output, torch.Tensor): - if output.numel() != 0: - with torch.cuda.device(devices[0]): - main_stream = torch.cuda.current_stream() - main_stream.wait_stream(streams[0]) - output.record_stream(main_stream) - else: - raise Exception(f'Unknown type {type(output)}.') - - -def get_input_device(input): - if isinstance(input, list): - for item in input: - input_device = get_input_device(item) - if input_device != -1: - return input_device - return -1 - elif isinstance(input, torch.Tensor): - return input.get_device() if input.is_cuda else -1 - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_gpus, input): - input_device = get_input_device(input) - streams = None - if input_device == -1 and target_gpus != [-1]: - # Perform CPU to GPU copies in a background stream - streams = [_get_stream(device) for device in target_gpus] - - outputs = scatter(input, target_gpus, streams) - # Synchronize with the copy stream - if streams is not None: - synchronize_stream(outputs, target_gpus, streams) - - return tuple(outputs) diff --git a/cv/detection/autoassign/pytorch/mmcv/parallel/collate.py b/cv/detection/autoassign/pytorch/mmcv/parallel/collate.py deleted file mode 100755 index ad749197d..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/parallel/collate.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence - -import torch -import torch.nn.functional as F -from torch.utils.data.dataloader import default_collate - -from .data_container import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Puts each data field into a tensor/DataContainer with outer dimension - batch size. - - Extend default_collate to add support for - :type:`~mmcv.parallel.DataContainer`. There are 3 cases. - - 1. cpu_only = True, e.g., meta data - 2. cpu_only = False, stack = True, e.g., images tensors - 3. cpu_only = False, stack = False, e.g., gt bboxes - """ - - if not isinstance(batch, Sequence): - raise TypeError(f'{batch.dtype} is not supported.') - - if isinstance(batch[0], DataContainer): - stacked = [] - if batch[0].cpu_only: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer( - stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) - elif batch[0].stack: - for i in range(0, len(batch), samples_per_gpu): - assert isinstance(batch[i].data, torch.Tensor) - - if batch[i].pad_dims is not None: - ndim = batch[i].dim() - assert ndim > batch[i].pad_dims - max_shape = [0 for _ in range(batch[i].pad_dims)] - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = batch[i].size(-dim) - for sample in batch[i:i + samples_per_gpu]: - for dim in range(0, ndim - batch[i].pad_dims): - assert batch[i].size(dim) == sample.size(dim) - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = max(max_shape[dim - 1], - sample.size(-dim)) - padded_samples = [] - for sample in batch[i:i + samples_per_gpu]: - pad = [0 for _ in range(batch[i].pad_dims * 2)] - for dim in range(1, batch[i].pad_dims + 1): - pad[2 * dim - - 1] = max_shape[dim - 1] - sample.size(-dim) - padded_samples.append( - F.pad( - sample.data, pad, value=sample.padding_value)) - stacked.append(default_collate(padded_samples)) - elif batch[i].pad_dims is None: - stacked.append( - default_collate([ - sample.data - for sample in batch[i:i + samples_per_gpu] - ])) - else: - raise ValueError( - 'pad_dims should be either None or integers (1-3)') - - else: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer(stacked, batch[0].stack, batch[0].padding_value) - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - return [collate(samples, samples_per_gpu) for samples in transposed] - elif isinstance(batch[0], Mapping): - return { - key: collate([d[key] for d in batch], samples_per_gpu) - for key in batch[0] - } - else: - return default_collate(batch) diff --git a/cv/detection/autoassign/pytorch/mmcv/parallel/data_container.py b/cv/detection/autoassign/pytorch/mmcv/parallel/data_container.py deleted file mode 100755 index cedb0d32a..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/parallel/data_container.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch - - -def assert_tensor_type(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not isinstance(args[0].data, torch.Tensor): - raise AttributeError( - f'{args[0].__class__.__name__} has no attribute ' - f'{func.__name__} for type {args[0].datatype}') - return func(*args, **kwargs) - - return wrapper - - -class DataContainer: - """A container for any type of objects. - - Typically tensors will be stacked in the collate function and sliced along - some dimension in the scatter function. This behavior has some limitations. - 1. All tensors have to be the same size. - 2. Types are limited (numpy array or Tensor). - - We design `DataContainer` and `MMDataParallel` to overcome these - limitations. The behavior can be either of the following. - - - copy to GPU, pad all tensors to the same size and stack them - - copy to GPU without stacking - - leave the objects as is and pass it to the model - - pad_dims specifies the number of last few dimensions to do padding - """ - - def __init__(self, - data, - stack=False, - padding_value=0, - cpu_only=False, - pad_dims=2): - self._data = data - self._cpu_only = cpu_only - self._stack = stack - self._padding_value = padding_value - assert pad_dims in [None, 1, 2, 3] - self._pad_dims = pad_dims - - def __repr__(self): - return f'{self.__class__.__name__}({repr(self.data)})' - - def __len__(self): - return len(self._data) - - @property - def data(self): - return self._data - - @property - def datatype(self): - if isinstance(self.data, torch.Tensor): - return self.data.type() - else: - return type(self.data) - - @property - def cpu_only(self): - return self._cpu_only - - @property - def stack(self): - return self._stack - - @property - def padding_value(self): - return self._padding_value - - @property - def pad_dims(self): - return self._pad_dims - - @assert_tensor_type - def size(self, *args, **kwargs): - return self.data.size(*args, **kwargs) - - @assert_tensor_type - def dim(self): - return self.data.dim() diff --git a/cv/detection/autoassign/pytorch/mmcv/parallel/data_parallel.py b/cv/detection/autoassign/pytorch/mmcv/parallel/data_parallel.py deleted file mode 100755 index 79b5f69b6..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/parallel/data_parallel.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from itertools import chain - -from torch.nn.parallel import DataParallel - -from .scatter_gather import scatter_kwargs - - -class MMDataParallel(DataParallel): - """The DataParallel module that supports DataContainer. - - MMDataParallel has two main differences with PyTorch DataParallel: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data during both GPU and CPU inference. - - It implement two more APIs ``train_step()`` and ``val_step()``. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - device_ids (list[int]): Device IDS of modules to be scattered to. - Defaults to None when GPU is not available. - output_device (str | int): Device ID for output. Defaults to None. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MMDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.dim = dim - - def forward(self, *inputs, **kwargs): - """Override the original forward function. - - The main difference lies in the CPU inference where the data in - :class:`DataContainers` will still be gathered. - """ - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module(*inputs[0], **kwargs[0]) - else: - return super().forward(*inputs, **kwargs) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.train_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - 'instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.train_step(*inputs[0], **kwargs[0]) - - def val_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.val_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/cv/detection/autoassign/pytorch/mmcv/parallel/distributed.py b/cv/detection/autoassign/pytorch/mmcv/parallel/distributed.py deleted file mode 100755 index b799a213d..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/parallel/distributed.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel.distributed import (DistributedDataParallel, - _find_tensors) - -from mmcv import print_log -from mmcv.utils import TORCH_VERSION, digit_version -from .scatter_gather import scatter_kwargs - - -class MMDistributedDataParallel(DistributedDataParallel): - """The DDP module that supports DataContainer. - - MMDDP has two main differences with PyTorch DDP: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data. - - It implement two APIs ``train_step()`` and ``val_step()``. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - """train_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.train_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if getattr(self, 'require_forward_param_sync', True): - self._sync_params() - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.train_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.train_step(*inputs, **kwargs) - - if torch.is_grad_enabled() and getattr( - self, 'require_backward_grad_sync', True): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output - - def val_step(self, *inputs, **kwargs): - """val_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.val_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if getattr(self, 'require_forward_param_sync', True): - self._sync_params() - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.val_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.val_step(*inputs, **kwargs) - - if torch.is_grad_enabled() and getattr( - self, 'require_backward_grad_sync', True): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output diff --git a/cv/detection/autoassign/pytorch/mmcv/parallel/distributed_deprecated.py b/cv/detection/autoassign/pytorch/mmcv/parallel/distributed_deprecated.py deleted file mode 100755 index b593d4a9e..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/parallel/distributed_deprecated.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn as nn -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter_kwargs - - -@MODULE_WRAPPERS.register_module() -class MMDistributedDataParallel(nn.Module): - - def __init__(self, - module, - dim=0, - broadcast_buffers=True, - bucket_cap_mb=25): - super(MMDistributedDataParallel, self).__init__() - self.module = module - self.dim = dim - self.broadcast_buffers = broadcast_buffers - - self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 - self._sync_params() - - def _dist_broadcast_coalesced(self, tensors, buffer_size): - for tensors in _take_tensors(tensors, buffer_size): - flat_tensors = _flatten_dense_tensors(tensors) - dist.broadcast(flat_tensors, 0) - for tensor, synced in zip( - tensors, _unflatten_dense_tensors(flat_tensors, tensors)): - tensor.copy_(synced) - - def _sync_params(self): - module_states = list(self.module.state_dict().values()) - if len(module_states) > 0: - self._dist_broadcast_coalesced(module_states, - self.broadcast_bucket_size) - if self.broadcast_buffers: - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) < digit_version('1.0')): - buffers = [b.data for b in self.module._all_buffers()] - else: - buffers = [b.data for b in self.module.buffers()] - if len(buffers) > 0: - self._dist_broadcast_coalesced(buffers, - self.broadcast_bucket_size) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/detection/autoassign/pytorch/mmcv/parallel/registry.py b/cv/detection/autoassign/pytorch/mmcv/parallel/registry.py deleted file mode 100755 index 144f9fb16..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/parallel/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel, DistributedDataParallel - -from mmcv.utils import Registry - -MODULE_WRAPPERS = Registry('module wrapper') -MODULE_WRAPPERS.register_module(module=DataParallel) -MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/cv/detection/autoassign/pytorch/mmcv/parallel/scatter_gather.py b/cv/detection/autoassign/pytorch/mmcv/parallel/scatter_gather.py deleted file mode 100755 index 900ff8856..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/parallel/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import Scatter as OrigScatter - -from ._functions import Scatter -from .data_container import DataContainer - - -def scatter(inputs, target_gpus, dim=0): - """Scatter inputs to target gpus. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_gpus != [-1]: - return OrigScatter.apply(target_gpus, None, dim, obj) - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_gpus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_gpus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_gpus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_gpus, dim) if inputs else [] - kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/detection/autoassign/pytorch/mmcv/parallel/utils.py b/cv/detection/autoassign/pytorch/mmcv/parallel/utils.py deleted file mode 100755 index 0f5712cb4..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/parallel/utils.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import MODULE_WRAPPERS - - -def is_module_wrapper(module): - """Check if a module is a module wrapper. - - The following 3 modules in MMCV (and their subclasses) are regarded as - module wrappers: DataParallel, DistributedDataParallel, - MMDistributedDataParallel (the deprecated version). You may add you own - module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: True if the input module is a module wrapper. - """ - module_wrappers = tuple(MODULE_WRAPPERS.module_dict.values()) - return isinstance(module, module_wrappers) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/__init__.py b/cv/detection/autoassign/pytorch/mmcv/runner/__init__.py deleted file mode 100755 index 52e4b48d3..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_module import BaseModule, ModuleList, Sequential -from .base_runner import BaseRunner -from .builder import RUNNERS, build_runner -from .checkpoint import (CheckpointLoader, _load_checkpoint, - _load_checkpoint_with_prefix, load_checkpoint, - load_state_dict, save_checkpoint, weights_to_cpu) -from .default_constructor import DefaultRunnerConstructor -from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, - init_dist, master_only) -from .epoch_based_runner import EpochBasedRunner, Runner -from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model -from .hooks import (HOOKS, CheckpointHook, ClosureHook, DistEvalHook, - DistSamplerSeedHook, DvcliveLoggerHook, EMAHook, EvalHook, - Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, Hook, IterTimerHook, - LoggerHook, LrUpdaterHook, MlflowLoggerHook, - NeptuneLoggerHook, OptimizerHook, PaviLoggerHook, - SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .iter_based_runner import IterBasedRunner, IterLoader -from .log_buffer import LogBuffer -from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, - DefaultOptimizerConstructor, build_optimizer, - build_optimizer_constructor) -from .priority import Priority, get_priority -from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed - -__all__ = [ - 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', - 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', - 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', - 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', - 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', - 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', - 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', - 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', - 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', - 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', - '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', - 'ModuleList', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor' -] diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/base_module.py b/cv/detection/autoassign/pytorch/mmcv/runner/base_module.py deleted file mode 100755 index 529575b81..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/base_module.py +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings -from abc import ABCMeta -from collections import defaultdict -from logging import FileHandler - -import torch.nn as nn - -from mmcv.runner.dist_utils import master_only -from mmcv.utils.logging import get_logger, logger_initialized, print_log - - -class BaseModule(nn.Module, metaclass=ABCMeta): - """Base module for all modules in openmmlab. - - ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional - functionality of parameter initialization. Compared with - ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. - - - ``init_cfg``: the config to control the initialization. - - ``init_weights``: The function of parameter - initialization and recording initialization - information. - - ``_params_init_info``: Used to track the parameter - initialization information. This attribute only - exists during executing the ``init_weights``. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, init_cfg=None): - """Initialize BaseModule, inherited from `torch.nn.Module`""" - - # NOTE init_cfg can be defined in different levels, but init_cfg - # in low levels has a higher priority. - - super(BaseModule, self).__init__() - # define default value of init_cfg instead of hard code - # in init_weights() function - self._is_init = False - - self.init_cfg = copy.deepcopy(init_cfg) - - # Backward compatibility in derived classes - # if pretrained is not None: - # warnings.warn('DeprecationWarning: pretrained is a deprecated \ - # key, please consider using init_cfg') - # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - - @property - def is_init(self): - return self._is_init - - def init_weights(self): - """Initialize the weights.""" - - is_top_level_module = False - # check if it is top-level module - if not hasattr(self, '_params_init_info'): - # The `_params_init_info` is used to record the initialization - # information of the parameters - # the key should be the obj:`nn.Parameter` of model and the value - # should be a dict containing - # - init_info (str): The string that describes the initialization. - # - tmp_mean_value (FloatTensor): The mean of the parameter, - # which indicates whether the parameter has been modified. - # this attribute would be deleted after all parameters - # is initialized. - self._params_init_info = defaultdict(dict) - is_top_level_module = True - - # Initialize the `_params_init_info`, - # When detecting the `tmp_mean_value` of - # the corresponding parameter is changed, update related - # initialization information - for name, param in self.named_parameters(): - self._params_init_info[param][ - 'init_info'] = f'The value is the same before and ' \ - f'after calling `init_weights` ' \ - f'of {self.__class__.__name__} ' - self._params_init_info[param][ - 'tmp_mean_value'] = param.data.mean() - - # pass `params_init_info` to all submodules - # All submodules share the same `params_init_info`, - # so it will be updated when parameters are - # modified at any level of the model. - for sub_module in self.modules(): - sub_module._params_init_info = self._params_init_info - - # Get the initialized logger, if not exist, - # create a logger named `mmcv` - logger_names = list(logger_initialized.keys()) - logger_name = logger_names[0] if logger_names else 'mmcv' - - from ..cnn import initialize - from ..cnn.utils.weight_init import update_init_info - module_name = self.__class__.__name__ - if not self._is_init: - if self.init_cfg: - print_log( - f'initialize {module_name} with init_cfg {self.init_cfg}', - logger=logger_name) - initialize(self, self.init_cfg) - if isinstance(self.init_cfg, dict): - # prevent the parameters of - # the pre-trained model - # from being overwritten by - # the `init_weights` - if self.init_cfg['type'] == 'Pretrained': - return - - for m in self.children(): - if hasattr(m, 'init_weights'): - m.init_weights() - # users may overload the `init_weights` - update_init_info( - m, - init_info=f'Initialized by ' - f'user-defined `init_weights`' - f' in {m.__class__.__name__} ') - - self._is_init = True - else: - warnings.warn(f'init_weights of {self.__class__.__name__} has ' - f'been called more than once.') - - if is_top_level_module: - self._dump_init_info(logger_name) - - for sub_module in self.modules(): - del sub_module._params_init_info - - @master_only - def _dump_init_info(self, logger_name): - """Dump the initialization information to a file named - `initialization.log.json` in workdir. - - Args: - logger_name (str): The name of logger. - """ - - logger = get_logger(logger_name) - - with_file_handler = False - # dump the information to the logger file if there is a `FileHandler` - for handler in logger.handlers: - if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True - if not with_file_handler: - for name, param in self.named_parameters(): - print_log( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n ", - logger=logger_name) - - def __repr__(self): - s = super().__repr__() - if self.init_cfg: - s += f'\ninit_cfg={self.init_cfg}' - return s - - -class Sequential(BaseModule, nn.Sequential): - """Sequential module in openmmlab. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, *args, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.Sequential.__init__(self, *args) - - -class ModuleList(BaseModule, nn.ModuleList): - """ModuleList in openmmlab. - - Args: - modules (iterable, optional): an iterable of modules to add. - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleList.__init__(self, modules) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/base_runner.py b/cv/detection/autoassign/pytorch/mmcv/runner/base_runner.py deleted file mode 100755 index 25cd98f51..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/base_runner.py +++ /dev/null @@ -1,542 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -import os.path as osp -import warnings -from abc import ABCMeta, abstractmethod - -import torch -from torch.optim import Optimizer - -import mmcv -from ..parallel import is_module_wrapper -from .checkpoint import load_checkpoint -from .dist_utils import get_dist_info -from .hooks import HOOKS, Hook -from .log_buffer import LogBuffer -from .priority import Priority, get_priority -from .utils import get_time_str - - -class BaseRunner(metaclass=ABCMeta): - """The base class of Runner, a training helper for PyTorch. - - All subclasses should implement the following APIs: - - - ``run()`` - - ``train()`` - - ``val()`` - - ``save_checkpoint()`` - - Args: - model (:obj:`torch.nn.Module`): The model to be run. - batch_processor (callable): A callable method that process a data - batch. The interface of this method should be - `batch_processor(model, data, train_mode) -> dict` - optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an - optimizer (in most cases) or a dict of optimizers (in models that - requires more than one optimizer, e.g., GAN). - work_dir (str, optional): The working directory to save checkpoints - and logs. Defaults to None. - logger (:obj:`logging.Logger`): Logger used during training. - Defaults to None. (The default value is just for backward - compatibility) - meta (dict | None): A dict records some import information such as - environment info and seed, which will be logged in logger hook. - Defaults to None. - max_epochs (int, optional): Total training epochs. - max_iters (int, optional): Total training iterations. - """ - - def __init__(self, - model, - batch_processor=None, - optimizer=None, - work_dir=None, - logger=None, - meta=None, - max_iters=None, - max_epochs=None): - if batch_processor is not None: - if not callable(batch_processor): - raise TypeError('batch_processor must be callable, ' - f'but got {type(batch_processor)}') - warnings.warn('batch_processor is deprecated, please implement ' - 'train_step() and val_step() in the model instead.') - # raise an error is `batch_processor` is not None and - # `model.train_step()` exists. - if is_module_wrapper(model): - _model = model.module - else: - _model = model - if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): - raise RuntimeError( - 'batch_processor and model.train_step()/model.val_step() ' - 'cannot be both available.') - else: - assert hasattr(model, 'train_step') - - # check the type of `optimizer` - if isinstance(optimizer, dict): - for name, optim in optimizer.items(): - if not isinstance(optim, Optimizer): - raise TypeError( - f'optimizer must be a dict of torch.optim.Optimizers, ' - f'but optimizer["{name}"] is a {type(optim)}') - elif not isinstance(optimizer, Optimizer) and optimizer is not None: - raise TypeError( - f'optimizer must be a torch.optim.Optimizer object ' - f'or dict or None, but got {type(optimizer)}') - - # check the type of `logger` - if not isinstance(logger, logging.Logger): - raise TypeError(f'logger must be a logging.Logger object, ' - f'but got {type(logger)}') - - # check the type of `meta` - if meta is not None and not isinstance(meta, dict): - raise TypeError( - f'meta must be a dict or None, but got {type(meta)}') - - self.model = model - self.batch_processor = batch_processor - self.optimizer = optimizer - self.logger = logger - self.meta = meta - # create work_dir - if mmcv.is_str(work_dir): - self.work_dir = osp.abspath(work_dir) - mmcv.mkdir_or_exist(self.work_dir) - elif work_dir is None: - self.work_dir = None - else: - raise TypeError('"work_dir" must be a str or None') - - # get model name from the model class - if hasattr(self.model, 'module'): - self._model_name = self.model.module.__class__.__name__ - else: - self._model_name = self.model.__class__.__name__ - - self._rank, self._world_size = get_dist_info() - self.timestamp = get_time_str() - self.mode = None - self._hooks = [] - self._epoch = 0 - self._iter = 0 - self._inner_iter = 0 - - if max_epochs is not None and max_iters is not None: - raise ValueError( - 'Only one of `max_epochs` or `max_iters` can be set.') - - self._max_epochs = max_epochs - self._max_iters = max_iters - # TODO: Redesign LogBuffer, it is not flexible and elegant enough - self.log_buffer = LogBuffer() - - @property - def model_name(self): - """str: Name of the model, usually the module class name.""" - return self._model_name - - @property - def rank(self): - """int: Rank of current process. (distributed training)""" - return self._rank - - @property - def world_size(self): - """int: Number of processes participating in the job. - (distributed training)""" - return self._world_size - - @property - def hooks(self): - """list[:obj:`Hook`]: A list of registered hooks.""" - return self._hooks - - @property - def epoch(self): - """int: Current epoch.""" - return self._epoch - - @property - def iter(self): - """int: Current iteration.""" - return self._iter - - @property - def inner_iter(self): - """int: Iteration in an epoch.""" - return self._inner_iter - - @property - def max_epochs(self): - """int: Maximum training epochs.""" - return self._max_epochs - - @property - def max_iters(self): - """int: Maximum training iterations.""" - return self._max_iters - - @abstractmethod - def train(self): - pass - - @abstractmethod - def val(self): - pass - - @abstractmethod - def run(self, data_loaders, workflow, **kwargs): - pass - - @abstractmethod - def save_checkpoint(self, - out_dir, - filename_tmpl, - save_optimizer=True, - meta=None, - create_symlink=True): - pass - - def current_lr(self): - """Get current learning rates. - - Returns: - list[float] | dict[str, list[float]]: Current learning rates of all - param groups. If the runner has a dict of optimizers, this - method will return a dict. - """ - if isinstance(self.optimizer, torch.optim.Optimizer): - lr = [group['lr'] for group in self.optimizer.param_groups] - elif isinstance(self.optimizer, dict): - lr = dict() - for name, optim in self.optimizer.items(): - lr[name] = [group['lr'] for group in optim.param_groups] - else: - raise RuntimeError( - 'lr is not applicable because optimizer does not exist.') - return lr - - def current_momentum(self): - """Get current momentums. - - Returns: - list[float] | dict[str, list[float]]: Current momentums of all - param groups. If the runner has a dict of optimizers, this - method will return a dict. - """ - - def _get_momentum(optimizer): - momentums = [] - for group in optimizer.param_groups: - if 'momentum' in group.keys(): - momentums.append(group['momentum']) - elif 'betas' in group.keys(): - momentums.append(group['betas'][0]) - else: - momentums.append(0) - return momentums - - if self.optimizer is None: - raise RuntimeError( - 'momentum is not applicable because optimizer does not exist.') - elif isinstance(self.optimizer, torch.optim.Optimizer): - momentums = _get_momentum(self.optimizer) - elif isinstance(self.optimizer, dict): - momentums = dict() - for name, optim in self.optimizer.items(): - momentums[name] = _get_momentum(optim) - return momentums - - def register_hook(self, hook, priority='NORMAL'): - """Register a hook into the hook list. - - The hook will be inserted into a priority queue, with the specified - priority (See :class:`Priority` for details of priorities). - For hooks with the same priority, they will be triggered in the same - order as they are registered. - - Args: - hook (:obj:`Hook`): The hook to be registered. - priority (int or str or :obj:`Priority`): Hook priority. - Lower value means higher priority. - """ - assert isinstance(hook, Hook) - if hasattr(hook, 'priority'): - raise ValueError('"priority" is a reserved attribute for hooks') - priority = get_priority(priority) - hook.priority = priority - # insert the hook to a sorted list - inserted = False - for i in range(len(self._hooks) - 1, -1, -1): - if priority >= self._hooks[i].priority: - self._hooks.insert(i + 1, hook) - inserted = True - break - if not inserted: - self._hooks.insert(0, hook) - - def register_hook_from_cfg(self, hook_cfg): - """Register a hook from its cfg. - - Args: - hook_cfg (dict): Hook config. It should have at least keys 'type' - and 'priority' indicating its type and priority. - - Notes: - The specific hook class to register should not use 'type' and - 'priority' arguments during initialization. - """ - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = mmcv.build_from_cfg(hook_cfg, HOOKS) - self.register_hook(hook, priority=priority) - - def call_hook(self, fn_name): - """Call all hooks. - - Args: - fn_name (str): The function name in each hook to be called, such as - "before_train_epoch". - """ - for hook in self._hooks: - getattr(hook, fn_name)(self) - - def get_hook_info(self): - # Get hooks info in each stage - stage_hook_map = {stage: [] for stage in Hook.stages} - for hook in self.hooks: - try: - priority = Priority(hook.priority).name - except ValueError: - priority = hook.priority - classname = hook.__class__.__name__ - hook_info = f'({priority:<12}) {classname:<35}' - for trigger_stage in hook.get_triggered_stages(): - stage_hook_map[trigger_stage].append(hook_info) - - stage_hook_infos = [] - for stage in Hook.stages: - hook_infos = stage_hook_map[stage] - if len(hook_infos) > 0: - info = f'{stage}:\n' - info += '\n'.join(hook_infos) - info += '\n -------------------- ' - stage_hook_infos.append(info) - return '\n'.join(stage_hook_infos) - - def load_checkpoint(self, - filename, - map_location='cpu', - strict=False, - revise_keys=[(r'^module.', '')]): - return load_checkpoint( - self.model, - filename, - map_location, - strict, - self.logger, - revise_keys=revise_keys) - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - if map_location == 'default': - if torch.cuda.is_available(): - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint(checkpoint) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - if self.meta is None: - self.meta = {} - self.meta.setdefault('hook_msgs', {}) - # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages - self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) - - # Re-calculate the number of iterations when resuming - # models with different number of GPUs - if 'config' in checkpoint['meta']: - config = mmcv.Config.fromstring( - checkpoint['meta']['config'], file_format='.py') - previous_gpu_ids = config.get('gpu_ids', None) - if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( - previous_gpu_ids) != self.world_size: - self._iter = int(self._iter * len(previous_gpu_ids) / - self.world_size) - self.logger.info('the iteration number is changed due to ' - 'change of GPU number') - - # resume meta information meta - self.meta = checkpoint['meta'] - - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - elif isinstance(lr_config, dict): - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = mmcv.build_from_cfg(lr_config, HOOKS) - else: - hook = lr_config - self.register_hook(hook, priority='VERY_HIGH') - - def register_momentum_hook(self, momentum_config): - if momentum_config is None: - return - if isinstance(momentum_config, dict): - assert 'policy' in momentum_config - policy_type = momentum_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of momentum updater. - # Since this is not applicable for - # `CosineAnnealingMomentumUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'MomentumUpdaterHook' - momentum_config['type'] = hook_type - hook = mmcv.build_from_cfg(momentum_config, HOOKS) - else: - hook = momentum_config - self.register_hook(hook, priority='HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = mmcv.build_from_cfg(optimizer_config, HOOKS) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def register_checkpoint_hook(self, checkpoint_config): - if checkpoint_config is None: - return - if isinstance(checkpoint_config, dict): - checkpoint_config.setdefault('type', 'CheckpointHook') - hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) - else: - hook = checkpoint_config - self.register_hook(hook, priority='NORMAL') - - def register_logger_hooks(self, log_config): - if log_config is None: - return - log_interval = log_config['interval'] - for info in log_config['hooks']: - logger_hook = mmcv.build_from_cfg( - info, HOOKS, default_args=dict(interval=log_interval)) - self.register_hook(logger_hook, priority='VERY_LOW') - - def register_timer_hook(self, timer_config): - if timer_config is None: - return - if isinstance(timer_config, dict): - timer_config_ = copy.deepcopy(timer_config) - hook = mmcv.build_from_cfg(timer_config_, HOOKS) - else: - hook = timer_config - self.register_hook(hook, priority='LOW') - - def register_custom_hooks(self, custom_config): - if custom_config is None: - return - - if not isinstance(custom_config, list): - custom_config = [custom_config] - - for item in custom_config: - if isinstance(item, dict): - self.register_hook_from_cfg(item) - else: - self.register_hook(item, priority='NORMAL') - - def register_profiler_hook(self, profiler_config): - if profiler_config is None: - return - if isinstance(profiler_config, dict): - profiler_config.setdefault('type', 'ProfilerHook') - hook = mmcv.build_from_cfg(profiler_config, HOOKS) - else: - hook = profiler_config - self.register_hook(hook) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - timer_config=dict(type='IterTimerHook'), - custom_hooks_config=None): - """Register default and custom hooks for training. - - Default and custom hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - self.register_lr_hook(lr_config) - self.register_momentum_hook(momentum_config) - self.register_optimizer_hook(optimizer_config) - self.register_checkpoint_hook(checkpoint_config) - self.register_timer_hook(timer_config) - self.register_logger_hooks(log_config) - self.register_custom_hooks(custom_hooks_config) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/builder.py b/cv/detection/autoassign/pytorch/mmcv/runner/builder.py deleted file mode 100755 index 77c96ba0b..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/builder.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from ..utils import Registry - -RUNNERS = Registry('runner') -RUNNER_BUILDERS = Registry('runner builder') - - -def build_runner_constructor(cfg): - return RUNNER_BUILDERS.build(cfg) - - -def build_runner(cfg, default_args=None): - runner_cfg = copy.deepcopy(cfg) - constructor_type = runner_cfg.pop('constructor', - 'DefaultRunnerConstructor') - runner_constructor = build_runner_constructor( - dict( - type=constructor_type, - runner_cfg=runner_cfg, - default_args=default_args)) - runner = runner_constructor() - return runner diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/checkpoint.py b/cv/detection/autoassign/pytorch/mmcv/runner/checkpoint.py deleted file mode 100755 index 6ad605b85..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/checkpoint.py +++ /dev/null @@ -1,707 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os -import os.path as osp -import pkgutil -import re -import time -import warnings -from collections import OrderedDict -from importlib import import_module -from tempfile import TemporaryDirectory - -import torch -import torchvision -from torch.optim import Optimizer -from torch.utils import model_zoo - -import mmcv -from ..fileio import FileClient -from ..fileio import load as load_file -from ..parallel import is_module_wrapper -from ..utils import mkdir_or_exist -from .dist_utils import get_dist_info - -ENV_MMCV_HOME = 'MMCV_HOME' -ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' -DEFAULT_CACHE_DIR = '~/.cache' - - -def _get_mmcv_home(): - mmcv_home = os.path.expanduser( - os.getenv( - ENV_MMCV_HOME, - os.path.join( - os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) - - mkdir_or_exist(mmcv_home) - return mmcv_home - - -def load_state_dict(module, state_dict, strict=False, logger=None): - """Load state_dict to a module. - - This method is modified from :meth:`torch.nn.Module.load_state_dict`. - Default value for ``strict`` is set to ``False`` and the message for - param mismatch will be shown even if strict is False. - - Args: - module (Module): Module that receives the state_dict. - state_dict (OrderedDict): Weights. - strict (bool): whether to strictly enforce that the keys - in :attr:`state_dict` match the keys returned by this module's - :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. - logger (:obj:`logging.Logger`, optional): Logger to log the error - message. If not specified, print function will be used. - """ - unexpected_keys = [] - all_missing_keys = [] - err_msg = [] - - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata - - # use _load_from_state_dict to enable checkpoint version control - def load(module, prefix=''): - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - local_metadata = {} if metadata is None else metadata.get( - prefix[:-1], {}) - module._load_from_state_dict(state_dict, prefix, local_metadata, True, - all_missing_keys, unexpected_keys, - err_msg) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(module) - load = None # break load->load reference cycle - - # ignore "num_batches_tracked" of BN layers - missing_keys = [ - key for key in all_missing_keys if 'num_batches_tracked' not in key - ] - - if unexpected_keys: - err_msg.append('unexpected key in source ' - f'state_dict: {", ".join(unexpected_keys)}\n') - if missing_keys: - err_msg.append( - f'missing keys in source state_dict: {", ".join(missing_keys)}\n') - - rank, _ = get_dist_info() - if len(err_msg) > 0 and rank == 0: - err_msg.insert( - 0, 'The model and loaded state dict do not match exactly\n') - err_msg = '\n'.join(err_msg) - if strict: - raise RuntimeError(err_msg) - elif logger is not None: - logger.warning(err_msg) - else: - print(err_msg) - - -def get_torchvision_models(): - model_urls = dict() - for _, name, ispkg in pkgutil.walk_packages(torchvision.models.__path__): - if ispkg: - continue - _zoo = import_module(f'torchvision.models.{name}') - if hasattr(_zoo, 'model_urls'): - _urls = getattr(_zoo, 'model_urls') - model_urls.update(_urls) - return model_urls - - -def get_external_models(): - mmcv_home = _get_mmcv_home() - default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') - default_urls = load_file(default_json_path) - assert isinstance(default_urls, dict) - external_json_path = osp.join(mmcv_home, 'open_mmlab.json') - if osp.exists(external_json_path): - external_urls = load_file(external_json_path) - assert isinstance(external_urls, dict) - default_urls.update(external_urls) - - return default_urls - - -def get_mmcls_models(): - mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') - mmcls_urls = load_file(mmcls_json_path) - - return mmcls_urls - - -def get_deprecated_model_names(): - deprecate_json_path = osp.join(mmcv.__path__[0], - 'model_zoo/deprecated.json') - deprecate_urls = load_file(deprecate_json_path) - assert isinstance(deprecate_urls, dict) - - return deprecate_urls - - -def _process_mmcls_checkpoint(checkpoint): - state_dict = checkpoint['state_dict'] - new_state_dict = OrderedDict() - for k, v in state_dict.items(): - if k.startswith('backbone.'): - new_state_dict[k[9:]] = v - new_checkpoint = dict(state_dict=new_state_dict) - - return new_checkpoint - - -class CheckpointLoader: - """A general checkpoint loader to manage all schemes.""" - - _schemes = {} - - @classmethod - def _register_scheme(cls, prefixes, loader, force=False): - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if (prefix not in cls._schemes) or force: - cls._schemes[prefix] = loader - else: - raise KeyError( - f'{prefix} is already registered as a loader backend, ' - 'add "force=True" if you want to override it') - # sort, longer prefixes take priority - cls._schemes = OrderedDict( - sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) - - @classmethod - def register_scheme(cls, prefixes, loader=None, force=False): - """Register a loader to CheckpointLoader. - - This method can be used as a normal class method or a decorator. - - Args: - prefixes (str or list[str] or tuple[str]): - The prefix of the registered loader. - loader (function, optional): The loader function to be registered. - When this method is used as a decorator, loader is None. - Defaults to None. - force (bool, optional): Whether to override the loader - if the prefix has already been registered. Defaults to False. - """ - - if loader is not None: - cls._register_scheme(prefixes, loader, force=force) - return - - def _register(loader_cls): - cls._register_scheme(prefixes, loader_cls, force=force) - return loader_cls - - return _register - - @classmethod - def _get_checkpoint_loader(cls, path): - """Finds a loader that supports the given path. Falls back to the local - loader if no other loader is found. - - Args: - path (str): checkpoint path - - Returns: - loader (function): checkpoint loader - """ - - for p in cls._schemes: - if path.startswith(p): - return cls._schemes[p] - - @classmethod - def load_checkpoint(cls, filename, map_location=None, logger=None): - """load checkpoint through URL scheme path. - - Args: - filename (str): checkpoint file name with given prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - logger (:mod:`logging.Logger`, optional): The logger for message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint_loader = cls._get_checkpoint_loader(filename) - class_name = checkpoint_loader.__name__ - mmcv.print_log( - f'load checkpoint from {class_name[10:]} path: {filename}', logger) - return checkpoint_loader(filename, map_location) - - -@CheckpointLoader.register_scheme(prefixes='') -def load_from_local(filename, map_location): - """load checkpoint by local file path. - - Args: - filename (str): local checkpoint file path - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - if not osp.isfile(filename): - raise IOError(f'{filename} is not a checkpoint file') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) -def load_from_http(filename, map_location=None, model_dir=None): - """load checkpoint through HTTP or HTTPS scheme path. In distributed - setting, this function only download checkpoint at local rank 0. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - model_dir (string, optional): directory in which to save the object, - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - rank, world_size = get_dist_info() - rank = int(os.environ.get('LOCAL_RANK', rank)) - if rank == 0: - checkpoint = model_zoo.load_url( - filename, model_dir=model_dir, map_location=map_location) - if world_size > 1: - torch.distributed.barrier() - if rank > 0: - checkpoint = model_zoo.load_url( - filename, model_dir=model_dir, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='pavi://') -def load_from_pavi(filename, map_location=None): - """load checkpoint through the file path prefixed with pavi. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with pavi prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - assert filename.startswith('pavi://'), \ - f'Expected filename startswith `pavi://`, but get {filename}' - model_path = filename[7:] - - try: - from pavi import modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - - model = modelcloud.get(model_path) - with TemporaryDirectory() as tmp_dir: - downloaded_file = osp.join(tmp_dir, model.name) - model.download(downloaded_file) - checkpoint = torch.load(downloaded_file, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='s3://') -def load_from_ceph(filename, map_location=None, backend='petrel'): - """load checkpoint through the file path prefixed with s3. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with s3 prefix - map_location (str, optional): Same as :func:`torch.load`. - backend (str, optional): The storage backend type. Options are 'ceph', - 'petrel'. Default: 'petrel'. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - allowed_backends = ['ceph', 'petrel'] - if backend not in allowed_backends: - raise ValueError(f'Load from Backend {backend} is not supported.') - - if backend == 'ceph': - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead') - - # CephClient and PetrelBackend have the same prefix 's3://' and the latter - # will be chosen as default. If PetrelBackend can not be instantiated - # successfully, the CephClient will be chosen. - try: - file_client = FileClient(backend=backend) - except ImportError: - allowed_backends.remove(backend) - file_client = FileClient(backend=allowed_backends[0]) - - with io.BytesIO(file_client.get(filename)) as buffer: - checkpoint = torch.load(buffer, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) -def load_from_torchvision(filename, map_location=None): - """load checkpoint through the file path prefixed with modelzoo or - torchvision. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - model_urls = get_torchvision_models() - if filename.startswith('modelzoo://'): - warnings.warn('The URL scheme of "modelzoo://" is deprecated, please ' - 'use "torchvision://" instead') - model_name = filename[11:] - else: - model_name = filename[14:] - return load_from_http(model_urls[model_name], map_location=map_location) - - -@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) -def load_from_openmmlab(filename, map_location=None): - """load checkpoint through the file path prefixed with open-mmlab or - openmmlab. - - Args: - filename (str): checkpoint file path with open-mmlab or - openmmlab prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_external_models() - prefix_str = 'open-mmlab://' - if filename.startswith(prefix_str): - model_name = filename[13:] - else: - model_name = filename[12:] - prefix_str = 'openmmlab://' - - deprecated_urls = get_deprecated_model_names() - if model_name in deprecated_urls: - warnings.warn(f'{prefix_str}{model_name} is deprecated in favor ' - f'of {prefix_str}{deprecated_urls[model_name]}') - model_name = deprecated_urls[model_name] - model_url = model_urls[model_name] - # check if is url - if model_url.startswith(('http://', 'https://')): - checkpoint = load_from_http(model_url, map_location=map_location) - else: - filename = osp.join(_get_mmcv_home(), model_url) - if not osp.isfile(filename): - raise IOError(f'{filename} is not a checkpoint file') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='mmcls://') -def load_from_mmcls(filename, map_location=None): - """load checkpoint through the file path prefixed with mmcls. - - Args: - filename (str): checkpoint file path with mmcls prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_mmcls_models() - model_name = filename[8:] - checkpoint = load_from_http( - model_urls[model_name], map_location=map_location) - checkpoint = _process_mmcls_checkpoint(checkpoint) - return checkpoint - - -def _load_checkpoint(filename, map_location=None, logger=None): - """Load checkpoint from somewhere (modelzoo, file, url). - - Args: - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str, optional): Same as :func:`torch.load`. - Default: None. - logger (:mod:`logging.Logger`, optional): The logger for error message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. It can be either an - OrderedDict storing model weights or a dict containing other - information, which depends on the checkpoint. - """ - return CheckpointLoader.load_checkpoint(filename, map_location, logger) - - -def _load_checkpoint_with_prefix(prefix, filename, map_location=None): - """Load partial pretrained model with specific prefix. - - Args: - prefix (str): The prefix of sub-module. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str | None): Same as :func:`torch.load`. Default: None. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint = _load_checkpoint(filename, map_location=map_location) - - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - if not prefix.endswith('.'): - prefix += '.' - prefix_len = len(prefix) - - state_dict = { - k[prefix_len:]: v - for k, v in state_dict.items() if k.startswith(prefix) - } - - assert state_dict, f'{prefix} is not in the pretrained model' - return state_dict - - -def load_checkpoint(model, - filename, - map_location=None, - strict=False, - logger=None, - revise_keys=[(r'^module\.', '')]): - """Load checkpoint from a file or URI. - - Args: - model (Module): Module to load checkpoint. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str): Same as :func:`torch.load`. - strict (bool): Whether to allow different params for the model and - checkpoint. - logger (:mod:`logging.Logger` or None): The logger for error message. - revise_keys (list): A list of customized keywords to modify the - state_dict in checkpoint. Each item is a (pattern, replacement) - pair of the regular expression operations. Default: strip - the prefix 'module.' by [(r'^module\\.', '')]. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - checkpoint = _load_checkpoint(filename, map_location, logger) - # OrderedDict is a subclass of dict - if not isinstance(checkpoint, dict): - raise RuntimeError( - f'No state_dict found in checkpoint file {filename}') - # get state_dict from checkpoint - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - - # strip prefix of state_dict - metadata = getattr(state_dict, '_metadata', OrderedDict()) - for p, r in revise_keys: - state_dict = OrderedDict( - {re.sub(p, r, k): v - for k, v in state_dict.items()}) - # Keep metadata in state_dict - state_dict._metadata = metadata - - # load state_dict - load_state_dict(model, state_dict, strict, logger) - return checkpoint - - -def weights_to_cpu(state_dict): - """Copy a model state_dict to cpu. - - Args: - state_dict (OrderedDict): Model weights on GPU. - - Returns: - OrderedDict: Model weights on GPU. - """ - state_dict_cpu = OrderedDict() - for key, val in state_dict.items(): - state_dict_cpu[key] = val.cpu() - # Keep metadata in state_dict - state_dict_cpu._metadata = getattr(state_dict, '_metadata', OrderedDict()) - return state_dict_cpu - - -def _save_to_state_dict(module, destination, prefix, keep_vars): - """Saves module state to `destination` dictionary. - - This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. - - Args: - module (nn.Module): The module to generate state_dict. - destination (dict): A dict where state will be stored. - prefix (str): The prefix for parameters and buffers used in this - module. - """ - for name, param in module._parameters.items(): - if param is not None: - destination[prefix + name] = param if keep_vars else param.detach() - for name, buf in module._buffers.items(): - # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d - if buf is not None: - destination[prefix + name] = buf if keep_vars else buf.detach() - - -def get_state_dict(module, destination=None, prefix='', keep_vars=False): - """Returns a dictionary containing a whole state of the module. - - Both parameters and persistent buffers (e.g. running averages) are - included. Keys are corresponding parameter and buffer names. - - This method is modified from :meth:`torch.nn.Module.state_dict` to - recursively check parallel module in case that the model has a complicated - structure, e.g., nn.Module(nn.Module(DDP)). - - Args: - module (nn.Module): The module to generate state_dict. - destination (OrderedDict): Returned dict for the state of the - module. - prefix (str): Prefix of the key. - keep_vars (bool): Whether to keep the variable property of the - parameters. Default: False. - - Returns: - dict: A dictionary containing a whole state of the module. - """ - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - - # below is the same as torch.nn.Module.state_dict() - if destination is None: - destination = OrderedDict() - destination._metadata = OrderedDict() - destination._metadata[prefix[:-1]] = local_metadata = dict( - version=module._version) - _save_to_state_dict(module, destination, prefix, keep_vars) - for name, child in module._modules.items(): - if child is not None: - get_state_dict( - child, destination, prefix + name + '.', keep_vars=keep_vars) - for hook in module._state_dict_hooks.values(): - hook_result = hook(module, destination, prefix, local_metadata) - if hook_result is not None: - destination = hook_result - return destination - - -def save_checkpoint(model, - filename, - optimizer=None, - meta=None, - file_client_args=None): - """Save checkpoint to file. - - The checkpoint will have 3 fields: ``meta``, ``state_dict`` and - ``optimizer``. By default ``meta`` will contain version and time info. - - Args: - model (Module): Module whose params are to be saved. - filename (str): Checkpoint filename. - optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. - meta (dict, optional): Metadata to be saved in checkpoint. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError(f'meta must be a dict or None, but got {type(meta)}') - meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) - - if is_module_wrapper(model): - model = model.module - - if hasattr(model, 'CLASSES') and model.CLASSES is not None: - # save class name to the meta - meta.update(CLASSES=model.CLASSES) - - checkpoint = { - 'meta': meta, - 'state_dict': weights_to_cpu(get_state_dict(model)) - } - # save optimizer state dict in the checkpoint - if isinstance(optimizer, Optimizer): - checkpoint['optimizer'] = optimizer.state_dict() - elif isinstance(optimizer, dict): - checkpoint['optimizer'] = {} - for name, optim in optimizer.items(): - checkpoint['optimizer'][name] = optim.state_dict() - - if filename.startswith('pavi://'): - if file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" if filename starts with' - f'"pavi://", but got {file_client_args}') - try: - from pavi import modelcloud - from pavi import exception - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - model_path = filename[7:] - root = modelcloud.Folder() - model_dir, model_name = osp.split(model_path) - try: - model = modelcloud.get(model_dir) - except exception.NodeNotFoundError: - model = root.create_training_model(model_dir) - with TemporaryDirectory() as tmp_dir: - checkpoint_file = osp.join(tmp_dir, model_name) - with open(checkpoint_file, 'wb') as f: - torch.save(checkpoint, f) - f.flush() - model.create_file(checkpoint_file, name=model_name) - else: - file_client = FileClient.infer_client(file_client_args, filename) - with io.BytesIO() as f: - torch.save(checkpoint, f) - file_client.put(f.getvalue(), filename) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/default_constructor.py b/cv/detection/autoassign/pytorch/mmcv/runner/default_constructor.py deleted file mode 100755 index 0bad847f2..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/default_constructor.py +++ /dev/null @@ -1,44 +0,0 @@ -from .builder import RUNNER_BUILDERS, RUNNERS - - -@RUNNER_BUILDERS.register_module() -class DefaultRunnerConstructor: - """Default constructor for runners. - - Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. - For example, We can inject some new properties and functions for `Runner`. - - Example: - >>> from mmcv.runner import RUNNER_BUILDERS, build_runner - >>> # Define a new RunnerReconstructor - >>> @RUNNER_BUILDERS.register_module() - >>> class MyRunnerConstructor: - ... def __init__(self, runner_cfg, default_args=None): - ... if not isinstance(runner_cfg, dict): - ... raise TypeError('runner_cfg should be a dict', - ... f'but got {type(runner_cfg)}') - ... self.runner_cfg = runner_cfg - ... self.default_args = default_args - ... - ... def __call__(self): - ... runner = RUNNERS.build(self.runner_cfg, - ... default_args=self.default_args) - ... # Add new properties for existing runner - ... runner.my_name = 'my_runner' - ... runner.my_function = lambda self: print(self.my_name) - ... ... - >>> # build your runner - >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, - ... constructor='MyRunnerConstructor') - >>> runner = build_runner(runner_cfg) - """ - - def __init__(self, runner_cfg, default_args=None): - if not isinstance(runner_cfg, dict): - raise TypeError('runner_cfg should be a dict', - f'but got {type(runner_cfg)}') - self.runner_cfg = runner_cfg - self.default_args = default_args - - def __call__(self): - return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/dist_utils.py b/cv/detection/autoassign/pytorch/mmcv/runner/dist_utils.py deleted file mode 100755 index d3a1ef3fd..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/dist_utils.py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import os -import subprocess -from collections import OrderedDict - -import torch -import torch.multiprocessing as mp -from torch import distributed as dist -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - - -def init_dist(launcher, backend='nccl', **kwargs): - if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') - if launcher == 'pytorch': - _init_dist_pytorch(backend, **kwargs) - elif launcher == 'mpi': - _init_dist_mpi(backend, **kwargs) - elif launcher == 'slurm': - _init_dist_slurm(backend, **kwargs) - else: - raise ValueError(f'Invalid launcher type: {launcher}') - - -def _init_dist_pytorch(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['RANK']) - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_mpi(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['OMPI_COMM_WORLD_RANK']) - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_slurm(backend, port=None): - """Initialize slurm distributed training environment. - - If argument ``port`` is not specified, then the master port will be system - environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system - environment variable, then a default port ``29500`` will be used. - - Args: - backend (str): Backend of torch.distributed. - port (int, optional): Master port. Defaults to None. - """ - proc_id = int(os.environ['SLURM_PROCID']) - ntasks = int(os.environ['SLURM_NTASKS']) - node_list = os.environ['SLURM_NODELIST'] - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(proc_id % num_gpus) - addr = subprocess.getoutput( - f'scontrol show hostname {node_list} | head -n1') - # specify master port - if port is not None: - os.environ['MASTER_PORT'] = str(port) - elif 'MASTER_PORT' in os.environ: - pass # use MASTER_PORT in the environment variable - else: - # 29500 is torch.distributed default port - os.environ['MASTER_PORT'] = '29500' - # use MASTER_ADDR in the environment variable if it already exists - if 'MASTER_ADDR' not in os.environ: - os.environ['MASTER_ADDR'] = addr - os.environ['WORLD_SIZE'] = str(ntasks) - os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) - os.environ['RANK'] = str(proc_id) - dist.init_process_group(backend=backend) - - -def get_dist_info(): - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - world_size = dist.get_world_size() - else: - rank = 0 - world_size = 1 - return rank, world_size - - -def master_only(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - rank, _ = get_dist_info() - if rank == 0: - return func(*args, **kwargs) - - return wrapper - - -def allreduce_params(params, coalesce=True, bucket_size_mb=-1): - """Allreduce parameters. - - Args: - params (list[torch.Parameters]): List of parameters or buffers of a - model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - _, world_size = get_dist_info() - if world_size == 1: - return - params = [param.data for param in params] - if coalesce: - _allreduce_coalesced(params, world_size, bucket_size_mb) - else: - for tensor in params: - dist.all_reduce(tensor.div_(world_size)) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - _, world_size = get_dist_info() - if world_size == 1: - return - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/epoch_based_runner.py b/cv/detection/autoassign/pytorch/mmcv/runner/epoch_based_runner.py deleted file mode 100755 index 2dd29357a..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/epoch_based_runner.py +++ /dev/null @@ -1,187 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .utils import get_host_info - - -@RUNNERS.register_module() -class EpochBasedRunner(BaseRunner): - """Epoch-based Runner. - - This runner train models epoch by epoch. - """ - - def run_iter(self, data_batch, train_mode, **kwargs): - if self.batch_processor is not None: - outputs = self.batch_processor( - self.model, data_batch, train_mode=train_mode, **kwargs) - elif train_mode: - outputs = self.model.train_step(data_batch, self.optimizer, - **kwargs) - else: - outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('"batch_processor()" or "model.train_step()"' - 'and "model.val_step()" must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - self._iter += 1 - - self.call_hook('after_train_epoch') - self._epoch += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - self.call_hook('before_val_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_val_iter') - self.run_iter(data_batch, train_mode=False) - self.call_hook('after_val_iter') - - self.call_hook('after_val_epoch') - - def run(self, data_loaders, workflow, max_epochs=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, epochs) to specify the - running order and epochs. E.g, [('train', 2), ('val', 1)] means - running 2 epochs for training and 1 epoch for validation, - iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_epochs is not None: - warnings.warn( - 'setting max_epochs in run is deprecated, ' - 'please set max_epochs in runner_config', DeprecationWarning) - self._max_epochs = max_epochs - - assert self._max_epochs is not None, ( - 'max_epochs must be specified during instantiation') - - for i, flow in enumerate(workflow): - mode, epochs = flow - if mode == 'train': - self._max_iters = self._max_epochs * len(data_loaders[i]) - break - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d epochs', workflow, - self._max_epochs) - self.call_hook('before_run') - - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - mode, epochs = flow - if isinstance(mode, str): # self.train() - if not hasattr(self, mode): - raise ValueError( - f'runner has no method named "{mode}" to run an ' - 'epoch') - epoch_runner = getattr(self, mode) - else: - raise TypeError( - 'mode in workflow must be a str, but got {}'.format( - type(mode))) - - for _ in range(epochs): - if mode == 'train' and self.epoch >= self._max_epochs: - break - epoch_runner(data_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_run') - - def save_checkpoint(self, - out_dir, - filename_tmpl='epoch_{}.pth', - save_optimizer=True, - meta=None, - create_symlink=True): - """Save the checkpoint. - - Args: - out_dir (str): The directory that checkpoints are saved. - filename_tmpl (str, optional): The checkpoint filename template, - which contains a placeholder for the epoch number. - Defaults to 'epoch_{}.pth'. - save_optimizer (bool, optional): Whether to save the optimizer to - the checkpoint. Defaults to True. - meta (dict, optional): The meta information to be saved in the - checkpoint. Defaults to None. - create_symlink (bool, optional): Whether to create a symlink - "latest.pth" to point to the latest checkpoint. - Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.epoch + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - -@RUNNERS.register_module() -class Runner(EpochBasedRunner): - """Deprecated name of EpochBasedRunner.""" - - def __init__(self, *args, **kwargs): - warnings.warn( - 'Runner was deprecated, please use EpochBasedRunner instead') - super().__init__(*args, **kwargs) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/fp16_utils.py b/cv/detection/autoassign/pytorch/mmcv/runner/fp16_utils.py deleted file mode 100755 index 4baab939a..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/fp16_utils.py +++ /dev/null @@ -1,410 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import warnings -from collections import abc -from inspect import getfullargspec - -import numpy as np -import torch -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .dist_utils import allreduce_grads as _allreduce_grads - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 - # manually, so the behavior may not be consistent with real amp. - from torch.cuda.amp import autocast -except ImportError: - pass - - -def cast_tensor_type(inputs, src_type, dst_type): - """Recursively convert Tensor in inputs from src_type to dst_type. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype): Source type.. - dst_type (torch.dtype): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - if isinstance(inputs, nn.Module): - return inputs - elif isinstance(inputs, torch.Tensor): - return inputs.to(dst_type) - elif isinstance(inputs, str): - return inputs - elif isinstance(inputs, np.ndarray): - return inputs - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type, dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type, dst_type) for item in inputs) - else: - return inputs - - -def auto_fp16(apply_to=None, out_fp32=False): - """Decorator to enable fp16 training automatically. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If inputs arguments are fp32 tensors, they will - be converted to fp16 automatically. Arguments other than fp32 tensors are - ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp32 (bool): Whether to convert the output back to fp32. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp16 - >>> @auto_fp16() - >>> def forward(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp16 - >>> @auto_fp16(apply_to=('pred', )) - >>> def do_something(self, pred, others): - >>> pass - """ - - def auto_fp16_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@auto_fp16 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - # NOTE: default args are not taken into consideration - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.float, torch.half)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = {} - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.float, torch.half) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=True): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp32: - output = cast_tensor_type(output, torch.half, torch.float) - return output - - return new_func - - return auto_fp16_wrapper - - -def force_fp32(apply_to=None, out_fp16=False): - """Decorator to convert input arguments to fp32 in force. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If there are some inputs that must be processed - in fp32 mode, then this decorator can handle it. If inputs arguments are - fp16 tensors, they will be converted to fp32 automatically. Arguments other - than fp16 tensors are ignored. If you are using PyTorch >= 1.6, - torch.cuda.amp is used as the backend, otherwise, original mmcv - implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp16 (bool): Whether to convert the output back to fp16. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp32 - >>> @force_fp32() - >>> def loss(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp32 - >>> @force_fp32(apply_to=('pred', )) - >>> def post_process(self, pred, others): - >>> pass - """ - - def force_fp32_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@force_fp32 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.half, torch.float)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = dict() - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.half, torch.float) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=False): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp16: - output = cast_tensor_type(output, torch.float, torch.half) - return output - - return new_func - - return force_fp32_wrapper - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - warnings.warning( - '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' - 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads') - _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) - - -def wrap_fp16_model(model): - """Wrap the FP32 model to FP16. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - For PyTorch >= 1.6, this function will - 1. Set fp16 flag inside the model to True. - - Otherwise: - 1. Convert FP32 model to FP16. - 2. Remain some necessary layers to be FP32, e.g., normalization layers. - 3. Set `fp16_enabled` flag inside the model to True. - - Args: - model (nn.Module): Model in FP32. - """ - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.6.0')): - # convert model to fp16 - model.half() - # patch the normalization layers to make it work in fp32 mode - patch_norm_fp32(model) - # set `fp16_enabled` flag - for m in model.modules(): - if hasattr(m, 'fp16_enabled'): - m.fp16_enabled = True - - -def patch_norm_fp32(module): - """Recursively convert normalization layers from FP16 to FP32. - - Args: - module (nn.Module): The modules to be converted in FP16. - - Returns: - nn.Module: The converted module, the normalization layers have been - converted to FP32. - """ - if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): - module.float() - if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': - module.forward = patch_forward_method(module.forward, torch.half, - torch.float) - for child in module.children(): - patch_norm_fp32(child) - return module - - -def patch_forward_method(func, src_type, dst_type, convert_output=True): - """Patch the forward method of a module. - - Args: - func (callable): The original forward method. - src_type (torch.dtype): Type of input arguments to be converted from. - dst_type (torch.dtype): Type of input arguments to be converted to. - convert_output (bool): Whether to convert the output back to src_type. - - Returns: - callable: The patched forward method. - """ - - def new_forward(*args, **kwargs): - output = func(*cast_tensor_type(args, src_type, dst_type), - **cast_tensor_type(kwargs, src_type, dst_type)) - if convert_output: - output = cast_tensor_type(output, dst_type, src_type) - return output - - return new_forward - - -class LossScaler: - """Class that manages loss scaling in mixed precision training which - supports both dynamic or static mode. - - The implementation refers to - https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. - Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. - It's important to understand how :class:`LossScaler` operates. - Loss scaling is designed to combat the problem of underflowing - gradients encountered at long times when training fp16 networks. - Dynamic loss scaling begins by attempting a very high loss - scale. Ironically, this may result in OVERflowing gradients. - If overflowing gradients are encountered, :class:`FP16_Optimizer` then - skips the update step for this particular iteration/minibatch, - and :class:`LossScaler` adjusts the loss scale to a lower value. - If a certain number of iterations occur without overflowing gradients - detected,:class:`LossScaler` increases the loss scale once more. - In this way :class:`LossScaler` attempts to "ride the edge" of always - using the highest loss scale possible without incurring overflow. - - Args: - init_scale (float): Initial loss scale value, default: 2**32. - scale_factor (float): Factor used when adjusting the loss scale. - Default: 2. - mode (str): Loss scaling mode. 'dynamic' or 'static' - scale_window (int): Number of consecutive iterations without an - overflow to wait before increasing the loss scale. Default: 1000. - """ - - def __init__(self, - init_scale=2**32, - mode='dynamic', - scale_factor=2., - scale_window=1000): - self.cur_scale = init_scale - self.cur_iter = 0 - assert mode in ('dynamic', - 'static'), 'mode can only be dynamic or static' - self.mode = mode - self.last_overflow_iter = -1 - self.scale_factor = scale_factor - self.scale_window = scale_window - - def has_overflow(self, params): - """Check if params contain overflow.""" - if self.mode != 'dynamic': - return False - for p in params: - if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): - return True - return False - - def _has_inf_or_nan(x): - """Check if params contain NaN.""" - try: - cpu_sum = float(x.float().sum()) - except RuntimeError as instance: - if 'value cannot be converted' not in instance.args[0]: - raise - return True - else: - if cpu_sum == float('inf') or cpu_sum == -float('inf') \ - or cpu_sum != cpu_sum: - return True - return False - - def update_scale(self, overflow): - """update the current loss scale value when overflow happens.""" - if self.mode != 'dynamic': - return - if overflow: - self.cur_scale = max(self.cur_scale / self.scale_factor, 1) - self.last_overflow_iter = self.cur_iter - else: - if (self.cur_iter - self.last_overflow_iter) % \ - self.scale_window == 0: - self.cur_scale *= self.scale_factor - self.cur_iter += 1 - - def state_dict(self): - """Returns the state of the scaler as a :class:`dict`.""" - return dict( - cur_scale=self.cur_scale, - cur_iter=self.cur_iter, - mode=self.mode, - last_overflow_iter=self.last_overflow_iter, - scale_factor=self.scale_factor, - scale_window=self.scale_window) - - def load_state_dict(self, state_dict): - """Loads the loss_scaler state dict. - - Args: - state_dict (dict): scaler state. - """ - self.cur_scale = state_dict['cur_scale'] - self.cur_iter = state_dict['cur_iter'] - self.mode = state_dict['mode'] - self.last_overflow_iter = state_dict['last_overflow_iter'] - self.scale_factor = state_dict['scale_factor'] - self.scale_window = state_dict['scale_window'] - - @property - def loss_scale(self): - return self.cur_scale diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/__init__.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/__init__.py deleted file mode 100755 index 915af28ce..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkpoint import CheckpointHook -from .closure import ClosureHook -from .ema import EMAHook -from .evaluation import DistEvalHook, EvalHook -from .hook import HOOKS, Hook -from .iter_timer import IterTimerHook -from .logger import (DvcliveLoggerHook, LoggerHook, MlflowLoggerHook, - NeptuneLoggerHook, PaviLoggerHook, TensorboardLoggerHook, - TextLoggerHook, WandbLoggerHook) -from .lr_updater import LrUpdaterHook -from .memory import EmptyCacheHook -from .momentum_updater import MomentumUpdaterHook -from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, OptimizerHook) -from .profiler import ProfilerHook -from .sampler_seed import DistSamplerSeedHook -from .sync_buffer import SyncBuffersHook - -__all__ = [ - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'OptimizerHook', 'Fp16OptimizerHook', 'IterTimerHook', - 'DistSamplerSeedHook', 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'DvcliveLoggerHook', - 'MomentumUpdaterHook', 'SyncBuffersHook', 'EMAHook', 'EvalHook', - 'DistEvalHook', 'ProfilerHook', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook' -] diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/checkpoint.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/checkpoint.py deleted file mode 100755 index 7bb75f402..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/checkpoint.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -from mmcv.fileio import FileClient -from ..dist_utils import allreduce_params, master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class CheckpointHook(Hook): - """Save checkpoints periodically. - - Args: - interval (int): The saving period. If ``by_epoch=True``, interval - indicates epochs, otherwise it indicates iterations. - Default: -1, which means "never". - by_epoch (bool): Saving checkpoints by epoch or by iteration. - Default: True. - save_optimizer (bool): Whether to save optimizer state_dict in the - checkpoint. It is usually used for resuming experiments. - Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, ``runner.work_dir`` will be used by default. If - specified, the ``out_dir`` will be the concatenation of ``out_dir`` - and the last level directory of ``runner.work_dir``. - `Changed in version 1.3.16.` - max_keep_ckpts (int, optional): The maximum checkpoints to keep. - In some cases we want only the latest few checkpoints and would - like to delete old ones to save the disk space. - Default: -1, which means unlimited. - save_last (bool, optional): Whether to force the last checkpoint to be - saved regardless of interval. Default: True. - sync_buffer (bool, optional): Whether to synchronize buffers in - different gpus. Default: False. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - - .. warning:: - Before v1.3.16, the ``out_dir`` argument indicates the path where the - checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the - root directory and the final path to save checkpoint is the - concatenation of ``out_dir`` and the last level directory of - ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" - and the value of ``runner.work_dir`` is "/path/of/B", then the final - path will be "/path/of/A/B". - """ - - def __init__(self, - interval=-1, - by_epoch=True, - save_optimizer=True, - out_dir=None, - max_keep_ckpts=-1, - save_last=True, - sync_buffer=False, - file_client_args=None, - **kwargs): - self.interval = interval - self.by_epoch = by_epoch - self.save_optimizer = save_optimizer - self.out_dir = out_dir - self.max_keep_ckpts = max_keep_ckpts - self.save_last = save_last - self.args = kwargs - self.sync_buffer = sync_buffer - self.file_client_args = file_client_args - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - - runner.logger.info((f'Checkpoints will be saved to {self.out_dir} by ' - f'{self.file_client.name}.')) - - # disable the create_symlink option because some file backends do not - # allow to create a symlink - if 'create_symlink' in self.args: - if self.args[ - 'create_symlink'] and not self.file_client.allow_symlink: - self.args['create_symlink'] = False - warnings.warn( - ('create_symlink is set as True by the user but is changed' - 'to be False because creating symbolic link is not ' - f'allowed in {self.file_client.name}')) - else: - self.args['create_symlink'] = self.file_client.allow_symlink - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` epochs - # 2. reach the last epoch of training - if self.every_n_epochs( - runner, self.interval) or (self.save_last - and self.is_last_epoch(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.epoch + 1} epochs') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) - - @master_only - def _save_checkpoint(self, runner): - """Save the current checkpoint and delete unwanted checkpoint.""" - runner.save_checkpoint( - self.out_dir, save_optimizer=self.save_optimizer, **self.args) - if runner.meta is not None: - if self.by_epoch: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) - else: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) - runner.meta.setdefault('hook_msgs', dict()) - runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( - self.out_dir, cur_ckpt_filename) - # remove other checkpoints - if self.max_keep_ckpts > 0: - if self.by_epoch: - name = 'epoch_{}.pth' - current_ckpt = runner.epoch + 1 - else: - name = 'iter_{}.pth' - current_ckpt = runner.iter + 1 - redundant_ckpts = range( - current_ckpt - self.max_keep_ckpts * self.interval, 0, - -self.interval) - filename_tmpl = self.args.get('filename_tmpl', name) - for _step in redundant_ckpts: - ckpt_path = self.file_client.join_path( - self.out_dir, filename_tmpl.format(_step)) - if self.file_client.isfile(ckpt_path): - self.file_client.remove(ckpt_path) - else: - break - - def after_train_iter(self, runner): - if self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` iterations - # 2. reach the last iteration of training - if self.every_n_iters( - runner, self.interval) or (self.save_last - and self.is_last_iter(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.iter + 1} iterations') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/closure.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/closure.py deleted file mode 100755 index b955f81f4..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/closure.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ClosureHook(Hook): - - def __init__(self, fn_name, fn): - assert hasattr(self, fn_name) - assert callable(fn) - setattr(self, fn_name, fn) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/ema.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/ema.py deleted file mode 100755 index 15c7e6808..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/ema.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...parallel import is_module_wrapper -from ..hooks.hook import HOOKS, Hook - - -@HOOKS.register_module() -class EMAHook(Hook): - r"""Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. - - .. math:: - - \text{Xema\_{t+1}} = (1 - \text{momentum}) \times - \text{Xema\_{t}} + \text{momentum} \times X_t - - Args: - momentum (float): The momentum used for updating ema parameter. - Defaults to 0.0002. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - warm_up (int): During first warm_up steps, we may use smaller momentum - to update ema parameters more slowly. Defaults to 100. - resume_from (str): The checkpoint path. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - warm_up=100, - resume_from=None): - assert isinstance(interval, int) and interval > 0 - self.warm_up = warm_up - self.interval = interval - assert momentum > 0 and momentum < 1 - self.momentum = momentum**interval - self.checkpoint = resume_from - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - self.model_parameters = dict(model.named_parameters(recurse=True)) - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers(recurse=True)) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - curr_step = runner.iter - # We warm up the momentum considering the instability at beginning - momentum = min(self.momentum, - (1 + curr_step) / (self.warm_up + curr_step)) - if curr_step % self.interval != 0: - return - for name, parameter in self.model_parameters.items(): - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/evaluation.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/evaluation.py deleted file mode 100755 index 1eeb44650..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/evaluation.py +++ /dev/null @@ -1,509 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from math import inf - -import torch.distributed as dist -from torch.nn.modules.batchnorm import _BatchNorm -from torch.utils.data import DataLoader - -from mmcv.fileio import FileClient -from mmcv.utils import is_seq_of -from .hook import Hook -from .logger import LoggerHook - - -class EvalHook(Hook): - """Non-Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader, and return the test results. If ``None``, the default - test function ``mmcv.engine.single_gpu_test`` will be used. - (default: ``None``) - greater_keys (List[str] | None, optional): Metric keys that will be - inferred by 'greater' comparison rule. If ``None``, - _default_greater_keys will be used. (default: ``None``) - less_keys (List[str] | None, optional): Metric keys that will be - inferred by 'less' comparison rule. If ``None``, _default_less_keys - will be used. (default: ``None``) - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - `New in version 1.3.16.` - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - `New in version 1.3.16.` - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - - Notes: - If new arguments are added for EvalHook, tools/test.py, - tools/eval_metric.py may be affected. - """ - - # Since the key for determine greater or less is related to the downstream - # tasks, downstream repos may need to overwrite the following inner - # variable accordingly. - - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - _default_greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - _default_less_keys = ['loss'] - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - out_dir=None, - file_client_args=None, - **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError(f'dataloader must be a pytorch DataLoader, ' - f'but got {type(dataloader)}') - - if interval <= 0: - raise ValueError(f'interval must be a positive number, ' - f'but got {interval}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' - - if start is not None and start < 0: - raise ValueError(f'The evaluation start epoch {start} is smaller ' - f'than 0') - - self.dataloader = dataloader - self.interval = interval - self.start = start - self.by_epoch = by_epoch - - assert isinstance(save_best, str) or save_best is None, \ - '""save_best"" should be a str or None ' \ - f'rather than {type(save_best)}' - self.save_best = save_best - self.eval_kwargs = eval_kwargs - self.initial_flag = True - - if test_fn is None: - from mmcv.engine import single_gpu_test - self.test_fn = single_gpu_test - else: - self.test_fn = test_fn - - if greater_keys is None: - self.greater_keys = self._default_greater_keys - else: - if not isinstance(greater_keys, (list, tuple)): - greater_keys = (greater_keys, ) - assert is_seq_of(greater_keys, str) - self.greater_keys = greater_keys - - if less_keys is None: - self.less_keys = self._default_less_keys - else: - if not isinstance(less_keys, (list, tuple)): - less_keys = (less_keys, ) - assert is_seq_of(less_keys, str) - self.less_keys = less_keys - - if self.save_best is not None: - self.best_ckpt_path = None - self._init_rule(rule, self.save_best) - - self.out_dir = out_dir - self.file_client_args = file_client_args - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific (note that the key indicator matching - is case-insensitive): - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator != 'auto': - # `_lc` here means we use the lower case of keys for - # case-insensitive matching - key_indicator_lc = key_indicator.lower() - greater_keys = [key.lower() for key in self.greater_keys] - less_keys = [key.lower() for key in self.less_keys] - - if key_indicator_lc in greater_keys: - rule = 'greater' - elif key_indicator_lc in less_keys: - rule = 'less' - elif any(key in key_indicator_lc for key in greater_keys): - rule = 'greater' - elif any(key in key_indicator_lc for key in less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - if self.rule is not None: - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'The best checkpoint will be saved to {self.out_dir} by ' - f'{self.file_client.name}')) - - if self.save_best is not None: - if runner.meta is None: - warnings.warn('runner.meta is None. Creating an empty one.') - runner.meta = dict() - runner.meta.setdefault('hook_msgs', dict()) - self.best_ckpt_path = runner.meta['hook_msgs'].get( - 'best_ckpt', None) - - def before_train_iter(self, runner): - """Evaluate the model only at the start of training by iteration.""" - if self.by_epoch or not self.initial_flag: - return - if self.start is not None and runner.iter >= self.start: - self.after_train_iter(runner) - self.initial_flag = False - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - if not (self.by_epoch and self.initial_flag): - return - if self.start is not None and runner.epoch >= self.start: - self.after_train_epoch(runner) - self.initial_flag = False - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch and self._should_evaluate(runner): - # Because the priority of EvalHook is higher than LoggerHook, the - # training log and the evaluating log are mixed. Therefore, - # we need to dump the training log and clear it before evaluating - # log is generated. In addition, this problem will only appear in - # `IterBasedRunner` whose `self.by_epoch` is False, because - # `EpochBasedRunner` whose `self.by_epoch` is True calls - # `_do_evaluate` in `after_train_epoch` stage, and at this stage - # the training log has been printed, so it will not cause any - # problem. more details at - # https://github.com/open-mmlab/mmsegmentation/issues/694 - for hook in runner._hooks: - if isinstance(hook, LoggerHook): - hook.after_train_iter(runner) - runner.log_buffer.clear() - - self._do_evaluate(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch and self._should_evaluate(runner): - self._do_evaluate(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - results = self.test_fn(runner.model, self.dataloader) - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - def _should_evaluate(self, runner): - """Judge whether to perform evaluation. - - Here is the rule to judge whether to perform evaluation: - 1. It will not perform evaluation during the epoch/iteration interval, - which is determined by ``self.interval``. - 2. It will not perform evaluation if the start time is larger than - current time. - 3. It will not perform evaluation when current time is larger than - the start time but during epoch/iteration interval. - - Returns: - bool: The flag indicating whether to perform evaluation. - """ - if self.by_epoch: - current = runner.epoch - check_time = self.every_n_epochs - else: - current = runner.iter - check_time = self.every_n_iters - - if self.start is None: - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - elif (current + 1) < self.start: - # No evaluation if start is larger than the current time. - return False - else: - # Evaluation only at epochs/iters 3, 5, 7... - # if start==3 and interval==2 - if (current + 1 - self.start) % self.interval: - return False - return True - - def _save_ckpt(self, runner, key_score): - """Save the best checkpoint. - - It will compare the score according to the compare function, write - related information (best score, best checkpoint path) and save the - best checkpoint into ``work_dir``. - """ - if self.by_epoch: - current = f'epoch_{runner.epoch + 1}' - cur_type, cur_time = 'epoch', runner.epoch + 1 - else: - current = f'iter_{runner.iter + 1}' - cur_type, cur_time = 'iter', runner.iter + 1 - - best_score = runner.meta['hook_msgs'].get( - 'best_score', self.init_value_map[self.rule]) - if self.compare_func(key_score, best_score): - best_score = key_score - runner.meta['hook_msgs']['best_score'] = best_score - - if self.best_ckpt_path and self.file_client.isfile( - self.best_ckpt_path): - self.file_client.remove(self.best_ckpt_path) - runner.logger.info( - (f'The previous best checkpoint {self.best_ckpt_path} was ' - 'removed')) - - best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' - self.best_ckpt_path = self.file_client.join_path( - self.out_dir, best_ckpt_name) - runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path - - runner.save_checkpoint( - self.out_dir, best_ckpt_name, create_symlink=False) - runner.logger.info( - f'Now best checkpoint is saved as {best_ckpt_name}.') - runner.logger.info( - f'Best {self.key_indicator} is {best_score:0.4f} ' - f'at {cur_time} {cur_type}.') - - def evaluate(self, runner, results): - """Evaluate the results. - - Args: - runner (:obj:`mmcv.Runner`): The underlined training runner. - results (list): Output results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - - if self.save_best is not None: - # If the performance of model is pool, the `eval_res` may be an - # empty dict and it will raise exception when `self.save_best` is - # not None. More details at - # https://github.com/open-mmlab/mmdetection/issues/6265. - if not eval_res: - warnings.warn( - 'Since `eval_res` is an empty dict, the behavior to save ' - 'the best checkpoint will be skipped in this evaluation.') - return None - - if self.key_indicator == 'auto': - # infer from eval_results - self._init_rule(self.rule, list(eval_res.keys())[0]) - return eval_res[self.key_indicator] - - return None - - -class DistEvalHook(EvalHook): - """Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader in a multi-gpu manner, and return the test results. If - ``None``, the default test function ``mmcv.engine.multi_gpu_test`` - will be used. (default: ``None``) - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - broadcast_bn_buffer (bool): Whether to broadcast the - buffer(running_mean and running_var) of rank 0 to other rank - before evaluation. Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - """ - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - broadcast_bn_buffer=True, - tmpdir=None, - gpu_collect=False, - out_dir=None, - file_client_args=None, - **eval_kwargs): - - if test_fn is None: - from mmcv.engine import multi_gpu_test - test_fn = multi_gpu_test - - super().__init__( - dataloader, - start=start, - interval=interval, - by_epoch=by_epoch, - save_best=save_best, - rule=rule, - test_fn=test_fn, - greater_keys=greater_keys, - less_keys=less_keys, - out_dir=out_dir, - file_client_args=file_client_args, - **eval_kwargs) - - self.broadcast_bn_buffer = broadcast_bn_buffer - self.tmpdir = tmpdir - self.gpu_collect = gpu_collect - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - results = self.test_fn( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to - # save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/hook.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/hook.py deleted file mode 100755 index f2d1c9865..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/hook.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, is_method_overridden - -HOOKS = Registry('hook') - - -class Hook: - stages = ('before_run', 'before_train_epoch', 'before_train_iter', - 'after_train_iter', 'after_train_epoch', 'before_val_epoch', - 'before_val_iter', 'after_val_iter', 'after_val_epoch', - 'after_run') - - def before_run(self, runner): - pass - - def after_run(self, runner): - pass - - def before_epoch(self, runner): - pass - - def after_epoch(self, runner): - pass - - def before_iter(self, runner): - pass - - def after_iter(self, runner): - pass - - def before_train_epoch(self, runner): - self.before_epoch(runner) - - def before_val_epoch(self, runner): - self.before_epoch(runner) - - def after_train_epoch(self, runner): - self.after_epoch(runner) - - def after_val_epoch(self, runner): - self.after_epoch(runner) - - def before_train_iter(self, runner): - self.before_iter(runner) - - def before_val_iter(self, runner): - self.before_iter(runner) - - def after_train_iter(self, runner): - self.after_iter(runner) - - def after_val_iter(self, runner): - self.after_iter(runner) - - def every_n_epochs(self, runner, n): - return (runner.epoch + 1) % n == 0 if n > 0 else False - - def every_n_inner_iters(self, runner, n): - return (runner.inner_iter + 1) % n == 0 if n > 0 else False - - def every_n_iters(self, runner, n): - return (runner.iter + 1) % n == 0 if n > 0 else False - - def end_of_epoch(self, runner): - return runner.inner_iter + 1 == len(runner.data_loader) - - def is_last_epoch(self, runner): - return runner.epoch + 1 == runner._max_epochs - - def is_last_iter(self, runner): - return runner.iter + 1 == runner._max_iters - - def get_triggered_stages(self): - trigger_stages = set() - for stage in Hook.stages: - if is_method_overridden(stage, Hook, self): - trigger_stages.add(stage) - - # some methods will be triggered in multi stages - # use this dict to map method to stages. - method_stages_map = { - 'before_epoch': ['before_train_epoch', 'before_val_epoch'], - 'after_epoch': ['after_train_epoch', 'after_val_epoch'], - 'before_iter': ['before_train_iter', 'before_val_iter'], - 'after_iter': ['after_train_iter', 'after_val_iter'], - } - - for method, map_stages in method_stages_map.items(): - if is_method_overridden(method, Hook, self): - trigger_stages.update(map_stages) - - return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/iter_timer.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/iter_timer.py deleted file mode 100755 index cfd5002fe..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/iter_timer.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import time - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class IterTimerHook(Hook): - - def before_epoch(self, runner): - self.t = time.time() - - def before_iter(self, runner): - runner.log_buffer.update({'data_time': time.time() - self.t}) - - def after_iter(self, runner): - runner.log_buffer.update({'time': time.time() - self.t}) - self.t = time.time() diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/__init__.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/__init__.py deleted file mode 100755 index a0b6b3456..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import LoggerHook -from .dvclive import DvcliveLoggerHook -from .mlflow import MlflowLoggerHook -from .neptune import NeptuneLoggerHook -from .pavi import PaviLoggerHook -from .tensorboard import TensorboardLoggerHook -from .text import TextLoggerHook -from .wandb import WandbLoggerHook - -__all__ = [ - 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', - 'NeptuneLoggerHook', 'DvcliveLoggerHook' -] diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/base.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/base.py deleted file mode 100755 index f84525672..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/base.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from abc import ABCMeta, abstractmethod - -import numpy as np -import torch - -from ..hook import Hook - - -class LoggerHook(Hook): - """Base class for logger hooks. - - Args: - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging. - by_epoch (bool): Whether EpochBasedRunner is used. - """ - - __metaclass__ = ABCMeta - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - self.interval = interval - self.ignore_last = ignore_last - self.reset_flag = reset_flag - self.by_epoch = by_epoch - - @abstractmethod - def log(self, runner): - pass - - @staticmethod - def is_scalar(val, include_np=True, include_torch=True): - """Tell the input variable is a scalar or not. - - Args: - val: Input variable. - include_np (bool): Whether include 0-d np.ndarray as a scalar. - include_torch (bool): Whether include 0-d torch.Tensor as a scalar. - - Returns: - bool: True or False. - """ - if isinstance(val, numbers.Number): - return True - elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: - return True - elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: - return True - else: - return False - - def get_mode(self, runner): - if runner.mode == 'train': - if 'time' in runner.log_buffer.output: - mode = 'train' - else: - mode = 'val' - elif runner.mode == 'val': - mode = 'val' - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return mode - - def get_epoch(self, runner): - if runner.mode == 'train': - epoch = runner.epoch + 1 - elif runner.mode == 'val': - # normal val mode - # runner.epoch += 1 has been done before val workflow - epoch = runner.epoch - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return epoch - - def get_iter(self, runner, inner_iter=False): - """Get the current training iteration step.""" - if self.by_epoch and inner_iter: - current_iter = runner.inner_iter + 1 - else: - current_iter = runner.iter + 1 - return current_iter - - def get_lr_tags(self, runner): - tags = {} - lrs = runner.current_lr() - if isinstance(lrs, dict): - for name, value in lrs.items(): - tags[f'learning_rate/{name}'] = value[0] - else: - tags['learning_rate'] = lrs[0] - return tags - - def get_momentum_tags(self, runner): - tags = {} - momentums = runner.current_momentum() - if isinstance(momentums, dict): - for name, value in momentums.items(): - tags[f'momentum/{name}'] = value[0] - else: - tags['momentum'] = momentums[0] - return tags - - def get_loggable_tags(self, - runner, - allow_scalar=True, - allow_text=False, - add_mode=True, - tags_to_skip=('time', 'data_time')): - tags = {} - for var, val in runner.log_buffer.output.items(): - if var in tags_to_skip: - continue - if self.is_scalar(val) and not allow_scalar: - continue - if isinstance(val, str) and not allow_text: - continue - if add_mode: - var = f'{self.get_mode(runner)}/{var}' - tags[var] = val - tags.update(self.get_lr_tags(runner)) - tags.update(self.get_momentum_tags(runner)) - return tags - - def before_run(self, runner): - for hook in runner.hooks[::-1]: - if isinstance(hook, LoggerHook): - hook.reset_flag = True - break - - def before_epoch(self, runner): - runner.log_buffer.clear() # clear logs of last epoch - - def after_train_iter(self, runner): - if self.by_epoch and self.every_n_inner_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif not self.by_epoch and self.every_n_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif self.end_of_epoch(runner) and not self.ignore_last: - # not precise but more stable - runner.log_buffer.average(self.interval) - - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_train_epoch(self, runner): - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_val_epoch(self, runner): - runner.log_buffer.average() - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/dvclive.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/dvclive.py deleted file mode 100755 index 687cdc58c..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/dvclive.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class DvcliveLoggerHook(LoggerHook): - """Class to log metrics with dvclive. - - It requires `dvclive`_ to be installed. - - Args: - path (str): Directory where dvclive will write TSV log files. - interval (int): Logging interval (every k iterations). - Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. - Default: True. - - .. _dvclive: - https://dvc.org/doc/dvclive - """ - - def __init__(self, - path, - interval=10, - ignore_last=True, - reset_flag=True, - by_epoch=True): - - super(DvcliveLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.path = path - self.import_dvclive() - - def import_dvclive(self): - try: - import dvclive - except ImportError: - raise ImportError( - 'Please run "pip install dvclive" to install dvclive') - self.dvclive = dvclive - - @master_only - def before_run(self, runner): - self.dvclive.init(self.path) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for k, v in tags.items(): - self.dvclive.log(k, v, step=self.get_iter(runner)) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/mlflow.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/mlflow.py deleted file mode 100755 index f9a72592b..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/mlflow.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class MlflowLoggerHook(LoggerHook): - - def __init__(self, - exp_name=None, - tags=None, - log_model=True, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - """Class to log metrics and (optionally) a trained model to MLflow. - - It requires `MLflow`_ to be installed. - - Args: - exp_name (str, optional): Name of the experiment to be used. - Default None. - If not None, set the active experiment. - If experiment does not exist, an experiment with provided name - will be created. - tags (dict of str: str, optional): Tags for the current run. - Default None. - If not None, set tags for the current run. - log_model (bool, optional): Whether to log an MLflow artifact. - Default True. - If True, log runner.model as an MLflow artifact - for the current run. - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging - by_epoch (bool): Whether EpochBasedRunner is used. - - .. _MLflow: - https://www.mlflow.org/docs/latest/index.html - """ - super(MlflowLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_mlflow() - self.exp_name = exp_name - self.tags = tags - self.log_model = log_model - - def import_mlflow(self): - try: - import mlflow - import mlflow.pytorch as mlflow_pytorch - except ImportError: - raise ImportError( - 'Please run "pip install mlflow" to install mlflow') - self.mlflow = mlflow - self.mlflow_pytorch = mlflow_pytorch - - @master_only - def before_run(self, runner): - super(MlflowLoggerHook, self).before_run(runner) - if self.exp_name is not None: - self.mlflow.set_experiment(self.exp_name) - if self.tags is not None: - self.mlflow.set_tags(self.tags) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.mlflow.log_metrics(tags, step=self.get_iter(runner)) - - @master_only - def after_run(self, runner): - if self.log_model: - self.mlflow_pytorch.log_model(runner.model, 'models') diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/neptune.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/neptune.py deleted file mode 100755 index 7a38772b0..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/neptune.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class NeptuneLoggerHook(LoggerHook): - """Class to log metrics to NeptuneAI. - - It requires `neptune-client` to be installed. - - Args: - init_kwargs (dict): a dict contains the initialization keys as below: - - project (str): Name of a project in a form of - namespace/project_name. If None, the value of - NEPTUNE_PROJECT environment variable will be taken. - - api_token (str): User’s API token. - If None, the value of NEPTUNE_API_TOKEN environment - variable will be taken. Note: It is strongly recommended - to use NEPTUNE_API_TOKEN environment variable rather than - placing your API token in plain text in your source code. - - name (str, optional, default is 'Untitled'): Editable name of - the run. Name is displayed in the run's Details and in - Runs table as a column. - Check https://docs.neptune.ai/api-reference/neptune#init for - more init arguments. - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging - by_epoch (bool): Whether EpochBasedRunner is used. - - .. _NeptuneAI: - https://docs.neptune.ai/you-should-know/logging-metadata - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=True, - with_step=True, - by_epoch=True): - - super(NeptuneLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_neptune() - self.init_kwargs = init_kwargs - self.with_step = with_step - - def import_neptune(self): - try: - import neptune.new as neptune - except ImportError: - raise ImportError( - 'Please run "pip install neptune-client" to install neptune') - self.neptune = neptune - self.run = None - - @master_only - def before_run(self, runner): - if self.init_kwargs: - self.run = self.neptune.init(**self.init_kwargs) - else: - self.run = self.neptune.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for tag_name, tag_value in tags.items(): - if self.with_step: - self.run[tag_name].log( - tag_value, step=self.get_iter(runner)) - else: - tags['global_step'] = self.get_iter(runner) - self.run[tag_name].log(tags) - - @master_only - def after_run(self, runner): - self.run.stop() diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/pavi.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/pavi.py deleted file mode 100755 index ba2f6e8df..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/pavi.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp - -import torch -import yaml - -import mmcv -from ....parallel.utils import is_module_wrapper -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class PaviLoggerHook(LoggerHook): - - def __init__(self, - init_kwargs=None, - add_graph=False, - add_last_ckpt=False, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - img_key='img_info'): - super(PaviLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.init_kwargs = init_kwargs - self.add_graph = add_graph - self.add_last_ckpt = add_last_ckpt - self.img_key = img_key - - @master_only - def before_run(self, runner): - super(PaviLoggerHook, self).before_run(runner) - try: - from pavi import SummaryWriter - except ImportError: - raise ImportError('Please run "pip install pavi" to install pavi.') - - self.run_name = runner.work_dir.split('/')[-1] - - if not self.init_kwargs: - self.init_kwargs = dict() - self.init_kwargs['name'] = self.run_name - self.init_kwargs['model'] = runner._model_name - if runner.meta is not None: - if 'config_dict' in runner.meta: - config_dict = runner.meta['config_dict'] - assert isinstance( - config_dict, - dict), ('meta["config_dict"] has to be of a dict, ' - f'but got {type(config_dict)}') - elif 'config_file' in runner.meta: - config_file = runner.meta['config_file'] - config_dict = dict(mmcv.Config.fromfile(config_file)) - else: - config_dict = None - if config_dict is not None: - # 'max_.*iter' is parsed in pavi sdk as the maximum iterations - # to properly set up the progress bar. - config_dict = config_dict.copy() - config_dict.setdefault('max_iter', runner.max_iters) - # non-serializable values are first converted in - # mmcv.dump to json - config_dict = json.loads( - mmcv.dump(config_dict, file_format='json')) - session_text = yaml.dump(config_dict) - self.init_kwargs['session_text'] = session_text - self.writer = SummaryWriter(**self.init_kwargs) - - def get_step(self, runner): - """Get the total training step/epoch.""" - if self.get_mode(runner) == 'val' and self.by_epoch: - return self.get_epoch(runner) - else: - return self.get_iter(runner) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, add_mode=False) - if tags: - self.writer.add_scalars( - self.get_mode(runner), tags, self.get_step(runner)) - - @master_only - def after_run(self, runner): - if self.add_last_ckpt: - ckpt_path = osp.join(runner.work_dir, 'latest.pth') - if osp.islink(ckpt_path): - ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) - - if osp.isfile(ckpt_path): - # runner.epoch += 1 has been done before `after_run`. - iteration = runner.epoch if self.by_epoch else runner.iter - return self.writer.add_snapshot_file( - tag=self.run_name, - snapshot_file_path=ckpt_path, - iteration=iteration) - - # flush the buffer and send a task ending signal to Pavi - self.writer.close() - - @master_only - def before_epoch(self, runner): - if runner.epoch == 0 and self.add_graph: - if is_module_wrapper(runner.model): - _model = runner.model.module - else: - _model = runner.model - device = next(_model.parameters()).device - data = next(iter(runner.data_loader)) - image = data[self.img_key][0:1].to(device) - with torch.no_grad(): - self.writer.add_graph(_model, image) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/tensorboard.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/tensorboard.py deleted file mode 100755 index a8d50366f..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/tensorboard.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import TORCH_VERSION, digit_version -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TensorboardLoggerHook(LoggerHook): - - def __init__(self, - log_dir=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(TensorboardLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.log_dir = log_dir - - @master_only - def before_run(self, runner): - super(TensorboardLoggerHook, self).before_run(runner) - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.1')): - try: - from tensorboardX import SummaryWriter - except ImportError: - raise ImportError('Please install tensorboardX to use ' - 'TensorboardLoggerHook.') - else: - try: - from torch.utils.tensorboard import SummaryWriter - except ImportError: - raise ImportError( - 'Please run "pip install future tensorboard" to install ' - 'the dependencies to use torch.utils.tensorboard ' - '(applicable to PyTorch 1.1 or higher)') - - if self.log_dir is None: - self.log_dir = osp.join(runner.work_dir, 'tf_logs') - self.writer = SummaryWriter(self.log_dir) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, allow_text=True) - for tag, val in tags.items(): - if isinstance(val, str): - self.writer.add_text(tag, val, self.get_iter(runner)) - else: - self.writer.add_scalar(tag, val, self.get_iter(runner)) - - @master_only - def after_run(self, runner): - self.writer.close() diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/text.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/text.py deleted file mode 100755 index 043c7bf20..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/text.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import datetime -import os -import os.path as osp -from collections import OrderedDict - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.fileio.file_client import FileClient -from mmcv.utils import is_tuple_of, scandir -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TextLoggerHook(LoggerHook): - """Logger hook in text. - - In this logger hook, the information will be printed on terminal and - saved in json file. - - Args: - by_epoch (bool, optional): Whether EpochBasedRunner is used. - Default: True. - interval (int, optional): Logging interval (every k iterations). - Default: 10. - ignore_last (bool, optional): Ignore the log of last iterations in each - epoch if less than :attr:`interval`. Default: True. - reset_flag (bool, optional): Whether to clear the output buffer after - logging. Default: False. - interval_exp_name (int, optional): Logging interval for experiment - name. This feature is to help users conveniently get the experiment - information from screen or log file. Default: 1000. - out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. - If ``out_dir`` is specified, logs will be copied to a new directory - which is the concatenation of ``out_dir`` and the last level - directory of ``runner.work_dir``. Default: None. - `New in version 1.3.16.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be copied to ``out_dir``. - Default: ('.log.json', '.log', '.py'). - `New in version 1.3.16.` - keep_local (bool, optional): Whether to keep local log when - :attr:`out_dir` is specified. If False, the local log will be - removed. Default: True. - `New in version 1.3.16.` - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - - def __init__(self, - by_epoch=True, - interval=10, - ignore_last=True, - reset_flag=False, - interval_exp_name=1000, - out_dir=None, - out_suffix=('.log.json', '.log', '.py'), - keep_local=True, - file_client_args=None): - super(TextLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.by_epoch = by_epoch - self.time_sec_tot = 0 - self.interval_exp_name = interval_exp_name - - if out_dir is None and file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" when `out_dir` is not' - 'specified.') - self.out_dir = out_dir - - if not (out_dir is None or isinstance(out_dir, str) - or is_tuple_of(out_dir, str)): - raise TypeError('out_dir should be "None" or string or tuple of ' - 'string, but got {out_dir}') - self.out_suffix = out_suffix - - self.keep_local = keep_local - self.file_client_args = file_client_args - if self.out_dir is not None: - self.file_client = FileClient.infer_client(file_client_args, - self.out_dir) - - def before_run(self, runner): - super(TextLoggerHook, self).before_run(runner) - - if self.out_dir is not None: - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - # The final `self.out_dir` is the concatenation of `self.out_dir` - # and the last level directory of `runner.work_dir` - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'Text logs will be saved to {self.out_dir} by ' - f'{self.file_client.name} after the training process.')) - - self.start_iter = runner.iter - self.json_log_path = osp.join(runner.work_dir, - f'{runner.timestamp}.log.json') - if runner.meta is not None: - self._dump_log(runner.meta, runner) - - def _get_max_memory(self, runner): - device = getattr(runner.model, 'output_device', None) - mem = torch.cuda.max_memory_allocated(device=device) - mem_mb = torch.tensor([mem / (1024 * 1024)], - dtype=torch.int, - device=device) - if runner.world_size > 1: - dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) - return mem_mb.item() - - def _log_info(self, log_dict, runner): - # print exp name for users to distinguish experiments - # at every ``interval_exp_name`` iterations and the end of each epoch - if runner.meta is not None and 'exp_name' in runner.meta: - if (self.every_n_iters(runner, self.interval_exp_name)) or ( - self.by_epoch and self.end_of_epoch(runner)): - exp_info = f'Exp name: {runner.meta["exp_name"]}' - runner.logger.info(exp_info) - - if log_dict['mode'] == 'train': - if isinstance(log_dict['lr'], dict): - lr_str = [] - for k, val in log_dict['lr'].items(): - lr_str.append(f'lr_{k}: {val:.3e}') - lr_str = ' '.join(lr_str) - else: - lr_str = f'lr: {log_dict["lr"]:.3e}' - - # by epoch: Epoch [4][100/1000] - # by iter: Iter [100/100000] - if self.by_epoch: - log_str = f'Epoch [{log_dict["epoch"]}]' \ - f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' - else: - log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' - log_str += f'{lr_str}, ' - - if 'time' in log_dict.keys(): - self.time_sec_tot += (log_dict['time'] * self.interval) - time_sec_avg = self.time_sec_tot / ( - runner.iter - self.start_iter + 1) - eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) - eta_str = str(datetime.timedelta(seconds=int(eta_sec))) - log_str += f'eta: {eta_str}, ' - log_str += f'time: {log_dict["time"]:.3f}, ' \ - f'data_time: {log_dict["data_time"]:.3f}, ' - # statistic memory - if torch.cuda.is_available(): - log_str += f'memory: {log_dict["memory"]}, ' - else: - # val/test time - # here 1000 is the length of the val dataloader - # by epoch: Epoch[val] [4][1000] - # by iter: Iter[val] [1000] - if self.by_epoch: - log_str = f'Epoch({log_dict["mode"]}) ' \ - f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' - else: - log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' - - log_items = [] - for name, val in log_dict.items(): - # TODO: resolve this hack - # these items have been in log_str - if name in [ - 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', - 'memory', 'epoch' - ]: - continue - if isinstance(val, float): - val = f'{val:.4f}' - log_items.append(f'{name}: {val}') - log_str += ', '.join(log_items) - - runner.logger.info(log_str) - - def _dump_log(self, log_dict, runner): - # dump log in json format - json_log = OrderedDict() - for k, v in log_dict.items(): - json_log[k] = self._round_float(v) - # only append log at last line - if runner.rank == 0: - with open(self.json_log_path, 'a+') as f: - mmcv.dump(json_log, f, file_format='json') - f.write('\n') - - def _round_float(self, items): - if isinstance(items, list): - return [self._round_float(item) for item in items] - elif isinstance(items, float): - return round(items, 5) - else: - return items - - def log(self, runner): - if 'eval_iter_num' in runner.log_buffer.output: - # this doesn't modify runner.iter and is regardless of by_epoch - cur_iter = runner.log_buffer.output.pop('eval_iter_num') - else: - cur_iter = self.get_iter(runner, inner_iter=True) - - log_dict = OrderedDict( - mode=self.get_mode(runner), - epoch=self.get_epoch(runner), - iter=cur_iter) - - # only record lr of the first param group - cur_lr = runner.current_lr() - if isinstance(cur_lr, list): - log_dict['lr'] = cur_lr[0] - else: - assert isinstance(cur_lr, dict) - log_dict['lr'] = {} - for k, lr_ in cur_lr.items(): - assert isinstance(lr_, list) - log_dict['lr'].update({k: lr_[0]}) - - if 'time' in runner.log_buffer.output: - # statistic memory - if torch.cuda.is_available(): - log_dict['memory'] = self._get_max_memory(runner) - - log_dict = dict(log_dict, **runner.log_buffer.output) - - self._log_info(log_dict, runner) - self._dump_log(log_dict, runner) - return log_dict - - def after_run(self, runner): - # copy or upload logs to self.out_dir - if self.out_dir is not None: - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - out_filepath = self.file_client.join_path( - self.out_dir, filename) - with open(local_filepath, 'r') as f: - self.file_client.put_text(f.read(), out_filepath) - - runner.logger.info( - (f'The file {local_filepath} has been uploaded to ' - f'{out_filepath}.')) - - if not self.keep_local: - os.remove(local_filepath) - runner.logger.info( - (f'{local_filepath} was removed due to the ' - '`self.keep_local=False`')) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/wandb.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/wandb.py deleted file mode 100755 index 9f6808462..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/logger/wandb.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class WandbLoggerHook(LoggerHook): - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - commit=True, - by_epoch=True, - with_step=True): - super(WandbLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_wandb() - self.init_kwargs = init_kwargs - self.commit = commit - self.with_step = with_step - - def import_wandb(self): - try: - import wandb - except ImportError: - raise ImportError( - 'Please run "pip install wandb" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(WandbLoggerHook, self).before_run(runner) - if self.wandb is None: - self.import_wandb() - if self.init_kwargs: - self.wandb.init(**self.init_kwargs) - else: - self.wandb.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - if self.with_step: - self.wandb.log( - tags, step=self.get_iter(runner), commit=self.commit) - else: - tags['global_step'] = self.get_iter(runner) - self.wandb.log(tags, commit=self.commit) - - @master_only - def after_run(self, runner): - self.wandb.join() diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/lr_updater.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/lr_updater.py deleted file mode 100755 index e5a124157..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/lr_updater.py +++ /dev/null @@ -1,670 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from math import cos, pi - -import mmcv -from .hook import HOOKS, Hook - - -class LrUpdaterHook(Hook): - """LR Scheduler in MMCV. - - Args: - by_epoch (bool): LR changes epoch by epoch - warmup (string): Type of warmup used. It can be None(use no warmup), - 'constant', 'linear' or 'exp' - warmup_iters (int): The number of iterations or epochs that warmup - lasts - warmup_ratio (float): LR used at the beginning of warmup equals to - warmup_ratio * initial_lr - warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters - means the number of epochs that warmup lasts, otherwise means the - number of iteration that warmup lasts - """ - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.1, - warmup_by_epoch=False): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_ratio" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - self.warmup_by_epoch = warmup_by_epoch - - if self.warmup_by_epoch: - self.warmup_epochs = self.warmup_iters - self.warmup_iters = None - else: - self.warmup_epochs = None - - self.base_lr = [] # initial lr for all param groups - self.regular_lr = [] # expected lr if no warming up is performed - - def _set_lr(self, runner, lr_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, lr in zip(optim.param_groups, lr_groups[k]): - param_group['lr'] = lr - else: - for param_group, lr in zip(runner.optimizer.param_groups, - lr_groups): - param_group['lr'] = lr - - def get_lr(self, runner, base_lr): - raise NotImplementedError - - def get_regular_lr(self, runner): - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(runner, _base_lr) - for _base_lr in self.base_lr[k] - ] - lr_groups.update({k: _lr_group}) - - return lr_groups - else: - return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - if self.warmup == 'constant': - warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_lr = [_lr * (1 - k) for _lr in regular_lr] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.regular_lr, dict): - lr_groups = {} - for key, regular_lr in self.regular_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.regular_lr) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - group.setdefault('initial_lr', group['lr']) - _base_lr = [ - group['initial_lr'] for group in optim.param_groups - ] - self.base_lr.update({k: _base_lr}) - else: - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['initial_lr'] for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if self.warmup_iters is None: - epoch_len = len(runner.data_loader) - self.warmup_iters = self.warmup_epochs * epoch_len - - if not self.by_epoch: - return - - self.regular_lr = self.get_regular_lr(runner) - self._set_lr(runner, self.regular_lr) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_lr = self.get_regular_lr(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - - -@HOOKS.register_module() -class FixedLrUpdaterHook(LrUpdaterHook): - - def __init__(self, **kwargs): - super(FixedLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - return base_lr - - -@HOOKS.register_module() -class StepLrUpdaterHook(LrUpdaterHook): - """Step LR scheduler with min_lr clipping. - - Args: - step (int | list[int]): Step to decay the LR. If an int value is given, - regard it as the decay interval. If a list is given, decay LR at - these steps. - gamma (float, optional): Decay LR ratio. Default: 0.1. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. If None - is given, we don't perform lr clipping. Default: None. - """ - - def __init__(self, step, gamma=0.1, min_lr=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_lr = min_lr - super(StepLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - lr = base_lr * (self.gamma**exp) - if self.min_lr is not None: - # clip to a minimum value - lr = max(lr, self.min_lr) - return lr - - -@HOOKS.register_module() -class ExpLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, **kwargs): - self.gamma = gamma - super(ExpLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * self.gamma**progress - - -@HOOKS.register_module() -class PolyLrUpdaterHook(LrUpdaterHook): - - def __init__(self, power=1., min_lr=0., **kwargs): - self.power = power - self.min_lr = min_lr - super(PolyLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - coeff = (1 - progress / max_progress)**self.power - return (base_lr - self.min_lr) * coeff + self.min_lr - - -@HOOKS.register_module() -class InvLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, power=1., **kwargs): - self.gamma = gamma - self.power = power - super(InvLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * (1 + self.gamma * progress)**(-self.power) - - -@HOOKS.register_module() -class CosineAnnealingLrUpdaterHook(LrUpdaterHook): - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(CosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): - """Flat + Cosine lr schedule. - - Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 - - Args: - start_percent (float): When to start annealing the learning rate - after the percentage of the total training steps. - The value should be in range [0, 1). - Default: 0.75 - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - start_percent=0.75, - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - if start_percent < 0 or start_percent > 1 or not isinstance( - start_percent, float): - raise ValueError( - 'expected float between 0 and 1 start_percent, but ' - f'got {start_percent}') - self.start_percent = start_percent - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(FlatCosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - start = round(runner.max_epochs * self.start_percent) - progress = runner.epoch - start - max_progress = runner.max_epochs - start - else: - start = round(runner.max_iters * self.start_percent) - progress = runner.iter - start - max_progress = runner.max_iters - start - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress < 0: - return base_lr - else: - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class CosineRestartLrUpdaterHook(LrUpdaterHook): - """Cosine annealing with restarts learning rate scheme. - - Args: - periods (list[int]): Periods for each cosine anneling cycle. - restart_weights (list[float], optional): Restart weights at each - restart iteration. Default: [1]. - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - periods, - restart_weights=[1], - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.periods = periods - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - self.restart_weights = restart_weights - assert (len(self.periods) == len(self.restart_weights) - ), 'periods and restart_weights should have the same length.' - super(CosineRestartLrUpdaterHook, self).__init__(**kwargs) - - self.cumulative_periods = [ - sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) - ] - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - else: - progress = runner.iter - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - idx = get_position_from_periods(progress, self.cumulative_periods) - current_weight = self.restart_weights[idx] - nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] - current_periods = self.periods[idx] - - alpha = min((progress - nearest_restart) / current_periods, 1) - return annealing_cos(base_lr, target_lr, alpha, current_weight) - - -def get_position_from_periods(iteration, cumulative_periods): - """Get the position from a period list. - - It will return the index of the right-closest number in the period list. - For example, the cumulative_periods = [100, 200, 300, 400], - if iteration == 50, return 0; - if iteration == 210, return 2; - if iteration == 300, return 3. - - Args: - iteration (int): Current iteration. - cumulative_periods (list[int]): Cumulative period list. - - Returns: - int: The position of the right-closest number in the period list. - """ - for i, period in enumerate(cumulative_periods): - if iteration < period: - return i - raise ValueError(f'Current iteration {iteration} exceeds ' - f'cumulative_periods {cumulative_periods}') - - -@HOOKS.register_module() -class CyclicLrUpdaterHook(LrUpdaterHook): - """Cyclic LR Scheduler. - - Implement the cyclical learning rate policy (CLR) described in - https://arxiv.org/pdf/1506.01186.pdf - - Different from the original paper, we use cosine annealing rather than - triangular policy inside a cycle. This improves the performance in the - 3D detection area. - - Args: - by_epoch (bool): Whether to update LR by epoch. - target_ratio (tuple[float]): Relative ratio of the highest LR and the - lowest LR to the initial LR. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of LR in - the total cycle. - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - """ - - def __init__(self, - by_epoch=False, - target_ratio=(10, 1e-4), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.lr_phases = [] # init lr_phases - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicLrUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicLrUpdaterHook, self).before_run(runner) - # initiate lr_phases - # total lr_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.lr_phases.append( - [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) - self.lr_phases.append([ - iter_up_phase, max_iter_per_phase, max_iter_per_phase, - self.target_ratio[0], self.target_ratio[1] - ]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - for (start_iter, end_iter, max_iter_per_phase, start_ratio, - end_ratio) in self.lr_phases: - curr_iter %= max_iter_per_phase - if start_iter <= curr_iter < end_iter: - progress = curr_iter - start_iter - return self.anneal_func(base_lr * start_ratio, - base_lr * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleLrUpdaterHook(LrUpdaterHook): - """One Cycle LR Scheduler. - - The 1cycle learning rate policy changes the learning rate after every - batch. The one cycle learning rate policy is described in - https://arxiv.org/pdf/1708.07120.pdf - - Args: - max_lr (float or list): Upper learning rate boundaries in the cycle - for each parameter group. - total_steps (int, optional): The total number of steps in the cycle. - Note that if a value is not provided here, it will be the max_iter - of runner. Default: None. - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - div_factor (float): Determines the initial learning rate via - initial_lr = max_lr/div_factor - Default: 25 - final_div_factor (float): Determines the minimum learning rate via - min_lr = initial_lr/final_div_factor - Default: 1e4 - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - max_lr, - total_steps=None, - pct_start=0.3, - anneal_strategy='cos', - div_factor=25, - final_div_factor=1e4, - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch = False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(max_lr, (numbers.Number, list, dict)): - raise ValueError('the type of max_lr must be the one of list or ' - f'dict, but got {type(max_lr)}') - self._max_lr = max_lr - if total_steps is not None: - if not isinstance(total_steps, int): - raise ValueError('the type of total_steps must be int, but' - f'got {type(total_steps)}') - self.total_steps = total_steps - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.div_factor = div_factor - self.final_div_factor = final_div_factor - self.three_phase = three_phase - self.lr_phases = [] # init lr_phases - super(OneCycleLrUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if hasattr(self, 'total_steps'): - total_steps = self.total_steps - else: - total_steps = runner.max_iters - if total_steps < runner.max_iters: - raise ValueError( - 'The total steps must be greater than or equal to max ' - f'iterations {runner.max_iters} of runner, but total steps ' - f'is {total_steps}.') - - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - _max_lr = format_param(k, optim, self._max_lr) - self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(optim.param_groups, self.base_lr[k]): - group.setdefault('initial_lr', lr) - else: - k = type(runner.optimizer).__name__ - _max_lr = format_param(k, runner.optimizer, self._max_lr) - self.base_lr = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(runner.optimizer.param_groups, self.base_lr): - group.setdefault('initial_lr', lr) - - if self.three_phase: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append([ - float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 - ]) - self.lr_phases.append( - [total_steps - 1, 1, 1 / self.final_div_factor]) - else: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append( - [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - start_iter = 0 - for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): - if curr_iter <= end_iter: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, - pct) - break - start_iter = end_iter - return lr - - -def annealing_cos(start, end, factor, weight=1): - """Calculate annealing cos learning rate. - - Cosine anneal from `weight * start + (1 - weight) * end` to `end` as - percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the cosine annealing. - end (float): The ending learing rate of the cosine annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - weight (float, optional): The combination factor of `start` and `end` - when calculating the actual starting learning rate. Default to 1. - """ - cos_out = cos(pi * factor) + 1 - return end + 0.5 * weight * (start - end) * cos_out - - -def annealing_linear(start, end, factor): - """Calculate annealing linear learning rate. - - Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the linear annealing. - end (float): The ending learing rate of the linear annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - """ - return start + (end - start) * factor - - -def format_param(name, optim, param): - if isinstance(param, numbers.Number): - return [param] * len(optim.param_groups) - elif isinstance(param, (list, tuple)): # multi param groups - if len(param) != len(optim.param_groups): - raise ValueError(f'expected {len(optim.param_groups)} ' - f'values for {name}, got {len(param)}') - return param - else: # multi optimizers - if name not in param: - raise KeyError(f'{name} is not found in {param.keys()}') - return param[name] diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/memory.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/memory.py deleted file mode 100755 index 70cf9a838..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class EmptyCacheHook(Hook): - - def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): - self._before_epoch = before_epoch - self._after_epoch = after_epoch - self._after_iter = after_iter - - def after_iter(self, runner): - if self._after_iter: - torch.cuda.empty_cache() - - def before_epoch(self, runner): - if self._before_epoch: - torch.cuda.empty_cache() - - def after_epoch(self, runner): - if self._after_epoch: - torch.cuda.empty_cache() diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/momentum_updater.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/momentum_updater.py deleted file mode 100755 index 13d0e2fab..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/momentum_updater.py +++ /dev/null @@ -1,493 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -from .hook import HOOKS, Hook -from .lr_updater import annealing_cos, annealing_linear, format_param - - -class MomentumUpdaterHook(Hook): - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.9): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_momentum" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - - self.base_momentum = [] # initial momentum for all param groups - self.regular_momentum = [ - ] # expected momentum if no warming up is performed - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, base_momentum): - raise NotImplementedError - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k in runner.optimizer.keys(): - _momentum_group = [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum[k] - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - return [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum - ] - - def get_warmup_momentum(self, cur_iters): - - def _get_warmup_momentum(cur_iters, regular_momentum): - if self.warmup == 'constant': - warmup_momentum = [ - _momentum / self.warmup_ratio - for _momentum in self.regular_momentum - ] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_momentum = [ - _momentum / (1 - k) for _momentum in self.regular_mom - ] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_momentum = [ - _momentum / k for _momentum in self.regular_mom - ] - return warmup_momentum - - if isinstance(self.regular_momentum, dict): - momentum_groups = {} - for key, regular_momentum in self.regular_momentum.items(): - momentum_groups[key] = _get_warmup_momentum( - cur_iters, regular_momentum) - return momentum_groups - else: - return _get_warmup_momentum(cur_iters, self.regular_momentum) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, - # if 'initial_momentum' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_momentum = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - _base_momentum = [ - group['initial_momentum'] for group in optim.param_groups - ] - self.base_momentum.update({k: _base_momentum}) - else: - for group in runner.optimizer.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - self.base_momentum = [ - group['initial_momentum'] - for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if not self.by_epoch: - return - self.regular_mom = self.get_regular_momentum(runner) - self._set_momentum(runner, self.regular_mom) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_mom = self.get_regular_momentum(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_momentum(runner, self.regular_mom) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_momentum(runner, self.regular_mom) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - - -@HOOKS.register_module() -class StepMomentumUpdaterHook(MomentumUpdaterHook): - """Step momentum scheduler with min value clipping. - - Args: - step (int | list[int]): Step to decay the momentum. If an int value is - given, regard it as the decay interval. If a list is given, decay - momentum at these steps. - gamma (float, optional): Decay momentum ratio. Default: 0.5. - min_momentum (float, optional): Minimum momentum value to keep. If - momentum after decay is lower than this value, it will be clipped - accordingly. If None is given, we don't perform lr clipping. - Default: None. - """ - - def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_momentum = min_momentum - super(StepMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - momentum = base_momentum * (self.gamma**exp) - if self.min_momentum is not None: - # clip to a minimum value - momentum = max(momentum, self.min_momentum) - return momentum - - -@HOOKS.register_module() -class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(CosineAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_cos(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class CyclicMomentumUpdaterHook(MomentumUpdaterHook): - """Cyclic momentum Scheduler. - - Implement the cyclical momentum scheduler policy described in - https://arxiv.org/pdf/1708.07120.pdf - - This momentum scheduler usually used together with the CyclicLRUpdater - to improve the performance in the 3D detection area. - - Attributes: - target_ratio (tuple[float]): Relative ratio of the lowest momentum and - the highest momentum to the initial momentum. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of momentum - in the total cycle. - by_epoch (bool): Whether to update momentum by epoch. - """ - - def __init__(self, - by_epoch=False, - target_ratio=(0.85 / 0.95, 1), - cyclic_times=1, - step_ratio_up=0.4, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.momentum_phases = [] # init momentum_phases - # currently only support by_epoch=False - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicMomentumUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicMomentumUpdaterHook, self).before_run(runner) - # initiate momentum_phases - # total momentum_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.momentum_phases.append( - [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) - self.momentum_phases.append([ - iter_up_phase, max_iter_per_phase, max_iter_per_phase, - self.target_ratio[0], self.target_ratio[1] - ]) - - def get_momentum(self, runner, base_momentum): - curr_iter = runner.iter - for (start_iter, end_iter, max_iter_per_phase, start_ratio, - end_ratio) in self.momentum_phases: - curr_iter %= max_iter_per_phase - if start_iter <= curr_iter < end_iter: - progress = curr_iter - start_iter - return annealing_cos(base_momentum * start_ratio, - base_momentum * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): - """OneCycle momentum Scheduler. - - This momentum scheduler usually used together with the OneCycleLrUpdater - to improve the performance. - - Args: - base_momentum (float or list): Lower momentum boundaries in the cycle - for each parameter group. Note that momentum is cycled inversely - to learning rate; at the peak of a cycle, momentum is - 'base_momentum' and learning rate is 'max_lr'. - Default: 0.85 - max_momentum (float or list): Upper momentum boundaries in the cycle - for each parameter group. Functionally, - it defines the cycle amplitude (max_momentum - base_momentum). - Note that momentum is cycled inversely - to learning rate; at the start of a cycle, momentum is - 'max_momentum' and learning rate is 'base_lr' - Default: 0.95 - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - base_momentum=0.85, - max_momentum=0.95, - pct_start=0.3, - anneal_strategy='cos', - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch=False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(base_momentum, (float, list, dict)): - raise ValueError('base_momentum must be the type among of float,' - 'list or dict.') - self._base_momentum = base_momentum - if not isinstance(max_momentum, (float, list, dict)): - raise ValueError('max_momentum must be the type among of float,' - 'list or dict.') - self._max_momentum = max_momentum - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('Expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must by one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.three_phase = three_phase - self.momentum_phases = [] # init momentum_phases - super(OneCycleMomentumUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip( - optim.param_groups, _base_momentum, _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - else: - optim = runner.optimizer - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - k = type(optim).__name__ - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip(optim.param_groups, - _base_momentum, - _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - - if self.three_phase: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': - float(2 * self.pct_start * runner.max_iters) - 2, - 'start_momentum': - 'base_momentum', - 'end_momentum': - 'max_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'max_momentum', - 'end_momentum': 'max_momentum' - }) - else: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'base_momentum', - 'end_momentum': 'max_momentum' - }) - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, param_group): - curr_iter = runner.iter - start_iter = 0 - for i, phase in enumerate(self.momentum_phases): - end_iter = phase['end_iter'] - if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - momentum = self.anneal_func( - param_group[phase['start_momentum']], - param_group[phase['end_momentum']], pct) - break - start_iter = end_iter - return momentum - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k, optim in runner.optimizer.items(): - _momentum_group = [ - self.get_momentum(runner, param_group) - for param_group in optim.param_groups - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - momentum_groups = [] - for param_group in runner.optimizer.param_groups: - momentum_groups.append(self.get_momentum(runner, param_group)) - return momentum_groups diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/optimizer.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/optimizer.py deleted file mode 100755 index f575ceda0..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/optimizer.py +++ /dev/null @@ -1,508 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -from collections import defaultdict -from itertools import chain - -from torch.nn.utils import clip_grad - -from mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version -from ..dist_utils import allreduce_grads -from ..fp16_utils import LossScaler, wrap_fp16_model -from .hook import HOOKS, Hook - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - from torch.cuda.amp import GradScaler -except ImportError: - pass - - -@HOOKS.register_module() -class OptimizerHook(Hook): - - def __init__(self, grad_clip=None): - self.grad_clip = grad_clip - - def clip_grads(self, params): - params = list( - filter(lambda p: p.requires_grad and p.grad is not None, params)) - if len(params) > 0: - return clip_grad.clip_grad_norm_(params, **self.grad_clip) - - def after_train_iter(self, runner): - runner.optimizer.zero_grad() - runner.outputs['loss'].backward() - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - - -@HOOKS.register_module() -class GradientCumulativeOptimizerHook(OptimizerHook): - """Optimizer Hook implements multi-iters gradient cumulating. - - Args: - cumulative_iters (int, optional): Num of gradient cumulative iters. - The optimizer will step every `cumulative_iters` iters. - Defaults to 1. - - Examples: - >>> # Use cumulative_iters to simulate a large batch size - >>> # It is helpful when the hardware cannot handle a large batch size. - >>> loader = DataLoader(data, batch_size=64) - >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) - >>> # almost equals to - >>> loader = DataLoader(data, batch_size=256) - >>> optim_hook = OptimizerHook() - """ - - def __init__(self, cumulative_iters=1, **kwargs): - super(GradientCumulativeOptimizerHook, self).__init__(**kwargs) - - assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ - f'cumulative_iters only accepts positive int, but got ' \ - f'{type(cumulative_iters)} instead.' - - self.cumulative_iters = cumulative_iters - self.divisible_iters = 0 - self.remainder_iters = 0 - self.initialized = False - - def has_batch_norm(self, module): - if isinstance(module, _BatchNorm): - return True - for m in module.children(): - if self.has_batch_norm(m): - return True - return False - - def _init(self, runner): - if runner.iter % self.cumulative_iters != 0: - runner.logger.warning( - 'Resume iter number is not divisible by cumulative_iters in ' - 'GradientCumulativeOptimizerHook, which means the gradient of ' - 'some iters is lost and the result may be influenced slightly.' - ) - - if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: - runner.logger.warning( - 'GradientCumulativeOptimizerHook may slightly decrease ' - 'performance if the model has BatchNorm layers.') - - residual_iters = runner.max_iters - runner.iter - - self.divisible_iters = ( - residual_iters // self.cumulative_iters * self.cumulative_iters) - self.remainder_iters = residual_iters - self.divisible_iters - - self.initialized = True - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - runner.optimizer.zero_grad() - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - self.loss_scaler = GradScaler() - elif isinstance(loss_scale, float): - self._scale_update_param = loss_scale - self.loss_scaler = GradScaler(init_scale=loss_scale) - elif isinstance(loss_scale, dict): - self.loss_scaler = GradScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training.""" - # wrap model mode to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer to - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients. - 3. Unscale the optimizer’s gradient tensors. - 4. Call optimizer.step() and update scale factor. - 5. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - - self.loss_scaler.scale(runner.outputs['loss']).backward() - self.loss_scaler.unscale_(runner.optimizer) - # grad clip - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using PyTorch's implementation) implements - multi-iters gradient cumulating. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - """ - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - - self.loss_scaler.scale(loss).backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - self.loss_scaler.unscale_(runner.optimizer) - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() - -else: - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (mmcv's implementation). - - The steps of fp16 optimizer is as follows. - 1. Scale the loss value. - 2. BP in the fp16 model. - 2. Copy gradients from fp16 model to fp32 weights. - 3. Update fp32 weights. - 4. Copy updated parameters from fp32 weights to fp16 model. - - Refer to https://arxiv.org/abs/1710.03740 for more details. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of LossScaler. - Defaults to 512. - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - if loss_scale == 'dynamic': - self.loss_scaler = LossScaler(mode='dynamic') - elif isinstance(loss_scale, float): - self.loss_scaler = LossScaler( - init_scale=loss_scale, mode='static') - elif isinstance(loss_scale, dict): - self.loss_scaler = LossScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training. - - 1. Make a master copy of fp32 weights for optimization. - 2. Convert the main model from fp32 to fp16. - """ - # keep a copy of fp32 weights - old_groups = runner.optimizer.param_groups - runner.optimizer.param_groups = copy.deepcopy( - runner.optimizer.param_groups) - state = defaultdict(dict) - p_map = { - old_p: p - for old_p, p in zip( - chain(*(g['params'] for g in old_groups)), - chain(*(g['params'] - for g in runner.optimizer.param_groups))) - } - for k, v in runner.optimizer.state.items(): - state[p_map[k]] = v - runner.optimizer.state = state - # convert model to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer `loss_scalar.py` - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients (fp16). - 3. Copy gradients from the model to the fp32 weight copy. - 4. Scale the gradients back and update the fp32 weight copy. - 5. Copy back the params from fp32 weight copy to the fp16 model. - 6. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - # scale the loss value - scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale - scaled_loss.backward() - # copy fp16 grads in the model to fp32 params in the optimizer - - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - self.loss_scaler.update_scale(has_overflow) - if has_overflow: - runner.logger.warning('Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using mmcv implementation) implements multi- - iters gradient cumulating.""" - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - - loss = runner.outputs['loss'] - loss = loss / loss_factor - - # scale the loss value - scaled_loss = loss * self.loss_scaler.loss_scale - scaled_loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - else: - runner.logger.warning( - 'Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - self.loss_scaler.update_scale(has_overflow) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/profiler.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/profiler.py deleted file mode 100755 index b70236997..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/profiler.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Callable, List, Optional, Union - -import torch - -from ..dist_utils import master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ProfilerHook(Hook): - """Profiler to analyze performance during training. - - PyTorch Profiler is a tool that allows the collection of the performance - metrics during the training. More details on Profiler can be found at - https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile - - Args: - by_epoch (bool): Profile performance by epoch or by iteration. - Default: True. - profile_iters (int): Number of iterations for profiling. - If ``by_epoch=True``, profile_iters indicates that they are the - first profile_iters epochs at the beginning of the - training, otherwise it indicates the first profile_iters - iterations. Default: 1. - activities (list[str]): List of activity groups (CPU, CUDA) to use in - profiling. Default: ['cpu', 'cuda']. - schedule (dict, optional): Config of generating the callable schedule. - if schedule is None, profiler will not add step markers into the - trace and table view. Default: None. - on_trace_ready (callable, dict): Either a handler or a dict of generate - handler. Default: None. - record_shapes (bool): Save information about operator's input shapes. - Default: False. - profile_memory (bool): Track tensor memory allocation/deallocation. - Default: False. - with_stack (bool): Record source information (file and line number) - for the ops. Default: False. - with_flops (bool): Use formula to estimate the FLOPS of specific - operators (matrix multiplication and 2D convolution). - Default: False. - json_trace_path (str, optional): Exports the collected trace in Chrome - JSON format. Default: None. - - Example: - >>> runner = ... # instantiate a Runner - >>> # tensorboard trace - >>> trace_config = dict(type='tb_trace', dir_name='work_dir') - >>> profiler_config = dict(on_trace_ready=trace_config) - >>> runner.register_profiler_hook(profiler_config) - >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) - """ - - def __init__(self, - by_epoch: bool = True, - profile_iters: int = 1, - activities: List[str] = ['cpu', 'cuda'], - schedule: Optional[dict] = None, - on_trace_ready: Optional[Union[Callable, dict]] = None, - record_shapes: bool = False, - profile_memory: bool = False, - with_stack: bool = False, - with_flops: bool = False, - json_trace_path: Optional[str] = None) -> None: - try: - from torch import profiler # torch version >= 1.8.1 - except ImportError: - raise ImportError('profiler is the new feature of torch1.8.1, ' - f'but your version is {torch.__version__}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' - self.by_epoch = by_epoch - - if profile_iters < 1: - raise ValueError('profile_iters should be greater than 0, but got ' - f'{profile_iters}') - self.profile_iters = profile_iters - - if not isinstance(activities, list): - raise ValueError( - f'activities should be list, but got {type(activities)}') - self.activities = [] - for activity in activities: - activity = activity.lower() - if activity == 'cpu': - self.activities.append(profiler.ProfilerActivity.CPU) - elif activity == 'cuda': - self.activities.append(profiler.ProfilerActivity.CUDA) - else: - raise ValueError( - f'activity should be "cpu" or "cuda", but got {activity}') - - if schedule is not None: - self.schedule = profiler.schedule(**schedule) - else: - self.schedule = None - - self.on_trace_ready = on_trace_ready - self.record_shapes = record_shapes - self.profile_memory = profile_memory - self.with_stack = with_stack - self.with_flops = with_flops - self.json_trace_path = json_trace_path - - @master_only - def before_run(self, runner): - if self.by_epoch and runner.max_epochs < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_epochs}') - - if not self.by_epoch and runner.max_iters < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_iters}') - - if callable(self.on_trace_ready): # handler - _on_trace_ready = self.on_trace_ready - elif isinstance(self.on_trace_ready, dict): # config of handler - trace_cfg = self.on_trace_ready.copy() - trace_type = trace_cfg.pop('type') # log_trace handler - if trace_type == 'log_trace': - - def _log_handler(prof): - print(prof.key_averages().table(**trace_cfg)) - - _on_trace_ready = _log_handler - elif trace_type == 'tb_trace': # tensorboard_trace handler - try: - import torch_tb_profiler # noqa: F401 - except ImportError: - raise ImportError('please run "pip install ' - 'torch-tb-profiler" to install ' - 'torch_tb_profiler') - _on_trace_ready = torch.profiler.tensorboard_trace_handler( - **trace_cfg) - else: - raise ValueError('trace_type should be "log_trace" or ' - f'"tb_trace", but got {trace_type}') - elif self.on_trace_ready is None: - _on_trace_ready = None # type: ignore - else: - raise ValueError('on_trace_ready should be handler, dict or None, ' - f'but got {type(self.on_trace_ready)}') - - if runner.max_epochs > 1: - warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' - 'instead of 1 epoch. Since profiler will slow down ' - 'the training, it is recommended to train 1 epoch ' - 'with ProfilerHook and adjust your setting according' - ' to the profiler summary. During normal training ' - '(epoch > 1), you may disable the ProfilerHook.') - - self.profiler = torch.profiler.profile( - activities=self.activities, - schedule=self.schedule, - on_trace_ready=_on_trace_ready, - record_shapes=self.record_shapes, - profile_memory=self.profile_memory, - with_stack=self.with_stack, - with_flops=self.with_flops) - - self.profiler.__enter__() - runner.logger.info('profiler is profiling...') - - @master_only - def after_train_epoch(self, runner): - if self.by_epoch and runner.epoch == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) - - @master_only - def after_train_iter(self, runner): - self.profiler.step() - if not self.by_epoch and runner.iter == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/sampler_seed.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/sampler_seed.py deleted file mode 100755 index ee0dc6bdd..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/sampler_seed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class DistSamplerSeedHook(Hook): - """Data-loading sampler for distributed training. - - When distributed training, it is only useful in conjunction with - :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same - purpose with :obj:`IterLoader`. - """ - - def before_epoch(self, runner): - if hasattr(runner.data_loader.sampler, 'set_epoch'): - # in case the data loader uses `SequentialSampler` in Pytorch - runner.data_loader.sampler.set_epoch(runner.epoch) - elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): - # batch sampler in pytorch warps the sampler as its attributes. - runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/sync_buffer.py b/cv/detection/autoassign/pytorch/mmcv/runner/hooks/sync_buffer.py deleted file mode 100755 index 6376b7ff8..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/hooks/sync_buffer.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..dist_utils import allreduce_params -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class SyncBuffersHook(Hook): - """Synchronize model buffers such as running_mean and running_var in BN at - the end of each epoch. - - Args: - distributed (bool): Whether distributed training is used. It is - effective only for distributed training. Defaults to True. - """ - - def __init__(self, distributed=True): - self.distributed = distributed - - def after_epoch(self, runner): - """All-reduce model buffers at the end of each epoch.""" - if self.distributed: - allreduce_params(runner.model.buffers()) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/iter_based_runner.py b/cv/detection/autoassign/pytorch/mmcv/runner/iter_based_runner.py deleted file mode 100755 index 9892b07a4..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/iter_based_runner.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch -from torch.optim import Optimizer - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .hooks import IterTimerHook -from .utils import get_host_info - - -class IterLoader: - - def __init__(self, dataloader): - self._dataloader = dataloader - self.iter_loader = iter(self._dataloader) - self._epoch = 0 - - @property - def epoch(self): - return self._epoch - - def __next__(self): - try: - data = next(self.iter_loader) - except StopIteration: - self._epoch += 1 - if hasattr(self._dataloader.sampler, 'set_epoch'): - self._dataloader.sampler.set_epoch(self._epoch) - time.sleep(2) # Prevent possible deadlock during epoch transition - self.iter_loader = iter(self._dataloader) - data = next(self.iter_loader) - - return data - - def __len__(self): - return len(self._dataloader) - - -@RUNNERS.register_module() -class IterBasedRunner(BaseRunner): - """Iteration-based Runner. - - This runner train models iteration by iteration. - """ - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._epoch = data_loader.epoch - data_batch = next(data_loader) - self.call_hook('before_train_iter') - outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.train_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_train_iter') - self._inner_iter += 1 - self._iter += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - data_batch = next(data_loader) - self.call_hook('before_val_iter') - outputs = self.model.val_step(data_batch, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.val_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_val_iter') - self._inner_iter += 1 - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, iters) to specify the - running order and iterations. E.g, [('train', 10000), - ('val', 1000)] means running 10000 iterations for training and - 1000 iterations for validation, iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - """Resume model from checkpoint. - - Args: - checkpoint (str): Checkpoint to resume from. - resume_optimizer (bool, optional): Whether resume the optimizer(s) - if the checkpoint file includes optimizer(s). Default to True. - map_location (str, optional): Same as :func:`torch.load`. - Default to 'default'. - """ - if map_location == 'default': - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - self._inner_iter = checkpoint['meta']['iter'] - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') - - def save_checkpoint(self, - out_dir, - filename_tmpl='iter_{}.pth', - meta=None, - save_optimizer=True, - create_symlink=True): - """Save checkpoint to file. - - Args: - out_dir (str): Directory to save checkpoint files. - filename_tmpl (str, optional): Checkpoint file template. - Defaults to 'iter_{}.pth'. - meta (dict, optional): Metadata to be saved in checkpoint. - Defaults to None. - save_optimizer (bool, optional): Whether save optimizer. - Defaults to True. - create_symlink (bool, optional): Whether create symlink to the - latest checkpoint file. Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.iter + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - custom_hooks_config=None): - """Register default hooks for iter-based training. - - Checkpoint hook, optimizer stepper hook and logger hooks will be set to - `by_epoch=False` by default. - - Default hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - if checkpoint_config is not None: - checkpoint_config.setdefault('by_epoch', False) - if lr_config is not None: - lr_config.setdefault('by_epoch', False) - if log_config is not None: - for info in log_config['hooks']: - info.setdefault('by_epoch', False) - super(IterBasedRunner, self).register_training_hooks( - lr_config=lr_config, - momentum_config=momentum_config, - optimizer_config=optimizer_config, - checkpoint_config=checkpoint_config, - log_config=log_config, - timer_config=IterTimerHook(), - custom_hooks_config=custom_hooks_config) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/log_buffer.py b/cv/detection/autoassign/pytorch/mmcv/runner/log_buffer.py deleted file mode 100755 index d949e2941..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/log_buffer.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import numpy as np - - -class LogBuffer: - - def __init__(self): - self.val_history = OrderedDict() - self.n_history = OrderedDict() - self.output = OrderedDict() - self.ready = False - - def clear(self): - self.val_history.clear() - self.n_history.clear() - self.clear_output() - - def clear_output(self): - self.output.clear() - self.ready = False - - def update(self, vars, count=1): - assert isinstance(vars, dict) - for key, var in vars.items(): - if key not in self.val_history: - self.val_history[key] = [] - self.n_history[key] = [] - self.val_history[key].append(var) - self.n_history[key].append(count) - - def average(self, n=0): - """Average latest n values or all values.""" - assert n >= 0 - for key in self.val_history: - values = np.array(self.val_history[key][-n:]) - nums = np.array(self.n_history[key][-n:]) - avg = np.sum(values * nums) / np.sum(nums) - self.output[key] = avg - self.ready = True diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/optimizer/__init__.py b/cv/detection/autoassign/pytorch/mmcv/runner/optimizer/__init__.py deleted file mode 100755 index 53c34d047..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/optimizer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, - build_optimizer_constructor) -from .default_constructor import DefaultOptimizerConstructor - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', - 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/optimizer/builder.py b/cv/detection/autoassign/pytorch/mmcv/runner/optimizer/builder.py deleted file mode 100755 index f9234eed8..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/optimizer/builder.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect - -import torch - -from ...utils import Registry, build_from_cfg - -OPTIMIZERS = Registry('optimizer') -OPTIMIZER_BUILDERS = Registry('optimizer builder') - - -def register_torch_optimizers(): - torch_optimizers = [] - for module_name in dir(torch.optim): - if module_name.startswith('__'): - continue - _optim = getattr(torch.optim, module_name) - if inspect.isclass(_optim) and issubclass(_optim, - torch.optim.Optimizer): - OPTIMIZERS.register_module()(_optim) - torch_optimizers.append(module_name) - return torch_optimizers - - -TORCH_OPTIMIZERS = register_torch_optimizers() - - -def build_optimizer_constructor(cfg): - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/optimizer/default_constructor.py b/cv/detection/autoassign/pytorch/mmcv/runner/optimizer/default_constructor.py deleted file mode 100755 index e5f5db271..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/optimizer/default_constructor.py +++ /dev/null @@ -1,247 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -from torch.nn import GroupNorm, LayerNorm - -from mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of -from mmcv.utils.ext_loader import check_ops_exist -from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS - - -@OPTIMIZER_BUILDERS.register_module() -class DefaultOptimizerConstructor: - """Default constructor for optimizers. - - By default each parameter share the same optimizer settings, and we - provide an argument ``paramwise_cfg`` to specify parameter-wise settings. - It is a dict and may contain the following fields: - - - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If - one of the keys in ``custom_keys`` is a substring of the name of one - parameter, then the setting of the parameter will be specified by - ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will - be ignored. It should be noted that the aforementioned ``key`` is the - longest key that is a substring of the name of the parameter. If there - are multiple matched keys with the same length, then the key with lower - alphabet order will be chosen. - ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` - and ``decay_mult``. See Example 2 below. - - ``bias_lr_mult`` (float): It will be multiplied to the learning - rate for all bias parameters (except for those in normalization - layers and offset layers of DCN). - - ``bias_decay_mult`` (float): It will be multiplied to the weight - decay for all bias parameters (except for those in - normalization layers, depthwise conv layers, offset layers of DCN). - - ``norm_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of normalization - layers. - - ``dwconv_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of depthwise conv - layers. - - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning - rate for parameters of offset layer in the deformable convs - of a model. - - ``bypass_duplicate`` (bool): If true, the duplicate parameters - would not be added into optimizer. Default: False. - - Note: - 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will - override the effect of ``bias_lr_mult`` in the bias of offset - layer. So be careful when using both ``bias_lr_mult`` and - ``dcn_offset_lr_mult``. If you wish to apply both of them to the - offset layer in deformable convs, set ``dcn_offset_lr_mult`` - to the original ``dcn_offset_lr_mult`` * ``bias_lr_mult``. - 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will - apply it to all the DCN layers in the model. So be careful when - the model contains multiple DCN layers in places other than - backbone. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - optimizer_cfg (dict): The config dict of the optimizer. - Positional fields are - - - `type`: class name of the optimizer. - - Optional fields are - - - any arguments of the corresponding optimizer type, e.g., - lr, weight_decay, momentum, etc. - paramwise_cfg (dict, optional): Parameter-wise options. - - Example 1: - >>> model = torch.nn.modules.Conv1d(1, 1, 1) - >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, - >>> weight_decay=0.0001) - >>> paramwise_cfg = dict(norm_decay_mult=0.) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - - Example 2: - >>> # assume model have attribute model.backbone and model.cls_head - >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) - >>> paramwise_cfg = dict(custom_keys={ - '.backbone': dict(lr_mult=0.1, decay_mult=0.9)}) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - >>> # Then the `lr` and `weight_decay` for model.backbone is - >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for - >>> # model.cls_head is (0.01, 0.95). - """ - - def __init__(self, optimizer_cfg, paramwise_cfg=None): - if not isinstance(optimizer_cfg, dict): - raise TypeError('optimizer_cfg should be a dict', - f'but got {type(optimizer_cfg)}') - self.optimizer_cfg = optimizer_cfg - self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg - self.base_lr = optimizer_cfg.get('lr', None) - self.base_wd = optimizer_cfg.get('weight_decay', None) - self._validate_cfg() - - def _validate_cfg(self): - if not isinstance(self.paramwise_cfg, dict): - raise TypeError('paramwise_cfg should be None or a dict, ' - f'but got {type(self.paramwise_cfg)}') - - if 'custom_keys' in self.paramwise_cfg: - if not isinstance(self.paramwise_cfg['custom_keys'], dict): - raise TypeError( - 'If specified, custom_keys must be a dict, ' - f'but got {type(self.paramwise_cfg["custom_keys"])}') - if self.base_wd is None: - for key in self.paramwise_cfg['custom_keys']: - if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: - raise ValueError('base_wd should not be None') - - # get base lr and weight decay - # weight_decay must be explicitly specified if mult is specified - if ('bias_decay_mult' in self.paramwise_cfg - or 'norm_decay_mult' in self.paramwise_cfg - or 'dwconv_decay_mult' in self.paramwise_cfg): - if self.base_wd is None: - raise ValueError('base_wd should not be None') - - def _is_in(self, param_group, param_group_list): - assert is_list_of(param_group_list, dict) - param = set(param_group['params']) - param_set = set() - for group in param_group_list: - param_set.update(set(group['params'])) - - return not param.isdisjoint(param_set) - - def add_params(self, params, module, prefix='', is_dcn_module=None): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - prefix (str): The prefix of the module - is_dcn_module (int|float|None): If the current module is a - submodule of DCN, `is_dcn_module` will be passed to - control conv_offset layer's learning rate. Defaults to None. - """ - # get param-wise options - custom_keys = self.paramwise_cfg.get('custom_keys', {}) - # first sort with alphabet order and then sort with reversed len of str - sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) - - bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) - bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) - norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) - dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) - bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) - dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) - - # special rules for norm layers and depth-wise conv layers - is_norm = isinstance(module, - (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) - is_dwconv = ( - isinstance(module, torch.nn.Conv2d) - and module.in_channels == module.groups) - - for name, param in module.named_parameters(recurse=False): - param_group = {'params': [param]} - if not param.requires_grad: - params.append(param_group) - continue - if bypass_duplicate and self._is_in(param_group, params): - warnings.warn(f'{prefix} is duplicate. It is skipped since ' - f'bypass_duplicate={bypass_duplicate}') - continue - # if the parameter match one of the custom keys, ignore other rules - is_custom = False - for key in sorted_keys: - if key in f'{prefix}.{name}': - is_custom = True - lr_mult = custom_keys[key].get('lr_mult', 1.) - param_group['lr'] = self.base_lr * lr_mult - if self.base_wd is not None: - decay_mult = custom_keys[key].get('decay_mult', 1.) - param_group['weight_decay'] = self.base_wd * decay_mult - break - - if not is_custom: - # bias_lr_mult affects all bias parameters - # except for norm.bias dcn.conv_offset.bias - if name == 'bias' and not (is_norm or is_dcn_module): - param_group['lr'] = self.base_lr * bias_lr_mult - - if (prefix.find('conv_offset') != -1 and is_dcn_module - and isinstance(module, torch.nn.Conv2d)): - # deal with both dcn_offset's bias & weight - param_group['lr'] = self.base_lr * dcn_offset_lr_mult - - # apply weight decay policies - if self.base_wd is not None: - # norm decay - if is_norm: - param_group[ - 'weight_decay'] = self.base_wd * norm_decay_mult - # depth-wise conv - elif is_dwconv: - param_group[ - 'weight_decay'] = self.base_wd * dwconv_decay_mult - # bias lr and decay - elif name == 'bias' and not is_dcn_module: - # TODO: current bias_decay_mult will have affect on DCN - param_group[ - 'weight_decay'] = self.base_wd * bias_decay_mult - params.append(param_group) - - if check_ops_exist(): - is_dcn_module = False - else: - is_dcn_module = False - for child_name, child_mod in module.named_children(): - child_prefix = f'{prefix}.{child_name}' if prefix else child_name - self.add_params( - params, - child_mod, - prefix=child_prefix, - is_dcn_module=is_dcn_module) - - def __call__(self, model): - if hasattr(model, 'module'): - model = model.module - - optimizer_cfg = self.optimizer_cfg.copy() - # if no paramwise option is specified, just use the global setting - if not self.paramwise_cfg: - optimizer_cfg['params'] = model.parameters() - return build_from_cfg(optimizer_cfg, OPTIMIZERS) - - # set param-wise lr and weight decay recursively - params = [] - self.add_params(params, model) - optimizer_cfg['params'] = params - - return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/priority.py b/cv/detection/autoassign/pytorch/mmcv/runner/priority.py deleted file mode 100755 index 64cc4e3a0..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/priority.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from enum import Enum - - -class Priority(Enum): - """Hook priority levels. - - +--------------+------------+ - | Level | Value | - +==============+============+ - | HIGHEST | 0 | - +--------------+------------+ - | VERY_HIGH | 10 | - +--------------+------------+ - | HIGH | 30 | - +--------------+------------+ - | ABOVE_NORMAL | 40 | - +--------------+------------+ - | NORMAL | 50 | - +--------------+------------+ - | BELOW_NORMAL | 60 | - +--------------+------------+ - | LOW | 70 | - +--------------+------------+ - | VERY_LOW | 90 | - +--------------+------------+ - | LOWEST | 100 | - +--------------+------------+ - """ - - HIGHEST = 0 - VERY_HIGH = 10 - HIGH = 30 - ABOVE_NORMAL = 40 - NORMAL = 50 - BELOW_NORMAL = 60 - LOW = 70 - VERY_LOW = 90 - LOWEST = 100 - - -def get_priority(priority): - """Get priority value. - - Args: - priority (int or str or :obj:`Priority`): Priority. - - Returns: - int: The priority value. - """ - if isinstance(priority, int): - if priority < 0 or priority > 100: - raise ValueError('priority must be between 0 and 100') - return priority - elif isinstance(priority, Priority): - return priority.value - elif isinstance(priority, str): - return Priority[priority.upper()].value - else: - raise TypeError('priority must be an integer or Priority enum value') diff --git a/cv/detection/autoassign/pytorch/mmcv/runner/utils.py b/cv/detection/autoassign/pytorch/mmcv/runner/utils.py deleted file mode 100755 index 144d11e1a..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/runner/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random -import sys -import time -import warnings -from getpass import getuser -from socket import gethostname - -import numpy as np -import torch - -import mmcv - - -def get_host_info(): - """Get hostname and username. - - Return empty string if exception raised, e.g. ``getpass.getuser()`` will - lead to error in docker container - """ - host = '' - try: - host = f'{getuser()}@{gethostname()}' - except Exception as e: - warnings.warn(f'Host or user not found: {str(e)}') - finally: - return host - - -def get_time_str(): - return time.strftime('%Y%m%d_%H%M%S', time.localtime()) - - -def obj_from_dict(info, parent=None, default_args=None): - """Initialize an object from dict. - - The dict must contain the key "type", which indicates the object type, it - can be either a string or type, such as "list" or ``list``. Remaining - fields are treated as the arguments for constructing the object. - - Args: - info (dict): Object types and arguments. - parent (:class:`module`): Module which may containing expected object - classes. - default_args (dict, optional): Default arguments for initializing the - object. - - Returns: - any type: Object built from the dict. - """ - assert isinstance(info, dict) and 'type' in info - assert isinstance(default_args, dict) or default_args is None - args = info.copy() - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if parent is not None: - obj_type = getattr(parent, obj_type) - else: - obj_type = sys.modules[obj_type] - elif not isinstance(obj_type, type): - raise TypeError('type must be a str or valid type, but ' - f'got {type(obj_type)}') - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - return obj_type(**args) - - -def set_random_seed(seed, deterministic=False, use_rank_shift=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - rank_shift (bool): Whether to add rank number to the random seed to - have different random seed in different threads. Default: False. - """ - if use_rank_shift: - rank, _ = mmcv.runner.get_dist_info() - seed += rank - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/__init__.py b/cv/detection/autoassign/pytorch/mmcv/utils/__init__.py deleted file mode 100755 index 378a00684..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/__init__.py +++ /dev/null @@ -1,69 +0,0 @@ -# flake8: noqa -# Copyright (c) OpenMMLab. All rights reserved. -from .config import Config, ConfigDict, DictAction -from .misc import (check_prerequisites, concat_list, deprecated_api_warning, - has_method, import_modules_from_strings, is_list_of, - is_method_overridden, is_seq_of, is_str, is_tuple_of, - iter_cast, list_cast, requires_executable, requires_package, - slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, - to_ntuple, tuple_cast) -from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, - scandir, symlink) -from .progressbar import (ProgressBar, track_iter_progress, - track_parallel_progress, track_progress) -from .testing import (assert_attrs_equal, assert_dict_contains_subset, - assert_dict_has_keys, assert_is_norm_layer, - assert_keys_equal, assert_params_all_zeros, - check_python_script) -from .timer import Timer, TimerError, check_time -from .version_utils import digit_version, get_git_hash - -try: - import torch -except ImportError: - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', - 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', - 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', - 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', - 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', - 'track_progress', 'track_iter_progress', 'track_parallel_progress', - 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', - 'digit_version', 'get_git_hash', 'import_modules_from_strings', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', - 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', - 'is_method_overridden', 'has_method' - ] -else: - from .env import collect_env - from .logging import get_logger, print_log - from .parrots_jit import jit, skip_no_elena - from .parrots_wrapper import ( - TORCH_VERSION, BuildExtension, CppExtension, CUDAExtension, DataLoader, - PoolDataLoader, SyncBatchNorm, _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, - _AvgPoolNd, _BatchNorm, _ConvNd, _ConvTransposeMixin, _InstanceNorm, - _MaxPoolNd, get_build_config, is_rocm_pytorch, _get_cuda_home) - from .registry import Registry, build_from_cfg - from .trace import is_jit_tracing - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', - 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', - 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', - 'check_prerequisites', 'requires_package', 'requires_executable', - 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', - 'symlink', 'scandir', 'ProgressBar', 'track_progress', - 'track_iter_progress', 'track_parallel_progress', 'Registry', - 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', - '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', - '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', - 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', - 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', - 'deprecated_api_warning', 'digit_version', 'get_git_hash', - 'import_modules_from_strings', 'jit', 'skip_no_elena', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', - 'assert_params_all_zeros', 'check_python_script', - 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', - '_get_cuda_home', 'has_method' - ] diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/config.py b/cv/detection/autoassign/pytorch/mmcv/utils/config.py deleted file mode 100755 index c71377c07..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/config.py +++ /dev/null @@ -1,688 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import ast -import copy -import os -import os.path as osp -import platform -import shutil -import sys -import tempfile -import uuid -import warnings -from argparse import Action, ArgumentParser -from collections import abc -from importlib import import_module - -from addict import Dict -from yapf.yapflib.yapf_api import FormatCode - -from .misc import import_modules_from_strings -from .path import check_file_exist - -if platform.system() == 'Windows': - import regex as re -else: - import re - -BASE_KEY = '_base_' -DELETE_KEY = '_delete_' -DEPRECATION_KEY = '_deprecation_' -RESERVED_KEYS = ['filename', 'text', 'pretty_text'] - - -class ConfigDict(Dict): - - def __missing__(self, name): - raise KeyError(name) - - def __getattr__(self, name): - try: - value = super(ConfigDict, self).__getattr__(name) - except KeyError: - ex = AttributeError(f"'{self.__class__.__name__}' object has no " - f"attribute '{name}'") - except Exception as e: - ex = e - else: - return value - raise ex - - -def add_args(parser, cfg, prefix=''): - for k, v in cfg.items(): - if isinstance(v, str): - parser.add_argument('--' + prefix + k) - elif isinstance(v, int): - parser.add_argument('--' + prefix + k, type=int) - elif isinstance(v, float): - parser.add_argument('--' + prefix + k, type=float) - elif isinstance(v, bool): - parser.add_argument('--' + prefix + k, action='store_true') - elif isinstance(v, dict): - add_args(parser, v, prefix + k + '.') - elif isinstance(v, abc.Iterable): - parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') - else: - print(f'cannot parse key {prefix + k} of type {type(v)}') - return parser - - -class Config: - """A facility for config and config files. - - It supports common file formats as configs: python/json/yaml. The interface - is the same as a dict object and also allows access config values as - attributes. - - Example: - >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - >>> cfg.a - 1 - >>> cfg.b - {'b1': [0, 1]} - >>> cfg.b.b1 - [0, 1] - >>> cfg = Config.fromfile('tests/data/config/a.py') - >>> cfg.filename - "/home/kchen/projects/mmcv/tests/data/config/a.py" - >>> cfg.item4 - 'test' - >>> cfg - "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " - "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" - """ - - @staticmethod - def _validate_py_syntax(filename): - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - content = f.read() - try: - ast.parse(content) - except SyntaxError as e: - raise SyntaxError('There are syntax errors in config ' - f'file {filename}: {e}') - - @staticmethod - def _substitute_predefined_vars(filename, temp_config_name): - file_dirname = osp.dirname(filename) - file_basename = osp.basename(filename) - file_basename_no_extension = osp.splitext(file_basename)[0] - file_extname = osp.splitext(filename)[1] - support_templates = dict( - fileDirname=file_dirname, - fileBasename=file_basename, - fileBasenameNoExtension=file_basename_no_extension, - fileExtname=file_extname) - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - for key, value in support_templates.items(): - regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' - value = value.replace('\\', '/') - config_file = re.sub(regexp, value, config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - - @staticmethod - def _pre_substitute_base_vars(filename, temp_config_name): - """Substitute base variable placehoders to string, so that parsing - would work.""" - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - base_var_dict = {} - regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' - base_vars = set(re.findall(regexp, config_file)) - for base_var in base_vars: - randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' - base_var_dict[randstr] = base_var - regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' - config_file = re.sub(regexp, f'"{randstr}"', config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - return base_var_dict - - @staticmethod - def _substitute_base_vars(cfg, base_var_dict, base_cfg): - """Substitute variable strings to their actual values.""" - cfg = copy.deepcopy(cfg) - - if isinstance(cfg, dict): - for k, v in cfg.items(): - if isinstance(v, str) and v in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[v].split('.'): - new_v = new_v[new_k] - cfg[k] = new_v - elif isinstance(v, (list, tuple, dict)): - cfg[k] = Config._substitute_base_vars( - v, base_var_dict, base_cfg) - elif isinstance(cfg, tuple): - cfg = tuple( - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg) - elif isinstance(cfg, list): - cfg = [ - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg - ] - elif isinstance(cfg, str) and cfg in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[cfg].split('.'): - new_v = new_v[new_k] - cfg = new_v - - return cfg - - @staticmethod - def _file2dict(filename, use_predefined_variables=True): - filename = osp.abspath(osp.expanduser(filename)) - check_file_exist(filename) - fileExtname = osp.splitext(filename)[1] - if fileExtname not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - - with tempfile.TemporaryDirectory() as temp_config_dir: - temp_config_file = tempfile.NamedTemporaryFile( - dir=temp_config_dir, suffix=fileExtname) - if platform.system() == 'Windows': - temp_config_file.close() - temp_config_name = osp.basename(temp_config_file.name) - # Substitute predefined variables - if use_predefined_variables: - Config._substitute_predefined_vars(filename, - temp_config_file.name) - else: - shutil.copyfile(filename, temp_config_file.name) - # Substitute base variables from placeholders to strings - base_var_dict = Config._pre_substitute_base_vars( - temp_config_file.name, temp_config_file.name) - - if filename.endswith('.py'): - temp_module_name = osp.splitext(temp_config_name)[0] - sys.path.insert(0, temp_config_dir) - Config._validate_py_syntax(filename) - mod = import_module(temp_module_name) - sys.path.pop(0) - cfg_dict = { - name: value - for name, value in mod.__dict__.items() - if not name.startswith('__') - } - # delete imported module - del sys.modules[temp_module_name] - elif filename.endswith(('.yml', '.yaml', '.json')): - import mmcv - cfg_dict = mmcv.load(temp_config_file.name) - # close temp file - temp_config_file.close() - - # check deprecation information - if DEPRECATION_KEY in cfg_dict: - deprecation_info = cfg_dict.pop(DEPRECATION_KEY) - warning_msg = f'The config file {filename} will be deprecated ' \ - 'in the future.' - if 'expected' in deprecation_info: - warning_msg += f' Please use {deprecation_info["expected"]} ' \ - 'instead.' - if 'reference' in deprecation_info: - warning_msg += ' More information can be found at ' \ - f'{deprecation_info["reference"]}' - warnings.warn(warning_msg) - - cfg_text = filename + '\n' - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - cfg_text += f.read() - - if BASE_KEY in cfg_dict: - cfg_dir = osp.dirname(filename) - base_filename = cfg_dict.pop(BASE_KEY) - base_filename = base_filename if isinstance( - base_filename, list) else [base_filename] - - cfg_dict_list = list() - cfg_text_list = list() - for f in base_filename: - _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) - cfg_dict_list.append(_cfg_dict) - cfg_text_list.append(_cfg_text) - - base_cfg_dict = dict() - for c in cfg_dict_list: - duplicate_keys = base_cfg_dict.keys() & c.keys() - if len(duplicate_keys) > 0: - raise KeyError('Duplicate key is not allowed among bases. ' - f'Duplicate keys: {duplicate_keys}') - base_cfg_dict.update(c) - - # Substitute base variables from strings to their actual values - cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, - base_cfg_dict) - - base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) - cfg_dict = base_cfg_dict - - # merge cfg_text - cfg_text_list.append(cfg_text) - cfg_text = '\n'.join(cfg_text_list) - - return cfg_dict, cfg_text - - @staticmethod - def _merge_a_into_b(a, b, allow_list_keys=False): - """merge dict ``a`` into dict ``b`` (non-inplace). - - Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid - in-place modifications. - - Args: - a (dict): The source dict to be merged into ``b``. - b (dict): The origin dict to be fetch keys from ``a``. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in source ``a`` and will replace the element of the - corresponding index in b if b is a list. Default: False. - - Returns: - dict: The modified dict of ``b`` using ``a``. - - Examples: - # Normally merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # Delete b first and merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # b is a list - >>> Config._merge_a_into_b( - ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) - [{'a': 2}, {'b': 2}] - """ - b = b.copy() - for k, v in a.items(): - if allow_list_keys and k.isdigit() and isinstance(b, list): - k = int(k) - if len(b) <= k: - raise KeyError(f'Index {k} exceeds the length of list {b}') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - elif isinstance(v, - dict) and k in b and not v.pop(DELETE_KEY, False): - allowed_types = (dict, list) if allow_list_keys else dict - if not isinstance(b[k], allowed_types): - raise TypeError( - f'{k}={v} in child config cannot inherit from base ' - f'because {k} is a dict in the child config but is of ' - f'type {type(b[k])} in base config. You may set ' - f'`{DELETE_KEY}=True` to ignore the base config') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - else: - b[k] = v - return b - - @staticmethod - def fromfile(filename, - use_predefined_variables=True, - import_custom_modules=True): - cfg_dict, cfg_text = Config._file2dict(filename, - use_predefined_variables) - if import_custom_modules and cfg_dict.get('custom_imports', None): - import_modules_from_strings(**cfg_dict['custom_imports']) - return Config(cfg_dict, cfg_text=cfg_text, filename=filename) - - @staticmethod - def fromstring(cfg_str, file_format): - """Generate config from config str. - - Args: - cfg_str (str): Config str. - file_format (str): Config file format corresponding to the - config str. Only py/yml/yaml/json type are supported now! - - Returns: - obj:`Config`: Config obj. - """ - if file_format not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - if file_format != '.py' and 'dict(' in cfg_str: - # check if users specify a wrong suffix for python - warnings.warn( - 'Please check "file_format", the file format may be .py') - with tempfile.NamedTemporaryFile( - 'w', encoding='utf-8', suffix=file_format, - delete=False) as temp_file: - temp_file.write(cfg_str) - # on windows, previous implementation cause error - # see PR 1077 for details - cfg = Config.fromfile(temp_file.name) - os.remove(temp_file.name) - return cfg - - @staticmethod - def auto_argparser(description=None): - """Generate argparser from config file automatically (experimental)""" - partial_parser = ArgumentParser(description=description) - partial_parser.add_argument('config', help='config file path') - cfg_file = partial_parser.parse_known_args()[0].config - cfg = Config.fromfile(cfg_file) - parser = ArgumentParser(description=description) - parser.add_argument('config', help='config file path') - add_args(parser, cfg) - return parser, cfg - - def __init__(self, cfg_dict=None, cfg_text=None, filename=None): - if cfg_dict is None: - cfg_dict = dict() - elif not isinstance(cfg_dict, dict): - raise TypeError('cfg_dict must be a dict, but ' - f'got {type(cfg_dict)}') - for key in cfg_dict: - if key in RESERVED_KEYS: - raise KeyError(f'{key} is reserved for config file') - - super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) - super(Config, self).__setattr__('_filename', filename) - if cfg_text: - text = cfg_text - elif filename: - with open(filename, 'r') as f: - text = f.read() - else: - text = '' - super(Config, self).__setattr__('_text', text) - - @property - def filename(self): - return self._filename - - @property - def text(self): - return self._text - - @property - def pretty_text(self): - - indent = 4 - - def _indent(s_, num_spaces): - s = s_.split('\n') - if len(s) == 1: - return s_ - first = s.pop(0) - s = [(num_spaces * ' ') + line for line in s] - s = '\n'.join(s) - s = first + '\n' + s - return s - - def _format_basic_types(k, v, use_mapping=False): - if isinstance(v, str): - v_str = f"'{v}'" - else: - v_str = str(v) - - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) - - return attr_str - - def _format_list(k, v, use_mapping=False): - # check if all items in the list are dict - if all(isinstance(_, dict) for _ in v): - v_str = '[\n' - v_str += '\n'.join( - f'dict({_indent(_format_dict(v_), indent)}),' - for v_ in v).rstrip(',') - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) + ']' - else: - attr_str = _format_basic_types(k, v, use_mapping) - return attr_str - - def _contain_invalid_identifier(dict_str): - contain_invalid_identifier = False - for key_name in dict_str: - contain_invalid_identifier |= \ - (not str(key_name).isidentifier()) - return contain_invalid_identifier - - def _format_dict(input_dict, outest_level=False): - r = '' - s = [] - - use_mapping = _contain_invalid_identifier(input_dict) - if use_mapping: - r += '{' - for idx, (k, v) in enumerate(input_dict.items()): - is_last = idx >= len(input_dict) - 1 - end = '' if outest_level or is_last else ',' - if isinstance(v, dict): - v_str = '\n' + _format_dict(v) - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: dict({v_str}' - else: - attr_str = f'{str(k)}=dict({v_str}' - attr_str = _indent(attr_str, indent) + ')' + end - elif isinstance(v, list): - attr_str = _format_list(k, v, use_mapping) + end - else: - attr_str = _format_basic_types(k, v, use_mapping) + end - - s.append(attr_str) - r += '\n'.join(s) - if use_mapping: - r += '}' - return r - - cfg_dict = self._cfg_dict.to_dict() - text = _format_dict(cfg_dict, outest_level=True) - # copied from setup.cfg - yapf_style = dict( - based_on_style='pep8', - blank_line_before_nested_class_or_def=True, - split_before_expression_after_opening_paren=True) - text, _ = FormatCode(text, style_config=yapf_style, verify=True) - - return text - - def __repr__(self): - return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' - - def __len__(self): - return len(self._cfg_dict) - - def __getattr__(self, name): - return getattr(self._cfg_dict, name) - - def __getitem__(self, name): - return self._cfg_dict.__getitem__(name) - - def __setattr__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setattr__(name, value) - - def __setitem__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setitem__(name, value) - - def __iter__(self): - return iter(self._cfg_dict) - - def __getstate__(self): - return (self._cfg_dict, self._filename, self._text) - - def __setstate__(self, state): - _cfg_dict, _filename, _text = state - super(Config, self).__setattr__('_cfg_dict', _cfg_dict) - super(Config, self).__setattr__('_filename', _filename) - super(Config, self).__setattr__('_text', _text) - - def dump(self, file=None): - cfg_dict = super(Config, self).__getattribute__('_cfg_dict').to_dict() - if self.filename.endswith('.py'): - if file is None: - return self.pretty_text - else: - with open(file, 'w', encoding='utf-8') as f: - f.write(self.pretty_text) - else: - import mmcv - if file is None: - file_format = self.filename.split('.')[-1] - return mmcv.dump(cfg_dict, file_format=file_format) - else: - mmcv.dump(cfg_dict, file) - - def merge_from_dict(self, options, allow_list_keys=True): - """Merge list into cfg_dict. - - Merge the dict parsed by MultipleKVAction into this cfg. - - Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} - >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) - >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - - # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) - >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) - >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - - Args: - options (dict): dict of configs to merge from. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in ``options`` and will replace the element of the - corresponding index in the config if the config is a list. - Default: True. - """ - option_cfg_dict = {} - for full_key, v in options.items(): - d = option_cfg_dict - key_list = full_key.split('.') - for subkey in key_list[:-1]: - d.setdefault(subkey, ConfigDict()) - d = d[subkey] - subkey = key_list[-1] - d[subkey] = v - - cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - super(Config, self).__setattr__( - '_cfg_dict', - Config._merge_a_into_b( - option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) - - -class DictAction(Action): - """ - argparse action to split an argument into KEY=VALUE form - on the first = and append to a dictionary. List options can - be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit - brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build - list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' - """ - - @staticmethod - def _parse_int_float_bool(val): - try: - return int(val) - except ValueError: - pass - try: - return float(val) - except ValueError: - pass - if val.lower() in ['true', 'false']: - return True if val.lower() == 'true' else False - return val - - @staticmethod - def _parse_iterable(val): - """Parse iterable values in the string. - - All elements inside '()' or '[]' are treated as iterable values. - - Args: - val (str): Value string. - - Returns: - list | tuple: The expanded list or tuple from the string. - - Examples: - >>> DictAction._parse_iterable('1,2,3') - [1, 2, 3] - >>> DictAction._parse_iterable('[a, b, c]') - ['a', 'b', 'c'] - >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') - [(1, 2, 3), ['a', 'b'], 'c'] - """ - - def find_next_comma(string): - """Find the position of next comma in the string. - - If no ',' is found in the string, return the string length. All - chars inside '()' and '[]' are treated as one element and thus ',' - inside these brackets are ignored. - """ - assert (string.count('(') == string.count(')')) and ( - string.count('[') == string.count(']')), \ - f'Imbalanced brackets exist in {string}' - end = len(string) - for idx, char in enumerate(string): - pre = string[:idx] - # The string before this ',' is balanced - if ((char == ',') and (pre.count('(') == pre.count(')')) - and (pre.count('[') == pre.count(']'))): - end = idx - break - return end - - # Strip ' and " characters and replace whitespace. - val = val.strip('\'\"').replace(' ', '') - is_tuple = False - if val.startswith('(') and val.endswith(')'): - is_tuple = True - val = val[1:-1] - elif val.startswith('[') and val.endswith(']'): - val = val[1:-1] - elif ',' not in val: - # val is a single value - return DictAction._parse_int_float_bool(val) - - values = [] - while len(val) > 0: - comma_idx = find_next_comma(val) - element = DictAction._parse_iterable(val[:comma_idx]) - values.append(element) - val = val[comma_idx + 1:] - if is_tuple: - values = tuple(values) - return values - - def __call__(self, parser, namespace, values, option_string=None): - options = {} - for kv in values: - key, val = kv.split('=', maxsplit=1) - options[key] = self._parse_iterable(val) - setattr(namespace, self.dest, options) diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/env.py b/cv/detection/autoassign/pytorch/mmcv/utils/env.py deleted file mode 100755 index e46a1094f..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/env.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This file holding some environment constant for sharing by other files.""" - -import os.path as osp -import subprocess -import sys -from collections import defaultdict - -import cv2 -import torch - -import mmcv -from .parrots_wrapper import get_build_config - - -def collect_env(): - """Collect the information of the running environments. - - Returns: - dict: The environment information. The following fields are contained. - - - sys.platform: The variable of ``sys.platform``. - - Python: Python version. - - CUDA available: Bool, indicating if CUDA is available. - - GPU devices: Device type of each GPU. - - CUDA_HOME (optional): The env var ``CUDA_HOME``. - - NVCC (optional): NVCC version. - - GCC: GCC version, "n/a" if GCC is not installed. - - PyTorch: PyTorch version. - - PyTorch compiling details: The output of \ - ``torch.__config__.show()``. - - TorchVision (optional): TorchVision version. - - OpenCV: OpenCV version. - - MMCV: MMCV version. - - MMCV Compiler: The GCC version for compiling MMCV ops. - - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. - """ - env_info = {} - env_info['sys.platform'] = sys.platform - env_info['Python'] = sys.version.replace('\n', '') - - cuda_available = torch.cuda.is_available() - env_info['CUDA available'] = cuda_available - - if cuda_available: - devices = defaultdict(list) - for k in range(torch.cuda.device_count()): - devices[torch.cuda.get_device_name(k)].append(str(k)) - for name, device_ids in devices.items(): - env_info['GPU ' + ','.join(device_ids)] = name - - from mmcv.utils.parrots_wrapper import _get_cuda_home - CUDA_HOME = _get_cuda_home() - env_info['CUDA_HOME'] = CUDA_HOME - - if CUDA_HOME is not None and osp.isdir(CUDA_HOME): - try: - nvcc = osp.join(CUDA_HOME, 'bin/nvcc') - nvcc = subprocess.check_output( - f'"{nvcc}" -V | tail -n1', shell=True) - nvcc = nvcc.decode('utf-8').strip() - except subprocess.SubprocessError: - nvcc = 'Not Available' - env_info['NVCC'] = nvcc - - try: - gcc = subprocess.check_output('gcc --version | head -n1', shell=True) - gcc = gcc.decode('utf-8').strip() - env_info['GCC'] = gcc - except subprocess.CalledProcessError: # gcc is unavailable - env_info['GCC'] = 'n/a' - - env_info['PyTorch'] = torch.__version__ - env_info['PyTorch compiling details'] = get_build_config() - - try: - import torchvision - env_info['TorchVision'] = torchvision.__version__ - except ModuleNotFoundError: - pass - - env_info['OpenCV'] = cv2.__version__ - - env_info['MMCV'] = mmcv.__version__ - - try: - from mmcv.ops import get_compiler_version, get_compiling_cuda_version - except ModuleNotFoundError: - env_info['MMCV Compiler'] = 'n/a' - env_info['MMCV CUDA Compiler'] = 'n/a' - else: - env_info['MMCV Compiler'] = get_compiler_version() - env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() - - return env_info diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/ext_loader.py b/cv/detection/autoassign/pytorch/mmcv/utils/ext_loader.py deleted file mode 100755 index 08132d2c1..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/ext_loader.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os -import pkgutil -import warnings -from collections import namedtuple - -import torch - -if torch.__version__ != 'parrots': - - def load_ext(name, funcs): - ext = importlib.import_module('mmcv.' + name) - for fun in funcs: - assert hasattr(ext, fun), f'{fun} miss in module {name}' - return ext -else: - from parrots import extension - from parrots.base import ParrotsException - - has_return_value_ops = [ - 'nms', - 'softnms', - 'nms_match', - 'nms_rotated', - 'top_pool_forward', - 'top_pool_backward', - 'bottom_pool_forward', - 'bottom_pool_backward', - 'left_pool_forward', - 'left_pool_backward', - 'right_pool_forward', - 'right_pool_backward', - 'fused_bias_leakyrelu', - 'upfirdn2d', - 'ms_deform_attn_forward', - 'pixel_group', - 'contour_expand', - ] - - def get_fake_func(name, e): - - def fake_func(*args, **kwargs): - warnings.warn(f'{name} is not supported in parrots now') - raise e - - return fake_func - - def load_ext(name, funcs): - ExtModule = namedtuple('ExtModule', funcs) - ext_list = [] - lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - for fun in funcs: - try: - ext_fun = extension.load(fun, name, lib_dir=lib_root) - except ParrotsException as e: - if 'No element registered' not in e.message: - warnings.warn(e.message) - ext_fun = get_fake_func(fun, e) - ext_list.append(ext_fun) - else: - if fun in has_return_value_ops: - ext_list.append(ext_fun.op) - else: - ext_list.append(ext_fun.op_) - return ExtModule(*ext_list) - - -def check_ops_exist(): - ext_loader = pkgutil.find_loader('mmcv._ext') - return ext_loader is not None diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/logging.py b/cv/detection/autoassign/pytorch/mmcv/utils/logging.py deleted file mode 100755 index 4aa0e04bb..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/logging.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.distributed as dist - -logger_initialized = {} - - -def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): - """Initialize and get a logger by name. - - If the logger has not been initialized, this method will initialize the - logger by adding one or two handlers, otherwise the initialized logger will - be directly returned. During initialization, a StreamHandler will always be - added. If `log_file` is specified and the process rank is 0, a FileHandler - will also be added. - - Args: - name (str): Logger name. - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the logger. - log_level (int): The logger level. Note that only the process of - rank 0 is affected, and other processes will set the level to - "Error" thus be silent most of the time. - file_mode (str): The file mode used in opening log file. - Defaults to 'w'. - - Returns: - logging.Logger: The expected logger. - """ - logger = logging.getLogger(name) - if name in logger_initialized: - return logger - # handle hierarchical names - # e.g., logger "a" is initialized, then logger "a.b" will skip the - # initialization since it is a child of "a". - for logger_name in logger_initialized: - if name.startswith(logger_name): - return logger - - # handle duplicate logs to the console - # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) - # to the root logger. As logger.propagate is True by default, this root - # level handler causes logging messages from rank>0 processes to - # unexpectedly show up on the console, creating much unwanted clutter. - # To fix this issue, we set the root logger's StreamHandler, if any, to log - # at the ERROR level. - for handler in logger.root.handlers: - if type(handler) is logging.StreamHandler: - handler.setLevel(logging.ERROR) - - stream_handler = logging.StreamHandler() - handlers = [stream_handler] - - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - else: - rank = 0 - - # only rank 0 will add a FileHandler - if rank == 0 and log_file is not None: - # Here, the default behaviour of the official logger is 'a'. Thus, we - # provide an interface to change the file mode to the default - # behaviour. - file_handler = logging.FileHandler(log_file, file_mode) - handlers.append(file_handler) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - for handler in handlers: - handler.setFormatter(formatter) - handler.setLevel(log_level) - logger.addHandler(handler) - - if rank == 0: - logger.setLevel(log_level) - else: - logger.setLevel(logging.ERROR) - - logger_initialized[name] = True - - return logger - - -def print_log(msg, logger=None, level=logging.INFO): - """Print a log message. - - Args: - msg (str): The message to be logged. - logger (logging.Logger | str | None): The logger to be used. - Some special loggers are: - - "silent": no message will be printed. - - other str: the logger obtained with `get_root_logger(logger)`. - - None: The `print()` method will be used to print log messages. - level (int): Logging level. Only available when `logger` is a Logger - object or "root". - """ - if logger is None: - print(msg) - elif isinstance(logger, logging.Logger): - logger.log(level, msg) - elif logger == 'silent': - pass - elif isinstance(logger, str): - _logger = get_logger(logger) - _logger.log(level, msg) - else: - raise TypeError( - 'logger should be either a logging.Logger object, str, ' - f'"silent" or None, but got {type(logger)}') diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/misc.py b/cv/detection/autoassign/pytorch/mmcv/utils/misc.py deleted file mode 100755 index 2c58d0d7f..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/misc.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections.abc -import functools -import itertools -import subprocess -import warnings -from collections import abc -from importlib import import_module -from inspect import getfullargspec -from itertools import repeat - - -# From PyTorch internals -def _ntuple(n): - - def parse(x): - if isinstance(x, collections.abc.Iterable): - return x - return tuple(repeat(x, n)) - - return parse - - -to_1tuple = _ntuple(1) -to_2tuple = _ntuple(2) -to_3tuple = _ntuple(3) -to_4tuple = _ntuple(4) -to_ntuple = _ntuple - - -def is_str(x): - """Whether the input is an string instance. - - Note: This method is deprecated since python 2 is no longer supported. - """ - return isinstance(x, str) - - -def import_modules_from_strings(imports, allow_failed_imports=False): - """Import modules from the given list of strings. - - Args: - imports (list | str | None): The given module names to be imported. - allow_failed_imports (bool): If True, the failed imports will return - None. Otherwise, an ImportError is raise. Default: False. - - Returns: - list[module] | module | None: The imported modules. - - Examples: - >>> osp, sys = import_modules_from_strings( - ... ['os.path', 'sys']) - >>> import os.path as osp_ - >>> import sys as sys_ - >>> assert osp == osp_ - >>> assert sys == sys_ - """ - if not imports: - return - single_import = False - if isinstance(imports, str): - single_import = True - imports = [imports] - if not isinstance(imports, list): - raise TypeError( - f'custom_imports must be a list but got type {type(imports)}') - imported = [] - for imp in imports: - if not isinstance(imp, str): - raise TypeError( - f'{imp} is of type {type(imp)} and cannot be imported.') - try: - imported_tmp = import_module(imp) - except ImportError: - if allow_failed_imports: - warnings.warn(f'{imp} failed to import and is ignored.', - UserWarning) - imported_tmp = None - else: - raise ImportError - imported.append(imported_tmp) - if single_import: - imported = imported[0] - return imported - - -def iter_cast(inputs, dst_type, return_type=None): - """Cast elements of an iterable object into some type. - - Args: - inputs (Iterable): The input object. - dst_type (type): Destination type. - return_type (type, optional): If specified, the output object will be - converted to this type, otherwise an iterator. - - Returns: - iterator or specified type: The converted object. - """ - if not isinstance(inputs, abc.Iterable): - raise TypeError('inputs must be an iterable object') - if not isinstance(dst_type, type): - raise TypeError('"dst_type" must be a valid type') - - out_iterable = map(dst_type, inputs) - - if return_type is None: - return out_iterable - else: - return return_type(out_iterable) - - -def list_cast(inputs, dst_type): - """Cast elements of an iterable object into a list of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=list) - - -def tuple_cast(inputs, dst_type): - """Cast elements of an iterable object into a tuple of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=tuple) - - -def is_seq_of(seq, expected_type, seq_type=None): - """Check whether it is a sequence of some type. - - Args: - seq (Sequence): The sequence to be checked. - expected_type (type): Expected type of sequence items. - seq_type (type, optional): Expected sequence type. - - Returns: - bool: Whether the sequence is valid. - """ - if seq_type is None: - exp_seq_type = abc.Sequence - else: - assert isinstance(seq_type, type) - exp_seq_type = seq_type - if not isinstance(seq, exp_seq_type): - return False - for item in seq: - if not isinstance(item, expected_type): - return False - return True - - -def is_list_of(seq, expected_type): - """Check whether it is a list of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=list) - - -def is_tuple_of(seq, expected_type): - """Check whether it is a tuple of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=tuple) - - -def slice_list(in_list, lens): - """Slice a list into several sub lists by a list of given length. - - Args: - in_list (list): The list to be sliced. - lens(int or list): The expected length of each out list. - - Returns: - list: A list of sliced list. - """ - if isinstance(lens, int): - assert len(in_list) % lens == 0 - lens = [lens] * int(len(in_list) / lens) - if not isinstance(lens, list): - raise TypeError('"indices" must be an integer or a list of integers') - elif sum(lens) != len(in_list): - raise ValueError('sum of lens and list length does not ' - f'match: {sum(lens)} != {len(in_list)}') - out_list = [] - idx = 0 - for i in range(len(lens)): - out_list.append(in_list[idx:idx + lens[i]]) - idx += lens[i] - return out_list - - -def concat_list(in_list): - """Concatenate a list of list into a single list. - - Args: - in_list (list): The list of list to be merged. - - Returns: - list: The concatenated flat list. - """ - return list(itertools.chain(*in_list)) - - -def check_prerequisites( - prerequisites, - checker, - msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' - 'found, please install them first.'): # yapf: disable - """A decorator factory to check if prerequisites are satisfied. - - Args: - prerequisites (str of list[str]): Prerequisites to be checked. - checker (callable): The checker method that returns True if a - prerequisite is meet, False otherwise. - msg_tmpl (str): The message template with two variables. - - Returns: - decorator: A specific decorator. - """ - - def wrap(func): - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - requirements = [prerequisites] if isinstance( - prerequisites, str) else prerequisites - missing = [] - for item in requirements: - if not checker(item): - missing.append(item) - if missing: - print(msg_tmpl.format(', '.join(missing), func.__name__)) - raise RuntimeError('Prerequisites not meet.') - else: - return func(*args, **kwargs) - - return wrapped_func - - return wrap - - -def _check_py_package(package): - try: - import_module(package) - except ImportError: - return False - else: - return True - - -def _check_executable(cmd): - if subprocess.call(f'which {cmd}', shell=True) != 0: - return False - else: - return True - - -def requires_package(prerequisites): - """A decorator to check if some python packages are installed. - - Example: - >>> @requires_package('numpy') - >>> func(arg1, args): - >>> return numpy.zeros(1) - array([0.]) - >>> @requires_package(['numpy', 'non_package']) - >>> func(arg1, args): - >>> return numpy.zeros(1) - ImportError - """ - return check_prerequisites(prerequisites, checker=_check_py_package) - - -def requires_executable(prerequisites): - """A decorator to check if some executable files are installed. - - Example: - >>> @requires_executable('ffmpeg') - >>> func(arg1, args): - >>> print(1) - 1 - """ - return check_prerequisites(prerequisites, checker=_check_executable) - - -def deprecated_api_warning(name_dict, cls_name=None): - """A decorator to check if some arguments are deprecate and try to replace - deprecate src_arg_name to dst_arg_name. - - Args: - name_dict(dict): - key (str): Deprecate argument names. - val (str): Expected argument names. - - Returns: - func: New function. - """ - - def api_warning_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get name of the function - func_name = old_func.__name__ - if cls_name is not None: - func_name = f'{cls_name}.{func_name}' - if args: - arg_names = args_info.args[:len(args)] - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in arg_names: - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead') - arg_names[arg_names.index(src_arg_name)] = dst_arg_name - if kwargs: - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in kwargs: - - assert dst_arg_name not in kwargs, ( - f'The expected behavior is to replace ' - f'the deprecated key `{src_arg_name}` to ' - f'new key `{dst_arg_name}`, but got them ' - f'in the arguments at the same time, which ' - f'is confusing. `{src_arg_name} will be ' - f'deprecated in the future, please ' - f'use `{dst_arg_name}` instead.') - - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead') - kwargs[dst_arg_name] = kwargs.pop(src_arg_name) - - # apply converted arguments to the decorated method - output = old_func(*args, **kwargs) - return output - - return new_func - - return api_warning_wrapper - - -def is_method_overridden(method, base_class, derived_class): - """Check if a method of base class is overridden in derived class. - - Args: - method (str): the method name to check. - base_class (type): the class of the base class. - derived_class (type | Any): the class or instance of the derived class. - """ - assert isinstance(base_class, type), \ - "base_class doesn't accept instance, Please pass class instead." - - if not isinstance(derived_class, type): - derived_class = derived_class.__class__ - - base_method = getattr(base_class, method) - derived_method = getattr(derived_class, method) - return derived_method != base_method - - -def has_method(obj: object, method: str) -> bool: - """Check whether the object has a method. - - Args: - method (str): The method name to check. - obj (object): The object to check. - - Returns: - bool: True if the object has the method else False. - """ - return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/parrots_jit.py b/cv/detection/autoassign/pytorch/mmcv/utils/parrots_jit.py deleted file mode 100755 index 61873f6db..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/parrots_jit.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -from .parrots_wrapper import TORCH_VERSION - -parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') - -if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': - from parrots.jit import pat as jit -else: - - def jit(func=None, - check_input=None, - full_shape=True, - derivate=False, - coderize=False, - optimize=False): - - def wrapper(func): - - def wrapper_inner(*args, **kargs): - return func(*args, **kargs) - - return wrapper_inner - - if func is None: - return wrapper - else: - return func - - -if TORCH_VERSION == 'parrots': - from parrots.utils.tester import skip_no_elena -else: - - def skip_no_elena(func): - - def wrapper(*args, **kargs): - return func(*args, **kargs) - - return wrapper diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/parrots_wrapper.py b/cv/detection/autoassign/pytorch/mmcv/utils/parrots_wrapper.py deleted file mode 100755 index 93c97640d..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/parrots_wrapper.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch - -TORCH_VERSION = torch.__version__ - - -def is_rocm_pytorch() -> bool: - is_rocm = False - if TORCH_VERSION != 'parrots': - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - return is_rocm - - -def _get_cuda_home(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import CUDA_HOME - else: - if is_rocm_pytorch(): - from torch.utils.cpp_extension import ROCM_HOME - CUDA_HOME = ROCM_HOME - else: - from torch.utils.cpp_extension import CUDA_HOME - return CUDA_HOME - - -def get_build_config(): - if TORCH_VERSION == 'parrots': - from parrots.config import get_build_info - return get_build_info() - else: - return torch.__config__.show() - - -def _get_conv(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin - else: - from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin - return _ConvNd, _ConvTransposeMixin - - -def _get_dataloader(): - if TORCH_VERSION == 'parrots': - from torch.utils.data import DataLoader, PoolDataLoader - else: - from torch.utils.data import DataLoader - PoolDataLoader = DataLoader - return DataLoader, PoolDataLoader - - -def _get_extension(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import BuildExtension, Extension - CppExtension = partial(Extension, cuda=False) - CUDAExtension = partial(Extension, cuda=True) - else: - from torch.utils.cpp_extension import (BuildExtension, CppExtension, - CUDAExtension) - return BuildExtension, CppExtension, CUDAExtension - - -def _get_pool(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - else: - from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd - - -def _get_norm(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm2d - else: - from torch.nn.modules.instancenorm import _InstanceNorm - from torch.nn.modules.batchnorm import _BatchNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm - return _BatchNorm, _InstanceNorm, SyncBatchNorm_ - - -_ConvNd, _ConvTransposeMixin = _get_conv() -DataLoader, PoolDataLoader = _get_dataloader() -BuildExtension, CppExtension, CUDAExtension = _get_extension() -_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() -_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() - - -class SyncBatchNorm(SyncBatchNorm_): - - def _check_input_dim(self, input): - if TORCH_VERSION == 'parrots': - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input (got {input.dim()}D input)') - else: - super()._check_input_dim(input) diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/path.py b/cv/detection/autoassign/pytorch/mmcv/utils/path.py deleted file mode 100755 index 7dab4b304..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/path.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -from pathlib import Path - -from .misc import is_str - - -def is_filepath(x): - return is_str(x) or isinstance(x, Path) - - -def fopen(filepath, *args, **kwargs): - if is_str(filepath): - return open(filepath, *args, **kwargs) - elif isinstance(filepath, Path): - return filepath.open(*args, **kwargs) - raise ValueError('`filepath` should be a string or a Path') - - -def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): - if not osp.isfile(filename): - raise FileNotFoundError(msg_tmpl.format(filename)) - - -def mkdir_or_exist(dir_name, mode=0o777): - if dir_name == '': - return - dir_name = osp.expanduser(dir_name) - os.makedirs(dir_name, mode=mode, exist_ok=True) - - -def symlink(src, dst, overwrite=True, **kwargs): - if os.path.lexists(dst) and overwrite: - os.remove(dst) - os.symlink(src, dst, **kwargs) - - -def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): - """Scan a directory to find the interested files. - - Args: - dir_path (str | obj:`Path`): Path of the directory. - suffix (str | tuple(str), optional): File suffix that we are - interested in. Default: None. - recursive (bool, optional): If set to True, recursively scan the - directory. Default: False. - case_sensitive (bool, optional) : If set to False, ignore the case of - suffix. Default: True. - - Returns: - A generator for all the interested files with relative paths. - """ - if isinstance(dir_path, (str, Path)): - dir_path = str(dir_path) - else: - raise TypeError('"dir_path" must be a string or Path object') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('"suffix" must be a string or tuple of strings') - - if suffix is not None and not case_sensitive: - suffix = suffix.lower() if isinstance(suffix, str) else tuple( - item.lower() for item in suffix) - - root = dir_path - - def _scandir(dir_path, suffix, recursive, case_sensitive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - _rel_path = rel_path if case_sensitive else rel_path.lower() - if suffix is None or _rel_path.endswith(suffix): - yield rel_path - elif recursive and os.path.isdir(entry.path): - # scan recursively if entry.path is a directory - yield from _scandir(entry.path, suffix, recursive, - case_sensitive) - - return _scandir(dir_path, suffix, recursive, case_sensitive) - - -def find_vcs_root(path, markers=('.git', )): - """Finds the root directory (including itself) of specified markers. - - Args: - path (str): Path of directory or file. - markers (list[str], optional): List of file or directory names. - - Returns: - The directory contained one of the markers or None if not found. - """ - if osp.isfile(path): - path = osp.dirname(path) - - prev, cur = None, osp.abspath(osp.expanduser(path)) - while cur != prev: - if any(osp.exists(osp.join(cur, marker)) for marker in markers): - return cur - prev, cur = cur, osp.split(cur)[0] - return None diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/progressbar.py b/cv/detection/autoassign/pytorch/mmcv/utils/progressbar.py deleted file mode 100755 index 0062f670d..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/progressbar.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from collections.abc import Iterable -from multiprocessing import Pool -from shutil import get_terminal_size - -from .timer import Timer - - -class ProgressBar: - """A progress bar which can print the progress.""" - - def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): - self.task_num = task_num - self.bar_width = bar_width - self.completed = 0 - self.file = file - if start: - self.start() - - @property - def terminal_width(self): - width, _ = get_terminal_size() - return width - - def start(self): - if self.task_num > 0: - self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' - 'elapsed: 0s, ETA:') - else: - self.file.write('completed: 0, elapsed: 0s') - self.file.flush() - self.timer = Timer() - - def update(self, num_tasks=1): - assert num_tasks > 0 - self.completed += num_tasks - elapsed = self.timer.since_start() - if elapsed > 0: - fps = self.completed / elapsed - else: - fps = float('inf') - if self.task_num > 0: - percentage = self.completed / float(self.task_num) - eta = int(elapsed * (1 - percentage) / percentage + 0.5) - msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ - f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ - f'ETA: {eta:5}s' - - bar_width = min(self.bar_width, - int(self.terminal_width - len(msg)) + 2, - int(self.terminal_width * 0.6)) - bar_width = max(2, bar_width) - mark_width = int(bar_width * percentage) - bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) - self.file.write(msg.format(bar_chars)) - else: - self.file.write( - f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' - f' {fps:.1f} tasks/s') - self.file.flush() - - -def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): - """Track the progress of tasks execution with a progress bar. - - Tasks are done with a simple for-loop. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - results = [] - for task in tasks: - results.append(func(task, **kwargs)) - prog_bar.update() - prog_bar.file.write('\n') - return results - - -def init_pool(process_num, initializer=None, initargs=None): - if initializer is None: - return Pool(process_num) - elif initargs is None: - return Pool(process_num, initializer) - else: - if not isinstance(initargs, tuple): - raise TypeError('"initargs" must be a tuple') - return Pool(process_num, initializer, initargs) - - -def track_parallel_progress(func, - tasks, - nproc, - initializer=None, - initargs=None, - bar_width=50, - chunksize=1, - skip_first=False, - keep_order=True, - file=sys.stdout): - """Track the progress of parallel task execution with a progress bar. - - The built-in :mod:`multiprocessing` module is used for process pools and - tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - nproc (int): Process (worker) number. - initializer (None or callable): Refer to :class:`multiprocessing.Pool` - for details. - initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for - details. - chunksize (int): Refer to :class:`multiprocessing.Pool` for details. - bar_width (int): Width of progress bar. - skip_first (bool): Whether to skip the first sample for each worker - when estimating fps, since the initialization step may takes - longer. - keep_order (bool): If True, :func:`Pool.imap` is used, otherwise - :func:`Pool.imap_unordered` is used. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - pool = init_pool(nproc, initializer, initargs) - start = not skip_first - task_num -= nproc * chunksize * int(skip_first) - prog_bar = ProgressBar(task_num, bar_width, start, file=file) - results = [] - if keep_order: - gen = pool.imap(func, tasks, chunksize) - else: - gen = pool.imap_unordered(func, tasks, chunksize) - for result in gen: - results.append(result) - if skip_first: - if len(results) < nproc * chunksize: - continue - elif len(results) == nproc * chunksize: - prog_bar.start() - continue - prog_bar.update() - prog_bar.file.write('\n') - pool.close() - pool.join() - return results - - -def track_iter_progress(tasks, bar_width=50, file=sys.stdout): - """Track the progress of tasks iteration or enumeration with a progress - bar. - - Tasks are yielded with a simple for-loop. - - Args: - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Yields: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - for task in tasks: - yield task - prog_bar.update() - prog_bar.file.write('\n') diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/registry.py b/cv/detection/autoassign/pytorch/mmcv/utils/registry.py deleted file mode 100755 index fa9df39bc..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/registry.py +++ /dev/null @@ -1,315 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import warnings -from functools import partial - -from .misc import is_seq_of - - -def build_from_cfg(cfg, registry, default_args=None): - """Build a module from config dict. - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - try: - return obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{obj_cls.__name__}: {e}') - - -class Registry: - """A registry to map strings to classes. - - Registered object could be built from registry. - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = MODELS.build(dict(type='ResNet')) - - Please refer to - https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for - advanced usage. - - Args: - name (str): Registry name. - build_func(func, optional): Build function to construct instance from - Registry, func:`build_from_cfg` is used if neither ``parent`` or - ``build_func`` is specified. If ``parent`` is specified and - ``build_func`` is not given, ``build_func`` will be inherited - from ``parent``. Default: None. - parent (Registry, optional): Parent registry. The class registered in - children registry could be built from parent. Default: None. - scope (str, optional): The scope of registry. It is the key to search - for children registry. If not specified, scope will be the name of - the package where class is defined, e.g. mmdet, mmcls, mmseg. - Default: None. - """ - - def __init__(self, name, build_func=None, parent=None, scope=None): - self._name = name - self._module_dict = dict() - self._children = dict() - self._scope = self.infer_scope() if scope is None else scope - - # self.build_func will be set with the following priority: - # 1. build_func - # 2. parent.build_func - # 3. build_from_cfg - if build_func is None: - if parent is not None: - self.build_func = parent.build_func - else: - self.build_func = build_from_cfg - else: - self.build_func = build_func - if parent is not None: - assert isinstance(parent, Registry) - parent._add_children(self) - self.parent = parent - else: - self.parent = None - - def __len__(self): - return len(self._module_dict) - - def __contains__(self, key): - return self.get(key) is not None - - def __repr__(self): - format_str = self.__class__.__name__ + \ - f'(name={self._name}, ' \ - f'items={self._module_dict})' - return format_str - - @staticmethod - def infer_scope(): - """Infer the scope of registry. - - The name of the package where registry is defined will be returned. - - Example: - # in mmdet/models/backbone/resnet.py - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - The scope of ``ResNet`` will be ``mmdet``. - - - Returns: - scope (str): The inferred scope name. - """ - # inspect.stack() trace where this function is called, the index-2 - # indicates the frame where `infer_scope()` is called - filename = inspect.getmodule(inspect.stack()[2][0]).__name__ - split_filename = filename.split('.') - return split_filename[0] - - @staticmethod - def split_scope_key(key): - """Split scope and key. - - The first scope will be split from key. - - Examples: - >>> Registry.split_scope_key('mmdet.ResNet') - 'mmdet', 'ResNet' - >>> Registry.split_scope_key('ResNet') - None, 'ResNet' - - Return: - scope (str, None): The first scope. - key (str): The remaining key. - """ - split_index = key.find('.') - if split_index != -1: - return key[:split_index], key[split_index + 1:] - else: - return None, key - - @property - def name(self): - return self._name - - @property - def scope(self): - return self._scope - - @property - def module_dict(self): - return self._module_dict - - @property - def children(self): - return self._children - - def get(self, key): - """Get the registry record. - - Args: - key (str): The class name in string format. - - Returns: - class: The corresponding class. - """ - scope, real_key = self.split_scope_key(key) - if scope is None or scope == self._scope: - # get from self - if real_key in self._module_dict: - return self._module_dict[real_key] - else: - # get from self._children - if scope in self._children: - return self._children[scope].get(real_key) - else: - # goto root - parent = self.parent - while parent.parent is not None: - parent = parent.parent - return parent.get(key) - - def build(self, *args, **kwargs): - return self.build_func(*args, **kwargs, registry=self) - - def _add_children(self, registry): - """Add children for a registry. - - The ``registry`` will be added as children based on its scope. - The parent registry could build objects from children registry. - - Example: - >>> models = Registry('models') - >>> mmdet_models = Registry('models', parent=models) - >>> @mmdet_models.register_module() - >>> class ResNet: - >>> pass - >>> resnet = models.build(dict(type='mmdet.ResNet')) - """ - - assert isinstance(registry, Registry) - assert registry.scope is not None - assert registry.scope not in self.children, \ - f'scope {registry.scope} exists in {self.name} registry' - self.children[registry.scope] = registry - - def _register_module(self, module_class, module_name=None, force=False): - if not inspect.isclass(module_class): - raise TypeError('module must be a class, ' - f'but got {type(module_class)}') - - if module_name is None: - module_name = module_class.__name__ - if isinstance(module_name, str): - module_name = [module_name] - for name in module_name: - if not force and name in self._module_dict: - raise KeyError(f'{name} is already registered ' - f'in {self.name}') - self._module_dict[name] = module_class - - def deprecated_register_module(self, cls=None, force=False): - warnings.warn( - 'The old API of register_module(module, force=False) ' - 'is deprecated and will be removed, please use the new API ' - 'register_module(name=None, force=False, module=None) instead.') - if cls is None: - return partial(self.deprecated_register_module, force=force) - self._register_module(cls, force=force) - return cls - - def register_module(self, name=None, force=False, module=None): - """Register a module. - - A record will be added to `self._module_dict`, whose key is the class - name or the specified name, and value is the class itself. - It can be used as a decorator or a normal function. - - Example: - >>> backbones = Registry('backbone') - >>> @backbones.register_module() - >>> class ResNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> @backbones.register_module(name='mnet') - >>> class MobileNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> class ResNet: - >>> pass - >>> backbones.register_module(ResNet) - - Args: - name (str | None): The module name to be registered. If not - specified, the class name will be used. - force (bool, optional): Whether to override an existing class with - the same name. Default: False. - module (type): Module class to be registered. - """ - if not isinstance(force, bool): - raise TypeError(f'force must be a boolean, but got {type(force)}') - # NOTE: This is a walkaround to be compatible with the old api, - # while it may introduce unexpected bugs. - if isinstance(name, type): - return self.deprecated_register_module(name, force=force) - - # raise the error ahead of time - if not (name is None or isinstance(name, str) or is_seq_of(name, str)): - raise TypeError( - 'name must be either of None, an instance of str or a sequence' - f' of str, but got {type(name)}') - - # use it as a normal method: x.register_module(module=SomeClass) - if module is not None: - self._register_module( - module_class=module, module_name=name, force=force) - return module - - # use it as a decorator: @x.register_module() - def _register(cls): - self._register_module( - module_class=cls, module_name=name, force=force) - return cls - - return _register diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/testing.py b/cv/detection/autoassign/pytorch/mmcv/utils/testing.py deleted file mode 100755 index a27f936da..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/testing.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) Open-MMLab. -import sys -from collections.abc import Iterable -from runpy import run_path -from shlex import split -from typing import Any, Dict, List -from unittest.mock import patch - - -def check_python_script(cmd): - """Run the python cmd script with `__main__`. The difference between - `os.system` is that, this function exectues code in the current process, so - that it can be tracked by coverage tools. Currently it supports two forms: - - - ./tests/data/scripts/hello.py zz - - python tests/data/scripts/hello.py zz - """ - args = split(cmd) - if args[0] == 'python': - args = args[1:] - with patch.object(sys, 'argv', args): - run_path(args[0], run_name='__main__') - - -def _any(judge_result): - """Since built-in ``any`` works only when the element of iterable is not - iterable, implement the function.""" - if not isinstance(judge_result, Iterable): - return judge_result - - try: - for element in judge_result: - if _any(element): - return True - except TypeError: - # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) - if judge_result: - return True - return False - - -def assert_dict_contains_subset(dict_obj: Dict[Any, Any], - expected_subset: Dict[Any, Any]) -> bool: - """Check if the dict_obj contains the expected_subset. - - Args: - dict_obj (Dict[Any, Any]): Dict object to be checked. - expected_subset (Dict[Any, Any]): Subset expected to be contained in - dict_obj. - - Returns: - bool: Whether the dict_obj contains the expected_subset. - """ - - for key, value in expected_subset.items(): - if key not in dict_obj.keys() or _any(dict_obj[key] != value): - return False - return True - - -def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: - """Check if attribute of class object is correct. - - Args: - obj (object): Class object to be checked. - expected_attrs (Dict[str, Any]): Dict of the expected attrs. - - Returns: - bool: Whether the attribute of class object is correct. - """ - for attr, value in expected_attrs.items(): - if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): - return False - return True - - -def assert_dict_has_keys(obj: Dict[str, Any], - expected_keys: List[str]) -> bool: - """Check if the obj has all the expected_keys. - - Args: - obj (Dict[str, Any]): Object to be checked. - expected_keys (List[str]): Keys expected to contained in the keys of - the obj. - - Returns: - bool: Whether the obj has the expected keys. - """ - return set(expected_keys).issubset(set(obj.keys())) - - -def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: - """Check if target_keys is equal to result_keys. - - Args: - result_keys (List[str]): Result keys to be checked. - target_keys (List[str]): Target keys to be checked. - - Returns: - bool: Whether target_keys is equal to result_keys. - """ - return set(result_keys) == set(target_keys) - - -def assert_is_norm_layer(module) -> bool: - """Check if the module is a norm layer. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the module is a norm layer. - """ - from .parrots_wrapper import _BatchNorm, _InstanceNorm - from torch.nn import GroupNorm, LayerNorm - norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) - return isinstance(module, norm_layer_candidates) - - -def assert_params_all_zeros(module) -> bool: - """Check if the parameters of the module is all zeros. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the parameters of the module is all zeros. - """ - weight_data = module.weight.data - is_weight_zero = weight_data.allclose( - weight_data.new_zeros(weight_data.size())) - - if hasattr(module, 'bias') and module.bias is not None: - bias_data = module.bias.data - is_bias_zero = bias_data.allclose( - bias_data.new_zeros(bias_data.size())) - else: - is_bias_zero = True - - return is_weight_zero and is_bias_zero diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/timer.py b/cv/detection/autoassign/pytorch/mmcv/utils/timer.py deleted file mode 100755 index 66d4a78a8..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/timer.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from time import time - - -class TimerError(Exception): - - def __init__(self, message): - self.message = message - super(TimerError, self).__init__(message) - - -class Timer: - """A flexible Timer class. - - :Example: - - >>> import time - >>> import mmcv - >>> with mmcv.Timer(): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - 1.000 - >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - it takes 1.0 seconds - >>> timer = mmcv.Timer() - >>> time.sleep(0.5) - >>> print(timer.since_start()) - 0.500 - >>> time.sleep(0.5) - >>> print(timer.since_last_check()) - 0.500 - >>> print(timer.since_start()) - 1.000 - """ - - def __init__(self, start=True, print_tmpl=None): - self._is_running = False - self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' - if start: - self.start() - - @property - def is_running(self): - """bool: indicate whether the timer is running""" - return self._is_running - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - print(self.print_tmpl.format(self.since_last_check())) - self._is_running = False - - def start(self): - """Start the timer.""" - if not self._is_running: - self._t_start = time() - self._is_running = True - self._t_last = time() - - def since_start(self): - """Total time since the timer is started. - - Returns (float): Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - self._t_last = time() - return self._t_last - self._t_start - - def since_last_check(self): - """Time since the last checking. - - Either :func:`since_start` or :func:`since_last_check` is a checking - operation. - - Returns (float): Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - dur = time() - self._t_last - self._t_last = time() - return dur - - -_g_timers = {} # global timers - - -def check_time(timer_id): - """Add check points in a single line. - - This method is suitable for running a task on a list of items. A timer will - be registered when the method is called for the first time. - - :Example: - - >>> import time - >>> import mmcv - >>> for i in range(1, 6): - >>> # simulate a code block - >>> time.sleep(i) - >>> mmcv.check_time('task1') - 2.000 - 3.000 - 4.000 - 5.000 - - Args: - timer_id (str): Timer identifier. - """ - if timer_id not in _g_timers: - _g_timers[timer_id] = Timer() - return 0 - else: - return _g_timers[timer_id].since_last_check() diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/trace.py b/cv/detection/autoassign/pytorch/mmcv/utils/trace.py deleted file mode 100755 index 8e49bfd38..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/trace.py +++ /dev/null @@ -1,23 +0,0 @@ -import warnings - -import torch - -from mmcv.utils import digit_version - - -def is_jit_tracing() -> bool: - if (torch.__version__ != 'parrots' - and digit_version(torch.__version__) >= digit_version('1.6.0')): - on_trace = torch.jit.is_tracing() - # In PyTorch 1.6, torch.jit.is_tracing has a bug. - # Refers to https://github.com/pytorch/pytorch/issues/42448 - if isinstance(on_trace, bool): - return on_trace - else: - return torch._C._is_tracing() - else: - warnings.warn( - 'torch.jit.is_tracing is only supported after v1.6.0. ' - 'Therefore is_tracing returns False automatically. Please ' - 'set on_trace manually if you are using trace.', UserWarning) - return False diff --git a/cv/detection/autoassign/pytorch/mmcv/utils/version_utils.py b/cv/detection/autoassign/pytorch/mmcv/utils/version_utils.py deleted file mode 100755 index 963c45a2e..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/utils/version_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import subprocess -import warnings - -from packaging.version import parse - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - assert 'parrots' not in version_str - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - -def get_git_hash(fallback='unknown', digits=None): - """Get the git hash of the current repo. - - Args: - fallback (str, optional): The fallback string when git hash is - unavailable. Defaults to 'unknown'. - digits (int, optional): kept digits of the hash. Defaults to None, - meaning all digits are kept. - - Returns: - str: Git commit hash. - """ - - if digits is not None and not isinstance(digits, int): - raise TypeError('digits must be None or an integer') - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - sha = out.strip().decode('ascii') - if digits is not None: - sha = sha[:digits] - except OSError: - sha = fallback - - return sha diff --git a/cv/detection/autoassign/pytorch/mmcv/version.py b/cv/detection/autoassign/pytorch/mmcv/version.py deleted file mode 100755 index 1cce4e50b..000000000 --- a/cv/detection/autoassign/pytorch/mmcv/version.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -__version__ = '1.3.17' - - -def parse_version_info(version_str: str, length: int = 4) -> tuple: - """Parse a version string into a tuple. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int | str]: The version info, e.g., "1.3.0" is parsed into - (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into - (2, 0, 0, 0, 'rc', 1) (when length is set to 4). - """ - from packaging.version import parse - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - release.extend(list(version.pre)) - elif version.is_postrelease: - release.extend(list(version.post)) - else: - release.extend([0, 0]) - return tuple(release) - - -version_info = tuple(int(x) for x in __version__.split('.')[:3]) - -__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/cv/detection/autoassign/pytorch/mmdet/__init__.py b/cv/detection/autoassign/pytorch/mmdet/__init__.py deleted file mode 100755 index a1a49ec86..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - -from .version import __version__, short_version - - -def digit_version(version_str): - digit_version = [] - for x in version_str.split('.'): - if x.isdigit(): - digit_version.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - digit_version.append(int(patch_version[0]) - 1) - digit_version.append(int(patch_version[1])) - return digit_version - - -mmcv_minimum_version = '1.3.17' -mmcv_maximum_version = '1.6.0' -mmcv_version = digit_version(mmcv.__version__) - - -assert (mmcv_version >= digit_version(mmcv_minimum_version) - and mmcv_version <= digit_version(mmcv_maximum_version)), \ - f'MMCV=={mmcv.__version__} is used but incompatible. ' \ - f'Please install mmcv>={mmcv_minimum_version}, <={mmcv_maximum_version}.' - -__all__ = ['__version__', 'short_version'] diff --git a/cv/detection/autoassign/pytorch/mmdet/apis/__init__.py b/cv/detection/autoassign/pytorch/mmdet/apis/__init__.py deleted file mode 100755 index 0188a1af4..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/apis/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .inference import (async_inference_detector, inference_detector, - init_detector, show_result_pyplot) -from .test import multi_gpu_test, single_gpu_test -from .train import (get_root_logger, init_random_seed, set_random_seed, - train_detector) - -__all__ = [ - 'get_root_logger', 'set_random_seed', 'train_detector', 'init_detector', - 'async_inference_detector', 'inference_detector', 'show_result_pyplot', - 'multi_gpu_test', 'single_gpu_test', 'init_random_seed' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/apis/inference.py b/cv/detection/autoassign/pytorch/mmdet/apis/inference.py deleted file mode 100755 index b3e6f862f..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/apis/inference.py +++ /dev/null @@ -1,251 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from pathlib import Path - -import mmcv -import numpy as np -import torch -from mmcv.ops import RoIPool -from mmcv.parallel import collate, scatter -from mmcv.runner import load_checkpoint - -from mmdet.core import get_classes -from mmdet.datasets import replace_ImageToTensor -from mmdet.datasets.pipelines import Compose -from mmdet.models import build_detector - - -def init_detector(config, checkpoint=None, device='cuda:0', cfg_options=None): - """Initialize a detector from config file. - - Args: - config (str, :obj:`Path`, or :obj:`mmcv.Config`): Config file path, - :obj:`Path`, or the config object. - checkpoint (str, optional): Checkpoint path. If left as None, the model - will not load any weights. - cfg_options (dict): Options to override some settings in the used - config. - - Returns: - nn.Module: The constructed detector. - """ - if isinstance(config, (str, Path)): - config = mmcv.Config.fromfile(config) - elif not isinstance(config, mmcv.Config): - raise TypeError('config must be a filename or Config object, ' - f'but got {type(config)}') - if cfg_options is not None: - config.merge_from_dict(cfg_options) - if 'pretrained' in config.model: - config.model.pretrained = None - elif 'init_cfg' in config.model.backbone: - config.model.backbone.init_cfg = None - config.model.train_cfg = None - model = build_detector(config.model, test_cfg=config.get('test_cfg')) - if checkpoint is not None: - checkpoint = load_checkpoint(model, checkpoint, map_location='cpu') - if 'CLASSES' in checkpoint.get('meta', {}): - model.CLASSES = checkpoint['meta']['CLASSES'] - else: - warnings.simplefilter('once') - warnings.warn('Class names are not saved in the checkpoint\'s ' - 'meta data, use COCO classes by default.') - model.CLASSES = get_classes('coco') - model.cfg = config # save the config in the model for convenience - model.to(device) - model.eval() - return model - - -class LoadImage: - """Deprecated. - - A simple pipeline to load image. - """ - - def __call__(self, results): - """Call function to load images into results. - - Args: - results (dict): A result dict contains the file name - of the image to be read. - Returns: - dict: ``results`` will be returned containing loaded image. - """ - warnings.simplefilter('once') - warnings.warn('`LoadImage` is deprecated and will be removed in ' - 'future releases. You may use `LoadImageFromWebcam` ' - 'from `mmdet.datasets.pipelines.` instead.') - if isinstance(results['img'], str): - results['filename'] = results['img'] - results['ori_filename'] = results['img'] - else: - results['filename'] = None - results['ori_filename'] = None - img = mmcv.imread(results['img']) - results['img'] = img - results['img_fields'] = ['img'] - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - return results - - -def inference_detector(model, imgs): - """Inference image(s) with the detector. - - Args: - model (nn.Module): The loaded detector. - imgs (str/ndarray or list[str/ndarray] or tuple[str/ndarray]): - Either image files or loaded images. - - Returns: - If imgs is a list or tuple, the same length list type results - will be returned, otherwise return the detection results directly. - """ - - if isinstance(imgs, (list, tuple)): - is_batch = True - else: - imgs = [imgs] - is_batch = False - - cfg = model.cfg - device = next(model.parameters()).device # model device - - if isinstance(imgs[0], np.ndarray): - cfg = cfg.copy() - # set loading pipeline type - cfg.data.test.pipeline[0].type = 'LoadImageFromWebcam' - - cfg.data.test.pipeline = replace_ImageToTensor(cfg.data.test.pipeline) - test_pipeline = Compose(cfg.data.test.pipeline) - - datas = [] - for img in imgs: - # prepare data - if isinstance(img, np.ndarray): - # directly add img - data = dict(img=img) - else: - # add information into dict - data = dict(img_info=dict(filename=img), img_prefix=None) - # build the data pipeline - data = test_pipeline(data) - datas.append(data) - - data = collate(datas, samples_per_gpu=len(imgs)) - # just get the actual data from DataContainer - data['img_metas'] = [img_metas.data[0] for img_metas in data['img_metas']] - data['img'] = [img.data[0] for img in data['img']] - if next(model.parameters()).is_cuda: - # scatter to specified GPU - data = scatter(data, [device])[0] - else: - for m in model.modules(): - assert not isinstance( - m, RoIPool - ), 'CPU inference with RoIPool is not supported currently.' - - # forward the model - with torch.no_grad(): - results = model(return_loss=False, rescale=True, **data) - - if not is_batch: - return results[0] - else: - return results - - -async def async_inference_detector(model, imgs): - """Async inference image(s) with the detector. - - Args: - model (nn.Module): The loaded detector. - img (str | ndarray): Either image files or loaded images. - - Returns: - Awaitable detection results. - """ - if not isinstance(imgs, (list, tuple)): - imgs = [imgs] - - cfg = model.cfg - device = next(model.parameters()).device # model device - - if isinstance(imgs[0], np.ndarray): - cfg = cfg.copy() - # set loading pipeline type - cfg.data.test.pipeline[0].type = 'LoadImageFromWebcam' - - cfg.data.test.pipeline = replace_ImageToTensor(cfg.data.test.pipeline) - test_pipeline = Compose(cfg.data.test.pipeline) - - datas = [] - for img in imgs: - # prepare data - if isinstance(img, np.ndarray): - # directly add img - data = dict(img=img) - else: - # add information into dict - data = dict(img_info=dict(filename=img), img_prefix=None) - # build the data pipeline - data = test_pipeline(data) - datas.append(data) - - data = collate(datas, samples_per_gpu=len(imgs)) - # just get the actual data from DataContainer - data['img_metas'] = [img_metas.data[0] for img_metas in data['img_metas']] - data['img'] = [img.data[0] for img in data['img']] - if next(model.parameters()).is_cuda: - # scatter to specified GPU - data = scatter(data, [device])[0] - else: - for m in model.modules(): - assert not isinstance( - m, RoIPool - ), 'CPU inference with RoIPool is not supported currently.' - - # We don't restore `torch.is_grad_enabled()` value during concurrent - # inference since execution can overlap - torch.set_grad_enabled(False) - results = await model.aforward_test(rescale=True, **data) - return results - - -def show_result_pyplot(model, - img, - result, - score_thr=0.3, - title='result', - wait_time=0, - palette=None, - out_file=None): - """Visualize the detection results on the image. - - Args: - model (nn.Module): The loaded detector. - img (str or np.ndarray): Image filename or loaded image. - result (tuple[list] or list): The detection result, can be either - (bbox, segm) or just bbox. - score_thr (float): The threshold to visualize the bboxes and masks. - title (str): Title of the pyplot figure. - wait_time (float): Value of waitKey param. Default: 0. - palette (str or tuple(int) or :obj:`Color`): Color. - The tuple of color should be in BGR order. - out_file (str or None): The path to write the image. - Default: None. - """ - if hasattr(model, 'module'): - model = model.module - model.show_result( - img, - result, - score_thr=score_thr, - show=True, - wait_time=wait_time, - win_name=title, - bbox_color=palette, - text_color=(200, 200, 200), - mask_color=palette, - out_file=out_file) diff --git a/cv/detection/autoassign/pytorch/mmdet/apis/test.py b/cv/detection/autoassign/pytorch/mmdet/apis/test.py deleted file mode 100755 index 2f8ad9929..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/apis/test.py +++ /dev/null @@ -1,209 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import mmcv -import torch -import torch.distributed as dist -from mmcv.image import tensor2imgs -from mmcv.runner import get_dist_info - -from mmdet.core import encode_mask_results - - -def single_gpu_test(model, - data_loader, - show=False, - out_dir=None, - show_score_thr=0.3): - model.eval() - results = [] - dataset = data_loader.dataset - PALETTE = getattr(dataset, 'PALETTE', None) - prog_bar = mmcv.ProgressBar(len(dataset)) - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - - batch_size = len(result) - if show or out_dir: - if batch_size == 1 and isinstance(data['img'][0], torch.Tensor): - img_tensor = data['img'][0] - else: - img_tensor = data['img'][0].data[0] - img_metas = data['img_metas'][0].data[0] - imgs = tensor2imgs(img_tensor, **img_metas[0]['img_norm_cfg']) - assert len(imgs) == len(img_metas) - - for i, (img, img_meta) in enumerate(zip(imgs, img_metas)): - h, w, _ = img_meta['img_shape'] - img_show = img[:h, :w, :] - - ori_h, ori_w = img_meta['ori_shape'][:-1] - img_show = mmcv.imresize(img_show, (ori_w, ori_h)) - - if out_dir: - out_file = osp.join(out_dir, img_meta['ori_filename']) - else: - out_file = None - - model.module.show_result( - img_show, - result[i], - bbox_color=PALETTE, - text_color=PALETTE, - mask_color=PALETTE, - show=show, - out_file=out_file, - score_thr=show_score_thr) - - # encode mask results - if isinstance(result[0], tuple): - result = [(bbox_results, encode_mask_results(mask_results)) - for bbox_results, mask_results in result] - # This logic is only used in panoptic segmentation test. - elif isinstance(result[0], dict) and 'ins_results' in result[0]: - for j in range(len(result)): - bbox_results, mask_results = result[j]['ins_results'] - result[j]['ins_results'] = (bbox_results, - encode_mask_results(mask_results)) - - results.extend(result) - - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' - it encodes results to gpu tensors and use gpu communication for results - collection. On cpu mode it saves the results on different gpus to 'tmpdir' - and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, rescale=True, **data) - # encode mask results - if isinstance(result[0], tuple): - result = [(bbox_results, encode_mask_results(mask_results)) - for bbox_results, mask_results in result] - # This logic is only used in panoptic segmentation test. - elif isinstance(result[0], dict) and 'ins_results' in result[0]: - for j in range(len(result)): - bbox_results, mask_results = result[j]['ins_results'] - result[j]['ins_results'] = ( - bbox_results, encode_mask_results(mask_results)) - - results.extend(result) - - if rank == 0: - batch_size = len(result) - for _ in range(batch_size * world_size): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_list.append(mmcv.load(part_file)) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_list.append( - pickle.loads(recv[:shape[0]].cpu().numpy().tobytes())) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/detection/autoassign/pytorch/mmdet/apis/train.py b/cv/detection/autoassign/pytorch/mmdet/apis/train.py deleted file mode 100755 index 0851029df..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/apis/train.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random - -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import (DistSamplerSeedHook, EpochBasedRunner, - Fp16OptimizerHook, OptimizerHook, build_runner, - get_dist_info) - -from mmdet.core import DistEvalHook, EvalHook, build_optimizer -from mmdet.datasets import (build_dataloader, build_dataset, - replace_ImageToTensor) -from mmdet.utils import (build_ddp, build_dp, compat_cfg, - find_latest_checkpoint, get_root_logger) - - -def init_random_seed(seed=None, device='cuda'): - """Initialize random seed. - - If the seed is not set, the seed will be automatically randomized, - and then broadcast to all processes to prevent some potential bugs. - - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - - Returns: - int: Seed to be used. - """ - if seed is not None: - return seed - - # Make sure all ranks share the same random seed to prevent - # some potential bugs. Please refer to - # https://github.com/open-mmlab/mmdetection/issues/6339 - rank, world_size = get_dist_info() - seed = np.random.randint(2**31) - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() - - -def set_random_seed(seed, deterministic=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - """ - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - - -def auto_scale_lr(cfg, distributed, logger): - """Automatically scaling LR according to GPU number and sample per GPU. - - Args: - cfg (config): Training config. - distributed (bool): Using distributed or not. - logger (logging.Logger): Logger. - """ - # Get flag from config - if ('auto_scale_lr' not in cfg) or \ - (not cfg.auto_scale_lr.get('enable', False)): - logger.info('Automatic scaling of learning rate (LR)' - ' has been disabled.') - return - - # Get base batch size from config - base_batch_size = cfg.auto_scale_lr.get('base_batch_size', None) - if base_batch_size is None: - return - - # Get gpu number - if distributed: - _, world_size = get_dist_info() - num_gpus = len(range(world_size)) - else: - num_gpus = len(cfg.gpu_ids) - - # calculate the batch size - samples_per_gpu = cfg.data.train_dataloader.samples_per_gpu - batch_size = num_gpus * samples_per_gpu - logger.info(f'Training with {num_gpus} GPU(s) with {samples_per_gpu} ' - f'samples per GPU. The total batch size is {batch_size}.') - - if batch_size != base_batch_size: - # scale LR with - # [linear scaling rule](https://arxiv.org/abs/1706.02677) - scaled_lr = (batch_size / base_batch_size) * cfg.optimizer.lr - logger.info('LR has been automatically scaled ' - f'from {cfg.optimizer.lr} to {scaled_lr}') - cfg.optimizer.lr = scaled_lr - else: - logger.info('The batch size match the ' - f'base batch size: {base_batch_size}, ' - f'will not scaling the LR ({cfg.optimizer.lr}).') - - -def train_detector(model, - dataset, - cfg, - distributed=False, - validate=False, - timestamp=None, - meta=None): - - cfg = compat_cfg(cfg) - logger = get_root_logger(log_level=cfg.log_level) - - # prepare data loaders - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - - runner_type = 'EpochBasedRunner' if 'runner' not in cfg else cfg.runner[ - 'type'] - - train_dataloader_default_args = dict( - samples_per_gpu=2, - workers_per_gpu=2, - # `num_gpus` will be ignored if distributed - num_gpus=len(cfg.gpu_ids), - dist=distributed, - seed=cfg.seed, - runner_type=runner_type, - persistent_workers=False) - - train_loader_cfg = { - **train_dataloader_default_args, - **cfg.data.get('train_dataloader', {}) - } - - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus - if distributed: - find_unused_parameters = cfg.get('find_unused_parameters', False) - # Sets the `find_unused_parameters` parameter in - # torch.nn.parallel.DistributedDataParallel - model = build_ddp( - model, - cfg.device, - device_ids=[int(os.environ['LOCAL_RANK'])], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - else: - model = build_dp(model, cfg.device, device_ids=cfg.gpu_ids) - - # build optimizer - auto_scale_lr(cfg, distributed, logger) - optimizer = build_optimizer(model, cfg.optimizer) - - runner = build_runner( - cfg.runner, - default_args=dict( - model=model, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta)) - - # an ugly workaround to make .log and .log.json filenames the same - runner.timestamp = timestamp - - # fp16 setting - fp16_cfg = cfg.get('fp16', None) - if fp16_cfg is not None: - optimizer_config = Fp16OptimizerHook( - **cfg.optimizer_config, **fp16_cfg, distributed=distributed) - elif distributed and 'type' not in cfg.optimizer_config: - optimizer_config = OptimizerHook(**cfg.optimizer_config) - else: - optimizer_config = cfg.optimizer_config - - # register hooks - runner.register_training_hooks( - cfg.lr_config, - optimizer_config, - cfg.checkpoint_config, - cfg.log_config, - cfg.get('momentum_config', None), - custom_hooks_config=cfg.get('custom_hooks', None)) - - if distributed: - if isinstance(runner, EpochBasedRunner): - runner.register_hook(DistSamplerSeedHook()) - - # register eval hooks - if validate: - val_dataloader_default_args = dict( - samples_per_gpu=1, - workers_per_gpu=2, - dist=distributed, - shuffle=False, - persistent_workers=False) - - val_dataloader_args = { - **val_dataloader_default_args, - **cfg.data.get('val_dataloader', {}) - } - # Support batch_size > 1 in validation - - if val_dataloader_args['samples_per_gpu'] > 1: - # Replace 'ImageToTensor' to 'DefaultFormatBundle' - cfg.data.val.pipeline = replace_ImageToTensor( - cfg.data.val.pipeline) - val_dataset = build_dataset(cfg.data.val, dict(test_mode=True)) - - val_dataloader = build_dataloader(val_dataset, **val_dataloader_args) - eval_cfg = cfg.get('evaluation', {}) - eval_cfg['by_epoch'] = cfg.runner['type'] != 'IterBasedRunner' - eval_hook = DistEvalHook if distributed else EvalHook - # In this PR (https://github.com/open-mmlab/mmcv/pull/1193), the - # priority of IterTimerHook has been modified from 'NORMAL' to 'LOW'. - runner.register_hook( - eval_hook(val_dataloader, **eval_cfg), priority='LOW') - - resume_from = None - if cfg.resume_from is None and cfg.get('auto_resume'): - resume_from = find_latest_checkpoint(cfg.work_dir) - if resume_from is not None: - cfg.resume_from = resume_from - - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/__init__.py deleted file mode 100755 index 354efb51a..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .anchor import * # noqa: F401, F403 -from .bbox import * # noqa: F401, F403 -from .data_structures import * # noqa: F401, F403 -from .evaluation import * # noqa: F401, F403 -from .hook import * # noqa: F401, F403 -from .mask import * # noqa: F401, F403 -from .optimizers import * # noqa: F401, F403 -from .post_processing import * # noqa: F401, F403 -from .utils import * # noqa: F401, F403 diff --git a/cv/detection/autoassign/pytorch/mmdet/core/anchor/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/anchor/__init__.py deleted file mode 100755 index c884cb0ea..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/anchor/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .anchor_generator import (AnchorGenerator, LegacyAnchorGenerator, - YOLOAnchorGenerator) -from .builder import (ANCHOR_GENERATORS, PRIOR_GENERATORS, - build_anchor_generator, build_prior_generator) -from .point_generator import MlvlPointGenerator, PointGenerator -from .utils import anchor_inside_flags, calc_region, images_to_levels - -__all__ = [ - 'AnchorGenerator', 'LegacyAnchorGenerator', 'anchor_inside_flags', - 'PointGenerator', 'images_to_levels', 'calc_region', - 'build_anchor_generator', 'ANCHOR_GENERATORS', 'YOLOAnchorGenerator', - 'build_prior_generator', 'PRIOR_GENERATORS', 'MlvlPointGenerator' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/anchor/anchor_generator.py b/cv/detection/autoassign/pytorch/mmdet/core/anchor/anchor_generator.py deleted file mode 100755 index 6d0e90f4b..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/anchor/anchor_generator.py +++ /dev/null @@ -1,866 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv -import numpy as np -import torch -from torch.nn.modules.utils import _pair - -from .builder import PRIOR_GENERATORS - - -@PRIOR_GENERATORS.register_module() -class AnchorGenerator: - """Standard anchor generator for 2D anchor-based detectors. - - Args: - strides (list[int] | list[tuple[int, int]]): Strides of anchors - in multiple feature levels in order (w, h). - ratios (list[float]): The list of ratios between the height and width - of anchors in a single level. - scales (list[int] | None): Anchor scales for anchors in a single level. - It cannot be set at the same time if `octave_base_scale` and - `scales_per_octave` are set. - base_sizes (list[int] | None): The basic sizes - of anchors in multiple levels. - If None is given, strides will be used as base_sizes. - (If strides are non square, the shortest stride is taken.) - scale_major (bool): Whether to multiply scales first when generating - base anchors. If true, the anchors in the same row will have the - same scales. By default it is True in V2.0 - octave_base_scale (int): The base scale of octave. - scales_per_octave (int): Number of scales for each octave. - `octave_base_scale` and `scales_per_octave` are usually used in - retinanet and the `scales` should be None when they are set. - centers (list[tuple[float, float]] | None): The centers of the anchor - relative to the feature grid center in multiple feature levels. - By default it is set to be None and not used. If a list of tuple of - float is given, they will be used to shift the centers of anchors. - center_offset (float): The offset of center in proportion to anchors' - width and height. By default it is 0 in V2.0. - - Examples: - >>> from mmdet.core import AnchorGenerator - >>> self = AnchorGenerator([16], [1.], [1.], [9]) - >>> all_anchors = self.grid_priors([(2, 2)], device='cpu') - >>> print(all_anchors) - [tensor([[-4.5000, -4.5000, 4.5000, 4.5000], - [11.5000, -4.5000, 20.5000, 4.5000], - [-4.5000, 11.5000, 4.5000, 20.5000], - [11.5000, 11.5000, 20.5000, 20.5000]])] - >>> self = AnchorGenerator([16, 32], [1.], [1.], [9, 18]) - >>> all_anchors = self.grid_priors([(2, 2), (1, 1)], device='cpu') - >>> print(all_anchors) - [tensor([[-4.5000, -4.5000, 4.5000, 4.5000], - [11.5000, -4.5000, 20.5000, 4.5000], - [-4.5000, 11.5000, 4.5000, 20.5000], - [11.5000, 11.5000, 20.5000, 20.5000]]), \ - tensor([[-9., -9., 9., 9.]])] - """ - - def __init__(self, - strides, - ratios, - scales=None, - base_sizes=None, - scale_major=True, - octave_base_scale=None, - scales_per_octave=None, - centers=None, - center_offset=0.): - # check center and center_offset - if center_offset != 0: - assert centers is None, 'center cannot be set when center_offset' \ - f'!=0, {centers} is given.' - if not (0 <= center_offset <= 1): - raise ValueError('center_offset should be in range [0, 1], ' - f'{center_offset} is given.') - if centers is not None: - assert len(centers) == len(strides), \ - 'The number of strides should be the same as centers, got ' \ - f'{strides} and {centers}' - - # calculate base sizes of anchors - self.strides = [_pair(stride) for stride in strides] - self.base_sizes = [min(stride) for stride in self.strides - ] if base_sizes is None else base_sizes - assert len(self.base_sizes) == len(self.strides), \ - 'The number of strides should be the same as base sizes, got ' \ - f'{self.strides} and {self.base_sizes}' - - # calculate scales of anchors - assert ((octave_base_scale is not None - and scales_per_octave is not None) ^ (scales is not None)), \ - 'scales and octave_base_scale with scales_per_octave cannot' \ - ' be set at the same time' - if scales is not None: - self.scales = torch.Tensor(scales) - elif octave_base_scale is not None and scales_per_octave is not None: - octave_scales = np.array( - [2**(i / scales_per_octave) for i in range(scales_per_octave)]) - scales = octave_scales * octave_base_scale - self.scales = torch.Tensor(scales) - else: - raise ValueError('Either scales or octave_base_scale with ' - 'scales_per_octave should be set') - - self.octave_base_scale = octave_base_scale - self.scales_per_octave = scales_per_octave - self.ratios = torch.Tensor(ratios) - self.scale_major = scale_major - self.centers = centers - self.center_offset = center_offset - self.base_anchors = self.gen_base_anchors() - - @property - def num_base_anchors(self): - """list[int]: total number of base anchors in a feature grid""" - return self.num_base_priors - - @property - def num_base_priors(self): - """list[int]: The number of priors (anchors) at a point - on the feature grid""" - return [base_anchors.size(0) for base_anchors in self.base_anchors] - - @property - def num_levels(self): - """int: number of feature levels that the generator will be applied""" - return len(self.strides) - - def gen_base_anchors(self): - """Generate base anchors. - - Returns: - list(torch.Tensor): Base anchors of a feature grid in multiple \ - feature levels. - """ - multi_level_base_anchors = [] - for i, base_size in enumerate(self.base_sizes): - center = None - if self.centers is not None: - center = self.centers[i] - multi_level_base_anchors.append( - self.gen_single_level_base_anchors( - base_size, - scales=self.scales, - ratios=self.ratios, - center=center)) - return multi_level_base_anchors - - def gen_single_level_base_anchors(self, - base_size, - scales, - ratios, - center=None): - """Generate base anchors of a single level. - - Args: - base_size (int | float): Basic size of an anchor. - scales (torch.Tensor): Scales of the anchor. - ratios (torch.Tensor): The ratio between between the height - and width of anchors in a single level. - center (tuple[float], optional): The center of the base anchor - related to a single feature grid. Defaults to None. - - Returns: - torch.Tensor: Anchors in a single-level feature maps. - """ - w = base_size - h = base_size - if center is None: - x_center = self.center_offset * w - y_center = self.center_offset * h - else: - x_center, y_center = center - - h_ratios = torch.sqrt(ratios) - w_ratios = 1 / h_ratios - if self.scale_major: - ws = (w * w_ratios[:, None] * scales[None, :]).view(-1) - hs = (h * h_ratios[:, None] * scales[None, :]).view(-1) - else: - ws = (w * scales[:, None] * w_ratios[None, :]).view(-1) - hs = (h * scales[:, None] * h_ratios[None, :]).view(-1) - - # use float anchor and the anchor's center is aligned with the - # pixel center - base_anchors = [ - x_center - 0.5 * ws, y_center - 0.5 * hs, x_center + 0.5 * ws, - y_center + 0.5 * hs - ] - base_anchors = torch.stack(base_anchors, dim=-1) - - return base_anchors - - def _meshgrid(self, x, y, row_major=True): - """Generate mesh grid of x and y. - - Args: - x (torch.Tensor): Grids of x dimension. - y (torch.Tensor): Grids of y dimension. - row_major (bool, optional): Whether to return y grids first. - Defaults to True. - - Returns: - tuple[torch.Tensor]: The mesh grids of x and y. - """ - # use shape instead of len to keep tracing while exporting to onnx - xx = x.repeat(y.shape[0]) - yy = y.view(-1, 1).repeat(1, x.shape[0]).view(-1) - if row_major: - return xx, yy - else: - return yy, xx - - def grid_priors(self, featmap_sizes, dtype=torch.float32, device='cuda'): - """Generate grid anchors in multiple feature levels. - - Args: - featmap_sizes (list[tuple]): List of feature map sizes in - multiple feature levels. - dtype (:obj:`torch.dtype`): Dtype of priors. - Default: torch.float32. - device (str): The device where the anchors will be put on. - - Return: - list[torch.Tensor]: Anchors in multiple feature levels. \ - The sizes of each tensor should be [N, 4], where \ - N = width * height * num_base_anchors, width and height \ - are the sizes of the corresponding feature level, \ - num_base_anchors is the number of anchors for that level. - """ - assert self.num_levels == len(featmap_sizes) - multi_level_anchors = [] - for i in range(self.num_levels): - anchors = self.single_level_grid_priors( - featmap_sizes[i], level_idx=i, dtype=dtype, device=device) - multi_level_anchors.append(anchors) - return multi_level_anchors - - def single_level_grid_priors(self, - featmap_size, - level_idx, - dtype=torch.float32, - device='cuda'): - """Generate grid anchors of a single level. - - Note: - This function is usually called by method ``self.grid_priors``. - - Args: - featmap_size (tuple[int]): Size of the feature maps. - level_idx (int): The index of corresponding feature map level. - dtype (obj:`torch.dtype`): Date type of points.Defaults to - ``torch.float32``. - device (str, optional): The device the tensor will be put on. - Defaults to 'cuda'. - - Returns: - torch.Tensor: Anchors in the overall feature maps. - """ - - base_anchors = self.base_anchors[level_idx].to(device).to(dtype) - feat_h, feat_w = featmap_size - stride_w, stride_h = self.strides[level_idx] - # First create Range with the default dtype, than convert to - # target `dtype` for onnx exporting. - shift_x = torch.arange(0, feat_w, device=device).to(dtype) * stride_w - shift_y = torch.arange(0, feat_h, device=device).to(dtype) * stride_h - - shift_xx, shift_yy = self._meshgrid(shift_x, shift_y) - shifts = torch.stack([shift_xx, shift_yy, shift_xx, shift_yy], dim=-1) - # first feat_w elements correspond to the first row of shifts - # add A anchors (1, A, 4) to K shifts (K, 1, 4) to get - # shifted anchors (K, A, 4), reshape to (K*A, 4) - - all_anchors = base_anchors[None, :, :] + shifts[:, None, :] - all_anchors = all_anchors.view(-1, 4) - # first A rows correspond to A anchors of (0, 0) in feature map, - # then (0, 1), (0, 2), ... - return all_anchors - - def sparse_priors(self, - prior_idxs, - featmap_size, - level_idx, - dtype=torch.float32, - device='cuda'): - """Generate sparse anchors according to the ``prior_idxs``. - - Args: - prior_idxs (Tensor): The index of corresponding anchors - in the feature map. - featmap_size (tuple[int]): feature map size arrange as (h, w). - level_idx (int): The level index of corresponding feature - map. - dtype (obj:`torch.dtype`): Date type of points.Defaults to - ``torch.float32``. - device (obj:`torch.device`): The device where the points is - located. - Returns: - Tensor: Anchor with shape (N, 4), N should be equal to - the length of ``prior_idxs``. - """ - - height, width = featmap_size - num_base_anchors = self.num_base_anchors[level_idx] - base_anchor_id = prior_idxs % num_base_anchors - x = (prior_idxs // - num_base_anchors) % width * self.strides[level_idx][0] - y = (prior_idxs // width // - num_base_anchors) % height * self.strides[level_idx][1] - priors = torch.stack([x, y, x, y], 1).to(dtype).to(device) + \ - self.base_anchors[level_idx][base_anchor_id, :].to(device) - - return priors - - def grid_anchors(self, featmap_sizes, device='cuda'): - """Generate grid anchors in multiple feature levels. - - Args: - featmap_sizes (list[tuple]): List of feature map sizes in - multiple feature levels. - device (str): Device where the anchors will be put on. - - Return: - list[torch.Tensor]: Anchors in multiple feature levels. \ - The sizes of each tensor should be [N, 4], where \ - N = width * height * num_base_anchors, width and height \ - are the sizes of the corresponding feature level, \ - num_base_anchors is the number of anchors for that level. - """ - warnings.warn('``grid_anchors`` would be deprecated soon. ' - 'Please use ``grid_priors`` ') - - assert self.num_levels == len(featmap_sizes) - multi_level_anchors = [] - for i in range(self.num_levels): - anchors = self.single_level_grid_anchors( - self.base_anchors[i].to(device), - featmap_sizes[i], - self.strides[i], - device=device) - multi_level_anchors.append(anchors) - return multi_level_anchors - - def single_level_grid_anchors(self, - base_anchors, - featmap_size, - stride=(16, 16), - device='cuda'): - """Generate grid anchors of a single level. - - Note: - This function is usually called by method ``self.grid_anchors``. - - Args: - base_anchors (torch.Tensor): The base anchors of a feature grid. - featmap_size (tuple[int]): Size of the feature maps. - stride (tuple[int], optional): Stride of the feature map in order - (w, h). Defaults to (16, 16). - device (str, optional): Device the tensor will be put on. - Defaults to 'cuda'. - - Returns: - torch.Tensor: Anchors in the overall feature maps. - """ - - warnings.warn( - '``single_level_grid_anchors`` would be deprecated soon. ' - 'Please use ``single_level_grid_priors`` ') - - # keep featmap_size as Tensor instead of int, so that we - # can convert to ONNX correctly - feat_h, feat_w = featmap_size - shift_x = torch.arange(0, feat_w, device=device) * stride[0] - shift_y = torch.arange(0, feat_h, device=device) * stride[1] - - shift_xx, shift_yy = self._meshgrid(shift_x, shift_y) - shifts = torch.stack([shift_xx, shift_yy, shift_xx, shift_yy], dim=-1) - shifts = shifts.type_as(base_anchors) - # first feat_w elements correspond to the first row of shifts - # add A anchors (1, A, 4) to K shifts (K, 1, 4) to get - # shifted anchors (K, A, 4), reshape to (K*A, 4) - - all_anchors = base_anchors[None, :, :] + shifts[:, None, :] - all_anchors = all_anchors.view(-1, 4) - # first A rows correspond to A anchors of (0, 0) in feature map, - # then (0, 1), (0, 2), ... - return all_anchors - - def valid_flags(self, featmap_sizes, pad_shape, device='cuda'): - """Generate valid flags of anchors in multiple feature levels. - - Args: - featmap_sizes (list(tuple)): List of feature map sizes in - multiple feature levels. - pad_shape (tuple): The padded shape of the image. - device (str): Device where the anchors will be put on. - - Return: - list(torch.Tensor): Valid flags of anchors in multiple levels. - """ - assert self.num_levels == len(featmap_sizes) - multi_level_flags = [] - for i in range(self.num_levels): - anchor_stride = self.strides[i] - feat_h, feat_w = featmap_sizes[i] - h, w = pad_shape[:2] - valid_feat_h = min(int(np.ceil(h / anchor_stride[1])), feat_h) - valid_feat_w = min(int(np.ceil(w / anchor_stride[0])), feat_w) - flags = self.single_level_valid_flags((feat_h, feat_w), - (valid_feat_h, valid_feat_w), - self.num_base_anchors[i], - device=device) - multi_level_flags.append(flags) - return multi_level_flags - - def single_level_valid_flags(self, - featmap_size, - valid_size, - num_base_anchors, - device='cuda'): - """Generate the valid flags of anchor in a single feature map. - - Args: - featmap_size (tuple[int]): The size of feature maps, arrange - as (h, w). - valid_size (tuple[int]): The valid size of the feature maps. - num_base_anchors (int): The number of base anchors. - device (str, optional): Device where the flags will be put on. - Defaults to 'cuda'. - - Returns: - torch.Tensor: The valid flags of each anchor in a single level \ - feature map. - """ - feat_h, feat_w = featmap_size - valid_h, valid_w = valid_size - assert valid_h <= feat_h and valid_w <= feat_w - valid_x = torch.zeros(feat_w, dtype=torch.bool, device=device) - valid_y = torch.zeros(feat_h, dtype=torch.bool, device=device) - valid_x[:valid_w] = 1 - valid_y[:valid_h] = 1 - valid_xx, valid_yy = self._meshgrid(valid_x, valid_y) - valid = valid_xx & valid_yy - valid = valid[:, None].expand(valid.size(0), - num_base_anchors).contiguous().view(-1) - return valid - - def __repr__(self): - """str: a string that describes the module""" - indent_str = ' ' - repr_str = self.__class__.__name__ + '(\n' - repr_str += f'{indent_str}strides={self.strides},\n' - repr_str += f'{indent_str}ratios={self.ratios},\n' - repr_str += f'{indent_str}scales={self.scales},\n' - repr_str += f'{indent_str}base_sizes={self.base_sizes},\n' - repr_str += f'{indent_str}scale_major={self.scale_major},\n' - repr_str += f'{indent_str}octave_base_scale=' - repr_str += f'{self.octave_base_scale},\n' - repr_str += f'{indent_str}scales_per_octave=' - repr_str += f'{self.scales_per_octave},\n' - repr_str += f'{indent_str}num_levels={self.num_levels}\n' - repr_str += f'{indent_str}centers={self.centers},\n' - repr_str += f'{indent_str}center_offset={self.center_offset})' - return repr_str - - -@PRIOR_GENERATORS.register_module() -class SSDAnchorGenerator(AnchorGenerator): - """Anchor generator for SSD. - - Args: - strides (list[int] | list[tuple[int, int]]): Strides of anchors - in multiple feature levels. - ratios (list[float]): The list of ratios between the height and width - of anchors in a single level. - min_sizes (list[float]): The list of minimum anchor sizes on each - level. - max_sizes (list[float]): The list of maximum anchor sizes on each - level. - basesize_ratio_range (tuple(float)): Ratio range of anchors. Being - used when not setting min_sizes and max_sizes. - input_size (int): Size of feature map, 300 for SSD300, 512 for - SSD512. Being used when not setting min_sizes and max_sizes. - scale_major (bool): Whether to multiply scales first when generating - base anchors. If true, the anchors in the same row will have the - same scales. It is always set to be False in SSD. - """ - - def __init__(self, - strides, - ratios, - min_sizes=None, - max_sizes=None, - basesize_ratio_range=(0.15, 0.9), - input_size=300, - scale_major=True): - assert len(strides) == len(ratios) - assert not (min_sizes is None) ^ (max_sizes is None) - self.strides = [_pair(stride) for stride in strides] - self.centers = [(stride[0] / 2., stride[1] / 2.) - for stride in self.strides] - - if min_sizes is None and max_sizes is None: - # use hard code to generate SSD anchors - self.input_size = input_size - assert mmcv.is_tuple_of(basesize_ratio_range, float) - self.basesize_ratio_range = basesize_ratio_range - # calculate anchor ratios and sizes - min_ratio, max_ratio = basesize_ratio_range - min_ratio = int(min_ratio * 100) - max_ratio = int(max_ratio * 100) - step = int(np.floor(max_ratio - min_ratio) / (self.num_levels - 2)) - min_sizes = [] - max_sizes = [] - for ratio in range(int(min_ratio), int(max_ratio) + 1, step): - min_sizes.append(int(self.input_size * ratio / 100)) - max_sizes.append(int(self.input_size * (ratio + step) / 100)) - if self.input_size == 300: - if basesize_ratio_range[0] == 0.15: # SSD300 COCO - min_sizes.insert(0, int(self.input_size * 7 / 100)) - max_sizes.insert(0, int(self.input_size * 15 / 100)) - elif basesize_ratio_range[0] == 0.2: # SSD300 VOC - min_sizes.insert(0, int(self.input_size * 10 / 100)) - max_sizes.insert(0, int(self.input_size * 20 / 100)) - else: - raise ValueError( - 'basesize_ratio_range[0] should be either 0.15' - 'or 0.2 when input_size is 300, got ' - f'{basesize_ratio_range[0]}.') - elif self.input_size == 512: - if basesize_ratio_range[0] == 0.1: # SSD512 COCO - min_sizes.insert(0, int(self.input_size * 4 / 100)) - max_sizes.insert(0, int(self.input_size * 10 / 100)) - elif basesize_ratio_range[0] == 0.15: # SSD512 VOC - min_sizes.insert(0, int(self.input_size * 7 / 100)) - max_sizes.insert(0, int(self.input_size * 15 / 100)) - else: - raise ValueError( - 'When not setting min_sizes and max_sizes,' - 'basesize_ratio_range[0] should be either 0.1' - 'or 0.15 when input_size is 512, got' - f' {basesize_ratio_range[0]}.') - else: - raise ValueError( - 'Only support 300 or 512 in SSDAnchorGenerator when ' - 'not setting min_sizes and max_sizes, ' - f'got {self.input_size}.') - - assert len(min_sizes) == len(max_sizes) == len(strides) - - anchor_ratios = [] - anchor_scales = [] - for k in range(len(self.strides)): - scales = [1., np.sqrt(max_sizes[k] / min_sizes[k])] - anchor_ratio = [1.] - for r in ratios[k]: - anchor_ratio += [1 / r, r] # 4 or 6 ratio - anchor_ratios.append(torch.Tensor(anchor_ratio)) - anchor_scales.append(torch.Tensor(scales)) - - self.base_sizes = min_sizes - self.scales = anchor_scales - self.ratios = anchor_ratios - self.scale_major = scale_major - self.center_offset = 0 - self.base_anchors = self.gen_base_anchors() - - def gen_base_anchors(self): - """Generate base anchors. - - Returns: - list(torch.Tensor): Base anchors of a feature grid in multiple \ - feature levels. - """ - multi_level_base_anchors = [] - for i, base_size in enumerate(self.base_sizes): - base_anchors = self.gen_single_level_base_anchors( - base_size, - scales=self.scales[i], - ratios=self.ratios[i], - center=self.centers[i]) - indices = list(range(len(self.ratios[i]))) - indices.insert(1, len(indices)) - base_anchors = torch.index_select(base_anchors, 0, - torch.LongTensor(indices)) - multi_level_base_anchors.append(base_anchors) - return multi_level_base_anchors - - def __repr__(self): - """str: a string that describes the module""" - indent_str = ' ' - repr_str = self.__class__.__name__ + '(\n' - repr_str += f'{indent_str}strides={self.strides},\n' - repr_str += f'{indent_str}scales={self.scales},\n' - repr_str += f'{indent_str}scale_major={self.scale_major},\n' - repr_str += f'{indent_str}input_size={self.input_size},\n' - repr_str += f'{indent_str}scales={self.scales},\n' - repr_str += f'{indent_str}ratios={self.ratios},\n' - repr_str += f'{indent_str}num_levels={self.num_levels},\n' - repr_str += f'{indent_str}base_sizes={self.base_sizes},\n' - repr_str += f'{indent_str}basesize_ratio_range=' - repr_str += f'{self.basesize_ratio_range})' - return repr_str - - -@PRIOR_GENERATORS.register_module() -class LegacyAnchorGenerator(AnchorGenerator): - """Legacy anchor generator used in MMDetection V1.x. - - Note: - Difference to the V2.0 anchor generator: - - 1. The center offset of V1.x anchors are set to be 0.5 rather than 0. - 2. The width/height are minused by 1 when calculating the anchors' \ - centers and corners to meet the V1.x coordinate system. - 3. The anchors' corners are quantized. - - Args: - strides (list[int] | list[tuple[int]]): Strides of anchors - in multiple feature levels. - ratios (list[float]): The list of ratios between the height and width - of anchors in a single level. - scales (list[int] | None): Anchor scales for anchors in a single level. - It cannot be set at the same time if `octave_base_scale` and - `scales_per_octave` are set. - base_sizes (list[int]): The basic sizes of anchors in multiple levels. - If None is given, strides will be used to generate base_sizes. - scale_major (bool): Whether to multiply scales first when generating - base anchors. If true, the anchors in the same row will have the - same scales. By default it is True in V2.0 - octave_base_scale (int): The base scale of octave. - scales_per_octave (int): Number of scales for each octave. - `octave_base_scale` and `scales_per_octave` are usually used in - retinanet and the `scales` should be None when they are set. - centers (list[tuple[float, float]] | None): The centers of the anchor - relative to the feature grid center in multiple feature levels. - By default it is set to be None and not used. It a list of float - is given, this list will be used to shift the centers of anchors. - center_offset (float): The offset of center in proportion to anchors' - width and height. By default it is 0.5 in V2.0 but it should be 0.5 - in v1.x models. - - Examples: - >>> from mmdet.core import LegacyAnchorGenerator - >>> self = LegacyAnchorGenerator( - >>> [16], [1.], [1.], [9], center_offset=0.5) - >>> all_anchors = self.grid_anchors(((2, 2),), device='cpu') - >>> print(all_anchors) - [tensor([[ 0., 0., 8., 8.], - [16., 0., 24., 8.], - [ 0., 16., 8., 24.], - [16., 16., 24., 24.]])] - """ - - def gen_single_level_base_anchors(self, - base_size, - scales, - ratios, - center=None): - """Generate base anchors of a single level. - - Note: - The width/height of anchors are minused by 1 when calculating \ - the centers and corners to meet the V1.x coordinate system. - - Args: - base_size (int | float): Basic size of an anchor. - scales (torch.Tensor): Scales of the anchor. - ratios (torch.Tensor): The ratio between between the height. - and width of anchors in a single level. - center (tuple[float], optional): The center of the base anchor - related to a single feature grid. Defaults to None. - - Returns: - torch.Tensor: Anchors in a single-level feature map. - """ - w = base_size - h = base_size - if center is None: - x_center = self.center_offset * (w - 1) - y_center = self.center_offset * (h - 1) - else: - x_center, y_center = center - - h_ratios = torch.sqrt(ratios) - w_ratios = 1 / h_ratios - if self.scale_major: - ws = (w * w_ratios[:, None] * scales[None, :]).view(-1) - hs = (h * h_ratios[:, None] * scales[None, :]).view(-1) - else: - ws = (w * scales[:, None] * w_ratios[None, :]).view(-1) - hs = (h * scales[:, None] * h_ratios[None, :]).view(-1) - - # use float anchor and the anchor's center is aligned with the - # pixel center - base_anchors = [ - x_center - 0.5 * (ws - 1), y_center - 0.5 * (hs - 1), - x_center + 0.5 * (ws - 1), y_center + 0.5 * (hs - 1) - ] - base_anchors = torch.stack(base_anchors, dim=-1).round() - - return base_anchors - - -@PRIOR_GENERATORS.register_module() -class LegacySSDAnchorGenerator(SSDAnchorGenerator, LegacyAnchorGenerator): - """Legacy anchor generator used in MMDetection V1.x. - - The difference between `LegacySSDAnchorGenerator` and `SSDAnchorGenerator` - can be found in `LegacyAnchorGenerator`. - """ - - def __init__(self, - strides, - ratios, - basesize_ratio_range, - input_size=300, - scale_major=True): - super(LegacySSDAnchorGenerator, self).__init__( - strides=strides, - ratios=ratios, - basesize_ratio_range=basesize_ratio_range, - input_size=input_size, - scale_major=scale_major) - self.centers = [((stride - 1) / 2., (stride - 1) / 2.) - for stride in strides] - self.base_anchors = self.gen_base_anchors() - - -@PRIOR_GENERATORS.register_module() -class YOLOAnchorGenerator(AnchorGenerator): - """Anchor generator for YOLO. - - Args: - strides (list[int] | list[tuple[int, int]]): Strides of anchors - in multiple feature levels. - base_sizes (list[list[tuple[int, int]]]): The basic sizes - of anchors in multiple levels. - """ - - def __init__(self, strides, base_sizes): - self.strides = [_pair(stride) for stride in strides] - self.centers = [(stride[0] / 2., stride[1] / 2.) - for stride in self.strides] - self.base_sizes = [] - num_anchor_per_level = len(base_sizes[0]) - for base_sizes_per_level in base_sizes: - assert num_anchor_per_level == len(base_sizes_per_level) - self.base_sizes.append( - [_pair(base_size) for base_size in base_sizes_per_level]) - self.base_anchors = self.gen_base_anchors() - - @property - def num_levels(self): - """int: number of feature levels that the generator will be applied""" - return len(self.base_sizes) - - def gen_base_anchors(self): - """Generate base anchors. - - Returns: - list(torch.Tensor): Base anchors of a feature grid in multiple \ - feature levels. - """ - multi_level_base_anchors = [] - for i, base_sizes_per_level in enumerate(self.base_sizes): - center = None - if self.centers is not None: - center = self.centers[i] - multi_level_base_anchors.append( - self.gen_single_level_base_anchors(base_sizes_per_level, - center)) - return multi_level_base_anchors - - def gen_single_level_base_anchors(self, base_sizes_per_level, center=None): - """Generate base anchors of a single level. - - Args: - base_sizes_per_level (list[tuple[int, int]]): Basic sizes of - anchors. - center (tuple[float], optional): The center of the base anchor - related to a single feature grid. Defaults to None. - - Returns: - torch.Tensor: Anchors in a single-level feature maps. - """ - x_center, y_center = center - base_anchors = [] - for base_size in base_sizes_per_level: - w, h = base_size - - # use float anchor and the anchor's center is aligned with the - # pixel center - base_anchor = torch.Tensor([ - x_center - 0.5 * w, y_center - 0.5 * h, x_center + 0.5 * w, - y_center + 0.5 * h - ]) - base_anchors.append(base_anchor) - base_anchors = torch.stack(base_anchors, dim=0) - - return base_anchors - - def responsible_flags(self, featmap_sizes, gt_bboxes, device='cuda'): - """Generate responsible anchor flags of grid cells in multiple scales. - - Args: - featmap_sizes (list(tuple)): List of feature map sizes in multiple - feature levels. - gt_bboxes (Tensor): Ground truth boxes, shape (n, 4). - device (str): Device where the anchors will be put on. - - Return: - list(torch.Tensor): responsible flags of anchors in multiple level - """ - assert self.num_levels == len(featmap_sizes) - multi_level_responsible_flags = [] - for i in range(self.num_levels): - anchor_stride = self.strides[i] - flags = self.single_level_responsible_flags( - featmap_sizes[i], - gt_bboxes, - anchor_stride, - self.num_base_anchors[i], - device=device) - multi_level_responsible_flags.append(flags) - return multi_level_responsible_flags - - def single_level_responsible_flags(self, - featmap_size, - gt_bboxes, - stride, - num_base_anchors, - device='cuda'): - """Generate the responsible flags of anchor in a single feature map. - - Args: - featmap_size (tuple[int]): The size of feature maps. - gt_bboxes (Tensor): Ground truth boxes, shape (n, 4). - stride (tuple(int)): stride of current level - num_base_anchors (int): The number of base anchors. - device (str, optional): Device where the flags will be put on. - Defaults to 'cuda'. - - Returns: - torch.Tensor: The valid flags of each anchor in a single level \ - feature map. - """ - feat_h, feat_w = featmap_size - gt_bboxes_cx = ((gt_bboxes[:, 0] + gt_bboxes[:, 2]) * 0.5).to(device) - gt_bboxes_cy = ((gt_bboxes[:, 1] + gt_bboxes[:, 3]) * 0.5).to(device) - gt_bboxes_grid_x = torch.floor(gt_bboxes_cx / stride[0]).long() - gt_bboxes_grid_y = torch.floor(gt_bboxes_cy / stride[1]).long() - - # row major indexing - gt_bboxes_grid_idx = gt_bboxes_grid_y * feat_w + gt_bboxes_grid_x - - responsible_grid = torch.zeros( - feat_h * feat_w, dtype=torch.uint8, device=device) - responsible_grid[gt_bboxes_grid_idx] = 1 - - responsible_grid = responsible_grid[:, None].expand( - responsible_grid.size(0), num_base_anchors).contiguous().view(-1) - return responsible_grid diff --git a/cv/detection/autoassign/pytorch/mmdet/core/anchor/builder.py b/cv/detection/autoassign/pytorch/mmdet/core/anchor/builder.py deleted file mode 100755 index a584c358b..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/anchor/builder.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -from mmcv.utils import Registry, build_from_cfg - -PRIOR_GENERATORS = Registry('Generator for anchors and points') - -ANCHOR_GENERATORS = PRIOR_GENERATORS - - -def build_prior_generator(cfg, default_args=None): - return build_from_cfg(cfg, PRIOR_GENERATORS, default_args) - - -def build_anchor_generator(cfg, default_args=None): - warnings.warn( - '``build_anchor_generator`` would be deprecated soon, please use ' - '``build_prior_generator`` ') - return build_prior_generator(cfg, default_args=default_args) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/anchor/point_generator.py b/cv/detection/autoassign/pytorch/mmdet/core/anchor/point_generator.py deleted file mode 100755 index 309c17c99..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/anchor/point_generator.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -from torch.nn.modules.utils import _pair - -from .builder import PRIOR_GENERATORS - - -@PRIOR_GENERATORS.register_module() -class PointGenerator: - - def _meshgrid(self, x, y, row_major=True): - xx = x.repeat(len(y)) - yy = y.view(-1, 1).repeat(1, len(x)).view(-1) - if row_major: - return xx, yy - else: - return yy, xx - - def grid_points(self, featmap_size, stride=16, device='cuda'): - feat_h, feat_w = featmap_size - shift_x = torch.arange(0., feat_w, device=device) * stride - shift_y = torch.arange(0., feat_h, device=device) * stride - shift_xx, shift_yy = self._meshgrid(shift_x, shift_y) - stride = shift_x.new_full((shift_xx.shape[0], ), stride) - shifts = torch.stack([shift_xx, shift_yy, stride], dim=-1) - all_points = shifts.to(device) - return all_points - - def valid_flags(self, featmap_size, valid_size, device='cuda'): - feat_h, feat_w = featmap_size - valid_h, valid_w = valid_size - assert valid_h <= feat_h and valid_w <= feat_w - valid_x = torch.zeros(feat_w, dtype=torch.bool, device=device) - valid_y = torch.zeros(feat_h, dtype=torch.bool, device=device) - valid_x[:valid_w] = 1 - valid_y[:valid_h] = 1 - valid_xx, valid_yy = self._meshgrid(valid_x, valid_y) - valid = valid_xx & valid_yy - return valid - - -@PRIOR_GENERATORS.register_module() -class MlvlPointGenerator: - """Standard points generator for multi-level (Mlvl) feature maps in 2D - points-based detectors. - - Args: - strides (list[int] | list[tuple[int, int]]): Strides of anchors - in multiple feature levels in order (w, h). - offset (float): The offset of points, the value is normalized with - corresponding stride. Defaults to 0.5. - """ - - def __init__(self, strides, offset=0.5): - self.strides = [_pair(stride) for stride in strides] - self.offset = offset - - @property - def num_levels(self): - """int: number of feature levels that the generator will be applied""" - return len(self.strides) - - @property - def num_base_priors(self): - """list[int]: The number of priors (points) at a point - on the feature grid""" - return [1 for _ in range(len(self.strides))] - - def _meshgrid(self, x, y, row_major=True): - yy, xx = torch.meshgrid(y, x) - if row_major: - # warning .flatten() would cause error in ONNX exporting - # have to use reshape here - return xx.reshape(-1), yy.reshape(-1) - - else: - return yy.reshape(-1), xx.reshape(-1) - - def grid_priors(self, - featmap_sizes, - dtype=torch.float32, - device='cuda', - with_stride=False): - """Generate grid points of multiple feature levels. - - Args: - featmap_sizes (list[tuple]): List of feature map sizes in - multiple feature levels, each size arrange as - as (h, w). - dtype (:obj:`dtype`): Dtype of priors. Default: torch.float32. - device (str): The device where the anchors will be put on. - with_stride (bool): Whether to concatenate the stride to - the last dimension of points. - - Return: - list[torch.Tensor]: Points of multiple feature levels. - The sizes of each tensor should be (N, 2) when with stride is - ``False``, where N = width * height, width and height - are the sizes of the corresponding feature level, - and the last dimension 2 represent (coord_x, coord_y), - otherwise the shape should be (N, 4), - and the last dimension 4 represent - (coord_x, coord_y, stride_w, stride_h). - """ - - assert self.num_levels == len(featmap_sizes) - multi_level_priors = [] - for i in range(self.num_levels): - priors = self.single_level_grid_priors( - featmap_sizes[i], - level_idx=i, - dtype=dtype, - device=device, - with_stride=with_stride) - multi_level_priors.append(priors) - return multi_level_priors - - def single_level_grid_priors(self, - featmap_size, - level_idx, - dtype=torch.float32, - device='cuda', - with_stride=False): - """Generate grid Points of a single level. - - Note: - This function is usually called by method ``self.grid_priors``. - - Args: - featmap_size (tuple[int]): Size of the feature maps, arrange as - (h, w). - level_idx (int): The index of corresponding feature map level. - dtype (:obj:`dtype`): Dtype of priors. Default: torch.float32. - device (str, optional): The device the tensor will be put on. - Defaults to 'cuda'. - with_stride (bool): Concatenate the stride to the last dimension - of points. - - Return: - Tensor: Points of single feature levels. - The shape of tensor should be (N, 2) when with stride is - ``False``, where N = width * height, width and height - are the sizes of the corresponding feature level, - and the last dimension 2 represent (coord_x, coord_y), - otherwise the shape should be (N, 4), - and the last dimension 4 represent - (coord_x, coord_y, stride_w, stride_h). - """ - feat_h, feat_w = featmap_size - stride_w, stride_h = self.strides[level_idx] - shift_x = (torch.arange(0, feat_w, device=device) + - self.offset) * stride_w - # keep featmap_size as Tensor instead of int, so that we - # can convert to ONNX correctly - shift_x = shift_x.to(dtype) - - shift_y = (torch.arange(0, feat_h, device=device) + - self.offset) * stride_h - # keep featmap_size as Tensor instead of int, so that we - # can convert to ONNX correctly - shift_y = shift_y.to(dtype) - shift_xx, shift_yy = self._meshgrid(shift_x, shift_y) - if not with_stride: - shifts = torch.stack([shift_xx, shift_yy], dim=-1) - else: - # use `shape[0]` instead of `len(shift_xx)` for ONNX export - stride_w = shift_xx.new_full((shift_xx.shape[0], ), - stride_w).to(dtype) - stride_h = shift_xx.new_full((shift_yy.shape[0], ), - stride_h).to(dtype) - shifts = torch.stack([shift_xx, shift_yy, stride_w, stride_h], - dim=-1) - all_points = shifts.to(device) - return all_points - - def valid_flags(self, featmap_sizes, pad_shape, device='cuda'): - """Generate valid flags of points of multiple feature levels. - - Args: - featmap_sizes (list(tuple)): List of feature map sizes in - multiple feature levels, each size arrange as - as (h, w). - pad_shape (tuple(int)): The padded shape of the image, - arrange as (h, w). - device (str): The device where the anchors will be put on. - - Return: - list(torch.Tensor): Valid flags of points of multiple levels. - """ - assert self.num_levels == len(featmap_sizes) - multi_level_flags = [] - for i in range(self.num_levels): - point_stride = self.strides[i] - feat_h, feat_w = featmap_sizes[i] - h, w = pad_shape[:2] - valid_feat_h = min(int(np.ceil(h / point_stride[1])), feat_h) - valid_feat_w = min(int(np.ceil(w / point_stride[0])), feat_w) - flags = self.single_level_valid_flags((feat_h, feat_w), - (valid_feat_h, valid_feat_w), - device=device) - multi_level_flags.append(flags) - return multi_level_flags - - def single_level_valid_flags(self, - featmap_size, - valid_size, - device='cuda'): - """Generate the valid flags of points of a single feature map. - - Args: - featmap_size (tuple[int]): The size of feature maps, arrange as - as (h, w). - valid_size (tuple[int]): The valid size of the feature maps. - The size arrange as as (h, w). - device (str, optional): The device where the flags will be put on. - Defaults to 'cuda'. - - Returns: - torch.Tensor: The valid flags of each points in a single level \ - feature map. - """ - feat_h, feat_w = featmap_size - valid_h, valid_w = valid_size - assert valid_h <= feat_h and valid_w <= feat_w - valid_x = torch.zeros(feat_w, dtype=torch.bool, device=device) - valid_y = torch.zeros(feat_h, dtype=torch.bool, device=device) - valid_x[:valid_w] = 1 - valid_y[:valid_h] = 1 - valid_xx, valid_yy = self._meshgrid(valid_x, valid_y) - valid = valid_xx & valid_yy - return valid - - def sparse_priors(self, - prior_idxs, - featmap_size, - level_idx, - dtype=torch.float32, - device='cuda'): - """Generate sparse points according to the ``prior_idxs``. - - Args: - prior_idxs (Tensor): The index of corresponding anchors - in the feature map. - featmap_size (tuple[int]): feature map size arrange as (w, h). - level_idx (int): The level index of corresponding feature - map. - dtype (obj:`torch.dtype`): Date type of points. Defaults to - ``torch.float32``. - device (obj:`torch.device`): The device where the points is - located. - Returns: - Tensor: Anchor with shape (N, 2), N should be equal to - the length of ``prior_idxs``. And last dimension - 2 represent (coord_x, coord_y). - """ - height, width = featmap_size - x = (prior_idxs % width + self.offset) * self.strides[level_idx][0] - y = ((prior_idxs // width) % height + - self.offset) * self.strides[level_idx][1] - prioris = torch.stack([x, y], 1).to(dtype) - prioris = prioris.to(device) - return prioris diff --git a/cv/detection/autoassign/pytorch/mmdet/core/anchor/utils.py b/cv/detection/autoassign/pytorch/mmdet/core/anchor/utils.py deleted file mode 100755 index 2138a3a39..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/anchor/utils.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def images_to_levels(target, num_levels): - """Convert targets by image to targets by feature level. - - [target_img0, target_img1] -> [target_level0, target_level1, ...] - """ - target = torch.stack(target, 0) - level_targets = [] - start = 0 - for n in num_levels: - end = start + n - # level_targets.append(target[:, start:end].squeeze(0)) - level_targets.append(target[:, start:end]) - start = end - return level_targets - - -def anchor_inside_flags(flat_anchors, - valid_flags, - img_shape, - allowed_border=0): - """Check whether the anchors are inside the border. - - Args: - flat_anchors (torch.Tensor): Flatten anchors, shape (n, 4). - valid_flags (torch.Tensor): An existing valid flags of anchors. - img_shape (tuple(int)): Shape of current image. - allowed_border (int, optional): The border to allow the valid anchor. - Defaults to 0. - - Returns: - torch.Tensor: Flags indicating whether the anchors are inside a \ - valid range. - """ - img_h, img_w = img_shape[:2] - if allowed_border >= 0: - inside_flags = valid_flags & \ - (flat_anchors[:, 0] >= -allowed_border) & \ - (flat_anchors[:, 1] >= -allowed_border) & \ - (flat_anchors[:, 2] < img_w + allowed_border) & \ - (flat_anchors[:, 3] < img_h + allowed_border) - else: - inside_flags = valid_flags - return inside_flags - - -def calc_region(bbox, ratio, featmap_size=None): - """Calculate a proportional bbox region. - - The bbox center are fixed and the new h' and w' is h * ratio and w * ratio. - - Args: - bbox (Tensor): Bboxes to calculate regions, shape (n, 4). - ratio (float): Ratio of the output region. - featmap_size (tuple): Feature map size used for clipping the boundary. - - Returns: - tuple: x1, y1, x2, y2 - """ - x1 = torch.round((1 - ratio) * bbox[0] + ratio * bbox[2]).long() - y1 = torch.round((1 - ratio) * bbox[1] + ratio * bbox[3]).long() - x2 = torch.round(ratio * bbox[0] + (1 - ratio) * bbox[2]).long() - y2 = torch.round(ratio * bbox[1] + (1 - ratio) * bbox[3]).long() - if featmap_size is not None: - x1 = x1.clamp(min=0, max=featmap_size[1]) - y1 = y1.clamp(min=0, max=featmap_size[0]) - x2 = x2.clamp(min=0, max=featmap_size[1]) - y2 = y2.clamp(min=0, max=featmap_size[0]) - return (x1, y1, x2, y2) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/__init__.py deleted file mode 100755 index 68caa1afc..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .assigners import (AssignResult, BaseAssigner, CenterRegionAssigner, - MaxIoUAssigner, RegionAssigner) -from .builder import build_assigner, build_bbox_coder, build_sampler -from .coder import (BaseBBoxCoder, DeltaXYWHBBoxCoder, DistancePointBBoxCoder, - PseudoBBoxCoder, TBLRBBoxCoder) -from .iou_calculators import BboxOverlaps2D, bbox_overlaps -from .samplers import (BaseSampler, CombinedSampler, - InstanceBalancedPosSampler, IoUBalancedNegSampler, - OHEMSampler, PseudoSampler, RandomSampler, - SamplingResult, ScoreHLRSampler) -from .transforms import (bbox2distance, bbox2result, bbox2roi, - bbox_cxcywh_to_xyxy, bbox_flip, bbox_mapping, - bbox_mapping_back, bbox_rescale, bbox_xyxy_to_cxcywh, - distance2bbox, find_inside_bboxes, roi2bbox) - -__all__ = [ - 'bbox_overlaps', 'BboxOverlaps2D', 'BaseAssigner', 'MaxIoUAssigner', - 'AssignResult', 'BaseSampler', 'PseudoSampler', 'RandomSampler', - 'InstanceBalancedPosSampler', 'IoUBalancedNegSampler', 'CombinedSampler', - 'OHEMSampler', 'SamplingResult', 'ScoreHLRSampler', 'build_assigner', - 'build_sampler', 'bbox_flip', 'bbox_mapping', 'bbox_mapping_back', - 'bbox2roi', 'roi2bbox', 'bbox2result', 'distance2bbox', 'bbox2distance', - 'build_bbox_coder', 'BaseBBoxCoder', 'PseudoBBoxCoder', - 'DeltaXYWHBBoxCoder', 'TBLRBBoxCoder', 'DistancePointBBoxCoder', - 'CenterRegionAssigner', 'bbox_rescale', 'bbox_cxcywh_to_xyxy', - 'bbox_xyxy_to_cxcywh', 'RegionAssigner', 'find_inside_bboxes' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/__init__.py deleted file mode 100755 index d5c7146c4..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .approx_max_iou_assigner import ApproxMaxIoUAssigner -from .assign_result import AssignResult -from .atss_assigner import ATSSAssigner -from .base_assigner import BaseAssigner -from .center_region_assigner import CenterRegionAssigner -from .grid_assigner import GridAssigner -from .hungarian_assigner import HungarianAssigner -from .mask_hungarian_assigner import MaskHungarianAssigner -from .max_iou_assigner import MaxIoUAssigner -from .point_assigner import PointAssigner -from .region_assigner import RegionAssigner -from .sim_ota_assigner import SimOTAAssigner -from .task_aligned_assigner import TaskAlignedAssigner -from .uniform_assigner import UniformAssigner - -__all__ = [ - 'BaseAssigner', 'MaxIoUAssigner', 'ApproxMaxIoUAssigner', 'AssignResult', - 'PointAssigner', 'ATSSAssigner', 'CenterRegionAssigner', 'GridAssigner', - 'HungarianAssigner', 'RegionAssigner', 'UniformAssigner', 'SimOTAAssigner', - 'TaskAlignedAssigner', 'MaskHungarianAssigner' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/approx_max_iou_assigner.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/approx_max_iou_assigner.py deleted file mode 100755 index 6278e7b36..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/approx_max_iou_assigner.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from .max_iou_assigner import MaxIoUAssigner - - -@BBOX_ASSIGNERS.register_module() -class ApproxMaxIoUAssigner(MaxIoUAssigner): - """Assign a corresponding gt bbox or background to each bbox. - - Each proposals will be assigned with an integer indicating the ground truth - index. (semi-positive index: gt label (0-based), -1: background) - - - -1: negative sample, no assigned gt - - semi-positive integer: positive sample, index (0-based) of assigned gt - - Args: - pos_iou_thr (float): IoU threshold for positive bboxes. - neg_iou_thr (float or tuple): IoU threshold for negative bboxes. - min_pos_iou (float): Minimum iou for a bbox to be considered as a - positive bbox. Positive samples can have smaller IoU than - pos_iou_thr due to the 4th step (assign max IoU sample to each gt). - gt_max_assign_all (bool): Whether to assign all bboxes with the same - highest overlap with some gt to that gt. - ignore_iof_thr (float): IoF threshold for ignoring bboxes (if - `gt_bboxes_ignore` is specified). Negative values mean not - ignoring any bboxes. - ignore_wrt_candidates (bool): Whether to compute the iof between - `bboxes` and `gt_bboxes_ignore`, or the contrary. - match_low_quality (bool): Whether to allow quality matches. This is - usually allowed for RPN and single stage detectors, but not allowed - in the second stage. - gpu_assign_thr (int): The upper bound of the number of GT for GPU - assign. When the number of gt is above this threshold, will assign - on CPU device. Negative values mean not assign on CPU. - """ - - def __init__(self, - pos_iou_thr, - neg_iou_thr, - min_pos_iou=.0, - gt_max_assign_all=True, - ignore_iof_thr=-1, - ignore_wrt_candidates=True, - match_low_quality=True, - gpu_assign_thr=-1, - iou_calculator=dict(type='BboxOverlaps2D')): - self.pos_iou_thr = pos_iou_thr - self.neg_iou_thr = neg_iou_thr - self.min_pos_iou = min_pos_iou - self.gt_max_assign_all = gt_max_assign_all - self.ignore_iof_thr = ignore_iof_thr - self.ignore_wrt_candidates = ignore_wrt_candidates - self.gpu_assign_thr = gpu_assign_thr - self.match_low_quality = match_low_quality - self.iou_calculator = build_iou_calculator(iou_calculator) - - def assign(self, - approxs, - squares, - approxs_per_octave, - gt_bboxes, - gt_bboxes_ignore=None, - gt_labels=None): - """Assign gt to approxs. - - This method assign a gt bbox to each group of approxs (bboxes), - each group of approxs is represent by a base approx (bbox) and - will be assigned with -1, or a semi-positive number. - background_label (-1) means negative sample, - semi-positive number is the index (0-based) of assigned gt. - The assignment is done in following steps, the order matters. - - 1. assign every bbox to background_label (-1) - 2. use the max IoU of each group of approxs to assign - 2. assign proposals whose iou with all gts < neg_iou_thr to background - 3. for each bbox, if the iou with its nearest gt >= pos_iou_thr, - assign it to that bbox - 4. for each gt bbox, assign its nearest proposals (may be more than - one) to itself - - Args: - approxs (Tensor): Bounding boxes to be assigned, - shape(approxs_per_octave*n, 4). - squares (Tensor): Base Bounding boxes to be assigned, - shape(n, 4). - approxs_per_octave (int): number of approxs per octave - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - - Returns: - :obj:`AssignResult`: The assign result. - """ - num_squares = squares.size(0) - num_gts = gt_bboxes.size(0) - - if num_squares == 0 or num_gts == 0: - # No predictions and/or truth, return empty assignment - overlaps = approxs.new(num_gts, num_squares) - assign_result = self.assign_wrt_overlaps(overlaps, gt_labels) - return assign_result - - # re-organize anchors by approxs_per_octave x num_squares - approxs = torch.transpose( - approxs.view(num_squares, approxs_per_octave, 4), 0, - 1).contiguous().view(-1, 4) - assign_on_cpu = True if (self.gpu_assign_thr > 0) and ( - num_gts > self.gpu_assign_thr) else False - # compute overlap and assign gt on CPU when number of GT is large - if assign_on_cpu: - device = approxs.device - approxs = approxs.cpu() - gt_bboxes = gt_bboxes.cpu() - if gt_bboxes_ignore is not None: - gt_bboxes_ignore = gt_bboxes_ignore.cpu() - if gt_labels is not None: - gt_labels = gt_labels.cpu() - all_overlaps = self.iou_calculator(approxs, gt_bboxes) - - overlaps, _ = all_overlaps.view(approxs_per_octave, num_squares, - num_gts).max(dim=0) - overlaps = torch.transpose(overlaps, 0, 1) - - if (self.ignore_iof_thr > 0 and gt_bboxes_ignore is not None - and gt_bboxes_ignore.numel() > 0 and squares.numel() > 0): - if self.ignore_wrt_candidates: - ignore_overlaps = self.iou_calculator( - squares, gt_bboxes_ignore, mode='iof') - ignore_max_overlaps, _ = ignore_overlaps.max(dim=1) - else: - ignore_overlaps = self.iou_calculator( - gt_bboxes_ignore, squares, mode='iof') - ignore_max_overlaps, _ = ignore_overlaps.max(dim=0) - overlaps[:, ignore_max_overlaps > self.ignore_iof_thr] = -1 - - assign_result = self.assign_wrt_overlaps(overlaps, gt_labels) - if assign_on_cpu: - assign_result.gt_inds = assign_result.gt_inds.to(device) - assign_result.max_overlaps = assign_result.max_overlaps.to(device) - if assign_result.labels is not None: - assign_result.labels = assign_result.labels.to(device) - return assign_result diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/assign_result.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/assign_result.py deleted file mode 100755 index 50c1e1626..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/assign_result.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmdet.utils import util_mixins - - -class AssignResult(util_mixins.NiceRepr): - """Stores assignments between predicted and truth boxes. - - Attributes: - num_gts (int): the number of truth boxes considered when computing this - assignment - - gt_inds (LongTensor): for each predicted box indicates the 1-based - index of the assigned truth box. 0 means unassigned and -1 means - ignore. - - max_overlaps (FloatTensor): the iou between the predicted box and its - assigned truth box. - - labels (None | LongTensor): If specified, for each predicted box - indicates the category label of the assigned truth box. - - Example: - >>> # An assign result between 4 predicted boxes and 9 true boxes - >>> # where only two boxes were assigned. - >>> num_gts = 9 - >>> max_overlaps = torch.LongTensor([0, .5, .9, 0]) - >>> gt_inds = torch.LongTensor([-1, 1, 2, 0]) - >>> labels = torch.LongTensor([0, 3, 4, 0]) - >>> self = AssignResult(num_gts, gt_inds, max_overlaps, labels) - >>> print(str(self)) # xdoctest: +IGNORE_WANT - - >>> # Force addition of gt labels (when adding gt as proposals) - >>> new_labels = torch.LongTensor([3, 4, 5]) - >>> self.add_gt_(new_labels) - >>> print(str(self)) # xdoctest: +IGNORE_WANT - - """ - - def __init__(self, num_gts, gt_inds, max_overlaps, labels=None): - self.num_gts = num_gts - self.gt_inds = gt_inds - self.max_overlaps = max_overlaps - self.labels = labels - # Interface for possible user-defined properties - self._extra_properties = {} - - @property - def num_preds(self): - """int: the number of predictions in this assignment""" - return len(self.gt_inds) - - def set_extra_property(self, key, value): - """Set user-defined new property.""" - assert key not in self.info - self._extra_properties[key] = value - - def get_extra_property(self, key): - """Get user-defined property.""" - return self._extra_properties.get(key, None) - - @property - def info(self): - """dict: a dictionary of info about the object""" - basic_info = { - 'num_gts': self.num_gts, - 'num_preds': self.num_preds, - 'gt_inds': self.gt_inds, - 'max_overlaps': self.max_overlaps, - 'labels': self.labels, - } - basic_info.update(self._extra_properties) - return basic_info - - def __nice__(self): - """str: a "nice" summary string describing this assign result""" - parts = [] - parts.append(f'num_gts={self.num_gts!r}') - if self.gt_inds is None: - parts.append(f'gt_inds={self.gt_inds!r}') - else: - parts.append(f'gt_inds.shape={tuple(self.gt_inds.shape)!r}') - if self.max_overlaps is None: - parts.append(f'max_overlaps={self.max_overlaps!r}') - else: - parts.append('max_overlaps.shape=' - f'{tuple(self.max_overlaps.shape)!r}') - if self.labels is None: - parts.append(f'labels={self.labels!r}') - else: - parts.append(f'labels.shape={tuple(self.labels.shape)!r}') - return ', '.join(parts) - - @classmethod - def random(cls, **kwargs): - """Create random AssignResult for tests or debugging. - - Args: - num_preds: number of predicted boxes - num_gts: number of true boxes - p_ignore (float): probability of a predicted box assigned to an - ignored truth - p_assigned (float): probability of a predicted box not being - assigned - p_use_label (float | bool): with labels or not - rng (None | int | numpy.random.RandomState): seed or state - - Returns: - :obj:`AssignResult`: Randomly generated assign results. - - Example: - >>> from mmdet.core.bbox.assigners.assign_result import * # NOQA - >>> self = AssignResult.random() - >>> print(self.info) - """ - from mmdet.core.bbox import demodata - rng = demodata.ensure_rng(kwargs.get('rng', None)) - - num_gts = kwargs.get('num_gts', None) - num_preds = kwargs.get('num_preds', None) - p_ignore = kwargs.get('p_ignore', 0.3) - p_assigned = kwargs.get('p_assigned', 0.7) - p_use_label = kwargs.get('p_use_label', 0.5) - num_classes = kwargs.get('p_use_label', 3) - - if num_gts is None: - num_gts = rng.randint(0, 8) - if num_preds is None: - num_preds = rng.randint(0, 16) - - if num_gts == 0: - max_overlaps = torch.zeros(num_preds, dtype=torch.float32) - gt_inds = torch.zeros(num_preds, dtype=torch.int64) - if p_use_label is True or p_use_label < rng.rand(): - labels = torch.zeros(num_preds, dtype=torch.int64) - else: - labels = None - else: - import numpy as np - - # Create an overlap for each predicted box - max_overlaps = torch.from_numpy(rng.rand(num_preds)) - - # Construct gt_inds for each predicted box - is_assigned = torch.from_numpy(rng.rand(num_preds) < p_assigned) - # maximum number of assignments constraints - n_assigned = min(num_preds, min(num_gts, is_assigned.sum())) - - assigned_idxs = np.where(is_assigned)[0] - rng.shuffle(assigned_idxs) - assigned_idxs = assigned_idxs[0:n_assigned] - assigned_idxs.sort() - - is_assigned[:] = 0 - is_assigned[assigned_idxs] = True - - is_ignore = torch.from_numpy( - rng.rand(num_preds) < p_ignore) & is_assigned - - gt_inds = torch.zeros(num_preds, dtype=torch.int64) - - true_idxs = np.arange(num_gts) - rng.shuffle(true_idxs) - true_idxs = torch.from_numpy(true_idxs) - gt_inds[is_assigned] = true_idxs[:n_assigned].long() - - gt_inds = torch.from_numpy( - rng.randint(1, num_gts + 1, size=num_preds)) - gt_inds[is_ignore] = -1 - gt_inds[~is_assigned] = 0 - max_overlaps[~is_assigned] = 0 - - if p_use_label is True or p_use_label < rng.rand(): - if num_classes == 0: - labels = torch.zeros(num_preds, dtype=torch.int64) - else: - labels = torch.from_numpy( - # remind that we set FG labels to [0, num_class-1] - # since mmdet v2.0 - # BG cat_id: num_class - rng.randint(0, num_classes, size=num_preds)) - labels[~is_assigned] = 0 - else: - labels = None - - self = cls(num_gts, gt_inds, max_overlaps, labels) - return self - - def add_gt_(self, gt_labels): - """Add ground truth as assigned results. - - Args: - gt_labels (torch.Tensor): Labels of gt boxes - """ - self_inds = torch.arange( - 1, len(gt_labels) + 1, dtype=torch.long, device=gt_labels.device) - self.gt_inds = torch.cat([self_inds, self.gt_inds]) - - self.max_overlaps = torch.cat( - [self.max_overlaps.new_ones(len(gt_labels)), self.max_overlaps]) - - if self.labels is not None: - self.labels = torch.cat([gt_labels, self.labels]) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/atss_assigner.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/atss_assigner.py deleted file mode 100755 index f05f2f672..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/atss_assigner.py +++ /dev/null @@ -1,234 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -@BBOX_ASSIGNERS.register_module() -class ATSSAssigner(BaseAssigner): - """Assign a corresponding gt bbox or background to each bbox. - - Each proposals will be assigned with `0` or a positive integer - indicating the ground truth index. - - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - - If ``alpha`` is not None, it means that the dynamic cost - ATSSAssigner is adopted, which is currently only used in the DDOD. - - Args: - topk (float): number of bbox selected in each level - """ - - def __init__(self, - topk, - alpha=None, - iou_calculator=dict(type='BboxOverlaps2D'), - ignore_iof_thr=-1): - self.topk = topk - self.alpha = alpha - self.iou_calculator = build_iou_calculator(iou_calculator) - self.ignore_iof_thr = ignore_iof_thr - - """Assign a corresponding gt bbox or background to each bbox. - - Args: - topk (int): number of bbox selected in each level. - alpha (float): param of cost rate for each proposal only in DDOD. - Default None. - iou_calculator (dict): builder of IoU calculator. - Default dict(type='BboxOverlaps2D'). - ignore_iof_thr (int): whether ignore max overlaps or not. - Default -1 (1 or -1). - """ - - # https://github.com/sfzhang15/ATSS/blob/master/atss_core/modeling/rpn/atss/loss.py - def assign(self, - bboxes, - num_level_bboxes, - gt_bboxes, - gt_bboxes_ignore=None, - gt_labels=None, - cls_scores=None, - bbox_preds=None): - """Assign gt to bboxes. - - The assignment is done in following steps - - 1. compute iou between all bbox (bbox of all pyramid levels) and gt - 2. compute center distance between all bbox and gt - 3. on each pyramid level, for each gt, select k bbox whose center - are closest to the gt center, so we total select k*l bbox as - candidates for each gt - 4. get corresponding iou for the these candidates, and compute the - mean and std, set mean + std as the iou threshold - 5. select these candidates whose iou are greater than or equal to - the threshold as positive - 6. limit the positive sample's center in gt - - If ``alpha`` is not None, and ``cls_scores`` and `bbox_preds` - are not None, the overlaps calculation in the first step - will also include dynamic cost, which is currently only used in - the DDOD. - - Args: - bboxes (Tensor): Bounding boxes to be assigned, shape(n, 4). - num_level_bboxes (List): num of bboxes in each level - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. Default None. - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - cls_scores (list[Tensor]): Classification scores for all scale - levels, each is a 4D-tensor, the channels number is - num_base_priors * num_classes. Default None. - bbox_preds (list[Tensor]): Box energies / deltas for all scale - levels, each is a 4D-tensor, the channels number is - num_base_priors * 4. Default None. - - Returns: - :obj:`AssignResult`: The assign result. - """ - INF = 100000000 - bboxes = bboxes[:, :4] - num_gt, num_bboxes = gt_bboxes.size(0), bboxes.size(0) - - message = 'Invalid alpha parameter because cls_scores or ' \ - 'bbox_preds are None. If you want to use the ' \ - 'cost-based ATSSAssigner, please set cls_scores, ' \ - 'bbox_preds and self.alpha at the same time. ' - - if self.alpha is None: - # ATSSAssigner - overlaps = self.iou_calculator(bboxes, gt_bboxes) - if cls_scores is not None or bbox_preds is not None: - warnings.warn(message) - else: - # Dynamic cost ATSSAssigner in DDOD - assert cls_scores is not None and bbox_preds is not None, message - - # compute cls cost for bbox and GT - cls_cost = torch.sigmoid(cls_scores[:, gt_labels]) - - # compute iou between all bbox and gt - overlaps = self.iou_calculator(bbox_preds, gt_bboxes) - - # make sure that we are in element-wise multiplication - assert cls_cost.shape == overlaps.shape - - # overlaps is actually a cost matrix - overlaps = cls_cost**(1 - self.alpha) * overlaps**self.alpha - - # assign 0 by default - assigned_gt_inds = overlaps.new_full((num_bboxes, ), - 0, - dtype=torch.long) - - if num_gt == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - max_overlaps = overlaps.new_zeros((num_bboxes, )) - if num_gt == 0: - # No truth, assign everything to background - assigned_gt_inds[:] = 0 - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = overlaps.new_full((num_bboxes, ), - -1, - dtype=torch.long) - return AssignResult( - num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels) - - # compute center distance between all bbox and gt - gt_cx = (gt_bboxes[:, 0] + gt_bboxes[:, 2]) / 2.0 - gt_cy = (gt_bboxes[:, 1] + gt_bboxes[:, 3]) / 2.0 - gt_points = torch.stack((gt_cx, gt_cy), dim=1) - - bboxes_cx = (bboxes[:, 0] + bboxes[:, 2]) / 2.0 - bboxes_cy = (bboxes[:, 1] + bboxes[:, 3]) / 2.0 - bboxes_points = torch.stack((bboxes_cx, bboxes_cy), dim=1) - - distances = (bboxes_points[:, None, :] - - gt_points[None, :, :]).pow(2).sum(-1).sqrt() - - if (self.ignore_iof_thr > 0 and gt_bboxes_ignore is not None - and gt_bboxes_ignore.numel() > 0 and bboxes.numel() > 0): - ignore_overlaps = self.iou_calculator( - bboxes, gt_bboxes_ignore, mode='iof') - ignore_max_overlaps, _ = ignore_overlaps.max(dim=1) - ignore_idxs = ignore_max_overlaps > self.ignore_iof_thr - distances[ignore_idxs, :] = INF - assigned_gt_inds[ignore_idxs] = -1 - - # Selecting candidates based on the center distance - candidate_idxs = [] - start_idx = 0 - for level, bboxes_per_level in enumerate(num_level_bboxes): - # on each pyramid level, for each gt, - # select k bbox whose center are closest to the gt center - end_idx = start_idx + bboxes_per_level - distances_per_level = distances[start_idx:end_idx, :] - selectable_k = min(self.topk, bboxes_per_level) - - _, topk_idxs_per_level = distances_per_level.topk( - selectable_k, dim=0, largest=False) - candidate_idxs.append(topk_idxs_per_level + start_idx) - start_idx = end_idx - candidate_idxs = torch.cat(candidate_idxs, dim=0) - - # get corresponding iou for the these candidates, and compute the - # mean and std, set mean + std as the iou threshold - candidate_overlaps = overlaps[candidate_idxs, torch.arange(num_gt)] - overlaps_mean_per_gt = candidate_overlaps.mean(0) - overlaps_std_per_gt = candidate_overlaps.std(0) - overlaps_thr_per_gt = overlaps_mean_per_gt + overlaps_std_per_gt - - is_pos = candidate_overlaps >= overlaps_thr_per_gt[None, :] - - # limit the positive sample's center in gt - for gt_idx in range(num_gt): - candidate_idxs[:, gt_idx] += gt_idx * num_bboxes - ep_bboxes_cx = bboxes_cx.view(1, -1).expand( - num_gt, num_bboxes).contiguous().view(-1) - ep_bboxes_cy = bboxes_cy.view(1, -1).expand( - num_gt, num_bboxes).contiguous().view(-1) - candidate_idxs = candidate_idxs.view(-1) - - # calculate the left, top, right, bottom distance between positive - # bbox center and gt side - l_ = ep_bboxes_cx[candidate_idxs].view(-1, num_gt) - gt_bboxes[:, 0] - t_ = ep_bboxes_cy[candidate_idxs].view(-1, num_gt) - gt_bboxes[:, 1] - r_ = gt_bboxes[:, 2] - ep_bboxes_cx[candidate_idxs].view(-1, num_gt) - b_ = gt_bboxes[:, 3] - ep_bboxes_cy[candidate_idxs].view(-1, num_gt) - is_in_gts = torch.stack([l_, t_, r_, b_], dim=1).min(dim=1)[0] > 0.01 - - is_pos = is_pos & is_in_gts - - # if an anchor box is assigned to multiple gts, - # the one with the highest IoU will be selected. - overlaps_inf = torch.full_like(overlaps, - -INF).t().contiguous().view(-1) - index = candidate_idxs.view(-1)[is_pos.view(-1)] - overlaps_inf[index] = overlaps.t().contiguous().view(-1)[index] - overlaps_inf = overlaps_inf.view(num_gt, -1).t() - - max_overlaps, argmax_overlaps = overlaps_inf.max(dim=1) - assigned_gt_inds[ - max_overlaps != -INF] = argmax_overlaps[max_overlaps != -INF] + 1 - - if gt_labels is not None: - assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1) - pos_inds = torch.nonzero( - assigned_gt_inds > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[ - assigned_gt_inds[pos_inds] - 1] - else: - assigned_labels = None - return AssignResult( - num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/base_assigner.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/base_assigner.py deleted file mode 100755 index 179ac4514..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/base_assigner.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseAssigner(metaclass=ABCMeta): - """Base assigner that assigns boxes to ground truth boxes.""" - - @abstractmethod - def assign(self, bboxes, gt_bboxes, gt_bboxes_ignore=None, gt_labels=None): - """Assign boxes to either a ground truth boxes or a negative boxes.""" diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/center_region_assigner.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/center_region_assigner.py deleted file mode 100755 index 88f33f630..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/center_region_assigner.py +++ /dev/null @@ -1,336 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -def scale_boxes(bboxes, scale): - """Expand an array of boxes by a given scale. - - Args: - bboxes (Tensor): Shape (m, 4) - scale (float): The scale factor of bboxes - - Returns: - (Tensor): Shape (m, 4). Scaled bboxes - """ - assert bboxes.size(1) == 4 - w_half = (bboxes[:, 2] - bboxes[:, 0]) * .5 - h_half = (bboxes[:, 3] - bboxes[:, 1]) * .5 - x_c = (bboxes[:, 2] + bboxes[:, 0]) * .5 - y_c = (bboxes[:, 3] + bboxes[:, 1]) * .5 - - w_half *= scale - h_half *= scale - - boxes_scaled = torch.zeros_like(bboxes) - boxes_scaled[:, 0] = x_c - w_half - boxes_scaled[:, 2] = x_c + w_half - boxes_scaled[:, 1] = y_c - h_half - boxes_scaled[:, 3] = y_c + h_half - return boxes_scaled - - -def is_located_in(points, bboxes): - """Are points located in bboxes. - - Args: - points (Tensor): Points, shape: (m, 2). - bboxes (Tensor): Bounding boxes, shape: (n, 4). - - Return: - Tensor: Flags indicating if points are located in bboxes, shape: (m, n). - """ - assert points.size(1) == 2 - assert bboxes.size(1) == 4 - return (points[:, 0].unsqueeze(1) > bboxes[:, 0].unsqueeze(0)) & \ - (points[:, 0].unsqueeze(1) < bboxes[:, 2].unsqueeze(0)) & \ - (points[:, 1].unsqueeze(1) > bboxes[:, 1].unsqueeze(0)) & \ - (points[:, 1].unsqueeze(1) < bboxes[:, 3].unsqueeze(0)) - - -def bboxes_area(bboxes): - """Compute the area of an array of bboxes. - - Args: - bboxes (Tensor): The coordinates ox bboxes. Shape: (m, 4) - - Returns: - Tensor: Area of the bboxes. Shape: (m, ) - """ - assert bboxes.size(1) == 4 - w = (bboxes[:, 2] - bboxes[:, 0]) - h = (bboxes[:, 3] - bboxes[:, 1]) - areas = w * h - return areas - - -@BBOX_ASSIGNERS.register_module() -class CenterRegionAssigner(BaseAssigner): - """Assign pixels at the center region of a bbox as positive. - - Each proposals will be assigned with `-1`, `0`, or a positive integer - indicating the ground truth index. - - -1: negative samples - - semi-positive numbers: positive sample, index (0-based) of assigned gt - - Args: - pos_scale (float): Threshold within which pixels are - labelled as positive. - neg_scale (float): Threshold above which pixels are - labelled as positive. - min_pos_iof (float): Minimum iof of a pixel with a gt to be - labelled as positive. Default: 1e-2 - ignore_gt_scale (float): Threshold within which the pixels - are ignored when the gt is labelled as shadowed. Default: 0.5 - foreground_dominate (bool): If True, the bbox will be assigned as - positive when a gt's kernel region overlaps with another's shadowed - (ignored) region, otherwise it is set as ignored. Default to False. - """ - - def __init__(self, - pos_scale, - neg_scale, - min_pos_iof=1e-2, - ignore_gt_scale=0.5, - foreground_dominate=False, - iou_calculator=dict(type='BboxOverlaps2D')): - self.pos_scale = pos_scale - self.neg_scale = neg_scale - self.min_pos_iof = min_pos_iof - self.ignore_gt_scale = ignore_gt_scale - self.foreground_dominate = foreground_dominate - self.iou_calculator = build_iou_calculator(iou_calculator) - - def get_gt_priorities(self, gt_bboxes): - """Get gt priorities according to their areas. - - Smaller gt has higher priority. - - Args: - gt_bboxes (Tensor): Ground truth boxes, shape (k, 4). - - Returns: - Tensor: The priority of gts so that gts with larger priority is \ - more likely to be assigned. Shape (k, ) - """ - gt_areas = bboxes_area(gt_bboxes) - # Rank all gt bbox areas. Smaller objects has larger priority - _, sort_idx = gt_areas.sort(descending=True) - sort_idx = sort_idx.argsort() - return sort_idx - - def assign(self, bboxes, gt_bboxes, gt_bboxes_ignore=None, gt_labels=None): - """Assign gt to bboxes. - - This method assigns gts to every bbox (proposal/anchor), each bbox \ - will be assigned with -1, or a semi-positive number. -1 means \ - negative sample, semi-positive number is the index (0-based) of \ - assigned gt. - - Args: - bboxes (Tensor): Bounding boxes to be assigned, shape(n, 4). - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - gt_labels (tensor, optional): Label of gt_bboxes, shape (num_gts,). - - Returns: - :obj:`AssignResult`: The assigned result. Note that \ - shadowed_labels of shape (N, 2) is also added as an \ - `assign_result` attribute. `shadowed_labels` is a tensor \ - composed of N pairs of anchor_ind, class_label], where N \ - is the number of anchors that lie in the outer region of a \ - gt, anchor_ind is the shadowed anchor index and class_label \ - is the shadowed class label. - - Example: - >>> self = CenterRegionAssigner(0.2, 0.2) - >>> bboxes = torch.Tensor([[0, 0, 10, 10], [10, 10, 20, 20]]) - >>> gt_bboxes = torch.Tensor([[0, 0, 10, 10]]) - >>> assign_result = self.assign(bboxes, gt_bboxes) - >>> expected_gt_inds = torch.LongTensor([1, 0]) - >>> assert torch.all(assign_result.gt_inds == expected_gt_inds) - """ - # There are in total 5 steps in the pixel assignment - # 1. Find core (the center region, say inner 0.2) - # and shadow (the relatively ourter part, say inner 0.2-0.5) - # regions of every gt. - # 2. Find all prior bboxes that lie in gt_core and gt_shadow regions - # 3. Assign prior bboxes in gt_core with a one-hot id of the gt in - # the image. - # 3.1. For overlapping objects, the prior bboxes in gt_core is - # assigned with the object with smallest area - # 4. Assign prior bboxes with class label according to its gt id. - # 4.1. Assign -1 to prior bboxes lying in shadowed gts - # 4.2. Assign positive prior boxes with the corresponding label - # 5. Find pixels lying in the shadow of an object and assign them with - # background label, but set the loss weight of its corresponding - # gt to zero. - assert bboxes.size(1) == 4, 'bboxes must have size of 4' - # 1. Find core positive and shadow region of every gt - gt_core = scale_boxes(gt_bboxes, self.pos_scale) - gt_shadow = scale_boxes(gt_bboxes, self.neg_scale) - - # 2. Find prior bboxes that lie in gt_core and gt_shadow regions - bbox_centers = (bboxes[:, 2:4] + bboxes[:, 0:2]) / 2 - # The center points lie within the gt boxes - is_bbox_in_gt = is_located_in(bbox_centers, gt_bboxes) - # Only calculate bbox and gt_core IoF. This enables small prior bboxes - # to match large gts - bbox_and_gt_core_overlaps = self.iou_calculator( - bboxes, gt_core, mode='iof') - # The center point of effective priors should be within the gt box - is_bbox_in_gt_core = is_bbox_in_gt & ( - bbox_and_gt_core_overlaps > self.min_pos_iof) # shape (n, k) - - is_bbox_in_gt_shadow = ( - self.iou_calculator(bboxes, gt_shadow, mode='iof') > - self.min_pos_iof) - # Rule out center effective positive pixels - is_bbox_in_gt_shadow &= (~is_bbox_in_gt_core) - - num_gts, num_bboxes = gt_bboxes.size(0), bboxes.size(0) - if num_gts == 0 or num_bboxes == 0: - # If no gts exist, assign all pixels to negative - assigned_gt_ids = \ - is_bbox_in_gt_core.new_zeros((num_bboxes,), - dtype=torch.long) - pixels_in_gt_shadow = assigned_gt_ids.new_empty((0, 2)) - else: - # Step 3: assign a one-hot gt id to each pixel, and smaller objects - # have high priority to assign the pixel. - sort_idx = self.get_gt_priorities(gt_bboxes) - assigned_gt_ids, pixels_in_gt_shadow = \ - self.assign_one_hot_gt_indices(is_bbox_in_gt_core, - is_bbox_in_gt_shadow, - gt_priority=sort_idx) - - if gt_bboxes_ignore is not None and gt_bboxes_ignore.numel() > 0: - # No ground truth or boxes, return empty assignment - gt_bboxes_ignore = scale_boxes( - gt_bboxes_ignore, scale=self.ignore_gt_scale) - is_bbox_in_ignored_gts = is_located_in(bbox_centers, - gt_bboxes_ignore) - is_bbox_in_ignored_gts = is_bbox_in_ignored_gts.any(dim=1) - assigned_gt_ids[is_bbox_in_ignored_gts] = -1 - - # 4. Assign prior bboxes with class label according to its gt id. - assigned_labels = None - shadowed_pixel_labels = None - if gt_labels is not None: - # Default assigned label is the background (-1) - assigned_labels = assigned_gt_ids.new_full((num_bboxes, ), -1) - pos_inds = torch.nonzero( - assigned_gt_ids > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[assigned_gt_ids[pos_inds] - - 1] - # 5. Find pixels lying in the shadow of an object - shadowed_pixel_labels = pixels_in_gt_shadow.clone() - if pixels_in_gt_shadow.numel() > 0: - pixel_idx, gt_idx =\ - pixels_in_gt_shadow[:, 0], pixels_in_gt_shadow[:, 1] - assert (assigned_gt_ids[pixel_idx] != gt_idx).all(), \ - 'Some pixels are dually assigned to ignore and gt!' - shadowed_pixel_labels[:, 1] = gt_labels[gt_idx - 1] - override = ( - assigned_labels[pixel_idx] == shadowed_pixel_labels[:, 1]) - if self.foreground_dominate: - # When a pixel is both positive and shadowed, set it as pos - shadowed_pixel_labels = shadowed_pixel_labels[~override] - else: - # When a pixel is both pos and shadowed, set it as shadowed - assigned_labels[pixel_idx[override]] = -1 - assigned_gt_ids[pixel_idx[override]] = 0 - - assign_result = AssignResult( - num_gts, assigned_gt_ids, None, labels=assigned_labels) - # Add shadowed_labels as assign_result property. Shape: (num_shadow, 2) - assign_result.set_extra_property('shadowed_labels', - shadowed_pixel_labels) - return assign_result - - def assign_one_hot_gt_indices(self, - is_bbox_in_gt_core, - is_bbox_in_gt_shadow, - gt_priority=None): - """Assign only one gt index to each prior box. - - Gts with large gt_priority are more likely to be assigned. - - Args: - is_bbox_in_gt_core (Tensor): Bool tensor indicating the bbox center - is in the core area of a gt (e.g. 0-0.2). - Shape: (num_prior, num_gt). - is_bbox_in_gt_shadow (Tensor): Bool tensor indicating the bbox - center is in the shadowed area of a gt (e.g. 0.2-0.5). - Shape: (num_prior, num_gt). - gt_priority (Tensor): Priorities of gts. The gt with a higher - priority is more likely to be assigned to the bbox when the bbox - match with multiple gts. Shape: (num_gt, ). - - Returns: - tuple: Returns (assigned_gt_inds, shadowed_gt_inds). - - - assigned_gt_inds: The assigned gt index of each prior bbox \ - (i.e. index from 1 to num_gts). Shape: (num_prior, ). - - shadowed_gt_inds: shadowed gt indices. It is a tensor of \ - shape (num_ignore, 2) with first column being the \ - shadowed prior bbox indices and the second column the \ - shadowed gt indices (1-based). - """ - num_bboxes, num_gts = is_bbox_in_gt_core.shape - - if gt_priority is None: - gt_priority = torch.arange( - num_gts, device=is_bbox_in_gt_core.device) - assert gt_priority.size(0) == num_gts - # The bigger gt_priority, the more preferable to be assigned - # The assigned inds are by default 0 (background) - assigned_gt_inds = is_bbox_in_gt_core.new_zeros((num_bboxes, ), - dtype=torch.long) - # Shadowed bboxes are assigned to be background. But the corresponding - # label is ignored during loss calculation, which is done through - # shadowed_gt_inds - shadowed_gt_inds = torch.nonzero(is_bbox_in_gt_shadow, as_tuple=False) - if is_bbox_in_gt_core.sum() == 0: # No gt match - shadowed_gt_inds[:, 1] += 1 # 1-based. For consistency issue - return assigned_gt_inds, shadowed_gt_inds - - # The priority of each prior box and gt pair. If one prior box is - # matched bo multiple gts. Only the pair with the highest priority - # is saved - pair_priority = is_bbox_in_gt_core.new_full((num_bboxes, num_gts), - -1, - dtype=torch.long) - - # Each bbox could match with multiple gts. - # The following codes deal with this situation - # Matched bboxes (to any gt). Shape: (num_pos_anchor, ) - inds_of_match = torch.any(is_bbox_in_gt_core, dim=1) - # The matched gt index of each positive bbox. Length >= num_pos_anchor - # , since one bbox could match multiple gts - matched_bbox_gt_inds = torch.nonzero( - is_bbox_in_gt_core, as_tuple=False)[:, 1] - # Assign priority to each bbox-gt pair. - pair_priority[is_bbox_in_gt_core] = gt_priority[matched_bbox_gt_inds] - _, argmax_priority = pair_priority[inds_of_match].max(dim=1) - assigned_gt_inds[inds_of_match] = argmax_priority + 1 # 1-based - # Zero-out the assigned anchor box to filter the shadowed gt indices - is_bbox_in_gt_core[inds_of_match, argmax_priority] = 0 - # Concat the shadowed indices due to overlapping with that out side of - # effective scale. shape: (total_num_ignore, 2) - shadowed_gt_inds = torch.cat( - (shadowed_gt_inds, torch.nonzero( - is_bbox_in_gt_core, as_tuple=False)), - dim=0) - # `is_bbox_in_gt_core` should be changed back to keep arguments intact. - is_bbox_in_gt_core[inds_of_match, argmax_priority] = 1 - # 1-based shadowed gt indices, to be consistent with `assigned_gt_inds` - if shadowed_gt_inds.numel() > 0: - shadowed_gt_inds[:, 1] += 1 - return assigned_gt_inds, shadowed_gt_inds diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/grid_assigner.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/grid_assigner.py deleted file mode 100755 index 3d3277f1c..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/grid_assigner.py +++ /dev/null @@ -1,156 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -@BBOX_ASSIGNERS.register_module() -class GridAssigner(BaseAssigner): - """Assign a corresponding gt bbox or background to each bbox. - - Each proposals will be assigned with `-1`, `0`, or a positive integer - indicating the ground truth index. - - - -1: don't care - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - - Args: - pos_iou_thr (float): IoU threshold for positive bboxes. - neg_iou_thr (float or tuple): IoU threshold for negative bboxes. - min_pos_iou (float): Minimum iou for a bbox to be considered as a - positive bbox. Positive samples can have smaller IoU than - pos_iou_thr due to the 4th step (assign max IoU sample to each gt). - gt_max_assign_all (bool): Whether to assign all bboxes with the same - highest overlap with some gt to that gt. - """ - - def __init__(self, - pos_iou_thr, - neg_iou_thr, - min_pos_iou=.0, - gt_max_assign_all=True, - iou_calculator=dict(type='BboxOverlaps2D')): - self.pos_iou_thr = pos_iou_thr - self.neg_iou_thr = neg_iou_thr - self.min_pos_iou = min_pos_iou - self.gt_max_assign_all = gt_max_assign_all - self.iou_calculator = build_iou_calculator(iou_calculator) - - def assign(self, bboxes, box_responsible_flags, gt_bboxes, gt_labels=None): - """Assign gt to bboxes. The process is very much like the max iou - assigner, except that positive samples are constrained within the cell - that the gt boxes fell in. - - This method assign a gt bbox to every bbox (proposal/anchor), each bbox - will be assigned with -1, 0, or a positive number. -1 means don't care, - 0 means negative sample, positive number is the index (1-based) of - assigned gt. - The assignment is done in following steps, the order matters. - - 1. assign every bbox to -1 - 2. assign proposals whose iou with all gts <= neg_iou_thr to 0 - 3. for each bbox within a cell, if the iou with its nearest gt > - pos_iou_thr and the center of that gt falls inside the cell, - assign it to that bbox - 4. for each gt bbox, assign its nearest proposals within the cell the - gt bbox falls in to itself. - - Args: - bboxes (Tensor): Bounding boxes to be assigned, shape(n, 4). - box_responsible_flags (Tensor): flag to indicate whether box is - responsible for prediction, shape(n, ) - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - - Returns: - :obj:`AssignResult`: The assign result. - """ - num_gts, num_bboxes = gt_bboxes.size(0), bboxes.size(0) - - # compute iou between all gt and bboxes - overlaps = self.iou_calculator(gt_bboxes, bboxes) - - # 1. assign -1 by default - assigned_gt_inds = overlaps.new_full((num_bboxes, ), - -1, - dtype=torch.long) - - if num_gts == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - max_overlaps = overlaps.new_zeros((num_bboxes, )) - if num_gts == 0: - # No truth, assign everything to background - assigned_gt_inds[:] = 0 - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = overlaps.new_full((num_bboxes, ), - -1, - dtype=torch.long) - return AssignResult( - num_gts, - assigned_gt_inds, - max_overlaps, - labels=assigned_labels) - - # 2. assign negative: below - # for each anchor, which gt best overlaps with it - # for each anchor, the max iou of all gts - # shape of max_overlaps == argmax_overlaps == num_bboxes - max_overlaps, argmax_overlaps = overlaps.max(dim=0) - - if isinstance(self.neg_iou_thr, float): - assigned_gt_inds[(max_overlaps >= 0) - & (max_overlaps <= self.neg_iou_thr)] = 0 - elif isinstance(self.neg_iou_thr, (tuple, list)): - assert len(self.neg_iou_thr) == 2 - assigned_gt_inds[(max_overlaps > self.neg_iou_thr[0]) - & (max_overlaps <= self.neg_iou_thr[1])] = 0 - - # 3. assign positive: falls into responsible cell and above - # positive IOU threshold, the order matters. - # the prior condition of comparison is to filter out all - # unrelated anchors, i.e. not box_responsible_flags - overlaps[:, ~box_responsible_flags.type(torch.bool)] = -1. - - # calculate max_overlaps again, but this time we only consider IOUs - # for anchors responsible for prediction - max_overlaps, argmax_overlaps = overlaps.max(dim=0) - - # for each gt, which anchor best overlaps with it - # for each gt, the max iou of all proposals - # shape of gt_max_overlaps == gt_argmax_overlaps == num_gts - gt_max_overlaps, gt_argmax_overlaps = overlaps.max(dim=1) - - pos_inds = (max_overlaps > - self.pos_iou_thr) & box_responsible_flags.type(torch.bool) - assigned_gt_inds[pos_inds] = argmax_overlaps[pos_inds] + 1 - - # 4. assign positive to max overlapped anchors within responsible cell - for i in range(num_gts): - if gt_max_overlaps[i] > self.min_pos_iou: - if self.gt_max_assign_all: - max_iou_inds = (overlaps[i, :] == gt_max_overlaps[i]) & \ - box_responsible_flags.type(torch.bool) - assigned_gt_inds[max_iou_inds] = i + 1 - elif box_responsible_flags[gt_argmax_overlaps[i]]: - assigned_gt_inds[gt_argmax_overlaps[i]] = i + 1 - - # assign labels of positive anchors - if gt_labels is not None: - assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1) - pos_inds = torch.nonzero( - assigned_gt_inds > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[ - assigned_gt_inds[pos_inds] - 1] - - else: - assigned_labels = None - - return AssignResult( - num_gts, assigned_gt_inds, max_overlaps, labels=assigned_labels) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/hungarian_assigner.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/hungarian_assigner.py deleted file mode 100755 index bc862933b..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/hungarian_assigner.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..match_costs import build_match_cost -from ..transforms import bbox_cxcywh_to_xyxy -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - -try: - from scipy.optimize import linear_sum_assignment -except ImportError: - linear_sum_assignment = None - - -@BBOX_ASSIGNERS.register_module() -class HungarianAssigner(BaseAssigner): - """Computes one-to-one matching between predictions and ground truth. - - This class computes an assignment between the targets and the predictions - based on the costs. The costs are weighted sum of three components: - classification cost, regression L1 cost and regression iou cost. The - targets don't include the no_object, so generally there are more - predictions than targets. After the one-to-one matching, the un-matched - are treated as backgrounds. Thus each query prediction will be assigned - with `0` or a positive integer indicating the ground truth index: - - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - - Args: - cls_weight (int | float, optional): The scale factor for classification - cost. Default 1.0. - bbox_weight (int | float, optional): The scale factor for regression - L1 cost. Default 1.0. - iou_weight (int | float, optional): The scale factor for regression - iou cost. Default 1.0. - iou_calculator (dict | optional): The config for the iou calculation. - Default type `BboxOverlaps2D`. - iou_mode (str | optional): "iou" (intersection over union), "iof" - (intersection over foreground), or "giou" (generalized - intersection over union). Default "giou". - """ - - def __init__(self, - cls_cost=dict(type='ClassificationCost', weight=1.), - reg_cost=dict(type='BBoxL1Cost', weight=1.0), - iou_cost=dict(type='IoUCost', iou_mode='giou', weight=1.0)): - self.cls_cost = build_match_cost(cls_cost) - self.reg_cost = build_match_cost(reg_cost) - self.iou_cost = build_match_cost(iou_cost) - - def assign(self, - bbox_pred, - cls_pred, - gt_bboxes, - gt_labels, - img_meta, - gt_bboxes_ignore=None, - eps=1e-7): - """Computes one-to-one matching based on the weighted costs. - - This method assign each query prediction to a ground truth or - background. The `assigned_gt_inds` with -1 means don't care, - 0 means negative sample, and positive number is the index (1-based) - of assigned gt. - The assignment is done in the following steps, the order matters. - - 1. assign every prediction to -1 - 2. compute the weighted costs - 3. do Hungarian matching on CPU based on the costs - 4. assign all to 0 (background) first, then for each matched pair - between predictions and gts, treat this prediction as foreground - and assign the corresponding gt index (plus 1) to it. - - Args: - bbox_pred (Tensor): Predicted boxes with normalized coordinates - (cx, cy, w, h), which are all in range [0, 1]. Shape - [num_query, 4]. - cls_pred (Tensor): Predicted classification logits, shape - [num_query, num_class]. - gt_bboxes (Tensor): Ground truth boxes with unnormalized - coordinates (x1, y1, x2, y2). Shape [num_gt, 4]. - gt_labels (Tensor): Label of `gt_bboxes`, shape (num_gt,). - img_meta (dict): Meta information for current image. - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`. Default None. - eps (int | float, optional): A value added to the denominator for - numerical stability. Default 1e-7. - - Returns: - :obj:`AssignResult`: The assigned result. - """ - assert gt_bboxes_ignore is None, \ - 'Only case when gt_bboxes_ignore is None is supported.' - num_gts, num_bboxes = gt_bboxes.size(0), bbox_pred.size(0) - - # 1. assign -1 by default - assigned_gt_inds = bbox_pred.new_full((num_bboxes, ), - -1, - dtype=torch.long) - assigned_labels = bbox_pred.new_full((num_bboxes, ), - -1, - dtype=torch.long) - if num_gts == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - if num_gts == 0: - # No ground truth, assign all to background - assigned_gt_inds[:] = 0 - return AssignResult( - num_gts, assigned_gt_inds, None, labels=assigned_labels) - img_h, img_w, _ = img_meta['img_shape'] - factor = gt_bboxes.new_tensor([img_w, img_h, img_w, - img_h]).unsqueeze(0) - - # 2. compute the weighted costs - # classification and bboxcost. - cls_cost = self.cls_cost(cls_pred, gt_labels) - # regression L1 cost - normalize_gt_bboxes = gt_bboxes / factor - reg_cost = self.reg_cost(bbox_pred, normalize_gt_bboxes) - # regression iou cost, defaultly giou is used in official DETR. - bboxes = bbox_cxcywh_to_xyxy(bbox_pred) * factor - iou_cost = self.iou_cost(bboxes, gt_bboxes) - # weighted sum of above three costs - cost = cls_cost + reg_cost + iou_cost - - # 3. do Hungarian matching on CPU using linear_sum_assignment - cost = cost.detach().cpu() - if linear_sum_assignment is None: - raise ImportError('Please run "pip install scipy" ' - 'to install scipy first.') - matched_row_inds, matched_col_inds = linear_sum_assignment(cost) - matched_row_inds = torch.from_numpy(matched_row_inds).to( - bbox_pred.device) - matched_col_inds = torch.from_numpy(matched_col_inds).to( - bbox_pred.device) - - # 4. assign backgrounds and foregrounds - # assign all indices to backgrounds first - assigned_gt_inds[:] = 0 - # assign foregrounds based on matching results - assigned_gt_inds[matched_row_inds] = matched_col_inds + 1 - assigned_labels[matched_row_inds] = gt_labels[matched_col_inds] - return AssignResult( - num_gts, assigned_gt_inds, None, labels=assigned_labels) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/mask_hungarian_assigner.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/mask_hungarian_assigner.py deleted file mode 100755 index 5b514d1b3..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/mask_hungarian_assigner.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmdet.core.bbox.builder import BBOX_ASSIGNERS -from mmdet.core.bbox.match_costs.builder import build_match_cost -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - -try: - from scipy.optimize import linear_sum_assignment -except ImportError: - linear_sum_assignment = None - - -@BBOX_ASSIGNERS.register_module() -class MaskHungarianAssigner(BaseAssigner): - """Computes one-to-one matching between predictions and ground truth for - mask. - - This class computes an assignment between the targets and the predictions - based on the costs. The costs are weighted sum of three components: - classification cost, mask focal cost and mask dice cost. The - targets don't include the no_object, so generally there are more - predictions than targets. After the one-to-one matching, the un-matched - are treated as backgrounds. Thus each query prediction will be assigned - with `0` or a positive integer indicating the ground truth index: - - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - - Args: - cls_cost (:obj:`mmcv.ConfigDict` | dict): Classification cost config. - mask_cost (:obj:`mmcv.ConfigDict` | dict): Mask cost config. - dice_cost (:obj:`mmcv.ConfigDict` | dict): Dice cost config. - """ - - def __init__(self, - cls_cost=dict(type='ClassificationCost', weight=1.0), - mask_cost=dict( - type='FocalLossCost', weight=1.0, binary_input=True), - dice_cost=dict(type='DiceCost', weight=1.0)): - self.cls_cost = build_match_cost(cls_cost) - self.mask_cost = build_match_cost(mask_cost) - self.dice_cost = build_match_cost(dice_cost) - - def assign(self, - cls_pred, - mask_pred, - gt_labels, - gt_mask, - img_meta, - gt_bboxes_ignore=None, - eps=1e-7): - """Computes one-to-one matching based on the weighted costs. - - Args: - cls_pred (Tensor | None): Class prediction in shape - (num_query, cls_out_channels). - mask_pred (Tensor): Mask prediction in shape (num_query, H, W). - gt_labels (Tensor): Label of 'gt_mask'in shape = (num_gt, ). - gt_mask (Tensor): Ground truth mask in shape = (num_gt, H, W). - img_meta (dict): Meta information for current image. - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`. Default None. - eps (int | float, optional): A value added to the denominator for - numerical stability. Default 1e-7. - - Returns: - :obj:`AssignResult`: The assigned result. - """ - assert gt_bboxes_ignore is None, \ - 'Only case when gt_bboxes_ignore is None is supported.' - # K-Net sometimes passes cls_pred=None to this assigner. - # So we should use the shape of mask_pred - num_gt, num_query = gt_labels.shape[0], mask_pred.shape[0] - - # 1. assign -1 by default - assigned_gt_inds = mask_pred.new_full((num_query, ), - -1, - dtype=torch.long) - assigned_labels = mask_pred.new_full((num_query, ), - -1, - dtype=torch.long) - if num_gt == 0 or num_query == 0: - # No ground truth or boxes, return empty assignment - if num_gt == 0: - # No ground truth, assign all to background - assigned_gt_inds[:] = 0 - return AssignResult( - num_gt, assigned_gt_inds, None, labels=assigned_labels) - - # 2. compute the weighted costs - # classification and maskcost. - if self.cls_cost.weight != 0 and cls_pred is not None: - cls_cost = self.cls_cost(cls_pred, gt_labels) - else: - cls_cost = 0 - - if self.mask_cost.weight != 0: - # mask_pred shape = [num_query, h, w] - # gt_mask shape = [num_gt, h, w] - # mask_cost shape = [num_query, num_gt] - mask_cost = self.mask_cost(mask_pred, gt_mask) - else: - mask_cost = 0 - - if self.dice_cost.weight != 0: - dice_cost = self.dice_cost(mask_pred, gt_mask) - else: - dice_cost = 0 - cost = cls_cost + mask_cost + dice_cost - - # 3. do Hungarian matching on CPU using linear_sum_assignment - cost = cost.detach().cpu() - if linear_sum_assignment is None: - raise ImportError('Please run "pip install scipy" ' - 'to install scipy first.') - - matched_row_inds, matched_col_inds = linear_sum_assignment(cost) - matched_row_inds = torch.from_numpy(matched_row_inds).to( - mask_pred.device) - matched_col_inds = torch.from_numpy(matched_col_inds).to( - mask_pred.device) - - # 4. assign backgrounds and foregrounds - # assign all indices to backgrounds first - assigned_gt_inds[:] = 0 - # assign foregrounds based on matching results - assigned_gt_inds[matched_row_inds] = matched_col_inds + 1 - assigned_labels[matched_row_inds] = gt_labels[matched_col_inds] - return AssignResult( - num_gt, assigned_gt_inds, None, labels=assigned_labels) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/max_iou_assigner.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/max_iou_assigner.py deleted file mode 100755 index 76359b892..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/max_iou_assigner.py +++ /dev/null @@ -1,218 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -@BBOX_ASSIGNERS.register_module() -class MaxIoUAssigner(BaseAssigner): - """Assign a corresponding gt bbox or background to each bbox. - - Each proposals will be assigned with `-1`, or a semi-positive integer - indicating the ground truth index. - - - -1: negative sample, no assigned gt - - semi-positive integer: positive sample, index (0-based) of assigned gt - - Args: - pos_iou_thr (float): IoU threshold for positive bboxes. - neg_iou_thr (float or tuple): IoU threshold for negative bboxes. - min_pos_iou (float): Minimum iou for a bbox to be considered as a - positive bbox. Positive samples can have smaller IoU than - pos_iou_thr due to the 4th step (assign max IoU sample to each gt). - `min_pos_iou` is set to avoid assigning bboxes that have extremely - small iou with GT as positive samples. It brings about 0.3 mAP - improvements in 1x schedule but does not affect the performance of - 3x schedule. More comparisons can be found in - `PR #7464 `_. - gt_max_assign_all (bool): Whether to assign all bboxes with the same - highest overlap with some gt to that gt. - ignore_iof_thr (float): IoF threshold for ignoring bboxes (if - `gt_bboxes_ignore` is specified). Negative values mean not - ignoring any bboxes. - ignore_wrt_candidates (bool): Whether to compute the iof between - `bboxes` and `gt_bboxes_ignore`, or the contrary. - match_low_quality (bool): Whether to allow low quality matches. This is - usually allowed for RPN and single stage detectors, but not allowed - in the second stage. Details are demonstrated in Step 4. - gpu_assign_thr (int): The upper bound of the number of GT for GPU - assign. When the number of gt is above this threshold, will assign - on CPU device. Negative values mean not assign on CPU. - """ - - def __init__(self, - pos_iou_thr, - neg_iou_thr, - min_pos_iou=.0, - gt_max_assign_all=True, - ignore_iof_thr=-1, - ignore_wrt_candidates=True, - match_low_quality=True, - gpu_assign_thr=-1, - iou_calculator=dict(type='BboxOverlaps2D')): - self.pos_iou_thr = pos_iou_thr - self.neg_iou_thr = neg_iou_thr - self.min_pos_iou = min_pos_iou - self.gt_max_assign_all = gt_max_assign_all - self.ignore_iof_thr = ignore_iof_thr - self.ignore_wrt_candidates = ignore_wrt_candidates - self.gpu_assign_thr = gpu_assign_thr - self.match_low_quality = match_low_quality - self.iou_calculator = build_iou_calculator(iou_calculator) - - def assign(self, bboxes, gt_bboxes, gt_bboxes_ignore=None, gt_labels=None): - """Assign gt to bboxes. - - This method assign a gt bbox to every bbox (proposal/anchor), each bbox - will be assigned with -1, or a semi-positive number. -1 means negative - sample, semi-positive number is the index (0-based) of assigned gt. - The assignment is done in following steps, the order matters. - - 1. assign every bbox to the background - 2. assign proposals whose iou with all gts < neg_iou_thr to 0 - 3. for each bbox, if the iou with its nearest gt >= pos_iou_thr, - assign it to that bbox - 4. for each gt bbox, assign its nearest proposals (may be more than - one) to itself - - Args: - bboxes (Tensor): Bounding boxes to be assigned, shape(n, 4). - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - - Returns: - :obj:`AssignResult`: The assign result. - - Example: - >>> self = MaxIoUAssigner(0.5, 0.5) - >>> bboxes = torch.Tensor([[0, 0, 10, 10], [10, 10, 20, 20]]) - >>> gt_bboxes = torch.Tensor([[0, 0, 10, 9]]) - >>> assign_result = self.assign(bboxes, gt_bboxes) - >>> expected_gt_inds = torch.LongTensor([1, 0]) - >>> assert torch.all(assign_result.gt_inds == expected_gt_inds) - """ - assign_on_cpu = True if (self.gpu_assign_thr > 0) and ( - gt_bboxes.shape[0] > self.gpu_assign_thr) else False - # compute overlap and assign gt on CPU when number of GT is large - if assign_on_cpu: - device = bboxes.device - bboxes = bboxes.cpu() - gt_bboxes = gt_bboxes.cpu() - if gt_bboxes_ignore is not None: - gt_bboxes_ignore = gt_bboxes_ignore.cpu() - if gt_labels is not None: - gt_labels = gt_labels.cpu() - - overlaps = self.iou_calculator(gt_bboxes, bboxes) - - if (self.ignore_iof_thr > 0 and gt_bboxes_ignore is not None - and gt_bboxes_ignore.numel() > 0 and bboxes.numel() > 0): - if self.ignore_wrt_candidates: - ignore_overlaps = self.iou_calculator( - bboxes, gt_bboxes_ignore, mode='iof') - ignore_max_overlaps, _ = ignore_overlaps.max(dim=1) - else: - ignore_overlaps = self.iou_calculator( - gt_bboxes_ignore, bboxes, mode='iof') - ignore_max_overlaps, _ = ignore_overlaps.max(dim=0) - overlaps[:, ignore_max_overlaps > self.ignore_iof_thr] = -1 - - assign_result = self.assign_wrt_overlaps(overlaps, gt_labels) - if assign_on_cpu: - assign_result.gt_inds = assign_result.gt_inds.to(device) - assign_result.max_overlaps = assign_result.max_overlaps.to(device) - if assign_result.labels is not None: - assign_result.labels = assign_result.labels.to(device) - return assign_result - - def assign_wrt_overlaps(self, overlaps, gt_labels=None): - """Assign w.r.t. the overlaps of bboxes with gts. - - Args: - overlaps (Tensor): Overlaps between k gt_bboxes and n bboxes, - shape(k, n). - gt_labels (Tensor, optional): Labels of k gt_bboxes, shape (k, ). - - Returns: - :obj:`AssignResult`: The assign result. - """ - num_gts, num_bboxes = overlaps.size(0), overlaps.size(1) - - # 1. assign -1 by default - assigned_gt_inds = overlaps.new_full((num_bboxes, ), - -1, - dtype=torch.long) - - if num_gts == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - max_overlaps = overlaps.new_zeros((num_bboxes, )) - if num_gts == 0: - # No truth, assign everything to background - assigned_gt_inds[:] = 0 - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = overlaps.new_full((num_bboxes, ), - -1, - dtype=torch.long) - return AssignResult( - num_gts, - assigned_gt_inds, - max_overlaps, - labels=assigned_labels) - - # for each anchor, which gt best overlaps with it - # for each anchor, the max iou of all gts - max_overlaps, argmax_overlaps = overlaps.max(dim=0) - # for each gt, which anchor best overlaps with it - # for each gt, the max iou of all proposals - gt_max_overlaps, gt_argmax_overlaps = overlaps.max(dim=1) - - # 2. assign negative: below - # the negative inds are set to be 0 - if isinstance(self.neg_iou_thr, float): - assigned_gt_inds[(max_overlaps >= 0) - & (max_overlaps < self.neg_iou_thr)] = 0 - elif isinstance(self.neg_iou_thr, tuple): - assert len(self.neg_iou_thr) == 2 - assigned_gt_inds[(max_overlaps >= self.neg_iou_thr[0]) - & (max_overlaps < self.neg_iou_thr[1])] = 0 - - # 3. assign positive: above positive IoU threshold - pos_inds = max_overlaps >= self.pos_iou_thr - assigned_gt_inds[pos_inds] = argmax_overlaps[pos_inds] + 1 - - if self.match_low_quality: - # Low-quality matching will overwrite the assigned_gt_inds assigned - # in Step 3. Thus, the assigned gt might not be the best one for - # prediction. - # For example, if bbox A has 0.9 and 0.8 iou with GT bbox 1 & 2, - # bbox 1 will be assigned as the best target for bbox A in step 3. - # However, if GT bbox 2's gt_argmax_overlaps = A, bbox A's - # assigned_gt_inds will be overwritten to be bbox 2. - # This might be the reason that it is not used in ROI Heads. - for i in range(num_gts): - if gt_max_overlaps[i] >= self.min_pos_iou: - if self.gt_max_assign_all: - max_iou_inds = overlaps[i, :] == gt_max_overlaps[i] - assigned_gt_inds[max_iou_inds] = i + 1 - else: - assigned_gt_inds[gt_argmax_overlaps[i]] = i + 1 - - if gt_labels is not None: - assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1) - pos_inds = torch.nonzero( - assigned_gt_inds > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[ - assigned_gt_inds[pos_inds] - 1] - else: - assigned_labels = None - - return AssignResult( - num_gts, assigned_gt_inds, max_overlaps, labels=assigned_labels) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/point_assigner.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/point_assigner.py deleted file mode 100755 index f5261ea66..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/point_assigner.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -@BBOX_ASSIGNERS.register_module() -class PointAssigner(BaseAssigner): - """Assign a corresponding gt bbox or background to each point. - - Each proposals will be assigned with `0`, or a positive integer - indicating the ground truth index. - - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - """ - - def __init__(self, scale=4, pos_num=3): - self.scale = scale - self.pos_num = pos_num - - def assign(self, points, gt_bboxes, gt_bboxes_ignore=None, gt_labels=None): - """Assign gt to points. - - This method assign a gt bbox to every points set, each points set - will be assigned with the background_label (-1), or a label number. - -1 is background, and semi-positive number is the index (0-based) of - assigned gt. - The assignment is done in following steps, the order matters. - - 1. assign every points to the background_label (-1) - 2. A point is assigned to some gt bbox if - (i) the point is within the k closest points to the gt bbox - (ii) the distance between this point and the gt is smaller than - other gt bboxes - - Args: - points (Tensor): points to be assigned, shape(n, 3) while last - dimension stands for (x, y, stride). - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - NOTE: currently unused. - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - - Returns: - :obj:`AssignResult`: The assign result. - """ - num_points = points.shape[0] - num_gts = gt_bboxes.shape[0] - - if num_gts == 0 or num_points == 0: - # If no truth assign everything to the background - assigned_gt_inds = points.new_full((num_points, ), - 0, - dtype=torch.long) - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = points.new_full((num_points, ), - -1, - dtype=torch.long) - return AssignResult( - num_gts, assigned_gt_inds, None, labels=assigned_labels) - - points_xy = points[:, :2] - points_stride = points[:, 2] - points_lvl = torch.log2( - points_stride).int() # [3...,4...,5...,6...,7...] - lvl_min, lvl_max = points_lvl.min(), points_lvl.max() - - # assign gt box - gt_bboxes_xy = (gt_bboxes[:, :2] + gt_bboxes[:, 2:]) / 2 - gt_bboxes_wh = (gt_bboxes[:, 2:] - gt_bboxes[:, :2]).clamp(min=1e-6) - scale = self.scale - gt_bboxes_lvl = ((torch.log2(gt_bboxes_wh[:, 0] / scale) + - torch.log2(gt_bboxes_wh[:, 1] / scale)) / 2).int() - gt_bboxes_lvl = torch.clamp(gt_bboxes_lvl, min=lvl_min, max=lvl_max) - - # stores the assigned gt index of each point - assigned_gt_inds = points.new_zeros((num_points, ), dtype=torch.long) - # stores the assigned gt dist (to this point) of each point - assigned_gt_dist = points.new_full((num_points, ), float('inf')) - points_range = torch.arange(points.shape[0]) - - for idx in range(num_gts): - gt_lvl = gt_bboxes_lvl[idx] - # get the index of points in this level - lvl_idx = gt_lvl == points_lvl - points_index = points_range[lvl_idx] - # get the points in this level - lvl_points = points_xy[lvl_idx, :] - # get the center point of gt - gt_point = gt_bboxes_xy[[idx], :] - # get width and height of gt - gt_wh = gt_bboxes_wh[[idx], :] - # compute the distance between gt center and - # all points in this level - points_gt_dist = ((lvl_points - gt_point) / gt_wh).norm(dim=1) - # find the nearest k points to gt center in this level - min_dist, min_dist_index = torch.topk( - points_gt_dist, self.pos_num, largest=False) - # the index of nearest k points to gt center in this level - min_dist_points_index = points_index[min_dist_index] - # The less_than_recorded_index stores the index - # of min_dist that is less then the assigned_gt_dist. Where - # assigned_gt_dist stores the dist from previous assigned gt - # (if exist) to each point. - less_than_recorded_index = min_dist < assigned_gt_dist[ - min_dist_points_index] - # The min_dist_points_index stores the index of points satisfy: - # (1) it is k nearest to current gt center in this level. - # (2) it is closer to current gt center than other gt center. - min_dist_points_index = min_dist_points_index[ - less_than_recorded_index] - # assign the result - assigned_gt_inds[min_dist_points_index] = idx + 1 - assigned_gt_dist[min_dist_points_index] = min_dist[ - less_than_recorded_index] - - if gt_labels is not None: - assigned_labels = assigned_gt_inds.new_full((num_points, ), -1) - pos_inds = torch.nonzero( - assigned_gt_inds > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[ - assigned_gt_inds[pos_inds] - 1] - else: - assigned_labels = None - - return AssignResult( - num_gts, assigned_gt_inds, None, labels=assigned_labels) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/region_assigner.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/region_assigner.py deleted file mode 100755 index 8440077ee..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/region_assigner.py +++ /dev/null @@ -1,222 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmdet.core import anchor_inside_flags -from ..builder import BBOX_ASSIGNERS -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -def calc_region(bbox, ratio, stride, featmap_size=None): - """Calculate region of the box defined by the ratio, the ratio is from the - center of the box to every edge.""" - # project bbox on the feature - f_bbox = bbox / stride - x1 = torch.round((1 - ratio) * f_bbox[0] + ratio * f_bbox[2]) - y1 = torch.round((1 - ratio) * f_bbox[1] + ratio * f_bbox[3]) - x2 = torch.round(ratio * f_bbox[0] + (1 - ratio) * f_bbox[2]) - y2 = torch.round(ratio * f_bbox[1] + (1 - ratio) * f_bbox[3]) - if featmap_size is not None: - x1 = x1.clamp(min=0, max=featmap_size[1]) - y1 = y1.clamp(min=0, max=featmap_size[0]) - x2 = x2.clamp(min=0, max=featmap_size[1]) - y2 = y2.clamp(min=0, max=featmap_size[0]) - return (x1, y1, x2, y2) - - -def anchor_ctr_inside_region_flags(anchors, stride, region): - """Get the flag indicate whether anchor centers are inside regions.""" - x1, y1, x2, y2 = region - f_anchors = anchors / stride - x = (f_anchors[:, 0] + f_anchors[:, 2]) * 0.5 - y = (f_anchors[:, 1] + f_anchors[:, 3]) * 0.5 - flags = (x >= x1) & (x <= x2) & (y >= y1) & (y <= y2) - return flags - - -@BBOX_ASSIGNERS.register_module() -class RegionAssigner(BaseAssigner): - """Assign a corresponding gt bbox or background to each bbox. - - Each proposals will be assigned with `-1`, `0`, or a positive integer - indicating the ground truth index. - - - -1: don't care - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - - Args: - center_ratio: ratio of the region in the center of the bbox to - define positive sample. - ignore_ratio: ratio of the region to define ignore samples. - """ - - def __init__(self, center_ratio=0.2, ignore_ratio=0.5): - self.center_ratio = center_ratio - self.ignore_ratio = ignore_ratio - - def assign(self, - mlvl_anchors, - mlvl_valid_flags, - gt_bboxes, - img_meta, - featmap_sizes, - anchor_scale, - anchor_strides, - gt_bboxes_ignore=None, - gt_labels=None, - allowed_border=0): - """Assign gt to anchors. - - This method assign a gt bbox to every bbox (proposal/anchor), each bbox - will be assigned with -1, 0, or a positive number. -1 means don't care, - 0 means negative sample, positive number is the index (1-based) of - assigned gt. - - The assignment is done in following steps, and the order matters. - - 1. Assign every anchor to 0 (negative) - 2. (For each gt_bboxes) Compute ignore flags based on ignore_region - then assign -1 to anchors w.r.t. ignore flags - 3. (For each gt_bboxes) Compute pos flags based on center_region then - assign gt_bboxes to anchors w.r.t. pos flags - 4. (For each gt_bboxes) Compute ignore flags based on adjacent anchor - level then assign -1 to anchors w.r.t. ignore flags - 5. Assign anchor outside of image to -1 - - Args: - mlvl_anchors (list[Tensor]): Multi level anchors. - mlvl_valid_flags (list[Tensor]): Multi level valid flags. - gt_bboxes (Tensor): Ground truth bboxes of image - img_meta (dict): Meta info of image. - featmap_sizes (list[Tensor]): Feature mapsize each level - anchor_scale (int): Scale of the anchor. - anchor_strides (list[int]): Stride of the anchor. - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - allowed_border (int, optional): The border to allow the valid - anchor. Defaults to 0. - - Returns: - :obj:`AssignResult`: The assign result. - """ - if gt_bboxes_ignore is not None: - raise NotImplementedError - - num_gts = gt_bboxes.shape[0] - num_bboxes = sum(x.shape[0] for x in mlvl_anchors) - - if num_gts == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - max_overlaps = gt_bboxes.new_zeros((num_bboxes, )) - assigned_gt_inds = gt_bboxes.new_zeros((num_bboxes, ), - dtype=torch.long) - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = gt_bboxes.new_full((num_bboxes, ), - -1, - dtype=torch.long) - return AssignResult( - num_gts, - assigned_gt_inds, - max_overlaps, - labels=assigned_labels) - - num_lvls = len(mlvl_anchors) - r1 = (1 - self.center_ratio) / 2 - r2 = (1 - self.ignore_ratio) / 2 - - scale = torch.sqrt((gt_bboxes[:, 2] - gt_bboxes[:, 0]) * - (gt_bboxes[:, 3] - gt_bboxes[:, 1])) - min_anchor_size = scale.new_full( - (1, ), float(anchor_scale * anchor_strides[0])) - target_lvls = torch.floor( - torch.log2(scale) - torch.log2(min_anchor_size) + 0.5) - target_lvls = target_lvls.clamp(min=0, max=num_lvls - 1).long() - - # 1. assign 0 (negative) by default - mlvl_assigned_gt_inds = [] - mlvl_ignore_flags = [] - for lvl in range(num_lvls): - h, w = featmap_sizes[lvl] - assert h * w == mlvl_anchors[lvl].shape[0] - assigned_gt_inds = gt_bboxes.new_full((h * w, ), - 0, - dtype=torch.long) - ignore_flags = torch.zeros_like(assigned_gt_inds) - mlvl_assigned_gt_inds.append(assigned_gt_inds) - mlvl_ignore_flags.append(ignore_flags) - - for gt_id in range(num_gts): - lvl = target_lvls[gt_id].item() - featmap_size = featmap_sizes[lvl] - stride = anchor_strides[lvl] - anchors = mlvl_anchors[lvl] - gt_bbox = gt_bboxes[gt_id, :4] - - # Compute regions - ignore_region = calc_region(gt_bbox, r2, stride, featmap_size) - ctr_region = calc_region(gt_bbox, r1, stride, featmap_size) - - # 2. Assign -1 to ignore flags - ignore_flags = anchor_ctr_inside_region_flags( - anchors, stride, ignore_region) - mlvl_assigned_gt_inds[lvl][ignore_flags] = -1 - - # 3. Assign gt_bboxes to pos flags - pos_flags = anchor_ctr_inside_region_flags(anchors, stride, - ctr_region) - mlvl_assigned_gt_inds[lvl][pos_flags] = gt_id + 1 - - # 4. Assign -1 to ignore adjacent lvl - if lvl > 0: - d_lvl = lvl - 1 - d_anchors = mlvl_anchors[d_lvl] - d_featmap_size = featmap_sizes[d_lvl] - d_stride = anchor_strides[d_lvl] - d_ignore_region = calc_region(gt_bbox, r2, d_stride, - d_featmap_size) - ignore_flags = anchor_ctr_inside_region_flags( - d_anchors, d_stride, d_ignore_region) - mlvl_ignore_flags[d_lvl][ignore_flags] = 1 - if lvl < num_lvls - 1: - u_lvl = lvl + 1 - u_anchors = mlvl_anchors[u_lvl] - u_featmap_size = featmap_sizes[u_lvl] - u_stride = anchor_strides[u_lvl] - u_ignore_region = calc_region(gt_bbox, r2, u_stride, - u_featmap_size) - ignore_flags = anchor_ctr_inside_region_flags( - u_anchors, u_stride, u_ignore_region) - mlvl_ignore_flags[u_lvl][ignore_flags] = 1 - - # 4. (cont.) Assign -1 to ignore adjacent lvl - for lvl in range(num_lvls): - ignore_flags = mlvl_ignore_flags[lvl] - mlvl_assigned_gt_inds[lvl][ignore_flags] = -1 - - # 5. Assign -1 to anchor outside of image - flat_assigned_gt_inds = torch.cat(mlvl_assigned_gt_inds) - flat_anchors = torch.cat(mlvl_anchors) - flat_valid_flags = torch.cat(mlvl_valid_flags) - assert (flat_assigned_gt_inds.shape[0] == flat_anchors.shape[0] == - flat_valid_flags.shape[0]) - inside_flags = anchor_inside_flags(flat_anchors, flat_valid_flags, - img_meta['img_shape'], - allowed_border) - outside_flags = ~inside_flags - flat_assigned_gt_inds[outside_flags] = -1 - - if gt_labels is not None: - assigned_labels = torch.zeros_like(flat_assigned_gt_inds) - pos_flags = assigned_gt_inds > 0 - assigned_labels[pos_flags] = gt_labels[ - flat_assigned_gt_inds[pos_flags] - 1] - else: - assigned_labels = None - - return AssignResult( - num_gts, flat_assigned_gt_inds, None, labels=assigned_labels) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/sim_ota_assigner.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/sim_ota_assigner.py deleted file mode 100755 index ad960b78f..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/sim_ota_assigner.py +++ /dev/null @@ -1,257 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -import torch.nn.functional as F - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import bbox_overlaps -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -@BBOX_ASSIGNERS.register_module() -class SimOTAAssigner(BaseAssigner): - """Computes matching between predictions and ground truth. - - Args: - center_radius (int | float, optional): Ground truth center size - to judge whether a prior is in center. Default 2.5. - candidate_topk (int, optional): The candidate top-k which used to - get top-k ious to calculate dynamic-k. Default 10. - iou_weight (int | float, optional): The scale factor for regression - iou cost. Default 3.0. - cls_weight (int | float, optional): The scale factor for classification - cost. Default 1.0. - """ - - def __init__(self, - center_radius=2.5, - candidate_topk=10, - iou_weight=3.0, - cls_weight=1.0): - self.center_radius = center_radius - self.candidate_topk = candidate_topk - self.iou_weight = iou_weight - self.cls_weight = cls_weight - - def assign(self, - pred_scores, - priors, - decoded_bboxes, - gt_bboxes, - gt_labels, - gt_bboxes_ignore=None, - eps=1e-7): - """Assign gt to priors using SimOTA. It will switch to CPU mode when - GPU is out of memory. - Args: - pred_scores (Tensor): Classification scores of one image, - a 2D-Tensor with shape [num_priors, num_classes] - priors (Tensor): All priors of one image, a 2D-Tensor with shape - [num_priors, 4] in [cx, xy, stride_w, stride_y] format. - decoded_bboxes (Tensor): Predicted bboxes, a 2D-Tensor with shape - [num_priors, 4] in [tl_x, tl_y, br_x, br_y] format. - gt_bboxes (Tensor): Ground truth bboxes of one image, a 2D-Tensor - with shape [num_gts, 4] in [tl_x, tl_y, br_x, br_y] format. - gt_labels (Tensor): Ground truth labels of one image, a Tensor - with shape [num_gts]. - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - eps (float): A value added to the denominator for numerical - stability. Default 1e-7. - Returns: - assign_result (obj:`AssignResult`): The assigned result. - """ - try: - assign_result = self._assign(pred_scores, priors, decoded_bboxes, - gt_bboxes, gt_labels, - gt_bboxes_ignore, eps) - return assign_result - except RuntimeError: - origin_device = pred_scores.device - warnings.warn('OOM RuntimeError is raised due to the huge memory ' - 'cost during label assignment. CPU mode is applied ' - 'in this batch. If you want to avoid this issue, ' - 'try to reduce the batch size or image size.') - torch.cuda.empty_cache() - - pred_scores = pred_scores.cpu() - priors = priors.cpu() - decoded_bboxes = decoded_bboxes.cpu() - gt_bboxes = gt_bboxes.cpu().float() - gt_labels = gt_labels.cpu() - - assign_result = self._assign(pred_scores, priors, decoded_bboxes, - gt_bboxes, gt_labels, - gt_bboxes_ignore, eps) - assign_result.gt_inds = assign_result.gt_inds.to(origin_device) - assign_result.max_overlaps = assign_result.max_overlaps.to( - origin_device) - assign_result.labels = assign_result.labels.to(origin_device) - - return assign_result - - def _assign(self, - pred_scores, - priors, - decoded_bboxes, - gt_bboxes, - gt_labels, - gt_bboxes_ignore=None, - eps=1e-7): - """Assign gt to priors using SimOTA. - Args: - pred_scores (Tensor): Classification scores of one image, - a 2D-Tensor with shape [num_priors, num_classes] - priors (Tensor): All priors of one image, a 2D-Tensor with shape - [num_priors, 4] in [cx, xy, stride_w, stride_y] format. - decoded_bboxes (Tensor): Predicted bboxes, a 2D-Tensor with shape - [num_priors, 4] in [tl_x, tl_y, br_x, br_y] format. - gt_bboxes (Tensor): Ground truth bboxes of one image, a 2D-Tensor - with shape [num_gts, 4] in [tl_x, tl_y, br_x, br_y] format. - gt_labels (Tensor): Ground truth labels of one image, a Tensor - with shape [num_gts]. - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - eps (float): A value added to the denominator for numerical - stability. Default 1e-7. - Returns: - :obj:`AssignResult`: The assigned result. - """ - INF = 100000.0 - num_gt = gt_bboxes.size(0) - num_bboxes = decoded_bboxes.size(0) - - # assign 0 by default - assigned_gt_inds = decoded_bboxes.new_full((num_bboxes, ), - 0, - dtype=torch.long) - valid_mask, is_in_boxes_and_center = self.get_in_gt_and_in_center_info( - priors, gt_bboxes) - valid_decoded_bbox = decoded_bboxes[valid_mask] - valid_pred_scores = pred_scores[valid_mask] - num_valid = valid_decoded_bbox.size(0) - - if num_gt == 0 or num_bboxes == 0 or num_valid == 0: - # No ground truth or boxes, return empty assignment - max_overlaps = decoded_bboxes.new_zeros((num_bboxes, )) - if num_gt == 0: - # No truth, assign everything to background - assigned_gt_inds[:] = 0 - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = decoded_bboxes.new_full((num_bboxes, ), - -1, - dtype=torch.long) - return AssignResult( - num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels) - - pairwise_ious = bbox_overlaps(valid_decoded_bbox, gt_bboxes) - iou_cost = -torch.log(pairwise_ious + eps) - - gt_onehot_label = ( - F.one_hot(gt_labels.to(torch.int64), - pred_scores.shape[-1]).float().unsqueeze(0).repeat( - num_valid, 1, 1)) - - valid_pred_scores = valid_pred_scores.unsqueeze(1).repeat(1, num_gt, 1) - cls_cost = ( - F.binary_cross_entropy( - valid_pred_scores.to(dtype=torch.float32).sqrt_(), - gt_onehot_label, - reduction='none', - ).sum(-1).to(dtype=valid_pred_scores.dtype)) - - cost_matrix = ( - cls_cost * self.cls_weight + iou_cost * self.iou_weight + - (~is_in_boxes_and_center) * INF) - - matched_pred_ious, matched_gt_inds = \ - self.dynamic_k_matching( - cost_matrix, pairwise_ious, num_gt, valid_mask) - - # convert to AssignResult format - assigned_gt_inds[valid_mask] = matched_gt_inds + 1 - assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1) - assigned_labels[valid_mask] = gt_labels[matched_gt_inds].long() - max_overlaps = assigned_gt_inds.new_full((num_bboxes, ), - -INF, - dtype=torch.float32) - max_overlaps[valid_mask] = matched_pred_ious - return AssignResult( - num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels) - - def get_in_gt_and_in_center_info(self, priors, gt_bboxes): - num_gt = gt_bboxes.size(0) - - repeated_x = priors[:, 0].unsqueeze(1).repeat(1, num_gt) - repeated_y = priors[:, 1].unsqueeze(1).repeat(1, num_gt) - repeated_stride_x = priors[:, 2].unsqueeze(1).repeat(1, num_gt) - repeated_stride_y = priors[:, 3].unsqueeze(1).repeat(1, num_gt) - - # is prior centers in gt bboxes, shape: [n_prior, n_gt] - l_ = repeated_x - gt_bboxes[:, 0] - t_ = repeated_y - gt_bboxes[:, 1] - r_ = gt_bboxes[:, 2] - repeated_x - b_ = gt_bboxes[:, 3] - repeated_y - - deltas = torch.stack([l_, t_, r_, b_], dim=1) - is_in_gts = deltas.min(dim=1).values > 0 - is_in_gts_all = is_in_gts.sum(dim=1) > 0 - - # is prior centers in gt centers - gt_cxs = (gt_bboxes[:, 0] + gt_bboxes[:, 2]) / 2.0 - gt_cys = (gt_bboxes[:, 1] + gt_bboxes[:, 3]) / 2.0 - ct_box_l = gt_cxs - self.center_radius * repeated_stride_x - ct_box_t = gt_cys - self.center_radius * repeated_stride_y - ct_box_r = gt_cxs + self.center_radius * repeated_stride_x - ct_box_b = gt_cys + self.center_radius * repeated_stride_y - - cl_ = repeated_x - ct_box_l - ct_ = repeated_y - ct_box_t - cr_ = ct_box_r - repeated_x - cb_ = ct_box_b - repeated_y - - ct_deltas = torch.stack([cl_, ct_, cr_, cb_], dim=1) - is_in_cts = ct_deltas.min(dim=1).values > 0 - is_in_cts_all = is_in_cts.sum(dim=1) > 0 - - # in boxes or in centers, shape: [num_priors] - is_in_gts_or_centers = is_in_gts_all | is_in_cts_all - - # both in boxes and centers, shape: [num_fg, num_gt] - is_in_boxes_and_centers = ( - is_in_gts[is_in_gts_or_centers, :] - & is_in_cts[is_in_gts_or_centers, :]) - return is_in_gts_or_centers, is_in_boxes_and_centers - - def dynamic_k_matching(self, cost, pairwise_ious, num_gt, valid_mask): - matching_matrix = torch.zeros_like(cost, dtype=torch.uint8) - # select candidate topk ious for dynamic-k calculation - candidate_topk = min(self.candidate_topk, pairwise_ious.size(0)) - topk_ious, _ = torch.topk(pairwise_ious, candidate_topk, dim=0) - # calculate dynamic k for each gt - dynamic_ks = torch.clamp(topk_ious.sum(0).int(), min=1) - for gt_idx in range(num_gt): - _, pos_idx = torch.topk( - cost[:, gt_idx], k=dynamic_ks[gt_idx], largest=False) - matching_matrix[:, gt_idx][pos_idx] = 1 - - del topk_ious, dynamic_ks, pos_idx - - prior_match_gt_mask = matching_matrix.sum(1) > 1 - if prior_match_gt_mask.sum() > 0: - cost_min, cost_argmin = torch.min( - cost[prior_match_gt_mask, :], dim=1) - matching_matrix[prior_match_gt_mask, :] *= 0 - matching_matrix[prior_match_gt_mask, cost_argmin] = 1 - # get foreground mask inside box and center prior - fg_mask_inboxes = matching_matrix.sum(1) > 0 - valid_mask[valid_mask.clone()] = fg_mask_inboxes - - matched_gt_inds = matching_matrix[fg_mask_inboxes, :].argmax(1) - matched_pred_ious = (matching_matrix * - pairwise_ious).sum(1)[fg_mask_inboxes] - return matched_pred_ious, matched_gt_inds diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/task_aligned_assigner.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/task_aligned_assigner.py deleted file mode 100755 index b2901b4b9..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/task_aligned_assigner.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - -INF = 100000000 - - -@BBOX_ASSIGNERS.register_module() -class TaskAlignedAssigner(BaseAssigner): - """Task aligned assigner used in the paper: - `TOOD: Task-aligned One-stage Object Detection. - `_. - - Assign a corresponding gt bbox or background to each predicted bbox. - Each bbox will be assigned with `0` or a positive integer - indicating the ground truth index. - - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - - Args: - topk (int): number of bbox selected in each level - iou_calculator (dict): Config dict for iou calculator. - Default: dict(type='BboxOverlaps2D') - """ - - def __init__(self, topk, iou_calculator=dict(type='BboxOverlaps2D')): - assert topk >= 1 - self.topk = topk - self.iou_calculator = build_iou_calculator(iou_calculator) - - def assign(self, - pred_scores, - decode_bboxes, - anchors, - gt_bboxes, - gt_bboxes_ignore=None, - gt_labels=None, - alpha=1, - beta=6): - """Assign gt to bboxes. - - The assignment is done in following steps - - 1. compute alignment metric between all bbox (bbox of all pyramid - levels) and gt - 2. select top-k bbox as candidates for each gt - 3. limit the positive sample's center in gt (because the anchor-free - detector only can predict positive distance) - - - Args: - pred_scores (Tensor): predicted class probability, - shape(n, num_classes) - decode_bboxes (Tensor): predicted bounding boxes, shape(n, 4) - anchors (Tensor): pre-defined anchors, shape(n, 4). - gt_bboxes (Tensor): Groundtruth boxes, shape (k, 4). - gt_bboxes_ignore (Tensor, optional): Ground truth bboxes that are - labelled as `ignored`, e.g., crowd boxes in COCO. - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - - Returns: - :obj:`TaskAlignedAssignResult`: The assign result. - """ - anchors = anchors[:, :4] - num_gt, num_bboxes = gt_bboxes.size(0), anchors.size(0) - # compute alignment metric between all bbox and gt - overlaps = self.iou_calculator(decode_bboxes, gt_bboxes).detach() - bbox_scores = pred_scores[:, gt_labels].detach() - # assign 0 by default - assigned_gt_inds = anchors.new_full((num_bboxes, ), - 0, - dtype=torch.long) - assign_metrics = anchors.new_zeros((num_bboxes, )) - - if num_gt == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - max_overlaps = anchors.new_zeros((num_bboxes, )) - if num_gt == 0: - # No gt boxes, assign everything to background - assigned_gt_inds[:] = 0 - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = anchors.new_full((num_bboxes, ), - -1, - dtype=torch.long) - assign_result = AssignResult( - num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels) - assign_result.assign_metrics = assign_metrics - return assign_result - - # select top-k bboxes as candidates for each gt - alignment_metrics = bbox_scores**alpha * overlaps**beta - topk = min(self.topk, alignment_metrics.size(0)) - _, candidate_idxs = alignment_metrics.topk(topk, dim=0, largest=True) - candidate_metrics = alignment_metrics[candidate_idxs, - torch.arange(num_gt)] - is_pos = candidate_metrics > 0 - - # limit the positive sample's center in gt - anchors_cx = (anchors[:, 0] + anchors[:, 2]) / 2.0 - anchors_cy = (anchors[:, 1] + anchors[:, 3]) / 2.0 - for gt_idx in range(num_gt): - candidate_idxs[:, gt_idx] += gt_idx * num_bboxes - ep_anchors_cx = anchors_cx.view(1, -1).expand( - num_gt, num_bboxes).contiguous().view(-1) - ep_anchors_cy = anchors_cy.view(1, -1).expand( - num_gt, num_bboxes).contiguous().view(-1) - candidate_idxs = candidate_idxs.view(-1) - - # calculate the left, top, right, bottom distance between positive - # bbox center and gt side - l_ = ep_anchors_cx[candidate_idxs].view(-1, num_gt) - gt_bboxes[:, 0] - t_ = ep_anchors_cy[candidate_idxs].view(-1, num_gt) - gt_bboxes[:, 1] - r_ = gt_bboxes[:, 2] - ep_anchors_cx[candidate_idxs].view(-1, num_gt) - b_ = gt_bboxes[:, 3] - ep_anchors_cy[candidate_idxs].view(-1, num_gt) - is_in_gts = torch.stack([l_, t_, r_, b_], dim=1).min(dim=1)[0] > 0.01 - is_pos = is_pos & is_in_gts - - # if an anchor box is assigned to multiple gts, - # the one with the highest iou will be selected. - overlaps_inf = torch.full_like(overlaps, - -INF).t().contiguous().view(-1) - index = candidate_idxs.view(-1)[is_pos.view(-1)] - overlaps_inf[index] = overlaps.t().contiguous().view(-1)[index] - overlaps_inf = overlaps_inf.view(num_gt, -1).t() - - max_overlaps, argmax_overlaps = overlaps_inf.max(dim=1) - assigned_gt_inds[ - max_overlaps != -INF] = argmax_overlaps[max_overlaps != -INF] + 1 - assign_metrics[max_overlaps != -INF] = alignment_metrics[ - max_overlaps != -INF, argmax_overlaps[max_overlaps != -INF]] - - if gt_labels is not None: - assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1) - pos_inds = torch.nonzero( - assigned_gt_inds > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[ - assigned_gt_inds[pos_inds] - 1] - else: - assigned_labels = None - assign_result = AssignResult( - num_gt, assigned_gt_inds, max_overlaps, labels=assigned_labels) - assign_result.assign_metrics = assign_metrics - return assign_result diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/uniform_assigner.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/uniform_assigner.py deleted file mode 100755 index 5fd42fe8b..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/assigners/uniform_assigner.py +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_ASSIGNERS -from ..iou_calculators import build_iou_calculator -from ..transforms import bbox_xyxy_to_cxcywh -from .assign_result import AssignResult -from .base_assigner import BaseAssigner - - -@BBOX_ASSIGNERS.register_module() -class UniformAssigner(BaseAssigner): - """Uniform Matching between the anchors and gt boxes, which can achieve - balance in positive anchors, and gt_bboxes_ignore was not considered for - now. - - Args: - pos_ignore_thr (float): the threshold to ignore positive anchors - neg_ignore_thr (float): the threshold to ignore negative anchors - match_times(int): Number of positive anchors for each gt box. - Default 4. - iou_calculator (dict): iou_calculator config - """ - - def __init__(self, - pos_ignore_thr, - neg_ignore_thr, - match_times=4, - iou_calculator=dict(type='BboxOverlaps2D')): - self.match_times = match_times - self.pos_ignore_thr = pos_ignore_thr - self.neg_ignore_thr = neg_ignore_thr - self.iou_calculator = build_iou_calculator(iou_calculator) - - def assign(self, - bbox_pred, - anchor, - gt_bboxes, - gt_bboxes_ignore=None, - gt_labels=None): - num_gts, num_bboxes = gt_bboxes.size(0), bbox_pred.size(0) - - # 1. assign -1 by default - assigned_gt_inds = bbox_pred.new_full((num_bboxes, ), - 0, - dtype=torch.long) - assigned_labels = bbox_pred.new_full((num_bboxes, ), - -1, - dtype=torch.long) - if num_gts == 0 or num_bboxes == 0: - # No ground truth or boxes, return empty assignment - if num_gts == 0: - # No ground truth, assign all to background - assigned_gt_inds[:] = 0 - assign_result = AssignResult( - num_gts, assigned_gt_inds, None, labels=assigned_labels) - assign_result.set_extra_property( - 'pos_idx', bbox_pred.new_empty(0, dtype=torch.bool)) - assign_result.set_extra_property('pos_predicted_boxes', - bbox_pred.new_empty((0, 4))) - assign_result.set_extra_property('target_boxes', - bbox_pred.new_empty((0, 4))) - return assign_result - - # 2. Compute the L1 cost between boxes - # Note that we use anchors and predict boxes both - cost_bbox = torch.cdist( - bbox_xyxy_to_cxcywh(bbox_pred), - bbox_xyxy_to_cxcywh(gt_bboxes), - p=1) - cost_bbox_anchors = torch.cdist( - bbox_xyxy_to_cxcywh(anchor), bbox_xyxy_to_cxcywh(gt_bboxes), p=1) - - # We found that topk function has different results in cpu and - # cuda mode. In order to ensure consistency with the source code, - # we also use cpu mode. - # TODO: Check whether the performance of cpu and cuda are the same. - C = cost_bbox.cpu() - C1 = cost_bbox_anchors.cpu() - - # self.match_times x n - index = torch.topk( - C, # c=b,n,x c[i]=n,x - k=self.match_times, - dim=0, - largest=False)[1] - - # self.match_times x n - index1 = torch.topk(C1, k=self.match_times, dim=0, largest=False)[1] - # (self.match_times*2) x n - indexes = torch.cat((index, index1), - dim=1).reshape(-1).to(bbox_pred.device) - - pred_overlaps = self.iou_calculator(bbox_pred, gt_bboxes) - anchor_overlaps = self.iou_calculator(anchor, gt_bboxes) - pred_max_overlaps, _ = pred_overlaps.max(dim=1) - anchor_max_overlaps, _ = anchor_overlaps.max(dim=0) - - # 3. Compute the ignore indexes use gt_bboxes and predict boxes - ignore_idx = pred_max_overlaps > self.neg_ignore_thr - assigned_gt_inds[ignore_idx] = -1 - - # 4. Compute the ignore indexes of positive sample use anchors - # and predict boxes - pos_gt_index = torch.arange( - 0, C1.size(1), - device=bbox_pred.device).repeat(self.match_times * 2) - pos_ious = anchor_overlaps[indexes, pos_gt_index] - pos_ignore_idx = pos_ious < self.pos_ignore_thr - - pos_gt_index_with_ignore = pos_gt_index + 1 - pos_gt_index_with_ignore[pos_ignore_idx] = -1 - assigned_gt_inds[indexes] = pos_gt_index_with_ignore - - if gt_labels is not None: - assigned_labels = assigned_gt_inds.new_full((num_bboxes, ), -1) - pos_inds = torch.nonzero( - assigned_gt_inds > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[ - assigned_gt_inds[pos_inds] - 1] - else: - assigned_labels = None - - assign_result = AssignResult( - num_gts, - assigned_gt_inds, - anchor_max_overlaps, - labels=assigned_labels) - assign_result.set_extra_property('pos_idx', ~pos_ignore_idx) - assign_result.set_extra_property('pos_predicted_boxes', - bbox_pred[indexes]) - assign_result.set_extra_property('target_boxes', - gt_bboxes[pos_gt_index]) - return assign_result diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/builder.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/builder.py deleted file mode 100755 index 33b5b8849..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/builder.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, build_from_cfg - -BBOX_ASSIGNERS = Registry('bbox_assigner') -BBOX_SAMPLERS = Registry('bbox_sampler') -BBOX_CODERS = Registry('bbox_coder') - - -def build_assigner(cfg, **default_args): - """Builder of box assigner.""" - return build_from_cfg(cfg, BBOX_ASSIGNERS, default_args) - - -def build_sampler(cfg, **default_args): - """Builder of box sampler.""" - return build_from_cfg(cfg, BBOX_SAMPLERS, default_args) - - -def build_bbox_coder(cfg, **default_args): - """Builder of box coder.""" - return build_from_cfg(cfg, BBOX_CODERS, default_args) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/__init__.py deleted file mode 100755 index af4a9dd2b..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_bbox_coder import BaseBBoxCoder -from .bucketing_bbox_coder import BucketingBBoxCoder -from .delta_xywh_bbox_coder import DeltaXYWHBBoxCoder -from .distance_point_bbox_coder import DistancePointBBoxCoder -from .legacy_delta_xywh_bbox_coder import LegacyDeltaXYWHBBoxCoder -from .pseudo_bbox_coder import PseudoBBoxCoder -from .tblr_bbox_coder import TBLRBBoxCoder -from .yolo_bbox_coder import YOLOBBoxCoder - -__all__ = [ - 'BaseBBoxCoder', 'PseudoBBoxCoder', 'DeltaXYWHBBoxCoder', - 'LegacyDeltaXYWHBBoxCoder', 'TBLRBBoxCoder', 'YOLOBBoxCoder', - 'BucketingBBoxCoder', 'DistancePointBBoxCoder' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/base_bbox_coder.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/base_bbox_coder.py deleted file mode 100755 index 0872bf008..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/base_bbox_coder.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseBBoxCoder(metaclass=ABCMeta): - """Base bounding box coder.""" - - def __init__(self, **kwargs): - pass - - @abstractmethod - def encode(self, bboxes, gt_bboxes): - """Encode deltas between bboxes and ground truth boxes.""" - - @abstractmethod - def decode(self, bboxes, bboxes_pred): - """Decode the predicted bboxes according to prediction and base - boxes.""" diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/bucketing_bbox_coder.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/bucketing_bbox_coder.py deleted file mode 100755 index 7c9de541e..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/bucketing_bbox_coder.py +++ /dev/null @@ -1,351 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np -import torch -import torch.nn.functional as F - -from ..builder import BBOX_CODERS -from ..transforms import bbox_rescale -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class BucketingBBoxCoder(BaseBBoxCoder): - """Bucketing BBox Coder for Side-Aware Boundary Localization (SABL). - - Boundary Localization with Bucketing and Bucketing Guided Rescoring - are implemented here. - - Please refer to https://arxiv.org/abs/1912.04260 for more details. - - Args: - num_buckets (int): Number of buckets. - scale_factor (int): Scale factor of proposals to generate buckets. - offset_topk (int): Topk buckets are used to generate - bucket fine regression targets. Defaults to 2. - offset_upperbound (float): Offset upperbound to generate - bucket fine regression targets. - To avoid too large offset displacements. Defaults to 1.0. - cls_ignore_neighbor (bool): Ignore second nearest bucket or Not. - Defaults to True. - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Defaults to True. - """ - - def __init__(self, - num_buckets, - scale_factor, - offset_topk=2, - offset_upperbound=1.0, - cls_ignore_neighbor=True, - clip_border=True): - super(BucketingBBoxCoder, self).__init__() - self.num_buckets = num_buckets - self.scale_factor = scale_factor - self.offset_topk = offset_topk - self.offset_upperbound = offset_upperbound - self.cls_ignore_neighbor = cls_ignore_neighbor - self.clip_border = clip_border - - def encode(self, bboxes, gt_bboxes): - """Get bucketing estimation and fine regression targets during - training. - - Args: - bboxes (torch.Tensor): source boxes, e.g., object proposals. - gt_bboxes (torch.Tensor): target of the transformation, e.g., - ground truth boxes. - - Returns: - encoded_bboxes(tuple[Tensor]): bucketing estimation - and fine regression targets and weights - """ - - assert bboxes.size(0) == gt_bboxes.size(0) - assert bboxes.size(-1) == gt_bboxes.size(-1) == 4 - encoded_bboxes = bbox2bucket(bboxes, gt_bboxes, self.num_buckets, - self.scale_factor, self.offset_topk, - self.offset_upperbound, - self.cls_ignore_neighbor) - return encoded_bboxes - - def decode(self, bboxes, pred_bboxes, max_shape=None): - """Apply transformation `pred_bboxes` to `boxes`. - Args: - boxes (torch.Tensor): Basic boxes. - pred_bboxes (torch.Tensor): Predictions for bucketing estimation - and fine regression - max_shape (tuple[int], optional): Maximum shape of boxes. - Defaults to None. - - Returns: - torch.Tensor: Decoded boxes. - """ - assert len(pred_bboxes) == 2 - cls_preds, offset_preds = pred_bboxes - assert cls_preds.size(0) == bboxes.size(0) and offset_preds.size( - 0) == bboxes.size(0) - decoded_bboxes = bucket2bbox(bboxes, cls_preds, offset_preds, - self.num_buckets, self.scale_factor, - max_shape, self.clip_border) - - return decoded_bboxes - - -@mmcv.jit(coderize=True) -def generat_buckets(proposals, num_buckets, scale_factor=1.0): - """Generate buckets w.r.t bucket number and scale factor of proposals. - - Args: - proposals (Tensor): Shape (n, 4) - num_buckets (int): Number of buckets. - scale_factor (float): Scale factor to rescale proposals. - - Returns: - tuple[Tensor]: (bucket_w, bucket_h, l_buckets, r_buckets, - t_buckets, d_buckets) - - - bucket_w: Width of buckets on x-axis. Shape (n, ). - - bucket_h: Height of buckets on y-axis. Shape (n, ). - - l_buckets: Left buckets. Shape (n, ceil(side_num/2)). - - r_buckets: Right buckets. Shape (n, ceil(side_num/2)). - - t_buckets: Top buckets. Shape (n, ceil(side_num/2)). - - d_buckets: Down buckets. Shape (n, ceil(side_num/2)). - """ - proposals = bbox_rescale(proposals, scale_factor) - - # number of buckets in each side - side_num = int(np.ceil(num_buckets / 2.0)) - pw = proposals[..., 2] - proposals[..., 0] - ph = proposals[..., 3] - proposals[..., 1] - px1 = proposals[..., 0] - py1 = proposals[..., 1] - px2 = proposals[..., 2] - py2 = proposals[..., 3] - - bucket_w = pw / num_buckets - bucket_h = ph / num_buckets - - # left buckets - l_buckets = px1[:, None] + (0.5 + torch.arange( - 0, side_num).to(proposals).float())[None, :] * bucket_w[:, None] - # right buckets - r_buckets = px2[:, None] - (0.5 + torch.arange( - 0, side_num).to(proposals).float())[None, :] * bucket_w[:, None] - # top buckets - t_buckets = py1[:, None] + (0.5 + torch.arange( - 0, side_num).to(proposals).float())[None, :] * bucket_h[:, None] - # down buckets - d_buckets = py2[:, None] - (0.5 + torch.arange( - 0, side_num).to(proposals).float())[None, :] * bucket_h[:, None] - return bucket_w, bucket_h, l_buckets, r_buckets, t_buckets, d_buckets - - -@mmcv.jit(coderize=True) -def bbox2bucket(proposals, - gt, - num_buckets, - scale_factor, - offset_topk=2, - offset_upperbound=1.0, - cls_ignore_neighbor=True): - """Generate buckets estimation and fine regression targets. - - Args: - proposals (Tensor): Shape (n, 4) - gt (Tensor): Shape (n, 4) - num_buckets (int): Number of buckets. - scale_factor (float): Scale factor to rescale proposals. - offset_topk (int): Topk buckets are used to generate - bucket fine regression targets. Defaults to 2. - offset_upperbound (float): Offset allowance to generate - bucket fine regression targets. - To avoid too large offset displacements. Defaults to 1.0. - cls_ignore_neighbor (bool): Ignore second nearest bucket or Not. - Defaults to True. - - Returns: - tuple[Tensor]: (offsets, offsets_weights, bucket_labels, cls_weights). - - - offsets: Fine regression targets. \ - Shape (n, num_buckets*2). - - offsets_weights: Fine regression weights. \ - Shape (n, num_buckets*2). - - bucket_labels: Bucketing estimation labels. \ - Shape (n, num_buckets*2). - - cls_weights: Bucketing estimation weights. \ - Shape (n, num_buckets*2). - """ - assert proposals.size() == gt.size() - - # generate buckets - proposals = proposals.float() - gt = gt.float() - (bucket_w, bucket_h, l_buckets, r_buckets, t_buckets, - d_buckets) = generat_buckets(proposals, num_buckets, scale_factor) - - gx1 = gt[..., 0] - gy1 = gt[..., 1] - gx2 = gt[..., 2] - gy2 = gt[..., 3] - - # generate offset targets and weights - # offsets from buckets to gts - l_offsets = (l_buckets - gx1[:, None]) / bucket_w[:, None] - r_offsets = (r_buckets - gx2[:, None]) / bucket_w[:, None] - t_offsets = (t_buckets - gy1[:, None]) / bucket_h[:, None] - d_offsets = (d_buckets - gy2[:, None]) / bucket_h[:, None] - - # select top-k nearest buckets - l_topk, l_label = l_offsets.abs().topk( - offset_topk, dim=1, largest=False, sorted=True) - r_topk, r_label = r_offsets.abs().topk( - offset_topk, dim=1, largest=False, sorted=True) - t_topk, t_label = t_offsets.abs().topk( - offset_topk, dim=1, largest=False, sorted=True) - d_topk, d_label = d_offsets.abs().topk( - offset_topk, dim=1, largest=False, sorted=True) - - offset_l_weights = l_offsets.new_zeros(l_offsets.size()) - offset_r_weights = r_offsets.new_zeros(r_offsets.size()) - offset_t_weights = t_offsets.new_zeros(t_offsets.size()) - offset_d_weights = d_offsets.new_zeros(d_offsets.size()) - inds = torch.arange(0, proposals.size(0)).to(proposals).long() - - # generate offset weights of top-k nearest buckets - for k in range(offset_topk): - if k >= 1: - offset_l_weights[inds, l_label[:, - k]] = (l_topk[:, k] < - offset_upperbound).float() - offset_r_weights[inds, r_label[:, - k]] = (r_topk[:, k] < - offset_upperbound).float() - offset_t_weights[inds, t_label[:, - k]] = (t_topk[:, k] < - offset_upperbound).float() - offset_d_weights[inds, d_label[:, - k]] = (d_topk[:, k] < - offset_upperbound).float() - else: - offset_l_weights[inds, l_label[:, k]] = 1.0 - offset_r_weights[inds, r_label[:, k]] = 1.0 - offset_t_weights[inds, t_label[:, k]] = 1.0 - offset_d_weights[inds, d_label[:, k]] = 1.0 - - offsets = torch.cat([l_offsets, r_offsets, t_offsets, d_offsets], dim=-1) - offsets_weights = torch.cat([ - offset_l_weights, offset_r_weights, offset_t_weights, offset_d_weights - ], - dim=-1) - - # generate bucket labels and weight - side_num = int(np.ceil(num_buckets / 2.0)) - labels = torch.stack( - [l_label[:, 0], r_label[:, 0], t_label[:, 0], d_label[:, 0]], dim=-1) - - batch_size = labels.size(0) - bucket_labels = F.one_hot(labels.view(-1), side_num).view(batch_size, - -1).float() - bucket_cls_l_weights = (l_offsets.abs() < 1).float() - bucket_cls_r_weights = (r_offsets.abs() < 1).float() - bucket_cls_t_weights = (t_offsets.abs() < 1).float() - bucket_cls_d_weights = (d_offsets.abs() < 1).float() - bucket_cls_weights = torch.cat([ - bucket_cls_l_weights, bucket_cls_r_weights, bucket_cls_t_weights, - bucket_cls_d_weights - ], - dim=-1) - # ignore second nearest buckets for cls if necessary - if cls_ignore_neighbor: - bucket_cls_weights = (~((bucket_cls_weights == 1) & - (bucket_labels == 0))).float() - else: - bucket_cls_weights[:] = 1.0 - return offsets, offsets_weights, bucket_labels, bucket_cls_weights - - -@mmcv.jit(coderize=True) -def bucket2bbox(proposals, - cls_preds, - offset_preds, - num_buckets, - scale_factor=1.0, - max_shape=None, - clip_border=True): - """Apply bucketing estimation (cls preds) and fine regression (offset - preds) to generate det bboxes. - - Args: - proposals (Tensor): Boxes to be transformed. Shape (n, 4) - cls_preds (Tensor): bucketing estimation. Shape (n, num_buckets*2). - offset_preds (Tensor): fine regression. Shape (n, num_buckets*2). - num_buckets (int): Number of buckets. - scale_factor (float): Scale factor to rescale proposals. - max_shape (tuple[int, int]): Maximum bounds for boxes. specifies (H, W) - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Defaults to True. - - Returns: - tuple[Tensor]: (bboxes, loc_confidence). - - - bboxes: predicted bboxes. Shape (n, 4) - - loc_confidence: localization confidence of predicted bboxes. - Shape (n,). - """ - - side_num = int(np.ceil(num_buckets / 2.0)) - cls_preds = cls_preds.view(-1, side_num) - offset_preds = offset_preds.view(-1, side_num) - - scores = F.softmax(cls_preds, dim=1) - score_topk, score_label = scores.topk(2, dim=1, largest=True, sorted=True) - - rescaled_proposals = bbox_rescale(proposals, scale_factor) - - pw = rescaled_proposals[..., 2] - rescaled_proposals[..., 0] - ph = rescaled_proposals[..., 3] - rescaled_proposals[..., 1] - px1 = rescaled_proposals[..., 0] - py1 = rescaled_proposals[..., 1] - px2 = rescaled_proposals[..., 2] - py2 = rescaled_proposals[..., 3] - - bucket_w = pw / num_buckets - bucket_h = ph / num_buckets - - score_inds_l = score_label[0::4, 0] - score_inds_r = score_label[1::4, 0] - score_inds_t = score_label[2::4, 0] - score_inds_d = score_label[3::4, 0] - l_buckets = px1 + (0.5 + score_inds_l.float()) * bucket_w - r_buckets = px2 - (0.5 + score_inds_r.float()) * bucket_w - t_buckets = py1 + (0.5 + score_inds_t.float()) * bucket_h - d_buckets = py2 - (0.5 + score_inds_d.float()) * bucket_h - - offsets = offset_preds.view(-1, 4, side_num) - inds = torch.arange(proposals.size(0)).to(proposals).long() - l_offsets = offsets[:, 0, :][inds, score_inds_l] - r_offsets = offsets[:, 1, :][inds, score_inds_r] - t_offsets = offsets[:, 2, :][inds, score_inds_t] - d_offsets = offsets[:, 3, :][inds, score_inds_d] - - x1 = l_buckets - l_offsets * bucket_w - x2 = r_buckets - r_offsets * bucket_w - y1 = t_buckets - t_offsets * bucket_h - y2 = d_buckets - d_offsets * bucket_h - - if clip_border and max_shape is not None: - x1 = x1.clamp(min=0, max=max_shape[1] - 1) - y1 = y1.clamp(min=0, max=max_shape[0] - 1) - x2 = x2.clamp(min=0, max=max_shape[1] - 1) - y2 = y2.clamp(min=0, max=max_shape[0] - 1) - bboxes = torch.cat([x1[:, None], y1[:, None], x2[:, None], y2[:, None]], - dim=-1) - - # bucketing guided rescoring - loc_confidence = score_topk[:, 0] - top2_neighbor_inds = (score_label[:, 0] - score_label[:, 1]).abs() == 1 - loc_confidence += score_topk[:, 1] * top2_neighbor_inds.float() - loc_confidence = loc_confidence.view(-1, 4).mean(dim=1) - - return bboxes, loc_confidence diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/delta_xywh_bbox_coder.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/delta_xywh_bbox_coder.py deleted file mode 100755 index 51ae91195..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/delta_xywh_bbox_coder.py +++ /dev/null @@ -1,392 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv -import numpy as np -import torch - -from ..builder import BBOX_CODERS -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class DeltaXYWHBBoxCoder(BaseBBoxCoder): - """Delta XYWH BBox coder. - - Following the practice in `R-CNN `_, - this coder encodes bbox (x1, y1, x2, y2) into delta (dx, dy, dw, dh) and - decodes delta (dx, dy, dw, dh) back to original bbox (x1, y1, x2, y2). - - Args: - target_means (Sequence[float]): Denormalizing means of target for - delta coordinates - target_stds (Sequence[float]): Denormalizing standard deviation of - target for delta coordinates - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Defaults to True. - add_ctr_clamp (bool): Whether to add center clamp, when added, the - predicted box is clamped is its center is too far away from - the original anchor's center. Only used by YOLOF. Default False. - ctr_clamp (int): the maximum pixel shift to clamp. Only used by YOLOF. - Default 32. - """ - - def __init__(self, - target_means=(0., 0., 0., 0.), - target_stds=(1., 1., 1., 1.), - clip_border=True, - add_ctr_clamp=False, - ctr_clamp=32): - super(BaseBBoxCoder, self).__init__() - self.means = target_means - self.stds = target_stds - self.clip_border = clip_border - self.add_ctr_clamp = add_ctr_clamp - self.ctr_clamp = ctr_clamp - - def encode(self, bboxes, gt_bboxes): - """Get box regression transformation deltas that can be used to - transform the ``bboxes`` into the ``gt_bboxes``. - - Args: - bboxes (torch.Tensor): Source boxes, e.g., object proposals. - gt_bboxes (torch.Tensor): Target of the transformation, e.g., - ground-truth boxes. - - Returns: - torch.Tensor: Box transformation deltas - """ - - assert bboxes.size(0) == gt_bboxes.size(0) - assert bboxes.size(-1) == gt_bboxes.size(-1) == 4 - encoded_bboxes = bbox2delta(bboxes, gt_bboxes, self.means, self.stds) - return encoded_bboxes - - def decode(self, - bboxes, - pred_bboxes, - max_shape=None, - wh_ratio_clip=16 / 1000): - """Apply transformation `pred_bboxes` to `boxes`. - - Args: - bboxes (torch.Tensor): Basic boxes. Shape (B, N, 4) or (N, 4) - pred_bboxes (Tensor): Encoded offsets with respect to each roi. - Has shape (B, N, num_classes * 4) or (B, N, 4) or - (N, num_classes * 4) or (N, 4). Note N = num_anchors * W * H - when rois is a grid of anchors.Offset encoding follows [1]_. - max_shape (Sequence[int] or torch.Tensor or Sequence[ - Sequence[int]],optional): Maximum bounds for boxes, specifies - (H, W, C) or (H, W). If bboxes shape is (B, N, 4), then - the max_shape should be a Sequence[Sequence[int]] - and the length of max_shape should also be B. - wh_ratio_clip (float, optional): The allowed ratio between - width and height. - - Returns: - torch.Tensor: Decoded boxes. - """ - - assert pred_bboxes.size(0) == bboxes.size(0) - if pred_bboxes.ndim == 3: - assert pred_bboxes.size(1) == bboxes.size(1) - - if pred_bboxes.ndim == 2 and not torch.onnx.is_in_onnx_export(): - # single image decode - decoded_bboxes = delta2bbox(bboxes, pred_bboxes, self.means, - self.stds, max_shape, wh_ratio_clip, - self.clip_border, self.add_ctr_clamp, - self.ctr_clamp) - else: - if pred_bboxes.ndim == 3 and not torch.onnx.is_in_onnx_export(): - warnings.warn( - 'DeprecationWarning: onnx_delta2bbox is deprecated ' - 'in the case of batch decoding and non-ONNX, ' - 'please use “delta2bbox” instead. In order to improve ' - 'the decoding speed, the batch function will no ' - 'longer be supported. ') - decoded_bboxes = onnx_delta2bbox(bboxes, pred_bboxes, self.means, - self.stds, max_shape, - wh_ratio_clip, self.clip_border, - self.add_ctr_clamp, - self.ctr_clamp) - - return decoded_bboxes - - -@mmcv.jit(coderize=True) -def bbox2delta(proposals, gt, means=(0., 0., 0., 0.), stds=(1., 1., 1., 1.)): - """Compute deltas of proposals w.r.t. gt. - - We usually compute the deltas of x, y, w, h of proposals w.r.t ground - truth bboxes to get regression target. - This is the inverse function of :func:`delta2bbox`. - - Args: - proposals (Tensor): Boxes to be transformed, shape (N, ..., 4) - gt (Tensor): Gt bboxes to be used as base, shape (N, ..., 4) - means (Sequence[float]): Denormalizing means for delta coordinates - stds (Sequence[float]): Denormalizing standard deviation for delta - coordinates - - Returns: - Tensor: deltas with shape (N, 4), where columns represent dx, dy, - dw, dh. - """ - assert proposals.size() == gt.size() - - proposals = proposals.float() - gt = gt.float() - px = (proposals[..., 0] + proposals[..., 2]) * 0.5 - py = (proposals[..., 1] + proposals[..., 3]) * 0.5 - pw = proposals[..., 2] - proposals[..., 0] - ph = proposals[..., 3] - proposals[..., 1] - - gx = (gt[..., 0] + gt[..., 2]) * 0.5 - gy = (gt[..., 1] + gt[..., 3]) * 0.5 - gw = gt[..., 2] - gt[..., 0] - gh = gt[..., 3] - gt[..., 1] - - dx = (gx - px) / pw - dy = (gy - py) / ph - dw = torch.log(gw / pw) - dh = torch.log(gh / ph) - deltas = torch.stack([dx, dy, dw, dh], dim=-1) - - means = deltas.new_tensor(means).unsqueeze(0) - stds = deltas.new_tensor(stds).unsqueeze(0) - deltas = deltas.sub_(means).div_(stds) - - return deltas - - -@mmcv.jit(coderize=True) -def delta2bbox(rois, - deltas, - means=(0., 0., 0., 0.), - stds=(1., 1., 1., 1.), - max_shape=None, - wh_ratio_clip=16 / 1000, - clip_border=True, - add_ctr_clamp=False, - ctr_clamp=32): - """Apply deltas to shift/scale base boxes. - - Typically the rois are anchor or proposed bounding boxes and the deltas are - network outputs used to shift/scale those boxes. - This is the inverse function of :func:`bbox2delta`. - - Args: - rois (Tensor): Boxes to be transformed. Has shape (N, 4). - deltas (Tensor): Encoded offsets relative to each roi. - Has shape (N, num_classes * 4) or (N, 4). Note - N = num_base_anchors * W * H, when rois is a grid of - anchors. Offset encoding follows [1]_. - means (Sequence[float]): Denormalizing means for delta coordinates. - Default (0., 0., 0., 0.). - stds (Sequence[float]): Denormalizing standard deviation for delta - coordinates. Default (1., 1., 1., 1.). - max_shape (tuple[int, int]): Maximum bounds for boxes, specifies - (H, W). Default None. - wh_ratio_clip (float): Maximum aspect ratio for boxes. Default - 16 / 1000. - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Default True. - add_ctr_clamp (bool): Whether to add center clamp. When set to True, - the center of the prediction bounding box will be clamped to - avoid being too far away from the center of the anchor. - Only used by YOLOF. Default False. - ctr_clamp (int): the maximum pixel shift to clamp. Only used by YOLOF. - Default 32. - - Returns: - Tensor: Boxes with shape (N, num_classes * 4) or (N, 4), where 4 - represent tl_x, tl_y, br_x, br_y. - - References: - .. [1] https://arxiv.org/abs/1311.2524 - - Example: - >>> rois = torch.Tensor([[ 0., 0., 1., 1.], - >>> [ 0., 0., 1., 1.], - >>> [ 0., 0., 1., 1.], - >>> [ 5., 5., 5., 5.]]) - >>> deltas = torch.Tensor([[ 0., 0., 0., 0.], - >>> [ 1., 1., 1., 1.], - >>> [ 0., 0., 2., -1.], - >>> [ 0.7, -1.9, -0.5, 0.3]]) - >>> delta2bbox(rois, deltas, max_shape=(32, 32, 3)) - tensor([[0.0000, 0.0000, 1.0000, 1.0000], - [0.1409, 0.1409, 2.8591, 2.8591], - [0.0000, 0.3161, 4.1945, 0.6839], - [5.0000, 5.0000, 5.0000, 5.0000]]) - """ - num_bboxes, num_classes = deltas.size(0), deltas.size(1) // 4 - if num_bboxes == 0: - return deltas - - deltas = deltas.reshape(-1, 4) - - means = deltas.new_tensor(means).view(1, -1) - stds = deltas.new_tensor(stds).view(1, -1) - denorm_deltas = deltas * stds + means - - dxy = denorm_deltas[:, :2] - dwh = denorm_deltas[:, 2:] - - # Compute width/height of each roi - rois_ = rois.repeat(1, num_classes).reshape(-1, 4) - pxy = ((rois_[:, :2] + rois_[:, 2:]) * 0.5) - pwh = (rois_[:, 2:] - rois_[:, :2]) - - dxy_wh = pwh * dxy - - max_ratio = np.abs(np.log(wh_ratio_clip)) - if add_ctr_clamp: - dxy_wh = torch.clamp(dxy_wh, max=ctr_clamp, min=-ctr_clamp) - dwh = torch.clamp(dwh, max=max_ratio) - else: - dwh = dwh.clamp(min=-max_ratio, max=max_ratio) - - gxy = pxy + dxy_wh - gwh = pwh * dwh.exp() - x1y1 = gxy - (gwh * 0.5) - x2y2 = gxy + (gwh * 0.5) - bboxes = torch.cat([x1y1, x2y2], dim=-1) - if clip_border and max_shape is not None: - bboxes[..., 0::2].clamp_(min=0, max=max_shape[1]) - bboxes[..., 1::2].clamp_(min=0, max=max_shape[0]) - bboxes = bboxes.reshape(num_bboxes, -1) - return bboxes - - -def onnx_delta2bbox(rois, - deltas, - means=(0., 0., 0., 0.), - stds=(1., 1., 1., 1.), - max_shape=None, - wh_ratio_clip=16 / 1000, - clip_border=True, - add_ctr_clamp=False, - ctr_clamp=32): - """Apply deltas to shift/scale base boxes. - - Typically the rois are anchor or proposed bounding boxes and the deltas are - network outputs used to shift/scale those boxes. - This is the inverse function of :func:`bbox2delta`. - - Args: - rois (Tensor): Boxes to be transformed. Has shape (N, 4) or (B, N, 4) - deltas (Tensor): Encoded offsets with respect to each roi. - Has shape (B, N, num_classes * 4) or (B, N, 4) or - (N, num_classes * 4) or (N, 4). Note N = num_anchors * W * H - when rois is a grid of anchors.Offset encoding follows [1]_. - means (Sequence[float]): Denormalizing means for delta coordinates. - Default (0., 0., 0., 0.). - stds (Sequence[float]): Denormalizing standard deviation for delta - coordinates. Default (1., 1., 1., 1.). - max_shape (Sequence[int] or torch.Tensor or Sequence[ - Sequence[int]],optional): Maximum bounds for boxes, specifies - (H, W, C) or (H, W). If rois shape is (B, N, 4), then - the max_shape should be a Sequence[Sequence[int]] - and the length of max_shape should also be B. Default None. - wh_ratio_clip (float): Maximum aspect ratio for boxes. - Default 16 / 1000. - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Default True. - add_ctr_clamp (bool): Whether to add center clamp, when added, the - predicted box is clamped is its center is too far away from - the original anchor's center. Only used by YOLOF. Default False. - ctr_clamp (int): the maximum pixel shift to clamp. Only used by YOLOF. - Default 32. - - Returns: - Tensor: Boxes with shape (B, N, num_classes * 4) or (B, N, 4) or - (N, num_classes * 4) or (N, 4), where 4 represent - tl_x, tl_y, br_x, br_y. - - References: - .. [1] https://arxiv.org/abs/1311.2524 - - Example: - >>> rois = torch.Tensor([[ 0., 0., 1., 1.], - >>> [ 0., 0., 1., 1.], - >>> [ 0., 0., 1., 1.], - >>> [ 5., 5., 5., 5.]]) - >>> deltas = torch.Tensor([[ 0., 0., 0., 0.], - >>> [ 1., 1., 1., 1.], - >>> [ 0., 0., 2., -1.], - >>> [ 0.7, -1.9, -0.5, 0.3]]) - >>> delta2bbox(rois, deltas, max_shape=(32, 32, 3)) - tensor([[0.0000, 0.0000, 1.0000, 1.0000], - [0.1409, 0.1409, 2.8591, 2.8591], - [0.0000, 0.3161, 4.1945, 0.6839], - [5.0000, 5.0000, 5.0000, 5.0000]]) - """ - means = deltas.new_tensor(means).view(1, - -1).repeat(1, - deltas.size(-1) // 4) - stds = deltas.new_tensor(stds).view(1, -1).repeat(1, deltas.size(-1) // 4) - denorm_deltas = deltas * stds + means - dx = denorm_deltas[..., 0::4] - dy = denorm_deltas[..., 1::4] - dw = denorm_deltas[..., 2::4] - dh = denorm_deltas[..., 3::4] - - x1, y1 = rois[..., 0], rois[..., 1] - x2, y2 = rois[..., 2], rois[..., 3] - # Compute center of each roi - px = ((x1 + x2) * 0.5).unsqueeze(-1).expand_as(dx) - py = ((y1 + y2) * 0.5).unsqueeze(-1).expand_as(dy) - # Compute width/height of each roi - pw = (x2 - x1).unsqueeze(-1).expand_as(dw) - ph = (y2 - y1).unsqueeze(-1).expand_as(dh) - - dx_width = pw * dx - dy_height = ph * dy - - max_ratio = np.abs(np.log(wh_ratio_clip)) - if add_ctr_clamp: - dx_width = torch.clamp(dx_width, max=ctr_clamp, min=-ctr_clamp) - dy_height = torch.clamp(dy_height, max=ctr_clamp, min=-ctr_clamp) - dw = torch.clamp(dw, max=max_ratio) - dh = torch.clamp(dh, max=max_ratio) - else: - dw = dw.clamp(min=-max_ratio, max=max_ratio) - dh = dh.clamp(min=-max_ratio, max=max_ratio) - # Use exp(network energy) to enlarge/shrink each roi - gw = pw * dw.exp() - gh = ph * dh.exp() - # Use network energy to shift the center of each roi - gx = px + dx_width - gy = py + dy_height - # Convert center-xy/width/height to top-left, bottom-right - x1 = gx - gw * 0.5 - y1 = gy - gh * 0.5 - x2 = gx + gw * 0.5 - y2 = gy + gh * 0.5 - - bboxes = torch.stack([x1, y1, x2, y2], dim=-1).view(deltas.size()) - - if clip_border and max_shape is not None: - # clip bboxes with dynamic `min` and `max` for onnx - if torch.onnx.is_in_onnx_export(): - from mmdet.core.export import dynamic_clip_for_onnx - x1, y1, x2, y2 = dynamic_clip_for_onnx(x1, y1, x2, y2, max_shape) - bboxes = torch.stack([x1, y1, x2, y2], dim=-1).view(deltas.size()) - return bboxes - if not isinstance(max_shape, torch.Tensor): - max_shape = x1.new_tensor(max_shape) - max_shape = max_shape[..., :2].type_as(x1) - if max_shape.ndim == 2: - assert bboxes.ndim == 3 - assert max_shape.size(0) == bboxes.size(0) - - min_xy = x1.new_tensor(0) - max_xy = torch.cat( - [max_shape] * (deltas.size(-1) // 2), - dim=-1).flip(-1).unsqueeze(-2) - bboxes = torch.where(bboxes < min_xy, min_xy, bboxes) - bboxes = torch.where(bboxes > max_xy, max_xy, bboxes) - - return bboxes diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/distance_point_bbox_coder.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/distance_point_bbox_coder.py deleted file mode 100755 index bd9d0cae6..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/distance_point_bbox_coder.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..builder import BBOX_CODERS -from ..transforms import bbox2distance, distance2bbox -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class DistancePointBBoxCoder(BaseBBoxCoder): - """Distance Point BBox coder. - - This coder encodes gt bboxes (x1, y1, x2, y2) into (top, bottom, left, - right) and decode it back to the original. - - Args: - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Defaults to True. - """ - - def __init__(self, clip_border=True): - super(BaseBBoxCoder, self).__init__() - self.clip_border = clip_border - - def encode(self, points, gt_bboxes, max_dis=None, eps=0.1): - """Encode bounding box to distances. - - Args: - points (Tensor): Shape (N, 2), The format is [x, y]. - gt_bboxes (Tensor): Shape (N, 4), The format is "xyxy" - max_dis (float): Upper bound of the distance. Default None. - eps (float): a small value to ensure target < max_dis, instead <=. - Default 0.1. - - Returns: - Tensor: Box transformation deltas. The shape is (N, 4). - """ - assert points.size(0) == gt_bboxes.size(0) - assert points.size(-1) == 2 - assert gt_bboxes.size(-1) == 4 - return bbox2distance(points, gt_bboxes, max_dis, eps) - - def decode(self, points, pred_bboxes, max_shape=None): - """Decode distance prediction to bounding box. - - Args: - points (Tensor): Shape (B, N, 2) or (N, 2). - pred_bboxes (Tensor): Distance from the given point to 4 - boundaries (left, top, right, bottom). Shape (B, N, 4) - or (N, 4) - max_shape (Sequence[int] or torch.Tensor or Sequence[ - Sequence[int]],optional): Maximum bounds for boxes, specifies - (H, W, C) or (H, W). If priors shape is (B, N, 4), then - the max_shape should be a Sequence[Sequence[int]], - and the length of max_shape should also be B. - Default None. - Returns: - Tensor: Boxes with shape (N, 4) or (B, N, 4) - """ - assert points.size(0) == pred_bboxes.size(0) - assert points.size(-1) == 2 - assert pred_bboxes.size(-1) == 4 - if self.clip_border is False: - max_shape = None - return distance2bbox(points, pred_bboxes, max_shape) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/legacy_delta_xywh_bbox_coder.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/legacy_delta_xywh_bbox_coder.py deleted file mode 100755 index 5385c8ce9..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/legacy_delta_xywh_bbox_coder.py +++ /dev/null @@ -1,216 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np -import torch - -from ..builder import BBOX_CODERS -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class LegacyDeltaXYWHBBoxCoder(BaseBBoxCoder): - """Legacy Delta XYWH BBox coder used in MMDet V1.x. - - Following the practice in R-CNN [1]_, this coder encodes bbox (x1, y1, x2, - y2) into delta (dx, dy, dw, dh) and decodes delta (dx, dy, dw, dh) - back to original bbox (x1, y1, x2, y2). - - Note: - The main difference between :class`LegacyDeltaXYWHBBoxCoder` and - :class:`DeltaXYWHBBoxCoder` is whether ``+ 1`` is used during width and - height calculation. We suggest to only use this coder when testing with - MMDet V1.x models. - - References: - .. [1] https://arxiv.org/abs/1311.2524 - - Args: - target_means (Sequence[float]): denormalizing means of target for - delta coordinates - target_stds (Sequence[float]): denormalizing standard deviation of - target for delta coordinates - """ - - def __init__(self, - target_means=(0., 0., 0., 0.), - target_stds=(1., 1., 1., 1.)): - super(BaseBBoxCoder, self).__init__() - self.means = target_means - self.stds = target_stds - - def encode(self, bboxes, gt_bboxes): - """Get box regression transformation deltas that can be used to - transform the ``bboxes`` into the ``gt_bboxes``. - - Args: - bboxes (torch.Tensor): source boxes, e.g., object proposals. - gt_bboxes (torch.Tensor): target of the transformation, e.g., - ground-truth boxes. - - Returns: - torch.Tensor: Box transformation deltas - """ - assert bboxes.size(0) == gt_bboxes.size(0) - assert bboxes.size(-1) == gt_bboxes.size(-1) == 4 - encoded_bboxes = legacy_bbox2delta(bboxes, gt_bboxes, self.means, - self.stds) - return encoded_bboxes - - def decode(self, - bboxes, - pred_bboxes, - max_shape=None, - wh_ratio_clip=16 / 1000): - """Apply transformation `pred_bboxes` to `boxes`. - - Args: - boxes (torch.Tensor): Basic boxes. - pred_bboxes (torch.Tensor): Encoded boxes with shape - max_shape (tuple[int], optional): Maximum shape of boxes. - Defaults to None. - wh_ratio_clip (float, optional): The allowed ratio between - width and height. - - Returns: - torch.Tensor: Decoded boxes. - """ - assert pred_bboxes.size(0) == bboxes.size(0) - decoded_bboxes = legacy_delta2bbox(bboxes, pred_bboxes, self.means, - self.stds, max_shape, wh_ratio_clip) - - return decoded_bboxes - - -@mmcv.jit(coderize=True) -def legacy_bbox2delta(proposals, - gt, - means=(0., 0., 0., 0.), - stds=(1., 1., 1., 1.)): - """Compute deltas of proposals w.r.t. gt in the MMDet V1.x manner. - - We usually compute the deltas of x, y, w, h of proposals w.r.t ground - truth bboxes to get regression target. - This is the inverse function of `delta2bbox()` - - Args: - proposals (Tensor): Boxes to be transformed, shape (N, ..., 4) - gt (Tensor): Gt bboxes to be used as base, shape (N, ..., 4) - means (Sequence[float]): Denormalizing means for delta coordinates - stds (Sequence[float]): Denormalizing standard deviation for delta - coordinates - - Returns: - Tensor: deltas with shape (N, 4), where columns represent dx, dy, - dw, dh. - """ - assert proposals.size() == gt.size() - - proposals = proposals.float() - gt = gt.float() - px = (proposals[..., 0] + proposals[..., 2]) * 0.5 - py = (proposals[..., 1] + proposals[..., 3]) * 0.5 - pw = proposals[..., 2] - proposals[..., 0] + 1.0 - ph = proposals[..., 3] - proposals[..., 1] + 1.0 - - gx = (gt[..., 0] + gt[..., 2]) * 0.5 - gy = (gt[..., 1] + gt[..., 3]) * 0.5 - gw = gt[..., 2] - gt[..., 0] + 1.0 - gh = gt[..., 3] - gt[..., 1] + 1.0 - - dx = (gx - px) / pw - dy = (gy - py) / ph - dw = torch.log(gw / pw) - dh = torch.log(gh / ph) - deltas = torch.stack([dx, dy, dw, dh], dim=-1) - - means = deltas.new_tensor(means).unsqueeze(0) - stds = deltas.new_tensor(stds).unsqueeze(0) - deltas = deltas.sub_(means).div_(stds) - - return deltas - - -@mmcv.jit(coderize=True) -def legacy_delta2bbox(rois, - deltas, - means=(0., 0., 0., 0.), - stds=(1., 1., 1., 1.), - max_shape=None, - wh_ratio_clip=16 / 1000): - """Apply deltas to shift/scale base boxes in the MMDet V1.x manner. - - Typically the rois are anchor or proposed bounding boxes and the deltas are - network outputs used to shift/scale those boxes. - This is the inverse function of `bbox2delta()` - - Args: - rois (Tensor): Boxes to be transformed. Has shape (N, 4) - deltas (Tensor): Encoded offsets with respect to each roi. - Has shape (N, 4 * num_classes). Note N = num_anchors * W * H when - rois is a grid of anchors. Offset encoding follows [1]_. - means (Sequence[float]): Denormalizing means for delta coordinates - stds (Sequence[float]): Denormalizing standard deviation for delta - coordinates - max_shape (tuple[int, int]): Maximum bounds for boxes. specifies (H, W) - wh_ratio_clip (float): Maximum aspect ratio for boxes. - - Returns: - Tensor: Boxes with shape (N, 4), where columns represent - tl_x, tl_y, br_x, br_y. - - References: - .. [1] https://arxiv.org/abs/1311.2524 - - Example: - >>> rois = torch.Tensor([[ 0., 0., 1., 1.], - >>> [ 0., 0., 1., 1.], - >>> [ 0., 0., 1., 1.], - >>> [ 5., 5., 5., 5.]]) - >>> deltas = torch.Tensor([[ 0., 0., 0., 0.], - >>> [ 1., 1., 1., 1.], - >>> [ 0., 0., 2., -1.], - >>> [ 0.7, -1.9, -0.5, 0.3]]) - >>> legacy_delta2bbox(rois, deltas, max_shape=(32, 32)) - tensor([[0.0000, 0.0000, 1.5000, 1.5000], - [0.0000, 0.0000, 5.2183, 5.2183], - [0.0000, 0.1321, 7.8891, 0.8679], - [5.3967, 2.4251, 6.0033, 3.7749]]) - """ - means = deltas.new_tensor(means).repeat(1, deltas.size(1) // 4) - stds = deltas.new_tensor(stds).repeat(1, deltas.size(1) // 4) - denorm_deltas = deltas * stds + means - dx = denorm_deltas[:, 0::4] - dy = denorm_deltas[:, 1::4] - dw = denorm_deltas[:, 2::4] - dh = denorm_deltas[:, 3::4] - max_ratio = np.abs(np.log(wh_ratio_clip)) - dw = dw.clamp(min=-max_ratio, max=max_ratio) - dh = dh.clamp(min=-max_ratio, max=max_ratio) - # Compute center of each roi - px = ((rois[:, 0] + rois[:, 2]) * 0.5).unsqueeze(1).expand_as(dx) - py = ((rois[:, 1] + rois[:, 3]) * 0.5).unsqueeze(1).expand_as(dy) - # Compute width/height of each roi - pw = (rois[:, 2] - rois[:, 0] + 1.0).unsqueeze(1).expand_as(dw) - ph = (rois[:, 3] - rois[:, 1] + 1.0).unsqueeze(1).expand_as(dh) - # Use exp(network energy) to enlarge/shrink each roi - gw = pw * dw.exp() - gh = ph * dh.exp() - # Use network energy to shift the center of each roi - gx = px + pw * dx - gy = py + ph * dy - # Convert center-xy/width/height to top-left, bottom-right - - # The true legacy box coder should +- 0.5 here. - # However, current implementation improves the performance when testing - # the models trained in MMDetection 1.X (~0.5 bbox AP, 0.2 mask AP) - x1 = gx - gw * 0.5 - y1 = gy - gh * 0.5 - x2 = gx + gw * 0.5 - y2 = gy + gh * 0.5 - if max_shape is not None: - x1 = x1.clamp(min=0, max=max_shape[1] - 1) - y1 = y1.clamp(min=0, max=max_shape[0] - 1) - x2 = x2.clamp(min=0, max=max_shape[1] - 1) - y2 = y2.clamp(min=0, max=max_shape[0] - 1) - bboxes = torch.stack([x1, y1, x2, y2], dim=-1).view_as(deltas) - return bboxes diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/pseudo_bbox_coder.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/pseudo_bbox_coder.py deleted file mode 100755 index 19b41cff3..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/pseudo_bbox_coder.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..builder import BBOX_CODERS -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class PseudoBBoxCoder(BaseBBoxCoder): - """Pseudo bounding box coder.""" - - def __init__(self, **kwargs): - super(BaseBBoxCoder, self).__init__(**kwargs) - - def encode(self, bboxes, gt_bboxes): - """torch.Tensor: return the given ``bboxes``""" - return gt_bboxes - - def decode(self, bboxes, pred_bboxes): - """torch.Tensor: return the given ``pred_bboxes``""" - return pred_bboxes diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/tblr_bbox_coder.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/tblr_bbox_coder.py deleted file mode 100755 index 47a3dc5ed..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/tblr_bbox_coder.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch - -from ..builder import BBOX_CODERS -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class TBLRBBoxCoder(BaseBBoxCoder): - """TBLR BBox coder. - - Following the practice in `FSAF `_, - this coder encodes gt bboxes (x1, y1, x2, y2) into (top, bottom, left, - right) and decode it back to the original. - - Args: - normalizer (list | float): Normalization factor to be - divided with when coding the coordinates. If it is a list, it should - have length of 4 indicating normalization factor in tblr dims. - Otherwise it is a unified float factor for all dims. Default: 4.0 - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Defaults to True. - """ - - def __init__(self, normalizer=4.0, clip_border=True): - super(BaseBBoxCoder, self).__init__() - self.normalizer = normalizer - self.clip_border = clip_border - - def encode(self, bboxes, gt_bboxes): - """Get box regression transformation deltas that can be used to - transform the ``bboxes`` into the ``gt_bboxes`` in the (top, left, - bottom, right) order. - - Args: - bboxes (torch.Tensor): source boxes, e.g., object proposals. - gt_bboxes (torch.Tensor): target of the transformation, e.g., - ground truth boxes. - - Returns: - torch.Tensor: Box transformation deltas - """ - assert bboxes.size(0) == gt_bboxes.size(0) - assert bboxes.size(-1) == gt_bboxes.size(-1) == 4 - encoded_bboxes = bboxes2tblr( - bboxes, gt_bboxes, normalizer=self.normalizer) - return encoded_bboxes - - def decode(self, bboxes, pred_bboxes, max_shape=None): - """Apply transformation `pred_bboxes` to `boxes`. - - Args: - bboxes (torch.Tensor): Basic boxes.Shape (B, N, 4) or (N, 4) - pred_bboxes (torch.Tensor): Encoded boxes with shape - (B, N, 4) or (N, 4) - max_shape (Sequence[int] or torch.Tensor or Sequence[ - Sequence[int]],optional): Maximum bounds for boxes, specifies - (H, W, C) or (H, W). If bboxes shape is (B, N, 4), then - the max_shape should be a Sequence[Sequence[int]] - and the length of max_shape should also be B. - - Returns: - torch.Tensor: Decoded boxes. - """ - decoded_bboxes = tblr2bboxes( - bboxes, - pred_bboxes, - normalizer=self.normalizer, - max_shape=max_shape, - clip_border=self.clip_border) - - return decoded_bboxes - - -@mmcv.jit(coderize=True) -def bboxes2tblr(priors, gts, normalizer=4.0, normalize_by_wh=True): - """Encode ground truth boxes to tblr coordinate. - - It first convert the gt coordinate to tblr format, - (top, bottom, left, right), relative to prior box centers. - The tblr coordinate may be normalized by the side length of prior bboxes - if `normalize_by_wh` is specified as True, and it is then normalized by - the `normalizer` factor. - - Args: - priors (Tensor): Prior boxes in point form - Shape: (num_proposals,4). - gts (Tensor): Coords of ground truth for each prior in point-form - Shape: (num_proposals, 4). - normalizer (Sequence[float] | float): normalization parameter of - encoded boxes. If it is a list, it has to have length = 4. - Default: 4.0 - normalize_by_wh (bool): Whether to normalize tblr coordinate by the - side length (wh) of prior bboxes. - - Return: - encoded boxes (Tensor), Shape: (num_proposals, 4) - """ - - # dist b/t match center and prior's center - if not isinstance(normalizer, float): - normalizer = torch.tensor(normalizer, device=priors.device) - assert len(normalizer) == 4, 'Normalizer must have length = 4' - assert priors.size(0) == gts.size(0) - prior_centers = (priors[:, 0:2] + priors[:, 2:4]) / 2 - xmin, ymin, xmax, ymax = gts.split(1, dim=1) - top = prior_centers[:, 1].unsqueeze(1) - ymin - bottom = ymax - prior_centers[:, 1].unsqueeze(1) - left = prior_centers[:, 0].unsqueeze(1) - xmin - right = xmax - prior_centers[:, 0].unsqueeze(1) - loc = torch.cat((top, bottom, left, right), dim=1) - if normalize_by_wh: - # Normalize tblr by anchor width and height - wh = priors[:, 2:4] - priors[:, 0:2] - w, h = torch.split(wh, 1, dim=1) - loc[:, :2] /= h # tb is normalized by h - loc[:, 2:] /= w # lr is normalized by w - # Normalize tblr by the given normalization factor - return loc / normalizer - - -@mmcv.jit(coderize=True) -def tblr2bboxes(priors, - tblr, - normalizer=4.0, - normalize_by_wh=True, - max_shape=None, - clip_border=True): - """Decode tblr outputs to prediction boxes. - - The process includes 3 steps: 1) De-normalize tblr coordinates by - multiplying it with `normalizer`; 2) De-normalize tblr coordinates by the - prior bbox width and height if `normalize_by_wh` is `True`; 3) Convert - tblr (top, bottom, left, right) pair relative to the center of priors back - to (xmin, ymin, xmax, ymax) coordinate. - - Args: - priors (Tensor): Prior boxes in point form (x0, y0, x1, y1) - Shape: (N,4) or (B, N, 4). - tblr (Tensor): Coords of network output in tblr form - Shape: (N, 4) or (B, N, 4). - normalizer (Sequence[float] | float): Normalization parameter of - encoded boxes. By list, it represents the normalization factors at - tblr dims. By float, it is the unified normalization factor at all - dims. Default: 4.0 - normalize_by_wh (bool): Whether the tblr coordinates have been - normalized by the side length (wh) of prior bboxes. - max_shape (Sequence[int] or torch.Tensor or Sequence[ - Sequence[int]],optional): Maximum bounds for boxes, specifies - (H, W, C) or (H, W). If priors shape is (B, N, 4), then - the max_shape should be a Sequence[Sequence[int]] - and the length of max_shape should also be B. - clip_border (bool, optional): Whether clip the objects outside the - border of the image. Defaults to True. - - Return: - encoded boxes (Tensor): Boxes with shape (N, 4) or (B, N, 4) - """ - if not isinstance(normalizer, float): - normalizer = torch.tensor(normalizer, device=priors.device) - assert len(normalizer) == 4, 'Normalizer must have length = 4' - assert priors.size(0) == tblr.size(0) - if priors.ndim == 3: - assert priors.size(1) == tblr.size(1) - - loc_decode = tblr * normalizer - prior_centers = (priors[..., 0:2] + priors[..., 2:4]) / 2 - if normalize_by_wh: - wh = priors[..., 2:4] - priors[..., 0:2] - w, h = torch.split(wh, 1, dim=-1) - # Inplace operation with slice would failed for exporting to ONNX - th = h * loc_decode[..., :2] # tb - tw = w * loc_decode[..., 2:] # lr - loc_decode = torch.cat([th, tw], dim=-1) - # Cannot be exported using onnx when loc_decode.split(1, dim=-1) - top, bottom, left, right = loc_decode.split((1, 1, 1, 1), dim=-1) - xmin = prior_centers[..., 0].unsqueeze(-1) - left - xmax = prior_centers[..., 0].unsqueeze(-1) + right - ymin = prior_centers[..., 1].unsqueeze(-1) - top - ymax = prior_centers[..., 1].unsqueeze(-1) + bottom - - bboxes = torch.cat((xmin, ymin, xmax, ymax), dim=-1) - - if clip_border and max_shape is not None: - # clip bboxes with dynamic `min` and `max` for onnx - if torch.onnx.is_in_onnx_export(): - from mmdet.core.export import dynamic_clip_for_onnx - xmin, ymin, xmax, ymax = dynamic_clip_for_onnx( - xmin, ymin, xmax, ymax, max_shape) - bboxes = torch.cat([xmin, ymin, xmax, ymax], dim=-1) - return bboxes - if not isinstance(max_shape, torch.Tensor): - max_shape = priors.new_tensor(max_shape) - max_shape = max_shape[..., :2].type_as(priors) - if max_shape.ndim == 2: - assert bboxes.ndim == 3 - assert max_shape.size(0) == bboxes.size(0) - - min_xy = priors.new_tensor(0) - max_xy = torch.cat([max_shape, max_shape], - dim=-1).flip(-1).unsqueeze(-2) - bboxes = torch.where(bboxes < min_xy, min_xy, bboxes) - bboxes = torch.where(bboxes > max_xy, max_xy, bboxes) - - return bboxes diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/yolo_bbox_coder.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/yolo_bbox_coder.py deleted file mode 100755 index 3ddbf4b5e..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/coder/yolo_bbox_coder.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch - -from ..builder import BBOX_CODERS -from .base_bbox_coder import BaseBBoxCoder - - -@BBOX_CODERS.register_module() -class YOLOBBoxCoder(BaseBBoxCoder): - """YOLO BBox coder. - - Following `YOLO `_, this coder divide - image into grids, and encode bbox (x1, y1, x2, y2) into (cx, cy, dw, dh). - cx, cy in [0., 1.], denotes relative center position w.r.t the center of - bboxes. dw, dh are the same as :obj:`DeltaXYWHBBoxCoder`. - - Args: - eps (float): Min value of cx, cy when encoding. - """ - - def __init__(self, eps=1e-6): - super(BaseBBoxCoder, self).__init__() - self.eps = eps - - @mmcv.jit(coderize=True) - def encode(self, bboxes, gt_bboxes, stride): - """Get box regression transformation deltas that can be used to - transform the ``bboxes`` into the ``gt_bboxes``. - - Args: - bboxes (torch.Tensor): Source boxes, e.g., anchors. - gt_bboxes (torch.Tensor): Target of the transformation, e.g., - ground-truth boxes. - stride (torch.Tensor | int): Stride of bboxes. - - Returns: - torch.Tensor: Box transformation deltas - """ - - assert bboxes.size(0) == gt_bboxes.size(0) - assert bboxes.size(-1) == gt_bboxes.size(-1) == 4 - x_center_gt = (gt_bboxes[..., 0] + gt_bboxes[..., 2]) * 0.5 - y_center_gt = (gt_bboxes[..., 1] + gt_bboxes[..., 3]) * 0.5 - w_gt = gt_bboxes[..., 2] - gt_bboxes[..., 0] - h_gt = gt_bboxes[..., 3] - gt_bboxes[..., 1] - x_center = (bboxes[..., 0] + bboxes[..., 2]) * 0.5 - y_center = (bboxes[..., 1] + bboxes[..., 3]) * 0.5 - w = bboxes[..., 2] - bboxes[..., 0] - h = bboxes[..., 3] - bboxes[..., 1] - w_target = torch.log((w_gt / w).clamp(min=self.eps)) - h_target = torch.log((h_gt / h).clamp(min=self.eps)) - x_center_target = ((x_center_gt - x_center) / stride + 0.5).clamp( - self.eps, 1 - self.eps) - y_center_target = ((y_center_gt - y_center) / stride + 0.5).clamp( - self.eps, 1 - self.eps) - encoded_bboxes = torch.stack( - [x_center_target, y_center_target, w_target, h_target], dim=-1) - return encoded_bboxes - - @mmcv.jit(coderize=True) - def decode(self, bboxes, pred_bboxes, stride): - """Apply transformation `pred_bboxes` to `boxes`. - - Args: - boxes (torch.Tensor): Basic boxes, e.g. anchors. - pred_bboxes (torch.Tensor): Encoded boxes with shape - stride (torch.Tensor | int): Strides of bboxes. - - Returns: - torch.Tensor: Decoded boxes. - """ - assert pred_bboxes.size(-1) == bboxes.size(-1) == 4 - xy_centers = (bboxes[..., :2] + bboxes[..., 2:]) * 0.5 + ( - pred_bboxes[..., :2] - 0.5) * stride - whs = (bboxes[..., 2:] - - bboxes[..., :2]) * 0.5 * pred_bboxes[..., 2:].exp() - decoded_bboxes = torch.stack( - (xy_centers[..., 0] - whs[..., 0], xy_centers[..., 1] - - whs[..., 1], xy_centers[..., 0] + whs[..., 0], - xy_centers[..., 1] + whs[..., 1]), - dim=-1) - return decoded_bboxes diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/demodata.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/demodata.py deleted file mode 100755 index 1638207ce..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/demodata.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch - -from mmdet.utils.util_random import ensure_rng - - -def random_boxes(num=1, scale=1, rng=None): - """Simple version of ``kwimage.Boxes.random`` - - Returns: - Tensor: shape (n, 4) in x1, y1, x2, y2 format. - - References: - https://gitlab.kitware.com/computer-vision/kwimage/blob/master/kwimage/structs/boxes.py#L1390 - - Example: - >>> num = 3 - >>> scale = 512 - >>> rng = 0 - >>> boxes = random_boxes(num, scale, rng) - >>> print(boxes) - tensor([[280.9925, 278.9802, 308.6148, 366.1769], - [216.9113, 330.6978, 224.0446, 456.5878], - [405.3632, 196.3221, 493.3953, 270.7942]]) - """ - rng = ensure_rng(rng) - - tlbr = rng.rand(num, 4).astype(np.float32) - - tl_x = np.minimum(tlbr[:, 0], tlbr[:, 2]) - tl_y = np.minimum(tlbr[:, 1], tlbr[:, 3]) - br_x = np.maximum(tlbr[:, 0], tlbr[:, 2]) - br_y = np.maximum(tlbr[:, 1], tlbr[:, 3]) - - tlbr[:, 0] = tl_x * scale - tlbr[:, 1] = tl_y * scale - tlbr[:, 2] = br_x * scale - tlbr[:, 3] = br_y * scale - - boxes = torch.from_numpy(tlbr) - return boxes diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/iou_calculators/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/iou_calculators/__init__.py deleted file mode 100755 index 496dc7963..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/iou_calculators/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_iou_calculator -from .iou2d_calculator import BboxOverlaps2D, bbox_overlaps - -__all__ = ['build_iou_calculator', 'BboxOverlaps2D', 'bbox_overlaps'] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/iou_calculators/builder.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/iou_calculators/builder.py deleted file mode 100755 index d79d1a143..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/iou_calculators/builder.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, build_from_cfg - -IOU_CALCULATORS = Registry('IoU calculator') - - -def build_iou_calculator(cfg, default_args=None): - """Builder of IoU calculator.""" - return build_from_cfg(cfg, IOU_CALCULATORS, default_args) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/iou_calculators/iou2d_calculator.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/iou_calculators/iou2d_calculator.py deleted file mode 100755 index 25afb22c1..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/iou_calculators/iou2d_calculator.py +++ /dev/null @@ -1,261 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .builder import IOU_CALCULATORS - - -def cast_tensor_type(x, scale=1., dtype=None): - if dtype == 'fp16': - # scale is for preventing overflows - x = (x / scale).half() - return x - - -def fp16_clamp(x, min=None, max=None): - if not x.is_cuda and x.dtype == torch.float16: - # clamp for cpu float16, tensor fp16 has no clamp implementation - return x.float().clamp(min, max).half() - - return x.clamp(min, max) - - -@IOU_CALCULATORS.register_module() -class BboxOverlaps2D: - """2D Overlaps (e.g. IoUs, GIoUs) Calculator.""" - - def __init__(self, scale=1., dtype=None): - self.scale = scale - self.dtype = dtype - - def __call__(self, bboxes1, bboxes2, mode='iou', is_aligned=False): - """Calculate IoU between 2D bboxes. - - Args: - bboxes1 (Tensor): bboxes have shape (m, 4) in - format, or shape (m, 5) in format. - bboxes2 (Tensor): bboxes have shape (m, 4) in - format, shape (m, 5) in format, or be - empty. If ``is_aligned `` is ``True``, then m and n must be - equal. - mode (str): "iou" (intersection over union), "iof" (intersection - over foreground), or "giou" (generalized intersection over - union). - is_aligned (bool, optional): If True, then m and n must be equal. - Default False. - - Returns: - Tensor: shape (m, n) if ``is_aligned `` is False else shape (m,) - """ - assert bboxes1.size(-1) in [0, 4, 5] - assert bboxes2.size(-1) in [0, 4, 5] - if bboxes2.size(-1) == 5: - bboxes2 = bboxes2[..., :4] - if bboxes1.size(-1) == 5: - bboxes1 = bboxes1[..., :4] - - if self.dtype == 'fp16': - # change tensor type to save cpu and cuda memory and keep speed - bboxes1 = cast_tensor_type(bboxes1, self.scale, self.dtype) - bboxes2 = cast_tensor_type(bboxes2, self.scale, self.dtype) - overlaps = bbox_overlaps(bboxes1, bboxes2, mode, is_aligned) - if not overlaps.is_cuda and overlaps.dtype == torch.float16: - # resume cpu float32 - overlaps = overlaps.float() - return overlaps - - return bbox_overlaps(bboxes1, bboxes2, mode, is_aligned) - - def __repr__(self): - """str: a string describing the module""" - repr_str = self.__class__.__name__ + f'(' \ - f'scale={self.scale}, dtype={self.dtype})' - return repr_str - - -def bbox_overlaps(bboxes1, bboxes2, mode='iou', is_aligned=False, eps=1e-6): - """Calculate overlap between two set of bboxes. - - FP16 Contributed by https://github.com/open-mmlab/mmdetection/pull/4889 - Note: - Assume bboxes1 is M x 4, bboxes2 is N x 4, when mode is 'iou', - there are some new generated variable when calculating IOU - using bbox_overlaps function: - - 1) is_aligned is False - area1: M x 1 - area2: N x 1 - lt: M x N x 2 - rb: M x N x 2 - wh: M x N x 2 - overlap: M x N x 1 - union: M x N x 1 - ious: M x N x 1 - - Total memory: - S = (9 x N x M + N + M) * 4 Byte, - - When using FP16, we can reduce: - R = (9 x N x M + N + M) * 4 / 2 Byte - R large than (N + M) * 4 * 2 is always true when N and M >= 1. - Obviously, N + M <= N * M < 3 * N * M, when N >=2 and M >=2, - N + 1 < 3 * N, when N or M is 1. - - Given M = 40 (ground truth), N = 400000 (three anchor boxes - in per grid, FPN, R-CNNs), - R = 275 MB (one times) - - A special case (dense detection), M = 512 (ground truth), - R = 3516 MB = 3.43 GB - - When the batch size is B, reduce: - B x R - - Therefore, CUDA memory runs out frequently. - - Experiments on GeForce RTX 2080Ti (11019 MiB): - - | dtype | M | N | Use | Real | Ideal | - |:----:|:----:|:----:|:----:|:----:|:----:| - | FP32 | 512 | 400000 | 8020 MiB | -- | -- | - | FP16 | 512 | 400000 | 4504 MiB | 3516 MiB | 3516 MiB | - | FP32 | 40 | 400000 | 1540 MiB | -- | -- | - | FP16 | 40 | 400000 | 1264 MiB | 276MiB | 275 MiB | - - 2) is_aligned is True - area1: N x 1 - area2: N x 1 - lt: N x 2 - rb: N x 2 - wh: N x 2 - overlap: N x 1 - union: N x 1 - ious: N x 1 - - Total memory: - S = 11 x N * 4 Byte - - When using FP16, we can reduce: - R = 11 x N * 4 / 2 Byte - - So do the 'giou' (large than 'iou'). - - Time-wise, FP16 is generally faster than FP32. - - When gpu_assign_thr is not -1, it takes more time on cpu - but not reduce memory. - There, we can reduce half the memory and keep the speed. - - If ``is_aligned`` is ``False``, then calculate the overlaps between each - bbox of bboxes1 and bboxes2, otherwise the overlaps between each aligned - pair of bboxes1 and bboxes2. - - Args: - bboxes1 (Tensor): shape (B, m, 4) in format or empty. - bboxes2 (Tensor): shape (B, n, 4) in format or empty. - B indicates the batch dim, in shape (B1, B2, ..., Bn). - If ``is_aligned`` is ``True``, then m and n must be equal. - mode (str): "iou" (intersection over union), "iof" (intersection over - foreground) or "giou" (generalized intersection over union). - Default "iou". - is_aligned (bool, optional): If True, then m and n must be equal. - Default False. - eps (float, optional): A value added to the denominator for numerical - stability. Default 1e-6. - - Returns: - Tensor: shape (m, n) if ``is_aligned`` is False else shape (m,) - - Example: - >>> bboxes1 = torch.FloatTensor([ - >>> [0, 0, 10, 10], - >>> [10, 10, 20, 20], - >>> [32, 32, 38, 42], - >>> ]) - >>> bboxes2 = torch.FloatTensor([ - >>> [0, 0, 10, 20], - >>> [0, 10, 10, 19], - >>> [10, 10, 20, 20], - >>> ]) - >>> overlaps = bbox_overlaps(bboxes1, bboxes2) - >>> assert overlaps.shape == (3, 3) - >>> overlaps = bbox_overlaps(bboxes1, bboxes2, is_aligned=True) - >>> assert overlaps.shape == (3, ) - - Example: - >>> empty = torch.empty(0, 4) - >>> nonempty = torch.FloatTensor([[0, 0, 10, 9]]) - >>> assert tuple(bbox_overlaps(empty, nonempty).shape) == (0, 1) - >>> assert tuple(bbox_overlaps(nonempty, empty).shape) == (1, 0) - >>> assert tuple(bbox_overlaps(empty, empty).shape) == (0, 0) - """ - - assert mode in ['iou', 'iof', 'giou'], f'Unsupported mode {mode}' - # Either the boxes are empty or the length of boxes' last dimension is 4 - assert (bboxes1.size(-1) == 4 or bboxes1.size(0) == 0) - assert (bboxes2.size(-1) == 4 or bboxes2.size(0) == 0) - - # Batch dim must be the same - # Batch dim: (B1, B2, ... Bn) - assert bboxes1.shape[:-2] == bboxes2.shape[:-2] - batch_shape = bboxes1.shape[:-2] - - rows = bboxes1.size(-2) - cols = bboxes2.size(-2) - if is_aligned: - assert rows == cols - - if rows * cols == 0: - if is_aligned: - return bboxes1.new(batch_shape + (rows, )) - else: - return bboxes1.new(batch_shape + (rows, cols)) - - area1 = (bboxes1[..., 2] - bboxes1[..., 0]) * ( - bboxes1[..., 3] - bboxes1[..., 1]) - area2 = (bboxes2[..., 2] - bboxes2[..., 0]) * ( - bboxes2[..., 3] - bboxes2[..., 1]) - - if is_aligned: - lt = torch.max(bboxes1[..., :2], bboxes2[..., :2]) # [B, rows, 2] - rb = torch.min(bboxes1[..., 2:], bboxes2[..., 2:]) # [B, rows, 2] - - wh = fp16_clamp(rb - lt, min=0) - overlap = wh[..., 0] * wh[..., 1] - - if mode in ['iou', 'giou']: - union = area1 + area2 - overlap - else: - union = area1 - if mode == 'giou': - enclosed_lt = torch.min(bboxes1[..., :2], bboxes2[..., :2]) - enclosed_rb = torch.max(bboxes1[..., 2:], bboxes2[..., 2:]) - else: - lt = torch.max(bboxes1[..., :, None, :2], - bboxes2[..., None, :, :2]) # [B, rows, cols, 2] - rb = torch.min(bboxes1[..., :, None, 2:], - bboxes2[..., None, :, 2:]) # [B, rows, cols, 2] - - wh = fp16_clamp(rb - lt, min=0) - overlap = wh[..., 0] * wh[..., 1] - - if mode in ['iou', 'giou']: - union = area1[..., None] + area2[..., None, :] - overlap - else: - union = area1[..., None] - if mode == 'giou': - enclosed_lt = torch.min(bboxes1[..., :, None, :2], - bboxes2[..., None, :, :2]) - enclosed_rb = torch.max(bboxes1[..., :, None, 2:], - bboxes2[..., None, :, 2:]) - - eps = union.new_tensor([eps]) - union = torch.max(union, eps) - ious = overlap / union - if mode in ['iou', 'iof']: - return ious - # calculate gious - enclose_wh = fp16_clamp(enclosed_rb - enclosed_lt, min=0) - enclose_area = enclose_wh[..., 0] * enclose_wh[..., 1] - enclose_area = torch.max(enclose_area, eps) - gious = ious - (enclose_area - union) / enclose_area - return gious diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/match_costs/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/match_costs/__init__.py deleted file mode 100755 index 4b0f72cf3..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/match_costs/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_match_cost -from .match_cost import (BBoxL1Cost, ClassificationCost, CrossEntropyLossCost, - DiceCost, FocalLossCost, IoUCost) - -__all__ = [ - 'build_match_cost', 'ClassificationCost', 'BBoxL1Cost', 'IoUCost', - 'FocalLossCost', 'DiceCost', 'CrossEntropyLossCost' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/match_costs/builder.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/match_costs/builder.py deleted file mode 100755 index 26bdde223..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/match_costs/builder.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, build_from_cfg - -MATCH_COST = Registry('Match Cost') - - -def build_match_cost(cfg, default_args=None): - """Builder of IoU calculator.""" - return build_from_cfg(cfg, MATCH_COST, default_args) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/match_costs/match_cost.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/match_costs/match_cost.py deleted file mode 100755 index 6f2e71091..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/match_costs/match_cost.py +++ /dev/null @@ -1,359 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn.functional as F - -from mmdet.core.bbox.iou_calculators import bbox_overlaps -from mmdet.core.bbox.transforms import bbox_cxcywh_to_xyxy, bbox_xyxy_to_cxcywh -from .builder import MATCH_COST - - -@MATCH_COST.register_module() -class BBoxL1Cost: - """BBoxL1Cost. - - Args: - weight (int | float, optional): loss_weight - box_format (str, optional): 'xyxy' for DETR, 'xywh' for Sparse_RCNN - - Examples: - >>> from mmdet.core.bbox.match_costs.match_cost import BBoxL1Cost - >>> import torch - >>> self = BBoxL1Cost() - >>> bbox_pred = torch.rand(1, 4) - >>> gt_bboxes= torch.FloatTensor([[0, 0, 2, 4], [1, 2, 3, 4]]) - >>> factor = torch.tensor([10, 8, 10, 8]) - >>> self(bbox_pred, gt_bboxes, factor) - tensor([[1.6172, 1.6422]]) - """ - - def __init__(self, weight=1., box_format='xyxy'): - self.weight = weight - assert box_format in ['xyxy', 'xywh'] - self.box_format = box_format - - def __call__(self, bbox_pred, gt_bboxes): - """ - Args: - bbox_pred (Tensor): Predicted boxes with normalized coordinates - (cx, cy, w, h), which are all in range [0, 1]. Shape - (num_query, 4). - gt_bboxes (Tensor): Ground truth boxes with normalized - coordinates (x1, y1, x2, y2). Shape (num_gt, 4). - - Returns: - torch.Tensor: bbox_cost value with weight - """ - if self.box_format == 'xywh': - gt_bboxes = bbox_xyxy_to_cxcywh(gt_bboxes) - elif self.box_format == 'xyxy': - bbox_pred = bbox_cxcywh_to_xyxy(bbox_pred) - bbox_cost = torch.cdist(bbox_pred, gt_bboxes, p=1) - return bbox_cost * self.weight - - -@MATCH_COST.register_module() -class FocalLossCost: - """FocalLossCost. - - Args: - weight (int | float, optional): loss_weight - alpha (int | float, optional): focal_loss alpha - gamma (int | float, optional): focal_loss gamma - eps (float, optional): default 1e-12 - binary_input (bool, optional): Whether the input is binary, - default False. - - Examples: - >>> from mmdet.core.bbox.match_costs.match_cost import FocalLossCost - >>> import torch - >>> self = FocalLossCost() - >>> cls_pred = torch.rand(4, 3) - >>> gt_labels = torch.tensor([0, 1, 2]) - >>> factor = torch.tensor([10, 8, 10, 8]) - >>> self(cls_pred, gt_labels) - tensor([[-0.3236, -0.3364, -0.2699], - [-0.3439, -0.3209, -0.4807], - [-0.4099, -0.3795, -0.2929], - [-0.1950, -0.1207, -0.2626]]) - """ - - def __init__(self, - weight=1., - alpha=0.25, - gamma=2, - eps=1e-12, - binary_input=False): - self.weight = weight - self.alpha = alpha - self.gamma = gamma - self.eps = eps - self.binary_input = binary_input - - def _focal_loss_cost(self, cls_pred, gt_labels): - """ - Args: - cls_pred (Tensor): Predicted classification logits, shape - (num_query, num_class). - gt_labels (Tensor): Label of `gt_bboxes`, shape (num_gt,). - - Returns: - torch.Tensor: cls_cost value with weight - """ - cls_pred = cls_pred.sigmoid() - neg_cost = -(1 - cls_pred + self.eps).log() * ( - 1 - self.alpha) * cls_pred.pow(self.gamma) - pos_cost = -(cls_pred + self.eps).log() * self.alpha * ( - 1 - cls_pred).pow(self.gamma) - - cls_cost = pos_cost[:, gt_labels] - neg_cost[:, gt_labels] - return cls_cost * self.weight - - def _mask_focal_loss_cost(self, cls_pred, gt_labels): - """ - Args: - cls_pred (Tensor): Predicted classfication logits - in shape (num_query, d1, ..., dn), dtype=torch.float32. - gt_labels (Tensor): Ground truth in shape (num_gt, d1, ..., dn), - dtype=torch.long. Labels should be binary. - - Returns: - Tensor: Focal cost matrix with weight in shape\ - (num_query, num_gt). - """ - cls_pred = cls_pred.flatten(1) - gt_labels = gt_labels.flatten(1).float() - n = cls_pred.shape[1] - cls_pred = cls_pred.sigmoid() - neg_cost = -(1 - cls_pred + self.eps).log() * ( - 1 - self.alpha) * cls_pred.pow(self.gamma) - pos_cost = -(cls_pred + self.eps).log() * self.alpha * ( - 1 - cls_pred).pow(self.gamma) - - cls_cost = torch.einsum('nc,mc->nm', pos_cost, gt_labels) + \ - torch.einsum('nc,mc->nm', neg_cost, (1 - gt_labels)) - return cls_cost / n * self.weight - - def __call__(self, cls_pred, gt_labels): - """ - Args: - cls_pred (Tensor): Predicted classfication logits. - gt_labels (Tensor)): Labels. - - Returns: - Tensor: Focal cost matrix with weight in shape\ - (num_query, num_gt). - """ - if self.binary_input: - return self._mask_focal_loss_cost(cls_pred, gt_labels) - else: - return self._focal_loss_cost(cls_pred, gt_labels) - - -@MATCH_COST.register_module() -class ClassificationCost: - """ClsSoftmaxCost. - - Args: - weight (int | float, optional): loss_weight - - Examples: - >>> from mmdet.core.bbox.match_costs.match_cost import \ - ... ClassificationCost - >>> import torch - >>> self = ClassificationCost() - >>> cls_pred = torch.rand(4, 3) - >>> gt_labels = torch.tensor([0, 1, 2]) - >>> factor = torch.tensor([10, 8, 10, 8]) - >>> self(cls_pred, gt_labels) - tensor([[-0.3430, -0.3525, -0.3045], - [-0.3077, -0.2931, -0.3992], - [-0.3664, -0.3455, -0.2881], - [-0.3343, -0.2701, -0.3956]]) - """ - - def __init__(self, weight=1.): - self.weight = weight - - def __call__(self, cls_pred, gt_labels): - """ - Args: - cls_pred (Tensor): Predicted classification logits, shape - (num_query, num_class). - gt_labels (Tensor): Label of `gt_bboxes`, shape (num_gt,). - - Returns: - torch.Tensor: cls_cost value with weight - """ - # Following the official DETR repo, contrary to the loss that - # NLL is used, we approximate it in 1 - cls_score[gt_label]. - # The 1 is a constant that doesn't change the matching, - # so it can be omitted. - cls_score = cls_pred.softmax(-1) - cls_cost = -cls_score[:, gt_labels] - return cls_cost * self.weight - - -@MATCH_COST.register_module() -class IoUCost: - """IoUCost. - - Args: - iou_mode (str, optional): iou mode such as 'iou' | 'giou' - weight (int | float, optional): loss weight - - Examples: - >>> from mmdet.core.bbox.match_costs.match_cost import IoUCost - >>> import torch - >>> self = IoUCost() - >>> bboxes = torch.FloatTensor([[1,1, 2, 2], [2, 2, 3, 4]]) - >>> gt_bboxes = torch.FloatTensor([[0, 0, 2, 4], [1, 2, 3, 4]]) - >>> self(bboxes, gt_bboxes) - tensor([[-0.1250, 0.1667], - [ 0.1667, -0.5000]]) - """ - - def __init__(self, iou_mode='giou', weight=1.): - self.weight = weight - self.iou_mode = iou_mode - - def __call__(self, bboxes, gt_bboxes): - """ - Args: - bboxes (Tensor): Predicted boxes with unnormalized coordinates - (x1, y1, x2, y2). Shape (num_query, 4). - gt_bboxes (Tensor): Ground truth boxes with unnormalized - coordinates (x1, y1, x2, y2). Shape (num_gt, 4). - - Returns: - torch.Tensor: iou_cost value with weight - """ - # overlaps: [num_bboxes, num_gt] - overlaps = bbox_overlaps( - bboxes, gt_bboxes, mode=self.iou_mode, is_aligned=False) - # The 1 is a constant that doesn't change the matching, so omitted. - iou_cost = -overlaps - return iou_cost * self.weight - - -@MATCH_COST.register_module() -class DiceCost: - """Cost of mask assignments based on dice losses. - - Args: - weight (int | float, optional): loss_weight. Defaults to 1. - pred_act (bool, optional): Whether to apply sigmoid to mask_pred. - Defaults to False. - eps (float, optional): default 1e-12. - naive_dice (bool, optional): If True, use the naive dice loss - in which the power of the number in the denominator is - the first power. If Flase, use the second power that - is adopted by K-Net and SOLO. - Defaults to True. - """ - - def __init__(self, weight=1., pred_act=False, eps=1e-3, naive_dice=True): - self.weight = weight - self.pred_act = pred_act - self.eps = eps - self.naive_dice = naive_dice - - def binary_mask_dice_loss(self, mask_preds, gt_masks): - """ - Args: - mask_preds (Tensor): Mask prediction in shape (num_query, *). - gt_masks (Tensor): Ground truth in shape (num_gt, *) - store 0 or 1, 0 for negative class and 1 for - positive class. - - Returns: - Tensor: Dice cost matrix in shape (num_query, num_gt). - """ - mask_preds = mask_preds.flatten(1) - gt_masks = gt_masks.flatten(1).float() - numerator = 2 * torch.einsum('nc,mc->nm', mask_preds, gt_masks) - if self.naive_dice: - denominator = mask_preds.sum(-1)[:, None] + \ - gt_masks.sum(-1)[None, :] - else: - denominator = mask_preds.pow(2).sum(1)[:, None] + \ - gt_masks.pow(2).sum(1)[None, :] - loss = 1 - (numerator + self.eps) / (denominator + self.eps) - return loss - - def __call__(self, mask_preds, gt_masks): - """ - Args: - mask_preds (Tensor): Mask prediction logits in shape (num_query, *) - gt_masks (Tensor): Ground truth in shape (num_gt, *) - - Returns: - Tensor: Dice cost matrix with weight in shape (num_query, num_gt). - """ - if self.pred_act: - mask_preds = mask_preds.sigmoid() - dice_cost = self.binary_mask_dice_loss(mask_preds, gt_masks) - return dice_cost * self.weight - - -@MATCH_COST.register_module() -class CrossEntropyLossCost: - """CrossEntropyLossCost. - - Args: - weight (int | float, optional): loss weight. Defaults to 1. - use_sigmoid (bool, optional): Whether the prediction uses sigmoid - of softmax. Defaults to True. - Examples: - >>> from mmdet.core.bbox.match_costs import CrossEntropyLossCost - >>> import torch - >>> bce = CrossEntropyLossCost(use_sigmoid=True) - >>> cls_pred = torch.tensor([[7.6, 1.2], [-1.3, 10]]) - >>> gt_labels = torch.tensor([[1, 1], [1, 0]]) - >>> print(bce(cls_pred, gt_labels)) - """ - - def __init__(self, weight=1., use_sigmoid=True): - assert use_sigmoid, 'use_sigmoid = False is not supported yet.' - self.weight = weight - self.use_sigmoid = use_sigmoid - - def _binary_cross_entropy(self, cls_pred, gt_labels): - """ - Args: - cls_pred (Tensor): The prediction with shape (num_query, 1, *) or - (num_query, *). - gt_labels (Tensor): The learning label of prediction with - shape (num_gt, *). - - Returns: - Tensor: Cross entropy cost matrix in shape (num_query, num_gt). - """ - cls_pred = cls_pred.flatten(1).float() - gt_labels = gt_labels.flatten(1).float() - n = cls_pred.shape[1] - pos = F.binary_cross_entropy_with_logits( - cls_pred, torch.ones_like(cls_pred), reduction='none') - neg = F.binary_cross_entropy_with_logits( - cls_pred, torch.zeros_like(cls_pred), reduction='none') - cls_cost = torch.einsum('nc,mc->nm', pos, gt_labels) + \ - torch.einsum('nc,mc->nm', neg, 1 - gt_labels) - cls_cost = cls_cost / n - - return cls_cost - - def __call__(self, cls_pred, gt_labels): - """ - Args: - cls_pred (Tensor): Predicted classification logits. - gt_labels (Tensor): Labels. - - Returns: - Tensor: Cross entropy cost matrix with weight in - shape (num_query, num_gt). - """ - if self.use_sigmoid: - cls_cost = self._binary_cross_entropy(cls_pred, gt_labels) - else: - raise NotImplementedError - - return cls_cost * self.weight diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/__init__.py deleted file mode 100755 index 3942bc30a..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_sampler import BaseSampler -from .combined_sampler import CombinedSampler -from .instance_balanced_pos_sampler import InstanceBalancedPosSampler -from .iou_balanced_neg_sampler import IoUBalancedNegSampler -from .mask_pseudo_sampler import MaskPseudoSampler -from .mask_sampling_result import MaskSamplingResult -from .ohem_sampler import OHEMSampler -from .pseudo_sampler import PseudoSampler -from .random_sampler import RandomSampler -from .sampling_result import SamplingResult -from .score_hlr_sampler import ScoreHLRSampler - -__all__ = [ - 'BaseSampler', 'PseudoSampler', 'RandomSampler', - 'InstanceBalancedPosSampler', 'IoUBalancedNegSampler', 'CombinedSampler', - 'OHEMSampler', 'SamplingResult', 'ScoreHLRSampler', 'MaskPseudoSampler', - 'MaskSamplingResult' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/base_sampler.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/base_sampler.py deleted file mode 100755 index 55436736d..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/base_sampler.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -import torch - -from .sampling_result import SamplingResult - - -class BaseSampler(metaclass=ABCMeta): - """Base class of samplers.""" - - def __init__(self, - num, - pos_fraction, - neg_pos_ub=-1, - add_gt_as_proposals=True, - **kwargs): - self.num = num - self.pos_fraction = pos_fraction - self.neg_pos_ub = neg_pos_ub - self.add_gt_as_proposals = add_gt_as_proposals - self.pos_sampler = self - self.neg_sampler = self - - @abstractmethod - def _sample_pos(self, assign_result, num_expected, **kwargs): - """Sample positive samples.""" - pass - - @abstractmethod - def _sample_neg(self, assign_result, num_expected, **kwargs): - """Sample negative samples.""" - pass - - def sample(self, - assign_result, - bboxes, - gt_bboxes, - gt_labels=None, - **kwargs): - """Sample positive and negative bboxes. - - This is a simple implementation of bbox sampling given candidates, - assigning results and ground truth bboxes. - - Args: - assign_result (:obj:`AssignResult`): Bbox assigning results. - bboxes (Tensor): Boxes to be sampled from. - gt_bboxes (Tensor): Ground truth bboxes. - gt_labels (Tensor, optional): Class labels of ground truth bboxes. - - Returns: - :obj:`SamplingResult`: Sampling result. - - Example: - >>> from mmdet.core.bbox import RandomSampler - >>> from mmdet.core.bbox import AssignResult - >>> from mmdet.core.bbox.demodata import ensure_rng, random_boxes - >>> rng = ensure_rng(None) - >>> assign_result = AssignResult.random(rng=rng) - >>> bboxes = random_boxes(assign_result.num_preds, rng=rng) - >>> gt_bboxes = random_boxes(assign_result.num_gts, rng=rng) - >>> gt_labels = None - >>> self = RandomSampler(num=32, pos_fraction=0.5, neg_pos_ub=-1, - >>> add_gt_as_proposals=False) - >>> self = self.sample(assign_result, bboxes, gt_bboxes, gt_labels) - """ - if len(bboxes.shape) < 2: - bboxes = bboxes[None, :] - - bboxes = bboxes[:, :4] - - gt_flags = bboxes.new_zeros((bboxes.shape[0], ), dtype=torch.uint8) - if self.add_gt_as_proposals and len(gt_bboxes) > 0: - if gt_labels is None: - raise ValueError( - 'gt_labels must be given when add_gt_as_proposals is True') - bboxes = torch.cat([gt_bboxes, bboxes], dim=0) - assign_result.add_gt_(gt_labels) - gt_ones = bboxes.new_ones(gt_bboxes.shape[0], dtype=torch.uint8) - gt_flags = torch.cat([gt_ones, gt_flags]) - - num_expected_pos = int(self.num * self.pos_fraction) - pos_inds = self.pos_sampler._sample_pos( - assign_result, num_expected_pos, bboxes=bboxes, **kwargs) - # We found that sampled indices have duplicated items occasionally. - # (may be a bug of PyTorch) - pos_inds = pos_inds.unique() - num_sampled_pos = pos_inds.numel() - num_expected_neg = self.num - num_sampled_pos - if self.neg_pos_ub >= 0: - _pos = max(1, num_sampled_pos) - neg_upper_bound = int(self.neg_pos_ub * _pos) - if num_expected_neg > neg_upper_bound: - num_expected_neg = neg_upper_bound - neg_inds = self.neg_sampler._sample_neg( - assign_result, num_expected_neg, bboxes=bboxes, **kwargs) - neg_inds = neg_inds.unique() - - sampling_result = SamplingResult(pos_inds, neg_inds, bboxes, gt_bboxes, - assign_result, gt_flags) - return sampling_result diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/combined_sampler.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/combined_sampler.py deleted file mode 100755 index 2937c497f..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/combined_sampler.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..builder import BBOX_SAMPLERS, build_sampler -from .base_sampler import BaseSampler - - -@BBOX_SAMPLERS.register_module() -class CombinedSampler(BaseSampler): - """A sampler that combines positive sampler and negative sampler.""" - - def __init__(self, pos_sampler, neg_sampler, **kwargs): - super(CombinedSampler, self).__init__(**kwargs) - self.pos_sampler = build_sampler(pos_sampler, **kwargs) - self.neg_sampler = build_sampler(neg_sampler, **kwargs) - - def _sample_pos(self, **kwargs): - """Sample positive samples.""" - raise NotImplementedError - - def _sample_neg(self, **kwargs): - """Sample negative samples.""" - raise NotImplementedError diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/instance_balanced_pos_sampler.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/instance_balanced_pos_sampler.py deleted file mode 100755 index 5dfb37850..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/instance_balanced_pos_sampler.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch - -from ..builder import BBOX_SAMPLERS -from .random_sampler import RandomSampler - - -@BBOX_SAMPLERS.register_module() -class InstanceBalancedPosSampler(RandomSampler): - """Instance balanced sampler that samples equal number of positive samples - for each instance.""" - - def _sample_pos(self, assign_result, num_expected, **kwargs): - """Sample positive boxes. - - Args: - assign_result (:obj:`AssignResult`): The assigned results of boxes. - num_expected (int): The number of expected positive samples - - Returns: - Tensor or ndarray: sampled indices. - """ - pos_inds = torch.nonzero(assign_result.gt_inds > 0, as_tuple=False) - if pos_inds.numel() != 0: - pos_inds = pos_inds.squeeze(1) - if pos_inds.numel() <= num_expected: - return pos_inds - else: - unique_gt_inds = assign_result.gt_inds[pos_inds].unique() - num_gts = len(unique_gt_inds) - num_per_gt = int(round(num_expected / float(num_gts)) + 1) - sampled_inds = [] - for i in unique_gt_inds: - inds = torch.nonzero( - assign_result.gt_inds == i.item(), as_tuple=False) - if inds.numel() != 0: - inds = inds.squeeze(1) - else: - continue - if len(inds) > num_per_gt: - inds = self.random_choice(inds, num_per_gt) - sampled_inds.append(inds) - sampled_inds = torch.cat(sampled_inds) - if len(sampled_inds) < num_expected: - num_extra = num_expected - len(sampled_inds) - extra_inds = np.array( - list(set(pos_inds.cpu()) - set(sampled_inds.cpu()))) - if len(extra_inds) > num_extra: - extra_inds = self.random_choice(extra_inds, num_extra) - extra_inds = torch.from_numpy(extra_inds).to( - assign_result.gt_inds.device).long() - sampled_inds = torch.cat([sampled_inds, extra_inds]) - elif len(sampled_inds) > num_expected: - sampled_inds = self.random_choice(sampled_inds, num_expected) - return sampled_inds diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/iou_balanced_neg_sampler.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/iou_balanced_neg_sampler.py deleted file mode 100755 index eba972f55..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/iou_balanced_neg_sampler.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch - -from ..builder import BBOX_SAMPLERS -from .random_sampler import RandomSampler - - -@BBOX_SAMPLERS.register_module() -class IoUBalancedNegSampler(RandomSampler): - """IoU Balanced Sampling. - - arXiv: https://arxiv.org/pdf/1904.02701.pdf (CVPR 2019) - - Sampling proposals according to their IoU. `floor_fraction` of needed RoIs - are sampled from proposals whose IoU are lower than `floor_thr` randomly. - The others are sampled from proposals whose IoU are higher than - `floor_thr`. These proposals are sampled from some bins evenly, which are - split by `num_bins` via IoU evenly. - - Args: - num (int): number of proposals. - pos_fraction (float): fraction of positive proposals. - floor_thr (float): threshold (minimum) IoU for IoU balanced sampling, - set to -1 if all using IoU balanced sampling. - floor_fraction (float): sampling fraction of proposals under floor_thr. - num_bins (int): number of bins in IoU balanced sampling. - """ - - def __init__(self, - num, - pos_fraction, - floor_thr=-1, - floor_fraction=0, - num_bins=3, - **kwargs): - super(IoUBalancedNegSampler, self).__init__(num, pos_fraction, - **kwargs) - assert floor_thr >= 0 or floor_thr == -1 - assert 0 <= floor_fraction <= 1 - assert num_bins >= 1 - - self.floor_thr = floor_thr - self.floor_fraction = floor_fraction - self.num_bins = num_bins - - def sample_via_interval(self, max_overlaps, full_set, num_expected): - """Sample according to the iou interval. - - Args: - max_overlaps (torch.Tensor): IoU between bounding boxes and ground - truth boxes. - full_set (set(int)): A full set of indices of boxes。 - num_expected (int): Number of expected samples。 - - Returns: - np.ndarray: Indices of samples - """ - max_iou = max_overlaps.max() - iou_interval = (max_iou - self.floor_thr) / self.num_bins - per_num_expected = int(num_expected / self.num_bins) - - sampled_inds = [] - for i in range(self.num_bins): - start_iou = self.floor_thr + i * iou_interval - end_iou = self.floor_thr + (i + 1) * iou_interval - tmp_set = set( - np.where( - np.logical_and(max_overlaps >= start_iou, - max_overlaps < end_iou))[0]) - tmp_inds = list(tmp_set & full_set) - if len(tmp_inds) > per_num_expected: - tmp_sampled_set = self.random_choice(tmp_inds, - per_num_expected) - else: - tmp_sampled_set = np.array(tmp_inds, dtype=np.int) - sampled_inds.append(tmp_sampled_set) - - sampled_inds = np.concatenate(sampled_inds) - if len(sampled_inds) < num_expected: - num_extra = num_expected - len(sampled_inds) - extra_inds = np.array(list(full_set - set(sampled_inds))) - if len(extra_inds) > num_extra: - extra_inds = self.random_choice(extra_inds, num_extra) - sampled_inds = np.concatenate([sampled_inds, extra_inds]) - - return sampled_inds - - def _sample_neg(self, assign_result, num_expected, **kwargs): - """Sample negative boxes. - - Args: - assign_result (:obj:`AssignResult`): The assigned results of boxes. - num_expected (int): The number of expected negative samples - - Returns: - Tensor or ndarray: sampled indices. - """ - neg_inds = torch.nonzero(assign_result.gt_inds == 0, as_tuple=False) - if neg_inds.numel() != 0: - neg_inds = neg_inds.squeeze(1) - if len(neg_inds) <= num_expected: - return neg_inds - else: - max_overlaps = assign_result.max_overlaps.cpu().numpy() - # balance sampling for negative samples - neg_set = set(neg_inds.cpu().numpy()) - - if self.floor_thr > 0: - floor_set = set( - np.where( - np.logical_and(max_overlaps >= 0, - max_overlaps < self.floor_thr))[0]) - iou_sampling_set = set( - np.where(max_overlaps >= self.floor_thr)[0]) - elif self.floor_thr == 0: - floor_set = set(np.where(max_overlaps == 0)[0]) - iou_sampling_set = set( - np.where(max_overlaps > self.floor_thr)[0]) - else: - floor_set = set() - iou_sampling_set = set( - np.where(max_overlaps > self.floor_thr)[0]) - # for sampling interval calculation - self.floor_thr = 0 - - floor_neg_inds = list(floor_set & neg_set) - iou_sampling_neg_inds = list(iou_sampling_set & neg_set) - num_expected_iou_sampling = int(num_expected * - (1 - self.floor_fraction)) - if len(iou_sampling_neg_inds) > num_expected_iou_sampling: - if self.num_bins >= 2: - iou_sampled_inds = self.sample_via_interval( - max_overlaps, set(iou_sampling_neg_inds), - num_expected_iou_sampling) - else: - iou_sampled_inds = self.random_choice( - iou_sampling_neg_inds, num_expected_iou_sampling) - else: - iou_sampled_inds = np.array( - iou_sampling_neg_inds, dtype=np.int) - num_expected_floor = num_expected - len(iou_sampled_inds) - if len(floor_neg_inds) > num_expected_floor: - sampled_floor_inds = self.random_choice( - floor_neg_inds, num_expected_floor) - else: - sampled_floor_inds = np.array(floor_neg_inds, dtype=np.int) - sampled_inds = np.concatenate( - (sampled_floor_inds, iou_sampled_inds)) - if len(sampled_inds) < num_expected: - num_extra = num_expected - len(sampled_inds) - extra_inds = np.array(list(neg_set - set(sampled_inds))) - if len(extra_inds) > num_extra: - extra_inds = self.random_choice(extra_inds, num_extra) - sampled_inds = np.concatenate((sampled_inds, extra_inds)) - sampled_inds = torch.from_numpy(sampled_inds).long().to( - assign_result.gt_inds.device) - return sampled_inds diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/mask_pseudo_sampler.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/mask_pseudo_sampler.py deleted file mode 100755 index 6e91959d6..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/mask_pseudo_sampler.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""copy from -https://github.com/ZwwWayne/K-Net/blob/main/knet/det/mask_pseudo_sampler.py.""" - -import torch - -from mmdet.core.bbox.builder import BBOX_SAMPLERS -from .base_sampler import BaseSampler -from .mask_sampling_result import MaskSamplingResult - - -@BBOX_SAMPLERS.register_module() -class MaskPseudoSampler(BaseSampler): - """A pseudo sampler that does not do sampling actually.""" - - def __init__(self, **kwargs): - pass - - def _sample_pos(self, **kwargs): - """Sample positive samples.""" - raise NotImplementedError - - def _sample_neg(self, **kwargs): - """Sample negative samples.""" - raise NotImplementedError - - def sample(self, assign_result, masks, gt_masks, **kwargs): - """Directly returns the positive and negative indices of samples. - - Args: - assign_result (:obj:`AssignResult`): Assigned results - masks (torch.Tensor): Bounding boxes - gt_masks (torch.Tensor): Ground truth boxes - Returns: - :obj:`SamplingResult`: sampler results - """ - pos_inds = torch.nonzero( - assign_result.gt_inds > 0, as_tuple=False).squeeze(-1).unique() - neg_inds = torch.nonzero( - assign_result.gt_inds == 0, as_tuple=False).squeeze(-1).unique() - gt_flags = masks.new_zeros(masks.shape[0], dtype=torch.uint8) - sampling_result = MaskSamplingResult(pos_inds, neg_inds, masks, - gt_masks, assign_result, gt_flags) - return sampling_result diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/mask_sampling_result.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/mask_sampling_result.py deleted file mode 100755 index 63602400a..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/mask_sampling_result.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""copy from -https://github.com/ZwwWayne/K-Net/blob/main/knet/det/mask_pseudo_sampler.py.""" - -import torch - -from .sampling_result import SamplingResult - - -class MaskSamplingResult(SamplingResult): - """Mask sampling result.""" - - def __init__(self, pos_inds, neg_inds, masks, gt_masks, assign_result, - gt_flags): - self.pos_inds = pos_inds - self.neg_inds = neg_inds - self.pos_masks = masks[pos_inds] - self.neg_masks = masks[neg_inds] - self.pos_is_gt = gt_flags[pos_inds] - - self.num_gts = gt_masks.shape[0] - self.pos_assigned_gt_inds = assign_result.gt_inds[pos_inds] - 1 - - if gt_masks.numel() == 0: - # hack for index error case - assert self.pos_assigned_gt_inds.numel() == 0 - self.pos_gt_masks = torch.empty_like(gt_masks) - else: - self.pos_gt_masks = gt_masks[self.pos_assigned_gt_inds, :] - - if assign_result.labels is not None: - self.pos_gt_labels = assign_result.labels[pos_inds] - else: - self.pos_gt_labels = None - - @property - def masks(self): - """torch.Tensor: concatenated positive and negative boxes""" - return torch.cat([self.pos_masks, self.neg_masks]) - - def __nice__(self): - data = self.info.copy() - data['pos_masks'] = data.pop('pos_masks').shape - data['neg_masks'] = data.pop('neg_masks').shape - parts = [f"'{k}': {v!r}" for k, v in sorted(data.items())] - body = ' ' + ',\n '.join(parts) - return '{\n' + body + '\n}' - - @property - def info(self): - """Returns a dictionary of info about the object.""" - return { - 'pos_inds': self.pos_inds, - 'neg_inds': self.neg_inds, - 'pos_masks': self.pos_masks, - 'neg_masks': self.neg_masks, - 'pos_is_gt': self.pos_is_gt, - 'num_gts': self.num_gts, - 'pos_assigned_gt_inds': self.pos_assigned_gt_inds, - } diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/ohem_sampler.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/ohem_sampler.py deleted file mode 100755 index 3ca13282b..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/ohem_sampler.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_SAMPLERS -from ..transforms import bbox2roi -from .base_sampler import BaseSampler - - -@BBOX_SAMPLERS.register_module() -class OHEMSampler(BaseSampler): - r"""Online Hard Example Mining Sampler described in `Training Region-based - Object Detectors with Online Hard Example Mining - `_. - """ - - def __init__(self, - num, - pos_fraction, - context, - neg_pos_ub=-1, - add_gt_as_proposals=True, - loss_key='loss_cls', - **kwargs): - super(OHEMSampler, self).__init__(num, pos_fraction, neg_pos_ub, - add_gt_as_proposals) - self.context = context - if not hasattr(self.context, 'num_stages'): - self.bbox_head = self.context.bbox_head - else: - self.bbox_head = self.context.bbox_head[self.context.current_stage] - - self.loss_key = loss_key - - def hard_mining(self, inds, num_expected, bboxes, labels, feats): - with torch.no_grad(): - rois = bbox2roi([bboxes]) - if not hasattr(self.context, 'num_stages'): - bbox_results = self.context._bbox_forward(feats, rois) - else: - bbox_results = self.context._bbox_forward( - self.context.current_stage, feats, rois) - cls_score = bbox_results['cls_score'] - loss = self.bbox_head.loss( - cls_score=cls_score, - bbox_pred=None, - rois=rois, - labels=labels, - label_weights=cls_score.new_ones(cls_score.size(0)), - bbox_targets=None, - bbox_weights=None, - reduction_override='none')[self.loss_key] - _, topk_loss_inds = loss.topk(num_expected) - return inds[topk_loss_inds] - - def _sample_pos(self, - assign_result, - num_expected, - bboxes=None, - feats=None, - **kwargs): - """Sample positive boxes. - - Args: - assign_result (:obj:`AssignResult`): Assigned results - num_expected (int): Number of expected positive samples - bboxes (torch.Tensor, optional): Boxes. Defaults to None. - feats (list[torch.Tensor], optional): Multi-level features. - Defaults to None. - - Returns: - torch.Tensor: Indices of positive samples - """ - # Sample some hard positive samples - pos_inds = torch.nonzero(assign_result.gt_inds > 0, as_tuple=False) - if pos_inds.numel() != 0: - pos_inds = pos_inds.squeeze(1) - if pos_inds.numel() <= num_expected: - return pos_inds - else: - return self.hard_mining(pos_inds, num_expected, bboxes[pos_inds], - assign_result.labels[pos_inds], feats) - - def _sample_neg(self, - assign_result, - num_expected, - bboxes=None, - feats=None, - **kwargs): - """Sample negative boxes. - - Args: - assign_result (:obj:`AssignResult`): Assigned results - num_expected (int): Number of expected negative samples - bboxes (torch.Tensor, optional): Boxes. Defaults to None. - feats (list[torch.Tensor], optional): Multi-level features. - Defaults to None. - - Returns: - torch.Tensor: Indices of negative samples - """ - # Sample some hard negative samples - neg_inds = torch.nonzero(assign_result.gt_inds == 0, as_tuple=False) - if neg_inds.numel() != 0: - neg_inds = neg_inds.squeeze(1) - if len(neg_inds) <= num_expected: - return neg_inds - else: - neg_labels = assign_result.labels.new_empty( - neg_inds.size(0)).fill_(self.bbox_head.num_classes) - return self.hard_mining(neg_inds, num_expected, bboxes[neg_inds], - neg_labels, feats) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/pseudo_sampler.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/pseudo_sampler.py deleted file mode 100755 index cc69dc1c3..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/pseudo_sampler.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_SAMPLERS -from .base_sampler import BaseSampler -from .sampling_result import SamplingResult - - -@BBOX_SAMPLERS.register_module() -class PseudoSampler(BaseSampler): - """A pseudo sampler that does not do sampling actually.""" - - def __init__(self, **kwargs): - pass - - def _sample_pos(self, **kwargs): - """Sample positive samples.""" - raise NotImplementedError - - def _sample_neg(self, **kwargs): - """Sample negative samples.""" - raise NotImplementedError - - def sample(self, assign_result, bboxes, gt_bboxes, *args, **kwargs): - """Directly returns the positive and negative indices of samples. - - Args: - assign_result (:obj:`AssignResult`): Assigned results - bboxes (torch.Tensor): Bounding boxes - gt_bboxes (torch.Tensor): Ground truth boxes - - Returns: - :obj:`SamplingResult`: sampler results - """ - pos_inds = torch.nonzero( - assign_result.gt_inds > 0, as_tuple=False).squeeze(-1).unique() - neg_inds = torch.nonzero( - assign_result.gt_inds == 0, as_tuple=False).squeeze(-1).unique() - gt_flags = bboxes.new_zeros(bboxes.shape[0], dtype=torch.uint8) - sampling_result = SamplingResult(pos_inds, neg_inds, bboxes, gt_bboxes, - assign_result, gt_flags) - return sampling_result diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/random_sampler.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/random_sampler.py deleted file mode 100755 index a1f806983..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/random_sampler.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from ..builder import BBOX_SAMPLERS -from .base_sampler import BaseSampler - - -@BBOX_SAMPLERS.register_module() -class RandomSampler(BaseSampler): - """Random sampler. - - Args: - num (int): Number of samples - pos_fraction (float): Fraction of positive samples - neg_pos_up (int, optional): Upper bound number of negative and - positive samples. Defaults to -1. - add_gt_as_proposals (bool, optional): Whether to add ground truth - boxes as proposals. Defaults to True. - """ - - def __init__(self, - num, - pos_fraction, - neg_pos_ub=-1, - add_gt_as_proposals=True, - **kwargs): - from mmdet.core.bbox import demodata - super(RandomSampler, self).__init__(num, pos_fraction, neg_pos_ub, - add_gt_as_proposals) - self.rng = demodata.ensure_rng(kwargs.get('rng', None)) - - def random_choice(self, gallery, num): - """Random select some elements from the gallery. - - If `gallery` is a Tensor, the returned indices will be a Tensor; - If `gallery` is a ndarray or list, the returned indices will be a - ndarray. - - Args: - gallery (Tensor | ndarray | list): indices pool. - num (int): expected sample num. - - Returns: - Tensor or ndarray: sampled indices. - """ - assert len(gallery) >= num - - is_tensor = isinstance(gallery, torch.Tensor) - if not is_tensor: - if torch.cuda.is_available(): - device = torch.cuda.current_device() - else: - device = 'cpu' - gallery = torch.tensor(gallery, dtype=torch.long, device=device) - # This is a temporary fix. We can revert the following code - # when PyTorch fixes the abnormal return of torch.randperm. - # See: https://github.com/open-mmlab/mmdetection/pull/5014 - perm = torch.randperm(gallery.numel())[:num].to(device=gallery.device) - rand_inds = gallery[perm] - if not is_tensor: - rand_inds = rand_inds.cpu().numpy() - return rand_inds - - def _sample_pos(self, assign_result, num_expected, **kwargs): - """Randomly sample some positive samples.""" - pos_inds = torch.nonzero(assign_result.gt_inds > 0, as_tuple=False) - if pos_inds.numel() != 0: - pos_inds = pos_inds.squeeze(1) - if pos_inds.numel() <= num_expected: - return pos_inds - else: - return self.random_choice(pos_inds, num_expected) - - def _sample_neg(self, assign_result, num_expected, **kwargs): - """Randomly sample some negative samples.""" - neg_inds = torch.nonzero(assign_result.gt_inds == 0, as_tuple=False) - if neg_inds.numel() != 0: - neg_inds = neg_inds.squeeze(1) - if len(neg_inds) <= num_expected: - return neg_inds - else: - return self.random_choice(neg_inds, num_expected) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/sampling_result.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/sampling_result.py deleted file mode 100755 index 3c0190428..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/sampling_result.py +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmdet.utils import util_mixins - - -class SamplingResult(util_mixins.NiceRepr): - """Bbox sampling result. - - Example: - >>> # xdoctest: +IGNORE_WANT - >>> from mmdet.core.bbox.samplers.sampling_result import * # NOQA - >>> self = SamplingResult.random(rng=10) - >>> print(f'self = {self}') - self = - """ - - def __init__(self, pos_inds, neg_inds, bboxes, gt_bboxes, assign_result, - gt_flags): - self.pos_inds = pos_inds - self.neg_inds = neg_inds - self.pos_bboxes = bboxes[pos_inds] - self.neg_bboxes = bboxes[neg_inds] - self.pos_is_gt = gt_flags[pos_inds] - - self.num_gts = gt_bboxes.shape[0] - self.pos_assigned_gt_inds = assign_result.gt_inds[pos_inds] - 1 - - if gt_bboxes.numel() == 0: - # hack for index error case - assert self.pos_assigned_gt_inds.numel() == 0 - self.pos_gt_bboxes = torch.empty_like(gt_bboxes).view(-1, 4) - else: - if len(gt_bboxes.shape) < 2: - gt_bboxes = gt_bboxes.view(-1, 4) - - self.pos_gt_bboxes = gt_bboxes[self.pos_assigned_gt_inds.long(), :] - - if assign_result.labels is not None: - self.pos_gt_labels = assign_result.labels[pos_inds] - else: - self.pos_gt_labels = None - - @property - def bboxes(self): - """torch.Tensor: concatenated positive and negative boxes""" - return torch.cat([self.pos_bboxes, self.neg_bboxes]) - - def to(self, device): - """Change the device of the data inplace. - - Example: - >>> self = SamplingResult.random() - >>> print(f'self = {self.to(None)}') - >>> # xdoctest: +REQUIRES(--gpu) - >>> print(f'self = {self.to(0)}') - """ - _dict = self.__dict__ - for key, value in _dict.items(): - if isinstance(value, torch.Tensor): - _dict[key] = value.to(device) - return self - - def __nice__(self): - data = self.info.copy() - data['pos_bboxes'] = data.pop('pos_bboxes').shape - data['neg_bboxes'] = data.pop('neg_bboxes').shape - parts = [f"'{k}': {v!r}" for k, v in sorted(data.items())] - body = ' ' + ',\n '.join(parts) - return '{\n' + body + '\n}' - - @property - def info(self): - """Returns a dictionary of info about the object.""" - return { - 'pos_inds': self.pos_inds, - 'neg_inds': self.neg_inds, - 'pos_bboxes': self.pos_bboxes, - 'neg_bboxes': self.neg_bboxes, - 'pos_is_gt': self.pos_is_gt, - 'num_gts': self.num_gts, - 'pos_assigned_gt_inds': self.pos_assigned_gt_inds, - } - - @classmethod - def random(cls, rng=None, **kwargs): - """ - Args: - rng (None | int | numpy.random.RandomState): seed or state. - kwargs (keyword arguments): - - num_preds: number of predicted boxes - - num_gts: number of true boxes - - p_ignore (float): probability of a predicted box assigned to \ - an ignored truth. - - p_assigned (float): probability of a predicted box not being \ - assigned. - - p_use_label (float | bool): with labels or not. - - Returns: - :obj:`SamplingResult`: Randomly generated sampling result. - - Example: - >>> from mmdet.core.bbox.samplers.sampling_result import * # NOQA - >>> self = SamplingResult.random() - >>> print(self.__dict__) - """ - from mmdet.core.bbox import demodata - from mmdet.core.bbox.assigners.assign_result import AssignResult - from mmdet.core.bbox.samplers.random_sampler import RandomSampler - rng = demodata.ensure_rng(rng) - - # make probabalistic? - num = 32 - pos_fraction = 0.5 - neg_pos_ub = -1 - - assign_result = AssignResult.random(rng=rng, **kwargs) - - # Note we could just compute an assignment - bboxes = demodata.random_boxes(assign_result.num_preds, rng=rng) - gt_bboxes = demodata.random_boxes(assign_result.num_gts, rng=rng) - - if rng.rand() > 0.2: - # sometimes algorithms squeeze their data, be robust to that - gt_bboxes = gt_bboxes.squeeze() - bboxes = bboxes.squeeze() - - if assign_result.labels is None: - gt_labels = None - else: - gt_labels = None # todo - - if gt_labels is None: - add_gt_as_proposals = False - else: - add_gt_as_proposals = True # make probabalistic? - - sampler = RandomSampler( - num, - pos_fraction, - neg_pos_ub=neg_pos_ub, - add_gt_as_proposals=add_gt_as_proposals, - rng=rng) - self = sampler.sample(assign_result, bboxes, gt_bboxes, gt_labels) - return self diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/score_hlr_sampler.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/score_hlr_sampler.py deleted file mode 100755 index 888c815a1..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/samplers/score_hlr_sampler.py +++ /dev/null @@ -1,265 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.ops import nms_match - -from ..builder import BBOX_SAMPLERS -from ..transforms import bbox2roi -from .base_sampler import BaseSampler -from .sampling_result import SamplingResult - - -@BBOX_SAMPLERS.register_module() -class ScoreHLRSampler(BaseSampler): - r"""Importance-based Sample Reweighting (ISR_N), described in `Prime Sample - Attention in Object Detection `_. - - Score hierarchical local rank (HLR) differentiates with RandomSampler in - negative part. It firstly computes Score-HLR in a two-step way, - then linearly maps score hlr to the loss weights. - - Args: - num (int): Total number of sampled RoIs. - pos_fraction (float): Fraction of positive samples. - context (:class:`BaseRoIHead`): RoI head that the sampler belongs to. - neg_pos_ub (int): Upper bound of the ratio of num negative to num - positive, -1 means no upper bound. - add_gt_as_proposals (bool): Whether to add ground truth as proposals. - k (float): Power of the non-linear mapping. - bias (float): Shift of the non-linear mapping. - score_thr (float): Minimum score that a negative sample is to be - considered as valid bbox. - """ - - def __init__(self, - num, - pos_fraction, - context, - neg_pos_ub=-1, - add_gt_as_proposals=True, - k=0.5, - bias=0, - score_thr=0.05, - iou_thr=0.5, - **kwargs): - super().__init__(num, pos_fraction, neg_pos_ub, add_gt_as_proposals) - self.k = k - self.bias = bias - self.score_thr = score_thr - self.iou_thr = iou_thr - self.context = context - # context of cascade detectors is a list, so distinguish them here. - if not hasattr(context, 'num_stages'): - self.bbox_roi_extractor = context.bbox_roi_extractor - self.bbox_head = context.bbox_head - self.with_shared_head = context.with_shared_head - if self.with_shared_head: - self.shared_head = context.shared_head - else: - self.bbox_roi_extractor = context.bbox_roi_extractor[ - context.current_stage] - self.bbox_head = context.bbox_head[context.current_stage] - - @staticmethod - def random_choice(gallery, num): - """Randomly select some elements from the gallery. - - If `gallery` is a Tensor, the returned indices will be a Tensor; - If `gallery` is a ndarray or list, the returned indices will be a - ndarray. - - Args: - gallery (Tensor | ndarray | list): indices pool. - num (int): expected sample num. - - Returns: - Tensor or ndarray: sampled indices. - """ - assert len(gallery) >= num - - is_tensor = isinstance(gallery, torch.Tensor) - if not is_tensor: - if torch.cuda.is_available(): - device = torch.cuda.current_device() - else: - device = 'cpu' - gallery = torch.tensor(gallery, dtype=torch.long, device=device) - perm = torch.randperm(gallery.numel(), device=gallery.device)[:num] - rand_inds = gallery[perm] - if not is_tensor: - rand_inds = rand_inds.cpu().numpy() - return rand_inds - - def _sample_pos(self, assign_result, num_expected, **kwargs): - """Randomly sample some positive samples.""" - pos_inds = torch.nonzero(assign_result.gt_inds > 0).flatten() - if pos_inds.numel() <= num_expected: - return pos_inds - else: - return self.random_choice(pos_inds, num_expected) - - def _sample_neg(self, - assign_result, - num_expected, - bboxes, - feats=None, - img_meta=None, - **kwargs): - """Sample negative samples. - - Score-HLR sampler is done in the following steps: - 1. Take the maximum positive score prediction of each negative samples - as s_i. - 2. Filter out negative samples whose s_i <= score_thr, the left samples - are called valid samples. - 3. Use NMS-Match to divide valid samples into different groups, - samples in the same group will greatly overlap with each other - 4. Rank the matched samples in two-steps to get Score-HLR. - (1) In the same group, rank samples with their scores. - (2) In the same score rank across different groups, - rank samples with their scores again. - 5. Linearly map Score-HLR to the final label weights. - - Args: - assign_result (:obj:`AssignResult`): result of assigner. - num_expected (int): Expected number of samples. - bboxes (Tensor): bbox to be sampled. - feats (Tensor): Features come from FPN. - img_meta (dict): Meta information dictionary. - """ - neg_inds = torch.nonzero(assign_result.gt_inds == 0).flatten() - num_neg = neg_inds.size(0) - if num_neg == 0: - return neg_inds, None - with torch.no_grad(): - neg_bboxes = bboxes[neg_inds] - neg_rois = bbox2roi([neg_bboxes]) - bbox_result = self.context._bbox_forward(feats, neg_rois) - cls_score, bbox_pred = bbox_result['cls_score'], bbox_result[ - 'bbox_pred'] - - ori_loss = self.bbox_head.loss( - cls_score=cls_score, - bbox_pred=None, - rois=None, - labels=neg_inds.new_full((num_neg, ), - self.bbox_head.num_classes), - label_weights=cls_score.new_ones(num_neg), - bbox_targets=None, - bbox_weights=None, - reduction_override='none')['loss_cls'] - - # filter out samples with the max score lower than score_thr - max_score, argmax_score = cls_score.softmax(-1)[:, :-1].max(-1) - valid_inds = (max_score > self.score_thr).nonzero().view(-1) - invalid_inds = (max_score <= self.score_thr).nonzero().view(-1) - num_valid = valid_inds.size(0) - num_invalid = invalid_inds.size(0) - - num_expected = min(num_neg, num_expected) - num_hlr = min(num_valid, num_expected) - num_rand = num_expected - num_hlr - if num_valid > 0: - valid_rois = neg_rois[valid_inds] - valid_max_score = max_score[valid_inds] - valid_argmax_score = argmax_score[valid_inds] - valid_bbox_pred = bbox_pred[valid_inds] - - # valid_bbox_pred shape: [num_valid, #num_classes, 4] - valid_bbox_pred = valid_bbox_pred.view( - valid_bbox_pred.size(0), -1, 4) - selected_bbox_pred = valid_bbox_pred[range(num_valid), - valid_argmax_score] - pred_bboxes = self.bbox_head.bbox_coder.decode( - valid_rois[:, 1:], selected_bbox_pred) - pred_bboxes_with_score = torch.cat( - [pred_bboxes, valid_max_score[:, None]], -1) - group = nms_match(pred_bboxes_with_score, self.iou_thr) - - # imp: importance - imp = cls_score.new_zeros(num_valid) - for g in group: - g_score = valid_max_score[g] - # g_score has already sorted - rank = g_score.new_tensor(range(g_score.size(0))) - imp[g] = num_valid - rank + g_score - _, imp_rank_inds = imp.sort(descending=True) - _, imp_rank = imp_rank_inds.sort() - hlr_inds = imp_rank_inds[:num_expected] - - if num_rand > 0: - rand_inds = torch.randperm(num_invalid)[:num_rand] - select_inds = torch.cat( - [valid_inds[hlr_inds], invalid_inds[rand_inds]]) - else: - select_inds = valid_inds[hlr_inds] - - neg_label_weights = cls_score.new_ones(num_expected) - - up_bound = max(num_expected, num_valid) - imp_weights = (up_bound - - imp_rank[hlr_inds].float()) / up_bound - neg_label_weights[:num_hlr] = imp_weights - neg_label_weights[num_hlr:] = imp_weights.min() - neg_label_weights = (self.bias + - (1 - self.bias) * neg_label_weights).pow( - self.k) - ori_selected_loss = ori_loss[select_inds] - new_loss = ori_selected_loss * neg_label_weights - norm_ratio = ori_selected_loss.sum() / new_loss.sum() - neg_label_weights *= norm_ratio - else: - neg_label_weights = cls_score.new_ones(num_expected) - select_inds = torch.randperm(num_neg)[:num_expected] - - return neg_inds[select_inds], neg_label_weights - - def sample(self, - assign_result, - bboxes, - gt_bboxes, - gt_labels=None, - img_meta=None, - **kwargs): - """Sample positive and negative bboxes. - - This is a simple implementation of bbox sampling given candidates, - assigning results and ground truth bboxes. - - Args: - assign_result (:obj:`AssignResult`): Bbox assigning results. - bboxes (Tensor): Boxes to be sampled from. - gt_bboxes (Tensor): Ground truth bboxes. - gt_labels (Tensor, optional): Class labels of ground truth bboxes. - - Returns: - tuple[:obj:`SamplingResult`, Tensor]: Sampling result and negative - label weights. - """ - bboxes = bboxes[:, :4] - - gt_flags = bboxes.new_zeros((bboxes.shape[0], ), dtype=torch.uint8) - if self.add_gt_as_proposals: - bboxes = torch.cat([gt_bboxes, bboxes], dim=0) - assign_result.add_gt_(gt_labels) - gt_ones = bboxes.new_ones(gt_bboxes.shape[0], dtype=torch.uint8) - gt_flags = torch.cat([gt_ones, gt_flags]) - - num_expected_pos = int(self.num * self.pos_fraction) - pos_inds = self.pos_sampler._sample_pos( - assign_result, num_expected_pos, bboxes=bboxes, **kwargs) - num_sampled_pos = pos_inds.numel() - num_expected_neg = self.num - num_sampled_pos - if self.neg_pos_ub >= 0: - _pos = max(1, num_sampled_pos) - neg_upper_bound = int(self.neg_pos_ub * _pos) - if num_expected_neg > neg_upper_bound: - num_expected_neg = neg_upper_bound - neg_inds, neg_label_weights = self.neg_sampler._sample_neg( - assign_result, - num_expected_neg, - bboxes, - img_meta=img_meta, - **kwargs) - - return SamplingResult(pos_inds, neg_inds, bboxes, gt_bboxes, - assign_result, gt_flags), neg_label_weights diff --git a/cv/detection/autoassign/pytorch/mmdet/core/bbox/transforms.py b/cv/detection/autoassign/pytorch/mmdet/core/bbox/transforms.py deleted file mode 100755 index aaff90265..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/bbox/transforms.py +++ /dev/null @@ -1,270 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch - - -def find_inside_bboxes(bboxes, img_h, img_w): - """Find bboxes as long as a part of bboxes is inside the image. - - Args: - bboxes (Tensor): Shape (N, 4). - img_h (int): Image height. - img_w (int): Image width. - - Returns: - Tensor: Index of the remaining bboxes. - """ - inside_inds = (bboxes[:, 0] < img_w) & (bboxes[:, 2] > 0) \ - & (bboxes[:, 1] < img_h) & (bboxes[:, 3] > 0) - return inside_inds - - -def bbox_flip(bboxes, img_shape, direction='horizontal'): - """Flip bboxes horizontally or vertically. - - Args: - bboxes (Tensor): Shape (..., 4*k) - img_shape (tuple): Image shape. - direction (str): Flip direction, options are "horizontal", "vertical", - "diagonal". Default: "horizontal" - - Returns: - Tensor: Flipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - assert direction in ['horizontal', 'vertical', 'diagonal'] - flipped = bboxes.clone() - if direction == 'horizontal': - flipped[..., 0::4] = img_shape[1] - bboxes[..., 2::4] - flipped[..., 2::4] = img_shape[1] - bboxes[..., 0::4] - elif direction == 'vertical': - flipped[..., 1::4] = img_shape[0] - bboxes[..., 3::4] - flipped[..., 3::4] = img_shape[0] - bboxes[..., 1::4] - else: - flipped[..., 0::4] = img_shape[1] - bboxes[..., 2::4] - flipped[..., 1::4] = img_shape[0] - bboxes[..., 3::4] - flipped[..., 2::4] = img_shape[1] - bboxes[..., 0::4] - flipped[..., 3::4] = img_shape[0] - bboxes[..., 1::4] - return flipped - - -def bbox_mapping(bboxes, - img_shape, - scale_factor, - flip, - flip_direction='horizontal'): - """Map bboxes from the original image scale to testing scale.""" - new_bboxes = bboxes * bboxes.new_tensor(scale_factor) - if flip: - new_bboxes = bbox_flip(new_bboxes, img_shape, flip_direction) - return new_bboxes - - -def bbox_mapping_back(bboxes, - img_shape, - scale_factor, - flip, - flip_direction='horizontal'): - """Map bboxes from testing scale to original image scale.""" - new_bboxes = bbox_flip(bboxes, img_shape, - flip_direction) if flip else bboxes - new_bboxes = new_bboxes.view(-1, 4) / new_bboxes.new_tensor(scale_factor) - return new_bboxes.view(bboxes.shape) - - -def bbox2roi(bbox_list): - """Convert a list of bboxes to roi format. - - Args: - bbox_list (list[Tensor]): a list of bboxes corresponding to a batch - of images. - - Returns: - Tensor: shape (n, 5), [batch_ind, x1, y1, x2, y2] - """ - rois_list = [] - for img_id, bboxes in enumerate(bbox_list): - if bboxes.size(0) > 0: - img_inds = bboxes.new_full((bboxes.size(0), 1), img_id) - rois = torch.cat([img_inds, bboxes[:, :4]], dim=-1) - else: - rois = bboxes.new_zeros((0, 5)) - rois_list.append(rois) - rois = torch.cat(rois_list, 0) - return rois - - -def roi2bbox(rois): - """Convert rois to bounding box format. - - Args: - rois (torch.Tensor): RoIs with the shape (n, 5) where the first - column indicates batch id of each RoI. - - Returns: - list[torch.Tensor]: Converted boxes of corresponding rois. - """ - bbox_list = [] - img_ids = torch.unique(rois[:, 0].cpu(), sorted=True) - for img_id in img_ids: - inds = (rois[:, 0] == img_id.item()) - bbox = rois[inds, 1:] - bbox_list.append(bbox) - return bbox_list - - -def bbox2result(bboxes, labels, num_classes): - """Convert detection results to a list of numpy arrays. - - Args: - bboxes (torch.Tensor | np.ndarray): shape (n, 5) - labels (torch.Tensor | np.ndarray): shape (n, ) - num_classes (int): class number, including background class - - Returns: - list(ndarray): bbox results of each class - """ - if bboxes.shape[0] == 0: - return [np.zeros((0, 5), dtype=np.float32) for i in range(num_classes)] - else: - if isinstance(bboxes, torch.Tensor): - bboxes = bboxes.detach().cpu().numpy() - labels = labels.detach().cpu().numpy() - return [bboxes[labels == i, :] for i in range(num_classes)] - - -def distance2bbox(points, distance, max_shape=None): - """Decode distance prediction to bounding box. - - Args: - points (Tensor): Shape (B, N, 2) or (N, 2). - distance (Tensor): Distance from the given point to 4 - boundaries (left, top, right, bottom). Shape (B, N, 4) or (N, 4) - max_shape (Sequence[int] or torch.Tensor or Sequence[ - Sequence[int]],optional): Maximum bounds for boxes, specifies - (H, W, C) or (H, W). If priors shape is (B, N, 4), then - the max_shape should be a Sequence[Sequence[int]] - and the length of max_shape should also be B. - - Returns: - Tensor: Boxes with shape (N, 4) or (B, N, 4) - """ - - x1 = points[..., 0] - distance[..., 0] - y1 = points[..., 1] - distance[..., 1] - x2 = points[..., 0] + distance[..., 2] - y2 = points[..., 1] + distance[..., 3] - - bboxes = torch.stack([x1, y1, x2, y2], -1) - - if max_shape is not None: - if bboxes.dim() == 2 and not torch.onnx.is_in_onnx_export(): - # speed up - bboxes[:, 0::2].clamp_(min=0, max=max_shape[1]) - bboxes[:, 1::2].clamp_(min=0, max=max_shape[0]) - return bboxes - - # clip bboxes with dynamic `min` and `max` for onnx - if torch.onnx.is_in_onnx_export(): - from mmdet.core.export import dynamic_clip_for_onnx - x1, y1, x2, y2 = dynamic_clip_for_onnx(x1, y1, x2, y2, max_shape) - bboxes = torch.stack([x1, y1, x2, y2], dim=-1) - return bboxes - if not isinstance(max_shape, torch.Tensor): - max_shape = x1.new_tensor(max_shape) - max_shape = max_shape[..., :2].type_as(x1) - if max_shape.ndim == 2: - assert bboxes.ndim == 3 - assert max_shape.size(0) == bboxes.size(0) - - min_xy = x1.new_tensor(0) - max_xy = torch.cat([max_shape, max_shape], - dim=-1).flip(-1).unsqueeze(-2) - bboxes = torch.where(bboxes < min_xy, min_xy, bboxes) - bboxes = torch.where(bboxes > max_xy, max_xy, bboxes) - - return bboxes - - -def bbox2distance(points, bbox, max_dis=None, eps=0.1): - """Decode bounding box based on distances. - - Args: - points (Tensor): Shape (n, 2), [x, y]. - bbox (Tensor): Shape (n, 4), "xyxy" format - max_dis (float): Upper bound of the distance. - eps (float): a small value to ensure target < max_dis, instead <= - - Returns: - Tensor: Decoded distances. - """ - left = points[:, 0] - bbox[:, 0] - top = points[:, 1] - bbox[:, 1] - right = bbox[:, 2] - points[:, 0] - bottom = bbox[:, 3] - points[:, 1] - if max_dis is not None: - left = left.clamp(min=0, max=max_dis - eps) - top = top.clamp(min=0, max=max_dis - eps) - right = right.clamp(min=0, max=max_dis - eps) - bottom = bottom.clamp(min=0, max=max_dis - eps) - return torch.stack([left, top, right, bottom], -1) - - -def bbox_rescale(bboxes, scale_factor=1.0): - """Rescale bounding box w.r.t. scale_factor. - - Args: - bboxes (Tensor): Shape (n, 4) for bboxes or (n, 5) for rois - scale_factor (float): rescale factor - - Returns: - Tensor: Rescaled bboxes. - """ - if bboxes.size(1) == 5: - bboxes_ = bboxes[:, 1:] - inds_ = bboxes[:, 0] - else: - bboxes_ = bboxes - cx = (bboxes_[:, 0] + bboxes_[:, 2]) * 0.5 - cy = (bboxes_[:, 1] + bboxes_[:, 3]) * 0.5 - w = bboxes_[:, 2] - bboxes_[:, 0] - h = bboxes_[:, 3] - bboxes_[:, 1] - w = w * scale_factor - h = h * scale_factor - x1 = cx - 0.5 * w - x2 = cx + 0.5 * w - y1 = cy - 0.5 * h - y2 = cy + 0.5 * h - if bboxes.size(1) == 5: - rescaled_bboxes = torch.stack([inds_, x1, y1, x2, y2], dim=-1) - else: - rescaled_bboxes = torch.stack([x1, y1, x2, y2], dim=-1) - return rescaled_bboxes - - -def bbox_cxcywh_to_xyxy(bbox): - """Convert bbox coordinates from (cx, cy, w, h) to (x1, y1, x2, y2). - - Args: - bbox (Tensor): Shape (n, 4) for bboxes. - - Returns: - Tensor: Converted bboxes. - """ - cx, cy, w, h = bbox.split((1, 1, 1, 1), dim=-1) - bbox_new = [(cx - 0.5 * w), (cy - 0.5 * h), (cx + 0.5 * w), (cy + 0.5 * h)] - return torch.cat(bbox_new, dim=-1) - - -def bbox_xyxy_to_cxcywh(bbox): - """Convert bbox coordinates from (x1, y1, x2, y2) to (cx, cy, w, h). - - Args: - bbox (Tensor): Shape (n, 4) for bboxes. - - Returns: - Tensor: Converted bboxes. - """ - x1, y1, x2, y2 = bbox.split((1, 1, 1, 1), dim=-1) - bbox_new = [(x1 + x2) / 2, (y1 + y2) / 2, (x2 - x1), (y2 - y1)] - return torch.cat(bbox_new, dim=-1) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/data_structures/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/data_structures/__init__.py deleted file mode 100755 index 1fb105b41..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/data_structures/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .general_data import GeneralData -from .instance_data import InstanceData - -__all__ = ['GeneralData', 'InstanceData'] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/data_structures/general_data.py b/cv/detection/autoassign/pytorch/mmdet/core/data_structures/general_data.py deleted file mode 100755 index 51ac32bfc..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/data_structures/general_data.py +++ /dev/null @@ -1,326 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -import numpy as np -import torch - -from mmdet.utils.util_mixins import NiceRepr - - -class GeneralData(NiceRepr): - """A general data structure of OpenMMlab. - - A data structure that stores the meta information, - the annotations of the images or the model predictions, - which can be used in communication between components. - - The attributes in `GeneralData` are divided into two parts, - the `meta_info_fields` and the `data_fields` respectively. - - - `meta_info_fields`: Usually contains the - information about the image such as filename, - image_shape, pad_shape, etc. All attributes in - it are immutable once set, - but the user can add new meta information with - `set_meta_info` function, all information can be accessed - with methods `meta_info_keys`, `meta_info_values`, - `meta_info_items`. - - - `data_fields`: Annotations or model predictions are - stored. The attributes can be accessed or modified by - dict-like or object-like operations, such as - `.` , `[]`, `in`, `del`, `pop(str)` `get(str)`, `keys()`, - `values()`, `items()`. Users can also apply tensor-like methods - to all obj:`torch.Tensor` in the `data_fileds`, - such as `.cuda()`, `.cpu()`, `.numpy()`, `device`, `.to()` - `.detach()`, `.numpy()` - - Args: - meta_info (dict, optional): A dict contains the meta information - of single image. such as `img_shape`, `scale_factor`, etc. - Default: None. - data (dict, optional): A dict contains annotations of single image or - model predictions. Default: None. - - Examples: - >>> from mmdet.core import GeneralData - >>> img_meta = dict(img_shape=(800, 1196, 3), pad_shape=(800, 1216, 3)) - >>> instance_data = GeneralData(meta_info=img_meta) - >>> img_shape in instance_data - True - >>> instance_data.det_labels = torch.LongTensor([0, 1, 2, 3]) - >>> instance_data["det_scores"] = torch.Tensor([0.01, 0.1, 0.2, 0.3]) - >>> print(results) - - >>> instance_data.det_scores - tensor([0.0100, 0.1000, 0.2000, 0.3000]) - >>> instance_data.det_labels - tensor([0, 1, 2, 3]) - >>> instance_data['det_labels'] - tensor([0, 1, 2, 3]) - >>> 'det_labels' in instance_data - True - >>> instance_data.img_shape - (800, 1196, 3) - >>> 'det_scores' in instance_data - True - >>> del instance_data.det_scores - >>> 'det_scores' in instance_data - False - >>> det_labels = instance_data.pop('det_labels', None) - >>> det_labels - tensor([0, 1, 2, 3]) - >>> 'det_labels' in instance_data - >>> False - """ - - def __init__(self, meta_info=None, data=None): - - self._meta_info_fields = set() - self._data_fields = set() - - if meta_info is not None: - self.set_meta_info(meta_info=meta_info) - if data is not None: - self.set_data(data) - - def set_meta_info(self, meta_info): - """Add meta information. - - Args: - meta_info (dict): A dict contains the meta information - of image. such as `img_shape`, `scale_factor`, etc. - Default: None. - """ - assert isinstance(meta_info, - dict), f'meta should be a `dict` but get {meta_info}' - meta = copy.deepcopy(meta_info) - for k, v in meta.items(): - # should be consistent with original meta_info - if k in self._meta_info_fields: - ori_value = getattr(self, k) - if isinstance(ori_value, (torch.Tensor, np.ndarray)): - if (ori_value == v).all(): - continue - else: - raise KeyError( - f'img_meta_info {k} has been set as ' - f'{getattr(self, k)} before, which is immutable ') - elif ori_value == v: - continue - else: - raise KeyError( - f'img_meta_info {k} has been set as ' - f'{getattr(self, k)} before, which is immutable ') - else: - self._meta_info_fields.add(k) - self.__dict__[k] = v - - def set_data(self, data): - """Update a dict to `data_fields`. - - Args: - data (dict): A dict contains annotations of image or - model predictions. Default: None. - """ - assert isinstance(data, - dict), f'meta should be a `dict` but get {data}' - for k, v in data.items(): - self.__setattr__(k, v) - - def new(self, meta_info=None, data=None): - """Return a new results with same image meta information. - - Args: - meta_info (dict, optional): A dict contains the meta information - of image. such as `img_shape`, `scale_factor`, etc. - Default: None. - data (dict, optional): A dict contains annotations of image or - model predictions. Default: None. - """ - new_data = self.__class__() - new_data.set_meta_info(dict(self.meta_info_items())) - if meta_info is not None: - new_data.set_meta_info(meta_info) - if data is not None: - new_data.set_data(data) - return new_data - - def keys(self): - """ - Returns: - list: Contains all keys in data_fields. - """ - return [key for key in self._data_fields] - - def meta_info_keys(self): - """ - Returns: - list: Contains all keys in meta_info_fields. - """ - return [key for key in self._meta_info_fields] - - def values(self): - """ - Returns: - list: Contains all values in data_fields. - """ - return [getattr(self, k) for k in self.keys()] - - def meta_info_values(self): - """ - Returns: - list: Contains all values in meta_info_fields. - """ - return [getattr(self, k) for k in self.meta_info_keys()] - - def items(self): - for k in self.keys(): - yield (k, getattr(self, k)) - - def meta_info_items(self): - for k in self.meta_info_keys(): - yield (k, getattr(self, k)) - - def __setattr__(self, name, val): - if name in ('_meta_info_fields', '_data_fields'): - if not hasattr(self, name): - super().__setattr__(name, val) - else: - raise AttributeError( - f'{name} has been used as a ' - f'private attribute, which is immutable. ') - else: - if name in self._meta_info_fields: - raise AttributeError(f'`{name}` is used in meta information,' - f'which is immutable') - - self._data_fields.add(name) - super().__setattr__(name, val) - - def __delattr__(self, item): - - if item in ('_meta_info_fields', '_data_fields'): - raise AttributeError(f'{item} has been used as a ' - f'private attribute, which is immutable. ') - - if item in self._meta_info_fields: - raise KeyError(f'{item} is used in meta information, ' - f'which is immutable.') - super().__delattr__(item) - if item in self._data_fields: - self._data_fields.remove(item) - - # dict-like methods - __setitem__ = __setattr__ - __delitem__ = __delattr__ - - def __getitem__(self, name): - return getattr(self, name) - - def get(self, *args): - assert len(args) < 3, '`get` get more than 2 arguments' - return self.__dict__.get(*args) - - def pop(self, *args): - assert len(args) < 3, '`pop` get more than 2 arguments' - name = args[0] - if name in self._meta_info_fields: - raise KeyError(f'{name} is a key in meta information, ' - f'which is immutable') - - if args[0] in self._data_fields: - self._data_fields.remove(args[0]) - return self.__dict__.pop(*args) - - # with default value - elif len(args) == 2: - return args[1] - else: - raise KeyError(f'{args[0]}') - - def __contains__(self, item): - return item in self._data_fields or \ - item in self._meta_info_fields - - # Tensor-like methods - def to(self, *args, **kwargs): - """Apply same name function to all tensors in data_fields.""" - new_data = self.new() - for k, v in self.items(): - if hasattr(v, 'to'): - v = v.to(*args, **kwargs) - new_data[k] = v - return new_data - - # Tensor-like methods - def cpu(self): - """Apply same name function to all tensors in data_fields.""" - new_data = self.new() - for k, v in self.items(): - if isinstance(v, torch.Tensor): - v = v.cpu() - new_data[k] = v - return new_data - - # Tensor-like methods - def mlu(self): - """Apply same name function to all tensors in data_fields.""" - new_data = self.new() - for k, v in self.items(): - if isinstance(v, torch.Tensor): - v = v.mlu() - new_data[k] = v - return new_data - - # Tensor-like methods - def cuda(self): - """Apply same name function to all tensors in data_fields.""" - new_data = self.new() - for k, v in self.items(): - if isinstance(v, torch.Tensor): - v = v.cuda() - new_data[k] = v - return new_data - - # Tensor-like methods - def detach(self): - """Apply same name function to all tensors in data_fields.""" - new_data = self.new() - for k, v in self.items(): - if isinstance(v, torch.Tensor): - v = v.detach() - new_data[k] = v - return new_data - - # Tensor-like methods - def numpy(self): - """Apply same name function to all tensors in data_fields.""" - new_data = self.new() - for k, v in self.items(): - if isinstance(v, torch.Tensor): - v = v.detach().cpu().numpy() - new_data[k] = v - return new_data - - def __nice__(self): - repr = '\n \n META INFORMATION \n' - for k, v in self.meta_info_items(): - repr += f'{k}: {v} \n' - repr += '\n DATA FIELDS \n' - for k, v in self.items(): - if isinstance(v, (torch.Tensor, np.ndarray)): - repr += f'shape of {k}: {v.shape} \n' - else: - repr += f'{k}: {v} \n' - return repr + '\n' diff --git a/cv/detection/autoassign/pytorch/mmdet/core/data_structures/instance_data.py b/cv/detection/autoassign/pytorch/mmdet/core/data_structures/instance_data.py deleted file mode 100755 index 1573d001d..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/data_structures/instance_data.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import itertools - -import numpy as np -import torch - -from .general_data import GeneralData - - -class InstanceData(GeneralData): - """Data structure for instance-level annnotations or predictions. - - Subclass of :class:`GeneralData`. All value in `data_fields` - should have the same length. This design refer to - https://github.com/facebookresearch/detectron2/blob/master/detectron2/structures/instances.py # noqa E501 - - Examples: - >>> from mmdet.core import InstanceData - >>> import numpy as np - >>> img_meta = dict(img_shape=(800, 1196, 3), pad_shape=(800, 1216, 3)) - >>> results = InstanceData(img_meta) - >>> img_shape in results - True - >>> results.det_labels = torch.LongTensor([0, 1, 2, 3]) - >>> results["det_scores"] = torch.Tensor([0.01, 0.7, 0.6, 0.3]) - >>> results["det_masks"] = np.ndarray(4, 2, 2) - >>> len(results) - 4 - >>> print(resutls) - - >>> sorted_results = results[results.det_scores.sort().indices] - >>> sorted_results.det_scores - tensor([0.0100, 0.3000, 0.6000, 0.7000]) - >>> sorted_results.det_labels - tensor([0, 3, 2, 1]) - >>> print(results[results.scores > 0.5]) - - >>> results[results.det_scores > 0.5].det_labels - tensor([1, 2]) - >>> results[results.det_scores > 0.5].det_scores - tensor([0.7000, 0.6000]) - """ - - def __setattr__(self, name, value): - - if name in ('_meta_info_fields', '_data_fields'): - if not hasattr(self, name): - super().__setattr__(name, value) - else: - raise AttributeError( - f'{name} has been used as a ' - f'private attribute, which is immutable. ') - - else: - assert isinstance(value, (torch.Tensor, np.ndarray, list)), \ - f'Can set {type(value)}, only support' \ - f' {(torch.Tensor, np.ndarray, list)}' - - if self._data_fields: - assert len(value) == len(self), f'the length of ' \ - f'values {len(value)} is ' \ - f'not consistent with' \ - f' the length ' \ - f'of this :obj:`InstanceData` ' \ - f'{len(self)} ' - super().__setattr__(name, value) - - def __getitem__(self, item): - """ - Args: - item (str, obj:`slice`, - obj`torch.LongTensor`, obj:`torch.BoolTensor`): - get the corresponding values according to item. - - Returns: - obj:`InstanceData`: Corresponding values. - """ - assert len(self), ' This is a empty instance' - - assert isinstance( - item, (str, slice, int, torch.LongTensor, torch.BoolTensor)) - - if isinstance(item, str): - return getattr(self, item) - - if type(item) == int: - if item >= len(self) or item < -len(self): - raise IndexError(f'Index {item} out of range!') - else: - # keep the dimension - item = slice(item, None, len(self)) - - new_data = self.new() - if isinstance(item, (torch.Tensor)): - assert item.dim() == 1, 'Only support to get the' \ - ' values along the first dimension.' - if isinstance(item, torch.BoolTensor): - assert len(item) == len(self), f'The shape of the' \ - f' input(BoolTensor)) ' \ - f'{len(item)} ' \ - f' does not match the shape ' \ - f'of the indexed tensor ' \ - f'in results_filed ' \ - f'{len(self)} at ' \ - f'first dimension. ' - - for k, v in self.items(): - if isinstance(v, torch.Tensor): - new_data[k] = v[item] - elif isinstance(v, np.ndarray): - new_data[k] = v[item.cpu().numpy()] - elif isinstance(v, list): - r_list = [] - # convert to indexes from boolTensor - if isinstance(item, torch.BoolTensor): - indexes = torch.nonzero(item).view(-1) - else: - indexes = item - for index in indexes: - r_list.append(v[index]) - new_data[k] = r_list - else: - # item is a slice - for k, v in self.items(): - new_data[k] = v[item] - return new_data - - @staticmethod - def cat(instances_list): - """Concat the predictions of all :obj:`InstanceData` in the list. - - Args: - instances_list (list[:obj:`InstanceData`]): A list - of :obj:`InstanceData`. - - Returns: - obj:`InstanceData` - """ - assert all( - isinstance(results, InstanceData) for results in instances_list) - assert len(instances_list) > 0 - if len(instances_list) == 1: - return instances_list[0] - - new_data = instances_list[0].new() - for k in instances_list[0]._data_fields: - values = [results[k] for results in instances_list] - v0 = values[0] - if isinstance(v0, torch.Tensor): - values = torch.cat(values, dim=0) - elif isinstance(v0, np.ndarray): - values = np.concatenate(values, axis=0) - elif isinstance(v0, list): - values = list(itertools.chain(*values)) - else: - raise ValueError( - f'Can not concat the {k} which is a {type(v0)}') - new_data[k] = values - return new_data - - def __len__(self): - if len(self._data_fields): - for v in self.values(): - return len(v) - else: - raise AssertionError('This is an empty `InstanceData`.') diff --git a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/evaluation/__init__.py deleted file mode 100755 index dedae8ef5..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .class_names import (cityscapes_classes, coco_classes, dataset_aliases, - get_classes, imagenet_det_classes, - imagenet_vid_classes, oid_challenge_classes, - oid_v6_classes, voc_classes) -from .eval_hooks import DistEvalHook, EvalHook -from .mean_ap import average_precision, eval_map, print_map_summary -from .panoptic_utils import INSTANCE_OFFSET -from .recall import (eval_recalls, plot_iou_recall, plot_num_recall, - print_recall_summary) - -__all__ = [ - 'voc_classes', 'imagenet_det_classes', 'imagenet_vid_classes', - 'coco_classes', 'cityscapes_classes', 'dataset_aliases', 'get_classes', - 'DistEvalHook', 'EvalHook', 'average_precision', 'eval_map', - 'print_map_summary', 'eval_recalls', 'print_recall_summary', - 'plot_num_recall', 'plot_iou_recall', 'oid_v6_classes', - 'oid_challenge_classes', 'INSTANCE_OFFSET' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/bbox_overlaps.py b/cv/detection/autoassign/pytorch/mmdet/core/evaluation/bbox_overlaps.py deleted file mode 100755 index ba52ed2c2..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/bbox_overlaps.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - - -def bbox_overlaps(bboxes1, - bboxes2, - mode='iou', - eps=1e-6, - use_legacy_coordinate=False): - """Calculate the ious between each bbox of bboxes1 and bboxes2. - - Args: - bboxes1 (ndarray): Shape (n, 4) - bboxes2 (ndarray): Shape (k, 4) - mode (str): IOU (intersection over union) or IOF (intersection - over foreground) - use_legacy_coordinate (bool): Whether to use coordinate system in - mmdet v1.x. which means width, height should be - calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively. - Note when function is used in `VOCDataset`, it should be - True to align with the official implementation - `http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCdevkit_18-May-2011.tar` - Default: False. - - Returns: - ious (ndarray): Shape (n, k) - """ - - assert mode in ['iou', 'iof'] - if not use_legacy_coordinate: - extra_length = 0. - else: - extra_length = 1. - bboxes1 = bboxes1.astype(np.float32) - bboxes2 = bboxes2.astype(np.float32) - rows = bboxes1.shape[0] - cols = bboxes2.shape[0] - ious = np.zeros((rows, cols), dtype=np.float32) - if rows * cols == 0: - return ious - exchange = False - if bboxes1.shape[0] > bboxes2.shape[0]: - bboxes1, bboxes2 = bboxes2, bboxes1 - ious = np.zeros((cols, rows), dtype=np.float32) - exchange = True - area1 = (bboxes1[:, 2] - bboxes1[:, 0] + extra_length) * ( - bboxes1[:, 3] - bboxes1[:, 1] + extra_length) - area2 = (bboxes2[:, 2] - bboxes2[:, 0] + extra_length) * ( - bboxes2[:, 3] - bboxes2[:, 1] + extra_length) - for i in range(bboxes1.shape[0]): - x_start = np.maximum(bboxes1[i, 0], bboxes2[:, 0]) - y_start = np.maximum(bboxes1[i, 1], bboxes2[:, 1]) - x_end = np.minimum(bboxes1[i, 2], bboxes2[:, 2]) - y_end = np.minimum(bboxes1[i, 3], bboxes2[:, 3]) - overlap = np.maximum(x_end - x_start + extra_length, 0) * np.maximum( - y_end - y_start + extra_length, 0) - if mode == 'iou': - union = area1[i] + area2 - overlap - else: - union = area1[i] if not exchange else area2 - union = np.maximum(union, eps) - ious[i, :] = overlap / union - if exchange: - ious = ious.T - return ious diff --git a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/class_names.py b/cv/detection/autoassign/pytorch/mmdet/core/evaluation/class_names.py deleted file mode 100755 index f959903d1..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/class_names.py +++ /dev/null @@ -1,332 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - - -def wider_face_classes(): - return ['face'] - - -def voc_classes(): - return [ - 'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', - 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', - 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor' - ] - - -def imagenet_det_classes(): - return [ - 'accordion', 'airplane', 'ant', 'antelope', 'apple', 'armadillo', - 'artichoke', 'axe', 'baby_bed', 'backpack', 'bagel', 'balance_beam', - 'banana', 'band_aid', 'banjo', 'baseball', 'basketball', 'bathing_cap', - 'beaker', 'bear', 'bee', 'bell_pepper', 'bench', 'bicycle', 'binder', - 'bird', 'bookshelf', 'bow_tie', 'bow', 'bowl', 'brassiere', 'burrito', - 'bus', 'butterfly', 'camel', 'can_opener', 'car', 'cart', 'cattle', - 'cello', 'centipede', 'chain_saw', 'chair', 'chime', 'cocktail_shaker', - 'coffee_maker', 'computer_keyboard', 'computer_mouse', 'corkscrew', - 'cream', 'croquet_ball', 'crutch', 'cucumber', 'cup_or_mug', 'diaper', - 'digital_clock', 'dishwasher', 'dog', 'domestic_cat', 'dragonfly', - 'drum', 'dumbbell', 'electric_fan', 'elephant', 'face_powder', 'fig', - 'filing_cabinet', 'flower_pot', 'flute', 'fox', 'french_horn', 'frog', - 'frying_pan', 'giant_panda', 'goldfish', 'golf_ball', 'golfcart', - 'guacamole', 'guitar', 'hair_dryer', 'hair_spray', 'hamburger', - 'hammer', 'hamster', 'harmonica', 'harp', 'hat_with_a_wide_brim', - 'head_cabbage', 'helmet', 'hippopotamus', 'horizontal_bar', 'horse', - 'hotdog', 'iPod', 'isopod', 'jellyfish', 'koala_bear', 'ladle', - 'ladybug', 'lamp', 'laptop', 'lemon', 'lion', 'lipstick', 'lizard', - 'lobster', 'maillot', 'maraca', 'microphone', 'microwave', 'milk_can', - 'miniskirt', 'monkey', 'motorcycle', 'mushroom', 'nail', 'neck_brace', - 'oboe', 'orange', 'otter', 'pencil_box', 'pencil_sharpener', 'perfume', - 'person', 'piano', 'pineapple', 'ping-pong_ball', 'pitcher', 'pizza', - 'plastic_bag', 'plate_rack', 'pomegranate', 'popsicle', 'porcupine', - 'power_drill', 'pretzel', 'printer', 'puck', 'punching_bag', 'purse', - 'rabbit', 'racket', 'ray', 'red_panda', 'refrigerator', - 'remote_control', 'rubber_eraser', 'rugby_ball', 'ruler', - 'salt_or_pepper_shaker', 'saxophone', 'scorpion', 'screwdriver', - 'seal', 'sheep', 'ski', 'skunk', 'snail', 'snake', 'snowmobile', - 'snowplow', 'soap_dispenser', 'soccer_ball', 'sofa', 'spatula', - 'squirrel', 'starfish', 'stethoscope', 'stove', 'strainer', - 'strawberry', 'stretcher', 'sunglasses', 'swimming_trunks', 'swine', - 'syringe', 'table', 'tape_player', 'tennis_ball', 'tick', 'tie', - 'tiger', 'toaster', 'traffic_light', 'train', 'trombone', 'trumpet', - 'turtle', 'tv_or_monitor', 'unicycle', 'vacuum', 'violin', - 'volleyball', 'waffle_iron', 'washer', 'water_bottle', 'watercraft', - 'whale', 'wine_bottle', 'zebra' - ] - - -def imagenet_vid_classes(): - return [ - 'airplane', 'antelope', 'bear', 'bicycle', 'bird', 'bus', 'car', - 'cattle', 'dog', 'domestic_cat', 'elephant', 'fox', 'giant_panda', - 'hamster', 'horse', 'lion', 'lizard', 'monkey', 'motorcycle', 'rabbit', - 'red_panda', 'sheep', 'snake', 'squirrel', 'tiger', 'train', 'turtle', - 'watercraft', 'whale', 'zebra' - ] - - -def coco_classes(): - return [ - 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', - 'truck', 'boat', 'traffic_light', 'fire_hydrant', 'stop_sign', - 'parking_meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', - 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', - 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', - 'sports_ball', 'kite', 'baseball_bat', 'baseball_glove', 'skateboard', - 'surfboard', 'tennis_racket', 'bottle', 'wine_glass', 'cup', 'fork', - 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', - 'broccoli', 'carrot', 'hot_dog', 'pizza', 'donut', 'cake', 'chair', - 'couch', 'potted_plant', 'bed', 'dining_table', 'toilet', 'tv', - 'laptop', 'mouse', 'remote', 'keyboard', 'cell_phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', - 'scissors', 'teddy_bear', 'hair_drier', 'toothbrush' - ] - - -def cityscapes_classes(): - return [ - 'person', 'rider', 'car', 'truck', 'bus', 'train', 'motorcycle', - 'bicycle' - ] - - -def oid_challenge_classes(): - return [ - 'Footwear', 'Jeans', 'House', 'Tree', 'Woman', 'Man', 'Land vehicle', - 'Person', 'Wheel', 'Bus', 'Human face', 'Bird', 'Dress', 'Girl', - 'Vehicle', 'Building', 'Cat', 'Car', 'Belt', 'Elephant', 'Dessert', - 'Butterfly', 'Train', 'Guitar', 'Poster', 'Book', 'Boy', 'Bee', - 'Flower', 'Window', 'Hat', 'Human head', 'Dog', 'Human arm', 'Drink', - 'Human mouth', 'Human hair', 'Human nose', 'Human hand', 'Table', - 'Marine invertebrates', 'Fish', 'Sculpture', 'Rose', 'Street light', - 'Glasses', 'Fountain', 'Skyscraper', 'Swimwear', 'Brassiere', 'Drum', - 'Duck', 'Countertop', 'Furniture', 'Ball', 'Human leg', 'Boat', - 'Balloon', 'Bicycle helmet', 'Goggles', 'Door', 'Human eye', 'Shirt', - 'Toy', 'Teddy bear', 'Pasta', 'Tomato', 'Human ear', - 'Vehicle registration plate', 'Microphone', 'Musical keyboard', - 'Tower', 'Houseplant', 'Flowerpot', 'Fruit', 'Vegetable', - 'Musical instrument', 'Suit', 'Motorcycle', 'Bagel', 'French fries', - 'Hamburger', 'Chair', 'Salt and pepper shakers', 'Snail', 'Airplane', - 'Horse', 'Laptop', 'Computer keyboard', 'Football helmet', 'Cocktail', - 'Juice', 'Tie', 'Computer monitor', 'Human beard', 'Bottle', - 'Saxophone', 'Lemon', 'Mouse', 'Sock', 'Cowboy hat', 'Sun hat', - 'Football', 'Porch', 'Sunglasses', 'Lobster', 'Crab', 'Picture frame', - 'Van', 'Crocodile', 'Surfboard', 'Shorts', 'Helicopter', 'Helmet', - 'Sports uniform', 'Taxi', 'Swan', 'Goose', 'Coat', 'Jacket', 'Handbag', - 'Flag', 'Skateboard', 'Television', 'Tire', 'Spoon', 'Palm tree', - 'Stairs', 'Salad', 'Castle', 'Oven', 'Microwave oven', 'Wine', - 'Ceiling fan', 'Mechanical fan', 'Cattle', 'Truck', 'Box', 'Ambulance', - 'Desk', 'Wine glass', 'Reptile', 'Tank', 'Traffic light', 'Billboard', - 'Tent', 'Insect', 'Spider', 'Treadmill', 'Cupboard', 'Shelf', - 'Seat belt', 'Human foot', 'Bicycle', 'Bicycle wheel', 'Couch', - 'Bookcase', 'Fedora', 'Backpack', 'Bench', 'Oyster', - 'Moths and butterflies', 'Lavender', 'Waffle', 'Fork', 'Animal', - 'Accordion', 'Mobile phone', 'Plate', 'Coffee cup', 'Saucer', - 'Platter', 'Dagger', 'Knife', 'Bull', 'Tortoise', 'Sea turtle', 'Deer', - 'Weapon', 'Apple', 'Ski', 'Taco', 'Traffic sign', 'Beer', 'Necklace', - 'Sunflower', 'Piano', 'Organ', 'Harpsichord', 'Bed', 'Cabinetry', - 'Nightstand', 'Curtain', 'Chest of drawers', 'Drawer', 'Parrot', - 'Sandal', 'High heels', 'Tableware', 'Cart', 'Mushroom', 'Kite', - 'Missile', 'Seafood', 'Camera', 'Paper towel', 'Toilet paper', - 'Sombrero', 'Radish', 'Lighthouse', 'Segway', 'Pig', 'Watercraft', - 'Golf cart', 'studio couch', 'Dolphin', 'Whale', 'Earrings', 'Otter', - 'Sea lion', 'Whiteboard', 'Monkey', 'Gondola', 'Zebra', - 'Baseball glove', 'Scarf', 'Adhesive tape', 'Trousers', 'Scoreboard', - 'Lily', 'Carnivore', 'Power plugs and sockets', 'Office building', - 'Sandwich', 'Swimming pool', 'Headphones', 'Tin can', 'Crown', 'Doll', - 'Cake', 'Frog', 'Beetle', 'Ant', 'Gas stove', 'Canoe', 'Falcon', - 'Blue jay', 'Egg', 'Fire hydrant', 'Raccoon', 'Muffin', 'Wall clock', - 'Coffee', 'Mug', 'Tea', 'Bear', 'Waste container', 'Home appliance', - 'Candle', 'Lion', 'Mirror', 'Starfish', 'Marine mammal', 'Wheelchair', - 'Umbrella', 'Alpaca', 'Violin', 'Cello', 'Brown bear', 'Canary', 'Bat', - 'Ruler', 'Plastic bag', 'Penguin', 'Watermelon', 'Harbor seal', 'Pen', - 'Pumpkin', 'Harp', 'Kitchen appliance', 'Roller skates', 'Bust', - 'Coffee table', 'Tennis ball', 'Tennis racket', 'Ladder', 'Boot', - 'Bowl', 'Stop sign', 'Volleyball', 'Eagle', 'Paddle', 'Chicken', - 'Skull', 'Lamp', 'Beehive', 'Maple', 'Sink', 'Goldfish', 'Tripod', - 'Coconut', 'Bidet', 'Tap', 'Bathroom cabinet', 'Toilet', - 'Filing cabinet', 'Pretzel', 'Table tennis racket', 'Bronze sculpture', - 'Rocket', 'Mouse', 'Hamster', 'Lizard', 'Lifejacket', 'Goat', - 'Washing machine', 'Trumpet', 'Horn', 'Trombone', 'Sheep', - 'Tablet computer', 'Pillow', 'Kitchen & dining room table', - 'Parachute', 'Raven', 'Glove', 'Loveseat', 'Christmas tree', - 'Shellfish', 'Rifle', 'Shotgun', 'Sushi', 'Sparrow', 'Bread', - 'Toaster', 'Watch', 'Asparagus', 'Artichoke', 'Suitcase', 'Antelope', - 'Broccoli', 'Ice cream', 'Racket', 'Banana', 'Cookie', 'Cucumber', - 'Dragonfly', 'Lynx', 'Caterpillar', 'Light bulb', 'Office supplies', - 'Miniskirt', 'Skirt', 'Fireplace', 'Potato', 'Light switch', - 'Croissant', 'Cabbage', 'Ladybug', 'Handgun', 'Luggage and bags', - 'Window blind', 'Snowboard', 'Baseball bat', 'Digital clock', - 'Serving tray', 'Infant bed', 'Sofa bed', 'Guacamole', 'Fox', 'Pizza', - 'Snowplow', 'Jet ski', 'Refrigerator', 'Lantern', 'Convenience store', - 'Sword', 'Rugby ball', 'Owl', 'Ostrich', 'Pancake', 'Strawberry', - 'Carrot', 'Tart', 'Dice', 'Turkey', 'Rabbit', 'Invertebrate', 'Vase', - 'Stool', 'Swim cap', 'Shower', 'Clock', 'Jellyfish', 'Aircraft', - 'Chopsticks', 'Orange', 'Snake', 'Sewing machine', 'Kangaroo', 'Mixer', - 'Food processor', 'Shrimp', 'Towel', 'Porcupine', 'Jaguar', 'Cannon', - 'Limousine', 'Mule', 'Squirrel', 'Kitchen knife', 'Tiara', 'Tiger', - 'Bow and arrow', 'Candy', 'Rhinoceros', 'Shark', 'Cricket ball', - 'Doughnut', 'Plumbing fixture', 'Camel', 'Polar bear', 'Coin', - 'Printer', 'Blender', 'Giraffe', 'Billiard table', 'Kettle', - 'Dinosaur', 'Pineapple', 'Zucchini', 'Jug', 'Barge', 'Teapot', - 'Golf ball', 'Binoculars', 'Scissors', 'Hot dog', 'Door handle', - 'Seahorse', 'Bathtub', 'Leopard', 'Centipede', 'Grapefruit', 'Snowman', - 'Cheetah', 'Alarm clock', 'Grape', 'Wrench', 'Wok', 'Bell pepper', - 'Cake stand', 'Barrel', 'Woodpecker', 'Flute', 'Corded phone', - 'Willow', 'Punching bag', 'Pomegranate', 'Telephone', 'Pear', - 'Common fig', 'Bench', 'Wood-burning stove', 'Burrito', 'Nail', - 'Turtle', 'Submarine sandwich', 'Drinking straw', 'Peach', 'Popcorn', - 'Frying pan', 'Picnic basket', 'Honeycomb', 'Envelope', 'Mango', - 'Cutting board', 'Pitcher', 'Stationary bicycle', 'Dumbbell', - 'Personal care', 'Dog bed', 'Snowmobile', 'Oboe', 'Briefcase', - 'Squash', 'Tick', 'Slow cooker', 'Coffeemaker', 'Measuring cup', - 'Crutch', 'Stretcher', 'Screwdriver', 'Flashlight', 'Spatula', - 'Pressure cooker', 'Ring binder', 'Beaker', 'Torch', 'Winter melon' - ] - - -def oid_v6_classes(): - return [ - 'Tortoise', 'Container', 'Magpie', 'Sea turtle', 'Football', - 'Ambulance', 'Ladder', 'Toothbrush', 'Syringe', 'Sink', 'Toy', - 'Organ (Musical Instrument)', 'Cassette deck', 'Apple', 'Human eye', - 'Cosmetics', 'Paddle', 'Snowman', 'Beer', 'Chopsticks', 'Human beard', - 'Bird', 'Parking meter', 'Traffic light', 'Croissant', 'Cucumber', - 'Radish', 'Towel', 'Doll', 'Skull', 'Washing machine', 'Glove', 'Tick', - 'Belt', 'Sunglasses', 'Banjo', 'Cart', 'Ball', 'Backpack', 'Bicycle', - 'Home appliance', 'Centipede', 'Boat', 'Surfboard', 'Boot', - 'Headphones', 'Hot dog', 'Shorts', 'Fast food', 'Bus', 'Boy', - 'Screwdriver', 'Bicycle wheel', 'Barge', 'Laptop', 'Miniskirt', - 'Drill (Tool)', 'Dress', 'Bear', 'Waffle', 'Pancake', 'Brown bear', - 'Woodpecker', 'Blue jay', 'Pretzel', 'Bagel', 'Tower', 'Teapot', - 'Person', 'Bow and arrow', 'Swimwear', 'Beehive', 'Brassiere', 'Bee', - 'Bat (Animal)', 'Starfish', 'Popcorn', 'Burrito', 'Chainsaw', - 'Balloon', 'Wrench', 'Tent', 'Vehicle registration plate', 'Lantern', - 'Toaster', 'Flashlight', 'Billboard', 'Tiara', 'Limousine', 'Necklace', - 'Carnivore', 'Scissors', 'Stairs', 'Computer keyboard', 'Printer', - 'Traffic sign', 'Chair', 'Shirt', 'Poster', 'Cheese', 'Sock', - 'Fire hydrant', 'Land vehicle', 'Earrings', 'Tie', 'Watercraft', - 'Cabinetry', 'Suitcase', 'Muffin', 'Bidet', 'Snack', 'Snowmobile', - 'Clock', 'Medical equipment', 'Cattle', 'Cello', 'Jet ski', 'Camel', - 'Coat', 'Suit', 'Desk', 'Cat', 'Bronze sculpture', 'Juice', 'Gondola', - 'Beetle', 'Cannon', 'Computer mouse', 'Cookie', 'Office building', - 'Fountain', 'Coin', 'Calculator', 'Cocktail', 'Computer monitor', - 'Box', 'Stapler', 'Christmas tree', 'Cowboy hat', 'Hiking equipment', - 'Studio couch', 'Drum', 'Dessert', 'Wine rack', 'Drink', 'Zucchini', - 'Ladle', 'Human mouth', 'Dairy Product', 'Dice', 'Oven', 'Dinosaur', - 'Ratchet (Device)', 'Couch', 'Cricket ball', 'Winter melon', 'Spatula', - 'Whiteboard', 'Pencil sharpener', 'Door', 'Hat', 'Shower', 'Eraser', - 'Fedora', 'Guacamole', 'Dagger', 'Scarf', 'Dolphin', 'Sombrero', - 'Tin can', 'Mug', 'Tap', 'Harbor seal', 'Stretcher', 'Can opener', - 'Goggles', 'Human body', 'Roller skates', 'Coffee cup', - 'Cutting board', 'Blender', 'Plumbing fixture', 'Stop sign', - 'Office supplies', 'Volleyball (Ball)', 'Vase', 'Slow cooker', - 'Wardrobe', 'Coffee', 'Whisk', 'Paper towel', 'Personal care', 'Food', - 'Sun hat', 'Tree house', 'Flying disc', 'Skirt', 'Gas stove', - 'Salt and pepper shakers', 'Mechanical fan', 'Face powder', 'Fax', - 'Fruit', 'French fries', 'Nightstand', 'Barrel', 'Kite', 'Tart', - 'Treadmill', 'Fox', 'Flag', 'French horn', 'Window blind', - 'Human foot', 'Golf cart', 'Jacket', 'Egg (Food)', 'Street light', - 'Guitar', 'Pillow', 'Human leg', 'Isopod', 'Grape', 'Human ear', - 'Power plugs and sockets', 'Panda', 'Giraffe', 'Woman', 'Door handle', - 'Rhinoceros', 'Bathtub', 'Goldfish', 'Houseplant', 'Goat', - 'Baseball bat', 'Baseball glove', 'Mixing bowl', - 'Marine invertebrates', 'Kitchen utensil', 'Light switch', 'House', - 'Horse', 'Stationary bicycle', 'Hammer', 'Ceiling fan', 'Sofa bed', - 'Adhesive tape', 'Harp', 'Sandal', 'Bicycle helmet', 'Saucer', - 'Harpsichord', 'Human hair', 'Heater', 'Harmonica', 'Hamster', - 'Curtain', 'Bed', 'Kettle', 'Fireplace', 'Scale', 'Drinking straw', - 'Insect', 'Hair dryer', 'Kitchenware', 'Indoor rower', 'Invertebrate', - 'Food processor', 'Bookcase', 'Refrigerator', 'Wood-burning stove', - 'Punching bag', 'Common fig', 'Cocktail shaker', 'Jaguar (Animal)', - 'Golf ball', 'Fashion accessory', 'Alarm clock', 'Filing cabinet', - 'Artichoke', 'Table', 'Tableware', 'Kangaroo', 'Koala', 'Knife', - 'Bottle', 'Bottle opener', 'Lynx', 'Lavender (Plant)', 'Lighthouse', - 'Dumbbell', 'Human head', 'Bowl', 'Humidifier', 'Porch', 'Lizard', - 'Billiard table', 'Mammal', 'Mouse', 'Motorcycle', - 'Musical instrument', 'Swim cap', 'Frying pan', 'Snowplow', - 'Bathroom cabinet', 'Missile', 'Bust', 'Man', 'Waffle iron', 'Milk', - 'Ring binder', 'Plate', 'Mobile phone', 'Baked goods', 'Mushroom', - 'Crutch', 'Pitcher (Container)', 'Mirror', 'Personal flotation device', - 'Table tennis racket', 'Pencil case', 'Musical keyboard', 'Scoreboard', - 'Briefcase', 'Kitchen knife', 'Nail (Construction)', 'Tennis ball', - 'Plastic bag', 'Oboe', 'Chest of drawers', 'Ostrich', 'Piano', 'Girl', - 'Plant', 'Potato', 'Hair spray', 'Sports equipment', 'Pasta', - 'Penguin', 'Pumpkin', 'Pear', 'Infant bed', 'Polar bear', 'Mixer', - 'Cupboard', 'Jacuzzi', 'Pizza', 'Digital clock', 'Pig', 'Reptile', - 'Rifle', 'Lipstick', 'Skateboard', 'Raven', 'High heels', 'Red panda', - 'Rose', 'Rabbit', 'Sculpture', 'Saxophone', 'Shotgun', 'Seafood', - 'Submarine sandwich', 'Snowboard', 'Sword', 'Picture frame', 'Sushi', - 'Loveseat', 'Ski', 'Squirrel', 'Tripod', 'Stethoscope', 'Submarine', - 'Scorpion', 'Segway', 'Training bench', 'Snake', 'Coffee table', - 'Skyscraper', 'Sheep', 'Television', 'Trombone', 'Tea', 'Tank', 'Taco', - 'Telephone', 'Torch', 'Tiger', 'Strawberry', 'Trumpet', 'Tree', - 'Tomato', 'Train', 'Tool', 'Picnic basket', 'Cooking spray', - 'Trousers', 'Bowling equipment', 'Football helmet', 'Truck', - 'Measuring cup', 'Coffeemaker', 'Violin', 'Vehicle', 'Handbag', - 'Paper cutter', 'Wine', 'Weapon', 'Wheel', 'Worm', 'Wok', 'Whale', - 'Zebra', 'Auto part', 'Jug', 'Pizza cutter', 'Cream', 'Monkey', 'Lion', - 'Bread', 'Platter', 'Chicken', 'Eagle', 'Helicopter', 'Owl', 'Duck', - 'Turtle', 'Hippopotamus', 'Crocodile', 'Toilet', 'Toilet paper', - 'Squid', 'Clothing', 'Footwear', 'Lemon', 'Spider', 'Deer', 'Frog', - 'Banana', 'Rocket', 'Wine glass', 'Countertop', 'Tablet computer', - 'Waste container', 'Swimming pool', 'Dog', 'Book', 'Elephant', 'Shark', - 'Candle', 'Leopard', 'Axe', 'Hand dryer', 'Soap dispenser', - 'Porcupine', 'Flower', 'Canary', 'Cheetah', 'Palm tree', 'Hamburger', - 'Maple', 'Building', 'Fish', 'Lobster', 'Garden Asparagus', - 'Furniture', 'Hedgehog', 'Airplane', 'Spoon', 'Otter', 'Bull', - 'Oyster', 'Horizontal bar', 'Convenience store', 'Bomb', 'Bench', - 'Ice cream', 'Caterpillar', 'Butterfly', 'Parachute', 'Orange', - 'Antelope', 'Beaker', 'Moths and butterflies', 'Window', 'Closet', - 'Castle', 'Jellyfish', 'Goose', 'Mule', 'Swan', 'Peach', 'Coconut', - 'Seat belt', 'Raccoon', 'Chisel', 'Fork', 'Lamp', 'Camera', - 'Squash (Plant)', 'Racket', 'Human face', 'Human arm', 'Vegetable', - 'Diaper', 'Unicycle', 'Falcon', 'Chime', 'Snail', 'Shellfish', - 'Cabbage', 'Carrot', 'Mango', 'Jeans', 'Flowerpot', 'Pineapple', - 'Drawer', 'Stool', 'Envelope', 'Cake', 'Dragonfly', 'Common sunflower', - 'Microwave oven', 'Honeycomb', 'Marine mammal', 'Sea lion', 'Ladybug', - 'Shelf', 'Watch', 'Candy', 'Salad', 'Parrot', 'Handgun', 'Sparrow', - 'Van', 'Grinder', 'Spice rack', 'Light bulb', 'Corded phone', - 'Sports uniform', 'Tennis racket', 'Wall clock', 'Serving tray', - 'Kitchen & dining room table', 'Dog bed', 'Cake stand', - 'Cat furniture', 'Bathroom accessory', 'Facial tissue holder', - 'Pressure cooker', 'Kitchen appliance', 'Tire', 'Ruler', - 'Luggage and bags', 'Microphone', 'Broccoli', 'Umbrella', 'Pastry', - 'Grapefruit', 'Band-aid', 'Animal', 'Bell pepper', 'Turkey', 'Lily', - 'Pomegranate', 'Doughnut', 'Glasses', 'Human nose', 'Pen', 'Ant', - 'Car', 'Aircraft', 'Human hand', 'Skunk', 'Teddy bear', 'Watermelon', - 'Cantaloupe', 'Dishwasher', 'Flute', 'Balance beam', 'Sandwich', - 'Shrimp', 'Sewing machine', 'Binoculars', 'Rays and skates', 'Ipod', - 'Accordion', 'Willow', 'Crab', 'Crown', 'Seahorse', 'Perfume', - 'Alpaca', 'Taxi', 'Canoe', 'Remote control', 'Wheelchair', - 'Rugby ball', 'Armadillo', 'Maracas', 'Helmet' - ] - - -dataset_aliases = { - 'voc': ['voc', 'pascal_voc', 'voc07', 'voc12'], - 'imagenet_det': ['det', 'imagenet_det', 'ilsvrc_det'], - 'imagenet_vid': ['vid', 'imagenet_vid', 'ilsvrc_vid'], - 'coco': ['coco', 'mscoco', 'ms_coco'], - 'wider_face': ['WIDERFaceDataset', 'wider_face', 'WIDERFace'], - 'cityscapes': ['cityscapes'], - 'oid_challenge': ['oid_challenge', 'openimages_challenge'], - 'oid_v6': ['oid_v6', 'openimages_v6'] -} - - -def get_classes(dataset): - """Get class names of a dataset.""" - alias2name = {} - for name, aliases in dataset_aliases.items(): - for alias in aliases: - alias2name[alias] = name - - if mmcv.is_str(dataset): - if dataset in alias2name: - labels = eval(alias2name[dataset] + '_classes()') - else: - raise ValueError(f'Unrecognized dataset: {dataset}') - else: - raise TypeError(f'dataset must a str, but got {type(dataset)}') - return labels diff --git a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/eval_hooks.py b/cv/detection/autoassign/pytorch/mmdet/core/evaluation/eval_hooks.py deleted file mode 100755 index 004589677..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/eval_hooks.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import bisect -import os.path as osp - -import mmcv -import torch.distributed as dist -from mmcv.runner import DistEvalHook as BaseDistEvalHook -from mmcv.runner import EvalHook as BaseEvalHook -from torch.nn.modules.batchnorm import _BatchNorm - - -def _calc_dynamic_intervals(start_interval, dynamic_interval_list): - assert mmcv.is_list_of(dynamic_interval_list, tuple) - - dynamic_milestones = [0] - dynamic_milestones.extend( - [dynamic_interval[0] for dynamic_interval in dynamic_interval_list]) - dynamic_intervals = [start_interval] - dynamic_intervals.extend( - [dynamic_interval[1] for dynamic_interval in dynamic_interval_list]) - return dynamic_milestones, dynamic_intervals - - -class EvalHook(BaseEvalHook): - - def __init__(self, *args, dynamic_intervals=None, **kwargs): - super(EvalHook, self).__init__(*args, **kwargs) - self.latest_results = None - - self.use_dynamic_intervals = dynamic_intervals is not None - if self.use_dynamic_intervals: - self.dynamic_milestones, self.dynamic_intervals = \ - _calc_dynamic_intervals(self.interval, dynamic_intervals) - - def _decide_interval(self, runner): - if self.use_dynamic_intervals: - progress = runner.epoch if self.by_epoch else runner.iter - step = bisect.bisect(self.dynamic_milestones, (progress + 1)) - # Dynamically modify the evaluation interval - self.interval = self.dynamic_intervals[step - 1] - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - self._decide_interval(runner) - super().before_train_epoch(runner) - - def before_train_iter(self, runner): - self._decide_interval(runner) - super().before_train_iter(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - if not self._should_evaluate(runner): - return - - from mmdet.apis import single_gpu_test - - # Changed results to self.results so that MMDetWandbHook can access - # the evaluation results and log them to wandb. - results = single_gpu_test(runner.model, self.dataloader, show=False) - self.latest_results = results - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - -# Note: Considering that MMCV's EvalHook updated its interface in V1.3.16, -# in order to avoid strong version dependency, we did not directly -# inherit EvalHook but BaseDistEvalHook. -class DistEvalHook(BaseDistEvalHook): - - def __init__(self, *args, dynamic_intervals=None, **kwargs): - super(DistEvalHook, self).__init__(*args, **kwargs) - self.latest_results = None - - self.use_dynamic_intervals = dynamic_intervals is not None - if self.use_dynamic_intervals: - self.dynamic_milestones, self.dynamic_intervals = \ - _calc_dynamic_intervals(self.interval, dynamic_intervals) - - def _decide_interval(self, runner): - if self.use_dynamic_intervals: - progress = runner.epoch if self.by_epoch else runner.iter - step = bisect.bisect(self.dynamic_milestones, (progress + 1)) - # Dynamically modify the evaluation interval - self.interval = self.dynamic_intervals[step - 1] - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - self._decide_interval(runner) - super().before_train_epoch(runner) - - def before_train_iter(self, runner): - self._decide_interval(runner) - super().before_train_iter(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - if not self._should_evaluate(runner): - return - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - from mmdet.apis import multi_gpu_test - - # Changed results to self.results so that MMDetWandbHook can access - # the evaluation results and log them to wandb. - results = multi_gpu_test( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - self.latest_results = results - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - - # the key_score may be `None` so it needs to skip - # the action to save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/mean_ap.py b/cv/detection/autoassign/pytorch/mmdet/core/evaluation/mean_ap.py deleted file mode 100755 index 7e35afad3..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/mean_ap.py +++ /dev/null @@ -1,782 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from multiprocessing import Pool - -import mmcv -import numpy as np -from mmcv.utils import print_log -from terminaltables import AsciiTable - -from .bbox_overlaps import bbox_overlaps -from .class_names import get_classes - - -def average_precision(recalls, precisions, mode='area'): - """Calculate average precision (for single or multiple scales). - - Args: - recalls (ndarray): shape (num_scales, num_dets) or (num_dets, ) - precisions (ndarray): shape (num_scales, num_dets) or (num_dets, ) - mode (str): 'area' or '11points', 'area' means calculating the area - under precision-recall curve, '11points' means calculating - the average precision of recalls at [0, 0.1, ..., 1] - - Returns: - float or ndarray: calculated average precision - """ - no_scale = False - if recalls.ndim == 1: - no_scale = True - recalls = recalls[np.newaxis, :] - precisions = precisions[np.newaxis, :] - assert recalls.shape == precisions.shape and recalls.ndim == 2 - num_scales = recalls.shape[0] - ap = np.zeros(num_scales, dtype=np.float32) - if mode == 'area': - zeros = np.zeros((num_scales, 1), dtype=recalls.dtype) - ones = np.ones((num_scales, 1), dtype=recalls.dtype) - mrec = np.hstack((zeros, recalls, ones)) - mpre = np.hstack((zeros, precisions, zeros)) - for i in range(mpre.shape[1] - 1, 0, -1): - mpre[:, i - 1] = np.maximum(mpre[:, i - 1], mpre[:, i]) - for i in range(num_scales): - ind = np.where(mrec[i, 1:] != mrec[i, :-1])[0] - ap[i] = np.sum( - (mrec[i, ind + 1] - mrec[i, ind]) * mpre[i, ind + 1]) - elif mode == '11points': - for i in range(num_scales): - for thr in np.arange(0, 1 + 1e-3, 0.1): - precs = precisions[i, recalls[i, :] >= thr] - prec = precs.max() if precs.size > 0 else 0 - ap[i] += prec - ap /= 11 - else: - raise ValueError( - 'Unrecognized mode, only "area" and "11points" are supported') - if no_scale: - ap = ap[0] - return ap - - -def tpfp_imagenet(det_bboxes, - gt_bboxes, - gt_bboxes_ignore=None, - default_iou_thr=0.5, - area_ranges=None, - use_legacy_coordinate=False, - **kwargs): - """Check if detected bboxes are true positive or false positive. - - Args: - det_bbox (ndarray): Detected bboxes of this image, of shape (m, 5). - gt_bboxes (ndarray): GT bboxes of this image, of shape (n, 4). - gt_bboxes_ignore (ndarray): Ignored gt bboxes of this image, - of shape (k, 4). Default: None - default_iou_thr (float): IoU threshold to be considered as matched for - medium and large bboxes (small ones have special rules). - Default: 0.5. - area_ranges (list[tuple] | None): Range of bbox areas to be evaluated, - in the format [(min1, max1), (min2, max2), ...]. Default: None. - use_legacy_coordinate (bool): Whether to use coordinate system in - mmdet v1.x. which means width, height should be - calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively. - Default: False. - - Returns: - tuple[np.ndarray]: (tp, fp) whose elements are 0 and 1. The shape of - each array is (num_scales, m). - """ - - if not use_legacy_coordinate: - extra_length = 0. - else: - extra_length = 1. - - # an indicator of ignored gts - gt_ignore_inds = np.concatenate( - (np.zeros(gt_bboxes.shape[0], dtype=np.bool), - np.ones(gt_bboxes_ignore.shape[0], dtype=np.bool))) - # stack gt_bboxes and gt_bboxes_ignore for convenience - gt_bboxes = np.vstack((gt_bboxes, gt_bboxes_ignore)) - - num_dets = det_bboxes.shape[0] - num_gts = gt_bboxes.shape[0] - if area_ranges is None: - area_ranges = [(None, None)] - num_scales = len(area_ranges) - # tp and fp are of shape (num_scales, num_gts), each row is tp or fp - # of a certain scale. - tp = np.zeros((num_scales, num_dets), dtype=np.float32) - fp = np.zeros((num_scales, num_dets), dtype=np.float32) - if gt_bboxes.shape[0] == 0: - if area_ranges == [(None, None)]: - fp[...] = 1 - else: - det_areas = ( - det_bboxes[:, 2] - det_bboxes[:, 0] + extra_length) * ( - det_bboxes[:, 3] - det_bboxes[:, 1] + extra_length) - for i, (min_area, max_area) in enumerate(area_ranges): - fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1 - return tp, fp - ious = bbox_overlaps( - det_bboxes, gt_bboxes - 1, use_legacy_coordinate=use_legacy_coordinate) - gt_w = gt_bboxes[:, 2] - gt_bboxes[:, 0] + extra_length - gt_h = gt_bboxes[:, 3] - gt_bboxes[:, 1] + extra_length - iou_thrs = np.minimum((gt_w * gt_h) / ((gt_w + 10.0) * (gt_h + 10.0)), - default_iou_thr) - # sort all detections by scores in descending order - sort_inds = np.argsort(-det_bboxes[:, -1]) - for k, (min_area, max_area) in enumerate(area_ranges): - gt_covered = np.zeros(num_gts, dtype=bool) - # if no area range is specified, gt_area_ignore is all False - if min_area is None: - gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool) - else: - gt_areas = gt_w * gt_h - gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area) - for i in sort_inds: - max_iou = -1 - matched_gt = -1 - # find best overlapped available gt - for j in range(num_gts): - # different from PASCAL VOC: allow finding other gts if the - # best overlapped ones are already matched by other det bboxes - if gt_covered[j]: - continue - elif ious[i, j] >= iou_thrs[j] and ious[i, j] > max_iou: - max_iou = ious[i, j] - matched_gt = j - # there are 4 cases for a det bbox: - # 1. it matches a gt, tp = 1, fp = 0 - # 2. it matches an ignored gt, tp = 0, fp = 0 - # 3. it matches no gt and within area range, tp = 0, fp = 1 - # 4. it matches no gt but is beyond area range, tp = 0, fp = 0 - if matched_gt >= 0: - gt_covered[matched_gt] = 1 - if not (gt_ignore_inds[matched_gt] - or gt_area_ignore[matched_gt]): - tp[k, i] = 1 - elif min_area is None: - fp[k, i] = 1 - else: - bbox = det_bboxes[i, :4] - area = (bbox[2] - bbox[0] + extra_length) * ( - bbox[3] - bbox[1] + extra_length) - if area >= min_area and area < max_area: - fp[k, i] = 1 - return tp, fp - - -def tpfp_default(det_bboxes, - gt_bboxes, - gt_bboxes_ignore=None, - iou_thr=0.5, - area_ranges=None, - use_legacy_coordinate=False, - **kwargs): - """Check if detected bboxes are true positive or false positive. - - Args: - det_bbox (ndarray): Detected bboxes of this image, of shape (m, 5). - gt_bboxes (ndarray): GT bboxes of this image, of shape (n, 4). - gt_bboxes_ignore (ndarray): Ignored gt bboxes of this image, - of shape (k, 4). Default: None - iou_thr (float): IoU threshold to be considered as matched. - Default: 0.5. - area_ranges (list[tuple] | None): Range of bbox areas to be - evaluated, in the format [(min1, max1), (min2, max2), ...]. - Default: None. - use_legacy_coordinate (bool): Whether to use coordinate system in - mmdet v1.x. which means width, height should be - calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively. - Default: False. - - Returns: - tuple[np.ndarray]: (tp, fp) whose elements are 0 and 1. The shape of - each array is (num_scales, m). - """ - - if not use_legacy_coordinate: - extra_length = 0. - else: - extra_length = 1. - - # an indicator of ignored gts - gt_ignore_inds = np.concatenate( - (np.zeros(gt_bboxes.shape[0], dtype=np.bool), - np.ones(gt_bboxes_ignore.shape[0], dtype=np.bool))) - # stack gt_bboxes and gt_bboxes_ignore for convenience - gt_bboxes = np.vstack((gt_bboxes, gt_bboxes_ignore)) - - num_dets = det_bboxes.shape[0] - num_gts = gt_bboxes.shape[0] - if area_ranges is None: - area_ranges = [(None, None)] - num_scales = len(area_ranges) - # tp and fp are of shape (num_scales, num_gts), each row is tp or fp of - # a certain scale - tp = np.zeros((num_scales, num_dets), dtype=np.float32) - fp = np.zeros((num_scales, num_dets), dtype=np.float32) - - # if there is no gt bboxes in this image, then all det bboxes - # within area range are false positives - if gt_bboxes.shape[0] == 0: - if area_ranges == [(None, None)]: - fp[...] = 1 - else: - det_areas = ( - det_bboxes[:, 2] - det_bboxes[:, 0] + extra_length) * ( - det_bboxes[:, 3] - det_bboxes[:, 1] + extra_length) - for i, (min_area, max_area) in enumerate(area_ranges): - fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1 - return tp, fp - - ious = bbox_overlaps( - det_bboxes, gt_bboxes, use_legacy_coordinate=use_legacy_coordinate) - # for each det, the max iou with all gts - ious_max = ious.max(axis=1) - # for each det, which gt overlaps most with it - ious_argmax = ious.argmax(axis=1) - # sort all dets in descending order by scores - sort_inds = np.argsort(-det_bboxes[:, -1]) - for k, (min_area, max_area) in enumerate(area_ranges): - gt_covered = np.zeros(num_gts, dtype=bool) - # if no area range is specified, gt_area_ignore is all False - if min_area is None: - gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool) - else: - gt_areas = (gt_bboxes[:, 2] - gt_bboxes[:, 0] + extra_length) * ( - gt_bboxes[:, 3] - gt_bboxes[:, 1] + extra_length) - gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area) - for i in sort_inds: - if ious_max[i] >= iou_thr: - matched_gt = ious_argmax[i] - if not (gt_ignore_inds[matched_gt] - or gt_area_ignore[matched_gt]): - if not gt_covered[matched_gt]: - gt_covered[matched_gt] = True - tp[k, i] = 1 - else: - fp[k, i] = 1 - # otherwise ignore this detected bbox, tp = 0, fp = 0 - elif min_area is None: - fp[k, i] = 1 - else: - bbox = det_bboxes[i, :4] - area = (bbox[2] - bbox[0] + extra_length) * ( - bbox[3] - bbox[1] + extra_length) - if area >= min_area and area < max_area: - fp[k, i] = 1 - return tp, fp - - -def tpfp_openimages(det_bboxes, - gt_bboxes, - gt_bboxes_ignore=None, - iou_thr=0.5, - area_ranges=None, - use_legacy_coordinate=False, - gt_bboxes_group_of=None, - use_group_of=True, - ioa_thr=0.5, - **kwargs): - """Check if detected bboxes are true positive or false positive. - - Args: - det_bbox (ndarray): Detected bboxes of this image, of shape (m, 5). - gt_bboxes (ndarray): GT bboxes of this image, of shape (n, 4). - gt_bboxes_ignore (ndarray): Ignored gt bboxes of this image, - of shape (k, 4). Default: None - iou_thr (float): IoU threshold to be considered as matched. - Default: 0.5. - area_ranges (list[tuple] | None): Range of bbox areas to be - evaluated, in the format [(min1, max1), (min2, max2), ...]. - Default: None. - use_legacy_coordinate (bool): Whether to use coordinate system in - mmdet v1.x. which means width, height should be - calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively. - Default: False. - gt_bboxes_group_of (ndarray): GT group_of of this image, of shape - (k, 1). Default: None - use_group_of (bool): Whether to use group of when calculate TP and FP, - which only used in OpenImages evaluation. Default: True. - ioa_thr (float | None): IoA threshold to be considered as matched, - which only used in OpenImages evaluation. Default: 0.5. - - Returns: - tuple[np.ndarray]: Returns a tuple (tp, fp, det_bboxes), where - (tp, fp) whose elements are 0 and 1. The shape of each array is - (num_scales, m). (det_bboxes) whose will filter those are not - matched by group of gts when processing Open Images evaluation. - The shape is (num_scales, m). - """ - - if not use_legacy_coordinate: - extra_length = 0. - else: - extra_length = 1. - - # an indicator of ignored gts - gt_ignore_inds = np.concatenate( - (np.zeros(gt_bboxes.shape[0], dtype=np.bool), - np.ones(gt_bboxes_ignore.shape[0], dtype=np.bool))) - # stack gt_bboxes and gt_bboxes_ignore for convenience - gt_bboxes = np.vstack((gt_bboxes, gt_bboxes_ignore)) - - num_dets = det_bboxes.shape[0] - num_gts = gt_bboxes.shape[0] - if area_ranges is None: - area_ranges = [(None, None)] - num_scales = len(area_ranges) - # tp and fp are of shape (num_scales, num_gts), each row is tp or fp of - # a certain scale - tp = np.zeros((num_scales, num_dets), dtype=np.float32) - fp = np.zeros((num_scales, num_dets), dtype=np.float32) - - # if there is no gt bboxes in this image, then all det bboxes - # within area range are false positives - if gt_bboxes.shape[0] == 0: - if area_ranges == [(None, None)]: - fp[...] = 1 - else: - det_areas = ( - det_bboxes[:, 2] - det_bboxes[:, 0] + extra_length) * ( - det_bboxes[:, 3] - det_bboxes[:, 1] + extra_length) - for i, (min_area, max_area) in enumerate(area_ranges): - fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1 - return tp, fp, det_bboxes - - if gt_bboxes_group_of is not None and use_group_of: - # if handle group-of boxes, divided gt boxes into two parts: - # non-group-of and group-of.Then calculate ious and ioas through - # non-group-of group-of gts respectively. This only used in - # OpenImages evaluation. - assert gt_bboxes_group_of.shape[0] == gt_bboxes.shape[0] - non_group_gt_bboxes = gt_bboxes[~gt_bboxes_group_of] - group_gt_bboxes = gt_bboxes[gt_bboxes_group_of] - num_gts_group = group_gt_bboxes.shape[0] - ious = bbox_overlaps(det_bboxes, non_group_gt_bboxes) - ioas = bbox_overlaps(det_bboxes, group_gt_bboxes, mode='iof') - else: - # if not consider group-of boxes, only calculate ious through gt boxes - ious = bbox_overlaps( - det_bboxes, gt_bboxes, use_legacy_coordinate=use_legacy_coordinate) - ioas = None - - if ious.shape[1] > 0: - # for each det, the max iou with all gts - ious_max = ious.max(axis=1) - # for each det, which gt overlaps most with it - ious_argmax = ious.argmax(axis=1) - # sort all dets in descending order by scores - sort_inds = np.argsort(-det_bboxes[:, -1]) - for k, (min_area, max_area) in enumerate(area_ranges): - gt_covered = np.zeros(num_gts, dtype=bool) - # if no area range is specified, gt_area_ignore is all False - if min_area is None: - gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool) - else: - gt_areas = ( - gt_bboxes[:, 2] - gt_bboxes[:, 0] + extra_length) * ( - gt_bboxes[:, 3] - gt_bboxes[:, 1] + extra_length) - gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area) - for i in sort_inds: - if ious_max[i] >= iou_thr: - matched_gt = ious_argmax[i] - if not (gt_ignore_inds[matched_gt] - or gt_area_ignore[matched_gt]): - if not gt_covered[matched_gt]: - gt_covered[matched_gt] = True - tp[k, i] = 1 - else: - fp[k, i] = 1 - # otherwise ignore this detected bbox, tp = 0, fp = 0 - elif min_area is None: - fp[k, i] = 1 - else: - bbox = det_bboxes[i, :4] - area = (bbox[2] - bbox[0] + extra_length) * ( - bbox[3] - bbox[1] + extra_length) - if area >= min_area and area < max_area: - fp[k, i] = 1 - else: - # if there is no no-group-of gt bboxes in this image, - # then all det bboxes within area range are false positives. - # Only used in OpenImages evaluation. - if area_ranges == [(None, None)]: - fp[...] = 1 - else: - det_areas = ( - det_bboxes[:, 2] - det_bboxes[:, 0] + extra_length) * ( - det_bboxes[:, 3] - det_bboxes[:, 1] + extra_length) - for i, (min_area, max_area) in enumerate(area_ranges): - fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1 - - if ioas is None or ioas.shape[1] <= 0: - return tp, fp, det_bboxes - else: - # The evaluation of group-of TP and FP are done in two stages: - # 1. All detections are first matched to non group-of boxes; true - # positives are determined. - # 2. Detections that are determined as false positives are matched - # against group-of boxes and calculated group-of TP and FP. - # Only used in OpenImages evaluation. - det_bboxes_group = np.zeros( - (num_scales, ioas.shape[1], det_bboxes.shape[1]), dtype=float) - match_group_of = np.zeros((num_scales, num_dets), dtype=bool) - tp_group = np.zeros((num_scales, num_gts_group), dtype=np.float32) - ioas_max = ioas.max(axis=1) - # for each det, which gt overlaps most with it - ioas_argmax = ioas.argmax(axis=1) - # sort all dets in descending order by scores - sort_inds = np.argsort(-det_bboxes[:, -1]) - for k, (min_area, max_area) in enumerate(area_ranges): - box_is_covered = tp[k] - # if no area range is specified, gt_area_ignore is all False - if min_area is None: - gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool) - else: - gt_areas = (gt_bboxes[:, 2] - gt_bboxes[:, 0]) * ( - gt_bboxes[:, 3] - gt_bboxes[:, 1]) - gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area) - for i in sort_inds: - matched_gt = ioas_argmax[i] - if not box_is_covered[i]: - if ioas_max[i] >= ioa_thr: - if not (gt_ignore_inds[matched_gt] - or gt_area_ignore[matched_gt]): - if not tp_group[k, matched_gt]: - tp_group[k, matched_gt] = 1 - match_group_of[k, i] = True - else: - match_group_of[k, i] = True - - if det_bboxes_group[k, matched_gt, -1] < \ - det_bboxes[i, -1]: - det_bboxes_group[k, matched_gt] = \ - det_bboxes[i] - - fp_group = (tp_group <= 0).astype(float) - tps = [] - fps = [] - # concatenate tp, fp, and det-boxes which not matched group of - # gt boxes and tp_group, fp_group, and det_bboxes_group which - # matched group of boxes respectively. - for i in range(num_scales): - tps.append( - np.concatenate((tp[i][~match_group_of[i]], tp_group[i]))) - fps.append( - np.concatenate((fp[i][~match_group_of[i]], fp_group[i]))) - det_bboxes = np.concatenate( - (det_bboxes[~match_group_of[i]], det_bboxes_group[i])) - - tp = np.vstack(tps) - fp = np.vstack(fps) - return tp, fp, det_bboxes - - -def get_cls_results(det_results, annotations, class_id): - """Get det results and gt information of a certain class. - - Args: - det_results (list[list]): Same as `eval_map()`. - annotations (list[dict]): Same as `eval_map()`. - class_id (int): ID of a specific class. - - Returns: - tuple[list[np.ndarray]]: detected bboxes, gt bboxes, ignored gt bboxes - """ - cls_dets = [img_res[class_id] for img_res in det_results] - cls_gts = [] - cls_gts_ignore = [] - for ann in annotations: - gt_inds = ann['labels'] == class_id - cls_gts.append(ann['bboxes'][gt_inds, :]) - - if ann.get('labels_ignore', None) is not None: - ignore_inds = ann['labels_ignore'] == class_id - cls_gts_ignore.append(ann['bboxes_ignore'][ignore_inds, :]) - else: - cls_gts_ignore.append(np.empty((0, 4), dtype=np.float32)) - - return cls_dets, cls_gts, cls_gts_ignore - - -def get_cls_group_ofs(annotations, class_id): - """Get `gt_group_of` of a certain class, which is used in Open Images. - - Args: - annotations (list[dict]): Same as `eval_map()`. - class_id (int): ID of a specific class. - - Returns: - list[np.ndarray]: `gt_group_of` of a certain class. - """ - gt_group_ofs = [] - for ann in annotations: - gt_inds = ann['labels'] == class_id - if ann.get('gt_is_group_ofs', None) is not None: - gt_group_ofs.append(ann['gt_is_group_ofs'][gt_inds]) - else: - gt_group_ofs.append(np.empty((0, 1), dtype=np.bool)) - - return gt_group_ofs - - -def eval_map(det_results, - annotations, - scale_ranges=None, - iou_thr=0.5, - ioa_thr=None, - dataset=None, - logger=None, - tpfp_fn=None, - nproc=4, - use_legacy_coordinate=False, - use_group_of=False): - """Evaluate mAP of a dataset. - - Args: - det_results (list[list]): [[cls1_det, cls2_det, ...], ...]. - The outer list indicates images, and the inner list indicates - per-class detected bboxes. - annotations (list[dict]): Ground truth annotations where each item of - the list indicates an image. Keys of annotations are: - - - `bboxes`: numpy array of shape (n, 4) - - `labels`: numpy array of shape (n, ) - - `bboxes_ignore` (optional): numpy array of shape (k, 4) - - `labels_ignore` (optional): numpy array of shape (k, ) - scale_ranges (list[tuple] | None): Range of scales to be evaluated, - in the format [(min1, max1), (min2, max2), ...]. A range of - (32, 64) means the area range between (32**2, 64**2). - Default: None. - iou_thr (float): IoU threshold to be considered as matched. - Default: 0.5. - ioa_thr (float | None): IoA threshold to be considered as matched, - which only used in OpenImages evaluation. Default: None. - dataset (list[str] | str | None): Dataset name or dataset classes, - there are minor differences in metrics for different datasets, e.g. - "voc07", "imagenet_det", etc. Default: None. - logger (logging.Logger | str | None): The way to print the mAP - summary. See `mmcv.utils.print_log()` for details. Default: None. - tpfp_fn (callable | None): The function used to determine true/ - false positives. If None, :func:`tpfp_default` is used as default - unless dataset is 'det' or 'vid' (:func:`tpfp_imagenet` in this - case). If it is given as a function, then this function is used - to evaluate tp & fp. Default None. - nproc (int): Processes used for computing TP and FP. - Default: 4. - use_legacy_coordinate (bool): Whether to use coordinate system in - mmdet v1.x. which means width, height should be - calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively. - Default: False. - use_group_of (bool): Whether to use group of when calculate TP and FP, - which only used in OpenImages evaluation. Default: False. - - Returns: - tuple: (mAP, [dict, dict, ...]) - """ - assert len(det_results) == len(annotations) - if not use_legacy_coordinate: - extra_length = 0. - else: - extra_length = 1. - - num_imgs = len(det_results) - num_scales = len(scale_ranges) if scale_ranges is not None else 1 - num_classes = len(det_results[0]) # positive class num - area_ranges = ([(rg[0]**2, rg[1]**2) for rg in scale_ranges] - if scale_ranges is not None else None) - - # There is no need to use multi processes to process - # when num_imgs = 1 . - if num_imgs > 1: - assert nproc > 0, 'nproc must be at least one.' - nproc = min(nproc, num_imgs) - pool = Pool(nproc) - - eval_results = [] - for i in range(num_classes): - # get gt and det bboxes of this class - cls_dets, cls_gts, cls_gts_ignore = get_cls_results( - det_results, annotations, i) - # choose proper function according to datasets to compute tp and fp - if tpfp_fn is None: - if dataset in ['det', 'vid']: - tpfp_fn = tpfp_imagenet - elif dataset in ['oid_challenge', 'oid_v6'] \ - or use_group_of is True: - tpfp_fn = tpfp_openimages - else: - tpfp_fn = tpfp_default - if not callable(tpfp_fn): - raise ValueError( - f'tpfp_fn has to be a function or None, but got {tpfp_fn}') - - if num_imgs > 1: - # compute tp and fp for each image with multiple processes - args = [] - if use_group_of: - # used in Open Images Dataset evaluation - gt_group_ofs = get_cls_group_ofs(annotations, i) - args.append(gt_group_ofs) - args.append([use_group_of for _ in range(num_imgs)]) - if ioa_thr is not None: - args.append([ioa_thr for _ in range(num_imgs)]) - - tpfp = pool.starmap( - tpfp_fn, - zip(cls_dets, cls_gts, cls_gts_ignore, - [iou_thr for _ in range(num_imgs)], - [area_ranges for _ in range(num_imgs)], - [use_legacy_coordinate for _ in range(num_imgs)], *args)) - else: - tpfp = tpfp_fn( - cls_dets[0], - cls_gts[0], - cls_gts_ignore[0], - iou_thr, - area_ranges, - use_legacy_coordinate, - gt_bboxes_group_of=(get_cls_group_ofs(annotations, i)[0] - if use_group_of else None), - use_group_of=use_group_of, - ioa_thr=ioa_thr) - tpfp = [tpfp] - - if use_group_of: - tp, fp, cls_dets = tuple(zip(*tpfp)) - else: - tp, fp = tuple(zip(*tpfp)) - # calculate gt number of each scale - # ignored gts or gts beyond the specific scale are not counted - num_gts = np.zeros(num_scales, dtype=int) - for j, bbox in enumerate(cls_gts): - if area_ranges is None: - num_gts[0] += bbox.shape[0] - else: - gt_areas = (bbox[:, 2] - bbox[:, 0] + extra_length) * ( - bbox[:, 3] - bbox[:, 1] + extra_length) - for k, (min_area, max_area) in enumerate(area_ranges): - num_gts[k] += np.sum((gt_areas >= min_area) - & (gt_areas < max_area)) - # sort all det bboxes by score, also sort tp and fp - cls_dets = np.vstack(cls_dets) - num_dets = cls_dets.shape[0] - sort_inds = np.argsort(-cls_dets[:, -1]) - tp = np.hstack(tp)[:, sort_inds] - fp = np.hstack(fp)[:, sort_inds] - # calculate recall and precision with tp and fp - tp = np.cumsum(tp, axis=1) - fp = np.cumsum(fp, axis=1) - eps = np.finfo(np.float32).eps - recalls = tp / np.maximum(num_gts[:, np.newaxis], eps) - precisions = tp / np.maximum((tp + fp), eps) - # calculate AP - if scale_ranges is None: - recalls = recalls[0, :] - precisions = precisions[0, :] - num_gts = num_gts.item() - mode = 'area' if dataset != 'voc07' else '11points' - ap = average_precision(recalls, precisions, mode) - eval_results.append({ - 'num_gts': num_gts, - 'num_dets': num_dets, - 'recall': recalls, - 'precision': precisions, - 'ap': ap - }) - - if num_imgs > 1: - pool.close() - - if scale_ranges is not None: - # shape (num_classes, num_scales) - all_ap = np.vstack([cls_result['ap'] for cls_result in eval_results]) - all_num_gts = np.vstack( - [cls_result['num_gts'] for cls_result in eval_results]) - mean_ap = [] - for i in range(num_scales): - if np.any(all_num_gts[:, i] > 0): - mean_ap.append(all_ap[all_num_gts[:, i] > 0, i].mean()) - else: - mean_ap.append(0.0) - else: - aps = [] - for cls_result in eval_results: - if cls_result['num_gts'] > 0: - aps.append(cls_result['ap']) - mean_ap = np.array(aps).mean().item() if aps else 0.0 - - print_map_summary( - mean_ap, eval_results, dataset, area_ranges, logger=logger) - - return mean_ap, eval_results - - -def print_map_summary(mean_ap, - results, - dataset=None, - scale_ranges=None, - logger=None): - """Print mAP and results of each class. - - A table will be printed to show the gts/dets/recall/AP of each class and - the mAP. - - Args: - mean_ap (float): Calculated from `eval_map()`. - results (list[dict]): Calculated from `eval_map()`. - dataset (list[str] | str | None): Dataset name or dataset classes. - scale_ranges (list[tuple] | None): Range of scales to be evaluated. - logger (logging.Logger | str | None): The way to print the mAP - summary. See `mmcv.utils.print_log()` for details. Default: None. - """ - - if logger == 'silent': - return - - if isinstance(results[0]['ap'], np.ndarray): - num_scales = len(results[0]['ap']) - else: - num_scales = 1 - - if scale_ranges is not None: - assert len(scale_ranges) == num_scales - - num_classes = len(results) - - recalls = np.zeros((num_scales, num_classes), dtype=np.float32) - aps = np.zeros((num_scales, num_classes), dtype=np.float32) - num_gts = np.zeros((num_scales, num_classes), dtype=int) - for i, cls_result in enumerate(results): - if cls_result['recall'].size > 0: - recalls[:, i] = np.array(cls_result['recall'], ndmin=2)[:, -1] - aps[:, i] = cls_result['ap'] - num_gts[:, i] = cls_result['num_gts'] - - if dataset is None: - label_names = [str(i) for i in range(num_classes)] - elif mmcv.is_str(dataset): - label_names = get_classes(dataset) - else: - label_names = dataset - - if not isinstance(mean_ap, list): - mean_ap = [mean_ap] - - header = ['class', 'gts', 'dets', 'recall', 'ap'] - for i in range(num_scales): - if scale_ranges is not None: - print_log(f'Scale range {scale_ranges[i]}', logger=logger) - table_data = [header] - for j in range(num_classes): - row_data = [ - label_names[j], num_gts[i, j], results[j]['num_dets'], - f'{recalls[i, j]:.3f}', f'{aps[i, j]:.3f}' - ] - table_data.append(row_data) - table_data.append(['mAP', '', '', '', f'{mean_ap[i]:.3f}']) - table = AsciiTable(table_data) - table.inner_footing_row_border = True - print_log('\n' + table.table, logger=logger) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/panoptic_utils.py b/cv/detection/autoassign/pytorch/mmdet/core/evaluation/panoptic_utils.py deleted file mode 100755 index abc941a4d..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/panoptic_utils.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# A custom value to distinguish instance ID and category ID; need to -# be greater than the number of categories. -# For a pixel in the panoptic result map: -# pan_id = ins_id * INSTANCE_OFFSET + cat_id -INSTANCE_OFFSET = 1000 diff --git a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/recall.py b/cv/detection/autoassign/pytorch/mmdet/core/evaluation/recall.py deleted file mode 100755 index 894f8059c..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/evaluation/recall.py +++ /dev/null @@ -1,197 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import numpy as np -from mmcv.utils import print_log -from terminaltables import AsciiTable - -from .bbox_overlaps import bbox_overlaps - - -def _recalls(all_ious, proposal_nums, thrs): - - img_num = all_ious.shape[0] - total_gt_num = sum([ious.shape[0] for ious in all_ious]) - - _ious = np.zeros((proposal_nums.size, total_gt_num), dtype=np.float32) - for k, proposal_num in enumerate(proposal_nums): - tmp_ious = np.zeros(0) - for i in range(img_num): - ious = all_ious[i][:, :proposal_num].copy() - gt_ious = np.zeros((ious.shape[0])) - if ious.size == 0: - tmp_ious = np.hstack((tmp_ious, gt_ious)) - continue - for j in range(ious.shape[0]): - gt_max_overlaps = ious.argmax(axis=1) - max_ious = ious[np.arange(0, ious.shape[0]), gt_max_overlaps] - gt_idx = max_ious.argmax() - gt_ious[j] = max_ious[gt_idx] - box_idx = gt_max_overlaps[gt_idx] - ious[gt_idx, :] = -1 - ious[:, box_idx] = -1 - tmp_ious = np.hstack((tmp_ious, gt_ious)) - _ious[k, :] = tmp_ious - - _ious = np.fliplr(np.sort(_ious, axis=1)) - recalls = np.zeros((proposal_nums.size, thrs.size)) - for i, thr in enumerate(thrs): - recalls[:, i] = (_ious >= thr).sum(axis=1) / float(total_gt_num) - - return recalls - - -def set_recall_param(proposal_nums, iou_thrs): - """Check proposal_nums and iou_thrs and set correct format.""" - if isinstance(proposal_nums, Sequence): - _proposal_nums = np.array(proposal_nums) - elif isinstance(proposal_nums, int): - _proposal_nums = np.array([proposal_nums]) - else: - _proposal_nums = proposal_nums - - if iou_thrs is None: - _iou_thrs = np.array([0.5]) - elif isinstance(iou_thrs, Sequence): - _iou_thrs = np.array(iou_thrs) - elif isinstance(iou_thrs, float): - _iou_thrs = np.array([iou_thrs]) - else: - _iou_thrs = iou_thrs - - return _proposal_nums, _iou_thrs - - -def eval_recalls(gts, - proposals, - proposal_nums=None, - iou_thrs=0.5, - logger=None, - use_legacy_coordinate=False): - """Calculate recalls. - - Args: - gts (list[ndarray]): a list of arrays of shape (n, 4) - proposals (list[ndarray]): a list of arrays of shape (k, 4) or (k, 5) - proposal_nums (int | Sequence[int]): Top N proposals to be evaluated. - iou_thrs (float | Sequence[float]): IoU thresholds. Default: 0.5. - logger (logging.Logger | str | None): The way to print the recall - summary. See `mmcv.utils.print_log()` for details. Default: None. - use_legacy_coordinate (bool): Whether use coordinate system - in mmdet v1.x. "1" was added to both height and width - which means w, h should be - computed as 'x2 - x1 + 1` and 'y2 - y1 + 1'. Default: False. - - - Returns: - ndarray: recalls of different ious and proposal nums - """ - - img_num = len(gts) - assert img_num == len(proposals) - proposal_nums, iou_thrs = set_recall_param(proposal_nums, iou_thrs) - all_ious = [] - for i in range(img_num): - if proposals[i].ndim == 2 and proposals[i].shape[1] == 5: - scores = proposals[i][:, 4] - sort_idx = np.argsort(scores)[::-1] - img_proposal = proposals[i][sort_idx, :] - else: - img_proposal = proposals[i] - prop_num = min(img_proposal.shape[0], proposal_nums[-1]) - if gts[i] is None or gts[i].shape[0] == 0: - ious = np.zeros((0, img_proposal.shape[0]), dtype=np.float32) - else: - ious = bbox_overlaps( - gts[i], - img_proposal[:prop_num, :4], - use_legacy_coordinate=use_legacy_coordinate) - all_ious.append(ious) - all_ious = np.array(all_ious) - recalls = _recalls(all_ious, proposal_nums, iou_thrs) - - print_recall_summary(recalls, proposal_nums, iou_thrs, logger=logger) - return recalls - - -def print_recall_summary(recalls, - proposal_nums, - iou_thrs, - row_idxs=None, - col_idxs=None, - logger=None): - """Print recalls in a table. - - Args: - recalls (ndarray): calculated from `bbox_recalls` - proposal_nums (ndarray or list): top N proposals - iou_thrs (ndarray or list): iou thresholds - row_idxs (ndarray): which rows(proposal nums) to print - col_idxs (ndarray): which cols(iou thresholds) to print - logger (logging.Logger | str | None): The way to print the recall - summary. See `mmcv.utils.print_log()` for details. Default: None. - """ - proposal_nums = np.array(proposal_nums, dtype=np.int32) - iou_thrs = np.array(iou_thrs) - if row_idxs is None: - row_idxs = np.arange(proposal_nums.size) - if col_idxs is None: - col_idxs = np.arange(iou_thrs.size) - row_header = [''] + iou_thrs[col_idxs].tolist() - table_data = [row_header] - for i, num in enumerate(proposal_nums[row_idxs]): - row = [f'{val:.3f}' for val in recalls[row_idxs[i], col_idxs].tolist()] - row.insert(0, num) - table_data.append(row) - table = AsciiTable(table_data) - print_log('\n' + table.table, logger=logger) - - -def plot_num_recall(recalls, proposal_nums): - """Plot Proposal_num-Recalls curve. - - Args: - recalls(ndarray or list): shape (k,) - proposal_nums(ndarray or list): same shape as `recalls` - """ - if isinstance(proposal_nums, np.ndarray): - _proposal_nums = proposal_nums.tolist() - else: - _proposal_nums = proposal_nums - if isinstance(recalls, np.ndarray): - _recalls = recalls.tolist() - else: - _recalls = recalls - - import matplotlib.pyplot as plt - f = plt.figure() - plt.plot([0] + _proposal_nums, [0] + _recalls) - plt.xlabel('Proposal num') - plt.ylabel('Recall') - plt.axis([0, proposal_nums.max(), 0, 1]) - f.show() - - -def plot_iou_recall(recalls, iou_thrs): - """Plot IoU-Recalls curve. - - Args: - recalls(ndarray or list): shape (k,) - iou_thrs(ndarray or list): same shape as `recalls` - """ - if isinstance(iou_thrs, np.ndarray): - _iou_thrs = iou_thrs.tolist() - else: - _iou_thrs = iou_thrs - if isinstance(recalls, np.ndarray): - _recalls = recalls.tolist() - else: - _recalls = recalls - - import matplotlib.pyplot as plt - f = plt.figure() - plt.plot(_iou_thrs + [1.0], _recalls + [0.]) - plt.xlabel('IoU') - plt.ylabel('Recall') - plt.axis([iou_thrs.min(), 1, 0, 1]) - f.show() diff --git a/cv/detection/autoassign/pytorch/mmdet/core/export/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/export/__init__.py deleted file mode 100755 index 4f986ccab..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/export/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .onnx_helper import (add_dummy_nms_for_onnx, dynamic_clip_for_onnx, - get_k_for_topk) -from .pytorch2onnx import (build_model_from_cfg, - generate_inputs_and_wrap_model, - preprocess_example_input) - -__all__ = [ - 'build_model_from_cfg', 'generate_inputs_and_wrap_model', - 'preprocess_example_input', 'get_k_for_topk', 'add_dummy_nms_for_onnx', - 'dynamic_clip_for_onnx' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/export/model_wrappers.py b/cv/detection/autoassign/pytorch/mmdet/core/export/model_wrappers.py deleted file mode 100755 index adcd9fa9a..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/export/model_wrappers.py +++ /dev/null @@ -1,183 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -import numpy as np -import torch - -from mmdet.core import bbox2result -from mmdet.models import BaseDetector - - -class DeployBaseDetector(BaseDetector): - """DeployBaseDetector.""" - - def __init__(self, class_names, device_id): - super(DeployBaseDetector, self).__init__() - self.CLASSES = class_names - self.device_id = device_id - - def simple_test(self, img, img_metas, **kwargs): - raise NotImplementedError('This method is not implemented.') - - def aug_test(self, imgs, img_metas, **kwargs): - raise NotImplementedError('This method is not implemented.') - - def extract_feat(self, imgs): - raise NotImplementedError('This method is not implemented.') - - def forward_train(self, imgs, img_metas, **kwargs): - raise NotImplementedError('This method is not implemented.') - - def val_step(self, data, optimizer): - raise NotImplementedError('This method is not implemented.') - - def train_step(self, data, optimizer): - raise NotImplementedError('This method is not implemented.') - - def forward_test(self, *, img, img_metas, **kwargs): - raise NotImplementedError('This method is not implemented.') - - def async_simple_test(self, img, img_metas, **kwargs): - raise NotImplementedError('This method is not implemented.') - - def forward(self, img, img_metas, return_loss=True, **kwargs): - outputs = self.forward_test(img, img_metas, **kwargs) - batch_dets, batch_labels = outputs[:2] - batch_masks = outputs[2] if len(outputs) == 3 else None - batch_size = img[0].shape[0] - img_metas = img_metas[0] - results = [] - rescale = kwargs.get('rescale', True) - for i in range(batch_size): - dets, labels = batch_dets[i], batch_labels[i] - if rescale: - scale_factor = img_metas[i]['scale_factor'] - - if isinstance(scale_factor, (list, tuple, np.ndarray)): - assert len(scale_factor) == 4 - scale_factor = np.array(scale_factor)[None, :] # [1,4] - dets[:, :4] /= scale_factor - - if 'border' in img_metas[i]: - # offset pixel of the top-left corners between original image - # and padded/enlarged image, 'border' is used when exporting - # CornerNet and CentripetalNet to onnx - x_off = img_metas[i]['border'][2] - y_off = img_metas[i]['border'][0] - dets[:, [0, 2]] -= x_off - dets[:, [1, 3]] -= y_off - dets[:, :4] *= (dets[:, :4] > 0).astype(dets.dtype) - - dets_results = bbox2result(dets, labels, len(self.CLASSES)) - - if batch_masks is not None: - masks = batch_masks[i] - img_h, img_w = img_metas[i]['img_shape'][:2] - ori_h, ori_w = img_metas[i]['ori_shape'][:2] - masks = masks[:, :img_h, :img_w] - if rescale: - masks = masks.astype(np.float32) - masks = torch.from_numpy(masks) - masks = torch.nn.functional.interpolate( - masks.unsqueeze(0), size=(ori_h, ori_w)) - masks = masks.squeeze(0).detach().numpy() - if masks.dtype != np.bool: - masks = masks >= 0.5 - segms_results = [[] for _ in range(len(self.CLASSES))] - for j in range(len(dets)): - segms_results[labels[j]].append(masks[j]) - results.append((dets_results, segms_results)) - else: - results.append(dets_results) - return results - - -class ONNXRuntimeDetector(DeployBaseDetector): - """Wrapper for detector's inference with ONNXRuntime.""" - - def __init__(self, onnx_file, class_names, device_id): - super(ONNXRuntimeDetector, self).__init__(class_names, device_id) - import onnxruntime as ort - - # get the custom op path - ort_custom_op_path = '' - try: - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - except (ImportError, ModuleNotFoundError): - warnings.warn('If input model has custom op from mmcv, \ - you may have to build mmcv with ONNXRuntime from source.') - session_options = ort.SessionOptions() - # register custom op for onnxruntime - if osp.exists(ort_custom_op_path): - session_options.register_custom_ops_library(ort_custom_op_path) - sess = ort.InferenceSession(onnx_file, session_options) - providers = ['CPUExecutionProvider'] - options = [{}] - is_cuda_available = ort.get_device() == 'GPU' - if is_cuda_available: - providers.insert(0, 'CUDAExecutionProvider') - options.insert(0, {'device_id': device_id}) - - sess.set_providers(providers, options) - - self.sess = sess - self.io_binding = sess.io_binding() - self.output_names = [_.name for _ in sess.get_outputs()] - self.is_cuda_available = is_cuda_available - - def forward_test(self, imgs, img_metas, **kwargs): - input_data = imgs[0] - # set io binding for inputs/outputs - device_type = 'cuda' if self.is_cuda_available else 'cpu' - if not self.is_cuda_available: - input_data = input_data.cpu() - self.io_binding.bind_input( - name='input', - device_type=device_type, - device_id=self.device_id, - element_type=np.float32, - shape=input_data.shape, - buffer_ptr=input_data.data_ptr()) - - for name in self.output_names: - self.io_binding.bind_output(name) - # run session to get outputs - self.sess.run_with_iobinding(self.io_binding) - ort_outputs = self.io_binding.copy_outputs_to_cpu() - return ort_outputs - - -class TensorRTDetector(DeployBaseDetector): - """Wrapper for detector's inference with TensorRT.""" - - def __init__(self, engine_file, class_names, device_id, output_names=None): - super(TensorRTDetector, self).__init__(class_names, device_id) - warnings.warn('`output_names` is deprecated and will be removed in ' - 'future releases.') - from mmcv.tensorrt import TRTWraper, load_tensorrt_plugin - try: - load_tensorrt_plugin() - except (ImportError, ModuleNotFoundError): - warnings.warn('If input model has custom op from mmcv, \ - you may have to build mmcv with TensorRT from source.') - - output_names = ['dets', 'labels'] - model = TRTWraper(engine_file, ['input'], output_names) - with_masks = False - # if TensorRT has totally 4 inputs/outputs, then - # the detector should have `mask` output. - if len(model.engine) == 4: - model.output_names = output_names + ['masks'] - with_masks = True - self.model = model - self.with_masks = with_masks - - def forward_test(self, imgs, img_metas, **kwargs): - input_data = imgs[0].contiguous() - with torch.cuda.device(self.device_id), torch.no_grad(): - outputs = self.model({'input': input_data}) - outputs = [outputs[name] for name in self.model.output_names] - outputs = [out.detach().cpu().numpy() for out in outputs] - return outputs diff --git a/cv/detection/autoassign/pytorch/mmdet/core/export/onnx_helper.py b/cv/detection/autoassign/pytorch/mmdet/core/export/onnx_helper.py deleted file mode 100755 index be0dcb956..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/export/onnx_helper.py +++ /dev/null @@ -1,223 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -import torch - - -def dynamic_clip_for_onnx(x1, y1, x2, y2, max_shape): - """Clip boxes dynamically for onnx. - - Since torch.clamp cannot have dynamic `min` and `max`, we scale the - boxes by 1/max_shape and clamp in the range [0, 1]. - - Args: - x1 (Tensor): The x1 for bounding boxes. - y1 (Tensor): The y1 for bounding boxes. - x2 (Tensor): The x2 for bounding boxes. - y2 (Tensor): The y2 for bounding boxes. - max_shape (Tensor or torch.Size): The (H,W) of original image. - Returns: - tuple(Tensor): The clipped x1, y1, x2, y2. - """ - assert isinstance( - max_shape, - torch.Tensor), '`max_shape` should be tensor of (h,w) for onnx' - - # scale by 1/max_shape - x1 = x1 / max_shape[1] - y1 = y1 / max_shape[0] - x2 = x2 / max_shape[1] - y2 = y2 / max_shape[0] - - # clamp [0, 1] - x1 = torch.clamp(x1, 0, 1) - y1 = torch.clamp(y1, 0, 1) - x2 = torch.clamp(x2, 0, 1) - y2 = torch.clamp(y2, 0, 1) - - # scale back - x1 = x1 * max_shape[1] - y1 = y1 * max_shape[0] - x2 = x2 * max_shape[1] - y2 = y2 * max_shape[0] - return x1, y1, x2, y2 - - -def get_k_for_topk(k, size): - """Get k of TopK for onnx exporting. - - The K of TopK in TensorRT should not be a Tensor, while in ONNX Runtime - it could be a Tensor.Due to dynamic shape feature, we have to decide - whether to do TopK and what K it should be while exporting to ONNX. - If returned K is less than zero, it means we do not have to do - TopK operation. - - Args: - k (int or Tensor): The set k value for nms from config file. - size (Tensor or torch.Size): The number of elements of \ - TopK's input tensor - Returns: - tuple: (int or Tensor): The final K for TopK. - """ - ret_k = -1 - if k <= 0 or size <= 0: - return ret_k - if torch.onnx.is_in_onnx_export(): - is_trt_backend = os.environ.get('ONNX_BACKEND') == 'MMCVTensorRT' - if is_trt_backend: - # TensorRT does not support dynamic K with TopK op - if 0 < k < size: - ret_k = k - else: - # Always keep topk op for dynamic input in onnx for ONNX Runtime - ret_k = torch.where(k < size, k, size) - elif k < size: - ret_k = k - else: - # ret_k is -1 - pass - return ret_k - - -def add_dummy_nms_for_onnx(boxes, - scores, - max_output_boxes_per_class=1000, - iou_threshold=0.5, - score_threshold=0.05, - pre_top_k=-1, - after_top_k=-1, - labels=None): - """Create a dummy onnx::NonMaxSuppression op while exporting to ONNX. - - This function helps exporting to onnx with batch and multiclass NMS op. - It only supports class-agnostic detection results. That is, the scores - is of shape (N, num_bboxes, num_classes) and the boxes is of shape - (N, num_boxes, 4). - - Args: - boxes (Tensor): The bounding boxes of shape [N, num_boxes, 4] - scores (Tensor): The detection scores of shape - [N, num_boxes, num_classes] - max_output_boxes_per_class (int): Maximum number of output - boxes per class of nms. Defaults to 1000. - iou_threshold (float): IOU threshold of nms. Defaults to 0.5 - score_threshold (float): score threshold of nms. - Defaults to 0.05. - pre_top_k (bool): Number of top K boxes to keep before nms. - Defaults to -1. - after_top_k (int): Number of top K boxes to keep after nms. - Defaults to -1. - labels (Tensor, optional): It not None, explicit labels would be used. - Otherwise, labels would be automatically generated using - num_classed. Defaults to None. - - Returns: - tuple[Tensor, Tensor]: dets of shape [N, num_det, 5] - and class labels of shape [N, num_det]. - """ - max_output_boxes_per_class = torch.LongTensor([max_output_boxes_per_class]) - iou_threshold = torch.tensor([iou_threshold], dtype=torch.float32) - score_threshold = torch.tensor([score_threshold], dtype=torch.float32) - batch_size = scores.shape[0] - num_class = scores.shape[2] - - nms_pre = torch.tensor(pre_top_k, device=scores.device, dtype=torch.long) - nms_pre = get_k_for_topk(nms_pre, boxes.shape[1]) - - if nms_pre > 0: - max_scores, _ = scores.max(-1) - _, topk_inds = max_scores.topk(nms_pre) - batch_inds = torch.arange(batch_size).view( - -1, 1).expand_as(topk_inds).long() - # Avoid onnx2tensorrt issue in https://github.com/NVIDIA/TensorRT/issues/1134 # noqa: E501 - transformed_inds = boxes.shape[1] * batch_inds + topk_inds - boxes = boxes.reshape(-1, 4)[transformed_inds, :].reshape( - batch_size, -1, 4) - scores = scores.reshape(-1, num_class)[transformed_inds, :].reshape( - batch_size, -1, num_class) - if labels is not None: - labels = labels.reshape(-1, 1)[transformed_inds].reshape( - batch_size, -1) - - scores = scores.permute(0, 2, 1) - num_box = boxes.shape[1] - # turn off tracing to create a dummy output of nms - state = torch._C._get_tracing_state() - # dummy indices of nms's output - num_fake_det = 2 - batch_inds = torch.randint(batch_size, (num_fake_det, 1)) - cls_inds = torch.randint(num_class, (num_fake_det, 1)) - box_inds = torch.randint(num_box, (num_fake_det, 1)) - indices = torch.cat([batch_inds, cls_inds, box_inds], dim=1) - output = indices - setattr(DummyONNXNMSop, 'output', output) - - # open tracing - torch._C._set_tracing_state(state) - selected_indices = DummyONNXNMSop.apply(boxes, scores, - max_output_boxes_per_class, - iou_threshold, score_threshold) - - batch_inds, cls_inds = selected_indices[:, 0], selected_indices[:, 1] - box_inds = selected_indices[:, 2] - if labels is None: - labels = torch.arange(num_class, dtype=torch.long).to(scores.device) - labels = labels.view(1, num_class, 1).expand_as(scores) - scores = scores.reshape(-1, 1) - boxes = boxes.reshape(batch_size, -1).repeat(1, num_class).reshape(-1, 4) - pos_inds = (num_class * batch_inds + cls_inds) * num_box + box_inds - mask = scores.new_zeros(scores.shape) - # Avoid onnx2tensorrt issue in https://github.com/NVIDIA/TensorRT/issues/1134 # noqa: E501 - # PyTorch style code: mask[batch_inds, box_inds] += 1 - mask[pos_inds, :] += 1 - scores = scores * mask - boxes = boxes * mask - - scores = scores.reshape(batch_size, -1) - boxes = boxes.reshape(batch_size, -1, 4) - labels = labels.reshape(batch_size, -1) - - nms_after = torch.tensor( - after_top_k, device=scores.device, dtype=torch.long) - nms_after = get_k_for_topk(nms_after, num_box * num_class) - - if nms_after > 0: - _, topk_inds = scores.topk(nms_after) - batch_inds = torch.arange(batch_size).view(-1, 1).expand_as(topk_inds) - # Avoid onnx2tensorrt issue in https://github.com/NVIDIA/TensorRT/issues/1134 # noqa: E501 - transformed_inds = scores.shape[1] * batch_inds + topk_inds - scores = scores.reshape(-1, 1)[transformed_inds, :].reshape( - batch_size, -1) - boxes = boxes.reshape(-1, 4)[transformed_inds, :].reshape( - batch_size, -1, 4) - labels = labels.reshape(-1, 1)[transformed_inds, :].reshape( - batch_size, -1) - - scores = scores.unsqueeze(2) - dets = torch.cat([boxes, scores], dim=2) - return dets, labels - - -class DummyONNXNMSop(torch.autograd.Function): - """DummyONNXNMSop. - - This class is only for creating onnx::NonMaxSuppression. - """ - - @staticmethod - def forward(ctx, boxes, scores, max_output_boxes_per_class, iou_threshold, - score_threshold): - - return DummyONNXNMSop.output - - @staticmethod - def symbolic(g, boxes, scores, max_output_boxes_per_class, iou_threshold, - score_threshold): - return g.op( - 'NonMaxSuppression', - boxes, - scores, - max_output_boxes_per_class, - iou_threshold, - score_threshold, - outputs=1) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/export/pytorch2onnx.py b/cv/detection/autoassign/pytorch/mmdet/core/export/pytorch2onnx.py deleted file mode 100755 index cef9f1683..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/export/pytorch2onnx.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import mmcv -import numpy as np -import torch -from mmcv.runner import load_checkpoint - - -def generate_inputs_and_wrap_model(config_path, - checkpoint_path, - input_config, - cfg_options=None): - """Prepare sample input and wrap model for ONNX export. - - The ONNX export API only accept args, and all inputs should be - torch.Tensor or corresponding types (such as tuple of tensor). - So we should call this function before exporting. This function will: - - 1. generate corresponding inputs which are used to execute the model. - 2. Wrap the model's forward function. - - For example, the MMDet models' forward function has a parameter - ``return_loss:bool``. As we want to set it as False while export API - supports neither bool type or kwargs. So we have to replace the forward - method like ``model.forward = partial(model.forward, return_loss=False)``. - - Args: - config_path (str): the OpenMMLab config for the model we want to - export to ONNX - checkpoint_path (str): Path to the corresponding checkpoint - input_config (dict): the exactly data in this dict depends on the - framework. For MMSeg, we can just declare the input shape, - and generate the dummy data accordingly. However, for MMDet, - we may pass the real img path, or the NMS will return None - as there is no legal bbox. - - Returns: - tuple: (model, tensor_data) wrapped model which can be called by - ``model(*tensor_data)`` and a list of inputs which are used to - execute the model while exporting. - """ - - model = build_model_from_cfg( - config_path, checkpoint_path, cfg_options=cfg_options) - one_img, one_meta = preprocess_example_input(input_config) - tensor_data = [one_img] - model.forward = partial( - model.forward, img_metas=[[one_meta]], return_loss=False) - - # pytorch has some bug in pytorch1.3, we have to fix it - # by replacing these existing op - opset_version = 11 - # put the import within the function thus it will not cause import error - # when not using this function - try: - from mmcv.onnx.symbolic import register_extra_symbolics - except ModuleNotFoundError: - raise NotImplementedError('please update mmcv to version>=v1.0.4') - register_extra_symbolics(opset_version) - - return model, tensor_data - - -def build_model_from_cfg(config_path, checkpoint_path, cfg_options=None): - """Build a model from config and load the given checkpoint. - - Args: - config_path (str): the OpenMMLab config for the model we want to - export to ONNX - checkpoint_path (str): Path to the corresponding checkpoint - - Returns: - torch.nn.Module: the built model - """ - from mmdet.models import build_detector - - cfg = mmcv.Config.fromfile(config_path) - if cfg_options is not None: - cfg.merge_from_dict(cfg_options) - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - cfg.model.pretrained = None - cfg.data.test.test_mode = True - - # build the model - cfg.model.train_cfg = None - model = build_detector(cfg.model, test_cfg=cfg.get('test_cfg')) - checkpoint = load_checkpoint(model, checkpoint_path, map_location='cpu') - if 'CLASSES' in checkpoint.get('meta', {}): - model.CLASSES = checkpoint['meta']['CLASSES'] - else: - from mmdet.datasets import DATASETS - dataset = DATASETS.get(cfg.data.test['type']) - assert (dataset is not None) - model.CLASSES = dataset.CLASSES - model.cpu().eval() - return model - - -def preprocess_example_input(input_config): - """Prepare an example input image for ``generate_inputs_and_wrap_model``. - - Args: - input_config (dict): customized config describing the example input. - - Returns: - tuple: (one_img, one_meta), tensor of the example input image and \ - meta information for the example input image. - - Examples: - >>> from mmdet.core.export import preprocess_example_input - >>> input_config = { - >>> 'input_shape': (1,3,224,224), - >>> 'input_path': 'demo/demo.jpg', - >>> 'normalize_cfg': { - >>> 'mean': (123.675, 116.28, 103.53), - >>> 'std': (58.395, 57.12, 57.375) - >>> } - >>> } - >>> one_img, one_meta = preprocess_example_input(input_config) - >>> print(one_img.shape) - torch.Size([1, 3, 224, 224]) - >>> print(one_meta) - {'img_shape': (224, 224, 3), - 'ori_shape': (224, 224, 3), - 'pad_shape': (224, 224, 3), - 'filename': '.png', - 'scale_factor': 1.0, - 'flip': False} - """ - input_path = input_config['input_path'] - input_shape = input_config['input_shape'] - one_img = mmcv.imread(input_path) - one_img = mmcv.imresize(one_img, input_shape[2:][::-1]) - show_img = one_img.copy() - if 'normalize_cfg' in input_config.keys(): - normalize_cfg = input_config['normalize_cfg'] - mean = np.array(normalize_cfg['mean'], dtype=np.float32) - std = np.array(normalize_cfg['std'], dtype=np.float32) - to_rgb = normalize_cfg.get('to_rgb', True) - one_img = mmcv.imnormalize(one_img, mean, std, to_rgb=to_rgb) - one_img = one_img.transpose(2, 0, 1) - one_img = torch.from_numpy(one_img).unsqueeze(0).float().requires_grad_( - True) - (_, C, H, W) = input_shape - one_meta = { - 'img_shape': (H, W, C), - 'ori_shape': (H, W, C), - 'pad_shape': (H, W, C), - 'filename': '.png', - 'scale_factor': np.ones(4, dtype=np.float32), - 'flip': False, - 'show_img': show_img, - 'flip_direction': None - } - - return one_img, one_meta diff --git a/cv/detection/autoassign/pytorch/mmdet/core/hook/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/hook/__init__.py deleted file mode 100755 index 92d068c92..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/hook/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkloss_hook import CheckInvalidLossHook -from .ema import ExpMomentumEMAHook, LinearMomentumEMAHook -from .memory_profiler_hook import MemoryProfilerHook -from .set_epoch_info_hook import SetEpochInfoHook -from .sync_norm_hook import SyncNormHook -from .sync_random_size_hook import SyncRandomSizeHook -from .wandblogger_hook import MMDetWandbHook -from .yolox_lrupdater_hook import YOLOXLrUpdaterHook -from .yolox_mode_switch_hook import YOLOXModeSwitchHook - -__all__ = [ - 'SyncRandomSizeHook', 'YOLOXModeSwitchHook', 'SyncNormHook', - 'ExpMomentumEMAHook', 'LinearMomentumEMAHook', 'YOLOXLrUpdaterHook', - 'CheckInvalidLossHook', 'SetEpochInfoHook', 'MemoryProfilerHook', - 'MMDetWandbHook' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/hook/checkloss_hook.py b/cv/detection/autoassign/pytorch/mmdet/core/hook/checkloss_hook.py deleted file mode 100755 index 66a3c1176..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/hook/checkloss_hook.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.runner.hooks import HOOKS, Hook - - -@HOOKS.register_module() -class CheckInvalidLossHook(Hook): - """Check invalid loss hook. - - This hook will regularly check whether the loss is valid - during training. - - Args: - interval (int): Checking interval (every k iterations). - Default: 50. - """ - - def __init__(self, interval=50): - self.interval = interval - - def after_train_iter(self, runner): - if self.every_n_iters(runner, self.interval): - assert torch.isfinite(runner.outputs['loss']), \ - runner.logger.info('loss become infinite or NaN!') diff --git a/cv/detection/autoassign/pytorch/mmdet/core/hook/ema.py b/cv/detection/autoassign/pytorch/mmdet/core/hook/ema.py deleted file mode 100755 index f72fde9be..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/hook/ema.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from mmcv.parallel import is_module_wrapper -from mmcv.runner.hooks import HOOKS, Hook - - -class BaseEMAHook(Hook): - """Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointHook. Note, - the original model parameters are actually saved in ema field after train. - - Args: - momentum (float): The momentum used for updating ema parameter. - Ema's parameter are updated with the formula: - `ema_param = (1-momentum) * ema_param + momentum * cur_param`. - Defaults to 0.0002. - skip_buffers (bool): Whether to skip the model buffers, such as - batchnorm running stats (running_mean, running_var), it does not - perform the ema operation. Default to False. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - resume_from (str, optional): The checkpoint path. Defaults to None. - momentum_fun (func, optional): The function to change momentum - during early iteration (also warmup) to help early training. - It uses `momentum` as a constant. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - skip_buffers=False, - resume_from=None, - momentum_fun=None): - assert 0 < momentum < 1 - self.momentum = momentum - self.skip_buffers = skip_buffers - self.interval = interval - self.checkpoint = resume_from - self.momentum_fun = momentum_fun - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model. - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - if self.skip_buffers: - self.model_parameters = dict(model.named_parameters()) - else: - self.model_parameters = model.state_dict() - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers()) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def get_momentum(self, runner): - return self.momentum_fun(runner.iter) if self.momentum_fun else \ - self.momentum - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - if (runner.iter + 1) % self.interval != 0: - return - momentum = self.get_momentum(runner) - for name, parameter in self.model_parameters.items(): - # exclude num_tracking - if parameter.dtype.is_floating_point: - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_( - parameter.data, alpha=momentum) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) - - -@HOOKS.register_module() -class ExpMomentumEMAHook(BaseEMAHook): - """EMAHook using exponential momentum strategy. - - Args: - total_iter (int): The total number of iterations of EMA momentum. - Defaults to 2000. - """ - - def __init__(self, total_iter=2000, **kwargs): - super(ExpMomentumEMAHook, self).__init__(**kwargs) - self.momentum_fun = lambda x: (1 - self.momentum) * math.exp(-( - 1 + x) / total_iter) + self.momentum - - -@HOOKS.register_module() -class LinearMomentumEMAHook(BaseEMAHook): - """EMAHook using linear momentum strategy. - - Args: - warm_up (int): During first warm_up steps, we may use smaller decay - to update ema parameters more slowly. Defaults to 100. - """ - - def __init__(self, warm_up=100, **kwargs): - super(LinearMomentumEMAHook, self).__init__(**kwargs) - self.momentum_fun = lambda x: min(self.momentum**self.interval, - (1 + x) / (warm_up + x)) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/hook/memory_profiler_hook.py b/cv/detection/autoassign/pytorch/mmdet/core/hook/memory_profiler_hook.py deleted file mode 100755 index 199cd6a5a..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/hook/memory_profiler_hook.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner.hooks import HOOKS, Hook - - -@HOOKS.register_module() -class MemoryProfilerHook(Hook): - """Memory profiler hook recording memory information including virtual - memory, swap memory, and the memory of the current process. - - Args: - interval (int): Checking interval (every k iterations). - Default: 50. - """ - - def __init__(self, interval=50): - try: - from psutil import swap_memory, virtual_memory - self._swap_memory = swap_memory - self._virtual_memory = virtual_memory - except ImportError: - raise ImportError('psutil is not installed, please install it by: ' - 'pip install psutil') - - try: - from memory_profiler import memory_usage - self._memory_usage = memory_usage - except ImportError: - raise ImportError( - 'memory_profiler is not installed, please install it by: ' - 'pip install memory_profiler') - - self.interval = interval - - def after_iter(self, runner): - if self.every_n_iters(runner, self.interval): - # in Byte - virtual_memory = self._virtual_memory() - swap_memory = self._swap_memory() - # in MB - process_memory = self._memory_usage()[0] - factor = 1024 * 1024 - runner.logger.info( - 'Memory information ' - 'available_memory: ' - f'{round(virtual_memory.available / factor)} MB, ' - 'used_memory: ' - f'{round(virtual_memory.used / factor)} MB, ' - f'memory_utilization: {virtual_memory.percent} %, ' - 'available_swap_memory: ' - f'{round((swap_memory.total - swap_memory.used) / factor)}' - ' MB, ' - f'used_swap_memory: {round(swap_memory.used / factor)} MB, ' - f'swap_memory_utilization: {swap_memory.percent} %, ' - 'current_process_memory: ' - f'{round(process_memory)} MB') diff --git a/cv/detection/autoassign/pytorch/mmdet/core/hook/set_epoch_info_hook.py b/cv/detection/autoassign/pytorch/mmdet/core/hook/set_epoch_info_hook.py deleted file mode 100755 index 640e6891d..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/hook/set_epoch_info_hook.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.parallel import is_module_wrapper -from mmcv.runner import HOOKS, Hook - - -@HOOKS.register_module() -class SetEpochInfoHook(Hook): - """Set runner's epoch information to the model.""" - - def before_train_epoch(self, runner): - epoch = runner.epoch - model = runner.model - if is_module_wrapper(model): - model = model.module - model.set_epoch(epoch) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/hook/sync_norm_hook.py b/cv/detection/autoassign/pytorch/mmdet/core/hook/sync_norm_hook.py deleted file mode 100755 index c060cda9c..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/hook/sync_norm_hook.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -from mmcv.runner import get_dist_info -from mmcv.runner.hooks import HOOKS, Hook -from torch import nn - -from ..utils.dist_utils import all_reduce_dict - - -def get_norm_states(module): - async_norm_states = OrderedDict() - for name, child in module.named_modules(): - if isinstance(child, nn.modules.batchnorm._NormBase): - for k, v in child.state_dict().items(): - async_norm_states['.'.join([name, k])] = v - return async_norm_states - - -@HOOKS.register_module() -class SyncNormHook(Hook): - """Synchronize Norm states after training epoch, currently used in YOLOX. - - Args: - num_last_epochs (int): The number of latter epochs in the end of the - training to switch to synchronizing norm interval. Default: 15. - interval (int): Synchronizing norm interval. Default: 1. - """ - - def __init__(self, num_last_epochs=15, interval=1): - self.interval = interval - self.num_last_epochs = num_last_epochs - - def before_train_epoch(self, runner): - epoch = runner.epoch - if (epoch + 1) == runner.max_epochs - self.num_last_epochs: - # Synchronize norm every epoch. - self.interval = 1 - - def after_train_epoch(self, runner): - """Synchronizing norm.""" - epoch = runner.epoch - module = runner.model - if (epoch + 1) % self.interval == 0: - _, world_size = get_dist_info() - if world_size == 1: - return - norm_states = get_norm_states(module) - if len(norm_states) == 0: - return - norm_states = all_reduce_dict(norm_states, op='mean') - module.load_state_dict(norm_states, strict=False) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/hook/sync_random_size_hook.py b/cv/detection/autoassign/pytorch/mmdet/core/hook/sync_random_size_hook.py deleted file mode 100755 index 480be7ef2..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/hook/sync_random_size_hook.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import random -import warnings - -import torch -from mmcv.runner import get_dist_info -from mmcv.runner.hooks import HOOKS, Hook -from torch import distributed as dist - - -@HOOKS.register_module() -class SyncRandomSizeHook(Hook): - """Change and synchronize the random image size across ranks. - SyncRandomSizeHook is deprecated, please use Resize pipeline to achieve - similar functions. Such as `dict(type='Resize', img_scale=[(448, 448), - (832, 832)], multiscale_mode='range', keep_ratio=True)`. - - Note: Due to the multi-process dataloader, its behavior is different - from YOLOX's official implementation, the official is to change the - size every fixed iteration interval and what we achieved is a fixed - epoch interval. - - Args: - ratio_range (tuple[int]): Random ratio range. It will be multiplied - by 32, and then change the dataset output image size. - Default: (14, 26). - img_scale (tuple[int]): Size of input image. Default: (640, 640). - interval (int): The epoch interval of change image size. Default: 1. - device (torch.device | str): device for returned tensors. - Default: 'cuda'. - """ - - def __init__(self, - ratio_range=(14, 26), - img_scale=(640, 640), - interval=1, - device='cuda'): - warnings.warn('DeprecationWarning: SyncRandomSizeHook is deprecated. ' - 'Please use Resize pipeline to achieve similar ' - 'functions. Due to the multi-process dataloader, ' - 'its behavior is different from YOLOX\'s official ' - 'implementation, the official is to change the size ' - 'every fixed iteration interval and what we achieved ' - 'is a fixed epoch interval.') - self.rank, world_size = get_dist_info() - self.is_distributed = world_size > 1 - self.ratio_range = ratio_range - self.img_scale = img_scale - self.interval = interval - self.device = device - - def after_train_epoch(self, runner): - """Change the dataset output image size.""" - if self.ratio_range is not None and (runner.epoch + - 1) % self.interval == 0: - # Due to DDP and DP get the device behavior inconsistent, - # so we did not get the device from runner.model. - tensor = torch.LongTensor(2).to(self.device) - - if self.rank == 0: - size_factor = self.img_scale[1] * 1. / self.img_scale[0] - size = random.randint(*self.ratio_range) - size = (int(32 * size), 32 * int(size * size_factor)) - tensor[0] = size[0] - tensor[1] = size[1] - - if self.is_distributed: - dist.barrier() - dist.broadcast(tensor, 0) - - runner.data_loader.dataset.update_dynamic_scale( - (tensor[0].item(), tensor[1].item())) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/hook/wandblogger_hook.py b/cv/detection/autoassign/pytorch/mmdet/core/hook/wandblogger_hook.py deleted file mode 100755 index 33d79860d..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/hook/wandblogger_hook.py +++ /dev/null @@ -1,587 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os.path as osp -import sys -import warnings - -import mmcv -import numpy as np -import pycocotools.mask as mask_util -from mmcv.runner import HOOKS -from mmcv.runner.dist_utils import master_only -from mmcv.runner.hooks.checkpoint import CheckpointHook -from mmcv.runner.hooks.logger.wandb import WandbLoggerHook -from mmcv.utils import digit_version - -from mmdet.core import DistEvalHook, EvalHook -from mmdet.core.mask.structures import polygon_to_bitmap - - -@HOOKS.register_module() -class MMDetWandbHook(WandbLoggerHook): - """Enhanced Wandb logger hook for MMDetection. - - Comparing with the :cls:`mmcv.runner.WandbLoggerHook`, this hook can not - only automatically log all the metrics but also log the following extra - information - saves model checkpoints as W&B Artifact, and - logs model prediction as interactive W&B Tables. - - - Metrics: The MMDetWandbHook will automatically log training - and validation metrics along with system metrics (CPU/GPU). - - - Checkpointing: If `log_checkpoint` is True, the checkpoint saved at - every checkpoint interval will be saved as W&B Artifacts. - This depends on the : class:`mmcv.runner.CheckpointHook` whose priority - is higher than this hook. Please refer to - https://docs.wandb.ai/guides/artifacts/model-versioning - to learn more about model versioning with W&B Artifacts. - - - Checkpoint Metadata: If evaluation results are available for a given - checkpoint artifact, it will have a metadata associated with it. - The metadata contains the evaluation metrics computed on validation - data with that checkpoint along with the current epoch. It depends - on `EvalHook` whose priority is more than MMDetWandbHook. - - - Evaluation: At every evaluation interval, the `MMDetWandbHook` logs the - model prediction as interactive W&B Tables. The number of samples - logged is given by `num_eval_images`. Currently, the `MMDetWandbHook` - logs the predicted bounding boxes along with the ground truth at every - evaluation interval. This depends on the `EvalHook` whose priority is - more than `MMDetWandbHook`. Also note that the data is just logged once - and subsequent evaluation tables uses reference to the logged data - to save memory usage. Please refer to - https://docs.wandb.ai/guides/data-vis to learn more about W&B Tables. - - For more details check out W&B's MMDetection docs: - https://docs.wandb.ai/guides/integrations/mmdetection - - ``` - Example: - log_config = dict( - ... - hooks=[ - ..., - dict(type='MMDetWandbHook', - init_kwargs={ - 'entity': "YOUR_ENTITY", - 'project': "YOUR_PROJECT_NAME" - }, - interval=50, - log_checkpoint=True, - log_checkpoint_metadata=True, - num_eval_images=100, - bbox_score_thr=0.3) - ]) - ``` - - Args: - init_kwargs (dict): A dict passed to wandb.init to initialize - a W&B run. Please refer to https://docs.wandb.ai/ref/python/init - for possible key-value pairs. - interval (int): Logging interval (every k iterations). Defaults to 50. - log_checkpoint (bool): Save the checkpoint at every checkpoint interval - as W&B Artifacts. Use this for model versioning where each version - is a checkpoint. Defaults to False. - log_checkpoint_metadata (bool): Log the evaluation metrics computed - on the validation data with the checkpoint, along with current - epoch as a metadata to that checkpoint. - Defaults to True. - num_eval_images (int): The number of validation images to be logged. - If zero, the evaluation won't be logged. Defaults to 100. - bbox_score_thr (float): Threshold for bounding box scores. - Defaults to 0.3. - """ - - def __init__(self, - init_kwargs=None, - interval=50, - log_checkpoint=False, - log_checkpoint_metadata=False, - num_eval_images=100, - bbox_score_thr=0.3, - **kwargs): - super(MMDetWandbHook, self).__init__(init_kwargs, interval, **kwargs) - - self.log_checkpoint = log_checkpoint - self.log_checkpoint_metadata = ( - log_checkpoint and log_checkpoint_metadata) - self.num_eval_images = num_eval_images - self.bbox_score_thr = bbox_score_thr - self.log_evaluation = (num_eval_images > 0) - self.ckpt_hook: CheckpointHook = None - self.eval_hook: EvalHook = None - - def import_wandb(self): - try: - import wandb - from wandb import init # noqa - - # Fix ResourceWarning when calling wandb.log in wandb v0.12.10. - # https://github.com/wandb/client/issues/2837 - if digit_version(wandb.__version__) < digit_version('0.12.10'): - warnings.warn( - f'The current wandb {wandb.__version__} is ' - f'lower than v0.12.10 will cause ResourceWarning ' - f'when calling wandb.log, Please run ' - f'"pip install --upgrade wandb"') - - except ImportError: - raise ImportError( - 'Please run "pip install "wandb>=0.12.10"" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(MMDetWandbHook, self).before_run(runner) - - # Save and Log config. - if runner.meta is not None and runner.meta.get('exp_name', - None) is not None: - src_cfg_path = osp.join(runner.work_dir, - runner.meta.get('exp_name', None)) - if osp.exists(src_cfg_path): - self.wandb.save(src_cfg_path, base_path=runner.work_dir) - self._update_wandb_config(runner) - else: - runner.logger.warning('No meta information found in the runner. ') - - # Inspect CheckpointHook and EvalHook - for hook in runner.hooks: - if isinstance(hook, CheckpointHook): - self.ckpt_hook = hook - if isinstance(hook, (EvalHook, DistEvalHook)): - self.eval_hook = hook - - # Check conditions to log checkpoint - if self.log_checkpoint: - if self.ckpt_hook is None: - self.log_checkpoint = False - self.log_checkpoint_metadata = False - runner.logger.warning( - 'To log checkpoint in MMDetWandbHook, `CheckpointHook` is' - 'required, please check hooks in the runner.') - else: - self.ckpt_interval = self.ckpt_hook.interval - - # Check conditions to log evaluation - if self.log_evaluation or self.log_checkpoint_metadata: - if self.eval_hook is None: - self.log_evaluation = False - self.log_checkpoint_metadata = False - runner.logger.warning( - 'To log evaluation or checkpoint metadata in ' - 'MMDetWandbHook, `EvalHook` or `DistEvalHook` in mmdet ' - 'is required, please check whether the validation ' - 'is enabled.') - else: - self.eval_interval = self.eval_hook.interval - self.val_dataset = self.eval_hook.dataloader.dataset - # Determine the number of samples to be logged. - if self.num_eval_images > len(self.val_dataset): - self.num_eval_images = len(self.val_dataset) - runner.logger.warning( - f'The num_eval_images ({self.num_eval_images}) is ' - 'greater than the total number of validation samples ' - f'({len(self.val_dataset)}). The complete validation ' - 'dataset will be logged.') - - # Check conditions to log checkpoint metadata - if self.log_checkpoint_metadata: - assert self.ckpt_interval % self.eval_interval == 0, \ - 'To log checkpoint metadata in MMDetWandbHook, the interval ' \ - f'of checkpoint saving ({self.ckpt_interval}) should be ' \ - 'divisible by the interval of evaluation ' \ - f'({self.eval_interval}).' - - # Initialize evaluation table - if self.log_evaluation: - # Initialize data table - self._init_data_table() - # Add data to the data table - self._add_ground_truth(runner) - # Log ground truth data - self._log_data_table() - - @master_only - def after_train_epoch(self, runner): - super(MMDetWandbHook, self).after_train_epoch(runner) - - if not self.by_epoch: - return - - # Log checkpoint and metadata. - if (self.log_checkpoint - and self.every_n_epochs(runner, self.ckpt_interval) - or (self.ckpt_hook.save_last and self.is_last_epoch(runner))): - if self.log_checkpoint_metadata and self.eval_hook: - metadata = { - 'epoch': runner.epoch + 1, - **self._get_eval_results() - } - else: - metadata = None - aliases = [f'epoch_{runner.epoch + 1}', 'latest'] - model_path = osp.join(self.ckpt_hook.out_dir, - f'epoch_{runner.epoch + 1}.pth') - self._log_ckpt_as_artifact(model_path, aliases, metadata) - - # Save prediction table - if self.log_evaluation and self.eval_hook._should_evaluate(runner): - results = self.eval_hook.latest_results - # Initialize evaluation table - self._init_pred_table() - # Log predictions - self._log_predictions(results) - # Log the table - self._log_eval_table(runner.epoch + 1) - - @master_only - def after_train_iter(self, runner): - if self.get_mode(runner) == 'train': - # An ugly patch. The iter-based eval hook will call the - # `after_train_iter` method of all logger hooks before evaluation. - # Use this trick to skip that call. - # Don't call super method at first, it will clear the log_buffer - return super(MMDetWandbHook, self).after_train_iter(runner) - else: - super(MMDetWandbHook, self).after_train_iter(runner) - - if self.by_epoch: - return - - # Save checkpoint and metadata - if (self.log_checkpoint - and self.every_n_iters(runner, self.ckpt_interval) - or (self.ckpt_hook.save_last and self.is_last_iter(runner))): - if self.log_checkpoint_metadata and self.eval_hook: - metadata = { - 'iter': runner.iter + 1, - **self._get_eval_results() - } - else: - metadata = None - aliases = [f'iter_{runner.iter + 1}', 'latest'] - model_path = osp.join(self.ckpt_hook.out_dir, - f'iter_{runner.iter + 1}.pth') - self._log_ckpt_as_artifact(model_path, aliases, metadata) - - # Save prediction table - if self.log_evaluation and self.eval_hook._should_evaluate(runner): - results = self.eval_hook.latest_results - # Initialize evaluation table - self._init_pred_table() - # Log predictions - self._log_predictions(results) - # Log the table - self._log_eval_table(runner.iter + 1) - - @master_only - def after_run(self, runner): - self.wandb.finish() - - def _update_wandb_config(self, runner): - """Update wandb config.""" - # Import the config file. - sys.path.append(runner.work_dir) - config_filename = runner.meta['exp_name'][:-3] - configs = importlib.import_module(config_filename) - # Prepare a nested dict of config variables. - config_keys = [key for key in dir(configs) if not key.startswith('__')] - config_dict = {key: getattr(configs, key) for key in config_keys} - # Update the W&B config. - self.wandb.config.update(config_dict) - - def _log_ckpt_as_artifact(self, model_path, aliases, metadata=None): - """Log model checkpoint as W&B Artifact. - - Args: - model_path (str): Path of the checkpoint to log. - aliases (list): List of the aliases associated with this artifact. - metadata (dict, optional): Metadata associated with this artifact. - """ - model_artifact = self.wandb.Artifact( - f'run_{self.wandb.run.id}_model', type='model', metadata=metadata) - model_artifact.add_file(model_path) - self.wandb.log_artifact(model_artifact, aliases=aliases) - - def _get_eval_results(self): - """Get model evaluation results.""" - results = self.eval_hook.latest_results - eval_results = self.val_dataset.evaluate( - results, logger='silent', **self.eval_hook.eval_kwargs) - return eval_results - - def _init_data_table(self): - """Initialize the W&B Tables for validation data.""" - columns = ['image_name', 'image'] - self.data_table = self.wandb.Table(columns=columns) - - def _init_pred_table(self): - """Initialize the W&B Tables for model evaluation.""" - columns = ['image_name', 'ground_truth', 'prediction'] - self.eval_table = self.wandb.Table(columns=columns) - - def _add_ground_truth(self, runner): - # Get image loading pipeline - from mmdet.datasets.pipelines import LoadImageFromFile - img_loader = None - for t in self.val_dataset.pipeline.transforms: - if isinstance(t, LoadImageFromFile): - img_loader = t - - if img_loader is None: - self.log_evaluation = False - runner.logger.warning( - 'LoadImageFromFile is required to add images ' - 'to W&B Tables.') - return - - # Select the images to be logged. - self.eval_image_indexs = np.arange(len(self.val_dataset)) - # Set seed so that same validation set is logged each time. - np.random.seed(42) - np.random.shuffle(self.eval_image_indexs) - self.eval_image_indexs = self.eval_image_indexs[:self.num_eval_images] - - CLASSES = self.val_dataset.CLASSES - self.class_id_to_label = { - id + 1: name - for id, name in enumerate(CLASSES) - } - self.class_set = self.wandb.Classes([{ - 'id': id, - 'name': name - } for id, name in self.class_id_to_label.items()]) - - img_prefix = self.val_dataset.img_prefix - - for idx in self.eval_image_indexs: - img_info = self.val_dataset.data_infos[idx] - image_name = img_info.get('filename', f'img_{idx}') - img_height, img_width = img_info['height'], img_info['width'] - - img_meta = img_loader( - dict(img_info=img_info, img_prefix=img_prefix)) - - # Get image and convert from BGR to RGB - image = mmcv.bgr2rgb(img_meta['img']) - - data_ann = self.val_dataset.get_ann_info(idx) - bboxes = data_ann['bboxes'] - labels = data_ann['labels'] - masks = data_ann.get('masks', None) - - # Get dict of bounding boxes to be logged. - assert len(bboxes) == len(labels) - wandb_boxes = self._get_wandb_bboxes(bboxes, labels) - - # Get dict of masks to be logged. - if masks is not None: - wandb_masks = self._get_wandb_masks( - masks, - labels, - is_poly_mask=True, - height=img_height, - width=img_width) - else: - wandb_masks = None - # TODO: Panoramic segmentation visualization. - - # Log a row to the data table. - self.data_table.add_data( - image_name, - self.wandb.Image( - image, - boxes=wandb_boxes, - masks=wandb_masks, - classes=self.class_set)) - - def _log_predictions(self, results): - table_idxs = self.data_table_ref.get_index() - assert len(table_idxs) == len(self.eval_image_indexs) - - for ndx, eval_image_index in enumerate(self.eval_image_indexs): - # Get the result - result = results[eval_image_index] - if isinstance(result, tuple): - bbox_result, segm_result = result - if isinstance(segm_result, tuple): - segm_result = segm_result[0] # ms rcnn - else: - bbox_result, segm_result = result, None - assert len(bbox_result) == len(self.class_id_to_label) - - # Get labels - bboxes = np.vstack(bbox_result) - labels = [ - np.full(bbox.shape[0], i, dtype=np.int32) - for i, bbox in enumerate(bbox_result) - ] - labels = np.concatenate(labels) - - # Get segmentation mask if available. - segms = None - if segm_result is not None and len(labels) > 0: - segms = mmcv.concat_list(segm_result) - segms = mask_util.decode(segms) - segms = segms.transpose(2, 0, 1) - assert len(segms) == len(labels) - # TODO: Panoramic segmentation visualization. - - # Remove bounding boxes and masks with score lower than threshold. - if self.bbox_score_thr > 0: - assert bboxes is not None and bboxes.shape[1] == 5 - scores = bboxes[:, -1] - inds = scores > self.bbox_score_thr - bboxes = bboxes[inds, :] - labels = labels[inds] - if segms is not None: - segms = segms[inds, ...] - - # Get dict of bounding boxes to be logged. - wandb_boxes = self._get_wandb_bboxes(bboxes, labels, log_gt=False) - # Get dict of masks to be logged. - if segms is not None: - wandb_masks = self._get_wandb_masks(segms, labels) - else: - wandb_masks = None - - # Log a row to the eval table. - self.eval_table.add_data( - self.data_table_ref.data[ndx][0], - self.data_table_ref.data[ndx][1], - self.wandb.Image( - self.data_table_ref.data[ndx][1], - boxes=wandb_boxes, - masks=wandb_masks, - classes=self.class_set)) - - def _get_wandb_bboxes(self, bboxes, labels, log_gt=True): - """Get list of structured dict for logging bounding boxes to W&B. - - Args: - bboxes (list): List of bounding box coordinates in - (minX, minY, maxX, maxY) format. - labels (int): List of label ids. - log_gt (bool): Whether to log ground truth or prediction boxes. - - Returns: - Dictionary of bounding boxes to be logged. - """ - wandb_boxes = {} - - box_data = [] - for bbox, label in zip(bboxes, labels): - if not isinstance(label, int): - label = int(label) - label = label + 1 - - if len(bbox) == 5: - confidence = float(bbox[4]) - class_name = self.class_id_to_label[label] - box_caption = f'{class_name} {confidence:.2f}' - else: - box_caption = str(self.class_id_to_label[label]) - - position = dict( - minX=int(bbox[0]), - minY=int(bbox[1]), - maxX=int(bbox[2]), - maxY=int(bbox[3])) - - box_data.append({ - 'position': position, - 'class_id': label, - 'box_caption': box_caption, - 'domain': 'pixel' - }) - - wandb_bbox_dict = { - 'box_data': box_data, - 'class_labels': self.class_id_to_label - } - - if log_gt: - wandb_boxes['ground_truth'] = wandb_bbox_dict - else: - wandb_boxes['predictions'] = wandb_bbox_dict - - return wandb_boxes - - def _get_wandb_masks(self, - masks, - labels, - is_poly_mask=False, - height=None, - width=None): - """Get list of structured dict for logging masks to W&B. - - Args: - masks (list): List of masks. - labels (int): List of label ids. - is_poly_mask (bool): Whether the mask is polygonal or not. - This is true for CocoDataset. - height (int): Height of the image. - width (int): Width of the image. - - Returns: - Dictionary of masks to be logged. - """ - mask_label_dict = dict() - for mask, label in zip(masks, labels): - label = label + 1 - # Get bitmap mask from polygon. - if is_poly_mask: - if height is not None and width is not None: - mask = polygon_to_bitmap(mask, height, width) - # Create composite masks for each class. - if label not in mask_label_dict.keys(): - mask_label_dict[label] = mask - else: - mask_label_dict[label] = np.logical_or(mask_label_dict[label], - mask) - - wandb_masks = dict() - for key, value in mask_label_dict.items(): - # Create mask for that class. - value = value.astype(np.uint8) - value[value > 0] = key - - # Create dict of masks for logging. - class_name = self.class_id_to_label[key] - wandb_masks[class_name] = { - 'mask_data': value, - 'class_labels': self.class_id_to_label - } - - return wandb_masks - - def _log_data_table(self): - """Log the W&B Tables for validation data as artifact and calls - `use_artifact` on it so that the evaluation table can use the reference - of already uploaded images. - - This allows the data to be uploaded just once. - """ - data_artifact = self.wandb.Artifact('val', type='dataset') - data_artifact.add(self.data_table, 'val_data') - - self.wandb.run.use_artifact(data_artifact) - data_artifact.wait() - - self.data_table_ref = data_artifact.get('val_data') - - def _log_eval_table(self, idx): - """Log the W&B Tables for model evaluation. - - The table will be logged multiple times creating new version. Use this - to compare models at different intervals interactively. - """ - pred_artifact = self.wandb.Artifact( - f'run_{self.wandb.run.id}_pred', type='evaluation') - pred_artifact.add(self.eval_table, 'eval_data') - if self.by_epoch: - aliases = ['latest', f'epoch_{idx}'] - else: - aliases = ['latest', f'iter_{idx}'] - self.wandb.run.log_artifact(pred_artifact, aliases=aliases) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/hook/yolox_lrupdater_hook.py b/cv/detection/autoassign/pytorch/mmdet/core/hook/yolox_lrupdater_hook.py deleted file mode 100755 index 65c4123a8..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/hook/yolox_lrupdater_hook.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner.hooks import HOOKS -from mmcv.runner.hooks.lr_updater import (CosineAnnealingLrUpdaterHook, - annealing_cos) - - -@HOOKS.register_module() -class YOLOXLrUpdaterHook(CosineAnnealingLrUpdaterHook): - """YOLOX learning rate scheme. - - There are two main differences between YOLOXLrUpdaterHook - and CosineAnnealingLrUpdaterHook. - - 1. When the current running epoch is greater than - `max_epoch-last_epoch`, a fixed learning rate will be used - 2. The exp warmup scheme is different with LrUpdaterHook in MMCV - - Args: - num_last_epochs (int): The number of epochs with a fixed learning rate - before the end of the training. - """ - - def __init__(self, num_last_epochs, **kwargs): - self.num_last_epochs = num_last_epochs - super(YOLOXLrUpdaterHook, self).__init__(**kwargs) - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - # exp warmup scheme - k = self.warmup_ratio * pow( - (cur_iters + 1) / float(self.warmup_iters), 2) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.base_lr, dict): - lr_groups = {} - for key, base_lr in self.base_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, base_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.base_lr) - - def get_lr(self, runner, base_lr): - last_iter = len(runner.data_loader) * self.num_last_epochs - - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - progress += 1 - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress >= max_progress - last_iter: - # fixed learning rate - return target_lr - else: - return annealing_cos( - base_lr, target_lr, (progress - self.warmup_iters) / - (max_progress - self.warmup_iters - last_iter)) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/hook/yolox_mode_switch_hook.py b/cv/detection/autoassign/pytorch/mmdet/core/hook/yolox_mode_switch_hook.py deleted file mode 100755 index a01d0699f..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/hook/yolox_mode_switch_hook.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.parallel import is_module_wrapper -from mmcv.runner.hooks import HOOKS, Hook - - -@HOOKS.register_module() -class YOLOXModeSwitchHook(Hook): - """Switch the mode of YOLOX during training. - - This hook turns off the mosaic and mixup data augmentation and switches - to use L1 loss in bbox_head. - - Args: - num_last_epochs (int): The number of latter epochs in the end of the - training to close the data augmentation and switch to L1 loss. - Default: 15. - skip_type_keys (list[str], optional): Sequence of type string to be - skip pipeline. Default: ('Mosaic', 'RandomAffine', 'MixUp') - """ - - def __init__(self, - num_last_epochs=15, - skip_type_keys=('Mosaic', 'RandomAffine', 'MixUp')): - self.num_last_epochs = num_last_epochs - self.skip_type_keys = skip_type_keys - self._restart_dataloader = False - - def before_train_epoch(self, runner): - """Close mosaic and mixup augmentation and switches to use L1 loss.""" - epoch = runner.epoch - train_loader = runner.data_loader - model = runner.model - if is_module_wrapper(model): - model = model.module - if (epoch + 1) == runner.max_epochs - self.num_last_epochs: - runner.logger.info('No mosaic and mixup aug now!') - # The dataset pipeline cannot be updated when persistent_workers - # is True, so we need to force the dataloader's multi-process - # restart. This is a very hacky approach. - train_loader.dataset.update_skip_type_keys(self.skip_type_keys) - if hasattr(train_loader, 'persistent_workers' - ) and train_loader.persistent_workers is True: - train_loader._DataLoader__initialized = False - train_loader._iterator = None - self._restart_dataloader = True - runner.logger.info('Add additional L1 loss now!') - model.bbox_head.use_l1 = True - else: - # Once the restart is complete, we need to restore - # the initialization flag. - if self._restart_dataloader: - train_loader._DataLoader__initialized = True diff --git a/cv/detection/autoassign/pytorch/mmdet/core/mask/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/mask/__init__.py deleted file mode 100755 index 04fec7f50..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/mask/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .mask_target import mask_target -from .structures import BaseInstanceMasks, BitmapMasks, PolygonMasks -from .utils import encode_mask_results, mask2bbox, split_combined_polys - -__all__ = [ - 'split_combined_polys', 'mask_target', 'BaseInstanceMasks', 'BitmapMasks', - 'PolygonMasks', 'encode_mask_results', 'mask2bbox' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/mask/mask_target.py b/cv/detection/autoassign/pytorch/mmdet/core/mask/mask_target.py deleted file mode 100755 index f14ac82c9..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/mask/mask_target.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -from torch.nn.modules.utils import _pair - - -def mask_target(pos_proposals_list, pos_assigned_gt_inds_list, gt_masks_list, - cfg): - """Compute mask target for positive proposals in multiple images. - - Args: - pos_proposals_list (list[Tensor]): Positive proposals in multiple - images. - pos_assigned_gt_inds_list (list[Tensor]): Assigned GT indices for each - positive proposals. - gt_masks_list (list[:obj:`BaseInstanceMasks`]): Ground truth masks of - each image. - cfg (dict): Config dict that specifies the mask size. - - Returns: - list[Tensor]: Mask target of each image. - - Example: - >>> import mmcv - >>> import mmdet - >>> from mmdet.core.mask import BitmapMasks - >>> from mmdet.core.mask.mask_target import * - >>> H, W = 17, 18 - >>> cfg = mmcv.Config({'mask_size': (13, 14)}) - >>> rng = np.random.RandomState(0) - >>> # Positive proposals (tl_x, tl_y, br_x, br_y) for each image - >>> pos_proposals_list = [ - >>> torch.Tensor([ - >>> [ 7.2425, 5.5929, 13.9414, 14.9541], - >>> [ 7.3241, 3.6170, 16.3850, 15.3102], - >>> ]), - >>> torch.Tensor([ - >>> [ 4.8448, 6.4010, 7.0314, 9.7681], - >>> [ 5.9790, 2.6989, 7.4416, 4.8580], - >>> [ 0.0000, 0.0000, 0.1398, 9.8232], - >>> ]), - >>> ] - >>> # Corresponding class index for each proposal for each image - >>> pos_assigned_gt_inds_list = [ - >>> torch.LongTensor([7, 0]), - >>> torch.LongTensor([5, 4, 1]), - >>> ] - >>> # Ground truth mask for each true object for each image - >>> gt_masks_list = [ - >>> BitmapMasks(rng.rand(8, H, W), height=H, width=W), - >>> BitmapMasks(rng.rand(6, H, W), height=H, width=W), - >>> ] - >>> mask_targets = mask_target( - >>> pos_proposals_list, pos_assigned_gt_inds_list, - >>> gt_masks_list, cfg) - >>> assert mask_targets.shape == (5,) + cfg['mask_size'] - """ - cfg_list = [cfg for _ in range(len(pos_proposals_list))] - mask_targets = map(mask_target_single, pos_proposals_list, - pos_assigned_gt_inds_list, gt_masks_list, cfg_list) - mask_targets = list(mask_targets) - if len(mask_targets) > 0: - mask_targets = torch.cat(mask_targets) - return mask_targets - - -def mask_target_single(pos_proposals, pos_assigned_gt_inds, gt_masks, cfg): - """Compute mask target for each positive proposal in the image. - - Args: - pos_proposals (Tensor): Positive proposals. - pos_assigned_gt_inds (Tensor): Assigned GT inds of positive proposals. - gt_masks (:obj:`BaseInstanceMasks`): GT masks in the format of Bitmap - or Polygon. - cfg (dict): Config dict that indicate the mask size. - - Returns: - Tensor: Mask target of each positive proposals in the image. - - Example: - >>> import mmcv - >>> import mmdet - >>> from mmdet.core.mask import BitmapMasks - >>> from mmdet.core.mask.mask_target import * # NOQA - >>> H, W = 32, 32 - >>> cfg = mmcv.Config({'mask_size': (7, 11)}) - >>> rng = np.random.RandomState(0) - >>> # Masks for each ground truth box (relative to the image) - >>> gt_masks_data = rng.rand(3, H, W) - >>> gt_masks = BitmapMasks(gt_masks_data, height=H, width=W) - >>> # Predicted positive boxes in one image - >>> pos_proposals = torch.FloatTensor([ - >>> [ 16.2, 5.5, 19.9, 20.9], - >>> [ 17.3, 13.6, 19.3, 19.3], - >>> [ 14.8, 16.4, 17.0, 23.7], - >>> [ 0.0, 0.0, 16.0, 16.0], - >>> [ 4.0, 0.0, 20.0, 16.0], - >>> ]) - >>> # For each predicted proposal, its assignment to a gt mask - >>> pos_assigned_gt_inds = torch.LongTensor([0, 1, 2, 1, 1]) - >>> mask_targets = mask_target_single( - >>> pos_proposals, pos_assigned_gt_inds, gt_masks, cfg) - >>> assert mask_targets.shape == (5,) + cfg['mask_size'] - """ - device = pos_proposals.device - mask_size = _pair(cfg.mask_size) - binarize = not cfg.get('soft_mask_target', False) - num_pos = pos_proposals.size(0) - if num_pos > 0: - proposals_np = pos_proposals.cpu().numpy() - maxh, maxw = gt_masks.height, gt_masks.width - proposals_np[:, [0, 2]] = np.clip(proposals_np[:, [0, 2]], 0, maxw) - proposals_np[:, [1, 3]] = np.clip(proposals_np[:, [1, 3]], 0, maxh) - pos_assigned_gt_inds = pos_assigned_gt_inds.cpu().numpy() - - mask_targets = gt_masks.crop_and_resize( - proposals_np, - mask_size, - device=device, - inds=pos_assigned_gt_inds, - binarize=binarize).to_ndarray() - - mask_targets = torch.from_numpy(mask_targets).float().to(device) - else: - mask_targets = pos_proposals.new_zeros((0, ) + mask_size) - - return mask_targets diff --git a/cv/detection/autoassign/pytorch/mmdet/core/mask/structures.py b/cv/detection/autoassign/pytorch/mmdet/core/mask/structures.py deleted file mode 100755 index bca614c71..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/mask/structures.py +++ /dev/null @@ -1,1102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -import cv2 -import mmcv -import numpy as np -import pycocotools.mask as maskUtils -import torch -from mmcv.ops.roi_align import roi_align - - -class BaseInstanceMasks(metaclass=ABCMeta): - """Base class for instance masks.""" - - @abstractmethod - def rescale(self, scale, interpolation='nearest'): - """Rescale masks as large as possible while keeping the aspect ratio. - For details can refer to `mmcv.imrescale`. - - Args: - scale (tuple[int]): The maximum size (h, w) of rescaled mask. - interpolation (str): Same as :func:`mmcv.imrescale`. - - Returns: - BaseInstanceMasks: The rescaled masks. - """ - - @abstractmethod - def resize(self, out_shape, interpolation='nearest'): - """Resize masks to the given out_shape. - - Args: - out_shape: Target (h, w) of resized mask. - interpolation (str): See :func:`mmcv.imresize`. - - Returns: - BaseInstanceMasks: The resized masks. - """ - - @abstractmethod - def flip(self, flip_direction='horizontal'): - """Flip masks alone the given direction. - - Args: - flip_direction (str): Either 'horizontal' or 'vertical'. - - Returns: - BaseInstanceMasks: The flipped masks. - """ - - @abstractmethod - def pad(self, out_shape, pad_val): - """Pad masks to the given size of (h, w). - - Args: - out_shape (tuple[int]): Target (h, w) of padded mask. - pad_val (int): The padded value. - - Returns: - BaseInstanceMasks: The padded masks. - """ - - @abstractmethod - def crop(self, bbox): - """Crop each mask by the given bbox. - - Args: - bbox (ndarray): Bbox in format [x1, y1, x2, y2], shape (4, ). - - Return: - BaseInstanceMasks: The cropped masks. - """ - - @abstractmethod - def crop_and_resize(self, - bboxes, - out_shape, - inds, - device, - interpolation='bilinear', - binarize=True): - """Crop and resize masks by the given bboxes. - - This function is mainly used in mask targets computation. - It firstly align mask to bboxes by assigned_inds, then crop mask by the - assigned bbox and resize to the size of (mask_h, mask_w) - - Args: - bboxes (Tensor): Bboxes in format [x1, y1, x2, y2], shape (N, 4) - out_shape (tuple[int]): Target (h, w) of resized mask - inds (ndarray): Indexes to assign masks to each bbox, - shape (N,) and values should be between [0, num_masks - 1]. - device (str): Device of bboxes - interpolation (str): See `mmcv.imresize` - binarize (bool): if True fractional values are rounded to 0 or 1 - after the resize operation. if False and unsupported an error - will be raised. Defaults to True. - - Return: - BaseInstanceMasks: the cropped and resized masks. - """ - - @abstractmethod - def expand(self, expanded_h, expanded_w, top, left): - """see :class:`Expand`.""" - - @property - @abstractmethod - def areas(self): - """ndarray: areas of each instance.""" - - @abstractmethod - def to_ndarray(self): - """Convert masks to the format of ndarray. - - Return: - ndarray: Converted masks in the format of ndarray. - """ - - @abstractmethod - def to_tensor(self, dtype, device): - """Convert masks to the format of Tensor. - - Args: - dtype (str): Dtype of converted mask. - device (torch.device): Device of converted masks. - - Returns: - Tensor: Converted masks in the format of Tensor. - """ - - @abstractmethod - def translate(self, - out_shape, - offset, - direction='horizontal', - fill_val=0, - interpolation='bilinear'): - """Translate the masks. - - Args: - out_shape (tuple[int]): Shape for output mask, format (h, w). - offset (int | float): The offset for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - fill_val (int | float): Border value. Default 0. - interpolation (str): Same as :func:`mmcv.imtranslate`. - - Returns: - Translated masks. - """ - - def shear(self, - out_shape, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear the masks. - - Args: - out_shape (tuple[int]): Shape for output mask, format (h, w). - magnitude (int | float): The magnitude used for shear. - direction (str): The shear direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. Default 0. - interpolation (str): Same as in :func:`mmcv.imshear`. - - Returns: - ndarray: Sheared masks. - """ - - @abstractmethod - def rotate(self, out_shape, angle, center=None, scale=1.0, fill_val=0): - """Rotate the masks. - - Args: - out_shape (tuple[int]): Shape for output mask, format (h, w). - angle (int | float): Rotation angle in degrees. Positive values - mean counter-clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the - rotation in source image. If not specified, the center of - the image will be used. - scale (int | float): Isotropic scale factor. - fill_val (int | float): Border value. Default 0 for masks. - - Returns: - Rotated masks. - """ - - -class BitmapMasks(BaseInstanceMasks): - """This class represents masks in the form of bitmaps. - - Args: - masks (ndarray): ndarray of masks in shape (N, H, W), where N is - the number of objects. - height (int): height of masks - width (int): width of masks - - Example: - >>> from mmdet.core.mask.structures import * # NOQA - >>> num_masks, H, W = 3, 32, 32 - >>> rng = np.random.RandomState(0) - >>> masks = (rng.rand(num_masks, H, W) > 0.1).astype(np.int) - >>> self = BitmapMasks(masks, height=H, width=W) - - >>> # demo crop_and_resize - >>> num_boxes = 5 - >>> bboxes = np.array([[0, 0, 30, 10.0]] * num_boxes) - >>> out_shape = (14, 14) - >>> inds = torch.randint(0, len(self), size=(num_boxes,)) - >>> device = 'cpu' - >>> interpolation = 'bilinear' - >>> new = self.crop_and_resize( - ... bboxes, out_shape, inds, device, interpolation) - >>> assert len(new) == num_boxes - >>> assert new.height, new.width == out_shape - """ - - def __init__(self, masks, height, width): - self.height = height - self.width = width - if len(masks) == 0: - self.masks = np.empty((0, self.height, self.width), dtype=np.uint8) - else: - assert isinstance(masks, (list, np.ndarray)) - if isinstance(masks, list): - assert isinstance(masks[0], np.ndarray) - assert masks[0].ndim == 2 # (H, W) - else: - assert masks.ndim == 3 # (N, H, W) - - self.masks = np.stack(masks).reshape(-1, height, width) - assert self.masks.shape[1] == self.height - assert self.masks.shape[2] == self.width - - def __getitem__(self, index): - """Index the BitmapMask. - - Args: - index (int | ndarray): Indices in the format of integer or ndarray. - - Returns: - :obj:`BitmapMasks`: Indexed bitmap masks. - """ - masks = self.masks[index].reshape(-1, self.height, self.width) - return BitmapMasks(masks, self.height, self.width) - - def __iter__(self): - return iter(self.masks) - - def __repr__(self): - s = self.__class__.__name__ + '(' - s += f'num_masks={len(self.masks)}, ' - s += f'height={self.height}, ' - s += f'width={self.width})' - return s - - def __len__(self): - """Number of masks.""" - return len(self.masks) - - def rescale(self, scale, interpolation='nearest'): - """See :func:`BaseInstanceMasks.rescale`.""" - if len(self.masks) == 0: - new_w, new_h = mmcv.rescale_size((self.width, self.height), scale) - rescaled_masks = np.empty((0, new_h, new_w), dtype=np.uint8) - else: - rescaled_masks = np.stack([ - mmcv.imrescale(mask, scale, interpolation=interpolation) - for mask in self.masks - ]) - height, width = rescaled_masks.shape[1:] - return BitmapMasks(rescaled_masks, height, width) - - def resize(self, out_shape, interpolation='nearest'): - """See :func:`BaseInstanceMasks.resize`.""" - if len(self.masks) == 0: - resized_masks = np.empty((0, *out_shape), dtype=np.uint8) - else: - resized_masks = np.stack([ - mmcv.imresize( - mask, out_shape[::-1], interpolation=interpolation) - for mask in self.masks - ]) - return BitmapMasks(resized_masks, *out_shape) - - def flip(self, flip_direction='horizontal'): - """See :func:`BaseInstanceMasks.flip`.""" - assert flip_direction in ('horizontal', 'vertical', 'diagonal') - - if len(self.masks) == 0: - flipped_masks = self.masks - else: - flipped_masks = np.stack([ - mmcv.imflip(mask, direction=flip_direction) - for mask in self.masks - ]) - return BitmapMasks(flipped_masks, self.height, self.width) - - def pad(self, out_shape, pad_val=0): - """See :func:`BaseInstanceMasks.pad`.""" - if len(self.masks) == 0: - padded_masks = np.empty((0, *out_shape), dtype=np.uint8) - else: - padded_masks = np.stack([ - mmcv.impad(mask, shape=out_shape, pad_val=pad_val) - for mask in self.masks - ]) - return BitmapMasks(padded_masks, *out_shape) - - def crop(self, bbox): - """See :func:`BaseInstanceMasks.crop`.""" - assert isinstance(bbox, np.ndarray) - assert bbox.ndim == 1 - - # clip the boundary - bbox = bbox.copy() - bbox[0::2] = np.clip(bbox[0::2], 0, self.width) - bbox[1::2] = np.clip(bbox[1::2], 0, self.height) - x1, y1, x2, y2 = bbox - w = np.maximum(x2 - x1, 1) - h = np.maximum(y2 - y1, 1) - - if len(self.masks) == 0: - cropped_masks = np.empty((0, h, w), dtype=np.uint8) - else: - cropped_masks = self.masks[:, y1:y1 + h, x1:x1 + w] - return BitmapMasks(cropped_masks, h, w) - - def crop_and_resize(self, - bboxes, - out_shape, - inds, - device='cpu', - interpolation='bilinear', - binarize=True): - """See :func:`BaseInstanceMasks.crop_and_resize`.""" - if len(self.masks) == 0: - empty_masks = np.empty((0, *out_shape), dtype=np.uint8) - return BitmapMasks(empty_masks, *out_shape) - - # convert bboxes to tensor - if isinstance(bboxes, np.ndarray): - bboxes = torch.from_numpy(bboxes).to(device=device) - if isinstance(inds, np.ndarray): - inds = torch.from_numpy(inds).to(device=device) - - num_bbox = bboxes.shape[0] - fake_inds = torch.arange( - num_bbox, device=device).to(dtype=bboxes.dtype)[:, None] - rois = torch.cat([fake_inds, bboxes], dim=1) # Nx5 - rois = rois.to(device=device) - if num_bbox > 0: - gt_masks_th = torch.from_numpy(self.masks).to(device).index_select( - 0, inds).to(dtype=rois.dtype) - targets = roi_align(gt_masks_th[:, None, :, :], rois, out_shape, - 1.0, 0, 'avg', True).squeeze(1) - if binarize: - resized_masks = (targets >= 0.5).cpu().numpy() - else: - resized_masks = targets.cpu().numpy() - else: - resized_masks = [] - return BitmapMasks(resized_masks, *out_shape) - - def expand(self, expanded_h, expanded_w, top, left): - """See :func:`BaseInstanceMasks.expand`.""" - if len(self.masks) == 0: - expanded_mask = np.empty((0, expanded_h, expanded_w), - dtype=np.uint8) - else: - expanded_mask = np.zeros((len(self), expanded_h, expanded_w), - dtype=np.uint8) - expanded_mask[:, top:top + self.height, - left:left + self.width] = self.masks - return BitmapMasks(expanded_mask, expanded_h, expanded_w) - - def translate(self, - out_shape, - offset, - direction='horizontal', - fill_val=0, - interpolation='bilinear'): - """Translate the BitmapMasks. - - Args: - out_shape (tuple[int]): Shape for output mask, format (h, w). - offset (int | float): The offset for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - fill_val (int | float): Border value. Default 0 for masks. - interpolation (str): Same as :func:`mmcv.imtranslate`. - - Returns: - BitmapMasks: Translated BitmapMasks. - - Example: - >>> from mmdet.core.mask.structures import BitmapMasks - >>> self = BitmapMasks.random(dtype=np.uint8) - >>> out_shape = (32, 32) - >>> offset = 4 - >>> direction = 'horizontal' - >>> fill_val = 0 - >>> interpolation = 'bilinear' - >>> # Note, There seem to be issues when: - >>> # * out_shape is different than self's shape - >>> # * the mask dtype is not supported by cv2.AffineWarp - >>> new = self.translate(out_shape, offset, direction, fill_val, - >>> interpolation) - >>> assert len(new) == len(self) - >>> assert new.height, new.width == out_shape - """ - if len(self.masks) == 0: - translated_masks = np.empty((0, *out_shape), dtype=np.uint8) - else: - translated_masks = mmcv.imtranslate( - self.masks.transpose((1, 2, 0)), - offset, - direction, - border_value=fill_val, - interpolation=interpolation) - if translated_masks.ndim == 2: - translated_masks = translated_masks[:, :, None] - translated_masks = translated_masks.transpose( - (2, 0, 1)).astype(self.masks.dtype) - return BitmapMasks(translated_masks, *out_shape) - - def shear(self, - out_shape, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear the BitmapMasks. - - Args: - out_shape (tuple[int]): Shape for output mask, format (h, w). - magnitude (int | float): The magnitude used for shear. - direction (str): The shear direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as in :func:`mmcv.imshear`. - - Returns: - BitmapMasks: The sheared masks. - """ - if len(self.masks) == 0: - sheared_masks = np.empty((0, *out_shape), dtype=np.uint8) - else: - sheared_masks = mmcv.imshear( - self.masks.transpose((1, 2, 0)), - magnitude, - direction, - border_value=border_value, - interpolation=interpolation) - if sheared_masks.ndim == 2: - sheared_masks = sheared_masks[:, :, None] - sheared_masks = sheared_masks.transpose( - (2, 0, 1)).astype(self.masks.dtype) - return BitmapMasks(sheared_masks, *out_shape) - - def rotate(self, out_shape, angle, center=None, scale=1.0, fill_val=0): - """Rotate the BitmapMasks. - - Args: - out_shape (tuple[int]): Shape for output mask, format (h, w). - angle (int | float): Rotation angle in degrees. Positive values - mean counter-clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the - rotation in source image. If not specified, the center of - the image will be used. - scale (int | float): Isotropic scale factor. - fill_val (int | float): Border value. Default 0 for masks. - - Returns: - BitmapMasks: Rotated BitmapMasks. - """ - if len(self.masks) == 0: - rotated_masks = np.empty((0, *out_shape), dtype=self.masks.dtype) - else: - rotated_masks = mmcv.imrotate( - self.masks.transpose((1, 2, 0)), - angle, - center=center, - scale=scale, - border_value=fill_val) - if rotated_masks.ndim == 2: - # case when only one mask, (h, w) - rotated_masks = rotated_masks[:, :, None] # (h, w, 1) - rotated_masks = rotated_masks.transpose( - (2, 0, 1)).astype(self.masks.dtype) - return BitmapMasks(rotated_masks, *out_shape) - - @property - def areas(self): - """See :py:attr:`BaseInstanceMasks.areas`.""" - return self.masks.sum((1, 2)) - - def to_ndarray(self): - """See :func:`BaseInstanceMasks.to_ndarray`.""" - return self.masks - - def to_tensor(self, dtype, device): - """See :func:`BaseInstanceMasks.to_tensor`.""" - return torch.tensor(self.masks, dtype=dtype, device=device) - - @classmethod - def random(cls, - num_masks=3, - height=32, - width=32, - dtype=np.uint8, - rng=None): - """Generate random bitmap masks for demo / testing purposes. - - Example: - >>> from mmdet.core.mask.structures import BitmapMasks - >>> self = BitmapMasks.random() - >>> print('self = {}'.format(self)) - self = BitmapMasks(num_masks=3, height=32, width=32) - """ - from mmdet.utils.util_random import ensure_rng - rng = ensure_rng(rng) - masks = (rng.rand(num_masks, height, width) > 0.1).astype(dtype) - self = cls(masks, height=height, width=width) - return self - - def get_bboxes(self): - num_masks = len(self) - boxes = np.zeros((num_masks, 4), dtype=np.float32) - x_any = self.masks.any(axis=1) - y_any = self.masks.any(axis=2) - for idx in range(num_masks): - x = np.where(x_any[idx, :])[0] - y = np.where(y_any[idx, :])[0] - if len(x) > 0 and len(y) > 0: - # use +1 for x_max and y_max so that the right and bottom - # boundary of instance masks are fully included by the box - boxes[idx, :] = np.array([x[0], y[0], x[-1] + 1, y[-1] + 1], - dtype=np.float32) - return boxes - - -class PolygonMasks(BaseInstanceMasks): - """This class represents masks in the form of polygons. - - Polygons is a list of three levels. The first level of the list - corresponds to objects, the second level to the polys that compose the - object, the third level to the poly coordinates - - Args: - masks (list[list[ndarray]]): The first level of the list - corresponds to objects, the second level to the polys that - compose the object, the third level to the poly coordinates - height (int): height of masks - width (int): width of masks - - Example: - >>> from mmdet.core.mask.structures import * # NOQA - >>> masks = [ - >>> [ np.array([0, 0, 10, 0, 10, 10., 0, 10, 0, 0]) ] - >>> ] - >>> height, width = 16, 16 - >>> self = PolygonMasks(masks, height, width) - - >>> # demo translate - >>> new = self.translate((16, 16), 4., direction='horizontal') - >>> assert np.all(new.masks[0][0][1::2] == masks[0][0][1::2]) - >>> assert np.all(new.masks[0][0][0::2] == masks[0][0][0::2] + 4) - - >>> # demo crop_and_resize - >>> num_boxes = 3 - >>> bboxes = np.array([[0, 0, 30, 10.0]] * num_boxes) - >>> out_shape = (16, 16) - >>> inds = torch.randint(0, len(self), size=(num_boxes,)) - >>> device = 'cpu' - >>> interpolation = 'bilinear' - >>> new = self.crop_and_resize( - ... bboxes, out_shape, inds, device, interpolation) - >>> assert len(new) == num_boxes - >>> assert new.height, new.width == out_shape - """ - - def __init__(self, masks, height, width): - assert isinstance(masks, list) - if len(masks) > 0: - assert isinstance(masks[0], list) - assert isinstance(masks[0][0], np.ndarray) - - self.height = height - self.width = width - self.masks = masks - - def __getitem__(self, index): - """Index the polygon masks. - - Args: - index (ndarray | List): The indices. - - Returns: - :obj:`PolygonMasks`: The indexed polygon masks. - """ - if isinstance(index, np.ndarray): - index = index.tolist() - if isinstance(index, list): - masks = [self.masks[i] for i in index] - else: - try: - masks = self.masks[index] - except Exception: - raise ValueError( - f'Unsupported input of type {type(index)} for indexing!') - if len(masks) and isinstance(masks[0], np.ndarray): - masks = [masks] # ensure a list of three levels - return PolygonMasks(masks, self.height, self.width) - - def __iter__(self): - return iter(self.masks) - - def __repr__(self): - s = self.__class__.__name__ + '(' - s += f'num_masks={len(self.masks)}, ' - s += f'height={self.height}, ' - s += f'width={self.width})' - return s - - def __len__(self): - """Number of masks.""" - return len(self.masks) - - def rescale(self, scale, interpolation=None): - """see :func:`BaseInstanceMasks.rescale`""" - new_w, new_h = mmcv.rescale_size((self.width, self.height), scale) - if len(self.masks) == 0: - rescaled_masks = PolygonMasks([], new_h, new_w) - else: - rescaled_masks = self.resize((new_h, new_w)) - return rescaled_masks - - def resize(self, out_shape, interpolation=None): - """see :func:`BaseInstanceMasks.resize`""" - if len(self.masks) == 0: - resized_masks = PolygonMasks([], *out_shape) - else: - h_scale = out_shape[0] / self.height - w_scale = out_shape[1] / self.width - resized_masks = [] - for poly_per_obj in self.masks: - resized_poly = [] - for p in poly_per_obj: - p = p.copy() - p[0::2] = p[0::2] * w_scale - p[1::2] = p[1::2] * h_scale - resized_poly.append(p) - resized_masks.append(resized_poly) - resized_masks = PolygonMasks(resized_masks, *out_shape) - return resized_masks - - def flip(self, flip_direction='horizontal'): - """see :func:`BaseInstanceMasks.flip`""" - assert flip_direction in ('horizontal', 'vertical', 'diagonal') - if len(self.masks) == 0: - flipped_masks = PolygonMasks([], self.height, self.width) - else: - flipped_masks = [] - for poly_per_obj in self.masks: - flipped_poly_per_obj = [] - for p in poly_per_obj: - p = p.copy() - if flip_direction == 'horizontal': - p[0::2] = self.width - p[0::2] - elif flip_direction == 'vertical': - p[1::2] = self.height - p[1::2] - else: - p[0::2] = self.width - p[0::2] - p[1::2] = self.height - p[1::2] - flipped_poly_per_obj.append(p) - flipped_masks.append(flipped_poly_per_obj) - flipped_masks = PolygonMasks(flipped_masks, self.height, - self.width) - return flipped_masks - - def crop(self, bbox): - """see :func:`BaseInstanceMasks.crop`""" - assert isinstance(bbox, np.ndarray) - assert bbox.ndim == 1 - - # clip the boundary - bbox = bbox.copy() - bbox[0::2] = np.clip(bbox[0::2], 0, self.width) - bbox[1::2] = np.clip(bbox[1::2], 0, self.height) - x1, y1, x2, y2 = bbox - w = np.maximum(x2 - x1, 1) - h = np.maximum(y2 - y1, 1) - - if len(self.masks) == 0: - cropped_masks = PolygonMasks([], h, w) - else: - cropped_masks = [] - for poly_per_obj in self.masks: - cropped_poly_per_obj = [] - for p in poly_per_obj: - # pycocotools will clip the boundary - p = p.copy() - p[0::2] = p[0::2] - bbox[0] - p[1::2] = p[1::2] - bbox[1] - cropped_poly_per_obj.append(p) - cropped_masks.append(cropped_poly_per_obj) - cropped_masks = PolygonMasks(cropped_masks, h, w) - return cropped_masks - - def pad(self, out_shape, pad_val=0): - """padding has no effect on polygons`""" - return PolygonMasks(self.masks, *out_shape) - - def expand(self, *args, **kwargs): - """TODO: Add expand for polygon""" - raise NotImplementedError - - def crop_and_resize(self, - bboxes, - out_shape, - inds, - device='cpu', - interpolation='bilinear', - binarize=True): - """see :func:`BaseInstanceMasks.crop_and_resize`""" - out_h, out_w = out_shape - if len(self.masks) == 0: - return PolygonMasks([], out_h, out_w) - - if not binarize: - raise ValueError('Polygons are always binary, ' - 'setting binarize=False is unsupported') - - resized_masks = [] - for i in range(len(bboxes)): - mask = self.masks[inds[i]] - bbox = bboxes[i, :] - x1, y1, x2, y2 = bbox - w = np.maximum(x2 - x1, 1) - h = np.maximum(y2 - y1, 1) - h_scale = out_h / max(h, 0.1) # avoid too large scale - w_scale = out_w / max(w, 0.1) - - resized_mask = [] - for p in mask: - p = p.copy() - # crop - # pycocotools will clip the boundary - p[0::2] = p[0::2] - bbox[0] - p[1::2] = p[1::2] - bbox[1] - - # resize - p[0::2] = p[0::2] * w_scale - p[1::2] = p[1::2] * h_scale - resized_mask.append(p) - resized_masks.append(resized_mask) - return PolygonMasks(resized_masks, *out_shape) - - def translate(self, - out_shape, - offset, - direction='horizontal', - fill_val=None, - interpolation=None): - """Translate the PolygonMasks. - - Example: - >>> self = PolygonMasks.random(dtype=np.int) - >>> out_shape = (self.height, self.width) - >>> new = self.translate(out_shape, 4., direction='horizontal') - >>> assert np.all(new.masks[0][0][1::2] == self.masks[0][0][1::2]) - >>> assert np.all(new.masks[0][0][0::2] == self.masks[0][0][0::2] + 4) # noqa: E501 - """ - assert fill_val is None or fill_val == 0, 'Here fill_val is not '\ - f'used, and defaultly should be None or 0. got {fill_val}.' - if len(self.masks) == 0: - translated_masks = PolygonMasks([], *out_shape) - else: - translated_masks = [] - for poly_per_obj in self.masks: - translated_poly_per_obj = [] - for p in poly_per_obj: - p = p.copy() - if direction == 'horizontal': - p[0::2] = np.clip(p[0::2] + offset, 0, out_shape[1]) - elif direction == 'vertical': - p[1::2] = np.clip(p[1::2] + offset, 0, out_shape[0]) - translated_poly_per_obj.append(p) - translated_masks.append(translated_poly_per_obj) - translated_masks = PolygonMasks(translated_masks, *out_shape) - return translated_masks - - def shear(self, - out_shape, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """See :func:`BaseInstanceMasks.shear`.""" - if len(self.masks) == 0: - sheared_masks = PolygonMasks([], *out_shape) - else: - sheared_masks = [] - if direction == 'horizontal': - shear_matrix = np.stack([[1, magnitude], - [0, 1]]).astype(np.float32) - elif direction == 'vertical': - shear_matrix = np.stack([[1, 0], [magnitude, - 1]]).astype(np.float32) - for poly_per_obj in self.masks: - sheared_poly = [] - for p in poly_per_obj: - p = np.stack([p[0::2], p[1::2]], axis=0) # [2, n] - new_coords = np.matmul(shear_matrix, p) # [2, n] - new_coords[0, :] = np.clip(new_coords[0, :], 0, - out_shape[1]) - new_coords[1, :] = np.clip(new_coords[1, :], 0, - out_shape[0]) - sheared_poly.append( - new_coords.transpose((1, 0)).reshape(-1)) - sheared_masks.append(sheared_poly) - sheared_masks = PolygonMasks(sheared_masks, *out_shape) - return sheared_masks - - def rotate(self, out_shape, angle, center=None, scale=1.0, fill_val=0): - """See :func:`BaseInstanceMasks.rotate`.""" - if len(self.masks) == 0: - rotated_masks = PolygonMasks([], *out_shape) - else: - rotated_masks = [] - rotate_matrix = cv2.getRotationMatrix2D(center, -angle, scale) - for poly_per_obj in self.masks: - rotated_poly = [] - for p in poly_per_obj: - p = p.copy() - coords = np.stack([p[0::2], p[1::2]], axis=1) # [n, 2] - # pad 1 to convert from format [x, y] to homogeneous - # coordinates format [x, y, 1] - coords = np.concatenate( - (coords, np.ones((coords.shape[0], 1), coords.dtype)), - axis=1) # [n, 3] - rotated_coords = np.matmul( - rotate_matrix[None, :, :], - coords[:, :, None])[..., 0] # [n, 2, 1] -> [n, 2] - rotated_coords[:, 0] = np.clip(rotated_coords[:, 0], 0, - out_shape[1]) - rotated_coords[:, 1] = np.clip(rotated_coords[:, 1], 0, - out_shape[0]) - rotated_poly.append(rotated_coords.reshape(-1)) - rotated_masks.append(rotated_poly) - rotated_masks = PolygonMasks(rotated_masks, *out_shape) - return rotated_masks - - def to_bitmap(self): - """convert polygon masks to bitmap masks.""" - bitmap_masks = self.to_ndarray() - return BitmapMasks(bitmap_masks, self.height, self.width) - - @property - def areas(self): - """Compute areas of masks. - - This func is modified from `detectron2 - `_. - The function only works with Polygons using the shoelace formula. - - Return: - ndarray: areas of each instance - """ # noqa: W501 - area = [] - for polygons_per_obj in self.masks: - area_per_obj = 0 - for p in polygons_per_obj: - area_per_obj += self._polygon_area(p[0::2], p[1::2]) - area.append(area_per_obj) - return np.asarray(area) - - def _polygon_area(self, x, y): - """Compute the area of a component of a polygon. - - Using the shoelace formula: - https://stackoverflow.com/questions/24467972/calculate-area-of-polygon-given-x-y-coordinates - - Args: - x (ndarray): x coordinates of the component - y (ndarray): y coordinates of the component - - Return: - float: the are of the component - """ # noqa: 501 - return 0.5 * np.abs( - np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1))) - - def to_ndarray(self): - """Convert masks to the format of ndarray.""" - if len(self.masks) == 0: - return np.empty((0, self.height, self.width), dtype=np.uint8) - bitmap_masks = [] - for poly_per_obj in self.masks: - bitmap_masks.append( - polygon_to_bitmap(poly_per_obj, self.height, self.width)) - return np.stack(bitmap_masks) - - def to_tensor(self, dtype, device): - """See :func:`BaseInstanceMasks.to_tensor`.""" - if len(self.masks) == 0: - return torch.empty((0, self.height, self.width), - dtype=dtype, - device=device) - ndarray_masks = self.to_ndarray() - return torch.tensor(ndarray_masks, dtype=dtype, device=device) - - @classmethod - def random(cls, - num_masks=3, - height=32, - width=32, - n_verts=5, - dtype=np.float32, - rng=None): - """Generate random polygon masks for demo / testing purposes. - - Adapted from [1]_ - - References: - .. [1] https://gitlab.kitware.com/computer-vision/kwimage/-/blob/928cae35ca8/kwimage/structs/polygon.py#L379 # noqa: E501 - - Example: - >>> from mmdet.core.mask.structures import PolygonMasks - >>> self = PolygonMasks.random() - >>> print('self = {}'.format(self)) - """ - from mmdet.utils.util_random import ensure_rng - rng = ensure_rng(rng) - - def _gen_polygon(n, irregularity, spikeyness): - """Creates the polygon by sampling points on a circle around the - centre. Random noise is added by varying the angular spacing - between sequential points, and by varying the radial distance of - each point from the centre. - - Based on original code by Mike Ounsworth - - Args: - n (int): number of vertices - irregularity (float): [0,1] indicating how much variance there - is in the angular spacing of vertices. [0,1] will map to - [0, 2pi/numberOfVerts] - spikeyness (float): [0,1] indicating how much variance there is - in each vertex from the circle of radius aveRadius. [0,1] - will map to [0, aveRadius] - - Returns: - a list of vertices, in CCW order. - """ - from scipy.stats import truncnorm - - # Generate around the unit circle - cx, cy = (0.0, 0.0) - radius = 1 - - tau = np.pi * 2 - - irregularity = np.clip(irregularity, 0, 1) * 2 * np.pi / n - spikeyness = np.clip(spikeyness, 1e-9, 1) - - # generate n angle steps - lower = (tau / n) - irregularity - upper = (tau / n) + irregularity - angle_steps = rng.uniform(lower, upper, n) - - # normalize the steps so that point 0 and point n+1 are the same - k = angle_steps.sum() / (2 * np.pi) - angles = (angle_steps / k).cumsum() + rng.uniform(0, tau) - - # Convert high and low values to be wrt the standard normal range - # https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.truncnorm.html - low = 0 - high = 2 * radius - mean = radius - std = spikeyness - a = (low - mean) / std - b = (high - mean) / std - tnorm = truncnorm(a=a, b=b, loc=mean, scale=std) - - # now generate the points - radii = tnorm.rvs(n, random_state=rng) - x_pts = cx + radii * np.cos(angles) - y_pts = cy + radii * np.sin(angles) - - points = np.hstack([x_pts[:, None], y_pts[:, None]]) - - # Scale to 0-1 space - points = points - points.min(axis=0) - points = points / points.max(axis=0) - - # Randomly place within 0-1 space - points = points * (rng.rand() * .8 + .2) - min_pt = points.min(axis=0) - max_pt = points.max(axis=0) - - high = (1 - max_pt) - low = (0 - min_pt) - offset = (rng.rand(2) * (high - low)) + low - points = points + offset - return points - - def _order_vertices(verts): - """ - References: - https://stackoverflow.com/questions/1709283/how-can-i-sort-a-coordinate-list-for-a-rectangle-counterclockwise - """ - mlat = verts.T[0].sum() / len(verts) - mlng = verts.T[1].sum() / len(verts) - - tau = np.pi * 2 - angle = (np.arctan2(mlat - verts.T[0], verts.T[1] - mlng) + - tau) % tau - sortx = angle.argsort() - verts = verts.take(sortx, axis=0) - return verts - - # Generate a random exterior for each requested mask - masks = [] - for _ in range(num_masks): - exterior = _order_vertices(_gen_polygon(n_verts, 0.9, 0.9)) - exterior = (exterior * [(width, height)]).astype(dtype) - masks.append([exterior.ravel()]) - - self = cls(masks, height, width) - return self - - def get_bboxes(self): - num_masks = len(self) - boxes = np.zeros((num_masks, 4), dtype=np.float32) - for idx, poly_per_obj in enumerate(self.masks): - # simply use a number that is big enough for comparison with - # coordinates - xy_min = np.array([self.width * 2, self.height * 2], - dtype=np.float32) - xy_max = np.zeros(2, dtype=np.float32) - for p in poly_per_obj: - xy = np.array(p).reshape(-1, 2).astype(np.float32) - xy_min = np.minimum(xy_min, np.min(xy, axis=0)) - xy_max = np.maximum(xy_max, np.max(xy, axis=0)) - boxes[idx, :2] = xy_min - boxes[idx, 2:] = xy_max - - return boxes - - -def polygon_to_bitmap(polygons, height, width): - """Convert masks from the form of polygons to bitmaps. - - Args: - polygons (list[ndarray]): masks in polygon representation - height (int): mask height - width (int): mask width - - Return: - ndarray: the converted masks in bitmap representation - """ - rles = maskUtils.frPyObjects(polygons, height, width) - rle = maskUtils.merge(rles) - bitmap_mask = maskUtils.decode(rle).astype(np.bool) - return bitmap_mask - - -def bitmap_to_polygon(bitmap): - """Convert masks from the form of bitmaps to polygons. - - Args: - bitmap (ndarray): masks in bitmap representation. - - Return: - list[ndarray]: the converted mask in polygon representation. - bool: whether the mask has holes. - """ - bitmap = np.ascontiguousarray(bitmap).astype(np.uint8) - # cv2.RETR_CCOMP: retrieves all of the contours and organizes them - # into a two-level hierarchy. At the top level, there are external - # boundaries of the components. At the second level, there are - # boundaries of the holes. If there is another contour inside a hole - # of a connected component, it is still put at the top level. - # cv2.CHAIN_APPROX_NONE: stores absolutely all the contour points. - outs = cv2.findContours(bitmap, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE) - contours = outs[-2] - hierarchy = outs[-1] - if hierarchy is None: - return [], False - # hierarchy[i]: 4 elements, for the indexes of next, previous, - # parent, or nested contours. If there is no corresponding contour, - # it will be -1. - with_hole = (hierarchy.reshape(-1, 4)[:, 3] >= 0).any() - contours = [c.reshape(-1, 2) for c in contours] - return contours, with_hole diff --git a/cv/detection/autoassign/pytorch/mmdet/core/mask/utils.py b/cv/detection/autoassign/pytorch/mmdet/core/mask/utils.py deleted file mode 100755 index 8aaa8a8ed..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/mask/utils.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np -import pycocotools.mask as mask_util -import torch - - -def split_combined_polys(polys, poly_lens, polys_per_mask): - """Split the combined 1-D polys into masks. - - A mask is represented as a list of polys, and a poly is represented as - a 1-D array. In dataset, all masks are concatenated into a single 1-D - tensor. Here we need to split the tensor into original representations. - - Args: - polys (list): a list (length = image num) of 1-D tensors - poly_lens (list): a list (length = image num) of poly length - polys_per_mask (list): a list (length = image num) of poly number - of each mask - - Returns: - list: a list (length = image num) of list (length = mask num) of \ - list (length = poly num) of numpy array. - """ - mask_polys_list = [] - for img_id in range(len(polys)): - polys_single = polys[img_id] - polys_lens_single = poly_lens[img_id].tolist() - polys_per_mask_single = polys_per_mask[img_id].tolist() - - split_polys = mmcv.slice_list(polys_single, polys_lens_single) - mask_polys = mmcv.slice_list(split_polys, polys_per_mask_single) - mask_polys_list.append(mask_polys) - return mask_polys_list - - -# TODO: move this function to more proper place -def encode_mask_results(mask_results): - """Encode bitmap mask to RLE code. - - Args: - mask_results (list | tuple[list]): bitmap mask results. - In mask scoring rcnn, mask_results is a tuple of (segm_results, - segm_cls_score). - - Returns: - list | tuple: RLE encoded mask. - """ - if isinstance(mask_results, tuple): # mask scoring - cls_segms, cls_mask_scores = mask_results - else: - cls_segms = mask_results - num_classes = len(cls_segms) - encoded_mask_results = [[] for _ in range(num_classes)] - for i in range(len(cls_segms)): - for cls_segm in cls_segms[i]: - encoded_mask_results[i].append( - mask_util.encode( - np.array( - cls_segm[:, :, np.newaxis], order='F', - dtype='uint8'))[0]) # encoded with RLE - if isinstance(mask_results, tuple): - return encoded_mask_results, cls_mask_scores - else: - return encoded_mask_results - - -def mask2bbox(masks): - """Obtain tight bounding boxes of binary masks. - - Args: - masks (Tensor): Binary mask of shape (n, h, w). - - Returns: - Tensor: Bboxe with shape (n, 4) of \ - positive region in binary mask. - """ - N = masks.shape[0] - bboxes = masks.new_zeros((N, 4), dtype=torch.float32) - x_any = torch.any(masks, dim=1) - y_any = torch.any(masks, dim=2) - for i in range(N): - x = torch.where(x_any[i, :])[0] - y = torch.where(y_any[i, :])[0] - if len(x) > 0 and len(y) > 0: - bboxes[i, :] = bboxes.new_tensor( - [x[0], y[0], x[-1] + 1, y[-1] + 1]) - - return bboxes diff --git a/cv/detection/autoassign/pytorch/mmdet/core/optimizers/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/optimizers/__init__.py deleted file mode 100755 index 7653ab5e8..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/optimizers/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import OPTIMIZER_BUILDERS, build_optimizer -from .layer_decay_optimizer_constructor import \ - LearningRateDecayOptimizerConstructor - -__all__ = [ - 'LearningRateDecayOptimizerConstructor', 'OPTIMIZER_BUILDERS', - 'build_optimizer' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/optimizers/builder.py b/cv/detection/autoassign/pytorch/mmdet/core/optimizers/builder.py deleted file mode 100755 index 25dacf4e7..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/optimizers/builder.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from mmcv.runner.optimizer import OPTIMIZER_BUILDERS as MMCV_OPTIMIZER_BUILDERS -from mmcv.utils import Registry, build_from_cfg - -OPTIMIZER_BUILDERS = Registry( - 'optimizer builder', parent=MMCV_OPTIMIZER_BUILDERS) - - -def build_optimizer_constructor(cfg): - constructor_type = cfg.get('type') - if constructor_type in OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - elif constructor_type in MMCV_OPTIMIZER_BUILDERS: - return build_from_cfg(cfg, MMCV_OPTIMIZER_BUILDERS) - else: - raise KeyError(f'{constructor_type} is not registered ' - 'in the optimizer builder registry.') - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/detection/autoassign/pytorch/mmdet/core/optimizers/layer_decay_optimizer_constructor.py b/cv/detection/autoassign/pytorch/mmdet/core/optimizers/layer_decay_optimizer_constructor.py deleted file mode 100755 index 20bc0d186..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/optimizers/layer_decay_optimizer_constructor.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -from mmcv.runner import DefaultOptimizerConstructor, get_dist_info - -from mmdet.utils import get_root_logger -from .builder import OPTIMIZER_BUILDERS - - -def get_layer_id_for_convnext(var_name, max_layer_id): - """Get the layer id to set the different learning rates in ``layer_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_layer_id (int): Maximum layer id. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - stage_id = int(var_name.split('.')[2]) - if stage_id == 0: - layer_id = 0 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - block_id = int(var_name.split('.')[3]) - if stage_id == 0: - layer_id = 1 - elif stage_id == 1: - layer_id = 2 - elif stage_id == 2: - layer_id = 3 + block_id // 3 - elif stage_id == 3: - layer_id = max_layer_id - return layer_id - else: - return max_layer_id + 1 - - -def get_stage_id_for_convnext(var_name, max_stage_id): - """Get the stage id to set the different learning rates in ``stage_wise`` - decay_type. - - Args: - var_name (str): The key of the model. - max_stage_id (int): Maximum stage id. - - Returns: - int: The id number corresponding to different learning rate in - ``LearningRateDecayOptimizerConstructor``. - """ - - if var_name in ('backbone.cls_token', 'backbone.mask_token', - 'backbone.pos_embed'): - return 0 - elif var_name.startswith('backbone.downsample_layers'): - return 0 - elif var_name.startswith('backbone.stages'): - stage_id = int(var_name.split('.')[2]) - return stage_id + 1 - else: - return max_stage_id - 1 - - -@OPTIMIZER_BUILDERS.register_module() -class LearningRateDecayOptimizerConstructor(DefaultOptimizerConstructor): - # Different learning rates are set for different layers of backbone. - # Note: Currently, this optimizer constructor is built for ConvNeXt. - - def add_params(self, params, module, **kwargs): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - """ - logger = get_root_logger() - - parameter_groups = {} - logger.info(f'self.paramwise_cfg is {self.paramwise_cfg}') - num_layers = self.paramwise_cfg.get('num_layers') + 2 - decay_rate = self.paramwise_cfg.get('decay_rate') - decay_type = self.paramwise_cfg.get('decay_type', 'layer_wise') - logger.info('Build LearningRateDecayOptimizerConstructor ' - f'{decay_type} {decay_rate} - {num_layers}') - weight_decay = self.base_wd - for name, param in module.named_parameters(): - if not param.requires_grad: - continue # frozen weights - if len(param.shape) == 1 or name.endswith('.bias') or name in ( - 'pos_embed', 'cls_token'): - group_name = 'no_decay' - this_weight_decay = 0. - else: - group_name = 'decay' - this_weight_decay = weight_decay - if 'layer_wise' in decay_type: - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_layer_id_for_convnext( - name, self.paramwise_cfg.get('num_layers')) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - elif decay_type == 'stage_wise': - if 'ConvNeXt' in module.backbone.__class__.__name__: - layer_id = get_stage_id_for_convnext(name, num_layers) - logger.info(f'set param {name} as id {layer_id}') - else: - raise NotImplementedError() - group_name = f'layer_{layer_id}_{group_name}' - - if group_name not in parameter_groups: - scale = decay_rate**(num_layers - layer_id - 1) - - parameter_groups[group_name] = { - 'weight_decay': this_weight_decay, - 'params': [], - 'param_names': [], - 'lr_scale': scale, - 'group_name': group_name, - 'lr': scale * self.base_lr, - } - - parameter_groups[group_name]['params'].append(param) - parameter_groups[group_name]['param_names'].append(name) - rank, _ = get_dist_info() - if rank == 0: - to_display = {} - for key in parameter_groups: - to_display[key] = { - 'param_names': parameter_groups[key]['param_names'], - 'lr_scale': parameter_groups[key]['lr_scale'], - 'lr': parameter_groups[key]['lr'], - 'weight_decay': parameter_groups[key]['weight_decay'], - } - logger.info(f'Param groups = {json.dumps(to_display, indent=2)}') - params.extend(parameter_groups.values()) diff --git a/cv/detection/autoassign/pytorch/mmdet/core/post_processing/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/post_processing/__init__.py deleted file mode 100755 index d0c756041..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/post_processing/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .bbox_nms import fast_nms, multiclass_nms -from .matrix_nms import mask_matrix_nms -from .merge_augs import (merge_aug_bboxes, merge_aug_masks, - merge_aug_proposals, merge_aug_scores) - -__all__ = [ - 'multiclass_nms', 'merge_aug_proposals', 'merge_aug_bboxes', - 'merge_aug_scores', 'merge_aug_masks', 'mask_matrix_nms', 'fast_nms' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/post_processing/bbox_nms.py b/cv/detection/autoassign/pytorch/mmdet/core/post_processing/bbox_nms.py deleted file mode 100755 index 5a66c69b7..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/post_processing/bbox_nms.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.ops.nms import batched_nms - -from mmdet.core.bbox.iou_calculators import bbox_overlaps - - -def multiclass_nms(multi_bboxes, - multi_scores, - score_thr, - nms_cfg, - max_num=-1, - score_factors=None, - return_inds=False): - """NMS for multi-class bboxes. - - Args: - multi_bboxes (Tensor): shape (n, #class*4) or (n, 4) - multi_scores (Tensor): shape (n, #class), where the last column - contains scores of the background class, but this will be ignored. - score_thr (float): bbox threshold, bboxes with scores lower than it - will not be considered. - nms_cfg (dict): a dict that contains the arguments of nms operations - max_num (int, optional): if there are more than max_num bboxes after - NMS, only top max_num will be kept. Default to -1. - score_factors (Tensor, optional): The factors multiplied to scores - before applying NMS. Default to None. - return_inds (bool, optional): Whether return the indices of kept - bboxes. Default to False. - - Returns: - tuple: (dets, labels, indices (optional)), tensors of shape (k, 5), - (k), and (k). Dets are boxes with scores. Labels are 0-based. - """ - num_classes = multi_scores.size(1) - 1 - # exclude background category - if multi_bboxes.shape[1] > 4: - bboxes = multi_bboxes.view(multi_scores.size(0), -1, 4) - else: - bboxes = multi_bboxes[:, None].expand( - multi_scores.size(0), num_classes, 4) - - scores = multi_scores[:, :-1] - - labels = torch.arange(num_classes, dtype=torch.long, device=scores.device) - labels = labels.view(1, -1).expand_as(scores) - - bboxes = bboxes.reshape(-1, 4) - scores = scores.reshape(-1) - labels = labels.reshape(-1) - - if not torch.onnx.is_in_onnx_export(): - # NonZero not supported in TensorRT - # remove low scoring boxes - valid_mask = scores > score_thr - # multiply score_factor after threshold to preserve more bboxes, improve - # mAP by 1% for YOLOv3 - if score_factors is not None: - # expand the shape to match original shape of score - score_factors = score_factors.view(-1, 1).expand( - multi_scores.size(0), num_classes) - score_factors = score_factors.reshape(-1) - scores = scores * score_factors - - if not torch.onnx.is_in_onnx_export(): - # NonZero not supported in TensorRT - inds = valid_mask.nonzero(as_tuple=False).squeeze(1) - bboxes, scores, labels = bboxes[inds], scores[inds], labels[inds] - else: - # TensorRT NMS plugin has invalid output filled with -1 - # add dummy data to make detection output correct. - bboxes = torch.cat([bboxes, bboxes.new_zeros(1, 4)], dim=0) - scores = torch.cat([scores, scores.new_zeros(1)], dim=0) - labels = torch.cat([labels, labels.new_zeros(1)], dim=0) - - if bboxes.numel() == 0: - if torch.onnx.is_in_onnx_export(): - raise RuntimeError('[ONNX Error] Can not record NMS ' - 'as it has not been executed this time') - dets = torch.cat([bboxes, scores[:, None]], -1) - if return_inds: - return dets, labels, inds - else: - return dets, labels - - dets, keep = batched_nms(bboxes, scores, labels, nms_cfg) - - if max_num > 0: - dets = dets[:max_num] - keep = keep[:max_num] - - if return_inds: - return dets, labels[keep], inds[keep] - else: - return dets, labels[keep] - - -def fast_nms(multi_bboxes, - multi_scores, - multi_coeffs, - score_thr, - iou_thr, - top_k, - max_num=-1): - """Fast NMS in `YOLACT `_. - - Fast NMS allows already-removed detections to suppress other detections so - that every instance can be decided to be kept or discarded in parallel, - which is not possible in traditional NMS. This relaxation allows us to - implement Fast NMS entirely in standard GPU-accelerated matrix operations. - - Args: - multi_bboxes (Tensor): shape (n, #class*4) or (n, 4) - multi_scores (Tensor): shape (n, #class+1), where the last column - contains scores of the background class, but this will be ignored. - multi_coeffs (Tensor): shape (n, #class*coeffs_dim). - score_thr (float): bbox threshold, bboxes with scores lower than it - will not be considered. - iou_thr (float): IoU threshold to be considered as conflicted. - top_k (int): if there are more than top_k bboxes before NMS, - only top top_k will be kept. - max_num (int): if there are more than max_num bboxes after NMS, - only top max_num will be kept. If -1, keep all the bboxes. - Default: -1. - - Returns: - tuple: (dets, labels, coefficients), tensors of shape (k, 5), (k, 1), - and (k, coeffs_dim). Dets are boxes with scores. - Labels are 0-based. - """ - - scores = multi_scores[:, :-1].t() # [#class, n] - scores, idx = scores.sort(1, descending=True) - - idx = idx[:, :top_k].contiguous() - scores = scores[:, :top_k] # [#class, topk] - num_classes, num_dets = idx.size() - boxes = multi_bboxes[idx.view(-1), :].view(num_classes, num_dets, 4) - coeffs = multi_coeffs[idx.view(-1), :].view(num_classes, num_dets, -1) - - iou = bbox_overlaps(boxes, boxes) # [#class, topk, topk] - iou.triu_(diagonal=1) - iou_max, _ = iou.max(dim=1) - - # Now just filter out the ones higher than the threshold - keep = iou_max <= iou_thr - - # Second thresholding introduces 0.2 mAP gain at negligible time cost - keep *= scores > score_thr - - # Assign each kept detection to its corresponding class - classes = torch.arange( - num_classes, device=boxes.device)[:, None].expand_as(keep) - classes = classes[keep] - - boxes = boxes[keep] - coeffs = coeffs[keep] - scores = scores[keep] - - # Only keep the top max_num highest scores across all classes - scores, idx = scores.sort(0, descending=True) - if max_num > 0: - idx = idx[:max_num] - scores = scores[:max_num] - - classes = classes[idx] - boxes = boxes[idx] - coeffs = coeffs[idx] - - cls_dets = torch.cat([boxes, scores[:, None]], dim=1) - return cls_dets, classes, coeffs diff --git a/cv/detection/autoassign/pytorch/mmdet/core/post_processing/matrix_nms.py b/cv/detection/autoassign/pytorch/mmdet/core/post_processing/matrix_nms.py deleted file mode 100755 index 0cc3f5ffb..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/post_processing/matrix_nms.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def mask_matrix_nms(masks, - labels, - scores, - filter_thr=-1, - nms_pre=-1, - max_num=-1, - kernel='gaussian', - sigma=2.0, - mask_area=None): - """Matrix NMS for multi-class masks. - - Args: - masks (Tensor): Has shape (num_instances, h, w) - labels (Tensor): Labels of corresponding masks, - has shape (num_instances,). - scores (Tensor): Mask scores of corresponding masks, - has shape (num_instances). - filter_thr (float): Score threshold to filter the masks - after matrix nms. Default: -1, which means do not - use filter_thr. - nms_pre (int): The max number of instances to do the matrix nms. - Default: -1, which means do not use nms_pre. - max_num (int, optional): If there are more than max_num masks after - matrix, only top max_num will be kept. Default: -1, which means - do not use max_num. - kernel (str): 'linear' or 'gaussian'. - sigma (float): std in gaussian method. - mask_area (Tensor): The sum of seg_masks. - - Returns: - tuple(Tensor): Processed mask results. - - - scores (Tensor): Updated scores, has shape (n,). - - labels (Tensor): Remained labels, has shape (n,). - - masks (Tensor): Remained masks, has shape (n, w, h). - - keep_inds (Tensor): The indices number of - the remaining mask in the input mask, has shape (n,). - """ - assert len(labels) == len(masks) == len(scores) - if len(labels) == 0: - return scores.new_zeros(0), labels.new_zeros(0), masks.new_zeros( - 0, *masks.shape[-2:]), labels.new_zeros(0) - if mask_area is None: - mask_area = masks.sum((1, 2)).float() - else: - assert len(masks) == len(mask_area) - - # sort and keep top nms_pre - scores, sort_inds = torch.sort(scores, descending=True) - - keep_inds = sort_inds - if nms_pre > 0 and len(sort_inds) > nms_pre: - sort_inds = sort_inds[:nms_pre] - keep_inds = keep_inds[:nms_pre] - scores = scores[:nms_pre] - masks = masks[sort_inds] - mask_area = mask_area[sort_inds] - labels = labels[sort_inds] - - num_masks = len(labels) - flatten_masks = masks.reshape(num_masks, -1).float() - # inter. - inter_matrix = torch.mm(flatten_masks, flatten_masks.transpose(1, 0)) - expanded_mask_area = mask_area.expand(num_masks, num_masks) - # Upper triangle iou matrix. - iou_matrix = (inter_matrix / - (expanded_mask_area + expanded_mask_area.transpose(1, 0) - - inter_matrix)).triu(diagonal=1) - # label_specific matrix. - expanded_labels = labels.expand(num_masks, num_masks) - # Upper triangle label matrix. - label_matrix = (expanded_labels == expanded_labels.transpose( - 1, 0)).triu(diagonal=1) - - # IoU compensation - compensate_iou, _ = (iou_matrix * label_matrix).max(0) - compensate_iou = compensate_iou.expand(num_masks, - num_masks).transpose(1, 0) - - # IoU decay - decay_iou = iou_matrix * label_matrix - - # Calculate the decay_coefficient - if kernel == 'gaussian': - decay_matrix = torch.exp(-1 * sigma * (decay_iou**2)) - compensate_matrix = torch.exp(-1 * sigma * (compensate_iou**2)) - decay_coefficient, _ = (decay_matrix / compensate_matrix).min(0) - elif kernel == 'linear': - decay_matrix = (1 - decay_iou) / (1 - compensate_iou) - decay_coefficient, _ = decay_matrix.min(0) - else: - raise NotImplementedError( - f'{kernel} kernel is not supported in matrix nms!') - # update the score. - scores = scores * decay_coefficient - - if filter_thr > 0: - keep = scores >= filter_thr - keep_inds = keep_inds[keep] - if not keep.any(): - return scores.new_zeros(0), labels.new_zeros(0), masks.new_zeros( - 0, *masks.shape[-2:]), labels.new_zeros(0) - masks = masks[keep] - scores = scores[keep] - labels = labels[keep] - - # sort and keep top max_num - scores, sort_inds = torch.sort(scores, descending=True) - keep_inds = keep_inds[sort_inds] - if max_num > 0 and len(sort_inds) > max_num: - sort_inds = sort_inds[:max_num] - keep_inds = keep_inds[:max_num] - scores = scores[:max_num] - masks = masks[sort_inds] - labels = labels[sort_inds] - - return scores, labels, masks, keep_inds diff --git a/cv/detection/autoassign/pytorch/mmdet/core/post_processing/merge_augs.py b/cv/detection/autoassign/pytorch/mmdet/core/post_processing/merge_augs.py deleted file mode 100755 index 5246daf1d..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/post_processing/merge_augs.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings - -import numpy as np -import torch -from mmcv import ConfigDict -from mmcv.ops import nms - -from ..bbox import bbox_mapping_back - - -def merge_aug_proposals(aug_proposals, img_metas, cfg): - """Merge augmented proposals (multiscale, flip, etc.) - - Args: - aug_proposals (list[Tensor]): proposals from different testing - schemes, shape (n, 5). Note that they are not rescaled to the - original image size. - - img_metas (list[dict]): list of image info dict where each dict has: - 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - `mmdet/datasets/pipelines/formatting.py:Collect`. - - cfg (dict): rpn test config. - - Returns: - Tensor: shape (n, 4), proposals corresponding to original image scale. - """ - - cfg = copy.deepcopy(cfg) - - # deprecate arguments warning - if 'nms' not in cfg or 'max_num' in cfg or 'nms_thr' in cfg: - warnings.warn( - 'In rpn_proposal or test_cfg, ' - 'nms_thr has been moved to a dict named nms as ' - 'iou_threshold, max_num has been renamed as max_per_img, ' - 'name of original arguments and the way to specify ' - 'iou_threshold of NMS will be deprecated.') - if 'nms' not in cfg: - cfg.nms = ConfigDict(dict(type='nms', iou_threshold=cfg.nms_thr)) - if 'max_num' in cfg: - if 'max_per_img' in cfg: - assert cfg.max_num == cfg.max_per_img, f'You set max_num and ' \ - f'max_per_img at the same time, but get {cfg.max_num} ' \ - f'and {cfg.max_per_img} respectively' \ - f'Please delete max_num which will be deprecated.' - else: - cfg.max_per_img = cfg.max_num - if 'nms_thr' in cfg: - assert cfg.nms.iou_threshold == cfg.nms_thr, f'You set ' \ - f'iou_threshold in nms and ' \ - f'nms_thr at the same time, but get ' \ - f'{cfg.nms.iou_threshold} and {cfg.nms_thr}' \ - f' respectively. Please delete the nms_thr ' \ - f'which will be deprecated.' - - recovered_proposals = [] - for proposals, img_info in zip(aug_proposals, img_metas): - img_shape = img_info['img_shape'] - scale_factor = img_info['scale_factor'] - flip = img_info['flip'] - flip_direction = img_info['flip_direction'] - _proposals = proposals.clone() - _proposals[:, :4] = bbox_mapping_back(_proposals[:, :4], img_shape, - scale_factor, flip, - flip_direction) - recovered_proposals.append(_proposals) - aug_proposals = torch.cat(recovered_proposals, dim=0) - merged_proposals, _ = nms(aug_proposals[:, :4].contiguous(), - aug_proposals[:, -1].contiguous(), - cfg.nms.iou_threshold) - scores = merged_proposals[:, 4] - _, order = scores.sort(0, descending=True) - num = min(cfg.max_per_img, merged_proposals.shape[0]) - order = order[:num] - merged_proposals = merged_proposals[order, :] - return merged_proposals - - -def merge_aug_bboxes(aug_bboxes, aug_scores, img_metas, rcnn_test_cfg): - """Merge augmented detection bboxes and scores. - - Args: - aug_bboxes (list[Tensor]): shape (n, 4*#class) - aug_scores (list[Tensor] or None): shape (n, #class) - img_shapes (list[Tensor]): shape (3, ). - rcnn_test_cfg (dict): rcnn test config. - - Returns: - tuple: (bboxes, scores) - """ - recovered_bboxes = [] - for bboxes, img_info in zip(aug_bboxes, img_metas): - img_shape = img_info[0]['img_shape'] - scale_factor = img_info[0]['scale_factor'] - flip = img_info[0]['flip'] - flip_direction = img_info[0]['flip_direction'] - bboxes = bbox_mapping_back(bboxes, img_shape, scale_factor, flip, - flip_direction) - recovered_bboxes.append(bboxes) - bboxes = torch.stack(recovered_bboxes).mean(dim=0) - if aug_scores is None: - return bboxes - else: - scores = torch.stack(aug_scores).mean(dim=0) - return bboxes, scores - - -def merge_aug_scores(aug_scores): - """Merge augmented bbox scores.""" - if isinstance(aug_scores[0], torch.Tensor): - return torch.mean(torch.stack(aug_scores), dim=0) - else: - return np.mean(aug_scores, axis=0) - - -def merge_aug_masks(aug_masks, img_metas, rcnn_test_cfg, weights=None): - """Merge augmented mask prediction. - - Args: - aug_masks (list[ndarray]): shape (n, #class, h, w) - img_shapes (list[ndarray]): shape (3, ). - rcnn_test_cfg (dict): rcnn test config. - - Returns: - tuple: (bboxes, scores) - """ - recovered_masks = [] - for mask, img_info in zip(aug_masks, img_metas): - flip = img_info[0]['flip'] - if flip: - flip_direction = img_info[0]['flip_direction'] - if flip_direction == 'horizontal': - mask = mask[:, :, :, ::-1] - elif flip_direction == 'vertical': - mask = mask[:, :, ::-1, :] - elif flip_direction == 'diagonal': - mask = mask[:, :, :, ::-1] - mask = mask[:, :, ::-1, :] - else: - raise ValueError( - f"Invalid flipping direction '{flip_direction}'") - recovered_masks.append(mask) - - if weights is None: - merged_masks = np.mean(recovered_masks, axis=0) - else: - merged_masks = np.average( - np.array(recovered_masks), axis=0, weights=np.array(weights)) - return merged_masks diff --git a/cv/detection/autoassign/pytorch/mmdet/core/utils/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/utils/__init__.py deleted file mode 100755 index 86f9d2854..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/utils/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .dist_utils import (DistOptimizerHook, all_reduce_dict, allreduce_grads, - reduce_mean, sync_random_seed) -from .misc import (center_of_mass, filter_scores_and_topk, flip_tensor, - generate_coordinate, mask2ndarray, multi_apply, - select_single_mlvl, unmap) - -__all__ = [ - 'allreduce_grads', 'DistOptimizerHook', 'reduce_mean', 'multi_apply', - 'unmap', 'mask2ndarray', 'flip_tensor', 'all_reduce_dict', - 'center_of_mass', 'generate_coordinate', 'select_single_mlvl', - 'filter_scores_and_topk', 'sync_random_seed' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/utils/dist_utils.py b/cv/detection/autoassign/pytorch/mmdet/core/utils/dist_utils.py deleted file mode 100755 index 1af8feb1c..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/utils/dist_utils.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import pickle -import warnings -from collections import OrderedDict - -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import OptimizerHook, get_dist_info -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - world_size = dist.get_world_size() - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -class DistOptimizerHook(OptimizerHook): - """Deprecated optimizer hook for distributed training.""" - - def __init__(self, *args, **kwargs): - warnings.warn('"DistOptimizerHook" is deprecated, please switch to' - '"mmcv.runner.OptimizerHook".') - super().__init__(*args, **kwargs) - - -def reduce_mean(tensor): - """"Obtain the mean of tensor on different GPUs.""" - if not (dist.is_available() and dist.is_initialized()): - return tensor - tensor = tensor.clone() - dist.all_reduce(tensor.div_(dist.get_world_size()), op=dist.ReduceOp.SUM) - return tensor - - -def obj2tensor(pyobj, device='cuda'): - """Serialize picklable python object to tensor.""" - storage = torch.ByteStorage.from_buffer(pickle.dumps(pyobj)) - return torch.ByteTensor(storage).to(device=device) - - -def tensor2obj(tensor): - """Deserialize tensor to picklable python object.""" - return pickle.loads(tensor.cpu().numpy().tobytes()) - - -@functools.lru_cache() -def _get_global_gloo_group(): - """Return a process group based on gloo backend, containing all the ranks - The result is cached.""" - if dist.get_backend() == 'nccl': - return dist.new_group(backend='gloo') - else: - return dist.group.WORLD - - -def all_reduce_dict(py_dict, op='sum', group=None, to_float=True): - """Apply all reduce function for python dict object. - - The code is modified from https://github.com/Megvii- - BaseDetection/YOLOX/blob/main/yolox/utils/allreduce_norm.py. - - NOTE: make sure that py_dict in different ranks has the same keys and - the values should be in the same shape. Currently only supports - nccl backend. - - Args: - py_dict (dict): Dict to be applied all reduce op. - op (str): Operator, could be 'sum' or 'mean'. Default: 'sum' - group (:obj:`torch.distributed.group`, optional): Distributed group, - Default: None. - to_float (bool): Whether to convert all values of dict to float. - Default: True. - - Returns: - OrderedDict: reduced python dict object. - """ - warnings.warn( - 'group` is deprecated. Currently only supports NCCL backend.') - _, world_size = get_dist_info() - if world_size == 1: - return py_dict - - # all reduce logic across different devices. - py_key = list(py_dict.keys()) - if not isinstance(py_dict, OrderedDict): - py_key_tensor = obj2tensor(py_key) - dist.broadcast(py_key_tensor, src=0) - py_key = tensor2obj(py_key_tensor) - - tensor_shapes = [py_dict[k].shape for k in py_key] - tensor_numels = [py_dict[k].numel() for k in py_key] - - if to_float: - warnings.warn('Note: the "to_float" is True, you need to ' - 'ensure that the behavior is reasonable.') - flatten_tensor = torch.cat( - [py_dict[k].flatten().float() for k in py_key]) - else: - flatten_tensor = torch.cat([py_dict[k].flatten() for k in py_key]) - - dist.all_reduce(flatten_tensor, op=dist.ReduceOp.SUM) - if op == 'mean': - flatten_tensor /= world_size - - split_tensors = [ - x.reshape(shape) for x, shape in zip( - torch.split(flatten_tensor, tensor_numels), tensor_shapes) - ] - out_dict = {k: v for k, v in zip(py_key, split_tensors)} - if isinstance(py_dict, OrderedDict): - out_dict = OrderedDict(out_dict) - return out_dict - - -def sync_random_seed(seed=None, device='cuda'): - """Make sure different ranks share the same seed. - - All workers must call this function, otherwise it will deadlock. - This method is generally used in `DistributedSampler`, - because the seed should be identical across all processes - in the distributed group. - - In distributed sampling, different ranks should sample non-overlapped - data in the dataset. Therefore, this function is used to make sure that - each rank shuffles the data indices in the same order based - on the same seed. Then different ranks could use different indices - to select non-overlapped data from the same data list. - - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - - Returns: - int: Seed to be used. - """ - if seed is None: - seed = np.random.randint(2**31) - assert isinstance(seed, int) - - rank, world_size = get_dist_info() - - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() diff --git a/cv/detection/autoassign/pytorch/mmdet/core/utils/misc.py b/cv/detection/autoassign/pytorch/mmdet/core/utils/misc.py deleted file mode 100755 index c67ec429b..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/utils/misc.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import numpy as np -import torch -from six.moves import map, zip - -from ..mask.structures import BitmapMasks, PolygonMasks - - -def multi_apply(func, *args, **kwargs): - """Apply function to a list of arguments. - - Note: - This function applies the ``func`` to multiple inputs and - map the multiple outputs of the ``func`` into different - list. Each list contains the same type of outputs corresponding - to different inputs. - - Args: - func (Function): A function that will be applied to a list of - arguments - - Returns: - tuple(list): A tuple containing multiple list, each list contains \ - a kind of returned results by the function - """ - pfunc = partial(func, **kwargs) if kwargs else func - map_results = map(pfunc, *args) - return tuple(map(list, zip(*map_results))) - - -def unmap(data, count, inds, fill=0): - """Unmap a subset of item (data) back to the original set of items (of size - count)""" - if data.dim() == 1: - ret = data.new_full((count, ), fill) - ret[inds.type(torch.bool)] = data - else: - new_size = (count, ) + data.size()[1:] - ret = data.new_full(new_size, fill) - ret[inds.type(torch.bool), :] = data - return ret - - -def mask2ndarray(mask): - """Convert Mask to ndarray.. - - Args: - mask (:obj:`BitmapMasks` or :obj:`PolygonMasks` or - torch.Tensor or np.ndarray): The mask to be converted. - - Returns: - np.ndarray: Ndarray mask of shape (n, h, w) that has been converted - """ - if isinstance(mask, (BitmapMasks, PolygonMasks)): - mask = mask.to_ndarray() - elif isinstance(mask, torch.Tensor): - mask = mask.detach().cpu().numpy() - elif not isinstance(mask, np.ndarray): - raise TypeError(f'Unsupported {type(mask)} data type') - return mask - - -def flip_tensor(src_tensor, flip_direction): - """flip tensor base on flip_direction. - - Args: - src_tensor (Tensor): input feature map, shape (B, C, H, W). - flip_direction (str): The flipping direction. Options are - 'horizontal', 'vertical', 'diagonal'. - - Returns: - out_tensor (Tensor): Flipped tensor. - """ - assert src_tensor.ndim == 4 - valid_directions = ['horizontal', 'vertical', 'diagonal'] - assert flip_direction in valid_directions - if flip_direction == 'horizontal': - out_tensor = torch.flip(src_tensor, [3]) - elif flip_direction == 'vertical': - out_tensor = torch.flip(src_tensor, [2]) - else: - out_tensor = torch.flip(src_tensor, [2, 3]) - return out_tensor - - -def select_single_mlvl(mlvl_tensors, batch_id, detach=True): - """Extract a multi-scale single image tensor from a multi-scale batch - tensor based on batch index. - - Note: The default value of detach is True, because the proposal gradient - needs to be detached during the training of the two-stage model. E.g - Cascade Mask R-CNN. - - Args: - mlvl_tensors (list[Tensor]): Batch tensor for all scale levels, - each is a 4D-tensor. - batch_id (int): Batch index. - detach (bool): Whether detach gradient. Default True. - - Returns: - list[Tensor]: Multi-scale single image tensor. - """ - assert isinstance(mlvl_tensors, (list, tuple)) - num_levels = len(mlvl_tensors) - - if detach: - mlvl_tensor_list = [ - mlvl_tensors[i][batch_id].detach() for i in range(num_levels) - ] - else: - mlvl_tensor_list = [ - mlvl_tensors[i][batch_id] for i in range(num_levels) - ] - return mlvl_tensor_list - - -def filter_scores_and_topk(scores, score_thr, topk, results=None): - """Filter results using score threshold and topk candidates. - - Args: - scores (Tensor): The scores, shape (num_bboxes, K). - score_thr (float): The score filter threshold. - topk (int): The number of topk candidates. - results (dict or list or Tensor, Optional): The results to - which the filtering rule is to be applied. The shape - of each item is (num_bboxes, N). - - Returns: - tuple: Filtered results - - - scores (Tensor): The scores after being filtered, \ - shape (num_bboxes_filtered, ). - - labels (Tensor): The class labels, shape \ - (num_bboxes_filtered, ). - - anchor_idxs (Tensor): The anchor indexes, shape \ - (num_bboxes_filtered, ). - - filtered_results (dict or list or Tensor, Optional): \ - The filtered results. The shape of each item is \ - (num_bboxes_filtered, N). - """ - valid_mask = scores > score_thr - scores = scores[valid_mask] - valid_idxs = torch.nonzero(valid_mask) - - num_topk = min(topk, valid_idxs.size(0)) - # torch.sort is actually faster than .topk (at least on GPUs) - scores, idxs = scores.sort(descending=True) - scores = scores[:num_topk] - topk_idxs = valid_idxs[idxs[:num_topk]] - keep_idxs, labels = topk_idxs.unbind(dim=1) - - filtered_results = None - if results is not None: - if isinstance(results, dict): - filtered_results = {k: v[keep_idxs] for k, v in results.items()} - elif isinstance(results, list): - filtered_results = [result[keep_idxs] for result in results] - elif isinstance(results, torch.Tensor): - filtered_results = results[keep_idxs] - else: - raise NotImplementedError(f'Only supports dict or list or Tensor, ' - f'but get {type(results)}.') - return scores, labels, keep_idxs, filtered_results - - -def center_of_mass(mask, esp=1e-6): - """Calculate the centroid coordinates of the mask. - - Args: - mask (Tensor): The mask to be calculated, shape (h, w). - esp (float): Avoid dividing by zero. Default: 1e-6. - - Returns: - tuple[Tensor]: the coordinates of the center point of the mask. - - - center_h (Tensor): the center point of the height. - - center_w (Tensor): the center point of the width. - """ - h, w = mask.shape - grid_h = torch.arange(h, device=mask.device)[:, None] - grid_w = torch.arange(w, device=mask.device) - normalizer = mask.sum().float().clamp(min=esp) - center_h = (mask * grid_h).sum() / normalizer - center_w = (mask * grid_w).sum() / normalizer - return center_h, center_w - - -def generate_coordinate(featmap_sizes, device='cuda'): - """Generate the coordinate. - - Args: - featmap_sizes (tuple): The feature to be calculated, - of shape (N, C, W, H). - device (str): The device where the feature will be put on. - Returns: - coord_feat (Tensor): The coordinate feature, of shape (N, 2, W, H). - """ - - x_range = torch.linspace(-1, 1, featmap_sizes[-1], device=device) - y_range = torch.linspace(-1, 1, featmap_sizes[-2], device=device) - y, x = torch.meshgrid(y_range, x_range) - y = y.expand([featmap_sizes[0], 1, -1, -1]) - x = x.expand([featmap_sizes[0], 1, -1, -1]) - coord_feat = torch.cat([x, y], 1) - - return coord_feat diff --git a/cv/detection/autoassign/pytorch/mmdet/core/visualization/__init__.py b/cv/detection/autoassign/pytorch/mmdet/core/visualization/__init__.py deleted file mode 100755 index dfbcb2d42..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/visualization/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .image import (color_val_matplotlib, imshow_det_bboxes, - imshow_gt_det_bboxes) -from .palette import get_palette, palette_val - -__all__ = [ - 'imshow_det_bboxes', 'imshow_gt_det_bboxes', 'color_val_matplotlib', - 'palette_val', 'get_palette' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/core/visualization/image.py b/cv/detection/autoassign/pytorch/mmdet/core/visualization/image.py deleted file mode 100755 index 6febf6aec..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/visualization/image.py +++ /dev/null @@ -1,559 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import matplotlib.pyplot as plt -import mmcv -import numpy as np -import pycocotools.mask as mask_util -from matplotlib.collections import PatchCollection -from matplotlib.patches import Polygon - -from mmdet.core.evaluation.panoptic_utils import INSTANCE_OFFSET -from ..mask.structures import bitmap_to_polygon -from ..utils import mask2ndarray -from .palette import get_palette, palette_val - -__all__ = [ - 'color_val_matplotlib', 'draw_masks', 'draw_bboxes', 'draw_labels', - 'imshow_det_bboxes', 'imshow_gt_det_bboxes' -] - -EPS = 1e-2 - - -def color_val_matplotlib(color): - """Convert various input in BGR order to normalized RGB matplotlib color - tuples. - - Args: - color (:obj`Color` | str | tuple | int | ndarray): Color inputs. - - Returns: - tuple[float]: A tuple of 3 normalized floats indicating RGB channels. - """ - color = mmcv.color_val(color) - color = [color / 255 for color in color[::-1]] - return tuple(color) - - -def _get_adaptive_scales(areas, min_area=800, max_area=30000): - """Get adaptive scales according to areas. - - The scale range is [0.5, 1.0]. When the area is less than - ``'min_area'``, the scale is 0.5 while the area is larger than - ``'max_area'``, the scale is 1.0. - - Args: - areas (ndarray): The areas of bboxes or masks with the - shape of (n, ). - min_area (int): Lower bound areas for adaptive scales. - Default: 800. - max_area (int): Upper bound areas for adaptive scales. - Default: 30000. - - Returns: - ndarray: The adaotive scales with the shape of (n, ). - """ - scales = 0.5 + (areas - min_area) / (max_area - min_area) - scales = np.clip(scales, 0.5, 1.0) - return scales - - -def _get_bias_color(base, max_dist=30): - """Get different colors for each masks. - - Get different colors for each masks by adding a bias - color to the base category color. - Args: - base (ndarray): The base category color with the shape - of (3, ). - max_dist (int): The max distance of bias. Default: 30. - - Returns: - ndarray: The new color for a mask with the shape of (3, ). - """ - new_color = base + np.random.randint( - low=-max_dist, high=max_dist + 1, size=3) - return np.clip(new_color, 0, 255, new_color) - - -def draw_bboxes(ax, bboxes, color='g', alpha=0.8, thickness=2): - """Draw bounding boxes on the axes. - - Args: - ax (matplotlib.Axes): The input axes. - bboxes (ndarray): The input bounding boxes with the shape - of (n, 4). - color (list[tuple] | matplotlib.color): the colors for each - bounding boxes. - alpha (float): Transparency of bounding boxes. Default: 0.8. - thickness (int): Thickness of lines. Default: 2. - - Returns: - matplotlib.Axes: The result axes. - """ - polygons = [] - for i, bbox in enumerate(bboxes): - bbox_int = bbox.astype(np.int32) - poly = [[bbox_int[0], bbox_int[1]], [bbox_int[0], bbox_int[3]], - [bbox_int[2], bbox_int[3]], [bbox_int[2], bbox_int[1]]] - np_poly = np.array(poly).reshape((4, 2)) - polygons.append(Polygon(np_poly)) - p = PatchCollection( - polygons, - facecolor='none', - edgecolors=color, - linewidths=thickness, - alpha=alpha) - ax.add_collection(p) - - return ax - - -def draw_labels(ax, - labels, - positions, - scores=None, - class_names=None, - color='w', - font_size=8, - scales=None, - horizontal_alignment='left'): - """Draw labels on the axes. - - Args: - ax (matplotlib.Axes): The input axes. - labels (ndarray): The labels with the shape of (n, ). - positions (ndarray): The positions to draw each labels. - scores (ndarray): The scores for each labels. - class_names (list[str]): The class names. - color (list[tuple] | matplotlib.color): The colors for labels. - font_size (int): Font size of texts. Default: 8. - scales (list[float]): Scales of texts. Default: None. - horizontal_alignment (str): The horizontal alignment method of - texts. Default: 'left'. - - Returns: - matplotlib.Axes: The result axes. - """ - for i, (pos, label) in enumerate(zip(positions, labels)): - label_text = class_names[ - label] if class_names is not None else f'class {label}' - if scores is not None: - label_text += f'|{scores[i]:.02f}' - text_color = color[i] if isinstance(color, list) else color - - font_size_mask = font_size if scales is None else font_size * scales[i] - ax.text( - pos[0], - pos[1], - f'{label_text}', - bbox={ - 'facecolor': 'black', - 'alpha': 0.8, - 'pad': 0.7, - 'edgecolor': 'none' - }, - color=text_color, - fontsize=font_size_mask, - verticalalignment='top', - horizontalalignment=horizontal_alignment) - - return ax - - -def draw_masks(ax, img, masks, color=None, with_edge=True, alpha=0.8): - """Draw masks on the image and their edges on the axes. - - Args: - ax (matplotlib.Axes): The input axes. - img (ndarray): The image with the shape of (3, h, w). - masks (ndarray): The masks with the shape of (n, h, w). - color (ndarray): The colors for each masks with the shape - of (n, 3). - with_edge (bool): Whether to draw edges. Default: True. - alpha (float): Transparency of bounding boxes. Default: 0.8. - - Returns: - matplotlib.Axes: The result axes. - ndarray: The result image. - """ - taken_colors = set([0, 0, 0]) - if color is None: - random_colors = np.random.randint(0, 255, (masks.size(0), 3)) - color = [tuple(c) for c in random_colors] - color = np.array(color, dtype=np.uint8) - polygons = [] - for i, mask in enumerate(masks): - if with_edge: - contours, _ = bitmap_to_polygon(mask) - polygons += [Polygon(c) for c in contours] - - color_mask = color[i] - while tuple(color_mask) in taken_colors: - color_mask = _get_bias_color(color_mask) - taken_colors.add(tuple(color_mask)) - - mask = mask.astype(bool) - img[mask] = img[mask] * (1 - alpha) + color_mask * alpha - - p = PatchCollection( - polygons, facecolor='none', edgecolors='w', linewidths=1, alpha=0.8) - ax.add_collection(p) - - return ax, img - - -def imshow_det_bboxes(img, - bboxes=None, - labels=None, - segms=None, - class_names=None, - score_thr=0, - bbox_color='green', - text_color='green', - mask_color=None, - thickness=2, - font_size=8, - win_name='', - show=True, - wait_time=0, - out_file=None): - """Draw bboxes and class labels (with scores) on an image. - - Args: - img (str | ndarray): The image to be displayed. - bboxes (ndarray): Bounding boxes (with scores), shaped (n, 4) or - (n, 5). - labels (ndarray): Labels of bboxes. - segms (ndarray | None): Masks, shaped (n,h,w) or None. - class_names (list[str]): Names of each classes. - score_thr (float): Minimum score of bboxes to be shown. Default: 0. - bbox_color (list[tuple] | tuple | str | None): Colors of bbox lines. - If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: 'green'. - text_color (list[tuple] | tuple | str | None): Colors of texts. - If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: 'green'. - mask_color (list[tuple] | tuple | str | None, optional): Colors of - masks. If a single color is given, it will be applied to all - classes. The tuple of color should be in RGB order. - Default: None. - thickness (int): Thickness of lines. Default: 2. - font_size (int): Font size of texts. Default: 13. - show (bool): Whether to show the image. Default: True. - win_name (str): The window name. Default: ''. - wait_time (float): Value of waitKey param. Default: 0. - out_file (str, optional): The filename to write the image. - Default: None. - - Returns: - ndarray: The image with bboxes drawn on it. - """ - assert bboxes is None or bboxes.ndim == 2, \ - f' bboxes ndim should be 2, but its ndim is {bboxes.ndim}.' - assert labels.ndim == 1, \ - f' labels ndim should be 1, but its ndim is {labels.ndim}.' - assert bboxes is None or bboxes.shape[1] == 4 or bboxes.shape[1] == 5, \ - f' bboxes.shape[1] should be 4 or 5, but its {bboxes.shape[1]}.' - assert bboxes is None or bboxes.shape[0] <= labels.shape[0], \ - 'labels.shape[0] should not be less than bboxes.shape[0].' - assert segms is None or segms.shape[0] == labels.shape[0], \ - 'segms.shape[0] and labels.shape[0] should have the same length.' - assert segms is not None or bboxes is not None, \ - 'segms and bboxes should not be None at the same time.' - - img = mmcv.imread(img).astype(np.uint8) - - if score_thr > 0: - assert bboxes is not None and bboxes.shape[1] == 5 - scores = bboxes[:, -1] - inds = scores > score_thr - bboxes = bboxes[inds, :] - labels = labels[inds] - if segms is not None: - segms = segms[inds, ...] - - img = mmcv.bgr2rgb(img) - width, height = img.shape[1], img.shape[0] - img = np.ascontiguousarray(img) - - fig = plt.figure(win_name, frameon=False) - plt.title(win_name) - canvas = fig.canvas - dpi = fig.get_dpi() - # add a small EPS to avoid precision lost due to matplotlib's truncation - # (https://github.com/matplotlib/matplotlib/issues/15363) - fig.set_size_inches((width + EPS) / dpi, (height + EPS) / dpi) - - # remove white edges by set subplot margin - plt.subplots_adjust(left=0, right=1, bottom=0, top=1) - ax = plt.gca() - ax.axis('off') - - max_label = int(max(labels) if len(labels) > 0 else 0) - text_palette = palette_val(get_palette(text_color, max_label + 1)) - text_colors = [text_palette[label] for label in labels] - - num_bboxes = 0 - if bboxes is not None: - num_bboxes = bboxes.shape[0] - bbox_palette = palette_val(get_palette(bbox_color, max_label + 1)) - colors = [bbox_palette[label] for label in labels[:num_bboxes]] - draw_bboxes(ax, bboxes, colors, alpha=0.8, thickness=thickness) - - horizontal_alignment = 'left' - positions = bboxes[:, :2].astype(np.int32) + thickness - areas = (bboxes[:, 3] - bboxes[:, 1]) * (bboxes[:, 2] - bboxes[:, 0]) - scales = _get_adaptive_scales(areas) - scores = bboxes[:, 4] if bboxes.shape[1] == 5 else None - draw_labels( - ax, - labels[:num_bboxes], - positions, - scores=scores, - class_names=class_names, - color=text_colors, - font_size=font_size, - scales=scales, - horizontal_alignment=horizontal_alignment) - - if segms is not None: - mask_palette = get_palette(mask_color, max_label + 1) - colors = [mask_palette[label] for label in labels] - colors = np.array(colors, dtype=np.uint8) - draw_masks(ax, img, segms, colors, with_edge=True) - - if num_bboxes < segms.shape[0]: - segms = segms[num_bboxes:] - horizontal_alignment = 'center' - areas = [] - positions = [] - for mask in segms: - _, _, stats, centroids = cv2.connectedComponentsWithStats( - mask.astype(np.uint8), connectivity=8) - largest_id = np.argmax(stats[1:, -1]) + 1 - positions.append(centroids[largest_id]) - areas.append(stats[largest_id, -1]) - areas = np.stack(areas, axis=0) - scales = _get_adaptive_scales(areas) - draw_labels( - ax, - labels[num_bboxes:], - positions, - class_names=class_names, - color=text_colors, - font_size=font_size, - scales=scales, - horizontal_alignment=horizontal_alignment) - - plt.imshow(img) - - stream, _ = canvas.print_to_buffer() - buffer = np.frombuffer(stream, dtype='uint8') - img_rgba = buffer.reshape(height, width, 4) - rgb, alpha = np.split(img_rgba, [3], axis=2) - img = rgb.astype('uint8') - img = mmcv.rgb2bgr(img) - - if show: - # We do not use cv2 for display because in some cases, opencv will - # conflict with Qt, it will output a warning: Current thread - # is not the object's thread. You can refer to - # https://github.com/opencv/opencv-python/issues/46 for details - if wait_time == 0: - plt.show() - else: - plt.show(block=False) - plt.pause(wait_time) - if out_file is not None: - mmcv.imwrite(img, out_file) - - plt.close() - - return img - - -def imshow_gt_det_bboxes(img, - annotation, - result, - class_names=None, - score_thr=0, - gt_bbox_color=(61, 102, 255), - gt_text_color=(200, 200, 200), - gt_mask_color=(61, 102, 255), - det_bbox_color=(241, 101, 72), - det_text_color=(200, 200, 200), - det_mask_color=(241, 101, 72), - thickness=2, - font_size=13, - win_name='', - show=True, - wait_time=0, - out_file=None, - overlay_gt_pred=True): - """General visualization GT and result function. - - Args: - img (str | ndarray): The image to be displayed. - annotation (dict): Ground truth annotations where contain keys of - 'gt_bboxes' and 'gt_labels' or 'gt_masks'. - result (tuple[list] | list): The detection result, can be either - (bbox, segm) or just bbox. - class_names (list[str]): Names of each classes. - score_thr (float): Minimum score of bboxes to be shown. Default: 0. - gt_bbox_color (list[tuple] | tuple | str | None): Colors of bbox lines. - If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: (61, 102, 255). - gt_text_color (list[tuple] | tuple | str | None): Colors of texts. - If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: (200, 200, 200). - gt_mask_color (list[tuple] | tuple | str | None, optional): Colors of - masks. If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: (61, 102, 255). - det_bbox_color (list[tuple] | tuple | str | None):Colors of bbox lines. - If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: (241, 101, 72). - det_text_color (list[tuple] | tuple | str | None):Colors of texts. - If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: (200, 200, 200). - det_mask_color (list[tuple] | tuple | str | None, optional): Color of - masks. If a single color is given, it will be applied to all classes. - The tuple of color should be in RGB order. Default: (241, 101, 72). - thickness (int): Thickness of lines. Default: 2. - font_size (int): Font size of texts. Default: 13. - win_name (str): The window name. Default: ''. - show (bool): Whether to show the image. Default: True. - wait_time (float): Value of waitKey param. Default: 0. - out_file (str, optional): The filename to write the image. - Default: None. - overlay_gt_pred (bool): Whether to plot gts and predictions on the - same image. If False, predictions and gts will be plotted on two same - image which will be concatenated in vertical direction. The image - above is drawn with gt, and the image below is drawn with the - prediction result. Default: True. - - Returns: - ndarray: The image with bboxes or masks drawn on it. - """ - assert 'gt_bboxes' in annotation - assert 'gt_labels' in annotation - assert isinstance(result, (tuple, list, dict)), 'Expected ' \ - f'tuple or list or dict, but get {type(result)}' - - gt_bboxes = annotation['gt_bboxes'] - gt_labels = annotation['gt_labels'] - gt_masks = annotation.get('gt_masks', None) - if gt_masks is not None: - gt_masks = mask2ndarray(gt_masks) - - gt_seg = annotation.get('gt_semantic_seg', None) - if gt_seg is not None: - pad_value = 255 # the padding value of gt_seg - sem_labels = np.unique(gt_seg) - all_labels = np.concatenate((gt_labels, sem_labels), axis=0) - all_labels, counts = np.unique(all_labels, return_counts=True) - stuff_labels = all_labels[np.logical_and(counts < 2, - all_labels != pad_value)] - stuff_masks = gt_seg[None] == stuff_labels[:, None, None] - gt_labels = np.concatenate((gt_labels, stuff_labels), axis=0) - gt_masks = np.concatenate((gt_masks, stuff_masks.astype(np.uint8)), - axis=0) - # If you need to show the bounding boxes, - # please comment the following line - # gt_bboxes = None - - img = mmcv.imread(img) - - img_with_gt = imshow_det_bboxes( - img, - gt_bboxes, - gt_labels, - gt_masks, - class_names=class_names, - bbox_color=gt_bbox_color, - text_color=gt_text_color, - mask_color=gt_mask_color, - thickness=thickness, - font_size=font_size, - win_name=win_name, - show=False) - - if not isinstance(result, dict): - if isinstance(result, tuple): - bbox_result, segm_result = result - if isinstance(segm_result, tuple): - segm_result = segm_result[0] # ms rcnn - else: - bbox_result, segm_result = result, None - - bboxes = np.vstack(bbox_result) - labels = [ - np.full(bbox.shape[0], i, dtype=np.int32) - for i, bbox in enumerate(bbox_result) - ] - labels = np.concatenate(labels) - - segms = None - if segm_result is not None and len(labels) > 0: # non empty - segms = mmcv.concat_list(segm_result) - segms = mask_util.decode(segms) - segms = segms.transpose(2, 0, 1) - else: - assert class_names is not None, 'We need to know the number ' \ - 'of classes.' - VOID = len(class_names) - bboxes = None - pan_results = result['pan_results'] - # keep objects ahead - ids = np.unique(pan_results)[::-1] - legal_indices = ids != VOID - ids = ids[legal_indices] - labels = np.array([id % INSTANCE_OFFSET for id in ids], dtype=np.int64) - segms = (pan_results[None] == ids[:, None, None]) - - if overlay_gt_pred: - img = imshow_det_bboxes( - img_with_gt, - bboxes, - labels, - segms=segms, - class_names=class_names, - score_thr=score_thr, - bbox_color=det_bbox_color, - text_color=det_text_color, - mask_color=det_mask_color, - thickness=thickness, - font_size=font_size, - win_name=win_name, - show=show, - wait_time=wait_time, - out_file=out_file) - else: - img_with_det = imshow_det_bboxes( - img, - bboxes, - labels, - segms=segms, - class_names=class_names, - score_thr=score_thr, - bbox_color=det_bbox_color, - text_color=det_text_color, - mask_color=det_mask_color, - thickness=thickness, - font_size=font_size, - win_name=win_name, - show=False) - img = np.concatenate([img_with_gt, img_with_det], axis=0) - - plt.imshow(img) - if show: - if wait_time == 0: - plt.show() - else: - plt.show(block=False) - plt.pause(wait_time) - if out_file is not None: - mmcv.imwrite(img, out_file) - plt.close() - - return img diff --git a/cv/detection/autoassign/pytorch/mmdet/core/visualization/palette.py b/cv/detection/autoassign/pytorch/mmdet/core/visualization/palette.py deleted file mode 100755 index 13a30614c..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/core/visualization/palette.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np - - -def palette_val(palette): - """Convert palette to matplotlib palette. - - Args: - palette List[tuple]: A list of color tuples. - - Returns: - List[tuple[float]]: A list of RGB matplotlib color tuples. - """ - new_palette = [] - for color in palette: - color = [c / 255 for c in color] - new_palette.append(tuple(color)) - return new_palette - - -def get_palette(palette, num_classes): - """Get palette from various inputs. - - Args: - palette (list[tuple] | str | tuple | :obj:`Color`): palette inputs. - num_classes (int): the number of classes. - - Returns: - list[tuple[int]]: A list of color tuples. - """ - assert isinstance(num_classes, int) - - if isinstance(palette, list): - dataset_palette = palette - elif isinstance(palette, tuple): - dataset_palette = [palette] * num_classes - elif palette == 'random' or palette is None: - state = np.random.get_state() - # random color - np.random.seed(42) - palette = np.random.randint(0, 256, size=(num_classes, 3)) - np.random.set_state(state) - dataset_palette = [tuple(c) for c in palette] - elif palette == 'coco': - from mmdet.datasets import CocoDataset, CocoPanopticDataset - dataset_palette = CocoDataset.PALETTE - if len(dataset_palette) < num_classes: - dataset_palette = CocoPanopticDataset.PALETTE - elif palette == 'citys': - from mmdet.datasets import CityscapesDataset - dataset_palette = CityscapesDataset.PALETTE - elif palette == 'voc': - from mmdet.datasets import VOCDataset - dataset_palette = VOCDataset.PALETTE - elif mmcv.is_str(palette): - dataset_palette = [mmcv.color_val(palette)[::-1]] * num_classes - else: - raise TypeError(f'Invalid type for palette: {type(palette)}') - - assert len(dataset_palette) >= num_classes, \ - 'The length of palette should not be less than `num_classes`.' - return dataset_palette diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/__init__.py b/cv/detection/autoassign/pytorch/mmdet/datasets/__init__.py deleted file mode 100755 index c91025936..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .builder import DATASETS, PIPELINES, build_dataloader, build_dataset -from .coco import CocoDataset -from .custom import CustomDataset -from .dataset_wrappers import (ClassBalancedDataset, ConcatDataset, - MultiImageMixDataset, RepeatDataset) -from .samplers import DistributedGroupSampler, DistributedSampler, GroupSampler -from .utils import (NumClassCheckHook, get_loading_pipeline, - replace_ImageToTensor) diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/api_wrappers/__init__.py b/cv/detection/autoassign/pytorch/mmdet/datasets/api_wrappers/__init__.py deleted file mode 100755 index af369500a..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/api_wrappers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .coco_api import COCO, COCOeval -from .panoptic_evaluation import pq_compute_multi_core, pq_compute_single_core - -__all__ = [ - 'COCO', 'COCOeval', 'pq_compute_multi_core', 'pq_compute_single_core' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/api_wrappers/coco_api.py b/cv/detection/autoassign/pytorch/mmdet/datasets/api_wrappers/coco_api.py deleted file mode 100755 index 370eb4c2a..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/api_wrappers/coco_api.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# This file add snake case alias for coco api - -import warnings - -import pycocotools -from pycocotools.coco import COCO as _COCO -from pycocotools.cocoeval import COCOeval as _COCOeval - - -class COCO(_COCO): - """This class is almost the same as official pycocotools package. - - It implements some snake case function aliases. So that the COCO class has - the same interface as LVIS class. - """ - - def __init__(self, annotation_file=None): - if getattr(pycocotools, '__version__', '0') >= '12.0.2': - warnings.warn( - 'mmpycocotools is deprecated. Please install official pycocotools by "pip install pycocotools"', # noqa: E501 - UserWarning) - super().__init__(annotation_file=annotation_file) - self.img_ann_map = self.imgToAnns - self.cat_img_map = self.catToImgs - - def get_ann_ids(self, img_ids=[], cat_ids=[], area_rng=[], iscrowd=None): - return self.getAnnIds(img_ids, cat_ids, area_rng, iscrowd) - - def get_cat_ids(self, cat_names=[], sup_names=[], cat_ids=[]): - return self.getCatIds(cat_names, sup_names, cat_ids) - - def get_img_ids(self, img_ids=[], cat_ids=[]): - return self.getImgIds(img_ids, cat_ids) - - def load_anns(self, ids): - return self.loadAnns(ids) - - def load_cats(self, ids): - return self.loadCats(ids) - - def load_imgs(self, ids): - return self.loadImgs(ids) - - -# just for the ease of import -COCOeval = _COCOeval diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/api_wrappers/panoptic_evaluation.py b/cv/detection/autoassign/pytorch/mmdet/datasets/api_wrappers/panoptic_evaluation.py deleted file mode 100755 index 3e1cf78cb..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/api_wrappers/panoptic_evaluation.py +++ /dev/null @@ -1,228 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -# Copyright (c) 2018, Alexander Kirillov -# This file supports `file_client` for `panopticapi`, -# the source code is copied from `panopticapi`, -# only the way to load the gt images is modified. -import multiprocessing -import os - -import mmcv -import numpy as np - -try: - from panopticapi.evaluation import OFFSET, VOID, PQStat - from panopticapi.utils import rgb2id -except ImportError: - PQStat = None - rgb2id = None - VOID = 0 - OFFSET = 256 * 256 * 256 - - -def pq_compute_single_core(proc_id, - annotation_set, - gt_folder, - pred_folder, - categories, - file_client=None, - print_log=False): - """The single core function to evaluate the metric of Panoptic - Segmentation. - - Same as the function with the same name in `panopticapi`. Only the function - to load the images is changed to use the file client. - - Args: - proc_id (int): The id of the mini process. - gt_folder (str): The path of the ground truth images. - pred_folder (str): The path of the prediction images. - categories (str): The categories of the dataset. - file_client (object): The file client of the dataset. If None, - the backend will be set to `disk`. - print_log (bool): Whether to print the log. Defaults to False. - """ - if PQStat is None: - raise RuntimeError( - 'panopticapi is not installed, please install it by: ' - 'pip install git+https://github.com/cocodataset/' - 'panopticapi.git.') - - if file_client is None: - file_client_args = dict(backend='disk') - file_client = mmcv.FileClient(**file_client_args) - - pq_stat = PQStat() - - idx = 0 - for gt_ann, pred_ann in annotation_set: - if print_log and idx % 100 == 0: - print('Core: {}, {} from {} images processed'.format( - proc_id, idx, len(annotation_set))) - idx += 1 - # The gt images can be on the local disk or `ceph`, so we use - # file_client here. - img_bytes = file_client.get( - os.path.join(gt_folder, gt_ann['file_name'])) - pan_gt = mmcv.imfrombytes(img_bytes, flag='color', channel_order='rgb') - pan_gt = rgb2id(pan_gt) - - # The predictions can only be on the local dist now. - pan_pred = mmcv.imread( - os.path.join(pred_folder, pred_ann['file_name']), - flag='color', - channel_order='rgb') - pan_pred = rgb2id(pan_pred) - - gt_segms = {el['id']: el for el in gt_ann['segments_info']} - pred_segms = {el['id']: el for el in pred_ann['segments_info']} - - # predicted segments area calculation + prediction sanity checks - pred_labels_set = set(el['id'] for el in pred_ann['segments_info']) - labels, labels_cnt = np.unique(pan_pred, return_counts=True) - for label, label_cnt in zip(labels, labels_cnt): - if label not in pred_segms: - if label == VOID: - continue - raise KeyError( - 'In the image with ID {} segment with ID {} is ' - 'presented in PNG and not presented in JSON.'.format( - gt_ann['image_id'], label)) - pred_segms[label]['area'] = label_cnt - pred_labels_set.remove(label) - if pred_segms[label]['category_id'] not in categories: - raise KeyError( - 'In the image with ID {} segment with ID {} has ' - 'unknown category_id {}.'.format( - gt_ann['image_id'], label, - pred_segms[label]['category_id'])) - if len(pred_labels_set) != 0: - raise KeyError( - 'In the image with ID {} the following segment IDs {} ' - 'are presented in JSON and not presented in PNG.'.format( - gt_ann['image_id'], list(pred_labels_set))) - - # confusion matrix calculation - pan_gt_pred = pan_gt.astype(np.uint64) * OFFSET + pan_pred.astype( - np.uint64) - gt_pred_map = {} - labels, labels_cnt = np.unique(pan_gt_pred, return_counts=True) - for label, intersection in zip(labels, labels_cnt): - gt_id = label // OFFSET - pred_id = label % OFFSET - gt_pred_map[(gt_id, pred_id)] = intersection - - # count all matched pairs - gt_matched = set() - pred_matched = set() - for label_tuple, intersection in gt_pred_map.items(): - gt_label, pred_label = label_tuple - if gt_label not in gt_segms: - continue - if pred_label not in pred_segms: - continue - if gt_segms[gt_label]['iscrowd'] == 1: - continue - if gt_segms[gt_label]['category_id'] != pred_segms[pred_label][ - 'category_id']: - continue - - union = pred_segms[pred_label]['area'] + gt_segms[gt_label][ - 'area'] - intersection - gt_pred_map.get((VOID, pred_label), 0) - iou = intersection / union - if iou > 0.5: - pq_stat[gt_segms[gt_label]['category_id']].tp += 1 - pq_stat[gt_segms[gt_label]['category_id']].iou += iou - gt_matched.add(gt_label) - pred_matched.add(pred_label) - - # count false positives - crowd_labels_dict = {} - for gt_label, gt_info in gt_segms.items(): - if gt_label in gt_matched: - continue - # crowd segments are ignored - if gt_info['iscrowd'] == 1: - crowd_labels_dict[gt_info['category_id']] = gt_label - continue - pq_stat[gt_info['category_id']].fn += 1 - - # count false positives - for pred_label, pred_info in pred_segms.items(): - if pred_label in pred_matched: - continue - # intersection of the segment with VOID - intersection = gt_pred_map.get((VOID, pred_label), 0) - # plus intersection with corresponding CROWD region if it exists - if pred_info['category_id'] in crowd_labels_dict: - intersection += gt_pred_map.get( - (crowd_labels_dict[pred_info['category_id']], pred_label), - 0) - # predicted segment is ignored if more than half of - # the segment correspond to VOID and CROWD regions - if intersection / pred_info['area'] > 0.5: - continue - pq_stat[pred_info['category_id']].fp += 1 - - if print_log: - print('Core: {}, all {} images processed'.format( - proc_id, len(annotation_set))) - return pq_stat - - -def pq_compute_multi_core(matched_annotations_list, - gt_folder, - pred_folder, - categories, - file_client=None, - nproc=32): - """Evaluate the metrics of Panoptic Segmentation with multithreading. - - Same as the function with the same name in `panopticapi`. - - Args: - matched_annotations_list (list): The matched annotation list. Each - element is a tuple of annotations of the same image with the - format (gt_anns, pred_anns). - gt_folder (str): The path of the ground truth images. - pred_folder (str): The path of the prediction images. - categories (str): The categories of the dataset. - file_client (object): The file client of the dataset. If None, - the backend will be set to `disk`. - nproc (int): Number of processes for panoptic quality computing. - Defaults to 32. When `nproc` exceeds the number of cpu cores, - the number of cpu cores is used. - """ - if PQStat is None: - raise RuntimeError( - 'panopticapi is not installed, please install it by: ' - 'pip install git+https://github.com/cocodataset/' - 'panopticapi.git.') - - if file_client is None: - file_client_args = dict(backend='disk') - file_client = mmcv.FileClient(**file_client_args) - - cpu_num = min(nproc, multiprocessing.cpu_count()) - - annotations_split = np.array_split(matched_annotations_list, cpu_num) - print('Number of cores: {}, images per core: {}'.format( - cpu_num, len(annotations_split[0]))) - workers = multiprocessing.Pool(processes=cpu_num) - processes = [] - for proc_id, annotation_set in enumerate(annotations_split): - p = workers.apply_async(pq_compute_single_core, - (proc_id, annotation_set, gt_folder, - pred_folder, categories, file_client)) - processes.append(p) - - # Close the process pool, otherwise it will lead to memory - # leaking problems. - workers.close() - workers.join() - - pq_stat = PQStat() - for p in processes: - pq_stat += p.get() - - return pq_stat diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/builder.py b/cv/detection/autoassign/pytorch/mmdet/datasets/builder.py deleted file mode 100755 index 70b8b9ef2..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/builder.py +++ /dev/null @@ -1,215 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import platform -import random -import warnings -from functools import partial - -import numpy as np -import torch -from mmcv.parallel import collate -from mmcv.runner import get_dist_info -from mmcv.utils import TORCH_VERSION, Registry, build_from_cfg, digit_version -from torch.utils.data import DataLoader - -from .samplers import (ClassAwareSampler, DistributedGroupSampler, - DistributedSampler, GroupSampler, InfiniteBatchSampler, - InfiniteGroupBatchSampler) - -if platform.system() != 'Windows': - # https://github.com/pytorch/pytorch/issues/973 - import resource - rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) - base_soft_limit = rlimit[0] - hard_limit = rlimit[1] - soft_limit = min(max(4096, base_soft_limit), hard_limit) - resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) - -DATASETS = Registry('dataset') -PIPELINES = Registry('pipeline') - - -def _concat_dataset(cfg, default_args=None): - from .dataset_wrappers import ConcatDataset - ann_files = cfg['ann_file'] - img_prefixes = cfg.get('img_prefix', None) - seg_prefixes = cfg.get('seg_prefix', None) - proposal_files = cfg.get('proposal_file', None) - separate_eval = cfg.get('separate_eval', True) - - datasets = [] - num_dset = len(ann_files) - for i in range(num_dset): - data_cfg = copy.deepcopy(cfg) - # pop 'separate_eval' since it is not a valid key for common datasets. - if 'separate_eval' in data_cfg: - data_cfg.pop('separate_eval') - data_cfg['ann_file'] = ann_files[i] - if isinstance(img_prefixes, (list, tuple)): - data_cfg['img_prefix'] = img_prefixes[i] - if isinstance(seg_prefixes, (list, tuple)): - data_cfg['seg_prefix'] = seg_prefixes[i] - if isinstance(proposal_files, (list, tuple)): - data_cfg['proposal_file'] = proposal_files[i] - datasets.append(build_dataset(data_cfg, default_args)) - - return ConcatDataset(datasets, separate_eval) - - -def build_dataset(cfg, default_args=None): - from .dataset_wrappers import (ClassBalancedDataset, ConcatDataset, - MultiImageMixDataset, RepeatDataset) - if isinstance(cfg, (list, tuple)): - dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) - elif cfg['type'] == 'ConcatDataset': - dataset = ConcatDataset( - [build_dataset(c, default_args) for c in cfg['datasets']], - cfg.get('separate_eval', True)) - elif cfg['type'] == 'RepeatDataset': - dataset = RepeatDataset( - build_dataset(cfg['dataset'], default_args), cfg['times']) - elif cfg['type'] == 'ClassBalancedDataset': - dataset = ClassBalancedDataset( - build_dataset(cfg['dataset'], default_args), cfg['oversample_thr']) - elif cfg['type'] == 'MultiImageMixDataset': - cp_cfg = copy.deepcopy(cfg) - cp_cfg['dataset'] = build_dataset(cp_cfg['dataset']) - cp_cfg.pop('type') - dataset = MultiImageMixDataset(**cp_cfg) - elif isinstance(cfg.get('ann_file'), (list, tuple)): - dataset = _concat_dataset(cfg, default_args) - else: - dataset = build_from_cfg(cfg, DATASETS, default_args) - - return dataset - - -def build_dataloader(dataset, - samples_per_gpu, - workers_per_gpu, - num_gpus=1, - dist=True, - shuffle=True, - seed=None, - runner_type='EpochBasedRunner', - persistent_workers=False, - class_aware_sampler=None, - **kwargs): - """Build PyTorch DataLoader. - - In distributed training, each GPU/process has a dataloader. - In non-distributed training, there is only one dataloader for all GPUs. - - Args: - dataset (Dataset): A PyTorch dataset. - samples_per_gpu (int): Number of training samples on each GPU, i.e., - batch size of each GPU. - workers_per_gpu (int): How many subprocesses to use for data loading - for each GPU. - num_gpus (int): Number of GPUs. Only used in non-distributed training. - dist (bool): Distributed training/test or not. Default: True. - shuffle (bool): Whether to shuffle the data at every epoch. - Default: True. - seed (int, Optional): Seed to be used. Default: None. - runner_type (str): Type of runner. Default: `EpochBasedRunner` - persistent_workers (bool): If True, the data loader will not shutdown - the worker processes after a dataset has been consumed once. - This allows to maintain the workers `Dataset` instances alive. - This argument is only valid when PyTorch>=1.7.0. Default: False. - class_aware_sampler (dict): Whether to use `ClassAwareSampler` - during training. Default: None. - kwargs: any keyword argument to be used to initialize DataLoader - - Returns: - DataLoader: A PyTorch dataloader. - """ - rank, world_size = get_dist_info() - - if dist: - # When model is :obj:`DistributedDataParallel`, - # `batch_size` of :obj:`dataloader` is the - # number of training samples on each GPU. - batch_size = samples_per_gpu - num_workers = workers_per_gpu - else: - # When model is obj:`DataParallel` - # the batch size is samples on all the GPUS - batch_size = num_gpus * samples_per_gpu - num_workers = num_gpus * workers_per_gpu - - if runner_type == 'IterBasedRunner': - # this is a batch sampler, which can yield - # a mini-batch indices each time. - # it can be used in both `DataParallel` and - # `DistributedDataParallel` - if shuffle: - batch_sampler = InfiniteGroupBatchSampler( - dataset, batch_size, world_size, rank, seed=seed) - else: - batch_sampler = InfiniteBatchSampler( - dataset, - batch_size, - world_size, - rank, - seed=seed, - shuffle=False) - batch_size = 1 - sampler = None - else: - if class_aware_sampler is not None: - # ClassAwareSampler can be used in both distributed and - # non-distributed training. - num_sample_class = class_aware_sampler.get('num_sample_class', 1) - sampler = ClassAwareSampler( - dataset, - samples_per_gpu, - world_size, - rank, - seed=seed, - num_sample_class=num_sample_class) - elif dist: - # DistributedGroupSampler will definitely shuffle the data to - # satisfy that images on each GPU are in the same group - if shuffle: - sampler = DistributedGroupSampler( - dataset, samples_per_gpu, world_size, rank, seed=seed) - else: - sampler = DistributedSampler( - dataset, world_size, rank, shuffle=False, seed=seed) - else: - sampler = GroupSampler(dataset, - samples_per_gpu) if shuffle else None - batch_sampler = None - - init_fn = partial( - worker_init_fn, num_workers=num_workers, rank=rank, - seed=seed) if seed is not None else None - - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.7.0')): - kwargs['persistent_workers'] = persistent_workers - elif persistent_workers is True: - warnings.warn('persistent_workers is invalid because your pytorch ' - 'version is lower than 1.7.0') - - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - batch_sampler=batch_sampler, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=kwargs.pop('pin_memory', False), - worker_init_fn=init_fn, - **kwargs) - - return data_loader - - -def worker_init_fn(worker_id, num_workers, rank, seed): - # The seed of each worker equals to - # num_worker * rank + worker_id + user_seed - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/coco.py b/cv/detection/autoassign/pytorch/mmdet/datasets/coco.py deleted file mode 100755 index 7998949b8..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/coco.py +++ /dev/null @@ -1,649 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import contextlib -import io -import itertools -import logging -import os.path as osp -import tempfile -import warnings -from collections import OrderedDict - -import mmcv -import numpy as np -from mmcv.utils import print_log -from terminaltables import AsciiTable - -from mmdet.core import eval_recalls -from .api_wrappers import COCO, COCOeval -from .builder import DATASETS -from .custom import CustomDataset - - -@DATASETS.register_module() -class CocoDataset(CustomDataset): - - CLASSES = ('person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', - 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', - 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', - 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', - 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', - 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', - 'baseball glove', 'skateboard', 'surfboard', 'tennis racket', - 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', - 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', - 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', - 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', - 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', - 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', - 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush') - - PALETTE = [(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230), - (106, 0, 228), (0, 60, 100), (0, 80, 100), (0, 0, 70), - (0, 0, 192), (250, 170, 30), (100, 170, 30), (220, 220, 0), - (175, 116, 175), (250, 0, 30), (165, 42, 42), (255, 77, 255), - (0, 226, 252), (182, 182, 255), (0, 82, 0), (120, 166, 157), - (110, 76, 0), (174, 57, 255), (199, 100, 0), (72, 0, 118), - (255, 179, 240), (0, 125, 92), (209, 0, 151), (188, 208, 182), - (0, 220, 176), (255, 99, 164), (92, 0, 73), (133, 129, 255), - (78, 180, 255), (0, 228, 0), (174, 255, 243), (45, 89, 255), - (134, 134, 103), (145, 148, 174), (255, 208, 186), - (197, 226, 255), (171, 134, 1), (109, 63, 54), (207, 138, 255), - (151, 0, 95), (9, 80, 61), (84, 105, 51), (74, 65, 105), - (166, 196, 102), (208, 195, 210), (255, 109, 65), (0, 143, 149), - (179, 0, 194), (209, 99, 106), (5, 121, 0), (227, 255, 205), - (147, 186, 208), (153, 69, 1), (3, 95, 161), (163, 255, 0), - (119, 0, 170), (0, 182, 199), (0, 165, 120), (183, 130, 88), - (95, 32, 0), (130, 114, 135), (110, 129, 133), (166, 74, 118), - (219, 142, 185), (79, 210, 114), (178, 90, 62), (65, 70, 15), - (127, 167, 115), (59, 105, 106), (142, 108, 45), (196, 172, 0), - (95, 54, 80), (128, 76, 255), (201, 57, 1), (246, 0, 122), - (191, 162, 208)] - - def load_annotations(self, ann_file): - """Load annotation from COCO style annotation file. - - Args: - ann_file (str): Path of annotation file. - - Returns: - list[dict]: Annotation info from COCO api. - """ - - self.coco = COCO(ann_file) - # The order of returned `cat_ids` will not - # change with the order of the CLASSES - self.cat_ids = self.coco.get_cat_ids(cat_names=self.CLASSES) - - self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)} - self.img_ids = self.coco.get_img_ids() - data_infos = [] - total_ann_ids = [] - for i in self.img_ids: - info = self.coco.load_imgs([i])[0] - info['filename'] = info['file_name'] - data_infos.append(info) - ann_ids = self.coco.get_ann_ids(img_ids=[i]) - total_ann_ids.extend(ann_ids) - assert len(set(total_ann_ids)) == len( - total_ann_ids), f"Annotation ids in '{ann_file}' are not unique!" - return data_infos - - def get_ann_info(self, idx): - """Get COCO annotation by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - img_id = self.data_infos[idx]['id'] - ann_ids = self.coco.get_ann_ids(img_ids=[img_id]) - ann_info = self.coco.load_anns(ann_ids) - return self._parse_ann_info(self.data_infos[idx], ann_info) - - def get_cat_ids(self, idx): - """Get COCO category ids by index. - - Args: - idx (int): Index of data. - - Returns: - list[int]: All categories in the image of specified index. - """ - - img_id = self.data_infos[idx]['id'] - ann_ids = self.coco.get_ann_ids(img_ids=[img_id]) - ann_info = self.coco.load_anns(ann_ids) - return [ann['category_id'] for ann in ann_info] - - def _filter_imgs(self, min_size=32): - """Filter images too small or without ground truths.""" - valid_inds = [] - # obtain images that contain annotation - ids_with_ann = set(_['image_id'] for _ in self.coco.anns.values()) - # obtain images that contain annotations of the required categories - ids_in_cat = set() - for i, class_id in enumerate(self.cat_ids): - ids_in_cat |= set(self.coco.cat_img_map[class_id]) - # merge the image id sets of the two conditions and use the merged set - # to filter out images if self.filter_empty_gt=True - ids_in_cat &= ids_with_ann - - valid_img_ids = [] - for i, img_info in enumerate(self.data_infos): - img_id = self.img_ids[i] - if self.filter_empty_gt and img_id not in ids_in_cat: - continue - if min(img_info['width'], img_info['height']) >= min_size: - valid_inds.append(i) - valid_img_ids.append(img_id) - self.img_ids = valid_img_ids - return valid_inds - - def _parse_ann_info(self, img_info, ann_info): - """Parse bbox and mask annotation. - - Args: - ann_info (list[dict]): Annotation info of an image. - with_mask (bool): Whether to parse mask annotations. - - Returns: - dict: A dict containing the following keys: bboxes, bboxes_ignore,\ - labels, masks, seg_map. "masks" are raw annotations and not \ - decoded into binary masks. - """ - gt_bboxes = [] - gt_labels = [] - gt_bboxes_ignore = [] - gt_masks_ann = [] - for i, ann in enumerate(ann_info): - if ann.get('ignore', False): - continue - x1, y1, w, h = ann['bbox'] - inter_w = max(0, min(x1 + w, img_info['width']) - max(x1, 0)) - inter_h = max(0, min(y1 + h, img_info['height']) - max(y1, 0)) - if inter_w * inter_h == 0: - continue - if ann['area'] <= 0 or w < 1 or h < 1: - continue - if ann['category_id'] not in self.cat_ids: - continue - bbox = [x1, y1, x1 + w, y1 + h] - if ann.get('iscrowd', False): - gt_bboxes_ignore.append(bbox) - else: - gt_bboxes.append(bbox) - gt_labels.append(self.cat2label[ann['category_id']]) - gt_masks_ann.append(ann.get('segmentation', None)) - - if gt_bboxes: - gt_bboxes = np.array(gt_bboxes, dtype=np.float32) - gt_labels = np.array(gt_labels, dtype=np.int64) - else: - gt_bboxes = np.zeros((0, 4), dtype=np.float32) - gt_labels = np.array([], dtype=np.int64) - - if gt_bboxes_ignore: - gt_bboxes_ignore = np.array(gt_bboxes_ignore, dtype=np.float32) - else: - gt_bboxes_ignore = np.zeros((0, 4), dtype=np.float32) - - seg_map = img_info['filename'].replace('jpg', 'png') - - ann = dict( - bboxes=gt_bboxes, - labels=gt_labels, - bboxes_ignore=gt_bboxes_ignore, - masks=gt_masks_ann, - seg_map=seg_map) - - return ann - - def xyxy2xywh(self, bbox): - """Convert ``xyxy`` style bounding boxes to ``xywh`` style for COCO - evaluation. - - Args: - bbox (numpy.ndarray): The bounding boxes, shape (4, ), in - ``xyxy`` order. - - Returns: - list[float]: The converted bounding boxes, in ``xywh`` order. - """ - - _bbox = bbox.tolist() - return [ - _bbox[0], - _bbox[1], - _bbox[2] - _bbox[0], - _bbox[3] - _bbox[1], - ] - - def _proposal2json(self, results): - """Convert proposal results to COCO json style.""" - json_results = [] - for idx in range(len(self)): - img_id = self.img_ids[idx] - bboxes = results[idx] - for i in range(bboxes.shape[0]): - data = dict() - data['image_id'] = img_id - data['bbox'] = self.xyxy2xywh(bboxes[i]) - data['score'] = float(bboxes[i][4]) - data['category_id'] = 1 - json_results.append(data) - return json_results - - def _det2json(self, results): - """Convert detection results to COCO json style.""" - json_results = [] - for idx in range(len(self)): - img_id = self.img_ids[idx] - result = results[idx] - for label in range(len(result)): - bboxes = result[label] - for i in range(bboxes.shape[0]): - data = dict() - data['image_id'] = img_id - data['bbox'] = self.xyxy2xywh(bboxes[i]) - data['score'] = float(bboxes[i][4]) - data['category_id'] = self.cat_ids[label] - json_results.append(data) - return json_results - - def _segm2json(self, results): - """Convert instance segmentation results to COCO json style.""" - bbox_json_results = [] - segm_json_results = [] - for idx in range(len(self)): - img_id = self.img_ids[idx] - det, seg = results[idx] - for label in range(len(det)): - # bbox results - bboxes = det[label] - for i in range(bboxes.shape[0]): - data = dict() - data['image_id'] = img_id - data['bbox'] = self.xyxy2xywh(bboxes[i]) - data['score'] = float(bboxes[i][4]) - data['category_id'] = self.cat_ids[label] - bbox_json_results.append(data) - - # segm results - # some detectors use different scores for bbox and mask - if isinstance(seg, tuple): - segms = seg[0][label] - mask_score = seg[1][label] - else: - segms = seg[label] - mask_score = [bbox[4] for bbox in bboxes] - for i in range(bboxes.shape[0]): - data = dict() - data['image_id'] = img_id - data['bbox'] = self.xyxy2xywh(bboxes[i]) - data['score'] = float(mask_score[i]) - data['category_id'] = self.cat_ids[label] - if isinstance(segms[i]['counts'], bytes): - segms[i]['counts'] = segms[i]['counts'].decode() - data['segmentation'] = segms[i] - segm_json_results.append(data) - return bbox_json_results, segm_json_results - - def results2json(self, results, outfile_prefix): - """Dump the detection results to a COCO style json file. - - There are 3 types of results: proposals, bbox predictions, mask - predictions, and they have different data types. This method will - automatically recognize the type, and dump them to json files. - - Args: - results (list[list | tuple | ndarray]): Testing results of the - dataset. - outfile_prefix (str): The filename prefix of the json files. If the - prefix is "somepath/xxx", the json files will be named - "somepath/xxx.bbox.json", "somepath/xxx.segm.json", - "somepath/xxx.proposal.json". - - Returns: - dict[str: str]: Possible keys are "bbox", "segm", "proposal", and \ - values are corresponding filenames. - """ - result_files = dict() - if isinstance(results[0], list): - json_results = self._det2json(results) - result_files['bbox'] = f'{outfile_prefix}.bbox.json' - result_files['proposal'] = f'{outfile_prefix}.bbox.json' - mmcv.dump(json_results, result_files['bbox']) - elif isinstance(results[0], tuple): - json_results = self._segm2json(results) - result_files['bbox'] = f'{outfile_prefix}.bbox.json' - result_files['proposal'] = f'{outfile_prefix}.bbox.json' - result_files['segm'] = f'{outfile_prefix}.segm.json' - mmcv.dump(json_results[0], result_files['bbox']) - mmcv.dump(json_results[1], result_files['segm']) - elif isinstance(results[0], np.ndarray): - json_results = self._proposal2json(results) - result_files['proposal'] = f'{outfile_prefix}.proposal.json' - mmcv.dump(json_results, result_files['proposal']) - else: - raise TypeError('invalid type of results') - return result_files - - def fast_eval_recall(self, results, proposal_nums, iou_thrs, logger=None): - gt_bboxes = [] - for i in range(len(self.img_ids)): - ann_ids = self.coco.get_ann_ids(img_ids=self.img_ids[i]) - ann_info = self.coco.load_anns(ann_ids) - if len(ann_info) == 0: - gt_bboxes.append(np.zeros((0, 4))) - continue - bboxes = [] - for ann in ann_info: - if ann.get('ignore', False) or ann['iscrowd']: - continue - x1, y1, w, h = ann['bbox'] - bboxes.append([x1, y1, x1 + w, y1 + h]) - bboxes = np.array(bboxes, dtype=np.float32) - if bboxes.shape[0] == 0: - bboxes = np.zeros((0, 4)) - gt_bboxes.append(bboxes) - - recalls = eval_recalls( - gt_bboxes, results, proposal_nums, iou_thrs, logger=logger) - ar = recalls.mean(axis=1) - return ar - - def format_results(self, results, jsonfile_prefix=None, **kwargs): - """Format the results to json (standard format for COCO evaluation). - - Args: - results (list[tuple | numpy.ndarray]): Testing results of the - dataset. - jsonfile_prefix (str | None): The prefix of json files. It includes - the file path and the prefix of filename, e.g., "a/b/prefix". - If not specified, a temp file will be created. Default: None. - - Returns: - tuple: (result_files, tmp_dir), result_files is a dict containing \ - the json filepaths, tmp_dir is the temporal directory created \ - for saving json files when jsonfile_prefix is not specified. - """ - assert isinstance(results, list), 'results must be a list' - assert len(results) == len(self), ( - 'The length of results is not equal to the dataset len: {} != {}'. - format(len(results), len(self))) - - if jsonfile_prefix is None: - tmp_dir = tempfile.TemporaryDirectory() - jsonfile_prefix = osp.join(tmp_dir.name, 'results') - else: - tmp_dir = None - result_files = self.results2json(results, jsonfile_prefix) - return result_files, tmp_dir - - def evaluate_det_segm(self, - results, - result_files, - coco_gt, - metrics, - logger=None, - classwise=False, - proposal_nums=(100, 300, 1000), - iou_thrs=None, - metric_items=None): - """Instance segmentation and object detection evaluation in COCO - protocol. - - Args: - results (list[list | tuple | dict]): Testing results of the - dataset. - result_files (dict[str, str]): a dict contains json file path. - coco_gt (COCO): COCO API object with ground truth annotation. - metric (str | list[str]): Metrics to be evaluated. Options are - 'bbox', 'segm', 'proposal', 'proposal_fast'. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - classwise (bool): Whether to evaluating the AP for each class. - proposal_nums (Sequence[int]): Proposal number used for evaluating - recalls, such as recall@100, recall@1000. - Default: (100, 300, 1000). - iou_thrs (Sequence[float], optional): IoU threshold used for - evaluating recalls/mAPs. If set to a list, the average of all - IoUs will also be computed. If not specified, [0.50, 0.55, - 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95] will be used. - Default: None. - metric_items (list[str] | str, optional): Metric items that will - be returned. If not specified, ``['AR@100', 'AR@300', - 'AR@1000', 'AR_s@1000', 'AR_m@1000', 'AR_l@1000' ]`` will be - used when ``metric=='proposal'``, ``['mAP', 'mAP_50', 'mAP_75', - 'mAP_s', 'mAP_m', 'mAP_l']`` will be used when - ``metric=='bbox' or metric=='segm'``. - - Returns: - dict[str, float]: COCO style evaluation metric. - """ - if iou_thrs is None: - iou_thrs = np.linspace( - .5, 0.95, int(np.round((0.95 - .5) / .05)) + 1, endpoint=True) - if metric_items is not None: - if not isinstance(metric_items, list): - metric_items = [metric_items] - - eval_results = OrderedDict() - for metric in metrics: - msg = f'Evaluating {metric}...' - if logger is None: - msg = '\n' + msg - print_log(msg, logger=logger) - - if metric == 'proposal_fast': - if isinstance(results[0], tuple): - raise KeyError('proposal_fast is not supported for ' - 'instance segmentation result.') - ar = self.fast_eval_recall( - results, proposal_nums, iou_thrs, logger='silent') - log_msg = [] - for i, num in enumerate(proposal_nums): - eval_results[f'AR@{num}'] = ar[i] - log_msg.append(f'\nAR@{num}\t{ar[i]:.4f}') - log_msg = ''.join(log_msg) - print_log(log_msg, logger=logger) - continue - - iou_type = 'bbox' if metric == 'proposal' else metric - if metric not in result_files: - raise KeyError(f'{metric} is not in results') - try: - predictions = mmcv.load(result_files[metric]) - if iou_type == 'segm': - # Refer to https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/coco.py#L331 # noqa - # When evaluating mask AP, if the results contain bbox, - # cocoapi will use the box area instead of the mask area - # for calculating the instance area. Though the overall AP - # is not affected, this leads to different - # small/medium/large mask AP results. - for x in predictions: - x.pop('bbox') - warnings.simplefilter('once') - warnings.warn( - 'The key "bbox" is deleted for more accurate mask AP ' - 'of small/medium/large instances since v2.12.0. This ' - 'does not change the overall mAP calculation.', - UserWarning) - coco_det = coco_gt.loadRes(predictions) - except IndexError: - print_log( - 'The testing results of the whole dataset is empty.', - logger=logger, - level=logging.ERROR) - break - - cocoEval = COCOeval(coco_gt, coco_det, iou_type) - cocoEval.params.catIds = self.cat_ids - cocoEval.params.imgIds = self.img_ids - cocoEval.params.maxDets = list(proposal_nums) - cocoEval.params.iouThrs = iou_thrs - # mapping of cocoEval.stats - coco_metric_names = { - 'mAP': 0, - 'mAP_50': 1, - 'mAP_75': 2, - 'mAP_s': 3, - 'mAP_m': 4, - 'mAP_l': 5, - 'AR@100': 6, - 'AR@300': 7, - 'AR@1000': 8, - 'AR_s@1000': 9, - 'AR_m@1000': 10, - 'AR_l@1000': 11 - } - if metric_items is not None: - for metric_item in metric_items: - if metric_item not in coco_metric_names: - raise KeyError( - f'metric item {metric_item} is not supported') - - if metric == 'proposal': - cocoEval.params.useCats = 0 - cocoEval.evaluate() - cocoEval.accumulate() - - # Save coco summarize print information to logger - redirect_string = io.StringIO() - with contextlib.redirect_stdout(redirect_string): - cocoEval.summarize() - print_log('\n' + redirect_string.getvalue(), logger=logger) - - if metric_items is None: - metric_items = [ - 'AR@100', 'AR@300', 'AR@1000', 'AR_s@1000', - 'AR_m@1000', 'AR_l@1000' - ] - - for item in metric_items: - val = float( - f'{cocoEval.stats[coco_metric_names[item]]:.3f}') - eval_results[item] = val - else: - cocoEval.evaluate() - cocoEval.accumulate() - - # Save coco summarize print information to logger - redirect_string = io.StringIO() - with contextlib.redirect_stdout(redirect_string): - cocoEval.summarize() - print_log('\n' + redirect_string.getvalue(), logger=logger) - - if classwise: # Compute per-category AP - # Compute per-category AP - # from https://github.com/facebookresearch/detectron2/ - precisions = cocoEval.eval['precision'] - # precision: (iou, recall, cls, area range, max dets) - assert len(self.cat_ids) == precisions.shape[2] - - results_per_category = [] - for idx, catId in enumerate(self.cat_ids): - # area range index 0: all area ranges - # max dets index -1: typically 100 per image - nm = self.coco.loadCats(catId)[0] - precision = precisions[:, :, idx, 0, -1] - precision = precision[precision > -1] - if precision.size: - ap = np.mean(precision) - else: - ap = float('nan') - results_per_category.append( - (f'{nm["name"]}', f'{float(ap):0.3f}')) - - num_columns = min(6, len(results_per_category) * 2) - results_flatten = list( - itertools.chain(*results_per_category)) - headers = ['category', 'AP'] * (num_columns // 2) - results_2d = itertools.zip_longest(*[ - results_flatten[i::num_columns] - for i in range(num_columns) - ]) - table_data = [headers] - table_data += [result for result in results_2d] - table = AsciiTable(table_data) - print_log('\n' + table.table, logger=logger) - - if metric_items is None: - metric_items = [ - 'mAP', 'mAP_50', 'mAP_75', 'mAP_s', 'mAP_m', 'mAP_l' - ] - - for metric_item in metric_items: - key = f'{metric}_{metric_item}' - val = float( - f'{cocoEval.stats[coco_metric_names[metric_item]]:.3f}' - ) - eval_results[key] = val - ap = cocoEval.stats[:6] - eval_results[f'{metric}_mAP_copypaste'] = ( - f'{ap[0]:.3f} {ap[1]:.3f} {ap[2]:.3f} {ap[3]:.3f} ' - f'{ap[4]:.3f} {ap[5]:.3f}') - - return eval_results - - def evaluate(self, - results, - metric='bbox', - logger=None, - jsonfile_prefix=None, - classwise=False, - proposal_nums=(100, 300, 1000), - iou_thrs=None, - metric_items=None): - """Evaluation in COCO protocol. - - Args: - results (list[list | tuple]): Testing results of the dataset. - metric (str | list[str]): Metrics to be evaluated. Options are - 'bbox', 'segm', 'proposal', 'proposal_fast'. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - jsonfile_prefix (str | None): The prefix of json files. It includes - the file path and the prefix of filename, e.g., "a/b/prefix". - If not specified, a temp file will be created. Default: None. - classwise (bool): Whether to evaluating the AP for each class. - proposal_nums (Sequence[int]): Proposal number used for evaluating - recalls, such as recall@100, recall@1000. - Default: (100, 300, 1000). - iou_thrs (Sequence[float], optional): IoU threshold used for - evaluating recalls/mAPs. If set to a list, the average of all - IoUs will also be computed. If not specified, [0.50, 0.55, - 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95] will be used. - Default: None. - metric_items (list[str] | str, optional): Metric items that will - be returned. If not specified, ``['AR@100', 'AR@300', - 'AR@1000', 'AR_s@1000', 'AR_m@1000', 'AR_l@1000' ]`` will be - used when ``metric=='proposal'``, ``['mAP', 'mAP_50', 'mAP_75', - 'mAP_s', 'mAP_m', 'mAP_l']`` will be used when - ``metric=='bbox' or metric=='segm'``. - - Returns: - dict[str, float]: COCO style evaluation metric. - """ - - metrics = metric if isinstance(metric, list) else [metric] - allowed_metrics = ['bbox', 'segm', 'proposal', 'proposal_fast'] - for metric in metrics: - if metric not in allowed_metrics: - raise KeyError(f'metric {metric} is not supported') - - coco_gt = self.coco - self.cat_ids = coco_gt.get_cat_ids(cat_names=self.CLASSES) - - result_files, tmp_dir = self.format_results(results, jsonfile_prefix) - eval_results = self.evaluate_det_segm(results, result_files, coco_gt, - metrics, logger, classwise, - proposal_nums, iou_thrs, - metric_items) - - if tmp_dir is not None: - tmp_dir.cleanup() - return eval_results diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/custom.py b/cv/detection/autoassign/pytorch/mmdet/datasets/custom.py deleted file mode 100755 index a2df0e0bd..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/custom.py +++ /dev/null @@ -1,410 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from collections import OrderedDict - -import mmcv -import numpy as np -from mmcv.utils import print_log -from terminaltables import AsciiTable -from torch.utils.data import Dataset - -from mmdet.core import eval_map, eval_recalls -from .builder import DATASETS -from .pipelines import Compose - - -@DATASETS.register_module() -class CustomDataset(Dataset): - """Custom dataset for detection. - - The annotation format is shown as follows. The `ann` field is optional for - testing. - - .. code-block:: none - - [ - { - 'filename': 'a.jpg', - 'width': 1280, - 'height': 720, - 'ann': { - 'bboxes': (n, 4) in (x1, y1, x2, y2) order. - 'labels': (n, ), - 'bboxes_ignore': (k, 4), (optional field) - 'labels_ignore': (k, 4) (optional field) - } - }, - ... - ] - - Args: - ann_file (str): Annotation file path. - pipeline (list[dict]): Processing pipeline. - classes (str | Sequence[str], optional): Specify classes to load. - If is None, ``cls.CLASSES`` will be used. Default: None. - data_root (str, optional): Data root for ``ann_file``, - ``img_prefix``, ``seg_prefix``, ``proposal_file`` if specified. - test_mode (bool, optional): If set True, annotation will not be loaded. - filter_empty_gt (bool, optional): If set true, images without bounding - boxes of the dataset's classes will be filtered out. This option - only works when `test_mode=False`, i.e., we never filter images - during tests. - """ - - CLASSES = None - - PALETTE = None - - def __init__(self, - ann_file, - pipeline, - classes=None, - data_root=None, - img_prefix='', - seg_prefix=None, - proposal_file=None, - test_mode=False, - filter_empty_gt=True, - file_client_args=dict(backend='disk')): - self.ann_file = ann_file - self.data_root = data_root - self.img_prefix = img_prefix - self.seg_prefix = seg_prefix - self.proposal_file = proposal_file - self.test_mode = test_mode - self.filter_empty_gt = filter_empty_gt - self.file_client = mmcv.FileClient(**file_client_args) - self.CLASSES = self.get_classes(classes) - - # join paths if data_root is specified - if self.data_root is not None: - if not osp.isabs(self.ann_file): - self.ann_file = osp.join(self.data_root, self.ann_file) - if not (self.img_prefix is None or osp.isabs(self.img_prefix)): - self.img_prefix = osp.join(self.data_root, self.img_prefix) - if not (self.seg_prefix is None or osp.isabs(self.seg_prefix)): - self.seg_prefix = osp.join(self.data_root, self.seg_prefix) - if not (self.proposal_file is None - or osp.isabs(self.proposal_file)): - self.proposal_file = osp.join(self.data_root, - self.proposal_file) - # load annotations (and proposals) - if hasattr(self.file_client, 'get_local_path'): - with self.file_client.get_local_path(self.ann_file) as local_path: - self.data_infos = self.load_annotations(local_path) - else: - warnings.warn( - 'The used MMCV version does not have get_local_path. ' - f'We treat the {self.ann_file} as local paths and it ' - 'might cause errors if the path is not a local path. ' - 'Please use MMCV>= 1.3.16 if you meet errors.') - self.data_infos = self.load_annotations(self.ann_file) - - if self.proposal_file is not None: - if hasattr(self.file_client, 'get_local_path'): - with self.file_client.get_local_path( - self.proposal_file) as local_path: - self.proposals = self.load_proposals(local_path) - else: - warnings.warn( - 'The used MMCV version does not have get_local_path. ' - f'We treat the {self.ann_file} as local paths and it ' - 'might cause errors if the path is not a local path. ' - 'Please use MMCV>= 1.3.16 if you meet errors.') - self.proposals = self.load_proposals(self.proposal_file) - else: - self.proposals = None - - # filter images too small and containing no annotations - if not test_mode: - valid_inds = self._filter_imgs() - self.data_infos = [self.data_infos[i] for i in valid_inds] - if self.proposals is not None: - self.proposals = [self.proposals[i] for i in valid_inds] - # set group flag for the sampler - self._set_group_flag() - - # processing pipeline - self.pipeline = Compose(pipeline) - - def __len__(self): - """Total number of samples of data.""" - return len(self.data_infos) - - def load_annotations(self, ann_file): - """Load annotation from annotation file.""" - return mmcv.load(ann_file) - - def load_proposals(self, proposal_file): - """Load proposal from proposal file.""" - return mmcv.load(proposal_file) - - def get_ann_info(self, idx): - """Get annotation by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - return self.data_infos[idx]['ann'] - - def get_cat_ids(self, idx): - """Get category ids by index. - - Args: - idx (int): Index of data. - - Returns: - list[int]: All categories in the image of specified index. - """ - - return self.data_infos[idx]['ann']['labels'].astype(np.int).tolist() - - def pre_pipeline(self, results): - """Prepare results dict for pipeline.""" - results['img_prefix'] = self.img_prefix - results['seg_prefix'] = self.seg_prefix - results['proposal_file'] = self.proposal_file - results['bbox_fields'] = [] - results['mask_fields'] = [] - results['seg_fields'] = [] - - def _filter_imgs(self, min_size=32): - """Filter images too small.""" - if self.filter_empty_gt: - warnings.warn( - 'CustomDataset does not support filtering empty gt images.') - valid_inds = [] - for i, img_info in enumerate(self.data_infos): - if min(img_info['width'], img_info['height']) >= min_size: - valid_inds.append(i) - return valid_inds - - def _set_group_flag(self): - """Set flag according to image aspect ratio. - - Images with aspect ratio greater than 1 will be set as group 1, - otherwise group 0. - """ - self.flag = np.zeros(len(self), dtype=np.uint8) - for i in range(len(self)): - img_info = self.data_infos[i] - if img_info['width'] / img_info['height'] > 1: - self.flag[i] = 1 - - def _rand_another(self, idx): - """Get another random index from the same group as the given index.""" - pool = np.where(self.flag == self.flag[idx])[0] - return np.random.choice(pool) - - def __getitem__(self, idx): - """Get training/test data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training/test data (with annotation if `test_mode` is set \ - True). - """ - - if self.test_mode: - return self.prepare_test_img(idx) - while True: - data = self.prepare_train_img(idx) - if data is None: - idx = self._rand_another(idx) - continue - return data - - def prepare_train_img(self, idx): - """Get training data and annotations after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Training data and annotation after pipeline with new keys \ - introduced by pipeline. - """ - - img_info = self.data_infos[idx] - ann_info = self.get_ann_info(idx) - results = dict(img_info=img_info, ann_info=ann_info) - if self.proposals is not None: - results['proposals'] = self.proposals[idx] - self.pre_pipeline(results) - return self.pipeline(results) - - def prepare_test_img(self, idx): - """Get testing data after pipeline. - - Args: - idx (int): Index of data. - - Returns: - dict: Testing data after pipeline with new keys introduced by \ - pipeline. - """ - - img_info = self.data_infos[idx] - results = dict(img_info=img_info) - if self.proposals is not None: - results['proposals'] = self.proposals[idx] - self.pre_pipeline(results) - return self.pipeline(results) - - @classmethod - def get_classes(cls, classes=None): - """Get class names of current dataset. - - Args: - classes (Sequence[str] | str | None): If classes is None, use - default CLASSES defined by builtin dataset. If classes is a - string, take it as a file name. The file contains the name of - classes where each line contains one class name. If classes is - a tuple or list, override the CLASSES defined by the dataset. - - Returns: - tuple[str] or list[str]: Names of categories of the dataset. - """ - if classes is None: - return cls.CLASSES - - if isinstance(classes, str): - # take it as a file path - class_names = mmcv.list_from_file(classes) - elif isinstance(classes, (tuple, list)): - class_names = classes - else: - raise ValueError(f'Unsupported type {type(classes)} of classes.') - - return class_names - - def get_cat2imgs(self): - """Get a dict with class as key and img_ids as values, which will be - used in :class:`ClassAwareSampler`. - - Returns: - dict[list]: A dict of per-label image list, - the item of the dict indicates a label index, - corresponds to the image index that contains the label. - """ - if self.CLASSES is None: - raise ValueError('self.CLASSES can not be None') - # sort the label index - cat2imgs = {i: [] for i in range(len(self.CLASSES))} - for i in range(len(self)): - cat_ids = set(self.get_cat_ids(i)) - for cat in cat_ids: - cat2imgs[cat].append(i) - return cat2imgs - - def format_results(self, results, **kwargs): - """Place holder to format result to dataset specific output.""" - - def evaluate(self, - results, - metric='mAP', - logger=None, - proposal_nums=(100, 300, 1000), - iou_thr=0.5, - scale_ranges=None): - """Evaluate the dataset. - - Args: - results (list): Testing results of the dataset. - metric (str | list[str]): Metrics to be evaluated. - logger (logging.Logger | None | str): Logger used for printing - related information during evaluation. Default: None. - proposal_nums (Sequence[int]): Proposal number used for evaluating - recalls, such as recall@100, recall@1000. - Default: (100, 300, 1000). - iou_thr (float | list[float]): IoU threshold. Default: 0.5. - scale_ranges (list[tuple] | None): Scale ranges for evaluating mAP. - Default: None. - """ - - if not isinstance(metric, str): - assert len(metric) == 1 - metric = metric[0] - allowed_metrics = ['mAP', 'recall'] - if metric not in allowed_metrics: - raise KeyError(f'metric {metric} is not supported') - annotations = [self.get_ann_info(i) for i in range(len(self))] - eval_results = OrderedDict() - iou_thrs = [iou_thr] if isinstance(iou_thr, float) else iou_thr - if metric == 'mAP': - assert isinstance(iou_thrs, list) - mean_aps = [] - for iou_thr in iou_thrs: - print_log(f'\n{"-" * 15}iou_thr: {iou_thr}{"-" * 15}') - mean_ap, _ = eval_map( - results, - annotations, - scale_ranges=scale_ranges, - iou_thr=iou_thr, - dataset=self.CLASSES, - logger=logger) - mean_aps.append(mean_ap) - eval_results[f'AP{int(iou_thr * 100):02d}'] = round(mean_ap, 3) - eval_results['mAP'] = sum(mean_aps) / len(mean_aps) - elif metric == 'recall': - gt_bboxes = [ann['bboxes'] for ann in annotations] - recalls = eval_recalls( - gt_bboxes, results, proposal_nums, iou_thr, logger=logger) - for i, num in enumerate(proposal_nums): - for j, iou in enumerate(iou_thrs): - eval_results[f'recall@{num}@{iou}'] = recalls[i, j] - if recalls.shape[1] > 1: - ar = recalls.mean(axis=1) - for i, num in enumerate(proposal_nums): - eval_results[f'AR@{num}'] = ar[i] - return eval_results - - def __repr__(self): - """Print the number of instance number.""" - dataset_type = 'Test' if self.test_mode else 'Train' - result = (f'\n{self.__class__.__name__} {dataset_type} dataset ' - f'with number of images {len(self)}, ' - f'and instance counts: \n') - if self.CLASSES is None: - result += 'Category names are not provided. \n' - return result - instance_count = np.zeros(len(self.CLASSES) + 1).astype(int) - # count the instance number in each image - for idx in range(len(self)): - label = self.get_ann_info(idx)['labels'] - unique, counts = np.unique(label, return_counts=True) - if len(unique) > 0: - # add the occurrence number to each class - instance_count[unique] += counts - else: - # background is the last index - instance_count[-1] += 1 - # create a table with category count - table_data = [['category', 'count'] * 5] - row_data = [] - for cls, count in enumerate(instance_count): - if cls < len(self.CLASSES): - row_data += [f'{cls} [{self.CLASSES[cls]}]', f'{count}'] - else: - # add the background number - row_data += ['-1 background', f'{count}'] - if len(row_data) == 10: - table_data.append(row_data) - row_data = [] - if len(row_data) >= 2: - if row_data[-1] == '0': - row_data = row_data[:-2] - if len(row_data) >= 2: - table_data.append([]) - table_data.append(row_data) - - table = AsciiTable(table_data) - result += table.table - return result diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/dataset_wrappers.py b/cv/detection/autoassign/pytorch/mmdet/datasets/dataset_wrappers.py deleted file mode 100755 index 205441536..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/dataset_wrappers.py +++ /dev/null @@ -1,456 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import bisect -import collections -import copy -import math -from collections import defaultdict - -import numpy as np -from mmcv.utils import build_from_cfg, print_log -from torch.utils.data.dataset import ConcatDataset as _ConcatDataset - -from .builder import DATASETS, PIPELINES -from .coco import CocoDataset - - -@DATASETS.register_module() -class ConcatDataset(_ConcatDataset): - """A wrapper of concatenated dataset. - - Same as :obj:`torch.utils.data.dataset.ConcatDataset`, but - concat the group flag for image aspect ratio. - - Args: - datasets (list[:obj:`Dataset`]): A list of datasets. - separate_eval (bool): Whether to evaluate the results - separately if it is used as validation dataset. - Defaults to True. - """ - - def __init__(self, datasets, separate_eval=True): - super(ConcatDataset, self).__init__(datasets) - self.CLASSES = datasets[0].CLASSES - self.PALETTE = getattr(datasets[0], 'PALETTE', None) - self.separate_eval = separate_eval - if not separate_eval: - if any([isinstance(ds, CocoDataset) for ds in datasets]): - raise NotImplementedError( - 'Evaluating concatenated CocoDataset as a whole is not' - ' supported! Please set "separate_eval=True"') - elif len(set([type(ds) for ds in datasets])) != 1: - raise NotImplementedError( - 'All the datasets should have same types') - - if hasattr(datasets[0], 'flag'): - flags = [] - for i in range(0, len(datasets)): - flags.append(datasets[i].flag) - self.flag = np.concatenate(flags) - - def get_cat_ids(self, idx): - """Get category ids of concatenated dataset by index. - - Args: - idx (int): Index of data. - - Returns: - list[int]: All categories in the image of specified index. - """ - - if idx < 0: - if -idx > len(self): - raise ValueError( - 'absolute value of index should not exceed dataset length') - idx = len(self) + idx - dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx) - if dataset_idx == 0: - sample_idx = idx - else: - sample_idx = idx - self.cumulative_sizes[dataset_idx - 1] - return self.datasets[dataset_idx].get_cat_ids(sample_idx) - - def get_ann_info(self, idx): - """Get annotation of concatenated dataset by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - if idx < 0: - if -idx > len(self): - raise ValueError( - 'absolute value of index should not exceed dataset length') - idx = len(self) + idx - dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx) - if dataset_idx == 0: - sample_idx = idx - else: - sample_idx = idx - self.cumulative_sizes[dataset_idx - 1] - return self.datasets[dataset_idx].get_ann_info(sample_idx) - - def evaluate(self, results, logger=None, **kwargs): - """Evaluate the results. - - Args: - results (list[list | tuple]): Testing results of the dataset. - logger (logging.Logger | str | None): Logger used for printing - related information during evaluation. Default: None. - - Returns: - dict[str: float]: AP results of the total dataset or each separate - dataset if `self.separate_eval=True`. - """ - assert len(results) == self.cumulative_sizes[-1], \ - ('Dataset and results have different sizes: ' - f'{self.cumulative_sizes[-1]} v.s. {len(results)}') - - # Check whether all the datasets support evaluation - for dataset in self.datasets: - assert hasattr(dataset, 'evaluate'), \ - f'{type(dataset)} does not implement evaluate function' - - if self.separate_eval: - dataset_idx = -1 - total_eval_results = dict() - for size, dataset in zip(self.cumulative_sizes, self.datasets): - start_idx = 0 if dataset_idx == -1 else \ - self.cumulative_sizes[dataset_idx] - end_idx = self.cumulative_sizes[dataset_idx + 1] - - results_per_dataset = results[start_idx:end_idx] - print_log( - f'\nEvaluateing {dataset.ann_file} with ' - f'{len(results_per_dataset)} images now', - logger=logger) - - eval_results_per_dataset = dataset.evaluate( - results_per_dataset, logger=logger, **kwargs) - dataset_idx += 1 - for k, v in eval_results_per_dataset.items(): - total_eval_results.update({f'{dataset_idx}_{k}': v}) - - return total_eval_results - elif any([isinstance(ds, CocoDataset) for ds in self.datasets]): - raise NotImplementedError( - 'Evaluating concatenated CocoDataset as a whole is not' - ' supported! Please set "separate_eval=True"') - elif len(set([type(ds) for ds in self.datasets])) != 1: - raise NotImplementedError( - 'All the datasets should have same types') - else: - original_data_infos = self.datasets[0].data_infos - self.datasets[0].data_infos = sum( - [dataset.data_infos for dataset in self.datasets], []) - eval_results = self.datasets[0].evaluate( - results, logger=logger, **kwargs) - self.datasets[0].data_infos = original_data_infos - return eval_results - - -@DATASETS.register_module() -class RepeatDataset: - """A wrapper of repeated dataset. - - The length of repeated dataset will be `times` larger than the original - dataset. This is useful when the data loading time is long but the dataset - is small. Using RepeatDataset can reduce the data loading time between - epochs. - - Args: - dataset (:obj:`Dataset`): The dataset to be repeated. - times (int): Repeat times. - """ - - def __init__(self, dataset, times): - self.dataset = dataset - self.times = times - self.CLASSES = dataset.CLASSES - self.PALETTE = getattr(dataset, 'PALETTE', None) - if hasattr(self.dataset, 'flag'): - self.flag = np.tile(self.dataset.flag, times) - - self._ori_len = len(self.dataset) - - def __getitem__(self, idx): - return self.dataset[idx % self._ori_len] - - def get_cat_ids(self, idx): - """Get category ids of repeat dataset by index. - - Args: - idx (int): Index of data. - - Returns: - list[int]: All categories in the image of specified index. - """ - - return self.dataset.get_cat_ids(idx % self._ori_len) - - def get_ann_info(self, idx): - """Get annotation of repeat dataset by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - - return self.dataset.get_ann_info(idx % self._ori_len) - - def __len__(self): - """Length after repetition.""" - return self.times * self._ori_len - - -# Modified from https://github.com/facebookresearch/detectron2/blob/41d475b75a230221e21d9cac5d69655e3415e3a4/detectron2/data/samplers/distributed_sampler.py#L57 # noqa -@DATASETS.register_module() -class ClassBalancedDataset: - """A wrapper of repeated dataset with repeat factor. - - Suitable for training on class imbalanced datasets like LVIS. Following - the sampling strategy in the `paper `_, - in each epoch, an image may appear multiple times based on its - "repeat factor". - The repeat factor for an image is a function of the frequency the rarest - category labeled in that image. The "frequency of category c" in [0, 1] - is defined by the fraction of images in the training set (without repeats) - in which category c appears. - The dataset needs to instantiate :func:`self.get_cat_ids` to support - ClassBalancedDataset. - - The repeat factor is computed as followed. - - 1. For each category c, compute the fraction # of images - that contain it: :math:`f(c)` - 2. For each category c, compute the category-level repeat factor: - :math:`r(c) = max(1, sqrt(t/f(c)))` - 3. For each image I, compute the image-level repeat factor: - :math:`r(I) = max_{c in I} r(c)` - - Args: - dataset (:obj:`CustomDataset`): The dataset to be repeated. - oversample_thr (float): frequency threshold below which data is - repeated. For categories with ``f_c >= oversample_thr``, there is - no oversampling. For categories with ``f_c < oversample_thr``, the - degree of oversampling following the square-root inverse frequency - heuristic above. - filter_empty_gt (bool, optional): If set true, images without bounding - boxes will not be oversampled. Otherwise, they will be categorized - as the pure background class and involved into the oversampling. - Default: True. - """ - - def __init__(self, dataset, oversample_thr, filter_empty_gt=True): - self.dataset = dataset - self.oversample_thr = oversample_thr - self.filter_empty_gt = filter_empty_gt - self.CLASSES = dataset.CLASSES - self.PALETTE = getattr(dataset, 'PALETTE', None) - - repeat_factors = self._get_repeat_factors(dataset, oversample_thr) - repeat_indices = [] - for dataset_idx, repeat_factor in enumerate(repeat_factors): - repeat_indices.extend([dataset_idx] * math.ceil(repeat_factor)) - self.repeat_indices = repeat_indices - - flags = [] - if hasattr(self.dataset, 'flag'): - for flag, repeat_factor in zip(self.dataset.flag, repeat_factors): - flags.extend([flag] * int(math.ceil(repeat_factor))) - assert len(flags) == len(repeat_indices) - self.flag = np.asarray(flags, dtype=np.uint8) - - def _get_repeat_factors(self, dataset, repeat_thr): - """Get repeat factor for each images in the dataset. - - Args: - dataset (:obj:`CustomDataset`): The dataset - repeat_thr (float): The threshold of frequency. If an image - contains the categories whose frequency below the threshold, - it would be repeated. - - Returns: - list[float]: The repeat factors for each images in the dataset. - """ - - # 1. For each category c, compute the fraction # of images - # that contain it: f(c) - category_freq = defaultdict(int) - num_images = len(dataset) - for idx in range(num_images): - cat_ids = set(self.dataset.get_cat_ids(idx)) - if len(cat_ids) == 0 and not self.filter_empty_gt: - cat_ids = set([len(self.CLASSES)]) - for cat_id in cat_ids: - category_freq[cat_id] += 1 - for k, v in category_freq.items(): - category_freq[k] = v / num_images - - # 2. For each category c, compute the category-level repeat factor: - # r(c) = max(1, sqrt(t/f(c))) - category_repeat = { - cat_id: max(1.0, math.sqrt(repeat_thr / cat_freq)) - for cat_id, cat_freq in category_freq.items() - } - - # 3. For each image I, compute the image-level repeat factor: - # r(I) = max_{c in I} r(c) - repeat_factors = [] - for idx in range(num_images): - cat_ids = set(self.dataset.get_cat_ids(idx)) - if len(cat_ids) == 0 and not self.filter_empty_gt: - cat_ids = set([len(self.CLASSES)]) - repeat_factor = 1 - if len(cat_ids) > 0: - repeat_factor = max( - {category_repeat[cat_id] - for cat_id in cat_ids}) - repeat_factors.append(repeat_factor) - - return repeat_factors - - def __getitem__(self, idx): - ori_index = self.repeat_indices[idx] - return self.dataset[ori_index] - - def get_ann_info(self, idx): - """Get annotation of dataset by index. - - Args: - idx (int): Index of data. - - Returns: - dict: Annotation info of specified index. - """ - ori_index = self.repeat_indices[idx] - return self.dataset.get_ann_info(ori_index) - - def __len__(self): - """Length after repetition.""" - return len(self.repeat_indices) - - -@DATASETS.register_module() -class MultiImageMixDataset: - """A wrapper of multiple images mixed dataset. - - Suitable for training on multiple images mixed data augmentation like - mosaic and mixup. For the augmentation pipeline of mixed image data, - the `get_indexes` method needs to be provided to obtain the image - indexes, and you can set `skip_flags` to change the pipeline running - process. At the same time, we provide the `dynamic_scale` parameter - to dynamically change the output image size. - - Args: - dataset (:obj:`CustomDataset`): The dataset to be mixed. - pipeline (Sequence[dict]): Sequence of transform object or - config dict to be composed. - dynamic_scale (tuple[int], optional): The image scale can be changed - dynamically. Default to None. It is deprecated. - skip_type_keys (list[str], optional): Sequence of type string to - be skip pipeline. Default to None. - max_refetch (int): The maximum number of retry iterations for getting - valid results from the pipeline. If the number of iterations is - greater than `max_refetch`, but results is still None, then the - iteration is terminated and raise the error. Default: 15. - """ - - def __init__(self, - dataset, - pipeline, - dynamic_scale=None, - skip_type_keys=None, - max_refetch=15): - if dynamic_scale is not None: - raise RuntimeError( - 'dynamic_scale is deprecated. Please use Resize pipeline ' - 'to achieve similar functions') - assert isinstance(pipeline, collections.abc.Sequence) - if skip_type_keys is not None: - assert all([ - isinstance(skip_type_key, str) - for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys - - self.pipeline = [] - self.pipeline_types = [] - for transform in pipeline: - if isinstance(transform, dict): - self.pipeline_types.append(transform['type']) - transform = build_from_cfg(transform, PIPELINES) - self.pipeline.append(transform) - else: - raise TypeError('pipeline must be a dict') - - self.dataset = dataset - self.CLASSES = dataset.CLASSES - self.PALETTE = getattr(dataset, 'PALETTE', None) - if hasattr(self.dataset, 'flag'): - self.flag = dataset.flag - self.num_samples = len(dataset) - self.max_refetch = max_refetch - - def __len__(self): - return self.num_samples - - def __getitem__(self, idx): - results = copy.deepcopy(self.dataset[idx]) - for (transform, transform_type) in zip(self.pipeline, - self.pipeline_types): - if self._skip_type_keys is not None and \ - transform_type in self._skip_type_keys: - continue - - if hasattr(transform, 'get_indexes'): - for i in range(self.max_refetch): - # Make sure the results passed the loading pipeline - # of the original dataset is not None. - indexes = transform.get_indexes(self.dataset) - if not isinstance(indexes, collections.abc.Sequence): - indexes = [indexes] - mix_results = [ - copy.deepcopy(self.dataset[index]) for index in indexes - ] - if None not in mix_results: - results['mix_results'] = mix_results - break - else: - raise RuntimeError( - 'The loading pipeline of the original dataset' - ' always return None. Please check the correctness ' - 'of the dataset and its pipeline.') - - for i in range(self.max_refetch): - # To confirm the results passed the training pipeline - # of the wrapper is not None. - updated_results = transform(copy.deepcopy(results)) - if updated_results is not None: - results = updated_results - break - else: - raise RuntimeError( - 'The training pipeline of the dataset wrapper' - ' always return None.Please check the correctness ' - 'of the dataset and its pipeline.') - - if 'mix_results' in results: - results.pop('mix_results') - - return results - - def update_skip_type_keys(self, skip_type_keys): - """Update skip_type_keys. It is called by an external hook. - - Args: - skip_type_keys (list[str], optional): Sequence of type - string to be skip pipeline. - """ - assert all([ - isinstance(skip_type_key, str) for skip_type_key in skip_type_keys - ]) - self._skip_type_keys = skip_type_keys diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/__init__.py b/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/__init__.py deleted file mode 100755 index 4ce7bebfe..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .compose import Compose -from .formatting import (Collect, DefaultFormatBundle, ImageToTensor, - ToDataContainer, ToTensor, Transpose, to_tensor) -from .loading import (FilterAnnotations, LoadAnnotations, LoadImageFromFile, - LoadImageFromWebcam, LoadMultiChannelImageFromFiles, - LoadPanopticAnnotations, LoadProposals) -from .test_time_aug import MultiScaleFlipAug -from .transforms import (Albu, CopyPaste, CutOut, Expand, MinIoURandomCrop, - MixUp, Mosaic, Normalize, Pad, PhotoMetricDistortion, - RandomAffine, RandomCenterCropPad, RandomCrop, - RandomFlip, RandomShift, Resize, SegRescale, - YOLOXHSVRandomAug) - diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/compose.py b/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/compose.py deleted file mode 100755 index 92c7bb525..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/compose.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections - -from mmcv.utils import build_from_cfg - -from ..builder import PIPELINES - - -@PIPELINES.register_module() -class Compose: - """Compose multiple transforms sequentially. - - Args: - transforms (Sequence[dict | callable]): Sequence of transform object or - config dict to be composed. - """ - - def __init__(self, transforms): - assert isinstance(transforms, collections.abc.Sequence) - self.transforms = [] - for transform in transforms: - if isinstance(transform, dict): - transform = build_from_cfg(transform, PIPELINES) - self.transforms.append(transform) - elif callable(transform): - self.transforms.append(transform) - else: - raise TypeError('transform must be callable or a dict') - - def __call__(self, data): - """Call function to apply transforms sequentially. - - Args: - data (dict): A result dict contains the data to transform. - - Returns: - dict: Transformed data. - """ - - for t in self.transforms: - data = t(data) - if data is None: - return None - return data - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - str_ = t.__repr__() - if 'Compose(' in str_: - str_ = str_.replace('\n', '\n ') - format_string += '\n' - format_string += f' {str_}' - format_string += '\n)' - return format_string diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/formating.py b/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/formating.py deleted file mode 100755 index 76d576c91..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/formating.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -import warnings - -from .formatting import * - -warnings.warn('DeprecationWarning: mmdet.datasets.pipelines.formating will be ' - 'deprecated, please replace it with ' - 'mmdet.datasets.pipelines.formatting.') diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/formatting.py b/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/formatting.py deleted file mode 100755 index b23e59584..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/formatting.py +++ /dev/null @@ -1,392 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import mmcv -import numpy as np -import torch -from mmcv.parallel import DataContainer as DC - -from ..builder import PIPELINES - - -def to_tensor(data): - """Convert objects of various python types to :obj:`torch.Tensor`. - - Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, - :class:`Sequence`, :class:`int` and :class:`float`. - - Args: - data (torch.Tensor | numpy.ndarray | Sequence | int | float): Data to - be converted. - """ - - if isinstance(data, torch.Tensor): - return data - elif isinstance(data, np.ndarray): - return torch.from_numpy(data) - elif isinstance(data, Sequence) and not mmcv.is_str(data): - return torch.tensor(data) - elif isinstance(data, int): - return torch.LongTensor([data]) - elif isinstance(data, float): - return torch.FloatTensor([data]) - else: - raise TypeError(f'type {type(data)} cannot be converted to tensor.') - - -@PIPELINES.register_module() -class ToTensor: - """Convert some results to :obj:`torch.Tensor` by given keys. - - Args: - keys (Sequence[str]): Keys that need to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert data in results to :obj:`torch.Tensor`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted - to :obj:`torch.Tensor`. - """ - for key in self.keys: - results[key] = to_tensor(results[key]) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class ImageToTensor: - """Convert image to :obj:`torch.Tensor` by given keys. - - The dimension order of input image is (H, W, C). The pipeline will convert - it to (C, H, W). If only 2 dimension (H, W) is given, the output would be - (1, H, W). - - Args: - keys (Sequence[str]): Key of images to be converted to Tensor. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function to convert image in results to :obj:`torch.Tensor` and - transpose the channel order. - - Args: - results (dict): Result dict contains the image data to convert. - - Returns: - dict: The result dict contains the image converted - to :obj:`torch.Tensor` and transposed to (C, H, W) order. - """ - for key in self.keys: - img = results[key] - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - results[key] = (to_tensor(img.transpose(2, 0, 1))).contiguous() - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class Transpose: - """Transpose some results by given keys. - - Args: - keys (Sequence[str]): Keys of results to be transposed. - order (Sequence[int]): Order of transpose. - """ - - def __init__(self, keys, order): - self.keys = keys - self.order = order - - def __call__(self, results): - """Call function to transpose the channel order of data in results. - - Args: - results (dict): Result dict contains the data to transpose. - - Returns: - dict: The result dict contains the data transposed to \ - ``self.order``. - """ - for key in self.keys: - results[key] = results[key].transpose(self.order) - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, order={self.order})' - - -@PIPELINES.register_module() -class ToDataContainer: - """Convert results to :obj:`mmcv.DataContainer` by given fields. - - Args: - fields (Sequence[dict]): Each field is a dict like - ``dict(key='xxx', **kwargs)``. The ``key`` in result will - be converted to :obj:`mmcv.DataContainer` with ``**kwargs``. - Default: ``(dict(key='img', stack=True), dict(key='gt_bboxes'), - dict(key='gt_labels'))``. - """ - - def __init__(self, - fields=(dict(key='img', stack=True), dict(key='gt_bboxes'), - dict(key='gt_labels'))): - self.fields = fields - - def __call__(self, results): - """Call function to convert data in results to - :obj:`mmcv.DataContainer`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted to \ - :obj:`mmcv.DataContainer`. - """ - - for field in self.fields: - field = field.copy() - key = field.pop('key') - results[key] = DC(results[key], **field) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(fields={self.fields})' - - -@PIPELINES.register_module() -class DefaultFormatBundle: - """Default formatting bundle. - - It simplifies the pipeline of formatting common fields, including "img", - "proposals", "gt_bboxes", "gt_labels", "gt_masks" and "gt_semantic_seg". - These fields are formatted as follows. - - - img: (1)transpose, (2)to tensor, (3)to DataContainer (stack=True) - - proposals: (1)to tensor, (2)to DataContainer - - gt_bboxes: (1)to tensor, (2)to DataContainer - - gt_bboxes_ignore: (1)to tensor, (2)to DataContainer - - gt_labels: (1)to tensor, (2)to DataContainer - - gt_masks: (1)to tensor, (2)to DataContainer (cpu_only=True) - - gt_semantic_seg: (1)unsqueeze dim-0 (2)to tensor, \ - (3)to DataContainer (stack=True) - - Args: - img_to_float (bool): Whether to force the image to be converted to - float type. Default: True. - pad_val (dict): A dict for padding value in batch collating, - the default value is `dict(img=0, masks=0, seg=255)`. - Without this argument, the padding value of "gt_semantic_seg" - will be set to 0 by default, which should be 255. - """ - - def __init__(self, - img_to_float=True, - pad_val=dict(img=0, masks=0, seg=255)): - self.img_to_float = img_to_float - self.pad_val = pad_val - - def __call__(self, results): - """Call function to transform and format common fields in results. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data that is formatted with \ - default bundle. - """ - - if 'img' in results: - img = results['img'] - if self.img_to_float is True and img.dtype == np.uint8: - # Normally, image is of uint8 type without normalization. - # At this time, it needs to be forced to be converted to - # flot32, otherwise the model training and inference - # will be wrong. Only used for YOLOX currently . - img = img.astype(np.float32) - # add default meta keys - results = self._add_default_meta_keys(results) - if len(img.shape) < 3: - img = np.expand_dims(img, -1) - img = np.ascontiguousarray(img.transpose(2, 0, 1)) - results['img'] = DC( - to_tensor(img), padding_value=self.pad_val['img'], stack=True) - for key in ['proposals', 'gt_bboxes', 'gt_bboxes_ignore', 'gt_labels']: - if key not in results: - continue - results[key] = DC(to_tensor(results[key])) - if 'gt_masks' in results: - results['gt_masks'] = DC( - results['gt_masks'], - padding_value=self.pad_val['masks'], - cpu_only=True) - if 'gt_semantic_seg' in results: - results['gt_semantic_seg'] = DC( - to_tensor(results['gt_semantic_seg'][None, ...]), - padding_value=self.pad_val['seg'], - stack=True) - return results - - def _add_default_meta_keys(self, results): - """Add default meta keys. - - We set default meta keys including `pad_shape`, `scale_factor` and - `img_norm_cfg` to avoid the case where no `Resize`, `Normalize` and - `Pad` are implemented during the whole pipeline. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - results (dict): Updated result dict contains the data to convert. - """ - img = results['img'] - results.setdefault('pad_shape', img.shape) - results.setdefault('scale_factor', 1.0) - num_channels = 1 if len(img.shape) < 3 else img.shape[2] - results.setdefault( - 'img_norm_cfg', - dict( - mean=np.zeros(num_channels, dtype=np.float32), - std=np.ones(num_channels, dtype=np.float32), - to_rgb=False)) - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(img_to_float={self.img_to_float})' - - -@PIPELINES.register_module() -class Collect: - """Collect data from the loader relevant to the specific task. - - This is usually the last stage of the data loader pipeline. Typically keys - is set to some subset of "img", "proposals", "gt_bboxes", - "gt_bboxes_ignore", "gt_labels", and/or "gt_masks". - - The "img_meta" item is always populated. The contents of the "img_meta" - dictionary depends on "meta_keys". By default this includes: - - - "img_shape": shape of the image input to the network as a tuple \ - (h, w, c). Note that images may be zero padded on the \ - bottom/right if the batch tensor is larger than this shape. - - - "scale_factor": a float indicating the preprocessing scale - - - "flip": a boolean indicating if image flip transform was used - - - "filename": path to the image file - - - "ori_shape": original shape of the image as a tuple (h, w, c) - - - "pad_shape": image shape after padding - - - "img_norm_cfg": a dict of normalization information: - - - mean - per channel mean subtraction - - std - per channel std divisor - - to_rgb - bool indicating if bgr was converted to rgb - - Args: - keys (Sequence[str]): Keys of results to be collected in ``data``. - meta_keys (Sequence[str], optional): Meta keys to be converted to - ``mmcv.DataContainer`` and collected in ``data[img_metas]``. - Default: ``('filename', 'ori_filename', 'ori_shape', 'img_shape', - 'pad_shape', 'scale_factor', 'flip', 'flip_direction', - 'img_norm_cfg')`` - """ - - def __init__(self, - keys, - meta_keys=('filename', 'ori_filename', 'ori_shape', - 'img_shape', 'pad_shape', 'scale_factor', 'flip', - 'flip_direction', 'img_norm_cfg')): - self.keys = keys - self.meta_keys = meta_keys - - def __call__(self, results): - """Call function to collect keys in results. The keys in ``meta_keys`` - will be converted to :obj:mmcv.DataContainer. - - Args: - results (dict): Result dict contains the data to collect. - - Returns: - dict: The result dict contains the following keys - - - keys in``self.keys`` - - ``img_metas`` - """ - - data = {} - img_meta = {} - for key in self.meta_keys: - img_meta[key] = results[key] - data['img_metas'] = DC(img_meta, cpu_only=True) - for key in self.keys: - data[key] = results[key] - return data - - def __repr__(self): - return self.__class__.__name__ + \ - f'(keys={self.keys}, meta_keys={self.meta_keys})' - - -@PIPELINES.register_module() -class WrapFieldsToLists: - """Wrap fields of the data dictionary into lists for evaluation. - - This class can be used as a last step of a test or validation - pipeline for single image evaluation or inference. - - Example: - >>> test_pipeline = [ - >>> dict(type='LoadImageFromFile'), - >>> dict(type='Normalize', - mean=[123.675, 116.28, 103.53], - std=[58.395, 57.12, 57.375], - to_rgb=True), - >>> dict(type='Pad', size_divisor=32), - >>> dict(type='ImageToTensor', keys=['img']), - >>> dict(type='Collect', keys=['img']), - >>> dict(type='WrapFieldsToLists') - >>> ] - """ - - def __call__(self, results): - """Call function to wrap fields into lists. - - Args: - results (dict): Result dict contains the data to wrap. - - Returns: - dict: The result dict where value of ``self.keys`` are wrapped \ - into list. - """ - - # Wrap dict fields into lists - for key, val in results.items(): - results[key] = [val] - return results - - def __repr__(self): - return f'{self.__class__.__name__}()' diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/loading.py b/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/loading.py deleted file mode 100755 index 224d612fb..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/loading.py +++ /dev/null @@ -1,643 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import numpy as np -import pycocotools.mask as maskUtils - -from mmdet.core import BitmapMasks, PolygonMasks -from ..builder import PIPELINES - -try: - from panopticapi.utils import rgb2id -except ImportError: - rgb2id = None - - -@PIPELINES.register_module() -class LoadImageFromFile: - """Load an image from file. - - Required keys are "img_prefix" and "img_info" (a dict that must contain the - key "filename"). Added or updated keys are "filename", "img", "img_shape", - "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`), - "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1). - - Args: - to_float32 (bool): Whether to convert the loaded image to a float32 - numpy array. If set to False, the loaded image is an uint8 array. - Defaults to False. - color_type (str): The flag argument for :func:`mmcv.imfrombytes`. - Defaults to 'color'. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - def __init__(self, - to_float32=False, - color_type='color', - channel_order='bgr', - file_client_args=dict(backend='disk')): - self.to_float32 = to_float32 - self.color_type = color_type - self.channel_order = channel_order - self.file_client_args = file_client_args.copy() - self.file_client = None - - def __call__(self, results): - """Call functions to load image and get image meta information. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded image and meta information. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results['img_prefix'] is not None: - filename = osp.join(results['img_prefix'], - results['img_info']['filename']) - else: - filename = results['img_info']['filename'] - - img_bytes = self.file_client.get(filename) - img = mmcv.imfrombytes( - img_bytes, flag=self.color_type, channel_order=self.channel_order) - if self.to_float32: - img = img.astype(np.float32) - - results['filename'] = filename - results['ori_filename'] = results['img_info']['filename'] - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - results['img_fields'] = ['img'] - return results - - def __repr__(self): - repr_str = (f'{self.__class__.__name__}(' - f'to_float32={self.to_float32}, ' - f"color_type='{self.color_type}', " - f"channel_order='{self.channel_order}', " - f'file_client_args={self.file_client_args})') - return repr_str - - -@PIPELINES.register_module() -class LoadImageFromWebcam(LoadImageFromFile): - """Load an image from webcam. - - Similar with :obj:`LoadImageFromFile`, but the image read from webcam is in - ``results['img']``. - """ - - def __call__(self, results): - """Call functions to add image meta information. - - Args: - results (dict): Result dict with Webcam read image in - ``results['img']``. - - Returns: - dict: The dict contains loaded image and meta information. - """ - - img = results['img'] - if self.to_float32: - img = img.astype(np.float32) - - results['filename'] = None - results['ori_filename'] = None - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - results['img_fields'] = ['img'] - return results - - -@PIPELINES.register_module() -class LoadMultiChannelImageFromFiles: - """Load multi-channel images from a list of separate channel files. - - Required keys are "img_prefix" and "img_info" (a dict that must contain the - key "filename", which is expected to be a list of filenames). - Added or updated keys are "filename", "img", "img_shape", - "ori_shape" (same as `img_shape`), "pad_shape" (same as `img_shape`), - "scale_factor" (1.0) and "img_norm_cfg" (means=0 and stds=1). - - Args: - to_float32 (bool): Whether to convert the loaded image to a float32 - numpy array. If set to False, the loaded image is an uint8 array. - Defaults to False. - color_type (str): The flag argument for :func:`mmcv.imfrombytes`. - Defaults to 'color'. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - def __init__(self, - to_float32=False, - color_type='unchanged', - file_client_args=dict(backend='disk')): - self.to_float32 = to_float32 - self.color_type = color_type - self.file_client_args = file_client_args.copy() - self.file_client = None - - def __call__(self, results): - """Call functions to load multiple images and get images meta - information. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded images and meta information. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - if results['img_prefix'] is not None: - filename = [ - osp.join(results['img_prefix'], fname) - for fname in results['img_info']['filename'] - ] - else: - filename = results['img_info']['filename'] - - img = [] - for name in filename: - img_bytes = self.file_client.get(name) - img.append(mmcv.imfrombytes(img_bytes, flag=self.color_type)) - img = np.stack(img, axis=-1) - if self.to_float32: - img = img.astype(np.float32) - - results['filename'] = filename - results['ori_filename'] = results['img_info']['filename'] - results['img'] = img - results['img_shape'] = img.shape - results['ori_shape'] = img.shape - # Set initial values for default meta_keys - results['pad_shape'] = img.shape - results['scale_factor'] = 1.0 - num_channels = 1 if len(img.shape) < 3 else img.shape[2] - results['img_norm_cfg'] = dict( - mean=np.zeros(num_channels, dtype=np.float32), - std=np.ones(num_channels, dtype=np.float32), - to_rgb=False) - return results - - def __repr__(self): - repr_str = (f'{self.__class__.__name__}(' - f'to_float32={self.to_float32}, ' - f"color_type='{self.color_type}', " - f'file_client_args={self.file_client_args})') - return repr_str - - -@PIPELINES.register_module() -class LoadAnnotations: - """Load multiple types of annotations. - - Args: - with_bbox (bool): Whether to parse and load the bbox annotation. - Default: True. - with_label (bool): Whether to parse and load the label annotation. - Default: True. - with_mask (bool): Whether to parse and load the mask annotation. - Default: False. - with_seg (bool): Whether to parse and load the semantic segmentation - annotation. Default: False. - poly2mask (bool): Whether to convert the instance masks from polygons - to bitmaps. Default: True. - denorm_bbox (bool): Whether to convert bbox from relative value to - absolute value. Only used in OpenImage Dataset. - Default: False. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - def __init__(self, - with_bbox=True, - with_label=True, - with_mask=False, - with_seg=False, - poly2mask=True, - denorm_bbox=False, - file_client_args=dict(backend='disk')): - self.with_bbox = with_bbox - self.with_label = with_label - self.with_mask = with_mask - self.with_seg = with_seg - self.poly2mask = poly2mask - self.denorm_bbox = denorm_bbox - self.file_client_args = file_client_args.copy() - self.file_client = None - - def _load_bboxes(self, results): - """Private function to load bounding box annotations. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded bounding box annotations. - """ - - ann_info = results['ann_info'] - results['gt_bboxes'] = ann_info['bboxes'].copy() - - if self.denorm_bbox: - bbox_num = results['gt_bboxes'].shape[0] - if bbox_num != 0: - h, w = results['img_shape'][:2] - results['gt_bboxes'][:, 0::2] *= w - results['gt_bboxes'][:, 1::2] *= h - - gt_bboxes_ignore = ann_info.get('bboxes_ignore', None) - if gt_bboxes_ignore is not None: - results['gt_bboxes_ignore'] = gt_bboxes_ignore.copy() - results['bbox_fields'].append('gt_bboxes_ignore') - results['bbox_fields'].append('gt_bboxes') - - gt_is_group_ofs = ann_info.get('gt_is_group_ofs', None) - if gt_is_group_ofs is not None: - results['gt_is_group_ofs'] = gt_is_group_ofs.copy() - - return results - - def _load_labels(self, results): - """Private function to load label annotations. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded label annotations. - """ - - results['gt_labels'] = results['ann_info']['labels'].copy() - return results - - def _poly2mask(self, mask_ann, img_h, img_w): - """Private function to convert masks represented with polygon to - bitmaps. - - Args: - mask_ann (list | dict): Polygon mask annotation input. - img_h (int): The height of output mask. - img_w (int): The width of output mask. - - Returns: - numpy.ndarray: The decode bitmap mask of shape (img_h, img_w). - """ - - if isinstance(mask_ann, list): - # polygon -- a single object might consist of multiple parts - # we merge all parts into one mask rle code - rles = maskUtils.frPyObjects(mask_ann, img_h, img_w) - rle = maskUtils.merge(rles) - elif isinstance(mask_ann['counts'], list): - # uncompressed RLE - rle = maskUtils.frPyObjects(mask_ann, img_h, img_w) - else: - # rle - rle = mask_ann - mask = maskUtils.decode(rle) - return mask - - def process_polygons(self, polygons): - """Convert polygons to list of ndarray and filter invalid polygons. - - Args: - polygons (list[list]): Polygons of one instance. - - Returns: - list[numpy.ndarray]: Processed polygons. - """ - - polygons = [np.array(p) for p in polygons] - valid_polygons = [] - for polygon in polygons: - if len(polygon) % 2 == 0 and len(polygon) >= 6: - valid_polygons.append(polygon) - return valid_polygons - - def _load_masks(self, results): - """Private function to load mask annotations. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded mask annotations. - If ``self.poly2mask`` is set ``True``, `gt_mask` will contain - :obj:`PolygonMasks`. Otherwise, :obj:`BitmapMasks` is used. - """ - - h, w = results['img_info']['height'], results['img_info']['width'] - gt_masks = results['ann_info']['masks'] - if self.poly2mask: - gt_masks = BitmapMasks( - [self._poly2mask(mask, h, w) for mask in gt_masks], h, w) - else: - gt_masks = PolygonMasks( - [self.process_polygons(polygons) for polygons in gt_masks], h, - w) - results['gt_masks'] = gt_masks - results['mask_fields'].append('gt_masks') - return results - - def _load_semantic_seg(self, results): - """Private function to load semantic segmentation annotations. - - Args: - results (dict): Result dict from :obj:`dataset`. - - Returns: - dict: The dict contains loaded semantic segmentation annotations. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - filename = osp.join(results['seg_prefix'], - results['ann_info']['seg_map']) - img_bytes = self.file_client.get(filename) - results['gt_semantic_seg'] = mmcv.imfrombytes( - img_bytes, flag='unchanged').squeeze() - results['seg_fields'].append('gt_semantic_seg') - return results - - def __call__(self, results): - """Call function to load multiple types annotations. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded bounding box, label, mask and - semantic segmentation annotations. - """ - - if self.with_bbox: - results = self._load_bboxes(results) - if results is None: - return None - if self.with_label: - results = self._load_labels(results) - if self.with_mask: - results = self._load_masks(results) - if self.with_seg: - results = self._load_semantic_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(with_bbox={self.with_bbox}, ' - repr_str += f'with_label={self.with_label}, ' - repr_str += f'with_mask={self.with_mask}, ' - repr_str += f'with_seg={self.with_seg}, ' - repr_str += f'poly2mask={self.poly2mask}, ' - repr_str += f'poly2mask={self.file_client_args})' - return repr_str - - -@PIPELINES.register_module() -class LoadPanopticAnnotations(LoadAnnotations): - """Load multiple types of panoptic annotations. - - Args: - with_bbox (bool): Whether to parse and load the bbox annotation. - Default: True. - with_label (bool): Whether to parse and load the label annotation. - Default: True. - with_mask (bool): Whether to parse and load the mask annotation. - Default: True. - with_seg (bool): Whether to parse and load the semantic segmentation - annotation. Default: True. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. - Defaults to ``dict(backend='disk')``. - """ - - def __init__(self, - with_bbox=True, - with_label=True, - with_mask=True, - with_seg=True, - file_client_args=dict(backend='disk')): - if rgb2id is None: - raise RuntimeError( - 'panopticapi is not installed, please install it by: ' - 'pip install git+https://github.com/cocodataset/' - 'panopticapi.git.') - - super(LoadPanopticAnnotations, self).__init__( - with_bbox=with_bbox, - with_label=with_label, - with_mask=with_mask, - with_seg=with_seg, - poly2mask=True, - denorm_bbox=False, - file_client_args=file_client_args) - - def _load_masks_and_semantic_segs(self, results): - """Private function to load mask and semantic segmentation annotations. - - In gt_semantic_seg, the foreground label is from `0` to - `num_things - 1`, the background label is from `num_things` to - `num_things + num_stuff - 1`, 255 means the ignored label (`VOID`). - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded mask and semantic segmentation - annotations. `BitmapMasks` is used for mask annotations. - """ - - if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) - - filename = osp.join(results['seg_prefix'], - results['ann_info']['seg_map']) - img_bytes = self.file_client.get(filename) - pan_png = mmcv.imfrombytes( - img_bytes, flag='color', channel_order='rgb').squeeze() - pan_png = rgb2id(pan_png) - - gt_masks = [] - gt_seg = np.zeros_like(pan_png) + 255 # 255 as ignore - - for mask_info in results['ann_info']['masks']: - mask = (pan_png == mask_info['id']) - gt_seg = np.where(mask, mask_info['category'], gt_seg) - - # The legal thing masks - if mask_info.get('is_thing'): - gt_masks.append(mask.astype(np.uint8)) - - if self.with_mask: - h, w = results['img_info']['height'], results['img_info']['width'] - gt_masks = BitmapMasks(gt_masks, h, w) - results['gt_masks'] = gt_masks - results['mask_fields'].append('gt_masks') - - if self.with_seg: - results['gt_semantic_seg'] = gt_seg - results['seg_fields'].append('gt_semantic_seg') - return results - - def __call__(self, results): - """Call function to load multiple types panoptic annotations. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded bounding box, label, mask and - semantic segmentation annotations. - """ - - if self.with_bbox: - results = self._load_bboxes(results) - if results is None: - return None - if self.with_label: - results = self._load_labels(results) - if self.with_mask or self.with_seg: - # The tasks completed by '_load_masks' and '_load_semantic_segs' - # in LoadAnnotations are merged to one function. - results = self._load_masks_and_semantic_segs(results) - - return results - - -@PIPELINES.register_module() -class LoadProposals: - """Load proposal pipeline. - - Required key is "proposals". Updated keys are "proposals", "bbox_fields". - - Args: - num_max_proposals (int, optional): Maximum number of proposals to load. - If not specified, all proposals will be loaded. - """ - - def __init__(self, num_max_proposals=None): - self.num_max_proposals = num_max_proposals - - def __call__(self, results): - """Call function to load proposals from file. - - Args: - results (dict): Result dict from :obj:`mmdet.CustomDataset`. - - Returns: - dict: The dict contains loaded proposal annotations. - """ - - proposals = results['proposals'] - if proposals.shape[1] not in (4, 5): - raise AssertionError( - 'proposals should have shapes (n, 4) or (n, 5), ' - f'but found {proposals.shape}') - proposals = proposals[:, :4] - - if self.num_max_proposals is not None: - proposals = proposals[:self.num_max_proposals] - - if len(proposals) == 0: - proposals = np.array([[0, 0, 0, 0]], dtype=np.float32) - results['proposals'] = proposals - results['bbox_fields'].append('proposals') - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(num_max_proposals={self.num_max_proposals})' - - -@PIPELINES.register_module() -class FilterAnnotations: - """Filter invalid annotations. - - Args: - min_gt_bbox_wh (tuple[float]): Minimum width and height of ground truth - boxes. Default: (1., 1.) - min_gt_mask_area (int): Minimum foreground area of ground truth masks. - Default: 1 - by_box (bool): Filter instances with bounding boxes not meeting the - min_gt_bbox_wh threshold. Default: True - by_mask (bool): Filter instances with masks not meeting - min_gt_mask_area threshold. Default: False - keep_empty (bool): Whether to return None when it - becomes an empty bbox after filtering. Default: True - """ - - def __init__(self, - min_gt_bbox_wh=(1., 1.), - min_gt_mask_area=1, - by_box=True, - by_mask=False, - keep_empty=True): - # TODO: add more filter options - assert by_box or by_mask - self.min_gt_bbox_wh = min_gt_bbox_wh - self.min_gt_mask_area = min_gt_mask_area - self.by_box = by_box - self.by_mask = by_mask - self.keep_empty = keep_empty - - def __call__(self, results): - if self.by_box: - assert 'gt_bboxes' in results - gt_bboxes = results['gt_bboxes'] - instance_num = gt_bboxes.shape[0] - if self.by_mask: - assert 'gt_masks' in results - gt_masks = results['gt_masks'] - instance_num = len(gt_masks) - - if instance_num == 0: - return results - - tests = [] - if self.by_box: - w = gt_bboxes[:, 2] - gt_bboxes[:, 0] - h = gt_bboxes[:, 3] - gt_bboxes[:, 1] - tests.append((w > self.min_gt_bbox_wh[0]) - & (h > self.min_gt_bbox_wh[1])) - if self.by_mask: - gt_masks = results['gt_masks'] - tests.append(gt_masks.areas >= self.min_gt_mask_area) - - keep = tests[0] - for t in tests[1:]: - keep = keep & t - - keys = ('gt_bboxes', 'gt_labels', 'gt_masks') - for key in keys: - if key in results: - results[key] = results[key][keep] - if not keep.any(): - if self.keep_empty: - return None - return results - - def __repr__(self): - return self.__class__.__name__ + \ - f'(min_gt_bbox_wh={self.min_gt_bbox_wh},' \ - f'(min_gt_mask_area={self.min_gt_mask_area},' \ - f'(by_box={self.by_box},' \ - f'(by_mask={self.by_mask},' \ - f'always_keep={self.always_keep})' diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/test_time_aug.py b/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/test_time_aug.py deleted file mode 100755 index c662a852c..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/test_time_aug.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import mmcv - -from ..builder import PIPELINES -from .compose import Compose - - -@PIPELINES.register_module() -class MultiScaleFlipAug: - """Test-time augmentation with multiple scales and flipping. - - An example configuration is as followed: - - .. code-block:: - - img_scale=[(1333, 400), (1333, 800)], - flip=True, - transforms=[ - dict(type='Resize', keep_ratio=True), - dict(type='RandomFlip'), - dict(type='Normalize', **img_norm_cfg), - dict(type='Pad', size_divisor=32), - dict(type='ImageToTensor', keys=['img']), - dict(type='Collect', keys=['img']), - ] - - After MultiScaleFLipAug with above configuration, the results are wrapped - into lists of the same length as followed: - - .. code-block:: - - dict( - img=[...], - img_shape=[...], - scale=[(1333, 400), (1333, 400), (1333, 800), (1333, 800)] - flip=[False, True, False, True] - ... - ) - - Args: - transforms (list[dict]): Transforms to apply in each augmentation. - img_scale (tuple | list[tuple] | None): Images scales for resizing. - scale_factor (float | list[float] | None): Scale factors for resizing. - flip (bool): Whether apply flip augmentation. Default: False. - flip_direction (str | list[str]): Flip augmentation directions, - options are "horizontal", "vertical" and "diagonal". If - flip_direction is a list, multiple flip augmentations will be - applied. It has no effect when flip == False. Default: - "horizontal". - """ - - def __init__(self, - transforms, - img_scale=None, - scale_factor=None, - flip=False, - flip_direction='horizontal'): - self.transforms = Compose(transforms) - assert (img_scale is None) ^ (scale_factor is None), ( - 'Must have but only one variable can be set') - if img_scale is not None: - self.img_scale = img_scale if isinstance(img_scale, - list) else [img_scale] - self.scale_key = 'scale' - assert mmcv.is_list_of(self.img_scale, tuple) - else: - self.img_scale = scale_factor if isinstance( - scale_factor, list) else [scale_factor] - self.scale_key = 'scale_factor' - - self.flip = flip - self.flip_direction = flip_direction if isinstance( - flip_direction, list) else [flip_direction] - assert mmcv.is_list_of(self.flip_direction, str) - if not self.flip and self.flip_direction != ['horizontal']: - warnings.warn( - 'flip_direction has no effect when flip is set to False') - if (self.flip - and not any([t['type'] == 'RandomFlip' for t in transforms])): - warnings.warn( - 'flip has no effect when RandomFlip is not in transforms') - - def __call__(self, results): - """Call function to apply test time augment transforms on results. - - Args: - results (dict): Result dict contains the data to transform. - - Returns: - dict[str: list]: The augmented data, where each value is wrapped - into a list. - """ - - aug_data = [] - flip_args = [(False, None)] - if self.flip: - flip_args += [(True, direction) - for direction in self.flip_direction] - for scale in self.img_scale: - for flip, direction in flip_args: - _results = results.copy() - _results[self.scale_key] = scale - _results['flip'] = flip - _results['flip_direction'] = direction - data = self.transforms(_results) - aug_data.append(data) - # list of dict to dict of list - aug_data_dict = {key: [] for key in aug_data[0]} - for data in aug_data: - for key, val in data.items(): - aug_data_dict[key].append(val) - return aug_data_dict - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(transforms={self.transforms}, ' - repr_str += f'img_scale={self.img_scale}, flip={self.flip}, ' - repr_str += f'flip_direction={self.flip_direction})' - return repr_str diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/transforms.py b/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/transforms.py deleted file mode 100755 index 5bed6d3aa..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/pipelines/transforms.py +++ /dev/null @@ -1,2919 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect -import math -import warnings - -import cv2 -import mmcv -import numpy as np -from numpy import random - -from mmdet.core import BitmapMasks, PolygonMasks, find_inside_bboxes -from mmdet.core.evaluation.bbox_overlaps import bbox_overlaps -from mmdet.utils import log_img_scale -from ..builder import PIPELINES - -try: - from imagecorruptions import corrupt -except ImportError: - corrupt = None - -try: - import albumentations - from albumentations import Compose -except ImportError: - albumentations = None - Compose = None - - -@PIPELINES.register_module() -class Resize: - """Resize images & bbox & mask. - - This transform resizes the input image to some scale. Bboxes and masks are - then resized with the same scale factor. If the input dict contains the key - "scale", then the scale in the input dict is used, otherwise the specified - scale in the init method is used. If the input dict contains the key - "scale_factor" (if MultiScaleFlipAug does not give img_scale but - scale_factor), the actual scale will be computed by image shape and - scale_factor. - - `img_scale` can either be a tuple (single-scale) or a list of tuple - (multi-scale). There are 3 multiscale modes: - - - ``ratio_range is not None``: randomly sample a ratio from the ratio \ - range and multiply it with the image scale. - - ``ratio_range is None`` and ``multiscale_mode == "range"``: randomly \ - sample a scale from the multiscale range. - - ``ratio_range is None`` and ``multiscale_mode == "value"``: randomly \ - sample a scale from multiple scales. - - Args: - img_scale (tuple or list[tuple]): Images scales for resizing. - multiscale_mode (str): Either "range" or "value". - ratio_range (tuple[float]): (min_ratio, max_ratio) - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. - bbox_clip_border (bool, optional): Whether to clip the objects outside - the border of the image. In some dataset like MOT17, the gt bboxes - are allowed to cross the border of images. Therefore, we don't - need to clip the gt bboxes in these cases. Defaults to True. - backend (str): Image resize backend, choices are 'cv2' and 'pillow'. - These two backends generates slightly different results. Defaults - to 'cv2'. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - override (bool, optional): Whether to override `scale` and - `scale_factor` so as to call resize twice. Default False. If True, - after the first resizing, the existed `scale` and `scale_factor` - will be ignored so the second resizing can be allowed. - This option is a work-around for multiple times of resize in DETR. - Defaults to False. - """ - - def __init__(self, - img_scale=None, - multiscale_mode='range', - ratio_range=None, - keep_ratio=True, - bbox_clip_border=True, - backend='cv2', - interpolation='bilinear', - override=False): - if img_scale is None: - self.img_scale = None - else: - if isinstance(img_scale, list): - self.img_scale = img_scale - else: - self.img_scale = [img_scale] - assert mmcv.is_list_of(self.img_scale, tuple) - - if ratio_range is not None: - # mode 1: given a scale and a range of image ratio - assert len(self.img_scale) == 1 - else: - # mode 2: given multiple scales or a range of scales - assert multiscale_mode in ['value', 'range'] - - self.backend = backend - self.multiscale_mode = multiscale_mode - self.ratio_range = ratio_range - self.keep_ratio = keep_ratio - # TODO: refactor the override option in Resize - self.interpolation = interpolation - self.override = override - self.bbox_clip_border = bbox_clip_border - - @staticmethod - def random_select(img_scales): - """Randomly select an img_scale from given candidates. - - Args: - img_scales (list[tuple]): Images scales for selection. - - Returns: - (tuple, int): Returns a tuple ``(img_scale, scale_dix)``, \ - where ``img_scale`` is the selected image scale and \ - ``scale_idx`` is the selected index in the given candidates. - """ - - assert mmcv.is_list_of(img_scales, tuple) - scale_idx = np.random.randint(len(img_scales)) - img_scale = img_scales[scale_idx] - return img_scale, scale_idx - - @staticmethod - def random_sample(img_scales): - """Randomly sample an img_scale when ``multiscale_mode=='range'``. - - Args: - img_scales (list[tuple]): Images scale range for sampling. - There must be two tuples in img_scales, which specify the lower - and upper bound of image scales. - - Returns: - (tuple, None): Returns a tuple ``(img_scale, None)``, where \ - ``img_scale`` is sampled scale and None is just a placeholder \ - to be consistent with :func:`random_select`. - """ - - assert mmcv.is_list_of(img_scales, tuple) and len(img_scales) == 2 - img_scale_long = [max(s) for s in img_scales] - img_scale_short = [min(s) for s in img_scales] - long_edge = np.random.randint( - min(img_scale_long), - max(img_scale_long) + 1) - short_edge = np.random.randint( - min(img_scale_short), - max(img_scale_short) + 1) - img_scale = (long_edge, short_edge) - return img_scale, None - - @staticmethod - def random_sample_ratio(img_scale, ratio_range): - """Randomly sample an img_scale when ``ratio_range`` is specified. - - A ratio will be randomly sampled from the range specified by - ``ratio_range``. Then it would be multiplied with ``img_scale`` to - generate sampled scale. - - Args: - img_scale (tuple): Images scale base to multiply with ratio. - ratio_range (tuple[float]): The minimum and maximum ratio to scale - the ``img_scale``. - - Returns: - (tuple, None): Returns a tuple ``(scale, None)``, where \ - ``scale`` is sampled ratio multiplied with ``img_scale`` and \ - None is just a placeholder to be consistent with \ - :func:`random_select`. - """ - - assert isinstance(img_scale, tuple) and len(img_scale) == 2 - min_ratio, max_ratio = ratio_range - assert min_ratio <= max_ratio - ratio = np.random.random_sample() * (max_ratio - min_ratio) + min_ratio - scale = int(img_scale[0] * ratio), int(img_scale[1] * ratio) - return scale, None - - def _random_scale(self, results): - """Randomly sample an img_scale according to ``ratio_range`` and - ``multiscale_mode``. - - If ``ratio_range`` is specified, a ratio will be sampled and be - multiplied with ``img_scale``. - If multiple scales are specified by ``img_scale``, a scale will be - sampled according to ``multiscale_mode``. - Otherwise, single scale will be used. - - Args: - results (dict): Result dict from :obj:`dataset`. - - Returns: - dict: Two new keys 'scale` and 'scale_idx` are added into \ - ``results``, which would be used by subsequent pipelines. - """ - - if self.ratio_range is not None: - scale, scale_idx = self.random_sample_ratio( - self.img_scale[0], self.ratio_range) - elif len(self.img_scale) == 1: - scale, scale_idx = self.img_scale[0], 0 - elif self.multiscale_mode == 'range': - scale, scale_idx = self.random_sample(self.img_scale) - elif self.multiscale_mode == 'value': - scale, scale_idx = self.random_select(self.img_scale) - else: - raise NotImplementedError - - results['scale'] = scale - results['scale_idx'] = scale_idx - - def _resize_img(self, results): - """Resize images with ``results['scale']``.""" - for key in results.get('img_fields', ['img']): - if self.keep_ratio: - img, scale_factor = mmcv.imrescale( - results[key], - results['scale'], - return_scale=True, - interpolation=self.interpolation, - backend=self.backend) - # the w_scale and h_scale has minor difference - # a real fix should be done in the mmcv.imrescale in the future - new_h, new_w = img.shape[:2] - h, w = results[key].shape[:2] - w_scale = new_w / w - h_scale = new_h / h - else: - img, w_scale, h_scale = mmcv.imresize( - results[key], - results['scale'], - return_scale=True, - interpolation=self.interpolation, - backend=self.backend) - results[key] = img - - scale_factor = np.array([w_scale, h_scale, w_scale, h_scale], - dtype=np.float32) - results['img_shape'] = img.shape - # in case that there is no padding - results['pad_shape'] = img.shape - results['scale_factor'] = scale_factor - results['keep_ratio'] = self.keep_ratio - - def _resize_bboxes(self, results): - """Resize bounding boxes with ``results['scale_factor']``.""" - for key in results.get('bbox_fields', []): - bboxes = results[key] * results['scale_factor'] - if self.bbox_clip_border: - img_shape = results['img_shape'] - bboxes[:, 0::2] = np.clip(bboxes[:, 0::2], 0, img_shape[1]) - bboxes[:, 1::2] = np.clip(bboxes[:, 1::2], 0, img_shape[0]) - results[key] = bboxes - - def _resize_masks(self, results): - """Resize masks with ``results['scale']``""" - for key in results.get('mask_fields', []): - if results[key] is None: - continue - if self.keep_ratio: - results[key] = results[key].rescale(results['scale']) - else: - results[key] = results[key].resize(results['img_shape'][:2]) - - def _resize_seg(self, results): - """Resize semantic segmentation map with ``results['scale']``.""" - for key in results.get('seg_fields', []): - if self.keep_ratio: - gt_seg = mmcv.imrescale( - results[key], - results['scale'], - interpolation='nearest', - backend=self.backend) - else: - gt_seg = mmcv.imresize( - results[key], - results['scale'], - interpolation='nearest', - backend=self.backend) - results[key] = gt_seg - - def __call__(self, results): - """Call function to resize images, bounding boxes, masks, semantic - segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Resized results, 'img_shape', 'pad_shape', 'scale_factor', \ - 'keep_ratio' keys are added into result dict. - """ - - if 'scale' not in results: - if 'scale_factor' in results: - img_shape = results['img'].shape[:2] - scale_factor = results['scale_factor'] - assert isinstance(scale_factor, float) - results['scale'] = tuple( - [int(x * scale_factor) for x in img_shape][::-1]) - else: - self._random_scale(results) - else: - if not self.override: - assert 'scale_factor' not in results, ( - 'scale and scale_factor cannot be both set.') - else: - results.pop('scale') - if 'scale_factor' in results: - results.pop('scale_factor') - self._random_scale(results) - - self._resize_img(results) - self._resize_bboxes(results) - self._resize_masks(results) - self._resize_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(img_scale={self.img_scale}, ' - repr_str += f'multiscale_mode={self.multiscale_mode}, ' - repr_str += f'ratio_range={self.ratio_range}, ' - repr_str += f'keep_ratio={self.keep_ratio}, ' - repr_str += f'bbox_clip_border={self.bbox_clip_border})' - return repr_str - - -@PIPELINES.register_module() -class RandomFlip: - """Flip the image & bbox & mask. - - If the input dict contains the key "flip", then the flag will be used, - otherwise it will be randomly decided by a ratio specified in the init - method. - - When random flip is enabled, ``flip_ratio``/``direction`` can either be a - float/string or tuple of float/string. There are 3 flip modes: - - - ``flip_ratio`` is float, ``direction`` is string: the image will be - ``direction``ly flipped with probability of ``flip_ratio`` . - E.g., ``flip_ratio=0.5``, ``direction='horizontal'``, - then image will be horizontally flipped with probability of 0.5. - - ``flip_ratio`` is float, ``direction`` is list of string: the image will - be ``direction[i]``ly flipped with probability of - ``flip_ratio/len(direction)``. - E.g., ``flip_ratio=0.5``, ``direction=['horizontal', 'vertical']``, - then image will be horizontally flipped with probability of 0.25, - vertically with probability of 0.25. - - ``flip_ratio`` is list of float, ``direction`` is list of string: - given ``len(flip_ratio) == len(direction)``, the image will - be ``direction[i]``ly flipped with probability of ``flip_ratio[i]``. - E.g., ``flip_ratio=[0.3, 0.5]``, ``direction=['horizontal', - 'vertical']``, then image will be horizontally flipped with probability - of 0.3, vertically with probability of 0.5. - - Args: - flip_ratio (float | list[float], optional): The flipping probability. - Default: None. - direction(str | list[str], optional): The flipping direction. Options - are 'horizontal', 'vertical', 'diagonal'. Default: 'horizontal'. - If input is a list, the length must equal ``flip_ratio``. Each - element in ``flip_ratio`` indicates the flip probability of - corresponding direction. - """ - - def __init__(self, flip_ratio=None, direction='horizontal'): - if isinstance(flip_ratio, list): - assert mmcv.is_list_of(flip_ratio, float) - assert 0 <= sum(flip_ratio) <= 1 - elif isinstance(flip_ratio, float): - assert 0 <= flip_ratio <= 1 - elif flip_ratio is None: - pass - else: - raise ValueError('flip_ratios must be None, float, ' - 'or list of float') - self.flip_ratio = flip_ratio - - valid_directions = ['horizontal', 'vertical', 'diagonal'] - if isinstance(direction, str): - assert direction in valid_directions - elif isinstance(direction, list): - assert mmcv.is_list_of(direction, str) - assert set(direction).issubset(set(valid_directions)) - else: - raise ValueError('direction must be either str or list of str') - self.direction = direction - - if isinstance(flip_ratio, list): - assert len(self.flip_ratio) == len(self.direction) - - def bbox_flip(self, bboxes, img_shape, direction): - """Flip bboxes horizontally. - - Args: - bboxes (numpy.ndarray): Bounding boxes, shape (..., 4*k) - img_shape (tuple[int]): Image shape (height, width) - direction (str): Flip direction. Options are 'horizontal', - 'vertical'. - - Returns: - numpy.ndarray: Flipped bounding boxes. - """ - - assert bboxes.shape[-1] % 4 == 0 - flipped = bboxes.copy() - if direction == 'horizontal': - w = img_shape[1] - flipped[..., 0::4] = w - bboxes[..., 2::4] - flipped[..., 2::4] = w - bboxes[..., 0::4] - elif direction == 'vertical': - h = img_shape[0] - flipped[..., 1::4] = h - bboxes[..., 3::4] - flipped[..., 3::4] = h - bboxes[..., 1::4] - elif direction == 'diagonal': - w = img_shape[1] - h = img_shape[0] - flipped[..., 0::4] = w - bboxes[..., 2::4] - flipped[..., 1::4] = h - bboxes[..., 3::4] - flipped[..., 2::4] = w - bboxes[..., 0::4] - flipped[..., 3::4] = h - bboxes[..., 1::4] - else: - raise ValueError(f"Invalid flipping direction '{direction}'") - return flipped - - def __call__(self, results): - """Call function to flip bounding boxes, masks, semantic segmentation - maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Flipped results, 'flip', 'flip_direction' keys are added \ - into result dict. - """ - - if 'flip' not in results: - if isinstance(self.direction, list): - # None means non-flip - direction_list = self.direction + [None] - else: - # None means non-flip - direction_list = [self.direction, None] - - if isinstance(self.flip_ratio, list): - non_flip_ratio = 1 - sum(self.flip_ratio) - flip_ratio_list = self.flip_ratio + [non_flip_ratio] - else: - non_flip_ratio = 1 - self.flip_ratio - # exclude non-flip - single_ratio = self.flip_ratio / (len(direction_list) - 1) - flip_ratio_list = [single_ratio] * (len(direction_list) - - 1) + [non_flip_ratio] - - cur_dir = np.random.choice(direction_list, p=flip_ratio_list) - - results['flip'] = cur_dir is not None - if 'flip_direction' not in results: - results['flip_direction'] = cur_dir - if results['flip']: - # flip image - for key in results.get('img_fields', ['img']): - results[key] = mmcv.imflip( - results[key], direction=results['flip_direction']) - # flip bboxes - for key in results.get('bbox_fields', []): - results[key] = self.bbox_flip(results[key], - results['img_shape'], - results['flip_direction']) - # flip masks - for key in results.get('mask_fields', []): - results[key] = results[key].flip(results['flip_direction']) - - # flip segs - for key in results.get('seg_fields', []): - results[key] = mmcv.imflip( - results[key], direction=results['flip_direction']) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(flip_ratio={self.flip_ratio})' - - -@PIPELINES.register_module() -class RandomShift: - """Shift the image and box given shift pixels and probability. - - Args: - shift_ratio (float): Probability of shifts. Default 0.5. - max_shift_px (int): The max pixels for shifting. Default 32. - filter_thr_px (int): The width and height threshold for filtering. - The bbox and the rest of the targets below the width and - height threshold will be filtered. Default 1. - """ - - def __init__(self, shift_ratio=0.5, max_shift_px=32, filter_thr_px=1): - assert 0 <= shift_ratio <= 1 - assert max_shift_px >= 0 - self.shift_ratio = shift_ratio - self.max_shift_px = max_shift_px - self.filter_thr_px = int(filter_thr_px) - # The key correspondence from bboxes to labels. - self.bbox2label = { - 'gt_bboxes': 'gt_labels', - 'gt_bboxes_ignore': 'gt_labels_ignore' - } - - def __call__(self, results): - """Call function to random shift images, bounding boxes. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Shift results. - """ - if random.random() < self.shift_ratio: - img_shape = results['img'].shape[:2] - - random_shift_x = random.randint(-self.max_shift_px, - self.max_shift_px) - random_shift_y = random.randint(-self.max_shift_px, - self.max_shift_px) - new_x = max(0, random_shift_x) - ori_x = max(0, -random_shift_x) - new_y = max(0, random_shift_y) - ori_y = max(0, -random_shift_y) - - # TODO: support mask and semantic segmentation maps. - for key in results.get('bbox_fields', []): - bboxes = results[key].copy() - bboxes[..., 0::2] += random_shift_x - bboxes[..., 1::2] += random_shift_y - - # clip border - bboxes[..., 0::2] = np.clip(bboxes[..., 0::2], 0, img_shape[1]) - bboxes[..., 1::2] = np.clip(bboxes[..., 1::2], 0, img_shape[0]) - - # remove invalid bboxes - bbox_w = bboxes[..., 2] - bboxes[..., 0] - bbox_h = bboxes[..., 3] - bboxes[..., 1] - valid_inds = (bbox_w > self.filter_thr_px) & ( - bbox_h > self.filter_thr_px) - # If the shift does not contain any gt-bbox area, skip this - # image. - if key == 'gt_bboxes' and not valid_inds.any(): - return results - bboxes = bboxes[valid_inds] - results[key] = bboxes - - # label fields. e.g. gt_labels and gt_labels_ignore - label_key = self.bbox2label.get(key) - if label_key in results: - results[label_key] = results[label_key][valid_inds] - - for key in results.get('img_fields', ['img']): - img = results[key] - new_img = np.zeros_like(img) - img_h, img_w = img.shape[:2] - new_h = img_h - np.abs(random_shift_y) - new_w = img_w - np.abs(random_shift_x) - new_img[new_y:new_y + new_h, new_x:new_x + new_w] \ - = img[ori_y:ori_y + new_h, ori_x:ori_x + new_w] - results[key] = new_img - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(max_shift_px={self.max_shift_px}, ' - return repr_str - - -@PIPELINES.register_module() -class Pad: - """Pad the image & masks & segmentation map. - - There are two padding modes: (1) pad to a fixed size and (2) pad to the - minimum size that is divisible by some number. - Added keys are "pad_shape", "pad_fixed_size", "pad_size_divisor", - - Args: - size (tuple, optional): Fixed padding size. - size_divisor (int, optional): The divisor of padded size. - pad_to_square (bool): Whether to pad the image into a square. - Currently only used for YOLOX. Default: False. - pad_val (dict, optional): A dict for padding value, the default - value is `dict(img=0, masks=0, seg=255)`. - """ - - def __init__(self, - size=None, - size_divisor=None, - pad_to_square=False, - pad_val=dict(img=0, masks=0, seg=255)): - self.size = size - self.size_divisor = size_divisor - if isinstance(pad_val, float) or isinstance(pad_val, int): - warnings.warn( - 'pad_val of float type is deprecated now, ' - f'please use pad_val=dict(img={pad_val}, ' - f'masks={pad_val}, seg=255) instead.', DeprecationWarning) - pad_val = dict(img=pad_val, masks=pad_val, seg=255) - assert isinstance(pad_val, dict) - self.pad_val = pad_val - self.pad_to_square = pad_to_square - - if pad_to_square: - assert size is None and size_divisor is None, \ - 'The size and size_divisor must be None ' \ - 'when pad2square is True' - else: - assert size is not None or size_divisor is not None, \ - 'only one of size and size_divisor should be valid' - assert size is None or size_divisor is None - - def _pad_img(self, results): - """Pad images according to ``self.size``.""" - pad_val = self.pad_val.get('img', 0) - for key in results.get('img_fields', ['img']): - if self.pad_to_square: - max_size = max(results[key].shape[:2]) - self.size = (max_size, max_size) - if self.size is not None: - padded_img = mmcv.impad( - results[key], shape=self.size, pad_val=pad_val) - elif self.size_divisor is not None: - padded_img = mmcv.impad_to_multiple( - results[key], self.size_divisor, pad_val=pad_val) - results[key] = padded_img - results['pad_shape'] = padded_img.shape - results['pad_fixed_size'] = self.size - results['pad_size_divisor'] = self.size_divisor - - def _pad_masks(self, results): - """Pad masks according to ``results['pad_shape']``.""" - pad_shape = results['pad_shape'][:2] - pad_val = self.pad_val.get('masks', 0) - for key in results.get('mask_fields', []): - results[key] = results[key].pad(pad_shape, pad_val=pad_val) - - def _pad_seg(self, results): - """Pad semantic segmentation map according to - ``results['pad_shape']``.""" - pad_val = self.pad_val.get('seg', 255) - for key in results.get('seg_fields', []): - results[key] = mmcv.impad( - results[key], shape=results['pad_shape'][:2], pad_val=pad_val) - - def __call__(self, results): - """Call function to pad images, masks, semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Updated result dict. - """ - self._pad_img(results) - self._pad_masks(results) - self._pad_seg(results) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(size={self.size}, ' - repr_str += f'size_divisor={self.size_divisor}, ' - repr_str += f'pad_to_square={self.pad_to_square}, ' - repr_str += f'pad_val={self.pad_val})' - return repr_str - - -@PIPELINES.register_module() -class Normalize: - """Normalize the image. - - Added key is "img_norm_cfg". - - Args: - mean (sequence): Mean values of 3 channels. - std (sequence): Std values of 3 channels. - to_rgb (bool): Whether to convert the image from BGR to RGB, - default is true. - """ - - def __init__(self, mean, std, to_rgb=True): - self.mean = np.array(mean, dtype=np.float32) - self.std = np.array(std, dtype=np.float32) - self.to_rgb = to_rgb - - def __call__(self, results): - """Call function to normalize images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Normalized results, 'img_norm_cfg' key is added into - result dict. - """ - for key in results.get('img_fields', ['img']): - results[key] = mmcv.imnormalize(results[key], self.mean, self.std, - self.to_rgb) - results['img_norm_cfg'] = dict( - mean=self.mean, std=self.std, to_rgb=self.to_rgb) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(mean={self.mean}, std={self.std}, to_rgb={self.to_rgb})' - return repr_str - - -@PIPELINES.register_module() -class RandomCrop: - """Random crop the image & bboxes & masks. - - The absolute `crop_size` is sampled based on `crop_type` and `image_size`, - then the cropped results are generated. - - Args: - crop_size (tuple): The relative ratio or absolute pixels of - height and width. - crop_type (str, optional): one of "relative_range", "relative", - "absolute", "absolute_range". "relative" randomly crops - (h * crop_size[0], w * crop_size[1]) part from an input of size - (h, w). "relative_range" uniformly samples relative crop size from - range [crop_size[0], 1] and [crop_size[1], 1] for height and width - respectively. "absolute" crops from an input with absolute size - (crop_size[0], crop_size[1]). "absolute_range" uniformly samples - crop_h in range [crop_size[0], min(h, crop_size[1])] and crop_w - in range [crop_size[0], min(w, crop_size[1])]. Default "absolute". - allow_negative_crop (bool, optional): Whether to allow a crop that does - not contain any bbox area. Default False. - recompute_bbox (bool, optional): Whether to re-compute the boxes based - on cropped instance masks. Default False. - bbox_clip_border (bool, optional): Whether clip the objects outside - the border of the image. Defaults to True. - - Note: - - If the image is smaller than the absolute crop size, return the - original image. - - The keys for bboxes, labels and masks must be aligned. That is, - `gt_bboxes` corresponds to `gt_labels` and `gt_masks`, and - `gt_bboxes_ignore` corresponds to `gt_labels_ignore` and - `gt_masks_ignore`. - - If the crop does not contain any gt-bbox region and - `allow_negative_crop` is set to False, skip this image. - """ - - def __init__(self, - crop_size, - crop_type='absolute', - allow_negative_crop=False, - recompute_bbox=False, - bbox_clip_border=True): - if crop_type not in [ - 'relative_range', 'relative', 'absolute', 'absolute_range' - ]: - raise ValueError(f'Invalid crop_type {crop_type}.') - if crop_type in ['absolute', 'absolute_range']: - assert crop_size[0] > 0 and crop_size[1] > 0 - assert isinstance(crop_size[0], int) and isinstance( - crop_size[1], int) - else: - assert 0 < crop_size[0] <= 1 and 0 < crop_size[1] <= 1 - self.crop_size = crop_size - self.crop_type = crop_type - self.allow_negative_crop = allow_negative_crop - self.bbox_clip_border = bbox_clip_border - self.recompute_bbox = recompute_bbox - # The key correspondence from bboxes to labels and masks. - self.bbox2label = { - 'gt_bboxes': 'gt_labels', - 'gt_bboxes_ignore': 'gt_labels_ignore' - } - self.bbox2mask = { - 'gt_bboxes': 'gt_masks', - 'gt_bboxes_ignore': 'gt_masks_ignore' - } - - def _crop_data(self, results, crop_size, allow_negative_crop): - """Function to randomly crop images, bounding boxes, masks, semantic - segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - crop_size (tuple): Expected absolute size after cropping, (h, w). - allow_negative_crop (bool): Whether to allow a crop that does not - contain any bbox area. Default to False. - - Returns: - dict: Randomly cropped results, 'img_shape' key in result dict is - updated according to crop size. - """ - assert crop_size[0] > 0 and crop_size[1] > 0 - for key in results.get('img_fields', ['img']): - img = results[key] - margin_h = max(img.shape[0] - crop_size[0], 0) - margin_w = max(img.shape[1] - crop_size[1], 0) - offset_h = np.random.randint(0, margin_h + 1) - offset_w = np.random.randint(0, margin_w + 1) - crop_y1, crop_y2 = offset_h, offset_h + crop_size[0] - crop_x1, crop_x2 = offset_w, offset_w + crop_size[1] - - # crop the image - img = img[crop_y1:crop_y2, crop_x1:crop_x2, ...] - img_shape = img.shape - results[key] = img - results['img_shape'] = img_shape - - # crop bboxes accordingly and clip to the image boundary - for key in results.get('bbox_fields', []): - # e.g. gt_bboxes and gt_bboxes_ignore - bbox_offset = np.array([offset_w, offset_h, offset_w, offset_h], - dtype=np.float32) - bboxes = results[key] - bbox_offset - if self.bbox_clip_border: - bboxes[:, 0::2] = np.clip(bboxes[:, 0::2], 0, img_shape[1]) - bboxes[:, 1::2] = np.clip(bboxes[:, 1::2], 0, img_shape[0]) - valid_inds = (bboxes[:, 2] > bboxes[:, 0]) & ( - bboxes[:, 3] > bboxes[:, 1]) - # If the crop does not contain any gt-bbox area and - # allow_negative_crop is False, skip this image. - if (key == 'gt_bboxes' and not valid_inds.any() - and not allow_negative_crop): - return None - results[key] = bboxes[valid_inds, :] - # label fields. e.g. gt_labels and gt_labels_ignore - label_key = self.bbox2label.get(key) - if label_key in results: - results[label_key] = results[label_key][valid_inds] - - # mask fields, e.g. gt_masks and gt_masks_ignore - mask_key = self.bbox2mask.get(key) - if mask_key in results: - results[mask_key] = results[mask_key][ - valid_inds.nonzero()[0]].crop( - np.asarray([crop_x1, crop_y1, crop_x2, crop_y2])) - if self.recompute_bbox: - results[key] = results[mask_key].get_bboxes() - - # crop semantic seg - for key in results.get('seg_fields', []): - results[key] = results[key][crop_y1:crop_y2, crop_x1:crop_x2] - - return results - - def _get_crop_size(self, image_size): - """Randomly generates the absolute crop size based on `crop_type` and - `image_size`. - - Args: - image_size (tuple): (h, w). - - Returns: - crop_size (tuple): (crop_h, crop_w) in absolute pixels. - """ - h, w = image_size - if self.crop_type == 'absolute': - return (min(self.crop_size[0], h), min(self.crop_size[1], w)) - elif self.crop_type == 'absolute_range': - assert self.crop_size[0] <= self.crop_size[1] - crop_h = np.random.randint( - min(h, self.crop_size[0]), - min(h, self.crop_size[1]) + 1) - crop_w = np.random.randint( - min(w, self.crop_size[0]), - min(w, self.crop_size[1]) + 1) - return crop_h, crop_w - elif self.crop_type == 'relative': - crop_h, crop_w = self.crop_size - return int(h * crop_h + 0.5), int(w * crop_w + 0.5) - elif self.crop_type == 'relative_range': - crop_size = np.asarray(self.crop_size, dtype=np.float32) - crop_h, crop_w = crop_size + np.random.rand(2) * (1 - crop_size) - return int(h * crop_h + 0.5), int(w * crop_w + 0.5) - - def __call__(self, results): - """Call function to randomly crop images, bounding boxes, masks, - semantic segmentation maps. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Randomly cropped results, 'img_shape' key in result dict is - updated according to crop size. - """ - image_size = results['img'].shape[:2] - crop_size = self._get_crop_size(image_size) - results = self._crop_data(results, crop_size, self.allow_negative_crop) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(crop_size={self.crop_size}, ' - repr_str += f'crop_type={self.crop_type}, ' - repr_str += f'allow_negative_crop={self.allow_negative_crop}, ' - repr_str += f'bbox_clip_border={self.bbox_clip_border})' - return repr_str - - -@PIPELINES.register_module() -class SegRescale: - """Rescale semantic segmentation maps. - - Args: - scale_factor (float): The scale factor of the final output. - backend (str): Image rescale backend, choices are 'cv2' and 'pillow'. - These two backends generates slightly different results. Defaults - to 'cv2'. - """ - - def __init__(self, scale_factor=1, backend='cv2'): - self.scale_factor = scale_factor - self.backend = backend - - def __call__(self, results): - """Call function to scale the semantic segmentation map. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with semantic segmentation map scaled. - """ - - for key in results.get('seg_fields', []): - if self.scale_factor != 1: - results[key] = mmcv.imrescale( - results[key], - self.scale_factor, - interpolation='nearest', - backend=self.backend) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(scale_factor={self.scale_factor})' - - -@PIPELINES.register_module() -class PhotoMetricDistortion: - """Apply photometric distortion to image sequentially, every transformation - is applied with a probability of 0.5. The position of random contrast is in - second or second to last. - - 1. random brightness - 2. random contrast (mode 0) - 3. convert color from BGR to HSV - 4. random saturation - 5. random hue - 6. convert color from HSV to BGR - 7. random contrast (mode 1) - 8. randomly swap channels - - Args: - brightness_delta (int): delta of brightness. - contrast_range (tuple): range of contrast. - saturation_range (tuple): range of saturation. - hue_delta (int): delta of hue. - """ - - def __init__(self, - brightness_delta=32, - contrast_range=(0.5, 1.5), - saturation_range=(0.5, 1.5), - hue_delta=18): - self.brightness_delta = brightness_delta - self.contrast_lower, self.contrast_upper = contrast_range - self.saturation_lower, self.saturation_upper = saturation_range - self.hue_delta = hue_delta - - def __call__(self, results): - """Call function to perform photometric distortion on images. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images distorted. - """ - - if 'img_fields' in results: - assert results['img_fields'] == ['img'], \ - 'Only single img_fields is allowed' - img = results['img'] - img = img.astype(np.float32) - # random brightness - if random.randint(2): - delta = random.uniform(-self.brightness_delta, - self.brightness_delta) - img += delta - - # mode == 0 --> do random contrast first - # mode == 1 --> do random contrast last - mode = random.randint(2) - if mode == 1: - if random.randint(2): - alpha = random.uniform(self.contrast_lower, - self.contrast_upper) - img *= alpha - - # convert color from BGR to HSV - img = mmcv.bgr2hsv(img) - - # random saturation - if random.randint(2): - img[..., 1] *= random.uniform(self.saturation_lower, - self.saturation_upper) - - # random hue - if random.randint(2): - img[..., 0] += random.uniform(-self.hue_delta, self.hue_delta) - img[..., 0][img[..., 0] > 360] -= 360 - img[..., 0][img[..., 0] < 0] += 360 - - # convert color from HSV to BGR - img = mmcv.hsv2bgr(img) - - # random contrast - if mode == 0: - if random.randint(2): - alpha = random.uniform(self.contrast_lower, - self.contrast_upper) - img *= alpha - - # randomly swap channels - if random.randint(2): - img = img[..., random.permutation(3)] - - results['img'] = img - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(\nbrightness_delta={self.brightness_delta},\n' - repr_str += 'contrast_range=' - repr_str += f'{(self.contrast_lower, self.contrast_upper)},\n' - repr_str += 'saturation_range=' - repr_str += f'{(self.saturation_lower, self.saturation_upper)},\n' - repr_str += f'hue_delta={self.hue_delta})' - return repr_str - - -@PIPELINES.register_module() -class Expand: - """Random expand the image & bboxes. - - Randomly place the original image on a canvas of 'ratio' x original image - size filled with mean values. The ratio is in the range of ratio_range. - - Args: - mean (tuple): mean value of dataset. - to_rgb (bool): if need to convert the order of mean to align with RGB. - ratio_range (tuple): range of expand ratio. - prob (float): probability of applying this transformation - """ - - def __init__(self, - mean=(0, 0, 0), - to_rgb=True, - ratio_range=(1, 4), - seg_ignore_label=None, - prob=0.5): - self.to_rgb = to_rgb - self.ratio_range = ratio_range - if to_rgb: - self.mean = mean[::-1] - else: - self.mean = mean - self.min_ratio, self.max_ratio = ratio_range - self.seg_ignore_label = seg_ignore_label - self.prob = prob - - def __call__(self, results): - """Call function to expand images, bounding boxes. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images, bounding boxes expanded - """ - - if random.uniform(0, 1) > self.prob: - return results - - if 'img_fields' in results: - assert results['img_fields'] == ['img'], \ - 'Only single img_fields is allowed' - img = results['img'] - - h, w, c = img.shape - ratio = random.uniform(self.min_ratio, self.max_ratio) - # speedup expand when meets large image - if np.all(self.mean == self.mean[0]): - expand_img = np.empty((int(h * ratio), int(w * ratio), c), - img.dtype) - expand_img.fill(self.mean[0]) - else: - expand_img = np.full((int(h * ratio), int(w * ratio), c), - self.mean, - dtype=img.dtype) - left = int(random.uniform(0, w * ratio - w)) - top = int(random.uniform(0, h * ratio - h)) - expand_img[top:top + h, left:left + w] = img - - results['img'] = expand_img - # expand bboxes - for key in results.get('bbox_fields', []): - results[key] = results[key] + np.tile( - (left, top), 2).astype(results[key].dtype) - - # expand masks - for key in results.get('mask_fields', []): - results[key] = results[key].expand( - int(h * ratio), int(w * ratio), top, left) - - # expand segs - for key in results.get('seg_fields', []): - gt_seg = results[key] - expand_gt_seg = np.full((int(h * ratio), int(w * ratio)), - self.seg_ignore_label, - dtype=gt_seg.dtype) - expand_gt_seg[top:top + h, left:left + w] = gt_seg - results[key] = expand_gt_seg - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(mean={self.mean}, to_rgb={self.to_rgb}, ' - repr_str += f'ratio_range={self.ratio_range}, ' - repr_str += f'seg_ignore_label={self.seg_ignore_label})' - return repr_str - - -@PIPELINES.register_module() -class MinIoURandomCrop: - """Random crop the image & bboxes, the cropped patches have minimum IoU - requirement with original image & bboxes, the IoU threshold is randomly - selected from min_ious. - - Args: - min_ious (tuple): minimum IoU threshold for all intersections with - bounding boxes - min_crop_size (float): minimum crop's size (i.e. h,w := a*h, a*w, - where a >= min_crop_size). - bbox_clip_border (bool, optional): Whether clip the objects outside - the border of the image. Defaults to True. - - Note: - The keys for bboxes, labels and masks should be paired. That is, \ - `gt_bboxes` corresponds to `gt_labels` and `gt_masks`, and \ - `gt_bboxes_ignore` to `gt_labels_ignore` and `gt_masks_ignore`. - """ - - def __init__(self, - min_ious=(0.1, 0.3, 0.5, 0.7, 0.9), - min_crop_size=0.3, - bbox_clip_border=True): - # 1: return ori img - self.min_ious = min_ious - self.sample_mode = (1, *min_ious, 0) - self.min_crop_size = min_crop_size - self.bbox_clip_border = bbox_clip_border - self.bbox2label = { - 'gt_bboxes': 'gt_labels', - 'gt_bboxes_ignore': 'gt_labels_ignore' - } - self.bbox2mask = { - 'gt_bboxes': 'gt_masks', - 'gt_bboxes_ignore': 'gt_masks_ignore' - } - - def __call__(self, results): - """Call function to crop images and bounding boxes with minimum IoU - constraint. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images and bounding boxes cropped, \ - 'img_shape' key is updated. - """ - - if 'img_fields' in results: - assert results['img_fields'] == ['img'], \ - 'Only single img_fields is allowed' - img = results['img'] - assert 'bbox_fields' in results - boxes = [results[key] for key in results['bbox_fields']] - boxes = np.concatenate(boxes, 0) - h, w, c = img.shape - while True: - mode = random.choice(self.sample_mode) - self.mode = mode - if mode == 1: - return results - - min_iou = mode - for i in range(50): - new_w = random.uniform(self.min_crop_size * w, w) - new_h = random.uniform(self.min_crop_size * h, h) - - # h / w in [0.5, 2] - if new_h / new_w < 0.5 or new_h / new_w > 2: - continue - - left = random.uniform(w - new_w) - top = random.uniform(h - new_h) - - patch = np.array( - (int(left), int(top), int(left + new_w), int(top + new_h))) - # Line or point crop is not allowed - if patch[2] == patch[0] or patch[3] == patch[1]: - continue - overlaps = bbox_overlaps( - patch.reshape(-1, 4), boxes.reshape(-1, 4)).reshape(-1) - if len(overlaps) > 0 and overlaps.min() < min_iou: - continue - - # center of boxes should inside the crop img - # only adjust boxes and instance masks when the gt is not empty - if len(overlaps) > 0: - # adjust boxes - def is_center_of_bboxes_in_patch(boxes, patch): - center = (boxes[:, :2] + boxes[:, 2:]) / 2 - mask = ((center[:, 0] > patch[0]) * - (center[:, 1] > patch[1]) * - (center[:, 0] < patch[2]) * - (center[:, 1] < patch[3])) - return mask - - mask = is_center_of_bboxes_in_patch(boxes, patch) - if not mask.any(): - continue - for key in results.get('bbox_fields', []): - boxes = results[key].copy() - mask = is_center_of_bboxes_in_patch(boxes, patch) - boxes = boxes[mask] - if self.bbox_clip_border: - boxes[:, 2:] = boxes[:, 2:].clip(max=patch[2:]) - boxes[:, :2] = boxes[:, :2].clip(min=patch[:2]) - boxes -= np.tile(patch[:2], 2) - - results[key] = boxes - # labels - label_key = self.bbox2label.get(key) - if label_key in results: - results[label_key] = results[label_key][mask] - - # mask fields - mask_key = self.bbox2mask.get(key) - if mask_key in results: - results[mask_key] = results[mask_key][ - mask.nonzero()[0]].crop(patch) - # adjust the img no matter whether the gt is empty before crop - img = img[patch[1]:patch[3], patch[0]:patch[2]] - results['img'] = img - results['img_shape'] = img.shape - - # seg fields - for key in results.get('seg_fields', []): - results[key] = results[key][patch[1]:patch[3], - patch[0]:patch[2]] - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(min_ious={self.min_ious}, ' - repr_str += f'min_crop_size={self.min_crop_size}, ' - repr_str += f'bbox_clip_border={self.bbox_clip_border})' - return repr_str - - -@PIPELINES.register_module() -class Corrupt: - """Corruption augmentation. - - Corruption transforms implemented based on - `imagecorruptions `_. - - Args: - corruption (str): Corruption name. - severity (int, optional): The severity of corruption. Default: 1. - """ - - def __init__(self, corruption, severity=1): - self.corruption = corruption - self.severity = severity - - def __call__(self, results): - """Call function to corrupt image. - - Args: - results (dict): Result dict from loading pipeline. - - Returns: - dict: Result dict with images corrupted. - """ - - if corrupt is None: - raise RuntimeError('imagecorruptions is not installed') - if 'img_fields' in results: - assert results['img_fields'] == ['img'], \ - 'Only single img_fields is allowed' - results['img'] = corrupt( - results['img'].astype(np.uint8), - corruption_name=self.corruption, - severity=self.severity) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(corruption={self.corruption}, ' - repr_str += f'severity={self.severity})' - return repr_str - - -@PIPELINES.register_module() -class Albu: - """Albumentation augmentation. - - Adds custom transformations from Albumentations library. - Please, visit `https://albumentations.readthedocs.io` - to get more information. - - An example of ``transforms`` is as followed: - - .. code-block:: - - [ - dict( - type='ShiftScaleRotate', - shift_limit=0.0625, - scale_limit=0.0, - rotate_limit=0, - interpolation=1, - p=0.5), - dict( - type='RandomBrightnessContrast', - brightness_limit=[0.1, 0.3], - contrast_limit=[0.1, 0.3], - p=0.2), - dict(type='ChannelShuffle', p=0.1), - dict( - type='OneOf', - transforms=[ - dict(type='Blur', blur_limit=3, p=1.0), - dict(type='MedianBlur', blur_limit=3, p=1.0) - ], - p=0.1), - ] - - Args: - transforms (list[dict]): A list of albu transformations - bbox_params (dict): Bbox_params for albumentation `Compose` - keymap (dict): Contains {'input key':'albumentation-style key'} - skip_img_without_anno (bool): Whether to skip the image if no ann left - after aug - """ - - def __init__(self, - transforms, - bbox_params=None, - keymap=None, - update_pad_shape=False, - skip_img_without_anno=False): - if Compose is None: - raise RuntimeError('albumentations is not installed') - - # Args will be modified later, copying it will be safer - transforms = copy.deepcopy(transforms) - if bbox_params is not None: - bbox_params = copy.deepcopy(bbox_params) - if keymap is not None: - keymap = copy.deepcopy(keymap) - self.transforms = transforms - self.filter_lost_elements = False - self.update_pad_shape = update_pad_shape - self.skip_img_without_anno = skip_img_without_anno - - # A simple workaround to remove masks without boxes - if (isinstance(bbox_params, dict) and 'label_fields' in bbox_params - and 'filter_lost_elements' in bbox_params): - self.filter_lost_elements = True - self.origin_label_fields = bbox_params['label_fields'] - bbox_params['label_fields'] = ['idx_mapper'] - del bbox_params['filter_lost_elements'] - - self.bbox_params = ( - self.albu_builder(bbox_params) if bbox_params else None) - self.aug = Compose([self.albu_builder(t) for t in self.transforms], - bbox_params=self.bbox_params) - - if not keymap: - self.keymap_to_albu = { - 'img': 'image', - 'gt_masks': 'masks', - 'gt_bboxes': 'bboxes' - } - else: - self.keymap_to_albu = keymap - self.keymap_back = {v: k for k, v in self.keymap_to_albu.items()} - - def albu_builder(self, cfg): - """Import a module from albumentations. - - It inherits some of :func:`build_from_cfg` logic. - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - - Returns: - obj: The constructed object. - """ - - assert isinstance(cfg, dict) and 'type' in cfg - args = cfg.copy() - - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if albumentations is None: - raise RuntimeError('albumentations is not installed') - obj_cls = getattr(albumentations, obj_type) - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - - if 'transforms' in args: - args['transforms'] = [ - self.albu_builder(transform) - for transform in args['transforms'] - ] - - return obj_cls(**args) - - @staticmethod - def mapper(d, keymap): - """Dictionary mapper. Renames keys according to keymap provided. - - Args: - d (dict): old dict - keymap (dict): {'old_key':'new_key'} - Returns: - dict: new dict. - """ - - updated_dict = {} - for k, v in zip(d.keys(), d.values()): - new_k = keymap.get(k, k) - updated_dict[new_k] = d[k] - return updated_dict - - def __call__(self, results): - # dict to albumentations format - results = self.mapper(results, self.keymap_to_albu) - # TODO: add bbox_fields - if 'bboxes' in results: - # to list of boxes - if isinstance(results['bboxes'], np.ndarray): - results['bboxes'] = [x for x in results['bboxes']] - # add pseudo-field for filtration - if self.filter_lost_elements: - results['idx_mapper'] = np.arange(len(results['bboxes'])) - - # TODO: Support mask structure in albu - if 'masks' in results: - if isinstance(results['masks'], PolygonMasks): - raise NotImplementedError( - 'Albu only supports BitMap masks now') - ori_masks = results['masks'] - if albumentations.__version__ < '0.5': - results['masks'] = results['masks'].masks - else: - results['masks'] = [mask for mask in results['masks'].masks] - - results = self.aug(**results) - - if 'bboxes' in results: - if isinstance(results['bboxes'], list): - results['bboxes'] = np.array( - results['bboxes'], dtype=np.float32) - results['bboxes'] = results['bboxes'].reshape(-1, 4) - - # filter label_fields - if self.filter_lost_elements: - - for label in self.origin_label_fields: - results[label] = np.array( - [results[label][i] for i in results['idx_mapper']]) - if 'masks' in results: - results['masks'] = np.array( - [results['masks'][i] for i in results['idx_mapper']]) - results['masks'] = ori_masks.__class__( - results['masks'], results['image'].shape[0], - results['image'].shape[1]) - - if (not len(results['idx_mapper']) - and self.skip_img_without_anno): - return None - - if 'gt_labels' in results: - if isinstance(results['gt_labels'], list): - results['gt_labels'] = np.array(results['gt_labels']) - results['gt_labels'] = results['gt_labels'].astype(np.int64) - - # back to the original format - results = self.mapper(results, self.keymap_back) - - # update final shape - if self.update_pad_shape: - results['pad_shape'] = results['img'].shape - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ + f'(transforms={self.transforms})' - return repr_str - - -@PIPELINES.register_module() -class RandomCenterCropPad: - """Random center crop and random around padding for CornerNet. - - This operation generates randomly cropped image from the original image and - pads it simultaneously. Different from :class:`RandomCrop`, the output - shape may not equal to ``crop_size`` strictly. We choose a random value - from ``ratios`` and the output shape could be larger or smaller than - ``crop_size``. The padding operation is also different from :class:`Pad`, - here we use around padding instead of right-bottom padding. - - The relation between output image (padding image) and original image: - - .. code:: text - - output image - - +----------------------------+ - | padded area | - +------|----------------------------|----------+ - | | cropped area | | - | | +---------------+ | | - | | | . center | | | original image - | | | range | | | - | | +---------------+ | | - +------|----------------------------|----------+ - | padded area | - +----------------------------+ - - There are 5 main areas in the figure: - - - output image: output image of this operation, also called padding - image in following instruction. - - original image: input image of this operation. - - padded area: non-intersect area of output image and original image. - - cropped area: the overlap of output image and original image. - - center range: a smaller area where random center chosen from. - center range is computed by ``border`` and original image's shape - to avoid our random center is too close to original image's border. - - Also this operation act differently in train and test mode, the summary - pipeline is listed below. - - Train pipeline: - - 1. Choose a ``random_ratio`` from ``ratios``, the shape of padding image - will be ``random_ratio * crop_size``. - 2. Choose a ``random_center`` in center range. - 3. Generate padding image with center matches the ``random_center``. - 4. Initialize the padding image with pixel value equals to ``mean``. - 5. Copy the cropped area to padding image. - 6. Refine annotations. - - Test pipeline: - - 1. Compute output shape according to ``test_pad_mode``. - 2. Generate padding image with center matches the original image - center. - 3. Initialize the padding image with pixel value equals to ``mean``. - 4. Copy the ``cropped area`` to padding image. - - Args: - crop_size (tuple | None): expected size after crop, final size will - computed according to ratio. Requires (h, w) in train mode, and - None in test mode. - ratios (tuple): random select a ratio from tuple and crop image to - (crop_size[0] * ratio) * (crop_size[1] * ratio). - Only available in train mode. - border (int): max distance from center select area to image border. - Only available in train mode. - mean (sequence): Mean values of 3 channels. - std (sequence): Std values of 3 channels. - to_rgb (bool): Whether to convert the image from BGR to RGB. - test_mode (bool): whether involve random variables in transform. - In train mode, crop_size is fixed, center coords and ratio is - random selected from predefined lists. In test mode, crop_size - is image's original shape, center coords and ratio is fixed. - test_pad_mode (tuple): padding method and padding shape value, only - available in test mode. Default is using 'logical_or' with - 127 as padding shape value. - - - 'logical_or': final_shape = input_shape | padding_shape_value - - 'size_divisor': final_shape = int( - ceil(input_shape / padding_shape_value) * padding_shape_value) - test_pad_add_pix (int): Extra padding pixel in test mode. Default 0. - bbox_clip_border (bool, optional): Whether clip the objects outside - the border of the image. Defaults to True. - """ - - def __init__(self, - crop_size=None, - ratios=(0.9, 1.0, 1.1), - border=128, - mean=None, - std=None, - to_rgb=None, - test_mode=False, - test_pad_mode=('logical_or', 127), - test_pad_add_pix=0, - bbox_clip_border=True): - if test_mode: - assert crop_size is None, 'crop_size must be None in test mode' - assert ratios is None, 'ratios must be None in test mode' - assert border is None, 'border must be None in test mode' - assert isinstance(test_pad_mode, (list, tuple)) - assert test_pad_mode[0] in ['logical_or', 'size_divisor'] - else: - assert isinstance(crop_size, (list, tuple)) - assert crop_size[0] > 0 and crop_size[1] > 0, ( - 'crop_size must > 0 in train mode') - assert isinstance(ratios, (list, tuple)) - assert test_pad_mode is None, ( - 'test_pad_mode must be None in train mode') - - self.crop_size = crop_size - self.ratios = ratios - self.border = border - # We do not set default value to mean, std and to_rgb because these - # hyper-parameters are easy to forget but could affect the performance. - # Please use the same setting as Normalize for performance assurance. - assert mean is not None and std is not None and to_rgb is not None - self.to_rgb = to_rgb - self.input_mean = mean - self.input_std = std - if to_rgb: - self.mean = mean[::-1] - self.std = std[::-1] - else: - self.mean = mean - self.std = std - self.test_mode = test_mode - self.test_pad_mode = test_pad_mode - self.test_pad_add_pix = test_pad_add_pix - self.bbox_clip_border = bbox_clip_border - - def _get_border(self, border, size): - """Get final border for the target size. - - This function generates a ``final_border`` according to image's shape. - The area between ``final_border`` and ``size - final_border`` is the - ``center range``. We randomly choose center from the ``center range`` - to avoid our random center is too close to original image's border. - Also ``center range`` should be larger than 0. - - Args: - border (int): The initial border, default is 128. - size (int): The width or height of original image. - Returns: - int: The final border. - """ - k = 2 * border / size - i = pow(2, np.ceil(np.log2(np.ceil(k))) + (k == int(k))) - return border // i - - def _filter_boxes(self, patch, boxes): - """Check whether the center of each box is in the patch. - - Args: - patch (list[int]): The cropped area, [left, top, right, bottom]. - boxes (numpy array, (N x 4)): Ground truth boxes. - - Returns: - mask (numpy array, (N,)): Each box is inside or outside the patch. - """ - center = (boxes[:, :2] + boxes[:, 2:]) / 2 - mask = (center[:, 0] > patch[0]) * (center[:, 1] > patch[1]) * ( - center[:, 0] < patch[2]) * ( - center[:, 1] < patch[3]) - return mask - - def _crop_image_and_paste(self, image, center, size): - """Crop image with a given center and size, then paste the cropped - image to a blank image with two centers align. - - This function is equivalent to generating a blank image with ``size`` - as its shape. Then cover it on the original image with two centers ( - the center of blank image and the random center of original image) - aligned. The overlap area is paste from the original image and the - outside area is filled with ``mean pixel``. - - Args: - image (np array, H x W x C): Original image. - center (list[int]): Target crop center coord. - size (list[int]): Target crop size. [target_h, target_w] - - Returns: - cropped_img (np array, target_h x target_w x C): Cropped image. - border (np array, 4): The distance of four border of - ``cropped_img`` to the original image area, [top, bottom, - left, right] - patch (list[int]): The cropped area, [left, top, right, bottom]. - """ - center_y, center_x = center - target_h, target_w = size - img_h, img_w, img_c = image.shape - - x0 = max(0, center_x - target_w // 2) - x1 = min(center_x + target_w // 2, img_w) - y0 = max(0, center_y - target_h // 2) - y1 = min(center_y + target_h // 2, img_h) - patch = np.array((int(x0), int(y0), int(x1), int(y1))) - - left, right = center_x - x0, x1 - center_x - top, bottom = center_y - y0, y1 - center_y - - cropped_center_y, cropped_center_x = target_h // 2, target_w // 2 - cropped_img = np.zeros((target_h, target_w, img_c), dtype=image.dtype) - for i in range(img_c): - cropped_img[:, :, i] += self.mean[i] - y_slice = slice(cropped_center_y - top, cropped_center_y + bottom) - x_slice = slice(cropped_center_x - left, cropped_center_x + right) - cropped_img[y_slice, x_slice, :] = image[y0:y1, x0:x1, :] - - border = np.array([ - cropped_center_y - top, cropped_center_y + bottom, - cropped_center_x - left, cropped_center_x + right - ], - dtype=np.float32) - - return cropped_img, border, patch - - def _train_aug(self, results): - """Random crop and around padding the original image. - - Args: - results (dict): Image infomations in the augment pipeline. - - Returns: - results (dict): The updated dict. - """ - img = results['img'] - h, w, c = img.shape - boxes = results['gt_bboxes'] - while True: - scale = random.choice(self.ratios) - new_h = int(self.crop_size[0] * scale) - new_w = int(self.crop_size[1] * scale) - h_border = self._get_border(self.border, h) - w_border = self._get_border(self.border, w) - - for i in range(50): - center_x = random.randint(low=w_border, high=w - w_border) - center_y = random.randint(low=h_border, high=h - h_border) - - cropped_img, border, patch = self._crop_image_and_paste( - img, [center_y, center_x], [new_h, new_w]) - - mask = self._filter_boxes(patch, boxes) - # if image do not have valid bbox, any crop patch is valid. - if not mask.any() and len(boxes) > 0: - continue - - results['img'] = cropped_img - results['img_shape'] = cropped_img.shape - results['pad_shape'] = cropped_img.shape - - x0, y0, x1, y1 = patch - - left_w, top_h = center_x - x0, center_y - y0 - cropped_center_x, cropped_center_y = new_w // 2, new_h // 2 - - # crop bboxes accordingly and clip to the image boundary - for key in results.get('bbox_fields', []): - mask = self._filter_boxes(patch, results[key]) - bboxes = results[key][mask] - bboxes[:, 0:4:2] += cropped_center_x - left_w - x0 - bboxes[:, 1:4:2] += cropped_center_y - top_h - y0 - if self.bbox_clip_border: - bboxes[:, 0:4:2] = np.clip(bboxes[:, 0:4:2], 0, new_w) - bboxes[:, 1:4:2] = np.clip(bboxes[:, 1:4:2], 0, new_h) - keep = (bboxes[:, 2] > bboxes[:, 0]) & ( - bboxes[:, 3] > bboxes[:, 1]) - bboxes = bboxes[keep] - results[key] = bboxes - if key in ['gt_bboxes']: - if 'gt_labels' in results: - labels = results['gt_labels'][mask] - labels = labels[keep] - results['gt_labels'] = labels - if 'gt_masks' in results: - raise NotImplementedError( - 'RandomCenterCropPad only supports bbox.') - - # crop semantic seg - for key in results.get('seg_fields', []): - raise NotImplementedError( - 'RandomCenterCropPad only supports bbox.') - return results - - def _test_aug(self, results): - """Around padding the original image without cropping. - - The padding mode and value are from ``test_pad_mode``. - - Args: - results (dict): Image infomations in the augment pipeline. - - Returns: - results (dict): The updated dict. - """ - img = results['img'] - h, w, c = img.shape - results['img_shape'] = img.shape - if self.test_pad_mode[0] in ['logical_or']: - # self.test_pad_add_pix is only used for centernet - target_h = (h | self.test_pad_mode[1]) + self.test_pad_add_pix - target_w = (w | self.test_pad_mode[1]) + self.test_pad_add_pix - elif self.test_pad_mode[0] in ['size_divisor']: - divisor = self.test_pad_mode[1] - target_h = int(np.ceil(h / divisor)) * divisor - target_w = int(np.ceil(w / divisor)) * divisor - else: - raise NotImplementedError( - 'RandomCenterCropPad only support two testing pad mode:' - 'logical-or and size_divisor.') - - cropped_img, border, _ = self._crop_image_and_paste( - img, [h // 2, w // 2], [target_h, target_w]) - results['img'] = cropped_img - results['pad_shape'] = cropped_img.shape - results['border'] = border - return results - - def __call__(self, results): - img = results['img'] - assert img.dtype == np.float32, ( - 'RandomCenterCropPad needs the input image of dtype np.float32,' - ' please set "to_float32=True" in "LoadImageFromFile" pipeline') - h, w, c = img.shape - assert c == len(self.mean) - if self.test_mode: - return self._test_aug(results) - else: - return self._train_aug(results) - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(crop_size={self.crop_size}, ' - repr_str += f'ratios={self.ratios}, ' - repr_str += f'border={self.border}, ' - repr_str += f'mean={self.input_mean}, ' - repr_str += f'std={self.input_std}, ' - repr_str += f'to_rgb={self.to_rgb}, ' - repr_str += f'test_mode={self.test_mode}, ' - repr_str += f'test_pad_mode={self.test_pad_mode}, ' - repr_str += f'bbox_clip_border={self.bbox_clip_border})' - return repr_str - - -@PIPELINES.register_module() -class CutOut: - """CutOut operation. - - Randomly drop some regions of image used in - `Cutout `_. - - Args: - n_holes (int | tuple[int, int]): Number of regions to be dropped. - If it is given as a list, number of holes will be randomly - selected from the closed interval [`n_holes[0]`, `n_holes[1]`]. - cutout_shape (tuple[int, int] | list[tuple[int, int]]): The candidate - shape of dropped regions. It can be `tuple[int, int]` to use a - fixed cutout shape, or `list[tuple[int, int]]` to randomly choose - shape from the list. - cutout_ratio (tuple[float, float] | list[tuple[float, float]]): The - candidate ratio of dropped regions. It can be `tuple[float, float]` - to use a fixed ratio or `list[tuple[float, float]]` to randomly - choose ratio from the list. Please note that `cutout_shape` - and `cutout_ratio` cannot be both given at the same time. - fill_in (tuple[float, float, float] | tuple[int, int, int]): The value - of pixel to fill in the dropped regions. Default: (0, 0, 0). - """ - - def __init__(self, - n_holes, - cutout_shape=None, - cutout_ratio=None, - fill_in=(0, 0, 0)): - - assert (cutout_shape is None) ^ (cutout_ratio is None), \ - 'Either cutout_shape or cutout_ratio should be specified.' - assert (isinstance(cutout_shape, (list, tuple)) - or isinstance(cutout_ratio, (list, tuple))) - if isinstance(n_holes, tuple): - assert len(n_holes) == 2 and 0 <= n_holes[0] < n_holes[1] - else: - n_holes = (n_holes, n_holes) - self.n_holes = n_holes - self.fill_in = fill_in - self.with_ratio = cutout_ratio is not None - self.candidates = cutout_ratio if self.with_ratio else cutout_shape - if not isinstance(self.candidates, list): - self.candidates = [self.candidates] - - def __call__(self, results): - """Call function to drop some regions of image.""" - h, w, c = results['img'].shape - n_holes = np.random.randint(self.n_holes[0], self.n_holes[1] + 1) - for _ in range(n_holes): - x1 = np.random.randint(0, w) - y1 = np.random.randint(0, h) - index = np.random.randint(0, len(self.candidates)) - if not self.with_ratio: - cutout_w, cutout_h = self.candidates[index] - else: - cutout_w = int(self.candidates[index][0] * w) - cutout_h = int(self.candidates[index][1] * h) - - x2 = np.clip(x1 + cutout_w, 0, w) - y2 = np.clip(y1 + cutout_h, 0, h) - results['img'][y1:y2, x1:x2, :] = self.fill_in - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(n_holes={self.n_holes}, ' - repr_str += (f'cutout_ratio={self.candidates}, ' if self.with_ratio - else f'cutout_shape={self.candidates}, ') - repr_str += f'fill_in={self.fill_in})' - return repr_str - - -@PIPELINES.register_module() -class Mosaic: - """Mosaic augmentation. - - Given 4 images, mosaic transform combines them into - one output image. The output image is composed of the parts from each sub- - image. - - .. code:: text - - mosaic transform - center_x - +------------------------------+ - | pad | pad | - | +-----------+ | - | | | | - | | image1 |--------+ | - | | | | | - | | | image2 | | - center_y |----+-------------+-----------| - | | cropped | | - |pad | image3 | image4 | - | | | | - +----|-------------+-----------+ - | | - +-------------+ - - The mosaic transform steps are as follows: - - 1. Choose the mosaic center as the intersections of 4 images - 2. Get the left top image according to the index, and randomly - sample another 3 images from the custom dataset. - 3. Sub image will be cropped if image is larger than mosaic patch - - Args: - img_scale (Sequence[int]): Image size after mosaic pipeline of single - image. The shape order should be (height, width). - Default to (640, 640). - center_ratio_range (Sequence[float]): Center ratio range of mosaic - output. Default to (0.5, 1.5). - min_bbox_size (int | float): The minimum pixel for filtering - invalid bboxes after the mosaic pipeline. Default to 0. - bbox_clip_border (bool, optional): Whether to clip the objects outside - the border of the image. In some dataset like MOT17, the gt bboxes - are allowed to cross the border of images. Therefore, we don't - need to clip the gt bboxes in these cases. Defaults to True. - skip_filter (bool): Whether to skip filtering rules. If it - is True, the filter rule will not be applied, and the - `min_bbox_size` is invalid. Default to True. - pad_val (int): Pad value. Default to 114. - prob (float): Probability of applying this transformation. - Default to 1.0. - """ - - def __init__(self, - img_scale=(640, 640), - center_ratio_range=(0.5, 1.5), - min_bbox_size=0, - bbox_clip_border=True, - skip_filter=True, - pad_val=114, - prob=1.0): - assert isinstance(img_scale, tuple) - assert 0 <= prob <= 1.0, 'The probability should be in range [0,1]. '\ - f'got {prob}.' - - log_img_scale(img_scale, skip_square=True) - self.img_scale = img_scale - self.center_ratio_range = center_ratio_range - self.min_bbox_size = min_bbox_size - self.bbox_clip_border = bbox_clip_border - self.skip_filter = skip_filter - self.pad_val = pad_val - self.prob = prob - - def __call__(self, results): - """Call function to make a mosaic of image. - - Args: - results (dict): Result dict. - - Returns: - dict: Result dict with mosaic transformed. - """ - - if random.uniform(0, 1) > self.prob: - return results - - results = self._mosaic_transform(results) - return results - - def get_indexes(self, dataset): - """Call function to collect indexes. - - Args: - dataset (:obj:`MultiImageMixDataset`): The dataset. - - Returns: - list: indexes. - """ - - indexes = [random.randint(0, len(dataset)) for _ in range(3)] - return indexes - - def _mosaic_transform(self, results): - """Mosaic transform function. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - mosaic_labels = [] - mosaic_bboxes = [] - if len(results['img'].shape) == 3: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2), 3), - self.pad_val, - dtype=results['img'].dtype) - else: - mosaic_img = np.full( - (int(self.img_scale[0] * 2), int(self.img_scale[1] * 2)), - self.pad_val, - dtype=results['img'].dtype) - - # mosaic center x, y - center_x = int( - random.uniform(*self.center_ratio_range) * self.img_scale[1]) - center_y = int( - random.uniform(*self.center_ratio_range) * self.img_scale[0]) - center_position = (center_x, center_y) - - loc_strs = ('top_left', 'top_right', 'bottom_left', 'bottom_right') - for i, loc in enumerate(loc_strs): - if loc == 'top_left': - results_patch = copy.deepcopy(results) - else: - results_patch = copy.deepcopy(results['mix_results'][i - 1]) - - img_i = results_patch['img'] - h_i, w_i = img_i.shape[:2] - # keep_ratio resize - scale_ratio_i = min(self.img_scale[0] / h_i, - self.img_scale[1] / w_i) - img_i = mmcv.imresize( - img_i, (int(w_i * scale_ratio_i), int(h_i * scale_ratio_i))) - - # compute the combine parameters - paste_coord, crop_coord = self._mosaic_combine( - loc, center_position, img_i.shape[:2][::-1]) - x1_p, y1_p, x2_p, y2_p = paste_coord - x1_c, y1_c, x2_c, y2_c = crop_coord - - # crop and paste image - mosaic_img[y1_p:y2_p, x1_p:x2_p] = img_i[y1_c:y2_c, x1_c:x2_c] - - # adjust coordinate - gt_bboxes_i = results_patch['gt_bboxes'] - gt_labels_i = results_patch['gt_labels'] - - if gt_bboxes_i.shape[0] > 0: - padw = x1_p - x1_c - padh = y1_p - y1_c - gt_bboxes_i[:, 0::2] = \ - scale_ratio_i * gt_bboxes_i[:, 0::2] + padw - gt_bboxes_i[:, 1::2] = \ - scale_ratio_i * gt_bboxes_i[:, 1::2] + padh - - mosaic_bboxes.append(gt_bboxes_i) - mosaic_labels.append(gt_labels_i) - - if len(mosaic_labels) > 0: - mosaic_bboxes = np.concatenate(mosaic_bboxes, 0) - mosaic_labels = np.concatenate(mosaic_labels, 0) - - if self.bbox_clip_border: - mosaic_bboxes[:, 0::2] = np.clip(mosaic_bboxes[:, 0::2], 0, - 2 * self.img_scale[1]) - mosaic_bboxes[:, 1::2] = np.clip(mosaic_bboxes[:, 1::2], 0, - 2 * self.img_scale[0]) - - if not self.skip_filter: - mosaic_bboxes, mosaic_labels = \ - self._filter_box_candidates(mosaic_bboxes, mosaic_labels) - - # remove outside bboxes - inside_inds = find_inside_bboxes(mosaic_bboxes, 2 * self.img_scale[0], - 2 * self.img_scale[1]) - mosaic_bboxes = mosaic_bboxes[inside_inds] - mosaic_labels = mosaic_labels[inside_inds] - - results['img'] = mosaic_img - results['img_shape'] = mosaic_img.shape - results['gt_bboxes'] = mosaic_bboxes - results['gt_labels'] = mosaic_labels - - return results - - def _mosaic_combine(self, loc, center_position_xy, img_shape_wh): - """Calculate global coordinate of mosaic image and local coordinate of - cropped sub-image. - - Args: - loc (str): Index for the sub-image, loc in ('top_left', - 'top_right', 'bottom_left', 'bottom_right'). - center_position_xy (Sequence[float]): Mixing center for 4 images, - (x, y). - img_shape_wh (Sequence[int]): Width and height of sub-image - - Returns: - tuple[tuple[float]]: Corresponding coordinate of pasting and - cropping - - paste_coord (tuple): paste corner coordinate in mosaic image. - - crop_coord (tuple): crop corner coordinate in mosaic image. - """ - assert loc in ('top_left', 'top_right', 'bottom_left', 'bottom_right') - if loc == 'top_left': - # index0 to top left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - center_position_xy[0], \ - center_position_xy[1] - crop_coord = img_shape_wh[0] - (x2 - x1), img_shape_wh[1] - ( - y2 - y1), img_shape_wh[0], img_shape_wh[1] - - elif loc == 'top_right': - # index1 to top right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - max(center_position_xy[1] - img_shape_wh[1], 0), \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - center_position_xy[1] - crop_coord = 0, img_shape_wh[1] - (y2 - y1), min( - img_shape_wh[0], x2 - x1), img_shape_wh[1] - - elif loc == 'bottom_left': - # index2 to bottom left part of image - x1, y1, x2, y2 = max(center_position_xy[0] - img_shape_wh[0], 0), \ - center_position_xy[1], \ - center_position_xy[0], \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = img_shape_wh[0] - (x2 - x1), 0, img_shape_wh[0], min( - y2 - y1, img_shape_wh[1]) - - else: - # index3 to bottom right part of image - x1, y1, x2, y2 = center_position_xy[0], \ - center_position_xy[1], \ - min(center_position_xy[0] + img_shape_wh[0], - self.img_scale[1] * 2), \ - min(self.img_scale[0] * 2, center_position_xy[1] + - img_shape_wh[1]) - crop_coord = 0, 0, min(img_shape_wh[0], - x2 - x1), min(y2 - y1, img_shape_wh[1]) - - paste_coord = x1, y1, x2, y2 - return paste_coord, crop_coord - - def _filter_box_candidates(self, bboxes, labels): - """Filter out bboxes too small after Mosaic.""" - bbox_w = bboxes[:, 2] - bboxes[:, 0] - bbox_h = bboxes[:, 3] - bboxes[:, 1] - valid_inds = (bbox_w > self.min_bbox_size) & \ - (bbox_h > self.min_bbox_size) - valid_inds = np.nonzero(valid_inds)[0] - return bboxes[valid_inds], labels[valid_inds] - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'img_scale={self.img_scale}, ' - repr_str += f'center_ratio_range={self.center_ratio_range}, ' - repr_str += f'pad_val={self.pad_val}, ' - repr_str += f'min_bbox_size={self.min_bbox_size}, ' - repr_str += f'skip_filter={self.skip_filter})' - return repr_str - - -@PIPELINES.register_module() -class MixUp: - """MixUp data augmentation. - - .. code:: text - - mixup transform - +------------------------------+ - | mixup image | | - | +--------|--------+ | - | | | | | - |---------------+ | | - | | | | - | | image | | - | | | | - | | | | - | |-----------------+ | - | pad | - +------------------------------+ - - The mixup transform steps are as follows: - - 1. Another random image is picked by dataset and embedded in - the top left patch(after padding and resizing) - 2. The target of mixup transform is the weighted average of mixup - image and origin image. - - Args: - img_scale (Sequence[int]): Image output size after mixup pipeline. - The shape order should be (height, width). Default: (640, 640). - ratio_range (Sequence[float]): Scale ratio of mixup image. - Default: (0.5, 1.5). - flip_ratio (float): Horizontal flip ratio of mixup image. - Default: 0.5. - pad_val (int): Pad value. Default: 114. - max_iters (int): The maximum number of iterations. If the number of - iterations is greater than `max_iters`, but gt_bbox is still - empty, then the iteration is terminated. Default: 15. - min_bbox_size (float): Width and height threshold to filter bboxes. - If the height or width of a box is smaller than this value, it - will be removed. Default: 5. - min_area_ratio (float): Threshold of area ratio between - original bboxes and wrapped bboxes. If smaller than this value, - the box will be removed. Default: 0.2. - max_aspect_ratio (float): Aspect ratio of width and height - threshold to filter bboxes. If max(h/w, w/h) larger than this - value, the box will be removed. Default: 20. - bbox_clip_border (bool, optional): Whether to clip the objects outside - the border of the image. In some dataset like MOT17, the gt bboxes - are allowed to cross the border of images. Therefore, we don't - need to clip the gt bboxes in these cases. Defaults to True. - skip_filter (bool): Whether to skip filtering rules. If it - is True, the filter rule will not be applied, and the - `min_bbox_size` and `min_area_ratio` and `max_aspect_ratio` - is invalid. Default to True. - """ - - def __init__(self, - img_scale=(640, 640), - ratio_range=(0.5, 1.5), - flip_ratio=0.5, - pad_val=114, - max_iters=15, - min_bbox_size=5, - min_area_ratio=0.2, - max_aspect_ratio=20, - bbox_clip_border=True, - skip_filter=True): - assert isinstance(img_scale, tuple) - log_img_scale(img_scale, skip_square=True) - self.dynamic_scale = img_scale - self.ratio_range = ratio_range - self.flip_ratio = flip_ratio - self.pad_val = pad_val - self.max_iters = max_iters - self.min_bbox_size = min_bbox_size - self.min_area_ratio = min_area_ratio - self.max_aspect_ratio = max_aspect_ratio - self.bbox_clip_border = bbox_clip_border - self.skip_filter = skip_filter - - def __call__(self, results): - """Call function to make a mixup of image. - - Args: - results (dict): Result dict. - - Returns: - dict: Result dict with mixup transformed. - """ - - results = self._mixup_transform(results) - return results - - def get_indexes(self, dataset): - """Call function to collect indexes. - - Args: - dataset (:obj:`MultiImageMixDataset`): The dataset. - - Returns: - list: indexes. - """ - - for i in range(self.max_iters): - index = random.randint(0, len(dataset)) - gt_bboxes_i = dataset.get_ann_info(index)['bboxes'] - if len(gt_bboxes_i) != 0: - break - - return index - - def _mixup_transform(self, results): - """MixUp transform function. - - Args: - results (dict): Result dict. - - Returns: - dict: Updated result dict. - """ - - assert 'mix_results' in results - assert len( - results['mix_results']) == 1, 'MixUp only support 2 images now !' - - if results['mix_results'][0]['gt_bboxes'].shape[0] == 0: - # empty bbox - return results - - retrieve_results = results['mix_results'][0] - retrieve_img = retrieve_results['img'] - - jit_factor = random.uniform(*self.ratio_range) - is_filp = random.uniform(0, 1) > self.flip_ratio - - if len(retrieve_img.shape) == 3: - out_img = np.ones( - (self.dynamic_scale[0], self.dynamic_scale[1], 3), - dtype=retrieve_img.dtype) * self.pad_val - else: - out_img = np.ones( - self.dynamic_scale, dtype=retrieve_img.dtype) * self.pad_val - - # 1. keep_ratio resize - scale_ratio = min(self.dynamic_scale[0] / retrieve_img.shape[0], - self.dynamic_scale[1] / retrieve_img.shape[1]) - retrieve_img = mmcv.imresize( - retrieve_img, (int(retrieve_img.shape[1] * scale_ratio), - int(retrieve_img.shape[0] * scale_ratio))) - - # 2. paste - out_img[:retrieve_img.shape[0], :retrieve_img.shape[1]] = retrieve_img - - # 3. scale jit - scale_ratio *= jit_factor - out_img = mmcv.imresize(out_img, (int(out_img.shape[1] * jit_factor), - int(out_img.shape[0] * jit_factor))) - - # 4. flip - if is_filp: - out_img = out_img[:, ::-1, :] - - # 5. random crop - ori_img = results['img'] - origin_h, origin_w = out_img.shape[:2] - target_h, target_w = ori_img.shape[:2] - padded_img = np.zeros( - (max(origin_h, target_h), max(origin_w, - target_w), 3)).astype(np.uint8) - padded_img[:origin_h, :origin_w] = out_img - - x_offset, y_offset = 0, 0 - if padded_img.shape[0] > target_h: - y_offset = random.randint(0, padded_img.shape[0] - target_h) - if padded_img.shape[1] > target_w: - x_offset = random.randint(0, padded_img.shape[1] - target_w) - padded_cropped_img = padded_img[y_offset:y_offset + target_h, - x_offset:x_offset + target_w] - - # 6. adjust bbox - retrieve_gt_bboxes = retrieve_results['gt_bboxes'] - retrieve_gt_bboxes[:, 0::2] = retrieve_gt_bboxes[:, 0::2] * scale_ratio - retrieve_gt_bboxes[:, 1::2] = retrieve_gt_bboxes[:, 1::2] * scale_ratio - if self.bbox_clip_border: - retrieve_gt_bboxes[:, 0::2] = np.clip(retrieve_gt_bboxes[:, 0::2], - 0, origin_w) - retrieve_gt_bboxes[:, 1::2] = np.clip(retrieve_gt_bboxes[:, 1::2], - 0, origin_h) - - if is_filp: - retrieve_gt_bboxes[:, 0::2] = ( - origin_w - retrieve_gt_bboxes[:, 0::2][:, ::-1]) - - # 7. filter - cp_retrieve_gt_bboxes = retrieve_gt_bboxes.copy() - cp_retrieve_gt_bboxes[:, 0::2] = \ - cp_retrieve_gt_bboxes[:, 0::2] - x_offset - cp_retrieve_gt_bboxes[:, 1::2] = \ - cp_retrieve_gt_bboxes[:, 1::2] - y_offset - if self.bbox_clip_border: - cp_retrieve_gt_bboxes[:, 0::2] = np.clip( - cp_retrieve_gt_bboxes[:, 0::2], 0, target_w) - cp_retrieve_gt_bboxes[:, 1::2] = np.clip( - cp_retrieve_gt_bboxes[:, 1::2], 0, target_h) - - # 8. mix up - ori_img = ori_img.astype(np.float32) - mixup_img = 0.5 * ori_img + 0.5 * padded_cropped_img.astype(np.float32) - - retrieve_gt_labels = retrieve_results['gt_labels'] - if not self.skip_filter: - keep_list = self._filter_box_candidates(retrieve_gt_bboxes.T, - cp_retrieve_gt_bboxes.T) - - retrieve_gt_labels = retrieve_gt_labels[keep_list] - cp_retrieve_gt_bboxes = cp_retrieve_gt_bboxes[keep_list] - - mixup_gt_bboxes = np.concatenate( - (results['gt_bboxes'], cp_retrieve_gt_bboxes), axis=0) - mixup_gt_labels = np.concatenate( - (results['gt_labels'], retrieve_gt_labels), axis=0) - - # remove outside bbox - inside_inds = find_inside_bboxes(mixup_gt_bboxes, target_h, target_w) - mixup_gt_bboxes = mixup_gt_bboxes[inside_inds] - mixup_gt_labels = mixup_gt_labels[inside_inds] - - results['img'] = mixup_img.astype(np.uint8) - results['img_shape'] = mixup_img.shape - results['gt_bboxes'] = mixup_gt_bboxes - results['gt_labels'] = mixup_gt_labels - - return results - - def _filter_box_candidates(self, bbox1, bbox2): - """Compute candidate boxes which include following 5 things: - - bbox1 before augment, bbox2 after augment, min_bbox_size (pixels), - min_area_ratio, max_aspect_ratio. - """ - - w1, h1 = bbox1[2] - bbox1[0], bbox1[3] - bbox1[1] - w2, h2 = bbox2[2] - bbox2[0], bbox2[3] - bbox2[1] - ar = np.maximum(w2 / (h2 + 1e-16), h2 / (w2 + 1e-16)) - return ((w2 > self.min_bbox_size) - & (h2 > self.min_bbox_size) - & (w2 * h2 / (w1 * h1 + 1e-16) > self.min_area_ratio) - & (ar < self.max_aspect_ratio)) - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'dynamic_scale={self.dynamic_scale}, ' - repr_str += f'ratio_range={self.ratio_range}, ' - repr_str += f'flip_ratio={self.flip_ratio}, ' - repr_str += f'pad_val={self.pad_val}, ' - repr_str += f'max_iters={self.max_iters}, ' - repr_str += f'min_bbox_size={self.min_bbox_size}, ' - repr_str += f'min_area_ratio={self.min_area_ratio}, ' - repr_str += f'max_aspect_ratio={self.max_aspect_ratio}, ' - repr_str += f'skip_filter={self.skip_filter})' - return repr_str - - -@PIPELINES.register_module() -class RandomAffine: - """Random affine transform data augmentation. - - This operation randomly generates affine transform matrix which including - rotation, translation, shear and scaling transforms. - - Args: - max_rotate_degree (float): Maximum degrees of rotation transform. - Default: 10. - max_translate_ratio (float): Maximum ratio of translation. - Default: 0.1. - scaling_ratio_range (tuple[float]): Min and max ratio of - scaling transform. Default: (0.5, 1.5). - max_shear_degree (float): Maximum degrees of shear - transform. Default: 2. - border (tuple[int]): Distance from height and width sides of input - image to adjust output shape. Only used in mosaic dataset. - Default: (0, 0). - border_val (tuple[int]): Border padding values of 3 channels. - Default: (114, 114, 114). - min_bbox_size (float): Width and height threshold to filter bboxes. - If the height or width of a box is smaller than this value, it - will be removed. Default: 2. - min_area_ratio (float): Threshold of area ratio between - original bboxes and wrapped bboxes. If smaller than this value, - the box will be removed. Default: 0.2. - max_aspect_ratio (float): Aspect ratio of width and height - threshold to filter bboxes. If max(h/w, w/h) larger than this - value, the box will be removed. - bbox_clip_border (bool, optional): Whether to clip the objects outside - the border of the image. In some dataset like MOT17, the gt bboxes - are allowed to cross the border of images. Therefore, we don't - need to clip the gt bboxes in these cases. Defaults to True. - skip_filter (bool): Whether to skip filtering rules. If it - is True, the filter rule will not be applied, and the - `min_bbox_size` and `min_area_ratio` and `max_aspect_ratio` - is invalid. Default to True. - """ - - def __init__(self, - max_rotate_degree=10.0, - max_translate_ratio=0.1, - scaling_ratio_range=(0.5, 1.5), - max_shear_degree=2.0, - border=(0, 0), - border_val=(114, 114, 114), - min_bbox_size=2, - min_area_ratio=0.2, - max_aspect_ratio=20, - bbox_clip_border=True, - skip_filter=True): - assert 0 <= max_translate_ratio <= 1 - assert scaling_ratio_range[0] <= scaling_ratio_range[1] - assert scaling_ratio_range[0] > 0 - self.max_rotate_degree = max_rotate_degree - self.max_translate_ratio = max_translate_ratio - self.scaling_ratio_range = scaling_ratio_range - self.max_shear_degree = max_shear_degree - self.border = border - self.border_val = border_val - self.min_bbox_size = min_bbox_size - self.min_area_ratio = min_area_ratio - self.max_aspect_ratio = max_aspect_ratio - self.bbox_clip_border = bbox_clip_border - self.skip_filter = skip_filter - - def __call__(self, results): - img = results['img'] - height = img.shape[0] + self.border[0] * 2 - width = img.shape[1] + self.border[1] * 2 - - # Rotation - rotation_degree = random.uniform(-self.max_rotate_degree, - self.max_rotate_degree) - rotation_matrix = self._get_rotation_matrix(rotation_degree) - - # Scaling - scaling_ratio = random.uniform(self.scaling_ratio_range[0], - self.scaling_ratio_range[1]) - scaling_matrix = self._get_scaling_matrix(scaling_ratio) - - # Shear - x_degree = random.uniform(-self.max_shear_degree, - self.max_shear_degree) - y_degree = random.uniform(-self.max_shear_degree, - self.max_shear_degree) - shear_matrix = self._get_shear_matrix(x_degree, y_degree) - - # Translation - trans_x = random.uniform(-self.max_translate_ratio, - self.max_translate_ratio) * width - trans_y = random.uniform(-self.max_translate_ratio, - self.max_translate_ratio) * height - translate_matrix = self._get_translation_matrix(trans_x, trans_y) - - warp_matrix = ( - translate_matrix @ shear_matrix @ rotation_matrix @ scaling_matrix) - - img = cv2.warpPerspective( - img, - warp_matrix, - dsize=(width, height), - borderValue=self.border_val) - results['img'] = img - results['img_shape'] = img.shape - - for key in results.get('bbox_fields', []): - bboxes = results[key] - num_bboxes = len(bboxes) - if num_bboxes: - # homogeneous coordinates - xs = bboxes[:, [0, 0, 2, 2]].reshape(num_bboxes * 4) - ys = bboxes[:, [1, 3, 3, 1]].reshape(num_bboxes * 4) - ones = np.ones_like(xs) - points = np.vstack([xs, ys, ones]) - - warp_points = warp_matrix @ points - warp_points = warp_points[:2] / warp_points[2] - xs = warp_points[0].reshape(num_bboxes, 4) - ys = warp_points[1].reshape(num_bboxes, 4) - - warp_bboxes = np.vstack( - (xs.min(1), ys.min(1), xs.max(1), ys.max(1))).T - - if self.bbox_clip_border: - warp_bboxes[:, [0, 2]] = \ - warp_bboxes[:, [0, 2]].clip(0, width) - warp_bboxes[:, [1, 3]] = \ - warp_bboxes[:, [1, 3]].clip(0, height) - - # remove outside bbox - valid_index = find_inside_bboxes(warp_bboxes, height, width) - if not self.skip_filter: - # filter bboxes - filter_index = self.filter_gt_bboxes( - bboxes * scaling_ratio, warp_bboxes) - valid_index = valid_index & filter_index - - results[key] = warp_bboxes[valid_index] - if key in ['gt_bboxes']: - if 'gt_labels' in results: - results['gt_labels'] = results['gt_labels'][ - valid_index] - - if 'gt_masks' in results: - raise NotImplementedError( - 'RandomAffine only supports bbox.') - return results - - def filter_gt_bboxes(self, origin_bboxes, wrapped_bboxes): - origin_w = origin_bboxes[:, 2] - origin_bboxes[:, 0] - origin_h = origin_bboxes[:, 3] - origin_bboxes[:, 1] - wrapped_w = wrapped_bboxes[:, 2] - wrapped_bboxes[:, 0] - wrapped_h = wrapped_bboxes[:, 3] - wrapped_bboxes[:, 1] - aspect_ratio = np.maximum(wrapped_w / (wrapped_h + 1e-16), - wrapped_h / (wrapped_w + 1e-16)) - - wh_valid_idx = (wrapped_w > self.min_bbox_size) & \ - (wrapped_h > self.min_bbox_size) - area_valid_idx = wrapped_w * wrapped_h / (origin_w * origin_h + - 1e-16) > self.min_area_ratio - aspect_ratio_valid_idx = aspect_ratio < self.max_aspect_ratio - return wh_valid_idx & area_valid_idx & aspect_ratio_valid_idx - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(max_rotate_degree={self.max_rotate_degree}, ' - repr_str += f'max_translate_ratio={self.max_translate_ratio}, ' - repr_str += f'scaling_ratio={self.scaling_ratio_range}, ' - repr_str += f'max_shear_degree={self.max_shear_degree}, ' - repr_str += f'border={self.border}, ' - repr_str += f'border_val={self.border_val}, ' - repr_str += f'min_bbox_size={self.min_bbox_size}, ' - repr_str += f'min_area_ratio={self.min_area_ratio}, ' - repr_str += f'max_aspect_ratio={self.max_aspect_ratio}, ' - repr_str += f'skip_filter={self.skip_filter})' - return repr_str - - @staticmethod - def _get_rotation_matrix(rotate_degrees): - radian = math.radians(rotate_degrees) - rotation_matrix = np.array( - [[np.cos(radian), -np.sin(radian), 0.], - [np.sin(radian), np.cos(radian), 0.], [0., 0., 1.]], - dtype=np.float32) - return rotation_matrix - - @staticmethod - def _get_scaling_matrix(scale_ratio): - scaling_matrix = np.array( - [[scale_ratio, 0., 0.], [0., scale_ratio, 0.], [0., 0., 1.]], - dtype=np.float32) - return scaling_matrix - - @staticmethod - def _get_share_matrix(scale_ratio): - scaling_matrix = np.array( - [[scale_ratio, 0., 0.], [0., scale_ratio, 0.], [0., 0., 1.]], - dtype=np.float32) - return scaling_matrix - - @staticmethod - def _get_shear_matrix(x_shear_degrees, y_shear_degrees): - x_radian = math.radians(x_shear_degrees) - y_radian = math.radians(y_shear_degrees) - shear_matrix = np.array([[1, np.tan(x_radian), 0.], - [np.tan(y_radian), 1, 0.], [0., 0., 1.]], - dtype=np.float32) - return shear_matrix - - @staticmethod - def _get_translation_matrix(x, y): - translation_matrix = np.array([[1, 0., x], [0., 1, y], [0., 0., 1.]], - dtype=np.float32) - return translation_matrix - - -@PIPELINES.register_module() -class YOLOXHSVRandomAug: - """Apply HSV augmentation to image sequentially. It is referenced from - https://github.com/Megvii- - BaseDetection/YOLOX/blob/main/yolox/data/data_augment.py#L21. - - Args: - hue_delta (int): delta of hue. Default: 5. - saturation_delta (int): delta of saturation. Default: 30. - value_delta (int): delat of value. Default: 30. - """ - - def __init__(self, hue_delta=5, saturation_delta=30, value_delta=30): - self.hue_delta = hue_delta - self.saturation_delta = saturation_delta - self.value_delta = value_delta - - def __call__(self, results): - img = results['img'] - hsv_gains = np.random.uniform(-1, 1, 3) * [ - self.hue_delta, self.saturation_delta, self.value_delta - ] - # random selection of h, s, v - hsv_gains *= np.random.randint(0, 2, 3) - # prevent overflow - hsv_gains = hsv_gains.astype(np.int16) - img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV).astype(np.int16) - - img_hsv[..., 0] = (img_hsv[..., 0] + hsv_gains[0]) % 180 - img_hsv[..., 1] = np.clip(img_hsv[..., 1] + hsv_gains[1], 0, 255) - img_hsv[..., 2] = np.clip(img_hsv[..., 2] + hsv_gains[2], 0, 255) - cv2.cvtColor(img_hsv.astype(img.dtype), cv2.COLOR_HSV2BGR, dst=img) - - results['img'] = img - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(hue_delta={self.hue_delta}, ' - repr_str += f'saturation_delta={self.saturation_delta}, ' - repr_str += f'value_delta={self.value_delta})' - return repr_str - - -@PIPELINES.register_module() -class CopyPaste: - """Simple Copy-Paste is a Strong Data Augmentation Method for Instance - Segmentation The simple copy-paste transform steps are as follows: - - 1. The destination image is already resized with aspect ratio kept, - cropped and padded. - 2. Randomly select a source image, which is also already resized - with aspect ratio kept, cropped and padded in a similar way - as the destination image. - 3. Randomly select some objects from the source image. - 4. Paste these source objects to the destination image directly, - due to the source and destination image have the same size. - 5. Update object masks of the destination image, for some origin objects - may be occluded. - 6. Generate bboxes from the updated destination masks and - filter some objects which are totally occluded, and adjust bboxes - which are partly occluded. - 7. Append selected source bboxes, masks, and labels. - - Args: - max_num_pasted (int): The maximum number of pasted objects. - Default: 100. - bbox_occluded_thr (int): The threshold of occluded bbox. - Default: 10. - mask_occluded_thr (int): The threshold of occluded mask. - Default: 300. - selected (bool): Whether select objects or not. If select is False, - all objects of the source image will be pasted to the - destination image. - Default: True. - """ - - def __init__( - self, - max_num_pasted=100, - bbox_occluded_thr=10, - mask_occluded_thr=300, - selected=True, - ): - self.max_num_pasted = max_num_pasted - self.bbox_occluded_thr = bbox_occluded_thr - self.mask_occluded_thr = mask_occluded_thr - self.selected = selected - - def get_indexes(self, dataset): - """Call function to collect indexes.s. - - Args: - dataset (:obj:`MultiImageMixDataset`): The dataset. - Returns: - list: Indexes. - """ - return random.randint(0, len(dataset)) - - def __call__(self, results): - """Call function to make a copy-paste of image. - - Args: - results (dict): Result dict. - Returns: - dict: Result dict with copy-paste transformed. - """ - - assert 'mix_results' in results - num_images = len(results['mix_results']) - assert num_images == 1, \ - f'CopyPaste only supports processing 2 images, got {num_images}' - if self.selected: - selected_results = self._select_object(results['mix_results'][0]) - else: - selected_results = results['mix_results'][0] - return self._copy_paste(results, selected_results) - - def _select_object(self, results): - """Select some objects from the source results.""" - bboxes = results['gt_bboxes'] - labels = results['gt_labels'] - masks = results['gt_masks'] - max_num_pasted = min(bboxes.shape[0] + 1, self.max_num_pasted) - num_pasted = np.random.randint(0, max_num_pasted) - selected_inds = np.random.choice( - bboxes.shape[0], size=num_pasted, replace=False) - - selected_bboxes = bboxes[selected_inds] - selected_labels = labels[selected_inds] - selected_masks = masks[selected_inds] - - results['gt_bboxes'] = selected_bboxes - results['gt_labels'] = selected_labels - results['gt_masks'] = selected_masks - return results - - def _copy_paste(self, dst_results, src_results): - """CopyPaste transform function. - - Args: - dst_results (dict): Result dict of the destination image. - src_results (dict): Result dict of the source image. - Returns: - dict: Updated result dict. - """ - dst_img = dst_results['img'] - dst_bboxes = dst_results['gt_bboxes'] - dst_labels = dst_results['gt_labels'] - dst_masks = dst_results['gt_masks'] - - src_img = src_results['img'] - src_bboxes = src_results['gt_bboxes'] - src_labels = src_results['gt_labels'] - src_masks = src_results['gt_masks'] - - if len(src_bboxes) == 0: - return dst_results - - # update masks and generate bboxes from updated masks - composed_mask = np.where(np.any(src_masks.masks, axis=0), 1, 0) - updated_dst_masks = self.get_updated_masks(dst_masks, composed_mask) - updated_dst_bboxes = updated_dst_masks.get_bboxes() - assert len(updated_dst_bboxes) == len(updated_dst_masks) - - # filter totally occluded objects - bboxes_inds = np.all( - np.abs( - (updated_dst_bboxes - dst_bboxes)) <= self.bbox_occluded_thr, - axis=-1) - masks_inds = updated_dst_masks.masks.sum( - axis=(1, 2)) > self.mask_occluded_thr - valid_inds = bboxes_inds | masks_inds - - # Paste source objects to destination image directly - img = dst_img * (1 - composed_mask[..., np.newaxis] - ) + src_img * composed_mask[..., np.newaxis] - bboxes = np.concatenate([updated_dst_bboxes[valid_inds], src_bboxes]) - labels = np.concatenate([dst_labels[valid_inds], src_labels]) - masks = np.concatenate( - [updated_dst_masks.masks[valid_inds], src_masks.masks]) - - dst_results['img'] = img - dst_results['gt_bboxes'] = bboxes - dst_results['gt_labels'] = labels - dst_results['gt_masks'] = BitmapMasks(masks, masks.shape[1], - masks.shape[2]) - - return dst_results - - def get_updated_masks(self, masks, composed_mask): - assert masks.masks.shape[-2:] == composed_mask.shape[-2:], \ - 'Cannot compare two arrays of different size' - masks.masks = np.where(composed_mask, 0, masks.masks) - return masks - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'max_num_pasted={self.max_num_pasted}, ' - repr_str += f'bbox_occluded_thr={self.bbox_occluded_thr}, ' - repr_str += f'mask_occluded_thr={self.mask_occluded_thr}, ' - repr_str += f'selected={self.selected}, ' - return repr_str diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/__init__.py b/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/__init__.py deleted file mode 100755 index bb46b9fb1..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .class_aware_sampler import ClassAwareSampler -from .distributed_sampler import DistributedSampler -from .group_sampler import DistributedGroupSampler, GroupSampler -from .infinite_sampler import InfiniteBatchSampler, InfiniteGroupBatchSampler - -__all__ = [ - 'DistributedSampler', 'DistributedGroupSampler', 'GroupSampler', - 'InfiniteGroupBatchSampler', 'InfiniteBatchSampler', 'ClassAwareSampler' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/class_aware_sampler.py b/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/class_aware_sampler.py deleted file mode 100755 index 0e786d4b2..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/class_aware_sampler.py +++ /dev/null @@ -1,176 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import torch -from mmcv.runner import get_dist_info -from torch.utils.data import Sampler - -from mmdet.core.utils import sync_random_seed - - -class ClassAwareSampler(Sampler): - r"""Sampler that restricts data loading to the label of the dataset. - - A class-aware sampling strategy to effectively tackle the - non-uniform class distribution. The length of the training data is - consistent with source data. Simple improvements based on `Relay - Backpropagation for Effective Learning of Deep Convolutional - Neural Networks `_ - - The implementation logic is referred to - https://github.com/Sense-X/TSD/blob/master/mmdet/datasets/samplers/distributed_classaware_sampler.py - - Args: - dataset: Dataset used for sampling. - samples_per_gpu (int): When model is :obj:`DistributedDataParallel`, - it is the number of training samples on each GPU. - When model is :obj:`DataParallel`, it is - `num_gpus * samples_per_gpu`. - Default : 1. - num_replicas (optional): Number of processes participating in - distributed training. - rank (optional): Rank of the current process within num_replicas. - seed (int, optional): random seed used to shuffle the sampler if - ``shuffle=True``. This number should be identical across all - processes in the distributed group. Default: 0. - num_sample_class (int): The number of samples taken from each - per-label list. Default: 1 - """ - - def __init__(self, - dataset, - samples_per_gpu=1, - num_replicas=None, - rank=None, - seed=0, - num_sample_class=1): - _rank, _num_replicas = get_dist_info() - if num_replicas is None: - num_replicas = _num_replicas - if rank is None: - rank = _rank - - self.dataset = dataset - self.num_replicas = num_replicas - self.samples_per_gpu = samples_per_gpu - self.rank = rank - self.epoch = 0 - # Must be the same across all workers. If None, will use a - # random seed shared among workers - # (require synchronization among all workers) - self.seed = sync_random_seed(seed) - - # The number of samples taken from each per-label list - assert num_sample_class > 0 and isinstance(num_sample_class, int) - self.num_sample_class = num_sample_class - # Get per-label image list from dataset - assert hasattr(dataset, 'get_cat2imgs'), \ - 'dataset must have `get_cat2imgs` function' - self.cat_dict = dataset.get_cat2imgs() - - self.num_samples = int( - math.ceil( - len(self.dataset) * 1.0 / self.num_replicas / - self.samples_per_gpu)) * self.samples_per_gpu - self.total_size = self.num_samples * self.num_replicas - - # get number of images containing each category - self.num_cat_imgs = [len(x) for x in self.cat_dict.values()] - # filter labels without images - self.valid_cat_inds = [ - i for i, length in enumerate(self.num_cat_imgs) if length != 0 - ] - self.num_classes = len(self.valid_cat_inds) - - def __iter__(self): - # deterministically shuffle based on epoch - g = torch.Generator() - g.manual_seed(self.epoch + self.seed) - - # initialize label list - label_iter_list = RandomCycleIter(self.valid_cat_inds, generator=g) - # initialize each per-label image list - data_iter_dict = dict() - for i in self.valid_cat_inds: - data_iter_dict[i] = RandomCycleIter(self.cat_dict[i], generator=g) - - def gen_cat_img_inds(cls_list, data_dict, num_sample_cls): - """Traverse the categories and extract `num_sample_cls` image - indexes of the corresponding categories one by one.""" - id_indices = [] - for _ in range(len(cls_list)): - cls_idx = next(cls_list) - for _ in range(num_sample_cls): - id = next(data_dict[cls_idx]) - id_indices.append(id) - return id_indices - - # deterministically shuffle based on epoch - num_bins = int( - math.ceil(self.total_size * 1.0 / self.num_classes / - self.num_sample_class)) - indices = [] - for i in range(num_bins): - indices += gen_cat_img_inds(label_iter_list, data_iter_dict, - self.num_sample_class) - - # fix extra samples to make it evenly divisible - if len(indices) >= self.total_size: - indices = indices[:self.total_size] - else: - indices += indices[:(self.total_size - len(indices))] - assert len(indices) == self.total_size - - # subsample - offset = self.num_samples * self.rank - indices = indices[offset:offset + self.num_samples] - assert len(indices) == self.num_samples - - return iter(indices) - - def __len__(self): - return self.num_samples - - def set_epoch(self, epoch): - self.epoch = epoch - - -class RandomCycleIter: - """Shuffle the list and do it again after the list have traversed. - - The implementation logic is referred to - https://github.com/wutong16/DistributionBalancedLoss/blob/master/mllt/datasets/loader/sampler.py - - Example: - >>> label_list = [0, 1, 2, 4, 5] - >>> g = torch.Generator() - >>> g.manual_seed(0) - >>> label_iter_list = RandomCycleIter(label_list, generator=g) - >>> index = next(label_iter_list) - Args: - data (list or ndarray): The data that needs to be shuffled. - generator: An torch.Generator object, which is used in setting the seed - for generating random numbers. - """ # noqa: W605 - - def __init__(self, data, generator=None): - self.data = data - self.length = len(data) - self.index = torch.randperm(self.length, generator=generator).numpy() - self.i = 0 - self.generator = generator - - def __iter__(self): - return self - - def __len__(self): - return len(self.data) - - def __next__(self): - if self.i == self.length: - self.index = torch.randperm( - self.length, generator=self.generator).numpy() - self.i = 0 - idx = self.data[self.index[self.i]] - self.i += 1 - return idx diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/distributed_sampler.py b/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/distributed_sampler.py deleted file mode 100755 index daebc830f..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/distributed_sampler.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import torch -from torch.utils.data import DistributedSampler as _DistributedSampler - -from mmdet.core.utils import sync_random_seed -from mmdet.utils import get_device - - -class DistributedSampler(_DistributedSampler): - - def __init__(self, - dataset, - num_replicas=None, - rank=None, - shuffle=True, - seed=0): - super().__init__( - dataset, num_replicas=num_replicas, rank=rank, shuffle=shuffle) - - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - device = get_device() - self.seed = sync_random_seed(seed, device) - - def __iter__(self): - # deterministically shuffle based on epoch - if self.shuffle: - g = torch.Generator() - # When :attr:`shuffle=True`, this ensures all replicas - # use a different random ordering for each epoch. - # Otherwise, the next iteration of this sampler will - # yield the same ordering. - g.manual_seed(self.epoch + self.seed) - indices = torch.randperm(len(self.dataset), generator=g).tolist() - else: - indices = torch.arange(len(self.dataset)).tolist() - - # add extra samples to make it evenly divisible - # in case that indices is shorter than half of total_size - indices = (indices * - math.ceil(self.total_size / len(indices)))[:self.total_size] - assert len(indices) == self.total_size - - # subsample - indices = indices[self.rank:self.total_size:self.num_replicas] - assert len(indices) == self.num_samples - - return iter(indices) diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/group_sampler.py b/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/group_sampler.py deleted file mode 100755 index 46b25f585..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/group_sampler.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -from mmcv.runner import get_dist_info -from torch.utils.data import Sampler - - -class GroupSampler(Sampler): - - def __init__(self, dataset, samples_per_gpu=1): - assert hasattr(dataset, 'flag') - self.dataset = dataset - self.samples_per_gpu = samples_per_gpu - self.flag = dataset.flag.astype(np.int64) - self.group_sizes = np.bincount(self.flag) - self.num_samples = 0 - for i, size in enumerate(self.group_sizes): - self.num_samples += int(np.ceil( - size / self.samples_per_gpu)) * self.samples_per_gpu - - def __iter__(self): - indices = [] - for i, size in enumerate(self.group_sizes): - if size == 0: - continue - indice = np.where(self.flag == i)[0] - assert len(indice) == size - np.random.shuffle(indice) - num_extra = int(np.ceil(size / self.samples_per_gpu) - ) * self.samples_per_gpu - len(indice) - indice = np.concatenate( - [indice, np.random.choice(indice, num_extra)]) - indices.append(indice) - indices = np.concatenate(indices) - indices = [ - indices[i * self.samples_per_gpu:(i + 1) * self.samples_per_gpu] - for i in np.random.permutation( - range(len(indices) // self.samples_per_gpu)) - ] - indices = np.concatenate(indices) - indices = indices.astype(np.int64).tolist() - assert len(indices) == self.num_samples - return iter(indices) - - def __len__(self): - return self.num_samples - - -class DistributedGroupSampler(Sampler): - """Sampler that restricts data loading to a subset of the dataset. - - It is especially useful in conjunction with - :class:`torch.nn.parallel.DistributedDataParallel`. In such case, each - process can pass a DistributedSampler instance as a DataLoader sampler, - and load a subset of the original dataset that is exclusive to it. - - .. note:: - Dataset is assumed to be of constant size. - - Arguments: - dataset: Dataset used for sampling. - num_replicas (optional): Number of processes participating in - distributed training. - rank (optional): Rank of the current process within num_replicas. - seed (int, optional): random seed used to shuffle the sampler if - ``shuffle=True``. This number should be identical across all - processes in the distributed group. Default: 0. - """ - - def __init__(self, - dataset, - samples_per_gpu=1, - num_replicas=None, - rank=None, - seed=0): - _rank, _num_replicas = get_dist_info() - if num_replicas is None: - num_replicas = _num_replicas - if rank is None: - rank = _rank - self.dataset = dataset - self.samples_per_gpu = samples_per_gpu - self.num_replicas = num_replicas - self.rank = rank - self.epoch = 0 - self.seed = seed if seed is not None else 0 - - assert hasattr(self.dataset, 'flag') - self.flag = self.dataset.flag - self.group_sizes = np.bincount(self.flag) - - self.num_samples = 0 - for i, j in enumerate(self.group_sizes): - self.num_samples += int( - math.ceil(self.group_sizes[i] * 1.0 / self.samples_per_gpu / - self.num_replicas)) * self.samples_per_gpu - self.total_size = self.num_samples * self.num_replicas - - def __iter__(self): - # deterministically shuffle based on epoch - g = torch.Generator() - g.manual_seed(self.epoch + self.seed) - - indices = [] - for i, size in enumerate(self.group_sizes): - if size > 0: - indice = np.where(self.flag == i)[0] - assert len(indice) == size - # add .numpy() to avoid bug when selecting indice in parrots. - # TODO: check whether torch.randperm() can be replaced by - # numpy.random.permutation(). - indice = indice[list( - torch.randperm(int(size), generator=g).numpy())].tolist() - extra = int( - math.ceil( - size * 1.0 / self.samples_per_gpu / self.num_replicas) - ) * self.samples_per_gpu * self.num_replicas - len(indice) - # pad indice - tmp = indice.copy() - for _ in range(extra // size): - indice.extend(tmp) - indice.extend(tmp[:extra % size]) - indices.extend(indice) - - assert len(indices) == self.total_size - - indices = [ - indices[j] for i in list( - torch.randperm( - len(indices) // self.samples_per_gpu, generator=g)) - for j in range(i * self.samples_per_gpu, (i + 1) * - self.samples_per_gpu) - ] - - # subsample - offset = self.num_samples * self.rank - indices = indices[offset:offset + self.num_samples] - assert len(indices) == self.num_samples - - return iter(indices) - - def __len__(self): - return self.num_samples - - def set_epoch(self, epoch): - self.epoch = epoch diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/infinite_sampler.py b/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/infinite_sampler.py deleted file mode 100755 index a4aab5a35..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/samplers/infinite_sampler.py +++ /dev/null @@ -1,186 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import itertools - -import numpy as np -import torch -from mmcv.runner import get_dist_info -from torch.utils.data.sampler import Sampler - -from mmdet.core.utils import sync_random_seed - - -class InfiniteGroupBatchSampler(Sampler): - """Similar to `BatchSampler` warping a `GroupSampler. It is designed for - iteration-based runners like `IterBasedRunner` and yields a mini-batch - indices each time, all indices in a batch should be in the same group. - - The implementation logic is referred to - https://github.com/facebookresearch/detectron2/blob/main/detectron2/data/samplers/grouped_batch_sampler.py - - Args: - dataset (object): The dataset. - batch_size (int): When model is :obj:`DistributedDataParallel`, - it is the number of training samples on each GPU. - When model is :obj:`DataParallel`, it is - `num_gpus * samples_per_gpu`. - Default : 1. - world_size (int, optional): Number of processes participating in - distributed training. Default: None. - rank (int, optional): Rank of current process. Default: None. - seed (int): Random seed. Default: 0. - shuffle (bool): Whether shuffle the indices of a dummy `epoch`, it - should be noted that `shuffle` can not guarantee that you can - generate sequential indices because it need to ensure - that all indices in a batch is in a group. Default: True. - """ # noqa: W605 - - def __init__(self, - dataset, - batch_size=1, - world_size=None, - rank=None, - seed=0, - shuffle=True): - _rank, _world_size = get_dist_info() - if world_size is None: - world_size = _world_size - if rank is None: - rank = _rank - self.rank = rank - self.world_size = world_size - self.dataset = dataset - self.batch_size = batch_size - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - self.shuffle = shuffle - - assert hasattr(self.dataset, 'flag') - self.flag = self.dataset.flag - self.group_sizes = np.bincount(self.flag) - # buffer used to save indices of each group - self.buffer_per_group = {k: [] for k in range(len(self.group_sizes))} - - self.size = len(dataset) - self.indices = self._indices_of_rank() - - def _infinite_indices(self): - """Infinitely yield a sequence of indices.""" - g = torch.Generator() - g.manual_seed(self.seed) - while True: - if self.shuffle: - yield from torch.randperm(self.size, generator=g).tolist() - - else: - yield from torch.arange(self.size).tolist() - - def _indices_of_rank(self): - """Slice the infinite indices by rank.""" - yield from itertools.islice(self._infinite_indices(), self.rank, None, - self.world_size) - - def __iter__(self): - # once batch size is reached, yield the indices - for idx in self.indices: - flag = self.flag[idx] - group_buffer = self.buffer_per_group[flag] - group_buffer.append(idx) - if len(group_buffer) == self.batch_size: - yield group_buffer[:] - del group_buffer[:] - - def __len__(self): - """Length of base dataset.""" - return self.size - - def set_epoch(self, epoch): - """Not supported in `IterationBased` runner.""" - raise NotImplementedError - - -class InfiniteBatchSampler(Sampler): - """Similar to `BatchSampler` warping a `DistributedSampler. It is designed - iteration-based runners like `IterBasedRunner` and yields a mini-batch - indices each time. - - The implementation logic is referred to - https://github.com/facebookresearch/detectron2/blob/main/detectron2/data/samplers/grouped_batch_sampler.py - - Args: - dataset (object): The dataset. - batch_size (int): When model is :obj:`DistributedDataParallel`, - it is the number of training samples on each GPU, - When model is :obj:`DataParallel`, it is - `num_gpus * samples_per_gpu`. - Default : 1. - world_size (int, optional): Number of processes participating in - distributed training. Default: None. - rank (int, optional): Rank of current process. Default: None. - seed (int): Random seed. Default: 0. - shuffle (bool): Whether shuffle the dataset or not. Default: True. - """ # noqa: W605 - - def __init__(self, - dataset, - batch_size=1, - world_size=None, - rank=None, - seed=0, - shuffle=True): - _rank, _world_size = get_dist_info() - if world_size is None: - world_size = _world_size - if rank is None: - rank = _rank - self.rank = rank - self.world_size = world_size - self.dataset = dataset - self.batch_size = batch_size - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - self.shuffle = shuffle - self.size = len(dataset) - self.indices = self._indices_of_rank() - - def _infinite_indices(self): - """Infinitely yield a sequence of indices.""" - g = torch.Generator() - g.manual_seed(self.seed) - while True: - if self.shuffle: - yield from torch.randperm(self.size, generator=g).tolist() - - else: - yield from torch.arange(self.size).tolist() - - def _indices_of_rank(self): - """Slice the infinite indices by rank.""" - yield from itertools.islice(self._infinite_indices(), self.rank, None, - self.world_size) - - def __iter__(self): - # once batch size is reached, yield the indices - batch_buffer = [] - for idx in self.indices: - batch_buffer.append(idx) - if len(batch_buffer) == self.batch_size: - yield batch_buffer - batch_buffer = [] - - def __len__(self): - """Length of base dataset.""" - return self.size - - def set_epoch(self, epoch): - """Not supported in `IterationBased` runner.""" - raise NotImplementedError diff --git a/cv/detection/autoassign/pytorch/mmdet/datasets/utils.py b/cv/detection/autoassign/pytorch/mmdet/datasets/utils.py deleted file mode 100755 index 0fb4986c5..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/datasets/utils.py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings - -from mmcv.cnn import VGG -from mmcv.runner.hooks import HOOKS, Hook - -from mmdet.datasets.builder import PIPELINES -from mmdet.datasets.pipelines import (LoadAnnotations, LoadImageFromFile, - LoadPanopticAnnotations) - - -def replace_ImageToTensor(pipelines): - """Replace the ImageToTensor transform in a data pipeline to - DefaultFormatBundle, which is normally useful in batch inference. - - Args: - pipelines (list[dict]): Data pipeline configs. - - Returns: - list: The new pipeline list with all ImageToTensor replaced by - DefaultFormatBundle. - - Examples: - >>> pipelines = [ - ... dict(type='LoadImageFromFile'), - ... dict( - ... type='MultiScaleFlipAug', - ... img_scale=(1333, 800), - ... flip=False, - ... transforms=[ - ... dict(type='Resize', keep_ratio=True), - ... dict(type='RandomFlip'), - ... dict(type='Normalize', mean=[0, 0, 0], std=[1, 1, 1]), - ... dict(type='Pad', size_divisor=32), - ... dict(type='ImageToTensor', keys=['img']), - ... dict(type='Collect', keys=['img']), - ... ]) - ... ] - >>> expected_pipelines = [ - ... dict(type='LoadImageFromFile'), - ... dict( - ... type='MultiScaleFlipAug', - ... img_scale=(1333, 800), - ... flip=False, - ... transforms=[ - ... dict(type='Resize', keep_ratio=True), - ... dict(type='RandomFlip'), - ... dict(type='Normalize', mean=[0, 0, 0], std=[1, 1, 1]), - ... dict(type='Pad', size_divisor=32), - ... dict(type='DefaultFormatBundle'), - ... dict(type='Collect', keys=['img']), - ... ]) - ... ] - >>> assert expected_pipelines == replace_ImageToTensor(pipelines) - """ - pipelines = copy.deepcopy(pipelines) - for i, pipeline in enumerate(pipelines): - if pipeline['type'] == 'MultiScaleFlipAug': - assert 'transforms' in pipeline - pipeline['transforms'] = replace_ImageToTensor( - pipeline['transforms']) - elif pipeline['type'] == 'ImageToTensor': - warnings.warn( - '"ImageToTensor" pipeline is replaced by ' - '"DefaultFormatBundle" for batch inference. It is ' - 'recommended to manually replace it in the test ' - 'data pipeline in your config file.', UserWarning) - pipelines[i] = {'type': 'DefaultFormatBundle'} - return pipelines - - -def get_loading_pipeline(pipeline): - """Only keep loading image and annotations related configuration. - - Args: - pipeline (list[dict]): Data pipeline configs. - - Returns: - list[dict]: The new pipeline list with only keep - loading image and annotations related configuration. - - Examples: - >>> pipelines = [ - ... dict(type='LoadImageFromFile'), - ... dict(type='LoadAnnotations', with_bbox=True), - ... dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), - ... dict(type='RandomFlip', flip_ratio=0.5), - ... dict(type='Normalize', **img_norm_cfg), - ... dict(type='Pad', size_divisor=32), - ... dict(type='DefaultFormatBundle'), - ... dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']) - ... ] - >>> expected_pipelines = [ - ... dict(type='LoadImageFromFile'), - ... dict(type='LoadAnnotations', with_bbox=True) - ... ] - >>> assert expected_pipelines ==\ - ... get_loading_pipeline(pipelines) - """ - loading_pipeline_cfg = [] - for cfg in pipeline: - obj_cls = PIPELINES.get(cfg['type']) - # TODO:use more elegant way to distinguish loading modules - if obj_cls is not None and obj_cls in (LoadImageFromFile, - LoadAnnotations, - LoadPanopticAnnotations): - loading_pipeline_cfg.append(cfg) - assert len(loading_pipeline_cfg) == 2, \ - 'The data pipeline in your config file must include ' \ - 'loading image and annotations related pipeline.' - return loading_pipeline_cfg - - -@HOOKS.register_module() -class NumClassCheckHook(Hook): - - def _check_head(self, runner): - """Check whether the `num_classes` in head matches the length of - `CLASSES` in `dataset`. - - Args: - runner (obj:`EpochBasedRunner`): Epoch based Runner. - """ - model = runner.model - dataset = runner.data_loader.dataset - if dataset.CLASSES is None: - runner.logger.warning( - f'Please set `CLASSES` ' - f'in the {dataset.__class__.__name__} and' - f'check if it is consistent with the `num_classes` ' - f'of head') - else: - assert type(dataset.CLASSES) is not str, \ - (f'`CLASSES` in {dataset.__class__.__name__}' - f'should be a tuple of str.' - f'Add comma if number of classes is 1 as ' - f'CLASSES = ({dataset.CLASSES},)') - for name, module in model.named_modules(): - if hasattr(module, 'num_classes') and not isinstance( - module, VGG): - assert module.num_classes == len(dataset.CLASSES), \ - (f'The `num_classes` ({module.num_classes}) in ' - f'{module.__class__.__name__} of ' - f'{model.__class__.__name__} does not matches ' - f'the length of `CLASSES` ' - f'{len(dataset.CLASSES)}) in ' - f'{dataset.__class__.__name__}') - - def before_train_epoch(self, runner): - """Check whether the training dataset is compatible with head. - - Args: - runner (obj:`EpochBasedRunner`): Epoch based Runner. - """ - self._check_head(runner) - - def before_val_epoch(self, runner): - """Check whether the dataset in val epoch is compatible with head. - - Args: - runner (obj:`EpochBasedRunner`): Epoch based Runner. - """ - self._check_head(runner) diff --git a/cv/detection/autoassign/pytorch/mmdet/models/__init__.py b/cv/detection/autoassign/pytorch/mmdet/models/__init__.py deleted file mode 100755 index 1c693a55a..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .backbones import * # noqa: F401,F403 -from .builder import (BACKBONES, DETECTORS, HEADS, LOSSES, NECKS, - ROI_EXTRACTORS, SHARED_HEADS, build_backbone, - build_detector, build_head, build_loss, build_neck, - build_roi_extractor, build_shared_head) -from .dense_heads import * # noqa: F401,F403 -from .detectors import * # noqa: F401,F403 -from .losses import * # noqa: F401,F403 -from .necks import * # noqa: F401,F403 diff --git a/cv/detection/autoassign/pytorch/mmdet/models/backbones/__init__.py b/cv/detection/autoassign/pytorch/mmdet/models/backbones/__init__.py deleted file mode 100755 index ee5926e2c..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/backbones/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .resnet import ResNet, ResNetV1d diff --git a/cv/detection/autoassign/pytorch/mmdet/models/backbones/resnet.py b/cv/detection/autoassign/pytorch/mmdet/models/backbones/resnet.py deleted file mode 100755 index d7a5d721f..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/backbones/resnet.py +++ /dev/null @@ -1,672 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn -import torch.utils.checkpoint as cp -from mmcv.cnn import build_conv_layer, build_norm_layer, build_plugin_layer -from mmcv.runner import BaseModule -from torch.nn.modules.batchnorm import _BatchNorm - -from ..builder import BACKBONES -from ..utils import ResLayer - - -class BasicBlock(BaseModule): - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - super(BasicBlock, self).__init__(init_cfg) - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - 3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - self.add_module(self.norm1_name, norm1) - self.conv2 = build_conv_layer( - conv_cfg, planes, planes, 3, padding=1, bias=False) - self.add_module(self.norm2_name, norm2) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.norm2(out) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -class Bottleneck(BaseModule): - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_cfg=None): - """Bottleneck block for ResNet. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if - it is "caffe", the stride-two layer is the first 1x1 conv layer. - """ - super(Bottleneck, self).__init__(init_cfg) - assert style in ['pytorch', 'caffe'] - assert dcn is None or isinstance(dcn, dict) - assert plugins is None or isinstance(plugins, list) - if plugins is not None: - allowed_position = ['after_conv1', 'after_conv2', 'after_conv3'] - assert all(p['position'] in allowed_position for p in plugins) - - self.inplanes = inplanes - self.planes = planes - self.stride = stride - self.dilation = dilation - self.style = style - self.with_cp = with_cp - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.dcn = dcn - self.with_dcn = dcn is not None - self.plugins = plugins - self.with_plugins = plugins is not None - - if self.with_plugins: - # collect plugins for conv1/conv2/conv3 - self.after_conv1_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv1' - ] - self.after_conv2_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv2' - ] - self.after_conv3_plugins = [ - plugin['cfg'] for plugin in plugins - if plugin['position'] == 'after_conv3' - ] - - if self.style == 'pytorch': - self.conv1_stride = 1 - self.conv2_stride = stride - else: - self.conv1_stride = stride - self.conv2_stride = 1 - - self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1) - self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2) - self.norm3_name, norm3 = build_norm_layer( - norm_cfg, planes * self.expansion, postfix=3) - - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - kernel_size=1, - stride=self.conv1_stride, - bias=False) - self.add_module(self.norm1_name, norm1) - fallback_on_stride = False - if self.with_dcn: - fallback_on_stride = dcn.pop('fallback_on_stride', False) - if not self.with_dcn or fallback_on_stride: - self.conv2 = build_conv_layer( - conv_cfg, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - else: - assert self.conv_cfg is None, 'conv_cfg must be None for DCN' - self.conv2 = build_conv_layer( - dcn, - planes, - planes, - kernel_size=3, - stride=self.conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.add_module(self.norm2_name, norm2) - self.conv3 = build_conv_layer( - conv_cfg, - planes, - planes * self.expansion, - kernel_size=1, - bias=False) - self.add_module(self.norm3_name, norm3) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - - if self.with_plugins: - self.after_conv1_plugin_names = self.make_block_plugins( - planes, self.after_conv1_plugins) - self.after_conv2_plugin_names = self.make_block_plugins( - planes, self.after_conv2_plugins) - self.after_conv3_plugin_names = self.make_block_plugins( - planes * self.expansion, self.after_conv3_plugins) - - def make_block_plugins(self, in_channels, plugins): - """make plugins for block. - - Args: - in_channels (int): Input channels of plugin. - plugins (list[dict]): List of plugins cfg to build. - - Returns: - list[str]: List of the names of plugin. - """ - assert isinstance(plugins, list) - plugin_names = [] - for plugin in plugins: - plugin = plugin.copy() - name, layer = build_plugin_layer( - plugin, - in_channels=in_channels, - postfix=plugin.pop('postfix', '')) - assert not hasattr(self, name), f'duplicate plugin {name}' - self.add_module(name, layer) - plugin_names.append(name) - return plugin_names - - def forward_plugin(self, x, plugin_names): - out = x - for name in plugin_names: - out = getattr(self, name)(out) - return out - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) - - @property - def norm3(self): - """nn.Module: normalization layer after the third convolution layer""" - return getattr(self, self.norm3_name) - - def forward(self, x): - """Forward function.""" - - def _inner_forward(x): - identity = x - out = self.conv1(x) - out = self.norm1(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv1_plugin_names) - - out = self.conv2(out) - out = self.norm2(out) - out = self.relu(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv2_plugin_names) - - out = self.conv3(out) - out = self.norm3(out) - - if self.with_plugins: - out = self.forward_plugin(out, self.after_conv3_plugin_names) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -@BACKBONES.register_module() -class ResNet(BaseModule): - """ResNet backbone. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - stem_channels (int | None): Number of stem channels. If not specified, - it will be the same as `base_channels`. Default: None. - base_channels (int): Number of base channels of res layer. Default: 64. - in_channels (int): Number of input image channels. Default: 3. - num_stages (int): Resnet stages. Default: 4. - strides (Sequence[int]): Strides of the first block of each stage. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. - deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. - frozen_stages (int): Stages to be frozen (stop grad and set eval mode). - -1 means not freezing any parameters. - norm_cfg (dict): Dictionary to construct and config norm layer. - norm_eval (bool): Whether to set norm layers to eval mode, namely, - freeze running stats (mean and var). Note: Effect on Batch Norm - and its variants only. - plugins (list[dict]): List of plugins for stages, each dict contains: - - - cfg (dict, required): Cfg dict to build plugin. - - position (str, required): Position inside block to insert - plugin, options are 'after_conv1', 'after_conv2', 'after_conv3'. - - stages (tuple[bool], optional): Stages to apply plugin, length - should be same as 'num_stages'. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. - zero_init_residual (bool): Whether to use zero init for last norm layer - in resblocks to let them behave as identity. - pretrained (str, optional): model pretrained path. Default: None - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None - - Example: - >>> from mmdet.models import ResNet - >>> import torch - >>> self = ResNet(depth=18) - >>> self.eval() - >>> inputs = torch.rand(1, 3, 32, 32) - >>> level_outputs = self.forward(inputs) - >>> for level_out in level_outputs: - ... print(tuple(level_out.shape)) - (1, 64, 8, 8) - (1, 128, 4, 4) - (1, 256, 2, 2) - (1, 512, 1, 1) - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - in_channels=3, - stem_channels=None, - base_channels=64, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - deep_stem=False, - avg_down=False, - frozen_stages=-1, - conv_cfg=None, - norm_cfg=dict(type='BN', requires_grad=True), - norm_eval=True, - dcn=None, - stage_with_dcn=(False, False, False, False), - plugins=None, - with_cp=False, - zero_init_residual=True, - pretrained=None, - init_cfg=None): - super(ResNet, self).__init__(init_cfg) - self.zero_init_residual = zero_init_residual - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - - block_init_cfg = None - assert not (init_cfg and pretrained), \ - 'init_cfg and pretrained cannot be specified at the same time' - if isinstance(pretrained, str): - warnings.warn('DeprecationWarning: pretrained is deprecated, ' - 'please use "init_cfg" instead') - self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - elif pretrained is None: - if init_cfg is None: - self.init_cfg = [ - dict(type='Kaiming', layer='Conv2d'), - dict( - type='Constant', - val=1, - layer=['_BatchNorm', 'GroupNorm']) - ] - block = self.arch_settings[depth][0] - if self.zero_init_residual: - if block is BasicBlock: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm2')) - elif block is Bottleneck: - block_init_cfg = dict( - type='Constant', - val=0, - override=dict(name='norm3')) - else: - raise TypeError('pretrained must be a str or None') - - self.depth = depth - if stem_channels is None: - stem_channels = base_channels - self.stem_channels = stem_channels - self.base_channels = base_channels - self.num_stages = num_stages - assert num_stages >= 1 and num_stages <= 4 - self.strides = strides - self.dilations = dilations - assert len(strides) == len(dilations) == num_stages - self.out_indices = out_indices - assert max(out_indices) < num_stages - self.style = style - self.deep_stem = deep_stem - self.avg_down = avg_down - self.frozen_stages = frozen_stages - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.with_cp = with_cp - self.norm_eval = norm_eval - self.dcn = dcn - self.stage_with_dcn = stage_with_dcn - if dcn is not None: - assert len(stage_with_dcn) == num_stages - self.plugins = plugins - self.block, stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - self.inplanes = stem_channels - - self._make_stem_layer(in_channels, stem_channels) - - self.res_layers = [] - for i, num_blocks in enumerate(self.stage_blocks): - stride = strides[i] - dilation = dilations[i] - dcn = self.dcn if self.stage_with_dcn[i] else None - if plugins is not None: - stage_plugins = self.make_stage_plugins(plugins, i) - else: - stage_plugins = None - planes = base_channels * 2**i - res_layer = self.make_res_layer( - block=self.block, - inplanes=self.inplanes, - planes=planes, - num_blocks=num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - avg_down=self.avg_down, - with_cp=with_cp, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - dcn=dcn, - plugins=stage_plugins, - init_cfg=block_init_cfg) - self.inplanes = planes * self.block.expansion - layer_name = f'layer{i + 1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self._freeze_stages() - - self.feat_dim = self.block.expansion * base_channels * 2**( - len(self.stage_blocks) - 1) - - def make_stage_plugins(self, plugins, stage_idx): - """Make plugins for ResNet ``stage_idx`` th stage. - - Currently we support to insert ``context_block``, - ``empirical_attention_block``, ``nonlocal_block`` into the backbone - like ResNet/ResNeXt. They could be inserted after conv1/conv2/conv3 of - Bottleneck. - - An example of plugins format could be: - - Examples: - >>> plugins=[ - ... dict(cfg=dict(type='xxx', arg1='xxx'), - ... stages=(False, True, True, True), - ... position='after_conv2'), - ... dict(cfg=dict(type='yyy'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='1'), - ... stages=(True, True, True, True), - ... position='after_conv3'), - ... dict(cfg=dict(type='zzz', postfix='2'), - ... stages=(True, True, True, True), - ... position='after_conv3') - ... ] - >>> self = ResNet(depth=18) - >>> stage_plugins = self.make_stage_plugins(plugins, 0) - >>> assert len(stage_plugins) == 3 - - Suppose ``stage_idx=0``, the structure of blocks in the stage would be: - - .. code-block:: none - - conv1-> conv2->conv3->yyy->zzz1->zzz2 - - Suppose 'stage_idx=1', the structure of blocks in the stage would be: - - .. code-block:: none - - conv1-> conv2->xxx->conv3->yyy->zzz1->zzz2 - - If stages is missing, the plugin would be applied to all stages. - - Args: - plugins (list[dict]): List of plugins cfg to build. The postfix is - required if multiple same type plugins are inserted. - stage_idx (int): Index of stage to build - - Returns: - list[dict]: Plugins for current stage - """ - stage_plugins = [] - for plugin in plugins: - plugin = plugin.copy() - stages = plugin.pop('stages', None) - assert stages is None or len(stages) == self.num_stages - # whether to insert plugin into current stage - if stages is None or stages[stage_idx]: - stage_plugins.append(plugin) - - return stage_plugins - - def make_res_layer(self, **kwargs): - """Pack all blocks in a stage into a ``ResLayer``.""" - return ResLayer(**kwargs) - - @property - def norm1(self): - """nn.Module: the normalization layer named "norm1" """ - return getattr(self, self.norm1_name) - - def _make_stem_layer(self, in_channels, stem_channels): - if self.deep_stem: - self.stem = nn.Sequential( - build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels // 2, - kernel_size=3, - stride=2, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels // 2, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels // 2)[1], - nn.ReLU(inplace=True), - build_conv_layer( - self.conv_cfg, - stem_channels // 2, - stem_channels, - kernel_size=3, - stride=1, - padding=1, - bias=False), - build_norm_layer(self.norm_cfg, stem_channels)[1], - nn.ReLU(inplace=True)) - else: - self.conv1 = build_conv_layer( - self.conv_cfg, - in_channels, - stem_channels, - kernel_size=7, - stride=2, - padding=3, - bias=False) - self.norm1_name, norm1 = build_norm_layer( - self.norm_cfg, stem_channels, postfix=1) - self.add_module(self.norm1_name, norm1) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - def _freeze_stages(self): - if self.frozen_stages >= 0: - if self.deep_stem: - self.stem.eval() - for param in self.stem.parameters(): - param.requires_grad = False - else: - self.norm1.eval() - for m in [self.conv1, self.norm1]: - for param in m.parameters(): - param.requires_grad = False - - for i in range(1, self.frozen_stages + 1): - m = getattr(self, f'layer{i}') - m.eval() - for param in m.parameters(): - param.requires_grad = False - - def forward(self, x): - """Forward function.""" - if self.deep_stem: - x = self.stem(x) - else: - x = self.conv1(x) - x = self.norm1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - return tuple(outs) - - def train(self, mode=True): - """Convert the model into training mode while keep normalization layer - freezed.""" - super(ResNet, self).train(mode) - self._freeze_stages() - if mode and self.norm_eval: - for m in self.modules(): - # trick: eval have effect on BatchNorm only - if isinstance(m, _BatchNorm): - m.eval() - - -@BACKBONES.register_module() -class ResNetV1d(ResNet): - r"""ResNetV1d variant described in `Bag of Tricks - `_. - - Compared with default ResNet(ResNetV1b), ResNetV1d replaces the 7x7 conv in - the input stem with three 3x3 convs. And in the downsampling block, a 2x2 - avg_pool with stride 2 is added before conv, whose stride is changed to 1. - """ - - def __init__(self, **kwargs): - super(ResNetV1d, self).__init__( - deep_stem=True, avg_down=True, **kwargs) diff --git a/cv/detection/autoassign/pytorch/mmdet/models/builder.py b/cv/detection/autoassign/pytorch/mmdet/models/builder.py deleted file mode 100755 index 2000a0619..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/builder.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -from mmcv.cnn import MODELS as MMCV_MODELS -from mmcv.utils import Registry - -MODELS = Registry('models', parent=MMCV_MODELS) - -BACKBONES = MODELS -NECKS = MODELS -ROI_EXTRACTORS = MODELS -SHARED_HEADS = MODELS -HEADS = MODELS -LOSSES = MODELS -DETECTORS = MODELS - - -def build_backbone(cfg): - """Build backbone.""" - return BACKBONES.build(cfg) - - -def build_neck(cfg): - """Build neck.""" - return NECKS.build(cfg) - - -def build_roi_extractor(cfg): - """Build roi extractor.""" - return ROI_EXTRACTORS.build(cfg) - - -def build_shared_head(cfg): - """Build shared head.""" - return SHARED_HEADS.build(cfg) - - -def build_head(cfg): - """Build head.""" - return HEADS.build(cfg) - - -def build_loss(cfg): - """Build loss.""" - return LOSSES.build(cfg) - - -def build_detector(cfg, train_cfg=None, test_cfg=None): - """Build detector.""" - if train_cfg is not None or test_cfg is not None: - warnings.warn( - 'train_cfg and test_cfg is deprecated, ' - 'please specify them in model', UserWarning) - assert cfg.get('train_cfg') is None or train_cfg is None, \ - 'train_cfg specified in both outer field and model field ' - assert cfg.get('test_cfg') is None or test_cfg is None, \ - 'test_cfg specified in both outer field and model field ' - return DETECTORS.build( - cfg, default_args=dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/__init__.py b/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/__init__.py deleted file mode 100755 index cc53dbc89..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .anchor_free_head import AnchorFreeHead -from .atss_head import ATSSHead -from .autoassign_head import AutoAssignHead -from .fcos_head import FCOSHead -from .paa_head import PAAHead diff --git a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/anchor_free_head.py b/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/anchor_free_head.py deleted file mode 100755 index 33a539885..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/anchor_free_head.py +++ /dev/null @@ -1,350 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from abc import abstractmethod - -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule -from mmcv.runner import force_fp32 - -from mmdet.core import build_bbox_coder, multi_apply -from mmdet.core.anchor.point_generator import MlvlPointGenerator -from ..builder import HEADS, build_loss -from .base_dense_head import BaseDenseHead -from .dense_test_mixins import BBoxTestMixin - - -@HEADS.register_module() -class AnchorFreeHead(BaseDenseHead, BBoxTestMixin): - """Anchor-free head (FCOS, Fovea, RepPoints, etc.). - - Args: - num_classes (int): Number of categories excluding the background - category. - in_channels (int): Number of channels in the input feature map. - feat_channels (int): Number of hidden channels. Used in child classes. - stacked_convs (int): Number of stacking convs of the head. - strides (tuple): Downsample factor of each feature map. - dcn_on_last_conv (bool): If true, use dcn in the last layer of - towers. Default: False. - conv_bias (bool | str): If specified as `auto`, it will be decided by - the norm_cfg. Bias of conv will be set as True if `norm_cfg` is - None, otherwise False. Default: "auto". - loss_cls (dict): Config of classification loss. - loss_bbox (dict): Config of localization loss. - bbox_coder (dict): Config of bbox coder. Defaults - 'DistancePointBBoxCoder'. - conv_cfg (dict): Config dict for convolution layer. Default: None. - norm_cfg (dict): Config dict for normalization layer. Default: None. - train_cfg (dict): Training config of anchor head. - test_cfg (dict): Testing config of anchor head. - init_cfg (dict or list[dict], optional): Initialization config dict. - """ # noqa: W605 - - _version = 1 - - def __init__(self, - num_classes, - in_channels, - feat_channels=256, - stacked_convs=4, - strides=(4, 8, 16, 32, 64), - dcn_on_last_conv=False, - conv_bias='auto', - loss_cls=dict( - type='FocalLoss', - use_sigmoid=True, - gamma=2.0, - alpha=0.25, - loss_weight=1.0), - loss_bbox=dict(type='IoULoss', loss_weight=1.0), - bbox_coder=dict(type='DistancePointBBoxCoder'), - conv_cfg=None, - norm_cfg=None, - train_cfg=None, - test_cfg=None, - init_cfg=dict( - type='Normal', - layer='Conv2d', - std=0.01, - override=dict( - type='Normal', - name='conv_cls', - std=0.01, - bias_prob=0.01))): - super(AnchorFreeHead, self).__init__(init_cfg) - self.num_classes = num_classes - self.use_sigmoid_cls = loss_cls.get('use_sigmoid', False) - if self.use_sigmoid_cls: - self.cls_out_channels = num_classes - else: - self.cls_out_channels = num_classes + 1 - self.in_channels = in_channels - self.feat_channels = feat_channels - self.stacked_convs = stacked_convs - self.strides = strides - self.dcn_on_last_conv = dcn_on_last_conv - assert conv_bias == 'auto' or isinstance(conv_bias, bool) - self.conv_bias = conv_bias - self.loss_cls = build_loss(loss_cls) - self.loss_bbox = build_loss(loss_bbox) - self.bbox_coder = build_bbox_coder(bbox_coder) - - self.prior_generator = MlvlPointGenerator(strides) - - # In order to keep a more general interface and be consistent with - # anchor_head. We can think of point like one anchor - self.num_base_priors = self.prior_generator.num_base_priors[0] - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.fp16_enabled = False - - self._init_layers() - - def _init_layers(self): - """Initialize layers of the head.""" - self._init_cls_convs() - self._init_reg_convs() - self._init_predictor() - - def _init_cls_convs(self): - """Initialize classification conv layers of the head.""" - self.cls_convs = nn.ModuleList() - for i in range(self.stacked_convs): - chn = self.in_channels if i == 0 else self.feat_channels - if self.dcn_on_last_conv and i == self.stacked_convs - 1: - conv_cfg = dict(type='DCNv2') - else: - conv_cfg = self.conv_cfg - self.cls_convs.append( - ConvModule( - chn, - self.feat_channels, - 3, - stride=1, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=self.norm_cfg, - bias=self.conv_bias)) - - def _init_reg_convs(self): - """Initialize bbox regression conv layers of the head.""" - self.reg_convs = nn.ModuleList() - for i in range(self.stacked_convs): - chn = self.in_channels if i == 0 else self.feat_channels - if self.dcn_on_last_conv and i == self.stacked_convs - 1: - conv_cfg = dict(type='DCNv2') - else: - conv_cfg = self.conv_cfg - self.reg_convs.append( - ConvModule( - chn, - self.feat_channels, - 3, - stride=1, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=self.norm_cfg, - bias=self.conv_bias)) - - def _init_predictor(self): - """Initialize predictor layers of the head.""" - self.conv_cls = nn.Conv2d( - self.feat_channels, self.cls_out_channels, 3, padding=1) - self.conv_reg = nn.Conv2d(self.feat_channels, 4, 3, padding=1) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - """Hack some keys of the model state dict so that can load checkpoints - of previous version.""" - version = local_metadata.get('version', None) - if version is None: - # the key is different in early versions - # for example, 'fcos_cls' become 'conv_cls' now - bbox_head_keys = [ - k for k in state_dict.keys() if k.startswith(prefix) - ] - ori_predictor_keys = [] - new_predictor_keys = [] - # e.g. 'fcos_cls' or 'fcos_reg' - for key in bbox_head_keys: - ori_predictor_keys.append(key) - key = key.split('.') - conv_name = None - if key[1].endswith('cls'): - conv_name = 'conv_cls' - elif key[1].endswith('reg'): - conv_name = 'conv_reg' - elif key[1].endswith('centerness'): - conv_name = 'conv_centerness' - else: - assert NotImplementedError - if conv_name is not None: - key[1] = conv_name - new_predictor_keys.append('.'.join(key)) - else: - ori_predictor_keys.pop(-1) - for i in range(len(new_predictor_keys)): - state_dict[new_predictor_keys[i]] = state_dict.pop( - ori_predictor_keys[i]) - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, missing_keys, unexpected_keys, - error_msgs) - - def forward(self, feats): - """Forward features from the upstream network. - - Args: - feats (tuple[Tensor]): Features from the upstream network, each is - a 4D-tensor. - - Returns: - tuple: Usually contain classification scores and bbox predictions. - cls_scores (list[Tensor]): Box scores for each scale level, - each is a 4D-tensor, the channel number is - num_points * num_classes. - bbox_preds (list[Tensor]): Box energies / deltas for each scale - level, each is a 4D-tensor, the channel number is - num_points * 4. - """ - return multi_apply(self.forward_single, feats)[:2] - - def forward_single(self, x): - """Forward features of a single scale level. - - Args: - x (Tensor): FPN feature maps of the specified stride. - - Returns: - tuple: Scores for each class, bbox predictions, features - after classification and regression conv layers, some - models needs these features like FCOS. - """ - cls_feat = x - reg_feat = x - - for cls_layer in self.cls_convs: - cls_feat = cls_layer(cls_feat) - cls_score = self.conv_cls(cls_feat) - - for reg_layer in self.reg_convs: - reg_feat = reg_layer(reg_feat) - bbox_pred = self.conv_reg(reg_feat) - return cls_score, bbox_pred, cls_feat, reg_feat - - @abstractmethod - @force_fp32(apply_to=('cls_scores', 'bbox_preds')) - def loss(self, - cls_scores, - bbox_preds, - gt_bboxes, - gt_labels, - img_metas, - gt_bboxes_ignore=None): - """Compute loss of the head. - - Args: - cls_scores (list[Tensor]): Box scores for each scale level, - each is a 4D-tensor, the channel number is - num_points * num_classes. - bbox_preds (list[Tensor]): Box energies / deltas for each scale - level, each is a 4D-tensor, the channel number is - num_points * 4. - gt_bboxes (list[Tensor]): Ground truth bboxes for each image with - shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format. - gt_labels (list[Tensor]): class indices corresponding to each box - img_metas (list[dict]): Meta information of each image, e.g., - image size, scaling factor, etc. - gt_bboxes_ignore (None | list[Tensor]): specify which bounding - boxes can be ignored when computing the loss. - """ - - raise NotImplementedError - - @abstractmethod - def get_targets(self, points, gt_bboxes_list, gt_labels_list): - """Compute regression, classification and centerness targets for points - in multiple images. - - Args: - points (list[Tensor]): Points of each fpn level, each has shape - (num_points, 2). - gt_bboxes_list (list[Tensor]): Ground truth bboxes of each image, - each has shape (num_gt, 4). - gt_labels_list (list[Tensor]): Ground truth labels of each box, - each has shape (num_gt,). - """ - raise NotImplementedError - - def _get_points_single(self, - featmap_size, - stride, - dtype, - device, - flatten=False): - """Get points of a single scale level. - - This function will be deprecated soon. - """ - - warnings.warn( - '`_get_points_single` in `AnchorFreeHead` will be ' - 'deprecated soon, we support a multi level point generator now' - 'you can get points of a single level feature map ' - 'with `self.prior_generator.single_level_grid_priors` ') - - h, w = featmap_size - # First create Range with the default dtype, than convert to - # target `dtype` for onnx exporting. - x_range = torch.arange(w, device=device).to(dtype) - y_range = torch.arange(h, device=device).to(dtype) - y, x = torch.meshgrid(y_range, x_range) - if flatten: - y = y.flatten() - x = x.flatten() - return y, x - - def get_points(self, featmap_sizes, dtype, device, flatten=False): - """Get points according to feature map sizes. - - Args: - featmap_sizes (list[tuple]): Multi-level feature map sizes. - dtype (torch.dtype): Type of points. - device (torch.device): Device of points. - - Returns: - tuple: points of each image. - """ - warnings.warn( - '`get_points` in `AnchorFreeHead` will be ' - 'deprecated soon, we support a multi level point generator now' - 'you can get points of all levels ' - 'with `self.prior_generator.grid_priors` ') - - mlvl_points = [] - for i in range(len(featmap_sizes)): - mlvl_points.append( - self._get_points_single(featmap_sizes[i], self.strides[i], - dtype, device, flatten)) - return mlvl_points - - def aug_test(self, feats, img_metas, rescale=False): - """Test function with test time augmentation. - - Args: - feats (list[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains features for all images in the batch. - img_metas (list[list[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. each dict has image information. - rescale (bool, optional): Whether to rescale the results. - Defaults to False. - - Returns: - list[ndarray]: bbox results of each class - """ - return self.aug_test_bboxes(feats, img_metas, rescale=rescale) diff --git a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/anchor_head.py b/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/anchor_head.py deleted file mode 100755 index 42e574662..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/anchor_head.py +++ /dev/null @@ -1,542 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -import torch.nn as nn -from mmcv.runner import force_fp32 - -from mmdet.core import (anchor_inside_flags, build_assigner, build_bbox_coder, - build_prior_generator, build_sampler, images_to_levels, - multi_apply, unmap) -from ..builder import HEADS, build_loss -from .base_dense_head import BaseDenseHead -from .dense_test_mixins import BBoxTestMixin - - -@HEADS.register_module() -class AnchorHead(BaseDenseHead, BBoxTestMixin): - """Anchor-based head (RPN, RetinaNet, SSD, etc.). - - Args: - num_classes (int): Number of categories excluding the background - category. - in_channels (int): Number of channels in the input feature map. - feat_channels (int): Number of hidden channels. Used in child classes. - anchor_generator (dict): Config dict for anchor generator - bbox_coder (dict): Config of bounding box coder. - reg_decoded_bbox (bool): If true, the regression loss would be - applied directly on decoded bounding boxes, converting both - the predicted boxes and regression targets to absolute - coordinates format. Default False. It should be `True` when - using `IoULoss`, `GIoULoss`, or `DIoULoss` in the bbox head. - loss_cls (dict): Config of classification loss. - loss_bbox (dict): Config of localization loss. - train_cfg (dict): Training config of anchor head. - test_cfg (dict): Testing config of anchor head. - init_cfg (dict or list[dict], optional): Initialization config dict. - """ # noqa: W605 - - def __init__(self, - num_classes, - in_channels, - feat_channels=256, - anchor_generator=dict( - type='AnchorGenerator', - scales=[8, 16, 32], - ratios=[0.5, 1.0, 2.0], - strides=[4, 8, 16, 32, 64]), - bbox_coder=dict( - type='DeltaXYWHBBoxCoder', - clip_border=True, - target_means=(.0, .0, .0, .0), - target_stds=(1.0, 1.0, 1.0, 1.0)), - reg_decoded_bbox=False, - loss_cls=dict( - type='CrossEntropyLoss', - use_sigmoid=True, - loss_weight=1.0), - loss_bbox=dict( - type='SmoothL1Loss', beta=1.0 / 9.0, loss_weight=1.0), - train_cfg=None, - test_cfg=None, - init_cfg=dict(type='Normal', layer='Conv2d', std=0.01)): - super(AnchorHead, self).__init__(init_cfg) - self.in_channels = in_channels - self.num_classes = num_classes - self.feat_channels = feat_channels - self.use_sigmoid_cls = loss_cls.get('use_sigmoid', False) - if self.use_sigmoid_cls: - self.cls_out_channels = num_classes - else: - self.cls_out_channels = num_classes + 1 - - if self.cls_out_channels <= 0: - raise ValueError(f'num_classes={num_classes} is too small') - self.reg_decoded_bbox = reg_decoded_bbox - - self.bbox_coder = build_bbox_coder(bbox_coder) - self.loss_cls = build_loss(loss_cls) - self.loss_bbox = build_loss(loss_bbox) - self.train_cfg = train_cfg - self.test_cfg = test_cfg - if self.train_cfg: - self.assigner = build_assigner(self.train_cfg.assigner) - if hasattr(self.train_cfg, - 'sampler') and self.train_cfg.sampler.type.split( - '.')[-1] != 'PseudoSampler': - self.sampling = True - sampler_cfg = self.train_cfg.sampler - # avoid BC-breaking - if loss_cls['type'] in [ - 'FocalLoss', 'GHMC', 'QualityFocalLoss' - ]: - warnings.warn( - 'DeprecationWarning: Determining whether to sampling' - 'by loss type is deprecated, please delete sampler in' - 'your config when using `FocalLoss`, `GHMC`, ' - '`QualityFocalLoss` or other FocalLoss variant.') - self.sampling = False - sampler_cfg = dict(type='PseudoSampler') - else: - self.sampling = False - sampler_cfg = dict(type='PseudoSampler') - self.sampler = build_sampler(sampler_cfg, context=self) - self.fp16_enabled = False - - self.prior_generator = build_prior_generator(anchor_generator) - - # Usually the numbers of anchors for each level are the same - # except SSD detectors. So it is an int in the most dense - # heads but a list of int in SSDHead - self.num_base_priors = self.prior_generator.num_base_priors[0] - self._init_layers() - - @property - def num_anchors(self): - warnings.warn('DeprecationWarning: `num_anchors` is deprecated, ' - 'for consistency or also use ' - '`num_base_priors` instead') - return self.prior_generator.num_base_priors[0] - - @property - def anchor_generator(self): - warnings.warn('DeprecationWarning: anchor_generator is deprecated, ' - 'please use "prior_generator" instead') - return self.prior_generator - - def _init_layers(self): - """Initialize layers of the head.""" - self.conv_cls = nn.Conv2d(self.in_channels, - self.num_base_priors * self.cls_out_channels, - 1) - self.conv_reg = nn.Conv2d(self.in_channels, self.num_base_priors * 4, - 1) - - def forward_single(self, x): - """Forward feature of a single scale level. - - Args: - x (Tensor): Features of a single scale level. - - Returns: - tuple: - cls_score (Tensor): Cls scores for a single scale level \ - the channels number is num_base_priors * num_classes. - bbox_pred (Tensor): Box energies / deltas for a single scale \ - level, the channels number is num_base_priors * 4. - """ - cls_score = self.conv_cls(x) - bbox_pred = self.conv_reg(x) - return cls_score, bbox_pred - - def forward(self, feats): - """Forward features from the upstream network. - - Args: - feats (tuple[Tensor]): Features from the upstream network, each is - a 4D-tensor. - - Returns: - tuple: A tuple of classification scores and bbox prediction. - - - cls_scores (list[Tensor]): Classification scores for all \ - scale levels, each is a 4D-tensor, the channels number \ - is num_base_priors * num_classes. - - bbox_preds (list[Tensor]): Box energies / deltas for all \ - scale levels, each is a 4D-tensor, the channels number \ - is num_base_priors * 4. - """ - return multi_apply(self.forward_single, feats) - - def get_anchors(self, featmap_sizes, img_metas, device='cuda'): - """Get anchors according to feature map sizes. - - Args: - featmap_sizes (list[tuple]): Multi-level feature map sizes. - img_metas (list[dict]): Image meta info. - device (torch.device | str): Device for returned tensors - - Returns: - tuple: - anchor_list (list[Tensor]): Anchors of each image. - valid_flag_list (list[Tensor]): Valid flags of each image. - """ - num_imgs = len(img_metas) - - # since feature map sizes of all images are the same, we only compute - # anchors for one time - multi_level_anchors = self.prior_generator.grid_priors( - featmap_sizes, device=device) - anchor_list = [multi_level_anchors for _ in range(num_imgs)] - - # for each image, we compute valid flags of multi level anchors - valid_flag_list = [] - for img_id, img_meta in enumerate(img_metas): - multi_level_flags = self.prior_generator.valid_flags( - featmap_sizes, img_meta['pad_shape'], device) - valid_flag_list.append(multi_level_flags) - - return anchor_list, valid_flag_list - - def _get_targets_single(self, - flat_anchors, - valid_flags, - gt_bboxes, - gt_bboxes_ignore, - gt_labels, - img_meta, - label_channels=1, - unmap_outputs=True): - """Compute regression and classification targets for anchors in a - single image. - - Args: - flat_anchors (Tensor): Multi-level anchors of the image, which are - concatenated into a single tensor of shape (num_anchors ,4) - valid_flags (Tensor): Multi level valid flags of the image, - which are concatenated into a single tensor of - shape (num_anchors,). - gt_bboxes (Tensor): Ground truth bboxes of the image, - shape (num_gts, 4). - gt_bboxes_ignore (Tensor): Ground truth bboxes to be - ignored, shape (num_ignored_gts, 4). - img_meta (dict): Meta info of the image. - gt_labels (Tensor): Ground truth labels of each box, - shape (num_gts,). - label_channels (int): Channel of label. - unmap_outputs (bool): Whether to map outputs back to the original - set of anchors. - - Returns: - tuple: - labels_list (list[Tensor]): Labels of each level - label_weights_list (list[Tensor]): Label weights of each level - bbox_targets_list (list[Tensor]): BBox targets of each level - bbox_weights_list (list[Tensor]): BBox weights of each level - num_total_pos (int): Number of positive samples in all images - num_total_neg (int): Number of negative samples in all images - """ - inside_flags = anchor_inside_flags(flat_anchors, valid_flags, - img_meta['img_shape'][:2], - self.train_cfg.allowed_border) - if not inside_flags.any(): - return (None, ) * 7 - # assign gt and sample anchors - anchors = flat_anchors[inside_flags, :] - - assign_result = self.assigner.assign( - anchors, gt_bboxes, gt_bboxes_ignore, - None if self.sampling else gt_labels) - sampling_result = self.sampler.sample(assign_result, anchors, - gt_bboxes) - - num_valid_anchors = anchors.shape[0] - bbox_targets = torch.zeros_like(anchors) - bbox_weights = torch.zeros_like(anchors) - labels = anchors.new_full((num_valid_anchors, ), - self.num_classes, - dtype=torch.long) - label_weights = anchors.new_zeros(num_valid_anchors, dtype=torch.float) - - pos_inds = sampling_result.pos_inds - neg_inds = sampling_result.neg_inds - if len(pos_inds) > 0: - if not self.reg_decoded_bbox: - pos_bbox_targets = self.bbox_coder.encode( - sampling_result.pos_bboxes, sampling_result.pos_gt_bboxes) - else: - pos_bbox_targets = sampling_result.pos_gt_bboxes - bbox_targets[pos_inds, :] = pos_bbox_targets - bbox_weights[pos_inds, :] = 1.0 - if gt_labels is None: - # Only rpn gives gt_labels as None - # Foreground is the first class since v2.5.0 - labels[pos_inds] = 0 - else: - labels[pos_inds] = gt_labels[ - sampling_result.pos_assigned_gt_inds] - if self.train_cfg.pos_weight <= 0: - label_weights[pos_inds] = 1.0 - else: - label_weights[pos_inds] = self.train_cfg.pos_weight - if len(neg_inds) > 0: - label_weights[neg_inds] = 1.0 - - # map up to original set of anchors - if unmap_outputs: - num_total_anchors = flat_anchors.size(0) - labels = unmap( - labels, num_total_anchors, inside_flags, - fill=self.num_classes) # fill bg label - label_weights = unmap(label_weights, num_total_anchors, - inside_flags) - bbox_targets = unmap(bbox_targets, num_total_anchors, inside_flags) - bbox_weights = unmap(bbox_weights, num_total_anchors, inside_flags) - - return (labels, label_weights, bbox_targets, bbox_weights, pos_inds, - neg_inds, sampling_result) - - def get_targets(self, - anchor_list, - valid_flag_list, - gt_bboxes_list, - img_metas, - gt_bboxes_ignore_list=None, - gt_labels_list=None, - label_channels=1, - unmap_outputs=True, - return_sampling_results=False): - """Compute regression and classification targets for anchors in - multiple images. - - Args: - anchor_list (list[list[Tensor]]): Multi level anchors of each - image. The outer list indicates images, and the inner list - corresponds to feature levels of the image. Each element of - the inner list is a tensor of shape (num_anchors, 4). - valid_flag_list (list[list[Tensor]]): Multi level valid flags of - each image. The outer list indicates images, and the inner list - corresponds to feature levels of the image. Each element of - the inner list is a tensor of shape (num_anchors, ) - gt_bboxes_list (list[Tensor]): Ground truth bboxes of each image. - img_metas (list[dict]): Meta info of each image. - gt_bboxes_ignore_list (list[Tensor]): Ground truth bboxes to be - ignored. - gt_labels_list (list[Tensor]): Ground truth labels of each box. - label_channels (int): Channel of label. - unmap_outputs (bool): Whether to map outputs back to the original - set of anchors. - - Returns: - tuple: Usually returns a tuple containing learning targets. - - - labels_list (list[Tensor]): Labels of each level. - - label_weights_list (list[Tensor]): Label weights of each - level. - - bbox_targets_list (list[Tensor]): BBox targets of each level. - - bbox_weights_list (list[Tensor]): BBox weights of each level. - - num_total_pos (int): Number of positive samples in all - images. - - num_total_neg (int): Number of negative samples in all - images. - - additional_returns: This function enables user-defined returns from - `self._get_targets_single`. These returns are currently refined - to properties at each feature map (i.e. having HxW dimension). - The results will be concatenated after the end - """ - num_imgs = len(img_metas) - assert len(anchor_list) == len(valid_flag_list) == num_imgs - - # anchor number of multi levels - num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]] - # concat all level anchors to a single tensor - concat_anchor_list = [] - concat_valid_flag_list = [] - for i in range(num_imgs): - assert len(anchor_list[i]) == len(valid_flag_list[i]) - concat_anchor_list.append(torch.cat(anchor_list[i])) - concat_valid_flag_list.append(torch.cat(valid_flag_list[i])) - - # compute targets for each image - if gt_bboxes_ignore_list is None: - gt_bboxes_ignore_list = [None for _ in range(num_imgs)] - if gt_labels_list is None: - gt_labels_list = [None for _ in range(num_imgs)] - results = multi_apply( - self._get_targets_single, - concat_anchor_list, - concat_valid_flag_list, - gt_bboxes_list, - gt_bboxes_ignore_list, - gt_labels_list, - img_metas, - label_channels=label_channels, - unmap_outputs=unmap_outputs) - (all_labels, all_label_weights, all_bbox_targets, all_bbox_weights, - pos_inds_list, neg_inds_list, sampling_results_list) = results[:7] - rest_results = list(results[7:]) # user-added return values - # no valid anchors - if any([labels is None for labels in all_labels]): - return None - # sampled anchors of all images - num_total_pos = sum([max(inds.numel(), 1) for inds in pos_inds_list]) - num_total_neg = sum([max(inds.numel(), 1) for inds in neg_inds_list]) - # split targets to a list w.r.t. multiple levels - labels_list = images_to_levels(all_labels, num_level_anchors) - label_weights_list = images_to_levels(all_label_weights, - num_level_anchors) - bbox_targets_list = images_to_levels(all_bbox_targets, - num_level_anchors) - bbox_weights_list = images_to_levels(all_bbox_weights, - num_level_anchors) - res = (labels_list, label_weights_list, bbox_targets_list, - bbox_weights_list, num_total_pos, num_total_neg) - if return_sampling_results: - res = res + (sampling_results_list, ) - for i, r in enumerate(rest_results): # user-added return values - rest_results[i] = images_to_levels(r, num_level_anchors) - - return res + tuple(rest_results) - - def loss_single(self, cls_score, bbox_pred, anchors, labels, label_weights, - bbox_targets, bbox_weights, num_total_samples): - """Compute loss of a single scale level. - - Args: - cls_score (Tensor): Box scores for each scale level - Has shape (N, num_anchors * num_classes, H, W). - bbox_pred (Tensor): Box energies / deltas for each scale - level with shape (N, num_anchors * 4, H, W). - anchors (Tensor): Box reference for each scale level with shape - (N, num_total_anchors, 4). - labels (Tensor): Labels of each anchors with shape - (N, num_total_anchors). - label_weights (Tensor): Label weights of each anchor with shape - (N, num_total_anchors) - bbox_targets (Tensor): BBox regression targets of each anchor - weight shape (N, num_total_anchors, 4). - bbox_weights (Tensor): BBox regression loss weights of each anchor - with shape (N, num_total_anchors, 4). - num_total_samples (int): If sampling, num total samples equal to - the number of total anchors; Otherwise, it is the number of - positive anchors. - - Returns: - dict[str, Tensor]: A dictionary of loss components. - """ - # classification loss - labels = labels.reshape(-1) - label_weights = label_weights.reshape(-1) - cls_score = cls_score.permute(0, 2, 3, - 1).reshape(-1, self.cls_out_channels) - loss_cls = self.loss_cls( - cls_score, labels, label_weights, avg_factor=num_total_samples) - # regression loss - bbox_targets = bbox_targets.reshape(-1, 4) - bbox_weights = bbox_weights.reshape(-1, 4) - bbox_pred = bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4) - if self.reg_decoded_bbox: - # When the regression loss (e.g. `IouLoss`, `GIouLoss`) - # is applied directly on the decoded bounding boxes, it - # decodes the already encoded coordinates to absolute format. - anchors = anchors.reshape(-1, 4) - bbox_pred = self.bbox_coder.decode(anchors, bbox_pred) - loss_bbox = self.loss_bbox( - bbox_pred, - bbox_targets, - bbox_weights, - avg_factor=num_total_samples) - return loss_cls, loss_bbox - - @force_fp32(apply_to=('cls_scores', 'bbox_preds')) - def loss(self, - cls_scores, - bbox_preds, - gt_bboxes, - gt_labels, - img_metas, - gt_bboxes_ignore=None): - """Compute losses of the head. - - Args: - cls_scores (list[Tensor]): Box scores for each scale level - Has shape (N, num_anchors * num_classes, H, W) - bbox_preds (list[Tensor]): Box energies / deltas for each scale - level with shape (N, num_anchors * 4, H, W) - gt_bboxes (list[Tensor]): Ground truth bboxes for each image with - shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format. - gt_labels (list[Tensor]): class indices corresponding to each box - img_metas (list[dict]): Meta information of each image, e.g., - image size, scaling factor, etc. - gt_bboxes_ignore (None | list[Tensor]): specify which bounding - boxes can be ignored when computing the loss. Default: None - - Returns: - dict[str, Tensor]: A dictionary of loss components. - """ - featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores] - assert len(featmap_sizes) == self.prior_generator.num_levels - - device = cls_scores[0].device - - anchor_list, valid_flag_list = self.get_anchors( - featmap_sizes, img_metas, device=device) - label_channels = self.cls_out_channels if self.use_sigmoid_cls else 1 - cls_reg_targets = self.get_targets( - anchor_list, - valid_flag_list, - gt_bboxes, - img_metas, - gt_bboxes_ignore_list=gt_bboxes_ignore, - gt_labels_list=gt_labels, - label_channels=label_channels) - if cls_reg_targets is None: - return None - (labels_list, label_weights_list, bbox_targets_list, bbox_weights_list, - num_total_pos, num_total_neg) = cls_reg_targets - num_total_samples = ( - num_total_pos + num_total_neg if self.sampling else num_total_pos) - - # anchor number of multi levels - num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]] - # concat all level anchors and flags to a single tensor - concat_anchor_list = [] - for i in range(len(anchor_list)): - concat_anchor_list.append(torch.cat(anchor_list[i])) - all_anchor_list = images_to_levels(concat_anchor_list, - num_level_anchors) - - losses_cls, losses_bbox = multi_apply( - self.loss_single, - cls_scores, - bbox_preds, - all_anchor_list, - labels_list, - label_weights_list, - bbox_targets_list, - bbox_weights_list, - num_total_samples=num_total_samples) - return dict(loss_cls=losses_cls, loss_bbox=losses_bbox) - - def aug_test(self, feats, img_metas, rescale=False): - """Test function with test time augmentation. - - Args: - feats (list[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains features for all images in the batch. - img_metas (list[list[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. each dict has image information. - rescale (bool, optional): Whether to rescale the results. - Defaults to False. - - Returns: - list[tuple[Tensor, Tensor]]: Each item in result_list is 2-tuple. - The first item is ``bboxes`` with shape (n, 5), where - 5 represent (tl_x, tl_y, br_x, br_y, score). - The shape of the second tensor in the tuple is ``labels`` - with shape (n,), The length of list should always be 1. - """ - return self.aug_test_bboxes(feats, img_metas, rescale=rescale) diff --git a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/atss_head.py b/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/atss_head.py deleted file mode 100755 index b9916dc04..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/atss_head.py +++ /dev/null @@ -1,501 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, Scale -from mmcv.runner import force_fp32 - -from mmdet.core import (anchor_inside_flags, build_assigner, build_sampler, - images_to_levels, multi_apply, reduce_mean, unmap) -from ..builder import HEADS, build_loss -from .anchor_head import AnchorHead - - -@HEADS.register_module() -class ATSSHead(AnchorHead): - """Bridging the Gap Between Anchor-based and Anchor-free Detection via - Adaptive Training Sample Selection. - - ATSS head structure is similar with FCOS, however ATSS use anchor boxes - and assign label by Adaptive Training Sample Selection instead max-iou. - - https://arxiv.org/abs/1912.02424 - """ - - def __init__(self, - num_classes, - in_channels, - pred_kernel_size=3, - stacked_convs=4, - conv_cfg=None, - norm_cfg=dict(type='GN', num_groups=32, requires_grad=True), - reg_decoded_bbox=True, - loss_centerness=dict( - type='CrossEntropyLoss', - use_sigmoid=True, - loss_weight=1.0), - init_cfg=dict( - type='Normal', - layer='Conv2d', - std=0.01, - override=dict( - type='Normal', - name='atss_cls', - std=0.01, - bias_prob=0.01)), - **kwargs): - self.pred_kernel_size = pred_kernel_size - self.stacked_convs = stacked_convs - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - super(ATSSHead, self).__init__( - num_classes, - in_channels, - reg_decoded_bbox=reg_decoded_bbox, - init_cfg=init_cfg, - **kwargs) - - self.sampling = False - if self.train_cfg: - self.assigner = build_assigner(self.train_cfg.assigner) - # SSD sampling=False so use PseudoSampler - sampler_cfg = dict(type='PseudoSampler') - self.sampler = build_sampler(sampler_cfg, context=self) - self.loss_centerness = build_loss(loss_centerness) - - def _init_layers(self): - """Initialize layers of the head.""" - self.relu = nn.ReLU(inplace=True) - self.cls_convs = nn.ModuleList() - self.reg_convs = nn.ModuleList() - for i in range(self.stacked_convs): - chn = self.in_channels if i == 0 else self.feat_channels - self.cls_convs.append( - ConvModule( - chn, - self.feat_channels, - 3, - stride=1, - padding=1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg)) - self.reg_convs.append( - ConvModule( - chn, - self.feat_channels, - 3, - stride=1, - padding=1, - conv_cfg=self.conv_cfg, - norm_cfg=self.norm_cfg)) - pred_pad_size = self.pred_kernel_size // 2 - self.atss_cls = nn.Conv2d( - self.feat_channels, - self.num_anchors * self.cls_out_channels, - self.pred_kernel_size, - padding=pred_pad_size) - self.atss_reg = nn.Conv2d( - self.feat_channels, - self.num_base_priors * 4, - self.pred_kernel_size, - padding=pred_pad_size) - self.atss_centerness = nn.Conv2d( - self.feat_channels, - self.num_base_priors * 1, - self.pred_kernel_size, - padding=pred_pad_size) - self.scales = nn.ModuleList( - [Scale(1.0) for _ in self.prior_generator.strides]) - - def forward(self, feats): - """Forward features from the upstream network. - - Args: - feats (tuple[Tensor]): Features from the upstream network, each is - a 4D-tensor. - - Returns: - tuple: Usually a tuple of classification scores and bbox prediction - cls_scores (list[Tensor]): Classification scores for all scale - levels, each is a 4D-tensor, the channels number is - num_anchors * num_classes. - bbox_preds (list[Tensor]): Box energies / deltas for all scale - levels, each is a 4D-tensor, the channels number is - num_anchors * 4. - """ - return multi_apply(self.forward_single, feats, self.scales) - - def forward_single(self, x, scale): - """Forward feature of a single scale level. - - Args: - x (Tensor): Features of a single scale level. - scale (:obj: `mmcv.cnn.Scale`): Learnable scale module to resize - the bbox prediction. - - Returns: - tuple: - cls_score (Tensor): Cls scores for a single scale level - the channels number is num_anchors * num_classes. - bbox_pred (Tensor): Box energies / deltas for a single scale - level, the channels number is num_anchors * 4. - centerness (Tensor): Centerness for a single scale level, the - channel number is (N, num_anchors * 1, H, W). - """ - cls_feat = x - reg_feat = x - for cls_conv in self.cls_convs: - cls_feat = cls_conv(cls_feat) - for reg_conv in self.reg_convs: - reg_feat = reg_conv(reg_feat) - cls_score = self.atss_cls(cls_feat) - # we just follow atss, not apply exp in bbox_pred - bbox_pred = scale(self.atss_reg(reg_feat)).float() - centerness = self.atss_centerness(reg_feat) - return cls_score, bbox_pred, centerness - - def loss_single(self, anchors, cls_score, bbox_pred, centerness, labels, - label_weights, bbox_targets, num_total_samples): - """Compute loss of a single scale level. - - Args: - cls_score (Tensor): Box scores for each scale level - Has shape (N, num_anchors * num_classes, H, W). - bbox_pred (Tensor): Box energies / deltas for each scale - level with shape (N, num_anchors * 4, H, W). - anchors (Tensor): Box reference for each scale level with shape - (N, num_total_anchors, 4). - labels (Tensor): Labels of each anchors with shape - (N, num_total_anchors). - label_weights (Tensor): Label weights of each anchor with shape - (N, num_total_anchors) - bbox_targets (Tensor): BBox regression targets of each anchor - weight shape (N, num_total_anchors, 4). - num_total_samples (int): Number os positive samples that is - reduced over all GPUs. - - Returns: - dict[str, Tensor]: A dictionary of loss components. - """ - - anchors = anchors.reshape(-1, 4) - cls_score = cls_score.permute(0, 2, 3, 1).reshape( - -1, self.cls_out_channels).contiguous() - bbox_pred = bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4) - centerness = centerness.permute(0, 2, 3, 1).reshape(-1) - bbox_targets = bbox_targets.reshape(-1, 4) - labels = labels.reshape(-1) - label_weights = label_weights.reshape(-1) - - # classification loss - loss_cls = self.loss_cls( - cls_score, labels, label_weights, avg_factor=num_total_samples) - - # FG cat_id: [0, num_classes -1], BG cat_id: num_classes - bg_class_ind = self.num_classes - pos_inds = ((labels >= 0) - & (labels < bg_class_ind)).nonzero().squeeze(1) - - if len(pos_inds) > 0: - pos_bbox_targets = bbox_targets[pos_inds] - pos_bbox_pred = bbox_pred[pos_inds] - pos_anchors = anchors[pos_inds] - pos_centerness = centerness[pos_inds] - - centerness_targets = self.centerness_target( - pos_anchors, pos_bbox_targets) - pos_decode_bbox_pred = self.bbox_coder.decode( - pos_anchors, pos_bbox_pred) - - # regression loss - loss_bbox = self.loss_bbox( - pos_decode_bbox_pred, - pos_bbox_targets, - weight=centerness_targets, - avg_factor=1.0) - - # centerness loss - loss_centerness = self.loss_centerness( - pos_centerness, - centerness_targets, - avg_factor=num_total_samples) - - else: - loss_bbox = bbox_pred.sum() * 0 - loss_centerness = centerness.sum() * 0 - centerness_targets = bbox_targets.new_tensor(0.) - - return loss_cls, loss_bbox, loss_centerness, centerness_targets.sum() - - @force_fp32(apply_to=('cls_scores', 'bbox_preds', 'centernesses')) - def loss(self, - cls_scores, - bbox_preds, - centernesses, - gt_bboxes, - gt_labels, - img_metas, - gt_bboxes_ignore=None): - """Compute losses of the head. - - Args: - cls_scores (list[Tensor]): Box scores for each scale level - Has shape (N, num_anchors * num_classes, H, W) - bbox_preds (list[Tensor]): Box energies / deltas for each scale - level with shape (N, num_anchors * 4, H, W) - centernesses (list[Tensor]): Centerness for each scale - level with shape (N, num_anchors * 1, H, W) - gt_bboxes (list[Tensor]): Ground truth bboxes for each image with - shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format. - gt_labels (list[Tensor]): class indices corresponding to each box - img_metas (list[dict]): Meta information of each image, e.g., - image size, scaling factor, etc. - gt_bboxes_ignore (list[Tensor] | None): specify which bounding - boxes can be ignored when computing the loss. - - Returns: - dict[str, Tensor]: A dictionary of loss components. - """ - featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores] - assert len(featmap_sizes) == self.prior_generator.num_levels - - device = cls_scores[0].device - anchor_list, valid_flag_list = self.get_anchors( - featmap_sizes, img_metas, device=device) - label_channels = self.cls_out_channels if self.use_sigmoid_cls else 1 - - cls_reg_targets = self.get_targets( - anchor_list, - valid_flag_list, - gt_bboxes, - img_metas, - gt_bboxes_ignore_list=gt_bboxes_ignore, - gt_labels_list=gt_labels, - label_channels=label_channels) - if cls_reg_targets is None: - return None - - (anchor_list, labels_list, label_weights_list, bbox_targets_list, - bbox_weights_list, num_total_pos, num_total_neg) = cls_reg_targets - - num_total_samples = reduce_mean( - torch.tensor(num_total_pos, dtype=torch.float, - device=device)).item() - num_total_samples = max(num_total_samples, 1.0) - - losses_cls, losses_bbox, loss_centerness,\ - bbox_avg_factor = multi_apply( - self.loss_single, - anchor_list, - cls_scores, - bbox_preds, - centernesses, - labels_list, - label_weights_list, - bbox_targets_list, - num_total_samples=num_total_samples) - - bbox_avg_factor = sum(bbox_avg_factor) - bbox_avg_factor = reduce_mean(bbox_avg_factor).clamp_(min=1).item() - losses_bbox = list(map(lambda x: x / bbox_avg_factor, losses_bbox)) - return dict( - loss_cls=losses_cls, - loss_bbox=losses_bbox, - loss_centerness=loss_centerness) - - def centerness_target(self, anchors, gts): - # only calculate pos centerness targets, otherwise there may be nan - anchors_cx = (anchors[:, 2] + anchors[:, 0]) / 2 - anchors_cy = (anchors[:, 3] + anchors[:, 1]) / 2 - l_ = anchors_cx - gts[:, 0] - t_ = anchors_cy - gts[:, 1] - r_ = gts[:, 2] - anchors_cx - b_ = gts[:, 3] - anchors_cy - - left_right = torch.stack([l_, r_], dim=1) - top_bottom = torch.stack([t_, b_], dim=1) - centerness = torch.sqrt( - (left_right.min(dim=-1)[0] / left_right.max(dim=-1)[0]) * - (top_bottom.min(dim=-1)[0] / top_bottom.max(dim=-1)[0])) - assert not torch.isnan(centerness).any() - return centerness - - def get_targets(self, - anchor_list, - valid_flag_list, - gt_bboxes_list, - img_metas, - gt_bboxes_ignore_list=None, - gt_labels_list=None, - label_channels=1, - unmap_outputs=True): - """Get targets for ATSS head. - - This method is almost the same as `AnchorHead.get_targets()`. Besides - returning the targets as the parent method does, it also returns the - anchors as the first element of the returned tuple. - """ - num_imgs = len(img_metas) - assert len(anchor_list) == len(valid_flag_list) == num_imgs - - # anchor number of multi levels - num_level_anchors = [anchors.size(0) for anchors in anchor_list[0]] - num_level_anchors_list = [num_level_anchors] * num_imgs - - # concat all level anchors and flags to a single tensor - for i in range(num_imgs): - assert len(anchor_list[i]) == len(valid_flag_list[i]) - anchor_list[i] = torch.cat(anchor_list[i]) - valid_flag_list[i] = torch.cat(valid_flag_list[i]) - - # compute targets for each image - if gt_bboxes_ignore_list is None: - gt_bboxes_ignore_list = [None for _ in range(num_imgs)] - if gt_labels_list is None: - gt_labels_list = [None for _ in range(num_imgs)] - (all_anchors, all_labels, all_label_weights, all_bbox_targets, - all_bbox_weights, pos_inds_list, neg_inds_list) = multi_apply( - self._get_target_single, - anchor_list, - valid_flag_list, - num_level_anchors_list, - gt_bboxes_list, - gt_bboxes_ignore_list, - gt_labels_list, - img_metas, - label_channels=label_channels, - unmap_outputs=unmap_outputs) - # no valid anchors - if any([labels is None for labels in all_labels]): - return None - # sampled anchors of all images - num_total_pos = sum([max(inds.numel(), 1) for inds in pos_inds_list]) - num_total_neg = sum([max(inds.numel(), 1) for inds in neg_inds_list]) - # split targets to a list w.r.t. multiple levels - anchors_list = images_to_levels(all_anchors, num_level_anchors) - labels_list = images_to_levels(all_labels, num_level_anchors) - label_weights_list = images_to_levels(all_label_weights, - num_level_anchors) - bbox_targets_list = images_to_levels(all_bbox_targets, - num_level_anchors) - bbox_weights_list = images_to_levels(all_bbox_weights, - num_level_anchors) - return (anchors_list, labels_list, label_weights_list, - bbox_targets_list, bbox_weights_list, num_total_pos, - num_total_neg) - - def _get_target_single(self, - flat_anchors, - valid_flags, - num_level_anchors, - gt_bboxes, - gt_bboxes_ignore, - gt_labels, - img_meta, - label_channels=1, - unmap_outputs=True): - """Compute regression, classification targets for anchors in a single - image. - - Args: - flat_anchors (Tensor): Multi-level anchors of the image, which are - concatenated into a single tensor of shape (num_anchors ,4) - valid_flags (Tensor): Multi level valid flags of the image, - which are concatenated into a single tensor of - shape (num_anchors,). - num_level_anchors Tensor): Number of anchors of each scale level. - gt_bboxes (Tensor): Ground truth bboxes of the image, - shape (num_gts, 4). - gt_bboxes_ignore (Tensor): Ground truth bboxes to be - ignored, shape (num_ignored_gts, 4). - gt_labels (Tensor): Ground truth labels of each box, - shape (num_gts,). - img_meta (dict): Meta info of the image. - label_channels (int): Channel of label. - unmap_outputs (bool): Whether to map outputs back to the original - set of anchors. - - Returns: - tuple: N is the number of total anchors in the image. - labels (Tensor): Labels of all anchors in the image with shape - (N,). - label_weights (Tensor): Label weights of all anchor in the - image with shape (N,). - bbox_targets (Tensor): BBox targets of all anchors in the - image with shape (N, 4). - bbox_weights (Tensor): BBox weights of all anchors in the - image with shape (N, 4) - pos_inds (Tensor): Indices of positive anchor with shape - (num_pos,). - neg_inds (Tensor): Indices of negative anchor with shape - (num_neg,). - """ - inside_flags = anchor_inside_flags(flat_anchors, valid_flags, - img_meta['img_shape'][:2], - self.train_cfg.allowed_border) - if not inside_flags.any(): - return (None, ) * 7 - # assign gt and sample anchors - anchors = flat_anchors[inside_flags, :] - - num_level_anchors_inside = self.get_num_level_anchors_inside( - num_level_anchors, inside_flags) - assign_result = self.assigner.assign(anchors, num_level_anchors_inside, - gt_bboxes, gt_bboxes_ignore, - gt_labels) - - sampling_result = self.sampler.sample(assign_result, anchors, - gt_bboxes) - - num_valid_anchors = anchors.shape[0] - bbox_targets = torch.zeros_like(anchors) - bbox_weights = torch.zeros_like(anchors) - labels = anchors.new_full((num_valid_anchors, ), - self.num_classes, - dtype=torch.long) - label_weights = anchors.new_zeros(num_valid_anchors, dtype=torch.float) - - pos_inds = sampling_result.pos_inds - neg_inds = sampling_result.neg_inds - if len(pos_inds) > 0: - if self.reg_decoded_bbox: - pos_bbox_targets = sampling_result.pos_gt_bboxes - else: - pos_bbox_targets = self.bbox_coder.encode( - sampling_result.pos_bboxes, sampling_result.pos_gt_bboxes) - - bbox_targets[pos_inds, :] = pos_bbox_targets - bbox_weights[pos_inds, :] = 1.0 - if gt_labels is None: - # Only rpn gives gt_labels as None - # Foreground is the first class since v2.5.0 - labels[pos_inds] = 0 - else: - labels[pos_inds] = gt_labels[ - sampling_result.pos_assigned_gt_inds] - if self.train_cfg.pos_weight <= 0: - label_weights[pos_inds] = 1.0 - else: - label_weights[pos_inds] = self.train_cfg.pos_weight - if len(neg_inds) > 0: - label_weights[neg_inds] = 1.0 - - # map up to original set of anchors - if unmap_outputs: - num_total_anchors = flat_anchors.size(0) - anchors = unmap(anchors, num_total_anchors, inside_flags) - labels = unmap( - labels, num_total_anchors, inside_flags, fill=self.num_classes) - label_weights = unmap(label_weights, num_total_anchors, - inside_flags) - bbox_targets = unmap(bbox_targets, num_total_anchors, inside_flags) - bbox_weights = unmap(bbox_weights, num_total_anchors, inside_flags) - - return (anchors, labels, label_weights, bbox_targets, bbox_weights, - pos_inds, neg_inds) - - def get_num_level_anchors_inside(self, num_level_anchors, inside_flags): - split_inside_flags = torch.split(inside_flags, num_level_anchors) - num_level_anchors_inside = [ - int(flags.sum()) for flags in split_inside_flags - ] - return num_level_anchors_inside diff --git a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/autoassign_head.py b/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/autoassign_head.py deleted file mode 100755 index ac1e432db..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/autoassign_head.py +++ /dev/null @@ -1,527 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import bias_init_with_prob, normal_init -from mmcv.runner import force_fp32 - -from mmdet.core import multi_apply -from mmdet.core.anchor.point_generator import MlvlPointGenerator -from mmdet.core.bbox import bbox_overlaps -from mmdet.models import HEADS -from mmdet.models.dense_heads.atss_head import reduce_mean -from mmdet.models.dense_heads.fcos_head import FCOSHead -from mmdet.models.dense_heads.paa_head import levels_to_images - -EPS = 1e-12 - - -class CenterPrior(nn.Module): - """Center Weighting module to adjust the category-specific prior - distributions. - - Args: - force_topk (bool): When no point falls into gt_bbox, forcibly - select the k points closest to the center to calculate - the center prior. Defaults to False. - topk (int): The number of points used to calculate the - center prior when no point falls in gt_bbox. Only work when - force_topk if True. Defaults to 9. - num_classes (int): The class number of dataset. Defaults to 80. - strides (tuple[int]): The stride of each input feature map. Defaults - to (8, 16, 32, 64, 128). - """ - - def __init__(self, - force_topk=False, - topk=9, - num_classes=80, - strides=(8, 16, 32, 64, 128)): - super(CenterPrior, self).__init__() - self.mean = nn.Parameter(torch.zeros(num_classes, 2)) - self.sigma = nn.Parameter(torch.ones(num_classes, 2)) - self.strides = strides - self.force_topk = force_topk - self.topk = topk - - def forward(self, anchor_points_list, gt_bboxes, labels, - inside_gt_bbox_mask): - """Get the center prior of each point on the feature map for each - instance. - - Args: - anchor_points_list (list[Tensor]): list of coordinate - of points on feature map. Each with shape - (num_points, 2). - gt_bboxes (Tensor): The gt_bboxes with shape of - (num_gt, 4). - labels (Tensor): The gt_labels with shape of (num_gt). - inside_gt_bbox_mask (Tensor): Tensor of bool type, - with shape of (num_points, num_gt), each - value is used to mark whether this point falls - within a certain gt. - - Returns: - tuple(Tensor): - - - center_prior_weights(Tensor): Float tensor with shape \ - of (num_points, num_gt). Each value represents \ - the center weighting coefficient. - - inside_gt_bbox_mask (Tensor): Tensor of bool type, \ - with shape of (num_points, num_gt), each \ - value is used to mark whether this point falls \ - within a certain gt or is the topk nearest points for \ - a specific gt_bbox. - """ - inside_gt_bbox_mask = inside_gt_bbox_mask.clone() - num_gts = len(labels) - num_points = sum([len(item) for item in anchor_points_list]) - if num_gts == 0: - return gt_bboxes.new_zeros(num_points, - num_gts), inside_gt_bbox_mask - center_prior_list = [] - for slvl_points, stride in zip(anchor_points_list, self.strides): - # slvl_points: points from single level in FPN, has shape (h*w, 2) - # single_level_points has shape (h*w, num_gt, 2) - single_level_points = slvl_points[:, None, :].expand( - (slvl_points.size(0), len(gt_bboxes), 2)) - gt_center_x = ((gt_bboxes[:, 0] + gt_bboxes[:, 2]) / 2) - gt_center_y = ((gt_bboxes[:, 1] + gt_bboxes[:, 3]) / 2) - gt_center = torch.stack((gt_center_x, gt_center_y), dim=1) - gt_center = gt_center[None] - # instance_center has shape (1, num_gt, 2) - instance_center = self.mean[labels][None] - # instance_sigma has shape (1, num_gt, 2) - instance_sigma = self.sigma[labels][None] - # distance has shape (num_points, num_gt, 2) - distance = (((single_level_points - gt_center) / float(stride) - - instance_center)**2) - center_prior = torch.exp(-distance / - (2 * instance_sigma**2)).prod(dim=-1) - center_prior_list.append(center_prior) - center_prior_weights = torch.cat(center_prior_list, dim=0) - - if self.force_topk: - gt_inds_no_points_inside = torch.nonzero( - inside_gt_bbox_mask.sum(0) == 0).reshape(-1) - if gt_inds_no_points_inside.numel(): - topk_center_index = \ - center_prior_weights[:, gt_inds_no_points_inside].topk( - self.topk, - dim=0)[1] - temp_mask = inside_gt_bbox_mask[:, gt_inds_no_points_inside] - inside_gt_bbox_mask[:, gt_inds_no_points_inside] = \ - torch.scatter(temp_mask, - dim=0, - index=topk_center_index, - src=torch.ones_like( - topk_center_index, - dtype=torch.bool)) - - center_prior_weights[~inside_gt_bbox_mask] = 0 - return center_prior_weights, inside_gt_bbox_mask - - -@HEADS.register_module() -class AutoAssignHead(FCOSHead): - """AutoAssignHead head used in AutoAssign. - - More details can be found in the `paper - `_ . - - Args: - force_topk (bool): Used in center prior initialization to - handle extremely small gt. Default is False. - topk (int): The number of points used to calculate the - center prior when no point falls in gt_bbox. Only work when - force_topk if True. Defaults to 9. - pos_loss_weight (float): The loss weight of positive loss - and with default value 0.25. - neg_loss_weight (float): The loss weight of negative loss - and with default value 0.75. - center_loss_weight (float): The loss weight of center prior - loss and with default value 0.75. - """ - - def __init__(self, - *args, - force_topk=False, - topk=9, - pos_loss_weight=0.25, - neg_loss_weight=0.75, - center_loss_weight=0.75, - **kwargs): - super().__init__(*args, conv_bias=True, **kwargs) - self.center_prior = CenterPrior( - force_topk=force_topk, - topk=topk, - num_classes=self.num_classes, - strides=self.strides) - self.pos_loss_weight = pos_loss_weight - self.neg_loss_weight = neg_loss_weight - self.center_loss_weight = center_loss_weight - self.prior_generator = MlvlPointGenerator(self.strides, offset=0) - - def init_weights(self): - """Initialize weights of the head. - - In particular, we have special initialization for classified conv's and - regression conv's bias - """ - - super(AutoAssignHead, self).init_weights() - bias_cls = bias_init_with_prob(0.02) - normal_init(self.conv_cls, std=0.01, bias=bias_cls) - normal_init(self.conv_reg, std=0.01, bias=4.0) - - def forward_single(self, x, scale, stride): - """Forward features of a single scale level. - - Args: - x (Tensor): FPN feature maps of the specified stride. - scale (:obj: `mmcv.cnn.Scale`): Learnable scale module to resize - the bbox prediction. - stride (int): The corresponding stride for feature maps, only - used to normalize the bbox prediction when self.norm_on_bbox - is True. - - Returns: - tuple: scores for each class, bbox predictions and centerness \ - predictions of input feature maps. - """ - cls_score, bbox_pred, cls_feat, reg_feat = super( - FCOSHead, self).forward_single(x) - centerness = self.conv_centerness(reg_feat) - # scale the bbox_pred of different level - # float to avoid overflow when enabling FP16 - bbox_pred = scale(bbox_pred).float() - # bbox_pred needed for gradient computation has been modified - # by F.relu(bbox_pred) when run with PyTorch 1.10. So replace - # F.relu(bbox_pred) with bbox_pred.clamp(min=0) - bbox_pred = bbox_pred.clamp(min=0) - bbox_pred *= stride - return cls_score, bbox_pred, centerness - - def get_pos_loss_single(self, cls_score, objectness, reg_loss, gt_labels, - center_prior_weights): - """Calculate the positive loss of all points in gt_bboxes. - - Args: - cls_score (Tensor): All category scores for each point on - the feature map. The shape is (num_points, num_class). - objectness (Tensor): Foreground probability of all points, - has shape (num_points, 1). - reg_loss (Tensor): The regression loss of each gt_bbox and each - prediction box, has shape of (num_points, num_gt). - gt_labels (Tensor): The zeros based gt_labels of all gt - with shape of (num_gt,). - center_prior_weights (Tensor): Float tensor with shape - of (num_points, num_gt). Each value represents - the center weighting coefficient. - - Returns: - tuple[Tensor]: - - - pos_loss (Tensor): The positive loss of all points - in the gt_bboxes. - """ - # p_loc: localization confidence - p_loc = torch.exp(-reg_loss) - # p_cls: classification confidence - p_cls = (cls_score * objectness)[:, gt_labels] - # p_pos: joint confidence indicator - p_pos = p_cls * p_loc - - # 3 is a hyper-parameter to control the contributions of high and - # low confidence locations towards positive losses. - confidence_weight = torch.exp(p_pos * 3) - p_pos_weight = (confidence_weight * center_prior_weights) / ( - (confidence_weight * center_prior_weights).sum( - 0, keepdim=True)).clamp(min=EPS) - reweighted_p_pos = (p_pos * p_pos_weight).sum(0) - pos_loss = F.binary_cross_entropy( - reweighted_p_pos, - torch.ones_like(reweighted_p_pos), - reduction='none') - pos_loss = pos_loss.sum() * self.pos_loss_weight - return pos_loss, - - def get_neg_loss_single(self, cls_score, objectness, gt_labels, ious, - inside_gt_bbox_mask): - """Calculate the negative loss of all points in feature map. - - Args: - cls_score (Tensor): All category scores for each point on - the feature map. The shape is (num_points, num_class). - objectness (Tensor): Foreground probability of all points - and is shape of (num_points, 1). - gt_labels (Tensor): The zeros based label of all gt with shape of - (num_gt). - ious (Tensor): Float tensor with shape of (num_points, num_gt). - Each value represent the iou of pred_bbox and gt_bboxes. - inside_gt_bbox_mask (Tensor): Tensor of bool type, - with shape of (num_points, num_gt), each - value is used to mark whether this point falls - within a certain gt. - - Returns: - tuple[Tensor]: - - - neg_loss (Tensor): The negative loss of all points - in the feature map. - """ - num_gts = len(gt_labels) - joint_conf = (cls_score * objectness) - p_neg_weight = torch.ones_like(joint_conf) - if num_gts > 0: - # the order of dinmension would affect the value of - # p_neg_weight, we strictly follow the original - # implementation. - inside_gt_bbox_mask = inside_gt_bbox_mask.permute(1, 0) - ious = ious.permute(1, 0) - - foreground_idxs = torch.nonzero(inside_gt_bbox_mask, as_tuple=True) - temp_weight = (1 / (1 - ious[foreground_idxs]).clamp_(EPS)) - - def normalize(x): - return (x - x.min() + EPS) / (x.max() - x.min() + EPS) - - for instance_idx in range(num_gts): - idxs = foreground_idxs[0] == instance_idx - if idxs.any(): - temp_weight[idxs] = normalize(temp_weight[idxs]) - - p_neg_weight[foreground_idxs[1], - gt_labels[foreground_idxs[0]]] = 1 - temp_weight - - logits = (joint_conf * p_neg_weight) - neg_loss = ( - logits**2 * F.binary_cross_entropy( - logits, torch.zeros_like(logits), reduction='none')) - neg_loss = neg_loss.sum() * self.neg_loss_weight - return neg_loss, - - @force_fp32(apply_to=('cls_scores', 'bbox_preds', 'objectnesses')) - def loss(self, - cls_scores, - bbox_preds, - objectnesses, - gt_bboxes, - gt_labels, - img_metas, - gt_bboxes_ignore=None): - """Compute loss of the head. - - Args: - cls_scores (list[Tensor]): Box scores for each scale level, - each is a 4D-tensor, the channel number is - num_points * num_classes. - bbox_preds (list[Tensor]): Box energies / deltas for each scale - level, each is a 4D-tensor, the channel number is - num_points * 4. - objectnesses (list[Tensor]): objectness for each scale level, each - is a 4D-tensor, the channel number is num_points * 1. - gt_bboxes (list[Tensor]): Ground truth bboxes for each image with - shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format. - gt_labels (list[Tensor]): class indices corresponding to each box - img_metas (list[dict]): Meta information of each image, e.g., - image size, scaling factor, etc. - gt_bboxes_ignore (None | list[Tensor]): specify which bounding - boxes can be ignored when computing the loss. - - Returns: - dict[str, Tensor]: A dictionary of loss components. - """ - - assert len(cls_scores) == len(bbox_preds) == len(objectnesses) - all_num_gt = sum([len(item) for item in gt_bboxes]) - featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores] - all_level_points = self.prior_generator.grid_priors( - featmap_sizes, - dtype=bbox_preds[0].dtype, - device=bbox_preds[0].device) - inside_gt_bbox_mask_list, bbox_targets_list = self.get_targets( - all_level_points, gt_bboxes) - - center_prior_weight_list = [] - temp_inside_gt_bbox_mask_list = [] - for gt_bboxe, gt_label, inside_gt_bbox_mask in zip( - gt_bboxes, gt_labels, inside_gt_bbox_mask_list): - center_prior_weight, inside_gt_bbox_mask = \ - self.center_prior(all_level_points, gt_bboxe, gt_label, - inside_gt_bbox_mask) - center_prior_weight_list.append(center_prior_weight) - temp_inside_gt_bbox_mask_list.append(inside_gt_bbox_mask) - inside_gt_bbox_mask_list = temp_inside_gt_bbox_mask_list - mlvl_points = torch.cat(all_level_points, dim=0) - bbox_preds = levels_to_images(bbox_preds) - cls_scores = levels_to_images(cls_scores) - objectnesses = levels_to_images(objectnesses) - - reg_loss_list = [] - ious_list = [] - num_points = len(mlvl_points) - - for bbox_pred, encoded_targets, inside_gt_bbox_mask in zip( - bbox_preds, bbox_targets_list, inside_gt_bbox_mask_list): - temp_num_gt = encoded_targets.size(1) - expand_mlvl_points = mlvl_points[:, None, :].expand( - num_points, temp_num_gt, 2).reshape(-1, 2) - encoded_targets = encoded_targets.reshape(-1, 4) - expand_bbox_pred = bbox_pred[:, None, :].expand( - num_points, temp_num_gt, 4).reshape(-1, 4) - decoded_bbox_preds = self.bbox_coder.decode( - expand_mlvl_points, expand_bbox_pred) - decoded_target_preds = self.bbox_coder.decode( - expand_mlvl_points, encoded_targets) - with torch.no_grad(): - ious = bbox_overlaps( - decoded_bbox_preds, decoded_target_preds, is_aligned=True) - ious = ious.reshape(num_points, temp_num_gt) - if temp_num_gt: - ious = ious.max( - dim=-1, keepdim=True).values.repeat(1, temp_num_gt) - else: - ious = ious.new_zeros(num_points, temp_num_gt) - ious[~inside_gt_bbox_mask] = 0 - ious_list.append(ious) - loss_bbox = self.loss_bbox( - decoded_bbox_preds, - decoded_target_preds, - weight=None, - reduction_override='none') - reg_loss_list.append(loss_bbox.reshape(num_points, temp_num_gt)) - - cls_scores = [item.sigmoid() for item in cls_scores] - objectnesses = [item.sigmoid() for item in objectnesses] - pos_loss_list, = multi_apply(self.get_pos_loss_single, cls_scores, - objectnesses, reg_loss_list, gt_labels, - center_prior_weight_list) - pos_avg_factor = reduce_mean( - bbox_pred.new_tensor(all_num_gt)).clamp_(min=1) - pos_loss = sum(pos_loss_list) / pos_avg_factor - - neg_loss_list, = multi_apply(self.get_neg_loss_single, cls_scores, - objectnesses, gt_labels, ious_list, - inside_gt_bbox_mask_list) - neg_avg_factor = sum(item.data.sum() - for item in center_prior_weight_list) - neg_avg_factor = reduce_mean(neg_avg_factor).clamp_(min=1) - neg_loss = sum(neg_loss_list) / neg_avg_factor - - center_loss = [] - for i in range(len(img_metas)): - - if inside_gt_bbox_mask_list[i].any(): - center_loss.append( - len(gt_bboxes[i]) / - center_prior_weight_list[i].sum().clamp_(min=EPS)) - # when width or height of gt_bbox is smaller than stride of p3 - else: - center_loss.append(center_prior_weight_list[i].sum() * 0) - - center_loss = torch.stack(center_loss).mean() * self.center_loss_weight - - # avoid dead lock in DDP - if all_num_gt == 0: - pos_loss = bbox_preds[0].sum() * 0 - dummy_center_prior_loss = self.center_prior.mean.sum( - ) * 0 + self.center_prior.sigma.sum() * 0 - center_loss = objectnesses[0].sum() * 0 + dummy_center_prior_loss - - loss = dict( - loss_pos=pos_loss, loss_neg=neg_loss, loss_center=center_loss) - - return loss - - def get_targets(self, points, gt_bboxes_list): - """Compute regression targets and each point inside or outside gt_bbox - in multiple images. - - Args: - points (list[Tensor]): Points of all fpn level, each has shape - (num_points, 2). - gt_bboxes_list (list[Tensor]): Ground truth bboxes of each image, - each has shape (num_gt, 4). - - Returns: - tuple(list[Tensor]): - - - inside_gt_bbox_mask_list (list[Tensor]): Each - Tensor is with bool type and shape of - (num_points, num_gt), each value - is used to mark whether this point falls - within a certain gt. - - concat_lvl_bbox_targets (list[Tensor]): BBox - targets of each level. Each tensor has shape - (num_points, num_gt, 4). - """ - - concat_points = torch.cat(points, dim=0) - # the number of points per img, per lvl - inside_gt_bbox_mask_list, bbox_targets_list = multi_apply( - self._get_target_single, gt_bboxes_list, points=concat_points) - return inside_gt_bbox_mask_list, bbox_targets_list - - def _get_target_single(self, gt_bboxes, points): - """Compute regression targets and each point inside or outside gt_bbox - for a single image. - - Args: - gt_bboxes (Tensor): gt_bbox of single image, has shape - (num_gt, 4). - points (Tensor): Points of all fpn level, has shape - (num_points, 2). - - Returns: - tuple[Tensor]: Containing the following Tensors: - - - inside_gt_bbox_mask (Tensor): Bool tensor with shape - (num_points, num_gt), each value is used to mark - whether this point falls within a certain gt. - - bbox_targets (Tensor): BBox targets of each points with - each gt_bboxes, has shape (num_points, num_gt, 4). - """ - num_points = points.size(0) - num_gts = gt_bboxes.size(0) - gt_bboxes = gt_bboxes[None].expand(num_points, num_gts, 4) - xs, ys = points[:, 0], points[:, 1] - xs = xs[:, None] - ys = ys[:, None] - left = xs - gt_bboxes[..., 0] - right = gt_bboxes[..., 2] - xs - top = ys - gt_bboxes[..., 1] - bottom = gt_bboxes[..., 3] - ys - bbox_targets = torch.stack((left, top, right, bottom), -1) - if num_gts: - inside_gt_bbox_mask = bbox_targets.min(-1)[0] > 0 - else: - inside_gt_bbox_mask = bbox_targets.new_zeros((num_points, num_gts), - dtype=torch.bool) - - return inside_gt_bbox_mask, bbox_targets - - def _get_points_single(self, - featmap_size, - stride, - dtype, - device, - flatten=False): - """Almost the same as the implementation in fcos, we remove half stride - offset to align with the original implementation. - - This function will be deprecated soon. - """ - warnings.warn( - '`_get_points_single` in `AutoAssignHead` will be ' - 'deprecated soon, we support a multi level point generator now' - 'you can get points of a single level feature map ' - 'with `self.prior_generator.single_level_grid_priors` ') - y, x = super(FCOSHead, - self)._get_points_single(featmap_size, stride, dtype, - device) - points = torch.stack((x.reshape(-1) * stride, y.reshape(-1) * stride), - dim=-1) - return points diff --git a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/base_dense_head.py b/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/base_dense_head.py deleted file mode 100755 index c6ac7d749..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/base_dense_head.py +++ /dev/null @@ -1,526 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - -import torch -from mmcv.cnn.utils.weight_init import constant_init -from mmcv.ops import batched_nms -from mmcv.runner import BaseModule, force_fp32 - -from mmdet.core.utils import filter_scores_and_topk, select_single_mlvl - - -class BaseDenseHead(BaseModule, metaclass=ABCMeta): - """Base class for DenseHeads.""" - - def __init__(self, init_cfg=None): - super(BaseDenseHead, self).__init__(init_cfg) - - def init_weights(self): - super(BaseDenseHead, self).init_weights() - # avoid init_cfg overwrite the initialization of `conv_offset` - for m in self.modules(): - # DeformConv2dPack, ModulatedDeformConv2dPack - if hasattr(m, 'conv_offset'): - constant_init(m.conv_offset, 0) - - @abstractmethod - def loss(self, **kwargs): - """Compute losses of the head.""" - pass - - @force_fp32(apply_to=('cls_scores', 'bbox_preds')) - def get_bboxes(self, - cls_scores, - bbox_preds, - score_factors=None, - img_metas=None, - cfg=None, - rescale=False, - with_nms=True, - **kwargs): - """Transform network outputs of a batch into bbox results. - - Note: When score_factors is not None, the cls_scores are - usually multiplied by it then obtain the real score used in NMS, - such as CenterNess in FCOS, IoU branch in ATSS. - - Args: - cls_scores (list[Tensor]): Classification scores for all - scale levels, each is a 4D-tensor, has shape - (batch_size, num_priors * num_classes, H, W). - bbox_preds (list[Tensor]): Box energies / deltas for all - scale levels, each is a 4D-tensor, has shape - (batch_size, num_priors * 4, H, W). - score_factors (list[Tensor], Optional): Score factor for - all scale level, each is a 4D-tensor, has shape - (batch_size, num_priors * 1, H, W). Default None. - img_metas (list[dict], Optional): Image meta info. Default None. - cfg (mmcv.Config, Optional): Test / postprocessing configuration, - if None, test_cfg would be used. Default None. - rescale (bool): If True, return boxes in original image space. - Default False. - with_nms (bool): If True, do nms before return boxes. - Default True. - - Returns: - list[list[Tensor, Tensor]]: Each item in result_list is 2-tuple. - The first item is an (n, 5) tensor, where the first 4 columns - are bounding box positions (tl_x, tl_y, br_x, br_y) and the - 5-th column is a score between 0 and 1. The second item is a - (n,) tensor where each item is the predicted class label of - the corresponding box. - """ - assert len(cls_scores) == len(bbox_preds) - - if score_factors is None: - # e.g. Retina, FreeAnchor, Foveabox, etc. - with_score_factors = False - else: - # e.g. FCOS, PAA, ATSS, AutoAssign, etc. - with_score_factors = True - assert len(cls_scores) == len(score_factors) - - num_levels = len(cls_scores) - - featmap_sizes = [cls_scores[i].shape[-2:] for i in range(num_levels)] - mlvl_priors = self.prior_generator.grid_priors( - featmap_sizes, - dtype=cls_scores[0].dtype, - device=cls_scores[0].device) - - result_list = [] - - for img_id in range(len(img_metas)): - img_meta = img_metas[img_id] - cls_score_list = select_single_mlvl(cls_scores, img_id) - bbox_pred_list = select_single_mlvl(bbox_preds, img_id) - if with_score_factors: - score_factor_list = select_single_mlvl(score_factors, img_id) - else: - score_factor_list = [None for _ in range(num_levels)] - - results = self._get_bboxes_single(cls_score_list, bbox_pred_list, - score_factor_list, mlvl_priors, - img_meta, cfg, rescale, with_nms, - **kwargs) - result_list.append(results) - return result_list - - def _get_bboxes_single(self, - cls_score_list, - bbox_pred_list, - score_factor_list, - mlvl_priors, - img_meta, - cfg, - rescale=False, - with_nms=True, - **kwargs): - """Transform outputs of a single image into bbox predictions. - - Args: - cls_score_list (list[Tensor]): Box scores from all scale - levels of a single image, each item has shape - (num_priors * num_classes, H, W). - bbox_pred_list (list[Tensor]): Box energies / deltas from - all scale levels of a single image, each item has shape - (num_priors * 4, H, W). - score_factor_list (list[Tensor]): Score factor from all scale - levels of a single image, each item has shape - (num_priors * 1, H, W). - mlvl_priors (list[Tensor]): Each element in the list is - the priors of a single level in feature pyramid. In all - anchor-based methods, it has shape (num_priors, 4). In - all anchor-free methods, it has shape (num_priors, 2) - when `with_stride=True`, otherwise it still has shape - (num_priors, 4). - img_meta (dict): Image meta info. - cfg (mmcv.Config): Test / postprocessing configuration, - if None, test_cfg would be used. - rescale (bool): If True, return boxes in original image space. - Default: False. - with_nms (bool): If True, do nms before return boxes. - Default: True. - - Returns: - tuple[Tensor]: Results of detected bboxes and labels. If with_nms - is False and mlvl_score_factor is None, return mlvl_bboxes and - mlvl_scores, else return mlvl_bboxes, mlvl_scores and - mlvl_score_factor. Usually with_nms is False is used for aug - test. If with_nms is True, then return the following format - - - det_bboxes (Tensor): Predicted bboxes with shape \ - [num_bboxes, 5], where the first 4 columns are bounding \ - box positions (tl_x, tl_y, br_x, br_y) and the 5-th \ - column are scores between 0 and 1. - - det_labels (Tensor): Predicted labels of the corresponding \ - box with shape [num_bboxes]. - """ - if score_factor_list[0] is None: - # e.g. Retina, FreeAnchor, etc. - with_score_factors = False - else: - # e.g. FCOS, PAA, ATSS, etc. - with_score_factors = True - - cfg = self.test_cfg if cfg is None else cfg - img_shape = img_meta['img_shape'] - nms_pre = cfg.get('nms_pre', -1) - - mlvl_bboxes = [] - mlvl_scores = [] - mlvl_labels = [] - if with_score_factors: - mlvl_score_factors = [] - else: - mlvl_score_factors = None - for level_idx, (cls_score, bbox_pred, score_factor, priors) in \ - enumerate(zip(cls_score_list, bbox_pred_list, - score_factor_list, mlvl_priors)): - - assert cls_score.size()[-2:] == bbox_pred.size()[-2:] - - bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, 4) - if with_score_factors: - score_factor = score_factor.permute(1, 2, - 0).reshape(-1).sigmoid() - cls_score = cls_score.permute(1, 2, - 0).reshape(-1, self.cls_out_channels) - if self.use_sigmoid_cls: - scores = cls_score.sigmoid() - else: - # remind that we set FG labels to [0, num_class-1] - # since mmdet v2.0 - # BG cat_id: num_class - scores = cls_score.softmax(-1)[:, :-1] - - # After https://github.com/open-mmlab/mmdetection/pull/6268/, - # this operation keeps fewer bboxes under the same `nms_pre`. - # There is no difference in performance for most models. If you - # find a slight drop in performance, you can set a larger - # `nms_pre` than before. - results = filter_scores_and_topk( - scores, cfg.score_thr, nms_pre, - dict(bbox_pred=bbox_pred, priors=priors)) - scores, labels, keep_idxs, filtered_results = results - - bbox_pred = filtered_results['bbox_pred'] - priors = filtered_results['priors'] - - if with_score_factors: - score_factor = score_factor[keep_idxs] - - bboxes = self.bbox_coder.decode( - priors, bbox_pred, max_shape=img_shape) - - mlvl_bboxes.append(bboxes) - mlvl_scores.append(scores) - mlvl_labels.append(labels) - if with_score_factors: - mlvl_score_factors.append(score_factor) - - return self._bbox_post_process(mlvl_scores, mlvl_labels, mlvl_bboxes, - img_meta['scale_factor'], cfg, rescale, - with_nms, mlvl_score_factors, **kwargs) - - def _bbox_post_process(self, - mlvl_scores, - mlvl_labels, - mlvl_bboxes, - scale_factor, - cfg, - rescale=False, - with_nms=True, - mlvl_score_factors=None, - **kwargs): - """bbox post-processing method. - - The boxes would be rescaled to the original image scale and do - the nms operation. Usually `with_nms` is False is used for aug test. - - Args: - mlvl_scores (list[Tensor]): Box scores from all scale - levels of a single image, each item has shape - (num_bboxes, ). - mlvl_labels (list[Tensor]): Box class labels from all scale - levels of a single image, each item has shape - (num_bboxes, ). - mlvl_bboxes (list[Tensor]): Decoded bboxes from all scale - levels of a single image, each item has shape (num_bboxes, 4). - scale_factor (ndarray, optional): Scale factor of the image arange - as (w_scale, h_scale, w_scale, h_scale). - cfg (mmcv.Config): Test / postprocessing configuration, - if None, test_cfg would be used. - rescale (bool): If True, return boxes in original image space. - Default: False. - with_nms (bool): If True, do nms before return boxes. - Default: True. - mlvl_score_factors (list[Tensor], optional): Score factor from - all scale levels of a single image, each item has shape - (num_bboxes, ). Default: None. - - Returns: - tuple[Tensor]: Results of detected bboxes and labels. If with_nms - is False and mlvl_score_factor is None, return mlvl_bboxes and - mlvl_scores, else return mlvl_bboxes, mlvl_scores and - mlvl_score_factor. Usually with_nms is False is used for aug - test. If with_nms is True, then return the following format - - - det_bboxes (Tensor): Predicted bboxes with shape \ - [num_bboxes, 5], where the first 4 columns are bounding \ - box positions (tl_x, tl_y, br_x, br_y) and the 5-th \ - column are scores between 0 and 1. - - det_labels (Tensor): Predicted labels of the corresponding \ - box with shape [num_bboxes]. - """ - assert len(mlvl_scores) == len(mlvl_bboxes) == len(mlvl_labels) - - mlvl_bboxes = torch.cat(mlvl_bboxes) - if rescale: - mlvl_bboxes /= mlvl_bboxes.new_tensor(scale_factor) - mlvl_scores = torch.cat(mlvl_scores) - mlvl_labels = torch.cat(mlvl_labels) - - if mlvl_score_factors is not None: - # TODO: Add sqrt operation in order to be consistent with - # the paper. - mlvl_score_factors = torch.cat(mlvl_score_factors) - mlvl_scores = mlvl_scores * mlvl_score_factors - - if with_nms: - if mlvl_bboxes.numel() == 0: - det_bboxes = torch.cat([mlvl_bboxes, mlvl_scores[:, None]], -1) - return det_bboxes, mlvl_labels - - det_bboxes, keep_idxs = batched_nms(mlvl_bboxes, mlvl_scores, - mlvl_labels, cfg.nms) - det_bboxes = det_bboxes[:cfg.max_per_img] - det_labels = mlvl_labels[keep_idxs][:cfg.max_per_img] - return det_bboxes, det_labels - else: - return mlvl_bboxes, mlvl_scores, mlvl_labels - - def forward_train(self, - x, - img_metas, - gt_bboxes, - gt_labels=None, - gt_bboxes_ignore=None, - proposal_cfg=None, - **kwargs): - """ - Args: - x (list[Tensor]): Features from FPN. - img_metas (list[dict]): Meta information of each image, e.g., - image size, scaling factor, etc. - gt_bboxes (Tensor): Ground truth bboxes of the image, - shape (num_gts, 4). - gt_labels (Tensor): Ground truth labels of each box, - shape (num_gts,). - gt_bboxes_ignore (Tensor): Ground truth bboxes to be - ignored, shape (num_ignored_gts, 4). - proposal_cfg (mmcv.Config): Test / postprocessing configuration, - if None, test_cfg would be used - - Returns: - tuple: - losses: (dict[str, Tensor]): A dictionary of loss components. - proposal_list (list[Tensor]): Proposals of each image. - """ - outs = self(x) - if gt_labels is None: - loss_inputs = outs + (gt_bboxes, img_metas) - else: - loss_inputs = outs + (gt_bboxes, gt_labels, img_metas) - losses = self.loss(*loss_inputs, gt_bboxes_ignore=gt_bboxes_ignore) - if proposal_cfg is None: - return losses - else: - proposal_list = self.get_bboxes( - *outs, img_metas=img_metas, cfg=proposal_cfg) - return losses, proposal_list - - def simple_test(self, feats, img_metas, rescale=False): - """Test function without test-time augmentation. - - Args: - feats (tuple[torch.Tensor]): Multi-level features from the - upstream network, each is a 4D-tensor. - img_metas (list[dict]): List of image information. - rescale (bool, optional): Whether to rescale the results. - Defaults to False. - - Returns: - list[tuple[Tensor, Tensor]]: Each item in result_list is 2-tuple. - The first item is ``bboxes`` with shape (n, 5), - where 5 represent (tl_x, tl_y, br_x, br_y, score). - The shape of the second tensor in the tuple is ``labels`` - with shape (n, ). - """ - return self.simple_test_bboxes(feats, img_metas, rescale=rescale) - - @force_fp32(apply_to=('cls_scores', 'bbox_preds')) - def onnx_export(self, - cls_scores, - bbox_preds, - score_factors=None, - img_metas=None, - with_nms=True): - """Transform network output for a batch into bbox predictions. - - Args: - cls_scores (list[Tensor]): Box scores for each scale level - with shape (N, num_points * num_classes, H, W). - bbox_preds (list[Tensor]): Box energies / deltas for each scale - level with shape (N, num_points * 4, H, W). - score_factors (list[Tensor]): score_factors for each s - cale level with shape (N, num_points * 1, H, W). - Default: None. - img_metas (list[dict]): Meta information of each image, e.g., - image size, scaling factor, etc. Default: None. - with_nms (bool): Whether apply nms to the bboxes. Default: True. - - Returns: - tuple[Tensor, Tensor] | list[tuple]: When `with_nms` is True, - it is tuple[Tensor, Tensor], first tensor bboxes with shape - [N, num_det, 5], 5 arrange as (x1, y1, x2, y2, score) - and second element is class labels of shape [N, num_det]. - When `with_nms` is False, first tensor is bboxes with - shape [N, num_det, 4], second tensor is raw score has - shape [N, num_det, num_classes]. - """ - assert len(cls_scores) == len(bbox_preds) - - num_levels = len(cls_scores) - - featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores] - mlvl_priors = self.prior_generator.grid_priors( - featmap_sizes, - dtype=bbox_preds[0].dtype, - device=bbox_preds[0].device) - - mlvl_cls_scores = [cls_scores[i].detach() for i in range(num_levels)] - mlvl_bbox_preds = [bbox_preds[i].detach() for i in range(num_levels)] - - assert len( - img_metas - ) == 1, 'Only support one input image while in exporting to ONNX' - img_shape = img_metas[0]['img_shape_for_onnx'] - - cfg = self.test_cfg - assert len(cls_scores) == len(bbox_preds) == len(mlvl_priors) - device = cls_scores[0].device - batch_size = cls_scores[0].shape[0] - # convert to tensor to keep tracing - nms_pre_tensor = torch.tensor( - cfg.get('nms_pre', -1), device=device, dtype=torch.long) - - # e.g. Retina, FreeAnchor, etc. - if score_factors is None: - with_score_factors = False - mlvl_score_factor = [None for _ in range(num_levels)] - else: - # e.g. FCOS, PAA, ATSS, etc. - with_score_factors = True - mlvl_score_factor = [ - score_factors[i].detach() for i in range(num_levels) - ] - mlvl_score_factors = [] - - mlvl_batch_bboxes = [] - mlvl_scores = [] - - for cls_score, bbox_pred, score_factors, priors in zip( - mlvl_cls_scores, mlvl_bbox_preds, mlvl_score_factor, - mlvl_priors): - assert cls_score.size()[-2:] == bbox_pred.size()[-2:] - - scores = cls_score.permute(0, 2, 3, - 1).reshape(batch_size, -1, - self.cls_out_channels) - if self.use_sigmoid_cls: - scores = scores.sigmoid() - nms_pre_score = scores - else: - scores = scores.softmax(-1) - nms_pre_score = scores - - if with_score_factors: - score_factors = score_factors.permute(0, 2, 3, 1).reshape( - batch_size, -1).sigmoid() - bbox_pred = bbox_pred.permute(0, 2, 3, - 1).reshape(batch_size, -1, 4) - priors = priors.expand(batch_size, -1, priors.size(-1)) - # Get top-k predictions - from mmdet.core.export import get_k_for_topk - nms_pre = get_k_for_topk(nms_pre_tensor, bbox_pred.shape[1]) - if nms_pre > 0: - - if with_score_factors: - nms_pre_score = (nms_pre_score * score_factors[..., None]) - else: - nms_pre_score = nms_pre_score - - # Get maximum scores for foreground classes. - if self.use_sigmoid_cls: - max_scores, _ = nms_pre_score.max(-1) - else: - # remind that we set FG labels to [0, num_class-1] - # since mmdet v2.0 - # BG cat_id: num_class - max_scores, _ = nms_pre_score[..., :-1].max(-1) - _, topk_inds = max_scores.topk(nms_pre) - - batch_inds = torch.arange( - batch_size, device=bbox_pred.device).view( - -1, 1).expand_as(topk_inds).long() - # Avoid onnx2tensorrt issue in https://github.com/NVIDIA/TensorRT/issues/1134 # noqa: E501 - transformed_inds = bbox_pred.shape[1] * batch_inds + topk_inds - priors = priors.reshape( - -1, priors.size(-1))[transformed_inds, :].reshape( - batch_size, -1, priors.size(-1)) - bbox_pred = bbox_pred.reshape(-1, - 4)[transformed_inds, :].reshape( - batch_size, -1, 4) - scores = scores.reshape( - -1, self.cls_out_channels)[transformed_inds, :].reshape( - batch_size, -1, self.cls_out_channels) - if with_score_factors: - score_factors = score_factors.reshape( - -1, 1)[transformed_inds].reshape(batch_size, -1) - - bboxes = self.bbox_coder.decode( - priors, bbox_pred, max_shape=img_shape) - - mlvl_batch_bboxes.append(bboxes) - mlvl_scores.append(scores) - if with_score_factors: - mlvl_score_factors.append(score_factors) - - batch_bboxes = torch.cat(mlvl_batch_bboxes, dim=1) - batch_scores = torch.cat(mlvl_scores, dim=1) - if with_score_factors: - batch_score_factors = torch.cat(mlvl_score_factors, dim=1) - - # Replace multiclass_nms with ONNX::NonMaxSuppression in deployment - - from mmdet.core.export import add_dummy_nms_for_onnx - - if not self.use_sigmoid_cls: - batch_scores = batch_scores[..., :self.num_classes] - - if with_score_factors: - batch_scores = batch_scores * (batch_score_factors.unsqueeze(2)) - - if with_nms: - max_output_boxes_per_class = cfg.nms.get( - 'max_output_boxes_per_class', 200) - iou_threshold = cfg.nms.get('iou_threshold', 0.5) - score_threshold = cfg.score_thr - nms_pre = cfg.get('deploy_nms_pre', -1) - return add_dummy_nms_for_onnx(batch_bboxes, batch_scores, - max_output_boxes_per_class, - iou_threshold, score_threshold, - nms_pre, cfg.max_per_img) - else: - return batch_bboxes, batch_scores diff --git a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/dense_test_mixins.py b/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/dense_test_mixins.py deleted file mode 100755 index 9a2829bcb..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/dense_test_mixins.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from inspect import signature - -import torch -from mmcv.ops import batched_nms - -from mmdet.core import bbox_mapping_back, merge_aug_proposals - -if sys.version_info >= (3, 7): - from mmdet.utils.contextmanagers import completed - - -class BBoxTestMixin(object): - """Mixin class for testing det bboxes via DenseHead.""" - - def simple_test_bboxes(self, feats, img_metas, rescale=False): - """Test det bboxes without test-time augmentation, can be applied in - DenseHead except for ``RPNHead`` and its variants, e.g., ``GARPNHead``, - etc. - - Args: - feats (tuple[torch.Tensor]): Multi-level features from the - upstream network, each is a 4D-tensor. - img_metas (list[dict]): List of image information. - rescale (bool, optional): Whether to rescale the results. - Defaults to False. - - Returns: - list[tuple[Tensor, Tensor]]: Each item in result_list is 2-tuple. - The first item is ``bboxes`` with shape (n, 5), - where 5 represent (tl_x, tl_y, br_x, br_y, score). - The shape of the second tensor in the tuple is ``labels`` - with shape (n,) - """ - outs = self.forward(feats) - results_list = self.get_bboxes( - *outs, img_metas=img_metas, rescale=rescale) - return results_list - - def aug_test_bboxes(self, feats, img_metas, rescale=False): - """Test det bboxes with test time augmentation, can be applied in - DenseHead except for ``RPNHead`` and its variants, e.g., ``GARPNHead``, - etc. - - Args: - feats (list[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains features for all images in the batch. - img_metas (list[list[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. each dict has image information. - rescale (bool, optional): Whether to rescale the results. - Defaults to False. - - Returns: - list[tuple[Tensor, Tensor]]: Each item in result_list is 2-tuple. - The first item is ``bboxes`` with shape (n, 5), - where 5 represent (tl_x, tl_y, br_x, br_y, score). - The shape of the second tensor in the tuple is ``labels`` - with shape (n,). The length of list should always be 1. - """ - # check with_nms argument - gb_sig = signature(self.get_bboxes) - gb_args = [p.name for p in gb_sig.parameters.values()] - gbs_sig = signature(self._get_bboxes_single) - gbs_args = [p.name for p in gbs_sig.parameters.values()] - assert ('with_nms' in gb_args) and ('with_nms' in gbs_args), \ - f'{self.__class__.__name__}' \ - ' does not support test-time augmentation' - - aug_bboxes = [] - aug_scores = [] - aug_labels = [] - for x, img_meta in zip(feats, img_metas): - # only one image in the batch - outs = self.forward(x) - bbox_outputs = self.get_bboxes( - *outs, - img_metas=img_meta, - cfg=self.test_cfg, - rescale=False, - with_nms=False)[0] - aug_bboxes.append(bbox_outputs[0]) - aug_scores.append(bbox_outputs[1]) - if len(bbox_outputs) >= 3: - aug_labels.append(bbox_outputs[2]) - - # after merging, bboxes will be rescaled to the original image size - merged_bboxes, merged_scores = self.merge_aug_bboxes( - aug_bboxes, aug_scores, img_metas) - merged_labels = torch.cat(aug_labels, dim=0) if aug_labels else None - - if merged_bboxes.numel() == 0: - det_bboxes = torch.cat([merged_bboxes, merged_scores[:, None]], -1) - return [ - (det_bboxes, merged_labels), - ] - - det_bboxes, keep_idxs = batched_nms(merged_bboxes, merged_scores, - merged_labels, self.test_cfg.nms) - det_bboxes = det_bboxes[:self.test_cfg.max_per_img] - det_labels = merged_labels[keep_idxs][:self.test_cfg.max_per_img] - - if rescale: - _det_bboxes = det_bboxes - else: - _det_bboxes = det_bboxes.clone() - _det_bboxes[:, :4] *= det_bboxes.new_tensor( - img_metas[0][0]['scale_factor']) - - return [ - (_det_bboxes, det_labels), - ] - - def simple_test_rpn(self, x, img_metas): - """Test without augmentation, only for ``RPNHead`` and its variants, - e.g., ``GARPNHead``, etc. - - Args: - x (tuple[Tensor]): Features from the upstream network, each is - a 4D-tensor. - img_metas (list[dict]): Meta info of each image. - - Returns: - list[Tensor]: Proposals of each image, each item has shape (n, 5), - where 5 represent (tl_x, tl_y, br_x, br_y, score). - """ - rpn_outs = self(x) - proposal_list = self.get_bboxes(*rpn_outs, img_metas=img_metas) - return proposal_list - - def aug_test_rpn(self, feats, img_metas): - """Test with augmentation for only for ``RPNHead`` and its variants, - e.g., ``GARPNHead``, etc. - - Args: - feats (tuple[Tensor]): Features from the upstream network, each is - a 4D-tensor. - img_metas (list[dict]): Meta info of each image. - - Returns: - list[Tensor]: Proposals of each image, each item has shape (n, 5), - where 5 represent (tl_x, tl_y, br_x, br_y, score). - """ - samples_per_gpu = len(img_metas[0]) - aug_proposals = [[] for _ in range(samples_per_gpu)] - for x, img_meta in zip(feats, img_metas): - proposal_list = self.simple_test_rpn(x, img_meta) - for i, proposals in enumerate(proposal_list): - aug_proposals[i].append(proposals) - # reorganize the order of 'img_metas' to match the dimensions - # of 'aug_proposals' - aug_img_metas = [] - for i in range(samples_per_gpu): - aug_img_meta = [] - for j in range(len(img_metas)): - aug_img_meta.append(img_metas[j][i]) - aug_img_metas.append(aug_img_meta) - # after merging, proposals will be rescaled to the original image size - merged_proposals = [ - merge_aug_proposals(proposals, aug_img_meta, self.test_cfg) - for proposals, aug_img_meta in zip(aug_proposals, aug_img_metas) - ] - return merged_proposals - - if sys.version_info >= (3, 7): - - async def async_simple_test_rpn(self, x, img_metas): - sleep_interval = self.test_cfg.pop('async_sleep_interval', 0.025) - async with completed( - __name__, 'rpn_head_forward', - sleep_interval=sleep_interval): - rpn_outs = self(x) - - proposal_list = self.get_bboxes(*rpn_outs, img_metas=img_metas) - return proposal_list - - def merge_aug_bboxes(self, aug_bboxes, aug_scores, img_metas): - """Merge augmented detection bboxes and scores. - - Args: - aug_bboxes (list[Tensor]): shape (n, 4*#class) - aug_scores (list[Tensor] or None): shape (n, #class) - img_shapes (list[Tensor]): shape (3, ). - - Returns: - tuple[Tensor]: ``bboxes`` with shape (n,4), where - 4 represent (tl_x, tl_y, br_x, br_y) - and ``scores`` with shape (n,). - """ - recovered_bboxes = [] - for bboxes, img_info in zip(aug_bboxes, img_metas): - img_shape = img_info[0]['img_shape'] - scale_factor = img_info[0]['scale_factor'] - flip = img_info[0]['flip'] - flip_direction = img_info[0]['flip_direction'] - bboxes = bbox_mapping_back(bboxes, img_shape, scale_factor, flip, - flip_direction) - recovered_bboxes.append(bboxes) - bboxes = torch.cat(recovered_bboxes, dim=0) - if aug_scores is None: - return bboxes - else: - scores = torch.cat(aug_scores, dim=0) - return bboxes, scores diff --git a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/fcos_head.py b/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/fcos_head.py deleted file mode 100755 index 3a30e499d..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/fcos_head.py +++ /dev/null @@ -1,455 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -import torch.nn as nn -from mmcv.cnn import Scale -from mmcv.runner import force_fp32 - -from mmdet.core import multi_apply, reduce_mean -from ..builder import HEADS, build_loss -from .anchor_free_head import AnchorFreeHead - -INF = 1e8 - - -@HEADS.register_module() -class FCOSHead(AnchorFreeHead): - """Anchor-free head used in `FCOS `_. - - The FCOS head does not use anchor boxes. Instead bounding boxes are - predicted at each pixel and a centerness measure is used to suppress - low-quality predictions. - Here norm_on_bbox, centerness_on_reg, dcn_on_last_conv are training - tricks used in official repo, which will bring remarkable mAP gains - of up to 4.9. Please see https://github.com/tianzhi0549/FCOS for - more detail. - - Args: - num_classes (int): Number of categories excluding the background - category. - in_channels (int): Number of channels in the input feature map. - strides (list[int] | list[tuple[int, int]]): Strides of points - in multiple feature levels. Default: (4, 8, 16, 32, 64). - regress_ranges (tuple[tuple[int, int]]): Regress range of multiple - level points. - center_sampling (bool): If true, use center sampling. Default: False. - center_sample_radius (float): Radius of center sampling. Default: 1.5. - norm_on_bbox (bool): If true, normalize the regression targets - with FPN strides. Default: False. - centerness_on_reg (bool): If true, position centerness on the - regress branch. Please refer to https://github.com/tianzhi0549/FCOS/issues/89#issuecomment-516877042. - Default: False. - conv_bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias of conv will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - loss_cls (dict): Config of classification loss. - loss_bbox (dict): Config of localization loss. - loss_centerness (dict): Config of centerness loss. - norm_cfg (dict): dictionary to construct and config norm layer. - Default: norm_cfg=dict(type='GN', num_groups=32, requires_grad=True). - init_cfg (dict or list[dict], optional): Initialization config dict. - - Example: - >>> self = FCOSHead(11, 7) - >>> feats = [torch.rand(1, 7, s, s) for s in [4, 8, 16, 32, 64]] - >>> cls_score, bbox_pred, centerness = self.forward(feats) - >>> assert len(cls_score) == len(self.scales) - """ # noqa: E501 - - def __init__(self, - num_classes, - in_channels, - regress_ranges=((-1, 64), (64, 128), (128, 256), (256, 512), - (512, INF)), - center_sampling=False, - center_sample_radius=1.5, - norm_on_bbox=False, - centerness_on_reg=False, - loss_cls=dict( - type='FocalLoss', - use_sigmoid=True, - gamma=2.0, - alpha=0.25, - loss_weight=1.0), - loss_bbox=dict(type='IoULoss', loss_weight=1.0), - loss_centerness=dict( - type='CrossEntropyLoss', - use_sigmoid=True, - loss_weight=1.0), - norm_cfg=dict(type='GN', num_groups=32, requires_grad=True), - init_cfg=dict( - type='Normal', - layer='Conv2d', - std=0.01, - override=dict( - type='Normal', - name='conv_cls', - std=0.01, - bias_prob=0.01)), - **kwargs): - self.regress_ranges = regress_ranges - self.center_sampling = center_sampling - self.center_sample_radius = center_sample_radius - self.norm_on_bbox = norm_on_bbox - self.centerness_on_reg = centerness_on_reg - super().__init__( - num_classes, - in_channels, - loss_cls=loss_cls, - loss_bbox=loss_bbox, - norm_cfg=norm_cfg, - init_cfg=init_cfg, - **kwargs) - self.loss_centerness = build_loss(loss_centerness) - - def _init_layers(self): - """Initialize layers of the head.""" - super()._init_layers() - self.conv_centerness = nn.Conv2d(self.feat_channels, 1, 3, padding=1) - self.scales = nn.ModuleList([Scale(1.0) for _ in self.strides]) - - def forward(self, feats): - """Forward features from the upstream network. - - Args: - feats (tuple[Tensor]): Features from the upstream network, each is - a 4D-tensor. - - Returns: - tuple: - cls_scores (list[Tensor]): Box scores for each scale level, \ - each is a 4D-tensor, the channel number is \ - num_points * num_classes. - bbox_preds (list[Tensor]): Box energies / deltas for each \ - scale level, each is a 4D-tensor, the channel number is \ - num_points * 4. - centernesses (list[Tensor]): centerness for each scale level, \ - each is a 4D-tensor, the channel number is num_points * 1. - """ - return multi_apply(self.forward_single, feats, self.scales, - self.strides) - - def forward_single(self, x, scale, stride): - """Forward features of a single scale level. - - Args: - x (Tensor): FPN feature maps of the specified stride. - scale (:obj: `mmcv.cnn.Scale`): Learnable scale module to resize - the bbox prediction. - stride (int): The corresponding stride for feature maps, only - used to normalize the bbox prediction when self.norm_on_bbox - is True. - - Returns: - tuple: scores for each class, bbox predictions and centerness \ - predictions of input feature maps. - """ - cls_score, bbox_pred, cls_feat, reg_feat = super().forward_single(x) - if self.centerness_on_reg: - centerness = self.conv_centerness(reg_feat) - else: - centerness = self.conv_centerness(cls_feat) - # scale the bbox_pred of different level - # float to avoid overflow when enabling FP16 - bbox_pred = scale(bbox_pred).float() - if self.norm_on_bbox: - # bbox_pred needed for gradient computation has been modified - # by F.relu(bbox_pred) when run with PyTorch 1.10. So replace - # F.relu(bbox_pred) with bbox_pred.clamp(min=0) - bbox_pred = bbox_pred.clamp(min=0) - if not self.training: - bbox_pred *= stride - else: - bbox_pred = bbox_pred.exp() - return cls_score, bbox_pred, centerness - - @force_fp32(apply_to=('cls_scores', 'bbox_preds', 'centernesses')) - def loss(self, - cls_scores, - bbox_preds, - centernesses, - gt_bboxes, - gt_labels, - img_metas, - gt_bboxes_ignore=None): - """Compute loss of the head. - - Args: - cls_scores (list[Tensor]): Box scores for each scale level, - each is a 4D-tensor, the channel number is - num_points * num_classes. - bbox_preds (list[Tensor]): Box energies / deltas for each scale - level, each is a 4D-tensor, the channel number is - num_points * 4. - centernesses (list[Tensor]): centerness for each scale level, each - is a 4D-tensor, the channel number is num_points * 1. - gt_bboxes (list[Tensor]): Ground truth bboxes for each image with - shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format. - gt_labels (list[Tensor]): class indices corresponding to each box - img_metas (list[dict]): Meta information of each image, e.g., - image size, scaling factor, etc. - gt_bboxes_ignore (None | list[Tensor]): specify which bounding - boxes can be ignored when computing the loss. - - Returns: - dict[str, Tensor]: A dictionary of loss components. - """ - assert len(cls_scores) == len(bbox_preds) == len(centernesses) - featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores] - all_level_points = self.prior_generator.grid_priors( - featmap_sizes, - dtype=bbox_preds[0].dtype, - device=bbox_preds[0].device) - labels, bbox_targets = self.get_targets(all_level_points, gt_bboxes, - gt_labels) - - num_imgs = cls_scores[0].size(0) - # flatten cls_scores, bbox_preds and centerness - flatten_cls_scores = [ - cls_score.permute(0, 2, 3, 1).reshape(-1, self.cls_out_channels) - for cls_score in cls_scores - ] - flatten_bbox_preds = [ - bbox_pred.permute(0, 2, 3, 1).reshape(-1, 4) - for bbox_pred in bbox_preds - ] - flatten_centerness = [ - centerness.permute(0, 2, 3, 1).reshape(-1) - for centerness in centernesses - ] - flatten_cls_scores = torch.cat(flatten_cls_scores) - flatten_bbox_preds = torch.cat(flatten_bbox_preds) - flatten_centerness = torch.cat(flatten_centerness) - flatten_labels = torch.cat(labels) - flatten_bbox_targets = torch.cat(bbox_targets) - # repeat points to align with bbox_preds - flatten_points = torch.cat( - [points.repeat(num_imgs, 1) for points in all_level_points]) - - # FG cat_id: [0, num_classes -1], BG cat_id: num_classes - bg_class_ind = self.num_classes - pos_inds = ((flatten_labels >= 0) - & (flatten_labels < bg_class_ind)).nonzero().reshape(-1) - num_pos = torch.tensor( - len(pos_inds), dtype=torch.float, device=bbox_preds[0].device) - num_pos = max(reduce_mean(num_pos), 1.0) - loss_cls = self.loss_cls( - flatten_cls_scores, flatten_labels, avg_factor=num_pos) - - pos_bbox_preds = flatten_bbox_preds[pos_inds] - pos_centerness = flatten_centerness[pos_inds] - pos_bbox_targets = flatten_bbox_targets[pos_inds] - pos_centerness_targets = self.centerness_target(pos_bbox_targets) - # centerness weighted iou loss - centerness_denorm = max( - reduce_mean(pos_centerness_targets.sum().detach()), 1e-6) - - if len(pos_inds) > 0: - pos_points = flatten_points[pos_inds] - pos_decoded_bbox_preds = self.bbox_coder.decode( - pos_points, pos_bbox_preds) - pos_decoded_target_preds = self.bbox_coder.decode( - pos_points, pos_bbox_targets) - loss_bbox = self.loss_bbox( - pos_decoded_bbox_preds, - pos_decoded_target_preds, - weight=pos_centerness_targets, - avg_factor=centerness_denorm) - loss_centerness = self.loss_centerness( - pos_centerness, pos_centerness_targets, avg_factor=num_pos) - else: - loss_bbox = pos_bbox_preds.sum() - loss_centerness = pos_centerness.sum() - - return dict( - loss_cls=loss_cls, - loss_bbox=loss_bbox, - loss_centerness=loss_centerness) - - def get_targets(self, points, gt_bboxes_list, gt_labels_list): - """Compute regression, classification and centerness targets for points - in multiple images. - - Args: - points (list[Tensor]): Points of each fpn level, each has shape - (num_points, 2). - gt_bboxes_list (list[Tensor]): Ground truth bboxes of each image, - each has shape (num_gt, 4). - gt_labels_list (list[Tensor]): Ground truth labels of each box, - each has shape (num_gt,). - - Returns: - tuple: - concat_lvl_labels (list[Tensor]): Labels of each level. \ - concat_lvl_bbox_targets (list[Tensor]): BBox targets of each \ - level. - """ - assert len(points) == len(self.regress_ranges) - num_levels = len(points) - # expand regress ranges to align with points - expanded_regress_ranges = [ - points[i].new_tensor(self.regress_ranges[i])[None].expand_as( - points[i]) for i in range(num_levels) - ] - # concat all levels points and regress ranges - concat_regress_ranges = torch.cat(expanded_regress_ranges, dim=0) - concat_points = torch.cat(points, dim=0) - - # the number of points per img, per lvl - num_points = [center.size(0) for center in points] - - # get labels and bbox_targets of each image - labels_list, bbox_targets_list = multi_apply( - self._get_target_single, - gt_bboxes_list, - gt_labels_list, - points=concat_points, - regress_ranges=concat_regress_ranges, - num_points_per_lvl=num_points) - - # split to per img, per level - labels_list = [labels.split(num_points, 0) for labels in labels_list] - bbox_targets_list = [ - bbox_targets.split(num_points, 0) - for bbox_targets in bbox_targets_list - ] - - # concat per level image - concat_lvl_labels = [] - concat_lvl_bbox_targets = [] - for i in range(num_levels): - concat_lvl_labels.append( - torch.cat([labels[i] for labels in labels_list])) - bbox_targets = torch.cat( - [bbox_targets[i] for bbox_targets in bbox_targets_list]) - if self.norm_on_bbox: - bbox_targets = bbox_targets / self.strides[i] - concat_lvl_bbox_targets.append(bbox_targets) - return concat_lvl_labels, concat_lvl_bbox_targets - - def _get_target_single(self, gt_bboxes, gt_labels, points, regress_ranges, - num_points_per_lvl): - """Compute regression and classification targets for a single image.""" - num_points = points.size(0) - num_gts = gt_labels.size(0) - if num_gts == 0: - return gt_labels.new_full((num_points,), self.num_classes), \ - gt_bboxes.new_zeros((num_points, 4)) - - areas = (gt_bboxes[:, 2] - gt_bboxes[:, 0]) * ( - gt_bboxes[:, 3] - gt_bboxes[:, 1]) - # TODO: figure out why these two are different - # areas = areas[None].expand(num_points, num_gts) - areas = areas[None].repeat(num_points, 1) - regress_ranges = regress_ranges[:, None, :].expand( - num_points, num_gts, 2) - gt_bboxes = gt_bboxes[None].expand(num_points, num_gts, 4) - xs, ys = points[:, 0], points[:, 1] - xs = xs[:, None].expand(num_points, num_gts) - ys = ys[:, None].expand(num_points, num_gts) - - left = xs - gt_bboxes[..., 0] - right = gt_bboxes[..., 2] - xs - top = ys - gt_bboxes[..., 1] - bottom = gt_bboxes[..., 3] - ys - bbox_targets = torch.stack((left, top, right, bottom), -1) - - if self.center_sampling: - # condition1: inside a `center bbox` - radius = self.center_sample_radius - center_xs = (gt_bboxes[..., 0] + gt_bboxes[..., 2]) / 2 - center_ys = (gt_bboxes[..., 1] + gt_bboxes[..., 3]) / 2 - center_gts = torch.zeros_like(gt_bboxes) - stride = center_xs.new_zeros(center_xs.shape) - - # project the points on current lvl back to the `original` sizes - lvl_begin = 0 - for lvl_idx, num_points_lvl in enumerate(num_points_per_lvl): - lvl_end = lvl_begin + num_points_lvl - stride[lvl_begin:lvl_end] = self.strides[lvl_idx] * radius - lvl_begin = lvl_end - - x_mins = center_xs - stride - y_mins = center_ys - stride - x_maxs = center_xs + stride - y_maxs = center_ys + stride - center_gts[..., 0] = torch.where(x_mins > gt_bboxes[..., 0], - x_mins, gt_bboxes[..., 0]) - center_gts[..., 1] = torch.where(y_mins > gt_bboxes[..., 1], - y_mins, gt_bboxes[..., 1]) - center_gts[..., 2] = torch.where(x_maxs > gt_bboxes[..., 2], - gt_bboxes[..., 2], x_maxs) - center_gts[..., 3] = torch.where(y_maxs > gt_bboxes[..., 3], - gt_bboxes[..., 3], y_maxs) - - cb_dist_left = xs - center_gts[..., 0] - cb_dist_right = center_gts[..., 2] - xs - cb_dist_top = ys - center_gts[..., 1] - cb_dist_bottom = center_gts[..., 3] - ys - center_bbox = torch.stack( - (cb_dist_left, cb_dist_top, cb_dist_right, cb_dist_bottom), -1) - inside_gt_bbox_mask = center_bbox.min(-1)[0] > 0 - else: - # condition1: inside a gt bbox - inside_gt_bbox_mask = bbox_targets.min(-1)[0] > 0 - - # condition2: limit the regression range for each location - max_regress_distance = bbox_targets.max(-1)[0] - inside_regress_range = ( - (max_regress_distance >= regress_ranges[..., 0]) - & (max_regress_distance <= regress_ranges[..., 1])) - - # if there are still more than one objects for a location, - # we choose the one with minimal area - areas[inside_gt_bbox_mask == 0] = INF - areas[inside_regress_range == 0] = INF - min_area, min_area_inds = areas.min(dim=1) - - labels = gt_labels[min_area_inds] - labels[min_area == INF] = self.num_classes # set as BG - bbox_targets = bbox_targets[range(num_points), min_area_inds] - - return labels, bbox_targets - - def centerness_target(self, pos_bbox_targets): - """Compute centerness targets. - - Args: - pos_bbox_targets (Tensor): BBox targets of positive bboxes in shape - (num_pos, 4) - - Returns: - Tensor: Centerness target. - """ - # only calculate pos centerness targets, otherwise there may be nan - left_right = pos_bbox_targets[:, [0, 2]] - top_bottom = pos_bbox_targets[:, [1, 3]] - if len(left_right) == 0: - centerness_targets = left_right[..., 0] - else: - centerness_targets = ( - left_right.min(dim=-1)[0] / left_right.max(dim=-1)[0]) * ( - top_bottom.min(dim=-1)[0] / top_bottom.max(dim=-1)[0]) - return torch.sqrt(centerness_targets) - - def _get_points_single(self, - featmap_size, - stride, - dtype, - device, - flatten=False): - """Get points according to feature map size. - - This function will be deprecated soon. - """ - warnings.warn( - '`_get_points_single` in `FCOSHead` will be ' - 'deprecated soon, we support a multi level point generator now' - 'you can get points of a single level feature map ' - 'with `self.prior_generator.single_level_grid_priors` ') - - y, x = super()._get_points_single(featmap_size, stride, dtype, device) - points = torch.stack((x.reshape(-1) * stride, y.reshape(-1) * stride), - dim=-1) + stride // 2 - return points diff --git a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/paa_head.py b/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/paa_head.py deleted file mode 100755 index 9200e2cb8..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/dense_heads/paa_head.py +++ /dev/null @@ -1,756 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -from mmcv.runner import force_fp32 - -from mmdet.core import multi_apply, multiclass_nms -from mmdet.core.bbox.iou_calculators import bbox_overlaps -from mmdet.models import HEADS -from mmdet.models.dense_heads import ATSSHead - -EPS = 1e-12 -try: - import sklearn.mixture as skm -except ImportError: - skm = None - - -def levels_to_images(mlvl_tensor): - """Concat multi-level feature maps by image. - - [feature_level0, feature_level1...] -> [feature_image0, feature_image1...] - Convert the shape of each element in mlvl_tensor from (N, C, H, W) to - (N, H*W , C), then split the element to N elements with shape (H*W, C), and - concat elements in same image of all level along first dimension. - - Args: - mlvl_tensor (list[torch.Tensor]): list of Tensor which collect from - corresponding level. Each element is of shape (N, C, H, W) - - Returns: - list[torch.Tensor]: A list that contains N tensors and each tensor is - of shape (num_elements, C) - """ - batch_size = mlvl_tensor[0].size(0) - batch_list = [[] for _ in range(batch_size)] - channels = mlvl_tensor[0].size(1) - for t in mlvl_tensor: - t = t.permute(0, 2, 3, 1) - t = t.view(batch_size, -1, channels).contiguous() - for img in range(batch_size): - batch_list[img].append(t[img]) - return [torch.cat(item, 0) for item in batch_list] - - -@HEADS.register_module() -class PAAHead(ATSSHead): - """Head of PAAAssignment: Probabilistic Anchor Assignment with IoU - Prediction for Object Detection. - - Code is modified from the `official github repo - `_. - - More details can be found in the `paper - `_ . - - Args: - topk (int): Select topk samples with smallest loss in - each level. - score_voting (bool): Whether to use score voting in post-process. - covariance_type : String describing the type of covariance parameters - to be used in :class:`sklearn.mixture.GaussianMixture`. - It must be one of: - - - 'full': each component has its own general covariance matrix - - 'tied': all components share the same general covariance matrix - - 'diag': each component has its own diagonal covariance matrix - - 'spherical': each component has its own single variance - Default: 'diag'. From 'full' to 'spherical', the gmm fitting - process is faster yet the performance could be influenced. For most - cases, 'diag' should be a good choice. - """ - - def __init__(self, - *args, - topk=9, - score_voting=True, - covariance_type='diag', - **kwargs): - # topk used in paa reassign process - self.topk = topk - self.with_score_voting = score_voting - self.covariance_type = covariance_type - super(PAAHead, self).__init__(*args, **kwargs) - - @force_fp32(apply_to=('cls_scores', 'bbox_preds', 'iou_preds')) - def loss(self, - cls_scores, - bbox_preds, - iou_preds, - gt_bboxes, - gt_labels, - img_metas, - gt_bboxes_ignore=None): - """Compute losses of the head. - - Args: - cls_scores (list[Tensor]): Box scores for each scale level - Has shape (N, num_anchors * num_classes, H, W) - bbox_preds (list[Tensor]): Box energies / deltas for each scale - level with shape (N, num_anchors * 4, H, W) - iou_preds (list[Tensor]): iou_preds for each scale - level with shape (N, num_anchors * 1, H, W) - gt_bboxes (list[Tensor]): Ground truth bboxes for each image with - shape (num_gts, 4) in [tl_x, tl_y, br_x, br_y] format. - gt_labels (list[Tensor]): class indices corresponding to each box - img_metas (list[dict]): Meta information of each image, e.g., - image size, scaling factor, etc. - gt_bboxes_ignore (list[Tensor] | None): Specify which bounding - boxes can be ignored when are computing the loss. - - Returns: - dict[str, Tensor]: A dictionary of loss gmm_assignment. - """ - - featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores] - assert len(featmap_sizes) == self.prior_generator.num_levels - - device = cls_scores[0].device - anchor_list, valid_flag_list = self.get_anchors( - featmap_sizes, img_metas, device=device) - label_channels = self.cls_out_channels if self.use_sigmoid_cls else 1 - cls_reg_targets = self.get_targets( - anchor_list, - valid_flag_list, - gt_bboxes, - img_metas, - gt_bboxes_ignore_list=gt_bboxes_ignore, - gt_labels_list=gt_labels, - label_channels=label_channels, - ) - (labels, labels_weight, bboxes_target, bboxes_weight, pos_inds, - pos_gt_index) = cls_reg_targets - cls_scores = levels_to_images(cls_scores) - cls_scores = [ - item.reshape(-1, self.cls_out_channels) for item in cls_scores - ] - bbox_preds = levels_to_images(bbox_preds) - bbox_preds = [item.reshape(-1, 4) for item in bbox_preds] - iou_preds = levels_to_images(iou_preds) - iou_preds = [item.reshape(-1, 1) for item in iou_preds] - pos_losses_list, = multi_apply(self.get_pos_loss, anchor_list, - cls_scores, bbox_preds, labels, - labels_weight, bboxes_target, - bboxes_weight, pos_inds) - - with torch.no_grad(): - reassign_labels, reassign_label_weight, \ - reassign_bbox_weights, num_pos = multi_apply( - self.paa_reassign, - pos_losses_list, - labels, - labels_weight, - bboxes_weight, - pos_inds, - pos_gt_index, - anchor_list) - num_pos = sum(num_pos) - # convert all tensor list to a flatten tensor - cls_scores = torch.cat(cls_scores, 0).view(-1, cls_scores[0].size(-1)) - bbox_preds = torch.cat(bbox_preds, 0).view(-1, bbox_preds[0].size(-1)) - iou_preds = torch.cat(iou_preds, 0).view(-1, iou_preds[0].size(-1)) - labels = torch.cat(reassign_labels, 0).view(-1) - flatten_anchors = torch.cat( - [torch.cat(item, 0) for item in anchor_list]) - labels_weight = torch.cat(reassign_label_weight, 0).view(-1) - bboxes_target = torch.cat(bboxes_target, - 0).view(-1, bboxes_target[0].size(-1)) - - pos_inds_flatten = ((labels >= 0) - & - (labels < self.num_classes)).nonzero().reshape(-1) - - losses_cls = self.loss_cls( - cls_scores, - labels, - labels_weight, - avg_factor=max(num_pos, len(img_metas))) # avoid num_pos=0 - if num_pos: - pos_bbox_pred = self.bbox_coder.decode( - flatten_anchors[pos_inds_flatten], - bbox_preds[pos_inds_flatten]) - pos_bbox_target = bboxes_target[pos_inds_flatten] - iou_target = bbox_overlaps( - pos_bbox_pred.detach(), pos_bbox_target, is_aligned=True) - losses_iou = self.loss_centerness( - iou_preds[pos_inds_flatten], - iou_target.unsqueeze(-1), - avg_factor=num_pos) - losses_bbox = self.loss_bbox( - pos_bbox_pred, - pos_bbox_target, - iou_target.clamp(min=EPS), - avg_factor=iou_target.sum()) - else: - losses_iou = iou_preds.sum() * 0 - losses_bbox = bbox_preds.sum() * 0 - - return dict( - loss_cls=losses_cls, loss_bbox=losses_bbox, loss_iou=losses_iou) - - def get_pos_loss(self, anchors, cls_score, bbox_pred, label, label_weight, - bbox_target, bbox_weight, pos_inds): - """Calculate loss of all potential positive samples obtained from first - match process. - - Args: - anchors (list[Tensor]): Anchors of each scale. - cls_score (Tensor): Box scores of single image with shape - (num_anchors, num_classes) - bbox_pred (Tensor): Box energies / deltas of single image - with shape (num_anchors, 4) - label (Tensor): classification target of each anchor with - shape (num_anchors,) - label_weight (Tensor): Classification loss weight of each - anchor with shape (num_anchors). - bbox_target (dict): Regression target of each anchor with - shape (num_anchors, 4). - bbox_weight (Tensor): Bbox weight of each anchor with shape - (num_anchors, 4). - pos_inds (Tensor): Index of all positive samples got from - first assign process. - - Returns: - Tensor: Losses of all positive samples in single image. - """ - if not len(pos_inds): - return cls_score.new([]), - anchors_all_level = torch.cat(anchors, 0) - pos_scores = cls_score[pos_inds] - pos_bbox_pred = bbox_pred[pos_inds] - pos_label = label[pos_inds] - pos_label_weight = label_weight[pos_inds] - pos_bbox_target = bbox_target[pos_inds] - pos_bbox_weight = bbox_weight[pos_inds] - pos_anchors = anchors_all_level[pos_inds] - pos_bbox_pred = self.bbox_coder.decode(pos_anchors, pos_bbox_pred) - - # to keep loss dimension - loss_cls = self.loss_cls( - pos_scores, - pos_label, - pos_label_weight, - avg_factor=1.0, - reduction_override='none') - - loss_bbox = self.loss_bbox( - pos_bbox_pred, - pos_bbox_target, - pos_bbox_weight, - avg_factor=1.0, # keep same loss weight before reassign - reduction_override='none') - - loss_cls = loss_cls.sum(-1) - pos_loss = loss_bbox + loss_cls - return pos_loss, - - def paa_reassign(self, pos_losses, label, label_weight, bbox_weight, - pos_inds, pos_gt_inds, anchors): - """Fit loss to GMM distribution and separate positive, ignore, negative - samples again with GMM model. - - Args: - pos_losses (Tensor): Losses of all positive samples in - single image. - label (Tensor): classification target of each anchor with - shape (num_anchors,) - label_weight (Tensor): Classification loss weight of each - anchor with shape (num_anchors). - bbox_weight (Tensor): Bbox weight of each anchor with shape - (num_anchors, 4). - pos_inds (Tensor): Index of all positive samples got from - first assign process. - pos_gt_inds (Tensor): Gt_index of all positive samples got - from first assign process. - anchors (list[Tensor]): Anchors of each scale. - - Returns: - tuple: Usually returns a tuple containing learning targets. - - - label (Tensor): classification target of each anchor after - paa assign, with shape (num_anchors,) - - label_weight (Tensor): Classification loss weight of each - anchor after paa assign, with shape (num_anchors). - - bbox_weight (Tensor): Bbox weight of each anchor with shape - (num_anchors, 4). - - num_pos (int): The number of positive samples after paa - assign. - """ - if not len(pos_inds): - return label, label_weight, bbox_weight, 0 - label = label.clone() - label_weight = label_weight.clone() - bbox_weight = bbox_weight.clone() - num_gt = pos_gt_inds.max() + 1 - num_level = len(anchors) - num_anchors_each_level = [item.size(0) for item in anchors] - num_anchors_each_level.insert(0, 0) - inds_level_interval = np.cumsum(num_anchors_each_level) - pos_level_mask = [] - for i in range(num_level): - mask = (pos_inds >= inds_level_interval[i]) & ( - pos_inds < inds_level_interval[i + 1]) - pos_level_mask.append(mask) - pos_inds_after_paa = [label.new_tensor([])] - ignore_inds_after_paa = [label.new_tensor([])] - for gt_ind in range(num_gt): - pos_inds_gmm = [] - pos_loss_gmm = [] - gt_mask = pos_gt_inds == gt_ind - for level in range(num_level): - level_mask = pos_level_mask[level] - level_gt_mask = level_mask & gt_mask - value, topk_inds = pos_losses[level_gt_mask].topk( - min(level_gt_mask.sum(), self.topk), largest=False) - pos_inds_gmm.append(pos_inds[level_gt_mask][topk_inds]) - pos_loss_gmm.append(value) - pos_inds_gmm = torch.cat(pos_inds_gmm) - pos_loss_gmm = torch.cat(pos_loss_gmm) - # fix gmm need at least two sample - if len(pos_inds_gmm) < 2: - continue - device = pos_inds_gmm.device - pos_loss_gmm, sort_inds = pos_loss_gmm.sort() - pos_inds_gmm = pos_inds_gmm[sort_inds] - pos_loss_gmm = pos_loss_gmm.view(-1, 1).cpu().numpy() - min_loss, max_loss = pos_loss_gmm.min(), pos_loss_gmm.max() - means_init = np.array([min_loss, max_loss]).reshape(2, 1) - weights_init = np.array([0.5, 0.5]) - precisions_init = np.array([1.0, 1.0]).reshape(2, 1, 1) # full - if self.covariance_type == 'spherical': - precisions_init = precisions_init.reshape(2) - elif self.covariance_type == 'diag': - precisions_init = precisions_init.reshape(2, 1) - elif self.covariance_type == 'tied': - precisions_init = np.array([[1.0]]) - if skm is None: - raise ImportError('Please run "pip install sklearn" ' - 'to install sklearn first.') - gmm = skm.GaussianMixture( - 2, - weights_init=weights_init, - means_init=means_init, - precisions_init=precisions_init, - covariance_type=self.covariance_type) - gmm.fit(pos_loss_gmm) - gmm_assignment = gmm.predict(pos_loss_gmm) - scores = gmm.score_samples(pos_loss_gmm) - gmm_assignment = torch.from_numpy(gmm_assignment).to(device) - scores = torch.from_numpy(scores).to(device) - - pos_inds_temp, ignore_inds_temp = self.gmm_separation_scheme( - gmm_assignment, scores, pos_inds_gmm) - pos_inds_after_paa.append(pos_inds_temp) - ignore_inds_after_paa.append(ignore_inds_temp) - - pos_inds_after_paa = torch.cat(pos_inds_after_paa) - ignore_inds_after_paa = torch.cat(ignore_inds_after_paa) - reassign_mask = (pos_inds.unsqueeze(1) != pos_inds_after_paa).all(1) - reassign_ids = pos_inds[reassign_mask] - label[reassign_ids] = self.num_classes - label_weight[ignore_inds_after_paa] = 0 - bbox_weight[reassign_ids] = 0 - num_pos = len(pos_inds_after_paa) - return label, label_weight, bbox_weight, num_pos - - def gmm_separation_scheme(self, gmm_assignment, scores, pos_inds_gmm): - """A general separation scheme for gmm model. - - It separates a GMM distribution of candidate samples into three - parts, 0 1 and uncertain areas, and you can implement other - separation schemes by rewriting this function. - - Args: - gmm_assignment (Tensor): The prediction of GMM which is of shape - (num_samples,). The 0/1 value indicates the distribution - that each sample comes from. - scores (Tensor): The probability of sample coming from the - fit GMM distribution. The tensor is of shape (num_samples,). - pos_inds_gmm (Tensor): All the indexes of samples which are used - to fit GMM model. The tensor is of shape (num_samples,) - - Returns: - tuple[Tensor]: The indices of positive and ignored samples. - - - pos_inds_temp (Tensor): Indices of positive samples. - - ignore_inds_temp (Tensor): Indices of ignore samples. - """ - # The implementation is (c) in Fig.3 in origin paper instead of (b). - # You can refer to issues such as - # https://github.com/kkhoot/PAA/issues/8 and - # https://github.com/kkhoot/PAA/issues/9. - fgs = gmm_assignment == 0 - pos_inds_temp = fgs.new_tensor([], dtype=torch.long) - ignore_inds_temp = fgs.new_tensor([], dtype=torch.long) - if fgs.nonzero().numel(): - _, pos_thr_ind = scores[fgs].topk(1) - pos_inds_temp = pos_inds_gmm[fgs][:pos_thr_ind + 1] - ignore_inds_temp = pos_inds_gmm.new_tensor([]) - return pos_inds_temp, ignore_inds_temp - - def get_targets( - self, - anchor_list, - valid_flag_list, - gt_bboxes_list, - img_metas, - gt_bboxes_ignore_list=None, - gt_labels_list=None, - label_channels=1, - unmap_outputs=True, - ): - """Get targets for PAA head. - - This method is almost the same as `AnchorHead.get_targets()`. We direct - return the results from _get_targets_single instead map it to levels - by images_to_levels function. - - Args: - anchor_list (list[list[Tensor]]): Multi level anchors of each - image. The outer list indicates images, and the inner list - corresponds to feature levels of the image. Each element of - the inner list is a tensor of shape (num_anchors, 4). - valid_flag_list (list[list[Tensor]]): Multi level valid flags of - each image. The outer list indicates images, and the inner list - corresponds to feature levels of the image. Each element of - the inner list is a tensor of shape (num_anchors, ) - gt_bboxes_list (list[Tensor]): Ground truth bboxes of each image. - img_metas (list[dict]): Meta info of each image. - gt_bboxes_ignore_list (list[Tensor]): Ground truth bboxes to be - ignored. - gt_labels_list (list[Tensor]): Ground truth labels of each box. - label_channels (int): Channel of label. - unmap_outputs (bool): Whether to map outputs back to the original - set of anchors. - - Returns: - tuple: Usually returns a tuple containing learning targets. - - - labels (list[Tensor]): Labels of all anchors, each with - shape (num_anchors,). - - label_weights (list[Tensor]): Label weights of all anchor. - each with shape (num_anchors,). - - bbox_targets (list[Tensor]): BBox targets of all anchors. - each with shape (num_anchors, 4). - - bbox_weights (list[Tensor]): BBox weights of all anchors. - each with shape (num_anchors, 4). - - pos_inds (list[Tensor]): Contains all index of positive - sample in all anchor. - - gt_inds (list[Tensor]): Contains all gt_index of positive - sample in all anchor. - """ - - num_imgs = len(img_metas) - assert len(anchor_list) == len(valid_flag_list) == num_imgs - concat_anchor_list = [] - concat_valid_flag_list = [] - for i in range(num_imgs): - assert len(anchor_list[i]) == len(valid_flag_list[i]) - concat_anchor_list.append(torch.cat(anchor_list[i])) - concat_valid_flag_list.append(torch.cat(valid_flag_list[i])) - - # compute targets for each image - if gt_bboxes_ignore_list is None: - gt_bboxes_ignore_list = [None for _ in range(num_imgs)] - if gt_labels_list is None: - gt_labels_list = [None for _ in range(num_imgs)] - results = multi_apply( - self._get_targets_single, - concat_anchor_list, - concat_valid_flag_list, - gt_bboxes_list, - gt_bboxes_ignore_list, - gt_labels_list, - img_metas, - label_channels=label_channels, - unmap_outputs=unmap_outputs) - - (labels, label_weights, bbox_targets, bbox_weights, valid_pos_inds, - valid_neg_inds, sampling_result) = results - - # Due to valid flag of anchors, we have to calculate the real pos_inds - # in origin anchor set. - pos_inds = [] - for i, single_labels in enumerate(labels): - pos_mask = (0 <= single_labels) & ( - single_labels < self.num_classes) - pos_inds.append(pos_mask.nonzero().view(-1)) - - gt_inds = [item.pos_assigned_gt_inds for item in sampling_result] - return (labels, label_weights, bbox_targets, bbox_weights, pos_inds, - gt_inds) - - def _get_targets_single(self, - flat_anchors, - valid_flags, - gt_bboxes, - gt_bboxes_ignore, - gt_labels, - img_meta, - label_channels=1, - unmap_outputs=True): - """Compute regression and classification targets for anchors in a - single image. - - This method is same as `AnchorHead._get_targets_single()`. - """ - assert unmap_outputs, 'We must map outputs back to the original' \ - 'set of anchors in PAAhead' - return super(ATSSHead, self)._get_targets_single( - flat_anchors, - valid_flags, - gt_bboxes, - gt_bboxes_ignore, - gt_labels, - img_meta, - label_channels=1, - unmap_outputs=True) - - @force_fp32(apply_to=('cls_scores', 'bbox_preds')) - def get_bboxes(self, - cls_scores, - bbox_preds, - score_factors=None, - img_metas=None, - cfg=None, - rescale=False, - with_nms=True, - **kwargs): - assert with_nms, 'PAA only supports "with_nms=True" now and it ' \ - 'means PAAHead does not support ' \ - 'test-time augmentation' - return super(ATSSHead, self).get_bboxes(cls_scores, bbox_preds, - score_factors, img_metas, cfg, - rescale, with_nms, **kwargs) - - def _get_bboxes_single(self, - cls_score_list, - bbox_pred_list, - score_factor_list, - mlvl_priors, - img_meta, - cfg, - rescale=False, - with_nms=True, - **kwargs): - """Transform outputs of a single image into bbox predictions. - - Args: - cls_score_list (list[Tensor]): Box scores from all scale - levels of a single image, each item has shape - (num_priors * num_classes, H, W). - bbox_pred_list (list[Tensor]): Box energies / deltas from - all scale levels of a single image, each item has shape - (num_priors * 4, H, W). - score_factor_list (list[Tensor]): Score factors from all scale - levels of a single image, each item has shape - (num_priors * 1, H, W). - mlvl_priors (list[Tensor]): Each element in the list is - the priors of a single level in feature pyramid, has shape - (num_priors, 4). - img_meta (dict): Image meta info. - cfg (mmcv.Config): Test / postprocessing configuration, - if None, test_cfg would be used. - rescale (bool): If True, return boxes in original image space. - Default: False. - with_nms (bool): If True, do nms before return boxes. - Default: True. - - Returns: - tuple[Tensor]: Results of detected bboxes and labels. If with_nms - is False and mlvl_score_factor is None, return mlvl_bboxes and - mlvl_scores, else return mlvl_bboxes, mlvl_scores and - mlvl_score_factor. Usually with_nms is False is used for aug - test. If with_nms is True, then return the following format - - - det_bboxes (Tensor): Predicted bboxes with shape \ - [num_bboxes, 5], where the first 4 columns are bounding \ - box positions (tl_x, tl_y, br_x, br_y) and the 5-th \ - column are scores between 0 and 1. - - det_labels (Tensor): Predicted labels of the corresponding \ - box with shape [num_bboxes]. - """ - cfg = self.test_cfg if cfg is None else cfg - img_shape = img_meta['img_shape'] - nms_pre = cfg.get('nms_pre', -1) - - mlvl_bboxes = [] - mlvl_scores = [] - mlvl_score_factors = [] - for level_idx, (cls_score, bbox_pred, score_factor, priors) in \ - enumerate(zip(cls_score_list, bbox_pred_list, - score_factor_list, mlvl_priors)): - assert cls_score.size()[-2:] == bbox_pred.size()[-2:] - - scores = cls_score.permute(1, 2, 0).reshape( - -1, self.cls_out_channels).sigmoid() - bbox_pred = bbox_pred.permute(1, 2, 0).reshape(-1, 4) - score_factor = score_factor.permute(1, 2, 0).reshape(-1).sigmoid() - - if 0 < nms_pre < scores.shape[0]: - max_scores, _ = (scores * - score_factor[:, None]).sqrt().max(dim=1) - _, topk_inds = max_scores.topk(nms_pre) - priors = priors[topk_inds, :] - bbox_pred = bbox_pred[topk_inds, :] - scores = scores[topk_inds, :] - score_factor = score_factor[topk_inds] - - bboxes = self.bbox_coder.decode( - priors, bbox_pred, max_shape=img_shape) - mlvl_bboxes.append(bboxes) - mlvl_scores.append(scores) - mlvl_score_factors.append(score_factor) - - return self._bbox_post_process(mlvl_scores, mlvl_bboxes, - img_meta['scale_factor'], cfg, rescale, - with_nms, mlvl_score_factors, **kwargs) - - def _bbox_post_process(self, - mlvl_scores, - mlvl_bboxes, - scale_factor, - cfg, - rescale=False, - with_nms=True, - mlvl_score_factors=None, - **kwargs): - """bbox post-processing method. - - The boxes would be rescaled to the original image scale and do - the nms operation. Usually with_nms is False is used for aug test. - - Args: - mlvl_scores (list[Tensor]): Box scores from all scale - levels of a single image, each item has shape - (num_bboxes, num_class). - mlvl_bboxes (list[Tensor]): Decoded bboxes from all scale - levels of a single image, each item has shape (num_bboxes, 4). - scale_factor (ndarray, optional): Scale factor of the image arange - as (w_scale, h_scale, w_scale, h_scale). - cfg (mmcv.Config): Test / postprocessing configuration, - if None, test_cfg would be used. - rescale (bool): If True, return boxes in original image space. - Default: False. - with_nms (bool): If True, do nms before return boxes. - Default: True. - mlvl_score_factors (list[Tensor], optional): Score factor from - all scale levels of a single image, each item has shape - (num_bboxes, ). Default: None. - - Returns: - tuple[Tensor]: Results of detected bboxes and labels. If with_nms - is False and mlvl_score_factor is None, return mlvl_bboxes and - mlvl_scores, else return mlvl_bboxes, mlvl_scores and - mlvl_score_factor. Usually with_nms is False is used for aug - test. If with_nms is True, then return the following format - - - det_bboxes (Tensor): Predicted bboxes with shape \ - [num_bboxes, 5], where the first 4 columns are bounding \ - box positions (tl_x, tl_y, br_x, br_y) and the 5-th \ - column are scores between 0 and 1. - - det_labels (Tensor): Predicted labels of the corresponding \ - box with shape [num_bboxes]. - """ - mlvl_bboxes = torch.cat(mlvl_bboxes) - if rescale: - mlvl_bboxes /= mlvl_bboxes.new_tensor(scale_factor) - mlvl_scores = torch.cat(mlvl_scores) - # Add a dummy background class to the backend when using sigmoid - # remind that we set FG labels to [0, num_class-1] since mmdet v2.0 - # BG cat_id: num_class - padding = mlvl_scores.new_zeros(mlvl_scores.shape[0], 1) - mlvl_scores = torch.cat([mlvl_scores, padding], dim=1) - - mlvl_iou_preds = torch.cat(mlvl_score_factors) - mlvl_nms_scores = (mlvl_scores * mlvl_iou_preds[:, None]).sqrt() - det_bboxes, det_labels = multiclass_nms( - mlvl_bboxes, - mlvl_nms_scores, - cfg.score_thr, - cfg.nms, - cfg.max_per_img, - score_factors=None) - if self.with_score_voting and len(det_bboxes) > 0: - det_bboxes, det_labels = self.score_voting(det_bboxes, det_labels, - mlvl_bboxes, - mlvl_nms_scores, - cfg.score_thr) - - return det_bboxes, det_labels - - def score_voting(self, det_bboxes, det_labels, mlvl_bboxes, - mlvl_nms_scores, score_thr): - """Implementation of score voting method works on each remaining boxes - after NMS procedure. - - Args: - det_bboxes (Tensor): Remaining boxes after NMS procedure, - with shape (k, 5), each dimension means - (x1, y1, x2, y2, score). - det_labels (Tensor): The label of remaining boxes, with shape - (k, 1),Labels are 0-based. - mlvl_bboxes (Tensor): All boxes before the NMS procedure, - with shape (num_anchors,4). - mlvl_nms_scores (Tensor): The scores of all boxes which is used - in the NMS procedure, with shape (num_anchors, num_class) - score_thr (float): The score threshold of bboxes. - - Returns: - tuple: Usually returns a tuple containing voting results. - - - det_bboxes_voted (Tensor): Remaining boxes after - score voting procedure, with shape (k, 5), each - dimension means (x1, y1, x2, y2, score). - - det_labels_voted (Tensor): Label of remaining bboxes - after voting, with shape (num_anchors,). - """ - candidate_mask = mlvl_nms_scores > score_thr - candidate_mask_nonzeros = candidate_mask.nonzero(as_tuple=False) - candidate_inds = candidate_mask_nonzeros[:, 0] - candidate_labels = candidate_mask_nonzeros[:, 1] - candidate_bboxes = mlvl_bboxes[candidate_inds] - candidate_scores = mlvl_nms_scores[candidate_mask] - det_bboxes_voted = [] - det_labels_voted = [] - for cls in range(self.cls_out_channels): - candidate_cls_mask = candidate_labels == cls - if not candidate_cls_mask.any(): - continue - candidate_cls_scores = candidate_scores[candidate_cls_mask] - candidate_cls_bboxes = candidate_bboxes[candidate_cls_mask] - det_cls_mask = det_labels == cls - det_cls_bboxes = det_bboxes[det_cls_mask].view( - -1, det_bboxes.size(-1)) - det_candidate_ious = bbox_overlaps(det_cls_bboxes[:, :4], - candidate_cls_bboxes) - for det_ind in range(len(det_cls_bboxes)): - single_det_ious = det_candidate_ious[det_ind] - pos_ious_mask = single_det_ious > 0.01 - pos_ious = single_det_ious[pos_ious_mask] - pos_bboxes = candidate_cls_bboxes[pos_ious_mask] - pos_scores = candidate_cls_scores[pos_ious_mask] - pis = (torch.exp(-(1 - pos_ious)**2 / 0.025) * - pos_scores)[:, None] - voted_box = torch.sum( - pis * pos_bboxes, dim=0) / torch.sum( - pis, dim=0) - voted_score = det_cls_bboxes[det_ind][-1:][None, :] - det_bboxes_voted.append( - torch.cat((voted_box[None, :], voted_score), dim=1)) - det_labels_voted.append(cls) - - det_bboxes_voted = torch.cat(det_bboxes_voted, dim=0) - det_labels_voted = det_labels.new_tensor(det_labels_voted) - return det_bboxes_voted, det_labels_voted diff --git a/cv/detection/autoassign/pytorch/mmdet/models/detectors/__init__.py b/cv/detection/autoassign/pytorch/mmdet/models/detectors/__init__.py deleted file mode 100755 index 9051b6b5f..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/detectors/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .autoassign import AutoAssign diff --git a/cv/detection/autoassign/pytorch/mmdet/models/detectors/autoassign.py b/cv/detection/autoassign/pytorch/mmdet/models/detectors/autoassign.py deleted file mode 100755 index 2001efa53..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/detectors/autoassign.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..builder import DETECTORS -from .single_stage import SingleStageDetector - - -@DETECTORS.register_module() -class AutoAssign(SingleStageDetector): - """Implementation of `AutoAssign: Differentiable Label Assignment for Dense - Object Detection `_.""" - - def __init__(self, - backbone, - neck, - bbox_head, - train_cfg=None, - test_cfg=None, - pretrained=None): - super(AutoAssign, self).__init__(backbone, neck, bbox_head, train_cfg, - test_cfg, pretrained) diff --git a/cv/detection/autoassign/pytorch/mmdet/models/detectors/base.py b/cv/detection/autoassign/pytorch/mmdet/models/detectors/base.py deleted file mode 100755 index c66e2c28f..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/detectors/base.py +++ /dev/null @@ -1,360 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod -from collections import OrderedDict - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import BaseModule, auto_fp16 - -from mmdet.core.visualization import imshow_det_bboxes - - -class BaseDetector(BaseModule, metaclass=ABCMeta): - """Base class for detectors.""" - - def __init__(self, init_cfg=None): - super(BaseDetector, self).__init__(init_cfg) - self.fp16_enabled = False - - @property - def with_neck(self): - """bool: whether the detector has a neck""" - return hasattr(self, 'neck') and self.neck is not None - - # TODO: these properties need to be carefully handled - # for both single stage & two stage detectors - @property - def with_shared_head(self): - """bool: whether the detector has a shared head in the RoI Head""" - return hasattr(self, 'roi_head') and self.roi_head.with_shared_head - - @property - def with_bbox(self): - """bool: whether the detector has a bbox head""" - return ((hasattr(self, 'roi_head') and self.roi_head.with_bbox) - or (hasattr(self, 'bbox_head') and self.bbox_head is not None)) - - @property - def with_mask(self): - """bool: whether the detector has a mask head""" - return ((hasattr(self, 'roi_head') and self.roi_head.with_mask) - or (hasattr(self, 'mask_head') and self.mask_head is not None)) - - @abstractmethod - def extract_feat(self, imgs): - """Extract features from images.""" - pass - - def extract_feats(self, imgs): - """Extract features from multiple images. - - Args: - imgs (list[torch.Tensor]): A list of images. The images are - augmented from the same image but in different ways. - - Returns: - list[torch.Tensor]: Features of different images - """ - assert isinstance(imgs, list) - return [self.extract_feat(img) for img in imgs] - - def forward_train(self, imgs, img_metas, **kwargs): - """ - Args: - img (Tensor): of shape (N, C, H, W) encoding input images. - Typically these should be mean centered and std scaled. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys, see - :class:`mmdet.datasets.pipelines.Collect`. - kwargs (keyword arguments): Specific to concrete implementation. - """ - # NOTE the batched image size information may be useful, e.g. - # in DETR, this is needed for the construction of masks, which is - # then used for the transformer_head. - batch_input_shape = tuple(imgs[0].size()[-2:]) - for img_meta in img_metas: - img_meta['batch_input_shape'] = batch_input_shape - - async def async_simple_test(self, img, img_metas, **kwargs): - raise NotImplementedError - - @abstractmethod - def simple_test(self, img, img_metas, **kwargs): - pass - - @abstractmethod - def aug_test(self, imgs, img_metas, **kwargs): - """Test function with test time augmentation.""" - pass - - async def aforward_test(self, *, img, img_metas, **kwargs): - for var, name in [(img, 'img'), (img_metas, 'img_metas')]: - if not isinstance(var, list): - raise TypeError(f'{name} must be a list, but got {type(var)}') - - num_augs = len(img) - if num_augs != len(img_metas): - raise ValueError(f'num of augmentations ({len(img)}) ' - f'!= num of image metas ({len(img_metas)})') - # TODO: remove the restriction of samples_per_gpu == 1 when prepared - samples_per_gpu = img[0].size(0) - assert samples_per_gpu == 1 - - if num_augs == 1: - return await self.async_simple_test(img[0], img_metas[0], **kwargs) - else: - raise NotImplementedError - - def forward_test(self, imgs, img_metas, **kwargs): - """ - Args: - imgs (List[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains all images in the batch. - img_metas (List[List[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. - """ - for var, name in [(imgs, 'imgs'), (img_metas, 'img_metas')]: - if not isinstance(var, list): - raise TypeError(f'{name} must be a list, but got {type(var)}') - - num_augs = len(imgs) - if num_augs != len(img_metas): - raise ValueError(f'num of augmentations ({len(imgs)}) ' - f'!= num of image meta ({len(img_metas)})') - - # NOTE the batched image size information may be useful, e.g. - # in DETR, this is needed for the construction of masks, which is - # then used for the transformer_head. - for img, img_meta in zip(imgs, img_metas): - batch_size = len(img_meta) - for img_id in range(batch_size): - img_meta[img_id]['batch_input_shape'] = tuple(img.size()[-2:]) - - if num_augs == 1: - # proposals (List[List[Tensor]]): the outer list indicates - # test-time augs (multiscale, flip, etc.) and the inner list - # indicates images in a batch. - # The Tensor should have a shape Px4, where P is the number of - # proposals. - if 'proposals' in kwargs: - kwargs['proposals'] = kwargs['proposals'][0] - return self.simple_test(imgs[0], img_metas[0], **kwargs) - else: - assert imgs[0].size(0) == 1, 'aug test does not support ' \ - 'inference with batch size ' \ - f'{imgs[0].size(0)}' - # TODO: support test augmentation for predefined proposals - assert 'proposals' not in kwargs - return self.aug_test(imgs, img_metas, **kwargs) - - @auto_fp16(apply_to=('img', )) - def forward(self, img, img_metas, return_loss=True, **kwargs): - """Calls either :func:`forward_train` or :func:`forward_test` depending - on whether ``return_loss`` is ``True``. - - Note this setting will change the expected inputs. When - ``return_loss=True``, img and img_meta are single-nested (i.e. Tensor - and List[dict]), and when ``resturn_loss=False``, img and img_meta - should be double nested (i.e. List[Tensor], List[List[dict]]), with - the outer list indicating test time augmentations. - """ - if torch.onnx.is_in_onnx_export(): - assert len(img_metas) == 1 - return self.onnx_export(img[0], img_metas[0]) - - if return_loss: - return self.forward_train(img, img_metas, **kwargs) - else: - return self.forward_test(img, img_metas, **kwargs) - - def _parse_losses(self, losses): - """Parse the raw outputs (losses) of the network. - - Args: - losses (dict): Raw output of the network, which usually contain - losses and other necessary information. - - Returns: - tuple[Tensor, dict]: (loss, log_vars), loss is the loss tensor \ - which may be a weighted sum of all losses, log_vars contains \ - all the variables to be sent to the logger. - """ - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(_value for _key, _value in log_vars.items() - if 'loss' in _key) - - # If the loss_vars has different length, GPUs will wait infinitely - if dist.is_available() and dist.is_initialized(): - log_var_length = torch.tensor(len(log_vars), device=loss.device) - dist.all_reduce(log_var_length) - message = (f'rank {dist.get_rank()}' + - f' len(log_vars): {len(log_vars)}' + ' keys: ' + - ','.join(log_vars.keys())) - assert log_var_length == len(log_vars) * dist.get_world_size(), \ - 'loss log variables are different across GPUs!\n' + message - - log_vars['loss'] = loss - for loss_name, loss_value in log_vars.items(): - # reduce loss when distributed training - if dist.is_available() and dist.is_initialized(): - loss_value = loss_value.data.clone() - dist.all_reduce(loss_value.div_(dist.get_world_size())) - log_vars[loss_name] = loss_value.item() - - return loss, log_vars - - def train_step(self, data, optimizer): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating is also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer` | dict): The optimizer of - runner is passed to ``train_step()``. This argument is unused - and reserved. - - Returns: - dict: It should contain at least 3 keys: ``loss``, ``log_vars``, \ - ``num_samples``. - - - ``loss`` is a tensor for back propagation, which can be a - weighted sum of multiple losses. - - ``log_vars`` contains all the variables to be sent to the - logger. - - ``num_samples`` indicates the batch size (when the model is - DDP, it means the batch size on each GPU), which is used for - averaging the logs. - """ - losses = self(**data) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, log_vars=log_vars, num_samples=len(data['img_metas'])) - - return outputs - - def val_step(self, data, optimizer=None): - """The iteration step during validation. - - This method shares the same signature as :func:`train_step`, but used - during val epochs. Note that the evaluation after training epochs is - not implemented with this method, but an evaluation hook. - """ - losses = self(**data) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, log_vars=log_vars, num_samples=len(data['img_metas'])) - - return outputs - - def show_result(self, - img, - result, - score_thr=0.3, - bbox_color=(72, 101, 241), - text_color=(72, 101, 241), - mask_color=None, - thickness=2, - font_size=13, - win_name='', - show=False, - wait_time=0, - out_file=None): - """Draw `result` over `img`. - - Args: - img (str or Tensor): The image to be displayed. - result (Tensor or tuple): The results to draw over `img` - bbox_result or (bbox_result, segm_result). - score_thr (float, optional): Minimum score of bboxes to be shown. - Default: 0.3. - bbox_color (str or tuple(int) or :obj:`Color`):Color of bbox lines. - The tuple of color should be in BGR order. Default: 'green' - text_color (str or tuple(int) or :obj:`Color`):Color of texts. - The tuple of color should be in BGR order. Default: 'green' - mask_color (None or str or tuple(int) or :obj:`Color`): - Color of masks. The tuple of color should be in BGR order. - Default: None - thickness (int): Thickness of lines. Default: 2 - font_size (int): Font size of texts. Default: 13 - win_name (str): The window name. Default: '' - wait_time (float): Value of waitKey param. - Default: 0. - show (bool): Whether to show the image. - Default: False. - out_file (str or None): The filename to write the image. - Default: None. - - Returns: - img (Tensor): Only if not `show` or `out_file` - """ - img = mmcv.imread(img) - img = img.copy() - if isinstance(result, tuple): - bbox_result, segm_result = result - if isinstance(segm_result, tuple): - segm_result = segm_result[0] # ms rcnn - else: - bbox_result, segm_result = result, None - bboxes = np.vstack(bbox_result) - labels = [ - np.full(bbox.shape[0], i, dtype=np.int32) - for i, bbox in enumerate(bbox_result) - ] - labels = np.concatenate(labels) - # draw segmentation masks - segms = None - if segm_result is not None and len(labels) > 0: # non empty - segms = mmcv.concat_list(segm_result) - if isinstance(segms[0], torch.Tensor): - segms = torch.stack(segms, dim=0).detach().cpu().numpy() - else: - segms = np.stack(segms, axis=0) - # if out_file specified, do not show image in window - if out_file is not None: - show = False - # draw bounding boxes - img = imshow_det_bboxes( - img, - bboxes, - labels, - segms, - class_names=self.CLASSES, - score_thr=score_thr, - bbox_color=bbox_color, - text_color=text_color, - mask_color=mask_color, - thickness=thickness, - font_size=font_size, - win_name=win_name, - show=show, - wait_time=wait_time, - out_file=out_file) - - if not (show or out_file): - return img - - def onnx_export(self, img, img_metas): - raise NotImplementedError(f'{self.__class__.__name__} does ' - f'not support ONNX EXPORT') diff --git a/cv/detection/autoassign/pytorch/mmdet/models/detectors/single_stage.py b/cv/detection/autoassign/pytorch/mmdet/models/detectors/single_stage.py deleted file mode 100755 index 126581e67..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/detectors/single_stage.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch - -from mmdet.core import bbox2result -from ..builder import DETECTORS, build_backbone, build_head, build_neck -from .base import BaseDetector - - -@DETECTORS.register_module() -class SingleStageDetector(BaseDetector): - """Base class for single-stage detectors. - - Single-stage detectors directly and densely predict bounding boxes on the - output features of the backbone+neck. - """ - - def __init__(self, - backbone, - neck=None, - bbox_head=None, - train_cfg=None, - test_cfg=None, - pretrained=None, - init_cfg=None): - super(SingleStageDetector, self).__init__(init_cfg) - if pretrained: - warnings.warn('DeprecationWarning: pretrained is deprecated, ' - 'please use "init_cfg" instead') - backbone.pretrained = pretrained - self.backbone = build_backbone(backbone) - if neck is not None: - self.neck = build_neck(neck) - bbox_head.update(train_cfg=train_cfg) - bbox_head.update(test_cfg=test_cfg) - self.bbox_head = build_head(bbox_head) - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - def extract_feat(self, img): - """Directly extract features from the backbone+neck.""" - x = self.backbone(img) - if self.with_neck: - x = self.neck(x) - return x - - def forward_dummy(self, img): - """Used for computing network flops. - - See `mmdetection/tools/analysis_tools/get_flops.py` - """ - x = self.extract_feat(img) - outs = self.bbox_head(x) - return outs - - def forward_train(self, - img, - img_metas, - gt_bboxes, - gt_labels, - gt_bboxes_ignore=None): - """ - Args: - img (Tensor): Input images of shape (N, C, H, W). - Typically these should be mean centered and std scaled. - img_metas (list[dict]): A List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys see - :class:`mmdet.datasets.pipelines.Collect`. - gt_bboxes (list[Tensor]): Each item are the truth boxes for each - image in [tl_x, tl_y, br_x, br_y] format. - gt_labels (list[Tensor]): Class indices corresponding to each box - gt_bboxes_ignore (None | list[Tensor]): Specify which bounding - boxes can be ignored when computing the loss. - - Returns: - dict[str, Tensor]: A dictionary of loss components. - """ - super(SingleStageDetector, self).forward_train(img, img_metas) - x = self.extract_feat(img) - losses = self.bbox_head.forward_train(x, img_metas, gt_bboxes, - gt_labels, gt_bboxes_ignore) - return losses - - def simple_test(self, img, img_metas, rescale=False): - """Test function without test-time augmentation. - - Args: - img (torch.Tensor): Images with shape (N, C, H, W). - img_metas (list[dict]): List of image information. - rescale (bool, optional): Whether to rescale the results. - Defaults to False. - - Returns: - list[list[np.ndarray]]: BBox results of each image and classes. - The outer list corresponds to each image. The inner list - corresponds to each class. - """ - feat = self.extract_feat(img) - results_list = self.bbox_head.simple_test( - feat, img_metas, rescale=rescale) - bbox_results = [ - bbox2result(det_bboxes, det_labels, self.bbox_head.num_classes) - for det_bboxes, det_labels in results_list - ] - return bbox_results - - def aug_test(self, imgs, img_metas, rescale=False): - """Test function with test time augmentation. - - Args: - imgs (list[Tensor]): the outer list indicates test-time - augmentations and inner Tensor should have a shape NxCxHxW, - which contains all images in the batch. - img_metas (list[list[dict]]): the outer list indicates test-time - augs (multiscale, flip, etc.) and the inner list indicates - images in a batch. each dict has image information. - rescale (bool, optional): Whether to rescale the results. - Defaults to False. - - Returns: - list[list[np.ndarray]]: BBox results of each image and classes. - The outer list corresponds to each image. The inner list - corresponds to each class. - """ - assert hasattr(self.bbox_head, 'aug_test'), \ - f'{self.bbox_head.__class__.__name__}' \ - ' does not support test-time augmentation' - - feats = self.extract_feats(imgs) - results_list = self.bbox_head.aug_test( - feats, img_metas, rescale=rescale) - bbox_results = [ - bbox2result(det_bboxes, det_labels, self.bbox_head.num_classes) - for det_bboxes, det_labels in results_list - ] - return bbox_results - - def onnx_export(self, img, img_metas, with_nms=True): - """Test function without test time augmentation. - - Args: - img (torch.Tensor): input images. - img_metas (list[dict]): List of image information. - - Returns: - tuple[Tensor, Tensor]: dets of shape [N, num_det, 5] - and class labels of shape [N, num_det]. - """ - x = self.extract_feat(img) - outs = self.bbox_head(x) - # get origin input shape to support onnx dynamic shape - - # get shape as tensor - img_shape = torch._shape_as_tensor(img)[2:] - img_metas[0]['img_shape_for_onnx'] = img_shape - # get pad input shape to support onnx dynamic shape for exporting - # `CornerNet` and `CentripetalNet`, which 'pad_shape' is used - # for inference - img_metas[0]['pad_shape_for_onnx'] = img_shape - - if len(outs) == 2: - # add dummy score_factor - outs = (*outs, None) - # TODO Can we change to `get_bboxes` when `onnx_export` fail - det_bboxes, det_labels = self.bbox_head.onnx_export( - *outs, img_metas, with_nms=with_nms) - - return det_bboxes, det_labels diff --git a/cv/detection/autoassign/pytorch/mmdet/models/losses/__init__.py b/cv/detection/autoassign/pytorch/mmdet/models/losses/__init__.py deleted file mode 100755 index bc21fee20..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/losses/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .accuracy import Accuracy, accuracy -from .cross_entropy_loss import (CrossEntropyLoss, binary_cross_entropy, - cross_entropy, mask_cross_entropy) -from .focal_loss import FocalLoss, sigmoid_focal_loss -from .iou_loss import (BoundedIoULoss, CIoULoss, DIoULoss, GIoULoss, IoULoss, - bounded_iou_loss, iou_loss) diff --git a/cv/detection/autoassign/pytorch/mmdet/models/losses/accuracy.py b/cv/detection/autoassign/pytorch/mmdet/models/losses/accuracy.py deleted file mode 100755 index 5637c21e6..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/losses/accuracy.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch.nn as nn - - -@mmcv.jit(coderize=True) -def accuracy(pred, target, topk=1, thresh=None): - """Calculate accuracy according to the prediction and target. - - Args: - pred (torch.Tensor): The model prediction, shape (N, num_class) - target (torch.Tensor): The target of each prediction, shape (N, ) - topk (int | tuple[int], optional): If the predictions in ``topk`` - matches the target, the predictions will be regarded as - correct ones. Defaults to 1. - thresh (float, optional): If not None, predictions with scores under - this threshold are considered incorrect. Default to None. - - Returns: - float | tuple[float]: If the input ``topk`` is a single integer, - the function will return a single float as accuracy. If - ``topk`` is a tuple containing multiple integers, the - function will return a tuple containing accuracies of - each ``topk`` number. - """ - assert isinstance(topk, (int, tuple)) - if isinstance(topk, int): - topk = (topk, ) - return_single = True - else: - return_single = False - - maxk = max(topk) - if pred.size(0) == 0: - accu = [pred.new_tensor(0.) for i in range(len(topk))] - return accu[0] if return_single else accu - assert pred.ndim == 2 and target.ndim == 1 - assert pred.size(0) == target.size(0) - assert maxk <= pred.size(1), \ - f'maxk {maxk} exceeds pred dimension {pred.size(1)}' - pred_value, pred_label = pred.topk(maxk, dim=1) - pred_label = pred_label.t() # transpose to shape (maxk, N) - correct = pred_label.eq(target.view(1, -1).expand_as(pred_label)) - if thresh is not None: - # Only prediction values larger than thresh are counted as correct - correct = correct & (pred_value > thresh).t() - res = [] - for k in topk: - correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) - res.append(correct_k.mul_(100.0 / pred.size(0))) - return res[0] if return_single else res - - -class Accuracy(nn.Module): - - def __init__(self, topk=(1, ), thresh=None): - """Module to calculate the accuracy. - - Args: - topk (tuple, optional): The criterion used to calculate the - accuracy. Defaults to (1,). - thresh (float, optional): If not None, predictions with scores - under this threshold are considered incorrect. Default to None. - """ - super().__init__() - self.topk = topk - self.thresh = thresh - - def forward(self, pred, target): - """Forward function to calculate accuracy. - - Args: - pred (torch.Tensor): Prediction of models. - target (torch.Tensor): Target for each prediction. - - Returns: - tuple[float]: The accuracies under different topk criterions. - """ - return accuracy(pred, target, self.topk, self.thresh) diff --git a/cv/detection/autoassign/pytorch/mmdet/models/losses/cross_entropy_loss.py b/cv/detection/autoassign/pytorch/mmdet/models/losses/cross_entropy_loss.py deleted file mode 100755 index 147f65c61..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/losses/cross_entropy_loss.py +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..builder import LOSSES -from .utils import weight_reduce_loss - - -def cross_entropy(pred, - label, - weight=None, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=-100, - avg_non_ignore=False): - """Calculate the CrossEntropy loss. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - label (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - reduction (str, optional): The method used to reduce the loss. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (int | None): The label index to be ignored. - If None, it will be set to default value. Default: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - - Returns: - torch.Tensor: The calculated loss - """ - # The default value of ignore_index is the same as F.cross_entropy - ignore_index = -100 if ignore_index is None else ignore_index - # element-wise losses - loss = F.cross_entropy( - pred, - label, - weight=class_weight, - reduction='none', - ignore_index=ignore_index) - - # average loss over non-ignored elements - # pytorch's official cross_entropy average loss over non-ignored elements - # refer to https://github.com/pytorch/pytorch/blob/56b43f4fec1f76953f15a627694d4bba34588969/torch/nn/functional.py#L2660 # noqa - if (avg_factor is None) and avg_non_ignore and reduction == 'mean': - avg_factor = label.numel() - (label == ignore_index).sum().item() - - # apply weights and do the reduction - if weight is not None: - weight = weight.float() - loss = weight_reduce_loss( - loss, weight=weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def _expand_onehot_labels(labels, label_weights, label_channels, ignore_index): - """Expand onehot labels to match the size of prediction.""" - bin_labels = labels.new_full((labels.size(0), label_channels), 0) - valid_mask = (labels >= 0) & (labels != ignore_index) - inds = torch.nonzero( - valid_mask & (labels < label_channels), as_tuple=False) - - if inds.numel() > 0: - bin_labels[inds, labels[inds]] = 1 - - valid_mask = valid_mask.view(-1, 1).expand(labels.size(0), - label_channels).float() - if label_weights is None: - bin_label_weights = valid_mask - else: - bin_label_weights = label_weights.view(-1, 1).repeat(1, label_channels) - bin_label_weights *= valid_mask - - return bin_labels, bin_label_weights, valid_mask - - -def binary_cross_entropy(pred, - label, - weight=None, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=-100, - avg_non_ignore=False): - """Calculate the binary CrossEntropy loss. - - Args: - pred (torch.Tensor): The prediction with shape (N, 1) or (N, ). - When the shape of pred is (N, 1), label will be expanded to - one-hot format, and when the shape of pred is (N, ), label - will not be expanded to one-hot format. - label (torch.Tensor): The learning label of the prediction, - with shape (N, ). - weight (torch.Tensor, optional): Sample-wise loss weight. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (int | None): The label index to be ignored. - If None, it will be set to default value. Default: -100. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - - Returns: - torch.Tensor: The calculated loss. - """ - # The default value of ignore_index is the same as F.cross_entropy - ignore_index = -100 if ignore_index is None else ignore_index - - if pred.dim() != label.dim(): - label, weight, valid_mask = _expand_onehot_labels( - label, weight, pred.size(-1), ignore_index) - else: - # should mask out the ignored elements - valid_mask = ((label >= 0) & (label != ignore_index)).float() - if weight is not None: - # The inplace writing method will have a mismatched broadcast - # shape error if the weight and valid_mask dimensions - # are inconsistent such as (B,N,1) and (B,N,C). - weight = weight * valid_mask - else: - weight = valid_mask - - # average loss over non-ignored elements - if (avg_factor is None) and avg_non_ignore and reduction == 'mean': - avg_factor = valid_mask.sum().item() - - # weighted element-wise losses - weight = weight.float() - loss = F.binary_cross_entropy_with_logits( - pred, label.float(), pos_weight=class_weight, reduction='none') - # do the reduction for the weighted loss - loss = weight_reduce_loss( - loss, weight, reduction=reduction, avg_factor=avg_factor) - - return loss - - -def mask_cross_entropy(pred, - target, - label, - reduction='mean', - avg_factor=None, - class_weight=None, - ignore_index=None, - **kwargs): - """Calculate the CrossEntropy loss for masks. - - Args: - pred (torch.Tensor): The prediction with shape (N, C, *), C is the - number of classes. The trailing * indicates arbitrary shape. - target (torch.Tensor): The learning label of the prediction. - label (torch.Tensor): ``label`` indicates the class label of the mask - corresponding object. This will be used to select the mask in the - of the class which the object belongs to when the mask prediction - if not class-agnostic. - reduction (str, optional): The method used to reduce the loss. - Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - class_weight (list[float], optional): The weight for each class. - ignore_index (None): Placeholder, to be consistent with other loss. - Default: None. - - Returns: - torch.Tensor: The calculated loss - - Example: - >>> N, C = 3, 11 - >>> H, W = 2, 2 - >>> pred = torch.randn(N, C, H, W) * 1000 - >>> target = torch.rand(N, H, W) - >>> label = torch.randint(0, C, size=(N,)) - >>> reduction = 'mean' - >>> avg_factor = None - >>> class_weights = None - >>> loss = mask_cross_entropy(pred, target, label, reduction, - >>> avg_factor, class_weights) - >>> assert loss.shape == (1,) - """ - assert ignore_index is None, 'BCE loss does not support ignore_index' - # TODO: handle these two reserved arguments - assert reduction == 'mean' and avg_factor is None - num_rois = pred.size()[0] - inds = torch.arange(0, num_rois, dtype=torch.long, device=pred.device) - pred_slice = pred[inds, label].squeeze(1) - return F.binary_cross_entropy_with_logits( - pred_slice, target, weight=class_weight, reduction='mean')[None] - - -@LOSSES.register_module() -class CrossEntropyLoss(nn.Module): - - def __init__(self, - use_sigmoid=False, - use_mask=False, - reduction='mean', - class_weight=None, - ignore_index=None, - loss_weight=1.0, - avg_non_ignore=False): - """CrossEntropyLoss. - - Args: - use_sigmoid (bool, optional): Whether the prediction uses sigmoid - of softmax. Defaults to False. - use_mask (bool, optional): Whether to use mask cross entropy loss. - Defaults to False. - reduction (str, optional): . Defaults to 'mean'. - Options are "none", "mean" and "sum". - class_weight (list[float], optional): Weight of each class. - Defaults to None. - ignore_index (int | None): The label index to be ignored. - Defaults to None. - loss_weight (float, optional): Weight of the loss. Defaults to 1.0. - avg_non_ignore (bool): The flag decides to whether the loss is - only averaged over non-ignored targets. Default: False. - """ - super(CrossEntropyLoss, self).__init__() - assert (use_sigmoid is False) or (use_mask is False) - self.use_sigmoid = use_sigmoid - self.use_mask = use_mask - self.reduction = reduction - self.loss_weight = loss_weight - self.class_weight = class_weight - self.ignore_index = ignore_index - self.avg_non_ignore = avg_non_ignore - if ((ignore_index is not None) and not self.avg_non_ignore - and self.reduction == 'mean'): - warnings.warn( - 'Default ``avg_non_ignore`` is False, if you would like to ' - 'ignore the certain label and average loss over non-ignore ' - 'labels, which is the same with PyTorch official ' - 'cross_entropy, set ``avg_non_ignore=True``.') - - if self.use_sigmoid: - self.cls_criterion = binary_cross_entropy - elif self.use_mask: - self.cls_criterion = mask_cross_entropy - else: - self.cls_criterion = cross_entropy - - def extra_repr(self): - """Extra repr.""" - s = f'avg_non_ignore={self.avg_non_ignore}' - return s - - def forward(self, - cls_score, - label, - weight=None, - avg_factor=None, - reduction_override=None, - ignore_index=None, - **kwargs): - """Forward function. - - Args: - cls_score (torch.Tensor): The prediction. - label (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - reduction_override (str, optional): The method used to reduce the - loss. Options are "none", "mean" and "sum". - ignore_index (int | None): The label index to be ignored. - If not None, it will override the default value. Default: None. - Returns: - torch.Tensor: The calculated loss. - """ - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if ignore_index is None: - ignore_index = self.ignore_index - - if self.class_weight is not None: - class_weight = cls_score.new_tensor( - self.class_weight, device=cls_score.device) - else: - class_weight = None - loss_cls = self.loss_weight * self.cls_criterion( - cls_score, - label, - weight, - class_weight=class_weight, - reduction=reduction, - avg_factor=avg_factor, - ignore_index=ignore_index, - avg_non_ignore=self.avg_non_ignore, - **kwargs) - return loss_cls diff --git a/cv/detection/autoassign/pytorch/mmdet/models/losses/focal_loss.py b/cv/detection/autoassign/pytorch/mmdet/models/losses/focal_loss.py deleted file mode 100755 index e27a33cb7..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/losses/focal_loss.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.ops import sigmoid_focal_loss as _sigmoid_focal_loss - -from ..builder import LOSSES -from .utils import weight_reduce_loss - - -# This method is only for debugging -def py_sigmoid_focal_loss(pred, - target, - weight=None, - gamma=2.0, - alpha=0.25, - reduction='mean', - avg_factor=None): - """PyTorch version of `Focal Loss `_. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the - number of classes - target (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float, optional): A balanced form for Focal Loss. - Defaults to 0.25. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - pred_sigmoid = pred.sigmoid() - target = target.type_as(pred) - pt = (1 - pred_sigmoid) * target + pred_sigmoid * (1 - target) - focal_weight = (alpha * target + (1 - alpha) * - (1 - target)) * pt.pow(gamma) - loss = F.binary_cross_entropy_with_logits( - pred, target, reduction='none') * focal_weight - if weight is not None: - if weight.shape != loss.shape: - if weight.size(0) == loss.size(0): - # For most cases, weight is of shape (num_priors, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - else: - # Sometimes, weight per anchor per class is also needed. e.g. - # in FSAF. But it may be flattened of shape - # (num_priors x num_class, ), while loss is still of shape - # (num_priors, num_class). - assert weight.numel() == loss.numel() - weight = weight.view(loss.size(0), -1) - assert weight.ndim == loss.ndim - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - -def py_focal_loss_with_prob(pred, - target, - weight=None, - gamma=2.0, - alpha=0.25, - reduction='mean', - avg_factor=None): - """PyTorch version of `Focal Loss `_. - Different from `py_sigmoid_focal_loss`, this function accepts probability - as input. - - Args: - pred (torch.Tensor): The prediction probability with shape (N, C), - C is the number of classes. - target (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float, optional): A balanced form for Focal Loss. - Defaults to 0.25. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - num_classes = pred.size(1) - target = F.one_hot(target, num_classes=num_classes + 1) - target = target[:, :num_classes] - - target = target.type_as(pred) - pt = (1 - pred) * target + pred * (1 - target) - focal_weight = (alpha * target + (1 - alpha) * - (1 - target)) * pt.pow(gamma) - loss = F.binary_cross_entropy( - pred, target, reduction='none') * focal_weight - if weight is not None: - if weight.shape != loss.shape: - if weight.size(0) == loss.size(0): - # For most cases, weight is of shape (num_priors, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - else: - # Sometimes, weight per anchor per class is also needed. e.g. - # in FSAF. But it may be flattened of shape - # (num_priors x num_class, ), while loss is still of shape - # (num_priors, num_class). - assert weight.numel() == loss.numel() - weight = weight.view(loss.size(0), -1) - assert weight.ndim == loss.ndim - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - -def sigmoid_focal_loss(pred, - target, - weight=None, - gamma=2.0, - alpha=0.25, - reduction='mean', - avg_factor=None): - r"""A warpper of cuda version `Focal Loss - `_. - - Args: - pred (torch.Tensor): The prediction with shape (N, C), C is the number - of classes. - target (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): Sample-wise loss weight. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float, optional): A balanced form for Focal Loss. - Defaults to 0.25. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and "sum". - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - """ - # Function.apply does not accept keyword arguments, so the decorator - # "weighted_loss" is not applicable - loss = _sigmoid_focal_loss(pred.contiguous(), target.contiguous(), gamma, - alpha, None, 'none') - if weight is not None: - if weight.shape != loss.shape: - if weight.size(0) == loss.size(0): - # For most cases, weight is of shape (num_priors, ), - # which means it does not have the second axis num_class - weight = weight.view(-1, 1) - else: - # Sometimes, weight per anchor per class is also needed. e.g. - # in FSAF. But it may be flattened of shape - # (num_priors x num_class, ), while loss is still of shape - # (num_priors, num_class). - assert weight.numel() == loss.numel() - weight = weight.view(loss.size(0), -1) - assert weight.ndim == loss.ndim - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - -@LOSSES.register_module() -class FocalLoss(nn.Module): - - def __init__(self, - use_sigmoid=True, - gamma=2.0, - alpha=0.25, - reduction='mean', - loss_weight=1.0, - activated=False): - """`Focal Loss `_ - - Args: - use_sigmoid (bool, optional): Whether to the prediction is - used for sigmoid or softmax. Defaults to True. - gamma (float, optional): The gamma for calculating the modulating - factor. Defaults to 2.0. - alpha (float, optional): A balanced form for Focal Loss. - Defaults to 0.25. - reduction (str, optional): The method used to reduce the loss into - a scalar. Defaults to 'mean'. Options are "none", "mean" and - "sum". - loss_weight (float, optional): Weight of loss. Defaults to 1.0. - activated (bool, optional): Whether the input is activated. - If True, it means the input has been activated and can be - treated as probabilities. Else, it should be treated as logits. - Defaults to False. - """ - super(FocalLoss, self).__init__() - assert use_sigmoid is True, 'Only sigmoid focal loss supported now.' - self.use_sigmoid = use_sigmoid - self.gamma = gamma - self.alpha = alpha - self.reduction = reduction - self.loss_weight = loss_weight - self.activated = activated - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None): - """Forward function. - - Args: - pred (torch.Tensor): The prediction. - target (torch.Tensor): The learning label of the prediction. - weight (torch.Tensor, optional): The weight of loss for each - prediction. Defaults to None. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - reduction_override (str, optional): The reduction method used to - override the original reduction method of the loss. - Options are "none", "mean" and "sum". - - Returns: - torch.Tensor: The calculated loss - """ - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if self.use_sigmoid: - if self.activated: - calculate_loss_func = py_focal_loss_with_prob - else: - if torch.cuda.is_available() and pred.is_cuda: - calculate_loss_func = sigmoid_focal_loss - else: - num_classes = pred.size(1) - target = F.one_hot(target, num_classes=num_classes + 1) - target = target[:, :num_classes] - calculate_loss_func = py_sigmoid_focal_loss - - loss_cls = self.loss_weight * calculate_loss_func( - pred, - target, - weight, - gamma=self.gamma, - alpha=self.alpha, - reduction=reduction, - avg_factor=avg_factor) - - else: - raise NotImplementedError - return loss_cls diff --git a/cv/detection/autoassign/pytorch/mmdet/models/losses/iou_loss.py b/cv/detection/autoassign/pytorch/mmdet/models/losses/iou_loss.py deleted file mode 100755 index 1c9473c92..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/losses/iou_loss.py +++ /dev/null @@ -1,474 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -import warnings - -import mmcv -import torch -import torch.nn as nn - -from mmdet.core import bbox_overlaps -from ..builder import LOSSES -from .utils import weighted_loss - - -@mmcv.jit(derivate=True, coderize=True) -@weighted_loss -def iou_loss(pred, target, linear=False, mode='log', eps=1e-6): - """IoU loss. - - Computing the IoU loss between a set of predicted bboxes and target bboxes. - The loss is calculated as negative log of IoU. - - Args: - pred (torch.Tensor): Predicted bboxes of format (x1, y1, x2, y2), - shape (n, 4). - target (torch.Tensor): Corresponding gt bboxes, shape (n, 4). - linear (bool, optional): If True, use linear scale of loss instead of - log scale. Default: False. - mode (str): Loss scaling mode, including "linear", "square", and "log". - Default: 'log' - eps (float): Eps to avoid log(0). - - Return: - torch.Tensor: Loss tensor. - """ - assert mode in ['linear', 'square', 'log'] - if linear: - mode = 'linear' - warnings.warn('DeprecationWarning: Setting "linear=True" in ' - 'iou_loss is deprecated, please use "mode=`linear`" ' - 'instead.') - ious = bbox_overlaps(pred, target, is_aligned=True).clamp(min=eps) - if mode == 'linear': - loss = 1 - ious - elif mode == 'square': - loss = 1 - ious**2 - elif mode == 'log': - loss = -ious.log() - else: - raise NotImplementedError - return loss - - -@mmcv.jit(derivate=True, coderize=True) -@weighted_loss -def bounded_iou_loss(pred, target, beta=0.2, eps=1e-3): - """BIoULoss. - - This is an implementation of paper - `Improving Object Localization with Fitness NMS and Bounded IoU Loss. - `_. - - Args: - pred (torch.Tensor): Predicted bboxes. - target (torch.Tensor): Target bboxes. - beta (float): beta parameter in smoothl1. - eps (float): eps to avoid NaN. - """ - pred_ctrx = (pred[:, 0] + pred[:, 2]) * 0.5 - pred_ctry = (pred[:, 1] + pred[:, 3]) * 0.5 - pred_w = pred[:, 2] - pred[:, 0] - pred_h = pred[:, 3] - pred[:, 1] - with torch.no_grad(): - target_ctrx = (target[:, 0] + target[:, 2]) * 0.5 - target_ctry = (target[:, 1] + target[:, 3]) * 0.5 - target_w = target[:, 2] - target[:, 0] - target_h = target[:, 3] - target[:, 1] - - dx = target_ctrx - pred_ctrx - dy = target_ctry - pred_ctry - - loss_dx = 1 - torch.max( - (target_w - 2 * dx.abs()) / - (target_w + 2 * dx.abs() + eps), torch.zeros_like(dx)) - loss_dy = 1 - torch.max( - (target_h - 2 * dy.abs()) / - (target_h + 2 * dy.abs() + eps), torch.zeros_like(dy)) - loss_dw = 1 - torch.min(target_w / (pred_w + eps), pred_w / - (target_w + eps)) - loss_dh = 1 - torch.min(target_h / (pred_h + eps), pred_h / - (target_h + eps)) - # view(..., -1) does not work for empty tensor - loss_comb = torch.stack([loss_dx, loss_dy, loss_dw, loss_dh], - dim=-1).flatten(1) - - loss = torch.where(loss_comb < beta, 0.5 * loss_comb * loss_comb / beta, - loss_comb - 0.5 * beta) - return loss - - -@mmcv.jit(derivate=True, coderize=True) -@weighted_loss -def giou_loss(pred, target, eps=1e-7): - r"""`Generalized Intersection over Union: A Metric and A Loss for Bounding - Box Regression `_. - - Args: - pred (torch.Tensor): Predicted bboxes of format (x1, y1, x2, y2), - shape (n, 4). - target (torch.Tensor): Corresponding gt bboxes, shape (n, 4). - eps (float): Eps to avoid log(0). - - Return: - Tensor: Loss tensor. - """ - gious = bbox_overlaps(pred, target, mode='giou', is_aligned=True, eps=eps) - loss = 1 - gious - return loss - - -@mmcv.jit(derivate=True, coderize=True) -@weighted_loss -def diou_loss(pred, target, eps=1e-7): - r"""`Implementation of Distance-IoU Loss: Faster and Better - Learning for Bounding Box Regression, https://arxiv.org/abs/1911.08287`_. - - Code is modified from https://github.com/Zzh-tju/DIoU. - - Args: - pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2), - shape (n, 4). - target (Tensor): Corresponding gt bboxes, shape (n, 4). - eps (float): Eps to avoid log(0). - Return: - Tensor: Loss tensor. - """ - # overlap - lt = torch.max(pred[:, :2], target[:, :2]) - rb = torch.min(pred[:, 2:], target[:, 2:]) - wh = (rb - lt).clamp(min=0) - overlap = wh[:, 0] * wh[:, 1] - - # union - ap = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) - ag = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) - union = ap + ag - overlap + eps - - # IoU - ious = overlap / union - - # enclose area - enclose_x1y1 = torch.min(pred[:, :2], target[:, :2]) - enclose_x2y2 = torch.max(pred[:, 2:], target[:, 2:]) - enclose_wh = (enclose_x2y2 - enclose_x1y1).clamp(min=0) - - cw = enclose_wh[:, 0] - ch = enclose_wh[:, 1] - - c2 = cw**2 + ch**2 + eps - - b1_x1, b1_y1 = pred[:, 0], pred[:, 1] - b1_x2, b1_y2 = pred[:, 2], pred[:, 3] - b2_x1, b2_y1 = target[:, 0], target[:, 1] - b2_x2, b2_y2 = target[:, 2], target[:, 3] - - left = ((b2_x1 + b2_x2) - (b1_x1 + b1_x2))**2 / 4 - right = ((b2_y1 + b2_y2) - (b1_y1 + b1_y2))**2 / 4 - rho2 = left + right - - # DIoU - dious = ious - rho2 / c2 - loss = 1 - dious - return loss - - -@mmcv.jit(derivate=True, coderize=True) -@weighted_loss -def ciou_loss(pred, target, eps=1e-7): - r"""`Implementation of paper `Enhancing Geometric Factors into - Model Learning and Inference for Object Detection and Instance - Segmentation `_. - - Code is modified from https://github.com/Zzh-tju/CIoU. - - Args: - pred (Tensor): Predicted bboxes of format (x1, y1, x2, y2), - shape (n, 4). - target (Tensor): Corresponding gt bboxes, shape (n, 4). - eps (float): Eps to avoid log(0). - Return: - Tensor: Loss tensor. - """ - # overlap - lt = torch.max(pred[:, :2], target[:, :2]) - rb = torch.min(pred[:, 2:], target[:, 2:]) - wh = (rb - lt).clamp(min=0) - overlap = wh[:, 0] * wh[:, 1] - - # union - ap = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) - ag = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) - union = ap + ag - overlap + eps - - # IoU - ious = overlap / union - - # enclose area - enclose_x1y1 = torch.min(pred[:, :2], target[:, :2]) - enclose_x2y2 = torch.max(pred[:, 2:], target[:, 2:]) - enclose_wh = (enclose_x2y2 - enclose_x1y1).clamp(min=0) - - cw = enclose_wh[:, 0] - ch = enclose_wh[:, 1] - - c2 = cw**2 + ch**2 + eps - - b1_x1, b1_y1 = pred[:, 0], pred[:, 1] - b1_x2, b1_y2 = pred[:, 2], pred[:, 3] - b2_x1, b2_y1 = target[:, 0], target[:, 1] - b2_x2, b2_y2 = target[:, 2], target[:, 3] - - w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps - w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps - - left = ((b2_x1 + b2_x2) - (b1_x1 + b1_x2))**2 / 4 - right = ((b2_y1 + b2_y2) - (b1_y1 + b1_y2))**2 / 4 - rho2 = left + right - - factor = 4 / math.pi**2 - v = factor * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2) - - with torch.no_grad(): - alpha = (ious > 0.5).float() * v / (1 - ious + v) - - # CIoU - cious = ious - (rho2 / c2 + alpha * v) - loss = 1 - cious.clamp(min=-1.0, max=1.0) - return loss - - -@LOSSES.register_module() -class IoULoss(nn.Module): - """IoULoss. - - Computing the IoU loss between a set of predicted bboxes and target bboxes. - - Args: - linear (bool): If True, use linear scale of loss else determined - by mode. Default: False. - eps (float): Eps to avoid log(0). - reduction (str): Options are "none", "mean" and "sum". - loss_weight (float): Weight of loss. - mode (str): Loss scaling mode, including "linear", "square", and "log". - Default: 'log' - """ - - def __init__(self, - linear=False, - eps=1e-6, - reduction='mean', - loss_weight=1.0, - mode='log'): - super(IoULoss, self).__init__() - assert mode in ['linear', 'square', 'log'] - if linear: - mode = 'linear' - warnings.warn('DeprecationWarning: Setting "linear=True" in ' - 'IOULoss is deprecated, please use "mode=`linear`" ' - 'instead.') - self.mode = mode - self.linear = linear - self.eps = eps - self.reduction = reduction - self.loss_weight = loss_weight - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - """Forward function. - - Args: - pred (torch.Tensor): The prediction. - target (torch.Tensor): The learning target of the prediction. - weight (torch.Tensor, optional): The weight of loss for each - prediction. Defaults to None. - avg_factor (int, optional): Average factor that is used to average - the loss. Defaults to None. - reduction_override (str, optional): The reduction method used to - override the original reduction method of the loss. - Defaults to None. Options are "none", "mean" and "sum". - """ - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if (weight is not None) and (not torch.any(weight > 0)) and ( - reduction != 'none'): - if pred.dim() == weight.dim() + 1: - weight = weight.unsqueeze(1) - return (pred * weight).sum() # 0 - if weight is not None and weight.dim() > 1: - # TODO: remove this in the future - # reduce the weight of shape (n, 4) to (n,) to match the - # iou_loss of shape (n,) - assert weight.shape == pred.shape - weight = weight.mean(-1) - loss = self.loss_weight * iou_loss( - pred, - target, - weight, - mode=self.mode, - eps=self.eps, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss - - -@LOSSES.register_module() -class BoundedIoULoss(nn.Module): - - def __init__(self, beta=0.2, eps=1e-3, reduction='mean', loss_weight=1.0): - super(BoundedIoULoss, self).__init__() - self.beta = beta - self.eps = eps - self.reduction = reduction - self.loss_weight = loss_weight - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - if weight is not None and not torch.any(weight > 0): - if pred.dim() == weight.dim() + 1: - weight = weight.unsqueeze(1) - return (pred * weight).sum() # 0 - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - loss = self.loss_weight * bounded_iou_loss( - pred, - target, - weight, - beta=self.beta, - eps=self.eps, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss - - -@LOSSES.register_module() -class GIoULoss(nn.Module): - - def __init__(self, eps=1e-6, reduction='mean', loss_weight=1.0): - super(GIoULoss, self).__init__() - self.eps = eps - self.reduction = reduction - self.loss_weight = loss_weight - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - if weight is not None and not torch.any(weight > 0): - if pred.dim() == weight.dim() + 1: - weight = weight.unsqueeze(1) - return (pred * weight).sum() # 0 - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if weight is not None and weight.dim() > 1: - # TODO: remove this in the future - # reduce the weight of shape (n, 4) to (n,) to match the - # giou_loss of shape (n,) - assert weight.shape == pred.shape - weight = weight.mean(-1) - loss = self.loss_weight * giou_loss( - pred, - target, - weight, - eps=self.eps, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss - - -@LOSSES.register_module() -class DIoULoss(nn.Module): - - def __init__(self, eps=1e-6, reduction='mean', loss_weight=1.0): - super(DIoULoss, self).__init__() - self.eps = eps - self.reduction = reduction - self.loss_weight = loss_weight - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - if weight is not None and not torch.any(weight > 0): - if pred.dim() == weight.dim() + 1: - weight = weight.unsqueeze(1) - return (pred * weight).sum() # 0 - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if weight is not None and weight.dim() > 1: - # TODO: remove this in the future - # reduce the weight of shape (n, 4) to (n,) to match the - # giou_loss of shape (n,) - assert weight.shape == pred.shape - weight = weight.mean(-1) - loss = self.loss_weight * diou_loss( - pred, - target, - weight, - eps=self.eps, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss - - -@LOSSES.register_module() -class CIoULoss(nn.Module): - - def __init__(self, eps=1e-6, reduction='mean', loss_weight=1.0): - super(CIoULoss, self).__init__() - self.eps = eps - self.reduction = reduction - self.loss_weight = loss_weight - - def forward(self, - pred, - target, - weight=None, - avg_factor=None, - reduction_override=None, - **kwargs): - if weight is not None and not torch.any(weight > 0): - if pred.dim() == weight.dim() + 1: - weight = weight.unsqueeze(1) - return (pred * weight).sum() # 0 - assert reduction_override in (None, 'none', 'mean', 'sum') - reduction = ( - reduction_override if reduction_override else self.reduction) - if weight is not None and weight.dim() > 1: - # TODO: remove this in the future - # reduce the weight of shape (n, 4) to (n,) to match the - # giou_loss of shape (n,) - assert weight.shape == pred.shape - weight = weight.mean(-1) - loss = self.loss_weight * ciou_loss( - pred, - target, - weight, - eps=self.eps, - reduction=reduction, - avg_factor=avg_factor, - **kwargs) - return loss diff --git a/cv/detection/autoassign/pytorch/mmdet/models/losses/utils.py b/cv/detection/autoassign/pytorch/mmdet/models/losses/utils.py deleted file mode 100755 index 4c7241d3f..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/losses/utils.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import mmcv -import torch -import torch.nn.functional as F - - -def reduce_loss(loss, reduction): - """Reduce loss as specified. - - Args: - loss (Tensor): Elementwise loss tensor. - reduction (str): Options are "none", "mean" and "sum". - - Return: - Tensor: Reduced loss tensor. - """ - reduction_enum = F._Reduction.get_enum(reduction) - # none: 0, elementwise_mean:1, sum: 2 - if reduction_enum == 0: - return loss - elif reduction_enum == 1: - return loss.mean() - elif reduction_enum == 2: - return loss.sum() - - -@mmcv.jit(derivate=True, coderize=True) -def weight_reduce_loss(loss, weight=None, reduction='mean', avg_factor=None): - """Apply element-wise weight and reduce loss. - - Args: - loss (Tensor): Element-wise loss. - weight (Tensor): Element-wise weights. - reduction (str): Same as built-in losses of PyTorch. - avg_factor (float): Average factor when computing the mean of losses. - - Returns: - Tensor: Processed loss values. - """ - # if weight is specified, apply element-wise weight - if weight is not None: - loss = loss * weight - - # if avg_factor is not specified, just reduce the loss - if avg_factor is None: - loss = reduce_loss(loss, reduction) - else: - # if reduction is mean, then average the loss by avg_factor - if reduction == 'mean': - # Avoid causing ZeroDivisionError when avg_factor is 0.0, - # i.e., all labels of an image belong to ignore index. - eps = torch.finfo(torch.float32).eps - loss = loss.sum() / (avg_factor + eps) - # if reduction is 'none', then do nothing, otherwise raise an error - elif reduction != 'none': - raise ValueError('avg_factor can not be used with reduction="sum"') - return loss - - -def weighted_loss(loss_func): - """Create a weighted version of a given loss function. - - To use this decorator, the loss function must have the signature like - `loss_func(pred, target, **kwargs)`. The function only needs to compute - element-wise loss without any reduction. This decorator will add weight - and reduction arguments to the function. The decorated function will have - the signature like `loss_func(pred, target, weight=None, reduction='mean', - avg_factor=None, **kwargs)`. - - :Example: - - >>> import torch - >>> @weighted_loss - >>> def l1_loss(pred, target): - >>> return (pred - target).abs() - - >>> pred = torch.Tensor([0, 2, 3]) - >>> target = torch.Tensor([1, 1, 1]) - >>> weight = torch.Tensor([1, 0, 1]) - - >>> l1_loss(pred, target) - tensor(1.3333) - >>> l1_loss(pred, target, weight) - tensor(1.) - >>> l1_loss(pred, target, reduction='none') - tensor([1., 1., 2.]) - >>> l1_loss(pred, target, weight, avg_factor=2) - tensor(1.5000) - """ - - @functools.wraps(loss_func) - def wrapper(pred, - target, - weight=None, - reduction='mean', - avg_factor=None, - **kwargs): - # get element-wise loss - loss = loss_func(pred, target, **kwargs) - loss = weight_reduce_loss(loss, weight, reduction, avg_factor) - return loss - - return wrapper diff --git a/cv/detection/autoassign/pytorch/mmdet/models/necks/__init__.py b/cv/detection/autoassign/pytorch/mmdet/models/necks/__init__.py deleted file mode 100755 index 80e9d536c..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/necks/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .fpn import FPN diff --git a/cv/detection/autoassign/pytorch/mmdet/models/necks/fpn.py b/cv/detection/autoassign/pytorch/mmdet/models/necks/fpn.py deleted file mode 100755 index d5d987b2d..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/necks/fpn.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule, auto_fp16 - -from ..builder import NECKS - - -@NECKS.register_module() -class FPN(BaseModule): - r"""Feature Pyramid Network. - - This is an implementation of paper `Feature Pyramid Networks for Object - Detection `_. - - Args: - in_channels (list[int]): Number of input channels per scale. - out_channels (int): Number of output channels (used at each scale). - num_outs (int): Number of output scales. - start_level (int): Index of the start input backbone level used to - build the feature pyramid. Default: 0. - end_level (int): Index of the end input backbone level (exclusive) to - build the feature pyramid. Default: -1, which means the last level. - add_extra_convs (bool | str): If bool, it decides whether to add conv - layers on top of the original feature maps. Default to False. - If True, it is equivalent to `add_extra_convs='on_input'`. - If str, it specifies the source feature map of the extra convs. - Only the following options are allowed - - - 'on_input': Last feat map of neck inputs (i.e. backbone feature). - - 'on_lateral': Last feature map after lateral convs. - - 'on_output': The last output feature map after fpn convs. - relu_before_extra_convs (bool): Whether to apply relu before the extra - conv. Default: False. - no_norm_on_lateral (bool): Whether to apply norm on lateral. - Default: False. - conv_cfg (dict): Config dict for convolution layer. Default: None. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer in ConvModule. - Default: None. - upsample_cfg (dict): Config dict for interpolate layer. - Default: dict(mode='nearest'). - init_cfg (dict or list[dict], optional): Initialization config dict. - - Example: - >>> import torch - >>> in_channels = [2, 3, 5, 7] - >>> scales = [340, 170, 84, 43] - >>> inputs = [torch.rand(1, c, s, s) - ... for c, s in zip(in_channels, scales)] - >>> self = FPN(in_channels, 11, len(in_channels)).eval() - >>> outputs = self.forward(inputs) - >>> for i in range(len(outputs)): - ... print(f'outputs[{i}].shape = {outputs[i].shape}') - outputs[0].shape = torch.Size([1, 11, 340, 340]) - outputs[1].shape = torch.Size([1, 11, 170, 170]) - outputs[2].shape = torch.Size([1, 11, 84, 84]) - outputs[3].shape = torch.Size([1, 11, 43, 43]) - """ - - def __init__(self, - in_channels, - out_channels, - num_outs, - start_level=0, - end_level=-1, - add_extra_convs=False, - relu_before_extra_convs=False, - no_norm_on_lateral=False, - conv_cfg=None, - norm_cfg=None, - act_cfg=None, - upsample_cfg=dict(mode='nearest'), - init_cfg=dict( - type='Xavier', layer='Conv2d', distribution='uniform')): - super(FPN, self).__init__(init_cfg) - assert isinstance(in_channels, list) - self.in_channels = in_channels - self.out_channels = out_channels - self.num_ins = len(in_channels) - self.num_outs = num_outs - self.relu_before_extra_convs = relu_before_extra_convs - self.no_norm_on_lateral = no_norm_on_lateral - self.fp16_enabled = False - self.upsample_cfg = upsample_cfg.copy() - - if end_level == -1 or end_level == self.num_ins - 1: - self.backbone_end_level = self.num_ins - assert num_outs >= self.num_ins - start_level - else: - # if end_level is not the last level, no extra level is allowed - self.backbone_end_level = end_level + 1 - assert end_level < self.num_ins - assert num_outs == end_level - start_level + 1 - self.start_level = start_level - self.end_level = end_level - self.add_extra_convs = add_extra_convs - assert isinstance(add_extra_convs, (str, bool)) - if isinstance(add_extra_convs, str): - # Extra_convs_source choices: 'on_input', 'on_lateral', 'on_output' - assert add_extra_convs in ('on_input', 'on_lateral', 'on_output') - elif add_extra_convs: # True - self.add_extra_convs = 'on_input' - - self.lateral_convs = nn.ModuleList() - self.fpn_convs = nn.ModuleList() - - for i in range(self.start_level, self.backbone_end_level): - l_conv = ConvModule( - in_channels[i], - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg if not self.no_norm_on_lateral else None, - act_cfg=act_cfg, - inplace=False) - fpn_conv = ConvModule( - out_channels, - out_channels, - 3, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - - self.lateral_convs.append(l_conv) - self.fpn_convs.append(fpn_conv) - - # add extra conv layers (e.g., RetinaNet) - extra_levels = num_outs - self.backbone_end_level + self.start_level - if self.add_extra_convs and extra_levels >= 1: - for i in range(extra_levels): - if i == 0 and self.add_extra_convs == 'on_input': - in_channels = self.in_channels[self.backbone_end_level - 1] - else: - in_channels = out_channels - extra_fpn_conv = ConvModule( - in_channels, - out_channels, - 3, - stride=2, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg, - inplace=False) - self.fpn_convs.append(extra_fpn_conv) - - @auto_fp16() - def forward(self, inputs): - """Forward function.""" - assert len(inputs) == len(self.in_channels) - - # build laterals - laterals = [ - lateral_conv(inputs[i + self.start_level]) - for i, lateral_conv in enumerate(self.lateral_convs) - ] - - # build top-down path - used_backbone_levels = len(laterals) - for i in range(used_backbone_levels - 1, 0, -1): - # In some cases, fixing `scale factor` (e.g. 2) is preferred, but - # it cannot co-exist with `size` in `F.interpolate`. - if 'scale_factor' in self.upsample_cfg: - # fix runtime error of "+=" inplace operation in PyTorch 1.10 - laterals[i - 1] = laterals[i - 1] + F.interpolate( - laterals[i], **self.upsample_cfg) - else: - prev_shape = laterals[i - 1].shape[2:] - laterals[i - 1] = laterals[i - 1] + F.interpolate( - laterals[i], size=prev_shape, **self.upsample_cfg) - - # build outputs - # part 1: from original levels - outs = [ - self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels) - ] - # part 2: add extra levels - if self.num_outs > len(outs): - # use max pool to get more levels on top of outputs - # (e.g., Faster R-CNN, Mask R-CNN) - if not self.add_extra_convs: - for i in range(self.num_outs - used_backbone_levels): - outs.append(F.max_pool2d(outs[-1], 1, stride=2)) - # add conv layers on top of original feature maps (RetinaNet) - else: - if self.add_extra_convs == 'on_input': - extra_source = inputs[self.backbone_end_level - 1] - elif self.add_extra_convs == 'on_lateral': - extra_source = laterals[-1] - elif self.add_extra_convs == 'on_output': - extra_source = outs[-1] - else: - raise NotImplementedError - outs.append(self.fpn_convs[used_backbone_levels](extra_source)) - for i in range(used_backbone_levels + 1, self.num_outs): - if self.relu_before_extra_convs: - outs.append(self.fpn_convs[i](F.relu(outs[-1]))) - else: - outs.append(self.fpn_convs[i](outs[-1])) - return tuple(outs) diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/__init__.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/__init__.py deleted file mode 100755 index b4e370824..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .brick_wrappers import AdaptiveAvgPool2d, adaptive_avg_pool2d -from .builder import build_linear_layer, build_transformer -from .ckpt_convert import pvt_convert -from .conv_upsample import ConvUpsample -from .csp_layer import CSPLayer -from .gaussian_target import gaussian_radius, gen_gaussian_target -from .inverted_residual import InvertedResidual -from .make_divisible import make_divisible -from .misc import interpolate_as, sigmoid_geometric_mean -from .normed_predictor import NormedConv2d, NormedLinear -from .panoptic_gt_processing import preprocess_panoptic_gt -from .point_sample import (get_uncertain_point_coords_with_randomness, - get_uncertainty) -from .positional_encoding import (LearnedPositionalEncoding, - SinePositionalEncoding) -from .res_layer import ResLayer, SimplifiedBasicBlock -from .se_layer import DyReLU, SELayer diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/brick_wrappers.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/brick_wrappers.py deleted file mode 100755 index ca0b760a6..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/brick_wrappers.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn.bricks.wrappers import NewEmptyTensorOp, obsolete_torch_version - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def adaptive_avg_pool2d(input, output_size): - """Handle empty batch dimension to adaptive_avg_pool2d. - - Args: - input (tensor): 4D tensor. - output_size (int, tuple[int,int]): the target output size. - """ - if input.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - if isinstance(output_size, int): - output_size = [output_size, output_size] - output_size = [*input.shape[:2], *output_size] - empty = NewEmptyTensorOp.apply(input, output_size) - return empty - else: - return F.adaptive_avg_pool2d(input, output_size) - - -class AdaptiveAvgPool2d(nn.AdaptiveAvgPool2d): - """Handle empty batch dimension to AdaptiveAvgPool2d.""" - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - output_size = self.output_size - if isinstance(output_size, int): - output_size = [output_size, output_size] - else: - output_size = [ - v if v is not None else d - for v, d in zip(output_size, - x.size()[-2:]) - ] - output_size = [*x.shape[:2], *output_size] - empty = NewEmptyTensorOp.apply(x, output_size) - return empty - - return super().forward(x) diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/builder.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/builder.py deleted file mode 100755 index 85843ae74..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/builder.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.utils import Registry, build_from_cfg - -TRANSFORMER = Registry('Transformer') -LINEAR_LAYERS = Registry('linear layers') - - -def build_transformer(cfg, default_args=None): - """Builder for Transformer.""" - return build_from_cfg(cfg, TRANSFORMER, default_args) - - -LINEAR_LAYERS.register_module('Linear', module=nn.Linear) - - -def build_linear_layer(cfg, *args, **kwargs): - """Build linear layer. - Args: - cfg (None or dict): The linear layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an linear layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding linear layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding linear layer. - Returns: - nn.Module: Created linear layer. - """ - if cfg is None: - cfg_ = dict(type='Linear') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in LINEAR_LAYERS: - raise KeyError(f'Unrecognized linear type {layer_type}') - else: - linear_layer = LINEAR_LAYERS.get(layer_type) - - layer = linear_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/ckpt_convert.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/ckpt_convert.py deleted file mode 100755 index 1cbb234d5..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/ckpt_convert.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -# This script consists of several convert functions which -# can modify the weights of model in original repo to be -# pre-trained weights. - -from collections import OrderedDict - -import torch - - -def pvt_convert(ckpt): - new_ckpt = OrderedDict() - # Process the concat between q linear weights and kv linear weights - use_abs_pos_embed = False - use_conv_ffn = False - for k in ckpt.keys(): - if k.startswith('pos_embed'): - use_abs_pos_embed = True - if k.find('dwconv') >= 0: - use_conv_ffn = True - for k, v in ckpt.items(): - if k.startswith('head'): - continue - if k.startswith('norm.'): - continue - if k.startswith('cls_token'): - continue - if k.startswith('pos_embed'): - stage_i = int(k.replace('pos_embed', '')) - new_k = k.replace(f'pos_embed{stage_i}', - f'layers.{stage_i - 1}.1.0.pos_embed') - if stage_i == 4 and v.size(1) == 50: # 1 (cls token) + 7 * 7 - new_v = v[:, 1:, :] # remove cls token - else: - new_v = v - elif k.startswith('patch_embed'): - stage_i = int(k.split('.')[0].replace('patch_embed', '')) - new_k = k.replace(f'patch_embed{stage_i}', - f'layers.{stage_i - 1}.0') - new_v = v - if 'proj.' in new_k: - new_k = new_k.replace('proj.', 'projection.') - elif k.startswith('block'): - stage_i = int(k.split('.')[0].replace('block', '')) - layer_i = int(k.split('.')[1]) - new_layer_i = layer_i + use_abs_pos_embed - new_k = k.replace(f'block{stage_i}.{layer_i}', - f'layers.{stage_i - 1}.1.{new_layer_i}') - new_v = v - if 'attn.q.' in new_k: - sub_item_k = k.replace('q.', 'kv.') - new_k = new_k.replace('q.', 'attn.in_proj_') - new_v = torch.cat([v, ckpt[sub_item_k]], dim=0) - elif 'attn.kv.' in new_k: - continue - elif 'attn.proj.' in new_k: - new_k = new_k.replace('proj.', 'attn.out_proj.') - elif 'attn.sr.' in new_k: - new_k = new_k.replace('sr.', 'sr.') - elif 'mlp.' in new_k: - string = f'{new_k}-' - new_k = new_k.replace('mlp.', 'ffn.layers.') - if 'fc1.weight' in new_k or 'fc2.weight' in new_k: - new_v = v.reshape((*v.shape, 1, 1)) - new_k = new_k.replace('fc1.', '0.') - new_k = new_k.replace('dwconv.dwconv.', '1.') - if use_conv_ffn: - new_k = new_k.replace('fc2.', '4.') - else: - new_k = new_k.replace('fc2.', '3.') - string += f'{new_k} {v.shape}-{new_v.shape}' - elif k.startswith('norm'): - stage_i = int(k[4]) - new_k = k.replace(f'norm{stage_i}', f'layers.{stage_i - 1}.2') - new_v = v - else: - new_k = k - new_v = v - new_ckpt[new_k] = new_v - - return new_ckpt - - -def swin_converter(ckpt): - - new_ckpt = OrderedDict() - - def correct_unfold_reduction_order(x): - out_channel, in_channel = x.shape - x = x.reshape(out_channel, 4, in_channel // 4) - x = x[:, [0, 2, 1, 3], :].transpose(1, - 2).reshape(out_channel, in_channel) - return x - - def correct_unfold_norm_order(x): - in_channel = x.shape[0] - x = x.reshape(4, in_channel // 4) - x = x[[0, 2, 1, 3], :].transpose(0, 1).reshape(in_channel) - return x - - for k, v in ckpt.items(): - if k.startswith('head'): - continue - elif k.startswith('layers'): - new_v = v - if 'attn.' in k: - new_k = k.replace('attn.', 'attn.w_msa.') - elif 'mlp.' in k: - if 'mlp.fc1.' in k: - new_k = k.replace('mlp.fc1.', 'ffn.layers.0.0.') - elif 'mlp.fc2.' in k: - new_k = k.replace('mlp.fc2.', 'ffn.layers.1.') - else: - new_k = k.replace('mlp.', 'ffn.') - elif 'downsample' in k: - new_k = k - if 'reduction.' in k: - new_v = correct_unfold_reduction_order(v) - elif 'norm.' in k: - new_v = correct_unfold_norm_order(v) - else: - new_k = k - new_k = new_k.replace('layers', 'stages', 1) - elif k.startswith('patch_embed'): - new_v = v - if 'proj' in k: - new_k = k.replace('proj', 'projection') - else: - new_k = k - else: - new_v = v - new_k = k - - new_ckpt['backbone.' + new_k] = new_v - - return new_ckpt diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/conv_upsample.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/conv_upsample.py deleted file mode 100755 index 87c673803..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/conv_upsample.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule, ModuleList - - -class ConvUpsample(BaseModule): - """ConvUpsample performs 2x upsampling after Conv. - - There are several `ConvModule` layers. In the first few layers, upsampling - will be applied after each layer of convolution. The number of upsampling - must be no more than the number of ConvModule layers. - - Args: - in_channels (int): Number of channels in the input feature map. - inner_channels (int): Number of channels produced by the convolution. - num_layers (int): Number of convolution layers. - num_upsample (int | optional): Number of upsampling layer. Must be no - more than num_layers. Upsampling will be applied after the first - ``num_upsample`` layers of convolution. Default: ``num_layers``. - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - init_cfg (dict): Config dict for initialization. Default: None. - kwargs (key word augments): Other augments used in ConvModule. - """ - - def __init__(self, - in_channels, - inner_channels, - num_layers=1, - num_upsample=None, - conv_cfg=None, - norm_cfg=None, - init_cfg=None, - **kwargs): - super(ConvUpsample, self).__init__(init_cfg) - if num_upsample is None: - num_upsample = num_layers - assert num_upsample <= num_layers, \ - f'num_upsample({num_upsample})must be no more than ' \ - f'num_layers({num_layers})' - self.num_layers = num_layers - self.num_upsample = num_upsample - self.conv = ModuleList() - for i in range(num_layers): - self.conv.append( - ConvModule( - in_channels, - inner_channels, - 3, - padding=1, - stride=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - in_channels = inner_channels - - def forward(self, x): - num_upsample = self.num_upsample - for i in range(self.num_layers): - x = self.conv[i](x) - if num_upsample > 0: - num_upsample -= 1 - x = F.interpolate( - x, scale_factor=2, mode='bilinear', align_corners=False) - return x diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/csp_layer.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/csp_layer.py deleted file mode 100755 index dbd81ec8b..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/csp_layer.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, DepthwiseSeparableConvModule -from mmcv.runner import BaseModule - - -class DarknetBottleneck(BaseModule): - """The basic bottleneck block used in Darknet. - - Each ResBlock consists of two ConvModules and the input is added to the - final output. Each ConvModule is composed of Conv, BN, and LeakyReLU. - The first convLayer has filter size of 1x1 and the second one has the - filter size of 3x3. - - Args: - in_channels (int): The input channels of this Module. - out_channels (int): The output channels of this Module. - expansion (int): The kernel size of the convolution. Default: 0.5 - add_identity (bool): Whether to add identity to the out. - Default: True - use_depthwise (bool): Whether to use depthwise separable convolution. - Default: False - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='Swish'). - """ - - def __init__(self, - in_channels, - out_channels, - expansion=0.5, - add_identity=True, - use_depthwise=False, - conv_cfg=None, - norm_cfg=dict(type='BN', momentum=0.03, eps=0.001), - act_cfg=dict(type='Swish'), - init_cfg=None): - super().__init__(init_cfg) - hidden_channels = int(out_channels * expansion) - conv = DepthwiseSeparableConvModule if use_depthwise else ConvModule - self.conv1 = ConvModule( - in_channels, - hidden_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.conv2 = conv( - hidden_channels, - out_channels, - 3, - stride=1, - padding=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.add_identity = \ - add_identity and in_channels == out_channels - - def forward(self, x): - identity = x - out = self.conv1(x) - out = self.conv2(out) - - if self.add_identity: - return out + identity - else: - return out - - -class CSPLayer(BaseModule): - """Cross Stage Partial Layer. - - Args: - in_channels (int): The input channels of the CSP layer. - out_channels (int): The output channels of the CSP layer. - expand_ratio (float): Ratio to adjust the number of channels of the - hidden layer. Default: 0.5 - num_blocks (int): Number of blocks. Default: 1 - add_identity (bool): Whether to add identity in blocks. - Default: True - use_depthwise (bool): Whether to depthwise separable convolution in - blocks. Default: False - conv_cfg (dict, optional): Config dict for convolution layer. - Default: None, which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN') - act_cfg (dict): Config dict for activation layer. - Default: dict(type='Swish') - """ - - def __init__(self, - in_channels, - out_channels, - expand_ratio=0.5, - num_blocks=1, - add_identity=True, - use_depthwise=False, - conv_cfg=None, - norm_cfg=dict(type='BN', momentum=0.03, eps=0.001), - act_cfg=dict(type='Swish'), - init_cfg=None): - super().__init__(init_cfg) - mid_channels = int(out_channels * expand_ratio) - self.main_conv = ConvModule( - in_channels, - mid_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.short_conv = ConvModule( - in_channels, - mid_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.final_conv = ConvModule( - 2 * mid_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - self.blocks = nn.Sequential(*[ - DarknetBottleneck( - mid_channels, - mid_channels, - 1.0, - add_identity, - use_depthwise, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) for _ in range(num_blocks) - ]) - - def forward(self, x): - x_short = self.short_conv(x) - - x_main = self.main_conv(x) - x_main = self.blocks(x_main) - - x_final = torch.cat((x_main, x_short), dim=1) - return self.final_conv(x_final) diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/gaussian_target.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/gaussian_target.py deleted file mode 100755 index 91c2093a4..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/gaussian_target.py +++ /dev/null @@ -1,268 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from math import sqrt - -import torch -import torch.nn.functional as F - - -def gaussian2D(radius, sigma=1, dtype=torch.float32, device='cpu'): - """Generate 2D gaussian kernel. - - Args: - radius (int): Radius of gaussian kernel. - sigma (int): Sigma of gaussian function. Default: 1. - dtype (torch.dtype): Dtype of gaussian tensor. Default: torch.float32. - device (str): Device of gaussian tensor. Default: 'cpu'. - - Returns: - h (Tensor): Gaussian kernel with a - ``(2 * radius + 1) * (2 * radius + 1)`` shape. - """ - x = torch.arange( - -radius, radius + 1, dtype=dtype, device=device).view(1, -1) - y = torch.arange( - -radius, radius + 1, dtype=dtype, device=device).view(-1, 1) - - h = (-(x * x + y * y) / (2 * sigma * sigma)).exp() - - h[h < torch.finfo(h.dtype).eps * h.max()] = 0 - return h - - -def gen_gaussian_target(heatmap, center, radius, k=1): - """Generate 2D gaussian heatmap. - - Args: - heatmap (Tensor): Input heatmap, the gaussian kernel will cover on - it and maintain the max value. - center (list[int]): Coord of gaussian kernel's center. - radius (int): Radius of gaussian kernel. - k (int): Coefficient of gaussian kernel. Default: 1. - - Returns: - out_heatmap (Tensor): Updated heatmap covered by gaussian kernel. - """ - diameter = 2 * radius + 1 - gaussian_kernel = gaussian2D( - radius, sigma=diameter / 6, dtype=heatmap.dtype, device=heatmap.device) - - x, y = center - - height, width = heatmap.shape[:2] - - left, right = min(x, radius), min(width - x, radius + 1) - top, bottom = min(y, radius), min(height - y, radius + 1) - - masked_heatmap = heatmap[y - top:y + bottom, x - left:x + right] - masked_gaussian = gaussian_kernel[radius - top:radius + bottom, - radius - left:radius + right] - out_heatmap = heatmap - torch.max( - masked_heatmap, - masked_gaussian * k, - out=out_heatmap[y - top:y + bottom, x - left:x + right]) - - return out_heatmap - - -def gaussian_radius(det_size, min_overlap): - r"""Generate 2D gaussian radius. - - This function is modified from the `official github repo - `_. - - Given ``min_overlap``, radius could computed by a quadratic equation - according to Vieta's formulas. - - There are 3 cases for computing gaussian radius, details are following: - - - Explanation of figure: ``lt`` and ``br`` indicates the left-top and - bottom-right corner of ground truth box. ``x`` indicates the - generated corner at the limited position when ``radius=r``. - - - Case1: one corner is inside the gt box and the other is outside. - - .. code:: text - - |< width >| - - lt-+----------+ - - | | | ^ - +--x----------+--+ - | | | | - | | | | height - | | overlap | | - | | | | - | | | | v - +--+---------br--+ - - | | | - +----------+--x - - To ensure IoU of generated box and gt box is larger than ``min_overlap``: - - .. math:: - \cfrac{(w-r)*(h-r)}{w*h+(w+h)r-r^2} \ge {iou} \quad\Rightarrow\quad - {r^2-(w+h)r+\cfrac{1-iou}{1+iou}*w*h} \ge 0 \\ - {a} = 1,\quad{b} = {-(w+h)},\quad{c} = {\cfrac{1-iou}{1+iou}*w*h} - {r} \le \cfrac{-b-\sqrt{b^2-4*a*c}}{2*a} - - - Case2: both two corners are inside the gt box. - - .. code:: text - - |< width >| - - lt-+----------+ - - | | | ^ - +--x-------+ | - | | | | - | |overlap| | height - | | | | - | +-------x--+ - | | | v - +----------+-br - - - To ensure IoU of generated box and gt box is larger than ``min_overlap``: - - .. math:: - \cfrac{(w-2*r)*(h-2*r)}{w*h} \ge {iou} \quad\Rightarrow\quad - {4r^2-2(w+h)r+(1-iou)*w*h} \ge 0 \\ - {a} = 4,\quad {b} = {-2(w+h)},\quad {c} = {(1-iou)*w*h} - {r} \le \cfrac{-b-\sqrt{b^2-4*a*c}}{2*a} - - - Case3: both two corners are outside the gt box. - - .. code:: text - - |< width >| - - x--+----------------+ - | | | - +-lt-------------+ | - - | | | | ^ - | | | | - | | overlap | | height - | | | | - | | | | v - | +------------br--+ - - | | | - +----------------+--x - - To ensure IoU of generated box and gt box is larger than ``min_overlap``: - - .. math:: - \cfrac{w*h}{(w+2*r)*(h+2*r)} \ge {iou} \quad\Rightarrow\quad - {4*iou*r^2+2*iou*(w+h)r+(iou-1)*w*h} \le 0 \\ - {a} = {4*iou},\quad {b} = {2*iou*(w+h)},\quad {c} = {(iou-1)*w*h} \\ - {r} \le \cfrac{-b+\sqrt{b^2-4*a*c}}{2*a} - - Args: - det_size (list[int]): Shape of object. - min_overlap (float): Min IoU with ground truth for boxes generated by - keypoints inside the gaussian kernel. - - Returns: - radius (int): Radius of gaussian kernel. - """ - height, width = det_size - - a1 = 1 - b1 = (height + width) - c1 = width * height * (1 - min_overlap) / (1 + min_overlap) - sq1 = sqrt(b1**2 - 4 * a1 * c1) - r1 = (b1 - sq1) / (2 * a1) - - a2 = 4 - b2 = 2 * (height + width) - c2 = (1 - min_overlap) * width * height - sq2 = sqrt(b2**2 - 4 * a2 * c2) - r2 = (b2 - sq2) / (2 * a2) - - a3 = 4 * min_overlap - b3 = -2 * min_overlap * (height + width) - c3 = (min_overlap - 1) * width * height - sq3 = sqrt(b3**2 - 4 * a3 * c3) - r3 = (b3 + sq3) / (2 * a3) - return min(r1, r2, r3) - - -def get_local_maximum(heat, kernel=3): - """Extract local maximum pixel with given kernel. - - Args: - heat (Tensor): Target heatmap. - kernel (int): Kernel size of max pooling. Default: 3. - - Returns: - heat (Tensor): A heatmap where local maximum pixels maintain its - own value and other positions are 0. - """ - pad = (kernel - 1) // 2 - hmax = F.max_pool2d(heat, kernel, stride=1, padding=pad) - keep = (hmax == heat).float() - return heat * keep - - -def get_topk_from_heatmap(scores, k=20): - """Get top k positions from heatmap. - - Args: - scores (Tensor): Target heatmap with shape - [batch, num_classes, height, width]. - k (int): Target number. Default: 20. - - Returns: - tuple[torch.Tensor]: Scores, indexes, categories and coords of - topk keypoint. Containing following Tensors: - - - topk_scores (Tensor): Max scores of each topk keypoint. - - topk_inds (Tensor): Indexes of each topk keypoint. - - topk_clses (Tensor): Categories of each topk keypoint. - - topk_ys (Tensor): Y-coord of each topk keypoint. - - topk_xs (Tensor): X-coord of each topk keypoint. - """ - batch, _, height, width = scores.size() - topk_scores, topk_inds = torch.topk(scores.view(batch, -1), k) - topk_clses = topk_inds // (height * width) - topk_inds = topk_inds % (height * width) - topk_ys = topk_inds // width - topk_xs = (topk_inds % width).int().float() - return topk_scores, topk_inds, topk_clses, topk_ys, topk_xs - - -def gather_feat(feat, ind, mask=None): - """Gather feature according to index. - - Args: - feat (Tensor): Target feature map. - ind (Tensor): Target coord index. - mask (Tensor | None): Mask of feature map. Default: None. - - Returns: - feat (Tensor): Gathered feature. - """ - dim = feat.size(2) - ind = ind.unsqueeze(2).repeat(1, 1, dim) - feat = feat.gather(1, ind) - if mask is not None: - mask = mask.unsqueeze(2).expand_as(feat) - feat = feat[mask] - feat = feat.view(-1, dim) - return feat - - -def transpose_and_gather_feat(feat, ind): - """Transpose and gather feature according to index. - - Args: - feat (Tensor): Target feature map. - ind (Tensor): Target coord index. - - Returns: - feat (Tensor): Transposed and gathered feature. - """ - feat = feat.permute(0, 2, 3, 1).contiguous() - feat = feat.view(feat.size(0), -1, feat.size(3)) - feat = gather_feat(feat, ind) - return feat diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/inverted_residual.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/inverted_residual.py deleted file mode 100755 index 84e3fa901..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/inverted_residual.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.utils.checkpoint as cp -from mmcv.cnn import ConvModule -from mmcv.cnn.bricks import DropPath -from mmcv.runner import BaseModule - -from .se_layer import SELayer - - -class InvertedResidual(BaseModule): - """Inverted Residual Block. - - Args: - in_channels (int): The input channels of this Module. - out_channels (int): The output channels of this Module. - mid_channels (int): The input channels of the depthwise convolution. - kernel_size (int): The kernel size of the depthwise convolution. - Default: 3. - stride (int): The stride of the depthwise convolution. Default: 1. - se_cfg (dict): Config dict for se layer. Default: None, which means no - se layer. - with_expand_conv (bool): Use expand conv or not. If set False, - mid_channels must be the same with in_channels. - Default: True. - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - drop_path_rate (float): stochastic depth rate. Defaults to 0. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. Default: False. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, - in_channels, - out_channels, - mid_channels, - kernel_size=3, - stride=1, - se_cfg=None, - with_expand_conv=True, - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - drop_path_rate=0., - with_cp=False, - init_cfg=None): - super(InvertedResidual, self).__init__(init_cfg) - self.with_res_shortcut = (stride == 1 and in_channels == out_channels) - assert stride in [1, 2], f'stride must in [1, 2]. ' \ - f'But received {stride}.' - self.with_cp = with_cp - self.drop_path = DropPath( - drop_path_rate) if drop_path_rate > 0 else nn.Identity() - self.with_se = se_cfg is not None - self.with_expand_conv = with_expand_conv - - if self.with_se: - assert isinstance(se_cfg, dict) - if not self.with_expand_conv: - assert mid_channels == in_channels - - if self.with_expand_conv: - self.expand_conv = ConvModule( - in_channels=in_channels, - out_channels=mid_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - self.depthwise_conv = ConvModule( - in_channels=mid_channels, - out_channels=mid_channels, - kernel_size=kernel_size, - stride=stride, - padding=kernel_size // 2, - groups=mid_channels, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg) - - if self.with_se: - self.se = SELayer(**se_cfg) - - self.linear_conv = ConvModule( - in_channels=mid_channels, - out_channels=out_channels, - kernel_size=1, - stride=1, - padding=0, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - def forward(self, x): - - def _inner_forward(x): - out = x - - if self.with_expand_conv: - out = self.expand_conv(out) - - out = self.depthwise_conv(out) - - if self.with_se: - out = self.se(out) - - out = self.linear_conv(out) - - if self.with_res_shortcut: - return x + self.drop_path(out) - else: - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - return out diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/make_divisible.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/make_divisible.py deleted file mode 100755 index 5c13a03ae..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/make_divisible.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def make_divisible(value, divisor, min_value=None, min_ratio=0.9): - """Make divisible function. - - This function rounds the channel number to the nearest value that can be - divisible by the divisor. It is taken from the original tf repo. It ensures - that all layers have a channel number that is divisible by divisor. It can - be seen here: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py # noqa - - Args: - value (int): The original channel number. - divisor (int): The divisor to fully divide the channel number. - min_value (int): The minimum value of the output channel. - Default: None, means that the minimum value equal to the divisor. - min_ratio (float): The minimum ratio of the rounded channel number to - the original channel number. Default: 0.9. - - Returns: - int: The modified output channel number. - """ - - if min_value is None: - min_value = divisor - new_value = max(min_value, int(value + divisor / 2) // divisor * divisor) - # Make sure that round down does not go down by more than (1-min_ratio). - if new_value < min_ratio * value: - new_value += divisor - return new_value diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/misc.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/misc.py deleted file mode 100755 index b759e458e..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/misc.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.autograd import Function -from torch.nn import functional as F - - -class SigmoidGeometricMean(Function): - """Forward and backward function of geometric mean of two sigmoid - functions. - - This implementation with analytical gradient function substitutes - the autograd function of (x.sigmoid() * y.sigmoid()).sqrt(). The - original implementation incurs none during gradient backprapagation - if both x and y are very small values. - """ - - @staticmethod - def forward(ctx, x, y): - x_sigmoid = x.sigmoid() - y_sigmoid = y.sigmoid() - z = (x_sigmoid * y_sigmoid).sqrt() - ctx.save_for_backward(x_sigmoid, y_sigmoid, z) - return z - - @staticmethod - def backward(ctx, grad_output): - x_sigmoid, y_sigmoid, z = ctx.saved_tensors - grad_x = grad_output * z * (1 - x_sigmoid) / 2 - grad_y = grad_output * z * (1 - y_sigmoid) / 2 - return grad_x, grad_y - - -sigmoid_geometric_mean = SigmoidGeometricMean.apply - - -def interpolate_as(source, target, mode='bilinear', align_corners=False): - """Interpolate the `source` to the shape of the `target`. - - The `source` must be a Tensor, but the `target` can be a Tensor or a - np.ndarray with the shape (..., target_h, target_w). - - Args: - source (Tensor): A 3D/4D Tensor with the shape (N, H, W) or - (N, C, H, W). - target (Tensor | np.ndarray): The interpolation target with the shape - (..., target_h, target_w). - mode (str): Algorithm used for interpolation. The options are the - same as those in F.interpolate(). Default: ``'bilinear'``. - align_corners (bool): The same as the argument in F.interpolate(). - - Returns: - Tensor: The interpolated source Tensor. - """ - assert len(target.shape) >= 2 - - def _interpolate_as(source, target, mode='bilinear', align_corners=False): - """Interpolate the `source` (4D) to the shape of the `target`.""" - target_h, target_w = target.shape[-2:] - source_h, source_w = source.shape[-2:] - if target_h != source_h or target_w != source_w: - source = F.interpolate( - source, - size=(target_h, target_w), - mode=mode, - align_corners=align_corners) - return source - - if len(source.shape) == 3: - source = source[:, None, :, :] - source = _interpolate_as(source, target, mode, align_corners) - return source[:, 0, :, :] - else: - return _interpolate_as(source, target, mode, align_corners) diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/normed_predictor.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/normed_predictor.py deleted file mode 100755 index 402f48c5f..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/normed_predictor.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import CONV_LAYERS - -from .builder import LINEAR_LAYERS - - -@LINEAR_LAYERS.register_module(name='NormedLinear') -class NormedLinear(nn.Linear): - """Normalized Linear Layer. - - Args: - tempeature (float, optional): Tempeature term. Default to 20. - power (int, optional): Power term. Default to 1.0. - eps (float, optional): The minimal value of divisor to - keep numerical stability. Default to 1e-6. - """ - - def __init__(self, *args, tempearture=20, power=1.0, eps=1e-6, **kwargs): - super(NormedLinear, self).__init__(*args, **kwargs) - self.tempearture = tempearture - self.power = power - self.eps = eps - self.init_weights() - - def init_weights(self): - nn.init.normal_(self.weight, mean=0, std=0.01) - if self.bias is not None: - nn.init.constant_(self.bias, 0) - - def forward(self, x): - weight_ = self.weight / ( - self.weight.norm(dim=1, keepdim=True).pow(self.power) + self.eps) - x_ = x / (x.norm(dim=1, keepdim=True).pow(self.power) + self.eps) - x_ = x_ * self.tempearture - - return F.linear(x_, weight_, self.bias) - - -@CONV_LAYERS.register_module(name='NormedConv2d') -class NormedConv2d(nn.Conv2d): - """Normalized Conv2d Layer. - - Args: - tempeature (float, optional): Tempeature term. Default to 20. - power (int, optional): Power term. Default to 1.0. - eps (float, optional): The minimal value of divisor to - keep numerical stability. Default to 1e-6. - norm_over_kernel (bool, optional): Normalize over kernel. - Default to False. - """ - - def __init__(self, - *args, - tempearture=20, - power=1.0, - eps=1e-6, - norm_over_kernel=False, - **kwargs): - super(NormedConv2d, self).__init__(*args, **kwargs) - self.tempearture = tempearture - self.power = power - self.norm_over_kernel = norm_over_kernel - self.eps = eps - - def forward(self, x): - if not self.norm_over_kernel: - weight_ = self.weight / ( - self.weight.norm(dim=1, keepdim=True).pow(self.power) + - self.eps) - else: - weight_ = self.weight / ( - self.weight.view(self.weight.size(0), -1).norm( - dim=1, keepdim=True).pow(self.power)[..., None, None] + - self.eps) - x_ = x / (x.norm(dim=1, keepdim=True).pow(self.power) + self.eps) - x_ = x_ * self.tempearture - - if hasattr(self, 'conv2d_forward'): - x_ = self.conv2d_forward(x_, weight_) - else: - if torch.__version__ >= '1.8': - x_ = self._conv_forward(x_, weight_, self.bias) - else: - x_ = self._conv_forward(x_, weight_) - return x_ diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/panoptic_gt_processing.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/panoptic_gt_processing.py deleted file mode 100755 index cdb5e7c4b..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/panoptic_gt_processing.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def preprocess_panoptic_gt(gt_labels, gt_masks, gt_semantic_seg, num_things, - num_stuff, img_metas): - """Preprocess the ground truth for a image. - - Args: - gt_labels (Tensor): Ground truth labels of each bbox, - with shape (num_gts, ). - gt_masks (BitmapMasks): Ground truth masks of each instances - of a image, shape (num_gts, h, w). - gt_semantic_seg (Tensor | None): Ground truth of semantic - segmentation with the shape (1, h, w). - [0, num_thing_class - 1] means things, - [num_thing_class, num_class-1] means stuff, - 255 means VOID. It's None when training instance segmentation. - img_metas (dict): List of image meta information. - - Returns: - tuple: a tuple containing the following targets. - - - labels (Tensor): Ground truth class indices for a - image, with shape (n, ), n is the sum of number - of stuff type and number of instance in a image. - - masks (Tensor): Ground truth mask for a image, with - shape (n, h, w). Contains stuff and things when training - panoptic segmentation, and things only when training - instance segmentation. - """ - num_classes = num_things + num_stuff - - things_masks = gt_masks.pad(img_metas['pad_shape'][:2], pad_val=0)\ - .to_tensor(dtype=torch.bool, device=gt_labels.device) - - if gt_semantic_seg is None: - masks = things_masks.long() - return gt_labels, masks - - things_labels = gt_labels - gt_semantic_seg = gt_semantic_seg.squeeze(0) - - semantic_labels = torch.unique( - gt_semantic_seg, - sorted=False, - return_inverse=False, - return_counts=False) - stuff_masks_list = [] - stuff_labels_list = [] - for label in semantic_labels: - if label < num_things or label >= num_classes: - continue - stuff_mask = gt_semantic_seg == label - stuff_masks_list.append(stuff_mask) - stuff_labels_list.append(label) - - if len(stuff_masks_list) > 0: - stuff_masks = torch.stack(stuff_masks_list, dim=0) - stuff_labels = torch.stack(stuff_labels_list, dim=0) - labels = torch.cat([things_labels, stuff_labels], dim=0) - masks = torch.cat([things_masks, stuff_masks], dim=0) - else: - labels = things_labels - masks = things_masks - - masks = masks.long() - return labels, masks diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/point_sample.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/point_sample.py deleted file mode 100755 index b5096899c..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/point_sample.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.ops import point_sample - - -def get_uncertainty(mask_pred, labels): - """Estimate uncertainty based on pred logits. - - We estimate uncertainty as L1 distance between 0.0 and the logits - prediction in 'mask_pred' for the foreground class in `classes`. - - Args: - mask_pred (Tensor): mask predication logits, shape (num_rois, - num_classes, mask_height, mask_width). - - labels (list[Tensor]): Either predicted or ground truth label for - each predicted mask, of length num_rois. - - Returns: - scores (Tensor): Uncertainty scores with the most uncertain - locations having the highest uncertainty score, - shape (num_rois, 1, mask_height, mask_width) - """ - if mask_pred.shape[1] == 1: - gt_class_logits = mask_pred.clone() - else: - inds = torch.arange(mask_pred.shape[0], device=mask_pred.device) - gt_class_logits = mask_pred[inds, labels].unsqueeze(1) - return -torch.abs(gt_class_logits) - - -def get_uncertain_point_coords_with_randomness(mask_pred, labels, num_points, - oversample_ratio, - importance_sample_ratio): - """Get ``num_points`` most uncertain points with random points during - train. - - Sample points in [0, 1] x [0, 1] coordinate space based on their - uncertainty. The uncertainties are calculated for each point using - 'get_uncertainty()' function that takes point's logit prediction as - input. - - Args: - mask_pred (Tensor): A tensor of shape (num_rois, num_classes, - mask_height, mask_width) for class-specific or class-agnostic - prediction. - labels (list): The ground truth class for each instance. - num_points (int): The number of points to sample. - oversample_ratio (int): Oversampling parameter. - importance_sample_ratio (float): Ratio of points that are sampled - via importnace sampling. - - Returns: - point_coords (Tensor): A tensor of shape (num_rois, num_points, 2) - that contains the coordinates sampled points. - """ - assert oversample_ratio >= 1 - assert 0 <= importance_sample_ratio <= 1 - batch_size = mask_pred.shape[0] - num_sampled = int(num_points * oversample_ratio) - point_coords = torch.rand( - batch_size, num_sampled, 2, device=mask_pred.device) - point_logits = point_sample(mask_pred, point_coords) - # It is crucial to calculate uncertainty based on the sampled - # prediction value for the points. Calculating uncertainties of the - # coarse predictions first and sampling them for points leads to - # incorrect results. To illustrate this: assume uncertainty func( - # logits)=-abs(logits), a sampled point between two coarse - # predictions with -1 and 1 logits has 0 logits, and therefore 0 - # uncertainty value. However, if we calculate uncertainties for the - # coarse predictions first, both will have -1 uncertainty, - # and sampled point will get -1 uncertainty. - point_uncertainties = get_uncertainty(point_logits, labels) - num_uncertain_points = int(importance_sample_ratio * num_points) - num_random_points = num_points - num_uncertain_points - idx = torch.topk( - point_uncertainties[:, 0, :], k=num_uncertain_points, dim=1)[1] - shift = num_sampled * torch.arange( - batch_size, dtype=torch.long, device=mask_pred.device) - idx += shift[:, None] - point_coords = point_coords.view(-1, 2)[idx.view(-1), :].view( - batch_size, num_uncertain_points, 2) - if num_random_points > 0: - rand_roi_coords = torch.rand( - batch_size, num_random_points, 2, device=mask_pred.device) - point_coords = torch.cat((point_coords, rand_roi_coords), dim=1) - return point_coords diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/positional_encoding.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/positional_encoding.py deleted file mode 100755 index 51e8e3df3..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/positional_encoding.py +++ /dev/null @@ -1,163 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import torch -import torch.nn as nn -from mmcv.cnn.bricks.transformer import POSITIONAL_ENCODING -from mmcv.runner import BaseModule - - -@POSITIONAL_ENCODING.register_module() -class SinePositionalEncoding(BaseModule): - """Position encoding with sine and cosine functions. - - See `End-to-End Object Detection with Transformers - `_ for details. - - Args: - num_feats (int): The feature dimension for each position - along x-axis or y-axis. Note the final returned dimension - for each position is 2 times of this value. - temperature (int, optional): The temperature used for scaling - the position embedding. Defaults to 10000. - normalize (bool, optional): Whether to normalize the position - embedding. Defaults to False. - scale (float, optional): A scale factor that scales the position - embedding. The scale will be used only when `normalize` is True. - Defaults to 2*pi. - eps (float, optional): A value added to the denominator for - numerical stability. Defaults to 1e-6. - offset (float): offset add to embed when do the normalization. - Defaults to 0. - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None - """ - - def __init__(self, - num_feats, - temperature=10000, - normalize=False, - scale=2 * math.pi, - eps=1e-6, - offset=0., - init_cfg=None): - super(SinePositionalEncoding, self).__init__(init_cfg) - if normalize: - assert isinstance(scale, (float, int)), 'when normalize is set,' \ - 'scale should be provided and in float or int type, ' \ - f'found {type(scale)}' - self.num_feats = num_feats - self.temperature = temperature - self.normalize = normalize - self.scale = scale - self.eps = eps - self.offset = offset - - def forward(self, mask): - """Forward function for `SinePositionalEncoding`. - - Args: - mask (Tensor): ByteTensor mask. Non-zero values representing - ignored positions, while zero values means valid positions - for this image. Shape [bs, h, w]. - - Returns: - pos (Tensor): Returned position embedding with shape - [bs, num_feats*2, h, w]. - """ - # For convenience of exporting to ONNX, it's required to convert - # `masks` from bool to int. - mask = mask.to(torch.int) - not_mask = 1 - mask # logical_not - y_embed = not_mask.cumsum(1, dtype=torch.float32) - x_embed = not_mask.cumsum(2, dtype=torch.float32) - if self.normalize: - y_embed = (y_embed + self.offset) / \ - (y_embed[:, -1:, :] + self.eps) * self.scale - x_embed = (x_embed + self.offset) / \ - (x_embed[:, :, -1:] + self.eps) * self.scale - dim_t = torch.arange( - self.num_feats, dtype=torch.float32, device=mask.device) - dim_t = self.temperature**(2 * (dim_t // 2) / self.num_feats) - pos_x = x_embed[:, :, :, None] / dim_t - pos_y = y_embed[:, :, :, None] / dim_t - # use `view` instead of `flatten` for dynamically exporting to ONNX - B, H, W = mask.size() - pos_x = torch.stack( - (pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), - dim=4).view(B, H, W, -1) - pos_y = torch.stack( - (pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), - dim=4).view(B, H, W, -1) - pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2) - return pos - - def __repr__(self): - """str: a string that describes the module""" - repr_str = self.__class__.__name__ - repr_str += f'(num_feats={self.num_feats}, ' - repr_str += f'temperature={self.temperature}, ' - repr_str += f'normalize={self.normalize}, ' - repr_str += f'scale={self.scale}, ' - repr_str += f'eps={self.eps})' - return repr_str - - -@POSITIONAL_ENCODING.register_module() -class LearnedPositionalEncoding(BaseModule): - """Position embedding with learnable embedding weights. - - Args: - num_feats (int): The feature dimension for each position - along x-axis or y-axis. The final returned dimension for - each position is 2 times of this value. - row_num_embed (int, optional): The dictionary size of row embeddings. - Default 50. - col_num_embed (int, optional): The dictionary size of col embeddings. - Default 50. - init_cfg (dict or list[dict], optional): Initialization config dict. - """ - - def __init__(self, - num_feats, - row_num_embed=50, - col_num_embed=50, - init_cfg=dict(type='Uniform', layer='Embedding')): - super(LearnedPositionalEncoding, self).__init__(init_cfg) - self.row_embed = nn.Embedding(row_num_embed, num_feats) - self.col_embed = nn.Embedding(col_num_embed, num_feats) - self.num_feats = num_feats - self.row_num_embed = row_num_embed - self.col_num_embed = col_num_embed - - def forward(self, mask): - """Forward function for `LearnedPositionalEncoding`. - - Args: - mask (Tensor): ByteTensor mask. Non-zero values representing - ignored positions, while zero values means valid positions - for this image. Shape [bs, h, w]. - - Returns: - pos (Tensor): Returned position embedding with shape - [bs, num_feats*2, h, w]. - """ - h, w = mask.shape[-2:] - x = torch.arange(w, device=mask.device) - y = torch.arange(h, device=mask.device) - x_embed = self.col_embed(x) - y_embed = self.row_embed(y) - pos = torch.cat( - (x_embed.unsqueeze(0).repeat(h, 1, 1), y_embed.unsqueeze(1).repeat( - 1, w, 1)), - dim=-1).permute(2, 0, - 1).unsqueeze(0).repeat(mask.shape[0], 1, 1, 1) - return pos - - def __repr__(self): - """str: a string that describes the module""" - repr_str = self.__class__.__name__ - repr_str += f'(num_feats={self.num_feats}, ' - repr_str += f'row_num_embed={self.row_num_embed}, ' - repr_str += f'col_num_embed={self.col_num_embed})' - return repr_str diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/res_layer.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/res_layer.py deleted file mode 100755 index dbf7c860e..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/res_layer.py +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner import BaseModule, Sequential -from torch import nn as nn - - -class ResLayer(Sequential): - """ResLayer to build ResNet style backbone. - - Args: - block (nn.Module): block used to build ResLayer. - inplanes (int): inplanes of block. - planes (int): planes of block. - num_blocks (int): number of blocks. - stride (int): stride of the first block. Default: 1 - avg_down (bool): Use AvgPool instead of stride conv when - downsampling in the bottleneck. Default: False - conv_cfg (dict): dictionary to construct and config conv layer. - Default: None - norm_cfg (dict): dictionary to construct and config norm layer. - Default: dict(type='BN') - downsample_first (bool): Downsample at the first block or last block. - False for Hourglass, True for ResNet. Default: True - """ - - def __init__(self, - block, - inplanes, - planes, - num_blocks, - stride=1, - avg_down=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - downsample_first=True, - **kwargs): - self.block = block - - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = [] - conv_stride = stride - if avg_down: - conv_stride = 1 - downsample.append( - nn.AvgPool2d( - kernel_size=stride, - stride=stride, - ceil_mode=True, - count_include_pad=False)) - downsample.extend([ - build_conv_layer( - conv_cfg, - inplanes, - planes * block.expansion, - kernel_size=1, - stride=conv_stride, - bias=False), - build_norm_layer(norm_cfg, planes * block.expansion)[1] - ]) - downsample = nn.Sequential(*downsample) - - layers = [] - if downsample_first: - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=stride, - downsample=downsample, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - inplanes = planes * block.expansion - for _ in range(1, num_blocks): - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - - else: # downsample_first=False is for HourglassModule - for _ in range(num_blocks - 1): - layers.append( - block( - inplanes=inplanes, - planes=inplanes, - stride=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - layers.append( - block( - inplanes=inplanes, - planes=planes, - stride=stride, - downsample=downsample, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - **kwargs)) - super(ResLayer, self).__init__(*layers) - - -class SimplifiedBasicBlock(BaseModule): - """Simplified version of original basic residual block. This is used in - `SCNet `_. - - - Norm layer is now optional - - Last ReLU in forward function is removed - """ - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False, - conv_cfg=None, - norm_cfg=dict(type='BN'), - dcn=None, - plugins=None, - init_fg=None): - super(SimplifiedBasicBlock, self).__init__(init_fg) - assert dcn is None, 'Not implemented yet.' - assert plugins is None, 'Not implemented yet.' - assert not with_cp, 'Not implemented yet.' - self.with_norm = norm_cfg is not None - with_bias = True if norm_cfg is None else False - self.conv1 = build_conv_layer( - conv_cfg, - inplanes, - planes, - 3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=with_bias) - if self.with_norm: - self.norm1_name, norm1 = build_norm_layer( - norm_cfg, planes, postfix=1) - self.add_module(self.norm1_name, norm1) - self.conv2 = build_conv_layer( - conv_cfg, planes, planes, 3, padding=1, bias=with_bias) - if self.with_norm: - self.norm2_name, norm2 = build_norm_layer( - norm_cfg, planes, postfix=2) - self.add_module(self.norm2_name, norm2) - - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - @property - def norm1(self): - """nn.Module: normalization layer after the first convolution layer""" - return getattr(self, self.norm1_name) if self.with_norm else None - - @property - def norm2(self): - """nn.Module: normalization layer after the second convolution layer""" - return getattr(self, self.norm2_name) if self.with_norm else None - - def forward(self, x): - """Forward function.""" - - identity = x - - out = self.conv1(x) - if self.with_norm: - out = self.norm1(out) - out = self.relu(out) - - out = self.conv2(out) - if self.with_norm: - out = self.norm2(out) - - if self.downsample is not None: - identity = self.downsample(x) - - out += identity - - return out diff --git a/cv/detection/autoassign/pytorch/mmdet/models/utils/se_layer.py b/cv/detection/autoassign/pytorch/mmdet/models/utils/se_layer.py deleted file mode 100755 index 466de58cd..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/models/utils/se_layer.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule -from mmcv.runner import BaseModule - - -class SELayer(BaseModule): - """Squeeze-and-Excitation Module. - - Args: - channels (int): The input (and output) channels of the SE layer. - ratio (int): Squeeze ratio in SELayer, the intermediate channel will be - ``int(channels/ratio)``. Default: 16. - conv_cfg (None or dict): Config dict for convolution layer. - Default: None, which means using conv2d. - act_cfg (dict or Sequence[dict]): Config dict for activation layer. - If act_cfg is a dict, two activation layers will be configurated - by this dict. If act_cfg is a sequence of dicts, the first - activation layer will be configurated by the first dict and the - second activation layer will be configurated by the second dict. - Default: (dict(type='ReLU'), dict(type='Sigmoid')) - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None - """ - - def __init__(self, - channels, - ratio=16, - conv_cfg=None, - act_cfg=(dict(type='ReLU'), dict(type='Sigmoid')), - init_cfg=None): - super(SELayer, self).__init__(init_cfg) - if isinstance(act_cfg, dict): - act_cfg = (act_cfg, act_cfg) - assert len(act_cfg) == 2 - assert mmcv.is_tuple_of(act_cfg, dict) - self.global_avgpool = nn.AdaptiveAvgPool2d(1) - self.conv1 = ConvModule( - in_channels=channels, - out_channels=int(channels / ratio), - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[0]) - self.conv2 = ConvModule( - in_channels=int(channels / ratio), - out_channels=channels, - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[1]) - - def forward(self, x): - out = self.global_avgpool(x) - out = self.conv1(out) - out = self.conv2(out) - return x * out - - -class DyReLU(BaseModule): - """Dynamic ReLU (DyReLU) module. - - See `Dynamic ReLU `_ for details. - Current implementation is specialized for task-aware attention in DyHead. - HSigmoid arguments in default act_cfg follow DyHead official code. - https://github.com/microsoft/DynamicHead/blob/master/dyhead/dyrelu.py - - Args: - channels (int): The input (and output) channels of DyReLU module. - ratio (int): Squeeze ratio in Squeeze-and-Excitation-like module, - the intermediate channel will be ``int(channels/ratio)``. - Default: 4. - conv_cfg (None or dict): Config dict for convolution layer. - Default: None, which means using conv2d. - act_cfg (dict or Sequence[dict]): Config dict for activation layer. - If act_cfg is a dict, two activation layers will be configurated - by this dict. If act_cfg is a sequence of dicts, the first - activation layer will be configurated by the first dict and the - second activation layer will be configurated by the second dict. - Default: (dict(type='ReLU'), dict(type='HSigmoid', bias=3.0, - divisor=6.0)) - init_cfg (dict or list[dict], optional): Initialization config dict. - Default: None - """ - - def __init__(self, - channels, - ratio=4, - conv_cfg=None, - act_cfg=(dict(type='ReLU'), - dict(type='HSigmoid', bias=3.0, divisor=6.0)), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - if isinstance(act_cfg, dict): - act_cfg = (act_cfg, act_cfg) - assert len(act_cfg) == 2 - assert mmcv.is_tuple_of(act_cfg, dict) - self.channels = channels - self.expansion = 4 # for a1, b1, a2, b2 - self.global_avgpool = nn.AdaptiveAvgPool2d(1) - self.conv1 = ConvModule( - in_channels=channels, - out_channels=int(channels / ratio), - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[0]) - self.conv2 = ConvModule( - in_channels=int(channels / ratio), - out_channels=channels * self.expansion, - kernel_size=1, - stride=1, - conv_cfg=conv_cfg, - act_cfg=act_cfg[1]) - - def forward(self, x): - """Forward function.""" - coeffs = self.global_avgpool(x) - coeffs = self.conv1(coeffs) - coeffs = self.conv2(coeffs) - 0.5 # value range: [-0.5, 0.5] - a1, b1, a2, b2 = torch.split(coeffs, self.channels, dim=1) - a1 = a1 * 2.0 + 1.0 # [-1.0, 1.0] + 1.0 - a2 = a2 * 2.0 # [-1.0, 1.0] - out = torch.max(x * a1 + b1, x * a2 + b2) - return out diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/__init__.py b/cv/detection/autoassign/pytorch/mmdet/utils/__init__.py deleted file mode 100755 index 4f8ffabfc..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collect_env import collect_env -from .compat_config import compat_cfg -from .logger import get_caller_name, get_root_logger, log_img_scale -from .memory import AvoidCUDAOOM, AvoidOOM -from .misc import find_latest_checkpoint, update_data_root -from .replace_cfg_vals import replace_cfg_vals -from .setup_env import setup_multi_processes -from .split_batch import split_batch -from .util_distribution import build_ddp, build_dp, get_device - -__all__ = [ - 'get_root_logger', 'collect_env', 'find_latest_checkpoint', - 'update_data_root', 'setup_multi_processes', 'get_caller_name', - 'log_img_scale', 'compat_cfg', 'split_batch', 'build_ddp', 'build_dp', - 'get_device', 'replace_cfg_vals', 'AvoidOOM', 'AvoidCUDAOOM' -] diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/collect_env.py b/cv/detection/autoassign/pytorch/mmdet/utils/collect_env.py deleted file mode 100755 index 7acbb6ab1..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/collect_env.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash - -import mmdet - - -def collect_env(): - """Collect the information of the running environments.""" - env_info = collect_base_env() - env_info['MMDetection'] = mmdet.__version__ + '+' + get_git_hash()[:7] - return env_info - - -if __name__ == '__main__': - for name, val in collect_env().items(): - print(f'{name}: {val}') diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/compat_config.py b/cv/detection/autoassign/pytorch/mmdet/utils/compat_config.py deleted file mode 100755 index c47550459..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/compat_config.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings - -from mmcv import ConfigDict - - -def compat_cfg(cfg): - """This function would modify some filed to keep the compatibility of - config. - - For example, it will move some args which will be deprecated to the correct - fields. - """ - cfg = copy.deepcopy(cfg) - cfg = compat_imgs_per_gpu(cfg) - cfg = compat_loader_args(cfg) - cfg = compat_runner_args(cfg) - return cfg - - -def compat_runner_args(cfg): - if 'runner' not in cfg: - cfg.runner = ConfigDict({ - 'type': 'EpochBasedRunner', - 'max_epochs': cfg.total_epochs - }) - warnings.warn( - 'config is now expected to have a `runner` section, ' - 'please set `runner` in your config.', UserWarning) - else: - if 'total_epochs' in cfg: - assert cfg.total_epochs == cfg.runner.max_epochs - return cfg - - -def compat_imgs_per_gpu(cfg): - cfg = copy.deepcopy(cfg) - if 'imgs_per_gpu' in cfg.data: - warnings.warn('"imgs_per_gpu" is deprecated in MMDet V2.0. ' - 'Please use "samples_per_gpu" instead') - if 'samples_per_gpu' in cfg.data: - warnings.warn( - f'Got "imgs_per_gpu"={cfg.data.imgs_per_gpu} and ' - f'"samples_per_gpu"={cfg.data.samples_per_gpu}, "imgs_per_gpu"' - f'={cfg.data.imgs_per_gpu} is used in this experiments') - else: - warnings.warn('Automatically set "samples_per_gpu"="imgs_per_gpu"=' - f'{cfg.data.imgs_per_gpu} in this experiments') - cfg.data.samples_per_gpu = cfg.data.imgs_per_gpu - return cfg - - -def compat_loader_args(cfg): - """Deprecated sample_per_gpu in cfg.data.""" - - cfg = copy.deepcopy(cfg) - if 'train_dataloader' not in cfg.data: - cfg.data['train_dataloader'] = ConfigDict() - if 'val_dataloader' not in cfg.data: - cfg.data['val_dataloader'] = ConfigDict() - if 'test_dataloader' not in cfg.data: - cfg.data['test_dataloader'] = ConfigDict() - - # special process for train_dataloader - if 'samples_per_gpu' in cfg.data: - - samples_per_gpu = cfg.data.pop('samples_per_gpu') - assert 'samples_per_gpu' not in \ - cfg.data.train_dataloader, ('`samples_per_gpu` are set ' - 'in `data` field and ` ' - 'data.train_dataloader` ' - 'at the same time. ' - 'Please only set it in ' - '`data.train_dataloader`. ') - cfg.data.train_dataloader['samples_per_gpu'] = samples_per_gpu - - if 'persistent_workers' in cfg.data: - - persistent_workers = cfg.data.pop('persistent_workers') - assert 'persistent_workers' not in \ - cfg.data.train_dataloader, ('`persistent_workers` are set ' - 'in `data` field and ` ' - 'data.train_dataloader` ' - 'at the same time. ' - 'Please only set it in ' - '`data.train_dataloader`. ') - cfg.data.train_dataloader['persistent_workers'] = persistent_workers - - if 'workers_per_gpu' in cfg.data: - - workers_per_gpu = cfg.data.pop('workers_per_gpu') - cfg.data.train_dataloader['workers_per_gpu'] = workers_per_gpu - cfg.data.val_dataloader['workers_per_gpu'] = workers_per_gpu - cfg.data.test_dataloader['workers_per_gpu'] = workers_per_gpu - - # special process for val_dataloader - if 'samples_per_gpu' in cfg.data.val: - # keep default value of `sample_per_gpu` is 1 - assert 'samples_per_gpu' not in \ - cfg.data.val_dataloader, ('`samples_per_gpu` are set ' - 'in `data.val` field and ` ' - 'data.val_dataloader` at ' - 'the same time. ' - 'Please only set it in ' - '`data.val_dataloader`. ') - cfg.data.val_dataloader['samples_per_gpu'] = \ - cfg.data.val.pop('samples_per_gpu') - # special process for val_dataloader - - # in case the test dataset is concatenated - if isinstance(cfg.data.test, dict): - if 'samples_per_gpu' in cfg.data.test: - assert 'samples_per_gpu' not in \ - cfg.data.test_dataloader, ('`samples_per_gpu` are set ' - 'in `data.test` field and ` ' - 'data.test_dataloader` ' - 'at the same time. ' - 'Please only set it in ' - '`data.test_dataloader`. ') - - cfg.data.test_dataloader['samples_per_gpu'] = \ - cfg.data.test.pop('samples_per_gpu') - - elif isinstance(cfg.data.test, list): - for ds_cfg in cfg.data.test: - if 'samples_per_gpu' in ds_cfg: - assert 'samples_per_gpu' not in \ - cfg.data.test_dataloader, ('`samples_per_gpu` are set ' - 'in `data.test` field and ` ' - 'data.test_dataloader` at' - ' the same time. ' - 'Please only set it in ' - '`data.test_dataloader`. ') - samples_per_gpu = max( - [ds_cfg.pop('samples_per_gpu', 1) for ds_cfg in cfg.data.test]) - cfg.data.test_dataloader['samples_per_gpu'] = samples_per_gpu - - return cfg diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/contextmanagers.py b/cv/detection/autoassign/pytorch/mmdet/utils/contextmanagers.py deleted file mode 100755 index 6ba413a24..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/contextmanagers.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import asyncio -import contextlib -import logging -import os -import time -from typing import List - -import torch - -logger = logging.getLogger(__name__) - -DEBUG_COMPLETED_TIME = bool(os.environ.get('DEBUG_COMPLETED_TIME', False)) - - -@contextlib.asynccontextmanager -async def completed(trace_name='', - name='', - sleep_interval=0.05, - streams: List[torch.cuda.Stream] = None): - """Async context manager that waits for work to complete on given CUDA - streams.""" - if not torch.cuda.is_available(): - yield - return - - stream_before_context_switch = torch.cuda.current_stream() - if not streams: - streams = [stream_before_context_switch] - else: - streams = [s if s else stream_before_context_switch for s in streams] - - end_events = [ - torch.cuda.Event(enable_timing=DEBUG_COMPLETED_TIME) for _ in streams - ] - - if DEBUG_COMPLETED_TIME: - start = torch.cuda.Event(enable_timing=True) - stream_before_context_switch.record_event(start) - - cpu_start = time.monotonic() - logger.debug('%s %s starting, streams: %s', trace_name, name, streams) - grad_enabled_before = torch.is_grad_enabled() - try: - yield - finally: - current_stream = torch.cuda.current_stream() - assert current_stream == stream_before_context_switch - - if DEBUG_COMPLETED_TIME: - cpu_end = time.monotonic() - for i, stream in enumerate(streams): - event = end_events[i] - stream.record_event(event) - - grad_enabled_after = torch.is_grad_enabled() - - # observed change of torch.is_grad_enabled() during concurrent run of - # async_test_bboxes code - assert (grad_enabled_before == grad_enabled_after - ), 'Unexpected is_grad_enabled() value change' - - are_done = [e.query() for e in end_events] - logger.debug('%s %s completed: %s streams: %s', trace_name, name, - are_done, streams) - with torch.cuda.stream(stream_before_context_switch): - while not all(are_done): - await asyncio.sleep(sleep_interval) - are_done = [e.query() for e in end_events] - logger.debug( - '%s %s completed: %s streams: %s', - trace_name, - name, - are_done, - streams, - ) - - current_stream = torch.cuda.current_stream() - assert current_stream == stream_before_context_switch - - if DEBUG_COMPLETED_TIME: - cpu_time = (cpu_end - cpu_start) * 1000 - stream_times_ms = '' - for i, stream in enumerate(streams): - elapsed_time = start.elapsed_time(end_events[i]) - stream_times_ms += f' {stream} {elapsed_time:.2f} ms' - logger.info('%s %s %.2f ms %s', trace_name, name, cpu_time, - stream_times_ms) - - -@contextlib.asynccontextmanager -async def concurrent(streamqueue: asyncio.Queue, - trace_name='concurrent', - name='stream'): - """Run code concurrently in different streams. - - :param streamqueue: asyncio.Queue instance. - - Queue tasks define the pool of streams used for concurrent execution. - """ - if not torch.cuda.is_available(): - yield - return - - initial_stream = torch.cuda.current_stream() - - with torch.cuda.stream(initial_stream): - stream = await streamqueue.get() - assert isinstance(stream, torch.cuda.Stream) - - try: - with torch.cuda.stream(stream): - logger.debug('%s %s is starting, stream: %s', trace_name, name, - stream) - yield - current = torch.cuda.current_stream() - assert current == stream - logger.debug('%s %s has finished, stream: %s', trace_name, - name, stream) - finally: - streamqueue.task_done() - streamqueue.put_nowait(stream) diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/logger.py b/cv/detection/autoassign/pytorch/mmdet/utils/logger.py deleted file mode 100755 index 4d7138745..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/logger.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import logging - -from mmcv.utils import get_logger - - -def get_root_logger(log_file=None, log_level=logging.INFO): - """Get root logger. - - Args: - log_file (str, optional): File path of log. Defaults to None. - log_level (int, optional): The level of logger. - Defaults to logging.INFO. - - Returns: - :obj:`logging.Logger`: The obtained logger - """ - logger = get_logger(name='mmdet', log_file=log_file, log_level=log_level) - - return logger - - -def get_caller_name(): - """Get name of caller method.""" - # this_func_frame = inspect.stack()[0][0] # i.e., get_caller_name - # callee_frame = inspect.stack()[1][0] # e.g., log_img_scale - caller_frame = inspect.stack()[2][0] # e.g., caller of log_img_scale - caller_method = caller_frame.f_code.co_name - try: - caller_class = caller_frame.f_locals['self'].__class__.__name__ - return f'{caller_class}.{caller_method}' - except KeyError: # caller is a function - return caller_method - - -def log_img_scale(img_scale, shape_order='hw', skip_square=False): - """Log image size. - - Args: - img_scale (tuple): Image size to be logged. - shape_order (str, optional): The order of image shape. - 'hw' for (height, width) and 'wh' for (width, height). - Defaults to 'hw'. - skip_square (bool, optional): Whether to skip logging for square - img_scale. Defaults to False. - - Returns: - bool: Whether to have done logging. - """ - if shape_order == 'hw': - height, width = img_scale - elif shape_order == 'wh': - width, height = img_scale - else: - raise ValueError(f'Invalid shape_order {shape_order}.') - - if skip_square and (height == width): - return False - - logger = get_root_logger() - caller = get_caller_name() - logger.info(f'image shape: height={height}, width={width} in {caller}') - - return True diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/memory.py b/cv/detection/autoassign/pytorch/mmdet/utils/memory.py deleted file mode 100755 index bc6d2b945..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/memory.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from collections import abc -from contextlib import contextmanager -from functools import wraps - -import torch - -from mmdet.utils import get_root_logger - - -def cast_tensor_type(inputs, src_type=None, dst_type=None): - """Recursively convert Tensor in inputs from ``src_type`` to ``dst_type``. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype | torch.device): Source type. - src_type (torch.dtype | torch.device): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - assert dst_type is not None - if isinstance(inputs, torch.Tensor): - if isinstance(dst_type, torch.device): - # convert Tensor to dst_device - if hasattr(inputs, 'to') and \ - hasattr(inputs, 'device') and \ - (inputs.device == src_type or src_type is None): - return inputs.to(dst_type) - else: - return inputs - else: - # convert Tensor to dst_dtype - if hasattr(inputs, 'to') and \ - hasattr(inputs, 'dtype') and \ - (inputs.dtype == src_type or src_type is None): - return inputs.to(dst_type) - else: - return inputs - # we need to ensure that the type of inputs to be casted are the same - # as the argument `src_type`. - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type=src_type, dst_type=dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type=src_type, dst_type=dst_type) - for item in inputs) - # TODO: Currently not supported - # elif isinstance(inputs, InstanceData): - # for key, value in inputs.items(): - # inputs[key] = cast_tensor_type( - # value, src_type=src_type, dst_type=dst_type) - # return inputs - else: - return inputs - - -@contextmanager -def _ignore_torch_cuda_oom(): - """A context which ignores CUDA OOM exception from pytorch. - - Code is modified from - # noqa: E501 - """ - try: - yield - except RuntimeError as e: - # NOTE: the string may change? - if 'CUDA out of memory. ' in str(e): - pass - else: - raise - - -class AvoidOOM: - """Try to convert inputs to FP16 and CPU if got a PyTorch's CUDA Out of - Memory error. It will do the following steps: - - 1. First retry after calling `torch.cuda.empty_cache()`. - 2. If that still fails, it will then retry by converting inputs - to FP16. - 3. If that still fails trying to convert inputs to CPUs. - In this case, it expects the function to dispatch to - CPU implementation. - - Args: - to_cpu (bool): Whether to convert outputs to CPU if get an OOM - error. This will slow down the code significantly. - Defaults to True. - test (bool): Skip `_ignore_torch_cuda_oom` operate that can use - lightweight data in unit test, only used in - test unit. Defaults to False. - - Examples: - >>> from mmdet.utils.memory import AvoidOOM - >>> AvoidCUDAOOM = AvoidOOM() - >>> output = AvoidOOM.retry_if_cuda_oom( - >>> some_torch_function)(input1, input2) - >>> # To use as a decorator - >>> # from mmdet.utils import AvoidCUDAOOM - >>> @AvoidCUDAOOM.retry_if_cuda_oom - >>> def function(*args, **kwargs): - >>> return None - ``` - - Note: - 1. The output may be on CPU even if inputs are on GPU. Processing - on CPU will slow down the code significantly. - 2. When converting inputs to CPU, it will only look at each argument - and check if it has `.device` and `.to` for conversion. Nested - structures of tensors are not supported. - 3. Since the function might be called more than once, it has to be - stateless. - """ - - def __init__(self, to_cpu=True, test=False): - self.to_cpu = to_cpu - self.test = test - - def retry_if_cuda_oom(self, func): - """Makes a function retry itself after encountering pytorch's CUDA OOM - error. - - The implementation logic is referred to - https://github.com/facebookresearch/detectron2/blob/main/detectron2/utils/memory.py - - Args: - func: a stateless callable that takes tensor-like objects - as arguments. - Returns: - func: a callable which retries `func` if OOM is encountered. - """ # noqa: W605 - - @wraps(func) - def wrapped(*args, **kwargs): - - # raw function - if not self.test: - with _ignore_torch_cuda_oom(): - return func(*args, **kwargs) - - # Clear cache and retry - torch.cuda.empty_cache() - with _ignore_torch_cuda_oom(): - return func(*args, **kwargs) - - # get the type and device of first tensor - dtype, device = None, None - values = args + tuple(kwargs.values()) - for value in values: - if isinstance(value, torch.Tensor): - dtype = value.dtype - device = value.device - break - if dtype is None or device is None: - raise ValueError('There is no tensor in the inputs, ' - 'cannot get dtype and device.') - - # Convert to FP16 - fp16_args = cast_tensor_type(args, dst_type=torch.half) - fp16_kwargs = cast_tensor_type(kwargs, dst_type=torch.half) - logger = get_root_logger() - logger.warning(f'Attempting to copy inputs of {str(func)} ' - 'to FP16 due to CUDA OOM') - - # get input tensor type, the output type will same as - # the first parameter type. - with _ignore_torch_cuda_oom(): - output = func(*fp16_args, **fp16_kwargs) - output = cast_tensor_type( - output, src_type=torch.half, dst_type=dtype) - if not self.test: - return output - logger.warning('Using FP16 still meet CUDA OOM') - - # Try on CPU. This will slow down the code significantly, - # therefore print a notice. - if self.to_cpu: - logger.warning(f'Attempting to copy inputs of {str(func)} ' - 'to CPU due to CUDA OOM') - cpu_device = torch.empty(0).device - cpu_args = cast_tensor_type(args, dst_type=cpu_device) - cpu_kwargs = cast_tensor_type(kwargs, dst_type=cpu_device) - - # convert outputs to GPU - with _ignore_torch_cuda_oom(): - logger.warning(f'Convert outputs to GPU (device={device})') - output = func(*cpu_args, **cpu_kwargs) - output = cast_tensor_type( - output, src_type=cpu_device, dst_type=device) - return output - - warnings.warn('Cannot convert output to GPU due to CUDA OOM, ' - 'the output is now on CPU, which might cause ' - 'errors if the output need to interact with GPU ' - 'data in subsequent operations') - logger.warning('Cannot convert output to GPU due to ' - 'CUDA OOM, the output is on CPU now.') - - return func(*cpu_args, **cpu_kwargs) - else: - # may still get CUDA OOM error - return func(*args, **kwargs) - - return wrapped - - -# To use AvoidOOM as a decorator -AvoidCUDAOOM = AvoidOOM() diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/misc.py b/cv/detection/autoassign/pytorch/mmdet/utils/misc.py deleted file mode 100755 index 926de2bad..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/misc.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os -import os.path as osp -import warnings - -import mmcv -from mmcv.utils import print_log - - -def find_latest_checkpoint(path, suffix='pth'): - """Find the latest checkpoint from the working directory. - - Args: - path(str): The path to find checkpoints. - suffix(str): File extension. - Defaults to pth. - - Returns: - latest_path(str | None): File path of the latest checkpoint. - References: - .. [1] https://github.com/microsoft/SoftTeacher - /blob/main/ssod/utils/patch.py - """ - if not osp.exists(path): - warnings.warn('The path of checkpoints does not exist.') - return None - if osp.exists(osp.join(path, f'latest.{suffix}')): - return osp.join(path, f'latest.{suffix}') - - checkpoints = glob.glob(osp.join(path, f'*.{suffix}')) - if len(checkpoints) == 0: - warnings.warn('There are no checkpoints in the path.') - return None - latest = -1 - latest_path = None - for checkpoint in checkpoints: - count = int(osp.basename(checkpoint).split('_')[-1].split('.')[0]) - if count > latest: - latest = count - latest_path = checkpoint - return latest_path - - -def update_data_root(cfg, logger=None): - """Update data root according to env MMDET_DATASETS. - - If set env MMDET_DATASETS, update cfg.data_root according to - MMDET_DATASETS. Otherwise, using cfg.data_root as default. - - Args: - cfg (mmcv.Config): The model config need to modify - logger (logging.Logger | str | None): the way to print msg - """ - assert isinstance(cfg, mmcv.Config), \ - f'cfg got wrong type: {type(cfg)}, expected mmcv.Config' - - if 'MMDET_DATASETS' in os.environ: - dst_root = os.environ['MMDET_DATASETS'] - print_log(f'MMDET_DATASETS has been set to be {dst_root}.' - f'Using {dst_root} as data root.') - else: - return - - assert isinstance(cfg, mmcv.Config), \ - f'cfg got wrong type: {type(cfg)}, expected mmcv.Config' - - def update(cfg, src_str, dst_str): - for k, v in cfg.items(): - if isinstance(v, mmcv.ConfigDict): - update(cfg[k], src_str, dst_str) - if isinstance(v, str) and src_str in v: - cfg[k] = v.replace(src_str, dst_str) - - update(cfg.data, cfg.data_root, dst_root) - cfg.data_root = dst_root diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/profiling.py b/cv/detection/autoassign/pytorch/mmdet/utils/profiling.py deleted file mode 100755 index 9a829e8a6..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/profiling.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import contextlib -import sys -import time - -import torch - -if sys.version_info >= (3, 7): - - @contextlib.contextmanager - def profile_time(trace_name, - name, - enabled=True, - stream=None, - end_stream=None): - """Print time spent by CPU and GPU. - - Useful as a temporary context manager to find sweet spots of code - suitable for async implementation. - """ - if (not enabled) or not torch.cuda.is_available(): - yield - return - stream = stream if stream else torch.cuda.current_stream() - end_stream = end_stream if end_stream else stream - start = torch.cuda.Event(enable_timing=True) - end = torch.cuda.Event(enable_timing=True) - stream.record_event(start) - try: - cpu_start = time.monotonic() - yield - finally: - cpu_end = time.monotonic() - end_stream.record_event(end) - end.synchronize() - cpu_time = (cpu_end - cpu_start) * 1000 - gpu_time = start.elapsed_time(end) - msg = f'{trace_name} {name} cpu_time {cpu_time:.2f} ms ' - msg += f'gpu_time {gpu_time:.2f} ms stream {stream}' - print(msg, end_stream) diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/replace_cfg_vals.py b/cv/detection/autoassign/pytorch/mmdet/utils/replace_cfg_vals.py deleted file mode 100755 index 2aebc8da9..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/replace_cfg_vals.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import re - -from mmcv.utils import Config - - -def replace_cfg_vals(ori_cfg): - """Replace the string "${key}" with the corresponding value. - - Replace the "${key}" with the value of ori_cfg.key in the config. And - support replacing the chained ${key}. Such as, replace "${key0.key1}" - with the value of cfg.key0.key1. Code is modified from `vars.py - < https://github.com/microsoft/SoftTeacher/blob/main/ssod/utils/vars.py>`_ # noqa: E501 - - Args: - ori_cfg (mmcv.utils.config.Config): - The origin config with "${key}" generated from a file. - - Returns: - updated_cfg [mmcv.utils.config.Config]: - The config with "${key}" replaced by the corresponding value. - """ - - def get_value(cfg, key): - for k in key.split('.'): - cfg = cfg[k] - return cfg - - def replace_value(cfg): - if isinstance(cfg, dict): - return {key: replace_value(value) for key, value in cfg.items()} - elif isinstance(cfg, list): - return [replace_value(item) for item in cfg] - elif isinstance(cfg, tuple): - return tuple([replace_value(item) for item in cfg]) - elif isinstance(cfg, str): - # the format of string cfg may be: - # 1) "${key}", which will be replaced with cfg.key directly - # 2) "xxx${key}xxx" or "xxx${key1}xxx${key2}xxx", - # which will be replaced with the string of the cfg.key - keys = pattern_key.findall(cfg) - values = [get_value(ori_cfg, key[2:-1]) for key in keys] - if len(keys) == 1 and keys[0] == cfg: - # the format of string cfg is "${key}" - cfg = values[0] - else: - for key, value in zip(keys, values): - # the format of string cfg is - # "xxx${key}xxx" or "xxx${key1}xxx${key2}xxx" - assert not isinstance(value, (dict, list, tuple)), \ - f'for the format of string cfg is ' \ - f"'xxxxx${key}xxxxx' or 'xxx${key}xxx${key}xxx', " \ - f"the type of the value of '${key}' " \ - f'can not be dict, list, or tuple' \ - f'but you input {type(value)} in {cfg}' - cfg = cfg.replace(key, str(value)) - return cfg - else: - return cfg - - # the pattern of string "${key}" - pattern_key = re.compile(r'\$\{[a-zA-Z\d_.]*\}') - # the type of ori_cfg._cfg_dict is mmcv.utils.config.ConfigDict - updated_cfg = Config( - replace_value(ori_cfg._cfg_dict), filename=ori_cfg.filename) - # replace the model with model_wrapper - if updated_cfg.get('model_wrapper', None) is not None: - updated_cfg.model = updated_cfg.model_wrapper - updated_cfg.pop('model_wrapper') - return updated_cfg diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/setup_env.py b/cv/detection/autoassign/pytorch/mmdet/utils/setup_env.py deleted file mode 100755 index 65bbcb97d..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/setup_env.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import platform -import warnings - -import cv2 -import torch.multiprocessing as mp - - -def setup_multi_processes(cfg): - """Setup multi-processing environment variables.""" - # set multi-process start method as `fork` to speed up the training - if platform.system() != 'Windows': - mp_start_method = cfg.get('mp_start_method', 'fork') - current_method = mp.get_start_method(allow_none=True) - if current_method is not None and current_method != mp_start_method: - warnings.warn( - f'Multi-processing start method `{mp_start_method}` is ' - f'different from the previous setting `{current_method}`.' - f'It will be force set to `{mp_start_method}`. You can change ' - f'this behavior by changing `mp_start_method` in your config.') - mp.set_start_method(mp_start_method, force=True) - - # disable opencv multithreading to avoid system being overloaded - opencv_num_threads = cfg.get('opencv_num_threads', 0) - cv2.setNumThreads(opencv_num_threads) - - # setup OMP threads - # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa - workers_per_gpu = cfg.data.get('workers_per_gpu', 1) - if 'train_dataloader' in cfg.data: - workers_per_gpu = \ - max(cfg.data.train_dataloader.get('workers_per_gpu', 1), - workers_per_gpu) - - if 'OMP_NUM_THREADS' not in os.environ and workers_per_gpu > 1: - omp_num_threads = 1 - warnings.warn( - f'Setting OMP_NUM_THREADS environment variable for each process ' - f'to be {omp_num_threads} in default, to avoid your system being ' - f'overloaded, please further tune the variable for optimal ' - f'performance in your application as needed.') - os.environ['OMP_NUM_THREADS'] = str(omp_num_threads) - - # setup MKL threads - if 'MKL_NUM_THREADS' not in os.environ and workers_per_gpu > 1: - mkl_num_threads = 1 - warnings.warn( - f'Setting MKL_NUM_THREADS environment variable for each process ' - f'to be {mkl_num_threads} in default, to avoid your system being ' - f'overloaded, please further tune the variable for optimal ' - f'performance in your application as needed.') - os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads) diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/split_batch.py b/cv/detection/autoassign/pytorch/mmdet/utils/split_batch.py deleted file mode 100755 index 0c25b6c8e..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/split_batch.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def split_batch(img, img_metas, kwargs): - """Split data_batch by tags. - - Code is modified from - # noqa: E501 - - Args: - img (Tensor): of shape (N, C, H, W) encoding input images. - Typically these should be mean centered and std scaled. - img_metas (list[dict]): List of image info dict where each dict - has: 'img_shape', 'scale_factor', 'flip', and may also contain - 'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'. - For details on the values of these keys, see - :class:`mmdet.datasets.pipelines.Collect`. - kwargs (dict): Specific to concrete implementation. - - Returns: - data_groups (dict): a dict that data_batch splited by tags, - such as 'sup', 'unsup_teacher', and 'unsup_student'. - """ - - # only stack img in the batch - def fuse_list(obj_list, obj): - return torch.stack(obj_list) if isinstance(obj, - torch.Tensor) else obj_list - - # select data with tag from data_batch - def select_group(data_batch, current_tag): - group_flag = [tag == current_tag for tag in data_batch['tag']] - return { - k: fuse_list([vv for vv, gf in zip(v, group_flag) if gf], v) - for k, v in data_batch.items() - } - - kwargs.update({'img': img, 'img_metas': img_metas}) - kwargs.update({'tag': [meta['tag'] for meta in img_metas]}) - tags = list(set(kwargs['tag'])) - data_groups = {tag: select_group(kwargs, tag) for tag in tags} - for tag, group in data_groups.items(): - group.pop('tag') - return data_groups diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/util_distribution.py b/cv/detection/autoassign/pytorch/mmdet/utils/util_distribution.py deleted file mode 100755 index 90f6adcfb..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/util_distribution.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel - -dp_factory = {'cuda': MMDataParallel, 'cpu': MMDataParallel} - -ddp_factory = {'cuda': MMDistributedDataParallel} - - -def build_dp(model, device='cuda', dim=0, *args, **kwargs): - """build DataParallel module by device type. - - if device is cuda, return a MMDataParallel model; if device is mlu, - return a MLUDataParallel model. - - Args: - model (:class:`nn.Module`): model to be parallelized. - device (str): device type, cuda, cpu or mlu. Defaults to cuda. - dim (int): Dimension used to scatter the data. Defaults to 0. - - Returns: - nn.Module: the model to be parallelized. - """ - if device == 'cuda': - model = model.cuda() - elif device == 'mlu': - from mmcv.device.mlu import MLUDataParallel - dp_factory['mlu'] = MLUDataParallel - model = model.mlu() - - return dp_factory[device](model, dim=dim, *args, **kwargs) - - -def build_ddp(model, device='cuda', *args, **kwargs): - """Build DistributedDataParallel module by device type. - - If device is cuda, return a MMDistributedDataParallel model; - if device is mlu, return a MLUDistributedDataParallel model. - - Args: - model (:class:`nn.Module`): module to be parallelized. - device (str): device type, mlu or cuda. - - Returns: - :class:`nn.Module`: the module to be parallelized - - References: - .. [1] https://pytorch.org/docs/stable/generated/torch.nn.parallel. - DistributedDataParallel.html - """ - assert device in ['cuda', 'mlu'], 'Only available for cuda or mlu devices.' - if device == 'cuda': - model = model.cuda() - elif device == 'mlu': - from mmcv.device.mlu import MLUDistributedDataParallel - ddp_factory['mlu'] = MLUDistributedDataParallel - model = model.mlu() - - return ddp_factory[device](model, *args, **kwargs) - - -def is_mlu_available(): - """Returns a bool indicating if MLU is currently available.""" - return hasattr(torch, 'is_mlu_available') and torch.is_mlu_available() - - -def get_device(): - """Returns an available device, cpu, cuda or mlu.""" - is_device_available = { - 'cuda': torch.cuda.is_available(), - 'mlu': is_mlu_available() - } - device_list = [k for k, v in is_device_available.items() if v] - return device_list[0] if len(device_list) == 1 else 'cpu' diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/util_mixins.py b/cv/detection/autoassign/pytorch/mmdet/utils/util_mixins.py deleted file mode 100755 index 655ed821b..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/util_mixins.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This module defines the :class:`NiceRepr` mixin class, which defines a -``__repr__`` and ``__str__`` method that only depend on a custom ``__nice__`` -method, which you must define. This means you only have to overload one -function instead of two. Furthermore, if the object defines a ``__len__`` -method, then the ``__nice__`` method defaults to something sensible, otherwise -it is treated as abstract and raises ``NotImplementedError``. - -To use simply have your object inherit from :class:`NiceRepr` -(multi-inheritance should be ok). - -This code was copied from the ubelt library: https://github.com/Erotemic/ubelt - -Example: - >>> # Objects that define __nice__ have a default __str__ and __repr__ - >>> class Student(NiceRepr): - ... def __init__(self, name): - ... self.name = name - ... def __nice__(self): - ... return self.name - >>> s1 = Student('Alice') - >>> s2 = Student('Bob') - >>> print(f's1 = {s1}') - >>> print(f's2 = {s2}') - s1 = - s2 = - -Example: - >>> # Objects that define __len__ have a default __nice__ - >>> class Group(NiceRepr): - ... def __init__(self, data): - ... self.data = data - ... def __len__(self): - ... return len(self.data) - >>> g = Group([1, 2, 3]) - >>> print(f'g = {g}') - g = -""" -import warnings - - -class NiceRepr: - """Inherit from this class and define ``__nice__`` to "nicely" print your - objects. - - Defines ``__str__`` and ``__repr__`` in terms of ``__nice__`` function - Classes that inherit from :class:`NiceRepr` should redefine ``__nice__``. - If the inheriting class has a ``__len__``, method then the default - ``__nice__`` method will return its length. - - Example: - >>> class Foo(NiceRepr): - ... def __nice__(self): - ... return 'info' - >>> foo = Foo() - >>> assert str(foo) == '' - >>> assert repr(foo).startswith('>> class Bar(NiceRepr): - ... pass - >>> bar = Bar() - >>> import pytest - >>> with pytest.warns(None) as record: - >>> assert 'object at' in str(bar) - >>> assert 'object at' in repr(bar) - - Example: - >>> class Baz(NiceRepr): - ... def __len__(self): - ... return 5 - >>> baz = Baz() - >>> assert str(baz) == '' - """ - - def __nice__(self): - """str: a "nice" summary string describing this module""" - if hasattr(self, '__len__'): - # It is a common pattern for objects to use __len__ in __nice__ - # As a convenience we define a default __nice__ for these objects - return str(len(self)) - else: - # In all other cases force the subclass to overload __nice__ - raise NotImplementedError( - f'Define the __nice__ method for {self.__class__!r}') - - def __repr__(self): - """str: the string of the module""" - try: - nice = self.__nice__() - classname = self.__class__.__name__ - return f'<{classname}({nice}) at {hex(id(self))}>' - except NotImplementedError as ex: - warnings.warn(str(ex), category=RuntimeWarning) - return object.__repr__(self) - - def __str__(self): - """str: the string of the module""" - try: - classname = self.__class__.__name__ - nice = self.__nice__() - return f'<{classname}({nice})>' - except NotImplementedError as ex: - warnings.warn(str(ex), category=RuntimeWarning) - return object.__repr__(self) diff --git a/cv/detection/autoassign/pytorch/mmdet/utils/util_random.py b/cv/detection/autoassign/pytorch/mmdet/utils/util_random.py deleted file mode 100755 index 46c83709b..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/utils/util_random.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""Helpers for random number generators.""" -import numpy as np - - -def ensure_rng(rng=None): - """Coerces input into a random number generator. - - If the input is None, then a global random state is returned. - - If the input is a numeric value, then that is used as a seed to construct a - random state. Otherwise the input is returned as-is. - - Adapted from [1]_. - - Args: - rng (int | numpy.random.RandomState | None): - if None, then defaults to the global rng. Otherwise this can be an - integer or a RandomState class - Returns: - (numpy.random.RandomState) : rng - - a numpy random number generator - - References: - .. [1] https://gitlab.kitware.com/computer-vision/kwarray/blob/master/kwarray/util_random.py#L270 # noqa: E501 - """ - - if rng is None: - rng = np.random.mtrand._rand - elif isinstance(rng, int): - rng = np.random.RandomState(rng) - else: - rng = rng - return rng diff --git a/cv/detection/autoassign/pytorch/mmdet/version.py b/cv/detection/autoassign/pytorch/mmdet/version.py deleted file mode 100755 index 524a728fd..000000000 --- a/cv/detection/autoassign/pytorch/mmdet/version.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -__version__ = '2.25.0' -short_version = __version__ - - -def parse_version_info(version_str): - version_info = [] - for x in version_str.split('.'): - if x.isdigit(): - version_info.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - version_info.append(int(patch_version[0])) - version_info.append(f'rc{patch_version[1]}') - return tuple(version_info) - - -version_info = parse_version_info(__version__) diff --git a/cv/detection/autoassign/pytorch/pytest.ini b/cv/detection/autoassign/pytorch/pytest.ini deleted file mode 100755 index f3687d91a..000000000 --- a/cv/detection/autoassign/pytorch/pytest.ini +++ /dev/null @@ -1,7 +0,0 @@ -[pytest] -addopts = --xdoctest --xdoctest-style=auto -norecursedirs = .git ignore build __pycache__ data docker docs .eggs - -filterwarnings= default - ignore:.*No cfgstr given in Cacher constructor or call.*:Warning - ignore:.*Define the __nice__ method for.*:Warning diff --git a/cv/detection/autoassign/pytorch/requirements.txt b/cv/detection/autoassign/pytorch/requirements.txt deleted file mode 100755 index 9fbc05253..000000000 --- a/cv/detection/autoassign/pytorch/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -addict -yapf -matplotlib -pycocotools -six -terminaltables -opencv-python diff --git a/cv/detection/autoassign/pytorch/setup.py b/cv/detection/autoassign/pytorch/setup.py deleted file mode 100755 index 3cc9f7a46..000000000 --- a/cv/detection/autoassign/pytorch/setup.py +++ /dev/null @@ -1,354 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -import glob -import os -import re -from pkg_resources import DistributionNotFound, get_distribution -from setuptools import find_packages, setup - -EXT_TYPE = '' -try: - import torch - if torch.__version__ == 'parrots': - from parrots.utils.build_extension import BuildExtension - EXT_TYPE = 'parrots' - else: - from torch.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - cmd_class = {'build_ext': BuildExtension} -except ModuleNotFoundError: - cmd_class = {} - print('Skip building ext ops due to the absence of torch.') - - -def choose_requirement(primary, secondary): - """If some version of primary requirement installed, return primary, else - return secondary.""" - try: - name = re.split(r'[!<>=]', primary)[0] - get_distribution(name) - except DistributionNotFound: - return secondary - - return str(primary) - - -def get_version(): - version_file = 'mmcv/version.py' - with open(version_file, 'r', encoding='utf-8') as f: - exec(compile(f.read(), version_file, 'exec')) - version = locals()['__version__'] - local_version_identifier = os.environ.get('MMCV_LOCAL_VERSION_IDENTIFIER', '') - if local_version_identifier != '': - version += '+' + local_version_identifier - return version - - -def parse_requirements(fname='requirements/runtime.txt', with_version=True): - """Parse the package dependencies listed in a requirements file but strips - specific versioning information. - - Args: - fname (str): path to requirements file - with_version (bool, default=False): if True include version specs - - Returns: - List[str]: list of requirements items - - CommandLine: - python -c "import setup; print(setup.parse_requirements())" - """ - import sys - from os.path import exists - require_fpath = fname - - def parse_line(line): - """Parse information from a line in a requirements text file.""" - if line.startswith('-r '): - # Allow specifying requirements in other files - target = line.split(' ')[1] - for info in parse_require_file(target): - yield info - else: - info = {'line': line} - if line.startswith('-e '): - info['package'] = line.split('#egg=')[1] - else: - # Remove versioning from the package - pat = '(' + '|'.join(['>=', '==', '>']) + ')' - parts = re.split(pat, line, maxsplit=1) - parts = [p.strip() for p in parts] - - info['package'] = parts[0] - if len(parts) > 1: - op, rest = parts[1:] - if ';' in rest: - # Handle platform specific dependencies - # http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies - version, platform_deps = map(str.strip, - rest.split(';')) - info['platform_deps'] = platform_deps - else: - version = rest # NOQA - info['version'] = (op, version) - yield info - - def parse_require_file(fpath): - with open(fpath, 'r') as f: - for line in f.readlines(): - line = line.strip() - if line and not line.startswith('#'): - for info in parse_line(line): - yield info - - def gen_packages_items(): - if exists(require_fpath): - for info in parse_require_file(require_fpath): - parts = [info['package']] - if with_version and 'version' in info: - parts.extend(info['version']) - if not sys.version.startswith('3.4'): - # apparently package_deps are broken in 3.4 - platform_deps = info.get('platform_deps') - if platform_deps is not None: - parts.append(';' + platform_deps) - item = ''.join(parts) - yield item - - packages = list(gen_packages_items()) - return packages - - -install_requires = parse_requirements() - -try: - # OpenCV installed via conda. - import cv2 # NOQA: F401 - major, minor, *rest = cv2.__version__.split('.') - if int(major) < 3: - raise RuntimeError( - f'OpenCV >=3 is required but {cv2.__version__} is installed') -except ImportError: - # If first not installed install second package - CHOOSE_INSTALL_REQUIRES = [('opencv-python-headless>=3', - 'opencv-python>=3')] - for main, secondary in CHOOSE_INSTALL_REQUIRES: - install_requires.append(choose_requirement(main, secondary)) - - -def get_extensions(): - extensions = [] - - if os.getenv('MMCV_WITH_TRT', '0') != '0': - ext_name = 'mmcv._ext_trt' - from torch.utils.cpp_extension import include_paths, library_paths - library_dirs = [] - libraries = [] - include_dirs = [] - tensorrt_path = os.getenv('TENSORRT_DIR', '0') - tensorrt_lib_path = glob.glob( - os.path.join(tensorrt_path, 'targets', '*', 'lib'))[0] - library_dirs += [tensorrt_lib_path] - libraries += ['nvinfer', 'nvparsers', 'nvinfer_plugin'] - libraries += ['cudart'] - define_macros = [] - extra_compile_args = {'cxx': []} - - include_path = os.path.abspath('./mmcv/ops/csrc/common/cuda') - include_trt_path = os.path.abspath('./mmcv/ops/csrc/tensorrt') - include_dirs.append(include_path) - include_dirs.append(include_trt_path) - include_dirs.append(os.path.join(tensorrt_path, 'include')) - include_dirs += include_paths(cuda=True) - - op_files = glob.glob('./mmcv/ops/csrc/tensorrt/plugins/*') - define_macros += [('MMCV_WITH_CUDA', None)] - define_macros += [('MMCV_WITH_TRT', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - library_dirs += library_paths(cuda=True) - - from setuptools import Extension - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - language='c++', - library_dirs=library_dirs, - libraries=libraries) - extensions.append(ext_ops) - - if os.getenv('MMCV_WITH_OPS', '0') == '0': - return extensions - - if EXT_TYPE == 'parrots': - ext_name = 'mmcv._ext' - from parrots.utils.build_extension import Extension - # new parrots op impl do not use MMCV_USE_PARROTS - # define_macros = [('MMCV_USE_PARROTS', None)] - define_macros = [] - include_dirs = [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') +\ - glob.glob('./mmcv/ops/csrc/parrots/*.cpp') - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args = { - 'nvcc': [cuda_args] if cuda_args else [], - 'cxx': [], - } - if torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - extra_compile_args['nvcc'] += [ - '-D__CUDA_NO_HALF_OPERATORS__', - '-D__CUDA_NO_HALF_CONVERSIONS__', - '-D__CUDA_NO_HALF2_OPERATORS__', - ] - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - cuda=True, - pytorch=True) - extensions.append(ext_ops) - elif EXT_TYPE == 'pytorch': - ext_name = 'mmcv._ext' - from torch.utils.cpp_extension import CppExtension, CUDAExtension - - # prevent ninja from using too many resources - try: - import psutil - num_cpu = len(psutil.Process().cpu_affinity()) - cpu_use = max(4, num_cpu - 1) - except (ModuleNotFoundError, AttributeError): - cpu_use = 4 - - os.environ.setdefault('MAX_JOBS', str(cpu_use)) - define_macros = [] - extra_compile_args = {'cxx': []} - include_dirs = [] - - is_rocm_pytorch = False - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm_pytorch = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - - project_dir = 'mmcv/ops/csrc/' - if is_rocm_pytorch: - from torch.utils.hipify import hipify_python - - hipify_python.hipify( - project_directory=project_dir, - output_directory=project_dir, - includes='mmcv/ops/csrc/*', - show_detailed=True, - is_pytorch_extension=True, - ) - define_macros += [('MMCV_WITH_CUDA', None)] - define_macros += [('HIP_DIFF', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/hip/*') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/hip')) - elif torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - else: - print(f'Compiling {ext_name} without CUDA') - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') - extension = CppExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - - ext_ops = extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args) - extensions.append(ext_ops) - - if EXT_TYPE == 'pytorch' and os.getenv('MMCV_WITH_ORT', '0') != '0': - ext_name = 'mmcv._ext_ort' - from torch.utils.cpp_extension import library_paths, include_paths - import onnxruntime - library_dirs = [] - libraries = [] - include_dirs = [] - ort_path = os.getenv('ONNXRUNTIME_DIR', '0') - library_dirs += [os.path.join(ort_path, 'lib')] - libraries.append('onnxruntime') - define_macros = [] - extra_compile_args = {'cxx': []} - - include_path = os.path.abspath('./mmcv/ops/csrc/onnxruntime') - include_dirs.append(include_path) - include_dirs.append(os.path.join(ort_path, 'include')) - - op_files = glob.glob('./mmcv/ops/csrc/onnxruntime/cpu/*') - if onnxruntime.get_device() == 'GPU' or os.getenv('FORCE_CUDA', - '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files += glob.glob('./mmcv/ops/csrc/onnxruntime/gpu/*') - include_dirs += include_paths(cuda=True) - library_dirs += library_paths(cuda=True) - else: - include_dirs += include_paths(cuda=False) - library_dirs += library_paths(cuda=False) - - from setuptools import Extension - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - language='c++', - library_dirs=library_dirs, - libraries=libraries) - extensions.append(ext_ops) - - return extensions - - -setup( - name='mmcv' if os.getenv('MMCV_WITH_OPS', '0') == '0' else 'mmcv-full', - version=get_version(), - description='OpenMMLab Computer Vision Foundation', - keywords='computer vision', - packages=find_packages(), - include_package_data=True, - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Topic :: Utilities', - ], - url='https://github.com/open-mmlab/mmcv', - author='MMCV Contributors', - author_email='openmmlab@gmail.com', - setup_requires=[], - tests_require=['pytest'], - install_requires=install_requires, - ext_modules=get_extensions(), - cmdclass=cmd_class, - zip_safe=False) diff --git a/cv/detection/autoassign/pytorch/train.py b/cv/detection/autoassign/pytorch/train.py deleted file mode 100755 index 0bfa664e8..000000000 --- a/cv/detection/autoassign/pytorch/train.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import argparse -import copy -import os -import os.path as osp -import time -import warnings - -import mmcv -import torch -import torch.distributed as dist -from mmcv import Config, DictAction -from mmcv.runner import get_dist_info, init_dist -from mmcv.utils import get_git_hash -import sys -sys.path.append('./') -from mmdet import __version__ -from mmdet.apis import init_random_seed, set_random_seed, train_detector -from mmdet.datasets import build_dataset -from mmdet.models import build_detector -from mmdet.utils import (collect_env, get_device, get_root_logger, - replace_cfg_vals, setup_multi_processes, - update_data_root) - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train a detector') - parser.add_argument('config', help='train config file path') - parser.add_argument('--work-dir', help='the dir to save logs and models') - parser.add_argument( - '--resume-from', help='the checkpoint file to resume from') - parser.add_argument( - '--auto-resume', - action='store_true', - help='resume from the latest checkpoint automatically') - parser.add_argument( - '--no-validate', - action='store_true', - help='whether not to evaluate the checkpoint during training') - group_gpus = parser.add_mutually_exclusive_group() - group_gpus.add_argument( - '--gpus', - type=int, - help='(Deprecated, please use --gpu-id) number of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-ids', - type=int, - nargs='+', - help='(Deprecated, please use --gpu-id) ids of gpus to use ' - '(only applicable to non-distributed training)') - group_gpus.add_argument( - '--gpu-id', - type=int, - default=0, - help='id of gpu to use ' - '(only applicable to non-distributed training)') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--diff-seed', - action='store_true', - help='Whether or not set different seeds for different ranks') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument( - '--options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file (deprecate), ' - 'change to --cfg-options instead.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - parser.add_argument( - '--auto-scale-lr', - action='store_true', - help='enable automatically scaling LR.') - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - if args.options and args.cfg_options: - raise ValueError( - '--options and --cfg-options cannot be both ' - 'specified, --options is deprecated in favor of --cfg-options') - if args.options: - warnings.warn('--options is deprecated in favor of --cfg-options') - args.cfg_options = args.options - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - - # replace the ${key} with the value of cfg.key - cfg = replace_cfg_vals(cfg) - - # update data root according to MMDET_DATASETS - update_data_root(cfg) - - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - if args.auto_scale_lr: - if 'auto_scale_lr' in cfg and \ - 'enable' in cfg.auto_scale_lr and \ - 'base_batch_size' in cfg.auto_scale_lr: - cfg.auto_scale_lr.enable = True - else: - warnings.warn('Can not find "auto_scale_lr" or ' - '"auto_scale_lr.enable" or ' - '"auto_scale_lr.base_batch_size" in your' - ' configuration file. Please update all the ' - 'configuration files to mmdet >= 2.24.1.') - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - - # work_dir is determined in this priority: CLI > segment in file > filename - if args.work_dir is not None: - # update configs according to CLI args if args.work_dir is not None - cfg.work_dir = args.work_dir - elif cfg.get('work_dir', None) is None: - # use config filename as default work_dir if cfg.work_dir is None - cfg.work_dir = osp.join('./work_dirs', - osp.splitext(osp.basename(args.config))[0]) - - if args.resume_from is not None: - cfg.resume_from = args.resume_from - cfg.auto_resume = args.auto_resume - if args.gpus is not None: - cfg.gpu_ids = range(1) - warnings.warn('`--gpus` is deprecated because we only support ' - 'single GPU mode in non-distributed training. ' - 'Use `gpus=1` now.') - if args.gpu_ids is not None: - cfg.gpu_ids = args.gpu_ids[0:1] - warnings.warn('`--gpu-ids` is deprecated, please use `--gpu-id`. ' - 'Because we only support single GPU mode in ' - 'non-distributed training. Use the first GPU ' - 'in `gpu_ids` now.') - if args.gpus is None and args.gpu_ids is None: - cfg.gpu_ids = [args.gpu_id] - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - # re-set gpu_ids with distributed training mode - _, world_size = get_dist_info() - cfg.gpu_ids = range(world_size) - - # create work_dir - mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) - # dump config - cfg.dump(osp.join(cfg.work_dir, osp.basename(args.config))) - # init the logger before other steps - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - log_file = osp.join(cfg.work_dir, f'{timestamp}.log') - logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) - - # init the meta dict to record some important information such as - # environment info and seed, which will be logged - meta = dict() - # log env info - env_info_dict = collect_env() - env_info = '\n'.join([(f'{k}: {v}') for k, v in env_info_dict.items()]) - dash_line = '-' * 60 + '\n' - logger.info('Environment info:\n' + dash_line + env_info + '\n' + - dash_line) - meta['env_info'] = env_info - meta['config'] = cfg.pretty_text - # log some basic info - logger.info(f'Distributed training: {distributed}') - logger.info(f'Config:\n{cfg.pretty_text}') - - cfg.device = get_device() - # set random seeds - seed = init_random_seed(args.seed, device=cfg.device) - seed = seed + dist.get_rank() if args.diff_seed else seed - logger.info(f'Set random seed to {seed}, ' - f'deterministic: {args.deterministic}') - set_random_seed(seed, deterministic=args.deterministic) - cfg.seed = seed - meta['seed'] = seed - meta['exp_name'] = osp.basename(args.config) - - model = build_detector( - cfg.model, - train_cfg=cfg.get('train_cfg'), - test_cfg=cfg.get('test_cfg')) - model.init_weights() - - datasets = [build_dataset(cfg.data.train)] - if len(cfg.workflow) == 2: - val_dataset = copy.deepcopy(cfg.data.val) - val_dataset.pipeline = cfg.data.train.pipeline - datasets.append(build_dataset(val_dataset)) - if cfg.checkpoint_config is not None: - # save mmdet version, config file content and class names in - # checkpoints as meta data - cfg.checkpoint_config.meta = dict( - mmdet_version=__version__ + get_git_hash()[:7], - CLASSES=datasets[0].CLASSES) - # add an attribute for visualization convenience - model.CLASSES = datasets[0].CLASSES - train_detector( - model, - datasets, - cfg, - distributed=distributed, - validate=(not args.no_validate), - timestamp=timestamp, - meta=meta) - - -if __name__ == '__main__': - main() -- Gitee From a7ba50cc6671cb97aa8f52038384f759360916e7 Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Thu, 6 Mar 2025 11:03:41 +0800 Subject: [PATCH 12/15] update liif --- cv/super_resolution/liif/pytorch/.gitignore | 135 -- cv/super_resolution/liif/pytorch/LICENSE | 203 --- cv/super_resolution/liif/pytorch/README.md | 34 +- .../pytorch/configs/restorers/liif/README.md | 51 - .../configs/restorers/liif/README_zh-CN.md | 41 - .../liif_edsr_norm_c64b16_g1_1000k_div2k.py | 161 -- ...iif_rdn_norm_x2-4_c64b16_g1_1000k_div2k.py | 161 -- .../configs/restorers/liif/metafile.yml | 237 --- cv/super_resolution/liif/pytorch/dist_test.sh | 37 - .../liif/pytorch/dist_train.sh | 35 - .../liif/pytorch/docker/Dockerfile | 24 - .../liif/pytorch/docker/README.md | 19 - .../liif/pytorch/mmcv/__init__.py | 14 - .../liif/pytorch/mmcv/arraymisc/__init__.py | 4 - .../pytorch/mmcv/arraymisc/quantization.py | 65 - .../liif/pytorch/mmcv/cnn/__init__.py | 41 - .../liif/pytorch/mmcv/cnn/alexnet.py | 63 - .../liif/pytorch/mmcv/cnn/bricks/__init__.py | 35 - .../pytorch/mmcv/cnn/bricks/activation.py | 93 -- .../pytorch/mmcv/cnn/bricks/context_block.py | 125 -- .../liif/pytorch/mmcv/cnn/bricks/conv.py | 44 - .../cnn/bricks/conv2d_adaptive_padding.py | 62 - .../pytorch/mmcv/cnn/bricks/conv_module.py | 206 --- .../liif/pytorch/mmcv/cnn/bricks/conv_ws.py | 148 -- .../bricks/depthwise_separable_conv_module.py | 96 -- .../liif/pytorch/mmcv/cnn/bricks/drop.py | 65 - .../mmcv/cnn/bricks/generalized_attention.py | 412 ----- .../liif/pytorch/mmcv/cnn/bricks/hsigmoid.py | 46 - .../liif/pytorch/mmcv/cnn/bricks/hswish.py | 38 - .../liif/pytorch/mmcv/cnn/bricks/non_local.py | 303 ---- .../liif/pytorch/mmcv/cnn/bricks/norm.py | 144 -- .../liif/pytorch/mmcv/cnn/bricks/padding.py | 36 - .../liif/pytorch/mmcv/cnn/bricks/plugin.py | 89 -- .../liif/pytorch/mmcv/cnn/bricks/registry.py | 16 - .../liif/pytorch/mmcv/cnn/bricks/scale.py | 21 - .../liif/pytorch/mmcv/cnn/bricks/swish.py | 25 - .../pytorch/mmcv/cnn/bricks/transformer.py | 944 ------------ .../liif/pytorch/mmcv/cnn/bricks/upsample.py | 84 -- .../liif/pytorch/mmcv/cnn/bricks/wrappers.py | 180 --- .../liif/pytorch/mmcv/cnn/builder.py | 30 - .../liif/pytorch/mmcv/cnn/resnet.py | 322 ---- .../liif/pytorch/mmcv/cnn/utils/__init__.py | 19 - .../pytorch/mmcv/cnn/utils/flops_counter.py | 603 -------- .../pytorch/mmcv/cnn/utils/fuse_conv_bn.py | 59 - .../liif/pytorch/mmcv/cnn/utils/sync_bn.py | 61 - .../pytorch/mmcv/cnn/utils/weight_init.py | 708 --------- .../liif/pytorch/mmcv/cnn/vgg.py | 177 --- .../liif/pytorch/mmcv/device/__init__.py | 4 - .../liif/pytorch/mmcv/device/ipu/__init__.py | 14 - .../pytorch/mmcv/device/ipu/dataloader.py | 157 -- .../device/ipu/hierarchical_data_manager.py | 243 --- .../pytorch/mmcv/device/ipu/hook_wrapper.py | 105 -- .../pytorch/mmcv/device/ipu/model_wrapper.py | 721 --------- .../liif/pytorch/mmcv/device/ipu/runner.py | 142 -- .../liif/pytorch/mmcv/device/ipu/utils.py | 244 --- .../liif/pytorch/mmcv/device/mlu/__init__.py | 9 - .../pytorch/mmcv/device/mlu/_functions.py | 24 - .../pytorch/mmcv/device/mlu/data_parallel.py | 41 - .../pytorch/mmcv/device/mlu/distributed.py | 20 - .../pytorch/mmcv/device/mlu/scatter_gather.py | 59 - .../liif/pytorch/mmcv/engine/__init__.py | 8 - .../liif/pytorch/mmcv/engine/test.py | 213 --- .../liif/pytorch/mmcv/fileio/__init__.py | 11 - .../liif/pytorch/mmcv/fileio/file_client.py | 1173 --------------- .../pytorch/mmcv/fileio/handlers/__init__.py | 7 - .../liif/pytorch/mmcv/fileio/handlers/base.py | 30 - .../mmcv/fileio/handlers/json_handler.py | 36 - .../mmcv/fileio/handlers/pickle_handler.py | 26 - .../mmcv/fileio/handlers/yaml_handler.py | 25 - .../liif/pytorch/mmcv/fileio/io.py | 163 -- .../liif/pytorch/mmcv/fileio/parse.py | 99 -- .../liif/pytorch/mmcv/image/__init__.py | 29 - .../liif/pytorch/mmcv/image/colorspace.py | 309 ---- .../liif/pytorch/mmcv/image/geometric.py | 741 --------- .../liif/pytorch/mmcv/image/io.py | 314 ---- .../liif/pytorch/mmcv/image/misc.py | 53 - .../liif/pytorch/mmcv/image/photometric.py | 471 ------ .../liif/pytorch/mmcv/parallel/__init__.py | 13 - .../liif/pytorch/mmcv/parallel/_functions.py | 76 - .../liif/pytorch/mmcv/parallel/collate.py | 84 -- .../pytorch/mmcv/parallel/data_container.py | 89 -- .../pytorch/mmcv/parallel/data_parallel.py | 97 -- .../liif/pytorch/mmcv/parallel/distributed.py | 138 -- .../mmcv/parallel/distributed_deprecated.py | 70 - .../liif/pytorch/mmcv/parallel/registry.py | 8 - .../pytorch/mmcv/parallel/scatter_gather.py | 59 - .../liif/pytorch/mmcv/parallel/utils.py | 30 - .../liif/pytorch/mmcv/runner/__init__.py | 73 - .../liif/pytorch/mmcv/runner/base_module.py | 213 --- .../liif/pytorch/mmcv/runner/base_runner.py | 544 ------- .../liif/pytorch/mmcv/runner/builder.py | 25 - .../liif/pytorch/mmcv/runner/checkpoint.py | 800 ---------- .../mmcv/runner/default_constructor.py | 47 - .../liif/pytorch/mmcv/runner/dist_utils.py | 209 --- .../pytorch/mmcv/runner/epoch_based_runner.py | 191 --- .../liif/pytorch/mmcv/runner/fp16_utils.py | 435 ------ .../pytorch/mmcv/runner/hooks/__init__.py | 48 - .../pytorch/mmcv/runner/hooks/checkpoint.py | 167 --- .../liif/pytorch/mmcv/runner/hooks/closure.py | 11 - .../liif/pytorch/mmcv/runner/hooks/ema.py | 89 -- .../pytorch/mmcv/runner/hooks/evaluation.py | 511 ------- .../liif/pytorch/mmcv/runner/hooks/hook.py | 92 -- .../pytorch/mmcv/runner/hooks/iter_timer.py | 30 - .../mmcv/runner/hooks/logger/__init__.py | 18 - .../pytorch/mmcv/runner/hooks/logger/base.py | 172 --- .../mmcv/runner/hooks/logger/clearml.py | 63 - .../mmcv/runner/hooks/logger/dvclive.py | 69 - .../mmcv/runner/hooks/logger/mlflow.py | 81 - .../mmcv/runner/hooks/logger/neptune.py | 89 -- .../pytorch/mmcv/runner/hooks/logger/pavi.py | 132 -- .../mmcv/runner/hooks/logger/segmind.py | 48 - .../mmcv/runner/hooks/logger/tensorboard.py | 69 - .../pytorch/mmcv/runner/hooks/logger/text.py | 256 ---- .../pytorch/mmcv/runner/hooks/logger/wandb.py | 107 -- .../pytorch/mmcv/runner/hooks/lr_updater.py | 751 ---------- .../liif/pytorch/mmcv/runner/hooks/memory.py | 25 - .../mmcv/runner/hooks/momentum_updater.py | 566 ------- .../pytorch/mmcv/runner/hooks/optimizer.py | 554 ------- .../pytorch/mmcv/runner/hooks/profiler.py | 180 --- .../pytorch/mmcv/runner/hooks/sampler_seed.py | 20 - .../pytorch/mmcv/runner/hooks/sync_buffer.py | 22 - .../pytorch/mmcv/runner/iter_based_runner.py | 277 ---- .../liif/pytorch/mmcv/runner/log_buffer.py | 41 - .../pytorch/mmcv/runner/optimizer/__init__.py | 9 - .../pytorch/mmcv/runner/optimizer/builder.py | 45 - .../runner/optimizer/default_constructor.py | 258 ---- .../liif/pytorch/mmcv/runner/priority.py | 61 - .../liif/pytorch/mmcv/runner/utils.py | 99 -- .../liif/pytorch/mmcv/utils/__init__.py | 78 - .../liif/pytorch/mmcv/utils/config.py | 741 --------- .../liif/pytorch/mmcv/utils/device_type.py | 24 - .../liif/pytorch/mmcv/utils/env.py | 120 -- .../liif/pytorch/mmcv/utils/ext_loader.py | 72 - .../liif/pytorch/mmcv/utils/hub.py | 131 -- .../liif/pytorch/mmcv/utils/logging.py | 111 -- .../liif/pytorch/mmcv/utils/misc.py | 377 ----- .../liif/pytorch/mmcv/utils/parrots_jit.py | 41 - .../pytorch/mmcv/utils/parrots_wrapper.py | 114 -- .../liif/pytorch/mmcv/utils/path.py | 101 -- .../liif/pytorch/mmcv/utils/progressbar.py | 208 --- .../liif/pytorch/mmcv/utils/registry.py | 340 ----- .../liif/pytorch/mmcv/utils/seed.py | 23 - .../liif/pytorch/mmcv/utils/testing.py | 141 -- .../liif/pytorch/mmcv/utils/timer.py | 118 -- .../liif/pytorch/mmcv/utils/trace.py | 24 - .../liif/pytorch/mmcv/utils/version_utils.py | 90 -- .../liif/pytorch/mmcv/version.py | 35 - .../liif/pytorch/mmedit/__init__.py | 37 - .../liif/pytorch/mmedit/apis/__init__.py | 14 - .../mmedit/apis/generation_inference.py | 63 - .../mmedit/apis/restoration_inference.py | 48 - .../liif/pytorch/mmedit/apis/test.py | 234 --- .../liif/pytorch/mmedit/apis/train.py | 361 ----- .../liif/pytorch/mmedit/core/__init__.py | 16 - .../mmedit/core/distributed_wrapper.py | 139 -- .../mmedit/core/evaluation/__init__.py | 12 - .../mmedit/core/evaluation/eval_hooks.py | 114 -- .../mmedit/core/evaluation/metric_utils.py | 81 - .../pytorch/mmedit/core/evaluation/metrics.py | 433 ------ .../core/evaluation/niqe_pris_params.npz | Bin 11850 -> 0 bytes .../pytorch/mmedit/core/export/__init__.py | 4 - .../pytorch/mmedit/core/export/wrappers.py | 134 -- .../pytorch/mmedit/core/hooks/__init__.py | 5 - .../liif/pytorch/mmedit/core/hooks/ema.py | 113 -- .../mmedit/core/hooks/visualization.py | 84 -- .../liif/pytorch/mmedit/core/mask.py | 316 ---- .../liif/pytorch/mmedit/core/misc.py | 74 - .../pytorch/mmedit/core/optimizer/__init__.py | 4 - .../pytorch/mmedit/core/optimizer/builder.py | 58 - .../pytorch/mmedit/core/scheduler/__init__.py | 4 - .../mmedit/core/scheduler/lr_updater.py | 304 ---- .../pytorch/mmedit/core/utils/__init__.py | 4 - .../pytorch/mmedit/core/utils/dist_utils.py | 35 - .../liif/pytorch/mmedit/datasets/__init__.py | 17 - .../pytorch/mmedit/datasets/base_dataset.py | 78 - .../mmedit/datasets/base_sr_dataset.py | 87 -- .../liif/pytorch/mmedit/datasets/builder.py | 181 --- .../mmedit/datasets/dataset_wrappers.py | 39 - .../mmedit/datasets/pipelines/__init__.py | 36 - .../mmedit/datasets/pipelines/augmentation.py | 1332 ----------------- .../mmedit/datasets/pipelines/compose.py | 53 - .../mmedit/datasets/pipelines/formating.py | 263 ---- .../datasets/pipelines/generate_assistant.py | 169 --- .../mmedit/datasets/pipelines/loading.py | 562 ------- .../datasets/pipelines/normalization.py | 103 -- .../pipelines/random_down_sampling.py | 125 -- .../mmedit/datasets/pipelines/utils.py | 154 -- .../liif/pytorch/mmedit/datasets/registry.py | 5 - .../mmedit/datasets/samplers/__init__.py | 4 - .../datasets/samplers/distributed_sampler.py | 71 - .../mmedit/datasets/sr_folder_dataset.py | 86 -- .../mmedit/datasets/sr_folder_gt_dataset.py | 64 - .../liif/pytorch/mmedit/models/__init__.py | 13 - .../mmedit/models/backbones/__init__.py | 5 - .../models/backbones/sr_backbones/__init__.py | 7 - .../models/backbones/sr_backbones/edsr.py | 132 -- .../models/backbones/sr_backbones/liif_net.py | 322 ---- .../models/backbones/sr_backbones/rdn.py | 201 --- .../liif/pytorch/mmedit/models/base.py | 105 -- .../liif/pytorch/mmedit/models/builder.py | 60 - .../pytorch/mmedit/models/common/__init__.py | 32 - .../liif/pytorch/mmedit/models/common/aspp.py | 125 -- .../models/common/contextual_attention.py | 379 ----- .../liif/pytorch/mmedit/models/common/conv.py | 6 - .../mmedit/models/common/downsample.py | 22 - .../pytorch/mmedit/models/common/ensemble.py | 105 -- .../pytorch/mmedit/models/common/flow_warp.py | 50 - .../mmedit/models/common/gated_conv_module.py | 72 - .../mmedit/models/common/gca_module.py | 358 ----- .../models/common/generation_model_utils.py | 301 ---- .../mmedit/models/common/img_normalize.py | 32 - .../mmedit/models/common/linear_module.py | 89 -- .../mmedit/models/common/mask_conv_module.py | 88 -- .../mmedit/models/common/model_utils.py | 136 -- .../mmedit/models/common/partial_conv.py | 102 -- .../models/common/separable_conv_module.py | 97 -- .../mmedit/models/common/sr_backbone_utils.py | 97 -- .../pytorch/mmedit/models/common/upsample.py | 51 - .../mmedit/models/components/__init__.py | 6 - .../models/components/refiners/__init__.py | 5 - .../models/components/refiners/mlp_refiner.py | 59 - .../pytorch/mmedit/models/losses/__init__.py | 5 - .../mmedit/models/losses/pixelwise_loss.py | 221 --- .../pytorch/mmedit/models/losses/utils.py | 118 -- .../liif/pytorch/mmedit/models/registry.py | 8 - .../mmedit/models/restorers/__init__.py | 5 - .../mmedit/models/restorers/basic_restorer.py | 210 --- .../pytorch/mmedit/models/restorers/liif.py | 193 --- .../liif/pytorch/mmedit/utils/__init__.py | 6 - .../liif/pytorch/mmedit/utils/cli.py | 18 - .../liif/pytorch/mmedit/utils/collect_env.py | 18 - .../liif/pytorch/mmedit/utils/logger.py | 27 - .../liif/pytorch/mmedit/utils/setup_env.py | 47 - .../liif/pytorch/mmedit/version.py | 18 - .../liif/pytorch/requirements.txt | 4 - cv/super_resolution/liif/pytorch/test.py | 159 -- cv/super_resolution/liif/pytorch/train.py | 176 --- 237 files changed, 17 insertions(+), 33450 deletions(-) delete mode 100755 cv/super_resolution/liif/pytorch/.gitignore delete mode 100755 cv/super_resolution/liif/pytorch/LICENSE delete mode 100755 cv/super_resolution/liif/pytorch/configs/restorers/liif/README.md delete mode 100755 cv/super_resolution/liif/pytorch/configs/restorers/liif/README_zh-CN.md delete mode 100755 cv/super_resolution/liif/pytorch/configs/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k.py delete mode 100755 cv/super_resolution/liif/pytorch/configs/restorers/liif/liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k.py delete mode 100755 cv/super_resolution/liif/pytorch/configs/restorers/liif/metafile.yml delete mode 100755 cv/super_resolution/liif/pytorch/dist_test.sh delete mode 100755 cv/super_resolution/liif/pytorch/dist_train.sh delete mode 100755 cv/super_resolution/liif/pytorch/docker/Dockerfile delete mode 100755 cv/super_resolution/liif/pytorch/docker/README.md delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/arraymisc/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/arraymisc/quantization.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/alexnet.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/activation.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/context_block.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv_module.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv_ws.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/drop.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/generalized_attention.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/hsigmoid.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/hswish.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/non_local.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/norm.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/padding.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/plugin.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/registry.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/scale.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/swish.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/transformer.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/upsample.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/wrappers.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/builder.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/resnet.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/utils/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/utils/flops_counter.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/utils/fuse_conv_bn.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/utils/sync_bn.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/utils/weight_init.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/cnn/vgg.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/device/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmcv/device/ipu/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmcv/device/ipu/dataloader.py delete mode 100755 cv/super_resolution/liif/pytorch/mmcv/device/ipu/hierarchical_data_manager.py delete mode 100755 cv/super_resolution/liif/pytorch/mmcv/device/ipu/hook_wrapper.py delete mode 100755 cv/super_resolution/liif/pytorch/mmcv/device/ipu/model_wrapper.py delete mode 100755 cv/super_resolution/liif/pytorch/mmcv/device/ipu/runner.py delete mode 100755 cv/super_resolution/liif/pytorch/mmcv/device/ipu/utils.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/device/mlu/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/device/mlu/_functions.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/device/mlu/data_parallel.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/device/mlu/distributed.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/device/mlu/scatter_gather.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/engine/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/engine/test.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/fileio/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/fileio/file_client.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/base.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/json_handler.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/pickle_handler.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/yaml_handler.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/fileio/io.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/fileio/parse.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/image/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/image/colorspace.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/image/geometric.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/image/io.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/image/misc.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/image/photometric.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/parallel/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/parallel/_functions.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/parallel/collate.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/parallel/data_container.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/parallel/data_parallel.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/parallel/distributed.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/parallel/distributed_deprecated.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/parallel/registry.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/parallel/scatter_gather.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/parallel/utils.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/base_module.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/base_runner.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/builder.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/checkpoint.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/default_constructor.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/dist_utils.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/epoch_based_runner.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/fp16_utils.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/checkpoint.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/closure.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/ema.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/evaluation.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/hook.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/iter_timer.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/base.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/clearml.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/dvclive.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/mlflow.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/neptune.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/pavi.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/segmind.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/tensorboard.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/text.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/wandb.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/lr_updater.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/memory.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/momentum_updater.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/optimizer.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/profiler.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/sampler_seed.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/hooks/sync_buffer.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/iter_based_runner.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/log_buffer.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/optimizer/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/optimizer/builder.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/optimizer/default_constructor.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/priority.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/runner/utils.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/config.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/device_type.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/env.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/ext_loader.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/hub.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/logging.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/misc.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/parrots_jit.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/parrots_wrapper.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/path.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/progressbar.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/registry.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/seed.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/testing.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/timer.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/trace.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/utils/version_utils.py delete mode 100644 cv/super_resolution/liif/pytorch/mmcv/version.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/apis/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/apis/generation_inference.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/apis/restoration_inference.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/apis/test.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/apis/train.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/distributed_wrapper.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/evaluation/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/evaluation/eval_hooks.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/evaluation/metric_utils.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/evaluation/metrics.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/evaluation/niqe_pris_params.npz delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/export/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/export/wrappers.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/hooks/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/hooks/ema.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/hooks/visualization.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/mask.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/misc.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/optimizer/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/optimizer/builder.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/scheduler/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/scheduler/lr_updater.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/utils/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/core/utils/dist_utils.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/base_dataset.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/base_sr_dataset.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/builder.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/dataset_wrappers.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/augmentation.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/compose.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/formating.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/generate_assistant.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/loading.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/normalization.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/random_down_sampling.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/utils.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/registry.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/samplers/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/samplers/distributed_sampler.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/sr_folder_dataset.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/datasets/sr_folder_gt_dataset.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/backbones/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/__init__.py delete mode 100644 cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/edsr.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/liif_net.py delete mode 100644 cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/rdn.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/base.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/builder.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/aspp.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/contextual_attention.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/conv.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/downsample.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/ensemble.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/flow_warp.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/gated_conv_module.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/gca_module.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/generation_model_utils.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/img_normalize.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/linear_module.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/mask_conv_module.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/model_utils.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/partial_conv.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/separable_conv_module.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/sr_backbone_utils.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/common/upsample.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/components/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/components/refiners/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/components/refiners/mlp_refiner.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/losses/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/losses/pixelwise_loss.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/losses/utils.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/registry.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/restorers/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/restorers/basic_restorer.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/models/restorers/liif.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/utils/__init__.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/utils/cli.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/utils/collect_env.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/utils/logger.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/utils/setup_env.py delete mode 100755 cv/super_resolution/liif/pytorch/mmedit/version.py delete mode 100755 cv/super_resolution/liif/pytorch/requirements.txt delete mode 100755 cv/super_resolution/liif/pytorch/test.py delete mode 100755 cv/super_resolution/liif/pytorch/train.py diff --git a/cv/super_resolution/liif/pytorch/.gitignore b/cv/super_resolution/liif/pytorch/.gitignore deleted file mode 100755 index b0ac8bd35..000000000 --- a/cv/super_resolution/liif/pytorch/.gitignore +++ /dev/null @@ -1,135 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class -**/*.pyc - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/en/_build/ -docs/en/_tmp/ -docs/zh_cn/_build/ -docs/zh_cn/_tmp/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -# custom -.vscode -.idea -*.pkl -*.pkl.json -*.log.json -work_dirs/ -/data/ -/data -mmedit/.mim - -# Pytorch -*.pth - -# onnx and tensorrt -*.onnx -*.trt - -# local history -.history/** - -# Pytorch Server -*.mar - -# MacOS -.DS_Store diff --git a/cv/super_resolution/liif/pytorch/LICENSE b/cv/super_resolution/liif/pytorch/LICENSE deleted file mode 100755 index 94d620d25..000000000 --- a/cv/super_resolution/liif/pytorch/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Copyright (c) MMEditing Authors. All rights reserved. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020 MMEditing Authors. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cv/super_resolution/liif/pytorch/README.md b/cv/super_resolution/liif/pytorch/README.md index 1e1a5179c..638092db6 100755 --- a/cv/super_resolution/liif/pytorch/README.md +++ b/cv/super_resolution/liif/pytorch/README.md @@ -7,47 +7,47 @@ How to represent an image? While the visual world is presented in a continuous m ## Step 1: Installing packages ```shell -pip3 install -r requirements.txt +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +git clone https://github.com/open-mmlab/mmagic.git -b v1.2.0 --depth=1 +cd mmagic/ +pip3 install -e . -v + +sed -i 's/diffusers.models.unet_2d_condition/diffusers.models.unets.unet_2d_condition/g' mmagic/models/editors/vico/vico_utils.py +pip install albumentations ``` ## Step 2: Preparing datasets ```shell -# Download DIV2K +# Download DIV2K: https://data.vision.ee.ethz.ch/cvl/DIV2K/ or you can follow this tools/dataset_converters/div2k/README.md mkdir -p data/DIV2K -# Home page: https://data.vision.ee.ethz.ch/cvl/DIV2K/ - -# Download validation samples -mkdir -p data/test -# Home page of Set5: http://people.rennes.inria.fr/Aline.Roumy/results/SR_BMVC12.html -# Home page of Set14: https://github.com/jbhuang0604/SelfExSR ``` ## Step 3: Training ### One single GPU ```shell -python3 train.py [training args] # config file can be found in the configs directory +python3 tools/train.py configs/liif/liif-edsr-norm_c64b16_1xb16-1000k_div2k.py ``` ### Mutiple GPUs on one machine ```shell -bash dist_train.sh [training args] # config file can be found in the configs directory +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/liif/liif-edsr-norm_c64b16_1xb16-1000k_div2k.py 8 ``` -### Example -```shell -bash dist_train.sh configs/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k.py 8 -``` ## Results on BI-V100 - | GPUs | FP16 | FPS | PSNR | |------|-------| ---- | ------------ | | 1x8 | False | 684 | 26.87 | ## Reference -https://github.com/open-mmlab/mmediting -https://arxiv.org/abs/2012.09161 +[mmagic](https://github.com/open-mmlab/mmagic) diff --git a/cv/super_resolution/liif/pytorch/configs/restorers/liif/README.md b/cv/super_resolution/liif/pytorch/configs/restorers/liif/README.md deleted file mode 100755 index b6bd7cf42..000000000 --- a/cv/super_resolution/liif/pytorch/configs/restorers/liif/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# LIIF (CVPR'2021) - -> [Learning Continuous Image Representation with Local Implicit Image Function](https://arxiv.org/abs/2012.09161) - - - -## Abstract - - - -How to represent an image? While the visual world is presented in a continuous manner, machines store and see the images in a discrete way with 2D arrays of pixels. In this paper, we seek to learn a continuous representation for images. Inspired by the recent progress in 3D reconstruction with implicit neural representation, we propose Local Implicit Image Function (LIIF), which takes an image coordinate and the 2D deep features around the coordinate as inputs, predicts the RGB value at a given coordinate as an output. Since the coordinates are continuous, LIIF can be presented in arbitrary resolution. To generate the continuous representation for images, we train an encoder with LIIF representation via a self-supervised task with super-resolution. The learned continuous representation can be presented in arbitrary resolution even extrapolate to x30 higher resolution, where the training tasks are not provided. We further show that LIIF representation builds a bridge between discrete and continuous representation in 2D, it naturally supports the learning tasks with size-varied image ground-truths and significantly outperforms the method with resizing the ground-truths. - - - -
- -
- -## Results and models - -| Method | scale | Set5
PSNR / SSIM | Set14
PSNR / SSIM | DIV2K
PSNR / SSIM | Download | -| :--------------------------------------------------------------------------------------------------------: | :---: | :-----------------: | :------------------: | :-------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| [liif_edsr_norm_c64b16_g1_1000k_div2k](/configs/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k.py) | x2 | 35.7131 / 0.9366 | 31.5579 / 0.8889 | 34.6647 / 0.9355 | [model](https://download.openmmlab.com/mmediting/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k_20210715-ab7ce3fc.pth) \| [log](https://download.openmmlab.com/mmediting/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k_20210715-ab7ce3fc.log.json) | -| △ | x3 | 32.3805 / 0.8915 | 28.4605 / 0.8039 | 30.9808 / 0.8724 | △ | -| △ | x4 | 30.2748 / 0.8509 | 26.8415 / 0.7381 | 29.0245 / 0.8187 | △ | -| △ | x6 | 27.1187 / 0.7774 | 24.7461 / 0.6444 | 26.7770 / 0.7425 | △ | -| △ | x18 | 20.8516 / 0.5406 | 20.0096 / 0.4525 | 22.1987 / 0.5955 | △ | -| △ | x30 | 18.8467 / 0.5010 | 18.1321 / 0.3963 | 20.5050 / 0.5577 | △ | -| [liif_rdn_norm_c64b16_g1_1000k_div2k](/configs/restorers/liif/liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k.py) | x2 | 35.7874 / 0.9366 | 31.6866 / 0.8896 | 34.7548 / 0.9356 | [model](https://download.openmmlab.com/mmediting/restorers/liif/liif_rdn_norm_c64b16_g1_1000k_div2k_20210717-22d6fdc8.pth) \| [log](https://download.openmmlab.com/mmediting/restorers/liif/liif_rdn_norm_c64b16_g1_1000k_div2k_20210717-22d6fdc8.log.json) | -| △ | x3 | 32.4992 / 0.8923 | 28.4905 / 0.8037 | 31.0744 / 0.8731 | △ | -| △ | x4 | 30.3835 / 0.8513 | 26.8734 / 0.7373 | 29.1101 / 0.8197 | △ | -| △ | x6 | 27.1914 / 0.7751 | 24.7824 / 0.6434 | 26.8693 / 0.7437 | △ | -| △ | x18 | 20.8913 / 0.5329 | 20.1077 / 0.4537 | 22.2972 / 0.5950 | △ | -| △ | x30 | 18.9354 / 0.4864 | 18.1448 / 0.3942 | 20.5663 / 0.5560 | △ | - -Note: - -- △ refers to ditto. -- Evaluated on RGB channels, `scale` pixels in each border are cropped before evaluation. - -## Citation - -```bibtex -@inproceedings{chen2021learning, - title={Learning continuous image representation with local implicit image function}, - author={Chen, Yinbo and Liu, Sifei and Wang, Xiaolong}, - booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition}, - pages={8628--8638}, - year={2021} -} -``` diff --git a/cv/super_resolution/liif/pytorch/configs/restorers/liif/README_zh-CN.md b/cv/super_resolution/liif/pytorch/configs/restorers/liif/README_zh-CN.md deleted file mode 100755 index 4bbc186d5..000000000 --- a/cv/super_resolution/liif/pytorch/configs/restorers/liif/README_zh-CN.md +++ /dev/null @@ -1,41 +0,0 @@ -# LIIF (CVPR'2021) - - - -
-LIIF (CVPR'2021) - -```bibtex -@inproceedings{chen2021learning, - title={Learning continuous image representation with local implicit image function}, - author={Chen, Yinbo and Liu, Sifei and Wang, Xiaolong}, - booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition}, - pages={8628--8638}, - year={2021} -} -``` - -
- -
- -| 算法 | scale | Set5
PSNR / SSIM | Set14
PSNR / SSIM | DIV2K
PSNR / SSIM | 下载 | -| :---------------------------------------------------------------------------------------------------------------: | :---: | :-----------------: | :------------------: | :-------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| [liif_edsr_norm_x2-4_c64b16_g1_1000k_div2k](/configs/restorers/liif/liif_edsr_norm_x2-4_c64b16_g1_1000k_div2k.py) | x2 | 35.7148 / 0.9367 | 31.5936 / 0.8889 | 34.5896 / 0.9352 | [模型](https://download.openmmlab.com/mmediting/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k_20210319-329ce255.pth) \| [日志](https://download.openmmlab.com/mmediting/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k_20210319-329ce255.log.json) | -| △ | x3 | 32.3596 / 0.8914 | 28.4475 / 0.8040 | 30.9154 / 0.8720 | △ | -| △ | x4 | 30.2583 / 0.8513 | 26.7867 / 0.7377 | 29.0048 / 0.8183 | △ | -| [liif_edsr_norm_c64b16_g1_1000k_div2k](/configs/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k.py) | x2 | 35.7120 / 0.9365 | 31.6106 / 0.8891 | 34.6401 / 0.9353 | [模型](https://download.openmmlab.com/mmediting/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k_20210319-329ce255.pth) \| [日志](https://download.openmmlab.com/mmediting/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k_20210319-329ce255.log.json) | -| △ | x3 | 32.3655 / 0.8913 | 28.4605 / 0.8039 | 30.9597 / 0.8711 | △ | -| △ | x4 | 30.2668 / 0.8511 | 26.8093 / 0.7377 | 29.0059 / 0.8183 | △ | -| △ | x6 | 27.0907 / 0.7775 | 24.7129 / 0.6438 | 26.7694 / 0.7422 | △ | -| △ | x12 | 22.9046 / 0.6255 | 21.5378 / 0.5088 | 23.7269 / 0.6373 | △ | -| △ | x18 | 20.8445 / 0.5390 | 20.0215 / 0.4521 | 22.1920 / 0.5947 | △ | -| △ | x24 | 19.7305 / 0.5033 | 19.0703 / 0.4218 | 21.2025 / 0.5714 | △ | -| △ | x30 | 18.6646 / 0.4818 | 18.0210 / 0.3905 | 20.5022 / 0.5568 | △ | - -注: - -- △ 指同上。 -- 这两个配置仅在 _testing pipeline_ 上有所不同。 所以他们使用相同的检查点。 -- 数据根据 [EDSR](/configs/restorers/edsr) 进行正则化。 -- 在 RGB 通道上进行评估,在评估之前裁剪每个边界中的 `scale` 像素。 diff --git a/cv/super_resolution/liif/pytorch/configs/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k.py b/cv/super_resolution/liif/pytorch/configs/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k.py deleted file mode 100755 index 515c7de05..000000000 --- a/cv/super_resolution/liif/pytorch/configs/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k.py +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - -exp_name = 'liif_edsr_norm_c64b16_g1_1000k_div2k' -scale_min, scale_max = 1, 4 - -# model settings -model = dict( - type='LIIF', - generator=dict( - type='LIIFEDSR', - encoder=dict( - type='EDSR', - in_channels=3, - out_channels=3, - mid_channels=64, - num_blocks=16), - imnet=dict( - type='MLPRefiner', - in_dim=64, - out_dim=3, - hidden_list=[256, 256, 256, 256]), - local_ensemble=True, - feat_unfold=True, - cell_decode=True, - eval_bsize=30000), - rgb_mean=(0.4488, 0.4371, 0.4040), - rgb_std=(1., 1., 1.), - pixel_loss=dict(type='L1Loss', loss_weight=1.0, reduction='mean')) -# model training and testing settings -train_cfg = None -test_cfg = dict(metrics=['PSNR', 'SSIM'], crop_border=scale_max) - -# dataset settings -train_dataset_type = 'SRFolderGTDataset' -val_dataset_type = 'SRFolderGTDataset' -test_dataset_type = 'SRFolderDataset' -train_pipeline = [ - dict( - type='LoadImageFromFile', - io_backend='disk', - key='gt', - flag='color', - channel_order='rgb'), - dict( - type='RandomDownSampling', - scale_min=scale_min, - scale_max=scale_max, - patch_size=48), - dict(type='RescaleToZeroOne', keys=['lq', 'gt']), - dict( - type='Flip', keys=['lq', 'gt'], flip_ratio=0.5, - direction='horizontal'), - dict(type='Flip', keys=['lq', 'gt'], flip_ratio=0.5, direction='vertical'), - dict(type='RandomTransposeHW', keys=['lq', 'gt'], transpose_ratio=0.5), - dict(type='ImageToTensor', keys=['lq', 'gt']), - dict(type='GenerateCoordinateAndCell', sample_quantity=2304), - dict( - type='Collect', - keys=['lq', 'gt', 'coord', 'cell'], - meta_keys=['gt_path']) -] -valid_pipeline = [ - dict( - type='LoadImageFromFile', - io_backend='disk', - key='gt', - flag='color', - channel_order='rgb'), - dict(type='RandomDownSampling', scale_min=scale_max, scale_max=scale_max), - dict(type='RescaleToZeroOne', keys=['lq', 'gt']), - dict(type='ImageToTensor', keys=['lq', 'gt']), - dict(type='GenerateCoordinateAndCell'), - dict( - type='Collect', - keys=['lq', 'gt', 'coord', 'cell'], - meta_keys=['gt_path']) -] -test_pipeline = [ - dict( - type='LoadImageFromFile', - io_backend='disk', - key='gt', - flag='color', - channel_order='rgb'), - dict( - type='LoadImageFromFile', - io_backend='disk', - key='lq', - flag='color', - channel_order='rgb'), - dict(type='RescaleToZeroOne', keys=['lq', 'gt']), - dict(type='ImageToTensor', keys=['lq', 'gt']), - dict(type='GenerateCoordinateAndCell', scale=scale_max), - dict( - type='Collect', - keys=['lq', 'gt', 'coord', 'cell'], - meta_keys=['gt_path']) -] - -data = dict( - workers_per_gpu=8, - train_dataloader=dict(samples_per_gpu=16, drop_last=True), - val_dataloader=dict(samples_per_gpu=1), - test_dataloader=dict(samples_per_gpu=1), - train=dict( - type='RepeatDataset', - times=20, - dataset=dict( - type=train_dataset_type, - gt_folder='data/DIV2K/DIV2K_train_HR', - pipeline=train_pipeline, - scale=scale_max)), - val=dict( - type=val_dataset_type, - gt_folder='data/test/Set14/GTmod12', - pipeline=valid_pipeline, - scale=scale_max), - # test=dict( - # type=test_dataset_type, - # lq_folder=f'data/val_set5/Set5_bicLRx{scale_max:d}', - # gt_folder='data/val_set5/Set5', - # pipeline=test_pipeline, - # scale=scale_max, - # filename_tmpl='{}'), - test=dict( - type=val_dataset_type, - gt_folder='data/test/Set14/GTmod12', - pipeline=valid_pipeline, - scale=scale_max)) - -# optimizer -optimizers = dict(type='Adam', lr=1.e-4) - -# learning policy -iter_per_epoch = 1000 -total_iters = 1000 * iter_per_epoch -lr_config = dict( - policy='Step', - by_epoch=False, - step=[200000, 400000, 600000, 800000], - gamma=0.5) - -checkpoint_config = dict(interval=3000, save_optimizer=True, by_epoch=False) -evaluation = dict(interval=3000, save_image=True, gpu_collect=True) -log_config = dict( - interval=100, - hooks=[ - dict(type='TextLoggerHook', by_epoch=False), - dict(type='TensorboardLoggerHook') - ]) -visual_config = None - -# runtime settings -dist_params = dict(backend='nccl') -log_level = 'INFO' -work_dir = f'./work_dirs/{exp_name}' -load_from = None -resume_from = None -workflow = [('train', 1)] -find_unused_parameters = True -cudnn_benchmark = True diff --git a/cv/super_resolution/liif/pytorch/configs/restorers/liif/liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k.py b/cv/super_resolution/liif/pytorch/configs/restorers/liif/liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k.py deleted file mode 100755 index 2dc933f1c..000000000 --- a/cv/super_resolution/liif/pytorch/configs/restorers/liif/liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k.py +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - -exp_name = 'liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k' -scale_min, scale_max = 1, 4 - -# model settings -model = dict( - type='LIIF', - generator=dict( - type='LIIFRDN', - encoder=dict( - type='RDN', - in_channels=3, - out_channels=3, - mid_channels=64, - num_blocks=16, - upscale_factor=4, - num_layers=8, - channel_growth=64), - imnet=dict( - type='MLPRefiner', - in_dim=64, - out_dim=3, - hidden_list=[256, 256, 256, 256]), - local_ensemble=True, - feat_unfold=True, - cell_decode=True, - eval_bsize=30000), - rgb_mean=(0.5, 0.5, 0.5), - rgb_std=(0.5, 0.5, 0.5), - pixel_loss=dict(type='L1Loss', loss_weight=1.0, reduction='mean')) -# model training and testing settings -train_cfg = None -test_cfg = dict(metrics=['PSNR', 'SSIM'], crop_border=scale_max) - -# dataset settings -scale_min, scale_max = 1, 4 -# dataset settings -train_dataset_type = 'SRFolderGTDataset' -val_dataset_type = 'SRFolderGTDataset' -test_dataset_type = 'SRFolderDataset' -train_pipeline = [ - dict( - type='LoadImageFromFile', - io_backend='disk', - key='gt', - flag='color', - channel_order='rgb'), - dict( - type='RandomDownSampling', - scale_min=scale_min, - scale_max=scale_max, - patch_size=48), - dict(type='RescaleToZeroOne', keys=['lq', 'gt']), - dict( - type='Flip', keys=['lq', 'gt'], flip_ratio=0.5, - direction='horizontal'), - dict(type='Flip', keys=['lq', 'gt'], flip_ratio=0.5, direction='vertical'), - dict(type='RandomTransposeHW', keys=['lq', 'gt'], transpose_ratio=0.5), - dict(type='ImageToTensor', keys=['lq', 'gt']), - dict(type='GenerateCoordinateAndCell', sample_quantity=2304), - dict( - type='Collect', - keys=['lq', 'gt', 'coord', 'cell'], - meta_keys=['gt_path']) -] -valid_pipeline = [ - dict( - type='LoadImageFromFile', - io_backend='disk', - key='gt', - flag='color', - channel_order='rgb'), - dict(type='RandomDownSampling', scale_min=scale_max, scale_max=scale_max), - dict(type='RescaleToZeroOne', keys=['lq', 'gt']), - dict(type='ImageToTensor', keys=['lq', 'gt']), - dict(type='GenerateCoordinateAndCell'), - dict( - type='Collect', - keys=['lq', 'gt', 'coord', 'cell'], - meta_keys=['gt_path']) -] -test_pipeline = [ - dict( - type='LoadImageFromFile', - io_backend='disk', - key='gt', - flag='color', - channel_order='rgb'), - dict( - type='LoadImageFromFile', - io_backend='disk', - key='lq', - flag='color', - channel_order='rgb'), - dict(type='RescaleToZeroOne', keys=['lq', 'gt']), - dict(type='ImageToTensor', keys=['lq', 'gt']), - dict(type='GenerateCoordinateAndCell', scale=scale_max), - dict( - type='Collect', - keys=['lq', 'gt', 'coord', 'cell'], - meta_keys=['gt_path']) -] - -data = dict( - workers_per_gpu=8, - train_dataloader=dict(samples_per_gpu=16, drop_last=True), - val_dataloader=dict(samples_per_gpu=1), - test_dataloader=dict(samples_per_gpu=1), - train=dict( - type='RepeatDataset', - times=20, - dataset=dict( - type=train_dataset_type, - gt_folder='data/DIV2K/DIV2K_train_HR', - pipeline=train_pipeline, - scale=scale_max)), - val=dict( - type=val_dataset_type, - gt_folder='data/val_set5/Set5', - pipeline=valid_pipeline, - scale=scale_max), - test=dict( - type=test_dataset_type, - lq_folder=f'data/val_set5/Set5_bicLRx{scale_max:d}', - gt_folder='data/val_set5/Set5', - pipeline=test_pipeline, - scale=scale_max, - filename_tmpl='{}')) - -# optimizer -optimizers = dict(type='Adam', lr=1.e-4) - -# learning policy -iter_per_epoch = 1000 -total_iters = 1000 * iter_per_epoch -lr_config = dict( - policy='Step', - by_epoch=False, - step=[200000, 400000, 600000, 800000], - gamma=0.5) - -checkpoint_config = dict(interval=3000, save_optimizer=True, by_epoch=False) -evaluation = dict(interval=3000, save_image=True, gpu_collect=True) -log_config = dict( - interval=100, - hooks=[ - dict(type='TextLoggerHook', by_epoch=False), - dict(type='TensorboardLoggerHook') - ]) -visual_config = None - -# runtime settings -dist_params = dict(backend='nccl') -log_level = 'INFO' -work_dir = f'./work_dirs/{exp_name}' -load_from = None -resume_from = None -workflow = [('train', 1)] -find_unused_parameters = True -cudnn_benchmark = True diff --git a/cv/super_resolution/liif/pytorch/configs/restorers/liif/metafile.yml b/cv/super_resolution/liif/pytorch/configs/restorers/liif/metafile.yml deleted file mode 100755 index d9e9a0629..000000000 --- a/cv/super_resolution/liif/pytorch/configs/restorers/liif/metafile.yml +++ /dev/null @@ -1,237 +0,0 @@ -Collections: -- Metadata: - Architecture: - - LIIF - Name: LIIF - Paper: - - https://arxiv.org/abs/2012.09161 - README: configs/restorers/liif/README.md -Models: -- Config: configs/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k.py - In Collection: LIIF - Metadata: - Training Data: DIV2K - Name: liif_edsr_norm_c64b16_g1_1000k_div2k - Results: - - Dataset: DIV2K - Metrics: - DIV2K
PSNR / SSIM: - PSNR: 34.6647 - SSIM: 0.9355 - Set14
PSNR / SSIM: - PSNR: 31.5579 - SSIM: 0.8889 - Set5
PSNR / SSIM: - PSNR: 35.7131 - SSIM: 0.9366 - Task: Restorers - Weights: https://download.openmmlab.com/mmediting/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k_20210715-ab7ce3fc.pth -- Config: configs/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k.py - In Collection: LIIF - Metadata: - Training Data: DIV2K - Name: liif_edsr_norm_c64b16_g1_1000k_div2k - Results: - - Dataset: DIV2K - Metrics: - DIV2K
PSNR / SSIM: - PSNR: 30.9808 - SSIM: 0.8724 - Set14
PSNR / SSIM: - PSNR: 28.4605 - SSIM: 0.8039 - Set5
PSNR / SSIM: - PSNR: 32.3805 - SSIM: 0.8915 - Task: Restorers - Weights: https://download.openmmlab.com/mmediting/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k_20210715-ab7ce3fc.pth -- Config: configs/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k.py - In Collection: LIIF - Metadata: - Training Data: DIV2K - Name: liif_edsr_norm_c64b16_g1_1000k_div2k - Results: - - Dataset: DIV2K - Metrics: - DIV2K
PSNR / SSIM: - PSNR: 29.0245 - SSIM: 0.8187 - Set14
PSNR / SSIM: - PSNR: 26.8415 - SSIM: 0.7381 - Set5
PSNR / SSIM: - PSNR: 30.2748 - SSIM: 0.8509 - Task: Restorers - Weights: https://download.openmmlab.com/mmediting/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k_20210715-ab7ce3fc.pth -- Config: configs/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k.py - In Collection: LIIF - Metadata: - Training Data: DIV2K - Name: liif_edsr_norm_c64b16_g1_1000k_div2k - Results: - - Dataset: DIV2K - Metrics: - DIV2K
PSNR / SSIM: - PSNR: 26.777 - SSIM: 0.7425 - Set14
PSNR / SSIM: - PSNR: 24.7461 - SSIM: 0.6444 - Set5
PSNR / SSIM: - PSNR: 27.1187 - SSIM: 0.7774 - Task: Restorers - Weights: https://download.openmmlab.com/mmediting/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k_20210715-ab7ce3fc.pth -- Config: configs/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k.py - In Collection: LIIF - Metadata: - Training Data: DIV2K - Name: liif_edsr_norm_c64b16_g1_1000k_div2k - Results: - - Dataset: DIV2K - Metrics: - DIV2K
PSNR / SSIM: - PSNR: 22.1987 - SSIM: 0.5955 - Set14
PSNR / SSIM: - PSNR: 20.0096 - SSIM: 0.4525 - Set5
PSNR / SSIM: - PSNR: 20.8516 - SSIM: 0.5406 - Task: Restorers - Weights: https://download.openmmlab.com/mmediting/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k_20210715-ab7ce3fc.pth -- Config: configs/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k.py - In Collection: LIIF - Metadata: - Training Data: DIV2K - Name: liif_edsr_norm_c64b16_g1_1000k_div2k - Results: - - Dataset: DIV2K - Metrics: - DIV2K
PSNR / SSIM: - PSNR: 20.505 - SSIM: 0.5577 - Set14
PSNR / SSIM: - PSNR: 18.1321 - SSIM: 0.3963 - Set5
PSNR / SSIM: - PSNR: 18.8467 - SSIM: 0.501 - Task: Restorers - Weights: https://download.openmmlab.com/mmediting/restorers/liif/liif_edsr_norm_c64b16_g1_1000k_div2k_20210715-ab7ce3fc.pth -- Config: configs/restorers/liif/liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k.py - In Collection: LIIF - Metadata: - Training Data: DIV2K - Name: liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k - Results: - - Dataset: DIV2K - Metrics: - DIV2K
PSNR / SSIM: - PSNR: 34.7548 - SSIM: 0.9356 - Set14
PSNR / SSIM: - PSNR: 31.6866 - SSIM: 0.8896 - Set5
PSNR / SSIM: - PSNR: 35.7874 - SSIM: 0.9366 - Task: Restorers - Weights: https://download.openmmlab.com/mmediting/restorers/liif/liif_rdn_norm_c64b16_g1_1000k_div2k_20210717-22d6fdc8.pth -- Config: configs/restorers/liif/liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k.py - In Collection: LIIF - Metadata: - Training Data: DIV2K - Name: liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k - Results: - - Dataset: DIV2K - Metrics: - DIV2K
PSNR / SSIM: - PSNR: 31.0744 - SSIM: 0.8731 - Set14
PSNR / SSIM: - PSNR: 28.4905 - SSIM: 0.8037 - Set5
PSNR / SSIM: - PSNR: 32.4992 - SSIM: 0.8923 - Task: Restorers - Weights: https://download.openmmlab.com/mmediting/restorers/liif/liif_rdn_norm_c64b16_g1_1000k_div2k_20210717-22d6fdc8.pth -- Config: configs/restorers/liif/liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k.py - In Collection: LIIF - Metadata: - Training Data: DIV2K - Name: liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k - Results: - - Dataset: DIV2K - Metrics: - DIV2K
PSNR / SSIM: - PSNR: 29.1101 - SSIM: 0.8197 - Set14
PSNR / SSIM: - PSNR: 26.8734 - SSIM: 0.7373 - Set5
PSNR / SSIM: - PSNR: 30.3835 - SSIM: 0.8513 - Task: Restorers - Weights: https://download.openmmlab.com/mmediting/restorers/liif/liif_rdn_norm_c64b16_g1_1000k_div2k_20210717-22d6fdc8.pth -- Config: configs/restorers/liif/liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k.py - In Collection: LIIF - Metadata: - Training Data: DIV2K - Name: liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k - Results: - - Dataset: DIV2K - Metrics: - DIV2K
PSNR / SSIM: - PSNR: 26.8693 - SSIM: 0.7437 - Set14
PSNR / SSIM: - PSNR: 24.7824 - SSIM: 0.6434 - Set5
PSNR / SSIM: - PSNR: 27.1914 - SSIM: 0.7751 - Task: Restorers - Weights: https://download.openmmlab.com/mmediting/restorers/liif/liif_rdn_norm_c64b16_g1_1000k_div2k_20210717-22d6fdc8.pth -- Config: configs/restorers/liif/liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k.py - In Collection: LIIF - Metadata: - Training Data: DIV2K - Name: liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k - Results: - - Dataset: DIV2K - Metrics: - DIV2K
PSNR / SSIM: - PSNR: 22.2972 - SSIM: 0.595 - Set14
PSNR / SSIM: - PSNR: 20.1077 - SSIM: 0.4537 - Set5
PSNR / SSIM: - PSNR: 20.8913 - SSIM: 0.5329 - Task: Restorers - Weights: https://download.openmmlab.com/mmediting/restorers/liif/liif_rdn_norm_c64b16_g1_1000k_div2k_20210717-22d6fdc8.pth -- Config: configs/restorers/liif/liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k.py - In Collection: LIIF - Metadata: - Training Data: DIV2K - Name: liif_rdn_norm_x2-4_c64b16_g1_1000k_div2k - Results: - - Dataset: DIV2K - Metrics: - DIV2K
PSNR / SSIM: - PSNR: 20.5663 - SSIM: 0.556 - Set14
PSNR / SSIM: - PSNR: 18.1448 - SSIM: 0.3942 - Set5
PSNR / SSIM: - PSNR: 18.9354 - SSIM: 0.4864 - Task: Restorers - Weights: https://download.openmmlab.com/mmediting/restorers/liif/liif_rdn_norm_c64b16_g1_1000k_div2k_20210717-22d6fdc8.pth diff --git a/cv/super_resolution/liif/pytorch/dist_test.sh b/cv/super_resolution/liif/pytorch/dist_test.sh deleted file mode 100755 index f5490ffba..000000000 --- a/cv/super_resolution/liif/pytorch/dist_test.sh +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -#!/usr/bin/env bash - -CONFIG=$1 -CHECKPOINT=$2 -GPUS=$3 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/test.py \ - $CONFIG \ - $CHECKPOINT \ - --launcher pytorch \ - ${@:4} diff --git a/cv/super_resolution/liif/pytorch/dist_train.sh b/cv/super_resolution/liif/pytorch/dist_train.sh deleted file mode 100755 index 0d970826f..000000000 --- a/cv/super_resolution/liif/pytorch/dist_train.sh +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -#!/usr/bin/env bash - -CONFIG=$1 -GPUS=$2 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/train.py \ - $CONFIG \ - --seed 0 \ - --launcher pytorch ${@:3} diff --git a/cv/super_resolution/liif/pytorch/docker/Dockerfile b/cv/super_resolution/liif/pytorch/docker/Dockerfile deleted file mode 100755 index 651287043..000000000 --- a/cv/super_resolution/liif/pytorch/docker/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -ARG PYTORCH="1.6.0" -ARG CUDA="10.1" -ARG CUDA_ALIAS="101" -ARG CUDNN="7" -ARG MMCV="1.3.1" - -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ENV TORCH_CUDA_ARCH_LIST="6.0 6.1 7.0+PTX" -ENV TORCH_NVCC_FLAGS="-Xfatbin -compress-all" -ENV CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" - -RUN apt-get update && apt-get install -y git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 libgl1-mesa-glx \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Install mmediting -RUN conda clean --all -RUN git clone https://github.com/open-mmlab/mmediting.git /mmediting -WORKDIR /mmediting -ENV FORCE_CUDA="1" -RUN pip install mmcv-full==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA_ALIAS}/torch${PYTORCH}/index.html -RUN pip install -r requirements.txt -RUN pip install --no-cache-dir -e . diff --git a/cv/super_resolution/liif/pytorch/docker/README.md b/cv/super_resolution/liif/pytorch/docker/README.md deleted file mode 100755 index 851c28b0b..000000000 --- a/cv/super_resolution/liif/pytorch/docker/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Docker Image - -We provide a [Dockerfile](Dockerfile) to build an image. - -```shell -# build an image with PyTorch 1.6, CUDA 10.1 -docker build -t mmediting docker/ -``` - -Run it with - -```shell -docker run --gpus all --shm-size=8g -it -v {DATA_DIR}:/mmediting/data mmediting -``` - -**Note**: -Versions defined in this [Dockerfile](Dockerfile) is not up-to-date. -If you use this Dockerfile in your project, you probably want to make some updates. -Feel free to submit an issue or PR for the update. diff --git a/cv/super_resolution/liif/pytorch/mmcv/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/__init__.py deleted file mode 100644 index 04402cacc..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -from .arraymisc import * -from .fileio import * -from .image import * -from .utils import * -from .version import * - -# The following modules are not imported to this level, so mmcv may be used -# without PyTorch. -# - runner -# - parallel -# - op -# - device diff --git a/cv/super_resolution/liif/pytorch/mmcv/arraymisc/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/arraymisc/__init__.py deleted file mode 100644 index 4b4700d61..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/arraymisc/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .quantization import dequantize, quantize - -__all__ = ['quantize', 'dequantize'] diff --git a/cv/super_resolution/liif/pytorch/mmcv/arraymisc/quantization.py b/cv/super_resolution/liif/pytorch/mmcv/arraymisc/quantization.py deleted file mode 100644 index 6182710d5..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/arraymisc/quantization.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from typing import Union - -import numpy as np - - -def quantize(arr: np.ndarray, - min_val: Union[int, float], - max_val: Union[int, float], - levels: int, - dtype=np.int64) -> tuple: - """Quantize an array of (-inf, inf) to [0, levels-1]. - - Args: - arr (ndarray): Input array. - min_val (int or float): Minimum value to be clipped. - max_val (int or float): Maximum value to be clipped. - levels (int): Quantization levels. - dtype (np.type): The type of the quantized array. - - Returns: - tuple: Quantized array. - """ - if not (isinstance(levels, int) and levels > 1): - raise ValueError( - f'levels must be a positive integer, but got {levels}') - if min_val >= max_val: - raise ValueError( - f'min_val ({min_val}) must be smaller than max_val ({max_val})') - - arr = np.clip(arr, min_val, max_val) - min_val - quantized_arr = np.minimum( - np.floor(levels * arr / (max_val - min_val)).astype(dtype), levels - 1) - - return quantized_arr - - -def dequantize(arr: np.ndarray, - min_val: Union[int, float], - max_val: Union[int, float], - levels: int, - dtype=np.float64) -> tuple: - """Dequantize an array. - - Args: - arr (ndarray): Input array. - min_val (int or float): Minimum value to be clipped. - max_val (int or float): Maximum value to be clipped. - levels (int): Quantization levels. - dtype (np.type): The type of the dequantized array. - - Returns: - tuple: Dequantized array. - """ - if not (isinstance(levels, int) and levels > 1): - raise ValueError( - f'levels must be a positive integer, but got {levels}') - if min_val >= max_val: - raise ValueError( - f'min_val ({min_val}) must be smaller than max_val ({max_val})') - - dequantized_arr = (arr + 0.5).astype(dtype) * (max_val - - min_val) / levels + min_val - - return dequantized_arr diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/__init__.py deleted file mode 100644 index 7246c8974..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .alexnet import AlexNet -# yapf: disable -from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, - ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, - ConvTranspose2d, ConvTranspose3d, ConvWS2d, - DepthwiseSeparableConvModule, GeneralizedAttention, - HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, - NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, - build_activation_layer, build_conv_layer, - build_norm_layer, build_padding_layer, build_plugin_layer, - build_upsample_layer, conv_ws_2d, is_norm) -from .builder import MODELS, build_model_from_cfg -# yapf: enable -from .resnet import ResNet, make_res_layer -from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, - NormalInit, PretrainedInit, TruncNormalInit, UniformInit, - XavierInit, bias_init_with_prob, caffe2_xavier_init, - constant_init, fuse_conv_bn, get_model_complexity_info, - initialize, kaiming_init, normal_init, trunc_normal_init, - uniform_init, xavier_init) -from .vgg import VGG, make_vgg_layer - -__all__ = [ - 'AlexNet', 'VGG', 'make_vgg_layer', 'ResNet', 'make_res_layer', - 'constant_init', 'xavier_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'kaiming_init', 'caffe2_xavier_init', - 'bias_init_with_prob', 'ConvModule', 'build_activation_layer', - 'build_conv_layer', 'build_norm_layer', 'build_padding_layer', - 'build_upsample_layer', 'build_plugin_layer', 'is_norm', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'HSigmoid', 'Swish', 'HSwish', - 'GeneralizedAttention', 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', - 'PADDING_LAYERS', 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', - 'get_model_complexity_info', 'conv_ws_2d', 'ConvAWS2d', 'ConvWS2d', - 'fuse_conv_bn', 'DepthwiseSeparableConvModule', 'Linear', 'Conv2d', - 'ConvTranspose2d', 'MaxPool2d', 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', - 'initialize', 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'MODELS', 'build_model_from_cfg' -] diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/alexnet.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/alexnet.py deleted file mode 100644 index 4d45d96d8..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/alexnet.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging -from typing import Optional - -import torch -import torch.nn as nn - - -class AlexNet(nn.Module): - """AlexNet backbone. - - Args: - num_classes (int): number of classes for classification. - """ - - def __init__(self, num_classes: int = -1): - super().__init__() - self.num_classes = num_classes - self.features = nn.Sequential( - nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - nn.Conv2d(64, 192, kernel_size=5, padding=2), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - nn.Conv2d(192, 384, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.Conv2d(384, 256, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.Conv2d(256, 256, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - ) - if self.num_classes > 0: - self.classifier = nn.Sequential( - nn.Dropout(), - nn.Linear(256 * 6 * 6, 4096), - nn.ReLU(inplace=True), - nn.Dropout(), - nn.Linear(4096, 4096), - nn.ReLU(inplace=True), - nn.Linear(4096, num_classes), - ) - - def init_weights(self, pretrained: Optional[str] = None) -> None: - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - # use default initializer - pass - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x: torch.Tensor) -> torch.Tensor: - - x = self.features(x) - if self.num_classes > 0: - x = x.view(x.size(0), 256 * 6 * 6) - x = self.classifier(x) - - return x diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/__init__.py deleted file mode 100644 index 0f33124ed..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .activation import build_activation_layer -from .context_block import ContextBlock -from .conv import build_conv_layer -from .conv2d_adaptive_padding import Conv2dAdaptivePadding -from .conv_module import ConvModule -from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d -from .depthwise_separable_conv_module import DepthwiseSeparableConvModule -from .drop import Dropout, DropPath -from .generalized_attention import GeneralizedAttention -from .hsigmoid import HSigmoid -from .hswish import HSwish -from .non_local import NonLocal1d, NonLocal2d, NonLocal3d -from .norm import build_norm_layer, is_norm -from .padding import build_padding_layer -from .plugin import build_plugin_layer -from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) -from .scale import Scale -from .swish import Swish -from .upsample import build_upsample_layer -from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, - Linear, MaxPool2d, MaxPool3d) - -__all__ = [ - 'ConvModule', 'build_activation_layer', 'build_conv_layer', - 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', - 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', - 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', - 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', - 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', - 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', - 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' -] diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/activation.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/activation.py deleted file mode 100644 index b82374cf5..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/activation.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version -from .registry import ACTIVATION_LAYERS - -for module in [ - nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, - nn.Sigmoid, nn.Tanh -]: - ACTIVATION_LAYERS.register_module(module=module) - - -@ACTIVATION_LAYERS.register_module(name='Clip') -@ACTIVATION_LAYERS.register_module() -class Clamp(nn.Module): - """Clamp activation layer. - - This activation function is to clamp the feature map value within - :math:`[min, max]`. More details can be found in ``torch.clamp()``. - - Args: - min (Number | optional): Lower-bound of the range to be clamped to. - Default to -1. - max (Number | optional): Upper-bound of the range to be clamped to. - Default to 1. - """ - - def __init__(self, min=-1., max=1.): - super().__init__() - self.min = min - self.max = max - - def forward(self, x): - """Forward function. - - Args: - x (torch.Tensor): The input tensor. - - Returns: - torch.Tensor: Clamped tensor. - """ - return torch.clamp(x, min=self.min, max=self.max) - - -class GELU(nn.Module): - r"""Applies the Gaussian Error Linear Units function: - - .. math:: - \text{GELU}(x) = x * \Phi(x) - where :math:`\Phi(x)` is the Cumulative Distribution Function for - Gaussian Distribution. - - Shape: - - Input: :math:`(N, *)` where `*` means, any number of additional - dimensions - - Output: :math:`(N, *)`, same shape as the input - - .. image:: scripts/activation_images/GELU.png - - Examples:: - - >>> m = nn.GELU() - >>> input = torch.randn(2) - >>> output = m(input) - """ - - def forward(self, input): - return F.gelu(input) - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.4')): - ACTIVATION_LAYERS.register_module(module=GELU) -else: - ACTIVATION_LAYERS.register_module(module=nn.GELU) - - -def build_activation_layer(cfg): - """Build activation layer. - - Args: - cfg (dict): The activation layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate an activation layer. - - Returns: - nn.Module: Created activation layer. - """ - return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/context_block.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/context_block.py deleted file mode 100644 index 92e5255b6..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/context_block.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn - -from ..utils import constant_init, kaiming_init -from .registry import PLUGIN_LAYERS - - -def last_zero_init(m): - if isinstance(m, nn.Sequential): - constant_init(m[-1], val=0) - else: - constant_init(m, val=0) - - -@PLUGIN_LAYERS.register_module() -class ContextBlock(nn.Module): - """ContextBlock module in GCNet. - - See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' - (https://arxiv.org/abs/1904.11492) for details. - - Args: - in_channels (int): Channels of the input feature map. - ratio (float): Ratio of channels of transform bottleneck - pooling_type (str): Pooling method for context modeling. - Options are 'att' and 'avg', stand for attention pooling and - average pooling respectively. Default: 'att'. - fusion_types (Sequence[str]): Fusion method for feature fusion, - Options are 'channels_add', 'channel_mul', stand for channelwise - addition and multiplication respectively. Default: ('channel_add',) - """ - - _abbr_ = 'context_block' - - def __init__(self, - in_channels, - ratio, - pooling_type='att', - fusion_types=('channel_add', )): - super().__init__() - assert pooling_type in ['avg', 'att'] - assert isinstance(fusion_types, (list, tuple)) - valid_fusion_types = ['channel_add', 'channel_mul'] - assert all([f in valid_fusion_types for f in fusion_types]) - assert len(fusion_types) > 0, 'at least one fusion should be used' - self.in_channels = in_channels - self.ratio = ratio - self.planes = int(in_channels * ratio) - self.pooling_type = pooling_type - self.fusion_types = fusion_types - if pooling_type == 'att': - self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) - self.softmax = nn.Softmax(dim=2) - else: - self.avg_pool = nn.AdaptiveAvgPool2d(1) - if 'channel_add' in fusion_types: - self.channel_add_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_add_conv = None - if 'channel_mul' in fusion_types: - self.channel_mul_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_mul_conv = None - self.reset_parameters() - - def reset_parameters(self): - if self.pooling_type == 'att': - kaiming_init(self.conv_mask, mode='fan_in') - self.conv_mask.inited = True - - if self.channel_add_conv is not None: - last_zero_init(self.channel_add_conv) - if self.channel_mul_conv is not None: - last_zero_init(self.channel_mul_conv) - - def spatial_pool(self, x): - batch, channel, height, width = x.size() - if self.pooling_type == 'att': - input_x = x - # [N, C, H * W] - input_x = input_x.view(batch, channel, height * width) - # [N, 1, C, H * W] - input_x = input_x.unsqueeze(1) - # [N, 1, H, W] - context_mask = self.conv_mask(x) - # [N, 1, H * W] - context_mask = context_mask.view(batch, 1, height * width) - # [N, 1, H * W] - context_mask = self.softmax(context_mask) - # [N, 1, H * W, 1] - context_mask = context_mask.unsqueeze(-1) - # [N, 1, C, 1] - context = torch.matmul(input_x, context_mask) - # [N, C, 1, 1] - context = context.view(batch, channel, 1, 1) - else: - # [N, C, 1, 1] - context = self.avg_pool(x) - - return context - - def forward(self, x): - # [N, C, 1, 1] - context = self.spatial_pool(x) - - out = x - if self.channel_mul_conv is not None: - # [N, C, 1, 1] - channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) - out = out * channel_mul_term - if self.channel_add_conv is not None: - # [N, C, 1, 1] - channel_add_term = self.channel_add_conv(context) - out = out + channel_add_term - - return out diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv.py deleted file mode 100644 index f6c35fd70..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from .registry import CONV_LAYERS - -CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) -CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) -CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) -CONV_LAYERS.register_module('Conv', module=nn.Conv2d) - - -def build_conv_layer(cfg, *args, **kwargs): - """Build convolution layer. - - Args: - cfg (None or dict): The conv layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an conv layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding conv layer. - - Returns: - nn.Module: Created conv layer. - """ - if cfg is None: - cfg_ = dict(type='Conv2d') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in CONV_LAYERS: - raise KeyError(f'Unrecognized layer type {layer_type}') - else: - conv_layer = CONV_LAYERS.get(layer_type) - - layer = conv_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py deleted file mode 100644 index b45e758ac..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from torch import nn -from torch.nn import functional as F - -from .registry import CONV_LAYERS - - -@CONV_LAYERS.register_module() -class Conv2dAdaptivePadding(nn.Conv2d): - """Implementation of 2D convolution in tensorflow with `padding` as "same", - which applies padding to input (if needed) so that input image gets fully - covered by filter and stride you specified. For stride 1, this will ensure - that output image size is same as input. For stride of 2, output dimensions - will be half, for example. - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the convolving kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If ``True``, adds a learnable bias to the - output. Default: ``True`` - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__(in_channels, out_channels, kernel_size, stride, 0, - dilation, groups, bias) - - def forward(self, x): - img_h, img_w = x.size()[-2:] - kernel_h, kernel_w = self.weight.size()[-2:] - stride_h, stride_w = self.stride - output_h = math.ceil(img_h / stride_h) - output_w = math.ceil(img_w / stride_w) - pad_h = ( - max((output_h - 1) * self.stride[0] + - (kernel_h - 1) * self.dilation[0] + 1 - img_h, 0)) - pad_w = ( - max((output_w - 1) * self.stride[1] + - (kernel_w - 1) * self.dilation[1] + 1 - img_w, 0)) - if pad_h > 0 or pad_w > 0: - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2 - ]) - return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv_module.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv_module.py deleted file mode 100644 index 1d9756859..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv_module.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from mmcv.utils import _BatchNorm, _InstanceNorm -from ..utils import constant_init, kaiming_init -from .activation import build_activation_layer -from .conv import build_conv_layer -from .norm import build_norm_layer -from .padding import build_padding_layer -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class ConvModule(nn.Module): - """A conv block that bundles conv/norm/activation layers. - - This block simplifies the usage of convolution layers, which are commonly - used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). - It is based upon three build methods: `build_conv_layer()`, - `build_norm_layer()` and `build_activation_layer()`. - - Besides, we add some additional features in this module. - 1. Automatically set `bias` of the conv layer. - 2. Spectral norm is supported. - 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only - supports zero and circular padding, and we add "reflect" padding mode. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. - groups (int): Number of blocked connections from input channels to - output channels. Same as that in ``nn._ConvNd``. - bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - inplace (bool): Whether to use inplace mode for activation. - Default: True. - with_spectral_norm (bool): Whether use spectral norm in conv module. - Default: False. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in PyTorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Common examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - Default: ('conv', 'norm', 'act'). - """ - - _abbr_ = 'conv_block' - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias='auto', - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - padding_mode='zeros', - order=('conv', 'norm', 'act')): - super().__init__() - assert conv_cfg is None or isinstance(conv_cfg, dict) - assert norm_cfg is None or isinstance(norm_cfg, dict) - assert act_cfg is None or isinstance(act_cfg, dict) - official_padding_mode = ['zeros', 'circular'] - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.with_explicit_padding = padding_mode not in official_padding_mode - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 3 - assert set(order) == {'conv', 'norm', 'act'} - - self.with_norm = norm_cfg is not None - self.with_activation = act_cfg is not None - # if the conv layer is before a norm layer, bias is unnecessary. - if bias == 'auto': - bias = not self.with_norm - self.with_bias = bias - - if self.with_explicit_padding: - pad_cfg = dict(type=padding_mode) - self.padding_layer = build_padding_layer(pad_cfg, padding) - - # reset padding to 0 for conv module - conv_padding = 0 if self.with_explicit_padding else padding - # build convolution layer - self.conv = build_conv_layer( - conv_cfg, - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=conv_padding, - dilation=dilation, - groups=groups, - bias=bias) - # export the attributes of self.conv to a higher level for convenience - self.in_channels = self.conv.in_channels - self.out_channels = self.conv.out_channels - self.kernel_size = self.conv.kernel_size - self.stride = self.conv.stride - self.padding = padding - self.dilation = self.conv.dilation - self.transposed = self.conv.transposed - self.output_padding = self.conv.output_padding - self.groups = self.conv.groups - - if self.with_spectral_norm: - self.conv = nn.utils.spectral_norm(self.conv) - - # build normalization layers - if self.with_norm: - # norm layer is after conv layer - if order.index('norm') > order.index('conv'): - norm_channels = out_channels - else: - norm_channels = in_channels - self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) - self.add_module(self.norm_name, norm) - if self.with_bias: - if isinstance(norm, (_BatchNorm, _InstanceNorm)): - warnings.warn( - 'Unnecessary conv bias before batch/instance norm') - else: - self.norm_name = None - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - # nn.Tanh has no 'inplace' argument - if act_cfg_['type'] not in [ - 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish', 'GELU' - ]: - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - @property - def norm(self): - if self.norm_name: - return getattr(self, self.norm_name) - else: - return None - - def init_weights(self): - # 1. It is mainly for customized conv layers with their own - # initialization manners by calling their own ``init_weights()``, - # and we do not want ConvModule to override the initialization. - # 2. For customized conv layers without their own initialization - # manners (that is, they don't have their own ``init_weights()``) - # and PyTorch's conv layers, they will be initialized by - # this method with default ``kaiming_init``. - # Note: For PyTorch's conv layers, they will be overwritten by our - # initialization implementation using default ``kaiming_init``. - if not hasattr(self.conv, 'init_weights'): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) - if self.with_norm: - constant_init(self.norm, 1, bias=0) - - def forward(self, x, activate=True, norm=True): - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - x = self.conv(x) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv_ws.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv_ws.py deleted file mode 100644 index fcd0f228a..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/conv_ws.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from .registry import CONV_LAYERS - - -def conv_ws_2d(input, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - eps=1e-5): - c_in = weight.size(0) - weight_flat = weight.view(c_in, -1) - mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1) - std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1) - weight = (weight - mean) / (std + eps) - return F.conv2d(input, weight, bias, stride, padding, dilation, groups) - - -@CONV_LAYERS.register_module('ConvWS') -class ConvWS2d(nn.Conv2d): - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True, - eps=1e-5): - super().__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.eps = eps - - def forward(self, x): - return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups, self.eps) - - -@CONV_LAYERS.register_module(name='ConvAWS') -class ConvAWS2d(nn.Conv2d): - """AWS (Adaptive Weight Standardization) - - This is a variant of Weight Standardization - (https://arxiv.org/pdf/1903.10520.pdf) - It is used in DetectoRS to avoid NaN - (https://arxiv.org/pdf/2006.02334.pdf) - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the conv kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If set True, adds a learnable bias to the - output. Default: True - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.register_buffer('weight_gamma', - torch.ones(self.out_channels, 1, 1, 1)) - self.register_buffer('weight_beta', - torch.zeros(self.out_channels, 1, 1, 1)) - - def _get_weight(self, weight): - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - weight = (weight - mean) / std - weight = self.weight_gamma * weight + self.weight_beta - return weight - - def forward(self, x): - weight = self._get_weight(self.weight) - return F.conv2d(x, weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - """Override default load function. - - AWS overrides the function _load_from_state_dict to recover - weight_gamma and weight_beta if they are missing. If weight_gamma and - weight_beta are found in the checkpoint, this function will return - after super()._load_from_state_dict. Otherwise, it will compute the - mean and std of the pretrained weights and store them in weight_beta - and weight_gamma. - """ - - self.weight_gamma.data.fill_(-1) - local_missing_keys = [] - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, local_missing_keys, - unexpected_keys, error_msgs) - if self.weight_gamma.data.mean() > 0: - for k in local_missing_keys: - missing_keys.append(k) - return - weight = self.weight.data - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - self.weight_beta.data.copy_(mean) - self.weight_gamma.data.copy_(std) - missing_gamma_beta = [ - k for k in local_missing_keys - if k.endswith('weight_gamma') or k.endswith('weight_beta') - ] - for k in missing_gamma_beta: - local_missing_keys.remove(k) - for k in local_missing_keys: - missing_keys.append(k) diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py deleted file mode 100644 index fa6136143..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .conv_module import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if `norm_cfg` and `act_cfg` are specified. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. Default: 1. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. Default: 0. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super().__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/drop.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/drop.py deleted file mode 100644 index 763b43215..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/drop.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from mmcv import build_from_cfg -from .registry import DROPOUT_LAYERS - - -def drop_path(x, drop_prob=0., training=False): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - """ - if drop_prob == 0. or not training: - return x - keep_prob = 1 - drop_prob - # handle tensors with different dimensions, not just 4D tensors. - shape = (x.shape[0], ) + (1, ) * (x.ndim - 1) - random_tensor = keep_prob + torch.rand( - shape, dtype=x.dtype, device=x.device) - output = x.div(keep_prob) * random_tensor.floor() - return output - - -@DROPOUT_LAYERS.register_module() -class DropPath(nn.Module): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - - Args: - drop_prob (float): Probability of the path to be zeroed. Default: 0.1 - """ - - def __init__(self, drop_prob=0.1): - super().__init__() - self.drop_prob = drop_prob - - def forward(self, x): - return drop_path(x, self.drop_prob, self.training) - - -@DROPOUT_LAYERS.register_module() -class Dropout(nn.Dropout): - """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of - ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with - ``DropPath`` - - Args: - drop_prob (float): Probability of the elements to be - zeroed. Default: 0.5. - inplace (bool): Do the operation inplace or not. Default: False. - """ - - def __init__(self, drop_prob=0.5, inplace=False): - super().__init__(p=drop_prob, inplace=inplace) - - -def build_dropout(cfg, default_args=None): - """Builder for drop out layers.""" - return build_from_cfg(cfg, DROPOUT_LAYERS, default_args) diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/generalized_attention.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/generalized_attention.py deleted file mode 100644 index 87c5c3780..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/generalized_attention.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import kaiming_init -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class GeneralizedAttention(nn.Module): - """GeneralizedAttention module. - - See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' - (https://arxiv.org/abs/1711.07971) for details. - - Args: - in_channels (int): Channels of the input feature map. - spatial_range (int): The spatial range. -1 indicates no spatial range - constraint. Default: -1. - num_heads (int): The head number of empirical_attention module. - Default: 9. - position_embedding_dim (int): The position embedding dimension. - Default: -1. - position_magnitude (int): A multiplier acting on coord difference. - Default: 1. - kv_stride (int): The feature stride acting on key/value feature map. - Default: 2. - q_stride (int): The feature stride acting on query feature map. - Default: 1. - attention_type (str): A binary indicator string for indicating which - items in generalized empirical_attention module are used. - Default: '1111'. - - - '1000' indicates 'query and key content' (appr - appr) item, - - '0100' indicates 'query content and relative position' - (appr - position) item, - - '0010' indicates 'key content only' (bias - appr) item, - - '0001' indicates 'relative position only' (bias - position) item. - """ - - _abbr_ = 'gen_attention_block' - - def __init__(self, - in_channels, - spatial_range=-1, - num_heads=9, - position_embedding_dim=-1, - position_magnitude=1, - kv_stride=2, - q_stride=1, - attention_type='1111'): - - super().__init__() - - # hard range means local range for non-local operation - self.position_embedding_dim = ( - position_embedding_dim - if position_embedding_dim > 0 else in_channels) - - self.position_magnitude = position_magnitude - self.num_heads = num_heads - self.in_channels = in_channels - self.spatial_range = spatial_range - self.kv_stride = kv_stride - self.q_stride = q_stride - self.attention_type = [bool(int(_)) for _ in attention_type] - self.qk_embed_dim = in_channels // num_heads - out_c = self.qk_embed_dim * num_heads - - if self.attention_type[0] or self.attention_type[1]: - self.query_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.query_conv.kaiming_init = True - - if self.attention_type[0] or self.attention_type[2]: - self.key_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.key_conv.kaiming_init = True - - self.v_dim = in_channels // num_heads - self.value_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=self.v_dim * num_heads, - kernel_size=1, - bias=False) - self.value_conv.kaiming_init = True - - if self.attention_type[1] or self.attention_type[3]: - self.appr_geom_fc_x = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_x.kaiming_init = True - - self.appr_geom_fc_y = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_y.kaiming_init = True - - if self.attention_type[2]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.appr_bias = nn.Parameter(appr_bias_value) - - if self.attention_type[3]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.geom_bias = nn.Parameter(geom_bias_value) - - self.proj_conv = nn.Conv2d( - in_channels=self.v_dim * num_heads, - out_channels=in_channels, - kernel_size=1, - bias=True) - self.proj_conv.kaiming_init = True - self.gamma = nn.Parameter(torch.zeros(1)) - - if self.spatial_range >= 0: - # only works when non local is after 3*3 conv - if in_channels == 256: - max_len = 84 - elif in_channels == 512: - max_len = 42 - - max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) - local_constraint_map = np.ones( - (max_len, max_len, max_len_kv, max_len_kv), dtype=int) - for iy in range(max_len): - for ix in range(max_len): - local_constraint_map[ - iy, ix, - max((iy - self.spatial_range) // - self.kv_stride, 0):min((iy + self.spatial_range + - 1) // self.kv_stride + - 1, max_len), - max((ix - self.spatial_range) // - self.kv_stride, 0):min((ix + self.spatial_range + - 1) // self.kv_stride + - 1, max_len)] = 0 - - self.local_constraint_map = nn.Parameter( - torch.from_numpy(local_constraint_map).byte(), - requires_grad=False) - - if self.q_stride > 1: - self.q_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.q_stride) - else: - self.q_downsample = None - - if self.kv_stride > 1: - self.kv_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.kv_stride) - else: - self.kv_downsample = None - - self.init_weights() - - def get_position_embedding(self, - h, - w, - h_kv, - w_kv, - q_stride, - kv_stride, - device, - dtype, - feat_dim, - wave_length=1000): - # the default type of Tensor is float32, leading to type mismatch - # in fp16 mode. Cast it to support fp16 mode. - h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) - h_idxs = h_idxs.view((h, 1)) * q_stride - - w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) - w_idxs = w_idxs.view((w, 1)) * q_stride - - h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( - device=device, dtype=dtype) - h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride - - w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( - device=device, dtype=dtype) - w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride - - # (h, h_kv, 1) - h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) - h_diff *= self.position_magnitude - - # (w, w_kv, 1) - w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) - w_diff *= self.position_magnitude - - feat_range = torch.arange(0, feat_dim / 4).to( - device=device, dtype=dtype) - - dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) - dim_mat = dim_mat**((4. / feat_dim) * feat_range) - dim_mat = dim_mat.view((1, 1, -1)) - - embedding_x = torch.cat( - ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) - - embedding_y = torch.cat( - ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) - - return embedding_x, embedding_y - - def forward(self, x_input): - num_heads = self.num_heads - - # use empirical_attention - if self.q_downsample is not None: - x_q = self.q_downsample(x_input) - else: - x_q = x_input - n, _, h, w = x_q.shape - - if self.kv_downsample is not None: - x_kv = self.kv_downsample(x_input) - else: - x_kv = x_input - _, _, h_kv, w_kv = x_kv.shape - - if self.attention_type[0] or self.attention_type[1]: - proj_query = self.query_conv(x_q).view( - (n, num_heads, self.qk_embed_dim, h * w)) - proj_query = proj_query.permute(0, 1, 3, 2) - - if self.attention_type[0] or self.attention_type[2]: - proj_key = self.key_conv(x_kv).view( - (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) - - if self.attention_type[1] or self.attention_type[3]: - position_embed_x, position_embed_y = self.get_position_embedding( - h, w, h_kv, w_kv, self.q_stride, self.kv_stride, - x_input.device, x_input.dtype, self.position_embedding_dim) - # (n, num_heads, w, w_kv, dim) - position_feat_x = self.appr_geom_fc_x(position_embed_x).\ - view(1, w, w_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - # (n, num_heads, h, h_kv, dim) - position_feat_y = self.appr_geom_fc_y(position_embed_y).\ - view(1, h, h_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - position_feat_x /= math.sqrt(2) - position_feat_y /= math.sqrt(2) - - # accelerate for saliency only - if (np.sum(self.attention_type) == 1) and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy = torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, h_kv * w_kv) - - h = 1 - w = 1 - else: - # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for - if not self.attention_type[0]: - energy = torch.zeros( - n, - num_heads, - h, - w, - h_kv, - w_kv, - dtype=x_input.dtype, - device=x_input.device) - - # attention_type[0]: appr - appr - # attention_type[1]: appr - position - # attention_type[2]: bias - appr - # attention_type[3]: bias - position - if self.attention_type[0] or self.attention_type[2]: - if self.attention_type[0] and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - energy = torch.matmul(proj_query + appr_bias, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[0]: - energy = torch.matmul(proj_query, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy += torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, 1, h_kv, w_kv) - - if self.attention_type[1] or self.attention_type[3]: - if self.attention_type[1] and self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - - proj_query_reshape = (proj_query + geom_bias).\ - view(n, num_heads, h, w, self.qk_embed_dim) - - energy_x = torch.matmul( - proj_query_reshape.permute(0, 1, 3, 2, 4), - position_feat_x.permute(0, 1, 2, 4, 3)) - energy_x = energy_x.\ - permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul( - proj_query_reshape, - position_feat_y.permute(0, 1, 2, 4, 3)) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[1]: - proj_query_reshape = proj_query.\ - view(n, num_heads, h, w, self.qk_embed_dim) - proj_query_reshape = proj_query_reshape.\ - permute(0, 1, 3, 2, 4) - position_feat_x_reshape = position_feat_x.\ - permute(0, 1, 2, 4, 3) - position_feat_y_reshape = position_feat_y.\ - permute(0, 1, 2, 4, 3) - - energy_x = torch.matmul(proj_query_reshape, - position_feat_x_reshape) - energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul(proj_query_reshape, - position_feat_y_reshape) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, self.qk_embed_dim, 1).\ - repeat(n, 1, 1, 1) - - position_feat_x_reshape = position_feat_x.\ - view(n, num_heads, w * w_kv, self.qk_embed_dim) - - position_feat_y_reshape = position_feat_y.\ - view(n, num_heads, h * h_kv, self.qk_embed_dim) - - energy_x = torch.matmul(position_feat_x_reshape, geom_bias) - energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) - - energy_y = torch.matmul(position_feat_y_reshape, geom_bias) - energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) - - energy += energy_x + energy_y - - energy = energy.view(n, num_heads, h * w, h_kv * w_kv) - - if self.spatial_range >= 0: - cur_local_constraint_map = \ - self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ - contiguous().\ - view(1, 1, h*w, h_kv*w_kv) - - energy = energy.masked_fill_(cur_local_constraint_map, - float('-inf')) - - attention = F.softmax(energy, 3) - - proj_value = self.value_conv(x_kv) - proj_value_reshape = proj_value.\ - view((n, num_heads, self.v_dim, h_kv * w_kv)).\ - permute(0, 1, 3, 2) - - out = torch.matmul(attention, proj_value_reshape).\ - permute(0, 1, 3, 2).\ - contiguous().\ - view(n, self.v_dim * self.num_heads, h, w) - - out = self.proj_conv(out) - - # output is downsampled, upsample back to input size - if self.q_downsample is not None: - out = F.interpolate( - out, - size=x_input.shape[2:], - mode='bilinear', - align_corners=False) - - out = self.gamma * out + x_input - return out - - def init_weights(self): - for m in self.modules(): - if hasattr(m, 'kaiming_init') and m.kaiming_init: - kaiming_init( - m, - mode='fan_in', - nonlinearity='leaky_relu', - bias=0, - distribution='uniform', - a=1) diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/hsigmoid.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/hsigmoid.py deleted file mode 100644 index 1b538371f..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/hsigmoid.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSigmoid(nn.Module): - """Hard Sigmoid Module. Apply the hard sigmoid function: - Hsigmoid(x) = min(max((x + bias) / divisor, min_value), max_value) - Default: Hsigmoid(x) = min(max((x + 3) / 6, 0), 1) - - Note: - In MMCV v1.4.4, we modified the default value of args to align with - PyTorch official. - - Args: - bias (float): Bias of the input feature map. Default: 3.0. - divisor (float): Divisor of the input feature map. Default: 6.0. - min_value (float): Lower bound value. Default: 0.0. - max_value (float): Upper bound value. Default: 1.0. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, bias=3.0, divisor=6.0, min_value=0.0, max_value=1.0): - super().__init__() - warnings.warn( - 'In MMCV v1.4.4, we modified the default value of args to align ' - 'with PyTorch official. Previous Implementation: ' - 'Hsigmoid(x) = min(max((x + 1) / 2, 0), 1). ' - 'Current Implementation: ' - 'Hsigmoid(x) = min(max((x + 3) / 6, 0), 1).') - self.bias = bias - self.divisor = divisor - assert self.divisor != 0 - self.min_value = min_value - self.max_value = max_value - - def forward(self, x): - x = (x + self.bias) / self.divisor - - return x.clamp_(self.min_value, self.max_value) diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/hswish.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/hswish.py deleted file mode 100644 index 6089d0cc1..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/hswish.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import ACTIVATION_LAYERS - - -class HSwish(nn.Module): - """Hard Swish Module. - - This module applies the hard swish function: - - .. math:: - Hswish(x) = x * ReLU6(x + 3) / 6 - - Args: - inplace (bool): can optionally do the operation in-place. - Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, inplace=False): - super().__init__() - self.act = nn.ReLU6(inplace) - - def forward(self, x): - return x * self.act(x + 3) / 6 - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.7')): - # Hardswish is not supported when PyTorch version < 1.6. - # And Hardswish in PyTorch 1.6 does not support inplace. - ACTIVATION_LAYERS.register_module(module=HSwish) -else: - ACTIVATION_LAYERS.register_module(module=nn.Hardswish, name='HSwish') diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/non_local.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/non_local.py deleted file mode 100644 index f79797995..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/non_local.py +++ /dev/null @@ -1,303 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta - -import torch -import torch.nn as nn - -from ..utils import constant_init, normal_init -from .conv_module import ConvModule -from .registry import PLUGIN_LAYERS - - -class _NonLocalNd(nn.Module, metaclass=ABCMeta): - """Basic Non-local module. - - This module is proposed in - "Non-local Neural Networks" - Paper reference: https://arxiv.org/abs/1711.07971 - Code reference: https://github.com/AlexHex7/Non-local_pytorch - - Args: - in_channels (int): Channels of the input feature map. - reduction (int): Channel reduction ratio. Default: 2. - use_scale (bool): Whether to scale pairwise_weight by - `1/sqrt(inter_channels)` when the mode is `embedded_gaussian`. - Default: True. - conv_cfg (None | dict): The config dict for convolution layers. - If not specified, it will use `nn.Conv2d` for convolution layers. - Default: None. - norm_cfg (None | dict): The config dict for normalization layers. - Default: None. (This parameter is only applicable to conv_out.) - mode (str): Options are `gaussian`, `concatenation`, - `embedded_gaussian` and `dot_product`. Default: embedded_gaussian. - """ - - def __init__(self, - in_channels, - reduction=2, - use_scale=True, - conv_cfg=None, - norm_cfg=None, - mode='embedded_gaussian', - **kwargs): - super().__init__() - self.in_channels = in_channels - self.reduction = reduction - self.use_scale = use_scale - self.inter_channels = max(in_channels // reduction, 1) - self.mode = mode - - if mode not in [ - 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation' - ]: - raise ValueError("Mode should be in 'gaussian', 'concatenation', " - f"'embedded_gaussian' or 'dot_product', but got " - f'{mode} instead.') - - # g, theta, phi are defaulted as `nn.ConvNd`. - # Here we use ConvModule for potential usage. - self.g = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.conv_out = ConvModule( - self.inter_channels, - self.in_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - if self.mode != 'gaussian': - self.theta = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.phi = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - - if self.mode == 'concatenation': - self.concat_project = ConvModule( - self.inter_channels * 2, - 1, - kernel_size=1, - stride=1, - padding=0, - bias=False, - act_cfg=dict(type='ReLU')) - - self.init_weights(**kwargs) - - def init_weights(self, std=0.01, zeros_init=True): - if self.mode != 'gaussian': - for m in [self.g, self.theta, self.phi]: - normal_init(m.conv, std=std) - else: - normal_init(self.g.conv, std=std) - if zeros_init: - if self.conv_out.norm_cfg is None: - constant_init(self.conv_out.conv, 0) - else: - constant_init(self.conv_out.norm, 0) - else: - if self.conv_out.norm_cfg is None: - normal_init(self.conv_out.conv, std=std) - else: - normal_init(self.conv_out.norm, std=std) - - def gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def embedded_gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - if self.use_scale: - # theta_x.shape[-1] is `self.inter_channels` - pairwise_weight /= theta_x.shape[-1]**0.5 - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def dot_product(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight /= pairwise_weight.shape[-1] - return pairwise_weight - - def concatenation(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - h = theta_x.size(2) - w = phi_x.size(3) - theta_x = theta_x.repeat(1, 1, 1, w) - phi_x = phi_x.repeat(1, 1, h, 1) - - concat_feature = torch.cat([theta_x, phi_x], dim=1) - pairwise_weight = self.concat_project(concat_feature) - n, _, h, w = pairwise_weight.size() - pairwise_weight = pairwise_weight.view(n, h, w) - pairwise_weight /= pairwise_weight.shape[-1] - - return pairwise_weight - - def forward(self, x): - # Assume `reduction = 1`, then `inter_channels = C` - # or `inter_channels = C` when `mode="gaussian"` - - # NonLocal1d x: [N, C, H] - # NonLocal2d x: [N, C, H, W] - # NonLocal3d x: [N, C, T, H, W] - n = x.size(0) - - # NonLocal1d g_x: [N, H, C] - # NonLocal2d g_x: [N, HxW, C] - # NonLocal3d g_x: [N, TxHxW, C] - g_x = self.g(x).view(n, self.inter_channels, -1) - g_x = g_x.permute(0, 2, 1) - - # NonLocal1d theta_x: [N, H, C], phi_x: [N, C, H] - # NonLocal2d theta_x: [N, HxW, C], phi_x: [N, C, HxW] - # NonLocal3d theta_x: [N, TxHxW, C], phi_x: [N, C, TxHxW] - if self.mode == 'gaussian': - theta_x = x.view(n, self.in_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - if self.sub_sample: - phi_x = self.phi(x).view(n, self.in_channels, -1) - else: - phi_x = x.view(n, self.in_channels, -1) - elif self.mode == 'concatenation': - theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) - phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) - else: - theta_x = self.theta(x).view(n, self.inter_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - phi_x = self.phi(x).view(n, self.inter_channels, -1) - - pairwise_func = getattr(self, self.mode) - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = pairwise_func(theta_x, phi_x) - - # NonLocal1d y: [N, H, C] - # NonLocal2d y: [N, HxW, C] - # NonLocal3d y: [N, TxHxW, C] - y = torch.matmul(pairwise_weight, g_x) - # NonLocal1d y: [N, C, H] - # NonLocal2d y: [N, C, H, W] - # NonLocal3d y: [N, C, T, H, W] - y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, - *x.size()[2:]) - - output = x + self.conv_out(y) - - return output - - -class NonLocal1d(_NonLocalNd): - """1D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv1d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv1d'), - **kwargs): - super().__init__(in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool1d(kernel_size=2) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -@PLUGIN_LAYERS.register_module() -class NonLocal2d(_NonLocalNd): - """2D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv2d'). - """ - - _abbr_ = 'nonlocal_block' - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv2d'), - **kwargs): - super().__init__(in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -class NonLocal3d(_NonLocalNd): - """3D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv3d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv3d'), - **kwargs): - super().__init__(in_channels, conv_cfg=conv_cfg, **kwargs) - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/norm.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/norm.py deleted file mode 100644 index 51efdc184..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/norm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import torch.nn as nn - -from mmcv.utils import is_tuple_of -from mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm -from .registry import NORM_LAYERS - -NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) -NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) -NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) -NORM_LAYERS.register_module('GN', module=nn.GroupNorm) -NORM_LAYERS.register_module('LN', module=nn.LayerNorm) -NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) -NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - When we build a norm layer with `build_norm_layer()`, we want to preserve - the norm type in variable names, e.g, self.bn1, self.gn. This method will - infer the abbreviation to map class types to abbreviations. - - Rule 1: If the class has the property "_abbr_", return the property. - Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or - InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and - "in" respectively. - Rule 3: If the class name contains "batch", "group", "layer" or "instance", - the abbreviation of this layer will be "bn", "gn", "ln" and "in" - respectively. - Rule 4: Otherwise, the abbreviation falls back to "norm". - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN - return 'in' - elif issubclass(class_type, _BatchNorm): - return 'bn' - elif issubclass(class_type, nn.GroupNorm): - return 'gn' - elif issubclass(class_type, nn.LayerNorm): - return 'ln' - else: - class_name = class_type.__name__.lower() - if 'batch' in class_name: - return 'bn' - elif 'group' in class_name: - return 'gn' - elif 'layer' in class_name: - return 'ln' - elif 'instance' in class_name: - return 'in' - else: - return 'norm_layer' - - -def build_norm_layer(cfg, num_features, postfix=''): - """Build normalization layer. - - Args: - cfg (dict): The norm layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate a norm layer. - - requires_grad (bool, optional): Whether stop gradient updates. - num_features (int): Number of input channels. - postfix (int | str): The postfix to be appended into norm abbreviation - to create named layer. - - Returns: - tuple[str, nn.Module]: The first element is the layer name consisting - of abbreviation and postfix, e.g., bn1, gn. The second element is the - created norm layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in NORM_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - - norm_layer = NORM_LAYERS.get(layer_type) - abbr = infer_abbr(norm_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - requires_grad = cfg_.pop('requires_grad', True) - cfg_.setdefault('eps', 1e-5) - if layer_type != 'GN': - layer = norm_layer(num_features, **cfg_) - if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): - layer._specify_ddp_gpu_num(1) - else: - assert 'num_groups' in cfg_ - layer = norm_layer(num_channels=num_features, **cfg_) - - for param in layer.parameters(): - param.requires_grad = requires_grad - - return name, layer - - -def is_norm(layer, exclude=None): - """Check if a layer is a normalization layer. - - Args: - layer (nn.Module): The layer to be checked. - exclude (type | tuple[type]): Types to be excluded. - - Returns: - bool: Whether the layer is a norm layer. - """ - if exclude is not None: - if not isinstance(exclude, tuple): - exclude = (exclude, ) - if not is_tuple_of(exclude, type): - raise TypeError( - f'"exclude" must be either None or type or a tuple of types, ' - f'but got {type(exclude)}: {exclude}') - - if exclude and isinstance(layer, exclude): - return False - - all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) - return isinstance(layer, all_norm_bases) diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/padding.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/padding.py deleted file mode 100644 index e4ac6b28a..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/padding.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import PADDING_LAYERS - -PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) -PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) -PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) - - -def build_padding_layer(cfg, *args, **kwargs): - """Build padding layer. - - Args: - cfg (None or dict): The padding layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate a padding layer. - - Returns: - nn.Module: Created padding layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - - cfg_ = cfg.copy() - padding_type = cfg_.pop('type') - if padding_type not in PADDING_LAYERS: - raise KeyError(f'Unrecognized padding type {padding_type}.') - else: - padding_layer = PADDING_LAYERS.get(padding_type) - - layer = padding_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/plugin.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/plugin.py deleted file mode 100644 index 6aa13f439..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/plugin.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import platform - -from .registry import PLUGIN_LAYERS - -if platform.system() == 'Windows': - import regex as re # type: ignore -else: - import re # type: ignore - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - This method will infer the abbreviation to map class types to - abbreviations. - - Rule 1: If the class has the property "abbr", return the property. - Rule 2: Otherwise, the abbreviation falls back to snake case of class - name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - - def camel2snack(word): - """Convert camel case word into snack case. - - Modified from `inflection lib - `_. - - Example:: - - >>> camel2snack("FancyBlock") - 'fancy_block' - """ - - word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) - word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) - word = word.replace('-', '_') - return word.lower() - - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - else: - return camel2snack(class_type.__name__) - - -def build_plugin_layer(cfg, postfix='', **kwargs): - """Build plugin layer. - - Args: - cfg (None or dict): cfg should contain: - - - type (str): identify plugin layer type. - - layer args: args needed to instantiate a plugin layer. - postfix (int, str): appended into norm abbreviation to - create named layer. Default: ''. - - Returns: - tuple[str, nn.Module]: The first one is the concatenation of - abbreviation and postfix. The second is the created plugin layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in PLUGIN_LAYERS: - raise KeyError(f'Unrecognized plugin type {layer_type}') - - plugin_layer = PLUGIN_LAYERS.get(layer_type) - abbr = infer_abbr(plugin_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - layer = plugin_layer(**kwargs, **cfg_) - - return name, layer diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/registry.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/registry.py deleted file mode 100644 index c29279776..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/registry.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -CONV_LAYERS = Registry('conv layer') -NORM_LAYERS = Registry('norm layer') -ACTIVATION_LAYERS = Registry('activation layer') -PADDING_LAYERS = Registry('padding layer') -UPSAMPLE_LAYERS = Registry('upsample layer') -PLUGIN_LAYERS = Registry('plugin layer') - -DROPOUT_LAYERS = Registry('drop out layers') -POSITIONAL_ENCODING = Registry('position encoding') -ATTENTION = Registry('attention') -FEEDFORWARD_NETWORK = Registry('feed-forward Network') -TRANSFORMER_LAYER = Registry('transformerLayer') -TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/scale.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/scale.py deleted file mode 100644 index afd5d2d4e..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/scale.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class Scale(nn.Module): - """A learnable scale parameter. - - This layer scales the input by a learnable factor. It multiplies a - learnable scale parameter of shape (1,) with input of any shape. - - Args: - scale (float): Initial value of scale factor. Default: 1.0 - """ - - def __init__(self, scale=1.0): - super().__init__() - self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) - - def forward(self, x): - return x * self.scale diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/swish.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/swish.py deleted file mode 100644 index 7df0fbba6..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/swish.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class Swish(nn.Module): - """Swish Module. - - This module applies the swish function: - - .. math:: - Swish(x) = x * Sigmoid(x) - - Returns: - Tensor: The output tensor. - """ - - def __init__(self): - super().__init__() - - def forward(self, x): - return x * torch.sigmoid(x) diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/transformer.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/transformer.py deleted file mode 100644 index f7ba4d9f8..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/transformer.py +++ /dev/null @@ -1,944 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings -from typing import Sequence - -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.cnn import (Linear, build_activation_layer, build_conv_layer, - build_norm_layer) -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential -from mmcv.utils import (ConfigDict, build_from_cfg, deprecated_api_warning, - to_2tuple) -from .drop import build_dropout -from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, - TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) - -# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file -try: - from mmcv.ops.multi_scale_deform_attn import \ - MultiScaleDeformableAttention # noqa F401 - warnings.warn( - ImportWarning( - '``MultiScaleDeformableAttention`` has been moved to ' - '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 - '``from mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 - 'to ``from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 - )) - -except ImportError: - warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' - '``mmcv.ops.multi_scale_deform_attn``, ' - 'You should install ``mmcv-full`` if you need this module. ') - - -def build_positional_encoding(cfg, default_args=None): - """Builder for Position Encoding.""" - return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) - - -def build_attention(cfg, default_args=None): - """Builder for attention.""" - return build_from_cfg(cfg, ATTENTION, default_args) - - -def build_feedforward_network(cfg, default_args=None): - """Builder for feed-forward network (FFN).""" - return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) - - -def build_transformer_layer(cfg, default_args=None): - """Builder for transformer layer.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) - - -def build_transformer_layer_sequence(cfg, default_args=None): - """Builder for transformer encoder and transformer decoder.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) - - -class AdaptivePadding(nn.Module): - """Applies padding adaptively to the input. - - This module can make input get fully covered by filter - you specified. It support two modes "same" and "corner". The - "same" mode is same with "SAME" padding mode in TensorFlow, pad - zero around input. The "corner" mode would pad zero - to bottom right. - - Args: - kernel_size (int | tuple): Size of the kernel. Default: 1. - stride (int | tuple): Stride of the filter. Default: 1. - dilation (int | tuple): Spacing between kernel elements. - Default: 1. - padding (str): Support "same" and "corner", "corner" mode - would pad zero to bottom right, and "same" mode would - pad zero around input. Default: "corner". - - Example: - >>> kernel_size = 16 - >>> stride = 16 - >>> dilation = 1 - >>> input = torch.rand(1, 1, 15, 17) - >>> adap_pad = AdaptivePadding( - >>> kernel_size=kernel_size, - >>> stride=stride, - >>> dilation=dilation, - >>> padding="corner") - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - >>> input = torch.rand(1, 1, 16, 17) - >>> out = adap_pad(input) - >>> assert (out.shape[2], out.shape[3]) == (16, 32) - """ - - def __init__(self, kernel_size=1, stride=1, dilation=1, padding='corner'): - super().__init__() - assert padding in ('same', 'corner') - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - self.padding = padding - self.kernel_size = kernel_size - self.stride = stride - self.dilation = dilation - - def get_pad_shape(self, input_shape): - """Calculate the padding size of input. - - Args: - input_shape (:obj:`torch.Size`): arrange as (H, W). - - Returns: - Tuple[int]: The padding size along the - original H and W directions - """ - input_h, input_w = input_shape - kernel_h, kernel_w = self.kernel_size - stride_h, stride_w = self.stride - output_h = math.ceil(input_h / stride_h) - output_w = math.ceil(input_w / stride_w) - pad_h = max((output_h - 1) * stride_h + - (kernel_h - 1) * self.dilation[0] + 1 - input_h, 0) - pad_w = max((output_w - 1) * stride_w + - (kernel_w - 1) * self.dilation[1] + 1 - input_w, 0) - return pad_h, pad_w - - def forward(self, x): - """Add padding to `x` - - Args: - x (Tensor): Input tensor has shape (B, C, H, W). - - Returns: - Tensor: The tensor with adaptive padding - """ - pad_h, pad_w = self.get_pad_shape(x.size()[-2:]) - if pad_h > 0 or pad_w > 0: - if self.padding == 'corner': - x = F.pad(x, [0, pad_w, 0, pad_h]) - elif self.padding == 'same': - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, - pad_h - pad_h // 2 - ]) - return x - - -class PatchEmbed(BaseModule): - """Image to Patch Embedding. - - We use a conv layer to implement PatchEmbed. - - Args: - in_channels (int): The num of input channels. Default: 3 - embed_dims (int): The dimensions of embedding. Default: 768 - conv_type (str): The type of convolution - to generate patch embedding. Default: "Conv2d". - kernel_size (int): The kernel_size of embedding conv. Default: 16. - stride (int): The slide stride of embedding conv. - Default: 16. - padding (int | tuple | string): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int): The dilation rate of embedding conv. Default: 1. - bias (bool): Bias of embed conv. Default: True. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: None. - input_size (int | tuple | None): The size of input, which will be - used to calculate the out size. Only works when `dynamic_size` - is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. - """ - - def __init__(self, - in_channels=3, - embed_dims=768, - conv_type='Conv2d', - kernel_size=16, - stride=16, - padding='corner', - dilation=1, - bias=True, - norm_cfg=None, - input_size=None, - init_cfg=None): - super().__init__(init_cfg=init_cfg) - - self.embed_dims = embed_dims - if stride is None: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of conv - padding = 0 - else: - self.adaptive_padding = None - padding = to_2tuple(padding) - - self.projection = build_conv_layer( - dict(type=conv_type), - in_channels=in_channels, - out_channels=embed_dims, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, embed_dims)[1] - else: - self.norm = None - - if input_size: - input_size = to_2tuple(input_size) - # `init_out_size` would be used outside to - # calculate the num_patches - # e.g. when `use_abs_pos_embed` outside - self.init_input_size = input_size - if self.adaptive_padding: - pad_h, pad_w = self.adaptive_padding.get_pad_shape(input_size) - input_h, input_w = input_size - input_h = input_h + pad_h - input_w = input_w + pad_w - input_size = (input_h, input_w) - - # https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html - h_out = (input_size[0] + 2 * padding[0] - dilation[0] * - (kernel_size[0] - 1) - 1) // stride[0] + 1 - w_out = (input_size[1] + 2 * padding[1] - dilation[1] * - (kernel_size[1] - 1) - 1) // stride[1] + 1 - self.init_out_size = (h_out, w_out) - else: - self.init_input_size = None - self.init_out_size = None - - def forward(self, x): - """ - Args: - x (Tensor): Has shape (B, C, H, W). In most case, C is 3. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, out_h * out_w, embed_dims) - - out_size (tuple[int]): Spatial shape of x, arrange as - (out_h, out_w). - """ - - if self.adaptive_padding: - x = self.adaptive_padding(x) - - x = self.projection(x) - out_size = (x.shape[2], x.shape[3]) - x = x.flatten(2).transpose(1, 2) - if self.norm is not None: - x = self.norm(x) - return x, out_size - - -class PatchMerging(BaseModule): - """Merge patch feature map. - - This layer groups feature map by kernel_size, and applies norm and linear - layers to the grouped feature map ((used in Swin Transformer)). - Our implementation uses `nn.Unfold` to - merge patches, which is about 25% faster than the original - implementation. However, we need to modify pretrained - models for compatibility. - - Args: - in_channels (int): The num of input channels. - to gets fully covered by filter and stride you specified. - out_channels (int): The num of output channels. - kernel_size (int | tuple, optional): the kernel size in the unfold - layer. Defaults to 2. - stride (int | tuple, optional): the stride of the sliding blocks in the - unfold layer. Default: None. (Would be set as `kernel_size`) - padding (int | tuple | string ): The padding length of - embedding conv. When it is a string, it means the mode - of adaptive padding, support "same" and "corner" now. - Default: "corner". - dilation (int | tuple, optional): dilation parameter in the unfold - layer. Default: 1. - bias (bool, optional): Whether to add bias in linear layer or not. - Defaults: False. - norm_cfg (dict, optional): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (dict, optional): The extra config for initialization. - Default: None. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=2, - stride=None, - padding='corner', - dilation=1, - bias=False, - norm_cfg=dict(type='LN'), - init_cfg=None): - super().__init__(init_cfg=init_cfg) - self.in_channels = in_channels - self.out_channels = out_channels - if stride: - stride = stride - else: - stride = kernel_size - - kernel_size = to_2tuple(kernel_size) - stride = to_2tuple(stride) - dilation = to_2tuple(dilation) - - if isinstance(padding, str): - self.adaptive_padding = AdaptivePadding( - kernel_size=kernel_size, - stride=stride, - dilation=dilation, - padding=padding) - # disable the padding of unfold - padding = 0 - else: - self.adaptive_padding = None - - padding = to_2tuple(padding) - self.sampler = nn.Unfold( - kernel_size=kernel_size, - dilation=dilation, - padding=padding, - stride=stride) - - sample_dim = kernel_size[0] * kernel_size[1] * in_channels - - if norm_cfg is not None: - self.norm = build_norm_layer(norm_cfg, sample_dim)[1] - else: - self.norm = None - - self.reduction = nn.Linear(sample_dim, out_channels, bias=bias) - - def forward(self, x, input_size): - """ - Args: - x (Tensor): Has shape (B, H*W, C_in). - input_size (tuple[int]): The spatial shape of x, arrange as (H, W). - Default: None. - - Returns: - tuple: Contains merged results and its spatial shape. - - - x (Tensor): Has shape (B, Merged_H * Merged_W, C_out) - - out_size (tuple[int]): Spatial shape of x, arrange as - (Merged_H, Merged_W). - """ - B, L, C = x.shape - assert isinstance(input_size, Sequence), f'Expect ' \ - f'input_size is ' \ - f'`Sequence` ' \ - f'but get {input_size}' - - H, W = input_size - assert L == H * W, 'input feature has wrong size' - - x = x.view(B, H, W, C).permute([0, 3, 1, 2]) # B, C, H, W - - if self.adaptive_padding: - x = self.adaptive_padding(x) - H, W = x.shape[-2:] - - # Use nn.Unfold to merge patch. About 25% faster than original method, - # but need to modify pretrained model for compatibility - # if kernel_size=2 and stride=2, x should has shape (B, 4*C, H/2*W/2) - x = self.sampler(x) - - out_h = (H + 2 * self.sampler.padding[0] - self.sampler.dilation[0] * - (self.sampler.kernel_size[0] - 1) - - 1) // self.sampler.stride[0] + 1 - out_w = (W + 2 * self.sampler.padding[1] - self.sampler.dilation[1] * - (self.sampler.kernel_size[1] - 1) - - 1) // self.sampler.stride[1] + 1 - - output_size = (out_h, out_w) - x = x.transpose(1, 2) # B, H/2*W/2, 4*C - x = self.norm(x) if self.norm else x - x = self.reduction(x) - return x, output_size - - -@ATTENTION.register_module() -class MultiheadAttention(BaseModule): - """A wrapper for ``torch.nn.MultiheadAttention``. - - This module implements MultiheadAttention with identity connection, - and positional encoding is also passed as input. - - Args: - embed_dims (int): The embedding dimension. - num_heads (int): Parallel attention heads. - attn_drop (float): A Dropout layer on attn_output_weights. - Default: 0.0. - proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. - Default: 0.0. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): When it is True, Key, Query and Value are shape of - (batch, n, embed_dim), otherwise (n, batch, embed_dim). - Default to False. - """ - - def __init__(self, - embed_dims, - num_heads, - attn_drop=0., - proj_drop=0., - dropout_layer=dict(type='Dropout', drop_prob=0.), - init_cfg=None, - batch_first=False, - **kwargs): - super().__init__(init_cfg) - if 'dropout' in kwargs: - warnings.warn( - 'The arguments `dropout` in MultiheadAttention ' - 'has been deprecated, now you can separately ' - 'set `attn_drop`(float), proj_drop(float), ' - 'and `dropout_layer`(dict) ', DeprecationWarning) - attn_drop = kwargs['dropout'] - dropout_layer['drop_prob'] = kwargs.pop('dropout') - - self.embed_dims = embed_dims - self.num_heads = num_heads - self.batch_first = batch_first - - self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, - **kwargs) - - self.proj_drop = nn.Dropout(proj_drop) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else nn.Identity() - - @deprecated_api_warning({'residual': 'identity'}, - cls_name='MultiheadAttention') - def forward(self, - query, - key=None, - value=None, - identity=None, - query_pos=None, - key_pos=None, - attn_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `MultiheadAttention`. - - **kwargs allow passing a more general data flow when combining - with other operations in `transformerlayer`. - - Args: - query (Tensor): The input query with shape [num_queries, bs, - embed_dims] if self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - If None, the ``query`` will be used. Defaults to None. - value (Tensor): The value tensor with same shape as `key`. - Same in `nn.MultiheadAttention.forward`. Defaults to None. - If None, the `key` will be used. - identity (Tensor): This tensor, with the same shape as x, - will be used for the identity link. - If None, `x` will be used. Defaults to None. - query_pos (Tensor): The positional encoding for query, with - the same shape as `x`. If not None, it will - be added to `x` before forward function. Defaults to None. - key_pos (Tensor): The positional encoding for `key`, with the - same shape as `key`. Defaults to None. If not None, it will - be added to `key` before forward function. If None, and - `query_pos` has the same shape as `key`, then `query_pos` - will be used for `key_pos`. Defaults to None. - attn_mask (Tensor): ByteTensor mask with shape [num_queries, - num_keys]. Same in `nn.MultiheadAttention.forward`. - Defaults to None. - key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. - Defaults to None. - - Returns: - Tensor: forwarded results with shape - [num_queries, bs, embed_dims] - if self.batch_first is False, else - [bs, num_queries embed_dims]. - """ - - if key is None: - key = query - if value is None: - value = key - if identity is None: - identity = query - if key_pos is None: - if query_pos is not None: - # use query_pos if key_pos is not available - if query_pos.shape == key.shape: - key_pos = query_pos - else: - warnings.warn(f'position encoding of key is' - f'missing in {self.__class__.__name__}.') - if query_pos is not None: - query = query + query_pos - if key_pos is not None: - key = key + key_pos - - # Because the dataflow('key', 'query', 'value') of - # ``torch.nn.MultiheadAttention`` is (num_query, batch, - # embed_dims), We should adjust the shape of dataflow from - # batch_first (batch, num_query, embed_dims) to num_query_first - # (num_query ,batch, embed_dims), and recover ``attn_output`` - # from num_query_first to batch_first. - if self.batch_first: - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - out = self.attn( - query=query, - key=key, - value=value, - attn_mask=attn_mask, - key_padding_mask=key_padding_mask)[0] - - if self.batch_first: - out = out.transpose(0, 1) - - return identity + self.dropout_layer(self.proj_drop(out)) - - -@FEEDFORWARD_NETWORK.register_module() -class FFN(BaseModule): - """Implements feed-forward networks (FFNs) with identity connection. - - Args: - embed_dims (int): The feature dimension. Same as - `MultiheadAttention`. Defaults: 256. - feedforward_channels (int): The hidden dimension of FFNs. - Defaults: 1024. - num_fcs (int, optional): The number of fully-connected layers in - FFNs. Default: 2. - act_cfg (dict, optional): The activation config for FFNs. - Default: dict(type='ReLU') - ffn_drop (float, optional): Probability of an element to be - zeroed in FFN. Default 0.0. - add_identity (bool, optional): Whether to add the - identity connection. Default: `True`. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - @deprecated_api_warning( - { - 'dropout': 'ffn_drop', - 'add_residual': 'add_identity' - }, - cls_name='FFN') - def __init__(self, - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - act_cfg=dict(type='ReLU', inplace=True), - ffn_drop=0., - dropout_layer=None, - add_identity=True, - init_cfg=None, - **kwargs): - super().__init__(init_cfg) - assert num_fcs >= 2, 'num_fcs should be no less ' \ - f'than 2. got {num_fcs}.' - self.embed_dims = embed_dims - self.feedforward_channels = feedforward_channels - self.num_fcs = num_fcs - self.act_cfg = act_cfg - self.activate = build_activation_layer(act_cfg) - - layers = [] - in_channels = embed_dims - for _ in range(num_fcs - 1): - layers.append( - Sequential( - Linear(in_channels, feedforward_channels), self.activate, - nn.Dropout(ffn_drop))) - in_channels = feedforward_channels - layers.append(Linear(feedforward_channels, embed_dims)) - layers.append(nn.Dropout(ffn_drop)) - self.layers = Sequential(*layers) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else torch.nn.Identity() - self.add_identity = add_identity - - @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') - def forward(self, x, identity=None): - """Forward function for `FFN`. - - The function would add x to the output tensor if residue is None. - """ - out = self.layers(x) - if not self.add_identity: - return self.dropout_layer(out) - if identity is None: - identity = x - return identity + self.dropout_layer(out) - - -@TRANSFORMER_LAYER.register_module() -class BaseTransformerLayer(BaseModule): - """Base `TransformerLayer` for vision transformer. - - It can be built from `mmcv.ConfigDict` and support more flexible - customization, for example, using any number of `FFN or LN ` and - use different kinds of `attention` by specifying a list of `ConfigDict` - named `attn_cfgs`. It is worth mentioning that it supports `prenorm` - when you specifying `norm` as the first element of `operation_order`. - More details about the `prenorm`: `On Layer Normalization in the - Transformer Architecture `_ . - - Args: - attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for `self_attention` or `cross_attention` modules, - The order of the configs in the list should be consistent with - corresponding attentions in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. Default: None. - ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for FFN, The order of the configs in the list should be - consistent with corresponding ffn in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. - operation_order (tuple[str]): The execution order of operation - in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). - Support `prenorm` when you specifying first element as `norm`. - Default:None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): Key, Query and Value are shape - of (batch, n, embed_dim) - or (n, batch, embed_dim). Default to False. - """ - - def __init__(self, - attn_cfgs=None, - ffn_cfgs=dict( - type='FFN', - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - ffn_drop=0., - act_cfg=dict(type='ReLU', inplace=True), - ), - operation_order=None, - norm_cfg=dict(type='LN'), - init_cfg=None, - batch_first=False, - **kwargs): - - deprecated_args = dict( - feedforward_channels='feedforward_channels', - ffn_dropout='ffn_drop', - ffn_num_fcs='num_fcs') - for ori_name, new_name in deprecated_args.items(): - if ori_name in kwargs: - warnings.warn( - f'The arguments `{ori_name}` in BaseTransformerLayer ' - f'has been deprecated, now you should set `{new_name}` ' - f'and other FFN related arguments ' - f'to a dict named `ffn_cfgs`. ', DeprecationWarning) - ffn_cfgs[new_name] = kwargs[ori_name] - - super().__init__(init_cfg) - - self.batch_first = batch_first - - assert set(operation_order) & { - 'self_attn', 'norm', 'ffn', 'cross_attn'} == \ - set(operation_order), f'The operation_order of' \ - f' {self.__class__.__name__} should ' \ - f'contains all four operation type ' \ - f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" - - num_attn = operation_order.count('self_attn') + operation_order.count( - 'cross_attn') - if isinstance(attn_cfgs, dict): - attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] - else: - assert num_attn == len(attn_cfgs), f'The length ' \ - f'of attn_cfg {num_attn} is ' \ - f'not consistent with the number of attention' \ - f'in operation_order {operation_order}.' - - self.num_attn = num_attn - self.operation_order = operation_order - self.norm_cfg = norm_cfg - self.pre_norm = operation_order[0] == 'norm' - self.attentions = ModuleList() - - index = 0 - for operation_name in operation_order: - if operation_name in ['self_attn', 'cross_attn']: - if 'batch_first' in attn_cfgs[index]: - assert self.batch_first == attn_cfgs[index]['batch_first'] - else: - attn_cfgs[index]['batch_first'] = self.batch_first - attention = build_attention(attn_cfgs[index]) - # Some custom attentions used as `self_attn` - # or `cross_attn` can have different behavior. - attention.operation_name = operation_name - self.attentions.append(attention) - index += 1 - - self.embed_dims = self.attentions[0].embed_dims - - self.ffns = ModuleList() - num_ffns = operation_order.count('ffn') - if isinstance(ffn_cfgs, dict): - ffn_cfgs = ConfigDict(ffn_cfgs) - if isinstance(ffn_cfgs, dict): - ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] - assert len(ffn_cfgs) == num_ffns - for ffn_index in range(num_ffns): - if 'embed_dims' not in ffn_cfgs[ffn_index]: - ffn_cfgs[ffn_index]['embed_dims'] = self.embed_dims - else: - assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims - self.ffns.append( - build_feedforward_network(ffn_cfgs[ffn_index], - dict(type='FFN'))) - - self.norms = ModuleList() - num_norms = operation_order.count('norm') - for _ in range(num_norms): - self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) - - def forward(self, - query, - key=None, - value=None, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerDecoderLayer`. - - **kwargs contains some specific arguments of attentions. - - Args: - query (Tensor): The input query with shape - [num_queries, bs, embed_dims] if - self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - value (Tensor): The value tensor with same shape as `key`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor] | None): 2D Tensor used in - calculation of corresponding attention. The length of - it should equal to the number of `attention` in - `operation_order`. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in `self_attn` layer. - Defaults to None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: forwarded results with shape [num_queries, bs, embed_dims]. - """ - - norm_index = 0 - attn_index = 0 - ffn_index = 0 - identity = query - if attn_masks is None: - attn_masks = [None for _ in range(self.num_attn)] - elif isinstance(attn_masks, torch.Tensor): - attn_masks = [ - copy.deepcopy(attn_masks) for _ in range(self.num_attn) - ] - warnings.warn(f'Use same attn_mask in all attentions in ' - f'{self.__class__.__name__} ') - else: - assert len(attn_masks) == self.num_attn, f'The length of ' \ - f'attn_masks {len(attn_masks)} must be equal ' \ - f'to the number of attention in ' \ - f'operation_order {self.num_attn}' - - for layer in self.operation_order: - if layer == 'self_attn': - temp_key = temp_value = query - query = self.attentions[attn_index]( - query, - temp_key, - temp_value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=query_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=query_key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'norm': - query = self.norms[norm_index](query) - norm_index += 1 - - elif layer == 'cross_attn': - query = self.attentions[attn_index]( - query, - key, - value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=key_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'ffn': - query = self.ffns[ffn_index]( - query, identity if self.pre_norm else None) - ffn_index += 1 - - return query - - -@TRANSFORMER_LAYER_SEQUENCE.register_module() -class TransformerLayerSequence(BaseModule): - """Base class for TransformerEncoder and TransformerDecoder in vision - transformer. - - As base-class of Encoder and Decoder in vision transformer. - Support customization such as specifying different kind - of `transformer_layer` in `transformer_coder`. - - Args: - transformerlayer (list[obj:`mmcv.ConfigDict`] | - obj:`mmcv.ConfigDict`): Config of transformerlayer - in TransformerCoder. If it is obj:`mmcv.ConfigDict`, - it would be repeated `num_layer` times to a - list[`mmcv.ConfigDict`]. Default: None. - num_layers (int): The number of `TransformerLayer`. Default: None. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): - super().__init__(init_cfg) - if isinstance(transformerlayers, dict): - transformerlayers = [ - copy.deepcopy(transformerlayers) for _ in range(num_layers) - ] - else: - assert isinstance(transformerlayers, list) and \ - len(transformerlayers) == num_layers - self.num_layers = num_layers - self.layers = ModuleList() - for i in range(num_layers): - self.layers.append(build_transformer_layer(transformerlayers[i])) - self.embed_dims = self.layers[0].embed_dims - self.pre_norm = self.layers[0].pre_norm - - def forward(self, - query, - key, - value, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerCoder`. - - Args: - query (Tensor): Input query with shape - `(num_queries, bs, embed_dims)`. - key (Tensor): The key tensor with shape - `(num_keys, bs, embed_dims)`. - value (Tensor): The value tensor with shape - `(num_keys, bs, embed_dims)`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor], optional): Each element is 2D Tensor - which is used in calculation of corresponding attention in - operation_order. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in self-attention - Default: None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: results with shape [num_queries, bs, embed_dims]. - """ - for layer in self.layers: - query = layer( - query, - key, - value, - query_pos=query_pos, - key_pos=key_pos, - attn_masks=attn_masks, - query_key_padding_mask=query_key_padding_mask, - key_padding_mask=key_padding_mask, - **kwargs) - return query diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/upsample.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/upsample.py deleted file mode 100644 index 15e4febde..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/upsample.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import xavier_init -from .registry import UPSAMPLE_LAYERS - -UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) -UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) - - -@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') -class PixelShufflePack(nn.Module): - """Pixel Shuffle upsample layer. - - This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to - achieve a simple upsampling with pixel shuffle. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of the conv layer to expand the - channels. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super().__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - xavier_init(self.upsample_conv, distribution='uniform') - - def forward(self, x): - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x - - -def build_upsample_layer(cfg, *args, **kwargs): - """Build upsample layer. - - Args: - cfg (dict): The upsample layer config, which should contain: - - - type (str): Layer type. - - scale_factor (int): Upsample ratio, which is not applicable to - deconv. - - layer args: Args needed to instantiate a upsample layer. - args (argument list): Arguments passed to the ``__init__`` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the - ``__init__`` method of the corresponding conv layer. - - Returns: - nn.Module: Created upsample layer. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - raise KeyError( - f'the cfg dict must contain the key "type", but got {cfg}') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in UPSAMPLE_LAYERS: - raise KeyError(f'Unrecognized upsample type {layer_type}') - else: - upsample = UPSAMPLE_LAYERS.get(layer_type) - - if upsample is nn.Upsample: - cfg_['mode'] = layer_type - layer = upsample(*args, **kwargs, **cfg_) - return layer diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/wrappers.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/wrappers.py deleted file mode 100644 index 8aebf67bf..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/bricks/wrappers.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 - -Wrap some nn modules to support empty tensor input. Currently, these wrappers -are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask -heads are trained on only positive RoIs. -""" -import math - -import torch -import torch.nn as nn -from torch.nn.modules.utils import _pair, _triple - -from .registry import CONV_LAYERS, UPSAMPLE_LAYERS - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def obsolete_torch_version(torch_version, version_threshold): - return torch_version == 'parrots' or torch_version <= version_threshold - - -class NewEmptyTensorOp(torch.autograd.Function): - - @staticmethod - def forward(ctx, x, new_shape): - ctx.shape = x.shape - return x.new_empty(new_shape) - - @staticmethod - def backward(ctx, grad): - shape = ctx.shape - return NewEmptyTensorOp.apply(grad, shape), None - - -@CONV_LAYERS.register_module('Conv', force=True) -class Conv2d(nn.Conv2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module('Conv3d', force=True) -class Conv3d(nn.Conv3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv') -@UPSAMPLE_LAYERS.register_module('deconv', force=True) -class ConvTranspose2d(nn.ConvTranspose2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv3d') -@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) -class ConvTranspose3d(nn.ConvTranspose3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -class MaxPool2d(nn.MaxPool2d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), - _pair(self.padding), _pair(self.stride), - _pair(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class MaxPool3d(nn.MaxPool3d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), - _triple(self.padding), - _triple(self.stride), - _triple(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class Linear(torch.nn.Linear): - - def forward(self, x): - # empty tensor forward of Linear layer is supported in Pytorch 1.6 - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): - out_shape = [x.shape[0], self.out_features] - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/builder.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/builder.py deleted file mode 100644 index 7567316c5..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/builder.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..runner import Sequential -from ..utils import Registry, build_from_cfg - - -def build_model_from_cfg(cfg, registry, default_args=None): - """Build a PyTorch model from config dict(s). Different from - ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. - - Args: - cfg (dict, list[dict]): The config of modules, is is either a config - dict or a list of config dicts. If cfg is a list, a - the built modules will be wrapped with ``nn.Sequential``. - registry (:obj:`Registry`): A registry the module belongs to. - default_args (dict, optional): Default arguments to build the module. - Defaults to None. - - Returns: - nn.Module: A built nn module. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return Sequential(*modules) - else: - return build_from_cfg(cfg, registry, default_args) - - -MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/resnet.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/resnet.py deleted file mode 100644 index fb29e6256..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/resnet.py +++ /dev/null @@ -1,322 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging -from typing import Optional, Sequence, Tuple, Union - -import torch.nn as nn -import torch.utils.checkpoint as cp -from torch import Tensor - -from .utils import constant_init, kaiming_init - - -def conv3x3(in_planes: int, - out_planes: int, - stride: int = 1, - dilation: int = 1): - """3x3 convolution with padding.""" - return nn.Conv2d( - in_planes, - out_planes, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, - inplanes: int, - planes: int, - stride: int = 1, - dilation: int = 1, - downsample: Optional[nn.Module] = None, - style: str = 'pytorch', - with_cp: bool = False): - super().__init__() - assert style in ['pytorch', 'caffe'] - self.conv1 = conv3x3(inplanes, planes, stride, dilation) - self.bn1 = nn.BatchNorm2d(planes) - self.relu = nn.ReLU(inplace=True) - self.conv2 = conv3x3(planes, planes) - self.bn2 = nn.BatchNorm2d(planes) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - assert not with_cp - - def forward(self, x: Tensor) -> Tensor: - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Bottleneck(nn.Module): - expansion = 4 - - def __init__(self, - inplanes: int, - planes: int, - stride: int = 1, - dilation: int = 1, - downsample: Optional[nn.Module] = None, - style: str = 'pytorch', - with_cp: bool = False): - """Bottleneck block. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if - it is "caffe", the stride-two layer is the first 1x1 conv layer. - """ - super().__init__() - assert style in ['pytorch', 'caffe'] - if style == 'pytorch': - conv1_stride = 1 - conv2_stride = stride - else: - conv1_stride = stride - conv2_stride = 1 - self.conv1 = nn.Conv2d( - inplanes, planes, kernel_size=1, stride=conv1_stride, bias=False) - self.conv2 = nn.Conv2d( - planes, - planes, - kernel_size=3, - stride=conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.bn1 = nn.BatchNorm2d(planes) - self.bn2 = nn.BatchNorm2d(planes) - self.conv3 = nn.Conv2d( - planes, planes * self.expansion, kernel_size=1, bias=False) - self.bn3 = nn.BatchNorm2d(planes * self.expansion) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - def forward(self, x: Tensor) -> Tensor: - - def _inner_forward(x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -def make_res_layer(block: nn.Module, - inplanes: int, - planes: int, - blocks: int, - stride: int = 1, - dilation: int = 1, - style: str = 'pytorch', - with_cp: bool = False) -> nn.Module: - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = nn.Sequential( - nn.Conv2d( - inplanes, - planes * block.expansion, - kernel_size=1, - stride=stride, - bias=False), - nn.BatchNorm2d(planes * block.expansion), - ) - - layers = [] - layers.append( - block( - inplanes, - planes, - stride, - dilation, - downsample, - style=style, - with_cp=with_cp)) - inplanes = planes * block.expansion - for _ in range(1, blocks): - layers.append( - block(inplanes, planes, 1, dilation, style=style, with_cp=with_cp)) - - return nn.Sequential(*layers) - - -class ResNet(nn.Module): - """ResNet backbone. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - num_stages (int): Resnet stages, normally 4. - strides (Sequence[int]): Strides of the first block of each stage. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze - running stats (mean and var). - bn_frozen (bool): Whether to freeze weight and bias of BN layers. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth: int, - num_stages: int = 4, - strides: Sequence[int] = (1, 2, 2, 2), - dilations: Sequence[int] = (1, 1, 1, 1), - out_indices: Sequence[int] = (0, 1, 2, 3), - style: str = 'pytorch', - frozen_stages: int = -1, - bn_eval: bool = True, - bn_frozen: bool = False, - with_cp: bool = False): - super().__init__() - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - assert num_stages >= 1 and num_stages <= 4 - block, stage_blocks = self.arch_settings[depth] - stage_blocks = stage_blocks[:num_stages] # type: ignore - assert len(strides) == len(dilations) == num_stages - assert max(out_indices) < num_stages - - self.out_indices = out_indices - self.style = style - self.frozen_stages = frozen_stages - self.bn_eval = bn_eval - self.bn_frozen = bn_frozen - self.with_cp = with_cp - - self.inplanes: int = 64 - self.conv1 = nn.Conv2d( - 3, 64, kernel_size=7, stride=2, padding=3, bias=False) - self.bn1 = nn.BatchNorm2d(64) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - self.res_layers = [] - for i, num_blocks in enumerate(stage_blocks): - stride = strides[i] - dilation = dilations[i] - planes = 64 * 2**i - res_layer = make_res_layer( - block, - self.inplanes, - planes, - num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - with_cp=with_cp) - self.inplanes = planes * block.expansion # type: ignore - layer_name = f'layer{i + 1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self.feat_dim = block.expansion * 64 * 2**( # type: ignore - len(stage_blocks) - 1) - - def init_weights(self, pretrained: Optional[str] = None) -> None: - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m) - elif isinstance(m, nn.BatchNorm2d): - constant_init(m, 1) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x: Tensor) -> Union[Tensor, Tuple[Tensor]]: - x = self.conv1(x) - x = self.bn1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - if len(outs) == 1: - return outs[0] - else: - return tuple(outs) - - def train(self, mode: bool = True) -> None: - super().train(mode) - if self.bn_eval: - for m in self.modules(): - if isinstance(m, nn.BatchNorm2d): - m.eval() - if self.bn_frozen: - for params in m.parameters(): - params.requires_grad = False - if mode and self.frozen_stages >= 0: - for param in self.conv1.parameters(): - param.requires_grad = False - for param in self.bn1.parameters(): - param.requires_grad = False - self.bn1.eval() - self.bn1.weight.requires_grad = False - self.bn1.bias.requires_grad = False - for i in range(1, self.frozen_stages + 1): - mod = getattr(self, f'layer{i}') - mod.eval() - for param in mod.parameters(): - param.requires_grad = False diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/__init__.py deleted file mode 100644 index a263e31c1..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .flops_counter import get_model_complexity_info -from .fuse_conv_bn import fuse_conv_bn -from .sync_bn import revert_sync_batchnorm -from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, - KaimingInit, NormalInit, PretrainedInit, - TruncNormalInit, UniformInit, XavierInit, - bias_init_with_prob, caffe2_xavier_init, - constant_init, initialize, kaiming_init, normal_init, - trunc_normal_init, uniform_init, xavier_init) - -__all__ = [ - 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', - 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', - 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'revert_sync_batchnorm' -] diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/flops_counter.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/flops_counter.py deleted file mode 100644 index 150a55992..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/flops_counter.py +++ /dev/null @@ -1,603 +0,0 @@ -# Modified from flops-counter.pytorch by Vladislav Sovrasov -# original repo: https://github.com/sovrasov/flops-counter.pytorch - -# MIT License - -# Copyright (c) 2018 Vladislav Sovrasov - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -import warnings -from functools import partial -from typing import Any, Callable, Dict, Optional, TextIO, Tuple - -import numpy as np -import torch -import torch.nn as nn - -import mmcv - - -def get_model_complexity_info(model: nn.Module, - input_shape: tuple, - print_per_layer_stat: bool = True, - as_strings: bool = True, - input_constructor: Optional[Callable] = None, - flush: bool = False, - ost: TextIO = sys.stdout) -> tuple: - """Get complexity information of a model. - - This method can calculate FLOPs and parameter counts of a model with - corresponding input shape. It can also print complexity information for - each layer in a model. - - Supported layers are listed as below: - - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. - - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, - ``nn.LeakyReLU``, ``nn.ReLU6``. - - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, - ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, - ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, - ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, - ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. - - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, - ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, - ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. - - Linear: ``nn.Linear``. - - Deconvolution: ``nn.ConvTranspose2d``. - - Upsample: ``nn.Upsample``. - - Args: - model (nn.Module): The model for complexity calculation. - input_shape (tuple): Input shape used for calculation. - print_per_layer_stat (bool): Whether to print complexity information - for each layer in a model. Default: True. - as_strings (bool): Output FLOPs and params counts in a string form. - Default: True. - input_constructor (None | callable): If specified, it takes a callable - method that generates input. otherwise, it will generate a random - tensor with input shape to calculate FLOPs. Default: None. - flush (bool): same as that in :func:`print`. Default: False. - ost (stream): same as ``file`` param in :func:`print`. - Default: sys.stdout. - - Returns: - tuple[float | str]: If ``as_strings`` is set to True, it will return - FLOPs and parameter counts in a string format. otherwise, it will - return those in a float number format. - """ - assert type(input_shape) is tuple - assert len(input_shape) >= 1 - assert isinstance(model, nn.Module) - flops_model = add_flops_counting_methods(model) - flops_model.eval() - flops_model.start_flops_count() - if input_constructor: - input = input_constructor(input_shape) - _ = flops_model(**input) - else: - try: - batch = torch.ones(()).new_empty( - (1, *input_shape), - dtype=next(flops_model.parameters()).dtype, - device=next(flops_model.parameters()).device) - except StopIteration: - # Avoid StopIteration for models which have no parameters, - # like `nn.Relu()`, `nn.AvgPool2d`, etc. - batch = torch.ones(()).new_empty((1, *input_shape)) - - _ = flops_model(batch) - - flops_count, params_count = flops_model.compute_average_flops_cost() - if print_per_layer_stat: - print_model_with_flops( - flops_model, flops_count, params_count, ost=ost, flush=flush) - flops_model.stop_flops_count() - - if as_strings: - return flops_to_string(flops_count), params_to_string(params_count) - - return flops_count, params_count - - -def flops_to_string(flops: float, - units: Optional[str] = 'GFLOPs', - precision: int = 2) -> str: - """Convert FLOPs number into a string. - - Note that Here we take a multiply-add counts as one FLOP. - - Args: - flops (float): FLOPs number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', - 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically - choose the most suitable unit for FLOPs. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted FLOPs number with units. - - Examples: - >>> flops_to_string(1e9) - '1.0 GFLOPs' - >>> flops_to_string(2e5, 'MFLOPs') - '0.2 MFLOPs' - >>> flops_to_string(3e-9, None) - '3e-09 FLOPs' - """ - if units is None: - if flops // 10**9 > 0: - return str(round(flops / 10.**9, precision)) + ' GFLOPs' - elif flops // 10**6 > 0: - return str(round(flops / 10.**6, precision)) + ' MFLOPs' - elif flops // 10**3 > 0: - return str(round(flops / 10.**3, precision)) + ' KFLOPs' - else: - return str(flops) + ' FLOPs' - else: - if units == 'GFLOPs': - return str(round(flops / 10.**9, precision)) + ' ' + units - elif units == 'MFLOPs': - return str(round(flops / 10.**6, precision)) + ' ' + units - elif units == 'KFLOPs': - return str(round(flops / 10.**3, precision)) + ' ' + units - else: - return str(flops) + ' FLOPs' - - -def params_to_string(num_params: float, - units: Optional[str] = None, - precision: int = 2) -> str: - """Convert parameter number into a string. - - Args: - num_params (float): Parameter number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'M', - 'K' and ''. If set to None, it will automatically choose the most - suitable unit for Parameter number. Default: None. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted parameter number with units. - - Examples: - >>> params_to_string(1e9) - '1000.0 M' - >>> params_to_string(2e5) - '200.0 k' - >>> params_to_string(3e-9) - '3e-09' - """ - if units is None: - if num_params // 10**6 > 0: - return str(round(num_params / 10**6, precision)) + ' M' - elif num_params // 10**3: - return str(round(num_params / 10**3, precision)) + ' k' - else: - return str(num_params) - else: - if units == 'M': - return str(round(num_params / 10.**6, precision)) + ' ' + units - elif units == 'K': - return str(round(num_params / 10.**3, precision)) + ' ' + units - else: - return str(num_params) - - -def print_model_with_flops(model: nn.Module, - total_flops: float, - total_params: float, - units: Optional[str] = 'GFLOPs', - precision: int = 3, - ost: TextIO = sys.stdout, - flush: bool = False) -> None: - """Print a model with FLOPs for each layer. - - Args: - model (nn.Module): The model to be printed. - total_flops (float): Total FLOPs of the model. - total_params (float): Total parameter counts of the model. - units (str | None): Converted FLOPs units. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 3. - ost (stream): same as `file` param in :func:`print`. - Default: sys.stdout. - flush (bool): same as that in :func:`print`. Default: False. - - Example: - >>> class ExampleModel(nn.Module): - - >>> def __init__(self): - >>> super().__init__() - >>> self.conv1 = nn.Conv2d(3, 8, 3) - >>> self.conv2 = nn.Conv2d(8, 256, 3) - >>> self.conv3 = nn.Conv2d(256, 8, 3) - >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) - >>> self.flatten = nn.Flatten() - >>> self.fc = nn.Linear(8, 1) - - >>> def forward(self, x): - >>> x = self.conv1(x) - >>> x = self.conv2(x) - >>> x = self.conv3(x) - >>> x = self.avg_pool(x) - >>> x = self.flatten(x) - >>> x = self.fc(x) - >>> return x - - >>> model = ExampleModel() - >>> x = (3, 16, 16) - to print the complexity information state for each layer, you can use - >>> get_model_complexity_info(model, x) - or directly use - >>> print_model_with_flops(model, 4579784.0, 37361) - ExampleModel( - 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, - (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 - (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) - (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) - (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) - (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) - (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) - ) - """ - - def accumulate_params(self): - if is_supported_instance(self): - return self.__params__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_params() - return sum - - def accumulate_flops(self): - if is_supported_instance(self): - return self.__flops__ / model.__batch_counter__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_flops() - return sum - - def flops_repr(self): - accumulated_num_params = self.accumulate_params() - accumulated_flops_cost = self.accumulate_flops() - return ', '.join([ - params_to_string( - accumulated_num_params, units='M', precision=precision), - f'{accumulated_num_params / total_params:.3%} Params', - flops_to_string( - accumulated_flops_cost, units=units, precision=precision), - f'{accumulated_flops_cost / total_flops:.3%} FLOPs', - self.original_extra_repr() - ]) - - def add_extra_repr(m): - m.accumulate_flops = accumulate_flops.__get__(m) - m.accumulate_params = accumulate_params.__get__(m) - flops_extra_repr = flops_repr.__get__(m) - if m.extra_repr != flops_extra_repr: - m.original_extra_repr = m.extra_repr - m.extra_repr = flops_extra_repr - assert m.extra_repr != m.original_extra_repr - - def del_extra_repr(m): - if hasattr(m, 'original_extra_repr'): - m.extra_repr = m.original_extra_repr - del m.original_extra_repr - if hasattr(m, 'accumulate_flops'): - del m.accumulate_flops - - model.apply(add_extra_repr) - print(model, file=ost, flush=flush) - model.apply(del_extra_repr) - - -def get_model_parameters_number(model: nn.Module) -> float: - """Calculate parameter number of a model. - - Args: - model (nn.module): The model for parameter number calculation. - - Returns: - float: Parameter number of the model. - """ - num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - return num_params - - -def add_flops_counting_methods(net_main_module: nn.Module) -> nn.Module: - # adding additional methods to the existing module object, - # this is done this way so that each function has access to self object - net_main_module.start_flops_count = start_flops_count.__get__( # type: ignore # noqa E501 - net_main_module) - net_main_module.stop_flops_count = stop_flops_count.__get__( # type: ignore # noqa E501 - net_main_module) - net_main_module.reset_flops_count = reset_flops_count.__get__( # type: ignore # noqa E501 - net_main_module) - net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # type: ignore # noqa E501 - net_main_module) - - net_main_module.reset_flops_count() - - return net_main_module - - -def compute_average_flops_cost(self) -> Tuple[float, float]: - """Compute average FLOPs cost. - - A method to compute average FLOPs cost, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - - Returns: - float: Current mean flops consumption per image. - """ - batches_count = self.__batch_counter__ - flops_sum = 0 - for module in self.modules(): - if is_supported_instance(module): - flops_sum += module.__flops__ - params_sum = get_model_parameters_number(self) - return flops_sum / batches_count, params_sum - - -def start_flops_count(self) -> None: - """Activate the computation of mean flops consumption per image. - - A method to activate the computation of mean flops consumption per image. - which will be available after ``add_flops_counting_methods()`` is called on - a desired net object. It should be called before running the network. - """ - add_batch_counter_hook_function(self) - - def add_flops_counter_hook_function(module: nn.Module) -> None: - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - return - - else: - handle = module.register_forward_hook( - get_modules_mapping()[type(module)]) - - module.__flops_handle__ = handle - - self.apply(partial(add_flops_counter_hook_function)) - - -def stop_flops_count(self) -> None: - """Stop computing the mean flops consumption per image. - - A method to stop computing the mean flops consumption per image, which will - be available after ``add_flops_counting_methods()`` is called on a desired - net object. It can be called to pause the computation whenever. - """ - remove_batch_counter_hook_function(self) - self.apply(remove_flops_counter_hook_function) - - -def reset_flops_count(self) -> None: - """Reset statistics computed so far. - - A method to Reset computed statistics, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - """ - add_batch_counter_variables_or_reset(self) - self.apply(add_flops_counter_variable_or_reset) - - -# ---- Internal functions -def empty_flops_counter_hook(module: nn.Module, input: tuple, - output: Any) -> None: - module.__flops__ += 0 - - -def upsample_flops_counter_hook(module: nn.Module, input: tuple, - output: torch.Tensor) -> None: - output_size = output[0] - batch_size = output_size.shape[0] - output_elements_count = batch_size - for val in output_size.shape[1:]: - output_elements_count *= val - module.__flops__ += int(output_elements_count) - - -def relu_flops_counter_hook(module: nn.Module, input: tuple, - output: torch.Tensor) -> None: - active_elements_count = output.numel() - module.__flops__ += int(active_elements_count) - - -def linear_flops_counter_hook(module: nn.Module, input: tuple, - output: torch.Tensor) -> None: - output_last_dim = output.shape[ - -1] # pytorch checks dimensions, so here we don't care much - module.__flops__ += int(np.prod(input[0].shape) * output_last_dim) - - -def pool_flops_counter_hook(module: nn.Module, input: tuple, - output: torch.Tensor) -> None: - module.__flops__ += int(np.prod(input[0].shape)) - - -def norm_flops_counter_hook(module: nn.Module, input: tuple, - output: torch.Tensor) -> None: - batch_flops = np.prod(input[0].shape) - if (getattr(module, 'affine', False) - or getattr(module, 'elementwise_affine', False)): - batch_flops *= 2 - module.__flops__ += int(batch_flops) - - -def deconv_flops_counter_hook(conv_module: nn.Module, input: tuple, - output: torch.Tensor) -> None: - # Can have multiple inputs, getting the first one - batch_size = input[0].shape[0] - input_height, input_width = input[0].shape[2:] - - kernel_height, kernel_width = conv_module.kernel_size - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = ( - kernel_height * kernel_width * in_channels * filters_per_channel) - - active_elements_count = batch_size * input_height * input_width - overall_conv_flops = conv_per_position_flops * active_elements_count - bias_flops = 0 - if conv_module.bias is not None: - output_height, output_width = output.shape[2:] - bias_flops = out_channels * batch_size * output_height * output_width - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def conv_flops_counter_hook(conv_module: nn.Module, input: tuple, - output: torch.Tensor) -> None: - # Can have multiple inputs, getting the first one - batch_size = input[0].shape[0] - output_dims = list(output.shape[2:]) - - kernel_dims = list(conv_module.kernel_size) - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = int( - np.prod(kernel_dims)) * in_channels * filters_per_channel - - active_elements_count = batch_size * int(np.prod(output_dims)) - - overall_conv_flops = conv_per_position_flops * active_elements_count - - bias_flops = 0 - - if conv_module.bias is not None: - - bias_flops = out_channels * active_elements_count - - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def batch_counter_hook(module: nn.Module, input: tuple, output: Any) -> None: - batch_size = 1 - if len(input) > 0: - # Can have multiple inputs, getting the first one - batch_size = len(input[0]) - else: - warnings.warn('No positional inputs found for a module, ' - 'assuming batch size is 1.') - module.__batch_counter__ += batch_size - - -def add_batch_counter_variables_or_reset(module: nn.Module) -> None: - - module.__batch_counter__ = 0 - - -def add_batch_counter_hook_function(module: nn.Module) -> None: - if hasattr(module, '__batch_counter_handle__'): - return - - handle = module.register_forward_hook(batch_counter_hook) - module.__batch_counter_handle__ = handle - - -def remove_batch_counter_hook_function(module: nn.Module) -> None: - if hasattr(module, '__batch_counter_handle__'): - module.__batch_counter_handle__.remove() - del module.__batch_counter_handle__ - - -def add_flops_counter_variable_or_reset(module: nn.Module) -> None: - if is_supported_instance(module): - if hasattr(module, '__flops__') or hasattr(module, '__params__'): - warnings.warn('variables __flops__ or __params__ are already ' - 'defined for the module' + type(module).__name__ + - ' ptflops can affect your code!') - module.__flops__ = 0 - module.__params__ = get_model_parameters_number(module) - - -def is_supported_instance(module: nn.Module) -> bool: - if type(module) in get_modules_mapping(): - return True - return False - - -def remove_flops_counter_hook_function(module: nn.Module) -> None: - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - module.__flops_handle__.remove() - del module.__flops_handle__ - - -def get_modules_mapping() -> Dict: - return { - # convolutions - nn.Conv1d: conv_flops_counter_hook, - nn.Conv2d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, - nn.Conv3d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, - # activations - nn.ReLU: relu_flops_counter_hook, - nn.PReLU: relu_flops_counter_hook, - nn.ELU: relu_flops_counter_hook, - nn.LeakyReLU: relu_flops_counter_hook, - nn.ReLU6: relu_flops_counter_hook, - # poolings - nn.MaxPool1d: pool_flops_counter_hook, - nn.AvgPool1d: pool_flops_counter_hook, - nn.AvgPool2d: pool_flops_counter_hook, - nn.MaxPool2d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, - nn.MaxPool3d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, - nn.AvgPool3d: pool_flops_counter_hook, - nn.AdaptiveMaxPool1d: pool_flops_counter_hook, - nn.AdaptiveAvgPool1d: pool_flops_counter_hook, - nn.AdaptiveMaxPool2d: pool_flops_counter_hook, - nn.AdaptiveAvgPool2d: pool_flops_counter_hook, - nn.AdaptiveMaxPool3d: pool_flops_counter_hook, - nn.AdaptiveAvgPool3d: pool_flops_counter_hook, - # normalizations - nn.BatchNorm1d: norm_flops_counter_hook, - nn.BatchNorm2d: norm_flops_counter_hook, - nn.BatchNorm3d: norm_flops_counter_hook, - nn.GroupNorm: norm_flops_counter_hook, - nn.InstanceNorm1d: norm_flops_counter_hook, - nn.InstanceNorm2d: norm_flops_counter_hook, - nn.InstanceNorm3d: norm_flops_counter_hook, - nn.LayerNorm: norm_flops_counter_hook, - # FC - nn.Linear: linear_flops_counter_hook, - mmcv.cnn.bricks.Linear: linear_flops_counter_hook, - # Upscale - nn.Upsample: upsample_flops_counter_hook, - # Deconvolution - nn.ConvTranspose2d: deconv_flops_counter_hook, - mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, - } diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/fuse_conv_bn.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/fuse_conv_bn.py deleted file mode 100644 index 6ccaab3bf..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/fuse_conv_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def _fuse_conv_bn(conv: nn.Module, bn: nn.Module) -> nn.Module: - """Fuse conv and bn into one module. - - Args: - conv (nn.Module): Conv to be fused. - bn (nn.Module): BN to be fused. - - Returns: - nn.Module: Fused module. - """ - conv_w = conv.weight - conv_b = conv.bias if conv.bias is not None else torch.zeros_like( - bn.running_mean) - - factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) - conv.weight = nn.Parameter(conv_w * - factor.reshape([conv.out_channels, 1, 1, 1])) - conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) - return conv - - -def fuse_conv_bn(module: nn.Module) -> nn.Module: - """Recursively fuse conv and bn in a module. - - During inference, the functionary of batch norm layers is turned off - but only the mean and var alone channels are used, which exposes the - chance to fuse it with the preceding conv layers to save computations and - simplify network structures. - - Args: - module (nn.Module): Module to be fused. - - Returns: - nn.Module: Fused module. - """ - last_conv = None - last_conv_name = None - - for name, child in module.named_children(): - if isinstance(child, - (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): - if last_conv is None: # only fuse BN that is after Conv - continue - fused_conv = _fuse_conv_bn(last_conv, child) - module._modules[last_conv_name] = fused_conv - # To reduce changes, set BN as Identity instead of deleting it. - module._modules[name] = nn.Identity() - last_conv = None - elif isinstance(child, nn.Conv2d): - last_conv = child - last_conv_name = name - else: - fuse_conv_bn(child) - return module diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/sync_bn.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/sync_bn.py deleted file mode 100644 index c534fc0e1..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/sync_bn.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -import mmcv - - -class _BatchNormXd(nn.modules.batchnorm._BatchNorm): - """A general BatchNorm layer without input dimension check. - - Reproduced from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc - is `_check_input_dim` that is designed for tensor sanity checks. - The check has been bypassed in this class for the convenience of converting - SyncBatchNorm. - """ - - def _check_input_dim(self, input: torch.Tensor): - return - - -def revert_sync_batchnorm(module: nn.Module) -> nn.Module: - """Helper function to convert all `SyncBatchNorm` (SyncBN) and - `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to - `BatchNormXd` layers. - - Adapted from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - - Args: - module (nn.Module): The module containing `SyncBatchNorm` layers. - - Returns: - module_output: The converted module with `BatchNormXd` layers. - """ - module_output = module - module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] - if hasattr(mmcv, 'ops'): - module_checklist.append(mmcv.ops.SyncBatchNorm) - if isinstance(module, tuple(module_checklist)): - module_output = _BatchNormXd(module.num_features, module.eps, - module.momentum, module.affine, - module.track_running_stats) - if module.affine: - # no_grad() may not be needed here but - # just to be consistent with `convert_sync_batchnorm()` - with torch.no_grad(): - module_output.weight = module.weight - module_output.bias = module.bias - module_output.running_mean = module.running_mean - module_output.running_var = module.running_var - module_output.num_batches_tracked = module.num_batches_tracked - module_output.training = module.training - # qconfig exists in quantized models - if hasattr(module, 'qconfig'): - module_output.qconfig = module.qconfig - for name, child in module.named_children(): - module_output.add_module(name, revert_sync_batchnorm(child)) - del module - return module_output diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/weight_init.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/weight_init.py deleted file mode 100644 index 6e0d293ad..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/utils/weight_init.py +++ /dev/null @@ -1,708 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings -from typing import Dict, List, Optional, Union - -import numpy as np -import torch -import torch.nn as nn -from torch import Tensor - -from mmcv.utils import Registry, build_from_cfg, get_logger, print_log - -INITIALIZERS = Registry('initializer') - - -def update_init_info(module: nn.Module, init_info: str) -> None: - """Update the `_params_init_info` in the module if the value of parameters - are changed. - - Args: - module (obj:`nn.Module`): The module of PyTorch with a user-defined - attribute `_params_init_info` which records the initialization - information. - init_info (str): The string that describes the initialization. - """ - assert hasattr( - module, - '_params_init_info'), f'Can not find `_params_init_info` in {module}' - for name, param in module.named_parameters(): - - assert param in module._params_init_info, ( - f'Find a new :obj:`Parameter` ' - f'named `{name}` during executing the ' - f'`init_weights` of ' - f'`{module.__class__.__name__}`. ' - f'Please do not add or ' - f'replace parameters during executing ' - f'the `init_weights`. ') - - # The parameter has been changed during executing the - # `init_weights` of module - mean_value = param.data.mean() - if module._params_init_info[param]['tmp_mean_value'] != mean_value: - module._params_init_info[param]['init_info'] = init_info - module._params_init_info[param]['tmp_mean_value'] = mean_value - - -def constant_init(module: nn.Module, val: float, bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - nn.init.constant_(module.weight, val) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def xavier_init(module: nn.Module, - gain: float = 1, - bias: float = 0, - distribution: str = 'normal') -> None: - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.xavier_uniform_(module.weight, gain=gain) - else: - nn.init.xavier_normal_(module.weight, gain=gain) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - nn.init.normal_(module.weight, mean, std) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def trunc_normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - trunc_normal_(module.weight, mean, std, a, b) # type: ignore - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) # type: ignore - - -def uniform_init(module: nn.Module, - a: float = 0, - b: float = 1, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - nn.init.uniform_(module.weight, a, b) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def kaiming_init(module: nn.Module, - a: float = 0, - mode: str = 'fan_out', - nonlinearity: str = 'relu', - bias: float = 0, - distribution: str = 'normal') -> None: - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.kaiming_uniform_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - else: - nn.init.kaiming_normal_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def caffe2_xavier_init(module: nn.Module, bias: float = 0) -> None: - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - kaiming_init( - module, - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - bias=bias, - distribution='uniform') - - -def bias_init_with_prob(prior_prob: float) -> float: - """initialize conv/fc bias value according to a given probability value.""" - bias_init = float(-np.log((1 - prior_prob) / prior_prob)) - return bias_init - - -def _get_bases_name(m: nn.Module) -> List[str]: - return [b.__name__ for b in m.__class__.__bases__] - - -class BaseInit: - - def __init__(self, - *, - bias: float = 0, - bias_prob: Optional[float] = None, - layer: Union[str, List, None] = None): - self.wholemodule = False - if not isinstance(bias, (int, float)): - raise TypeError(f'bias must be a number, but got a {type(bias)}') - - if bias_prob is not None: - if not isinstance(bias_prob, float): - raise TypeError(f'bias_prob type must be float, \ - but got {type(bias_prob)}') - - if layer is not None: - if not isinstance(layer, (str, list)): - raise TypeError(f'layer must be a str or a list of str, \ - but got a {type(layer)}') - else: - layer = [] - - if bias_prob is not None: - self.bias = bias_init_with_prob(bias_prob) - else: - self.bias = bias - self.layer = [layer] if isinstance(layer, str) else layer - - def _get_init_info(self) -> str: - info = f'{self.__class__.__name__}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Constant') -class ConstantInit(BaseInit): - """Initialize module parameters with constant values. - - Args: - val (int | float): the value to fill the weights in the module with - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, val: Union[int, float], **kwargs): - super().__init__(**kwargs) - self.val = val - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - constant_init(m, self.val, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - constant_init(m, self.val, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self) -> str: - info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Xavier') -class XavierInit(BaseInit): - r"""Initialize module parameters with values according to the method - described in `Understanding the difficulty of training deep feedforward - neural networks - Glorot, X. & Bengio, Y. (2010). - `_ - - Args: - gain (int | float): an optional scaling factor. Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` - or ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - gain: float = 1, - distribution: str = 'normal', - **kwargs): - super().__init__(**kwargs) - self.gain = gain - self.distribution = distribution - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - xavier_init(m, self.gain, self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - xavier_init(m, self.gain, self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self) -> str: - info = f'{self.__class__.__name__}: gain={self.gain}, ' \ - f'distribution={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Normal') -class NormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. - - Args: - mean (int | float):the mean of the normal distribution. Defaults to 0. - std (int | float): the standard deviation of the normal distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, mean: float = 0, std: float = 1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.std = std - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - normal_init(m, self.mean, self.std, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - normal_init(m, self.mean, self.std, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self) -> str: - info = f'{self.__class__.__name__}: mean={self.mean},' \ - f' std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='TruncNormal') -class TruncNormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values - outside :math:`[a, b]`. - - Args: - mean (float): the mean of the normal distribution. Defaults to 0. - std (float): the standard deviation of the normal distribution. - Defaults to 1. - a (float): The minimum cutoff value. - b ( float): The maximum cutoff value. - bias (float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - **kwargs) -> None: - super().__init__(**kwargs) - self.mean = mean - self.std = std - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ - f' mean={self.mean}, std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Uniform') -class UniformInit(BaseInit): - r"""Initialize module parameters with values drawn from the uniform - distribution :math:`\mathcal{U}(a, b)`. - - Args: - a (int | float): the lower bound of the uniform distribution. - Defaults to 0. - b (int | float): the upper bound of the uniform distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, a: float = 0., b: float = 1., **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - uniform_init(m, self.a, self.b, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - uniform_init(m, self.a, self.b, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self) -> str: - info = f'{self.__class__.__name__}: a={self.a},' \ - f' b={self.b}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Kaiming') -class KaimingInit(BaseInit): - r"""Initialize module parameters with the values according to the method - described in `Delving deep into rectifiers: Surpassing human-level - performance on ImageNet classification - He, K. et al. (2015). - `_ - - Args: - a (int | float): the negative slope of the rectifier used after this - layer (only used with ``'leaky_relu'``). Defaults to 0. - mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing - ``'fan_in'`` preserves the magnitude of the variance of the weights - in the forward pass. Choosing ``'fan_out'`` preserves the - magnitudes in the backwards pass. Defaults to ``'fan_out'``. - nonlinearity (str): the non-linear function (`nn.functional` name), - recommended to use only with ``'relu'`` or ``'leaky_relu'`` . - Defaults to 'relu'. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` or - ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - a: float = 0, - mode: str = 'fan_out', - nonlinearity: str = 'relu', - distribution: str = 'normal', - **kwargs): - super().__init__(**kwargs) - self.a = a - self.mode = mode - self.nonlinearity = nonlinearity - self.distribution = distribution - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self) -> str: - info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ - f'nonlinearity={self.nonlinearity}, ' \ - f'distribution ={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Caffe2Xavier') -class Caffe2XavierInit(KaimingInit): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - def __init__(self, **kwargs): - super().__init__( - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='uniform', - **kwargs) - - def __call__(self, module: nn.Module) -> None: - super().__call__(module) - - -@INITIALIZERS.register_module(name='Pretrained') -class PretrainedInit: - """Initialize module by loading a pretrained model. - - Args: - checkpoint (str): the checkpoint file of the pretrained model should - be load. - prefix (str, optional): the prefix of a sub-module in the pretrained - model. it is for loading a part of the pretrained model to - initialize. For example, if we would like to only load the - backbone of a detector model, we can set ``prefix='backbone.'``. - Defaults to None. - map_location (str): map tensors into proper locations. - """ - - def __init__(self, - checkpoint: str, - prefix: Optional[str] = None, - map_location: Optional[str] = None): - self.checkpoint = checkpoint - self.prefix = prefix - self.map_location = map_location - - def __call__(self, module: nn.Module) -> None: - from mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, - load_state_dict) - logger = get_logger('mmcv') - if self.prefix is None: - print_log(f'load model from: {self.checkpoint}', logger=logger) - load_checkpoint( - module, - self.checkpoint, - map_location=self.map_location, - strict=False, - logger=logger) - else: - print_log( - f'load {self.prefix} in model from: {self.checkpoint}', - logger=logger) - state_dict = _load_checkpoint_with_prefix( - self.prefix, self.checkpoint, map_location=self.map_location) - load_state_dict(module, state_dict, strict=False, logger=logger) - - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self) -> str: - info = f'{self.__class__.__name__}: load from {self.checkpoint}' - return info - - -def _initialize(module: nn.Module, - cfg: Dict, - wholemodule: bool = False) -> None: - func = build_from_cfg(cfg, INITIALIZERS) - # wholemodule flag is for override mode, there is no layer key in override - # and initializer will give init values for the whole module with the name - # in override. - func.wholemodule = wholemodule - func(module) - - -def _initialize_override(module: nn.Module, override: Union[Dict, List], - cfg: Dict) -> None: - if not isinstance(override, (dict, list)): - raise TypeError(f'override must be a dict or a list of dict, \ - but got {type(override)}') - - override = [override] if isinstance(override, dict) else override - - for override_ in override: - - cp_override = copy.deepcopy(override_) - name = cp_override.pop('name', None) - if name is None: - raise ValueError('`override` must contain the key "name",' - f'but got {cp_override}') - # if override only has name key, it means use args in init_cfg - if not cp_override: - cp_override.update(cfg) - # if override has name key and other args except type key, it will - # raise error - elif 'type' not in cp_override.keys(): - raise ValueError( - f'`override` need "type" key, but got {cp_override}') - - if hasattr(module, name): - _initialize(getattr(module, name), cp_override, wholemodule=True) - else: - raise RuntimeError(f'module did not have attribute {name}, ' - f'but init_cfg is {cp_override}.') - - -def initialize(module: nn.Module, init_cfg: Union[Dict, List[dict]]) -> None: - r"""Initialize a module. - - Args: - module (``torch.nn.Module``): the module will be initialized. - init_cfg (dict | list[dict]): initialization configuration dict to - define initializer. OpenMMLab has implemented 6 initializers - including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, - ``Kaiming``, and ``Pretrained``. - - Example: - >>> module = nn.Linear(2, 3, bias=True) - >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) - >>> initialize(module, init_cfg) - - >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) - >>> # define key ``'layer'`` for initializing layer with different - >>> # configuration - >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), - dict(type='Constant', layer='Linear', val=2)] - >>> initialize(module, init_cfg) - - >>> # define key``'override'`` to initialize some specific part in - >>> # module - >>> class FooNet(nn.Module): - >>> def __init__(self): - >>> super().__init__() - >>> self.feat = nn.Conv2d(3, 16, 3) - >>> self.reg = nn.Conv2d(16, 10, 3) - >>> self.cls = nn.Conv2d(16, 5, 3) - >>> model = FooNet() - >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', - >>> override=dict(type='Constant', name='reg', val=3, bias=4)) - >>> initialize(model, init_cfg) - - >>> model = ResNet(depth=50) - >>> # Initialize weights with the pretrained model. - >>> init_cfg = dict(type='Pretrained', - checkpoint='torchvision://resnet50') - >>> initialize(model, init_cfg) - - >>> # Initialize weights of a sub-module with the specific part of - >>> # a pretrained model by using "prefix". - >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ - >>> 'retinanet_r50_fpn_1x_coco/'\ - >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' - >>> init_cfg = dict(type='Pretrained', - checkpoint=url, prefix='backbone.') - """ - if not isinstance(init_cfg, (dict, list)): - raise TypeError(f'init_cfg must be a dict or a list of dict, \ - but got {type(init_cfg)}') - - if isinstance(init_cfg, dict): - init_cfg = [init_cfg] - - for cfg in init_cfg: - # should deeply copy the original config because cfg may be used by - # other modules, e.g., one init_cfg shared by multiple bottleneck - # blocks, the expected cfg will be changed after pop and will change - # the initialization behavior of other modules - cp_cfg = copy.deepcopy(cfg) - override = cp_cfg.pop('override', None) - _initialize(module, cp_cfg) - - if override is not None: - cp_cfg.pop('layer', None) - _initialize_override(module, override, cp_cfg) - else: - # All attributes in module have same initialization. - pass - - -def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, - b: float) -> Tensor: - # Method based on - # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf - # Modified from - # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - def norm_cdf(x): - # Computes standard normal cumulative distribution function - return (1. + math.erf(x / math.sqrt(2.))) / 2. - - if (mean < a - 2 * std) or (mean > b + 2 * std): - warnings.warn( - 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' - 'The distribution of values may be incorrect.', - stacklevel=2) - - with torch.no_grad(): - # Values are generated by using a truncated uniform distribution and - # then using the inverse CDF for the normal distribution. - # Get upper and lower cdf values - lower = norm_cdf((a - mean) / std) - upper = norm_cdf((b - mean) / std) - - # Uniformly fill tensor with values from [lower, upper], then translate - # to [2lower-1, 2upper-1]. - tensor.uniform_(2 * lower - 1, 2 * upper - 1) - - # Use inverse cdf transform for normal distribution to get truncated - # standard normal - tensor.erfinv_() - - # Transform to proper mean, std - tensor.mul_(std * math.sqrt(2.)) - tensor.add_(mean) - - # Clamp to ensure it's in the proper range - tensor.clamp_(min=a, max=b) - return tensor - - -def trunc_normal_(tensor: Tensor, - mean: float = 0., - std: float = 1., - a: float = -2., - b: float = 2.) -> Tensor: - r"""Fills the input Tensor with values drawn from a truncated - normal distribution. The values are effectively drawn from the - normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` - with values outside :math:`[a, b]` redrawn until they are within - the bounds. The method used for generating the random values works - best when :math:`a \leq \text{mean} \leq b`. - - Modified from - https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - - Args: - tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. - mean (float): the mean of the normal distribution. - std (float): the standard deviation of the normal distribution. - a (float): the minimum cutoff value. - b (float): the maximum cutoff value. - """ - return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/cv/super_resolution/liif/pytorch/mmcv/cnn/vgg.py b/cv/super_resolution/liif/pytorch/mmcv/cnn/vgg.py deleted file mode 100644 index a1d9ba211..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/cnn/vgg.py +++ /dev/null @@ -1,177 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging -from typing import List, Optional, Sequence, Tuple, Union - -import torch.nn as nn -from torch import Tensor - -from .utils import constant_init, kaiming_init, normal_init - - -def conv3x3(in_planes: int, out_planes: int, dilation: int = 1) -> nn.Module: - """3x3 convolution with padding.""" - return nn.Conv2d( - in_planes, - out_planes, - kernel_size=3, - padding=dilation, - dilation=dilation) - - -def make_vgg_layer(inplanes: int, - planes: int, - num_blocks: int, - dilation: int = 1, - with_bn: bool = False, - ceil_mode: bool = False) -> List[nn.Module]: - layers = [] - for _ in range(num_blocks): - layers.append(conv3x3(inplanes, planes, dilation)) - if with_bn: - layers.append(nn.BatchNorm2d(planes)) - layers.append(nn.ReLU(inplace=True)) - inplanes = planes - layers.append(nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=ceil_mode)) - - return layers - - -class VGG(nn.Module): - """VGG backbone. - - Args: - depth (int): Depth of vgg, from {11, 13, 16, 19}. - with_bn (bool): Use BatchNorm or not. - num_classes (int): number of classes for classification. - num_stages (int): VGG stages, normally 5. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze - running stats (mean and var). - bn_frozen (bool): Whether to freeze weight and bias of BN layers. - """ - - arch_settings = { - 11: (1, 1, 2, 2, 2), - 13: (2, 2, 2, 2, 2), - 16: (2, 2, 3, 3, 3), - 19: (2, 2, 4, 4, 4) - } - - def __init__(self, - depth: int, - with_bn: bool = False, - num_classes: int = -1, - num_stages: int = 5, - dilations: Sequence[int] = (1, 1, 1, 1, 1), - out_indices: Sequence[int] = (0, 1, 2, 3, 4), - frozen_stages: int = -1, - bn_eval: bool = True, - bn_frozen: bool = False, - ceil_mode: bool = False, - with_last_pool: bool = True): - super().__init__() - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for vgg') - assert num_stages >= 1 and num_stages <= 5 - stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - assert len(dilations) == num_stages - assert max(out_indices) <= num_stages - - self.num_classes = num_classes - self.out_indices = out_indices - self.frozen_stages = frozen_stages - self.bn_eval = bn_eval - self.bn_frozen = bn_frozen - - self.inplanes = 3 - start_idx = 0 - vgg_layers = [] - self.range_sub_modules = [] - for i, num_blocks in enumerate(self.stage_blocks): - num_modules = num_blocks * (2 + with_bn) + 1 - end_idx = start_idx + num_modules - dilation = dilations[i] - planes = 64 * 2**i if i < 4 else 512 - vgg_layer = make_vgg_layer( - self.inplanes, - planes, - num_blocks, - dilation=dilation, - with_bn=with_bn, - ceil_mode=ceil_mode) - vgg_layers.extend(vgg_layer) - self.inplanes = planes - self.range_sub_modules.append([start_idx, end_idx]) - start_idx = end_idx - if not with_last_pool: - vgg_layers.pop(-1) - self.range_sub_modules[-1][1] -= 1 - self.module_name = 'features' - self.add_module(self.module_name, nn.Sequential(*vgg_layers)) - - if self.num_classes > 0: - self.classifier = nn.Sequential( - nn.Linear(512 * 7 * 7, 4096), - nn.ReLU(True), - nn.Dropout(), - nn.Linear(4096, 4096), - nn.ReLU(True), - nn.Dropout(), - nn.Linear(4096, num_classes), - ) - - def init_weights(self, pretrained: Optional[str] = None) -> None: - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m) - elif isinstance(m, nn.BatchNorm2d): - constant_init(m, 1) - elif isinstance(m, nn.Linear): - normal_init(m, std=0.01) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x: Tensor) -> Union[Tensor, Tuple[Tensor, ...]]: - outs = [] - vgg_layers = getattr(self, self.module_name) - for i in range(len(self.stage_blocks)): - for j in range(*self.range_sub_modules[i]): - vgg_layer = vgg_layers[j] - x = vgg_layer(x) - if i in self.out_indices: - outs.append(x) - if self.num_classes > 0: - x = x.view(x.size(0), -1) - x = self.classifier(x) - outs.append(x) - if len(outs) == 1: - return outs[0] - else: - return tuple(outs) - - def train(self, mode: bool = True) -> None: - super().train(mode) - if self.bn_eval: - for m in self.modules(): - if isinstance(m, nn.BatchNorm2d): - m.eval() - if self.bn_frozen: - for params in m.parameters(): - params.requires_grad = False - vgg_layers = getattr(self, self.module_name) - if mode and self.frozen_stages >= 0: - for i in range(self.frozen_stages): - for j in range(*self.range_sub_modules[i]): - mod = vgg_layers[j] - mod.eval() - for param in mod.parameters(): - param.requires_grad = False diff --git a/cv/super_resolution/liif/pytorch/mmcv/device/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/device/__init__.py deleted file mode 100644 index 6ac55e63b..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/device/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from . import ipu, mlu - -__all__ = ['mlu', 'ipu'] diff --git a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/device/ipu/__init__.py deleted file mode 100755 index d550865ad..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import IPUFp16OptimizerHook - from .model_wrapper import ipu_model_wrapper - from .runner import IPUBaseRunner, IPUEpochBasedRunner, IPUIterBasedRunner - from .utils import cfg2options - __all__ = [ - 'cfg2options', 'ipu_model_wrapper', 'IPUFp16OptimizerHook', - 'IPUDataLoader', 'IPUBaseRunner', 'IPUEpochBasedRunner', - 'IPUIterBasedRunner' - ] diff --git a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/dataloader.py b/cv/super_resolution/liif/pytorch/mmcv/device/ipu/dataloader.py deleted file mode 100755 index 1485df2f3..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/dataloader.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence -from functools import partial - -import poptorch -from torch.utils.data.dataloader import default_collate - -from mmcv.parallel import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Put each data field into a tensor/DataContainer with outer dimension - batch size. - - TODO support for - :type:`~mmcv.parallel.DataContainer`. Currently, it will be ignored. - There are 3 cases. - - 1. cpu_only = True, e.g., meta data. - 2. cpu_only = False, stack = True, e.g., images tensors. - 3. cpu_only = False, stack = False, e.g., gt bboxes. - """ - - if not isinstance(batch, Sequence): - raise TypeError( - f'`batch` should be a sequence, but got {type(batch)}.') - - if isinstance(batch[0], DataContainer): - # TODO `DataContainer` will be supported in the future. - raise TypeError('DataContainer is not supported in ipu data loader.') - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - collated_batch = [] - for samples in transposed: - if not isinstance(samples[0], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch.append(collate(samples, samples_per_gpu)) - return collated_batch - elif isinstance(batch[0], Mapping): - collated_batch = {} - for key in batch[0]: - if not isinstance(batch[0][key], DataContainer): - # At present, we will skip the processing of datacontainer, - # which will reduce the performance of IPU DataLoder - collated_batch[key] = collate([d[key] for d in batch]) - return collated_batch - else: - return default_collate(batch) - - -class IPUDataLoader(poptorch.DataLoader): - """Thin wrapper of `torch.utils.data.DataLoader`. - - Compared with the pytorch DataLoder, this DataLoder changes the way of - calculation of batch size and adds the AsynchronousDataAccessor to - load and release data faster in cpu mode. - - If this data loader is used in a distributed execution environment, it will - ensure that each process uses a different subset of the dataset, providing - you first call ``options.randomSeed(N)`` with an integer N which is the - same across all hosts. - - Args: - dataset (torch.utils.data.Dataset): The dataset to get the data from. - options (poptorch.Options): Options that will be used to compile - and run the model. - batch_size (int, optional): This is the batch size in the conventional - sense of being the size that runs through an operation in the model - at any given time. - shuffle (bool, optional): set to ``True`` to have the data reshuffled - at every epoch (default: ``False``). - num_workers (int, optional): how many subprocesses to use for data - loading. ``0`` means that the data will be loaded in the main - process. (default: ``0``) - drop_last (bool, optional): If True and the number of elements in the - dataset is not a multiple of the combined batch size then the - incomplete batch at the end will be dropped. - persistent_workers (bool, optional): Re-use workers between - iterations if True. - auto_distributed_partitioning (bool, optional): If True, partitions the - dataset for distributed execution automatically. Otherwise, it is - assumed that partitioning has been handled manually. - mode (poptorch.DataLoaderMode, optional): If `DataLoaderMode.Async`, - uses an :py:class:`~poptorch.AsynchronousDataAccessor` to access - the dataset. If `DataLoaderMode.Sync`, accesses the dataset - synchronously. - async_options (Dict[str, Any], optional): Options to pass to - :py:class:`~poptorch.AsynchronousDataAccessor`. - rebatched_worker_size (int, optional): When using AsyncRebatched: batch - size of the tensors loaded by the workers. - Default to the combined batch size. - If specified the ``rebatched_worker_size`` must be less than - or equal to the combined batch size. - kwargs (Dict[str, Any], optional): Other options to pass to PyTorch's - ``DataLoader`` constructor. - """ - - def __init__(self, - dataset, - options, - batch_size=1, - shuffle=False, - num_workers=0, - drop_last=True, - persistent_workers=True, - auto_distributed_partitioning=True, - mode='sync', - async_options=None, - rebatched_worker_size=None, - **kwargs): - """Lazy init: - - In many frameworks, the dataloader will be constructed before the - initialization of the ipu options, so the lazy init method is used - here, and the real initialization will not be done until the dataloader - needs to be used and the options are input. - """ - # lazy init: sometimes, we cannot get IPU options when build data - # loader - self.kwargs = { - 'dataset': dataset, - 'batch_size': batch_size, - 'shuffle': shuffle, - 'num_workers': num_workers, - 'drop_last': drop_last, - 'persistent_workers': persistent_workers, - 'auto_distributed_partitioning': auto_distributed_partitioning, - 'mode': mode, - 'collate_fn': partial(collate, samples_per_gpu=batch_size), - 'async_options': async_options, - 'rebatched_worker_size': rebatched_worker_size, - **kwargs - } - self.dataset = dataset - self.initialized = False - if options: - self.init(options=options) - - def init(self, options, **kwargs): - if not self.initialized: - kwargs = {**self.kwargs, **kwargs, 'options': options} - if kwargs['mode'] == 'sync': - kwargs['mode'] = poptorch.DataLoaderMode.Sync - elif kwargs['mode'] == 'async': - kwargs['mode'] = poptorch.DataLoaderMode.AsyncRebatched - if kwargs['async_options'] is None: - kwargs['async_options'] = { - 'load_indefinitely': True, - 'buffer_size': 8 - } - if kwargs['rebatched_worker_size'] is None: - kwargs['rebatched_worker_size'] = 128 - super().__init__(**kwargs) - self.initialized = True - - return self diff --git a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/hierarchical_data_manager.py b/cv/super_resolution/liif/pytorch/mmcv/device/ipu/hierarchical_data_manager.py deleted file mode 100755 index a6f3b3cd2..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/hierarchical_data_manager.py +++ /dev/null @@ -1,243 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import numpy as np -import torch - -from mmcv.parallel import DataContainer - -# A customized None type for HierarchicalDataManager -HierarchicalDataNone = object() - - -class HierarchicalDataManager: - """A class manage all the tensors in the hierarchical data. - - At present, the input data structure accepted by IPU is limited, - when the input data structure of mmcv varies. - Here, an intermediate class is needed to get and update tensors - from the original data. - - HierarchicalDataManager will record a hierarchical input/output data in - self._hierarchical_data. For example, we have an input data: - {'img': tensorA, 'label': tensorB, 'img_metas': [tensorC, tensorD]} - To enable IPU to use the input, HierarchicalDataManager will collect - the torch tensors from self._hierarchical_data into a tuple like: - (tensorA, tensorB, tensorC, tensorD). - Meanwhile, the return of IPU is a tuple of tensors, HierarchicalDataManager - also have a function named update_all_tensors to update tensors in - self._hierarchical_data which is the output for upper calls. - - Args: - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - """ - - def __init__(self, logger=None): - self.atomic_types = (int, str, float, np.ndarray, type(None)) - self.warning = warnings.warn if logger is None else logger.warning - # enable or disable input data's shape and value check - self.quick_mode = False - self._hierarchical_data = None - - def quick(self): - self.quick_mode = True - - def compare_atomic_type(self, a, b): - """Compare data, supported datatypes are numpy array and python basic - types.""" - if isinstance(a, np.ndarray): - return np.all(a == b) - else: - return a == b - - def record_hierarchical_data(self, data): - """Record a hierarchical data.""" - if self._hierarchical_data is not None: - if isinstance(data, torch.Tensor): - assert isinstance(self._hierarchical_data, torch.Tensor), \ - 'original hierarchical data is not torch.tensor' - self._hierarchical_data = data - else: - self.update_hierarchical_data(data) - else: - self._hierarchical_data = data - - @property - def hierarchical_data(self): - return self._hierarchical_data - - def update_hierarchical_data(self, - dataA, - dataB=HierarchicalDataNone, - strict=True, - address='data'): - """Update dataB with dataA in-place. - - Args: - dataA (list or dict or tuple): New hierarchical data. - dataB (list or dict or tuple): hierarchical data to update. - if not specified, self.hierarchical_data will be updated then. - strict (bool, optional): If true, an error will be reported - when the following conditions occur: - 1. Non-torch.Tensor data changed. - 2. Torch.Tensor data shape changed. - address (str): Record the address of current data to be updated. - Default: 'data'. - """ - if dataB is HierarchicalDataNone: - dataB = self.hierarchical_data - - # Update with a da ta with the same structure - # but different values(tensors and basic python data types) - if isinstance(dataA, (tuple, list)): - for idx, node in enumerate(dataA): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(idx)}]' - assert isinstance(node, type(dataB[idx])),\ - f'data structure changed: {new_address}' - if isinstance(node, torch.Tensor): - dataB[idx] = node - else: - self.update_hierarchical_data( - node, dataB[idx], strict, address=new_address) - elif isinstance(dataA, dict): - for k, v in dataA.items(): - new_address = '' - if not self.quick_mode: - new_address = address + f'[{str(k)}]' - assert isinstance(v, type(dataB[k])),\ - f'data structure changed: {new_address}' - if isinstance(v, torch.Tensor): - dataB[k] = v - else: - self.update_hierarchical_data( - v, dataB[k], strict, address=new_address) - elif isinstance(dataA, self.atomic_types): - if not self.quick_mode: - is_equal = self.compare_atomic_type(dataA, dataB) - if not is_equal: - if strict: - raise ValueError( - 'all data except torch.Tensor should be same, ' - f'but data({address}) is changed.') - else: - self.warning( - f'find a non-torch.Tensor data({type(dataA)}) ' - f'changed, and the address is {address}') - elif isinstance(dataA, DataContainer): - if not self.quick_mode: - assert isinstance(dataB, DataContainer) - new_address = address + '.data' - self.update_hierarchical_data( - dataA.data, dataB.data, False, address=new_address) - else: - raise NotImplementedError( - f'not supported datatype:{type(dataA)}, address is {address}') - - def collect_all_tensors(self, hierarchical_data=None): - """Collect torch.Tensor data from self.hierarchical_data to a list and - return.""" - # get a list of tensor from self._hierarchical_data - if hierarchical_data is None: - hierarchical_data = self._hierarchical_data - tensors = [] - if isinstance(hierarchical_data, torch.Tensor): - tensors = [hierarchical_data] - else: - self._collect_tensors(hierarchical_data, tensors) - return tensors - - def _collect_tensors(self, data, tensors): - if isinstance(data, (tuple, list)): - for node in data: - if isinstance(node, torch.Tensor): - tensors.append(node) - else: - self._collect_tensors(node, tensors) - elif isinstance(data, dict): - for v in data.values(): - if isinstance(v, torch.Tensor): - tensors.append(v) - else: - self._collect_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._collect_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def update_all_tensors(self, tensors): - """Put tensors from tuple back to self.hierarchical_data.""" - if isinstance(self._hierarchical_data, torch.Tensor): - print(tensors, len(tensors)) - assert len(tensors) == 1 - assert isinstance(tensors[0], torch.Tensor) - self._hierarchical_data = tensors[0] - else: - # convert to list if tensors is tuple - tensors = list(tensors) - self._set_tensors(self._hierarchical_data, tensors) - return self.hierarchical_data - - def _set_tensors(self, data, tensors): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = tensors.pop(0) - else: - self._set_tensors(data[idx], tensors) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = tensors.pop(0) - else: - self._set_tensors(v, tensors) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._set_tensors(data.data, tensors) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') - - def clean_all_tensors(self): - """Delete tensors from self.hierarchical_data.""" - self._clean_tensors(self._hierarchical_data) - - def _clean_tensors(self, data): - if isinstance(data, tuple): - data = list(data) - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - data = tuple(data) - elif isinstance(data, list): - for idx in range(len(data)): - if isinstance(data[idx], torch.Tensor): - data[idx] = None - else: - self._clean_tensors(data[idx]) - elif isinstance(data, dict): - for k, v in data.items(): - if isinstance(v, torch.Tensor): - data[k] = None - else: - self._clean_tensors(v) - elif isinstance(data, self.atomic_types): - pass - elif isinstance(data, DataContainer): - self._clean_tensors(data.data) - else: - raise NotImplementedError(f'not supported datatype:{type(data)}') diff --git a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/hook_wrapper.py b/cv/super_resolution/liif/pytorch/mmcv/device/ipu/hook_wrapper.py deleted file mode 100755 index 141afb86d..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/hook_wrapper.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import HOOKS, LrUpdaterHook, OptimizerHook -from mmcv.utils import TORCH_VERSION, digit_version - - -def wrap_lr_updater_hook(lr_hook_class): - """A wrapper function to wrap any subclass of LrUpdaterHook. - - IPU needs extra operations to upload optimizer settings. This wrapper will - override function(_set_lr) of a subclass of LrUpdaterHook. - """ - assert issubclass(lr_hook_class, LrUpdaterHook) - - class ipu_lr_hook_class(lr_hook_class): - - def _set_lr(self, runner, *args, **kwargs): - super()._set_lr(runner, *args, **kwargs) - # convert torch optimizer to poptorch optimizer - runner.model.setOptimizer(runner.optimizer) - - return ipu_lr_hook_class - - -def wrap_optimizer_hook(optimizer_hook_class): - """A wrapper function to wrap OptimizerHook. - - This is an non-intrusive implementation of wrapping optimizer hook (or you - need to change every config file to use IPU optimizer hook) IPU's clip-norm - implementation is different from pytorch, so there should be an error - raised when using clip-norm. - """ - - class ipu_optimizer_hook_class(OptimizerHook): - - def __init__(self, **kwargs): - super().__init__(**kwargs) - if self.grad_clip is not None: - raise NotImplementedError('IPU does not support gradient clip') - - return ipu_optimizer_hook_class - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class IPUFp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - assert grad_clip is None,\ - 'IPU mode does not support `grad_clip` currently' - assert coalesce,\ - 'implemented all reduce in distributed training currently' - assert bucket_size_mb == -1,\ - '`bucket_size_mb` should not be set in IPU mode' - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - raise NotImplementedError( - 'IPU mode does not support dynamic loss scale currently') - elif isinstance(loss_scale, float): - self.loss_scale = loss_scale - elif isinstance(loss_scale, dict): - raise NotImplementedError( - 'IPU mode supports single scale currently') - else: - raise ValueError( - f'loss_scale should be float, but got {loss_scale} ') - - def after_train_iter(self, runner): - pass - -else: - raise RuntimeError('The IPU mode only supports torch 1.6 and above') diff --git a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/model_wrapper.py b/cv/super_resolution/liif/pytorch/mmcv/device/ipu/model_wrapper.py deleted file mode 100755 index c345537e2..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/model_wrapper.py +++ /dev/null @@ -1,721 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect -from collections import OrderedDict -from typing import Optional, Union - -import poptorch -import torch -import torch.nn as nn -from poptorch import PoplarExecutor, __version__, identity_loss -from poptorch._args_parser import ArgsParser - -from mmcv.runner import auto_fp16 -from .hierarchical_data_manager import HierarchicalDataManager -from .utils import compare_ndarray, model_sharding, recomputation_checkpoint - - -class DictArgsParser(ArgsParser): - """A helper class for handling model input. - - Args: - inputs (list): Inputs of model. - """ - - def __init__(self, inputs): - # Combine args and kwargs: - self._has_variadic_arguments = True - self._varnames = list(inputs.keys()) - self._defaults = [inspect.Parameter.empty for _ in self._varnames] - self._warned_not_contiguous_input = False - - -class WrappedNet(nn.Module): - """A net wrapper for model conversion. - - This wrapper will make some changes and add some extra functions to - training/inference model. - - Args: - model (:obj:`nn.Module`): The model to run. - inputs_manager (:obj:`HierarchicalDataManager`): A parser - converting inputs from tuple to dictionary. - outputs_manager (:obj:`HierarchicalDataManager`): A parser - converting outputs from dictionary to tuple. - inter_outputs_in_cpu (dict): Specify the features to be - recorded. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - model, - inputs_manager, - outputs_manager, - inter_outputs_in_cpu, - modules_to_record=None): - super().__init__() - self.model = model - self.inputs_manager = inputs_manager - self.outputs_manager = outputs_manager - self.training = model.training - # Register a hook function to capture the intermediate features - # generated by the network to align the outputs between ipu and cpu - # Used to confirm whether the implementation of CPU is consistent - # with the implementation of IPU - self.inter_outputs_in_cpu = inter_outputs_in_cpu - if modules_to_record is None: - modules_to_record = [] - - for idx, (name, module) in enumerate(model.named_modules()): - if name in modules_to_record or idx in modules_to_record: - features_hook = self.get_input_output_hook( - name, idx, self.inter_outputs_in_cpu) - module.register_forward_hook(hook=features_hook) - - def get_input_output_hook(self, name, idx, save_dict): - - def input_output_hook(module, fea_in, fea_out): - if isinstance(fea_in, tuple): - fea_in = list(fea_in) - if isinstance(fea_out, tuple): - fea_out = list(fea_out) - save_dict[name] = { - 'fea_in': fea_in, - 'fea_out': fea_out, - 'idx': idx - } - return None - - return input_output_hook - - def forward(self, inputs_tuple): - """This function is used to be compiled to ipu, the inputs and outputs - need to be tuples, so here we need to restore the input back to a - dictionary and convert the output to a tuple.""" - self.inputs_manager.update_all_tensors(inputs_tuple) - kwargs = {**(self.inputs_manager.hierarchical_data)} - if self.training: - outputs = self.forward_train(kwargs) - # tell poptorch which loss will be used finally - identity_loss(outputs['loss'], reduction='none') - else: - outputs = self.forward_eval(kwargs) - - if isinstance(outputs, torch.Tensor): - # currently not support single tensor output, - # need to wrap it with a dictionary, - # use a keyword to identify this case - outputs = {'output of WrappedNet: single tensor': outputs} - - # if there are some features need to be record, add extra outputs - for name in self.inter_outputs_in_cpu: - outputs[name] = self.inter_outputs_in_cpu[name] - - # record all the places of return tensors in the converting stage - # while in the real run stage, all the tensor are changed in-place - # that means the output can be obtained directly outside this function - self.outputs_manager.record_hierarchical_data(outputs) - plain_outputs = self.outputs_manager.collect_all_tensors() - return plain_outputs - - def forward_train(self, kwargs): - optimizer = kwargs.pop('optimizer') - outputs = self.train_step(kwargs, optimizer) - return outputs - - def train_step(self, data, optimizer=None, **kwargs): - """The iteration step during training. - - This method defines an iteration step during training, except for the - back propagation and optimizer updating, which are done in an optimizer - hook. Note that in some complicated cases or models, the whole process - including back propagation and optimizer updating are also defined in - this method, such as GAN. - - Args: - data (dict): The output of dataloader. - optimizer (:obj:`torch.optim.Optimizer`, optional): The - optimizer of runner is passed to ``train_step()``. This - argument is unused and reserved. - - Returns: - dict: Dict of outputs. The following fields are contained. - - loss (torch.Tensor): A tensor for back propagation, which \ - can be a weighted sum of multiple losses. - - log_vars (dict): Dict contains all the variables to be sent \ - to the logger. - - num_samples (int): Indicates the batch size (when the model \ - is DDP, it means the batch size on each GPU), which is \ - used for averaging the logs. - """ - losses = self.model(**data) - loss, log_vars = self._parse_losses(losses) - - outputs = dict( - loss=loss, log_vars=log_vars, num_samples=len(data['img'].data)) - - return outputs - - def _parse_losses(self, losses): - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(loss.mean() for loss in loss_value) - elif isinstance(loss_value, dict): - for name, value in loss_value.items(): - log_vars[name] = value - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(value for key, value in log_vars.items() if 'loss' in key) - log_vars['loss'] = loss - - return loss, log_vars - - def forward_eval(self, kwargs): - img = kwargs.pop('img') - img_metas = kwargs.pop('img_metas', None) - return_loss = kwargs.pop('return_loss') - assert not return_loss - # TODO Temporarily hard-code to close post_process, - # otherwise, in the third trace(_check_trace), - # post_process will convert output tensor to numpy array automatically, - # resulting in _check_trace failure - outputs = self.model( - img, - img_metas=img_metas, - return_loss=return_loss, - post_process=False) - return outputs - - -class MMPoplarExecutor(PoplarExecutor): - """An executor for inputs/outputs parsing, model compilation, data - alignment and IPU upload/download. - - Args: - model (:obj:`nn.Module`): The model to be compiled. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - training (bool): Model in training mode or eval mode. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - args (argument list): Arguments passed to the `__init__` - method of PoplarExecutor. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of PoplarExecutor. - """ - - def __init__(self, - model, - logger=None, - training=True, - modules_to_record=None, - *args, - **kwargs): - # self.model == self._user_model: input pytorch model - # self._model: wrapped model which is used to compile - # and update weights, these two models use same weights - # wrapped model only accept and output tuple, so - # HierarchicalDataManager will convert dictionary - # to tuple and convert them back - self.inputs_manager = HierarchicalDataManager(logger=logger) - self.outputs_manager = HierarchicalDataManager(logger=logger) - self.logger = logger - # the features calculated by CPU - self.inter_outputs_in_cpu = {} - # the features calculated by IPU - self.inter_outputs_in_ipu = {} - if modules_to_record is None: - # It is possible that the IPU implementation of some operators - # is inconsistent with the expected (CPU), here you can use - # this method to confirm whether there is a problem - self.compare_with_cpu = False - else: - self.compare_with_cpu = True - # move model.fp16_enabled to self.fp16_enabled, - # modify the position where the input is automatically casted to half - if getattr(model, 'fp16_enabled', False): - model.fp16_enabled = False - self.fp16_enabled = True - # make torch.jit.trace convert self._model - model = WrappedNet( - model, - self.inputs_manager, - self.outputs_manager, - self.inter_outputs_in_cpu, - modules_to_record=modules_to_record) - super().__init__(model, training=training, *args, **kwargs) - # overwrite self._args_parser in train_step or val_step - self._args_parser = None - if training: - assert self.training - else: - assert not self.training - - @property - def training(self): - # If trying to get the attribute(training) of self, - # since the class has no training attribute, - # it will automatically look for the training attribute of self.model. - # However, the real attribute we want to check is self._training, - # self.model.training and self._training are often inconsistent. - # It is not clear whether it is a Poptorch bug or a special design, - # temporarily use this function to fix the problem - return self._training # comes from self.model._training - - @auto_fp16(supported_types=(PoplarExecutor, )) - def run_model(self, data_dict): - # this function is used to parse input_dict - # and convert to output_dict - if self.isCompiled(): - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - else: - # get tensors out of data and put them in a tuple - self.inputs_manager.record_hierarchical_data(data_dict) - inputs_tuple = tuple(self.inputs_manager.collect_all_tensors()) - # turn logger in data manager off after compilation - self.inputs_manager.quick() - self.outputs_manager.quick() - - # parser args in the first iter - if self._args_parser is None: - self._args_parser = DictArgsParser({'args': inputs_tuple}) - - # run or convert model - # the plain_outputs will be used in converting stage - plain_outputs = self(inputs_tuple) - - self.inputs_manager.clean_all_tensors() - - # put list of tensors back to the output dict - # according to the same order - self.outputs_manager.update_all_tensors(plain_outputs) - # get the real output dictionary from self.outputs_manager - output_dict = self.outputs_manager.hierarchical_data - - # split output_dict into inter_outputs_in_ipu - # and output of the torch model - torch_model_output = {} - for name in output_dict: - if name in self.inter_outputs_in_cpu: - self.inter_outputs_in_ipu[name] = output_dict[name] - else: - torch_model_output[name] = output_dict[name] - - if 'output of WrappedNet: single tensor' in output_dict: - assert len(torch_model_output) == 1 - assert isinstance( - torch_model_output['output of WrappedNet: single tensor'], - torch.Tensor) - torch_model_output = \ - torch_model_output['output of WrappedNet: single tensor'] - - return torch_model_output - - def train_step(self, data, optimizer=None, **kwargs): - # arguments from mmcls/models/classifiers/base.py: - # BaseClassifier.train_step - assert self.training - assert len(kwargs) == 0 # TODO, support later if necessary - - # TODO support datacontainer as input - # currently, auto_fp16 and HierarchicalDataManager take too much - # time on traversing datacontainer - data['img_metas'] = None - num_samples = len(data['img'].data) - - # TODO we will ignore optimizer because it will not be used in model, - # support later if necessary - data['optimizer'] = None - output_dict = self.run_model(data) - - # outputs contained loss, log_vars, num_samples, - # only loss(torch.tensor) has been updated - # remove all unchanged vars, left torch.tensor - neat_output_dict = {'loss': output_dict['loss']} - - # re-parse outputs, get back log_vars and num_samples - loss, log_vars = self.model._parse_losses(neat_output_dict) - final_output_dict = dict( - loss=loss, log_vars=log_vars, num_samples=num_samples) - return final_output_dict - - def eval_call(self, img, img_metas=None, return_loss=True, **kwargs): - # arguments from mmdet/models/detectors/base.py:BaseDetector.forward - # tmp usssage for eval mode - assert not self.training - assert len(kwargs) == 0 # TODO, support later if necessary - assert not return_loss - data = {'img': img, 'img_metas': img_metas, 'return_loss': return_loss} - - output_dict = self.run_model(data) - - return output_dict - - def detachFromDevice(self): - if self.isCompiled() and self._is_attached: - super().detachFromDevice() - - def attachToDevice(self): - if self.isCompiled() and not self._is_attached: - super().attachToDevice() - - -class TrainEvalModel: - """A class maintaining training MMPoplarExecutor and inference - MMPoplarExecutor. - - Args: - train_model (:obj:`nn.Module`): The training model to be compiled. - ``train_model`` can be None if only executing validation. - eval_model (:obj:`nn.Module`): The inference model to be compiled. - options (mmcv.Config, dict): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - """ - - def __init__(self, - train_model, - eval_model, - options, - optimizer, - modules_to_record=None, - logger=None): - if train_model is None: - self._train_executor = None - self.training = False - else: - self._train_executor = get_training_model( - train_model, - options=options['training'], - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - self.training = True - self._eval_executor = get_inference_model( - eval_model, options=options['inference'], logger=logger) - - @property - def executor(self): - if self.training: - return self._train_executor - else: - return self._eval_executor - - def train(self, mode: bool = True): - """Sets the module in training mode. - - This has any effect only on certain modules. See documentations of - particular modules for details of their behaviors in - training/evaluation mode, if they are affected, - e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - Args: - mode (bool): whether to set training mode (``True``) or evaluation - mode (``False``). Default: ``True``. - - Returns: - Module: self - """ - if not isinstance(mode, bool): - raise ValueError('training mode is expected to be boolean, ' - f'but got {type(mode)}') - if self._train_executor is None and mode: - raise RuntimeError( - 'The train_executor is not initialized.' - 'If you want to initialize train_executor,' - 'you need to input optimizer when converting pytorch model') - - if mode == self.training: - self.model.train(mode) - return self - else: - if self.isCompiled(): - # copy weights from IPU to cpu before off-load current session - self.copyWeightsToHost() - # detach the current session before change the mode, - # if is training mode and weights are updated, - # poptorch will copy weights from IPU to host - self.detachFromDevice() - - self.training = mode # session will changed with mode changing - self.model.train(mode) - - # after changing mode, attach the current new session, - # and this function will copy weights of model to device - self.attachToDevice() - return self - - def eval(self): - """Sets the module in evaluation mode. - - This has any effect only on certain modules. - See documentations of particular modules - for details of their behaviors in training/evaluation mode, - if they are affected, e.g. :class:`Dropout`, :class:`BatchNorm`, etc. - - This is equivalent with :meth:`self.train(False) - `. - - See :ref:`locally-disable-grad-doc` for a comparison between - `.eval()` and several similar mechanisms that may be confused with it. - - Returns: - Module: self - """ - return self.train(False) - - def compare_data_between_ipu_and_cpu(self, inter_outputs_in_cpu, - inter_outputs_in_ipu): - for key, val in inter_outputs_in_cpu.items(): - is_tensor = isinstance(val['fea_in'], torch.Tensor) - fea_in_cpu = val['fea_in'] - fea_in_cpu_list = [fea_in_cpu] if is_tensor else fea_in_cpu - fea_in_ipu = inter_outputs_in_ipu[key]['fea_in'] - fea_in_ipu_list = [fea_in_ipu] if is_tensor else fea_in_ipu - - is_tensor = isinstance(val['fea_out'], torch.Tensor) - fea_out_cpu = val['fea_out'] - fea_out_cpu_list = [fea_out_cpu] if is_tensor else fea_out_cpu - fea_out_ipu = inter_outputs_in_ipu[key]['fea_out'] - fea_out_ipu_list = [fea_out_ipu] if is_tensor else fea_out_ipu - - print('comparing layer:', key) - for idx, (featA, featB) in \ - enumerate(zip(fea_in_cpu_list, fea_in_ipu_list)): - print('fea_in, tensor ', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - for idx, (featA, featB) in \ - enumerate(zip(fea_out_cpu_list, fea_out_ipu_list)): - print('fea_out, tensor', idx) - compare_ndarray(featA.detach().numpy(), featB.detach().numpy()) - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def train_step(self, data, optimizer=None, **kwargs): - assert self.training, 'not supported train_step on eval mode' - inter_outputs_in_cpu = {} - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu): - self.copyWeightsToHost() - # run in CPU mode - self._train_executor.model.train_step(data, optimizer, **kwargs) - inter_outputs_in_cpu = { - **(self._train_executor.inter_outputs_in_cpu) - } - # run in IPU mode - result = self._train_executor.train_step(data, optimizer, **kwargs) - if (self._train_executor.isCompiled() - and self._train_executor.compare_with_cpu - and len(inter_outputs_in_cpu) > 0): - self.compare_data_between_ipu_and_cpu( - inter_outputs_in_cpu, - self._train_executor.inter_outputs_in_ipu) - return result - - # TODO Unified training and eval interface, - # merge train_step(train) and __call__(eval) together - def __call__(self, *args, **kwargs): - if self.training: - raise NotImplementedError('use train_step rather than __call__') - else: - return self._eval_executor.eval_call(*args, **kwargs) - - def __getattr__(self, attr): - return getattr(self.executor, attr) - - -def get_training_model(model: nn.Module, - options: Optional[poptorch.Options] = None, - optimizer: Optional[torch.optim.Optimizer] = None, - logger=None, - modules_to_record=None) -> poptorch.PoplarExecutor: - """Create a PopTorch training model from a PyTorch model, running on IPU - hardware in training mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned training model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.train()`` on the original model, which - changes the ``training`` bool of the model instance, will not alter the - model returned by this function. You may need to call ``model.train()`` - on your model before you call this function for correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - optimizer (:obj:`torch.optim.Optimizer`, optional): The optimizers - to apply during training. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place - of ``model``. - """ - # Create a copy of the original model in case it needs to be wrapped - maybe_wrapped_model = copy.copy(model) - - return MMPoplarExecutor( - model=maybe_wrapped_model, - logger=logger, - options=options, - training=True, - optimizer=optimizer, - user_model=model, - modules_to_record=modules_to_record, - poptorch_version=__version__) - - -def get_inference_model(model: Union[nn.Module, poptorch.PoplarExecutor], - options: Optional[poptorch.Options] = None, - logger=None) -> poptorch.PoplarExecutor: - """Create a PopTorch inference model from a PyTorch model, running on IPU - hardware in inference mode. - - Note: - PopTorch makes a shallow copy of the model. Changes to the - parameters in the returned inference model affect the original model - and vice versa. However, primitive variable types are not synced: for - example calling ``model.eval()`` on the original model will not alter - the model returned by this function. You may need to call - ``model.eval()`` on your model before you call this function for - correct behavior. - - Args: - model (:obj:`nn.Module`): The model to run. - options (poptorch.Options): Options that will be used to compile - and run the model. - logger (:obj:`logging.Logger`): Logger used during running. - Defaults to None. - - Returns: - The :class:`poptorch.PoplarExecutor` wrapper to use in place of - ``model``. - """ - - return MMPoplarExecutor( - model=copy.copy(model), - logger=logger, - options=options, - training=False, - poptorch_version=__version__) - - -def ipu_model_wrapper(model, - options, - optimizer=None, - logger=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None): - """Convert torch model to IPU model. - - Args: - model (nn.Module): The target model to be converted. - options (dict[str, poptorch.Options]): IPU options, generated - by :func:`cfg2options`. - optimizer (:obj:`torch.optim.Optimizer`, optional): torch - optimizer, necessary if in training mode - logger (:obj:`logging.Logger`): Logger used during training. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (dict): A dictionary contains train_split_edges and - train_ckpt_nodes, See details in :func:`model_sharding` and - :func:`recomputation_checkpoint` functions. - fp16_cfg (dict): Config for IPU fp16 training. Currently supports - configs: `loss_scale`, `velocity_accum_type` and `accum_type`. - See details in - https://docs.graphcore.ai/projects/poptorch-user-guide/en/latest/index.html - - Returns: - TrainEvalModel: IPU wrapped model. - """ - if ipu_model_cfg is None: - ipu_model_cfg = {} - training = model.training if optimizer is not None else False - # set mixed-precision - if fp16_cfg is not None: - from mmcv.runner import wrap_fp16_model - loss_scale = fp16_cfg['loss_scale'] - wrap_fp16_model(model) - model.half() - # TODO tmp ussage to set loss scaling for torch original optimizer - if optimizer is not None: - optimizer.loss_scaling = loss_scale - if fp16_cfg.get('velocity_accum_type', False): - if fp16_cfg['velocity_accum_type'] == 'half': - optimizer.velocity_accum_type = torch.half - else: - optimizer.velocity_accum_type = torch.float32 - if fp16_cfg.get('accum_type', False): - if fp16_cfg['accum_type'] == 'half': - optimizer.accum_type = torch.half - else: - optimizer.accum_type = torch.float32 - # TODO support feature alignment for fp16 - if modules_to_record is not None: - raise NotImplementedError( - 'Feature alignment for fp16 is not implemented') - - # set model partition - if optimizer is None: - train_model = None - else: - # split model into multi-IPUs if specified - train_model = model_sharding( - copy.copy(model).train(), - ipu_model_cfg.get('train_split_edges', [])) - - recomputation_checkpoint(train_model, - ipu_model_cfg.get('train_ckpt_nodes', [])) - - # TODO support feature alignment for gradient accumulation mode - gradient_accumulation = \ - getattr(options['training'].Training, 'gradient_accumulation', 1) - if gradient_accumulation > 1: - assert modules_to_record is None, \ - 'Feature alignment for grad-accumulation mode not implemented' - - # TODO support feature alignment for multi-replica mode - replication_factor = \ - getattr(options['training'], 'replication_factor', 1) - if replication_factor > 1: - assert modules_to_record is None, \ - 'Feature alignment for multi-replica mode not implemented' - - # TODO supports different model partitions between train and eval mode - assert len(ipu_model_cfg.get('eval_split_edges', [])) == 0,\ - 'Currently, BeginBlock can only be used once on the same model' - eval_model = copy.copy(model).eval() - - # wrap model for compilation - model = TrainEvalModel( - train_model, - eval_model, - options=options, - optimizer=optimizer, - logger=logger, - modules_to_record=modules_to_record) - model.train(training) - return model diff --git a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/runner.py b/cv/super_resolution/liif/pytorch/mmcv/device/ipu/runner.py deleted file mode 100755 index e2d492267..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/runner.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.runner import (HOOKS, RUNNERS, BaseRunner, EpochBasedRunner, - IterBasedRunner) -from mmcv.utils import IS_IPU_AVAILABLE - -if IS_IPU_AVAILABLE: - from .dataloader import IPUDataLoader - from .hook_wrapper import (IPUFp16OptimizerHook, wrap_lr_updater_hook, - wrap_optimizer_hook) - from .model_wrapper import ipu_model_wrapper - from .utils import build_from_cfg_with_wrapper, cfg2options - - -class IPUBaseRunner(BaseRunner): - """A base runner for IPU. - - This runner has some extra processes for IPU which are shown below: - - 1. Parse options for IPU - 2. wrap pytorch model for IPU - 3. Raise errors while encountering illegal usage - 4. Input IPU options and initialize dataloader if finding an instance - of IPUDataLoader - - Args: - model (:obj:`nn.Module`): The model to run. - options_cfg (mmcv.Config, dict): Options that will be used to compile - and run the model. - modules_to_record (mmcv.Config, list): Index or name of modules which - will be recorded for output. It is necessary to specify output for - static graph of model training or inference. - ipu_model_cfg (mmcv.Config, dict): Config of model partition and - recomputing checkpoint - fp16_cfg (mmcv.Config): Config for fp16 training. - batch_processor (callable): A callable method that process a data - batch. Should be None for IPU runner - kwargs (Dict[str, Any], optional): Keyword arguments will be passed to - ``base_runner.BaseRunner``. - """ - - def __init__(self, - model, - options_cfg=None, - modules_to_record=None, - ipu_model_cfg=None, - fp16_cfg=None, - batch_processor=None, - **kwargs): - assert hasattr(model, 'train_step') and batch_processor is None,\ - 'only support model with train_step' - - if options_cfg is None: - options_cfg = {} - # call BaseRunner.__init__() here - super().__init__(model, **kwargs) - - # process options of ipu - if IS_IPU_AVAILABLE: - self.options = cfg2options(options_cfg) - self.model = ipu_model_wrapper( - self.model, - self.options, - self.optimizer, - self.logger, - modules_to_record=modules_to_record, - ipu_model_cfg=ipu_model_cfg, - fp16_cfg=fp16_cfg) - else: - raise NotImplementedError('cpu mode on IPURunner is not supported') - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - assert isinstance(lr_config, dict) - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, - # e.g., 'cyclic', then its first letter will be capitalized, - # e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, the string will not be changed - # if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = build_from_cfg_with_wrapper(lr_config, HOOKS, - wrap_lr_updater_hook) - self.register_hook(hook, priority='VERY_HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - assert isinstance(optimizer_config, (dict, IPUFp16OptimizerHook)) - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = build_from_cfg_with_wrapper(optimizer_config, HOOKS, - wrap_optimizer_hook) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def run(self, data_loaders, workflow, *args, **kwargs): - for i, flow in enumerate(workflow): - mode, _ = flow - # initialize IPU dataloader if not initialized - assert isinstance(data_loaders[i], IPUDataLoader),\ - 'IPU runner can only work with `IPUDataLoader`' - data_loaders[i].init(options=self.get_options(mode)) - - super().run(data_loaders, workflow, *args, **kwargs) - - def get_options(self, mode): - if mode == 'train': - return self.options['training'] - elif mode == 'val': - return self.options['inference'] - else: - raise ValueError(f'mode should be train or val but got {mode}') - - -@RUNNERS.register_module() -class IPUEpochBasedRunner(IPUBaseRunner, EpochBasedRunner): - """Epoch-based Runner for IPU. - - The Inheritance order(MRO) is: IPUEpochBasedRunner -> IPUBaseRunner -> - EpochBasedRunner -> BaseRunner This runner train models epoch by epoch. - """ - pass - - -@RUNNERS.register_module() -class IPUIterBasedRunner(IPUBaseRunner, IterBasedRunner): - """Iteration-based Runner for IPU. - - The Inheritance order(MRO) is: IPUIterBasedRunner -> IPUBaseRunner -> - IterBasedRunner -> BaseRunner This runner train models iteration by - iteration. - """ - pass diff --git a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/utils.py b/cv/super_resolution/liif/pytorch/mmcv/device/ipu/utils.py deleted file mode 100755 index 79709db1e..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/device/ipu/utils.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import numpy as np -import popart -import poptorch -import torch -import torch.nn as nn - -from mmcv.utils import Registry - - -def _options_assigner(cfg, options_node): - # set popart.options by config - # cfg: dict, python data type - # options_node: python module or function - if isinstance(cfg, dict): - for key in cfg: - _options_assigner(cfg[key], getattr(options_node, key)) - elif isinstance(cfg, (int, float, str, list)): - if callable(options_node): - options_node(cfg) - else: - error_msg = f'options_node type {type(options_node)} not supported' - raise NotImplementedError(error_msg) - else: - error_msg = f'cfg type {type(cfg)} not supported' - raise NotImplementedError(error_msg) - - -def cfg2options(cfg): - """Parse dictionary to ipu options. - - Args: - cfg (dict): A dictionary of ipu settings. - - Returns: - dict[str, poptorch.Options]: Training options and inference options - of IPU. - """ - # set ipu options for inference and training by config - train_cfg = cfg.pop('train_cfg', {}) - eval_cfg = cfg.pop('eval_cfg', {}) - eval_cfg['replicationFactor'] = 1 # eval mode only use one replica - eval_cfg['executionStrategy'] = 'ShardedExecution' - # overwrite default ipu cfg with specified train cfgs - training_ipu_cfg = {**cfg, **train_cfg} - # overwrite default ipu cfg with specified eval cfgs - inference_ipu_cfg = {**cfg, **eval_cfg} - - ipu_options = { - 'training': _cast_to_options(training_ipu_cfg), - 'inference': _cast_to_options(inference_ipu_cfg) - } - - # TODO configure these codes - ipu_options['training']._Popart.set('disableGradAccumulationTensorStreams', - True) - ipu_options['training']._Popart.set( - 'accumulateOuterFragmentSettings.schedule', - int(popart.AccumulateOuterFragmentSchedule.OverlapMemoryOptimized)) - ipu_options['training'].Precision.enableStochasticRounding(True) - - return ipu_options - - -def _cast_to_options(cfg): - # If it cannot be directly assigned, use if statement to parse it, - # and if it can be directly assigned, use _options_assigner to assign - options = poptorch.Options() - - if 'availableMemoryProportion' in cfg: - available_memory_proportion = cfg.pop('availableMemoryProportion') - mem_props = {} - for i, mem_prop in enumerate(available_memory_proportion): - mem_props[f'IPU{i}'] = mem_prop - options.setAvailableMemoryProportion(mem_props) - - if 'executionStrategy' in cfg: - execution_strategy = cfg.pop('executionStrategy') - if execution_strategy == 'SameAsIpu': - options.setExecutionStrategy( - poptorch.PipelinedExecution( - getattr(poptorch.AutoStage, execution_strategy))) - elif execution_strategy == 'ShardedExecution': - options.setExecutionStrategy(poptorch.ShardedExecution()) - else: - raise NotImplementedError( - 'executionStrategy should be "SameAsIpu" or "ShardedExecution"' - f', but got {execution_strategy}') - - if 'partialsType' in cfg: - partials_type = cfg.pop('partialsType') - options.Precision.setPartialsType(getattr( - torch, partials_type)) # half or float - - _options_assigner(cfg, options) - return options - - -def model_sharding(model, split_edges): - """split models in-place into multi-IPUs. - - Args: - model (nn.Module): The target model to be split. - split_edges (list of dict): Model layer names or layer numbers - of split edge. Each item of ``split_edges`` is a dictionary, - which may contain the following key-pairs: - - - layer_to_call: PyTorch module to assign to the block - - user_id (optional): A user defined identifier for the block. - - ipu_id: The id of the IPU to run on. - - Examples: - >>> split_edges = [ - ... dict(layer_to_call='model.conv1', ipu_id=0), - ... dict(layer_to_call='model.conv3', ipu_id=1)] - >>> sharding_model = model_sharding(torch_model, split_edges) - - Returns: - nn.Module: Split model. - """ - if len(split_edges) == 0: - return model - assert isinstance(split_edges, list) - spilt_edges_dict = {edge['layer_to_call']: edge for edge in split_edges} - - for idx, (name, module) in enumerate(model.named_modules()): - if idx in spilt_edges_dict and name in spilt_edges_dict: - raise ValueError( - 'The same layer is referenced twice while doing model' - f' partition: idx is {idx} and name is {name}') - - edge = spilt_edges_dict.pop(name, None) - edge = spilt_edges_dict.pop(idx, edge) - if edge is not None: - poptorch.BeginBlock(module, edge.get('user_id', name), - edge['ipu_id']) - - # ensure all split_edges are used - if len(spilt_edges_dict) > 0: - split_edge_names = list(spilt_edges_dict.keys()) - raise RuntimeError( - f'split_edges: {split_edge_names} are not contained in the model') - return model - - -def recomputation_checkpoint(model: nn.Module, module_names: list): - """Annotates the output of a module to be checkpointed instead of - recomputed. - - If recomputation mode is enabled, ipu will release the activations of - the middle layers to save memory. During the backward of gradient, - the activation of the middle layer will be recalculated again. - This function is used to declare the activations of some intermediate - layers that need to be saved in order to skip the recomputation of - some layers. - - Args: - model (nn.Module): The target model to apply recomputation - checkpoint. - module_names (list): Layer names of module. - """ - - def recompute_outputs(module, inputs, outputs): - if isinstance(outputs, tuple): - return tuple(poptorch.recomputationCheckpoint(y) for y in outputs) - else: - return poptorch.recomputationCheckpoint(outputs) - - for name, module in model.named_modules(): - if name in module_names: - module.register_forward_hook(recompute_outputs) - module_names.remove(name) - - # check all module_names are used - assert len(module_names) == 0,\ - f'recomputed nodes: {module_names} are not contained in the model' - - -def compare_ndarray(featA, featB, rtol=1e-3, atol=1e-5): - """Align data between two activations or weights.""" - try: - np.testing.assert_allclose(featA, featB, rtol=rtol, atol=atol) - except AssertionError as e: - print(e) - - -def build_from_cfg_with_wrapper(cfg, - registry, - wrapper_func=None, - default_args=None): - """Build a module from config dict and wrap module with "wrapper_func". - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - wrapper_func (function): Used to wrap class - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - - if wrapper_func is None: - wrapped_obj_cls = obj_cls - else: - wrapped_obj_cls = wrapper_func(obj_cls) - try: - return wrapped_obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{wrapped_obj_cls.__name__}: {e}') diff --git a/cv/super_resolution/liif/pytorch/mmcv/device/mlu/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/device/mlu/__init__.py deleted file mode 100644 index 572c4da7e..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/device/mlu/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .data_parallel import MLUDataParallel -from .distributed import MLUDistributedDataParallel -from .scatter_gather import scatter, scatter_kwargs - -__all__ = [ - 'MLUDataParallel', 'MLUDistributedDataParallel', 'scatter', - 'scatter_kwargs' -] diff --git a/cv/super_resolution/liif/pytorch/mmcv/device/mlu/_functions.py b/cv/super_resolution/liif/pytorch/mmcv/device/mlu/_functions.py deleted file mode 100644 index 75660fa9b..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/device/mlu/_functions.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from typing import List, Union - -import torch - - -def scatter(input: Union[List, torch.Tensor], devices: List) -> List: - """scatter copies tensor to MLU directly.""" - if isinstance(input, list): - outputs = [scatter(_input, devices) for _input in input] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - return output.to('mlu') if devices != [-1] else output - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_mlus, input): - outputs = scatter(input, target_mlus) - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/super_resolution/liif/pytorch/mmcv/device/mlu/data_parallel.py b/cv/super_resolution/liif/pytorch/mmcv/device/mlu/data_parallel.py deleted file mode 100644 index ebe14c0a5..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/device/mlu/data_parallel.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -import torch - -from mmcv.parallel import MMDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDataParallel(MMDataParallel): - """The MLUDataParallel module that supports DataContainer. - - MLUDataParallel is a class inherited from MMDataParall, which supports - MLU training and inference only. - - The main differences with MMDataParallel: - - - It only supports single-card of MLU, and only use first card to - run training and inference. - - - It uses direct host-to-device copy instead of stream-background - scatter. - - .. warning:: - MLUDataParallel only supports single MLU training, if you need to - train with multiple MLUs, please use MLUDistributedDataParallel - instead. If you have multiple MLUs, you can set the environment - variable ``MLU_VISIBLE_DEVICES=0`` (or any other card number(s)) - to specify the running device. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super().__init__(*args, dim=dim, **kwargs) - self.device_ids = [0] - self.src_device_obj = torch.device('mlu:0') - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/super_resolution/liif/pytorch/mmcv/device/mlu/distributed.py b/cv/super_resolution/liif/pytorch/mmcv/device/mlu/distributed.py deleted file mode 100644 index 3768c754c..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/device/mlu/distributed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from mmcv.parallel import MMDistributedDataParallel -from .scatter_gather import scatter_kwargs - - -class MLUDistributedDataParallel(MMDistributedDataParallel): - """The DDP module supports DataContainer. - - MLUDDP has one difference from MMDDP which moves data to MLU with coping - instead of scattering. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) diff --git a/cv/super_resolution/liif/pytorch/mmcv/device/mlu/scatter_gather.py b/cv/super_resolution/liif/pytorch/mmcv/device/mlu/scatter_gather.py deleted file mode 100644 index 0b0c9b96f..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/device/mlu/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from mmcv.parallel.data_container import DataContainer -from ._functions import Scatter - - -def scatter(inputs, target_mlus, dim=0): - """Scatter inputs to target mlu. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_mlus != [-1]: - obj = obj.to('mlu') - return [obj] - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_mlus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_mlus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_mlus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_mlus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_mlus, dim) if inputs else [] - kwargs = scatter(kwargs, target_mlus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/super_resolution/liif/pytorch/mmcv/engine/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/engine/__init__.py deleted file mode 100644 index 3193b7f66..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, - single_gpu_test) - -__all__ = [ - 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', - 'single_gpu_test' -] diff --git a/cv/super_resolution/liif/pytorch/mmcv/engine/test.py b/cv/super_resolution/liif/pytorch/mmcv/engine/test.py deleted file mode 100644 index 83546caec..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/engine/test.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time -from typing import Optional - -import torch -import torch.distributed as dist -import torch.nn as nn -from torch.utils.data import DataLoader - -import mmcv -from mmcv.runner import get_dist_info - - -def single_gpu_test(model: nn.Module, data_loader: DataLoader) -> list: - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - # Assume result has the same length of batch_size - # refer to https://github.com/open-mmlab/mmcv/issues/985 - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model: nn.Module, - data_loader: DataLoader, - tmpdir: Optional[str] = None, - gpu_collect: bool = False) -> Optional[list]: - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting - ``gpu_collect=True``, it encodes results to gpu tensors and use gpu - communication for results collection. On cpu mode it saves the results on - different gpus to ``tmpdir`` and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - if rank == 0: - batch_size = len(result) - batch_size_all = batch_size * world_size - if batch_size_all + prog_bar.completed > len(dataset): - batch_size_all = len(dataset) - prog_bar.completed - for _ in range(batch_size_all): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - result_from_ranks = collect_results_gpu(results, len(dataset)) - else: - result_from_ranks = collect_results_cpu(results, len(dataset), tmpdir) - return result_from_ranks - - -def collect_results_cpu(result_part: list, - size: int, - tmpdir: Optional[str] = None) -> Optional[list]: - """Collect results under cpu mode. - - On cpu mode, this function will save the results on different gpus to - ``tmpdir`` and collect them by the rank 0 worker. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - tmpdir (str | None): temporal directory for collected results to - store. If set to None, it will create a random temporal directory - for it. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - part_file = osp.join(tmpdir, f'part_{rank}.pkl') # type: ignore - mmcv.dump(result_part, part_file) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') # type: ignore - part_result = mmcv.load(part_file) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) # type: ignore - return ordered_results - - -def collect_results_gpu(result_part: list, size: int) -> Optional[list]: - """Collect results under gpu mode. - - On gpu mode, this function will encode results to gpu tensors and use gpu - communication for results collection. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results - else: - return None diff --git a/cv/super_resolution/liif/pytorch/mmcv/fileio/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/fileio/__init__.py deleted file mode 100644 index 2051b85f7..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/fileio/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .file_client import BaseStorageBackend, FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler -from .io import dump, load, register_handler -from .parse import dict_from_file, list_from_file - -__all__ = [ - 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', - 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', - 'list_from_file', 'dict_from_file' -] diff --git a/cv/super_resolution/liif/pytorch/mmcv/fileio/file_client.py b/cv/super_resolution/liif/pytorch/mmcv/fileio/file_client.py deleted file mode 100644 index ee7c3164e..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/fileio/file_client.py +++ /dev/null @@ -1,1173 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import os -import os.path as osp -import re -import tempfile -import warnings -from abc import ABCMeta, abstractmethod -from contextlib import contextmanager -from pathlib import Path -from typing import Any, Generator, Iterator, Optional, Tuple, Union -from urllib.request import urlopen - -import mmcv -from mmcv.utils.misc import has_method -from mmcv.utils.path import is_filepath - - -class BaseStorageBackend(metaclass=ABCMeta): - """Abstract class of storage backends. - - All backends need to implement two apis: ``get()`` and ``get_text()``. - ``get()`` reads the file as a byte stream and ``get_text()`` reads the file - as texts. - """ - - # a flag to indicate whether the backend can create a symlink for a file - _allow_symlink = False - - @property - def name(self): - return self.__class__.__name__ - - @property - def allow_symlink(self): - return self._allow_symlink - - @abstractmethod - def get(self, filepath): - pass - - @abstractmethod - def get_text(self, filepath): - pass - - -class CephBackend(BaseStorageBackend): - """Ceph storage backend (for internal use). - - Args: - path_mapping (dict|None): path mapping dict from local path to Petrel - path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` - will be replaced by ``dst``. Default: None. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - """ - - def __init__(self, path_mapping=None): - try: - import ceph - except ImportError: - raise ImportError('Please install ceph to enable CephBackend.') - - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - self._client = ceph.S3Client() - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def get(self, filepath): - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class PetrelBackend(BaseStorageBackend): - """Petrel storage backend (for internal use). - - PetrelBackend supports reading and writing data to multiple clusters. - If the file path contains the cluster name, PetrelBackend will read data - from specified cluster or write data to it. Otherwise, PetrelBackend will - access the default cluster. - - Args: - path_mapping (dict, optional): Path mapping dict from local path to - Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in - ``filepath`` will be replaced by ``dst``. Default: None. - enable_mc (bool, optional): Whether to enable memcached support. - Default: True. - - Examples: - >>> filepath1 = 's3://path/of/file' - >>> filepath2 = 'cluster-name:s3://path/of/file' - >>> client = PetrelBackend() - >>> client.get(filepath1) # get data from default cluster - >>> client.get(filepath2) # get data from 'cluster-name' cluster - """ - - def __init__(self, - path_mapping: Optional[dict] = None, - enable_mc: bool = True): - try: - from petrel_client import client - except ImportError: - raise ImportError('Please install petrel_client to enable ' - 'PetrelBackend.') - - self._client = client.Client(enable_mc=enable_mc) - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def _map_path(self, filepath: Union[str, Path]) -> str: - """Map ``filepath`` to a string path whose prefix will be replaced by - :attr:`self.path_mapping`. - - Args: - filepath (str): Path to be mapped. - """ - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - return filepath - - def _format_path(self, filepath: str) -> str: - """Convert a ``filepath`` to standard format of petrel oss. - - If the ``filepath`` is concatenated by ``os.path.join``, in a Windows - environment, the ``filepath`` will be the format of - 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the - above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. - - Args: - filepath (str): Path to be formatted. - """ - return re.sub(r'\\+', '/', filepath) - - def get(self, filepath: Union[str, Path]) -> memoryview: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - memoryview: A memory view of expected bytes object to avoid - copying. The memoryview object can be converted to bytes by - ``value_buf.tobytes()``. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return str(self.get(filepath), encoding=encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Save data to a given ``filepath``. - - Args: - obj (bytes): Data to be saved. - filepath (str or Path): Path to write data. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.put(filepath, obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Save data to a given ``filepath``. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to encode the ``obj``. - Default: 'utf-8'. - """ - self.put(bytes(obj, encoding=encoding), filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - if not has_method(self._client, 'delete'): - raise NotImplementedError( - 'Current version of Petrel Python SDK has not supported ' - 'the `delete` method, please use a higher version or dev' - ' branch instead.') - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.delete(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - if not (has_method(self._client, 'contains') - and has_method(self._client, 'isdir')): - raise NotImplementedError( - 'Current version of Petrel Python SDK has not supported ' - 'the `contains` and `isdir` methods, please use a higher' - 'version or dev branch instead.') - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) or self._client.isdir(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - if not has_method(self._client, 'isdir'): - raise NotImplementedError( - 'Current version of Petrel Python SDK has not supported ' - 'the `isdir` method, please use a higher version or dev' - ' branch instead.') - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - if not has_method(self._client, 'contains'): - raise NotImplementedError( - 'Current version of Petrel Python SDK has not supported ' - 'the `contains` method, please use a higher version or ' - 'dev branch instead.') - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result after concatenation. - """ - filepath = self._format_path(self._map_path(filepath)) - if filepath.endswith('/'): - filepath = filepath[:-1] - formatted_paths = [filepath] - for path in filepaths: - formatted_paths.append(self._format_path(self._map_path(path))) - return '/'.join(formatted_paths) - - @contextmanager - def get_local_path( - self, - filepath: Union[str, - Path]) -> Generator[Union[str, Path], None, None]: - """Download a file from ``filepath`` and return a temporary path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str | Path): Download a file from ``filepath``. - - Examples: - >>> client = PetrelBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('s3://path/of/your/file') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one temporary path. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - assert self.isfile(filepath) - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - Petrel has no concept of directories but it simulates the directory - hierarchy in the filesystem through public prefixes. In addition, - if the returned path ends with '/', it means the path is a public - prefix which is a logical directory. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - In addition, the returned path of directory will not contains the - suffix '/' which is consistent with other backends. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if not has_method(self._client, 'list'): - raise NotImplementedError( - 'Current version of Petrel Python SDK has not supported ' - 'the `list` method, please use a higher version or dev' - ' branch instead.') - - dir_path = self._map_path(dir_path) - dir_path = self._format_path(dir_path) - if list_dir and suffix is not None: - raise TypeError( - '`list_dir` should be False when `suffix` is not None') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - # Petrel's simulated directory hierarchy assumes that directory paths - # should end with `/` - if not dir_path.endswith('/'): - dir_path += '/' - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for path in self._client.list(dir_path): - # the `self.isdir` is not used here to determine whether path - # is a directory, because `self.isdir` relies on - # `self._client.list` - if path.endswith('/'): # a directory path - next_dir_path = self.join_path(dir_path, path) - if list_dir: - # get the relative path and exclude the last - # character '/' - rel_dir = next_dir_path[len(root):-1] - yield rel_dir - if recursive: - yield from _list_dir_or_file(next_dir_path, list_dir, - list_file, suffix, - recursive) - else: # a file path - absolute_path = self.join_path(dir_path, path) - rel_path = absolute_path[len(root):] - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class MemcachedBackend(BaseStorageBackend): - """Memcached storage backend. - - Attributes: - server_list_cfg (str): Config file for memcached server list. - client_cfg (str): Config file for memcached client. - sys_path (str | None): Additional path to be appended to `sys.path`. - Default: None. - """ - - def __init__(self, server_list_cfg, client_cfg, sys_path=None): - if sys_path is not None: - import sys - sys.path.append(sys_path) - try: - import mc - except ImportError: - raise ImportError( - 'Please install memcached to enable MemcachedBackend.') - - self.server_list_cfg = server_list_cfg - self.client_cfg = client_cfg - self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, - self.client_cfg) - # mc.pyvector servers as a point which points to a memory cache - self._mc_buffer = mc.pyvector() - - def get(self, filepath): - filepath = str(filepath) - import mc - self._client.Get(filepath, self._mc_buffer) - value_buf = mc.ConvertBuffer(self._mc_buffer) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class LmdbBackend(BaseStorageBackend): - """Lmdb storage backend. - - Args: - db_path (str): Lmdb database path. - readonly (bool, optional): Lmdb environment parameter. If True, - disallow any write operations. Default: True. - lock (bool, optional): Lmdb environment parameter. If False, when - concurrent access occurs, do not lock the database. Default: False. - readahead (bool, optional): Lmdb environment parameter. If False, - disable the OS filesystem readahead mechanism, which may improve - random read performance when a database is larger than RAM. - Default: False. - - Attributes: - db_path (str): Lmdb database path. - """ - - def __init__(self, - db_path, - readonly=True, - lock=False, - readahead=False, - **kwargs): - try: - import lmdb # NOQA - except ImportError: - raise ImportError('Please install lmdb to enable LmdbBackend.') - - self.db_path = str(db_path) - self.readonly = readonly - self.lock = lock - self.readahead = readahead - self.kwargs = kwargs - self._client = None - - def get(self, filepath): - """Get values according to the filepath. - - Args: - filepath (str | obj:`Path`): Here, filepath is the lmdb key. - """ - if self._client is None: - self._client = self._get_client() - - with self._client.begin(write=False) as txn: - value_buf = txn.get(str(filepath).encode('utf-8')) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - def _get_client(self): - import lmdb - - return lmdb.open( - self.db_path, - readonly=self.readonly, - lock=self.lock, - readahead=self.readahead, - **self.kwargs) - - def __del__(self): - self._client.close() - - -class HardDiskBackend(BaseStorageBackend): - """Raw hard disks storage backend.""" - - _allow_symlink = True - - def get(self, filepath: Union[str, Path]) -> bytes: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes: Expected bytes object. - """ - with open(filepath, 'rb') as f: - value_buf = f.read() - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - with open(filepath, encoding=encoding) as f: - value_buf = f.read() - return value_buf - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` will create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'wb') as f: - f.write(obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` will create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'w', encoding=encoding) as f: - f.write(obj) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - os.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return osp.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return osp.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return osp.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return osp.join(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, - filepath: Union[str, - Path]) -> Generator[Union[str, Path], None, None]: - """Only for unified API and do nothing.""" - yield filepath - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if list_dir and suffix is not None: - raise TypeError('`suffix` should be None when `list_dir` is True') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - elif osp.isdir(entry.path): - if list_dir: - rel_dir = osp.relpath(entry.path, root) - yield rel_dir - if recursive: - yield from _list_dir_or_file(entry.path, list_dir, - list_file, suffix, - recursive) - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class HTTPBackend(BaseStorageBackend): - """HTTP and HTTPS storage bachend.""" - - def get(self, filepath): - value_buf = urlopen(filepath).read() - return value_buf - - def get_text(self, filepath, encoding='utf-8'): - value_buf = urlopen(filepath).read() - return value_buf.decode(encoding) - - @contextmanager - def get_local_path( - self, filepath: str) -> Generator[Union[str, Path], None, None]: - """Download a file from ``filepath``. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str): Download a file from ``filepath``. - - Examples: - >>> client = HTTPBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('http://path/of/your/file') as path: - ... # do something here - """ - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - -class FileClient: - """A general file client to access files in different backends. - - The client loads a file or text in a specified backend from its path - and returns it as a binary or text file. There are two ways to choose a - backend, the name of backend and the prefix of path. Although both of them - can be used to choose a storage backend, ``backend`` has a higher priority - that is if they are all set, the storage backend will be chosen by the - backend argument. If they are all `None`, the disk backend will be chosen. - Note that It can also register other backend accessor with a given name, - prefixes, and backend class. In addition, We use the singleton pattern to - avoid repeated object creation. If the arguments are the same, the same - object will be returned. - - Args: - backend (str, optional): The storage backend type. Options are "disk", - "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. - prefix (str, optional): The prefix of the registered storage backend. - Options are "s3", "http", "https". Default: None. - - Examples: - >>> # only set backend - >>> file_client = FileClient(backend='petrel') - >>> # only set prefix - >>> file_client = FileClient(prefix='s3') - >>> # set both backend and prefix but use backend to choose client - >>> file_client = FileClient(backend='petrel', prefix='s3') - >>> # if the arguments are the same, the same object is returned - >>> file_client1 = FileClient(backend='petrel') - >>> file_client1 is file_client - True - - Attributes: - client (:obj:`BaseStorageBackend`): The backend object. - """ - - _backends = { - 'disk': HardDiskBackend, - 'ceph': CephBackend, - 'memcached': MemcachedBackend, - 'lmdb': LmdbBackend, - 'petrel': PetrelBackend, - 'http': HTTPBackend, - } - - _prefix_to_backends = { - 's3': PetrelBackend, - 'http': HTTPBackend, - 'https': HTTPBackend, - } - - _instances: dict = {} - - client: Any - - def __new__(cls, backend=None, prefix=None, **kwargs): - if backend is None and prefix is None: - backend = 'disk' - if backend is not None and backend not in cls._backends: - raise ValueError( - f'Backend {backend} is not supported. Currently supported ones' - f' are {list(cls._backends.keys())}') - if prefix is not None and prefix not in cls._prefix_to_backends: - raise ValueError( - f'prefix {prefix} is not supported. Currently supported ones ' - f'are {list(cls._prefix_to_backends.keys())}') - - # concatenate the arguments to a unique key for determining whether - # objects with the same arguments were created - arg_key = f'{backend}:{prefix}' - for key, value in kwargs.items(): - arg_key += f':{key}:{value}' - - if arg_key in cls._instances: - _instance = cls._instances[arg_key] - else: - # create a new object and put it to _instance - _instance = super().__new__(cls) - if backend is not None: - _instance.client = cls._backends[backend](**kwargs) - else: - _instance.client = cls._prefix_to_backends[prefix](**kwargs) - - cls._instances[arg_key] = _instance - - return _instance - - @property - def name(self): - return self.client.name - - @property - def allow_symlink(self): - return self.client.allow_symlink - - @staticmethod - def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: - """Parse the prefix of a uri. - - Args: - uri (str | Path): Uri to be parsed that contains the file prefix. - - Examples: - >>> FileClient.parse_uri_prefix('s3://path/of/your/file') - 's3' - - Returns: - str | None: Return the prefix of uri if the uri contains '://' else - ``None``. - """ - assert is_filepath(uri) - uri = str(uri) - if '://' not in uri: - return None - else: - prefix, _ = uri.split('://') - # In the case of PetrelBackend, the prefix may contains the cluster - # name like clusterName:s3 - if ':' in prefix: - _, prefix = prefix.split(':') - return prefix - - @classmethod - def infer_client(cls, - file_client_args: Optional[dict] = None, - uri: Optional[Union[str, Path]] = None) -> 'FileClient': - """Infer a suitable file client based on the URI and arguments. - - Args: - file_client_args (dict, optional): Arguments to instantiate a - FileClient. Default: None. - uri (str | Path, optional): Uri to be parsed that contains the file - prefix. Default: None. - - Examples: - >>> uri = 's3://path/of/your/file' - >>> file_client = FileClient.infer_client(uri=uri) - >>> file_client_args = {'backend': 'petrel'} - >>> file_client = FileClient.infer_client(file_client_args) - - Returns: - FileClient: Instantiated FileClient object. - """ - assert file_client_args is not None or uri is not None - if file_client_args is None: - file_prefix = cls.parse_uri_prefix(uri) # type: ignore - return cls(prefix=file_prefix) - else: - return cls(**file_client_args) - - @classmethod - def _register_backend(cls, name, backend, force=False, prefixes=None): - if not isinstance(name, str): - raise TypeError('the backend name should be a string, ' - f'but got {type(name)}') - if not inspect.isclass(backend): - raise TypeError( - f'backend should be a class but got {type(backend)}') - if not issubclass(backend, BaseStorageBackend): - raise TypeError( - f'backend {backend} is not a subclass of BaseStorageBackend') - if not force and name in cls._backends: - raise KeyError( - f'{name} is already registered as a storage backend, ' - 'add "force=True" if you want to override it') - - if name in cls._backends and force: - for arg_key, instance in list(cls._instances.items()): - if isinstance(instance.client, cls._backends[name]): - cls._instances.pop(arg_key) - cls._backends[name] = backend - - if prefixes is not None: - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if prefix not in cls._prefix_to_backends: - cls._prefix_to_backends[prefix] = backend - elif (prefix in cls._prefix_to_backends) and force: - overridden_backend = cls._prefix_to_backends[prefix] - if isinstance(overridden_backend, list): - overridden_backend = tuple(overridden_backend) - for arg_key, instance in list(cls._instances.items()): - if isinstance(instance.client, overridden_backend): - cls._instances.pop(arg_key) - cls._prefix_to_backends[prefix] = backend - else: - raise KeyError( - f'{prefix} is already registered as a storage backend,' - ' add "force=True" if you want to override it') - - @classmethod - def register_backend(cls, name, backend=None, force=False, prefixes=None): - """Register a backend to FileClient. - - This method can be used as a normal class method or a decorator. - - .. code-block:: python - - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - FileClient.register_backend('new', NewBackend) - - or - - .. code-block:: python - - @FileClient.register_backend('new') - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - Args: - name (str): The name of the registered backend. - backend (class, optional): The backend class to be registered, - which must be a subclass of :class:`BaseStorageBackend`. - When this method is used as a decorator, backend is None. - Defaults to None. - force (bool, optional): Whether to override the backend if the name - has already been registered. Defaults to False. - prefixes (str or list[str] or tuple[str], optional): The prefixes - of the registered storage backend. Default: None. - `New in version 1.3.15.` - """ - if backend is not None: - cls._register_backend( - name, backend, force=force, prefixes=prefixes) - return - - def _register(backend_cls): - cls._register_backend( - name, backend_cls, force=force, prefixes=prefixes) - return backend_cls - - return _register - - def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: - """Read data from a given ``filepath`` with 'rb' mode. - - Note: - There are two types of return values for ``get``, one is ``bytes`` - and the other is ``memoryview``. The advantage of using memoryview - is that you can avoid copying, and if you want to convert it to - ``bytes``, you can use ``.tobytes()``. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes | memoryview: Expected bytes object or a memory view of the - bytes object. - """ - return self.client.get(filepath) - - def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return self.client.get_text(filepath, encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` should create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - self.client.put(obj, filepath) - - def put_text(self, obj: str, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` should create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str, optional): The encoding format used to open the - `filepath`. Default: 'utf-8'. - """ - self.client.put_text(obj, filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str, Path): Path to be removed. - """ - self.client.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return self.client.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return self.client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return self.client.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return self.client.join_path(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, - filepath: Union[str, - Path]) -> Generator[Union[str, Path], None, None]: - """Download data from ``filepath`` and write the data to local path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Note: - If the ``filepath`` is a local path, just return itself. - - .. warning:: - ``get_local_path`` is an experimental interface that may change in - the future. - - Args: - filepath (str or Path): Path to be read data. - - Examples: - >>> file_client = FileClient(prefix='s3') - >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one path. - """ - with self.client.get_local_path(str(filepath)) as local_path: - yield local_path - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, - suffix, recursive) diff --git a/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/__init__.py deleted file mode 100644 index aa24d9197..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseFileHandler -from .json_handler import JsonHandler -from .pickle_handler import PickleHandler -from .yaml_handler import YamlHandler - -__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/base.py b/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/base.py deleted file mode 100644 index 0c9cc15b6..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseFileHandler(metaclass=ABCMeta): - # `str_like` is a flag to indicate whether the type of file object is - # str-like object or bytes-like object. Pickle only processes bytes-like - # objects but json only processes str-like object. If it is str-like - # object, `StringIO` will be used to process the buffer. - str_like = True - - @abstractmethod - def load_from_fileobj(self, file, **kwargs): - pass - - @abstractmethod - def dump_to_fileobj(self, obj, file, **kwargs): - pass - - @abstractmethod - def dump_to_str(self, obj, **kwargs): - pass - - def load_from_path(self, filepath: str, mode: str = 'r', **kwargs): - with open(filepath, mode) as f: - return self.load_from_fileobj(f, **kwargs) - - def dump_to_path(self, obj, filepath: str, mode: str = 'w', **kwargs): - with open(filepath, mode) as f: - self.dump_to_fileobj(obj, f, **kwargs) diff --git a/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/json_handler.py b/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/json_handler.py deleted file mode 100644 index 18d4f15f7..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/json_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -import numpy as np - -from .base import BaseFileHandler - - -def set_default(obj): - """Set default json values for non-serializable values. - - It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. - It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, - etc.) into plain numbers of plain python built-in types. - """ - if isinstance(obj, (set, range)): - return list(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, np.generic): - return obj.item() - raise TypeError(f'{type(obj)} is unsupported for json dump') - - -class JsonHandler(BaseFileHandler): - - def load_from_fileobj(self, file): - return json.load(file) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('default', set_default) - json.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('default', set_default) - return json.dumps(obj, **kwargs) diff --git a/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/pickle_handler.py b/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/pickle_handler.py deleted file mode 100644 index 073856fd2..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/pickle_handler.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import pickle - -from .base import BaseFileHandler - - -class PickleHandler(BaseFileHandler): - - str_like = False - - def load_from_fileobj(self, file, **kwargs): - return pickle.load(file, **kwargs) - - def load_from_path(self, filepath, **kwargs): - return super().load_from_path(filepath, mode='rb', **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('protocol', 2) - return pickle.dumps(obj, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('protocol', 2) - pickle.dump(obj, file, **kwargs) - - def dump_to_path(self, obj, filepath, **kwargs): - super().dump_to_path(obj, filepath, mode='wb', **kwargs) diff --git a/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/yaml_handler.py b/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/yaml_handler.py deleted file mode 100644 index 1c1b07794..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/fileio/handlers/yaml_handler.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import yaml - -try: - from yaml import CDumper as Dumper - from yaml import CLoader as Loader -except ImportError: - from yaml import Loader, Dumper # type: ignore - -from .base import BaseFileHandler # isort:skip - - -class YamlHandler(BaseFileHandler): - - def load_from_fileobj(self, file, **kwargs): - kwargs.setdefault('Loader', Loader) - return yaml.load(file, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('Dumper', Dumper) - yaml.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('Dumper', Dumper) - return yaml.dump(obj, **kwargs) diff --git a/cv/super_resolution/liif/pytorch/mmcv/fileio/io.py b/cv/super_resolution/liif/pytorch/mmcv/fileio/io.py deleted file mode 100644 index 91192103c..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/fileio/io.py +++ /dev/null @@ -1,163 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from io import BytesIO, StringIO -from pathlib import Path -from typing import Any, Callable, Dict, List, Optional, TextIO, Union - -from ..utils import is_list_of -from .file_client import FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler - -FileLikeObject = Union[TextIO, StringIO, BytesIO] - -file_handlers = { - 'json': JsonHandler(), - 'yaml': YamlHandler(), - 'yml': YamlHandler(), - 'pickle': PickleHandler(), - 'pkl': PickleHandler() -} - - -def load(file: Union[str, Path, FileLikeObject], - file_format: Optional[str] = None, - file_client_args: Optional[Dict] = None, - **kwargs): - """Load data from json/yaml/pickle files. - - This method provides a unified api for loading data from serialized files. - - Note: - In v1.3.16 and later, ``load`` supports loading data from serialized - files those can be storaged in different backends. - - Args: - file (str or :obj:`Path` or file-like object): Filename or a file-like - object. - file_format (str, optional): If not specified, the file format will be - inferred from the file extension, otherwise use the specified one. - Currently supported formats include "json", "yaml/yml" and - "pickle/pkl". - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> load('/path/of/your/file') # file is storaged in disk - >>> load('https://path/of/your/file') # file is storaged in Internet - >>> load('s3://path/of/your/file') # file is storaged in petrel - - Returns: - The content from the file. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None and isinstance(file, str): - file_format = file.split('.')[-1] - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - f: FileLikeObject - if isinstance(file, str): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO(file_client.get_text(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - else: - with BytesIO(file_client.get(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - elif hasattr(file, 'read'): - obj = handler.load_from_fileobj(file, **kwargs) - else: - raise TypeError('"file" must be a filepath str or a file-object') - return obj - - -def dump(obj: Any, - file: Optional[Union[str, Path, FileLikeObject]] = None, - file_format: Optional[str] = None, - file_client_args: Optional[Dict] = None, - **kwargs): - """Dump data to json/yaml/pickle strings or files. - - This method provides a unified api for dumping data as strings or to files, - and also supports custom arguments for each file format. - - Note: - In v1.3.16 and later, ``dump`` supports dumping data as strings or to - files which is saved to different backends. - - Args: - obj (any): The python object to be dumped. - file (str or :obj:`Path` or file-like object, optional): If not - specified, then the object is dumped to a str, otherwise to a file - specified by the filename or file-like object. - file_format (str, optional): Same as :func:`load`. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dump('hello world', '/path/of/your/file') # disk - >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel - - Returns: - bool: True for success, False otherwise. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None: - if isinstance(file, str): - file_format = file.split('.')[-1] - elif file is None: - raise ValueError( - 'file_format must be specified since file is None') - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - f: FileLikeObject - handler = file_handlers[file_format] - if file is None: - return handler.dump_to_str(obj, **kwargs) - elif isinstance(file, str): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put_text(f.getvalue(), file) - else: - with BytesIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put(f.getvalue(), file) - elif hasattr(file, 'write'): - handler.dump_to_fileobj(obj, file, **kwargs) - else: - raise TypeError('"file" must be a filename str or a file-object') - - -def _register_handler(handler: BaseFileHandler, - file_formats: Union[str, List[str]]) -> None: - """Register a handler for some file extensions. - - Args: - handler (:obj:`BaseFileHandler`): Handler to be registered. - file_formats (str or list[str]): File formats to be handled by this - handler. - """ - if not isinstance(handler, BaseFileHandler): - raise TypeError( - f'handler must be a child of BaseFileHandler, not {type(handler)}') - if isinstance(file_formats, str): - file_formats = [file_formats] - if not is_list_of(file_formats, str): - raise TypeError('file_formats must be a str or a list of str') - for ext in file_formats: - file_handlers[ext] = handler - - -def register_handler(file_formats: Union[str, list], **kwargs) -> Callable: - - def wrap(cls): - _register_handler(cls(**kwargs), file_formats) - return cls - - return wrap diff --git a/cv/super_resolution/liif/pytorch/mmcv/fileio/parse.py b/cv/super_resolution/liif/pytorch/mmcv/fileio/parse.py deleted file mode 100644 index f28e59119..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/fileio/parse.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from io import StringIO -from pathlib import Path -from typing import Dict, List, Optional, Union - -from .file_client import FileClient - - -def list_from_file(filename: Union[str, Path], - prefix: str = '', - offset: int = 0, - max_num: int = 0, - encoding: str = 'utf-8', - file_client_args: Optional[Dict] = None) -> List: - """Load a text file and parse the content as a list of strings. - - Note: - In v1.3.16 and later, ``list_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a list for strings. - - Args: - filename (str): Filename. - prefix (str): The prefix to be inserted to the beginning of each item. - offset (int): The offset of lines. - max_num (int): The maximum number of lines to be read, - zeros and negatives mean no limitation. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> list_from_file('/path/of/your/file') # disk - ['hello', 'world'] - >>> list_from_file('s3://path/of/your/file') # ceph or petrel - ['hello', 'world'] - - Returns: - list[str]: A list of strings. - """ - cnt = 0 - item_list = [] - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for _ in range(offset): - f.readline() - for line in f: - if 0 < max_num <= cnt: - break - item_list.append(prefix + line.rstrip('\n\r')) - cnt += 1 - return item_list - - -def dict_from_file(filename: Union[str, Path], - key_type: type = str, - encoding: str = 'utf-8', - file_client_args: Optional[Dict] = None) -> Dict: - """Load a text file and parse the content as a dict. - - Each line of the text file will be two or more columns split by - whitespaces or tabs. The first column will be parsed as dict keys, and - the following columns will be parsed as dict values. - - Note: - In v1.3.16 and later, ``dict_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a dict. - - Args: - filename(str): Filename. - key_type(type): Type of the dict keys. str is user by default and - type conversion will be performed if specified. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dict_from_file('/path/of/your/file') # disk - {'key1': 'value1', 'key2': 'value2'} - >>> dict_from_file('s3://path/of/your/file') # ceph or petrel - {'key1': 'value1', 'key2': 'value2'} - - Returns: - dict: The parsed contents. - """ - mapping = {} - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for line in f: - items = line.rstrip('\n').split() - assert len(items) >= 2 - key = key_type(items[0]) - val = items[1:] if len(items) > 2 else items[1] - mapping[key] = val - return mapping diff --git a/cv/super_resolution/liif/pytorch/mmcv/image/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/image/__init__.py deleted file mode 100644 index 92ecec404..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/image/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, - gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, - rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) -from .geometric import (cutout, imcrop, imflip, imflip_, impad, - impad_to_multiple, imrescale, imresize, imresize_like, - imresize_to_multiple, imrotate, imshear, imtranslate, - rescale_size) -from .io import imfrombytes, imread, imwrite, supported_backends, use_backend -from .misc import tensor2imgs -from .photometric import (adjust_brightness, adjust_color, adjust_contrast, - adjust_hue, adjust_lighting, adjust_sharpness, - auto_contrast, clahe, imdenormalize, imequalize, - iminvert, imnormalize, imnormalize_, lut_transform, - posterize, solarize) - -__all__ = [ - 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', - 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', - 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', - 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', - 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', - 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', - 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', - 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', - 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', - 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting', - 'adjust_hue' -] diff --git a/cv/super_resolution/liif/pytorch/mmcv/image/colorspace.py b/cv/super_resolution/liif/pytorch/mmcv/image/colorspace.py deleted file mode 100644 index 08f995240..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/image/colorspace.py +++ /dev/null @@ -1,309 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from typing import Callable, Union - -import cv2 -import numpy as np - - -def imconvert(img: np.ndarray, src: str, dst: str) -> np.ndarray: - """Convert an image from the src colorspace to dst colorspace. - - Args: - img (ndarray): The input image. - src (str): The source colorspace, e.g., 'rgb', 'hsv'. - dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. - - Returns: - ndarray: The converted image. - """ - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - out_img = cv2.cvtColor(img, code) - return out_img - - -def bgr2gray(img: np.ndarray, keepdim: bool = False) -> np.ndarray: - """Convert a BGR image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def rgb2gray(img: np.ndarray, keepdim: bool = False) -> np.ndarray: - """Convert a RGB image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def gray2bgr(img: np.ndarray) -> np.ndarray: - """Convert a grayscale image to BGR image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted BGR image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return out_img - - -def gray2rgb(img: np.ndarray) -> np.ndarray: - """Convert a grayscale image to RGB image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted RGB image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) - return out_img - - -def _convert_input_type_range(img: np.ndarray) -> np.ndarray: - """Convert the type and range of the input image. - - It converts the input image to np.float32 type and range of [0, 1]. - It is mainly used for pre-processing the input image in colorspace - conversion functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - (ndarray): The converted image with type of np.float32 and range of - [0, 1]. - """ - img_type = img.dtype - img = img.astype(np.float32) - if img_type == np.float32: - pass - elif img_type == np.uint8: - img /= 255. - else: - raise TypeError('The img type should be np.float32 or np.uint8, ' - f'but got {img_type}') - return img - - -def _convert_output_type_range( - img: np.ndarray, dst_type: Union[np.uint8, np.float32]) -> np.ndarray: - """Convert the type and range of the image according to dst_type. - - It converts the image to desired type and range. If `dst_type` is np.uint8, - images will be converted to np.uint8 type with range [0, 255]. If - `dst_type` is np.float32, it converts the image to np.float32 type with - range [0, 1]. - It is mainly used for post-processing images in colorspace conversion - functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The image to be converted with np.float32 type and - range [0, 255]. - dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it - converts the image to np.uint8 type with range [0, 255]. If - dst_type is np.float32, it converts the image to np.float32 type - with range [0, 1]. - - Returns: - (ndarray): The converted image with desired type and range. - """ - if dst_type not in (np.uint8, np.float32): - raise TypeError('The dst_type should be np.float32 or np.uint8, ' - f'but got {dst_type}') - if dst_type == np.uint8: - img = img.round() - else: - img /= 255. - return img.astype(dst_type) - - -def rgb2ycbcr(img: np.ndarray, y_only: bool = False) -> np.ndarray: - """Convert a RGB image to YCbCr image. - - This function produces the same results as Matlab's `rgb2ycbcr` function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 - else: - out_img = np.matmul( - img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], - [24.966, 112.0, -18.214]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def bgr2ycbcr(img: np.ndarray, y_only: bool = False) -> np.ndarray: - """Convert a BGR image to YCbCr image. - - The bgr version of rgb2ycbcr. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 - else: - out_img = np.matmul( - img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], - [65.481, -37.797, 112.0]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2rgb(img: np.ndarray) -> np.ndarray: - """Convert a YCbCr image to RGB image. - - This function produces the same results as Matlab's ycbcr2rgb function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted RGB image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0, -0.00153632, 0.00791071], - [0.00625893, -0.00318811, 0]]) * 255.0 + [ - -222.921, 135.576, -276.836 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2bgr(img: np.ndarray) -> np.ndarray: - """Convert a YCbCr image to BGR image. - - The bgr version of ycbcr2rgb. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted BGR image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0.00791071, -0.00153632, 0], - [0, -0.00318811, 0.00625893]]) * 255.0 + [ - -276.836, 135.576, -222.921 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def convert_color_factory(src: str, dst: str) -> Callable: - - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - - def convert_color(img: np.ndarray) -> np.ndarray: - out_img = cv2.cvtColor(img, code) - return out_img - - convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} - image. - - Args: - img (ndarray or str): The input image. - - Returns: - ndarray: The converted {dst.upper()} image. - """ - - return convert_color - - -bgr2rgb = convert_color_factory('bgr', 'rgb') - -rgb2bgr = convert_color_factory('rgb', 'bgr') - -bgr2hsv = convert_color_factory('bgr', 'hsv') - -hsv2bgr = convert_color_factory('hsv', 'bgr') - -bgr2hls = convert_color_factory('bgr', 'hls') - -hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/cv/super_resolution/liif/pytorch/mmcv/image/geometric.py b/cv/super_resolution/liif/pytorch/mmcv/image/geometric.py deleted file mode 100644 index eecd795ea..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/image/geometric.py +++ /dev/null @@ -1,741 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers - -import cv2 -import numpy as np - -from ..utils import to_2tuple -from .io import imread_backend - -try: - from PIL import Image -except ImportError: - Image = None - - -def _scale_size(size, scale): - """Rescale a size by a ratio. - - Args: - size (tuple[int]): (w, h). - scale (float | tuple(float)): Scaling factor. - - Returns: - tuple[int]: scaled size. - """ - if isinstance(scale, (float, int)): - scale = (scale, scale) - w, h = size - return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) - - -cv2_interp_codes = { - 'nearest': cv2.INTER_NEAREST, - 'bilinear': cv2.INTER_LINEAR, - 'bicubic': cv2.INTER_CUBIC, - 'area': cv2.INTER_AREA, - 'lanczos': cv2.INTER_LANCZOS4 -} - -# Pillow >=v9.1.0 use a slightly different naming scheme for filters. -# Set pillow_interp_codes according to the naming scheme used. -if Image is not None: - if hasattr(Image, 'Resampling'): - pillow_interp_codes = { - 'nearest': Image.Resampling.NEAREST, - 'bilinear': Image.Resampling.BILINEAR, - 'bicubic': Image.Resampling.BICUBIC, - 'box': Image.Resampling.BOX, - 'lanczos': Image.Resampling.LANCZOS, - 'hamming': Image.Resampling.HAMMING - } - else: - pillow_interp_codes = { - 'nearest': Image.NEAREST, - 'bilinear': Image.BILINEAR, - 'bicubic': Image.BICUBIC, - 'box': Image.BOX, - 'lanczos': Image.LANCZOS, - 'hamming': Image.HAMMING - } - - -def imresize(img, - size, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image to a given size. - - Args: - img (ndarray): The input image. - size (tuple[int]): Target size (w, h). - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if backend is None: - backend = imread_backend - if backend not in ['cv2', 'pillow']: - raise ValueError(f'backend: {backend} is not supported for resize.' - f"Supported backends are 'cv2', 'pillow'") - - if backend == 'pillow': - assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' - pil_image = Image.fromarray(img) - pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) - resized_img = np.array(pil_image) - else: - resized_img = cv2.resize( - img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) - if not return_scale: - return resized_img - else: - w_scale = size[0] / w - h_scale = size[1] / h - return resized_img, w_scale, h_scale - - -def imresize_to_multiple(img, - divisor, - size=None, - scale_factor=None, - keep_ratio=False, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image according to a given size or scale factor and then rounds - up the the resized or rescaled image size to the nearest value that can be - divided by the divisor. - - Args: - img (ndarray): The input image. - divisor (int | tuple): Resized image size will be a multiple of - divisor. If divisor is a tuple, divisor should be - (w_divisor, h_divisor). - size (None | int | tuple[int]): Target size (w, h). Default: None. - scale_factor (None | float | tuple[float]): Multiplier for spatial - size. Should match input size if it is a tuple and the 2D style is - (w_scale_factor, h_scale_factor). Default: None. - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: False. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if size is not None and scale_factor is not None: - raise ValueError('only one of size or scale_factor should be defined') - elif size is None and scale_factor is None: - raise ValueError('one of size or scale_factor should be defined') - elif size is not None: - size = to_2tuple(size) - if keep_ratio: - size = rescale_size((w, h), size, return_scale=False) - else: - size = _scale_size((w, h), scale_factor) - - divisor = to_2tuple(divisor) - size = tuple(int(np.ceil(s / d)) * d for s, d in zip(size, divisor)) - resized_img, w_scale, h_scale = imresize( - img, - size, - return_scale=True, - interpolation=interpolation, - out=out, - backend=backend) - if return_scale: - return resized_img, w_scale, h_scale - else: - return resized_img - - -def imresize_like(img, - dst_img, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image to the same size of a given image. - - Args: - img (ndarray): The input image. - dst_img (ndarray): The target image. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = dst_img.shape[:2] - return imresize(img, (w, h), return_scale, interpolation, backend=backend) - - -def rescale_size(old_size, scale, return_scale=False): - """Calculate the new size to be rescaled to. - - Args: - old_size (tuple[int]): The old size (w, h) of image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image size. - - Returns: - tuple[int]: The new rescaled image size. - """ - w, h = old_size - if isinstance(scale, (float, int)): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - scale_factor = scale - elif isinstance(scale, tuple): - max_long_edge = max(scale) - max_short_edge = min(scale) - scale_factor = min(max_long_edge / max(h, w), - max_short_edge / min(h, w)) - else: - raise TypeError( - f'Scale must be a number or tuple of int, but got {type(scale)}') - - new_size = _scale_size((w, h), scale_factor) - - if return_scale: - return new_size, scale_factor - else: - return new_size - - -def imrescale(img, - scale, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image while keeping the aspect ratio. - - Args: - img (ndarray): The input image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - ndarray: The rescaled image. - """ - h, w = img.shape[:2] - new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) - rescaled_img = imresize( - img, new_size, interpolation=interpolation, backend=backend) - if return_scale: - return rescaled_img, scale_factor - else: - return rescaled_img - - -def imflip(img, direction='horizontal'): - """Flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image. - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return np.flip(img, axis=1) - elif direction == 'vertical': - return np.flip(img, axis=0) - else: - return np.flip(img, axis=(0, 1)) - - -def imflip_(img, direction='horizontal'): - """Inplace flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image (inplace). - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return cv2.flip(img, 1, img) - elif direction == 'vertical': - return cv2.flip(img, 0, img) - else: - return cv2.flip(img, -1, img) - - -def imrotate(img, - angle, - center=None, - scale=1.0, - border_value=0, - interpolation='bilinear', - auto_bound=False): - """Rotate an image. - - Args: - img (ndarray): Image to be rotated. - angle (float): Rotation angle in degrees, positive values mean - clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. - scale (float): Isotropic scale factor. - border_value (int): Border value. - interpolation (str): Same as :func:`resize`. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. - - Returns: - ndarray: The rotated image. - """ - if center is not None and auto_bound: - raise ValueError('`auto_bound` conflicts with `center`') - h, w = img.shape[:2] - if center is None: - center = ((w - 1) * 0.5, (h - 1) * 0.5) - assert isinstance(center, tuple) - - matrix = cv2.getRotationMatrix2D(center, -angle, scale) - if auto_bound: - cos = np.abs(matrix[0, 0]) - sin = np.abs(matrix[0, 1]) - new_w = h * sin + w * cos - new_h = h * cos + w * sin - matrix[0, 2] += (new_w - w) * 0.5 - matrix[1, 2] += (new_h - h) * 0.5 - w = int(np.round(new_w)) - h = int(np.round(new_h)) - rotated = cv2.warpAffine( - img, - matrix, (w, h), - flags=cv2_interp_codes[interpolation], - borderValue=border_value) - return rotated - - -def bbox_clip(bboxes, img_shape): - """Clip bboxes to fit the image shape. - - Args: - bboxes (ndarray): Shape (..., 4*k) - img_shape (tuple[int]): (height, width) of the image. - - Returns: - ndarray: Clipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) - cmin[0::2] = img_shape[1] - 1 - cmin[1::2] = img_shape[0] - 1 - clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) - return clipped_bboxes - - -def bbox_scaling(bboxes, scale, clip_shape=None): - """Scaling bboxes w.r.t the box center. - - Args: - bboxes (ndarray): Shape(..., 4). - scale (float): Scaling factor. - clip_shape (tuple[int], optional): If specified, bboxes that exceed the - boundary will be clipped according to the given shape (h, w). - - Returns: - ndarray: Scaled bboxes. - """ - if float(scale) == 1.0: - scaled_bboxes = bboxes.copy() - else: - w = bboxes[..., 2] - bboxes[..., 0] + 1 - h = bboxes[..., 3] - bboxes[..., 1] + 1 - dw = (w * (scale - 1)) * 0.5 - dh = (h * (scale - 1)) * 0.5 - scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) - if clip_shape is not None: - return bbox_clip(scaled_bboxes, clip_shape) - else: - return scaled_bboxes - - -def imcrop(img, bboxes, scale=1.0, pad_fill=None): - """Crop image patches. - - 3 steps: scale the bboxes -> clip bboxes -> crop and pad. - - Args: - img (ndarray): Image to be cropped. - bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. - scale (float, optional): Scale ratio of bboxes, the default value - 1.0 means no padding. - pad_fill (Number | list[Number]): Value to be filled for padding. - Default: None, which means no padding. - - Returns: - list[ndarray] | ndarray: The cropped image patches. - """ - chn = 1 if img.ndim == 2 else img.shape[2] - if pad_fill is not None: - if isinstance(pad_fill, (int, float)): - pad_fill = [pad_fill for _ in range(chn)] - assert len(pad_fill) == chn - - _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes - scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) - clipped_bbox = bbox_clip(scaled_bboxes, img.shape) - - patches = [] - for i in range(clipped_bbox.shape[0]): - x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) - if pad_fill is None: - patch = img[y1:y2 + 1, x1:x2 + 1, ...] - else: - _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) - if chn == 1: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) - else: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) - patch = np.array( - pad_fill, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - x_start = 0 if _x1 >= 0 else -_x1 - y_start = 0 if _y1 >= 0 else -_y1 - w = x2 - x1 + 1 - h = y2 - y1 + 1 - patch[y_start:y_start + h, x_start:x_start + w, - ...] = img[y1:y1 + h, x1:x1 + w, ...] - patches.append(patch) - - if bboxes.ndim == 1: - return patches[0] - else: - return patches - - -def impad(img, - *, - shape=None, - padding=None, - pad_val=0, - padding_mode='constant'): - """Pad the given image to a certain shape or pad on all sides with - specified padding mode and padding value. - - Args: - img (ndarray): Image to be padded. - shape (tuple[int]): Expected padding shape (h, w). Default: None. - padding (int or tuple[int]): Padding on each border. If a single int is - provided this is used to pad all borders. If tuple of length 2 is - provided this is the padding on left/right and top/bottom - respectively. If a tuple of length 4 is provided this is the - padding for the left, top, right and bottom borders respectively. - Default: None. Note that `shape` and `padding` can not be both - set. - pad_val (Number | Sequence[Number]): Values to be filled in padding - areas when padding_mode is 'constant'. Default: 0. - padding_mode (str): Type of padding. Should be: constant, edge, - reflect or symmetric. Default: constant. - - constant: pads with a constant value, this value is specified - with pad_val. - - edge: pads with the last value at the edge of the image. - - reflect: pads with reflection of image without repeating the last - value on the edge. For example, padding [1, 2, 3, 4] with 2 - elements on both sides in reflect mode will result in - [3, 2, 1, 2, 3, 4, 3, 2]. - - symmetric: pads with reflection of image repeating the last value - on the edge. For example, padding [1, 2, 3, 4] with 2 elements on - both sides in symmetric mode will result in - [2, 1, 1, 2, 3, 4, 4, 3] - - Returns: - ndarray: The padded image. - """ - - assert (shape is not None) ^ (padding is not None) - if shape is not None: - width = max(shape[1] - img.shape[1], 0) - height = max(shape[0] - img.shape[0], 0) - padding = (0, 0, width, height) - - # check pad_val - if isinstance(pad_val, tuple): - assert len(pad_val) == img.shape[-1] - elif not isinstance(pad_val, numbers.Number): - raise TypeError('pad_val must be a int or a tuple. ' - f'But received {type(pad_val)}') - - # check padding - if isinstance(padding, tuple) and len(padding) in [2, 4]: - if len(padding) == 2: - padding = (padding[0], padding[1], padding[0], padding[1]) - elif isinstance(padding, numbers.Number): - padding = (padding, padding, padding, padding) - else: - raise ValueError('Padding must be a int or a 2, or 4 element tuple.' - f'But received {padding}') - - # check padding mode - assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] - - border_type = { - 'constant': cv2.BORDER_CONSTANT, - 'edge': cv2.BORDER_REPLICATE, - 'reflect': cv2.BORDER_REFLECT_101, - 'symmetric': cv2.BORDER_REFLECT - } - img = cv2.copyMakeBorder( - img, - padding[1], - padding[3], - padding[0], - padding[2], - border_type[padding_mode], - value=pad_val) - - return img - - -def impad_to_multiple(img, divisor, pad_val=0): - """Pad an image to ensure each edge to be multiple to some number. - - Args: - img (ndarray): Image to be padded. - divisor (int): Padded image edges will be multiple to divisor. - pad_val (Number | Sequence[Number]): Same as :func:`impad`. - - Returns: - ndarray: The padded image. - """ - pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor - pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor - return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) - - -def cutout(img, shape, pad_val=0): - """Randomly cut out a rectangle from the original img. - - Args: - img (ndarray): Image to be cutout. - shape (int | tuple[int]): Expected cutout shape (h, w). If given as a - int, the value will be used for both h and w. - pad_val (int | float | tuple[int | float]): Values to be filled in the - cut area. Defaults to 0. - - Returns: - ndarray: The cutout image. - """ - - channels = 1 if img.ndim == 2 else img.shape[2] - if isinstance(shape, int): - cut_h, cut_w = shape, shape - else: - assert isinstance(shape, tuple) and len(shape) == 2, \ - f'shape must be a int or a tuple with length 2, but got type ' \ - f'{type(shape)} instead.' - cut_h, cut_w = shape - if isinstance(pad_val, (int, float)): - pad_val = tuple([pad_val] * channels) - elif isinstance(pad_val, tuple): - assert len(pad_val) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(pad_val), channels) - else: - raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') - - img_h, img_w = img.shape[:2] - y0 = np.random.uniform(img_h) - x0 = np.random.uniform(img_w) - - y1 = int(max(0, y0 - cut_h / 2.)) - x1 = int(max(0, x0 - cut_w / 2.)) - y2 = min(img_h, y1 + cut_h) - x2 = min(img_w, x1 + cut_w) - - if img.ndim == 2: - patch_shape = (y2 - y1, x2 - x1) - else: - patch_shape = (y2 - y1, x2 - x1, channels) - - img_cutout = img.copy() - patch = np.array( - pad_val, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - img_cutout[y1:y2, x1:x2, ...] = patch - - return img_cutout - - -def _get_shear_matrix(magnitude, direction='horizontal'): - """Generate the shear matrix for transformation. - - Args: - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - - Returns: - ndarray: The shear matrix with dtype float32. - """ - if direction == 'horizontal': - shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) - elif direction == 'vertical': - shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) - return shear_matrix - - -def imshear(img, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear an image. - - Args: - img (ndarray): Image to be sheared with format (h, w) - or (h, w, c). - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The sheared image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`') - shear_matrix = _get_shear_matrix(magnitude, direction) - sheared = cv2.warpAffine( - img, - shear_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. shearing masks whose channels large - # than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return sheared - - -def _get_translate_matrix(offset, direction='horizontal'): - """Generate the translate matrix. - - Args: - offset (int | float): The offset used for translate. - direction (str): The translate direction, either - "horizontal" or "vertical". - - Returns: - ndarray: The translate matrix with dtype float32. - """ - if direction == 'horizontal': - translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) - elif direction == 'vertical': - translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) - return translate_matrix - - -def imtranslate(img, - offset, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Translate an image. - - Args: - img (ndarray): Image to be translated with format - (h, w) or (h, w, c). - offset (int | float): The offset used for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The translated image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`.') - translate_matrix = _get_translate_matrix(offset, direction) - translated = cv2.warpAffine( - img, - translate_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. translating masks whose channels - # large than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return translated diff --git a/cv/super_resolution/liif/pytorch/mmcv/image/io.py b/cv/super_resolution/liif/pytorch/mmcv/image/io.py deleted file mode 100644 index ae81b561a..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/image/io.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os.path as osp -import warnings -from pathlib import Path - -import cv2 -import numpy as np -from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, - IMREAD_UNCHANGED) - -from mmcv.fileio import FileClient -from mmcv.utils import is_filepath, is_str - -try: - from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG -except ImportError: - TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None - -try: - from PIL import Image, ImageOps -except ImportError: - Image = None - -try: - import tifffile -except ImportError: - tifffile = None - -jpeg = None -supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] - -imread_flags = { - 'color': IMREAD_COLOR, - 'grayscale': IMREAD_GRAYSCALE, - 'unchanged': IMREAD_UNCHANGED, - 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, - 'grayscale_ignore_orientation': - IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE -} - -imread_backend = 'cv2' - - -def use_backend(backend): - """Select a backend for image decoding. - - Args: - backend (str): The image decoding backend type. Options are `cv2`, - `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) - and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` - file format. - """ - assert backend in supported_backends - global imread_backend - imread_backend = backend - if imread_backend == 'turbojpeg': - if TurboJPEG is None: - raise ImportError('`PyTurboJPEG` is not installed') - global jpeg - if jpeg is None: - jpeg = TurboJPEG() - elif imread_backend == 'pillow': - if Image is None: - raise ImportError('`Pillow` is not installed') - elif imread_backend == 'tifffile': - if tifffile is None: - raise ImportError('`tifffile` is not installed') - - -def _jpegflag(flag='color', channel_order='bgr'): - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'color': - if channel_order == 'bgr': - return TJPF_BGR - elif channel_order == 'rgb': - return TJCS_RGB - elif flag == 'grayscale': - return TJPF_GRAY - else: - raise ValueError('flag must be "color" or "grayscale"') - - -def _pillow2array(img, flag='color', channel_order='bgr'): - """Convert a pillow image to numpy array. - - Args: - img (:obj:`PIL.Image.Image`): The image loaded using PIL - flag (str): Flags specifying the color type of a loaded image, - candidates are 'color', 'grayscale' and 'unchanged'. - Default to 'color'. - channel_order (str): The channel order of the output image array, - candidates are 'bgr' and 'rgb'. Default to 'bgr'. - - Returns: - np.ndarray: The converted numpy array - """ - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'unchanged': - array = np.array(img) - if array.ndim >= 3 and array.shape[2] >= 3: # color image - array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR - else: - # Handle exif orientation tag - if flag in ['color', 'grayscale']: - img = ImageOps.exif_transpose(img) - # If the image mode is not 'RGB', convert it to 'RGB' first. - if img.mode != 'RGB': - if img.mode != 'LA': - # Most formats except 'LA' can be directly converted to RGB - img = img.convert('RGB') - else: - # When the mode is 'LA', the default conversion will fill in - # the canvas with black, which sometimes shadows black objects - # in the foreground. - # - # Therefore, a random color (124, 117, 104) is used for canvas - img_rgba = img.convert('RGBA') - img = Image.new('RGB', img_rgba.size, (124, 117, 104)) - img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha - if flag in ['color', 'color_ignore_orientation']: - array = np.array(img) - if channel_order != 'rgb': - array = array[:, :, ::-1] # RGB to BGR - elif flag in ['grayscale', 'grayscale_ignore_orientation']: - img = img.convert('L') - array = np.array(img) - else: - raise ValueError( - 'flag must be "color", "grayscale", "unchanged", ' - f'"color_ignore_orientation" or "grayscale_ignore_orientation"' - f' but got {flag}') - return array - - -def imread(img_or_path, - flag='color', - channel_order='bgr', - backend=None, - file_client_args=None): - """Read an image. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Args: - img_or_path (ndarray or str or Path): Either a numpy array or str or - pathlib.Path. If it is a numpy array (loaded image), then - it will be returned as is. - flag (str): Flags specifying the color type of a loaded image, - candidates are `color`, `grayscale`, `unchanged`, - `color_ignore_orientation` and `grayscale_ignore_orientation`. - By default, `cv2` and `pillow` backend would rotate the image - according to its EXIF info unless called with `unchanged` or - `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend - always ignore image's EXIF info regardless of the flag. - The `turbojpeg` backend only supports `color` and `grayscale`. - channel_order (str): Order of channel, candidates are `bgr` and `rgb`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. - If backend is None, the global imread_backend specified by - ``mmcv.use_backend()`` will be used. Default: None. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> import mmcv - >>> img_path = '/path/to/img.jpg' - >>> img = mmcv.imread(img_path) - >>> img = mmcv.imread(img_path, flag='color', channel_order='rgb', - ... backend='cv2') - >>> img = mmcv.imread(img_path, flag='color', channel_order='bgr', - ... backend='pillow') - >>> s3_img_path = 's3://bucket/img.jpg' - >>> # infer the file backend by the prefix s3 - >>> img = mmcv.imread(s3_img_path) - >>> # manually set the file backend petrel - >>> img = mmcv.imread(s3_img_path, file_client_args={ - ... 'backend': 'petrel'}) - >>> http_img_path = 'http://path/to/img.jpg' - >>> img = mmcv.imread(http_img_path) - >>> img = mmcv.imread(http_img_path, file_client_args={ - ... 'backend': 'http'}) - """ - - if isinstance(img_or_path, Path): - img_or_path = str(img_or_path) - - if isinstance(img_or_path, np.ndarray): - return img_or_path - elif is_str(img_or_path): - file_client = FileClient.infer_client(file_client_args, img_or_path) - img_bytes = file_client.get(img_or_path) - return imfrombytes(img_bytes, flag, channel_order, backend) - else: - raise TypeError('"img" must be a numpy array or a str or ' - 'a pathlib.Path object') - - -def imfrombytes(content, flag='color', channel_order='bgr', backend=None): - """Read an image from bytes. - - Args: - content (bytes): Image bytes got from files or other streams. - flag (str): Same as :func:`imread`. - channel_order (str): The channel order of the output, candidates - are 'bgr' and 'rgb'. Default to 'bgr'. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. If backend is - None, the global imread_backend specified by ``mmcv.use_backend()`` - will be used. Default: None. - - Returns: - ndarray: Loaded image array. - - Examples: - >>> img_path = '/path/to/img.jpg' - >>> with open(img_path, 'rb') as f: - >>> img_buff = f.read() - >>> img = mmcv.imfrombytes(img_buff) - >>> img = mmcv.imfrombytes(img_buff, flag='color', channel_order='rgb') - >>> img = mmcv.imfrombytes(img_buff, backend='pillow') - >>> img = mmcv.imfrombytes(img_buff, backend='cv2') - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError( - f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow', 'tifffile'") - if backend == 'turbojpeg': - img = jpeg.decode(content, _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - with io.BytesIO(content) as buff: - img = Image.open(buff) - img = _pillow2array(img, flag, channel_order) - return img - elif backend == 'tifffile': - with io.BytesIO(content) as buff: - img = tifffile.imread(buff) - return img - else: - img_np = np.frombuffer(content, np.uint8) - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imdecode(img_np, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - - -def imwrite(img, - file_path, - params=None, - auto_mkdir=None, - file_client_args=None): - """Write image to file. - - Note: - In v1.4.1 and later, add `file_client_args` parameters. - - Warning: - The parameter `auto_mkdir` will be deprecated in the future and every - file clients will make directory automatically. - - Args: - img (ndarray): Image array to be written. - file_path (str): Image file path. - params (None or list): Same as opencv :func:`imwrite` interface. - auto_mkdir (bool): If the parent folder of `file_path` does not exist, - whether to create it automatically. It will be deprecated. - file_client_args (dict | None): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Returns: - bool: Successful or not. - - Examples: - >>> # write to hard disk client - >>> ret = mmcv.imwrite(img, '/path/to/img.jpg') - >>> # infer the file backend by the prefix s3 - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg') - >>> # manually set the file backend petrel - >>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg', file_client_args={ - ... 'backend': 'petrel'}) - """ - assert is_filepath(file_path) - file_path = str(file_path) - if auto_mkdir is not None: - warnings.warn( - 'The parameter `auto_mkdir` will be deprecated in the future and ' - 'every file clients will make directory automatically.') - file_client = FileClient.infer_client(file_client_args, file_path) - img_ext = osp.splitext(file_path)[-1] - # Encode image according to image suffix. - # For example, if image path is '/path/your/img.jpg', the encode - # format is '.jpg'. - flag, img_buff = cv2.imencode(img_ext, img, params) - file_client.put(img_buff.tobytes(), file_path) - return flag diff --git a/cv/super_resolution/liif/pytorch/mmcv/image/misc.py b/cv/super_resolution/liif/pytorch/mmcv/image/misc.py deleted file mode 100644 index 43934a689..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/image/misc.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - -import mmcv - -try: - import torch -except ImportError: - torch = None - - -def tensor2imgs(tensor, mean=None, std=None, to_rgb=True): - """Convert tensor to 3-channel images or 1-channel gray images. - - Args: - tensor (torch.Tensor): Tensor that contains multiple images, shape ( - N, C, H, W). :math:`C` can be either 3 or 1. - mean (tuple[float], optional): Mean of images. If None, - (0, 0, 0) will be used for tensor with 3-channel, - while (0, ) for tensor with 1-channel. Defaults to None. - std (tuple[float], optional): Standard deviation of images. If None, - (1, 1, 1) will be used for tensor with 3-channel, - while (1, ) for tensor with 1-channel. Defaults to None. - to_rgb (bool, optional): Whether the tensor was converted to RGB - format in the first place. If so, convert it back to BGR. - For the tensor with 1 channel, it must be False. Defaults to True. - - Returns: - list[np.ndarray]: A list that contains multiple images. - """ - - if torch is None: - raise RuntimeError('pytorch is not installed') - assert torch.is_tensor(tensor) and tensor.ndim == 4 - channels = tensor.size(1) - assert channels in [1, 3] - if mean is None: - mean = (0, ) * channels - if std is None: - std = (1, ) * channels - assert (channels == len(mean) == len(std) == 3) or \ - (channels == len(mean) == len(std) == 1 and not to_rgb) - - num_imgs = tensor.size(0) - mean = np.array(mean, dtype=np.float32) - std = np.array(std, dtype=np.float32) - imgs = [] - for img_id in range(num_imgs): - img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) - img = mmcv.imdenormalize( - img, mean, std, to_bgr=to_rgb).astype(np.uint8) - imgs.append(np.ascontiguousarray(img)) - return imgs diff --git a/cv/super_resolution/liif/pytorch/mmcv/image/photometric.py b/cv/super_resolution/liif/pytorch/mmcv/image/photometric.py deleted file mode 100644 index b41cea717..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/image/photometric.py +++ /dev/null @@ -1,471 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - -from ..utils import is_tuple_of -from .colorspace import bgr2gray, gray2bgr - - -def imnormalize(img, mean, std, to_rgb=True): - """Normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - img = img.copy().astype(np.float32) - return imnormalize_(img, mean, std, to_rgb) - - -def imnormalize_(img, mean, std, to_rgb=True): - """Inplace normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - # cv2 inplace normalization does not accept uint8 - assert img.dtype != np.uint8 - mean = np.float64(mean.reshape(1, -1)) - stdinv = 1 / np.float64(std.reshape(1, -1)) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - cv2.subtract(img, mean, img) # inplace - cv2.multiply(img, stdinv, img) # inplace - return img - - -def imdenormalize(img, mean, std, to_bgr=True): - assert img.dtype != np.uint8 - mean = mean.reshape(1, -1).astype(np.float64) - std = std.reshape(1, -1).astype(np.float64) - img = cv2.multiply(img, std) # make a copy - cv2.add(img, mean, img) # inplace - if to_bgr: - cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace - return img - - -def iminvert(img): - """Invert (negate) an image. - - Args: - img (ndarray): Image to be inverted. - - Returns: - ndarray: The inverted image. - """ - return np.full_like(img, 255) - img - - -def solarize(img, thr=128): - """Solarize an image (invert all pixel values above a threshold) - - Args: - img (ndarray): Image to be solarized. - thr (int): Threshold for solarizing (0 - 255). - - Returns: - ndarray: The solarized image. - """ - img = np.where(img < thr, img, 255 - img) - return img - - -def posterize(img, bits): - """Posterize an image (reduce the number of bits for each color channel) - - Args: - img (ndarray): Image to be posterized. - bits (int): Number of bits (1 to 8) to use for posterizing. - - Returns: - ndarray: The posterized image. - """ - shift = 8 - bits - img = np.left_shift(np.right_shift(img, shift), shift) - return img - - -def adjust_color(img, alpha=1, beta=None, gamma=0): - r"""It blends the source image and its gray image: - - .. math:: - output = img * alpha + gray\_img * beta + gamma - - Args: - img (ndarray): The input source image. - alpha (int | float): Weight for the source image. Default 1. - beta (int | float): Weight for the converted gray image. - If None, it's assigned the value (1 - `alpha`). - gamma (int | float): Scalar added to each sum. - Same as :func:`cv2.addWeighted`. Default 0. - - Returns: - ndarray: Colored image which has the same size and dtype as input. - """ - gray_img = bgr2gray(img) - gray_img = np.tile(gray_img[..., None], [1, 1, 3]) - if beta is None: - beta = 1 - alpha - colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) - if not colored_img.dtype == np.uint8: - # Note when the dtype of `img` is not the default `np.uint8` - # (e.g. np.float32), the value in `colored_img` got from cv2 - # is not guaranteed to be in range [0, 255], so here clip - # is needed. - colored_img = np.clip(colored_img, 0, 255) - return colored_img - - -def imequalize(img): - """Equalize the image histogram. - - This function applies a non-linear mapping to the input image, - in order to create a uniform distribution of grayscale values - in the output image. - - Args: - img (ndarray): Image to be equalized. - - Returns: - ndarray: The equalized image. - """ - - def _scale_channel(im, c): - """Scale the data in the corresponding channel.""" - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # For computing the step, filter out the nonzeros. - nonzero_histo = histo[histo > 0] - step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 - if not step: - lut = np.array(range(256)) - else: - # Compute the cumulative sum, shifted by step // 2 - # and then normalized by step. - lut = (np.cumsum(histo) + (step // 2)) // step - # Shift lut, prepending with 0. - lut = np.concatenate([[0], lut[:-1]], 0) - # handle potential integer overflow - lut[lut > 255] = 255 - # If step is zero, return the original image. - # Otherwise, index from lut. - return np.where(np.equal(step, 0), im, lut[im]) - - # Scales each channel independently and then stacks - # the result. - s1 = _scale_channel(img, 0) - s2 = _scale_channel(img, 1) - s3 = _scale_channel(img, 2) - equalized_img = np.stack([s1, s2, s3], axis=-1) - return equalized_img.astype(img.dtype) - - -def adjust_brightness(img, factor=1.): - """Adjust image brightness. - - This function controls the brightness of an image. An - enhancement factor of 0.0 gives a black image. - A factor of 1.0 gives the original image. This function - blends the source image and the degenerated black image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be brightened. - factor (float): A value controls the enhancement. - Factor 1.0 returns the original image, lower - factors mean less color (brightness, contrast, - etc), and higher values more. Default 1. - - Returns: - ndarray: The brightened image. - """ - degenerated = np.zeros_like(img) - # Note manually convert the dtype to np.float32, to - # achieve as close results as PIL.ImageEnhance.Brightness. - # Set beta=1-factor, and gamma=0 - brightened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - brightened_img = np.clip(brightened_img, 0, 255) - return brightened_img.astype(img.dtype) - - -def adjust_contrast(img, factor=1.): - """Adjust image contrast. - - This function controls the contrast of an image. An - enhancement factor of 0.0 gives a solid grey - image. A factor of 1.0 gives the original image. It - blends the source image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be contrasted. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - - Returns: - ndarray: The contrasted image. - """ - gray_img = bgr2gray(img) - hist = np.histogram(gray_img, 256, (0, 255))[0] - mean = round(np.sum(gray_img) / np.sum(hist)) - degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) - degenerated = gray2bgr(degenerated) - contrasted_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - contrasted_img = np.clip(contrasted_img, 0, 255) - return contrasted_img.astype(img.dtype) - - -def auto_contrast(img, cutoff=0): - """Auto adjust image contrast. - - This function maximize (normalize) image contrast by first removing cutoff - percent of the lightest and darkest pixels from the histogram and remapping - the image so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - Args: - img (ndarray): Image to be contrasted. BGR order. - cutoff (int | float | tuple): The cutoff percent of the lightest and - darkest pixels to be removed. If given as tuple, it shall be - (low, high). Otherwise, the single value will be used for both. - Defaults to 0. - - Returns: - ndarray: The contrasted image. - """ - - def _auto_contrast_channel(im, c, cutoff): - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # Remove cut-off percent pixels from histo - histo_sum = np.cumsum(histo) - cut_low = histo_sum[-1] * cutoff[0] // 100 - cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 - histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low - histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) - - # Compute mapping - low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] - # If all the values have been cut off, return the origin img - if low >= high: - return im - scale = 255.0 / (high - low) - offset = -low * scale - lut = np.array(range(256)) - lut = lut * scale + offset - lut = np.clip(lut, 0, 255) - return lut[im] - - if isinstance(cutoff, (int, float)): - cutoff = (cutoff, cutoff) - else: - assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ - f'float or tuple, but got {type(cutoff)} instead.' - # Auto adjusts contrast for each channel independently and then stacks - # the result. - s1 = _auto_contrast_channel(img, 0, cutoff) - s2 = _auto_contrast_channel(img, 1, cutoff) - s3 = _auto_contrast_channel(img, 2, cutoff) - contrasted_img = np.stack([s1, s2, s3], axis=-1) - return contrasted_img.astype(img.dtype) - - -def adjust_sharpness(img, factor=1., kernel=None): - """Adjust image sharpness. - - This function controls the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image. A - factor of 1.0 gives the original image. And a factor - of 2.0 gives a sharpened image. It blends the source - image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be sharpened. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - kernel (np.ndarray, optional): Filter kernel to be applied on the img - to obtain the degenerated img. Defaults to None. - - Note: - No value sanity check is enforced on the kernel set by users. So with - an inappropriate kernel, the ``adjust_sharpness`` may fail to perform - the function its name indicates but end up performing whatever - transform determined by the kernel. - - Returns: - ndarray: The sharpened image. - """ - - if kernel is None: - # adopted from PIL.ImageFilter.SMOOTH - kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 - assert isinstance(kernel, np.ndarray), \ - f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' - assert kernel.ndim == 2, \ - f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' - - degenerated = cv2.filter2D(img, -1, kernel) - sharpened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - sharpened_img = np.clip(sharpened_img, 0, 255) - return sharpened_img.astype(img.dtype) - - -def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): - """AlexNet-style PCA jitter. - - This data augmentation is proposed in `ImageNet Classification with Deep - Convolutional Neural Networks - `_. - - Args: - img (ndarray): Image to be adjusted lighting. BGR order. - eigval (ndarray): the eigenvalue of the convariance matrix of pixel - values, respectively. - eigvec (ndarray): the eigenvector of the convariance matrix of pixel - values, respectively. - alphastd (float): The standard deviation for distribution of alpha. - Defaults to 0.1 - to_rgb (bool): Whether to convert img to rgb. - - Returns: - ndarray: The adjusted image. - """ - assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ - f'eigval and eigvec should both be of type np.ndarray, got ' \ - f'{type(eigval)} and {type(eigvec)} instead.' - - assert eigval.ndim == 1 and eigvec.ndim == 2 - assert eigvec.shape == (3, eigval.shape[0]) - n_eigval = eigval.shape[0] - assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ - f'got {type(alphastd)} instead.' - - img = img.copy().astype(np.float32) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - - alpha = np.random.normal(0, alphastd, n_eigval) - alter = eigvec \ - * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ - * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) - alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) - img_adjusted = img + alter - return img_adjusted - - -def lut_transform(img, lut_table): - """Transform array by look-up table. - - The function lut_transform fills the output array with values from the - look-up table. Indices of the entries are taken from the input array. - - Args: - img (ndarray): Image to be transformed. - lut_table (ndarray): look-up table of 256 elements; in case of - multi-channel input array, the table should either have a single - channel (in this case the same table is used for all channels) or - the same number of channels as in the input array. - - Returns: - ndarray: The transformed image. - """ - assert isinstance(img, np.ndarray) - assert 0 <= np.min(img) and np.max(img) <= 255 - assert isinstance(lut_table, np.ndarray) - assert lut_table.shape == (256, ) - - return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) - - -def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - img (ndarray): Image to be processed. - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - - Returns: - ndarray: The processed image. - """ - assert isinstance(img, np.ndarray) - assert img.ndim == 2 - assert isinstance(clip_limit, (float, int)) - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - - clahe = cv2.createCLAHE(clip_limit, tile_grid_size) - return clahe.apply(np.array(img, dtype=np.uint8)) - - -def adjust_hue(img: np.ndarray, hue_factor: float) -> np.ndarray: - """Adjust hue of an image. - - The image hue is adjusted by converting the image to HSV and cyclically - shifting the intensities in the hue channel (H). The image is then - converted back to original image mode. - - `hue_factor` is the amount of shift in H channel and must be in the - interval `[-0.5, 0.5]`. - - Modified from - https://github.com/pytorch/vision/blob/main/torchvision/ - transforms/functional.py - - Args: - img (ndarray): Image to be adjusted. - hue_factor (float): How much to shift the hue channel. Should be in - [-0.5, 0.5]. 0.5 and -0.5 give complete reversal of hue channel in - HSV space in positive and negative direction respectively. - 0 means no shift. Therefore, both -0.5 and 0.5 will give an image - with complementary colors while 0 gives the original image. - - Returns: - ndarray: Hue adjusted image. - """ - - if not (-0.5 <= hue_factor <= 0.5): - raise ValueError(f'hue_factor:{hue_factor} is not in [-0.5, 0.5].') - if not (isinstance(img, np.ndarray) and (img.ndim in {2, 3})): - raise TypeError('img should be ndarray with dim=[2 or 3].') - - dtype = img.dtype - img = img.astype(np.uint8) - hsv_img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV_FULL) - h, s, v = cv2.split(hsv_img) - h = h.astype(np.uint8) - # uint8 addition take cares of rotation across boundaries - with np.errstate(over='ignore'): - h += np.uint8(hue_factor * 255) - hsv_img = cv2.merge([h, s, v]) - return cv2.cvtColor(hsv_img, cv2.COLOR_HSV2RGB_FULL).astype(dtype) diff --git a/cv/super_resolution/liif/pytorch/mmcv/parallel/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/parallel/__init__.py deleted file mode 100644 index 2ed2c17ad..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/parallel/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collate import collate -from .data_container import DataContainer -from .data_parallel import MMDataParallel -from .distributed import MMDistributedDataParallel -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter, scatter_kwargs -from .utils import is_module_wrapper - -__all__ = [ - 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', - 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' -] diff --git a/cv/super_resolution/liif/pytorch/mmcv/parallel/_functions.py b/cv/super_resolution/liif/pytorch/mmcv/parallel/_functions.py deleted file mode 100644 index 95c58bf1a..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/parallel/_functions.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import _get_stream - - -def scatter(input, devices, streams=None): - """Scatters tensor across multiple GPUs.""" - if streams is None: - streams = [None] * len(devices) - - if isinstance(input, list): - chunk_size = (len(input) - 1) // len(devices) + 1 - outputs = [ - scatter(input[i], [devices[i // chunk_size]], - [streams[i // chunk_size]]) for i in range(len(input)) - ] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - # TODO: copy to a pinned buffer first (if copying from CPU) - stream = streams[0] if output.numel() > 0 else None - if devices != [-1]: - with torch.cuda.device(devices[0]), torch.cuda.stream(stream): - output = output.cuda(devices[0], non_blocking=True) - - return output - else: - raise Exception(f'Unknown type {type(input)}.') - - -def synchronize_stream(output, devices, streams): - if isinstance(output, list): - chunk_size = len(output) // len(devices) - for i in range(len(devices)): - for j in range(chunk_size): - synchronize_stream(output[i * chunk_size + j], [devices[i]], - [streams[i]]) - elif isinstance(output, torch.Tensor): - if output.numel() != 0: - with torch.cuda.device(devices[0]): - main_stream = torch.cuda.current_stream() - main_stream.wait_stream(streams[0]) - output.record_stream(main_stream) - else: - raise Exception(f'Unknown type {type(output)}.') - - -def get_input_device(input): - if isinstance(input, list): - for item in input: - input_device = get_input_device(item) - if input_device != -1: - return input_device - return -1 - elif isinstance(input, torch.Tensor): - return input.get_device() if input.is_cuda else -1 - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_gpus, input): - input_device = get_input_device(input) - streams = None - if input_device == -1 and target_gpus != [-1]: - # Perform CPU to GPU copies in a background stream - streams = [_get_stream(device) for device in target_gpus] - - outputs = scatter(input, target_gpus, streams) - # Synchronize with the copy stream - if streams is not None: - synchronize_stream(outputs, target_gpus, streams) - - return tuple(outputs) if isinstance(outputs, list) else (outputs, ) diff --git a/cv/super_resolution/liif/pytorch/mmcv/parallel/collate.py b/cv/super_resolution/liif/pytorch/mmcv/parallel/collate.py deleted file mode 100644 index ad749197d..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/parallel/collate.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence - -import torch -import torch.nn.functional as F -from torch.utils.data.dataloader import default_collate - -from .data_container import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Puts each data field into a tensor/DataContainer with outer dimension - batch size. - - Extend default_collate to add support for - :type:`~mmcv.parallel.DataContainer`. There are 3 cases. - - 1. cpu_only = True, e.g., meta data - 2. cpu_only = False, stack = True, e.g., images tensors - 3. cpu_only = False, stack = False, e.g., gt bboxes - """ - - if not isinstance(batch, Sequence): - raise TypeError(f'{batch.dtype} is not supported.') - - if isinstance(batch[0], DataContainer): - stacked = [] - if batch[0].cpu_only: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer( - stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) - elif batch[0].stack: - for i in range(0, len(batch), samples_per_gpu): - assert isinstance(batch[i].data, torch.Tensor) - - if batch[i].pad_dims is not None: - ndim = batch[i].dim() - assert ndim > batch[i].pad_dims - max_shape = [0 for _ in range(batch[i].pad_dims)] - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = batch[i].size(-dim) - for sample in batch[i:i + samples_per_gpu]: - for dim in range(0, ndim - batch[i].pad_dims): - assert batch[i].size(dim) == sample.size(dim) - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = max(max_shape[dim - 1], - sample.size(-dim)) - padded_samples = [] - for sample in batch[i:i + samples_per_gpu]: - pad = [0 for _ in range(batch[i].pad_dims * 2)] - for dim in range(1, batch[i].pad_dims + 1): - pad[2 * dim - - 1] = max_shape[dim - 1] - sample.size(-dim) - padded_samples.append( - F.pad( - sample.data, pad, value=sample.padding_value)) - stacked.append(default_collate(padded_samples)) - elif batch[i].pad_dims is None: - stacked.append( - default_collate([ - sample.data - for sample in batch[i:i + samples_per_gpu] - ])) - else: - raise ValueError( - 'pad_dims should be either None or integers (1-3)') - - else: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer(stacked, batch[0].stack, batch[0].padding_value) - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - return [collate(samples, samples_per_gpu) for samples in transposed] - elif isinstance(batch[0], Mapping): - return { - key: collate([d[key] for d in batch], samples_per_gpu) - for key in batch[0] - } - else: - return default_collate(batch) diff --git a/cv/super_resolution/liif/pytorch/mmcv/parallel/data_container.py b/cv/super_resolution/liif/pytorch/mmcv/parallel/data_container.py deleted file mode 100644 index cedb0d32a..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/parallel/data_container.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch - - -def assert_tensor_type(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not isinstance(args[0].data, torch.Tensor): - raise AttributeError( - f'{args[0].__class__.__name__} has no attribute ' - f'{func.__name__} for type {args[0].datatype}') - return func(*args, **kwargs) - - return wrapper - - -class DataContainer: - """A container for any type of objects. - - Typically tensors will be stacked in the collate function and sliced along - some dimension in the scatter function. This behavior has some limitations. - 1. All tensors have to be the same size. - 2. Types are limited (numpy array or Tensor). - - We design `DataContainer` and `MMDataParallel` to overcome these - limitations. The behavior can be either of the following. - - - copy to GPU, pad all tensors to the same size and stack them - - copy to GPU without stacking - - leave the objects as is and pass it to the model - - pad_dims specifies the number of last few dimensions to do padding - """ - - def __init__(self, - data, - stack=False, - padding_value=0, - cpu_only=False, - pad_dims=2): - self._data = data - self._cpu_only = cpu_only - self._stack = stack - self._padding_value = padding_value - assert pad_dims in [None, 1, 2, 3] - self._pad_dims = pad_dims - - def __repr__(self): - return f'{self.__class__.__name__}({repr(self.data)})' - - def __len__(self): - return len(self._data) - - @property - def data(self): - return self._data - - @property - def datatype(self): - if isinstance(self.data, torch.Tensor): - return self.data.type() - else: - return type(self.data) - - @property - def cpu_only(self): - return self._cpu_only - - @property - def stack(self): - return self._stack - - @property - def padding_value(self): - return self._padding_value - - @property - def pad_dims(self): - return self._pad_dims - - @assert_tensor_type - def size(self, *args, **kwargs): - return self.data.size(*args, **kwargs) - - @assert_tensor_type - def dim(self): - return self.data.dim() diff --git a/cv/super_resolution/liif/pytorch/mmcv/parallel/data_parallel.py b/cv/super_resolution/liif/pytorch/mmcv/parallel/data_parallel.py deleted file mode 100644 index e86b47a5c..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/parallel/data_parallel.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from itertools import chain - -from torch.nn.parallel import DataParallel - -from .scatter_gather import scatter_kwargs - - -class MMDataParallel(DataParallel): - """The DataParallel module that supports DataContainer. - - MMDataParallel has two main differences with PyTorch DataParallel: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data during both GPU and CPU inference. - - It implement two more APIs ``train_step()`` and ``val_step()``. - - .. warning:: - MMDataParallel only supports single GPU training, if you need to - train with multiple GPUs, please use MMDistributedDataParallel - instead. If you have multiple GPUs and you just want to use - MMDataParallel, you can set the environment variable - ``CUDA_VISIBLE_DEVICES=0`` or instantiate ``MMDataParallel`` with - ``device_ids=[0]``. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - device_ids (list[int]): Device IDS of modules to be scattered to. - Defaults to None when GPU is not available. - output_device (str | int): Device ID for output. Defaults to None. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super().__init__(*args, dim=dim, **kwargs) - self.dim = dim - - def forward(self, *inputs, **kwargs): - """Override the original forward function. - - The main difference lies in the CPU inference where the data in - :class:`DataContainers` will still be gathered. - """ - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module(*inputs[0], **kwargs[0]) - else: - return super().forward(*inputs, **kwargs) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.train_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.train_step(*inputs[0], **kwargs[0]) - - def val_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.val_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/cv/super_resolution/liif/pytorch/mmcv/parallel/distributed.py b/cv/super_resolution/liif/pytorch/mmcv/parallel/distributed.py deleted file mode 100644 index 0188ca4ab..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/parallel/distributed.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel.distributed import (DistributedDataParallel, - _find_tensors) - -from mmcv import print_log -from mmcv.utils import TORCH_VERSION, digit_version -from .scatter_gather import scatter_kwargs - - -class MMDistributedDataParallel(DistributedDataParallel): - """The DDP module that supports DataContainer. - - MMDDP has two main differences with PyTorch DDP: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data. - - It implement two APIs ``train_step()`` and ``val_step()``. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - """train_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.train_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.train_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.train_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output - - def val_step(self, *inputs, **kwargs): - """val_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.val_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_pre_fwd(): - self._sync_buffers() - else: - if (getattr(self, 'require_forward_param_sync', False) - and self.require_forward_param_sync): - self._sync_params() - - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.val_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.val_step(*inputs, **kwargs) - - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.11.0a0')): - if self._check_sync_bufs_post_fwd(): - self._sync_buffers() - - if (torch.is_grad_enabled() - and getattr(self, 'require_backward_grad_sync', False) - and self.require_backward_grad_sync): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output diff --git a/cv/super_resolution/liif/pytorch/mmcv/parallel/distributed_deprecated.py b/cv/super_resolution/liif/pytorch/mmcv/parallel/distributed_deprecated.py deleted file mode 100644 index d438b58bd..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/parallel/distributed_deprecated.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn as nn -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter_kwargs - - -@MODULE_WRAPPERS.register_module() -class MMDistributedDataParallel(nn.Module): - - def __init__(self, - module, - dim=0, - broadcast_buffers=True, - bucket_cap_mb=25): - super().__init__() - self.module = module - self.dim = dim - self.broadcast_buffers = broadcast_buffers - - self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 - self._sync_params() - - def _dist_broadcast_coalesced(self, tensors, buffer_size): - for tensors in _take_tensors(tensors, buffer_size): - flat_tensors = _flatten_dense_tensors(tensors) - dist.broadcast(flat_tensors, 0) - for tensor, synced in zip( - tensors, _unflatten_dense_tensors(flat_tensors, tensors)): - tensor.copy_(synced) - - def _sync_params(self): - module_states = list(self.module.state_dict().values()) - if len(module_states) > 0: - self._dist_broadcast_coalesced(module_states, - self.broadcast_bucket_size) - if self.broadcast_buffers: - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) < digit_version('1.0')): - buffers = [b.data for b in self.module._all_buffers()] - else: - buffers = [b.data for b in self.module.buffers()] - if len(buffers) > 0: - self._dist_broadcast_coalesced(buffers, - self.broadcast_bucket_size) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/super_resolution/liif/pytorch/mmcv/parallel/registry.py b/cv/super_resolution/liif/pytorch/mmcv/parallel/registry.py deleted file mode 100644 index 144f9fb16..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/parallel/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel, DistributedDataParallel - -from mmcv.utils import Registry - -MODULE_WRAPPERS = Registry('module wrapper') -MODULE_WRAPPERS.register_module(module=DataParallel) -MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/cv/super_resolution/liif/pytorch/mmcv/parallel/scatter_gather.py b/cv/super_resolution/liif/pytorch/mmcv/parallel/scatter_gather.py deleted file mode 100644 index 900ff8856..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/parallel/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import Scatter as OrigScatter - -from ._functions import Scatter -from .data_container import DataContainer - - -def scatter(inputs, target_gpus, dim=0): - """Scatter inputs to target gpus. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_gpus != [-1]: - return OrigScatter.apply(target_gpus, None, dim, obj) - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_gpus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_gpus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_gpus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_gpus, dim) if inputs else [] - kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/super_resolution/liif/pytorch/mmcv/parallel/utils.py b/cv/super_resolution/liif/pytorch/mmcv/parallel/utils.py deleted file mode 100644 index cc800d6b9..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/parallel/utils.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import MODULE_WRAPPERS - - -def is_module_wrapper(module): - """Check if a module is a module wrapper. - - The following 3 modules in MMCV (and their subclasses) are regarded as - module wrappers: DataParallel, DistributedDataParallel, - MMDistributedDataParallel (the deprecated version). You may add you own - module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS or - its children registries. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: True if the input module is a module wrapper. - """ - - def is_module_in_wrapper(module, module_wrapper): - module_wrappers = tuple(module_wrapper.module_dict.values()) - if isinstance(module, module_wrappers): - return True - for child in module_wrapper.children.values(): - if is_module_in_wrapper(module, child): - return True - return False - - return is_module_in_wrapper(module, MODULE_WRAPPERS) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/runner/__init__.py deleted file mode 100644 index 183d53672..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_module import BaseModule, ModuleDict, ModuleList, Sequential -from .base_runner import BaseRunner -from .builder import RUNNERS, build_runner -from .checkpoint import (CheckpointLoader, _load_checkpoint, - _load_checkpoint_with_prefix, load_checkpoint, - load_state_dict, save_checkpoint, weights_to_cpu) -from .default_constructor import DefaultRunnerConstructor -from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, - init_dist, master_only) -from .epoch_based_runner import EpochBasedRunner, Runner -from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model -from .hooks import (HOOKS, CheckpointHook, ClearMLLoggerHook, ClosureHook, - DistEvalHook, DistSamplerSeedHook, DvcliveLoggerHook, - EMAHook, EvalHook, Fp16OptimizerHook, - GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, Hook, IterTimerHook, - LoggerHook, MlflowLoggerHook, NeptuneLoggerHook, - OptimizerHook, PaviLoggerHook, SegmindLoggerHook, - SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .hooks.lr_updater import StepLrUpdaterHook # noqa -from .hooks.lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, - InvLrUpdaterHook, LinearAnnealingLrUpdaterHook, - LrUpdaterHook, OneCycleLrUpdaterHook, - PolyLrUpdaterHook) -from .hooks.momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .iter_based_runner import IterBasedRunner, IterLoader -from .log_buffer import LogBuffer -from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, - DefaultOptimizerConstructor, build_optimizer, - build_optimizer_constructor) -from .priority import Priority, get_priority -from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed - -# initialize ipu to registor ipu runner to RUNNERS -from mmcv.device import ipu # isort:skip # noqa - -__all__ = [ - 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', - 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', - 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', - 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', - 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', - 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', - 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', - 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', - 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', - 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', - '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', - 'ModuleDict', 'ModuleList', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor', - 'SegmindLoggerHook', 'LinearAnnealingMomentumUpdaterHook', - 'LinearAnnealingLrUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/base_module.py b/cv/super_resolution/liif/pytorch/mmcv/runner/base_module.py deleted file mode 100644 index 7e64bdfb1..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/base_module.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings -from abc import ABCMeta -from collections import defaultdict -from logging import FileHandler -from typing import Iterable, Optional - -import torch.nn as nn - -from mmcv.runner.dist_utils import master_only -from mmcv.utils.logging import get_logger, logger_initialized, print_log - - -class BaseModule(nn.Module, metaclass=ABCMeta): - """Base module for all modules in openmmlab. - - ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional - functionality of parameter initialization. Compared with - ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. - - - ``init_cfg``: the config to control the initialization. - - ``init_weights``: The function of parameter initialization and recording - initialization information. - - ``_params_init_info``: Used to track the parameter initialization - information. This attribute only exists during executing the - ``init_weights``. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, init_cfg: Optional[dict] = None): - """Initialize BaseModule, inherited from `torch.nn.Module`""" - - # NOTE init_cfg can be defined in different levels, but init_cfg - # in low levels has a higher priority. - - super().__init__() - # define default value of init_cfg instead of hard code - # in init_weights() function - self._is_init = False - - self.init_cfg = copy.deepcopy(init_cfg) - - # Backward compatibility in derived classes - # if pretrained is not None: - # warnings.warn('DeprecationWarning: pretrained is a deprecated \ - # key, please consider using init_cfg') - # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - - @property - def is_init(self): - return self._is_init - - def init_weights(self): - """Initialize the weights.""" - - is_top_level_module = False - # check if it is top-level module - if not hasattr(self, '_params_init_info'): - # The `_params_init_info` is used to record the initialization - # information of the parameters - # the key should be the obj:`nn.Parameter` of model and the value - # should be a dict containing - # - init_info (str): The string that describes the initialization. - # - tmp_mean_value (FloatTensor): The mean of the parameter, - # which indicates whether the parameter has been modified. - # this attribute would be deleted after all parameters - # is initialized. - self._params_init_info = defaultdict(dict) - is_top_level_module = True - - # Initialize the `_params_init_info`, - # When detecting the `tmp_mean_value` of - # the corresponding parameter is changed, update related - # initialization information - for name, param in self.named_parameters(): - self._params_init_info[param][ - 'init_info'] = f'The value is the same before and ' \ - f'after calling `init_weights` ' \ - f'of {self.__class__.__name__} ' - self._params_init_info[param][ - 'tmp_mean_value'] = param.data.mean() - - # pass `params_init_info` to all submodules - # All submodules share the same `params_init_info`, - # so it will be updated when parameters are - # modified at any level of the model. - for sub_module in self.modules(): - sub_module._params_init_info = self._params_init_info - - # Get the initialized logger, if not exist, - # create a logger named `mmcv` - logger_names = list(logger_initialized.keys()) - logger_name = logger_names[0] if logger_names else 'mmcv' - - from ..cnn import initialize - from ..cnn.utils.weight_init import update_init_info - module_name = self.__class__.__name__ - if not self._is_init: - if self.init_cfg: - print_log( - f'initialize {module_name} with init_cfg {self.init_cfg}', - logger=logger_name) - initialize(self, self.init_cfg) - if isinstance(self.init_cfg, dict): - # prevent the parameters of - # the pre-trained model - # from being overwritten by - # the `init_weights` - if self.init_cfg['type'] == 'Pretrained': - return - - for m in self.children(): - if hasattr(m, 'init_weights'): - m.init_weights() - # users may overload the `init_weights` - update_init_info( - m, - init_info=f'Initialized by ' - f'user-defined `init_weights`' - f' in {m.__class__.__name__} ') - - self._is_init = True - else: - warnings.warn(f'init_weights of {self.__class__.__name__} has ' - f'been called more than once.') - - if is_top_level_module: - self._dump_init_info(logger_name) - - for sub_module in self.modules(): - del sub_module._params_init_info - - @master_only - def _dump_init_info(self, logger_name: str): - """Dump the initialization information to a file named - `initialization.log.json` in workdir. - - Args: - logger_name (str): The name of logger. - """ - - logger = get_logger(logger_name) - - with_file_handler = False - # dump the information to the logger file if there is a `FileHandler` - for handler in logger.handlers: - if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True - if not with_file_handler: - for name, param in self.named_parameters(): - print_log( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n ", - logger=logger_name) - - def __repr__(self): - s = super().__repr__() - if self.init_cfg: - s += f'\ninit_cfg={self.init_cfg}' - return s - - -class Sequential(BaseModule, nn.Sequential): - """Sequential module in openmmlab. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, *args, init_cfg: Optional[dict] = None): - BaseModule.__init__(self, init_cfg) - nn.Sequential.__init__(self, *args) - - -class ModuleList(BaseModule, nn.ModuleList): - """ModuleList in openmmlab. - - Args: - modules (iterable, optional): an iterable of modules to add. - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, - modules: Optional[Iterable] = None, - init_cfg: Optional[dict] = None): - BaseModule.__init__(self, init_cfg) - nn.ModuleList.__init__(self, modules) - - -class ModuleDict(BaseModule, nn.ModuleDict): - """ModuleDict in openmmlab. - - Args: - modules (dict, optional): a mapping (dictionary) of (string: module) - or an iterable of key-value pairs of type (string, module). - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, - modules: Optional[dict] = None, - init_cfg: Optional[dict] = None): - BaseModule.__init__(self, init_cfg) - nn.ModuleDict.__init__(self, modules) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/base_runner.py b/cv/super_resolution/liif/pytorch/mmcv/runner/base_runner.py deleted file mode 100644 index 12a0025f8..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/base_runner.py +++ /dev/null @@ -1,544 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -import os.path as osp -import warnings -from abc import ABCMeta, abstractmethod - -import torch -from torch.optim import Optimizer - -import mmcv -from ..parallel import is_module_wrapper -from .checkpoint import load_checkpoint -from .dist_utils import get_dist_info -from .hooks import HOOKS, Hook -from .log_buffer import LogBuffer -from .priority import Priority, get_priority -from .utils import get_time_str - - -class BaseRunner(metaclass=ABCMeta): - """The base class of Runner, a training helper for PyTorch. - - All subclasses should implement the following APIs: - - - ``run()`` - - ``train()`` - - ``val()`` - - ``save_checkpoint()`` - - Args: - model (:obj:`torch.nn.Module`): The model to be run. - batch_processor (callable): A callable method that process a data - batch. The interface of this method should be - `batch_processor(model, data, train_mode) -> dict` - optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an - optimizer (in most cases) or a dict of optimizers (in models that - requires more than one optimizer, e.g., GAN). - work_dir (str, optional): The working directory to save checkpoints - and logs. Defaults to None. - logger (:obj:`logging.Logger`): Logger used during training. - Defaults to None. (The default value is just for backward - compatibility) - meta (dict | None): A dict records some import information such as - environment info and seed, which will be logged in logger hook. - Defaults to None. - max_epochs (int, optional): Total training epochs. - max_iters (int, optional): Total training iterations. - """ - - def __init__(self, - model, - batch_processor=None, - optimizer=None, - work_dir=None, - logger=None, - meta=None, - max_iters=None, - max_epochs=None): - if batch_processor is not None: - if not callable(batch_processor): - raise TypeError('batch_processor must be callable, ' - f'but got {type(batch_processor)}') - warnings.warn( - 'batch_processor is deprecated, please implement ' - 'train_step() and val_step() in the model instead.', - DeprecationWarning) - # raise an error is `batch_processor` is not None and - # `model.train_step()` exists. - if is_module_wrapper(model): - _model = model.module - else: - _model = model - if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): - raise RuntimeError( - 'batch_processor and model.train_step()/model.val_step() ' - 'cannot be both available.') - else: - assert hasattr(model, 'train_step') - - # check the type of `optimizer` - if isinstance(optimizer, dict): - for name, optim in optimizer.items(): - if not isinstance(optim, Optimizer): - raise TypeError( - f'optimizer must be a dict of torch.optim.Optimizers, ' - f'but optimizer["{name}"] is a {type(optim)}') - elif not isinstance(optimizer, Optimizer) and optimizer is not None: - raise TypeError( - f'optimizer must be a torch.optim.Optimizer object ' - f'or dict or None, but got {type(optimizer)}') - - # check the type of `logger` - if not isinstance(logger, logging.Logger): - raise TypeError(f'logger must be a logging.Logger object, ' - f'but got {type(logger)}') - - # check the type of `meta` - if meta is not None and not isinstance(meta, dict): - raise TypeError( - f'meta must be a dict or None, but got {type(meta)}') - - self.model = model - self.batch_processor = batch_processor - self.optimizer = optimizer - self.logger = logger - self.meta = meta - # create work_dir - if mmcv.is_str(work_dir): - self.work_dir = osp.abspath(work_dir) - mmcv.mkdir_or_exist(self.work_dir) - elif work_dir is None: - self.work_dir = None - else: - raise TypeError('"work_dir" must be a str or None') - - # get model name from the model class - if hasattr(self.model, 'module'): - self._model_name = self.model.module.__class__.__name__ - else: - self._model_name = self.model.__class__.__name__ - - self._rank, self._world_size = get_dist_info() - self.timestamp = get_time_str() - self.mode = None - self._hooks = [] - self._epoch = 0 - self._iter = 0 - self._inner_iter = 0 - - if max_epochs is not None and max_iters is not None: - raise ValueError( - 'Only one of `max_epochs` or `max_iters` can be set.') - - self._max_epochs = max_epochs - self._max_iters = max_iters - # TODO: Redesign LogBuffer, it is not flexible and elegant enough - self.log_buffer = LogBuffer() - - @property - def model_name(self): - """str: Name of the model, usually the module class name.""" - return self._model_name - - @property - def rank(self): - """int: Rank of current process. (distributed training)""" - return self._rank - - @property - def world_size(self): - """int: Number of processes participating in the job. - (distributed training)""" - return self._world_size - - @property - def hooks(self): - """list[:obj:`Hook`]: A list of registered hooks.""" - return self._hooks - - @property - def epoch(self): - """int: Current epoch.""" - return self._epoch - - @property - def iter(self): - """int: Current iteration.""" - return self._iter - - @property - def inner_iter(self): - """int: Iteration in an epoch.""" - return self._inner_iter - - @property - def max_epochs(self): - """int: Maximum training epochs.""" - return self._max_epochs - - @property - def max_iters(self): - """int: Maximum training iterations.""" - return self._max_iters - - @abstractmethod - def train(self): - pass - - @abstractmethod - def val(self): - pass - - @abstractmethod - def run(self, data_loaders, workflow, **kwargs): - pass - - @abstractmethod - def save_checkpoint(self, - out_dir, - filename_tmpl, - save_optimizer=True, - meta=None, - create_symlink=True): - pass - - def current_lr(self): - """Get current learning rates. - - Returns: - list[float] | dict[str, list[float]]: Current learning rates of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - if isinstance(self.optimizer, torch.optim.Optimizer): - lr = [group['lr'] for group in self.optimizer.param_groups] - elif isinstance(self.optimizer, dict): - lr = dict() - for name, optim in self.optimizer.items(): - lr[name] = [group['lr'] for group in optim.param_groups] - else: - raise RuntimeError( - 'lr is not applicable because optimizer does not exist.') - return lr - - def current_momentum(self): - """Get current momentums. - - Returns: - list[float] | dict[str, list[float]]: Current momentums of all - param groups. If the runner has a dict of optimizers, this method - will return a dict. - """ - - def _get_momentum(optimizer): - momentums = [] - for group in optimizer.param_groups: - if 'momentum' in group.keys(): - momentums.append(group['momentum']) - elif 'betas' in group.keys(): - momentums.append(group['betas'][0]) - else: - momentums.append(0) - return momentums - - if self.optimizer is None: - raise RuntimeError( - 'momentum is not applicable because optimizer does not exist.') - elif isinstance(self.optimizer, torch.optim.Optimizer): - momentums = _get_momentum(self.optimizer) - elif isinstance(self.optimizer, dict): - momentums = dict() - for name, optim in self.optimizer.items(): - momentums[name] = _get_momentum(optim) - return momentums - - def register_hook(self, hook, priority='NORMAL'): - """Register a hook into the hook list. - - The hook will be inserted into a priority queue, with the specified - priority (See :class:`Priority` for details of priorities). - For hooks with the same priority, they will be triggered in the same - order as they are registered. - - Args: - hook (:obj:`Hook`): The hook to be registered. - priority (int or str or :obj:`Priority`): Hook priority. - Lower value means higher priority. - """ - assert isinstance(hook, Hook) - if hasattr(hook, 'priority'): - raise ValueError('"priority" is a reserved attribute for hooks') - priority = get_priority(priority) - hook.priority = priority - # insert the hook to a sorted list - inserted = False - for i in range(len(self._hooks) - 1, -1, -1): - if priority >= self._hooks[i].priority: - self._hooks.insert(i + 1, hook) - inserted = True - break - if not inserted: - self._hooks.insert(0, hook) - - def register_hook_from_cfg(self, hook_cfg): - """Register a hook from its cfg. - - Args: - hook_cfg (dict): Hook config. It should have at least keys 'type' - and 'priority' indicating its type and priority. - - Note: - The specific hook class to register should not use 'type' and - 'priority' arguments during initialization. - """ - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = mmcv.build_from_cfg(hook_cfg, HOOKS) - self.register_hook(hook, priority=priority) - - def call_hook(self, fn_name): - """Call all hooks. - - Args: - fn_name (str): The function name in each hook to be called, such as - "before_train_epoch". - """ - for hook in self._hooks: - getattr(hook, fn_name)(self) - - def get_hook_info(self): - # Get hooks info in each stage - stage_hook_map = {stage: [] for stage in Hook.stages} - for hook in self.hooks: - try: - priority = Priority(hook.priority).name - except ValueError: - priority = hook.priority - classname = hook.__class__.__name__ - hook_info = f'({priority:<12}) {classname:<35}' - for trigger_stage in hook.get_triggered_stages(): - stage_hook_map[trigger_stage].append(hook_info) - - stage_hook_infos = [] - for stage in Hook.stages: - hook_infos = stage_hook_map[stage] - if len(hook_infos) > 0: - info = f'{stage}:\n' - info += '\n'.join(hook_infos) - info += '\n -------------------- ' - stage_hook_infos.append(info) - return '\n'.join(stage_hook_infos) - - def load_checkpoint(self, - filename, - map_location='cpu', - strict=False, - revise_keys=[(r'^module.', '')]): - return load_checkpoint( - self.model, - filename, - map_location, - strict, - self.logger, - revise_keys=revise_keys) - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - if map_location == 'default': - if torch.cuda.is_available(): - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint(checkpoint) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - if self.meta is None: - self.meta = {} - self.meta.setdefault('hook_msgs', {}) - # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages - self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) - - # Re-calculate the number of iterations when resuming - # models with different number of GPUs - if 'config' in checkpoint['meta']: - config = mmcv.Config.fromstring( - checkpoint['meta']['config'], file_format='.py') - previous_gpu_ids = config.get('gpu_ids', None) - if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( - previous_gpu_ids) != self.world_size: - self._iter = int(self._iter * len(previous_gpu_ids) / - self.world_size) - self.logger.info('the iteration number is changed due to ' - 'change of GPU number') - - # resume meta information meta - self.meta = checkpoint['meta'] - - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - elif isinstance(lr_config, dict): - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = mmcv.build_from_cfg(lr_config, HOOKS) - else: - hook = lr_config - self.register_hook(hook, priority='VERY_HIGH') - - def register_momentum_hook(self, momentum_config): - if momentum_config is None: - return - if isinstance(momentum_config, dict): - assert 'policy' in momentum_config - policy_type = momentum_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of momentum updater. - # Since this is not applicable for - # `CosineAnnealingMomentumUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'MomentumUpdaterHook' - momentum_config['type'] = hook_type - hook = mmcv.build_from_cfg(momentum_config, HOOKS) - else: - hook = momentum_config - self.register_hook(hook, priority='HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = mmcv.build_from_cfg(optimizer_config, HOOKS) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def register_checkpoint_hook(self, checkpoint_config): - if checkpoint_config is None: - return - if isinstance(checkpoint_config, dict): - checkpoint_config.setdefault('type', 'CheckpointHook') - hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) - else: - hook = checkpoint_config - self.register_hook(hook, priority='NORMAL') - - def register_logger_hooks(self, log_config): - if log_config is None: - return - log_interval = log_config['interval'] - for info in log_config['hooks']: - logger_hook = mmcv.build_from_cfg( - info, HOOKS, default_args=dict(interval=log_interval)) - self.register_hook(logger_hook, priority='VERY_LOW') - - def register_timer_hook(self, timer_config): - if timer_config is None: - return - if isinstance(timer_config, dict): - timer_config_ = copy.deepcopy(timer_config) - hook = mmcv.build_from_cfg(timer_config_, HOOKS) - else: - hook = timer_config - self.register_hook(hook, priority='LOW') - - def register_custom_hooks(self, custom_config): - if custom_config is None: - return - - if not isinstance(custom_config, list): - custom_config = [custom_config] - - for item in custom_config: - if isinstance(item, dict): - self.register_hook_from_cfg(item) - else: - self.register_hook(item, priority='NORMAL') - - def register_profiler_hook(self, profiler_config): - if profiler_config is None: - return - if isinstance(profiler_config, dict): - profiler_config.setdefault('type', 'ProfilerHook') - hook = mmcv.build_from_cfg(profiler_config, HOOKS) - else: - hook = profiler_config - self.register_hook(hook) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - timer_config=dict(type='IterTimerHook'), - custom_hooks_config=None): - """Register default and custom hooks for training. - - Default and custom hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - self.register_lr_hook(lr_config) - self.register_momentum_hook(momentum_config) - self.register_optimizer_hook(optimizer_config) - self.register_checkpoint_hook(checkpoint_config) - self.register_timer_hook(timer_config) - self.register_logger_hooks(log_config) - self.register_custom_hooks(custom_hooks_config) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/builder.py b/cv/super_resolution/liif/pytorch/mmcv/runner/builder.py deleted file mode 100644 index 008da32aa..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/builder.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -from typing import Optional - -from ..utils import Registry - -RUNNERS = Registry('runner') -RUNNER_BUILDERS = Registry('runner builder') - - -def build_runner_constructor(cfg: dict): - return RUNNER_BUILDERS.build(cfg) - - -def build_runner(cfg: dict, default_args: Optional[dict] = None): - runner_cfg = copy.deepcopy(cfg) - constructor_type = runner_cfg.pop('constructor', - 'DefaultRunnerConstructor') - runner_constructor = build_runner_constructor( - dict( - type=constructor_type, - runner_cfg=runner_cfg, - default_args=default_args)) - runner = runner_constructor() - return runner diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/checkpoint.py b/cv/super_resolution/liif/pytorch/mmcv/runner/checkpoint.py deleted file mode 100644 index 693dee934..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/checkpoint.py +++ /dev/null @@ -1,800 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import logging -import os -import os.path as osp -import pkgutil -import re -import time -import warnings -from collections import OrderedDict -from importlib import import_module -from tempfile import TemporaryDirectory -from typing import Callable, List, Optional, Sequence, Union - -import torch -import torchvision -from torch.optim import Optimizer - -import mmcv -from ..fileio import FileClient -from ..fileio import load as load_file -from ..parallel import is_module_wrapper -from ..utils import digit_version, load_url, mkdir_or_exist -from .dist_utils import get_dist_info - -ENV_MMCV_HOME = 'MMCV_HOME' -ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' -DEFAULT_CACHE_DIR = '~/.cache' - - -def _get_mmcv_home(): - mmcv_home = os.path.expanduser( - os.getenv( - ENV_MMCV_HOME, - os.path.join( - os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) - - mkdir_or_exist(mmcv_home) - return mmcv_home - - -def load_state_dict(module: torch.nn.Module, - state_dict: Union[dict, OrderedDict], - strict: bool = False, - logger: Optional[logging.Logger] = None) -> None: - """Load state_dict to a module. - - This method is modified from :meth:`torch.nn.Module.load_state_dict`. - Default value for ``strict`` is set to ``False`` and the message for - param mismatch will be shown even if strict is False. - - Args: - module (Module): Module that receives the state_dict. - state_dict (OrderedDict): Weights. - strict (bool): whether to strictly enforce that the keys - in :attr:`state_dict` match the keys returned by this module's - :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. - logger (:obj:`logging.Logger`, optional): Logger to log the error - message. If not specified, print function will be used. - """ - unexpected_keys: List = [] - all_missing_keys: List = [] - err_msg: List = [] - - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata # type: ignore - - # use _load_from_state_dict to enable checkpoint version control - def load(module, prefix=''): - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - local_metadata = {} if metadata is None else metadata.get( - prefix[:-1], {}) - module._load_from_state_dict(state_dict, prefix, local_metadata, True, - all_missing_keys, unexpected_keys, - err_msg) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(module) - # break load->load reference cycle - load = None # type: ignore - - # ignore "num_batches_tracked" of BN layers - missing_keys = [ - key for key in all_missing_keys if 'num_batches_tracked' not in key - ] - - if unexpected_keys: - err_msg.append('unexpected key in source ' - f'state_dict: {", ".join(unexpected_keys)}\n') - if missing_keys: - err_msg.append( - f'missing keys in source state_dict: {", ".join(missing_keys)}\n') - - rank, _ = get_dist_info() - if len(err_msg) > 0 and rank == 0: - err_msg.insert( - 0, 'The model and loaded state dict do not match exactly\n') - err_msg = '\n'.join(err_msg) # type: ignore - if strict: - raise RuntimeError(err_msg) - elif logger is not None: - logger.warning(err_msg) - else: - print(err_msg) - - -def get_torchvision_models(): - if digit_version(torchvision.__version__) < digit_version('0.13.0a0'): - model_urls = dict() - # When the version of torchvision is lower than 0.13, the model url is - # not declared in `torchvision.model.__init__.py`, so we need to - # iterate through `torchvision.models.__path__` to get the url for each - # model. - for _, name, ispkg in pkgutil.walk_packages( - torchvision.models.__path__): - if ispkg: - continue - _zoo = import_module(f'torchvision.models.{name}') - if hasattr(_zoo, 'model_urls'): - _urls = getattr(_zoo, 'model_urls') - model_urls.update(_urls) - else: - # Since torchvision bumps to v0.13, the weight loading logic, - # model keys and model urls have been changed. Here the URLs of old - # version is loaded to avoid breaking back compatibility. If the - # torchvision version>=0.13.0, new URLs will be added. Users can get - # the resnet50 checkpoint by setting 'resnet50.imagent1k_v1', - # 'resnet50' or 'ResNet50_Weights.IMAGENET1K_V1' in the config. - json_path = osp.join(mmcv.__path__[0], - 'model_zoo/torchvision_0.12.json') - model_urls = mmcv.load(json_path) - for cls_name, cls in torchvision.models.__dict__.items(): - # The name of torchvision model weights classes ends with - # `_Weights` such as `ResNet18_Weights`. However, some model weight - # classes, such as `MNASNet0_75_Weights` does not have any urls in - # torchvision 0.13.0 and cannot be iterated. Here we simply check - # `DEFAULT` attribute to ensure the class is not empty. - if (not cls_name.endswith('_Weights') - or not hasattr(cls, 'DEFAULT')): - continue - # Since `cls.DEFAULT` can not be accessed by iterating cls, we set - # default urls explicitly. - cls_key = cls_name.replace('_Weights', '').lower() - model_urls[f'{cls_key}.default'] = cls.DEFAULT.url - for weight_enum in cls: - cls_key = cls_name.replace('_Weights', '').lower() - cls_key = f'{cls_key}.{weight_enum.name.lower()}' - model_urls[cls_key] = weight_enum.url - - return model_urls - - -def get_external_models(): - mmcv_home = _get_mmcv_home() - default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') - default_urls = load_file(default_json_path) - assert isinstance(default_urls, dict) - external_json_path = osp.join(mmcv_home, 'open_mmlab.json') - if osp.exists(external_json_path): - external_urls = load_file(external_json_path) - assert isinstance(external_urls, dict) - default_urls.update(external_urls) - - return default_urls - - -def get_mmcls_models(): - mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') - mmcls_urls = load_file(mmcls_json_path) - - return mmcls_urls - - -def get_deprecated_model_names(): - deprecate_json_path = osp.join(mmcv.__path__[0], - 'model_zoo/deprecated.json') - deprecate_urls = load_file(deprecate_json_path) - assert isinstance(deprecate_urls, dict) - - return deprecate_urls - - -def _process_mmcls_checkpoint(checkpoint): - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - # Some checkpoints converted from 3rd-party repo don't - # have the "state_dict" key. - state_dict = checkpoint - new_state_dict = OrderedDict() - for k, v in state_dict.items(): - if k.startswith('backbone.'): - new_state_dict[k[9:]] = v - new_checkpoint = dict(state_dict=new_state_dict) - - return new_checkpoint - - -class CheckpointLoader: - """A general checkpoint loader to manage all schemes.""" - - _schemes: dict = {} - - @classmethod - def _register_scheme(cls, prefixes, loader, force=False): - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if (prefix not in cls._schemes) or force: - cls._schemes[prefix] = loader - else: - raise KeyError( - f'{prefix} is already registered as a loader backend, ' - 'add "force=True" if you want to override it') - # sort, longer prefixes take priority - cls._schemes = OrderedDict( - sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) - - @classmethod - def register_scheme(cls, - prefixes: Union[str, Sequence[str]], - loader: Optional[Callable] = None, - force: bool = False): - """Register a loader to CheckpointLoader. - - This method can be used as a normal class method or a decorator. - - Args: - prefixes (str or Sequence[str]): - The prefix of the registered loader. - loader (function, optional): The loader function to be registered. - When this method is used as a decorator, loader is None. - Defaults to None. - force (bool, optional): Whether to override the loader - if the prefix has already been registered. Defaults to False. - """ - - if loader is not None: - cls._register_scheme(prefixes, loader, force=force) - return - - def _register(loader_cls): - cls._register_scheme(prefixes, loader_cls, force=force) - return loader_cls - - return _register - - @classmethod - def _get_checkpoint_loader(cls, path): - """Finds a loader that supports the given path. Falls back to the local - loader if no other loader is found. - - Args: - path (str): checkpoint path - - Returns: - callable: checkpoint loader - """ - for p in cls._schemes: - # use regular match to handle some cases that where the prefix of - # loader has a prefix. For example, both 's3://path' and - # 'open-mmlab:s3://path' should return `load_from_ceph` - if re.match(p, path) is not None: - return cls._schemes[p] - - @classmethod - def load_checkpoint( - cls, - filename: str, - map_location: Optional[str] = None, - logger: Optional[logging.Logger] = None - ) -> Union[dict, OrderedDict]: - """load checkpoint through URL scheme path. - - Args: - filename (str): checkpoint file name with given prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - logger (:mod:`logging.Logger`, optional): The logger for message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint_loader = cls._get_checkpoint_loader(filename) - class_name = checkpoint_loader.__name__ - mmcv.print_log( - f'load checkpoint from {class_name[10:]} path: {filename}', logger) - return checkpoint_loader(filename, map_location) - - -@CheckpointLoader.register_scheme(prefixes='') -def load_from_local( - filename: str, - map_location: Optional[str] = None) -> Union[dict, OrderedDict]: - """load checkpoint by local file path. - - Args: - filename (str): local checkpoint file path - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - filename = osp.expanduser(filename) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) -def load_from_http( - filename: str, - map_location: Optional[str] = None, - model_dir: Optional[str] = None) -> Union[dict, OrderedDict]: - """load checkpoint through HTTP or HTTPS scheme path. In distributed - setting, this function only download checkpoint at local rank 0. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - model_dir (str, optional): directory in which to save the object, - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - rank, world_size = get_dist_info() - if rank == 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - if world_size > 1: - torch.distributed.barrier() - if rank > 0: - checkpoint = load_url( - filename, model_dir=model_dir, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='pavi://') -def load_from_pavi( - filename: str, - map_location: Optional[str] = None) -> Union[dict, OrderedDict]: - """load checkpoint through the file path prefixed with pavi. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with pavi prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - assert filename.startswith('pavi://'), \ - f'Expected filename startswith `pavi://`, but get {filename}' - model_path = filename[7:] - - try: - from pavi import modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - - model = modelcloud.get(model_path) - with TemporaryDirectory() as tmp_dir: - downloaded_file = osp.join(tmp_dir, model.name) - model.download(downloaded_file) - checkpoint = torch.load(downloaded_file, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=r'(\S+\:)?s3://') -def load_from_ceph(filename: str, - map_location: Optional[str] = None, - backend: str = 'petrel') -> Union[dict, OrderedDict]: - """load checkpoint through the file path prefixed with s3. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Note: - Since v1.4.1, the registered scheme prefixes have been enhanced to - support bucket names in the path prefix, e.g. 's3://xx.xx/xx.path', - 'bucket1:s3://xx.xx/xx.path'. - - Args: - filename (str): checkpoint file path with s3 prefix - map_location (str, optional): Same as :func:`torch.load`. - backend (str): The storage backend type. Options are 'ceph', - 'petrel'. Default: 'petrel'. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - allowed_backends = ['ceph', 'petrel'] - if backend not in allowed_backends: - raise ValueError(f'Load from Backend {backend} is not supported.') - - if backend == 'ceph': - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead', - DeprecationWarning) - - # CephClient and PetrelBackend have the same prefix 's3://' and the latter - # will be chosen as default. If PetrelBackend can not be instantiated - # successfully, the CephClient will be chosen. - try: - file_client = FileClient(backend=backend) - except ImportError: - allowed_backends.remove(backend) - file_client = FileClient(backend=allowed_backends[0]) - - with io.BytesIO(file_client.get(filename)) as buffer: - checkpoint = torch.load(buffer, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) -def load_from_torchvision( - filename: str, - map_location: Optional[str] = None) -> Union[dict, OrderedDict]: - """load checkpoint through the file path prefixed with modelzoo or - torchvision. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - model_urls = get_torchvision_models() - if filename.startswith('modelzoo://'): - warnings.warn( - 'The URL scheme of "modelzoo://" is deprecated, please ' - 'use "torchvision://" instead', DeprecationWarning) - model_name = filename[11:] - else: - model_name = filename[14:] - - # Support getting model urls in the same way as torchvision - # `ResNet50_Weights.IMAGENET1K_V1` will be mapped to - # resnet50.imagenet1k_v1. - model_name = model_name.lower().replace('_weights', '') - return load_from_http(model_urls[model_name], map_location=map_location) - - -@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) -def load_from_openmmlab( - filename: str, - map_location: Optional[str] = None) -> Union[dict, OrderedDict]: - """load checkpoint through the file path prefixed with open-mmlab or - openmmlab. - - Args: - filename (str): checkpoint file path with open-mmlab or - openmmlab prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_external_models() - prefix_str = 'open-mmlab://' - if filename.startswith(prefix_str): - model_name = filename[13:] - else: - model_name = filename[12:] - prefix_str = 'openmmlab://' - - deprecated_urls = get_deprecated_model_names() - if model_name in deprecated_urls: - warnings.warn( - f'{prefix_str}{model_name} is deprecated in favor ' - f'of {prefix_str}{deprecated_urls[model_name]}', - DeprecationWarning) - model_name = deprecated_urls[model_name] - model_url = model_urls[model_name] - # check if is url - if model_url.startswith(('http://', 'https://')): - checkpoint = load_from_http(model_url, map_location=map_location) - else: - filename = osp.join(_get_mmcv_home(), model_url) - if not osp.isfile(filename): - raise FileNotFoundError(f'{filename} can not be found.') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='mmcls://') -def load_from_mmcls( - filename: str, - map_location: Optional[str] = None) -> Union[dict, OrderedDict]: - """load checkpoint through the file path prefixed with mmcls. - - Args: - filename (str): checkpoint file path with mmcls prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_mmcls_models() - model_name = filename[8:] - checkpoint = load_from_http( - model_urls[model_name], map_location=map_location) - checkpoint = _process_mmcls_checkpoint(checkpoint) - return checkpoint - - -def _load_checkpoint( - filename: str, - map_location: Optional[str] = None, - logger: Optional[logging.Logger] = None) -> Union[dict, OrderedDict]: - """Load checkpoint from somewhere (modelzoo, file, url). - - Args: - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str, optional): Same as :func:`torch.load`. - Default: None. - logger (:mod:`logging.Logger`, optional): The logger for error message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. It can be either an - OrderedDict storing model weights or a dict containing other - information, which depends on the checkpoint. - """ - return CheckpointLoader.load_checkpoint(filename, map_location, logger) - - -def _load_checkpoint_with_prefix( - prefix: str, - filename: str, - map_location: Optional[str] = None) -> Union[dict, OrderedDict]: - """Load partial pretrained model with specific prefix. - - Args: - prefix (str): The prefix of sub-module. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str | None): Same as :func:`torch.load`. Default: None. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint = _load_checkpoint(filename, map_location=map_location) - - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - if not prefix.endswith('.'): - prefix += '.' - prefix_len = len(prefix) - - state_dict = { - k[prefix_len:]: v - for k, v in state_dict.items() if k.startswith(prefix) - } - - assert state_dict, f'{prefix} is not in the pretrained model' - return state_dict - - -def load_checkpoint( - model: torch.nn.Module, - filename: str, - map_location: Optional[str] = None, - strict: bool = False, - logger: Optional[logging.Logger] = None, - revise_keys: list = [(r'^module\.', '')]) -> Union[dict, OrderedDict]: - """Load checkpoint from a file or URI. - - Args: - model (Module): Module to load checkpoint. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str): Same as :func:`torch.load`. - strict (bool): Whether to allow different params for the model and - checkpoint. - logger (:mod:`logging.Logger` or None): The logger for error message. - revise_keys (list): A list of customized keywords to modify the - state_dict in checkpoint. Each item is a (pattern, replacement) - pair of the regular expression operations. Default: strip - the prefix 'module.' by [(r'^module\\.', '')]. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - checkpoint = _load_checkpoint(filename, map_location, logger) - # OrderedDict is a subclass of dict - if not isinstance(checkpoint, dict): - raise RuntimeError( - f'No state_dict found in checkpoint file {filename}') - # get state_dict from checkpoint - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - - # strip prefix of state_dict - metadata = getattr(state_dict, '_metadata', OrderedDict()) - for p, r in revise_keys: - state_dict = OrderedDict( - {re.sub(p, r, k): v - for k, v in state_dict.items()}) - # Keep metadata in state_dict - state_dict._metadata = metadata - - # load state_dict - load_state_dict(model, state_dict, strict, logger) - return checkpoint - - -def weights_to_cpu(state_dict: OrderedDict) -> OrderedDict: - """Copy a model state_dict to cpu. - - Args: - state_dict (OrderedDict): Model weights on GPU. - - Returns: - OrderedDict: Model weights on GPU. - """ - state_dict_cpu = OrderedDict() - for key, val in state_dict.items(): - state_dict_cpu[key] = val.cpu() - # Keep metadata in state_dict - state_dict_cpu._metadata = getattr( # type: ignore - state_dict, '_metadata', OrderedDict()) - return state_dict_cpu - - -def _save_to_state_dict(module: torch.nn.Module, destination: dict, - prefix: str, keep_vars: bool) -> None: - """Saves module state to `destination` dictionary. - - This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. - - Args: - module (nn.Module): The module to generate state_dict. - destination (dict): A dict where state will be stored. - prefix (str): The prefix for parameters and buffers used in this - module. - """ - for name, param in module._parameters.items(): - if param is not None: - destination[prefix + name] = param if keep_vars else param.detach() - for name, buf in module._buffers.items(): - # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d - if buf is not None: - destination[prefix + name] = buf if keep_vars else buf.detach() - - -def get_state_dict(module: torch.nn.Module, - destination: Optional[OrderedDict] = None, - prefix: str = '', - keep_vars: bool = False) -> OrderedDict: - """Returns a dictionary containing a whole state of the module. - - Both parameters and persistent buffers (e.g. running averages) are - included. Keys are corresponding parameter and buffer names. - - This method is modified from :meth:`torch.nn.Module.state_dict` to - recursively check parallel module in case that the model has a complicated - structure, e.g., nn.Module(nn.Module(DDP)). - - Args: - module (nn.Module): The module to generate state_dict. - destination (OrderedDict): Returned dict for the state of the - module. - prefix (str): Prefix of the key. - keep_vars (bool): Whether to keep the variable property of the - parameters. Default: False. - - Returns: - dict: A dictionary containing a whole state of the module. - """ - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - - # below is the same as torch.nn.Module.state_dict() - if destination is None: - destination = OrderedDict() - destination._metadata = OrderedDict() # type: ignore - destination._metadata[prefix[:-1]] = local_metadata = dict( # type: ignore - version=module._version) - _save_to_state_dict(module, destination, prefix, keep_vars) - for name, child in module._modules.items(): - if child is not None: - get_state_dict( - child, destination, prefix + name + '.', keep_vars=keep_vars) - for hook in module._state_dict_hooks.values(): - hook_result = hook(module, destination, prefix, local_metadata) - if hook_result is not None: - destination = hook_result - return destination # type: ignore - - -def save_checkpoint(model: torch.nn.Module, - filename: str, - optimizer: Optional[Optimizer] = None, - meta: Optional[dict] = None, - file_client_args: Optional[dict] = None) -> None: - """Save checkpoint to file. - - The checkpoint will have 3 fields: ``meta``, ``state_dict`` and - ``optimizer``. By default ``meta`` will contain version and time info. - - Args: - model (Module): Module whose params are to be saved. - filename (str): Checkpoint filename. - optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. - meta (dict, optional): Metadata to be saved in checkpoint. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError(f'meta must be a dict or None, but got {type(meta)}') - meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) - - if is_module_wrapper(model): - model = model.module - - if hasattr(model, 'CLASSES') and model.CLASSES is not None: - # save class name to the meta - meta.update(CLASSES=model.CLASSES) - - checkpoint = { - 'meta': meta, - 'state_dict': weights_to_cpu(get_state_dict(model)) - } - # save optimizer state dict in the checkpoint - if isinstance(optimizer, Optimizer): - checkpoint['optimizer'] = optimizer.state_dict() - elif isinstance(optimizer, dict): - checkpoint['optimizer'] = {} - for name, optim in optimizer.items(): - checkpoint['optimizer'][name] = optim.state_dict() - - if filename.startswith('pavi://'): - if file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" if filename starts with' - f'"pavi://", but got {file_client_args}') - try: - from pavi import exception, modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - model_path = filename[7:] - root = modelcloud.Folder() - model_dir, model_name = osp.split(model_path) - try: - model = modelcloud.get(model_dir) - except exception.NodeNotFoundError: - model = root.create_training_model(model_dir) - with TemporaryDirectory() as tmp_dir: - checkpoint_file = osp.join(tmp_dir, model_name) - with open(checkpoint_file, 'wb') as f: - torch.save(checkpoint, f) - f.flush() - model.create_file(checkpoint_file, name=model_name) - else: - file_client = FileClient.infer_client(file_client_args, filename) - with io.BytesIO() as f: - torch.save(checkpoint, f) - file_client.put(f.getvalue(), filename) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/default_constructor.py b/cv/super_resolution/liif/pytorch/mmcv/runner/default_constructor.py deleted file mode 100644 index 394b51cfd..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/default_constructor.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from typing import Optional - -from .builder import RUNNER_BUILDERS, RUNNERS - - -@RUNNER_BUILDERS.register_module() -class DefaultRunnerConstructor: - """Default constructor for runners. - - Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. - For example, We can inject some new properties and functions for `Runner`. - - Example: - >>> from mmcv.runner import RUNNER_BUILDERS, build_runner - >>> # Define a new RunnerReconstructor - >>> @RUNNER_BUILDERS.register_module() - >>> class MyRunnerConstructor: - ... def __init__(self, runner_cfg, default_args=None): - ... if not isinstance(runner_cfg, dict): - ... raise TypeError('runner_cfg should be a dict', - ... f'but got {type(runner_cfg)}') - ... self.runner_cfg = runner_cfg - ... self.default_args = default_args - ... - ... def __call__(self): - ... runner = RUNNERS.build(self.runner_cfg, - ... default_args=self.default_args) - ... # Add new properties for existing runner - ... runner.my_name = 'my_runner' - ... runner.my_function = lambda self: print(self.my_name) - ... ... - >>> # build your runner - >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, - ... constructor='MyRunnerConstructor') - >>> runner = build_runner(runner_cfg) - """ - - def __init__(self, runner_cfg: dict, default_args: Optional[dict] = None): - if not isinstance(runner_cfg, dict): - raise TypeError('runner_cfg should be a dict', - f'but got {type(runner_cfg)}') - self.runner_cfg = runner_cfg - self.default_args = default_args - - def __call__(self): - return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/dist_utils.py b/cv/super_resolution/liif/pytorch/mmcv/runner/dist_utils.py deleted file mode 100644 index abed57d2c..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/dist_utils.py +++ /dev/null @@ -1,209 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved -import functools -import os -import socket -import subprocess -from collections import OrderedDict -from typing import Callable, List, Optional, Tuple - -import torch -import torch.multiprocessing as mp -from torch import distributed as dist -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import IS_MLU_AVAILABLE - - -def _find_free_port(): - # Copied from https://github.com/facebookresearch/detectron2/blob/main/detectron2/engine/launch.py # noqa: E501 - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - # Binding to port 0 will cause the OS to find an available port for us - sock.bind(('', 0)) - port = sock.getsockname()[1] - sock.close() - # NOTE: there is still a chance the port could be taken by other processes. - return port - - -def _is_free_port(port): - ips = socket.gethostbyname_ex(socket.gethostname())[-1] - ips.append('localhost') - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - return all(s.connect_ex((ip, port)) != 0 for ip in ips) - - -def init_dist(launcher: str, backend: str = 'nccl', **kwargs) -> None: - if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') - if launcher == 'pytorch': - _init_dist_pytorch(backend, **kwargs) - elif launcher == 'mpi': - _init_dist_mpi(backend, **kwargs) - elif launcher == 'slurm': - _init_dist_slurm(backend, **kwargs) - else: - raise ValueError(f'Invalid launcher type: {launcher}') - - -def _init_dist_pytorch(backend: str, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['RANK']) - if IS_MLU_AVAILABLE: - import torch_mlu # noqa: F401 - torch.mlu.set_device(rank) - dist.init_process_group( - backend='cncl', - rank=rank, - world_size=int(os.environ['WORLD_SIZE']), - **kwargs) - else: - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_mpi(backend: str, **kwargs): - local_rank = int(os.environ['OMPI_COMM_WORLD_LOCAL_RANK']) - torch.cuda.set_device(local_rank) - if 'MASTER_PORT' not in os.environ: - # 29500 is torch.distributed default port - os.environ['MASTER_PORT'] = '29500' - if 'MASTER_ADDR' not in os.environ: - raise KeyError('The environment variable MASTER_ADDR is not set') - os.environ['WORLD_SIZE'] = os.environ['OMPI_COMM_WORLD_SIZE'] - os.environ['RANK'] = os.environ['OMPI_COMM_WORLD_RANK'] - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_slurm(backend: str, port: Optional[int] = None): - """Initialize slurm distributed training environment. - - If argument ``port`` is not specified, then the master port will be system - environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system - environment variable, then a default port ``29500`` will be used. - - Args: - backend (str): Backend of torch.distributed. - port (int, optional): Master port. Defaults to None. - """ - proc_id = int(os.environ['SLURM_PROCID']) - ntasks = int(os.environ['SLURM_NTASKS']) - node_list = os.environ['SLURM_NODELIST'] - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(proc_id % num_gpus) - addr = subprocess.getoutput( - f'scontrol show hostname {node_list} | head -n1') - # specify master port - if port is not None: - os.environ['MASTER_PORT'] = str(port) - elif 'MASTER_PORT' in os.environ: - pass # use MASTER_PORT in the environment variable - else: - # if torch.distributed default port(29500) is available - # then use it, else find a free port - if _is_free_port(29500): - os.environ['MASTER_PORT'] = '29500' - else: - os.environ['MASTER_PORT'] = str(_find_free_port()) - # use MASTER_ADDR in the environment variable if it already exists - if 'MASTER_ADDR' not in os.environ: - os.environ['MASTER_ADDR'] = addr - os.environ['WORLD_SIZE'] = str(ntasks) - os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) - os.environ['RANK'] = str(proc_id) - dist.init_process_group(backend=backend) - - -def get_dist_info() -> Tuple[int, int]: - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - world_size = dist.get_world_size() - else: - rank = 0 - world_size = 1 - return rank, world_size - - -def master_only(func: Callable) -> Callable: - - @functools.wraps(func) - def wrapper(*args, **kwargs): - rank, _ = get_dist_info() - if rank == 0: - return func(*args, **kwargs) - - return wrapper - - -def allreduce_params(params: List[torch.nn.Parameter], - coalesce: bool = True, - bucket_size_mb: int = -1) -> None: - """Allreduce parameters. - - Args: - params (list[torch.nn.Parameter]): List of parameters or buffers - of a model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - _, world_size = get_dist_info() - if world_size == 1: - return - params = [param.data for param in params] - if coalesce: - _allreduce_coalesced(params, world_size, bucket_size_mb) - else: - for tensor in params: - dist.all_reduce(tensor.div_(world_size)) - - -def allreduce_grads(params: List[torch.nn.Parameter], - coalesce: bool = True, - bucket_size_mb: int = -1) -> None: - """Allreduce gradients. - - Args: - params (list[torch.nn.Parameter]): List of parameters of a model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - _, world_size = get_dist_info() - if world_size == 1: - return - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/epoch_based_runner.py b/cv/super_resolution/liif/pytorch/mmcv/runner/epoch_based_runner.py deleted file mode 100644 index 7a1b10302..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/epoch_based_runner.py +++ /dev/null @@ -1,191 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .utils import get_host_info - - -@RUNNERS.register_module() -class EpochBasedRunner(BaseRunner): - """Epoch-based Runner. - - This runner train models epoch by epoch. - """ - - def run_iter(self, data_batch, train_mode, **kwargs): - if self.batch_processor is not None: - outputs = self.batch_processor( - self.model, data_batch, train_mode=train_mode, **kwargs) - elif train_mode: - outputs = self.model.train_step(data_batch, self.optimizer, - **kwargs) - else: - outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('"batch_processor()" or "model.train_step()"' - 'and "model.val_step()" must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self.data_batch = data_batch - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - del self.data_batch - self._iter += 1 - - self.call_hook('after_train_epoch') - self._epoch += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - self.call_hook('before_val_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self.data_batch = data_batch - self._inner_iter = i - self.call_hook('before_val_iter') - self.run_iter(data_batch, train_mode=False) - self.call_hook('after_val_iter') - del self.data_batch - self.call_hook('after_val_epoch') - - def run(self, data_loaders, workflow, max_epochs=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, epochs) to specify the - running order and epochs. E.g, [('train', 2), ('val', 1)] means - running 2 epochs for training and 1 epoch for validation, - iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_epochs is not None: - warnings.warn( - 'setting max_epochs in run is deprecated, ' - 'please set max_epochs in runner_config', DeprecationWarning) - self._max_epochs = max_epochs - - assert self._max_epochs is not None, ( - 'max_epochs must be specified during instantiation') - - for i, flow in enumerate(workflow): - mode, epochs = flow - if mode == 'train': - self._max_iters = self._max_epochs * len(data_loaders[i]) - break - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d epochs', workflow, - self._max_epochs) - self.call_hook('before_run') - - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - mode, epochs = flow - if isinstance(mode, str): # self.train() - if not hasattr(self, mode): - raise ValueError( - f'runner has no method named "{mode}" to run an ' - 'epoch') - epoch_runner = getattr(self, mode) - else: - raise TypeError( - 'mode in workflow must be a str, but got {}'.format( - type(mode))) - - for _ in range(epochs): - if mode == 'train' and self.epoch >= self._max_epochs: - break - epoch_runner(data_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_run') - - def save_checkpoint(self, - out_dir, - filename_tmpl='epoch_{}.pth', - save_optimizer=True, - meta=None, - create_symlink=True): - """Save the checkpoint. - - Args: - out_dir (str): The directory that checkpoints are saved. - filename_tmpl (str, optional): The checkpoint filename template, - which contains a placeholder for the epoch number. - Defaults to 'epoch_{}.pth'. - save_optimizer (bool, optional): Whether to save the optimizer to - the checkpoint. Defaults to True. - meta (dict, optional): The meta information to be saved in the - checkpoint. Defaults to None. - create_symlink (bool, optional): Whether to create a symlink - "latest.pth" to point to the latest checkpoint. - Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.epoch + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - -@RUNNERS.register_module() -class Runner(EpochBasedRunner): - """Deprecated name of EpochBasedRunner.""" - - def __init__(self, *args, **kwargs): - warnings.warn( - 'Runner was deprecated, please use EpochBasedRunner instead', - DeprecationWarning) - super().__init__(*args, **kwargs) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/fp16_utils.py b/cv/super_resolution/liif/pytorch/mmcv/runner/fp16_utils.py deleted file mode 100644 index 9deb228a5..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/fp16_utils.py +++ /dev/null @@ -1,435 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import warnings -from collections import abc -from inspect import getfullargspec -from typing import Callable, Iterable, List, Optional - -import numpy as np -import torch -import torch.nn as nn -from torch.nn.parameter import Parameter - -from mmcv.utils import TORCH_VERSION, digit_version -from .dist_utils import allreduce_grads as _allreduce_grads - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 - # manually, so the behavior may not be consistent with real amp. - from torch.cuda.amp import autocast -except ImportError: - pass - - -def cast_tensor_type(inputs, src_type: torch.dtype, dst_type: torch.dtype): - """Recursively convert Tensor in inputs from src_type to dst_type. - - Note: - In v1.4.4 and later, ``cast_tersor_type`` will only convert the - torch.Tensor which is consistent with ``src_type`` to the ``dst_type``. - Before v1.4.4, it ignores the ``src_type`` argument, leading to some - potential problems. For example, - ``cast_tensor_type(inputs, torch.float, torch.half)`` will convert all - tensors in inputs to ``torch.half`` including those originally in - ``torch.Int`` or other types, which is not expected. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype): Source type.. - dst_type (torch.dtype): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - if isinstance(inputs, nn.Module): - return inputs - elif isinstance(inputs, torch.Tensor): - # we need to ensure that the type of inputs to be casted are the same - # as the argument `src_type`. - return inputs.to(dst_type) if inputs.dtype == src_type else inputs - elif isinstance(inputs, str): - return inputs - elif isinstance(inputs, np.ndarray): - return inputs - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ # type: ignore - k: cast_tensor_type(v, src_type, dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( # type: ignore - cast_tensor_type(item, src_type, dst_type) for item in inputs) - else: - return inputs - - -def auto_fp16( - apply_to: Optional[Iterable] = None, - out_fp32: bool = False, - supported_types: tuple = (nn.Module, ), -) -> Callable: - """Decorator to enable fp16 training automatically. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If inputs arguments are fp32 tensors, they will - be converted to fp16 automatically. Arguments other than fp32 tensors are - ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp32 (bool): Whether to convert the output back to fp32. - supported_types (tuple): Classes can be decorated by ``auto_fp16``. - `New in version 1.5.0.` - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp16 - >>> @auto_fp16() - >>> def forward(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp16 - >>> @auto_fp16(apply_to=('pred', )) - >>> def do_something(self, pred, others): - >>> pass - """ - - def auto_fp16_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], supported_types): - raise TypeError('@auto_fp16 can only be used to decorate the ' - f'method of those classes {supported_types}') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - # NOTE: default args are not taken into consideration - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.float, torch.half)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = {} - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.float, torch.half) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=True): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp32: - output = cast_tensor_type(output, torch.half, torch.float) - return output - - return new_func - - return auto_fp16_wrapper - - -def force_fp32(apply_to: Optional[Iterable] = None, - out_fp16: bool = False) -> Callable: - """Decorator to convert input arguments to fp32 in force. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If there are some inputs that must be processed - in fp32 mode, then this decorator can handle it. If inputs arguments are - fp16 tensors, they will be converted to fp32 automatically. Arguments other - than fp16 tensors are ignored. If you are using PyTorch >= 1.6, - torch.cuda.amp is used as the backend, otherwise, original mmcv - implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp16 (bool): Whether to convert the output back to fp16. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp32 - >>> @force_fp32() - >>> def loss(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp32 - >>> @force_fp32(apply_to=('pred', )) - >>> def post_process(self, pred, others): - >>> pass - """ - - def force_fp32_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@force_fp32 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.half, torch.float)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = dict() - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.half, torch.float) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=False): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp16: - output = cast_tensor_type(output, torch.float, torch.half) - return output - - return new_func - - return force_fp32_wrapper - - -def allreduce_grads(params: List[Parameter], - coalesce: bool = True, - bucket_size_mb: int = -1) -> None: - warnings.warn( - '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' - 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads', - DeprecationWarning) - _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) - - -def wrap_fp16_model(model: nn.Module) -> None: - """Wrap the FP32 model to FP16. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - For PyTorch >= 1.6, this function will - 1. Set fp16 flag inside the model to True. - - Otherwise: - 1. Convert FP32 model to FP16. - 2. Remain some necessary layers to be FP32, e.g., normalization layers. - 3. Set `fp16_enabled` flag inside the model to True. - - Args: - model (nn.Module): Model in FP32. - """ - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.6.0')): - # convert model to fp16 - model.half() - # patch the normalization layers to make it work in fp32 mode - patch_norm_fp32(model) - # set `fp16_enabled` flag - for m in model.modules(): - if hasattr(m, 'fp16_enabled'): - m.fp16_enabled = True - - -def patch_norm_fp32(module: nn.Module) -> nn.Module: - """Recursively convert normalization layers from FP16 to FP32. - - Args: - module (nn.Module): The modules to be converted in FP16. - - Returns: - nn.Module: The converted module, the normalization layers have been - converted to FP32. - """ - if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): - module.float() - if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': - module.forward = patch_forward_method(module.forward, torch.half, - torch.float) - for child in module.children(): - patch_norm_fp32(child) - return module - - -def patch_forward_method(func: Callable, - src_type: torch.dtype, - dst_type: torch.dtype, - convert_output: bool = True) -> Callable: - """Patch the forward method of a module. - - Args: - func (callable): The original forward method. - src_type (torch.dtype): Type of input arguments to be converted from. - dst_type (torch.dtype): Type of input arguments to be converted to. - convert_output (bool): Whether to convert the output back to src_type. - - Returns: - callable: The patched forward method. - """ - - def new_forward(*args, **kwargs): - output = func(*cast_tensor_type(args, src_type, dst_type), - **cast_tensor_type(kwargs, src_type, dst_type)) - if convert_output: - output = cast_tensor_type(output, dst_type, src_type) - return output - - return new_forward - - -class LossScaler: - """Class that manages loss scaling in mixed precision training which - supports both dynamic or static mode. - - The implementation refers to - https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. - Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. - It's important to understand how :class:`LossScaler` operates. - Loss scaling is designed to combat the problem of underflowing - gradients encountered at long times when training fp16 networks. - Dynamic loss scaling begins by attempting a very high loss - scale. Ironically, this may result in OVERflowing gradients. - If overflowing gradients are encountered, :class:`FP16_Optimizer` then - skips the update step for this particular iteration/minibatch, - and :class:`LossScaler` adjusts the loss scale to a lower value. - If a certain number of iterations occur without overflowing gradients - detected,:class:`LossScaler` increases the loss scale once more. - In this way :class:`LossScaler` attempts to "ride the edge" of always - using the highest loss scale possible without incurring overflow. - - Args: - init_scale (float): Initial loss scale value, default: 2**32. - scale_factor (float): Factor used when adjusting the loss scale. - Default: 2. - mode (str): Loss scaling mode. 'dynamic' or 'static' - scale_window (int): Number of consecutive iterations without an - overflow to wait before increasing the loss scale. Default: 1000. - """ - - def __init__(self, - init_scale: float = 2**32, - mode: str = 'dynamic', - scale_factor: float = 2., - scale_window: int = 1000): - self.cur_scale = init_scale - self.cur_iter = 0 - assert mode in ('dynamic', - 'static'), 'mode can only be dynamic or static' - self.mode = mode - self.last_overflow_iter = -1 - self.scale_factor = scale_factor - self.scale_window = scale_window - - def has_overflow(self, params: List[Parameter]) -> bool: - """Check if params contain overflow.""" - if self.mode != 'dynamic': - return False - for p in params: - if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): - return True - return False - - def _has_inf_or_nan(x): - """Check if params contain NaN.""" - try: - cpu_sum = float(x.float().sum()) - except RuntimeError as instance: - if 'value cannot be converted' not in instance.args[0]: - raise - return True - else: - if cpu_sum == float('inf') or cpu_sum == -float('inf') \ - or cpu_sum != cpu_sum: - return True - return False - - def update_scale(self, overflow: bool) -> None: - """update the current loss scale value when overflow happens.""" - if self.mode != 'dynamic': - return - if overflow: - self.cur_scale = max(self.cur_scale / self.scale_factor, 1) - self.last_overflow_iter = self.cur_iter - else: - if (self.cur_iter - self.last_overflow_iter) % \ - self.scale_window == 0: - self.cur_scale *= self.scale_factor - self.cur_iter += 1 - - def state_dict(self): - """Returns the state of the scaler as a :class:`dict`.""" - return dict( - cur_scale=self.cur_scale, - cur_iter=self.cur_iter, - mode=self.mode, - last_overflow_iter=self.last_overflow_iter, - scale_factor=self.scale_factor, - scale_window=self.scale_window) - - def load_state_dict(self, state_dict: dict) -> None: - """Loads the loss_scaler state dict. - - Args: - state_dict (dict): scaler state. - """ - self.cur_scale = state_dict['cur_scale'] - self.cur_iter = state_dict['cur_iter'] - self.mode = state_dict['mode'] - self.last_overflow_iter = state_dict['last_overflow_iter'] - self.scale_factor = state_dict['scale_factor'] - self.scale_window = state_dict['scale_window'] - - @property - def loss_scale(self): - return self.cur_scale diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/__init__.py deleted file mode 100644 index 03e2a619e..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkpoint import CheckpointHook -from .closure import ClosureHook -from .ema import EMAHook -from .evaluation import DistEvalHook, EvalHook -from .hook import HOOKS, Hook -from .iter_timer import IterTimerHook -from .logger import (ClearMLLoggerHook, DvcliveLoggerHook, LoggerHook, - MlflowLoggerHook, NeptuneLoggerHook, PaviLoggerHook, - SegmindLoggerHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .lr_updater import (CosineAnnealingLrUpdaterHook, - CosineRestartLrUpdaterHook, CyclicLrUpdaterHook, - ExpLrUpdaterHook, FixedLrUpdaterHook, - FlatCosineAnnealingLrUpdaterHook, InvLrUpdaterHook, - LinearAnnealingLrUpdaterHook, LrUpdaterHook, - OneCycleLrUpdaterHook, PolyLrUpdaterHook, - StepLrUpdaterHook) -from .memory import EmptyCacheHook -from .momentum_updater import (CosineAnnealingMomentumUpdaterHook, - CyclicMomentumUpdaterHook, - LinearAnnealingMomentumUpdaterHook, - MomentumUpdaterHook, - OneCycleMomentumUpdaterHook, - StepMomentumUpdaterHook) -from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, OptimizerHook) -from .profiler import ProfilerHook -from .sampler_seed import DistSamplerSeedHook -from .sync_buffer import SyncBuffersHook - -__all__ = [ - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'FixedLrUpdaterHook', 'StepLrUpdaterHook', 'ExpLrUpdaterHook', - 'PolyLrUpdaterHook', 'InvLrUpdaterHook', 'CosineAnnealingLrUpdaterHook', - 'FlatCosineAnnealingLrUpdaterHook', 'CosineRestartLrUpdaterHook', - 'CyclicLrUpdaterHook', 'OneCycleLrUpdaterHook', 'OptimizerHook', - 'Fp16OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', - 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TextLoggerHook', 'TensorboardLoggerHook', 'NeptuneLoggerHook', - 'WandbLoggerHook', 'DvcliveLoggerHook', 'MomentumUpdaterHook', - 'StepMomentumUpdaterHook', 'CosineAnnealingMomentumUpdaterHook', - 'CyclicMomentumUpdaterHook', 'OneCycleMomentumUpdaterHook', - 'SyncBuffersHook', 'EMAHook', 'EvalHook', 'DistEvalHook', 'ProfilerHook', - 'GradientCumulativeOptimizerHook', 'GradientCumulativeFp16OptimizerHook', - 'SegmindLoggerHook', 'LinearAnnealingLrUpdaterHook', - 'LinearAnnealingMomentumUpdaterHook', 'ClearMLLoggerHook' -] diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/checkpoint.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/checkpoint.py deleted file mode 100644 index 3449e98a9..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/checkpoint.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -from mmcv.fileio import FileClient -from ..dist_utils import allreduce_params, master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class CheckpointHook(Hook): - """Save checkpoints periodically. - - Args: - interval (int): The saving period. If ``by_epoch=True``, interval - indicates epochs, otherwise it indicates iterations. - Default: -1, which means "never". - by_epoch (bool): Saving checkpoints by epoch or by iteration. - Default: True. - save_optimizer (bool): Whether to save optimizer state_dict in the - checkpoint. It is usually used for resuming experiments. - Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, ``runner.work_dir`` will be used by default. If - specified, the ``out_dir`` will be the concatenation of ``out_dir`` - and the last level directory of ``runner.work_dir``. - `Changed in version 1.3.16.` - max_keep_ckpts (int, optional): The maximum checkpoints to keep. - In some cases we want only the latest few checkpoints and would - like to delete old ones to save the disk space. - Default: -1, which means unlimited. - save_last (bool, optional): Whether to force the last checkpoint to be - saved regardless of interval. Default: True. - sync_buffer (bool, optional): Whether to synchronize buffers in - different gpus. Default: False. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - - .. warning:: - Before v1.3.16, the ``out_dir`` argument indicates the path where the - checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the - root directory and the final path to save checkpoint is the - concatenation of ``out_dir`` and the last level directory of - ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" - and the value of ``runner.work_dir`` is "/path/of/B", then the final - path will be "/path/of/A/B". - """ - - def __init__(self, - interval=-1, - by_epoch=True, - save_optimizer=True, - out_dir=None, - max_keep_ckpts=-1, - save_last=True, - sync_buffer=False, - file_client_args=None, - **kwargs): - self.interval = interval - self.by_epoch = by_epoch - self.save_optimizer = save_optimizer - self.out_dir = out_dir - self.max_keep_ckpts = max_keep_ckpts - self.save_last = save_last - self.args = kwargs - self.sync_buffer = sync_buffer - self.file_client_args = file_client_args - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - - runner.logger.info(f'Checkpoints will be saved to {self.out_dir} by ' - f'{self.file_client.name}.') - - # disable the create_symlink option because some file backends do not - # allow to create a symlink - if 'create_symlink' in self.args: - if self.args[ - 'create_symlink'] and not self.file_client.allow_symlink: - self.args['create_symlink'] = False - warnings.warn( - 'create_symlink is set as True by the user but is changed' - 'to be False because creating symbolic link is not ' - f'allowed in {self.file_client.name}') - else: - self.args['create_symlink'] = self.file_client.allow_symlink - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` epochs - # 2. reach the last epoch of training - if self.every_n_epochs( - runner, self.interval) or (self.save_last - and self.is_last_epoch(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.epoch + 1} epochs') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) - - @master_only - def _save_checkpoint(self, runner): - """Save the current checkpoint and delete unwanted checkpoint.""" - runner.save_checkpoint( - self.out_dir, save_optimizer=self.save_optimizer, **self.args) - if runner.meta is not None: - if self.by_epoch: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) - else: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) - runner.meta.setdefault('hook_msgs', dict()) - runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( - self.out_dir, cur_ckpt_filename) - # remove other checkpoints - if self.max_keep_ckpts > 0: - if self.by_epoch: - name = 'epoch_{}.pth' - current_ckpt = runner.epoch + 1 - else: - name = 'iter_{}.pth' - current_ckpt = runner.iter + 1 - redundant_ckpts = range( - current_ckpt - self.max_keep_ckpts * self.interval, 0, - -self.interval) - filename_tmpl = self.args.get('filename_tmpl', name) - for _step in redundant_ckpts: - ckpt_path = self.file_client.join_path( - self.out_dir, filename_tmpl.format(_step)) - if self.file_client.isfile(ckpt_path): - self.file_client.remove(ckpt_path) - else: - break - - def after_train_iter(self, runner): - if self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` iterations - # 2. reach the last iteration of training - if self.every_n_iters( - runner, self.interval) or (self.save_last - and self.is_last_iter(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.iter + 1} iterations') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/closure.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/closure.py deleted file mode 100644 index b955f81f4..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/closure.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ClosureHook(Hook): - - def __init__(self, fn_name, fn): - assert hasattr(self, fn_name) - assert callable(fn) - setattr(self, fn_name, fn) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/ema.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/ema.py deleted file mode 100644 index 6ed77b84e..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/ema.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...parallel import is_module_wrapper -from ..hooks.hook import HOOKS, Hook - - -@HOOKS.register_module() -class EMAHook(Hook): - r"""Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. - - .. math:: - - Xema\_{t+1} = (1 - \text{momentum}) \times - Xema\_{t} + \text{momentum} \times X_t - - Args: - momentum (float): The momentum used for updating ema parameter. - Defaults to 0.0002. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - warm_up (int): During first warm_up steps, we may use smaller momentum - to update ema parameters more slowly. Defaults to 100. - resume_from (str): The checkpoint path. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - warm_up=100, - resume_from=None): - assert isinstance(interval, int) and interval > 0 - self.warm_up = warm_up - self.interval = interval - assert momentum > 0 and momentum < 1 - self.momentum = momentum**interval - self.checkpoint = resume_from - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - self.model_parameters = dict(model.named_parameters(recurse=True)) - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers(recurse=True)) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - curr_step = runner.iter - # We warm up the momentum considering the instability at beginning - momentum = min(self.momentum, - (1 + curr_step) / (self.warm_up + curr_step)) - if curr_step % self.interval != 0: - return - for name, parameter in self.model_parameters.items(): - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/evaluation.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/evaluation.py deleted file mode 100644 index c79628f98..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/evaluation.py +++ /dev/null @@ -1,511 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from math import inf - -import torch.distributed as dist -from torch.nn.modules.batchnorm import _BatchNorm -from torch.utils.data import DataLoader - -from mmcv.fileio import FileClient -from mmcv.utils import is_seq_of -from .hook import Hook -from .logger import LoggerHook - - -class EvalHook(Hook): - """Non-Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader, and return the test results. If ``None``, the default - test function ``mmcv.engine.single_gpu_test`` will be used. - (default: ``None``) - greater_keys (List[str] | None, optional): Metric keys that will be - inferred by 'greater' comparison rule. If ``None``, - _default_greater_keys will be used. (default: ``None``) - less_keys (List[str] | None, optional): Metric keys that will be - inferred by 'less' comparison rule. If ``None``, _default_less_keys - will be used. (default: ``None``) - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - `New in version 1.3.16.` - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - `New in version 1.3.16.` - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - - Note: - If new arguments are added for EvalHook, tools/test.py, - tools/eval_metric.py may be affected. - """ - - # Since the key for determine greater or less is related to the downstream - # tasks, downstream repos may need to overwrite the following inner - # variable accordingly. - - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - _default_greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - _default_less_keys = ['loss'] - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - out_dir=None, - file_client_args=None, - **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError(f'dataloader must be a pytorch DataLoader, ' - f'but got {type(dataloader)}') - - if interval <= 0: - raise ValueError(f'interval must be a positive number, ' - f'but got {interval}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' - - if start is not None and start < 0: - raise ValueError(f'The evaluation start epoch {start} is smaller ' - f'than 0') - - self.dataloader = dataloader - self.interval = interval - self.start = start - self.by_epoch = by_epoch - - assert isinstance(save_best, str) or save_best is None, \ - '""save_best"" should be a str or None ' \ - f'rather than {type(save_best)}' - self.save_best = save_best - self.eval_kwargs = eval_kwargs - self.initial_flag = True - - if test_fn is None: - from mmcv.engine import single_gpu_test - self.test_fn = single_gpu_test - else: - self.test_fn = test_fn - - if greater_keys is None: - self.greater_keys = self._default_greater_keys - else: - if not isinstance(greater_keys, (list, tuple)): - greater_keys = (greater_keys, ) - assert is_seq_of(greater_keys, str) - self.greater_keys = greater_keys - - if less_keys is None: - self.less_keys = self._default_less_keys - else: - if not isinstance(less_keys, (list, tuple)): - less_keys = (less_keys, ) - assert is_seq_of(less_keys, str) - self.less_keys = less_keys - - if self.save_best is not None: - self.best_ckpt_path = None - self._init_rule(rule, self.save_best) - - self.out_dir = out_dir - self.file_client_args = file_client_args - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific (note that the key indicator matching - is case-insensitive): - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if any one item in ``self.greater_keys`` is a substring of - key_indicator , the rule will be specified as 'greater'. - 4. Or if any one item in ``self.less_keys`` is a substring of - key_indicator , the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator != 'auto': - # `_lc` here means we use the lower case of keys for - # case-insensitive matching - key_indicator_lc = key_indicator.lower() - greater_keys = [key.lower() for key in self.greater_keys] - less_keys = [key.lower() for key in self.less_keys] - - if key_indicator_lc in greater_keys: - rule = 'greater' - elif key_indicator_lc in less_keys: - rule = 'less' - elif any(key in key_indicator_lc for key in greater_keys): - rule = 'greater' - elif any(key in key_indicator_lc for key in less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - if self.rule is not None: - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - f'The best checkpoint will be saved to {self.out_dir} by ' - f'{self.file_client.name}') - - if self.save_best is not None: - if runner.meta is None: - warnings.warn('runner.meta is None. Creating an empty one.') - runner.meta = dict() - runner.meta.setdefault('hook_msgs', dict()) - self.best_ckpt_path = runner.meta['hook_msgs'].get( - 'best_ckpt', None) - - def before_train_iter(self, runner): - """Evaluate the model only at the start of training by iteration.""" - if self.by_epoch or not self.initial_flag: - return - if self.start is not None and runner.iter >= self.start: - self.after_train_iter(runner) - self.initial_flag = False - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - if not (self.by_epoch and self.initial_flag): - return - if self.start is not None and runner.epoch >= self.start: - self.after_train_epoch(runner) - self.initial_flag = False - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch and self._should_evaluate(runner): - # Because the priority of EvalHook is higher than LoggerHook, the - # training log and the evaluating log are mixed. Therefore, - # we need to dump the training log and clear it before evaluating - # log is generated. In addition, this problem will only appear in - # `IterBasedRunner` whose `self.by_epoch` is False, because - # `EpochBasedRunner` whose `self.by_epoch` is True calls - # `_do_evaluate` in `after_train_epoch` stage, and at this stage - # the training log has been printed, so it will not cause any - # problem. more details at - # https://github.com/open-mmlab/mmsegmentation/issues/694 - for hook in runner._hooks: - if isinstance(hook, LoggerHook): - hook.after_train_iter(runner) - runner.log_buffer.clear() - - self._do_evaluate(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch and self._should_evaluate(runner): - self._do_evaluate(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - results = self.test_fn(runner.model, self.dataloader) - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - def _should_evaluate(self, runner): - """Judge whether to perform evaluation. - - Here is the rule to judge whether to perform evaluation: - 1. It will not perform evaluation during the epoch/iteration interval, - which is determined by ``self.interval``. - 2. It will not perform evaluation if the start time is larger than - current time. - 3. It will not perform evaluation when current time is larger than - the start time but during epoch/iteration interval. - - Returns: - bool: The flag indicating whether to perform evaluation. - """ - if self.by_epoch: - current = runner.epoch - check_time = self.every_n_epochs - else: - current = runner.iter - check_time = self.every_n_iters - - if self.start is None: - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - elif (current + 1) < self.start: - # No evaluation if start is larger than the current time. - return False - else: - # Evaluation only at epochs/iters 3, 5, 7... - # if start==3 and interval==2 - if (current + 1 - self.start) % self.interval: - return False - return True - - def _save_ckpt(self, runner, key_score): - """Save the best checkpoint. - - It will compare the score according to the compare function, write - related information (best score, best checkpoint path) and save the - best checkpoint into ``work_dir``. - """ - if self.by_epoch: - current = f'epoch_{runner.epoch + 1}' - cur_type, cur_time = 'epoch', runner.epoch + 1 - else: - current = f'iter_{runner.iter + 1}' - cur_type, cur_time = 'iter', runner.iter + 1 - - best_score = runner.meta['hook_msgs'].get( - 'best_score', self.init_value_map[self.rule]) - if self.compare_func(key_score, best_score): - best_score = key_score - runner.meta['hook_msgs']['best_score'] = best_score - - if self.best_ckpt_path and self.file_client.isfile( - self.best_ckpt_path): - self.file_client.remove(self.best_ckpt_path) - runner.logger.info( - f'The previous best checkpoint {self.best_ckpt_path} was ' - 'removed') - - best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' - self.best_ckpt_path = self.file_client.join_path( - self.out_dir, best_ckpt_name) - runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path - - runner.save_checkpoint( - self.out_dir, - filename_tmpl=best_ckpt_name, - create_symlink=False) - runner.logger.info( - f'Now best checkpoint is saved as {best_ckpt_name}.') - runner.logger.info( - f'Best {self.key_indicator} is {best_score:0.4f} ' - f'at {cur_time} {cur_type}.') - - def evaluate(self, runner, results): - """Evaluate the results. - - Args: - runner (:obj:`mmcv.Runner`): The underlined training runner. - results (list): Output results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - - if self.save_best is not None: - # If the performance of model is pool, the `eval_res` may be an - # empty dict and it will raise exception when `self.save_best` is - # not None. More details at - # https://github.com/open-mmlab/mmdetection/issues/6265. - if not eval_res: - warnings.warn( - 'Since `eval_res` is an empty dict, the behavior to save ' - 'the best checkpoint will be skipped in this evaluation.') - return None - - if self.key_indicator == 'auto': - # infer from eval_results - self._init_rule(self.rule, list(eval_res.keys())[0]) - return eval_res[self.key_indicator] - - return None - - -class DistEvalHook(EvalHook): - """Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader in a multi-gpu manner, and return the test results. If - ``None``, the default test function ``mmcv.engine.multi_gpu_test`` - will be used. (default: ``None``) - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - broadcast_bn_buffer (bool): Whether to broadcast the - buffer(running_mean and running_var) of rank 0 to other rank - before evaluation. Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - """ - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - broadcast_bn_buffer=True, - tmpdir=None, - gpu_collect=False, - out_dir=None, - file_client_args=None, - **eval_kwargs): - - if test_fn is None: - from mmcv.engine import multi_gpu_test - test_fn = multi_gpu_test - - super().__init__( - dataloader, - start=start, - interval=interval, - by_epoch=by_epoch, - save_best=save_best, - rule=rule, - test_fn=test_fn, - greater_keys=greater_keys, - less_keys=less_keys, - out_dir=out_dir, - file_client_args=file_client_args, - **eval_kwargs) - - self.broadcast_bn_buffer = broadcast_bn_buffer - self.tmpdir = tmpdir - self.gpu_collect = gpu_collect - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - results = self.test_fn( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to - # save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/hook.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/hook.py deleted file mode 100644 index f2d1c9865..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/hook.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, is_method_overridden - -HOOKS = Registry('hook') - - -class Hook: - stages = ('before_run', 'before_train_epoch', 'before_train_iter', - 'after_train_iter', 'after_train_epoch', 'before_val_epoch', - 'before_val_iter', 'after_val_iter', 'after_val_epoch', - 'after_run') - - def before_run(self, runner): - pass - - def after_run(self, runner): - pass - - def before_epoch(self, runner): - pass - - def after_epoch(self, runner): - pass - - def before_iter(self, runner): - pass - - def after_iter(self, runner): - pass - - def before_train_epoch(self, runner): - self.before_epoch(runner) - - def before_val_epoch(self, runner): - self.before_epoch(runner) - - def after_train_epoch(self, runner): - self.after_epoch(runner) - - def after_val_epoch(self, runner): - self.after_epoch(runner) - - def before_train_iter(self, runner): - self.before_iter(runner) - - def before_val_iter(self, runner): - self.before_iter(runner) - - def after_train_iter(self, runner): - self.after_iter(runner) - - def after_val_iter(self, runner): - self.after_iter(runner) - - def every_n_epochs(self, runner, n): - return (runner.epoch + 1) % n == 0 if n > 0 else False - - def every_n_inner_iters(self, runner, n): - return (runner.inner_iter + 1) % n == 0 if n > 0 else False - - def every_n_iters(self, runner, n): - return (runner.iter + 1) % n == 0 if n > 0 else False - - def end_of_epoch(self, runner): - return runner.inner_iter + 1 == len(runner.data_loader) - - def is_last_epoch(self, runner): - return runner.epoch + 1 == runner._max_epochs - - def is_last_iter(self, runner): - return runner.iter + 1 == runner._max_iters - - def get_triggered_stages(self): - trigger_stages = set() - for stage in Hook.stages: - if is_method_overridden(stage, Hook, self): - trigger_stages.add(stage) - - # some methods will be triggered in multi stages - # use this dict to map method to stages. - method_stages_map = { - 'before_epoch': ['before_train_epoch', 'before_val_epoch'], - 'after_epoch': ['after_train_epoch', 'after_val_epoch'], - 'before_iter': ['before_train_iter', 'before_val_iter'], - 'after_iter': ['after_train_iter', 'after_val_iter'], - } - - for method, map_stages in method_stages_map.items(): - if is_method_overridden(method, Hook, self): - trigger_stages.update(map_stages) - - return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/iter_timer.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/iter_timer.py deleted file mode 100644 index a072f4c51..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/iter_timer.py +++ /dev/null @@ -1,30 +0,0 @@ -# copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright (c) OpenMMLab. All rights reserved. - -import time -import torch.distributed as dist - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class IterTimerHook(Hook): - - def before_epoch(self, runner): - self.t = time.time() - - def before_iter(self, runner): - self.batch_size = runner.data_loader._dataloader.batch_size - runner.log_buffer.update({'data_time': time.time() - self.t}) - - def after_iter(self, runner): - iter_info = {'time': time.time() - self.t} - fps = self.batch_size / iter_info["time"] - - if dist.is_initialized(): - fps = fps * dist.get_world_size() - - iter_info["fps"] = fps - - runner.log_buffer.update(iter_info) - self.t = time.time() diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/__init__.py deleted file mode 100644 index 062709e70..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import LoggerHook -from .clearml import ClearMLLoggerHook -from .dvclive import DvcliveLoggerHook -from .mlflow import MlflowLoggerHook -from .neptune import NeptuneLoggerHook -from .pavi import PaviLoggerHook -from .segmind import SegmindLoggerHook -from .tensorboard import TensorboardLoggerHook -from .text import TextLoggerHook -from .wandb import WandbLoggerHook - -__all__ = [ - 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', - 'NeptuneLoggerHook', 'DvcliveLoggerHook', 'SegmindLoggerHook', - 'ClearMLLoggerHook' -] diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/base.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/base.py deleted file mode 100644 index 416a1b751..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/base.py +++ /dev/null @@ -1,172 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from abc import ABCMeta, abstractmethod -from typing import Dict - -import numpy as np -import torch - -from ..hook import Hook - - -class LoggerHook(Hook): - """Base class for logger hooks. - - Args: - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - """ - - __metaclass__ = ABCMeta - - def __init__(self, - interval: int = 10, - ignore_last: bool = True, - reset_flag: bool = False, - by_epoch: bool = True): - self.interval = interval - self.ignore_last = ignore_last - self.reset_flag = reset_flag - self.by_epoch = by_epoch - - @abstractmethod - def log(self, runner): - pass - - @staticmethod - def is_scalar(val, - include_np: bool = True, - include_torch: bool = True) -> bool: - """Tell the input variable is a scalar or not. - - Args: - val: Input variable. - include_np (bool): Whether include 0-d np.ndarray as a scalar. - include_torch (bool): Whether include 0-d torch.Tensor as a scalar. - - Returns: - bool: True or False. - """ - if isinstance(val, numbers.Number): - return True - elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: - return True - elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: - return True - else: - return False - - def get_mode(self, runner) -> str: - if runner.mode == 'train': - if 'time' in runner.log_buffer.output: - mode = 'train' - else: - mode = 'val' - elif runner.mode == 'val': - mode = 'val' - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return mode - - def get_epoch(self, runner) -> int: - if runner.mode == 'train': - epoch = runner.epoch + 1 - elif runner.mode == 'val': - # normal val mode - # runner.epoch += 1 has been done before val workflow - epoch = runner.epoch - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return epoch - - def get_iter(self, runner, inner_iter: bool = False) -> int: - """Get the current training iteration step.""" - if self.by_epoch and inner_iter: - current_iter = runner.inner_iter + 1 - else: - current_iter = runner.iter + 1 - return current_iter - - def get_lr_tags(self, runner) -> Dict[str, float]: - tags = {} - lrs = runner.current_lr() - if isinstance(lrs, dict): - for name, value in lrs.items(): - tags[f'learning_rate/{name}'] = value[0] - else: - tags['learning_rate'] = lrs[0] - return tags - - def get_momentum_tags(self, runner) -> Dict[str, float]: - tags = {} - momentums = runner.current_momentum() - if isinstance(momentums, dict): - for name, value in momentums.items(): - tags[f'momentum/{name}'] = value[0] - else: - tags['momentum'] = momentums[0] - return tags - - def get_loggable_tags( - self, - runner, - allow_scalar: bool = True, - allow_text: bool = False, - add_mode: bool = True, - tags_to_skip: tuple = ('time', 'data_time') - ) -> Dict: - tags = {} - for var, val in runner.log_buffer.output.items(): - if var in tags_to_skip: - continue - if self.is_scalar(val) and not allow_scalar: - continue - if isinstance(val, str) and not allow_text: - continue - if add_mode: - var = f'{self.get_mode(runner)}/{var}' - tags[var] = val - tags.update(self.get_lr_tags(runner)) - tags.update(self.get_momentum_tags(runner)) - return tags - - def before_run(self, runner) -> None: - for hook in runner.hooks[::-1]: - if isinstance(hook, LoggerHook): - hook.reset_flag = True - break - - def before_epoch(self, runner) -> None: - runner.log_buffer.clear() # clear logs of last epoch - - def after_train_iter(self, runner) -> None: - if self.by_epoch and self.every_n_inner_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif not self.by_epoch and self.every_n_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif self.end_of_epoch(runner) and not self.ignore_last: - # not precise but more stable - runner.log_buffer.average(self.interval) - - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_train_epoch(self, runner) -> None: - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_val_epoch(self, runner) -> None: - runner.log_buffer.average() - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/clearml.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/clearml.py deleted file mode 100644 index 7db651f03..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/clearml.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from typing import Dict, Optional - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class ClearMLLoggerHook(LoggerHook): - """Class to log metrics with clearml. - - It requires `clearml`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the `clearml.Task.init` - initialization keys. See `taskinit`_ for more details. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _clearml: - https://clear.ml/docs/latest/docs/ - .. _taskinit: - https://clear.ml/docs/latest/docs/references/sdk/task/#taskinit - """ - - def __init__(self, - init_kwargs: Optional[Dict] = None, - interval: int = 10, - ignore_last: bool = True, - reset_flag: bool = False, - by_epoch: bool = True): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.import_clearml() - self.init_kwargs = init_kwargs - - def import_clearml(self): - try: - import clearml - except ImportError: - raise ImportError( - 'Please run "pip install clearml" to install clearml') - self.clearml = clearml - - @master_only - def before_run(self, runner) -> None: - super().before_run(runner) - task_kwargs = self.init_kwargs if self.init_kwargs else {} - self.task = self.clearml.Task.init(**task_kwargs) - self.task_logger = self.task.get_logger() - - @master_only - def log(self, runner) -> None: - tags = self.get_loggable_tags(runner) - for tag, val in tags.items(): - self.task_logger.report_scalar(tag, tag, val, - self.get_iter(runner)) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/dvclive.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/dvclive.py deleted file mode 100644 index fc0a58c49..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/dvclive.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from pathlib import Path -from typing import Optional - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class DvcliveLoggerHook(LoggerHook): - """Class to log metrics with dvclive. - - It requires `dvclive`_ to be installed. - - Args: - model_file (str): Default None. If not None, after each epoch the - model will be saved to {model_file}. - interval (int): Logging interval (every k iterations). Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - kwargs: Arguments for instantiating `Live`_. - - .. _dvclive: - https://dvc.org/doc/dvclive - - .. _Live: - https://dvc.org/doc/dvclive/api-reference/live#parameters - """ - - def __init__(self, - model_file: Optional[str] = None, - interval: int = 10, - ignore_last: bool = True, - reset_flag: bool = False, - by_epoch: bool = True, - **kwargs): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.model_file = model_file - self.import_dvclive(**kwargs) - - def import_dvclive(self, **kwargs) -> None: - try: - from dvclive import Live - except ImportError: - raise ImportError( - 'Please run "pip install dvclive" to install dvclive') - self.dvclive = Live(**kwargs) - - @master_only - def log(self, runner) -> None: - tags = self.get_loggable_tags(runner) - if tags: - self.dvclive.set_step(self.get_iter(runner)) - for k, v in tags.items(): - self.dvclive.log(k, v) - - @master_only - def after_train_epoch(self, runner) -> None: - super().after_train_epoch(runner) - if self.model_file is not None: - runner.save_checkpoint( - Path(self.model_file).parent, - filename_tmpl=Path(self.model_file).name, - create_symlink=False, - ) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/mlflow.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/mlflow.py deleted file mode 100644 index a76b0426b..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/mlflow.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from typing import Dict, Optional - -from mmcv.utils import TORCH_VERSION -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class MlflowLoggerHook(LoggerHook): - """Class to log metrics and (optionally) a trained model to MLflow. - - It requires `MLflow`_ to be installed. - - Args: - exp_name (str, optional): Name of the experiment to be used. - Default None. If not None, set the active experiment. - If experiment does not exist, an experiment with provided name - will be created. - tags (Dict[str], optional): Tags for the current run. - Default None. If not None, set tags for the current run. - log_model (bool, optional): Whether to log an MLflow artifact. - Default True. If True, log runner.model as an MLflow artifact - for the current run. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _MLflow: - https://www.mlflow.org/docs/latest/index.html - """ - - def __init__(self, - exp_name: Optional[str] = None, - tags: Optional[Dict] = None, - log_model: bool = True, - interval: int = 10, - ignore_last: bool = True, - reset_flag: bool = False, - by_epoch: bool = True): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.import_mlflow() - self.exp_name = exp_name - self.tags = tags - self.log_model = log_model - - def import_mlflow(self) -> None: - try: - import mlflow - import mlflow.pytorch as mlflow_pytorch - except ImportError: - raise ImportError( - 'Please run "pip install mlflow" to install mlflow') - self.mlflow = mlflow - self.mlflow_pytorch = mlflow_pytorch - - @master_only - def before_run(self, runner) -> None: - super().before_run(runner) - if self.exp_name is not None: - self.mlflow.set_experiment(self.exp_name) - if self.tags is not None: - self.mlflow.set_tags(self.tags) - - @master_only - def log(self, runner) -> None: - tags = self.get_loggable_tags(runner) - if tags: - self.mlflow.log_metrics(tags, step=self.get_iter(runner)) - - @master_only - def after_run(self, runner) -> None: - if self.log_model: - self.mlflow_pytorch.log_model( - runner.model, - 'models', - pip_requirements=[f'torch=={TORCH_VERSION}']) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/neptune.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/neptune.py deleted file mode 100644 index e398fe1e7..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/neptune.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from typing import Dict, Optional - -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class NeptuneLoggerHook(LoggerHook): - """Class to log metrics to NeptuneAI. - - It requires `Neptune`_ to be installed. - - Args: - init_kwargs (dict): a dict contains the initialization keys as below: - - - project (str): Name of a project in a form of - namespace/project_name. If None, the value of NEPTUNE_PROJECT - environment variable will be taken. - - api_token (str): User’s API token. If None, the value of - NEPTUNE_API_TOKEN environment variable will be taken. Note: It is - strongly recommended to use NEPTUNE_API_TOKEN environment - variable rather than placing your API token in plain text in your - source code. - - name (str, optional, default is 'Untitled'): Editable name of the - run. Name is displayed in the run's Details and in Runs table as - a column. - - Check https://docs.neptune.ai/api-reference/neptune#init for more - init arguments. - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than ``interval``. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - - .. _Neptune: - https://docs.neptune.ai - """ - - def __init__(self, - init_kwargs: Optional[Dict] = None, - interval: int = 10, - ignore_last: bool = True, - reset_flag: bool = True, - with_step: bool = True, - by_epoch: bool = True): - - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.import_neptune() - self.init_kwargs = init_kwargs - self.with_step = with_step - - def import_neptune(self) -> None: - try: - import neptune.new as neptune - except ImportError: - raise ImportError( - 'Please run "pip install neptune-client" to install neptune') - self.neptune = neptune - self.run = None - - @master_only - def before_run(self, runner) -> None: - if self.init_kwargs: - self.run = self.neptune.init(**self.init_kwargs) - else: - self.run = self.neptune.init() - - @master_only - def log(self, runner) -> None: - tags = self.get_loggable_tags(runner) - if tags: - for tag_name, tag_value in tags.items(): - if self.with_step: - self.run[tag_name].log( # type: ignore - tag_value, step=self.get_iter(runner)) - else: - tags['global_step'] = self.get_iter(runner) - self.run[tag_name].log(tags) # type: ignore - - @master_only - def after_run(self, runner) -> None: - self.run.stop() # type: ignore diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/pavi.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/pavi.py deleted file mode 100644 index 13fbf7b26..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/pavi.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp -from typing import Dict, Optional - -import torch -import yaml - -import mmcv -from ....parallel.utils import is_module_wrapper -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class PaviLoggerHook(LoggerHook): - """Class to visual model, log metrics (for internal use). - - Args: - init_kwargs (dict): A dict contains the initialization keys. - add_graph (bool): Whether to visual model. Default: False. - add_last_ckpt (bool): Whether to save checkpoint after run. - Default: False. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - img_key (string): Get image data from Dataset. Default: 'img_info'. - """ - - def __init__(self, - init_kwargs: Optional[Dict] = None, - add_graph: bool = False, - add_last_ckpt: bool = False, - interval: int = 10, - ignore_last: bool = True, - reset_flag: bool = False, - by_epoch: bool = True, - img_key: str = 'img_info'): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.init_kwargs = init_kwargs - self.add_graph = add_graph - self.add_last_ckpt = add_last_ckpt - self.img_key = img_key - - @master_only - def before_run(self, runner) -> None: - super().before_run(runner) - try: - from pavi import SummaryWriter - except ImportError: - raise ImportError('Please run "pip install pavi" to install pavi.') - - self.run_name = runner.work_dir.split('/')[-1] - - if not self.init_kwargs: - self.init_kwargs = dict() - self.init_kwargs['name'] = self.run_name - self.init_kwargs['model'] = runner._model_name - if runner.meta is not None: - if 'config_dict' in runner.meta: - config_dict = runner.meta['config_dict'] - assert isinstance( - config_dict, - dict), ('meta["config_dict"] has to be of a dict, ' - f'but got {type(config_dict)}') - elif 'config_file' in runner.meta: - config_file = runner.meta['config_file'] - config_dict = dict(mmcv.Config.fromfile(config_file)) - else: - config_dict = None - if config_dict is not None: - # 'max_.*iter' is parsed in pavi sdk as the maximum iterations - # to properly set up the progress bar. - config_dict = config_dict.copy() - config_dict.setdefault('max_iter', runner.max_iters) - # non-serializable values are first converted in - # mmcv.dump to json - config_dict = json.loads( - mmcv.dump(config_dict, file_format='json')) - session_text = yaml.dump(config_dict) - self.init_kwargs['session_text'] = session_text - self.writer = SummaryWriter(**self.init_kwargs) - - def get_step(self, runner) -> int: - """Get the total training step/epoch.""" - if self.get_mode(runner) == 'val' and self.by_epoch: - return self.get_epoch(runner) - else: - return self.get_iter(runner) - - @master_only - def log(self, runner) -> None: - tags = self.get_loggable_tags(runner, add_mode=False) - if tags: - self.writer.add_scalars( - self.get_mode(runner), tags, self.get_step(runner)) - - @master_only - def after_run(self, runner) -> None: - if self.add_last_ckpt: - ckpt_path = osp.join(runner.work_dir, 'latest.pth') - if osp.islink(ckpt_path): - ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) - - if osp.isfile(ckpt_path): - # runner.epoch += 1 has been done before `after_run`. - iteration = runner.epoch if self.by_epoch else runner.iter - return self.writer.add_snapshot_file( - tag=self.run_name, - snapshot_file_path=ckpt_path, - iteration=iteration) - - # flush the buffer and send a task ending signal to Pavi - self.writer.close() - - @master_only - def before_epoch(self, runner) -> None: - if runner.epoch == 0 and self.add_graph: - if is_module_wrapper(runner.model): - _model = runner.model.module - else: - _model = runner.model - device = next(_model.parameters()).device - data = next(iter(runner.data_loader)) - image = data[self.img_key][0:1].to(device) - with torch.no_grad(): - self.writer.add_graph(_model, image) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/segmind.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/segmind.py deleted file mode 100644 index ecb3751ed..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/segmind.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class SegmindLoggerHook(LoggerHook): - """Class to log metrics to Segmind. - - It requires `Segmind`_ to be installed. - - Args: - interval (int): Logging interval (every k iterations). Default: 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default False. - by_epoch (bool): Whether EpochBasedRunner is used. Default True. - - .. _Segmind: - https://docs.segmind.com/python-library - """ - - def __init__(self, - interval: int = 10, - ignore_last: bool = True, - reset_flag: bool = False, - by_epoch=True): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.import_segmind() - - def import_segmind(self) -> None: - try: - import segmind - except ImportError: - raise ImportError( - "Please run 'pip install segmind' to install segmind") - self.log_metrics = segmind.tracking.fluent.log_metrics - self.mlflow_log = segmind.utils.logging_utils.try_mlflow_log - - @master_only - def log(self, runner) -> None: - tags = self.get_loggable_tags(runner) - if tags: - # logging metrics to segmind - self.mlflow_log( - self.log_metrics, tags, step=runner.epoch, epoch=runner.epoch) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/tensorboard.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/tensorboard.py deleted file mode 100644 index 11d079911..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/tensorboard.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -from typing import Optional - -from mmcv.utils import TORCH_VERSION, digit_version -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TensorboardLoggerHook(LoggerHook): - """Class to log metrics to Tensorboard. - - Args: - log_dir (string): Save directory location. Default: None. If default - values are used, directory location is ``runner.work_dir``/tf_logs. - interval (int): Logging interval (every k iterations). Default: True. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - by_epoch (bool): Whether EpochBasedRunner is used. Default: True. - """ - - def __init__(self, - log_dir: Optional[str] = None, - interval: int = 10, - ignore_last: bool = True, - reset_flag: bool = False, - by_epoch: bool = True): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.log_dir = log_dir - - @master_only - def before_run(self, runner) -> None: - super().before_run(runner) - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.1')): - try: - from tensorboardX import SummaryWriter - except ImportError: - raise ImportError('Please install tensorboardX to use ' - 'TensorboardLoggerHook.') - else: - try: - from torch.utils.tensorboard import SummaryWriter - except ImportError: - raise ImportError( - 'Please run "pip install future tensorboard" to install ' - 'the dependencies to use torch.utils.tensorboard ' - '(applicable to PyTorch 1.1 or higher)') - - if self.log_dir is None: - self.log_dir = osp.join(runner.work_dir, 'tf_logs') - self.writer = SummaryWriter(self.log_dir) - - @master_only - def log(self, runner) -> None: - tags = self.get_loggable_tags(runner, allow_text=True) - for tag, val in tags.items(): - if isinstance(val, str): - self.writer.add_text(tag, val, self.get_iter(runner)) - else: - self.writer.add_scalar(tag, val, self.get_iter(runner)) - - @master_only - def after_run(self, runner) -> None: - self.writer.close() diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/text.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/text.py deleted file mode 100644 index 05d3442c4..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/text.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import datetime -import os -import os.path as osp -from collections import OrderedDict -from typing import Dict, Optional, Union - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.fileio.file_client import FileClient -from mmcv.utils import is_tuple_of, scandir -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TextLoggerHook(LoggerHook): - """Logger hook in text. - - In this logger hook, the information will be printed on terminal and - saved in json file. - - Args: - by_epoch (bool, optional): Whether EpochBasedRunner is used. - Default: True. - interval (int, optional): Logging interval (every k iterations). - Default: 10. - ignore_last (bool, optional): Ignore the log of last iterations in each - epoch if less than :attr:`interval`. Default: True. - reset_flag (bool, optional): Whether to clear the output buffer after - logging. Default: False. - interval_exp_name (int, optional): Logging interval for experiment - name. This feature is to help users conveniently get the experiment - information from screen or log file. Default: 1000. - out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. - If ``out_dir`` is specified, logs will be copied to a new directory - which is the concatenation of ``out_dir`` and the last level - directory of ``runner.work_dir``. Default: None. - `New in version 1.3.16.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be copied to ``out_dir``. - Default: ('.log.json', '.log', '.py'). - `New in version 1.3.16.` - keep_local (bool, optional): Whether to keep local log when - :attr:`out_dir` is specified. If False, the local log will be - removed. Default: True. - `New in version 1.3.16.` - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - - def __init__(self, - by_epoch: bool = True, - interval: int = 10, - ignore_last: bool = True, - reset_flag: bool = False, - interval_exp_name: int = 1000, - out_dir: Optional[str] = None, - out_suffix: Union[str, tuple] = ('.log.json', '.log', '.py'), - keep_local: bool = True, - file_client_args: Optional[Dict] = None): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.by_epoch = by_epoch - self.time_sec_tot = 0 - self.interval_exp_name = interval_exp_name - - if out_dir is None and file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" when `out_dir` is not' - 'specified.') - self.out_dir = out_dir - - if not (out_dir is None or isinstance(out_dir, str) - or is_tuple_of(out_dir, str)): - raise TypeError('out_dir should be "None" or string or tuple of ' - 'string, but got {out_dir}') - self.out_suffix = out_suffix - - self.keep_local = keep_local - self.file_client_args = file_client_args - if self.out_dir is not None: - self.file_client = FileClient.infer_client(file_client_args, - self.out_dir) - - def before_run(self, runner) -> None: - super().before_run(runner) - - if self.out_dir is not None: - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - # The final `self.out_dir` is the concatenation of `self.out_dir` - # and the last level directory of `runner.work_dir` - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - f'Text logs will be saved to {self.out_dir} by ' - f'{self.file_client.name} after the training process.') - - self.start_iter = runner.iter - self.json_log_path = osp.join(runner.work_dir, - f'{runner.timestamp}.log.json') - if runner.meta is not None: - self._dump_log(runner.meta, runner) - - def _get_max_memory(self, runner) -> int: - device = getattr(runner.model, 'output_device', None) - mem = torch.cuda.max_memory_allocated(device=device) - mem_mb = torch.tensor([int(mem) // (1024 * 1024)], - dtype=torch.int, - device=device) - if runner.world_size > 1: - dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) - return mem_mb.item() - - def _log_info(self, log_dict: Dict, runner) -> None: - # print exp name for users to distinguish experiments - # at every ``interval_exp_name`` iterations and the end of each epoch - if runner.meta is not None and 'exp_name' in runner.meta: - if (self.every_n_iters(runner, self.interval_exp_name)) or ( - self.by_epoch and self.end_of_epoch(runner)): - exp_info = f'Exp name: {runner.meta["exp_name"]}' - runner.logger.info(exp_info) - - if log_dict['mode'] == 'train': - if isinstance(log_dict['lr'], dict): - lr_str = [] - for k, val in log_dict['lr'].items(): - lr_str.append(f'lr_{k}: {val:.3e}') - lr_str = ' '.join(lr_str) # type: ignore - else: - lr_str = f'lr: {log_dict["lr"]:.3e}' # type: ignore - - # by epoch: Epoch [4][100/1000] - # by iter: Iter [100/100000] - if self.by_epoch: - log_str = f'Epoch [{log_dict["epoch"]}]' \ - f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' - else: - log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' - log_str += f'{lr_str}, ' - - if 'time' in log_dict.keys(): - self.time_sec_tot += (log_dict['time'] * self.interval) - time_sec_avg = self.time_sec_tot / ( - runner.iter - self.start_iter + 1) - eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) - eta_str = str(datetime.timedelta(seconds=int(eta_sec))) - log_str += f'eta: {eta_str}, ' - log_str += f'time: {log_dict["time"]:.3f}, ' \ - f'data_time: {log_dict["data_time"]:.3f}, ' - # statistic memory - #if torch.cuda.is_available(): - #log_str += f'memory: {log_dict["memory"]}, ' - else: - # val/test time - # here 1000 is the length of the val dataloader - # by epoch: Epoch[val] [4][1000] - # by iter: Iter[val] [1000] - if self.by_epoch: - log_str = f'Epoch({log_dict["mode"]}) ' \ - f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' - else: - log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' - - log_items = [] - for name, val in log_dict.items(): - # TODO: resolve this hack - # these items have been in log_str - if name in [ - 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', - 'epoch' - ]: - continue - if isinstance(val, float): - val = f'{val:.4f}' - log_items.append(f'{name}: {val}') - log_str += ', '.join(log_items) - - runner.logger.info(log_str) - - def _dump_log(self, log_dict: Dict, runner) -> None: - # dump log in json format - json_log = OrderedDict() - for k, v in log_dict.items(): - json_log[k] = self._round_float(v) - # only append log at last line - if runner.rank == 0: - with open(self.json_log_path, 'a+') as f: - mmcv.dump(json_log, f, file_format='json') - f.write('\n') - - def _round_float(self, items): - if isinstance(items, list): - return [self._round_float(item) for item in items] - elif isinstance(items, float): - return round(items, 5) - else: - return items - - def log(self, runner) -> OrderedDict: - if 'eval_iter_num' in runner.log_buffer.output: - # this doesn't modify runner.iter and is regardless of by_epoch - cur_iter = runner.log_buffer.output.pop('eval_iter_num') - else: - cur_iter = self.get_iter(runner, inner_iter=True) - - log_dict = OrderedDict( - mode=self.get_mode(runner), - epoch=self.get_epoch(runner), - iter=cur_iter) - - # only record lr of the first param group - cur_lr = runner.current_lr() - if isinstance(cur_lr, list): - log_dict['lr'] = cur_lr[0] - else: - assert isinstance(cur_lr, dict) - log_dict['lr'] = {} - for k, lr_ in cur_lr.items(): - assert isinstance(lr_, list) - log_dict['lr'].update({k: lr_[0]}) - - #if 'time' in runner.log_buffer.output: - # statistic memory - #if torch.cuda.is_available(): - #log_dict['memory'] = self._get_max_memory(runner) - - log_dict = dict(log_dict, **runner.log_buffer.output) # type: ignore - - self._log_info(log_dict, runner) - self._dump_log(log_dict, runner) - return log_dict - - def after_run(self, runner) -> None: - # copy or upload logs to self.out_dir - if self.out_dir is not None: - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - out_filepath = self.file_client.join_path( - self.out_dir, filename) - with open(local_filepath) as f: - self.file_client.put_text(f.read(), out_filepath) - - runner.logger.info( - f'The file {local_filepath} has been uploaded to ' - f'{out_filepath}.') - - if not self.keep_local: - os.remove(local_filepath) - runner.logger.info( - f'{local_filepath} was removed due to the ' - '`self.keep_local=False`') diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/wandb.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/wandb.py deleted file mode 100644 index 1cf165507..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/logger/wandb.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -from typing import Dict, Optional, Union - -from mmcv.utils import scandir -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class WandbLoggerHook(LoggerHook): - """Class to log metrics with wandb. - - It requires `wandb`_ to be installed. - - - Args: - init_kwargs (dict): A dict contains the initialization keys. Check - https://docs.wandb.ai/ref/python/init for more init arguments. - interval (int): Logging interval (every k iterations). - Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: False. - commit (bool): Save the metrics dict to the wandb server and increment - the step. If false ``wandb.log`` just updates the current metrics - dict with the row argument and metrics won't be saved until - ``wandb.log`` is called with ``commit=True``. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. - Default: True. - with_step (bool): If True, the step will be logged from - ``self.get_iters``. Otherwise, step will not be logged. - Default: True. - log_artifact (bool): If True, artifacts in {work_dir} will be uploaded - to wandb after training ends. - Default: True - `New in version 1.4.3.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be uploaded to wandb. - Default: ('.log.json', '.log', '.py'). - `New in version 1.4.3.` - - .. _wandb: - https://docs.wandb.ai - """ - - def __init__(self, - init_kwargs: Optional[Dict] = None, - interval: int = 10, - ignore_last: bool = True, - reset_flag: bool = False, - commit: bool = True, - by_epoch: bool = True, - with_step: bool = True, - log_artifact: bool = True, - out_suffix: Union[str, tuple] = ('.log.json', '.log', '.py')): - super().__init__(interval, ignore_last, reset_flag, by_epoch) - self.import_wandb() - self.init_kwargs = init_kwargs - self.commit = commit - self.with_step = with_step - self.log_artifact = log_artifact - self.out_suffix = out_suffix - - def import_wandb(self) -> None: - try: - import wandb - except ImportError: - raise ImportError( - 'Please run "pip install wandb" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner) -> None: - super().before_run(runner) - if self.wandb is None: - self.import_wandb() - if self.init_kwargs: - self.wandb.init(**self.init_kwargs) # type: ignore - else: - self.wandb.init() # type: ignore - - @master_only - def log(self, runner) -> None: - tags = self.get_loggable_tags(runner) - if tags: - if self.with_step: - self.wandb.log( - tags, step=self.get_iter(runner), commit=self.commit) - else: - tags['global_step'] = self.get_iter(runner) - self.wandb.log(tags, commit=self.commit) - - @master_only - def after_run(self, runner) -> None: - if self.log_artifact: - wandb_artifact = self.wandb.Artifact( - name='artifacts', type='model') - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - wandb_artifact.add_file(local_filepath) - self.wandb.log_artifact(wandb_artifact) - self.wandb.join() diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/lr_updater.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/lr_updater.py deleted file mode 100644 index 3d3351d56..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/lr_updater.py +++ /dev/null @@ -1,751 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from math import cos, pi -from typing import Callable, List, Optional, Union - -import mmcv -from mmcv import runner -from .hook import HOOKS, Hook - - -class LrUpdaterHook(Hook): - """LR Scheduler in MMCV. - - Args: - by_epoch (bool): LR changes epoch by epoch - warmup (string): Type of warmup used. It can be None(use no warmup), - 'constant', 'linear' or 'exp' - warmup_iters (int): The number of iterations or epochs that warmup - lasts - warmup_ratio (float): LR used at the beginning of warmup equals to - warmup_ratio * initial_lr - warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters - means the number of epochs that warmup lasts, otherwise means the - number of iteration that warmup lasts - """ - - def __init__(self, - by_epoch: bool = True, - warmup: Optional[str] = None, - warmup_iters: int = 0, - warmup_ratio: float = 0.1, - warmup_by_epoch: bool = False) -> None: - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant", "linear" and "exp"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_ratio" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters: Optional[int] = warmup_iters - self.warmup_ratio = warmup_ratio - self.warmup_by_epoch = warmup_by_epoch - - if self.warmup_by_epoch: - self.warmup_epochs: Optional[int] = self.warmup_iters - self.warmup_iters = None - else: - self.warmup_epochs = None - - self.base_lr: Union[list, dict] = [] # initial lr for all param groups - self.regular_lr: list = [] # expected lr if no warming up is performed - - def _set_lr(self, runner, lr_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, lr in zip(optim.param_groups, lr_groups[k]): - param_group['lr'] = lr - else: - for param_group, lr in zip(runner.optimizer.param_groups, - lr_groups): - param_group['lr'] = lr - - def get_lr(self, runner: 'runner.BaseRunner', base_lr: float): - raise NotImplementedError - - def get_regular_lr(self, runner: 'runner.BaseRunner'): - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(runner, _base_lr) - for _base_lr in self.base_lr[k] - ] - lr_groups.update({k: _lr_group}) - - return lr_groups - else: - return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] - - def get_warmup_lr(self, cur_iters: int): - - def _get_warmup_lr(cur_iters, regular_lr): - if self.warmup == 'constant': - warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_lr = [_lr * (1 - k) for _lr in regular_lr] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.regular_lr, dict): - lr_groups = {} - for key, regular_lr in self.regular_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.regular_lr) - - def before_run(self, runner: 'runner.BaseRunner'): - # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - group.setdefault('initial_lr', group['lr']) - _base_lr = [ - group['initial_lr'] for group in optim.param_groups - ] - self.base_lr.update({k: _base_lr}) - else: - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['initial_lr'] for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner: 'runner.BaseRunner'): - if self.warmup_iters is None: - epoch_len = len(runner.data_loader) # type: ignore - self.warmup_iters = self.warmup_epochs * epoch_len # type: ignore - - if not self.by_epoch: - return - - self.regular_lr = self.get_regular_lr(runner) - self._set_lr(runner, self.regular_lr) - - def before_train_iter(self, runner: 'runner.BaseRunner'): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_lr = self.get_regular_lr(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - - -@HOOKS.register_module() -class FixedLrUpdaterHook(LrUpdaterHook): - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - def get_lr(self, runner, base_lr): - return base_lr - - -@HOOKS.register_module() -class StepLrUpdaterHook(LrUpdaterHook): - """Step LR scheduler with min_lr clipping. - - Args: - step (int | list[int]): Step to decay the LR. If an int value is given, - regard it as the decay interval. If a list is given, decay LR at - these steps. - gamma (float): Decay LR ratio. Defaults to 0.1. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. If None - is given, we don't perform lr clipping. Default: None. - """ - - def __init__(self, - step: Union[int, List[int]], - gamma: float = 0.1, - min_lr: Optional[float] = None, - **kwargs) -> None: - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_lr = min_lr - super().__init__(**kwargs) - - def get_lr(self, runner: 'runner.BaseRunner', base_lr: float): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - lr = base_lr * (self.gamma**exp) - if self.min_lr is not None: - # clip to a minimum value - lr = max(lr, self.min_lr) - return lr - - -@HOOKS.register_module() -class ExpLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma: float, **kwargs) -> None: - self.gamma = gamma - super().__init__(**kwargs) - - def get_lr(self, runner: 'runner.BaseRunner', base_lr: float): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * self.gamma**progress - - -@HOOKS.register_module() -class PolyLrUpdaterHook(LrUpdaterHook): - - def __init__(self, - power: float = 1., - min_lr: float = 0., - **kwargs) -> None: - self.power = power - self.min_lr = min_lr - super().__init__(**kwargs) - - def get_lr(self, runner: 'runner.BaseRunner', base_lr: float): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - coeff = (1 - progress / max_progress)**self.power - return (base_lr - self.min_lr) * coeff + self.min_lr - - -@HOOKS.register_module() -class InvLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma: float, power: float = 1., **kwargs) -> None: - self.gamma = gamma - self.power = power - super().__init__(**kwargs) - - def get_lr(self, runner: 'runner.BaseRunner', base_lr: float): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * (1 + self.gamma * progress)**(-self.power) - - -@HOOKS.register_module() -class CosineAnnealingLrUpdaterHook(LrUpdaterHook): - """CosineAnnealing LR scheduler. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - min_lr: Optional[float] = None, - min_lr_ratio: Optional[float] = None, - **kwargs) -> None: - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super().__init__(**kwargs) - - def get_lr(self, runner: 'runner.BaseRunner', base_lr: float): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr # type:ignore - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): - """Flat + Cosine lr schedule. - - Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 - - Args: - start_percent (float): When to start annealing the learning rate - after the percentage of the total training steps. - The value should be in range [0, 1). - Default: 0.75 - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - start_percent: float = 0.75, - min_lr: Optional[float] = None, - min_lr_ratio: Optional[float] = None, - **kwargs) -> None: - assert (min_lr is None) ^ (min_lr_ratio is None) - if start_percent < 0 or start_percent > 1 or not isinstance( - start_percent, float): - raise ValueError( - 'expected float between 0 and 1 start_percent, but ' - f'got {start_percent}') - self.start_percent = start_percent - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super().__init__(**kwargs) - - def get_lr(self, runner: 'runner.BaseRunner', base_lr: float): - if self.by_epoch: - start = round(runner.max_epochs * self.start_percent) - progress = runner.epoch - start - max_progress = runner.max_epochs - start - else: - start = round(runner.max_iters * self.start_percent) - progress = runner.iter - start - max_progress = runner.max_iters - start - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr # type:ignore - - if progress < 0: - return base_lr - else: - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class CosineRestartLrUpdaterHook(LrUpdaterHook): - """Cosine annealing with restarts learning rate scheme. - - Args: - periods (list[int]): Periods for each cosine anneling cycle. - restart_weights (list[float]): Restart weights at each - restart iteration. Defaults to [1]. - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - periods: List[int], - restart_weights: List[float] = [1], - min_lr: Optional[float] = None, - min_lr_ratio: Optional[float] = None, - **kwargs) -> None: - assert (min_lr is None) ^ (min_lr_ratio is None) - self.periods = periods - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - self.restart_weights = restart_weights - assert (len(self.periods) == len(self.restart_weights) - ), 'periods and restart_weights should have the same length.' - super().__init__(**kwargs) - - self.cumulative_periods = [ - sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) - ] - - def get_lr(self, runner: 'runner.BaseRunner', base_lr: float): - if self.by_epoch: - progress = runner.epoch - else: - progress = runner.iter - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr # type:ignore - - idx = get_position_from_periods(progress, self.cumulative_periods) - current_weight = self.restart_weights[idx] - nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] - current_periods = self.periods[idx] - - alpha = min((progress - nearest_restart) / current_periods, 1) - return annealing_cos(base_lr, target_lr, alpha, current_weight) - - -def get_position_from_periods(iteration: int, cumulative_periods: List[int]): - """Get the position from a period list. - - It will return the index of the right-closest number in the period list. - For example, the cumulative_periods = [100, 200, 300, 400], - if iteration == 50, return 0; - if iteration == 210, return 2; - if iteration == 300, return 3. - - Args: - iteration (int): Current iteration. - cumulative_periods (list[int]): Cumulative period list. - - Returns: - int: The position of the right-closest number in the period list. - """ - for i, period in enumerate(cumulative_periods): - if iteration < period: - return i - raise ValueError(f'Current iteration {iteration} exceeds ' - f'cumulative_periods {cumulative_periods}') - - -@HOOKS.register_module() -class CyclicLrUpdaterHook(LrUpdaterHook): - """Cyclic LR Scheduler. - - Implement the cyclical learning rate policy (CLR) described in - https://arxiv.org/pdf/1506.01186.pdf - - Different from the original paper, we use cosine annealing rather than - triangular policy inside a cycle. This improves the performance in the - 3D detection area. - - Args: - by_epoch (bool, optional): Whether to update LR by epoch. - target_ratio (tuple[float], optional): Relative ratio of the highest LR - and the lowest LR to the initial LR. - cyclic_times (int, optional): Number of cycles during training - step_ratio_up (float, optional): The ratio of the increasing process of - LR in the total cycle. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch: bool = False, - target_ratio: Union[float, tuple] = (10, 1e-4), - cyclic_times: int = 1, - step_ratio_up: float = 0.4, - anneal_strategy: str = 'cos', - gamma: float = 1, - **kwargs) -> None: - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - assert 0 < gamma <= 1, \ - '"gamma" must be in range (0, 1]' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.max_iter_per_phase = None - self.lr_phases: list = [] # init lr_phases - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func: Callable[[float, float, float], - float] = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super().__init__(by_epoch, **kwargs) - - def before_run(self, runner: 'runner.BaseRunner'): - super().before_run(runner) - # initiate lr_phases - # total lr_phases are separated as up and down - self.max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * - self.max_iter_per_phase) # type:ignore - self.lr_phases.append([0, iter_up_phase, 1, self.target_ratio[0]]) - self.lr_phases.append([ - iter_up_phase, self.max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_lr(self, runner: 'runner.BaseRunner', base_lr: float): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - # Update weight decay - scale = self.gamma**curr_cycle - - for (start_iter, end_iter, start_ratio, end_ratio) in self.lr_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_lr and base lr. The target end_ratio can be - # expressed as: - # end_ratio = (base_lr + scale * (max_lr - base_lr)) / base_lr - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_lr * start_ratio, - base_lr * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleLrUpdaterHook(LrUpdaterHook): - """One Cycle LR Scheduler. - - The 1cycle learning rate policy changes the learning rate after every - batch. The one cycle learning rate policy is described in - https://arxiv.org/pdf/1708.07120.pdf - - Args: - max_lr (float or list): Upper learning rate boundaries in the cycle - for each parameter group. - total_steps (int, optional): The total number of steps in the cycle. - Note that if a value is not provided here, it will be the max_iter - of runner. Default: None. - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - div_factor (float): Determines the initial learning rate via - initial_lr = max_lr/div_factor - Default: 25 - final_div_factor (float): Determines the minimum learning rate via - min_lr = initial_lr/final_div_factor - Default: 1e4 - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - max_lr: Union[float, List], - total_steps: Optional[int] = None, - pct_start: float = 0.3, - anneal_strategy: str = 'cos', - div_factor: float = 25, - final_div_factor: float = 1e4, - three_phase: bool = False, - **kwargs) -> None: - # validate by_epoch, currently only support by_epoch = False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(max_lr, (numbers.Number, list, dict)): - raise ValueError('the type of max_lr must be the one of list or ' - f'dict, but got {type(max_lr)}') - self._max_lr = max_lr - if total_steps is not None: - if not isinstance(total_steps, int): - raise ValueError('the type of total_steps must be int, but' - f'got {type(total_steps)}') - self.total_steps = total_steps - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func: Callable[[float, float, float], - float] = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.div_factor = div_factor - self.final_div_factor = final_div_factor - self.three_phase = three_phase - self.lr_phases: list = [] # init lr_phases - super().__init__(**kwargs) - - def before_run(self, runner: 'runner.BaseRunner'): - if hasattr(self, 'total_steps'): - total_steps = self.total_steps - else: - total_steps = runner.max_iters - if total_steps < runner.max_iters: - raise ValueError( - 'The total steps must be greater than or equal to max ' - f'iterations {runner.max_iters} of runner, but total steps ' - f'is {total_steps}.') - - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - _max_lr = format_param(k, optim, self._max_lr) - self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(optim.param_groups, self.base_lr[k]): - group.setdefault('initial_lr', lr) - else: - k = type(runner.optimizer).__name__ - _max_lr = format_param(k, runner.optimizer, self._max_lr) - self.base_lr = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(runner.optimizer.param_groups, self.base_lr): - group.setdefault('initial_lr', lr) - - if self.three_phase: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append([ - float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 - ]) - self.lr_phases.append( - [total_steps - 1, 1, 1 / self.final_div_factor]) - else: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append( - [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) - - def get_lr(self, runner: 'runner.BaseRunner', base_lr: float): - curr_iter = runner.iter - start_iter = 0 - for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): - if curr_iter <= end_iter: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, - pct) - break - start_iter = end_iter - return lr - - -@HOOKS.register_module() -class LinearAnnealingLrUpdaterHook(LrUpdaterHook): - """Linear annealing LR Scheduler decays the learning rate of each parameter - group linearly. - - Args: - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - min_lr: Optional[float] = None, - min_lr_ratio: Optional[float] = None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super().__init__(**kwargs) - - def get_lr(self, runner: 'runner.BaseRunner', base_lr: float): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr # type:ignore - return annealing_linear(base_lr, target_lr, progress / max_progress) - - -def annealing_cos(start: float, - end: float, - factor: float, - weight: float = 1) -> float: - """Calculate annealing cos learning rate. - - Cosine anneal from `weight * start + (1 - weight) * end` to `end` as - percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the cosine annealing. - end (float): The ending learing rate of the cosine annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - weight (float, optional): The combination factor of `start` and `end` - when calculating the actual starting learning rate. Default to 1. - """ - cos_out = cos(pi * factor) + 1 - return end + 0.5 * weight * (start - end) * cos_out - - -def annealing_linear(start: float, end: float, factor: float) -> float: - """Calculate annealing linear learning rate. - - Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the linear annealing. - end (float): The ending learing rate of the linear annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - """ - return start + (end - start) * factor - - -def format_param(name, optim, param): - if isinstance(param, numbers.Number): - return [param] * len(optim.param_groups) - elif isinstance(param, (list, tuple)): # multi param groups - if len(param) != len(optim.param_groups): - raise ValueError(f'expected {len(optim.param_groups)} ' - f'values for {name}, got {len(param)}') - return param - else: # multi optimizers - if name not in param: - raise KeyError(f'{name} is not found in {param.keys()}') - return param[name] diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/memory.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/memory.py deleted file mode 100644 index 70cf9a838..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class EmptyCacheHook(Hook): - - def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): - self._before_epoch = before_epoch - self._after_epoch = after_epoch - self._after_iter = after_iter - - def after_iter(self, runner): - if self._after_iter: - torch.cuda.empty_cache() - - def before_epoch(self, runner): - if self._before_epoch: - torch.cuda.empty_cache() - - def after_epoch(self, runner): - if self._after_epoch: - torch.cuda.empty_cache() diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/momentum_updater.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/momentum_updater.py deleted file mode 100644 index c86d6e905..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/momentum_updater.py +++ /dev/null @@ -1,566 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -from .hook import HOOKS, Hook -from .lr_updater import annealing_cos, annealing_linear, format_param - - -class MomentumUpdaterHook(Hook): - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.9): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_momentum" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - - self.base_momentum = [] # initial momentum for all param groups - self.regular_momentum = [ - ] # expected momentum if no warming up is performed - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, base_momentum): - raise NotImplementedError - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k in runner.optimizer.keys(): - _momentum_group = [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum[k] - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - return [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum - ] - - def get_warmup_momentum(self, cur_iters): - - def _get_warmup_momentum(cur_iters, regular_momentum): - if self.warmup == 'constant': - warmup_momentum = [ - _momentum / self.warmup_ratio - for _momentum in regular_momentum - ] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_momentum = [ - _momentum / (1 - k) for _momentum in regular_momentum - ] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_momentum = [ - _momentum / k for _momentum in regular_momentum - ] - return warmup_momentum - - if isinstance(self.regular_momentum, dict): - momentum_groups = {} - for key, regular_momentum in self.regular_momentum.items(): - momentum_groups[key] = _get_warmup_momentum( - cur_iters, regular_momentum) - return momentum_groups - else: - return _get_warmup_momentum(cur_iters, self.regular_momentum) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, - # if 'initial_momentum' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_momentum = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - _base_momentum = [ - group['initial_momentum'] for group in optim.param_groups - ] - self.base_momentum.update({k: _base_momentum}) - else: - for group in runner.optimizer.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - self.base_momentum = [ - group['initial_momentum'] - for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if not self.by_epoch: - return - self.regular_momentum = self.get_regular_momentum(runner) - self._set_momentum(runner, self.regular_momentum) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_momentum = self.get_regular_momentum(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_momentum(runner, self.regular_momentum) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - - -@HOOKS.register_module() -class StepMomentumUpdaterHook(MomentumUpdaterHook): - """Step momentum scheduler with min value clipping. - - Args: - step (int | list[int]): Step to decay the momentum. If an int value is - given, regard it as the decay interval. If a list is given, decay - momentum at these steps. - gamma (float, optional): Decay momentum ratio. Default: 0.5. - min_momentum (float, optional): Minimum momentum value to keep. If - momentum after decay is lower than this value, it will be clipped - accordingly. If None is given, we don't perform lr clipping. - Default: None. - """ - - def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_momentum = min_momentum - super().__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - momentum = base_momentum * (self.gamma**exp) - if self.min_momentum is not None: - # clip to a minimum value - momentum = max(momentum, self.min_momentum) - return momentum - - -@HOOKS.register_module() -class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Cosine annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super().__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_cos(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class LinearAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - """Linear annealing LR Momentum decays the Momentum of each parameter group - linearly. - - Args: - min_momentum (float, optional): The minimum momentum. Default: None. - min_momentum_ratio (float, optional): The ratio of minimum momentum to - the base momentum. Either `min_momentum` or `min_momentum_ratio` - should be specified. Default: None. - """ - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super().__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_linear(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class CyclicMomentumUpdaterHook(MomentumUpdaterHook): - """Cyclic momentum Scheduler. - - Implement the cyclical momentum scheduler policy described in - https://arxiv.org/pdf/1708.07120.pdf - - This momentum scheduler usually used together with the CyclicLRUpdater - to improve the performance in the 3D detection area. - - Args: - target_ratio (tuple[float]): Relative ratio of the lowest momentum and - the highest momentum to the initial momentum. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of momentum - in the total cycle. - by_epoch (bool): Whether to update momentum by epoch. - anneal_strategy (str, optional): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - gamma (float, optional): Cycle decay ratio. Default: 1. - It takes values in the range (0, 1]. The difference between the - maximum learning rate and the minimum learning rate decreases - periodically when it is less than 1. `New in version 1.4.4.` - """ - - def __init__(self, - by_epoch=False, - target_ratio=(0.85 / 0.95, 1), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - gamma=1, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.gamma = gamma - self.momentum_phases = [] # init momentum_phases - - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - # currently only support by_epoch=False - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super().__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super().before_run(runner) - # initiate momentum_phases - # total momentum_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.max_iter_per_phase = max_iter_per_phase - self.momentum_phases.append( - [0, iter_up_phase, 1, self.target_ratio[0]]) - self.momentum_phases.append([ - iter_up_phase, max_iter_per_phase, self.target_ratio[0], - self.target_ratio[1] - ]) - - def get_momentum(self, runner, base_momentum): - curr_iter = runner.iter % self.max_iter_per_phase - curr_cycle = runner.iter // self.max_iter_per_phase - scale = self.gamma**curr_cycle - for (start_iter, end_iter, start_ratio, end_ratio) \ - in self.momentum_phases: - if start_iter <= curr_iter < end_iter: - # Apply cycle scaling to gradually reduce the difference - # between max_momentum and base momentum. The target end_ratio - # can be expressed as: - # end_ratio = (base_momentum + scale * \ - # (max_momentum - base_momentum)) / base_momentum - # iteration: 0-iter_up_phase: - if start_iter == 0: - end_ratio = 1 - scale + end_ratio * scale - # iteration: iter_up_phase-self.max_iter_per_phase - else: - start_ratio = 1 - scale + start_ratio * scale - progress = curr_iter - start_iter - return self.anneal_func(base_momentum * start_ratio, - base_momentum * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): - """OneCycle momentum Scheduler. - - This momentum scheduler usually used together with the OneCycleLrUpdater - to improve the performance. - - Args: - base_momentum (float or list): Lower momentum boundaries in the cycle - for each parameter group. Note that momentum is cycled inversely - to learning rate; at the peak of a cycle, momentum is - 'base_momentum' and learning rate is 'max_lr'. - Default: 0.85 - max_momentum (float or list): Upper momentum boundaries in the cycle - for each parameter group. Functionally, - it defines the cycle amplitude (max_momentum - base_momentum). - Note that momentum is cycled inversely - to learning rate; at the start of a cycle, momentum is - 'max_momentum' and learning rate is 'base_lr' - Default: 0.95 - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - base_momentum=0.85, - max_momentum=0.95, - pct_start=0.3, - anneal_strategy='cos', - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch=False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(base_momentum, (float, list, dict)): - raise ValueError('base_momentum must be the type among of float,' - 'list or dict.') - self._base_momentum = base_momentum - if not isinstance(max_momentum, (float, list, dict)): - raise ValueError('max_momentum must be the type among of float,' - 'list or dict.') - self._max_momentum = max_momentum - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('Expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must by one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.three_phase = three_phase - self.momentum_phases = [] # init momentum_phases - super().__init__(**kwargs) - - def before_run(self, runner): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip( - optim.param_groups, _base_momentum, _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - else: - optim = runner.optimizer - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - k = type(optim).__name__ - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip(optim.param_groups, - _base_momentum, - _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - - if self.three_phase: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': - float(2 * self.pct_start * runner.max_iters) - 2, - 'start_momentum': - 'base_momentum', - 'end_momentum': - 'max_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'max_momentum', - 'end_momentum': 'max_momentum' - }) - else: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'base_momentum', - 'end_momentum': 'max_momentum' - }) - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, param_group): - curr_iter = runner.iter - start_iter = 0 - for i, phase in enumerate(self.momentum_phases): - end_iter = phase['end_iter'] - if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - momentum = self.anneal_func( - param_group[phase['start_momentum']], - param_group[phase['end_momentum']], pct) - break - start_iter = end_iter - return momentum - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k, optim in runner.optimizer.items(): - _momentum_group = [ - self.get_momentum(runner, param_group) - for param_group in optim.param_groups - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - momentum_groups = [] - for param_group in runner.optimizer.param_groups: - momentum_groups.append(self.get_momentum(runner, param_group)) - return momentum_groups diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/optimizer.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/optimizer.py deleted file mode 100644 index a5c10988d..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/optimizer.py +++ /dev/null @@ -1,554 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -from collections import defaultdict -from itertools import chain - -from torch.nn.utils import clip_grad - -from mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version -from ..dist_utils import allreduce_grads -from ..fp16_utils import LossScaler, wrap_fp16_model -from .hook import HOOKS, Hook - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - from torch.cuda.amp import GradScaler -except ImportError: - pass - - -@HOOKS.register_module() -class OptimizerHook(Hook): - """A hook contains custom operations for the optimizer. - - Args: - grad_clip (dict, optional): A config dict to control the clip_grad. - Default: None. - detect_anomalous_params (bool): This option is only used for - debugging which will slow down the training speed. - Detect anomalous parameters that are not included in - the computational graph with `loss` as the root. - There are two cases - - - Parameters were not used during - forward pass. - - Parameters were not used to produce - loss. - Default: False. - """ - - def __init__(self, grad_clip=None, detect_anomalous_params=False): - self.grad_clip = grad_clip - self.detect_anomalous_params = detect_anomalous_params - - def clip_grads(self, params): - params = list( - filter(lambda p: p.requires_grad and p.grad is not None, params)) - if len(params) > 0: - return clip_grad.clip_grad_norm_(params, **self.grad_clip) - - def after_train_iter(self, runner): - runner.optimizer.zero_grad() - if self.detect_anomalous_params: - self.detect_anomalous_parameters(runner.outputs['loss'], runner) - runner.outputs['loss'].backward() - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - - def detect_anomalous_parameters(self, loss, runner): - logger = runner.logger - parameters_in_graph = set() - visited = set() - - def traverse(grad_fn): - if grad_fn is None: - return - if grad_fn not in visited: - visited.add(grad_fn) - if hasattr(grad_fn, 'variable'): - parameters_in_graph.add(grad_fn.variable) - parents = grad_fn.next_functions - if parents is not None: - for parent in parents: - grad_fn = parent[0] - traverse(grad_fn) - - traverse(loss.grad_fn) - for n, p in runner.model.named_parameters(): - if p not in parameters_in_graph and p.requires_grad: - logger.log( - level=logging.ERROR, - msg=f'{n} with shape {p.size()} is not ' - f'in the computational graph \n') - - -@HOOKS.register_module() -class GradientCumulativeOptimizerHook(OptimizerHook): - """Optimizer Hook implements multi-iters gradient cumulating. - - Args: - cumulative_iters (int, optional): Num of gradient cumulative iters. - The optimizer will step every `cumulative_iters` iters. - Defaults to 1. - - Examples: - >>> # Use cumulative_iters to simulate a large batch size - >>> # It is helpful when the hardware cannot handle a large batch size. - >>> loader = DataLoader(data, batch_size=64) - >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) - >>> # almost equals to - >>> loader = DataLoader(data, batch_size=256) - >>> optim_hook = OptimizerHook() - """ - - def __init__(self, cumulative_iters=1, **kwargs): - super().__init__(**kwargs) - - assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ - f'cumulative_iters only accepts positive int, but got ' \ - f'{type(cumulative_iters)} instead.' - - self.cumulative_iters = cumulative_iters - self.divisible_iters = 0 - self.remainder_iters = 0 - self.initialized = False - - def has_batch_norm(self, module): - if isinstance(module, _BatchNorm): - return True - for m in module.children(): - if self.has_batch_norm(m): - return True - return False - - def _init(self, runner): - if runner.iter % self.cumulative_iters != 0: - runner.logger.warning( - 'Resume iter number is not divisible by cumulative_iters in ' - 'GradientCumulativeOptimizerHook, which means the gradient of ' - 'some iters is lost and the result may be influenced slightly.' - ) - - if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: - runner.logger.warning( - 'GradientCumulativeOptimizerHook may slightly decrease ' - 'performance if the model has BatchNorm layers.') - - residual_iters = runner.max_iters - runner.iter - - self.divisible_iters = ( - residual_iters // self.cumulative_iters * self.cumulative_iters) - self.remainder_iters = residual_iters - self.divisible_iters - - self.initialized = True - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - runner.optimizer.zero_grad() - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - self.loss_scaler = GradScaler() - elif isinstance(loss_scale, float): - self._scale_update_param = loss_scale - self.loss_scaler = GradScaler(init_scale=loss_scale) - elif isinstance(loss_scale, dict): - self.loss_scaler = GradScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training.""" - # wrap model mode to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer to - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients. - 3. Unscale the optimizer’s gradient tensors. - 4. Call optimizer.step() and update scale factor. - 5. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - - self.loss_scaler.scale(runner.outputs['loss']).backward() - self.loss_scaler.unscale_(runner.optimizer) - # grad clip - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using PyTorch's implementation) implements - multi-iters gradient cumulating. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - - self.loss_scaler.scale(loss).backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - self.loss_scaler.unscale_(runner.optimizer) - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() - -else: - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): # type: ignore - """FP16 optimizer hook (mmcv's implementation). - - The steps of fp16 optimizer is as follows. - 1. Scale the loss value. - 2. BP in the fp16 model. - 2. Copy gradients from fp16 model to fp32 weights. - 3. Update fp32 weights. - 4. Copy updated parameters from fp32 weights to fp16 model. - - Refer to https://arxiv.org/abs/1710.03740 for more details. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of LossScaler. - Defaults to 512. - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - if loss_scale == 'dynamic': - self.loss_scaler = LossScaler(mode='dynamic') - elif isinstance(loss_scale, float): - self.loss_scaler = LossScaler( - init_scale=loss_scale, mode='static') - elif isinstance(loss_scale, dict): - self.loss_scaler = LossScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training. - - 1. Make a master copy of fp32 weights for optimization. - 2. Convert the main model from fp32 to fp16. - """ - # keep a copy of fp32 weights - old_groups = runner.optimizer.param_groups - runner.optimizer.param_groups = copy.deepcopy( - runner.optimizer.param_groups) - state = defaultdict(dict) - p_map = { - old_p: p - for old_p, p in zip( - chain(*(g['params'] for g in old_groups)), - chain(*(g['params'] - for g in runner.optimizer.param_groups))) - } - for k, v in runner.optimizer.state.items(): - state[p_map[k]] = v - runner.optimizer.state = state - # convert model to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer `loss_scalar.py` - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients (fp16). - 3. Copy gradients from the model to the fp32 weight copy. - 4. Scale the gradients back and update the fp32 weight copy. - 5. Copy back the params from fp32 weight copy to the fp16 model. - 6. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - # scale the loss value - scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale - scaled_loss.backward() - # copy fp16 grads in the model to fp32 params in the optimizer - - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - self.loss_scaler.update_scale(has_overflow) - if has_overflow: - runner.logger.warning('Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook( # type: ignore - GradientCumulativeOptimizerHook, Fp16OptimizerHook): - """Fp16 optimizer Hook (using mmcv implementation) implements multi- - iters gradient cumulating.""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - - loss = runner.outputs['loss'] - loss = loss / loss_factor - - # scale the loss value - scaled_loss = loss * self.loss_scaler.loss_scale - scaled_loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - else: - runner.logger.warning( - 'Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - self.loss_scaler.update_scale(has_overflow) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/profiler.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/profiler.py deleted file mode 100644 index fef9adc13..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/profiler.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Callable, List, Optional, Union - -import torch - -from ..dist_utils import master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ProfilerHook(Hook): - """Profiler to analyze performance during training. - - PyTorch Profiler is a tool that allows the collection of the performance - metrics during the training. More details on Profiler can be found at - https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile - - Args: - by_epoch (bool): Profile performance by epoch or by iteration. - Default: True. - profile_iters (int): Number of iterations for profiling. - If ``by_epoch=True``, profile_iters indicates that they are the - first profile_iters epochs at the beginning of the - training, otherwise it indicates the first profile_iters - iterations. Default: 1. - activities (list[str]): List of activity groups (CPU, CUDA) to use in - profiling. Default: ['cpu', 'cuda']. - schedule (dict, optional): Config of generating the callable schedule. - if schedule is None, profiler will not add step markers into the - trace and table view. Default: None. - on_trace_ready (callable, dict): Either a handler or a dict of generate - handler. Default: None. - record_shapes (bool): Save information about operator's input shapes. - Default: False. - profile_memory (bool): Track tensor memory allocation/deallocation. - Default: False. - with_stack (bool): Record source information (file and line number) - for the ops. Default: False. - with_flops (bool): Use formula to estimate the FLOPS of specific - operators (matrix multiplication and 2D convolution). - Default: False. - json_trace_path (str, optional): Exports the collected trace in Chrome - JSON format. Default: None. - - Example: - >>> runner = ... # instantiate a Runner - >>> # tensorboard trace - >>> trace_config = dict(type='tb_trace', dir_name='work_dir') - >>> profiler_config = dict(on_trace_ready=trace_config) - >>> runner.register_profiler_hook(profiler_config) - >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) - """ - - def __init__(self, - by_epoch: bool = True, - profile_iters: int = 1, - activities: List[str] = ['cpu', 'cuda'], - schedule: Optional[dict] = None, - on_trace_ready: Optional[Union[Callable, dict]] = None, - record_shapes: bool = False, - profile_memory: bool = False, - with_stack: bool = False, - with_flops: bool = False, - json_trace_path: Optional[str] = None) -> None: - try: - from torch import profiler # torch version >= 1.8.1 - except ImportError: - raise ImportError('profiler is the new feature of torch1.8.1, ' - f'but your version is {torch.__version__}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' - self.by_epoch = by_epoch - - if profile_iters < 1: - raise ValueError('profile_iters should be greater than 0, but got ' - f'{profile_iters}') - self.profile_iters = profile_iters - - if not isinstance(activities, list): - raise ValueError( - f'activities should be list, but got {type(activities)}') - self.activities = [] - for activity in activities: - activity = activity.lower() - if activity == 'cpu': - self.activities.append(profiler.ProfilerActivity.CPU) - elif activity == 'cuda': - self.activities.append(profiler.ProfilerActivity.CUDA) - else: - raise ValueError( - f'activity should be "cpu" or "cuda", but got {activity}') - - if schedule is not None: - self.schedule = profiler.schedule(**schedule) - else: - self.schedule = None - - self.on_trace_ready = on_trace_ready - self.record_shapes = record_shapes - self.profile_memory = profile_memory - self.with_stack = with_stack - self.with_flops = with_flops - self.json_trace_path = json_trace_path - - @master_only - def before_run(self, runner): - if self.by_epoch and runner.max_epochs < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_epochs}') - - if not self.by_epoch and runner.max_iters < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_iters}') - - if callable(self.on_trace_ready): # handler - _on_trace_ready = self.on_trace_ready - elif isinstance(self.on_trace_ready, dict): # config of handler - trace_cfg = self.on_trace_ready.copy() - trace_type = trace_cfg.pop('type') # log_trace handler - if trace_type == 'log_trace': - - def _log_handler(prof): - print(prof.key_averages().table(**trace_cfg)) - - _on_trace_ready = _log_handler - elif trace_type == 'tb_trace': # tensorboard_trace handler - try: - import torch_tb_profiler # noqa: F401 - except ImportError: - raise ImportError('please run "pip install ' - 'torch-tb-profiler" to install ' - 'torch_tb_profiler') - _on_trace_ready = torch.profiler.tensorboard_trace_handler( - **trace_cfg) - else: - raise ValueError('trace_type should be "log_trace" or ' - f'"tb_trace", but got {trace_type}') - elif self.on_trace_ready is None: - _on_trace_ready = None # type: ignore - else: - raise ValueError('on_trace_ready should be handler, dict or None, ' - f'but got {type(self.on_trace_ready)}') - - if self.by_epoch and runner.max_epochs > 1: - warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' - 'instead of 1 epoch. Since profiler will slow down ' - 'the training, it is recommended to train 1 epoch ' - 'with ProfilerHook and adjust your setting according' - ' to the profiler summary. During normal training ' - '(epoch > 1), you may disable the ProfilerHook.') - - self.profiler = torch.profiler.profile( - activities=self.activities, - schedule=self.schedule, - on_trace_ready=_on_trace_ready, - record_shapes=self.record_shapes, - profile_memory=self.profile_memory, - with_stack=self.with_stack, - with_flops=self.with_flops) - - self.profiler.__enter__() - runner.logger.info('profiler is profiling...') - - @master_only - def after_train_epoch(self, runner): - if self.by_epoch and runner.epoch == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) - - @master_only - def after_train_iter(self, runner): - self.profiler.step() - if not self.by_epoch and runner.iter == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/sampler_seed.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/sampler_seed.py deleted file mode 100644 index ee0dc6bdd..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/sampler_seed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class DistSamplerSeedHook(Hook): - """Data-loading sampler for distributed training. - - When distributed training, it is only useful in conjunction with - :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same - purpose with :obj:`IterLoader`. - """ - - def before_epoch(self, runner): - if hasattr(runner.data_loader.sampler, 'set_epoch'): - # in case the data loader uses `SequentialSampler` in Pytorch - runner.data_loader.sampler.set_epoch(runner.epoch) - elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): - # batch sampler in pytorch warps the sampler as its attributes. - runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/sync_buffer.py b/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/sync_buffer.py deleted file mode 100644 index 6376b7ff8..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/hooks/sync_buffer.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..dist_utils import allreduce_params -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class SyncBuffersHook(Hook): - """Synchronize model buffers such as running_mean and running_var in BN at - the end of each epoch. - - Args: - distributed (bool): Whether distributed training is used. It is - effective only for distributed training. Defaults to True. - """ - - def __init__(self, distributed=True): - self.distributed = distributed - - def after_epoch(self, runner): - """All-reduce model buffers at the end of each epoch.""" - if self.distributed: - allreduce_params(runner.model.buffers()) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/iter_based_runner.py b/cv/super_resolution/liif/pytorch/mmcv/runner/iter_based_runner.py deleted file mode 100644 index 2de7317b7..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/iter_based_runner.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch -from torch.optim import Optimizer - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .hooks import IterTimerHook -from .utils import get_host_info - - -class IterLoader: - - def __init__(self, dataloader): - self._dataloader = dataloader - self.iter_loader = iter(self._dataloader) - self._epoch = 0 - - @property - def epoch(self): - return self._epoch - - def __next__(self): - try: - data = next(self.iter_loader) - except StopIteration: - self._epoch += 1 - if hasattr(self._dataloader.sampler, 'set_epoch'): - self._dataloader.sampler.set_epoch(self._epoch) - time.sleep(2) # Prevent possible deadlock during epoch transition - self.iter_loader = iter(self._dataloader) - data = next(self.iter_loader) - - return data - - def __len__(self): - return len(self._dataloader) - - -@RUNNERS.register_module() -class IterBasedRunner(BaseRunner): - """Iteration-based Runner. - - This runner train models iteration by iteration. - """ - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._epoch = data_loader.epoch - data_batch = next(data_loader) - self.data_batch = data_batch - self.call_hook('before_train_iter') - outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.train_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_train_iter') - del self.data_batch - self._inner_iter += 1 - self._iter += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - data_batch = next(data_loader) - self.data_batch = data_batch - self.call_hook('before_val_iter') - outputs = self.model.val_step(data_batch, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.val_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_val_iter') - del self.data_batch - self._inner_iter += 1 - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, iters) to specify the - running order and iterations. E.g, [('train', 10000), - ('val', 1000)] means running 10000 iterations for training and - 1000 iterations for validation, iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - """Resume model from checkpoint. - - Args: - checkpoint (str): Checkpoint to resume from. - resume_optimizer (bool, optional): Whether resume the optimizer(s) - if the checkpoint file includes optimizer(s). Default to True. - map_location (str, optional): Same as :func:`torch.load`. - Default to 'default'. - """ - if map_location == 'default': - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - self._inner_iter = checkpoint['meta']['iter'] - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') - - def save_checkpoint(self, - out_dir, - filename_tmpl='iter_{}.pth', - meta=None, - save_optimizer=True, - create_symlink=True): - """Save checkpoint to file. - - Args: - out_dir (str): Directory to save checkpoint files. - filename_tmpl (str, optional): Checkpoint file template. - Defaults to 'iter_{}.pth'. - meta (dict, optional): Metadata to be saved in checkpoint. - Defaults to None. - save_optimizer (bool, optional): Whether save optimizer. - Defaults to True. - create_symlink (bool, optional): Whether create symlink to the - latest checkpoint file. Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.iter + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - custom_hooks_config=None): - """Register default hooks for iter-based training. - - Checkpoint hook, optimizer stepper hook and logger hooks will be set to - `by_epoch=False` by default. - - Default hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - if checkpoint_config is not None: - checkpoint_config.setdefault('by_epoch', False) - if lr_config is not None: - lr_config.setdefault('by_epoch', False) - if log_config is not None: - for info in log_config['hooks']: - info.setdefault('by_epoch', False) - super().register_training_hooks( - lr_config=lr_config, - momentum_config=momentum_config, - optimizer_config=optimizer_config, - checkpoint_config=checkpoint_config, - log_config=log_config, - timer_config=IterTimerHook(), - custom_hooks_config=custom_hooks_config) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/log_buffer.py b/cv/super_resolution/liif/pytorch/mmcv/runner/log_buffer.py deleted file mode 100644 index 3c9f37963..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/log_buffer.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import numpy as np - - -class LogBuffer: - - def __init__(self): - self.val_history = OrderedDict() - self.n_history = OrderedDict() - self.output = OrderedDict() - self.ready = False - - def clear(self) -> None: - self.val_history.clear() - self.n_history.clear() - self.clear_output() - - def clear_output(self) -> None: - self.output.clear() - self.ready = False - - def update(self, vars: dict, count: int = 1) -> None: - assert isinstance(vars, dict) - for key, var in vars.items(): - if key not in self.val_history: - self.val_history[key] = [] - self.n_history[key] = [] - self.val_history[key].append(var) - self.n_history[key].append(count) - - def average(self, n: int = 0) -> None: - """Average latest n values or all values.""" - assert n >= 0 - for key in self.val_history: - values = np.array(self.val_history[key][-n:]) - nums = np.array(self.n_history[key][-n:]) - avg = np.sum(values * nums) / np.sum(nums) - self.output[key] = avg - self.ready = True diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/optimizer/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/runner/optimizer/__init__.py deleted file mode 100644 index 53c34d047..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/optimizer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, - build_optimizer_constructor) -from .default_constructor import DefaultOptimizerConstructor - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', - 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/optimizer/builder.py b/cv/super_resolution/liif/pytorch/mmcv/runner/optimizer/builder.py deleted file mode 100644 index 49d8f05a2..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/optimizer/builder.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect -from typing import Dict, List - -import torch - -from ...utils import Registry, build_from_cfg - -OPTIMIZERS = Registry('optimizer') -OPTIMIZER_BUILDERS = Registry('optimizer builder') - - -def register_torch_optimizers() -> List: - torch_optimizers = [] - for module_name in dir(torch.optim): - if module_name.startswith('__'): - continue - _optim = getattr(torch.optim, module_name) - if inspect.isclass(_optim) and issubclass(_optim, - torch.optim.Optimizer): - OPTIMIZERS.register_module()(_optim) - torch_optimizers.append(module_name) - return torch_optimizers - - -TORCH_OPTIMIZERS = register_torch_optimizers() - - -def build_optimizer_constructor(cfg: Dict): - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - - -def build_optimizer(model, cfg: Dict): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/optimizer/default_constructor.py b/cv/super_resolution/liif/pytorch/mmcv/runner/optimizer/default_constructor.py deleted file mode 100644 index c82b56e52..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/optimizer/default_constructor.py +++ /dev/null @@ -1,258 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Dict, List, Optional, Union - -import torch -import torch.nn as nn -from torch.nn import GroupNorm, LayerNorm - -from mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of -from mmcv.utils.ext_loader import check_ops_exist -from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS - - -@OPTIMIZER_BUILDERS.register_module() -class DefaultOptimizerConstructor: - """Default constructor for optimizers. - - By default each parameter share the same optimizer settings, and we - provide an argument ``paramwise_cfg`` to specify parameter-wise settings. - It is a dict and may contain the following fields: - - - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If - one of the keys in ``custom_keys`` is a substring of the name of one - parameter, then the setting of the parameter will be specified by - ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will - be ignored. It should be noted that the aforementioned ``key`` is the - longest key that is a substring of the name of the parameter. If there - are multiple matched keys with the same length, then the key with lower - alphabet order will be chosen. - ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` - and ``decay_mult``. See Example 2 below. - - ``bias_lr_mult`` (float): It will be multiplied to the learning - rate for all bias parameters (except for those in normalization - layers and offset layers of DCN). - - ``bias_decay_mult`` (float): It will be multiplied to the weight - decay for all bias parameters (except for those in - normalization layers, depthwise conv layers, offset layers of DCN). - - ``norm_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of normalization - layers. - - ``dwconv_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of depthwise conv - layers. - - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning - rate for parameters of offset layer in the deformable convs - of a model. - - ``bypass_duplicate`` (bool): If true, the duplicate parameters - would not be added into optimizer. Default: False. - - Note: - - 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will - override the effect of ``bias_lr_mult`` in the bias of offset layer. - So be careful when using both ``bias_lr_mult`` and - ``dcn_offset_lr_mult``. If you wish to apply both of them to the offset - layer in deformable convs, set ``dcn_offset_lr_mult`` to the original - ``dcn_offset_lr_mult`` * ``bias_lr_mult``. - - 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will - apply it to all the DCN layers in the model. So be careful when the - model contains multiple DCN layers in places other than backbone. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - optimizer_cfg (dict): The config dict of the optimizer. - Positional fields are - - - `type`: class name of the optimizer. - - Optional fields are - - - any arguments of the corresponding optimizer type, e.g., - lr, weight_decay, momentum, etc. - paramwise_cfg (dict, optional): Parameter-wise options. - - Example 1: - >>> model = torch.nn.modules.Conv1d(1, 1, 1) - >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, - >>> weight_decay=0.0001) - >>> paramwise_cfg = dict(norm_decay_mult=0.) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - - Example 2: - >>> # assume model have attribute model.backbone and model.cls_head - >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) - >>> paramwise_cfg = dict(custom_keys={ - 'backbone': dict(lr_mult=0.1, decay_mult=0.9)}) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - >>> # Then the `lr` and `weight_decay` for model.backbone is - >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for - >>> # model.cls_head is (0.01, 0.95). - """ - - def __init__(self, - optimizer_cfg: Dict, - paramwise_cfg: Optional[Dict] = None): - if not isinstance(optimizer_cfg, dict): - raise TypeError('optimizer_cfg should be a dict', - f'but got {type(optimizer_cfg)}') - self.optimizer_cfg = optimizer_cfg - self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg - self.base_lr = optimizer_cfg.get('lr', None) - self.base_wd = optimizer_cfg.get('weight_decay', None) - self._validate_cfg() - - def _validate_cfg(self) -> None: - if not isinstance(self.paramwise_cfg, dict): - raise TypeError('paramwise_cfg should be None or a dict, ' - f'but got {type(self.paramwise_cfg)}') - - if 'custom_keys' in self.paramwise_cfg: - if not isinstance(self.paramwise_cfg['custom_keys'], dict): - raise TypeError( - 'If specified, custom_keys must be a dict, ' - f'but got {type(self.paramwise_cfg["custom_keys"])}') - if self.base_wd is None: - for key in self.paramwise_cfg['custom_keys']: - if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: - raise ValueError('base_wd should not be None') - - # get base lr and weight decay - # weight_decay must be explicitly specified if mult is specified - if ('bias_decay_mult' in self.paramwise_cfg - or 'norm_decay_mult' in self.paramwise_cfg - or 'dwconv_decay_mult' in self.paramwise_cfg): - if self.base_wd is None: - raise ValueError('base_wd should not be None') - - def _is_in(self, param_group: Dict, param_group_list: List) -> bool: - assert is_list_of(param_group_list, dict) - param = set(param_group['params']) - param_set = set() - for group in param_group_list: - param_set.update(set(group['params'])) - - return not param.isdisjoint(param_set) - - def add_params(self, - params: List[Dict], - module: nn.Module, - prefix: str = '', - is_dcn_module: Union[int, float, None] = None) -> None: - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - prefix (str): The prefix of the module - is_dcn_module (int|float|None): If the current module is a - submodule of DCN, `is_dcn_module` will be passed to - control conv_offset layer's learning rate. Defaults to None. - """ - # get param-wise options - custom_keys = self.paramwise_cfg.get('custom_keys', {}) - # first sort with alphabet order and then sort with reversed len of str - sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) - - bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) - bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) - norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) - dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) - bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) - dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) - - # special rules for norm layers and depth-wise conv layers - is_norm = isinstance(module, - (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) - is_dwconv = ( - isinstance(module, torch.nn.Conv2d) - and module.in_channels == module.groups) - - for name, param in module.named_parameters(recurse=False): - param_group = {'params': [param]} - if not param.requires_grad: - params.append(param_group) - continue - if bypass_duplicate and self._is_in(param_group, params): - warnings.warn(f'{prefix} is duplicate. It is skipped since ' - f'bypass_duplicate={bypass_duplicate}') - continue - # if the parameter match one of the custom keys, ignore other rules - is_custom = False - for key in sorted_keys: - if key in f'{prefix}.{name}': - is_custom = True - lr_mult = custom_keys[key].get('lr_mult', 1.) - param_group['lr'] = self.base_lr * lr_mult - if self.base_wd is not None: - decay_mult = custom_keys[key].get('decay_mult', 1.) - param_group['weight_decay'] = self.base_wd * decay_mult - break - - if not is_custom: - # bias_lr_mult affects all bias parameters - # except for norm.bias dcn.conv_offset.bias - if name == 'bias' and not (is_norm or is_dcn_module): - param_group['lr'] = self.base_lr * bias_lr_mult - - if (prefix.find('conv_offset') != -1 and is_dcn_module - and isinstance(module, torch.nn.Conv2d)): - # deal with both dcn_offset's bias & weight - param_group['lr'] = self.base_lr * dcn_offset_lr_mult - - # apply weight decay policies - if self.base_wd is not None: - # norm decay - if is_norm: - param_group[ - 'weight_decay'] = self.base_wd * norm_decay_mult - # depth-wise conv - elif is_dwconv: - param_group[ - 'weight_decay'] = self.base_wd * dwconv_decay_mult - # bias lr and decay - elif name == 'bias' and not is_dcn_module: - # TODO: current bias_decay_mult will have affect on DCN - param_group[ - 'weight_decay'] = self.base_wd * bias_decay_mult - params.append(param_group) - - if check_ops_exist(): - from mmcv.ops import DeformConv2d, ModulatedDeformConv2d - is_dcn_module = isinstance(module, - (DeformConv2d, ModulatedDeformConv2d)) - else: - is_dcn_module = False - for child_name, child_mod in module.named_children(): - child_prefix = f'{prefix}.{child_name}' if prefix else child_name - self.add_params( - params, - child_mod, - prefix=child_prefix, - is_dcn_module=is_dcn_module) - - def __call__(self, model: nn.Module): - if hasattr(model, 'module'): - model = model.module - - optimizer_cfg = self.optimizer_cfg.copy() - # if no paramwise option is specified, just use the global setting - if not self.paramwise_cfg: - optimizer_cfg['params'] = model.parameters() - return build_from_cfg(optimizer_cfg, OPTIMIZERS) - - # set param-wise lr and weight decay recursively - params: List[Dict] = [] - self.add_params(params, model) - optimizer_cfg['params'] = params - - return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/priority.py b/cv/super_resolution/liif/pytorch/mmcv/runner/priority.py deleted file mode 100644 index ff644043b..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/priority.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from enum import Enum -from typing import Union - - -class Priority(Enum): - """Hook priority levels. - - +--------------+------------+ - | Level | Value | - +==============+============+ - | HIGHEST | 0 | - +--------------+------------+ - | VERY_HIGH | 10 | - +--------------+------------+ - | HIGH | 30 | - +--------------+------------+ - | ABOVE_NORMAL | 40 | - +--------------+------------+ - | NORMAL | 50 | - +--------------+------------+ - | BELOW_NORMAL | 60 | - +--------------+------------+ - | LOW | 70 | - +--------------+------------+ - | VERY_LOW | 90 | - +--------------+------------+ - | LOWEST | 100 | - +--------------+------------+ - """ - - HIGHEST = 0 - VERY_HIGH = 10 - HIGH = 30 - ABOVE_NORMAL = 40 - NORMAL = 50 - BELOW_NORMAL = 60 - LOW = 70 - VERY_LOW = 90 - LOWEST = 100 - - -def get_priority(priority: Union[int, str, Priority]) -> int: - """Get priority value. - - Args: - priority (int or str or :obj:`Priority`): Priority. - - Returns: - int: The priority value. - """ - if isinstance(priority, int): - if priority < 0 or priority > 100: - raise ValueError('priority must be between 0 and 100') - return priority - elif isinstance(priority, Priority): - return priority.value - elif isinstance(priority, str): - return Priority[priority.upper()].value - else: - raise TypeError('priority must be an integer or Priority enum value') diff --git a/cv/super_resolution/liif/pytorch/mmcv/runner/utils.py b/cv/super_resolution/liif/pytorch/mmcv/runner/utils.py deleted file mode 100644 index 34e4ed5a6..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/runner/utils.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random -import sys -import time -import warnings -from getpass import getuser -from socket import gethostname -from types import ModuleType -from typing import Optional - -import numpy as np -import torch - -import mmcv - - -def get_host_info(): - """Get hostname and username. - - Return empty string if exception raised, e.g. ``getpass.getuser()`` will - lead to error in docker container - """ - host = '' - try: - host = f'{getuser()}@{gethostname()}' - except Exception as e: - warnings.warn(f'Host or user not found: {str(e)}') - finally: - return host - - -def get_time_str(): - return time.strftime('%Y%m%d_%H%M%S', time.localtime()) - - -def obj_from_dict(info: dict, - parent: Optional[ModuleType] = None, - default_args: Optional[dict] = None): - """Initialize an object from dict. - - The dict must contain the key "type", which indicates the object type, it - can be either a string or type, such as "list" or ``list``. Remaining - fields are treated as the arguments for constructing the object. - - Args: - info (dict): Object types and arguments. - parent (:class:`module`): Module which may containing expected object - classes. - default_args (dict, optional): Default arguments for initializing the - object. - - Returns: - any type: Object built from the dict. - """ - assert isinstance(info, dict) and 'type' in info - assert isinstance(default_args, dict) or default_args is None - args = info.copy() - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if parent is not None: - obj_type = getattr(parent, obj_type) - else: - obj_type = sys.modules[obj_type] - elif not isinstance(obj_type, type): - raise TypeError('type must be a str or valid type, but ' - f'got {type(obj_type)}') - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - return obj_type(**args) - - -def set_random_seed(seed: int, - deterministic: bool = False, - use_rank_shift: bool = False) -> None: - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - rank_shift (bool): Whether to add rank number to the random seed to - have different random seed in different threads. Default: False. - """ - if use_rank_shift: - rank, _ = mmcv.runner.get_dist_info() - seed += rank - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/__init__.py b/cv/super_resolution/liif/pytorch/mmcv/utils/__init__.py deleted file mode 100644 index e0825ed67..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/__init__.py +++ /dev/null @@ -1,78 +0,0 @@ -# flake8: noqa -# Copyright (c) OpenMMLab. All rights reserved. -from .config import Config, ConfigDict, DictAction -from .misc import (check_prerequisites, concat_list, deprecated_api_warning, - has_method, import_modules_from_strings, is_list_of, - is_method_overridden, is_seq_of, is_str, is_tuple_of, - iter_cast, list_cast, requires_executable, requires_package, - slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, - to_ntuple, tuple_cast) -from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, - scandir, symlink) -from .progressbar import (ProgressBar, track_iter_progress, - track_parallel_progress, track_progress) -from .testing import (assert_attrs_equal, assert_dict_contains_subset, - assert_dict_has_keys, assert_is_norm_layer, - assert_keys_equal, assert_params_all_zeros, - check_python_script) -from .timer import Timer, TimerError, check_time -from .version_utils import digit_version, get_git_hash - -try: - import torch -except ImportError: - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', - 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', - 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', - 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', - 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', - 'track_progress', 'track_iter_progress', 'track_parallel_progress', - 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', - 'digit_version', 'get_git_hash', 'import_modules_from_strings', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', - 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', - 'is_method_overridden', 'has_method' - ] -else: - from .device_type import IS_IPU_AVAILABLE, IS_MLU_AVAILABLE - from .env import collect_env - from .hub import load_url - from .logging import get_logger, print_log - from .parrots_jit import jit, skip_no_elena - # yapf: disable - from .parrots_wrapper import (IS_CUDA_AVAILABLE, TORCH_VERSION, - BuildExtension, CppExtension, CUDAExtension, - DataLoader, PoolDataLoader, SyncBatchNorm, - _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, - _AvgPoolNd, _BatchNorm, _ConvNd, - _ConvTransposeMixin, _get_cuda_home, - _InstanceNorm, _MaxPoolNd, get_build_config, - is_rocm_pytorch) - # yapf: enable - from .registry import Registry, build_from_cfg - from .seed import worker_init_fn - from .trace import is_jit_tracing - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', - 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', - 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', - 'check_prerequisites', 'requires_package', 'requires_executable', - 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', - 'symlink', 'scandir', 'ProgressBar', 'track_progress', - 'track_iter_progress', 'track_parallel_progress', 'Registry', - 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', - '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', - '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', - 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', - 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', - 'deprecated_api_warning', 'digit_version', 'get_git_hash', - 'import_modules_from_strings', 'jit', 'skip_no_elena', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', - 'assert_params_all_zeros', 'check_python_script', - 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', - '_get_cuda_home', 'load_url', 'has_method', 'IS_CUDA_AVAILABLE', - 'worker_init_fn', 'IS_MLU_AVAILABLE', 'IS_IPU_AVAILABLE' - ] diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/config.py b/cv/super_resolution/liif/pytorch/mmcv/utils/config.py deleted file mode 100644 index a76bc4872..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/config.py +++ /dev/null @@ -1,741 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import ast -import copy -import os -import os.path as osp -import platform -import shutil -import sys -import tempfile -import types -import uuid -import warnings -from argparse import Action, ArgumentParser -from collections import abc -from importlib import import_module -from pathlib import Path - -from addict import Dict -from yapf.yapflib.yapf_api import FormatCode - -from .misc import import_modules_from_strings -from .path import check_file_exist - -if platform.system() == 'Windows': - import regex as re # type: ignore -else: - import re # type: ignore - -BASE_KEY = '_base_' -DELETE_KEY = '_delete_' -DEPRECATION_KEY = '_deprecation_' -RESERVED_KEYS = ['filename', 'text', 'pretty_text'] - - -class ConfigDict(Dict): - - def __missing__(self, name): - raise KeyError(name) - - def __getattr__(self, name): - try: - value = super().__getattr__(name) - except KeyError: - ex = AttributeError(f"'{self.__class__.__name__}' object has no " - f"attribute '{name}'") - except Exception as e: - ex = e - else: - return value - raise ex - - -def add_args(parser, cfg, prefix=''): - for k, v in cfg.items(): - if isinstance(v, str): - parser.add_argument('--' + prefix + k) - elif isinstance(v, int): - parser.add_argument('--' + prefix + k, type=int) - elif isinstance(v, float): - parser.add_argument('--' + prefix + k, type=float) - elif isinstance(v, bool): - parser.add_argument('--' + prefix + k, action='store_true') - elif isinstance(v, dict): - add_args(parser, v, prefix + k + '.') - elif isinstance(v, abc.Iterable): - parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') - else: - print(f'cannot parse key {prefix + k} of type {type(v)}') - return parser - - -class Config: - """A facility for config and config files. - - It supports common file formats as configs: python/json/yaml. The interface - is the same as a dict object and also allows access config values as - attributes. - - Example: - >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - >>> cfg.a - 1 - >>> cfg.b - {'b1': [0, 1]} - >>> cfg.b.b1 - [0, 1] - >>> cfg = Config.fromfile('tests/data/config/a.py') - >>> cfg.filename - "/home/kchen/projects/mmcv/tests/data/config/a.py" - >>> cfg.item4 - 'test' - >>> cfg - "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " - "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" - """ - - @staticmethod - def _validate_py_syntax(filename): - with open(filename, encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - content = f.read() - try: - ast.parse(content) - except SyntaxError as e: - raise SyntaxError('There are syntax errors in config ' - f'file {filename}: {e}') - - @staticmethod - def _substitute_predefined_vars(filename, temp_config_name): - file_dirname = osp.dirname(filename) - file_basename = osp.basename(filename) - file_basename_no_extension = osp.splitext(file_basename)[0] - file_extname = osp.splitext(filename)[1] - support_templates = dict( - fileDirname=file_dirname, - fileBasename=file_basename, - fileBasenameNoExtension=file_basename_no_extension, - fileExtname=file_extname) - with open(filename, encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - for key, value in support_templates.items(): - regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' - value = value.replace('\\', '/') - config_file = re.sub(regexp, value, config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - - @staticmethod - def _pre_substitute_base_vars(filename, temp_config_name): - """Substitute base variable placehoders to string, so that parsing - would work.""" - with open(filename, encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - base_var_dict = {} - regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' - base_vars = set(re.findall(regexp, config_file)) - for base_var in base_vars: - randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' - base_var_dict[randstr] = base_var - regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' - config_file = re.sub(regexp, f'"{randstr}"', config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - return base_var_dict - - @staticmethod - def _substitute_base_vars(cfg, base_var_dict, base_cfg): - """Substitute variable strings to their actual values.""" - cfg = copy.deepcopy(cfg) - - if isinstance(cfg, dict): - for k, v in cfg.items(): - if isinstance(v, str) and v in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[v].split('.'): - new_v = new_v[new_k] - cfg[k] = new_v - elif isinstance(v, (list, tuple, dict)): - cfg[k] = Config._substitute_base_vars( - v, base_var_dict, base_cfg) - elif isinstance(cfg, tuple): - cfg = tuple( - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg) - elif isinstance(cfg, list): - cfg = [ - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg - ] - elif isinstance(cfg, str) and cfg in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[cfg].split('.'): - new_v = new_v[new_k] - cfg = new_v - - return cfg - - @staticmethod - def _file2dict(filename, use_predefined_variables=True): - filename = osp.abspath(osp.expanduser(filename)) - check_file_exist(filename) - fileExtname = osp.splitext(filename)[1] - if fileExtname not in ['.py', '.json', '.yaml', '.yml']: - raise OSError('Only py/yml/yaml/json type are supported now!') - - with tempfile.TemporaryDirectory() as temp_config_dir: - temp_config_file = tempfile.NamedTemporaryFile( - dir=temp_config_dir, suffix=fileExtname) - if platform.system() == 'Windows': - temp_config_file.close() - temp_config_name = osp.basename(temp_config_file.name) - # Substitute predefined variables - if use_predefined_variables: - Config._substitute_predefined_vars(filename, - temp_config_file.name) - else: - shutil.copyfile(filename, temp_config_file.name) - # Substitute base variables from placeholders to strings - base_var_dict = Config._pre_substitute_base_vars( - temp_config_file.name, temp_config_file.name) - - if filename.endswith('.py'): - temp_module_name = osp.splitext(temp_config_name)[0] - sys.path.insert(0, temp_config_dir) - Config._validate_py_syntax(filename) - mod = import_module(temp_module_name) - sys.path.pop(0) - cfg_dict = { - name: value - for name, value in mod.__dict__.items() - if not name.startswith('__') - and not isinstance(value, types.ModuleType) - and not isinstance(value, types.FunctionType) - } - # delete imported module - del sys.modules[temp_module_name] - elif filename.endswith(('.yml', '.yaml', '.json')): - import mmcv - cfg_dict = mmcv.load(temp_config_file.name) - # close temp file - temp_config_file.close() - - # check deprecation information - if DEPRECATION_KEY in cfg_dict: - deprecation_info = cfg_dict.pop(DEPRECATION_KEY) - warning_msg = f'The config file {filename} will be deprecated ' \ - 'in the future.' - if 'expected' in deprecation_info: - warning_msg += f' Please use {deprecation_info["expected"]} ' \ - 'instead.' - if 'reference' in deprecation_info: - warning_msg += ' More information can be found at ' \ - f'{deprecation_info["reference"]}' - warnings.warn(warning_msg, DeprecationWarning) - - cfg_text = filename + '\n' - with open(filename, encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - cfg_text += f.read() - - if BASE_KEY in cfg_dict: - cfg_dir = osp.dirname(filename) - base_filename = cfg_dict.pop(BASE_KEY) - base_filename = base_filename if isinstance( - base_filename, list) else [base_filename] - - cfg_dict_list = list() - cfg_text_list = list() - for f in base_filename: - _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) - cfg_dict_list.append(_cfg_dict) - cfg_text_list.append(_cfg_text) - - base_cfg_dict = dict() - for c in cfg_dict_list: - duplicate_keys = base_cfg_dict.keys() & c.keys() - if len(duplicate_keys) > 0: - raise KeyError('Duplicate key is not allowed among bases. ' - f'Duplicate keys: {duplicate_keys}') - base_cfg_dict.update(c) - - # Substitute base variables from strings to their actual values - cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, - base_cfg_dict) - - base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) - cfg_dict = base_cfg_dict - - # merge cfg_text - cfg_text_list.append(cfg_text) - cfg_text = '\n'.join(cfg_text_list) - - return cfg_dict, cfg_text - - @staticmethod - def _merge_a_into_b(a, b, allow_list_keys=False): - """merge dict ``a`` into dict ``b`` (non-inplace). - - Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid - in-place modifications. - - Args: - a (dict): The source dict to be merged into ``b``. - b (dict): The origin dict to be fetch keys from ``a``. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in source ``a`` and will replace the element of the - corresponding index in b if b is a list. Default: False. - - Returns: - dict: The modified dict of ``b`` using ``a``. - - Examples: - # Normally merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # Delete b first and merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # b is a list - >>> Config._merge_a_into_b( - ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) - [{'a': 2}, {'b': 2}] - """ - b = b.copy() - for k, v in a.items(): - if allow_list_keys and k.isdigit() and isinstance(b, list): - k = int(k) - if len(b) <= k: - raise KeyError(f'Index {k} exceeds the length of list {b}') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - elif isinstance(v, dict): - if k in b and not v.pop(DELETE_KEY, False): - allowed_types = (dict, list) if allow_list_keys else dict - if not isinstance(b[k], allowed_types): - raise TypeError( - f'{k}={v} in child config cannot inherit from ' - f'base because {k} is a dict in the child config ' - f'but is of type {type(b[k])} in base config. ' - f'You may set `{DELETE_KEY}=True` to ignore the ' - f'base config.') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - else: - b[k] = ConfigDict(v) - else: - b[k] = v - return b - - @staticmethod - def fromfile(filename, - use_predefined_variables=True, - import_custom_modules=True): - if isinstance(filename, Path): - filename = str(filename) - cfg_dict, cfg_text = Config._file2dict(filename, - use_predefined_variables) - if import_custom_modules and cfg_dict.get('custom_imports', None): - import_modules_from_strings(**cfg_dict['custom_imports']) - return Config(cfg_dict, cfg_text=cfg_text, filename=filename) - - @staticmethod - def fromstring(cfg_str, file_format): - """Generate config from config str. - - Args: - cfg_str (str): Config str. - file_format (str): Config file format corresponding to the - config str. Only py/yml/yaml/json type are supported now! - - Returns: - :obj:`Config`: Config obj. - """ - if file_format not in ['.py', '.json', '.yaml', '.yml']: - raise OSError('Only py/yml/yaml/json type are supported now!') - if file_format != '.py' and 'dict(' in cfg_str: - # check if users specify a wrong suffix for python - warnings.warn( - 'Please check "file_format", the file format may be .py') - with tempfile.NamedTemporaryFile( - 'w', encoding='utf-8', suffix=file_format, - delete=False) as temp_file: - temp_file.write(cfg_str) - # on windows, previous implementation cause error - # see PR 1077 for details - cfg = Config.fromfile(temp_file.name) - os.remove(temp_file.name) - return cfg - - @staticmethod - def auto_argparser(description=None): - """Generate argparser from config file automatically (experimental)""" - partial_parser = ArgumentParser(description=description) - partial_parser.add_argument('config', help='config file path') - cfg_file = partial_parser.parse_known_args()[0].config - cfg = Config.fromfile(cfg_file) - parser = ArgumentParser(description=description) - parser.add_argument('config', help='config file path') - add_args(parser, cfg) - return parser, cfg - - def __init__(self, cfg_dict=None, cfg_text=None, filename=None): - if cfg_dict is None: - cfg_dict = dict() - elif not isinstance(cfg_dict, dict): - raise TypeError('cfg_dict must be a dict, but ' - f'got {type(cfg_dict)}') - for key in cfg_dict: - if key in RESERVED_KEYS: - raise KeyError(f'{key} is reserved for config file') - - if isinstance(filename, Path): - filename = str(filename) - - super().__setattr__('_cfg_dict', ConfigDict(cfg_dict)) - super().__setattr__('_filename', filename) - if cfg_text: - text = cfg_text - elif filename: - with open(filename) as f: - text = f.read() - else: - text = '' - super().__setattr__('_text', text) - - @property - def filename(self): - return self._filename - - @property - def text(self): - return self._text - - @property - def pretty_text(self): - - indent = 4 - - def _indent(s_, num_spaces): - s = s_.split('\n') - if len(s) == 1: - return s_ - first = s.pop(0) - s = [(num_spaces * ' ') + line for line in s] - s = '\n'.join(s) - s = first + '\n' + s - return s - - def _format_basic_types(k, v, use_mapping=False): - if isinstance(v, str): - v_str = f"'{v}'" - else: - v_str = str(v) - - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) - - return attr_str - - def _format_list(k, v, use_mapping=False): - # check if all items in the list are dict - if all(isinstance(_, dict) for _ in v): - v_str = '[\n' - v_str += '\n'.join( - f'dict({_indent(_format_dict(v_), indent)}),' - for v_ in v).rstrip(',') - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) + ']' - else: - attr_str = _format_basic_types(k, v, use_mapping) - return attr_str - - def _contain_invalid_identifier(dict_str): - contain_invalid_identifier = False - for key_name in dict_str: - contain_invalid_identifier |= \ - (not str(key_name).isidentifier()) - return contain_invalid_identifier - - def _format_dict(input_dict, outest_level=False): - r = '' - s = [] - - use_mapping = _contain_invalid_identifier(input_dict) - if use_mapping: - r += '{' - for idx, (k, v) in enumerate(input_dict.items()): - is_last = idx >= len(input_dict) - 1 - end = '' if outest_level or is_last else ',' - if isinstance(v, dict): - v_str = '\n' + _format_dict(v) - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: dict({v_str}' - else: - attr_str = f'{str(k)}=dict({v_str}' - attr_str = _indent(attr_str, indent) + ')' + end - elif isinstance(v, list): - attr_str = _format_list(k, v, use_mapping) + end - else: - attr_str = _format_basic_types(k, v, use_mapping) + end - - s.append(attr_str) - r += '\n'.join(s) - if use_mapping: - r += '}' - return r - - cfg_dict = self._cfg_dict.to_dict() - text = _format_dict(cfg_dict, outest_level=True) - # copied from setup.cfg - yapf_style = dict( - based_on_style='pep8', - blank_line_before_nested_class_or_def=True, - split_before_expression_after_opening_paren=True) - text, _ = FormatCode(text, style_config=yapf_style, verify=True) - - return text - - def __repr__(self): - return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' - - def __len__(self): - return len(self._cfg_dict) - - def __getattr__(self, name): - return getattr(self._cfg_dict, name) - - def __getitem__(self, name): - return self._cfg_dict.__getitem__(name) - - def __setattr__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setattr__(name, value) - - def __setitem__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setitem__(name, value) - - def __iter__(self): - return iter(self._cfg_dict) - - def __getstate__(self): - return (self._cfg_dict, self._filename, self._text) - - def __copy__(self): - cls = self.__class__ - other = cls.__new__(cls) - other.__dict__.update(self.__dict__) - - return other - - def __deepcopy__(self, memo): - cls = self.__class__ - other = cls.__new__(cls) - memo[id(self)] = other - - for key, value in self.__dict__.items(): - super(Config, other).__setattr__(key, copy.deepcopy(value, memo)) - - return other - - def __setstate__(self, state): - _cfg_dict, _filename, _text = state - super().__setattr__('_cfg_dict', _cfg_dict) - super().__setattr__('_filename', _filename) - super().__setattr__('_text', _text) - - def dump(self, file=None): - """Dumps config into a file or returns a string representation of the - config. - - If a file argument is given, saves the config to that file using the - format defined by the file argument extension. - - Otherwise, returns a string representing the config. The formatting of - this returned string is defined by the extension of `self.filename`. If - `self.filename` is not defined, returns a string representation of a - dict (lowercased and using ' for strings). - - Examples: - >>> cfg_dict = dict(item1=[1, 2], item2=dict(a=0), - ... item3=True, item4='test') - >>> cfg = Config(cfg_dict=cfg_dict) - >>> dump_file = "a.py" - >>> cfg.dump(dump_file) - - Args: - file (str, optional): Path of the output file where the config - will be dumped. Defaults to None. - """ - import mmcv - cfg_dict = super().__getattribute__('_cfg_dict').to_dict() - if file is None: - if self.filename is None or self.filename.endswith('.py'): - return self.pretty_text - else: - file_format = self.filename.split('.')[-1] - return mmcv.dump(cfg_dict, file_format=file_format) - elif file.endswith('.py'): - with open(file, 'w', encoding='utf-8') as f: - f.write(self.pretty_text) - else: - file_format = file.split('.')[-1] - return mmcv.dump(cfg_dict, file=file, file_format=file_format) - - def merge_from_dict(self, options, allow_list_keys=True): - """Merge list into cfg_dict. - - Merge the dict parsed by MultipleKVAction into this cfg. - - Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} - >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) - >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - - >>> # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) - >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) - >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - - Args: - options (dict): dict of configs to merge from. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in ``options`` and will replace the element of the - corresponding index in the config if the config is a list. - Default: True. - """ - option_cfg_dict = {} - for full_key, v in options.items(): - d = option_cfg_dict - key_list = full_key.split('.') - for subkey in key_list[:-1]: - d.setdefault(subkey, ConfigDict()) - d = d[subkey] - subkey = key_list[-1] - d[subkey] = v - - cfg_dict = super().__getattribute__('_cfg_dict') - super().__setattr__( - '_cfg_dict', - Config._merge_a_into_b( - option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) - - -class DictAction(Action): - """ - argparse action to split an argument into KEY=VALUE form - on the first = and append to a dictionary. List options can - be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit - brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build - list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' - """ - - @staticmethod - def _parse_int_float_bool(val): - try: - return int(val) - except ValueError: - pass - try: - return float(val) - except ValueError: - pass - if val.lower() in ['true', 'false']: - return True if val.lower() == 'true' else False - if val == 'None': - return None - return val - - @staticmethod - def _parse_iterable(val): - """Parse iterable values in the string. - - All elements inside '()' or '[]' are treated as iterable values. - - Args: - val (str): Value string. - - Returns: - list | tuple: The expanded list or tuple from the string. - - Examples: - >>> DictAction._parse_iterable('1,2,3') - [1, 2, 3] - >>> DictAction._parse_iterable('[a, b, c]') - ['a', 'b', 'c'] - >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') - [(1, 2, 3), ['a', 'b'], 'c'] - """ - - def find_next_comma(string): - """Find the position of next comma in the string. - - If no ',' is found in the string, return the string length. All - chars inside '()' and '[]' are treated as one element and thus ',' - inside these brackets are ignored. - """ - assert (string.count('(') == string.count(')')) and ( - string.count('[') == string.count(']')), \ - f'Imbalanced brackets exist in {string}' - end = len(string) - for idx, char in enumerate(string): - pre = string[:idx] - # The string before this ',' is balanced - if ((char == ',') and (pre.count('(') == pre.count(')')) - and (pre.count('[') == pre.count(']'))): - end = idx - break - return end - - # Strip ' and " characters and replace whitespace. - val = val.strip('\'\"').replace(' ', '') - is_tuple = False - if val.startswith('(') and val.endswith(')'): - is_tuple = True - val = val[1:-1] - elif val.startswith('[') and val.endswith(']'): - val = val[1:-1] - elif ',' not in val: - # val is a single value - return DictAction._parse_int_float_bool(val) - - values = [] - while len(val) > 0: - comma_idx = find_next_comma(val) - element = DictAction._parse_iterable(val[:comma_idx]) - values.append(element) - val = val[comma_idx + 1:] - if is_tuple: - values = tuple(values) - return values - - def __call__(self, parser, namespace, values, option_string=None): - options = {} - for kv in values: - key, val = kv.split('=', maxsplit=1) - options[key] = self._parse_iterable(val) - setattr(namespace, self.dest, options) diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/device_type.py b/cv/super_resolution/liif/pytorch/mmcv/utils/device_type.py deleted file mode 100644 index c66052c2e..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/device_type.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - - -def is_ipu_available() -> bool: - try: - import poptorch - return poptorch.ipuHardwareIsAvailable() - except ImportError: - return False - - -IS_IPU_AVAILABLE = is_ipu_available() - - -def is_mlu_available() -> bool: - try: - import torch - return (hasattr(torch, 'is_mlu_available') - and torch.is_mlu_available()) - except Exception: - return False - - -IS_MLU_AVAILABLE = is_mlu_available() diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/env.py b/cv/super_resolution/liif/pytorch/mmcv/utils/env.py deleted file mode 100644 index 511332506..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/env.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This file holding some environment constant for sharing by other files.""" - -import os.path as osp -import subprocess -import sys -from collections import defaultdict - -import cv2 -import torch - -import mmcv -from .parrots_wrapper import get_build_config - - -def collect_env(): - """Collect the information of the running environments. - - Returns: - dict: The environment information. The following fields are contained. - - - sys.platform: The variable of ``sys.platform``. - - Python: Python version. - - CUDA available: Bool, indicating if CUDA is available. - - GPU devices: Device type of each GPU. - - CUDA_HOME (optional): The env var ``CUDA_HOME``. - - NVCC (optional): NVCC version. - - GCC: GCC version, "n/a" if GCC is not installed. - - MSVC: Microsoft Virtual C++ Compiler version, Windows only. - - PyTorch: PyTorch version. - - PyTorch compiling details: The output of \ - ``torch.__config__.show()``. - - TorchVision (optional): TorchVision version. - - OpenCV: OpenCV version. - - MMCV: MMCV version. - - MMCV Compiler: The GCC version for compiling MMCV ops. - - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. - """ - env_info = {} - env_info['sys.platform'] = sys.platform - env_info['Python'] = sys.version.replace('\n', '') - - cuda_available = torch.cuda.is_available() - env_info['CUDA available'] = cuda_available - - if cuda_available: - devices = defaultdict(list) - for k in range(torch.cuda.device_count()): - devices[torch.cuda.get_device_name(k)].append(str(k)) - for name, device_ids in devices.items(): - env_info['GPU ' + ','.join(device_ids)] = name - - from mmcv.utils.parrots_wrapper import _get_cuda_home - CUDA_HOME = _get_cuda_home() - env_info['CUDA_HOME'] = CUDA_HOME - - if CUDA_HOME is not None and osp.isdir(CUDA_HOME): - try: - nvcc = osp.join(CUDA_HOME, 'bin/nvcc') - nvcc = subprocess.check_output(f'"{nvcc}" -V', shell=True) - nvcc = nvcc.decode('utf-8').strip() - release = nvcc.rfind('Cuda compilation tools') - build = nvcc.rfind('Build ') - nvcc = nvcc[release:build].strip() - except subprocess.SubprocessError: - nvcc = 'Not Available' - env_info['NVCC'] = nvcc - - try: - # Check C++ Compiler. - # For Unix-like, sysconfig has 'CC' variable like 'gcc -pthread ...', - # indicating the compiler used, we use this to get the compiler name - import sysconfig - cc = sysconfig.get_config_var('CC') - if cc: - cc = osp.basename(cc.split()[0]) - cc_info = subprocess.check_output(f'{cc} --version', shell=True) - env_info['GCC'] = cc_info.decode('utf-8').partition( - '\n')[0].strip() - else: - # on Windows, cl.exe is not in PATH. We need to find the path. - # distutils.ccompiler.new_compiler() returns a msvccompiler - # object and after initialization, path to cl.exe is found. - import locale - import os - from distutils.ccompiler import new_compiler - ccompiler = new_compiler() - ccompiler.initialize() - cc = subprocess.check_output( - f'{ccompiler.cc}', stderr=subprocess.STDOUT, shell=True) - encoding = os.device_encoding( - sys.stdout.fileno()) or locale.getpreferredencoding() - env_info['MSVC'] = cc.decode(encoding).partition('\n')[0].strip() - env_info['GCC'] = 'n/a' - except subprocess.CalledProcessError: - env_info['GCC'] = 'n/a' - - env_info['PyTorch'] = torch.__version__ - env_info['PyTorch compiling details'] = get_build_config() - - try: - import torchvision - env_info['TorchVision'] = torchvision.__version__ - except ModuleNotFoundError: - pass - - env_info['OpenCV'] = cv2.__version__ - - env_info['MMCV'] = mmcv.__version__ - - try: - from mmcv.ops import get_compiler_version, get_compiling_cuda_version - except ModuleNotFoundError: - env_info['MMCV Compiler'] = 'n/a' - env_info['MMCV CUDA Compiler'] = 'n/a' - else: - env_info['MMCV Compiler'] = get_compiler_version() - env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() - - return env_info diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/ext_loader.py b/cv/super_resolution/liif/pytorch/mmcv/utils/ext_loader.py deleted file mode 100644 index a31e107df..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/ext_loader.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os -import pkgutil -import warnings -from collections import namedtuple - -import torch - -if torch.__version__ != 'parrots': - - def load_ext(name, funcs): - ext = importlib.import_module('mmcv.' + name) - for fun in funcs: - assert hasattr(ext, fun), f'{fun} miss in module {name}' - return ext -else: - from parrots import extension - from parrots.base import ParrotsException - - has_return_value_ops = [ - 'nms', - 'softnms', - 'nms_match', - 'nms_rotated', - 'top_pool_forward', - 'top_pool_backward', - 'bottom_pool_forward', - 'bottom_pool_backward', - 'left_pool_forward', - 'left_pool_backward', - 'right_pool_forward', - 'right_pool_backward', - 'fused_bias_leakyrelu', - 'upfirdn2d', - 'ms_deform_attn_forward', - 'pixel_group', - 'contour_expand', - 'diff_iou_rotated_sort_vertices_forward', - ] - - def get_fake_func(name, e): - - def fake_func(*args, **kwargs): - warnings.warn(f'{name} is not supported in parrots now') - raise e - - return fake_func - - def load_ext(name, funcs): - ExtModule = namedtuple('ExtModule', funcs) - ext_list = [] - lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - for fun in funcs: - try: - ext_fun = extension.load(fun, name, lib_dir=lib_root) - except ParrotsException as e: - if 'No element registered' not in e.message: - warnings.warn(e.message) - ext_fun = get_fake_func(fun, e) - ext_list.append(ext_fun) - else: - if fun in has_return_value_ops: - ext_list.append(ext_fun.op) - else: - ext_list.append(ext_fun.op_) - return ExtModule(*ext_list) - - -def check_ops_exist() -> bool: - ext_loader = pkgutil.find_loader('mmcv._ext') - return ext_loader is not None diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/hub.py b/cv/super_resolution/liif/pytorch/mmcv/utils/hub.py deleted file mode 100644 index a9cbbc95b..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/hub.py +++ /dev/null @@ -1,131 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# The 1.6 release of PyTorch switched torch.save to use a new zipfile-based -# file format. It will cause RuntimeError when a checkpoint was saved in -# torch >= 1.6.0 but loaded in torch < 1.7.0. -# More details at https://github.com/open-mmlab/mmpose/issues/904 -from .parrots_wrapper import TORCH_VERSION -from .path import mkdir_or_exist -from .version_utils import digit_version - -if TORCH_VERSION != 'parrots' and digit_version(TORCH_VERSION) < digit_version( - '1.7.0'): - # Modified from https://github.com/pytorch/pytorch/blob/master/torch/hub.py - import os - import sys - import warnings - import zipfile - from urllib.parse import urlparse - - import torch - from torch.hub import HASH_REGEX, _get_torch_home, download_url_to_file - - # Hub used to support automatically extracts from zipfile manually - # compressed by users. The legacy zip format expects only one file from - # torch.save() < 1.6 in the zip. We should remove this support since - # zipfile is now default zipfile format for torch.save(). - def _is_legacy_zip_format(filename): - if zipfile.is_zipfile(filename): - infolist = zipfile.ZipFile(filename).infolist() - return len(infolist) == 1 and not infolist[0].is_dir() - return False - - def _legacy_zip_load(filename, model_dir, map_location): - warnings.warn( - 'Falling back to the old format < 1.6. This support will' - ' be deprecated in favor of default zipfile format ' - 'introduced in 1.6. Please redo torch.save() to save it ' - 'in the new zipfile format.', DeprecationWarning) - # Note: extractall() defaults to overwrite file if exists. No need to - # clean up beforehand. We deliberately don't handle tarfile here - # since our legacy serialization format was in tar. - # E.g. resnet18-5c106cde.pth which is widely used. - with zipfile.ZipFile(filename) as f: - members = f.infolist() - if len(members) != 1: - raise RuntimeError( - 'Only one file(not dir) is allowed in the zipfile') - f.extractall(model_dir) - extraced_name = members[0].filename - extracted_file = os.path.join(model_dir, extraced_name) - return torch.load(extracted_file, map_location=map_location) - - def load_url(url, - model_dir=None, - map_location=None, - progress=True, - check_hash=False, - file_name=None): - r"""Loads the Torch serialized object at the given URL. - - If downloaded file is a zip file, it will be automatically decompressed - - If the object is already present in `model_dir`, it's deserialized and - returned. - The default value of ``model_dir`` is ``/checkpoints`` where - ``hub_dir`` is the directory returned by :func:`~torch.hub.get_dir`. - - Args: - url (str): URL of the object to download - model_dir (str, optional): directory in which to save the object - map_location (optional): a function or a dict specifying how to - remap storage locations (see torch.load) - progress (bool, optional): whether or not to display a progress bar - to stderr. Default: True - check_hash(bool, optional): If True, the filename part of the URL - should follow the naming convention ``filename-.ext`` - where ```` is the first eight or more digits of the - SHA256 hash of the contents of the file. The hash is used to - ensure unique names and to verify the contents of the file. - Default: False - file_name (str, optional): name for the downloaded file. Filename - from ``url`` will be used if not set. Default: None. - - Example: - >>> url = ('https://s3.amazonaws.com/pytorch/models/resnet18-5c106' - ... 'cde.pth') - >>> state_dict = torch.hub.load_state_dict_from_url(url) - """ - # Issue warning to move data if old env is set - if os.getenv('TORCH_MODEL_ZOO'): - warnings.warn( - 'TORCH_MODEL_ZOO is deprecated, please use env ' - 'TORCH_HOME instead', DeprecationWarning) - - if model_dir is None: - torch_home = _get_torch_home() - model_dir = os.path.join(torch_home, 'checkpoints') - - mkdir_or_exist(model_dir) - - parts = urlparse(url) - filename = os.path.basename(parts.path) - if file_name is not None: - filename = file_name - cached_file = os.path.join(model_dir, filename) - if not os.path.exists(cached_file): - sys.stderr.write('Downloading: "{}" to {}\n'.format( - url, cached_file)) - hash_prefix = None - if check_hash: - r = HASH_REGEX.search(filename) # r is Optional[Match[str]] - hash_prefix = r.group(1) if r else None - download_url_to_file( - url, cached_file, hash_prefix, progress=progress) - - if _is_legacy_zip_format(cached_file): - return _legacy_zip_load(cached_file, model_dir, map_location) - - try: - return torch.load(cached_file, map_location=map_location) - except RuntimeError as error: - if digit_version(TORCH_VERSION) < digit_version('1.5.0'): - warnings.warn( - f'If the error is the same as "{cached_file} is a zip ' - 'archive (did you mean to use torch.jit.load()?)", you can' - ' upgrade your torch to 1.5.0 or higher (current torch ' - f'version is {TORCH_VERSION}). The error was raised ' - ' because the checkpoint was saved in torch>=1.6.0 but ' - 'loaded in torch<1.5.') - raise error -else: - from torch.utils.model_zoo import load_url # type: ignore # noqa: F401 diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/logging.py b/cv/super_resolution/liif/pytorch/mmcv/utils/logging.py deleted file mode 100644 index 5a90aac8b..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/logging.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.distributed as dist - -logger_initialized: dict = {} - - -def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): - """Initialize and get a logger by name. - - If the logger has not been initialized, this method will initialize the - logger by adding one or two handlers, otherwise the initialized logger will - be directly returned. During initialization, a StreamHandler will always be - added. If `log_file` is specified and the process rank is 0, a FileHandler - will also be added. - - Args: - name (str): Logger name. - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the logger. - log_level (int): The logger level. Note that only the process of - rank 0 is affected, and other processes will set the level to - "Error" thus be silent most of the time. - file_mode (str): The file mode used in opening log file. - Defaults to 'w'. - - Returns: - logging.Logger: The expected logger. - """ - logger = logging.getLogger(name) - if name in logger_initialized: - return logger - # handle hierarchical names - # e.g., logger "a" is initialized, then logger "a.b" will skip the - # initialization since it is a child of "a". - for logger_name in logger_initialized: - if name.startswith(logger_name): - return logger - - # handle duplicate logs to the console - # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) - # to the root logger. As logger.propagate is True by default, this root - # level handler causes logging messages from rank>0 processes to - # unexpectedly show up on the console, creating much unwanted clutter. - # To fix this issue, we set the root logger's StreamHandler, if any, to log - # at the ERROR level. - for handler in logger.root.handlers: - if type(handler) is logging.StreamHandler: - handler.setLevel(logging.ERROR) - - stream_handler = logging.StreamHandler() - handlers = [stream_handler] - - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - else: - rank = 0 - - # only rank 0 will add a FileHandler - if rank == 0 and log_file is not None: - # Here, the default behaviour of the official logger is 'a'. Thus, we - # provide an interface to change the file mode to the default - # behaviour. - file_handler = logging.FileHandler(log_file, file_mode) - handlers.append(file_handler) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - for handler in handlers: - handler.setFormatter(formatter) - handler.setLevel(log_level) - logger.addHandler(handler) - - if rank == 0: - logger.setLevel(log_level) - else: - logger.setLevel(logging.ERROR) - - logger_initialized[name] = True - - return logger - - -def print_log(msg, logger=None, level=logging.INFO): - """Print a log message. - - Args: - msg (str): The message to be logged. - logger (logging.Logger | str | None): The logger to be used. - Some special loggers are: - - - "silent": no message will be printed. - - other str: the logger obtained with `get_root_logger(logger)`. - - None: The `print()` method will be used to print log messages. - level (int): Logging level. Only available when `logger` is a Logger - object or "root". - """ - if logger is None: - print(msg) - elif isinstance(logger, logging.Logger): - logger.log(level, msg) - elif logger == 'silent': - pass - elif isinstance(logger, str): - _logger = get_logger(logger) - _logger.log(level, msg) - else: - raise TypeError( - 'logger should be either a logging.Logger object, str, ' - f'"silent" or None, but got {type(logger)}') diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/misc.py b/cv/super_resolution/liif/pytorch/mmcv/utils/misc.py deleted file mode 100644 index 7957ea89b..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/misc.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections.abc -import functools -import itertools -import subprocess -import warnings -from collections import abc -from importlib import import_module -from inspect import getfullargspec -from itertools import repeat - - -# From PyTorch internals -def _ntuple(n): - - def parse(x): - if isinstance(x, collections.abc.Iterable): - return x - return tuple(repeat(x, n)) - - return parse - - -to_1tuple = _ntuple(1) -to_2tuple = _ntuple(2) -to_3tuple = _ntuple(3) -to_4tuple = _ntuple(4) -to_ntuple = _ntuple - - -def is_str(x): - """Whether the input is an string instance. - - Note: This method is deprecated since python 2 is no longer supported. - """ - return isinstance(x, str) - - -def import_modules_from_strings(imports, allow_failed_imports=False): - """Import modules from the given list of strings. - - Args: - imports (list | str | None): The given module names to be imported. - allow_failed_imports (bool): If True, the failed imports will return - None. Otherwise, an ImportError is raise. Default: False. - - Returns: - list[module] | module | None: The imported modules. - - Examples: - >>> osp, sys = import_modules_from_strings( - ... ['os.path', 'sys']) - >>> import os.path as osp_ - >>> import sys as sys_ - >>> assert osp == osp_ - >>> assert sys == sys_ - """ - if not imports: - return - single_import = False - if isinstance(imports, str): - single_import = True - imports = [imports] - if not isinstance(imports, list): - raise TypeError( - f'custom_imports must be a list but got type {type(imports)}') - imported = [] - for imp in imports: - if not isinstance(imp, str): - raise TypeError( - f'{imp} is of type {type(imp)} and cannot be imported.') - try: - imported_tmp = import_module(imp) - except ImportError: - if allow_failed_imports: - warnings.warn(f'{imp} failed to import and is ignored.', - UserWarning) - imported_tmp = None - else: - raise ImportError - imported.append(imported_tmp) - if single_import: - imported = imported[0] - return imported - - -def iter_cast(inputs, dst_type, return_type=None): - """Cast elements of an iterable object into some type. - - Args: - inputs (Iterable): The input object. - dst_type (type): Destination type. - return_type (type, optional): If specified, the output object will be - converted to this type, otherwise an iterator. - - Returns: - iterator or specified type: The converted object. - """ - if not isinstance(inputs, abc.Iterable): - raise TypeError('inputs must be an iterable object') - if not isinstance(dst_type, type): - raise TypeError('"dst_type" must be a valid type') - - out_iterable = map(dst_type, inputs) - - if return_type is None: - return out_iterable - else: - return return_type(out_iterable) - - -def list_cast(inputs, dst_type): - """Cast elements of an iterable object into a list of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=list) - - -def tuple_cast(inputs, dst_type): - """Cast elements of an iterable object into a tuple of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=tuple) - - -def is_seq_of(seq, expected_type, seq_type=None): - """Check whether it is a sequence of some type. - - Args: - seq (Sequence): The sequence to be checked. - expected_type (type): Expected type of sequence items. - seq_type (type, optional): Expected sequence type. - - Returns: - bool: Whether the sequence is valid. - """ - if seq_type is None: - exp_seq_type = abc.Sequence - else: - assert isinstance(seq_type, type) - exp_seq_type = seq_type - if not isinstance(seq, exp_seq_type): - return False - for item in seq: - if not isinstance(item, expected_type): - return False - return True - - -def is_list_of(seq, expected_type): - """Check whether it is a list of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=list) - - -def is_tuple_of(seq, expected_type): - """Check whether it is a tuple of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=tuple) - - -def slice_list(in_list, lens): - """Slice a list into several sub lists by a list of given length. - - Args: - in_list (list): The list to be sliced. - lens(int or list): The expected length of each out list. - - Returns: - list: A list of sliced list. - """ - if isinstance(lens, int): - assert len(in_list) % lens == 0 - lens = [lens] * int(len(in_list) / lens) - if not isinstance(lens, list): - raise TypeError('"indices" must be an integer or a list of integers') - elif sum(lens) != len(in_list): - raise ValueError('sum of lens and list length does not ' - f'match: {sum(lens)} != {len(in_list)}') - out_list = [] - idx = 0 - for i in range(len(lens)): - out_list.append(in_list[idx:idx + lens[i]]) - idx += lens[i] - return out_list - - -def concat_list(in_list): - """Concatenate a list of list into a single list. - - Args: - in_list (list): The list of list to be merged. - - Returns: - list: The concatenated flat list. - """ - return list(itertools.chain(*in_list)) - - -def check_prerequisites( - prerequisites, - checker, - msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' - 'found, please install them first.'): # yapf: disable - """A decorator factory to check if prerequisites are satisfied. - - Args: - prerequisites (str of list[str]): Prerequisites to be checked. - checker (callable): The checker method that returns True if a - prerequisite is meet, False otherwise. - msg_tmpl (str): The message template with two variables. - - Returns: - decorator: A specific decorator. - """ - - def wrap(func): - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - requirements = [prerequisites] if isinstance( - prerequisites, str) else prerequisites - missing = [] - for item in requirements: - if not checker(item): - missing.append(item) - if missing: - print(msg_tmpl.format(', '.join(missing), func.__name__)) - raise RuntimeError('Prerequisites not meet.') - else: - return func(*args, **kwargs) - - return wrapped_func - - return wrap - - -def _check_py_package(package): - try: - import_module(package) - except ImportError: - return False - else: - return True - - -def _check_executable(cmd): - if subprocess.call(f'which {cmd}', shell=True) != 0: - return False - else: - return True - - -def requires_package(prerequisites): - """A decorator to check if some python packages are installed. - - Example: - >>> @requires_package('numpy') - >>> func(arg1, args): - >>> return numpy.zeros(1) - array([0.]) - >>> @requires_package(['numpy', 'non_package']) - >>> func(arg1, args): - >>> return numpy.zeros(1) - ImportError - """ - return check_prerequisites(prerequisites, checker=_check_py_package) - - -def requires_executable(prerequisites): - """A decorator to check if some executable files are installed. - - Example: - >>> @requires_executable('ffmpeg') - >>> func(arg1, args): - >>> print(1) - 1 - """ - return check_prerequisites(prerequisites, checker=_check_executable) - - -def deprecated_api_warning(name_dict, cls_name=None): - """A decorator to check if some arguments are deprecate and try to replace - deprecate src_arg_name to dst_arg_name. - - Args: - name_dict(dict): - key (str): Deprecate argument names. - val (str): Expected argument names. - - Returns: - func: New function. - """ - - def api_warning_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get name of the function - func_name = old_func.__name__ - if cls_name is not None: - func_name = f'{cls_name}.{func_name}' - if args: - arg_names = args_info.args[:len(args)] - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in arg_names: - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - arg_names[arg_names.index(src_arg_name)] = dst_arg_name - if kwargs: - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in kwargs: - - assert dst_arg_name not in kwargs, ( - f'The expected behavior is to replace ' - f'the deprecated key `{src_arg_name}` to ' - f'new key `{dst_arg_name}`, but got them ' - f'in the arguments at the same time, which ' - f'is confusing. `{src_arg_name} will be ' - f'deprecated in the future, please ' - f'use `{dst_arg_name}` instead.') - - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead', DeprecationWarning) - kwargs[dst_arg_name] = kwargs.pop(src_arg_name) - - # apply converted arguments to the decorated method - output = old_func(*args, **kwargs) - return output - - return new_func - - return api_warning_wrapper - - -def is_method_overridden(method, base_class, derived_class): - """Check if a method of base class is overridden in derived class. - - Args: - method (str): the method name to check. - base_class (type): the class of the base class. - derived_class (type | Any): the class or instance of the derived class. - """ - assert isinstance(base_class, type), \ - "base_class doesn't accept instance, Please pass class instead." - - if not isinstance(derived_class, type): - derived_class = derived_class.__class__ - - base_method = getattr(base_class, method) - derived_method = getattr(derived_class, method) - return derived_method != base_method - - -def has_method(obj: object, method: str) -> bool: - """Check whether the object has a method. - - Args: - method (str): The method name to check. - obj (object): The object to check. - - Returns: - bool: True if the object has the method else False. - """ - return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/parrots_jit.py b/cv/super_resolution/liif/pytorch/mmcv/utils/parrots_jit.py deleted file mode 100644 index 61873f6db..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/parrots_jit.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -from .parrots_wrapper import TORCH_VERSION - -parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') - -if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': - from parrots.jit import pat as jit -else: - - def jit(func=None, - check_input=None, - full_shape=True, - derivate=False, - coderize=False, - optimize=False): - - def wrapper(func): - - def wrapper_inner(*args, **kargs): - return func(*args, **kargs) - - return wrapper_inner - - if func is None: - return wrapper - else: - return func - - -if TORCH_VERSION == 'parrots': - from parrots.utils.tester import skip_no_elena -else: - - def skip_no_elena(func): - - def wrapper(*args, **kargs): - return func(*args, **kargs) - - return wrapper diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/parrots_wrapper.py b/cv/super_resolution/liif/pytorch/mmcv/utils/parrots_wrapper.py deleted file mode 100644 index cf2c7e5ce..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/parrots_wrapper.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch - -TORCH_VERSION = torch.__version__ - - -def is_cuda_available() -> bool: - return torch.cuda.is_available() - - -IS_CUDA_AVAILABLE = is_cuda_available() - - -def is_rocm_pytorch() -> bool: - is_rocm = False - if TORCH_VERSION != 'parrots': - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - return is_rocm - - -def _get_cuda_home(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import CUDA_HOME - else: - if is_rocm_pytorch(): - from torch.utils.cpp_extension import ROCM_HOME - CUDA_HOME = ROCM_HOME - else: - from torch.utils.cpp_extension import CUDA_HOME - return CUDA_HOME - - -def get_build_config(): - if TORCH_VERSION == 'parrots': - from parrots.config import get_build_info - return get_build_info() - else: - return torch.__config__.show() - - -def _get_conv(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin - else: - from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin - return _ConvNd, _ConvTransposeMixin - - -def _get_dataloader(): - if TORCH_VERSION == 'parrots': - from torch.utils.data import DataLoader, PoolDataLoader - else: - from torch.utils.data import DataLoader - PoolDataLoader = DataLoader - return DataLoader, PoolDataLoader - - -def _get_extension(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import BuildExtension, Extension - CppExtension = partial(Extension, cuda=False) - CUDAExtension = partial(Extension, cuda=True) - else: - from torch.utils.cpp_extension import (BuildExtension, CppExtension, - CUDAExtension) - return BuildExtension, CppExtension, CUDAExtension - - -def _get_pool(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - else: - from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd - - -def _get_norm(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm2d - else: - from torch.nn.modules.batchnorm import _BatchNorm - from torch.nn.modules.instancenorm import _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm - return _BatchNorm, _InstanceNorm, SyncBatchNorm_ - - -_ConvNd, _ConvTransposeMixin = _get_conv() -DataLoader, PoolDataLoader = _get_dataloader() -BuildExtension, CppExtension, CUDAExtension = _get_extension() -_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() -_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() - - -class SyncBatchNorm(SyncBatchNorm_): # type: ignore - - def _check_input_dim(self, input): - if TORCH_VERSION == 'parrots': - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input (got {input.dim()}D input)') - else: - super()._check_input_dim(input) diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/path.py b/cv/super_resolution/liif/pytorch/mmcv/utils/path.py deleted file mode 100644 index 568081837..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/path.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -from pathlib import Path - -from .misc import is_str - - -def is_filepath(x): - return is_str(x) or isinstance(x, Path) - - -def fopen(filepath, *args, **kwargs): - if is_str(filepath): - return open(filepath, *args, **kwargs) - elif isinstance(filepath, Path): - return filepath.open(*args, **kwargs) - raise ValueError('`filepath` should be a string or a Path') - - -def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): - if not osp.isfile(filename): - raise FileNotFoundError(msg_tmpl.format(filename)) - - -def mkdir_or_exist(dir_name, mode=0o777): - if dir_name == '': - return - dir_name = osp.expanduser(dir_name) - os.makedirs(dir_name, mode=mode, exist_ok=True) - - -def symlink(src, dst, overwrite=True, **kwargs): - if os.path.lexists(dst) and overwrite: - os.remove(dst) - os.symlink(src, dst, **kwargs) - - -def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): - """Scan a directory to find the interested files. - - Args: - dir_path (str | :obj:`Path`): Path of the directory. - suffix (str | tuple(str), optional): File suffix that we are - interested in. Default: None. - recursive (bool, optional): If set to True, recursively scan the - directory. Default: False. - case_sensitive (bool, optional) : If set to False, ignore the case of - suffix. Default: True. - - Returns: - A generator for all the interested files with relative paths. - """ - if isinstance(dir_path, (str, Path)): - dir_path = str(dir_path) - else: - raise TypeError('"dir_path" must be a string or Path object') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('"suffix" must be a string or tuple of strings') - - if suffix is not None and not case_sensitive: - suffix = suffix.lower() if isinstance(suffix, str) else tuple( - item.lower() for item in suffix) - - root = dir_path - - def _scandir(dir_path, suffix, recursive, case_sensitive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - _rel_path = rel_path if case_sensitive else rel_path.lower() - if suffix is None or _rel_path.endswith(suffix): - yield rel_path - elif recursive and os.path.isdir(entry.path): - # scan recursively if entry.path is a directory - yield from _scandir(entry.path, suffix, recursive, - case_sensitive) - - return _scandir(dir_path, suffix, recursive, case_sensitive) - - -def find_vcs_root(path, markers=('.git', )): - """Finds the root directory (including itself) of specified markers. - - Args: - path (str): Path of directory or file. - markers (list[str], optional): List of file or directory names. - - Returns: - The directory contained one of the markers or None if not found. - """ - if osp.isfile(path): - path = osp.dirname(path) - - prev, cur = None, osp.abspath(osp.expanduser(path)) - while cur != prev: - if any(osp.exists(osp.join(cur, marker)) for marker in markers): - return cur - prev, cur = cur, osp.split(cur)[0] - return None diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/progressbar.py b/cv/super_resolution/liif/pytorch/mmcv/utils/progressbar.py deleted file mode 100644 index 0062f670d..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/progressbar.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from collections.abc import Iterable -from multiprocessing import Pool -from shutil import get_terminal_size - -from .timer import Timer - - -class ProgressBar: - """A progress bar which can print the progress.""" - - def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): - self.task_num = task_num - self.bar_width = bar_width - self.completed = 0 - self.file = file - if start: - self.start() - - @property - def terminal_width(self): - width, _ = get_terminal_size() - return width - - def start(self): - if self.task_num > 0: - self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' - 'elapsed: 0s, ETA:') - else: - self.file.write('completed: 0, elapsed: 0s') - self.file.flush() - self.timer = Timer() - - def update(self, num_tasks=1): - assert num_tasks > 0 - self.completed += num_tasks - elapsed = self.timer.since_start() - if elapsed > 0: - fps = self.completed / elapsed - else: - fps = float('inf') - if self.task_num > 0: - percentage = self.completed / float(self.task_num) - eta = int(elapsed * (1 - percentage) / percentage + 0.5) - msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ - f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ - f'ETA: {eta:5}s' - - bar_width = min(self.bar_width, - int(self.terminal_width - len(msg)) + 2, - int(self.terminal_width * 0.6)) - bar_width = max(2, bar_width) - mark_width = int(bar_width * percentage) - bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) - self.file.write(msg.format(bar_chars)) - else: - self.file.write( - f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' - f' {fps:.1f} tasks/s') - self.file.flush() - - -def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): - """Track the progress of tasks execution with a progress bar. - - Tasks are done with a simple for-loop. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - results = [] - for task in tasks: - results.append(func(task, **kwargs)) - prog_bar.update() - prog_bar.file.write('\n') - return results - - -def init_pool(process_num, initializer=None, initargs=None): - if initializer is None: - return Pool(process_num) - elif initargs is None: - return Pool(process_num, initializer) - else: - if not isinstance(initargs, tuple): - raise TypeError('"initargs" must be a tuple') - return Pool(process_num, initializer, initargs) - - -def track_parallel_progress(func, - tasks, - nproc, - initializer=None, - initargs=None, - bar_width=50, - chunksize=1, - skip_first=False, - keep_order=True, - file=sys.stdout): - """Track the progress of parallel task execution with a progress bar. - - The built-in :mod:`multiprocessing` module is used for process pools and - tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - nproc (int): Process (worker) number. - initializer (None or callable): Refer to :class:`multiprocessing.Pool` - for details. - initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for - details. - chunksize (int): Refer to :class:`multiprocessing.Pool` for details. - bar_width (int): Width of progress bar. - skip_first (bool): Whether to skip the first sample for each worker - when estimating fps, since the initialization step may takes - longer. - keep_order (bool): If True, :func:`Pool.imap` is used, otherwise - :func:`Pool.imap_unordered` is used. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - pool = init_pool(nproc, initializer, initargs) - start = not skip_first - task_num -= nproc * chunksize * int(skip_first) - prog_bar = ProgressBar(task_num, bar_width, start, file=file) - results = [] - if keep_order: - gen = pool.imap(func, tasks, chunksize) - else: - gen = pool.imap_unordered(func, tasks, chunksize) - for result in gen: - results.append(result) - if skip_first: - if len(results) < nproc * chunksize: - continue - elif len(results) == nproc * chunksize: - prog_bar.start() - continue - prog_bar.update() - prog_bar.file.write('\n') - pool.close() - pool.join() - return results - - -def track_iter_progress(tasks, bar_width=50, file=sys.stdout): - """Track the progress of tasks iteration or enumeration with a progress - bar. - - Tasks are yielded with a simple for-loop. - - Args: - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Yields: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - for task in tasks: - yield task - prog_bar.update() - prog_bar.file.write('\n') diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/registry.py b/cv/super_resolution/liif/pytorch/mmcv/utils/registry.py deleted file mode 100644 index a7db6bd44..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/registry.py +++ /dev/null @@ -1,340 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import warnings -from functools import partial -from typing import Any, Dict, Optional - -from .misc import deprecated_api_warning, is_seq_of - - -def build_from_cfg(cfg: Dict, - registry: 'Registry', - default_args: Optional[Dict] = None) -> Any: - """Build a module from config dict when it is a class configuration, or - call a function from config dict when it is a function configuration. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = build_from_cfg(dict(type='Resnet'), MODELS) - >>> # Returns an instantiated object - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = build_from_cfg(dict(type='resnet50'), MODELS) - >>> # Return a result of the calling function - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type) or inspect.isfunction(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - try: - return obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{obj_cls.__name__}: {e}') - - -class Registry: - """A registry to map strings to classes or functions. - - Registered object could be built from registry. Meanwhile, registered - functions could be called from registry. - - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = MODELS.build(dict(type='ResNet')) - >>> @MODELS.register_module() - >>> def resnet50(): - >>> pass - >>> resnet = MODELS.build(dict(type='resnet50')) - - Please refer to - https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for - advanced usage. - - Args: - name (str): Registry name. - build_func(func, optional): Build function to construct instance from - Registry, func:`build_from_cfg` is used if neither ``parent`` or - ``build_func`` is specified. If ``parent`` is specified and - ``build_func`` is not given, ``build_func`` will be inherited - from ``parent``. Default: None. - parent (Registry, optional): Parent registry. The class registered in - children registry could be built from parent. Default: None. - scope (str, optional): The scope of registry. It is the key to search - for children registry. If not specified, scope will be the name of - the package where class is defined, e.g. mmdet, mmcls, mmseg. - Default: None. - """ - - def __init__(self, name, build_func=None, parent=None, scope=None): - self._name = name - self._module_dict = dict() - self._children = dict() - self._scope = self.infer_scope() if scope is None else scope - - # self.build_func will be set with the following priority: - # 1. build_func - # 2. parent.build_func - # 3. build_from_cfg - if build_func is None: - if parent is not None: - self.build_func = parent.build_func - else: - self.build_func = build_from_cfg - else: - self.build_func = build_func - if parent is not None: - assert isinstance(parent, Registry) - parent._add_children(self) - self.parent = parent - else: - self.parent = None - - def __len__(self): - return len(self._module_dict) - - def __contains__(self, key): - return self.get(key) is not None - - def __repr__(self): - format_str = self.__class__.__name__ + \ - f'(name={self._name}, ' \ - f'items={self._module_dict})' - return format_str - - @staticmethod - def infer_scope(): - """Infer the scope of registry. - - The name of the package where registry is defined will be returned. - - Example: - >>> # in mmdet/models/backbone/resnet.py - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - The scope of ``ResNet`` will be ``mmdet``. - - Returns: - str: The inferred scope name. - """ - # We access the caller using inspect.currentframe() instead of - # inspect.stack() for performance reasons. See details in PR #1844 - frame = inspect.currentframe() - # get the frame where `infer_scope()` is called - infer_scope_caller = frame.f_back.f_back - filename = inspect.getmodule(infer_scope_caller).__name__ - split_filename = filename.split('.') - return split_filename[0] - - @staticmethod - def split_scope_key(key): - """Split scope and key. - - The first scope will be split from key. - - Examples: - >>> Registry.split_scope_key('mmdet.ResNet') - 'mmdet', 'ResNet' - >>> Registry.split_scope_key('ResNet') - None, 'ResNet' - - Return: - tuple[str | None, str]: The former element is the first scope of - the key, which can be ``None``. The latter is the remaining key. - """ - split_index = key.find('.') - if split_index != -1: - return key[:split_index], key[split_index + 1:] - else: - return None, key - - @property - def name(self): - return self._name - - @property - def scope(self): - return self._scope - - @property - def module_dict(self): - return self._module_dict - - @property - def children(self): - return self._children - - def get(self, key): - """Get the registry record. - - Args: - key (str): The class name in string format. - - Returns: - class: The corresponding class. - """ - scope, real_key = self.split_scope_key(key) - if scope is None or scope == self._scope: - # get from self - if real_key in self._module_dict: - return self._module_dict[real_key] - else: - # get from self._children - if scope in self._children: - return self._children[scope].get(real_key) - else: - # goto root - parent = self.parent - while parent.parent is not None: - parent = parent.parent - return parent.get(key) - - def build(self, *args, **kwargs): - return self.build_func(*args, **kwargs, registry=self) - - def _add_children(self, registry): - """Add children for a registry. - - The ``registry`` will be added as children based on its scope. - The parent registry could build objects from children registry. - - Example: - >>> models = Registry('models') - >>> mmdet_models = Registry('models', parent=models) - >>> @mmdet_models.register_module() - >>> class ResNet: - >>> pass - >>> resnet = models.build(dict(type='mmdet.ResNet')) - """ - - assert isinstance(registry, Registry) - assert registry.scope is not None - assert registry.scope not in self.children, \ - f'scope {registry.scope} exists in {self.name} registry' - self.children[registry.scope] = registry - - @deprecated_api_warning(name_dict=dict(module_class='module')) - def _register_module(self, module, module_name=None, force=False): - if not inspect.isclass(module) and not inspect.isfunction(module): - raise TypeError('module must be a class or a function, ' - f'but got {type(module)}') - - if module_name is None: - module_name = module.__name__ - if isinstance(module_name, str): - module_name = [module_name] - for name in module_name: - if not force and name in self._module_dict: - raise KeyError(f'{name} is already registered ' - f'in {self.name}') - self._module_dict[name] = module - - def deprecated_register_module(self, cls=None, force=False): - warnings.warn( - 'The old API of register_module(module, force=False) ' - 'is deprecated and will be removed, please use the new API ' - 'register_module(name=None, force=False, module=None) instead.', - DeprecationWarning) - if cls is None: - return partial(self.deprecated_register_module, force=force) - self._register_module(cls, force=force) - return cls - - def register_module(self, name=None, force=False, module=None): - """Register a module. - - A record will be added to `self._module_dict`, whose key is the class - name or the specified name, and value is the class itself. - It can be used as a decorator or a normal function. - - Example: - >>> backbones = Registry('backbone') - >>> @backbones.register_module() - >>> class ResNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> @backbones.register_module(name='mnet') - >>> class MobileNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> class ResNet: - >>> pass - >>> backbones.register_module(ResNet) - - Args: - name (str | None): The module name to be registered. If not - specified, the class name will be used. - force (bool, optional): Whether to override an existing class with - the same name. Default: False. - module (type): Module class or function to be registered. - """ - if not isinstance(force, bool): - raise TypeError(f'force must be a boolean, but got {type(force)}') - # NOTE: This is a walkaround to be compatible with the old api, - # while it may introduce unexpected bugs. - if isinstance(name, type): - return self.deprecated_register_module(name, force=force) - - # raise the error ahead of time - if not (name is None or isinstance(name, str) or is_seq_of(name, str)): - raise TypeError( - 'name must be either of None, an instance of str or a sequence' - f' of str, but got {type(name)}') - - # use it as a normal method: x.register_module(module=SomeClass) - if module is not None: - self._register_module(module=module, module_name=name, force=force) - return module - - # use it as a decorator: @x.register_module() - def _register(module): - self._register_module(module=module, module_name=name, force=force) - return module - - return _register diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/seed.py b/cv/super_resolution/liif/pytorch/mmcv/utils/seed.py deleted file mode 100644 index 003f92367..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/seed.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import random - -import numpy as np -import torch - - -def worker_init_fn(worker_id: int, num_workers: int, rank: int, seed: int): - """Function to initialize each worker. - - The seed of each worker equals to - ``num_worker * rank + worker_id + user_seed``. - - Args: - worker_id (int): Id for each worker. - num_workers (int): Number of workers. - rank (int): Rank in distributed training. - seed (int): Random seed. - """ - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/testing.py b/cv/super_resolution/liif/pytorch/mmcv/utils/testing.py deleted file mode 100644 index 7b64e8fae..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/testing.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (c) Open-MMLab. -import sys -from collections.abc import Iterable -from runpy import run_path -from shlex import split -from typing import Any, Dict, List -from unittest.mock import patch - - -def check_python_script(cmd): - """Run the python cmd script with `__main__`. The difference between - `os.system` is that, this function exectues code in the current process, so - that it can be tracked by coverage tools. Currently it supports two forms: - - - ./tests/data/scripts/hello.py zz - - python tests/data/scripts/hello.py zz - """ - args = split(cmd) - if args[0] == 'python': - args = args[1:] - with patch.object(sys, 'argv', args): - run_path(args[0], run_name='__main__') - - -def _any(judge_result): - """Since built-in ``any`` works only when the element of iterable is not - iterable, implement the function.""" - if not isinstance(judge_result, Iterable): - return judge_result - - try: - for element in judge_result: - if _any(element): - return True - except TypeError: - # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) - if judge_result: - return True - return False - - -def assert_dict_contains_subset(dict_obj: Dict[Any, Any], - expected_subset: Dict[Any, Any]) -> bool: - """Check if the dict_obj contains the expected_subset. - - Args: - dict_obj (Dict[Any, Any]): Dict object to be checked. - expected_subset (Dict[Any, Any]): Subset expected to be contained in - dict_obj. - - Returns: - bool: Whether the dict_obj contains the expected_subset. - """ - - for key, value in expected_subset.items(): - if key not in dict_obj.keys() or _any(dict_obj[key] != value): - return False - return True - - -def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: - """Check if attribute of class object is correct. - - Args: - obj (object): Class object to be checked. - expected_attrs (Dict[str, Any]): Dict of the expected attrs. - - Returns: - bool: Whether the attribute of class object is correct. - """ - for attr, value in expected_attrs.items(): - if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): - return False - return True - - -def assert_dict_has_keys(obj: Dict[str, Any], - expected_keys: List[str]) -> bool: - """Check if the obj has all the expected_keys. - - Args: - obj (Dict[str, Any]): Object to be checked. - expected_keys (List[str]): Keys expected to contained in the keys of - the obj. - - Returns: - bool: Whether the obj has the expected keys. - """ - return set(expected_keys).issubset(set(obj.keys())) - - -def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: - """Check if target_keys is equal to result_keys. - - Args: - result_keys (List[str]): Result keys to be checked. - target_keys (List[str]): Target keys to be checked. - - Returns: - bool: Whether target_keys is equal to result_keys. - """ - return set(result_keys) == set(target_keys) - - -def assert_is_norm_layer(module) -> bool: - """Check if the module is a norm layer. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the module is a norm layer. - """ - from torch.nn import GroupNorm, LayerNorm - - from .parrots_wrapper import _BatchNorm, _InstanceNorm - norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) - return isinstance(module, norm_layer_candidates) - - -def assert_params_all_zeros(module) -> bool: - """Check if the parameters of the module is all zeros. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the parameters of the module is all zeros. - """ - weight_data = module.weight.data - is_weight_zero = weight_data.allclose( - weight_data.new_zeros(weight_data.size())) - - if hasattr(module, 'bias') and module.bias is not None: - bias_data = module.bias.data - is_bias_zero = bias_data.allclose( - bias_data.new_zeros(bias_data.size())) - else: - is_bias_zero = True - - return is_weight_zero and is_bias_zero diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/timer.py b/cv/super_resolution/liif/pytorch/mmcv/utils/timer.py deleted file mode 100644 index 087a969cf..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/timer.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from time import time - - -class TimerError(Exception): - - def __init__(self, message): - self.message = message - super().__init__(message) - - -class Timer: - """A flexible Timer class. - - Examples: - >>> import time - >>> import mmcv - >>> with mmcv.Timer(): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - 1.000 - >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - it takes 1.0 seconds - >>> timer = mmcv.Timer() - >>> time.sleep(0.5) - >>> print(timer.since_start()) - 0.500 - >>> time.sleep(0.5) - >>> print(timer.since_last_check()) - 0.500 - >>> print(timer.since_start()) - 1.000 - """ - - def __init__(self, start=True, print_tmpl=None): - self._is_running = False - self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' - if start: - self.start() - - @property - def is_running(self): - """bool: indicate whether the timer is running""" - return self._is_running - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - print(self.print_tmpl.format(self.since_last_check())) - self._is_running = False - - def start(self): - """Start the timer.""" - if not self._is_running: - self._t_start = time() - self._is_running = True - self._t_last = time() - - def since_start(self): - """Total time since the timer is started. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - self._t_last = time() - return self._t_last - self._t_start - - def since_last_check(self): - """Time since the last checking. - - Either :func:`since_start` or :func:`since_last_check` is a checking - operation. - - Returns: - float: Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - dur = time() - self._t_last - self._t_last = time() - return dur - - -_g_timers = {} # global timers - - -def check_time(timer_id): - """Add check points in a single line. - - This method is suitable for running a task on a list of items. A timer will - be registered when the method is called for the first time. - - Examples: - >>> import time - >>> import mmcv - >>> for i in range(1, 6): - >>> # simulate a code block - >>> time.sleep(i) - >>> mmcv.check_time('task1') - 2.000 - 3.000 - 4.000 - 5.000 - - Args: - str: Timer identifier. - """ - if timer_id not in _g_timers: - _g_timers[timer_id] = Timer() - return 0 - else: - return _g_timers[timer_id].since_last_check() diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/trace.py b/cv/super_resolution/liif/pytorch/mmcv/utils/trace.py deleted file mode 100644 index 45423bd05..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/trace.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch - -from mmcv.utils import digit_version - - -def is_jit_tracing() -> bool: - if (torch.__version__ != 'parrots' - and digit_version(torch.__version__) >= digit_version('1.6.0')): - on_trace = torch.jit.is_tracing() - # In PyTorch 1.6, torch.jit.is_tracing has a bug. - # Refers to https://github.com/pytorch/pytorch/issues/42448 - if isinstance(on_trace, bool): - return on_trace - else: - return torch._C._is_tracing() - else: - warnings.warn( - 'torch.jit.is_tracing is only supported after v1.6.0. ' - 'Therefore is_tracing returns False automatically. Please ' - 'set on_trace manually if you are using trace.', UserWarning) - return False diff --git a/cv/super_resolution/liif/pytorch/mmcv/utils/version_utils.py b/cv/super_resolution/liif/pytorch/mmcv/utils/version_utils.py deleted file mode 100644 index 77c41f608..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/utils/version_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import subprocess -import warnings - -from packaging.version import parse - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - assert 'parrots' not in version_str - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) # type: ignore - else: - release.extend([0, 0]) - return tuple(release) - - -def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - -def get_git_hash(fallback='unknown', digits=None): - """Get the git hash of the current repo. - - Args: - fallback (str, optional): The fallback string when git hash is - unavailable. Defaults to 'unknown'. - digits (int, optional): kept digits of the hash. Defaults to None, - meaning all digits are kept. - - Returns: - str: Git commit hash. - """ - - if digits is not None and not isinstance(digits, int): - raise TypeError('digits must be None or an integer') - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - sha = out.strip().decode('ascii') - if digits is not None: - sha = sha[:digits] - except OSError: - sha = fallback - - return sha diff --git a/cv/super_resolution/liif/pytorch/mmcv/version.py b/cv/super_resolution/liif/pytorch/mmcv/version.py deleted file mode 100644 index a65e32789..000000000 --- a/cv/super_resolution/liif/pytorch/mmcv/version.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -__version__ = '1.5.3' - - -def parse_version_info(version_str: str, length: int = 4) -> tuple: - """Parse a version string into a tuple. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int | str]: The version info, e.g., "1.3.0" is parsed into - (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into - (2, 0, 0, 0, 'rc', 1) (when length is set to 4). - """ - from packaging.version import parse - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - release.extend(list(version.pre)) # type: ignore - elif version.is_postrelease: - release.extend(list(version.post)) # type: ignore - else: - release.extend([0, 0]) - return tuple(release) - - -version_info = tuple(int(x) for x in __version__.split('.')[:3]) - -__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/cv/super_resolution/liif/pytorch/mmedit/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/__init__.py deleted file mode 100755 index b26e6a312..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import mmcv - -from .version import __version__, version_info - -try: - from mmcv.utils import digit_version -except ImportError: - - def digit_version(version_str): - digit_ver = [] - for x in version_str.split('.'): - if x.isdigit(): - digit_ver.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - digit_ver.append(int(patch_version[0]) - 1) - digit_ver.append(int(patch_version[1])) - return digit_ver - - -MMCV_MIN = '1.3.13' -MMCV_MAX = '1.6' - -mmcv_min_version = digit_version(MMCV_MIN) -mmcv_max_version = digit_version(MMCV_MAX) -mmcv_version = digit_version(mmcv.__version__) - - -assert (mmcv_min_version <= mmcv_version <= mmcv_max_version), \ - f'mmcv=={mmcv.__version__} is used but incompatible. ' \ - f'Please install mmcv-full>={mmcv_min_version}, <={mmcv_max_version}.' - -__all__ = ['__version__', 'version_info'] diff --git a/cv/super_resolution/liif/pytorch/mmedit/apis/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/apis/__init__.py deleted file mode 100755 index 164bab286..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/apis/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .restoration_inference import restoration_inference -from .test import multi_gpu_test, single_gpu_test -from .train import init_random_seed, set_random_seed, train_model - -__all__ = [ - 'train_model', 'set_random_seed', 'init_model', - 'restoration_inference', - 'multi_gpu_test', 'single_gpu_test', - 'init_random_seed' -] diff --git a/cv/super_resolution/liif/pytorch/mmedit/apis/generation_inference.py b/cv/super_resolution/liif/pytorch/mmedit/apis/generation_inference.py deleted file mode 100755 index c8e829bb1..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/apis/generation_inference.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -from mmcv.parallel import collate, scatter - -from mmedit.core import tensor2img -from mmedit.datasets.pipelines import Compose - - -def generation_inference(model, img, img_unpaired=None): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img (str): File path of input image. - img_unpaired (str, optional): File path of the unpaired image. - If not None, perform unpaired image generation. Default: None. - - Returns: - np.ndarray: The predicted generation result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # build the data pipeline - test_pipeline = Compose(cfg.test_pipeline) - # prepare data - if img_unpaired is None: - data = dict(pair_path=img) - else: - data = dict(img_a_path=img, img_b_path=img_unpaired) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - # forward the model - with torch.no_grad(): - results = model(test_mode=True, **data) - # process generation shown mode - if img_unpaired is None: - if model.show_input: - output = np.concatenate([ - tensor2img(results['real_a'], min_max=(-1, 1)), - tensor2img(results['fake_b'], min_max=(-1, 1)), - tensor2img(results['real_b'], min_max=(-1, 1)) - ], - axis=1) - else: - output = tensor2img(results['fake_b'], min_max=(-1, 1)) - else: - if model.show_input: - output = np.concatenate([ - tensor2img(results['real_a'], min_max=(-1, 1)), - tensor2img(results['fake_b'], min_max=(-1, 1)), - tensor2img(results['real_b'], min_max=(-1, 1)), - tensor2img(results['fake_a'], min_max=(-1, 1)) - ], - axis=1) - else: - if model.test_direction == 'a2b': - output = tensor2img(results['fake_b'], min_max=(-1, 1)) - else: - output = tensor2img(results['fake_a'], min_max=(-1, 1)) - return output diff --git a/cv/super_resolution/liif/pytorch/mmedit/apis/restoration_inference.py b/cv/super_resolution/liif/pytorch/mmedit/apis/restoration_inference.py deleted file mode 100755 index 98c08abc8..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/apis/restoration_inference.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.parallel import collate, scatter - -from mmedit.datasets.pipelines import Compose - - -def restoration_inference(model, img, ref=None): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img (str): File path of input image. - ref (str | None): File path of reference image. Default: None. - - Returns: - Tensor: The predicted restoration result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # remove gt from test_pipeline - keys_to_remove = ['gt', 'gt_path'] - for key in keys_to_remove: - for pipeline in list(cfg.test_pipeline): - if 'key' in pipeline and key == pipeline['key']: - cfg.test_pipeline.remove(pipeline) - if 'keys' in pipeline and key in pipeline['keys']: - pipeline['keys'].remove(key) - if len(pipeline['keys']) == 0: - cfg.test_pipeline.remove(pipeline) - if 'meta_keys' in pipeline and key in pipeline['meta_keys']: - pipeline['meta_keys'].remove(key) - # build the data pipeline - test_pipeline = Compose(cfg.test_pipeline) - # prepare data - if ref: # Ref-SR - data = dict(lq_path=img, ref_path=ref) - else: # SISR - data = dict(lq_path=img) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - # forward the model - with torch.no_grad(): - result = model(test_mode=True, **data) - - return result['output'] diff --git a/cv/super_resolution/liif/pytorch/mmedit/apis/test.py b/cv/super_resolution/liif/pytorch/mmedit/apis/test.py deleted file mode 100755 index 535511eee..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/apis/test.py +++ /dev/null @@ -1,234 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile - -import mmcv -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, - data_loader, - save_image=False, - save_path=None, - iteration=None): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - save_image (bool): Whether save image. Default: False. - save_path (str): The path to save image. Default: None. - iteration (int): Iteration number. It is used for the save image name. - Default: None. - - Returns: - list: The prediction results. - """ - if save_image and save_path is None: - raise ValueError( - "When 'save_image' is True, you should also set 'save_path'.") - - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model( - test_mode=True, - save_image=save_image, - save_path=save_path, - iteration=iteration, - **data) - results.append(result) - - # get batch size - for _, v in data.items(): - if isinstance(v, torch.Tensor): - batch_size = v.size(0) - break - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, - data_loader, - tmpdir=None, - gpu_collect=False, - save_image=False, - save_path=None, - iteration=None, - empty_cache=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' - it encodes results to gpu tensors and use gpu communication for results - collection. On cpu mode it saves the results on different gpus to 'tmpdir' - and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - save_image (bool): Whether save image. Default: False. - save_path (str): The path to save image. Default: None. - iteration (int): Iteration number. It is used for the save image name. - Default: None. - empty_cache (bool): empty cache in every iteration. Default: False. - - Returns: - list: The prediction results. - """ - - if save_image and save_path is None: - raise ValueError( - "When 'save_image' is True, you should also set 'save_path'.") - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model( - test_mode=True, - save_image=save_image, - save_path=save_path, - iteration=iteration, - **data) - results.append(result) - if empty_cache: - torch.cuda.empty_cache() - if rank == 0: - # get batch size - for _, v in data.items(): - if isinstance(v, torch.Tensor): - batch_size = v.size(0) - break - for _ in range(batch_size * world_size): - prog_bar.update() - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results in cpu mode. - - It saves the results on different gpus to 'tmpdir' and collects - them by the rank 0 worker. - - Args: - result_part (list): Results to be collected - size (int): Result size. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. Default: None - - Returns: - list: Ordered results. - """ - - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # synchronizes all processes to make sure tmpdir exist - dist.barrier() - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, 'part_{}.pkl'.format(rank))) - # synchronizes all processes for loading pickle file - dist.barrier() - # collect all parts - if rank != 0: - return None - - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, 'part_{}.pkl'.format(i)) - part_list.append(mmcv.load(part_file)) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results in gpu mode. - - It encodes results to gpu tensors and use gpu communication for results - collection. - - Args: - result_part (list): Results to be collected - size (int): Result size. - - Returns: - list: Ordered results. - """ - - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank != 0: - return None - - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_list.append(pickle.loads(recv[:shape[0]].cpu().numpy().tobytes())) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/super_resolution/liif/pytorch/mmedit/apis/train.py b/cv/super_resolution/liif/pytorch/mmedit/apis/train.py deleted file mode 100755 index 359943ca8..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/apis/train.py +++ /dev/null @@ -1,361 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -import random -import warnings - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.parallel import MMDataParallel -from mmcv.runner import HOOKS, IterBasedRunner, get_dist_info -from mmcv.utils import build_from_cfg - -from mmedit.core import DistEvalIterHook, EvalIterHook, build_optimizers -from mmedit.core.distributed_wrapper import DistributedDataParallelWrapper -from mmedit.datasets.builder import build_dataloader, build_dataset -from mmedit.utils import get_root_logger - - -def init_random_seed(seed=None, device='cuda'): - """Initialize random seed. - If the seed is not set, the seed will be automatically randomized, - and then broadcast to all processes to prevent some potential bugs. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is not None: - return seed - - # Make sure all ranks share the same random seed to prevent - # some potential bugs. Please refer to - # https://github.com/open-mmlab/mmdetection/issues/6339 - rank, world_size = get_dist_info() - seed = np.random.randint(2**31) - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() - - -def set_random_seed(seed, deterministic=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - """ - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - - -def train_model(model, - dataset, - cfg, - distributed=False, - validate=False, - timestamp=None, - meta=None): - """Train model entry function. - - Args: - model (nn.Module): The model to be trained. - dataset (:obj:`Dataset`): Train dataset. - cfg (dict): The config dict for training. - distributed (bool): Whether to use distributed training. - Default: False. - validate (bool): Whether to do evaluation. Default: False. - timestamp (str | None): Local time for runner. Default: None. - meta (dict | None): Meta dict to record some important information. - Default: None - """ - logger = get_root_logger(log_level=cfg.log_level) - - # start training - if distributed: - _dist_train( - model, - dataset, - cfg, - validate=validate, - logger=logger, - timestamp=timestamp, - meta=meta) - else: - _non_dist_train( - model, - dataset, - cfg, - validate=validate, - logger=logger, - timestamp=timestamp, - meta=meta) - - -def _dist_train(model, - dataset, - cfg, - validate=False, - logger=None, - timestamp=None, - meta=None): - """Distributed training function. - - Args: - model (nn.Module): The model to be trained. - dataset (:obj:`Dataset`): Train dataset. - cfg (dict): The config dict for training. - validate (bool): Whether to do evaluation. Default: False. - logger (logging.Logger | None): Logger for training. Default: None. - timestamp (str | None): Local time for runner. Default: None. - meta (dict | None): Meta dict to record some important information. - Default: None. - """ - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - - # step 1: give default values and override (if exist) from cfg.data - loader_cfg = { - **dict(seed=cfg.get('seed'), drop_last=False, dist=True), - **({} if torch.__version__ != 'parrots' else dict( - prefetch_num=2, - pin_memory=False, - )), - **dict((k, cfg.data[k]) for k in [ - 'samples_per_gpu', - 'workers_per_gpu', - 'shuffle', - 'seed', - 'drop_last', - 'prefetch_num', - 'pin_memory', - ] if k in cfg.data) - } - - # step 2: cfg.data.train_dataloader has highest priority - train_loader_cfg = dict(loader_cfg, **cfg.data.get('train_dataloader', {})) - - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus - find_unused_parameters = cfg.get('find_unused_parameters', False) - model = DistributedDataParallelWrapper( - model, - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - - # build runner - optimizer = build_optimizers(model, cfg.optimizers) - runner = IterBasedRunner( - model, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta) - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register hooks - runner.register_training_hooks( - cfg.lr_config, - checkpoint_config=cfg.checkpoint_config, - log_config=cfg.log_config) - - # visual hook - if cfg.get('visual_config', None) is not None: - cfg.visual_config['output_dir'] = os.path.join( - cfg.work_dir, cfg.visual_config['output_dir']) - runner.register_hook(mmcv.build_from_cfg(cfg.visual_config, HOOKS)) - - # evaluation hook - if validate and cfg.get('evaluation', None) is not None: - dataset = build_dataset(cfg.data.val) - - if ('val_samples_per_gpu' in cfg.data - or 'val_workers_per_gpu' in cfg.data): - warnings.warn('"val_samples_per_gpu/val_workers_per_gpu" have ' - 'been deprecated. Please use ' - '"val_dataloader=dict(samples_per_gpu=1)" instead. ' - 'Details see ' - 'https://github.com/open-mmlab/mmediting/pull/201') - - val_loader_cfg = { - **loader_cfg, - **dict(shuffle=False, drop_last=False), - **dict((newk, cfg.data[oldk]) for oldk, newk in [ - ('val_samples_per_gpu', 'samples_per_gpu'), - ('val_workers_per_gpu', 'workers_per_gpu'), - ] if oldk in cfg.data), - **cfg.data.get('val_dataloader', {}) - } - - data_loader = build_dataloader(dataset, **val_loader_cfg) - save_path = osp.join(cfg.work_dir, 'val_visuals') - runner.register_hook( - DistEvalIterHook( - data_loader, save_path=save_path, **cfg.evaluation), - priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow, cfg.total_iters) - - -def _non_dist_train(model, - dataset, - cfg, - validate=False, - logger=None, - timestamp=None, - meta=None): - """Non-Distributed training function. - - Args: - model (nn.Module): The model to be trained. - dataset (:obj:`Dataset`): Train dataset. - cfg (dict): The config dict for training. - validate (bool): Whether to do evaluation. Default: False. - logger (logging.Logger | None): Logger for training. Default: None. - timestamp (str | None): Local time for runner. Default: None. - meta (dict | None): Meta dict to record some important information. - Default: None. - """ - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - - # step 1: give default values and override (if exist) from cfg.data - loader_cfg = { - **dict( - seed=cfg.get('seed'), - drop_last=False, - dist=False, - num_gpus=cfg.gpus), - **({} if torch.__version__ != 'parrots' else dict( - prefetch_num=2, - pin_memory=False, - )), - **dict((k, cfg.data[k]) for k in [ - 'samples_per_gpu', - 'workers_per_gpu', - 'shuffle', - 'seed', - 'drop_last', - 'prefetch_num', - 'pin_memory', - ] if k in cfg.data) - } - - # step 2: cfg.data.train_dataloader has highest priority - train_loader_cfg = dict(loader_cfg, **cfg.data.get('train_dataloader', {})) - - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus/cpus - model = MMDataParallel(model, device_ids=range(cfg.gpus)) - - # build runner - optimizer = build_optimizers(model, cfg.optimizers) - runner = IterBasedRunner( - model, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta) - - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register hooks - runner.register_training_hooks( - cfg.lr_config, - checkpoint_config=cfg.checkpoint_config, - log_config=cfg.log_config) - - # visual hook - if cfg.get('visual_config', None) is not None: - cfg.visual_config['output_dir'] = os.path.join( - cfg.work_dir, cfg.visual_config['output_dir']) - runner.register_hook(mmcv.build_from_cfg(cfg.visual_config, HOOKS)) - - # evaluation hook - if validate and cfg.get('evaluation', None) is not None: - dataset = build_dataset(cfg.data.val) - - if ('val_samples_per_gpu' in cfg.data - or 'val_workers_per_gpu' in cfg.data): - warnings.warn('"val_samples_per_gpu/val_workers_per_gpu" have ' - 'been deprecated. Please use ' - '"val_dataloader=dict(samples_per_gpu=1)" instead. ' - 'Details see ' - 'https://github.com/open-mmlab/mmediting/pull/201') - - val_loader_cfg = { - **loader_cfg, - **dict(shuffle=False, drop_last=False), - **dict((newk, cfg.data[oldk]) for oldk, newk in [ - ('val_samples_per_gpu', 'samples_per_gpu'), - ('val_workers_per_gpu', 'workers_per_gpu'), - ] if oldk in cfg.data), - **cfg.data.get('val_dataloader', {}) - } - - data_loader = build_dataloader(dataset, **val_loader_cfg) - save_path = osp.join(cfg.work_dir, 'val_visuals') - runner.register_hook( - EvalIterHook(data_loader, save_path=save_path, **cfg.evaluation), - priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow, cfg.total_iters) diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/core/__init__.py deleted file mode 100755 index 5d4159e7c..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .evaluation import (DistEvalIterHook, EvalIterHook, L1Evaluation, mae, - mse, psnr, reorder_image, sad, ssim) -from .hooks import VisualizationHook -from .misc import tensor2img -from .optimizer import build_optimizers -from .scheduler import LinearLrUpdaterHook, ReduceLrUpdaterHook - -__all__ = [ - 'build_optimizers', 'tensor2img', 'EvalIterHook', 'DistEvalIterHook', - 'mse', 'psnr', 'reorder_image', 'sad', 'ssim', 'LinearLrUpdaterHook', - 'VisualizationHook', 'L1Evaluation', 'ReduceLrUpdaterHook', 'mae' -] diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/distributed_wrapper.py b/cv/super_resolution/liif/pytorch/mmedit/core/distributed_wrapper.py deleted file mode 100755 index 660f41f3f..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/distributed_wrapper.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.parallel import MODULE_WRAPPERS, MMDistributedDataParallel -from mmcv.parallel.scatter_gather import scatter_kwargs -from torch.cuda._utils import _get_device_index - - -@MODULE_WRAPPERS.register_module() -class DistributedDataParallelWrapper(nn.Module): - """A DistributedDataParallel wrapper for models in MMediting. - - In MMedting, there is a need to wrap different modules in the models - with separate DistributedDataParallel. Otherwise, it will cause - errors for GAN training. - More specific, the GAN model, usually has two sub-modules: - generator and discriminator. If we wrap both of them in one - standard DistributedDataParallel, it will cause errors during training, - because when we update the parameters of the generator (or discriminator), - the parameters of the discriminator (or generator) is not updated, which is - not allowed for DistributedDataParallel. - So we design this wrapper to separately wrap DistributedDataParallel - for generator and discriminator. - - In this wrapper, we perform two operations: - 1. Wrap the modules in the models with separate MMDistributedDataParallel. - Note that only modules with parameters will be wrapped. - 2. Do scatter operation for 'forward', 'train_step' and 'val_step'. - - Note that the arguments of this wrapper is the same as those in - `torch.nn.parallel.distributed.DistributedDataParallel`. - - Args: - module (nn.Module): Module that needs to be wrapped. - device_ids (list[int | `torch.device`]): Same as that in - `torch.nn.parallel.distributed.DistributedDataParallel`. - dim (int, optional): Same as that in the official scatter function in - pytorch. Defaults to 0. - broadcast_buffers (bool): Same as that in - `torch.nn.parallel.distributed.DistributedDataParallel`. - Defaults to False. - find_unused_parameters (bool, optional): Same as that in - `torch.nn.parallel.distributed.DistributedDataParallel`. - Traverse the autograd graph of all tensors contained in returned - value of the wrapped module’s forward function. Defaults to False. - kwargs (dict): Other arguments used in - `torch.nn.parallel.distributed.DistributedDataParallel`. - """ - - def __init__(self, - module, - device_ids, - dim=0, - broadcast_buffers=False, - find_unused_parameters=False, - **kwargs): - super().__init__() - assert len(device_ids) == 1, ( - 'Currently, DistributedDataParallelWrapper only supports one' - 'single CUDA device for each process.' - f'The length of device_ids must be 1, but got {len(device_ids)}.') - self.module = module - self.dim = dim - self.to_ddp( - device_ids=device_ids, - dim=dim, - broadcast_buffers=broadcast_buffers, - find_unused_parameters=find_unused_parameters, - **kwargs) - self.output_device = _get_device_index(device_ids[0], True) - - def to_ddp(self, device_ids, dim, broadcast_buffers, - find_unused_parameters, **kwargs): - """Wrap models with separate MMDistributedDataParallel. - - It only wraps the modules with parameters. - """ - for name, module in self.module._modules.items(): - if next(module.parameters(), None) is None: - module = module.cuda() - elif all(not p.requires_grad for p in module.parameters()): - module = module.cuda() - else: - module = MMDistributedDataParallel( - module.cuda(), - device_ids=device_ids, - dim=dim, - broadcast_buffers=broadcast_buffers, - find_unused_parameters=find_unused_parameters, - **kwargs) - self.module._modules[name] = module - - def scatter(self, inputs, kwargs, device_ids): - """Scatter function. - - Args: - inputs (Tensor): Input Tensor. - kwargs (dict): Args for - ``mmcv.parallel.scatter_gather.scatter_kwargs``. - device_ids (int): Device id. - """ - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - """Forward function. - - Args: - inputs (tuple): Input data. - kwargs (dict): Args for - ``mmcv.parallel.scatter_gather.scatter_kwargs``. - """ - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - """Train step function. - - Args: - inputs (Tensor): Input Tensor. - kwargs (dict): Args for - ``mmcv.parallel.scatter_gather.scatter_kwargs``. - """ - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - """Validation step function. - - Args: - inputs (tuple): Input data. - kwargs (dict): Args for ``scatter_kwargs``. - """ - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/__init__.py deleted file mode 100755 index a81681152..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .eval_hooks import DistEvalIterHook, EvalIterHook -from .metrics import (L1Evaluation, connectivity, gradient_error, mae, mse, - psnr, reorder_image, sad, ssim) - -__all__ = [ - 'mse', 'sad', 'psnr', 'reorder_image', 'ssim', 'EvalIterHook', - 'DistEvalIterHook', 'L1Evaluation', 'gradient_error', 'connectivity', 'mae' -] diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/eval_hooks.py b/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/eval_hooks.py deleted file mode 100755 index bda0f846c..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/eval_hooks.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.runner import Hook -from torch.utils.data import DataLoader - - -class EvalIterHook(Hook): - """Non-Distributed evaluation hook for iteration-based runner. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader. - interval (int): Evaluation interval. Default: 1. - eval_kwargs (dict): Other eval kwargs. It contains: - save_image (bool): Whether to save image. - save_path (str): The path to save image. - """ - - def __init__(self, dataloader, interval=1, **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError('dataloader must be a pytorch DataLoader, ' - f'but got { type(dataloader)}') - self.dataloader = dataloader - self.interval = interval - self.eval_kwargs = eval_kwargs - self.save_image = self.eval_kwargs.pop('save_image', False) - self.save_path = self.eval_kwargs.pop('save_path', None) - - def after_train_iter(self, runner): - """The behavior after each train iteration. - - Args: - runner (``mmcv.runner.BaseRunner``): The runner. - """ - if not self.every_n_iters(runner, self.interval): - return - runner.log_buffer.clear() - from mmedit.apis import single_gpu_test - results = single_gpu_test( - runner.model, - self.dataloader, - save_image=self.save_image, - save_path=self.save_path, - iteration=runner.iter) - self.evaluate(runner, results) - - def evaluate(self, runner, results): - """Evaluation function. - - Args: - runner (``mmcv.runner.BaseRunner``): The runner. - results (dict): Model forward results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - # call `after_val_epoch` after evaluation. - # This is a hack. - # Because epoch does not naturally exist In IterBasedRunner, - # thus we consider the end of an evluation as the end of an epoch. - # With this hack , we can support epoch based hooks. - if 'iter' in runner.__class__.__name__.lower(): - runner.call_hook('after_val_epoch') - - -class DistEvalIterHook(EvalIterHook): - """Distributed evaluation hook. - - Args: - dataloader (DataLoader): A PyTorch dataloader. - interval (int): Evaluation interval. Default: 1. - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - eval_kwargs (dict): Other eval kwargs. It may contain: - save_image (bool): Whether save image. - save_path (str): The path to save image. - """ - - def __init__(self, - dataloader, - interval=1, - gpu_collect=False, - **eval_kwargs): - super().__init__(dataloader, interval, **eval_kwargs) - self.gpu_collect = gpu_collect - - def after_train_iter(self, runner): - """The behavior after each train iteration. - - Args: - runner (``mmcv.runner.BaseRunner``): The runner. - """ - if not self.every_n_iters(runner, self.interval): - return - runner.log_buffer.clear() - from mmedit.apis import multi_gpu_test - results = multi_gpu_test( - runner.model, - self.dataloader, - tmpdir=osp.join(runner.work_dir, '.eval_hook'), - gpu_collect=self.gpu_collect, - save_image=self.save_image, - save_path=self.save_path, - iteration=runner.iter) - if runner.rank == 0: - print('\n') - self.evaluate(runner, results) diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/metric_utils.py b/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/metric_utils.py deleted file mode 100755 index ec6017678..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/metric_utils.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def gaussian(x, sigma): - """Gaussian function. - - Args: - x (array_like): The independent variable. - sigma (float): Standard deviation of the gaussian function. - - Return: - ndarray or scalar: Gaussian value of `x`. - """ - return np.exp(-x**2 / (2 * sigma**2)) / (sigma * np.sqrt(2 * np.pi)) - - -def dgaussian(x, sigma): - """Gradient of gaussian. - - Args: - x (array_like): The independent variable. - sigma (float): Standard deviation of the gaussian function. - - Return: - ndarray or scalar: Gradient of gaussian of `x`. - """ - return -x * gaussian(x, sigma) / sigma**2 - - -def gauss_filter(sigma, epsilon=1e-2): - """Gradient of gaussian. - - Args: - sigma (float): Standard deviation of the gaussian kernel. - epsilon (float): Small value used when calculating kernel size. - Default: 1e-2. - - Return: - tuple[ndarray]: Gaussian filter along x and y axis. - """ - half_size = np.ceil( - sigma * np.sqrt(-2 * np.log(np.sqrt(2 * np.pi) * sigma * epsilon))) - size = int(2 * half_size + 1) - - # create filter in x axis - filter_x = np.zeros((size, size)) - for i in range(size): - for j in range(size): - filter_x[i, j] = gaussian(i - half_size, sigma) * dgaussian( - j - half_size, sigma) - - # normalize filter - norm = np.sqrt((filter_x**2).sum()) - filter_x = filter_x / norm - filter_y = np.transpose(filter_x) - - return filter_x, filter_y - - -def gauss_gradient(img, sigma): - """Gaussian gradient. - - From https://www.mathworks.com/matlabcentral/mlc-downloads/downloads/ - submissions/8060/versions/2/previews/gaussgradient/gaussgradient.m/ - index.html - - Args: - img (ndarray): Input image. - sigma (float): Standard deviation of the gaussian kernel. - - Return: - ndarray: Gaussian gradient of input `img`. - """ - filter_x, filter_y = gauss_filter(sigma) - img_filtered_x = cv2.filter2D( - img, -1, filter_x, borderType=cv2.BORDER_REPLICATE) - img_filtered_y = cv2.filter2D( - img, -1, filter_y, borderType=cv2.BORDER_REPLICATE) - return np.sqrt(img_filtered_x**2 + img_filtered_y**2) diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/metrics.py b/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/metrics.py deleted file mode 100755 index bc1760fd9..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/metrics.py +++ /dev/null @@ -1,433 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import cv2 -import mmcv -import numpy as np -from scipy.ndimage import convolve -from scipy.special import gamma - -from .metric_utils import gauss_gradient - - -def sad(alpha, trimap, pred_alpha): - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - assert (pred_alpha[trimap == 0] == 0).all() - assert (pred_alpha[trimap == 255] == 255).all() - alpha = alpha.astype(np.float64) / 255 - pred_alpha = pred_alpha.astype(np.float64) / 255 - sad_result = np.abs(pred_alpha - alpha).sum() / 1000 - return sad_result - - -def mse(alpha, trimap, pred_alpha): - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - assert (pred_alpha[trimap == 0] == 0).all() - assert (pred_alpha[trimap == 255] == 255).all() - alpha = alpha.astype(np.float64) / 255 - pred_alpha = pred_alpha.astype(np.float64) / 255 - weight_sum = (trimap == 128).sum() - if weight_sum != 0: - mse_result = ((pred_alpha - alpha)**2).sum() / weight_sum - else: - mse_result = 0 - return mse_result - - -def gradient_error(alpha, trimap, pred_alpha, sigma=1.4): - """Gradient error for evaluating alpha matte prediction. - - Args: - alpha (ndarray): Ground-truth alpha matte. - trimap (ndarray): Input trimap with its value in {0, 128, 255}. - pred_alpha (ndarray): Predicted alpha matte. - sigma (float): Standard deviation of the gaussian kernel. Default: 1.4. - """ - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - if not ((pred_alpha[trimap == 0] == 0).all() and - (pred_alpha[trimap == 255] == 255).all()): - raise ValueError( - 'pred_alpha should be masked by trimap before evaluation') - alpha = alpha.astype(np.float64) - pred_alpha = pred_alpha.astype(np.float64) - alpha_normed = np.zeros_like(alpha) - pred_alpha_normed = np.zeros_like(pred_alpha) - cv2.normalize(alpha, alpha_normed, 1., 0., cv2.NORM_MINMAX) - cv2.normalize(pred_alpha, pred_alpha_normed, 1., 0., cv2.NORM_MINMAX) - - alpha_grad = gauss_gradient(alpha_normed, sigma).astype(np.float32) - pred_alpha_grad = gauss_gradient(pred_alpha_normed, - sigma).astype(np.float32) - - grad_loss = ((alpha_grad - pred_alpha_grad)**2 * (trimap == 128)).sum() - # same as SAD, divide by 1000 to reduce the magnitude of the result - return grad_loss / 1000 - - -def connectivity(alpha, trimap, pred_alpha, step=0.1): - """Connectivity error for evaluating alpha matte prediction. - - Args: - alpha (ndarray): Ground-truth alpha matte with shape (height, width). - Value range of alpha is [0, 255]. - trimap (ndarray): Input trimap with shape (height, width). Elements - in trimap are one of {0, 128, 255}. - pred_alpha (ndarray): Predicted alpha matte with shape (height, width). - Value range of pred_alpha is [0, 255]. - step (float): Step of threshold when computing intersection between - `alpha` and `pred_alpha`. - """ - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - if not ((pred_alpha[trimap == 0] == 0).all() and - (pred_alpha[trimap == 255] == 255).all()): - raise ValueError( - 'pred_alpha should be masked by trimap before evaluation') - alpha = alpha.astype(np.float32) / 255 - pred_alpha = pred_alpha.astype(np.float32) / 255 - - thresh_steps = np.arange(0, 1 + step, step) - round_down_map = -np.ones_like(alpha) - for i in range(1, len(thresh_steps)): - alpha_thresh = alpha >= thresh_steps[i] - pred_alpha_thresh = pred_alpha >= thresh_steps[i] - intersection = (alpha_thresh & pred_alpha_thresh).astype(np.uint8) - - # connected components - _, output, stats, _ = cv2.connectedComponentsWithStats( - intersection, connectivity=4) - # start from 1 in dim 0 to exclude background - size = stats[1:, -1] - - # largest connected component of the intersection - omega = np.zeros_like(alpha) - if len(size) != 0: - max_id = np.argmax(size) - # plus one to include background - omega[output == max_id + 1] = 1 - - mask = (round_down_map == -1) & (omega == 0) - round_down_map[mask] = thresh_steps[i - 1] - round_down_map[round_down_map == -1] = 1 - - alpha_diff = alpha - round_down_map - pred_alpha_diff = pred_alpha - round_down_map - # only calculate difference larger than or equal to 0.15 - alpha_phi = 1 - alpha_diff * (alpha_diff >= 0.15) - pred_alpha_phi = 1 - pred_alpha_diff * (pred_alpha_diff >= 0.15) - - connectivity_error = np.sum( - np.abs(alpha_phi - pred_alpha_phi) * (trimap == 128)) - # same as SAD, divide by 1000 to reduce the magnitude of the result - return connectivity_error / 1000 - - -def reorder_image(img, input_order='HWC'): - """Reorder images to 'HWC' order. - - If the input_order is (h, w), return (h, w, 1); - If the input_order is (c, h, w), return (h, w, c); - If the input_order is (h, w, c), return as it is. - - Args: - img (ndarray): Input image. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - If the input image shape is (h, w), input_order will not have - effects. Default: 'HWC'. - - Returns: - ndarray: reordered image. - """ - - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - if len(img.shape) == 2: - img = img[..., None] - return img - if input_order == 'CHW': - img = img.transpose(1, 2, 0) - return img - - -def psnr(img1, img2, crop_border=0, input_order='HWC', convert_to=None): - """Calculate PSNR (Peak Signal-to-Noise Ratio). - - Ref: https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio - - Args: - img1 (ndarray): Images with range [0, 255]. - img2 (ndarray): Images with range [0, 255]. - crop_border (int): Cropped pixels in each edges of an image. These - pixels are not involved in the PSNR calculation. Default: 0. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether to convert the images to other color models. - If None, the images are not altered. When computing for 'Y', - the images are assumed to be in BGR order. Options are 'Y' and - None. Default: None. - - Returns: - float: psnr result. - """ - - assert img1.shape == img2.shape, ( - f'Image shapes are different: {img1.shape}, {img2.shape}.') - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - img1 = reorder_image(img1, input_order=input_order) - img2 = reorder_image(img2, input_order=input_order) - - img1, img2 = img1.astype(np.float32), img2.astype(np.float32) - if isinstance(convert_to, str) and convert_to.lower() == 'y': - img1 = mmcv.bgr2ycbcr(img1 / 255., y_only=True) * 255. - img2 = mmcv.bgr2ycbcr(img2 / 255., y_only=True) * 255. - elif convert_to is not None: - raise ValueError('Wrong color model. Supported values are ' - '"Y" and None.') - - if crop_border != 0: - img1 = img1[crop_border:-crop_border, crop_border:-crop_border, None] - img2 = img2[crop_border:-crop_border, crop_border:-crop_border, None] - - mse_value = np.mean((img1 - img2)**2) - if mse_value == 0: - return float('inf') - return 20. * np.log10(255. / np.sqrt(mse_value)) - - -def mae(img1, img2, crop_border=0, input_order='HWC', convert_to=None): - """Calculate mean average error for evaluation. - - Args: - img1 (ndarray): Images with range [0, 255]. - img2 (ndarray): Images with range [0, 255]. - crop_border (int): Cropped pixels in each edges of an image. These - pixels are not involved in the PSNR calculation. Default: 0. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether to convert the images to other color models. - If None, the images are not altered. Options are 'RGB2Y', 'BGR2Y' - and None. Default: None. - - Returns: - float: mae result. - """ - - assert img1.shape == img2.shape, ( - f'Image shapes are different: {img1.shape}, {img2.shape}.') - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - img1 = reorder_image(img1, input_order=input_order) - img2 = reorder_image(img2, input_order=input_order) - - img1, img2 = img1.astype(np.float32), img2.astype(np.float32) - img1, img2 = img1 / 255., img2 / 255. - if isinstance(convert_to, str) and convert_to.lower() == 'rgb2y': - img1 = mmcv.rgb2ycbcr(img1, y_only=True) - img2 = mmcv.rgb2ycbcr(img2, y_only=True) - elif isinstance(convert_to, str) and convert_to.lower() == 'bgr2y': - img1 = mmcv.bgr2ycbcr(img1, y_only=True) - img2 = mmcv.bgr2ycbcr(img2, y_only=True) - elif convert_to is not None: - raise ValueError('Wrong color model. Supported values are ' - '"RGB2Y", "BGR2Y" and None.') - - if crop_border != 0: - img1 = img1[crop_border:-crop_border, crop_border:-crop_border, None] - img2 = img2[crop_border:-crop_border, crop_border:-crop_border, None] - - l1_value = np.mean(np.abs(img1 - img2)) - - return l1_value - - -def _ssim(img1, img2): - """Calculate SSIM (structural similarity) for one channel images. - - It is called by func:`calculate_ssim`. - - Args: - img1, img2 (ndarray): Images with range [0, 255] with order 'HWC'. - - Returns: - float: ssim result. - """ - - C1 = (0.01 * 255)**2 - C2 = (0.03 * 255)**2 - - img1 = img1.astype(np.float64) - img2 = img2.astype(np.float64) - kernel = cv2.getGaussianKernel(11, 1.5) - window = np.outer(kernel, kernel.transpose()) - - mu1 = cv2.filter2D(img1, -1, window)[5:-5, 5:-5] - mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5] - mu1_sq = mu1**2 - mu2_sq = mu2**2 - mu1_mu2 = mu1 * mu2 - sigma1_sq = cv2.filter2D(img1**2, -1, window)[5:-5, 5:-5] - mu1_sq - sigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sq - sigma12 = cv2.filter2D(img1 * img2, -1, window)[5:-5, 5:-5] - mu1_mu2 - - ssim_map = ((2 * mu1_mu2 + C1) * - (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) * - (sigma1_sq + sigma2_sq + C2)) - return ssim_map.mean() - - -def ssim(img1, img2, crop_border=0, input_order='HWC', convert_to=None): - """Calculate SSIM (structural similarity). - - Ref: - Image quality assessment: From error visibility to structural similarity - - The results are the same as that of the official released MATLAB code in - https://ece.uwaterloo.ca/~z70wang/research/ssim/. - - For three-channel images, SSIM is calculated for each channel and then - averaged. - - Args: - img1 (ndarray): Images with range [0, 255]. - img2 (ndarray): Images with range [0, 255]. - crop_border (int): Cropped pixels in each edges of an image. These - pixels are not involved in the SSIM calculation. Default: 0. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether to convert the images to other color models. - If None, the images are not altered. When computing for 'Y', - the images are assumed to be in BGR order. Options are 'Y' and - None. Default: None. - - Returns: - float: ssim result. - """ - - assert img1.shape == img2.shape, ( - f'Image shapes are different: {img1.shape}, {img2.shape}.') - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - img1 = reorder_image(img1, input_order=input_order) - img2 = reorder_image(img2, input_order=input_order) - - if isinstance(convert_to, str) and convert_to.lower() == 'y': - img1, img2 = img1.astype(np.float32), img2.astype(np.float32) - img1 = mmcv.bgr2ycbcr(img1 / 255., y_only=True) * 255. - img2 = mmcv.bgr2ycbcr(img2 / 255., y_only=True) * 255. - img1 = np.expand_dims(img1, axis=2) - img2 = np.expand_dims(img2, axis=2) - elif convert_to is not None: - raise ValueError('Wrong color model. Supported values are ' - '"Y" and None') - - if crop_border != 0: - img1 = img1[crop_border:-crop_border, crop_border:-crop_border, None] - img2 = img2[crop_border:-crop_border, crop_border:-crop_border, None] - - ssims = [] - for i in range(img1.shape[2]): - ssims.append(_ssim(img1[..., i], img2[..., i])) - return np.array(ssims).mean() - - -class L1Evaluation: - """L1 evaluation metric. - - Args: - data_dict (dict): Must contain keys of 'gt_img' and 'fake_res'. If - 'mask' is given, the results will be computed with mask as weight. - """ - - def __call__(self, data_dict): - gt = data_dict['gt_img'] - if 'fake_img' in data_dict: - pred = data_dict.get('fake_img') - else: - pred = data_dict.get('fake_res') - mask = data_dict.get('mask', None) - - from mmedit.models.losses.pixelwise_loss import l1_loss - l1_error = l1_loss(pred, gt, weight=mask, reduction='mean') - - return l1_error - - -def estimate_aggd_param(block): - """Estimate AGGD (Asymmetric Generalized Gaussian Distribution) parameters. - - Args: - block (ndarray): 2D Image block. - - Returns: - tuple: alpha (float), beta_l (float) and beta_r (float) for the AGGD - distribution (Estimating the parames in Equation 7 in the paper). - """ - block = block.flatten() - gam = np.arange(0.2, 10.001, 0.001) # len = 9801 - gam_reciprocal = np.reciprocal(gam) - r_gam = np.square(gamma(gam_reciprocal * 2)) / ( - gamma(gam_reciprocal) * gamma(gam_reciprocal * 3)) - - left_std = np.sqrt(np.mean(block[block < 0]**2)) - right_std = np.sqrt(np.mean(block[block > 0]**2)) - gammahat = left_std / right_std - rhat = (np.mean(np.abs(block)))**2 / np.mean(block**2) - rhatnorm = (rhat * (gammahat**3 + 1) * - (gammahat + 1)) / ((gammahat**2 + 1)**2) - array_position = np.argmin((r_gam - rhatnorm)**2) - - alpha = gam[array_position] - beta_l = left_std * np.sqrt(gamma(1 / alpha) / gamma(3 / alpha)) - beta_r = right_std * np.sqrt(gamma(1 / alpha) / gamma(3 / alpha)) - return (alpha, beta_l, beta_r) - - -def compute_feature(block): - """Compute features. - - Args: - block (ndarray): 2D Image block. - - Returns: - list: Features with length of 18. - """ - feat = [] - alpha, beta_l, beta_r = estimate_aggd_param(block) - feat.extend([alpha, (beta_l + beta_r) / 2]) - - # distortions disturb the fairly regular structure of natural images. - # This deviation can be captured by analyzing the sample distribution of - # the products of pairs of adjacent coefficients computed along - # horizontal, vertical and diagonal orientations. - shifts = [[0, 1], [1, 0], [1, 1], [1, -1]] - for shift in shifts: - shifted_block = np.roll(block, shift, axis=(0, 1)) - alpha, beta_l, beta_r = estimate_aggd_param(block * shifted_block) - mean = (beta_r - beta_l) * (gamma(2 / alpha) / gamma(1 / alpha)) - feat.extend([alpha, mean, beta_l, beta_r]) - return feat - diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/niqe_pris_params.npz b/cv/super_resolution/liif/pytorch/mmedit/core/evaluation/niqe_pris_params.npz deleted file mode 100755 index 204ddcee87c4cd39aca04a42b539f0a5bfccecc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11850 zcmbt)cUaH;+jr3=DyzX|m!hnqC9AVZMABZ`oA%y&X;D&HMUqmXgpjuqEi+{lC3KOH zvO@CYcVDlL`*+><^T+f2zK%G)KA+F`=s3UU`8r?Yt*tqhOOWHgULqW0eZL;d<>cV_ z{WFzAki*5t!rjx!%fj8t)5=BC)jfcdW6HlS{(j*1O}}61TKZeGjX61dIfBG(?YwL} z#a9c9ueDbYUn(qa@8;?4Y2|9+=4os9`~TNl?ewx6`F}4*D|fq*_Yy0X3d_hZS}GhO z{QvvnnI`=;G2{-HlBB`x_0d1xQg?O8QO%(qnsX|_U3TGla@yMO{j0i~EZ13=saQ19 zs$Ija!?JJad3blt(Dzf6o4IXBA@>?hy~l6iD$qc}-_4DDJ>QUV{rspT(fzE#WmAQm zXg&R`|LdE$!gbQGerum;`G!JI?(dQr%w|tgvUZDpipczx02yA zzneR2-cxJ-d%+jWdTD-fMhoBc3VJ4AyhiGHEy=i5$!~4ENn?ficteAG$>j5Eo?kM> z7DnTQ4dnDn}rvW|bKT@{wg)=4h9w>YDT)_R!DHJe^f&obA< z&S~zYiU8|hAwA=4Gn!hTB&h@&)bYvErN)GvQ)U*g1Q7XExe z`||h_;>fYBHaT{jE^V&f*>I(q1pUq&HXJ%l3bpR}?TtlL(%QMb;EOAj42_wmE_j5Z zzq$3!OU|crzZRu)brn%H*9C_gnx5>Nq>WIGaUR*m8H6hMy-ACe6_nob+v?2Z{d&fCCCX-~=>-`b8)5*cpIw-|{AK5+ssC)6O zHjDYp-|o(}hvYsxu5Ep{hf3y`n3})LpnXdgrfRSDVG~}QKY9FUIyv9nAbNXQ3eDdT zIVRaJgBt62+Gx2q>)e~!W0jXq^6zz8-qao^qUwx1Tz&JvfW@Muvu5_DrA+ zYE)2}t;}wYskAO|iKCrWaV^1W33Q;O^XJW1@pM%_F6o7lC7U$l;pF)>o|GbLoSyBA zqdQYSM>+?`Qq7c#7RfMI=Fy=zJT$b6Ottn~?b{tin;$<+N{opi<-v||%^}Y0WQC0` zx5X|};(RT-<9O6)zo0JXBUWs_Cz3{eb2sIOggVl?MWxC!<044-*h_UCi6klv5i7D< zn?@mbhP?H19w_q1U;cEyCWe;ry=+Tf5>Mqyp+|)>_fX`=-Q!)KTCrPxX}J=-2^24& z{?`oCXeys5D6X?@4-K{^j&HnR$r}E2-QB~VK)!tH7iN`3(U8*u{qgaIwENYZzhu6r zke8f_cJ)v!#hq5V6w0xZPI;7f&3GL~d0DesjV44<(Ljr4eM2k_E>PU);Ix(ro#^KH zaVwncn)j`Y?v0=)`?jd@B}UWxguH6?Jafj+OYOYJB8d&Nw5*z78WU5pslO?TWIh?M zTyx)q#ogkm;NB5Q8`bv9-Ea>cog?%M>T=gfan)XNciJMYQhO{+iP>0r{ygC9L`Hmj z+a zRO8@wT?r+YV>ZiD-`bLPzPR2uvE5)4Uoc1L7u4l@k}uE2i$V1Eokhd&+fXV!w(8E5 z;&7UoR;OH5CQV5j3vRBi3M9`ftM{dz3Z*p`vRPL@hS8hij1TuSELrmBl*BWf;WR6M zwab`ve;O0k*p{>;jGWJ>UD=~(#Rl$3HBS69oW8t|s+n2uN9&BObogQuX?#hyTgYGp z-HcW9zTo3Ucg#3)=O!AkU~awG)8*bYba|b0!5^N~JI!YHy&vv$@PNk8_0mF2!?p0i zj4BU0cEa&xy{H$}Z%)Z*H*}%bO=%|&>FBa^y4g7<_r2&@+P6tk(ymnPZ6Nh|)=m-= zOLe}Vs>_t-_8q)b=S4S~PKb(LcNxW9;0xvm{err*1(qwmS56>}{*TLZHIhjnFj&Fg zL7&}Q(#dmYNeuZVC?-k7M3a`7v&}q@M2d9LeyEsX%dD%$*>rx2rL2UWxus_!XhW#v z<(JbF$t?Ze_Q}5X?959+9{w$HRQ9qzTj)hNO}B3iwbnjNio+9lhh2Bk%JN5KW)Mz; zwUaHpG}TElS7=`Tl>kzEc6sRT^)PZ-6m2MSJ)CAASSOv|V#1V;@mqV{4JDDqA~O9+ zLG-|&D%yKZ2;I!w`qO`#1>=#oD-ul%BP-pD1FO{h$c`md8b}Az`PViF+qf*)(gtts z@AhHDUEC_8`ptKA9|3oPFPJ0r3+l4ly^M23VLH8OA5W7e=&;ig3l<-}8b@Qce=py< zHJ-RmnV9)mr%}P3v{K$$8x~jA@RaXk0%fjX3!TnHQ=7-td0z9+`S=m zyMni2+Wjaxr7>n1w|zV<ztwS z#*!sQI~K|Q8AUyF-I@gpg6Vyo`Z%$cXsWGK`1Qv-6Q(h3N~!qqNV0a85P$kOcyv!= z9|3oPFPJ0r3+gh?`a55WryFID^>?_t!H)e|74IRXrA|jWX1DY`Q)PquXKejxXh2gc zO+&dZ>#?(ACYx+j7iFn2PlmQ}sWa2unA1(oMr1Yq_A$PELzY;S^q{JXgRZFvL(Cgv--B0q3*xjU8QHJ)$^zKBG7MF|+1<1_|U38n1^(}d6 zK3>$0_UZ;icgwA3)9vE(g$GTj^zf7K-jz!!;dtT4#j94)W(NhX*6wu_KaaoUhVW+k zk$mEZkeL{r*c_s(OpD3aS*rZ#3>6xvJF6QIw1L21u&1$)fV;pK%n|wpbvfMQFme0M zC<^GYdnFeVPAv-S+>|0?NP#n~v_js2ndV#Z7R?{|e$1_F+9!n2pOPz&ipa*$?DB3$ zttrl|M|(zzcWyM9Z%Hy-xiN@*B>U_pf7wgk<_m>4eTbqBj*{Fbl>O+sLQ3bQ32M~y zre@;ubDm_kTKCHGbAGh?+`7Cqd;O`ua`vuC=dIb|{_ch6^aIG|M8(9mr=H|JQBtMx zPaj%dJ9mT0cr(@&-rBD>)t|zR%a%>K=uRg&jMg^0dDGezYnGayH(?)}CO^}@;78*^ zo4faQjO?esc^&u*_B8epa2NQ3IYPgnE(TZPxSpm(QK*2-`-)j{G}P2K5M*q@?hJTu zPRWa++3^B7Z*#(E+@l;{xx=xf!6UUpwA!34b`L6(IS@rrDmrZkuZ2?1nyMVdMf<78 zLvDNZqj;)Uy%Jhb6+#jxpYKZEs>bfiDn&@_3Z(3TF9Sk5f{1vkLZ|BllWz6B%AsI! z_O5QBVWv+2&5$1I-x3o<&OzaVYp(gzd0yq-Z{0?0VK~nlKhHzRv@H+4p>}l*H z;4bh5bA*0DUCPuI<8=lcsl)r4@;EM6cJ9fV!yjYySjorspc9qW%w9u(gr=xbmC~SB z)(Sh8y1m}fsLz5mxw%$enxV#yC_0E)R_n74)6_RgCl#5v%L)2=Re}t1e*M~dMx9Nz zY<;L3tj>Ok9BNobs~E3A^FXKEHcERg*ekYNmra(>+1fO)gweuU!Ak!1Y)Zti`Mad; z^gV{_(5X*GOuNv_cG3g^sm8#Rm_}uaBppFej!y#-6p_ zW-}w5VyWQ9t)FKd!pUNJf1lKzY`VXv-aB!5JoSB!IPzlJ$oyodUG%NzVcMCyBo@i~ zQ_v=Z(gdR*+9zEmBbgjTiY0qD|MA_BO}<J`9)7VGAUEmAm2>pV(jGeyOQ>8A5#w))cQH{}5vj6l{F$XI)!F`rk z`j!Y1nxqx07#KjG45oxjf61hx$dcY+7EOP>OKEog=}&5_jVnLy=4X4#yZ>-f_n|V& z2d{P>_al>+UtfA%_NQi*o=0ZZD$Ff?z0v)pzGP|N;wl#EOL=cwuIwG_OKZ*+9^WLi zjdhhbc6{0GM^^I{(p~@XB730@5!W~$Dt~pkkDJSkiO!h*)%mSIopV%gPY?1K)zi?8 z&_~b#;Je^;;4j$I*hj!!;0xvm{errrW^`Duu}!Bb-$V6FPusDvs@*P{PZLO9d*)e* zgeZ#Zo?dm`qnH$=8@4$dO{NF_UzW-G#gf6<#%YFb`lK}Tp7x9pe0`bG`1taTND|ob zl~+$Bj>dgBG01M95VjN9{rS|&wN^vNV{RiB_yp89a21tOGKcUhqE z>%ITsnuavHaqy~b!QdvE za%ZvUn>1~v(7i>>Jx`A1d)|9HTw!ul7!}@ z<)+$9aIIa;uOAkazdCDE@FP>^p6b4K(~cik;J?6UfS!hKgg$}}0N(|#1AoDu#y$e> z0$(sk=oi$b@x5eceXfs#Y5}QrSFLP(SeI7?*iBcO2_mALAwpzi0 zpQ7y6RgQJ7V*d2RP~Y}ceIN0mD&Ghq#A2O*gy=>(=e~QiXU%anQh;;6Y^e)r#CKaQBs-GkJNyzY*-{3bt zirxNxw~eDJqgS6_+En?FPVj)$Qdk$7BOA9;v|0xo*3(- zVc|g!L&rZ%+u%(L3xC;*S8QRbyjh8t_V`l7jm;Z-R(g*5b?~L&zrbgJo`!COK7tMa z-vzG&f5D!{J_7CnUoc1L7t}?(jYG@$c@ODX_&qUvS4M}z{If)C&ePAT&a>+AzLX%v zAKCW(7-b6GQ`+8jl4LUu)$-52Kr7wf284|0@@9U&>73K@$+@b@!Km*feOk7a*W+0w z4embNzg5JH`J_rOiRdbVR)jYn(ZKBppgMZ-Cgsemo>gxR1cPN(nUfLcp@=l`Eg{Nz- zPPJpvfrp~?Y75hA zorZXlNi;=jvff)w=8*M{Q)I*!FzFT0Xx0uN^}XTm!pDSP2VV;Q3w#FXY3N4iBj^C| zUGO^a7wl>5Bj7IZ1#^UcL0t-R<0RDQxKi-^sVBM~nJ}^Ps`F#Xm?U0&ZO!+xr3OK< zIq5Rn*$c<6(2PxnCrj4Q_rN#PW`@a9g5}w}(|puM^8onX@OR;3!moob1^)#;1N1a> zBlHn;0QfF=9rz3OH1-j27x;oXLcgFc9|{DTJXJ#ILF-IA>%>5s>t;Du-6e!Lnm;S) z_2`gK^6n)mjXre9H?~VcKZNcV^`88EEO^8}xB8y?K$8XhjN>=l7DO|LFY%l?=tn<1 z&PK+L?7wrR8v6DHM(owZX{)>If@x-1lUDXBpV6EIc>sKG_`C2i;n%^Jg8u@a0eTv` z5&8%^0DKp`4*Ugs8v6*i3w*&Gp+{ z0KN-e2mXRRjeP{%1-@X8&@ZSteU>F@VA)BZI-92-BFFqz!B+>@s0k*59ileRJ{beVhX%tMWu*EwDe5#&lV7MGDEBrk3@bz(YMczWyW&Tn^-iTZc`4Msw z3RCX2bBp_q=CH^ck*gs;LQaA_0KPZ;UHF*r>)=ble}T^cJw2)$ z|JFy)0pPpfb>J`9)7VGAUEmAm2>pV(G-XKNk0L-jl#(~YCZ@$s#nPui6jJ-^d5 zPb!QgCuSsWa|@>YO^-J0jgO=iT8+{0KN-e2mXRRjeP{%1-@X8&@ZUVJ6$og zj;1}d;_{o*9VOb##`;*1cyKgDiND}};wISKLr_}=h$;bX$DgD(aD1wI4xG;|~M5p)3fE_faI z3-&bj5pWmyf;mFJpf0-?2TTu=wRj50 z-@LcvwbKC|_ONk&c!S?a9$7JGfrD${=s6FZXFzU`d>T0{@>0l6WgJ-8 zkNzWRS}RA-OW|A)&cEQC2hKAfw?{sW92R*aay8^f$Vrd~!1so~3m+4H9egSHFYpB4G3!8O}@LToBH` z;G74}Ga$D|K8+j}c_VT)%d>I zr?HQKyTBLB5&8vnF}$Ux-Yw_*k293zU$gwikKy3>{Ub1PhSI^x$IHuUz!e_h!Xm^{PL9%!{9nR`LwC2Qt(f7EH=kfWtKW>sn*S?lwvMqV! z=XCch;XSV7e*8Z8eB9svaO&986R(UukL$P}&*Ss(`FI}haUJ*L_r>pz>-c=U$93HQ z&-&o={`vd-^L}kj&M|^x|8J+R|Ep(y{~i9X1J?iJ4E8@C|MwHU|NZe7BOmZT&-ecG k`G4=`|NgxA5|00^x3x9@829_Ou_J%3j{NJr?DxC>1Ij;2^Z)<= diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/export/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/core/export/__init__.py deleted file mode 100755 index 6cf757d83..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/export/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .wrappers import ONNXRuntimeEditing - -__all__ = ['ONNXRuntimeEditing'] diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/export/wrappers.py b/cv/super_resolution/liif/pytorch/mmedit/core/export/wrappers.py deleted file mode 100755 index 5b4d55fbc..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/export/wrappers.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -import numpy as np -import onnxruntime as ort -import torch -from torch import nn - -from mmedit.models import BaseMattor, BasicRestorer, build_model - - -def inference_with_session(sess, io_binding, output_names, input_tensor): - device_type = input_tensor.device.type - device_id = input_tensor.device.index - device_id = 0 if device_id is None else device_id - io_binding.bind_input( - name='input', - device_type=device_type, - device_id=device_id, - element_type=np.float32, - shape=input_tensor.shape, - buffer_ptr=input_tensor.data_ptr()) - for name in output_names: - io_binding.bind_output(name) - sess.run_with_iobinding(io_binding) - pred = io_binding.copy_outputs_to_cpu() - return pred - - -class ONNXRuntimeMattor(nn.Module): - - def __init__(self, sess, io_binding, output_names, base_model): - super(ONNXRuntimeMattor, self).__init__() - self.sess = sess - self.io_binding = io_binding - self.output_names = output_names - self.base_model = base_model - - def forward(self, - merged, - trimap, - meta, - test_mode=False, - save_image=False, - save_path=None, - iteration=None): - input_tensor = torch.cat((merged, trimap), 1).contiguous() - pred_alpha = inference_with_session(self.sess, self.io_binding, - self.output_names, input_tensor)[0] - - pred_alpha = pred_alpha.squeeze() - pred_alpha = self.base_model.restore_shape(pred_alpha, meta) - eval_result = self.base_model.evaluate(pred_alpha, meta) - - if save_image: - self.base_model.save_image(pred_alpha, meta, save_path, iteration) - - return {'pred_alpha': pred_alpha, 'eval_result': eval_result} - - -class RestorerGenerator(nn.Module): - - def __init__(self, sess, io_binding, output_names): - super(RestorerGenerator, self).__init__() - self.sess = sess - self.io_binding = io_binding - self.output_names = output_names - - def forward(self, x): - pred = inference_with_session(self.sess, self.io_binding, - self.output_names, x)[0] - pred = torch.from_numpy(pred) - return pred - - -class ONNXRuntimeRestorer(nn.Module): - - def __init__(self, sess, io_binding, output_names, base_model): - super(ONNXRuntimeRestorer, self).__init__() - self.sess = sess - self.io_binding = io_binding - self.output_names = output_names - self.base_model = base_model - restorer_generator = RestorerGenerator(self.sess, self.io_binding, - self.output_names) - base_model.generator = restorer_generator - - def forward(self, lq, gt=None, test_mode=False, **kwargs): - return self.base_model(lq, gt=gt, test_mode=test_mode, **kwargs) - - -class ONNXRuntimeEditing(nn.Module): - - def __init__(self, onnx_file, cfg, device_id): - super(ONNXRuntimeEditing, self).__init__() - ort_custom_op_path = '' - try: - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - except (ImportError, ModuleNotFoundError): - warnings.warn('If input model has custom op from mmcv, \ - you may have to build mmcv with ONNXRuntime from source.') - session_options = ort.SessionOptions() - # register custom op for onnxruntime - if osp.exists(ort_custom_op_path): - session_options.register_custom_ops_library(ort_custom_op_path) - sess = ort.InferenceSession(onnx_file, session_options) - providers = ['CPUExecutionProvider'] - options = [{}] - is_cuda_available = ort.get_device() == 'GPU' - if is_cuda_available: - providers.insert(0, 'CUDAExecutionProvider') - options.insert(0, {'device_id': device_id}) - - sess.set_providers(providers, options) - - self.sess = sess - self.device_id = device_id - self.io_binding = sess.io_binding() - self.output_names = [_.name for _ in sess.get_outputs()] - - base_model = build_model( - cfg.model, train_cfg=None, test_cfg=cfg.test_cfg) - - if isinstance(base_model, BaseMattor): - WrapperClass = ONNXRuntimeMattor - elif isinstance(base_model, BasicRestorer): - WrapperClass = ONNXRuntimeRestorer - self.wrapper = WrapperClass(self.sess, self.io_binding, - self.output_names, base_model) - - def forward(self, **kwargs): - return self.wrapper(**kwargs) diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/hooks/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/core/hooks/__init__.py deleted file mode 100755 index 575c43b35..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/hooks/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .ema import ExponentialMovingAverageHook -from .visualization import VisualizationHook - -__all__ = ['VisualizationHook', 'ExponentialMovingAverageHook'] diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/hooks/ema.py b/cv/super_resolution/liif/pytorch/mmedit/core/hooks/ema.py deleted file mode 100755 index 0e7f0b2e8..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/hooks/ema.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from copy import deepcopy -from functools import partial - -import mmcv -import torch -from mmcv.parallel import is_module_wrapper -from mmcv.runner import HOOKS, Hook - - -@HOOKS.register_module() -class ExponentialMovingAverageHook(Hook): - """Exponential Moving Average Hook. - - Exponential moving average is a trick that widely used in current GAN - literature, e.g., PGGAN, StyleGAN, and BigGAN. This general idea of it is - maintaining a model with the same architecture, but its parameters are - updated as a moving average of the trained weights in the original model. - In general, the model with moving averaged weights achieves better - performance. - - Args: - module_keys (str | tuple[str]): The name of the ema model. Note that we - require these keys are followed by '_ema' so that we can easily - find the original model by discarding the last four characters. - interp_mode (str, optional): Mode of the interpolation method. - Defaults to 'lerp'. - interp_cfg (dict | None, optional): Set arguments of the interpolation - function. Defaults to None. - interval (int, optional): Evaluation interval (by iterations). - Default: -1. - start_iter (int, optional): Start iteration for ema. If the start - iteration is not reached, the weights of ema model will maintain - the same as the original one. Otherwise, its parameters are updated - as a moving average of the trained weights in the original model. - Default: 0. - """ - - def __init__(self, - module_keys, - interp_mode='lerp', - interp_cfg=None, - interval=-1, - start_iter=0): - super().__init__() - assert isinstance(module_keys, str) or mmcv.is_tuple_of( - module_keys, str) - self.module_keys = (module_keys, ) if isinstance(module_keys, - str) else module_keys - # sanity check for the format of module keys - for k in self.module_keys: - assert k.endswith( - '_ema'), 'You should give keys that end with "_ema".' - self.interp_mode = interp_mode - self.interp_cfg = dict() if interp_cfg is None else deepcopy( - interp_cfg) - self.interval = interval - self.start_iter = start_iter - - assert hasattr( - self, interp_mode - ), f'Currently, we do not support {self.interp_mode} for EMA.' - self.interp_func = partial( - getattr(self, interp_mode), **self.interp_cfg) - - @staticmethod - def lerp(a, b, momentum=0.999, momentum_nontrainable=0., trainable=True): - m = momentum if trainable else momentum_nontrainable - return a + (b - a) * m - - def every_n_iters(self, runner, n): - if runner.iter < self.start_iter: - return True - return (runner.iter + 1 - self.start_iter) % n == 0 if n > 0 else False - - @torch.no_grad() - def after_train_iter(self, runner): - if not self.every_n_iters(runner, self.interval): - return - - model = runner.model.module if is_module_wrapper( - runner.model) else runner.model - - for key in self.module_keys: - # get current ema states - ema_net = getattr(model, key) - states_ema = ema_net.state_dict(keep_vars=False) - # get currently original states - net = getattr(model, key[:-4]) - states_orig = net.state_dict(keep_vars=True) - - for k, v in states_orig.items(): - if runner.iter < self.start_iter: - states_ema[k].data.copy_(v.data) - else: - states_ema[k] = self.interp_func( - v, states_ema[k], trainable=v.requires_grad).detach() - ema_net.load_state_dict(states_ema, strict=True) - - def before_run(self, runner): - model = runner.model.module if is_module_wrapper( - runner.model) else runner.model - # sanity check for ema model - for k in self.module_keys: - if not hasattr(model, k) and not hasattr(model, k[:-4]): - raise RuntimeError( - f'Cannot find both {k[:-4]} and {k} network for EMA hook.') - if not hasattr(model, k) and hasattr(model, k[:-4]): - setattr(model, k, deepcopy(getattr(model, k[:-4]))) - warnings.warn( - f'We do not suggest construct and initialize EMA model {k}' - ' in hook. You may explicitly define it by yourself.') diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/hooks/visualization.py b/cv/super_resolution/liif/pytorch/mmedit/core/hooks/visualization.py deleted file mode 100755 index 63c8ee7d8..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/hooks/visualization.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import torch -from mmcv.runner import HOOKS, Hook -from mmcv.runner.dist_utils import master_only -from torchvision.utils import save_image - - -@HOOKS.register_module() -class VisualizationHook(Hook): - """Visualization hook. - - In this hook, we use the official api `save_image` in torchvision to save - the visualization results. - - Args: - output_dir (str): The file path to store visualizations. - res_name_list (str): The list contains the name of results in outputs - dict. The results in outputs dict must be a torch.Tensor with shape - (n, c, h, w). - interval (int): The interval of calling this hook. If set to -1, - the visualization hook will not be called. Default: -1. - filename_tmpl (str): Format string used to save images. The output file - name will be formatted as this args. Default: 'iter_{}.png'. - rerange (bool): Whether to rerange the output value from [-1, 1] to - [0, 1]. We highly recommend users should preprocess the - visualization results on their own. Here, we just provide a simple - interface. Default: True. - bgr2rgb (bool): Whether to reformat the channel dimension from BGR to - RGB. The final image we will save is following RGB style. - Default: True. - nrow (int): The number of samples in a row. Default: 1. - padding (int): The number of padding pixels between each samples. - Default: 4. - """ - - def __init__(self, - output_dir, - res_name_list, - interval=-1, - filename_tmpl='iter_{}.png', - rerange=True, - bgr2rgb=True, - nrow=1, - padding=4): - assert mmcv.is_list_of(res_name_list, str) - self.output_dir = output_dir - self.res_name_list = res_name_list - self.interval = interval - self.filename_tmpl = filename_tmpl - self.bgr2rgb = bgr2rgb - self.rerange = rerange - self.nrow = nrow - self.padding = padding - - mmcv.mkdir_or_exist(self.output_dir) - - @master_only - def after_train_iter(self, runner): - """The behavior after each train iteration. - - Args: - runner (object): The runner. - """ - if not self.every_n_iters(runner, self.interval): - return - results = runner.outputs['results'] - - filename = self.filename_tmpl.format(runner.iter + 1) - - img_list = [x for k, x in results.items() if k in self.res_name_list] - img_cat = torch.cat(img_list, dim=3).detach() - if self.rerange: - img_cat = ((img_cat + 1) / 2) - if self.bgr2rgb: - img_cat = img_cat[:, [2, 1, 0], ...] - img_cat = img_cat.clamp_(0, 1) - save_image( - img_cat, - osp.join(self.output_dir, filename), - nrow=self.nrow, - padding=self.padding) diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/mask.py b/cv/super_resolution/liif/pytorch/mmedit/core/mask.py deleted file mode 100755 index 51486111c..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/mask.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import cv2 -import mmcv -import numpy as np -from PIL import Image, ImageDraw - - -def random_bbox(img_shape, max_bbox_shape, max_bbox_delta=40, min_margin=20): - """Generate a random bbox for the mask on a given image. - - In our implementation, the max value cannot be obtained since we use - `np.random.randint`. And this may be different with other standard scripts - in the community. - - Args: - img_shape (tuple[int]): The size of a image, in the form of (h, w). - max_bbox_shape (int | tuple[int]): Maximum shape of the mask box, - in the form of (h, w). If it is an integer, the mask box will be - square. - max_bbox_delta (int | tuple[int]): Maximum delta of the mask box, - in the form of (delta_h, delta_w). If it is an integer, delta_h - and delta_w will be the same. Mask shape will be randomly sampled - from the range of `max_bbox_shape - max_bbox_delta` and - `max_bbox_shape`. Default: (40, 40). - min_margin (int | tuple[int]): The minimum margin size from the - edges of mask box to the image boarder, in the form of - (margin_h, margin_w). If it is an integer, margin_h and margin_w - will be the same. Default: (20, 20). - - Returns: - tuple[int]: The generated box, (top, left, h, w). - """ - if not isinstance(max_bbox_shape, tuple): - max_bbox_shape = (max_bbox_shape, max_bbox_shape) - if not isinstance(max_bbox_delta, tuple): - max_bbox_delta = (max_bbox_delta, max_bbox_delta) - if not isinstance(min_margin, tuple): - min_margin = (min_margin, min_margin) - assert mmcv.is_tuple_of(max_bbox_shape, int) - assert mmcv.is_tuple_of(max_bbox_delta, int) - assert mmcv.is_tuple_of(min_margin, int) - - img_h, img_w = img_shape[:2] - max_mask_h, max_mask_w = max_bbox_shape - max_delta_h, max_delta_w = max_bbox_delta - margin_h, margin_w = min_margin - - if max_mask_h > img_h or max_mask_w > img_w: - raise ValueError(f'mask shape {max_bbox_shape} should be smaller than ' - f'image shape {img_shape}') - if (max_delta_h // 2 * 2 >= max_mask_h - or max_delta_w // 2 * 2 >= max_mask_w): - raise ValueError(f'mask delta {max_bbox_delta} should be smaller than' - f'mask shape {max_bbox_shape}') - if img_h - max_mask_h < 2 * margin_h or img_w - max_mask_w < 2 * margin_w: - raise ValueError(f'Margin {min_margin} cannot be satisfied for img' - f'shape {img_shape} and mask shape {max_bbox_shape}') - - # get the max value of (top, left) - max_top = img_h - margin_h - max_mask_h - max_left = img_w - margin_w - max_mask_w - # randomly select a (top, left) - top = np.random.randint(margin_h, max_top) - left = np.random.randint(margin_w, max_left) - # randomly shrink the shape of mask box according to `max_bbox_delta` - # the center of box is fixed - delta_top = np.random.randint(0, max_delta_h // 2 + 1) - delta_left = np.random.randint(0, max_delta_w // 2 + 1) - top = top + delta_top - left = left + delta_left - h = max_mask_h - delta_top - w = max_mask_w - delta_left - return (top, left, h, w) - - -def bbox2mask(img_shape, bbox, dtype='uint8'): - """Generate mask in ndarray from bbox. - - The returned mask has the shape of (h, w, 1). '1' indicates the - hole and '0' indicates the valid regions. - - We prefer to use `uint8` as the data type of masks, which may be different - from other codes in the community. - - Args: - img_shape (tuple[int]): The size of the image. - bbox (tuple[int]): Configuration tuple, (top, left, height, width) - dtype (str): Indicate the data type of returned masks. Default: 'uint8' - - Return: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - height, width = img_shape[:2] - - mask = np.zeros((height, width, 1), dtype=dtype) - mask[bbox[0]:bbox[0] + bbox[2], bbox[1]:bbox[1] + bbox[3], :] = 1 - - return mask - - -def brush_stroke_mask(img_shape, - num_vertices=(4, 12), - mean_angle=2 * math.pi / 5, - angle_range=2 * math.pi / 15, - brush_width=(12, 40), - max_loops=4, - dtype='uint8'): - """Generate free-form mask. - - The method of generating free-form mask is in the following paper: - Free-Form Image Inpainting with Gated Convolution. - - When you set the config of this type of mask. You may note the usage of - `np.random.randint` and the range of `np.random.randint` is [left, right). - - We prefer to use `uint8` as the data type of masks, which may be different - from other codes in the community. - - TODO: Rewrite the implementation of this function. - - Args: - img_shape (tuple[int]): Size of the image. - num_vertices (int | tuple[int]): Min and max number of vertices. If - only give an integer, we will fix the number of vertices. - Default: (4, 12). - mean_angle (float): Mean value of the angle in each vertex. The angle - is measured in radians. Default: 2 * math.pi / 5. - angle_range (float): Range of the random angle. - Default: 2 * math.pi / 15. - brush_width (int | tuple[int]): (min_width, max_width). If only give - an integer, we will fix the width of brush. Default: (12, 40). - max_loops (int): The max number of for loops of drawing strokes. - dtype (str): Indicate the data type of returned masks. - Default: 'uint8'. - - Returns: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - img_h, img_w = img_shape[:2] - if isinstance(num_vertices, int): - min_num_vertices, max_num_vertices = num_vertices, num_vertices + 1 - elif isinstance(num_vertices, tuple): - min_num_vertices, max_num_vertices = num_vertices - else: - raise TypeError('The type of num_vertices should be int' - f'or tuple[int], but got type: {num_vertices}') - - if isinstance(brush_width, tuple): - min_width, max_width = brush_width - elif isinstance(brush_width, int): - min_width, max_width = brush_width, brush_width + 1 - else: - raise TypeError('The type of brush_width should be int' - f'or tuple[int], but got type: {brush_width}') - - average_radius = math.sqrt(img_h * img_h + img_w * img_w) / 8 - mask = Image.new('L', (img_w, img_h), 0) - - loop_num = np.random.randint(1, max_loops) - num_vertex_list = np.random.randint( - min_num_vertices, max_num_vertices, size=loop_num) - angle_min_list = np.random.uniform(0, angle_range, size=loop_num) - angle_max_list = np.random.uniform(0, angle_range, size=loop_num) - - for loop_n in range(loop_num): - num_vertex = num_vertex_list[loop_n] - angle_min = mean_angle - angle_min_list[loop_n] - angle_max = mean_angle + angle_max_list[loop_n] - angles = [] - vertex = [] - - # set random angle on each vertex - angles = np.random.uniform(angle_min, angle_max, size=num_vertex) - reverse_mask = (np.arange(num_vertex, dtype=np.float32) % 2) == 0 - angles[reverse_mask] = 2 * math.pi - angles[reverse_mask] - - h, w = mask.size - - # set random vertices - vertex.append((np.random.randint(0, w), np.random.randint(0, h))) - r_list = np.random.normal( - loc=average_radius, scale=average_radius // 2, size=num_vertex) - for i in range(num_vertex): - r = np.clip(r_list[i], 0, 2 * average_radius) - new_x = np.clip(vertex[-1][0] + r * math.cos(angles[i]), 0, w) - new_y = np.clip(vertex[-1][1] + r * math.sin(angles[i]), 0, h) - vertex.append((int(new_x), int(new_y))) - # draw brush strokes according to the vertex and angle list - draw = ImageDraw.Draw(mask) - width = np.random.randint(min_width, max_width) - draw.line(vertex, fill=1, width=width) - for v in vertex: - draw.ellipse((v[0] - width // 2, v[1] - width // 2, - v[0] + width // 2, v[1] + width // 2), - fill=1) - # randomly flip the mask - if np.random.normal() > 0: - mask.transpose(Image.FLIP_LEFT_RIGHT) - if np.random.normal() > 0: - mask.transpose(Image.FLIP_TOP_BOTTOM) - mask = np.array(mask).astype(dtype=getattr(np, dtype)) - mask = mask[:, :, None] - return mask - - -def random_irregular_mask(img_shape, - num_vertices=(4, 8), - max_angle=4, - length_range=(10, 100), - brush_width=(10, 40), - dtype='uint8'): - """Generate random irregular masks. - - This is a modified version of free-form mask implemented in - 'brush_stroke_mask'. - - We prefer to use `uint8` as the data type of masks, which may be different - from other codes in the community. - - TODO: Rewrite the implementation of this function. - - Args: - img_shape (tuple[int]): Size of the image. - num_vertices (int | tuple[int]): Min and max number of vertices. If - only give an integer, we will fix the number of vertices. - Default: (4, 8). - max_angle (float): Max value of angle at each vertex. Default 4.0. - length_range (int | tuple[int]): (min_length, max_length). If only give - an integer, we will fix the length of brush. Default: (10, 100). - brush_width (int | tuple[int]): (min_width, max_width). If only give - an integer, we will fix the width of brush. Default: (10, 40). - dtype (str): Indicate the data type of returned masks. Default: 'uint8' - - Returns: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - h, w = img_shape[:2] - - mask = np.zeros((h, w), dtype=dtype) - if isinstance(length_range, int): - min_length, max_length = length_range, length_range + 1 - elif isinstance(length_range, tuple): - min_length, max_length = length_range - else: - raise TypeError('The type of length_range should be int' - f'or tuple[int], but got type: {length_range}') - if isinstance(num_vertices, int): - min_num_vertices, max_num_vertices = num_vertices, num_vertices + 1 - elif isinstance(num_vertices, tuple): - min_num_vertices, max_num_vertices = num_vertices - else: - raise TypeError('The type of num_vertices should be int' - f'or tuple[int], but got type: {num_vertices}') - - if isinstance(brush_width, int): - min_brush_width, max_brush_width = brush_width, brush_width + 1 - elif isinstance(brush_width, tuple): - min_brush_width, max_brush_width = brush_width - else: - raise TypeError('The type of brush_width should be int' - f'or tuple[int], but got type: {brush_width}') - - num_v = np.random.randint(min_num_vertices, max_num_vertices) - - for i in range(num_v): - start_x = np.random.randint(w) - start_y = np.random.randint(h) - # from the start point, randomly setlect n \in [1, 6] directions. - direction_num = np.random.randint(1, 6) - angle_list = np.random.randint(0, max_angle, size=direction_num) - length_list = np.random.randint( - min_length, max_length, size=direction_num) - brush_width_list = np.random.randint( - min_brush_width, max_brush_width, size=direction_num) - for direct_n in range(direction_num): - angle = 0.01 + angle_list[direct_n] - if i % 2 == 0: - angle = 2 * math.pi - angle - length = length_list[direct_n] - brush_w = brush_width_list[direct_n] - # compute end point according to the random angle - end_x = (start_x + length * np.sin(angle)).astype(np.int32) - end_y = (start_y + length * np.cos(angle)).astype(np.int32) - - cv2.line(mask, (start_y, start_x), (end_y, end_x), 1, brush_w) - start_x, start_y = end_x, end_y - mask = np.expand_dims(mask, axis=2) - - return mask - - -def get_irregular_mask(img_shape, area_ratio_range=(0.15, 0.5), **kwargs): - """Get irregular mask with the constraints in mask ratio - - Args: - img_shape (tuple[int]): Size of the image. - area_ratio_range (tuple(float)): Contain the minimum and maximum area - ratio. Default: (0.15, 0.5). - - Returns: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - mask = random_irregular_mask(img_shape, **kwargs) - min_ratio, max_ratio = area_ratio_range - - while not min_ratio < (np.sum(mask) / - (img_shape[0] * img_shape[1])) < max_ratio: - mask = random_irregular_mask(img_shape, **kwargs) - - return mask diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/misc.py b/cv/super_resolution/liif/pytorch/mmedit/core/misc.py deleted file mode 100755 index 21c5d4770..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/misc.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -from torchvision.utils import make_grid - - -def tensor2img(tensor, out_type=np.uint8, min_max=(0, 1)): - """Convert torch Tensors into image numpy arrays. - - After clamping to (min, max), image values will be normalized to [0, 1]. - - For different tensor shapes, this function will have different behaviors: - - 1. 4D mini-batch Tensor of shape (N x 3/1 x H x W): - Use `make_grid` to stitch images in the batch dimension, and then - convert it to numpy array. - 2. 3D Tensor of shape (3/1 x H x W) and 2D Tensor of shape (H x W): - Directly change to numpy array. - - Note that the image channel in input tensors should be RGB order. This - function will convert it to cv2 convention, i.e., (H x W x C) with BGR - order. - - Args: - tensor (Tensor | list[Tensor]): Input tensors. - out_type (numpy type): Output types. If ``np.uint8``, transform outputs - to uint8 type with range [0, 255]; otherwise, float type with - range [0, 1]. Default: ``np.uint8``. - min_max (tuple): min and max values for clamp. - - Returns: - (Tensor | list[Tensor]): 3D ndarray of shape (H x W x C) or 2D ndarray - of shape (H x W). - """ - if not (torch.is_tensor(tensor) or - (isinstance(tensor, list) - and all(torch.is_tensor(t) for t in tensor))): - raise TypeError( - f'tensor or list of tensors expected, got {type(tensor)}') - - if torch.is_tensor(tensor): - tensor = [tensor] - result = [] - for _tensor in tensor: - # Squeeze two times so that: - # 1. (1, 1, h, w) -> (h, w) or - # 3. (1, 3, h, w) -> (3, h, w) or - # 2. (n>1, 3/1, h, w) -> (n>1, 3/1, h, w) - _tensor = _tensor.squeeze(0).squeeze(0) - _tensor = _tensor.float().detach().cpu().clamp_(*min_max) - _tensor = (_tensor - min_max[0]) / (min_max[1] - min_max[0]) - n_dim = _tensor.dim() - if n_dim == 4: - img_np = make_grid( - _tensor, nrow=int(math.sqrt(_tensor.size(0))), - normalize=False).numpy() - img_np = np.transpose(img_np[[2, 1, 0], :, :], (1, 2, 0)) - elif n_dim == 3: - img_np = _tensor.numpy() - img_np = np.transpose(img_np[[2, 1, 0], :, :], (1, 2, 0)) - elif n_dim == 2: - img_np = _tensor.numpy() - else: - raise ValueError('Only support 4D, 3D or 2D tensor. ' - f'But received with dimension: {n_dim}') - if out_type == np.uint8: - # Unlike MATLAB, numpy.unit8() WILL NOT round by default. - img_np = (img_np * 255.0).round() - img_np = img_np.astype(out_type) - result.append(img_np) - result = result[0] if len(result) == 1 else result - return result diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/optimizer/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/core/optimizer/__init__.py deleted file mode 100755 index e1c477dd1..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/optimizer/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_optimizers - -__all__ = ['build_optimizers'] diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/optimizer/builder.py b/cv/super_resolution/liif/pytorch/mmedit/core/optimizer/builder.py deleted file mode 100755 index 2edf94dad..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/optimizer/builder.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import build_optimizer - - -def build_optimizers(model, cfgs): - """Build multiple optimizers from configs. - - If `cfgs` contains several dicts for optimizers, then a dict for each - constructed optimizers will be returned. - If `cfgs` only contains one optimizer config, the constructed optimizer - itself will be returned. - - For example, - - 1) Multiple optimizer configs: - - .. code-block:: python - - optimizer_cfg = dict( - model1=dict(type='SGD', lr=lr), - model2=dict(type='SGD', lr=lr)) - - The return dict is - ``dict('model1': torch.optim.Optimizer, 'model2': torch.optim.Optimizer)`` - - 2) Single optimizer config: - - .. code-block:: python - - optimizer_cfg = dict(type='SGD', lr=lr) - - The return is ``torch.optim.Optimizer``. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - cfgs (dict): The config dict of the optimizer. - - Returns: - dict[:obj:`torch.optim.Optimizer`] | :obj:`torch.optim.Optimizer`: - The initialized optimizers. - """ - optimizers = {} - if hasattr(model, 'module'): - model = model.module - # determine whether 'cfgs' has several dicts for optimizers - is_dict_of_dict = True - for key, cfg in cfgs.items(): - if not isinstance(cfg, dict): - is_dict_of_dict = False - - if is_dict_of_dict: - for key, cfg in cfgs.items(): - cfg_ = cfg.copy() - module = getattr(model, key) - optimizers[key] = build_optimizer(module, cfg_) - return optimizers - - return build_optimizer(model, cfgs) diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/scheduler/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/core/scheduler/__init__.py deleted file mode 100755 index af0458206..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/scheduler/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .lr_updater import LinearLrUpdaterHook, ReduceLrUpdaterHook - -__all__ = ['LinearLrUpdaterHook', 'ReduceLrUpdaterHook'] diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/scheduler/lr_updater.py b/cv/super_resolution/liif/pytorch/mmedit/core/scheduler/lr_updater.py deleted file mode 100755 index 0677373c6..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/scheduler/lr_updater.py +++ /dev/null @@ -1,304 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import HOOKS, LrUpdaterHook - - -@HOOKS.register_module() -class LinearLrUpdaterHook(LrUpdaterHook): - """Linear learning rate scheduler for image generation. - - In the beginning, the learning rate is 'base_lr' defined in mmcv. - We give a target learning rate 'target_lr' and a start point 'start' - (iteration / epoch). Before 'start', we fix learning rate as 'base_lr'; - After 'start', we linearly update learning rate to 'target_lr'. - - Args: - target_lr (float): The target learning rate. Default: 0. - start (int): The start point (iteration / epoch, specified by args - 'by_epoch' in its parent class in mmcv) to update learning rate. - Default: 0. - interval (int): The interval to update the learning rate. Default: 1. - """ - - def __init__(self, target_lr=0, start=0, interval=1, **kwargs): - super().__init__(**kwargs) - self.target_lr = target_lr - self.start = start - self.interval = interval - - def get_lr(self, runner, base_lr): - """Calculates the learning rate. - - Args: - runner (object): The passed runner. - base_lr (float): Base learning rate. - - Returns: - float: Current learning rate. - """ - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - assert max_progress >= self.start - - if max_progress == self.start: - return base_lr - - # Before 'start', fix lr; After 'start', linearly update lr. - factor = (max(0, progress - self.start) // self.interval) / ( - (max_progress - self.start) // self.interval) - return base_lr + (self.target_lr - base_lr) * factor - - -@HOOKS.register_module() -class ReduceLrUpdaterHook(LrUpdaterHook): - """ReduceLROnPlateau Scheduler. - - Reduce learning rate when a metric has stopped improving. This scheduler - reads a metrics quantity and if no improvement is seen for a 'patience' - number of epochs, the learning rate is reduced. - - Args: - val_metric (str, optional): Metrics to be evaluated. If val_metric is - None, the metrics will be loss value. Default: None. - mode (str, optional): One of `min`, `max`. In `min` mode, lr will - be reduced when the quantity monitored has stopped - decreasing; in `max` mode it will be reduced when the - quantity monitored has stopped increasing. Default: 'min'. - factor (float, optional): Factor by which the learning rate will be - reduced. new_lr = lr * factor. Default: 0.1. - patience (int, optional): Number of epochs with no improvement after - which learning rate will be reduced. For example, if - `patience = 2`, then we will ignore the first 2 epochs - with no improvement, and will only decrease the LR after the - 3rd epoch if the loss still hasn't improved then. - Default: 10. - threshold (float, optional): Threshold for measuring the new optimum, - to only focus on significant changes. Default: 1e-4. - threshold_mode (str, optional): One of `rel`, `abs`. In `rel` mode, - dynamic_threshold = best * ( 1 + threshold ) in 'max' - mode or best * ( 1 - threshold ) in `min` mode. - In `abs` mode, dynamic_threshold = best + threshold in - `max` mode or best - threshold in `min` mode. Default: 'rel'. - cooldown (int, optional): Number of epochs to wait before resuming - normal operation after lr has been reduced. Default: 0. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. - Default: 0. - eps (float, optional): Minimal decay applied to lr. If the difference - between new and old lr is smaller than eps, the update is - ignored. Default: 1e-8. - verbose (bool): If ``True``, prints a message to stdout for - each update. Default: ``False``. - epoch_base_valid (None | Bool): Whether use epoch base valid. - If `None`, follow `by_epoch` (inherited from `LrUpdaterHook`). - Default: None. - """ - - def __init__(self, - val_metric: str = None, - mode: str = 'min', - factor: float = 0.1, - patience: int = 10, - threshold: float = 1e-4, - threshold_mode: str = 'rel', - cooldown: int = 0, - min_lr: float = 0., - eps: float = 1e-8, - verbose: bool = False, - epoch_base_valid=None, - **kwargs): - - self.val_metric = val_metric - - if mode not in ['min', 'max']: - raise ValueError( - 'mode must be one of "min" or "max", instead got {mode}') - self.mode = mode - - if factor >= 1.0 or factor < 0: - raise ValueError('Factor should be < 1.0 and >=0') - self.factor = factor - - self.patience = patience - self.threshold = threshold - - if threshold_mode not in ['rel', 'abs']: - raise ValueError('thresh_mode must be one of "rel" or "abs",' - f'instead got {threshold_mode}') - self.threshold_mode = threshold_mode - - self.cooldown = cooldown - self.cooldown_counter = 0 - self.best = None - self.num_bad_epochs = None - self.mode_worse = None # the worse value for the chosen mode - self.min_lr = min_lr - self.eps = eps - self.verbose = verbose - self.last_epoch = 0 - self._init_is_better(self.mode) - self._reset() - - super().__init__(**kwargs) - if epoch_base_valid is None: - self.epoch_base_valid = self.by_epoch - else: - self.epoch_base_valid = epoch_base_valid - - def get_lr(self, regular_lr, optimizer_name): - if self.num_bad_epochs > self.patience: - self.cooldown_counter = self.cooldown - self.num_bad_epochs = 0 - if regular_lr - regular_lr * self.factor > self.eps: - new_lr = max(regular_lr * self.factor, self.min_lr) - if self.verbose: - print(f'Reducing learning rate of {optimizer_name} from ' - f'{regular_lr:.4e} to {new_lr:.4e}.') - else: - new_lr = regular_lr - return new_lr - else: - return regular_lr - - def get_regular_lr(self, runner): - if not self.regular_lr: - self.regular_lr = self.base_lr - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(_regular_lr, k) - for _regular_lr in self.regular_lr[k] - ] - lr_groups.update({k: _lr_group}) - else: - lr_groups = [ - self.get_lr(_regular_lr, 'generator') - for _regular_lr in self.regular_lr - ] - self.regular_lr = lr_groups - return lr_groups - - def _init_is_better(self, mode): - if mode == 'min': - self.mode_worse = float('inf') - else: - self.mode_worse = float('-inf') - - def _reset(self): - self.best = self.mode_worse - self.cooldown_counter = 0 - self.num_bad_epochs = 0 - - def is_better(self, a, best): - if self.mode == 'min' and self.threshold_mode == 'rel': - rel_epsilon = 1. - self.threshold - return a < best * rel_epsilon - elif self.mode == 'min' and self.threshold_mode == 'abs': - return a < best - self.threshold - elif self.mode == 'max' and self.threshold_mode == 'rel': - rel_epsilon = 1. + self.threshold - return a > best * rel_epsilon - else: - return a > best + self.threshold - - @property - def in_cooldown(self): - return self.cooldown_counter > 0 - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - cur_epoch = runner.epoch - if self.warmup is not None and self.warmup_by_epoch: - if cur_epoch <= self.warmup_epochs: - return - # If val_metric is None, we check training loss to reduce learning - # rate. - if self.val_metric is None: - current = runner.outputs['log_vars']['loss'] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'train_epoch--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') - - def after_train_iter(self, runner): - if self.by_epoch: - return - cur_iter = runner.iter - if self.warmup_epochs is not None and cur_iter <= self.warmup_iters: - return - # If val_metric is None, we check training loss to reduce learning - # rate. - if self.val_metric is None: - current = runner.outputs['log_vars']['loss'] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'train_iter--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') - - def after_val_epoch(self, runner): - if not self.by_epoch and not self.epoch_base_valid: - return - cur_epoch = runner.epoch - if self.warmup is not None and self.warmup_by_epoch: - if cur_epoch <= self.warmup_epochs: - return - # If val_metric is not None, we check its value to reduce learning - # rate. - if self.val_metric is not None: - current = runner.log_buffer.output[self.val_metric] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'val_epoch--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') - - def after_val_iter(self, runner): - if self.by_epoch or self.epoch_base_valid: - return - cur_iter = runner.iter - if self.warmup_epochs is not None and cur_iter <= self.warmup_iters: - return - # If val_metric is not None, we check its value to reduce learning - # rate. - if self.val_metric is not None: - current = runner.eval_result[self.val_metric] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'val_iter--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/utils/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/core/utils/__init__.py deleted file mode 100755 index f894e827c..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/utils/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .dist_utils import sync_random_seed - -__all__ = ['sync_random_seed'] diff --git a/cv/super_resolution/liif/pytorch/mmedit/core/utils/dist_utils.py b/cv/super_resolution/liif/pytorch/mmedit/core/utils/dist_utils.py deleted file mode 100755 index 8a3af5bb0..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/core/utils/dist_utils.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def sync_random_seed(seed=None, device='cuda'): - """Make sure different ranks share the same seed. - All workers must call this function, otherwise it will deadlock. - This method is generally used in `DistributedSampler`, - because the seed should be identical across all processes - in the distributed group. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is None: - seed = np.random.randint(2**31) - assert isinstance(seed, int) - - rank, world_size = get_dist_info() - - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/__init__.py deleted file mode 100755 index da693239e..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .base_dataset import BaseDataset -from .base_sr_dataset import BaseSRDataset -from .builder import build_dataloader, build_dataset -from .dataset_wrappers import RepeatDataset -from .registry import DATASETS, PIPELINES -from .sr_folder_gt_dataset import SRFolderGTDataset -from .sr_folder_dataset import SRFolderDataset - - -__all__ = [ - 'DATASETS', 'PIPELINES', 'build_dataset', 'build_dataloader', - 'BaseDataset', 'BaseSRDataset', 'RepeatDataset', 'SRFolderGTDataset', 'SRFolderDataset' -] diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/base_dataset.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/base_dataset.py deleted file mode 100755 index c8ffea6eb..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/base_dataset.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -from abc import ABCMeta, abstractmethod - -from torch.utils.data import Dataset - -from .pipelines import Compose - - -class BaseDataset(Dataset, metaclass=ABCMeta): - """Base class for datasets. - - All datasets should subclass it. - All subclasses should overwrite: - - ``load_annotations``, supporting to load information and generate - image lists. - - Args: - pipeline (list[dict | callable]): A sequence of data transforms. - test_mode (bool): If True, the dataset will work in test mode. - Otherwise, in train mode. - """ - - def __init__(self, pipeline, test_mode=False): - super().__init__() - self.test_mode = test_mode - self.pipeline = Compose(pipeline) - - @abstractmethod - def load_annotations(self): - """Abstract function for loading annotation. - - All subclasses should overwrite this function - """ - - def prepare_train_data(self, idx): - """Prepare training data. - - Args: - idx (int): Index of the training batch data. - - Returns: - dict: Returned training batch. - """ - results = copy.deepcopy(self.data_infos[idx]) - return self.pipeline(results) - - def prepare_test_data(self, idx): - """Prepare testing data. - - Args: - idx (int): Index for getting each testing batch. - - Returns: - Tensor: Returned testing batch. - """ - results = copy.deepcopy(self.data_infos[idx]) - return self.pipeline(results) - - def __len__(self): - """Length of the dataset. - - Returns: - int: Length of the dataset. - """ - return len(self.data_infos) - - def __getitem__(self, idx): - """Get item at each call. - - Args: - idx (int): Index for getting each item. - """ - if self.test_mode: - return self.prepare_test_data(idx) - - return self.prepare_train_data(idx) diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/base_sr_dataset.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/base_sr_dataset.py deleted file mode 100755 index 6e8115056..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/base_sr_dataset.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import os.path as osp -from collections import defaultdict -from pathlib import Path - -from mmcv import scandir - -from .base_dataset import BaseDataset - -IMG_EXTENSIONS = ('.jpg', '.JPG', '.jpeg', '.JPEG', '.png', '.PNG', '.ppm', - '.PPM', '.bmp', '.BMP', '.tif', '.TIF', '.tiff', '.TIFF') - - -class BaseSRDataset(BaseDataset): - """Base class for super resolution datasets. - """ - - def __init__(self, pipeline, scale, test_mode=False): - super().__init__(pipeline, test_mode) - self.scale = scale - - @staticmethod - def scan_folder(path): - """Obtain image path list (including sub-folders) from a given folder. - - Args: - path (str | :obj:`Path`): Folder path. - - Returns: - list[str]: image list obtained form given folder. - """ - - if isinstance(path, (str, Path)): - path = str(path) - else: - raise TypeError("'path' must be a str or a Path object, " - f'but received {type(path)}.') - - images = list(scandir(path, suffix=IMG_EXTENSIONS, recursive=True)) - images = [osp.join(path, v) for v in images] - assert images, f'{path} has no valid image file.' - return images - - def __getitem__(self, idx): - """Get item at each call. - - Args: - idx (int): Index for getting each item. - """ - results = copy.deepcopy(self.data_infos[idx]) - results['scale'] = self.scale - return self.pipeline(results) - - def evaluate(self, results, logger=None): - """Evaluate with different metrics. - - Args: - results (list[tuple]): The output of forward_test() of the model. - - Return: - dict: Evaluation results dict. - """ - if not isinstance(results, list): - raise TypeError(f'results must be a list, but got {type(results)}') - assert len(results) == len(self), ( - 'The length of results is not equal to the dataset len: ' - f'{len(results)} != {len(self)}') - - results = [res['eval_result'] for res in results] # a list of dict - eval_result = defaultdict(list) # a dict of list - - for res in results: - for metric, val in res.items(): - eval_result[metric].append(val) - for metric, val_list in eval_result.items(): - assert len(val_list) == len(self), ( - f'Length of evaluation result of {metric} is {len(val_list)}, ' - f'should be {len(self)}') - - # average the results - eval_result = { - metric: sum(values) / len(self) - for metric, values in eval_result.items() - } - - return eval_result diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/builder.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/builder.py deleted file mode 100755 index 4414d375e..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/builder.py +++ /dev/null @@ -1,181 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import platform -import random -from functools import partial - -import numpy as np -import torch -from mmcv.parallel import collate -from mmcv.runner import get_dist_info -from mmcv.utils import build_from_cfg -from packaging import version -from torch.utils.data import ConcatDataset, DataLoader - -from .dataset_wrappers import RepeatDataset -from .registry import DATASETS -from .samplers import DistributedSampler - -if platform.system() != 'Windows': - # https://github.com/pytorch/pytorch/issues/973 - import resource - rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) - base_soft_limit = rlimit[0] - hard_limit = rlimit[1] - soft_limit = min(max(4096, base_soft_limit), hard_limit) - resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) - - -def _concat_dataset(cfg, default_args=None): - """Concat datasets with different ann_file but the same type. - - Args: - cfg (dict): The config of dataset. - default_args (dict, optional): Default initialization arguments. - Default: None. - - Returns: - Dataset: The concatenated dataset. - """ - ann_files = cfg['ann_file'] - - datasets = [] - num_dset = len(ann_files) - for i in range(num_dset): - data_cfg = copy.deepcopy(cfg) - data_cfg['ann_file'] = ann_files[i] - datasets.append(build_dataset(data_cfg, default_args)) - - return ConcatDataset(datasets) - - -def build_dataset(cfg, default_args=None): - """Build a dataset from config dict. - - It supports a variety of dataset config. If ``cfg`` is a Sequential (list - or dict), it will be a concatenated dataset of the datasets specified by - the Sequential. If it is a ``RepeatDataset``, then it will repeat the - dataset ``cfg['dataset']`` for ``cfg['times']`` times. If the ``ann_file`` - of the dataset is a Sequential, then it will build a concatenated dataset - with the same dataset type but different ``ann_file``. - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - default_args (dict, optional): Default initialization arguments. - Default: None. - - Returns: - Dataset: The constructed dataset. - """ - if isinstance(cfg, (list, tuple)): - dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) - elif cfg['type'] == 'RepeatDataset': - dataset = RepeatDataset( - build_dataset(cfg['dataset'], default_args), cfg['times']) - elif isinstance(cfg.get('ann_file'), (list, tuple)): - dataset = _concat_dataset(cfg, default_args) - else: - dataset = build_from_cfg(cfg, DATASETS, default_args) - - return dataset - - -def build_dataloader(dataset, - samples_per_gpu, - workers_per_gpu, - num_gpus=1, - dist=True, - shuffle=True, - seed=None, - drop_last=False, - pin_memory=True, - persistent_workers=True, - **kwargs): - """Build PyTorch DataLoader. - - In distributed training, each GPU/process has a dataloader. - In non-distributed training, there is only one dataloader for all GPUs. - - Args: - dataset (:obj:`Dataset`): A PyTorch dataset. - samples_per_gpu (int): Number of samples on each GPU, i.e., - batch size of each GPU. - workers_per_gpu (int): How many subprocesses to use for data - loading for each GPU. - num_gpus (int): Number of GPUs. Only used in non-distributed - training. Default: 1. - dist (bool): Distributed training/test or not. Default: True. - shuffle (bool): Whether to shuffle the data at every epoch. - Default: True. - seed (int | None): Seed to be used. Default: None. - drop_last (bool): Whether to drop the last incomplete batch in epoch. - Default: False - pin_memory (bool): Whether to use pin_memory in DataLoader. - Default: True - persistent_workers (bool): If True, the data loader will not shutdown - the worker processes after a dataset has been consumed once. - This allows to maintain the workers Dataset instances alive. - The argument also has effect in PyTorch>=1.7.0. - Default: True - kwargs (dict, optional): Any keyword argument to be used to initialize - DataLoader. - - Returns: - DataLoader: A PyTorch dataloader. - """ - rank, world_size = get_dist_info() - if dist: - sampler = DistributedSampler( - dataset, - world_size, - rank, - shuffle=shuffle, - samples_per_gpu=samples_per_gpu, - seed=seed) - shuffle = False - batch_size = samples_per_gpu - num_workers = workers_per_gpu - else: - sampler = None - batch_size = num_gpus * samples_per_gpu - num_workers = num_gpus * workers_per_gpu - - init_fn = partial( - worker_init_fn, num_workers=num_workers, rank=rank, - seed=seed) if seed is not None else None - - if version.parse(torch.__version__) >= version.parse('1.7.0'): - kwargs['persistent_workers'] = persistent_workers - - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - **kwargs) - - return data_loader - - -def worker_init_fn(worker_id, num_workers, rank, seed): - """Function to initialize each worker. - - The seed of each worker equals to - ``num_worker * rank + worker_id + user_seed``. - - Args: - worker_id (int): Id for each worker. - num_workers (int): Number of workers. - rank (int): Rank in distributed training. - seed (int): Random seed. - """ - - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/dataset_wrappers.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/dataset_wrappers.py deleted file mode 100755 index 3dbca31ea..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/dataset_wrappers.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import DATASETS - - -@DATASETS.register_module() -class RepeatDataset: - """A wrapper of repeated dataset. - - The length of repeated dataset will be `times` larger than the original - dataset. This is useful when the data loading time is long but the dataset - is small. Using RepeatDataset can reduce the data loading time between - epochs. - - Args: - dataset (:obj:`Dataset`): The dataset to be repeated. - times (int): Repeat times. - """ - - def __init__(self, dataset, times): - self.dataset = dataset - self.times = times - - self._ori_len = len(self.dataset) - - def __getitem__(self, idx): - """Get item at each call. - - Args: - idx (int): Index for getting each item. - """ - return self.dataset[idx % self._ori_len] - - def __len__(self): - """Length of the dataset. - - Returns: - int: Length of the dataset. - """ - return self.times * self._ori_len diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/__init__.py deleted file mode 100755 index 8d8000de7..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .augmentation import (BinarizeImage, ColorJitter, CopyValues, Flip, - GenerateFrameIndices, - GenerateFrameIndiceswithPadding, - GenerateSegmentIndices, MirrorSequence, Pad, - Quantize, RandomAffine, RandomJitter, - RandomMaskDilation, RandomTransposeHW, Resize, - TemporalReverse, UnsharpMasking) -from .compose import Compose -from .formating import (Collect, FormatTrimap, GetMaskedImage, ImageToTensor, - ToTensor) -from .generate_assistant import GenerateCoordinateAndCell, GenerateHeatmap -from .loading import (GetSpatialDiscountMask, LoadImageFromFile, - LoadImageFromFileList, LoadMask, LoadPairedImageFromFile, - RandomLoadResizeBg) -from .normalization import Normalize, RescaleToZeroOne -from .random_down_sampling import RandomDownSampling - -__all__ = [ - 'Collect', 'FormatTrimap', 'LoadImageFromFile', 'LoadMask', - 'RandomLoadResizeBg', 'Compose', 'ImageToTensor', 'ToTensor', - 'GetMaskedImage', 'BinarizeImage', 'Flip', 'Pad', 'RandomAffine', - 'RandomJitter', 'ColorJitter', 'RandomMaskDilation', 'RandomTransposeHW', - 'Resize', - 'Normalize', - 'RescaleToZeroOne', - 'TemporalReverse', 'LoadImageFromFileList', 'GenerateFrameIndices', - 'GenerateFrameIndiceswithPadding', 'LoadPairedImageFromFile', - 'GetSpatialDiscountMask', 'RandomDownSampling', - 'GenerateCoordinateAndCell', 'GenerateSegmentIndices', 'MirrorSequence', - 'GenerateHeatmap', 'CopyValues', - 'Quantize', 'UnsharpMasking', -] diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/augmentation.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/augmentation.py deleted file mode 100755 index e16f80a1c..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/augmentation.py +++ /dev/null @@ -1,1332 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import numbers -import os -import os.path as osp -import random - -import cv2 -import mmcv -import numpy as np -import torch -import torchvision.transforms as transforms -from PIL import Image - -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class Resize: - """Resize data to a specific size for training or resize the images to fit - the network input regulation for testing. - - When used for resizing images to fit network input regulation, the case is - that a network may have several downsample and then upsample operation, - then the input height and width should be divisible by the downsample - factor of the network. - For example, the network would downsample the input for 5 times with - stride 2, then the downsample factor is 2^5 = 32 and the height - and width should be divisible by 32. - - Required keys are the keys in attribute "keys", added or modified keys are - "keep_ratio", "scale_factor", "interpolation" and the - keys in attribute "keys". - - All keys in "keys" should have the same shape. "test_trans" is used to - record the test transformation to align the input's shape. - - Args: - keys (list[str]): The images to be resized. - scale (float | tuple[int]): If scale is tuple[int], target spatial - size (h, w). Otherwise, target spatial size is scaled by input - size. - Note that when it is used, `size_factor` and `max_size` are - useless. Default: None - keep_ratio (bool): If set to True, images will be resized without - changing the aspect ratio. Otherwise, it will resize images to a - given size. Default: False. - Note that it is used togher with `scale`. - size_factor (int): Let the output shape be a multiple of size_factor. - Default:None. - Note that when it is used, `scale` should be set to None and - `keep_ratio` should be set to False. - max_size (int): The maximum size of the longest side of the output. - Default:None. - Note that it is used togher with `size_factor`. - interpolation (str): Algorithm used for interpolation: - "nearest" | "bilinear" | "bicubic" | "area" | "lanczos". - Default: "bilinear". - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. - Default: None. - output_keys (list[str] | None): The resized images. Default: None - Note that if it is not `None`, its length should be equal to keys. - """ - - def __init__(self, - keys, - scale=None, - keep_ratio=False, - size_factor=None, - max_size=None, - interpolation='bilinear', - backend=None, - output_keys=None): - assert keys, 'Keys should not be empty.' - if output_keys: - assert len(output_keys) == len(keys) - else: - output_keys = keys - if size_factor: - assert scale is None, ('When size_factor is used, scale should ', - f'be None. But received {scale}.') - assert keep_ratio is False, ('When size_factor is used, ' - 'keep_ratio should be False.') - if max_size: - assert size_factor is not None, ( - 'When max_size is used, ' - f'size_factor should also be set. But received {size_factor}.') - if isinstance(scale, float): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - elif mmcv.is_tuple_of(scale, int): - max_long_edge = max(scale) - max_short_edge = min(scale) - if max_short_edge == -1: - # assign np.inf to long edge for rescaling short edge later. - scale = (np.inf, max_long_edge) - elif scale is not None: - raise TypeError( - f'Scale must be None, float or tuple of int, but got ' - f'{type(scale)}.') - self.keys = keys - self.output_keys = output_keys - self.scale = scale - self.size_factor = size_factor - self.max_size = max_size - self.keep_ratio = keep_ratio - self.interpolation = interpolation - self.backend = backend - - def _resize(self, img): - if self.keep_ratio: - img, self.scale_factor = mmcv.imrescale( - img, - self.scale, - return_scale=True, - interpolation=self.interpolation, - backend=self.backend) - else: - img, w_scale, h_scale = mmcv.imresize( - img, - self.scale, - return_scale=True, - interpolation=self.interpolation, - backend=self.backend) - self.scale_factor = np.array((w_scale, h_scale), dtype=np.float32) - return img - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - if self.size_factor: - h, w = results[self.keys[0]].shape[:2] - new_h = h - (h % self.size_factor) - new_w = w - (w % self.size_factor) - if self.max_size: - new_h = min(self.max_size - (self.max_size % self.size_factor), - new_h) - new_w = min(self.max_size - (self.max_size % self.size_factor), - new_w) - self.scale = (new_w, new_h) - for key, out_key in zip(self.keys, self.output_keys): - results[out_key] = self._resize(results[key]) - if len(results[out_key].shape) == 2: - results[out_key] = np.expand_dims(results[out_key], axis=2) - - results['scale_factor'] = self.scale_factor - results['keep_ratio'] = self.keep_ratio - results['interpolation'] = self.interpolation - results['backend'] = self.backend - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(keys={self.keys}, output_keys={self.output_keys}, ' - f'scale={self.scale}, ' - f'keep_ratio={self.keep_ratio}, size_factor={self.size_factor}, ' - f'max_size={self.max_size}, interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class RandomRotation: - """Rotate the image by a randomly-chosen angle, measured in degree. - - Args: - keys (list[str]): The images to be rotated. - degrees (tuple[float] | tuple[int] | float | int): If it is a tuple, - it represents a range (min, max). If it is a float or int, - the range is constructed as (-degrees, degrees). - """ - - def __init__(self, keys, degrees): - if isinstance(degrees, (int, float)): - if degrees < 0.0: - raise ValueError('Degrees must be positive if it is a number.') - else: - degrees = (-degrees, degrees) - elif not mmcv.is_tuple_of(degrees, (int, float)): - raise TypeError(f'Degrees must be float | int or tuple of float | ' - 'int, but got ' - f'{type(degrees)}.') - - self.keys = keys - self.degrees = degrees - - def __call__(self, results): - angle = random.uniform(self.degrees[0], self.degrees[1]) - - for k in self.keys: - results[k] = mmcv.imrotate(results[k], angle) - if results[k].ndim == 2: - results[k] = np.expand_dims(results[k], axis=2) - results['degrees'] = self.degrees - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, degrees={self.degrees})') - return repr_str - - -@PIPELINES.register_module() -class Flip: - """Flip the input data with a probability. - - Reverse the order of elements in the given data with a specific direction. - The shape of the data is preserved, but the elements are reordered. - Required keys are the keys in attributes "keys", added or modified keys are - "flip", "flip_direction" and the keys in attributes "keys". - It also supports flipping a list of images with the same flip. - - Args: - keys (list[str]): The images to be flipped. - flip_ratio (float): The propability to flip the images. - direction (str): Flip images horizontally or vertically. Options are - "horizontal" | "vertical". Default: "horizontal". - """ - _directions = ['horizontal', 'vertical'] - - def __init__(self, keys, flip_ratio=0.5, direction='horizontal'): - if direction not in self._directions: - raise ValueError(f'Direction {direction} is not supported.' - f'Currently support ones are {self._directions}') - self.keys = keys - self.flip_ratio = flip_ratio - self.direction = direction - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - flip = np.random.random() < self.flip_ratio - - if flip: - for key in self.keys: - if isinstance(results[key], list): - for v in results[key]: - mmcv.imflip_(v, self.direction) - else: - mmcv.imflip_(results[key], self.direction) - - results['flip'] = flip - results['flip_direction'] = self.direction - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, flip_ratio={self.flip_ratio}, ' - f'direction={self.direction})') - return repr_str - - -@PIPELINES.register_module() -class Pad: - """Pad the images to align with network downsample factor for testing. - - See `Reshape` for more explanation. `numpy.pad` is used for the pad - operation. - Required keys are the keys in attribute "keys", added or - modified keys are "test_trans" and the keys in attribute - "keys". All keys in "keys" should have the same shape. "test_trans" is used - to record the test transformation to align the input's shape. - - Args: - keys (list[str]): The images to be padded. - ds_factor (int): Downsample factor of the network. The height and - weight will be padded to a multiple of ds_factor. Default: 32. - kwargs (option): any keyword arguments to be passed to `numpy.pad`. - """ - - def __init__(self, keys, ds_factor=32, **kwargs): - self.keys = keys - self.ds_factor = ds_factor - self.kwargs = kwargs - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - h, w = results[self.keys[0]].shape[:2] - - new_h = self.ds_factor * ((h - 1) // self.ds_factor + 1) - new_w = self.ds_factor * ((w - 1) // self.ds_factor + 1) - - pad_h = new_h - h - pad_w = new_w - w - if new_h != h or new_w != w: - pad_width = ((0, pad_h), (0, pad_w), (0, 0)) - for key in self.keys: - results[key] = np.pad(results[key], - pad_width[:results[key].ndim], - **self.kwargs) - results['pad'] = (pad_h, pad_w) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - kwargs_str = ', '.join( - [f'{key}={val}' for key, val in self.kwargs.items()]) - repr_str += (f'(keys={self.keys}, ds_factor={self.ds_factor}, ' - f'{kwargs_str})') - return repr_str - - -@PIPELINES.register_module() -class RandomAffine: - """Apply random affine to input images. - - This class is adopted from - https://github.com/pytorch/vision/blob/v0.5.0/torchvision/transforms/ - transforms.py#L1015 - It should be noted that in - https://github.com/Yaoyi-Li/GCA-Matting/blob/master/dataloader/ - data_generator.py#L70 - random flip is added. See explanation of `flip_ratio` below. - Required keys are the keys in attribute "keys", modified keys - are keys in attribute "keys". - - Args: - keys (Sequence[str]): The images to be affined. - degrees (float | tuple[float]): Range of degrees to select from. If it - is a float instead of a tuple like (min, max), the range of degrees - will be (-degrees, +degrees). Set to 0 to deactivate rotations. - translate (tuple, optional): Tuple of maximum absolute fraction for - horizontal and vertical translations. For example translate=(a, b), - then horizontal shift is randomly sampled in the range - -img_width * a < dx < img_width * a and vertical shift is randomly - sampled in the range -img_height * b < dy < img_height * b. - Default: None. - scale (tuple, optional): Scaling factor interval, e.g (a, b), then - scale is randomly sampled from the range a <= scale <= b. - Default: None. - shear (float | tuple[float], optional): Range of shear degrees to - select from. If shear is a float, a shear parallel to the x axis - and a shear parallel to the y axis in the range (-shear, +shear) - will be applied. Else if shear is a tuple of 2 values, a x-axis - shear and a y-axis shear in (shear[0], shear[1]) will be applied. - Default: None. - flip_ratio (float, optional): Probability of the image being flipped. - The flips in horizontal direction and vertical direction are - independent. The image may be flipped in both directions. - Default: None. - """ - - def __init__(self, - keys, - degrees, - translate=None, - scale=None, - shear=None, - flip_ratio=None): - self.keys = keys - if isinstance(degrees, numbers.Number): - assert degrees >= 0, ('If degrees is a single number, ' - 'it must be positive.') - self.degrees = (-degrees, degrees) - else: - assert isinstance(degrees, tuple) and len(degrees) == 2, \ - 'degrees should be a tuple and it must be of length 2.' - self.degrees = degrees - - if translate is not None: - assert isinstance(translate, tuple) and len(translate) == 2, \ - 'translate should be a tuple and it must be of length 2.' - for t in translate: - assert 0.0 <= t <= 1.0, ('translation values should be ' - 'between 0 and 1.') - self.translate = translate - - if scale is not None: - assert isinstance(scale, tuple) and len(scale) == 2, \ - 'scale should be a tuple and it must be of length 2.' - for s in scale: - assert s > 0, 'scale values should be positive.' - self.scale = scale - - if shear is not None: - if isinstance(shear, numbers.Number): - assert shear >= 0, ('If shear is a single number, ' - 'it must be positive.') - self.shear = (-shear, shear) - else: - assert isinstance(shear, tuple) and len(shear) == 2, \ - 'shear should be a tuple and it must be of length 2.' - # X-Axis and Y-Axis shear with (min, max) - self.shear = shear - else: - self.shear = shear - - if flip_ratio is not None: - assert isinstance(flip_ratio, - float), 'flip_ratio should be a float.' - self.flip_ratio = flip_ratio - else: - self.flip_ratio = 0 - - @staticmethod - def _get_params(degrees, translate, scale_ranges, shears, flip_ratio, - img_size): - """Get parameters for affine transformation. - - Returns: - paras (tuple): Params to be passed to the affine transformation. - """ - angle = np.random.uniform(degrees[0], degrees[1]) - if translate is not None: - max_dx = translate[0] * img_size[0] - max_dy = translate[1] * img_size[1] - translations = (np.round(np.random.uniform(-max_dx, max_dx)), - np.round(np.random.uniform(-max_dy, max_dy))) - else: - translations = (0, 0) - - if scale_ranges is not None: - scale = (np.random.uniform(scale_ranges[0], scale_ranges[1]), - np.random.uniform(scale_ranges[0], scale_ranges[1])) - else: - scale = (1.0, 1.0) - - if shears is not None: - shear = np.random.uniform(shears[0], shears[1]) - else: - shear = 0.0 - - # Because `flip` is used as a multiplier in line 479 and 480, - # so -1 stands for flip and 1 stands for no flip. Thus `flip` - # should be an 'inverse' flag as the result of the comparison. - # See https://github.com/open-mmlab/mmediting/pull/799 for more detail - flip = (np.random.rand(2) > flip_ratio).astype(np.int32) * 2 - 1 - - return angle, translations, scale, shear, flip - - @staticmethod - def _get_inverse_affine_matrix(center, angle, translate, scale, shear, - flip): - """Helper method to compute inverse matrix for affine transformation. - - As it is explained in PIL.Image.rotate, we need compute INVERSE of - affine transformation matrix: M = T * C * RSS * C^-1 where - T is translation matrix: - [1, 0, tx | 0, 1, ty | 0, 0, 1]; - C is translation matrix to keep center: - [1, 0, cx | 0, 1, cy | 0, 0, 1]; - RSS is rotation with scale and shear matrix. - - It is different from the original function in torchvision. - 1. The order are changed to flip -> scale -> rotation -> shear. - 2. x and y have different scale factors. - RSS(shear, a, scale, f) = - [ cos(a + shear)*scale_x*f -sin(a + shear)*scale_y 0] - [ sin(a)*scale_x*f cos(a)*scale_y 0] - [ 0 0 1] - Thus, the inverse is M^-1 = C * RSS^-1 * C^-1 * T^-1. - """ - - angle = math.radians(angle) - shear = math.radians(shear) - scale_x = 1.0 / scale[0] * flip[0] - scale_y = 1.0 / scale[1] * flip[1] - - # Inverted rotation matrix with scale and shear - d = math.cos(angle + shear) * math.cos(angle) + math.sin( - angle + shear) * math.sin(angle) - matrix = [ - math.cos(angle) * scale_x, - math.sin(angle + shear) * scale_x, 0, -math.sin(angle) * scale_y, - math.cos(angle + shear) * scale_y, 0 - ] - matrix = [m / d for m in matrix] - - # Apply inverse of translation and of center translation: - # RSS^-1 * C^-1 * T^-1 - matrix[2] += matrix[0] * (-center[0] - translate[0]) + matrix[1] * ( - -center[1] - translate[1]) - matrix[5] += matrix[3] * (-center[0] - translate[0]) + matrix[4] * ( - -center[1] - translate[1]) - - # Apply center translation: C * RSS^-1 * C^-1 * T^-1 - matrix[2] += center[0] - matrix[5] += center[1] - - return matrix - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - h, w = results[self.keys[0]].shape[:2] - # if image is too small, set degree to 0 to reduce introduced dark area - if np.maximum(h, w) < 1024: - params = self._get_params((0, 0), self.translate, self.scale, - self.shear, self.flip_ratio, (h, w)) - else: - params = self._get_params(self.degrees, self.translate, self.scale, - self.shear, self.flip_ratio, (h, w)) - - center = (w * 0.5 - 0.5, h * 0.5 - 0.5) - M = self._get_inverse_affine_matrix(center, *params) - M = np.array(M).reshape((2, 3)) - - for key in self.keys: - results[key] = cv2.warpAffine( - results[key], - M, (w, h), - flags=cv2.INTER_NEAREST + cv2.WARP_INVERSE_MAP) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, degrees={self.degrees}, ' - f'translate={self.translate}, scale={self.scale}, ' - f'shear={self.shear}, flip_ratio={self.flip_ratio})') - return repr_str - - -@PIPELINES.register_module() -class RandomJitter: - """Randomly jitter the foreground in hsv space. - - The jitter range of hue is adjustable while the jitter ranges of saturation - and value are adaptive to the images. Side effect: the "fg" image will be - converted to `np.float32`. - Required keys are "fg" and "alpha", modified key is "fg". - - Args: - hue_range (float | tuple[float]): Range of hue jittering. If it is a - float instead of a tuple like (min, max), the range of hue - jittering will be (-hue_range, +hue_range). Default: 40. - """ - - def __init__(self, hue_range=40): - if isinstance(hue_range, numbers.Number): - assert hue_range >= 0, ('If hue_range is a single number, ' - 'it must be positive.') - self.hue_range = (-hue_range, hue_range) - else: - assert isinstance(hue_range, tuple) and len(hue_range) == 2, \ - 'hue_range should be a tuple and it must be of length 2.' - self.hue_range = hue_range - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - fg, alpha = results['fg'], results['alpha'] - - # convert to HSV space; - # convert to float32 image to keep precision during space conversion. - fg = mmcv.bgr2hsv(fg.astype(np.float32) / 255) - # Hue noise - hue_jitter = np.random.randint(self.hue_range[0], self.hue_range[1]) - fg[:, :, 0] = np.remainder(fg[:, :, 0] + hue_jitter, 360) - - # Saturation noise - sat_mean = fg[:, :, 1][alpha > 0].mean() - # jitter saturation within range (1.1 - sat_mean) * [-0.1, 0.1] - sat_jitter = (1.1 - sat_mean) * (np.random.rand() * 0.2 - 0.1) - sat = fg[:, :, 1] - sat = np.abs(sat + sat_jitter) - sat[sat > 1] = 2 - sat[sat > 1] - fg[:, :, 1] = sat - - # Value noise - val_mean = fg[:, :, 2][alpha > 0].mean() - # jitter value within range (1.1 - val_mean) * [-0.1, 0.1] - val_jitter = (1.1 - val_mean) * (np.random.rand() * 0.2 - 0.1) - val = fg[:, :, 2] - val = np.abs(val + val_jitter) - val[val > 1] = 2 - val[val > 1] - fg[:, :, 2] = val - # convert back to BGR space - fg = mmcv.hsv2bgr(fg) - results['fg'] = fg * 255 - - return results - - def __repr__(self): - return self.__class__.__name__ + f'hue_range={self.hue_range}' - - -@PIPELINES.register_module() -class ColorJitter: - """An interface for torch color jitter so that it can be invoked in - mmediting pipeline. - - Randomly change the brightness, contrast and saturation of an image. - Modified keys are the attributes specified in "keys". - - Args: - keys (list[str]): The images to be resized. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'rgb'. - - Notes: ``**kwards`` follows the args list of - ``torchvision.transforms.ColorJitter``. - - brightness (float or tuple of float (min, max)): How much to jitter - brightness. brightness_factor is chosen uniformly from - [max(0, 1 - brightness), 1 + brightness] or the given [min, max]. - Should be non negative numbers. - contrast (float or tuple of float (min, max)): How much to jitter - contrast. contrast_factor is chosen uniformly from - [max(0, 1 - contrast), 1 + contrast] or the given [min, max]. - Should be non negative numbers. - saturation (float or tuple of float (min, max)): How much to jitter - saturation. saturation_factor is chosen uniformly from - [max(0, 1 - saturation), 1 + saturation] or the given [min, max]. - Should be non negative numbers. - hue (float or tuple of float (min, max)): How much to jitter hue. - hue_factor is chosen uniformly from [-hue, hue] or the given - [min, max]. - Should have 0<= hue <= 0.5 or -0.5 <= min <= max <= 0.5. - """ - - def __init__(self, keys, channel_order='rgb', **kwargs): - assert keys, 'Keys should not be empty.' - assert 'to_rgb' not in kwargs, ( - '`to_rgb` is not support in ColorJitter, ' - "which is replaced by `channel_order` ('rgb' or 'bgr')") - - self.keys = keys - self.channel_order = channel_order - self.transform = transforms.ColorJitter(**kwargs) - - def _color_jitter(self, image, this_seed): - - if self.channel_order.lower() == 'bgr': - image = image[..., ::-1] - - image = Image.fromarray(image) - torch.manual_seed(this_seed) - image = self.transform(image) - image = np.asarray(image) - - if self.channel_order.lower() == 'bgr': - image = image[..., ::-1] - - return image - - def __call__(self, results): - - this_seed = random.randint(0, 2**32) - - for k in self.keys: - if isinstance(results[k], list): - results[k] = [ - self._color_jitter(v, this_seed) for v in results[k] - ] - else: - results[k] = self._color_jitter(results[k], this_seed) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, channel_order={self.channel_order}, ' - f'brightness={self.transform.brightness}, ' - f'contrast={self.transform.contrast}, ' - f'saturation={self.transform.saturation}, ' - f'hue={self.transform.hue})') - - return repr_str - - -class BinarizeImage: - """Binarize image. - - Args: - keys (Sequence[str]): The images to be binarized. - binary_thr (float): Threshold for binarization. - to_int (bool): If True, return image as int32, otherwise - return image as float32. - """ - - def __init__(self, keys, binary_thr, to_int=False): - self.keys = keys - self.binary_thr = binary_thr - self.to_int = to_int - - def _binarize(self, img): - type_ = np.float32 if not self.to_int else np.int32 - img = (img[..., :] > self.binary_thr).astype(type_) - - return img - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - results[k] = self._binarize(results[k]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, binary_thr={self.binary_thr}, ' - f'to_int={self.to_int})') - - return repr_str - - -@PIPELINES.register_module() -class RandomMaskDilation: - """Randomly dilate binary masks. - - Args: - keys (Sequence[str]): The images to be resized. - get_binary (bool): If True, according to binary_thr, reset final - output as binary mask. Otherwise, return masks directly. - binary_thr (float): Threshold for obtaining binary mask. - kernel_min (int): Min size of dilation kernel. - kernel_max (int): Max size of dilation kernel. - """ - - def __init__(self, keys, binary_thr=0., kernel_min=9, kernel_max=49): - self.keys = keys - self.kernel_min = kernel_min - self.kernel_max = kernel_max - self.binary_thr = binary_thr - - def _random_dilate(self, img): - kernel_size = np.random.randint(self.kernel_min, self.kernel_max + 1) - kernel = np.ones((kernel_size, kernel_size), dtype=np.uint8) - dilate_kernel_size = kernel_size - img_ = cv2.dilate(img, kernel, iterations=1) - - img_ = (img_ > self.binary_thr).astype(np.float32) - - return img_, dilate_kernel_size - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - results[k], d_kernel = self._random_dilate(results[k]) - if len(results[k].shape) == 2: - results[k] = np.expand_dims(results[k], axis=2) - results[k + '_dilate_kernel_size'] = d_kernel - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, kernel_min={self.kernel_min}, ' - f'kernel_max={self.kernel_max})') - - return repr_str - - -@PIPELINES.register_module() -class RandomTransposeHW: - """Randomly transpose images in H and W dimensions with a probability. - - (TransposeHW = horizontal flip + anti-clockwise rotatation by 90 degrees) - When used with horizontal/vertical flips, it serves as a way of rotation - augmentation. - It also supports randomly transposing a list of images. - - Required keys are the keys in attributes "keys", added or modified keys are - "transpose" and the keys in attributes "keys". - - Args: - keys (list[str]): The images to be transposed. - transpose_ratio (float): The propability to transpose the images. - """ - - def __init__(self, keys, transpose_ratio=0.5): - self.keys = keys - self.transpose_ratio = transpose_ratio - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - transpose = np.random.random() < self.transpose_ratio - - if transpose: - for key in self.keys: - if isinstance(results[key], list): - results[key] = [v.transpose(1, 0, 2) for v in results[key]] - else: - results[key] = results[key].transpose(1, 0, 2) - - results['transpose'] = transpose - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(keys={self.keys}, transpose_ratio={self.transpose_ratio})') - return repr_str - - -@PIPELINES.register_module() -class GenerateFrameIndiceswithPadding: - """Generate frame index with padding for REDS dataset and Vid4 dataset - during testing. - - Required keys: lq_path, gt_path, key, num_input_frames, max_frame_num - Added or modified keys: lq_path, gt_path - - Args: - padding (str): padding mode, one of - 'replicate' | 'reflection' | 'reflection_circle' | 'circle'. - - Examples: current_idx = 0, num_input_frames = 5 - The generated frame indices under different padding mode: - - replicate: [0, 0, 0, 1, 2] - reflection: [2, 1, 0, 1, 2] - reflection_circle: [4, 3, 0, 1, 2] - circle: [3, 4, 0, 1, 2] - - filename_tmpl (str): Template for file name. Default: '{:08d}'. - """ - - def __init__(self, padding, filename_tmpl='{:08d}'): - if padding not in ('replicate', 'reflection', 'reflection_circle', - 'circle'): - raise ValueError(f'Wrong padding mode {padding}.' - 'Should be "replicate", "reflection", ' - '"reflection_circle", "circle"') - self.padding = padding - self.filename_tmpl = filename_tmpl - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - clip_name, frame_name = results['key'].split(os.sep) - current_idx = int(frame_name) - max_frame_num = results['max_frame_num'] - 1 # start from 0 - num_input_frames = results['num_input_frames'] - num_pad = num_input_frames // 2 - - frame_list = [] - for i in range(current_idx - num_pad, current_idx + num_pad + 1): - if i < 0: - if self.padding == 'replicate': - pad_idx = 0 - elif self.padding == 'reflection': - pad_idx = -i - elif self.padding == 'reflection_circle': - pad_idx = current_idx + num_pad - i - else: - pad_idx = num_input_frames + i - elif i > max_frame_num: - if self.padding == 'replicate': - pad_idx = max_frame_num - elif self.padding == 'reflection': - pad_idx = max_frame_num * 2 - i - elif self.padding == 'reflection_circle': - pad_idx = (current_idx - num_pad) - (i - max_frame_num) - else: - pad_idx = i - num_input_frames - else: - pad_idx = i - frame_list.append(pad_idx) - - lq_path_root = results['lq_path'] - gt_path_root = results['gt_path'] - lq_paths = [ - osp.join(lq_path_root, clip_name, - f'{self.filename_tmpl.format(idx)}.png') - for idx in frame_list - ] - gt_paths = [osp.join(gt_path_root, clip_name, f'{frame_name}.png')] - results['lq_path'] = lq_paths - results['gt_path'] = gt_paths - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ + f"(padding='{self.padding}')" - return repr_str - - -@PIPELINES.register_module() -class GenerateFrameIndices: - """Generate frame index for REDS datasets. It also performs - temporal augmention with random interval. - - Required keys: lq_path, gt_path, key, num_input_frames - Added or modified keys: lq_path, gt_path, interval, reverse - - Args: - interval_list (list[int]): Interval list for temporal augmentation. - It will randomly pick an interval from interval_list and sample - frame index with the interval. - frames_per_clip(int): Number of frames per clips. Default: 99 for - REDS dataset. - """ - - def __init__(self, interval_list, frames_per_clip=99): - self.interval_list = interval_list - self.frames_per_clip = frames_per_clip - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - clip_name, frame_name = results['key'].split( - os.sep) # key example: 000/00000000 - center_frame_idx = int(frame_name) - num_half_frames = results['num_input_frames'] // 2 - - max_frame_num = results.get('max_frame_num', self.frames_per_clip + 1) - frames_per_clip = min(self.frames_per_clip, max_frame_num - 1) - - interval = np.random.choice(self.interval_list) - # ensure not exceeding the borders - start_frame_idx = center_frame_idx - num_half_frames * interval - end_frame_idx = center_frame_idx + num_half_frames * interval - while (start_frame_idx < 0) or (end_frame_idx > frames_per_clip): - center_frame_idx = np.random.randint(0, frames_per_clip + 1) - start_frame_idx = center_frame_idx - num_half_frames * interval - end_frame_idx = center_frame_idx + num_half_frames * interval - frame_name = f'{center_frame_idx:08d}' - neighbor_list = list( - range(center_frame_idx - num_half_frames * interval, - center_frame_idx + num_half_frames * interval + 1, interval)) - - lq_path_root = results['lq_path'] - gt_path_root = results['gt_path'] - lq_path = [ - osp.join(lq_path_root, clip_name, f'{v:08d}.png') - for v in neighbor_list - ] - gt_path = [osp.join(gt_path_root, clip_name, f'{frame_name}.png')] - results['lq_path'] = lq_path - results['gt_path'] = gt_path - results['interval'] = interval - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(interval_list={self.interval_list}, ' - f'frames_per_clip={self.frames_per_clip})') - return repr_str - - -@PIPELINES.register_module() -class TemporalReverse: - """Reverse frame lists for temporal augmentation. - - Required keys are the keys in attributes "lq" and "gt", - added or modified keys are "lq", "gt" and "reverse". - - Args: - keys (list[str]): The frame lists to be reversed. - reverse_ratio (float): The propability to reverse the frame lists. - Default: 0.5. - """ - - def __init__(self, keys, reverse_ratio=0.5): - self.keys = keys - self.reverse_ratio = reverse_ratio - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - reverse = np.random.random() < self.reverse_ratio - - if reverse: - for key in self.keys: - results[key].reverse() - - results['reverse'] = reverse - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(keys={self.keys}, reverse_ratio={self.reverse_ratio})' - return repr_str - - -@PIPELINES.register_module() -class GenerateSegmentIndices: - """Generate frame indices for a segment. It also performs temporal - augmention with random interval. - - Required keys: lq_path, gt_path, key, num_input_frames, sequence_length - Added or modified keys: lq_path, gt_path, interval, reverse - - Args: - interval_list (list[int]): Interval list for temporal augmentation. - It will randomly pick an interval from interval_list and sample - frame index with the interval. - start_idx (int): The index corresponds to the first frame in the - sequence. Default: 0. - filename_tmpl (str): Template for file name. Default: '{:08d}.png'. - """ - - def __init__(self, interval_list, start_idx=0, filename_tmpl='{:08d}.png'): - self.interval_list = interval_list - self.filename_tmpl = filename_tmpl - self.start_idx = start_idx - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - # key example: '000', 'calendar' (sequence name) - clip_name = results['key'] - interval = np.random.choice(self.interval_list) - - self.sequence_length = results['sequence_length'] - num_input_frames = results.get('num_input_frames', - self.sequence_length) - - # randomly select a frame as start - if self.sequence_length - num_input_frames * interval < 0: - raise ValueError('The input sequence is not long enough to ' - 'support the current choice of [interval] or ' - '[num_input_frames].') - start_frame_idx = np.random.randint( - 0, self.sequence_length - num_input_frames * interval + 1) - end_frame_idx = start_frame_idx + num_input_frames * interval - neighbor_list = list(range(start_frame_idx, end_frame_idx, interval)) - neighbor_list = [v + self.start_idx for v in neighbor_list] - - # add the corresponding file paths - lq_path_root = results['lq_path'] - gt_path_root = results['gt_path'] - lq_path = [ - osp.join(lq_path_root, clip_name, self.filename_tmpl.format(v)) - for v in neighbor_list - ] - gt_path = [ - osp.join(gt_path_root, clip_name, self.filename_tmpl.format(v)) - for v in neighbor_list - ] - - results['lq_path'] = lq_path - results['gt_path'] = gt_path - results['interval'] = interval - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(interval_list={self.interval_list})') - return repr_str - - -@PIPELINES.register_module() -class MirrorSequence: - """Extend short sequences (e.g. Vimeo-90K) by mirroring the sequences - - Given a sequence with N frames (x1, ..., xN), extend the sequence to - (x1, ..., xN, xN, ..., x1). - - Args: - keys (list[str]): The frame lists to be extended. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if isinstance(results[key], list): - results[key] = results[key] + results[key][::-1] - else: - raise TypeError('The input must be of class list[nparray]. ' - f'Got {type(results[key])}.') - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys})') - return repr_str - - -@PIPELINES.register_module() -class CopyValues: - """Copy the value of a source key to a destination key. - - - It does the following: results[dst_key] = results[src_key] for - (src_key, dst_key) in zip(src_keys, dst_keys). - - Added keys are the keys in the attribute "dst_keys". - - Args: - src_keys (list[str]): The source keys. - dst_keys (list[str]): The destination keys. - """ - - def __init__(self, src_keys, dst_keys): - - if not isinstance(src_keys, list) or not isinstance(dst_keys, list): - raise AssertionError('"src_keys" and "dst_keys" must be lists.') - - if len(src_keys) != len(dst_keys): - raise ValueError('"src_keys" and "dst_keys" should have the same' - 'number of elements.') - - self.src_keys = src_keys - self.dst_keys = dst_keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict with a key added/modified. - """ - for (src_key, dst_key) in zip(self.src_keys, self.dst_keys): - results[dst_key] = copy.deepcopy(results[src_key]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(src_keys={self.src_keys})') - repr_str += (f'(dst_keys={self.dst_keys})') - return repr_str - - -@PIPELINES.register_module() -class Quantize: - """Quantize and clip the image to [0, 1]. - - It is assumed that the the input has range [0, 1]. - - Modified keys are the attributes specified in "keys". - - Args: - keys (list[str]): The keys whose values are clipped. - """ - - def __init__(self, keys): - self.keys = keys - - def _quantize_clip(self, input_): - is_single_image = False - if isinstance(input_, np.ndarray): - is_single_image = True - input_ = [input_] - - # quantize and clip - input_ = [np.clip((v * 255.0).round(), 0, 255) / 255. for v in input_] - - if is_single_image: - input_ = input_[0] - - return input_ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict with the values of the specified keys are rounded - and clipped. - """ - - for key in self.keys: - results[key] = self._quantize_clip(results[key]) - - return results - - def __repr__(self): - return self.__class__.__name__ - - -@PIPELINES.register_module() -class UnsharpMasking: - """Apply unsharp masking to an image or a sequence of images. - - Args: - kernel_size (int): The kernel_size of the Gaussian kernel. - sigma (float): The standard deviation of the Gaussian. - weight (float): The weight of the "details" in the final output. - threshold (float): Pixel differences larger than this value are - regarded as "details". - keys (list[str]): The keys whose values are processed. - - Added keys are "xxx_unsharp", where "xxx" are the attributes specified - in "keys". - - """ - - def __init__(self, kernel_size, sigma, weight, threshold, keys): - if kernel_size % 2 == 0: - raise ValueError('kernel_size must be an odd number, but ' - f'got {kernel_size}.') - - self.kernel_size = kernel_size - self.sigma = sigma - self.weight = weight - self.threshold = threshold - self.keys = keys - - kernel = cv2.getGaussianKernel(kernel_size, sigma) - self.kernel = np.matmul(kernel, kernel.transpose()) - - def _unsharp_masking(self, imgs): - is_single_image = False - if isinstance(imgs, np.ndarray): - is_single_image = True - imgs = [imgs] - - outputs = [] - for img in imgs: - residue = img - cv2.filter2D(img, -1, self.kernel) - mask = np.float32(np.abs(residue) * 255 > self.threshold) - soft_mask = cv2.filter2D(mask, -1, self.kernel) - sharpened = np.clip(img + self.weight * residue, 0, 1) - - outputs.append(soft_mask * sharpened + (1 - soft_mask) * img) - - if is_single_image: - outputs = outputs[0] - - return outputs - - def __call__(self, results): - for key in self.keys: - results[f'{key}_unsharp'] = self._unsharp_masking(results[key]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, kernel_size={self.kernel_size}, ' - f'sigma={self.sigma}, weight={self.weight}, ' - f'threshold={self.threshold})') - return repr_str diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/compose.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/compose.py deleted file mode 100755 index 0ffb0fd57..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/compose.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -from mmcv.utils import build_from_cfg - -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class Compose: - """Compose a data pipeline with a sequence of transforms. - - Args: - transforms (list[dict | callable]): - Either config dicts of transforms or transform objects. - """ - - def __init__(self, transforms): - assert isinstance(transforms, Sequence) - self.transforms = [] - for transform in transforms: - if isinstance(transform, dict): - transform = build_from_cfg(transform, PIPELINES) - self.transforms.append(transform) - elif callable(transform): - self.transforms.append(transform) - else: - raise TypeError(f'transform must be callable or a dict, ' - f'but got {type(transform)}') - - def __call__(self, data): - """Call function. - - Args: - data (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for t in self.transforms: - data = t(data) - if data is None: - return None - return data - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - format_string += '\n' - format_string += f' {t}' - format_string += '\n)' - return format_string diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/formating.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/formating.py deleted file mode 100755 index 6ae98c050..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/formating.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import mmcv -import numpy as np -import torch -from mmcv.parallel import DataContainer as DC -from torch.nn import functional as F - -from ..registry import PIPELINES - - -def to_tensor(data): - """Convert objects of various python types to :obj:`torch.Tensor`. - - Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, - :class:`Sequence`, :class:`int` and :class:`float`. - """ - if isinstance(data, torch.Tensor): - return data - if isinstance(data, np.ndarray): - return torch.from_numpy(data) - if isinstance(data, Sequence) and not mmcv.is_str(data): - return torch.tensor(data) - if isinstance(data, int): - return torch.LongTensor([data]) - if isinstance(data, float): - return torch.FloatTensor([data]) - - raise TypeError(f'type {type(data)} cannot be converted to tensor.') - - -@PIPELINES.register_module() -class ToTensor: - """Convert some values in results dict to `torch.Tensor` type - in data loader pipeline. - - Args: - keys (Sequence[str]): Required keys to be converted. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - results[key] = to_tensor(results[key]) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class ImageToTensor: - """Convert image type to `torch.Tensor` type. - - Args: - keys (Sequence[str]): Required keys to be converted. - to_float32 (bool): Whether convert numpy image array to np.float32 - before converted to tensor. Default: True. - """ - - def __init__(self, keys, to_float32=True): - self.keys = keys - self.to_float32 = to_float32 - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - # deal with gray scale img: expand a color channel - if len(results[key].shape) == 2: - results[key] = results[key][..., None] - if self.to_float32 and not isinstance(results[key], np.float32): - results[key] = results[key].astype(np.float32) - results[key] = to_tensor(results[key].transpose(2, 0, 1)) - return results - - def __repr__(self): - return self.__class__.__name__ + ( - f'(keys={self.keys}, to_float32={self.to_float32})') - - -@PIPELINES.register_module() -class FramesToTensor(ImageToTensor): - """Convert frames type to `torch.Tensor` type. - - It accepts a list of frames, converts each to `torch.Tensor` type and then - concatenates in a new dimension (dim=0). - - Args: - keys (Sequence[str]): Required keys to be converted. - to_float32 (bool): Whether convert numpy image array to np.float32 - before converted to tensor. Default: True. - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if not isinstance(results[key], list): - raise TypeError(f'results["{key}"] should be a list, ' - f'but got {type(results[key])}') - for idx, v in enumerate(results[key]): - # deal with gray scale img: expand a color channel - if len(v.shape) == 2: - v = v[..., None] - if self.to_float32 and not isinstance(v, np.float32): - v = v.astype(np.float32) - results[key][idx] = to_tensor(v.transpose(2, 0, 1)) - results[key] = torch.stack(results[key], dim=0) - if results[key].size(0) == 1: - results[key].squeeze_() - return results - - -@PIPELINES.register_module() -class GetMaskedImage: - """Get masked image. - - Args: - img_name (str): Key for clean image. - mask_name (str): Key for mask image. The mask shape should be - (h, w, 1) while '1' indicate holes and '0' indicate valid - regions. - """ - - def __init__(self, img_name='gt_img', mask_name='mask'): - self.img_name = img_name - self.mask_name = mask_name - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - clean_img = results[self.img_name] - mask = results[self.mask_name] - - masked_img = clean_img * (1. - mask) - results['masked_img'] = masked_img - - return results - - def __repr__(self): - return self.__class__.__name__ + ( - f"(img_name='{self.img_name}', mask_name='{self.mask_name}')") - - -@PIPELINES.register_module() -class FormatTrimap: - """Convert trimap (tensor) to one-hot representation. - - It transforms the trimap label from (0, 128, 255) to (0, 1, 2). If - ``to_onehot`` is set to True, the trimap will convert to one-hot tensor of - shape (3, H, W). Required key is "trimap", added or modified key are - "trimap" and "to_onehot". - - Args: - to_onehot (bool): whether convert trimap to one-hot tensor. Default: - ``False``. - """ - - def __init__(self, to_onehot=False): - self.to_onehot = to_onehot - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - trimap = results['trimap'].squeeze() - trimap[trimap == 128] = 1 - trimap[trimap == 255] = 2 - if self.to_onehot: - trimap = F.one_hot(trimap.to(torch.long), num_classes=3) - trimap = trimap.permute(2, 0, 1) - else: - trimap = trimap[None, ...] # expand the channels dimension - results['trimap'] = trimap.float() - results['meta'].data['to_onehot'] = self.to_onehot - return results - - def __repr__(self): - return self.__class__.__name__ + f'(to_onehot={self.to_onehot})' - - -@PIPELINES.register_module() -class Collect: - """Collect data from the loader relevant to the specific task. - - This is usually the last stage of the data loader pipeline. Typically keys - is set to some subset of "img", "gt_labels". - - The "img_meta" item is always populated. The contents of the "meta" - dictionary depends on "meta_keys". - - Args: - keys (Sequence[str]): Required keys to be collected. - meta_keys (Sequence[str]): Required keys to be collected to "meta". - Default: None. - """ - - def __init__(self, keys, meta_keys=None): - self.keys = keys - self.meta_keys = meta_keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - data = {} - img_meta = {} - for key in self.meta_keys: - img_meta[key] = results[key] - data['meta'] = DC(img_meta, cpu_only=True) - for key in self.keys: - data[key] = results[key] - return data - - def __repr__(self): - return self.__class__.__name__ + ( - f'(keys={self.keys}, meta_keys={self.meta_keys})') diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/generate_assistant.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/generate_assistant.py deleted file mode 100755 index 8d8395534..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/generate_assistant.py +++ /dev/null @@ -1,169 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch - -from ..registry import PIPELINES -from .utils import make_coord - - -@PIPELINES.register_module() -class GenerateHeatmap: - """Generate heatmap from keypoint. - - Args: - keypoint (str): Key of keypoint in dict. - ori_size (int | Tuple[int]): Original image size of keypoint. - target_size (int | Tuple[int]): Target size of heatmap. - sigma (float): Sigma parameter of heatmap. Default: 1.0 - """ - - def __init__(self, keypoint, ori_size, target_size, sigma=1.0): - if isinstance(ori_size, int): - ori_size = (ori_size, ori_size) - else: - ori_size = ori_size[:2] - if isinstance(target_size, int): - target_size = (target_size, target_size) - else: - target_size = target_size[:2] - self.size_ratio = (target_size[0] / ori_size[0], - target_size[1] / ori_size[1]) - self.keypoint = keypoint - self.sigma = sigma - self.target_size = target_size - self.ori_size = ori_size - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. Require keypoint. - - Returns: - dict: A dict containing the processed data and information. - Add 'heatmap'. - """ - keypoint_list = [(keypoint[0] * self.size_ratio[0], - keypoint[1] * self.size_ratio[1]) - for keypoint in results[self.keypoint]] - heatmap_list = [ - self._generate_one_heatmap(keypoint) for keypoint in keypoint_list - ] - results['heatmap'] = np.stack(heatmap_list, axis=2) - return results - - def _generate_one_heatmap(self, keypoint): - """Generate One Heatmap. - - Args: - landmark (Tuple[float]): Location of a landmark. - - results: - heatmap (np.ndarray): A heatmap of landmark. - """ - w, h = self.target_size - - x_range = np.arange(start=0, stop=w, dtype=int) - y_range = np.arange(start=0, stop=h, dtype=int) - grid_x, grid_y = np.meshgrid(x_range, y_range) - dist2 = (grid_x - keypoint[0])**2 + (grid_y - keypoint[1])**2 - exponent = dist2 / 2.0 / self.sigma / self.sigma - heatmap = np.exp(-exponent) - return heatmap - - def __repr__(self): - return (f'{self.__class__.__name__}, ' - f'keypoint={self.keypoint}, ' - f'ori_size={self.ori_size}, ' - f'target_size={self.target_size}, ' - f'sigma={self.sigma}') - - -@PIPELINES.register_module() -class GenerateCoordinateAndCell: - """Generate coordinate and cell. - - Generate coordinate from the desired size of SR image. - Train or val: - 1. Generate coordinate from GT. - 2. Reshape GT image to (HgWg, 3) and transpose to (3, HgWg). - where `Hg` and `Wg` represent the height and width of GT. - Test: - Generate coordinate from LQ and scale or target_size. - Then generate cell from coordinate. - - Args: - sample_quantity (int): The quantity of samples in coordinates. - To ensure that the GT tensors in a batch have the same dimensions. - Default: None. - scale (float): Scale of upsampling. - Default: None. - target_size (tuple[int]): Size of target image. - Default: None. - - The priority of getting 'size of target image' is: - 1, results['gt'].shape[-2:] - 2, results['lq'].shape[-2:] * scale - 3, target_size - """ - - def __init__(self, sample_quantity=None, scale=None, target_size=None): - self.sample_quantity = sample_quantity - self.scale = scale - self.target_size = target_size - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - Require either in results: - 1. 'lq' (tensor), whose shape is similar as (3, H, W). - 2. 'gt' (tensor), whose shape is similar as (3, H, W). - 3. None, the premise is - self.target_size and len(self.target_size) >= 2. - - Returns: - dict: A dict containing the processed data and information. - Reshape 'gt' to (-1, 3) and transpose to (3, -1) if 'gt' - in results. - Add 'coord' and 'cell'. - """ - # generate hr_coord (and hr_rgb) - if 'gt' in results: - crop_hr = results['gt'] - self.target_size = crop_hr.shape - hr_rgb = crop_hr.contiguous().view(3, -1).permute(1, 0) - results['gt'] = hr_rgb - elif self.scale is not None and 'lq' in results: - _, h_lr, w_lr = results['lq'].shape - self.target_size = (round(h_lr * self.scale), - round(w_lr * self.scale)) - else: - assert self.target_size is not None - assert len(self.target_size) >= 2 - hr_coord = make_coord(self.target_size[-2:]) - - if self.sample_quantity is not None and 'gt' in results: - sample_lst = np.random.choice( - len(hr_coord), self.sample_quantity, replace=False) - hr_coord = hr_coord[sample_lst] - results['gt'] = results['gt'][sample_lst] - - # Preparations for cell decoding - cell = torch.ones_like(hr_coord) - cell[:, 0] *= 2 / self.target_size[-2] - cell[:, 1] *= 2 / self.target_size[-1] - - results['coord'] = hr_coord - results['cell'] = cell - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'sample_quantity={self.sample_quantity}, ' - f'scale={self.scale}, target_size={self.target_size}') - return repr_str diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/loading.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/loading.py deleted file mode 100755 index c7f162732..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/loading.py +++ /dev/null @@ -1,562 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from pathlib import Path - -import mmcv -import numpy as np -from mmcv.fileio import FileClient - -from mmedit.core.mask import (bbox2mask, brush_stroke_mask, get_irregular_mask, - random_bbox) -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class LoadImageFromFile: - """Load image from file. - - Args: - io_backend (str): io backend where images are store. Default: 'disk'. - key (str): Keys in results to find corresponding path. Default: 'gt'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - convert_to (str | None): The color space of the output image. If None, - no conversion is conducted. Default: None. - save_original_img (bool): If True, maintain a copy of the image in - `results` dict with name of `f'ori_{key}'`. Default: False. - use_cache (bool): If True, load all images at once. Default: False. - backend (str): The image loading backend type. Options are `cv2`, - `pillow`, and 'turbojpeg'. Default: None. - kwargs (dict): Args for file client. - """ - - def __init__(self, - io_backend='disk', - key='gt', - flag='color', - channel_order='bgr', - convert_to=None, - save_original_img=False, - use_cache=False, - backend=None, - **kwargs): - - self.io_backend = io_backend - self.key = key - self.flag = flag - self.save_original_img = save_original_img - self.channel_order = channel_order - self.convert_to = convert_to - self.kwargs = kwargs - self.file_client = None - self.use_cache = use_cache - self.cache = dict() if use_cache else None - self.backend = backend - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - filepath = str(results[f'{self.key}_path']) - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - if self.use_cache: - if filepath in self.cache: - img = self.cache[filepath] - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - self.cache[filepath] = img - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - - if self.convert_to is not None: - if self.channel_order == 'bgr' and self.convert_to.lower() == 'y': - img = mmcv.bgr2ycbcr(img, y_only=True) - elif self.channel_order == 'rgb': - img = mmcv.rgb2ycbcr(img, y_only=True) - else: - raise ValueError('Currently support only "bgr2ycbcr" or ' - '"bgr2ycbcr".') - if img.ndim == 2: - img = np.expand_dims(img, axis=2) - - results[self.key] = img - results[f'{self.key}_path'] = filepath - results[f'{self.key}_ori_shape'] = img.shape - if self.save_original_img: - results[f'ori_{self.key}'] = img.copy() - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(io_backend={self.io_backend}, key={self.key}, ' - f'flag={self.flag}, save_original_img={self.save_original_img}, ' - f'channel_order={self.channel_order}, use_cache={self.use_cache})') - return repr_str - - -@PIPELINES.register_module() -class LoadImageFromFileList(LoadImageFromFile): - """Load image from file list. - - It accepts a list of path and read each frame from each path. A list - of frames will be returned. - - Args: - io_backend (str): io backend where images are store. Default: 'disk'. - key (str): Keys in results to find corresponding path. Default: 'gt'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - convert_to (str | None): The color space of the output image. If None, - no conversion is conducted. Default: None. - save_original_img (bool): If True, maintain a copy of the image in - `results` dict with name of `f'ori_{key}'`. Default: False. - use_cache (bool): If True, load all images at once. Default: False. - backend (str): The image loading backend type. Options are `cv2`, - `pillow`, and 'turbojpeg'. Default: None. - kwargs (dict): Args for file client. - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - filepaths = results[f'{self.key}_path'] - if not isinstance(filepaths, list): - raise TypeError( - f'filepath should be list, but got {type(filepaths)}') - - filepaths = [str(v) for v in filepaths] - - imgs = [] - shapes = [] - if self.save_original_img: - ori_imgs = [] - for filepath in filepaths: - if self.use_cache: - if filepath in self.cache: - img = self.cache[filepath] - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - self.cache[filepath] = img - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - - # convert to y-channel, if specified - if self.convert_to is not None: - if self.channel_order == 'bgr' and self.convert_to.lower( - ) == 'y': - img = mmcv.bgr2ycbcr(img, y_only=True) - elif self.channel_order == 'rgb': - img = mmcv.rgb2ycbcr(img, y_only=True) - else: - raise ValueError('Currently support only "bgr2ycbcr" or ' - '"bgr2ycbcr".') - - if img.ndim == 2: - img = np.expand_dims(img, axis=2) - - imgs.append(img) - shapes.append(img.shape) - if self.save_original_img: - ori_imgs.append(img.copy()) - - results[self.key] = imgs - results[f'{self.key}_path'] = filepaths - results[f'{self.key}_ori_shape'] = shapes - if self.save_original_img: - results[f'ori_{self.key}'] = ori_imgs - - return results - - -@PIPELINES.register_module() -class RandomLoadResizeBg: - """Randomly load a background image and resize it. - - Required key is "fg", added key is "bg". - - Args: - bg_dir (str): Path of directory to load background images from. - io_backend (str): io backend where images are store. Default: 'disk'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - kwargs (dict): Args for file client. - """ - - def __init__(self, - bg_dir, - io_backend='disk', - flag='color', - channel_order='bgr', - **kwargs): - self.bg_dir = bg_dir - self.bg_list = list(mmcv.scandir(bg_dir)) - self.io_backend = io_backend - self.flag = flag - self.channel_order = channel_order - self.kwargs = kwargs - self.file_client = None - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - h, w = results['fg'].shape[:2] - idx = np.random.randint(len(self.bg_list)) - filepath = Path(self.bg_dir).joinpath(self.bg_list[idx]) - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, flag=self.flag, channel_order=self.channel_order) # HWC - bg = mmcv.imresize(img, (w, h), interpolation='bicubic') - results['bg'] = bg - return results - - def __repr__(self): - return self.__class__.__name__ + f"(bg_dir='{self.bg_dir}')" - - -@PIPELINES.register_module() -class LoadMask: - """Load Mask for multiple types. - - For different types of mask, users need to provide the corresponding - config dict. - - Example config for bbox: - - .. code-block:: python - - config = dict(img_shape=(256, 256), max_bbox_shape=128) - - Example config for irregular: - - .. code-block:: python - - config = dict( - img_shape=(256, 256), - num_vertices=(4, 12), - max_angle=4., - length_range=(10, 100), - brush_width=(10, 40), - area_ratio_range=(0.15, 0.5)) - - Example config for ff: - - .. code-block:: python - - config = dict( - img_shape=(256, 256), - num_vertices=(4, 12), - mean_angle=1.2, - angle_range=0.4, - brush_width=(12, 40)) - - Example config for set: - - .. code-block:: python - - config = dict( - mask_list_file='xxx/xxx/ooxx.txt', - prefix='/xxx/xxx/ooxx/', - io_backend='disk', - flag='unchanged', - file_client_kwargs=dict() - ) - - The mask_list_file contains the list of mask file name like this: - test1.jpeg - test2.jpeg - ... - ... - - The prefix gives the data path. - - Args: - mask_mode (str): Mask mode in ['bbox', 'irregular', 'ff', 'set', - 'file']. - * bbox: square bounding box masks. - * irregular: irregular holes. - * ff: free-form holes from DeepFillv2. - * set: randomly get a mask from a mask set. - * file: get mask from 'mask_path' in results. - mask_config (dict): Params for creating masks. Each type of mask needs - different configs. - """ - - def __init__(self, mask_mode='bbox', mask_config=None): - self.mask_mode = mask_mode - self.mask_config = dict() if mask_config is None else mask_config - assert isinstance(self.mask_config, dict) - - # set init info if needed in some modes - self._init_info() - - def _init_info(self): - if self.mask_mode == 'set': - # get mask list information - self.mask_list = [] - mask_list_file = self.mask_config['mask_list_file'] - with open(mask_list_file, 'r') as f: - for line in f: - line_split = line.strip().split(' ') - mask_name = line_split[0] - self.mask_list.append( - Path(self.mask_config['prefix']).joinpath(mask_name)) - self.mask_set_size = len(self.mask_list) - self.io_backend = self.mask_config['io_backend'] - self.flag = self.mask_config['flag'] - self.file_client_kwargs = self.mask_config['file_client_kwargs'] - self.file_client = None - elif self.mask_mode == 'file': - self.io_backend = 'disk' - self.flag = 'unchanged' - self.file_client_kwargs = dict() - self.file_client = None - - def _get_random_mask_from_set(self): - if self.file_client is None: - self.file_client = FileClient(self.io_backend, - **self.file_client_kwargs) - # minus 1 to avoid out of range error - mask_idx = np.random.randint(0, self.mask_set_size) - mask_bytes = self.file_client.get(self.mask_list[mask_idx]) - mask = mmcv.imfrombytes(mask_bytes, flag=self.flag) # HWC, BGR - if mask.ndim == 2: - mask = np.expand_dims(mask, axis=2) - else: - mask = mask[:, :, 0:1] - - mask[mask > 0] = 1. - return mask - - def _get_mask_from_file(self, path): - if self.file_client is None: - self.file_client = FileClient(self.io_backend, - **self.file_client_kwargs) - mask_bytes = self.file_client.get(path) - mask = mmcv.imfrombytes(mask_bytes, flag=self.flag) # HWC, BGR - if mask.ndim == 2: - mask = np.expand_dims(mask, axis=2) - else: - mask = mask[:, :, 0:1] - - mask[mask > 0] = 1. - return mask - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - if self.mask_mode == 'bbox': - mask_bbox = random_bbox(**self.mask_config) - mask = bbox2mask(self.mask_config['img_shape'], mask_bbox) - results['mask_bbox'] = mask_bbox - elif self.mask_mode == 'irregular': - mask = get_irregular_mask(**self.mask_config) - elif self.mask_mode == 'set': - mask = self._get_random_mask_from_set() - elif self.mask_mode == 'ff': - mask = brush_stroke_mask(**self.mask_config) - elif self.mask_mode == 'file': - mask = self._get_mask_from_file(results['mask_path']) - else: - raise NotImplementedError( - f'Mask mode {self.mask_mode} has not been implemented.') - results['mask'] = mask - return results - - def __repr__(self): - return self.__class__.__name__ + f"(mask_mode='{self.mask_mode}')" - - -@PIPELINES.register_module() -class GetSpatialDiscountMask: - """Get spatial discounting mask constant. - - Spatial discounting mask is first introduced in: - Generative Image Inpainting with Contextual Attention. - - Args: - gamma (float, optional): Gamma for computing spatial discounting. - Defaults to 0.99. - beta (float, optional): Beta for computing spatial discounting. - Defaults to 1.5. - """ - - def __init__(self, gamma=0.99, beta=1.5): - self.gamma = gamma - self.beta = beta - - def spatial_discount_mask(self, mask_width, mask_height): - """Generate spatial discounting mask constant. - - Args: - mask_width (int): The width of bbox hole. - mask_height (int): The height of bbox height. - - Returns: - np.ndarray: Spatial discounting mask. - """ - w, h = np.meshgrid(np.arange(mask_width), np.arange(mask_height)) - grid_stack = np.stack([h, w], axis=2) - mask_values = (self.gamma**(np.minimum( - grid_stack, [mask_height - 1, mask_width - 1] - grid_stack) * - self.beta)).max( - axis=2, keepdims=True) - - return mask_values - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - mask_bbox = results['mask_bbox'] - mask = results['mask'] - mask_height, mask_width = mask_bbox[-2:] - discount_hole = self.spatial_discount_mask(mask_width, mask_height) - discount_mask = np.zeros_like(mask) - discount_mask[mask_bbox[0]:mask_bbox[0] + mask_height, - mask_bbox[1]:mask_bbox[1] + mask_width, - ...] = discount_hole - - results['discount_mask'] = discount_mask - - return results - - def __repr__(self): - return self.__class__.__name__ + (f'(gamma={self.gamma}, ' - f'beta={self.beta})') - - -@PIPELINES.register_module() -class LoadPairedImageFromFile(LoadImageFromFile): - """Load a pair of images from file. - - Each sample contains a pair of images, which are concatenated in the w - dimension (a|b). This is a special loading class for generation paired - dataset. It loads a pair of images as the common loader does and crops - it into two images with the same shape in different domains. - - Required key is "pair_path". Added or modified keys are "pair", - "pair_ori_shape", "ori_pair", "img_a", "img_b", "img_a_path", - "img_b_path", "img_a_ori_shape", "img_b_ori_shape", "ori_img_a" and - "ori_img_b". - - Args: - io_backend (str): io backend where images are store. Default: 'disk'. - key (str): Keys in results to find corresponding path. Default: 'gt'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - save_original_img (bool): If True, maintain a copy of the image in - `results` dict with name of `f'ori_{key}'`. Default: False. - kwargs (dict): Args for file client. - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - filepath = str(results[f'{self.key}_path']) - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, flag=self.flag, channel_order=self.channel_order) # HWC - if img.ndim == 2: - img = np.expand_dims(img, axis=2) - - results[self.key] = img - results[f'{self.key}_path'] = filepath - results[f'{self.key}_ori_shape'] = img.shape - if self.save_original_img: - results[f'ori_{self.key}'] = img.copy() - - # crop pair into a and b - w = img.shape[1] - if w % 2 != 0: - raise ValueError( - f'The width of image pair must be even number, but got {w}.') - new_w = w // 2 - img_a = img[:, :new_w, :] - img_b = img[:, new_w:, :] - - results['img_a'] = img_a - results['img_b'] = img_b - results['img_a_path'] = filepath - results['img_b_path'] = filepath - results['img_a_ori_shape'] = img_a.shape - results['img_b_ori_shape'] = img_b.shape - if self.save_original_img: - results['ori_img_a'] = img_a.copy() - results['ori_img_b'] = img_b.copy() - - return results diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/normalization.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/normalization.py deleted file mode 100755 index 8ff774d7f..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/normalization.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np - -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class Normalize: - """Normalize images with the given mean and std value. - - Required keys are the keys in attribute "keys", added or modified keys are - the keys in attribute "keys" and these keys with postfix '_norm_cfg'. - It also supports normalizing a list of images. - - Args: - keys (Sequence[str]): The images to be normalized. - mean (np.ndarray): Mean values of different channels. - std (np.ndarray): Std values of different channels. - to_rgb (bool): Whether to convert channels from BGR to RGB. - """ - - def __init__(self, keys, mean, std, to_rgb=False, save_original=False): - self.keys = keys - self.mean = np.array(mean, dtype=np.float32) - self.std = np.array(std, dtype=np.float32) - self.to_rgb = to_rgb - self.save_original = save_original - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if isinstance(results[key], list): - if self.save_original: - results[key + '_unnormalised'] = [ - v.copy() for v in results[key] - ] - results[key] = [ - mmcv.imnormalize(v, self.mean, self.std, self.to_rgb) - for v in results[key] - ] - else: - if self.save_original: - results[key + '_unnormalised'] = results[key].copy() - results[key] = mmcv.imnormalize(results[key], self.mean, - self.std, self.to_rgb) - - results['img_norm_cfg'] = dict( - mean=self.mean, std=self.std, to_rgb=self.to_rgb) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, mean={self.mean}, std={self.std}, ' - f'to_rgb={self.to_rgb})') - - return repr_str - - -@PIPELINES.register_module() -class RescaleToZeroOne: - """Transform the images into a range between 0 and 1. - - Required keys are the keys in attribute "keys", added or modified keys are - the keys in attribute "keys". - It also supports rescaling a list of images. - - Args: - keys (Sequence[str]): The images to be transformed. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if isinstance(results[key], list): - results[key] = [ - v.astype(np.float32) / 255. for v in results[key] - ] - else: - results[key] = results[key].astype(np.float32) / 255. - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/random_down_sampling.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/random_down_sampling.py deleted file mode 100755 index bba1ef7bb..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/random_down_sampling.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -from mmcv import imresize - -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class RandomDownSampling: - """Generate LQ image from GT (and crop), which will randomly pick a scale. - - Args: - scale_min (float): The minimum of upsampling scale, inclusive. - Default: 1.0. - scale_max (float): The maximum of upsampling scale, exclusive. - Default: 4.0. - patch_size (int): The cropped lr patch size. - Default: None, means no crop. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear", "bicubic", "box", "lanczos", - "hamming" for 'pillow' backend. - Default: "bicubic". - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. - Default: "pillow". - - Scale will be picked in the range of [scale_min, scale_max). - """ - - def __init__(self, - scale_min=1.0, - scale_max=4.0, - patch_size=None, - interpolation='bicubic', - backend='pillow'): - assert scale_max >= scale_min - self.scale_min = scale_min - self.scale_max = scale_max - self.patch_size = patch_size - self.interpolation = interpolation - self.backend = backend - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. 'gt' is required. - - Returns: - dict: A dict containing the processed data and information. - modified 'gt', supplement 'lq' and 'scale' to keys. - """ - img = results['gt'] - scale = np.random.uniform(self.scale_min, self.scale_max) - - if self.patch_size is None: - h_lr = math.floor(img.shape[-3] / scale + 1e-9) - w_lr = math.floor(img.shape[-2] / scale + 1e-9) - img = img[:round(h_lr * scale), :round(w_lr * scale), :] - img_down = resize_fn(img, (w_lr, h_lr), self.interpolation, - self.backend) - crop_lr, crop_hr = img_down, img - else: - w_lr = self.patch_size - w_hr = round(w_lr * scale) - x0 = np.random.randint(0, img.shape[-3] - w_hr) - y0 = np.random.randint(0, img.shape[-2] - w_hr) - crop_hr = img[x0:x0 + w_hr, y0:y0 + w_hr, :] - crop_lr = resize_fn(crop_hr, w_lr, self.interpolation, - self.backend) - results['gt'] = crop_hr - results['lq'] = crop_lr - results['scale'] = scale - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f' scale_min={self.scale_min}, ' - f'scale_max={self.scale_max}, ' - f'patch_size={self.patch_size}, ' - f'interpolation={self.interpolation}, ' - f'backend={self.backend}') - - return repr_str - - -def resize_fn(img, size, interpolation='bicubic', backend='pillow'): - """Resize the given image to a given size. - - Args: - img (ndarray | torch.Tensor): The input image. - size (int | tuple[int]): Target size w or (w, h). - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear", "bicubic", "box", "lanczos", - "hamming" for 'pillow' backend. - Default: "bicubic". - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. - Default: "pillow". - - Returns: - ndarray | torch.Tensor: `resized_img`, whose type is same as `img`. - """ - if isinstance(size, int): - size = (size, size) - if isinstance(img, np.ndarray): - return imresize( - img, size, interpolation=interpolation, backend=backend) - elif isinstance(img, torch.Tensor): - image = imresize( - img.numpy(), size, interpolation=interpolation, backend=backend) - return torch.from_numpy(image) - - else: - raise TypeError('img should got np.ndarray or torch.Tensor,' - f'but got {type(img)}') diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/utils.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/utils.py deleted file mode 100755 index 42d470c34..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/pipelines/utils.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import numpy as np -import torch -from mmcv.utils import print_log - -_integer_types = ( - np.byte, - np.ubyte, # 8 bits - np.short, - np.ushort, # 16 bits - np.intc, - np.uintc, # 16 or 32 or 64 bits - np.int_, - np.uint, # 32 or 64 bits - np.longlong, - np.ulonglong) # 64 bits - -_integer_ranges = { - t: (np.iinfo(t).min, np.iinfo(t).max) - for t in _integer_types -} - -dtype_range = { - np.bool_: (False, True), - np.bool8: (False, True), - np.float16: (-1, 1), - np.float32: (-1, 1), - np.float64: (-1, 1) -} -dtype_range.update(_integer_ranges) - - -def dtype_limits(image, clip_negative=False): - """Return intensity limits, i.e. (min, max) tuple, of the image's dtype. - - This function is adopted from skimage: - https://github.com/scikit-image/scikit-image/blob/ - 7e4840bd9439d1dfb6beaf549998452c99f97fdd/skimage/util/dtype.py#L35 - - Args: - image (ndarray): Input image. - clip_negative (bool, optional): If True, clip the negative range - (i.e. return 0 for min intensity) even if the image dtype allows - negative values. - - Returns - tuple: Lower and upper intensity limits. - """ - imin, imax = dtype_range[image.dtype.type] - if clip_negative: - imin = 0 - return imin, imax - - -def adjust_gamma(image, gamma=1, gain=1): - """Performs Gamma Correction on the input image. - - This function is adopted from skimage: - https://github.com/scikit-image/scikit-image/blob/ - 7e4840bd9439d1dfb6beaf549998452c99f97fdd/skimage/exposure/ - exposure.py#L439-L494 - - Also known as Power Law Transform. - This function transforms the input image pixelwise according to the - equation ``O = I**gamma`` after scaling each pixel to the range 0 to 1. - - Args: - image (ndarray): Input image. - gamma (float, optional): Non negative real number. Defaults to 1. - gain (float, optional): The constant multiplier. Defaults to 1. - - Returns: - ndarray: Gamma corrected output image. - """ - if np.any(image < 0): - raise ValueError('Image Correction methods work correctly only on ' - 'images with non-negative values. Use ' - 'skimage.exposure.rescale_intensity.') - - dtype = image.dtype.type - - if gamma < 0: - raise ValueError('Gamma should be a non-negative real number.') - - scale = float(dtype_limits(image, True)[1] - dtype_limits(image, True)[0]) - - out = ((image / scale)**gamma) * scale * gain - return out.astype(dtype) - - -def random_choose_unknown(unknown, crop_size): - """Randomly choose an unknown start (top-left) point for a given crop_size. - - Args: - unknown (np.ndarray): The binary unknown mask. - crop_size (tuple[int]): The given crop size. - - Returns: - tuple[int]: The top-left point of the chosen bbox. - """ - h, w = unknown.shape - crop_h, crop_w = crop_size - delta_h = center_h = crop_h // 2 - delta_w = center_w = crop_w // 2 - - # mask out the validate area for selecting the cropping center - mask = np.zeros_like(unknown) - mask[delta_h:h - delta_h, delta_w:w - delta_w] = 1 - if np.any(unknown & mask): - center_h_list, center_w_list = np.where(unknown & mask) - elif np.any(unknown): - center_h_list, center_w_list = np.where(unknown) - else: - print_log('No unknown pixels found!', level=logging.WARNING) - center_h_list = [center_h] - center_w_list = [center_w] - num_unknowns = len(center_h_list) - rand_ind = np.random.randint(num_unknowns) - center_h = center_h_list[rand_ind] - center_w = center_w_list[rand_ind] - - # make sure the top-left point is valid - top = np.clip(center_h - delta_h, 0, h - crop_h) - left = np.clip(center_w - delta_w, 0, w - crop_w) - - return top, left - - -def make_coord(shape, ranges=None, flatten=True): - """ Make coordinates at grid centers. - - Args: - shape (tuple): shape of image. - ranges (tuple): range of coordinate value. Default: None. - flatten (bool): flatten to (n, 2) or Not. Default: True. - - return: - coord (Tensor): coordinates. - """ - coord_seqs = [] - for i, n in enumerate(shape): - if ranges is None: - v0, v1 = -1, 1 - else: - v0, v1 = ranges[i] - r = (v1 - v0) / (2 * n) - seq = v0 + r + (2 * r) * torch.arange(n).float() - coord_seqs.append(seq) - coord = torch.stack(torch.meshgrid(*coord_seqs), dim=-1) - if flatten: - coord = coord.view(-1, coord.shape[-1]) - return coord diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/registry.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/registry.py deleted file mode 100755 index 984580ef2..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/registry.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -DATASETS = Registry('dataset') -PIPELINES = Registry('pipeline') diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/samplers/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/samplers/__init__.py deleted file mode 100755 index da09effaf..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/samplers/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .distributed_sampler import DistributedSampler - -__all__ = ['DistributedSampler'] diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/samplers/distributed_sampler.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/samplers/distributed_sampler.py deleted file mode 100755 index 7e800c813..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/samplers/distributed_sampler.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from __future__ import division -import math - -import torch -from torch.utils.data import DistributedSampler as _DistributedSampler - -from mmedit.core.utils import sync_random_seed - - -class DistributedSampler(_DistributedSampler): - """DistributedSampler inheriting from `torch.utils.data.DistributedSampler`. - - In pytorch of lower versions, there is no `shuffle` argument. This child - class will port one to DistributedSampler. - """ - - def __init__(self, - dataset, - num_replicas=None, - rank=None, - shuffle=True, - samples_per_gpu=1, - seed=0): - super().__init__(dataset, num_replicas=num_replicas, rank=rank) - self.shuffle = shuffle - self.samples_per_gpu = samples_per_gpu - # fix the bug of the official implementation - self.num_samples_per_replica = int( - math.ceil( - len(self.dataset) * 1.0 / self.num_replicas / samples_per_gpu)) - self.num_samples = self.num_samples_per_replica * self.samples_per_gpu - self.total_size = self.num_samples * self.num_replicas - - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - - # to avoid padding bug when meeting too small dataset - if len(dataset) < self.num_replicas * samples_per_gpu: - raise ValueError( - 'You may use too small dataset and our distributed ' - 'sampler cannot pad your dataset correctly. We highly ' - 'recommend you to use fewer GPUs to finish your work') - - def __iter__(self): - # deterministically shuffle based on epoch - if self.shuffle: - g = torch.Generator() - # When :attr:`shuffle=True`, this ensures all replicas - # use a different random ordering for each epoch. - # Otherwise, the next iteration of this sampler will - # yield the same ordering. - g.manual_seed(self.epoch + self.seed) - indices = torch.randperm(len(self.dataset), generator=g).tolist() - else: - indices = torch.arange(len(self.dataset)).tolist() - - # add extra samples to make it evenly divisible - indices += indices[:(self.total_size - len(indices))] - assert len(indices) == self.total_size - - # subsample - indices = indices[self.rank:self.total_size:self.num_replicas] - assert len(indices) == self.num_samples - - return iter(indices) diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/sr_folder_dataset.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/sr_folder_dataset.py deleted file mode 100755 index 68426794e..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/sr_folder_dataset.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from .base_sr_dataset import BaseSRDataset -from .registry import DATASETS - - -@DATASETS.register_module() -class SRFolderDataset(BaseSRDataset): - """General paired image folder dataset for image restoration. - - The dataset loads lq (Low Quality) and gt (Ground-Truth) image pairs, - applies specified transforms and finally returns a dict containing paired - data and other information. - - This is the "folder mode", which needs to specify the lq folder path and gt - folder path, each folder containing the corresponding images. - Image lists will be generated automatically. You can also specify the - filename template to match the lq and gt pairs. - - For example, we have two folders with the following structures: - - :: - - data_root - ├── lq - │ ├── 0001_x4.png - │ ├── 0002_x4.png - ├── gt - │ ├── 0001.png - │ ├── 0002.png - - then, you need to set: - - .. code-block:: python - - lq_folder = data_root/lq - gt_folder = data_root/gt - filename_tmpl = '{}_x4' - - Args: - lq_folder (str | :obj:`Path`): Path to a lq folder. - gt_folder (str | :obj:`Path`): Path to a gt folder. - pipeline (List[dict | callable]): A sequence of data transformations. - scale (int): Upsampling scale ratio. - test_mode (bool): Store `True` when building test dataset. - Default: `False`. - filename_tmpl (str): Template for each filename. Note that the - template excludes the file extension. Default: '{}'. - """ - - def __init__(self, - lq_folder, - gt_folder, - pipeline, - scale, - test_mode=False, - filename_tmpl='{}'): - super().__init__(pipeline, scale, test_mode) - self.lq_folder = str(lq_folder) - self.gt_folder = str(gt_folder) - self.filename_tmpl = filename_tmpl - self.data_infos = self.load_annotations() - - def load_annotations(self): - """Load annotations for SR dataset. - - It loads the LQ and GT image path from folders. - - Returns: - list[dict]: A list of dicts for paired paths of LQ and GT. - """ - data_infos = [] - lq_paths = self.scan_folder(self.lq_folder) - gt_paths = self.scan_folder(self.gt_folder) - assert len(lq_paths) == len(gt_paths), ( - f'gt and lq datasets have different number of images: ' - f'{len(lq_paths)}, {len(gt_paths)}.') - for gt_path in gt_paths: - basename, ext = osp.splitext(osp.basename(gt_path)) - lq_path = osp.join(self.lq_folder, - (f'{self.filename_tmpl.format(basename)}' - f'{ext}')) - assert lq_path in lq_paths, f'{lq_path} is not in lq_paths.' - data_infos.append(dict(lq_path=lq_path, gt_path=gt_path)) - return data_infos diff --git a/cv/super_resolution/liif/pytorch/mmedit/datasets/sr_folder_gt_dataset.py b/cv/super_resolution/liif/pytorch/mmedit/datasets/sr_folder_gt_dataset.py deleted file mode 100755 index 852c879bd..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/datasets/sr_folder_gt_dataset.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_sr_dataset import BaseSRDataset -from .registry import DATASETS - - -@DATASETS.register_module() -class SRFolderGTDataset(BaseSRDataset): - """General ground-truth image folder dataset for image restoration. - - The dataset loads gt (Ground-Truth) image only, - applies specified transforms and finally returns a dict containing paired - data and other information. - - This is the "gt folder mode", which needs to specify the gt - folder path, each folder containing the corresponding images. - Image lists will be generated automatically. - - For example, we have a folder with the following structure: - - :: - - data_root - ├── gt - │ ├── 0001.png - │ ├── 0002.png - - then, you need to set: - - .. code-block:: python - - gt_folder = data_root/gt - - Args: - gt_folder (str | :obj:`Path`): Path to a gt folder. - pipeline (List[dict | callable]): A sequence of data transformations. - scale (int | tuple): Upsampling scale or upsampling scale range. - test_mode (bool): Store `True` when building test dataset. - Default: `False`. - """ - - def __init__(self, - gt_folder, - pipeline, - scale, - test_mode=False, - filename_tmpl='{}'): - super().__init__(pipeline, scale, test_mode) - self.gt_folder = str(gt_folder) - self.filename_tmpl = filename_tmpl - self.data_infos = self.load_annotations() - - def load_annotations(self): - """Load annotations for SR dataset. - - It loads the GT image path from folder. - - Returns: - list[dict]: A list of dicts for path of GT. - """ - data_infos = [] - gt_paths = self.scan_folder(self.gt_folder) - for gt_path in gt_paths: - data_infos.append(dict(gt_path=gt_path)) - return data_infos diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/models/__init__.py deleted file mode 100755 index b536c616c..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .backbones import * # noqa: F401, F403 -from .base import BaseModel -from .builder import (build, build_backbone, build_component, build_loss, - build_model) -from .common import * # noqa: F401, F403 -from .components import * # noqa: F401, F403 -from .losses import * # noqa: F401, F403 -from .registry import BACKBONES, COMPONENTS, LOSSES, MODELS -from .restorers import LIIF diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/backbones/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/models/backbones/__init__.py deleted file mode 100755 index 97a52c43a..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/backbones/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .sr_backbones import (LIIFEDSR, LIIFRDN, EDSR, RDN) diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/__init__.py deleted file mode 100755 index 0f4fc69aa..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .liif_net import LIIFEDSR, LIIFRDN -from .edsr import EDSR -from .rdn import RDN diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/edsr.py b/cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/edsr.py deleted file mode 100644 index 0b038a128..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/edsr.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import torch -import torch.nn as nn -from mmcv.runner import load_checkpoint - -from mmedit.models.common import (PixelShufflePack, ResidualBlockNoBN, - make_layer) -from mmedit.models.registry import BACKBONES -from mmedit.utils import get_root_logger - - -class UpsampleModule(nn.Sequential): - """Upsample module used in EDSR. - - Args: - scale (int): Scale factor. Supported scales: 2^n and 3. - mid_channels (int): Channel number of intermediate features. - """ - - def __init__(self, scale, mid_channels): - modules = [] - if (scale & (scale - 1)) == 0: # scale = 2^n - for _ in range(int(math.log(scale, 2))): - modules.append( - PixelShufflePack( - mid_channels, mid_channels, 2, upsample_kernel=3)) - elif scale == 3: - modules.append( - PixelShufflePack( - mid_channels, mid_channels, scale, upsample_kernel=3)) - else: - raise ValueError(f'scale {scale} is not supported. ' - 'Supported scales: 2^n and 3.') - - super().__init__(*modules) - - -@BACKBONES.register_module() -class EDSR(nn.Module): - """EDSR network structure. - - Paper: Enhanced Deep Residual Networks for Single Image Super-Resolution. - Ref repo: https://github.com/thstkdgus35/EDSR-PyTorch - - Args: - in_channels (int): Channel number of inputs. - out_channels (int): Channel number of outputs. - mid_channels (int): Channel number of intermediate features. - Default: 64. - num_blocks (int): Block number in the trunk network. Default: 16. - upscale_factor (int): Upsampling factor. Support 2^n and 3. - Default: 4. - res_scale (float): Used to scale the residual in residual block. - Default: 1. - rgb_mean (list[float]): Image mean in RGB orders. - Default: [0.4488, 0.4371, 0.4040], calculated from DIV2K dataset. - rgb_std (list[float]): Image std in RGB orders. In EDSR, it uses - [1.0, 1.0, 1.0]. Default: [1.0, 1.0, 1.0]. - """ - - def __init__(self, - in_channels, - out_channels, - mid_channels=64, - num_blocks=16, - upscale_factor=4, - res_scale=1, - rgb_mean=[0.4488, 0.4371, 0.4040], - rgb_std=[1.0, 1.0, 1.0]): - super().__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.mid_channels = mid_channels - self.num_blocks = num_blocks - self.upscale_factor = upscale_factor - - self.mean = torch.Tensor(rgb_mean).view(1, -1, 1, 1) - self.std = torch.Tensor(rgb_std).view(1, -1, 1, 1) - - self.conv_first = nn.Conv2d(in_channels, mid_channels, 3, padding=1) - self.body = make_layer( - ResidualBlockNoBN, - num_blocks, - mid_channels=mid_channels, - res_scale=res_scale) - self.conv_after_body = nn.Conv2d(mid_channels, mid_channels, 3, 1, 1) - self.upsample = UpsampleModule(upscale_factor, mid_channels) - self.conv_last = nn.Conv2d( - mid_channels, out_channels, 3, 1, 1, bias=True) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - self.mean = self.mean.to(x) - self.std = self.std.to(x) - - x = (x - self.mean) / self.std - x = self.conv_first(x) - res = self.conv_after_body(self.body(x)) - res += x - - x = self.conv_last(self.upsample(res)) - x = x * self.std + self.mean - - return x - - def init_weights(self, pretrained=None, strict=True): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - strict (boo, optional): Whether strictly load the pretrained model. - Defaults to True. - """ - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=strict, logger=logger) - elif pretrained is None: - pass # use default initialization - else: - raise TypeError('"pretrained" must be a str or None. ' - f'But received {type(pretrained)}.') diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/liif_net.py b/cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/liif_net.py deleted file mode 100755 index 4eb6c9f2f..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/liif_net.py +++ /dev/null @@ -1,322 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.runner import load_checkpoint - -from mmedit.datasets.pipelines.utils import make_coord -from mmedit.models.builder import build_backbone, build_component -from mmedit.models.registry import BACKBONES -from mmedit.utils import get_root_logger - - -class LIIFNet(nn.Module): - """LIIF net for single image super-resolution, CVPR, 2021. - - Paper: Learning Continuous Image Representation with - Local Implicit Image Function - - The subclasses should define `generator` with `encoder` and `imnet`, - and overwrite the function `gen_feature`. - If `encoder` does not contain `mid_channels`, `__init__` should be - overwrite. - - Args: - encoder (dict): Config for the generator. - imnet (dict): Config for the imnet. - local_ensemble (bool): Whether to use local ensemble. Default: True. - feat_unfold (bool): Whether to use feature unfold. Default: True. - cell_decode (bool): Whether to use cell decode. Default: True. - eval_bsize (int): Size of batched predict. Default: None. - """ - - def __init__(self, - encoder, - imnet, - local_ensemble=True, - feat_unfold=True, - cell_decode=True, - eval_bsize=None): - super().__init__() - - self.local_ensemble = local_ensemble - self.feat_unfold = feat_unfold - self.cell_decode = cell_decode - self.eval_bsize = eval_bsize - - # model - self.encoder = build_backbone(encoder) - imnet_in_dim = self.encoder.mid_channels - if self.feat_unfold: - imnet_in_dim *= 9 - imnet_in_dim += 2 # attach coordinates - if self.cell_decode: - imnet_in_dim += 2 - imnet['in_dim'] = imnet_in_dim - self.imnet = build_component(imnet) - - def forward(self, x, coord, cell, test_mode=False): - """Forward function. - - Args: - x: input tensor. - coord (Tensor): coordinates tensor. - cell (Tensor): cell tensor. - test_mode (bool): Whether in test mode or not. Default: False. - - Returns: - pred (Tensor): output of model. - """ - - feature = self.gen_feature(x) - if self.eval_bsize is None or not test_mode: - pred = self.query_rgb(feature, coord, cell) - else: - pred = self.batched_predict(feature, coord, cell) - - return pred - - def query_rgb(self, feature, coord, cell=None): - """Query RGB value of GT. - - Adapted from 'https://github.com/yinboc/liif.git' - 'liif/models/liif.py' - Copyright (c) 2020, Yinbo Chen, under BSD 3-Clause License. - - Args: - feature (Tensor): encoded feature. - coord (Tensor): coord tensor, shape (BHW, 2). - cell (Tensor | None): cell tensor. Default: None. - - Returns: - result (Tensor): (part of) output. - """ - - if self.imnet is None: - result = F.grid_sample( - feature, - coord.flip(-1).unsqueeze(1), - mode='nearest', - align_corners=False) - result = result[:, :, 0, :].permute(0, 2, 1) - return result - - if self.feat_unfold: - feature = F.unfold( - feature, 3, - padding=1).view(feature.shape[0], feature.shape[1] * 9, - feature.shape[2], feature.shape[3]) - - if self.local_ensemble: - vx_lst = [-1, 1] - vy_lst = [-1, 1] - eps_shift = 1e-6 - else: - vx_lst, vy_lst, eps_shift = [0], [0], 0 - - # field radius (global: [-1, 1]) - radius_x = 2 / feature.shape[-2] / 2 - radius_y = 2 / feature.shape[-1] / 2 - - feat_coord = make_coord(feature.shape[-2:], flatten=False) \ - .permute(2, 0, 1) \ - .unsqueeze(0).expand(feature.shape[0], 2, *feature.shape[-2:]) - feat_coord = feat_coord.to(coord) - - preds = [] - areas = [] - for vx in vx_lst: - for vy in vy_lst: - coord_ = coord.clone() - coord_[:, :, 0] += vx * radius_x + eps_shift - coord_[:, :, 1] += vy * radius_y + eps_shift - coord_.clamp_(-1 + 1e-6, 1 - 1e-6) - query_feat = F.grid_sample( - feature, coord_.flip(-1).unsqueeze(1), - mode='nearest', align_corners=False)[:, :, 0, :] \ - .permute(0, 2, 1) - query_coord = F.grid_sample( - feat_coord, coord_.flip(-1).unsqueeze(1), - mode='nearest', align_corners=False)[:, :, 0, :] \ - .permute(0, 2, 1) - rel_coord = coord - query_coord - rel_coord[:, :, 0] *= feature.shape[-2] - rel_coord[:, :, 1] *= feature.shape[-1] - mid_tensor = torch.cat([query_feat, rel_coord], dim=-1) - - if self.cell_decode: - rel_cell = cell.clone() - rel_cell[:, :, 0] *= feature.shape[-2] - rel_cell[:, :, 1] *= feature.shape[-1] - mid_tensor = torch.cat([mid_tensor, rel_cell], dim=-1) - - bs, q = coord.shape[:2] - pred = self.imnet(mid_tensor.view(bs * q, -1)).view(bs, q, -1) - preds.append(pred) - - area = torch.abs(rel_coord[:, :, 0] * rel_coord[:, :, 1]) - areas.append(area + 1e-9) - - total_area = torch.stack(areas).sum(dim=0) - if self.local_ensemble: - areas = areas[::-1] - result = 0 - for pred, area in zip(preds, areas): - result = result + pred * (area / total_area).unsqueeze(-1) - - return result - - def batched_predict(self, x, coord, cell): - """Batched predict. - - Args: - x (Tensor): Input tensor. - coord (Tensor): coord tensor. - cell (Tensor): cell tensor. - - Returns: - pred (Tensor): output of model. - """ - with torch.no_grad(): - n = coord.shape[1] - left = 0 - preds = [] - while left < n: - right = min(left + self.eval_bsize, n) - pred = self.query_rgb(x, coord[:, left:right, :], - cell[:, left:right, :]) - preds.append(pred) - left = right - pred = torch.cat(preds, dim=1) - return pred - - def init_weights(self, pretrained=None, strict=True): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - strict (boo, optional): Whether strictly load the pretrained model. - Defaults to True. - """ - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=strict, logger=logger) - elif pretrained is not None: - raise TypeError('"pretrained" must be a str or None. ' - f'But received {type(pretrained)}.') - - -@BACKBONES.register_module() -class LIIFEDSR(LIIFNet): - """LIIF net based on EDSR. - - Paper: Learning Continuous Image Representation with - Local Implicit Image Function - - Args: - encoder (dict): Config for the generator. - imnet (dict): Config for the imnet. - local_ensemble (bool): Whether to use local ensemble. Default: True. - feat_unfold (bool): Whether to use feature unfold. Default: True. - cell_decode (bool): Whether to use cell decode. Default: True. - eval_bsize (int): Size of batched predict. Default: None. - """ - - def __init__(self, - encoder, - imnet, - local_ensemble=True, - feat_unfold=True, - cell_decode=True, - eval_bsize=None): - super().__init__( - encoder=encoder, - imnet=imnet, - local_ensemble=local_ensemble, - feat_unfold=feat_unfold, - cell_decode=cell_decode, - eval_bsize=eval_bsize) - - self.conv_first = self.encoder.conv_first - self.body = self.encoder.body - self.conv_after_body = self.encoder.conv_after_body - del self.encoder - - def gen_feature(self, x): - """Generate feature. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - x = self.conv_first(x) - res = self.body(x) - res = self.conv_after_body(res) - res += x - - return res - - -@BACKBONES.register_module() -class LIIFRDN(LIIFNet): - """LIIF net based on RDN. - - Paper: Learning Continuous Image Representation with - Local Implicit Image Function - - Args: - encoder (dict): Config for the generator. - imnet (dict): Config for the imnet. - local_ensemble (bool): Whether to use local ensemble. Default: True. - feat_unfold (bool): Whether to use feat unfold. Default: True. - cell_decode (bool): Whether to use cell decode. Default: True. - eval_bsize (int): Size of batched predict. Default: None. - """ - - def __init__(self, - encoder, - imnet, - local_ensemble=True, - feat_unfold=True, - cell_decode=True, - eval_bsize=None): - super().__init__( - encoder=encoder, - imnet=imnet, - local_ensemble=local_ensemble, - feat_unfold=feat_unfold, - cell_decode=cell_decode, - eval_bsize=eval_bsize) - - self.sfe1 = self.encoder.sfe1 - self.sfe2 = self.encoder.sfe2 - self.rdbs = self.encoder.rdbs - self.gff = self.encoder.gff - self.num_blocks = self.encoder.num_blocks - del self.encoder - - def gen_feature(self, x): - """Generate feature. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - sfe1 = self.sfe1(x) - sfe2 = self.sfe2(sfe1) - - x = sfe2 - local_features = [] - for i in range(self.num_blocks): - x = self.rdbs[i](x) - local_features.append(x) - - x = self.gff(torch.cat(local_features, 1)) + sfe1 - - return x diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/rdn.py b/cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/rdn.py deleted file mode 100644 index b4f073ae2..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/backbones/sr_backbones/rdn.py +++ /dev/null @@ -1,201 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -import torch -from mmcv.runner import load_checkpoint -from torch import nn - -from mmedit.models.registry import BACKBONES -from mmedit.utils import get_root_logger - - -class DenseLayer(nn.Module): - """Dense layer - - Args: - in_channels (int): Channel number of inputs. - out_channels (int): Channel number of outputs. - - """ - - def __init__(self, in_channels, out_channels): - super().__init__() - self.conv = nn.Conv2d( - in_channels, out_channels, kernel_size=3, padding=3 // 2) - self.relu = nn.ReLU(inplace=True) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c_in, h, w). - - Returns: - Tensor: Forward results, tensor with shape (n, c_in+c_out, h, w). - """ - return torch.cat([x, self.relu(self.conv(x))], 1) - - -class RDB(nn.Module): - """Residual Dense Block of Residual Dense Network - - Args: - in_channels (int): Channel number of inputs. - channel_growth (int): Channels growth in each layer. - num_layers (int): Layer number in the Residual Dense Block. - """ - - def __init__(self, in_channels, channel_growth, num_layers): - super().__init__() - self.layers = nn.Sequential(*[ - DenseLayer(in_channels + channel_growth * i, channel_growth) - for i in range(num_layers) - ]) - - # local feature fusion - self.lff = nn.Conv2d( - in_channels + channel_growth * num_layers, - channel_growth, - kernel_size=1) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - return x + self.lff(self.layers(x)) # local residual learning - - -@BACKBONES.register_module() -class RDN(nn.Module): - """RDN model for single image super-resolution. - - Paper: Residual Dense Network for Image Super-Resolution - - Adapted from 'https://github.com/yjn870/RDN-pytorch.git' - 'RDN-pytorch/blob/master/models.py' - Copyright (c) 2021, JaeYun Yeo, under MIT License. - - Args: - in_channels (int): Channel number of inputs. - out_channels (int): Channel number of outputs. - mid_channels (int): Channel number of intermediate features. - Default: 64. - num_blocks (int): Block number in the trunk network. Default: 16. - upscale_factor (int): Upsampling factor. Support 2^n and 3. - Default: 4. - num_layer (int): Layer number in the Residual Dense Block. - Default: 8. - channel_growth(int): Channels growth in each layer of RDB. - Default: 64. - """ - - def __init__(self, - in_channels, - out_channels, - mid_channels=64, - num_blocks=16, - upscale_factor=4, - num_layers=8, - channel_growth=64): - - super().__init__() - self.mid_channels = mid_channels - self.channel_growth = channel_growth - self.num_blocks = num_blocks - self.num_layers = num_layers - - # shallow feature extraction - self.sfe1 = nn.Conv2d( - in_channels, mid_channels, kernel_size=3, padding=3 // 2) - self.sfe2 = nn.Conv2d( - mid_channels, mid_channels, kernel_size=3, padding=3 // 2) - - # residual dense blocks - self.rdbs = nn.ModuleList( - [RDB(self.mid_channels, self.channel_growth, self.num_layers)]) - for _ in range(self.num_blocks - 1): - self.rdbs.append( - RDB(self.channel_growth, self.channel_growth, self.num_layers)) - - # global feature fusion - self.gff = nn.Sequential( - nn.Conv2d( - self.channel_growth * self.num_blocks, - self.mid_channels, - kernel_size=1), - nn.Conv2d( - self.mid_channels, - self.mid_channels, - kernel_size=3, - padding=3 // 2)) - - # up-sampling - assert 2 <= upscale_factor <= 4 - if upscale_factor == 2 or upscale_factor == 4: - self.upscale = [] - for _ in range(upscale_factor // 2): - self.upscale.extend([ - nn.Conv2d( - self.mid_channels, - self.mid_channels * (2**2), - kernel_size=3, - padding=3 // 2), - nn.PixelShuffle(2) - ]) - self.upscale = nn.Sequential(*self.upscale) - else: - self.upscale = nn.Sequential( - nn.Conv2d( - self.mid_channels, - self.mid_channels * (upscale_factor**2), - kernel_size=3, - padding=3 // 2), nn.PixelShuffle(upscale_factor)) - - self.output = nn.Conv2d( - self.mid_channels, out_channels, kernel_size=3, padding=3 // 2) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - sfe1 = self.sfe1(x) - sfe2 = self.sfe2(sfe1) - - x = sfe2 - local_features = [] - for i in range(self.num_blocks): - x = self.rdbs[i](x) - local_features.append(x) - - x = self.gff(torch.cat(local_features, 1)) + sfe1 - # global residual learning - x = self.upscale(x) - x = self.output(x) - return x - - def init_weights(self, pretrained=None, strict=True): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - strict (boo, optional): Whether strictly load the pretrained model. - Defaults to True. - """ - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=strict, logger=logger) - elif pretrained is None: - pass # use default initialization - else: - raise TypeError('"pretrained" must be a str or None. ' - f'But received {type(pretrained)}.') diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/base.py b/cv/super_resolution/liif/pytorch/mmedit/models/base.py deleted file mode 100755 index 02327e283..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/base.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod -from collections import OrderedDict - -import torch -import torch.nn as nn - - -class BaseModel(nn.Module, metaclass=ABCMeta): - """Base model. - - All models should subclass it. - All subclass should overwrite: - - ``init_weights``, supporting to initialize models. - - ``forward_train``, supporting to forward when training. - - ``forward_test``, supporting to forward when testing. - - ``train_step``, supporting to train one step when training. - """ - - @abstractmethod - def init_weights(self): - """Abstract method for initializing weight. - - All subclass should overwrite it. - """ - - @abstractmethod - def forward_train(self, imgs, labels): - """Abstract method for training forward. - - All subclass should overwrite it. - """ - - @abstractmethod - def forward_test(self, imgs): - """Abstract method for testing forward. - - All subclass should overwrite it. - """ - - def forward(self, imgs, labels, test_mode, **kwargs): - """Forward function for base model. - - Args: - imgs (Tensor): Input image(s). - labels (Tensor): Ground-truth label(s). - test_mode (bool): Whether in test mode. - kwargs (dict): Other arguments. - - Returns: - Tensor: Forward results. - """ - - if test_mode: - return self.forward_test(imgs, **kwargs) - - return self.forward_train(imgs, labels, **kwargs) - - @abstractmethod - def train_step(self, data_batch, optimizer): - """Abstract method for one training step. - - All subclass should overwrite it. - """ - - def val_step(self, data_batch, **kwargs): - """Abstract method for one validation step. - - All subclass should overwrite it. - """ - output = self.forward_test(**data_batch, **kwargs) - return output - - def parse_losses(self, losses): - """Parse losses dict for different loss variants. - - Args: - losses (dict): Loss dict. - - Returns: - loss (float): Sum of the total loss. - log_vars (dict): loss dict for different variants. - """ - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(_value for _key, _value in log_vars.items() - if 'loss' in _key) - - log_vars['loss'] = loss - for name in log_vars: - log_vars[name] = log_vars[name].item() - - return loss, log_vars diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/builder.py b/cv/super_resolution/liif/pytorch/mmedit/models/builder.py deleted file mode 100755 index 8606225aa..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/builder.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv import build_from_cfg - -from .registry import BACKBONES, COMPONENTS, LOSSES, MODELS - - -def build(cfg, registry, default_args=None): - """Build module function. - - Args: - cfg (dict): Configuration for building modules. - registry (obj): ``registry`` object. - default_args (dict, optional): Default arguments. Defaults to None. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return nn.Sequential(*modules) - - return build_from_cfg(cfg, registry, default_args) - - -def build_backbone(cfg): - """Build backbone. - - Args: - cfg (dict): Configuration for building backbone. - """ - return build(cfg, BACKBONES) - - -def build_component(cfg): - """Build component. - - Args: - cfg (dict): Configuration for building component. - """ - return build(cfg, COMPONENTS) - - -def build_loss(cfg): - """Build loss. - - Args: - cfg (dict): Configuration for building loss. - """ - return build(cfg, LOSSES) - - -def build_model(cfg, train_cfg=None, test_cfg=None): - """Build model. - - Args: - cfg (dict): Configuration for building model. - train_cfg (dict): Training configuration. Default: None. - test_cfg (dict): Testing configuration. Default: None. - """ - return build(cfg, MODELS, dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/__init__.py deleted file mode 100755 index 7ebeb4fba..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .aspp import ASPP -from .contextual_attention import ContextualAttentionModule -from .conv import * # noqa: F401, F403 -from .downsample import pixel_unshuffle -from .ensemble import SpatialTemporalEnsemble -from .flow_warp import flow_warp -from .gated_conv_module import SimpleGatedConvModule -from .gca_module import GCAModule -from .generation_model_utils import (GANImageBuffer, ResidualBlockWithDropout, - UnetSkipConnectionBlock, - generation_init_weights) -from .img_normalize import ImgNormalize -from .linear_module import LinearModule -from .mask_conv_module import MaskConvModule -from .model_utils import (extract_around_bbox, extract_bbox_patch, scale_bbox, - set_requires_grad) -from .partial_conv import PartialConv2d -from .separable_conv_module import DepthwiseSeparableConvModule -from .sr_backbone_utils import (ResidualBlockNoBN, default_init_weights, - make_layer) -from .upsample import PixelShufflePack - -__all__ = [ - 'ASPP', 'PartialConv2d', 'PixelShufflePack', 'default_init_weights', - 'ResidualBlockNoBN', 'make_layer', 'MaskConvModule', 'extract_bbox_patch', - 'extract_around_bbox', 'set_requires_grad', 'scale_bbox', - 'DepthwiseSeparableConvModule', 'ContextualAttentionModule', 'GCAModule', - 'SimpleGatedConvModule', 'LinearModule', 'flow_warp', 'ImgNormalize', - 'generation_init_weights', 'GANImageBuffer', 'UnetSkipConnectionBlock', - 'ResidualBlockWithDropout', 'pixel_unshuffle', 'SpatialTemporalEnsemble' -] diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/aspp.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/aspp.py deleted file mode 100755 index c1e58e858..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/aspp.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.cnn import ConvModule -from torch import nn -from torch.nn import functional as F - -from .separable_conv_module import DepthwiseSeparableConvModule - - -class ASPPPooling(nn.Sequential): - - def __init__(self, in_channels, out_channels, conv_cfg, norm_cfg, act_cfg): - super().__init__( - nn.AdaptiveAvgPool2d(1), - ConvModule( - in_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, x): - size = x.shape[-2:] - for mod in self: - x = mod(x) - return F.interpolate( - x, size=size, mode='bilinear', align_corners=False) - - -class ASPP(nn.Module): - """ASPP module from DeepLabV3. - - The code is adopted from - https://github.com/pytorch/vision/blob/master/torchvision/models/ - segmentation/deeplabv3.py - - For more information about the module: - `"Rethinking Atrous Convolution for Semantic Image Segmentation" - `_. - - Args: - in_channels (int): Input channels of the module. - out_channels (int): Output channels of the module. - mid_channels (int): Output channels of the intermediate ASPP conv - modules. - dilations (Sequence[int]): Dilation rate of three ASPP conv module. - Default: [12, 24, 36]. - conv_cfg (dict): Config dict for convolution layer. If "None", - nn.Conv2d will be applied. Default: None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - separable_conv (bool): Whether replace normal conv with depthwise - separable conv which is faster. Default: False. - """ - - def __init__(self, - in_channels, - out_channels=256, - mid_channels=256, - dilations=(12, 24, 36), - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - separable_conv=False): - super().__init__() - - if separable_conv: - conv_module = DepthwiseSeparableConvModule - else: - conv_module = ConvModule - - modules = [] - modules.append( - ConvModule( - in_channels, - mid_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - for dilation in dilations: - modules.append( - conv_module( - in_channels, - mid_channels, - 3, - padding=dilation, - dilation=dilation, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - modules.append( - ASPPPooling(in_channels, mid_channels, conv_cfg, norm_cfg, - act_cfg)) - - self.convs = nn.ModuleList(modules) - - self.project = nn.Sequential( - ConvModule( - 5 * mid_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg), nn.Dropout(0.5)) - - def forward(self, x): - """Forward function for ASPP module. - - Args: - x (Tensor): Input tensor with shape (N, C, H, W). - - Returns: - Tensor: Output tensor. - """ - res = [] - for conv in self.convs: - res.append(conv(x)) - res = torch.cat(res, dim=1) - return self.project(res) diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/contextual_attention.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/contextual_attention.py deleted file mode 100755 index 7dcf4099e..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/contextual_attention.py +++ /dev/null @@ -1,379 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch -import torch.nn as nn -import torch.nn.functional as F - - -class ContextualAttentionModule(nn.Module): - """Contexture attention module. - - The details of this module can be found in: - Generative Image Inpainting with Contextual Attention - - Args: - unfold_raw_kernel_size (int): Kernel size used in unfolding raw - feature. Default: 4. - unfold_raw_stride (int): Stride used in unfolding raw feature. Default: - 2. - unfold_raw_padding (int): Padding used in unfolding raw feature. - Default: 1. - unfold_corr_kernel_size (int): Kernel size used in unfolding - context for computing correlation maps. Default: 3. - unfold_corr_stride (int): Stride used in unfolding context for - computing correlation maps. Default: 1. - unfold_corr_dilation (int): Dilation used in unfolding context for - computing correlation maps. Default: 1. - unfold_corr_padding (int): Padding used in unfolding context for - computing correlation maps. Default: 1. - scale (float): The resale factor used in resize input features. - Default: 0.5. - fuse_kernel_size (int): The kernel size used in fusion module. - Default: 3. - softmax_scale (float): The scale factor for softmax function. - Default: 10. - return_attention_score (bool): If True, the attention score will be - returned. Default: True. - """ - - def __init__(self, - unfold_raw_kernel_size=4, - unfold_raw_stride=2, - unfold_raw_padding=1, - unfold_corr_kernel_size=3, - unfold_corr_stride=1, - unfold_corr_dilation=1, - unfold_corr_padding=1, - scale=0.5, - fuse_kernel_size=3, - softmax_scale=10, - return_attention_score=True): - super().__init__() - self.unfold_raw_kernel_size = unfold_raw_kernel_size - self.unfold_raw_stride = unfold_raw_stride - self.unfold_raw_padding = unfold_raw_padding - self.unfold_corr_kernel_size = unfold_corr_kernel_size - self.unfold_corr_stride = unfold_corr_stride - self.unfold_corr_dilation = unfold_corr_dilation - self.unfold_corr_padding = unfold_corr_padding - self.scale = scale - self.fuse_kernel_size = fuse_kernel_size - self.with_fuse_correlation = fuse_kernel_size > 1 - self.softmax_scale = softmax_scale - self.return_attention_score = return_attention_score - - if self.with_fuse_correlation: - assert fuse_kernel_size % 2 == 1 - fuse_kernel = torch.eye(fuse_kernel_size).view( - 1, 1, fuse_kernel_size, fuse_kernel_size) - self.register_buffer('fuse_kernel', fuse_kernel) - padding = int((fuse_kernel_size - 1) // 2) - self.fuse_conv = partial(F.conv2d, padding=padding, stride=1) - self.softmax = nn.Softmax(dim=1) - - def forward(self, x, context, mask=None): - """Forward Function. - - Args: - x (torch.Tensor): Tensor with shape (n, c, h, w). - context (torch.Tensor): Tensor with shape (n, c, h, w). - mask (torch.Tensor): Tensor with shape (n, 1, h, w). Default: None. - - Returns: - tuple(torch.Tensor): Features after contextural attention. - """ - # raw features to be used in copy (deconv) - raw_context = context - raw_context_cols = self.im2col( - raw_context, - kernel_size=self.unfold_raw_kernel_size, - stride=self.unfold_raw_stride, - padding=self.unfold_raw_padding, - normalize=False, - return_cols=True) - # resize the feature to reduce computational cost - x = F.interpolate(x, scale_factor=self.scale) - context = F.interpolate(context, scale_factor=self.scale) - - context_cols = self.im2col( - context, - kernel_size=self.unfold_corr_kernel_size, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation, - normalize=True, - return_cols=True) - h_unfold, w_unfold = self.calculate_unfold_hw( - context.size()[-2:], - kernel_size=self.unfold_corr_kernel_size, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation, - ) - # reshape context_cols to - # (n*h_unfold*w_unfold, c, unfold_mks, unfold_mks) - # 'mks' is short for 'mask_kernel_size' - context_cols = context_cols.reshape(-1, *context_cols.shape[2:]) - - # the shape of correlation map should be: - # (n, h_unfold*w_unfold, h', w') - correlation_map = self.patch_correlation(x, context_cols) - # fuse correlation map to enlarge consistent attention region. - if self.with_fuse_correlation: - correlation_map = self.fuse_correlation_map( - correlation_map, h_unfold, w_unfold) - - correlation_map = self.mask_correlation_map(correlation_map, mask=mask) - - attention_score = self.softmax(correlation_map * self.softmax_scale) - - raw_context_filter = raw_context_cols.reshape( - -1, *raw_context_cols.shape[2:]) - output = self.patch_copy_deconv(attention_score, raw_context_filter) - # deconv will cause overlap and we need to remove the effects of that - overlap_factor = self.calculate_overlap_factor(attention_score) - output /= overlap_factor - - if self.return_attention_score: - n, _, h_s, w_s = attention_score.size() - attention_score = attention_score.view(n, h_unfold, w_unfold, h_s, - w_s) - return output, attention_score - - return output - - def patch_correlation(self, x, kernel): - """Calculate patch correlation. - - Args: - x (torch.Tensor): Input tensor. - kernel (torch.Tensor): Kernel tensor. - - Returns: - torch.Tensor: Tensor with shape of (n, l, h, w). - """ - n, _, h_in, w_in = x.size() - - patch_corr = F.conv2d( - x.view(1, -1, h_in, w_in), - kernel, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation, - groups=n) - h_out, w_out = patch_corr.size()[-2:] - return patch_corr.view(n, -1, h_out, w_out) - - def patch_copy_deconv(self, attention_score, context_filter): - """Copy patches using deconv. - - Args: - attention_score (torch.Tensor): Tensor with shape of (n, l , h, w). - context_filter (torch.Tensor): Filter kernel. - - Returns: - torch.Tensor: Tensor with shape of (n, c, h, w). - """ - n, _, h, w = attention_score.size() - attention_score = attention_score.view(1, -1, h, w) - output = F.conv_transpose2d( - attention_score, - context_filter, - stride=self.unfold_raw_stride, - padding=self.unfold_raw_padding, - groups=n) - h_out, w_out = output.size()[-2:] - return output.view(n, -1, h_out, w_out) - - def fuse_correlation_map(self, correlation_map, h_unfold, w_unfold): - """Fuse correlation map. - - This operation is to fuse correlation map for increasing large - consistent correlation regions. - - The mechanism behind this op is simple and easy to understand. A - standard 'Eye' matrix will be applied as a filter on the correlation - map in horizontal and vertical direction. - - The shape of input correlation map is (n, h_unfold*w_unfold, h, w). - When adopting fusing, we will apply convolutional filter in the - reshaped feature map with shape of (n, 1, h_unfold*w_fold, h*w). - - A simple specification for horizontal direction is shown below: - - .. code-block:: python - - (h, (h, (h, (h, - 0) 1) 2) 3) ... - (h, 0) - (h, 1) 1 - (h, 2) 1 - (h, 3) 1 - ... - - """ - # horizontal direction - n, _, h_map, w_map = correlation_map.size() - map_ = correlation_map.permute(0, 2, 3, 1) - map_ = map_.reshape(n, h_map * w_map, h_unfold * w_unfold, 1) - map_ = map_.permute(0, 3, 1, 2).contiguous() - map_ = self.fuse_conv(map_, self.fuse_kernel) - - correlation_map = map_.view(n, h_unfold, w_unfold, h_map, w_map) - - # vertical direction - map_ = correlation_map.permute(0, 2, 1, 4, - 3).reshape(n, 1, h_unfold * w_unfold, - h_map * w_map) - map_ = self.fuse_conv(map_, self.fuse_kernel) - - # Note that the dimension should be transposed since the convolution of - # eye matrix will put the normed scores into the last several dimension - correlation_map = map_.view(n, w_unfold, h_unfold, w_map, - h_map).permute(0, 4, 3, 2, 1) - correlation_map = correlation_map.reshape(n, -1, h_unfold, w_unfold) - - return correlation_map - - def calculate_unfold_hw(self, - input_size, - kernel_size=3, - stride=1, - dilation=1, - padding=0): - """Calculate (h, w) after unfolding - - The official implementation of `unfold` in pytorch will put the - dimension (h, w) into `L`. Thus, this function is just to calculate the - (h, w) according to the equation in: - https://pytorch.org/docs/stable/nn.html#torch.nn.Unfold - """ - h_in, w_in = input_size - - h_unfold = int((h_in + 2 * padding - dilation * - (kernel_size - 1) - 1) / stride + 1) - - w_unfold = int((w_in + 2 * padding - dilation * - (kernel_size - 1) - 1) / stride + 1) - return h_unfold, w_unfold - - def calculate_overlap_factor(self, attention_score): - """Calculate the overlap factor after applying deconv. - - Args: - attention_score (torch.Tensor): The attention score with shape of - (n, c, h, w). - - Returns: - torch.Tensor: The overlap factor will be returned. - """ - h, w = attention_score.shape[-2:] - kernel_size = self.unfold_raw_kernel_size - - ones_input = torch.ones(1, 1, h, w).to(attention_score) - ones_filter = torch.ones(1, 1, kernel_size, - kernel_size).to(attention_score) - overlap = F.conv_transpose2d( - ones_input, - ones_filter, - stride=self.unfold_raw_stride, - padding=self.unfold_raw_padding) - - # avoid division by zero - overlap[overlap == 0] = 1. - return overlap - - def mask_correlation_map(self, correlation_map, mask): - """Add mask weight for correlation map. - - Add a negative infinity number to the masked regions so that softmax - function will result in 'zero' in those regions. - - Args: - correlation_map (torch.Tensor): Correlation map with shape of - (n, h_unfold*w_unfold, h_map, w_map). - mask (torch.Tensor): Mask tensor with shape of (n, c, h, w). '1' - in the mask indicates masked region while '0' indicates valid - region. - - Returns: - torch.Tensor: Updated correlation map with mask. - """ - if mask is not None: - mask = F.interpolate(mask, scale_factor=self.scale) - # if any pixel is masked in patch, the patch is considered to be - # masked - mask_cols = self.im2col( - mask, - kernel_size=self.unfold_corr_kernel_size, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation) - mask_cols = (mask_cols.sum(dim=1, keepdim=True) > 0).float() - mask_cols = mask_cols.permute(0, 2, - 1).reshape(mask.size(0), -1, 1, 1) - # add negative inf will bring zero in softmax - mask_cols[mask_cols == 1] = -float('inf') - correlation_map += mask_cols - return correlation_map - - def im2col(self, - img, - kernel_size, - stride=1, - padding=0, - dilation=1, - normalize=False, - return_cols=False): - """Reshape image-style feature to columns. - - This function is used for unfold feature maps to columns. The - details of this function can be found in: - https://pytorch.org/docs/1.1.0/nn.html?highlight=unfold#torch.nn.Unfold - - Args: - img (torch.Tensor): Features to be unfolded. The shape of this - feature should be (n, c, h, w). - kernel_size (int): In this function, we only support square kernel - with same height and width. - stride (int): Stride number in unfolding. Default: 1. - padding (int): Padding number in unfolding. Default: 0. - dilation (int): Dilation number in unfolding. Default: 1. - normalize (bool): If True, the unfolded feature will be normalized. - Default: False. - return_cols (bool): The official implementation in PyTorch of - unfolding will return features with shape of - (n, c*$prod{kernel_size}$, L). If True, the features will be - reshaped to (n, L, c, kernel_size, kernel_size). Otherwise, - the results will maintain the shape as the official - implementation. - - Returns: - torch.Tensor: Unfolded columns. If `return_cols` is True, the \ - shape of output tensor is \ - `(n, L, c, kernel_size, kernel_size)`. Otherwise, the shape \ - will be `(n, c*$prod{kernel_size}$, L)`. - """ - - # unfold img to columns with shape (n, c*kernel_size**2, num_cols) - img_unfold = F.unfold( - img, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation) - # normalize the feature map - if normalize: - norm = torch.sqrt((img_unfold**2).sum(dim=1, keepdim=True)) - eps = torch.tensor([1e-4]).to(img) - img_unfold = img_unfold / torch.max(norm, eps) - - if return_cols: - img_unfold_ = img_unfold.permute(0, 2, 1) - n, num_cols = img_unfold_.size()[:2] - img_cols = img_unfold_.view(n, num_cols, img.size(1), kernel_size, - kernel_size) - return img_cols - - return img_unfold diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/conv.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/conv.py deleted file mode 100755 index 8e03d0f7d..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/conv.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import CONV_LAYERS -from torch import nn - -CONV_LAYERS.register_module('Deconv', module=nn.ConvTranspose2d) -# TODO: octave conv diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/downsample.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/downsample.py deleted file mode 100755 index 7d51a5b55..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/downsample.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def pixel_unshuffle(x, scale): - """Down-sample by pixel unshuffle. - - Args: - x (Tensor): Input tensor. - scale (int): Scale factor. - - Returns: - Tensor: Output tensor. - """ - - b, c, h, w = x.shape - if h % scale != 0 or w % scale != 0: - raise AssertionError( - f'Invalid scale ({scale}) of pixel unshuffle for tensor ' - f'with shape: {x.shape}') - h = int(h / scale) - w = int(w / scale) - x = x.view(b, c, h, scale, w, scale) - x = x.permute(0, 1, 3, 5, 2, 4) - return x.reshape(b, -1, h, w) diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/ensemble.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/ensemble.py deleted file mode 100755 index 019b6e0f0..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/ensemble.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class SpatialTemporalEnsemble(nn.Module): - """ Apply spatial and temporal ensemble and compute outputs - - Args: - is_temporal_ensemble (bool, optional): Whether to apply ensemble - temporally. If True, the sequence will also be flipped temporally. - If the input is an image, this argument must be set to False. - Default: False. - - """ - - def __init__(self, is_temporal_ensemble=False): - - super().__init__() - - self.is_temporal_ensemble = is_temporal_ensemble - - def _transform(self, imgs, mode): - """Apply spatial transform (flip, rotate) to the images. - - Args: - imgs (torch.Tensor): The images to be transformed/ - mode (str): The mode of transform. Supported values are 'vertical', - 'horizontal', and 'transpose', corresponding to vertical flip, - horizontal flip, and rotation, respectively. - - Returns: - torch.Tensor: Output of the model with spatial ensemble applied. - - """ - - is_single_image = False - if imgs.ndim == 4: - if self.is_temporal_ensemble: - raise ValueError('"is_temporal_ensemble" must be False if ' - 'the input is an image.') - is_single_image = True - imgs = imgs.unsqueeze(1) - - if mode == 'vertical': - imgs = imgs.flip(4).clone() - elif mode == 'horizontal': - imgs = imgs.flip(3).clone() - elif mode == 'transpose': - imgs = imgs.permute(0, 1, 2, 4, 3).clone() - - if is_single_image: - imgs = imgs.squeeze(1) - - return imgs - - def spatial_ensemble(self, imgs, model): - """Apply spatial ensemble. - - Args: - imgs (torch.Tensor): The images to be processed by the model. Its - size should be either (n, t, c, h, w) or (n, c, h, w). - model (nn.Module): The model to process the images. - - Returns: - torch.Tensor: Output of the model with spatial ensemble applied. - - """ - - img_list = [imgs.cpu()] - for mode in ['vertical', 'horizontal', 'transpose']: - img_list.extend([self._transform(t, mode) for t in img_list]) - - output_list = [model(t.to(imgs.device)).cpu() for t in img_list] - for i in range(len(output_list)): - if i > 3: - output_list[i] = self._transform(output_list[i], 'transpose') - if i % 4 > 1: - output_list[i] = self._transform(output_list[i], 'horizontal') - if (i % 4) % 2 == 1: - output_list[i] = self._transform(output_list[i], 'vertical') - - outputs = torch.stack(output_list, dim=0) - outputs = outputs.mean(dim=0, keepdim=False) - - return outputs.to(imgs.device) - - def forward(self, imgs, model): - """Apply spatial and temporal ensemble. - - Args: - imgs (torch.Tensor): The images to be processed by the model. Its - size should be either (n, t, c, h, w) or (n, c, h, w). - model (nn.Module): The model to process the images. - - Returns: - torch.Tensor: Output of the model with spatial ensemble applied. - - """ - outputs = self.spatial_ensemble(imgs, model) - if self.is_temporal_ensemble: - outputs += self.spatial_ensemble(imgs.flip(1), model).flip(1) - outputs *= 0.5 - - return outputs diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/flow_warp.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/flow_warp.py deleted file mode 100755 index 7083230db..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/flow_warp.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn.functional as F - - -def flow_warp(x, - flow, - interpolation='bilinear', - padding_mode='zeros', - align_corners=True): - """Warp an image or a feature map with optical flow. - - Args: - x (Tensor): Tensor with size (n, c, h, w). - flow (Tensor): Tensor with size (n, h, w, 2). The last dimension is - a two-channel, denoting the width and height relative offsets. - Note that the values are not normalized to [-1, 1]. - interpolation (str): Interpolation mode: 'nearest' or 'bilinear'. - Default: 'bilinear'. - padding_mode (str): Padding mode: 'zeros' or 'border' or 'reflection'. - Default: 'zeros'. - align_corners (bool): Whether align corners. Default: True. - - Returns: - Tensor: Warped image or feature map. - """ - if x.size()[-2:] != flow.size()[1:3]: - raise ValueError(f'The spatial sizes of input ({x.size()[-2:]}) and ' - f'flow ({flow.size()[1:3]}) are not the same.') - _, _, h, w = x.size() - # create mesh grid - device = flow.device - grid_y, grid_x = torch.meshgrid( - torch.arange(0, h, device=device, dtype=x.dtype), - torch.arange(0, w, device=device, dtype=x.dtype)) - grid = torch.stack((grid_x, grid_y), 2) # h, w, 2 - grid.requires_grad = False - - grid_flow = grid + flow - # scale grid_flow to [-1,1] - grid_flow_x = 2.0 * grid_flow[:, :, :, 0] / max(w - 1, 1) - 1.0 - grid_flow_y = 2.0 * grid_flow[:, :, :, 1] / max(h - 1, 1) - 1.0 - grid_flow = torch.stack((grid_flow_x, grid_flow_y), dim=3) - output = F.grid_sample( - x, - grid_flow, - mode=interpolation, - padding_mode=padding_mode, - align_corners=align_corners) - return output diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/gated_conv_module.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/gated_conv_module.py deleted file mode 100755 index fed22c4f5..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/gated_conv_module.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, build_activation_layer - - -class SimpleGatedConvModule(nn.Module): - """Simple Gated Convolutional Module. - - This module is a simple gated convolutional module. The detailed formula - is: - - .. math:: - y = \\phi(conv1(x)) * \\sigma(conv2(x)), - - where `phi` is the feature activation function and `sigma` is the gate - activation function. In default, the gate activation function is sigmoid. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): The number of channels of the output feature. Note - that `out_channels` in the conv module is doubled since this module - contains two convolutions for feature and gate separately. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - feat_act_cfg (dict): Config dict for feature activation layer. - gate_act_cfg (dict): Config dict for gate activation layer. - kwargs (keyword arguments): Same as `ConvModule`. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - feat_act_cfg=dict(type='ELU'), - gate_act_cfg=dict(type='Sigmoid'), - **kwargs): - super().__init__() - # the activation function should specified outside conv module - kwargs_ = copy.deepcopy(kwargs) - kwargs_['act_cfg'] = None - self.with_feat_act = feat_act_cfg is not None - self.with_gate_act = gate_act_cfg is not None - - self.conv = ConvModule(in_channels, out_channels * 2, kernel_size, - **kwargs_) - - if self.with_feat_act: - self.feat_act = build_activation_layer(feat_act_cfg) - - if self.with_gate_act: - self.gate_act = build_activation_layer(gate_act_cfg) - - def forward(self, x): - """Forward Function. - - Args: - x (torch.Tensor): Input tensor with shape of (n, c, h, w). - - Returns: - torch.Tensor: Output tensor with shape of (n, c, h', w'). - """ - x = self.conv(x) - x, gate = torch.split(x, x.size(1) // 2, dim=1) - if self.with_feat_act: - x = self.feat_act(x) - if self.with_gate_act: - gate = self.gate_act(gate) - x = x * gate - - return x diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/gca_module.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/gca_module.py deleted file mode 100755 index 75ab64e5b..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/gca_module.py +++ /dev/null @@ -1,358 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, constant_init, xavier_init -from torch.nn import functional as F - - -class GCAModule(nn.Module): - """Guided Contextual Attention Module. - - From https://arxiv.org/pdf/2001.04069.pdf. - Based on https://github.com/nbei/Deep-Flow-Guided-Video-Inpainting. - This module use image feature map to augment the alpha feature map with - guided contextual attention score. - - Image feature and alpha feature are unfolded to small patches and later - used as conv kernel. Thus, we refer the unfolding size as kernel size. - Image feature patches have a default kernel size 3 while the kernel size of - alpha feature patches could be specified by `rate` (see `rate` below). The - image feature patches are used to convolve with the image feature itself - to calculate the contextual attention. Then the attention feature map is - convolved by alpha feature patches to obtain the attention alpha feature. - At last, the attention alpha feature is added to the input alpha feature. - - Args: - in_channels (int): Input channels of the guided contextual attention - module. - out_channels (int): Output channels of the guided contextual attention - module. - kernel_size (int): Kernel size of image feature patches. Default 3. - stride (int): Stride when unfolding the image feature. Default 1. - rate (int): The downsample rate of image feature map. The corresponding - kernel size and stride of alpha feature patches will be `rate x 2` - and `rate`. It could be regarded as the granularity of the gca - module. Default: 2. - pad_args (dict): Parameters of padding when convolve image feature with - image feature patches or alpha feature patches. Allowed keys are - `mode` and `value`. See torch.nn.functional.pad() for more - information. Default: dict(mode='reflect'). - interpolation (str): Interpolation method in upsampling and - downsampling. - penalty (float): Punishment hyperparameter to avoid a large correlation - between each unknown patch and itself. - eps (float): A small number to avoid dividing by 0 when calculating - the normed image feature patch. Default: 1e-4. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=3, - stride=1, - rate=2, - pad_args=dict(mode='reflect'), - interpolation='nearest', - penalty=-1e4, - eps=1e-4): - super().__init__() - self.kernel_size = kernel_size - self.stride = stride - self.rate = rate - self.pad_args = pad_args - self.interpolation = interpolation - self.penalty = penalty - self.eps = eps - - # reduced the channels of input image feature. - self.guidance_conv = nn.Conv2d(in_channels, in_channels // 2, 1) - - # convolution after the attention alpha feature - self.out_conv = ConvModule( - out_channels, - out_channels, - 1, - norm_cfg=dict(type='BN'), - act_cfg=None) - - self.init_weights() - - def init_weights(self): - xavier_init(self.guidance_conv, distribution='uniform') - xavier_init(self.out_conv.conv, distribution='uniform') - constant_init(self.out_conv.norm, 1e-3) - - def forward(self, img_feat, alpha_feat, unknown=None, softmax_scale=1.): - """Forward function of GCAModule. - - Args: - img_feat (Tensor): Image feature map of shape - (N, ori_c, ori_h, ori_w). - alpha_feat (Tensor): Alpha feature map of shape - (N, alpha_c, ori_h, ori_w). - unknown (Tensor, optional): Unknown area map generated by trimap. - If specified, this tensor should have shape - (N, 1, ori_h, ori_w). - softmax_scale (float, optional): The softmax scale of the attention - if unknown area is not provided in forward. Default: 1. - - Returns: - Tensor: The augmented alpha feature. - """ - - if alpha_feat.shape[2:4] != img_feat.shape[2:4]: - raise ValueError( - 'image feature size does not align with alpha feature size: ' - f'image feature size {img_feat.shape[2:4]}, ' - f'alpha feature size {alpha_feat.shape[2:4]}') - - if unknown is not None and unknown.shape[2:4] != img_feat.shape[2:4]: - raise ValueError( - 'image feature size does not align with unknown mask size: ' - f'image feature size {img_feat.shape[2:4]}, ' - f'unknown mask size {unknown.shape[2:4]}') - - # preprocess image feature - img_feat = self.guidance_conv(img_feat) - img_feat = F.interpolate( - img_feat, scale_factor=1 / self.rate, mode=self.interpolation) - - # preprocess unknown mask - unknown, softmax_scale = self.process_unknown_mask( - unknown, img_feat, softmax_scale) - - img_ps, alpha_ps, unknown_ps = self.extract_feature_maps_patches( - img_feat, alpha_feat, unknown) - - # create self correlation mask with shape: - # (N, img_h*img_w, img_h, img_w) - self_mask = self.get_self_correlation_mask(img_feat) - - # split tensors by batch dimension; tuple is returned - img_groups = torch.split(img_feat, 1, dim=0) - img_ps_groups = torch.split(img_ps, 1, dim=0) - alpha_ps_groups = torch.split(alpha_ps, 1, dim=0) - unknown_ps_groups = torch.split(unknown_ps, 1, dim=0) - scale_groups = torch.split(softmax_scale, 1, dim=0) - groups = (img_groups, img_ps_groups, alpha_ps_groups, - unknown_ps_groups, scale_groups) - - out = [] - # i is the virtual index of the sample in the current batch - for img_i, img_ps_i, alpha_ps_i, unknown_ps_i, scale_i in zip(*groups): - similarity_map = self.compute_similarity_map(img_i, img_ps_i) - - gca_score = self.compute_guided_attention_score( - similarity_map, unknown_ps_i, scale_i, self_mask) - - out_i = self.propagate_alpha_feature(gca_score, alpha_ps_i) - - out.append(out_i) - - out = torch.cat(out, dim=0) - out.reshape_as(alpha_feat) - - out = self.out_conv(out) + alpha_feat - return out - - def extract_feature_maps_patches(self, img_feat, alpha_feat, unknown): - """Extract image feature, alpha feature unknown patches. - - Args: - img_feat (Tensor): Image feature map of shape - (N, img_c, img_h, img_w). - alpha_feat (Tensor): Alpha feature map of shape - (N, alpha_c, ori_h, ori_w). - unknown (Tensor, optional): Unknown area map generated by trimap of - shape (N, 1, img_h, img_w). - - Returns: - tuple: 3-tuple of - - ``Tensor``: Image feature patches of shape \ - (N, img_h*img_w, img_c, img_ks, img_ks). - - ``Tensor``: Guided contextual attention alpha feature map. \ - (N, img_h*img_w, alpha_c, alpha_ks, alpha_ks). - - ``Tensor``: Unknown mask of shape (N, img_h*img_w, 1, 1). - """ - # extract image feature patches with shape: - # (N, img_h*img_w, img_c, img_ks, img_ks) - img_ks = self.kernel_size - img_ps = self.extract_patches(img_feat, img_ks, self.stride) - - # extract alpha feature patches with shape: - # (N, img_h*img_w, alpha_c, alpha_ks, alpha_ks) - alpha_ps = self.extract_patches(alpha_feat, self.rate * 2, self.rate) - - # extract unknown mask patches with shape: (N, img_h*img_w, 1, 1) - unknown_ps = self.extract_patches(unknown, img_ks, self.stride) - unknown_ps = unknown_ps.squeeze(dim=2) # squeeze channel dimension - unknown_ps = unknown_ps.mean(dim=[2, 3], keepdim=True) - - return img_ps, alpha_ps, unknown_ps - - def compute_similarity_map(self, img_feat, img_ps): - """Compute similarity between image feature patches. - - Args: - img_feat (Tensor): Image feature map of shape - (1, img_c, img_h, img_w). - img_ps (Tensor): Image feature patches tensor of shape - (1, img_h*img_w, img_c, img_ks, img_ks). - - Returns: - Tensor: Similarity map between image feature patches with shape \ - (1, img_h*img_w, img_h, img_w). - """ - img_ps = img_ps[0] # squeeze dim 0 - # convolve the feature to get correlation (similarity) map - escape_NaN = torch.FloatTensor([self.eps]).to(img_feat) - img_ps_normed = img_ps / torch.max(self.l2_norm(img_ps), escape_NaN) - img_feat = self.pad(img_feat, self.kernel_size, self.stride) - similarity_map = F.conv2d(img_feat, img_ps_normed) - - return similarity_map - - def compute_guided_attention_score(self, similarity_map, unknown_ps, scale, - self_mask): - """Compute guided attention score. - - Args: - similarity_map (Tensor): Similarity map of image feature with shape - (1, img_h*img_w, img_h, img_w). - unknown_ps (Tensor): Unknown area patches tensor of shape - (1, img_h*img_w, 1, 1). - scale (Tensor): Softmax scale of known and unknown area: - [unknown_scale, known_scale]. - self_mask (Tensor): Self correlation mask of shape - (1, img_h*img_w, img_h, img_w). At (1, i*i, i, i) mask value - equals -1e4 for i in [1, img_h*img_w] and other area is all - zero. - - Returns: - Tensor: Similarity map between image feature patches with shape \ - (1, img_h*img_w, img_h, img_w). - """ - # scale the correlation with predicted scale factor for known and - # unknown area - unknown_scale, known_scale = scale[0] - out = similarity_map * ( - unknown_scale * unknown_ps.gt(0.).float() + - known_scale * unknown_ps.le(0.).float()) - # mask itself, self-mask only applied to unknown area - out = out + self_mask * unknown_ps - gca_score = F.softmax(out, dim=1) - - return gca_score - - def propagate_alpha_feature(self, gca_score, alpha_ps): - """Propagate alpha feature based on guided attention score. - - Args: - gca_score (Tensor): Guided attention score map of shape - (1, img_h*img_w, img_h, img_w). - alpha_ps (Tensor): Alpha feature patches tensor of shape - (1, img_h*img_w, alpha_c, alpha_ks, alpha_ks). - - Returns: - Tensor: Propagated alpha feature map of shape \ - (1, alpha_c, alpha_h, alpha_w). - """ - alpha_ps = alpha_ps[0] # squeeze dim 0 - if self.rate == 1: - gca_score = self.pad(gca_score, kernel_size=2, stride=1) - alpha_ps = alpha_ps.permute(1, 0, 2, 3) - out = F.conv2d(gca_score, alpha_ps) / 4. - else: - out = F.conv_transpose2d( - gca_score, alpha_ps, stride=self.rate, padding=1) / 4. - - return out - - def process_unknown_mask(self, unknown, img_feat, softmax_scale): - """Process unknown mask. - - Args: - unknown (Tensor, optional): Unknown area map generated by trimap of - shape (N, 1, ori_h, ori_w) - img_feat (Tensor): The interpolated image feature map of shape - (N, img_c, img_h, img_w). - softmax_scale (float, optional): The softmax scale of the attention - if unknown area is not provided in forward. Default: 1. - - Returns: - tuple: 2-tuple of - - ``Tensor``: Interpolated unknown area map of shape \ - (N, img_h*img_w, img_h, img_w). - - ``Tensor``: Softmax scale tensor of known and unknown area of \ - shape (N, 2). - """ - n, _, h, w = img_feat.shape - - if unknown is not None: - unknown = unknown.clone() - unknown = F.interpolate( - unknown, scale_factor=1 / self.rate, mode=self.interpolation) - unknown_mean = unknown.mean(dim=[2, 3]) - known_mean = 1 - unknown_mean - unknown_scale = torch.clamp( - torch.sqrt(unknown_mean / known_mean), 0.1, 10).to(img_feat) - known_scale = torch.clamp( - torch.sqrt(known_mean / unknown_mean), 0.1, 10).to(img_feat) - softmax_scale = torch.cat([unknown_scale, known_scale], dim=1) - else: - unknown = torch.ones((n, 1, h, w)).to(img_feat) - softmax_scale = torch.FloatTensor( - [softmax_scale, - softmax_scale]).view(1, 2).repeat(n, 1).to(img_feat) - - return unknown, softmax_scale - - def extract_patches(self, x, kernel_size, stride): - """Extract feature patches. - - The feature map will be padded automatically to make sure the number of - patches is equal to `(H / stride) * (W / stride)`. - - Args: - x (Tensor): Feature map of shape (N, C, H, W). - kernel_size (int): Size of each patches. - stride (int): Stride between patches. - - Returns: - Tensor: Extracted patches of shape \ - (N, (H / stride) * (W / stride) , C, kernel_size, kernel_size). - """ - n, c, _, _ = x.shape - x = self.pad(x, kernel_size, stride) - x = F.unfold(x, (kernel_size, kernel_size), stride=(stride, stride)) - x = x.permute(0, 2, 1) - x = x.reshape(n, -1, c, kernel_size, kernel_size) - return x - - def pad(self, x, kernel_size, stride): - left = (kernel_size - stride + 1) // 2 - right = (kernel_size - stride) // 2 - pad = (left, right, left, right) - return F.pad(x, pad, **self.pad_args) - - def get_self_correlation_mask(self, img_feat): - _, _, h, w = img_feat.shape - # As ONNX does not support dynamic num_classes, we have to convert it - # into an integer - self_mask = F.one_hot( - torch.arange(h * w).view(h, w), num_classes=int(h * w)) - self_mask = self_mask.permute(2, 0, 1).view(1, h * w, h, w) - # use large negative value to mask out self-correlation before softmax - self_mask = self_mask * self.penalty - return self_mask.to(img_feat) - - @staticmethod - def l2_norm(x): - x = x**2 - x = x.sum(dim=[1, 2, 3], keepdim=True) - return torch.sqrt(x) diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/generation_model_utils.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/generation_model_utils.py deleted file mode 100755 index 21d45fbe3..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/generation_model_utils.py +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, kaiming_init, normal_init, xavier_init -from torch.nn import init - - -def generation_init_weights(module, init_type='normal', init_gain=0.02): - """Default initialization of network weights for image generation. - - By default, we use normal init, but xavier and kaiming might work - better for some applications. - - Args: - module (nn.Module): Module to be initialized. - init_type (str): The name of an initialization method: - normal | xavier | kaiming | orthogonal. - init_gain (float): Scaling factor for normal, xavier and - orthogonal. - """ - - def init_func(m): - """Initialization function. - - Args: - m (nn.Module): Module to be initialized. - """ - classname = m.__class__.__name__ - if hasattr(m, 'weight') and (classname.find('Conv') != -1 - or classname.find('Linear') != -1): - if init_type == 'normal': - normal_init(m, 0.0, init_gain) - elif init_type == 'xavier': - xavier_init(m, gain=init_gain, distribution='normal') - elif init_type == 'kaiming': - kaiming_init( - m, - a=0, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='normal') - elif init_type == 'orthogonal': - init.orthogonal_(m.weight, gain=init_gain) - init.constant_(m.bias.data, 0.0) - else: - raise NotImplementedError( - f"Initialization method '{init_type}' is not implemented") - elif classname.find('BatchNorm2d') != -1: - # BatchNorm Layer's weight is not a matrix; - # only normal distribution applies. - normal_init(m, 1.0, init_gain) - - module.apply(init_func) - - -class GANImageBuffer: - """This class implements an image buffer that stores previously - generated images. - - This buffer allows us to update the discriminator using a history of - generated images rather than the ones produced by the latest generator - to reduce model oscillation. - - Args: - buffer_size (int): The size of image buffer. If buffer_size = 0, - no buffer will be created. - buffer_ratio (float): The chance / possibility to use the images - previously stored in the buffer. - """ - - def __init__(self, buffer_size, buffer_ratio=0.5): - self.buffer_size = buffer_size - # create an empty buffer - if self.buffer_size > 0: - self.img_num = 0 - self.image_buffer = [] - self.buffer_ratio = buffer_ratio - - def query(self, images): - """Query current image batch using a history of generated images. - - Args: - images (Tensor): Current image batch without history information. - """ - if self.buffer_size == 0: # if the buffer size is 0, do nothing - return images - return_images = [] - for image in images: - image = torch.unsqueeze(image.data, 0) - # if the buffer is not full, keep inserting current images - if self.img_num < self.buffer_size: - self.img_num = self.img_num + 1 - self.image_buffer.append(image) - return_images.append(image) - else: - use_buffer = np.random.random() < self.buffer_ratio - # by self.buffer_ratio, the buffer will return a previously - # stored image, and insert the current image into the buffer - if use_buffer: - random_id = np.random.randint(0, self.buffer_size) - image_tmp = self.image_buffer[random_id].clone() - self.image_buffer[random_id] = image - return_images.append(image_tmp) - # by (1 - self.buffer_ratio), the buffer will return the - # current image - else: - return_images.append(image) - # collect all the images and return - return_images = torch.cat(return_images, 0) - return return_images - - -class UnetSkipConnectionBlock(nn.Module): - """Construct a Unet submodule with skip connections, with the following - structure: downsampling - `submodule` - upsampling. - - Args: - outer_channels (int): Number of channels at the outer conv layer. - inner_channels (int): Number of channels at the inner conv layer. - in_channels (int): Number of channels in input images/features. If is - None, equals to `outer_channels`. Default: None. - submodule (UnetSkipConnectionBlock): Previously constructed submodule. - Default: None. - is_outermost (bool): Whether this module is the outermost module. - Default: False. - is_innermost (bool): Whether this module is the innermost module. - Default: False. - norm_cfg (dict): Config dict to build norm layer. Default: - `dict(type='BN')`. - use_dropout (bool): Whether to use dropout layers. Default: False. - """ - - def __init__(self, - outer_channels, - inner_channels, - in_channels=None, - submodule=None, - is_outermost=False, - is_innermost=False, - norm_cfg=dict(type='BN'), - use_dropout=False): - super().__init__() - # cannot be both outermost and innermost - assert not (is_outermost and is_innermost), ( - "'is_outermost' and 'is_innermost' cannot be True" - 'at the same time.') - self.is_outermost = is_outermost - assert isinstance(norm_cfg, dict), ("'norm_cfg' should be dict, but" - f'got {type(norm_cfg)}') - assert 'type' in norm_cfg, "'norm_cfg' must have key 'type'" - # We use norm layers in the unet skip connection block. - # Only for IN, use bias since it does not have affine parameters. - use_bias = norm_cfg['type'] == 'IN' - - kernel_size = 4 - stride = 2 - padding = 1 - if in_channels is None: - in_channels = outer_channels - down_conv_cfg = dict(type='Conv2d') - down_norm_cfg = norm_cfg - down_act_cfg = dict(type='LeakyReLU', negative_slope=0.2) - up_conv_cfg = dict(type='Deconv') - up_norm_cfg = norm_cfg - up_act_cfg = dict(type='ReLU') - up_in_channels = inner_channels * 2 - up_bias = use_bias - middle = [submodule] - upper = [] - - if is_outermost: - down_act_cfg = None - down_norm_cfg = None - up_bias = True - up_norm_cfg = None - upper = [nn.Tanh()] - elif is_innermost: - down_norm_cfg = None - up_in_channels = inner_channels - middle = [] - else: - upper = [nn.Dropout(0.5)] if use_dropout else [] - - down = [ - ConvModule( - in_channels=in_channels, - out_channels=inner_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - bias=use_bias, - conv_cfg=down_conv_cfg, - norm_cfg=down_norm_cfg, - act_cfg=down_act_cfg, - order=('act', 'conv', 'norm')) - ] - up = [ - ConvModule( - in_channels=up_in_channels, - out_channels=outer_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - bias=up_bias, - conv_cfg=up_conv_cfg, - norm_cfg=up_norm_cfg, - act_cfg=up_act_cfg, - order=('act', 'conv', 'norm')) - ] - - model = down + middle + up + upper - - self.model = nn.Sequential(*model) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - if self.is_outermost: - return self.model(x) - - # add skip connections - return torch.cat([x, self.model(x)], 1) - - -class ResidualBlockWithDropout(nn.Module): - """Define a Residual Block with dropout layers. - - Ref: - Deep Residual Learning for Image Recognition - - A residual block is a conv block with skip connections. A dropout layer is - added between two common conv modules. - - Args: - channels (int): Number of channels in the conv layer. - padding_mode (str): The name of padding layer: - 'reflect' | 'replicate' | 'zeros'. - norm_cfg (dict): Config dict to build norm layer. Default: - `dict(type='IN')`. - use_dropout (bool): Whether to use dropout layers. Default: True. - """ - - def __init__(self, - channels, - padding_mode, - norm_cfg=dict(type='BN'), - use_dropout=True): - super().__init__() - assert isinstance(norm_cfg, dict), ("'norm_cfg' should be dict, but" - f'got {type(norm_cfg)}') - assert 'type' in norm_cfg, "'norm_cfg' must have key 'type'" - # We use norm layers in the residual block with dropout layers. - # Only for IN, use bias since it does not have affine parameters. - use_bias = norm_cfg['type'] == 'IN' - - block = [ - ConvModule( - in_channels=channels, - out_channels=channels, - kernel_size=3, - padding=1, - bias=use_bias, - norm_cfg=norm_cfg, - padding_mode=padding_mode) - ] - - if use_dropout: - block += [nn.Dropout(0.5)] - - block += [ - ConvModule( - in_channels=channels, - out_channels=channels, - kernel_size=3, - padding=1, - bias=use_bias, - norm_cfg=norm_cfg, - act_cfg=None, - padding_mode=padding_mode) - ] - - self.block = nn.Sequential(*block) - - def forward(self, x): - """Forward function. Add skip connections without final ReLU. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - out = x + self.block(x) - return out diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/img_normalize.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/img_normalize.py deleted file mode 100755 index 1bd8f76aa..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/img_normalize.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class ImgNormalize(nn.Conv2d): - """Normalize images with the given mean and std value. - - Based on Conv2d layer, can work in GPU. - - Args: - pixel_range (float): Pixel range of feature. - img_mean (Tuple[float]): Image mean of each channel. - img_std (Tuple[float]): Image std of each channel. - sign (int): Sign of bias. Default -1. - """ - - def __init__(self, pixel_range, img_mean, img_std, sign=-1): - - assert len(img_mean) == len(img_std) - num_channels = len(img_mean) - super().__init__(num_channels, num_channels, kernel_size=1) - - std = torch.Tensor(img_std) - self.weight.data = torch.eye(num_channels).view( - num_channels, num_channels, 1, 1) - self.weight.data.div_(std.view(num_channels, 1, 1, 1)) - self.bias.data = sign * pixel_range * torch.Tensor(img_mean) - self.bias.data.div_(std) - - self.weight.requires_grad = False - self.bias.requires_grad = False diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/linear_module.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/linear_module.py deleted file mode 100755 index 5101ad164..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/linear_module.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import build_activation_layer, kaiming_init - - -class LinearModule(nn.Module): - """A linear block that contains linear/norm/activation layers. - - For low level vision, we add spectral norm and padding layer. - - Args: - in_features (int): Same as nn.Linear. - out_features (int): Same as nn.Linear. - bias (bool): Same as nn.Linear. - act_cfg (dict): Config dict for activation layer, "relu" by default. - inplace (bool): Whether to use inplace mode for activation. - with_spectral_norm (bool): Whether use spectral norm in linear module. - order (tuple[str]): The order of linear/activation layers. It is a - sequence of "linear", "norm" and "act". Examples are - ("linear", "act") and ("act", "linear"). - """ - - def __init__(self, - in_features, - out_features, - bias=True, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - order=('linear', 'act')): - super().__init__() - assert act_cfg is None or isinstance(act_cfg, dict) - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 2 - assert set(order) == set(['linear', 'act']) - - self.with_activation = act_cfg is not None - self.with_bias = bias - - # build linear layer - self.linear = nn.Linear(in_features, out_features, bias=bias) - # export the attributes of self.linear to a higher level for - # convenience - self.in_features = self.linear.in_features - self.out_features = self.linear.out_features - - if self.with_spectral_norm: - self.linear = nn.utils.spectral_norm(self.linear) - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - def init_weights(self): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - - kaiming_init(self.linear, a=a, nonlinearity=nonlinearity) - - def forward(self, x, activate=True): - """Forward Function. - - Args: - x (torch.Tensor): Input tensor with shape of :math:`(n, *, c)`. - Same as ``torch.nn.Linear``. - activate (bool, optional): Whether to use activation layer. - Defaults to True. - - Returns: - torch.Tensor: Same as ``torch.nn.Linear``. - """ - for layer in self.order: - if layer == 'linear': - x = self.linear(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/mask_conv_module.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/mask_conv_module.py deleted file mode 100755 index e70868686..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/mask_conv_module.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import ConvModule - - -class MaskConvModule(ConvModule): - """Mask convolution module. - - This is a simple wrapper for mask convolution like: 'partial conv'. - Convolutions in this module always need a mask as extra input. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - stride (int or tuple[int]): Same as nn.Conv2d. - padding (int or tuple[int]): Same as nn.Conv2d. - dilation (int or tuple[int]): Same as nn.Conv2d. - groups (int): Same as nn.Conv2d. - bias (bool or str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if norm_cfg is None, otherwise - False. - conv_cfg (dict): Config dict for convolution layer. - norm_cfg (dict): Config dict for normalization layer. - act_cfg (dict): Config dict for activation layer, "relu" by default. - inplace (bool): Whether to use inplace mode for activation. - with_spectral_norm (bool): Whether use spectral norm in conv module. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in Pytorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - """ - supported_conv_list = ['PConv'] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.conv_cfg['type'] in self.supported_conv_list - - self.init_weights() - - def forward(self, - x, - mask=None, - activate=True, - norm=True, - return_mask=True): - """Forward function for partial conv2d. - - Args: - input (torch.Tensor): Tensor with shape of (n, c, h, w). - mask (torch.Tensor): Tensor with shape of (n, c, h, w) or - (n, 1, h, w). If mask is not given, the function will - work as standard conv2d. Default: None. - activate (bool): Whether use activation layer. - norm (bool): Whether use norm layer. - return_mask (bool): If True and mask is not None, the updated - mask will be returned. Default: True. - - Returns: - Tensor or tuple: Result Tensor or 2-tuple of - - ``Tensor``: Results after partial conv. - - ``Tensor``: Updated mask will be returned if mask is given \ - and `return_mask` is True. - """ - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - mask = self.padding_layer(mask) - if return_mask: - x, updated_mask = self.conv( - x, mask, return_mask=return_mask) - else: - x = self.conv(x, mask, return_mask=False) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - - if return_mask: - return x, updated_mask - - return x diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/model_utils.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/model_utils.py deleted file mode 100755 index 62135c6e6..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/model_utils.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch - - -def set_requires_grad(nets, requires_grad=False): - """Set requires_grad for all the networks. - - Args: - nets (nn.Module | list[nn.Module]): A list of networks or a single - network. - requires_grad (bool): Whether the networks require gradients or not - """ - if not isinstance(nets, list): - nets = [nets] - for net in nets: - if net is not None: - for param in net.parameters(): - param.requires_grad = requires_grad - - -def extract_bbox_patch(bbox, img, channel_first=True): - """Extract patch from a given bbox - - Args: - bbox (torch.Tensor | numpy.array): Bbox with (top, left, h, w). If - `img` has batch dimension, the `bbox` must be stacked at first - dimension. The shape should be (4,) or (n, 4). - img (torch.Tensor | numpy.array): Image data to be extracted. If - organized in batch dimension, the batch dimension must be the first - order like (n, h, w, c) or (n, c, h, w). - channel_first (bool): If True, the channel dimension of img is before - height and width, e.g. (c, h, w). Otherwise, the img shape (samples - in the batch) is like (h, w, c). - - Returns: - (torch.Tensor | numpy.array): Extracted patches. The dimension of the \ - output should be the same as `img`. - """ - - def _extract(bbox, img): - assert len(bbox) == 4 - t, l, h, w = bbox - if channel_first: - img_patch = img[..., t:t + h, l:l + w] - else: - img_patch = img[t:t + h, l:l + w, ...] - - return img_patch - - input_size = img.shape - assert len(input_size) == 3 or len(input_size) == 4 - bbox_size = bbox.shape - assert bbox_size == (4, ) or (len(bbox_size) == 2 - and bbox_size[0] == input_size[0]) - - # images with batch dimension - if len(input_size) == 4: - output_list = [] - for i in range(input_size[0]): - img_patch_ = _extract(bbox[i], img[i:i + 1, ...]) - output_list.append(img_patch_) - if isinstance(img, torch.Tensor): - img_patch = torch.cat(output_list, dim=0) - else: - img_patch = np.concatenate(output_list, axis=0) - # standardize image - else: - img_patch = _extract(bbox, img) - - return img_patch - - -def scale_bbox(bbox, target_size): - """Modify bbox to target size. - - The original bbox will be enlarged to the target size with the original - bbox in the center of the new bbox. - - Args: - bbox (np.ndarray | torch.Tensor): Bboxes to be modified. Bbox can - be in batch or not. The shape should be (4,) or (n, 4). - target_size (tuple[int]): Target size of final bbox. - - Returns: - (np.ndarray | torch.Tensor): Modified bboxes. - """ - - def _mod(bbox, target_size): - top_ori, left_ori, h_ori, w_ori = bbox - h, w = target_size - assert h >= h_ori and w >= w_ori - top = int(max(0, top_ori - (h - h_ori) // 2)) - left = int(max(0, left_ori - (w - w_ori) // 2)) - - if isinstance(bbox, torch.Tensor): - bbox_new = torch.Tensor([top, left, h, w]).type_as(bbox) - else: - bbox_new = np.asarray([top, left, h, w]) - - return bbox_new - - if isinstance(bbox, torch.Tensor): - bbox_new = torch.zeros_like(bbox) - elif isinstance(bbox, np.ndarray): - bbox_new = np.zeros_like(bbox) - else: - raise TypeError('bbox mush be torch.Tensor or numpy.ndarray' - f'but got type {type(bbox)}') - bbox_shape = list(bbox.shape) - - if len(bbox_shape) == 2: - for i in range(bbox_shape[0]): - bbox_new[i, :] = _mod(bbox[i], target_size) - else: - bbox_new = _mod(bbox, target_size) - - return bbox_new - - -def extract_around_bbox(img, bbox, target_size, channel_first=True): - """Extract patches around the given bbox. - - Args: - bbox (np.ndarray | torch.Tensor): Bboxes to be modified. Bbox can - be in batch or not. - target_size (List(int)): Target size of final bbox. - - Returns: - (torch.Tensor | numpy.array): Extracted patches. The dimension of the \ - output should be the same as `img`. - """ - bbox_new = scale_bbox(bbox, target_size) - img_patch = extract_bbox_patch(bbox_new, img, channel_first=channel_first) - - return img_patch, bbox_new diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/partial_conv.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/partial_conv.py deleted file mode 100755 index 51b50feca..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/partial_conv.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import CONV_LAYERS - - -@CONV_LAYERS.register_module(name='PConv') -class PartialConv2d(nn.Conv2d): - """Implementation for partial convolution. - - Image Inpainting for Irregular Holes Using Partial Convolutions - [https://arxiv.org/abs/1804.07723] - - Args: - multi_channel (bool): If True, the mask is multi-channel. Otherwise, - the mask is single-channel. - eps (float): Need to be changed for mixed precision training. - For mixed precision training, you need change 1e-8 to 1e-6. - """ - - def __init__(self, *args, multi_channel=False, eps=1e-8, **kwargs): - super().__init__(*args, **kwargs) - - # whether the mask is multi-channel or not - self.multi_channel = multi_channel - self.eps = eps - - if self.multi_channel: - out_channels, in_channels = self.out_channels, self.in_channels - else: - out_channels, in_channels = 1, 1 - - self.register_buffer( - 'weight_mask_updater', - torch.ones(out_channels, in_channels, self.kernel_size[0], - self.kernel_size[1])) - - self.mask_kernel_numel = np.prod(self.weight_mask_updater.shape[1:4]) - self.mask_kernel_numel = (self.mask_kernel_numel).item() - - def forward(self, input, mask=None, return_mask=True): - """Forward function for partial conv2d. - - Args: - input (torch.Tensor): Tensor with shape of (n, c, h, w). - mask (torch.Tensor): Tensor with shape of (n, c, h, w) or - (n, 1, h, w). If mask is not given, the function will - work as standard conv2d. Default: None. - return_mask (bool): If True and mask is not None, the updated - mask will be returned. Default: True. - - Returns: - torch.Tensor : Results after partial conv.\ - torch.Tensor : Updated mask will be returned if mask is given and \ - ``return_mask`` is True. - """ - assert input.dim() == 4 - if mask is not None: - assert mask.dim() == 4 - if self.multi_channel: - assert mask.shape[1] == input.shape[1] - else: - assert mask.shape[1] == 1 - - # update mask and compute mask ratio - if mask is not None: - with torch.no_grad(): - - updated_mask = F.conv2d( - mask, - self.weight_mask_updater, - bias=None, - stride=self.stride, - padding=self.padding, - dilation=self.dilation) - mask_ratio = self.mask_kernel_numel / (updated_mask + self.eps) - - updated_mask = torch.clamp(updated_mask, 0, 1) - mask_ratio = mask_ratio * updated_mask - - # standard conv2d - if mask is not None: - input = input * mask - raw_out = super().forward(input) - - if mask is not None: - if self.bias is None: - output = raw_out * mask_ratio - else: - # compute new bias when mask is given - bias_view = self.bias.view(1, self.out_channels, 1, 1) - output = (raw_out - bias_view) * mask_ratio + bias_view - output = output * updated_mask - else: - output = raw_out - - if return_mask and mask is not None: - return output, updated_mask - - return output diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/separable_conv_module.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/separable_conv_module.py deleted file mode 100755 index 139a54e58..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/separable_conv_module.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if ``norm_cfg`` and ``act_cfg`` are specified. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - stride (int or tuple[int]): Same as nn.Conv2d. Default: 1. - padding (int or tuple[int]): Same as nn.Conv2d. Default: 0. - dilation (int or tuple[int]): Same as nn.Conv2d. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as ``norm_cfg``. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as ``act_cfg``. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as ``act_cfg``. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super().__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (N, C, H, W). - - Returns: - Tensor: Output tensor. - """ - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/sr_backbone_utils.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/sr_backbone_utils.py deleted file mode 100755 index b4b0aad91..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/sr_backbone_utils.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import constant_init, kaiming_init -from mmcv.utils.parrots_wrapper import _BatchNorm - - -def default_init_weights(module, scale=1): - """Initialize network weights. - - Args: - modules (nn.Module): Modules to be initialized. - scale (float): Scale initialized weights, especially for residual - blocks. - """ - for m in module.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m, a=0, mode='fan_in', bias=0) - m.weight.data *= scale - elif isinstance(m, nn.Linear): - kaiming_init(m, a=0, mode='fan_in', bias=0) - m.weight.data *= scale - elif isinstance(m, _BatchNorm): - constant_init(m.weight, val=1, bias=0) - - -def make_layer(block, num_blocks, **kwarg): - """Make layers by stacking the same blocks. - - Args: - block (nn.module): nn.module class for basic block. - num_blocks (int): number of blocks. - - Returns: - nn.Sequential: Stacked blocks in nn.Sequential. - """ - layers = [] - for _ in range(num_blocks): - layers.append(block(**kwarg)) - return nn.Sequential(*layers) - - -class ResidualBlockNoBN(nn.Module): - """Residual block without BN. - - It has a style of: - - :: - - ---Conv-ReLU-Conv-+- - |________________| - - Args: - mid_channels (int): Channel number of intermediate features. - Default: 64. - res_scale (float): Used to scale the residual before addition. - Default: 1.0. - """ - - def __init__(self, mid_channels=64, res_scale=1.0): - super().__init__() - self.res_scale = res_scale - self.conv1 = nn.Conv2d(mid_channels, mid_channels, 3, 1, 1, bias=True) - self.conv2 = nn.Conv2d(mid_channels, mid_channels, 3, 1, 1, bias=True) - - self.relu = nn.ReLU(inplace=True) - - # if res_scale < 1.0, use the default initialization, as in EDSR. - # if res_scale = 1.0, use scaled kaiming_init, as in MSRResNet. - if res_scale == 1.0: - self.init_weights() - - def init_weights(self): - """Initialize weights for ResidualBlockNoBN. - - Initialization methods like `kaiming_init` are for VGG-style - modules. For modules with residual paths, using smaller std is - better for stability and performance. We empirically use 0.1. - See more details in "ESRGAN: Enhanced Super-Resolution Generative - Adversarial Networks" - """ - - for m in [self.conv1, self.conv2]: - default_init_weights(m, 0.1) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - identity = x - out = self.conv2(self.relu(self.conv1(x))) - return identity + out * self.res_scale diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/common/upsample.py b/cv/super_resolution/liif/pytorch/mmedit/models/common/upsample.py deleted file mode 100755 index f39ec1a9e..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/common/upsample.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from .sr_backbone_utils import default_init_weights - - -class PixelShufflePack(nn.Module): - """ Pixel Shuffle upsample layer. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of Conv layer to expand channels. - - Returns: - Upsampled feature map. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super().__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - """Initialize weights for PixelShufflePack. - """ - default_init_weights(self, 1) - - def forward(self, x): - """Forward function for PixelShufflePack. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/components/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/models/components/__init__.py deleted file mode 100755 index 7c6238312..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/components/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .refiners import MLPRefiner - diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/components/refiners/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/models/components/refiners/__init__.py deleted file mode 100755 index 5e9de0729..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/components/refiners/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .mlp_refiner import MLPRefiner diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/components/refiners/mlp_refiner.py b/cv/super_resolution/liif/pytorch/mmedit/models/components/refiners/mlp_refiner.py deleted file mode 100755 index 1d6e09f33..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/components/refiners/mlp_refiner.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.runner import load_checkpoint - -from mmedit.models.registry import COMPONENTS -from mmedit.utils import get_root_logger - - -@COMPONENTS.register_module() -class MLPRefiner(nn.Module): - """Multilayer perceptrons (MLPs), refiner used in LIIF. - - Args: - in_dim (int): Input dimension. - out_dim (int): Output dimension. - hidden_list (list[int]): List of hidden dimensions. - """ - - def __init__(self, in_dim, out_dim, hidden_list): - super().__init__() - layers = [] - lastv = in_dim - for hidden in hidden_list: - layers.append(nn.Linear(lastv, hidden)) - layers.append(nn.ReLU()) - lastv = hidden - layers.append(nn.Linear(lastv, out_dim)) - self.layers = nn.Sequential(*layers) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): The input of MLP. - - Returns: - Tensor: The output of MLP. - """ - shape = x.shape[:-1] - x = self.layers(x.view(-1, x.shape[-1])) - return x.view(*shape, -1) - - def init_weights(self, pretrained=None, strict=True): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - strict (boo, optional): Whether strictly load the pretrained model. - Defaults to True. - """ - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=strict, logger=logger) - elif pretrained is None: - pass - else: - raise TypeError(f'"pretrained" must be a str or None. ' - f'But received {type(pretrained)}.') diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/losses/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/models/losses/__init__.py deleted file mode 100755 index 6fa5741d6..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/losses/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .pixelwise_loss import CharbonnierLoss, L1Loss, MaskedTVLoss, MSELoss diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/losses/pixelwise_loss.py b/cv/super_resolution/liif/pytorch/mmedit/models/losses/pixelwise_loss.py deleted file mode 100755 index 2f2435b8d..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/losses/pixelwise_loss.py +++ /dev/null @@ -1,221 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..registry import LOSSES -from .utils import masked_loss - -_reduction_modes = ['none', 'mean', 'sum'] - - -@masked_loss -def l1_loss(pred, target): - """L1 loss. - - Args: - pred (Tensor): Prediction Tensor with shape (n, c, h, w). - target ([type]): Target Tensor with shape (n, c, h, w). - - Returns: - Tensor: Calculated L1 loss. - """ - return F.l1_loss(pred, target, reduction='none') - - -@masked_loss -def mse_loss(pred, target): - """MSE loss. - - Args: - pred (Tensor): Prediction Tensor with shape (n, c, h, w). - target ([type]): Target Tensor with shape (n, c, h, w). - - Returns: - Tensor: Calculated MSE loss. - """ - return F.mse_loss(pred, target, reduction='none') - - -@masked_loss -def charbonnier_loss(pred, target, eps=1e-12): - """Charbonnier loss. - - Args: - pred (Tensor): Prediction Tensor with shape (n, c, h, w). - target ([type]): Target Tensor with shape (n, c, h, w). - - Returns: - Tensor: Calculated Charbonnier loss. - """ - return torch.sqrt((pred - target)**2 + eps) - - -@LOSSES.register_module() -class L1Loss(nn.Module): - """L1 (mean absolute error, MAE) loss. - - Args: - loss_weight (float): Loss weight for L1 loss. Default: 1.0. - reduction (str): Specifies the reduction to apply to the output. - Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduce loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - """ - - def __init__(self, loss_weight=1.0, reduction='mean', sample_wise=False): - super().__init__() - if reduction not in ['none', 'mean', 'sum']: - raise ValueError(f'Unsupported reduction mode: {reduction}. ' - f'Supported ones are: {_reduction_modes}') - - self.loss_weight = loss_weight - self.reduction = reduction - self.sample_wise = sample_wise - - def forward(self, pred, target, weight=None, **kwargs): - """Forward Function. - - Args: - pred (Tensor): of shape (N, C, H, W). Predicted tensor. - target (Tensor): of shape (N, C, H, W). Ground truth tensor. - weight (Tensor, optional): of shape (N, C, H, W). Element-wise - weights. Default: None. - """ - return self.loss_weight * l1_loss( - pred, - target, - weight, - reduction=self.reduction, - sample_wise=self.sample_wise) - - -@LOSSES.register_module() -class MSELoss(nn.Module): - """MSE (L2) loss. - - Args: - loss_weight (float): Loss weight for MSE loss. Default: 1.0. - reduction (str): Specifies the reduction to apply to the output. - Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduces loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - """ - - def __init__(self, loss_weight=1.0, reduction='mean', sample_wise=False): - super().__init__() - if reduction not in ['none', 'mean', 'sum']: - raise ValueError(f'Unsupported reduction mode: {reduction}. ' - f'Supported ones are: {_reduction_modes}') - - self.loss_weight = loss_weight - self.reduction = reduction - self.sample_wise = sample_wise - - def forward(self, pred, target, weight=None, **kwargs): - """Forward Function. - - Args: - pred (Tensor): of shape (N, C, H, W). Predicted tensor. - target (Tensor): of shape (N, C, H, W). Ground truth tensor. - weight (Tensor, optional): of shape (N, C, H, W). Element-wise - weights. Default: None. - """ - return self.loss_weight * mse_loss( - pred, - target, - weight, - reduction=self.reduction, - sample_wise=self.sample_wise) - - -@LOSSES.register_module() -class CharbonnierLoss(nn.Module): - """Charbonnier loss (one variant of Robust L1Loss, a differentiable - variant of L1Loss). - - Described in "Deep Laplacian Pyramid Networks for Fast and Accurate - Super-Resolution". - - Args: - loss_weight (float): Loss weight for L1 loss. Default: 1.0. - reduction (str): Specifies the reduction to apply to the output. - Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduces loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - eps (float): A value used to control the curvature near zero. - Default: 1e-12. - """ - - def __init__(self, - loss_weight=1.0, - reduction='mean', - sample_wise=False, - eps=1e-12): - super().__init__() - if reduction not in ['none', 'mean', 'sum']: - raise ValueError(f'Unsupported reduction mode: {reduction}. ' - f'Supported ones are: {_reduction_modes}') - - self.loss_weight = loss_weight - self.reduction = reduction - self.sample_wise = sample_wise - self.eps = eps - - def forward(self, pred, target, weight=None, **kwargs): - """Forward Function. - - Args: - pred (Tensor): of shape (N, C, H, W). Predicted tensor. - target (Tensor): of shape (N, C, H, W). Ground truth tensor. - weight (Tensor, optional): of shape (N, C, H, W). Element-wise - weights. Default: None. - """ - return self.loss_weight * charbonnier_loss( - pred, - target, - weight, - eps=self.eps, - reduction=self.reduction, - sample_wise=self.sample_wise) - - -@LOSSES.register_module() -class MaskedTVLoss(L1Loss): - """Masked TV loss. - - Args: - loss_weight (float, optional): Loss weight. Defaults to 1.0. - """ - - def __init__(self, loss_weight=1.0): - super().__init__(loss_weight=loss_weight) - - def forward(self, pred, mask=None): - """Forward function. - - Args: - pred (torch.Tensor): Tensor with shape of (n, c, h, w). - mask (torch.Tensor, optional): Tensor with shape of (n, 1, h, w). - Defaults to None. - - Returns: - [type]: [description] - """ - y_diff = super().forward( - pred[:, :, :-1, :], pred[:, :, 1:, :], weight=mask[:, :, :-1, :]) - x_diff = super().forward( - pred[:, :, :, :-1], pred[:, :, :, 1:], weight=mask[:, :, :, :-1]) - - loss = x_diff + y_diff - - return loss diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/losses/utils.py b/cv/super_resolution/liif/pytorch/mmedit/models/losses/utils.py deleted file mode 100755 index 3a19c0575..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/losses/utils.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import functools - -import torch.nn.functional as F - - -def reduce_loss(loss, reduction): - """Reduce loss as specified. - - Args: - loss (Tensor): Elementwise loss tensor. - reduction (str): Options are "none", "mean" and "sum". - - Returns: - Tensor: Reduced loss tensor. - """ - reduction_enum = F._Reduction.get_enum(reduction) - # none: 0, elementwise_mean:1, sum: 2 - if reduction_enum == 0: - return loss - if reduction_enum == 1: - return loss.mean() - - return loss.sum() - - -def mask_reduce_loss(loss, weight=None, reduction='mean', sample_wise=False): - """Apply element-wise weight and reduce loss. - - Args: - loss (Tensor): Element-wise loss. - weight (Tensor): Element-wise weights. Default: None. - reduction (str): Same as built-in losses of PyTorch. Options are - "none", "mean" and "sum". Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduces loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - - Returns: - Tensor: Processed loss values. - """ - # if weight is specified, apply element-wise weight - if weight is not None: - assert weight.dim() == loss.dim() - assert weight.size(1) == 1 or weight.size(1) == loss.size(1) - loss = loss * weight - - # if weight is not specified or reduction is sum, just reduce the loss - if weight is None or reduction == 'sum': - loss = reduce_loss(loss, reduction) - # if reduction is mean, then compute mean over masked region - elif reduction == 'mean': - # expand weight from N1HW to NCHW - if weight.size(1) == 1: - weight = weight.expand_as(loss) - # small value to prevent division by zero - eps = 1e-12 - - # perform sample-wise mean - if sample_wise: - weight = weight.sum(dim=[1, 2, 3], keepdim=True) # NCHW to N111 - loss = (loss / (weight + eps)).sum() / weight.size(0) - # perform pixel-wise mean - else: - loss = loss.sum() / (weight.sum() + eps) - - return loss - - -def masked_loss(loss_func): - """Create a masked version of a given loss function. - - To use this decorator, the loss function must have the signature like - `loss_func(pred, target, **kwargs)`. The function only needs to compute - element-wise loss without any reduction. This decorator will add weight - and reduction arguments to the function. The decorated function will have - the signature like `loss_func(pred, target, weight=None, reduction='mean', - avg_factor=None, **kwargs)`. - - :Example: - - >>> import torch - >>> @masked_loss - >>> def l1_loss(pred, target): - >>> return (pred - target).abs() - - >>> pred = torch.Tensor([0, 2, 3]) - >>> target = torch.Tensor([1, 1, 1]) - >>> weight = torch.Tensor([1, 0, 1]) - - >>> l1_loss(pred, target) - tensor(1.3333) - >>> l1_loss(pred, target, weight) - tensor(1.5000) - >>> l1_loss(pred, target, reduction='none') - tensor([1., 1., 2.]) - >>> l1_loss(pred, target, weight, reduction='sum') - tensor(3.) - """ - - @functools.wraps(loss_func) - def wrapper(pred, - target, - weight=None, - reduction='mean', - sample_wise=False, - **kwargs): - # get element-wise loss - loss = loss_func(pred, target, **kwargs) - loss = mask_reduce_loss(loss, weight, reduction, sample_wise) - return loss - - return wrapper diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/registry.py b/cv/super_resolution/liif/pytorch/mmedit/models/registry.py deleted file mode 100755 index 0a574b667..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import MODELS as MMCV_MODELS -from mmcv.utils import Registry - -MODELS = Registry('model', parent=MMCV_MODELS) -BACKBONES = MODELS -COMPONENTS = MODELS -LOSSES = MODELS diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/restorers/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/models/restorers/__init__.py deleted file mode 100755 index bae7147e1..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/restorers/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .liif import LIIF diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/restorers/basic_restorer.py b/cv/super_resolution/liif/pytorch/mmedit/models/restorers/basic_restorer.py deleted file mode 100755 index 5114b8e9a..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/restorers/basic_restorer.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -import os.path as osp - -import mmcv -from mmcv.runner import auto_fp16 - -from mmedit.core import psnr, ssim, tensor2img -from ..base import BaseModel -from ..builder import build_backbone, build_loss -from ..registry import MODELS - - -@MODELS.register_module() -class BasicRestorer(BaseModel): - """Basic model for image restoration. - - It must contain a generator that takes an image as inputs and outputs a - restored image. It also has a pixel-wise loss for training. - - The subclasses should overwrite the function `forward_train`, - `forward_test` and `train_step`. - - Args: - generator (dict): Config for the generator structure. - pixel_loss (dict): Config for pixel-wise loss. - train_cfg (dict): Config for training. Default: None. - test_cfg (dict): Config for testing. Default: None. - pretrained (str): Path for pretrained model. Default: None. - """ - allowed_metrics = {'PSNR': psnr, 'SSIM': ssim} - - def __init__(self, - generator, - pixel_loss, - train_cfg=None, - test_cfg=None, - pretrained=None): - super().__init__() - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - # support fp16 - self.fp16_enabled = False - - # generator - self.generator = build_backbone(generator) - self.init_weights(pretrained) - - # loss - self.pixel_loss = build_loss(pixel_loss) - - def init_weights(self, pretrained=None): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - """ - self.generator.init_weights(pretrained) - - @auto_fp16(apply_to=('lq', )) - def forward(self, lq, gt=None, test_mode=False, **kwargs): - """Forward function. - - Args: - lq (Tensor): Input lq images. - gt (Tensor): Ground-truth image. Default: None. - test_mode (bool): Whether in test mode or not. Default: False. - kwargs (dict): Other arguments. - """ - - if test_mode: - return self.forward_test(lq, gt, **kwargs) - - return self.forward_train(lq, gt) - - def forward_train(self, lq, gt): - """Training forward function. - - Args: - lq (Tensor): LQ Tensor with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). - - Returns: - Tensor: Output tensor. - """ - losses = dict() - output = self.generator(lq) - loss_pix = self.pixel_loss(output, gt) - losses['loss_pix'] = loss_pix - outputs = dict( - losses=losses, - num_samples=len(gt.data), - results=dict(lq=lq.cpu(), gt=gt.cpu(), output=output.cpu())) - return outputs - - def evaluate(self, output, gt): - """Evaluation function. - - Args: - output (Tensor): Model output with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). - - Returns: - dict: Evaluation results. - """ - crop_border = self.test_cfg.crop_border - - output = tensor2img(output) - gt = tensor2img(gt) - - eval_result = dict() - for metric in self.test_cfg.metrics: - eval_result[metric] = self.allowed_metrics[metric](output, gt, - crop_border) - return eval_result - - def forward_test(self, - lq, - gt=None, - meta=None, - save_image=False, - save_path=None, - iteration=None): - """Testing forward function. - - Args: - lq (Tensor): LQ Tensor with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). Default: None. - save_image (bool): Whether to save image. Default: False. - save_path (str): Path to save image. Default: None. - iteration (int): Iteration for the saving image name. - Default: None. - - Returns: - dict: Output results. - """ - output = self.generator(lq) - if self.test_cfg is not None and self.test_cfg.get('metrics', None): - assert gt is not None, ( - 'evaluation with metrics must have gt images.') - results = dict(eval_result=self.evaluate(output, gt)) - else: - results = dict(lq=lq.cpu(), output=output.cpu()) - if gt is not None: - results['gt'] = gt.cpu() - - # save image - if save_image: - lq_path = meta[0]['lq_path'] - folder_name = osp.splitext(osp.basename(lq_path))[0] - if isinstance(iteration, numbers.Number): - save_path = osp.join(save_path, folder_name, - f'{folder_name}-{iteration + 1:06d}.png') - elif iteration is None: - save_path = osp.join(save_path, f'{folder_name}.png') - else: - raise ValueError('iteration should be number or None, ' - f'but got {type(iteration)}') - mmcv.imwrite(tensor2img(output), save_path) - - return results - - def forward_dummy(self, img): - """Used for computing network FLOPs. - - Args: - img (Tensor): Input image. - - Returns: - Tensor: Output image. - """ - out = self.generator(img) - return out - - def train_step(self, data_batch, optimizer): - """Train step. - - Args: - data_batch (dict): A batch of data. - optimizer (obj): Optimizer. - - Returns: - dict: Returned output. - """ - outputs = self(**data_batch, test_mode=False) - loss, log_vars = self.parse_losses(outputs.pop('losses')) - - # optimize - optimizer['generator'].zero_grad() - loss.backward() - optimizer['generator'].step() - - outputs.update({'log_vars': log_vars}) - return outputs - - def val_step(self, data_batch, **kwargs): - """Validation step. - - Args: - data_batch (dict): A batch of data. - kwargs (dict): Other arguments for ``val_step``. - - Returns: - dict: Returned output. - """ - output = self.forward_test(**data_batch, **kwargs) - return output diff --git a/cv/super_resolution/liif/pytorch/mmedit/models/restorers/liif.py b/cv/super_resolution/liif/pytorch/mmedit/models/restorers/liif.py deleted file mode 100755 index f9462ca26..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/models/restorers/liif.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -import numbers -import os.path as osp - -import mmcv -import torch - -from mmedit.core import tensor2img -from ..registry import MODELS -from .basic_restorer import BasicRestorer - - -@MODELS.register_module() -class LIIF(BasicRestorer): - """LIIF model for single image super-resolution. - - Paper: Learning Continuous Image Representation with - Local Implicit Image Function - - Args: - generator (dict): Config for the generator. - pixel_loss (dict): Config for the pixel loss. - rgb_mean (tuple[float]): Data mean. - Default: (0.5, 0.5, 0.5). - rgb_std (tuple[float]): Data std. - Default: (0.5, 0.5, 0.5). - train_cfg (dict): Config for train. Default: None. - test_cfg (dict): Config for testing. Default: None. - pretrained (str): Path for pretrained model. Default: None. - """ - - def __init__(self, - generator, - pixel_loss, - rgb_mean=(0.5, 0.5, 0.5), - rgb_std=(0.5, 0.5, 0.5), - train_cfg=None, - test_cfg=None, - pretrained=None): - super().__init__( - generator, - pixel_loss, - train_cfg=train_cfg, - test_cfg=test_cfg, - pretrained=pretrained) - - # norm - rgb_mean = torch.FloatTensor(rgb_mean) - rgb_std = torch.FloatTensor(rgb_std) - self.lq_mean = rgb_mean.view(1, -1, 1, 1) - self.lq_std = rgb_std.view(1, -1, 1, 1) - self.gt_mean = rgb_mean.view(1, 1, -1) - self.gt_std = rgb_std.view(1, 1, -1) - - def train_step(self, data_batch, optimizer): - """Train step. - - Args: - data_batch (dict): A batch of data, which requires - 'coord', 'lq', 'gt', 'cell' - optimizer (obj): Optimizer. - - Returns: - dict: Returned output, which includes: - log_vars, num_samples, results (lq, gt and pred). - - """ - # data - coord = data_batch['coord'] - cell = data_batch['cell'] - lq = data_batch['lq'] - gt = data_batch['gt'] - - # norm - self.lq_mean = self.lq_mean.to(lq) - self.lq_std = self.lq_std.to(lq) - self.gt_mean = self.gt_mean.to(gt) - self.gt_std = self.gt_std.to(gt) - lq = (lq - self.lq_mean) / self.lq_std - gt = (gt - self.gt_mean) / self.gt_std - - # generator - pred = self.generator(lq, coord, cell) - - # loss - losses = dict() - log_vars = dict() - losses['loss_pix'] = self.pixel_loss(pred, gt) - - # parse loss - loss, log_vars = self.parse_losses(losses) - - # optimize - optimizer.zero_grad() - loss.backward() - optimizer.step() - - log_vars.pop('loss') # remove the unnecessary 'loss' - outputs = dict( - log_vars=log_vars, - num_samples=len(gt.data), - results=dict(lq=lq.cpu(), gt=gt.cpu(), output=pred.cpu())) - - return outputs - - def forward_test(self, - lq, - gt, - coord, - cell, - meta=None, - save_image=False, - save_path=None, - iteration=None): - """Testing forward function. - - Args: - lq (Tensor): LQ image. - gt (Tensor): GT image. - coord (Tensor): Coord tensor. - cell (Tensor): Cell tensor. - meta (list[dict]): Meta data, such as path of GT file. - Default: None. - save_image (bool): Whether to save image. Default: False. - save_path (str): Path to save image. Default: None. - iteration (int): Iteration for the saving image name. - Default: None. - - Returns: - dict: Output results, which contain either key(s) - 1. 'eval_result'. - 2. 'lq', 'pred'. - 3. 'lq', 'pred', 'gt'. - """ - - # norm - self.lq_mean = self.lq_mean.to(lq) - self.lq_std = self.lq_std.to(lq) - lq = (lq - self.lq_mean) / self.lq_std - - # generator - with torch.no_grad(): - pred = self.generator(lq, coord, cell, test_mode=True) - self.gt_mean = self.gt_mean.to(pred) - self.gt_std = self.gt_std.to(pred) - pred = pred * self.gt_std + self.gt_mean - pred.clamp_(0, 1) - - # reshape for eval - ih, iw = lq.shape[-2:] - s = math.sqrt(coord.shape[1] / (ih * iw)) - shape = [lq.shape[0], round(ih * s), round(iw * s), 3] - pred = pred.view(*shape).permute(0, 3, 1, 2).contiguous() - if gt is not None: - gt = gt.view(*shape).permute(0, 3, 1, 2).contiguous() - - if self.test_cfg is not None and self.test_cfg.get('metrics', None): - assert gt is not None, ( - 'evaluation with metrics must have gt images.') - results = dict(eval_result=self.evaluate(pred, gt)) - else: - results = dict(lq=lq.cpu(), output=pred.cpu()) - if gt is not None: - results['gt'] = gt.cpu() - - # save image - if save_image: - gt_path = meta[0]['gt_path'] - folder_name = osp.splitext(osp.basename(gt_path))[0] - if isinstance(iteration, numbers.Number): - save_path = osp.join(save_path, folder_name, - f'{folder_name}-{iteration + 1:06d}.png') - elif iteration is None: - save_path = osp.join(save_path, f'{folder_name}.png') - else: - raise ValueError('iteration should be number or None, ' - f'but got {type(iteration)}') - mmcv.imwrite(tensor2img(pred), save_path) - - return results - - def init_weights(self, pretrained=None, strict=True): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - strict (boo, optional): Whether strictly load the pretrained model. - Defaults to True. - """ - - self.generator.init_weights(pretrained, strict) diff --git a/cv/super_resolution/liif/pytorch/mmedit/utils/__init__.py b/cv/super_resolution/liif/pytorch/mmedit/utils/__init__.py deleted file mode 100755 index 0521b4a1c..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/utils/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .cli import modify_args -from .logger import get_root_logger -from .setup_env import setup_multi_processes - -__all__ = ['get_root_logger', 'setup_multi_processes', 'modify_args'] diff --git a/cv/super_resolution/liif/pytorch/mmedit/utils/cli.py b/cv/super_resolution/liif/pytorch/mmedit/utils/cli.py deleted file mode 100755 index a78b32712..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/utils/cli.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import re -import sys -import warnings - - -def modify_args(): - for i, v in enumerate(sys.argv): - if i == 0: - assert v.endswith('.py') - elif re.match(r'--\w+_.*', v): - new_arg = v.replace('_', '-') - warnings.warn( - f'command line argument {v} is deprecated, ' - f'please use {new_arg} instead.', - category=DeprecationWarning, - ) - sys.argv[i] = new_arg diff --git a/cv/super_resolution/liif/pytorch/mmedit/utils/collect_env.py b/cv/super_resolution/liif/pytorch/mmedit/utils/collect_env.py deleted file mode 100755 index af22082c7..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/utils/collect_env.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash - -import mmedit - - -def collect_env(): - """Collect the information of the running environments.""" - env_info = collect_base_env() - env_info['MMEditing'] = f'{mmedit.__version__}+{get_git_hash()[:7]}' - - return env_info - - -if __name__ == '__main__': - for name, val in collect_env().items(): - print('{}: {}'.format(name, val)) diff --git a/cv/super_resolution/liif/pytorch/mmedit/utils/logger.py b/cv/super_resolution/liif/pytorch/mmedit/utils/logger.py deleted file mode 100755 index cdc340f18..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/utils/logger.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -from mmcv.utils import get_logger - - -def get_root_logger(log_file=None, log_level=logging.INFO): - """Get the root logger. - - The logger will be initialized if it has not been initialized. By default a - StreamHandler will be added. If `log_file` is specified, a FileHandler will - also be added. The name of the root logger is the top-level package name, - e.g., "mmedit". - - Args: - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the root logger. - log_level (int): The root logger level. Note that only the process of - rank 0 is affected, while other processes will set the level to - "Error" and be silent most of the time. - - Returns: - logging.Logger: The root logger. - """ - # root logger name: mmedit - logger = get_logger(__name__.split('.')[0], log_file, log_level) - return logger diff --git a/cv/super_resolution/liif/pytorch/mmedit/utils/setup_env.py b/cv/super_resolution/liif/pytorch/mmedit/utils/setup_env.py deleted file mode 100755 index 21def2f08..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/utils/setup_env.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import platform -import warnings - -import cv2 -import torch.multiprocessing as mp - - -def setup_multi_processes(cfg): - """Setup multi-processing environment variables.""" - # set multi-process start method as `fork` to speed up the training - if platform.system() != 'Windows': - mp_start_method = cfg.get('mp_start_method', 'fork') - current_method = mp.get_start_method(allow_none=True) - if current_method is not None and current_method != mp_start_method: - warnings.warn( - f'Multi-processing start method `{mp_start_method}` is ' - f'different from the previous setting `{current_method}`.' - f'It will be force set to `{mp_start_method}`. You can change ' - f'this behavior by changing `mp_start_method` in your config.') - mp.set_start_method(mp_start_method, force=True) - - # disable opencv multithreading to avoid system being overloaded - opencv_num_threads = cfg.get('opencv_num_threads', 0) - cv2.setNumThreads(opencv_num_threads) - - # setup OMP threads - # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa - if 'OMP_NUM_THREADS' not in os.environ and cfg.data.workers_per_gpu > 1: - omp_num_threads = 1 - warnings.warn( - f'Setting OMP_NUM_THREADS environment variable for each process ' - f'to be {omp_num_threads} in default, to avoid your system being ' - f'overloaded, please further tune the variable for optimal ' - f'performance in your application as needed.') - os.environ['OMP_NUM_THREADS'] = str(omp_num_threads) - - # setup MKL threads - if 'MKL_NUM_THREADS' not in os.environ and cfg.data.workers_per_gpu > 1: - mkl_num_threads = 1 - warnings.warn( - f'Setting MKL_NUM_THREADS environment variable for each process ' - f'to be {mkl_num_threads} in default, to avoid your system being ' - f'overloaded, please further tune the variable for optimal ' - f'performance in your application as needed.') - os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads) diff --git a/cv/super_resolution/liif/pytorch/mmedit/version.py b/cv/super_resolution/liif/pytorch/mmedit/version.py deleted file mode 100755 index e397b0bc5..000000000 --- a/cv/super_resolution/liif/pytorch/mmedit/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) Open-MMLab. All rights reserved. - -__version__ = '0.14.0' - - -def parse_version_info(version_str): - ver_info = [] - for x in version_str.split('.'): - if x.isdigit(): - ver_info.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - ver_info.append(int(patch_version[0])) - ver_info.append(f'rc{patch_version[1]}') - return tuple(ver_info) - - -version_info = parse_version_info(__version__) diff --git a/cv/super_resolution/liif/pytorch/requirements.txt b/cv/super_resolution/liif/pytorch/requirements.txt deleted file mode 100755 index bef817c47..000000000 --- a/cv/super_resolution/liif/pytorch/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -addict -yapf -opencv-python -tensorboard diff --git a/cv/super_resolution/liif/pytorch/test.py b/cv/super_resolution/liif/pytorch/test.py deleted file mode 100755 index 026b4c8a7..000000000 --- a/cv/super_resolution/liif/pytorch/test.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import argparse -import os - -import mmcv -import torch -from mmcv import Config, DictAction -from mmcv.parallel import MMDataParallel -from mmcv.runner import get_dist_info, init_dist, load_checkpoint - -from mmedit.apis import multi_gpu_test, set_random_seed, single_gpu_test -from mmedit.core.distributed_wrapper import DistributedDataParallelWrapper -from mmedit.datasets import build_dataloader, build_dataset -from mmedit.models import build_model -from mmedit.utils import setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser(description='mmediting tester') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument('--out', help='output result pickle file') - parser.add_argument( - '--gpu-collect', - action='store_true', - help='whether to use gpu to collect results') - parser.add_argument( - '--save-path', - default=None, - type=str, - help='path to store images and if not given, will not save image') - parser.add_argument('--tmpdir', help='tmp dir for writing some results') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - cfg.model.pretrained = None - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - - rank, _ = get_dist_info() - - # set random seeds - if args.seed is not None: - if rank == 0: - print('set random seed to', args.seed) - set_random_seed(args.seed, deterministic=args.deterministic) - - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - - loader_cfg = { - **dict((k, cfg.data[k]) for k in ['workers_per_gpu'] if k in cfg.data), - **dict( - samples_per_gpu=1, - drop_last=False, - shuffle=False, - dist=distributed), - **cfg.data.get('test_dataloader', {}) - } - - data_loader = build_dataloader(dataset, **loader_cfg) - - # build the model and load checkpoint - model = build_model(cfg.model, train_cfg=None, test_cfg=cfg.test_cfg) - - args.save_image = args.save_path is not None - empty_cache = cfg.get('empty_cache', False) - if not distributed: - _ = load_checkpoint(model, args.checkpoint, map_location='cpu') - model = MMDataParallel(model, device_ids=[0]) - outputs = single_gpu_test( - model, - data_loader, - save_path=args.save_path, - save_image=args.save_image) - else: - find_unused_parameters = cfg.get('find_unused_parameters', False) - model = DistributedDataParallelWrapper( - model, - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - - device_id = torch.cuda.current_device() - _ = load_checkpoint( - model, - args.checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - outputs = multi_gpu_test( - model, - data_loader, - args.tmpdir, - args.gpu_collect, - save_path=args.save_path, - save_image=args.save_image, - empty_cache=empty_cache) - - if rank == 0 and 'eval_result' in outputs[0]: - print('') - # print metrics - stats = dataset.evaluate(outputs) - for stat in stats: - print('Eval-{}: {}'.format(stat, stats[stat])) - - # save result pickle - if args.out: - print('writing results to {}'.format(args.out)) - mmcv.dump(outputs, args.out) - - -if __name__ == '__main__': - main() diff --git a/cv/super_resolution/liif/pytorch/train.py b/cv/super_resolution/liif/pytorch/train.py deleted file mode 100755 index 0be099fcf..000000000 --- a/cv/super_resolution/liif/pytorch/train.py +++ /dev/null @@ -1,176 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import argparse -import copy -import os -import os.path as osp -import time - -import mmcv -import torch -import torch.distributed as dist -from mmcv import Config, DictAction -from mmcv.runner import init_dist - -from mmedit import __version__ -from mmedit.apis import init_random_seed, set_random_seed, train_model -from mmedit.datasets import build_dataset -from mmedit.models import build_model -from mmedit.utils import collect_env, get_root_logger, setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train an editor') - parser.add_argument('config', help='train config file path') - parser.add_argument('--work-dir', help='the dir to save logs and models') - parser.add_argument( - '--resume-from', help='the checkpoint file to resume from') - parser.add_argument( - '--no-validate', - action='store_true', - help='whether not to evaluate the checkpoint during training') - parser.add_argument( - '--gpus', - type=int, - default=1, - help='number of gpus to use ' - '(only applicable to non-distributed training)') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--diff_seed', - action='store_true', - help='Whether or not set different seeds for different ranks') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - parser.add_argument( - '--autoscale-lr', - action='store_true', - help='automatically scale lr with the number of gpus') - parser.add_argument( - '--dist_backend', type=str, default=None) - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - # update configs according to CLI args - if args.work_dir is not None: - cfg.work_dir = args.work_dir - if args.resume_from is not None: - cfg.resume_from = args.resume_from - cfg.gpus = args.gpus - - if args.autoscale_lr: - # apply the linear scaling rule (https://arxiv.org/abs/1706.02677) - cfg.optimizer['lr'] = cfg.optimizer['lr'] * cfg.gpus / 8 - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - if args.dist_backend is not None: - cfg.dist_params.backend = args.dist_backend - init_dist(args.launcher, **cfg.dist_params) - - # create work_dir - mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) - # init the logger before other steps - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - log_file = osp.join(cfg.work_dir, f'{timestamp}.log') - logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) - - # log env info - env_info_dict = collect_env.collect_env() - env_info = '\n'.join([f'{k}: {v}' for k, v in env_info_dict.items()]) - dash_line = '-' * 60 + '\n' - logger.info('Environment info:\n' + dash_line + env_info + '\n' + - dash_line) - - # log some basic info - logger.info('Distributed training: {}'.format(distributed)) - logger.info('mmedit Version: {}'.format(__version__)) - logger.info('Config:\n{}'.format(cfg.text)) - - # set random seeds - seed = init_random_seed(args.seed) - seed = seed + dist.get_rank() if args.diff_seed else seed - logger.info('Set random seed to {}, deterministic: {}'.format( - seed, args.deterministic)) - set_random_seed(seed, deterministic=args.deterministic) - cfg.seed = seed - - model = build_model( - cfg.model, train_cfg=cfg.train_cfg, test_cfg=cfg.test_cfg) - - datasets = [build_dataset(cfg.data.train)] - if len(cfg.workflow) == 2: - val_dataset = copy.deepcopy(cfg.data.val) - val_dataset.pipeline = cfg.data.train.pipeline - datasets.append(build_dataset(val_dataset)) - if cfg.checkpoint_config is not None: - # save version, config file content and class names in - # checkpoints as meta data - cfg.checkpoint_config.meta = dict( - mmedit_version=__version__, - config=cfg.text, - ) - - # meta information - meta = dict() - if cfg.get('exp_name', None) is None: - cfg['exp_name'] = osp.splitext(osp.basename(cfg.work_dir))[0] - meta['exp_name'] = cfg.exp_name - meta['mmedit Version'] = __version__ - meta['seed'] = seed - meta['env_info'] = env_info - - # add an attribute for visualization convenience - train_model( - model, - datasets, - cfg, - distributed=distributed, - validate=(not args.no_validate), - timestamp=timestamp, - meta=meta) - - -if __name__ == '__main__': - main() -- Gitee From 5cc9973b64f57f937c50e37510796c6e5897fb6e Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Thu, 6 Mar 2025 11:43:07 +0800 Subject: [PATCH 13/15] update oriented_reppoints --- .../oriented_reppoints/pytorch/README.md | 50 ++--- .../pytorch/patch/bbox_nms_rotated.py | 147 --------------- .../pytorch/patch/convex_assigner.py | 173 ------------------ .../pytorch/patch/schedule_1x.py | 18 -- 4 files changed, 16 insertions(+), 372 deletions(-) delete mode 100644 cv/detection/oriented_reppoints/pytorch/patch/bbox_nms_rotated.py delete mode 100644 cv/detection/oriented_reppoints/pytorch/patch/convex_assigner.py delete mode 100644 cv/detection/oriented_reppoints/pytorch/patch/schedule_1x.py diff --git a/cv/detection/oriented_reppoints/pytorch/README.md b/cv/detection/oriented_reppoints/pytorch/README.md index bbbe887f2..24db5781a 100644 --- a/cv/detection/oriented_reppoints/pytorch/README.md +++ b/cv/detection/oriented_reppoints/pytorch/README.md @@ -16,39 +16,23 @@ aerial datasets including DOTA, HRSC2016, UCAS-AOD and DIOR-R, demonstrate the e ## Step 1: Installation ```bash -# Install mmcv -pushd ../../../../toolbox/MMDetection/ -bash clean_mmcv.sh -bash build_mmcv.sh -bash install_mmcv.sh -popd +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx # Install mmdetection -git clone -b v2.25.0 https://gitee.com/open-mmlab/mmdetection.git -pushd mmdetection -sed -i 's/sklearn/scikit-learn/g' requirements/optional.txt -sed -i 's/github.com/gitee.com/g' requirements/tests.txt -pip3 install -r requirements.txt -pip3 install yapf addict opencv-python -yum install mesa-libGL -python3 setup.py develop -popd +pip install mmdet==3.3.0 # Install mmrotate - -git clone -b v0.3.2 https://gitee.com/open-mmlab/mmrotate.git +git clone -b v1.0.0rc1 https://gitee.com/open-mmlab/mmrotate.git --depth=1 cd mmrotate/ -sed -i 's/sklearn/scikit-learn/g' requirements/optional.txt -sed -i 's/sklearn/scikit-learn/g' requirements/tests.txt +pip install -v -e . sed -i 's/python /python3 /g' tools/dist_train.sh -mv ../patch/convex_assigner.py mmrotate/core/bbox/assigners/convex_assigner.py -mv ../patch/bbox_nms_rotated.py mmrotate/core/post_processing/bbox_nms_rotated.py -mv ../patch/schedule_1x.py configs/_base_/schedules/schedule_1x.py -pip3 install -r requirements.txt -pip3 install shapely -python3 setup.py develop - -pip3 install urllib3==1.26.6 +sed -i 's/3.1.0/3.4.0/g' mmrotate/__init__.py +sed -i 's@points_range\s*=\s*torch\.arange\s*(\s*points\.shape\[0\]\s*)@&.to(points.device)@' mmrotate/models/task_modules/assigners/convex_assigner.py +sed -i 's/from collections import Sequence/from collections.abc import Sequence/g' mmrotate/models/detectors/refine_single_stage.py ``` ## Step 2: Preparing datasets @@ -91,21 +75,17 @@ python3 tools/data/dota/split/img_split.py --base-json \ Please change `data_root` in `configs/_base_/datasets/dotav1.py` to split DOTA dataset. ```bash -sed -i 's#data/split_1024_dota1_0/#data/split_ss_dota/#g' configs/_base_/datasets/dotav1.py +sed -i 's#data/split_ss_dota1_5/#data/split_ss_dota/#g' configs/_base_/datasets/dotav15.py ``` - ## Step 3: Training - ```bash # On single GPU -python3 tools/train.py configs/oriented_reppoints/oriented_reppoints_r50_fpn_1x_dota_le135.py +python3 tools/train.py configs/oriented_reppoints/oriented-reppoints-qbox_r50_fpn_1x_dota.py # Multiple GPUs on one machine -export USE_GLOOGPU=1 -export UMD_ENABLEMEMPOOL=0 -bash tools/dist_train.sh configs/oriented_reppoints/oriented_reppoints_r50_fpn_1x_dota_le135.py 8 +bash tools/dist_train.sh configs/oriented_reppoints/oriented-reppoints-qbox_r50_fpn_1x_dota.py 8 ``` ## Results @@ -114,3 +94,5 @@ bash tools/dist_train.sh configs/oriented_reppoints/oriented_reppoints_r50_fpn_1 |----------| ----------- | | BI-V100 x8 | MAP=0.8265 | +## Reference +[mmrotate](https://github.com/open-mmlab/mmrotate) \ No newline at end of file diff --git a/cv/detection/oriented_reppoints/pytorch/patch/bbox_nms_rotated.py b/cv/detection/oriented_reppoints/pytorch/patch/bbox_nms_rotated.py deleted file mode 100644 index 25df0aa9f..000000000 --- a/cv/detection/oriented_reppoints/pytorch/patch/bbox_nms_rotated.py +++ /dev/null @@ -1,147 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import torch -from mmcv.ops import nms_rotated - - -def multiclass_nms_rotated(multi_bboxes, - multi_scores, - score_thr, - nms, - max_num=-1, - score_factors=None, - return_inds=False): - """NMS for multi-class bboxes. - - Args: - multi_bboxes (torch.Tensor): shape (n, #class*5) or (n, 5) - multi_scores (torch.Tensor): shape (n, #class), where the last column - contains scores of the background class, but this will be ignored. - score_thr (float): bbox threshold, bboxes with scores lower than it - will not be considered. - nms (float): Config of NMS. - max_num (int, optional): if there are more than max_num bboxes after - NMS, only top max_num will be kept. Default to -1. - score_factors (Tensor, optional): The factors multiplied to scores - before applying NMS. Default to None. - return_inds (bool, optional): Whether return the indices of kept - bboxes. Default to False. - - Returns: - tuple (dets, labels, indices (optional)): tensors of shape (k, 5), \ - (k), and (k). Dets are boxes with scores. Labels are 0-based. - """ - num_classes = multi_scores.size(1) - 1 - # exclude background category - if multi_bboxes.shape[1] > 5: - bboxes = multi_bboxes.view(multi_scores.size(0), -1, 5) - else: - bboxes = multi_bboxes[:, None].expand( - multi_scores.size(0), num_classes, 5) - scores = multi_scores[:, :-1] - - labels = torch.arange(num_classes, dtype=torch.long).cuda() - labels = labels.view(1, -1).expand_as(scores) - bboxes = bboxes.reshape(-1, 5) - scores = scores.reshape(-1) - labels = labels.reshape(-1) - - # remove low scoring boxes - valid_mask = scores > score_thr - if score_factors is not None: - # expand the shape to match original shape of score - score_factors = score_factors.view(-1, 1).expand( - multi_scores.size(0), num_classes) - score_factors = score_factors.reshape(-1) - scores = scores * score_factors - - inds = valid_mask.nonzero(as_tuple=False).squeeze(1) - bboxes, scores, labels = bboxes[inds], scores[inds], labels[inds] - - if bboxes.numel() == 0: - dets = torch.cat([bboxes, scores[:, None]], -1) - if return_inds: - return dets, labels, inds - else: - return dets, labels - - # Strictly, the maximum coordinates of the rotating box (x,y,w,h,a) - # should be calculated by polygon coordinates. - # But the conversion from rbbox to polygon will slow down the speed. - # So we use max(x,y) + max(w,h) as max coordinate - # which is larger than polygon max coordinate - # max(x1, y1, x2, y2,x3, y3, x4, y4) - max_coordinate = bboxes[:, :2].max() + bboxes[:, 2:4].max() - offsets = labels.to(bboxes) * (max_coordinate + 1) - if bboxes.size(-1) == 5: - bboxes_for_nms = bboxes.clone() - bboxes_for_nms[:, :2] = bboxes_for_nms[:, :2] + offsets[:, None] - else: - bboxes_for_nms = bboxes + offsets[:, None] - _, keep = nms_rotated(bboxes_for_nms, scores, nms.iou_thr) - - if max_num > 0: - keep = keep[:max_num] - - bboxes = bboxes[keep] - scores = scores[keep] - labels = labels[keep] - - if return_inds: - return torch.cat([bboxes, scores[:, None]], 1), labels, keep - else: - return torch.cat([bboxes, scores[:, None]], 1), labels - - -def aug_multiclass_nms_rotated(merged_bboxes, merged_labels, score_thr, nms, - max_num, classes): - """NMS for aug multi-class bboxes. - - Args: - multi_bboxes (torch.Tensor): shape (n, #class*5) or (n, 5) - multi_scores (torch.Tensor): shape (n, #class), where the last column - contains scores of the background class, but this will be ignored. - score_thr (float): bbox threshold, bboxes with scores lower than it - will not be considered. - nms (float): Config of NMS. - max_num (int, optional): if there are more than max_num bboxes after - NMS, only top max_num will be kept. Default to -1. - classes (int): number of classes. - - Returns: - tuple (dets, labels): tensors of shape (k, 5), and (k). Dets are boxes - with scores. Labels are 0-based. - """ - bboxes, labels = [], [] - - for cls in range(classes): - cls_bboxes = merged_bboxes[merged_labels == cls] - inds = cls_bboxes[:, -1] > score_thr - if len(inds) == 0: - continue - cur_bboxes = cls_bboxes[inds, :] - cls_dets, _ = nms_rotated(cur_bboxes[:, :5], cur_bboxes[:, -1], - nms.iou_thr) - cls_labels = merged_bboxes.new_full((cls_dets.shape[0], ), - cls, - dtype=torch.long) - if cls_dets.size()[0] == 0: - continue - bboxes.append(cls_dets) - labels.append(cls_labels) - - if bboxes: - bboxes = torch.cat(bboxes) - labels = torch.cat(labels) - if bboxes.shape[0] > max_num: - _, _inds = bboxes[:, -1].sort(descending=True) - _inds = _inds[:max_num] - bboxes = bboxes[_inds] - labels = labels[_inds] - else: - bboxes = merged_bboxes.new_zeros((0, merged_bboxes.size(-1))) - labels = merged_bboxes.new_zeros((0, ), dtype=torch.long) - - return bboxes, labels diff --git a/cv/detection/oriented_reppoints/pytorch/patch/convex_assigner.py b/cv/detection/oriented_reppoints/pytorch/patch/convex_assigner.py deleted file mode 100644 index 979264aec..000000000 --- a/cv/detection/oriented_reppoints/pytorch/patch/convex_assigner.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import torch -from mmdet.core.bbox.assigners.assign_result import AssignResult -from mmdet.core.bbox.assigners.base_assigner import BaseAssigner - -from ..builder import ROTATED_BBOX_ASSIGNERS - - -@ROTATED_BBOX_ASSIGNERS.register_module() -class ConvexAssigner(BaseAssigner): - """Assign a corresponding gt bbox or background to each bbox. Each - proposals will be assigned with `0` or a positive integer indicating the - ground truth index. - - - 0: negative sample, no assigned gt - - positive integer: positive sample, index (1-based) of assigned gt - - Args: - scale (float): IoU threshold for positive bboxes. - pos_num (float): find the nearest pos_num points to gt center in this - level. - """ - - def __init__(self, scale=4, pos_num=3): - self.scale = scale - self.pos_num = pos_num - - def get_horizontal_bboxes(self, gt_rbboxes): - """get_horizontal_bboxes from polygons. - - Args: - gt_rbboxes (torch.Tensor): Groundtruth polygons, shape (k, 8). - - Returns: - gt_rect_bboxes (torch.Tensor): The horizontal bboxes, shape (k, 4). - """ - gt_xs, gt_ys = gt_rbboxes[:, 0::2], gt_rbboxes[:, 1::2] - gt_xmin, _ = gt_xs.min(1) - gt_ymin, _ = gt_ys.min(1) - gt_xmax, _ = gt_xs.max(1) - gt_ymax, _ = gt_ys.max(1) - gt_rect_bboxes = torch.cat([ - gt_xmin[:, None], gt_ymin[:, None], gt_xmax[:, None], gt_ymax[:, - None] - ], - dim=1) - - return gt_rect_bboxes - - def assign(self, - points, - gt_rbboxes, - gt_rbboxes_ignore=None, - gt_labels=None, - overlaps=None): - """Assign gt to bboxes. - - The assignment is done in following steps - - 1. compute iou between all bbox (bbox of all pyramid levels) and gt - 2. compute center distance between all bbox and gt - 3. on each pyramid level, for each gt, select k bbox whose center - are closest to the gt center, so we total select k*l bbox as - candidates for each gt - 4. get corresponding iou for the these candidates, and compute the - mean and std, set mean + std as the iou threshold - 5. select these candidates whose iou are greater than or equal to - the threshold as positive - 6. limit the positive sample's center in gt - - Args: - points (torch.Tensor): Points to be assigned, shape(n, 18). - gt_rbboxes (torch.Tensor): Groundtruth polygons, shape (k, 8). - gt_rbboxes_ignore (Tensor, optional): Ground truth polygons that - are labelled as `ignored`, e.g., crowd boxes in COCO. - gt_labels (Tensor, optional): Label of gt_bboxes, shape (k, ). - - Returns: - :obj:`AssignResult`: The assign result. - """ - num_points = points.shape[0] - num_gts = gt_rbboxes.shape[0] - - if num_gts == 0 or num_points == 0: - # If no truth assign everything to the background - assigned_gt_inds = points.new_full((num_points, ), - 0, - dtype=torch.long) - if gt_labels is None: - assigned_labels = None - else: - assigned_labels = points.new_full((num_points, ), - -1, - dtype=torch.long) - return AssignResult( - num_gts, assigned_gt_inds, None, labels=assigned_labels) - - points_xy = points[:, :2] - points_stride = points[:, 2] - points_lvl = torch.log2(points_stride).int() - lvl_min, lvl_max = points_lvl.min(), points_lvl.max() - - assert gt_rbboxes.size(1) == 8, 'gt_rbboxes should be (N * 8)' - gt_bboxes = self.get_horizontal_bboxes(gt_rbboxes) - - # assign gt rbox - gt_bboxes_xy = (gt_bboxes[:, :2] + gt_bboxes[:, 2:]) / 2 - - gt_bboxes_wh = (gt_bboxes[:, 2:] - gt_bboxes[:, :2]).clamp(min=1e-6) - scale = self.scale - gt_bboxes_lvl = ((torch.log2(gt_bboxes_wh[:, 0] / scale) + - torch.log2(gt_bboxes_wh[:, 1] / scale)) / 2).int() - gt_bboxes_lvl = torch.clamp(gt_bboxes_lvl, min=lvl_min, max=lvl_max) - - # stores the assigned gt index of each point - assigned_gt_inds = points.new_zeros((num_points, ), dtype=torch.long) - # stores the assigned gt dist (to this point) of each point - assigned_gt_dist = points.new_full((num_points, ), float('inf')) - points_range = torch.arange(points.shape[0]).cuda() - - for idx in range(num_gts): - gt_lvl = gt_bboxes_lvl[idx] - # get the index of points in this level - lvl_idx = gt_lvl == points_lvl - points_index = points_range[lvl_idx] - # get the points in this level - lvl_points = points_xy[lvl_idx, :] - # get the center point of gt - gt_point = gt_bboxes_xy[[idx], :] - # get width and height of gt - gt_wh = gt_bboxes_wh[[idx], :] - # compute the distance between gt center and - # all points in this level - points_gt_dist = ((lvl_points - gt_point) / gt_wh).norm(dim=1) - # find the nearest k points to gt center in this level - min_dist, min_dist_index = torch.topk( - points_gt_dist, self.pos_num, largest=False) - # the index of nearest k points to gt center in this level - min_dist_points_index = points_index[min_dist_index] - - # The less_than_recorded_index stores the index - # of min_dist that is less then the assigned_gt_dist. Where - # assigned_gt_dist stores the dist from previous assigned gt - # (if exist) to each point. - less_than_recorded_index = min_dist < assigned_gt_dist[ - min_dist_points_index] - # The min_dist_points_index stores the index of points satisfy: - # (1) it is k nearest to current gt center in this level. - # (2) it is closer to current gt center than other gt center. - min_dist_points_index = min_dist_points_index[ - less_than_recorded_index] - # assign the result - assigned_gt_inds[min_dist_points_index] = idx + 1 - assigned_gt_dist[min_dist_points_index] = min_dist[ - less_than_recorded_index] - - if gt_labels is not None: - assigned_labels = assigned_gt_inds.new_full((num_points, ), - -1, - dtype=torch.long) - pos_inds = torch.nonzero( - assigned_gt_inds > 0, as_tuple=False).squeeze() - if pos_inds.numel() > 0: - assigned_labels[pos_inds] = gt_labels[ - assigned_gt_inds[pos_inds] - 1] - else: - assigned_labels = None - - return AssignResult( - num_gts, assigned_gt_inds, None, labels=assigned_labels) diff --git a/cv/detection/oriented_reppoints/pytorch/patch/schedule_1x.py b/cv/detection/oriented_reppoints/pytorch/patch/schedule_1x.py deleted file mode 100644 index ee93f4cec..000000000 --- a/cv/detection/oriented_reppoints/pytorch/patch/schedule_1x.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2023, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -# evaluation -evaluation = dict(interval=12, metric='mAP') -# optimizer -optimizer = dict(type='SGD', lr=0.0025, momentum=0.9, weight_decay=0.0001) -optimizer_config = dict(grad_clip=dict(max_norm=35, norm_type=2)) -# learning policy -lr_config = dict( - policy='step', - warmup='linear', - warmup_iters=500, - warmup_ratio=1.0 / 3, - step=[8, 11]) -runner = dict(type='EpochBasedRunner', max_epochs=12) -checkpoint_config = dict(interval=1) - -- Gitee From f533eda17b068e27ae24cafba9914c84a2c0d964 Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Thu, 6 Mar 2025 13:44:11 +0800 Subject: [PATCH 14/15] update real_basicvsr --- .../basicvsr++/pytorch/README.md | 2 +- .../real_basicvsr/pytorch/.gitignore | 140 -- .../real_basicvsr/pytorch/LICENSE | 203 --- .../real_basicvsr/pytorch/README.md | 46 +- .../real_basicvsr/pytorch/build_env.sh | 14 - ...basicvsr_c64b20_1x30x8_lr5e-5_150k_reds.py | 341 ----- ...sr_wogan_c64b20_2x30x8_lr1e-4_300k_reds.py | 309 ---- .../real_basicvsr/pytorch/crop_sub_images.py | 195 --- .../real_basicvsr/pytorch/dist_test.sh | 23 - .../real_basicvsr/pytorch/dist_train.sh | 21 - .../real_basicvsr/pytorch/docker/Dockerfile | 24 - .../real_basicvsr/pytorch/docker/README.md | 19 - .../real_basicvsr/pytorch/mmcv/__init__.py | 13 - .../pytorch/mmcv/arraymisc/__init__.py | 4 - .../pytorch/mmcv/arraymisc/quantization.py | 55 - .../pytorch/mmcv/cnn/__init__.py | 41 - .../real_basicvsr/pytorch/mmcv/cnn/alexnet.py | 61 - .../pytorch/mmcv/cnn/bricks/__init__.py | 35 - .../pytorch/mmcv/cnn/bricks/activation.py | 92 -- .../pytorch/mmcv/cnn/bricks/context_block.py | 125 -- .../pytorch/mmcv/cnn/bricks/conv.py | 44 - .../cnn/bricks/conv2d_adaptive_padding.py | 62 - .../pytorch/mmcv/cnn/bricks/conv_module.py | 206 --- .../pytorch/mmcv/cnn/bricks/conv_ws.py | 148 -- .../bricks/depthwise_separable_conv_module.py | 96 -- .../pytorch/mmcv/cnn/bricks/drop.py | 65 - .../mmcv/cnn/bricks/generalized_attention.py | 412 ----- .../pytorch/mmcv/cnn/bricks/hsigmoid.py | 34 - .../pytorch/mmcv/cnn/bricks/hswish.py | 29 - .../pytorch/mmcv/cnn/bricks/non_local.py | 306 ---- .../pytorch/mmcv/cnn/bricks/norm.py | 144 -- .../pytorch/mmcv/cnn/bricks/padding.py | 36 - .../pytorch/mmcv/cnn/bricks/plugin.py | 88 -- .../pytorch/mmcv/cnn/bricks/registry.py | 16 - .../pytorch/mmcv/cnn/bricks/scale.py | 21 - .../pytorch/mmcv/cnn/bricks/swish.py | 25 - .../pytorch/mmcv/cnn/bricks/transformer.py | 595 -------- .../pytorch/mmcv/cnn/bricks/upsample.py | 84 -- .../pytorch/mmcv/cnn/bricks/wrappers.py | 180 --- .../real_basicvsr/pytorch/mmcv/cnn/builder.py | 30 - .../real_basicvsr/pytorch/mmcv/cnn/resnet.py | 316 ---- .../pytorch/mmcv/cnn/utils/__init__.py | 19 - .../pytorch/mmcv/cnn/utils/flops_counter.py | 599 -------- .../pytorch/mmcv/cnn/utils/fuse_conv_bn.py | 59 - .../pytorch/mmcv/cnn/utils/sync_bn.py | 59 - .../pytorch/mmcv/cnn/utils/weight_init.py | 684 --------- .../real_basicvsr/pytorch/mmcv/cnn/vgg.py | 175 --- .../pytorch/mmcv/engine/__init__.py | 8 - .../real_basicvsr/pytorch/mmcv/engine/test.py | 202 --- .../pytorch/mmcv/fileio/__init__.py | 11 - .../pytorch/mmcv/fileio/file_client.py | 1148 -------------- .../pytorch/mmcv/fileio/handlers/__init__.py | 7 - .../pytorch/mmcv/fileio/handlers/base.py | 30 - .../mmcv/fileio/handlers/json_handler.py | 36 - .../mmcv/fileio/handlers/pickle_handler.py | 28 - .../mmcv/fileio/handlers/yaml_handler.py | 24 - .../real_basicvsr/pytorch/mmcv/fileio/io.py | 151 -- .../pytorch/mmcv/fileio/parse.py | 97 -- .../pytorch/mmcv/image/__init__.py | 28 - .../pytorch/mmcv/image/colorspace.py | 306 ---- .../pytorch/mmcv/image/geometric.py | 728 --------- .../real_basicvsr/pytorch/mmcv/image/io.py | 258 ---- .../real_basicvsr/pytorch/mmcv/image/misc.py | 44 - .../pytorch/mmcv/image/photometric.py | 428 ------ .../pytorch/mmcv/ops/__init__.py | 15 - .../csrc/common/cuda/common_cuda_helper.hpp | 112 -- .../modulated_deform_conv_cuda_kernel.cuh | 399 ----- .../csrc/common/cuda/sync_bn_cuda_kernel.cuh | 331 ---- .../ops/csrc/common/pytorch_cpp_helper.hpp | 24 - .../ops/csrc/common/pytorch_cuda_helper.hpp | 19 - .../cuda/modulated_deform_conv_cuda.cu | 96 -- .../ops/csrc/pytorch/cuda/sync_bn_cuda.cu | 110 -- .../pytorch/mmcv/ops/csrc/pytorch/info.cpp | 56 - .../csrc/pytorch/modulated_deform_conv.cpp | 338 ----- .../pytorch/modulated_deform_conv_cpu.cpp | 403 ----- .../pytorch/mmcv/ops/csrc/pytorch/pybind.cpp | 82 - .../pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp | 159 -- .../pytorch/mmcv/ops/deprecated_wrappers.py | 43 - .../real_basicvsr/pytorch/mmcv/ops/info.py | 36 - .../pytorch/mmcv/ops/modulated_deform_conv.py | 282 ---- .../real_basicvsr/pytorch/mmcv/ops/sync_bn.py | 279 ---- .../pytorch/mmcv/parallel/__init__.py | 13 - .../pytorch/mmcv/parallel/_functions.py | 79 - .../pytorch/mmcv/parallel/collate.py | 84 -- .../pytorch/mmcv/parallel/data_container.py | 89 -- .../pytorch/mmcv/parallel/data_parallel.py | 89 -- .../pytorch/mmcv/parallel/distributed.py | 112 -- .../mmcv/parallel/distributed_deprecated.py | 70 - .../pytorch/mmcv/parallel/registry.py | 8 - .../pytorch/mmcv/parallel/scatter_gather.py | 59 - .../pytorch/mmcv/parallel/utils.py | 20 - .../pytorch/mmcv/runner/__init__.py | 47 - .../pytorch/mmcv/runner/base_module.py | 195 --- .../pytorch/mmcv/runner/base_runner.py | 542 ------- .../pytorch/mmcv/runner/builder.py | 24 - .../pytorch/mmcv/runner/checkpoint.py | 707 --------- .../mmcv/runner/default_constructor.py | 44 - .../pytorch/mmcv/runner/dist_utils.py | 164 -- .../pytorch/mmcv/runner/epoch_based_runner.py | 187 --- .../pytorch/mmcv/runner/fp16_utils.py | 410 ----- .../pytorch/mmcv/runner/hooks/__init__.py | 29 - .../pytorch/mmcv/runner/hooks/checkpoint.py | 167 --- .../pytorch/mmcv/runner/hooks/closure.py | 11 - .../pytorch/mmcv/runner/hooks/ema.py | 89 -- .../pytorch/mmcv/runner/hooks/evaluation.py | 509 ------- .../pytorch/mmcv/runner/hooks/hook.py | 92 -- .../pytorch/mmcv/runner/hooks/iter_timer.py | 30 - .../mmcv/runner/hooks/logger/__init__.py | 15 - .../pytorch/mmcv/runner/hooks/logger/base.py | 166 -- .../mmcv/runner/hooks/logger/dvclive.py | 58 - .../mmcv/runner/hooks/logger/mlflow.py | 78 - .../mmcv/runner/hooks/logger/neptune.py | 82 - .../pytorch/mmcv/runner/hooks/logger/pavi.py | 117 -- .../mmcv/runner/hooks/logger/tensorboard.py | 57 - .../pytorch/mmcv/runner/hooks/logger/text.py | 256 ---- .../pytorch/mmcv/runner/hooks/logger/wandb.py | 56 - .../pytorch/mmcv/runner/hooks/lr_updater.py | 670 --------- .../pytorch/mmcv/runner/hooks/memory.py | 25 - .../mmcv/runner/hooks/momentum_updater.py | 493 ------ .../pytorch/mmcv/runner/hooks/optimizer.py | 508 ------- .../pytorch/mmcv/runner/hooks/profiler.py | 180 --- .../pytorch/mmcv/runner/hooks/sampler_seed.py | 20 - .../pytorch/mmcv/runner/hooks/sync_buffer.py | 22 - .../pytorch/mmcv/runner/iter_based_runner.py | 273 ---- .../pytorch/mmcv/runner/log_buffer.py | 41 - .../pytorch/mmcv/runner/optimizer/__init__.py | 9 - .../pytorch/mmcv/runner/optimizer/builder.py | 44 - .../runner/optimizer/default_constructor.py | 249 --- .../pytorch/mmcv/runner/priority.py | 60 - .../pytorch/mmcv/runner/utils.py | 93 -- .../pytorch/mmcv/utils/__init__.py | 69 - .../pytorch/mmcv/utils/config.py | 688 --------- .../real_basicvsr/pytorch/mmcv/utils/env.py | 95 -- .../pytorch/mmcv/utils/ext_loader.py | 71 - .../pytorch/mmcv/utils/logging.py | 110 -- .../real_basicvsr/pytorch/mmcv/utils/misc.py | 377 ----- .../pytorch/mmcv/utils/parrots_jit.py | 41 - .../pytorch/mmcv/utils/parrots_wrapper.py | 107 -- .../real_basicvsr/pytorch/mmcv/utils/path.py | 101 -- .../pytorch/mmcv/utils/progressbar.py | 208 --- .../pytorch/mmcv/utils/registry.py | 315 ---- .../pytorch/mmcv/utils/testing.py | 140 -- .../real_basicvsr/pytorch/mmcv/utils/timer.py | 118 -- .../real_basicvsr/pytorch/mmcv/utils/trace.py | 23 - .../pytorch/mmcv/utils/version_utils.py | 90 -- .../real_basicvsr/pytorch/mmcv/version.py | 35 - .../real_basicvsr/pytorch/mmedit/__init__.py | 34 - .../pytorch/mmedit/apis/__init__.py | 18 - .../mmedit/apis/generation_inference.py | 63 - .../mmedit/apis/inpainting_inference.py | 53 - .../pytorch/mmedit/apis/matting_inference.py | 78 - .../mmedit/apis/restoration_face_inference.py | 90 -- .../mmedit/apis/restoration_inference.py | 48 - .../apis/restoration_video_inference.py | 129 -- .../real_basicvsr/pytorch/mmedit/apis/test.py | 234 --- .../pytorch/mmedit/apis/train.py | 361 ----- .../apis/video_interpolation_inference.py | 204 --- .../pytorch/mmedit/core/__init__.py | 13 - .../mmedit/core/distributed_wrapper.py | 139 -- .../mmedit/core/evaluation/__init__.py | 10 - .../mmedit/core/evaluation/eval_hooks.py | 114 -- .../mmedit/core/evaluation/metric_utils.py | 81 - .../pytorch/mmedit/core/evaluation/metrics.py | 572 ------- .../core/evaluation/niqe_pris_params.npz | Bin 11850 -> 0 bytes .../pytorch/mmedit/core/export/__init__.py | 4 - .../pytorch/mmedit/core/export/wrappers.py | 134 -- .../pytorch/mmedit/core/hooks/__init__.py | 5 - .../pytorch/mmedit/core/hooks/ema.py | 113 -- .../mmedit/core/hooks/visualization.py | 84 -- .../real_basicvsr/pytorch/mmedit/core/mask.py | 316 ---- .../real_basicvsr/pytorch/mmedit/core/misc.py | 74 - .../pytorch/mmedit/core/optimizer/__init__.py | 4 - .../pytorch/mmedit/core/optimizer/builder.py | 58 - .../pytorch/mmedit/core/scheduler/__init__.py | 4 - .../mmedit/core/scheduler/lr_updater.py | 304 ---- .../pytorch/mmedit/core/utils/__init__.py | 4 - .../pytorch/mmedit/core/utils/dist_utils.py | 35 - .../pytorch/mmedit/datasets/__init__.py | 11 - .../pytorch/mmedit/datasets/base_dataset.py | 78 - .../mmedit/datasets/base_sr_dataset.py | 86 -- .../pytorch/mmedit/datasets/builder.py | 181 --- .../mmedit/datasets/dataset_wrappers.py | 39 - .../mmedit/datasets/pipelines/__init__.py | 25 - .../mmedit/datasets/pipelines/augmentation.py | 1332 ----------------- .../mmedit/datasets/pipelines/blur_kernels.py | 536 ------- .../mmedit/datasets/pipelines/compose.py | 53 - .../pytorch/mmedit/datasets/pipelines/crop.py | 749 --------- .../mmedit/datasets/pipelines/formating.py | 263 ---- .../mmedit/datasets/pipelines/loading.py | 562 ------- .../datasets/pipelines/matlab_like_resize.py | 275 ---- .../datasets/pipelines/normalization.py | 103 -- .../datasets/pipelines/random_degradations.py | 556 ------- .../mmedit/datasets/pipelines/utils.py | 154 -- .../pytorch/mmedit/datasets/registry.py | 5 - .../mmedit/datasets/samplers/__init__.py | 4 - .../datasets/samplers/distributed_sampler.py | 71 - .../datasets/sr_folder_multiple_gt_dataset.py | 119 -- .../pytorch/mmedit/models/__init__.py | 13 - .../mmedit/models/backbones/__init__.py | 6 - .../models/backbones/sr_backbones/__init__.py | 6 - .../backbones/sr_backbones/basicvsr_net.py | 420 ------ .../sr_backbones/real_basicvsr_net.py | 108 -- .../pytorch/mmedit/models/base.py | 105 -- .../pytorch/mmedit/models/builder.py | 60 - .../pytorch/mmedit/models/common/__init__.py | 32 - .../pytorch/mmedit/models/common/aspp.py | 125 -- .../models/common/contextual_attention.py | 379 ----- .../pytorch/mmedit/models/common/conv.py | 6 - .../mmedit/models/common/downsample.py | 22 - .../pytorch/mmedit/models/common/ensemble.py | 105 -- .../pytorch/mmedit/models/common/flow_warp.py | 50 - .../mmedit/models/common/gated_conv_module.py | 72 - .../mmedit/models/common/gca_module.py | 358 ----- .../models/common/generation_model_utils.py | 301 ---- .../mmedit/models/common/img_normalize.py | 32 - .../mmedit/models/common/linear_module.py | 89 -- .../mmedit/models/common/mask_conv_module.py | 88 -- .../mmedit/models/common/model_utils.py | 136 -- .../mmedit/models/common/partial_conv.py | 102 -- .../models/common/separable_conv_module.py | 97 -- .../mmedit/models/common/sr_backbone_utils.py | 97 -- .../pytorch/mmedit/models/common/upsample.py | 51 - .../mmedit/models/components/__init__.py | 2 - .../components/discriminators/__init__.py | 6 - .../components/discriminators/unet_disc.py | 112 -- .../pytorch/mmedit/models/losses/__init__.py | 10 - .../pytorch/mmedit/models/losses/gan_loss.py | 344 ----- .../mmedit/models/losses/perceptual_loss.py | 287 ---- .../mmedit/models/losses/pixelwise_loss.py | 221 --- .../pytorch/mmedit/models/losses/utils.py | 115 -- .../pytorch/mmedit/models/registry.py | 8 - .../mmedit/models/restorers/__init__.py | 7 - .../mmedit/models/restorers/basic_restorer.py | 210 --- .../mmedit/models/restorers/real_basicvsr.py | 239 --- .../mmedit/models/restorers/real_esrgan.py | 234 --- .../pytorch/mmedit/models/restorers/srgan.py | 176 --- .../pytorch/mmedit/utils/__init__.py | 6 - .../real_basicvsr/pytorch/mmedit/utils/cli.py | 18 - .../pytorch/mmedit/utils/collect_env.py | 18 - .../pytorch/mmedit/utils/logger.py | 27 - .../pytorch/mmedit/utils/setup_env.py | 47 - .../real_basicvsr/pytorch/mmedit/version.py | 18 - .../real_basicvsr/pytorch/requirements.txt | 3 - .../real_basicvsr/pytorch/setup.py | 354 ----- .../real_basicvsr/pytorch/test.py | 159 -- .../real_basicvsr/pytorch/train.py | 172 --- 246 files changed, 22 insertions(+), 36856 deletions(-) delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/.gitignore delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/LICENSE delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/build_env.sh delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/configs/real_basicvsr/realbasicvsr_c64b20_1x30x8_lr5e-5_150k_reds.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/configs/real_basicvsr/realbasicvsr_wogan_c64b20_2x30x8_lr1e-4_300k_reds.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/crop_sub_images.py delete mode 100644 cv/super_resolution/real_basicvsr/pytorch/dist_test.sh delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/dist_train.sh delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/docker/Dockerfile delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/docker/README.md delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/arraymisc/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/arraymisc/quantization.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/alexnet.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/activation.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/context_block.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv_module.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv_ws.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/drop.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/generalized_attention.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/hsigmoid.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/hswish.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/non_local.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/norm.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/padding.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/plugin.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/registry.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/scale.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/swish.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/transformer.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/upsample.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/wrappers.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/builder.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/resnet.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/flops_counter.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/fuse_conv_bn.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/sync_bn.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/weight_init.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/vgg.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/engine/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/engine/test.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/file_client.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/base.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/json_handler.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/pickle_handler.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/yaml_handler.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/io.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/parse.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/image/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/image/colorspace.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/image/geometric.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/image/io.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/image/misc.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/image/photometric.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/cuda/modulated_deform_conv_cuda_kernel.cuh delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/cuda/modulated_deform_conv_cuda.cu delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/info.cpp delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv.cpp delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv_cpu.cpp delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/deprecated_wrappers.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/info.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/modulated_deform_conv.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/sync_bn.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/_functions.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/collate.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/data_container.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/data_parallel.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/distributed.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/distributed_deprecated.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/registry.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/scatter_gather.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/utils.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/base_module.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/base_runner.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/builder.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/checkpoint.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/default_constructor.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/dist_utils.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/epoch_based_runner.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/fp16_utils.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/checkpoint.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/closure.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/ema.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/evaluation.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/hook.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/iter_timer.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/base.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/dvclive.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/mlflow.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/neptune.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/pavi.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/tensorboard.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/text.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/wandb.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/lr_updater.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/memory.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/momentum_updater.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/optimizer.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/profiler.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/sampler_seed.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/sync_buffer.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/iter_based_runner.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/log_buffer.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/optimizer/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/optimizer/builder.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/optimizer/default_constructor.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/priority.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/utils.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/config.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/env.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/ext_loader.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/logging.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/misc.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/parrots_jit.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/parrots_wrapper.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/path.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/progressbar.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/registry.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/testing.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/timer.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/trace.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/version_utils.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmcv/version.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/generation_inference.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/inpainting_inference.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/matting_inference.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/restoration_face_inference.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/restoration_inference.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/restoration_video_inference.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/test.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/train.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/video_interpolation_inference.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/distributed_wrapper.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/eval_hooks.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/metric_utils.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/metrics.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/niqe_pris_params.npz delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/export/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/export/wrappers.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/hooks/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/hooks/ema.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/hooks/visualization.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/mask.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/misc.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/optimizer/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/optimizer/builder.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/scheduler/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/scheduler/lr_updater.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/utils/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/core/utils/dist_utils.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/base_dataset.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/base_sr_dataset.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/builder.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/dataset_wrappers.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/augmentation.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/blur_kernels.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/compose.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/crop.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/formating.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/loading.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/matlab_like_resize.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/normalization.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/random_degradations.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/utils.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/registry.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/samplers/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/samplers/distributed_sampler.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/sr_folder_multiple_gt_dataset.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/sr_backbones/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/sr_backbones/basicvsr_net.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/sr_backbones/real_basicvsr_net.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/base.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/builder.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/aspp.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/contextual_attention.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/conv.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/downsample.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/ensemble.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/flow_warp.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/gated_conv_module.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/gca_module.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/generation_model_utils.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/img_normalize.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/linear_module.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/mask_conv_module.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/model_utils.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/partial_conv.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/separable_conv_module.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/sr_backbone_utils.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/upsample.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/components/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/components/discriminators/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/components/discriminators/unet_disc.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/gan_loss.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/perceptual_loss.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/pixelwise_loss.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/utils.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/registry.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/basic_restorer.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/real_basicvsr.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/real_esrgan.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/srgan.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/__init__.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/cli.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/collect_env.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/logger.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/setup_env.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/mmedit/version.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/requirements.txt delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/setup.py delete mode 100644 cv/super_resolution/real_basicvsr/pytorch/test.py delete mode 100755 cv/super_resolution/real_basicvsr/pytorch/train.py diff --git a/cv/super_resolution/basicvsr++/pytorch/README.md b/cv/super_resolution/basicvsr++/pytorch/README.md index c5058120b..675b3a870 100755 --- a/cv/super_resolution/basicvsr++/pytorch/README.md +++ b/cv/super_resolution/basicvsr++/pytorch/README.md @@ -23,7 +23,7 @@ pip install albumentations ## Step 2: Preparing datasets -Download REDS dataset from [homepage](https://seungjunnah.github.io/Datasets/reds.html) +Download REDS dataset from [homepage](https://seungjunnah.github.io/Datasets/reds.html) or you can follow tools/dataset_converters/reds/README.md ```shell mkdir -p data/ ln -s ${REDS_DATASET_PATH} data/REDS diff --git a/cv/super_resolution/real_basicvsr/pytorch/.gitignore b/cv/super_resolution/real_basicvsr/pytorch/.gitignore deleted file mode 100755 index d31714deb..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/.gitignore +++ /dev/null @@ -1,140 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class -**/*.pyc - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/en/_build/ -docs/en/_tmp/ -docs/zh_cn/_build/ -docs/zh_cn/_tmp/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -# custom -.vscode -.idea -*.pkl -*.pkl.json -*.log.json -work_dirs/ -/data/ -/data -mmedit/.mim - -# Pytorch -*.pth - -# onnx and tensorrt -*.onnx -*.trt - -# local history -.history/** - -# Pytorch Server -*.mar - -# MacOS -.DS_Store -work_dirs/ -tests/ -demo/ -tools/ -experiments/ diff --git a/cv/super_resolution/real_basicvsr/pytorch/LICENSE b/cv/super_resolution/real_basicvsr/pytorch/LICENSE deleted file mode 100755 index 94d620d25..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ -Copyright (c) MMEditing Authors. All rights reserved. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020 MMEditing Authors. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/cv/super_resolution/real_basicvsr/pytorch/README.md b/cv/super_resolution/real_basicvsr/pytorch/README.md index fc8ed346c..a0ac404dd 100755 --- a/cv/super_resolution/real_basicvsr/pytorch/README.md +++ b/cv/super_resolution/real_basicvsr/pytorch/README.md @@ -8,47 +8,43 @@ The diversity and complexity of degradations in real-world video super-resolutio ## Step 1: Installing packages ```shell -bash build_env.sh +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +git clone https://github.com/open-mmlab/mmagic.git -b v1.2.0 --depth=1 +cd mmagic/ +pip3 install -e . -v + +sed -i 's/diffusers.models.unet_2d_condition/diffusers.models.unets.unet_2d_condition/g' mmagic/models/editors/vico/vico_utils.py +pip install albumentations av==12.0.0 ``` ## Step 2: Preparing datasets -```shell - -# Download REDS -mkdir -p data/REDS -# Homepage of REDS: https://seungjunnah.github.io/Datasets/reds.html -python3 crop_sub_images.py # cut REDS images into patches for fas - -# Download UDM10 -cd .. -# Homepage of UDM10: https://www.terabox.com/web/share/link?surl=LMuQCVntRegfZSxn7s3hXw&path=%2Fproject%2Fpfnl -``` -## Step 3: Download pretrained weights +Download UDM10 https://www.terabox.com/web/share/link?surl=LMuQCVntRegfZSxn7s3hXw&path=%2Fproject%2Fpfnl to data/UDM10 +Download REDS dataset from [homepage](https://seungjunnah.github.io/Datasets/reds.html) or you can follow tools/dataset_converters/reds/README.md ```shell -mkdir pretrained && cd pretrained -wget https://download.openmmlab.com/mmediting/restorers/basicvsr/spynet_20210409-c6c1bd09.pth -wget https://download.openmmlab.com/mmediting/restorers/real_basicvsr/realbasicvsr_wogan_c64b20_2x30x8_lr1e-4_300k_reds_20211027-0e2ff207.pth -wget https://download.pytorch.org/models/vgg19-dcbb9e9d.pth -cd .. +mkdir -p data/ +ln -s ${REDS_DATASET_PATH} data/REDS +python tools/dataset_converters/reds/crop_sub_images.py --data-root ./data/REDS # cut REDS images into patches for fas ``` ## Step 3: Training ### Training on single card ```shell -python3 train.py [training args] # config file can be found in the configs directory +python3 tools/train.py configs/real_basicvsr/realbasicvsr_wogan-c64b20-2x30x8_8xb2-lr1e-4-300k_reds.py ``` ### Mutiple GPUs on one machine ```shell -bash dist_train.sh [training args] # config file can be found in the configs directory +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/real_basicvsr/realbasicvsr_wogan-c64b20-2x30x8_8xb2-lr1e-4-300k_reds.py ``` -### Example -```shell -python3 train.py configs/real_basicvsr/realbasicvsr_wogan_c64b20_2x30x8_lr1e-4_300k_reds.py -``` ## Reference -https://github.com/open-mmlab/mmediting +[mmagic](https://github.com/open-mmlab/mmagic) diff --git a/cv/super_resolution/real_basicvsr/pytorch/build_env.sh b/cv/super_resolution/real_basicvsr/pytorch/build_env.sh deleted file mode 100755 index 5c77a38df..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/build_env.sh +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. - - -PIPCMD=pip3 - -MMCV_WITH_OPS=1 python3 setup.py build && cp build/lib.linux*/mmcv/_ext.cpython* mmcv -$PIPCMD install -r requirements.txt diff --git a/cv/super_resolution/real_basicvsr/pytorch/configs/real_basicvsr/realbasicvsr_c64b20_1x30x8_lr5e-5_150k_reds.py b/cv/super_resolution/real_basicvsr/pytorch/configs/real_basicvsr/realbasicvsr_c64b20_1x30x8_lr5e-5_150k_reds.py deleted file mode 100755 index 8d0aaed80..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/configs/real_basicvsr/realbasicvsr_c64b20_1x30x8_lr5e-5_150k_reds.py +++ /dev/null @@ -1,341 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -exp_name = 'realbasicvsr_c64b20_1x30x8_lr5e-5_150k_reds' - -scale = 4 - -# model settings -model = dict( - type='RealBasicVSR', - generator=dict( - type='RealBasicVSRNet', - mid_channels=64, - num_propagation_blocks=20, - num_cleaning_blocks=20, - dynamic_refine_thres=255, # change to 5 for test - spynet_pretrained='pretrained/spynet_20210409-c6c1bd09.pth', - is_fix_cleaning=False, - is_sequential_cleaning=False), - discriminator=dict( - type='UNetDiscriminatorWithSpectralNorm', - in_channels=3, - mid_channels=64, - skip_connection=True), - pixel_loss=dict(type='L1Loss', loss_weight=1.0, reduction='mean'), - cleaning_loss=dict(type='L1Loss', loss_weight=1.0, reduction='mean'), - perceptual_loss=dict( - type='PerceptualLoss', - layer_weights={ - '2': 0.1, - '7': 0.1, - '16': 1.0, - '25': 1.0, - '34': 1.0, - }, - vgg_type='vgg19', - perceptual_weight=1.0, - style_weight=0, - norm_img=False, - pretrained='pretrained/vgg19-dcbb9e9d.pth'), - gan_loss=dict( - type='GANLoss', - gan_type='vanilla', - loss_weight=5e-2, - real_label_val=1.0, - fake_label_val=0), - is_use_sharpened_gt_in_pixel=True, - is_use_sharpened_gt_in_percep=True, - is_use_sharpened_gt_in_gan=False, - is_use_ema=True, -) - -# model training and testing settings -train_cfg = dict() -test_cfg = dict(metrics=['PSNR'], crop_border=0) # change to [] for test - -# dataset settings -train_dataset_type = 'SRFolderMultipleGTDataset' -val_dataset_type = 'SRFolderMultipleGTDataset' -train_pipeline = [ - dict(type='GenerateSegmentIndices', interval_list=[1]), - dict( - type='LoadImageFromFileList', - io_backend='disk', - key='gt', - channel_order='rgb'), - dict(type='FixedCrop', keys=['gt'], crop_size=(256, 256)), - dict(type='RescaleToZeroOne', keys=['gt']), - dict(type='Flip', keys=['gt'], flip_ratio=0.5, direction='horizontal'), - dict(type='Flip', keys=['gt'], flip_ratio=0.5, direction='vertical'), - dict(type='RandomTransposeHW', keys=['gt'], transpose_ratio=0.5), - dict(type='MirrorSequence', keys=['gt']), - dict( - type='UnsharpMasking', - keys=['gt'], - kernel_size=51, - sigma=0, - weight=0.5, - threshold=10), - dict(type='CopyValues', src_keys=['gt_unsharp'], dst_keys=['lq']), - dict( - type='RandomBlur', - params=dict( - kernel_size=[7, 9, 11, 13, 15, 17, 19, 21], - kernel_list=[ - 'iso', 'aniso', 'generalized_iso', 'generalized_aniso', - 'plateau_iso', 'plateau_aniso', 'sinc' - ], - kernel_prob=[0.405, 0.225, 0.108, 0.027, 0.108, 0.027, 0.1], - sigma_x=[0.2, 3], - sigma_y=[0.2, 3], - rotate_angle=[-3.1416, 3.1416], - beta_gaussian=[0.5, 4], - beta_plateau=[1, 2], - sigma_x_step=0.02, - sigma_y_step=0.02, - rotate_angle_step=0.31416, - beta_gaussian_step=0.05, - beta_plateau_step=0.1, - omega_step=0.0628), - keys=['lq'], - ), - dict( - type='RandomResize', - params=dict( - resize_mode_prob=[0.2, 0.7, 0.1], # up, down, keep - resize_scale=[0.15, 1.5], - resize_opt=['bilinear', 'area', 'bicubic'], - resize_prob=[1 / 3.0, 1 / 3.0, 1 / 3.0], - resize_step=0.015, - is_size_even=True), - keys=['lq'], - ), - dict( - type='RandomNoise', - params=dict( - noise_type=['gaussian', 'poisson'], - noise_prob=[0.5, 0.5], - gaussian_sigma=[1, 30], - gaussian_gray_noise_prob=0.4, - poisson_scale=[0.05, 3], - poisson_gray_noise_prob=0.4, - gaussian_sigma_step=0.1, - poisson_scale_step=0.005), - keys=['lq'], - ), - dict( - type='RandomJPEGCompression', - params=dict(quality=[30, 95], quality_step=3), - keys=['lq'], - ), - dict( - type='RandomVideoCompression', - params=dict( - codec=['libx264', 'h264', 'mpeg4'], - codec_prob=[1 / 3., 1 / 3., 1 / 3.], - bitrate=[1e4, 1e5]), - keys=['lq'], - ), - dict( - type='RandomBlur', - params=dict( - prob=0.8, - kernel_size=[7, 9, 11, 13, 15, 17, 19, 21], - kernel_list=[ - 'iso', 'aniso', 'generalized_iso', 'generalized_aniso', - 'plateau_iso', 'plateau_aniso', 'sinc' - ], - kernel_prob=[0.405, 0.225, 0.108, 0.027, 0.108, 0.027, 0.1], - sigma_x=[0.2, 1.5], - sigma_y=[0.2, 1.5], - rotate_angle=[-3.1416, 3.1416], - beta_gaussian=[0.5, 4], - beta_plateau=[1, 2], - sigma_x_step=0.02, - sigma_y_step=0.02, - rotate_angle_step=0.31416, - beta_gaussian_step=0.05, - beta_plateau_step=0.1, - omega_step=0.0628), - keys=['lq'], - ), - dict( - type='RandomResize', - params=dict( - resize_mode_prob=[0.3, 0.4, 0.3], # up, down, keep - resize_scale=[0.3, 1.2], - resize_opt=['bilinear', 'area', 'bicubic'], - resize_prob=[1 / 3., 1 / 3., 1 / 3.], - resize_step=0.03, - is_size_even=True), - keys=['lq'], - ), - dict( - type='RandomNoise', - params=dict( - noise_type=['gaussian', 'poisson'], - noise_prob=[0.5, 0.5], - gaussian_sigma=[1, 25], - gaussian_gray_noise_prob=0.4, - poisson_scale=[0.05, 2.5], - poisson_gray_noise_prob=0.4, - gaussian_sigma_step=0.1, - poisson_scale_step=0.005), - keys=['lq'], - ), - dict( - type='RandomJPEGCompression', - params=dict(quality=[30, 95], quality_step=3), - keys=['lq'], - ), - dict( - type='DegradationsWithShuffle', - degradations=[ - dict( - type='RandomVideoCompression', - params=dict( - codec=['libx264', 'h264', 'mpeg4'], - codec_prob=[1 / 3., 1 / 3., 1 / 3.], - bitrate=[1e4, 1e5]), - keys=['lq'], - ), - [ - dict( - type='RandomResize', - params=dict( - target_size=(64, 64), - resize_opt=['bilinear', 'area', 'bicubic'], - resize_prob=[1 / 3., 1 / 3., 1 / 3.]), - ), - dict( - type='RandomBlur', - params=dict( - prob=0.8, - kernel_size=[7, 9, 11, 13, 15, 17, 19, 21], - kernel_list=['sinc'], - kernel_prob=[1], - omega=[3.1416 / 3, 3.1416], - omega_step=0.0628), - ), - ] - ], - keys=['lq'], - ), - dict(type='Quantize', keys=['lq']), - dict(type='FramesToTensor', keys=['lq', 'gt', 'gt_unsharp']), - dict( - type='Collect', keys=['lq', 'gt', 'gt_unsharp'], meta_keys=['gt_path']) -] - -val_pipeline = [ - dict(type='GenerateSegmentIndices', interval_list=[1]), - dict( - type='LoadImageFromFileList', - io_backend='disk', - key='lq', - channel_order='rgb'), - dict( - type='LoadImageFromFileList', - io_backend='disk', - key='gt', - channel_order='rgb'), - dict(type='RescaleToZeroOne', keys=['lq', 'gt']), - dict(type='FramesToTensor', keys=['lq', 'gt']), - dict( - type='Collect', - keys=['lq', 'gt'], - meta_keys=['lq_path', 'gt_path', 'key']) -] - -test_pipeline = [ - dict( - type='GenerateSegmentIndices', - interval_list=[1], - filename_tmpl='{:08d}.png'), - dict( - type='LoadImageFromFileList', - io_backend='disk', - key='lq', - channel_order='rgb'), - dict(type='RescaleToZeroOne', keys=['lq']), - dict(type='FramesToTensor', keys=['lq']), - dict(type='Collect', keys=['lq'], meta_keys=['lq_path', 'key']) -] - -data = dict( - workers_per_gpu=0, - train_dataloader=dict(samples_per_gpu=1, drop_last=True), - val_dataloader=dict(samples_per_gpu=1), - test_dataloader=dict(samples_per_gpu=1, workers_per_gpu=1), - - # train - train=dict( - type='RepeatDataset', - times=150, - dataset=dict( - type=train_dataset_type, - lq_folder='data/REDS/train/train_sharp_sub', - gt_folder='data/REDS/train/train_sharp_sub', - num_input_frames=15, - pipeline=train_pipeline, - scale=4, - test_mode=False)), - # val - val=dict( - type=val_dataset_type, - lq_folder='data/UDM10/BIx4', - gt_folder='data/UDM10/GT', - pipeline=val_pipeline, - scale=4, - test_mode=True), - # test - test=dict( - type=val_dataset_type, - #lq_folder='data/VideoLQ', - #gt_folder='data/VideoLQ', - - lq_folder='data/UDM10/BIx4', - gt_folder='data/UDM10/GT', - num_input_frames=15, - pipeline=test_pipeline, - scale=4, - test_mode=True), -) - -# optimizer -optimizers = dict( - generator=dict(type='Adam', lr=5e-5, betas=(0.9, 0.99)), - discriminator=dict(type='Adam', lr=1e-4, betas=(0.9, 0.99))) - -# learning policy -total_iters = 150000 -lr_config = dict(policy='Step', by_epoch=False, step=[400000], gamma=1) - -checkpoint_config = dict(interval=5000, save_optimizer=True, by_epoch=False) - -# remove gpu_collect=True in non distributed training -evaluation = dict(interval=5000, save_image=False)# gpu_collect=True) -log_config = dict( - interval=1, - hooks=[ - dict(type='TextLoggerHook', by_epoch=False), - dict(type='TensorboardLoggerHook'), - ]) -visual_config = None - -# custom hook -custom_hooks = [ - dict( - type='ExponentialMovingAverageHook', - module_keys=('generator_ema', ), - interval=1, - interp_cfg=dict(momentum=0.999), - ) -] - -dist_params = dict(backend='nccl') -log_level = 'INFO' -work_dir = f'./experiments/{exp_name}' -load_from = 'pretrained/realbasicvsr_wogan_c64b20_2x30x8_lr1e-4_300k_reds_20211027-0e2ff207.pth' # noqa -resume_from = None -workflow = [('train', 1)] -cudnn_benchmark = True diff --git a/cv/super_resolution/real_basicvsr/pytorch/configs/real_basicvsr/realbasicvsr_wogan_c64b20_2x30x8_lr1e-4_300k_reds.py b/cv/super_resolution/real_basicvsr/pytorch/configs/real_basicvsr/realbasicvsr_wogan_c64b20_2x30x8_lr1e-4_300k_reds.py deleted file mode 100755 index 1558e2761..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/configs/real_basicvsr/realbasicvsr_wogan_c64b20_2x30x8_lr1e-4_300k_reds.py +++ /dev/null @@ -1,309 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -exp_name = 'realbasicvsr_wogan_c64b20_2x30x8_lr1e-4_300k_reds' - -scale = 4 - -# model settings -model = dict( - type='RealBasicVSR', - generator=dict( - type='RealBasicVSRNet', - mid_channels=64, - num_propagation_blocks=20, - num_cleaning_blocks=20, - dynamic_refine_thres=255, # change to 1.5 for test - spynet_pretrained='pretrained/spynet_20210409-c6c1bd09.pth', - is_fix_cleaning=False, - is_sequential_cleaning=False), - pixel_loss=dict(type='L1Loss', loss_weight=1.0, reduction='mean'), - cleaning_loss=dict(type='L1Loss', loss_weight=1.0, reduction='mean'), - is_use_sharpened_gt_in_pixel=True, - is_use_ema=True, -) - -# model training and testing settings -train_cfg = dict() -test_cfg = dict(metrics=['PSNR'], crop_border=0) # change to [] for test - -# dataset settings -train_dataset_type = 'SRFolderMultipleGTDataset' -val_dataset_type = 'SRFolderMultipleGTDataset' -train_pipeline = [ - dict(type='GenerateSegmentIndices', interval_list=[1]), - dict( - type='LoadImageFromFileList', - io_backend='disk', - key='gt', - channel_order='rgb'), - dict(type='FixedCrop', keys=['gt'], crop_size=(256, 256)), - dict(type='RescaleToZeroOne', keys=['gt']), - dict(type='Flip', keys=['gt'], flip_ratio=0.5, direction='horizontal'), - dict(type='Flip', keys=['gt'], flip_ratio=0.5, direction='vertical'), - dict(type='RandomTransposeHW', keys=['gt'], transpose_ratio=0.5), - dict(type='MirrorSequence', keys=['gt']), - dict( - type='UnsharpMasking', - keys=['gt'], - kernel_size=51, - sigma=0, - weight=0.5, - threshold=10), - dict(type='CopyValues', src_keys=['gt_unsharp'], dst_keys=['lq']), - dict( - type='RandomBlur', - params=dict( - kernel_size=[7, 9, 11, 13, 15, 17, 19, 21], - kernel_list=[ - 'iso', 'aniso', 'generalized_iso', 'generalized_aniso', - 'plateau_iso', 'plateau_aniso', 'sinc' - ], - kernel_prob=[0.405, 0.225, 0.108, 0.027, 0.108, 0.027, 0.1], - sigma_x=[0.2, 3], - sigma_y=[0.2, 3], - rotate_angle=[-3.1416, 3.1416], - beta_gaussian=[0.5, 4], - beta_plateau=[1, 2], - sigma_x_step=0.02, - sigma_y_step=0.02, - rotate_angle_step=0.31416, - beta_gaussian_step=0.05, - beta_plateau_step=0.1, - omega_step=0.0628), - keys=['lq'], - ), - dict( - type='RandomResize', - params=dict( - resize_mode_prob=[0.2, 0.7, 0.1], # up, down, keep - resize_scale=[0.15, 1.5], - resize_opt=['bilinear', 'area', 'bicubic'], - resize_prob=[1 / 3.0, 1 / 3.0, 1 / 3.0], - resize_step=0.015, - is_size_even=True), - keys=['lq'], - ), - dict( - type='RandomNoise', - params=dict( - noise_type=['gaussian', 'poisson'], - noise_prob=[0.5, 0.5], - gaussian_sigma=[1, 30], - gaussian_gray_noise_prob=0.4, - poisson_scale=[0.05, 3], - poisson_gray_noise_prob=0.4, - gaussian_sigma_step=0.1, - poisson_scale_step=0.005), - keys=['lq'], - ), - dict( - type='RandomJPEGCompression', - params=dict(quality=[30, 95], quality_step=3), - keys=['lq'], - ), - dict( - type='RandomVideoCompression', - params=dict( - codec=['libx264', 'h264', 'mpeg4'], - codec_prob=[1 / 3., 1 / 3., 1 / 3.], - bitrate=[1e4, 1e5]), - keys=['lq'], - ), - dict( - type='RandomBlur', - params=dict( - prob=0.8, - kernel_size=[7, 9, 11, 13, 15, 17, 19, 21], - kernel_list=[ - 'iso', 'aniso', 'generalized_iso', 'generalized_aniso', - 'plateau_iso', 'plateau_aniso', 'sinc' - ], - kernel_prob=[0.405, 0.225, 0.108, 0.027, 0.108, 0.027, 0.1], - sigma_x=[0.2, 1.5], - sigma_y=[0.2, 1.5], - rotate_angle=[-3.1416, 3.1416], - beta_gaussian=[0.5, 4], - beta_plateau=[1, 2], - sigma_x_step=0.02, - sigma_y_step=0.02, - rotate_angle_step=0.31416, - beta_gaussian_step=0.05, - beta_plateau_step=0.1, - omega_step=0.0628), - keys=['lq'], - ), - dict( - type='RandomResize', - params=dict( - resize_mode_prob=[0.3, 0.4, 0.3], # up, down, keep - resize_scale=[0.3, 1.2], - resize_opt=['bilinear', 'area', 'bicubic'], - resize_prob=[1 / 3., 1 / 3., 1 / 3.], - resize_step=0.03, - is_size_even=True), - keys=['lq'], - ), - dict( - type='RandomNoise', - params=dict( - noise_type=['gaussian', 'poisson'], - noise_prob=[0.5, 0.5], - gaussian_sigma=[1, 25], - gaussian_gray_noise_prob=0.4, - poisson_scale=[0.05, 2.5], - poisson_gray_noise_prob=0.4, - gaussian_sigma_step=0.1, - poisson_scale_step=0.005), - keys=['lq'], - ), - dict( - type='RandomJPEGCompression', - params=dict(quality=[30, 95], quality_step=3), - keys=['lq'], - ), - dict( - type='DegradationsWithShuffle', - degradations=[ - dict( - type='RandomVideoCompression', - params=dict( - codec=['libx264', 'h264', 'mpeg4'], - codec_prob=[1 / 3., 1 / 3., 1 / 3.], - bitrate=[1e4, 1e5]), - keys=['lq'], - ), - [ - dict( - type='RandomResize', - params=dict( - target_size=(64, 64), - resize_opt=['bilinear', 'area', 'bicubic'], - resize_prob=[1 / 3., 1 / 3., 1 / 3.]), - ), - dict( - type='RandomBlur', - params=dict( - prob=0.8, - kernel_size=[7, 9, 11, 13, 15, 17, 19, 21], - kernel_list=['sinc'], - kernel_prob=[1], - omega=[3.1416 / 3, 3.1416], - omega_step=0.0628), - ), - ] - ], - keys=['lq'], - ), - dict(type='Quantize', keys=['lq']), - dict(type='FramesToTensor', keys=['lq', 'gt', 'gt_unsharp']), - dict( - type='Collect', keys=['lq', 'gt', 'gt_unsharp'], meta_keys=['gt_path']) -] - -val_pipeline = [ - dict(type='GenerateSegmentIndices', interval_list=[1]), - dict( - type='LoadImageFromFileList', - io_backend='disk', - key='lq', - channel_order='rgb'), - dict( - type='LoadImageFromFileList', - io_backend='disk', - key='gt', - channel_order='rgb'), - dict(type='RescaleToZeroOne', keys=['lq', 'gt']), - dict(type='FramesToTensor', keys=['lq', 'gt']), - dict( - type='Collect', - keys=['lq', 'gt'], - meta_keys=['lq_path', 'gt_path', 'key']) -] - -test_pipeline = [ - dict( - type='GenerateSegmentIndices', - interval_list=[1], - filename_tmpl='{:08d}.png'), - dict( - type='LoadImageFromFileList', - io_backend='disk', - key='lq', - channel_order='rgb'), - dict(type='RescaleToZeroOne', keys=['lq']), - dict(type='FramesToTensor', keys=['lq']), - dict(type='Collect', keys=['lq'], meta_keys=['lq_path', 'key']) -] - -data = dict( - workers_per_gpu=10, - train_dataloader=dict( - samples_per_gpu=2, drop_last=True, persistent_workers=False), - val_dataloader=dict(samples_per_gpu=1, persistent_workers=False), - test_dataloader=dict(samples_per_gpu=1, workers_per_gpu=1), - - # train - train=dict( - type='RepeatDataset', - times=150, - dataset=dict( - type=train_dataset_type, - lq_folder='data/REDS/train/train_sharp', - gt_folder='data/REDS/train/train_sharp', - num_input_frames=15, - pipeline=train_pipeline, - scale=4, - test_mode=False)), - # val - val=dict( - type=val_dataset_type, - lq_folder='data/UDM10/BIx4', - gt_folder='data/UDM10/GT', - pipeline=val_pipeline, - scale=4, - test_mode=True), - # test - test=dict( - type=val_dataset_type, - lq_folder='data/VideoLQ', - gt_folder='data/VideoLQ', - pipeline=test_pipeline, - scale=4, - test_mode=True), -) - -# optimizer -optimizers = dict(generator=dict(type='Adam', lr=1e-4, betas=(0.9, 0.99))) - -# learning policy -total_iters = 300000 -lr_config = dict(policy='Step', by_epoch=False, step=[400000], gamma=1) - -checkpoint_config = dict(interval=5000, save_optimizer=True, by_epoch=False) - -# remove gpu_collect=True in non distributed training -evaluation = dict(interval=5000, save_image=False)# , gpu_collect=True) -log_config = dict( - interval=10, - hooks=[ - dict(type='TextLoggerHook', by_epoch=False), - dict(type='TensorboardLoggerHook'), - ]) -visual_config = None - -# custom hook -custom_hooks = [ - dict( - type='ExponentialMovingAverageHook', - module_keys=('generator_ema', ), - interval=1, - interp_cfg=dict(momentum=0.999), - ) -] - -dist_params = dict(backend='nccl') -log_level = 'INFO' -work_dir = f'./experiments/{exp_name}' -load_from = None -resume_from = None -workflow = [('train', 1)] -cudnn_benchmark = True diff --git a/cv/super_resolution/real_basicvsr/pytorch/crop_sub_images.py b/cv/super_resolution/real_basicvsr/pytorch/crop_sub_images.py deleted file mode 100755 index 60003e369..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/crop_sub_images.py +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import argparse -import os -import os.path as osp -import re -import sys -from multiprocessing import Pool - -import cv2 -import mmcv -import numpy as np - - -def worker(path, opt): - """Worker for each process. - - Args: - path (str): Image path. - opt (dict): Configuration dict. It contains: - crop_size (int): Crop size. - step (int): Step for overlapped sliding window. - thresh_size (int): Threshold size. Patches whose size is smaller - than thresh_size will be dropped. - save_folder (str): Path to save folder. - compression_level (int): for cv2.IMWRITE_PNG_COMPRESSION. - - Returns: - process_info (str): Process information displayed in progress bar. - """ - crop_size = opt['crop_size'] - step = opt['step'] - thresh_size = opt['thresh_size'] - sequence, img_name = re.split(r'[\\/]', path)[-2:] - img_name, extension = osp.splitext(osp.basename(path)) - - img = mmcv.imread(path, flag='unchanged') - - if img.ndim == 2 or img.ndim == 3: - h, w = img.shape[:2] - else: - raise ValueError(f'Image ndim should be 2 or 3, but got {img.ndim}') - - h_space = np.arange(0, h - crop_size + 1, step) - if h - (h_space[-1] + crop_size) > thresh_size: - h_space = np.append(h_space, h - crop_size) - w_space = np.arange(0, w - crop_size + 1, step) - if w - (w_space[-1] + crop_size) > thresh_size: - w_space = np.append(w_space, w - crop_size) - - index = 0 - for x in h_space: - for y in w_space: - index += 1 - cropped_img = img[x:x + crop_size, y:y + crop_size, ...] - sub_folder = osp.join(opt['save_folder'], - f'{sequence}_s{index:03d}') - mmcv.mkdir_or_exist(sub_folder) - cv2.imwrite( - osp.join(sub_folder, f'{img_name}{extension}'), cropped_img, - [cv2.IMWRITE_PNG_COMPRESSION, opt['compression_level']]) - process_info = f'Processing {img_name} ...' - return process_info - - -def extract_subimages(opt): - """Crop images to subimages. - - Args: - opt (dict): Configuration dict. It contains: - input_folder (str): Path to the input folder. - save_folder (str): Path to save folder. - n_thread (int): Thread number. - """ - input_folder = opt['input_folder'] - save_folder = opt['save_folder'] - if not osp.exists(save_folder): - os.makedirs(save_folder) - print(f'mkdir {save_folder} ...') - else: - print(f'Folder {save_folder} already exists. Exit.') - sys.exit(1) - - img_list = list(mmcv.scandir(input_folder, recursive=True)) - - img_list = [osp.join(input_folder, v) for v in img_list] - prog_bar = mmcv.ProgressBar(len(img_list)) - pool = Pool(opt['n_thread']) - for path in img_list: - pool.apply_async( - worker, args=(path, opt), callback=lambda arg: prog_bar.update()) - pool.close() - pool.join() - print('All processes done.') - - -def main_extract_subimages(args): - """A multi-thread tool to crop large images to sub-images for faster IO. - - It is used for REDS dataset. - - opt (dict): Configuration dict. It contains: - n_thread (int): Thread number. - compression_level (int): CV_IMWRITE_PNG_COMPRESSION from 0 to 9. - A higher value means a smaller size and longer compression time. - Use 0 for faster CPU decompression. Default: 3, same in cv2. - - scales (list[int]): The downsampling factors corresponding to the - LR folders you want to process. - Default: []. - input_folder (str): Path to the input folder. - save_folder (str): Path to save folder. - crop_size (int): Crop size. - step (int): Step for overlapped sliding window. - thresh_size (int): Threshold size. Patches whose size is lower - than thresh_size will be dropped. - - Usage: - For each folder, run this script. - For example, if scales = [4], there are two folders to be processed: - train_sharp - train_sharp_bicubic/X4 - After process, each sub_folder should have the same number of - subimages. You can also specify scales by modifying the argument - 'scales'. Remember to modify opt configurations according to your - settings. - """ - - opt = {} - opt['n_thread'] = args.n_thread - opt['compression_level'] = args.compression_level - - # HR images - opt['input_folder'] = osp.join(args.data_root, 'train_sharp') - opt['save_folder'] = osp.join(args.data_root, 'train_sharp_sub') - opt['crop_size'] = args.crop_size - opt['step'] = args.step - opt['thresh_size'] = args.thresh_size - extract_subimages(opt) - - for scale in args.scales: - opt['input_folder'] = osp.join(args.data_root, - f'train_sharp_bicubic/X{scale}') - opt['save_folder'] = osp.join(args.data_root, - f'train_sharp_bicubic/X{scale}_sub') - opt['crop_size'] = args.crop_size // scale - opt['step'] = args.step // scale - opt['thresh_size'] = args.thresh_size // scale - extract_subimages(opt) - - -def parse_args(): - parser = argparse.ArgumentParser( - description='Preprocess REDS datasets', - epilog='You can first download REDS datasets using the script from:' - 'https://gist.github.com/SeungjunNah/b10d369b92840cb8dd2118dd4f41d643') - parser.add_argument('--data-root', type=str, default='data/REDS/train', help='root path for REDS') - parser.add_argument( - '--scales', nargs='*', default=[4], help='scale factor list') - parser.add_argument( - '--crop-size', - nargs='?', - default=480, - help='cropped size for HR images') - parser.add_argument( - '--step', nargs='?', default=240, help='step size for HR images') - parser.add_argument( - '--thresh-size', - nargs='?', - default=0, - help='threshold size for HR images') - parser.add_argument( - '--compression-level', - nargs='?', - default=3, - help='compression level when save png images') - parser.add_argument( - '--n-thread', - nargs='?', - default=20, - help='thread number when using multiprocessing') - - args = parser.parse_args() - return args - - -if __name__ == '__main__': - args = parse_args() - - # extract subimages - args.scales = [int(v) for v in args.scales] - main_extract_subimages(args) - diff --git a/cv/super_resolution/real_basicvsr/pytorch/dist_test.sh b/cv/super_resolution/real_basicvsr/pytorch/dist_test.sh deleted file mode 100644 index 282eef5ff..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/dist_test.sh +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -#!/usr/bin/env bash - -CONFIG=$1 -CHECKPOINT=$2 -GPUS=$3 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/test.py \ - $CONFIG \ - $CHECKPOINT \ - --launcher pytorch \ - ${@:4} diff --git a/cv/super_resolution/real_basicvsr/pytorch/dist_train.sh b/cv/super_resolution/real_basicvsr/pytorch/dist_train.sh deleted file mode 100755 index 87c687cd7..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/dist_train.sh +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -#!/usr/bin/env bash - -CONFIG=$1 -GPUS=$2 -NNODES=${NNODES:-1} -NODE_RANK=${NODE_RANK:-0} -PORT=${PORT:-29500} -MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} - -PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ -python3 -m torch.distributed.launch \ - --nnodes=$NNODES \ - --node_rank=$NODE_RANK \ - --master_addr=$MASTER_ADDR \ - --nproc_per_node=$GPUS \ - --master_port=$PORT \ - $(dirname "$0")/train.py \ - $CONFIG \ - --seed 0 \ - --launcher pytorch ${@:3} diff --git a/cv/super_resolution/real_basicvsr/pytorch/docker/Dockerfile b/cv/super_resolution/real_basicvsr/pytorch/docker/Dockerfile deleted file mode 100755 index 651287043..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/docker/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -ARG PYTORCH="1.6.0" -ARG CUDA="10.1" -ARG CUDA_ALIAS="101" -ARG CUDNN="7" -ARG MMCV="1.3.1" - -FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel - -ENV TORCH_CUDA_ARCH_LIST="6.0 6.1 7.0+PTX" -ENV TORCH_NVCC_FLAGS="-Xfatbin -compress-all" -ENV CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" - -RUN apt-get update && apt-get install -y git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 libgl1-mesa-glx \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Install mmediting -RUN conda clean --all -RUN git clone https://github.com/open-mmlab/mmediting.git /mmediting -WORKDIR /mmediting -ENV FORCE_CUDA="1" -RUN pip install mmcv-full==${MMCV} -f https://download.openmmlab.com/mmcv/dist/cu${CUDA_ALIAS}/torch${PYTORCH}/index.html -RUN pip install -r requirements.txt -RUN pip install --no-cache-dir -e . diff --git a/cv/super_resolution/real_basicvsr/pytorch/docker/README.md b/cv/super_resolution/real_basicvsr/pytorch/docker/README.md deleted file mode 100755 index 851c28b0b..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/docker/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Docker Image - -We provide a [Dockerfile](Dockerfile) to build an image. - -```shell -# build an image with PyTorch 1.6, CUDA 10.1 -docker build -t mmediting docker/ -``` - -Run it with - -```shell -docker run --gpus all --shm-size=8g -it -v {DATA_DIR}:/mmediting/data mmediting -``` - -**Note**: -Versions defined in this [Dockerfile](Dockerfile) is not up-to-date. -If you use this Dockerfile in your project, you probably want to make some updates. -Feel free to submit an issue or PR for the update. diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/__init__.py deleted file mode 100755 index 30b661fb5..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# flake8: noqa -from .arraymisc import * -from .fileio import * -from .image import * -from .utils import * -from .version import * - -# The following modules are not imported to this level, so mmcv may be used -# without PyTorch. -# - runner -# - parallel -# - op diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/arraymisc/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/arraymisc/__init__.py deleted file mode 100755 index 4b4700d61..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/arraymisc/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .quantization import dequantize, quantize - -__all__ = ['quantize', 'dequantize'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/arraymisc/quantization.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/arraymisc/quantization.py deleted file mode 100755 index 8e47a3545..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/arraymisc/quantization.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - - -def quantize(arr, min_val, max_val, levels, dtype=np.int64): - """Quantize an array of (-inf, inf) to [0, levels-1]. - - Args: - arr (ndarray): Input array. - min_val (scalar): Minimum value to be clipped. - max_val (scalar): Maximum value to be clipped. - levels (int): Quantization levels. - dtype (np.type): The type of the quantized array. - - Returns: - tuple: Quantized array. - """ - if not (isinstance(levels, int) and levels > 1): - raise ValueError( - f'levels must be a positive integer, but got {levels}') - if min_val >= max_val: - raise ValueError( - f'min_val ({min_val}) must be smaller than max_val ({max_val})') - - arr = np.clip(arr, min_val, max_val) - min_val - quantized_arr = np.minimum( - np.floor(levels * arr / (max_val - min_val)).astype(dtype), levels - 1) - - return quantized_arr - - -def dequantize(arr, min_val, max_val, levels, dtype=np.float64): - """Dequantize an array. - - Args: - arr (ndarray): Input array. - min_val (scalar): Minimum value to be clipped. - max_val (scalar): Maximum value to be clipped. - levels (int): Quantization levels. - dtype (np.type): The type of the dequantized array. - - Returns: - tuple: Dequantized array. - """ - if not (isinstance(levels, int) and levels > 1): - raise ValueError( - f'levels must be a positive integer, but got {levels}') - if min_val >= max_val: - raise ValueError( - f'min_val ({min_val}) must be smaller than max_val ({max_val})') - - dequantized_arr = (arr + 0.5).astype(dtype) * (max_val - - min_val) / levels + min_val - - return dequantized_arr diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/__init__.py deleted file mode 100755 index 7246c8974..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .alexnet import AlexNet -# yapf: disable -from .bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS, - ContextBlock, Conv2d, Conv3d, ConvAWS2d, ConvModule, - ConvTranspose2d, ConvTranspose3d, ConvWS2d, - DepthwiseSeparableConvModule, GeneralizedAttention, - HSigmoid, HSwish, Linear, MaxPool2d, MaxPool3d, - NonLocal1d, NonLocal2d, NonLocal3d, Scale, Swish, - build_activation_layer, build_conv_layer, - build_norm_layer, build_padding_layer, build_plugin_layer, - build_upsample_layer, conv_ws_2d, is_norm) -from .builder import MODELS, build_model_from_cfg -# yapf: enable -from .resnet import ResNet, make_res_layer -from .utils import (INITIALIZERS, Caffe2XavierInit, ConstantInit, KaimingInit, - NormalInit, PretrainedInit, TruncNormalInit, UniformInit, - XavierInit, bias_init_with_prob, caffe2_xavier_init, - constant_init, fuse_conv_bn, get_model_complexity_info, - initialize, kaiming_init, normal_init, trunc_normal_init, - uniform_init, xavier_init) -from .vgg import VGG, make_vgg_layer - -__all__ = [ - 'AlexNet', 'VGG', 'make_vgg_layer', 'ResNet', 'make_res_layer', - 'constant_init', 'xavier_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'kaiming_init', 'caffe2_xavier_init', - 'bias_init_with_prob', 'ConvModule', 'build_activation_layer', - 'build_conv_layer', 'build_norm_layer', 'build_padding_layer', - 'build_upsample_layer', 'build_plugin_layer', 'is_norm', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'HSigmoid', 'Swish', 'HSwish', - 'GeneralizedAttention', 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', - 'PADDING_LAYERS', 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', - 'get_model_complexity_info', 'conv_ws_2d', 'ConvAWS2d', 'ConvWS2d', - 'fuse_conv_bn', 'DepthwiseSeparableConvModule', 'Linear', 'Conv2d', - 'ConvTranspose2d', 'MaxPool2d', 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', - 'initialize', 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'MODELS', 'build_model_from_cfg' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/alexnet.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/alexnet.py deleted file mode 100755 index 89e36b8c7..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/alexnet.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn - - -class AlexNet(nn.Module): - """AlexNet backbone. - - Args: - num_classes (int): number of classes for classification. - """ - - def __init__(self, num_classes=-1): - super(AlexNet, self).__init__() - self.num_classes = num_classes - self.features = nn.Sequential( - nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - nn.Conv2d(64, 192, kernel_size=5, padding=2), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - nn.Conv2d(192, 384, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.Conv2d(384, 256, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.Conv2d(256, 256, kernel_size=3, padding=1), - nn.ReLU(inplace=True), - nn.MaxPool2d(kernel_size=3, stride=2), - ) - if self.num_classes > 0: - self.classifier = nn.Sequential( - nn.Dropout(), - nn.Linear(256 * 6 * 6, 4096), - nn.ReLU(inplace=True), - nn.Dropout(), - nn.Linear(4096, 4096), - nn.ReLU(inplace=True), - nn.Linear(4096, num_classes), - ) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - # use default initializer - pass - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - - x = self.features(x) - if self.num_classes > 0: - x = x.view(x.size(0), 256 * 6 * 6) - x = self.classifier(x) - - return x diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/__init__.py deleted file mode 100755 index 0f33124ed..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .activation import build_activation_layer -from .context_block import ContextBlock -from .conv import build_conv_layer -from .conv2d_adaptive_padding import Conv2dAdaptivePadding -from .conv_module import ConvModule -from .conv_ws import ConvAWS2d, ConvWS2d, conv_ws_2d -from .depthwise_separable_conv_module import DepthwiseSeparableConvModule -from .drop import Dropout, DropPath -from .generalized_attention import GeneralizedAttention -from .hsigmoid import HSigmoid -from .hswish import HSwish -from .non_local import NonLocal1d, NonLocal2d, NonLocal3d -from .norm import build_norm_layer, is_norm -from .padding import build_padding_layer -from .plugin import build_plugin_layer -from .registry import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, - PADDING_LAYERS, PLUGIN_LAYERS, UPSAMPLE_LAYERS) -from .scale import Scale -from .swish import Swish -from .upsample import build_upsample_layer -from .wrappers import (Conv2d, Conv3d, ConvTranspose2d, ConvTranspose3d, - Linear, MaxPool2d, MaxPool3d) - -__all__ = [ - 'ConvModule', 'build_activation_layer', 'build_conv_layer', - 'build_norm_layer', 'build_padding_layer', 'build_upsample_layer', - 'build_plugin_layer', 'is_norm', 'HSigmoid', 'HSwish', 'NonLocal1d', - 'NonLocal2d', 'NonLocal3d', 'ContextBlock', 'GeneralizedAttention', - 'ACTIVATION_LAYERS', 'CONV_LAYERS', 'NORM_LAYERS', 'PADDING_LAYERS', - 'UPSAMPLE_LAYERS', 'PLUGIN_LAYERS', 'Scale', 'ConvAWS2d', 'ConvWS2d', - 'conv_ws_2d', 'DepthwiseSeparableConvModule', 'Swish', 'Linear', - 'Conv2dAdaptivePadding', 'Conv2d', 'ConvTranspose2d', 'MaxPool2d', - 'ConvTranspose3d', 'MaxPool3d', 'Conv3d', 'Dropout', 'DropPath' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/activation.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/activation.py deleted file mode 100755 index 79f198838..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/activation.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from mmcv.utils import TORCH_VERSION, build_from_cfg, digit_version -from .registry import ACTIVATION_LAYERS - -for module in [ - nn.ReLU, nn.LeakyReLU, nn.PReLU, nn.RReLU, nn.ReLU6, nn.ELU, - nn.Sigmoid, nn.Tanh -]: - ACTIVATION_LAYERS.register_module(module=module) - - -@ACTIVATION_LAYERS.register_module(name='Clip') -@ACTIVATION_LAYERS.register_module() -class Clamp(nn.Module): - """Clamp activation layer. - - This activation function is to clamp the feature map value within - :math:`[min, max]`. More details can be found in ``torch.clamp()``. - - Args: - min (Number | optional): Lower-bound of the range to be clamped to. - Default to -1. - max (Number | optional): Upper-bound of the range to be clamped to. - Default to 1. - """ - - def __init__(self, min=-1., max=1.): - super(Clamp, self).__init__() - self.min = min - self.max = max - - def forward(self, x): - """Forward function. - - Args: - x (torch.Tensor): The input tensor. - - Returns: - torch.Tensor: Clamped tensor. - """ - return torch.clamp(x, min=self.min, max=self.max) - - -class GELU(nn.Module): - r"""Applies the Gaussian Error Linear Units function: - - .. math:: - \text{GELU}(x) = x * \Phi(x) - where :math:`\Phi(x)` is the Cumulative Distribution Function for - Gaussian Distribution. - - Shape: - - Input: :math:`(N, *)` where `*` means, any number of additional - dimensions - - Output: :math:`(N, *)`, same shape as the input - - .. image:: scripts/activation_images/GELU.png - - Examples:: - - >>> m = nn.GELU() - >>> input = torch.randn(2) - >>> output = m(input) - """ - - def forward(self, input): - return F.gelu(input) - - -if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.4')): - ACTIVATION_LAYERS.register_module(module=GELU) -else: - ACTIVATION_LAYERS.register_module(module=nn.GELU) - - -def build_activation_layer(cfg): - """Build activation layer. - - Args: - cfg (dict): The activation layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an activation layer. - - Returns: - nn.Module: Created activation layer. - """ - return build_from_cfg(cfg, ACTIVATION_LAYERS) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/context_block.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/context_block.py deleted file mode 100755 index d60fdb904..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/context_block.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch import nn - -from ..utils import constant_init, kaiming_init -from .registry import PLUGIN_LAYERS - - -def last_zero_init(m): - if isinstance(m, nn.Sequential): - constant_init(m[-1], val=0) - else: - constant_init(m, val=0) - - -@PLUGIN_LAYERS.register_module() -class ContextBlock(nn.Module): - """ContextBlock module in GCNet. - - See 'GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond' - (https://arxiv.org/abs/1904.11492) for details. - - Args: - in_channels (int): Channels of the input feature map. - ratio (float): Ratio of channels of transform bottleneck - pooling_type (str): Pooling method for context modeling. - Options are 'att' and 'avg', stand for attention pooling and - average pooling respectively. Default: 'att'. - fusion_types (Sequence[str]): Fusion method for feature fusion, - Options are 'channels_add', 'channel_mul', stand for channelwise - addition and multiplication respectively. Default: ('channel_add',) - """ - - _abbr_ = 'context_block' - - def __init__(self, - in_channels, - ratio, - pooling_type='att', - fusion_types=('channel_add', )): - super(ContextBlock, self).__init__() - assert pooling_type in ['avg', 'att'] - assert isinstance(fusion_types, (list, tuple)) - valid_fusion_types = ['channel_add', 'channel_mul'] - assert all([f in valid_fusion_types for f in fusion_types]) - assert len(fusion_types) > 0, 'at least one fusion should be used' - self.in_channels = in_channels - self.ratio = ratio - self.planes = int(in_channels * ratio) - self.pooling_type = pooling_type - self.fusion_types = fusion_types - if pooling_type == 'att': - self.conv_mask = nn.Conv2d(in_channels, 1, kernel_size=1) - self.softmax = nn.Softmax(dim=2) - else: - self.avg_pool = nn.AdaptiveAvgPool2d(1) - if 'channel_add' in fusion_types: - self.channel_add_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_add_conv = None - if 'channel_mul' in fusion_types: - self.channel_mul_conv = nn.Sequential( - nn.Conv2d(self.in_channels, self.planes, kernel_size=1), - nn.LayerNorm([self.planes, 1, 1]), - nn.ReLU(inplace=True), # yapf: disable - nn.Conv2d(self.planes, self.in_channels, kernel_size=1)) - else: - self.channel_mul_conv = None - self.reset_parameters() - - def reset_parameters(self): - if self.pooling_type == 'att': - kaiming_init(self.conv_mask, mode='fan_in') - self.conv_mask.inited = True - - if self.channel_add_conv is not None: - last_zero_init(self.channel_add_conv) - if self.channel_mul_conv is not None: - last_zero_init(self.channel_mul_conv) - - def spatial_pool(self, x): - batch, channel, height, width = x.size() - if self.pooling_type == 'att': - input_x = x - # [N, C, H * W] - input_x = input_x.view(batch, channel, height * width) - # [N, 1, C, H * W] - input_x = input_x.unsqueeze(1) - # [N, 1, H, W] - context_mask = self.conv_mask(x) - # [N, 1, H * W] - context_mask = context_mask.view(batch, 1, height * width) - # [N, 1, H * W] - context_mask = self.softmax(context_mask) - # [N, 1, H * W, 1] - context_mask = context_mask.unsqueeze(-1) - # [N, 1, C, 1] - context = torch.matmul(input_x, context_mask) - # [N, C, 1, 1] - context = context.view(batch, channel, 1, 1) - else: - # [N, C, 1, 1] - context = self.avg_pool(x) - - return context - - def forward(self, x): - # [N, C, 1, 1] - context = self.spatial_pool(x) - - out = x - if self.channel_mul_conv is not None: - # [N, C, 1, 1] - channel_mul_term = torch.sigmoid(self.channel_mul_conv(context)) - out = out * channel_mul_term - if self.channel_add_conv is not None: - # [N, C, 1, 1] - channel_add_term = self.channel_add_conv(context) - out = out + channel_add_term - - return out diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv.py deleted file mode 100755 index cf5449199..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch import nn - -from .registry import CONV_LAYERS - -CONV_LAYERS.register_module('Conv1d', module=nn.Conv1d) -CONV_LAYERS.register_module('Conv2d', module=nn.Conv2d) -CONV_LAYERS.register_module('Conv3d', module=nn.Conv3d) -CONV_LAYERS.register_module('Conv', module=nn.Conv2d) - - -def build_conv_layer(cfg, *args, **kwargs): - """Build convolution layer. - - Args: - cfg (None or dict): The conv layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate an conv layer. - args (argument list): Arguments passed to the `__init__` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the `__init__` - method of the corresponding conv layer. - - Returns: - nn.Module: Created conv layer. - """ - if cfg is None: - cfg_ = dict(type='Conv2d') - else: - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in CONV_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - else: - conv_layer = CONV_LAYERS.get(layer_type) - - layer = conv_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py deleted file mode 100755 index b45e758ac..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv2d_adaptive_padding.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -from torch import nn -from torch.nn import functional as F - -from .registry import CONV_LAYERS - - -@CONV_LAYERS.register_module() -class Conv2dAdaptivePadding(nn.Conv2d): - """Implementation of 2D convolution in tensorflow with `padding` as "same", - which applies padding to input (if needed) so that input image gets fully - covered by filter and stride you specified. For stride 1, this will ensure - that output image size is same as input. For stride of 2, output dimensions - will be half, for example. - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the convolving kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If ``True``, adds a learnable bias to the - output. Default: ``True`` - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__(in_channels, out_channels, kernel_size, stride, 0, - dilation, groups, bias) - - def forward(self, x): - img_h, img_w = x.size()[-2:] - kernel_h, kernel_w = self.weight.size()[-2:] - stride_h, stride_w = self.stride - output_h = math.ceil(img_h / stride_h) - output_w = math.ceil(img_w / stride_w) - pad_h = ( - max((output_h - 1) * self.stride[0] + - (kernel_h - 1) * self.dilation[0] + 1 - img_h, 0)) - pad_w = ( - max((output_w - 1) * self.stride[1] + - (kernel_w - 1) * self.dilation[1] + 1 - img_w, 0)) - if pad_h > 0 or pad_w > 0: - x = F.pad(x, [ - pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2 - ]) - return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv_module.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv_module.py deleted file mode 100755 index 4f19f1d0c..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv_module.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch.nn as nn - -from mmcv.utils import _BatchNorm, _InstanceNorm -from ..utils import constant_init, kaiming_init -from .activation import build_activation_layer -from .conv import build_conv_layer -from .norm import build_norm_layer -from .padding import build_padding_layer -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class ConvModule(nn.Module): - """A conv block that bundles conv/norm/activation layers. - - This block simplifies the usage of convolution layers, which are commonly - used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU). - It is based upon three build methods: `build_conv_layer()`, - `build_norm_layer()` and `build_activation_layer()`. - - Besides, we add some additional features in this module. - 1. Automatically set `bias` of the conv layer. - 2. Spectral norm is supported. - 3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only - supports zero and circular padding, and we add "reflect" padding mode. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. - groups (int): Number of blocked connections from input channels to - output channels. Same as that in ``nn._ConvNd``. - bias (bool | str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if `norm_cfg` is None, otherwise - False. Default: "auto". - conv_cfg (dict): Config dict for convolution layer. Default: None, - which means using conv2d. - norm_cfg (dict): Config dict for normalization layer. Default: None. - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - inplace (bool): Whether to use inplace mode for activation. - Default: True. - with_spectral_norm (bool): Whether use spectral norm in conv module. - Default: False. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in PyTorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Common examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - Default: ('conv', 'norm', 'act'). - """ - - _abbr_ = 'conv_block' - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias='auto', - conv_cfg=None, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - padding_mode='zeros', - order=('conv', 'norm', 'act')): - super(ConvModule, self).__init__() - assert conv_cfg is None or isinstance(conv_cfg, dict) - assert norm_cfg is None or isinstance(norm_cfg, dict) - assert act_cfg is None or isinstance(act_cfg, dict) - official_padding_mode = ['zeros', 'circular'] - self.conv_cfg = conv_cfg - self.norm_cfg = norm_cfg - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.with_explicit_padding = padding_mode not in official_padding_mode - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 3 - assert set(order) == set(['conv', 'norm', 'act']) - - self.with_norm = norm_cfg is not None - self.with_activation = act_cfg is not None - # if the conv layer is before a norm layer, bias is unnecessary. - if bias == 'auto': - bias = not self.with_norm - self.with_bias = bias - - if self.with_explicit_padding: - pad_cfg = dict(type=padding_mode) - self.padding_layer = build_padding_layer(pad_cfg, padding) - - # reset padding to 0 for conv module - conv_padding = 0 if self.with_explicit_padding else padding - # build convolution layer - self.conv = build_conv_layer( - conv_cfg, - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=conv_padding, - dilation=dilation, - groups=groups, - bias=bias) - # export the attributes of self.conv to a higher level for convenience - self.in_channels = self.conv.in_channels - self.out_channels = self.conv.out_channels - self.kernel_size = self.conv.kernel_size - self.stride = self.conv.stride - self.padding = padding - self.dilation = self.conv.dilation - self.transposed = self.conv.transposed - self.output_padding = self.conv.output_padding - self.groups = self.conv.groups - - if self.with_spectral_norm: - self.conv = nn.utils.spectral_norm(self.conv) - - # build normalization layers - if self.with_norm: - # norm layer is after conv layer - if order.index('norm') > order.index('conv'): - norm_channels = out_channels - else: - norm_channels = in_channels - self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels) - self.add_module(self.norm_name, norm) - if self.with_bias: - if isinstance(norm, (_BatchNorm, _InstanceNorm)): - warnings.warn( - 'Unnecessary conv bias before batch/instance norm') - else: - self.norm_name = None - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - # nn.Tanh has no 'inplace' argument - if act_cfg_['type'] not in [ - 'Tanh', 'PReLU', 'Sigmoid', 'HSigmoid', 'Swish' - ]: - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - @property - def norm(self): - if self.norm_name: - return getattr(self, self.norm_name) - else: - return None - - def init_weights(self): - # 1. It is mainly for customized conv layers with their own - # initialization manners by calling their own ``init_weights()``, - # and we do not want ConvModule to override the initialization. - # 2. For customized conv layers without their own initialization - # manners (that is, they don't have their own ``init_weights()``) - # and PyTorch's conv layers, they will be initialized by - # this method with default ``kaiming_init``. - # Note: For PyTorch's conv layers, they will be overwritten by our - # initialization implementation using default ``kaiming_init``. - if not hasattr(self.conv, 'init_weights'): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - kaiming_init(self.conv, a=a, nonlinearity=nonlinearity) - if self.with_norm: - constant_init(self.norm, 1, bias=0) - - def forward(self, x, activate=True, norm=True): - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - x = self.conv(x) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv_ws.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv_ws.py deleted file mode 100755 index a3941e278..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/conv_ws.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from .registry import CONV_LAYERS - - -def conv_ws_2d(input, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - eps=1e-5): - c_in = weight.size(0) - weight_flat = weight.view(c_in, -1) - mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1) - std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1) - weight = (weight - mean) / (std + eps) - return F.conv2d(input, weight, bias, stride, padding, dilation, groups) - - -@CONV_LAYERS.register_module('ConvWS') -class ConvWS2d(nn.Conv2d): - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True, - eps=1e-5): - super(ConvWS2d, self).__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.eps = eps - - def forward(self, x): - return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding, - self.dilation, self.groups, self.eps) - - -@CONV_LAYERS.register_module(name='ConvAWS') -class ConvAWS2d(nn.Conv2d): - """AWS (Adaptive Weight Standardization) - - This is a variant of Weight Standardization - (https://arxiv.org/pdf/1903.10520.pdf) - It is used in DetectoRS to avoid NaN - (https://arxiv.org/pdf/2006.02334.pdf) - - Args: - in_channels (int): Number of channels in the input image - out_channels (int): Number of channels produced by the convolution - kernel_size (int or tuple): Size of the conv kernel - stride (int or tuple, optional): Stride of the convolution. Default: 1 - padding (int or tuple, optional): Zero-padding added to both sides of - the input. Default: 0 - dilation (int or tuple, optional): Spacing between kernel elements. - Default: 1 - groups (int, optional): Number of blocked connections from input - channels to output channels. Default: 1 - bias (bool, optional): If set True, adds a learnable bias to the - output. Default: True - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - bias=True): - super().__init__( - in_channels, - out_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - self.register_buffer('weight_gamma', - torch.ones(self.out_channels, 1, 1, 1)) - self.register_buffer('weight_beta', - torch.zeros(self.out_channels, 1, 1, 1)) - - def _get_weight(self, weight): - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - weight = (weight - mean) / std - weight = self.weight_gamma * weight + self.weight_beta - return weight - - def forward(self, x): - weight = self._get_weight(self.weight) - return F.conv2d(x, weight, self.bias, self.stride, self.padding, - self.dilation, self.groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - """Override default load function. - - AWS overrides the function _load_from_state_dict to recover - weight_gamma and weight_beta if they are missing. If weight_gamma and - weight_beta are found in the checkpoint, this function will return - after super()._load_from_state_dict. Otherwise, it will compute the - mean and std of the pretrained weights and store them in weight_beta - and weight_gamma. - """ - - self.weight_gamma.data.fill_(-1) - local_missing_keys = [] - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, local_missing_keys, - unexpected_keys, error_msgs) - if self.weight_gamma.data.mean() > 0: - for k in local_missing_keys: - missing_keys.append(k) - return - weight = self.weight.data - weight_flat = weight.view(weight.size(0), -1) - mean = weight_flat.mean(dim=1).view(-1, 1, 1, 1) - std = torch.sqrt(weight_flat.var(dim=1) + 1e-5).view(-1, 1, 1, 1) - self.weight_beta.data.copy_(mean) - self.weight_gamma.data.copy_(std) - missing_gamma_beta = [ - k for k in local_missing_keys - if k.endswith('weight_gamma') or k.endswith('weight_beta') - ] - for k in missing_gamma_beta: - local_missing_keys.remove(k) - for k in local_missing_keys: - missing_keys.append(k) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py deleted file mode 100755 index 722d5d8d7..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/depthwise_separable_conv_module.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .conv_module import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if `norm_cfg` and `act_cfg` are specified. - - Args: - in_channels (int): Number of channels in the input feature map. - Same as that in ``nn._ConvNd``. - out_channels (int): Number of channels produced by the convolution. - Same as that in ``nn._ConvNd``. - kernel_size (int | tuple[int]): Size of the convolving kernel. - Same as that in ``nn._ConvNd``. - stride (int | tuple[int]): Stride of the convolution. - Same as that in ``nn._ConvNd``. Default: 1. - padding (int | tuple[int]): Zero-padding added to both sides of - the input. Same as that in ``nn._ConvNd``. Default: 0. - dilation (int | tuple[int]): Spacing between kernel elements. - Same as that in ``nn._ConvNd``. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as `act_cfg`. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super(DepthwiseSeparableConvModule, self).__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/drop.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/drop.py deleted file mode 100755 index b0a026654..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/drop.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from mmcv import build_from_cfg -from .registry import DROPOUT_LAYERS - - -def drop_path(x, drop_prob=0., training=False): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - """ - if drop_prob == 0. or not training: - return x - keep_prob = 1 - drop_prob - # handle tensors with different dimensions, not just 4D tensors. - shape = (x.shape[0], ) + (1, ) * (x.ndim - 1) - random_tensor = keep_prob + torch.rand( - shape, dtype=x.dtype, device=x.device) - output = x.div(keep_prob) * random_tensor.floor() - return output - - -@DROPOUT_LAYERS.register_module() -class DropPath(nn.Module): - """Drop paths (Stochastic Depth) per sample (when applied in main path of - residual blocks). - - We follow the implementation - https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 - - Args: - drop_prob (float): Probability of the path to be zeroed. Default: 0.1 - """ - - def __init__(self, drop_prob=0.1): - super(DropPath, self).__init__() - self.drop_prob = drop_prob - - def forward(self, x): - return drop_path(x, self.drop_prob, self.training) - - -@DROPOUT_LAYERS.register_module() -class Dropout(nn.Dropout): - """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of - ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with - ``DropPath`` - - Args: - drop_prob (float): Probability of the elements to be - zeroed. Default: 0.5. - inplace (bool): Do the operation inplace or not. Default: False. - """ - - def __init__(self, drop_prob=0.5, inplace=False): - super().__init__(p=drop_prob, inplace=inplace) - - -def build_dropout(cfg, default_args=None): - """Builder for drop out layers.""" - return build_from_cfg(cfg, DROPOUT_LAYERS, default_args) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/generalized_attention.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/generalized_attention.py deleted file mode 100755 index 988d9adf2..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/generalized_attention.py +++ /dev/null @@ -1,412 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import kaiming_init -from .registry import PLUGIN_LAYERS - - -@PLUGIN_LAYERS.register_module() -class GeneralizedAttention(nn.Module): - """GeneralizedAttention module. - - See 'An Empirical Study of Spatial Attention Mechanisms in Deep Networks' - (https://arxiv.org/abs/1711.07971) for details. - - Args: - in_channels (int): Channels of the input feature map. - spatial_range (int): The spatial range. -1 indicates no spatial range - constraint. Default: -1. - num_heads (int): The head number of empirical_attention module. - Default: 9. - position_embedding_dim (int): The position embedding dimension. - Default: -1. - position_magnitude (int): A multiplier acting on coord difference. - Default: 1. - kv_stride (int): The feature stride acting on key/value feature map. - Default: 2. - q_stride (int): The feature stride acting on query feature map. - Default: 1. - attention_type (str): A binary indicator string for indicating which - items in generalized empirical_attention module are used. - Default: '1111'. - - - '1000' indicates 'query and key content' (appr - appr) item, - - '0100' indicates 'query content and relative position' - (appr - position) item, - - '0010' indicates 'key content only' (bias - appr) item, - - '0001' indicates 'relative position only' (bias - position) item. - """ - - _abbr_ = 'gen_attention_block' - - def __init__(self, - in_channels, - spatial_range=-1, - num_heads=9, - position_embedding_dim=-1, - position_magnitude=1, - kv_stride=2, - q_stride=1, - attention_type='1111'): - - super(GeneralizedAttention, self).__init__() - - # hard range means local range for non-local operation - self.position_embedding_dim = ( - position_embedding_dim - if position_embedding_dim > 0 else in_channels) - - self.position_magnitude = position_magnitude - self.num_heads = num_heads - self.in_channels = in_channels - self.spatial_range = spatial_range - self.kv_stride = kv_stride - self.q_stride = q_stride - self.attention_type = [bool(int(_)) for _ in attention_type] - self.qk_embed_dim = in_channels // num_heads - out_c = self.qk_embed_dim * num_heads - - if self.attention_type[0] or self.attention_type[1]: - self.query_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.query_conv.kaiming_init = True - - if self.attention_type[0] or self.attention_type[2]: - self.key_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_c, - kernel_size=1, - bias=False) - self.key_conv.kaiming_init = True - - self.v_dim = in_channels // num_heads - self.value_conv = nn.Conv2d( - in_channels=in_channels, - out_channels=self.v_dim * num_heads, - kernel_size=1, - bias=False) - self.value_conv.kaiming_init = True - - if self.attention_type[1] or self.attention_type[3]: - self.appr_geom_fc_x = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_x.kaiming_init = True - - self.appr_geom_fc_y = nn.Linear( - self.position_embedding_dim // 2, out_c, bias=False) - self.appr_geom_fc_y.kaiming_init = True - - if self.attention_type[2]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - appr_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.appr_bias = nn.Parameter(appr_bias_value) - - if self.attention_type[3]: - stdv = 1.0 / math.sqrt(self.qk_embed_dim * 2) - geom_bias_value = -2 * stdv * torch.rand(out_c) + stdv - self.geom_bias = nn.Parameter(geom_bias_value) - - self.proj_conv = nn.Conv2d( - in_channels=self.v_dim * num_heads, - out_channels=in_channels, - kernel_size=1, - bias=True) - self.proj_conv.kaiming_init = True - self.gamma = nn.Parameter(torch.zeros(1)) - - if self.spatial_range >= 0: - # only works when non local is after 3*3 conv - if in_channels == 256: - max_len = 84 - elif in_channels == 512: - max_len = 42 - - max_len_kv = int((max_len - 1.0) / self.kv_stride + 1) - local_constraint_map = np.ones( - (max_len, max_len, max_len_kv, max_len_kv), dtype=np.int) - for iy in range(max_len): - for ix in range(max_len): - local_constraint_map[ - iy, ix, - max((iy - self.spatial_range) // - self.kv_stride, 0):min((iy + self.spatial_range + - 1) // self.kv_stride + - 1, max_len), - max((ix - self.spatial_range) // - self.kv_stride, 0):min((ix + self.spatial_range + - 1) // self.kv_stride + - 1, max_len)] = 0 - - self.local_constraint_map = nn.Parameter( - torch.from_numpy(local_constraint_map).byte(), - requires_grad=False) - - if self.q_stride > 1: - self.q_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.q_stride) - else: - self.q_downsample = None - - if self.kv_stride > 1: - self.kv_downsample = nn.AvgPool2d( - kernel_size=1, stride=self.kv_stride) - else: - self.kv_downsample = None - - self.init_weights() - - def get_position_embedding(self, - h, - w, - h_kv, - w_kv, - q_stride, - kv_stride, - device, - dtype, - feat_dim, - wave_length=1000): - # the default type of Tensor is float32, leading to type mismatch - # in fp16 mode. Cast it to support fp16 mode. - h_idxs = torch.linspace(0, h - 1, h).to(device=device, dtype=dtype) - h_idxs = h_idxs.view((h, 1)) * q_stride - - w_idxs = torch.linspace(0, w - 1, w).to(device=device, dtype=dtype) - w_idxs = w_idxs.view((w, 1)) * q_stride - - h_kv_idxs = torch.linspace(0, h_kv - 1, h_kv).to( - device=device, dtype=dtype) - h_kv_idxs = h_kv_idxs.view((h_kv, 1)) * kv_stride - - w_kv_idxs = torch.linspace(0, w_kv - 1, w_kv).to( - device=device, dtype=dtype) - w_kv_idxs = w_kv_idxs.view((w_kv, 1)) * kv_stride - - # (h, h_kv, 1) - h_diff = h_idxs.unsqueeze(1) - h_kv_idxs.unsqueeze(0) - h_diff *= self.position_magnitude - - # (w, w_kv, 1) - w_diff = w_idxs.unsqueeze(1) - w_kv_idxs.unsqueeze(0) - w_diff *= self.position_magnitude - - feat_range = torch.arange(0, feat_dim / 4).to( - device=device, dtype=dtype) - - dim_mat = torch.Tensor([wave_length]).to(device=device, dtype=dtype) - dim_mat = dim_mat**((4. / feat_dim) * feat_range) - dim_mat = dim_mat.view((1, 1, -1)) - - embedding_x = torch.cat( - ((w_diff / dim_mat).sin(), (w_diff / dim_mat).cos()), dim=2) - - embedding_y = torch.cat( - ((h_diff / dim_mat).sin(), (h_diff / dim_mat).cos()), dim=2) - - return embedding_x, embedding_y - - def forward(self, x_input): - num_heads = self.num_heads - - # use empirical_attention - if self.q_downsample is not None: - x_q = self.q_downsample(x_input) - else: - x_q = x_input - n, _, h, w = x_q.shape - - if self.kv_downsample is not None: - x_kv = self.kv_downsample(x_input) - else: - x_kv = x_input - _, _, h_kv, w_kv = x_kv.shape - - if self.attention_type[0] or self.attention_type[1]: - proj_query = self.query_conv(x_q).view( - (n, num_heads, self.qk_embed_dim, h * w)) - proj_query = proj_query.permute(0, 1, 3, 2) - - if self.attention_type[0] or self.attention_type[2]: - proj_key = self.key_conv(x_kv).view( - (n, num_heads, self.qk_embed_dim, h_kv * w_kv)) - - if self.attention_type[1] or self.attention_type[3]: - position_embed_x, position_embed_y = self.get_position_embedding( - h, w, h_kv, w_kv, self.q_stride, self.kv_stride, - x_input.device, x_input.dtype, self.position_embedding_dim) - # (n, num_heads, w, w_kv, dim) - position_feat_x = self.appr_geom_fc_x(position_embed_x).\ - view(1, w, w_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - # (n, num_heads, h, h_kv, dim) - position_feat_y = self.appr_geom_fc_y(position_embed_y).\ - view(1, h, h_kv, num_heads, self.qk_embed_dim).\ - permute(0, 3, 1, 2, 4).\ - repeat(n, 1, 1, 1, 1) - - position_feat_x /= math.sqrt(2) - position_feat_y /= math.sqrt(2) - - # accelerate for saliency only - if (np.sum(self.attention_type) == 1) and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy = torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, h_kv * w_kv) - - h = 1 - w = 1 - else: - # (n, num_heads, h*w, h_kv*w_kv), query before key, 540mb for - if not self.attention_type[0]: - energy = torch.zeros( - n, - num_heads, - h, - w, - h_kv, - w_kv, - dtype=x_input.dtype, - device=x_input.device) - - # attention_type[0]: appr - appr - # attention_type[1]: appr - position - # attention_type[2]: bias - appr - # attention_type[3]: bias - position - if self.attention_type[0] or self.attention_type[2]: - if self.attention_type[0] and self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - energy = torch.matmul(proj_query + appr_bias, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[0]: - energy = torch.matmul(proj_query, proj_key).\ - view(n, num_heads, h, w, h_kv, w_kv) - - elif self.attention_type[2]: - appr_bias = self.appr_bias.\ - view(1, num_heads, 1, self.qk_embed_dim).\ - repeat(n, 1, 1, 1) - - energy += torch.matmul(appr_bias, proj_key).\ - view(n, num_heads, 1, 1, h_kv, w_kv) - - if self.attention_type[1] or self.attention_type[3]: - if self.attention_type[1] and self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, 1, self.qk_embed_dim) - - proj_query_reshape = (proj_query + geom_bias).\ - view(n, num_heads, h, w, self.qk_embed_dim) - - energy_x = torch.matmul( - proj_query_reshape.permute(0, 1, 3, 2, 4), - position_feat_x.permute(0, 1, 2, 4, 3)) - energy_x = energy_x.\ - permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul( - proj_query_reshape, - position_feat_y.permute(0, 1, 2, 4, 3)) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[1]: - proj_query_reshape = proj_query.\ - view(n, num_heads, h, w, self.qk_embed_dim) - proj_query_reshape = proj_query_reshape.\ - permute(0, 1, 3, 2, 4) - position_feat_x_reshape = position_feat_x.\ - permute(0, 1, 2, 4, 3) - position_feat_y_reshape = position_feat_y.\ - permute(0, 1, 2, 4, 3) - - energy_x = torch.matmul(proj_query_reshape, - position_feat_x_reshape) - energy_x = energy_x.permute(0, 1, 3, 2, 4).unsqueeze(4) - - energy_y = torch.matmul(proj_query_reshape, - position_feat_y_reshape) - energy_y = energy_y.unsqueeze(5) - - energy += energy_x + energy_y - - elif self.attention_type[3]: - geom_bias = self.geom_bias.\ - view(1, num_heads, self.qk_embed_dim, 1).\ - repeat(n, 1, 1, 1) - - position_feat_x_reshape = position_feat_x.\ - view(n, num_heads, w*w_kv, self.qk_embed_dim) - - position_feat_y_reshape = position_feat_y.\ - view(n, num_heads, h * h_kv, self.qk_embed_dim) - - energy_x = torch.matmul(position_feat_x_reshape, geom_bias) - energy_x = energy_x.view(n, num_heads, 1, w, 1, w_kv) - - energy_y = torch.matmul(position_feat_y_reshape, geom_bias) - energy_y = energy_y.view(n, num_heads, h, 1, h_kv, 1) - - energy += energy_x + energy_y - - energy = energy.view(n, num_heads, h * w, h_kv * w_kv) - - if self.spatial_range >= 0: - cur_local_constraint_map = \ - self.local_constraint_map[:h, :w, :h_kv, :w_kv].\ - contiguous().\ - view(1, 1, h*w, h_kv*w_kv) - - energy = energy.masked_fill_(cur_local_constraint_map, - float('-inf')) - - attention = F.softmax(energy, 3) - - proj_value = self.value_conv(x_kv) - proj_value_reshape = proj_value.\ - view((n, num_heads, self.v_dim, h_kv * w_kv)).\ - permute(0, 1, 3, 2) - - out = torch.matmul(attention, proj_value_reshape).\ - permute(0, 1, 3, 2).\ - contiguous().\ - view(n, self.v_dim * self.num_heads, h, w) - - out = self.proj_conv(out) - - # output is downsampled, upsample back to input size - if self.q_downsample is not None: - out = F.interpolate( - out, - size=x_input.shape[2:], - mode='bilinear', - align_corners=False) - - out = self.gamma * out + x_input - return out - - def init_weights(self): - for m in self.modules(): - if hasattr(m, 'kaiming_init') and m.kaiming_init: - kaiming_init( - m, - mode='fan_in', - nonlinearity='leaky_relu', - bias=0, - distribution='uniform', - a=1) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/hsigmoid.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/hsigmoid.py deleted file mode 100755 index 30b1a3d65..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/hsigmoid.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSigmoid(nn.Module): - """Hard Sigmoid Module. Apply the hard sigmoid function: - Hsigmoid(x) = min(max((x + bias) / divisor, min_value), max_value) - Default: Hsigmoid(x) = min(max((x + 1) / 2, 0), 1) - - Args: - bias (float): Bias of the input feature map. Default: 1.0. - divisor (float): Divisor of the input feature map. Default: 2.0. - min_value (float): Lower bound value. Default: 0.0. - max_value (float): Upper bound value. Default: 1.0. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, bias=1.0, divisor=2.0, min_value=0.0, max_value=1.0): - super(HSigmoid, self).__init__() - self.bias = bias - self.divisor = divisor - assert self.divisor != 0 - self.min_value = min_value - self.max_value = max_value - - def forward(self, x): - x = (x + self.bias) / self.divisor - - return x.clamp_(self.min_value, self.max_value) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/hswish.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/hswish.py deleted file mode 100755 index 7e0c090ff..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/hswish.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class HSwish(nn.Module): - """Hard Swish Module. - - This module applies the hard swish function: - - .. math:: - Hswish(x) = x * ReLU6(x + 3) / 6 - - Args: - inplace (bool): can optionally do the operation in-place. - Default: False. - - Returns: - Tensor: The output tensor. - """ - - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.act = nn.ReLU6(inplace) - - def forward(self, x): - return x * self.act(x + 3) / 6 diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/non_local.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/non_local.py deleted file mode 100755 index 92d00155e..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/non_local.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta - -import torch -import torch.nn as nn - -from ..utils import constant_init, normal_init -from .conv_module import ConvModule -from .registry import PLUGIN_LAYERS - - -class _NonLocalNd(nn.Module, metaclass=ABCMeta): - """Basic Non-local module. - - This module is proposed in - "Non-local Neural Networks" - Paper reference: https://arxiv.org/abs/1711.07971 - Code reference: https://github.com/AlexHex7/Non-local_pytorch - - Args: - in_channels (int): Channels of the input feature map. - reduction (int): Channel reduction ratio. Default: 2. - use_scale (bool): Whether to scale pairwise_weight by - `1/sqrt(inter_channels)` when the mode is `embedded_gaussian`. - Default: True. - conv_cfg (None | dict): The config dict for convolution layers. - If not specified, it will use `nn.Conv2d` for convolution layers. - Default: None. - norm_cfg (None | dict): The config dict for normalization layers. - Default: None. (This parameter is only applicable to conv_out.) - mode (str): Options are `gaussian`, `concatenation`, - `embedded_gaussian` and `dot_product`. Default: embedded_gaussian. - """ - - def __init__(self, - in_channels, - reduction=2, - use_scale=True, - conv_cfg=None, - norm_cfg=None, - mode='embedded_gaussian', - **kwargs): - super(_NonLocalNd, self).__init__() - self.in_channels = in_channels - self.reduction = reduction - self.use_scale = use_scale - self.inter_channels = max(in_channels // reduction, 1) - self.mode = mode - - if mode not in [ - 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation' - ]: - raise ValueError("Mode should be in 'gaussian', 'concatenation', " - f"'embedded_gaussian' or 'dot_product', but got " - f'{mode} instead.') - - # g, theta, phi are defaulted as `nn.ConvNd`. - # Here we use ConvModule for potential usage. - self.g = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.conv_out = ConvModule( - self.inter_channels, - self.in_channels, - kernel_size=1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=None) - - if self.mode != 'gaussian': - self.theta = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - self.phi = ConvModule( - self.in_channels, - self.inter_channels, - kernel_size=1, - conv_cfg=conv_cfg, - act_cfg=None) - - if self.mode == 'concatenation': - self.concat_project = ConvModule( - self.inter_channels * 2, - 1, - kernel_size=1, - stride=1, - padding=0, - bias=False, - act_cfg=dict(type='ReLU')) - - self.init_weights(**kwargs) - - def init_weights(self, std=0.01, zeros_init=True): - if self.mode != 'gaussian': - for m in [self.g, self.theta, self.phi]: - normal_init(m.conv, std=std) - else: - normal_init(self.g.conv, std=std) - if zeros_init: - if self.conv_out.norm_cfg is None: - constant_init(self.conv_out.conv, 0) - else: - constant_init(self.conv_out.norm, 0) - else: - if self.conv_out.norm_cfg is None: - normal_init(self.conv_out.conv, std=std) - else: - normal_init(self.conv_out.norm, std=std) - - def gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def embedded_gaussian(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - if self.use_scale: - # theta_x.shape[-1] is `self.inter_channels` - pairwise_weight /= theta_x.shape[-1]**0.5 - pairwise_weight = pairwise_weight.softmax(dim=-1) - return pairwise_weight - - def dot_product(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = torch.matmul(theta_x, phi_x) - pairwise_weight /= pairwise_weight.shape[-1] - return pairwise_weight - - def concatenation(self, theta_x, phi_x): - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - h = theta_x.size(2) - w = phi_x.size(3) - theta_x = theta_x.repeat(1, 1, 1, w) - phi_x = phi_x.repeat(1, 1, h, 1) - - concat_feature = torch.cat([theta_x, phi_x], dim=1) - pairwise_weight = self.concat_project(concat_feature) - n, _, h, w = pairwise_weight.size() - pairwise_weight = pairwise_weight.view(n, h, w) - pairwise_weight /= pairwise_weight.shape[-1] - - return pairwise_weight - - def forward(self, x): - # Assume `reduction = 1`, then `inter_channels = C` - # or `inter_channels = C` when `mode="gaussian"` - - # NonLocal1d x: [N, C, H] - # NonLocal2d x: [N, C, H, W] - # NonLocal3d x: [N, C, T, H, W] - n = x.size(0) - - # NonLocal1d g_x: [N, H, C] - # NonLocal2d g_x: [N, HxW, C] - # NonLocal3d g_x: [N, TxHxW, C] - g_x = self.g(x).view(n, self.inter_channels, -1) - g_x = g_x.permute(0, 2, 1) - - # NonLocal1d theta_x: [N, H, C], phi_x: [N, C, H] - # NonLocal2d theta_x: [N, HxW, C], phi_x: [N, C, HxW] - # NonLocal3d theta_x: [N, TxHxW, C], phi_x: [N, C, TxHxW] - if self.mode == 'gaussian': - theta_x = x.view(n, self.in_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - if self.sub_sample: - phi_x = self.phi(x).view(n, self.in_channels, -1) - else: - phi_x = x.view(n, self.in_channels, -1) - elif self.mode == 'concatenation': - theta_x = self.theta(x).view(n, self.inter_channels, -1, 1) - phi_x = self.phi(x).view(n, self.inter_channels, 1, -1) - else: - theta_x = self.theta(x).view(n, self.inter_channels, -1) - theta_x = theta_x.permute(0, 2, 1) - phi_x = self.phi(x).view(n, self.inter_channels, -1) - - pairwise_func = getattr(self, self.mode) - # NonLocal1d pairwise_weight: [N, H, H] - # NonLocal2d pairwise_weight: [N, HxW, HxW] - # NonLocal3d pairwise_weight: [N, TxHxW, TxHxW] - pairwise_weight = pairwise_func(theta_x, phi_x) - - # NonLocal1d y: [N, H, C] - # NonLocal2d y: [N, HxW, C] - # NonLocal3d y: [N, TxHxW, C] - y = torch.matmul(pairwise_weight, g_x) - # NonLocal1d y: [N, C, H] - # NonLocal2d y: [N, C, H, W] - # NonLocal3d y: [N, C, T, H, W] - y = y.permute(0, 2, 1).contiguous().reshape(n, self.inter_channels, - *x.size()[2:]) - - output = x + self.conv_out(y) - - return output - - -class NonLocal1d(_NonLocalNd): - """1D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv1d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv1d'), - **kwargs): - super(NonLocal1d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool1d(kernel_size=2) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -@PLUGIN_LAYERS.register_module() -class NonLocal2d(_NonLocalNd): - """2D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv2d'). - """ - - _abbr_ = 'nonlocal_block' - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv2d'), - **kwargs): - super(NonLocal2d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer - - -class NonLocal3d(_NonLocalNd): - """3D Non-local module. - - Args: - in_channels (int): Same as `NonLocalND`. - sub_sample (bool): Whether to apply max pooling after pairwise - function (Note that the `sub_sample` is applied on spatial only). - Default: False. - conv_cfg (None | dict): Same as `NonLocalND`. - Default: dict(type='Conv3d'). - """ - - def __init__(self, - in_channels, - sub_sample=False, - conv_cfg=dict(type='Conv3d'), - **kwargs): - super(NonLocal3d, self).__init__( - in_channels, conv_cfg=conv_cfg, **kwargs) - self.sub_sample = sub_sample - - if sub_sample: - max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) - self.g = nn.Sequential(self.g, max_pool_layer) - if self.mode != 'gaussian': - self.phi = nn.Sequential(self.phi, max_pool_layer) - else: - self.phi = max_pool_layer diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/norm.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/norm.py deleted file mode 100755 index cfb326bdb..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/norm.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect - -import torch.nn as nn - -from mmcv.utils import is_tuple_of -from mmcv.utils.parrots_wrapper import SyncBatchNorm, _BatchNorm, _InstanceNorm -from .registry import NORM_LAYERS - -NORM_LAYERS.register_module('BN', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN1d', module=nn.BatchNorm1d) -NORM_LAYERS.register_module('BN2d', module=nn.BatchNorm2d) -NORM_LAYERS.register_module('BN3d', module=nn.BatchNorm3d) -NORM_LAYERS.register_module('SyncBN', module=SyncBatchNorm) -NORM_LAYERS.register_module('GN', module=nn.GroupNorm) -NORM_LAYERS.register_module('LN', module=nn.LayerNorm) -NORM_LAYERS.register_module('IN', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN1d', module=nn.InstanceNorm1d) -NORM_LAYERS.register_module('IN2d', module=nn.InstanceNorm2d) -NORM_LAYERS.register_module('IN3d', module=nn.InstanceNorm3d) - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - When we build a norm layer with `build_norm_layer()`, we want to preserve - the norm type in variable names, e.g, self.bn1, self.gn. This method will - infer the abbreviation to map class types to abbreviations. - - Rule 1: If the class has the property "_abbr_", return the property. - Rule 2: If the parent class is _BatchNorm, GroupNorm, LayerNorm or - InstanceNorm, the abbreviation of this layer will be "bn", "gn", "ln" and - "in" respectively. - Rule 3: If the class name contains "batch", "group", "layer" or "instance", - the abbreviation of this layer will be "bn", "gn", "ln" and "in" - respectively. - Rule 4: Otherwise, the abbreviation falls back to "norm". - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - if issubclass(class_type, _InstanceNorm): # IN is a subclass of BN - return 'in' - elif issubclass(class_type, _BatchNorm): - return 'bn' - elif issubclass(class_type, nn.GroupNorm): - return 'gn' - elif issubclass(class_type, nn.LayerNorm): - return 'ln' - else: - class_name = class_type.__name__.lower() - if 'batch' in class_name: - return 'bn' - elif 'group' in class_name: - return 'gn' - elif 'layer' in class_name: - return 'ln' - elif 'instance' in class_name: - return 'in' - else: - return 'norm_layer' - - -def build_norm_layer(cfg, num_features, postfix=''): - """Build normalization layer. - - Args: - cfg (dict): The norm layer config, which should contain: - - - type (str): Layer type. - - layer args: Args needed to instantiate a norm layer. - - requires_grad (bool, optional): Whether stop gradient updates. - num_features (int): Number of input channels. - postfix (int | str): The postfix to be appended into norm abbreviation - to create named layer. - - Returns: - (str, nn.Module): The first element is the layer name consisting of - abbreviation and postfix, e.g., bn1, gn. The second element is the - created norm layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in NORM_LAYERS: - raise KeyError(f'Unrecognized norm type {layer_type}') - - norm_layer = NORM_LAYERS.get(layer_type) - abbr = infer_abbr(norm_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - requires_grad = cfg_.pop('requires_grad', True) - cfg_.setdefault('eps', 1e-5) - if layer_type != 'GN': - layer = norm_layer(num_features, **cfg_) - if layer_type == 'SyncBN' and hasattr(layer, '_specify_ddp_gpu_num'): - layer._specify_ddp_gpu_num(1) - else: - assert 'num_groups' in cfg_ - layer = norm_layer(num_channels=num_features, **cfg_) - - for param in layer.parameters(): - param.requires_grad = requires_grad - - return name, layer - - -def is_norm(layer, exclude=None): - """Check if a layer is a normalization layer. - - Args: - layer (nn.Module): The layer to be checked. - exclude (type | tuple[type]): Types to be excluded. - - Returns: - bool: Whether the layer is a norm layer. - """ - if exclude is not None: - if not isinstance(exclude, tuple): - exclude = (exclude, ) - if not is_tuple_of(exclude, type): - raise TypeError( - f'"exclude" must be either None or type or a tuple of types, ' - f'but got {type(exclude)}: {exclude}') - - if exclude and isinstance(layer, exclude): - return False - - all_norm_bases = (_BatchNorm, _InstanceNorm, nn.GroupNorm, nn.LayerNorm) - return isinstance(layer, all_norm_bases) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/padding.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/padding.py deleted file mode 100755 index e4ac6b28a..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/padding.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn - -from .registry import PADDING_LAYERS - -PADDING_LAYERS.register_module('zero', module=nn.ZeroPad2d) -PADDING_LAYERS.register_module('reflect', module=nn.ReflectionPad2d) -PADDING_LAYERS.register_module('replicate', module=nn.ReplicationPad2d) - - -def build_padding_layer(cfg, *args, **kwargs): - """Build padding layer. - - Args: - cfg (None or dict): The padding layer config, which should contain: - - type (str): Layer type. - - layer args: Args needed to instantiate a padding layer. - - Returns: - nn.Module: Created padding layer. - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - - cfg_ = cfg.copy() - padding_type = cfg_.pop('type') - if padding_type not in PADDING_LAYERS: - raise KeyError(f'Unrecognized padding type {padding_type}.') - else: - padding_layer = PADDING_LAYERS.get(padding_type) - - layer = padding_layer(*args, **kwargs, **cfg_) - - return layer diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/plugin.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/plugin.py deleted file mode 100755 index 07c010d40..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/plugin.py +++ /dev/null @@ -1,88 +0,0 @@ -import inspect -import platform - -from .registry import PLUGIN_LAYERS - -if platform.system() == 'Windows': - import regex as re -else: - import re - - -def infer_abbr(class_type): - """Infer abbreviation from the class name. - - This method will infer the abbreviation to map class types to - abbreviations. - - Rule 1: If the class has the property "abbr", return the property. - Rule 2: Otherwise, the abbreviation falls back to snake case of class - name, e.g. the abbreviation of ``FancyBlock`` will be ``fancy_block``. - - Args: - class_type (type): The norm layer type. - - Returns: - str: The inferred abbreviation. - """ - - def camel2snack(word): - """Convert camel case word into snack case. - - Modified from `inflection lib - `_. - - Example:: - - >>> camel2snack("FancyBlock") - 'fancy_block' - """ - - word = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', word) - word = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', word) - word = word.replace('-', '_') - return word.lower() - - if not inspect.isclass(class_type): - raise TypeError( - f'class_type must be a type, but got {type(class_type)}') - if hasattr(class_type, '_abbr_'): - return class_type._abbr_ - else: - return camel2snack(class_type.__name__) - - -def build_plugin_layer(cfg, postfix='', **kwargs): - """Build plugin layer. - - Args: - cfg (None or dict): cfg should contain: - type (str): identify plugin layer type. - layer args: args needed to instantiate a plugin layer. - postfix (int, str): appended into norm abbreviation to - create named layer. Default: ''. - - Returns: - tuple[str, nn.Module]: - name (str): abbreviation + postfix - layer (nn.Module): created plugin layer - """ - if not isinstance(cfg, dict): - raise TypeError('cfg must be a dict') - if 'type' not in cfg: - raise KeyError('the cfg dict must contain the key "type"') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in PLUGIN_LAYERS: - raise KeyError(f'Unrecognized plugin type {layer_type}') - - plugin_layer = PLUGIN_LAYERS.get(layer_type) - abbr = infer_abbr(plugin_layer) - - assert isinstance(postfix, (int, str)) - name = abbr + str(postfix) - - layer = plugin_layer(**kwargs, **cfg_) - - return name, layer diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/registry.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/registry.py deleted file mode 100755 index c29279776..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/registry.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -CONV_LAYERS = Registry('conv layer') -NORM_LAYERS = Registry('norm layer') -ACTIVATION_LAYERS = Registry('activation layer') -PADDING_LAYERS = Registry('padding layer') -UPSAMPLE_LAYERS = Registry('upsample layer') -PLUGIN_LAYERS = Registry('plugin layer') - -DROPOUT_LAYERS = Registry('drop out layers') -POSITIONAL_ENCODING = Registry('position encoding') -ATTENTION = Registry('attention') -FEEDFORWARD_NETWORK = Registry('feed-forward Network') -TRANSFORMER_LAYER = Registry('transformerLayer') -TRANSFORMER_LAYER_SEQUENCE = Registry('transformer-layers sequence') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/scale.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/scale.py deleted file mode 100755 index c905fffcc..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/scale.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class Scale(nn.Module): - """A learnable scale parameter. - - This layer scales the input by a learnable factor. It multiplies a - learnable scale parameter of shape (1,) with input of any shape. - - Args: - scale (float): Initial value of scale factor. Default: 1.0 - """ - - def __init__(self, scale=1.0): - super(Scale, self).__init__() - self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) - - def forward(self, x): - return x * self.scale diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/swish.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/swish.py deleted file mode 100755 index e2ca8ed7b..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/swish.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - -from .registry import ACTIVATION_LAYERS - - -@ACTIVATION_LAYERS.register_module() -class Swish(nn.Module): - """Swish Module. - - This module applies the swish function: - - .. math:: - Swish(x) = x * Sigmoid(x) - - Returns: - Tensor: The output tensor. - """ - - def __init__(self): - super(Swish, self).__init__() - - def forward(self, x): - return x * torch.sigmoid(x) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/transformer.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/transformer.py deleted file mode 100755 index ed32688af..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/transformer.py +++ /dev/null @@ -1,595 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings - -import torch -import torch.nn as nn - -from mmcv import ConfigDict, deprecated_api_warning -from mmcv.cnn import Linear, build_activation_layer, build_norm_layer -from mmcv.runner.base_module import BaseModule, ModuleList, Sequential -from mmcv.utils import build_from_cfg -from .drop import build_dropout -from .registry import (ATTENTION, FEEDFORWARD_NETWORK, POSITIONAL_ENCODING, - TRANSFORMER_LAYER, TRANSFORMER_LAYER_SEQUENCE) - -# Avoid BC-breaking of importing MultiScaleDeformableAttention from this file -try: - from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention # noqa F401 - warnings.warn( - ImportWarning( - '``MultiScaleDeformableAttention`` has been moved to ' - '``mmcv.ops.multi_scale_deform_attn``, please change original path ' # noqa E501 - '``from mmcv.cnn.bricks.transformer import MultiScaleDeformableAttention`` ' # noqa E501 - 'to ``from mmcv.ops.multi_scale_deform_attn import MultiScaleDeformableAttention`` ' # noqa E501 - )) - -except ImportError: - warnings.warn('Fail to import ``MultiScaleDeformableAttention`` from ' - '``mmcv.ops.multi_scale_deform_attn``, ' - 'You should install ``mmcv-full`` if you need this module. ') - - -def build_positional_encoding(cfg, default_args=None): - """Builder for Position Encoding.""" - return build_from_cfg(cfg, POSITIONAL_ENCODING, default_args) - - -def build_attention(cfg, default_args=None): - """Builder for attention.""" - return build_from_cfg(cfg, ATTENTION, default_args) - - -def build_feedforward_network(cfg, default_args=None): - """Builder for feed-forward network (FFN).""" - return build_from_cfg(cfg, FEEDFORWARD_NETWORK, default_args) - - -def build_transformer_layer(cfg, default_args=None): - """Builder for transformer layer.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER, default_args) - - -def build_transformer_layer_sequence(cfg, default_args=None): - """Builder for transformer encoder and transformer decoder.""" - return build_from_cfg(cfg, TRANSFORMER_LAYER_SEQUENCE, default_args) - - -@ATTENTION.register_module() -class MultiheadAttention(BaseModule): - """A wrapper for ``torch.nn.MultiheadAttention``. - - This module implements MultiheadAttention with identity connection, - and positional encoding is also passed as input. - - Args: - embed_dims (int): The embedding dimension. - num_heads (int): Parallel attention heads. - attn_drop (float): A Dropout layer on attn_output_weights. - Default: 0.0. - proj_drop (float): A Dropout layer after `nn.MultiheadAttention`. - Default: 0.0. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): When it is True, Key, Query and Value are shape of - (batch, n, embed_dim), otherwise (n, batch, embed_dim). - Default to False. - """ - - def __init__(self, - embed_dims, - num_heads, - attn_drop=0., - proj_drop=0., - dropout_layer=dict(type='Dropout', drop_prob=0.), - init_cfg=None, - batch_first=False, - **kwargs): - super(MultiheadAttention, self).__init__(init_cfg) - if 'dropout' in kwargs: - warnings.warn('The arguments `dropout` in MultiheadAttention ' - 'has been deprecated, now you can separately ' - 'set `attn_drop`(float), proj_drop(float), ' - 'and `dropout_layer`(dict) ') - attn_drop = kwargs['dropout'] - dropout_layer['drop_prob'] = kwargs.pop('dropout') - - self.embed_dims = embed_dims - self.num_heads = num_heads - self.batch_first = batch_first - - self.attn = nn.MultiheadAttention(embed_dims, num_heads, attn_drop, - **kwargs) - - self.proj_drop = nn.Dropout(proj_drop) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else nn.Identity() - - @deprecated_api_warning({'residual': 'identity'}, - cls_name='MultiheadAttention') - def forward(self, - query, - key=None, - value=None, - identity=None, - query_pos=None, - key_pos=None, - attn_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `MultiheadAttention`. - - **kwargs allow passing a more general data flow when combining - with other operations in `transformerlayer`. - - Args: - query (Tensor): The input query with shape [num_queries, bs, - embed_dims] if self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - If None, the ``query`` will be used. Defaults to None. - value (Tensor): The value tensor with same shape as `key`. - Same in `nn.MultiheadAttention.forward`. Defaults to None. - If None, the `key` will be used. - identity (Tensor): This tensor, with the same shape as x, - will be used for the identity link. - If None, `x` will be used. Defaults to None. - query_pos (Tensor): The positional encoding for query, with - the same shape as `x`. If not None, it will - be added to `x` before forward function. Defaults to None. - key_pos (Tensor): The positional encoding for `key`, with the - same shape as `key`. Defaults to None. If not None, it will - be added to `key` before forward function. If None, and - `query_pos` has the same shape as `key`, then `query_pos` - will be used for `key_pos`. Defaults to None. - attn_mask (Tensor): ByteTensor mask with shape [num_queries, - num_keys]. Same in `nn.MultiheadAttention.forward`. - Defaults to None. - key_padding_mask (Tensor): ByteTensor with shape [bs, num_keys]. - Defaults to None. - - Returns: - Tensor: forwarded results with shape - [num_queries, bs, embed_dims] - if self.batch_first is False, else - [bs, num_queries embed_dims]. - """ - - if key is None: - key = query - if value is None: - value = key - if identity is None: - identity = query - if key_pos is None: - if query_pos is not None: - # use query_pos if key_pos is not available - if query_pos.shape == key.shape: - key_pos = query_pos - else: - warnings.warn(f'position encoding of key is' - f'missing in {self.__class__.__name__}.') - if query_pos is not None: - query = query + query_pos - if key_pos is not None: - key = key + key_pos - - # Because the dataflow('key', 'query', 'value') of - # ``torch.nn.MultiheadAttention`` is (num_query, batch, - # embed_dims), We should adjust the shape of dataflow from - # batch_first (batch, num_query, embed_dims) to num_query_first - # (num_query ,batch, embed_dims), and recover ``attn_output`` - # from num_query_first to batch_first. - if self.batch_first: - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - out = self.attn( - query=query, - key=key, - value=value, - attn_mask=attn_mask, - key_padding_mask=key_padding_mask)[0] - - if self.batch_first: - out = out.transpose(0, 1) - - return identity + self.dropout_layer(self.proj_drop(out)) - - -@FEEDFORWARD_NETWORK.register_module() -class FFN(BaseModule): - """Implements feed-forward networks (FFNs) with identity connection. - - Args: - embed_dims (int): The feature dimension. Same as - `MultiheadAttention`. Defaults: 256. - feedforward_channels (int): The hidden dimension of FFNs. - Defaults: 1024. - num_fcs (int, optional): The number of fully-connected layers in - FFNs. Default: 2. - act_cfg (dict, optional): The activation config for FFNs. - Default: dict(type='ReLU') - ffn_drop (float, optional): Probability of an element to be - zeroed in FFN. Default 0.0. - add_identity (bool, optional): Whether to add the - identity connection. Default: `True`. - dropout_layer (obj:`ConfigDict`): The dropout_layer used - when adding the shortcut. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - @deprecated_api_warning( - { - 'dropout': 'ffn_drop', - 'add_residual': 'add_identity' - }, - cls_name='FFN') - def __init__(self, - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - act_cfg=dict(type='ReLU', inplace=True), - ffn_drop=0., - dropout_layer=None, - add_identity=True, - init_cfg=None, - **kwargs): - super(FFN, self).__init__(init_cfg) - assert num_fcs >= 2, 'num_fcs should be no less ' \ - f'than 2. got {num_fcs}.' - self.embed_dims = embed_dims - self.feedforward_channels = feedforward_channels - self.num_fcs = num_fcs - self.act_cfg = act_cfg - self.activate = build_activation_layer(act_cfg) - - layers = [] - in_channels = embed_dims - for _ in range(num_fcs - 1): - layers.append( - Sequential( - Linear(in_channels, feedforward_channels), self.activate, - nn.Dropout(ffn_drop))) - in_channels = feedforward_channels - layers.append(Linear(feedforward_channels, embed_dims)) - layers.append(nn.Dropout(ffn_drop)) - self.layers = Sequential(*layers) - self.dropout_layer = build_dropout( - dropout_layer) if dropout_layer else torch.nn.Identity() - self.add_identity = add_identity - - @deprecated_api_warning({'residual': 'identity'}, cls_name='FFN') - def forward(self, x, identity=None): - """Forward function for `FFN`. - - The function would add x to the output tensor if residue is None. - """ - out = self.layers(x) - if not self.add_identity: - return self.dropout_layer(out) - if identity is None: - identity = x - return identity + self.dropout_layer(out) - - -@TRANSFORMER_LAYER.register_module() -class BaseTransformerLayer(BaseModule): - """Base `TransformerLayer` for vision transformer. - - It can be built from `mmcv.ConfigDict` and support more flexible - customization, for example, using any number of `FFN or LN ` and - use different kinds of `attention` by specifying a list of `ConfigDict` - named `attn_cfgs`. It is worth mentioning that it supports `prenorm` - when you specifying `norm` as the first element of `operation_order`. - More details about the `prenorm`: `On Layer Normalization in the - Transformer Architecture `_ . - - Args: - attn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for `self_attention` or `cross_attention` modules, - The order of the configs in the list should be consistent with - corresponding attentions in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. Default: None. - ffn_cfgs (list[`mmcv.ConfigDict`] | obj:`mmcv.ConfigDict` | None )): - Configs for FFN, The order of the configs in the list should be - consistent with corresponding ffn in operation_order. - If it is a dict, all of the attention modules in operation_order - will be built with this config. - operation_order (tuple[str]): The execution order of operation - in transformer. Such as ('self_attn', 'norm', 'ffn', 'norm'). - Support `prenorm` when you specifying first element as `norm`. - Default:None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='LN'). - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - batch_first (bool): Key, Query and Value are shape - of (batch, n, embed_dim) - or (n, batch, embed_dim). Default to False. - """ - - def __init__(self, - attn_cfgs=None, - ffn_cfgs=dict( - type='FFN', - embed_dims=256, - feedforward_channels=1024, - num_fcs=2, - ffn_drop=0., - act_cfg=dict(type='ReLU', inplace=True), - ), - operation_order=None, - norm_cfg=dict(type='LN'), - init_cfg=None, - batch_first=False, - **kwargs): - - deprecated_args = dict( - feedforward_channels='feedforward_channels', - ffn_dropout='ffn_drop', - ffn_num_fcs='num_fcs') - for ori_name, new_name in deprecated_args.items(): - if ori_name in kwargs: - warnings.warn( - f'The arguments `{ori_name}` in BaseTransformerLayer ' - f'has been deprecated, now you should set `{new_name}` ' - f'and other FFN related arguments ' - f'to a dict named `ffn_cfgs`. ') - ffn_cfgs[new_name] = kwargs[ori_name] - - super(BaseTransformerLayer, self).__init__(init_cfg) - - self.batch_first = batch_first - - assert set(operation_order) & set( - ['self_attn', 'norm', 'ffn', 'cross_attn']) == \ - set(operation_order), f'The operation_order of' \ - f' {self.__class__.__name__} should ' \ - f'contains all four operation type ' \ - f"{['self_attn', 'norm', 'ffn', 'cross_attn']}" - - num_attn = operation_order.count('self_attn') + operation_order.count( - 'cross_attn') - if isinstance(attn_cfgs, dict): - attn_cfgs = [copy.deepcopy(attn_cfgs) for _ in range(num_attn)] - else: - assert num_attn == len(attn_cfgs), f'The length ' \ - f'of attn_cfg {num_attn} is ' \ - f'not consistent with the number of attention' \ - f'in operation_order {operation_order}.' - - self.num_attn = num_attn - self.operation_order = operation_order - self.norm_cfg = norm_cfg - self.pre_norm = operation_order[0] == 'norm' - self.attentions = ModuleList() - - index = 0 - for operation_name in operation_order: - if operation_name in ['self_attn', 'cross_attn']: - if 'batch_first' in attn_cfgs[index]: - assert self.batch_first == attn_cfgs[index]['batch_first'] - else: - attn_cfgs[index]['batch_first'] = self.batch_first - attention = build_attention(attn_cfgs[index]) - # Some custom attentions used as `self_attn` - # or `cross_attn` can have different behavior. - attention.operation_name = operation_name - self.attentions.append(attention) - index += 1 - - self.embed_dims = self.attentions[0].embed_dims - - self.ffns = ModuleList() - num_ffns = operation_order.count('ffn') - if isinstance(ffn_cfgs, dict): - ffn_cfgs = ConfigDict(ffn_cfgs) - if isinstance(ffn_cfgs, dict): - ffn_cfgs = [copy.deepcopy(ffn_cfgs) for _ in range(num_ffns)] - assert len(ffn_cfgs) == num_ffns - for ffn_index in range(num_ffns): - if 'embed_dims' not in ffn_cfgs[ffn_index]: - ffn_cfgs['embed_dims'] = self.embed_dims - else: - assert ffn_cfgs[ffn_index]['embed_dims'] == self.embed_dims - self.ffns.append( - build_feedforward_network(ffn_cfgs[ffn_index], - dict(type='FFN'))) - - self.norms = ModuleList() - num_norms = operation_order.count('norm') - for _ in range(num_norms): - self.norms.append(build_norm_layer(norm_cfg, self.embed_dims)[1]) - - def forward(self, - query, - key=None, - value=None, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerDecoderLayer`. - - **kwargs contains some specific arguments of attentions. - - Args: - query (Tensor): The input query with shape - [num_queries, bs, embed_dims] if - self.batch_first is False, else - [bs, num_queries embed_dims]. - key (Tensor): The key tensor with shape [num_keys, bs, - embed_dims] if self.batch_first is False, else - [bs, num_keys, embed_dims] . - value (Tensor): The value tensor with same shape as `key`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor] | None): 2D Tensor used in - calculation of corresponding attention. The length of - it should equal to the number of `attention` in - `operation_order`. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in `self_attn` layer. - Defaults to None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: forwarded results with shape [num_queries, bs, embed_dims]. - """ - - norm_index = 0 - attn_index = 0 - ffn_index = 0 - identity = query - if attn_masks is None: - attn_masks = [None for _ in range(self.num_attn)] - elif isinstance(attn_masks, torch.Tensor): - attn_masks = [ - copy.deepcopy(attn_masks) for _ in range(self.num_attn) - ] - warnings.warn(f'Use same attn_mask in all attentions in ' - f'{self.__class__.__name__} ') - else: - assert len(attn_masks) == self.num_attn, f'The length of ' \ - f'attn_masks {len(attn_masks)} must be equal ' \ - f'to the number of attention in ' \ - f'operation_order {self.num_attn}' - - for layer in self.operation_order: - if layer == 'self_attn': - temp_key = temp_value = query - query = self.attentions[attn_index]( - query, - temp_key, - temp_value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=query_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=query_key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'norm': - query = self.norms[norm_index](query) - norm_index += 1 - - elif layer == 'cross_attn': - query = self.attentions[attn_index]( - query, - key, - value, - identity if self.pre_norm else None, - query_pos=query_pos, - key_pos=key_pos, - attn_mask=attn_masks[attn_index], - key_padding_mask=key_padding_mask, - **kwargs) - attn_index += 1 - identity = query - - elif layer == 'ffn': - query = self.ffns[ffn_index]( - query, identity if self.pre_norm else None) - ffn_index += 1 - - return query - - -@TRANSFORMER_LAYER_SEQUENCE.register_module() -class TransformerLayerSequence(BaseModule): - """Base class for TransformerEncoder and TransformerDecoder in vision - transformer. - - As base-class of Encoder and Decoder in vision transformer. - Support customization such as specifying different kind - of `transformer_layer` in `transformer_coder`. - - Args: - transformerlayer (list[obj:`mmcv.ConfigDict`] | - obj:`mmcv.ConfigDict`): Config of transformerlayer - in TransformerCoder. If it is obj:`mmcv.ConfigDict`, - it would be repeated `num_layer` times to a - list[`mmcv.ConfigDict`]. Default: None. - num_layers (int): The number of `TransformerLayer`. Default: None. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. - Default: None. - """ - - def __init__(self, transformerlayers=None, num_layers=None, init_cfg=None): - super(TransformerLayerSequence, self).__init__(init_cfg) - if isinstance(transformerlayers, dict): - transformerlayers = [ - copy.deepcopy(transformerlayers) for _ in range(num_layers) - ] - else: - assert isinstance(transformerlayers, list) and \ - len(transformerlayers) == num_layers - self.num_layers = num_layers - self.layers = ModuleList() - for i in range(num_layers): - self.layers.append(build_transformer_layer(transformerlayers[i])) - self.embed_dims = self.layers[0].embed_dims - self.pre_norm = self.layers[0].pre_norm - - def forward(self, - query, - key, - value, - query_pos=None, - key_pos=None, - attn_masks=None, - query_key_padding_mask=None, - key_padding_mask=None, - **kwargs): - """Forward function for `TransformerCoder`. - - Args: - query (Tensor): Input query with shape - `(num_queries, bs, embed_dims)`. - key (Tensor): The key tensor with shape - `(num_keys, bs, embed_dims)`. - value (Tensor): The value tensor with shape - `(num_keys, bs, embed_dims)`. - query_pos (Tensor): The positional encoding for `query`. - Default: None. - key_pos (Tensor): The positional encoding for `key`. - Default: None. - attn_masks (List[Tensor], optional): Each element is 2D Tensor - which is used in calculation of corresponding attention in - operation_order. Default: None. - query_key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_queries]. Only used in self-attention - Default: None. - key_padding_mask (Tensor): ByteTensor for `query`, with - shape [bs, num_keys]. Default: None. - - Returns: - Tensor: results with shape [num_queries, bs, embed_dims]. - """ - for layer in self.layers: - query = layer( - query, - key, - value, - query_pos=query_pos, - key_pos=key_pos, - attn_masks=attn_masks, - query_key_padding_mask=query_key_padding_mask, - key_padding_mask=key_padding_mask, - **kwargs) - return query diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/upsample.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/upsample.py deleted file mode 100755 index a1a353767..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/upsample.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from ..utils import xavier_init -from .registry import UPSAMPLE_LAYERS - -UPSAMPLE_LAYERS.register_module('nearest', module=nn.Upsample) -UPSAMPLE_LAYERS.register_module('bilinear', module=nn.Upsample) - - -@UPSAMPLE_LAYERS.register_module(name='pixel_shuffle') -class PixelShufflePack(nn.Module): - """Pixel Shuffle upsample layer. - - This module packs `F.pixel_shuffle()` and a nn.Conv2d module together to - achieve a simple upsampling with pixel shuffle. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of the conv layer to expand the - channels. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super(PixelShufflePack, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - xavier_init(self.upsample_conv, distribution='uniform') - - def forward(self, x): - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x - - -def build_upsample_layer(cfg, *args, **kwargs): - """Build upsample layer. - - Args: - cfg (dict): The upsample layer config, which should contain: - - - type (str): Layer type. - - scale_factor (int): Upsample ratio, which is not applicable to - deconv. - - layer args: Args needed to instantiate a upsample layer. - args (argument list): Arguments passed to the ``__init__`` - method of the corresponding conv layer. - kwargs (keyword arguments): Keyword arguments passed to the - ``__init__`` method of the corresponding conv layer. - - Returns: - nn.Module: Created upsample layer. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - raise KeyError( - f'the cfg dict must contain the key "type", but got {cfg}') - cfg_ = cfg.copy() - - layer_type = cfg_.pop('type') - if layer_type not in UPSAMPLE_LAYERS: - raise KeyError(f'Unrecognized upsample type {layer_type}') - else: - upsample = UPSAMPLE_LAYERS.get(layer_type) - - if upsample is nn.Upsample: - cfg_['mode'] = layer_type - layer = upsample(*args, **kwargs, **cfg_) - return layer diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/wrappers.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/wrappers.py deleted file mode 100755 index 8aebf67bf..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/bricks/wrappers.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -r"""Modified from https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/wrappers.py # noqa: E501 - -Wrap some nn modules to support empty tensor input. Currently, these wrappers -are mainly used in mask heads like fcn_mask_head and maskiou_heads since mask -heads are trained on only positive RoIs. -""" -import math - -import torch -import torch.nn as nn -from torch.nn.modules.utils import _pair, _triple - -from .registry import CONV_LAYERS, UPSAMPLE_LAYERS - -if torch.__version__ == 'parrots': - TORCH_VERSION = torch.__version__ -else: - # torch.__version__ could be 1.3.1+cu92, we only need the first two - # for comparison - TORCH_VERSION = tuple(int(x) for x in torch.__version__.split('.')[:2]) - - -def obsolete_torch_version(torch_version, version_threshold): - return torch_version == 'parrots' or torch_version <= version_threshold - - -class NewEmptyTensorOp(torch.autograd.Function): - - @staticmethod - def forward(ctx, x, new_shape): - ctx.shape = x.shape - return x.new_empty(new_shape) - - @staticmethod - def backward(ctx, grad): - shape = ctx.shape - return NewEmptyTensorOp.apply(grad, shape), None - - -@CONV_LAYERS.register_module('Conv', force=True) -class Conv2d(nn.Conv2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module('Conv3d', force=True) -class Conv3d(nn.Conv3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, self.dilation): - o = (i + 2 * p - (d * (k - 1) + 1)) // s + 1 - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv') -@UPSAMPLE_LAYERS.register_module('deconv', force=True) -class ConvTranspose2d(nn.ConvTranspose2d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-2:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -@CONV_LAYERS.register_module() -@CONV_LAYERS.register_module('deconv3d') -@UPSAMPLE_LAYERS.register_module('deconv3d', force=True) -class ConvTranspose3d(nn.ConvTranspose3d): - - def forward(self, x): - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 4)): - out_shape = [x.shape[0], self.out_channels] - for i, k, p, s, d, op in zip(x.shape[-3:], self.kernel_size, - self.padding, self.stride, - self.dilation, self.output_padding): - out_shape.append((i - 1) * s - 2 * p + (d * (k - 1) + 1) + op) - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) - - -class MaxPool2d(nn.MaxPool2d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-2:], _pair(self.kernel_size), - _pair(self.padding), _pair(self.stride), - _pair(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class MaxPool3d(nn.MaxPool3d): - - def forward(self, x): - # PyTorch 1.9 does not support empty tensor inference yet - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 9)): - out_shape = list(x.shape[:2]) - for i, k, p, s, d in zip(x.shape[-3:], _triple(self.kernel_size), - _triple(self.padding), - _triple(self.stride), - _triple(self.dilation)): - o = (i + 2 * p - (d * (k - 1) + 1)) / s + 1 - o = math.ceil(o) if self.ceil_mode else math.floor(o) - out_shape.append(o) - empty = NewEmptyTensorOp.apply(x, out_shape) - return empty - - return super().forward(x) - - -class Linear(torch.nn.Linear): - - def forward(self, x): - # empty tensor forward of Linear layer is supported in Pytorch 1.6 - if x.numel() == 0 and obsolete_torch_version(TORCH_VERSION, (1, 5)): - out_shape = [x.shape[0], self.out_features] - empty = NewEmptyTensorOp.apply(x, out_shape) - if self.training: - # produce dummy gradient to avoid DDP warning. - dummy = sum(x.view(-1)[0] for x in self.parameters()) * 0.0 - return empty + dummy - else: - return empty - - return super().forward(x) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/builder.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/builder.py deleted file mode 100755 index 7567316c5..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/builder.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..runner import Sequential -from ..utils import Registry, build_from_cfg - - -def build_model_from_cfg(cfg, registry, default_args=None): - """Build a PyTorch model from config dict(s). Different from - ``build_from_cfg``, if cfg is a list, a ``nn.Sequential`` will be built. - - Args: - cfg (dict, list[dict]): The config of modules, is is either a config - dict or a list of config dicts. If cfg is a list, a - the built modules will be wrapped with ``nn.Sequential``. - registry (:obj:`Registry`): A registry the module belongs to. - default_args (dict, optional): Default arguments to build the module. - Defaults to None. - - Returns: - nn.Module: A built nn module. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return Sequential(*modules) - else: - return build_from_cfg(cfg, registry, default_args) - - -MODELS = Registry('model', build_func=build_model_from_cfg) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/resnet.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/resnet.py deleted file mode 100755 index 1cb3ac057..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/resnet.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn -import torch.utils.checkpoint as cp - -from .utils import constant_init, kaiming_init - - -def conv3x3(in_planes, out_planes, stride=1, dilation=1): - """3x3 convolution with padding.""" - return nn.Conv2d( - in_planes, - out_planes, - kernel_size=3, - stride=stride, - padding=dilation, - dilation=dilation, - bias=False) - - -class BasicBlock(nn.Module): - expansion = 1 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False): - super(BasicBlock, self).__init__() - assert style in ['pytorch', 'caffe'] - self.conv1 = conv3x3(inplanes, planes, stride, dilation) - self.bn1 = nn.BatchNorm2d(planes) - self.relu = nn.ReLU(inplace=True) - self.conv2 = conv3x3(planes, planes) - self.bn2 = nn.BatchNorm2d(planes) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - assert not with_cp - - def forward(self, x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - out = self.relu(out) - - return out - - -class Bottleneck(nn.Module): - expansion = 4 - - def __init__(self, - inplanes, - planes, - stride=1, - dilation=1, - downsample=None, - style='pytorch', - with_cp=False): - """Bottleneck block. - - If style is "pytorch", the stride-two layer is the 3x3 conv layer, if - it is "caffe", the stride-two layer is the first 1x1 conv layer. - """ - super(Bottleneck, self).__init__() - assert style in ['pytorch', 'caffe'] - if style == 'pytorch': - conv1_stride = 1 - conv2_stride = stride - else: - conv1_stride = stride - conv2_stride = 1 - self.conv1 = nn.Conv2d( - inplanes, planes, kernel_size=1, stride=conv1_stride, bias=False) - self.conv2 = nn.Conv2d( - planes, - planes, - kernel_size=3, - stride=conv2_stride, - padding=dilation, - dilation=dilation, - bias=False) - - self.bn1 = nn.BatchNorm2d(planes) - self.bn2 = nn.BatchNorm2d(planes) - self.conv3 = nn.Conv2d( - planes, planes * self.expansion, kernel_size=1, bias=False) - self.bn3 = nn.BatchNorm2d(planes * self.expansion) - self.relu = nn.ReLU(inplace=True) - self.downsample = downsample - self.stride = stride - self.dilation = dilation - self.with_cp = with_cp - - def forward(self, x): - - def _inner_forward(x): - residual = x - - out = self.conv1(x) - out = self.bn1(out) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out) - out = self.relu(out) - - out = self.conv3(out) - out = self.bn3(out) - - if self.downsample is not None: - residual = self.downsample(x) - - out += residual - - return out - - if self.with_cp and x.requires_grad: - out = cp.checkpoint(_inner_forward, x) - else: - out = _inner_forward(x) - - out = self.relu(out) - - return out - - -def make_res_layer(block, - inplanes, - planes, - blocks, - stride=1, - dilation=1, - style='pytorch', - with_cp=False): - downsample = None - if stride != 1 or inplanes != planes * block.expansion: - downsample = nn.Sequential( - nn.Conv2d( - inplanes, - planes * block.expansion, - kernel_size=1, - stride=stride, - bias=False), - nn.BatchNorm2d(planes * block.expansion), - ) - - layers = [] - layers.append( - block( - inplanes, - planes, - stride, - dilation, - downsample, - style=style, - with_cp=with_cp)) - inplanes = planes * block.expansion - for _ in range(1, blocks): - layers.append( - block(inplanes, planes, 1, dilation, style=style, with_cp=with_cp)) - - return nn.Sequential(*layers) - - -class ResNet(nn.Module): - """ResNet backbone. - - Args: - depth (int): Depth of resnet, from {18, 34, 50, 101, 152}. - num_stages (int): Resnet stages, normally 4. - strides (Sequence[int]): Strides of the first block of each stage. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two - layer is the 3x3 conv layer, otherwise the stride-two layer is - the first 1x1 conv layer. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze - running stats (mean and var). - bn_frozen (bool): Whether to freeze weight and bias of BN layers. - with_cp (bool): Use checkpoint or not. Using checkpoint will save some - memory while slowing down the training speed. - """ - - arch_settings = { - 18: (BasicBlock, (2, 2, 2, 2)), - 34: (BasicBlock, (3, 4, 6, 3)), - 50: (Bottleneck, (3, 4, 6, 3)), - 101: (Bottleneck, (3, 4, 23, 3)), - 152: (Bottleneck, (3, 8, 36, 3)) - } - - def __init__(self, - depth, - num_stages=4, - strides=(1, 2, 2, 2), - dilations=(1, 1, 1, 1), - out_indices=(0, 1, 2, 3), - style='pytorch', - frozen_stages=-1, - bn_eval=True, - bn_frozen=False, - with_cp=False): - super(ResNet, self).__init__() - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for resnet') - assert num_stages >= 1 and num_stages <= 4 - block, stage_blocks = self.arch_settings[depth] - stage_blocks = stage_blocks[:num_stages] - assert len(strides) == len(dilations) == num_stages - assert max(out_indices) < num_stages - - self.out_indices = out_indices - self.style = style - self.frozen_stages = frozen_stages - self.bn_eval = bn_eval - self.bn_frozen = bn_frozen - self.with_cp = with_cp - - self.inplanes = 64 - self.conv1 = nn.Conv2d( - 3, 64, kernel_size=7, stride=2, padding=3, bias=False) - self.bn1 = nn.BatchNorm2d(64) - self.relu = nn.ReLU(inplace=True) - self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) - - self.res_layers = [] - for i, num_blocks in enumerate(stage_blocks): - stride = strides[i] - dilation = dilations[i] - planes = 64 * 2**i - res_layer = make_res_layer( - block, - self.inplanes, - planes, - num_blocks, - stride=stride, - dilation=dilation, - style=self.style, - with_cp=with_cp) - self.inplanes = planes * block.expansion - layer_name = f'layer{i + 1}' - self.add_module(layer_name, res_layer) - self.res_layers.append(layer_name) - - self.feat_dim = block.expansion * 64 * 2**(len(stage_blocks) - 1) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m) - elif isinstance(m, nn.BatchNorm2d): - constant_init(m, 1) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - x = self.relu(x) - x = self.maxpool(x) - outs = [] - for i, layer_name in enumerate(self.res_layers): - res_layer = getattr(self, layer_name) - x = res_layer(x) - if i in self.out_indices: - outs.append(x) - if len(outs) == 1: - return outs[0] - else: - return tuple(outs) - - def train(self, mode=True): - super(ResNet, self).train(mode) - if self.bn_eval: - for m in self.modules(): - if isinstance(m, nn.BatchNorm2d): - m.eval() - if self.bn_frozen: - for params in m.parameters(): - params.requires_grad = False - if mode and self.frozen_stages >= 0: - for param in self.conv1.parameters(): - param.requires_grad = False - for param in self.bn1.parameters(): - param.requires_grad = False - self.bn1.eval() - self.bn1.weight.requires_grad = False - self.bn1.bias.requires_grad = False - for i in range(1, self.frozen_stages + 1): - mod = getattr(self, f'layer{i}') - mod.eval() - for param in mod.parameters(): - param.requires_grad = False diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/__init__.py deleted file mode 100755 index a263e31c1..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .flops_counter import get_model_complexity_info -from .fuse_conv_bn import fuse_conv_bn -from .sync_bn import revert_sync_batchnorm -from .weight_init import (INITIALIZERS, Caffe2XavierInit, ConstantInit, - KaimingInit, NormalInit, PretrainedInit, - TruncNormalInit, UniformInit, XavierInit, - bias_init_with_prob, caffe2_xavier_init, - constant_init, initialize, kaiming_init, normal_init, - trunc_normal_init, uniform_init, xavier_init) - -__all__ = [ - 'get_model_complexity_info', 'bias_init_with_prob', 'caffe2_xavier_init', - 'constant_init', 'kaiming_init', 'normal_init', 'trunc_normal_init', - 'uniform_init', 'xavier_init', 'fuse_conv_bn', 'initialize', - 'INITIALIZERS', 'ConstantInit', 'XavierInit', 'NormalInit', - 'TruncNormalInit', 'UniformInit', 'KaimingInit', 'PretrainedInit', - 'Caffe2XavierInit', 'revert_sync_batchnorm' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/flops_counter.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/flops_counter.py deleted file mode 100755 index dceeb398b..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/flops_counter.py +++ /dev/null @@ -1,599 +0,0 @@ -# Modified from flops-counter.pytorch by Vladislav Sovrasov -# original repo: https://github.com/sovrasov/flops-counter.pytorch - -# MIT License - -# Copyright (c) 2018 Vladislav Sovrasov - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys -from functools import partial - -import numpy as np -import torch -import torch.nn as nn - -import mmcv - - -def get_model_complexity_info(model, - input_shape, - print_per_layer_stat=True, - as_strings=True, - input_constructor=None, - flush=False, - ost=sys.stdout): - """Get complexity information of a model. - - This method can calculate FLOPs and parameter counts of a model with - corresponding input shape. It can also print complexity information for - each layer in a model. - - Supported layers are listed as below: - - Convolutions: ``nn.Conv1d``, ``nn.Conv2d``, ``nn.Conv3d``. - - Activations: ``nn.ReLU``, ``nn.PReLU``, ``nn.ELU``, ``nn.LeakyReLU``, - ``nn.ReLU6``. - - Poolings: ``nn.MaxPool1d``, ``nn.MaxPool2d``, ``nn.MaxPool3d``, - ``nn.AvgPool1d``, ``nn.AvgPool2d``, ``nn.AvgPool3d``, - ``nn.AdaptiveMaxPool1d``, ``nn.AdaptiveMaxPool2d``, - ``nn.AdaptiveMaxPool3d``, ``nn.AdaptiveAvgPool1d``, - ``nn.AdaptiveAvgPool2d``, ``nn.AdaptiveAvgPool3d``. - - BatchNorms: ``nn.BatchNorm1d``, ``nn.BatchNorm2d``, - ``nn.BatchNorm3d``, ``nn.GroupNorm``, ``nn.InstanceNorm1d``, - ``InstanceNorm2d``, ``InstanceNorm3d``, ``nn.LayerNorm``. - - Linear: ``nn.Linear``. - - Deconvolution: ``nn.ConvTranspose2d``. - - Upsample: ``nn.Upsample``. - - Args: - model (nn.Module): The model for complexity calculation. - input_shape (tuple): Input shape used for calculation. - print_per_layer_stat (bool): Whether to print complexity information - for each layer in a model. Default: True. - as_strings (bool): Output FLOPs and params counts in a string form. - Default: True. - input_constructor (None | callable): If specified, it takes a callable - method that generates input. otherwise, it will generate a random - tensor with input shape to calculate FLOPs. Default: None. - flush (bool): same as that in :func:`print`. Default: False. - ost (stream): same as ``file`` param in :func:`print`. - Default: sys.stdout. - - Returns: - tuple[float | str]: If ``as_strings`` is set to True, it will return - FLOPs and parameter counts in a string format. otherwise, it will - return those in a float number format. - """ - assert type(input_shape) is tuple - assert len(input_shape) >= 1 - assert isinstance(model, nn.Module) - flops_model = add_flops_counting_methods(model) - flops_model.eval() - flops_model.start_flops_count() - if input_constructor: - input = input_constructor(input_shape) - _ = flops_model(**input) - else: - try: - batch = torch.ones(()).new_empty( - (1, *input_shape), - dtype=next(flops_model.parameters()).dtype, - device=next(flops_model.parameters()).device) - except StopIteration: - # Avoid StopIteration for models which have no parameters, - # like `nn.Relu()`, `nn.AvgPool2d`, etc. - batch = torch.ones(()).new_empty((1, *input_shape)) - - _ = flops_model(batch) - - flops_count, params_count = flops_model.compute_average_flops_cost() - if print_per_layer_stat: - print_model_with_flops( - flops_model, flops_count, params_count, ost=ost, flush=flush) - flops_model.stop_flops_count() - - if as_strings: - return flops_to_string(flops_count), params_to_string(params_count) - - return flops_count, params_count - - -def flops_to_string(flops, units='GFLOPs', precision=2): - """Convert FLOPs number into a string. - - Note that Here we take a multiply-add counts as one FLOP. - - Args: - flops (float): FLOPs number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'GFLOPs', - 'MFLOPs', 'KFLOPs', 'FLOPs'. If set to None, it will automatically - choose the most suitable unit for FLOPs. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted FLOPs number with units. - - Examples: - >>> flops_to_string(1e9) - '1.0 GFLOPs' - >>> flops_to_string(2e5, 'MFLOPs') - '0.2 MFLOPs' - >>> flops_to_string(3e-9, None) - '3e-09 FLOPs' - """ - if units is None: - if flops // 10**9 > 0: - return str(round(flops / 10.**9, precision)) + ' GFLOPs' - elif flops // 10**6 > 0: - return str(round(flops / 10.**6, precision)) + ' MFLOPs' - elif flops // 10**3 > 0: - return str(round(flops / 10.**3, precision)) + ' KFLOPs' - else: - return str(flops) + ' FLOPs' - else: - if units == 'GFLOPs': - return str(round(flops / 10.**9, precision)) + ' ' + units - elif units == 'MFLOPs': - return str(round(flops / 10.**6, precision)) + ' ' + units - elif units == 'KFLOPs': - return str(round(flops / 10.**3, precision)) + ' ' + units - else: - return str(flops) + ' FLOPs' - - -def params_to_string(num_params, units=None, precision=2): - """Convert parameter number into a string. - - Args: - num_params (float): Parameter number to be converted. - units (str | None): Converted FLOPs units. Options are None, 'M', - 'K' and ''. If set to None, it will automatically choose the most - suitable unit for Parameter number. Default: None. - precision (int): Digit number after the decimal point. Default: 2. - - Returns: - str: The converted parameter number with units. - - Examples: - >>> params_to_string(1e9) - '1000.0 M' - >>> params_to_string(2e5) - '200.0 k' - >>> params_to_string(3e-9) - '3e-09' - """ - if units is None: - if num_params // 10**6 > 0: - return str(round(num_params / 10**6, precision)) + ' M' - elif num_params // 10**3: - return str(round(num_params / 10**3, precision)) + ' k' - else: - return str(num_params) - else: - if units == 'M': - return str(round(num_params / 10.**6, precision)) + ' ' + units - elif units == 'K': - return str(round(num_params / 10.**3, precision)) + ' ' + units - else: - return str(num_params) - - -def print_model_with_flops(model, - total_flops, - total_params, - units='GFLOPs', - precision=3, - ost=sys.stdout, - flush=False): - """Print a model with FLOPs for each layer. - - Args: - model (nn.Module): The model to be printed. - total_flops (float): Total FLOPs of the model. - total_params (float): Total parameter counts of the model. - units (str | None): Converted FLOPs units. Default: 'GFLOPs'. - precision (int): Digit number after the decimal point. Default: 3. - ost (stream): same as `file` param in :func:`print`. - Default: sys.stdout. - flush (bool): same as that in :func:`print`. Default: False. - - Example: - >>> class ExampleModel(nn.Module): - - >>> def __init__(self): - >>> super().__init__() - >>> self.conv1 = nn.Conv2d(3, 8, 3) - >>> self.conv2 = nn.Conv2d(8, 256, 3) - >>> self.conv3 = nn.Conv2d(256, 8, 3) - >>> self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) - >>> self.flatten = nn.Flatten() - >>> self.fc = nn.Linear(8, 1) - - >>> def forward(self, x): - >>> x = self.conv1(x) - >>> x = self.conv2(x) - >>> x = self.conv3(x) - >>> x = self.avg_pool(x) - >>> x = self.flatten(x) - >>> x = self.fc(x) - >>> return x - - >>> model = ExampleModel() - >>> x = (3, 16, 16) - to print the complexity information state for each layer, you can use - >>> get_model_complexity_info(model, x) - or directly use - >>> print_model_with_flops(model, 4579784.0, 37361) - ExampleModel( - 0.037 M, 100.000% Params, 0.005 GFLOPs, 100.000% FLOPs, - (conv1): Conv2d(0.0 M, 0.600% Params, 0.0 GFLOPs, 0.959% FLOPs, 3, 8, kernel_size=(3, 3), stride=(1, 1)) # noqa: E501 - (conv2): Conv2d(0.019 M, 50.020% Params, 0.003 GFLOPs, 58.760% FLOPs, 8, 256, kernel_size=(3, 3), stride=(1, 1)) - (conv3): Conv2d(0.018 M, 49.356% Params, 0.002 GFLOPs, 40.264% FLOPs, 256, 8, kernel_size=(3, 3), stride=(1, 1)) - (avg_pool): AdaptiveAvgPool2d(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.017% FLOPs, output_size=(1, 1)) - (flatten): Flatten(0.0 M, 0.000% Params, 0.0 GFLOPs, 0.000% FLOPs, ) - (fc): Linear(0.0 M, 0.024% Params, 0.0 GFLOPs, 0.000% FLOPs, in_features=8, out_features=1, bias=True) - ) - """ - - def accumulate_params(self): - if is_supported_instance(self): - return self.__params__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_params() - return sum - - def accumulate_flops(self): - if is_supported_instance(self): - return self.__flops__ / model.__batch_counter__ - else: - sum = 0 - for m in self.children(): - sum += m.accumulate_flops() - return sum - - def flops_repr(self): - accumulated_num_params = self.accumulate_params() - accumulated_flops_cost = self.accumulate_flops() - return ', '.join([ - params_to_string( - accumulated_num_params, units='M', precision=precision), - '{:.3%} Params'.format(accumulated_num_params / total_params), - flops_to_string( - accumulated_flops_cost, units=units, precision=precision), - '{:.3%} FLOPs'.format(accumulated_flops_cost / total_flops), - self.original_extra_repr() - ]) - - def add_extra_repr(m): - m.accumulate_flops = accumulate_flops.__get__(m) - m.accumulate_params = accumulate_params.__get__(m) - flops_extra_repr = flops_repr.__get__(m) - if m.extra_repr != flops_extra_repr: - m.original_extra_repr = m.extra_repr - m.extra_repr = flops_extra_repr - assert m.extra_repr != m.original_extra_repr - - def del_extra_repr(m): - if hasattr(m, 'original_extra_repr'): - m.extra_repr = m.original_extra_repr - del m.original_extra_repr - if hasattr(m, 'accumulate_flops'): - del m.accumulate_flops - - model.apply(add_extra_repr) - print(model, file=ost, flush=flush) - model.apply(del_extra_repr) - - -def get_model_parameters_number(model): - """Calculate parameter number of a model. - - Args: - model (nn.module): The model for parameter number calculation. - - Returns: - float: Parameter number of the model. - """ - num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - return num_params - - -def add_flops_counting_methods(net_main_module): - # adding additional methods to the existing module object, - # this is done this way so that each function has access to self object - net_main_module.start_flops_count = start_flops_count.__get__( - net_main_module) - net_main_module.stop_flops_count = stop_flops_count.__get__( - net_main_module) - net_main_module.reset_flops_count = reset_flops_count.__get__( - net_main_module) - net_main_module.compute_average_flops_cost = compute_average_flops_cost.__get__( # noqa: E501 - net_main_module) - - net_main_module.reset_flops_count() - - return net_main_module - - -def compute_average_flops_cost(self): - """Compute average FLOPs cost. - - A method to compute average FLOPs cost, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - - Returns: - float: Current mean flops consumption per image. - """ - batches_count = self.__batch_counter__ - flops_sum = 0 - for module in self.modules(): - if is_supported_instance(module): - flops_sum += module.__flops__ - params_sum = get_model_parameters_number(self) - return flops_sum / batches_count, params_sum - - -def start_flops_count(self): - """Activate the computation of mean flops consumption per image. - - A method to activate the computation of mean flops consumption per image. - which will be available after ``add_flops_counting_methods()`` is called on - a desired net object. It should be called before running the network. - """ - add_batch_counter_hook_function(self) - - def add_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - return - - else: - handle = module.register_forward_hook( - get_modules_mapping()[type(module)]) - - module.__flops_handle__ = handle - - self.apply(partial(add_flops_counter_hook_function)) - - -def stop_flops_count(self): - """Stop computing the mean flops consumption per image. - - A method to stop computing the mean flops consumption per image, which will - be available after ``add_flops_counting_methods()`` is called on a desired - net object. It can be called to pause the computation whenever. - """ - remove_batch_counter_hook_function(self) - self.apply(remove_flops_counter_hook_function) - - -def reset_flops_count(self): - """Reset statistics computed so far. - - A method to Reset computed statistics, which will be available after - `add_flops_counting_methods()` is called on a desired net object. - """ - add_batch_counter_variables_or_reset(self) - self.apply(add_flops_counter_variable_or_reset) - - -# ---- Internal functions -def empty_flops_counter_hook(module, input, output): - module.__flops__ += 0 - - -def upsample_flops_counter_hook(module, input, output): - output_size = output[0] - batch_size = output_size.shape[0] - output_elements_count = batch_size - for val in output_size.shape[1:]: - output_elements_count *= val - module.__flops__ += int(output_elements_count) - - -def relu_flops_counter_hook(module, input, output): - active_elements_count = output.numel() - module.__flops__ += int(active_elements_count) - - -def linear_flops_counter_hook(module, input, output): - input = input[0] - output_last_dim = output.shape[ - -1] # pytorch checks dimensions, so here we don't care much - module.__flops__ += int(np.prod(input.shape) * output_last_dim) - - -def pool_flops_counter_hook(module, input, output): - input = input[0] - module.__flops__ += int(np.prod(input.shape)) - - -def norm_flops_counter_hook(module, input, output): - input = input[0] - - batch_flops = np.prod(input.shape) - if (getattr(module, 'affine', False) - or getattr(module, 'elementwise_affine', False)): - batch_flops *= 2 - module.__flops__ += int(batch_flops) - - -def deconv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - input_height, input_width = input.shape[2:] - - kernel_height, kernel_width = conv_module.kernel_size - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = ( - kernel_height * kernel_width * in_channels * filters_per_channel) - - active_elements_count = batch_size * input_height * input_width - overall_conv_flops = conv_per_position_flops * active_elements_count - bias_flops = 0 - if conv_module.bias is not None: - output_height, output_width = output.shape[2:] - bias_flops = out_channels * batch_size * output_height * output_height - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def conv_flops_counter_hook(conv_module, input, output): - # Can have multiple inputs, getting the first one - input = input[0] - - batch_size = input.shape[0] - output_dims = list(output.shape[2:]) - - kernel_dims = list(conv_module.kernel_size) - in_channels = conv_module.in_channels - out_channels = conv_module.out_channels - groups = conv_module.groups - - filters_per_channel = out_channels // groups - conv_per_position_flops = int( - np.prod(kernel_dims)) * in_channels * filters_per_channel - - active_elements_count = batch_size * int(np.prod(output_dims)) - - overall_conv_flops = conv_per_position_flops * active_elements_count - - bias_flops = 0 - - if conv_module.bias is not None: - - bias_flops = out_channels * active_elements_count - - overall_flops = overall_conv_flops + bias_flops - - conv_module.__flops__ += int(overall_flops) - - -def batch_counter_hook(module, input, output): - batch_size = 1 - if len(input) > 0: - # Can have multiple inputs, getting the first one - input = input[0] - batch_size = len(input) - else: - pass - print('Warning! No positional inputs found for a module, ' - 'assuming batch size is 1.') - module.__batch_counter__ += batch_size - - -def add_batch_counter_variables_or_reset(module): - - module.__batch_counter__ = 0 - - -def add_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - return - - handle = module.register_forward_hook(batch_counter_hook) - module.__batch_counter_handle__ = handle - - -def remove_batch_counter_hook_function(module): - if hasattr(module, '__batch_counter_handle__'): - module.__batch_counter_handle__.remove() - del module.__batch_counter_handle__ - - -def add_flops_counter_variable_or_reset(module): - if is_supported_instance(module): - if hasattr(module, '__flops__') or hasattr(module, '__params__'): - print('Warning: variables __flops__ or __params__ are already ' - 'defined for the module' + type(module).__name__ + - ' ptflops can affect your code!') - module.__flops__ = 0 - module.__params__ = get_model_parameters_number(module) - - -def is_supported_instance(module): - if type(module) in get_modules_mapping(): - return True - return False - - -def remove_flops_counter_hook_function(module): - if is_supported_instance(module): - if hasattr(module, '__flops_handle__'): - module.__flops_handle__.remove() - del module.__flops_handle__ - - -def get_modules_mapping(): - return { - # convolutions - nn.Conv1d: conv_flops_counter_hook, - nn.Conv2d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv2d: conv_flops_counter_hook, - nn.Conv3d: conv_flops_counter_hook, - mmcv.cnn.bricks.Conv3d: conv_flops_counter_hook, - # activations - nn.ReLU: relu_flops_counter_hook, - nn.PReLU: relu_flops_counter_hook, - nn.ELU: relu_flops_counter_hook, - nn.LeakyReLU: relu_flops_counter_hook, - nn.ReLU6: relu_flops_counter_hook, - # poolings - nn.MaxPool1d: pool_flops_counter_hook, - nn.AvgPool1d: pool_flops_counter_hook, - nn.AvgPool2d: pool_flops_counter_hook, - nn.MaxPool2d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool2d: pool_flops_counter_hook, - nn.MaxPool3d: pool_flops_counter_hook, - mmcv.cnn.bricks.MaxPool3d: pool_flops_counter_hook, - nn.AvgPool3d: pool_flops_counter_hook, - nn.AdaptiveMaxPool1d: pool_flops_counter_hook, - nn.AdaptiveAvgPool1d: pool_flops_counter_hook, - nn.AdaptiveMaxPool2d: pool_flops_counter_hook, - nn.AdaptiveAvgPool2d: pool_flops_counter_hook, - nn.AdaptiveMaxPool3d: pool_flops_counter_hook, - nn.AdaptiveAvgPool3d: pool_flops_counter_hook, - # normalizations - nn.BatchNorm1d: norm_flops_counter_hook, - nn.BatchNorm2d: norm_flops_counter_hook, - nn.BatchNorm3d: norm_flops_counter_hook, - nn.GroupNorm: norm_flops_counter_hook, - nn.InstanceNorm1d: norm_flops_counter_hook, - nn.InstanceNorm2d: norm_flops_counter_hook, - nn.InstanceNorm3d: norm_flops_counter_hook, - nn.LayerNorm: norm_flops_counter_hook, - # FC - nn.Linear: linear_flops_counter_hook, - mmcv.cnn.bricks.Linear: linear_flops_counter_hook, - # Upscale - nn.Upsample: upsample_flops_counter_hook, - # Deconvolution - nn.ConvTranspose2d: deconv_flops_counter_hook, - mmcv.cnn.bricks.ConvTranspose2d: deconv_flops_counter_hook, - } diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/fuse_conv_bn.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/fuse_conv_bn.py deleted file mode 100755 index cb7076f80..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/fuse_conv_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -def _fuse_conv_bn(conv, bn): - """Fuse conv and bn into one module. - - Args: - conv (nn.Module): Conv to be fused. - bn (nn.Module): BN to be fused. - - Returns: - nn.Module: Fused module. - """ - conv_w = conv.weight - conv_b = conv.bias if conv.bias is not None else torch.zeros_like( - bn.running_mean) - - factor = bn.weight / torch.sqrt(bn.running_var + bn.eps) - conv.weight = nn.Parameter(conv_w * - factor.reshape([conv.out_channels, 1, 1, 1])) - conv.bias = nn.Parameter((conv_b - bn.running_mean) * factor + bn.bias) - return conv - - -def fuse_conv_bn(module): - """Recursively fuse conv and bn in a module. - - During inference, the functionary of batch norm layers is turned off - but only the mean and var alone channels are used, which exposes the - chance to fuse it with the preceding conv layers to save computations and - simplify network structures. - - Args: - module (nn.Module): Module to be fused. - - Returns: - nn.Module: Fused module. - """ - last_conv = None - last_conv_name = None - - for name, child in module.named_children(): - if isinstance(child, - (nn.modules.batchnorm._BatchNorm, nn.SyncBatchNorm)): - if last_conv is None: # only fuse BN that is after Conv - continue - fused_conv = _fuse_conv_bn(last_conv, child) - module._modules[last_conv_name] = fused_conv - # To reduce changes, set BN as Identity instead of deleting it. - module._modules[name] = nn.Identity() - last_conv = None - elif isinstance(child, nn.Conv2d): - last_conv = child - last_conv_name = name - else: - fuse_conv_bn(child) - return module diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/sync_bn.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/sync_bn.py deleted file mode 100755 index 8a79ff4a4..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/sync_bn.py +++ /dev/null @@ -1,59 +0,0 @@ -import torch - -import mmcv - - -class _BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): - """A general BatchNorm layer without input dimension check. - - Reproduced from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc - is `_check_input_dim` that is designed for tensor sanity checks. - The check has been bypassed in this class for the convenience of converting - SyncBatchNorm. - """ - - def _check_input_dim(self, input): - return - - -def revert_sync_batchnorm(module): - """Helper function to convert all `SyncBatchNorm` (SyncBN) and - `mmcv.ops.sync_bn.SyncBatchNorm`(MMSyncBN) layers in the model to - `BatchNormXd` layers. - - Adapted from @kapily's work: - (https://github.com/pytorch/pytorch/issues/41081#issuecomment-783961547) - - Args: - module (nn.Module): The module containing `SyncBatchNorm` layers. - - Returns: - module_output: The converted module with `BatchNormXd` layers. - """ - module_output = module - module_checklist = [torch.nn.modules.batchnorm.SyncBatchNorm] - if hasattr(mmcv, 'ops'): - module_checklist.append(mmcv.ops.SyncBatchNorm) - if isinstance(module, tuple(module_checklist)): - module_output = _BatchNormXd(module.num_features, module.eps, - module.momentum, module.affine, - module.track_running_stats) - if module.affine: - # no_grad() may not be needed here but - # just to be consistent with `convert_sync_batchnorm()` - with torch.no_grad(): - module_output.weight = module.weight - module_output.bias = module.bias - module_output.running_mean = module.running_mean - module_output.running_var = module.running_var - module_output.num_batches_tracked = module.num_batches_tracked - module_output.training = module.training - # qconfig exists in quantized models - if hasattr(module, 'qconfig'): - module_output.qconfig = module.qconfig - for name, child in module.named_children(): - module_output.add_module(name, revert_sync_batchnorm(child)) - del module - return module_output diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/weight_init.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/weight_init.py deleted file mode 100755 index e1ac999e2..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/utils/weight_init.py +++ /dev/null @@ -1,684 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import warnings - -import numpy as np -import torch -import torch.nn as nn -from torch import Tensor - -from mmcv.utils import Registry, build_from_cfg, get_logger, print_log - -INITIALIZERS = Registry('initializer') - - -def update_init_info(module, init_info): - """Update the `_params_init_info` in the module if the value of parameters - are changed. - - Args: - module (obj:`nn.Module`): The module of PyTorch with a user-defined - attribute `_params_init_info` which records the initialization - information. - init_info (str): The string that describes the initialization. - """ - assert hasattr( - module, - '_params_init_info'), f'Can not find `_params_init_info` in {module}' - for name, param in module.named_parameters(): - - assert param in module._params_init_info, ( - f'Find a new :obj:`Parameter` ' - f'named `{name}` during executing the ' - f'`init_weights` of ' - f'`{module.__class__.__name__}`. ' - f'Please do not add or ' - f'replace parameters during executing ' - f'the `init_weights`. ') - - # The parameter has been changed during executing the - # `init_weights` of module - mean_value = param.data.mean() - if module._params_init_info[param]['tmp_mean_value'] != mean_value: - module._params_init_info[param]['init_info'] = init_info - module._params_init_info[param]['tmp_mean_value'] = mean_value - - -def constant_init(module, val, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.constant_(module.weight, val) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def xavier_init(module, gain=1, bias=0, distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.xavier_uniform_(module.weight, gain=gain) - else: - nn.init.xavier_normal_(module.weight, gain=gain) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def normal_init(module, mean=0, std=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.normal_(module.weight, mean, std) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def trunc_normal_init(module: nn.Module, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - bias: float = 0) -> None: - if hasattr(module, 'weight') and module.weight is not None: - trunc_normal_(module.weight, mean, std, a, b) # type: ignore - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) # type: ignore - - -def uniform_init(module, a=0, b=1, bias=0): - if hasattr(module, 'weight') and module.weight is not None: - nn.init.uniform_(module.weight, a, b) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def kaiming_init(module, - a=0, - mode='fan_out', - nonlinearity='relu', - bias=0, - distribution='normal'): - assert distribution in ['uniform', 'normal'] - if hasattr(module, 'weight') and module.weight is not None: - if distribution == 'uniform': - nn.init.kaiming_uniform_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - else: - nn.init.kaiming_normal_( - module.weight, a=a, mode=mode, nonlinearity=nonlinearity) - if hasattr(module, 'bias') and module.bias is not None: - nn.init.constant_(module.bias, bias) - - -def caffe2_xavier_init(module, bias=0): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - kaiming_init( - module, - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - bias=bias, - distribution='uniform') - - -def bias_init_with_prob(prior_prob): - """initialize conv/fc bias value according to a given probability value.""" - bias_init = float(-np.log((1 - prior_prob) / prior_prob)) - return bias_init - - -def _get_bases_name(m): - return [b.__name__ for b in m.__class__.__bases__] - - -class BaseInit(object): - - def __init__(self, *, bias=0, bias_prob=None, layer=None): - self.wholemodule = False - if not isinstance(bias, (int, float)): - raise TypeError(f'bias must be a number, but got a {type(bias)}') - - if bias_prob is not None: - if not isinstance(bias_prob, float): - raise TypeError(f'bias_prob type must be float, \ - but got {type(bias_prob)}') - - if layer is not None: - if not isinstance(layer, (str, list)): - raise TypeError(f'layer must be a str or a list of str, \ - but got a {type(layer)}') - else: - layer = [] - - if bias_prob is not None: - self.bias = bias_init_with_prob(bias_prob) - else: - self.bias = bias - self.layer = [layer] if isinstance(layer, str) else layer - - def _get_init_info(self): - info = f'{self.__class__.__name__}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Constant') -class ConstantInit(BaseInit): - """Initialize module parameters with constant values. - - Args: - val (int | float): the value to fill the weights in the module with - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, val, **kwargs): - super().__init__(**kwargs) - self.val = val - - def __call__(self, module): - - def init(m): - if self.wholemodule: - constant_init(m, self.val, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - constant_init(m, self.val, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: val={self.val}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Xavier') -class XavierInit(BaseInit): - r"""Initialize module parameters with values according to the method - described in `Understanding the difficulty of training deep feedforward - neural networks - Glorot, X. & Bengio, Y. (2010). - `_ - - Args: - gain (int | float): an optional scaling factor. Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` - or ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, gain=1, distribution='normal', **kwargs): - super().__init__(**kwargs) - self.gain = gain - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - xavier_init(m, self.gain, self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - xavier_init(m, self.gain, self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: gain={self.gain}, ' \ - f'distribution={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Normal') -class NormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`. - - Args: - mean (int | float):the mean of the normal distribution. Defaults to 0. - std (int | float): the standard deviation of the normal distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, mean=0, std=1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.std = std - - def __call__(self, module): - - def init(m): - if self.wholemodule: - normal_init(m, self.mean, self.std, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - normal_init(m, self.mean, self.std, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: mean={self.mean},' \ - f' std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='TruncNormal') -class TruncNormalInit(BaseInit): - r"""Initialize module parameters with the values drawn from the normal - distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` with values - outside :math:`[a, b]`. - - Args: - mean (float): the mean of the normal distribution. Defaults to 0. - std (float): the standard deviation of the normal distribution. - Defaults to 1. - a (float): The minimum cutoff value. - b ( float): The maximum cutoff value. - bias (float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - - """ - - def __init__(self, - mean: float = 0, - std: float = 1, - a: float = -2, - b: float = 2, - **kwargs) -> None: - super().__init__(**kwargs) - self.mean = mean - self.std = std - self.a = a - self.b = b - - def __call__(self, module: nn.Module) -> None: - - def init(m): - if self.wholemodule: - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - trunc_normal_init(m, self.mean, self.std, self.a, self.b, - self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, b={self.b},' \ - f' mean={self.mean}, std={self.std}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Uniform') -class UniformInit(BaseInit): - r"""Initialize module parameters with values drawn from the uniform - distribution :math:`\mathcal{U}(a, b)`. - - Args: - a (int | float): the lower bound of the uniform distribution. - Defaults to 0. - b (int | float): the upper bound of the uniform distribution. - Defaults to 1. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, a=0, b=1, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def __call__(self, module): - - def init(m): - if self.wholemodule: - uniform_init(m, self.a, self.b, self.bias) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - uniform_init(m, self.a, self.b, self.bias) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a},' \ - f' b={self.b}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Kaiming') -class KaimingInit(BaseInit): - r"""Initialize module parameters with the values according to the method - described in `Delving deep into rectifiers: Surpassing human-level - performance on ImageNet classification - He, K. et al. (2015). - `_ - - Args: - a (int | float): the negative slope of the rectifier used after this - layer (only used with ``'leaky_relu'``). Defaults to 0. - mode (str): either ``'fan_in'`` or ``'fan_out'``. Choosing - ``'fan_in'`` preserves the magnitude of the variance of the weights - in the forward pass. Choosing ``'fan_out'`` preserves the - magnitudes in the backwards pass. Defaults to ``'fan_out'``. - nonlinearity (str): the non-linear function (`nn.functional` name), - recommended to use only with ``'relu'`` or ``'leaky_relu'`` . - Defaults to 'relu'. - bias (int | float): the value to fill the bias. Defaults to 0. - bias_prob (float, optional): the probability for bias initialization. - Defaults to None. - distribution (str): distribution either be ``'normal'`` or - ``'uniform'``. Defaults to ``'normal'``. - layer (str | list[str], optional): the layer will be initialized. - Defaults to None. - """ - - def __init__(self, - a=0, - mode='fan_out', - nonlinearity='relu', - distribution='normal', - **kwargs): - super().__init__(**kwargs) - self.a = a - self.mode = mode - self.nonlinearity = nonlinearity - self.distribution = distribution - - def __call__(self, module): - - def init(m): - if self.wholemodule: - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - else: - layername = m.__class__.__name__ - basesname = _get_bases_name(m) - if len(set(self.layer) & set([layername] + basesname)): - kaiming_init(m, self.a, self.mode, self.nonlinearity, - self.bias, self.distribution) - - module.apply(init) - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: a={self.a}, mode={self.mode}, ' \ - f'nonlinearity={self.nonlinearity}, ' \ - f'distribution ={self.distribution}, bias={self.bias}' - return info - - -@INITIALIZERS.register_module(name='Caffe2Xavier') -class Caffe2XavierInit(KaimingInit): - # `XavierFill` in Caffe2 corresponds to `kaiming_uniform_` in PyTorch - # Acknowledgment to FAIR's internal code - def __init__(self, **kwargs): - super().__init__( - a=1, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='uniform', - **kwargs) - - def __call__(self, module): - super().__call__(module) - - -@INITIALIZERS.register_module(name='Pretrained') -class PretrainedInit(object): - """Initialize module by loading a pretrained model. - - Args: - checkpoint (str): the checkpoint file of the pretrained model should - be load. - prefix (str, optional): the prefix of a sub-module in the pretrained - model. it is for loading a part of the pretrained model to - initialize. For example, if we would like to only load the - backbone of a detector model, we can set ``prefix='backbone.'``. - Defaults to None. - map_location (str): map tensors into proper locations. - """ - - def __init__(self, checkpoint, prefix=None, map_location=None): - self.checkpoint = checkpoint - self.prefix = prefix - self.map_location = map_location - - def __call__(self, module): - from mmcv.runner import (_load_checkpoint_with_prefix, load_checkpoint, - load_state_dict) - logger = get_logger('mmcv') - if self.prefix is None: - print_log(f'load model from: {self.checkpoint}', logger=logger) - load_checkpoint( - module, - self.checkpoint, - map_location=self.map_location, - strict=False, - logger=logger) - else: - print_log( - f'load {self.prefix} in model from: {self.checkpoint}', - logger=logger) - state_dict = _load_checkpoint_with_prefix( - self.prefix, self.checkpoint, map_location=self.map_location) - load_state_dict(module, state_dict, strict=False, logger=logger) - - if hasattr(module, '_params_init_info'): - update_init_info(module, init_info=self._get_init_info()) - - def _get_init_info(self): - info = f'{self.__class__.__name__}: load from {self.checkpoint}' - return info - - -def _initialize(module, cfg, wholemodule=False): - func = build_from_cfg(cfg, INITIALIZERS) - # wholemodule flag is for override mode, there is no layer key in override - # and initializer will give init values for the whole module with the name - # in override. - func.wholemodule = wholemodule - func(module) - - -def _initialize_override(module, override, cfg): - if not isinstance(override, (dict, list)): - raise TypeError(f'override must be a dict or a list of dict, \ - but got {type(override)}') - - override = [override] if isinstance(override, dict) else override - - for override_ in override: - - cp_override = copy.deepcopy(override_) - name = cp_override.pop('name', None) - if name is None: - raise ValueError('`override` must contain the key "name",' - f'but got {cp_override}') - # if override only has name key, it means use args in init_cfg - if not cp_override: - cp_override.update(cfg) - # if override has name key and other args except type key, it will - # raise error - elif 'type' not in cp_override.keys(): - raise ValueError( - f'`override` need "type" key, but got {cp_override}') - - if hasattr(module, name): - _initialize(getattr(module, name), cp_override, wholemodule=True) - else: - raise RuntimeError(f'module did not have attribute {name}, ' - f'but init_cfg is {cp_override}.') - - -def initialize(module, init_cfg): - """Initialize a module. - - Args: - module (``torch.nn.Module``): the module will be initialized. - init_cfg (dict | list[dict]): initialization configuration dict to - define initializer. OpenMMLab has implemented 6 initializers - including ``Constant``, ``Xavier``, ``Normal``, ``Uniform``, - ``Kaiming``, and ``Pretrained``. - Example: - >>> module = nn.Linear(2, 3, bias=True) - >>> init_cfg = dict(type='Constant', layer='Linear', val =1 , bias =2) - >>> initialize(module, init_cfg) - - >>> module = nn.Sequential(nn.Conv1d(3, 1, 3), nn.Linear(1,2)) - >>> # define key ``'layer'`` for initializing layer with different - >>> # configuration - >>> init_cfg = [dict(type='Constant', layer='Conv1d', val=1), - dict(type='Constant', layer='Linear', val=2)] - >>> initialize(module, init_cfg) - - >>> # define key``'override'`` to initialize some specific part in - >>> # module - >>> class FooNet(nn.Module): - >>> def __init__(self): - >>> super().__init__() - >>> self.feat = nn.Conv2d(3, 16, 3) - >>> self.reg = nn.Conv2d(16, 10, 3) - >>> self.cls = nn.Conv2d(16, 5, 3) - >>> model = FooNet() - >>> init_cfg = dict(type='Constant', val=1, bias=2, layer='Conv2d', - >>> override=dict(type='Constant', name='reg', val=3, bias=4)) - >>> initialize(model, init_cfg) - - >>> model = ResNet(depth=50) - >>> # Initialize weights with the pretrained model. - >>> init_cfg = dict(type='Pretrained', - checkpoint='torchvision://resnet50') - >>> initialize(model, init_cfg) - - >>> # Initialize weights of a sub-module with the specific part of - >>> # a pretrained model by using "prefix". - >>> url = 'http://download.openmmlab.com/mmdetection/v2.0/retinanet/'\ - >>> 'retinanet_r50_fpn_1x_coco/'\ - >>> 'retinanet_r50_fpn_1x_coco_20200130-c2398f9e.pth' - >>> init_cfg = dict(type='Pretrained', - checkpoint=url, prefix='backbone.') - """ - if not isinstance(init_cfg, (dict, list)): - raise TypeError(f'init_cfg must be a dict or a list of dict, \ - but got {type(init_cfg)}') - - if isinstance(init_cfg, dict): - init_cfg = [init_cfg] - - for cfg in init_cfg: - # should deeply copy the original config because cfg may be used by - # other modules, e.g., one init_cfg shared by multiple bottleneck - # blocks, the expected cfg will be changed after pop and will change - # the initialization behavior of other modules - cp_cfg = copy.deepcopy(cfg) - override = cp_cfg.pop('override', None) - _initialize(module, cp_cfg) - - if override is not None: - cp_cfg.pop('layer', None) - _initialize_override(module, override, cp_cfg) - else: - # All attributes in module have same initialization. - pass - - -def _no_grad_trunc_normal_(tensor: Tensor, mean: float, std: float, a: float, - b: float) -> Tensor: - # Method based on - # https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf - # Modified from - # https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - def norm_cdf(x): - # Computes standard normal cumulative distribution function - return (1. + math.erf(x / math.sqrt(2.))) / 2. - - if (mean < a - 2 * std) or (mean > b + 2 * std): - warnings.warn( - 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' - 'The distribution of values may be incorrect.', - stacklevel=2) - - with torch.no_grad(): - # Values are generated by using a truncated uniform distribution and - # then using the inverse CDF for the normal distribution. - # Get upper and lower cdf values - lower = norm_cdf((a - mean) / std) - upper = norm_cdf((b - mean) / std) - - # Uniformly fill tensor with values from [lower, upper], then translate - # to [2lower-1, 2upper-1]. - tensor.uniform_(2 * lower - 1, 2 * upper - 1) - - # Use inverse cdf transform for normal distribution to get truncated - # standard normal - tensor.erfinv_() - - # Transform to proper mean, std - tensor.mul_(std * math.sqrt(2.)) - tensor.add_(mean) - - # Clamp to ensure it's in the proper range - tensor.clamp_(min=a, max=b) - return tensor - - -def trunc_normal_(tensor: Tensor, - mean: float = 0., - std: float = 1., - a: float = -2., - b: float = 2.) -> Tensor: - r"""Fills the input Tensor with values drawn from a truncated - normal distribution. The values are effectively drawn from the - normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` - with values outside :math:`[a, b]` redrawn until they are within - the bounds. The method used for generating the random values works - best when :math:`a \leq \text{mean} \leq b`. - - Modified from - https://github.com/pytorch/pytorch/blob/master/torch/nn/init.py - - Args: - tensor (``torch.Tensor``): an n-dimensional `torch.Tensor`. - mean (float): the mean of the normal distribution. - std (float): the standard deviation of the normal distribution. - a (float): the minimum cutoff value. - b (float): the maximum cutoff value. - """ - return _no_grad_trunc_normal_(tensor, mean, std, a, b) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/vgg.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/vgg.py deleted file mode 100755 index 8778b6495..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/cnn/vgg.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.nn as nn - -from .utils import constant_init, kaiming_init, normal_init - - -def conv3x3(in_planes, out_planes, dilation=1): - """3x3 convolution with padding.""" - return nn.Conv2d( - in_planes, - out_planes, - kernel_size=3, - padding=dilation, - dilation=dilation) - - -def make_vgg_layer(inplanes, - planes, - num_blocks, - dilation=1, - with_bn=False, - ceil_mode=False): - layers = [] - for _ in range(num_blocks): - layers.append(conv3x3(inplanes, planes, dilation)) - if with_bn: - layers.append(nn.BatchNorm2d(planes)) - layers.append(nn.ReLU(inplace=True)) - inplanes = planes - layers.append(nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=ceil_mode)) - - return layers - - -class VGG(nn.Module): - """VGG backbone. - - Args: - depth (int): Depth of vgg, from {11, 13, 16, 19}. - with_bn (bool): Use BatchNorm or not. - num_classes (int): number of classes for classification. - num_stages (int): VGG stages, normally 5. - dilations (Sequence[int]): Dilation of each stage. - out_indices (Sequence[int]): Output from which stages. - frozen_stages (int): Stages to be frozen (all param fixed). -1 means - not freezing any parameters. - bn_eval (bool): Whether to set BN layers as eval mode, namely, freeze - running stats (mean and var). - bn_frozen (bool): Whether to freeze weight and bias of BN layers. - """ - - arch_settings = { - 11: (1, 1, 2, 2, 2), - 13: (2, 2, 2, 2, 2), - 16: (2, 2, 3, 3, 3), - 19: (2, 2, 4, 4, 4) - } - - def __init__(self, - depth, - with_bn=False, - num_classes=-1, - num_stages=5, - dilations=(1, 1, 1, 1, 1), - out_indices=(0, 1, 2, 3, 4), - frozen_stages=-1, - bn_eval=True, - bn_frozen=False, - ceil_mode=False, - with_last_pool=True): - super(VGG, self).__init__() - if depth not in self.arch_settings: - raise KeyError(f'invalid depth {depth} for vgg') - assert num_stages >= 1 and num_stages <= 5 - stage_blocks = self.arch_settings[depth] - self.stage_blocks = stage_blocks[:num_stages] - assert len(dilations) == num_stages - assert max(out_indices) <= num_stages - - self.num_classes = num_classes - self.out_indices = out_indices - self.frozen_stages = frozen_stages - self.bn_eval = bn_eval - self.bn_frozen = bn_frozen - - self.inplanes = 3 - start_idx = 0 - vgg_layers = [] - self.range_sub_modules = [] - for i, num_blocks in enumerate(self.stage_blocks): - num_modules = num_blocks * (2 + with_bn) + 1 - end_idx = start_idx + num_modules - dilation = dilations[i] - planes = 64 * 2**i if i < 4 else 512 - vgg_layer = make_vgg_layer( - self.inplanes, - planes, - num_blocks, - dilation=dilation, - with_bn=with_bn, - ceil_mode=ceil_mode) - vgg_layers.extend(vgg_layer) - self.inplanes = planes - self.range_sub_modules.append([start_idx, end_idx]) - start_idx = end_idx - if not with_last_pool: - vgg_layers.pop(-1) - self.range_sub_modules[-1][1] -= 1 - self.module_name = 'features' - self.add_module(self.module_name, nn.Sequential(*vgg_layers)) - - if self.num_classes > 0: - self.classifier = nn.Sequential( - nn.Linear(512 * 7 * 7, 4096), - nn.ReLU(True), - nn.Dropout(), - nn.Linear(4096, 4096), - nn.ReLU(True), - nn.Dropout(), - nn.Linear(4096, num_classes), - ) - - def init_weights(self, pretrained=None): - if isinstance(pretrained, str): - logger = logging.getLogger() - from ..runner import load_checkpoint - load_checkpoint(self, pretrained, strict=False, logger=logger) - elif pretrained is None: - for m in self.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m) - elif isinstance(m, nn.BatchNorm2d): - constant_init(m, 1) - elif isinstance(m, nn.Linear): - normal_init(m, std=0.01) - else: - raise TypeError('pretrained must be a str or None') - - def forward(self, x): - outs = [] - vgg_layers = getattr(self, self.module_name) - for i in range(len(self.stage_blocks)): - for j in range(*self.range_sub_modules[i]): - vgg_layer = vgg_layers[j] - x = vgg_layer(x) - if i in self.out_indices: - outs.append(x) - if self.num_classes > 0: - x = x.view(x.size(0), -1) - x = self.classifier(x) - outs.append(x) - if len(outs) == 1: - return outs[0] - else: - return tuple(outs) - - def train(self, mode=True): - super(VGG, self).train(mode) - if self.bn_eval: - for m in self.modules(): - if isinstance(m, nn.BatchNorm2d): - m.eval() - if self.bn_frozen: - for params in m.parameters(): - params.requires_grad = False - vgg_layers = getattr(self, self.module_name) - if mode and self.frozen_stages >= 0: - for i in range(self.frozen_stages): - for j in range(*self.range_sub_modules[i]): - mod = vgg_layers[j] - mod.eval() - for param in mod.parameters(): - param.requires_grad = False diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/engine/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/engine/__init__.py deleted file mode 100755 index 3193b7f66..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/engine/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .test import (collect_results_cpu, collect_results_gpu, multi_gpu_test, - single_gpu_test) - -__all__ = [ - 'collect_results_cpu', 'collect_results_gpu', 'multi_gpu_test', - 'single_gpu_test' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/engine/test.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/engine/test.py deleted file mode 100755 index f236b1cda..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/engine/test.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile -import time - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, data_loader): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - # Assume result has the same length of batch_size - # refer to https://github.com/open-mmlab/mmcv/issues/985 - batch_size = len(result) - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting - ``gpu_collect=True``, it encodes results to gpu tensors and use gpu - communication for results collection. On cpu mode it saves the results on - different gpus to ``tmpdir`` and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - - Returns: - list: The prediction results. - """ - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - time.sleep(2) # This line can prevent deadlock problem in some cases. - for i, data in enumerate(data_loader): - with torch.no_grad(): - result = model(return_loss=False, **data) - results.extend(result) - - if rank == 0: - batch_size = len(result) - batch_size_all = batch_size * world_size - if batch_size_all + prog_bar.completed > len(dataset): - batch_size_all = len(dataset) - prog_bar.completed - for _ in range(batch_size_all): - prog_bar.update() - - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results under cpu mode. - - On cpu mode, this function will save the results on different gpus to - ``tmpdir`` and collect them by the rank 0 worker. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - tmpdir (str | None): temporal directory for collected results to - store. If set to None, it will create a random temporal directory - for it. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl')) - dist.barrier() - # collect all parts - if rank != 0: - return None - else: - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, f'part_{i}.pkl') - part_result = mmcv.load(part_file) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results under gpu mode. - - On gpu mode, this function will encode results to gpu tensors and use gpu - communication for results collection. - - Args: - result_part (list): Result list containing result parts - to be collected. - size (int): Size of the results, commonly equal to length of - the results. - - Returns: - list: The collected results. - """ - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank == 0: - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()) - # When data is severely insufficient, an empty part_result - # on a certain gpu could makes the overall outputs empty. - if part_result: - part_list.append(part_result) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/__init__.py deleted file mode 100755 index 2051b85f7..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .file_client import BaseStorageBackend, FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler -from .io import dump, load, register_handler -from .parse import dict_from_file, list_from_file - -__all__ = [ - 'BaseStorageBackend', 'FileClient', 'load', 'dump', 'register_handler', - 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', - 'list_from_file', 'dict_from_file' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/file_client.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/file_client.py deleted file mode 100755 index b2d622868..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/file_client.py +++ /dev/null @@ -1,1148 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import os -import os.path as osp -import re -import tempfile -import warnings -from abc import ABCMeta, abstractmethod -from contextlib import contextmanager -from pathlib import Path -from typing import Iterable, Iterator, Optional, Tuple, Union -from urllib.request import urlopen - -import mmcv -from mmcv.utils.misc import has_method -from mmcv.utils.path import is_filepath - - -class BaseStorageBackend(metaclass=ABCMeta): - """Abstract class of storage backends. - - All backends need to implement two apis: ``get()`` and ``get_text()``. - ``get()`` reads the file as a byte stream and ``get_text()`` reads the file - as texts. - """ - - # a flag to indicate whether the backend can create a symlink for a file - _allow_symlink = False - - @property - def name(self): - return self.__class__.__name__ - - @property - def allow_symlink(self): - return self._allow_symlink - - @abstractmethod - def get(self, filepath): - pass - - @abstractmethod - def get_text(self, filepath): - pass - - -class CephBackend(BaseStorageBackend): - """Ceph storage backend (for internal use). - - Args: - path_mapping (dict|None): path mapping dict from local path to Petrel - path. When ``path_mapping={'src': 'dst'}``, ``src`` in ``filepath`` - will be replaced by ``dst``. Default: None. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - """ - - def __init__(self, path_mapping=None): - try: - import ceph - except ImportError: - raise ImportError('Please install ceph to enable CephBackend.') - - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead') - self._client = ceph.S3Client() - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def get(self, filepath): - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class PetrelBackend(BaseStorageBackend): - """Petrel storage backend (for internal use). - - PetrelBackend supports reading and writing data to multiple clusters. - If the file path contains the cluster name, PetrelBackend will read data - from specified cluster or write data to it. Otherwise, PetrelBackend will - access the default cluster. - - Args: - path_mapping (dict, optional): Path mapping dict from local path to - Petrel path. When ``path_mapping={'src': 'dst'}``, ``src`` in - ``filepath`` will be replaced by ``dst``. Default: None. - enable_mc (bool, optional): Whether to enable memcached support. - Default: True. - - Examples: - >>> filepath1 = 's3://path/of/file' - >>> filepath2 = 'cluster-name:s3://path/of/file' - >>> client = PetrelBackend() - >>> client.get(filepath1) # get data from default cluster - >>> client.get(filepath2) # get data from 'cluster-name' cluster - """ - - def __init__(self, - path_mapping: Optional[dict] = None, - enable_mc: bool = True): - try: - from petrel_client import client - except ImportError: - raise ImportError('Please install petrel_client to enable ' - 'PetrelBackend.') - - self._client = client.Client(enable_mc=enable_mc) - assert isinstance(path_mapping, dict) or path_mapping is None - self.path_mapping = path_mapping - - def _map_path(self, filepath: Union[str, Path]) -> str: - """Map ``filepath`` to a string path whose prefix will be replaced by - :attr:`self.path_mapping`. - - Args: - filepath (str): Path to be mapped. - """ - filepath = str(filepath) - if self.path_mapping is not None: - for k, v in self.path_mapping.items(): - filepath = filepath.replace(k, v) - return filepath - - def _format_path(self, filepath: str) -> str: - """Convert a ``filepath`` to standard format of petrel oss. - - If the ``filepath`` is concatenated by ``os.path.join``, in a Windows - environment, the ``filepath`` will be the format of - 's3://bucket_name\\image.jpg'. By invoking :meth:`_format_path`, the - above ``filepath`` will be converted to 's3://bucket_name/image.jpg'. - - Args: - filepath (str): Path to be formatted. - """ - return re.sub(r'\\+', '/', filepath) - - def get(self, filepath: Union[str, Path]) -> memoryview: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - memoryview: A memory view of expected bytes object to avoid - copying. The memoryview object can be converted to bytes by - ``value_buf.tobytes()``. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - value = self._client.Get(filepath) - value_buf = memoryview(value) - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return str(self.get(filepath), encoding=encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Save data to a given ``filepath``. - - Args: - obj (bytes): Data to be saved. - filepath (str or Path): Path to write data. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.put(filepath, obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Save data to a given ``filepath``. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to encode the ``obj``. - Default: 'utf-8'. - """ - self.put(bytes(obj, encoding=encoding), filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - if not has_method(self._client, 'delete'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `delete` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - self._client.delete(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - if not (has_method(self._client, 'contains') - and has_method(self._client, 'isdir')): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` and `isdir` methods, please use a higher' - 'version or dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) or self._client.isdir(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - if not has_method(self._client, 'isdir'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `isdir` method, please use a higher version or dev' - ' branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - if not has_method(self._client, 'contains'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `contains` method, please use a higher version or ' - 'dev branch instead.')) - - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - return self._client.contains(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result after concatenation. - """ - filepath = self._format_path(self._map_path(filepath)) - if filepath.endswith('/'): - filepath = filepath[:-1] - formatted_paths = [filepath] - for path in filepaths: - formatted_paths.append(self._format_path(self._map_path(path))) - return '/'.join(formatted_paths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download a file from ``filepath`` and return a temporary path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str | Path): Download a file from ``filepath``. - - Examples: - >>> client = PetrelBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('s3://path/of/your/file') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one temporary path. - """ - filepath = self._map_path(filepath) - filepath = self._format_path(filepath) - assert self.isfile(filepath) - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - Petrel has no concept of directories but it simulates the directory - hierarchy in the filesystem through public prefixes. In addition, - if the returned path ends with '/', it means the path is a public - prefix which is a logical directory. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - In addition, the returned path of directory will not contains the - suffix '/' which is consistent with other backends. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if not has_method(self._client, 'list'): - raise NotImplementedError( - ('Current version of Petrel Python SDK has not supported ' - 'the `list` method, please use a higher version or dev' - ' branch instead.')) - - dir_path = self._map_path(dir_path) - dir_path = self._format_path(dir_path) - if list_dir and suffix is not None: - raise TypeError( - '`list_dir` should be False when `suffix` is not None') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - # Petrel's simulated directory hierarchy assumes that directory paths - # should end with `/` - if not dir_path.endswith('/'): - dir_path += '/' - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for path in self._client.list(dir_path): - # the `self.isdir` is not used here to determine whether path - # is a directory, because `self.isdir` relies on - # `self._client.list` - if path.endswith('/'): # a directory path - next_dir_path = self.join_path(dir_path, path) - if list_dir: - # get the relative path and exclude the last - # character '/' - rel_dir = next_dir_path[len(root):-1] - yield rel_dir - if recursive: - yield from _list_dir_or_file(next_dir_path, list_dir, - list_file, suffix, - recursive) - else: # a file path - absolute_path = self.join_path(dir_path, path) - rel_path = absolute_path[len(root):] - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class MemcachedBackend(BaseStorageBackend): - """Memcached storage backend. - - Attributes: - server_list_cfg (str): Config file for memcached server list. - client_cfg (str): Config file for memcached client. - sys_path (str | None): Additional path to be appended to `sys.path`. - Default: None. - """ - - def __init__(self, server_list_cfg, client_cfg, sys_path=None): - if sys_path is not None: - import sys - sys.path.append(sys_path) - try: - import mc - except ImportError: - raise ImportError( - 'Please install memcached to enable MemcachedBackend.') - - self.server_list_cfg = server_list_cfg - self.client_cfg = client_cfg - self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, - self.client_cfg) - # mc.pyvector servers as a point which points to a memory cache - self._mc_buffer = mc.pyvector() - - def get(self, filepath): - filepath = str(filepath) - import mc - self._client.Get(filepath, self._mc_buffer) - value_buf = mc.ConvertBuffer(self._mc_buffer) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class LmdbBackend(BaseStorageBackend): - """Lmdb storage backend. - - Args: - db_path (str): Lmdb database path. - readonly (bool, optional): Lmdb environment parameter. If True, - disallow any write operations. Default: True. - lock (bool, optional): Lmdb environment parameter. If False, when - concurrent access occurs, do not lock the database. Default: False. - readahead (bool, optional): Lmdb environment parameter. If False, - disable the OS filesystem readahead mechanism, which may improve - random read performance when a database is larger than RAM. - Default: False. - - Attributes: - db_path (str): Lmdb database path. - """ - - def __init__(self, - db_path, - readonly=True, - lock=False, - readahead=False, - **kwargs): - try: - import lmdb - except ImportError: - raise ImportError('Please install lmdb to enable LmdbBackend.') - - self.db_path = str(db_path) - self._client = lmdb.open( - self.db_path, - readonly=readonly, - lock=lock, - readahead=readahead, - **kwargs) - - def get(self, filepath): - """Get values according to the filepath. - - Args: - filepath (str | obj:`Path`): Here, filepath is the lmdb key. - """ - filepath = str(filepath) - with self._client.begin(write=False) as txn: - value_buf = txn.get(filepath.encode('ascii')) - return value_buf - - def get_text(self, filepath, encoding=None): - raise NotImplementedError - - -class HardDiskBackend(BaseStorageBackend): - """Raw hard disks storage backend.""" - - _allow_symlink = True - - def get(self, filepath: Union[str, Path]) -> bytes: - """Read data from a given ``filepath`` with 'rb' mode. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes: Expected bytes object. - """ - with open(filepath, 'rb') as f: - value_buf = f.read() - return value_buf - - def get_text(self, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - with open(filepath, 'r', encoding=encoding) as f: - value_buf = f.read() - return value_buf - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` will create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'wb') as f: - f.write(obj) - - def put_text(self, - obj: str, - filepath: Union[str, Path], - encoding: str = 'utf-8') -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` will create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - """ - mmcv.mkdir_or_exist(osp.dirname(filepath)) - with open(filepath, 'w', encoding=encoding) as f: - f.write(obj) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str or Path): Path to be removed. - """ - os.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return osp.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return osp.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return osp.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return osp.join(filepath, *filepaths) - - @contextmanager - def get_local_path( - self, filepath: Union[str, Path]) -> Iterable[Union[str, Path]]: - """Only for unified API and do nothing.""" - yield filepath - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - if list_dir and suffix is not None: - raise TypeError('`suffix` should be None when `list_dir` is True') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('`suffix` must be a string or tuple of strings') - - root = dir_path - - def _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - if (suffix is None - or rel_path.endswith(suffix)) and list_file: - yield rel_path - elif osp.isdir(entry.path): - if list_dir: - rel_dir = osp.relpath(entry.path, root) - yield rel_dir - if recursive: - yield from _list_dir_or_file(entry.path, list_dir, - list_file, suffix, - recursive) - - return _list_dir_or_file(dir_path, list_dir, list_file, suffix, - recursive) - - -class HTTPBackend(BaseStorageBackend): - """HTTP and HTTPS storage bachend.""" - - def get(self, filepath): - value_buf = urlopen(filepath).read() - return value_buf - - def get_text(self, filepath, encoding='utf-8'): - value_buf = urlopen(filepath).read() - return value_buf.decode(encoding) - - @contextmanager - def get_local_path(self, filepath: str) -> Iterable[str]: - """Download a file from ``filepath``. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Args: - filepath (str): Download a file from ``filepath``. - - Examples: - >>> client = HTTPBackend() - >>> # After existing from the ``with`` clause, - >>> # the path will be removed - >>> with client.get_local_path('http://path/of/your/file') as path: - ... # do something here - """ - try: - f = tempfile.NamedTemporaryFile(delete=False) - f.write(self.get(filepath)) - f.close() - yield f.name - finally: - os.remove(f.name) - - -class FileClient: - """A general file client to access files in different backends. - - The client loads a file or text in a specified backend from its path - and returns it as a binary or text file. There are two ways to choose a - backend, the name of backend and the prefix of path. Although both of them - can be used to choose a storage backend, ``backend`` has a higher priority - that is if they are all set, the storage backend will be chosen by the - backend argument. If they are all `None`, the disk backend will be chosen. - Note that It can also register other backend accessor with a given name, - prefixes, and backend class. In addition, We use the singleton pattern to - avoid repeated object creation. If the arguments are the same, the same - object will be returned. - - Args: - backend (str, optional): The storage backend type. Options are "disk", - "ceph", "memcached", "lmdb", "http" and "petrel". Default: None. - prefix (str, optional): The prefix of the registered storage backend. - Options are "s3", "http", "https". Default: None. - - Examples: - >>> # only set backend - >>> file_client = FileClient(backend='petrel') - >>> # only set prefix - >>> file_client = FileClient(prefix='s3') - >>> # set both backend and prefix but use backend to choose client - >>> file_client = FileClient(backend='petrel', prefix='s3') - >>> # if the arguments are the same, the same object is returned - >>> file_client1 = FileClient(backend='petrel') - >>> file_client1 is file_client - True - - Attributes: - client (:obj:`BaseStorageBackend`): The backend object. - """ - - _backends = { - 'disk': HardDiskBackend, - 'ceph': CephBackend, - 'memcached': MemcachedBackend, - 'lmdb': LmdbBackend, - 'petrel': PetrelBackend, - 'http': HTTPBackend, - } - # This collection is used to record the overridden backends, and when a - # backend appears in the collection, the singleton pattern is disabled for - # that backend, because if the singleton pattern is used, then the object - # returned will be the backend before overwriting - _overridden_backends = set() - _prefix_to_backends = { - 's3': PetrelBackend, - 'http': HTTPBackend, - 'https': HTTPBackend, - } - _overridden_prefixes = set() - - _instances = {} - - def __new__(cls, backend=None, prefix=None, **kwargs): - if backend is None and prefix is None: - backend = 'disk' - if backend is not None and backend not in cls._backends: - raise ValueError( - f'Backend {backend} is not supported. Currently supported ones' - f' are {list(cls._backends.keys())}') - if prefix is not None and prefix not in cls._prefix_to_backends: - raise ValueError( - f'prefix {prefix} is not supported. Currently supported ones ' - f'are {list(cls._prefix_to_backends.keys())}') - - # concatenate the arguments to a unique key for determining whether - # objects with the same arguments were created - arg_key = f'{backend}:{prefix}' - for key, value in kwargs.items(): - arg_key += f':{key}:{value}' - - # if a backend was overridden, it will create a new object - if (arg_key in cls._instances - and backend not in cls._overridden_backends - and prefix not in cls._overridden_prefixes): - _instance = cls._instances[arg_key] - else: - # create a new object and put it to _instance - _instance = super().__new__(cls) - if backend is not None: - _instance.client = cls._backends[backend](**kwargs) - else: - _instance.client = cls._prefix_to_backends[prefix](**kwargs) - - cls._instances[arg_key] = _instance - - return _instance - - @property - def name(self): - return self.client.name - - @property - def allow_symlink(self): - return self.client.allow_symlink - - @staticmethod - def parse_uri_prefix(uri: Union[str, Path]) -> Optional[str]: - """Parse the prefix of a uri. - - Args: - uri (str | Path): Uri to be parsed that contains the file prefix. - - Examples: - >>> FileClient.parse_uri_prefix('s3://path/of/your/file') - 's3' - - Returns: - str | None: Return the prefix of uri if the uri contains '://' - else ``None``. - """ - assert is_filepath(uri) - uri = str(uri) - if '://' not in uri: - return None - else: - prefix, _ = uri.split('://') - # In the case of PetrelBackend, the prefix may contains the cluster - # name like clusterName:s3 - if ':' in prefix: - _, prefix = prefix.split(':') - return prefix - - @classmethod - def infer_client(cls, - file_client_args: Optional[dict] = None, - uri: Optional[Union[str, Path]] = None) -> 'FileClient': - """Infer a suitable file client based on the URI and arguments. - - Args: - file_client_args (dict, optional): Arguments to instantiate a - FileClient. Default: None. - uri (str | Path, optional): Uri to be parsed that contains the file - prefix. Default: None. - - Examples: - >>> uri = 's3://path/of/your/file' - >>> file_client = FileClient.infer_client(uri=uri) - >>> file_client_args = {'backend': 'petrel'} - >>> file_client = FileClient.infer_client(file_client_args) - - Returns: - FileClient: Instantiated FileClient object. - """ - assert file_client_args is not None or uri is not None - if file_client_args is None: - file_prefix = cls.parse_uri_prefix(uri) # type: ignore - return cls(prefix=file_prefix) - else: - return cls(**file_client_args) - - @classmethod - def _register_backend(cls, name, backend, force=False, prefixes=None): - if not isinstance(name, str): - raise TypeError('the backend name should be a string, ' - f'but got {type(name)}') - if not inspect.isclass(backend): - raise TypeError( - f'backend should be a class but got {type(backend)}') - if not issubclass(backend, BaseStorageBackend): - raise TypeError( - f'backend {backend} is not a subclass of BaseStorageBackend') - if not force and name in cls._backends: - raise KeyError( - f'{name} is already registered as a storage backend, ' - 'add "force=True" if you want to override it') - - if name in cls._backends and force: - cls._overridden_backends.add(name) - cls._backends[name] = backend - - if prefixes is not None: - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if prefix not in cls._prefix_to_backends: - cls._prefix_to_backends[prefix] = backend - elif (prefix in cls._prefix_to_backends) and force: - cls._overridden_prefixes.add(prefix) - cls._prefix_to_backends[prefix] = backend - else: - raise KeyError( - f'{prefix} is already registered as a storage backend,' - ' add "force=True" if you want to override it') - - @classmethod - def register_backend(cls, name, backend=None, force=False, prefixes=None): - """Register a backend to FileClient. - - This method can be used as a normal class method or a decorator. - - .. code-block:: python - - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - FileClient.register_backend('new', NewBackend) - - or - - .. code-block:: python - - @FileClient.register_backend('new') - class NewBackend(BaseStorageBackend): - - def get(self, filepath): - return filepath - - def get_text(self, filepath): - return filepath - - Args: - name (str): The name of the registered backend. - backend (class, optional): The backend class to be registered, - which must be a subclass of :class:`BaseStorageBackend`. - When this method is used as a decorator, backend is None. - Defaults to None. - force (bool, optional): Whether to override the backend if the name - has already been registered. Defaults to False. - prefixes (str or list[str] or tuple[str], optional): The prefixes - of the registered storage backend. Default: None. - `New in version 1.3.15.` - """ - if backend is not None: - cls._register_backend( - name, backend, force=force, prefixes=prefixes) - return - - def _register(backend_cls): - cls._register_backend( - name, backend_cls, force=force, prefixes=prefixes) - return backend_cls - - return _register - - def get(self, filepath: Union[str, Path]) -> Union[bytes, memoryview]: - """Read data from a given ``filepath`` with 'rb' mode. - - Note: - There are two types of return values for ``get``, one is ``bytes`` - and the other is ``memoryview``. The advantage of using memoryview - is that you can avoid copying, and if you want to convert it to - ``bytes``, you can use ``.tobytes()``. - - Args: - filepath (str or Path): Path to read data. - - Returns: - bytes | memoryview: Expected bytes object or a memory view of the - bytes object. - """ - return self.client.get(filepath) - - def get_text(self, filepath: Union[str, Path], encoding='utf-8') -> str: - """Read data from a given ``filepath`` with 'r' mode. - - Args: - filepath (str or Path): Path to read data. - encoding (str): The encoding format used to open the ``filepath``. - Default: 'utf-8'. - - Returns: - str: Expected text reading from ``filepath``. - """ - return self.client.get_text(filepath, encoding) - - def put(self, obj: bytes, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'wb' mode. - - Note: - ``put`` should create a directory if the directory of ``filepath`` - does not exist. - - Args: - obj (bytes): Data to be written. - filepath (str or Path): Path to write data. - """ - self.client.put(obj, filepath) - - def put_text(self, obj: str, filepath: Union[str, Path]) -> None: - """Write data to a given ``filepath`` with 'w' mode. - - Note: - ``put_text`` should create a directory if the directory of - ``filepath`` does not exist. - - Args: - obj (str): Data to be written. - filepath (str or Path): Path to write data. - encoding (str, optional): The encoding format used to open the - `filepath`. Default: 'utf-8'. - """ - self.client.put_text(obj, filepath) - - def remove(self, filepath: Union[str, Path]) -> None: - """Remove a file. - - Args: - filepath (str, Path): Path to be removed. - """ - self.client.remove(filepath) - - def exists(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path exists. - - Args: - filepath (str or Path): Path to be checked whether exists. - - Returns: - bool: Return ``True`` if ``filepath`` exists, ``False`` otherwise. - """ - return self.client.exists(filepath) - - def isdir(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a directory. - - Args: - filepath (str or Path): Path to be checked whether it is a - directory. - - Returns: - bool: Return ``True`` if ``filepath`` points to a directory, - ``False`` otherwise. - """ - return self.client.isdir(filepath) - - def isfile(self, filepath: Union[str, Path]) -> bool: - """Check whether a file path is a file. - - Args: - filepath (str or Path): Path to be checked whether it is a file. - - Returns: - bool: Return ``True`` if ``filepath`` points to a file, ``False`` - otherwise. - """ - return self.client.isfile(filepath) - - def join_path(self, filepath: Union[str, Path], - *filepaths: Union[str, Path]) -> str: - """Concatenate all file paths. - - Join one or more filepath components intelligently. The return value - is the concatenation of filepath and any members of *filepaths. - - Args: - filepath (str or Path): Path to be concatenated. - - Returns: - str: The result of concatenation. - """ - return self.client.join_path(filepath, *filepaths) - - @contextmanager - def get_local_path(self, filepath: Union[str, Path]) -> Iterable[str]: - """Download data from ``filepath`` and write the data to local path. - - ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It - can be called with ``with`` statement, and when exists from the - ``with`` statement, the temporary path will be released. - - Note: - If the ``filepath`` is a local path, just return itself. - - .. warning:: - ``get_local_path`` is an experimental interface that may change in - the future. - - Args: - filepath (str or Path): Path to be read data. - - Examples: - >>> file_client = FileClient(prefix='s3') - >>> with file_client.get_local_path('s3://bucket/abc.jpg') as path: - ... # do something here - - Yields: - Iterable[str]: Only yield one path. - """ - with self.client.get_local_path(str(filepath)) as local_path: - yield local_path - - def list_dir_or_file(self, - dir_path: Union[str, Path], - list_dir: bool = True, - list_file: bool = True, - suffix: Optional[Union[str, Tuple[str]]] = None, - recursive: bool = False) -> Iterator[str]: - """Scan a directory to find the interested directories or files in - arbitrary order. - - Note: - :meth:`list_dir_or_file` returns the path relative to ``dir_path``. - - Args: - dir_path (str | Path): Path of the directory. - list_dir (bool): List the directories. Default: True. - list_file (bool): List the path of files. Default: True. - suffix (str or tuple[str], optional): File suffix - that we are interested in. Default: None. - recursive (bool): If set to True, recursively scan the - directory. Default: False. - - Yields: - Iterable[str]: A relative path to ``dir_path``. - """ - yield from self.client.list_dir_or_file(dir_path, list_dir, list_file, - suffix, recursive) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/__init__.py deleted file mode 100755 index aa24d9197..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import BaseFileHandler -from .json_handler import JsonHandler -from .pickle_handler import PickleHandler -from .yaml_handler import YamlHandler - -__all__ = ['BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/base.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/base.py deleted file mode 100755 index 288878bc5..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/base.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod - - -class BaseFileHandler(metaclass=ABCMeta): - # `str_like` is a flag to indicate whether the type of file object is - # str-like object or bytes-like object. Pickle only processes bytes-like - # objects but json only processes str-like object. If it is str-like - # object, `StringIO` will be used to process the buffer. - str_like = True - - @abstractmethod - def load_from_fileobj(self, file, **kwargs): - pass - - @abstractmethod - def dump_to_fileobj(self, obj, file, **kwargs): - pass - - @abstractmethod - def dump_to_str(self, obj, **kwargs): - pass - - def load_from_path(self, filepath, mode='r', **kwargs): - with open(filepath, mode) as f: - return self.load_from_fileobj(f, **kwargs) - - def dump_to_path(self, obj, filepath, mode='w', **kwargs): - with open(filepath, mode) as f: - self.dump_to_fileobj(obj, f, **kwargs) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/json_handler.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/json_handler.py deleted file mode 100755 index 18d4f15f7..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/json_handler.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json - -import numpy as np - -from .base import BaseFileHandler - - -def set_default(obj): - """Set default json values for non-serializable values. - - It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. - It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, - etc.) into plain numbers of plain python built-in types. - """ - if isinstance(obj, (set, range)): - return list(obj) - elif isinstance(obj, np.ndarray): - return obj.tolist() - elif isinstance(obj, np.generic): - return obj.item() - raise TypeError(f'{type(obj)} is unsupported for json dump') - - -class JsonHandler(BaseFileHandler): - - def load_from_fileobj(self, file): - return json.load(file) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('default', set_default) - json.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('default', set_default) - return json.dumps(obj, **kwargs) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/pickle_handler.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/pickle_handler.py deleted file mode 100755 index b37c79bed..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/pickle_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import pickle - -from .base import BaseFileHandler - - -class PickleHandler(BaseFileHandler): - - str_like = False - - def load_from_fileobj(self, file, **kwargs): - return pickle.load(file, **kwargs) - - def load_from_path(self, filepath, **kwargs): - return super(PickleHandler, self).load_from_path( - filepath, mode='rb', **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('protocol', 2) - return pickle.dumps(obj, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('protocol', 2) - pickle.dump(obj, file, **kwargs) - - def dump_to_path(self, obj, filepath, **kwargs): - super(PickleHandler, self).dump_to_path( - obj, filepath, mode='wb', **kwargs) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/yaml_handler.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/yaml_handler.py deleted file mode 100755 index c5aa2eea1..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/handlers/yaml_handler.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import yaml - -try: - from yaml import CLoader as Loader, CDumper as Dumper -except ImportError: - from yaml import Loader, Dumper - -from .base import BaseFileHandler # isort:skip - - -class YamlHandler(BaseFileHandler): - - def load_from_fileobj(self, file, **kwargs): - kwargs.setdefault('Loader', Loader) - return yaml.load(file, **kwargs) - - def dump_to_fileobj(self, obj, file, **kwargs): - kwargs.setdefault('Dumper', Dumper) - yaml.dump(obj, file, **kwargs) - - def dump_to_str(self, obj, **kwargs): - kwargs.setdefault('Dumper', Dumper) - return yaml.dump(obj, **kwargs) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/io.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/io.py deleted file mode 100755 index aaefde58a..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/io.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from io import BytesIO, StringIO -from pathlib import Path - -from ..utils import is_list_of, is_str -from .file_client import FileClient -from .handlers import BaseFileHandler, JsonHandler, PickleHandler, YamlHandler - -file_handlers = { - 'json': JsonHandler(), - 'yaml': YamlHandler(), - 'yml': YamlHandler(), - 'pickle': PickleHandler(), - 'pkl': PickleHandler() -} - - -def load(file, file_format=None, file_client_args=None, **kwargs): - """Load data from json/yaml/pickle files. - - This method provides a unified api for loading data from serialized files. - - Note: - In v1.3.16 and later, ``load`` supports loading data from serialized - files those can be storaged in different backends. - - Args: - file (str or :obj:`Path` or file-like object): Filename or a file-like - object. - file_format (str, optional): If not specified, the file format will be - inferred from the file extension, otherwise use the specified one. - Currently supported formats include "json", "yaml/yml" and - "pickle/pkl". - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> load('/path/of/your/file') # file is storaged in disk - >>> load('https://path/of/your/file') # file is storaged in Internet - >>> load('s3://path/of/your/file') # file is storaged in petrel - - Returns: - The content from the file. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None and is_str(file): - file_format = file.split('.')[-1] - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO(file_client.get_text(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - else: - with BytesIO(file_client.get(file)) as f: - obj = handler.load_from_fileobj(f, **kwargs) - elif hasattr(file, 'read'): - obj = handler.load_from_fileobj(file, **kwargs) - else: - raise TypeError('"file" must be a filepath str or a file-object') - return obj - - -def dump(obj, file=None, file_format=None, file_client_args=None, **kwargs): - """Dump data to json/yaml/pickle strings or files. - - This method provides a unified api for dumping data as strings or to files, - and also supports custom arguments for each file format. - - Note: - In v1.3.16 and later, ``dump`` supports dumping data as strings or to - files which is saved to different backends. - - Args: - obj (any): The python object to be dumped. - file (str or :obj:`Path` or file-like object, optional): If not - specified, then the object is dumped to a str, otherwise to a file - specified by the filename or file-like object. - file_format (str, optional): Same as :func:`load`. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dump('hello world', '/path/of/your/file') # disk - >>> dump('hello world', 's3://path/of/your/file') # ceph or petrel - - Returns: - bool: True for success, False otherwise. - """ - if isinstance(file, Path): - file = str(file) - if file_format is None: - if is_str(file): - file_format = file.split('.')[-1] - elif file is None: - raise ValueError( - 'file_format must be specified since file is None') - if file_format not in file_handlers: - raise TypeError(f'Unsupported format: {file_format}') - - handler = file_handlers[file_format] - if file is None: - return handler.dump_to_str(obj, **kwargs) - elif is_str(file): - file_client = FileClient.infer_client(file_client_args, file) - if handler.str_like: - with StringIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put_text(f.getvalue(), file) - else: - with BytesIO() as f: - handler.dump_to_fileobj(obj, f, **kwargs) - file_client.put(f.getvalue(), file) - elif hasattr(file, 'write'): - handler.dump_to_fileobj(obj, file, **kwargs) - else: - raise TypeError('"file" must be a filename str or a file-object') - - -def _register_handler(handler, file_formats): - """Register a handler for some file extensions. - - Args: - handler (:obj:`BaseFileHandler`): Handler to be registered. - file_formats (str or list[str]): File formats to be handled by this - handler. - """ - if not isinstance(handler, BaseFileHandler): - raise TypeError( - f'handler must be a child of BaseFileHandler, not {type(handler)}') - if isinstance(file_formats, str): - file_formats = [file_formats] - if not is_list_of(file_formats, str): - raise TypeError('file_formats must be a str or a list of str') - for ext in file_formats: - file_handlers[ext] = handler - - -def register_handler(file_formats, **kwargs): - - def wrap(cls): - _register_handler(cls(**kwargs), file_formats) - return cls - - return wrap diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/parse.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/parse.py deleted file mode 100755 index f60f0d611..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/fileio/parse.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. - -from io import StringIO - -from .file_client import FileClient - - -def list_from_file(filename, - prefix='', - offset=0, - max_num=0, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a list of strings. - - Note: - In v1.3.16 and later, ``list_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a list for strings. - - Args: - filename (str): Filename. - prefix (str): The prefix to be inserted to the beginning of each item. - offset (int): The offset of lines. - max_num (int): The maximum number of lines to be read, - zeros and negatives mean no limitation. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> list_from_file('/path/of/your/file') # disk - ['hello', 'world'] - >>> list_from_file('s3://path/of/your/file') # ceph or petrel - ['hello', 'world'] - - Returns: - list[str]: A list of strings. - """ - cnt = 0 - item_list = [] - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for _ in range(offset): - f.readline() - for line in f: - if 0 < max_num <= cnt: - break - item_list.append(prefix + line.rstrip('\n\r')) - cnt += 1 - return item_list - - -def dict_from_file(filename, - key_type=str, - encoding='utf-8', - file_client_args=None): - """Load a text file and parse the content as a dict. - - Each line of the text file will be two or more columns split by - whitespaces or tabs. The first column will be parsed as dict keys, and - the following columns will be parsed as dict values. - - Note: - In v1.3.16 and later, ``dict_from_file`` supports loading a text file - which can be storaged in different backends and parsing the content as - a dict. - - Args: - filename(str): Filename. - key_type(type): Type of the dict keys. str is user by default and - type conversion will be performed if specified. - encoding (str): Encoding used to open the file. Default utf-8. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - - Examples: - >>> dict_from_file('/path/of/your/file') # disk - {'key1': 'value1', 'key2': 'value2'} - >>> dict_from_file('s3://path/of/your/file') # ceph or petrel - {'key1': 'value1', 'key2': 'value2'} - - Returns: - dict: The parsed contents. - """ - mapping = {} - file_client = FileClient.infer_client(file_client_args, filename) - with StringIO(file_client.get_text(filename, encoding)) as f: - for line in f: - items = line.rstrip('\n').split() - assert len(items) >= 2 - key = key_type(items[0]) - val = items[1:] if len(items) > 2 else items[1] - mapping[key] = val - return mapping diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/__init__.py deleted file mode 100755 index d0051d609..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, - gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, - rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) -from .geometric import (cutout, imcrop, imflip, imflip_, impad, - impad_to_multiple, imrescale, imresize, imresize_like, - imresize_to_multiple, imrotate, imshear, imtranslate, - rescale_size) -from .io import imfrombytes, imread, imwrite, supported_backends, use_backend -from .misc import tensor2imgs -from .photometric import (adjust_brightness, adjust_color, adjust_contrast, - adjust_lighting, adjust_sharpness, auto_contrast, - clahe, imdenormalize, imequalize, iminvert, - imnormalize, imnormalize_, lut_transform, posterize, - solarize) - -__all__ = [ - 'bgr2gray', 'bgr2hls', 'bgr2hsv', 'bgr2rgb', 'gray2bgr', 'gray2rgb', - 'hls2bgr', 'hsv2bgr', 'imconvert', 'rgb2bgr', 'rgb2gray', 'imrescale', - 'imresize', 'imresize_like', 'imresize_to_multiple', 'rescale_size', - 'imcrop', 'imflip', 'imflip_', 'impad', 'impad_to_multiple', 'imrotate', - 'imfrombytes', 'imread', 'imwrite', 'supported_backends', 'use_backend', - 'imdenormalize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', - 'solarize', 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', - 'tensor2imgs', 'imshear', 'imtranslate', 'adjust_color', 'imequalize', - 'adjust_brightness', 'adjust_contrast', 'lut_transform', 'clahe', - 'adjust_sharpness', 'auto_contrast', 'cutout', 'adjust_lighting' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/colorspace.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/colorspace.py deleted file mode 100755 index 814533952..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/colorspace.py +++ /dev/null @@ -1,306 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def imconvert(img, src, dst): - """Convert an image from the src colorspace to dst colorspace. - - Args: - img (ndarray): The input image. - src (str): The source colorspace, e.g., 'rgb', 'hsv'. - dst (str): The destination colorspace, e.g., 'rgb', 'hsv'. - - Returns: - ndarray: The converted image. - """ - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - out_img = cv2.cvtColor(img, code) - return out_img - - -def bgr2gray(img, keepdim=False): - """Convert a BGR image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def rgb2gray(img, keepdim=False): - """Convert a RGB image to grayscale image. - - Args: - img (ndarray): The input image. - keepdim (bool): If False (by default), then return the grayscale image - with 2 dims, otherwise 3 dims. - - Returns: - ndarray: The converted grayscale image. - """ - out_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) - if keepdim: - out_img = out_img[..., None] - return out_img - - -def gray2bgr(img): - """Convert a grayscale image to BGR image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted BGR image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return out_img - - -def gray2rgb(img): - """Convert a grayscale image to RGB image. - - Args: - img (ndarray): The input image. - - Returns: - ndarray: The converted RGB image. - """ - img = img[..., None] if img.ndim == 2 else img - out_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) - return out_img - - -def _convert_input_type_range(img): - """Convert the type and range of the input image. - - It converts the input image to np.float32 type and range of [0, 1]. - It is mainly used for pre-processing the input image in colorspace - conversion functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - (ndarray): The converted image with type of np.float32 and range of - [0, 1]. - """ - img_type = img.dtype - img = img.astype(np.float32) - if img_type == np.float32: - pass - elif img_type == np.uint8: - img /= 255. - else: - raise TypeError('The img type should be np.float32 or np.uint8, ' - f'but got {img_type}') - return img - - -def _convert_output_type_range(img, dst_type): - """Convert the type and range of the image according to dst_type. - - It converts the image to desired type and range. If `dst_type` is np.uint8, - images will be converted to np.uint8 type with range [0, 255]. If - `dst_type` is np.float32, it converts the image to np.float32 type with - range [0, 1]. - It is mainly used for post-processing images in colorspace conversion - functions such as rgb2ycbcr and ycbcr2rgb. - - Args: - img (ndarray): The image to be converted with np.float32 type and - range [0, 255]. - dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it - converts the image to np.uint8 type with range [0, 255]. If - dst_type is np.float32, it converts the image to np.float32 type - with range [0, 1]. - - Returns: - (ndarray): The converted image with desired type and range. - """ - if dst_type not in (np.uint8, np.float32): - raise TypeError('The dst_type should be np.float32 or np.uint8, ' - f'but got {dst_type}') - if dst_type == np.uint8: - img = img.round() - else: - img /= 255. - return img.astype(dst_type) - - -def rgb2ycbcr(img, y_only=False): - """Convert a RGB image to YCbCr image. - - This function produces the same results as Matlab's `rgb2ycbcr` function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 - else: - out_img = np.matmul( - img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], - [24.966, 112.0, -18.214]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def bgr2ycbcr(img, y_only=False): - """Convert a BGR image to YCbCr image. - - The bgr version of rgb2ycbcr. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - y_only (bool): Whether to only return Y channel. Default: False. - - Returns: - ndarray: The converted YCbCr image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) - if y_only: - out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 - else: - out_img = np.matmul( - img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], - [65.481, -37.797, 112.0]]) + [16, 128, 128] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2rgb(img): - """Convert a YCbCr image to RGB image. - - This function produces the same results as Matlab's ycbcr2rgb function. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted RGB image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0, -0.00153632, 0.00791071], - [0.00625893, -0.00318811, 0]]) * 255.0 + [ - -222.921, 135.576, -276.836 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def ycbcr2bgr(img): - """Convert a YCbCr image to BGR image. - - The bgr version of ycbcr2rgb. - It implements the ITU-R BT.601 conversion for standard-definition - television. See more details in - https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. - - It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. - In OpenCV, it implements a JPEG conversion. See more details in - https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. - - Args: - img (ndarray): The input image. It accepts: - 1. np.uint8 type with range [0, 255]; - 2. np.float32 type with range [0, 1]. - - Returns: - ndarray: The converted BGR image. The output image has the same type - and range as input image. - """ - img_type = img.dtype - img = _convert_input_type_range(img) * 255 - out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], - [0.00791071, -0.00153632, 0], - [0, -0.00318811, 0.00625893]]) * 255.0 + [ - -276.836, 135.576, -222.921 - ] - out_img = _convert_output_type_range(out_img, img_type) - return out_img - - -def convert_color_factory(src, dst): - - code = getattr(cv2, f'COLOR_{src.upper()}2{dst.upper()}') - - def convert_color(img): - out_img = cv2.cvtColor(img, code) - return out_img - - convert_color.__doc__ = f"""Convert a {src.upper()} image to {dst.upper()} - image. - - Args: - img (ndarray or str): The input image. - - Returns: - ndarray: The converted {dst.upper()} image. - """ - - return convert_color - - -bgr2rgb = convert_color_factory('bgr', 'rgb') - -rgb2bgr = convert_color_factory('rgb', 'bgr') - -bgr2hsv = convert_color_factory('bgr', 'hsv') - -hsv2bgr = convert_color_factory('hsv', 'bgr') - -bgr2hls = convert_color_factory('bgr', 'hls') - -hls2bgr = convert_color_factory('hls', 'bgr') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/geometric.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/geometric.py deleted file mode 100755 index cf97c201c..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/geometric.py +++ /dev/null @@ -1,728 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers - -import cv2 -import numpy as np - -from ..utils import to_2tuple -from .io import imread_backend - -try: - from PIL import Image -except ImportError: - Image = None - - -def _scale_size(size, scale): - """Rescale a size by a ratio. - - Args: - size (tuple[int]): (w, h). - scale (float | tuple(float)): Scaling factor. - - Returns: - tuple[int]: scaled size. - """ - if isinstance(scale, (float, int)): - scale = (scale, scale) - w, h = size - return int(w * float(scale[0]) + 0.5), int(h * float(scale[1]) + 0.5) - - -cv2_interp_codes = { - 'nearest': cv2.INTER_NEAREST, - 'bilinear': cv2.INTER_LINEAR, - 'bicubic': cv2.INTER_CUBIC, - 'area': cv2.INTER_AREA, - 'lanczos': cv2.INTER_LANCZOS4 -} - -if Image is not None: - pillow_interp_codes = { - 'nearest': Image.NEAREST, - 'bilinear': Image.BILINEAR, - 'bicubic': Image.BICUBIC, - 'box': Image.BOX, - 'lanczos': Image.LANCZOS, - 'hamming': Image.HAMMING - } - - -def imresize(img, - size, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image to a given size. - - Args: - img (ndarray): The input image. - size (tuple[int]): Target size (w, h). - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if backend is None: - backend = imread_backend - if backend not in ['cv2', 'pillow']: - raise ValueError(f'backend: {backend} is not supported for resize.' - f"Supported backends are 'cv2', 'pillow'") - - if backend == 'pillow': - assert img.dtype == np.uint8, 'Pillow backend only support uint8 type' - pil_image = Image.fromarray(img) - pil_image = pil_image.resize(size, pillow_interp_codes[interpolation]) - resized_img = np.array(pil_image) - else: - resized_img = cv2.resize( - img, size, dst=out, interpolation=cv2_interp_codes[interpolation]) - if not return_scale: - return resized_img - else: - w_scale = size[0] / w - h_scale = size[1] / h - return resized_img, w_scale, h_scale - - -def imresize_to_multiple(img, - divisor, - size=None, - scale_factor=None, - keep_ratio=False, - return_scale=False, - interpolation='bilinear', - out=None, - backend=None): - """Resize image according to a given size or scale factor and then rounds - up the the resized or rescaled image size to the nearest value that can be - divided by the divisor. - - Args: - img (ndarray): The input image. - divisor (int | tuple): Resized image size will be a multiple of - divisor. If divisor is a tuple, divisor should be - (w_divisor, h_divisor). - size (None | int | tuple[int]): Target size (w, h). Default: None. - scale_factor (None | float | tuple[float]): Multiplier for spatial - size. Should match input size if it is a tuple and the 2D style is - (w_scale_factor, h_scale_factor). Default: None. - keep_ratio (bool): Whether to keep the aspect ratio when resizing the - image. Default: False. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Interpolation method, accepted values are - "nearest", "bilinear", "bicubic", "area", "lanczos" for 'cv2' - backend, "nearest", "bilinear" for 'pillow' backend. - out (ndarray): The output destination. - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - tuple | ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = img.shape[:2] - if size is not None and scale_factor is not None: - raise ValueError('only one of size or scale_factor should be defined') - elif size is None and scale_factor is None: - raise ValueError('one of size or scale_factor should be defined') - elif size is not None: - size = to_2tuple(size) - if keep_ratio: - size = rescale_size((w, h), size, return_scale=False) - else: - size = _scale_size((w, h), scale_factor) - - divisor = to_2tuple(divisor) - size = tuple([int(np.ceil(s / d)) * d for s, d in zip(size, divisor)]) - resized_img, w_scale, h_scale = imresize( - img, - size, - return_scale=True, - interpolation=interpolation, - out=out, - backend=backend) - if return_scale: - return resized_img, w_scale, h_scale - else: - return resized_img - - -def imresize_like(img, - dst_img, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image to the same size of a given image. - - Args: - img (ndarray): The input image. - dst_img (ndarray): The target image. - return_scale (bool): Whether to return `w_scale` and `h_scale`. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - tuple or ndarray: (`resized_img`, `w_scale`, `h_scale`) or - `resized_img`. - """ - h, w = dst_img.shape[:2] - return imresize(img, (w, h), return_scale, interpolation, backend=backend) - - -def rescale_size(old_size, scale, return_scale=False): - """Calculate the new size to be rescaled to. - - Args: - old_size (tuple[int]): The old size (w, h) of image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image size. - - Returns: - tuple[int]: The new rescaled image size. - """ - w, h = old_size - if isinstance(scale, (float, int)): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - scale_factor = scale - elif isinstance(scale, tuple): - max_long_edge = max(scale) - max_short_edge = min(scale) - scale_factor = min(max_long_edge / max(h, w), - max_short_edge / min(h, w)) - else: - raise TypeError( - f'Scale must be a number or tuple of int, but got {type(scale)}') - - new_size = _scale_size((w, h), scale_factor) - - if return_scale: - return new_size, scale_factor - else: - return new_size - - -def imrescale(img, - scale, - return_scale=False, - interpolation='bilinear', - backend=None): - """Resize image while keeping the aspect ratio. - - Args: - img (ndarray): The input image. - scale (float | tuple[int]): The scaling factor or maximum size. - If it is a float number, then the image will be rescaled by this - factor, else if it is a tuple of 2 integers, then the image will - be rescaled as large as possible within the scale. - return_scale (bool): Whether to return the scaling factor besides the - rescaled image. - interpolation (str): Same as :func:`resize`. - backend (str | None): Same as :func:`resize`. - - Returns: - ndarray: The rescaled image. - """ - h, w = img.shape[:2] - new_size, scale_factor = rescale_size((w, h), scale, return_scale=True) - rescaled_img = imresize( - img, new_size, interpolation=interpolation, backend=backend) - if return_scale: - return rescaled_img, scale_factor - else: - return rescaled_img - - -def imflip(img, direction='horizontal'): - """Flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image. - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return np.flip(img, axis=1) - elif direction == 'vertical': - return np.flip(img, axis=0) - else: - return np.flip(img, axis=(0, 1)) - - -def imflip_(img, direction='horizontal'): - """Inplace flip an image horizontally or vertically. - - Args: - img (ndarray): Image to be flipped. - direction (str): The flip direction, either "horizontal" or - "vertical" or "diagonal". - - Returns: - ndarray: The flipped image (inplace). - """ - assert direction in ['horizontal', 'vertical', 'diagonal'] - if direction == 'horizontal': - return cv2.flip(img, 1, img) - elif direction == 'vertical': - return cv2.flip(img, 0, img) - else: - return cv2.flip(img, -1, img) - - -def imrotate(img, - angle, - center=None, - scale=1.0, - border_value=0, - interpolation='bilinear', - auto_bound=False): - """Rotate an image. - - Args: - img (ndarray): Image to be rotated. - angle (float): Rotation angle in degrees, positive values mean - clockwise rotation. - center (tuple[float], optional): Center point (w, h) of the rotation in - the source image. If not specified, the center of the image will be - used. - scale (float): Isotropic scale factor. - border_value (int): Border value. - interpolation (str): Same as :func:`resize`. - auto_bound (bool): Whether to adjust the image size to cover the whole - rotated image. - - Returns: - ndarray: The rotated image. - """ - if center is not None and auto_bound: - raise ValueError('`auto_bound` conflicts with `center`') - h, w = img.shape[:2] - if center is None: - center = ((w - 1) * 0.5, (h - 1) * 0.5) - assert isinstance(center, tuple) - - matrix = cv2.getRotationMatrix2D(center, -angle, scale) - if auto_bound: - cos = np.abs(matrix[0, 0]) - sin = np.abs(matrix[0, 1]) - new_w = h * sin + w * cos - new_h = h * cos + w * sin - matrix[0, 2] += (new_w - w) * 0.5 - matrix[1, 2] += (new_h - h) * 0.5 - w = int(np.round(new_w)) - h = int(np.round(new_h)) - rotated = cv2.warpAffine( - img, - matrix, (w, h), - flags=cv2_interp_codes[interpolation], - borderValue=border_value) - return rotated - - -def bbox_clip(bboxes, img_shape): - """Clip bboxes to fit the image shape. - - Args: - bboxes (ndarray): Shape (..., 4*k) - img_shape (tuple[int]): (height, width) of the image. - - Returns: - ndarray: Clipped bboxes. - """ - assert bboxes.shape[-1] % 4 == 0 - cmin = np.empty(bboxes.shape[-1], dtype=bboxes.dtype) - cmin[0::2] = img_shape[1] - 1 - cmin[1::2] = img_shape[0] - 1 - clipped_bboxes = np.maximum(np.minimum(bboxes, cmin), 0) - return clipped_bboxes - - -def bbox_scaling(bboxes, scale, clip_shape=None): - """Scaling bboxes w.r.t the box center. - - Args: - bboxes (ndarray): Shape(..., 4). - scale (float): Scaling factor. - clip_shape (tuple[int], optional): If specified, bboxes that exceed the - boundary will be clipped according to the given shape (h, w). - - Returns: - ndarray: Scaled bboxes. - """ - if float(scale) == 1.0: - scaled_bboxes = bboxes.copy() - else: - w = bboxes[..., 2] - bboxes[..., 0] + 1 - h = bboxes[..., 3] - bboxes[..., 1] + 1 - dw = (w * (scale - 1)) * 0.5 - dh = (h * (scale - 1)) * 0.5 - scaled_bboxes = bboxes + np.stack((-dw, -dh, dw, dh), axis=-1) - if clip_shape is not None: - return bbox_clip(scaled_bboxes, clip_shape) - else: - return scaled_bboxes - - -def imcrop(img, bboxes, scale=1.0, pad_fill=None): - """Crop image patches. - - 3 steps: scale the bboxes -> clip bboxes -> crop and pad. - - Args: - img (ndarray): Image to be cropped. - bboxes (ndarray): Shape (k, 4) or (4, ), location of cropped bboxes. - scale (float, optional): Scale ratio of bboxes, the default value - 1.0 means no padding. - pad_fill (Number | list[Number]): Value to be filled for padding. - Default: None, which means no padding. - - Returns: - list[ndarray] | ndarray: The cropped image patches. - """ - chn = 1 if img.ndim == 2 else img.shape[2] - if pad_fill is not None: - if isinstance(pad_fill, (int, float)): - pad_fill = [pad_fill for _ in range(chn)] - assert len(pad_fill) == chn - - _bboxes = bboxes[None, ...] if bboxes.ndim == 1 else bboxes - scaled_bboxes = bbox_scaling(_bboxes, scale).astype(np.int32) - clipped_bbox = bbox_clip(scaled_bboxes, img.shape) - - patches = [] - for i in range(clipped_bbox.shape[0]): - x1, y1, x2, y2 = tuple(clipped_bbox[i, :]) - if pad_fill is None: - patch = img[y1:y2 + 1, x1:x2 + 1, ...] - else: - _x1, _y1, _x2, _y2 = tuple(scaled_bboxes[i, :]) - if chn == 1: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1) - else: - patch_shape = (_y2 - _y1 + 1, _x2 - _x1 + 1, chn) - patch = np.array( - pad_fill, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - x_start = 0 if _x1 >= 0 else -_x1 - y_start = 0 if _y1 >= 0 else -_y1 - w = x2 - x1 + 1 - h = y2 - y1 + 1 - patch[y_start:y_start + h, x_start:x_start + w, - ...] = img[y1:y1 + h, x1:x1 + w, ...] - patches.append(patch) - - if bboxes.ndim == 1: - return patches[0] - else: - return patches - - -def impad(img, - *, - shape=None, - padding=None, - pad_val=0, - padding_mode='constant'): - """Pad the given image to a certain shape or pad on all sides with - specified padding mode and padding value. - - Args: - img (ndarray): Image to be padded. - shape (tuple[int]): Expected padding shape (h, w). Default: None. - padding (int or tuple[int]): Padding on each border. If a single int is - provided this is used to pad all borders. If tuple of length 2 is - provided this is the padding on left/right and top/bottom - respectively. If a tuple of length 4 is provided this is the - padding for the left, top, right and bottom borders respectively. - Default: None. Note that `shape` and `padding` can not be both - set. - pad_val (Number | Sequence[Number]): Values to be filled in padding - areas when padding_mode is 'constant'. Default: 0. - padding_mode (str): Type of padding. Should be: constant, edge, - reflect or symmetric. Default: constant. - - - constant: pads with a constant value, this value is specified - with pad_val. - - edge: pads with the last value at the edge of the image. - - reflect: pads with reflection of image without repeating the - last value on the edge. For example, padding [1, 2, 3, 4] - with 2 elements on both sides in reflect mode will result - in [3, 2, 1, 2, 3, 4, 3, 2]. - - symmetric: pads with reflection of image repeating the last - value on the edge. For example, padding [1, 2, 3, 4] with - 2 elements on both sides in symmetric mode will result in - [2, 1, 1, 2, 3, 4, 4, 3] - - Returns: - ndarray: The padded image. - """ - - assert (shape is not None) ^ (padding is not None) - if shape is not None: - padding = (0, 0, shape[1] - img.shape[1], shape[0] - img.shape[0]) - - # check pad_val - if isinstance(pad_val, tuple): - assert len(pad_val) == img.shape[-1] - elif not isinstance(pad_val, numbers.Number): - raise TypeError('pad_val must be a int or a tuple. ' - f'But received {type(pad_val)}') - - # check padding - if isinstance(padding, tuple) and len(padding) in [2, 4]: - if len(padding) == 2: - padding = (padding[0], padding[1], padding[0], padding[1]) - elif isinstance(padding, numbers.Number): - padding = (padding, padding, padding, padding) - else: - raise ValueError('Padding must be a int or a 2, or 4 element tuple.' - f'But received {padding}') - - # check padding mode - assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric'] - - border_type = { - 'constant': cv2.BORDER_CONSTANT, - 'edge': cv2.BORDER_REPLICATE, - 'reflect': cv2.BORDER_REFLECT_101, - 'symmetric': cv2.BORDER_REFLECT - } - img = cv2.copyMakeBorder( - img, - padding[1], - padding[3], - padding[0], - padding[2], - border_type[padding_mode], - value=pad_val) - - return img - - -def impad_to_multiple(img, divisor, pad_val=0): - """Pad an image to ensure each edge to be multiple to some number. - - Args: - img (ndarray): Image to be padded. - divisor (int): Padded image edges will be multiple to divisor. - pad_val (Number | Sequence[Number]): Same as :func:`impad`. - - Returns: - ndarray: The padded image. - """ - pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor - pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor - return impad(img, shape=(pad_h, pad_w), pad_val=pad_val) - - -def cutout(img, shape, pad_val=0): - """Randomly cut out a rectangle from the original img. - - Args: - img (ndarray): Image to be cutout. - shape (int | tuple[int]): Expected cutout shape (h, w). If given as a - int, the value will be used for both h and w. - pad_val (int | float | tuple[int | float]): Values to be filled in the - cut area. Defaults to 0. - - Returns: - ndarray: The cutout image. - """ - - channels = 1 if img.ndim == 2 else img.shape[2] - if isinstance(shape, int): - cut_h, cut_w = shape, shape - else: - assert isinstance(shape, tuple) and len(shape) == 2, \ - f'shape must be a int or a tuple with length 2, but got type ' \ - f'{type(shape)} instead.' - cut_h, cut_w = shape - if isinstance(pad_val, (int, float)): - pad_val = tuple([pad_val] * channels) - elif isinstance(pad_val, tuple): - assert len(pad_val) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(pad_val), channels) - else: - raise TypeError(f'Invalid type {type(pad_val)} for `pad_val`') - - img_h, img_w = img.shape[:2] - y0 = np.random.uniform(img_h) - x0 = np.random.uniform(img_w) - - y1 = int(max(0, y0 - cut_h / 2.)) - x1 = int(max(0, x0 - cut_w / 2.)) - y2 = min(img_h, y1 + cut_h) - x2 = min(img_w, x1 + cut_w) - - if img.ndim == 2: - patch_shape = (y2 - y1, x2 - x1) - else: - patch_shape = (y2 - y1, x2 - x1, channels) - - img_cutout = img.copy() - patch = np.array( - pad_val, dtype=img.dtype) * np.ones( - patch_shape, dtype=img.dtype) - img_cutout[y1:y2, x1:x2, ...] = patch - - return img_cutout - - -def _get_shear_matrix(magnitude, direction='horizontal'): - """Generate the shear matrix for transformation. - - Args: - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - - Returns: - ndarray: The shear matrix with dtype float32. - """ - if direction == 'horizontal': - shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]]) - elif direction == 'vertical': - shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]]) - return shear_matrix - - -def imshear(img, - magnitude, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Shear an image. - - Args: - img (ndarray): Image to be sheared with format (h, w) - or (h, w, c). - magnitude (int | float): The magnitude used for shear. - direction (str): The flip direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The sheared image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`') - shear_matrix = _get_shear_matrix(magnitude, direction) - sheared = cv2.warpAffine( - img, - shear_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. shearing masks whose channels large - # than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return sheared - - -def _get_translate_matrix(offset, direction='horizontal'): - """Generate the translate matrix. - - Args: - offset (int | float): The offset used for translate. - direction (str): The translate direction, either - "horizontal" or "vertical". - - Returns: - ndarray: The translate matrix with dtype float32. - """ - if direction == 'horizontal': - translate_matrix = np.float32([[1, 0, offset], [0, 1, 0]]) - elif direction == 'vertical': - translate_matrix = np.float32([[1, 0, 0], [0, 1, offset]]) - return translate_matrix - - -def imtranslate(img, - offset, - direction='horizontal', - border_value=0, - interpolation='bilinear'): - """Translate an image. - - Args: - img (ndarray): Image to be translated with format - (h, w) or (h, w, c). - offset (int | float): The offset used for translate. - direction (str): The translate direction, either "horizontal" - or "vertical". - border_value (int | tuple[int]): Value used in case of a - constant border. - interpolation (str): Same as :func:`resize`. - - Returns: - ndarray: The translated image. - """ - assert direction in ['horizontal', - 'vertical'], f'Invalid direction: {direction}' - height, width = img.shape[:2] - if img.ndim == 2: - channels = 1 - elif img.ndim == 3: - channels = img.shape[-1] - if isinstance(border_value, int): - border_value = tuple([border_value] * channels) - elif isinstance(border_value, tuple): - assert len(border_value) == channels, \ - 'Expected the num of elements in tuple equals the channels' \ - 'of input image. Found {} vs {}'.format( - len(border_value), channels) - else: - raise ValueError( - f'Invalid type {type(border_value)} for `border_value`.') - translate_matrix = _get_translate_matrix(offset, direction) - translated = cv2.warpAffine( - img, - translate_matrix, - (width, height), - # Note case when the number elements in `border_value` - # greater than 3 (e.g. translating masks whose channels - # large than 3) will raise TypeError in `cv2.warpAffine`. - # Here simply slice the first 3 values in `border_value`. - borderValue=border_value[:3], - flags=cv2_interp_codes[interpolation]) - return translated diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/io.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/io.py deleted file mode 100755 index d47aaa845..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/io.py +++ /dev/null @@ -1,258 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os.path as osp -from pathlib import Path - -import cv2 -import numpy as np -from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION, - IMREAD_UNCHANGED) - -from mmcv.utils import check_file_exist, is_str, mkdir_or_exist - -try: - from turbojpeg import TJCS_RGB, TJPF_BGR, TJPF_GRAY, TurboJPEG -except ImportError: - TJCS_RGB = TJPF_GRAY = TJPF_BGR = TurboJPEG = None - -try: - from PIL import Image, ImageOps -except ImportError: - Image = None - -try: - import tifffile -except ImportError: - tifffile = None - -jpeg = None -supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] - -imread_flags = { - 'color': IMREAD_COLOR, - 'grayscale': IMREAD_GRAYSCALE, - 'unchanged': IMREAD_UNCHANGED, - 'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR, - 'grayscale_ignore_orientation': - IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE -} - -imread_backend = 'cv2' - - -def use_backend(backend): - """Select a backend for image decoding. - - Args: - backend (str): The image decoding backend type. Options are `cv2`, - `pillow`, `turbojpeg` (see https://github.com/lilohuang/PyTurboJPEG) - and `tifffile`. `turbojpeg` is faster but it only supports `.jpeg` - file format. - """ - assert backend in supported_backends - global imread_backend - imread_backend = backend - if imread_backend == 'turbojpeg': - if TurboJPEG is None: - raise ImportError('`PyTurboJPEG` is not installed') - global jpeg - if jpeg is None: - jpeg = TurboJPEG() - elif imread_backend == 'pillow': - if Image is None: - raise ImportError('`Pillow` is not installed') - elif imread_backend == 'tifffile': - if tifffile is None: - raise ImportError('`tifffile` is not installed') - - -def _jpegflag(flag='color', channel_order='bgr'): - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'color': - if channel_order == 'bgr': - return TJPF_BGR - elif channel_order == 'rgb': - return TJCS_RGB - elif flag == 'grayscale': - return TJPF_GRAY - else: - raise ValueError('flag must be "color" or "grayscale"') - - -def _pillow2array(img, flag='color', channel_order='bgr'): - """Convert a pillow image to numpy array. - - Args: - img (:obj:`PIL.Image.Image`): The image loaded using PIL - flag (str): Flags specifying the color type of a loaded image, - candidates are 'color', 'grayscale' and 'unchanged'. - Default to 'color'. - channel_order (str): The channel order of the output image array, - candidates are 'bgr' and 'rgb'. Default to 'bgr'. - - Returns: - np.ndarray: The converted numpy array - """ - channel_order = channel_order.lower() - if channel_order not in ['rgb', 'bgr']: - raise ValueError('channel order must be either "rgb" or "bgr"') - - if flag == 'unchanged': - array = np.array(img) - if array.ndim >= 3 and array.shape[2] >= 3: # color image - array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR - else: - # Handle exif orientation tag - if flag in ['color', 'grayscale']: - img = ImageOps.exif_transpose(img) - # If the image mode is not 'RGB', convert it to 'RGB' first. - if img.mode != 'RGB': - if img.mode != 'LA': - # Most formats except 'LA' can be directly converted to RGB - img = img.convert('RGB') - else: - # When the mode is 'LA', the default conversion will fill in - # the canvas with black, which sometimes shadows black objects - # in the foreground. - # - # Therefore, a random color (124, 117, 104) is used for canvas - img_rgba = img.convert('RGBA') - img = Image.new('RGB', img_rgba.size, (124, 117, 104)) - img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha - if flag in ['color', 'color_ignore_orientation']: - array = np.array(img) - if channel_order != 'rgb': - array = array[:, :, ::-1] # RGB to BGR - elif flag in ['grayscale', 'grayscale_ignore_orientation']: - img = img.convert('L') - array = np.array(img) - else: - raise ValueError( - 'flag must be "color", "grayscale", "unchanged", ' - f'"color_ignore_orientation" or "grayscale_ignore_orientation"' - f' but got {flag}') - return array - - -def imread(img_or_path, flag='color', channel_order='bgr', backend=None): - """Read an image. - - Args: - img_or_path (ndarray or str or Path): Either a numpy array or str or - pathlib.Path. If it is a numpy array (loaded image), then - it will be returned as is. - flag (str): Flags specifying the color type of a loaded image, - candidates are `color`, `grayscale`, `unchanged`, - `color_ignore_orientation` and `grayscale_ignore_orientation`. - By default, `cv2` and `pillow` backend would rotate the image - according to its EXIF info unless called with `unchanged` or - `*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend - always ignore image's EXIF info regardless of the flag. - The `turbojpeg` backend only supports `color` and `grayscale`. - channel_order (str): Order of channel, candidates are `bgr` and `rgb`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. - If backend is None, the global imread_backend specified by - ``mmcv.use_backend()`` will be used. Default: None. - - Returns: - ndarray: Loaded image array. - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError(f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow'") - if isinstance(img_or_path, Path): - img_or_path = str(img_or_path) - - if isinstance(img_or_path, np.ndarray): - return img_or_path - elif is_str(img_or_path): - check_file_exist(img_or_path, - f'img file does not exist: {img_or_path}') - if backend == 'turbojpeg': - with open(img_or_path, 'rb') as in_file: - img = jpeg.decode(in_file.read(), - _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - img = Image.open(img_or_path) - img = _pillow2array(img, flag, channel_order) - return img - elif backend == 'tifffile': - img = tifffile.imread(img_or_path) - return img - else: - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imread(img_or_path, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - else: - raise TypeError('"img" must be a numpy array or a str or ' - 'a pathlib.Path object') - - -def imfrombytes(content, flag='color', channel_order='bgr', backend=None): - """Read an image from bytes. - - Args: - content (bytes): Image bytes got from files or other streams. - flag (str): Same as :func:`imread`. - backend (str | None): The image decoding backend type. Options are - `cv2`, `pillow`, `turbojpeg`, `None`. If backend is None, the - global imread_backend specified by ``mmcv.use_backend()`` will be - used. Default: None. - - Returns: - ndarray: Loaded image array. - """ - - if backend is None: - backend = imread_backend - if backend not in supported_backends: - raise ValueError(f'backend: {backend} is not supported. Supported ' - "backends are 'cv2', 'turbojpeg', 'pillow'") - if backend == 'turbojpeg': - img = jpeg.decode(content, _jpegflag(flag, channel_order)) - if img.shape[-1] == 1: - img = img[:, :, 0] - return img - elif backend == 'pillow': - buff = io.BytesIO(content) - img = Image.open(buff) - img = _pillow2array(img, flag, channel_order) - return img - else: - img_np = np.frombuffer(content, np.uint8) - flag = imread_flags[flag] if is_str(flag) else flag - img = cv2.imdecode(img_np, flag) - if flag == IMREAD_COLOR and channel_order == 'rgb': - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) - return img - - -def imwrite(img, file_path, params=None, auto_mkdir=True): - """Write image to file. - - Args: - img (ndarray): Image array to be written. - file_path (str): Image file path. - params (None or list): Same as opencv :func:`imwrite` interface. - auto_mkdir (bool): If the parent folder of `file_path` does not exist, - whether to create it automatically. - - Returns: - bool: Successful or not. - """ - if auto_mkdir: - dir_name = osp.abspath(osp.dirname(file_path)) - mkdir_or_exist(dir_name) - return cv2.imwrite(file_path, img, params) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/misc.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/misc.py deleted file mode 100755 index dfc4a9c6e..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/misc.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np - -import mmcv - -try: - import torch -except ImportError: - torch = None - - -def tensor2imgs(tensor, mean=(0, 0, 0), std=(1, 1, 1), to_rgb=True): - """Convert tensor to 3-channel images. - - Args: - tensor (torch.Tensor): Tensor that contains multiple images, shape ( - N, C, H, W). - mean (tuple[float], optional): Mean of images. Defaults to (0, 0, 0). - std (tuple[float], optional): Standard deviation of images. - Defaults to (1, 1, 1). - to_rgb (bool, optional): Whether the tensor was converted to RGB - format in the first place. If so, convert it back to BGR. - Defaults to True. - - Returns: - list[np.ndarray]: A list that contains multiple images. - """ - - if torch is None: - raise RuntimeError('pytorch is not installed') - assert torch.is_tensor(tensor) and tensor.ndim == 4 - assert len(mean) == 3 - assert len(std) == 3 - - num_imgs = tensor.size(0) - mean = np.array(mean, dtype=np.float32) - std = np.array(std, dtype=np.float32) - imgs = [] - for img_id in range(num_imgs): - img = tensor[img_id, ...].cpu().numpy().transpose(1, 2, 0) - img = mmcv.imdenormalize( - img, mean, std, to_bgr=to_rgb).astype(np.uint8) - imgs.append(np.ascontiguousarray(img)) - return imgs diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/photometric.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/photometric.py deleted file mode 100755 index 5085d0120..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/image/photometric.py +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - -from ..utils import is_tuple_of -from .colorspace import bgr2gray, gray2bgr - - -def imnormalize(img, mean, std, to_rgb=True): - """Normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - img = img.copy().astype(np.float32) - return imnormalize_(img, mean, std, to_rgb) - - -def imnormalize_(img, mean, std, to_rgb=True): - """Inplace normalize an image with mean and std. - - Args: - img (ndarray): Image to be normalized. - mean (ndarray): The mean to be used for normalize. - std (ndarray): The std to be used for normalize. - to_rgb (bool): Whether to convert to rgb. - - Returns: - ndarray: The normalized image. - """ - # cv2 inplace normalization does not accept uint8 - assert img.dtype != np.uint8 - mean = np.float64(mean.reshape(1, -1)) - stdinv = 1 / np.float64(std.reshape(1, -1)) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - cv2.subtract(img, mean, img) # inplace - cv2.multiply(img, stdinv, img) # inplace - return img - - -def imdenormalize(img, mean, std, to_bgr=True): - assert img.dtype != np.uint8 - mean = mean.reshape(1, -1).astype(np.float64) - std = std.reshape(1, -1).astype(np.float64) - img = cv2.multiply(img, std) # make a copy - cv2.add(img, mean, img) # inplace - if to_bgr: - cv2.cvtColor(img, cv2.COLOR_RGB2BGR, img) # inplace - return img - - -def iminvert(img): - """Invert (negate) an image. - - Args: - img (ndarray): Image to be inverted. - - Returns: - ndarray: The inverted image. - """ - return np.full_like(img, 255) - img - - -def solarize(img, thr=128): - """Solarize an image (invert all pixel values above a threshold) - - Args: - img (ndarray): Image to be solarized. - thr (int): Threshold for solarizing (0 - 255). - - Returns: - ndarray: The solarized image. - """ - img = np.where(img < thr, img, 255 - img) - return img - - -def posterize(img, bits): - """Posterize an image (reduce the number of bits for each color channel) - - Args: - img (ndarray): Image to be posterized. - bits (int): Number of bits (1 to 8) to use for posterizing. - - Returns: - ndarray: The posterized image. - """ - shift = 8 - bits - img = np.left_shift(np.right_shift(img, shift), shift) - return img - - -def adjust_color(img, alpha=1, beta=None, gamma=0): - r"""It blends the source image and its gray image: - - .. math:: - output = img * alpha + gray\_img * beta + gamma - - Args: - img (ndarray): The input source image. - alpha (int | float): Weight for the source image. Default 1. - beta (int | float): Weight for the converted gray image. - If None, it's assigned the value (1 - `alpha`). - gamma (int | float): Scalar added to each sum. - Same as :func:`cv2.addWeighted`. Default 0. - - Returns: - ndarray: Colored image which has the same size and dtype as input. - """ - gray_img = bgr2gray(img) - gray_img = np.tile(gray_img[..., None], [1, 1, 3]) - if beta is None: - beta = 1 - alpha - colored_img = cv2.addWeighted(img, alpha, gray_img, beta, gamma) - if not colored_img.dtype == np.uint8: - # Note when the dtype of `img` is not the default `np.uint8` - # (e.g. np.float32), the value in `colored_img` got from cv2 - # is not guaranteed to be in range [0, 255], so here clip - # is needed. - colored_img = np.clip(colored_img, 0, 255) - return colored_img - - -def imequalize(img): - """Equalize the image histogram. - - This function applies a non-linear mapping to the input image, - in order to create a uniform distribution of grayscale values - in the output image. - - Args: - img (ndarray): Image to be equalized. - - Returns: - ndarray: The equalized image. - """ - - def _scale_channel(im, c): - """Scale the data in the corresponding channel.""" - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # For computing the step, filter out the nonzeros. - nonzero_histo = histo[histo > 0] - step = (np.sum(nonzero_histo) - nonzero_histo[-1]) // 255 - if not step: - lut = np.array(range(256)) - else: - # Compute the cumulative sum, shifted by step // 2 - # and then normalized by step. - lut = (np.cumsum(histo) + (step // 2)) // step - # Shift lut, prepending with 0. - lut = np.concatenate([[0], lut[:-1]], 0) - # handle potential integer overflow - lut[lut > 255] = 255 - # If step is zero, return the original image. - # Otherwise, index from lut. - return np.where(np.equal(step, 0), im, lut[im]) - - # Scales each channel independently and then stacks - # the result. - s1 = _scale_channel(img, 0) - s2 = _scale_channel(img, 1) - s3 = _scale_channel(img, 2) - equalized_img = np.stack([s1, s2, s3], axis=-1) - return equalized_img.astype(img.dtype) - - -def adjust_brightness(img, factor=1.): - """Adjust image brightness. - - This function controls the brightness of an image. An - enhancement factor of 0.0 gives a black image. - A factor of 1.0 gives the original image. This function - blends the source image and the degenerated black image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be brightened. - factor (float): A value controls the enhancement. - Factor 1.0 returns the original image, lower - factors mean less color (brightness, contrast, - etc), and higher values more. Default 1. - - Returns: - ndarray: The brightened image. - """ - degenerated = np.zeros_like(img) - # Note manually convert the dtype to np.float32, to - # achieve as close results as PIL.ImageEnhance.Brightness. - # Set beta=1-factor, and gamma=0 - brightened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - brightened_img = np.clip(brightened_img, 0, 255) - return brightened_img.astype(img.dtype) - - -def adjust_contrast(img, factor=1.): - """Adjust image contrast. - - This function controls the contrast of an image. An - enhancement factor of 0.0 gives a solid grey - image. A factor of 1.0 gives the original image. It - blends the source image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be contrasted. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - - Returns: - ndarray: The contrasted image. - """ - gray_img = bgr2gray(img) - hist = np.histogram(gray_img, 256, (0, 255))[0] - mean = round(np.sum(gray_img) / np.sum(hist)) - degenerated = (np.ones_like(img[..., 0]) * mean).astype(img.dtype) - degenerated = gray2bgr(degenerated) - contrasted_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - contrasted_img = np.clip(contrasted_img, 0, 255) - return contrasted_img.astype(img.dtype) - - -def auto_contrast(img, cutoff=0): - """Auto adjust image contrast. - - This function maximize (normalize) image contrast by first removing cutoff - percent of the lightest and darkest pixels from the histogram and remapping - the image so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - Args: - img (ndarray): Image to be contrasted. BGR order. - cutoff (int | float | tuple): The cutoff percent of the lightest and - darkest pixels to be removed. If given as tuple, it shall be - (low, high). Otherwise, the single value will be used for both. - Defaults to 0. - - Returns: - ndarray: The contrasted image. - """ - - def _auto_contrast_channel(im, c, cutoff): - im = im[:, :, c] - # Compute the histogram of the image channel. - histo = np.histogram(im, 256, (0, 255))[0] - # Remove cut-off percent pixels from histo - histo_sum = np.cumsum(histo) - cut_low = histo_sum[-1] * cutoff[0] // 100 - cut_high = histo_sum[-1] - histo_sum[-1] * cutoff[1] // 100 - histo_sum = np.clip(histo_sum, cut_low, cut_high) - cut_low - histo = np.concatenate([[histo_sum[0]], np.diff(histo_sum)], 0) - - # Compute mapping - low, high = np.nonzero(histo)[0][0], np.nonzero(histo)[0][-1] - # If all the values have been cut off, return the origin img - if low >= high: - return im - scale = 255.0 / (high - low) - offset = -low * scale - lut = np.array(range(256)) - lut = lut * scale + offset - lut = np.clip(lut, 0, 255) - return lut[im] - - if isinstance(cutoff, (int, float)): - cutoff = (cutoff, cutoff) - else: - assert isinstance(cutoff, tuple), 'cutoff must be of type int, ' \ - f'float or tuple, but got {type(cutoff)} instead.' - # Auto adjusts contrast for each channel independently and then stacks - # the result. - s1 = _auto_contrast_channel(img, 0, cutoff) - s2 = _auto_contrast_channel(img, 1, cutoff) - s3 = _auto_contrast_channel(img, 2, cutoff) - contrasted_img = np.stack([s1, s2, s3], axis=-1) - return contrasted_img.astype(img.dtype) - - -def adjust_sharpness(img, factor=1., kernel=None): - """Adjust image sharpness. - - This function controls the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image. A - factor of 1.0 gives the original image. And a factor - of 2.0 gives a sharpened image. It blends the source - image and the degenerated mean image: - - .. math:: - output = img * factor + degenerated * (1 - factor) - - Args: - img (ndarray): Image to be sharpened. BGR order. - factor (float): Same as :func:`mmcv.adjust_brightness`. - kernel (np.ndarray, optional): Filter kernel to be applied on the img - to obtain the degenerated img. Defaults to None. - - Note: - No value sanity check is enforced on the kernel set by users. So with - an inappropriate kernel, the ``adjust_sharpness`` may fail to perform - the function its name indicates but end up performing whatever - transform determined by the kernel. - - Returns: - ndarray: The sharpened image. - """ - - if kernel is None: - # adopted from PIL.ImageFilter.SMOOTH - kernel = np.array([[1., 1., 1.], [1., 5., 1.], [1., 1., 1.]]) / 13 - assert isinstance(kernel, np.ndarray), \ - f'kernel must be of type np.ndarray, but got {type(kernel)} instead.' - assert kernel.ndim == 2, \ - f'kernel must have a dimension of 2, but got {kernel.ndim} instead.' - - degenerated = cv2.filter2D(img, -1, kernel) - sharpened_img = cv2.addWeighted( - img.astype(np.float32), factor, degenerated.astype(np.float32), - 1 - factor, 0) - sharpened_img = np.clip(sharpened_img, 0, 255) - return sharpened_img.astype(img.dtype) - - -def adjust_lighting(img, eigval, eigvec, alphastd=0.1, to_rgb=True): - """AlexNet-style PCA jitter. - - This data augmentation is proposed in `ImageNet Classification with Deep - Convolutional Neural Networks - `_. - - Args: - img (ndarray): Image to be adjusted lighting. BGR order. - eigval (ndarray): the eigenvalue of the convariance matrix of pixel - values, respectively. - eigvec (ndarray): the eigenvector of the convariance matrix of pixel - values, respectively. - alphastd (float): The standard deviation for distribution of alpha. - Defaults to 0.1 - to_rgb (bool): Whether to convert img to rgb. - - Returns: - ndarray: The adjusted image. - """ - assert isinstance(eigval, np.ndarray) and isinstance(eigvec, np.ndarray), \ - f'eigval and eigvec should both be of type np.ndarray, got ' \ - f'{type(eigval)} and {type(eigvec)} instead.' - - assert eigval.ndim == 1 and eigvec.ndim == 2 - assert eigvec.shape == (3, eigval.shape[0]) - n_eigval = eigval.shape[0] - assert isinstance(alphastd, float), 'alphastd should be of type float, ' \ - f'got {type(alphastd)} instead.' - - img = img.copy().astype(np.float32) - if to_rgb: - cv2.cvtColor(img, cv2.COLOR_BGR2RGB, img) # inplace - - alpha = np.random.normal(0, alphastd, n_eigval) - alter = eigvec \ - * np.broadcast_to(alpha.reshape(1, n_eigval), (3, n_eigval)) \ - * np.broadcast_to(eigval.reshape(1, n_eigval), (3, n_eigval)) - alter = np.broadcast_to(alter.sum(axis=1).reshape(1, 1, 3), img.shape) - img_adjusted = img + alter - return img_adjusted - - -def lut_transform(img, lut_table): - """Transform array by look-up table. - - The function lut_transform fills the output array with values from the - look-up table. Indices of the entries are taken from the input array. - - Args: - img (ndarray): Image to be transformed. - lut_table (ndarray): look-up table of 256 elements; in case of - multi-channel input array, the table should either have a single - channel (in this case the same table is used for all channels) or - the same number of channels as in the input array. - - Returns: - ndarray: The transformed image. - """ - assert isinstance(img, np.ndarray) - assert 0 <= np.min(img) and np.max(img) <= 255 - assert isinstance(lut_table, np.ndarray) - assert lut_table.shape == (256, ) - - return cv2.LUT(np.array(img, dtype=np.uint8), lut_table) - - -def clahe(img, clip_limit=40.0, tile_grid_size=(8, 8)): - """Use CLAHE method to process the image. - - See `ZUIDERVELD,K. Contrast Limited Adaptive Histogram Equalization[J]. - Graphics Gems, 1994:474-485.` for more information. - - Args: - img (ndarray): Image to be processed. - clip_limit (float): Threshold for contrast limiting. Default: 40.0. - tile_grid_size (tuple[int]): Size of grid for histogram equalization. - Input image will be divided into equally sized rectangular tiles. - It defines the number of tiles in row and column. Default: (8, 8). - - Returns: - ndarray: The processed image. - """ - assert isinstance(img, np.ndarray) - assert img.ndim == 2 - assert isinstance(clip_limit, (float, int)) - assert is_tuple_of(tile_grid_size, int) - assert len(tile_grid_size) == 2 - - clahe = cv2.createCLAHE(clip_limit, tile_grid_size) - return clahe.apply(np.array(img, dtype=np.uint8)) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/__init__.py deleted file mode 100755 index 8976a6654..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .deprecated_wrappers import Conv2d_deprecated as Conv2d -from .deprecated_wrappers import ConvTranspose2d_deprecated as ConvTranspose2d -from .deprecated_wrappers import Linear_deprecated as Linear -from .deprecated_wrappers import MaxPool2d_deprecated as MaxPool2d -from .info import (get_compiler_version, get_compiling_cuda_version, - get_onnxruntime_op_path) - -from .modulated_deform_conv import (ModulatedDeformConv2d, - ModulatedDeformConv2dPack, - modulated_deform_conv2d) - -from .sync_bn import SyncBatchNorm - - diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp deleted file mode 100755 index a1e926adb..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/cuda/common_cuda_helper.hpp +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef COMMON_CUDA_HELPER -#define COMMON_CUDA_HELPER - -#include - -#define CUDA_1D_KERNEL_LOOP(i, n) \ - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ - i += blockDim.x * gridDim.x) - -#define THREADS_PER_BLOCK 512 - -#define DIVUP(m, n) ((m) / (n) + ((m) % (n) > 0)) - -inline int GET_BLOCKS(const int N) { - int optimal_block_num = (N + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; - int max_block_num = 4096; - return std::min(optimal_block_num, max_block_num); -} - -template -__device__ T bilinear_interpolate(const T* input, const int height, - const int width, T y, T x, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) return 0; - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - int y_low = (int)y; - int x_low = (int)x; - int y_high; - int x_high; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - // do bilinear interpolation - T v1 = input[y_low * width + x_low]; - T v2 = input[y_low * width + x_high]; - T v3 = input[y_high * width + x_low]; - T v4 = input[y_high * width + x_high]; - T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - return val; -} - -template -__device__ void bilinear_interpolate_gradient( - const int height, const int width, T y, T x, T& w1, T& w2, T& w3, T& w4, - int& x_low, int& x_high, int& y_low, int& y_high, - const int index /* index for debug only*/) { - // deal with cases that inverse elements are out of feature map boundary - if (y < -1.0 || y > height || x < -1.0 || x > width) { - // empty - w1 = w2 = w3 = w4 = 0.; - x_low = x_high = y_low = y_high = -1; - return; - } - - if (y <= 0) y = 0; - if (x <= 0) x = 0; - - y_low = (int)y; - x_low = (int)x; - - if (y_low >= height - 1) { - y_high = y_low = height - 1; - y = (T)y_low; - } else { - y_high = y_low + 1; - } - - if (x_low >= width - 1) { - x_high = x_low = width - 1; - x = (T)x_low; - } else { - x_high = x_low + 1; - } - - T ly = y - y_low; - T lx = x - x_low; - T hy = 1. - ly, hx = 1. - lx; - - // reference in forward - // T v1 = input[y_low * width + x_low]; - // T v2 = input[y_low * width + x_high]; - // T v3 = input[y_high * width + x_low]; - // T v4 = input[y_high * width + x_high]; - // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - - w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; - - return; -} -#endif // COMMON_CUDA_HELPER diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/cuda/modulated_deform_conv_cuda_kernel.cuh b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/cuda/modulated_deform_conv_cuda_kernel.cuh deleted file mode 100755 index ca0e91a25..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/cuda/modulated_deform_conv_cuda_kernel.cuh +++ /dev/null @@ -1,399 +0,0 @@ -/*! - ******************* BEGIN Caffe Copyright Notice and Disclaimer - ***************** - * - * COPYRIGHT - * - * All contributions by the University of California: - * Copyright (c) 2014-2017 The Regents of the University of California (Regents) - * All rights reserved. - * - * All other contributions: - * Copyright (c) 2014-2017, the respective contributors - * All rights reserved. - * - * Caffe uses a shared copyright model: each contributor holds copyright over - * their contributions to Caffe. The project versioning records all such - * contribution and copyright details. If a contributor wants to further mark - * their specific copyright on a particular contribution, they should indicate - * their copyright solely in the commit message of the change when it is - * committed. - * - * LICENSE - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - *this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - *AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - *IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - *FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - *DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - *SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - *CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - *OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - *OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * CONTRIBUTION AGREEMENT - * - * By contributing to the BVLC/caffe repository through pull-request, comment, - * or otherwise, the contributor releases their content to the - * license and copyright terms herein. - * - ***************** END Caffe Copyright Notice and Disclaimer - ********************* - * - * Copyright (c) 2018 Microsoft - * Licensed under The MIT License [see LICENSE for details] - * \file modulated_deformable_im2col.cuh - * \brief Function definitions of converting an image to - * column matrix based on kernel, padding, dilation, and offset. - * These functions are mainly used in deformable convolution operators. - * \ref: https://arxiv.org/abs/1703.06211 - * \author Yuwen Xiong, Haozhi Qi, Jifeng Dai, Xizhou Zhu, Han Hu, Dazhi Cheng - */ - -// modified from -// https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/blob/mmdetection/mmdet/ops/dcn/src/deform_conv_cuda_kernel.cu - -#ifndef MODULATED_DEFORM_CONV_CUDA_KERNEL_CUH -#define MODULATED_DEFORM_CONV_CUDA_KERNEL_CUH - -#include -#ifdef MMCV_WITH_TRT -#include "common_cuda_helper.hpp" -#else // MMCV_WITH_TRT -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else // MMCV_USE_PARROTS -#include "pytorch_cuda_helper.hpp" -#endif // MMCV_USE_PARROTS -#endif // MMCV_WITH_TRT - -template -__device__ T dmcn_im2col_bilinear(const T *input, const int data_width, - const int height, const int width, T h, T w) { - int h_low = floorf(h); - int w_low = floorf(w); - int h_high = h_low + 1; - int w_high = w_low + 1; - - T lh = h - h_low; - T lw = w - w_low; - T hh = 1 - lh, hw = 1 - lw; - - T v1 = 0; - if (h_low >= 0 && w_low >= 0) v1 = input[h_low * data_width + w_low]; - T v2 = 0; - if (h_low >= 0 && w_high <= width - 1) - v2 = input[h_low * data_width + w_high]; - T v3 = 0; - if (h_high <= height - 1 && w_low >= 0) - v3 = input[h_high * data_width + w_low]; - T v4 = 0; - if (h_high <= height - 1 && w_high <= width - 1) - v4 = input[h_high * data_width + w_high]; - - T w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - return val; -} - -template -__device__ T dmcn_get_gradient_weight(T argmax_h, T argmax_w, const int h, - const int w, const int height, - const int width) { - if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || - argmax_w >= width) { - // empty - return 0; - } - - int argmax_h_low = floorf(argmax_h); - int argmax_w_low = floorf(argmax_w); - int argmax_h_high = argmax_h_low + 1; - int argmax_w_high = argmax_w_low + 1; - - T weight = 0; - if (h == argmax_h_low && w == argmax_w_low) - weight = (h + 1 - argmax_h) * (w + 1 - argmax_w); - if (h == argmax_h_low && w == argmax_w_high) - weight = (h + 1 - argmax_h) * (argmax_w + 1 - w); - if (h == argmax_h_high && w == argmax_w_low) - weight = (argmax_h + 1 - h) * (w + 1 - argmax_w); - if (h == argmax_h_high && w == argmax_w_high) - weight = (argmax_h + 1 - h) * (argmax_w + 1 - w); - return weight; -} - -template -__device__ T dmcn_get_coordinate_weight(T argmax_h, T argmax_w, - const int height, const int width, - const T *im_data, const int data_width, - const int bp_dir) { - if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || - argmax_w >= width) { - // empty - return 0; - } - - int argmax_h_low = floorf(argmax_h); - int argmax_w_low = floorf(argmax_w); - int argmax_h_high = argmax_h_low + 1; - int argmax_w_high = argmax_w_low + 1; - - T weight = 0; - - if (bp_dir == 0) { - if (argmax_h_low >= 0 && argmax_w_low >= 0) - weight += -1 * (argmax_w_low + 1 - argmax_w) * - im_data[argmax_h_low * data_width + argmax_w_low]; - if (argmax_h_low >= 0 && argmax_w_high <= width - 1) - weight += -1 * (argmax_w - argmax_w_low) * - im_data[argmax_h_low * data_width + argmax_w_high]; - if (argmax_h_high <= height - 1 && argmax_w_low >= 0) - weight += (argmax_w_low + 1 - argmax_w) * - im_data[argmax_h_high * data_width + argmax_w_low]; - if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) - weight += (argmax_w - argmax_w_low) * - im_data[argmax_h_high * data_width + argmax_w_high]; - } else if (bp_dir == 1) { - if (argmax_h_low >= 0 && argmax_w_low >= 0) - weight += -1 * (argmax_h_low + 1 - argmax_h) * - im_data[argmax_h_low * data_width + argmax_w_low]; - if (argmax_h_low >= 0 && argmax_w_high <= width - 1) - weight += (argmax_h_low + 1 - argmax_h) * - im_data[argmax_h_low * data_width + argmax_w_high]; - if (argmax_h_high <= height - 1 && argmax_w_low >= 0) - weight += -1 * (argmax_h - argmax_h_low) * - im_data[argmax_h_high * data_width + argmax_w_low]; - if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) - weight += (argmax_h - argmax_h_low) * - im_data[argmax_h_high * data_width + argmax_w_high]; - } - - return weight; -} - -template -__global__ void modulated_deformable_im2col_gpu_kernel( - const int n, const T *data_im, const T *data_offset, const T *data_mask, - const int height, const int width, const int kernel_h, const int kernel_w, - const int pad_h, const int pad_w, const int stride_h, const int stride_w, - const int dilation_h, const int dilation_w, - const int channel_per_deformable_group, const int batch_size, - const int num_channels, const int deformable_group, const int height_col, - const int width_col, T *data_col) { - CUDA_1D_KERNEL_LOOP(index, n) { - // index index of output matrix - const int w_col = index % width_col; - const int h_col = (index / width_col) % height_col; - const int b_col = (index / width_col / height_col) % batch_size; - const int c_im = (index / width_col / height_col) / batch_size; - const int c_col = c_im * kernel_h * kernel_w; - - // compute deformable group index - const int deformable_group_index = c_im / channel_per_deformable_group; - - const int h_in = h_col * stride_h - pad_h; - const int w_in = w_col * stride_w - pad_w; - - T *data_col_ptr = - data_col + - ((c_col * batch_size + b_col) * height_col + h_col) * width_col + w_col; - const T *data_im_ptr = - data_im + (b_col * num_channels + c_im) * height * width; - const T *data_offset_ptr = - data_offset + (b_col * deformable_group + deformable_group_index) * 2 * - kernel_h * kernel_w * height_col * width_col; - - const T *data_mask_ptr = - data_mask + (b_col * deformable_group + deformable_group_index) * - kernel_h * kernel_w * height_col * width_col; - - for (int i = 0; i < kernel_h; ++i) { - for (int j = 0; j < kernel_w; ++j) { - const int data_offset_h_ptr = - ((2 * (i * kernel_w + j)) * height_col + h_col) * width_col + w_col; - const int data_offset_w_ptr = - ((2 * (i * kernel_w + j) + 1) * height_col + h_col) * width_col + - w_col; - const int data_mask_hw_ptr = - ((i * kernel_w + j) * height_col + h_col) * width_col + w_col; - const T offset_h = data_offset_ptr[data_offset_h_ptr]; - const T offset_w = data_offset_ptr[data_offset_w_ptr]; - const T mask = data_mask_ptr[data_mask_hw_ptr]; - T val = static_cast(0); - const T h_im = h_in + i * dilation_h + offset_h; - const T w_im = w_in + j * dilation_w + offset_w; - if (h_im > -1 && w_im > -1 && h_im < height && w_im < width) - val = dmcn_im2col_bilinear(data_im_ptr, width, height, width, h_im, - w_im); - *data_col_ptr = val * mask; - data_col_ptr += batch_size * height_col * width_col; - } - } - } -} - -template -__global__ void modulated_deformable_col2im_gpu_kernel( - const int n, const T *data_col, const T *data_offset, const T *data_mask, - const int channels, const int height, const int width, const int kernel_h, - const int kernel_w, const int pad_h, const int pad_w, const int stride_h, - const int stride_w, const int dilation_h, const int dilation_w, - const int channel_per_deformable_group, const int batch_size, - const int deformable_group, const int height_col, const int width_col, - T *grad_im) { - CUDA_1D_KERNEL_LOOP(index, n) { - const int j = (index / width_col / height_col / batch_size) % kernel_w; - const int i = - (index / width_col / height_col / batch_size / kernel_w) % kernel_h; - const int c = - index / width_col / height_col / batch_size / kernel_w / kernel_h; - // compute the start and end of the output - - const int deformable_group_index = c / channel_per_deformable_group; - - int w_out = index % width_col; - int h_out = (index / width_col) % height_col; - int b = (index / width_col / height_col) % batch_size; - int w_in = w_out * stride_w - pad_w; - int h_in = h_out * stride_h - pad_h; - - const T *data_offset_ptr = - data_offset + (b * deformable_group + deformable_group_index) * 2 * - kernel_h * kernel_w * height_col * width_col; - const T *data_mask_ptr = - data_mask + (b * deformable_group + deformable_group_index) * kernel_h * - kernel_w * height_col * width_col; - const int data_offset_h_ptr = - ((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out; - const int data_offset_w_ptr = - ((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out; - const int data_mask_hw_ptr = - ((i * kernel_w + j) * height_col + h_out) * width_col + w_out; - const T offset_h = data_offset_ptr[data_offset_h_ptr]; - const T offset_w = data_offset_ptr[data_offset_w_ptr]; - const T mask = data_mask_ptr[data_mask_hw_ptr]; - const T cur_inv_h_data = h_in + i * dilation_h + offset_h; - const T cur_inv_w_data = w_in + j * dilation_w + offset_w; - - const T cur_top_grad = data_col[index] * mask; - const int cur_h = (int)cur_inv_h_data; - const int cur_w = (int)cur_inv_w_data; - for (int dy = -2; dy <= 2; dy++) { - for (int dx = -2; dx <= 2; dx++) { - if (cur_h + dy >= 0 && cur_h + dy < height && cur_w + dx >= 0 && - cur_w + dx < width && abs(cur_inv_h_data - (cur_h + dy)) < 1 && - abs(cur_inv_w_data - (cur_w + dx)) < 1) { - int cur_bottom_grad_pos = - ((b * channels + c) * height + cur_h + dy) * width + cur_w + dx; - T weight = - dmcn_get_gradient_weight(cur_inv_h_data, cur_inv_w_data, - cur_h + dy, cur_w + dx, height, width); - atomicAdd(grad_im + cur_bottom_grad_pos, weight * cur_top_grad); - } - } - } - } -} - -template -__global__ void modulated_deformable_col2im_coord_gpu_kernel( - const int n, const T *data_col, const T *data_im, const T *data_offset, - const T *data_mask, const int channels, const int height, const int width, - const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int channel_per_deformable_group, - const int batch_size, const int offset_channels, const int deformable_group, - const int height_col, const int width_col, T *grad_offset, T *grad_mask) { - CUDA_1D_KERNEL_LOOP(index, n) { - T val = 0, mval = 0; - int w = index % width_col; - int h = (index / width_col) % height_col; - int c = (index / width_col / height_col) % offset_channels; - int b = (index / width_col / height_col) / offset_channels; - // compute the start and end of the output - - const int deformable_group_index = c / (2 * kernel_h * kernel_w); - const int col_step = kernel_h * kernel_w; - int cnt = 0; - const T *data_col_ptr = data_col + deformable_group_index * - channel_per_deformable_group * - batch_size * width_col * height_col; - const T *data_im_ptr = - data_im + (b * deformable_group + deformable_group_index) * - channel_per_deformable_group / kernel_h / kernel_w * - height * width; - const T *data_offset_ptr = - data_offset + (b * deformable_group + deformable_group_index) * 2 * - kernel_h * kernel_w * height_col * width_col; - const T *data_mask_ptr = - data_mask + (b * deformable_group + deformable_group_index) * kernel_h * - kernel_w * height_col * width_col; - - const int offset_c = c - deformable_group_index * 2 * kernel_h * kernel_w; - - for (int col_c = (offset_c / 2); col_c < channel_per_deformable_group; - col_c += col_step) { - const int col_pos = - (((col_c * batch_size + b) * height_col) + h) * width_col + w; - const int bp_dir = offset_c % 2; - - int j = (col_pos / width_col / height_col / batch_size) % kernel_w; - int i = - (col_pos / width_col / height_col / batch_size / kernel_w) % kernel_h; - int w_out = col_pos % width_col; - int h_out = (col_pos / width_col) % height_col; - int w_in = w_out * stride_w - pad_w; - int h_in = h_out * stride_h - pad_h; - const int data_offset_h_ptr = - (((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out); - const int data_offset_w_ptr = - (((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + - w_out); - const int data_mask_hw_ptr = - (((i * kernel_w + j) * height_col + h_out) * width_col + w_out); - const T offset_h = data_offset_ptr[data_offset_h_ptr]; - const T offset_w = data_offset_ptr[data_offset_w_ptr]; - const T mask = data_mask_ptr[data_mask_hw_ptr]; - T inv_h = h_in + i * dilation_h + offset_h; - T inv_w = w_in + j * dilation_w + offset_w; - if (inv_h <= -1 || inv_w <= -1 || inv_h >= height || inv_w >= width) - inv_h = inv_w = -2; - else - mval += data_col_ptr[col_pos] * - dmcn_im2col_bilinear(data_im_ptr + cnt * height * width, width, - height, width, inv_h, inv_w); - const T weight = dmcn_get_coordinate_weight( - inv_h, inv_w, height, width, data_im_ptr + cnt * height * width, - width, bp_dir); - val += weight * data_col_ptr[col_pos] * mask; - cnt += 1; - } - // KERNEL_ASSIGN(grad_offset[index], offset_req, val); - grad_offset[index] = val; - if (offset_c % 2 == 0) - // KERNEL_ASSIGN(grad_mask[(((b * deformable_group + - // deformable_group_index) * kernel_h * kernel_w + offset_c / 2) * - // height_col + h) * width_col + w], mask_req, mval); - grad_mask[(((b * deformable_group + deformable_group_index) * kernel_h * - kernel_w + - offset_c / 2) * - height_col + - h) * - width_col + - w] = mval; - } -} - -#endif // MODULATED_DEFORM_CONV_CUDA_KERNEL_CUH diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh deleted file mode 100755 index 4ec6a4668..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/cuda/sync_bn_cuda_kernel.cuh +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#ifndef SYNCBN_CUDA_KERNEL_CUH -#define SYNCBN_CUDA_KERNEL_CUH - -#ifdef MMCV_USE_PARROTS -#include "parrots_cuda_helper.hpp" -#else -#include "pytorch_cuda_helper.hpp" -#endif - -template -__global__ void sync_bn_forward_mean_cuda_kernel(const T *input, float *mean, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += input[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_mean_cuda_kernel(const phalf *input, - float *mean, int num, - int channels, int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer[tid] += static_cast(input[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - mean[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_var_cuda_kernel(const T *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = input[index] - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template <> -__global__ void sync_bn_forward_var_cuda_kernel(const phalf *input, - const float *mean, float *var, - int num, int channels, - int spatial) { - __shared__ float buffer[THREADS_PER_BLOCK]; - int tid = threadIdx.x; - int c = blockIdx.x; - buffer[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - float td = static_cast(input[index]) - mean[c]; - buffer[tid] += td * td; - } - __syncthreads(); - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer[tid] += buffer[tid + s]; - } - __syncthreads(); - } - int total = num * spatial; - if (tid == 0) { - var[c] = buffer[0] / total; - } -} - -template -__global__ void sync_bn_forward_output_cuda_kernel( - const T *input, const float *mean, const float *var, float *running_mean, - float *running_var, const float *weight, const float *bias, float *norm, - float *std, T *output, int num, int channels, int spatial, float eps, - float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = (input[index] - mean_value) / std_value; - output[index] = norm[index] * weight_value + bias_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - (input[index] - mean_value) / std_value * weight_value + bias_value; - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = norm[index] = (input[index] - mean_value) / std_value; - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = (input[index] - mean_value) / std_value; - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template <> -__global__ void sync_bn_forward_output_cuda_kernel( - const phalf *input, const float *mean, const float *var, - float *running_mean, float *running_var, const float *weight, - const float *bias, float *norm, float *std, phalf *output, int num, - int channels, int spatial, float eps, float momentum, int group_size) { - int tid = threadIdx.x; - int c = blockIdx.x; - float mean_value = mean[c]; - float std_value = sqrt(var[c] + eps); - if (weight != nullptr) { - float weight_value = weight[c]; - float bias_value = bias[c]; - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = - static_cast(norm[index] * weight_value + bias_value); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = - static_cast((static_cast(input[index]) - mean_value) / - std_value * weight_value + - bias_value); - } - } - } else { - if (norm != nullptr) { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - norm[index] = - (static_cast(input[index]) - mean_value) / std_value; - output[index] = static_cast(norm[index]); - } - } else { - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = - (i / spatial) * channels * spatial + c * spatial + i % spatial; - output[index] = static_cast( - (static_cast(input[index]) - mean_value) / std_value); - } - } - } - if (tid == 0) { - if (std != nullptr) std[c] = std_value; - if (running_mean != nullptr) { - running_mean[c] = - momentum * mean_value + (1 - momentum) * running_mean[c]; - int count = num * spatial * group_size; - float var_unbias = count > 1 ? var[c] * count / (count - 1) : var[c]; - running_var[c] = momentum * var_unbias + (1 - momentum) * running_var[c]; - } - } -} - -template -__global__ void sync_bn_backward_param_cuda_kernel(const T *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += grad_output[index] * norm[index]; - buffer2[tid] += grad_output[index]; - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template <> -__global__ void sync_bn_backward_param_cuda_kernel(const phalf *grad_output, - const float *norm, - float *grad_weight, - float *grad_bias, int num, - int channels, int spatial) { - __shared__ float buffer1[THREADS_PER_BLOCK]; - __shared__ float buffer2[THREADS_PER_BLOCK]; - - int tid = threadIdx.x; - int c = blockIdx.x; - buffer1[tid] = buffer2[tid] = 0; - for (int i = tid; i < num * spatial; i += blockDim.x) { - int index = (i / spatial) * channels * spatial + c * spatial + i % spatial; - buffer1[tid] += static_cast(grad_output[index]) * norm[index]; - buffer2[tid] += static_cast(grad_output[index]); - } - __syncthreads(); - - for (int s = blockDim.x / 2; s > 0; s >>= 1) { - if (tid < s) { - buffer1[tid] += buffer1[tid + s]; - buffer2[tid] += buffer2[tid + s]; - } - __syncthreads(); - } - if (tid == 0) { - grad_weight[c] = buffer1[0]; - grad_bias[c] = buffer2[0]; - } -} - -template -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const T *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, T *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = - weight[c] * - (grad_output[index] - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]; - } -} - -template <> -__global__ void sync_bn_backward_data_cuda_kernel( - int output_size, const phalf *grad_output, const float *weight, - const float *grad_weight, const float *grad_bias, const float *norm, - const float *std, phalf *grad_input, int num, int channels, int spatial) { - int factor = num * spatial; - CUDA_1D_KERNEL_LOOP(index, output_size) { - int c = (index / spatial) % channels; - grad_input[index] = static_cast( - weight[c] * - (static_cast(grad_output[index]) - - (grad_weight[c] * norm[index] + grad_bias[c]) / factor) / - std[c]); - } -} - -#endif // SYNCBN_CUDA_KERNEL_CUH diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp deleted file mode 100755 index c7f9f35b7..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/pytorch_cpp_helper.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef PYTORCH_CPP_HELPER -#define PYTORCH_CPP_HELPER -#include - -#include - -using namespace at; - -#define DIVUP(m, n) ((m) / (n) + ((m) % (n) > 0)) - -#define CHECK_CUDA(x) \ - TORCH_CHECK(x.device().is_cuda(), #x " must be a CUDA tensor") -#define CHECK_CPU(x) \ - TORCH_CHECK(!x.device().is_cuda(), #x " must be a CPU tensor") -#define CHECK_CONTIGUOUS(x) \ - TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") -#define CHECK_CUDA_INPUT(x) \ - CHECK_CUDA(x); \ - CHECK_CONTIGUOUS(x) -#define CHECK_CPU_INPUT(x) \ - CHECK_CPU(x); \ - CHECK_CONTIGUOUS(x) - -#endif // PYTORCH_CPP_HELPER diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp deleted file mode 100755 index 9869b535f..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/common/pytorch_cuda_helper.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PYTORCH_CUDA_HELPER -#define PYTORCH_CUDA_HELPER - -#include -#include -#include - -#include -#include - -#include "common_cuda_helper.hpp" - -using at::Half; -using at::Tensor; -using phalf = at::Half; - -#define __PHALF(x) (x) - -#endif // PYTORCH_CUDA_HELPER diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/cuda/modulated_deform_conv_cuda.cu b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/cuda/modulated_deform_conv_cuda.cu deleted file mode 100755 index 27b706702..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/cuda/modulated_deform_conv_cuda.cu +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "modulated_deform_conv_cuda_kernel.cuh" -#include "pytorch_cuda_helper.hpp" - -void modulated_deformable_im2col_cuda( - const Tensor data_im, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor data_col) { - // num_axes should be smaller than block size - const int channel_per_deformable_group = channels / deformable_group; - const int num_kernels = channels * batch_size * height_col * width_col; - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - data_im.scalar_type(), "modulated_deformable_im2col_gpu", ([&] { - const scalar_t *data_im_ = data_im.data_ptr(); - const scalar_t *data_offset_ = data_offset.data_ptr(); - const scalar_t *data_mask_ = data_mask.data_ptr(); - scalar_t *data_col_ = data_col.data_ptr(); - - modulated_deformable_im2col_gpu_kernel<<< - GET_BLOCKS(num_kernels), THREADS_PER_BLOCK, 0, - at::cuda::getCurrentCUDAStream()>>>( - num_kernels, data_im_, data_offset_, data_mask_, height_im, - width_im, kernel_h, kenerl_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, channel_per_deformable_group, batch_size, - channels, deformable_group, height_col, width_col, data_col_); - })); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void modulated_deformable_col2im_cuda( - const Tensor data_col, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor grad_im) { - const int channel_per_deformable_group = channels / deformable_group; - const int num_kernels = - channels * kernel_h * kernel_w * batch_size * height_col * width_col; - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - data_col.scalar_type(), "modulated_deformable_col2im_gpu", ([&] { - const scalar_t *data_col_ = data_col.data_ptr(); - const scalar_t *data_offset_ = data_offset.data_ptr(); - const scalar_t *data_mask_ = data_mask.data_ptr(); - scalar_t *grad_im_ = grad_im.data_ptr(); - - modulated_deformable_col2im_gpu_kernel<<< - GET_BLOCKS(num_kernels), THREADS_PER_BLOCK, 0, - at::cuda::getCurrentCUDAStream()>>>( - num_kernels, data_col_, data_offset_, data_mask_, channels, - height_im, width_im, kernel_h, kernel_w, pad_h, pad_w, stride_h, - stride_w, dilation_h, dilation_w, channel_per_deformable_group, - batch_size, deformable_group, height_col, width_col, grad_im_); - })); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void modulated_deformable_col2im_coord_cuda( - const Tensor data_col, const Tensor data_im, const Tensor data_offset, - const Tensor data_mask, const int batch_size, const int channels, - const int height_im, const int width_im, const int height_col, - const int width_col, const int kernel_h, const int kernel_w, - const int pad_h, const int pad_w, const int stride_h, const int stride_w, - const int dilation_h, const int dilation_w, const int deformable_group, - Tensor grad_offset, Tensor grad_mask) { - const int num_kernels = batch_size * height_col * width_col * 2 * kernel_h * - kernel_w * deformable_group; - const int channel_per_deformable_group = - channels * kernel_h * kernel_w / deformable_group; - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - data_col.scalar_type(), "modulated_deformable_col2im_coord_gpu", ([&] { - const scalar_t *data_col_ = data_col.data_ptr(); - const scalar_t *data_im_ = data_im.data_ptr(); - const scalar_t *data_offset_ = data_offset.data_ptr(); - const scalar_t *data_mask_ = data_mask.data_ptr(); - scalar_t *grad_offset_ = grad_offset.data_ptr(); - scalar_t *grad_mask_ = grad_mask.data_ptr(); - - modulated_deformable_col2im_coord_gpu_kernel<<< - GET_BLOCKS(num_kernels), THREADS_PER_BLOCK, 0, - at::cuda::getCurrentCUDAStream()>>>( - num_kernels, data_col_, data_im_, data_offset_, data_mask_, - channels, height_im, width_im, kernel_h, kernel_w, pad_h, pad_w, - stride_h, stride_w, dilation_h, dilation_w, - channel_per_deformable_group, batch_size, - 2 * kernel_h * kernel_w * deformable_group, deformable_group, - height_col, width_col, grad_offset_, grad_mask_); - })); - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu deleted file mode 100755 index 657c81701..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/cuda/sync_bn_cuda.cu +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cuda_helper.hpp" -#include "sync_bn_cuda_kernel.cuh" - -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_mean_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_var_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), num, channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size) { - int num = input.size(0); - int channels = input.size(1); - int spatial = input.size(2); - - at::cuda::CUDAGuard device_guard(input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - input.scalar_type(), "sync_bn_forward_mean_cuda_kernel", [&] { - sync_bn_forward_output_cuda_kernel - <<>>( - input.data_ptr(), mean.data_ptr(), - var.data_ptr(), running_mean.data_ptr(), - running_var.data_ptr(), weight.data_ptr(), - bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), output.data_ptr(), num, - channels, spatial, eps, momentum, group_size); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias) { - int num = grad_output.size(0); - int channels = grad_output.size(1); - int spatial = grad_output.size(2); - - at::cuda::CUDAGuard device_guard(grad_output.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_param_cuda_kernel", [&] { - sync_bn_backward_param_cuda_kernel - <<>>( - grad_output.data_ptr(), norm.data_ptr(), - grad_weight.data_ptr(), grad_bias.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - int output_size = grad_input.numel(); - int num = grad_input.size(0); - int channels = grad_input.size(1); - int spatial = grad_input.size(2); - - at::cuda::CUDAGuard device_guard(grad_input.device()); - cudaStream_t stream = at::cuda::getCurrentCUDAStream(); - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - grad_output.scalar_type(), "sync_bn_backward_data_cuda_kernel", [&] { - sync_bn_backward_data_cuda_kernel - <<>>( - output_size, grad_output.data_ptr(), - weight.data_ptr(), grad_weight.data_ptr(), - grad_bias.data_ptr(), norm.data_ptr(), - std.data_ptr(), grad_input.data_ptr(), num, - channels, spatial); - }); - AT_CUDA_CHECK(cudaGetLastError()); -} diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/info.cpp b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/info.cpp deleted file mode 100755 index a08d227d4..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/info.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -// modified from -// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/vision.cpp -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF -#include -int get_cudart_version() { return CUDART_VERSION; } -#endif -#endif - -std::string get_compiling_cuda_version() { -#ifdef MMCV_WITH_CUDA -#ifndef HIP_DIFF - std::ostringstream oss; - // copied from - // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/cuda/detail/CUDAHooks.cpp#L231 - auto printCudaStyleVersion = [&](int v) { - oss << (v / 1000) << "." << (v / 10 % 100); - if (v % 10 != 0) { - oss << "." << (v % 10); - } - }; - printCudaStyleVersion(get_cudart_version()); - return oss.str(); -#else - return std::string("rocm not available"); -#endif -#else - return std::string("not available"); -#endif -} - -// similar to -// https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/Version.cpp -std::string get_compiler_version() { - std::ostringstream ss; -#if defined(__GNUC__) -#ifndef __clang__ - { ss << "GCC " << __GNUC__ << "." << __GNUC_MINOR__; } -#endif -#endif - -#if defined(__clang_major__) - { - ss << "clang " << __clang_major__ << "." << __clang_minor__ << "." - << __clang_patchlevel__; - } -#endif - -#if defined(_MSC_VER) - { ss << "MSVC " << _MSC_FULL_VER; } -#endif - return ss.str(); -} diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv.cpp b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv.cpp deleted file mode 100755 index c5e78c3a3..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv.cpp +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA - -void modulated_deformable_im2col_cuda( - const Tensor data_im, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor data_col); - -void modulated_deformable_col2im_cuda( - const Tensor data_col, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor grad_im); - -void modulated_deformable_col2im_coord_cuda( - const Tensor data_col, const Tensor data_im, const Tensor data_offset, - const Tensor data_mask, const int batch_size, const int channels, - const int height_im, const int width_im, const int height_col, - const int width_col, const int kernel_h, const int kernel_w, - const int pad_h, const int pad_w, const int stride_h, const int stride_w, - const int dilation_h, const int dilation_w, const int deformable_group, - Tensor grad_offset, Tensor grad_mask); - -#endif - -void modulated_deformable_im2col_cpu( - const Tensor data_im, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor data_col); - -void modulated_deformable_col2im_cpu( - const Tensor data_col, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor grad_im); - -void modulated_deformable_col2im_coord_cpu( - const Tensor data_col, const Tensor data_im, const Tensor data_offset, - const Tensor data_mask, const int batch_size, const int channels, - const int height_im, const int width_im, const int height_col, - const int width_col, const int kernel_h, const int kernel_w, - const int pad_h, const int pad_w, const int stride_h, const int stride_w, - const int dilation_h, const int dilation_w, const int deformable_group, - Tensor grad_offset, Tensor grad_mask); - -void modulated_deform_conv_forward( - Tensor input, Tensor weight, Tensor bias, Tensor ones, Tensor offset, - Tensor mask, Tensor output, Tensor columns, int kernel_h, int kernel_w, - const int stride_h, const int stride_w, const int pad_h, const int pad_w, - const int dilation_h, const int dilation_w, const int group, - const int deformable_group, const bool with_bias) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(bias); - CHECK_CUDA_INPUT(ones); - CHECK_CUDA_INPUT(offset); - CHECK_CUDA_INPUT(mask); - CHECK_CUDA_INPUT(output); - CHECK_CUDA_INPUT(columns); - -#else - AT_ERROR("ModulatedDeformConv is not compiled with GPU support"); -#endif - } else { - CHECK_CPU_INPUT(input); - CHECK_CPU_INPUT(weight); - CHECK_CPU_INPUT(bias); - CHECK_CPU_INPUT(ones); - CHECK_CPU_INPUT(offset); - CHECK_CPU_INPUT(mask); - CHECK_CPU_INPUT(output); - CHECK_CPU_INPUT(columns); - } - - at::DeviceGuard guard(input.device()); - - const int batch = input.size(0); - const int channels = input.size(1); - const int height = input.size(2); - const int width = input.size(3); - - const int channels_out = weight.size(0); - const int channels_kernel = weight.size(1); - const int kernel_h_ = weight.size(2); - const int kernel_w_ = weight.size(3); - - if (kernel_h_ != kernel_h || kernel_w_ != kernel_w) - AT_ERROR("Input shape and kernel shape won't match: (%d x %d vs %d x %d).", - kernel_h_, kernel_w, kernel_h_, kernel_w_); - if (channels != channels_kernel * group) - AT_ERROR("Input shape and kernel channels won't match: (%d vs %d).", - channels, channels_kernel * group); - - const int height_out = - (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1; - const int width_out = - (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1; - - if (ones.ndimension() != 2 || - ones.size(0) * ones.size(1) < height_out * width_out) { - // Resize plane and fill with ones... - ones = at::ones({height_out, width_out}, input.options()); - } - - // resize output - output = output.view({batch, channels_out, height_out, width_out}).zero_(); - // resize temporary columns - columns = - at::zeros({channels * kernel_h * kernel_w, 1 * height_out * width_out}, - input.options()); - - output = output.view({output.size(0), group, output.size(1) / group, - output.size(2), output.size(3)}); - - for (int b = 0; b < batch; b++) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - modulated_deformable_im2col_cuda( - input[b], offset[b], mask[b], 1, channels, height, width, height_out, - width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, deformable_group, columns); -#endif - } else { - modulated_deformable_im2col_cpu( - input[b], offset[b], mask[b], 1, channels, height, width, height_out, - width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, deformable_group, columns); - } - - // divide into group - weight = weight.view({group, weight.size(0) / group, weight.size(1), - weight.size(2), weight.size(3)}); - columns = columns.view({group, columns.size(0) / group, columns.size(1)}); - - for (int g = 0; g < group; g++) { - output[b][g] = output[b][g] - .flatten(1) - .addmm_(weight[g].flatten(1), columns[g]) - .view_as(output[b][g]); - } - - weight = weight.view({weight.size(0) * weight.size(1), weight.size(2), - weight.size(3), weight.size(4)}); - columns = - columns.view({columns.size(0) * columns.size(1), columns.size(2)}); - } - - output = output.view({output.size(0), output.size(1) * output.size(2), - output.size(3), output.size(4)}); - - if (with_bias) { - output += bias.view({1, bias.size(0), 1, 1}); - } -} - -void modulated_deform_conv_backward( - Tensor input, Tensor weight, Tensor bias, Tensor ones, Tensor offset, - Tensor mask, Tensor columns, Tensor grad_input, Tensor grad_weight, - Tensor grad_bias, Tensor grad_offset, Tensor grad_mask, Tensor grad_output, - int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, - int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, - const bool with_bias) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(bias); - CHECK_CUDA_INPUT(ones); - CHECK_CUDA_INPUT(offset); - CHECK_CUDA_INPUT(mask); - CHECK_CUDA_INPUT(columns); - CHECK_CUDA_INPUT(grad_input); - CHECK_CUDA_INPUT(grad_weight); - CHECK_CUDA_INPUT(grad_bias); - CHECK_CUDA_INPUT(grad_offset); - CHECK_CUDA_INPUT(grad_mask); - CHECK_CUDA_INPUT(grad_output); - -#else - AT_ERROR("ModulatedDeformConv is not compiled with GPU support"); -#endif - } else { - CHECK_CPU_INPUT(input); - CHECK_CPU_INPUT(weight); - CHECK_CPU_INPUT(bias); - CHECK_CPU_INPUT(ones); - CHECK_CPU_INPUT(offset); - CHECK_CPU_INPUT(mask); - CHECK_CPU_INPUT(columns); - CHECK_CPU_INPUT(grad_input); - CHECK_CPU_INPUT(grad_weight); - CHECK_CPU_INPUT(grad_bias); - CHECK_CPU_INPUT(grad_offset); - CHECK_CPU_INPUT(grad_mask); - CHECK_CPU_INPUT(grad_output); - } - - at::DeviceGuard guard(input.device()); - - const int batch = input.size(0); - const int channels = input.size(1); - const int height = input.size(2); - const int width = input.size(3); - - const int channels_kernel = weight.size(1); - const int kernel_h_ = weight.size(2); - const int kernel_w_ = weight.size(3); - if (kernel_h_ != kernel_h || kernel_w_ != kernel_w) - AT_ERROR("Input shape and kernel shape won't match: (%d x %d vs %d x %d).", - kernel_h_, kernel_w, kernel_h_, kernel_w_); - if (channels != channels_kernel * group) - AT_ERROR("Input shape and kernel channels won't match: (%d vs %d).", - channels, channels_kernel * group); - - const int height_out = - (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1; - const int width_out = - (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1; - - if (ones.ndimension() != 2 || - ones.size(0) * ones.size(1) < height_out * width_out) { - // Resize plane and fill with ones... - ones = at::ones({height_out, width_out}, input.options()); - } - - grad_input = grad_input.view({batch, channels, height, width}); - columns = at::zeros({channels * kernel_h * kernel_w, height_out * width_out}, - input.options()); - - grad_output = - grad_output.view({grad_output.size(0), group, grad_output.size(1) / group, - grad_output.size(2), grad_output.size(3)}); - - for (int b = 0; b < batch; b++) { - // divide int group - columns = columns.view({group, columns.size(0) / group, columns.size(1)}); - weight = weight.view({group, weight.size(0) / group, weight.size(1), - weight.size(2), weight.size(3)}); - - for (int g = 0; g < group; g++) { - columns[g].addmm_(weight[g].flatten(1).transpose(0, 1), - grad_output[b][g].flatten(1), 0.0f, 1.0f); - } - - columns = - columns.view({columns.size(0) * columns.size(1), columns.size(2)}); - weight = weight.view({weight.size(0) * weight.size(1), weight.size(2), - weight.size(3), weight.size(4)}); - - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - // gradient w.r.t. input coordinate data - modulated_deformable_col2im_coord_cuda( - columns, input[b], offset[b], mask[b], 1, channels, height, width, - height_out, width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, - stride_w, dilation_h, dilation_w, deformable_group, grad_offset[b], - grad_mask[b]); - // gradient w.r.t. input data - modulated_deformable_col2im_cuda( - columns, offset[b], mask[b], 1, channels, height, width, height_out, - width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, deformable_group, grad_input[b]); - - // gradient w.r.t. weight, dWeight should accumulate across the batch and - // group - modulated_deformable_im2col_cuda( - input[b], offset[b], mask[b], 1, channels, height, width, height_out, - width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, deformable_group, columns); -#endif - } else { - // gradient w.r.t. input coordinate data - modulated_deformable_col2im_coord_cpu( - columns, input[b], offset[b], mask[b], 1, channels, height, width, - height_out, width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, - stride_w, dilation_h, dilation_w, deformable_group, grad_offset[b], - grad_mask[b]); - // gradient w.r.t. input data - modulated_deformable_col2im_cpu( - columns, offset[b], mask[b], 1, channels, height, width, height_out, - width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, deformable_group, grad_input[b]); - // gradient w.r.t. weight, dWeight should accumulate across the batch and - // group - modulated_deformable_im2col_cpu( - input[b], offset[b], mask[b], 1, channels, height, width, height_out, - width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, deformable_group, columns); - } - - columns = columns.view({group, columns.size(0) / group, columns.size(1)}); - grad_weight = grad_weight.view({group, grad_weight.size(0) / group, - grad_weight.size(1), grad_weight.size(2), - grad_weight.size(3)}); - if (with_bias) - grad_bias = grad_bias.view({group, grad_bias.size(0) / group}); - - for (int g = 0; g < group; g++) { - grad_weight[g] = - grad_weight[g] - .flatten(1) - .addmm_(grad_output[b][g].flatten(1), columns[g].transpose(0, 1)) - .view_as(grad_weight[g]); - if (with_bias) { - grad_bias[g] = - grad_bias[g] - .view({-1, 1}) - .addmm_(grad_output[b][g].flatten(1), ones.view({-1, 1})) - .view(-1); - } - } - - columns = - columns.view({columns.size(0) * columns.size(1), columns.size(2)}); - grad_weight = grad_weight.view({grad_weight.size(0) * grad_weight.size(1), - grad_weight.size(2), grad_weight.size(3), - grad_weight.size(4)}); - if (with_bias) - grad_bias = grad_bias.view({grad_bias.size(0) * grad_bias.size(1)}); - } - grad_output = grad_output.view({grad_output.size(0) * grad_output.size(1), - grad_output.size(2), grad_output.size(3), - grad_output.size(4)}); -} diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv_cpu.cpp b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv_cpu.cpp deleted file mode 100755 index 89a81d733..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/modulated_deform_conv_cpu.cpp +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -template -T dmcn_im2col_bilinear_cpu(const T *input, const int data_width, - const int height, const int width, T h, T w) { - int h_low = floorf(h); - int w_low = floorf(w); - int h_high = h_low + 1; - int w_high = w_low + 1; - - T lh = h - h_low; - T lw = w - w_low; - T hh = 1 - lh, hw = 1 - lw; - - T v1 = 0; - if (h_low >= 0 && w_low >= 0) v1 = input[h_low * data_width + w_low]; - T v2 = 0; - if (h_low >= 0 && w_high <= width - 1) - v2 = input[h_low * data_width + w_high]; - T v3 = 0; - if (h_high <= height - 1 && w_low >= 0) - v3 = input[h_high * data_width + w_low]; - T v4 = 0; - if (h_high <= height - 1 && w_high <= width - 1) - v4 = input[h_high * data_width + w_high]; - - T w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; - - T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); - return val; -} - -template -T dmcn_get_gradient_weight_cpu(T argmax_h, T argmax_w, const int h, const int w, - const int height, const int width) { - if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || - argmax_w >= width) { - // empty - return 0; - } - - int argmax_h_low = floorf(argmax_h); - int argmax_w_low = floorf(argmax_w); - int argmax_h_high = argmax_h_low + 1; - int argmax_w_high = argmax_w_low + 1; - - T weight = 0; - if (h == argmax_h_low && w == argmax_w_low) - weight = (h + 1 - argmax_h) * (w + 1 - argmax_w); - if (h == argmax_h_low && w == argmax_w_high) - weight = (h + 1 - argmax_h) * (argmax_w + 1 - w); - if (h == argmax_h_high && w == argmax_w_low) - weight = (argmax_h + 1 - h) * (w + 1 - argmax_w); - if (h == argmax_h_high && w == argmax_w_high) - weight = (argmax_h + 1 - h) * (argmax_w + 1 - w); - return weight; -} - -template -T dmcn_get_coordinate_weight_cpu(T argmax_h, T argmax_w, const int height, - const int width, const T *im_data, - const int data_width, const int bp_dir) { - if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || - argmax_w >= width) { - // empty - return 0; - } - - int argmax_h_low = floorf(argmax_h); - int argmax_w_low = floorf(argmax_w); - int argmax_h_high = argmax_h_low + 1; - int argmax_w_high = argmax_w_low + 1; - - T weight = 0; - - if (bp_dir == 0) { - if (argmax_h_low >= 0 && argmax_w_low >= 0) - weight += -1 * (argmax_w_low + 1 - argmax_w) * - im_data[argmax_h_low * data_width + argmax_w_low]; - if (argmax_h_low >= 0 && argmax_w_high <= width - 1) - weight += -1 * (argmax_w - argmax_w_low) * - im_data[argmax_h_low * data_width + argmax_w_high]; - if (argmax_h_high <= height - 1 && argmax_w_low >= 0) - weight += (argmax_w_low + 1 - argmax_w) * - im_data[argmax_h_high * data_width + argmax_w_low]; - if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) - weight += (argmax_w - argmax_w_low) * - im_data[argmax_h_high * data_width + argmax_w_high]; - } else if (bp_dir == 1) { - if (argmax_h_low >= 0 && argmax_w_low >= 0) - weight += -1 * (argmax_h_low + 1 - argmax_h) * - im_data[argmax_h_low * data_width + argmax_w_low]; - if (argmax_h_low >= 0 && argmax_w_high <= width - 1) - weight += (argmax_h_low + 1 - argmax_h) * - im_data[argmax_h_low * data_width + argmax_w_high]; - if (argmax_h_high <= height - 1 && argmax_w_low >= 0) - weight += -1 * (argmax_h - argmax_h_low) * - im_data[argmax_h_high * data_width + argmax_w_low]; - if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) - weight += (argmax_h - argmax_h_low) * - im_data[argmax_h_high * data_width + argmax_w_high]; - } - - return weight; -} - -template -void modulated_deformable_im2col_cpu_kernel( - const int n, const T *data_im, const T *data_offset, const T *data_mask, - const int height, const int width, const int kernel_h, const int kernel_w, - const int pad_h, const int pad_w, const int stride_h, const int stride_w, - const int dilation_h, const int dilation_w, - const int channel_per_deformable_group, const int batch_size, - const int num_channels, const int deformable_group, const int height_col, - const int width_col, T *data_col) { - for (int index = 0; index < n; index++) { - // index index of output matrix - const int w_col = index % width_col; - const int h_col = (index / width_col) % height_col; - const int b_col = (index / width_col / height_col) % batch_size; - const int c_im = (index / width_col / height_col) / batch_size; - const int c_col = c_im * kernel_h * kernel_w; - - // compute deformable group index - const int deformable_group_index = c_im / channel_per_deformable_group; - - const int h_in = h_col * stride_h - pad_h; - const int w_in = w_col * stride_w - pad_w; - - T *data_col_ptr = - data_col + - ((c_col * batch_size + b_col) * height_col + h_col) * width_col + w_col; - const T *data_im_ptr = - data_im + (b_col * num_channels + c_im) * height * width; - const T *data_offset_ptr = - data_offset + (b_col * deformable_group + deformable_group_index) * 2 * - kernel_h * kernel_w * height_col * width_col; - - const T *data_mask_ptr = - data_mask + (b_col * deformable_group + deformable_group_index) * - kernel_h * kernel_w * height_col * width_col; - - for (int i = 0; i < kernel_h; ++i) { - for (int j = 0; j < kernel_w; ++j) { - const int data_offset_h_ptr = - ((2 * (i * kernel_w + j)) * height_col + h_col) * width_col + w_col; - const int data_offset_w_ptr = - ((2 * (i * kernel_w + j) + 1) * height_col + h_col) * width_col + - w_col; - const int data_mask_hw_ptr = - ((i * kernel_w + j) * height_col + h_col) * width_col + w_col; - const T offset_h = data_offset_ptr[data_offset_h_ptr]; - const T offset_w = data_offset_ptr[data_offset_w_ptr]; - const T mask = data_mask_ptr[data_mask_hw_ptr]; - T val = static_cast(0); - const T h_im = h_in + i * dilation_h + offset_h; - const T w_im = w_in + j * dilation_w + offset_w; - if (h_im > -1 && w_im > -1 && h_im < height && w_im < width) - val = dmcn_im2col_bilinear_cpu(data_im_ptr, width, height, width, - h_im, w_im); - *data_col_ptr = val * mask; - data_col_ptr += batch_size * height_col * width_col; - } - } - } -} - -template -void modulated_deformable_col2im_cpu_kernel( - const int n, const T *data_col, const T *data_offset, const T *data_mask, - const int channels, const int height, const int width, const int kernel_h, - const int kernel_w, const int pad_h, const int pad_w, const int stride_h, - const int stride_w, const int dilation_h, const int dilation_w, - const int channel_per_deformable_group, const int batch_size, - const int deformable_group, const int height_col, const int width_col, - T *grad_im) { - for (int index = 0; index < n; index++) { - const int j = (index / width_col / height_col / batch_size) % kernel_w; - const int i = - (index / width_col / height_col / batch_size / kernel_w) % kernel_h; - const int c = - index / width_col / height_col / batch_size / kernel_w / kernel_h; - // compute the start and end of the output - - const int deformable_group_index = c / channel_per_deformable_group; - - int w_out = index % width_col; - int h_out = (index / width_col) % height_col; - int b = (index / width_col / height_col) % batch_size; - int w_in = w_out * stride_w - pad_w; - int h_in = h_out * stride_h - pad_h; - - const T *data_offset_ptr = - data_offset + (b * deformable_group + deformable_group_index) * 2 * - kernel_h * kernel_w * height_col * width_col; - const T *data_mask_ptr = - data_mask + (b * deformable_group + deformable_group_index) * kernel_h * - kernel_w * height_col * width_col; - const int data_offset_h_ptr = - ((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out; - const int data_offset_w_ptr = - ((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out; - const int data_mask_hw_ptr = - ((i * kernel_w + j) * height_col + h_out) * width_col + w_out; - const T offset_h = data_offset_ptr[data_offset_h_ptr]; - const T offset_w = data_offset_ptr[data_offset_w_ptr]; - const T mask = data_mask_ptr[data_mask_hw_ptr]; - const T cur_inv_h_data = h_in + i * dilation_h + offset_h; - const T cur_inv_w_data = w_in + j * dilation_w + offset_w; - - const T cur_top_grad = data_col[index] * mask; - const int cur_h = (int)cur_inv_h_data; - const int cur_w = (int)cur_inv_w_data; - for (int dy = -2; dy <= 2; dy++) { - for (int dx = -2; dx <= 2; dx++) { - if (cur_h + dy >= 0 && cur_h + dy < height && cur_w + dx >= 0 && - cur_w + dx < width && abs(cur_inv_h_data - (cur_h + dy)) < 1 && - abs(cur_inv_w_data - (cur_w + dx)) < 1) { - int cur_bottom_grad_pos = - ((b * channels + c) * height + cur_h + dy) * width + cur_w + dx; - T weight = dmcn_get_gradient_weight_cpu(cur_inv_h_data, - cur_inv_w_data, cur_h + dy, - cur_w + dx, height, width); - *(grad_im + cur_bottom_grad_pos) += weight * cur_top_grad; - } - } - } - } -} - -template -void modulated_deformable_col2im_coord_cpu_kernel( - const int n, const T *data_col, const T *data_im, const T *data_offset, - const T *data_mask, const int channels, const int height, const int width, - const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int channel_per_deformable_group, - const int batch_size, const int offset_channels, const int deformable_group, - const int height_col, const int width_col, T *grad_offset, T *grad_mask) { - for (int index = 0; index < n; index++) { - T val = 0, mval = 0; - int w = index % width_col; - int h = (index / width_col) % height_col; - int c = (index / width_col / height_col) % offset_channels; - int b = (index / width_col / height_col) / offset_channels; - // compute the start and end of the output - - const int deformable_group_index = c / (2 * kernel_h * kernel_w); - const int col_step = kernel_h * kernel_w; - int cnt = 0; - const T *data_col_ptr = data_col + deformable_group_index * - channel_per_deformable_group * - batch_size * width_col * height_col; - const T *data_im_ptr = - data_im + (b * deformable_group + deformable_group_index) * - channel_per_deformable_group / kernel_h / kernel_w * - height * width; - const T *data_offset_ptr = - data_offset + (b * deformable_group + deformable_group_index) * 2 * - kernel_h * kernel_w * height_col * width_col; - const T *data_mask_ptr = - data_mask + (b * deformable_group + deformable_group_index) * kernel_h * - kernel_w * height_col * width_col; - - const int offset_c = c - deformable_group_index * 2 * kernel_h * kernel_w; - - for (int col_c = (offset_c / 2); col_c < channel_per_deformable_group; - col_c += col_step) { - const int col_pos = - (((col_c * batch_size + b) * height_col) + h) * width_col + w; - const int bp_dir = offset_c % 2; - - int j = (col_pos / width_col / height_col / batch_size) % kernel_w; - int i = - (col_pos / width_col / height_col / batch_size / kernel_w) % kernel_h; - int w_out = col_pos % width_col; - int h_out = (col_pos / width_col) % height_col; - int w_in = w_out * stride_w - pad_w; - int h_in = h_out * stride_h - pad_h; - const int data_offset_h_ptr = - (((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out); - const int data_offset_w_ptr = - (((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + - w_out); - const int data_mask_hw_ptr = - (((i * kernel_w + j) * height_col + h_out) * width_col + w_out); - const T offset_h = data_offset_ptr[data_offset_h_ptr]; - const T offset_w = data_offset_ptr[data_offset_w_ptr]; - const T mask = data_mask_ptr[data_mask_hw_ptr]; - T inv_h = h_in + i * dilation_h + offset_h; - T inv_w = w_in + j * dilation_w + offset_w; - if (inv_h <= -1 || inv_w <= -1 || inv_h >= height || inv_w >= width) - inv_h = inv_w = -2; - else - mval += data_col_ptr[col_pos] * - dmcn_im2col_bilinear_cpu(data_im_ptr + cnt * height * width, - width, height, width, inv_h, inv_w); - const T weight = dmcn_get_coordinate_weight_cpu( - inv_h, inv_w, height, width, data_im_ptr + cnt * height * width, - width, bp_dir); - val += weight * data_col_ptr[col_pos] * mask; - cnt += 1; - } - // KERNEL_ASSIGN(grad_offset[index], offset_req, val); - grad_offset[index] = val; - if (offset_c % 2 == 0) - // KERNEL_ASSIGN(grad_mask[(((b * deformable_group + - // deformable_group_index) * kernel_h * kernel_w + offset_c / 2) * - // height_col + h) * width_col + w], mask_req, mval); - grad_mask[(((b * deformable_group + deformable_group_index) * kernel_h * - kernel_w + - offset_c / 2) * - height_col + - h) * - width_col + - w] = mval; - } -} - -void modulated_deformable_im2col_cpu( - const Tensor data_im, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor data_col) { - // num_axes should be smaller than block size - const int channel_per_deformable_group = channels / deformable_group; - const int num_kernels = channels * batch_size * height_col * width_col; - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - data_im.scalar_type(), "modulated_deformable_im2col_cpu", ([&] { - const scalar_t *data_im_ = data_im.data_ptr(); - const scalar_t *data_offset_ = data_offset.data_ptr(); - const scalar_t *data_mask_ = data_mask.data_ptr(); - scalar_t *data_col_ = data_col.data_ptr(); - - modulated_deformable_im2col_cpu_kernel( - num_kernels, data_im_, data_offset_, data_mask_, height_im, - width_im, kernel_h, kenerl_w, pad_h, pad_w, stride_h, stride_w, - dilation_h, dilation_w, channel_per_deformable_group, batch_size, - channels, deformable_group, height_col, width_col, data_col_); - })); -} - -void modulated_deformable_col2im_cpu( - const Tensor data_col, const Tensor data_offset, const Tensor data_mask, - const int batch_size, const int channels, const int height_im, - const int width_im, const int height_col, const int width_col, - const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, - const int stride_h, const int stride_w, const int dilation_h, - const int dilation_w, const int deformable_group, Tensor grad_im) { - const int channel_per_deformable_group = channels / deformable_group; - const int num_kernels = - channels * kernel_h * kernel_w * batch_size * height_col * width_col; - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - data_col.scalar_type(), "modulated_deformable_col2im_cpu", ([&] { - const scalar_t *data_col_ = data_col.data_ptr(); - const scalar_t *data_offset_ = data_offset.data_ptr(); - const scalar_t *data_mask_ = data_mask.data_ptr(); - scalar_t *grad_im_ = grad_im.data_ptr(); - - modulated_deformable_col2im_cpu_kernel( - num_kernels, data_col_, data_offset_, data_mask_, channels, - height_im, width_im, kernel_h, kernel_w, pad_h, pad_w, stride_h, - stride_w, dilation_h, dilation_w, channel_per_deformable_group, - batch_size, deformable_group, height_col, width_col, grad_im_); - })); -} - -void modulated_deformable_col2im_coord_cpu( - const Tensor data_col, const Tensor data_im, const Tensor data_offset, - const Tensor data_mask, const int batch_size, const int channels, - const int height_im, const int width_im, const int height_col, - const int width_col, const int kernel_h, const int kernel_w, - const int pad_h, const int pad_w, const int stride_h, const int stride_w, - const int dilation_h, const int dilation_w, const int deformable_group, - Tensor grad_offset, Tensor grad_mask) { - const int num_kernels = batch_size * height_col * width_col * 2 * kernel_h * - kernel_w * deformable_group; - const int channel_per_deformable_group = - channels * kernel_h * kernel_w / deformable_group; - - AT_DISPATCH_FLOATING_TYPES_AND_HALF( - data_col.scalar_type(), "modulated_deformable_col2im_coord_cpu", ([&] { - const scalar_t *data_col_ = data_col.data_ptr(); - const scalar_t *data_im_ = data_im.data_ptr(); - const scalar_t *data_offset_ = data_offset.data_ptr(); - const scalar_t *data_mask_ = data_mask.data_ptr(); - scalar_t *grad_offset_ = grad_offset.data_ptr(); - scalar_t *grad_mask_ = grad_mask.data_ptr(); - - modulated_deformable_col2im_coord_cpu_kernel( - num_kernels, data_col_, data_im_, data_offset_, data_mask_, - channels, height_im, width_im, kernel_h, kernel_w, pad_h, pad_w, - stride_h, stride_w, dilation_h, dilation_w, - channel_per_deformable_group, batch_size, - 2 * kernel_h * kernel_w * deformable_group, deformable_group, - height_col, width_col, grad_offset_, grad_mask_); - })); -} diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp deleted file mode 100755 index e9273ecd2..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/pybind.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -std::string get_compiler_version(); -std::string get_compiling_cuda_version(); - -void modulated_deform_conv_forward( - Tensor input, Tensor weight, Tensor bias, Tensor ones, Tensor offset, - Tensor mask, Tensor output, Tensor columns, int kernel_h, int kernel_w, - const int stride_h, const int stride_w, const int pad_h, const int pad_w, - const int dilation_h, const int dilation_w, const int group, - const int deformable_group, const bool with_bias); - -void modulated_deform_conv_backward( - Tensor input, Tensor weight, Tensor bias, Tensor ones, Tensor offset, - Tensor mask, Tensor columns, Tensor grad_input, Tensor grad_weight, - Tensor grad_bias, Tensor grad_offset, Tensor grad_mask, Tensor grad_output, - int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, - int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, - const bool with_bias); - -void sync_bn_forward_mean(const Tensor input, Tensor mean); - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var); - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size); - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias); - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { - - m.def("get_compiler_version", &get_compiler_version, "get_compiler_version"); - m.def("get_compiling_cuda_version", &get_compiling_cuda_version, - "get_compiling_cuda_version"); - - m.def("modulated_deform_conv_forward", &modulated_deform_conv_forward, - "modulated deform conv forward", py::arg("input"), py::arg("weight"), - py::arg("bias"), py::arg("ones"), py::arg("offset"), py::arg("mask"), - py::arg("output"), py::arg("columns"), py::arg("kernel_h"), - py::arg("kernel_w"), py::arg("stride_h"), py::arg("stride_w"), - py::arg("pad_h"), py::arg("pad_w"), py::arg("dilation_h"), - py::arg("dilation_w"), py::arg("group"), py::arg("deformable_group"), - py::arg("with_bias")); - m.def("modulated_deform_conv_backward", &modulated_deform_conv_backward, - "modulated deform conv backward", py::arg("input"), py::arg("weight"), - py::arg("bias"), py::arg("ones"), py::arg("offset"), py::arg("mask"), - py::arg("columns"), py::arg("grad_input"), py::arg("grad_weight"), - py::arg("grad_bias"), py::arg("grad_offset"), py::arg("grad_mask"), - py::arg("grad_output"), py::arg("kernel_h"), py::arg("kernel_w"), - py::arg("stride_h"), py::arg("stride_w"), py::arg("pad_h"), - py::arg("pad_w"), py::arg("dilation_h"), py::arg("dilation_w"), - py::arg("group"), py::arg("deformable_group"), py::arg("with_bias")); - - m.def("sync_bn_forward_mean", &sync_bn_forward_mean, "sync_bn forward_mean", - py::arg("input"), py::arg("mean")); - m.def("sync_bn_forward_var", &sync_bn_forward_var, "sync_bn forward_var", - py::arg("input"), py::arg("mean"), py::arg("var")); - m.def("sync_bn_forward_output", &sync_bn_forward_output, - "sync_bn forward_output", py::arg("input"), py::arg("mean"), - py::arg("var"), py::arg("weight"), py::arg("bias"), - py::arg("running_mean"), py::arg("running_var"), py::arg("norm"), - py::arg("std"), py::arg("output"), py::arg("eps"), py::arg("momentum"), - py::arg("group_size")); - m.def("sync_bn_backward_param", &sync_bn_backward_param, - "sync_bn backward_param", py::arg("grad_output"), py::arg("norm"), - py::arg("grad_weight"), py::arg("grad_bias")); - m.def("sync_bn_backward_data", &sync_bn_backward_data, - "sync_bn backward_data", py::arg("grad_output"), py::arg("weight"), - py::arg("grad_weight"), py::arg("grad_bias"), py::arg("norm"), - py::arg("std"), py::arg("grad_input")); -} diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp deleted file mode 100755 index 2e023a859..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/csrc/pytorch/sync_bn.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) OpenMMLab. All rights reserved -#include "pytorch_cpp_helper.hpp" - -#ifdef MMCV_WITH_CUDA -void SyncBNForwardMeanCUDAKernelLauncher(const Tensor input, Tensor mean); - -void SyncBNForwardVarCUDAKernelLauncher(const Tensor input, const Tensor mean, - Tensor var); - -void SyncBNForwardOutputCUDAKernelLauncher( - const Tensor input, const Tensor mean, const Tensor var, - Tensor running_mean, Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, Tensor output, float eps, - float momentum, int group_size); - -void SyncBNBackwardParamCUDAKernelLauncher(const Tensor grad_output, - const Tensor norm, - Tensor grad_weight, - Tensor grad_bias); - -void SyncBNBackwardDataCUDAKernelLauncher(const Tensor grad_output, - const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input); - -void sync_bn_forward_mean_cuda(const Tensor input, Tensor mean) { - SyncBNForwardMeanCUDAKernelLauncher(input, mean); -} - -void sync_bn_forward_var_cuda(const Tensor input, const Tensor mean, - Tensor var) { - SyncBNForwardVarCUDAKernelLauncher(input, mean, var); -} - -void sync_bn_forward_output_cuda(const Tensor input, const Tensor mean, - const Tensor var, Tensor running_mean, - Tensor running_var, const Tensor weight, - const Tensor bias, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - SyncBNForwardOutputCUDAKernelLauncher(input, mean, var, running_mean, - running_var, weight, bias, norm, std, - output, eps, momentum, group_size); -} - -void sync_bn_backward_param_cuda(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - SyncBNBackwardParamCUDAKernelLauncher(grad_output, norm, grad_weight, - grad_bias); -} - -void sync_bn_backward_data_cuda(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, - const Tensor grad_bias, const Tensor norm, - const Tensor std, Tensor grad_input) { - SyncBNBackwardDataCUDAKernelLauncher(grad_output, weight, grad_weight, - grad_bias, norm, std, grad_input); -} -#endif - -void sync_bn_forward_mean(const Tensor input, Tensor mean) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - sync_bn_forward_mean_cuda(input, mean); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_forward_var(const Tensor input, const Tensor mean, Tensor var) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - CHECK_CUDA_INPUT(var); - sync_bn_forward_var_cuda(input, mean, var); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_forward_output(const Tensor input, const Tensor mean, - const Tensor var, const Tensor weight, - const Tensor bias, Tensor running_mean, - Tensor running_var, Tensor norm, Tensor std, - Tensor output, float eps, float momentum, - int group_size) { - if (input.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(input); - CHECK_CUDA_INPUT(mean); - CHECK_CUDA_INPUT(var); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(bias); - CHECK_CUDA_INPUT(running_mean); - CHECK_CUDA_INPUT(running_var); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(std); - CHECK_CUDA_INPUT(output); - sync_bn_forward_output_cuda(input, mean, var, running_mean, running_var, - weight, bias, norm, std, output, eps, momentum, - group_size); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_backward_param(const Tensor grad_output, const Tensor norm, - Tensor grad_weight, Tensor grad_bias) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(grad_weight); - CHECK_CUDA_INPUT(grad_bias); - sync_bn_backward_param_cuda(grad_output, norm, grad_weight, grad_bias); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} - -void sync_bn_backward_data(const Tensor grad_output, const Tensor weight, - const Tensor grad_weight, const Tensor grad_bias, - const Tensor norm, const Tensor std, - Tensor grad_input) { - if (grad_output.device().is_cuda()) { -#ifdef MMCV_WITH_CUDA - CHECK_CUDA_INPUT(grad_output); - CHECK_CUDA_INPUT(weight); - CHECK_CUDA_INPUT(grad_weight); - CHECK_CUDA_INPUT(grad_bias); - CHECK_CUDA_INPUT(norm); - CHECK_CUDA_INPUT(std); - CHECK_CUDA_INPUT(grad_input); - sync_bn_backward_data_cuda(grad_output, weight, grad_weight, grad_bias, - norm, std, grad_input); -#else - AT_ERROR("SyncBatchNorm is not compiled with GPU support"); -#endif - } else { - AT_ERROR("SyncBatchNorm is not implemented on CPU"); - } -} diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/deprecated_wrappers.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/deprecated_wrappers.py deleted file mode 100755 index a2e593df9..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/deprecated_wrappers.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# This file is for backward compatibility. -# Module wrappers for empty tensor have been moved to mmcv.cnn.bricks. -import warnings - -from ..cnn.bricks.wrappers import Conv2d, ConvTranspose2d, Linear, MaxPool2d - - -class Conv2d_deprecated(Conv2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Conv2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') - - -class ConvTranspose2d_deprecated(ConvTranspose2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing ConvTranspose2d wrapper from "mmcv.ops" will be ' - 'deprecated in the future. Please import them from "mmcv.cnn" ' - 'instead') - - -class MaxPool2d_deprecated(MaxPool2d): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing MaxPool2d wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') - - -class Linear_deprecated(Linear): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - warnings.warn( - 'Importing Linear wrapper from "mmcv.ops" will be deprecated in' - ' the future. Please import them from "mmcv.cnn" instead') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/info.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/info.py deleted file mode 100755 index 29f2e5598..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/info.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os - -import torch - -if torch.__version__ == 'parrots': - import parrots - - def get_compiler_version(): - return 'GCC ' + parrots.version.compiler - - def get_compiling_cuda_version(): - return parrots.version.cuda -else: - from ..utils import ext_loader - ext_module = ext_loader.load_ext( - '_ext', ['get_compiler_version', 'get_compiling_cuda_version']) - - def get_compiler_version(): - return ext_module.get_compiler_version() - - def get_compiling_cuda_version(): - return ext_module.get_compiling_cuda_version() - - -def get_onnxruntime_op_path(): - wildcard = os.path.join( - os.path.abspath(os.path.dirname(os.path.dirname(__file__))), - '_ext_ort.*.so') - - paths = glob.glob(wildcard) - if len(paths) > 0: - return paths[0] - else: - return '' diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/modulated_deform_conv.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/modulated_deform_conv.py deleted file mode 100755 index 341798059..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/modulated_deform_conv.py +++ /dev/null @@ -1,282 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import torch -import torch.nn as nn -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.utils import _pair, _single - -from mmcv.utils import deprecated_api_warning -from ..cnn import CONV_LAYERS -from ..utils import ext_loader, print_log - -ext_module = ext_loader.load_ext( - '_ext', - ['modulated_deform_conv_forward', 'modulated_deform_conv_backward']) - - -class ModulatedDeformConv2dFunction(Function): - - @staticmethod - def symbolic(g, input, offset, mask, weight, bias, stride, padding, - dilation, groups, deform_groups): - input_tensors = [input, offset, mask, weight] - if bias is not None: - input_tensors.append(bias) - return g.op( - 'mmcv::MMCVModulatedDeformConv2d', - *input_tensors, - stride_i=stride, - padding_i=padding, - dilation_i=dilation, - groups_i=groups, - deform_groups_i=deform_groups) - - @staticmethod - def forward(ctx, - input, - offset, - mask, - weight, - bias=None, - stride=1, - padding=0, - dilation=1, - groups=1, - deform_groups=1): - if input is not None and input.dim() != 4: - raise ValueError( - f'Expected 4D tensor as input, got {input.dim()}D tensor \ - instead.') - ctx.stride = _pair(stride) - ctx.padding = _pair(padding) - ctx.dilation = _pair(dilation) - ctx.groups = groups - ctx.deform_groups = deform_groups - ctx.with_bias = bias is not None - if not ctx.with_bias: - bias = input.new_empty(0) # fake tensor - # When pytorch version >= 1.6.0, amp is adopted for fp16 mode; - # amp won't cast the type of model (float32), but "offset" is cast - # to float16 by nn.Conv2d automatically, leading to the type - # mismatch with input (when it is float32) or weight. - # The flag for whether to use fp16 or amp is the type of "offset", - # we cast weight and input to temporarily support fp16 and amp - # whatever the pytorch version is. - input = input.type_as(offset) - weight = weight.type_as(input) - ctx.save_for_backward(input, offset, mask, weight, bias) - output = input.new_empty( - ModulatedDeformConv2dFunction._output_size(ctx, input, weight)) - ctx._bufs = [input.new_empty(0), input.new_empty(0)] - ext_module.modulated_deform_conv_forward( - input, - weight, - bias, - ctx._bufs[0], - offset, - mask, - output, - ctx._bufs[1], - kernel_h=weight.size(2), - kernel_w=weight.size(3), - stride_h=ctx.stride[0], - stride_w=ctx.stride[1], - pad_h=ctx.padding[0], - pad_w=ctx.padding[1], - dilation_h=ctx.dilation[0], - dilation_w=ctx.dilation[1], - group=ctx.groups, - deformable_group=ctx.deform_groups, - with_bias=ctx.with_bias) - return output - - @staticmethod - @once_differentiable - def backward(ctx, grad_output): - input, offset, mask, weight, bias = ctx.saved_tensors - grad_input = torch.zeros_like(input) - grad_offset = torch.zeros_like(offset) - grad_mask = torch.zeros_like(mask) - grad_weight = torch.zeros_like(weight) - grad_bias = torch.zeros_like(bias) - grad_output = grad_output.contiguous() - ext_module.modulated_deform_conv_backward( - input, - weight, - bias, - ctx._bufs[0], - offset, - mask, - ctx._bufs[1], - grad_input, - grad_weight, - grad_bias, - grad_offset, - grad_mask, - grad_output, - kernel_h=weight.size(2), - kernel_w=weight.size(3), - stride_h=ctx.stride[0], - stride_w=ctx.stride[1], - pad_h=ctx.padding[0], - pad_w=ctx.padding[1], - dilation_h=ctx.dilation[0], - dilation_w=ctx.dilation[1], - group=ctx.groups, - deformable_group=ctx.deform_groups, - with_bias=ctx.with_bias) - if not ctx.with_bias: - grad_bias = None - - return (grad_input, grad_offset, grad_mask, grad_weight, grad_bias, - None, None, None, None, None) - - @staticmethod - def _output_size(ctx, input, weight): - channels = weight.size(0) - output_size = (input.size(0), channels) - for d in range(input.dim() - 2): - in_size = input.size(d + 2) - pad = ctx.padding[d] - kernel = ctx.dilation[d] * (weight.size(d + 2) - 1) + 1 - stride_ = ctx.stride[d] - output_size += ((in_size + (2 * pad) - kernel) // stride_ + 1, ) - if not all(map(lambda s: s > 0, output_size)): - raise ValueError( - 'convolution input is too small (output would be ' + - 'x'.join(map(str, output_size)) + ')') - return output_size - - -modulated_deform_conv2d = ModulatedDeformConv2dFunction.apply - - -class ModulatedDeformConv2d(nn.Module): - - @deprecated_api_warning({'deformable_groups': 'deform_groups'}, - cls_name='ModulatedDeformConv2d') - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - groups=1, - deform_groups=1, - bias=True): - super(ModulatedDeformConv2d, self).__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.kernel_size = _pair(kernel_size) - self.stride = _pair(stride) - self.padding = _pair(padding) - self.dilation = _pair(dilation) - self.groups = groups - self.deform_groups = deform_groups - # enable compatibility with nn.Conv2d - self.transposed = False - self.output_padding = _single(0) - - self.weight = nn.Parameter( - torch.Tensor(out_channels, in_channels // groups, - *self.kernel_size)) - if bias: - self.bias = nn.Parameter(torch.Tensor(out_channels)) - else: - self.register_parameter('bias', None) - self.init_weights() - - def init_weights(self): - n = self.in_channels - for k in self.kernel_size: - n *= k - stdv = 1. / math.sqrt(n) - self.weight.data.uniform_(-stdv, stdv) - if self.bias is not None: - self.bias.data.zero_() - - def forward(self, x, offset, mask): - return modulated_deform_conv2d(x, offset, mask, self.weight, self.bias, - self.stride, self.padding, - self.dilation, self.groups, - self.deform_groups) - - -@CONV_LAYERS.register_module('DCNv2') -class ModulatedDeformConv2dPack(ModulatedDeformConv2d): - """A ModulatedDeformable Conv Encapsulation that acts as normal Conv - layers. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - stride (int): Same as nn.Conv2d, while tuple is not supported. - padding (int): Same as nn.Conv2d, while tuple is not supported. - dilation (int): Same as nn.Conv2d, while tuple is not supported. - groups (int): Same as nn.Conv2d. - bias (bool or str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if norm_cfg is None, otherwise - False. - """ - - _version = 2 - - def __init__(self, *args, **kwargs): - super(ModulatedDeformConv2dPack, self).__init__(*args, **kwargs) - self.conv_offset = nn.Conv2d( - self.in_channels, - self.deform_groups * 3 * self.kernel_size[0] * self.kernel_size[1], - kernel_size=self.kernel_size, - stride=self.stride, - padding=self.padding, - dilation=self.dilation, - bias=True) - self.init_weights() - - def init_weights(self): - super(ModulatedDeformConv2dPack, self).init_weights() - if hasattr(self, 'conv_offset'): - self.conv_offset.weight.data.zero_() - self.conv_offset.bias.data.zero_() - - def forward(self, x): - out = self.conv_offset(x) - o1, o2, mask = torch.chunk(out, 3, dim=1) - offset = torch.cat((o1, o2), dim=1) - mask = torch.sigmoid(mask) - return modulated_deform_conv2d(x, offset, mask, self.weight, self.bias, - self.stride, self.padding, - self.dilation, self.groups, - self.deform_groups) - - def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, - missing_keys, unexpected_keys, error_msgs): - version = local_metadata.get('version', None) - - if version is None or version < 2: - # the key is different in early versions - # In version < 2, ModulatedDeformConvPack - # loads previous benchmark models. - if (prefix + 'conv_offset.weight' not in state_dict - and prefix[:-1] + '_offset.weight' in state_dict): - state_dict[prefix + 'conv_offset.weight'] = state_dict.pop( - prefix[:-1] + '_offset.weight') - if (prefix + 'conv_offset.bias' not in state_dict - and prefix[:-1] + '_offset.bias' in state_dict): - state_dict[prefix + - 'conv_offset.bias'] = state_dict.pop(prefix[:-1] + - '_offset.bias') - - if version is not None and version > 1: - print_log( - f'ModulatedDeformConvPack {prefix.rstrip(".")} is upgraded to ' - 'version 2.', - logger='root') - - super()._load_from_state_dict(state_dict, prefix, local_metadata, - strict, missing_keys, unexpected_keys, - error_msgs) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/sync_bn.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/sync_bn.py deleted file mode 100755 index 04302f031..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/ops/sync_bn.py +++ /dev/null @@ -1,279 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn.functional as F -from torch.autograd import Function -from torch.autograd.function import once_differentiable -from torch.nn.modules.module import Module -from torch.nn.parameter import Parameter - -from mmcv.cnn import NORM_LAYERS -from ..utils import ext_loader - -ext_module = ext_loader.load_ext('_ext', [ - 'sync_bn_forward_mean', 'sync_bn_forward_var', 'sync_bn_forward_output', - 'sync_bn_backward_param', 'sync_bn_backward_data' -]) - - -class SyncBatchNormFunction(Function): - - @staticmethod - def symbolic(g, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - return g.op( - 'mmcv::MMCVSyncBatchNorm', - input, - running_mean, - running_var, - weight, - bias, - momentum_f=momentum, - eps_f=eps, - group_i=group, - group_size_i=group_size, - stats_mode=stats_mode) - - @staticmethod - def forward(self, input, running_mean, running_var, weight, bias, momentum, - eps, group, group_size, stats_mode): - self.momentum = momentum - self.eps = eps - self.group = group - self.group_size = group_size - self.stats_mode = stats_mode - - assert isinstance( - input, (torch.HalfTensor, torch.FloatTensor, - torch.cuda.HalfTensor, torch.cuda.FloatTensor)), \ - f'only support Half or Float Tensor, but {input.type()}' - output = torch.zeros_like(input) - input3d = input.flatten(start_dim=2) - output3d = output.view_as(input3d) - num_channels = input3d.size(1) - - # ensure mean/var/norm/std are initialized as zeros - # ``torch.empty()`` does not guarantee that - mean = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - var = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - norm = torch.zeros_like( - input3d, dtype=torch.float, device=input3d.device) - std = torch.zeros( - num_channels, dtype=torch.float, device=input3d.device) - - batch_size = input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_forward_mean(input3d, mean) - batch_flag = torch.ones([1], device=mean.device, dtype=mean.dtype) - else: - # skip updating mean and leave it as zeros when the input is empty - batch_flag = torch.zeros([1], device=mean.device, dtype=mean.dtype) - - # synchronize mean and the batch flag - vec = torch.cat([mean, batch_flag]) - if self.stats_mode == 'N': - vec *= batch_size - if self.group_size > 1: - dist.all_reduce(vec, group=self.group) - total_batch = vec[-1].detach() - mean = vec[:num_channels] - - if self.stats_mode == 'default': - mean = mean / self.group_size - elif self.stats_mode == 'N': - mean = mean / total_batch.clamp(min=1) - else: - raise NotImplementedError - - # leave var as zeros when the input is empty - if batch_size > 0: - ext_module.sync_bn_forward_var(input3d, mean, var) - - if self.stats_mode == 'N': - var *= batch_size - if self.group_size > 1: - dist.all_reduce(var, group=self.group) - - if self.stats_mode == 'default': - var /= self.group_size - elif self.stats_mode == 'N': - var /= total_batch.clamp(min=1) - else: - raise NotImplementedError - - # if the total batch size over all the ranks is zero, - # we should not update the statistics in the current batch - update_flag = total_batch.clamp(max=1) - momentum = update_flag * self.momentum - ext_module.sync_bn_forward_output( - input3d, - mean, - var, - weight, - bias, - running_mean, - running_var, - norm, - std, - output3d, - eps=self.eps, - momentum=momentum, - group_size=self.group_size) - self.save_for_backward(norm, std, weight) - return output - - @staticmethod - @once_differentiable - def backward(self, grad_output): - norm, std, weight = self.saved_tensors - grad_weight = torch.zeros_like(weight) - grad_bias = torch.zeros_like(weight) - grad_input = torch.zeros_like(grad_output) - grad_output3d = grad_output.flatten(start_dim=2) - grad_input3d = grad_input.view_as(grad_output3d) - - batch_size = grad_input3d.size(0) - if batch_size > 0: - ext_module.sync_bn_backward_param(grad_output3d, norm, grad_weight, - grad_bias) - - # all reduce - if self.group_size > 1: - dist.all_reduce(grad_weight, group=self.group) - dist.all_reduce(grad_bias, group=self.group) - grad_weight /= self.group_size - grad_bias /= self.group_size - - if batch_size > 0: - ext_module.sync_bn_backward_data(grad_output3d, weight, - grad_weight, grad_bias, norm, std, - grad_input3d) - - return grad_input, None, None, grad_weight, grad_bias, \ - None, None, None, None, None - - -@NORM_LAYERS.register_module(name='MMSyncBN') -class SyncBatchNorm(Module): - """Synchronized Batch Normalization. - - Args: - num_features (int): number of features/chennels in input tensor - eps (float, optional): a value added to the denominator for numerical - stability. Defaults to 1e-5. - momentum (float, optional): the value used for the running_mean and - running_var computation. Defaults to 0.1. - affine (bool, optional): whether to use learnable affine parameters. - Defaults to True. - track_running_stats (bool, optional): whether to track the running - mean and variance during training. When set to False, this - module does not track such statistics, and initializes statistics - buffers ``running_mean`` and ``running_var`` as ``None``. When - these buffers are ``None``, this module always uses batch - statistics in both training and eval modes. Defaults to True. - group (int, optional): synchronization of stats happen within - each process group individually. By default it is synchronization - across the whole world. Defaults to None. - stats_mode (str, optional): The statistical mode. Available options - includes ``'default'`` and ``'N'``. Defaults to 'default'. - When ``stats_mode=='default'``, it computes the overall statistics - using those from each worker with equal weight, i.e., the - statistics are synchronized and simply divied by ``group``. This - mode will produce inaccurate statistics when empty tensors occur. - When ``stats_mode=='N'``, it compute the overall statistics using - the total number of batches in each worker ignoring the number of - group, i.e., the statistics are synchronized and then divied by - the total batch ``N``. This mode is beneficial when empty tensors - occur during training, as it average the total mean by the real - number of batch. - """ - - def __init__(self, - num_features, - eps=1e-5, - momentum=0.1, - affine=True, - track_running_stats=True, - group=None, - stats_mode='default'): - super(SyncBatchNorm, self).__init__() - self.num_features = num_features - self.eps = eps - self.momentum = momentum - self.affine = affine - self.track_running_stats = track_running_stats - group = dist.group.WORLD if group is None else group - self.group = group - self.group_size = dist.get_world_size(group) - assert stats_mode in ['default', 'N'], \ - f'"stats_mode" only accepts "default" and "N", got "{stats_mode}"' - self.stats_mode = stats_mode - if self.affine: - self.weight = Parameter(torch.Tensor(num_features)) - self.bias = Parameter(torch.Tensor(num_features)) - else: - self.register_parameter('weight', None) - self.register_parameter('bias', None) - if self.track_running_stats: - self.register_buffer('running_mean', torch.zeros(num_features)) - self.register_buffer('running_var', torch.ones(num_features)) - self.register_buffer('num_batches_tracked', - torch.tensor(0, dtype=torch.long)) - else: - self.register_buffer('running_mean', None) - self.register_buffer('running_var', None) - self.register_buffer('num_batches_tracked', None) - self.reset_parameters() - - def reset_running_stats(self): - if self.track_running_stats: - self.running_mean.zero_() - self.running_var.fill_(1) - self.num_batches_tracked.zero_() - - def reset_parameters(self): - self.reset_running_stats() - if self.affine: - self.weight.data.uniform_() # pytorch use ones_() - self.bias.data.zero_() - - def forward(self, input): - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input, got {input.dim()}D input') - if self.momentum is None: - exponential_average_factor = 0.0 - else: - exponential_average_factor = self.momentum - - if self.training and self.track_running_stats: - if self.num_batches_tracked is not None: - self.num_batches_tracked += 1 - if self.momentum is None: # use cumulative moving average - exponential_average_factor = 1.0 / float( - self.num_batches_tracked) - else: # use exponential moving average - exponential_average_factor = self.momentum - - if self.training or not self.track_running_stats: - return SyncBatchNormFunction.apply( - input, self.running_mean, self.running_var, self.weight, - self.bias, exponential_average_factor, self.eps, self.group, - self.group_size, self.stats_mode) - else: - return F.batch_norm(input, self.running_mean, self.running_var, - self.weight, self.bias, False, - exponential_average_factor, self.eps) - - def __repr__(self): - s = self.__class__.__name__ - s += f'({self.num_features}, ' - s += f'eps={self.eps}, ' - s += f'momentum={self.momentum}, ' - s += f'affine={self.affine}, ' - s += f'track_running_stats={self.track_running_stats}, ' - s += f'group_size={self.group_size},' - s += f'stats_mode={self.stats_mode})' - return s diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/__init__.py deleted file mode 100755 index 2ed2c17ad..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .collate import collate -from .data_container import DataContainer -from .data_parallel import MMDataParallel -from .distributed import MMDistributedDataParallel -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter, scatter_kwargs -from .utils import is_module_wrapper - -__all__ = [ - 'collate', 'DataContainer', 'MMDataParallel', 'MMDistributedDataParallel', - 'scatter', 'scatter_kwargs', 'is_module_wrapper', 'MODULE_WRAPPERS' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/_functions.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/_functions.py deleted file mode 100755 index 9b5a8a444..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/_functions.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import _get_stream - - -def scatter(input, devices, streams=None): - """Scatters tensor across multiple GPUs.""" - if streams is None: - streams = [None] * len(devices) - - if isinstance(input, list): - chunk_size = (len(input) - 1) // len(devices) + 1 - outputs = [ - scatter(input[i], [devices[i // chunk_size]], - [streams[i // chunk_size]]) for i in range(len(input)) - ] - return outputs - elif isinstance(input, torch.Tensor): - output = input.contiguous() - # TODO: copy to a pinned buffer first (if copying from CPU) - stream = streams[0] if output.numel() > 0 else None - if devices != [-1]: - with torch.cuda.device(devices[0]), torch.cuda.stream(stream): - output = output.cuda(devices[0], non_blocking=True) - else: - # unsqueeze the first dimension thus the tensor's shape is the - # same as those scattered with GPU. - output = output.unsqueeze(0) - return output - else: - raise Exception(f'Unknown type {type(input)}.') - - -def synchronize_stream(output, devices, streams): - if isinstance(output, list): - chunk_size = len(output) // len(devices) - for i in range(len(devices)): - for j in range(chunk_size): - synchronize_stream(output[i * chunk_size + j], [devices[i]], - [streams[i]]) - elif isinstance(output, torch.Tensor): - if output.numel() != 0: - with torch.cuda.device(devices[0]): - main_stream = torch.cuda.current_stream() - main_stream.wait_stream(streams[0]) - output.record_stream(main_stream) - else: - raise Exception(f'Unknown type {type(output)}.') - - -def get_input_device(input): - if isinstance(input, list): - for item in input: - input_device = get_input_device(item) - if input_device != -1: - return input_device - return -1 - elif isinstance(input, torch.Tensor): - return input.get_device() if input.is_cuda else -1 - else: - raise Exception(f'Unknown type {type(input)}.') - - -class Scatter: - - @staticmethod - def forward(target_gpus, input): - input_device = get_input_device(input) - streams = None - if input_device == -1 and target_gpus != [-1]: - # Perform CPU to GPU copies in a background stream - streams = [_get_stream(device) for device in target_gpus] - - outputs = scatter(input, target_gpus, streams) - # Synchronize with the copy stream - if streams is not None: - synchronize_stream(outputs, target_gpus, streams) - - return tuple(outputs) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/collate.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/collate.py deleted file mode 100755 index ad749197d..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/collate.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Mapping, Sequence - -import torch -import torch.nn.functional as F -from torch.utils.data.dataloader import default_collate - -from .data_container import DataContainer - - -def collate(batch, samples_per_gpu=1): - """Puts each data field into a tensor/DataContainer with outer dimension - batch size. - - Extend default_collate to add support for - :type:`~mmcv.parallel.DataContainer`. There are 3 cases. - - 1. cpu_only = True, e.g., meta data - 2. cpu_only = False, stack = True, e.g., images tensors - 3. cpu_only = False, stack = False, e.g., gt bboxes - """ - - if not isinstance(batch, Sequence): - raise TypeError(f'{batch.dtype} is not supported.') - - if isinstance(batch[0], DataContainer): - stacked = [] - if batch[0].cpu_only: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer( - stacked, batch[0].stack, batch[0].padding_value, cpu_only=True) - elif batch[0].stack: - for i in range(0, len(batch), samples_per_gpu): - assert isinstance(batch[i].data, torch.Tensor) - - if batch[i].pad_dims is not None: - ndim = batch[i].dim() - assert ndim > batch[i].pad_dims - max_shape = [0 for _ in range(batch[i].pad_dims)] - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = batch[i].size(-dim) - for sample in batch[i:i + samples_per_gpu]: - for dim in range(0, ndim - batch[i].pad_dims): - assert batch[i].size(dim) == sample.size(dim) - for dim in range(1, batch[i].pad_dims + 1): - max_shape[dim - 1] = max(max_shape[dim - 1], - sample.size(-dim)) - padded_samples = [] - for sample in batch[i:i + samples_per_gpu]: - pad = [0 for _ in range(batch[i].pad_dims * 2)] - for dim in range(1, batch[i].pad_dims + 1): - pad[2 * dim - - 1] = max_shape[dim - 1] - sample.size(-dim) - padded_samples.append( - F.pad( - sample.data, pad, value=sample.padding_value)) - stacked.append(default_collate(padded_samples)) - elif batch[i].pad_dims is None: - stacked.append( - default_collate([ - sample.data - for sample in batch[i:i + samples_per_gpu] - ])) - else: - raise ValueError( - 'pad_dims should be either None or integers (1-3)') - - else: - for i in range(0, len(batch), samples_per_gpu): - stacked.append( - [sample.data for sample in batch[i:i + samples_per_gpu]]) - return DataContainer(stacked, batch[0].stack, batch[0].padding_value) - elif isinstance(batch[0], Sequence): - transposed = zip(*batch) - return [collate(samples, samples_per_gpu) for samples in transposed] - elif isinstance(batch[0], Mapping): - return { - key: collate([d[key] for d in batch], samples_per_gpu) - for key in batch[0] - } - else: - return default_collate(batch) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/data_container.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/data_container.py deleted file mode 100755 index cedb0d32a..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/data_container.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch - - -def assert_tensor_type(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not isinstance(args[0].data, torch.Tensor): - raise AttributeError( - f'{args[0].__class__.__name__} has no attribute ' - f'{func.__name__} for type {args[0].datatype}') - return func(*args, **kwargs) - - return wrapper - - -class DataContainer: - """A container for any type of objects. - - Typically tensors will be stacked in the collate function and sliced along - some dimension in the scatter function. This behavior has some limitations. - 1. All tensors have to be the same size. - 2. Types are limited (numpy array or Tensor). - - We design `DataContainer` and `MMDataParallel` to overcome these - limitations. The behavior can be either of the following. - - - copy to GPU, pad all tensors to the same size and stack them - - copy to GPU without stacking - - leave the objects as is and pass it to the model - - pad_dims specifies the number of last few dimensions to do padding - """ - - def __init__(self, - data, - stack=False, - padding_value=0, - cpu_only=False, - pad_dims=2): - self._data = data - self._cpu_only = cpu_only - self._stack = stack - self._padding_value = padding_value - assert pad_dims in [None, 1, 2, 3] - self._pad_dims = pad_dims - - def __repr__(self): - return f'{self.__class__.__name__}({repr(self.data)})' - - def __len__(self): - return len(self._data) - - @property - def data(self): - return self._data - - @property - def datatype(self): - if isinstance(self.data, torch.Tensor): - return self.data.type() - else: - return type(self.data) - - @property - def cpu_only(self): - return self._cpu_only - - @property - def stack(self): - return self._stack - - @property - def padding_value(self): - return self._padding_value - - @property - def pad_dims(self): - return self._pad_dims - - @assert_tensor_type - def size(self, *args, **kwargs): - return self.data.size(*args, **kwargs) - - @assert_tensor_type - def dim(self): - return self.data.dim() diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/data_parallel.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/data_parallel.py deleted file mode 100755 index 79b5f69b6..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/data_parallel.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from itertools import chain - -from torch.nn.parallel import DataParallel - -from .scatter_gather import scatter_kwargs - - -class MMDataParallel(DataParallel): - """The DataParallel module that supports DataContainer. - - MMDataParallel has two main differences with PyTorch DataParallel: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data during both GPU and CPU inference. - - It implement two more APIs ``train_step()`` and ``val_step()``. - - Args: - module (:class:`nn.Module`): Module to be encapsulated. - device_ids (list[int]): Device IDS of modules to be scattered to. - Defaults to None when GPU is not available. - output_device (str | int): Device ID for output. Defaults to None. - dim (int): Dimension used to scatter the data. Defaults to 0. - """ - - def __init__(self, *args, dim=0, **kwargs): - super(MMDataParallel, self).__init__(*args, dim=dim, **kwargs) - self.dim = dim - - def forward(self, *inputs, **kwargs): - """Override the original forward function. - - The main difference lies in the CPU inference where the data in - :class:`DataContainers` will still be gathered. - """ - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module(*inputs[0], **kwargs[0]) - else: - return super().forward(*inputs, **kwargs) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.train_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - 'instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.train_step(*inputs[0], **kwargs[0]) - - def val_step(self, *inputs, **kwargs): - if not self.device_ids: - # We add the following line thus the module could gather and - # convert data containers as those in GPU inference - inputs, kwargs = self.scatter(inputs, kwargs, [-1]) - return self.module.val_step(*inputs[0], **kwargs[0]) - - assert len(self.device_ids) == 1, \ - ('MMDataParallel only supports single GPU training, if you need to' - ' train with multiple GPUs, please use MMDistributedDataParallel' - ' instead.') - - for t in chain(self.module.parameters(), self.module.buffers()): - if t.device != self.src_device_obj: - raise RuntimeError( - 'module must have its parameters and buffers ' - f'on device {self.src_device_obj} (device_ids[0]) but ' - f'found one of them on device: {t.device}') - - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - return self.module.val_step(*inputs[0], **kwargs[0]) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/distributed.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/distributed.py deleted file mode 100755 index b799a213d..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/distributed.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel.distributed import (DistributedDataParallel, - _find_tensors) - -from mmcv import print_log -from mmcv.utils import TORCH_VERSION, digit_version -from .scatter_gather import scatter_kwargs - - -class MMDistributedDataParallel(DistributedDataParallel): - """The DDP module that supports DataContainer. - - MMDDP has two main differences with PyTorch DDP: - - - It supports a custom type :class:`DataContainer` which allows more - flexible control of input data. - - It implement two APIs ``train_step()`` and ``val_step()``. - """ - - def to_kwargs(self, inputs, kwargs, device_id): - # Use `self.to_kwargs` instead of `self.scatter` in pytorch1.8 - # to move all tensors to device_id - return scatter_kwargs(inputs, kwargs, [device_id], dim=self.dim) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def train_step(self, *inputs, **kwargs): - """train_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.train_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if getattr(self, 'require_forward_param_sync', True): - self._sync_params() - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.train_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.train_step(*inputs, **kwargs) - - if torch.is_grad_enabled() and getattr( - self, 'require_backward_grad_sync', True): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output - - def val_step(self, *inputs, **kwargs): - """val_step() API for module wrapped by DistributedDataParallel. - - This method is basically the same as - ``DistributedDataParallel.forward()``, while replacing - ``self.module.forward()`` with ``self.module.val_step()``. - It is compatible with PyTorch 1.1 - 1.5. - """ - # In PyTorch >= 1.7, ``reducer._rebuild_buckets()`` is moved from the - # end of backward to the beginning of forward. - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) >= digit_version('1.7') - and self.reducer._rebuild_buckets()): - print_log( - 'Reducer buckets have been rebuilt in this iteration.', - logger='mmcv') - - if getattr(self, 'require_forward_param_sync', True): - self._sync_params() - if self.device_ids: - inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) - if len(self.device_ids) == 1: - output = self.module.val_step(*inputs[0], **kwargs[0]) - else: - outputs = self.parallel_apply( - self._module_copies[:len(inputs)], inputs, kwargs) - output = self.gather(outputs, self.output_device) - else: - output = self.module.val_step(*inputs, **kwargs) - - if torch.is_grad_enabled() and getattr( - self, 'require_backward_grad_sync', True): - if self.find_unused_parameters: - self.reducer.prepare_for_backward(list(_find_tensors(output))) - else: - self.reducer.prepare_for_backward([]) - else: - if ('parrots' not in TORCH_VERSION - and digit_version(TORCH_VERSION) > digit_version('1.2')): - self.require_forward_param_sync = False - return output diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/distributed_deprecated.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/distributed_deprecated.py deleted file mode 100755 index b593d4a9e..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/distributed_deprecated.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.distributed as dist -import torch.nn as nn -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - -from mmcv.utils import TORCH_VERSION, digit_version -from .registry import MODULE_WRAPPERS -from .scatter_gather import scatter_kwargs - - -@MODULE_WRAPPERS.register_module() -class MMDistributedDataParallel(nn.Module): - - def __init__(self, - module, - dim=0, - broadcast_buffers=True, - bucket_cap_mb=25): - super(MMDistributedDataParallel, self).__init__() - self.module = module - self.dim = dim - self.broadcast_buffers = broadcast_buffers - - self.broadcast_bucket_size = bucket_cap_mb * 1024 * 1024 - self._sync_params() - - def _dist_broadcast_coalesced(self, tensors, buffer_size): - for tensors in _take_tensors(tensors, buffer_size): - flat_tensors = _flatten_dense_tensors(tensors) - dist.broadcast(flat_tensors, 0) - for tensor, synced in zip( - tensors, _unflatten_dense_tensors(flat_tensors, tensors)): - tensor.copy_(synced) - - def _sync_params(self): - module_states = list(self.module.state_dict().values()) - if len(module_states) > 0: - self._dist_broadcast_coalesced(module_states, - self.broadcast_bucket_size) - if self.broadcast_buffers: - if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) < digit_version('1.0')): - buffers = [b.data for b in self.module._all_buffers()] - else: - buffers = [b.data for b in self.module.buffers()] - if len(buffers) > 0: - self._dist_broadcast_coalesced(buffers, - self.broadcast_bucket_size) - - def scatter(self, inputs, kwargs, device_ids): - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/registry.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/registry.py deleted file mode 100755 index 144f9fb16..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel, DistributedDataParallel - -from mmcv.utils import Registry - -MODULE_WRAPPERS = Registry('module wrapper') -MODULE_WRAPPERS.register_module(module=DataParallel) -MODULE_WRAPPERS.register_module(module=DistributedDataParallel) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/scatter_gather.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/scatter_gather.py deleted file mode 100755 index 900ff8856..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/scatter_gather.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from torch.nn.parallel._functions import Scatter as OrigScatter - -from ._functions import Scatter -from .data_container import DataContainer - - -def scatter(inputs, target_gpus, dim=0): - """Scatter inputs to target gpus. - - The only difference from original :func:`scatter` is to add support for - :type:`~mmcv.parallel.DataContainer`. - """ - - def scatter_map(obj): - if isinstance(obj, torch.Tensor): - if target_gpus != [-1]: - return OrigScatter.apply(target_gpus, None, dim, obj) - else: - # for CPU inference we use self-implemented scatter - return Scatter.forward(target_gpus, obj) - if isinstance(obj, DataContainer): - if obj.cpu_only: - return obj.data - else: - return Scatter.forward(target_gpus, obj.data) - if isinstance(obj, tuple) and len(obj) > 0: - return list(zip(*map(scatter_map, obj))) - if isinstance(obj, list) and len(obj) > 0: - out = list(map(list, zip(*map(scatter_map, obj)))) - return out - if isinstance(obj, dict) and len(obj) > 0: - out = list(map(type(obj), zip(*map(scatter_map, obj.items())))) - return out - return [obj for targets in target_gpus] - - # After scatter_map is called, a scatter_map cell will exist. This cell - # has a reference to the actual function scatter_map, which has references - # to a closure that has a reference to the scatter_map cell (because the - # fn is recursive). To avoid this reference cycle, we set the function to - # None, clearing the cell - try: - return scatter_map(inputs) - finally: - scatter_map = None - - -def scatter_kwargs(inputs, kwargs, target_gpus, dim=0): - """Scatter with support for kwargs dictionary.""" - inputs = scatter(inputs, target_gpus, dim) if inputs else [] - kwargs = scatter(kwargs, target_gpus, dim) if kwargs else [] - if len(inputs) < len(kwargs): - inputs.extend([() for _ in range(len(kwargs) - len(inputs))]) - elif len(kwargs) < len(inputs): - kwargs.extend([{} for _ in range(len(inputs) - len(kwargs))]) - inputs = tuple(inputs) - kwargs = tuple(kwargs) - return inputs, kwargs diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/utils.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/utils.py deleted file mode 100755 index 0f5712cb4..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/parallel/utils.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import MODULE_WRAPPERS - - -def is_module_wrapper(module): - """Check if a module is a module wrapper. - - The following 3 modules in MMCV (and their subclasses) are regarded as - module wrappers: DataParallel, DistributedDataParallel, - MMDistributedDataParallel (the deprecated version). You may add you own - module wrapper by registering it to mmcv.parallel.MODULE_WRAPPERS. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: True if the input module is a module wrapper. - """ - module_wrappers = tuple(MODULE_WRAPPERS.module_dict.values()) - return isinstance(module, module_wrappers) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/__init__.py deleted file mode 100755 index 52e4b48d3..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base_module import BaseModule, ModuleList, Sequential -from .base_runner import BaseRunner -from .builder import RUNNERS, build_runner -from .checkpoint import (CheckpointLoader, _load_checkpoint, - _load_checkpoint_with_prefix, load_checkpoint, - load_state_dict, save_checkpoint, weights_to_cpu) -from .default_constructor import DefaultRunnerConstructor -from .dist_utils import (allreduce_grads, allreduce_params, get_dist_info, - init_dist, master_only) -from .epoch_based_runner import EpochBasedRunner, Runner -from .fp16_utils import LossScaler, auto_fp16, force_fp32, wrap_fp16_model -from .hooks import (HOOKS, CheckpointHook, ClosureHook, DistEvalHook, - DistSamplerSeedHook, DvcliveLoggerHook, EMAHook, EvalHook, - Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, Hook, IterTimerHook, - LoggerHook, LrUpdaterHook, MlflowLoggerHook, - NeptuneLoggerHook, OptimizerHook, PaviLoggerHook, - SyncBuffersHook, TensorboardLoggerHook, TextLoggerHook, - WandbLoggerHook) -from .iter_based_runner import IterBasedRunner, IterLoader -from .log_buffer import LogBuffer -from .optimizer import (OPTIMIZER_BUILDERS, OPTIMIZERS, - DefaultOptimizerConstructor, build_optimizer, - build_optimizer_constructor) -from .priority import Priority, get_priority -from .utils import get_host_info, get_time_str, obj_from_dict, set_random_seed - -__all__ = [ - 'BaseRunner', 'Runner', 'EpochBasedRunner', 'IterBasedRunner', 'LogBuffer', - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'OptimizerHook', 'IterTimerHook', 'DistSamplerSeedHook', 'LoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'MlflowLoggerHook', - 'DvcliveLoggerHook', '_load_checkpoint', 'load_state_dict', - 'load_checkpoint', 'weights_to_cpu', 'save_checkpoint', 'Priority', - 'get_priority', 'get_host_info', 'get_time_str', 'obj_from_dict', - 'init_dist', 'get_dist_info', 'master_only', 'OPTIMIZER_BUILDERS', - 'OPTIMIZERS', 'DefaultOptimizerConstructor', 'build_optimizer', - 'build_optimizer_constructor', 'IterLoader', 'set_random_seed', - 'auto_fp16', 'force_fp32', 'wrap_fp16_model', 'Fp16OptimizerHook', - 'SyncBuffersHook', 'EMAHook', 'build_runner', 'RUNNERS', 'allreduce_grads', - 'allreduce_params', 'LossScaler', 'CheckpointLoader', 'BaseModule', - '_load_checkpoint_with_prefix', 'EvalHook', 'DistEvalHook', 'Sequential', - 'ModuleList', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook', 'DefaultRunnerConstructor' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/base_module.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/base_module.py deleted file mode 100755 index 529575b81..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/base_module.py +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import warnings -from abc import ABCMeta -from collections import defaultdict -from logging import FileHandler - -import torch.nn as nn - -from mmcv.runner.dist_utils import master_only -from mmcv.utils.logging import get_logger, logger_initialized, print_log - - -class BaseModule(nn.Module, metaclass=ABCMeta): - """Base module for all modules in openmmlab. - - ``BaseModule`` is a wrapper of ``torch.nn.Module`` with additional - functionality of parameter initialization. Compared with - ``torch.nn.Module``, ``BaseModule`` mainly adds three attributes. - - - ``init_cfg``: the config to control the initialization. - - ``init_weights``: The function of parameter - initialization and recording initialization - information. - - ``_params_init_info``: Used to track the parameter - initialization information. This attribute only - exists during executing the ``init_weights``. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, init_cfg=None): - """Initialize BaseModule, inherited from `torch.nn.Module`""" - - # NOTE init_cfg can be defined in different levels, but init_cfg - # in low levels has a higher priority. - - super(BaseModule, self).__init__() - # define default value of init_cfg instead of hard code - # in init_weights() function - self._is_init = False - - self.init_cfg = copy.deepcopy(init_cfg) - - # Backward compatibility in derived classes - # if pretrained is not None: - # warnings.warn('DeprecationWarning: pretrained is a deprecated \ - # key, please consider using init_cfg') - # self.init_cfg = dict(type='Pretrained', checkpoint=pretrained) - - @property - def is_init(self): - return self._is_init - - def init_weights(self): - """Initialize the weights.""" - - is_top_level_module = False - # check if it is top-level module - if not hasattr(self, '_params_init_info'): - # The `_params_init_info` is used to record the initialization - # information of the parameters - # the key should be the obj:`nn.Parameter` of model and the value - # should be a dict containing - # - init_info (str): The string that describes the initialization. - # - tmp_mean_value (FloatTensor): The mean of the parameter, - # which indicates whether the parameter has been modified. - # this attribute would be deleted after all parameters - # is initialized. - self._params_init_info = defaultdict(dict) - is_top_level_module = True - - # Initialize the `_params_init_info`, - # When detecting the `tmp_mean_value` of - # the corresponding parameter is changed, update related - # initialization information - for name, param in self.named_parameters(): - self._params_init_info[param][ - 'init_info'] = f'The value is the same before and ' \ - f'after calling `init_weights` ' \ - f'of {self.__class__.__name__} ' - self._params_init_info[param][ - 'tmp_mean_value'] = param.data.mean() - - # pass `params_init_info` to all submodules - # All submodules share the same `params_init_info`, - # so it will be updated when parameters are - # modified at any level of the model. - for sub_module in self.modules(): - sub_module._params_init_info = self._params_init_info - - # Get the initialized logger, if not exist, - # create a logger named `mmcv` - logger_names = list(logger_initialized.keys()) - logger_name = logger_names[0] if logger_names else 'mmcv' - - from ..cnn import initialize - from ..cnn.utils.weight_init import update_init_info - module_name = self.__class__.__name__ - if not self._is_init: - if self.init_cfg: - print_log( - f'initialize {module_name} with init_cfg {self.init_cfg}', - logger=logger_name) - initialize(self, self.init_cfg) - if isinstance(self.init_cfg, dict): - # prevent the parameters of - # the pre-trained model - # from being overwritten by - # the `init_weights` - if self.init_cfg['type'] == 'Pretrained': - return - - for m in self.children(): - if hasattr(m, 'init_weights'): - m.init_weights() - # users may overload the `init_weights` - update_init_info( - m, - init_info=f'Initialized by ' - f'user-defined `init_weights`' - f' in {m.__class__.__name__} ') - - self._is_init = True - else: - warnings.warn(f'init_weights of {self.__class__.__name__} has ' - f'been called more than once.') - - if is_top_level_module: - self._dump_init_info(logger_name) - - for sub_module in self.modules(): - del sub_module._params_init_info - - @master_only - def _dump_init_info(self, logger_name): - """Dump the initialization information to a file named - `initialization.log.json` in workdir. - - Args: - logger_name (str): The name of logger. - """ - - logger = get_logger(logger_name) - - with_file_handler = False - # dump the information to the logger file if there is a `FileHandler` - for handler in logger.handlers: - if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True - if not with_file_handler: - for name, param in self.named_parameters(): - print_log( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n ", - logger=logger_name) - - def __repr__(self): - s = super().__repr__() - if self.init_cfg: - s += f'\ninit_cfg={self.init_cfg}' - return s - - -class Sequential(BaseModule, nn.Sequential): - """Sequential module in openmmlab. - - Args: - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, *args, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.Sequential.__init__(self, *args) - - -class ModuleList(BaseModule, nn.ModuleList): - """ModuleList in openmmlab. - - Args: - modules (iterable, optional): an iterable of modules to add. - init_cfg (dict, optional): Initialization config dict. - """ - - def __init__(self, modules=None, init_cfg=None): - BaseModule.__init__(self, init_cfg) - nn.ModuleList.__init__(self, modules) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/base_runner.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/base_runner.py deleted file mode 100755 index 25cd98f51..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/base_runner.py +++ /dev/null @@ -1,542 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import logging -import os.path as osp -import warnings -from abc import ABCMeta, abstractmethod - -import torch -from torch.optim import Optimizer - -import mmcv -from ..parallel import is_module_wrapper -from .checkpoint import load_checkpoint -from .dist_utils import get_dist_info -from .hooks import HOOKS, Hook -from .log_buffer import LogBuffer -from .priority import Priority, get_priority -from .utils import get_time_str - - -class BaseRunner(metaclass=ABCMeta): - """The base class of Runner, a training helper for PyTorch. - - All subclasses should implement the following APIs: - - - ``run()`` - - ``train()`` - - ``val()`` - - ``save_checkpoint()`` - - Args: - model (:obj:`torch.nn.Module`): The model to be run. - batch_processor (callable): A callable method that process a data - batch. The interface of this method should be - `batch_processor(model, data, train_mode) -> dict` - optimizer (dict or :obj:`torch.optim.Optimizer`): It can be either an - optimizer (in most cases) or a dict of optimizers (in models that - requires more than one optimizer, e.g., GAN). - work_dir (str, optional): The working directory to save checkpoints - and logs. Defaults to None. - logger (:obj:`logging.Logger`): Logger used during training. - Defaults to None. (The default value is just for backward - compatibility) - meta (dict | None): A dict records some import information such as - environment info and seed, which will be logged in logger hook. - Defaults to None. - max_epochs (int, optional): Total training epochs. - max_iters (int, optional): Total training iterations. - """ - - def __init__(self, - model, - batch_processor=None, - optimizer=None, - work_dir=None, - logger=None, - meta=None, - max_iters=None, - max_epochs=None): - if batch_processor is not None: - if not callable(batch_processor): - raise TypeError('batch_processor must be callable, ' - f'but got {type(batch_processor)}') - warnings.warn('batch_processor is deprecated, please implement ' - 'train_step() and val_step() in the model instead.') - # raise an error is `batch_processor` is not None and - # `model.train_step()` exists. - if is_module_wrapper(model): - _model = model.module - else: - _model = model - if hasattr(_model, 'train_step') or hasattr(_model, 'val_step'): - raise RuntimeError( - 'batch_processor and model.train_step()/model.val_step() ' - 'cannot be both available.') - else: - assert hasattr(model, 'train_step') - - # check the type of `optimizer` - if isinstance(optimizer, dict): - for name, optim in optimizer.items(): - if not isinstance(optim, Optimizer): - raise TypeError( - f'optimizer must be a dict of torch.optim.Optimizers, ' - f'but optimizer["{name}"] is a {type(optim)}') - elif not isinstance(optimizer, Optimizer) and optimizer is not None: - raise TypeError( - f'optimizer must be a torch.optim.Optimizer object ' - f'or dict or None, but got {type(optimizer)}') - - # check the type of `logger` - if not isinstance(logger, logging.Logger): - raise TypeError(f'logger must be a logging.Logger object, ' - f'but got {type(logger)}') - - # check the type of `meta` - if meta is not None and not isinstance(meta, dict): - raise TypeError( - f'meta must be a dict or None, but got {type(meta)}') - - self.model = model - self.batch_processor = batch_processor - self.optimizer = optimizer - self.logger = logger - self.meta = meta - # create work_dir - if mmcv.is_str(work_dir): - self.work_dir = osp.abspath(work_dir) - mmcv.mkdir_or_exist(self.work_dir) - elif work_dir is None: - self.work_dir = None - else: - raise TypeError('"work_dir" must be a str or None') - - # get model name from the model class - if hasattr(self.model, 'module'): - self._model_name = self.model.module.__class__.__name__ - else: - self._model_name = self.model.__class__.__name__ - - self._rank, self._world_size = get_dist_info() - self.timestamp = get_time_str() - self.mode = None - self._hooks = [] - self._epoch = 0 - self._iter = 0 - self._inner_iter = 0 - - if max_epochs is not None and max_iters is not None: - raise ValueError( - 'Only one of `max_epochs` or `max_iters` can be set.') - - self._max_epochs = max_epochs - self._max_iters = max_iters - # TODO: Redesign LogBuffer, it is not flexible and elegant enough - self.log_buffer = LogBuffer() - - @property - def model_name(self): - """str: Name of the model, usually the module class name.""" - return self._model_name - - @property - def rank(self): - """int: Rank of current process. (distributed training)""" - return self._rank - - @property - def world_size(self): - """int: Number of processes participating in the job. - (distributed training)""" - return self._world_size - - @property - def hooks(self): - """list[:obj:`Hook`]: A list of registered hooks.""" - return self._hooks - - @property - def epoch(self): - """int: Current epoch.""" - return self._epoch - - @property - def iter(self): - """int: Current iteration.""" - return self._iter - - @property - def inner_iter(self): - """int: Iteration in an epoch.""" - return self._inner_iter - - @property - def max_epochs(self): - """int: Maximum training epochs.""" - return self._max_epochs - - @property - def max_iters(self): - """int: Maximum training iterations.""" - return self._max_iters - - @abstractmethod - def train(self): - pass - - @abstractmethod - def val(self): - pass - - @abstractmethod - def run(self, data_loaders, workflow, **kwargs): - pass - - @abstractmethod - def save_checkpoint(self, - out_dir, - filename_tmpl, - save_optimizer=True, - meta=None, - create_symlink=True): - pass - - def current_lr(self): - """Get current learning rates. - - Returns: - list[float] | dict[str, list[float]]: Current learning rates of all - param groups. If the runner has a dict of optimizers, this - method will return a dict. - """ - if isinstance(self.optimizer, torch.optim.Optimizer): - lr = [group['lr'] for group in self.optimizer.param_groups] - elif isinstance(self.optimizer, dict): - lr = dict() - for name, optim in self.optimizer.items(): - lr[name] = [group['lr'] for group in optim.param_groups] - else: - raise RuntimeError( - 'lr is not applicable because optimizer does not exist.') - return lr - - def current_momentum(self): - """Get current momentums. - - Returns: - list[float] | dict[str, list[float]]: Current momentums of all - param groups. If the runner has a dict of optimizers, this - method will return a dict. - """ - - def _get_momentum(optimizer): - momentums = [] - for group in optimizer.param_groups: - if 'momentum' in group.keys(): - momentums.append(group['momentum']) - elif 'betas' in group.keys(): - momentums.append(group['betas'][0]) - else: - momentums.append(0) - return momentums - - if self.optimizer is None: - raise RuntimeError( - 'momentum is not applicable because optimizer does not exist.') - elif isinstance(self.optimizer, torch.optim.Optimizer): - momentums = _get_momentum(self.optimizer) - elif isinstance(self.optimizer, dict): - momentums = dict() - for name, optim in self.optimizer.items(): - momentums[name] = _get_momentum(optim) - return momentums - - def register_hook(self, hook, priority='NORMAL'): - """Register a hook into the hook list. - - The hook will be inserted into a priority queue, with the specified - priority (See :class:`Priority` for details of priorities). - For hooks with the same priority, they will be triggered in the same - order as they are registered. - - Args: - hook (:obj:`Hook`): The hook to be registered. - priority (int or str or :obj:`Priority`): Hook priority. - Lower value means higher priority. - """ - assert isinstance(hook, Hook) - if hasattr(hook, 'priority'): - raise ValueError('"priority" is a reserved attribute for hooks') - priority = get_priority(priority) - hook.priority = priority - # insert the hook to a sorted list - inserted = False - for i in range(len(self._hooks) - 1, -1, -1): - if priority >= self._hooks[i].priority: - self._hooks.insert(i + 1, hook) - inserted = True - break - if not inserted: - self._hooks.insert(0, hook) - - def register_hook_from_cfg(self, hook_cfg): - """Register a hook from its cfg. - - Args: - hook_cfg (dict): Hook config. It should have at least keys 'type' - and 'priority' indicating its type and priority. - - Notes: - The specific hook class to register should not use 'type' and - 'priority' arguments during initialization. - """ - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = mmcv.build_from_cfg(hook_cfg, HOOKS) - self.register_hook(hook, priority=priority) - - def call_hook(self, fn_name): - """Call all hooks. - - Args: - fn_name (str): The function name in each hook to be called, such as - "before_train_epoch". - """ - for hook in self._hooks: - getattr(hook, fn_name)(self) - - def get_hook_info(self): - # Get hooks info in each stage - stage_hook_map = {stage: [] for stage in Hook.stages} - for hook in self.hooks: - try: - priority = Priority(hook.priority).name - except ValueError: - priority = hook.priority - classname = hook.__class__.__name__ - hook_info = f'({priority:<12}) {classname:<35}' - for trigger_stage in hook.get_triggered_stages(): - stage_hook_map[trigger_stage].append(hook_info) - - stage_hook_infos = [] - for stage in Hook.stages: - hook_infos = stage_hook_map[stage] - if len(hook_infos) > 0: - info = f'{stage}:\n' - info += '\n'.join(hook_infos) - info += '\n -------------------- ' - stage_hook_infos.append(info) - return '\n'.join(stage_hook_infos) - - def load_checkpoint(self, - filename, - map_location='cpu', - strict=False, - revise_keys=[(r'^module.', '')]): - return load_checkpoint( - self.model, - filename, - map_location, - strict, - self.logger, - revise_keys=revise_keys) - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - if map_location == 'default': - if torch.cuda.is_available(): - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint(checkpoint) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - if self.meta is None: - self.meta = {} - self.meta.setdefault('hook_msgs', {}) - # load `last_ckpt`, `best_score`, `best_ckpt`, etc. for hook messages - self.meta['hook_msgs'].update(checkpoint['meta'].get('hook_msgs', {})) - - # Re-calculate the number of iterations when resuming - # models with different number of GPUs - if 'config' in checkpoint['meta']: - config = mmcv.Config.fromstring( - checkpoint['meta']['config'], file_format='.py') - previous_gpu_ids = config.get('gpu_ids', None) - if previous_gpu_ids and len(previous_gpu_ids) > 0 and len( - previous_gpu_ids) != self.world_size: - self._iter = int(self._iter * len(previous_gpu_ids) / - self.world_size) - self.logger.info('the iteration number is changed due to ' - 'change of GPU number') - - # resume meta information meta - self.meta = checkpoint['meta'] - - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info('resumed epoch %d, iter %d', self.epoch, self.iter) - - def register_lr_hook(self, lr_config): - if lr_config is None: - return - elif isinstance(lr_config, dict): - assert 'policy' in lr_config - policy_type = lr_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of Lr updater. - # Since this is not applicable for ` - # CosineAnnealingLrUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'LrUpdaterHook' - lr_config['type'] = hook_type - hook = mmcv.build_from_cfg(lr_config, HOOKS) - else: - hook = lr_config - self.register_hook(hook, priority='VERY_HIGH') - - def register_momentum_hook(self, momentum_config): - if momentum_config is None: - return - if isinstance(momentum_config, dict): - assert 'policy' in momentum_config - policy_type = momentum_config.pop('policy') - # If the type of policy is all in lower case, e.g., 'cyclic', - # then its first letter will be capitalized, e.g., to be 'Cyclic'. - # This is for the convenient usage of momentum updater. - # Since this is not applicable for - # `CosineAnnealingMomentumUpdater`, - # the string will not be changed if it contains capital letters. - if policy_type == policy_type.lower(): - policy_type = policy_type.title() - hook_type = policy_type + 'MomentumUpdaterHook' - momentum_config['type'] = hook_type - hook = mmcv.build_from_cfg(momentum_config, HOOKS) - else: - hook = momentum_config - self.register_hook(hook, priority='HIGH') - - def register_optimizer_hook(self, optimizer_config): - if optimizer_config is None: - return - if isinstance(optimizer_config, dict): - optimizer_config.setdefault('type', 'OptimizerHook') - hook = mmcv.build_from_cfg(optimizer_config, HOOKS) - else: - hook = optimizer_config - self.register_hook(hook, priority='ABOVE_NORMAL') - - def register_checkpoint_hook(self, checkpoint_config): - if checkpoint_config is None: - return - if isinstance(checkpoint_config, dict): - checkpoint_config.setdefault('type', 'CheckpointHook') - hook = mmcv.build_from_cfg(checkpoint_config, HOOKS) - else: - hook = checkpoint_config - self.register_hook(hook, priority='NORMAL') - - def register_logger_hooks(self, log_config): - if log_config is None: - return - log_interval = log_config['interval'] - for info in log_config['hooks']: - logger_hook = mmcv.build_from_cfg( - info, HOOKS, default_args=dict(interval=log_interval)) - self.register_hook(logger_hook, priority='VERY_LOW') - - def register_timer_hook(self, timer_config): - if timer_config is None: - return - if isinstance(timer_config, dict): - timer_config_ = copy.deepcopy(timer_config) - hook = mmcv.build_from_cfg(timer_config_, HOOKS) - else: - hook = timer_config - self.register_hook(hook, priority='LOW') - - def register_custom_hooks(self, custom_config): - if custom_config is None: - return - - if not isinstance(custom_config, list): - custom_config = [custom_config] - - for item in custom_config: - if isinstance(item, dict): - self.register_hook_from_cfg(item) - else: - self.register_hook(item, priority='NORMAL') - - def register_profiler_hook(self, profiler_config): - if profiler_config is None: - return - if isinstance(profiler_config, dict): - profiler_config.setdefault('type', 'ProfilerHook') - hook = mmcv.build_from_cfg(profiler_config, HOOKS) - else: - hook = profiler_config - self.register_hook(hook) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - timer_config=dict(type='IterTimerHook'), - custom_hooks_config=None): - """Register default and custom hooks for training. - - Default and custom hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - self.register_lr_hook(lr_config) - self.register_momentum_hook(momentum_config) - self.register_optimizer_hook(optimizer_config) - self.register_checkpoint_hook(checkpoint_config) - self.register_timer_hook(timer_config) - self.register_logger_hooks(log_config) - self.register_custom_hooks(custom_hooks_config) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/builder.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/builder.py deleted file mode 100755 index 77c96ba0b..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/builder.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -from ..utils import Registry - -RUNNERS = Registry('runner') -RUNNER_BUILDERS = Registry('runner builder') - - -def build_runner_constructor(cfg): - return RUNNER_BUILDERS.build(cfg) - - -def build_runner(cfg, default_args=None): - runner_cfg = copy.deepcopy(cfg) - constructor_type = runner_cfg.pop('constructor', - 'DefaultRunnerConstructor') - runner_constructor = build_runner_constructor( - dict( - type=constructor_type, - runner_cfg=runner_cfg, - default_args=default_args)) - runner = runner_constructor() - return runner diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/checkpoint.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/checkpoint.py deleted file mode 100755 index 6ad605b85..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/checkpoint.py +++ /dev/null @@ -1,707 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import os -import os.path as osp -import pkgutil -import re -import time -import warnings -from collections import OrderedDict -from importlib import import_module -from tempfile import TemporaryDirectory - -import torch -import torchvision -from torch.optim import Optimizer -from torch.utils import model_zoo - -import mmcv -from ..fileio import FileClient -from ..fileio import load as load_file -from ..parallel import is_module_wrapper -from ..utils import mkdir_or_exist -from .dist_utils import get_dist_info - -ENV_MMCV_HOME = 'MMCV_HOME' -ENV_XDG_CACHE_HOME = 'XDG_CACHE_HOME' -DEFAULT_CACHE_DIR = '~/.cache' - - -def _get_mmcv_home(): - mmcv_home = os.path.expanduser( - os.getenv( - ENV_MMCV_HOME, - os.path.join( - os.getenv(ENV_XDG_CACHE_HOME, DEFAULT_CACHE_DIR), 'mmcv'))) - - mkdir_or_exist(mmcv_home) - return mmcv_home - - -def load_state_dict(module, state_dict, strict=False, logger=None): - """Load state_dict to a module. - - This method is modified from :meth:`torch.nn.Module.load_state_dict`. - Default value for ``strict`` is set to ``False`` and the message for - param mismatch will be shown even if strict is False. - - Args: - module (Module): Module that receives the state_dict. - state_dict (OrderedDict): Weights. - strict (bool): whether to strictly enforce that the keys - in :attr:`state_dict` match the keys returned by this module's - :meth:`~torch.nn.Module.state_dict` function. Default: ``False``. - logger (:obj:`logging.Logger`, optional): Logger to log the error - message. If not specified, print function will be used. - """ - unexpected_keys = [] - all_missing_keys = [] - err_msg = [] - - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata - - # use _load_from_state_dict to enable checkpoint version control - def load(module, prefix=''): - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - local_metadata = {} if metadata is None else metadata.get( - prefix[:-1], {}) - module._load_from_state_dict(state_dict, prefix, local_metadata, True, - all_missing_keys, unexpected_keys, - err_msg) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(module) - load = None # break load->load reference cycle - - # ignore "num_batches_tracked" of BN layers - missing_keys = [ - key for key in all_missing_keys if 'num_batches_tracked' not in key - ] - - if unexpected_keys: - err_msg.append('unexpected key in source ' - f'state_dict: {", ".join(unexpected_keys)}\n') - if missing_keys: - err_msg.append( - f'missing keys in source state_dict: {", ".join(missing_keys)}\n') - - rank, _ = get_dist_info() - if len(err_msg) > 0 and rank == 0: - err_msg.insert( - 0, 'The model and loaded state dict do not match exactly\n') - err_msg = '\n'.join(err_msg) - if strict: - raise RuntimeError(err_msg) - elif logger is not None: - logger.warning(err_msg) - else: - print(err_msg) - - -def get_torchvision_models(): - model_urls = dict() - for _, name, ispkg in pkgutil.walk_packages(torchvision.models.__path__): - if ispkg: - continue - _zoo = import_module(f'torchvision.models.{name}') - if hasattr(_zoo, 'model_urls'): - _urls = getattr(_zoo, 'model_urls') - model_urls.update(_urls) - return model_urls - - -def get_external_models(): - mmcv_home = _get_mmcv_home() - default_json_path = osp.join(mmcv.__path__[0], 'model_zoo/open_mmlab.json') - default_urls = load_file(default_json_path) - assert isinstance(default_urls, dict) - external_json_path = osp.join(mmcv_home, 'open_mmlab.json') - if osp.exists(external_json_path): - external_urls = load_file(external_json_path) - assert isinstance(external_urls, dict) - default_urls.update(external_urls) - - return default_urls - - -def get_mmcls_models(): - mmcls_json_path = osp.join(mmcv.__path__[0], 'model_zoo/mmcls.json') - mmcls_urls = load_file(mmcls_json_path) - - return mmcls_urls - - -def get_deprecated_model_names(): - deprecate_json_path = osp.join(mmcv.__path__[0], - 'model_zoo/deprecated.json') - deprecate_urls = load_file(deprecate_json_path) - assert isinstance(deprecate_urls, dict) - - return deprecate_urls - - -def _process_mmcls_checkpoint(checkpoint): - state_dict = checkpoint['state_dict'] - new_state_dict = OrderedDict() - for k, v in state_dict.items(): - if k.startswith('backbone.'): - new_state_dict[k[9:]] = v - new_checkpoint = dict(state_dict=new_state_dict) - - return new_checkpoint - - -class CheckpointLoader: - """A general checkpoint loader to manage all schemes.""" - - _schemes = {} - - @classmethod - def _register_scheme(cls, prefixes, loader, force=False): - if isinstance(prefixes, str): - prefixes = [prefixes] - else: - assert isinstance(prefixes, (list, tuple)) - for prefix in prefixes: - if (prefix not in cls._schemes) or force: - cls._schemes[prefix] = loader - else: - raise KeyError( - f'{prefix} is already registered as a loader backend, ' - 'add "force=True" if you want to override it') - # sort, longer prefixes take priority - cls._schemes = OrderedDict( - sorted(cls._schemes.items(), key=lambda t: t[0], reverse=True)) - - @classmethod - def register_scheme(cls, prefixes, loader=None, force=False): - """Register a loader to CheckpointLoader. - - This method can be used as a normal class method or a decorator. - - Args: - prefixes (str or list[str] or tuple[str]): - The prefix of the registered loader. - loader (function, optional): The loader function to be registered. - When this method is used as a decorator, loader is None. - Defaults to None. - force (bool, optional): Whether to override the loader - if the prefix has already been registered. Defaults to False. - """ - - if loader is not None: - cls._register_scheme(prefixes, loader, force=force) - return - - def _register(loader_cls): - cls._register_scheme(prefixes, loader_cls, force=force) - return loader_cls - - return _register - - @classmethod - def _get_checkpoint_loader(cls, path): - """Finds a loader that supports the given path. Falls back to the local - loader if no other loader is found. - - Args: - path (str): checkpoint path - - Returns: - loader (function): checkpoint loader - """ - - for p in cls._schemes: - if path.startswith(p): - return cls._schemes[p] - - @classmethod - def load_checkpoint(cls, filename, map_location=None, logger=None): - """load checkpoint through URL scheme path. - - Args: - filename (str): checkpoint file name with given prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - logger (:mod:`logging.Logger`, optional): The logger for message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint_loader = cls._get_checkpoint_loader(filename) - class_name = checkpoint_loader.__name__ - mmcv.print_log( - f'load checkpoint from {class_name[10:]} path: {filename}', logger) - return checkpoint_loader(filename, map_location) - - -@CheckpointLoader.register_scheme(prefixes='') -def load_from_local(filename, map_location): - """load checkpoint by local file path. - - Args: - filename (str): local checkpoint file path - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - if not osp.isfile(filename): - raise IOError(f'{filename} is not a checkpoint file') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('http://', 'https://')) -def load_from_http(filename, map_location=None, model_dir=None): - """load checkpoint through HTTP or HTTPS scheme path. In distributed - setting, this function only download checkpoint at local rank 0. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - model_dir (string, optional): directory in which to save the object, - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - rank, world_size = get_dist_info() - rank = int(os.environ.get('LOCAL_RANK', rank)) - if rank == 0: - checkpoint = model_zoo.load_url( - filename, model_dir=model_dir, map_location=map_location) - if world_size > 1: - torch.distributed.barrier() - if rank > 0: - checkpoint = model_zoo.load_url( - filename, model_dir=model_dir, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='pavi://') -def load_from_pavi(filename, map_location=None): - """load checkpoint through the file path prefixed with pavi. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with pavi prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - assert filename.startswith('pavi://'), \ - f'Expected filename startswith `pavi://`, but get {filename}' - model_path = filename[7:] - - try: - from pavi import modelcloud - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - - model = modelcloud.get(model_path) - with TemporaryDirectory() as tmp_dir: - downloaded_file = osp.join(tmp_dir, model.name) - model.download(downloaded_file) - checkpoint = torch.load(downloaded_file, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='s3://') -def load_from_ceph(filename, map_location=None, backend='petrel'): - """load checkpoint through the file path prefixed with s3. In distributed - setting, this function download ckpt at all ranks to different temporary - directories. - - Args: - filename (str): checkpoint file path with s3 prefix - map_location (str, optional): Same as :func:`torch.load`. - backend (str, optional): The storage backend type. Options are 'ceph', - 'petrel'. Default: 'petrel'. - - .. warning:: - :class:`mmcv.fileio.file_client.CephBackend` will be deprecated, - please use :class:`mmcv.fileio.file_client.PetrelBackend` instead. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - allowed_backends = ['ceph', 'petrel'] - if backend not in allowed_backends: - raise ValueError(f'Load from Backend {backend} is not supported.') - - if backend == 'ceph': - warnings.warn( - 'CephBackend will be deprecated, please use PetrelBackend instead') - - # CephClient and PetrelBackend have the same prefix 's3://' and the latter - # will be chosen as default. If PetrelBackend can not be instantiated - # successfully, the CephClient will be chosen. - try: - file_client = FileClient(backend=backend) - except ImportError: - allowed_backends.remove(backend) - file_client = FileClient(backend=allowed_backends[0]) - - with io.BytesIO(file_client.get(filename)) as buffer: - checkpoint = torch.load(buffer, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes=('modelzoo://', 'torchvision://')) -def load_from_torchvision(filename, map_location=None): - """load checkpoint through the file path prefixed with modelzoo or - torchvision. - - Args: - filename (str): checkpoint file path with modelzoo or - torchvision prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - model_urls = get_torchvision_models() - if filename.startswith('modelzoo://'): - warnings.warn('The URL scheme of "modelzoo://" is deprecated, please ' - 'use "torchvision://" instead') - model_name = filename[11:] - else: - model_name = filename[14:] - return load_from_http(model_urls[model_name], map_location=map_location) - - -@CheckpointLoader.register_scheme(prefixes=('open-mmlab://', 'openmmlab://')) -def load_from_openmmlab(filename, map_location=None): - """load checkpoint through the file path prefixed with open-mmlab or - openmmlab. - - Args: - filename (str): checkpoint file path with open-mmlab or - openmmlab prefix - map_location (str, optional): Same as :func:`torch.load`. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_external_models() - prefix_str = 'open-mmlab://' - if filename.startswith(prefix_str): - model_name = filename[13:] - else: - model_name = filename[12:] - prefix_str = 'openmmlab://' - - deprecated_urls = get_deprecated_model_names() - if model_name in deprecated_urls: - warnings.warn(f'{prefix_str}{model_name} is deprecated in favor ' - f'of {prefix_str}{deprecated_urls[model_name]}') - model_name = deprecated_urls[model_name] - model_url = model_urls[model_name] - # check if is url - if model_url.startswith(('http://', 'https://')): - checkpoint = load_from_http(model_url, map_location=map_location) - else: - filename = osp.join(_get_mmcv_home(), model_url) - if not osp.isfile(filename): - raise IOError(f'{filename} is not a checkpoint file') - checkpoint = torch.load(filename, map_location=map_location) - return checkpoint - - -@CheckpointLoader.register_scheme(prefixes='mmcls://') -def load_from_mmcls(filename, map_location=None): - """load checkpoint through the file path prefixed with mmcls. - - Args: - filename (str): checkpoint file path with mmcls prefix - map_location (str, optional): Same as :func:`torch.load`. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - model_urls = get_mmcls_models() - model_name = filename[8:] - checkpoint = load_from_http( - model_urls[model_name], map_location=map_location) - checkpoint = _process_mmcls_checkpoint(checkpoint) - return checkpoint - - -def _load_checkpoint(filename, map_location=None, logger=None): - """Load checkpoint from somewhere (modelzoo, file, url). - - Args: - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str, optional): Same as :func:`torch.load`. - Default: None. - logger (:mod:`logging.Logger`, optional): The logger for error message. - Default: None - - Returns: - dict or OrderedDict: The loaded checkpoint. It can be either an - OrderedDict storing model weights or a dict containing other - information, which depends on the checkpoint. - """ - return CheckpointLoader.load_checkpoint(filename, map_location, logger) - - -def _load_checkpoint_with_prefix(prefix, filename, map_location=None): - """Load partial pretrained model with specific prefix. - - Args: - prefix (str): The prefix of sub-module. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str | None): Same as :func:`torch.load`. Default: None. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - - checkpoint = _load_checkpoint(filename, map_location=map_location) - - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - if not prefix.endswith('.'): - prefix += '.' - prefix_len = len(prefix) - - state_dict = { - k[prefix_len:]: v - for k, v in state_dict.items() if k.startswith(prefix) - } - - assert state_dict, f'{prefix} is not in the pretrained model' - return state_dict - - -def load_checkpoint(model, - filename, - map_location=None, - strict=False, - logger=None, - revise_keys=[(r'^module\.', '')]): - """Load checkpoint from a file or URI. - - Args: - model (Module): Module to load checkpoint. - filename (str): Accept local filepath, URL, ``torchvision://xxx``, - ``open-mmlab://xxx``. Please refer to ``docs/model_zoo.md`` for - details. - map_location (str): Same as :func:`torch.load`. - strict (bool): Whether to allow different params for the model and - checkpoint. - logger (:mod:`logging.Logger` or None): The logger for error message. - revise_keys (list): A list of customized keywords to modify the - state_dict in checkpoint. Each item is a (pattern, replacement) - pair of the regular expression operations. Default: strip - the prefix 'module.' by [(r'^module\\.', '')]. - - Returns: - dict or OrderedDict: The loaded checkpoint. - """ - checkpoint = _load_checkpoint(filename, map_location, logger) - # OrderedDict is a subclass of dict - if not isinstance(checkpoint, dict): - raise RuntimeError( - f'No state_dict found in checkpoint file {filename}') - # get state_dict from checkpoint - if 'state_dict' in checkpoint: - state_dict = checkpoint['state_dict'] - else: - state_dict = checkpoint - - # strip prefix of state_dict - metadata = getattr(state_dict, '_metadata', OrderedDict()) - for p, r in revise_keys: - state_dict = OrderedDict( - {re.sub(p, r, k): v - for k, v in state_dict.items()}) - # Keep metadata in state_dict - state_dict._metadata = metadata - - # load state_dict - load_state_dict(model, state_dict, strict, logger) - return checkpoint - - -def weights_to_cpu(state_dict): - """Copy a model state_dict to cpu. - - Args: - state_dict (OrderedDict): Model weights on GPU. - - Returns: - OrderedDict: Model weights on GPU. - """ - state_dict_cpu = OrderedDict() - for key, val in state_dict.items(): - state_dict_cpu[key] = val.cpu() - # Keep metadata in state_dict - state_dict_cpu._metadata = getattr(state_dict, '_metadata', OrderedDict()) - return state_dict_cpu - - -def _save_to_state_dict(module, destination, prefix, keep_vars): - """Saves module state to `destination` dictionary. - - This method is modified from :meth:`torch.nn.Module._save_to_state_dict`. - - Args: - module (nn.Module): The module to generate state_dict. - destination (dict): A dict where state will be stored. - prefix (str): The prefix for parameters and buffers used in this - module. - """ - for name, param in module._parameters.items(): - if param is not None: - destination[prefix + name] = param if keep_vars else param.detach() - for name, buf in module._buffers.items(): - # remove check of _non_persistent_buffers_set to allow nn.BatchNorm2d - if buf is not None: - destination[prefix + name] = buf if keep_vars else buf.detach() - - -def get_state_dict(module, destination=None, prefix='', keep_vars=False): - """Returns a dictionary containing a whole state of the module. - - Both parameters and persistent buffers (e.g. running averages) are - included. Keys are corresponding parameter and buffer names. - - This method is modified from :meth:`torch.nn.Module.state_dict` to - recursively check parallel module in case that the model has a complicated - structure, e.g., nn.Module(nn.Module(DDP)). - - Args: - module (nn.Module): The module to generate state_dict. - destination (OrderedDict): Returned dict for the state of the - module. - prefix (str): Prefix of the key. - keep_vars (bool): Whether to keep the variable property of the - parameters. Default: False. - - Returns: - dict: A dictionary containing a whole state of the module. - """ - # recursively check parallel module in case that the model has a - # complicated structure, e.g., nn.Module(nn.Module(DDP)) - if is_module_wrapper(module): - module = module.module - - # below is the same as torch.nn.Module.state_dict() - if destination is None: - destination = OrderedDict() - destination._metadata = OrderedDict() - destination._metadata[prefix[:-1]] = local_metadata = dict( - version=module._version) - _save_to_state_dict(module, destination, prefix, keep_vars) - for name, child in module._modules.items(): - if child is not None: - get_state_dict( - child, destination, prefix + name + '.', keep_vars=keep_vars) - for hook in module._state_dict_hooks.values(): - hook_result = hook(module, destination, prefix, local_metadata) - if hook_result is not None: - destination = hook_result - return destination - - -def save_checkpoint(model, - filename, - optimizer=None, - meta=None, - file_client_args=None): - """Save checkpoint to file. - - The checkpoint will have 3 fields: ``meta``, ``state_dict`` and - ``optimizer``. By default ``meta`` will contain version and time info. - - Args: - model (Module): Module whose params are to be saved. - filename (str): Checkpoint filename. - optimizer (:obj:`Optimizer`, optional): Optimizer to be saved. - meta (dict, optional): Metadata to be saved in checkpoint. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError(f'meta must be a dict or None, but got {type(meta)}') - meta.update(mmcv_version=mmcv.__version__, time=time.asctime()) - - if is_module_wrapper(model): - model = model.module - - if hasattr(model, 'CLASSES') and model.CLASSES is not None: - # save class name to the meta - meta.update(CLASSES=model.CLASSES) - - checkpoint = { - 'meta': meta, - 'state_dict': weights_to_cpu(get_state_dict(model)) - } - # save optimizer state dict in the checkpoint - if isinstance(optimizer, Optimizer): - checkpoint['optimizer'] = optimizer.state_dict() - elif isinstance(optimizer, dict): - checkpoint['optimizer'] = {} - for name, optim in optimizer.items(): - checkpoint['optimizer'][name] = optim.state_dict() - - if filename.startswith('pavi://'): - if file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" if filename starts with' - f'"pavi://", but got {file_client_args}') - try: - from pavi import modelcloud - from pavi import exception - except ImportError: - raise ImportError( - 'Please install pavi to load checkpoint from modelcloud.') - model_path = filename[7:] - root = modelcloud.Folder() - model_dir, model_name = osp.split(model_path) - try: - model = modelcloud.get(model_dir) - except exception.NodeNotFoundError: - model = root.create_training_model(model_dir) - with TemporaryDirectory() as tmp_dir: - checkpoint_file = osp.join(tmp_dir, model_name) - with open(checkpoint_file, 'wb') as f: - torch.save(checkpoint, f) - f.flush() - model.create_file(checkpoint_file, name=model_name) - else: - file_client = FileClient.infer_client(file_client_args, filename) - with io.BytesIO() as f: - torch.save(checkpoint, f) - file_client.put(f.getvalue(), filename) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/default_constructor.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/default_constructor.py deleted file mode 100755 index 0bad847f2..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/default_constructor.py +++ /dev/null @@ -1,44 +0,0 @@ -from .builder import RUNNER_BUILDERS, RUNNERS - - -@RUNNER_BUILDERS.register_module() -class DefaultRunnerConstructor: - """Default constructor for runners. - - Custom existing `Runner` like `EpocBasedRunner` though `RunnerConstructor`. - For example, We can inject some new properties and functions for `Runner`. - - Example: - >>> from mmcv.runner import RUNNER_BUILDERS, build_runner - >>> # Define a new RunnerReconstructor - >>> @RUNNER_BUILDERS.register_module() - >>> class MyRunnerConstructor: - ... def __init__(self, runner_cfg, default_args=None): - ... if not isinstance(runner_cfg, dict): - ... raise TypeError('runner_cfg should be a dict', - ... f'but got {type(runner_cfg)}') - ... self.runner_cfg = runner_cfg - ... self.default_args = default_args - ... - ... def __call__(self): - ... runner = RUNNERS.build(self.runner_cfg, - ... default_args=self.default_args) - ... # Add new properties for existing runner - ... runner.my_name = 'my_runner' - ... runner.my_function = lambda self: print(self.my_name) - ... ... - >>> # build your runner - >>> runner_cfg = dict(type='EpochBasedRunner', max_epochs=40, - ... constructor='MyRunnerConstructor') - >>> runner = build_runner(runner_cfg) - """ - - def __init__(self, runner_cfg, default_args=None): - if not isinstance(runner_cfg, dict): - raise TypeError('runner_cfg should be a dict', - f'but got {type(runner_cfg)}') - self.runner_cfg = runner_cfg - self.default_args = default_args - - def __call__(self): - return RUNNERS.build(self.runner_cfg, default_args=self.default_args) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/dist_utils.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/dist_utils.py deleted file mode 100755 index d3a1ef3fd..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/dist_utils.py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import os -import subprocess -from collections import OrderedDict - -import torch -import torch.multiprocessing as mp -from torch import distributed as dist -from torch._utils import (_flatten_dense_tensors, _take_tensors, - _unflatten_dense_tensors) - - -def init_dist(launcher, backend='nccl', **kwargs): - if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') - if launcher == 'pytorch': - _init_dist_pytorch(backend, **kwargs) - elif launcher == 'mpi': - _init_dist_mpi(backend, **kwargs) - elif launcher == 'slurm': - _init_dist_slurm(backend, **kwargs) - else: - raise ValueError(f'Invalid launcher type: {launcher}') - - -def _init_dist_pytorch(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['RANK']) - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_mpi(backend, **kwargs): - # TODO: use local_rank instead of rank % num_gpus - rank = int(os.environ['OMPI_COMM_WORLD_RANK']) - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(rank % num_gpus) - dist.init_process_group(backend=backend, **kwargs) - - -def _init_dist_slurm(backend, port=None): - """Initialize slurm distributed training environment. - - If argument ``port`` is not specified, then the master port will be system - environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system - environment variable, then a default port ``29500`` will be used. - - Args: - backend (str): Backend of torch.distributed. - port (int, optional): Master port. Defaults to None. - """ - proc_id = int(os.environ['SLURM_PROCID']) - ntasks = int(os.environ['SLURM_NTASKS']) - node_list = os.environ['SLURM_NODELIST'] - num_gpus = torch.cuda.device_count() - torch.cuda.set_device(proc_id % num_gpus) - addr = subprocess.getoutput( - f'scontrol show hostname {node_list} | head -n1') - # specify master port - if port is not None: - os.environ['MASTER_PORT'] = str(port) - elif 'MASTER_PORT' in os.environ: - pass # use MASTER_PORT in the environment variable - else: - # 29500 is torch.distributed default port - os.environ['MASTER_PORT'] = '29500' - # use MASTER_ADDR in the environment variable if it already exists - if 'MASTER_ADDR' not in os.environ: - os.environ['MASTER_ADDR'] = addr - os.environ['WORLD_SIZE'] = str(ntasks) - os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) - os.environ['RANK'] = str(proc_id) - dist.init_process_group(backend=backend) - - -def get_dist_info(): - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - world_size = dist.get_world_size() - else: - rank = 0 - world_size = 1 - return rank, world_size - - -def master_only(func): - - @functools.wraps(func) - def wrapper(*args, **kwargs): - rank, _ = get_dist_info() - if rank == 0: - return func(*args, **kwargs) - - return wrapper - - -def allreduce_params(params, coalesce=True, bucket_size_mb=-1): - """Allreduce parameters. - - Args: - params (list[torch.Parameters]): List of parameters or buffers of a - model. - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - _, world_size = get_dist_info() - if world_size == 1: - return - params = [param.data for param in params] - if coalesce: - _allreduce_coalesced(params, world_size, bucket_size_mb) - else: - for tensor in params: - dist.all_reduce(tensor.div_(world_size)) - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - """Allreduce gradients. - - Args: - params (list[torch.Parameters]): List of parameters of a model - coalesce (bool, optional): Whether allreduce parameters as a whole. - Defaults to True. - bucket_size_mb (int, optional): Size of bucket, the unit is MB. - Defaults to -1. - """ - grads = [ - param.grad.data for param in params - if param.requires_grad and param.grad is not None - ] - _, world_size = get_dist_info() - if world_size == 1: - return - if coalesce: - _allreduce_coalesced(grads, world_size, bucket_size_mb) - else: - for tensor in grads: - dist.all_reduce(tensor.div_(world_size)) - - -def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1): - if bucket_size_mb > 0: - bucket_size_bytes = bucket_size_mb * 1024 * 1024 - buckets = _take_tensors(tensors, bucket_size_bytes) - else: - buckets = OrderedDict() - for tensor in tensors: - tp = tensor.type() - if tp not in buckets: - buckets[tp] = [] - buckets[tp].append(tensor) - buckets = buckets.values() - - for bucket in buckets: - flat_tensors = _flatten_dense_tensors(bucket) - dist.all_reduce(flat_tensors) - flat_tensors.div_(world_size) - for tensor, synced in zip( - bucket, _unflatten_dense_tensors(flat_tensors, bucket)): - tensor.copy_(synced) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/epoch_based_runner.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/epoch_based_runner.py deleted file mode 100755 index 2dd29357a..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/epoch_based_runner.py +++ /dev/null @@ -1,187 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .utils import get_host_info - - -@RUNNERS.register_module() -class EpochBasedRunner(BaseRunner): - """Epoch-based Runner. - - This runner train models epoch by epoch. - """ - - def run_iter(self, data_batch, train_mode, **kwargs): - if self.batch_processor is not None: - outputs = self.batch_processor( - self.model, data_batch, train_mode=train_mode, **kwargs) - elif train_mode: - outputs = self.model.train_step(data_batch, self.optimizer, - **kwargs) - else: - outputs = self.model.val_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('"batch_processor()" or "model.train_step()"' - 'and "model.val_step()" must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._max_iters = self._max_epochs * len(self.data_loader) - self.call_hook('before_train_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_train_iter') - self.run_iter(data_batch, train_mode=True, **kwargs) - self.call_hook('after_train_iter') - self._iter += 1 - - self.call_hook('after_train_epoch') - self._epoch += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - self.call_hook('before_val_epoch') - time.sleep(2) # Prevent possible deadlock during epoch transition - for i, data_batch in enumerate(self.data_loader): - self._inner_iter = i - self.call_hook('before_val_iter') - self.run_iter(data_batch, train_mode=False) - self.call_hook('after_val_iter') - - self.call_hook('after_val_epoch') - - def run(self, data_loaders, workflow, max_epochs=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, epochs) to specify the - running order and epochs. E.g, [('train', 2), ('val', 1)] means - running 2 epochs for training and 1 epoch for validation, - iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_epochs is not None: - warnings.warn( - 'setting max_epochs in run is deprecated, ' - 'please set max_epochs in runner_config', DeprecationWarning) - self._max_epochs = max_epochs - - assert self._max_epochs is not None, ( - 'max_epochs must be specified during instantiation') - - for i, flow in enumerate(workflow): - mode, epochs = flow - if mode == 'train': - self._max_iters = self._max_epochs * len(data_loaders[i]) - break - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d epochs', workflow, - self._max_epochs) - self.call_hook('before_run') - - while self.epoch < self._max_epochs: - for i, flow in enumerate(workflow): - mode, epochs = flow - if isinstance(mode, str): # self.train() - if not hasattr(self, mode): - raise ValueError( - f'runner has no method named "{mode}" to run an ' - 'epoch') - epoch_runner = getattr(self, mode) - else: - raise TypeError( - 'mode in workflow must be a str, but got {}'.format( - type(mode))) - - for _ in range(epochs): - if mode == 'train' and self.epoch >= self._max_epochs: - break - epoch_runner(data_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_run') - - def save_checkpoint(self, - out_dir, - filename_tmpl='epoch_{}.pth', - save_optimizer=True, - meta=None, - create_symlink=True): - """Save the checkpoint. - - Args: - out_dir (str): The directory that checkpoints are saved. - filename_tmpl (str, optional): The checkpoint filename template, - which contains a placeholder for the epoch number. - Defaults to 'epoch_{}.pth'. - save_optimizer (bool, optional): Whether to save the optimizer to - the checkpoint. Defaults to True. - meta (dict, optional): The meta information to be saved in the - checkpoint. Defaults to None. - create_symlink (bool, optional): Whether to create a symlink - "latest.pth" to point to the latest checkpoint. - Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.epoch + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - -@RUNNERS.register_module() -class Runner(EpochBasedRunner): - """Deprecated name of EpochBasedRunner.""" - - def __init__(self, *args, **kwargs): - warnings.warn( - 'Runner was deprecated, please use EpochBasedRunner instead') - super().__init__(*args, **kwargs) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/fp16_utils.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/fp16_utils.py deleted file mode 100755 index 4baab939a..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/fp16_utils.py +++ /dev/null @@ -1,410 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools -import warnings -from collections import abc -from inspect import getfullargspec - -import numpy as np -import torch -import torch.nn as nn - -from mmcv.utils import TORCH_VERSION, digit_version -from .dist_utils import allreduce_grads as _allreduce_grads - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.autocast would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - # Note that when PyTorch >= 1.6.0, we still cast tensor types to fp16 - # manually, so the behavior may not be consistent with real amp. - from torch.cuda.amp import autocast -except ImportError: - pass - - -def cast_tensor_type(inputs, src_type, dst_type): - """Recursively convert Tensor in inputs from src_type to dst_type. - - Args: - inputs: Inputs that to be casted. - src_type (torch.dtype): Source type.. - dst_type (torch.dtype): Destination type. - - Returns: - The same type with inputs, but all contained Tensors have been cast. - """ - if isinstance(inputs, nn.Module): - return inputs - elif isinstance(inputs, torch.Tensor): - return inputs.to(dst_type) - elif isinstance(inputs, str): - return inputs - elif isinstance(inputs, np.ndarray): - return inputs - elif isinstance(inputs, abc.Mapping): - return type(inputs)({ - k: cast_tensor_type(v, src_type, dst_type) - for k, v in inputs.items() - }) - elif isinstance(inputs, abc.Iterable): - return type(inputs)( - cast_tensor_type(item, src_type, dst_type) for item in inputs) - else: - return inputs - - -def auto_fp16(apply_to=None, out_fp32=False): - """Decorator to enable fp16 training automatically. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If inputs arguments are fp32 tensors, they will - be converted to fp16 automatically. Arguments other than fp32 tensors are - ignored. If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp32 (bool): Whether to convert the output back to fp32. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp16 - >>> @auto_fp16() - >>> def forward(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp16 - >>> @auto_fp16(apply_to=('pred', )) - >>> def do_something(self, pred, others): - >>> pass - """ - - def auto_fp16_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@auto_fp16 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - # NOTE: default args are not taken into consideration - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.float, torch.half)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = {} - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.float, torch.half) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=True): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp32: - output = cast_tensor_type(output, torch.half, torch.float) - return output - - return new_func - - return auto_fp16_wrapper - - -def force_fp32(apply_to=None, out_fp16=False): - """Decorator to convert input arguments to fp32 in force. - - This decorator is useful when you write custom modules and want to support - mixed precision training. If there are some inputs that must be processed - in fp32 mode, then this decorator can handle it. If inputs arguments are - fp16 tensors, they will be converted to fp32 automatically. Arguments other - than fp16 tensors are ignored. If you are using PyTorch >= 1.6, - torch.cuda.amp is used as the backend, otherwise, original mmcv - implementation will be adopted. - - Args: - apply_to (Iterable, optional): The argument names to be converted. - `None` indicates all arguments. - out_fp16 (bool): Whether to convert the output back to fp16. - - Example: - - >>> import torch.nn as nn - >>> class MyModule1(nn.Module): - >>> - >>> # Convert x and y to fp32 - >>> @force_fp32() - >>> def loss(self, x, y): - >>> pass - - >>> import torch.nn as nn - >>> class MyModule2(nn.Module): - >>> - >>> # convert pred to fp32 - >>> @force_fp32(apply_to=('pred', )) - >>> def post_process(self, pred, others): - >>> pass - """ - - def force_fp32_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # check if the module has set the attribute `fp16_enabled`, if not, - # just fallback to the original method. - if not isinstance(args[0], torch.nn.Module): - raise TypeError('@force_fp32 can only be used to decorate the ' - 'method of nn.Module') - if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled): - return old_func(*args, **kwargs) - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get the argument names to be casted - args_to_cast = args_info.args if apply_to is None else apply_to - # convert the args that need to be processed - new_args = [] - if args: - arg_names = args_info.args[:len(args)] - for i, arg_name in enumerate(arg_names): - if arg_name in args_to_cast: - new_args.append( - cast_tensor_type(args[i], torch.half, torch.float)) - else: - new_args.append(args[i]) - # convert the kwargs that need to be processed - new_kwargs = dict() - if kwargs: - for arg_name, arg_value in kwargs.items(): - if arg_name in args_to_cast: - new_kwargs[arg_name] = cast_tensor_type( - arg_value, torch.half, torch.float) - else: - new_kwargs[arg_name] = arg_value - # apply converted arguments to the decorated method - if (TORCH_VERSION != 'parrots' and - digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - with autocast(enabled=False): - output = old_func(*new_args, **new_kwargs) - else: - output = old_func(*new_args, **new_kwargs) - # cast the results back to fp32 if necessary - if out_fp16: - output = cast_tensor_type(output, torch.float, torch.half) - return output - - return new_func - - return force_fp32_wrapper - - -def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): - warnings.warning( - '"mmcv.runner.fp16_utils.allreduce_grads" is deprecated, and will be ' - 'removed in v2.8. Please switch to "mmcv.runner.allreduce_grads') - _allreduce_grads(params, coalesce=coalesce, bucket_size_mb=bucket_size_mb) - - -def wrap_fp16_model(model): - """Wrap the FP32 model to FP16. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the - backend, otherwise, original mmcv implementation will be adopted. - - For PyTorch >= 1.6, this function will - 1. Set fp16 flag inside the model to True. - - Otherwise: - 1. Convert FP32 model to FP16. - 2. Remain some necessary layers to be FP32, e.g., normalization layers. - 3. Set `fp16_enabled` flag inside the model to True. - - Args: - model (nn.Module): Model in FP32. - """ - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.6.0')): - # convert model to fp16 - model.half() - # patch the normalization layers to make it work in fp32 mode - patch_norm_fp32(model) - # set `fp16_enabled` flag - for m in model.modules(): - if hasattr(m, 'fp16_enabled'): - m.fp16_enabled = True - - -def patch_norm_fp32(module): - """Recursively convert normalization layers from FP16 to FP32. - - Args: - module (nn.Module): The modules to be converted in FP16. - - Returns: - nn.Module: The converted module, the normalization layers have been - converted to FP32. - """ - if isinstance(module, (nn.modules.batchnorm._BatchNorm, nn.GroupNorm)): - module.float() - if isinstance(module, nn.GroupNorm) or torch.__version__ < '1.3': - module.forward = patch_forward_method(module.forward, torch.half, - torch.float) - for child in module.children(): - patch_norm_fp32(child) - return module - - -def patch_forward_method(func, src_type, dst_type, convert_output=True): - """Patch the forward method of a module. - - Args: - func (callable): The original forward method. - src_type (torch.dtype): Type of input arguments to be converted from. - dst_type (torch.dtype): Type of input arguments to be converted to. - convert_output (bool): Whether to convert the output back to src_type. - - Returns: - callable: The patched forward method. - """ - - def new_forward(*args, **kwargs): - output = func(*cast_tensor_type(args, src_type, dst_type), - **cast_tensor_type(kwargs, src_type, dst_type)) - if convert_output: - output = cast_tensor_type(output, dst_type, src_type) - return output - - return new_forward - - -class LossScaler: - """Class that manages loss scaling in mixed precision training which - supports both dynamic or static mode. - - The implementation refers to - https://github.com/NVIDIA/apex/blob/master/apex/fp16_utils/loss_scaler.py. - Indirectly, by supplying ``mode='dynamic'`` for dynamic loss scaling. - It's important to understand how :class:`LossScaler` operates. - Loss scaling is designed to combat the problem of underflowing - gradients encountered at long times when training fp16 networks. - Dynamic loss scaling begins by attempting a very high loss - scale. Ironically, this may result in OVERflowing gradients. - If overflowing gradients are encountered, :class:`FP16_Optimizer` then - skips the update step for this particular iteration/minibatch, - and :class:`LossScaler` adjusts the loss scale to a lower value. - If a certain number of iterations occur without overflowing gradients - detected,:class:`LossScaler` increases the loss scale once more. - In this way :class:`LossScaler` attempts to "ride the edge" of always - using the highest loss scale possible without incurring overflow. - - Args: - init_scale (float): Initial loss scale value, default: 2**32. - scale_factor (float): Factor used when adjusting the loss scale. - Default: 2. - mode (str): Loss scaling mode. 'dynamic' or 'static' - scale_window (int): Number of consecutive iterations without an - overflow to wait before increasing the loss scale. Default: 1000. - """ - - def __init__(self, - init_scale=2**32, - mode='dynamic', - scale_factor=2., - scale_window=1000): - self.cur_scale = init_scale - self.cur_iter = 0 - assert mode in ('dynamic', - 'static'), 'mode can only be dynamic or static' - self.mode = mode - self.last_overflow_iter = -1 - self.scale_factor = scale_factor - self.scale_window = scale_window - - def has_overflow(self, params): - """Check if params contain overflow.""" - if self.mode != 'dynamic': - return False - for p in params: - if p.grad is not None and LossScaler._has_inf_or_nan(p.grad.data): - return True - return False - - def _has_inf_or_nan(x): - """Check if params contain NaN.""" - try: - cpu_sum = float(x.float().sum()) - except RuntimeError as instance: - if 'value cannot be converted' not in instance.args[0]: - raise - return True - else: - if cpu_sum == float('inf') or cpu_sum == -float('inf') \ - or cpu_sum != cpu_sum: - return True - return False - - def update_scale(self, overflow): - """update the current loss scale value when overflow happens.""" - if self.mode != 'dynamic': - return - if overflow: - self.cur_scale = max(self.cur_scale / self.scale_factor, 1) - self.last_overflow_iter = self.cur_iter - else: - if (self.cur_iter - self.last_overflow_iter) % \ - self.scale_window == 0: - self.cur_scale *= self.scale_factor - self.cur_iter += 1 - - def state_dict(self): - """Returns the state of the scaler as a :class:`dict`.""" - return dict( - cur_scale=self.cur_scale, - cur_iter=self.cur_iter, - mode=self.mode, - last_overflow_iter=self.last_overflow_iter, - scale_factor=self.scale_factor, - scale_window=self.scale_window) - - def load_state_dict(self, state_dict): - """Loads the loss_scaler state dict. - - Args: - state_dict (dict): scaler state. - """ - self.cur_scale = state_dict['cur_scale'] - self.cur_iter = state_dict['cur_iter'] - self.mode = state_dict['mode'] - self.last_overflow_iter = state_dict['last_overflow_iter'] - self.scale_factor = state_dict['scale_factor'] - self.scale_window = state_dict['scale_window'] - - @property - def loss_scale(self): - return self.cur_scale diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/__init__.py deleted file mode 100755 index 915af28ce..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .checkpoint import CheckpointHook -from .closure import ClosureHook -from .ema import EMAHook -from .evaluation import DistEvalHook, EvalHook -from .hook import HOOKS, Hook -from .iter_timer import IterTimerHook -from .logger import (DvcliveLoggerHook, LoggerHook, MlflowLoggerHook, - NeptuneLoggerHook, PaviLoggerHook, TensorboardLoggerHook, - TextLoggerHook, WandbLoggerHook) -from .lr_updater import LrUpdaterHook -from .memory import EmptyCacheHook -from .momentum_updater import MomentumUpdaterHook -from .optimizer import (Fp16OptimizerHook, GradientCumulativeFp16OptimizerHook, - GradientCumulativeOptimizerHook, OptimizerHook) -from .profiler import ProfilerHook -from .sampler_seed import DistSamplerSeedHook -from .sync_buffer import SyncBuffersHook - -__all__ = [ - 'HOOKS', 'Hook', 'CheckpointHook', 'ClosureHook', 'LrUpdaterHook', - 'OptimizerHook', 'Fp16OptimizerHook', 'IterTimerHook', - 'DistSamplerSeedHook', 'EmptyCacheHook', 'LoggerHook', 'MlflowLoggerHook', - 'PaviLoggerHook', 'TextLoggerHook', 'TensorboardLoggerHook', - 'NeptuneLoggerHook', 'WandbLoggerHook', 'DvcliveLoggerHook', - 'MomentumUpdaterHook', 'SyncBuffersHook', 'EMAHook', 'EvalHook', - 'DistEvalHook', 'ProfilerHook', 'GradientCumulativeOptimizerHook', - 'GradientCumulativeFp16OptimizerHook' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/checkpoint.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/checkpoint.py deleted file mode 100755 index 7bb75f402..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/checkpoint.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -from mmcv.fileio import FileClient -from ..dist_utils import allreduce_params, master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class CheckpointHook(Hook): - """Save checkpoints periodically. - - Args: - interval (int): The saving period. If ``by_epoch=True``, interval - indicates epochs, otherwise it indicates iterations. - Default: -1, which means "never". - by_epoch (bool): Saving checkpoints by epoch or by iteration. - Default: True. - save_optimizer (bool): Whether to save optimizer state_dict in the - checkpoint. It is usually used for resuming experiments. - Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, ``runner.work_dir`` will be used by default. If - specified, the ``out_dir`` will be the concatenation of ``out_dir`` - and the last level directory of ``runner.work_dir``. - `Changed in version 1.3.16.` - max_keep_ckpts (int, optional): The maximum checkpoints to keep. - In some cases we want only the latest few checkpoints and would - like to delete old ones to save the disk space. - Default: -1, which means unlimited. - save_last (bool, optional): Whether to force the last checkpoint to be - saved regardless of interval. Default: True. - sync_buffer (bool, optional): Whether to synchronize buffers in - different gpus. Default: False. - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - - .. warning:: - Before v1.3.16, the ``out_dir`` argument indicates the path where the - checkpoint is stored. However, since v1.3.16, ``out_dir`` indicates the - root directory and the final path to save checkpoint is the - concatenation of ``out_dir`` and the last level directory of - ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" - and the value of ``runner.work_dir`` is "/path/of/B", then the final - path will be "/path/of/A/B". - """ - - def __init__(self, - interval=-1, - by_epoch=True, - save_optimizer=True, - out_dir=None, - max_keep_ckpts=-1, - save_last=True, - sync_buffer=False, - file_client_args=None, - **kwargs): - self.interval = interval - self.by_epoch = by_epoch - self.save_optimizer = save_optimizer - self.out_dir = out_dir - self.max_keep_ckpts = max_keep_ckpts - self.save_last = save_last - self.args = kwargs - self.sync_buffer = sync_buffer - self.file_client_args = file_client_args - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - - runner.logger.info((f'Checkpoints will be saved to {self.out_dir} by ' - f'{self.file_client.name}.')) - - # disable the create_symlink option because some file backends do not - # allow to create a symlink - if 'create_symlink' in self.args: - if self.args[ - 'create_symlink'] and not self.file_client.allow_symlink: - self.args['create_symlink'] = False - warnings.warn( - ('create_symlink is set as True by the user but is changed' - 'to be False because creating symbolic link is not ' - f'allowed in {self.file_client.name}')) - else: - self.args['create_symlink'] = self.file_client.allow_symlink - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` epochs - # 2. reach the last epoch of training - if self.every_n_epochs( - runner, self.interval) or (self.save_last - and self.is_last_epoch(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.epoch + 1} epochs') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) - - @master_only - def _save_checkpoint(self, runner): - """Save the current checkpoint and delete unwanted checkpoint.""" - runner.save_checkpoint( - self.out_dir, save_optimizer=self.save_optimizer, **self.args) - if runner.meta is not None: - if self.by_epoch: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'epoch_{}.pth').format(runner.epoch + 1) - else: - cur_ckpt_filename = self.args.get( - 'filename_tmpl', 'iter_{}.pth').format(runner.iter + 1) - runner.meta.setdefault('hook_msgs', dict()) - runner.meta['hook_msgs']['last_ckpt'] = self.file_client.join_path( - self.out_dir, cur_ckpt_filename) - # remove other checkpoints - if self.max_keep_ckpts > 0: - if self.by_epoch: - name = 'epoch_{}.pth' - current_ckpt = runner.epoch + 1 - else: - name = 'iter_{}.pth' - current_ckpt = runner.iter + 1 - redundant_ckpts = range( - current_ckpt - self.max_keep_ckpts * self.interval, 0, - -self.interval) - filename_tmpl = self.args.get('filename_tmpl', name) - for _step in redundant_ckpts: - ckpt_path = self.file_client.join_path( - self.out_dir, filename_tmpl.format(_step)) - if self.file_client.isfile(ckpt_path): - self.file_client.remove(ckpt_path) - else: - break - - def after_train_iter(self, runner): - if self.by_epoch: - return - - # save checkpoint for following cases: - # 1. every ``self.interval`` iterations - # 2. reach the last iteration of training - if self.every_n_iters( - runner, self.interval) or (self.save_last - and self.is_last_iter(runner)): - runner.logger.info( - f'Saving checkpoint at {runner.iter + 1} iterations') - if self.sync_buffer: - allreduce_params(runner.model.buffers()) - self._save_checkpoint(runner) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/closure.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/closure.py deleted file mode 100755 index b955f81f4..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/closure.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ClosureHook(Hook): - - def __init__(self, fn_name, fn): - assert hasattr(self, fn_name) - assert callable(fn) - setattr(self, fn_name, fn) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/ema.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/ema.py deleted file mode 100755 index 15c7e6808..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/ema.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...parallel import is_module_wrapper -from ..hooks.hook import HOOKS, Hook - - -@HOOKS.register_module() -class EMAHook(Hook): - r"""Exponential Moving Average Hook. - - Use Exponential Moving Average on all parameters of model in training - process. All parameters have a ema backup, which update by the formula - as below. EMAHook takes priority over EvalHook and CheckpointSaverHook. - - .. math:: - - \text{Xema\_{t+1}} = (1 - \text{momentum}) \times - \text{Xema\_{t}} + \text{momentum} \times X_t - - Args: - momentum (float): The momentum used for updating ema parameter. - Defaults to 0.0002. - interval (int): Update ema parameter every interval iteration. - Defaults to 1. - warm_up (int): During first warm_up steps, we may use smaller momentum - to update ema parameters more slowly. Defaults to 100. - resume_from (str): The checkpoint path. Defaults to None. - """ - - def __init__(self, - momentum=0.0002, - interval=1, - warm_up=100, - resume_from=None): - assert isinstance(interval, int) and interval > 0 - self.warm_up = warm_up - self.interval = interval - assert momentum > 0 and momentum < 1 - self.momentum = momentum**interval - self.checkpoint = resume_from - - def before_run(self, runner): - """To resume model with it's ema parameters more friendly. - - Register ema parameter as ``named_buffer`` to model - """ - model = runner.model - if is_module_wrapper(model): - model = model.module - self.param_ema_buffer = {} - self.model_parameters = dict(model.named_parameters(recurse=True)) - for name, value in self.model_parameters.items(): - # "." is not allowed in module's buffer name - buffer_name = f"ema_{name.replace('.', '_')}" - self.param_ema_buffer[name] = buffer_name - model.register_buffer(buffer_name, value.data.clone()) - self.model_buffers = dict(model.named_buffers(recurse=True)) - if self.checkpoint is not None: - runner.resume(self.checkpoint) - - def after_train_iter(self, runner): - """Update ema parameter every self.interval iterations.""" - curr_step = runner.iter - # We warm up the momentum considering the instability at beginning - momentum = min(self.momentum, - (1 + curr_step) / (self.warm_up + curr_step)) - if curr_step % self.interval != 0: - return - for name, parameter in self.model_parameters.items(): - buffer_name = self.param_ema_buffer[name] - buffer_parameter = self.model_buffers[buffer_name] - buffer_parameter.mul_(1 - momentum).add_(momentum, parameter.data) - - def after_train_epoch(self, runner): - """We load parameter values from ema backup to model before the - EvalHook.""" - self._swap_ema_parameters() - - def before_train_epoch(self, runner): - """We recover model's parameter from ema backup after last epoch's - EvalHook.""" - self._swap_ema_parameters() - - def _swap_ema_parameters(self): - """Swap the parameter of model with parameter in ema_buffer.""" - for name, value in self.model_parameters.items(): - temp = value.data.clone() - ema_buffer = self.model_buffers[self.param_ema_buffer[name]] - value.data.copy_(ema_buffer.data) - ema_buffer.data.copy_(temp) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/evaluation.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/evaluation.py deleted file mode 100755 index 1eeb44650..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/evaluation.py +++ /dev/null @@ -1,509 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings -from math import inf - -import torch.distributed as dist -from torch.nn.modules.batchnorm import _BatchNorm -from torch.utils.data import DataLoader - -from mmcv.fileio import FileClient -from mmcv.utils import is_seq_of -from .hook import Hook -from .logger import LoggerHook - - -class EvalHook(Hook): - """Non-Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - Default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader, and return the test results. If ``None``, the default - test function ``mmcv.engine.single_gpu_test`` will be used. - (default: ``None``) - greater_keys (List[str] | None, optional): Metric keys that will be - inferred by 'greater' comparison rule. If ``None``, - _default_greater_keys will be used. (default: ``None``) - less_keys (List[str] | None, optional): Metric keys that will be - inferred by 'less' comparison rule. If ``None``, _default_less_keys - will be used. (default: ``None``) - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - `New in version 1.3.16.` - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - `New in version 1.3.16.` - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - - Notes: - If new arguments are added for EvalHook, tools/test.py, - tools/eval_metric.py may be affected. - """ - - # Since the key for determine greater or less is related to the downstream - # tasks, downstream repos may need to overwrite the following inner - # variable accordingly. - - rule_map = {'greater': lambda x, y: x > y, 'less': lambda x, y: x < y} - init_value_map = {'greater': -inf, 'less': inf} - _default_greater_keys = [ - 'acc', 'top', 'AR@', 'auc', 'precision', 'mAP', 'mDice', 'mIoU', - 'mAcc', 'aAcc' - ] - _default_less_keys = ['loss'] - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - out_dir=None, - file_client_args=None, - **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError(f'dataloader must be a pytorch DataLoader, ' - f'but got {type(dataloader)}') - - if interval <= 0: - raise ValueError(f'interval must be a positive number, ' - f'but got {interval}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean' - - if start is not None and start < 0: - raise ValueError(f'The evaluation start epoch {start} is smaller ' - f'than 0') - - self.dataloader = dataloader - self.interval = interval - self.start = start - self.by_epoch = by_epoch - - assert isinstance(save_best, str) or save_best is None, \ - '""save_best"" should be a str or None ' \ - f'rather than {type(save_best)}' - self.save_best = save_best - self.eval_kwargs = eval_kwargs - self.initial_flag = True - - if test_fn is None: - from mmcv.engine import single_gpu_test - self.test_fn = single_gpu_test - else: - self.test_fn = test_fn - - if greater_keys is None: - self.greater_keys = self._default_greater_keys - else: - if not isinstance(greater_keys, (list, tuple)): - greater_keys = (greater_keys, ) - assert is_seq_of(greater_keys, str) - self.greater_keys = greater_keys - - if less_keys is None: - self.less_keys = self._default_less_keys - else: - if not isinstance(less_keys, (list, tuple)): - less_keys = (less_keys, ) - assert is_seq_of(less_keys, str) - self.less_keys = less_keys - - if self.save_best is not None: - self.best_ckpt_path = None - self._init_rule(rule, self.save_best) - - self.out_dir = out_dir - self.file_client_args = file_client_args - - def _init_rule(self, rule, key_indicator): - """Initialize rule, key_indicator, comparison_func, and best score. - - Here is the rule to determine which rule is used for key indicator - when the rule is not specific (note that the key indicator matching - is case-insensitive): - 1. If the key indicator is in ``self.greater_keys``, the rule will be - specified as 'greater'. - 2. Or if the key indicator is in ``self.less_keys``, the rule will be - specified as 'less'. - 3. Or if the key indicator is equal to the substring in any one item - in ``self.greater_keys``, the rule will be specified as 'greater'. - 4. Or if the key indicator is equal to the substring in any one item - in ``self.less_keys``, the rule will be specified as 'less'. - - Args: - rule (str | None): Comparison rule for best score. - key_indicator (str | None): Key indicator to determine the - comparison rule. - """ - if rule not in self.rule_map and rule is not None: - raise KeyError(f'rule must be greater, less or None, ' - f'but got {rule}.') - - if rule is None: - if key_indicator != 'auto': - # `_lc` here means we use the lower case of keys for - # case-insensitive matching - key_indicator_lc = key_indicator.lower() - greater_keys = [key.lower() for key in self.greater_keys] - less_keys = [key.lower() for key in self.less_keys] - - if key_indicator_lc in greater_keys: - rule = 'greater' - elif key_indicator_lc in less_keys: - rule = 'less' - elif any(key in key_indicator_lc for key in greater_keys): - rule = 'greater' - elif any(key in key_indicator_lc for key in less_keys): - rule = 'less' - else: - raise ValueError(f'Cannot infer the rule for key ' - f'{key_indicator}, thus a specific rule ' - f'must be specified.') - self.rule = rule - self.key_indicator = key_indicator - if self.rule is not None: - self.compare_func = self.rule_map[self.rule] - - def before_run(self, runner): - if not self.out_dir: - self.out_dir = runner.work_dir - - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - - # if `self.out_dir` is not equal to `runner.work_dir`, it means that - # `self.out_dir` is set so the final `self.out_dir` is the - # concatenation of `self.out_dir` and the last level directory of - # `runner.work_dir` - if self.out_dir != runner.work_dir: - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'The best checkpoint will be saved to {self.out_dir} by ' - f'{self.file_client.name}')) - - if self.save_best is not None: - if runner.meta is None: - warnings.warn('runner.meta is None. Creating an empty one.') - runner.meta = dict() - runner.meta.setdefault('hook_msgs', dict()) - self.best_ckpt_path = runner.meta['hook_msgs'].get( - 'best_ckpt', None) - - def before_train_iter(self, runner): - """Evaluate the model only at the start of training by iteration.""" - if self.by_epoch or not self.initial_flag: - return - if self.start is not None and runner.iter >= self.start: - self.after_train_iter(runner) - self.initial_flag = False - - def before_train_epoch(self, runner): - """Evaluate the model only at the start of training by epoch.""" - if not (self.by_epoch and self.initial_flag): - return - if self.start is not None and runner.epoch >= self.start: - self.after_train_epoch(runner) - self.initial_flag = False - - def after_train_iter(self, runner): - """Called after every training iter to evaluate the results.""" - if not self.by_epoch and self._should_evaluate(runner): - # Because the priority of EvalHook is higher than LoggerHook, the - # training log and the evaluating log are mixed. Therefore, - # we need to dump the training log and clear it before evaluating - # log is generated. In addition, this problem will only appear in - # `IterBasedRunner` whose `self.by_epoch` is False, because - # `EpochBasedRunner` whose `self.by_epoch` is True calls - # `_do_evaluate` in `after_train_epoch` stage, and at this stage - # the training log has been printed, so it will not cause any - # problem. more details at - # https://github.com/open-mmlab/mmsegmentation/issues/694 - for hook in runner._hooks: - if isinstance(hook, LoggerHook): - hook.after_train_iter(runner) - runner.log_buffer.clear() - - self._do_evaluate(runner) - - def after_train_epoch(self, runner): - """Called after every training epoch to evaluate the results.""" - if self.by_epoch and self._should_evaluate(runner): - self._do_evaluate(runner) - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - results = self.test_fn(runner.model, self.dataloader) - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to save - # the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) - - def _should_evaluate(self, runner): - """Judge whether to perform evaluation. - - Here is the rule to judge whether to perform evaluation: - 1. It will not perform evaluation during the epoch/iteration interval, - which is determined by ``self.interval``. - 2. It will not perform evaluation if the start time is larger than - current time. - 3. It will not perform evaluation when current time is larger than - the start time but during epoch/iteration interval. - - Returns: - bool: The flag indicating whether to perform evaluation. - """ - if self.by_epoch: - current = runner.epoch - check_time = self.every_n_epochs - else: - current = runner.iter - check_time = self.every_n_iters - - if self.start is None: - if not check_time(runner, self.interval): - # No evaluation during the interval. - return False - elif (current + 1) < self.start: - # No evaluation if start is larger than the current time. - return False - else: - # Evaluation only at epochs/iters 3, 5, 7... - # if start==3 and interval==2 - if (current + 1 - self.start) % self.interval: - return False - return True - - def _save_ckpt(self, runner, key_score): - """Save the best checkpoint. - - It will compare the score according to the compare function, write - related information (best score, best checkpoint path) and save the - best checkpoint into ``work_dir``. - """ - if self.by_epoch: - current = f'epoch_{runner.epoch + 1}' - cur_type, cur_time = 'epoch', runner.epoch + 1 - else: - current = f'iter_{runner.iter + 1}' - cur_type, cur_time = 'iter', runner.iter + 1 - - best_score = runner.meta['hook_msgs'].get( - 'best_score', self.init_value_map[self.rule]) - if self.compare_func(key_score, best_score): - best_score = key_score - runner.meta['hook_msgs']['best_score'] = best_score - - if self.best_ckpt_path and self.file_client.isfile( - self.best_ckpt_path): - self.file_client.remove(self.best_ckpt_path) - runner.logger.info( - (f'The previous best checkpoint {self.best_ckpt_path} was ' - 'removed')) - - best_ckpt_name = f'best_{self.key_indicator}_{current}.pth' - self.best_ckpt_path = self.file_client.join_path( - self.out_dir, best_ckpt_name) - runner.meta['hook_msgs']['best_ckpt'] = self.best_ckpt_path - - runner.save_checkpoint( - self.out_dir, best_ckpt_name, create_symlink=False) - runner.logger.info( - f'Now best checkpoint is saved as {best_ckpt_name}.') - runner.logger.info( - f'Best {self.key_indicator} is {best_score:0.4f} ' - f'at {cur_time} {cur_type}.') - - def evaluate(self, runner, results): - """Evaluate the results. - - Args: - runner (:obj:`mmcv.Runner`): The underlined training runner. - results (list): Output results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - - if self.save_best is not None: - # If the performance of model is pool, the `eval_res` may be an - # empty dict and it will raise exception when `self.save_best` is - # not None. More details at - # https://github.com/open-mmlab/mmdetection/issues/6265. - if not eval_res: - warnings.warn( - 'Since `eval_res` is an empty dict, the behavior to save ' - 'the best checkpoint will be skipped in this evaluation.') - return None - - if self.key_indicator == 'auto': - # infer from eval_results - self._init_rule(self.rule, list(eval_res.keys())[0]) - return eval_res[self.key_indicator] - - return None - - -class DistEvalHook(EvalHook): - """Distributed evaluation hook. - - This hook will regularly perform evaluation in a given interval when - performing in distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader, whose dataset has - implemented ``evaluate`` function. - start (int | None, optional): Evaluation starting epoch. It enables - evaluation before the training starts if ``start`` <= the resuming - epoch. If None, whether to evaluate is merely decided by - ``interval``. Default: None. - interval (int): Evaluation interval. Default: 1. - by_epoch (bool): Determine perform evaluation by epoch or by iteration. - If set to True, it will perform by epoch. Otherwise, by iteration. - default: True. - save_best (str, optional): If a metric is specified, it would measure - the best checkpoint during evaluation. The information about best - checkpoint would be saved in ``runner.meta['hook_msgs']`` to keep - best score value and best checkpoint path, which will be also - loaded when resume checkpoint. Options are the evaluation metrics - on the test dataset. e.g., ``bbox_mAP``, ``segm_mAP`` for bbox - detection and instance segmentation. ``AR@100`` for proposal - recall. If ``save_best`` is ``auto``, the first key of the returned - ``OrderedDict`` result will be used. Default: None. - rule (str | None, optional): Comparison rule for best score. If set to - None, it will infer a reasonable rule. Keys such as 'acc', 'top' - .etc will be inferred by 'greater' rule. Keys contain 'loss' will - be inferred by 'less' rule. Options are 'greater', 'less', None. - Default: None. - test_fn (callable, optional): test a model with samples from a - dataloader in a multi-gpu manner, and return the test results. If - ``None``, the default test function ``mmcv.engine.multi_gpu_test`` - will be used. (default: ``None``) - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - broadcast_bn_buffer (bool): Whether to broadcast the - buffer(running_mean and running_var) of rank 0 to other rank - before evaluation. Default: True. - out_dir (str, optional): The root directory to save checkpoints. If not - specified, `runner.work_dir` will be used by default. If specified, - the `out_dir` will be the concatenation of `out_dir` and the last - level directory of `runner.work_dir`. - file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. Default: None. - **eval_kwargs: Evaluation arguments fed into the evaluate function of - the dataset. - """ - - def __init__(self, - dataloader, - start=None, - interval=1, - by_epoch=True, - save_best=None, - rule=None, - test_fn=None, - greater_keys=None, - less_keys=None, - broadcast_bn_buffer=True, - tmpdir=None, - gpu_collect=False, - out_dir=None, - file_client_args=None, - **eval_kwargs): - - if test_fn is None: - from mmcv.engine import multi_gpu_test - test_fn = multi_gpu_test - - super().__init__( - dataloader, - start=start, - interval=interval, - by_epoch=by_epoch, - save_best=save_best, - rule=rule, - test_fn=test_fn, - greater_keys=greater_keys, - less_keys=less_keys, - out_dir=out_dir, - file_client_args=file_client_args, - **eval_kwargs) - - self.broadcast_bn_buffer = broadcast_bn_buffer - self.tmpdir = tmpdir - self.gpu_collect = gpu_collect - - def _do_evaluate(self, runner): - """perform evaluation and save ckpt.""" - # Synchronization of BatchNorm's buffer (running_mean - # and running_var) is not supported in the DDP of pytorch, - # which may cause the inconsistent performance of models in - # different ranks, so we broadcast BatchNorm's buffers - # of rank 0 to other ranks to avoid this. - if self.broadcast_bn_buffer: - model = runner.model - for name, module in model.named_modules(): - if isinstance(module, - _BatchNorm) and module.track_running_stats: - dist.broadcast(module.running_var, 0) - dist.broadcast(module.running_mean, 0) - - tmpdir = self.tmpdir - if tmpdir is None: - tmpdir = osp.join(runner.work_dir, '.eval_hook') - - results = self.test_fn( - runner.model, - self.dataloader, - tmpdir=tmpdir, - gpu_collect=self.gpu_collect) - if runner.rank == 0: - print('\n') - runner.log_buffer.output['eval_iter_num'] = len(self.dataloader) - key_score = self.evaluate(runner, results) - # the key_score may be `None` so it needs to skip the action to - # save the best checkpoint - if self.save_best and key_score: - self._save_ckpt(runner, key_score) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/hook.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/hook.py deleted file mode 100755 index f2d1c9865..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/hook.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry, is_method_overridden - -HOOKS = Registry('hook') - - -class Hook: - stages = ('before_run', 'before_train_epoch', 'before_train_iter', - 'after_train_iter', 'after_train_epoch', 'before_val_epoch', - 'before_val_iter', 'after_val_iter', 'after_val_epoch', - 'after_run') - - def before_run(self, runner): - pass - - def after_run(self, runner): - pass - - def before_epoch(self, runner): - pass - - def after_epoch(self, runner): - pass - - def before_iter(self, runner): - pass - - def after_iter(self, runner): - pass - - def before_train_epoch(self, runner): - self.before_epoch(runner) - - def before_val_epoch(self, runner): - self.before_epoch(runner) - - def after_train_epoch(self, runner): - self.after_epoch(runner) - - def after_val_epoch(self, runner): - self.after_epoch(runner) - - def before_train_iter(self, runner): - self.before_iter(runner) - - def before_val_iter(self, runner): - self.before_iter(runner) - - def after_train_iter(self, runner): - self.after_iter(runner) - - def after_val_iter(self, runner): - self.after_iter(runner) - - def every_n_epochs(self, runner, n): - return (runner.epoch + 1) % n == 0 if n > 0 else False - - def every_n_inner_iters(self, runner, n): - return (runner.inner_iter + 1) % n == 0 if n > 0 else False - - def every_n_iters(self, runner, n): - return (runner.iter + 1) % n == 0 if n > 0 else False - - def end_of_epoch(self, runner): - return runner.inner_iter + 1 == len(runner.data_loader) - - def is_last_epoch(self, runner): - return runner.epoch + 1 == runner._max_epochs - - def is_last_iter(self, runner): - return runner.iter + 1 == runner._max_iters - - def get_triggered_stages(self): - trigger_stages = set() - for stage in Hook.stages: - if is_method_overridden(stage, Hook, self): - trigger_stages.add(stage) - - # some methods will be triggered in multi stages - # use this dict to map method to stages. - method_stages_map = { - 'before_epoch': ['before_train_epoch', 'before_val_epoch'], - 'after_epoch': ['after_train_epoch', 'after_val_epoch'], - 'before_iter': ['before_train_iter', 'before_val_iter'], - 'after_iter': ['after_train_iter', 'after_val_iter'], - } - - for method, map_stages in method_stages_map.items(): - if is_method_overridden(method, Hook, self): - trigger_stages.update(map_stages) - - return [stage for stage in Hook.stages if stage in trigger_stages] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/iter_timer.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/iter_timer.py deleted file mode 100755 index f91d2492d..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/iter_timer.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright (c) OpenMMLab. All rights reserved. - -import time -import torch.distributed as dist - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class IterTimerHook(Hook): - - def before_epoch(self, runner): - self.t = time.time() - - def before_iter(self, runner): - self.batch_size = runner.data_loader._dataloader.batch_size - runner.log_buffer.update({'data_time': time.time() - self.t}) - - def after_iter(self, runner): - iter_info = {'time': time.time() - self.t} - fps = self.batch_size / iter_info["time"] - - if dist.is_initialized(): - fps = fps * dist.get_world_size() - - iter_info["fps"] = fps - - runner.log_buffer.update(iter_info) - self.t = time.time() diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/__init__.py deleted file mode 100755 index a0b6b3456..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .base import LoggerHook -from .dvclive import DvcliveLoggerHook -from .mlflow import MlflowLoggerHook -from .neptune import NeptuneLoggerHook -from .pavi import PaviLoggerHook -from .tensorboard import TensorboardLoggerHook -from .text import TextLoggerHook -from .wandb import WandbLoggerHook - -__all__ = [ - 'LoggerHook', 'MlflowLoggerHook', 'PaviLoggerHook', - 'TensorboardLoggerHook', 'TextLoggerHook', 'WandbLoggerHook', - 'NeptuneLoggerHook', 'DvcliveLoggerHook' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/base.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/base.py deleted file mode 100755 index f84525672..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/base.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from abc import ABCMeta, abstractmethod - -import numpy as np -import torch - -from ..hook import Hook - - -class LoggerHook(Hook): - """Base class for logger hooks. - - Args: - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging. - by_epoch (bool): Whether EpochBasedRunner is used. - """ - - __metaclass__ = ABCMeta - - def __init__(self, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - self.interval = interval - self.ignore_last = ignore_last - self.reset_flag = reset_flag - self.by_epoch = by_epoch - - @abstractmethod - def log(self, runner): - pass - - @staticmethod - def is_scalar(val, include_np=True, include_torch=True): - """Tell the input variable is a scalar or not. - - Args: - val: Input variable. - include_np (bool): Whether include 0-d np.ndarray as a scalar. - include_torch (bool): Whether include 0-d torch.Tensor as a scalar. - - Returns: - bool: True or False. - """ - if isinstance(val, numbers.Number): - return True - elif include_np and isinstance(val, np.ndarray) and val.ndim == 0: - return True - elif include_torch and isinstance(val, torch.Tensor) and len(val) == 1: - return True - else: - return False - - def get_mode(self, runner): - if runner.mode == 'train': - if 'time' in runner.log_buffer.output: - mode = 'train' - else: - mode = 'val' - elif runner.mode == 'val': - mode = 'val' - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return mode - - def get_epoch(self, runner): - if runner.mode == 'train': - epoch = runner.epoch + 1 - elif runner.mode == 'val': - # normal val mode - # runner.epoch += 1 has been done before val workflow - epoch = runner.epoch - else: - raise ValueError(f"runner mode should be 'train' or 'val', " - f'but got {runner.mode}') - return epoch - - def get_iter(self, runner, inner_iter=False): - """Get the current training iteration step.""" - if self.by_epoch and inner_iter: - current_iter = runner.inner_iter + 1 - else: - current_iter = runner.iter + 1 - return current_iter - - def get_lr_tags(self, runner): - tags = {} - lrs = runner.current_lr() - if isinstance(lrs, dict): - for name, value in lrs.items(): - tags[f'learning_rate/{name}'] = value[0] - else: - tags['learning_rate'] = lrs[0] - return tags - - def get_momentum_tags(self, runner): - tags = {} - momentums = runner.current_momentum() - if isinstance(momentums, dict): - for name, value in momentums.items(): - tags[f'momentum/{name}'] = value[0] - else: - tags['momentum'] = momentums[0] - return tags - - def get_loggable_tags(self, - runner, - allow_scalar=True, - allow_text=False, - add_mode=True, - tags_to_skip=('time', 'data_time')): - tags = {} - for var, val in runner.log_buffer.output.items(): - if var in tags_to_skip: - continue - if self.is_scalar(val) and not allow_scalar: - continue - if isinstance(val, str) and not allow_text: - continue - if add_mode: - var = f'{self.get_mode(runner)}/{var}' - tags[var] = val - tags.update(self.get_lr_tags(runner)) - tags.update(self.get_momentum_tags(runner)) - return tags - - def before_run(self, runner): - for hook in runner.hooks[::-1]: - if isinstance(hook, LoggerHook): - hook.reset_flag = True - break - - def before_epoch(self, runner): - runner.log_buffer.clear() # clear logs of last epoch - - def after_train_iter(self, runner): - if self.by_epoch and self.every_n_inner_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif not self.by_epoch and self.every_n_iters(runner, self.interval): - runner.log_buffer.average(self.interval) - elif self.end_of_epoch(runner) and not self.ignore_last: - # not precise but more stable - runner.log_buffer.average(self.interval) - - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_train_epoch(self, runner): - if runner.log_buffer.ready: - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() - - def after_val_epoch(self, runner): - runner.log_buffer.average() - self.log(runner) - if self.reset_flag: - runner.log_buffer.clear_output() diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/dvclive.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/dvclive.py deleted file mode 100755 index 687cdc58c..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/dvclive.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class DvcliveLoggerHook(LoggerHook): - """Class to log metrics with dvclive. - - It requires `dvclive`_ to be installed. - - Args: - path (str): Directory where dvclive will write TSV log files. - interval (int): Logging interval (every k iterations). - Default 10. - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - Default: True. - reset_flag (bool): Whether to clear the output buffer after logging. - Default: True. - by_epoch (bool): Whether EpochBasedRunner is used. - Default: True. - - .. _dvclive: - https://dvc.org/doc/dvclive - """ - - def __init__(self, - path, - interval=10, - ignore_last=True, - reset_flag=True, - by_epoch=True): - - super(DvcliveLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.path = path - self.import_dvclive() - - def import_dvclive(self): - try: - import dvclive - except ImportError: - raise ImportError( - 'Please run "pip install dvclive" to install dvclive') - self.dvclive = dvclive - - @master_only - def before_run(self, runner): - self.dvclive.init(self.path) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for k, v in tags.items(): - self.dvclive.log(k, v, step=self.get_iter(runner)) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/mlflow.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/mlflow.py deleted file mode 100755 index f9a72592b..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/mlflow.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class MlflowLoggerHook(LoggerHook): - - def __init__(self, - exp_name=None, - tags=None, - log_model=True, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - """Class to log metrics and (optionally) a trained model to MLflow. - - It requires `MLflow`_ to be installed. - - Args: - exp_name (str, optional): Name of the experiment to be used. - Default None. - If not None, set the active experiment. - If experiment does not exist, an experiment with provided name - will be created. - tags (dict of str: str, optional): Tags for the current run. - Default None. - If not None, set tags for the current run. - log_model (bool, optional): Whether to log an MLflow artifact. - Default True. - If True, log runner.model as an MLflow artifact - for the current run. - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging - by_epoch (bool): Whether EpochBasedRunner is used. - - .. _MLflow: - https://www.mlflow.org/docs/latest/index.html - """ - super(MlflowLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_mlflow() - self.exp_name = exp_name - self.tags = tags - self.log_model = log_model - - def import_mlflow(self): - try: - import mlflow - import mlflow.pytorch as mlflow_pytorch - except ImportError: - raise ImportError( - 'Please run "pip install mlflow" to install mlflow') - self.mlflow = mlflow - self.mlflow_pytorch = mlflow_pytorch - - @master_only - def before_run(self, runner): - super(MlflowLoggerHook, self).before_run(runner) - if self.exp_name is not None: - self.mlflow.set_experiment(self.exp_name) - if self.tags is not None: - self.mlflow.set_tags(self.tags) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - self.mlflow.log_metrics(tags, step=self.get_iter(runner)) - - @master_only - def after_run(self, runner): - if self.log_model: - self.mlflow_pytorch.log_model(runner.model, 'models') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/neptune.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/neptune.py deleted file mode 100755 index 7a38772b0..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/neptune.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class NeptuneLoggerHook(LoggerHook): - """Class to log metrics to NeptuneAI. - - It requires `neptune-client` to be installed. - - Args: - init_kwargs (dict): a dict contains the initialization keys as below: - - project (str): Name of a project in a form of - namespace/project_name. If None, the value of - NEPTUNE_PROJECT environment variable will be taken. - - api_token (str): User’s API token. - If None, the value of NEPTUNE_API_TOKEN environment - variable will be taken. Note: It is strongly recommended - to use NEPTUNE_API_TOKEN environment variable rather than - placing your API token in plain text in your source code. - - name (str, optional, default is 'Untitled'): Editable name of - the run. Name is displayed in the run's Details and in - Runs table as a column. - Check https://docs.neptune.ai/api-reference/neptune#init for - more init arguments. - interval (int): Logging interval (every k iterations). - ignore_last (bool): Ignore the log of last iterations in each epoch - if less than `interval`. - reset_flag (bool): Whether to clear the output buffer after logging - by_epoch (bool): Whether EpochBasedRunner is used. - - .. _NeptuneAI: - https://docs.neptune.ai/you-should-know/logging-metadata - """ - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=True, - with_step=True, - by_epoch=True): - - super(NeptuneLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_neptune() - self.init_kwargs = init_kwargs - self.with_step = with_step - - def import_neptune(self): - try: - import neptune.new as neptune - except ImportError: - raise ImportError( - 'Please run "pip install neptune-client" to install neptune') - self.neptune = neptune - self.run = None - - @master_only - def before_run(self, runner): - if self.init_kwargs: - self.run = self.neptune.init(**self.init_kwargs) - else: - self.run = self.neptune.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - for tag_name, tag_value in tags.items(): - if self.with_step: - self.run[tag_name].log( - tag_value, step=self.get_iter(runner)) - else: - tags['global_step'] = self.get_iter(runner) - self.run[tag_name].log(tags) - - @master_only - def after_run(self, runner): - self.run.stop() diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/pavi.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/pavi.py deleted file mode 100755 index ba2f6e8df..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/pavi.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp - -import torch -import yaml - -import mmcv -from ....parallel.utils import is_module_wrapper -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class PaviLoggerHook(LoggerHook): - - def __init__(self, - init_kwargs=None, - add_graph=False, - add_last_ckpt=False, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True, - img_key='img_info'): - super(PaviLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.init_kwargs = init_kwargs - self.add_graph = add_graph - self.add_last_ckpt = add_last_ckpt - self.img_key = img_key - - @master_only - def before_run(self, runner): - super(PaviLoggerHook, self).before_run(runner) - try: - from pavi import SummaryWriter - except ImportError: - raise ImportError('Please run "pip install pavi" to install pavi.') - - self.run_name = runner.work_dir.split('/')[-1] - - if not self.init_kwargs: - self.init_kwargs = dict() - self.init_kwargs['name'] = self.run_name - self.init_kwargs['model'] = runner._model_name - if runner.meta is not None: - if 'config_dict' in runner.meta: - config_dict = runner.meta['config_dict'] - assert isinstance( - config_dict, - dict), ('meta["config_dict"] has to be of a dict, ' - f'but got {type(config_dict)}') - elif 'config_file' in runner.meta: - config_file = runner.meta['config_file'] - config_dict = dict(mmcv.Config.fromfile(config_file)) - else: - config_dict = None - if config_dict is not None: - # 'max_.*iter' is parsed in pavi sdk as the maximum iterations - # to properly set up the progress bar. - config_dict = config_dict.copy() - config_dict.setdefault('max_iter', runner.max_iters) - # non-serializable values are first converted in - # mmcv.dump to json - config_dict = json.loads( - mmcv.dump(config_dict, file_format='json')) - session_text = yaml.dump(config_dict) - self.init_kwargs['session_text'] = session_text - self.writer = SummaryWriter(**self.init_kwargs) - - def get_step(self, runner): - """Get the total training step/epoch.""" - if self.get_mode(runner) == 'val' and self.by_epoch: - return self.get_epoch(runner) - else: - return self.get_iter(runner) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, add_mode=False) - if tags: - self.writer.add_scalars( - self.get_mode(runner), tags, self.get_step(runner)) - - @master_only - def after_run(self, runner): - if self.add_last_ckpt: - ckpt_path = osp.join(runner.work_dir, 'latest.pth') - if osp.islink(ckpt_path): - ckpt_path = osp.join(runner.work_dir, os.readlink(ckpt_path)) - - if osp.isfile(ckpt_path): - # runner.epoch += 1 has been done before `after_run`. - iteration = runner.epoch if self.by_epoch else runner.iter - return self.writer.add_snapshot_file( - tag=self.run_name, - snapshot_file_path=ckpt_path, - iteration=iteration) - - # flush the buffer and send a task ending signal to Pavi - self.writer.close() - - @master_only - def before_epoch(self, runner): - if runner.epoch == 0 and self.add_graph: - if is_module_wrapper(runner.model): - _model = runner.model.module - else: - _model = runner.model - device = next(_model.parameters()).device - data = next(iter(runner.data_loader)) - image = data[self.img_key][0:1].to(device) - with torch.no_grad(): - self.writer.add_graph(_model, image) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/tensorboard.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/tensorboard.py deleted file mode 100755 index a8d50366f..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/tensorboard.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.utils import TORCH_VERSION, digit_version -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TensorboardLoggerHook(LoggerHook): - - def __init__(self, - log_dir=None, - interval=10, - ignore_last=True, - reset_flag=False, - by_epoch=True): - super(TensorboardLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.log_dir = log_dir - - @master_only - def before_run(self, runner): - super(TensorboardLoggerHook, self).before_run(runner) - if (TORCH_VERSION == 'parrots' - or digit_version(TORCH_VERSION) < digit_version('1.1')): - try: - from tensorboardX import SummaryWriter - except ImportError: - raise ImportError('Please install tensorboardX to use ' - 'TensorboardLoggerHook.') - else: - try: - from torch.utils.tensorboard import SummaryWriter - except ImportError: - raise ImportError( - 'Please run "pip install future tensorboard" to install ' - 'the dependencies to use torch.utils.tensorboard ' - '(applicable to PyTorch 1.1 or higher)') - - if self.log_dir is None: - self.log_dir = osp.join(runner.work_dir, 'tf_logs') - self.writer = SummaryWriter(self.log_dir) - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner, allow_text=True) - for tag, val in tags.items(): - if isinstance(val, str): - self.writer.add_text(tag, val, self.get_iter(runner)) - else: - self.writer.add_scalar(tag, val, self.get_iter(runner)) - - @master_only - def after_run(self, runner): - self.writer.close() diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/text.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/text.py deleted file mode 100755 index 043c7bf20..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/text.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import datetime -import os -import os.path as osp -from collections import OrderedDict - -import torch -import torch.distributed as dist - -import mmcv -from mmcv.fileio.file_client import FileClient -from mmcv.utils import is_tuple_of, scandir -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class TextLoggerHook(LoggerHook): - """Logger hook in text. - - In this logger hook, the information will be printed on terminal and - saved in json file. - - Args: - by_epoch (bool, optional): Whether EpochBasedRunner is used. - Default: True. - interval (int, optional): Logging interval (every k iterations). - Default: 10. - ignore_last (bool, optional): Ignore the log of last iterations in each - epoch if less than :attr:`interval`. Default: True. - reset_flag (bool, optional): Whether to clear the output buffer after - logging. Default: False. - interval_exp_name (int, optional): Logging interval for experiment - name. This feature is to help users conveniently get the experiment - information from screen or log file. Default: 1000. - out_dir (str, optional): Logs are saved in ``runner.work_dir`` default. - If ``out_dir`` is specified, logs will be copied to a new directory - which is the concatenation of ``out_dir`` and the last level - directory of ``runner.work_dir``. Default: None. - `New in version 1.3.16.` - out_suffix (str or tuple[str], optional): Those filenames ending with - ``out_suffix`` will be copied to ``out_dir``. - Default: ('.log.json', '.log', '.py'). - `New in version 1.3.16.` - keep_local (bool, optional): Whether to keep local log when - :attr:`out_dir` is specified. If False, the local log will be - removed. Default: True. - `New in version 1.3.16.` - file_client_args (dict, optional): Arguments to instantiate a - FileClient. See :class:`mmcv.fileio.FileClient` for details. - Default: None. - `New in version 1.3.16.` - """ - - def __init__(self, - by_epoch=True, - interval=10, - ignore_last=True, - reset_flag=False, - interval_exp_name=1000, - out_dir=None, - out_suffix=('.log.json', '.log', '.py'), - keep_local=True, - file_client_args=None): - super(TextLoggerHook, self).__init__(interval, ignore_last, reset_flag, - by_epoch) - self.by_epoch = by_epoch - self.time_sec_tot = 0 - self.interval_exp_name = interval_exp_name - - if out_dir is None and file_client_args is not None: - raise ValueError( - 'file_client_args should be "None" when `out_dir` is not' - 'specified.') - self.out_dir = out_dir - - if not (out_dir is None or isinstance(out_dir, str) - or is_tuple_of(out_dir, str)): - raise TypeError('out_dir should be "None" or string or tuple of ' - 'string, but got {out_dir}') - self.out_suffix = out_suffix - - self.keep_local = keep_local - self.file_client_args = file_client_args - if self.out_dir is not None: - self.file_client = FileClient.infer_client(file_client_args, - self.out_dir) - - def before_run(self, runner): - super(TextLoggerHook, self).before_run(runner) - - if self.out_dir is not None: - self.file_client = FileClient.infer_client(self.file_client_args, - self.out_dir) - # The final `self.out_dir` is the concatenation of `self.out_dir` - # and the last level directory of `runner.work_dir` - basename = osp.basename(runner.work_dir.rstrip(osp.sep)) - self.out_dir = self.file_client.join_path(self.out_dir, basename) - runner.logger.info( - (f'Text logs will be saved to {self.out_dir} by ' - f'{self.file_client.name} after the training process.')) - - self.start_iter = runner.iter - self.json_log_path = osp.join(runner.work_dir, - f'{runner.timestamp}.log.json') - if runner.meta is not None: - self._dump_log(runner.meta, runner) - - def _get_max_memory(self, runner): - device = getattr(runner.model, 'output_device', None) - mem = torch.cuda.max_memory_allocated(device=device) - mem_mb = torch.tensor([mem / (1024 * 1024)], - dtype=torch.int, - device=device) - if runner.world_size > 1: - dist.reduce(mem_mb, 0, op=dist.ReduceOp.MAX) - return mem_mb.item() - - def _log_info(self, log_dict, runner): - # print exp name for users to distinguish experiments - # at every ``interval_exp_name`` iterations and the end of each epoch - if runner.meta is not None and 'exp_name' in runner.meta: - if (self.every_n_iters(runner, self.interval_exp_name)) or ( - self.by_epoch and self.end_of_epoch(runner)): - exp_info = f'Exp name: {runner.meta["exp_name"]}' - runner.logger.info(exp_info) - - if log_dict['mode'] == 'train': - if isinstance(log_dict['lr'], dict): - lr_str = [] - for k, val in log_dict['lr'].items(): - lr_str.append(f'lr_{k}: {val:.3e}') - lr_str = ' '.join(lr_str) - else: - lr_str = f'lr: {log_dict["lr"]:.3e}' - - # by epoch: Epoch [4][100/1000] - # by iter: Iter [100/100000] - if self.by_epoch: - log_str = f'Epoch [{log_dict["epoch"]}]' \ - f'[{log_dict["iter"]}/{len(runner.data_loader)}]\t' - else: - log_str = f'Iter [{log_dict["iter"]}/{runner.max_iters}]\t' - log_str += f'{lr_str}, ' - - if 'time' in log_dict.keys(): - self.time_sec_tot += (log_dict['time'] * self.interval) - time_sec_avg = self.time_sec_tot / ( - runner.iter - self.start_iter + 1) - eta_sec = time_sec_avg * (runner.max_iters - runner.iter - 1) - eta_str = str(datetime.timedelta(seconds=int(eta_sec))) - log_str += f'eta: {eta_str}, ' - log_str += f'time: {log_dict["time"]:.3f}, ' \ - f'data_time: {log_dict["data_time"]:.3f}, ' - # statistic memory - if torch.cuda.is_available(): - log_str += f'memory: {log_dict["memory"]}, ' - else: - # val/test time - # here 1000 is the length of the val dataloader - # by epoch: Epoch[val] [4][1000] - # by iter: Iter[val] [1000] - if self.by_epoch: - log_str = f'Epoch({log_dict["mode"]}) ' \ - f'[{log_dict["epoch"]}][{log_dict["iter"]}]\t' - else: - log_str = f'Iter({log_dict["mode"]}) [{log_dict["iter"]}]\t' - - log_items = [] - for name, val in log_dict.items(): - # TODO: resolve this hack - # these items have been in log_str - if name in [ - 'mode', 'Epoch', 'iter', 'lr', 'time', 'data_time', - 'memory', 'epoch' - ]: - continue - if isinstance(val, float): - val = f'{val:.4f}' - log_items.append(f'{name}: {val}') - log_str += ', '.join(log_items) - - runner.logger.info(log_str) - - def _dump_log(self, log_dict, runner): - # dump log in json format - json_log = OrderedDict() - for k, v in log_dict.items(): - json_log[k] = self._round_float(v) - # only append log at last line - if runner.rank == 0: - with open(self.json_log_path, 'a+') as f: - mmcv.dump(json_log, f, file_format='json') - f.write('\n') - - def _round_float(self, items): - if isinstance(items, list): - return [self._round_float(item) for item in items] - elif isinstance(items, float): - return round(items, 5) - else: - return items - - def log(self, runner): - if 'eval_iter_num' in runner.log_buffer.output: - # this doesn't modify runner.iter and is regardless of by_epoch - cur_iter = runner.log_buffer.output.pop('eval_iter_num') - else: - cur_iter = self.get_iter(runner, inner_iter=True) - - log_dict = OrderedDict( - mode=self.get_mode(runner), - epoch=self.get_epoch(runner), - iter=cur_iter) - - # only record lr of the first param group - cur_lr = runner.current_lr() - if isinstance(cur_lr, list): - log_dict['lr'] = cur_lr[0] - else: - assert isinstance(cur_lr, dict) - log_dict['lr'] = {} - for k, lr_ in cur_lr.items(): - assert isinstance(lr_, list) - log_dict['lr'].update({k: lr_[0]}) - - if 'time' in runner.log_buffer.output: - # statistic memory - if torch.cuda.is_available(): - log_dict['memory'] = self._get_max_memory(runner) - - log_dict = dict(log_dict, **runner.log_buffer.output) - - self._log_info(log_dict, runner) - self._dump_log(log_dict, runner) - return log_dict - - def after_run(self, runner): - # copy or upload logs to self.out_dir - if self.out_dir is not None: - for filename in scandir(runner.work_dir, self.out_suffix, True): - local_filepath = osp.join(runner.work_dir, filename) - out_filepath = self.file_client.join_path( - self.out_dir, filename) - with open(local_filepath, 'r') as f: - self.file_client.put_text(f.read(), out_filepath) - - runner.logger.info( - (f'The file {local_filepath} has been uploaded to ' - f'{out_filepath}.')) - - if not self.keep_local: - os.remove(local_filepath) - runner.logger.info( - (f'{local_filepath} was removed due to the ' - '`self.keep_local=False`')) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/wandb.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/wandb.py deleted file mode 100755 index 9f6808462..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/logger/wandb.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ...dist_utils import master_only -from ..hook import HOOKS -from .base import LoggerHook - - -@HOOKS.register_module() -class WandbLoggerHook(LoggerHook): - - def __init__(self, - init_kwargs=None, - interval=10, - ignore_last=True, - reset_flag=False, - commit=True, - by_epoch=True, - with_step=True): - super(WandbLoggerHook, self).__init__(interval, ignore_last, - reset_flag, by_epoch) - self.import_wandb() - self.init_kwargs = init_kwargs - self.commit = commit - self.with_step = with_step - - def import_wandb(self): - try: - import wandb - except ImportError: - raise ImportError( - 'Please run "pip install wandb" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(WandbLoggerHook, self).before_run(runner) - if self.wandb is None: - self.import_wandb() - if self.init_kwargs: - self.wandb.init(**self.init_kwargs) - else: - self.wandb.init() - - @master_only - def log(self, runner): - tags = self.get_loggable_tags(runner) - if tags: - if self.with_step: - self.wandb.log( - tags, step=self.get_iter(runner), commit=self.commit) - else: - tags['global_step'] = self.get_iter(runner) - self.wandb.log(tags, commit=self.commit) - - @master_only - def after_run(self, runner): - self.wandb.join() diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/lr_updater.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/lr_updater.py deleted file mode 100755 index e5a124157..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/lr_updater.py +++ /dev/null @@ -1,670 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -from math import cos, pi - -import mmcv -from .hook import HOOKS, Hook - - -class LrUpdaterHook(Hook): - """LR Scheduler in MMCV. - - Args: - by_epoch (bool): LR changes epoch by epoch - warmup (string): Type of warmup used. It can be None(use no warmup), - 'constant', 'linear' or 'exp' - warmup_iters (int): The number of iterations or epochs that warmup - lasts - warmup_ratio (float): LR used at the beginning of warmup equals to - warmup_ratio * initial_lr - warmup_by_epoch (bool): When warmup_by_epoch == True, warmup_iters - means the number of epochs that warmup lasts, otherwise means the - number of iteration that warmup lasts - """ - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.1, - warmup_by_epoch=False): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_ratio" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - self.warmup_by_epoch = warmup_by_epoch - - if self.warmup_by_epoch: - self.warmup_epochs = self.warmup_iters - self.warmup_iters = None - else: - self.warmup_epochs = None - - self.base_lr = [] # initial lr for all param groups - self.regular_lr = [] # expected lr if no warming up is performed - - def _set_lr(self, runner, lr_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, lr in zip(optim.param_groups, lr_groups[k]): - param_group['lr'] = lr - else: - for param_group, lr in zip(runner.optimizer.param_groups, - lr_groups): - param_group['lr'] = lr - - def get_lr(self, runner, base_lr): - raise NotImplementedError - - def get_regular_lr(self, runner): - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(runner, _base_lr) - for _base_lr in self.base_lr[k] - ] - lr_groups.update({k: _lr_group}) - - return lr_groups - else: - return [self.get_lr(runner, _base_lr) for _base_lr in self.base_lr] - - def get_warmup_lr(self, cur_iters): - - def _get_warmup_lr(cur_iters, regular_lr): - if self.warmup == 'constant': - warmup_lr = [_lr * self.warmup_ratio for _lr in regular_lr] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_lr = [_lr * (1 - k) for _lr in regular_lr] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_lr = [_lr * k for _lr in regular_lr] - return warmup_lr - - if isinstance(self.regular_lr, dict): - lr_groups = {} - for key, regular_lr in self.regular_lr.items(): - lr_groups[key] = _get_warmup_lr(cur_iters, regular_lr) - return lr_groups - else: - return _get_warmup_lr(cur_iters, self.regular_lr) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, if 'initial_lr' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - group.setdefault('initial_lr', group['lr']) - _base_lr = [ - group['initial_lr'] for group in optim.param_groups - ] - self.base_lr.update({k: _base_lr}) - else: - for group in runner.optimizer.param_groups: - group.setdefault('initial_lr', group['lr']) - self.base_lr = [ - group['initial_lr'] for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if self.warmup_iters is None: - epoch_len = len(runner.data_loader) - self.warmup_iters = self.warmup_epochs * epoch_len - - if not self.by_epoch: - return - - self.regular_lr = self.get_regular_lr(runner) - self._set_lr(runner, self.regular_lr) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_lr = self.get_regular_lr(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_lr(runner, self.regular_lr) - else: - warmup_lr = self.get_warmup_lr(cur_iter) - self._set_lr(runner, warmup_lr) - - -@HOOKS.register_module() -class FixedLrUpdaterHook(LrUpdaterHook): - - def __init__(self, **kwargs): - super(FixedLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - return base_lr - - -@HOOKS.register_module() -class StepLrUpdaterHook(LrUpdaterHook): - """Step LR scheduler with min_lr clipping. - - Args: - step (int | list[int]): Step to decay the LR. If an int value is given, - regard it as the decay interval. If a list is given, decay LR at - these steps. - gamma (float, optional): Decay LR ratio. Default: 0.1. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. If None - is given, we don't perform lr clipping. Default: None. - """ - - def __init__(self, step, gamma=0.1, min_lr=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_lr = min_lr - super(StepLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - lr = base_lr * (self.gamma**exp) - if self.min_lr is not None: - # clip to a minimum value - lr = max(lr, self.min_lr) - return lr - - -@HOOKS.register_module() -class ExpLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, **kwargs): - self.gamma = gamma - super(ExpLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * self.gamma**progress - - -@HOOKS.register_module() -class PolyLrUpdaterHook(LrUpdaterHook): - - def __init__(self, power=1., min_lr=0., **kwargs): - self.power = power - self.min_lr = min_lr - super(PolyLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - coeff = (1 - progress / max_progress)**self.power - return (base_lr - self.min_lr) * coeff + self.min_lr - - -@HOOKS.register_module() -class InvLrUpdaterHook(LrUpdaterHook): - - def __init__(self, gamma, power=1., **kwargs): - self.gamma = gamma - self.power = power - super(InvLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - progress = runner.epoch if self.by_epoch else runner.iter - return base_lr * (1 + self.gamma * progress)**(-self.power) - - -@HOOKS.register_module() -class CosineAnnealingLrUpdaterHook(LrUpdaterHook): - - def __init__(self, min_lr=None, min_lr_ratio=None, **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(CosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class FlatCosineAnnealingLrUpdaterHook(LrUpdaterHook): - """Flat + Cosine lr schedule. - - Modified from https://github.com/fastai/fastai/blob/master/fastai/callback/schedule.py#L128 # noqa: E501 - - Args: - start_percent (float): When to start annealing the learning rate - after the percentage of the total training steps. - The value should be in range [0, 1). - Default: 0.75 - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - start_percent=0.75, - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - if start_percent < 0 or start_percent > 1 or not isinstance( - start_percent, float): - raise ValueError( - 'expected float between 0 and 1 start_percent, but ' - f'got {start_percent}') - self.start_percent = start_percent - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - super(FlatCosineAnnealingLrUpdaterHook, self).__init__(**kwargs) - - def get_lr(self, runner, base_lr): - if self.by_epoch: - start = round(runner.max_epochs * self.start_percent) - progress = runner.epoch - start - max_progress = runner.max_epochs - start - else: - start = round(runner.max_iters * self.start_percent) - progress = runner.iter - start - max_progress = runner.max_iters - start - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - if progress < 0: - return base_lr - else: - return annealing_cos(base_lr, target_lr, progress / max_progress) - - -@HOOKS.register_module() -class CosineRestartLrUpdaterHook(LrUpdaterHook): - """Cosine annealing with restarts learning rate scheme. - - Args: - periods (list[int]): Periods for each cosine anneling cycle. - restart_weights (list[float], optional): Restart weights at each - restart iteration. Default: [1]. - min_lr (float, optional): The minimum lr. Default: None. - min_lr_ratio (float, optional): The ratio of minimum lr to the base lr. - Either `min_lr` or `min_lr_ratio` should be specified. - Default: None. - """ - - def __init__(self, - periods, - restart_weights=[1], - min_lr=None, - min_lr_ratio=None, - **kwargs): - assert (min_lr is None) ^ (min_lr_ratio is None) - self.periods = periods - self.min_lr = min_lr - self.min_lr_ratio = min_lr_ratio - self.restart_weights = restart_weights - assert (len(self.periods) == len(self.restart_weights) - ), 'periods and restart_weights should have the same length.' - super(CosineRestartLrUpdaterHook, self).__init__(**kwargs) - - self.cumulative_periods = [ - sum(self.periods[0:i + 1]) for i in range(0, len(self.periods)) - ] - - def get_lr(self, runner, base_lr): - if self.by_epoch: - progress = runner.epoch - else: - progress = runner.iter - - if self.min_lr_ratio is not None: - target_lr = base_lr * self.min_lr_ratio - else: - target_lr = self.min_lr - - idx = get_position_from_periods(progress, self.cumulative_periods) - current_weight = self.restart_weights[idx] - nearest_restart = 0 if idx == 0 else self.cumulative_periods[idx - 1] - current_periods = self.periods[idx] - - alpha = min((progress - nearest_restart) / current_periods, 1) - return annealing_cos(base_lr, target_lr, alpha, current_weight) - - -def get_position_from_periods(iteration, cumulative_periods): - """Get the position from a period list. - - It will return the index of the right-closest number in the period list. - For example, the cumulative_periods = [100, 200, 300, 400], - if iteration == 50, return 0; - if iteration == 210, return 2; - if iteration == 300, return 3. - - Args: - iteration (int): Current iteration. - cumulative_periods (list[int]): Cumulative period list. - - Returns: - int: The position of the right-closest number in the period list. - """ - for i, period in enumerate(cumulative_periods): - if iteration < period: - return i - raise ValueError(f'Current iteration {iteration} exceeds ' - f'cumulative_periods {cumulative_periods}') - - -@HOOKS.register_module() -class CyclicLrUpdaterHook(LrUpdaterHook): - """Cyclic LR Scheduler. - - Implement the cyclical learning rate policy (CLR) described in - https://arxiv.org/pdf/1506.01186.pdf - - Different from the original paper, we use cosine annealing rather than - triangular policy inside a cycle. This improves the performance in the - 3D detection area. - - Args: - by_epoch (bool): Whether to update LR by epoch. - target_ratio (tuple[float]): Relative ratio of the highest LR and the - lowest LR to the initial LR. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of LR in - the total cycle. - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. Default: 'cos'. - """ - - def __init__(self, - by_epoch=False, - target_ratio=(10, 1e-4), - cyclic_times=1, - step_ratio_up=0.4, - anneal_strategy='cos', - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.lr_phases = [] # init lr_phases - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicLrUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicLrUpdaterHook, self).before_run(runner) - # initiate lr_phases - # total lr_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.lr_phases.append( - [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) - self.lr_phases.append([ - iter_up_phase, max_iter_per_phase, max_iter_per_phase, - self.target_ratio[0], self.target_ratio[1] - ]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - for (start_iter, end_iter, max_iter_per_phase, start_ratio, - end_ratio) in self.lr_phases: - curr_iter %= max_iter_per_phase - if start_iter <= curr_iter < end_iter: - progress = curr_iter - start_iter - return self.anneal_func(base_lr * start_ratio, - base_lr * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleLrUpdaterHook(LrUpdaterHook): - """One Cycle LR Scheduler. - - The 1cycle learning rate policy changes the learning rate after every - batch. The one cycle learning rate policy is described in - https://arxiv.org/pdf/1708.07120.pdf - - Args: - max_lr (float or list): Upper learning rate boundaries in the cycle - for each parameter group. - total_steps (int, optional): The total number of steps in the cycle. - Note that if a value is not provided here, it will be the max_iter - of runner. Default: None. - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - div_factor (float): Determines the initial learning rate via - initial_lr = max_lr/div_factor - Default: 25 - final_div_factor (float): Determines the minimum learning rate via - min_lr = initial_lr/final_div_factor - Default: 1e4 - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - max_lr, - total_steps=None, - pct_start=0.3, - anneal_strategy='cos', - div_factor=25, - final_div_factor=1e4, - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch = False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(max_lr, (numbers.Number, list, dict)): - raise ValueError('the type of max_lr must be the one of list or ' - f'dict, but got {type(max_lr)}') - self._max_lr = max_lr - if total_steps is not None: - if not isinstance(total_steps, int): - raise ValueError('the type of total_steps must be int, but' - f'got {type(total_steps)}') - self.total_steps = total_steps - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must be one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.div_factor = div_factor - self.final_div_factor = final_div_factor - self.three_phase = three_phase - self.lr_phases = [] # init lr_phases - super(OneCycleLrUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if hasattr(self, 'total_steps'): - total_steps = self.total_steps - else: - total_steps = runner.max_iters - if total_steps < runner.max_iters: - raise ValueError( - 'The total steps must be greater than or equal to max ' - f'iterations {runner.max_iters} of runner, but total steps ' - f'is {total_steps}.') - - if isinstance(runner.optimizer, dict): - self.base_lr = {} - for k, optim in runner.optimizer.items(): - _max_lr = format_param(k, optim, self._max_lr) - self.base_lr[k] = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(optim.param_groups, self.base_lr[k]): - group.setdefault('initial_lr', lr) - else: - k = type(runner.optimizer).__name__ - _max_lr = format_param(k, runner.optimizer, self._max_lr) - self.base_lr = [lr / self.div_factor for lr in _max_lr] - for group, lr in zip(runner.optimizer.param_groups, self.base_lr): - group.setdefault('initial_lr', lr) - - if self.three_phase: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append([ - float(2 * self.pct_start * total_steps) - 2, self.div_factor, 1 - ]) - self.lr_phases.append( - [total_steps - 1, 1, 1 / self.final_div_factor]) - else: - self.lr_phases.append( - [float(self.pct_start * total_steps) - 1, 1, self.div_factor]) - self.lr_phases.append( - [total_steps - 1, self.div_factor, 1 / self.final_div_factor]) - - def get_lr(self, runner, base_lr): - curr_iter = runner.iter - start_iter = 0 - for i, (end_iter, start_lr, end_lr) in enumerate(self.lr_phases): - if curr_iter <= end_iter: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - lr = self.anneal_func(base_lr * start_lr, base_lr * end_lr, - pct) - break - start_iter = end_iter - return lr - - -def annealing_cos(start, end, factor, weight=1): - """Calculate annealing cos learning rate. - - Cosine anneal from `weight * start + (1 - weight) * end` to `end` as - percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the cosine annealing. - end (float): The ending learing rate of the cosine annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - weight (float, optional): The combination factor of `start` and `end` - when calculating the actual starting learning rate. Default to 1. - """ - cos_out = cos(pi * factor) + 1 - return end + 0.5 * weight * (start - end) * cos_out - - -def annealing_linear(start, end, factor): - """Calculate annealing linear learning rate. - - Linear anneal from `start` to `end` as percentage goes from 0.0 to 1.0. - - Args: - start (float): The starting learning rate of the linear annealing. - end (float): The ending learing rate of the linear annealing. - factor (float): The coefficient of `pi` when calculating the current - percentage. Range from 0.0 to 1.0. - """ - return start + (end - start) * factor - - -def format_param(name, optim, param): - if isinstance(param, numbers.Number): - return [param] * len(optim.param_groups) - elif isinstance(param, (list, tuple)): # multi param groups - if len(param) != len(optim.param_groups): - raise ValueError(f'expected {len(optim.param_groups)} ' - f'values for {name}, got {len(param)}') - return param - else: # multi optimizers - if name not in param: - raise KeyError(f'{name} is not found in {param.keys()}') - return param[name] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/memory.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/memory.py deleted file mode 100755 index 70cf9a838..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class EmptyCacheHook(Hook): - - def __init__(self, before_epoch=False, after_epoch=True, after_iter=False): - self._before_epoch = before_epoch - self._after_epoch = after_epoch - self._after_iter = after_iter - - def after_iter(self, runner): - if self._after_iter: - torch.cuda.empty_cache() - - def before_epoch(self, runner): - if self._before_epoch: - torch.cuda.empty_cache() - - def after_epoch(self, runner): - if self._after_epoch: - torch.cuda.empty_cache() diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/momentum_updater.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/momentum_updater.py deleted file mode 100755 index 13d0e2fab..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/momentum_updater.py +++ /dev/null @@ -1,493 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -from .hook import HOOKS, Hook -from .lr_updater import annealing_cos, annealing_linear, format_param - - -class MomentumUpdaterHook(Hook): - - def __init__(self, - by_epoch=True, - warmup=None, - warmup_iters=0, - warmup_ratio=0.9): - # validate the "warmup" argument - if warmup is not None: - if warmup not in ['constant', 'linear', 'exp']: - raise ValueError( - f'"{warmup}" is not a supported type for warming up, valid' - ' types are "constant" and "linear"') - if warmup is not None: - assert warmup_iters > 0, \ - '"warmup_iters" must be a positive integer' - assert 0 < warmup_ratio <= 1.0, \ - '"warmup_momentum" must be in range (0,1]' - - self.by_epoch = by_epoch - self.warmup = warmup - self.warmup_iters = warmup_iters - self.warmup_ratio = warmup_ratio - - self.base_momentum = [] # initial momentum for all param groups - self.regular_momentum = [ - ] # expected momentum if no warming up is performed - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, base_momentum): - raise NotImplementedError - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k in runner.optimizer.keys(): - _momentum_group = [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum[k] - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - return [ - self.get_momentum(runner, _base_momentum) - for _base_momentum in self.base_momentum - ] - - def get_warmup_momentum(self, cur_iters): - - def _get_warmup_momentum(cur_iters, regular_momentum): - if self.warmup == 'constant': - warmup_momentum = [ - _momentum / self.warmup_ratio - for _momentum in self.regular_momentum - ] - elif self.warmup == 'linear': - k = (1 - cur_iters / self.warmup_iters) * (1 - - self.warmup_ratio) - warmup_momentum = [ - _momentum / (1 - k) for _momentum in self.regular_mom - ] - elif self.warmup == 'exp': - k = self.warmup_ratio**(1 - cur_iters / self.warmup_iters) - warmup_momentum = [ - _momentum / k for _momentum in self.regular_mom - ] - return warmup_momentum - - if isinstance(self.regular_momentum, dict): - momentum_groups = {} - for key, regular_momentum in self.regular_momentum.items(): - momentum_groups[key] = _get_warmup_momentum( - cur_iters, regular_momentum) - return momentum_groups - else: - return _get_warmup_momentum(cur_iters, self.regular_momentum) - - def before_run(self, runner): - # NOTE: when resuming from a checkpoint, - # if 'initial_momentum' is not saved, - # it will be set according to the optimizer params - if isinstance(runner.optimizer, dict): - self.base_momentum = {} - for k, optim in runner.optimizer.items(): - for group in optim.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - _base_momentum = [ - group['initial_momentum'] for group in optim.param_groups - ] - self.base_momentum.update({k: _base_momentum}) - else: - for group in runner.optimizer.param_groups: - if 'momentum' in group.keys(): - group.setdefault('initial_momentum', group['momentum']) - else: - group.setdefault('initial_momentum', group['betas'][0]) - self.base_momentum = [ - group['initial_momentum'] - for group in runner.optimizer.param_groups - ] - - def before_train_epoch(self, runner): - if not self.by_epoch: - return - self.regular_mom = self.get_regular_momentum(runner) - self._set_momentum(runner, self.regular_mom) - - def before_train_iter(self, runner): - cur_iter = runner.iter - if not self.by_epoch: - self.regular_mom = self.get_regular_momentum(runner) - if self.warmup is None or cur_iter >= self.warmup_iters: - self._set_momentum(runner, self.regular_mom) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - elif self.by_epoch: - if self.warmup is None or cur_iter > self.warmup_iters: - return - elif cur_iter == self.warmup_iters: - self._set_momentum(runner, self.regular_mom) - else: - warmup_momentum = self.get_warmup_momentum(cur_iter) - self._set_momentum(runner, warmup_momentum) - - -@HOOKS.register_module() -class StepMomentumUpdaterHook(MomentumUpdaterHook): - """Step momentum scheduler with min value clipping. - - Args: - step (int | list[int]): Step to decay the momentum. If an int value is - given, regard it as the decay interval. If a list is given, decay - momentum at these steps. - gamma (float, optional): Decay momentum ratio. Default: 0.5. - min_momentum (float, optional): Minimum momentum value to keep. If - momentum after decay is lower than this value, it will be clipped - accordingly. If None is given, we don't perform lr clipping. - Default: None. - """ - - def __init__(self, step, gamma=0.5, min_momentum=None, **kwargs): - if isinstance(step, list): - assert mmcv.is_list_of(step, int) - assert all([s > 0 for s in step]) - elif isinstance(step, int): - assert step > 0 - else: - raise TypeError('"step" must be a list or integer') - self.step = step - self.gamma = gamma - self.min_momentum = min_momentum - super(StepMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - progress = runner.epoch if self.by_epoch else runner.iter - - # calculate exponential term - if isinstance(self.step, int): - exp = progress // self.step - else: - exp = len(self.step) - for i, s in enumerate(self.step): - if progress < s: - exp = i - break - - momentum = base_momentum * (self.gamma**exp) - if self.min_momentum is not None: - # clip to a minimum value - momentum = max(momentum, self.min_momentum) - return momentum - - -@HOOKS.register_module() -class CosineAnnealingMomentumUpdaterHook(MomentumUpdaterHook): - - def __init__(self, min_momentum=None, min_momentum_ratio=None, **kwargs): - assert (min_momentum is None) ^ (min_momentum_ratio is None) - self.min_momentum = min_momentum - self.min_momentum_ratio = min_momentum_ratio - super(CosineAnnealingMomentumUpdaterHook, self).__init__(**kwargs) - - def get_momentum(self, runner, base_momentum): - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - if self.min_momentum_ratio is not None: - target_momentum = base_momentum * self.min_momentum_ratio - else: - target_momentum = self.min_momentum - return annealing_cos(base_momentum, target_momentum, - progress / max_progress) - - -@HOOKS.register_module() -class CyclicMomentumUpdaterHook(MomentumUpdaterHook): - """Cyclic momentum Scheduler. - - Implement the cyclical momentum scheduler policy described in - https://arxiv.org/pdf/1708.07120.pdf - - This momentum scheduler usually used together with the CyclicLRUpdater - to improve the performance in the 3D detection area. - - Attributes: - target_ratio (tuple[float]): Relative ratio of the lowest momentum and - the highest momentum to the initial momentum. - cyclic_times (int): Number of cycles during training - step_ratio_up (float): The ratio of the increasing process of momentum - in the total cycle. - by_epoch (bool): Whether to update momentum by epoch. - """ - - def __init__(self, - by_epoch=False, - target_ratio=(0.85 / 0.95, 1), - cyclic_times=1, - step_ratio_up=0.4, - **kwargs): - if isinstance(target_ratio, float): - target_ratio = (target_ratio, target_ratio / 1e5) - elif isinstance(target_ratio, tuple): - target_ratio = (target_ratio[0], target_ratio[0] / 1e5) \ - if len(target_ratio) == 1 else target_ratio - else: - raise ValueError('target_ratio should be either float ' - f'or tuple, got {type(target_ratio)}') - - assert len(target_ratio) == 2, \ - '"target_ratio" must be list or tuple of two floats' - assert 0 <= step_ratio_up < 1.0, \ - '"step_ratio_up" must be in range [0,1)' - - self.target_ratio = target_ratio - self.cyclic_times = cyclic_times - self.step_ratio_up = step_ratio_up - self.momentum_phases = [] # init momentum_phases - # currently only support by_epoch=False - assert not by_epoch, \ - 'currently only support "by_epoch" = False' - super(CyclicMomentumUpdaterHook, self).__init__(by_epoch, **kwargs) - - def before_run(self, runner): - super(CyclicMomentumUpdaterHook, self).before_run(runner) - # initiate momentum_phases - # total momentum_phases are separated as up and down - max_iter_per_phase = runner.max_iters // self.cyclic_times - iter_up_phase = int(self.step_ratio_up * max_iter_per_phase) - self.momentum_phases.append( - [0, iter_up_phase, max_iter_per_phase, 1, self.target_ratio[0]]) - self.momentum_phases.append([ - iter_up_phase, max_iter_per_phase, max_iter_per_phase, - self.target_ratio[0], self.target_ratio[1] - ]) - - def get_momentum(self, runner, base_momentum): - curr_iter = runner.iter - for (start_iter, end_iter, max_iter_per_phase, start_ratio, - end_ratio) in self.momentum_phases: - curr_iter %= max_iter_per_phase - if start_iter <= curr_iter < end_iter: - progress = curr_iter - start_iter - return annealing_cos(base_momentum * start_ratio, - base_momentum * end_ratio, - progress / (end_iter - start_iter)) - - -@HOOKS.register_module() -class OneCycleMomentumUpdaterHook(MomentumUpdaterHook): - """OneCycle momentum Scheduler. - - This momentum scheduler usually used together with the OneCycleLrUpdater - to improve the performance. - - Args: - base_momentum (float or list): Lower momentum boundaries in the cycle - for each parameter group. Note that momentum is cycled inversely - to learning rate; at the peak of a cycle, momentum is - 'base_momentum' and learning rate is 'max_lr'. - Default: 0.85 - max_momentum (float or list): Upper momentum boundaries in the cycle - for each parameter group. Functionally, - it defines the cycle amplitude (max_momentum - base_momentum). - Note that momentum is cycled inversely - to learning rate; at the start of a cycle, momentum is - 'max_momentum' and learning rate is 'base_lr' - Default: 0.95 - pct_start (float): The percentage of the cycle (in number of steps) - spent increasing the learning rate. - Default: 0.3 - anneal_strategy (str): {'cos', 'linear'} - Specifies the annealing strategy: 'cos' for cosine annealing, - 'linear' for linear annealing. - Default: 'cos' - three_phase (bool): If three_phase is True, use a third phase of the - schedule to annihilate the learning rate according to - final_div_factor instead of modifying the second phase (the first - two phases will be symmetrical about the step indicated by - pct_start). - Default: False - """ - - def __init__(self, - base_momentum=0.85, - max_momentum=0.95, - pct_start=0.3, - anneal_strategy='cos', - three_phase=False, - **kwargs): - # validate by_epoch, currently only support by_epoch=False - if 'by_epoch' not in kwargs: - kwargs['by_epoch'] = False - else: - assert not kwargs['by_epoch'], \ - 'currently only support "by_epoch" = False' - if not isinstance(base_momentum, (float, list, dict)): - raise ValueError('base_momentum must be the type among of float,' - 'list or dict.') - self._base_momentum = base_momentum - if not isinstance(max_momentum, (float, list, dict)): - raise ValueError('max_momentum must be the type among of float,' - 'list or dict.') - self._max_momentum = max_momentum - # validate pct_start - if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): - raise ValueError('Expected float between 0 and 1 pct_start, but ' - f'got {pct_start}') - self.pct_start = pct_start - # validate anneal_strategy - if anneal_strategy not in ['cos', 'linear']: - raise ValueError('anneal_strategy must by one of "cos" or ' - f'"linear", instead got {anneal_strategy}') - elif anneal_strategy == 'cos': - self.anneal_func = annealing_cos - elif anneal_strategy == 'linear': - self.anneal_func = annealing_linear - self.three_phase = three_phase - self.momentum_phases = [] # init momentum_phases - super(OneCycleMomentumUpdaterHook, self).__init__(**kwargs) - - def before_run(self, runner): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip( - optim.param_groups, _base_momentum, _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - else: - optim = runner.optimizer - if ('momentum' not in optim.defaults - and 'betas' not in optim.defaults): - raise ValueError('optimizer must support momentum with' - 'option enabled') - self.use_beta1 = 'betas' in optim.defaults - k = type(optim).__name__ - _base_momentum = format_param(k, optim, self._base_momentum) - _max_momentum = format_param(k, optim, self._max_momentum) - for group, b_momentum, m_momentum in zip(optim.param_groups, - _base_momentum, - _max_momentum): - if self.use_beta1: - _, beta2 = group['betas'] - group['betas'] = (m_momentum, beta2) - else: - group['momentum'] = m_momentum - group['base_momentum'] = b_momentum - group['max_momentum'] = m_momentum - - if self.three_phase: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': - float(2 * self.pct_start * runner.max_iters) - 2, - 'start_momentum': - 'base_momentum', - 'end_momentum': - 'max_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'max_momentum', - 'end_momentum': 'max_momentum' - }) - else: - self.momentum_phases.append({ - 'end_iter': - float(self.pct_start * runner.max_iters) - 1, - 'start_momentum': - 'max_momentum', - 'end_momentum': - 'base_momentum' - }) - self.momentum_phases.append({ - 'end_iter': runner.max_iters - 1, - 'start_momentum': 'base_momentum', - 'end_momentum': 'max_momentum' - }) - - def _set_momentum(self, runner, momentum_groups): - if isinstance(runner.optimizer, dict): - for k, optim in runner.optimizer.items(): - for param_group, mom in zip(optim.param_groups, - momentum_groups[k]): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - else: - for param_group, mom in zip(runner.optimizer.param_groups, - momentum_groups): - if 'momentum' in param_group.keys(): - param_group['momentum'] = mom - elif 'betas' in param_group.keys(): - param_group['betas'] = (mom, param_group['betas'][1]) - - def get_momentum(self, runner, param_group): - curr_iter = runner.iter - start_iter = 0 - for i, phase in enumerate(self.momentum_phases): - end_iter = phase['end_iter'] - if curr_iter <= end_iter or i == len(self.momentum_phases) - 1: - pct = (curr_iter - start_iter) / (end_iter - start_iter) - momentum = self.anneal_func( - param_group[phase['start_momentum']], - param_group[phase['end_momentum']], pct) - break - start_iter = end_iter - return momentum - - def get_regular_momentum(self, runner): - if isinstance(runner.optimizer, dict): - momentum_groups = {} - for k, optim in runner.optimizer.items(): - _momentum_group = [ - self.get_momentum(runner, param_group) - for param_group in optim.param_groups - ] - momentum_groups.update({k: _momentum_group}) - return momentum_groups - else: - momentum_groups = [] - for param_group in runner.optimizer.param_groups: - momentum_groups.append(self.get_momentum(runner, param_group)) - return momentum_groups diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/optimizer.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/optimizer.py deleted file mode 100755 index f575ceda0..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/optimizer.py +++ /dev/null @@ -1,508 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -from collections import defaultdict -from itertools import chain - -from torch.nn.utils import clip_grad - -from mmcv.utils import TORCH_VERSION, _BatchNorm, digit_version -from ..dist_utils import allreduce_grads -from ..fp16_utils import LossScaler, wrap_fp16_model -from .hook import HOOKS, Hook - -try: - # If PyTorch version >= 1.6.0, torch.cuda.amp.GradScaler would be imported - # and used; otherwise, auto fp16 will adopt mmcv's implementation. - from torch.cuda.amp import GradScaler -except ImportError: - pass - - -@HOOKS.register_module() -class OptimizerHook(Hook): - - def __init__(self, grad_clip=None): - self.grad_clip = grad_clip - - def clip_grads(self, params): - params = list( - filter(lambda p: p.requires_grad and p.grad is not None, params)) - if len(params) > 0: - return clip_grad.clip_grad_norm_(params, **self.grad_clip) - - def after_train_iter(self, runner): - runner.optimizer.zero_grad() - runner.outputs['loss'].backward() - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - - -@HOOKS.register_module() -class GradientCumulativeOptimizerHook(OptimizerHook): - """Optimizer Hook implements multi-iters gradient cumulating. - - Args: - cumulative_iters (int, optional): Num of gradient cumulative iters. - The optimizer will step every `cumulative_iters` iters. - Defaults to 1. - - Examples: - >>> # Use cumulative_iters to simulate a large batch size - >>> # It is helpful when the hardware cannot handle a large batch size. - >>> loader = DataLoader(data, batch_size=64) - >>> optim_hook = GradientCumulativeOptimizerHook(cumulative_iters=4) - >>> # almost equals to - >>> loader = DataLoader(data, batch_size=256) - >>> optim_hook = OptimizerHook() - """ - - def __init__(self, cumulative_iters=1, **kwargs): - super(GradientCumulativeOptimizerHook, self).__init__(**kwargs) - - assert isinstance(cumulative_iters, int) and cumulative_iters > 0, \ - f'cumulative_iters only accepts positive int, but got ' \ - f'{type(cumulative_iters)} instead.' - - self.cumulative_iters = cumulative_iters - self.divisible_iters = 0 - self.remainder_iters = 0 - self.initialized = False - - def has_batch_norm(self, module): - if isinstance(module, _BatchNorm): - return True - for m in module.children(): - if self.has_batch_norm(m): - return True - return False - - def _init(self, runner): - if runner.iter % self.cumulative_iters != 0: - runner.logger.warning( - 'Resume iter number is not divisible by cumulative_iters in ' - 'GradientCumulativeOptimizerHook, which means the gradient of ' - 'some iters is lost and the result may be influenced slightly.' - ) - - if self.has_batch_norm(runner.model) and self.cumulative_iters > 1: - runner.logger.warning( - 'GradientCumulativeOptimizerHook may slightly decrease ' - 'performance if the model has BatchNorm layers.') - - residual_iters = runner.max_iters - runner.iter - - self.divisible_iters = ( - residual_iters // self.cumulative_iters * self.cumulative_iters) - self.remainder_iters = residual_iters - self.divisible_iters - - self.initialized = True - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - runner.optimizer.step() - runner.optimizer.zero_grad() - - -if (TORCH_VERSION != 'parrots' - and digit_version(TORCH_VERSION) >= digit_version('1.6.0')): - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (using PyTorch's implementation). - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of GradScalar. - Defaults to 512. For Pytorch >= 1.6, mmcv uses official - implementation of GradScaler. If you use a dict version of - loss_scale to create GradScaler, please refer to: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler - for the parameters. - - Examples: - >>> loss_scale = dict( - ... init_scale=65536.0, - ... growth_factor=2.0, - ... backoff_factor=0.5, - ... growth_interval=2000 - ... ) - >>> optimizer_hook = Fp16OptimizerHook(loss_scale=loss_scale) - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - self._scale_update_param = None - if loss_scale == 'dynamic': - self.loss_scaler = GradScaler() - elif isinstance(loss_scale, float): - self._scale_update_param = loss_scale - self.loss_scaler = GradScaler(init_scale=loss_scale) - elif isinstance(loss_scale, dict): - self.loss_scaler = GradScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training.""" - # wrap model mode to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer to - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.GradScaler. - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients. - 3. Unscale the optimizer’s gradient tensors. - 4. Call optimizer.step() and update scale factor. - 5. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - - self.loss_scaler.scale(runner.outputs['loss']).backward() - self.loss_scaler.unscale_(runner.optimizer) - # grad clip - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update({'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using PyTorch's implementation) implements - multi-iters gradient cumulating. - - If you are using PyTorch >= 1.6, torch.cuda.amp is used as the backend, - to take care of the optimization procedure. - """ - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - loss = runner.outputs['loss'] - loss = loss / loss_factor - - self.loss_scaler.scale(loss).backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - self.loss_scaler.unscale_(runner.optimizer) - - if self.grad_clip is not None: - grad_norm = self.clip_grads(runner.model.parameters()) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - - # backward and update scaler - self.loss_scaler.step(runner.optimizer) - self.loss_scaler.update(self._scale_update_param) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() - -else: - - @HOOKS.register_module() - class Fp16OptimizerHook(OptimizerHook): - """FP16 optimizer hook (mmcv's implementation). - - The steps of fp16 optimizer is as follows. - 1. Scale the loss value. - 2. BP in the fp16 model. - 2. Copy gradients from fp16 model to fp32 weights. - 3. Update fp32 weights. - 4. Copy updated parameters from fp32 weights to fp16 model. - - Refer to https://arxiv.org/abs/1710.03740 for more details. - - Args: - loss_scale (float | str | dict): Scale factor configuration. - If loss_scale is a float, static loss scaling will be used with - the specified scale. If loss_scale is a string, it must be - 'dynamic', then dynamic loss scaling will be used. - It can also be a dict containing arguments of LossScaler. - Defaults to 512. - """ - - def __init__(self, - grad_clip=None, - coalesce=True, - bucket_size_mb=-1, - loss_scale=512., - distributed=True): - self.grad_clip = grad_clip - self.coalesce = coalesce - self.bucket_size_mb = bucket_size_mb - self.distributed = distributed - if loss_scale == 'dynamic': - self.loss_scaler = LossScaler(mode='dynamic') - elif isinstance(loss_scale, float): - self.loss_scaler = LossScaler( - init_scale=loss_scale, mode='static') - elif isinstance(loss_scale, dict): - self.loss_scaler = LossScaler(**loss_scale) - else: - raise ValueError('loss_scale must be of type float, dict, or ' - f'"dynamic", got {loss_scale}') - - def before_run(self, runner): - """Preparing steps before Mixed Precision Training. - - 1. Make a master copy of fp32 weights for optimization. - 2. Convert the main model from fp32 to fp16. - """ - # keep a copy of fp32 weights - old_groups = runner.optimizer.param_groups - runner.optimizer.param_groups = copy.deepcopy( - runner.optimizer.param_groups) - state = defaultdict(dict) - p_map = { - old_p: p - for old_p, p in zip( - chain(*(g['params'] for g in old_groups)), - chain(*(g['params'] - for g in runner.optimizer.param_groups))) - } - for k, v in runner.optimizer.state.items(): - state[p_map[k]] = v - runner.optimizer.state = state - # convert model to fp16 - wrap_fp16_model(runner.model) - # resume from state dict - if 'fp16' in runner.meta and 'loss_scaler' in runner.meta['fp16']: - scaler_state_dict = runner.meta['fp16']['loss_scaler'] - self.loss_scaler.load_state_dict(scaler_state_dict) - - def copy_grads_to_fp32(self, fp16_net, fp32_weights): - """Copy gradients from fp16 model to fp32 weight copy.""" - for fp32_param, fp16_param in zip(fp32_weights, - fp16_net.parameters()): - if fp16_param.grad is not None: - if fp32_param.grad is None: - fp32_param.grad = fp32_param.data.new( - fp32_param.size()) - fp32_param.grad.copy_(fp16_param.grad) - - def copy_params_to_fp16(self, fp16_net, fp32_weights): - """Copy updated params from fp32 weight copy to fp16 model.""" - for fp16_param, fp32_param in zip(fp16_net.parameters(), - fp32_weights): - fp16_param.data.copy_(fp32_param.data) - - def after_train_iter(self, runner): - """Backward optimization steps for Mixed Precision Training. For - dynamic loss scaling, please refer `loss_scalar.py` - - 1. Scale the loss by a scale factor. - 2. Backward the loss to obtain the gradients (fp16). - 3. Copy gradients from the model to the fp32 weight copy. - 4. Scale the gradients back and update the fp32 weight copy. - 5. Copy back the params from fp32 weight copy to the fp16 model. - 6. Save loss_scaler state_dict for resume purpose. - """ - # clear grads of last iteration - runner.model.zero_grad() - runner.optimizer.zero_grad() - # scale the loss value - scaled_loss = runner.outputs['loss'] * self.loss_scaler.loss_scale - scaled_loss.backward() - # copy fp16 grads in the model to fp32 params in the optimizer - - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - self.loss_scaler.update_scale(has_overflow) - if has_overflow: - runner.logger.warning('Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - @HOOKS.register_module() - class GradientCumulativeFp16OptimizerHook(GradientCumulativeOptimizerHook, - Fp16OptimizerHook): - """Fp16 optimizer Hook (using mmcv implementation) implements multi- - iters gradient cumulating.""" - - def __init__(self, *args, **kwargs): - super(GradientCumulativeFp16OptimizerHook, - self).__init__(*args, **kwargs) - - def after_train_iter(self, runner): - if not self.initialized: - self._init(runner) - - if runner.iter < self.divisible_iters: - loss_factor = self.cumulative_iters - else: - loss_factor = self.remainder_iters - - loss = runner.outputs['loss'] - loss = loss / loss_factor - - # scale the loss value - scaled_loss = loss * self.loss_scaler.loss_scale - scaled_loss.backward() - - if (self.every_n_iters(runner, self.cumulative_iters) - or self.is_last_iter(runner)): - - # copy fp16 grads in the model to fp32 params in the optimizer - fp32_weights = [] - for param_group in runner.optimizer.param_groups: - fp32_weights += param_group['params'] - self.copy_grads_to_fp32(runner.model, fp32_weights) - # allreduce grads - if self.distributed: - allreduce_grads(fp32_weights, self.coalesce, - self.bucket_size_mb) - - has_overflow = self.loss_scaler.has_overflow(fp32_weights) - # if has overflow, skip this iteration - if not has_overflow: - # scale the gradients back - for param in fp32_weights: - if param.grad is not None: - param.grad.div_(self.loss_scaler.loss_scale) - if self.grad_clip is not None: - grad_norm = self.clip_grads(fp32_weights) - if grad_norm is not None: - # Add grad norm to the logger - runner.log_buffer.update( - {'grad_norm': float(grad_norm)}, - runner.outputs['num_samples']) - # update fp32 params - runner.optimizer.step() - # copy fp32 params to the fp16 model - self.copy_params_to_fp16(runner.model, fp32_weights) - else: - runner.logger.warning( - 'Check overflow, downscale loss scale ' - f'to {self.loss_scaler.cur_scale}') - - self.loss_scaler.update_scale(has_overflow) - - # save state_dict of loss_scaler - runner.meta.setdefault( - 'fp16', {})['loss_scaler'] = self.loss_scaler.state_dict() - - # clear grads - runner.model.zero_grad() - runner.optimizer.zero_grad() diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/profiler.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/profiler.py deleted file mode 100755 index b70236997..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/profiler.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from typing import Callable, List, Optional, Union - -import torch - -from ..dist_utils import master_only -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class ProfilerHook(Hook): - """Profiler to analyze performance during training. - - PyTorch Profiler is a tool that allows the collection of the performance - metrics during the training. More details on Profiler can be found at - https://pytorch.org/docs/1.8.1/profiler.html#torch.profiler.profile - - Args: - by_epoch (bool): Profile performance by epoch or by iteration. - Default: True. - profile_iters (int): Number of iterations for profiling. - If ``by_epoch=True``, profile_iters indicates that they are the - first profile_iters epochs at the beginning of the - training, otherwise it indicates the first profile_iters - iterations. Default: 1. - activities (list[str]): List of activity groups (CPU, CUDA) to use in - profiling. Default: ['cpu', 'cuda']. - schedule (dict, optional): Config of generating the callable schedule. - if schedule is None, profiler will not add step markers into the - trace and table view. Default: None. - on_trace_ready (callable, dict): Either a handler or a dict of generate - handler. Default: None. - record_shapes (bool): Save information about operator's input shapes. - Default: False. - profile_memory (bool): Track tensor memory allocation/deallocation. - Default: False. - with_stack (bool): Record source information (file and line number) - for the ops. Default: False. - with_flops (bool): Use formula to estimate the FLOPS of specific - operators (matrix multiplication and 2D convolution). - Default: False. - json_trace_path (str, optional): Exports the collected trace in Chrome - JSON format. Default: None. - - Example: - >>> runner = ... # instantiate a Runner - >>> # tensorboard trace - >>> trace_config = dict(type='tb_trace', dir_name='work_dir') - >>> profiler_config = dict(on_trace_ready=trace_config) - >>> runner.register_profiler_hook(profiler_config) - >>> runner.run(data_loaders=[trainloader], workflow=[('train', 1)]) - """ - - def __init__(self, - by_epoch: bool = True, - profile_iters: int = 1, - activities: List[str] = ['cpu', 'cuda'], - schedule: Optional[dict] = None, - on_trace_ready: Optional[Union[Callable, dict]] = None, - record_shapes: bool = False, - profile_memory: bool = False, - with_stack: bool = False, - with_flops: bool = False, - json_trace_path: Optional[str] = None) -> None: - try: - from torch import profiler # torch version >= 1.8.1 - except ImportError: - raise ImportError('profiler is the new feature of torch1.8.1, ' - f'but your version is {torch.__version__}') - - assert isinstance(by_epoch, bool), '``by_epoch`` should be a boolean.' - self.by_epoch = by_epoch - - if profile_iters < 1: - raise ValueError('profile_iters should be greater than 0, but got ' - f'{profile_iters}') - self.profile_iters = profile_iters - - if not isinstance(activities, list): - raise ValueError( - f'activities should be list, but got {type(activities)}') - self.activities = [] - for activity in activities: - activity = activity.lower() - if activity == 'cpu': - self.activities.append(profiler.ProfilerActivity.CPU) - elif activity == 'cuda': - self.activities.append(profiler.ProfilerActivity.CUDA) - else: - raise ValueError( - f'activity should be "cpu" or "cuda", but got {activity}') - - if schedule is not None: - self.schedule = profiler.schedule(**schedule) - else: - self.schedule = None - - self.on_trace_ready = on_trace_ready - self.record_shapes = record_shapes - self.profile_memory = profile_memory - self.with_stack = with_stack - self.with_flops = with_flops - self.json_trace_path = json_trace_path - - @master_only - def before_run(self, runner): - if self.by_epoch and runner.max_epochs < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_epochs}') - - if not self.by_epoch and runner.max_iters < self.profile_iters: - raise ValueError('self.profile_iters should not be greater than ' - f'{runner.max_iters}') - - if callable(self.on_trace_ready): # handler - _on_trace_ready = self.on_trace_ready - elif isinstance(self.on_trace_ready, dict): # config of handler - trace_cfg = self.on_trace_ready.copy() - trace_type = trace_cfg.pop('type') # log_trace handler - if trace_type == 'log_trace': - - def _log_handler(prof): - print(prof.key_averages().table(**trace_cfg)) - - _on_trace_ready = _log_handler - elif trace_type == 'tb_trace': # tensorboard_trace handler - try: - import torch_tb_profiler # noqa: F401 - except ImportError: - raise ImportError('please run "pip install ' - 'torch-tb-profiler" to install ' - 'torch_tb_profiler') - _on_trace_ready = torch.profiler.tensorboard_trace_handler( - **trace_cfg) - else: - raise ValueError('trace_type should be "log_trace" or ' - f'"tb_trace", but got {trace_type}') - elif self.on_trace_ready is None: - _on_trace_ready = None # type: ignore - else: - raise ValueError('on_trace_ready should be handler, dict or None, ' - f'but got {type(self.on_trace_ready)}') - - if runner.max_epochs > 1: - warnings.warn(f'profiler will profile {runner.max_epochs} epochs ' - 'instead of 1 epoch. Since profiler will slow down ' - 'the training, it is recommended to train 1 epoch ' - 'with ProfilerHook and adjust your setting according' - ' to the profiler summary. During normal training ' - '(epoch > 1), you may disable the ProfilerHook.') - - self.profiler = torch.profiler.profile( - activities=self.activities, - schedule=self.schedule, - on_trace_ready=_on_trace_ready, - record_shapes=self.record_shapes, - profile_memory=self.profile_memory, - with_stack=self.with_stack, - with_flops=self.with_flops) - - self.profiler.__enter__() - runner.logger.info('profiler is profiling...') - - @master_only - def after_train_epoch(self, runner): - if self.by_epoch and runner.epoch == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) - - @master_only - def after_train_iter(self, runner): - self.profiler.step() - if not self.by_epoch and runner.iter == self.profile_iters - 1: - runner.logger.info('profiler may take a few minutes...') - self.profiler.__exit__(None, None, None) - if self.json_trace_path is not None: - self.profiler.export_chrome_trace(self.json_trace_path) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/sampler_seed.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/sampler_seed.py deleted file mode 100755 index ee0dc6bdd..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/sampler_seed.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class DistSamplerSeedHook(Hook): - """Data-loading sampler for distributed training. - - When distributed training, it is only useful in conjunction with - :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same - purpose with :obj:`IterLoader`. - """ - - def before_epoch(self, runner): - if hasattr(runner.data_loader.sampler, 'set_epoch'): - # in case the data loader uses `SequentialSampler` in Pytorch - runner.data_loader.sampler.set_epoch(runner.epoch) - elif hasattr(runner.data_loader.batch_sampler.sampler, 'set_epoch'): - # batch sampler in pytorch warps the sampler as its attributes. - runner.data_loader.batch_sampler.sampler.set_epoch(runner.epoch) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/sync_buffer.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/sync_buffer.py deleted file mode 100755 index 6376b7ff8..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/hooks/sync_buffer.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from ..dist_utils import allreduce_params -from .hook import HOOKS, Hook - - -@HOOKS.register_module() -class SyncBuffersHook(Hook): - """Synchronize model buffers such as running_mean and running_var in BN at - the end of each epoch. - - Args: - distributed (bool): Whether distributed training is used. It is - effective only for distributed training. Defaults to True. - """ - - def __init__(self, distributed=True): - self.distributed = distributed - - def after_epoch(self, runner): - """All-reduce model buffers at the end of each epoch.""" - if self.distributed: - allreduce_params(runner.model.buffers()) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/iter_based_runner.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/iter_based_runner.py deleted file mode 100755 index 9892b07a4..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/iter_based_runner.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import platform -import shutil -import time -import warnings - -import torch -from torch.optim import Optimizer - -import mmcv -from .base_runner import BaseRunner -from .builder import RUNNERS -from .checkpoint import save_checkpoint -from .hooks import IterTimerHook -from .utils import get_host_info - - -class IterLoader: - - def __init__(self, dataloader): - self._dataloader = dataloader - self.iter_loader = iter(self._dataloader) - self._epoch = 0 - - @property - def epoch(self): - return self._epoch - - def __next__(self): - try: - data = next(self.iter_loader) - except StopIteration: - self._epoch += 1 - if hasattr(self._dataloader.sampler, 'set_epoch'): - self._dataloader.sampler.set_epoch(self._epoch) - time.sleep(2) # Prevent possible deadlock during epoch transition - self.iter_loader = iter(self._dataloader) - data = next(self.iter_loader) - - return data - - def __len__(self): - return len(self._dataloader) - - -@RUNNERS.register_module() -class IterBasedRunner(BaseRunner): - """Iteration-based Runner. - - This runner train models iteration by iteration. - """ - - def train(self, data_loader, **kwargs): - self.model.train() - self.mode = 'train' - self.data_loader = data_loader - self._epoch = data_loader.epoch - data_batch = next(data_loader) - self.call_hook('before_train_iter') - outputs = self.model.train_step(data_batch, self.optimizer, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.train_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_train_iter') - self._inner_iter += 1 - self._iter += 1 - - @torch.no_grad() - def val(self, data_loader, **kwargs): - self.model.eval() - self.mode = 'val' - self.data_loader = data_loader - data_batch = next(data_loader) - self.call_hook('before_val_iter') - outputs = self.model.val_step(data_batch, **kwargs) - if not isinstance(outputs, dict): - raise TypeError('model.val_step() must return a dict') - if 'log_vars' in outputs: - self.log_buffer.update(outputs['log_vars'], outputs['num_samples']) - self.outputs = outputs - self.call_hook('after_val_iter') - self._inner_iter += 1 - - def run(self, data_loaders, workflow, max_iters=None, **kwargs): - """Start running. - - Args: - data_loaders (list[:obj:`DataLoader`]): Dataloaders for training - and validation. - workflow (list[tuple]): A list of (phase, iters) to specify the - running order and iterations. E.g, [('train', 10000), - ('val', 1000)] means running 10000 iterations for training and - 1000 iterations for validation, iteratively. - """ - assert isinstance(data_loaders, list) - assert mmcv.is_list_of(workflow, tuple) - assert len(data_loaders) == len(workflow) - if max_iters is not None: - warnings.warn( - 'setting max_iters in run is deprecated, ' - 'please set max_iters in runner_config', DeprecationWarning) - self._max_iters = max_iters - assert self._max_iters is not None, ( - 'max_iters must be specified during instantiation') - - work_dir = self.work_dir if self.work_dir is not None else 'NONE' - self.logger.info('Start running, host: %s, work_dir: %s', - get_host_info(), work_dir) - self.logger.info('Hooks will be executed in the following order:\n%s', - self.get_hook_info()) - self.logger.info('workflow: %s, max: %d iters', workflow, - self._max_iters) - self.call_hook('before_run') - - iter_loaders = [IterLoader(x) for x in data_loaders] - - self.call_hook('before_epoch') - - while self.iter < self._max_iters: - for i, flow in enumerate(workflow): - self._inner_iter = 0 - mode, iters = flow - if not isinstance(mode, str) or not hasattr(self, mode): - raise ValueError( - 'runner has no method named "{}" to run a workflow'. - format(mode)) - iter_runner = getattr(self, mode) - for _ in range(iters): - if mode == 'train' and self.iter >= self._max_iters: - break - iter_runner(iter_loaders[i], **kwargs) - - time.sleep(1) # wait for some hooks like loggers to finish - self.call_hook('after_epoch') - self.call_hook('after_run') - - def resume(self, - checkpoint, - resume_optimizer=True, - map_location='default'): - """Resume model from checkpoint. - - Args: - checkpoint (str): Checkpoint to resume from. - resume_optimizer (bool, optional): Whether resume the optimizer(s) - if the checkpoint file includes optimizer(s). Default to True. - map_location (str, optional): Same as :func:`torch.load`. - Default to 'default'. - """ - if map_location == 'default': - device_id = torch.cuda.current_device() - checkpoint = self.load_checkpoint( - checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - else: - checkpoint = self.load_checkpoint( - checkpoint, map_location=map_location) - - self._epoch = checkpoint['meta']['epoch'] - self._iter = checkpoint['meta']['iter'] - self._inner_iter = checkpoint['meta']['iter'] - if 'optimizer' in checkpoint and resume_optimizer: - if isinstance(self.optimizer, Optimizer): - self.optimizer.load_state_dict(checkpoint['optimizer']) - elif isinstance(self.optimizer, dict): - for k in self.optimizer.keys(): - self.optimizer[k].load_state_dict( - checkpoint['optimizer'][k]) - else: - raise TypeError( - 'Optimizer should be dict or torch.optim.Optimizer ' - f'but got {type(self.optimizer)}') - - self.logger.info(f'resumed from epoch: {self.epoch}, iter {self.iter}') - - def save_checkpoint(self, - out_dir, - filename_tmpl='iter_{}.pth', - meta=None, - save_optimizer=True, - create_symlink=True): - """Save checkpoint to file. - - Args: - out_dir (str): Directory to save checkpoint files. - filename_tmpl (str, optional): Checkpoint file template. - Defaults to 'iter_{}.pth'. - meta (dict, optional): Metadata to be saved in checkpoint. - Defaults to None. - save_optimizer (bool, optional): Whether save optimizer. - Defaults to True. - create_symlink (bool, optional): Whether create symlink to the - latest checkpoint file. Defaults to True. - """ - if meta is None: - meta = {} - elif not isinstance(meta, dict): - raise TypeError( - f'meta should be a dict or None, but got {type(meta)}') - if self.meta is not None: - meta.update(self.meta) - # Note: meta.update(self.meta) should be done before - # meta.update(epoch=self.epoch + 1, iter=self.iter) otherwise - # there will be problems with resumed checkpoints. - # More details in https://github.com/open-mmlab/mmcv/pull/1108 - meta.update(epoch=self.epoch + 1, iter=self.iter) - - filename = filename_tmpl.format(self.iter + 1) - filepath = osp.join(out_dir, filename) - optimizer = self.optimizer if save_optimizer else None - save_checkpoint(self.model, filepath, optimizer=optimizer, meta=meta) - # in some environments, `os.symlink` is not supported, you may need to - # set `create_symlink` to False - if create_symlink: - dst_file = osp.join(out_dir, 'latest.pth') - if platform.system() != 'Windows': - mmcv.symlink(filename, dst_file) - else: - shutil.copy(filepath, dst_file) - - def register_training_hooks(self, - lr_config, - optimizer_config=None, - checkpoint_config=None, - log_config=None, - momentum_config=None, - custom_hooks_config=None): - """Register default hooks for iter-based training. - - Checkpoint hook, optimizer stepper hook and logger hooks will be set to - `by_epoch=False` by default. - - Default hooks include: - - +----------------------+-------------------------+ - | Hooks | Priority | - +======================+=========================+ - | LrUpdaterHook | VERY_HIGH (10) | - +----------------------+-------------------------+ - | MomentumUpdaterHook | HIGH (30) | - +----------------------+-------------------------+ - | OptimizerStepperHook | ABOVE_NORMAL (40) | - +----------------------+-------------------------+ - | CheckpointSaverHook | NORMAL (50) | - +----------------------+-------------------------+ - | IterTimerHook | LOW (70) | - +----------------------+-------------------------+ - | LoggerHook(s) | VERY_LOW (90) | - +----------------------+-------------------------+ - | CustomHook(s) | defaults to NORMAL (50) | - +----------------------+-------------------------+ - - If custom hooks have same priority with default hooks, custom hooks - will be triggered after default hooks. - """ - if checkpoint_config is not None: - checkpoint_config.setdefault('by_epoch', False) - if lr_config is not None: - lr_config.setdefault('by_epoch', False) - if log_config is not None: - for info in log_config['hooks']: - info.setdefault('by_epoch', False) - super(IterBasedRunner, self).register_training_hooks( - lr_config=lr_config, - momentum_config=momentum_config, - optimizer_config=optimizer_config, - checkpoint_config=checkpoint_config, - log_config=log_config, - timer_config=IterTimerHook(), - custom_hooks_config=custom_hooks_config) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/log_buffer.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/log_buffer.py deleted file mode 100755 index d949e2941..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/log_buffer.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections import OrderedDict - -import numpy as np - - -class LogBuffer: - - def __init__(self): - self.val_history = OrderedDict() - self.n_history = OrderedDict() - self.output = OrderedDict() - self.ready = False - - def clear(self): - self.val_history.clear() - self.n_history.clear() - self.clear_output() - - def clear_output(self): - self.output.clear() - self.ready = False - - def update(self, vars, count=1): - assert isinstance(vars, dict) - for key, var in vars.items(): - if key not in self.val_history: - self.val_history[key] = [] - self.n_history[key] = [] - self.val_history[key].append(var) - self.n_history[key].append(count) - - def average(self, n=0): - """Average latest n values or all values.""" - assert n >= 0 - for key in self.val_history: - values = np.array(self.val_history[key][-n:]) - nums = np.array(self.n_history[key][-n:]) - avg = np.sum(values * nums) / np.sum(nums) - self.output[key] = avg - self.ready = True diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/optimizer/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/optimizer/__init__.py deleted file mode 100755 index 53c34d047..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/optimizer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import (OPTIMIZER_BUILDERS, OPTIMIZERS, build_optimizer, - build_optimizer_constructor) -from .default_constructor import DefaultOptimizerConstructor - -__all__ = [ - 'OPTIMIZER_BUILDERS', 'OPTIMIZERS', 'DefaultOptimizerConstructor', - 'build_optimizer', 'build_optimizer_constructor' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/optimizer/builder.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/optimizer/builder.py deleted file mode 100755 index f9234eed8..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/optimizer/builder.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import inspect - -import torch - -from ...utils import Registry, build_from_cfg - -OPTIMIZERS = Registry('optimizer') -OPTIMIZER_BUILDERS = Registry('optimizer builder') - - -def register_torch_optimizers(): - torch_optimizers = [] - for module_name in dir(torch.optim): - if module_name.startswith('__'): - continue - _optim = getattr(torch.optim, module_name) - if inspect.isclass(_optim) and issubclass(_optim, - torch.optim.Optimizer): - OPTIMIZERS.register_module()(_optim) - torch_optimizers.append(module_name) - return torch_optimizers - - -TORCH_OPTIMIZERS = register_torch_optimizers() - - -def build_optimizer_constructor(cfg): - return build_from_cfg(cfg, OPTIMIZER_BUILDERS) - - -def build_optimizer(model, cfg): - optimizer_cfg = copy.deepcopy(cfg) - constructor_type = optimizer_cfg.pop('constructor', - 'DefaultOptimizerConstructor') - paramwise_cfg = optimizer_cfg.pop('paramwise_cfg', None) - optim_constructor = build_optimizer_constructor( - dict( - type=constructor_type, - optimizer_cfg=optimizer_cfg, - paramwise_cfg=paramwise_cfg)) - optimizer = optim_constructor(model) - return optimizer diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/optimizer/default_constructor.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/optimizer/default_constructor.py deleted file mode 100755 index 64d06847b..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/optimizer/default_constructor.py +++ /dev/null @@ -1,249 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings - -import torch -from torch.nn import GroupNorm, LayerNorm - -from mmcv.utils import _BatchNorm, _InstanceNorm, build_from_cfg, is_list_of -from mmcv.utils.ext_loader import check_ops_exist -from .builder import OPTIMIZER_BUILDERS, OPTIMIZERS - - -@OPTIMIZER_BUILDERS.register_module() -class DefaultOptimizerConstructor: - """Default constructor for optimizers. - - By default each parameter share the same optimizer settings, and we - provide an argument ``paramwise_cfg`` to specify parameter-wise settings. - It is a dict and may contain the following fields: - - - ``custom_keys`` (dict): Specified parameters-wise settings by keys. If - one of the keys in ``custom_keys`` is a substring of the name of one - parameter, then the setting of the parameter will be specified by - ``custom_keys[key]`` and other setting like ``bias_lr_mult`` etc. will - be ignored. It should be noted that the aforementioned ``key`` is the - longest key that is a substring of the name of the parameter. If there - are multiple matched keys with the same length, then the key with lower - alphabet order will be chosen. - ``custom_keys[key]`` should be a dict and may contain fields ``lr_mult`` - and ``decay_mult``. See Example 2 below. - - ``bias_lr_mult`` (float): It will be multiplied to the learning - rate for all bias parameters (except for those in normalization - layers and offset layers of DCN). - - ``bias_decay_mult`` (float): It will be multiplied to the weight - decay for all bias parameters (except for those in - normalization layers, depthwise conv layers, offset layers of DCN). - - ``norm_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of normalization - layers. - - ``dwconv_decay_mult`` (float): It will be multiplied to the weight - decay for all weight and bias parameters of depthwise conv - layers. - - ``dcn_offset_lr_mult`` (float): It will be multiplied to the learning - rate for parameters of offset layer in the deformable convs - of a model. - - ``bypass_duplicate`` (bool): If true, the duplicate parameters - would not be added into optimizer. Default: False. - - Note: - 1. If the option ``dcn_offset_lr_mult`` is used, the constructor will - override the effect of ``bias_lr_mult`` in the bias of offset - layer. So be careful when using both ``bias_lr_mult`` and - ``dcn_offset_lr_mult``. If you wish to apply both of them to the - offset layer in deformable convs, set ``dcn_offset_lr_mult`` - to the original ``dcn_offset_lr_mult`` * ``bias_lr_mult``. - 2. If the option ``dcn_offset_lr_mult`` is used, the constructor will - apply it to all the DCN layers in the model. So be careful when - the model contains multiple DCN layers in places other than - backbone. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - optimizer_cfg (dict): The config dict of the optimizer. - Positional fields are - - - `type`: class name of the optimizer. - - Optional fields are - - - any arguments of the corresponding optimizer type, e.g., - lr, weight_decay, momentum, etc. - paramwise_cfg (dict, optional): Parameter-wise options. - - Example 1: - >>> model = torch.nn.modules.Conv1d(1, 1, 1) - >>> optimizer_cfg = dict(type='SGD', lr=0.01, momentum=0.9, - >>> weight_decay=0.0001) - >>> paramwise_cfg = dict(norm_decay_mult=0.) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - - Example 2: - >>> # assume model have attribute model.backbone and model.cls_head - >>> optimizer_cfg = dict(type='SGD', lr=0.01, weight_decay=0.95) - >>> paramwise_cfg = dict(custom_keys={ - '.backbone': dict(lr_mult=0.1, decay_mult=0.9)}) - >>> optim_builder = DefaultOptimizerConstructor( - >>> optimizer_cfg, paramwise_cfg) - >>> optimizer = optim_builder(model) - >>> # Then the `lr` and `weight_decay` for model.backbone is - >>> # (0.01 * 0.1, 0.95 * 0.9). `lr` and `weight_decay` for - >>> # model.cls_head is (0.01, 0.95). - """ - - def __init__(self, optimizer_cfg, paramwise_cfg=None): - if not isinstance(optimizer_cfg, dict): - raise TypeError('optimizer_cfg should be a dict', - f'but got {type(optimizer_cfg)}') - self.optimizer_cfg = optimizer_cfg - self.paramwise_cfg = {} if paramwise_cfg is None else paramwise_cfg - self.base_lr = optimizer_cfg.get('lr', None) - self.base_wd = optimizer_cfg.get('weight_decay', None) - self._validate_cfg() - - def _validate_cfg(self): - if not isinstance(self.paramwise_cfg, dict): - raise TypeError('paramwise_cfg should be None or a dict, ' - f'but got {type(self.paramwise_cfg)}') - - if 'custom_keys' in self.paramwise_cfg: - if not isinstance(self.paramwise_cfg['custom_keys'], dict): - raise TypeError( - 'If specified, custom_keys must be a dict, ' - f'but got {type(self.paramwise_cfg["custom_keys"])}') - if self.base_wd is None: - for key in self.paramwise_cfg['custom_keys']: - if 'decay_mult' in self.paramwise_cfg['custom_keys'][key]: - raise ValueError('base_wd should not be None') - - # get base lr and weight decay - # weight_decay must be explicitly specified if mult is specified - if ('bias_decay_mult' in self.paramwise_cfg - or 'norm_decay_mult' in self.paramwise_cfg - or 'dwconv_decay_mult' in self.paramwise_cfg): - if self.base_wd is None: - raise ValueError('base_wd should not be None') - - def _is_in(self, param_group, param_group_list): - assert is_list_of(param_group_list, dict) - param = set(param_group['params']) - param_set = set() - for group in param_group_list: - param_set.update(set(group['params'])) - - return not param.isdisjoint(param_set) - - def add_params(self, params, module, prefix='', is_dcn_module=None): - """Add all parameters of module to the params list. - - The parameters of the given module will be added to the list of param - groups, with specific rules defined by paramwise_cfg. - - Args: - params (list[dict]): A list of param groups, it will be modified - in place. - module (nn.Module): The module to be added. - prefix (str): The prefix of the module - is_dcn_module (int|float|None): If the current module is a - submodule of DCN, `is_dcn_module` will be passed to - control conv_offset layer's learning rate. Defaults to None. - """ - # get param-wise options - custom_keys = self.paramwise_cfg.get('custom_keys', {}) - # first sort with alphabet order and then sort with reversed len of str - sorted_keys = sorted(sorted(custom_keys.keys()), key=len, reverse=True) - - bias_lr_mult = self.paramwise_cfg.get('bias_lr_mult', 1.) - bias_decay_mult = self.paramwise_cfg.get('bias_decay_mult', 1.) - norm_decay_mult = self.paramwise_cfg.get('norm_decay_mult', 1.) - dwconv_decay_mult = self.paramwise_cfg.get('dwconv_decay_mult', 1.) - bypass_duplicate = self.paramwise_cfg.get('bypass_duplicate', False) - dcn_offset_lr_mult = self.paramwise_cfg.get('dcn_offset_lr_mult', 1.) - - # special rules for norm layers and depth-wise conv layers - is_norm = isinstance(module, - (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm)) - is_dwconv = ( - isinstance(module, torch.nn.Conv2d) - and module.in_channels == module.groups) - - for name, param in module.named_parameters(recurse=False): - param_group = {'params': [param]} - if not param.requires_grad: - params.append(param_group) - continue - if bypass_duplicate and self._is_in(param_group, params): - warnings.warn(f'{prefix} is duplicate. It is skipped since ' - f'bypass_duplicate={bypass_duplicate}') - continue - # if the parameter match one of the custom keys, ignore other rules - is_custom = False - for key in sorted_keys: - if key in f'{prefix}.{name}': - is_custom = True - lr_mult = custom_keys[key].get('lr_mult', 1.) - param_group['lr'] = self.base_lr * lr_mult - if self.base_wd is not None: - decay_mult = custom_keys[key].get('decay_mult', 1.) - param_group['weight_decay'] = self.base_wd * decay_mult - break - - if not is_custom: - # bias_lr_mult affects all bias parameters - # except for norm.bias dcn.conv_offset.bias - if name == 'bias' and not (is_norm or is_dcn_module): - param_group['lr'] = self.base_lr * bias_lr_mult - - if (prefix.find('conv_offset') != -1 and is_dcn_module - and isinstance(module, torch.nn.Conv2d)): - # deal with both dcn_offset's bias & weight - param_group['lr'] = self.base_lr * dcn_offset_lr_mult - - # apply weight decay policies - if self.base_wd is not None: - # norm decay - if is_norm: - param_group[ - 'weight_decay'] = self.base_wd * norm_decay_mult - # depth-wise conv - elif is_dwconv: - param_group[ - 'weight_decay'] = self.base_wd * dwconv_decay_mult - # bias lr and decay - elif name == 'bias' and not is_dcn_module: - # TODO: current bias_decay_mult will have affect on DCN - param_group[ - 'weight_decay'] = self.base_wd * bias_decay_mult - params.append(param_group) - - if check_ops_exist(): - from mmcv.ops import ModulatedDeformConv2d - is_dcn_module = isinstance(module, - (ModulatedDeformConv2d)) - else: - is_dcn_module = False - for child_name, child_mod in module.named_children(): - child_prefix = f'{prefix}.{child_name}' if prefix else child_name - self.add_params( - params, - child_mod, - prefix=child_prefix, - is_dcn_module=is_dcn_module) - - def __call__(self, model): - if hasattr(model, 'module'): - model = model.module - - optimizer_cfg = self.optimizer_cfg.copy() - # if no paramwise option is specified, just use the global setting - if not self.paramwise_cfg: - optimizer_cfg['params'] = model.parameters() - return build_from_cfg(optimizer_cfg, OPTIMIZERS) - - # set param-wise lr and weight decay recursively - params = [] - self.add_params(params, model) - optimizer_cfg['params'] = params - - return build_from_cfg(optimizer_cfg, OPTIMIZERS) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/priority.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/priority.py deleted file mode 100755 index 64cc4e3a0..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/priority.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from enum import Enum - - -class Priority(Enum): - """Hook priority levels. - - +--------------+------------+ - | Level | Value | - +==============+============+ - | HIGHEST | 0 | - +--------------+------------+ - | VERY_HIGH | 10 | - +--------------+------------+ - | HIGH | 30 | - +--------------+------------+ - | ABOVE_NORMAL | 40 | - +--------------+------------+ - | NORMAL | 50 | - +--------------+------------+ - | BELOW_NORMAL | 60 | - +--------------+------------+ - | LOW | 70 | - +--------------+------------+ - | VERY_LOW | 90 | - +--------------+------------+ - | LOWEST | 100 | - +--------------+------------+ - """ - - HIGHEST = 0 - VERY_HIGH = 10 - HIGH = 30 - ABOVE_NORMAL = 40 - NORMAL = 50 - BELOW_NORMAL = 60 - LOW = 70 - VERY_LOW = 90 - LOWEST = 100 - - -def get_priority(priority): - """Get priority value. - - Args: - priority (int or str or :obj:`Priority`): Priority. - - Returns: - int: The priority value. - """ - if isinstance(priority, int): - if priority < 0 or priority > 100: - raise ValueError('priority must be between 0 and 100') - return priority - elif isinstance(priority, Priority): - return priority.value - elif isinstance(priority, str): - return Priority[priority.upper()].value - else: - raise TypeError('priority must be an integer or Priority enum value') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/utils.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/utils.py deleted file mode 100755 index 144d11e1a..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/runner/utils.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import random -import sys -import time -import warnings -from getpass import getuser -from socket import gethostname - -import numpy as np -import torch - -import mmcv - - -def get_host_info(): - """Get hostname and username. - - Return empty string if exception raised, e.g. ``getpass.getuser()`` will - lead to error in docker container - """ - host = '' - try: - host = f'{getuser()}@{gethostname()}' - except Exception as e: - warnings.warn(f'Host or user not found: {str(e)}') - finally: - return host - - -def get_time_str(): - return time.strftime('%Y%m%d_%H%M%S', time.localtime()) - - -def obj_from_dict(info, parent=None, default_args=None): - """Initialize an object from dict. - - The dict must contain the key "type", which indicates the object type, it - can be either a string or type, such as "list" or ``list``. Remaining - fields are treated as the arguments for constructing the object. - - Args: - info (dict): Object types and arguments. - parent (:class:`module`): Module which may containing expected object - classes. - default_args (dict, optional): Default arguments for initializing the - object. - - Returns: - any type: Object built from the dict. - """ - assert isinstance(info, dict) and 'type' in info - assert isinstance(default_args, dict) or default_args is None - args = info.copy() - obj_type = args.pop('type') - if mmcv.is_str(obj_type): - if parent is not None: - obj_type = getattr(parent, obj_type) - else: - obj_type = sys.modules[obj_type] - elif not isinstance(obj_type, type): - raise TypeError('type must be a str or valid type, but ' - f'got {type(obj_type)}') - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - return obj_type(**args) - - -def set_random_seed(seed, deterministic=False, use_rank_shift=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - rank_shift (bool): Whether to add rank number to the random seed to - have different random seed in different threads. Default: False. - """ - if use_rank_shift: - rank, _ = mmcv.runner.get_dist_info() - seed += rank - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/__init__.py deleted file mode 100755 index 378a00684..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/__init__.py +++ /dev/null @@ -1,69 +0,0 @@ -# flake8: noqa -# Copyright (c) OpenMMLab. All rights reserved. -from .config import Config, ConfigDict, DictAction -from .misc import (check_prerequisites, concat_list, deprecated_api_warning, - has_method, import_modules_from_strings, is_list_of, - is_method_overridden, is_seq_of, is_str, is_tuple_of, - iter_cast, list_cast, requires_executable, requires_package, - slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, - to_ntuple, tuple_cast) -from .path import (check_file_exist, fopen, is_filepath, mkdir_or_exist, - scandir, symlink) -from .progressbar import (ProgressBar, track_iter_progress, - track_parallel_progress, track_progress) -from .testing import (assert_attrs_equal, assert_dict_contains_subset, - assert_dict_has_keys, assert_is_norm_layer, - assert_keys_equal, assert_params_all_zeros, - check_python_script) -from .timer import Timer, TimerError, check_time -from .version_utils import digit_version, get_git_hash - -try: - import torch -except ImportError: - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'is_str', 'iter_cast', - 'list_cast', 'tuple_cast', 'is_seq_of', 'is_list_of', 'is_tuple_of', - 'slice_list', 'concat_list', 'check_prerequisites', 'requires_package', - 'requires_executable', 'is_filepath', 'fopen', 'check_file_exist', - 'mkdir_or_exist', 'symlink', 'scandir', 'ProgressBar', - 'track_progress', 'track_iter_progress', 'track_parallel_progress', - 'Timer', 'TimerError', 'check_time', 'deprecated_api_warning', - 'digit_version', 'get_git_hash', 'import_modules_from_strings', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'check_python_script', - 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', - 'is_method_overridden', 'has_method' - ] -else: - from .env import collect_env - from .logging import get_logger, print_log - from .parrots_jit import jit, skip_no_elena - from .parrots_wrapper import ( - TORCH_VERSION, BuildExtension, CppExtension, CUDAExtension, DataLoader, - PoolDataLoader, SyncBatchNorm, _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, - _AvgPoolNd, _BatchNorm, _ConvNd, _ConvTransposeMixin, _InstanceNorm, - _MaxPoolNd, get_build_config, is_rocm_pytorch, _get_cuda_home) - from .registry import Registry, build_from_cfg - from .trace import is_jit_tracing - __all__ = [ - 'Config', 'ConfigDict', 'DictAction', 'collect_env', 'get_logger', - 'print_log', 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', - 'is_seq_of', 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', - 'check_prerequisites', 'requires_package', 'requires_executable', - 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', - 'symlink', 'scandir', 'ProgressBar', 'track_progress', - 'track_iter_progress', 'track_parallel_progress', 'Registry', - 'build_from_cfg', 'Timer', 'TimerError', 'check_time', 'SyncBatchNorm', - '_AdaptiveAvgPoolNd', '_AdaptiveMaxPoolNd', '_AvgPoolNd', '_BatchNorm', - '_ConvNd', '_ConvTransposeMixin', '_InstanceNorm', '_MaxPoolNd', - 'get_build_config', 'BuildExtension', 'CppExtension', 'CUDAExtension', - 'DataLoader', 'PoolDataLoader', 'TORCH_VERSION', - 'deprecated_api_warning', 'digit_version', 'get_git_hash', - 'import_modules_from_strings', 'jit', 'skip_no_elena', - 'assert_dict_contains_subset', 'assert_attrs_equal', - 'assert_dict_has_keys', 'assert_keys_equal', 'assert_is_norm_layer', - 'assert_params_all_zeros', 'check_python_script', - 'is_method_overridden', 'is_jit_tracing', 'is_rocm_pytorch', - '_get_cuda_home', 'has_method' - ] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/config.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/config.py deleted file mode 100755 index c71377c07..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/config.py +++ /dev/null @@ -1,688 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import ast -import copy -import os -import os.path as osp -import platform -import shutil -import sys -import tempfile -import uuid -import warnings -from argparse import Action, ArgumentParser -from collections import abc -from importlib import import_module - -from addict import Dict -from yapf.yapflib.yapf_api import FormatCode - -from .misc import import_modules_from_strings -from .path import check_file_exist - -if platform.system() == 'Windows': - import regex as re -else: - import re - -BASE_KEY = '_base_' -DELETE_KEY = '_delete_' -DEPRECATION_KEY = '_deprecation_' -RESERVED_KEYS = ['filename', 'text', 'pretty_text'] - - -class ConfigDict(Dict): - - def __missing__(self, name): - raise KeyError(name) - - def __getattr__(self, name): - try: - value = super(ConfigDict, self).__getattr__(name) - except KeyError: - ex = AttributeError(f"'{self.__class__.__name__}' object has no " - f"attribute '{name}'") - except Exception as e: - ex = e - else: - return value - raise ex - - -def add_args(parser, cfg, prefix=''): - for k, v in cfg.items(): - if isinstance(v, str): - parser.add_argument('--' + prefix + k) - elif isinstance(v, int): - parser.add_argument('--' + prefix + k, type=int) - elif isinstance(v, float): - parser.add_argument('--' + prefix + k, type=float) - elif isinstance(v, bool): - parser.add_argument('--' + prefix + k, action='store_true') - elif isinstance(v, dict): - add_args(parser, v, prefix + k + '.') - elif isinstance(v, abc.Iterable): - parser.add_argument('--' + prefix + k, type=type(v[0]), nargs='+') - else: - print(f'cannot parse key {prefix + k} of type {type(v)}') - return parser - - -class Config: - """A facility for config and config files. - - It supports common file formats as configs: python/json/yaml. The interface - is the same as a dict object and also allows access config values as - attributes. - - Example: - >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - >>> cfg.a - 1 - >>> cfg.b - {'b1': [0, 1]} - >>> cfg.b.b1 - [0, 1] - >>> cfg = Config.fromfile('tests/data/config/a.py') - >>> cfg.filename - "/home/kchen/projects/mmcv/tests/data/config/a.py" - >>> cfg.item4 - 'test' - >>> cfg - "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: " - "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}" - """ - - @staticmethod - def _validate_py_syntax(filename): - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - content = f.read() - try: - ast.parse(content) - except SyntaxError as e: - raise SyntaxError('There are syntax errors in config ' - f'file {filename}: {e}') - - @staticmethod - def _substitute_predefined_vars(filename, temp_config_name): - file_dirname = osp.dirname(filename) - file_basename = osp.basename(filename) - file_basename_no_extension = osp.splitext(file_basename)[0] - file_extname = osp.splitext(filename)[1] - support_templates = dict( - fileDirname=file_dirname, - fileBasename=file_basename, - fileBasenameNoExtension=file_basename_no_extension, - fileExtname=file_extname) - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - for key, value in support_templates.items(): - regexp = r'\{\{\s*' + str(key) + r'\s*\}\}' - value = value.replace('\\', '/') - config_file = re.sub(regexp, value, config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - - @staticmethod - def _pre_substitute_base_vars(filename, temp_config_name): - """Substitute base variable placehoders to string, so that parsing - would work.""" - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - config_file = f.read() - base_var_dict = {} - regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}' - base_vars = set(re.findall(regexp, config_file)) - for base_var in base_vars: - randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}' - base_var_dict[randstr] = base_var - regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}' - config_file = re.sub(regexp, f'"{randstr}"', config_file) - with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file: - tmp_config_file.write(config_file) - return base_var_dict - - @staticmethod - def _substitute_base_vars(cfg, base_var_dict, base_cfg): - """Substitute variable strings to their actual values.""" - cfg = copy.deepcopy(cfg) - - if isinstance(cfg, dict): - for k, v in cfg.items(): - if isinstance(v, str) and v in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[v].split('.'): - new_v = new_v[new_k] - cfg[k] = new_v - elif isinstance(v, (list, tuple, dict)): - cfg[k] = Config._substitute_base_vars( - v, base_var_dict, base_cfg) - elif isinstance(cfg, tuple): - cfg = tuple( - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg) - elif isinstance(cfg, list): - cfg = [ - Config._substitute_base_vars(c, base_var_dict, base_cfg) - for c in cfg - ] - elif isinstance(cfg, str) and cfg in base_var_dict: - new_v = base_cfg - for new_k in base_var_dict[cfg].split('.'): - new_v = new_v[new_k] - cfg = new_v - - return cfg - - @staticmethod - def _file2dict(filename, use_predefined_variables=True): - filename = osp.abspath(osp.expanduser(filename)) - check_file_exist(filename) - fileExtname = osp.splitext(filename)[1] - if fileExtname not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - - with tempfile.TemporaryDirectory() as temp_config_dir: - temp_config_file = tempfile.NamedTemporaryFile( - dir=temp_config_dir, suffix=fileExtname) - if platform.system() == 'Windows': - temp_config_file.close() - temp_config_name = osp.basename(temp_config_file.name) - # Substitute predefined variables - if use_predefined_variables: - Config._substitute_predefined_vars(filename, - temp_config_file.name) - else: - shutil.copyfile(filename, temp_config_file.name) - # Substitute base variables from placeholders to strings - base_var_dict = Config._pre_substitute_base_vars( - temp_config_file.name, temp_config_file.name) - - if filename.endswith('.py'): - temp_module_name = osp.splitext(temp_config_name)[0] - sys.path.insert(0, temp_config_dir) - Config._validate_py_syntax(filename) - mod = import_module(temp_module_name) - sys.path.pop(0) - cfg_dict = { - name: value - for name, value in mod.__dict__.items() - if not name.startswith('__') - } - # delete imported module - del sys.modules[temp_module_name] - elif filename.endswith(('.yml', '.yaml', '.json')): - import mmcv - cfg_dict = mmcv.load(temp_config_file.name) - # close temp file - temp_config_file.close() - - # check deprecation information - if DEPRECATION_KEY in cfg_dict: - deprecation_info = cfg_dict.pop(DEPRECATION_KEY) - warning_msg = f'The config file {filename} will be deprecated ' \ - 'in the future.' - if 'expected' in deprecation_info: - warning_msg += f' Please use {deprecation_info["expected"]} ' \ - 'instead.' - if 'reference' in deprecation_info: - warning_msg += ' More information can be found at ' \ - f'{deprecation_info["reference"]}' - warnings.warn(warning_msg) - - cfg_text = filename + '\n' - with open(filename, 'r', encoding='utf-8') as f: - # Setting encoding explicitly to resolve coding issue on windows - cfg_text += f.read() - - if BASE_KEY in cfg_dict: - cfg_dir = osp.dirname(filename) - base_filename = cfg_dict.pop(BASE_KEY) - base_filename = base_filename if isinstance( - base_filename, list) else [base_filename] - - cfg_dict_list = list() - cfg_text_list = list() - for f in base_filename: - _cfg_dict, _cfg_text = Config._file2dict(osp.join(cfg_dir, f)) - cfg_dict_list.append(_cfg_dict) - cfg_text_list.append(_cfg_text) - - base_cfg_dict = dict() - for c in cfg_dict_list: - duplicate_keys = base_cfg_dict.keys() & c.keys() - if len(duplicate_keys) > 0: - raise KeyError('Duplicate key is not allowed among bases. ' - f'Duplicate keys: {duplicate_keys}') - base_cfg_dict.update(c) - - # Substitute base variables from strings to their actual values - cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict, - base_cfg_dict) - - base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict) - cfg_dict = base_cfg_dict - - # merge cfg_text - cfg_text_list.append(cfg_text) - cfg_text = '\n'.join(cfg_text_list) - - return cfg_dict, cfg_text - - @staticmethod - def _merge_a_into_b(a, b, allow_list_keys=False): - """merge dict ``a`` into dict ``b`` (non-inplace). - - Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid - in-place modifications. - - Args: - a (dict): The source dict to be merged into ``b``. - b (dict): The origin dict to be fetch keys from ``a``. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in source ``a`` and will replace the element of the - corresponding index in b if b is a list. Default: False. - - Returns: - dict: The modified dict of ``b`` using ``a``. - - Examples: - # Normally merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # Delete b first and merge a into b. - >>> Config._merge_a_into_b( - ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1))) - {'obj': {'a': 2}} - - # b is a list - >>> Config._merge_a_into_b( - ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True) - [{'a': 2}, {'b': 2}] - """ - b = b.copy() - for k, v in a.items(): - if allow_list_keys and k.isdigit() and isinstance(b, list): - k = int(k) - if len(b) <= k: - raise KeyError(f'Index {k} exceeds the length of list {b}') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - elif isinstance(v, - dict) and k in b and not v.pop(DELETE_KEY, False): - allowed_types = (dict, list) if allow_list_keys else dict - if not isinstance(b[k], allowed_types): - raise TypeError( - f'{k}={v} in child config cannot inherit from base ' - f'because {k} is a dict in the child config but is of ' - f'type {type(b[k])} in base config. You may set ' - f'`{DELETE_KEY}=True` to ignore the base config') - b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys) - else: - b[k] = v - return b - - @staticmethod - def fromfile(filename, - use_predefined_variables=True, - import_custom_modules=True): - cfg_dict, cfg_text = Config._file2dict(filename, - use_predefined_variables) - if import_custom_modules and cfg_dict.get('custom_imports', None): - import_modules_from_strings(**cfg_dict['custom_imports']) - return Config(cfg_dict, cfg_text=cfg_text, filename=filename) - - @staticmethod - def fromstring(cfg_str, file_format): - """Generate config from config str. - - Args: - cfg_str (str): Config str. - file_format (str): Config file format corresponding to the - config str. Only py/yml/yaml/json type are supported now! - - Returns: - obj:`Config`: Config obj. - """ - if file_format not in ['.py', '.json', '.yaml', '.yml']: - raise IOError('Only py/yml/yaml/json type are supported now!') - if file_format != '.py' and 'dict(' in cfg_str: - # check if users specify a wrong suffix for python - warnings.warn( - 'Please check "file_format", the file format may be .py') - with tempfile.NamedTemporaryFile( - 'w', encoding='utf-8', suffix=file_format, - delete=False) as temp_file: - temp_file.write(cfg_str) - # on windows, previous implementation cause error - # see PR 1077 for details - cfg = Config.fromfile(temp_file.name) - os.remove(temp_file.name) - return cfg - - @staticmethod - def auto_argparser(description=None): - """Generate argparser from config file automatically (experimental)""" - partial_parser = ArgumentParser(description=description) - partial_parser.add_argument('config', help='config file path') - cfg_file = partial_parser.parse_known_args()[0].config - cfg = Config.fromfile(cfg_file) - parser = ArgumentParser(description=description) - parser.add_argument('config', help='config file path') - add_args(parser, cfg) - return parser, cfg - - def __init__(self, cfg_dict=None, cfg_text=None, filename=None): - if cfg_dict is None: - cfg_dict = dict() - elif not isinstance(cfg_dict, dict): - raise TypeError('cfg_dict must be a dict, but ' - f'got {type(cfg_dict)}') - for key in cfg_dict: - if key in RESERVED_KEYS: - raise KeyError(f'{key} is reserved for config file') - - super(Config, self).__setattr__('_cfg_dict', ConfigDict(cfg_dict)) - super(Config, self).__setattr__('_filename', filename) - if cfg_text: - text = cfg_text - elif filename: - with open(filename, 'r') as f: - text = f.read() - else: - text = '' - super(Config, self).__setattr__('_text', text) - - @property - def filename(self): - return self._filename - - @property - def text(self): - return self._text - - @property - def pretty_text(self): - - indent = 4 - - def _indent(s_, num_spaces): - s = s_.split('\n') - if len(s) == 1: - return s_ - first = s.pop(0) - s = [(num_spaces * ' ') + line for line in s] - s = '\n'.join(s) - s = first + '\n' + s - return s - - def _format_basic_types(k, v, use_mapping=False): - if isinstance(v, str): - v_str = f"'{v}'" - else: - v_str = str(v) - - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) - - return attr_str - - def _format_list(k, v, use_mapping=False): - # check if all items in the list are dict - if all(isinstance(_, dict) for _ in v): - v_str = '[\n' - v_str += '\n'.join( - f'dict({_indent(_format_dict(v_), indent)}),' - for v_ in v).rstrip(',') - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: {v_str}' - else: - attr_str = f'{str(k)}={v_str}' - attr_str = _indent(attr_str, indent) + ']' - else: - attr_str = _format_basic_types(k, v, use_mapping) - return attr_str - - def _contain_invalid_identifier(dict_str): - contain_invalid_identifier = False - for key_name in dict_str: - contain_invalid_identifier |= \ - (not str(key_name).isidentifier()) - return contain_invalid_identifier - - def _format_dict(input_dict, outest_level=False): - r = '' - s = [] - - use_mapping = _contain_invalid_identifier(input_dict) - if use_mapping: - r += '{' - for idx, (k, v) in enumerate(input_dict.items()): - is_last = idx >= len(input_dict) - 1 - end = '' if outest_level or is_last else ',' - if isinstance(v, dict): - v_str = '\n' + _format_dict(v) - if use_mapping: - k_str = f"'{k}'" if isinstance(k, str) else str(k) - attr_str = f'{k_str}: dict({v_str}' - else: - attr_str = f'{str(k)}=dict({v_str}' - attr_str = _indent(attr_str, indent) + ')' + end - elif isinstance(v, list): - attr_str = _format_list(k, v, use_mapping) + end - else: - attr_str = _format_basic_types(k, v, use_mapping) + end - - s.append(attr_str) - r += '\n'.join(s) - if use_mapping: - r += '}' - return r - - cfg_dict = self._cfg_dict.to_dict() - text = _format_dict(cfg_dict, outest_level=True) - # copied from setup.cfg - yapf_style = dict( - based_on_style='pep8', - blank_line_before_nested_class_or_def=True, - split_before_expression_after_opening_paren=True) - text, _ = FormatCode(text, style_config=yapf_style, verify=True) - - return text - - def __repr__(self): - return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}' - - def __len__(self): - return len(self._cfg_dict) - - def __getattr__(self, name): - return getattr(self._cfg_dict, name) - - def __getitem__(self, name): - return self._cfg_dict.__getitem__(name) - - def __setattr__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setattr__(name, value) - - def __setitem__(self, name, value): - if isinstance(value, dict): - value = ConfigDict(value) - self._cfg_dict.__setitem__(name, value) - - def __iter__(self): - return iter(self._cfg_dict) - - def __getstate__(self): - return (self._cfg_dict, self._filename, self._text) - - def __setstate__(self, state): - _cfg_dict, _filename, _text = state - super(Config, self).__setattr__('_cfg_dict', _cfg_dict) - super(Config, self).__setattr__('_filename', _filename) - super(Config, self).__setattr__('_text', _text) - - def dump(self, file=None): - cfg_dict = super(Config, self).__getattribute__('_cfg_dict').to_dict() - if self.filename.endswith('.py'): - if file is None: - return self.pretty_text - else: - with open(file, 'w', encoding='utf-8') as f: - f.write(self.pretty_text) - else: - import mmcv - if file is None: - file_format = self.filename.split('.')[-1] - return mmcv.dump(cfg_dict, file_format=file_format) - else: - mmcv.dump(cfg_dict, file) - - def merge_from_dict(self, options, allow_list_keys=True): - """Merge list into cfg_dict. - - Merge the dict parsed by MultipleKVAction into this cfg. - - Examples: - >>> options = {'model.backbone.depth': 50, - ... 'model.backbone.with_cp':True} - >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet')))) - >>> cfg.merge_from_dict(options) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict( - ... model=dict(backbone=dict(depth=50, with_cp=True))) - - # Merge list element - >>> cfg = Config(dict(pipeline=[ - ... dict(type='LoadImage'), dict(type='LoadAnnotations')])) - >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')}) - >>> cfg.merge_from_dict(options, allow_list_keys=True) - >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - >>> assert cfg_dict == dict(pipeline=[ - ... dict(type='SelfLoadImage'), dict(type='LoadAnnotations')]) - - Args: - options (dict): dict of configs to merge from. - allow_list_keys (bool): If True, int string keys (e.g. '0', '1') - are allowed in ``options`` and will replace the element of the - corresponding index in the config if the config is a list. - Default: True. - """ - option_cfg_dict = {} - for full_key, v in options.items(): - d = option_cfg_dict - key_list = full_key.split('.') - for subkey in key_list[:-1]: - d.setdefault(subkey, ConfigDict()) - d = d[subkey] - subkey = key_list[-1] - d[subkey] = v - - cfg_dict = super(Config, self).__getattribute__('_cfg_dict') - super(Config, self).__setattr__( - '_cfg_dict', - Config._merge_a_into_b( - option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys)) - - -class DictAction(Action): - """ - argparse action to split an argument into KEY=VALUE form - on the first = and append to a dictionary. List options can - be passed as comma separated values, i.e 'KEY=V1,V2,V3', or with explicit - brackets, i.e. 'KEY=[V1,V2,V3]'. It also support nested brackets to build - list/tuple values. e.g. 'KEY=[(V1,V2),(V3,V4)]' - """ - - @staticmethod - def _parse_int_float_bool(val): - try: - return int(val) - except ValueError: - pass - try: - return float(val) - except ValueError: - pass - if val.lower() in ['true', 'false']: - return True if val.lower() == 'true' else False - return val - - @staticmethod - def _parse_iterable(val): - """Parse iterable values in the string. - - All elements inside '()' or '[]' are treated as iterable values. - - Args: - val (str): Value string. - - Returns: - list | tuple: The expanded list or tuple from the string. - - Examples: - >>> DictAction._parse_iterable('1,2,3') - [1, 2, 3] - >>> DictAction._parse_iterable('[a, b, c]') - ['a', 'b', 'c'] - >>> DictAction._parse_iterable('[(1, 2, 3), [a, b], c]') - [(1, 2, 3), ['a', 'b'], 'c'] - """ - - def find_next_comma(string): - """Find the position of next comma in the string. - - If no ',' is found in the string, return the string length. All - chars inside '()' and '[]' are treated as one element and thus ',' - inside these brackets are ignored. - """ - assert (string.count('(') == string.count(')')) and ( - string.count('[') == string.count(']')), \ - f'Imbalanced brackets exist in {string}' - end = len(string) - for idx, char in enumerate(string): - pre = string[:idx] - # The string before this ',' is balanced - if ((char == ',') and (pre.count('(') == pre.count(')')) - and (pre.count('[') == pre.count(']'))): - end = idx - break - return end - - # Strip ' and " characters and replace whitespace. - val = val.strip('\'\"').replace(' ', '') - is_tuple = False - if val.startswith('(') and val.endswith(')'): - is_tuple = True - val = val[1:-1] - elif val.startswith('[') and val.endswith(']'): - val = val[1:-1] - elif ',' not in val: - # val is a single value - return DictAction._parse_int_float_bool(val) - - values = [] - while len(val) > 0: - comma_idx = find_next_comma(val) - element = DictAction._parse_iterable(val[:comma_idx]) - values.append(element) - val = val[comma_idx + 1:] - if is_tuple: - values = tuple(values) - return values - - def __call__(self, parser, namespace, values, option_string=None): - options = {} - for kv in values: - key, val = kv.split('=', maxsplit=1) - options[key] = self._parse_iterable(val) - setattr(namespace, self.dest, options) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/env.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/env.py deleted file mode 100755 index e46a1094f..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/env.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -"""This file holding some environment constant for sharing by other files.""" - -import os.path as osp -import subprocess -import sys -from collections import defaultdict - -import cv2 -import torch - -import mmcv -from .parrots_wrapper import get_build_config - - -def collect_env(): - """Collect the information of the running environments. - - Returns: - dict: The environment information. The following fields are contained. - - - sys.platform: The variable of ``sys.platform``. - - Python: Python version. - - CUDA available: Bool, indicating if CUDA is available. - - GPU devices: Device type of each GPU. - - CUDA_HOME (optional): The env var ``CUDA_HOME``. - - NVCC (optional): NVCC version. - - GCC: GCC version, "n/a" if GCC is not installed. - - PyTorch: PyTorch version. - - PyTorch compiling details: The output of \ - ``torch.__config__.show()``. - - TorchVision (optional): TorchVision version. - - OpenCV: OpenCV version. - - MMCV: MMCV version. - - MMCV Compiler: The GCC version for compiling MMCV ops. - - MMCV CUDA Compiler: The CUDA version for compiling MMCV ops. - """ - env_info = {} - env_info['sys.platform'] = sys.platform - env_info['Python'] = sys.version.replace('\n', '') - - cuda_available = torch.cuda.is_available() - env_info['CUDA available'] = cuda_available - - if cuda_available: - devices = defaultdict(list) - for k in range(torch.cuda.device_count()): - devices[torch.cuda.get_device_name(k)].append(str(k)) - for name, device_ids in devices.items(): - env_info['GPU ' + ','.join(device_ids)] = name - - from mmcv.utils.parrots_wrapper import _get_cuda_home - CUDA_HOME = _get_cuda_home() - env_info['CUDA_HOME'] = CUDA_HOME - - if CUDA_HOME is not None and osp.isdir(CUDA_HOME): - try: - nvcc = osp.join(CUDA_HOME, 'bin/nvcc') - nvcc = subprocess.check_output( - f'"{nvcc}" -V | tail -n1', shell=True) - nvcc = nvcc.decode('utf-8').strip() - except subprocess.SubprocessError: - nvcc = 'Not Available' - env_info['NVCC'] = nvcc - - try: - gcc = subprocess.check_output('gcc --version | head -n1', shell=True) - gcc = gcc.decode('utf-8').strip() - env_info['GCC'] = gcc - except subprocess.CalledProcessError: # gcc is unavailable - env_info['GCC'] = 'n/a' - - env_info['PyTorch'] = torch.__version__ - env_info['PyTorch compiling details'] = get_build_config() - - try: - import torchvision - env_info['TorchVision'] = torchvision.__version__ - except ModuleNotFoundError: - pass - - env_info['OpenCV'] = cv2.__version__ - - env_info['MMCV'] = mmcv.__version__ - - try: - from mmcv.ops import get_compiler_version, get_compiling_cuda_version - except ModuleNotFoundError: - env_info['MMCV Compiler'] = 'n/a' - env_info['MMCV CUDA Compiler'] = 'n/a' - else: - env_info['MMCV Compiler'] = get_compiler_version() - env_info['MMCV CUDA Compiler'] = get_compiling_cuda_version() - - return env_info diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/ext_loader.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/ext_loader.py deleted file mode 100755 index 08132d2c1..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/ext_loader.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os -import pkgutil -import warnings -from collections import namedtuple - -import torch - -if torch.__version__ != 'parrots': - - def load_ext(name, funcs): - ext = importlib.import_module('mmcv.' + name) - for fun in funcs: - assert hasattr(ext, fun), f'{fun} miss in module {name}' - return ext -else: - from parrots import extension - from parrots.base import ParrotsException - - has_return_value_ops = [ - 'nms', - 'softnms', - 'nms_match', - 'nms_rotated', - 'top_pool_forward', - 'top_pool_backward', - 'bottom_pool_forward', - 'bottom_pool_backward', - 'left_pool_forward', - 'left_pool_backward', - 'right_pool_forward', - 'right_pool_backward', - 'fused_bias_leakyrelu', - 'upfirdn2d', - 'ms_deform_attn_forward', - 'pixel_group', - 'contour_expand', - ] - - def get_fake_func(name, e): - - def fake_func(*args, **kwargs): - warnings.warn(f'{name} is not supported in parrots now') - raise e - - return fake_func - - def load_ext(name, funcs): - ExtModule = namedtuple('ExtModule', funcs) - ext_list = [] - lib_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - for fun in funcs: - try: - ext_fun = extension.load(fun, name, lib_dir=lib_root) - except ParrotsException as e: - if 'No element registered' not in e.message: - warnings.warn(e.message) - ext_fun = get_fake_func(fun, e) - ext_list.append(ext_fun) - else: - if fun in has_return_value_ops: - ext_list.append(ext_fun.op) - else: - ext_list.append(ext_fun.op_) - return ExtModule(*ext_list) - - -def check_ops_exist(): - ext_loader = pkgutil.find_loader('mmcv._ext') - return ext_loader is not None diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/logging.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/logging.py deleted file mode 100755 index 4aa0e04bb..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/logging.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import torch.distributed as dist - -logger_initialized = {} - - -def get_logger(name, log_file=None, log_level=logging.INFO, file_mode='w'): - """Initialize and get a logger by name. - - If the logger has not been initialized, this method will initialize the - logger by adding one or two handlers, otherwise the initialized logger will - be directly returned. During initialization, a StreamHandler will always be - added. If `log_file` is specified and the process rank is 0, a FileHandler - will also be added. - - Args: - name (str): Logger name. - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the logger. - log_level (int): The logger level. Note that only the process of - rank 0 is affected, and other processes will set the level to - "Error" thus be silent most of the time. - file_mode (str): The file mode used in opening log file. - Defaults to 'w'. - - Returns: - logging.Logger: The expected logger. - """ - logger = logging.getLogger(name) - if name in logger_initialized: - return logger - # handle hierarchical names - # e.g., logger "a" is initialized, then logger "a.b" will skip the - # initialization since it is a child of "a". - for logger_name in logger_initialized: - if name.startswith(logger_name): - return logger - - # handle duplicate logs to the console - # Starting in 1.8.0, PyTorch DDP attaches a StreamHandler (NOTSET) - # to the root logger. As logger.propagate is True by default, this root - # level handler causes logging messages from rank>0 processes to - # unexpectedly show up on the console, creating much unwanted clutter. - # To fix this issue, we set the root logger's StreamHandler, if any, to log - # at the ERROR level. - for handler in logger.root.handlers: - if type(handler) is logging.StreamHandler: - handler.setLevel(logging.ERROR) - - stream_handler = logging.StreamHandler() - handlers = [stream_handler] - - if dist.is_available() and dist.is_initialized(): - rank = dist.get_rank() - else: - rank = 0 - - # only rank 0 will add a FileHandler - if rank == 0 and log_file is not None: - # Here, the default behaviour of the official logger is 'a'. Thus, we - # provide an interface to change the file mode to the default - # behaviour. - file_handler = logging.FileHandler(log_file, file_mode) - handlers.append(file_handler) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - for handler in handlers: - handler.setFormatter(formatter) - handler.setLevel(log_level) - logger.addHandler(handler) - - if rank == 0: - logger.setLevel(log_level) - else: - logger.setLevel(logging.ERROR) - - logger_initialized[name] = True - - return logger - - -def print_log(msg, logger=None, level=logging.INFO): - """Print a log message. - - Args: - msg (str): The message to be logged. - logger (logging.Logger | str | None): The logger to be used. - Some special loggers are: - - "silent": no message will be printed. - - other str: the logger obtained with `get_root_logger(logger)`. - - None: The `print()` method will be used to print log messages. - level (int): Logging level. Only available when `logger` is a Logger - object or "root". - """ - if logger is None: - print(msg) - elif isinstance(logger, logging.Logger): - logger.log(level, msg) - elif logger == 'silent': - pass - elif isinstance(logger, str): - _logger = get_logger(logger) - _logger.log(level, msg) - else: - raise TypeError( - 'logger should be either a logging.Logger object, str, ' - f'"silent" or None, but got {type(logger)}') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/misc.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/misc.py deleted file mode 100755 index 2c58d0d7f..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/misc.py +++ /dev/null @@ -1,377 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import collections.abc -import functools -import itertools -import subprocess -import warnings -from collections import abc -from importlib import import_module -from inspect import getfullargspec -from itertools import repeat - - -# From PyTorch internals -def _ntuple(n): - - def parse(x): - if isinstance(x, collections.abc.Iterable): - return x - return tuple(repeat(x, n)) - - return parse - - -to_1tuple = _ntuple(1) -to_2tuple = _ntuple(2) -to_3tuple = _ntuple(3) -to_4tuple = _ntuple(4) -to_ntuple = _ntuple - - -def is_str(x): - """Whether the input is an string instance. - - Note: This method is deprecated since python 2 is no longer supported. - """ - return isinstance(x, str) - - -def import_modules_from_strings(imports, allow_failed_imports=False): - """Import modules from the given list of strings. - - Args: - imports (list | str | None): The given module names to be imported. - allow_failed_imports (bool): If True, the failed imports will return - None. Otherwise, an ImportError is raise. Default: False. - - Returns: - list[module] | module | None: The imported modules. - - Examples: - >>> osp, sys = import_modules_from_strings( - ... ['os.path', 'sys']) - >>> import os.path as osp_ - >>> import sys as sys_ - >>> assert osp == osp_ - >>> assert sys == sys_ - """ - if not imports: - return - single_import = False - if isinstance(imports, str): - single_import = True - imports = [imports] - if not isinstance(imports, list): - raise TypeError( - f'custom_imports must be a list but got type {type(imports)}') - imported = [] - for imp in imports: - if not isinstance(imp, str): - raise TypeError( - f'{imp} is of type {type(imp)} and cannot be imported.') - try: - imported_tmp = import_module(imp) - except ImportError: - if allow_failed_imports: - warnings.warn(f'{imp} failed to import and is ignored.', - UserWarning) - imported_tmp = None - else: - raise ImportError - imported.append(imported_tmp) - if single_import: - imported = imported[0] - return imported - - -def iter_cast(inputs, dst_type, return_type=None): - """Cast elements of an iterable object into some type. - - Args: - inputs (Iterable): The input object. - dst_type (type): Destination type. - return_type (type, optional): If specified, the output object will be - converted to this type, otherwise an iterator. - - Returns: - iterator or specified type: The converted object. - """ - if not isinstance(inputs, abc.Iterable): - raise TypeError('inputs must be an iterable object') - if not isinstance(dst_type, type): - raise TypeError('"dst_type" must be a valid type') - - out_iterable = map(dst_type, inputs) - - if return_type is None: - return out_iterable - else: - return return_type(out_iterable) - - -def list_cast(inputs, dst_type): - """Cast elements of an iterable object into a list of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=list) - - -def tuple_cast(inputs, dst_type): - """Cast elements of an iterable object into a tuple of some type. - - A partial method of :func:`iter_cast`. - """ - return iter_cast(inputs, dst_type, return_type=tuple) - - -def is_seq_of(seq, expected_type, seq_type=None): - """Check whether it is a sequence of some type. - - Args: - seq (Sequence): The sequence to be checked. - expected_type (type): Expected type of sequence items. - seq_type (type, optional): Expected sequence type. - - Returns: - bool: Whether the sequence is valid. - """ - if seq_type is None: - exp_seq_type = abc.Sequence - else: - assert isinstance(seq_type, type) - exp_seq_type = seq_type - if not isinstance(seq, exp_seq_type): - return False - for item in seq: - if not isinstance(item, expected_type): - return False - return True - - -def is_list_of(seq, expected_type): - """Check whether it is a list of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=list) - - -def is_tuple_of(seq, expected_type): - """Check whether it is a tuple of some type. - - A partial method of :func:`is_seq_of`. - """ - return is_seq_of(seq, expected_type, seq_type=tuple) - - -def slice_list(in_list, lens): - """Slice a list into several sub lists by a list of given length. - - Args: - in_list (list): The list to be sliced. - lens(int or list): The expected length of each out list. - - Returns: - list: A list of sliced list. - """ - if isinstance(lens, int): - assert len(in_list) % lens == 0 - lens = [lens] * int(len(in_list) / lens) - if not isinstance(lens, list): - raise TypeError('"indices" must be an integer or a list of integers') - elif sum(lens) != len(in_list): - raise ValueError('sum of lens and list length does not ' - f'match: {sum(lens)} != {len(in_list)}') - out_list = [] - idx = 0 - for i in range(len(lens)): - out_list.append(in_list[idx:idx + lens[i]]) - idx += lens[i] - return out_list - - -def concat_list(in_list): - """Concatenate a list of list into a single list. - - Args: - in_list (list): The list of list to be merged. - - Returns: - list: The concatenated flat list. - """ - return list(itertools.chain(*in_list)) - - -def check_prerequisites( - prerequisites, - checker, - msg_tmpl='Prerequisites "{}" are required in method "{}" but not ' - 'found, please install them first.'): # yapf: disable - """A decorator factory to check if prerequisites are satisfied. - - Args: - prerequisites (str of list[str]): Prerequisites to be checked. - checker (callable): The checker method that returns True if a - prerequisite is meet, False otherwise. - msg_tmpl (str): The message template with two variables. - - Returns: - decorator: A specific decorator. - """ - - def wrap(func): - - @functools.wraps(func) - def wrapped_func(*args, **kwargs): - requirements = [prerequisites] if isinstance( - prerequisites, str) else prerequisites - missing = [] - for item in requirements: - if not checker(item): - missing.append(item) - if missing: - print(msg_tmpl.format(', '.join(missing), func.__name__)) - raise RuntimeError('Prerequisites not meet.') - else: - return func(*args, **kwargs) - - return wrapped_func - - return wrap - - -def _check_py_package(package): - try: - import_module(package) - except ImportError: - return False - else: - return True - - -def _check_executable(cmd): - if subprocess.call(f'which {cmd}', shell=True) != 0: - return False - else: - return True - - -def requires_package(prerequisites): - """A decorator to check if some python packages are installed. - - Example: - >>> @requires_package('numpy') - >>> func(arg1, args): - >>> return numpy.zeros(1) - array([0.]) - >>> @requires_package(['numpy', 'non_package']) - >>> func(arg1, args): - >>> return numpy.zeros(1) - ImportError - """ - return check_prerequisites(prerequisites, checker=_check_py_package) - - -def requires_executable(prerequisites): - """A decorator to check if some executable files are installed. - - Example: - >>> @requires_executable('ffmpeg') - >>> func(arg1, args): - >>> print(1) - 1 - """ - return check_prerequisites(prerequisites, checker=_check_executable) - - -def deprecated_api_warning(name_dict, cls_name=None): - """A decorator to check if some arguments are deprecate and try to replace - deprecate src_arg_name to dst_arg_name. - - Args: - name_dict(dict): - key (str): Deprecate argument names. - val (str): Expected argument names. - - Returns: - func: New function. - """ - - def api_warning_wrapper(old_func): - - @functools.wraps(old_func) - def new_func(*args, **kwargs): - # get the arg spec of the decorated method - args_info = getfullargspec(old_func) - # get name of the function - func_name = old_func.__name__ - if cls_name is not None: - func_name = f'{cls_name}.{func_name}' - if args: - arg_names = args_info.args[:len(args)] - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in arg_names: - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead') - arg_names[arg_names.index(src_arg_name)] = dst_arg_name - if kwargs: - for src_arg_name, dst_arg_name in name_dict.items(): - if src_arg_name in kwargs: - - assert dst_arg_name not in kwargs, ( - f'The expected behavior is to replace ' - f'the deprecated key `{src_arg_name}` to ' - f'new key `{dst_arg_name}`, but got them ' - f'in the arguments at the same time, which ' - f'is confusing. `{src_arg_name} will be ' - f'deprecated in the future, please ' - f'use `{dst_arg_name}` instead.') - - warnings.warn( - f'"{src_arg_name}" is deprecated in ' - f'`{func_name}`, please use "{dst_arg_name}" ' - 'instead') - kwargs[dst_arg_name] = kwargs.pop(src_arg_name) - - # apply converted arguments to the decorated method - output = old_func(*args, **kwargs) - return output - - return new_func - - return api_warning_wrapper - - -def is_method_overridden(method, base_class, derived_class): - """Check if a method of base class is overridden in derived class. - - Args: - method (str): the method name to check. - base_class (type): the class of the base class. - derived_class (type | Any): the class or instance of the derived class. - """ - assert isinstance(base_class, type), \ - "base_class doesn't accept instance, Please pass class instead." - - if not isinstance(derived_class, type): - derived_class = derived_class.__class__ - - base_method = getattr(base_class, method) - derived_method = getattr(derived_class, method) - return derived_method != base_method - - -def has_method(obj: object, method: str) -> bool: - """Check whether the object has a method. - - Args: - method (str): The method name to check. - obj (object): The object to check. - - Returns: - bool: True if the object has the method else False. - """ - return hasattr(obj, method) and callable(getattr(obj, method)) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/parrots_jit.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/parrots_jit.py deleted file mode 100755 index 61873f6db..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/parrots_jit.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os - -from .parrots_wrapper import TORCH_VERSION - -parrots_jit_option = os.getenv('PARROTS_JIT_OPTION') - -if TORCH_VERSION == 'parrots' and parrots_jit_option == 'ON': - from parrots.jit import pat as jit -else: - - def jit(func=None, - check_input=None, - full_shape=True, - derivate=False, - coderize=False, - optimize=False): - - def wrapper(func): - - def wrapper_inner(*args, **kargs): - return func(*args, **kargs) - - return wrapper_inner - - if func is None: - return wrapper - else: - return func - - -if TORCH_VERSION == 'parrots': - from parrots.utils.tester import skip_no_elena -else: - - def skip_no_elena(func): - - def wrapper(*args, **kargs): - return func(*args, **kargs) - - return wrapper diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/parrots_wrapper.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/parrots_wrapper.py deleted file mode 100755 index 93c97640d..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/parrots_wrapper.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch - -TORCH_VERSION = torch.__version__ - - -def is_rocm_pytorch() -> bool: - is_rocm = False - if TORCH_VERSION != 'parrots': - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - return is_rocm - - -def _get_cuda_home(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import CUDA_HOME - else: - if is_rocm_pytorch(): - from torch.utils.cpp_extension import ROCM_HOME - CUDA_HOME = ROCM_HOME - else: - from torch.utils.cpp_extension import CUDA_HOME - return CUDA_HOME - - -def get_build_config(): - if TORCH_VERSION == 'parrots': - from parrots.config import get_build_info - return get_build_info() - else: - return torch.__config__.show() - - -def _get_conv(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.conv import _ConvNd, _ConvTransposeMixin - else: - from torch.nn.modules.conv import _ConvNd, _ConvTransposeMixin - return _ConvNd, _ConvTransposeMixin - - -def _get_dataloader(): - if TORCH_VERSION == 'parrots': - from torch.utils.data import DataLoader, PoolDataLoader - else: - from torch.utils.data import DataLoader - PoolDataLoader = DataLoader - return DataLoader, PoolDataLoader - - -def _get_extension(): - if TORCH_VERSION == 'parrots': - from parrots.utils.build_extension import BuildExtension, Extension - CppExtension = partial(Extension, cuda=False) - CUDAExtension = partial(Extension, cuda=True) - else: - from torch.utils.cpp_extension import (BuildExtension, CppExtension, - CUDAExtension) - return BuildExtension, CppExtension, CUDAExtension - - -def _get_pool(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.pool import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - else: - from torch.nn.modules.pooling import (_AdaptiveAvgPoolNd, - _AdaptiveMaxPoolNd, _AvgPoolNd, - _MaxPoolNd) - return _AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd - - -def _get_norm(): - if TORCH_VERSION == 'parrots': - from parrots.nn.modules.batchnorm import _BatchNorm, _InstanceNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm2d - else: - from torch.nn.modules.instancenorm import _InstanceNorm - from torch.nn.modules.batchnorm import _BatchNorm - SyncBatchNorm_ = torch.nn.SyncBatchNorm - return _BatchNorm, _InstanceNorm, SyncBatchNorm_ - - -_ConvNd, _ConvTransposeMixin = _get_conv() -DataLoader, PoolDataLoader = _get_dataloader() -BuildExtension, CppExtension, CUDAExtension = _get_extension() -_BatchNorm, _InstanceNorm, SyncBatchNorm_ = _get_norm() -_AdaptiveAvgPoolNd, _AdaptiveMaxPoolNd, _AvgPoolNd, _MaxPoolNd = _get_pool() - - -class SyncBatchNorm(SyncBatchNorm_): - - def _check_input_dim(self, input): - if TORCH_VERSION == 'parrots': - if input.dim() < 2: - raise ValueError( - f'expected at least 2D input (got {input.dim()}D input)') - else: - super()._check_input_dim(input) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/path.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/path.py deleted file mode 100755 index 7dab4b304..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/path.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -from pathlib import Path - -from .misc import is_str - - -def is_filepath(x): - return is_str(x) or isinstance(x, Path) - - -def fopen(filepath, *args, **kwargs): - if is_str(filepath): - return open(filepath, *args, **kwargs) - elif isinstance(filepath, Path): - return filepath.open(*args, **kwargs) - raise ValueError('`filepath` should be a string or a Path') - - -def check_file_exist(filename, msg_tmpl='file "{}" does not exist'): - if not osp.isfile(filename): - raise FileNotFoundError(msg_tmpl.format(filename)) - - -def mkdir_or_exist(dir_name, mode=0o777): - if dir_name == '': - return - dir_name = osp.expanduser(dir_name) - os.makedirs(dir_name, mode=mode, exist_ok=True) - - -def symlink(src, dst, overwrite=True, **kwargs): - if os.path.lexists(dst) and overwrite: - os.remove(dst) - os.symlink(src, dst, **kwargs) - - -def scandir(dir_path, suffix=None, recursive=False, case_sensitive=True): - """Scan a directory to find the interested files. - - Args: - dir_path (str | obj:`Path`): Path of the directory. - suffix (str | tuple(str), optional): File suffix that we are - interested in. Default: None. - recursive (bool, optional): If set to True, recursively scan the - directory. Default: False. - case_sensitive (bool, optional) : If set to False, ignore the case of - suffix. Default: True. - - Returns: - A generator for all the interested files with relative paths. - """ - if isinstance(dir_path, (str, Path)): - dir_path = str(dir_path) - else: - raise TypeError('"dir_path" must be a string or Path object') - - if (suffix is not None) and not isinstance(suffix, (str, tuple)): - raise TypeError('"suffix" must be a string or tuple of strings') - - if suffix is not None and not case_sensitive: - suffix = suffix.lower() if isinstance(suffix, str) else tuple( - item.lower() for item in suffix) - - root = dir_path - - def _scandir(dir_path, suffix, recursive, case_sensitive): - for entry in os.scandir(dir_path): - if not entry.name.startswith('.') and entry.is_file(): - rel_path = osp.relpath(entry.path, root) - _rel_path = rel_path if case_sensitive else rel_path.lower() - if suffix is None or _rel_path.endswith(suffix): - yield rel_path - elif recursive and os.path.isdir(entry.path): - # scan recursively if entry.path is a directory - yield from _scandir(entry.path, suffix, recursive, - case_sensitive) - - return _scandir(dir_path, suffix, recursive, case_sensitive) - - -def find_vcs_root(path, markers=('.git', )): - """Finds the root directory (including itself) of specified markers. - - Args: - path (str): Path of directory or file. - markers (list[str], optional): List of file or directory names. - - Returns: - The directory contained one of the markers or None if not found. - """ - if osp.isfile(path): - path = osp.dirname(path) - - prev, cur = None, osp.abspath(osp.expanduser(path)) - while cur != prev: - if any(osp.exists(osp.join(cur, marker)) for marker in markers): - return cur - prev, cur = cur, osp.split(cur)[0] - return None diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/progressbar.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/progressbar.py deleted file mode 100755 index 0062f670d..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/progressbar.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import sys -from collections.abc import Iterable -from multiprocessing import Pool -from shutil import get_terminal_size - -from .timer import Timer - - -class ProgressBar: - """A progress bar which can print the progress.""" - - def __init__(self, task_num=0, bar_width=50, start=True, file=sys.stdout): - self.task_num = task_num - self.bar_width = bar_width - self.completed = 0 - self.file = file - if start: - self.start() - - @property - def terminal_width(self): - width, _ = get_terminal_size() - return width - - def start(self): - if self.task_num > 0: - self.file.write(f'[{" " * self.bar_width}] 0/{self.task_num}, ' - 'elapsed: 0s, ETA:') - else: - self.file.write('completed: 0, elapsed: 0s') - self.file.flush() - self.timer = Timer() - - def update(self, num_tasks=1): - assert num_tasks > 0 - self.completed += num_tasks - elapsed = self.timer.since_start() - if elapsed > 0: - fps = self.completed / elapsed - else: - fps = float('inf') - if self.task_num > 0: - percentage = self.completed / float(self.task_num) - eta = int(elapsed * (1 - percentage) / percentage + 0.5) - msg = f'\r[{{}}] {self.completed}/{self.task_num}, ' \ - f'{fps:.1f} task/s, elapsed: {int(elapsed + 0.5)}s, ' \ - f'ETA: {eta:5}s' - - bar_width = min(self.bar_width, - int(self.terminal_width - len(msg)) + 2, - int(self.terminal_width * 0.6)) - bar_width = max(2, bar_width) - mark_width = int(bar_width * percentage) - bar_chars = '>' * mark_width + ' ' * (bar_width - mark_width) - self.file.write(msg.format(bar_chars)) - else: - self.file.write( - f'completed: {self.completed}, elapsed: {int(elapsed + 0.5)}s,' - f' {fps:.1f} tasks/s') - self.file.flush() - - -def track_progress(func, tasks, bar_width=50, file=sys.stdout, **kwargs): - """Track the progress of tasks execution with a progress bar. - - Tasks are done with a simple for-loop. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - results = [] - for task in tasks: - results.append(func(task, **kwargs)) - prog_bar.update() - prog_bar.file.write('\n') - return results - - -def init_pool(process_num, initializer=None, initargs=None): - if initializer is None: - return Pool(process_num) - elif initargs is None: - return Pool(process_num, initializer) - else: - if not isinstance(initargs, tuple): - raise TypeError('"initargs" must be a tuple') - return Pool(process_num, initializer, initargs) - - -def track_parallel_progress(func, - tasks, - nproc, - initializer=None, - initargs=None, - bar_width=50, - chunksize=1, - skip_first=False, - keep_order=True, - file=sys.stdout): - """Track the progress of parallel task execution with a progress bar. - - The built-in :mod:`multiprocessing` module is used for process pools and - tasks are done with :func:`Pool.map` or :func:`Pool.imap_unordered`. - - Args: - func (callable): The function to be applied to each task. - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - nproc (int): Process (worker) number. - initializer (None or callable): Refer to :class:`multiprocessing.Pool` - for details. - initargs (None or tuple): Refer to :class:`multiprocessing.Pool` for - details. - chunksize (int): Refer to :class:`multiprocessing.Pool` for details. - bar_width (int): Width of progress bar. - skip_first (bool): Whether to skip the first sample for each worker - when estimating fps, since the initialization step may takes - longer. - keep_order (bool): If True, :func:`Pool.imap` is used, otherwise - :func:`Pool.imap_unordered` is used. - - Returns: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - pool = init_pool(nproc, initializer, initargs) - start = not skip_first - task_num -= nproc * chunksize * int(skip_first) - prog_bar = ProgressBar(task_num, bar_width, start, file=file) - results = [] - if keep_order: - gen = pool.imap(func, tasks, chunksize) - else: - gen = pool.imap_unordered(func, tasks, chunksize) - for result in gen: - results.append(result) - if skip_first: - if len(results) < nproc * chunksize: - continue - elif len(results) == nproc * chunksize: - prog_bar.start() - continue - prog_bar.update() - prog_bar.file.write('\n') - pool.close() - pool.join() - return results - - -def track_iter_progress(tasks, bar_width=50, file=sys.stdout): - """Track the progress of tasks iteration or enumeration with a progress - bar. - - Tasks are yielded with a simple for-loop. - - Args: - tasks (list or tuple[Iterable, int]): A list of tasks or - (tasks, total num). - bar_width (int): Width of progress bar. - - Yields: - list: The task results. - """ - if isinstance(tasks, tuple): - assert len(tasks) == 2 - assert isinstance(tasks[0], Iterable) - assert isinstance(tasks[1], int) - task_num = tasks[1] - tasks = tasks[0] - elif isinstance(tasks, Iterable): - task_num = len(tasks) - else: - raise TypeError( - '"tasks" must be an iterable object or a (iterator, int) tuple') - prog_bar = ProgressBar(task_num, bar_width, file=file) - for task in tasks: - yield task - prog_bar.update() - prog_bar.file.write('\n') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/registry.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/registry.py deleted file mode 100755 index fa9df39bc..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/registry.py +++ /dev/null @@ -1,315 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import inspect -import warnings -from functools import partial - -from .misc import is_seq_of - - -def build_from_cfg(cfg, registry, default_args=None): - """Build a module from config dict. - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - registry (:obj:`Registry`): The registry to search the type from. - default_args (dict, optional): Default initialization arguments. - - Returns: - object: The constructed object. - """ - if not isinstance(cfg, dict): - raise TypeError(f'cfg must be a dict, but got {type(cfg)}') - if 'type' not in cfg: - if default_args is None or 'type' not in default_args: - raise KeyError( - '`cfg` or `default_args` must contain the key "type", ' - f'but got {cfg}\n{default_args}') - if not isinstance(registry, Registry): - raise TypeError('registry must be an mmcv.Registry object, ' - f'but got {type(registry)}') - if not (isinstance(default_args, dict) or default_args is None): - raise TypeError('default_args must be a dict or None, ' - f'but got {type(default_args)}') - - args = cfg.copy() - - if default_args is not None: - for name, value in default_args.items(): - args.setdefault(name, value) - - obj_type = args.pop('type') - if isinstance(obj_type, str): - obj_cls = registry.get(obj_type) - if obj_cls is None: - raise KeyError( - f'{obj_type} is not in the {registry.name} registry') - elif inspect.isclass(obj_type): - obj_cls = obj_type - else: - raise TypeError( - f'type must be a str or valid type, but got {type(obj_type)}') - try: - return obj_cls(**args) - except Exception as e: - # Normal TypeError does not print class name. - raise type(e)(f'{obj_cls.__name__}: {e}') - - -class Registry: - """A registry to map strings to classes. - - Registered object could be built from registry. - Example: - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - >>> resnet = MODELS.build(dict(type='ResNet')) - - Please refer to - https://mmcv.readthedocs.io/en/latest/understand_mmcv/registry.html for - advanced usage. - - Args: - name (str): Registry name. - build_func(func, optional): Build function to construct instance from - Registry, func:`build_from_cfg` is used if neither ``parent`` or - ``build_func`` is specified. If ``parent`` is specified and - ``build_func`` is not given, ``build_func`` will be inherited - from ``parent``. Default: None. - parent (Registry, optional): Parent registry. The class registered in - children registry could be built from parent. Default: None. - scope (str, optional): The scope of registry. It is the key to search - for children registry. If not specified, scope will be the name of - the package where class is defined, e.g. mmdet, mmcls, mmseg. - Default: None. - """ - - def __init__(self, name, build_func=None, parent=None, scope=None): - self._name = name - self._module_dict = dict() - self._children = dict() - self._scope = self.infer_scope() if scope is None else scope - - # self.build_func will be set with the following priority: - # 1. build_func - # 2. parent.build_func - # 3. build_from_cfg - if build_func is None: - if parent is not None: - self.build_func = parent.build_func - else: - self.build_func = build_from_cfg - else: - self.build_func = build_func - if parent is not None: - assert isinstance(parent, Registry) - parent._add_children(self) - self.parent = parent - else: - self.parent = None - - def __len__(self): - return len(self._module_dict) - - def __contains__(self, key): - return self.get(key) is not None - - def __repr__(self): - format_str = self.__class__.__name__ + \ - f'(name={self._name}, ' \ - f'items={self._module_dict})' - return format_str - - @staticmethod - def infer_scope(): - """Infer the scope of registry. - - The name of the package where registry is defined will be returned. - - Example: - # in mmdet/models/backbone/resnet.py - >>> MODELS = Registry('models') - >>> @MODELS.register_module() - >>> class ResNet: - >>> pass - The scope of ``ResNet`` will be ``mmdet``. - - - Returns: - scope (str): The inferred scope name. - """ - # inspect.stack() trace where this function is called, the index-2 - # indicates the frame where `infer_scope()` is called - filename = inspect.getmodule(inspect.stack()[2][0]).__name__ - split_filename = filename.split('.') - return split_filename[0] - - @staticmethod - def split_scope_key(key): - """Split scope and key. - - The first scope will be split from key. - - Examples: - >>> Registry.split_scope_key('mmdet.ResNet') - 'mmdet', 'ResNet' - >>> Registry.split_scope_key('ResNet') - None, 'ResNet' - - Return: - scope (str, None): The first scope. - key (str): The remaining key. - """ - split_index = key.find('.') - if split_index != -1: - return key[:split_index], key[split_index + 1:] - else: - return None, key - - @property - def name(self): - return self._name - - @property - def scope(self): - return self._scope - - @property - def module_dict(self): - return self._module_dict - - @property - def children(self): - return self._children - - def get(self, key): - """Get the registry record. - - Args: - key (str): The class name in string format. - - Returns: - class: The corresponding class. - """ - scope, real_key = self.split_scope_key(key) - if scope is None or scope == self._scope: - # get from self - if real_key in self._module_dict: - return self._module_dict[real_key] - else: - # get from self._children - if scope in self._children: - return self._children[scope].get(real_key) - else: - # goto root - parent = self.parent - while parent.parent is not None: - parent = parent.parent - return parent.get(key) - - def build(self, *args, **kwargs): - return self.build_func(*args, **kwargs, registry=self) - - def _add_children(self, registry): - """Add children for a registry. - - The ``registry`` will be added as children based on its scope. - The parent registry could build objects from children registry. - - Example: - >>> models = Registry('models') - >>> mmdet_models = Registry('models', parent=models) - >>> @mmdet_models.register_module() - >>> class ResNet: - >>> pass - >>> resnet = models.build(dict(type='mmdet.ResNet')) - """ - - assert isinstance(registry, Registry) - assert registry.scope is not None - assert registry.scope not in self.children, \ - f'scope {registry.scope} exists in {self.name} registry' - self.children[registry.scope] = registry - - def _register_module(self, module_class, module_name=None, force=False): - if not inspect.isclass(module_class): - raise TypeError('module must be a class, ' - f'but got {type(module_class)}') - - if module_name is None: - module_name = module_class.__name__ - if isinstance(module_name, str): - module_name = [module_name] - for name in module_name: - if not force and name in self._module_dict: - raise KeyError(f'{name} is already registered ' - f'in {self.name}') - self._module_dict[name] = module_class - - def deprecated_register_module(self, cls=None, force=False): - warnings.warn( - 'The old API of register_module(module, force=False) ' - 'is deprecated and will be removed, please use the new API ' - 'register_module(name=None, force=False, module=None) instead.') - if cls is None: - return partial(self.deprecated_register_module, force=force) - self._register_module(cls, force=force) - return cls - - def register_module(self, name=None, force=False, module=None): - """Register a module. - - A record will be added to `self._module_dict`, whose key is the class - name or the specified name, and value is the class itself. - It can be used as a decorator or a normal function. - - Example: - >>> backbones = Registry('backbone') - >>> @backbones.register_module() - >>> class ResNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> @backbones.register_module(name='mnet') - >>> class MobileNet: - >>> pass - - >>> backbones = Registry('backbone') - >>> class ResNet: - >>> pass - >>> backbones.register_module(ResNet) - - Args: - name (str | None): The module name to be registered. If not - specified, the class name will be used. - force (bool, optional): Whether to override an existing class with - the same name. Default: False. - module (type): Module class to be registered. - """ - if not isinstance(force, bool): - raise TypeError(f'force must be a boolean, but got {type(force)}') - # NOTE: This is a walkaround to be compatible with the old api, - # while it may introduce unexpected bugs. - if isinstance(name, type): - return self.deprecated_register_module(name, force=force) - - # raise the error ahead of time - if not (name is None or isinstance(name, str) or is_seq_of(name, str)): - raise TypeError( - 'name must be either of None, an instance of str or a sequence' - f' of str, but got {type(name)}') - - # use it as a normal method: x.register_module(module=SomeClass) - if module is not None: - self._register_module( - module_class=module, module_name=name, force=force) - return module - - # use it as a decorator: @x.register_module() - def _register(cls): - self._register_module( - module_class=cls, module_name=name, force=force) - return cls - - return _register diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/testing.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/testing.py deleted file mode 100755 index a27f936da..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/testing.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) Open-MMLab. -import sys -from collections.abc import Iterable -from runpy import run_path -from shlex import split -from typing import Any, Dict, List -from unittest.mock import patch - - -def check_python_script(cmd): - """Run the python cmd script with `__main__`. The difference between - `os.system` is that, this function exectues code in the current process, so - that it can be tracked by coverage tools. Currently it supports two forms: - - - ./tests/data/scripts/hello.py zz - - python tests/data/scripts/hello.py zz - """ - args = split(cmd) - if args[0] == 'python': - args = args[1:] - with patch.object(sys, 'argv', args): - run_path(args[0], run_name='__main__') - - -def _any(judge_result): - """Since built-in ``any`` works only when the element of iterable is not - iterable, implement the function.""" - if not isinstance(judge_result, Iterable): - return judge_result - - try: - for element in judge_result: - if _any(element): - return True - except TypeError: - # Maybe encounter the case: torch.tensor(True) | torch.tensor(False) - if judge_result: - return True - return False - - -def assert_dict_contains_subset(dict_obj: Dict[Any, Any], - expected_subset: Dict[Any, Any]) -> bool: - """Check if the dict_obj contains the expected_subset. - - Args: - dict_obj (Dict[Any, Any]): Dict object to be checked. - expected_subset (Dict[Any, Any]): Subset expected to be contained in - dict_obj. - - Returns: - bool: Whether the dict_obj contains the expected_subset. - """ - - for key, value in expected_subset.items(): - if key not in dict_obj.keys() or _any(dict_obj[key] != value): - return False - return True - - -def assert_attrs_equal(obj: Any, expected_attrs: Dict[str, Any]) -> bool: - """Check if attribute of class object is correct. - - Args: - obj (object): Class object to be checked. - expected_attrs (Dict[str, Any]): Dict of the expected attrs. - - Returns: - bool: Whether the attribute of class object is correct. - """ - for attr, value in expected_attrs.items(): - if not hasattr(obj, attr) or _any(getattr(obj, attr) != value): - return False - return True - - -def assert_dict_has_keys(obj: Dict[str, Any], - expected_keys: List[str]) -> bool: - """Check if the obj has all the expected_keys. - - Args: - obj (Dict[str, Any]): Object to be checked. - expected_keys (List[str]): Keys expected to contained in the keys of - the obj. - - Returns: - bool: Whether the obj has the expected keys. - """ - return set(expected_keys).issubset(set(obj.keys())) - - -def assert_keys_equal(result_keys: List[str], target_keys: List[str]) -> bool: - """Check if target_keys is equal to result_keys. - - Args: - result_keys (List[str]): Result keys to be checked. - target_keys (List[str]): Target keys to be checked. - - Returns: - bool: Whether target_keys is equal to result_keys. - """ - return set(result_keys) == set(target_keys) - - -def assert_is_norm_layer(module) -> bool: - """Check if the module is a norm layer. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the module is a norm layer. - """ - from .parrots_wrapper import _BatchNorm, _InstanceNorm - from torch.nn import GroupNorm, LayerNorm - norm_layer_candidates = (_BatchNorm, _InstanceNorm, GroupNorm, LayerNorm) - return isinstance(module, norm_layer_candidates) - - -def assert_params_all_zeros(module) -> bool: - """Check if the parameters of the module is all zeros. - - Args: - module (nn.Module): The module to be checked. - - Returns: - bool: Whether the parameters of the module is all zeros. - """ - weight_data = module.weight.data - is_weight_zero = weight_data.allclose( - weight_data.new_zeros(weight_data.size())) - - if hasattr(module, 'bias') and module.bias is not None: - bias_data = module.bias.data - is_bias_zero = bias_data.allclose( - bias_data.new_zeros(bias_data.size())) - else: - is_bias_zero = True - - return is_weight_zero and is_bias_zero diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/timer.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/timer.py deleted file mode 100755 index 66d4a78a8..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/timer.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from time import time - - -class TimerError(Exception): - - def __init__(self, message): - self.message = message - super(TimerError, self).__init__(message) - - -class Timer: - """A flexible Timer class. - - :Example: - - >>> import time - >>> import mmcv - >>> with mmcv.Timer(): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - 1.000 - >>> with mmcv.Timer(print_tmpl='it takes {:.1f} seconds'): - >>> # simulate a code block that will run for 1s - >>> time.sleep(1) - it takes 1.0 seconds - >>> timer = mmcv.Timer() - >>> time.sleep(0.5) - >>> print(timer.since_start()) - 0.500 - >>> time.sleep(0.5) - >>> print(timer.since_last_check()) - 0.500 - >>> print(timer.since_start()) - 1.000 - """ - - def __init__(self, start=True, print_tmpl=None): - self._is_running = False - self.print_tmpl = print_tmpl if print_tmpl else '{:.3f}' - if start: - self.start() - - @property - def is_running(self): - """bool: indicate whether the timer is running""" - return self._is_running - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - print(self.print_tmpl.format(self.since_last_check())) - self._is_running = False - - def start(self): - """Start the timer.""" - if not self._is_running: - self._t_start = time() - self._is_running = True - self._t_last = time() - - def since_start(self): - """Total time since the timer is started. - - Returns (float): Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - self._t_last = time() - return self._t_last - self._t_start - - def since_last_check(self): - """Time since the last checking. - - Either :func:`since_start` or :func:`since_last_check` is a checking - operation. - - Returns (float): Time in seconds. - """ - if not self._is_running: - raise TimerError('timer is not running') - dur = time() - self._t_last - self._t_last = time() - return dur - - -_g_timers = {} # global timers - - -def check_time(timer_id): - """Add check points in a single line. - - This method is suitable for running a task on a list of items. A timer will - be registered when the method is called for the first time. - - :Example: - - >>> import time - >>> import mmcv - >>> for i in range(1, 6): - >>> # simulate a code block - >>> time.sleep(i) - >>> mmcv.check_time('task1') - 2.000 - 3.000 - 4.000 - 5.000 - - Args: - timer_id (str): Timer identifier. - """ - if timer_id not in _g_timers: - _g_timers[timer_id] = Timer() - return 0 - else: - return _g_timers[timer_id].since_last_check() diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/trace.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/trace.py deleted file mode 100755 index 8e49bfd38..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/trace.py +++ /dev/null @@ -1,23 +0,0 @@ -import warnings - -import torch - -from mmcv.utils import digit_version - - -def is_jit_tracing() -> bool: - if (torch.__version__ != 'parrots' - and digit_version(torch.__version__) >= digit_version('1.6.0')): - on_trace = torch.jit.is_tracing() - # In PyTorch 1.6, torch.jit.is_tracing has a bug. - # Refers to https://github.com/pytorch/pytorch/issues/42448 - if isinstance(on_trace, bool): - return on_trace - else: - return torch._C._is_tracing() - else: - warnings.warn( - 'torch.jit.is_tracing is only supported after v1.6.0. ' - 'Therefore is_tracing returns False automatically. Please ' - 'set on_trace manually if you are using trace.', UserWarning) - return False diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/version_utils.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/version_utils.py deleted file mode 100755 index 963c45a2e..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/utils/version_utils.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import subprocess -import warnings - -from packaging.version import parse - - -def digit_version(version_str: str, length: int = 4): - """Convert a version string into a tuple of integers. - - This method is usually used for comparing two versions. For pre-release - versions: alpha < beta < rc. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int]: The version info in digits (integers). - """ - assert 'parrots' not in version_str - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - mapping = {'a': -3, 'b': -2, 'rc': -1} - val = -4 - # version.pre can be None - if version.pre: - if version.pre[0] not in mapping: - warnings.warn(f'unknown prerelease version {version.pre[0]}, ' - 'version checking may go wrong') - else: - val = mapping[version.pre[0]] - release.extend([val, version.pre[-1]]) - else: - release.extend([val, 0]) - - elif version.is_postrelease: - release.extend([1, version.post]) - else: - release.extend([0, 0]) - return tuple(release) - - -def _minimal_ext_cmd(cmd): - # construct minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: - v = os.environ.get(k) - if v is not None: - env[k] = v - # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] - return out - - -def get_git_hash(fallback='unknown', digits=None): - """Get the git hash of the current repo. - - Args: - fallback (str, optional): The fallback string when git hash is - unavailable. Defaults to 'unknown'. - digits (int, optional): kept digits of the hash. Defaults to None, - meaning all digits are kept. - - Returns: - str: Git commit hash. - """ - - if digits is not None and not isinstance(digits, int): - raise TypeError('digits must be None or an integer') - - try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - sha = out.strip().decode('ascii') - if digits is not None: - sha = sha[:digits] - except OSError: - sha = fallback - - return sha diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmcv/version.py b/cv/super_resolution/real_basicvsr/pytorch/mmcv/version.py deleted file mode 100755 index 1cce4e50b..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmcv/version.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -__version__ = '1.3.17' - - -def parse_version_info(version_str: str, length: int = 4) -> tuple: - """Parse a version string into a tuple. - - Args: - version_str (str): The version string. - length (int): The maximum number of version levels. Default: 4. - - Returns: - tuple[int | str]: The version info, e.g., "1.3.0" is parsed into - (1, 3, 0, 0, 0, 0), and "2.0.0rc1" is parsed into - (2, 0, 0, 0, 'rc', 1) (when length is set to 4). - """ - from packaging.version import parse - version = parse(version_str) - assert version.release, f'failed to parse version {version_str}' - release = list(version.release) - release = release[:length] - if len(release) < length: - release = release + [0] * (length - len(release)) - if version.is_prerelease: - release.extend(list(version.pre)) - elif version.is_postrelease: - release.extend(list(version.post)) - else: - release.extend([0, 0]) - return tuple(release) - - -version_info = tuple(int(x) for x in __version__.split('.')[:3]) - -__all__ = ['__version__', 'version_info', 'parse_version_info'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/__init__.py deleted file mode 100755 index 05d4c20b7..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv - -from .version import __version__, version_info - -try: - from mmcv.utils import digit_version -except ImportError: - - def digit_version(version_str): - digit_ver = [] - for x in version_str.split('.'): - if x.isdigit(): - digit_ver.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - digit_ver.append(int(patch_version[0]) - 1) - digit_ver.append(int(patch_version[1])) - return digit_ver - - -MMCV_MIN = '1.3.13' -MMCV_MAX = '1.6' - -mmcv_min_version = digit_version(MMCV_MIN) -mmcv_max_version = digit_version(MMCV_MAX) -mmcv_version = digit_version(mmcv.__version__) - - -assert (mmcv_min_version <= mmcv_version <= mmcv_max_version), \ - f'mmcv=={mmcv.__version__} is used but incompatible. ' \ - f'Please install mmcv-full>={mmcv_min_version}, <={mmcv_max_version}.' - -__all__ = ['__version__', 'version_info'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/__init__.py deleted file mode 100755 index 1ae70035e..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .generation_inference import generation_inference -from .inpainting_inference import inpainting_inference -from .matting_inference import init_model, matting_inference -from .restoration_face_inference import restoration_face_inference -from .restoration_inference import restoration_inference -from .restoration_video_inference import restoration_video_inference -from .test import multi_gpu_test, single_gpu_test -from .train import init_random_seed, set_random_seed, train_model -from .video_interpolation_inference import video_interpolation_inference - -__all__ = [ - 'train_model', 'set_random_seed', 'init_model', 'matting_inference', - 'inpainting_inference', 'restoration_inference', 'generation_inference', - 'multi_gpu_test', 'single_gpu_test', 'restoration_video_inference', - 'restoration_face_inference', 'video_interpolation_inference', - 'init_random_seed' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/generation_inference.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/generation_inference.py deleted file mode 100755 index c8e829bb1..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/generation_inference.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -from mmcv.parallel import collate, scatter - -from mmedit.core import tensor2img -from mmedit.datasets.pipelines import Compose - - -def generation_inference(model, img, img_unpaired=None): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img (str): File path of input image. - img_unpaired (str, optional): File path of the unpaired image. - If not None, perform unpaired image generation. Default: None. - - Returns: - np.ndarray: The predicted generation result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # build the data pipeline - test_pipeline = Compose(cfg.test_pipeline) - # prepare data - if img_unpaired is None: - data = dict(pair_path=img) - else: - data = dict(img_a_path=img, img_b_path=img_unpaired) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - # forward the model - with torch.no_grad(): - results = model(test_mode=True, **data) - # process generation shown mode - if img_unpaired is None: - if model.show_input: - output = np.concatenate([ - tensor2img(results['real_a'], min_max=(-1, 1)), - tensor2img(results['fake_b'], min_max=(-1, 1)), - tensor2img(results['real_b'], min_max=(-1, 1)) - ], - axis=1) - else: - output = tensor2img(results['fake_b'], min_max=(-1, 1)) - else: - if model.show_input: - output = np.concatenate([ - tensor2img(results['real_a'], min_max=(-1, 1)), - tensor2img(results['fake_b'], min_max=(-1, 1)), - tensor2img(results['real_b'], min_max=(-1, 1)), - tensor2img(results['fake_a'], min_max=(-1, 1)) - ], - axis=1) - else: - if model.test_direction == 'a2b': - output = tensor2img(results['fake_b'], min_max=(-1, 1)) - else: - output = tensor2img(results['fake_a'], min_max=(-1, 1)) - return output diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/inpainting_inference.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/inpainting_inference.py deleted file mode 100755 index f80650b74..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/inpainting_inference.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.parallel import collate, scatter - -from mmedit.datasets.pipelines import Compose - - -def inpainting_inference(model, masked_img, mask): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - masked_img (str): File path of image with mask. - mask (str): Mask file path. - - Returns: - Tensor: The predicted inpainting result. - """ - device = next(model.parameters()).device # model device - - infer_pipeline = [ - dict(type='LoadImageFromFile', key='masked_img'), - dict(type='LoadMask', mask_mode='file', mask_config=dict()), - dict(type='Pad', keys=['masked_img', 'mask'], mode='reflect'), - dict( - type='Normalize', - keys=['masked_img'], - mean=[127.5] * 3, - std=[127.5] * 3, - to_rgb=False), - dict(type='GetMaskedImage', img_name='masked_img'), - dict( - type='Collect', - keys=['masked_img', 'mask'], - meta_keys=['masked_img_path']), - dict(type='ImageToTensor', keys=['masked_img', 'mask']) - ] - - # build the data pipeline - test_pipeline = Compose(infer_pipeline) - # prepare data - data = dict(masked_img_path=masked_img, mask_path=mask) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - else: - data.pop('meta') - # forward the model - with torch.no_grad(): - result = model(test_mode=True, **data) - - return result['fake_img'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/matting_inference.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/matting_inference.py deleted file mode 100755 index 5446afe73..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/matting_inference.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import torch -from mmcv.parallel import collate, scatter -from mmcv.runner import load_checkpoint - -from mmedit.datasets.pipelines import Compose -from mmedit.models import build_model - - -def init_model(config, checkpoint=None, device='cuda:0'): - """Initialize a model from config file. - - Args: - config (str or :obj:`mmcv.Config`): Config file path or the config - object. - checkpoint (str, optional): Checkpoint path. If left as None, the model - will not load any weights. - device (str): Which device the model will deploy. Default: 'cuda:0'. - - Returns: - nn.Module: The constructed model. - """ - if isinstance(config, str): - config = mmcv.Config.fromfile(config) - elif not isinstance(config, mmcv.Config): - raise TypeError('config must be a filename or Config object, ' - f'but got {type(config)}') - config.model.pretrained = None - config.test_cfg.metrics = None - model = build_model(config.model, test_cfg=config.test_cfg) - if checkpoint is not None: - checkpoint = load_checkpoint(model, checkpoint) - - model.cfg = config # save the config in the model for convenience - model.to(device) - model.eval() - return model - - -def matting_inference(model, img, trimap): - """Inference image(s) with the model. - - Args: - model (nn.Module): The loaded model. - img (str): Image file path. - trimap (str): Trimap file path. - - Returns: - np.ndarray: The predicted alpha matte. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # remove alpha from test_pipeline - keys_to_remove = ['alpha', 'ori_alpha'] - for key in keys_to_remove: - for pipeline in list(cfg.test_pipeline): - if 'key' in pipeline and key == pipeline['key']: - cfg.test_pipeline.remove(pipeline) - if 'keys' in pipeline and key in pipeline['keys']: - pipeline['keys'].remove(key) - if len(pipeline['keys']) == 0: - cfg.test_pipeline.remove(pipeline) - if 'meta_keys' in pipeline and key in pipeline['meta_keys']: - pipeline['meta_keys'].remove(key) - # build the data pipeline - test_pipeline = Compose(cfg.test_pipeline) - # prepare data - data = dict(merged_path=img, trimap_path=trimap) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - # forward the model - with torch.no_grad(): - result = model(test_mode=True, **data) - - return result['pred_alpha'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/restoration_face_inference.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/restoration_face_inference.py deleted file mode 100755 index dbe12e11a..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/restoration_face_inference.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -from mmcv.parallel import collate, scatter - -from mmedit.datasets.pipelines import Compose - -try: - from facexlib.utils.face_restoration_helper import FaceRestoreHelper - has_facexlib = True -except ImportError: - has_facexlib = False - - -def restoration_face_inference(model, img, upscale_factor=1, face_size=1024): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img (str): File path of input image. - upscale_factor (int, optional): The number of times the input image - is upsampled. Default: 1. - face_size (int, optional): The size of the cropped and aligned faces. - Default: 1024. - - Returns: - Tensor: The predicted restoration result. - """ - device = next(model.parameters()).device # model device - - # build the data pipeline - if model.cfg.get('demo_pipeline', None): - test_pipeline = model.cfg.demo_pipeline - elif model.cfg.get('test_pipeline', None): - test_pipeline = model.cfg.test_pipeline - else: - test_pipeline = model.cfg.val_pipeline - - # remove gt from test_pipeline - keys_to_remove = ['gt', 'gt_path'] - for key in keys_to_remove: - for pipeline in list(test_pipeline): - if 'key' in pipeline and key == pipeline['key']: - test_pipeline.remove(pipeline) - if 'keys' in pipeline and key in pipeline['keys']: - pipeline['keys'].remove(key) - if len(pipeline['keys']) == 0: - test_pipeline.remove(pipeline) - if 'meta_keys' in pipeline and key in pipeline['meta_keys']: - pipeline['meta_keys'].remove(key) - # build the data pipeline - test_pipeline = Compose(test_pipeline) - - # face helper for detecting and aligning faces - assert has_facexlib, 'Please install FaceXLib to use the demo.' - face_helper = FaceRestoreHelper( - upscale_factor, - face_size=face_size, - crop_ratio=(1, 1), - det_model='retinaface_resnet50', - template_3points=True, - save_ext='png', - device=device) - - face_helper.read_image(img) - # get face landmarks for each face - face_helper.get_face_landmarks_5( - only_center_face=False, eye_dist_threshold=None) - # align and warp each face - face_helper.align_warp_face() - - for i, img in enumerate(face_helper.cropped_faces): - # prepare data - data = dict(lq=img.astype(np.float32)) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - - with torch.no_grad(): - output = model(test_mode=True, **data)['output'].clip_(0, 1) - - output = output.squeeze(0).permute(1, 2, 0)[:, :, [2, 1, 0]] - output = output.cpu().numpy() * 255 # (0, 255) - face_helper.add_restored_face(output) - - face_helper.get_inverse_affine(None) - restored_img = face_helper.paste_faces_to_input_image(upsample_img=None) - - return restored_img diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/restoration_inference.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/restoration_inference.py deleted file mode 100755 index 98c08abc8..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/restoration_inference.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.parallel import collate, scatter - -from mmedit.datasets.pipelines import Compose - - -def restoration_inference(model, img, ref=None): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img (str): File path of input image. - ref (str | None): File path of reference image. Default: None. - - Returns: - Tensor: The predicted restoration result. - """ - cfg = model.cfg - device = next(model.parameters()).device # model device - # remove gt from test_pipeline - keys_to_remove = ['gt', 'gt_path'] - for key in keys_to_remove: - for pipeline in list(cfg.test_pipeline): - if 'key' in pipeline and key == pipeline['key']: - cfg.test_pipeline.remove(pipeline) - if 'keys' in pipeline and key in pipeline['keys']: - pipeline['keys'].remove(key) - if len(pipeline['keys']) == 0: - cfg.test_pipeline.remove(pipeline) - if 'meta_keys' in pipeline and key in pipeline['meta_keys']: - pipeline['meta_keys'].remove(key) - # build the data pipeline - test_pipeline = Compose(cfg.test_pipeline) - # prepare data - if ref: # Ref-SR - data = dict(lq_path=img, ref_path=ref) - else: # SISR - data = dict(lq_path=img) - data = test_pipeline(data) - data = collate([data], samples_per_gpu=1) - if 'cuda' in str(device): - data = scatter(data, [device])[0] - # forward the model - with torch.no_grad(): - result = model(test_mode=True, **data) - - return result['output'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/restoration_video_inference.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/restoration_video_inference.py deleted file mode 100755 index ca6369dfc..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/restoration_video_inference.py +++ /dev/null @@ -1,129 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os.path as osp -import re -from functools import reduce - -import mmcv -import numpy as np -import torch - -from mmedit.datasets.pipelines import Compose - -VIDEO_EXTENSIONS = ('.mp4', '.mov') - - -def pad_sequence(data, window_size): - padding = window_size // 2 - - data = torch.cat([ - data[:, 1 + padding:1 + 2 * padding].flip(1), data, - data[:, -1 - 2 * padding:-1 - padding].flip(1) - ], - dim=1) - - return data - - -def restoration_video_inference(model, - img_dir, - window_size, - start_idx, - filename_tmpl, - max_seq_len=None): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - img_dir (str): Directory of the input video. - window_size (int): The window size used in sliding-window framework. - This value should be set according to the settings of the network. - A value smaller than 0 means using recurrent framework. - start_idx (int): The index corresponds to the first frame in the - sequence. - filename_tmpl (str): Template for file name. - max_seq_len (int | None): The maximum sequence length that the model - processes. If the sequence length is larger than this number, - the sequence is split into multiple segments. If it is None, - the entire sequence is processed at once. - - Returns: - Tensor: The predicted restoration result. - """ - - device = next(model.parameters()).device # model device - - # build the data pipeline - if model.cfg.get('demo_pipeline', None): - test_pipeline = model.cfg.demo_pipeline - elif model.cfg.get('test_pipeline', None): - test_pipeline = model.cfg.test_pipeline - else: - test_pipeline = model.cfg.val_pipeline - - # check if the input is a video - file_extension = osp.splitext(img_dir)[1] - if file_extension in VIDEO_EXTENSIONS: - video_reader = mmcv.VideoReader(img_dir) - # load the images - data = dict(lq=[], lq_path=None, key=img_dir) - for frame in video_reader: - data['lq'].append(np.flip(frame, axis=2)) - - # remove the data loading pipeline - tmp_pipeline = [] - for pipeline in test_pipeline: - if pipeline['type'] not in [ - 'GenerateSegmentIndices', 'LoadImageFromFileList' - ]: - tmp_pipeline.append(pipeline) - test_pipeline = tmp_pipeline - else: - # the first element in the pipeline must be 'GenerateSegmentIndices' - if test_pipeline[0]['type'] != 'GenerateSegmentIndices': - raise TypeError('The first element in the pipeline must be ' - f'"GenerateSegmentIndices", but got ' - f'"{test_pipeline[0]["type"]}".') - - # specify start_idx and filename_tmpl - test_pipeline[0]['start_idx'] = start_idx - test_pipeline[0]['filename_tmpl'] = filename_tmpl - - # prepare data - sequence_length = len(glob.glob(osp.join(img_dir, '*'))) - img_dir_split = re.split(r'[\\/]', img_dir) - key = img_dir_split[-1] - lq_folder = reduce(osp.join, img_dir_split[:-1]) - data = dict( - lq_path=lq_folder, - gt_path='', - key=key, - sequence_length=sequence_length) - - # compose the pipeline - test_pipeline = Compose(test_pipeline) - data = test_pipeline(data) - data = data['lq'].unsqueeze(0) # in cpu - - # forward the model - with torch.no_grad(): - if window_size > 0: # sliding window framework - data = pad_sequence(data, window_size) - result = [] - for i in range(0, data.size(1) - 2 * (window_size // 2)): - data_i = data[:, i:i + window_size].to(device) - result.append(model(lq=data_i, test_mode=True)['output'].cpu()) - result = torch.stack(result, dim=1) - else: # recurrent framework - if max_seq_len is None: - result = model( - lq=data.to(device), test_mode=True)['output'].cpu() - else: - result = [] - for i in range(0, data.size(1), max_seq_len): - result.append( - model( - lq=data[:, i:i + max_seq_len].to(device), - test_mode=True)['output'].cpu()) - result = torch.cat(result, dim=1) - return result diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/test.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/test.py deleted file mode 100755 index 535511eee..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/test.py +++ /dev/null @@ -1,234 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import pickle -import shutil -import tempfile - -import mmcv -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def single_gpu_test(model, - data_loader, - save_image=False, - save_path=None, - iteration=None): - """Test model with a single gpu. - - This method tests model with a single gpu and displays test progress bar. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - save_image (bool): Whether save image. Default: False. - save_path (str): The path to save image. Default: None. - iteration (int): Iteration number. It is used for the save image name. - Default: None. - - Returns: - list: The prediction results. - """ - if save_image and save_path is None: - raise ValueError( - "When 'save_image' is True, you should also set 'save_path'.") - - model.eval() - results = [] - dataset = data_loader.dataset - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model( - test_mode=True, - save_image=save_image, - save_path=save_path, - iteration=iteration, - **data) - results.append(result) - - # get batch size - for _, v in data.items(): - if isinstance(v, torch.Tensor): - batch_size = v.size(0) - break - for _ in range(batch_size): - prog_bar.update() - return results - - -def multi_gpu_test(model, - data_loader, - tmpdir=None, - gpu_collect=False, - save_image=False, - save_path=None, - iteration=None, - empty_cache=False): - """Test model with multiple gpus. - - This method tests model with multiple gpus and collects the results - under two different modes: gpu and cpu modes. By setting 'gpu_collect=True' - it encodes results to gpu tensors and use gpu communication for results - collection. On cpu mode it saves the results on different gpus to 'tmpdir' - and collects them by the rank 0 worker. - - Args: - model (nn.Module): Model to be tested. - data_loader (nn.Dataloader): Pytorch data loader. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. - gpu_collect (bool): Option to use either gpu or cpu to collect results. - save_image (bool): Whether save image. Default: False. - save_path (str): The path to save image. Default: None. - iteration (int): Iteration number. It is used for the save image name. - Default: None. - empty_cache (bool): empty cache in every iteration. Default: False. - - Returns: - list: The prediction results. - """ - - if save_image and save_path is None: - raise ValueError( - "When 'save_image' is True, you should also set 'save_path'.") - model.eval() - results = [] - dataset = data_loader.dataset - rank, world_size = get_dist_info() - if rank == 0: - prog_bar = mmcv.ProgressBar(len(dataset)) - for data in data_loader: - with torch.no_grad(): - result = model( - test_mode=True, - save_image=save_image, - save_path=save_path, - iteration=iteration, - **data) - results.append(result) - if empty_cache: - torch.cuda.empty_cache() - if rank == 0: - # get batch size - for _, v in data.items(): - if isinstance(v, torch.Tensor): - batch_size = v.size(0) - break - for _ in range(batch_size * world_size): - prog_bar.update() - # collect results from all ranks - if gpu_collect: - results = collect_results_gpu(results, len(dataset)) - else: - results = collect_results_cpu(results, len(dataset), tmpdir) - return results - - -def collect_results_cpu(result_part, size, tmpdir=None): - """Collect results in cpu mode. - - It saves the results on different gpus to 'tmpdir' and collects - them by the rank 0 worker. - - Args: - result_part (list): Results to be collected - size (int): Result size. - tmpdir (str): Path of directory to save the temporary results from - different gpus under cpu mode. Default: None - - Returns: - list: Ordered results. - """ - - rank, world_size = get_dist_info() - # create a tmp dir if it is not specified - if tmpdir is None: - MAX_LEN = 512 - # 32 is whitespace - dir_tensor = torch.full((MAX_LEN, ), - 32, - dtype=torch.uint8, - device='cuda') - if rank == 0: - mmcv.mkdir_or_exist('.dist_test') - tmpdir = tempfile.mkdtemp(dir='.dist_test') - tmpdir = torch.tensor( - bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda') - dir_tensor[:len(tmpdir)] = tmpdir - dist.broadcast(dir_tensor, 0) - tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip() - else: - mmcv.mkdir_or_exist(tmpdir) - # synchronizes all processes to make sure tmpdir exist - dist.barrier() - # dump the part result to the dir - mmcv.dump(result_part, osp.join(tmpdir, 'part_{}.pkl'.format(rank))) - # synchronizes all processes for loading pickle file - dist.barrier() - # collect all parts - if rank != 0: - return None - - # load results of all parts from tmp dir - part_list = [] - for i in range(world_size): - part_file = osp.join(tmpdir, 'part_{}.pkl'.format(i)) - part_list.append(mmcv.load(part_file)) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - # remove tmp dir - shutil.rmtree(tmpdir) - return ordered_results - - -def collect_results_gpu(result_part, size): - """Collect results in gpu mode. - - It encodes results to gpu tensors and use gpu communication for results - collection. - - Args: - result_part (list): Results to be collected - size (int): Result size. - - Returns: - list: Ordered results. - """ - - rank, world_size = get_dist_info() - # dump result part to tensor with pickle - part_tensor = torch.tensor( - bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda') - # gather all result part tensor shape - shape_tensor = torch.tensor(part_tensor.shape, device='cuda') - shape_list = [shape_tensor.clone() for _ in range(world_size)] - dist.all_gather(shape_list, shape_tensor) - # padding result part tensor to max length - shape_max = torch.tensor(shape_list).max() - part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda') - part_send[:shape_tensor[0]] = part_tensor - part_recv_list = [ - part_tensor.new_zeros(shape_max) for _ in range(world_size) - ] - # gather all result part - dist.all_gather(part_recv_list, part_send) - - if rank != 0: - return None - - part_list = [] - for recv, shape in zip(part_recv_list, shape_list): - part_list.append(pickle.loads(recv[:shape[0]].cpu().numpy().tobytes())) - # sort the results - ordered_results = [] - for res in zip(*part_list): - ordered_results.extend(list(res)) - # the dataloader may pad some samples - ordered_results = ordered_results[:size] - return ordered_results diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/train.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/train.py deleted file mode 100755 index 359943ca8..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/train.py +++ /dev/null @@ -1,361 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import os.path as osp -import random -import warnings - -import mmcv -import numpy as np -import torch -import torch.distributed as dist -from mmcv.parallel import MMDataParallel -from mmcv.runner import HOOKS, IterBasedRunner, get_dist_info -from mmcv.utils import build_from_cfg - -from mmedit.core import DistEvalIterHook, EvalIterHook, build_optimizers -from mmedit.core.distributed_wrapper import DistributedDataParallelWrapper -from mmedit.datasets.builder import build_dataloader, build_dataset -from mmedit.utils import get_root_logger - - -def init_random_seed(seed=None, device='cuda'): - """Initialize random seed. - If the seed is not set, the seed will be automatically randomized, - and then broadcast to all processes to prevent some potential bugs. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is not None: - return seed - - # Make sure all ranks share the same random seed to prevent - # some potential bugs. Please refer to - # https://github.com/open-mmlab/mmdetection/issues/6339 - rank, world_size = get_dist_info() - seed = np.random.randint(2**31) - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() - - -def set_random_seed(seed, deterministic=False): - """Set random seed. - - Args: - seed (int): Seed to be used. - deterministic (bool): Whether to set the deterministic option for - CUDNN backend, i.e., set `torch.backends.cudnn.deterministic` - to True and `torch.backends.cudnn.benchmark` to False. - Default: False. - """ - random.seed(seed) - np.random.seed(seed) - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - torch.cuda.manual_seed_all(seed) - os.environ['PYTHONHASHSEED'] = str(seed) - if deterministic: - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - - -def train_model(model, - dataset, - cfg, - distributed=False, - validate=False, - timestamp=None, - meta=None): - """Train model entry function. - - Args: - model (nn.Module): The model to be trained. - dataset (:obj:`Dataset`): Train dataset. - cfg (dict): The config dict for training. - distributed (bool): Whether to use distributed training. - Default: False. - validate (bool): Whether to do evaluation. Default: False. - timestamp (str | None): Local time for runner. Default: None. - meta (dict | None): Meta dict to record some important information. - Default: None - """ - logger = get_root_logger(log_level=cfg.log_level) - - # start training - if distributed: - _dist_train( - model, - dataset, - cfg, - validate=validate, - logger=logger, - timestamp=timestamp, - meta=meta) - else: - _non_dist_train( - model, - dataset, - cfg, - validate=validate, - logger=logger, - timestamp=timestamp, - meta=meta) - - -def _dist_train(model, - dataset, - cfg, - validate=False, - logger=None, - timestamp=None, - meta=None): - """Distributed training function. - - Args: - model (nn.Module): The model to be trained. - dataset (:obj:`Dataset`): Train dataset. - cfg (dict): The config dict for training. - validate (bool): Whether to do evaluation. Default: False. - logger (logging.Logger | None): Logger for training. Default: None. - timestamp (str | None): Local time for runner. Default: None. - meta (dict | None): Meta dict to record some important information. - Default: None. - """ - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - - # step 1: give default values and override (if exist) from cfg.data - loader_cfg = { - **dict(seed=cfg.get('seed'), drop_last=False, dist=True), - **({} if torch.__version__ != 'parrots' else dict( - prefetch_num=2, - pin_memory=False, - )), - **dict((k, cfg.data[k]) for k in [ - 'samples_per_gpu', - 'workers_per_gpu', - 'shuffle', - 'seed', - 'drop_last', - 'prefetch_num', - 'pin_memory', - ] if k in cfg.data) - } - - # step 2: cfg.data.train_dataloader has highest priority - train_loader_cfg = dict(loader_cfg, **cfg.data.get('train_dataloader', {})) - - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus - find_unused_parameters = cfg.get('find_unused_parameters', False) - model = DistributedDataParallelWrapper( - model, - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - - # build runner - optimizer = build_optimizers(model, cfg.optimizers) - runner = IterBasedRunner( - model, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta) - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register hooks - runner.register_training_hooks( - cfg.lr_config, - checkpoint_config=cfg.checkpoint_config, - log_config=cfg.log_config) - - # visual hook - if cfg.get('visual_config', None) is not None: - cfg.visual_config['output_dir'] = os.path.join( - cfg.work_dir, cfg.visual_config['output_dir']) - runner.register_hook(mmcv.build_from_cfg(cfg.visual_config, HOOKS)) - - # evaluation hook - if validate and cfg.get('evaluation', None) is not None: - dataset = build_dataset(cfg.data.val) - - if ('val_samples_per_gpu' in cfg.data - or 'val_workers_per_gpu' in cfg.data): - warnings.warn('"val_samples_per_gpu/val_workers_per_gpu" have ' - 'been deprecated. Please use ' - '"val_dataloader=dict(samples_per_gpu=1)" instead. ' - 'Details see ' - 'https://github.com/open-mmlab/mmediting/pull/201') - - val_loader_cfg = { - **loader_cfg, - **dict(shuffle=False, drop_last=False), - **dict((newk, cfg.data[oldk]) for oldk, newk in [ - ('val_samples_per_gpu', 'samples_per_gpu'), - ('val_workers_per_gpu', 'workers_per_gpu'), - ] if oldk in cfg.data), - **cfg.data.get('val_dataloader', {}) - } - - data_loader = build_dataloader(dataset, **val_loader_cfg) - save_path = osp.join(cfg.work_dir, 'val_visuals') - runner.register_hook( - DistEvalIterHook( - data_loader, save_path=save_path, **cfg.evaluation), - priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow, cfg.total_iters) - - -def _non_dist_train(model, - dataset, - cfg, - validate=False, - logger=None, - timestamp=None, - meta=None): - """Non-Distributed training function. - - Args: - model (nn.Module): The model to be trained. - dataset (:obj:`Dataset`): Train dataset. - cfg (dict): The config dict for training. - validate (bool): Whether to do evaluation. Default: False. - logger (logging.Logger | None): Logger for training. Default: None. - timestamp (str | None): Local time for runner. Default: None. - meta (dict | None): Meta dict to record some important information. - Default: None. - """ - dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset] - - # step 1: give default values and override (if exist) from cfg.data - loader_cfg = { - **dict( - seed=cfg.get('seed'), - drop_last=False, - dist=False, - num_gpus=cfg.gpus), - **({} if torch.__version__ != 'parrots' else dict( - prefetch_num=2, - pin_memory=False, - )), - **dict((k, cfg.data[k]) for k in [ - 'samples_per_gpu', - 'workers_per_gpu', - 'shuffle', - 'seed', - 'drop_last', - 'prefetch_num', - 'pin_memory', - ] if k in cfg.data) - } - - # step 2: cfg.data.train_dataloader has highest priority - train_loader_cfg = dict(loader_cfg, **cfg.data.get('train_dataloader', {})) - - data_loaders = [build_dataloader(ds, **train_loader_cfg) for ds in dataset] - - # put model on gpus/cpus - model = MMDataParallel(model, device_ids=range(cfg.gpus)) - - # build runner - optimizer = build_optimizers(model, cfg.optimizers) - runner = IterBasedRunner( - model, - optimizer=optimizer, - work_dir=cfg.work_dir, - logger=logger, - meta=meta) - - # an ugly walkaround to make the .log and .log.json filenames the same - runner.timestamp = timestamp - - # register hooks - runner.register_training_hooks( - cfg.lr_config, - checkpoint_config=cfg.checkpoint_config, - log_config=cfg.log_config) - - # visual hook - if cfg.get('visual_config', None) is not None: - cfg.visual_config['output_dir'] = os.path.join( - cfg.work_dir, cfg.visual_config['output_dir']) - runner.register_hook(mmcv.build_from_cfg(cfg.visual_config, HOOKS)) - - # evaluation hook - if validate and cfg.get('evaluation', None) is not None: - dataset = build_dataset(cfg.data.val) - - if ('val_samples_per_gpu' in cfg.data - or 'val_workers_per_gpu' in cfg.data): - warnings.warn('"val_samples_per_gpu/val_workers_per_gpu" have ' - 'been deprecated. Please use ' - '"val_dataloader=dict(samples_per_gpu=1)" instead. ' - 'Details see ' - 'https://github.com/open-mmlab/mmediting/pull/201') - - val_loader_cfg = { - **loader_cfg, - **dict(shuffle=False, drop_last=False), - **dict((newk, cfg.data[oldk]) for oldk, newk in [ - ('val_samples_per_gpu', 'samples_per_gpu'), - ('val_workers_per_gpu', 'workers_per_gpu'), - ] if oldk in cfg.data), - **cfg.data.get('val_dataloader', {}) - } - - data_loader = build_dataloader(dataset, **val_loader_cfg) - save_path = osp.join(cfg.work_dir, 'val_visuals') - runner.register_hook( - EvalIterHook(data_loader, save_path=save_path, **cfg.evaluation), - priority='LOW') - - # user-defined hooks - if cfg.get('custom_hooks', None): - custom_hooks = cfg.custom_hooks - assert isinstance(custom_hooks, list), \ - f'custom_hooks expect list type, but got {type(custom_hooks)}' - for hook_cfg in cfg.custom_hooks: - assert isinstance(hook_cfg, dict), \ - 'Each item in custom_hooks expects dict type, but got ' \ - f'{type(hook_cfg)}' - hook_cfg = hook_cfg.copy() - priority = hook_cfg.pop('priority', 'NORMAL') - hook = build_from_cfg(hook_cfg, HOOKS) - runner.register_hook(hook, priority=priority) - - if cfg.resume_from: - runner.resume(cfg.resume_from) - elif cfg.load_from: - runner.load_checkpoint(cfg.load_from) - runner.run(data_loaders, cfg.workflow, cfg.total_iters) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/video_interpolation_inference.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/video_interpolation_inference.py deleted file mode 100755 index eaabb4308..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/apis/video_interpolation_inference.py +++ /dev/null @@ -1,204 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -import os -import os.path as osp - -import cv2 -import mmcv -import numpy as np -import torch -from mmcv.fileio import FileClient -from mmcv.parallel import collate - -from mmedit.datasets.pipelines import Compose - -VIDEO_EXTENSIONS = ('.mp4', '.mov', '.avi') -FILE_CLIENT = FileClient('disk') - - -def read_image(filepath): - """Read image from file. - - Args: - filepath (str): File path. - - Returns: - image (np.array): Image. - """ - img_bytes = FILE_CLIENT.get(filepath) - image = mmcv.imfrombytes( - img_bytes, flag='color', channel_order='rgb', backend='pillow') - return image - - -def read_frames(source, start_index, num_frames, from_video, end_index): - """Read frames from file or video. - - Args: - source (list | mmcv.VideoReader): Source of frames. - start_index (int): Start index of frames. - num_frames (int): frames number to be read. - from_video (bool): Weather read frames from video. - end_index (int): The end index of frames. - - Returns: - images (np.array): Images. - """ - images = [] - last_index = min(start_index + num_frames, end_index) - # read frames from video - if from_video: - for index in range(start_index, last_index): - if index >= source.frame_cnt: - break - images.append(np.flip(source.get_frame(index), axis=2)) - else: - files = source[start_index:last_index] - images = [read_image(f) for f in files] - return images - - -def video_interpolation_inference(model, - input_dir, - output_dir, - start_idx=0, - end_idx=None, - batch_size=4, - fps_multiplier=0, - fps=0, - filename_tmpl='{:08d}.png'): - """Inference image with the model. - - Args: - model (nn.Module): The loaded model. - input_dir (str): Directory of the input video. - output_dir (str): Directory of the output video. - start_idx (int): The index corresponding to the first frame in the - sequence. Default: 0 - end_idx (int | None): The index corresponding to the last interpolated - frame in the sequence. If it is None, interpolate to the last - frame of video or sequence. Default: None - batch_size (int): Batch size. Default: 4 - fps_multiplier (float): multiply the fps based on the input video. - Default: 0. - fps (float): frame rate of the output video. Default: 0. - filename_tmpl (str): template of the file names. Default: '{:08d}.png' - - Returns: - output (list[numpy.array]): The predicted interpolation result. - It is an image sequence. - input_fps (float): The fps of input video. If the input is an image - sequence, input_fps=0.0 - """ - - device = next(model.parameters()).device # model device - - # build the data pipeline - if model.cfg.get('demo_pipeline', None): - test_pipeline = model.cfg.demo_pipeline - elif model.cfg.get('test_pipeline', None): - test_pipeline = model.cfg.test_pipeline - else: - test_pipeline = model.cfg.val_pipeline - - # remove the data loading pipeline - tmp_pipeline = [] - for pipeline in test_pipeline: - if pipeline['type'] not in [ - 'GenerateSegmentIndices', 'LoadImageFromFileList', - 'LoadImageFromFile' - ]: - tmp_pipeline.append(pipeline) - test_pipeline = tmp_pipeline - - # compose the pipeline - test_pipeline = Compose(test_pipeline) - - # check if the input is a video - input_file_extension = os.path.splitext(input_dir)[1] - if input_file_extension in VIDEO_EXTENSIONS: - source = mmcv.VideoReader(input_dir) - input_fps = source.fps - length = source.frame_cnt - from_video = True - h, w = source.height, source.width - if fps_multiplier: - assert fps_multiplier > 0, '`fps_multiplier` cannot be negative' - output_fps = fps_multiplier * input_fps - else: - output_fps = fps if fps > 0 else input_fps * 2 - else: - files = os.listdir(input_dir) - files = [osp.join(input_dir, f) for f in files] - files.sort() - source = files - length = files.__len__() - from_video = False - example_frame = read_image(files[0]) - h, w = example_frame.shape[:2] - output_fps = fps - - # check if the output is a video - output_file_extension = os.path.splitext(output_dir)[1] - if output_file_extension in VIDEO_EXTENSIONS: - fourcc = cv2.VideoWriter_fourcc(*'mp4v') - target = cv2.VideoWriter(output_dir, fourcc, output_fps, (w, h)) - to_video = True - else: - to_video = False - - end_idx = min(end_idx, length) if end_idx is not None else length - - # calculate step args - step_size = model.step_frames * batch_size - lenth_per_step = model.required_frames + model.step_frames * ( - batch_size - 1) - repeat_frame = model.required_frames - model.step_frames - - prog_bar = mmcv.ProgressBar( - math.ceil( - (end_idx + step_size - lenth_per_step - start_idx) / step_size)) - output_index = start_idx - for start_index in range(start_idx, end_idx, step_size): - images = read_frames( - source, start_index, lenth_per_step, from_video, end_index=end_idx) - - # data prepare - data = dict(inputs=images, inputs_path=None, key=input_dir) - data = [test_pipeline(data)] - data = collate(data, samples_per_gpu=1)['inputs'] - # data.shape: [1, t, c, h, w] - - # forward the model - data = model.split_frames(data) - input_tensors = data.clone().detach() - with torch.no_grad(): - output = model(data.to(device), test_mode=True)['output'] - if len(output.shape) == 4: - output = output.unsqueeze(1) - output_tensors = output.cpu() - if len(output_tensors.shape) == 4: - output_tensors = output_tensors.unsqueeze(1) - result = model.merge_frames(input_tensors, output_tensors) - if not start_idx == start_index: - result = result[repeat_frame:] - prog_bar.update() - - # save frames - if to_video: - for frame in result: - target.write(frame) - else: - for frame in result: - save_path = osp.join(output_dir, - filename_tmpl.format(output_index)) - mmcv.imwrite(frame, save_path) - output_index += 1 - - if start_index + lenth_per_step >= end_idx: - break - - print() - print(f'Output dir: {output_dir}') - if to_video: - target.release() diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/__init__.py deleted file mode 100755 index 2b24ce348..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .evaluation import (DistEvalIterHook, EvalIterHook, L1Evaluation, mae, - mse, psnr, reorder_image, sad, ssim) -from .hooks import VisualizationHook -from .misc import tensor2img -from .optimizer import build_optimizers -from .scheduler import LinearLrUpdaterHook, ReduceLrUpdaterHook - -__all__ = [ - 'build_optimizers', 'tensor2img', 'EvalIterHook', 'DistEvalIterHook', - 'mse', 'psnr', 'reorder_image', 'sad', 'ssim', 'LinearLrUpdaterHook', - 'VisualizationHook', 'L1Evaluation', 'ReduceLrUpdaterHook', 'mae' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/distributed_wrapper.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/distributed_wrapper.py deleted file mode 100755 index 660f41f3f..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/distributed_wrapper.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.parallel import MODULE_WRAPPERS, MMDistributedDataParallel -from mmcv.parallel.scatter_gather import scatter_kwargs -from torch.cuda._utils import _get_device_index - - -@MODULE_WRAPPERS.register_module() -class DistributedDataParallelWrapper(nn.Module): - """A DistributedDataParallel wrapper for models in MMediting. - - In MMedting, there is a need to wrap different modules in the models - with separate DistributedDataParallel. Otherwise, it will cause - errors for GAN training. - More specific, the GAN model, usually has two sub-modules: - generator and discriminator. If we wrap both of them in one - standard DistributedDataParallel, it will cause errors during training, - because when we update the parameters of the generator (or discriminator), - the parameters of the discriminator (or generator) is not updated, which is - not allowed for DistributedDataParallel. - So we design this wrapper to separately wrap DistributedDataParallel - for generator and discriminator. - - In this wrapper, we perform two operations: - 1. Wrap the modules in the models with separate MMDistributedDataParallel. - Note that only modules with parameters will be wrapped. - 2. Do scatter operation for 'forward', 'train_step' and 'val_step'. - - Note that the arguments of this wrapper is the same as those in - `torch.nn.parallel.distributed.DistributedDataParallel`. - - Args: - module (nn.Module): Module that needs to be wrapped. - device_ids (list[int | `torch.device`]): Same as that in - `torch.nn.parallel.distributed.DistributedDataParallel`. - dim (int, optional): Same as that in the official scatter function in - pytorch. Defaults to 0. - broadcast_buffers (bool): Same as that in - `torch.nn.parallel.distributed.DistributedDataParallel`. - Defaults to False. - find_unused_parameters (bool, optional): Same as that in - `torch.nn.parallel.distributed.DistributedDataParallel`. - Traverse the autograd graph of all tensors contained in returned - value of the wrapped module’s forward function. Defaults to False. - kwargs (dict): Other arguments used in - `torch.nn.parallel.distributed.DistributedDataParallel`. - """ - - def __init__(self, - module, - device_ids, - dim=0, - broadcast_buffers=False, - find_unused_parameters=False, - **kwargs): - super().__init__() - assert len(device_ids) == 1, ( - 'Currently, DistributedDataParallelWrapper only supports one' - 'single CUDA device for each process.' - f'The length of device_ids must be 1, but got {len(device_ids)}.') - self.module = module - self.dim = dim - self.to_ddp( - device_ids=device_ids, - dim=dim, - broadcast_buffers=broadcast_buffers, - find_unused_parameters=find_unused_parameters, - **kwargs) - self.output_device = _get_device_index(device_ids[0], True) - - def to_ddp(self, device_ids, dim, broadcast_buffers, - find_unused_parameters, **kwargs): - """Wrap models with separate MMDistributedDataParallel. - - It only wraps the modules with parameters. - """ - for name, module in self.module._modules.items(): - if next(module.parameters(), None) is None: - module = module.cuda() - elif all(not p.requires_grad for p in module.parameters()): - module = module.cuda() - else: - module = MMDistributedDataParallel( - module.cuda(), - device_ids=device_ids, - dim=dim, - broadcast_buffers=broadcast_buffers, - find_unused_parameters=find_unused_parameters, - **kwargs) - self.module._modules[name] = module - - def scatter(self, inputs, kwargs, device_ids): - """Scatter function. - - Args: - inputs (Tensor): Input Tensor. - kwargs (dict): Args for - ``mmcv.parallel.scatter_gather.scatter_kwargs``. - device_ids (int): Device id. - """ - return scatter_kwargs(inputs, kwargs, device_ids, dim=self.dim) - - def forward(self, *inputs, **kwargs): - """Forward function. - - Args: - inputs (tuple): Input data. - kwargs (dict): Args for - ``mmcv.parallel.scatter_gather.scatter_kwargs``. - """ - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - return self.module(*inputs[0], **kwargs[0]) - - def train_step(self, *inputs, **kwargs): - """Train step function. - - Args: - inputs (Tensor): Input Tensor. - kwargs (dict): Args for - ``mmcv.parallel.scatter_gather.scatter_kwargs``. - """ - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.train_step(*inputs[0], **kwargs[0]) - return output - - def val_step(self, *inputs, **kwargs): - """Validation step function. - - Args: - inputs (tuple): Input data. - kwargs (dict): Args for ``scatter_kwargs``. - """ - inputs, kwargs = self.scatter(inputs, kwargs, - [torch.cuda.current_device()]) - output = self.module.val_step(*inputs[0], **kwargs[0]) - return output diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/__init__.py deleted file mode 100755 index 5294618cf..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .eval_hooks import DistEvalIterHook, EvalIterHook -from .metrics import (L1Evaluation, connectivity, gradient_error, mae, mse, - niqe, psnr, reorder_image, sad, ssim) - -__all__ = [ - 'mse', 'sad', 'psnr', 'reorder_image', 'ssim', 'EvalIterHook', - 'DistEvalIterHook', 'L1Evaluation', 'gradient_error', 'connectivity', - 'niqe', 'mae' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/eval_hooks.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/eval_hooks.py deleted file mode 100755 index bda0f846c..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/eval_hooks.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -from mmcv.runner import Hook -from torch.utils.data import DataLoader - - -class EvalIterHook(Hook): - """Non-Distributed evaluation hook for iteration-based runner. - - This hook will regularly perform evaluation in a given interval when - performing in non-distributed environment. - - Args: - dataloader (DataLoader): A PyTorch dataloader. - interval (int): Evaluation interval. Default: 1. - eval_kwargs (dict): Other eval kwargs. It contains: - save_image (bool): Whether to save image. - save_path (str): The path to save image. - """ - - def __init__(self, dataloader, interval=1, **eval_kwargs): - if not isinstance(dataloader, DataLoader): - raise TypeError('dataloader must be a pytorch DataLoader, ' - f'but got { type(dataloader)}') - self.dataloader = dataloader - self.interval = interval - self.eval_kwargs = eval_kwargs - self.save_image = self.eval_kwargs.pop('save_image', False) - self.save_path = self.eval_kwargs.pop('save_path', None) - - def after_train_iter(self, runner): - """The behavior after each train iteration. - - Args: - runner (``mmcv.runner.BaseRunner``): The runner. - """ - if not self.every_n_iters(runner, self.interval): - return - runner.log_buffer.clear() - from mmedit.apis import single_gpu_test - results = single_gpu_test( - runner.model, - self.dataloader, - save_image=self.save_image, - save_path=self.save_path, - iteration=runner.iter) - self.evaluate(runner, results) - - def evaluate(self, runner, results): - """Evaluation function. - - Args: - runner (``mmcv.runner.BaseRunner``): The runner. - results (dict): Model forward results. - """ - eval_res = self.dataloader.dataset.evaluate( - results, logger=runner.logger, **self.eval_kwargs) - for name, val in eval_res.items(): - runner.log_buffer.output[name] = val - runner.log_buffer.ready = True - # call `after_val_epoch` after evaluation. - # This is a hack. - # Because epoch does not naturally exist In IterBasedRunner, - # thus we consider the end of an evluation as the end of an epoch. - # With this hack , we can support epoch based hooks. - if 'iter' in runner.__class__.__name__.lower(): - runner.call_hook('after_val_epoch') - - -class DistEvalIterHook(EvalIterHook): - """Distributed evaluation hook. - - Args: - dataloader (DataLoader): A PyTorch dataloader. - interval (int): Evaluation interval. Default: 1. - tmpdir (str | None): Temporary directory to save the results of all - processes. Default: None. - gpu_collect (bool): Whether to use gpu or cpu to collect results. - Default: False. - eval_kwargs (dict): Other eval kwargs. It may contain: - save_image (bool): Whether save image. - save_path (str): The path to save image. - """ - - def __init__(self, - dataloader, - interval=1, - gpu_collect=False, - **eval_kwargs): - super().__init__(dataloader, interval, **eval_kwargs) - self.gpu_collect = gpu_collect - - def after_train_iter(self, runner): - """The behavior after each train iteration. - - Args: - runner (``mmcv.runner.BaseRunner``): The runner. - """ - if not self.every_n_iters(runner, self.interval): - return - runner.log_buffer.clear() - from mmedit.apis import multi_gpu_test - results = multi_gpu_test( - runner.model, - self.dataloader, - tmpdir=osp.join(runner.work_dir, '.eval_hook'), - gpu_collect=self.gpu_collect, - save_image=self.save_image, - save_path=self.save_path, - iteration=runner.iter) - if runner.rank == 0: - print('\n') - self.evaluate(runner, results) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/metric_utils.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/metric_utils.py deleted file mode 100755 index ec6017678..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/metric_utils.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import cv2 -import numpy as np - - -def gaussian(x, sigma): - """Gaussian function. - - Args: - x (array_like): The independent variable. - sigma (float): Standard deviation of the gaussian function. - - Return: - ndarray or scalar: Gaussian value of `x`. - """ - return np.exp(-x**2 / (2 * sigma**2)) / (sigma * np.sqrt(2 * np.pi)) - - -def dgaussian(x, sigma): - """Gradient of gaussian. - - Args: - x (array_like): The independent variable. - sigma (float): Standard deviation of the gaussian function. - - Return: - ndarray or scalar: Gradient of gaussian of `x`. - """ - return -x * gaussian(x, sigma) / sigma**2 - - -def gauss_filter(sigma, epsilon=1e-2): - """Gradient of gaussian. - - Args: - sigma (float): Standard deviation of the gaussian kernel. - epsilon (float): Small value used when calculating kernel size. - Default: 1e-2. - - Return: - tuple[ndarray]: Gaussian filter along x and y axis. - """ - half_size = np.ceil( - sigma * np.sqrt(-2 * np.log(np.sqrt(2 * np.pi) * sigma * epsilon))) - size = int(2 * half_size + 1) - - # create filter in x axis - filter_x = np.zeros((size, size)) - for i in range(size): - for j in range(size): - filter_x[i, j] = gaussian(i - half_size, sigma) * dgaussian( - j - half_size, sigma) - - # normalize filter - norm = np.sqrt((filter_x**2).sum()) - filter_x = filter_x / norm - filter_y = np.transpose(filter_x) - - return filter_x, filter_y - - -def gauss_gradient(img, sigma): - """Gaussian gradient. - - From https://www.mathworks.com/matlabcentral/mlc-downloads/downloads/ - submissions/8060/versions/2/previews/gaussgradient/gaussgradient.m/ - index.html - - Args: - img (ndarray): Input image. - sigma (float): Standard deviation of the gaussian kernel. - - Return: - ndarray: Gaussian gradient of input `img`. - """ - filter_x, filter_y = gauss_filter(sigma) - img_filtered_x = cv2.filter2D( - img, -1, filter_x, borderType=cv2.BORDER_REPLICATE) - img_filtered_y = cv2.filter2D( - img, -1, filter_y, borderType=cv2.BORDER_REPLICATE) - return np.sqrt(img_filtered_x**2 + img_filtered_y**2) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/metrics.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/metrics.py deleted file mode 100755 index 9e37dbe1a..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/metrics.py +++ /dev/null @@ -1,572 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import cv2 -import mmcv -import numpy as np -from scipy.ndimage import convolve -from scipy.special import gamma - -from mmedit.datasets.pipelines.matlab_like_resize import MATLABLikeResize -from .metric_utils import gauss_gradient - - -def sad(alpha, trimap, pred_alpha): - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - assert (pred_alpha[trimap == 0] == 0).all() - assert (pred_alpha[trimap == 255] == 255).all() - alpha = alpha.astype(np.float64) / 255 - pred_alpha = pred_alpha.astype(np.float64) / 255 - sad_result = np.abs(pred_alpha - alpha).sum() / 1000 - return sad_result - - -def mse(alpha, trimap, pred_alpha): - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - assert (pred_alpha[trimap == 0] == 0).all() - assert (pred_alpha[trimap == 255] == 255).all() - alpha = alpha.astype(np.float64) / 255 - pred_alpha = pred_alpha.astype(np.float64) / 255 - weight_sum = (trimap == 128).sum() - if weight_sum != 0: - mse_result = ((pred_alpha - alpha)**2).sum() / weight_sum - else: - mse_result = 0 - return mse_result - - -def gradient_error(alpha, trimap, pred_alpha, sigma=1.4): - """Gradient error for evaluating alpha matte prediction. - - Args: - alpha (ndarray): Ground-truth alpha matte. - trimap (ndarray): Input trimap with its value in {0, 128, 255}. - pred_alpha (ndarray): Predicted alpha matte. - sigma (float): Standard deviation of the gaussian kernel. Default: 1.4. - """ - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - if not ((pred_alpha[trimap == 0] == 0).all() and - (pred_alpha[trimap == 255] == 255).all()): - raise ValueError( - 'pred_alpha should be masked by trimap before evaluation') - alpha = alpha.astype(np.float64) - pred_alpha = pred_alpha.astype(np.float64) - alpha_normed = np.zeros_like(alpha) - pred_alpha_normed = np.zeros_like(pred_alpha) - cv2.normalize(alpha, alpha_normed, 1., 0., cv2.NORM_MINMAX) - cv2.normalize(pred_alpha, pred_alpha_normed, 1., 0., cv2.NORM_MINMAX) - - alpha_grad = gauss_gradient(alpha_normed, sigma).astype(np.float32) - pred_alpha_grad = gauss_gradient(pred_alpha_normed, - sigma).astype(np.float32) - - grad_loss = ((alpha_grad - pred_alpha_grad)**2 * (trimap == 128)).sum() - # same as SAD, divide by 1000 to reduce the magnitude of the result - return grad_loss / 1000 - - -def connectivity(alpha, trimap, pred_alpha, step=0.1): - """Connectivity error for evaluating alpha matte prediction. - - Args: - alpha (ndarray): Ground-truth alpha matte with shape (height, width). - Value range of alpha is [0, 255]. - trimap (ndarray): Input trimap with shape (height, width). Elements - in trimap are one of {0, 128, 255}. - pred_alpha (ndarray): Predicted alpha matte with shape (height, width). - Value range of pred_alpha is [0, 255]. - step (float): Step of threshold when computing intersection between - `alpha` and `pred_alpha`. - """ - if alpha.ndim != 2 or trimap.ndim != 2 or pred_alpha.ndim != 2: - raise ValueError( - 'input alpha, trimap and pred_alpha should has two dimensions, ' - f'alpha {alpha.shape}, please check their shape: ' - f'trimap {trimap.shape}, pred_alpha {pred_alpha.shape}') - if not ((pred_alpha[trimap == 0] == 0).all() and - (pred_alpha[trimap == 255] == 255).all()): - raise ValueError( - 'pred_alpha should be masked by trimap before evaluation') - alpha = alpha.astype(np.float32) / 255 - pred_alpha = pred_alpha.astype(np.float32) / 255 - - thresh_steps = np.arange(0, 1 + step, step) - round_down_map = -np.ones_like(alpha) - for i in range(1, len(thresh_steps)): - alpha_thresh = alpha >= thresh_steps[i] - pred_alpha_thresh = pred_alpha >= thresh_steps[i] - intersection = (alpha_thresh & pred_alpha_thresh).astype(np.uint8) - - # connected components - _, output, stats, _ = cv2.connectedComponentsWithStats( - intersection, connectivity=4) - # start from 1 in dim 0 to exclude background - size = stats[1:, -1] - - # largest connected component of the intersection - omega = np.zeros_like(alpha) - if len(size) != 0: - max_id = np.argmax(size) - # plus one to include background - omega[output == max_id + 1] = 1 - - mask = (round_down_map == -1) & (omega == 0) - round_down_map[mask] = thresh_steps[i - 1] - round_down_map[round_down_map == -1] = 1 - - alpha_diff = alpha - round_down_map - pred_alpha_diff = pred_alpha - round_down_map - # only calculate difference larger than or equal to 0.15 - alpha_phi = 1 - alpha_diff * (alpha_diff >= 0.15) - pred_alpha_phi = 1 - pred_alpha_diff * (pred_alpha_diff >= 0.15) - - connectivity_error = np.sum( - np.abs(alpha_phi - pred_alpha_phi) * (trimap == 128)) - # same as SAD, divide by 1000 to reduce the magnitude of the result - return connectivity_error / 1000 - - -def reorder_image(img, input_order='HWC'): - """Reorder images to 'HWC' order. - - If the input_order is (h, w), return (h, w, 1); - If the input_order is (c, h, w), return (h, w, c); - If the input_order is (h, w, c), return as it is. - - Args: - img (ndarray): Input image. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - If the input image shape is (h, w), input_order will not have - effects. Default: 'HWC'. - - Returns: - ndarray: reordered image. - """ - - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - if len(img.shape) == 2: - img = img[..., None] - return img - if input_order == 'CHW': - img = img.transpose(1, 2, 0) - return img - - -def psnr(img1, img2, crop_border=0, input_order='HWC', convert_to=None): - """Calculate PSNR (Peak Signal-to-Noise Ratio). - - Ref: https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio - - Args: - img1 (ndarray): Images with range [0, 255]. - img2 (ndarray): Images with range [0, 255]. - crop_border (int): Cropped pixels in each edges of an image. These - pixels are not involved in the PSNR calculation. Default: 0. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether to convert the images to other color models. - If None, the images are not altered. When computing for 'Y', - the images are assumed to be in BGR order. Options are 'Y' and - None. Default: None. - - Returns: - float: psnr result. - """ - - assert img1.shape == img2.shape, ( - f'Image shapes are different: {img1.shape}, {img2.shape}.') - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - img1 = reorder_image(img1, input_order=input_order) - img2 = reorder_image(img2, input_order=input_order) - - img1, img2 = img1.astype(np.float32), img2.astype(np.float32) - if isinstance(convert_to, str) and convert_to.lower() == 'y': - img1 = mmcv.bgr2ycbcr(img1 / 255., y_only=True) * 255. - img2 = mmcv.bgr2ycbcr(img2 / 255., y_only=True) * 255. - elif convert_to is not None: - raise ValueError('Wrong color model. Supported values are ' - '"Y" and None.') - - if crop_border != 0: - img1 = img1[crop_border:-crop_border, crop_border:-crop_border, None] - img2 = img2[crop_border:-crop_border, crop_border:-crop_border, None] - - mse_value = np.mean((img1 - img2)**2) - if mse_value == 0: - return float('inf') - return 20. * np.log10(255. / np.sqrt(mse_value)) - - -def mae(img1, img2, crop_border=0, input_order='HWC', convert_to=None): - """Calculate mean average error for evaluation. - - Args: - img1 (ndarray): Images with range [0, 255]. - img2 (ndarray): Images with range [0, 255]. - crop_border (int): Cropped pixels in each edges of an image. These - pixels are not involved in the PSNR calculation. Default: 0. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether to convert the images to other color models. - If None, the images are not altered. Options are 'RGB2Y', 'BGR2Y' - and None. Default: None. - - Returns: - float: mae result. - """ - - assert img1.shape == img2.shape, ( - f'Image shapes are different: {img1.shape}, {img2.shape}.') - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - img1 = reorder_image(img1, input_order=input_order) - img2 = reorder_image(img2, input_order=input_order) - - img1, img2 = img1.astype(np.float32), img2.astype(np.float32) - img1, img2 = img1 / 255., img2 / 255. - if isinstance(convert_to, str) and convert_to.lower() == 'rgb2y': - img1 = mmcv.rgb2ycbcr(img1, y_only=True) - img2 = mmcv.rgb2ycbcr(img2, y_only=True) - elif isinstance(convert_to, str) and convert_to.lower() == 'bgr2y': - img1 = mmcv.bgr2ycbcr(img1, y_only=True) - img2 = mmcv.bgr2ycbcr(img2, y_only=True) - elif convert_to is not None: - raise ValueError('Wrong color model. Supported values are ' - '"RGB2Y", "BGR2Y" and None.') - - if crop_border != 0: - img1 = img1[crop_border:-crop_border, crop_border:-crop_border, None] - img2 = img2[crop_border:-crop_border, crop_border:-crop_border, None] - - l1_value = np.mean(np.abs(img1 - img2)) - - return l1_value - - -def _ssim(img1, img2): - """Calculate SSIM (structural similarity) for one channel images. - - It is called by func:`calculate_ssim`. - - Args: - img1, img2 (ndarray): Images with range [0, 255] with order 'HWC'. - - Returns: - float: ssim result. - """ - - C1 = (0.01 * 255)**2 - C2 = (0.03 * 255)**2 - - img1 = img1.astype(np.float64) - img2 = img2.astype(np.float64) - kernel = cv2.getGaussianKernel(11, 1.5) - window = np.outer(kernel, kernel.transpose()) - - mu1 = cv2.filter2D(img1, -1, window)[5:-5, 5:-5] - mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5] - mu1_sq = mu1**2 - mu2_sq = mu2**2 - mu1_mu2 = mu1 * mu2 - sigma1_sq = cv2.filter2D(img1**2, -1, window)[5:-5, 5:-5] - mu1_sq - sigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sq - sigma12 = cv2.filter2D(img1 * img2, -1, window)[5:-5, 5:-5] - mu1_mu2 - - ssim_map = ((2 * mu1_mu2 + C1) * - (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) * - (sigma1_sq + sigma2_sq + C2)) - return ssim_map.mean() - - -def ssim(img1, img2, crop_border=0, input_order='HWC', convert_to=None): - """Calculate SSIM (structural similarity). - - Ref: - Image quality assessment: From error visibility to structural similarity - - The results are the same as that of the official released MATLAB code in - https://ece.uwaterloo.ca/~z70wang/research/ssim/. - - For three-channel images, SSIM is calculated for each channel and then - averaged. - - Args: - img1 (ndarray): Images with range [0, 255]. - img2 (ndarray): Images with range [0, 255]. - crop_border (int): Cropped pixels in each edges of an image. These - pixels are not involved in the SSIM calculation. Default: 0. - input_order (str): Whether the input order is 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether to convert the images to other color models. - If None, the images are not altered. When computing for 'Y', - the images are assumed to be in BGR order. Options are 'Y' and - None. Default: None. - - Returns: - float: ssim result. - """ - - assert img1.shape == img2.shape, ( - f'Image shapes are different: {img1.shape}, {img2.shape}.') - if input_order not in ['HWC', 'CHW']: - raise ValueError( - f'Wrong input_order {input_order}. Supported input_orders are ' - '"HWC" and "CHW"') - img1 = reorder_image(img1, input_order=input_order) - img2 = reorder_image(img2, input_order=input_order) - - if isinstance(convert_to, str) and convert_to.lower() == 'y': - img1, img2 = img1.astype(np.float32), img2.astype(np.float32) - img1 = mmcv.bgr2ycbcr(img1 / 255., y_only=True) * 255. - img2 = mmcv.bgr2ycbcr(img2 / 255., y_only=True) * 255. - img1 = np.expand_dims(img1, axis=2) - img2 = np.expand_dims(img2, axis=2) - elif convert_to is not None: - raise ValueError('Wrong color model. Supported values are ' - '"Y" and None') - - if crop_border != 0: - img1 = img1[crop_border:-crop_border, crop_border:-crop_border, None] - img2 = img2[crop_border:-crop_border, crop_border:-crop_border, None] - - ssims = [] - for i in range(img1.shape[2]): - ssims.append(_ssim(img1[..., i], img2[..., i])) - return np.array(ssims).mean() - - -class L1Evaluation: - """L1 evaluation metric. - - Args: - data_dict (dict): Must contain keys of 'gt_img' and 'fake_res'. If - 'mask' is given, the results will be computed with mask as weight. - """ - - def __call__(self, data_dict): - gt = data_dict['gt_img'] - if 'fake_img' in data_dict: - pred = data_dict.get('fake_img') - else: - pred = data_dict.get('fake_res') - mask = data_dict.get('mask', None) - - from mmedit.models.losses.pixelwise_loss import l1_loss - l1_error = l1_loss(pred, gt, weight=mask, reduction='mean') - - return l1_error - - -def estimate_aggd_param(block): - """Estimate AGGD (Asymmetric Generalized Gaussian Distribution) parameters. - - Args: - block (ndarray): 2D Image block. - - Returns: - tuple: alpha (float), beta_l (float) and beta_r (float) for the AGGD - distribution (Estimating the parames in Equation 7 in the paper). - """ - block = block.flatten() - gam = np.arange(0.2, 10.001, 0.001) # len = 9801 - gam_reciprocal = np.reciprocal(gam) - r_gam = np.square(gamma(gam_reciprocal * 2)) / ( - gamma(gam_reciprocal) * gamma(gam_reciprocal * 3)) - - left_std = np.sqrt(np.mean(block[block < 0]**2)) - right_std = np.sqrt(np.mean(block[block > 0]**2)) - gammahat = left_std / right_std - rhat = (np.mean(np.abs(block)))**2 / np.mean(block**2) - rhatnorm = (rhat * (gammahat**3 + 1) * - (gammahat + 1)) / ((gammahat**2 + 1)**2) - array_position = np.argmin((r_gam - rhatnorm)**2) - - alpha = gam[array_position] - beta_l = left_std * np.sqrt(gamma(1 / alpha) / gamma(3 / alpha)) - beta_r = right_std * np.sqrt(gamma(1 / alpha) / gamma(3 / alpha)) - return (alpha, beta_l, beta_r) - - -def compute_feature(block): - """Compute features. - - Args: - block (ndarray): 2D Image block. - - Returns: - list: Features with length of 18. - """ - feat = [] - alpha, beta_l, beta_r = estimate_aggd_param(block) - feat.extend([alpha, (beta_l + beta_r) / 2]) - - # distortions disturb the fairly regular structure of natural images. - # This deviation can be captured by analyzing the sample distribution of - # the products of pairs of adjacent coefficients computed along - # horizontal, vertical and diagonal orientations. - shifts = [[0, 1], [1, 0], [1, 1], [1, -1]] - for shift in shifts: - shifted_block = np.roll(block, shift, axis=(0, 1)) - alpha, beta_l, beta_r = estimate_aggd_param(block * shifted_block) - mean = (beta_r - beta_l) * (gamma(2 / alpha) / gamma(1 / alpha)) - feat.extend([alpha, mean, beta_l, beta_r]) - return feat - - -def niqe_core(img, - mu_pris_param, - cov_pris_param, - gaussian_window, - block_size_h=96, - block_size_w=96): - """Calculate NIQE (Natural Image Quality Evaluator) metric. - - Ref: Making a "Completely Blind" Image Quality Analyzer. - This implementation could produce almost the same results as the official - MATLAB codes: http://live.ece.utexas.edu/research/quality/niqe_release.zip - - Note that we do not include block overlap height and width, since they are - always 0 in the official implementation. - - For good performance, it is advisable by the official implementation to - divide the distorted image in to the same size patched as used for the - construction of multivariate Gaussian model. - - Args: - img (ndarray): Input image whose quality needs to be computed. The - image must be a gray or Y (of YCbCr) image with shape (h, w). - Range [0, 255] with float type. - mu_pris_param (ndarray): Mean of a pre-defined multivariate Gaussian - model calculated on the pristine dataset. - cov_pris_param (ndarray): Covariance of a pre-defined multivariate - Gaussian model calculated on the pristine dataset. - gaussian_window (ndarray): A 7x7 Gaussian window used for smoothing the - image. - block_size_h (int): Height of the blocks in to which image is divided. - Default: 96 (the official recommended value). - block_size_w (int): Width of the blocks in to which image is divided. - Default: 96 (the official recommended value). - """ - # crop image - h, w = img.shape - num_block_h = math.floor(h / block_size_h) - num_block_w = math.floor(w / block_size_w) - img = img[0:num_block_h * block_size_h, 0:num_block_w * block_size_w] - - distparam = [] # dist param is actually the multiscale features - for scale in (1, 2): # perform on two scales (1, 2) - mu = convolve(img, gaussian_window, mode='nearest') - - sigma = np.sqrt( - np.abs( - convolve(np.square(img), gaussian_window, mode='nearest') - - np.square(mu))) - # normalize, as in Eq. 1 in the paper - img_nomalized = (img - mu) / (sigma + 1) - - feat = [] - for idx_w in range(num_block_w): - for idx_h in range(num_block_h): - # process each block - block = img_nomalized[idx_h * block_size_h // - scale:(idx_h + 1) * block_size_h // - scale, idx_w * block_size_w // - scale:(idx_w + 1) * block_size_w // - scale] - feat.append(compute_feature(block)) - - distparam.append(np.array(feat)) - - # matlab-like bicubic downsample with anti-aliasing - if scale == 1: - resize = MATLABLikeResize(keys=None, scale=0.5) - img = resize._resize(img[:, :, np.newaxis] / 255.)[:, :, 0] * 255. - - distparam = np.concatenate(distparam, axis=1) - - # fit a MVG (multivariate Gaussian) model to distorted patch features - mu_distparam = np.nanmean(distparam, axis=0) - distparam_no_nan = distparam[~np.isnan(distparam).any(axis=1)] - cov_distparam = np.cov(distparam_no_nan, rowvar=False) - - # compute niqe quality, Eq. 10 in the paper - invcov_param = np.linalg.pinv((cov_pris_param + cov_distparam) / 2) - quality = np.matmul( - np.matmul((mu_pris_param - mu_distparam), invcov_param), - np.transpose((mu_pris_param - mu_distparam))) - - return np.squeeze(np.sqrt(quality)) - - -def niqe(img, crop_border, input_order='HWC', convert_to='y'): - """Calculate NIQE (Natural Image Quality Evaluator) metric. - - Ref: Making a "Completely Blind" Image Quality Analyzer. - This implementation could produce almost the same results as the official - MATLAB codes: http://live.ece.utexas.edu/research/quality/niqe_release.zip - - We use the official params estimated from the pristine dataset. - We use the recommended block size (96, 96) without overlaps. - - Args: - img (ndarray): Input image whose quality needs to be computed. - The input image must be in range [0, 255] with float/int type. - The input_order of image can be 'HW' or 'HWC' or 'CHW'. (BGR order) - If the input order is 'HWC' or 'CHW', it will be converted to gray - or Y (of YCbCr) image according to the ``convert_to`` argument. - crop_border (int): Cropped pixels in each edge of an image. These - pixels are not involved in the metric calculation. - input_order (str): Whether the input order is 'HW', 'HWC' or 'CHW'. - Default: 'HWC'. - convert_to (str): Whether converted to 'y' (of MATLAB YCbCr) or 'gray'. - Default: 'y'. - - Returns: - float: NIQE result. - """ - - # we use the official params estimated from the pristine dataset. - niqe_pris_params = np.load('mmedit/core/evaluation/niqe_pris_params.npz') - mu_pris_param = niqe_pris_params['mu_pris_param'] - cov_pris_param = niqe_pris_params['cov_pris_param'] - gaussian_window = niqe_pris_params['gaussian_window'] - - img = img.astype(np.float32) - if input_order != 'HW': - img = reorder_image(img, input_order=input_order) - if convert_to == 'y': - img = mmcv.bgr2ycbcr(img / 255., y_only=True) * 255. - elif convert_to == 'gray': - img = mmcv.bgr2gray(img / 255., cv2.COLOR_BGR2GRAY) * 255. - img = np.squeeze(img) - - if crop_border != 0: - img = img[crop_border:-crop_border, crop_border:-crop_border] - - # round to follow official implementation - img = img.round() - - niqe_result = niqe_core(img, mu_pris_param, cov_pris_param, - gaussian_window) - - return niqe_result diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/niqe_pris_params.npz b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/evaluation/niqe_pris_params.npz deleted file mode 100755 index 204ddcee87c4cd39aca04a42b539f0a5bfccecc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11850 zcmbt)cUaH;+jr3=DyzX|m!hnqC9AVZMABZ`oA%y&X;D&HMUqmXgpjuqEi+{lC3KOH zvO@CYcVDlL`*+><^T+f2zK%G)KA+F`=s3UU`8r?Yt*tqhOOWHgULqW0eZL;d<>cV_ z{WFzAki*5t!rjx!%fj8t)5=BC)jfcdW6HlS{(j*1O}}61TKZeGjX61dIfBG(?YwL} z#a9c9ueDbYUn(qa@8;?4Y2|9+=4os9`~TNl?ewx6`F}4*D|fq*_Yy0X3d_hZS}GhO z{QvvnnI`=;G2{-HlBB`x_0d1xQg?O8QO%(qnsX|_U3TGla@yMO{j0i~EZ13=saQ19 zs$Ija!?JJad3blt(Dzf6o4IXBA@>?hy~l6iD$qc}-_4DDJ>QUV{rspT(fzE#WmAQm zXg&R`|LdE$!gbQGerum;`G!JI?(dQr%w|tgvUZDpipczx02yA zzneR2-cxJ-d%+jWdTD-fMhoBc3VJ4AyhiGHEy=i5$!~4ENn?ficteAG$>j5Eo?kM> z7DnTQ4dnDn}rvW|bKT@{wg)=4h9w>YDT)_R!DHJe^f&obA< z&S~zYiU8|hAwA=4Gn!hTB&h@&)bYvErN)GvQ)U*g1Q7XExe z`||h_;>fYBHaT{jE^V&f*>I(q1pUq&HXJ%l3bpR}?TtlL(%QMb;EOAj42_wmE_j5Z zzq$3!OU|crzZRu)brn%H*9C_gnx5>Nq>WIGaUR*m8H6hMy-ACe6_nob+v?2Z{d&fCCCX-~=>-`b8)5*cpIw-|{AK5+ssC)6O zHjDYp-|o(}hvYsxu5Ep{hf3y`n3})LpnXdgrfRSDVG~}QKY9FUIyv9nAbNXQ3eDdT zIVRaJgBt62+Gx2q>)e~!W0jXq^6zz8-qao^qUwx1Tz&JvfW@Muvu5_DrA+ zYE)2}t;}wYskAO|iKCrWaV^1W33Q;O^XJW1@pM%_F6o7lC7U$l;pF)>o|GbLoSyBA zqdQYSM>+?`Qq7c#7RfMI=Fy=zJT$b6Ottn~?b{tin;$<+N{opi<-v||%^}Y0WQC0` zx5X|};(RT-<9O6)zo0JXBUWs_Cz3{eb2sIOggVl?MWxC!<044-*h_UCi6klv5i7D< zn?@mbhP?H19w_q1U;cEyCWe;ry=+Tf5>Mqyp+|)>_fX`=-Q!)KTCrPxX}J=-2^24& z{?`oCXeys5D6X?@4-K{^j&HnR$r}E2-QB~VK)!tH7iN`3(U8*u{qgaIwENYZzhu6r zke8f_cJ)v!#hq5V6w0xZPI;7f&3GL~d0DesjV44<(Ljr4eM2k_E>PU);Ix(ro#^KH zaVwncn)j`Y?v0=)`?jd@B}UWxguH6?Jafj+OYOYJB8d&Nw5*z78WU5pslO?TWIh?M zTyx)q#ogkm;NB5Q8`bv9-Ea>cog?%M>T=gfan)XNciJMYQhO{+iP>0r{ygC9L`Hmj z+a zRO8@wT?r+YV>ZiD-`bLPzPR2uvE5)4Uoc1L7u4l@k}uE2i$V1Eokhd&+fXV!w(8E5 z;&7UoR;OH5CQV5j3vRBi3M9`ftM{dz3Z*p`vRPL@hS8hij1TuSELrmBl*BWf;WR6M zwab`ve;O0k*p{>;jGWJ>UD=~(#Rl$3HBS69oW8t|s+n2uN9&BObogQuX?#hyTgYGp z-HcW9zTo3Ucg#3)=O!AkU~awG)8*bYba|b0!5^N~JI!YHy&vv$@PNk8_0mF2!?p0i zj4BU0cEa&xy{H$}Z%)Z*H*}%bO=%|&>FBa^y4g7<_r2&@+P6tk(ymnPZ6Nh|)=m-= zOLe}Vs>_t-_8q)b=S4S~PKb(LcNxW9;0xvm{err*1(qwmS56>}{*TLZHIhjnFj&Fg zL7&}Q(#dmYNeuZVC?-k7M3a`7v&}q@M2d9LeyEsX%dD%$*>rx2rL2UWxus_!XhW#v z<(JbF$t?Ze_Q}5X?959+9{w$HRQ9qzTj)hNO}B3iwbnjNio+9lhh2Bk%JN5KW)Mz; zwUaHpG}TElS7=`Tl>kzEc6sRT^)PZ-6m2MSJ)CAASSOv|V#1V;@mqV{4JDDqA~O9+ zLG-|&D%yKZ2;I!w`qO`#1>=#oD-ul%BP-pD1FO{h$c`md8b}Az`PViF+qf*)(gtts z@AhHDUEC_8`ptKA9|3oPFPJ0r3+l4ly^M23VLH8OA5W7e=&;ig3l<-}8b@Qce=py< zHJ-RmnV9)mr%}P3v{K$$8x~jA@RaXk0%fjX3!TnHQ=7-td0z9+`S=m zyMni2+Wjaxr7>n1w|zV<ztwS z#*!sQI~K|Q8AUyF-I@gpg6Vyo`Z%$cXsWGK`1Qv-6Q(h3N~!qqNV0a85P$kOcyv!= z9|3oPFPJ0r3+gh?`a55WryFID^>?_t!H)e|74IRXrA|jWX1DY`Q)PquXKejxXh2gc zO+&dZ>#?(ACYx+j7iFn2PlmQ}sWa2unA1(oMr1Yq_A$PELzY;S^q{JXgRZFvL(Cgv--B0q3*xjU8QHJ)$^zKBG7MF|+1<1_|U38n1^(}d6 zK3>$0_UZ;icgwA3)9vE(g$GTj^zf7K-jz!!;dtT4#j94)W(NhX*6wu_KaaoUhVW+k zk$mEZkeL{r*c_s(OpD3aS*rZ#3>6xvJF6QIw1L21u&1$)fV;pK%n|wpbvfMQFme0M zC<^GYdnFeVPAv-S+>|0?NP#n~v_js2ndV#Z7R?{|e$1_F+9!n2pOPz&ipa*$?DB3$ zttrl|M|(zzcWyM9Z%Hy-xiN@*B>U_pf7wgk<_m>4eTbqBj*{Fbl>O+sLQ3bQ32M~y zre@;ubDm_kTKCHGbAGh?+`7Cqd;O`ua`vuC=dIb|{_ch6^aIG|M8(9mr=H|JQBtMx zPaj%dJ9mT0cr(@&-rBD>)t|zR%a%>K=uRg&jMg^0dDGezYnGayH(?)}CO^}@;78*^ zo4faQjO?esc^&u*_B8epa2NQ3IYPgnE(TZPxSpm(QK*2-`-)j{G}P2K5M*q@?hJTu zPRWa++3^B7Z*#(E+@l;{xx=xf!6UUpwA!34b`L6(IS@rrDmrZkuZ2?1nyMVdMf<78 zLvDNZqj;)Uy%Jhb6+#jxpYKZEs>bfiDn&@_3Z(3TF9Sk5f{1vkLZ|BllWz6B%AsI! z_O5QBVWv+2&5$1I-x3o<&OzaVYp(gzd0yq-Z{0?0VK~nlKhHzRv@H+4p>}l*H z;4bh5bA*0DUCPuI<8=lcsl)r4@;EM6cJ9fV!yjYySjorspc9qW%w9u(gr=xbmC~SB z)(Sh8y1m}fsLz5mxw%$enxV#yC_0E)R_n74)6_RgCl#5v%L)2=Re}t1e*M~dMx9Nz zY<;L3tj>Ok9BNobs~E3A^FXKEHcERg*ekYNmra(>+1fO)gweuU!Ak!1Y)Zti`Mad; z^gV{_(5X*GOuNv_cG3g^sm8#Rm_}uaBppFej!y#-6p_ zW-}w5VyWQ9t)FKd!pUNJf1lKzY`VXv-aB!5JoSB!IPzlJ$oyodUG%NzVcMCyBo@i~ zQ_v=Z(gdR*+9zEmBbgjTiY0qD|MA_BO}<J`9)7VGAUEmAm2>pV(jGeyOQ>8A5#w))cQH{}5vj6l{F$XI)!F`rk z`j!Y1nxqx07#KjG45oxjf61hx$dcY+7EOP>OKEog=}&5_jVnLy=4X4#yZ>-f_n|V& z2d{P>_al>+UtfA%_NQi*o=0ZZD$Ff?z0v)pzGP|N;wl#EOL=cwuIwG_OKZ*+9^WLi zjdhhbc6{0GM^^I{(p~@XB730@5!W~$Dt~pkkDJSkiO!h*)%mSIopV%gPY?1K)zi?8 z&_~b#;Je^;;4j$I*hj!!;0xvm{errrW^`Duu}!Bb-$V6FPusDvs@*P{PZLO9d*)e* zgeZ#Zo?dm`qnH$=8@4$dO{NF_UzW-G#gf6<#%YFb`lK}Tp7x9pe0`bG`1taTND|ob zl~+$Bj>dgBG01M95VjN9{rS|&wN^vNV{RiB_yp89a21tOGKcUhqE z>%ITsnuavHaqy~b!QdvE za%ZvUn>1~v(7i>>Jx`A1d)|9HTw!ul7!}@ z<)+$9aIIa;uOAkazdCDE@FP>^p6b4K(~cik;J?6UfS!hKgg$}}0N(|#1AoDu#y$e> z0$(sk=oi$b@x5eceXfs#Y5}QrSFLP(SeI7?*iBcO2_mALAwpzi0 zpQ7y6RgQJ7V*d2RP~Y}ceIN0mD&Ghq#A2O*gy=>(=e~QiXU%anQh;;6Y^e)r#CKaQBs-GkJNyzY*-{3bt zirxNxw~eDJqgS6_+En?FPVj)$Qdk$7BOA9;v|0xo*3(- zVc|g!L&rZ%+u%(L3xC;*S8QRbyjh8t_V`l7jm;Z-R(g*5b?~L&zrbgJo`!COK7tMa z-vzG&f5D!{J_7CnUoc1L7t}?(jYG@$c@ODX_&qUvS4M}z{If)C&ePAT&a>+AzLX%v zAKCW(7-b6GQ`+8jl4LUu)$-52Kr7wf284|0@@9U&>73K@$+@b@!Km*feOk7a*W+0w z4embNzg5JH`J_rOiRdbVR)jYn(ZKBppgMZ-Cgsemo>gxR1cPN(nUfLcp@=l`Eg{Nz- zPPJpvfrp~?Y75hA zorZXlNi;=jvff)w=8*M{Q)I*!FzFT0Xx0uN^}XTm!pDSP2VV;Q3w#FXY3N4iBj^C| zUGO^a7wl>5Bj7IZ1#^UcL0t-R<0RDQxKi-^sVBM~nJ}^Ps`F#Xm?U0&ZO!+xr3OK< zIq5Rn*$c<6(2PxnCrj4Q_rN#PW`@a9g5}w}(|puM^8onX@OR;3!moob1^)#;1N1a> zBlHn;0QfF=9rz3OH1-j27x;oXLcgFc9|{DTJXJ#ILF-IA>%>5s>t;Du-6e!Lnm;S) z_2`gK^6n)mjXre9H?~VcKZNcV^`88EEO^8}xB8y?K$8XhjN>=l7DO|LFY%l?=tn<1 z&PK+L?7wrR8v6DHM(owZX{)>If@x-1lUDXBpV6EIc>sKG_`C2i;n%^Jg8u@a0eTv` z5&8%^0DKp`4*Ugs8v6*i3w*&Gp+{ z0KN-e2mXRRjeP{%1-@X8&@ZSteU>F@VA)BZI-92-BFFqz!B+>@s0k*59ileRJ{beVhX%tMWu*EwDe5#&lV7MGDEBrk3@bz(YMczWyW&Tn^-iTZc`4Msw z3RCX2bBp_q=CH^ck*gs;LQaA_0KPZ;UHF*r>)=ble}T^cJw2)$ z|JFy)0pPpfb>J`9)7VGAUEmAm2>pV(G-XKNk0L-jl#(~YCZ@$s#nPui6jJ-^d5 zPb!QgCuSsWa|@>YO^-J0jgO=iT8+{0KN-e2mXRRjeP{%1-@X8&@ZUVJ6$og zj;1}d;_{o*9VOb##`;*1cyKgDiND}};wISKLr_}=h$;bX$DgD(aD1wI4xG;|~M5p)3fE_faI z3-&bj5pWmyf;mFJpf0-?2TTu=wRj50 z-@LcvwbKC|_ONk&c!S?a9$7JGfrD${=s6FZXFzU`d>T0{@>0l6WgJ-8 zkNzWRS}RA-OW|A)&cEQC2hKAfw?{sW92R*aay8^f$Vrd~!1so~3m+4H9egSHFYpB4G3!8O}@LToBH` z;G74}Ga$D|K8+j}c_VT)%d>I zr?HQKyTBLB5&8vnF}$Ux-Yw_*k293zU$gwikKy3>{Ub1PhSI^x$IHuUz!e_h!Xm^{PL9%!{9nR`LwC2Qt(f7EH=kfWtKW>sn*S?lwvMqV! z=XCch;XSV7e*8Z8eB9svaO&986R(UukL$P}&*Ss(`FI}haUJ*L_r>pz>-c=U$93HQ z&-&o={`vd-^L}kj&M|^x|8J+R|Ep(y{~i9X1J?iJ4E8@C|MwHU|NZe7BOmZT&-ecG k`G4=`|NgxA5|00^x3x9@829_Ou_J%3j{NJr?DxC>1Ij;2^Z)<= diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/export/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/export/__init__.py deleted file mode 100755 index 6cf757d83..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/export/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .wrappers import ONNXRuntimeEditing - -__all__ = ['ONNXRuntimeEditing'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/export/wrappers.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/export/wrappers.py deleted file mode 100755 index 5b4d55fbc..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/export/wrappers.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp -import warnings - -import numpy as np -import onnxruntime as ort -import torch -from torch import nn - -from mmedit.models import BaseMattor, BasicRestorer, build_model - - -def inference_with_session(sess, io_binding, output_names, input_tensor): - device_type = input_tensor.device.type - device_id = input_tensor.device.index - device_id = 0 if device_id is None else device_id - io_binding.bind_input( - name='input', - device_type=device_type, - device_id=device_id, - element_type=np.float32, - shape=input_tensor.shape, - buffer_ptr=input_tensor.data_ptr()) - for name in output_names: - io_binding.bind_output(name) - sess.run_with_iobinding(io_binding) - pred = io_binding.copy_outputs_to_cpu() - return pred - - -class ONNXRuntimeMattor(nn.Module): - - def __init__(self, sess, io_binding, output_names, base_model): - super(ONNXRuntimeMattor, self).__init__() - self.sess = sess - self.io_binding = io_binding - self.output_names = output_names - self.base_model = base_model - - def forward(self, - merged, - trimap, - meta, - test_mode=False, - save_image=False, - save_path=None, - iteration=None): - input_tensor = torch.cat((merged, trimap), 1).contiguous() - pred_alpha = inference_with_session(self.sess, self.io_binding, - self.output_names, input_tensor)[0] - - pred_alpha = pred_alpha.squeeze() - pred_alpha = self.base_model.restore_shape(pred_alpha, meta) - eval_result = self.base_model.evaluate(pred_alpha, meta) - - if save_image: - self.base_model.save_image(pred_alpha, meta, save_path, iteration) - - return {'pred_alpha': pred_alpha, 'eval_result': eval_result} - - -class RestorerGenerator(nn.Module): - - def __init__(self, sess, io_binding, output_names): - super(RestorerGenerator, self).__init__() - self.sess = sess - self.io_binding = io_binding - self.output_names = output_names - - def forward(self, x): - pred = inference_with_session(self.sess, self.io_binding, - self.output_names, x)[0] - pred = torch.from_numpy(pred) - return pred - - -class ONNXRuntimeRestorer(nn.Module): - - def __init__(self, sess, io_binding, output_names, base_model): - super(ONNXRuntimeRestorer, self).__init__() - self.sess = sess - self.io_binding = io_binding - self.output_names = output_names - self.base_model = base_model - restorer_generator = RestorerGenerator(self.sess, self.io_binding, - self.output_names) - base_model.generator = restorer_generator - - def forward(self, lq, gt=None, test_mode=False, **kwargs): - return self.base_model(lq, gt=gt, test_mode=test_mode, **kwargs) - - -class ONNXRuntimeEditing(nn.Module): - - def __init__(self, onnx_file, cfg, device_id): - super(ONNXRuntimeEditing, self).__init__() - ort_custom_op_path = '' - try: - from mmcv.ops import get_onnxruntime_op_path - ort_custom_op_path = get_onnxruntime_op_path() - except (ImportError, ModuleNotFoundError): - warnings.warn('If input model has custom op from mmcv, \ - you may have to build mmcv with ONNXRuntime from source.') - session_options = ort.SessionOptions() - # register custom op for onnxruntime - if osp.exists(ort_custom_op_path): - session_options.register_custom_ops_library(ort_custom_op_path) - sess = ort.InferenceSession(onnx_file, session_options) - providers = ['CPUExecutionProvider'] - options = [{}] - is_cuda_available = ort.get_device() == 'GPU' - if is_cuda_available: - providers.insert(0, 'CUDAExecutionProvider') - options.insert(0, {'device_id': device_id}) - - sess.set_providers(providers, options) - - self.sess = sess - self.device_id = device_id - self.io_binding = sess.io_binding() - self.output_names = [_.name for _ in sess.get_outputs()] - - base_model = build_model( - cfg.model, train_cfg=None, test_cfg=cfg.test_cfg) - - if isinstance(base_model, BaseMattor): - WrapperClass = ONNXRuntimeMattor - elif isinstance(base_model, BasicRestorer): - WrapperClass = ONNXRuntimeRestorer - self.wrapper = WrapperClass(self.sess, self.io_binding, - self.output_names, base_model) - - def forward(self, **kwargs): - return self.wrapper(**kwargs) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/hooks/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/hooks/__init__.py deleted file mode 100755 index 575c43b35..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/hooks/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .ema import ExponentialMovingAverageHook -from .visualization import VisualizationHook - -__all__ = ['VisualizationHook', 'ExponentialMovingAverageHook'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/hooks/ema.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/hooks/ema.py deleted file mode 100755 index 0e7f0b2e8..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/hooks/ema.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import warnings -from copy import deepcopy -from functools import partial - -import mmcv -import torch -from mmcv.parallel import is_module_wrapper -from mmcv.runner import HOOKS, Hook - - -@HOOKS.register_module() -class ExponentialMovingAverageHook(Hook): - """Exponential Moving Average Hook. - - Exponential moving average is a trick that widely used in current GAN - literature, e.g., PGGAN, StyleGAN, and BigGAN. This general idea of it is - maintaining a model with the same architecture, but its parameters are - updated as a moving average of the trained weights in the original model. - In general, the model with moving averaged weights achieves better - performance. - - Args: - module_keys (str | tuple[str]): The name of the ema model. Note that we - require these keys are followed by '_ema' so that we can easily - find the original model by discarding the last four characters. - interp_mode (str, optional): Mode of the interpolation method. - Defaults to 'lerp'. - interp_cfg (dict | None, optional): Set arguments of the interpolation - function. Defaults to None. - interval (int, optional): Evaluation interval (by iterations). - Default: -1. - start_iter (int, optional): Start iteration for ema. If the start - iteration is not reached, the weights of ema model will maintain - the same as the original one. Otherwise, its parameters are updated - as a moving average of the trained weights in the original model. - Default: 0. - """ - - def __init__(self, - module_keys, - interp_mode='lerp', - interp_cfg=None, - interval=-1, - start_iter=0): - super().__init__() - assert isinstance(module_keys, str) or mmcv.is_tuple_of( - module_keys, str) - self.module_keys = (module_keys, ) if isinstance(module_keys, - str) else module_keys - # sanity check for the format of module keys - for k in self.module_keys: - assert k.endswith( - '_ema'), 'You should give keys that end with "_ema".' - self.interp_mode = interp_mode - self.interp_cfg = dict() if interp_cfg is None else deepcopy( - interp_cfg) - self.interval = interval - self.start_iter = start_iter - - assert hasattr( - self, interp_mode - ), f'Currently, we do not support {self.interp_mode} for EMA.' - self.interp_func = partial( - getattr(self, interp_mode), **self.interp_cfg) - - @staticmethod - def lerp(a, b, momentum=0.999, momentum_nontrainable=0., trainable=True): - m = momentum if trainable else momentum_nontrainable - return a + (b - a) * m - - def every_n_iters(self, runner, n): - if runner.iter < self.start_iter: - return True - return (runner.iter + 1 - self.start_iter) % n == 0 if n > 0 else False - - @torch.no_grad() - def after_train_iter(self, runner): - if not self.every_n_iters(runner, self.interval): - return - - model = runner.model.module if is_module_wrapper( - runner.model) else runner.model - - for key in self.module_keys: - # get current ema states - ema_net = getattr(model, key) - states_ema = ema_net.state_dict(keep_vars=False) - # get currently original states - net = getattr(model, key[:-4]) - states_orig = net.state_dict(keep_vars=True) - - for k, v in states_orig.items(): - if runner.iter < self.start_iter: - states_ema[k].data.copy_(v.data) - else: - states_ema[k] = self.interp_func( - v, states_ema[k], trainable=v.requires_grad).detach() - ema_net.load_state_dict(states_ema, strict=True) - - def before_run(self, runner): - model = runner.model.module if is_module_wrapper( - runner.model) else runner.model - # sanity check for ema model - for k in self.module_keys: - if not hasattr(model, k) and not hasattr(model, k[:-4]): - raise RuntimeError( - f'Cannot find both {k[:-4]} and {k} network for EMA hook.') - if not hasattr(model, k) and hasattr(model, k[:-4]): - setattr(model, k, deepcopy(getattr(model, k[:-4]))) - warnings.warn( - f'We do not suggest construct and initialize EMA model {k}' - ' in hook. You may explicitly define it by yourself.') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/hooks/visualization.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/hooks/visualization.py deleted file mode 100755 index 63c8ee7d8..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/hooks/visualization.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os.path as osp - -import mmcv -import torch -from mmcv.runner import HOOKS, Hook -from mmcv.runner.dist_utils import master_only -from torchvision.utils import save_image - - -@HOOKS.register_module() -class VisualizationHook(Hook): - """Visualization hook. - - In this hook, we use the official api `save_image` in torchvision to save - the visualization results. - - Args: - output_dir (str): The file path to store visualizations. - res_name_list (str): The list contains the name of results in outputs - dict. The results in outputs dict must be a torch.Tensor with shape - (n, c, h, w). - interval (int): The interval of calling this hook. If set to -1, - the visualization hook will not be called. Default: -1. - filename_tmpl (str): Format string used to save images. The output file - name will be formatted as this args. Default: 'iter_{}.png'. - rerange (bool): Whether to rerange the output value from [-1, 1] to - [0, 1]. We highly recommend users should preprocess the - visualization results on their own. Here, we just provide a simple - interface. Default: True. - bgr2rgb (bool): Whether to reformat the channel dimension from BGR to - RGB. The final image we will save is following RGB style. - Default: True. - nrow (int): The number of samples in a row. Default: 1. - padding (int): The number of padding pixels between each samples. - Default: 4. - """ - - def __init__(self, - output_dir, - res_name_list, - interval=-1, - filename_tmpl='iter_{}.png', - rerange=True, - bgr2rgb=True, - nrow=1, - padding=4): - assert mmcv.is_list_of(res_name_list, str) - self.output_dir = output_dir - self.res_name_list = res_name_list - self.interval = interval - self.filename_tmpl = filename_tmpl - self.bgr2rgb = bgr2rgb - self.rerange = rerange - self.nrow = nrow - self.padding = padding - - mmcv.mkdir_or_exist(self.output_dir) - - @master_only - def after_train_iter(self, runner): - """The behavior after each train iteration. - - Args: - runner (object): The runner. - """ - if not self.every_n_iters(runner, self.interval): - return - results = runner.outputs['results'] - - filename = self.filename_tmpl.format(runner.iter + 1) - - img_list = [x for k, x in results.items() if k in self.res_name_list] - img_cat = torch.cat(img_list, dim=3).detach() - if self.rerange: - img_cat = ((img_cat + 1) / 2) - if self.bgr2rgb: - img_cat = img_cat[:, [2, 1, 0], ...] - img_cat = img_cat.clamp_(0, 1) - save_image( - img_cat, - osp.join(self.output_dir, filename), - nrow=self.nrow, - padding=self.padding) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/mask.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/mask.py deleted file mode 100755 index 51486111c..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/mask.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import cv2 -import mmcv -import numpy as np -from PIL import Image, ImageDraw - - -def random_bbox(img_shape, max_bbox_shape, max_bbox_delta=40, min_margin=20): - """Generate a random bbox for the mask on a given image. - - In our implementation, the max value cannot be obtained since we use - `np.random.randint`. And this may be different with other standard scripts - in the community. - - Args: - img_shape (tuple[int]): The size of a image, in the form of (h, w). - max_bbox_shape (int | tuple[int]): Maximum shape of the mask box, - in the form of (h, w). If it is an integer, the mask box will be - square. - max_bbox_delta (int | tuple[int]): Maximum delta of the mask box, - in the form of (delta_h, delta_w). If it is an integer, delta_h - and delta_w will be the same. Mask shape will be randomly sampled - from the range of `max_bbox_shape - max_bbox_delta` and - `max_bbox_shape`. Default: (40, 40). - min_margin (int | tuple[int]): The minimum margin size from the - edges of mask box to the image boarder, in the form of - (margin_h, margin_w). If it is an integer, margin_h and margin_w - will be the same. Default: (20, 20). - - Returns: - tuple[int]: The generated box, (top, left, h, w). - """ - if not isinstance(max_bbox_shape, tuple): - max_bbox_shape = (max_bbox_shape, max_bbox_shape) - if not isinstance(max_bbox_delta, tuple): - max_bbox_delta = (max_bbox_delta, max_bbox_delta) - if not isinstance(min_margin, tuple): - min_margin = (min_margin, min_margin) - assert mmcv.is_tuple_of(max_bbox_shape, int) - assert mmcv.is_tuple_of(max_bbox_delta, int) - assert mmcv.is_tuple_of(min_margin, int) - - img_h, img_w = img_shape[:2] - max_mask_h, max_mask_w = max_bbox_shape - max_delta_h, max_delta_w = max_bbox_delta - margin_h, margin_w = min_margin - - if max_mask_h > img_h or max_mask_w > img_w: - raise ValueError(f'mask shape {max_bbox_shape} should be smaller than ' - f'image shape {img_shape}') - if (max_delta_h // 2 * 2 >= max_mask_h - or max_delta_w // 2 * 2 >= max_mask_w): - raise ValueError(f'mask delta {max_bbox_delta} should be smaller than' - f'mask shape {max_bbox_shape}') - if img_h - max_mask_h < 2 * margin_h or img_w - max_mask_w < 2 * margin_w: - raise ValueError(f'Margin {min_margin} cannot be satisfied for img' - f'shape {img_shape} and mask shape {max_bbox_shape}') - - # get the max value of (top, left) - max_top = img_h - margin_h - max_mask_h - max_left = img_w - margin_w - max_mask_w - # randomly select a (top, left) - top = np.random.randint(margin_h, max_top) - left = np.random.randint(margin_w, max_left) - # randomly shrink the shape of mask box according to `max_bbox_delta` - # the center of box is fixed - delta_top = np.random.randint(0, max_delta_h // 2 + 1) - delta_left = np.random.randint(0, max_delta_w // 2 + 1) - top = top + delta_top - left = left + delta_left - h = max_mask_h - delta_top - w = max_mask_w - delta_left - return (top, left, h, w) - - -def bbox2mask(img_shape, bbox, dtype='uint8'): - """Generate mask in ndarray from bbox. - - The returned mask has the shape of (h, w, 1). '1' indicates the - hole and '0' indicates the valid regions. - - We prefer to use `uint8` as the data type of masks, which may be different - from other codes in the community. - - Args: - img_shape (tuple[int]): The size of the image. - bbox (tuple[int]): Configuration tuple, (top, left, height, width) - dtype (str): Indicate the data type of returned masks. Default: 'uint8' - - Return: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - height, width = img_shape[:2] - - mask = np.zeros((height, width, 1), dtype=dtype) - mask[bbox[0]:bbox[0] + bbox[2], bbox[1]:bbox[1] + bbox[3], :] = 1 - - return mask - - -def brush_stroke_mask(img_shape, - num_vertices=(4, 12), - mean_angle=2 * math.pi / 5, - angle_range=2 * math.pi / 15, - brush_width=(12, 40), - max_loops=4, - dtype='uint8'): - """Generate free-form mask. - - The method of generating free-form mask is in the following paper: - Free-Form Image Inpainting with Gated Convolution. - - When you set the config of this type of mask. You may note the usage of - `np.random.randint` and the range of `np.random.randint` is [left, right). - - We prefer to use `uint8` as the data type of masks, which may be different - from other codes in the community. - - TODO: Rewrite the implementation of this function. - - Args: - img_shape (tuple[int]): Size of the image. - num_vertices (int | tuple[int]): Min and max number of vertices. If - only give an integer, we will fix the number of vertices. - Default: (4, 12). - mean_angle (float): Mean value of the angle in each vertex. The angle - is measured in radians. Default: 2 * math.pi / 5. - angle_range (float): Range of the random angle. - Default: 2 * math.pi / 15. - brush_width (int | tuple[int]): (min_width, max_width). If only give - an integer, we will fix the width of brush. Default: (12, 40). - max_loops (int): The max number of for loops of drawing strokes. - dtype (str): Indicate the data type of returned masks. - Default: 'uint8'. - - Returns: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - img_h, img_w = img_shape[:2] - if isinstance(num_vertices, int): - min_num_vertices, max_num_vertices = num_vertices, num_vertices + 1 - elif isinstance(num_vertices, tuple): - min_num_vertices, max_num_vertices = num_vertices - else: - raise TypeError('The type of num_vertices should be int' - f'or tuple[int], but got type: {num_vertices}') - - if isinstance(brush_width, tuple): - min_width, max_width = brush_width - elif isinstance(brush_width, int): - min_width, max_width = brush_width, brush_width + 1 - else: - raise TypeError('The type of brush_width should be int' - f'or tuple[int], but got type: {brush_width}') - - average_radius = math.sqrt(img_h * img_h + img_w * img_w) / 8 - mask = Image.new('L', (img_w, img_h), 0) - - loop_num = np.random.randint(1, max_loops) - num_vertex_list = np.random.randint( - min_num_vertices, max_num_vertices, size=loop_num) - angle_min_list = np.random.uniform(0, angle_range, size=loop_num) - angle_max_list = np.random.uniform(0, angle_range, size=loop_num) - - for loop_n in range(loop_num): - num_vertex = num_vertex_list[loop_n] - angle_min = mean_angle - angle_min_list[loop_n] - angle_max = mean_angle + angle_max_list[loop_n] - angles = [] - vertex = [] - - # set random angle on each vertex - angles = np.random.uniform(angle_min, angle_max, size=num_vertex) - reverse_mask = (np.arange(num_vertex, dtype=np.float32) % 2) == 0 - angles[reverse_mask] = 2 * math.pi - angles[reverse_mask] - - h, w = mask.size - - # set random vertices - vertex.append((np.random.randint(0, w), np.random.randint(0, h))) - r_list = np.random.normal( - loc=average_radius, scale=average_radius // 2, size=num_vertex) - for i in range(num_vertex): - r = np.clip(r_list[i], 0, 2 * average_radius) - new_x = np.clip(vertex[-1][0] + r * math.cos(angles[i]), 0, w) - new_y = np.clip(vertex[-1][1] + r * math.sin(angles[i]), 0, h) - vertex.append((int(new_x), int(new_y))) - # draw brush strokes according to the vertex and angle list - draw = ImageDraw.Draw(mask) - width = np.random.randint(min_width, max_width) - draw.line(vertex, fill=1, width=width) - for v in vertex: - draw.ellipse((v[0] - width // 2, v[1] - width // 2, - v[0] + width // 2, v[1] + width // 2), - fill=1) - # randomly flip the mask - if np.random.normal() > 0: - mask.transpose(Image.FLIP_LEFT_RIGHT) - if np.random.normal() > 0: - mask.transpose(Image.FLIP_TOP_BOTTOM) - mask = np.array(mask).astype(dtype=getattr(np, dtype)) - mask = mask[:, :, None] - return mask - - -def random_irregular_mask(img_shape, - num_vertices=(4, 8), - max_angle=4, - length_range=(10, 100), - brush_width=(10, 40), - dtype='uint8'): - """Generate random irregular masks. - - This is a modified version of free-form mask implemented in - 'brush_stroke_mask'. - - We prefer to use `uint8` as the data type of masks, which may be different - from other codes in the community. - - TODO: Rewrite the implementation of this function. - - Args: - img_shape (tuple[int]): Size of the image. - num_vertices (int | tuple[int]): Min and max number of vertices. If - only give an integer, we will fix the number of vertices. - Default: (4, 8). - max_angle (float): Max value of angle at each vertex. Default 4.0. - length_range (int | tuple[int]): (min_length, max_length). If only give - an integer, we will fix the length of brush. Default: (10, 100). - brush_width (int | tuple[int]): (min_width, max_width). If only give - an integer, we will fix the width of brush. Default: (10, 40). - dtype (str): Indicate the data type of returned masks. Default: 'uint8' - - Returns: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - h, w = img_shape[:2] - - mask = np.zeros((h, w), dtype=dtype) - if isinstance(length_range, int): - min_length, max_length = length_range, length_range + 1 - elif isinstance(length_range, tuple): - min_length, max_length = length_range - else: - raise TypeError('The type of length_range should be int' - f'or tuple[int], but got type: {length_range}') - if isinstance(num_vertices, int): - min_num_vertices, max_num_vertices = num_vertices, num_vertices + 1 - elif isinstance(num_vertices, tuple): - min_num_vertices, max_num_vertices = num_vertices - else: - raise TypeError('The type of num_vertices should be int' - f'or tuple[int], but got type: {num_vertices}') - - if isinstance(brush_width, int): - min_brush_width, max_brush_width = brush_width, brush_width + 1 - elif isinstance(brush_width, tuple): - min_brush_width, max_brush_width = brush_width - else: - raise TypeError('The type of brush_width should be int' - f'or tuple[int], but got type: {brush_width}') - - num_v = np.random.randint(min_num_vertices, max_num_vertices) - - for i in range(num_v): - start_x = np.random.randint(w) - start_y = np.random.randint(h) - # from the start point, randomly setlect n \in [1, 6] directions. - direction_num = np.random.randint(1, 6) - angle_list = np.random.randint(0, max_angle, size=direction_num) - length_list = np.random.randint( - min_length, max_length, size=direction_num) - brush_width_list = np.random.randint( - min_brush_width, max_brush_width, size=direction_num) - for direct_n in range(direction_num): - angle = 0.01 + angle_list[direct_n] - if i % 2 == 0: - angle = 2 * math.pi - angle - length = length_list[direct_n] - brush_w = brush_width_list[direct_n] - # compute end point according to the random angle - end_x = (start_x + length * np.sin(angle)).astype(np.int32) - end_y = (start_y + length * np.cos(angle)).astype(np.int32) - - cv2.line(mask, (start_y, start_x), (end_y, end_x), 1, brush_w) - start_x, start_y = end_x, end_y - mask = np.expand_dims(mask, axis=2) - - return mask - - -def get_irregular_mask(img_shape, area_ratio_range=(0.15, 0.5), **kwargs): - """Get irregular mask with the constraints in mask ratio - - Args: - img_shape (tuple[int]): Size of the image. - area_ratio_range (tuple(float)): Contain the minimum and maximum area - ratio. Default: (0.15, 0.5). - - Returns: - numpy.ndarray: Mask in the shape of (h, w, 1). - """ - - mask = random_irregular_mask(img_shape, **kwargs) - min_ratio, max_ratio = area_ratio_range - - while not min_ratio < (np.sum(mask) / - (img_shape[0] * img_shape[1])) < max_ratio: - mask = random_irregular_mask(img_shape, **kwargs) - - return mask diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/misc.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/misc.py deleted file mode 100755 index 21c5d4770..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/misc.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math - -import numpy as np -import torch -from torchvision.utils import make_grid - - -def tensor2img(tensor, out_type=np.uint8, min_max=(0, 1)): - """Convert torch Tensors into image numpy arrays. - - After clamping to (min, max), image values will be normalized to [0, 1]. - - For different tensor shapes, this function will have different behaviors: - - 1. 4D mini-batch Tensor of shape (N x 3/1 x H x W): - Use `make_grid` to stitch images in the batch dimension, and then - convert it to numpy array. - 2. 3D Tensor of shape (3/1 x H x W) and 2D Tensor of shape (H x W): - Directly change to numpy array. - - Note that the image channel in input tensors should be RGB order. This - function will convert it to cv2 convention, i.e., (H x W x C) with BGR - order. - - Args: - tensor (Tensor | list[Tensor]): Input tensors. - out_type (numpy type): Output types. If ``np.uint8``, transform outputs - to uint8 type with range [0, 255]; otherwise, float type with - range [0, 1]. Default: ``np.uint8``. - min_max (tuple): min and max values for clamp. - - Returns: - (Tensor | list[Tensor]): 3D ndarray of shape (H x W x C) or 2D ndarray - of shape (H x W). - """ - if not (torch.is_tensor(tensor) or - (isinstance(tensor, list) - and all(torch.is_tensor(t) for t in tensor))): - raise TypeError( - f'tensor or list of tensors expected, got {type(tensor)}') - - if torch.is_tensor(tensor): - tensor = [tensor] - result = [] - for _tensor in tensor: - # Squeeze two times so that: - # 1. (1, 1, h, w) -> (h, w) or - # 3. (1, 3, h, w) -> (3, h, w) or - # 2. (n>1, 3/1, h, w) -> (n>1, 3/1, h, w) - _tensor = _tensor.squeeze(0).squeeze(0) - _tensor = _tensor.float().detach().cpu().clamp_(*min_max) - _tensor = (_tensor - min_max[0]) / (min_max[1] - min_max[0]) - n_dim = _tensor.dim() - if n_dim == 4: - img_np = make_grid( - _tensor, nrow=int(math.sqrt(_tensor.size(0))), - normalize=False).numpy() - img_np = np.transpose(img_np[[2, 1, 0], :, :], (1, 2, 0)) - elif n_dim == 3: - img_np = _tensor.numpy() - img_np = np.transpose(img_np[[2, 1, 0], :, :], (1, 2, 0)) - elif n_dim == 2: - img_np = _tensor.numpy() - else: - raise ValueError('Only support 4D, 3D or 2D tensor. ' - f'But received with dimension: {n_dim}') - if out_type == np.uint8: - # Unlike MATLAB, numpy.unit8() WILL NOT round by default. - img_np = (img_np * 255.0).round() - img_np = img_np.astype(out_type) - result.append(img_np) - result = result[0] if len(result) == 1 else result - return result diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/optimizer/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/optimizer/__init__.py deleted file mode 100755 index e1c477dd1..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/optimizer/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .builder import build_optimizers - -__all__ = ['build_optimizers'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/optimizer/builder.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/optimizer/builder.py deleted file mode 100755 index 2edf94dad..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/optimizer/builder.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import build_optimizer - - -def build_optimizers(model, cfgs): - """Build multiple optimizers from configs. - - If `cfgs` contains several dicts for optimizers, then a dict for each - constructed optimizers will be returned. - If `cfgs` only contains one optimizer config, the constructed optimizer - itself will be returned. - - For example, - - 1) Multiple optimizer configs: - - .. code-block:: python - - optimizer_cfg = dict( - model1=dict(type='SGD', lr=lr), - model2=dict(type='SGD', lr=lr)) - - The return dict is - ``dict('model1': torch.optim.Optimizer, 'model2': torch.optim.Optimizer)`` - - 2) Single optimizer config: - - .. code-block:: python - - optimizer_cfg = dict(type='SGD', lr=lr) - - The return is ``torch.optim.Optimizer``. - - Args: - model (:obj:`nn.Module`): The model with parameters to be optimized. - cfgs (dict): The config dict of the optimizer. - - Returns: - dict[:obj:`torch.optim.Optimizer`] | :obj:`torch.optim.Optimizer`: - The initialized optimizers. - """ - optimizers = {} - if hasattr(model, 'module'): - model = model.module - # determine whether 'cfgs' has several dicts for optimizers - is_dict_of_dict = True - for key, cfg in cfgs.items(): - if not isinstance(cfg, dict): - is_dict_of_dict = False - - if is_dict_of_dict: - for key, cfg in cfgs.items(): - cfg_ = cfg.copy() - module = getattr(model, key) - optimizers[key] = build_optimizer(module, cfg_) - return optimizers - - return build_optimizer(model, cfgs) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/scheduler/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/scheduler/__init__.py deleted file mode 100755 index af0458206..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/scheduler/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .lr_updater import LinearLrUpdaterHook, ReduceLrUpdaterHook - -__all__ = ['LinearLrUpdaterHook', 'ReduceLrUpdaterHook'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/scheduler/lr_updater.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/scheduler/lr_updater.py deleted file mode 100755 index 0677373c6..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/scheduler/lr_updater.py +++ /dev/null @@ -1,304 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import HOOKS, LrUpdaterHook - - -@HOOKS.register_module() -class LinearLrUpdaterHook(LrUpdaterHook): - """Linear learning rate scheduler for image generation. - - In the beginning, the learning rate is 'base_lr' defined in mmcv. - We give a target learning rate 'target_lr' and a start point 'start' - (iteration / epoch). Before 'start', we fix learning rate as 'base_lr'; - After 'start', we linearly update learning rate to 'target_lr'. - - Args: - target_lr (float): The target learning rate. Default: 0. - start (int): The start point (iteration / epoch, specified by args - 'by_epoch' in its parent class in mmcv) to update learning rate. - Default: 0. - interval (int): The interval to update the learning rate. Default: 1. - """ - - def __init__(self, target_lr=0, start=0, interval=1, **kwargs): - super().__init__(**kwargs) - self.target_lr = target_lr - self.start = start - self.interval = interval - - def get_lr(self, runner, base_lr): - """Calculates the learning rate. - - Args: - runner (object): The passed runner. - base_lr (float): Base learning rate. - - Returns: - float: Current learning rate. - """ - if self.by_epoch: - progress = runner.epoch - max_progress = runner.max_epochs - else: - progress = runner.iter - max_progress = runner.max_iters - assert max_progress >= self.start - - if max_progress == self.start: - return base_lr - - # Before 'start', fix lr; After 'start', linearly update lr. - factor = (max(0, progress - self.start) // self.interval) / ( - (max_progress - self.start) // self.interval) - return base_lr + (self.target_lr - base_lr) * factor - - -@HOOKS.register_module() -class ReduceLrUpdaterHook(LrUpdaterHook): - """ReduceLROnPlateau Scheduler. - - Reduce learning rate when a metric has stopped improving. This scheduler - reads a metrics quantity and if no improvement is seen for a 'patience' - number of epochs, the learning rate is reduced. - - Args: - val_metric (str, optional): Metrics to be evaluated. If val_metric is - None, the metrics will be loss value. Default: None. - mode (str, optional): One of `min`, `max`. In `min` mode, lr will - be reduced when the quantity monitored has stopped - decreasing; in `max` mode it will be reduced when the - quantity monitored has stopped increasing. Default: 'min'. - factor (float, optional): Factor by which the learning rate will be - reduced. new_lr = lr * factor. Default: 0.1. - patience (int, optional): Number of epochs with no improvement after - which learning rate will be reduced. For example, if - `patience = 2`, then we will ignore the first 2 epochs - with no improvement, and will only decrease the LR after the - 3rd epoch if the loss still hasn't improved then. - Default: 10. - threshold (float, optional): Threshold for measuring the new optimum, - to only focus on significant changes. Default: 1e-4. - threshold_mode (str, optional): One of `rel`, `abs`. In `rel` mode, - dynamic_threshold = best * ( 1 + threshold ) in 'max' - mode or best * ( 1 - threshold ) in `min` mode. - In `abs` mode, dynamic_threshold = best + threshold in - `max` mode or best - threshold in `min` mode. Default: 'rel'. - cooldown (int, optional): Number of epochs to wait before resuming - normal operation after lr has been reduced. Default: 0. - min_lr (float, optional): Minimum LR value to keep. If LR after decay - is lower than `min_lr`, it will be clipped to this value. - Default: 0. - eps (float, optional): Minimal decay applied to lr. If the difference - between new and old lr is smaller than eps, the update is - ignored. Default: 1e-8. - verbose (bool): If ``True``, prints a message to stdout for - each update. Default: ``False``. - epoch_base_valid (None | Bool): Whether use epoch base valid. - If `None`, follow `by_epoch` (inherited from `LrUpdaterHook`). - Default: None. - """ - - def __init__(self, - val_metric: str = None, - mode: str = 'min', - factor: float = 0.1, - patience: int = 10, - threshold: float = 1e-4, - threshold_mode: str = 'rel', - cooldown: int = 0, - min_lr: float = 0., - eps: float = 1e-8, - verbose: bool = False, - epoch_base_valid=None, - **kwargs): - - self.val_metric = val_metric - - if mode not in ['min', 'max']: - raise ValueError( - 'mode must be one of "min" or "max", instead got {mode}') - self.mode = mode - - if factor >= 1.0 or factor < 0: - raise ValueError('Factor should be < 1.0 and >=0') - self.factor = factor - - self.patience = patience - self.threshold = threshold - - if threshold_mode not in ['rel', 'abs']: - raise ValueError('thresh_mode must be one of "rel" or "abs",' - f'instead got {threshold_mode}') - self.threshold_mode = threshold_mode - - self.cooldown = cooldown - self.cooldown_counter = 0 - self.best = None - self.num_bad_epochs = None - self.mode_worse = None # the worse value for the chosen mode - self.min_lr = min_lr - self.eps = eps - self.verbose = verbose - self.last_epoch = 0 - self._init_is_better(self.mode) - self._reset() - - super().__init__(**kwargs) - if epoch_base_valid is None: - self.epoch_base_valid = self.by_epoch - else: - self.epoch_base_valid = epoch_base_valid - - def get_lr(self, regular_lr, optimizer_name): - if self.num_bad_epochs > self.patience: - self.cooldown_counter = self.cooldown - self.num_bad_epochs = 0 - if regular_lr - regular_lr * self.factor > self.eps: - new_lr = max(regular_lr * self.factor, self.min_lr) - if self.verbose: - print(f'Reducing learning rate of {optimizer_name} from ' - f'{regular_lr:.4e} to {new_lr:.4e}.') - else: - new_lr = regular_lr - return new_lr - else: - return regular_lr - - def get_regular_lr(self, runner): - if not self.regular_lr: - self.regular_lr = self.base_lr - if isinstance(runner.optimizer, dict): - lr_groups = {} - for k in runner.optimizer.keys(): - _lr_group = [ - self.get_lr(_regular_lr, k) - for _regular_lr in self.regular_lr[k] - ] - lr_groups.update({k: _lr_group}) - else: - lr_groups = [ - self.get_lr(_regular_lr, 'generator') - for _regular_lr in self.regular_lr - ] - self.regular_lr = lr_groups - return lr_groups - - def _init_is_better(self, mode): - if mode == 'min': - self.mode_worse = float('inf') - else: - self.mode_worse = float('-inf') - - def _reset(self): - self.best = self.mode_worse - self.cooldown_counter = 0 - self.num_bad_epochs = 0 - - def is_better(self, a, best): - if self.mode == 'min' and self.threshold_mode == 'rel': - rel_epsilon = 1. - self.threshold - return a < best * rel_epsilon - elif self.mode == 'min' and self.threshold_mode == 'abs': - return a < best - self.threshold - elif self.mode == 'max' and self.threshold_mode == 'rel': - rel_epsilon = 1. + self.threshold - return a > best * rel_epsilon - else: - return a > best + self.threshold - - @property - def in_cooldown(self): - return self.cooldown_counter > 0 - - def after_train_epoch(self, runner): - if not self.by_epoch: - return - cur_epoch = runner.epoch - if self.warmup is not None and self.warmup_by_epoch: - if cur_epoch <= self.warmup_epochs: - return - # If val_metric is None, we check training loss to reduce learning - # rate. - if self.val_metric is None: - current = runner.outputs['log_vars']['loss'] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'train_epoch--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') - - def after_train_iter(self, runner): - if self.by_epoch: - return - cur_iter = runner.iter - if self.warmup_epochs is not None and cur_iter <= self.warmup_iters: - return - # If val_metric is None, we check training loss to reduce learning - # rate. - if self.val_metric is None: - current = runner.outputs['log_vars']['loss'] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'train_iter--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') - - def after_val_epoch(self, runner): - if not self.by_epoch and not self.epoch_base_valid: - return - cur_epoch = runner.epoch - if self.warmup is not None and self.warmup_by_epoch: - if cur_epoch <= self.warmup_epochs: - return - # If val_metric is not None, we check its value to reduce learning - # rate. - if self.val_metric is not None: - current = runner.log_buffer.output[self.val_metric] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'val_epoch--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') - - def after_val_iter(self, runner): - if self.by_epoch or self.epoch_base_valid: - return - cur_iter = runner.iter - if self.warmup_epochs is not None and cur_iter <= self.warmup_iters: - return - # If val_metric is not None, we check its value to reduce learning - # rate. - if self.val_metric is not None: - current = runner.eval_result[self.val_metric] - if self.is_better(current, self.best): - self.best = current - self.num_bad_epochs = 0 - else: - self.num_bad_epochs += 1 - - if self.in_cooldown: - self.cooldown_counter -= 1 - self.num_bad_epochs = 0 - print(f'val_iter--current {current:.6f} best {self.best:.6f}, ' - f'num_bad_epochs {self.num_bad_epochs}, ' - f'cooldown {self.in_cooldown} {self.cooldown_counter}') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/utils/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/utils/__init__.py deleted file mode 100755 index f894e827c..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/utils/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .dist_utils import sync_random_seed - -__all__ = ['sync_random_seed'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/utils/dist_utils.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/utils/dist_utils.py deleted file mode 100755 index 8a3af5bb0..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/core/utils/dist_utils.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.distributed as dist -from mmcv.runner import get_dist_info - - -def sync_random_seed(seed=None, device='cuda'): - """Make sure different ranks share the same seed. - All workers must call this function, otherwise it will deadlock. - This method is generally used in `DistributedSampler`, - because the seed should be identical across all processes - in the distributed group. - Args: - seed (int, Optional): The seed. Default to None. - device (str): The device where the seed will be put on. - Default to 'cuda'. - Returns: - int: Seed to be used. - """ - if seed is None: - seed = np.random.randint(2**31) - assert isinstance(seed, int) - - rank, world_size = get_dist_info() - - if world_size == 1: - return seed - - if rank == 0: - random_num = torch.tensor(seed, dtype=torch.int32, device=device) - else: - random_num = torch.tensor(0, dtype=torch.int32, device=device) - dist.broadcast(random_num, src=0) - return random_num.item() diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/__init__.py deleted file mode 100755 index ef52be5ec..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .base_dataset import BaseDataset -from .base_sr_dataset import BaseSRDataset -from .builder import build_dataloader, build_dataset -from .dataset_wrappers import RepeatDataset -from .registry import DATASETS, PIPELINES -from .sr_folder_multiple_gt_dataset import SRFolderMultipleGTDataset - diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/base_dataset.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/base_dataset.py deleted file mode 100755 index c8ffea6eb..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/base_dataset.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -from abc import ABCMeta, abstractmethod - -from torch.utils.data import Dataset - -from .pipelines import Compose - - -class BaseDataset(Dataset, metaclass=ABCMeta): - """Base class for datasets. - - All datasets should subclass it. - All subclasses should overwrite: - - ``load_annotations``, supporting to load information and generate - image lists. - - Args: - pipeline (list[dict | callable]): A sequence of data transforms. - test_mode (bool): If True, the dataset will work in test mode. - Otherwise, in train mode. - """ - - def __init__(self, pipeline, test_mode=False): - super().__init__() - self.test_mode = test_mode - self.pipeline = Compose(pipeline) - - @abstractmethod - def load_annotations(self): - """Abstract function for loading annotation. - - All subclasses should overwrite this function - """ - - def prepare_train_data(self, idx): - """Prepare training data. - - Args: - idx (int): Index of the training batch data. - - Returns: - dict: Returned training batch. - """ - results = copy.deepcopy(self.data_infos[idx]) - return self.pipeline(results) - - def prepare_test_data(self, idx): - """Prepare testing data. - - Args: - idx (int): Index for getting each testing batch. - - Returns: - Tensor: Returned testing batch. - """ - results = copy.deepcopy(self.data_infos[idx]) - return self.pipeline(results) - - def __len__(self): - """Length of the dataset. - - Returns: - int: Length of the dataset. - """ - return len(self.data_infos) - - def __getitem__(self, idx): - """Get item at each call. - - Args: - idx (int): Index for getting each item. - """ - if self.test_mode: - return self.prepare_test_data(idx) - - return self.prepare_train_data(idx) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/base_sr_dataset.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/base_sr_dataset.py deleted file mode 100755 index 75b664000..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/base_sr_dataset.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import os.path as osp -from collections import defaultdict -from pathlib import Path - -from mmcv import scandir - -from .base_dataset import BaseDataset - -IMG_EXTENSIONS = ('.jpg', '.JPG', '.jpeg', '.JPEG', '.png', '.PNG', '.ppm', - '.PPM', '.bmp', '.BMP', '.tif', '.TIF', '.tiff', '.TIFF') - - -class BaseSRDataset(BaseDataset): - """Base class for super resolution datasets. - """ - - def __init__(self, pipeline, scale, test_mode=False): - super().__init__(pipeline, test_mode) - self.scale = scale - - @staticmethod - def scan_folder(path): - """Obtain image path list (including sub-folders) from a given folder. - - Args: - path (str | :obj:`Path`): Folder path. - - Returns: - list[str]: image list obtained form given folder. - """ - - if isinstance(path, (str, Path)): - path = str(path) - else: - raise TypeError("'path' must be a str or a Path object, " - f'but received {type(path)}.') - - images = list(scandir(path, suffix=IMG_EXTENSIONS, recursive=True)) - images = [osp.join(path, v) for v in images] - assert images, f'{path} has no valid image file.' - return images - - def __getitem__(self, idx): - """Get item at each call. - - Args: - idx (int): Index for getting each item. - """ - results = copy.deepcopy(self.data_infos[idx]) - results['scale'] = self.scale - return self.pipeline(results) - - def evaluate(self, results, logger=None): - """Evaluate with different metrics. - - Args: - results (list[tuple]): The output of forward_test() of the model. - - Return: - dict: Evaluation results dict. - """ - if not isinstance(results, list): - raise TypeError(f'results must be a list, but got {type(results)}') - assert len(results) == len(self), ( - 'The length of results is not equal to the dataset len: ' - f'{len(results)} != {len(self)}') - # results = [res['eval_result'] for res in results] # a list of dict - eval_result = defaultdict(list) # a dict of list - - for res in results: - for metric, val in res.items(): - eval_result[metric].append(val) - for metric, val_list in eval_result.items(): - assert len(val_list) == len(self), ( - f'Length of evaluation result of {metric} is {len(val_list)}, ' - f'should be {len(self)}') - - # average the results - eval_result = { - metric: sum(values) / len(self) - for metric, values in eval_result.items() - } - - return eval_result diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/builder.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/builder.py deleted file mode 100755 index d7a18fc11..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/builder.py +++ /dev/null @@ -1,181 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import platform -import random -from functools import partial - -import numpy as np -import torch -from mmcv.parallel import collate -from mmcv.runner import get_dist_info -from mmcv.utils import build_from_cfg -from packaging import version -from torch.utils.data import ConcatDataset, DataLoader - -from .dataset_wrappers import RepeatDataset -from .registry import DATASETS -from .samplers import DistributedSampler - -if platform.system() != 'Windows': - # https://github.com/pytorch/pytorch/issues/973 - import resource - rlimit = resource.getrlimit(resource.RLIMIT_NOFILE) - base_soft_limit = rlimit[0] - hard_limit = rlimit[1] - soft_limit = min(max(4096, base_soft_limit), hard_limit) - resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit)) - - -def _concat_dataset(cfg, default_args=None): - """Concat datasets with different ann_file but the same type. - - Args: - cfg (dict): The config of dataset. - default_args (dict, optional): Default initialization arguments. - Default: None. - - Returns: - Dataset: The concatenated dataset. - """ - ann_files = cfg['ann_file'] - - datasets = [] - num_dset = len(ann_files) - for i in range(num_dset): - data_cfg = copy.deepcopy(cfg) - data_cfg['ann_file'] = ann_files[i] - datasets.append(build_dataset(data_cfg, default_args)) - - return ConcatDataset(datasets) - - -def build_dataset(cfg, default_args=None): - """Build a dataset from config dict. - - It supports a variety of dataset config. If ``cfg`` is a Sequential (list - or dict), it will be a concatenated dataset of the datasets specified by - the Sequential. If it is a ``RepeatDataset``, then it will repeat the - dataset ``cfg['dataset']`` for ``cfg['times']`` times. If the ``ann_file`` - of the dataset is a Sequential, then it will build a concatenated dataset - with the same dataset type but different ``ann_file``. - - Args: - cfg (dict): Config dict. It should at least contain the key "type". - default_args (dict, optional): Default initialization arguments. - Default: None. - - Returns: - Dataset: The constructed dataset. - """ - if isinstance(cfg, (list, tuple)): - dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg]) - elif cfg['type'] == 'RepeatDataset': - dataset = RepeatDataset( - build_dataset(cfg['dataset'], default_args), cfg['times']) - elif isinstance(cfg.get('ann_file'), (list, tuple)): - dataset = _concat_dataset(cfg, default_args) - else: - dataset = build_from_cfg(cfg, DATASETS, default_args) - - return dataset - - -def build_dataloader(dataset, - samples_per_gpu, - workers_per_gpu, - num_gpus=1, - dist=True, - shuffle=True, - seed=None, - drop_last=False, - pin_memory=True, - persistent_workers=False, - **kwargs): - """Build PyTorch DataLoader. - - In distributed training, each GPU/process has a dataloader. - In non-distributed training, there is only one dataloader for all GPUs. - - Args: - dataset (:obj:`Dataset`): A PyTorch dataset. - samples_per_gpu (int): Number of samples on each GPU, i.e., - batch size of each GPU. - workers_per_gpu (int): How many subprocesses to use for data - loading for each GPU. - num_gpus (int): Number of GPUs. Only used in non-distributed - training. Default: 1. - dist (bool): Distributed training/test or not. Default: True. - shuffle (bool): Whether to shuffle the data at every epoch. - Default: True. - seed (int | None): Seed to be used. Default: None. - drop_last (bool): Whether to drop the last incomplete batch in epoch. - Default: False - pin_memory (bool): Whether to use pin_memory in DataLoader. - Default: True - persistent_workers (bool): If True, the data loader will not shutdown - the worker processes after a dataset has been consumed once. - This allows to maintain the workers Dataset instances alive. - The argument also has effect in PyTorch>=1.7.0. - Default: True - kwargs (dict, optional): Any keyword argument to be used to initialize - DataLoader. - - Returns: - DataLoader: A PyTorch dataloader. - """ - rank, world_size = get_dist_info() - if dist: - sampler = DistributedSampler( - dataset, - world_size, - rank, - shuffle=shuffle, - samples_per_gpu=samples_per_gpu, - seed=seed) - shuffle = False - batch_size = samples_per_gpu - num_workers = workers_per_gpu - else: - sampler = None - batch_size = num_gpus * samples_per_gpu - num_workers = num_gpus * workers_per_gpu - - init_fn = partial( - worker_init_fn, num_workers=num_workers, rank=rank, - seed=seed) if seed is not None else None - - if version.parse(torch.__version__) >= version.parse('1.7.0'): - kwargs['persistent_workers'] = persistent_workers - - data_loader = DataLoader( - dataset, - batch_size=batch_size, - sampler=sampler, - num_workers=num_workers, - collate_fn=partial(collate, samples_per_gpu=samples_per_gpu), - pin_memory=pin_memory, - shuffle=shuffle, - worker_init_fn=init_fn, - drop_last=drop_last, - **kwargs) - - return data_loader - - -def worker_init_fn(worker_id, num_workers, rank, seed): - """Function to initialize each worker. - - The seed of each worker equals to - ``num_worker * rank + worker_id + user_seed``. - - Args: - worker_id (int): Id for each worker. - num_workers (int): Number of workers. - rank (int): Rank in distributed training. - seed (int): Random seed. - """ - - worker_seed = num_workers * rank + worker_id + seed - np.random.seed(worker_seed) - random.seed(worker_seed) - torch.manual_seed(worker_seed) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/dataset_wrappers.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/dataset_wrappers.py deleted file mode 100755 index 3dbca31ea..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/dataset_wrappers.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .registry import DATASETS - - -@DATASETS.register_module() -class RepeatDataset: - """A wrapper of repeated dataset. - - The length of repeated dataset will be `times` larger than the original - dataset. This is useful when the data loading time is long but the dataset - is small. Using RepeatDataset can reduce the data loading time between - epochs. - - Args: - dataset (:obj:`Dataset`): The dataset to be repeated. - times (int): Repeat times. - """ - - def __init__(self, dataset, times): - self.dataset = dataset - self.times = times - - self._ori_len = len(self.dataset) - - def __getitem__(self, idx): - """Get item at each call. - - Args: - idx (int): Index for getting each item. - """ - return self.dataset[idx % self._ori_len] - - def __len__(self): - """Length of the dataset. - - Returns: - int: Length of the dataset. - """ - return self.times * self._ori_len diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/__init__.py deleted file mode 100755 index c2e2a7e60..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .augmentation import (BinarizeImage, ColorJitter, CopyValues, Flip, - GenerateFrameIndices, - GenerateFrameIndiceswithPadding, - GenerateSegmentIndices, MirrorSequence, Pad, - Quantize, RandomAffine, RandomJitter, - RandomMaskDilation, RandomTransposeHW, Resize, - TemporalReverse, UnsharpMasking) -from .compose import Compose -from .crop import (Crop, CropAroundCenter, CropAroundFg, CropAroundUnknown, - CropLike, FixedCrop, ModCrop, PairedRandomCrop, - RandomResizedCrop) -from .formating import (Collect, FormatTrimap, GetMaskedImage, ImageToTensor, - ToTensor) -from .loading import (GetSpatialDiscountMask, LoadImageFromFile, - LoadImageFromFileList, LoadMask, LoadPairedImageFromFile, - RandomLoadResizeBg) -from .matlab_like_resize import MATLABLikeResize -from .normalization import Normalize, RescaleToZeroOne -from .random_degradations import (DegradationsWithShuffle, RandomBlur, - RandomJPEGCompression, RandomNoise, - RandomResize, RandomVideoCompression) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/augmentation.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/augmentation.py deleted file mode 100755 index e16f80a1c..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/augmentation.py +++ /dev/null @@ -1,1332 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy -import math -import numbers -import os -import os.path as osp -import random - -import cv2 -import mmcv -import numpy as np -import torch -import torchvision.transforms as transforms -from PIL import Image - -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class Resize: - """Resize data to a specific size for training or resize the images to fit - the network input regulation for testing. - - When used for resizing images to fit network input regulation, the case is - that a network may have several downsample and then upsample operation, - then the input height and width should be divisible by the downsample - factor of the network. - For example, the network would downsample the input for 5 times with - stride 2, then the downsample factor is 2^5 = 32 and the height - and width should be divisible by 32. - - Required keys are the keys in attribute "keys", added or modified keys are - "keep_ratio", "scale_factor", "interpolation" and the - keys in attribute "keys". - - All keys in "keys" should have the same shape. "test_trans" is used to - record the test transformation to align the input's shape. - - Args: - keys (list[str]): The images to be resized. - scale (float | tuple[int]): If scale is tuple[int], target spatial - size (h, w). Otherwise, target spatial size is scaled by input - size. - Note that when it is used, `size_factor` and `max_size` are - useless. Default: None - keep_ratio (bool): If set to True, images will be resized without - changing the aspect ratio. Otherwise, it will resize images to a - given size. Default: False. - Note that it is used togher with `scale`. - size_factor (int): Let the output shape be a multiple of size_factor. - Default:None. - Note that when it is used, `scale` should be set to None and - `keep_ratio` should be set to False. - max_size (int): The maximum size of the longest side of the output. - Default:None. - Note that it is used togher with `size_factor`. - interpolation (str): Algorithm used for interpolation: - "nearest" | "bilinear" | "bicubic" | "area" | "lanczos". - Default: "bilinear". - backend (str | None): The image resize backend type. Options are `cv2`, - `pillow`, `None`. If backend is None, the global imread_backend - specified by ``mmcv.use_backend()`` will be used. - Default: None. - output_keys (list[str] | None): The resized images. Default: None - Note that if it is not `None`, its length should be equal to keys. - """ - - def __init__(self, - keys, - scale=None, - keep_ratio=False, - size_factor=None, - max_size=None, - interpolation='bilinear', - backend=None, - output_keys=None): - assert keys, 'Keys should not be empty.' - if output_keys: - assert len(output_keys) == len(keys) - else: - output_keys = keys - if size_factor: - assert scale is None, ('When size_factor is used, scale should ', - f'be None. But received {scale}.') - assert keep_ratio is False, ('When size_factor is used, ' - 'keep_ratio should be False.') - if max_size: - assert size_factor is not None, ( - 'When max_size is used, ' - f'size_factor should also be set. But received {size_factor}.') - if isinstance(scale, float): - if scale <= 0: - raise ValueError(f'Invalid scale {scale}, must be positive.') - elif mmcv.is_tuple_of(scale, int): - max_long_edge = max(scale) - max_short_edge = min(scale) - if max_short_edge == -1: - # assign np.inf to long edge for rescaling short edge later. - scale = (np.inf, max_long_edge) - elif scale is not None: - raise TypeError( - f'Scale must be None, float or tuple of int, but got ' - f'{type(scale)}.') - self.keys = keys - self.output_keys = output_keys - self.scale = scale - self.size_factor = size_factor - self.max_size = max_size - self.keep_ratio = keep_ratio - self.interpolation = interpolation - self.backend = backend - - def _resize(self, img): - if self.keep_ratio: - img, self.scale_factor = mmcv.imrescale( - img, - self.scale, - return_scale=True, - interpolation=self.interpolation, - backend=self.backend) - else: - img, w_scale, h_scale = mmcv.imresize( - img, - self.scale, - return_scale=True, - interpolation=self.interpolation, - backend=self.backend) - self.scale_factor = np.array((w_scale, h_scale), dtype=np.float32) - return img - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - if self.size_factor: - h, w = results[self.keys[0]].shape[:2] - new_h = h - (h % self.size_factor) - new_w = w - (w % self.size_factor) - if self.max_size: - new_h = min(self.max_size - (self.max_size % self.size_factor), - new_h) - new_w = min(self.max_size - (self.max_size % self.size_factor), - new_w) - self.scale = (new_w, new_h) - for key, out_key in zip(self.keys, self.output_keys): - results[out_key] = self._resize(results[key]) - if len(results[out_key].shape) == 2: - results[out_key] = np.expand_dims(results[out_key], axis=2) - - results['scale_factor'] = self.scale_factor - results['keep_ratio'] = self.keep_ratio - results['interpolation'] = self.interpolation - results['backend'] = self.backend - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(keys={self.keys}, output_keys={self.output_keys}, ' - f'scale={self.scale}, ' - f'keep_ratio={self.keep_ratio}, size_factor={self.size_factor}, ' - f'max_size={self.max_size}, interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class RandomRotation: - """Rotate the image by a randomly-chosen angle, measured in degree. - - Args: - keys (list[str]): The images to be rotated. - degrees (tuple[float] | tuple[int] | float | int): If it is a tuple, - it represents a range (min, max). If it is a float or int, - the range is constructed as (-degrees, degrees). - """ - - def __init__(self, keys, degrees): - if isinstance(degrees, (int, float)): - if degrees < 0.0: - raise ValueError('Degrees must be positive if it is a number.') - else: - degrees = (-degrees, degrees) - elif not mmcv.is_tuple_of(degrees, (int, float)): - raise TypeError(f'Degrees must be float | int or tuple of float | ' - 'int, but got ' - f'{type(degrees)}.') - - self.keys = keys - self.degrees = degrees - - def __call__(self, results): - angle = random.uniform(self.degrees[0], self.degrees[1]) - - for k in self.keys: - results[k] = mmcv.imrotate(results[k], angle) - if results[k].ndim == 2: - results[k] = np.expand_dims(results[k], axis=2) - results['degrees'] = self.degrees - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, degrees={self.degrees})') - return repr_str - - -@PIPELINES.register_module() -class Flip: - """Flip the input data with a probability. - - Reverse the order of elements in the given data with a specific direction. - The shape of the data is preserved, but the elements are reordered. - Required keys are the keys in attributes "keys", added or modified keys are - "flip", "flip_direction" and the keys in attributes "keys". - It also supports flipping a list of images with the same flip. - - Args: - keys (list[str]): The images to be flipped. - flip_ratio (float): The propability to flip the images. - direction (str): Flip images horizontally or vertically. Options are - "horizontal" | "vertical". Default: "horizontal". - """ - _directions = ['horizontal', 'vertical'] - - def __init__(self, keys, flip_ratio=0.5, direction='horizontal'): - if direction not in self._directions: - raise ValueError(f'Direction {direction} is not supported.' - f'Currently support ones are {self._directions}') - self.keys = keys - self.flip_ratio = flip_ratio - self.direction = direction - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - flip = np.random.random() < self.flip_ratio - - if flip: - for key in self.keys: - if isinstance(results[key], list): - for v in results[key]: - mmcv.imflip_(v, self.direction) - else: - mmcv.imflip_(results[key], self.direction) - - results['flip'] = flip - results['flip_direction'] = self.direction - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, flip_ratio={self.flip_ratio}, ' - f'direction={self.direction})') - return repr_str - - -@PIPELINES.register_module() -class Pad: - """Pad the images to align with network downsample factor for testing. - - See `Reshape` for more explanation. `numpy.pad` is used for the pad - operation. - Required keys are the keys in attribute "keys", added or - modified keys are "test_trans" and the keys in attribute - "keys". All keys in "keys" should have the same shape. "test_trans" is used - to record the test transformation to align the input's shape. - - Args: - keys (list[str]): The images to be padded. - ds_factor (int): Downsample factor of the network. The height and - weight will be padded to a multiple of ds_factor. Default: 32. - kwargs (option): any keyword arguments to be passed to `numpy.pad`. - """ - - def __init__(self, keys, ds_factor=32, **kwargs): - self.keys = keys - self.ds_factor = ds_factor - self.kwargs = kwargs - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - h, w = results[self.keys[0]].shape[:2] - - new_h = self.ds_factor * ((h - 1) // self.ds_factor + 1) - new_w = self.ds_factor * ((w - 1) // self.ds_factor + 1) - - pad_h = new_h - h - pad_w = new_w - w - if new_h != h or new_w != w: - pad_width = ((0, pad_h), (0, pad_w), (0, 0)) - for key in self.keys: - results[key] = np.pad(results[key], - pad_width[:results[key].ndim], - **self.kwargs) - results['pad'] = (pad_h, pad_w) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - kwargs_str = ', '.join( - [f'{key}={val}' for key, val in self.kwargs.items()]) - repr_str += (f'(keys={self.keys}, ds_factor={self.ds_factor}, ' - f'{kwargs_str})') - return repr_str - - -@PIPELINES.register_module() -class RandomAffine: - """Apply random affine to input images. - - This class is adopted from - https://github.com/pytorch/vision/blob/v0.5.0/torchvision/transforms/ - transforms.py#L1015 - It should be noted that in - https://github.com/Yaoyi-Li/GCA-Matting/blob/master/dataloader/ - data_generator.py#L70 - random flip is added. See explanation of `flip_ratio` below. - Required keys are the keys in attribute "keys", modified keys - are keys in attribute "keys". - - Args: - keys (Sequence[str]): The images to be affined. - degrees (float | tuple[float]): Range of degrees to select from. If it - is a float instead of a tuple like (min, max), the range of degrees - will be (-degrees, +degrees). Set to 0 to deactivate rotations. - translate (tuple, optional): Tuple of maximum absolute fraction for - horizontal and vertical translations. For example translate=(a, b), - then horizontal shift is randomly sampled in the range - -img_width * a < dx < img_width * a and vertical shift is randomly - sampled in the range -img_height * b < dy < img_height * b. - Default: None. - scale (tuple, optional): Scaling factor interval, e.g (a, b), then - scale is randomly sampled from the range a <= scale <= b. - Default: None. - shear (float | tuple[float], optional): Range of shear degrees to - select from. If shear is a float, a shear parallel to the x axis - and a shear parallel to the y axis in the range (-shear, +shear) - will be applied. Else if shear is a tuple of 2 values, a x-axis - shear and a y-axis shear in (shear[0], shear[1]) will be applied. - Default: None. - flip_ratio (float, optional): Probability of the image being flipped. - The flips in horizontal direction and vertical direction are - independent. The image may be flipped in both directions. - Default: None. - """ - - def __init__(self, - keys, - degrees, - translate=None, - scale=None, - shear=None, - flip_ratio=None): - self.keys = keys - if isinstance(degrees, numbers.Number): - assert degrees >= 0, ('If degrees is a single number, ' - 'it must be positive.') - self.degrees = (-degrees, degrees) - else: - assert isinstance(degrees, tuple) and len(degrees) == 2, \ - 'degrees should be a tuple and it must be of length 2.' - self.degrees = degrees - - if translate is not None: - assert isinstance(translate, tuple) and len(translate) == 2, \ - 'translate should be a tuple and it must be of length 2.' - for t in translate: - assert 0.0 <= t <= 1.0, ('translation values should be ' - 'between 0 and 1.') - self.translate = translate - - if scale is not None: - assert isinstance(scale, tuple) and len(scale) == 2, \ - 'scale should be a tuple and it must be of length 2.' - for s in scale: - assert s > 0, 'scale values should be positive.' - self.scale = scale - - if shear is not None: - if isinstance(shear, numbers.Number): - assert shear >= 0, ('If shear is a single number, ' - 'it must be positive.') - self.shear = (-shear, shear) - else: - assert isinstance(shear, tuple) and len(shear) == 2, \ - 'shear should be a tuple and it must be of length 2.' - # X-Axis and Y-Axis shear with (min, max) - self.shear = shear - else: - self.shear = shear - - if flip_ratio is not None: - assert isinstance(flip_ratio, - float), 'flip_ratio should be a float.' - self.flip_ratio = flip_ratio - else: - self.flip_ratio = 0 - - @staticmethod - def _get_params(degrees, translate, scale_ranges, shears, flip_ratio, - img_size): - """Get parameters for affine transformation. - - Returns: - paras (tuple): Params to be passed to the affine transformation. - """ - angle = np.random.uniform(degrees[0], degrees[1]) - if translate is not None: - max_dx = translate[0] * img_size[0] - max_dy = translate[1] * img_size[1] - translations = (np.round(np.random.uniform(-max_dx, max_dx)), - np.round(np.random.uniform(-max_dy, max_dy))) - else: - translations = (0, 0) - - if scale_ranges is not None: - scale = (np.random.uniform(scale_ranges[0], scale_ranges[1]), - np.random.uniform(scale_ranges[0], scale_ranges[1])) - else: - scale = (1.0, 1.0) - - if shears is not None: - shear = np.random.uniform(shears[0], shears[1]) - else: - shear = 0.0 - - # Because `flip` is used as a multiplier in line 479 and 480, - # so -1 stands for flip and 1 stands for no flip. Thus `flip` - # should be an 'inverse' flag as the result of the comparison. - # See https://github.com/open-mmlab/mmediting/pull/799 for more detail - flip = (np.random.rand(2) > flip_ratio).astype(np.int32) * 2 - 1 - - return angle, translations, scale, shear, flip - - @staticmethod - def _get_inverse_affine_matrix(center, angle, translate, scale, shear, - flip): - """Helper method to compute inverse matrix for affine transformation. - - As it is explained in PIL.Image.rotate, we need compute INVERSE of - affine transformation matrix: M = T * C * RSS * C^-1 where - T is translation matrix: - [1, 0, tx | 0, 1, ty | 0, 0, 1]; - C is translation matrix to keep center: - [1, 0, cx | 0, 1, cy | 0, 0, 1]; - RSS is rotation with scale and shear matrix. - - It is different from the original function in torchvision. - 1. The order are changed to flip -> scale -> rotation -> shear. - 2. x and y have different scale factors. - RSS(shear, a, scale, f) = - [ cos(a + shear)*scale_x*f -sin(a + shear)*scale_y 0] - [ sin(a)*scale_x*f cos(a)*scale_y 0] - [ 0 0 1] - Thus, the inverse is M^-1 = C * RSS^-1 * C^-1 * T^-1. - """ - - angle = math.radians(angle) - shear = math.radians(shear) - scale_x = 1.0 / scale[0] * flip[0] - scale_y = 1.0 / scale[1] * flip[1] - - # Inverted rotation matrix with scale and shear - d = math.cos(angle + shear) * math.cos(angle) + math.sin( - angle + shear) * math.sin(angle) - matrix = [ - math.cos(angle) * scale_x, - math.sin(angle + shear) * scale_x, 0, -math.sin(angle) * scale_y, - math.cos(angle + shear) * scale_y, 0 - ] - matrix = [m / d for m in matrix] - - # Apply inverse of translation and of center translation: - # RSS^-1 * C^-1 * T^-1 - matrix[2] += matrix[0] * (-center[0] - translate[0]) + matrix[1] * ( - -center[1] - translate[1]) - matrix[5] += matrix[3] * (-center[0] - translate[0]) + matrix[4] * ( - -center[1] - translate[1]) - - # Apply center translation: C * RSS^-1 * C^-1 * T^-1 - matrix[2] += center[0] - matrix[5] += center[1] - - return matrix - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - h, w = results[self.keys[0]].shape[:2] - # if image is too small, set degree to 0 to reduce introduced dark area - if np.maximum(h, w) < 1024: - params = self._get_params((0, 0), self.translate, self.scale, - self.shear, self.flip_ratio, (h, w)) - else: - params = self._get_params(self.degrees, self.translate, self.scale, - self.shear, self.flip_ratio, (h, w)) - - center = (w * 0.5 - 0.5, h * 0.5 - 0.5) - M = self._get_inverse_affine_matrix(center, *params) - M = np.array(M).reshape((2, 3)) - - for key in self.keys: - results[key] = cv2.warpAffine( - results[key], - M, (w, h), - flags=cv2.INTER_NEAREST + cv2.WARP_INVERSE_MAP) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, degrees={self.degrees}, ' - f'translate={self.translate}, scale={self.scale}, ' - f'shear={self.shear}, flip_ratio={self.flip_ratio})') - return repr_str - - -@PIPELINES.register_module() -class RandomJitter: - """Randomly jitter the foreground in hsv space. - - The jitter range of hue is adjustable while the jitter ranges of saturation - and value are adaptive to the images. Side effect: the "fg" image will be - converted to `np.float32`. - Required keys are "fg" and "alpha", modified key is "fg". - - Args: - hue_range (float | tuple[float]): Range of hue jittering. If it is a - float instead of a tuple like (min, max), the range of hue - jittering will be (-hue_range, +hue_range). Default: 40. - """ - - def __init__(self, hue_range=40): - if isinstance(hue_range, numbers.Number): - assert hue_range >= 0, ('If hue_range is a single number, ' - 'it must be positive.') - self.hue_range = (-hue_range, hue_range) - else: - assert isinstance(hue_range, tuple) and len(hue_range) == 2, \ - 'hue_range should be a tuple and it must be of length 2.' - self.hue_range = hue_range - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - fg, alpha = results['fg'], results['alpha'] - - # convert to HSV space; - # convert to float32 image to keep precision during space conversion. - fg = mmcv.bgr2hsv(fg.astype(np.float32) / 255) - # Hue noise - hue_jitter = np.random.randint(self.hue_range[0], self.hue_range[1]) - fg[:, :, 0] = np.remainder(fg[:, :, 0] + hue_jitter, 360) - - # Saturation noise - sat_mean = fg[:, :, 1][alpha > 0].mean() - # jitter saturation within range (1.1 - sat_mean) * [-0.1, 0.1] - sat_jitter = (1.1 - sat_mean) * (np.random.rand() * 0.2 - 0.1) - sat = fg[:, :, 1] - sat = np.abs(sat + sat_jitter) - sat[sat > 1] = 2 - sat[sat > 1] - fg[:, :, 1] = sat - - # Value noise - val_mean = fg[:, :, 2][alpha > 0].mean() - # jitter value within range (1.1 - val_mean) * [-0.1, 0.1] - val_jitter = (1.1 - val_mean) * (np.random.rand() * 0.2 - 0.1) - val = fg[:, :, 2] - val = np.abs(val + val_jitter) - val[val > 1] = 2 - val[val > 1] - fg[:, :, 2] = val - # convert back to BGR space - fg = mmcv.hsv2bgr(fg) - results['fg'] = fg * 255 - - return results - - def __repr__(self): - return self.__class__.__name__ + f'hue_range={self.hue_range}' - - -@PIPELINES.register_module() -class ColorJitter: - """An interface for torch color jitter so that it can be invoked in - mmediting pipeline. - - Randomly change the brightness, contrast and saturation of an image. - Modified keys are the attributes specified in "keys". - - Args: - keys (list[str]): The images to be resized. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'rgb'. - - Notes: ``**kwards`` follows the args list of - ``torchvision.transforms.ColorJitter``. - - brightness (float or tuple of float (min, max)): How much to jitter - brightness. brightness_factor is chosen uniformly from - [max(0, 1 - brightness), 1 + brightness] or the given [min, max]. - Should be non negative numbers. - contrast (float or tuple of float (min, max)): How much to jitter - contrast. contrast_factor is chosen uniformly from - [max(0, 1 - contrast), 1 + contrast] or the given [min, max]. - Should be non negative numbers. - saturation (float or tuple of float (min, max)): How much to jitter - saturation. saturation_factor is chosen uniformly from - [max(0, 1 - saturation), 1 + saturation] or the given [min, max]. - Should be non negative numbers. - hue (float or tuple of float (min, max)): How much to jitter hue. - hue_factor is chosen uniformly from [-hue, hue] or the given - [min, max]. - Should have 0<= hue <= 0.5 or -0.5 <= min <= max <= 0.5. - """ - - def __init__(self, keys, channel_order='rgb', **kwargs): - assert keys, 'Keys should not be empty.' - assert 'to_rgb' not in kwargs, ( - '`to_rgb` is not support in ColorJitter, ' - "which is replaced by `channel_order` ('rgb' or 'bgr')") - - self.keys = keys - self.channel_order = channel_order - self.transform = transforms.ColorJitter(**kwargs) - - def _color_jitter(self, image, this_seed): - - if self.channel_order.lower() == 'bgr': - image = image[..., ::-1] - - image = Image.fromarray(image) - torch.manual_seed(this_seed) - image = self.transform(image) - image = np.asarray(image) - - if self.channel_order.lower() == 'bgr': - image = image[..., ::-1] - - return image - - def __call__(self, results): - - this_seed = random.randint(0, 2**32) - - for k in self.keys: - if isinstance(results[k], list): - results[k] = [ - self._color_jitter(v, this_seed) for v in results[k] - ] - else: - results[k] = self._color_jitter(results[k], this_seed) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, channel_order={self.channel_order}, ' - f'brightness={self.transform.brightness}, ' - f'contrast={self.transform.contrast}, ' - f'saturation={self.transform.saturation}, ' - f'hue={self.transform.hue})') - - return repr_str - - -class BinarizeImage: - """Binarize image. - - Args: - keys (Sequence[str]): The images to be binarized. - binary_thr (float): Threshold for binarization. - to_int (bool): If True, return image as int32, otherwise - return image as float32. - """ - - def __init__(self, keys, binary_thr, to_int=False): - self.keys = keys - self.binary_thr = binary_thr - self.to_int = to_int - - def _binarize(self, img): - type_ = np.float32 if not self.to_int else np.int32 - img = (img[..., :] > self.binary_thr).astype(type_) - - return img - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - results[k] = self._binarize(results[k]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, binary_thr={self.binary_thr}, ' - f'to_int={self.to_int})') - - return repr_str - - -@PIPELINES.register_module() -class RandomMaskDilation: - """Randomly dilate binary masks. - - Args: - keys (Sequence[str]): The images to be resized. - get_binary (bool): If True, according to binary_thr, reset final - output as binary mask. Otherwise, return masks directly. - binary_thr (float): Threshold for obtaining binary mask. - kernel_min (int): Min size of dilation kernel. - kernel_max (int): Max size of dilation kernel. - """ - - def __init__(self, keys, binary_thr=0., kernel_min=9, kernel_max=49): - self.keys = keys - self.kernel_min = kernel_min - self.kernel_max = kernel_max - self.binary_thr = binary_thr - - def _random_dilate(self, img): - kernel_size = np.random.randint(self.kernel_min, self.kernel_max + 1) - kernel = np.ones((kernel_size, kernel_size), dtype=np.uint8) - dilate_kernel_size = kernel_size - img_ = cv2.dilate(img, kernel, iterations=1) - - img_ = (img_ > self.binary_thr).astype(np.float32) - - return img_, dilate_kernel_size - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - results[k], d_kernel = self._random_dilate(results[k]) - if len(results[k].shape) == 2: - results[k] = np.expand_dims(results[k], axis=2) - results[k + '_dilate_kernel_size'] = d_kernel - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, kernel_min={self.kernel_min}, ' - f'kernel_max={self.kernel_max})') - - return repr_str - - -@PIPELINES.register_module() -class RandomTransposeHW: - """Randomly transpose images in H and W dimensions with a probability. - - (TransposeHW = horizontal flip + anti-clockwise rotatation by 90 degrees) - When used with horizontal/vertical flips, it serves as a way of rotation - augmentation. - It also supports randomly transposing a list of images. - - Required keys are the keys in attributes "keys", added or modified keys are - "transpose" and the keys in attributes "keys". - - Args: - keys (list[str]): The images to be transposed. - transpose_ratio (float): The propability to transpose the images. - """ - - def __init__(self, keys, transpose_ratio=0.5): - self.keys = keys - self.transpose_ratio = transpose_ratio - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - transpose = np.random.random() < self.transpose_ratio - - if transpose: - for key in self.keys: - if isinstance(results[key], list): - results[key] = [v.transpose(1, 0, 2) for v in results[key]] - else: - results[key] = results[key].transpose(1, 0, 2) - - results['transpose'] = transpose - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(keys={self.keys}, transpose_ratio={self.transpose_ratio})') - return repr_str - - -@PIPELINES.register_module() -class GenerateFrameIndiceswithPadding: - """Generate frame index with padding for REDS dataset and Vid4 dataset - during testing. - - Required keys: lq_path, gt_path, key, num_input_frames, max_frame_num - Added or modified keys: lq_path, gt_path - - Args: - padding (str): padding mode, one of - 'replicate' | 'reflection' | 'reflection_circle' | 'circle'. - - Examples: current_idx = 0, num_input_frames = 5 - The generated frame indices under different padding mode: - - replicate: [0, 0, 0, 1, 2] - reflection: [2, 1, 0, 1, 2] - reflection_circle: [4, 3, 0, 1, 2] - circle: [3, 4, 0, 1, 2] - - filename_tmpl (str): Template for file name. Default: '{:08d}'. - """ - - def __init__(self, padding, filename_tmpl='{:08d}'): - if padding not in ('replicate', 'reflection', 'reflection_circle', - 'circle'): - raise ValueError(f'Wrong padding mode {padding}.' - 'Should be "replicate", "reflection", ' - '"reflection_circle", "circle"') - self.padding = padding - self.filename_tmpl = filename_tmpl - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - clip_name, frame_name = results['key'].split(os.sep) - current_idx = int(frame_name) - max_frame_num = results['max_frame_num'] - 1 # start from 0 - num_input_frames = results['num_input_frames'] - num_pad = num_input_frames // 2 - - frame_list = [] - for i in range(current_idx - num_pad, current_idx + num_pad + 1): - if i < 0: - if self.padding == 'replicate': - pad_idx = 0 - elif self.padding == 'reflection': - pad_idx = -i - elif self.padding == 'reflection_circle': - pad_idx = current_idx + num_pad - i - else: - pad_idx = num_input_frames + i - elif i > max_frame_num: - if self.padding == 'replicate': - pad_idx = max_frame_num - elif self.padding == 'reflection': - pad_idx = max_frame_num * 2 - i - elif self.padding == 'reflection_circle': - pad_idx = (current_idx - num_pad) - (i - max_frame_num) - else: - pad_idx = i - num_input_frames - else: - pad_idx = i - frame_list.append(pad_idx) - - lq_path_root = results['lq_path'] - gt_path_root = results['gt_path'] - lq_paths = [ - osp.join(lq_path_root, clip_name, - f'{self.filename_tmpl.format(idx)}.png') - for idx in frame_list - ] - gt_paths = [osp.join(gt_path_root, clip_name, f'{frame_name}.png')] - results['lq_path'] = lq_paths - results['gt_path'] = gt_paths - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ + f"(padding='{self.padding}')" - return repr_str - - -@PIPELINES.register_module() -class GenerateFrameIndices: - """Generate frame index for REDS datasets. It also performs - temporal augmention with random interval. - - Required keys: lq_path, gt_path, key, num_input_frames - Added or modified keys: lq_path, gt_path, interval, reverse - - Args: - interval_list (list[int]): Interval list for temporal augmentation. - It will randomly pick an interval from interval_list and sample - frame index with the interval. - frames_per_clip(int): Number of frames per clips. Default: 99 for - REDS dataset. - """ - - def __init__(self, interval_list, frames_per_clip=99): - self.interval_list = interval_list - self.frames_per_clip = frames_per_clip - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - clip_name, frame_name = results['key'].split( - os.sep) # key example: 000/00000000 - center_frame_idx = int(frame_name) - num_half_frames = results['num_input_frames'] // 2 - - max_frame_num = results.get('max_frame_num', self.frames_per_clip + 1) - frames_per_clip = min(self.frames_per_clip, max_frame_num - 1) - - interval = np.random.choice(self.interval_list) - # ensure not exceeding the borders - start_frame_idx = center_frame_idx - num_half_frames * interval - end_frame_idx = center_frame_idx + num_half_frames * interval - while (start_frame_idx < 0) or (end_frame_idx > frames_per_clip): - center_frame_idx = np.random.randint(0, frames_per_clip + 1) - start_frame_idx = center_frame_idx - num_half_frames * interval - end_frame_idx = center_frame_idx + num_half_frames * interval - frame_name = f'{center_frame_idx:08d}' - neighbor_list = list( - range(center_frame_idx - num_half_frames * interval, - center_frame_idx + num_half_frames * interval + 1, interval)) - - lq_path_root = results['lq_path'] - gt_path_root = results['gt_path'] - lq_path = [ - osp.join(lq_path_root, clip_name, f'{v:08d}.png') - for v in neighbor_list - ] - gt_path = [osp.join(gt_path_root, clip_name, f'{frame_name}.png')] - results['lq_path'] = lq_path - results['gt_path'] = gt_path - results['interval'] = interval - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(interval_list={self.interval_list}, ' - f'frames_per_clip={self.frames_per_clip})') - return repr_str - - -@PIPELINES.register_module() -class TemporalReverse: - """Reverse frame lists for temporal augmentation. - - Required keys are the keys in attributes "lq" and "gt", - added or modified keys are "lq", "gt" and "reverse". - - Args: - keys (list[str]): The frame lists to be reversed. - reverse_ratio (float): The propability to reverse the frame lists. - Default: 0.5. - """ - - def __init__(self, keys, reverse_ratio=0.5): - self.keys = keys - self.reverse_ratio = reverse_ratio - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - reverse = np.random.random() < self.reverse_ratio - - if reverse: - for key in self.keys: - results[key].reverse() - - results['reverse'] = reverse - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(keys={self.keys}, reverse_ratio={self.reverse_ratio})' - return repr_str - - -@PIPELINES.register_module() -class GenerateSegmentIndices: - """Generate frame indices for a segment. It also performs temporal - augmention with random interval. - - Required keys: lq_path, gt_path, key, num_input_frames, sequence_length - Added or modified keys: lq_path, gt_path, interval, reverse - - Args: - interval_list (list[int]): Interval list for temporal augmentation. - It will randomly pick an interval from interval_list and sample - frame index with the interval. - start_idx (int): The index corresponds to the first frame in the - sequence. Default: 0. - filename_tmpl (str): Template for file name. Default: '{:08d}.png'. - """ - - def __init__(self, interval_list, start_idx=0, filename_tmpl='{:08d}.png'): - self.interval_list = interval_list - self.filename_tmpl = filename_tmpl - self.start_idx = start_idx - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - # key example: '000', 'calendar' (sequence name) - clip_name = results['key'] - interval = np.random.choice(self.interval_list) - - self.sequence_length = results['sequence_length'] - num_input_frames = results.get('num_input_frames', - self.sequence_length) - - # randomly select a frame as start - if self.sequence_length - num_input_frames * interval < 0: - raise ValueError('The input sequence is not long enough to ' - 'support the current choice of [interval] or ' - '[num_input_frames].') - start_frame_idx = np.random.randint( - 0, self.sequence_length - num_input_frames * interval + 1) - end_frame_idx = start_frame_idx + num_input_frames * interval - neighbor_list = list(range(start_frame_idx, end_frame_idx, interval)) - neighbor_list = [v + self.start_idx for v in neighbor_list] - - # add the corresponding file paths - lq_path_root = results['lq_path'] - gt_path_root = results['gt_path'] - lq_path = [ - osp.join(lq_path_root, clip_name, self.filename_tmpl.format(v)) - for v in neighbor_list - ] - gt_path = [ - osp.join(gt_path_root, clip_name, self.filename_tmpl.format(v)) - for v in neighbor_list - ] - - results['lq_path'] = lq_path - results['gt_path'] = gt_path - results['interval'] = interval - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(interval_list={self.interval_list})') - return repr_str - - -@PIPELINES.register_module() -class MirrorSequence: - """Extend short sequences (e.g. Vimeo-90K) by mirroring the sequences - - Given a sequence with N frames (x1, ..., xN), extend the sequence to - (x1, ..., xN, xN, ..., x1). - - Args: - keys (list[str]): The frame lists to be extended. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if isinstance(results[key], list): - results[key] = results[key] + results[key][::-1] - else: - raise TypeError('The input must be of class list[nparray]. ' - f'Got {type(results[key])}.') - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys})') - return repr_str - - -@PIPELINES.register_module() -class CopyValues: - """Copy the value of a source key to a destination key. - - - It does the following: results[dst_key] = results[src_key] for - (src_key, dst_key) in zip(src_keys, dst_keys). - - Added keys are the keys in the attribute "dst_keys". - - Args: - src_keys (list[str]): The source keys. - dst_keys (list[str]): The destination keys. - """ - - def __init__(self, src_keys, dst_keys): - - if not isinstance(src_keys, list) or not isinstance(dst_keys, list): - raise AssertionError('"src_keys" and "dst_keys" must be lists.') - - if len(src_keys) != len(dst_keys): - raise ValueError('"src_keys" and "dst_keys" should have the same' - 'number of elements.') - - self.src_keys = src_keys - self.dst_keys = dst_keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict with a key added/modified. - """ - for (src_key, dst_key) in zip(self.src_keys, self.dst_keys): - results[dst_key] = copy.deepcopy(results[src_key]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(src_keys={self.src_keys})') - repr_str += (f'(dst_keys={self.dst_keys})') - return repr_str - - -@PIPELINES.register_module() -class Quantize: - """Quantize and clip the image to [0, 1]. - - It is assumed that the the input has range [0, 1]. - - Modified keys are the attributes specified in "keys". - - Args: - keys (list[str]): The keys whose values are clipped. - """ - - def __init__(self, keys): - self.keys = keys - - def _quantize_clip(self, input_): - is_single_image = False - if isinstance(input_, np.ndarray): - is_single_image = True - input_ = [input_] - - # quantize and clip - input_ = [np.clip((v * 255.0).round(), 0, 255) / 255. for v in input_] - - if is_single_image: - input_ = input_[0] - - return input_ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict with the values of the specified keys are rounded - and clipped. - """ - - for key in self.keys: - results[key] = self._quantize_clip(results[key]) - - return results - - def __repr__(self): - return self.__class__.__name__ - - -@PIPELINES.register_module() -class UnsharpMasking: - """Apply unsharp masking to an image or a sequence of images. - - Args: - kernel_size (int): The kernel_size of the Gaussian kernel. - sigma (float): The standard deviation of the Gaussian. - weight (float): The weight of the "details" in the final output. - threshold (float): Pixel differences larger than this value are - regarded as "details". - keys (list[str]): The keys whose values are processed. - - Added keys are "xxx_unsharp", where "xxx" are the attributes specified - in "keys". - - """ - - def __init__(self, kernel_size, sigma, weight, threshold, keys): - if kernel_size % 2 == 0: - raise ValueError('kernel_size must be an odd number, but ' - f'got {kernel_size}.') - - self.kernel_size = kernel_size - self.sigma = sigma - self.weight = weight - self.threshold = threshold - self.keys = keys - - kernel = cv2.getGaussianKernel(kernel_size, sigma) - self.kernel = np.matmul(kernel, kernel.transpose()) - - def _unsharp_masking(self, imgs): - is_single_image = False - if isinstance(imgs, np.ndarray): - is_single_image = True - imgs = [imgs] - - outputs = [] - for img in imgs: - residue = img - cv2.filter2D(img, -1, self.kernel) - mask = np.float32(np.abs(residue) * 255 > self.threshold) - soft_mask = cv2.filter2D(mask, -1, self.kernel) - sharpened = np.clip(img + self.weight * residue, 0, 1) - - outputs.append(soft_mask * sharpened + (1 - soft_mask) * img) - - if is_single_image: - outputs = outputs[0] - - return outputs - - def __call__(self, results): - for key in self.keys: - results[f'{key}_unsharp'] = self._unsharp_masking(results[key]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, kernel_size={self.kernel_size}, ' - f'sigma={self.sigma}, weight={self.weight}, ' - f'threshold={self.threshold})') - return repr_str diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/blur_kernels.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/blur_kernels.py deleted file mode 100755 index dca2ac9e4..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/blur_kernels.py +++ /dev/null @@ -1,536 +0,0 @@ -# This code is referenced from BasicSR with modifications. -# Reference: https://github.com/xinntao/BasicSR/blob/master/basicsr/data/degradations.py # noqa -# Original licence: Copyright (c) 2020 xinntao, under the Apache 2.0 license. - -import numpy as np -from scipy import special - - -def get_rotated_sigma_matrix(sig_x, sig_y, theta): - """Calculate the rotated sigma matrix (two dimensional matrix). - - Args: - sig_x (float): Standard deviation along the horizontal direction. - sig_y (float): Standard deviation along the vertical direction. - theta (float): Rotation in radian. - - Returns: - ndarray: Rotated sigma matrix. - """ - - diag = np.array([[sig_x**2, 0], [0, sig_y**2]]).astype(np.float32) - rot = np.array([[np.cos(theta), -np.sin(theta)], - [np.sin(theta), np.cos(theta)]]).astype(np.float32) - - return np.matmul(rot, np.matmul(diag, rot.T)) - - -def _mesh_grid(kernel_size): - """Generate the mesh grid, centering at zero. - - Args: - kernel_size (int): The size of the kernel. - - Returns: - x_grid (ndarray): x-coordinates with shape (kernel_size, kernel_size). - y_grid (ndarray): y-coordiantes with shape (kernel_size, kernel_size). - xy_grid (ndarray): stacked coordinates with shape - (kernel_size, kernel_size, 2). - """ - - range_ = np.arange(-kernel_size // 2 + 1., kernel_size // 2 + 1.) - x_grid, y_grid = np.meshgrid(range_, range_) - xy_grid = np.hstack((x_grid.reshape((kernel_size * kernel_size, 1)), - y_grid.reshape(kernel_size * kernel_size, - 1))).reshape(kernel_size, kernel_size, - 2) - - return xy_grid, x_grid, y_grid - - -def calculate_gaussian_pdf(sigma_matrix, grid): - """Calculate PDF of the bivariate Gaussian distribution. - - Args: - sigma_matrix (ndarray): The variance matrix with shape (2, 2). - grid (ndarray): Coordinates generated by :func:`_mesh_grid`, - with shape (K, K, 2), where K is the kernel size. - - Returns: - kernel (ndarrray): Un-normalized kernel. - """ - - inverse_sigma = np.linalg.inv(sigma_matrix) - kernel = np.exp(-0.5 * np.sum(np.matmul(grid, inverse_sigma) * grid, 2)) - - return kernel - - -def bivariate_gaussian(kernel_size, - sig_x, - sig_y=None, - theta=None, - grid=None, - is_isotropic=True): - """Generate a bivariate isotropic or anisotropic Gaussian kernel. - - In isotropic mode, only `sig_x` is used. `sig_y` and `theta` are - ignored. - - Args: - kernel_size (int): The size of the kernel - sig_x (float): Standard deviation along horizontal direction. - sig_y (float | None, optional): Standard deviation along the vertical - direction. If it is None, 'is_isotropic' must be set to True. - Default: None. - theta (float | None, optional): Rotation in radian. If it is None, - 'is_isotropic' must be set to True. Default: None. - grid (ndarray, optional): Coordinates generated by :func:`_mesh_grid`, - with shape (K, K, 2), where K is the kernel size. Default: None - is_isotropic (bool, optional): Whether to use an isotropic kernel. - Default: True. - - Returns: - kernel (ndarray): normalized kernel (i.e. sum to 1). - """ - - if grid is None: - grid, _, _ = _mesh_grid(kernel_size) - - if is_isotropic: - sigma_matrix = np.array([[sig_x**2, 0], [0, - sig_x**2]]).astype(np.float32) - else: - if sig_y is None: - raise ValueError('"sig_y" cannot be None if "is_isotropic" is ' - 'False.') - - sigma_matrix = get_rotated_sigma_matrix(sig_x, sig_y, theta) - - kernel = calculate_gaussian_pdf(sigma_matrix, grid) - kernel = kernel / np.sum(kernel) - - return kernel - - -def bivariate_generalized_gaussian(kernel_size, - sig_x, - sig_y=None, - theta=None, - beta=1, - grid=None, - is_isotropic=True): - """Generate a bivariate generalized Gaussian kernel. - - Described in `Parameter Estimation For Multivariate Generalized - Gaussian Distributions` by Pascal et. al (2013). In isotropic mode, - only `sig_x` is used. `sig_y` and `theta` is ignored. - - Args: - kernel_size (int): The size of the kernel - sig_x (float): Standard deviation along horizontal direction - sig_y (float | None, optional): Standard deviation along the vertical - direction. If it is None, 'is_isotropic' must be set to True. - Default: None. - theta (float | None, optional): Rotation in radian. If it is None, - 'is_isotropic' must be set to True. Default: None. - beta (float, optional): Shape parameter, beta = 1 is the normal - distribution. Default: 1. - grid (ndarray, optional): Coordinates generated by :func:`_mesh_grid`, - with shape (K, K, 2), where K is the kernel size. Default: None - is_isotropic (bool, optional): Whether to use an isotropic kernel. - Default: True. - - Returns: - kernel (ndarray): normalized kernel. - - """ - - if grid is None: - grid, _, _ = _mesh_grid(kernel_size) - - if is_isotropic: - sigma_matrix = np.array([[sig_x**2, 0], [0, - sig_x**2]]).astype(np.float32) - else: - sigma_matrix = get_rotated_sigma_matrix(sig_x, sig_y, theta) - - inverse_sigma = np.linalg.inv(sigma_matrix) - kernel = np.exp( - -0.5 * - np.power(np.sum(np.matmul(grid, inverse_sigma) * grid, 2), beta)) - kernel = kernel / np.sum(kernel) - - return kernel - - -def bivariate_plateau(kernel_size, - sig_x, - sig_y, - theta, - beta, - grid=None, - is_isotropic=True): - """Generate a plateau-like anisotropic kernel. - - This kernel has a form of 1 / (1+x^(beta)). - Ref: https://stats.stackexchange.com/questions/203629/is-there-a-plateau-shaped-distribution # noqa - In the isotropic mode, only `sig_x` is used. `sig_y` and `theta` is ignored. - - Args: - kernel_size (int): The size of the kernel - sig_x (float): Standard deviation along horizontal direction - sig_y (float): Standard deviation along the vertical direction. - theta (float): Rotation in radian. - beta (float): Shape parameter, beta = 1 is the normal distribution. - grid (ndarray, optional): Coordinates generated by :func:`_mesh_grid`, - with shape (K, K, 2), where K is the kernel size. Default: None - is_isotropic (bool, optional): Whether to use an isotropic kernel. - Default: True. - Returns: - kernel (ndarray): normalized kernel (i.e. sum to 1). - """ - if grid is None: - grid, _, _ = _mesh_grid(kernel_size) - - if is_isotropic: - sigma_matrix = np.array([[sig_x**2, 0], [0, - sig_x**2]]).astype(np.float32) - else: - sigma_matrix = get_rotated_sigma_matrix(sig_x, sig_y, theta) - - inverse_sigma = np.linalg.inv(sigma_matrix) - kernel = np.reciprocal( - np.power(np.sum(np.matmul(grid, inverse_sigma) * grid, 2), beta) + 1) - kernel = kernel / np.sum(kernel) - - return kernel - - -def random_bivariate_gaussian_kernel(kernel_size, - sigma_x_range, - sigma_y_range, - rotation_range, - noise_range=None, - is_isotropic=True): - """Randomly generate bivariate isotropic or anisotropic Gaussian kernels. - - In the isotropic mode, only `sigma_x_range` is used. `sigma_y_range` and - `rotation_range` is ignored. - - Args: - kernel_size (int): The size of the kernel. - sigma_x_range (tuple): The range of the standard deviation along the - horizontal direction. Default: [0.6, 5] - sigma_y_range (tuple): The range of the standard deviation along the - vertical direction. Default: [0.6, 5] - rotation_range (tuple): Range of rotation in radian. - noise_range (tuple, optional): Multiplicative kernel noise. - Default: None. - is_isotropic (bool, optional): Whether to use an isotropic kernel. - Default: True. - - Returns: - kernel (ndarray): The kernel whose parameters are sampled from the - specified range. - """ - - assert kernel_size % 2 == 1, 'Kernel size must be an odd number.' - assert sigma_x_range[0] <= sigma_x_range[1], 'Wrong sigma_x_range.' - - sigma_x = np.random.uniform(sigma_x_range[0], sigma_x_range[1]) - if is_isotropic is False: - assert sigma_y_range[0] <= sigma_y_range[1], 'Wrong sigma_y_range.' - assert rotation_range[0] <= rotation_range[1], 'Wrong rotation_range.' - sigma_y = np.random.uniform(sigma_y_range[0], sigma_y_range[1]) - rotation = np.random.uniform(rotation_range[0], rotation_range[1]) - else: - sigma_y = sigma_x - rotation = 0 - - kernel = bivariate_gaussian( - kernel_size, sigma_x, sigma_y, rotation, is_isotropic=is_isotropic) - - # add multiplicative noise - if noise_range is not None: - assert noise_range[0] <= noise_range[1], 'Wrong noise range.' - noise = np.random.uniform( - noise_range[0], noise_range[1], size=kernel.shape) - kernel = kernel * noise - kernel = kernel / np.sum(kernel) - - return kernel - - -def random_bivariate_generalized_gaussian_kernel(kernel_size, - sigma_x_range, - sigma_y_range, - rotation_range, - beta_range, - noise_range=None, - is_isotropic=True): - """Randomly generate bivariate generalized Gaussian kernels. - - In the isotropic mode, only `sigma_x_range` is used. `sigma_y_range` and - `rotation_range` is ignored. - - Args: - kernel_size (int): The size of the kernel. - sigma_x_range (tuple): The range of the standard deviation along the - horizontal direction. Default: [0.6, 5] - sigma_y_range (tuple): The range of the standard deviation along the - vertical direction. Default: [0.6, 5] - rotation_range (tuple): Range of rotation in radian. - beta_range (float): The range of the shape parameter, beta = 1 is the - normal distribution. - noise_range (tuple, optional): Multiplicative kernel noise. - Default: None. - is_isotropic (bool, optional): Whether to use an isotropic kernel. - Default: True. - - Returns: - kernel (ndarray): - """ - - assert kernel_size % 2 == 1, 'Kernel size must be an odd number.' - assert sigma_x_range[0] <= sigma_x_range[1], 'Wrong sigma_x_range.' - - sigma_x = np.random.uniform(sigma_x_range[0], sigma_x_range[1]) - if is_isotropic is False: - assert sigma_y_range[0] <= sigma_y_range[1], 'Wrong sigma_y_range.' - assert rotation_range[0] <= rotation_range[1], 'Wrong rotation_range.' - sigma_y = np.random.uniform(sigma_y_range[0], sigma_y_range[1]) - rotation = np.random.uniform(rotation_range[0], rotation_range[1]) - else: - sigma_y = sigma_x - rotation = 0 - - # assume beta_range[0] <= 1 <= beta_range[1] - if np.random.uniform() <= 0.5: - beta = np.random.uniform(beta_range[0], 1) - else: - beta = np.random.uniform(1, beta_range[1]) - - kernel = bivariate_generalized_gaussian( - kernel_size, - sigma_x, - sigma_y, - rotation, - beta, - is_isotropic=is_isotropic) - - # add multiplicative noise - if noise_range is not None: - assert noise_range[0] <= noise_range[1], 'Wrong noise range.' - noise = np.random.uniform( - noise_range[0], noise_range[1], size=kernel.shape) - kernel = kernel * noise - kernel = kernel / np.sum(kernel) - - return kernel - - -def random_bivariate_plateau_kernel(kernel_size, - sigma_x_range, - sigma_y_range, - rotation_range, - beta_range, - noise_range=None, - is_isotropic=True): - """Randomly generate bivariate plateau kernels. - - In the isotropic mode, only `sigma_x_range` is used. `sigma_y_range` and - `rotation_range` is ignored. - - Args: - kernel_size (int): The size of the kernel. - sigma_x_range (tuple): The range of the standard deviation along the - horizontal direction. Default: [0.6, 5] - sigma_y_range (tuple): The range of the standard deviation along the - vertical direction. Default: [0.6, 5] - rotation_range (tuple): Range of rotation in radian. - beta_range (float): The range of the shape parameter, beta = 1 is the - normal distribution. - noise_range (tuple, optional): Multiplicative kernel noise. - Default: None. - is_isotropic (bool, optional): Whether to use an isotropic kernel. - Default: True. - - Returns: - kernel (ndarray): - """ - - assert kernel_size % 2 == 1, 'Kernel size must be an odd number.' - assert sigma_x_range[0] <= sigma_x_range[1], 'Wrong sigma_x_range.' - sigma_x = np.random.uniform(sigma_x_range[0], sigma_x_range[1]) - - if is_isotropic is False: - assert sigma_y_range[0] <= sigma_y_range[1], 'Wrong sigma_y_range.' - assert rotation_range[0] <= rotation_range[1], 'Wrong rotation_range.' - sigma_y = np.random.uniform(sigma_y_range[0], sigma_y_range[1]) - rotation = np.random.uniform(rotation_range[0], rotation_range[1]) - else: - sigma_y = sigma_x - rotation = 0 - - # TODO: this may be not proper - if np.random.uniform() <= 0.5: - beta = np.random.uniform(beta_range[0], 1) - else: - beta = np.random.uniform(1, beta_range[1]) - - kernel = bivariate_plateau( - kernel_size, - sigma_x, - sigma_y, - rotation, - beta, - is_isotropic=is_isotropic) - - # add multiplicative noise - if noise_range is not None: - assert noise_range[0] <= noise_range[1], 'Wrong noise range.' - noise = np.random.uniform( - noise_range[0], noise_range[1], size=kernel.shape) - kernel = kernel * noise - kernel = kernel / np.sum(kernel) - - return kernel - - -def random_circular_lowpass_kernel(omega_range, kernel_size, pad_to=0): - """ Generate a 2D Sinc filter - - Reference: https://dsp.stackexchange.com/questions/58301/2-d-circularly-symmetric-low-pass-filter # noqa - - Args: - omega_range (tuple): The cutoff frequency in radian (pi is max). - kernel_size (int): The size of the kernel. It must be an odd number. - pad_to (int, optional): The size of the padded kernel. It must be odd - or zero. Default: 0. - - Returns: - ndarray: The Sinc kernel with specified parameters. - """ - err = np.geterr() - np.seterr(divide='ignore', invalid='ignore') - - assert kernel_size % 2 == 1, 'Kernel size must be an odd number.' - omega = np.random.uniform(omega_range[0], omega_range[-1]) - - kernel = np.fromfunction( - lambda x, y: omega * special.j1(omega * np.sqrt( - (x - (kernel_size - 1) / 2)**2 + (y - (kernel_size - 1) / 2)**2)) / - (2 * np.pi * np.sqrt((x - (kernel_size - 1) / 2)**2 + - (y - (kernel_size - 1) / 2)**2)), - [kernel_size, kernel_size]) - kernel[(kernel_size - 1) // 2, - (kernel_size - 1) // 2] = omega**2 / (4 * np.pi) - kernel = kernel / np.sum(kernel) - - if pad_to > kernel_size: - pad_size = (pad_to - kernel_size) // 2 - kernel = np.pad(kernel, ((pad_size, pad_size), (pad_size, pad_size))) - - np.seterr(**err) - - return kernel - - -def random_mixed_kernels(kernel_list, - kernel_prob, - kernel_size, - sigma_x_range=[0.6, 5], - sigma_y_range=[0.6, 5], - rotation_range=[-np.pi, np.pi], - beta_gaussian_range=[0.5, 8], - beta_plateau_range=[1, 2], - omega_range=[0, np.pi], - noise_range=None): - """Randomly generate a kernel. - - - Args: - kernel_list (list): A list of kernel types. Choices are - 'iso', 'aniso', 'skew', 'generalized_iso', 'generalized_aniso', - 'plateau_iso', 'plateau_aniso', 'sinc'. - kernel_prob (list): The probability of choosing of the corresponding - kernel. - kernel_size (int): The size of the kernel. - sigma_x_range (list, optional): The range of the standard deviation - along the horizontal direction. Default: (0.6, 5). - sigma_y_range (list, optional): The range of the standard deviation - along the vertical direction. Default: (0.6, 5). - rotation_range (list, optional): Range of rotation in radian. - Default: (-np.pi, np.pi). - beta_gaussian_range (list, optional): The range of the shape parameter - for generalized Gaussian. Default: (0.5, 8). - beta_plateau_range (list, optional): The range of the shape parameter - for plateau kernel. Default: (1, 2). - omega_range (list, optional): The range of omega used in Sinc kernel. - Default: (0, np.pi). - noise_range (list, optional): Multiplicative kernel noise. - Default: None. - - Returns: - kernel (ndarray): The kernel whose parameters are sampled from the - specified range. - """ - - kernel_type = np.random.choice(kernel_list, p=kernel_prob) - if kernel_type == 'iso': - kernel = random_bivariate_gaussian_kernel( - kernel_size, - sigma_x_range, - sigma_y_range, - rotation_range, - noise_range=noise_range, - is_isotropic=True) - elif kernel_type == 'aniso': - kernel = random_bivariate_gaussian_kernel( - kernel_size, - sigma_x_range, - sigma_y_range, - rotation_range, - noise_range=noise_range, - is_isotropic=False) - elif kernel_type == 'generalized_iso': - kernel = random_bivariate_generalized_gaussian_kernel( - kernel_size, - sigma_x_range, - sigma_y_range, - rotation_range, - beta_gaussian_range, - noise_range=noise_range, - is_isotropic=True) - elif kernel_type == 'generalized_aniso': - kernel = random_bivariate_generalized_gaussian_kernel( - kernel_size, - sigma_x_range, - sigma_y_range, - rotation_range, - beta_gaussian_range, - noise_range=noise_range, - is_isotropic=False) - elif kernel_type == 'plateau_iso': - kernel = random_bivariate_plateau_kernel( - kernel_size, - sigma_x_range, - sigma_y_range, - rotation_range, - beta_plateau_range, - noise_range=None, - is_isotropic=True) - elif kernel_type == 'plateau_aniso': - kernel = random_bivariate_plateau_kernel( - kernel_size, - sigma_x_range, - sigma_y_range, - rotation_range, - beta_plateau_range, - noise_range=None, - is_isotropic=False) - elif kernel_type == 'sinc': - kernel = random_circular_lowpass_kernel(omega_range, kernel_size) - - return kernel diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/compose.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/compose.py deleted file mode 100755 index 0ffb0fd57..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/compose.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -from mmcv.utils import build_from_cfg - -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class Compose: - """Compose a data pipeline with a sequence of transforms. - - Args: - transforms (list[dict | callable]): - Either config dicts of transforms or transform objects. - """ - - def __init__(self, transforms): - assert isinstance(transforms, Sequence) - self.transforms = [] - for transform in transforms: - if isinstance(transform, dict): - transform = build_from_cfg(transform, PIPELINES) - self.transforms.append(transform) - elif callable(transform): - self.transforms.append(transform) - else: - raise TypeError(f'transform must be callable or a dict, ' - f'but got {type(transform)}') - - def __call__(self, data): - """Call function. - - Args: - data (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for t in self.transforms: - data = t(data) - if data is None: - return None - return data - - def __repr__(self): - format_string = self.__class__.__name__ + '(' - for t in self.transforms: - format_string += '\n' - format_string += f' {t}' - format_string += '\n)' - return format_string diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/crop.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/crop.py deleted file mode 100755 index 51fa24253..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/crop.py +++ /dev/null @@ -1,749 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import math -import random - -import mmcv -import numpy as np -from torch.nn.modules.utils import _pair - -from ..registry import PIPELINES -from .utils import random_choose_unknown - - -@PIPELINES.register_module() -class Crop: - """Crop data to specific size for training. - - Args: - keys (Sequence[str]): The images to be cropped. - crop_size (Tuple[int]): Target spatial size (h, w). - random_crop (bool): If set to True, it will random crop - image. Otherwise, it will work as center crop. - is_pad_zeros (bool, optional): Whether to pad the image with 0 if - crop_size is greater than image size. Default: False. - """ - - def __init__(self, keys, crop_size, random_crop=True, is_pad_zeros=False): - if not mmcv.is_tuple_of(crop_size, int): - raise TypeError( - 'Elements of crop_size must be int and crop_size must be' - f' tuple, but got {type(crop_size[0])} in {type(crop_size)}') - - self.keys = keys - self.crop_size = crop_size - self.random_crop = random_crop - self.is_pad_zeros = is_pad_zeros - - def _crop(self, data): - if not isinstance(data, list): - data_list = [data] - else: - data_list = data - - crop_bbox_list = [] - data_list_ = [] - - for item in data_list: - data_h, data_w = item.shape[:2] - crop_h, crop_w = self.crop_size - - if self.is_pad_zeros: - - crop_y_offset, crop_x_offset = 0, 0 - - if crop_h > data_h: - crop_y_offset = (crop_h - data_h) // 2 - if crop_w > data_w: - crop_x_offset = (crop_w - data_w) // 2 - - if crop_y_offset > 0 or crop_x_offset > 0: - pad_width = [(2 * crop_y_offset, 2 * crop_y_offset), - (2 * crop_x_offset, 2 * crop_x_offset)] - if item.ndim == 3: - pad_width.append((0, 0)) - item = np.pad( - item, - tuple(pad_width), - mode='constant', - constant_values=0) - - data_h, data_w = item.shape[:2] - - crop_h = min(data_h, crop_h) - crop_w = min(data_w, crop_w) - - if self.random_crop: - x_offset = np.random.randint(0, data_w - crop_w + 1) - y_offset = np.random.randint(0, data_h - crop_h + 1) - else: - x_offset = max(0, (data_w - crop_w)) // 2 - y_offset = max(0, (data_h - crop_h)) // 2 - - crop_bbox = [x_offset, y_offset, crop_w, crop_h] - item_ = item[y_offset:y_offset + crop_h, - x_offset:x_offset + crop_w, ...] - crop_bbox_list.append(crop_bbox) - data_list_.append(item_) - - if not isinstance(data, list): - return data_list_[0], crop_bbox_list[0] - return data_list_, crop_bbox_list - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - data_, crop_bbox = self._crop(results[k]) - results[k] = data_ - results[k + '_crop_bbox'] = crop_bbox - results['crop_size'] = self.crop_size - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'keys={self.keys}, crop_size={self.crop_size}, ' - f'random_crop={self.random_crop}') - - return repr_str - - -@PIPELINES.register_module() -class RandomResizedCrop(object): - """Crop data to random size and aspect ratio. - - A crop of a random proportion of the original image - and a random aspect ratio of the original aspect ratio is made. - The cropped image is finally resized to a given size specified - by 'crop_size'. Modified keys are the attributes specified in "keys". - - This code is partially adopted from - torchvision.transforms.RandomResizedCrop: - [https://pytorch.org/vision/stable/_modules/torchvision/transforms/\ - transforms.html#RandomResizedCrop]. - - Args: - keys (list[str]): The images to be resized and random-cropped. - crop_size (int | tuple[int]): Target spatial size (h, w). - scale (tuple[float], optional): Range of the proportion of the original - image to be cropped. Default: (0.08, 1.0). - ratio (tuple[float], optional): Range of aspect ratio of the crop. - Default: (3. / 4., 4. / 3.). - interpolation (str, optional): Algorithm used for interpolation. - It can be only either one of the following: - "nearest" | "bilinear" | "bicubic" | "area" | "lanczos". - Default: "bilinear". - """ - - def __init__(self, - keys, - crop_size, - scale=(0.08, 1.0), - ratio=(3. / 4., 4. / 3.), - interpolation='bilinear'): - assert keys, 'Keys should not be empty.' - if isinstance(crop_size, int): - crop_size = (crop_size, crop_size) - elif not mmcv.is_tuple_of(crop_size, int): - raise TypeError('"crop_size" must be an integer ' - 'or a tuple of integers, but got ' - f'{type(crop_size)}') - if not mmcv.is_tuple_of(scale, float): - raise TypeError('"scale" must be a tuple of float, ' - f'but got {type(scale)}') - if not mmcv.is_tuple_of(ratio, float): - raise TypeError('"ratio" must be a tuple of float, ' - f'but got {type(ratio)}') - - self.keys = keys - self.crop_size = crop_size - self.scale = scale - self.ratio = ratio - self.interpolation = interpolation - - def get_params(self, data): - """Get parameters for a random sized crop. - - Args: - data (np.ndarray): Image of type numpy array to be cropped. - - Returns: - A tuple containing the coordinates of the top left corner - and the chosen crop size. - """ - data_h, data_w = data.shape[:2] - area = data_h * data_w - - for _ in range(10): - target_area = random.uniform(*self.scale) * area - log_ratio = (math.log(self.ratio[0]), math.log(self.ratio[1])) - aspect_ratio = math.exp(random.uniform(*log_ratio)) - - crop_w = int(round(math.sqrt(target_area * aspect_ratio))) - crop_h = int(round(math.sqrt(target_area / aspect_ratio))) - - if 0 < crop_w <= data_w and 0 < crop_h <= data_h: - top = random.randint(0, data_h - crop_h) - left = random.randint(0, data_w - crop_w) - return top, left, crop_h, crop_w - - # Fall back to center crop - in_ratio = float(data_w) / float(data_h) - if (in_ratio < min(self.ratio)): - crop_w = data_w - crop_h = int(round(crop_w / min(self.ratio))) - elif (in_ratio > max(self.ratio)): - crop_h = data_h - crop_w = int(round(crop_h * max(self.ratio))) - else: # whole image - crop_w = data_w - crop_h = data_h - top = (data_h - crop_h) // 2 - left = (data_w - crop_w) // 2 - return top, left, crop_h, crop_w - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for k in self.keys: - top, left, crop_h, crop_w = self.get_params(results[k]) - crop_bbox = [top, left, crop_w, crop_h] - results[k] = results[k][top:top + crop_h, left:left + crop_w, ...] - results[k] = mmcv.imresize( - results[k], - self.crop_size, - return_scale=False, - interpolation=self.interpolation) - results[k + '_crop_bbox'] = crop_bbox - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, crop_size={self.crop_size}, ' - f'scale={self.scale}, ratio={self.ratio}, ' - f'interpolation={self.interpolation})') - return repr_str - - -@PIPELINES.register_module() -class FixedCrop: - """Crop paired data (at a specific position) to specific size for training. - - Args: - keys (Sequence[str]): The images to be cropped. - crop_size (Tuple[int]): Target spatial size (h, w). - crop_pos (Tuple[int]): Specific position (x, y). If set to None, - random initialize the position to crop paired data batch. - """ - - def __init__(self, keys, crop_size, crop_pos=None): - if not mmcv.is_tuple_of(crop_size, int): - raise TypeError( - 'Elements of crop_size must be int and crop_size must be' - f' tuple, but got {type(crop_size[0])} in {type(crop_size)}') - if not mmcv.is_tuple_of(crop_pos, int) and (crop_pos is not None): - raise TypeError( - 'Elements of crop_pos must be int and crop_pos must be' - f' tuple or None, but got {type(crop_pos[0])} in ' - f'{type(crop_pos)}') - - self.keys = keys - self.crop_size = crop_size - self.crop_pos = crop_pos - - def _crop(self, data, x_offset, y_offset, crop_w, crop_h): - crop_bbox = [x_offset, y_offset, crop_w, crop_h] - data_ = data[y_offset:y_offset + crop_h, x_offset:x_offset + crop_w, - ...] - return data_, crop_bbox - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - if isinstance(results[self.keys[0]], list): - data_h, data_w = results[self.keys[0]][0].shape[:2] - else: - data_h, data_w = results[self.keys[0]].shape[:2] - crop_h, crop_w = self.crop_size - crop_h = min(data_h, crop_h) - crop_w = min(data_w, crop_w) - - if self.crop_pos is None: - x_offset = np.random.randint(0, data_w - crop_w + 1) - y_offset = np.random.randint(0, data_h - crop_h + 1) - else: - x_offset, y_offset = self.crop_pos - crop_w = min(data_w - x_offset, crop_w) - crop_h = min(data_h - y_offset, crop_h) - - for k in self.keys: - images = results[k] - is_list = isinstance(images, list) - if not is_list: - images = [images] - cropped_images = [] - crop_bbox = None - for image in images: - # In fixed crop for paired images, sizes should be the same - if (image.shape[0] != data_h or image.shape[1] != data_w): - raise ValueError( - 'The sizes of paired images should be the same. ' - f'Expected ({data_h}, {data_w}), ' - f'but got ({image.shape[0]}, ' - f'{image.shape[1]}).') - data_, crop_bbox = self._crop(image, x_offset, y_offset, - crop_w, crop_h) - cropped_images.append(data_) - results[k + '_crop_bbox'] = crop_bbox - if not is_list: - cropped_images = cropped_images[0] - results[k] = cropped_images - results['crop_size'] = self.crop_size - results['crop_pos'] = self.crop_pos - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'keys={self.keys}, crop_size={self.crop_size}, ' - f'crop_pos={self.crop_pos}') - return repr_str - - -@PIPELINES.register_module() -class PairedRandomCrop: - """Paried random crop. - - It crops a pair of lq and gt images with corresponding locations. - It also supports accepting lq list and gt list. - Required keys are "scale", "lq", and "gt", - added or modified keys are "lq" and "gt". - - Args: - gt_patch_size (int): cropped gt patch size. - """ - - def __init__(self, gt_patch_size): - self.gt_patch_size = gt_patch_size - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - scale = results['scale'] - lq_patch_size = self.gt_patch_size // scale - - lq_is_list = isinstance(results['lq'], list) - if not lq_is_list: - results['lq'] = [results['lq']] - gt_is_list = isinstance(results['gt'], list) - if not gt_is_list: - results['gt'] = [results['gt']] - - h_lq, w_lq, _ = results['lq'][0].shape - h_gt, w_gt, _ = results['gt'][0].shape - - if h_gt != h_lq * scale or w_gt != w_lq * scale: - raise ValueError( - f'Scale mismatches. GT ({h_gt}, {w_gt}) is not {scale}x ' - f'multiplication of LQ ({h_lq}, {w_lq}).') - if h_lq < lq_patch_size or w_lq < lq_patch_size: - raise ValueError( - f'LQ ({h_lq}, {w_lq}) is smaller than patch size ' - f'({lq_patch_size}, {lq_patch_size}). Please check ' - f'{results["lq_path"][0]} and {results["gt_path"][0]}.') - - # randomly choose top and left coordinates for lq patch - top = np.random.randint(h_lq - lq_patch_size + 1) - left = np.random.randint(w_lq - lq_patch_size + 1) - # crop lq patch - results['lq'] = [ - v[top:top + lq_patch_size, left:left + lq_patch_size, ...] - for v in results['lq'] - ] - # crop corresponding gt patch - top_gt, left_gt = int(top * scale), int(left * scale) - results['gt'] = [ - v[top_gt:top_gt + self.gt_patch_size, - left_gt:left_gt + self.gt_patch_size, ...] for v in results['gt'] - ] - - if not lq_is_list: - results['lq'] = results['lq'][0] - if not gt_is_list: - results['gt'] = results['gt'][0] - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += f'(gt_patch_size={self.gt_patch_size})' - return repr_str - - -@PIPELINES.register_module() -class CropAroundCenter: - """Randomly crop the images around unknown area in the center 1/4 images. - - This cropping strategy is adopted in GCA matting. The `unknown area` is the - same as `semi-transparent area`. - https://arxiv.org/pdf/2001.04069.pdf - - It retains the center 1/4 images and resizes the images to 'crop_size'. - Required keys are "fg", "bg", "trimap" and "alpha", added or modified keys - are "crop_bbox", "fg", "bg", "trimap" and "alpha". - - Args: - crop_size (int | tuple): Desired output size. If int, square crop is - applied. - """ - - def __init__(self, crop_size): - if mmcv.is_tuple_of(crop_size, int): - assert len(crop_size) == 2, 'length of crop_size must be 2.' - elif not isinstance(crop_size, int): - raise TypeError('crop_size must be int or a tuple of int, but got ' - f'{type(crop_size)}') - self.crop_size = _pair(crop_size) - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - fg = results['fg'] - alpha = results['alpha'] - trimap = results['trimap'] - bg = results['bg'] - h, w = fg.shape[:2] - assert bg.shape == fg.shape, (f'shape of bg {bg.shape} should be the ' - f'same as fg {fg.shape}.') - - crop_h, crop_w = self.crop_size - # Make sure h >= crop_h, w >= crop_w. If not, rescale imgs - rescale_ratio = max(crop_h / h, crop_w / w) - if rescale_ratio > 1: - new_h = max(int(h * rescale_ratio), crop_h) - new_w = max(int(w * rescale_ratio), crop_w) - fg = mmcv.imresize(fg, (new_w, new_h), interpolation='nearest') - alpha = mmcv.imresize( - alpha, (new_w, new_h), interpolation='nearest') - trimap = mmcv.imresize( - trimap, (new_w, new_h), interpolation='nearest') - bg = mmcv.imresize(bg, (new_w, new_h), interpolation='bicubic') - h, w = new_h, new_w - - # resize to 1/4 to ignore small unknown patches - small_trimap = mmcv.imresize( - trimap, (w // 4, h // 4), interpolation='nearest') - # find unknown area in center 1/4 region - margin_h, margin_w = crop_h // 2, crop_w // 2 - sample_area = small_trimap[margin_h // 4:(h - margin_h) // 4, - margin_w // 4:(w - margin_w) // 4] - unknown_xs, unknown_ys = np.where(sample_area == 128) - unknown_num = len(unknown_xs) - if unknown_num < 10: - # too few unknown area in the center, crop from the whole image - top = np.random.randint(0, h - crop_h + 1) - left = np.random.randint(0, w - crop_w + 1) - else: - idx = np.random.randint(unknown_num) - top = unknown_xs[idx] * 4 - left = unknown_ys[idx] * 4 - bottom = top + crop_h - right = left + crop_w - - results['fg'] = fg[top:bottom, left:right] - results['alpha'] = alpha[top:bottom, left:right] - results['trimap'] = trimap[top:bottom, left:right] - results['bg'] = bg[top:bottom, left:right] - results['crop_bbox'] = (left, top, right, bottom) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(crop_size={self.crop_size})' - - -@PIPELINES.register_module() -class CropAroundUnknown: - """Crop around unknown area with a randomly selected scale. - - Randomly select the w and h from a list of (w, h). - Required keys are the keys in argument `keys`, added or - modified keys are "crop_bbox" and the keys in argument `keys`. - This class assumes value of "alpha" ranges from 0 to 255. - - Args: - keys (Sequence[str]): The images to be cropped. It must contain - 'alpha'. If unknown_source is set to 'trimap', then it must also - contain 'trimap'. - crop_sizes (list[int | tuple[int]]): List of (w, h) to be selected. - unknown_source (str, optional): Unknown area to select from. It must be - 'alpha' or 'tirmap'. Default to 'alpha'. - interpolations (str | list[str], optional): Interpolation method of - mmcv.imresize. The interpolation operation will be applied when - image size is smaller than the crop_size. If given as a list of - str, it should have the same length as `keys`. Or if given as a - str all the keys will be resized with the same method. - Default to 'bilinear'. - """ - - def __init__(self, - keys, - crop_sizes, - unknown_source='alpha', - interpolations='bilinear'): - if 'alpha' not in keys: - raise ValueError(f'"alpha" must be in keys, but got {keys}') - self.keys = keys - - if not isinstance(crop_sizes, list): - raise TypeError( - f'Crop sizes must be list, but got {type(crop_sizes)}.') - self.crop_sizes = [_pair(crop_size) for crop_size in crop_sizes] - if not mmcv.is_tuple_of(self.crop_sizes[0], int): - raise TypeError('Elements of crop_sizes must be int or tuple of ' - f'int, but got {type(self.crop_sizes[0][0])}.') - - if unknown_source not in ['alpha', 'trimap']: - raise ValueError('unknown_source must be "alpha" or "trimap", ' - f'but got {unknown_source}') - if unknown_source not in keys: - # it could only be trimap, since alpha is checked before - raise ValueError( - 'if unknown_source is "trimap", it must also be set in keys') - self.unknown_source = unknown_source - - if isinstance(interpolations, str): - self.interpolations = [interpolations] * len(self.keys) - elif mmcv.is_list_of(interpolations, - str) and len(interpolations) == len(self.keys): - self.interpolations = interpolations - else: - raise TypeError( - 'interpolations must be a str or list of str with ' - f'the same length as keys, but got {interpolations}') - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - h, w = results[self.keys[0]].shape[:2] - - rand_ind = np.random.randint(len(self.crop_sizes)) - crop_h, crop_w = self.crop_sizes[rand_ind] - - # Make sure h >= crop_h, w >= crop_w. If not, rescale imgs - rescale_ratio = max(crop_h / h, crop_w / w) - if rescale_ratio > 1: - h = max(int(h * rescale_ratio), crop_h) - w = max(int(w * rescale_ratio), crop_w) - for key, interpolation in zip(self.keys, self.interpolations): - results[key] = mmcv.imresize( - results[key], (w, h), interpolation=interpolation) - - # Select the cropping top-left point which is an unknown pixel - if self.unknown_source == 'alpha': - unknown = (results['alpha'] > 0) & (results['alpha'] < 255) - else: - unknown = results['trimap'] == 128 - top, left = random_choose_unknown(unknown.squeeze(), (crop_h, crop_w)) - - bottom = top + crop_h - right = left + crop_w - - for key in self.keys: - results[key] = results[key][top:bottom, left:right] - results['crop_bbox'] = (left, top, right, bottom) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, crop_sizes={self.crop_sizes}, ' - f"unknown_source='{self.unknown_source}', " - f'interpolations={self.interpolations})') - return repr_str - - -@PIPELINES.register_module() -class CropAroundFg: - """Crop around the whole foreground in the segmentation mask. - - Required keys are "seg" and the keys in argument `keys`. - Meanwhile, "seg" must be in argument `keys`. Added or modified keys are - "crop_bbox" and the keys in argument `keys`. - - Args: - keys (Sequence[str]): The images to be cropped. It must contain - 'seg'. - bd_ratio_range (tuple, optional): The range of the boundary (bd) ratio - to select from. The boundary ratio is the ratio of the boundary to - the minimal bbox that contains the whole foreground given by - segmentation. Default to (0.1, 0.4). - test_mode (bool): Whether use test mode. In test mode, the tight crop - area of foreground will be extended to the a square. - Default to False. - """ - - def __init__(self, keys, bd_ratio_range=(0.1, 0.4), test_mode=False): - if 'seg' not in keys: - raise ValueError(f'"seg" must be in keys, but got {keys}') - if (not mmcv.is_tuple_of(bd_ratio_range, float) - or len(bd_ratio_range) != 2): - raise TypeError('bd_ratio_range must be a tuple of 2 int, but got ' - f'{bd_ratio_range}') - self.keys = keys - self.bd_ratio_range = bd_ratio_range - self.test_mode = test_mode - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - seg = results['seg'] - height, width = seg.shape[:2] - - # get foreground bbox - fg_coor = np.array(np.where(seg)) - top, left = np.amin(fg_coor, axis=1) - bottom, right = np.amax(fg_coor, axis=1) - - # enlarge bbox - long_side = np.maximum(bottom - top, right - left) - if self.test_mode: - bottom = top + long_side - right = left + long_side - boundary_ratio = np.random.uniform(*self.bd_ratio_range) - boundary = int(np.round(boundary_ratio * long_side)) - # NOTE: Different from the original repo, we keep track of the four - # corners of the bbox (left, top, right, bottom) while the original - # repo use (top, left, height, width) to represent bbox. This may - # introduce an difference of 1 pixel. - top = max(top - boundary, 0) - left = max(left - boundary, 0) - bottom = min(bottom + boundary, height) - right = min(right + boundary, width) - - for key in self.keys: - results[key] = results[key][top:bottom, left:right] - results['crop_bbox'] = (left, top, right, bottom) - return results - - -@PIPELINES.register_module() -class ModCrop: - """Mod crop gt images, used during testing. - - Required keys are "scale" and "gt", - added or modified keys are "gt". - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - img = results['gt'].copy() - scale = results['scale'] - if img.ndim in [2, 3]: - h, w = img.shape[0], img.shape[1] - h_remainder, w_remainder = h % scale, w % scale - img = img[:h - h_remainder, :w - w_remainder, ...] - else: - raise ValueError(f'Wrong img ndim: {img.ndim}.') - results['gt'] = img - return results - - -@PIPELINES.register_module() -class CropLike: - """Crop/pad the image in the target_key according to the size of image - in the reference_key . - - Args: - target_key (str): The key needs to be cropped. - reference_key (str | None): The reference key, need its size. - Default: None. - """ - - def __init__(self, target_key, reference_key=None): - - assert reference_key and target_key - self.target_key = target_key - self.reference_key = reference_key - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - Require self.target_key and self.reference_key. - - Returns: - dict: A dict containing the processed data and information. - Modify self.target_key. - """ - size = results[self.reference_key].shape - old_image = results[self.target_key] - old_size = old_image.shape - h, w = old_size[:2] - new_size = size[:2] + old_size[2:] - h_cover, w_cover = min(h, size[0]), min(w, size[1]) - - format_image = np.zeros(new_size, dtype=old_image.dtype) - format_image[:h_cover, :w_cover] = old_image[:h_cover, :w_cover] - results[self.target_key] = format_image - - return results - - def __repr__(self): - return (self.__class__.__name__ + f' target_key={self.target_key}, ' + - f'reference_key={self.reference_key}') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/formating.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/formating.py deleted file mode 100755 index 6ae98c050..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/formating.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from collections.abc import Sequence - -import mmcv -import numpy as np -import torch -from mmcv.parallel import DataContainer as DC -from torch.nn import functional as F - -from ..registry import PIPELINES - - -def to_tensor(data): - """Convert objects of various python types to :obj:`torch.Tensor`. - - Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`, - :class:`Sequence`, :class:`int` and :class:`float`. - """ - if isinstance(data, torch.Tensor): - return data - if isinstance(data, np.ndarray): - return torch.from_numpy(data) - if isinstance(data, Sequence) and not mmcv.is_str(data): - return torch.tensor(data) - if isinstance(data, int): - return torch.LongTensor([data]) - if isinstance(data, float): - return torch.FloatTensor([data]) - - raise TypeError(f'type {type(data)} cannot be converted to tensor.') - - -@PIPELINES.register_module() -class ToTensor: - """Convert some values in results dict to `torch.Tensor` type - in data loader pipeline. - - Args: - keys (Sequence[str]): Required keys to be converted. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - results[key] = to_tensor(results[key]) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' - - -@PIPELINES.register_module() -class ImageToTensor: - """Convert image type to `torch.Tensor` type. - - Args: - keys (Sequence[str]): Required keys to be converted. - to_float32 (bool): Whether convert numpy image array to np.float32 - before converted to tensor. Default: True. - """ - - def __init__(self, keys, to_float32=True): - self.keys = keys - self.to_float32 = to_float32 - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - # deal with gray scale img: expand a color channel - if len(results[key].shape) == 2: - results[key] = results[key][..., None] - if self.to_float32 and not isinstance(results[key], np.float32): - results[key] = results[key].astype(np.float32) - results[key] = to_tensor(results[key].transpose(2, 0, 1)) - return results - - def __repr__(self): - return self.__class__.__name__ + ( - f'(keys={self.keys}, to_float32={self.to_float32})') - - -@PIPELINES.register_module() -class FramesToTensor(ImageToTensor): - """Convert frames type to `torch.Tensor` type. - - It accepts a list of frames, converts each to `torch.Tensor` type and then - concatenates in a new dimension (dim=0). - - Args: - keys (Sequence[str]): Required keys to be converted. - to_float32 (bool): Whether convert numpy image array to np.float32 - before converted to tensor. Default: True. - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if not isinstance(results[key], list): - raise TypeError(f'results["{key}"] should be a list, ' - f'but got {type(results[key])}') - for idx, v in enumerate(results[key]): - # deal with gray scale img: expand a color channel - if len(v.shape) == 2: - v = v[..., None] - if self.to_float32 and not isinstance(v, np.float32): - v = v.astype(np.float32) - results[key][idx] = to_tensor(v.transpose(2, 0, 1)) - results[key] = torch.stack(results[key], dim=0) - if results[key].size(0) == 1: - results[key].squeeze_() - return results - - -@PIPELINES.register_module() -class GetMaskedImage: - """Get masked image. - - Args: - img_name (str): Key for clean image. - mask_name (str): Key for mask image. The mask shape should be - (h, w, 1) while '1' indicate holes and '0' indicate valid - regions. - """ - - def __init__(self, img_name='gt_img', mask_name='mask'): - self.img_name = img_name - self.mask_name = mask_name - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - clean_img = results[self.img_name] - mask = results[self.mask_name] - - masked_img = clean_img * (1. - mask) - results['masked_img'] = masked_img - - return results - - def __repr__(self): - return self.__class__.__name__ + ( - f"(img_name='{self.img_name}', mask_name='{self.mask_name}')") - - -@PIPELINES.register_module() -class FormatTrimap: - """Convert trimap (tensor) to one-hot representation. - - It transforms the trimap label from (0, 128, 255) to (0, 1, 2). If - ``to_onehot`` is set to True, the trimap will convert to one-hot tensor of - shape (3, H, W). Required key is "trimap", added or modified key are - "trimap" and "to_onehot". - - Args: - to_onehot (bool): whether convert trimap to one-hot tensor. Default: - ``False``. - """ - - def __init__(self, to_onehot=False): - self.to_onehot = to_onehot - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - trimap = results['trimap'].squeeze() - trimap[trimap == 128] = 1 - trimap[trimap == 255] = 2 - if self.to_onehot: - trimap = F.one_hot(trimap.to(torch.long), num_classes=3) - trimap = trimap.permute(2, 0, 1) - else: - trimap = trimap[None, ...] # expand the channels dimension - results['trimap'] = trimap.float() - results['meta'].data['to_onehot'] = self.to_onehot - return results - - def __repr__(self): - return self.__class__.__name__ + f'(to_onehot={self.to_onehot})' - - -@PIPELINES.register_module() -class Collect: - """Collect data from the loader relevant to the specific task. - - This is usually the last stage of the data loader pipeline. Typically keys - is set to some subset of "img", "gt_labels". - - The "img_meta" item is always populated. The contents of the "meta" - dictionary depends on "meta_keys". - - Args: - keys (Sequence[str]): Required keys to be collected. - meta_keys (Sequence[str]): Required keys to be collected to "meta". - Default: None. - """ - - def __init__(self, keys, meta_keys=None): - self.keys = keys - self.meta_keys = meta_keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - data = {} - img_meta = {} - for key in self.meta_keys: - img_meta[key] = results[key] - data['meta'] = DC(img_meta, cpu_only=True) - for key in self.keys: - data[key] = results[key] - return data - - def __repr__(self): - return self.__class__.__name__ + ( - f'(keys={self.keys}, meta_keys={self.meta_keys})') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/loading.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/loading.py deleted file mode 100755 index c7f162732..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/loading.py +++ /dev/null @@ -1,562 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from pathlib import Path - -import mmcv -import numpy as np -from mmcv.fileio import FileClient - -from mmedit.core.mask import (bbox2mask, brush_stroke_mask, get_irregular_mask, - random_bbox) -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class LoadImageFromFile: - """Load image from file. - - Args: - io_backend (str): io backend where images are store. Default: 'disk'. - key (str): Keys in results to find corresponding path. Default: 'gt'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - convert_to (str | None): The color space of the output image. If None, - no conversion is conducted. Default: None. - save_original_img (bool): If True, maintain a copy of the image in - `results` dict with name of `f'ori_{key}'`. Default: False. - use_cache (bool): If True, load all images at once. Default: False. - backend (str): The image loading backend type. Options are `cv2`, - `pillow`, and 'turbojpeg'. Default: None. - kwargs (dict): Args for file client. - """ - - def __init__(self, - io_backend='disk', - key='gt', - flag='color', - channel_order='bgr', - convert_to=None, - save_original_img=False, - use_cache=False, - backend=None, - **kwargs): - - self.io_backend = io_backend - self.key = key - self.flag = flag - self.save_original_img = save_original_img - self.channel_order = channel_order - self.convert_to = convert_to - self.kwargs = kwargs - self.file_client = None - self.use_cache = use_cache - self.cache = dict() if use_cache else None - self.backend = backend - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - filepath = str(results[f'{self.key}_path']) - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - if self.use_cache: - if filepath in self.cache: - img = self.cache[filepath] - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - self.cache[filepath] = img - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - - if self.convert_to is not None: - if self.channel_order == 'bgr' and self.convert_to.lower() == 'y': - img = mmcv.bgr2ycbcr(img, y_only=True) - elif self.channel_order == 'rgb': - img = mmcv.rgb2ycbcr(img, y_only=True) - else: - raise ValueError('Currently support only "bgr2ycbcr" or ' - '"bgr2ycbcr".') - if img.ndim == 2: - img = np.expand_dims(img, axis=2) - - results[self.key] = img - results[f'{self.key}_path'] = filepath - results[f'{self.key}_ori_shape'] = img.shape - if self.save_original_img: - results[f'ori_{self.key}'] = img.copy() - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(io_backend={self.io_backend}, key={self.key}, ' - f'flag={self.flag}, save_original_img={self.save_original_img}, ' - f'channel_order={self.channel_order}, use_cache={self.use_cache})') - return repr_str - - -@PIPELINES.register_module() -class LoadImageFromFileList(LoadImageFromFile): - """Load image from file list. - - It accepts a list of path and read each frame from each path. A list - of frames will be returned. - - Args: - io_backend (str): io backend where images are store. Default: 'disk'. - key (str): Keys in results to find corresponding path. Default: 'gt'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - convert_to (str | None): The color space of the output image. If None, - no conversion is conducted. Default: None. - save_original_img (bool): If True, maintain a copy of the image in - `results` dict with name of `f'ori_{key}'`. Default: False. - use_cache (bool): If True, load all images at once. Default: False. - backend (str): The image loading backend type. Options are `cv2`, - `pillow`, and 'turbojpeg'. Default: None. - kwargs (dict): Args for file client. - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - filepaths = results[f'{self.key}_path'] - if not isinstance(filepaths, list): - raise TypeError( - f'filepath should be list, but got {type(filepaths)}') - - filepaths = [str(v) for v in filepaths] - - imgs = [] - shapes = [] - if self.save_original_img: - ori_imgs = [] - for filepath in filepaths: - if self.use_cache: - if filepath in self.cache: - img = self.cache[filepath] - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - self.cache[filepath] = img - else: - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, - flag=self.flag, - channel_order=self.channel_order, - backend=self.backend) # HWC - - # convert to y-channel, if specified - if self.convert_to is not None: - if self.channel_order == 'bgr' and self.convert_to.lower( - ) == 'y': - img = mmcv.bgr2ycbcr(img, y_only=True) - elif self.channel_order == 'rgb': - img = mmcv.rgb2ycbcr(img, y_only=True) - else: - raise ValueError('Currently support only "bgr2ycbcr" or ' - '"bgr2ycbcr".') - - if img.ndim == 2: - img = np.expand_dims(img, axis=2) - - imgs.append(img) - shapes.append(img.shape) - if self.save_original_img: - ori_imgs.append(img.copy()) - - results[self.key] = imgs - results[f'{self.key}_path'] = filepaths - results[f'{self.key}_ori_shape'] = shapes - if self.save_original_img: - results[f'ori_{self.key}'] = ori_imgs - - return results - - -@PIPELINES.register_module() -class RandomLoadResizeBg: - """Randomly load a background image and resize it. - - Required key is "fg", added key is "bg". - - Args: - bg_dir (str): Path of directory to load background images from. - io_backend (str): io backend where images are store. Default: 'disk'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - kwargs (dict): Args for file client. - """ - - def __init__(self, - bg_dir, - io_backend='disk', - flag='color', - channel_order='bgr', - **kwargs): - self.bg_dir = bg_dir - self.bg_list = list(mmcv.scandir(bg_dir)) - self.io_backend = io_backend - self.flag = flag - self.channel_order = channel_order - self.kwargs = kwargs - self.file_client = None - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - h, w = results['fg'].shape[:2] - idx = np.random.randint(len(self.bg_list)) - filepath = Path(self.bg_dir).joinpath(self.bg_list[idx]) - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, flag=self.flag, channel_order=self.channel_order) # HWC - bg = mmcv.imresize(img, (w, h), interpolation='bicubic') - results['bg'] = bg - return results - - def __repr__(self): - return self.__class__.__name__ + f"(bg_dir='{self.bg_dir}')" - - -@PIPELINES.register_module() -class LoadMask: - """Load Mask for multiple types. - - For different types of mask, users need to provide the corresponding - config dict. - - Example config for bbox: - - .. code-block:: python - - config = dict(img_shape=(256, 256), max_bbox_shape=128) - - Example config for irregular: - - .. code-block:: python - - config = dict( - img_shape=(256, 256), - num_vertices=(4, 12), - max_angle=4., - length_range=(10, 100), - brush_width=(10, 40), - area_ratio_range=(0.15, 0.5)) - - Example config for ff: - - .. code-block:: python - - config = dict( - img_shape=(256, 256), - num_vertices=(4, 12), - mean_angle=1.2, - angle_range=0.4, - brush_width=(12, 40)) - - Example config for set: - - .. code-block:: python - - config = dict( - mask_list_file='xxx/xxx/ooxx.txt', - prefix='/xxx/xxx/ooxx/', - io_backend='disk', - flag='unchanged', - file_client_kwargs=dict() - ) - - The mask_list_file contains the list of mask file name like this: - test1.jpeg - test2.jpeg - ... - ... - - The prefix gives the data path. - - Args: - mask_mode (str): Mask mode in ['bbox', 'irregular', 'ff', 'set', - 'file']. - * bbox: square bounding box masks. - * irregular: irregular holes. - * ff: free-form holes from DeepFillv2. - * set: randomly get a mask from a mask set. - * file: get mask from 'mask_path' in results. - mask_config (dict): Params for creating masks. Each type of mask needs - different configs. - """ - - def __init__(self, mask_mode='bbox', mask_config=None): - self.mask_mode = mask_mode - self.mask_config = dict() if mask_config is None else mask_config - assert isinstance(self.mask_config, dict) - - # set init info if needed in some modes - self._init_info() - - def _init_info(self): - if self.mask_mode == 'set': - # get mask list information - self.mask_list = [] - mask_list_file = self.mask_config['mask_list_file'] - with open(mask_list_file, 'r') as f: - for line in f: - line_split = line.strip().split(' ') - mask_name = line_split[0] - self.mask_list.append( - Path(self.mask_config['prefix']).joinpath(mask_name)) - self.mask_set_size = len(self.mask_list) - self.io_backend = self.mask_config['io_backend'] - self.flag = self.mask_config['flag'] - self.file_client_kwargs = self.mask_config['file_client_kwargs'] - self.file_client = None - elif self.mask_mode == 'file': - self.io_backend = 'disk' - self.flag = 'unchanged' - self.file_client_kwargs = dict() - self.file_client = None - - def _get_random_mask_from_set(self): - if self.file_client is None: - self.file_client = FileClient(self.io_backend, - **self.file_client_kwargs) - # minus 1 to avoid out of range error - mask_idx = np.random.randint(0, self.mask_set_size) - mask_bytes = self.file_client.get(self.mask_list[mask_idx]) - mask = mmcv.imfrombytes(mask_bytes, flag=self.flag) # HWC, BGR - if mask.ndim == 2: - mask = np.expand_dims(mask, axis=2) - else: - mask = mask[:, :, 0:1] - - mask[mask > 0] = 1. - return mask - - def _get_mask_from_file(self, path): - if self.file_client is None: - self.file_client = FileClient(self.io_backend, - **self.file_client_kwargs) - mask_bytes = self.file_client.get(path) - mask = mmcv.imfrombytes(mask_bytes, flag=self.flag) # HWC, BGR - if mask.ndim == 2: - mask = np.expand_dims(mask, axis=2) - else: - mask = mask[:, :, 0:1] - - mask[mask > 0] = 1. - return mask - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - if self.mask_mode == 'bbox': - mask_bbox = random_bbox(**self.mask_config) - mask = bbox2mask(self.mask_config['img_shape'], mask_bbox) - results['mask_bbox'] = mask_bbox - elif self.mask_mode == 'irregular': - mask = get_irregular_mask(**self.mask_config) - elif self.mask_mode == 'set': - mask = self._get_random_mask_from_set() - elif self.mask_mode == 'ff': - mask = brush_stroke_mask(**self.mask_config) - elif self.mask_mode == 'file': - mask = self._get_mask_from_file(results['mask_path']) - else: - raise NotImplementedError( - f'Mask mode {self.mask_mode} has not been implemented.') - results['mask'] = mask - return results - - def __repr__(self): - return self.__class__.__name__ + f"(mask_mode='{self.mask_mode}')" - - -@PIPELINES.register_module() -class GetSpatialDiscountMask: - """Get spatial discounting mask constant. - - Spatial discounting mask is first introduced in: - Generative Image Inpainting with Contextual Attention. - - Args: - gamma (float, optional): Gamma for computing spatial discounting. - Defaults to 0.99. - beta (float, optional): Beta for computing spatial discounting. - Defaults to 1.5. - """ - - def __init__(self, gamma=0.99, beta=1.5): - self.gamma = gamma - self.beta = beta - - def spatial_discount_mask(self, mask_width, mask_height): - """Generate spatial discounting mask constant. - - Args: - mask_width (int): The width of bbox hole. - mask_height (int): The height of bbox height. - - Returns: - np.ndarray: Spatial discounting mask. - """ - w, h = np.meshgrid(np.arange(mask_width), np.arange(mask_height)) - grid_stack = np.stack([h, w], axis=2) - mask_values = (self.gamma**(np.minimum( - grid_stack, [mask_height - 1, mask_width - 1] - grid_stack) * - self.beta)).max( - axis=2, keepdims=True) - - return mask_values - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - - mask_bbox = results['mask_bbox'] - mask = results['mask'] - mask_height, mask_width = mask_bbox[-2:] - discount_hole = self.spatial_discount_mask(mask_width, mask_height) - discount_mask = np.zeros_like(mask) - discount_mask[mask_bbox[0]:mask_bbox[0] + mask_height, - mask_bbox[1]:mask_bbox[1] + mask_width, - ...] = discount_hole - - results['discount_mask'] = discount_mask - - return results - - def __repr__(self): - return self.__class__.__name__ + (f'(gamma={self.gamma}, ' - f'beta={self.beta})') - - -@PIPELINES.register_module() -class LoadPairedImageFromFile(LoadImageFromFile): - """Load a pair of images from file. - - Each sample contains a pair of images, which are concatenated in the w - dimension (a|b). This is a special loading class for generation paired - dataset. It loads a pair of images as the common loader does and crops - it into two images with the same shape in different domains. - - Required key is "pair_path". Added or modified keys are "pair", - "pair_ori_shape", "ori_pair", "img_a", "img_b", "img_a_path", - "img_b_path", "img_a_ori_shape", "img_b_ori_shape", "ori_img_a" and - "ori_img_b". - - Args: - io_backend (str): io backend where images are store. Default: 'disk'. - key (str): Keys in results to find corresponding path. Default: 'gt'. - flag (str): Loading flag for images. Default: 'color'. - channel_order (str): Order of channel, candidates are 'bgr' and 'rgb'. - Default: 'bgr'. - save_original_img (bool): If True, maintain a copy of the image in - `results` dict with name of `f'ori_{key}'`. Default: False. - kwargs (dict): Args for file client. - """ - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - if self.file_client is None: - self.file_client = FileClient(self.io_backend, **self.kwargs) - filepath = str(results[f'{self.key}_path']) - img_bytes = self.file_client.get(filepath) - img = mmcv.imfrombytes( - img_bytes, flag=self.flag, channel_order=self.channel_order) # HWC - if img.ndim == 2: - img = np.expand_dims(img, axis=2) - - results[self.key] = img - results[f'{self.key}_path'] = filepath - results[f'{self.key}_ori_shape'] = img.shape - if self.save_original_img: - results[f'ori_{self.key}'] = img.copy() - - # crop pair into a and b - w = img.shape[1] - if w % 2 != 0: - raise ValueError( - f'The width of image pair must be even number, but got {w}.') - new_w = w // 2 - img_a = img[:, :new_w, :] - img_b = img[:, new_w:, :] - - results['img_a'] = img_a - results['img_b'] = img_b - results['img_a_path'] = filepath - results['img_b_path'] = filepath - results['img_a_ori_shape'] = img_a.shape - results['img_b_ori_shape'] = img_b.shape - if self.save_original_img: - results['ori_img_a'] = img_a.copy() - results['ori_img_b'] = img_b.copy() - - return results diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/matlab_like_resize.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/matlab_like_resize.py deleted file mode 100755 index b6f63102a..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/matlab_like_resize.py +++ /dev/null @@ -1,275 +0,0 @@ -# This code is referenced from matlab_imresize with modifications -# Reference: https://github.com/fatheral/matlab_imresize/blob/master/imresize.py # noqa -# Original licence: Copyright (c) 2020 fatheral, under the MIT License. -import numpy as np - -from ..registry import PIPELINES - - -def get_size_from_scale(input_size, scale_factor): - """Get the output size given input size and scale factor. - - Args: - input_size (tuple): The size of the input image. - scale_factor (float): The resize factor. - - Returns: - list[int]: The size of the output image. - """ - - output_shape = [ - int(np.ceil(scale * shape)) - for (scale, shape) in zip(scale_factor, input_size) - ] - - return output_shape - - -def get_scale_from_size(input_size, output_size): - """Get the scale factor given input size and output size. - - Args: - input_size (tuple(int)): The size of the input image. - output_size (tuple(int)): The size of the output image. - - Returns: - list[float]: The scale factor of each dimension. - """ - - scale = [ - 1.0 * output_shape / input_shape - for (input_shape, output_shape) in zip(input_size, output_size) - ] - - return scale - - -def _cubic(x): - """ Cubic function. - - Args: - x (ndarray): The distance from the center position. - - Returns: - ndarray: The weight corresponding to a particular distance. - - """ - - x = np.array(x, dtype=np.float32) - x_abs = np.abs(x) - x_abs_sq = x_abs**2 - x_abs_cu = x_abs_sq * x_abs - - # if |x| <= 1: y = 1.5|x|^3 - 2.5|x|^2 + 1 - # if 1 < |x| <= 2: -0.5|x|^3 + 2.5|x|^2 - 4|x| + 2 - f = (1.5 * x_abs_cu - 2.5 * x_abs_sq + 1) * (x_abs <= 1) + ( - -0.5 * x_abs_cu + 2.5 * x_abs_sq - 4 * x_abs + 2) * ((1 < x_abs) & - (x_abs <= 2)) - - return f - - -def get_weights_indices(input_length, output_length, scale, kernel, - kernel_width): - """Get weights and indices for interpolation. - - Args: - input_length (int): Length of the input sequence. - output_length (int): Length of the output sequence. - scale (float): Scale factor. - kernel (func): The kernel used for resizing. - kernel_width (int): The width of the kernel. - - Returns: - list[ndarray]: The weights and the indices for interpolation. - - - """ - if scale < 1: # modified kernel for antialiasing - - def h(x): - return scale * kernel(scale * x) - - kernel_width = 1.0 * kernel_width / scale - else: - h = kernel - kernel_width = kernel_width - - # coordinates of output - x = np.arange(1, output_length + 1).astype(np.float32) - - # coordinates of input - u = x / scale + 0.5 * (1 - 1 / scale) - left = np.floor(u - kernel_width / 2) # leftmost pixel - p = int(np.ceil(kernel_width)) + 2 # maximum number of pixels - - # indices of input pixels - ind = left[:, np.newaxis, ...] + np.arange(p) - indices = ind.astype(np.int32) - - # weights of input pixels - weights = h(u[:, np.newaxis, ...] - indices - 1) - - weights = weights / np.sum(weights, axis=1)[:, np.newaxis, ...] - - # remove all-zero columns - aux = np.concatenate( - (np.arange(input_length), np.arange(input_length - 1, -1, - step=-1))).astype(np.int32) - indices = aux[np.mod(indices, aux.size)] - ind2store = np.nonzero(np.any(weights, axis=0)) - weights = weights[:, ind2store] - indices = indices[:, ind2store] - - return weights, indices - - -def resize_along_dim(img_in, weights, indices, dim): - """Resize along a specific dimension. - - Args: - img_in (ndarray): The input image. - weights (ndarray): The weights used for interpolation, computed from - [get_weights_indices]. - indices (ndarray): The indices used for interpolation, computed from - [get_weights_indices]. - dim (int): Which dimension to undergo interpolation. - - Returns: - ndarray: Interpolated (along one dimension) image. - """ - - img_in = img_in.astype(np.float32) - w_shape = weights.shape - output_shape = list(img_in.shape) - output_shape[dim] = w_shape[0] - img_out = np.zeros(output_shape) - - if dim == 0: - for i in range(w_shape[0]): - w = weights[i, :][np.newaxis, ...] - ind = indices[i, :] - img_slice = img_in[ind, :] - img_out[i] = np.sum(np.squeeze(img_slice, axis=0) * w.T, axis=0) - elif dim == 1: - for i in range(w_shape[0]): - w = weights[i, :][:, :, np.newaxis] - ind = indices[i, :] - img_slice = img_in[:, ind] - img_out[:, i] = np.sum(np.squeeze(img_slice, axis=1) * w.T, axis=1) - - if img_in.dtype == np.uint8: - img_out = np.clip(img_out, 0, 255) - return np.around(img_out).astype(np.uint8) - else: - return img_out - - -@PIPELINES.register_module() -class MATLABLikeResize: - """Resize the input image using MATLAB-like downsampling. - - Currently support bicubic interpolation only. Note that the output of - this function is slightly different from the official MATLAB function. - - Required keys are the keys in attribute "keys". Added or modified keys - are "scale" and "output_shape", and the keys in attribute "keys". - - Args: - keys (list[str]): A list of keys whose values are modified. - scale (float | None, optional): The scale factor of the resize - operation. If None, it will be determined by output_shape. - Default: None. - output_shape (tuple(int) | None, optional): The size of the output - image. If None, it will be determined by scale. Note that if - scale is provided, output_shape will not be used. - Default: None. - kernel (str, optional): The kernel for the resize operation. - Currently support 'bicubic' only. Default: 'bicubic'. - kernel_width (float): The kernel width. Currently support 4.0 only. - Default: 4.0. - """ - - def __init__(self, - keys, - scale=None, - output_shape=None, - kernel='bicubic', - kernel_width=4.0): - - if kernel.lower() != 'bicubic': - raise ValueError('Currently support bicubic kernel only.') - - if float(kernel_width) != 4.0: - raise ValueError('Current support only width=4 only.') - - if scale is None and output_shape is None: - raise ValueError('"scale" and "output_shape" cannot be both None') - - self.kernel_func = _cubic - self.keys = keys - self.scale = scale - self.output_shape = output_shape - self.kernel = kernel - self.kernel_width = kernel_width - - def _resize(self, img): - weights = {} - indices = {} - - # compute scale and output_size - if self.scale is not None: - scale = float(self.scale) - scale = [scale, scale] - output_size = get_size_from_scale(img.shape, scale) - else: - scale = get_scale_from_size(img.shape, self.output_shape) - output_size = list(self.output_shape) - - # apply cubic interpolation along two dimensions - order = np.argsort(np.array(scale)) - for k in range(2): - key = (img.shape[k], output_size[k], scale[k], self.kernel_func, - self.kernel_width) - weight, index = get_weights_indices(img.shape[k], output_size[k], - scale[k], self.kernel_func, - self.kernel_width) - weights[key] = weight - indices[key] = index - - output = np.copy(img) - if output.ndim == 2: # grayscale image - output = output[:, :, np.newaxis] - - for k in range(2): - dim = order[k] - key = (img.shape[dim], output_size[dim], scale[dim], - self.kernel_func, self.kernel_width) - output = resize_along_dim(output, weights[key], indices[key], dim) - - return output - - def __call__(self, results): - for key in self.keys: - is_single_image = False - if isinstance(results[key], np.ndarray): - is_single_image = True - results[key] = [results[key]] - - results[key] = [self._resize(img) for img in results[key]] - - if is_single_image: - results[key] = results[key][0] - - results['scale'] = self.scale - results['output_shape'] = self.output_shape - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += ( - f'(keys={self.keys}, scale={self.scale}, ' - f'output_shape={self.output_shape}, ' - f'kernel={self.kernel}, kernel_width={self.kernel_width})') - return repr_str diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/normalization.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/normalization.py deleted file mode 100755 index 8ff774d7f..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/normalization.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import mmcv -import numpy as np - -from ..registry import PIPELINES - - -@PIPELINES.register_module() -class Normalize: - """Normalize images with the given mean and std value. - - Required keys are the keys in attribute "keys", added or modified keys are - the keys in attribute "keys" and these keys with postfix '_norm_cfg'. - It also supports normalizing a list of images. - - Args: - keys (Sequence[str]): The images to be normalized. - mean (np.ndarray): Mean values of different channels. - std (np.ndarray): Std values of different channels. - to_rgb (bool): Whether to convert channels from BGR to RGB. - """ - - def __init__(self, keys, mean, std, to_rgb=False, save_original=False): - self.keys = keys - self.mean = np.array(mean, dtype=np.float32) - self.std = np.array(std, dtype=np.float32) - self.to_rgb = to_rgb - self.save_original = save_original - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if isinstance(results[key], list): - if self.save_original: - results[key + '_unnormalised'] = [ - v.copy() for v in results[key] - ] - results[key] = [ - mmcv.imnormalize(v, self.mean, self.std, self.to_rgb) - for v in results[key] - ] - else: - if self.save_original: - results[key + '_unnormalised'] = results[key].copy() - results[key] = mmcv.imnormalize(results[key], self.mean, - self.std, self.to_rgb) - - results['img_norm_cfg'] = dict( - mean=self.mean, std=self.std, to_rgb=self.to_rgb) - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(keys={self.keys}, mean={self.mean}, std={self.std}, ' - f'to_rgb={self.to_rgb})') - - return repr_str - - -@PIPELINES.register_module() -class RescaleToZeroOne: - """Transform the images into a range between 0 and 1. - - Required keys are the keys in attribute "keys", added or modified keys are - the keys in attribute "keys". - It also supports rescaling a list of images. - - Args: - keys (Sequence[str]): The images to be transformed. - """ - - def __init__(self, keys): - self.keys = keys - - def __call__(self, results): - """Call function. - - Args: - results (dict): A dict containing the necessary information and - data for augmentation. - - Returns: - dict: A dict containing the processed data and information. - """ - for key in self.keys: - if isinstance(results[key], list): - results[key] = [ - v.astype(np.float32) / 255. for v in results[key] - ] - else: - results[key] = results[key].astype(np.float32) / 255. - return results - - def __repr__(self): - return self.__class__.__name__ + f'(keys={self.keys})' diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/random_degradations.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/random_degradations.py deleted file mode 100755 index 989e59800..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/random_degradations.py +++ /dev/null @@ -1,556 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import io -import logging -import random - -import cv2 -import numpy as np - -from mmedit.datasets.pipelines import blur_kernels as blur_kernels -from ..registry import PIPELINES - -try: - import av - has_av = True -except ImportError: - has_av = False - - -@PIPELINES.register_module() -class RandomBlur: - """Apply random blur to the input. - - Modified keys are the attributed specified in "keys". - - Args: - params (dict): A dictionary specifying the degradation settings. - keys (list[str]): A list specifying the keys whose values are - modified. - """ - - def __init__(self, params, keys): - self.keys = keys - self.params = params - - def get_kernel(self, num_kernels): - kernel_type = np.random.choice( - self.params['kernel_list'], p=self.params['kernel_prob']) - kernel_size = random.choice(self.params['kernel_size']) - - sigma_x_range = self.params.get('sigma_x', [0, 0]) - sigma_x = np.random.uniform(sigma_x_range[0], sigma_x_range[1]) - sigma_x_step = self.params.get('sigma_x_step', 0) - - sigma_y_range = self.params.get('sigma_y', [0, 0]) - sigma_y = np.random.uniform(sigma_y_range[0], sigma_y_range[1]) - sigma_y_step = self.params.get('sigma_y_step', 0) - - rotate_angle_range = self.params.get('rotate_angle', [-np.pi, np.pi]) - rotate_angle = np.random.uniform(rotate_angle_range[0], - rotate_angle_range[1]) - rotate_angle_step = self.params.get('rotate_angle_step', 0) - - beta_gau_range = self.params.get('beta_gaussian', [0.5, 4]) - beta_gau = np.random.uniform(beta_gau_range[0], beta_gau_range[1]) - beta_gau_step = self.params.get('beta_gaussian_step', 0) - - beta_pla_range = self.params.get('beta_plateau', [1, 2]) - beta_pla = np.random.uniform(beta_pla_range[0], beta_pla_range[1]) - beta_pla_step = self.params.get('beta_plateau_step', 0) - - omega_range = self.params.get('omega', None) - omega_step = self.params.get('omega_step', 0) - if omega_range is None: # follow Real-ESRGAN settings if not specified - if kernel_size < 13: - omega_range = [np.pi / 3., np.pi] - else: - omega_range = [np.pi / 5., np.pi] - omega = np.random.uniform(omega_range[0], omega_range[1]) - - # determine blurring kernel - kernels = [] - for _ in range(0, num_kernels): - kernel = blur_kernels.random_mixed_kernels( - [kernel_type], - [1], - kernel_size, - [sigma_x, sigma_x], - [sigma_y, sigma_y], - [rotate_angle, rotate_angle], - [beta_gau, beta_gau], - [beta_pla, beta_pla], - [omega, omega], - None, - ) - kernels.append(kernel) - - # update kernel parameters - sigma_x += np.random.uniform(-sigma_x_step, sigma_x_step) - sigma_y += np.random.uniform(-sigma_y_step, sigma_y_step) - rotate_angle += np.random.uniform(-rotate_angle_step, - rotate_angle_step) - beta_gau += np.random.uniform(-beta_gau_step, beta_gau_step) - beta_pla += np.random.uniform(-beta_pla_step, beta_pla_step) - omega += np.random.uniform(-omega_step, omega_step) - - sigma_x = np.clip(sigma_x, sigma_x_range[0], sigma_x_range[1]) - sigma_y = np.clip(sigma_y, sigma_y_range[0], sigma_y_range[1]) - rotate_angle = np.clip(rotate_angle, rotate_angle_range[0], - rotate_angle_range[1]) - beta_gau = np.clip(beta_gau, beta_gau_range[0], beta_gau_range[1]) - beta_pla = np.clip(beta_pla, beta_pla_range[0], beta_pla_range[1]) - omega = np.clip(omega, omega_range[0], omega_range[1]) - - return kernels - - def _apply_random_blur(self, imgs): - is_single_image = False - if isinstance(imgs, np.ndarray): - is_single_image = True - imgs = [imgs] - - # get kernel and blur the input - kernels = self.get_kernel(num_kernels=len(imgs)) - imgs = [ - cv2.filter2D(img, -1, kernel) - for img, kernel in zip(imgs, kernels) - ] - - if is_single_image: - imgs = imgs[0] - - return imgs - - def __call__(self, results): - if np.random.uniform() > self.params.get('prob', 1): - return results - - for key in self.keys: - results[key] = self._apply_random_blur(results[key]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(params={self.params}, keys={self.keys})') - return repr_str - - -@PIPELINES.register_module() -class RandomResize: - """Randomly resize the input. - - Modified keys are the attributed specified in "keys". - - Args: - params (dict): A dictionary specifying the degradation settings. - keys (list[str]): A list specifying the keys whose values are - modified. - """ - - def __init__(self, params, keys): - self.keys = keys - self.params = params - - self.resize_dict = dict( - bilinear=cv2.INTER_LINEAR, - bicubic=cv2.INTER_CUBIC, - area=cv2.INTER_AREA, - lanczos=cv2.INTER_LANCZOS4) - - def _random_resize(self, imgs): - is_single_image = False - if isinstance(imgs, np.ndarray): - is_single_image = True - imgs = [imgs] - - h, w = imgs[0].shape[:2] - - resize_opt = self.params['resize_opt'] - resize_prob = self.params['resize_prob'] - resize_opt = np.random.choice(resize_opt, p=resize_prob).lower() - if resize_opt not in self.resize_dict: - raise NotImplementedError(f'resize_opt [{resize_opt}] is not ' - 'implemented') - resize_opt = self.resize_dict[resize_opt] - - resize_step = self.params.get('resize_step', 0) - - # determine the target size, if not provided - target_size = self.params.get('target_size', None) - if target_size is None: - resize_mode = np.random.choice(['up', 'down', 'keep'], - p=self.params['resize_mode_prob']) - resize_scale = self.params['resize_scale'] - if resize_mode == 'up': - scale_factor = np.random.uniform(1, resize_scale[1]) - elif resize_mode == 'down': - scale_factor = np.random.uniform(resize_scale[0], 1) - else: - scale_factor = 1 - - # determine output size - h_out, w_out = h * scale_factor, w * scale_factor - if self.params.get('is_size_even', False): - h_out, w_out = 2 * (h_out // 2), 2 * (w_out // 2) - target_size = (int(h_out), int(w_out)) - else: - resize_step = 0 - - # resize the input - if resize_step == 0: # same target_size for all input images - outputs = [ - cv2.resize(img, target_size[::-1], interpolation=resize_opt) - for img in imgs - ] - else: # different target_size for each input image - outputs = [] - for img in imgs: - img = cv2.resize( - img, target_size[::-1], interpolation=resize_opt) - outputs.append(img) - - # update scale - scale_factor += np.random.uniform(-resize_step, resize_step) - scale_factor = np.clip(scale_factor, resize_scale[0], - resize_scale[1]) - - # determine output size - h_out, w_out = h * scale_factor, w * scale_factor - if self.params.get('is_size_even', False): - h_out, w_out = 2 * (h_out // 2), 2 * (w_out // 2) - target_size = (int(h_out), int(w_out)) - - if is_single_image: - outputs = outputs[0] - - return outputs - - def __call__(self, results): - if np.random.uniform() > self.params.get('prob', 1): - return results - - for key in self.keys: - results[key] = self._random_resize(results[key]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(params={self.params}, keys={self.keys})') - return repr_str - - -@PIPELINES.register_module() -class RandomNoise: - """Apply random noise to the input. - - Currently support Gaussian noise and Poisson noise. - - Modified keys are the attributed specified in "keys". - - Args: - params (dict): A dictionary specifying the degradation settings. - keys (list[str]): A list specifying the keys whose values are - modified. - """ - - def __init__(self, params, keys): - self.keys = keys - self.params = params - - def _apply_gaussian_noise(self, imgs): - sigma_range = self.params['gaussian_sigma'] - sigma = np.random.uniform(sigma_range[0], sigma_range[1]) / 255. - - sigma_step = self.params.get('gaussian_sigma_step', 0) - - gray_noise_prob = self.params['gaussian_gray_noise_prob'] - is_gray_noise = np.random.uniform() < gray_noise_prob - - outputs = [] - for img in imgs: - noise = np.float32(np.random.randn(*(img.shape))) * sigma - if is_gray_noise: - noise = noise[:, :, :1] - outputs.append(img + noise) - - # update noise level - sigma += np.random.uniform(-sigma_step, sigma_step) / 255. - sigma = np.clip(sigma, sigma_range[0] / 255., - sigma_range[1] / 255.) - - return outputs - - def _apply_poisson_noise(self, imgs): - scale_range = self.params['poisson_scale'] - scale = np.random.uniform(scale_range[0], scale_range[1]) - - scale_step = self.params.get('poisson_scale_step', 0) - - gray_noise_prob = self.params['poisson_gray_noise_prob'] - is_gray_noise = np.random.uniform() < gray_noise_prob - - outputs = [] - for img in imgs: - noise = img.copy() - if is_gray_noise: - noise = cv2.cvtColor(noise[..., [2, 1, 0]], cv2.COLOR_BGR2GRAY) - noise = noise[..., np.newaxis] - noise = np.clip((noise * 255.0).round(), 0, 255) / 255. - unique_val = 2**np.ceil(np.log2(len(np.unique(noise)))) - noise = np.random.poisson(noise * unique_val) / unique_val - noise - - outputs.append(img + noise * scale) - - # update noise level - scale += np.random.uniform(-scale_step, scale_step) - scale = np.clip(scale, scale_range[0], scale_range[1]) - - return outputs - - def _apply_random_noise(self, imgs): - noise_type = np.random.choice( - self.params['noise_type'], p=self.params['noise_prob']) - - is_single_image = False - if isinstance(imgs, np.ndarray): - is_single_image = True - imgs = [imgs] - - if noise_type.lower() == 'gaussian': - imgs = self._apply_gaussian_noise(imgs) - elif noise_type.lower() == 'poisson': - imgs = self._apply_poisson_noise(imgs) - else: - raise NotImplementedError(f'"noise_type" [{noise_type}] is ' - 'not implemented.') - - if is_single_image: - imgs = imgs[0] - - return imgs - - def __call__(self, results): - if np.random.uniform() > self.params.get('prob', 1): - return results - - for key in self.keys: - results[key] = self._apply_random_noise(results[key]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(params={self.params}, keys={self.keys})') - return repr_str - - -@PIPELINES.register_module() -class RandomJPEGCompression: - """Apply random JPEG compression to the input. - - Modified keys are the attributed specified in "keys". - - Args: - params (dict): A dictionary specifying the degradation settings. - keys (list[str]): A list specifying the keys whose values are - modified. - """ - - def __init__(self, params, keys): - self.keys = keys - self.params = params - - def _apply_random_compression(self, imgs): - is_single_image = False - if isinstance(imgs, np.ndarray): - is_single_image = True - imgs = [imgs] - - # determine initial compression level and the step size - quality = self.params['quality'] - quality_step = self.params.get('quality_step', 0) - jpeg_param = round(np.random.uniform(quality[0], quality[1])) - - # apply jpeg compression - outputs = [] - for img in imgs: - encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), jpeg_param] - _, img_encoded = cv2.imencode('.jpg', img * 255., encode_param) - outputs.append(np.float32(cv2.imdecode(img_encoded, 1)) / 255.) - - # update compression level - jpeg_param += np.random.uniform(-quality_step, quality_step) - jpeg_param = round(np.clip(jpeg_param, quality[0], quality[1])) - - if is_single_image: - outputs = outputs[0] - - return outputs - - def __call__(self, results): - if np.random.uniform() > self.params.get('prob', 1): - return results - - for key in self.keys: - results[key] = self._apply_random_compression(results[key]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(params={self.params}, keys={self.keys})') - return repr_str - - -@PIPELINES.register_module() -class RandomVideoCompression: - """Apply random video compression to the input. - - Modified keys are the attributed specified in "keys". - - Args: - params (dict): A dictionary specifying the degradation settings. - keys (list[str]): A list specifying the keys whose values are - modified. - """ - - def __init__(self, params, keys): - assert has_av, 'Please install av to use video compression.' - - self.keys = keys - self.params = params - logging.getLogger('libav').setLevel(50) - - def _apply_random_compression(self, imgs): - codec = random.choices(self.params['codec'], - self.params['codec_prob'])[0] - bitrate = self.params['bitrate'] - bitrate = np.random.randint(bitrate[0], bitrate[1] + 1) - - buf = io.BytesIO() - with av.open(buf, 'w', 'mp4') as container: - stream = container.add_stream(codec, rate=1) - stream.height = imgs[0].shape[0] - stream.width = imgs[0].shape[1] - stream.pix_fmt = 'yuv420p' - stream.bit_rate = bitrate - - for img in imgs: - img = (255 * img).astype(np.uint8) - frame = av.VideoFrame.from_ndarray(img, format='rgb24') - frame.pict_type = 'NONE' - for packet in stream.encode(frame): - container.mux(packet) - - # Flush stream - for packet in stream.encode(): - container.mux(packet) - - outputs = [] - with av.open(buf, 'r', 'mp4') as container: - if container.streams.video: - for frame in container.decode(**{'video': 0}): - outputs.append( - frame.to_rgb().to_ndarray().astype(np.float32) / 255.) - - return outputs - - def __call__(self, results): - if np.random.uniform() > self.params.get('prob', 1): - return results - - for key in self.keys: - results[key] = self._apply_random_compression(results[key]) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(params={self.params}, keys={self.keys})') - return repr_str - - -allowed_degradations = { - 'RandomBlur': RandomBlur, - 'RandomResize': RandomResize, - 'RandomNoise': RandomNoise, - 'RandomJPEGCompression': RandomJPEGCompression, - 'RandomVideoCompression': RandomVideoCompression, -} - - -@PIPELINES.register_module() -class DegradationsWithShuffle: - """Apply random degradations to input, with degradations being shuffled. - - Degradation groups are supported. The order of degradations within the same - group is preserved. For example, if we have degradations = [a, b, [c, d]] - and shuffle_idx = None, then the possible orders are - - :: - - [a, b, [c, d]] - [a, [c, d], b] - [b, a, [c, d]] - [b, [c, d], a] - [[c, d], a, b] - [[c, d], b, a] - - Modified keys are the attributed specified in "keys". - - Args: - degradations (list[dict]): The list of degradations. - keys (list[str]): A list specifying the keys whose values are - modified. - shuffle_idx (list | None, optional): The degradations corresponding to - these indices are shuffled. If None, all degradations are shuffled. - """ - - def __init__(self, degradations, keys, shuffle_idx=None): - - self.keys = keys - - self.degradations = self._build_degradations(degradations) - - if shuffle_idx is None: - self.shuffle_idx = list(range(0, len(degradations))) - else: - self.shuffle_idx = shuffle_idx - - def _build_degradations(self, degradations): - for i, degradation in enumerate(degradations): - if isinstance(degradation, (list, tuple)): - degradations[i] = self._build_degradations(degradation) - else: - degradation_ = allowed_degradations[degradation['type']] - degradations[i] = degradation_(degradation['params'], - self.keys) - - return degradations - - def __call__(self, results): - # shuffle degradations - if len(self.shuffle_idx) > 0: - shuffle_list = [self.degradations[i] for i in self.shuffle_idx] - np.random.shuffle(shuffle_list) - for i, idx in enumerate(self.shuffle_idx): - self.degradations[idx] = shuffle_list[i] - - # apply degradations to input - for degradation in self.degradations: - if isinstance(degradation, (tuple, list)): - for subdegrdation in degradation: - results = subdegrdation(results) - else: - results = degradation(results) - - return results - - def __repr__(self): - repr_str = self.__class__.__name__ - repr_str += (f'(degradations={self.degradations}, ' - f'keys={self.keys}, ' - f'shuffle_idx={self.shuffle_idx})') - return repr_str diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/utils.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/utils.py deleted file mode 100755 index 42d470c34..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/pipelines/utils.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -import numpy as np -import torch -from mmcv.utils import print_log - -_integer_types = ( - np.byte, - np.ubyte, # 8 bits - np.short, - np.ushort, # 16 bits - np.intc, - np.uintc, # 16 or 32 or 64 bits - np.int_, - np.uint, # 32 or 64 bits - np.longlong, - np.ulonglong) # 64 bits - -_integer_ranges = { - t: (np.iinfo(t).min, np.iinfo(t).max) - for t in _integer_types -} - -dtype_range = { - np.bool_: (False, True), - np.bool8: (False, True), - np.float16: (-1, 1), - np.float32: (-1, 1), - np.float64: (-1, 1) -} -dtype_range.update(_integer_ranges) - - -def dtype_limits(image, clip_negative=False): - """Return intensity limits, i.e. (min, max) tuple, of the image's dtype. - - This function is adopted from skimage: - https://github.com/scikit-image/scikit-image/blob/ - 7e4840bd9439d1dfb6beaf549998452c99f97fdd/skimage/util/dtype.py#L35 - - Args: - image (ndarray): Input image. - clip_negative (bool, optional): If True, clip the negative range - (i.e. return 0 for min intensity) even if the image dtype allows - negative values. - - Returns - tuple: Lower and upper intensity limits. - """ - imin, imax = dtype_range[image.dtype.type] - if clip_negative: - imin = 0 - return imin, imax - - -def adjust_gamma(image, gamma=1, gain=1): - """Performs Gamma Correction on the input image. - - This function is adopted from skimage: - https://github.com/scikit-image/scikit-image/blob/ - 7e4840bd9439d1dfb6beaf549998452c99f97fdd/skimage/exposure/ - exposure.py#L439-L494 - - Also known as Power Law Transform. - This function transforms the input image pixelwise according to the - equation ``O = I**gamma`` after scaling each pixel to the range 0 to 1. - - Args: - image (ndarray): Input image. - gamma (float, optional): Non negative real number. Defaults to 1. - gain (float, optional): The constant multiplier. Defaults to 1. - - Returns: - ndarray: Gamma corrected output image. - """ - if np.any(image < 0): - raise ValueError('Image Correction methods work correctly only on ' - 'images with non-negative values. Use ' - 'skimage.exposure.rescale_intensity.') - - dtype = image.dtype.type - - if gamma < 0: - raise ValueError('Gamma should be a non-negative real number.') - - scale = float(dtype_limits(image, True)[1] - dtype_limits(image, True)[0]) - - out = ((image / scale)**gamma) * scale * gain - return out.astype(dtype) - - -def random_choose_unknown(unknown, crop_size): - """Randomly choose an unknown start (top-left) point for a given crop_size. - - Args: - unknown (np.ndarray): The binary unknown mask. - crop_size (tuple[int]): The given crop size. - - Returns: - tuple[int]: The top-left point of the chosen bbox. - """ - h, w = unknown.shape - crop_h, crop_w = crop_size - delta_h = center_h = crop_h // 2 - delta_w = center_w = crop_w // 2 - - # mask out the validate area for selecting the cropping center - mask = np.zeros_like(unknown) - mask[delta_h:h - delta_h, delta_w:w - delta_w] = 1 - if np.any(unknown & mask): - center_h_list, center_w_list = np.where(unknown & mask) - elif np.any(unknown): - center_h_list, center_w_list = np.where(unknown) - else: - print_log('No unknown pixels found!', level=logging.WARNING) - center_h_list = [center_h] - center_w_list = [center_w] - num_unknowns = len(center_h_list) - rand_ind = np.random.randint(num_unknowns) - center_h = center_h_list[rand_ind] - center_w = center_w_list[rand_ind] - - # make sure the top-left point is valid - top = np.clip(center_h - delta_h, 0, h - crop_h) - left = np.clip(center_w - delta_w, 0, w - crop_w) - - return top, left - - -def make_coord(shape, ranges=None, flatten=True): - """ Make coordinates at grid centers. - - Args: - shape (tuple): shape of image. - ranges (tuple): range of coordinate value. Default: None. - flatten (bool): flatten to (n, 2) or Not. Default: True. - - return: - coord (Tensor): coordinates. - """ - coord_seqs = [] - for i, n in enumerate(shape): - if ranges is None: - v0, v1 = -1, 1 - else: - v0, v1 = ranges[i] - r = (v1 - v0) / (2 * n) - seq = v0 + r + (2 * r) * torch.arange(n).float() - coord_seqs.append(seq) - coord = torch.stack(torch.meshgrid(*coord_seqs), dim=-1) - if flatten: - coord = coord.view(-1, coord.shape[-1]) - return coord diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/registry.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/registry.py deleted file mode 100755 index 984580ef2..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/registry.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import Registry - -DATASETS = Registry('dataset') -PIPELINES = Registry('pipeline') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/samplers/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/samplers/__init__.py deleted file mode 100755 index da09effaf..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/samplers/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .distributed_sampler import DistributedSampler - -__all__ = ['DistributedSampler'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/samplers/distributed_sampler.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/samplers/distributed_sampler.py deleted file mode 100755 index 7e800c813..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/samplers/distributed_sampler.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from __future__ import division -import math - -import torch -from torch.utils.data import DistributedSampler as _DistributedSampler - -from mmedit.core.utils import sync_random_seed - - -class DistributedSampler(_DistributedSampler): - """DistributedSampler inheriting from `torch.utils.data.DistributedSampler`. - - In pytorch of lower versions, there is no `shuffle` argument. This child - class will port one to DistributedSampler. - """ - - def __init__(self, - dataset, - num_replicas=None, - rank=None, - shuffle=True, - samples_per_gpu=1, - seed=0): - super().__init__(dataset, num_replicas=num_replicas, rank=rank) - self.shuffle = shuffle - self.samples_per_gpu = samples_per_gpu - # fix the bug of the official implementation - self.num_samples_per_replica = int( - math.ceil( - len(self.dataset) * 1.0 / self.num_replicas / samples_per_gpu)) - self.num_samples = self.num_samples_per_replica * self.samples_per_gpu - self.total_size = self.num_samples * self.num_replicas - - # In distributed sampling, different ranks should sample - # non-overlapped data in the dataset. Therefore, this function - # is used to make sure that each rank shuffles the data indices - # in the same order based on the same seed. Then different ranks - # could use different indices to select non-overlapped data from the - # same data list. - self.seed = sync_random_seed(seed) - - # to avoid padding bug when meeting too small dataset - if len(dataset) < self.num_replicas * samples_per_gpu: - raise ValueError( - 'You may use too small dataset and our distributed ' - 'sampler cannot pad your dataset correctly. We highly ' - 'recommend you to use fewer GPUs to finish your work') - - def __iter__(self): - # deterministically shuffle based on epoch - if self.shuffle: - g = torch.Generator() - # When :attr:`shuffle=True`, this ensures all replicas - # use a different random ordering for each epoch. - # Otherwise, the next iteration of this sampler will - # yield the same ordering. - g.manual_seed(self.epoch + self.seed) - indices = torch.randperm(len(self.dataset), generator=g).tolist() - else: - indices = torch.arange(len(self.dataset)).tolist() - - # add extra samples to make it evenly divisible - indices += indices[:(self.total_size - len(indices))] - assert len(indices) == self.total_size - - # subsample - indices = indices[self.rank:self.total_size:self.num_replicas] - assert len(indices) == self.num_samples - - return iter(indices) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/sr_folder_multiple_gt_dataset.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/sr_folder_multiple_gt_dataset.py deleted file mode 100755 index bc9630a7a..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/datasets/sr_folder_multiple_gt_dataset.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import glob -import os -import os.path as osp - -import mmcv - -from .base_sr_dataset import BaseSRDataset -from .registry import DATASETS - - -@DATASETS.register_module() -class SRFolderMultipleGTDataset(BaseSRDataset): - """General dataset for video super resolution, used for recurrent networks. - - The dataset loads several LQ (Low-Quality) frames and GT (Ground-Truth) - frames. Then it applies specified transforms and finally returns a dict - containing paired data and other information. - - This dataset takes an annotation file specifying the sequences used in - training or test. If no annotation file is provided, it assumes all video - sequences under the root directory is used for training or test. - - In the annotation file (.txt), each line contains: - - 1. folder name; - 2. number of frames in this sequence (in the same folder) - - Examples: - - :: - - calendar 41 - city 34 - foliage 49 - walk 47 - - Args: - lq_folder (str | :obj:`Path`): Path to a lq folder. - gt_folder (str | :obj:`Path`): Path to a gt folder. - pipeline (list[dict | callable]): A sequence of data transformations. - scale (int): Upsampling scale ratio. - ann_file (str): The path to the annotation file. If None, we assume - that all sequences in the folder is used. Default: None - num_input_frames (None | int): The number of frames per iteration. - If None, the whole clip is extracted. If it is a positive integer, - a sequence of 'num_input_frames' frames is extracted from the clip. - Note that non-positive integers are not accepted. Default: None. - test_mode (bool): Store `True` when building test dataset. - Default: `True`. - """ - - def __init__(self, - lq_folder, - gt_folder, - pipeline, - scale, - ann_file=None, - num_input_frames=None, - test_mode=True): - super().__init__(pipeline, scale, test_mode) - - self.lq_folder = str(lq_folder) - self.gt_folder = str(gt_folder) - self.ann_file = ann_file - - if num_input_frames is not None and num_input_frames <= 0: - raise ValueError('"num_input_frames" must be None or positive, ' - f'but got {num_input_frames}.') - self.num_input_frames = num_input_frames - - self.data_infos = self.load_annotations() - - def _load_annotations_from_file(self): - data_infos = [] - - ann_list = mmcv.list_from_file(self.ann_file) - for ann in ann_list: - key, sequence_length = ann.strip().split(' ') - if self.num_input_frames is None: - num_input_frames = sequence_length - else: - num_input_frames = self.num_input_frames - data_infos.append( - dict( - lq_path=self.lq_folder, - gt_path=self.gt_folder, - key=key, - num_input_frames=int(num_input_frames), - sequence_length=int(sequence_length))) - - return data_infos - - def load_annotations(self): - """Load annotations for the dataset. - - Returns: - list[dict]: Returned list of dicts for paired paths of LQ and GT. - """ - - if self.ann_file: - return self._load_annotations_from_file() - sequences = sorted(glob.glob(osp.join(self.lq_folder, '*'))) - data_infos = [] - for sequence in sequences: - sequence_length = len(glob.glob(osp.join(sequence, '*.png'))) - if self.num_input_frames is None: - num_input_frames = sequence_length - else: - num_input_frames = self.num_input_frames - data_infos.append( - dict( - lq_path=self.lq_folder, - gt_path=self.gt_folder, - key=sequence.replace(f'{self.lq_folder}{os.sep}', ''), - num_input_frames=num_input_frames, - sequence_length=sequence_length)) - - return data_infos diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/__init__.py deleted file mode 100755 index f49c97168..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .backbones import * # noqa: F401, F403 -from .base import BaseModel -from .builder import (build, build_backbone, build_component, build_loss, - build_model) -from .common import * # noqa: F401, F403 -from .losses import * # noqa: F401, F403 -from .registry import BACKBONES, COMPONENTS, LOSSES, MODELS -from .restorers import BasicRestorer -from .components import * diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/__init__.py deleted file mode 100755 index 7f6538695..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .sr_backbones import RealBasicVSRNet - diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/sr_backbones/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/sr_backbones/__init__.py deleted file mode 100755 index 07a19b607..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/sr_backbones/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .basicvsr_net import BasicVSRNet -from .real_basicvsr_net import RealBasicVSRNet diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/sr_backbones/basicvsr_net.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/sr_backbones/basicvsr_net.py deleted file mode 100755 index 1c98c4c73..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/sr_backbones/basicvsr_net.py +++ /dev/null @@ -1,420 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import ConvModule -from mmcv.runner import load_checkpoint - -from mmedit.models.common import (PixelShufflePack, ResidualBlockNoBN, - flow_warp, make_layer) -from mmedit.models.registry import BACKBONES -from mmedit.utils import get_root_logger - - -@BACKBONES.register_module() -class BasicVSRNet(nn.Module): - """BasicVSR network structure for video super-resolution. - - Support only x4 upsampling. - Paper: - BasicVSR: The Search for Essential Components in Video Super-Resolution - and Beyond, CVPR, 2021 - - Args: - mid_channels (int): Channel number of the intermediate features. - Default: 64. - num_blocks (int): Number of residual blocks in each propagation branch. - Default: 30. - spynet_pretrained (str): Pre-trained model path of SPyNet. - Default: None. - """ - - def __init__(self, mid_channels=64, num_blocks=30, spynet_pretrained=None): - - super().__init__() - - self.mid_channels = mid_channels - - # optical flow network for feature alignment - self.spynet = SPyNet(pretrained=spynet_pretrained) - - # propagation branches - self.backward_resblocks = ResidualBlocksWithInputConv( - mid_channels + 3, mid_channels, num_blocks) - self.forward_resblocks = ResidualBlocksWithInputConv( - mid_channels + 3, mid_channels, num_blocks) - - # upsample - self.fusion = nn.Conv2d( - mid_channels * 2, mid_channels, 1, 1, 0, bias=True) - self.upsample1 = PixelShufflePack( - mid_channels, mid_channels, 2, upsample_kernel=3) - self.upsample2 = PixelShufflePack( - mid_channels, 64, 2, upsample_kernel=3) - self.conv_hr = nn.Conv2d(64, 64, 3, 1, 1) - self.conv_last = nn.Conv2d(64, 3, 3, 1, 1) - self.img_upsample = nn.Upsample( - scale_factor=4, mode='bilinear', align_corners=False) - - # activation function - self.lrelu = nn.LeakyReLU(negative_slope=0.1, inplace=True) - - def check_if_mirror_extended(self, lrs): - """Check whether the input is a mirror-extended sequence. - - If mirror-extended, the i-th (i=0, ..., t-1) frame is equal to the - (t-1-i)-th frame. - - Args: - lrs (tensor): Input LR images with shape (n, t, c, h, w) - """ - - self.is_mirror_extended = False - if lrs.size(1) % 2 == 0: - lrs_1, lrs_2 = torch.chunk(lrs, 2, dim=1) - if torch.norm(lrs_1 - lrs_2.flip(1)) == 0: - self.is_mirror_extended = True - - def compute_flow(self, lrs): - """Compute optical flow using SPyNet for feature warping. - - Note that if the input is an mirror-extended sequence, 'flows_forward' - is not needed, since it is equal to 'flows_backward.flip(1)'. - - Args: - lrs (tensor): Input LR images with shape (n, t, c, h, w) - - Return: - tuple(Tensor): Optical flow. 'flows_forward' corresponds to the - flows used for forward-time propagation (current to previous). - 'flows_backward' corresponds to the flows used for - backward-time propagation (current to next). - """ - - n, t, c, h, w = lrs.size() - lrs_1 = lrs[:, :-1, :, :, :].reshape(-1, c, h, w) - lrs_2 = lrs[:, 1:, :, :, :].reshape(-1, c, h, w) - - flows_backward = self.spynet(lrs_1, lrs_2).view(n, t - 1, 2, h, w) - - if self.is_mirror_extended: # flows_forward = flows_backward.flip(1) - flows_forward = None - else: - flows_forward = self.spynet(lrs_2, lrs_1).view(n, t - 1, 2, h, w) - - return flows_forward, flows_backward - - def forward(self, lrs): - """Forward function for BasicVSR. - - Args: - lrs (Tensor): Input LR sequence with shape (n, t, c, h, w). - - Returns: - Tensor: Output HR sequence with shape (n, t, c, 4h, 4w). - """ - - n, t, c, h, w = lrs.size() - assert h >= 64 and w >= 64, ( - 'The height and width of inputs should be at least 64, ' - f'but got {h} and {w}.') - - # check whether the input is an extended sequence - self.check_if_mirror_extended(lrs) - - # compute optical flow - flows_forward, flows_backward = self.compute_flow(lrs) - - # backward-time propagation - outputs = [] - feat_prop = lrs.new_zeros(n, self.mid_channels, h, w) - for i in range(t - 1, -1, -1): - if i < t - 1: # no warping required for the last timestep - flow = flows_backward[:, i, :, :, :] - feat_prop = flow_warp(feat_prop, flow.permute(0, 2, 3, 1)) - - feat_prop = torch.cat([lrs[:, i, :, :, :], feat_prop], dim=1) - feat_prop = self.backward_resblocks(feat_prop) - - outputs.append(feat_prop) - outputs = outputs[::-1] - - # forward-time propagation and upsampling - feat_prop = torch.zeros_like(feat_prop) - for i in range(0, t): - lr_curr = lrs[:, i, :, :, :] - if i > 0: # no warping required for the first timestep - if flows_forward is not None: - flow = flows_forward[:, i - 1, :, :, :] - else: - flow = flows_backward[:, -i, :, :, :] - feat_prop = flow_warp(feat_prop, flow.permute(0, 2, 3, 1)) - - feat_prop = torch.cat([lr_curr, feat_prop], dim=1) - feat_prop = self.forward_resblocks(feat_prop) - - # upsampling given the backward and forward features - out = torch.cat([outputs[i], feat_prop], dim=1) - out = self.lrelu(self.fusion(out)) - out = self.lrelu(self.upsample1(out)) - out = self.lrelu(self.upsample2(out)) - out = self.lrelu(self.conv_hr(out)) - out = self.conv_last(out) - base = self.img_upsample(lr_curr) - out += base - outputs[i] = out - - return torch.stack(outputs, dim=1) - - def init_weights(self, pretrained=None, strict=True): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults: None. - strict (boo, optional): Whether strictly load the pretrained model. - Defaults to True. - """ - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=strict, logger=logger) - elif pretrained is not None: - raise TypeError(f'"pretrained" must be a str or None. ' - f'But received {type(pretrained)}.') - - -class ResidualBlocksWithInputConv(nn.Module): - """Residual blocks with a convolution in front. - - Args: - in_channels (int): Number of input channels of the first conv. - out_channels (int): Number of channels of the residual blocks. - Default: 64. - num_blocks (int): Number of residual blocks. Default: 30. - """ - - def __init__(self, in_channels, out_channels=64, num_blocks=30): - super().__init__() - - main = [] - - # a convolution used to match the channels of the residual blocks - main.append(nn.Conv2d(in_channels, out_channels, 3, 1, 1, bias=True)) - main.append(nn.LeakyReLU(negative_slope=0.1, inplace=True)) - - # residual blocks - main.append( - make_layer( - ResidualBlockNoBN, num_blocks, mid_channels=out_channels)) - - self.main = nn.Sequential(*main) - - def forward(self, feat): - """ - Forward function for ResidualBlocksWithInputConv. - - Args: - feat (Tensor): Input feature with shape (n, in_channels, h, w) - - Returns: - Tensor: Output feature with shape (n, out_channels, h, w) - """ - return self.main(feat) - - -class SPyNet(nn.Module): - """SPyNet network structure. - - The difference to the SPyNet in [tof.py] is that - 1. more SPyNetBasicModule is used in this version, and - 2. no batch normalization is used in this version. - - Paper: - Optical Flow Estimation using a Spatial Pyramid Network, CVPR, 2017 - - Args: - pretrained (str): path for pre-trained SPyNet. Default: None. - """ - - def __init__(self, pretrained): - super().__init__() - - self.basic_module = nn.ModuleList( - [SPyNetBasicModule() for _ in range(6)]) - - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=True, logger=logger) - elif pretrained is not None: - raise TypeError('[pretrained] should be str or None, ' - f'but got {type(pretrained)}.') - - self.register_buffer( - 'mean', - torch.Tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1)) - self.register_buffer( - 'std', - torch.Tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1)) - - def compute_flow(self, ref, supp): - """Compute flow from ref to supp. - - Note that in this function, the images are already resized to a - multiple of 32. - - Args: - ref (Tensor): Reference image with shape of (n, 3, h, w). - supp (Tensor): Supporting image with shape of (n, 3, h, w). - - Returns: - Tensor: Estimated optical flow: (n, 2, h, w). - """ - n, _, h, w = ref.size() - - # normalize the input images - ref = [(ref - self.mean) / self.std] - supp = [(supp - self.mean) / self.std] - - # generate downsampled frames - for level in range(5): - ref.append( - F.avg_pool2d( - input=ref[-1], - kernel_size=2, - stride=2, - count_include_pad=False)) - supp.append( - F.avg_pool2d( - input=supp[-1], - kernel_size=2, - stride=2, - count_include_pad=False)) - ref = ref[::-1] - supp = supp[::-1] - - # flow computation - flow = ref[0].new_zeros(n, 2, h // 32, w // 32) - for level in range(len(ref)): - if level == 0: - flow_up = flow - else: - flow_up = F.interpolate( - input=flow, - scale_factor=2, - mode='bilinear', - align_corners=True) * 2.0 - - # add the residue to the upsampled flow - flow = flow_up + self.basic_module[level]( - torch.cat([ - ref[level], - flow_warp( - supp[level], - flow_up.permute(0, 2, 3, 1), - padding_mode='border'), flow_up - ], 1)) - - return flow - - def forward(self, ref, supp): - """Forward function of SPyNet. - - This function computes the optical flow from ref to supp. - - Args: - ref (Tensor): Reference image with shape of (n, 3, h, w). - supp (Tensor): Supporting image with shape of (n, 3, h, w). - - Returns: - Tensor: Estimated optical flow: (n, 2, h, w). - """ - - # upsize to a multiple of 32 - h, w = ref.shape[2:4] - w_up = w if (w % 32) == 0 else 32 * (w // 32 + 1) - h_up = h if (h % 32) == 0 else 32 * (h // 32 + 1) - ref = F.interpolate( - input=ref, size=(h_up, w_up), mode='bilinear', align_corners=False) - supp = F.interpolate( - input=supp, - size=(h_up, w_up), - mode='bilinear', - align_corners=False) - - # compute flow, and resize back to the original resolution - flow = F.interpolate( - input=self.compute_flow(ref, supp), - size=(h, w), - mode='bilinear', - align_corners=False) - - # adjust the flow values - flow[:, 0, :, :] *= float(w) / float(w_up) - flow[:, 1, :, :] *= float(h) / float(h_up) - - return flow - - -class SPyNetBasicModule(nn.Module): - """Basic Module for SPyNet. - - Paper: - Optical Flow Estimation using a Spatial Pyramid Network, CVPR, 2017 - """ - - def __init__(self): - super().__init__() - - self.basic_module = nn.Sequential( - ConvModule( - in_channels=8, - out_channels=32, - kernel_size=7, - stride=1, - padding=3, - norm_cfg=None, - act_cfg=dict(type='ReLU')), - ConvModule( - in_channels=32, - out_channels=64, - kernel_size=7, - stride=1, - padding=3, - norm_cfg=None, - act_cfg=dict(type='ReLU')), - ConvModule( - in_channels=64, - out_channels=32, - kernel_size=7, - stride=1, - padding=3, - norm_cfg=None, - act_cfg=dict(type='ReLU')), - ConvModule( - in_channels=32, - out_channels=16, - kernel_size=7, - stride=1, - padding=3, - norm_cfg=None, - act_cfg=dict(type='ReLU')), - ConvModule( - in_channels=16, - out_channels=2, - kernel_size=7, - stride=1, - padding=3, - norm_cfg=None, - act_cfg=None)) - - def forward(self, tensor_input): - """ - Args: - tensor_input (Tensor): Input tensor with shape (b, 8, h, w). - 8 channels contain: - [reference image (3), neighbor image (3), initial flow (2)]. - - Returns: - Tensor: Refined flow with shape (b, 2, h, w) - """ - return self.basic_module(tensor_input) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/sr_backbones/real_basicvsr_net.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/sr_backbones/real_basicvsr_net.py deleted file mode 100755 index d87da027f..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/backbones/sr_backbones/real_basicvsr_net.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.runner import load_checkpoint - -from mmedit.models.backbones.sr_backbones.basicvsr_net import ( - BasicVSRNet, ResidualBlocksWithInputConv) -from mmedit.models.registry import BACKBONES -from mmedit.utils import get_root_logger - - -@BACKBONES.register_module() -class RealBasicVSRNet(nn.Module): - """RealBasicVSR network structure for real-world video super-resolution. - - Support only x4 upsampling. - Paper: - Investigating Tradeoffs in Real-World Video Super-Resolution, arXiv - - Args: - mid_channels (int, optional): Channel number of the intermediate - features. Default: 64. - num_propagation_blocks (int, optional): Number of residual blocks in - each propagation branch. Default: 20. - num_cleaning_blocks (int, optional): Number of residual blocks in the - image cleaning module. Default: 20. - dynamic_refine_thres (int, optional): Stop cleaning the images when - the residue is smaller than this value. Default: 255. - spynet_pretrained (str, optional): Pre-trained model path of SPyNet. - Default: None. - is_fix_cleaning (bool, optional): Whether to fix the weights of - the image cleaning module during training. Default: False. - is_sequential_cleaning (bool, optional): Whether to clean the images - sequentially. This is used to save GPU memory, but the speed is - slightly slower. Default: False. - """ - - def __init__(self, - mid_channels=64, - num_propagation_blocks=20, - num_cleaning_blocks=20, - dynamic_refine_thres=255, - spynet_pretrained=None, - is_fix_cleaning=False, - is_sequential_cleaning=False): - - super().__init__() - - self.dynamic_refine_thres = dynamic_refine_thres / 255. - self.is_sequential_cleaning = is_sequential_cleaning - - # image cleaning module - self.image_cleaning = nn.Sequential( - ResidualBlocksWithInputConv(3, mid_channels, num_cleaning_blocks), - nn.Conv2d(mid_channels, 3, 3, 1, 1, bias=True), - ) - - if is_fix_cleaning: # keep the weights of the cleaning module fixed - self.image_cleaning.requires_grad_(False) - - # BasicVSR - self.basicvsr = BasicVSRNet(mid_channels, num_propagation_blocks, - spynet_pretrained) - self.basicvsr.spynet.requires_grad_(False) - - def forward(self, lqs, return_lqs=False): - n, t, c, h, w = lqs.size() - - for _ in range(0, 3): # at most 3 cleaning, determined empirically - if self.is_sequential_cleaning: - residues = [] - for i in range(0, t): - residue_i = self.image_cleaning(lqs[:, i, :, :, :]) - lqs[:, i, :, :, :] += residue_i - residues.append(residue_i) - residues = torch.stack(residues, dim=1) - else: # time -> batch, then apply cleaning at once - lqs = lqs.view(-1, c, h, w) - residues = self.image_cleaning(lqs) - lqs = (lqs + residues).view(n, t, c, h, w) - - # determine whether to continue cleaning - if torch.mean(torch.abs(residues)) < self.dynamic_refine_thres: - break - - # Super-resolution (BasicVSR) - outputs = self.basicvsr(lqs) - - if return_lqs: - return outputs, lqs - else: - return outputs - - def init_weights(self, pretrained=None, strict=True): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults: None. - strict (boo, optional): Whether strictly load the pretrained model. - Defaults to True. - """ - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=strict, logger=logger) - elif pretrained is not None: - raise TypeError(f'"pretrained" must be a str or None. ' - f'But received {type(pretrained)}.') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/base.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/base.py deleted file mode 100755 index 02327e283..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/base.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from abc import ABCMeta, abstractmethod -from collections import OrderedDict - -import torch -import torch.nn as nn - - -class BaseModel(nn.Module, metaclass=ABCMeta): - """Base model. - - All models should subclass it. - All subclass should overwrite: - - ``init_weights``, supporting to initialize models. - - ``forward_train``, supporting to forward when training. - - ``forward_test``, supporting to forward when testing. - - ``train_step``, supporting to train one step when training. - """ - - @abstractmethod - def init_weights(self): - """Abstract method for initializing weight. - - All subclass should overwrite it. - """ - - @abstractmethod - def forward_train(self, imgs, labels): - """Abstract method for training forward. - - All subclass should overwrite it. - """ - - @abstractmethod - def forward_test(self, imgs): - """Abstract method for testing forward. - - All subclass should overwrite it. - """ - - def forward(self, imgs, labels, test_mode, **kwargs): - """Forward function for base model. - - Args: - imgs (Tensor): Input image(s). - labels (Tensor): Ground-truth label(s). - test_mode (bool): Whether in test mode. - kwargs (dict): Other arguments. - - Returns: - Tensor: Forward results. - """ - - if test_mode: - return self.forward_test(imgs, **kwargs) - - return self.forward_train(imgs, labels, **kwargs) - - @abstractmethod - def train_step(self, data_batch, optimizer): - """Abstract method for one training step. - - All subclass should overwrite it. - """ - - def val_step(self, data_batch, **kwargs): - """Abstract method for one validation step. - - All subclass should overwrite it. - """ - output = self.forward_test(**data_batch, **kwargs) - return output - - def parse_losses(self, losses): - """Parse losses dict for different loss variants. - - Args: - losses (dict): Loss dict. - - Returns: - loss (float): Sum of the total loss. - log_vars (dict): loss dict for different variants. - """ - log_vars = OrderedDict() - for loss_name, loss_value in losses.items(): - if isinstance(loss_value, torch.Tensor): - log_vars[loss_name] = loss_value.mean() - elif isinstance(loss_value, list): - log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) - else: - raise TypeError( - f'{loss_name} is not a tensor or list of tensors') - - loss = sum(_value for _key, _value in log_vars.items() - if 'loss' in _key) - - log_vars['loss'] = loss - for name in log_vars: - log_vars[name] = log_vars[name].item() - - return loss, log_vars diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/builder.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/builder.py deleted file mode 100755 index 8606225aa..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/builder.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv import build_from_cfg - -from .registry import BACKBONES, COMPONENTS, LOSSES, MODELS - - -def build(cfg, registry, default_args=None): - """Build module function. - - Args: - cfg (dict): Configuration for building modules. - registry (obj): ``registry`` object. - default_args (dict, optional): Default arguments. Defaults to None. - """ - if isinstance(cfg, list): - modules = [ - build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg - ] - return nn.Sequential(*modules) - - return build_from_cfg(cfg, registry, default_args) - - -def build_backbone(cfg): - """Build backbone. - - Args: - cfg (dict): Configuration for building backbone. - """ - return build(cfg, BACKBONES) - - -def build_component(cfg): - """Build component. - - Args: - cfg (dict): Configuration for building component. - """ - return build(cfg, COMPONENTS) - - -def build_loss(cfg): - """Build loss. - - Args: - cfg (dict): Configuration for building loss. - """ - return build(cfg, LOSSES) - - -def build_model(cfg, train_cfg=None, test_cfg=None): - """Build model. - - Args: - cfg (dict): Configuration for building model. - train_cfg (dict): Training configuration. Default: None. - test_cfg (dict): Testing configuration. Default: None. - """ - return build(cfg, MODELS, dict(train_cfg=train_cfg, test_cfg=test_cfg)) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/__init__.py deleted file mode 100755 index 7ebeb4fba..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .aspp import ASPP -from .contextual_attention import ContextualAttentionModule -from .conv import * # noqa: F401, F403 -from .downsample import pixel_unshuffle -from .ensemble import SpatialTemporalEnsemble -from .flow_warp import flow_warp -from .gated_conv_module import SimpleGatedConvModule -from .gca_module import GCAModule -from .generation_model_utils import (GANImageBuffer, ResidualBlockWithDropout, - UnetSkipConnectionBlock, - generation_init_weights) -from .img_normalize import ImgNormalize -from .linear_module import LinearModule -from .mask_conv_module import MaskConvModule -from .model_utils import (extract_around_bbox, extract_bbox_patch, scale_bbox, - set_requires_grad) -from .partial_conv import PartialConv2d -from .separable_conv_module import DepthwiseSeparableConvModule -from .sr_backbone_utils import (ResidualBlockNoBN, default_init_weights, - make_layer) -from .upsample import PixelShufflePack - -__all__ = [ - 'ASPP', 'PartialConv2d', 'PixelShufflePack', 'default_init_weights', - 'ResidualBlockNoBN', 'make_layer', 'MaskConvModule', 'extract_bbox_patch', - 'extract_around_bbox', 'set_requires_grad', 'scale_bbox', - 'DepthwiseSeparableConvModule', 'ContextualAttentionModule', 'GCAModule', - 'SimpleGatedConvModule', 'LinearModule', 'flow_warp', 'ImgNormalize', - 'generation_init_weights', 'GANImageBuffer', 'UnetSkipConnectionBlock', - 'ResidualBlockWithDropout', 'pixel_unshuffle', 'SpatialTemporalEnsemble' -] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/aspp.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/aspp.py deleted file mode 100755 index c1e58e858..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/aspp.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.cnn import ConvModule -from torch import nn -from torch.nn import functional as F - -from .separable_conv_module import DepthwiseSeparableConvModule - - -class ASPPPooling(nn.Sequential): - - def __init__(self, in_channels, out_channels, conv_cfg, norm_cfg, act_cfg): - super().__init__( - nn.AdaptiveAvgPool2d(1), - ConvModule( - in_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - def forward(self, x): - size = x.shape[-2:] - for mod in self: - x = mod(x) - return F.interpolate( - x, size=size, mode='bilinear', align_corners=False) - - -class ASPP(nn.Module): - """ASPP module from DeepLabV3. - - The code is adopted from - https://github.com/pytorch/vision/blob/master/torchvision/models/ - segmentation/deeplabv3.py - - For more information about the module: - `"Rethinking Atrous Convolution for Semantic Image Segmentation" - `_. - - Args: - in_channels (int): Input channels of the module. - out_channels (int): Output channels of the module. - mid_channels (int): Output channels of the intermediate ASPP conv - modules. - dilations (Sequence[int]): Dilation rate of three ASPP conv module. - Default: [12, 24, 36]. - conv_cfg (dict): Config dict for convolution layer. If "None", - nn.Conv2d will be applied. Default: None. - norm_cfg (dict): Config dict for normalization layer. - Default: dict(type='BN'). - act_cfg (dict): Config dict for activation layer. - Default: dict(type='ReLU'). - separable_conv (bool): Whether replace normal conv with depthwise - separable conv which is faster. Default: False. - """ - - def __init__(self, - in_channels, - out_channels=256, - mid_channels=256, - dilations=(12, 24, 36), - conv_cfg=None, - norm_cfg=dict(type='BN'), - act_cfg=dict(type='ReLU'), - separable_conv=False): - super().__init__() - - if separable_conv: - conv_module = DepthwiseSeparableConvModule - else: - conv_module = ConvModule - - modules = [] - modules.append( - ConvModule( - in_channels, - mid_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - for dilation in dilations: - modules.append( - conv_module( - in_channels, - mid_channels, - 3, - padding=dilation, - dilation=dilation, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg)) - - modules.append( - ASPPPooling(in_channels, mid_channels, conv_cfg, norm_cfg, - act_cfg)) - - self.convs = nn.ModuleList(modules) - - self.project = nn.Sequential( - ConvModule( - 5 * mid_channels, - out_channels, - 1, - conv_cfg=conv_cfg, - norm_cfg=norm_cfg, - act_cfg=act_cfg), nn.Dropout(0.5)) - - def forward(self, x): - """Forward function for ASPP module. - - Args: - x (Tensor): Input tensor with shape (N, C, H, W). - - Returns: - Tensor: Output tensor. - """ - res = [] - for conv in self.convs: - res.append(conv(x)) - res = torch.cat(res, dim=1) - return self.project(res) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/contextual_attention.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/contextual_attention.py deleted file mode 100755 index 7dcf4099e..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/contextual_attention.py +++ /dev/null @@ -1,379 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from functools import partial - -import torch -import torch.nn as nn -import torch.nn.functional as F - - -class ContextualAttentionModule(nn.Module): - """Contexture attention module. - - The details of this module can be found in: - Generative Image Inpainting with Contextual Attention - - Args: - unfold_raw_kernel_size (int): Kernel size used in unfolding raw - feature. Default: 4. - unfold_raw_stride (int): Stride used in unfolding raw feature. Default: - 2. - unfold_raw_padding (int): Padding used in unfolding raw feature. - Default: 1. - unfold_corr_kernel_size (int): Kernel size used in unfolding - context for computing correlation maps. Default: 3. - unfold_corr_stride (int): Stride used in unfolding context for - computing correlation maps. Default: 1. - unfold_corr_dilation (int): Dilation used in unfolding context for - computing correlation maps. Default: 1. - unfold_corr_padding (int): Padding used in unfolding context for - computing correlation maps. Default: 1. - scale (float): The resale factor used in resize input features. - Default: 0.5. - fuse_kernel_size (int): The kernel size used in fusion module. - Default: 3. - softmax_scale (float): The scale factor for softmax function. - Default: 10. - return_attention_score (bool): If True, the attention score will be - returned. Default: True. - """ - - def __init__(self, - unfold_raw_kernel_size=4, - unfold_raw_stride=2, - unfold_raw_padding=1, - unfold_corr_kernel_size=3, - unfold_corr_stride=1, - unfold_corr_dilation=1, - unfold_corr_padding=1, - scale=0.5, - fuse_kernel_size=3, - softmax_scale=10, - return_attention_score=True): - super().__init__() - self.unfold_raw_kernel_size = unfold_raw_kernel_size - self.unfold_raw_stride = unfold_raw_stride - self.unfold_raw_padding = unfold_raw_padding - self.unfold_corr_kernel_size = unfold_corr_kernel_size - self.unfold_corr_stride = unfold_corr_stride - self.unfold_corr_dilation = unfold_corr_dilation - self.unfold_corr_padding = unfold_corr_padding - self.scale = scale - self.fuse_kernel_size = fuse_kernel_size - self.with_fuse_correlation = fuse_kernel_size > 1 - self.softmax_scale = softmax_scale - self.return_attention_score = return_attention_score - - if self.with_fuse_correlation: - assert fuse_kernel_size % 2 == 1 - fuse_kernel = torch.eye(fuse_kernel_size).view( - 1, 1, fuse_kernel_size, fuse_kernel_size) - self.register_buffer('fuse_kernel', fuse_kernel) - padding = int((fuse_kernel_size - 1) // 2) - self.fuse_conv = partial(F.conv2d, padding=padding, stride=1) - self.softmax = nn.Softmax(dim=1) - - def forward(self, x, context, mask=None): - """Forward Function. - - Args: - x (torch.Tensor): Tensor with shape (n, c, h, w). - context (torch.Tensor): Tensor with shape (n, c, h, w). - mask (torch.Tensor): Tensor with shape (n, 1, h, w). Default: None. - - Returns: - tuple(torch.Tensor): Features after contextural attention. - """ - # raw features to be used in copy (deconv) - raw_context = context - raw_context_cols = self.im2col( - raw_context, - kernel_size=self.unfold_raw_kernel_size, - stride=self.unfold_raw_stride, - padding=self.unfold_raw_padding, - normalize=False, - return_cols=True) - # resize the feature to reduce computational cost - x = F.interpolate(x, scale_factor=self.scale) - context = F.interpolate(context, scale_factor=self.scale) - - context_cols = self.im2col( - context, - kernel_size=self.unfold_corr_kernel_size, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation, - normalize=True, - return_cols=True) - h_unfold, w_unfold = self.calculate_unfold_hw( - context.size()[-2:], - kernel_size=self.unfold_corr_kernel_size, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation, - ) - # reshape context_cols to - # (n*h_unfold*w_unfold, c, unfold_mks, unfold_mks) - # 'mks' is short for 'mask_kernel_size' - context_cols = context_cols.reshape(-1, *context_cols.shape[2:]) - - # the shape of correlation map should be: - # (n, h_unfold*w_unfold, h', w') - correlation_map = self.patch_correlation(x, context_cols) - # fuse correlation map to enlarge consistent attention region. - if self.with_fuse_correlation: - correlation_map = self.fuse_correlation_map( - correlation_map, h_unfold, w_unfold) - - correlation_map = self.mask_correlation_map(correlation_map, mask=mask) - - attention_score = self.softmax(correlation_map * self.softmax_scale) - - raw_context_filter = raw_context_cols.reshape( - -1, *raw_context_cols.shape[2:]) - output = self.patch_copy_deconv(attention_score, raw_context_filter) - # deconv will cause overlap and we need to remove the effects of that - overlap_factor = self.calculate_overlap_factor(attention_score) - output /= overlap_factor - - if self.return_attention_score: - n, _, h_s, w_s = attention_score.size() - attention_score = attention_score.view(n, h_unfold, w_unfold, h_s, - w_s) - return output, attention_score - - return output - - def patch_correlation(self, x, kernel): - """Calculate patch correlation. - - Args: - x (torch.Tensor): Input tensor. - kernel (torch.Tensor): Kernel tensor. - - Returns: - torch.Tensor: Tensor with shape of (n, l, h, w). - """ - n, _, h_in, w_in = x.size() - - patch_corr = F.conv2d( - x.view(1, -1, h_in, w_in), - kernel, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation, - groups=n) - h_out, w_out = patch_corr.size()[-2:] - return patch_corr.view(n, -1, h_out, w_out) - - def patch_copy_deconv(self, attention_score, context_filter): - """Copy patches using deconv. - - Args: - attention_score (torch.Tensor): Tensor with shape of (n, l , h, w). - context_filter (torch.Tensor): Filter kernel. - - Returns: - torch.Tensor: Tensor with shape of (n, c, h, w). - """ - n, _, h, w = attention_score.size() - attention_score = attention_score.view(1, -1, h, w) - output = F.conv_transpose2d( - attention_score, - context_filter, - stride=self.unfold_raw_stride, - padding=self.unfold_raw_padding, - groups=n) - h_out, w_out = output.size()[-2:] - return output.view(n, -1, h_out, w_out) - - def fuse_correlation_map(self, correlation_map, h_unfold, w_unfold): - """Fuse correlation map. - - This operation is to fuse correlation map for increasing large - consistent correlation regions. - - The mechanism behind this op is simple and easy to understand. A - standard 'Eye' matrix will be applied as a filter on the correlation - map in horizontal and vertical direction. - - The shape of input correlation map is (n, h_unfold*w_unfold, h, w). - When adopting fusing, we will apply convolutional filter in the - reshaped feature map with shape of (n, 1, h_unfold*w_fold, h*w). - - A simple specification for horizontal direction is shown below: - - .. code-block:: python - - (h, (h, (h, (h, - 0) 1) 2) 3) ... - (h, 0) - (h, 1) 1 - (h, 2) 1 - (h, 3) 1 - ... - - """ - # horizontal direction - n, _, h_map, w_map = correlation_map.size() - map_ = correlation_map.permute(0, 2, 3, 1) - map_ = map_.reshape(n, h_map * w_map, h_unfold * w_unfold, 1) - map_ = map_.permute(0, 3, 1, 2).contiguous() - map_ = self.fuse_conv(map_, self.fuse_kernel) - - correlation_map = map_.view(n, h_unfold, w_unfold, h_map, w_map) - - # vertical direction - map_ = correlation_map.permute(0, 2, 1, 4, - 3).reshape(n, 1, h_unfold * w_unfold, - h_map * w_map) - map_ = self.fuse_conv(map_, self.fuse_kernel) - - # Note that the dimension should be transposed since the convolution of - # eye matrix will put the normed scores into the last several dimension - correlation_map = map_.view(n, w_unfold, h_unfold, w_map, - h_map).permute(0, 4, 3, 2, 1) - correlation_map = correlation_map.reshape(n, -1, h_unfold, w_unfold) - - return correlation_map - - def calculate_unfold_hw(self, - input_size, - kernel_size=3, - stride=1, - dilation=1, - padding=0): - """Calculate (h, w) after unfolding - - The official implementation of `unfold` in pytorch will put the - dimension (h, w) into `L`. Thus, this function is just to calculate the - (h, w) according to the equation in: - https://pytorch.org/docs/stable/nn.html#torch.nn.Unfold - """ - h_in, w_in = input_size - - h_unfold = int((h_in + 2 * padding - dilation * - (kernel_size - 1) - 1) / stride + 1) - - w_unfold = int((w_in + 2 * padding - dilation * - (kernel_size - 1) - 1) / stride + 1) - return h_unfold, w_unfold - - def calculate_overlap_factor(self, attention_score): - """Calculate the overlap factor after applying deconv. - - Args: - attention_score (torch.Tensor): The attention score with shape of - (n, c, h, w). - - Returns: - torch.Tensor: The overlap factor will be returned. - """ - h, w = attention_score.shape[-2:] - kernel_size = self.unfold_raw_kernel_size - - ones_input = torch.ones(1, 1, h, w).to(attention_score) - ones_filter = torch.ones(1, 1, kernel_size, - kernel_size).to(attention_score) - overlap = F.conv_transpose2d( - ones_input, - ones_filter, - stride=self.unfold_raw_stride, - padding=self.unfold_raw_padding) - - # avoid division by zero - overlap[overlap == 0] = 1. - return overlap - - def mask_correlation_map(self, correlation_map, mask): - """Add mask weight for correlation map. - - Add a negative infinity number to the masked regions so that softmax - function will result in 'zero' in those regions. - - Args: - correlation_map (torch.Tensor): Correlation map with shape of - (n, h_unfold*w_unfold, h_map, w_map). - mask (torch.Tensor): Mask tensor with shape of (n, c, h, w). '1' - in the mask indicates masked region while '0' indicates valid - region. - - Returns: - torch.Tensor: Updated correlation map with mask. - """ - if mask is not None: - mask = F.interpolate(mask, scale_factor=self.scale) - # if any pixel is masked in patch, the patch is considered to be - # masked - mask_cols = self.im2col( - mask, - kernel_size=self.unfold_corr_kernel_size, - stride=self.unfold_corr_stride, - padding=self.unfold_corr_padding, - dilation=self.unfold_corr_dilation) - mask_cols = (mask_cols.sum(dim=1, keepdim=True) > 0).float() - mask_cols = mask_cols.permute(0, 2, - 1).reshape(mask.size(0), -1, 1, 1) - # add negative inf will bring zero in softmax - mask_cols[mask_cols == 1] = -float('inf') - correlation_map += mask_cols - return correlation_map - - def im2col(self, - img, - kernel_size, - stride=1, - padding=0, - dilation=1, - normalize=False, - return_cols=False): - """Reshape image-style feature to columns. - - This function is used for unfold feature maps to columns. The - details of this function can be found in: - https://pytorch.org/docs/1.1.0/nn.html?highlight=unfold#torch.nn.Unfold - - Args: - img (torch.Tensor): Features to be unfolded. The shape of this - feature should be (n, c, h, w). - kernel_size (int): In this function, we only support square kernel - with same height and width. - stride (int): Stride number in unfolding. Default: 1. - padding (int): Padding number in unfolding. Default: 0. - dilation (int): Dilation number in unfolding. Default: 1. - normalize (bool): If True, the unfolded feature will be normalized. - Default: False. - return_cols (bool): The official implementation in PyTorch of - unfolding will return features with shape of - (n, c*$prod{kernel_size}$, L). If True, the features will be - reshaped to (n, L, c, kernel_size, kernel_size). Otherwise, - the results will maintain the shape as the official - implementation. - - Returns: - torch.Tensor: Unfolded columns. If `return_cols` is True, the \ - shape of output tensor is \ - `(n, L, c, kernel_size, kernel_size)`. Otherwise, the shape \ - will be `(n, c*$prod{kernel_size}$, L)`. - """ - - # unfold img to columns with shape (n, c*kernel_size**2, num_cols) - img_unfold = F.unfold( - img, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation) - # normalize the feature map - if normalize: - norm = torch.sqrt((img_unfold**2).sum(dim=1, keepdim=True)) - eps = torch.tensor([1e-4]).to(img) - img_unfold = img_unfold / torch.max(norm, eps) - - if return_cols: - img_unfold_ = img_unfold.permute(0, 2, 1) - n, num_cols = img_unfold_.size()[:2] - img_cols = img_unfold_.view(n, num_cols, img.size(1), kernel_size, - kernel_size) - return img_cols - - return img_unfold diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/conv.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/conv.py deleted file mode 100755 index 8e03d0f7d..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/conv.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import CONV_LAYERS -from torch import nn - -CONV_LAYERS.register_module('Deconv', module=nn.ConvTranspose2d) -# TODO: octave conv diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/downsample.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/downsample.py deleted file mode 100755 index 7d51a5b55..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/downsample.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -def pixel_unshuffle(x, scale): - """Down-sample by pixel unshuffle. - - Args: - x (Tensor): Input tensor. - scale (int): Scale factor. - - Returns: - Tensor: Output tensor. - """ - - b, c, h, w = x.shape - if h % scale != 0 or w % scale != 0: - raise AssertionError( - f'Invalid scale ({scale}) of pixel unshuffle for tensor ' - f'with shape: {x.shape}') - h = int(h / scale) - w = int(w / scale) - x = x.view(b, c, h, scale, w, scale) - x = x.permute(0, 1, 3, 5, 2, 4) - return x.reshape(b, -1, h, w) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/ensemble.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/ensemble.py deleted file mode 100755 index 019b6e0f0..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/ensemble.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class SpatialTemporalEnsemble(nn.Module): - """ Apply spatial and temporal ensemble and compute outputs - - Args: - is_temporal_ensemble (bool, optional): Whether to apply ensemble - temporally. If True, the sequence will also be flipped temporally. - If the input is an image, this argument must be set to False. - Default: False. - - """ - - def __init__(self, is_temporal_ensemble=False): - - super().__init__() - - self.is_temporal_ensemble = is_temporal_ensemble - - def _transform(self, imgs, mode): - """Apply spatial transform (flip, rotate) to the images. - - Args: - imgs (torch.Tensor): The images to be transformed/ - mode (str): The mode of transform. Supported values are 'vertical', - 'horizontal', and 'transpose', corresponding to vertical flip, - horizontal flip, and rotation, respectively. - - Returns: - torch.Tensor: Output of the model with spatial ensemble applied. - - """ - - is_single_image = False - if imgs.ndim == 4: - if self.is_temporal_ensemble: - raise ValueError('"is_temporal_ensemble" must be False if ' - 'the input is an image.') - is_single_image = True - imgs = imgs.unsqueeze(1) - - if mode == 'vertical': - imgs = imgs.flip(4).clone() - elif mode == 'horizontal': - imgs = imgs.flip(3).clone() - elif mode == 'transpose': - imgs = imgs.permute(0, 1, 2, 4, 3).clone() - - if is_single_image: - imgs = imgs.squeeze(1) - - return imgs - - def spatial_ensemble(self, imgs, model): - """Apply spatial ensemble. - - Args: - imgs (torch.Tensor): The images to be processed by the model. Its - size should be either (n, t, c, h, w) or (n, c, h, w). - model (nn.Module): The model to process the images. - - Returns: - torch.Tensor: Output of the model with spatial ensemble applied. - - """ - - img_list = [imgs.cpu()] - for mode in ['vertical', 'horizontal', 'transpose']: - img_list.extend([self._transform(t, mode) for t in img_list]) - - output_list = [model(t.to(imgs.device)).cpu() for t in img_list] - for i in range(len(output_list)): - if i > 3: - output_list[i] = self._transform(output_list[i], 'transpose') - if i % 4 > 1: - output_list[i] = self._transform(output_list[i], 'horizontal') - if (i % 4) % 2 == 1: - output_list[i] = self._transform(output_list[i], 'vertical') - - outputs = torch.stack(output_list, dim=0) - outputs = outputs.mean(dim=0, keepdim=False) - - return outputs.to(imgs.device) - - def forward(self, imgs, model): - """Apply spatial and temporal ensemble. - - Args: - imgs (torch.Tensor): The images to be processed by the model. Its - size should be either (n, t, c, h, w) or (n, c, h, w). - model (nn.Module): The model to process the images. - - Returns: - torch.Tensor: Output of the model with spatial ensemble applied. - - """ - outputs = self.spatial_ensemble(imgs, model) - if self.is_temporal_ensemble: - outputs += self.spatial_ensemble(imgs.flip(1), model).flip(1) - outputs *= 0.5 - - return outputs diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/flow_warp.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/flow_warp.py deleted file mode 100755 index 7083230db..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/flow_warp.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn.functional as F - - -def flow_warp(x, - flow, - interpolation='bilinear', - padding_mode='zeros', - align_corners=True): - """Warp an image or a feature map with optical flow. - - Args: - x (Tensor): Tensor with size (n, c, h, w). - flow (Tensor): Tensor with size (n, h, w, 2). The last dimension is - a two-channel, denoting the width and height relative offsets. - Note that the values are not normalized to [-1, 1]. - interpolation (str): Interpolation mode: 'nearest' or 'bilinear'. - Default: 'bilinear'. - padding_mode (str): Padding mode: 'zeros' or 'border' or 'reflection'. - Default: 'zeros'. - align_corners (bool): Whether align corners. Default: True. - - Returns: - Tensor: Warped image or feature map. - """ - if x.size()[-2:] != flow.size()[1:3]: - raise ValueError(f'The spatial sizes of input ({x.size()[-2:]}) and ' - f'flow ({flow.size()[1:3]}) are not the same.') - _, _, h, w = x.size() - # create mesh grid - device = flow.device - grid_y, grid_x = torch.meshgrid( - torch.arange(0, h, device=device, dtype=x.dtype), - torch.arange(0, w, device=device, dtype=x.dtype)) - grid = torch.stack((grid_x, grid_y), 2) # h, w, 2 - grid.requires_grad = False - - grid_flow = grid + flow - # scale grid_flow to [-1,1] - grid_flow_x = 2.0 * grid_flow[:, :, :, 0] / max(w - 1, 1) - 1.0 - grid_flow_y = 2.0 * grid_flow[:, :, :, 1] / max(h - 1, 1) - 1.0 - grid_flow = torch.stack((grid_flow_x, grid_flow_y), dim=3) - output = F.grid_sample( - x, - grid_flow, - mode=interpolation, - padding_mode=padding_mode, - align_corners=align_corners) - return output diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/gated_conv_module.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/gated_conv_module.py deleted file mode 100755 index fed22c4f5..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/gated_conv_module.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import copy - -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, build_activation_layer - - -class SimpleGatedConvModule(nn.Module): - """Simple Gated Convolutional Module. - - This module is a simple gated convolutional module. The detailed formula - is: - - .. math:: - y = \\phi(conv1(x)) * \\sigma(conv2(x)), - - where `phi` is the feature activation function and `sigma` is the gate - activation function. In default, the gate activation function is sigmoid. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): The number of channels of the output feature. Note - that `out_channels` in the conv module is doubled since this module - contains two convolutions for feature and gate separately. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - feat_act_cfg (dict): Config dict for feature activation layer. - gate_act_cfg (dict): Config dict for gate activation layer. - kwargs (keyword arguments): Same as `ConvModule`. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - feat_act_cfg=dict(type='ELU'), - gate_act_cfg=dict(type='Sigmoid'), - **kwargs): - super().__init__() - # the activation function should specified outside conv module - kwargs_ = copy.deepcopy(kwargs) - kwargs_['act_cfg'] = None - self.with_feat_act = feat_act_cfg is not None - self.with_gate_act = gate_act_cfg is not None - - self.conv = ConvModule(in_channels, out_channels * 2, kernel_size, - **kwargs_) - - if self.with_feat_act: - self.feat_act = build_activation_layer(feat_act_cfg) - - if self.with_gate_act: - self.gate_act = build_activation_layer(gate_act_cfg) - - def forward(self, x): - """Forward Function. - - Args: - x (torch.Tensor): Input tensor with shape of (n, c, h, w). - - Returns: - torch.Tensor: Output tensor with shape of (n, c, h', w'). - """ - x = self.conv(x) - x, gate = torch.split(x, x.size(1) // 2, dim=1) - if self.with_feat_act: - x = self.feat_act(x) - if self.with_gate_act: - gate = self.gate_act(gate) - x = x * gate - - return x diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/gca_module.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/gca_module.py deleted file mode 100755 index 75ab64e5b..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/gca_module.py +++ /dev/null @@ -1,358 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, constant_init, xavier_init -from torch.nn import functional as F - - -class GCAModule(nn.Module): - """Guided Contextual Attention Module. - - From https://arxiv.org/pdf/2001.04069.pdf. - Based on https://github.com/nbei/Deep-Flow-Guided-Video-Inpainting. - This module use image feature map to augment the alpha feature map with - guided contextual attention score. - - Image feature and alpha feature are unfolded to small patches and later - used as conv kernel. Thus, we refer the unfolding size as kernel size. - Image feature patches have a default kernel size 3 while the kernel size of - alpha feature patches could be specified by `rate` (see `rate` below). The - image feature patches are used to convolve with the image feature itself - to calculate the contextual attention. Then the attention feature map is - convolved by alpha feature patches to obtain the attention alpha feature. - At last, the attention alpha feature is added to the input alpha feature. - - Args: - in_channels (int): Input channels of the guided contextual attention - module. - out_channels (int): Output channels of the guided contextual attention - module. - kernel_size (int): Kernel size of image feature patches. Default 3. - stride (int): Stride when unfolding the image feature. Default 1. - rate (int): The downsample rate of image feature map. The corresponding - kernel size and stride of alpha feature patches will be `rate x 2` - and `rate`. It could be regarded as the granularity of the gca - module. Default: 2. - pad_args (dict): Parameters of padding when convolve image feature with - image feature patches or alpha feature patches. Allowed keys are - `mode` and `value`. See torch.nn.functional.pad() for more - information. Default: dict(mode='reflect'). - interpolation (str): Interpolation method in upsampling and - downsampling. - penalty (float): Punishment hyperparameter to avoid a large correlation - between each unknown patch and itself. - eps (float): A small number to avoid dividing by 0 when calculating - the normed image feature patch. Default: 1e-4. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size=3, - stride=1, - rate=2, - pad_args=dict(mode='reflect'), - interpolation='nearest', - penalty=-1e4, - eps=1e-4): - super().__init__() - self.kernel_size = kernel_size - self.stride = stride - self.rate = rate - self.pad_args = pad_args - self.interpolation = interpolation - self.penalty = penalty - self.eps = eps - - # reduced the channels of input image feature. - self.guidance_conv = nn.Conv2d(in_channels, in_channels // 2, 1) - - # convolution after the attention alpha feature - self.out_conv = ConvModule( - out_channels, - out_channels, - 1, - norm_cfg=dict(type='BN'), - act_cfg=None) - - self.init_weights() - - def init_weights(self): - xavier_init(self.guidance_conv, distribution='uniform') - xavier_init(self.out_conv.conv, distribution='uniform') - constant_init(self.out_conv.norm, 1e-3) - - def forward(self, img_feat, alpha_feat, unknown=None, softmax_scale=1.): - """Forward function of GCAModule. - - Args: - img_feat (Tensor): Image feature map of shape - (N, ori_c, ori_h, ori_w). - alpha_feat (Tensor): Alpha feature map of shape - (N, alpha_c, ori_h, ori_w). - unknown (Tensor, optional): Unknown area map generated by trimap. - If specified, this tensor should have shape - (N, 1, ori_h, ori_w). - softmax_scale (float, optional): The softmax scale of the attention - if unknown area is not provided in forward. Default: 1. - - Returns: - Tensor: The augmented alpha feature. - """ - - if alpha_feat.shape[2:4] != img_feat.shape[2:4]: - raise ValueError( - 'image feature size does not align with alpha feature size: ' - f'image feature size {img_feat.shape[2:4]}, ' - f'alpha feature size {alpha_feat.shape[2:4]}') - - if unknown is not None and unknown.shape[2:4] != img_feat.shape[2:4]: - raise ValueError( - 'image feature size does not align with unknown mask size: ' - f'image feature size {img_feat.shape[2:4]}, ' - f'unknown mask size {unknown.shape[2:4]}') - - # preprocess image feature - img_feat = self.guidance_conv(img_feat) - img_feat = F.interpolate( - img_feat, scale_factor=1 / self.rate, mode=self.interpolation) - - # preprocess unknown mask - unknown, softmax_scale = self.process_unknown_mask( - unknown, img_feat, softmax_scale) - - img_ps, alpha_ps, unknown_ps = self.extract_feature_maps_patches( - img_feat, alpha_feat, unknown) - - # create self correlation mask with shape: - # (N, img_h*img_w, img_h, img_w) - self_mask = self.get_self_correlation_mask(img_feat) - - # split tensors by batch dimension; tuple is returned - img_groups = torch.split(img_feat, 1, dim=0) - img_ps_groups = torch.split(img_ps, 1, dim=0) - alpha_ps_groups = torch.split(alpha_ps, 1, dim=0) - unknown_ps_groups = torch.split(unknown_ps, 1, dim=0) - scale_groups = torch.split(softmax_scale, 1, dim=0) - groups = (img_groups, img_ps_groups, alpha_ps_groups, - unknown_ps_groups, scale_groups) - - out = [] - # i is the virtual index of the sample in the current batch - for img_i, img_ps_i, alpha_ps_i, unknown_ps_i, scale_i in zip(*groups): - similarity_map = self.compute_similarity_map(img_i, img_ps_i) - - gca_score = self.compute_guided_attention_score( - similarity_map, unknown_ps_i, scale_i, self_mask) - - out_i = self.propagate_alpha_feature(gca_score, alpha_ps_i) - - out.append(out_i) - - out = torch.cat(out, dim=0) - out.reshape_as(alpha_feat) - - out = self.out_conv(out) + alpha_feat - return out - - def extract_feature_maps_patches(self, img_feat, alpha_feat, unknown): - """Extract image feature, alpha feature unknown patches. - - Args: - img_feat (Tensor): Image feature map of shape - (N, img_c, img_h, img_w). - alpha_feat (Tensor): Alpha feature map of shape - (N, alpha_c, ori_h, ori_w). - unknown (Tensor, optional): Unknown area map generated by trimap of - shape (N, 1, img_h, img_w). - - Returns: - tuple: 3-tuple of - - ``Tensor``: Image feature patches of shape \ - (N, img_h*img_w, img_c, img_ks, img_ks). - - ``Tensor``: Guided contextual attention alpha feature map. \ - (N, img_h*img_w, alpha_c, alpha_ks, alpha_ks). - - ``Tensor``: Unknown mask of shape (N, img_h*img_w, 1, 1). - """ - # extract image feature patches with shape: - # (N, img_h*img_w, img_c, img_ks, img_ks) - img_ks = self.kernel_size - img_ps = self.extract_patches(img_feat, img_ks, self.stride) - - # extract alpha feature patches with shape: - # (N, img_h*img_w, alpha_c, alpha_ks, alpha_ks) - alpha_ps = self.extract_patches(alpha_feat, self.rate * 2, self.rate) - - # extract unknown mask patches with shape: (N, img_h*img_w, 1, 1) - unknown_ps = self.extract_patches(unknown, img_ks, self.stride) - unknown_ps = unknown_ps.squeeze(dim=2) # squeeze channel dimension - unknown_ps = unknown_ps.mean(dim=[2, 3], keepdim=True) - - return img_ps, alpha_ps, unknown_ps - - def compute_similarity_map(self, img_feat, img_ps): - """Compute similarity between image feature patches. - - Args: - img_feat (Tensor): Image feature map of shape - (1, img_c, img_h, img_w). - img_ps (Tensor): Image feature patches tensor of shape - (1, img_h*img_w, img_c, img_ks, img_ks). - - Returns: - Tensor: Similarity map between image feature patches with shape \ - (1, img_h*img_w, img_h, img_w). - """ - img_ps = img_ps[0] # squeeze dim 0 - # convolve the feature to get correlation (similarity) map - escape_NaN = torch.FloatTensor([self.eps]).to(img_feat) - img_ps_normed = img_ps / torch.max(self.l2_norm(img_ps), escape_NaN) - img_feat = self.pad(img_feat, self.kernel_size, self.stride) - similarity_map = F.conv2d(img_feat, img_ps_normed) - - return similarity_map - - def compute_guided_attention_score(self, similarity_map, unknown_ps, scale, - self_mask): - """Compute guided attention score. - - Args: - similarity_map (Tensor): Similarity map of image feature with shape - (1, img_h*img_w, img_h, img_w). - unknown_ps (Tensor): Unknown area patches tensor of shape - (1, img_h*img_w, 1, 1). - scale (Tensor): Softmax scale of known and unknown area: - [unknown_scale, known_scale]. - self_mask (Tensor): Self correlation mask of shape - (1, img_h*img_w, img_h, img_w). At (1, i*i, i, i) mask value - equals -1e4 for i in [1, img_h*img_w] and other area is all - zero. - - Returns: - Tensor: Similarity map between image feature patches with shape \ - (1, img_h*img_w, img_h, img_w). - """ - # scale the correlation with predicted scale factor for known and - # unknown area - unknown_scale, known_scale = scale[0] - out = similarity_map * ( - unknown_scale * unknown_ps.gt(0.).float() + - known_scale * unknown_ps.le(0.).float()) - # mask itself, self-mask only applied to unknown area - out = out + self_mask * unknown_ps - gca_score = F.softmax(out, dim=1) - - return gca_score - - def propagate_alpha_feature(self, gca_score, alpha_ps): - """Propagate alpha feature based on guided attention score. - - Args: - gca_score (Tensor): Guided attention score map of shape - (1, img_h*img_w, img_h, img_w). - alpha_ps (Tensor): Alpha feature patches tensor of shape - (1, img_h*img_w, alpha_c, alpha_ks, alpha_ks). - - Returns: - Tensor: Propagated alpha feature map of shape \ - (1, alpha_c, alpha_h, alpha_w). - """ - alpha_ps = alpha_ps[0] # squeeze dim 0 - if self.rate == 1: - gca_score = self.pad(gca_score, kernel_size=2, stride=1) - alpha_ps = alpha_ps.permute(1, 0, 2, 3) - out = F.conv2d(gca_score, alpha_ps) / 4. - else: - out = F.conv_transpose2d( - gca_score, alpha_ps, stride=self.rate, padding=1) / 4. - - return out - - def process_unknown_mask(self, unknown, img_feat, softmax_scale): - """Process unknown mask. - - Args: - unknown (Tensor, optional): Unknown area map generated by trimap of - shape (N, 1, ori_h, ori_w) - img_feat (Tensor): The interpolated image feature map of shape - (N, img_c, img_h, img_w). - softmax_scale (float, optional): The softmax scale of the attention - if unknown area is not provided in forward. Default: 1. - - Returns: - tuple: 2-tuple of - - ``Tensor``: Interpolated unknown area map of shape \ - (N, img_h*img_w, img_h, img_w). - - ``Tensor``: Softmax scale tensor of known and unknown area of \ - shape (N, 2). - """ - n, _, h, w = img_feat.shape - - if unknown is not None: - unknown = unknown.clone() - unknown = F.interpolate( - unknown, scale_factor=1 / self.rate, mode=self.interpolation) - unknown_mean = unknown.mean(dim=[2, 3]) - known_mean = 1 - unknown_mean - unknown_scale = torch.clamp( - torch.sqrt(unknown_mean / known_mean), 0.1, 10).to(img_feat) - known_scale = torch.clamp( - torch.sqrt(known_mean / unknown_mean), 0.1, 10).to(img_feat) - softmax_scale = torch.cat([unknown_scale, known_scale], dim=1) - else: - unknown = torch.ones((n, 1, h, w)).to(img_feat) - softmax_scale = torch.FloatTensor( - [softmax_scale, - softmax_scale]).view(1, 2).repeat(n, 1).to(img_feat) - - return unknown, softmax_scale - - def extract_patches(self, x, kernel_size, stride): - """Extract feature patches. - - The feature map will be padded automatically to make sure the number of - patches is equal to `(H / stride) * (W / stride)`. - - Args: - x (Tensor): Feature map of shape (N, C, H, W). - kernel_size (int): Size of each patches. - stride (int): Stride between patches. - - Returns: - Tensor: Extracted patches of shape \ - (N, (H / stride) * (W / stride) , C, kernel_size, kernel_size). - """ - n, c, _, _ = x.shape - x = self.pad(x, kernel_size, stride) - x = F.unfold(x, (kernel_size, kernel_size), stride=(stride, stride)) - x = x.permute(0, 2, 1) - x = x.reshape(n, -1, c, kernel_size, kernel_size) - return x - - def pad(self, x, kernel_size, stride): - left = (kernel_size - stride + 1) // 2 - right = (kernel_size - stride) // 2 - pad = (left, right, left, right) - return F.pad(x, pad, **self.pad_args) - - def get_self_correlation_mask(self, img_feat): - _, _, h, w = img_feat.shape - # As ONNX does not support dynamic num_classes, we have to convert it - # into an integer - self_mask = F.one_hot( - torch.arange(h * w).view(h, w), num_classes=int(h * w)) - self_mask = self_mask.permute(2, 0, 1).view(1, h * w, h, w) - # use large negative value to mask out self-correlation before softmax - self_mask = self_mask * self.penalty - return self_mask.to(img_feat) - - @staticmethod - def l2_norm(x): - x = x**2 - x = x.sum(dim=[1, 2, 3], keepdim=True) - return torch.sqrt(x) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/generation_model_utils.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/generation_model_utils.py deleted file mode 100755 index 21d45fbe3..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/generation_model_utils.py +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.nn as nn -from mmcv.cnn import ConvModule, kaiming_init, normal_init, xavier_init -from torch.nn import init - - -def generation_init_weights(module, init_type='normal', init_gain=0.02): - """Default initialization of network weights for image generation. - - By default, we use normal init, but xavier and kaiming might work - better for some applications. - - Args: - module (nn.Module): Module to be initialized. - init_type (str): The name of an initialization method: - normal | xavier | kaiming | orthogonal. - init_gain (float): Scaling factor for normal, xavier and - orthogonal. - """ - - def init_func(m): - """Initialization function. - - Args: - m (nn.Module): Module to be initialized. - """ - classname = m.__class__.__name__ - if hasattr(m, 'weight') and (classname.find('Conv') != -1 - or classname.find('Linear') != -1): - if init_type == 'normal': - normal_init(m, 0.0, init_gain) - elif init_type == 'xavier': - xavier_init(m, gain=init_gain, distribution='normal') - elif init_type == 'kaiming': - kaiming_init( - m, - a=0, - mode='fan_in', - nonlinearity='leaky_relu', - distribution='normal') - elif init_type == 'orthogonal': - init.orthogonal_(m.weight, gain=init_gain) - init.constant_(m.bias.data, 0.0) - else: - raise NotImplementedError( - f"Initialization method '{init_type}' is not implemented") - elif classname.find('BatchNorm2d') != -1: - # BatchNorm Layer's weight is not a matrix; - # only normal distribution applies. - normal_init(m, 1.0, init_gain) - - module.apply(init_func) - - -class GANImageBuffer: - """This class implements an image buffer that stores previously - generated images. - - This buffer allows us to update the discriminator using a history of - generated images rather than the ones produced by the latest generator - to reduce model oscillation. - - Args: - buffer_size (int): The size of image buffer. If buffer_size = 0, - no buffer will be created. - buffer_ratio (float): The chance / possibility to use the images - previously stored in the buffer. - """ - - def __init__(self, buffer_size, buffer_ratio=0.5): - self.buffer_size = buffer_size - # create an empty buffer - if self.buffer_size > 0: - self.img_num = 0 - self.image_buffer = [] - self.buffer_ratio = buffer_ratio - - def query(self, images): - """Query current image batch using a history of generated images. - - Args: - images (Tensor): Current image batch without history information. - """ - if self.buffer_size == 0: # if the buffer size is 0, do nothing - return images - return_images = [] - for image in images: - image = torch.unsqueeze(image.data, 0) - # if the buffer is not full, keep inserting current images - if self.img_num < self.buffer_size: - self.img_num = self.img_num + 1 - self.image_buffer.append(image) - return_images.append(image) - else: - use_buffer = np.random.random() < self.buffer_ratio - # by self.buffer_ratio, the buffer will return a previously - # stored image, and insert the current image into the buffer - if use_buffer: - random_id = np.random.randint(0, self.buffer_size) - image_tmp = self.image_buffer[random_id].clone() - self.image_buffer[random_id] = image - return_images.append(image_tmp) - # by (1 - self.buffer_ratio), the buffer will return the - # current image - else: - return_images.append(image) - # collect all the images and return - return_images = torch.cat(return_images, 0) - return return_images - - -class UnetSkipConnectionBlock(nn.Module): - """Construct a Unet submodule with skip connections, with the following - structure: downsampling - `submodule` - upsampling. - - Args: - outer_channels (int): Number of channels at the outer conv layer. - inner_channels (int): Number of channels at the inner conv layer. - in_channels (int): Number of channels in input images/features. If is - None, equals to `outer_channels`. Default: None. - submodule (UnetSkipConnectionBlock): Previously constructed submodule. - Default: None. - is_outermost (bool): Whether this module is the outermost module. - Default: False. - is_innermost (bool): Whether this module is the innermost module. - Default: False. - norm_cfg (dict): Config dict to build norm layer. Default: - `dict(type='BN')`. - use_dropout (bool): Whether to use dropout layers. Default: False. - """ - - def __init__(self, - outer_channels, - inner_channels, - in_channels=None, - submodule=None, - is_outermost=False, - is_innermost=False, - norm_cfg=dict(type='BN'), - use_dropout=False): - super().__init__() - # cannot be both outermost and innermost - assert not (is_outermost and is_innermost), ( - "'is_outermost' and 'is_innermost' cannot be True" - 'at the same time.') - self.is_outermost = is_outermost - assert isinstance(norm_cfg, dict), ("'norm_cfg' should be dict, but" - f'got {type(norm_cfg)}') - assert 'type' in norm_cfg, "'norm_cfg' must have key 'type'" - # We use norm layers in the unet skip connection block. - # Only for IN, use bias since it does not have affine parameters. - use_bias = norm_cfg['type'] == 'IN' - - kernel_size = 4 - stride = 2 - padding = 1 - if in_channels is None: - in_channels = outer_channels - down_conv_cfg = dict(type='Conv2d') - down_norm_cfg = norm_cfg - down_act_cfg = dict(type='LeakyReLU', negative_slope=0.2) - up_conv_cfg = dict(type='Deconv') - up_norm_cfg = norm_cfg - up_act_cfg = dict(type='ReLU') - up_in_channels = inner_channels * 2 - up_bias = use_bias - middle = [submodule] - upper = [] - - if is_outermost: - down_act_cfg = None - down_norm_cfg = None - up_bias = True - up_norm_cfg = None - upper = [nn.Tanh()] - elif is_innermost: - down_norm_cfg = None - up_in_channels = inner_channels - middle = [] - else: - upper = [nn.Dropout(0.5)] if use_dropout else [] - - down = [ - ConvModule( - in_channels=in_channels, - out_channels=inner_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - bias=use_bias, - conv_cfg=down_conv_cfg, - norm_cfg=down_norm_cfg, - act_cfg=down_act_cfg, - order=('act', 'conv', 'norm')) - ] - up = [ - ConvModule( - in_channels=up_in_channels, - out_channels=outer_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - bias=up_bias, - conv_cfg=up_conv_cfg, - norm_cfg=up_norm_cfg, - act_cfg=up_act_cfg, - order=('act', 'conv', 'norm')) - ] - - model = down + middle + up + upper - - self.model = nn.Sequential(*model) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - if self.is_outermost: - return self.model(x) - - # add skip connections - return torch.cat([x, self.model(x)], 1) - - -class ResidualBlockWithDropout(nn.Module): - """Define a Residual Block with dropout layers. - - Ref: - Deep Residual Learning for Image Recognition - - A residual block is a conv block with skip connections. A dropout layer is - added between two common conv modules. - - Args: - channels (int): Number of channels in the conv layer. - padding_mode (str): The name of padding layer: - 'reflect' | 'replicate' | 'zeros'. - norm_cfg (dict): Config dict to build norm layer. Default: - `dict(type='IN')`. - use_dropout (bool): Whether to use dropout layers. Default: True. - """ - - def __init__(self, - channels, - padding_mode, - norm_cfg=dict(type='BN'), - use_dropout=True): - super().__init__() - assert isinstance(norm_cfg, dict), ("'norm_cfg' should be dict, but" - f'got {type(norm_cfg)}') - assert 'type' in norm_cfg, "'norm_cfg' must have key 'type'" - # We use norm layers in the residual block with dropout layers. - # Only for IN, use bias since it does not have affine parameters. - use_bias = norm_cfg['type'] == 'IN' - - block = [ - ConvModule( - in_channels=channels, - out_channels=channels, - kernel_size=3, - padding=1, - bias=use_bias, - norm_cfg=norm_cfg, - padding_mode=padding_mode) - ] - - if use_dropout: - block += [nn.Dropout(0.5)] - - block += [ - ConvModule( - in_channels=channels, - out_channels=channels, - kernel_size=3, - padding=1, - bias=use_bias, - norm_cfg=norm_cfg, - act_cfg=None, - padding_mode=padding_mode) - ] - - self.block = nn.Sequential(*block) - - def forward(self, x): - """Forward function. Add skip connections without final ReLU. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - out = x + self.block(x) - return out diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/img_normalize.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/img_normalize.py deleted file mode 100755 index 1bd8f76aa..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/img_normalize.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn - - -class ImgNormalize(nn.Conv2d): - """Normalize images with the given mean and std value. - - Based on Conv2d layer, can work in GPU. - - Args: - pixel_range (float): Pixel range of feature. - img_mean (Tuple[float]): Image mean of each channel. - img_std (Tuple[float]): Image std of each channel. - sign (int): Sign of bias. Default -1. - """ - - def __init__(self, pixel_range, img_mean, img_std, sign=-1): - - assert len(img_mean) == len(img_std) - num_channels = len(img_mean) - super().__init__(num_channels, num_channels, kernel_size=1) - - std = torch.Tensor(img_std) - self.weight.data = torch.eye(num_channels).view( - num_channels, num_channels, 1, 1) - self.weight.data.div_(std.view(num_channels, 1, 1, 1)) - self.bias.data = sign * pixel_range * torch.Tensor(img_mean) - self.bias.data.div_(std) - - self.weight.requires_grad = False - self.bias.requires_grad = False diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/linear_module.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/linear_module.py deleted file mode 100755 index 5101ad164..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/linear_module.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import build_activation_layer, kaiming_init - - -class LinearModule(nn.Module): - """A linear block that contains linear/norm/activation layers. - - For low level vision, we add spectral norm and padding layer. - - Args: - in_features (int): Same as nn.Linear. - out_features (int): Same as nn.Linear. - bias (bool): Same as nn.Linear. - act_cfg (dict): Config dict for activation layer, "relu" by default. - inplace (bool): Whether to use inplace mode for activation. - with_spectral_norm (bool): Whether use spectral norm in linear module. - order (tuple[str]): The order of linear/activation layers. It is a - sequence of "linear", "norm" and "act". Examples are - ("linear", "act") and ("act", "linear"). - """ - - def __init__(self, - in_features, - out_features, - bias=True, - act_cfg=dict(type='ReLU'), - inplace=True, - with_spectral_norm=False, - order=('linear', 'act')): - super().__init__() - assert act_cfg is None or isinstance(act_cfg, dict) - self.act_cfg = act_cfg - self.inplace = inplace - self.with_spectral_norm = with_spectral_norm - self.order = order - assert isinstance(self.order, tuple) and len(self.order) == 2 - assert set(order) == set(['linear', 'act']) - - self.with_activation = act_cfg is not None - self.with_bias = bias - - # build linear layer - self.linear = nn.Linear(in_features, out_features, bias=bias) - # export the attributes of self.linear to a higher level for - # convenience - self.in_features = self.linear.in_features - self.out_features = self.linear.out_features - - if self.with_spectral_norm: - self.linear = nn.utils.spectral_norm(self.linear) - - # build activation layer - if self.with_activation: - act_cfg_ = act_cfg.copy() - act_cfg_.setdefault('inplace', inplace) - self.activate = build_activation_layer(act_cfg_) - - # Use msra init by default - self.init_weights() - - def init_weights(self): - if self.with_activation and self.act_cfg['type'] == 'LeakyReLU': - nonlinearity = 'leaky_relu' - a = self.act_cfg.get('negative_slope', 0.01) - else: - nonlinearity = 'relu' - a = 0 - - kaiming_init(self.linear, a=a, nonlinearity=nonlinearity) - - def forward(self, x, activate=True): - """Forward Function. - - Args: - x (torch.Tensor): Input tensor with shape of :math:`(n, *, c)`. - Same as ``torch.nn.Linear``. - activate (bool, optional): Whether to use activation layer. - Defaults to True. - - Returns: - torch.Tensor: Same as ``torch.nn.Linear``. - """ - for layer in self.order: - if layer == 'linear': - x = self.linear(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - return x diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/mask_conv_module.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/mask_conv_module.py deleted file mode 100755 index e70868686..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/mask_conv_module.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import ConvModule - - -class MaskConvModule(ConvModule): - """Mask convolution module. - - This is a simple wrapper for mask convolution like: 'partial conv'. - Convolutions in this module always need a mask as extra input. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - stride (int or tuple[int]): Same as nn.Conv2d. - padding (int or tuple[int]): Same as nn.Conv2d. - dilation (int or tuple[int]): Same as nn.Conv2d. - groups (int): Same as nn.Conv2d. - bias (bool or str): If specified as `auto`, it will be decided by the - norm_cfg. Bias will be set as True if norm_cfg is None, otherwise - False. - conv_cfg (dict): Config dict for convolution layer. - norm_cfg (dict): Config dict for normalization layer. - act_cfg (dict): Config dict for activation layer, "relu" by default. - inplace (bool): Whether to use inplace mode for activation. - with_spectral_norm (bool): Whether use spectral norm in conv module. - padding_mode (str): If the `padding_mode` has not been supported by - current `Conv2d` in Pytorch, we will use our own padding layer - instead. Currently, we support ['zeros', 'circular'] with official - implementation and ['reflect'] with our own implementation. - Default: 'zeros'. - order (tuple[str]): The order of conv/norm/activation layers. It is a - sequence of "conv", "norm" and "act". Examples are - ("conv", "norm", "act") and ("act", "conv", "norm"). - """ - supported_conv_list = ['PConv'] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - assert self.conv_cfg['type'] in self.supported_conv_list - - self.init_weights() - - def forward(self, - x, - mask=None, - activate=True, - norm=True, - return_mask=True): - """Forward function for partial conv2d. - - Args: - input (torch.Tensor): Tensor with shape of (n, c, h, w). - mask (torch.Tensor): Tensor with shape of (n, c, h, w) or - (n, 1, h, w). If mask is not given, the function will - work as standard conv2d. Default: None. - activate (bool): Whether use activation layer. - norm (bool): Whether use norm layer. - return_mask (bool): If True and mask is not None, the updated - mask will be returned. Default: True. - - Returns: - Tensor or tuple: Result Tensor or 2-tuple of - - ``Tensor``: Results after partial conv. - - ``Tensor``: Updated mask will be returned if mask is given \ - and `return_mask` is True. - """ - for layer in self.order: - if layer == 'conv': - if self.with_explicit_padding: - x = self.padding_layer(x) - mask = self.padding_layer(mask) - if return_mask: - x, updated_mask = self.conv( - x, mask, return_mask=return_mask) - else: - x = self.conv(x, mask, return_mask=False) - elif layer == 'norm' and norm and self.with_norm: - x = self.norm(x) - elif layer == 'act' and activate and self.with_activation: - x = self.activate(x) - - if return_mask: - return x, updated_mask - - return x diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/model_utils.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/model_utils.py deleted file mode 100755 index 62135c6e6..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/model_utils.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch - - -def set_requires_grad(nets, requires_grad=False): - """Set requires_grad for all the networks. - - Args: - nets (nn.Module | list[nn.Module]): A list of networks or a single - network. - requires_grad (bool): Whether the networks require gradients or not - """ - if not isinstance(nets, list): - nets = [nets] - for net in nets: - if net is not None: - for param in net.parameters(): - param.requires_grad = requires_grad - - -def extract_bbox_patch(bbox, img, channel_first=True): - """Extract patch from a given bbox - - Args: - bbox (torch.Tensor | numpy.array): Bbox with (top, left, h, w). If - `img` has batch dimension, the `bbox` must be stacked at first - dimension. The shape should be (4,) or (n, 4). - img (torch.Tensor | numpy.array): Image data to be extracted. If - organized in batch dimension, the batch dimension must be the first - order like (n, h, w, c) or (n, c, h, w). - channel_first (bool): If True, the channel dimension of img is before - height and width, e.g. (c, h, w). Otherwise, the img shape (samples - in the batch) is like (h, w, c). - - Returns: - (torch.Tensor | numpy.array): Extracted patches. The dimension of the \ - output should be the same as `img`. - """ - - def _extract(bbox, img): - assert len(bbox) == 4 - t, l, h, w = bbox - if channel_first: - img_patch = img[..., t:t + h, l:l + w] - else: - img_patch = img[t:t + h, l:l + w, ...] - - return img_patch - - input_size = img.shape - assert len(input_size) == 3 or len(input_size) == 4 - bbox_size = bbox.shape - assert bbox_size == (4, ) or (len(bbox_size) == 2 - and bbox_size[0] == input_size[0]) - - # images with batch dimension - if len(input_size) == 4: - output_list = [] - for i in range(input_size[0]): - img_patch_ = _extract(bbox[i], img[i:i + 1, ...]) - output_list.append(img_patch_) - if isinstance(img, torch.Tensor): - img_patch = torch.cat(output_list, dim=0) - else: - img_patch = np.concatenate(output_list, axis=0) - # standardize image - else: - img_patch = _extract(bbox, img) - - return img_patch - - -def scale_bbox(bbox, target_size): - """Modify bbox to target size. - - The original bbox will be enlarged to the target size with the original - bbox in the center of the new bbox. - - Args: - bbox (np.ndarray | torch.Tensor): Bboxes to be modified. Bbox can - be in batch or not. The shape should be (4,) or (n, 4). - target_size (tuple[int]): Target size of final bbox. - - Returns: - (np.ndarray | torch.Tensor): Modified bboxes. - """ - - def _mod(bbox, target_size): - top_ori, left_ori, h_ori, w_ori = bbox - h, w = target_size - assert h >= h_ori and w >= w_ori - top = int(max(0, top_ori - (h - h_ori) // 2)) - left = int(max(0, left_ori - (w - w_ori) // 2)) - - if isinstance(bbox, torch.Tensor): - bbox_new = torch.Tensor([top, left, h, w]).type_as(bbox) - else: - bbox_new = np.asarray([top, left, h, w]) - - return bbox_new - - if isinstance(bbox, torch.Tensor): - bbox_new = torch.zeros_like(bbox) - elif isinstance(bbox, np.ndarray): - bbox_new = np.zeros_like(bbox) - else: - raise TypeError('bbox mush be torch.Tensor or numpy.ndarray' - f'but got type {type(bbox)}') - bbox_shape = list(bbox.shape) - - if len(bbox_shape) == 2: - for i in range(bbox_shape[0]): - bbox_new[i, :] = _mod(bbox[i], target_size) - else: - bbox_new = _mod(bbox, target_size) - - return bbox_new - - -def extract_around_bbox(img, bbox, target_size, channel_first=True): - """Extract patches around the given bbox. - - Args: - bbox (np.ndarray | torch.Tensor): Bboxes to be modified. Bbox can - be in batch or not. - target_size (List(int)): Target size of final bbox. - - Returns: - (torch.Tensor | numpy.array): Extracted patches. The dimension of the \ - output should be the same as `img`. - """ - bbox_new = scale_bbox(bbox, target_size) - img_patch = extract_bbox_patch(bbox_new, img, channel_first=channel_first) - - return img_patch, bbox_new diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/partial_conv.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/partial_conv.py deleted file mode 100755 index 51b50feca..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/partial_conv.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numpy as np -import torch -import torch.nn as nn -import torch.nn.functional as F -from mmcv.cnn import CONV_LAYERS - - -@CONV_LAYERS.register_module(name='PConv') -class PartialConv2d(nn.Conv2d): - """Implementation for partial convolution. - - Image Inpainting for Irregular Holes Using Partial Convolutions - [https://arxiv.org/abs/1804.07723] - - Args: - multi_channel (bool): If True, the mask is multi-channel. Otherwise, - the mask is single-channel. - eps (float): Need to be changed for mixed precision training. - For mixed precision training, you need change 1e-8 to 1e-6. - """ - - def __init__(self, *args, multi_channel=False, eps=1e-8, **kwargs): - super().__init__(*args, **kwargs) - - # whether the mask is multi-channel or not - self.multi_channel = multi_channel - self.eps = eps - - if self.multi_channel: - out_channels, in_channels = self.out_channels, self.in_channels - else: - out_channels, in_channels = 1, 1 - - self.register_buffer( - 'weight_mask_updater', - torch.ones(out_channels, in_channels, self.kernel_size[0], - self.kernel_size[1])) - - self.mask_kernel_numel = np.prod(self.weight_mask_updater.shape[1:4]) - self.mask_kernel_numel = (self.mask_kernel_numel).item() - - def forward(self, input, mask=None, return_mask=True): - """Forward function for partial conv2d. - - Args: - input (torch.Tensor): Tensor with shape of (n, c, h, w). - mask (torch.Tensor): Tensor with shape of (n, c, h, w) or - (n, 1, h, w). If mask is not given, the function will - work as standard conv2d. Default: None. - return_mask (bool): If True and mask is not None, the updated - mask will be returned. Default: True. - - Returns: - torch.Tensor : Results after partial conv.\ - torch.Tensor : Updated mask will be returned if mask is given and \ - ``return_mask`` is True. - """ - assert input.dim() == 4 - if mask is not None: - assert mask.dim() == 4 - if self.multi_channel: - assert mask.shape[1] == input.shape[1] - else: - assert mask.shape[1] == 1 - - # update mask and compute mask ratio - if mask is not None: - with torch.no_grad(): - - updated_mask = F.conv2d( - mask, - self.weight_mask_updater, - bias=None, - stride=self.stride, - padding=self.padding, - dilation=self.dilation) - mask_ratio = self.mask_kernel_numel / (updated_mask + self.eps) - - updated_mask = torch.clamp(updated_mask, 0, 1) - mask_ratio = mask_ratio * updated_mask - - # standard conv2d - if mask is not None: - input = input * mask - raw_out = super().forward(input) - - if mask is not None: - if self.bias is None: - output = raw_out * mask_ratio - else: - # compute new bias when mask is given - bias_view = self.bias.view(1, self.out_channels, 1, 1) - output = (raw_out - bias_view) * mask_ratio + bias_view - output = output * updated_mask - else: - output = raw_out - - if return_mask and mask is not None: - return output, updated_mask - - return output diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/separable_conv_module.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/separable_conv_module.py deleted file mode 100755 index 139a54e58..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/separable_conv_module.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import ConvModule - - -class DepthwiseSeparableConvModule(nn.Module): - """Depthwise separable convolution module. - - See https://arxiv.org/pdf/1704.04861.pdf for details. - - This module can replace a ConvModule with the conv block replaced by two - conv block: depthwise conv block and pointwise conv block. The depthwise - conv block contains depthwise-conv/norm/activation layers. The pointwise - conv block contains pointwise-conv/norm/activation layers. It should be - noted that there will be norm/activation layer in the depthwise conv block - if ``norm_cfg`` and ``act_cfg`` are specified. - - Args: - in_channels (int): Same as nn.Conv2d. - out_channels (int): Same as nn.Conv2d. - kernel_size (int or tuple[int]): Same as nn.Conv2d. - stride (int or tuple[int]): Same as nn.Conv2d. Default: 1. - padding (int or tuple[int]): Same as nn.Conv2d. Default: 0. - dilation (int or tuple[int]): Same as nn.Conv2d. Default: 1. - norm_cfg (dict): Default norm config for both depthwise ConvModule and - pointwise ConvModule. Default: None. - act_cfg (dict): Default activation config for both depthwise ConvModule - and pointwise ConvModule. Default: dict(type='ReLU'). - dw_norm_cfg (dict): Norm config of depthwise ConvModule. If it is - 'default', it will be the same as ``norm_cfg``. Default: 'default'. - dw_act_cfg (dict): Activation config of depthwise ConvModule. If it is - 'default', it will be the same as ``act_cfg``. Default: 'default'. - pw_norm_cfg (dict): Norm config of pointwise ConvModule. If it is - 'default', it will be the same as `norm_cfg`. Default: 'default'. - pw_act_cfg (dict): Activation config of pointwise ConvModule. If it is - 'default', it will be the same as ``act_cfg``. Default: 'default'. - kwargs (optional): Other shared arguments for depthwise and pointwise - ConvModule. See ConvModule for ref. - """ - - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride=1, - padding=0, - dilation=1, - norm_cfg=None, - act_cfg=dict(type='ReLU'), - dw_norm_cfg='default', - dw_act_cfg='default', - pw_norm_cfg='default', - pw_act_cfg='default', - **kwargs): - super().__init__() - assert 'groups' not in kwargs, 'groups should not be specified' - - # if norm/activation config of depthwise/pointwise ConvModule is not - # specified, use default config. - dw_norm_cfg = dw_norm_cfg if dw_norm_cfg != 'default' else norm_cfg - dw_act_cfg = dw_act_cfg if dw_act_cfg != 'default' else act_cfg - pw_norm_cfg = pw_norm_cfg if pw_norm_cfg != 'default' else norm_cfg - pw_act_cfg = pw_act_cfg if pw_act_cfg != 'default' else act_cfg - - # depthwise convolution - self.depthwise_conv = ConvModule( - in_channels, - in_channels, - kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=in_channels, - norm_cfg=dw_norm_cfg, - act_cfg=dw_act_cfg, - **kwargs) - - self.pointwise_conv = ConvModule( - in_channels, - out_channels, - 1, - norm_cfg=pw_norm_cfg, - act_cfg=pw_act_cfg, - **kwargs) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (N, C, H, W). - - Returns: - Tensor: Output tensor. - """ - x = self.depthwise_conv(x) - x = self.pointwise_conv(x) - return x diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/sr_backbone_utils.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/sr_backbone_utils.py deleted file mode 100755 index b4b0aad91..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/sr_backbone_utils.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.cnn import constant_init, kaiming_init -from mmcv.utils.parrots_wrapper import _BatchNorm - - -def default_init_weights(module, scale=1): - """Initialize network weights. - - Args: - modules (nn.Module): Modules to be initialized. - scale (float): Scale initialized weights, especially for residual - blocks. - """ - for m in module.modules(): - if isinstance(m, nn.Conv2d): - kaiming_init(m, a=0, mode='fan_in', bias=0) - m.weight.data *= scale - elif isinstance(m, nn.Linear): - kaiming_init(m, a=0, mode='fan_in', bias=0) - m.weight.data *= scale - elif isinstance(m, _BatchNorm): - constant_init(m.weight, val=1, bias=0) - - -def make_layer(block, num_blocks, **kwarg): - """Make layers by stacking the same blocks. - - Args: - block (nn.module): nn.module class for basic block. - num_blocks (int): number of blocks. - - Returns: - nn.Sequential: Stacked blocks in nn.Sequential. - """ - layers = [] - for _ in range(num_blocks): - layers.append(block(**kwarg)) - return nn.Sequential(*layers) - - -class ResidualBlockNoBN(nn.Module): - """Residual block without BN. - - It has a style of: - - :: - - ---Conv-ReLU-Conv-+- - |________________| - - Args: - mid_channels (int): Channel number of intermediate features. - Default: 64. - res_scale (float): Used to scale the residual before addition. - Default: 1.0. - """ - - def __init__(self, mid_channels=64, res_scale=1.0): - super().__init__() - self.res_scale = res_scale - self.conv1 = nn.Conv2d(mid_channels, mid_channels, 3, 1, 1, bias=True) - self.conv2 = nn.Conv2d(mid_channels, mid_channels, 3, 1, 1, bias=True) - - self.relu = nn.ReLU(inplace=True) - - # if res_scale < 1.0, use the default initialization, as in EDSR. - # if res_scale = 1.0, use scaled kaiming_init, as in MSRResNet. - if res_scale == 1.0: - self.init_weights() - - def init_weights(self): - """Initialize weights for ResidualBlockNoBN. - - Initialization methods like `kaiming_init` are for VGG-style - modules. For modules with residual paths, using smaller std is - better for stability and performance. We empirically use 0.1. - See more details in "ESRGAN: Enhanced Super-Resolution Generative - Adversarial Networks" - """ - - for m in [self.conv1, self.conv2]: - default_init_weights(m, 0.1) - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - identity = x - out = self.conv2(self.relu(self.conv1(x))) - return identity + out * self.res_scale diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/upsample.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/upsample.py deleted file mode 100755 index f39ec1a9e..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/common/upsample.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -import torch.nn.functional as F - -from .sr_backbone_utils import default_init_weights - - -class PixelShufflePack(nn.Module): - """ Pixel Shuffle upsample layer. - - Args: - in_channels (int): Number of input channels. - out_channels (int): Number of output channels. - scale_factor (int): Upsample ratio. - upsample_kernel (int): Kernel size of Conv layer to expand channels. - - Returns: - Upsampled feature map. - """ - - def __init__(self, in_channels, out_channels, scale_factor, - upsample_kernel): - super().__init__() - self.in_channels = in_channels - self.out_channels = out_channels - self.scale_factor = scale_factor - self.upsample_kernel = upsample_kernel - self.upsample_conv = nn.Conv2d( - self.in_channels, - self.out_channels * scale_factor * scale_factor, - self.upsample_kernel, - padding=(self.upsample_kernel - 1) // 2) - self.init_weights() - - def init_weights(self): - """Initialize weights for PixelShufflePack. - """ - default_init_weights(self, 1) - - def forward(self, x): - """Forward function for PixelShufflePack. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - x = self.upsample_conv(x) - x = F.pixel_shuffle(x, self.scale_factor) - return x diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/components/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/components/__init__.py deleted file mode 100755 index 3415f6d3c..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/components/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .discriminators import UNetDiscriminatorWithSpectralNorm diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/components/discriminators/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/components/discriminators/__init__.py deleted file mode 100755 index c372a8476..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/components/discriminators/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .unet_disc import UNetDiscriminatorWithSpectralNorm - diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/components/discriminators/unet_disc.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/components/discriminators/unet_disc.py deleted file mode 100755 index 43c253d1d..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/components/discriminators/unet_disc.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn as nn -from mmcv.runner import load_checkpoint -from torch.nn.utils import spectral_norm - -from mmedit.models.registry import COMPONENTS -from mmedit.utils import get_root_logger - - -@COMPONENTS.register_module() -class UNetDiscriminatorWithSpectralNorm(nn.Module): - """A U-Net discriminator with spectral normalization. - - Args: - in_channels (int): Channel number of the input. - mid_channels (int, optional): Channel number of the intermediate - features. Default: 64. - skip_connection (bool, optional): Whether to use skip connection. - Default: True. - """ - - def __init__(self, in_channels, mid_channels=64, skip_connection=True): - - super().__init__() - - self.skip_connection = skip_connection - - self.conv_0 = nn.Conv2d( - in_channels, mid_channels, kernel_size=3, stride=1, padding=1) - - # downsample - self.conv_1 = spectral_norm( - nn.Conv2d(mid_channels, mid_channels * 2, 4, 2, 1, bias=False)) - self.conv_2 = spectral_norm( - nn.Conv2d(mid_channels * 2, mid_channels * 4, 4, 2, 1, bias=False)) - self.conv_3 = spectral_norm( - nn.Conv2d(mid_channels * 4, mid_channels * 8, 4, 2, 1, bias=False)) - - # upsample - self.conv_4 = spectral_norm( - nn.Conv2d(mid_channels * 8, mid_channels * 4, 3, 1, 1, bias=False)) - self.conv_5 = spectral_norm( - nn.Conv2d(mid_channels * 4, mid_channels * 2, 3, 1, 1, bias=False)) - self.conv_6 = spectral_norm( - nn.Conv2d(mid_channels * 2, mid_channels, 3, 1, 1, bias=False)) - - # final layers - self.conv_7 = spectral_norm( - nn.Conv2d(mid_channels, mid_channels, 3, 1, 1, bias=False)) - self.conv_8 = spectral_norm( - nn.Conv2d(mid_channels, mid_channels, 3, 1, 1, bias=False)) - self.conv_9 = nn.Conv2d(mid_channels, 1, 3, 1, 1) - - self.upsample = nn.Upsample( - scale_factor=2, mode='bilinear', align_corners=False) - self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) - - def forward(self, img): - """Forward function. - - Args: - img (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - feat_0 = self.lrelu(self.conv_0(img)) - - # downsample - feat_1 = self.lrelu(self.conv_1(feat_0)) - feat_2 = self.lrelu(self.conv_2(feat_1)) - feat_3 = self.lrelu(self.conv_3(feat_2)) - - # upsample - feat_3 = self.upsample(feat_3) - feat_4 = self.lrelu(self.conv_4(feat_3)) - if self.skip_connection: - feat_4 = feat_4 + feat_2 - - feat_4 = self.upsample(feat_4) - feat_5 = self.lrelu(self.conv_5(feat_4)) - if self.skip_connection: - feat_5 = feat_5 + feat_1 - - feat_5 = self.upsample(feat_5) - feat_6 = self.lrelu(self.conv_6(feat_5)) - if self.skip_connection: - feat_6 = feat_6 + feat_0 - - # final layers - out = self.lrelu(self.conv_7(feat_6)) - out = self.lrelu(self.conv_8(out)) - - return self.conv_9(out) - - def init_weights(self, pretrained=None, strict=True): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - strict (boo, optional): Whether strictly load the pretrained model. - Defaults to True. - """ - - if isinstance(pretrained, str): - logger = get_root_logger() - load_checkpoint(self, pretrained, strict=strict, logger=logger) - elif pretrained is not None: # Use PyTorch default initialization. - raise TypeError(f'"pretrained" must be a str or None. ' - f'But received {type(pretrained)}.') diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/__init__.py deleted file mode 100755 index ac79be43d..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .gan_loss import DiscShiftLoss, GANLoss, GaussianBlur, GradientPenaltyLoss -from .perceptual_loss import (PerceptualLoss, PerceptualVGG, - TransferalPerceptualLoss) -from .pixelwise_loss import CharbonnierLoss, L1Loss, MaskedTVLoss, MSELoss -from .utils import mask_reduce_loss, reduce_loss - diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/gan_loss.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/gan_loss.py deleted file mode 100755 index 35a6af812..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/gan_loss.py +++ /dev/null @@ -1,344 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.autograd as autograd -import torch.nn as nn -import torch.nn.functional as F -from torch.nn.functional import conv2d - -from ..registry import LOSSES - - -@LOSSES.register_module() -class GANLoss(nn.Module): - """Define GAN loss. - - Args: - gan_type (str): Support 'vanilla', 'lsgan', 'wgan', 'hinge'. - real_label_val (float): The value for real label. Default: 1.0. - fake_label_val (float): The value for fake label. Default: 0.0. - loss_weight (float): Loss weight. Default: 1.0. - Note that loss_weight is only for generators; and it is always 1.0 - for discriminators. - """ - - def __init__(self, - gan_type, - real_label_val=1.0, - fake_label_val=0.0, - loss_weight=1.0): - super().__init__() - self.gan_type = gan_type - self.real_label_val = real_label_val - self.fake_label_val = fake_label_val - self.loss_weight = loss_weight - if self.gan_type == 'smgan': - self.gaussian_blur = GaussianBlur() - - if self.gan_type == 'vanilla': - self.loss = nn.BCEWithLogitsLoss() - elif self.gan_type == 'lsgan' or self.gan_type == 'smgan': - self.loss = nn.MSELoss() - elif self.gan_type == 'wgan': - self.loss = self._wgan_loss - elif self.gan_type == 'hinge': - self.loss = nn.ReLU() - else: - raise NotImplementedError( - f'GAN type {self.gan_type} is not implemented.') - - def _wgan_loss(self, input, target): - """wgan loss. - - Args: - input (Tensor): Input tensor. - target (bool): Target label. - - Returns: - Tensor: wgan loss. - """ - - return -input.mean() if target else input.mean() - - def get_target_label(self, input, target_is_real): - """Get target label. - - Args: - input (Tensor): Input tensor. - target_is_real (bool): Whether the target is real or fake. - - Returns: - (bool | Tensor): Target tensor. Return bool for wgan, otherwise, - return Tensor. - """ - - if self.gan_type == 'wgan': - return target_is_real - target_val = ( - self.real_label_val if target_is_real else self.fake_label_val) - return input.new_ones(input.size()) * target_val - - def forward(self, input, target_is_real, is_disc=False, mask=None): - """ - Args: - input (Tensor): The input for the loss module, i.e., the network - prediction. - target_is_real (bool): Whether the target is real or fake. - is_disc (bool): Whether the loss for discriminators or not. - Default: False. - - Returns: - Tensor: GAN loss value. - """ - - target_label = self.get_target_label(input, target_is_real) - if self.gan_type == 'hinge': - if is_disc: # for discriminators in hinge-gan - input = -input if target_is_real else input - loss = self.loss(1 + input).mean() - else: # for generators in hinge-gan - loss = -input.mean() - elif self.gan_type == 'smgan': - - input_height, input_width = input.shape[2:] - mask_height, mask_width = mask.shape[2:] - - # Handle inconsistent size between outputs and masks - if input_height != mask_height or input_width != mask_width: - input = F.interpolate( - input, - size=(mask_height, mask_width), - mode='bilinear', - align_corners=True) - - target_label = self.get_target_label(input, target_is_real) - - if is_disc: - if target_is_real: - target_label = target_label - else: - target_label = self.gaussian_blur(mask).detach().cuda( - ) if mask.is_cuda else self.gaussian_blur( - mask).detach().cpu() - # target_label = self.gaussian_blur(mask).detach().cpu() - loss = self.loss(input, target_label) - else: - loss = self.loss(input, target_label) * mask / mask.mean() - loss = loss.mean() - else: # other gan types - loss = self.loss(input, target_label) - - # loss_weight is always 1.0 for discriminators - return loss if is_disc else loss * self.loss_weight - - -@LOSSES.register_module() -class GaussianBlur(nn.Module): - """A Gaussian filter which blurs a given tensor with a two-dimensional - gaussian kernel by convolving it along each channel. Batch operation - is supported. - - This function is modified from kornia.filters.gaussian: - ``. - - Args: - kernel_size (tuple[int]): The size of the kernel. Default: (71, 71). - sigma (tuple[float]): The standard deviation of the kernel. - Default (10.0, 10.0) - - Returns: - Tensor: The Gaussian-blurred tensor. - - Shape: - - input: Tensor with shape of (n, c, h, w) - - output: Tensor with shape of (n, c, h, w) - """ - - def __init__(self, kernel_size=(71, 71), sigma=(10.0, 10.0)): - super(GaussianBlur, self).__init__() - self.kernel_size = kernel_size - self.sigma = sigma - self.padding = self.compute_zero_padding(kernel_size) - self.kernel = self.get_2d_gaussian_kernel(kernel_size, sigma) - - @staticmethod - def compute_zero_padding(kernel_size): - """Compute zero padding tuple.""" - - padding = [(ks - 1) // 2 for ks in kernel_size] - - return padding[0], padding[1] - - def get_2d_gaussian_kernel(self, kernel_size, sigma): - """Get the two-dimensional Gaussian filter matrix coefficients. - - Args: - kernel_size (tuple[int]): Kernel filter size in the x and y - direction. The kernel sizes - should be odd and positive. - sigma (tuple[int]): Gaussian standard deviation in - the x and y direction. - - Returns: - kernel_2d (Tensor): A 2D torch tensor with gaussian filter - matrix coefficients. - """ - - if not isinstance(kernel_size, tuple) or len(kernel_size) != 2: - raise TypeError( - 'kernel_size must be a tuple of length two. Got {}'.format( - kernel_size)) - if not isinstance(sigma, tuple) or len(sigma) != 2: - raise TypeError( - 'sigma must be a tuple of length two. Got {}'.format(sigma)) - - kernel_size_x, kernel_size_y = kernel_size - sigma_x, sigma_y = sigma - - kernel_x = self.get_1d_gaussian_kernel(kernel_size_x, sigma_x) - kernel_y = self.get_1d_gaussian_kernel(kernel_size_y, sigma_y) - kernel_2d = torch.matmul( - kernel_x.unsqueeze(-1), - kernel_y.unsqueeze(-1).t()) - - return kernel_2d - - def get_1d_gaussian_kernel(self, kernel_size, sigma): - """Get the Gaussian filter coefficients in one dimension (x or y direction). - - Args: - kernel_size (int): Kernel filter size in x or y direction. - Should be odd and positive. - sigma (float): Gaussian standard deviation in x or y direction. - - Returns: - kernel_1d (Tensor): A 1D torch tensor with gaussian filter - coefficients in x or y direction. - """ - - if not isinstance(kernel_size, - int) or kernel_size % 2 == 0 or kernel_size <= 0: - raise TypeError( - 'kernel_size must be an odd positive integer. Got {}'.format( - kernel_size)) - - kernel_1d = self.gaussian(kernel_size, sigma) - return kernel_1d - - def gaussian(self, kernel_size, sigma): - - def gauss_arg(x): - return -(x - kernel_size // 2)**2 / float(2 * sigma**2) - - gauss = torch.stack([ - torch.exp(torch.tensor(gauss_arg(x))) for x in range(kernel_size) - ]) - return gauss / gauss.sum() - - def forward(self, x): - if not torch.is_tensor(x): - raise TypeError( - 'Input x type is not a torch.Tensor. Got {}'.format(type(x))) - if not len(x.shape) == 4: - raise ValueError( - 'Invalid input shape, we expect BxCxHxW. Got: {}'.format( - x.shape)) - _, c, _, _ = x.shape - tmp_kernel = self.kernel.to(x.device).to(x.dtype) - kernel = tmp_kernel.repeat(c, 1, 1, 1) - - return conv2d(x, kernel, padding=self.padding, stride=1, groups=c) - - -def gradient_penalty_loss(discriminator, real_data, fake_data, mask=None): - """Calculate gradient penalty for wgan-gp. - - Args: - discriminator (nn.Module): Network for the discriminator. - real_data (Tensor): Real input data. - fake_data (Tensor): Fake input data. - mask (Tensor): Masks for inpainting. Default: None. - - Returns: - Tensor: A tensor for gradient penalty. - """ - - batch_size = real_data.size(0) - alpha = torch.rand(batch_size, 1, 1, 1).to(real_data) - - # interpolate between real_data and fake_data - interpolates = alpha * real_data + (1. - alpha) * fake_data - interpolates = autograd.Variable(interpolates, requires_grad=True) - - disc_interpolates = discriminator(interpolates) - gradients = autograd.grad( - outputs=disc_interpolates, - inputs=interpolates, - grad_outputs=torch.ones_like(disc_interpolates), - create_graph=True, - retain_graph=True, - only_inputs=True)[0] - - if mask is not None: - gradients = gradients * mask - - gradients_penalty = ((gradients.norm(2, dim=1) - 1)**2).mean() - if mask is not None: - gradients_penalty /= torch.mean(mask) - - return gradients_penalty - - -@LOSSES.register_module() -class GradientPenaltyLoss(nn.Module): - """Gradient penalty loss for wgan-gp. - - Args: - loss_weight (float): Loss weight. Default: 1.0. - """ - - def __init__(self, loss_weight=1.): - super().__init__() - self.loss_weight = loss_weight - - def forward(self, discriminator, real_data, fake_data, mask=None): - """Forward function. - - Args: - discriminator (nn.Module): Network for the discriminator. - real_data (Tensor): Real input data. - fake_data (Tensor): Fake input data. - mask (Tensor): Masks for inpainting. Default: None. - - Returns: - Tensor: Loss. - """ - loss = gradient_penalty_loss( - discriminator, real_data, fake_data, mask=mask) - - return loss * self.loss_weight - - -@LOSSES.register_module() -class DiscShiftLoss(nn.Module): - """Disc shift loss. - - Args: - loss_weight (float, optional): Loss weight. Defaults to 1.0. - """ - - def __init__(self, loss_weight=0.1): - super().__init__() - self.loss_weight = loss_weight - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Tensor with shape (n, c, h, w) - - Returns: - Tensor: Loss. - """ - loss = torch.mean(x**2) - - return loss * self.loss_weight diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/perceptual_loss.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/perceptual_loss.py deleted file mode 100755 index 365790464..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/perceptual_loss.py +++ /dev/null @@ -1,287 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torchvision.models.vgg as vgg -from mmcv.runner import load_checkpoint -from torch.nn import functional as F - -from mmedit.utils import get_root_logger -from ..registry import LOSSES - - -class PerceptualVGG(nn.Module): - """VGG network used in calculating perceptual loss. - - In this implementation, we allow users to choose whether use normalization - in the input feature and the type of vgg network. Note that the pretrained - path must fit the vgg type. - - Args: - layer_name_list (list[str]): According to the name in this list, - forward function will return the corresponding features. This - list contains the name each layer in `vgg.feature`. An example - of this list is ['4', '10']. - vgg_type (str): Set the type of vgg network. Default: 'vgg19'. - use_input_norm (bool): If True, normalize the input image. - Importantly, the input feature must in the range [0, 1]. - Default: True. - pretrained (str): Path for pretrained weights. Default: - 'torchvision://vgg19' - """ - - def __init__(self, - layer_name_list, - vgg_type='vgg19', - use_input_norm=True, - pretrained='torchvision://vgg19'): - super().__init__() - if pretrained.startswith('torchvision://'): - assert vgg_type in pretrained - self.layer_name_list = layer_name_list - self.use_input_norm = use_input_norm - - # get vgg model and load pretrained vgg weight - # remove _vgg from attributes to avoid `find_unused_parameters` bug - _vgg = getattr(vgg, vgg_type)() - self.init_weights(_vgg, pretrained) - num_layers = max(map(int, layer_name_list)) + 1 - assert len(_vgg.features) >= num_layers - # only borrow layers that will be used from _vgg to avoid unused params - self.vgg_layers = _vgg.features[:num_layers] - - if self.use_input_norm: - # the mean is for image with range [0, 1] - self.register_buffer( - 'mean', - torch.Tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1)) - # the std is for image with range [-1, 1] - self.register_buffer( - 'std', - torch.Tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1)) - - for v in self.vgg_layers.parameters(): - v.requires_grad = False - - def forward(self, x): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - if self.use_input_norm: - x = (x - self.mean) / self.std - output = {} - - for name, module in self.vgg_layers.named_children(): - x = module(x) - if name in self.layer_name_list: - output[name] = x.clone() - return output - - def init_weights(self, model, pretrained): - """Init weights. - - Args: - model (nn.Module): Models to be inited. - pretrained (str): Path for pretrained weights. - """ - logger = get_root_logger() - load_checkpoint(model, pretrained, logger=logger) - - -@LOSSES.register_module() -class PerceptualLoss(nn.Module): - """Perceptual loss with commonly used style loss. - - Args: - layers_weights (dict): The weight for each layer of vgg feature for - perceptual loss. Here is an example: {'4': 1., '9': 1., '18': 1.}, - which means the 5th, 10th and 18th feature layer will be - extracted with weight 1.0 in calculating losses. - layers_weights_style (dict): The weight for each layer of vgg feature - for style loss. If set to 'None', the weights are set equal to - the weights for perceptual loss. Default: None. - vgg_type (str): The type of vgg network used as feature extractor. - Default: 'vgg19'. - use_input_norm (bool): If True, normalize the input image in vgg. - Default: True. - perceptual_weight (float): If `perceptual_weight > 0`, the perceptual - loss will be calculated and the loss will multiplied by the - weight. Default: 1.0. - style_weight (float): If `style_weight > 0`, the style loss will be - calculated and the loss will multiplied by the weight. - Default: 1.0. - norm_img (bool): If True, the image will be normed to [0, 1]. Note that - this is different from the `use_input_norm` which norm the input in - in forward function of vgg according to the statistics of dataset. - Importantly, the input image must be in range [-1, 1]. - pretrained (str): Path for pretrained weights. Default: - 'torchvision://vgg19'. - criterion (str): Criterion type. Options are 'l1' and 'mse'. - Default: 'l1'. - """ - - def __init__(self, - layer_weights, - layer_weights_style=None, - vgg_type='vgg19', - use_input_norm=True, - perceptual_weight=1.0, - style_weight=1.0, - norm_img=True, - pretrained='torchvision://vgg19', - criterion='l1'): - super().__init__() - self.norm_img = norm_img - self.perceptual_weight = perceptual_weight - self.style_weight = style_weight - self.layer_weights = layer_weights - self.layer_weights_style = layer_weights_style - - self.vgg = PerceptualVGG( - layer_name_list=list(self.layer_weights.keys()), - vgg_type=vgg_type, - use_input_norm=use_input_norm, - pretrained=pretrained) - - if self.layer_weights_style is not None and \ - self.layer_weights_style != self.layer_weights: - self.vgg_style = PerceptualVGG( - layer_name_list=list(self.layer_weights_style.keys()), - vgg_type=vgg_type, - use_input_norm=use_input_norm, - pretrained=pretrained) - else: - self.layer_weights_style = self.layer_weights - self.vgg_style = None - - criterion = criterion.lower() - if criterion == 'l1': - self.criterion = torch.nn.L1Loss() - elif criterion == 'mse': - self.criterion = torch.nn.MSELoss() - else: - raise NotImplementedError( - f'{criterion} criterion has not been supported in' - ' this version.') - - def forward(self, x, gt): - """Forward function. - - Args: - x (Tensor): Input tensor with shape (n, c, h, w). - gt (Tensor): Ground-truth tensor with shape (n, c, h, w). - - Returns: - Tensor: Forward results. - """ - - if self.norm_img: - x = (x + 1.) * 0.5 - gt = (gt + 1.) * 0.5 - # extract vgg features - x_features = self.vgg(x) - gt_features = self.vgg(gt.detach()) - - # calculate perceptual loss - if self.perceptual_weight > 0: - percep_loss = 0 - for k in x_features.keys(): - percep_loss += self.criterion( - x_features[k], gt_features[k]) * self.layer_weights[k] - percep_loss *= self.perceptual_weight - else: - percep_loss = None - - # calculate style loss - if self.style_weight > 0: - if self.vgg_style is not None: - x_features = self.vgg_style(x) - gt_features = self.vgg_style(gt.detach()) - - style_loss = 0 - for k in x_features.keys(): - style_loss += self.criterion( - self._gram_mat(x_features[k]), - self._gram_mat( - gt_features[k])) * self.layer_weights_style[k] - style_loss *= self.style_weight - else: - style_loss = None - - return percep_loss, style_loss - - def _gram_mat(self, x): - """Calculate Gram matrix. - - Args: - x (torch.Tensor): Tensor with shape of (n, c, h, w). - - Returns: - torch.Tensor: Gram matrix. - """ - (n, c, h, w) = x.size() - features = x.view(n, c, w * h) - features_t = features.transpose(1, 2) - gram = features.bmm(features_t) / (c * h * w) - return gram - - -@LOSSES.register_module() -class TransferalPerceptualLoss(nn.Module): - """Transferal perceptual loss. - - Args: - loss_weight (float): Loss weight. Default: 1.0. - use_attention (bool): If True, use soft-attention tensor. Default: True - criterion (str): Criterion type. Options are 'l1' and 'mse'. - Default: 'l1'. - """ - - def __init__(self, loss_weight=1.0, use_attention=True, criterion='mse'): - super().__init__() - self.use_attention = use_attention - self.loss_weight = loss_weight - criterion = criterion.lower() - if criterion == 'l1': - self.loss_function = torch.nn.L1Loss() - elif criterion == 'mse': - self.loss_function = torch.nn.MSELoss() - else: - raise ValueError( - f"criterion should be 'l1' or 'mse', but got {criterion}") - - def forward(self, maps, soft_attention, textures): - """Forward function. - - Args: - maps (Tuple[Tensor]): Input tensors. - soft_attention (Tensor): Soft-attention tensor. - textures (Tuple[Tensor]): Ground-truth tensors. - - Returns: - Tensor: Forward results. - """ - - if self.use_attention: - h, w = soft_attention.shape[-2:] - softs = [torch.sigmoid(soft_attention)] - for i in range(1, len(maps)): - softs.append( - F.interpolate( - soft_attention, - size=(h * pow(2, i), w * pow(2, i)), - mode='bicubic', - align_corners=False)) - else: - softs = [1., 1., 1.] - - loss_texture = 0 - for map, soft, texture in zip(maps, softs, textures): - loss_texture += self.loss_function(map * soft, texture * soft) - - return loss_texture * self.loss_weight diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/pixelwise_loss.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/pixelwise_loss.py deleted file mode 100755 index 2f2435b8d..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/pixelwise_loss.py +++ /dev/null @@ -1,221 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -import torch.nn as nn -import torch.nn.functional as F - -from ..registry import LOSSES -from .utils import masked_loss - -_reduction_modes = ['none', 'mean', 'sum'] - - -@masked_loss -def l1_loss(pred, target): - """L1 loss. - - Args: - pred (Tensor): Prediction Tensor with shape (n, c, h, w). - target ([type]): Target Tensor with shape (n, c, h, w). - - Returns: - Tensor: Calculated L1 loss. - """ - return F.l1_loss(pred, target, reduction='none') - - -@masked_loss -def mse_loss(pred, target): - """MSE loss. - - Args: - pred (Tensor): Prediction Tensor with shape (n, c, h, w). - target ([type]): Target Tensor with shape (n, c, h, w). - - Returns: - Tensor: Calculated MSE loss. - """ - return F.mse_loss(pred, target, reduction='none') - - -@masked_loss -def charbonnier_loss(pred, target, eps=1e-12): - """Charbonnier loss. - - Args: - pred (Tensor): Prediction Tensor with shape (n, c, h, w). - target ([type]): Target Tensor with shape (n, c, h, w). - - Returns: - Tensor: Calculated Charbonnier loss. - """ - return torch.sqrt((pred - target)**2 + eps) - - -@LOSSES.register_module() -class L1Loss(nn.Module): - """L1 (mean absolute error, MAE) loss. - - Args: - loss_weight (float): Loss weight for L1 loss. Default: 1.0. - reduction (str): Specifies the reduction to apply to the output. - Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduce loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - """ - - def __init__(self, loss_weight=1.0, reduction='mean', sample_wise=False): - super().__init__() - if reduction not in ['none', 'mean', 'sum']: - raise ValueError(f'Unsupported reduction mode: {reduction}. ' - f'Supported ones are: {_reduction_modes}') - - self.loss_weight = loss_weight - self.reduction = reduction - self.sample_wise = sample_wise - - def forward(self, pred, target, weight=None, **kwargs): - """Forward Function. - - Args: - pred (Tensor): of shape (N, C, H, W). Predicted tensor. - target (Tensor): of shape (N, C, H, W). Ground truth tensor. - weight (Tensor, optional): of shape (N, C, H, W). Element-wise - weights. Default: None. - """ - return self.loss_weight * l1_loss( - pred, - target, - weight, - reduction=self.reduction, - sample_wise=self.sample_wise) - - -@LOSSES.register_module() -class MSELoss(nn.Module): - """MSE (L2) loss. - - Args: - loss_weight (float): Loss weight for MSE loss. Default: 1.0. - reduction (str): Specifies the reduction to apply to the output. - Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduces loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - """ - - def __init__(self, loss_weight=1.0, reduction='mean', sample_wise=False): - super().__init__() - if reduction not in ['none', 'mean', 'sum']: - raise ValueError(f'Unsupported reduction mode: {reduction}. ' - f'Supported ones are: {_reduction_modes}') - - self.loss_weight = loss_weight - self.reduction = reduction - self.sample_wise = sample_wise - - def forward(self, pred, target, weight=None, **kwargs): - """Forward Function. - - Args: - pred (Tensor): of shape (N, C, H, W). Predicted tensor. - target (Tensor): of shape (N, C, H, W). Ground truth tensor. - weight (Tensor, optional): of shape (N, C, H, W). Element-wise - weights. Default: None. - """ - return self.loss_weight * mse_loss( - pred, - target, - weight, - reduction=self.reduction, - sample_wise=self.sample_wise) - - -@LOSSES.register_module() -class CharbonnierLoss(nn.Module): - """Charbonnier loss (one variant of Robust L1Loss, a differentiable - variant of L1Loss). - - Described in "Deep Laplacian Pyramid Networks for Fast and Accurate - Super-Resolution". - - Args: - loss_weight (float): Loss weight for L1 loss. Default: 1.0. - reduction (str): Specifies the reduction to apply to the output. - Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduces loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - eps (float): A value used to control the curvature near zero. - Default: 1e-12. - """ - - def __init__(self, - loss_weight=1.0, - reduction='mean', - sample_wise=False, - eps=1e-12): - super().__init__() - if reduction not in ['none', 'mean', 'sum']: - raise ValueError(f'Unsupported reduction mode: {reduction}. ' - f'Supported ones are: {_reduction_modes}') - - self.loss_weight = loss_weight - self.reduction = reduction - self.sample_wise = sample_wise - self.eps = eps - - def forward(self, pred, target, weight=None, **kwargs): - """Forward Function. - - Args: - pred (Tensor): of shape (N, C, H, W). Predicted tensor. - target (Tensor): of shape (N, C, H, W). Ground truth tensor. - weight (Tensor, optional): of shape (N, C, H, W). Element-wise - weights. Default: None. - """ - return self.loss_weight * charbonnier_loss( - pred, - target, - weight, - eps=self.eps, - reduction=self.reduction, - sample_wise=self.sample_wise) - - -@LOSSES.register_module() -class MaskedTVLoss(L1Loss): - """Masked TV loss. - - Args: - loss_weight (float, optional): Loss weight. Defaults to 1.0. - """ - - def __init__(self, loss_weight=1.0): - super().__init__(loss_weight=loss_weight) - - def forward(self, pred, mask=None): - """Forward function. - - Args: - pred (torch.Tensor): Tensor with shape of (n, c, h, w). - mask (torch.Tensor, optional): Tensor with shape of (n, 1, h, w). - Defaults to None. - - Returns: - [type]: [description] - """ - y_diff = super().forward( - pred[:, :, :-1, :], pred[:, :, 1:, :], weight=mask[:, :, :-1, :]) - x_diff = super().forward( - pred[:, :, :, :-1], pred[:, :, :, 1:], weight=mask[:, :, :, :-1]) - - loss = x_diff + y_diff - - return loss diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/utils.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/utils.py deleted file mode 100755 index 2f536d924..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/losses/utils.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import functools - -import torch.nn.functional as F - - -def reduce_loss(loss, reduction): - """Reduce loss as specified. - - Args: - loss (Tensor): Elementwise loss tensor. - reduction (str): Options are "none", "mean" and "sum". - - Returns: - Tensor: Reduced loss tensor. - """ - reduction_enum = F._Reduction.get_enum(reduction) - # none: 0, elementwise_mean:1, sum: 2 - if reduction_enum == 0: - return loss - if reduction_enum == 1: - return loss.mean() - - return loss.sum() - - -def mask_reduce_loss(loss, weight=None, reduction='mean', sample_wise=False): - """Apply element-wise weight and reduce loss. - - Args: - loss (Tensor): Element-wise loss. - weight (Tensor): Element-wise weights. Default: None. - reduction (str): Same as built-in losses of PyTorch. Options are - "none", "mean" and "sum". Default: 'mean'. - sample_wise (bool): Whether calculate the loss sample-wise. This - argument only takes effect when `reduction` is 'mean' and `weight` - (argument of `forward()`) is not None. It will first reduces loss - with 'mean' per-sample, and then it means over all the samples. - Default: False. - - Returns: - Tensor: Processed loss values. - """ - # if weight is specified, apply element-wise weight - if weight is not None: - assert weight.dim() == loss.dim() - assert weight.size(1) == 1 or weight.size(1) == loss.size(1) - loss = loss * weight - - # if weight is not specified or reduction is sum, just reduce the loss - if weight is None or reduction == 'sum': - loss = reduce_loss(loss, reduction) - # if reduction is mean, then compute mean over masked region - elif reduction == 'mean': - # expand weight from N1HW to NCHW - if weight.size(1) == 1: - weight = weight.expand_as(loss) - # small value to prevent division by zero - eps = 1e-12 - - # perform sample-wise mean - if sample_wise: - weight = weight.sum(dim=[1, 2, 3], keepdim=True) # NCHW to N111 - loss = (loss / (weight + eps)).sum() / weight.size(0) - # perform pixel-wise mean - else: - loss = loss.sum() / (weight.sum() + eps) - - return loss - - -def masked_loss(loss_func): - """Create a masked version of a given loss function. - - To use this decorator, the loss function must have the signature like - `loss_func(pred, target, **kwargs)`. The function only needs to compute - element-wise loss without any reduction. This decorator will add weight - and reduction arguments to the function. The decorated function will have - the signature like `loss_func(pred, target, weight=None, reduction='mean', - avg_factor=None, **kwargs)`. - - :Example: - - >>> import torch - >>> @masked_loss - >>> def l1_loss(pred, target): - >>> return (pred - target).abs() - - >>> pred = torch.Tensor([0, 2, 3]) - >>> target = torch.Tensor([1, 1, 1]) - >>> weight = torch.Tensor([1, 0, 1]) - - >>> l1_loss(pred, target) - tensor(1.3333) - >>> l1_loss(pred, target, weight) - tensor(1.5000) - >>> l1_loss(pred, target, reduction='none') - tensor([1., 1., 2.]) - >>> l1_loss(pred, target, weight, reduction='sum') - tensor(3.) - """ - - @functools.wraps(loss_func) - def wrapper(pred, - target, - weight=None, - reduction='mean', - sample_wise=False, - **kwargs): - # get element-wise loss - loss = loss_func(pred, target, **kwargs) - loss = mask_reduce_loss(loss, weight, reduction, sample_wise) - return loss - - return wrapper diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/registry.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/registry.py deleted file mode 100755 index 0a574b667..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/registry.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.cnn import MODELS as MMCV_MODELS -from mmcv.utils import Registry - -MODELS = Registry('model', parent=MMCV_MODELS) -BACKBONES = MODELS -COMPONENTS = MODELS -LOSSES = MODELS diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/__init__.py deleted file mode 100755 index 3cee3b6c8..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -from .basic_restorer import BasicRestorer -from .real_basicvsr import RealBasicVSR - diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/basic_restorer.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/basic_restorer.py deleted file mode 100755 index 5114b8e9a..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/basic_restorer.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -import os.path as osp - -import mmcv -from mmcv.runner import auto_fp16 - -from mmedit.core import psnr, ssim, tensor2img -from ..base import BaseModel -from ..builder import build_backbone, build_loss -from ..registry import MODELS - - -@MODELS.register_module() -class BasicRestorer(BaseModel): - """Basic model for image restoration. - - It must contain a generator that takes an image as inputs and outputs a - restored image. It also has a pixel-wise loss for training. - - The subclasses should overwrite the function `forward_train`, - `forward_test` and `train_step`. - - Args: - generator (dict): Config for the generator structure. - pixel_loss (dict): Config for pixel-wise loss. - train_cfg (dict): Config for training. Default: None. - test_cfg (dict): Config for testing. Default: None. - pretrained (str): Path for pretrained model. Default: None. - """ - allowed_metrics = {'PSNR': psnr, 'SSIM': ssim} - - def __init__(self, - generator, - pixel_loss, - train_cfg=None, - test_cfg=None, - pretrained=None): - super().__init__() - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - # support fp16 - self.fp16_enabled = False - - # generator - self.generator = build_backbone(generator) - self.init_weights(pretrained) - - # loss - self.pixel_loss = build_loss(pixel_loss) - - def init_weights(self, pretrained=None): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - """ - self.generator.init_weights(pretrained) - - @auto_fp16(apply_to=('lq', )) - def forward(self, lq, gt=None, test_mode=False, **kwargs): - """Forward function. - - Args: - lq (Tensor): Input lq images. - gt (Tensor): Ground-truth image. Default: None. - test_mode (bool): Whether in test mode or not. Default: False. - kwargs (dict): Other arguments. - """ - - if test_mode: - return self.forward_test(lq, gt, **kwargs) - - return self.forward_train(lq, gt) - - def forward_train(self, lq, gt): - """Training forward function. - - Args: - lq (Tensor): LQ Tensor with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). - - Returns: - Tensor: Output tensor. - """ - losses = dict() - output = self.generator(lq) - loss_pix = self.pixel_loss(output, gt) - losses['loss_pix'] = loss_pix - outputs = dict( - losses=losses, - num_samples=len(gt.data), - results=dict(lq=lq.cpu(), gt=gt.cpu(), output=output.cpu())) - return outputs - - def evaluate(self, output, gt): - """Evaluation function. - - Args: - output (Tensor): Model output with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). - - Returns: - dict: Evaluation results. - """ - crop_border = self.test_cfg.crop_border - - output = tensor2img(output) - gt = tensor2img(gt) - - eval_result = dict() - for metric in self.test_cfg.metrics: - eval_result[metric] = self.allowed_metrics[metric](output, gt, - crop_border) - return eval_result - - def forward_test(self, - lq, - gt=None, - meta=None, - save_image=False, - save_path=None, - iteration=None): - """Testing forward function. - - Args: - lq (Tensor): LQ Tensor with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). Default: None. - save_image (bool): Whether to save image. Default: False. - save_path (str): Path to save image. Default: None. - iteration (int): Iteration for the saving image name. - Default: None. - - Returns: - dict: Output results. - """ - output = self.generator(lq) - if self.test_cfg is not None and self.test_cfg.get('metrics', None): - assert gt is not None, ( - 'evaluation with metrics must have gt images.') - results = dict(eval_result=self.evaluate(output, gt)) - else: - results = dict(lq=lq.cpu(), output=output.cpu()) - if gt is not None: - results['gt'] = gt.cpu() - - # save image - if save_image: - lq_path = meta[0]['lq_path'] - folder_name = osp.splitext(osp.basename(lq_path))[0] - if isinstance(iteration, numbers.Number): - save_path = osp.join(save_path, folder_name, - f'{folder_name}-{iteration + 1:06d}.png') - elif iteration is None: - save_path = osp.join(save_path, f'{folder_name}.png') - else: - raise ValueError('iteration should be number or None, ' - f'but got {type(iteration)}') - mmcv.imwrite(tensor2img(output), save_path) - - return results - - def forward_dummy(self, img): - """Used for computing network FLOPs. - - Args: - img (Tensor): Input image. - - Returns: - Tensor: Output image. - """ - out = self.generator(img) - return out - - def train_step(self, data_batch, optimizer): - """Train step. - - Args: - data_batch (dict): A batch of data. - optimizer (obj): Optimizer. - - Returns: - dict: Returned output. - """ - outputs = self(**data_batch, test_mode=False) - loss, log_vars = self.parse_losses(outputs.pop('losses')) - - # optimize - optimizer['generator'].zero_grad() - loss.backward() - optimizer['generator'].step() - - outputs.update({'log_vars': log_vars}) - return outputs - - def val_step(self, data_batch, **kwargs): - """Validation step. - - Args: - data_batch (dict): A batch of data. - kwargs (dict): Other arguments for ``val_step``. - - Returns: - dict: Returned output. - """ - output = self.forward_test(**data_batch, **kwargs) - return output diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/real_basicvsr.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/real_basicvsr.py deleted file mode 100755 index 9e4bca931..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/real_basicvsr.py +++ /dev/null @@ -1,239 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch.nn.functional as F -from mmcv.parallel import is_module_wrapper - -from ..builder import build_loss -from ..common import set_requires_grad -from ..registry import MODELS -from .real_esrgan import RealESRGAN -from os import path as osp -import numbers -import mmcv -from mmedit.core import tensor2img - - -@MODELS.register_module() -class RealBasicVSR(RealESRGAN): - """RealBasicVSR model for real-world video super-resolution. - - Ref: - Investigating Tradeoffs in Real-World Video Super-Resolution, arXiv - - Args: - generator (dict): Config for the generator. - discriminator (dict, optional): Config for the discriminator. - Default: None. - gan_loss (dict, optional): Config for the gan loss. - Note that the loss weight in gan loss is only for the generator. - pixel_loss (dict, optional): Config for the pixel loss. Default: None. - cleaning_loss (dict, optional): Config for the image cleaning loss. - Default: None. - perceptual_loss (dict, optional): Config for the perceptual loss. - Default: None. - is_use_sharpened_gt_in_pixel (bool, optional): Whether to use the image - sharpened by unsharp masking as the GT for pixel loss. - Default: False. - is_use_sharpened_gt_in_percep (bool, optional): Whether to use the - image sharpened by unsharp masking as the GT for perceptual loss. - Default: False. - is_use_sharpened_gt_in_gan (bool, optional): Whether to use the - image sharpened by unsharp masking as the GT for adversarial loss. - Default: False. - is_use_ema (bool, optional): When to apply exponential moving average - on the network weights. Default: True. - train_cfg (dict): Config for training. Default: None. - You may change the training of gan by setting: - `disc_steps`: how many discriminator updates after one generate - update; - `disc_init_steps`: how many discriminator updates at the start of - the training. - These two keys are useful when training with WGAN. - test_cfg (dict): Config for testing. Default: None. - pretrained (str): Path for pretrained model. Default: None. - """ - - def __init__(self, - generator, - discriminator=None, - gan_loss=None, - pixel_loss=None, - cleaning_loss=None, - perceptual_loss=None, - is_use_sharpened_gt_in_pixel=False, - is_use_sharpened_gt_in_percep=False, - is_use_sharpened_gt_in_gan=False, - is_use_ema=True, - train_cfg=None, - test_cfg=None, - pretrained=None): - - super().__init__(generator, discriminator, gan_loss, pixel_loss, - perceptual_loss, is_use_sharpened_gt_in_pixel, - is_use_sharpened_gt_in_percep, - is_use_sharpened_gt_in_gan, is_use_ema, train_cfg, - test_cfg, pretrained) - - self.cleaning_loss = build_loss( - cleaning_loss) if cleaning_loss else None - - def train_step(self, data_batch, optimizer): - """Train step. - - Args: - data_batch (dict): A batch of data. - optimizer (obj): Optimizer. - - Returns: - dict: Returned output. - """ - - # during initialization, load weights from the ema model - if (self.step_counter == self.start_iter - and self.generator_ema is not None): - if is_module_wrapper(self.generator): - self.generator.module.load_state_dict( - self.generator_ema.module.state_dict()) - else: - self.generator.load_state_dict(self.generator_ema.state_dict()) - - # data - lq = data_batch['lq'] - gt = data_batch['gt'] - - gt_pixel, gt_percep, gt_gan = gt.clone(), gt.clone(), gt.clone() - if self.is_use_sharpened_gt_in_pixel: - gt_pixel = data_batch['gt_unsharp'] - if self.is_use_sharpened_gt_in_percep: - gt_percep = data_batch['gt_unsharp'] - if self.is_use_sharpened_gt_in_gan: - gt_gan = data_batch['gt_unsharp'] - - if self.cleaning_loss: - n, t, c, h, w = gt.size() - gt_clean = gt_pixel.view(-1, c, h, w) - gt_clean = F.interpolate(gt_clean, scale_factor=0.25, mode='area') - gt_clean = gt_clean.view(n, t, c, h // 4, w // 4) - - # generator - fake_g_output, fake_g_lq = self.generator(lq, return_lqs=True) - losses = dict() - log_vars = dict() - - # reshape: (n, t, c, h, w) -> (n*t, c, h, w) - c, h, w = gt.shape[2:] - gt_pixel = gt_pixel.view(-1, c, h, w) - gt_percep = gt_percep.view(-1, c, h, w) - gt_gan = gt_gan.view(-1, c, h, w) - fake_g_output = fake_g_output.view(-1, c, h, w) - - # no updates to discriminator parameters - if self.gan_loss: - set_requires_grad(self.discriminator, False) - - if (self.step_counter % self.disc_steps == 0 - and self.step_counter >= self.disc_init_steps): - if self.pixel_loss: - losses['loss_pix'] = self.pixel_loss(fake_g_output, gt_pixel) - if self.cleaning_loss: - losses['loss_clean'] = self.cleaning_loss(fake_g_lq, gt_clean) - if self.perceptual_loss: - loss_percep, loss_style = self.perceptual_loss( - fake_g_output, gt_percep) - if loss_percep is not None: - losses['loss_perceptual'] = loss_percep - if loss_style is not None: - losses['loss_style'] = loss_style - - # gan loss for generator - if self.gan_loss: - fake_g_pred = self.discriminator(fake_g_output) - losses['loss_gan'] = self.gan_loss( - fake_g_pred, target_is_real=True, is_disc=False) - - # parse loss - loss_g, log_vars_g = self.parse_losses(losses) - log_vars.update(log_vars_g) - - # optimize - optimizer['generator'].zero_grad() - loss_g.backward() - optimizer['generator'].step() - - # discriminator - if self.gan_loss: - set_requires_grad(self.discriminator, True) - # real - real_d_pred = self.discriminator(gt_gan) - loss_d_real = self.gan_loss( - real_d_pred, target_is_real=True, is_disc=True) - loss_d, log_vars_d = self.parse_losses( - dict(loss_d_real=loss_d_real)) - optimizer['discriminator'].zero_grad() - loss_d.backward() - log_vars.update(log_vars_d) - - # fake - fake_d_pred = self.discriminator(fake_g_output.detach()) - loss_d_fake = self.gan_loss( - fake_d_pred, target_is_real=False, is_disc=True) - loss_d, log_vars_d = self.parse_losses( - dict(loss_d_fake=loss_d_fake)) - loss_d.backward() - log_vars.update(log_vars_d) - - optimizer['discriminator'].step() - - self.step_counter += 1 - - log_vars.pop('loss') # remove the unnecessary 'loss' - outputs = dict( - log_vars=log_vars, - num_samples=len(gt.data), - results=dict(lq=lq.cpu(), gt=gt.cpu(), output=fake_g_output.cpu())) - - return outputs - - def forward_test(self, - lq, - gt=None, - meta=None, - save_image=False, - save_path=None, - iteration=None): - """Testing forward function. - - Args: - lq (Tensor): LQ Tensor with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). Default: None. - save_image (bool): Whether to save image. Default: False. - save_path (str): Path to save image. Default: None. - iteration (int): Iteration for the saving image name. - Default: None. - - Returns: - dict: Output results. - """ - _model = self.generator_ema if self.is_use_ema else self.generator - output = _model(lq) - - if self.test_cfg is not None and self.test_cfg.get( - 'metrics', None) and gt is not None: - results = dict(eval_result=self.evaluate(output, gt)) - else: - results = dict(lq=lq.cpu(), output=output.cpu()) - - # save image - if save_image: - lq_path = meta[0]['lq_path'] - folder_name = osp.splitext(osp.basename(lq_path[0]))[0] - if isinstance(iteration, numbers.Number): - save_path = osp.join(save_path, folder_name, - f'{folder_name}-{iteration + 1:06d}.png') - elif iteration is None: - save_path = osp.join(save_path, f'{folder_name}.png') - else: - raise ValueError('iteration should be number or None, ' - f'but got {type(iteration)}') - mmcv.imwrite(tensor2img(output), save_path) - - return results diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/real_esrgan.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/real_esrgan.py deleted file mode 100755 index 2f166815d..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/real_esrgan.py +++ /dev/null @@ -1,234 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import numbers -import os.path as osp -from copy import deepcopy - -import mmcv -import torch -from mmcv.parallel import is_module_wrapper - -from mmedit.core import tensor2img -from ..common import set_requires_grad -from ..registry import MODELS -from .srgan import SRGAN - - -@MODELS.register_module() -class RealESRGAN(SRGAN): - """Real-ESRGAN model for single image super-resolution. - - Ref: - Real-ESRGAN: Training Real-World Blind Super-Resolution with Pure - Synthetic Data, 2021. - - Args: - generator (dict): Config for the generator. - discriminator (dict, optional): Config for the discriminator. - Default: None. - gan_loss (dict, optional): Config for the gan loss. - Note that the loss weight in gan loss is only for the generator. - pixel_loss (dict, optional): Config for the pixel loss. Default: None. - perceptual_loss (dict, optional): Config for the perceptual loss. - Default: None. - is_use_sharpened_gt_in_pixel (bool, optional): Whether to use the image - sharpened by unsharp masking as the GT for pixel loss. - Default: False. - is_use_sharpened_gt_in_percep (bool, optional): Whether to use the - image sharpened by unsharp masking as the GT for perceptual loss. - Default: False. - is_use_sharpened_gt_in_gan (bool, optional): Whether to use the - image sharpened by unsharp masking as the GT for adversarial loss. - Default: False. - is_use_ema (bool, optional): When to apply exponential moving average - on the network weights. Default: True. - train_cfg (dict): Config for training. Default: None. - You may change the training of gan by setting: - `disc_steps`: how many discriminator updates after one generate - update; - `disc_init_steps`: how many discriminator updates at the start of - the training. - These two keys are useful when training with WGAN. - test_cfg (dict): Config for testing. Default: None. - pretrained (str): Path for pretrained model. Default: None. - """ - - def __init__(self, - generator, - discriminator=None, - gan_loss=None, - pixel_loss=None, - perceptual_loss=None, - is_use_sharpened_gt_in_pixel=False, - is_use_sharpened_gt_in_percep=False, - is_use_sharpened_gt_in_gan=False, - is_use_ema=True, - train_cfg=None, - test_cfg=None, - pretrained=None): - - super().__init__(generator, discriminator, gan_loss, pixel_loss, - perceptual_loss, train_cfg, test_cfg, pretrained) - - self.is_use_sharpened_gt_in_pixel = is_use_sharpened_gt_in_pixel - self.is_use_sharpened_gt_in_percep = is_use_sharpened_gt_in_percep - self.is_use_sharpened_gt_in_gan = is_use_sharpened_gt_in_gan - - self.is_use_ema = is_use_ema - if is_use_ema: - self.generator_ema = deepcopy(self.generator) - else: - self.generator_ema = None - - del self.step_counter - self.register_buffer('step_counter', torch.zeros(1)) - - if train_cfg is not None: # used for initializing from ema model - self.start_iter = train_cfg.get('start_iter', -1) - else: - self.start_iter = -1 - - def train_step(self, data_batch, optimizer): - """Train step. - - Args: - data_batch (dict): A batch of data. - optimizer (obj): Optimizer. - - Returns: - dict: Returned output. - """ - # during initialization, load weights from the ema model - if (self.step_counter == self.start_iter - and self.generator_ema is not None): - if is_module_wrapper(self.generator): - self.generator.module.load_state_dict( - self.generator_ema.module.state_dict()) - else: - self.generator.load_state_dict(self.generator_ema.state_dict()) - - # data - lq = data_batch['lq'] - gt = data_batch['gt'] - - gt_pixel, gt_percep, gt_gan = gt.clone(), gt.clone(), gt.clone() - if self.is_use_sharpened_gt_in_pixel: - gt_pixel = data_batch['gt_unsharp'] - if self.is_use_sharpened_gt_in_percep: - gt_percep = data_batch['gt_unsharp'] - if self.is_use_sharpened_gt_in_gan: - gt_gan = data_batch['gt_unsharp'] - - # generator - fake_g_output = self.generator(lq) - - losses = dict() - log_vars = dict() - - # no updates to discriminator parameters. - if self.gan_loss: - set_requires_grad(self.discriminator, False) - - if (self.step_counter % self.disc_steps == 0 - and self.step_counter >= self.disc_init_steps): - if self.pixel_loss: - losses['loss_pix'] = self.pixel_loss(fake_g_output, gt_pixel) - if self.perceptual_loss: - loss_percep, loss_style = self.perceptual_loss( - fake_g_output, gt_percep) - if loss_percep is not None: - losses['loss_perceptual'] = loss_percep - if loss_style is not None: - losses['loss_style'] = loss_style - - # gan loss for generator - if self.gan_loss: - fake_g_pred = self.discriminator(fake_g_output) - losses['loss_gan'] = self.gan_loss( - fake_g_pred, target_is_real=True, is_disc=False) - - # parse loss - loss_g, log_vars_g = self.parse_losses(losses) - log_vars.update(log_vars_g) - - # optimize - optimizer['generator'].zero_grad() - loss_g.backward() - optimizer['generator'].step() - - # discriminator - if self.gan_loss: - set_requires_grad(self.discriminator, True) - # real - real_d_pred = self.discriminator(gt_gan) - loss_d_real = self.gan_loss( - real_d_pred, target_is_real=True, is_disc=True) - loss_d, log_vars_d = self.parse_losses( - dict(loss_d_real=loss_d_real)) - optimizer['discriminator'].zero_grad() - loss_d.backward() - log_vars.update(log_vars_d) - # fake - fake_d_pred = self.discriminator(fake_g_output.detach()) - loss_d_fake = self.gan_loss( - fake_d_pred, target_is_real=False, is_disc=True) - loss_d, log_vars_d = self.parse_losses( - dict(loss_d_fake=loss_d_fake)) - loss_d.backward() - log_vars.update(log_vars_d) - - optimizer['discriminator'].step() - - self.step_counter += 1 - - log_vars.pop('loss') # remove the unnecessary 'loss' - outputs = dict( - log_vars=log_vars, - num_samples=len(gt.data), - results=dict(lq=lq.cpu(), gt=gt.cpu(), output=fake_g_output.cpu())) - - return outputs - - def forward_test(self, - lq, - gt=None, - meta=None, - save_image=False, - save_path=None, - iteration=None): - """Testing forward function. - - Args: - lq (Tensor): LQ Tensor with shape (n, c, h, w). - gt (Tensor): GT Tensor with shape (n, c, h, w). Default: None. - save_image (bool): Whether to save image. Default: False. - save_path (str): Path to save image. Default: None. - iteration (int): Iteration for the saving image name. - Default: None. - - Returns: - dict: Output results. - """ - _model = self.generator_ema if self.is_use_ema else self.generator - output = _model(lq) - - if self.test_cfg is not None and self.test_cfg.get( - 'metrics', None) and gt is not None: - results = dict(eval_result=self.evaluate(output, gt)) - else: - results = dict(lq=lq.cpu(), output=output.cpu()) - - # save image - if save_image: - lq_path = meta[0]['lq_path'] - folder_name = osp.splitext(osp.basename(lq_path))[0] - if isinstance(iteration, numbers.Number): - save_path = osp.join(save_path, folder_name, - f'{folder_name}-{iteration + 1:06d}.png') - elif iteration is None: - save_path = osp.join(save_path, f'{folder_name}.png') - else: - raise ValueError('iteration should be number or None, ' - f'but got {type(iteration)}') - mmcv.imwrite(tensor2img(output), save_path) - - return results diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/srgan.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/srgan.py deleted file mode 100755 index a2724bd53..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/models/restorers/srgan.py +++ /dev/null @@ -1,176 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.runner import auto_fp16 - -from ..builder import build_backbone, build_component, build_loss -from ..common import set_requires_grad -from ..registry import MODELS -from .basic_restorer import BasicRestorer - - -@MODELS.register_module() -class SRGAN(BasicRestorer): - """SRGAN model for single image super-resolution. - - Ref: - Photo-Realistic Single Image Super-Resolution Using a Generative - Adversarial Network. - - Args: - generator (dict): Config for the generator. - discriminator (dict): Config for the discriminator. Default: None. - gan_loss (dict): Config for the gan loss. - Note that the loss weight in gan loss is only for the generator. - pixel_loss (dict): Config for the pixel loss. Default: None. - perceptual_loss (dict): Config for the perceptual loss. Default: None. - train_cfg (dict): Config for training. Default: None. - You may change the training of gan by setting: - `disc_steps`: how many discriminator updates after one generate - update; - `disc_init_steps`: how many discriminator updates at the start of - the training. - These two keys are useful when training with WGAN. - test_cfg (dict): Config for testing. Default: None. - pretrained (str): Path for pretrained model. Default: None. - """ - - def __init__(self, - generator, - discriminator=None, - gan_loss=None, - pixel_loss=None, - perceptual_loss=None, - train_cfg=None, - test_cfg=None, - pretrained=None): - super(BasicRestorer, self).__init__() - - self.train_cfg = train_cfg - self.test_cfg = test_cfg - - # generator - self.generator = build_backbone(generator) - # discriminator - self.discriminator = build_component( - discriminator) if discriminator else None - - # support fp16 - self.fp16_enabled = False - - # loss - self.gan_loss = build_loss(gan_loss) if gan_loss else None - self.pixel_loss = build_loss(pixel_loss) if pixel_loss else None - self.perceptual_loss = build_loss( - perceptual_loss) if perceptual_loss else None - - self.disc_steps = 1 if self.train_cfg is None else self.train_cfg.get( - 'disc_steps', 1) - self.disc_init_steps = (0 if self.train_cfg is None else - self.train_cfg.get('disc_init_steps', 0)) - self.step_counter = 0 # counting training steps - - self.init_weights(pretrained) - - def init_weights(self, pretrained=None): - """Init weights for models. - - Args: - pretrained (str, optional): Path for pretrained weights. If given - None, pretrained weights will not be loaded. Defaults to None. - """ - self.generator.init_weights(pretrained=pretrained) - if self.discriminator: - self.discriminator.init_weights(pretrained=pretrained) - - @auto_fp16(apply_to=('lq', )) - def forward(self, lq, gt=None, test_mode=False, **kwargs): - """Forward function. - - Args: - lq (Tensor): Input lq images. - gt (Tensor): Ground-truth image. Default: None. - test_mode (bool): Whether in test mode or not. Default: False. - kwargs (dict): Other arguments. - """ - if test_mode: - return self.forward_test(lq, gt, **kwargs) - - raise ValueError( - 'SRGAN model does not support `forward_train` function.') - - def train_step(self, data_batch, optimizer): - """Train step. - - Args: - data_batch (dict): A batch of data. - optimizer (obj): Optimizer. - - Returns: - dict: Returned output. - """ - # data - lq = data_batch['lq'] - gt = data_batch['gt'] - - # generator - fake_g_output = self.generator(lq) - - losses = dict() - log_vars = dict() - - # no updates to discriminator parameters. - set_requires_grad(self.discriminator, False) - - if (self.step_counter % self.disc_steps == 0 - and self.step_counter >= self.disc_init_steps): - if self.pixel_loss: - losses['loss_pix'] = self.pixel_loss(fake_g_output, gt) - if self.perceptual_loss: - loss_percep, loss_style = self.perceptual_loss( - fake_g_output, gt) - if loss_percep is not None: - losses['loss_perceptual'] = loss_percep - if loss_style is not None: - losses['loss_style'] = loss_style - # gan loss for generator - fake_g_pred = self.discriminator(fake_g_output) - losses['loss_gan'] = self.gan_loss( - fake_g_pred, target_is_real=True, is_disc=False) - - # parse loss - loss_g, log_vars_g = self.parse_losses(losses) - log_vars.update(log_vars_g) - - # optimize - optimizer['generator'].zero_grad() - loss_g.backward() - optimizer['generator'].step() - - # discriminator - set_requires_grad(self.discriminator, True) - # real - real_d_pred = self.discriminator(gt) - loss_d_real = self.gan_loss( - real_d_pred, target_is_real=True, is_disc=True) - loss_d, log_vars_d = self.parse_losses(dict(loss_d_real=loss_d_real)) - optimizer['discriminator'].zero_grad() - loss_d.backward() - log_vars.update(log_vars_d) - # fake - fake_d_pred = self.discriminator(fake_g_output.detach()) - loss_d_fake = self.gan_loss( - fake_d_pred, target_is_real=False, is_disc=True) - loss_d, log_vars_d = self.parse_losses(dict(loss_d_fake=loss_d_fake)) - loss_d.backward() - log_vars.update(log_vars_d) - - optimizer['discriminator'].step() - - self.step_counter += 1 - - log_vars.pop('loss') # remove the unnecessary 'loss' - outputs = dict( - log_vars=log_vars, - num_samples=len(gt.data), - results=dict(lq=lq.cpu(), gt=gt.cpu(), output=fake_g_output.cpu())) - - return outputs diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/__init__.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/__init__.py deleted file mode 100755 index 0521b4a1c..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from .cli import modify_args -from .logger import get_root_logger -from .setup_env import setup_multi_processes - -__all__ = ['get_root_logger', 'setup_multi_processes', 'modify_args'] diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/cli.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/cli.py deleted file mode 100755 index a78b32712..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/cli.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import re -import sys -import warnings - - -def modify_args(): - for i, v in enumerate(sys.argv): - if i == 0: - assert v.endswith('.py') - elif re.match(r'--\w+_.*', v): - new_arg = v.replace('_', '-') - warnings.warn( - f'command line argument {v} is deprecated, ' - f'please use {new_arg} instead.', - category=DeprecationWarning, - ) - sys.argv[i] = new_arg diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/collect_env.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/collect_env.py deleted file mode 100755 index af22082c7..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/collect_env.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash - -import mmedit - - -def collect_env(): - """Collect the information of the running environments.""" - env_info = collect_base_env() - env_info['MMEditing'] = f'{mmedit.__version__}+{get_git_hash()[:7]}' - - return env_info - - -if __name__ == '__main__': - for name, val in collect_env().items(): - print('{}: {}'.format(name, val)) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/logger.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/logger.py deleted file mode 100755 index cdc340f18..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/logger.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import logging - -from mmcv.utils import get_logger - - -def get_root_logger(log_file=None, log_level=logging.INFO): - """Get the root logger. - - The logger will be initialized if it has not been initialized. By default a - StreamHandler will be added. If `log_file` is specified, a FileHandler will - also be added. The name of the root logger is the top-level package name, - e.g., "mmedit". - - Args: - log_file (str | None): The log filename. If specified, a FileHandler - will be added to the root logger. - log_level (int): The root logger level. Note that only the process of - rank 0 is affected, while other processes will set the level to - "Error" and be silent most of the time. - - Returns: - logging.Logger: The root logger. - """ - # root logger name: mmedit - logger = get_logger(__name__.split('.')[0], log_file, log_level) - return logger diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/setup_env.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/setup_env.py deleted file mode 100755 index 21def2f08..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/utils/setup_env.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import os -import platform -import warnings - -import cv2 -import torch.multiprocessing as mp - - -def setup_multi_processes(cfg): - """Setup multi-processing environment variables.""" - # set multi-process start method as `fork` to speed up the training - if platform.system() != 'Windows': - mp_start_method = cfg.get('mp_start_method', 'fork') - current_method = mp.get_start_method(allow_none=True) - if current_method is not None and current_method != mp_start_method: - warnings.warn( - f'Multi-processing start method `{mp_start_method}` is ' - f'different from the previous setting `{current_method}`.' - f'It will be force set to `{mp_start_method}`. You can change ' - f'this behavior by changing `mp_start_method` in your config.') - mp.set_start_method(mp_start_method, force=True) - - # disable opencv multithreading to avoid system being overloaded - opencv_num_threads = cfg.get('opencv_num_threads', 0) - cv2.setNumThreads(opencv_num_threads) - - # setup OMP threads - # This code is referred from https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py # noqa - if 'OMP_NUM_THREADS' not in os.environ and cfg.data.workers_per_gpu > 1: - omp_num_threads = 1 - warnings.warn( - f'Setting OMP_NUM_THREADS environment variable for each process ' - f'to be {omp_num_threads} in default, to avoid your system being ' - f'overloaded, please further tune the variable for optimal ' - f'performance in your application as needed.') - os.environ['OMP_NUM_THREADS'] = str(omp_num_threads) - - # setup MKL threads - if 'MKL_NUM_THREADS' not in os.environ and cfg.data.workers_per_gpu > 1: - mkl_num_threads = 1 - warnings.warn( - f'Setting MKL_NUM_THREADS environment variable for each process ' - f'to be {mkl_num_threads} in default, to avoid your system being ' - f'overloaded, please further tune the variable for optimal ' - f'performance in your application as needed.') - os.environ['MKL_NUM_THREADS'] = str(mkl_num_threads) diff --git a/cv/super_resolution/real_basicvsr/pytorch/mmedit/version.py b/cv/super_resolution/real_basicvsr/pytorch/mmedit/version.py deleted file mode 100755 index ffec9df73..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/mmedit/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) Open-MMLab. All rights reserved. - -__version__ = '0.15.0' - - -def parse_version_info(version_str): - ver_info = [] - for x in version_str.split('.'): - if x.isdigit(): - ver_info.append(int(x)) - elif x.find('rc') != -1: - patch_version = x.split('rc') - ver_info.append(int(patch_version[0])) - ver_info.append(f'rc{patch_version[1]}') - return tuple(ver_info) - - -version_info = parse_version_info(__version__) diff --git a/cv/super_resolution/real_basicvsr/pytorch/requirements.txt b/cv/super_resolution/real_basicvsr/pytorch/requirements.txt deleted file mode 100755 index ef11313e6..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -yapf -addict -av diff --git a/cv/super_resolution/real_basicvsr/pytorch/setup.py b/cv/super_resolution/real_basicvsr/pytorch/setup.py deleted file mode 100755 index 3cc9f7a46..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/setup.py +++ /dev/null @@ -1,354 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -import glob -import os -import re -from pkg_resources import DistributionNotFound, get_distribution -from setuptools import find_packages, setup - -EXT_TYPE = '' -try: - import torch - if torch.__version__ == 'parrots': - from parrots.utils.build_extension import BuildExtension - EXT_TYPE = 'parrots' - else: - from torch.utils.cpp_extension import BuildExtension - EXT_TYPE = 'pytorch' - cmd_class = {'build_ext': BuildExtension} -except ModuleNotFoundError: - cmd_class = {} - print('Skip building ext ops due to the absence of torch.') - - -def choose_requirement(primary, secondary): - """If some version of primary requirement installed, return primary, else - return secondary.""" - try: - name = re.split(r'[!<>=]', primary)[0] - get_distribution(name) - except DistributionNotFound: - return secondary - - return str(primary) - - -def get_version(): - version_file = 'mmcv/version.py' - with open(version_file, 'r', encoding='utf-8') as f: - exec(compile(f.read(), version_file, 'exec')) - version = locals()['__version__'] - local_version_identifier = os.environ.get('MMCV_LOCAL_VERSION_IDENTIFIER', '') - if local_version_identifier != '': - version += '+' + local_version_identifier - return version - - -def parse_requirements(fname='requirements/runtime.txt', with_version=True): - """Parse the package dependencies listed in a requirements file but strips - specific versioning information. - - Args: - fname (str): path to requirements file - with_version (bool, default=False): if True include version specs - - Returns: - List[str]: list of requirements items - - CommandLine: - python -c "import setup; print(setup.parse_requirements())" - """ - import sys - from os.path import exists - require_fpath = fname - - def parse_line(line): - """Parse information from a line in a requirements text file.""" - if line.startswith('-r '): - # Allow specifying requirements in other files - target = line.split(' ')[1] - for info in parse_require_file(target): - yield info - else: - info = {'line': line} - if line.startswith('-e '): - info['package'] = line.split('#egg=')[1] - else: - # Remove versioning from the package - pat = '(' + '|'.join(['>=', '==', '>']) + ')' - parts = re.split(pat, line, maxsplit=1) - parts = [p.strip() for p in parts] - - info['package'] = parts[0] - if len(parts) > 1: - op, rest = parts[1:] - if ';' in rest: - # Handle platform specific dependencies - # http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies - version, platform_deps = map(str.strip, - rest.split(';')) - info['platform_deps'] = platform_deps - else: - version = rest # NOQA - info['version'] = (op, version) - yield info - - def parse_require_file(fpath): - with open(fpath, 'r') as f: - for line in f.readlines(): - line = line.strip() - if line and not line.startswith('#'): - for info in parse_line(line): - yield info - - def gen_packages_items(): - if exists(require_fpath): - for info in parse_require_file(require_fpath): - parts = [info['package']] - if with_version and 'version' in info: - parts.extend(info['version']) - if not sys.version.startswith('3.4'): - # apparently package_deps are broken in 3.4 - platform_deps = info.get('platform_deps') - if platform_deps is not None: - parts.append(';' + platform_deps) - item = ''.join(parts) - yield item - - packages = list(gen_packages_items()) - return packages - - -install_requires = parse_requirements() - -try: - # OpenCV installed via conda. - import cv2 # NOQA: F401 - major, minor, *rest = cv2.__version__.split('.') - if int(major) < 3: - raise RuntimeError( - f'OpenCV >=3 is required but {cv2.__version__} is installed') -except ImportError: - # If first not installed install second package - CHOOSE_INSTALL_REQUIRES = [('opencv-python-headless>=3', - 'opencv-python>=3')] - for main, secondary in CHOOSE_INSTALL_REQUIRES: - install_requires.append(choose_requirement(main, secondary)) - - -def get_extensions(): - extensions = [] - - if os.getenv('MMCV_WITH_TRT', '0') != '0': - ext_name = 'mmcv._ext_trt' - from torch.utils.cpp_extension import include_paths, library_paths - library_dirs = [] - libraries = [] - include_dirs = [] - tensorrt_path = os.getenv('TENSORRT_DIR', '0') - tensorrt_lib_path = glob.glob( - os.path.join(tensorrt_path, 'targets', '*', 'lib'))[0] - library_dirs += [tensorrt_lib_path] - libraries += ['nvinfer', 'nvparsers', 'nvinfer_plugin'] - libraries += ['cudart'] - define_macros = [] - extra_compile_args = {'cxx': []} - - include_path = os.path.abspath('./mmcv/ops/csrc/common/cuda') - include_trt_path = os.path.abspath('./mmcv/ops/csrc/tensorrt') - include_dirs.append(include_path) - include_dirs.append(include_trt_path) - include_dirs.append(os.path.join(tensorrt_path, 'include')) - include_dirs += include_paths(cuda=True) - - op_files = glob.glob('./mmcv/ops/csrc/tensorrt/plugins/*') - define_macros += [('MMCV_WITH_CUDA', None)] - define_macros += [('MMCV_WITH_TRT', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - library_dirs += library_paths(cuda=True) - - from setuptools import Extension - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - language='c++', - library_dirs=library_dirs, - libraries=libraries) - extensions.append(ext_ops) - - if os.getenv('MMCV_WITH_OPS', '0') == '0': - return extensions - - if EXT_TYPE == 'parrots': - ext_name = 'mmcv._ext' - from parrots.utils.build_extension import Extension - # new parrots op impl do not use MMCV_USE_PARROTS - # define_macros = [('MMCV_USE_PARROTS', None)] - define_macros = [] - include_dirs = [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') +\ - glob.glob('./mmcv/ops/csrc/parrots/*.cpp') - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args = { - 'nvcc': [cuda_args] if cuda_args else [], - 'cxx': [], - } - if torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - extra_compile_args['nvcc'] += [ - '-D__CUDA_NO_HALF_OPERATORS__', - '-D__CUDA_NO_HALF_CONVERSIONS__', - '-D__CUDA_NO_HALF2_OPERATORS__', - ] - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - cuda=True, - pytorch=True) - extensions.append(ext_ops) - elif EXT_TYPE == 'pytorch': - ext_name = 'mmcv._ext' - from torch.utils.cpp_extension import CppExtension, CUDAExtension - - # prevent ninja from using too many resources - try: - import psutil - num_cpu = len(psutil.Process().cpu_affinity()) - cpu_use = max(4, num_cpu - 1) - except (ModuleNotFoundError, AttributeError): - cpu_use = 4 - - os.environ.setdefault('MAX_JOBS', str(cpu_use)) - define_macros = [] - extra_compile_args = {'cxx': []} - include_dirs = [] - - is_rocm_pytorch = False - try: - from torch.utils.cpp_extension import ROCM_HOME - is_rocm_pytorch = True if ((torch.version.hip is not None) and - (ROCM_HOME is not None)) else False - except ImportError: - pass - - project_dir = 'mmcv/ops/csrc/' - if is_rocm_pytorch: - from torch.utils.hipify import hipify_python - - hipify_python.hipify( - project_directory=project_dir, - output_directory=project_dir, - includes='mmcv/ops/csrc/*', - show_detailed=True, - is_pytorch_extension=True, - ) - define_macros += [('MMCV_WITH_CUDA', None)] - define_macros += [('HIP_DIFF', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/hip/*') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/hip')) - elif torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') + \ - glob.glob('./mmcv/ops/csrc/pytorch/cuda/*.cu') - extension = CUDAExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common/cuda')) - else: - print(f'Compiling {ext_name} without CUDA') - op_files = glob.glob('./mmcv/ops/csrc/pytorch/*.cpp') - extension = CppExtension - include_dirs.append(os.path.abspath('./mmcv/ops/csrc/common')) - - ext_ops = extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args) - extensions.append(ext_ops) - - if EXT_TYPE == 'pytorch' and os.getenv('MMCV_WITH_ORT', '0') != '0': - ext_name = 'mmcv._ext_ort' - from torch.utils.cpp_extension import library_paths, include_paths - import onnxruntime - library_dirs = [] - libraries = [] - include_dirs = [] - ort_path = os.getenv('ONNXRUNTIME_DIR', '0') - library_dirs += [os.path.join(ort_path, 'lib')] - libraries.append('onnxruntime') - define_macros = [] - extra_compile_args = {'cxx': []} - - include_path = os.path.abspath('./mmcv/ops/csrc/onnxruntime') - include_dirs.append(include_path) - include_dirs.append(os.path.join(ort_path, 'include')) - - op_files = glob.glob('./mmcv/ops/csrc/onnxruntime/cpu/*') - if onnxruntime.get_device() == 'GPU' or os.getenv('FORCE_CUDA', - '0') == '1': - define_macros += [('MMCV_WITH_CUDA', None)] - cuda_args = os.getenv('MMCV_CUDA_ARGS') - extra_compile_args['nvcc'] = [cuda_args] if cuda_args else [] - op_files += glob.glob('./mmcv/ops/csrc/onnxruntime/gpu/*') - include_dirs += include_paths(cuda=True) - library_dirs += library_paths(cuda=True) - else: - include_dirs += include_paths(cuda=False) - library_dirs += library_paths(cuda=False) - - from setuptools import Extension - ext_ops = Extension( - name=ext_name, - sources=op_files, - include_dirs=include_dirs, - define_macros=define_macros, - extra_compile_args=extra_compile_args, - language='c++', - library_dirs=library_dirs, - libraries=libraries) - extensions.append(ext_ops) - - return extensions - - -setup( - name='mmcv' if os.getenv('MMCV_WITH_OPS', '0') == '0' else 'mmcv-full', - version=get_version(), - description='OpenMMLab Computer Vision Foundation', - keywords='computer vision', - packages=find_packages(), - include_package_data=True, - classifiers=[ - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Topic :: Utilities', - ], - url='https://github.com/open-mmlab/mmcv', - author='MMCV Contributors', - author_email='openmmlab@gmail.com', - setup_requires=[], - tests_require=['pytest'], - install_requires=install_requires, - ext_modules=get_extensions(), - cmdclass=cmd_class, - zip_safe=False) diff --git a/cv/super_resolution/real_basicvsr/pytorch/test.py b/cv/super_resolution/real_basicvsr/pytorch/test.py deleted file mode 100644 index 026b4c8a7..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/test.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import argparse -import os - -import mmcv -import torch -from mmcv import Config, DictAction -from mmcv.parallel import MMDataParallel -from mmcv.runner import get_dist_info, init_dist, load_checkpoint - -from mmedit.apis import multi_gpu_test, set_random_seed, single_gpu_test -from mmedit.core.distributed_wrapper import DistributedDataParallelWrapper -from mmedit.datasets import build_dataloader, build_dataset -from mmedit.models import build_model -from mmedit.utils import setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser(description='mmediting tester') - parser.add_argument('config', help='test config file path') - parser.add_argument('checkpoint', help='checkpoint file') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument('--out', help='output result pickle file') - parser.add_argument( - '--gpu-collect', - action='store_true', - help='whether to use gpu to collect results') - parser.add_argument( - '--save-path', - default=None, - type=str, - help='path to store images and if not given, will not save image') - parser.add_argument('--tmpdir', help='tmp dir for writing some results') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - cfg.model.pretrained = None - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - - rank, _ = get_dist_info() - - # set random seeds - if args.seed is not None: - if rank == 0: - print('set random seed to', args.seed) - set_random_seed(args.seed, deterministic=args.deterministic) - - # build the dataloader - # TODO: support multiple images per gpu (only minor changes are needed) - dataset = build_dataset(cfg.data.test) - - loader_cfg = { - **dict((k, cfg.data[k]) for k in ['workers_per_gpu'] if k in cfg.data), - **dict( - samples_per_gpu=1, - drop_last=False, - shuffle=False, - dist=distributed), - **cfg.data.get('test_dataloader', {}) - } - - data_loader = build_dataloader(dataset, **loader_cfg) - - # build the model and load checkpoint - model = build_model(cfg.model, train_cfg=None, test_cfg=cfg.test_cfg) - - args.save_image = args.save_path is not None - empty_cache = cfg.get('empty_cache', False) - if not distributed: - _ = load_checkpoint(model, args.checkpoint, map_location='cpu') - model = MMDataParallel(model, device_ids=[0]) - outputs = single_gpu_test( - model, - data_loader, - save_path=args.save_path, - save_image=args.save_image) - else: - find_unused_parameters = cfg.get('find_unused_parameters', False) - model = DistributedDataParallelWrapper( - model, - device_ids=[torch.cuda.current_device()], - broadcast_buffers=False, - find_unused_parameters=find_unused_parameters) - - device_id = torch.cuda.current_device() - _ = load_checkpoint( - model, - args.checkpoint, - map_location=lambda storage, loc: storage.cuda(device_id)) - outputs = multi_gpu_test( - model, - data_loader, - args.tmpdir, - args.gpu_collect, - save_path=args.save_path, - save_image=args.save_image, - empty_cache=empty_cache) - - if rank == 0 and 'eval_result' in outputs[0]: - print('') - # print metrics - stats = dataset.evaluate(outputs) - for stat in stats: - print('Eval-{}: {}'.format(stat, stats[stat])) - - # save result pickle - if args.out: - print('writing results to {}'.format(args.out)) - mmcv.dump(outputs, args.out) - - -if __name__ == '__main__': - main() diff --git a/cv/super_resolution/real_basicvsr/pytorch/train.py b/cv/super_resolution/real_basicvsr/pytorch/train.py deleted file mode 100755 index d85aabcad..000000000 --- a/cv/super_resolution/real_basicvsr/pytorch/train.py +++ /dev/null @@ -1,172 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. - -import argparse -import copy -import os -import os.path as osp -import time - -import mmcv -import torch -import torch.distributed as dist -from mmcv import Config, DictAction -from mmcv.runner import init_dist - -from mmedit import __version__ -from mmedit.apis import init_random_seed, set_random_seed, train_model -from mmedit.datasets import build_dataset -from mmedit.models import build_model -from mmedit.utils import collect_env, get_root_logger, setup_multi_processes - - -def parse_args(): - parser = argparse.ArgumentParser(description='Train an editor') - parser.add_argument('config', help='train config file path') - parser.add_argument('--work-dir', help='the dir to save logs and models') - parser.add_argument( - '--resume-from', help='the checkpoint file to resume from') - parser.add_argument( - '--no-validate', - action='store_true', - help='whether not to evaluate the checkpoint during training') - parser.add_argument( - '--gpus', - type=int, - default=1, - help='number of gpus to use ' - '(only applicable to non-distributed training)') - parser.add_argument('--seed', type=int, default=None, help='random seed') - parser.add_argument( - '--diff_seed', - action='store_true', - help='Whether or not set different seeds for different ranks') - parser.add_argument( - '--deterministic', - action='store_true', - help='whether to set deterministic options for CUDNN backend.') - parser.add_argument( - '--cfg-options', - nargs='+', - action=DictAction, - help='override some settings in the used config, the key-value pair ' - 'in xxx=yyy format will be merged into config file. If the value to ' - 'be overwritten is a list, it should be like key="[a,b]" or key=a,b ' - 'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" ' - 'Note that the quotation marks are necessary and that no white space ' - 'is allowed.') - parser.add_argument( - '--launcher', - choices=['none', 'pytorch', 'slurm', 'mpi'], - default='none', - help='job launcher') - parser.add_argument('--local_rank', type=int, default=0) - parser.add_argument( - '--autoscale-lr', - action='store_true', - help='automatically scale lr with the number of gpus') - args = parser.parse_args() - if 'LOCAL_RANK' not in os.environ: - os.environ['LOCAL_RANK'] = str(args.local_rank) - - return args - - -def main(): - args = parse_args() - - cfg = Config.fromfile(args.config) - - if args.cfg_options is not None: - cfg.merge_from_dict(args.cfg_options) - - # set multi-process settings - setup_multi_processes(cfg) - - # set cudnn_benchmark - if cfg.get('cudnn_benchmark', False): - torch.backends.cudnn.benchmark = True - # update configs according to CLI args - if args.work_dir is not None: - cfg.work_dir = args.work_dir - if args.resume_from is not None: - cfg.resume_from = args.resume_from - cfg.gpus = args.gpus - - if args.autoscale_lr: - # apply the linear scaling rule (https://arxiv.org/abs/1706.02677) - cfg.optimizer['lr'] = cfg.optimizer['lr'] * cfg.gpus / 8 - - # init distributed env first, since logger depends on the dist info. - if args.launcher == 'none': - distributed = False - else: - distributed = True - init_dist(args.launcher, **cfg.dist_params) - - # create work_dir - mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) - # init the logger before other steps - timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) - log_file = osp.join(cfg.work_dir, f'{timestamp}.log') - logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) - - # log env info - env_info_dict = collect_env.collect_env() - env_info = '\n'.join([f'{k}: {v}' for k, v in env_info_dict.items()]) - dash_line = '-' * 60 + '\n' - logger.info('Environment info:\n' + dash_line + env_info + '\n' + - dash_line) - - # log some basic info - logger.info('Distributed training: {}'.format(distributed)) - logger.info('mmedit Version: {}'.format(__version__)) - logger.info('Config:\n{}'.format(cfg.text)) - - # set random seeds - seed = init_random_seed(args.seed) - seed = seed + dist.get_rank() if args.diff_seed else seed - logger.info('Set random seed to {}, deterministic: {}'.format( - seed, args.deterministic)) - set_random_seed(seed, deterministic=args.deterministic) - cfg.seed = seed - - model = build_model( - cfg.model, train_cfg=cfg.train_cfg, test_cfg=cfg.test_cfg) - - datasets = [build_dataset(cfg.data.train)] - if len(cfg.workflow) == 2: - val_dataset = copy.deepcopy(cfg.data.val) - val_dataset.pipeline = cfg.data.train.pipeline - datasets.append(build_dataset(val_dataset)) - if cfg.checkpoint_config is not None: - # save version, config file content and class names in - # checkpoints as meta data - cfg.checkpoint_config.meta = dict( - mmedit_version=__version__, - config=cfg.text, - ) - - # meta information - meta = dict() - if cfg.get('exp_name', None) is None: - cfg['exp_name'] = osp.splitext(osp.basename(cfg.work_dir))[0] - meta['exp_name'] = cfg.exp_name - meta['mmedit Version'] = __version__ - meta['seed'] = seed - meta['env_info'] = env_info - - # add an attribute for visualization convenience - train_model( - model, - datasets, - cfg, - distributed=distributed, - validate=(not args.no_validate), - timestamp=timestamp, - meta=meta) - - -if __name__ == '__main__': - main() -- Gitee From 01e6a7b0188528f9b57593574f08ebe0c23dd7e6 Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Thu, 6 Mar 2025 14:42:16 +0800 Subject: [PATCH 15/15] update ttsr --- .../real_basicvsr/pytorch/README.md | 2 +- cv/super_resolution/ttsr/pytorch/.gitignore | 4 - cv/super_resolution/ttsr/pytorch/LICENSE | 21 -- cv/super_resolution/ttsr/pytorch/README.md | 40 ++- .../ttsr/pytorch/checkpoints/args.txt | 41 --- .../ttsr/pytorch/checkpoints/train.log | 13 - .../ttsr/pytorch/dataset/cufed.py | 175 ----------- .../ttsr/pytorch/dataset/dataloader.py | 35 --- cv/super_resolution/ttsr/pytorch/eval.sh | 10 - .../ttsr/pytorch/loss/discriminator.py | 62 ---- cv/super_resolution/ttsr/pytorch/loss/loss.py | 157 ---------- cv/super_resolution/ttsr/pytorch/main.py | 94 ------ cv/super_resolution/ttsr/pytorch/model/LTE.py | 45 --- .../ttsr/pytorch/model/MainNet.py | 282 ------------------ .../ttsr/pytorch/model/SearchTransfer.py | 50 ---- .../ttsr/pytorch/model/TTSR.py | 35 --- .../ttsr/pytorch/model/Vgg19.py | 31 -- cv/super_resolution/ttsr/pytorch/option.py | 113 ------- .../ttsr/pytorch/requirements.txt | 1 - cv/super_resolution/ttsr/pytorch/test.sh | 9 - .../ttsr/pytorch/test/demo/lr/0.png | Bin 21602 -> 0 bytes .../ttsr/pytorch/test/demo/ref/0.png | Bin 341314 -> 0 bytes cv/super_resolution/ttsr/pytorch/train.sh | 75 ----- cv/super_resolution/ttsr/pytorch/trainer.py | 231 -------------- cv/super_resolution/ttsr/pytorch/utils.py | 163 ---------- 25 files changed, 28 insertions(+), 1661 deletions(-) delete mode 100755 cv/super_resolution/ttsr/pytorch/.gitignore delete mode 100755 cv/super_resolution/ttsr/pytorch/LICENSE delete mode 100755 cv/super_resolution/ttsr/pytorch/checkpoints/args.txt delete mode 100755 cv/super_resolution/ttsr/pytorch/checkpoints/train.log delete mode 100755 cv/super_resolution/ttsr/pytorch/dataset/cufed.py delete mode 100755 cv/super_resolution/ttsr/pytorch/dataset/dataloader.py delete mode 100755 cv/super_resolution/ttsr/pytorch/eval.sh delete mode 100755 cv/super_resolution/ttsr/pytorch/loss/discriminator.py delete mode 100755 cv/super_resolution/ttsr/pytorch/loss/loss.py delete mode 100755 cv/super_resolution/ttsr/pytorch/main.py delete mode 100755 cv/super_resolution/ttsr/pytorch/model/LTE.py delete mode 100755 cv/super_resolution/ttsr/pytorch/model/MainNet.py delete mode 100755 cv/super_resolution/ttsr/pytorch/model/SearchTransfer.py delete mode 100755 cv/super_resolution/ttsr/pytorch/model/TTSR.py delete mode 100755 cv/super_resolution/ttsr/pytorch/model/Vgg19.py delete mode 100755 cv/super_resolution/ttsr/pytorch/option.py delete mode 100755 cv/super_resolution/ttsr/pytorch/requirements.txt delete mode 100755 cv/super_resolution/ttsr/pytorch/test.sh delete mode 100755 cv/super_resolution/ttsr/pytorch/test/demo/lr/0.png delete mode 100755 cv/super_resolution/ttsr/pytorch/test/demo/ref/0.png delete mode 100755 cv/super_resolution/ttsr/pytorch/train.sh delete mode 100755 cv/super_resolution/ttsr/pytorch/trainer.py delete mode 100755 cv/super_resolution/ttsr/pytorch/utils.py diff --git a/cv/super_resolution/real_basicvsr/pytorch/README.md b/cv/super_resolution/real_basicvsr/pytorch/README.md index a0ac404dd..b97c6ddbe 100755 --- a/cv/super_resolution/real_basicvsr/pytorch/README.md +++ b/cv/super_resolution/real_basicvsr/pytorch/README.md @@ -43,7 +43,7 @@ python3 tools/train.py configs/real_basicvsr/realbasicvsr_wogan-c64b20-2x30x8_8x ### Mutiple GPUs on one machine ```shell sed -i 's/python /python3 /g' tools/dist_train.sh -bash tools/dist_train.sh configs/real_basicvsr/realbasicvsr_wogan-c64b20-2x30x8_8xb2-lr1e-4-300k_reds.py +bash tools/dist_train.sh configs/real_basicvsr/realbasicvsr_wogan-c64b20-2x30x8_8xb2-lr1e-4-300k_reds.py 8 ``` ## Reference diff --git a/cv/super_resolution/ttsr/pytorch/.gitignore b/cv/super_resolution/ttsr/pytorch/.gitignore deleted file mode 100755 index 3a0eee64b..000000000 --- a/cv/super_resolution/ttsr/pytorch/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -data/ -pretrained-weights/ -IMG/ -*pyc diff --git a/cv/super_resolution/ttsr/pytorch/LICENSE b/cv/super_resolution/ttsr/pytorch/LICENSE deleted file mode 100755 index 52ec40ed1..000000000 --- a/cv/super_resolution/ttsr/pytorch/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Multimedia Research - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/cv/super_resolution/ttsr/pytorch/README.md b/cv/super_resolution/ttsr/pytorch/README.md index 681b74351..bbcbb0ef6 100755 --- a/cv/super_resolution/ttsr/pytorch/README.md +++ b/cv/super_resolution/ttsr/pytorch/README.md @@ -8,7 +8,18 @@ We study on image super-resolution (SR), which aims to recover realistic texture ## Step 1: Installing packages ```bash -pip3 install -r requirements.txt +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-glx + +git clone https://github.com/open-mmlab/mmagic.git -b v1.2.0 --depth=1 +cd mmagic/ +pip3 install -e . -v + +sed -i 's/diffusers.models.unet_2d_condition/diffusers.models.unets.unet_2d_condition/g' mmagic/models/editors/vico/vico_utils.py +pip install albumentations ``` ## Step 2: Preparing datasets @@ -19,24 +30,27 @@ cd data # Download CUFED Dataset from [homepage](https://zzutk.github.io/SRNTT-Project-Page) # the folder would be like: data/CUFED/ -└── train -├ ├── input -├ └── ref -└── test - └── CUFED5 +└── input +├── ref +└── CUFED5 + +# Prepare vgg19-dcbb9e9d.pth, skip this if fast network +mkdir -p /root/.cache/torch/hub/checkpoints/ +wget https://download.pytorch.org/models/vgg19-dcbb9e9d.pth -O /root/.cache/torch/hub/checkpoints/vgg19-dcbb9e9d.pth ``` ## Step 3: Training -### Multiple GPUs on one machine - -```bash -CUDA_VISIBLE_DEVICES=${gpu_id_1,gpu_id_2,...} bash train.sh ${num_gpus} +### Training on single card +```shell +python3 tools/train.py configs/ttsr/ttsr-gan_x4c64b16_1xb9-500k_CUFED.py ``` -```bash -CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 bash train.sh 8 +### Mutiple GPUs on one machine +```shell +sed -i 's/python /python3 /g' tools/dist_train.sh +bash tools/dist_train.sh configs/ttsr/ttsr-gan_x4c64b16_1xb9-500k_CUFED.py 8 ``` ## Reference -https://github.com/open-mmlab/mmediting +[mmagic](https://github.com/open-mmlab/mmagic) \ No newline at end of file diff --git a/cv/super_resolution/ttsr/pytorch/checkpoints/args.txt b/cv/super_resolution/ttsr/pytorch/checkpoints/args.txt deleted file mode 100755 index 3f104f539..000000000 --- a/cv/super_resolution/ttsr/pytorch/checkpoints/args.txt +++ /dev/null @@ -1,41 +0,0 @@ - save_dir checkpoints - reset True - log_file_name train.log - logger_name TTSR - cpu False - num_gpu 4 - dataset CUFED - dataset_dir data/CUFED - num_workers 4 - num_res_blocks 16+16+8+4 - n_feats 64 - res_scale 1.0 - GAN_type WGAN_GP - GAN_k 2 - tpl_use_S False - tpl_type l2 - rec_w 1.0 - per_w 0.01 - tpl_w 0.01 - adv_w 0.001 - beta1 0.9 - beta2 0.999 - eps 1e-08 - lr_rate 0.0001 - lr_rate_dis 0.0001 - lr_rate_lte 1e-05 - decay 999999 - gamma 0.5 - batch_size 4 - train_crop_size 40 - num_init_epochs 0 - num_epochs 50 - print_every 100 - save_every 10 - val_every 10 - eval False - eval_save_results False - model_path None - test False - lr_path ./test/demo/lr/lr.png - ref_path ./test/demo/ref/ref.png diff --git a/cv/super_resolution/ttsr/pytorch/checkpoints/train.log b/cv/super_resolution/ttsr/pytorch/checkpoints/train.log deleted file mode 100755 index 6e2320c47..000000000 --- a/cv/super_resolution/ttsr/pytorch/checkpoints/train.log +++ /dev/null @@ -1,13 +0,0 @@ -[2022-08-16 11:45:30,596] - [trainer.py file line:75] - INFO: Current epoch learning rate: 1.000000e-04 -[2022-08-16 11:46:09,391] - [trainer.py file line:97] - INFO: training fps = 41.24274159862805 -[2022-08-16 11:46:09,392] - [trainer.py file line:102] - INFO: epoch: 1 batch: 100 -[2022-08-16 11:46:09,392] - [trainer.py file line:104] - INFO: rec_loss: 0.1996560842 -[2022-08-16 11:46:09,404] - [trainer.py file line:114] - INFO: per_loss: 0.0691360161 -[2022-08-16 11:46:09,408] - [trainer.py file line:121] - INFO: tpl_loss: 0.0240736995 -[2022-08-16 11:46:09,517] - [trainer.py file line:126] - INFO: adv_loss: -0.1518322527 -[2022-08-16 11:46:45,718] - [trainer.py file line:97] - INFO: training fps = 44.04433695749565 -[2022-08-16 11:46:45,719] - [trainer.py file line:102] - INFO: epoch: 1 batch: 200 -[2022-08-16 11:46:45,719] - [trainer.py file line:104] - INFO: rec_loss: 0.1592512131 -[2022-08-16 11:46:45,731] - [trainer.py file line:114] - INFO: per_loss: 0.0439612940 -[2022-08-16 11:46:45,735] - [trainer.py file line:121] - INFO: tpl_loss: 0.0166233946 -[2022-08-16 11:46:45,844] - [trainer.py file line:126] - INFO: adv_loss: 0.0254640970 diff --git a/cv/super_resolution/ttsr/pytorch/dataset/cufed.py b/cv/super_resolution/ttsr/pytorch/dataset/cufed.py deleted file mode 100755 index 2832041b5..000000000 --- a/cv/super_resolution/ttsr/pytorch/dataset/cufed.py +++ /dev/null @@ -1,175 +0,0 @@ -import os -from imageio import imread -from PIL import Image -import numpy as np -import glob -import random - -import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.utils.data import Dataset -from torchvision import transforms - - -# Ignore warnings -import warnings -warnings.filterwarnings("ignore") - - -class RandomRotate(object): - def __call__(self, sample): - k1 = np.random.randint(0, 4) - sample['LR'] = np.rot90(sample['LR'], k1).copy() - sample['HR'] = np.rot90(sample['HR'], k1).copy() - sample['LR_sr'] = np.rot90(sample['LR_sr'], k1).copy() - k2 = np.random.randint(0, 4) - sample['Ref'] = np.rot90(sample['Ref'], k2).copy() - sample['Ref_sr'] = np.rot90(sample['Ref_sr'], k2).copy() - return sample - - -class RandomFlip(object): - def __call__(self, sample): - if (np.random.randint(0, 2) == 1): - sample['LR'] = np.fliplr(sample['LR']).copy() - sample['HR'] = np.fliplr(sample['HR']).copy() - sample['LR_sr'] = np.fliplr(sample['LR_sr']).copy() - if (np.random.randint(0, 2) == 1): - sample['Ref'] = np.fliplr(sample['Ref']).copy() - sample['Ref_sr'] = np.fliplr(sample['Ref_sr']).copy() - if (np.random.randint(0, 2) == 1): - sample['LR'] = np.flipud(sample['LR']).copy() - sample['HR'] = np.flipud(sample['HR']).copy() - sample['LR_sr'] = np.flipud(sample['LR_sr']).copy() - if (np.random.randint(0, 2) == 1): - sample['Ref'] = np.flipud(sample['Ref']).copy() - sample['Ref_sr'] = np.flipud(sample['Ref_sr']).copy() - return sample - - -class ToTensor(object): - def __call__(self, sample): - LR, LR_sr, HR, Ref, Ref_sr = sample['LR'], sample['LR_sr'], sample['HR'], sample['Ref'], sample['Ref_sr'] - LR = LR.transpose((2,0,1)) - LR_sr = LR_sr.transpose((2,0,1)) - HR = HR.transpose((2,0,1)) - Ref = Ref.transpose((2,0,1)) - Ref_sr = Ref_sr.transpose((2,0,1)) - return {'LR': torch.from_numpy(LR).float(), - 'LR_sr': torch.from_numpy(LR_sr).float(), - 'HR': torch.from_numpy(HR).float(), - 'Ref': torch.from_numpy(Ref).float(), - 'Ref_sr': torch.from_numpy(Ref_sr).float()} - - -class TrainSet(Dataset): - def __init__(self, args, transform=transforms.Compose([RandomFlip(), RandomRotate(), ToTensor()]) ): - self.input_list = sorted([os.path.join(args.dataset_dir, 'train/input', name) for name in - os.listdir( os.path.join(args.dataset_dir, 'train/input') )]) - self.ref_list = sorted([os.path.join(args.dataset_dir, 'train/ref', name) for name in - os.listdir( os.path.join(args.dataset_dir, 'train/ref') )]) - self.transform = transform - - def __len__(self): - return len(self.input_list) - - def __getitem__(self, idx): - ### HR - HR = imread(self.input_list[idx]) - h,w = HR.shape[:2] - #HR = HR[:h//4*4, :w//4*4, :] - - ### LR and LR_sr - LR = np.array(Image.fromarray(HR).resize((w//4, h//4), Image.BICUBIC)) - LR_sr = np.array(Image.fromarray(LR).resize((w, h), Image.BICUBIC)) - - ### Ref and Ref_sr - Ref_sub = imread(self.ref_list[idx]) - h2, w2 = Ref_sub.shape[:2] - Ref_sr_sub = np.array(Image.fromarray(Ref_sub).resize((w2//4, h2//4), Image.BICUBIC)) - Ref_sr_sub = np.array(Image.fromarray(Ref_sr_sub).resize((w2, h2), Image.BICUBIC)) - - ### complete ref and ref_sr to the same size, to use batch_size > 1 - Ref = np.zeros((160, 160, 3)) - Ref_sr = np.zeros((160, 160, 3)) - Ref[:h2, :w2, :] = Ref_sub - Ref_sr[:h2, :w2, :] = Ref_sr_sub - - ### change type - LR = LR.astype(np.float32) - LR_sr = LR_sr.astype(np.float32) - HR = HR.astype(np.float32) - Ref = Ref.astype(np.float32) - Ref_sr = Ref_sr.astype(np.float32) - - ### rgb range to [-1, 1] - LR = LR / 127.5 - 1. - LR_sr = LR_sr / 127.5 - 1. - HR = HR / 127.5 - 1. - Ref = Ref / 127.5 - 1. - Ref_sr = Ref_sr / 127.5 - 1. - - sample = {'LR': LR, - 'LR_sr': LR_sr, - 'HR': HR, - 'Ref': Ref, - 'Ref_sr': Ref_sr} - - if self.transform: - sample = self.transform(sample) - return sample - - -class TestSet(Dataset): - def __init__(self, args, ref_level='1', transform=transforms.Compose([ToTensor()])): - self.input_list = sorted(glob.glob(os.path.join(args.dataset_dir, 'test/CUFED5', '*_0.png'))) - self.ref_list = sorted(glob.glob(os.path.join(args.dataset_dir, 'test/CUFED5', - '*_' + ref_level + '.png'))) - self.transform = transform - - def __len__(self): - return len(self.input_list) - - def __getitem__(self, idx): - ### HR - HR = imread(self.input_list[idx]) - h, w = HR.shape[:2] - h, w = h//4*4, w//4*4 - HR = HR[:h, :w, :] ### crop to the multiple of 4 - - ### LR and LR_sr - LR = np.array(Image.fromarray(HR).resize((w//4, h//4), Image.BICUBIC)) - LR_sr = np.array(Image.fromarray(LR).resize((w, h), Image.BICUBIC)) - - ### Ref and Ref_sr - Ref = imread(self.ref_list[idx]) - h2, w2 = Ref.shape[:2] - h2, w2 = h2//4*4, w2//4*4 - Ref = Ref[:h2, :w2, :] - Ref_sr = np.array(Image.fromarray(Ref).resize((w2//4, h2//4), Image.BICUBIC)) - Ref_sr = np.array(Image.fromarray(Ref_sr).resize((w2, h2), Image.BICUBIC)) - - ### change type - LR = LR.astype(np.float32) - LR_sr = LR_sr.astype(np.float32) - HR = HR.astype(np.float32) - Ref = Ref.astype(np.float32) - Ref_sr = Ref_sr.astype(np.float32) - - ### rgb range to [-1, 1] - LR = LR / 127.5 - 1. - LR_sr = LR_sr / 127.5 - 1. - HR = HR / 127.5 - 1. - Ref = Ref / 127.5 - 1. - Ref_sr = Ref_sr / 127.5 - 1. - - sample = {'LR': LR, - 'LR_sr': LR_sr, - 'HR': HR, - 'Ref': Ref, - 'Ref_sr': Ref_sr} - - if self.transform: - sample = self.transform(sample) - return sample \ No newline at end of file diff --git a/cv/super_resolution/ttsr/pytorch/dataset/dataloader.py b/cv/super_resolution/ttsr/pytorch/dataset/dataloader.py deleted file mode 100755 index 51d5a536b..000000000 --- a/cv/super_resolution/ttsr/pytorch/dataset/dataloader.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - - -from torch.utils.data import DataLoader -import torch -from importlib import import_module - - -def get_dataloader(args): - ### import module - m = import_module('dataset.' + args.dataset.lower()) - - if (args.dataset == 'CUFED'): - data_train = getattr(m, 'TrainSet')(args) - train_sampler = None - if ((not args.cpu) and (args.num_gpu > 1)): - train_sampler = torch.utils.data.distributed.DistributedSampler(data_train) - dataloader_train = DataLoader(data_train, batch_size=args.batch_size, sampler=train_sampler, num_workers=args.num_workers) - else: - dataloader_train = DataLoader(data_train, batch_size=args.batch_size, shuffle=True, num_workers=args.num_workers) - dataloader_test = {} - for i in range(5): - data_test = getattr(m, 'TestSet')(args=args, ref_level=str(i+1)) - test_sampler = None - if ((not args.cpu) and (args.num_gpu > 1)): - test_sampler = torch.utils.data.distributed.DistributedSampler(data_test) - dataloader_test[str(i+1)] = DataLoader(data_test, batch_size=1, sampler=test_sampler, num_workers=args.num_workers) - else: - dataloader_test[str(i+1)] = DataLoader(data_test, batch_size=1, shuffle=False, num_workers=args.num_workers) - dataloader = {'train': dataloader_train, 'test': dataloader_test} - - else: - raise SystemExit('Error: no such type of dataset!') - - return dataloader \ No newline at end of file diff --git a/cv/super_resolution/ttsr/pytorch/eval.sh b/cv/super_resolution/ttsr/pytorch/eval.sh deleted file mode 100755 index cad9e309c..000000000 --- a/cv/super_resolution/ttsr/pytorch/eval.sh +++ /dev/null @@ -1,10 +0,0 @@ -### evaluation -python main.py --save_dir ./eval/CUFED/TTSR \ - --reset True \ - --log_file_name eval.log \ - --eval True \ - --eval_save_results True \ - --num_workers 4 \ - --dataset CUFED \ - --dataset_dir /D_data/v-fuyang/CUFED/ \ - --model_path ./TTSR.pt \ No newline at end of file diff --git a/cv/super_resolution/ttsr/pytorch/loss/discriminator.py b/cv/super_resolution/ttsr/pytorch/loss/discriminator.py deleted file mode 100755 index e05b5302e..000000000 --- a/cv/super_resolution/ttsr/pytorch/loss/discriminator.py +++ /dev/null @@ -1,62 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F - - -def conv3x3(in_channels, out_channels, stride=1): - return nn.Conv2d(in_channels, out_channels, kernel_size=3, - stride=stride, padding=1, bias=True) - - -class Discriminator(nn.Module): - def __init__(self, in_size=160): - super(Discriminator, self).__init__() - self.conv1 = conv3x3(3, 32) - self.LReLU1 = nn.LeakyReLU(0.2) - self.conv2 = conv3x3(32, 32, 2) - self.LReLU2 = nn.LeakyReLU(0.2) - self.conv3 = conv3x3(32, 64) - self.LReLU3 = nn.LeakyReLU(0.2) - self.conv4 = conv3x3(64, 64, 2) - self.LReLU4 = nn.LeakyReLU(0.2) - self.conv5 = conv3x3(64, 128) - self.LReLU5 = nn.LeakyReLU(0.2) - self.conv6 = conv3x3(128, 128, 2) - self.LReLU6 = nn.LeakyReLU(0.2) - self.conv7 = conv3x3(128, 256) - self.LReLU7 = nn.LeakyReLU(0.2) - self.conv8 = conv3x3(256, 256, 2) - self.LReLU8 = nn.LeakyReLU(0.2) - self.conv9 = conv3x3(256, 512) - self.LReLU9 = nn.LeakyReLU(0.2) - self.conv10 = conv3x3(512, 512, 2) - self.LReLU10 = nn.LeakyReLU(0.2) - - self.fc1 = nn.Linear(in_size//32 * in_size//32 * 512, 1024) - self.LReLU11 = nn.LeakyReLU(0.2) - self.fc2 = nn.Linear(1024, 1) - - def forward(self, x): - x = self.LReLU1(self.conv1(x)) - x = self.LReLU2(self.conv2(x)) - x = self.LReLU3(self.conv3(x)) - x = self.LReLU4(self.conv4(x)) - x = self.LReLU5(self.conv5(x)) - x = self.LReLU6(self.conv6(x)) - x = self.LReLU7(self.conv7(x)) - x = self.LReLU8(self.conv8(x)) - x = self.LReLU9(self.conv9(x)) - x = self.LReLU10(self.conv10(x)) - - x = x.view(x.size(0), -1) - x = self.LReLU11(self.fc1(x)) - x = self.fc2(x) - - return x - - -if __name__ == '__main__': - model = discriminator() - x = torch.ones(1, 3, 160, 160) - out = model(x) - print (out.size()) \ No newline at end of file diff --git a/cv/super_resolution/ttsr/pytorch/loss/loss.py b/cv/super_resolution/ttsr/pytorch/loss/loss.py deleted file mode 100755 index f0f9d7aa5..000000000 --- a/cv/super_resolution/ttsr/pytorch/loss/loss.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - - -from loss import discriminator -import os -import torch -import torch.nn as nn -import torch.nn.functional as F -import torch.optim as optim - - -class ReconstructionLoss(nn.Module): - def __init__(self, type='l1'): - super(ReconstructionLoss, self).__init__() - if (type == 'l1'): - self.loss = nn.L1Loss() - elif (type == 'l2'): - self.loss = nn.MSELoss() - else: - raise SystemExit('Error: no such type of ReconstructionLoss!') - - def forward(self, sr, hr): - return self.loss(sr, hr) - - -class PerceptualLoss(nn.Module): - def __init__(self): - super(PerceptualLoss, self).__init__() - - def forward(self, sr_relu5_1, hr_relu5_1): - loss = F.mse_loss(sr_relu5_1, hr_relu5_1) - return loss - - -class TPerceptualLoss(nn.Module): - def __init__(self, use_S=True, type='l2'): - super(TPerceptualLoss, self).__init__() - self.use_S = use_S - self.type = type - - def gram_matrix(self, x): - b, ch, h, w = x.size() - f = x.view(b, ch, h*w) - f_T = f.transpose(1, 2) - G = f.bmm(f_T) / (h * w * ch) - return G - - def forward(self, map_lv3, map_lv2, map_lv1, S, T_lv3, T_lv2, T_lv1): - ### S.size(): [N, 1, h, w] - if (self.use_S): - S_lv3 = torch.sigmoid(S) - S_lv2 = torch.sigmoid(F.interpolate(S, size=(S.size(-2)*2, S.size(-1)*2), mode='bicubic')) - S_lv1 = torch.sigmoid(F.interpolate(S, size=(S.size(-2)*4, S.size(-1)*4), mode='bicubic')) - else: - S_lv3, S_lv2, S_lv1 = 1., 1., 1. - - if (self.type == 'l1'): - loss_texture = F.l1_loss(map_lv3 * S_lv3, T_lv3 * S_lv3) - loss_texture += F.l1_loss(map_lv2 * S_lv2, T_lv2 * S_lv2) - loss_texture += F.l1_loss(map_lv1 * S_lv1, T_lv1 * S_lv1) - loss_texture /= 3. - elif (self.type == 'l2'): - loss_texture = F.mse_loss(map_lv3 * S_lv3, T_lv3 * S_lv3) - loss_texture += F.mse_loss(map_lv2 * S_lv2, T_lv2 * S_lv2) - loss_texture += F.mse_loss(map_lv1 * S_lv1, T_lv1 * S_lv1) - loss_texture /= 3. - - return loss_texture - - -class AdversarialLoss(nn.Module): - def __init__(self, use_cpu=False, num_gpu=1, gan_type='WGAN_GP', gan_k=1, - lr_dis=1e-4, train_crop_size=40): - - super(AdversarialLoss, self).__init__() - self.gan_type = gan_type - self.gan_k = gan_k - self.device = torch.device('cpu' if use_cpu else 'cuda') - self.discriminator = discriminator.Discriminator(train_crop_size*4).to(self.device) - if (num_gpu > 1): - cur_gpu = int(os.environ['LOCAL_RANK']) - self.discriminator = nn.parallel.DistributedDataParallel(self.discriminator, device_ids=[cur_gpu]) - if (gan_type in ['WGAN_GP', 'GAN']): - self.optimizer = optim.Adam( - self.discriminator.parameters(), - betas=(0, 0.9), eps=1e-8, lr=lr_dis - ) - else: - raise SystemExit('Error: no such type of GAN!') - - self.bce_loss = torch.nn.BCELoss().to(self.device) - - def forward(self, fake, real): - fake_detach = fake.detach() - - for _ in range(self.gan_k): - self.optimizer.zero_grad() - d_fake = self.discriminator(fake_detach) - d_real = self.discriminator(real) - if (self.gan_type.find('WGAN') >= 0): - loss_d = (d_fake - d_real).mean() - if self.gan_type.find('GP') >= 0: - epsilon = torch.rand(real.size(0), 1, 1, 1).to(self.device) - epsilon = epsilon.expand(real.size()) - hat = fake_detach.mul(1 - epsilon) + real.mul(epsilon) - hat.requires_grad = True - d_hat = self.discriminator(hat) - gradients = torch.autograd.grad( - outputs=d_hat.sum(), inputs=hat, - retain_graph=True, create_graph=True, only_inputs=True - )[0] - gradients = gradients.view(gradients.size(0), -1) - gradient_norm = gradients.norm(2, dim=1) - gradient_penalty = 10 * gradient_norm.sub(1).pow(2).mean() - loss_d += gradient_penalty - - elif (self.gan_type == 'GAN'): - valid_score = torch.ones(real.size(0), 1).to(self.device) - fake_score = torch.zeros(real.size(0), 1).to(self.device) - real_loss = self.bce_loss(torch.sigmoid(d_real), valid_score) - fake_loss = self.bce_loss(torch.sigmoid(d_fake), fake_score) - loss_d = (real_loss + fake_loss) / 2. - - # Discriminator update - loss_d.backward() - self.optimizer.step() - - d_fake_for_g = self.discriminator(fake) - if (self.gan_type.find('WGAN') >= 0): - loss_g = -d_fake_for_g.mean() - elif (self.gan_type == 'GAN'): - loss_g = self.bce_loss(torch.sigmoid(d_fake_for_g), valid_score) - - # Generator loss - return loss_g - - def state_dict(self): - D_state_dict = self.discriminator.state_dict() - D_optim_state_dict = self.optimizer.state_dict() - return D_state_dict, D_optim_state_dict - - -def get_loss_dict(args): - loss = {} - if (abs(args.rec_w - 0) <= 1e-8): - raise SystemExit('NotImplementError: ReconstructionLoss must exist!') - else: - loss['rec_loss'] = ReconstructionLoss(type='l1') - if (abs(args.per_w - 0) > 1e-8): - loss['per_loss'] = PerceptualLoss() - if (abs(args.tpl_w - 0) > 1e-8): - loss['tpl_loss'] = TPerceptualLoss(use_S=args.tpl_use_S, type=args.tpl_type) - if (abs(args.adv_w - 0) > 1e-8): - loss['adv_loss'] = AdversarialLoss(use_cpu=args.cpu, num_gpu=args.num_gpu, - gan_type=args.GAN_type, gan_k=args.GAN_k, lr_dis=args.lr_rate_dis, - train_crop_size=args.train_crop_size) - return loss \ No newline at end of file diff --git a/cv/super_resolution/ttsr/pytorch/main.py b/cv/super_resolution/ttsr/pytorch/main.py deleted file mode 100755 index 01cf673b1..000000000 --- a/cv/super_resolution/ttsr/pytorch/main.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - - -from option import args -from utils import mkExpDir,Logger -from dataset import dataloader -from model import TTSR -from loss.loss import get_loss_dict -from trainer import Trainer - -import os -import torch -import torch.nn as nn -import warnings -warnings.filterwarnings('ignore') - -torch.backends.cudnn.benchmark = True - -def setup_for_distributed(is_master): - """ - This function disables printing when not in master process - """ - import builtins as __builtin__ - builtin_print = __builtin__.print - - def print(*args, **kwargs): - force = kwargs.pop('force', False) - if is_master or force: - builtin_print(*args, **kwargs) - - __builtin__.print = print - -def init_distributed_mode(args): - if ((not args.cpu) and (args.num_gpu > 1)) and 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: - env_rank = int(os.environ["RANK"]) - env_world_size = int(os.environ["WORLD_SIZE"]) - env_gpu = int(os.environ['LOCAL_RANK']) - else: - print('Not using distributed mode') - return - - dist_backend = "nccl" - print('| distributed init (rank {}) (size {})'.format(env_rank,env_world_size), flush=True) - torch.distributed.init_process_group(backend=dist_backend, init_method='env://', - world_size=env_world_size, rank=env_rank) - - torch.cuda.set_device(env_gpu) - torch.distributed.barrier() - setup_for_distributed(env_rank == 0) - - -if __name__ == '__main__': - init_distributed_mode(args) - ### make save_dir - if ((not args.cpu) and (args.num_gpu > 1)): - if int(os.environ["RANK"]) == 0: - mkExpDir(args) - else: - mkExpDir(args) - - _logger = Logger(log_file_name=os.path.join(args.save_dir, args.log_file_name), - logger_name=args.logger_name).get_log() - ### dataloader of training set and testing set - _dataloader = dataloader.get_dataloader(args) if (not args.test) else None - - ### device and model - device = torch.device('cpu' if args.cpu else 'cuda') - _model = TTSR.TTSR(args).to(device) - model_without_ddp = _model - if ((not args.cpu) and (args.num_gpu > 1)): - cur_gpu = int(os.environ['LOCAL_RANK']) - _model = nn.parallel.DistributedDataParallel(_model, device_ids=[cur_gpu]) - model_without_ddp = _model.module - - ### loss - _loss_all = get_loss_dict(args) - - ### trainer - t = Trainer(args, _logger, _dataloader, _model, _loss_all) - - ### test / eval / train - if (args.test): - t.load(model_path=args.model_path) - t.test() - elif (args.eval): - t.load(model_path=args.model_path) - t.evaluate() - else: - for epoch in range(1, args.num_init_epochs+1): - t.train(current_epoch=epoch, is_init=True) - for epoch in range(1, args.num_epochs+1): - t.train(current_epoch=epoch, is_init=False) - if (epoch % args.val_every == 0): - t.evaluate(current_epoch=epoch) diff --git a/cv/super_resolution/ttsr/pytorch/model/LTE.py b/cv/super_resolution/ttsr/pytorch/model/LTE.py deleted file mode 100755 index ccea371d4..000000000 --- a/cv/super_resolution/ttsr/pytorch/model/LTE.py +++ /dev/null @@ -1,45 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F -from torchvision import models -from utils import MeanShift - - -class LTE(torch.nn.Module): - def __init__(self, requires_grad=True, rgb_range=1): - super(LTE, self).__init__() - - ### use vgg19 weights to initialize - vgg_pretrained_features = models.vgg19(pretrained=True).features - - self.slice1 = torch.nn.Sequential() - self.slice2 = torch.nn.Sequential() - self.slice3 = torch.nn.Sequential() - - for x in range(2): - self.slice1.add_module(str(x), vgg_pretrained_features[x]) - for x in range(2, 7): - self.slice2.add_module(str(x), vgg_pretrained_features[x]) - for x in range(7, 12): - self.slice3.add_module(str(x), vgg_pretrained_features[x]) - if not requires_grad: - for param in self.slice1.parameters(): - param.requires_grad = requires_grad - for param in self.slice2.parameters(): - param.requires_grad = requires_grad - for param in self.slice3.parameters(): - param.requires_grad = requires_grad - - vgg_mean = (0.485, 0.456, 0.406) - vgg_std = (0.229 * rgb_range, 0.224 * rgb_range, 0.225 * rgb_range) - self.sub_mean = MeanShift(rgb_range, vgg_mean, vgg_std) - - def forward(self, x): - x = self.sub_mean(x) - x = self.slice1(x) - x_lv1 = x - x = self.slice2(x) - x_lv2 = x - x = self.slice3(x) - x_lv3 = x - return x_lv1, x_lv2, x_lv3 \ No newline at end of file diff --git a/cv/super_resolution/ttsr/pytorch/model/MainNet.py b/cv/super_resolution/ttsr/pytorch/model/MainNet.py deleted file mode 100755 index 9e24c0ca6..000000000 --- a/cv/super_resolution/ttsr/pytorch/model/MainNet.py +++ /dev/null @@ -1,282 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F - - -def conv1x1(in_channels, out_channels, stride=1): - return nn.Conv2d(in_channels, out_channels, kernel_size=1, - stride=stride, padding=0, bias=True) - - -def conv3x3(in_channels, out_channels, stride=1): - return nn.Conv2d(in_channels, out_channels, kernel_size=3, - stride=stride, padding=1, bias=True) - - -class ResBlock(nn.Module): - def __init__(self, in_channels, out_channels, stride=1, downsample=None, res_scale=1): - super(ResBlock, self).__init__() - self.res_scale = res_scale - self.conv1 = conv3x3(in_channels, out_channels, stride) - self.relu = nn.ReLU(inplace=True) - self.conv2 = conv3x3(out_channels, out_channels) - - def forward(self, x): - x1 = x - out = self.conv1(x) - out = self.relu(out) - out = self.conv2(out) - out = out * self.res_scale + x1 - return out - - -class SFE(nn.Module): - def __init__(self, num_res_blocks, n_feats, res_scale): - super(SFE, self).__init__() - self.num_res_blocks = num_res_blocks - self.conv_head = conv3x3(3, n_feats) - - self.RBs = nn.ModuleList() - for i in range(self.num_res_blocks): - self.RBs.append(ResBlock(in_channels=n_feats, out_channels=n_feats, - res_scale=res_scale)) - - self.conv_tail = conv3x3(n_feats, n_feats) - - def forward(self, x): - x = F.relu(self.conv_head(x)) - x1 = x - for i in range(self.num_res_blocks): - x = self.RBs[i](x) - x = self.conv_tail(x) - x = x + x1 - return x - - -class CSFI2(nn.Module): - def __init__(self, n_feats): - super(CSFI2, self).__init__() - self.conv12 = conv1x1(n_feats, n_feats) - self.conv21 = conv3x3(n_feats, n_feats, 2) - - self.conv_merge1 = conv3x3(n_feats*2, n_feats) - self.conv_merge2 = conv3x3(n_feats*2, n_feats) - - def forward(self, x1, x2): - x12 = F.interpolate(x1, scale_factor=2, mode='bicubic') - x12 = F.relu(self.conv12(x12)) - x21 = F.relu(self.conv21(x2)) - - x1 = F.relu(self.conv_merge1( torch.cat((x1, x21), dim=1) )) - x2 = F.relu(self.conv_merge2( torch.cat((x2, x12), dim=1) )) - - return x1, x2 - - -class CSFI3(nn.Module): - def __init__(self, n_feats): - super(CSFI3, self).__init__() - self.conv12 = conv1x1(n_feats, n_feats) - self.conv13 = conv1x1(n_feats, n_feats) - - self.conv21 = conv3x3(n_feats, n_feats, 2) - self.conv23 = conv1x1(n_feats, n_feats) - - self.conv31_1 = conv3x3(n_feats, n_feats, 2) - self.conv31_2 = conv3x3(n_feats, n_feats, 2) - self.conv32 = conv3x3(n_feats, n_feats, 2) - - self.conv_merge1 = conv3x3(n_feats*3, n_feats) - self.conv_merge2 = conv3x3(n_feats*3, n_feats) - self.conv_merge3 = conv3x3(n_feats*3, n_feats) - - def forward(self, x1, x2, x3): - x12 = F.interpolate(x1, scale_factor=2, mode='bicubic') - x12 = F.relu(self.conv12(x12)) - x13 = F.interpolate(x1, scale_factor=4, mode='bicubic') - x13 = F.relu(self.conv13(x13)) - - x21 = F.relu(self.conv21(x2)) - x23 = F.interpolate(x2, scale_factor=2, mode='bicubic') - x23 = F.relu(self.conv23(x23)) - - x31 = F.relu(self.conv31_1(x3)) - x31 = F.relu(self.conv31_2(x31)) - x32 = F.relu(self.conv32(x3)) - - x1 = F.relu(self.conv_merge1( torch.cat((x1, x21, x31), dim=1) )) - x2 = F.relu(self.conv_merge2( torch.cat((x2, x12, x32), dim=1) )) - x3 = F.relu(self.conv_merge3( torch.cat((x3, x13, x23), dim=1) )) - - return x1, x2, x3 - - -class MergeTail(nn.Module): - def __init__(self, n_feats): - super(MergeTail, self).__init__() - self.conv13 = conv1x1(n_feats, n_feats) - self.conv23 = conv1x1(n_feats, n_feats) - self.conv_merge = conv3x3(n_feats*3, n_feats) - self.conv_tail1 = conv3x3(n_feats, n_feats//2) - self.conv_tail2 = conv1x1(n_feats//2, 3) - - def forward(self, x1, x2, x3): - x13 = F.interpolate(x1, scale_factor=4, mode='bicubic') - x13 = F.relu(self.conv13(x13)) - x23 = F.interpolate(x2, scale_factor=2, mode='bicubic') - x23 = F.relu(self.conv23(x23)) - - x = F.relu(self.conv_merge( torch.cat((x3, x13, x23), dim=1) )) - x = self.conv_tail1(x) - x = self.conv_tail2(x) - x = torch.clamp(x, -1, 1) - - return x - - -class MainNet(nn.Module): - def __init__(self, num_res_blocks, n_feats, res_scale): - super(MainNet, self).__init__() - self.num_res_blocks = num_res_blocks ### a list containing number of resblocks of different stages - self.n_feats = n_feats - - self.SFE = SFE(self.num_res_blocks[0], n_feats, res_scale) - - ### stage11 - self.conv11_head = conv3x3(256+n_feats, n_feats) - self.RB11 = nn.ModuleList() - for i in range(self.num_res_blocks[1]): - self.RB11.append(ResBlock(in_channels=n_feats, out_channels=n_feats, - res_scale=res_scale)) - self.conv11_tail = conv3x3(n_feats, n_feats) - - ### subpixel 1 -> 2 - self.conv12 = conv3x3(n_feats, n_feats*4) - self.ps12 = nn.PixelShuffle(2) - - ### stage21, 22 - #self.conv21_head = conv3x3(n_feats, n_feats) - self.conv22_head = conv3x3(128+n_feats, n_feats) - - self.ex12 = CSFI2(n_feats) - - self.RB21 = nn.ModuleList() - self.RB22 = nn.ModuleList() - for i in range(self.num_res_blocks[2]): - self.RB21.append(ResBlock(in_channels=n_feats, out_channels=n_feats, - res_scale=res_scale)) - self.RB22.append(ResBlock(in_channels=n_feats, out_channels=n_feats, - res_scale=res_scale)) - - self.conv21_tail = conv3x3(n_feats, n_feats) - self.conv22_tail = conv3x3(n_feats, n_feats) - - ### subpixel 2 -> 3 - self.conv23 = conv3x3(n_feats, n_feats*4) - self.ps23 = nn.PixelShuffle(2) - - ### stage31, 32, 33 - #self.conv31_head = conv3x3(n_feats, n_feats) - #self.conv32_head = conv3x3(n_feats, n_feats) - self.conv33_head = conv3x3(64+n_feats, n_feats) - - self.ex123 = CSFI3(n_feats) - - self.RB31 = nn.ModuleList() - self.RB32 = nn.ModuleList() - self.RB33 = nn.ModuleList() - for i in range(self.num_res_blocks[3]): - self.RB31.append(ResBlock(in_channels=n_feats, out_channels=n_feats, - res_scale=res_scale)) - self.RB32.append(ResBlock(in_channels=n_feats, out_channels=n_feats, - res_scale=res_scale)) - self.RB33.append(ResBlock(in_channels=n_feats, out_channels=n_feats, - res_scale=res_scale)) - - self.conv31_tail = conv3x3(n_feats, n_feats) - self.conv32_tail = conv3x3(n_feats, n_feats) - self.conv33_tail = conv3x3(n_feats, n_feats) - - self.merge_tail = MergeTail(n_feats) - - def forward(self, x, S=None, T_lv3=None, T_lv2=None, T_lv1=None): - ### shallow feature extraction - x = self.SFE(x) - - ### stage11 - x11 = x - - ### soft-attention - x11_res = x11 - x11_res = torch.cat((x11_res, T_lv3), dim=1) - x11_res = self.conv11_head(x11_res) #F.relu(self.conv11_head(x11_res)) - x11_res = x11_res * S - x11 = x11 + x11_res - - x11_res = x11 - - for i in range(self.num_res_blocks[1]): - x11_res = self.RB11[i](x11_res) - x11_res = self.conv11_tail(x11_res) - x11 = x11 + x11_res - - ### stage21, 22 - x21 = x11 - x21_res = x21 - x22 = self.conv12(x11) - x22 = F.relu(self.ps12(x22)) - - ### soft-attention - x22_res = x22 - x22_res = torch.cat((x22_res, T_lv2), dim=1) - x22_res = self.conv22_head(x22_res) #F.relu(self.conv22_head(x22_res)) - x22_res = x22_res * F.interpolate(S, scale_factor=2, mode='bicubic') - x22 = x22 + x22_res - - x22_res = x22 - - x21_res, x22_res = self.ex12(x21_res, x22_res) - - for i in range(self.num_res_blocks[2]): - x21_res = self.RB21[i](x21_res) - x22_res = self.RB22[i](x22_res) - - x21_res = self.conv21_tail(x21_res) - x22_res = self.conv22_tail(x22_res) - x21 = x21 + x21_res - x22 = x22 + x22_res - - ### stage31, 32, 33 - x31 = x21 - x31_res = x31 - x32 = x22 - x32_res = x32 - x33 = self.conv23(x22) - x33 = F.relu(self.ps23(x33)) - - ### soft-attention - x33_res = x33 - x33_res = torch.cat((x33_res, T_lv1), dim=1) - x33_res = self.conv33_head(x33_res) #F.relu(self.conv33_head(x33_res)) - x33_res = x33_res * F.interpolate(S, scale_factor=4, mode='bicubic') - x33 = x33 + x33_res - - x33_res = x33 - - x31_res, x32_res, x33_res = self.ex123(x31_res, x32_res, x33_res) - - for i in range(self.num_res_blocks[3]): - x31_res = self.RB31[i](x31_res) - x32_res = self.RB32[i](x32_res) - x33_res = self.RB33[i](x33_res) - - x31_res = self.conv31_tail(x31_res) - x32_res = self.conv32_tail(x32_res) - x33_res = self.conv33_tail(x33_res) - x31 = x31 + x31_res - x32 = x32 + x32_res - x33 = x33 + x33_res - - x = self.merge_tail(x31, x32, x33) - - return x \ No newline at end of file diff --git a/cv/super_resolution/ttsr/pytorch/model/SearchTransfer.py b/cv/super_resolution/ttsr/pytorch/model/SearchTransfer.py deleted file mode 100755 index 2b5783ca0..000000000 --- a/cv/super_resolution/ttsr/pytorch/model/SearchTransfer.py +++ /dev/null @@ -1,50 +0,0 @@ -import math -import torch -import torch.nn as nn -import torch.nn.functional as F - - -class SearchTransfer(nn.Module): - def __init__(self): - super(SearchTransfer, self).__init__() - - def bis(self, input, dim, index): - # batch index select - # input: [N, ?, ?, ...] - # dim: scalar > 0 - # index: [N, idx] - views = [input.size(0)] + [1 if i!=dim else -1 for i in range(1, len(input.size()))] - expanse = list(input.size()) - expanse[0] = -1 - expanse[dim] = -1 - index = index.view(views).expand(expanse) - return torch.gather(input, dim, index) - - def forward(self, lrsr_lv3, refsr_lv3, ref_lv1, ref_lv2, ref_lv3): - ### search - lrsr_lv3_unfold = F.unfold(lrsr_lv3, kernel_size=(3, 3), padding=1) - refsr_lv3_unfold = F.unfold(refsr_lv3, kernel_size=(3, 3), padding=1) - refsr_lv3_unfold = refsr_lv3_unfold.permute(0, 2, 1) - - refsr_lv3_unfold = F.normalize(refsr_lv3_unfold, dim=2) # [N, Hr*Wr, C*k*k] - lrsr_lv3_unfold = F.normalize(lrsr_lv3_unfold, dim=1) # [N, C*k*k, H*W] - - R_lv3 = torch.bmm(refsr_lv3_unfold, lrsr_lv3_unfold) #[N, Hr*Wr, H*W] - R_lv3_star, R_lv3_star_arg = torch.max(R_lv3, dim=1) #[N, H*W] - - ### transfer - ref_lv3_unfold = F.unfold(ref_lv3, kernel_size=(3, 3), padding=1) - ref_lv2_unfold = F.unfold(ref_lv2, kernel_size=(6, 6), padding=2, stride=2) - ref_lv1_unfold = F.unfold(ref_lv1, kernel_size=(12, 12), padding=4, stride=4) - - T_lv3_unfold = self.bis(ref_lv3_unfold, 2, R_lv3_star_arg) - T_lv2_unfold = self.bis(ref_lv2_unfold, 2, R_lv3_star_arg) - T_lv1_unfold = self.bis(ref_lv1_unfold, 2, R_lv3_star_arg) - - T_lv3 = F.fold(T_lv3_unfold, output_size=lrsr_lv3.size()[-2:], kernel_size=(3,3), padding=1) / (3.*3.) - T_lv2 = F.fold(T_lv2_unfold, output_size=(lrsr_lv3.size(2)*2, lrsr_lv3.size(3)*2), kernel_size=(6,6), padding=2, stride=2) / (3.*3.) - T_lv1 = F.fold(T_lv1_unfold, output_size=(lrsr_lv3.size(2)*4, lrsr_lv3.size(3)*4), kernel_size=(12,12), padding=4, stride=4) / (3.*3.) - - S = R_lv3_star.view(R_lv3_star.size(0), 1, lrsr_lv3.size(2), lrsr_lv3.size(3)) - - return S, T_lv3, T_lv2, T_lv1 \ No newline at end of file diff --git a/cv/super_resolution/ttsr/pytorch/model/TTSR.py b/cv/super_resolution/ttsr/pytorch/model/TTSR.py deleted file mode 100755 index e04bafcad..000000000 --- a/cv/super_resolution/ttsr/pytorch/model/TTSR.py +++ /dev/null @@ -1,35 +0,0 @@ -from model import MainNet, LTE, SearchTransfer - -import torch -import torch.nn as nn -import torch.nn.functional as F - - -class TTSR(nn.Module): - def __init__(self, args): - super(TTSR, self).__init__() - self.args = args - self.num_res_blocks = list( map(int, args.num_res_blocks.split('+')) ) - self.MainNet = MainNet.MainNet(num_res_blocks=self.num_res_blocks, n_feats=args.n_feats, - res_scale=args.res_scale) - self.LTE = LTE.LTE(requires_grad=True) - self.LTE_copy = LTE.LTE(requires_grad=False) ### used in transferal perceptual loss - self.SearchTransfer = SearchTransfer.SearchTransfer() - - def forward(self, lr=None, lrsr=None, ref=None, refsr=None, sr=None): - if (type(sr) != type(None)): - ### used in transferal perceptual loss - self.LTE_copy.load_state_dict(self.LTE.state_dict()) - sr_lv1, sr_lv2, sr_lv3 = self.LTE_copy((sr + 1.) / 2.) - return sr_lv1, sr_lv2, sr_lv3 - - _, _, lrsr_lv3 = self.LTE((lrsr.detach() + 1.) / 2.) - _, _, refsr_lv3 = self.LTE((refsr.detach() + 1.) / 2.) - - ref_lv1, ref_lv2, ref_lv3 = self.LTE((ref.detach() + 1.) / 2.) - - S, T_lv3, T_lv2, T_lv1 = self.SearchTransfer(lrsr_lv3, refsr_lv3, ref_lv1, ref_lv2, ref_lv3) - - sr = self.MainNet(lr, S, T_lv3, T_lv2, T_lv1) - - return sr, S, T_lv3, T_lv2, T_lv1 diff --git a/cv/super_resolution/ttsr/pytorch/model/Vgg19.py b/cv/super_resolution/ttsr/pytorch/model/Vgg19.py deleted file mode 100755 index 64e775314..000000000 --- a/cv/super_resolution/ttsr/pytorch/model/Vgg19.py +++ /dev/null @@ -1,31 +0,0 @@ -import torch -from torchvision import models -from utils import MeanShift - - -class Vgg19(torch.nn.Module): - def __init__(self, requires_grad=False, rgb_range=1): - super(Vgg19, self).__init__() - - vgg_pretrained_features = models.vgg19(pretrained=True).features - - self.slice1 = torch.nn.Sequential() - for x in range(30): - self.slice1.add_module(str(x), vgg_pretrained_features[x]) - - if not requires_grad: - for param in self.slice1.parameters(): - param.requires_grad = False - - vgg_mean = (0.485, 0.456, 0.406) - vgg_std = (0.229 * rgb_range, 0.224 * rgb_range, 0.225 * rgb_range) - self.sub_mean = MeanShift(rgb_range, vgg_mean, vgg_std) - - def forward(self, X): - h = self.sub_mean(X) - h_relu5_1 = self.slice1(h) - return h_relu5_1 - - -if __name__ == '__main__': - vgg19 = Vgg19(requires_grad=False) \ No newline at end of file diff --git a/cv/super_resolution/ttsr/pytorch/option.py b/cv/super_resolution/ttsr/pytorch/option.py deleted file mode 100755 index 9dafa53f9..000000000 --- a/cv/super_resolution/ttsr/pytorch/option.py +++ /dev/null @@ -1,113 +0,0 @@ -import argparse - -def str2bool(v): - if v.lower() in ('yes', 'true', 't', 'y', '1'): - return True - elif v.lower() in ('no', 'false', 'f', 'n', '0'): - return False - else: - raise argparse.ArgumentTypeError('Boolean value expected.') - -parser = argparse.ArgumentParser(description='TTSR') - -### log setting -parser.add_argument('--save_dir', type=str, default='save_dir', - help='Directory to save log, arguments, models and images') -parser.add_argument('--reset', type=str2bool, default=False, - help='Delete save_dir to create a new one') -parser.add_argument('--log_file_name', type=str, default='TTSR.log', - help='Log file name') -parser.add_argument('--logger_name', type=str, default='TTSR', - help='Logger name') - -### device setting -parser.add_argument('--cpu', type=str2bool, default=False, - help='Use CPU to run code') -parser.add_argument('--num_gpu', type=int, default=1, - help='The number of GPU used in training') - -### dataset setting -parser.add_argument('--dataset', type=str, default='CUFED', - help='Which dataset to train and test') -parser.add_argument('--dataset_dir', type=str, default='/home/v-fuyang/Data/CUFED/', - help='Directory of dataset') - -### dataloader setting -parser.add_argument('--num_workers', type=int, default=4, - help='The number of workers when loading data') - -### model setting -parser.add_argument('--num_res_blocks', type=str, default='16+16+8+4', - help='The number of residual blocks in each stage') -parser.add_argument('--n_feats', type=int, default=64, - help='The number of channels in network') -parser.add_argument('--res_scale', type=float, default=1., - help='Residual scale') - -### loss setting -parser.add_argument('--GAN_type', type=str, default='WGAN_GP', - help='The type of GAN used in training') -parser.add_argument('--GAN_k', type=int, default=2, - help='Training discriminator k times when training generator once') -parser.add_argument('--tpl_use_S', type=str2bool, default=False, - help='Whether to multiply soft-attention map in transferal perceptual loss') -parser.add_argument('--tpl_type', type=str, default='l2', - help='Which loss type to calculate gram matrix difference in transferal perceptual loss [l1 / l2]') -parser.add_argument('--rec_w', type=float, default=1., - help='The weight of reconstruction loss') -parser.add_argument('--per_w', type=float, default=0, - help='The weight of perceptual loss') -parser.add_argument('--tpl_w', type=float, default=0, - help='The weight of transferal perceptual loss') -parser.add_argument('--adv_w', type=float, default=0, - help='The weight of adversarial loss') - -### optimizer setting -parser.add_argument('--beta1', type=float, default=0.9, - help='The beta1 in Adam optimizer') -parser.add_argument('--beta2', type=float, default=0.999, - help='The beta2 in Adam optimizer') -parser.add_argument('--eps', type=float, default=1e-8, - help='The eps in Adam optimizer') -parser.add_argument('--lr_rate', type=float, default=1e-4, - help='Learning rate') -parser.add_argument('--lr_rate_dis', type=float, default=1e-4, - help='Learning rate of discriminator') -parser.add_argument('--lr_rate_lte', type=float, default=1e-5, - help='Learning rate of LTE') -parser.add_argument('--decay', type=float, default=999999, - help='Learning rate decay type') -parser.add_argument('--gamma', type=float, default=0.5, - help='Learning rate decay factor for step decay') - -### training setting -parser.add_argument('--batch_size', type=int, default=9, - help='Training batch size') -parser.add_argument('--train_crop_size', type=int, default=40, - help='Training data crop size') -parser.add_argument('--num_init_epochs', type=int, default=2, - help='The number of init epochs which are trained with only reconstruction loss') -parser.add_argument('--num_epochs', type=int, default=1, - help='The number of training epochs') -parser.add_argument('--print_every', type=int, default=1, - help='Print period') -parser.add_argument('--save_every', type=int, default=999999, - help='Save period') -parser.add_argument('--val_every', type=int, default=999999, - help='Validation period') - -### evaluate / test / finetune setting -parser.add_argument('--eval', type=str2bool, default=False, - help='Evaluation mode') -parser.add_argument('--eval_save_results', type=str2bool, default=False, - help='Save each image during evaluation') -parser.add_argument('--model_path', type=str, default=None, - help='The path of model to evaluation') -parser.add_argument('--test', type=str2bool, default=False, - help='Test mode') -parser.add_argument('--lr_path', type=str, default='./test/demo/lr/lr.png', - help='The path of input lr image when testing') -parser.add_argument('--ref_path', type=str, default='./test/demo/ref/ref.png', - help='The path of ref image when testing') - -args = parser.parse_args() diff --git a/cv/super_resolution/ttsr/pytorch/requirements.txt b/cv/super_resolution/ttsr/pytorch/requirements.txt deleted file mode 100755 index a464e4cd3..000000000 --- a/cv/super_resolution/ttsr/pytorch/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -imageio diff --git a/cv/super_resolution/ttsr/pytorch/test.sh b/cv/super_resolution/ttsr/pytorch/test.sh deleted file mode 100755 index f589bb7cb..000000000 --- a/cv/super_resolution/ttsr/pytorch/test.sh +++ /dev/null @@ -1,9 +0,0 @@ -### test -python main.py --save_dir ./test/demo/output/ \ - --reset True \ - --log_file_name test.log \ - --test True \ - --num_workers 1 \ - --lr_path ./test/demo/lr/0.png \ - --ref_path ./test/demo/ref/0.png \ - --model_path ./TTSR.pt \ No newline at end of file diff --git a/cv/super_resolution/ttsr/pytorch/test/demo/lr/0.png b/cv/super_resolution/ttsr/pytorch/test/demo/lr/0.png deleted file mode 100755 index 7721c4c3dd2caac02c9e0a56b00f7a82d22faedb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21602 zcmV)gK%~EkP)1VJK@1cw6*F@wo-C*GV>-_Bj#UAgLnz4u!FyM##&|u06;`$1{8>l41mB400aOG$pwG` z5fBjo5P$)g836^RS0-Wxu*3|=00LP60)hZA#GJk}+BmrR)}J4K_NR@d(eG zgYT9H2a7u&vL;ThKmbIXT!$F}nHU73LPmkWObps450Ttndbc8ZG5}-%24)5X5CSB! zNbZ=yGFT!+6v+o5018AQQ3@dtgh(G40R*5#l<9vV0?>VnVwpKvDntMf0)+r6Kp_f5 zk-QEO5gmb!z)ZfJ9h}&_dtNDp2n;}g#0U&*^wg0j*Kc2dxFrN4AVK9NZzllLuaGW^=qYFaKOoiAPE&D&Ztn zE!mRZ?Jc44mql0NGP86d(nLZT4KJ)l6VbgV#5=eCjer!iD&^RM2RQ`Vsal6B!Utspl8m~{Qzc8e@>PNQ3xbtW)K2h zff5k`P&gyIPb^-3TPiQvwN$4VnAlp{{lKSgzV{MB;(IZ*xTh+?Yx0tGTA zt{M}e6{!IBR3QN}*!PHt#9&M`HFvVHv1Egql2QsG6K@2_0LJRvz*u2?&+?_$P`L@@ zU`{u;=M7N`001R~BZMQRl0u@8Qb-{LF%VMGJ|2 zB*s!m?3r!o`wJpqf*#UJeN)e|C%T$khXtY(AONI5g$zAQo4kh*a$^5O3zy!Kj%S%| zvdR(@4`g+`^VpMHEAytcCZrMqWoisk!AUk!K^@k=jDh)5PT6gy?xfPF9++=MQ^!0g18ilG1HSz$Jo| zkp77PK(K_ihr|V@I28b=4?jA8;SI;}5VpOHOw0>Wd2H4#POV>iODd0RIn?Y=z0T?>zGO!sR!W zcW8n0ozcd0+q=ts21R)s(BO(J@2E#xAi4T*AQl#%J zAqj2!QppyW5-cc?$reXu(8*R;Za9u3urH6P%maYu`nyhk{O$+e^^_wJi8+P&GR164 zV1j8SqC^nsZF7Q=ML(eK;ZsiRToQ|=m^WGYUf_`ITi+1%JVvPMU-A=Vo7gc4oCsgfeI%3+NJN2992(2!2R0Ti3+V_%iJaV)L>9=* zXtmz=$n*EEoUu_;DVdl9N1>943=Av2H$VfNk`MNPren|6(}>liX9$#1C__2*{$3M0$`El2}^_p_Jpgj_10L>$$E<%)K!? zk3Z3zKQGua0E0;4L(wzA42+IHb?EV%@4YCLOKEtN>aP(> z4x1Z6qq4TTa_icq&GpL3CqMDc!r@%7U5+Wh&cy{EN_IkDh~0vM2qkd2wx zbG){EWApCWvE3)Ejr)uz0U@Pv76K@fIDKX*1QBC@SF!KY00D?O2@vo1wI=aC0wktS zmcW+~%Tmc!JN%ok z{T5k1@#r&g*lBF7u5N5rKYVR+-zf&cOuhITdUt}>=zR}=`qIl^9iEyKj+aPv8uBJm zlYsu;eCb+$ONHo}N(5wPfW8aqKO*;xDfSi%1@y0w%2c03O+^9w0148V0|Wvv*d8~> zz)Zjh5>e~u)o=XgpjnL?TPIH(zIOfAxl31$9op|_vz2PSR_hAojZTc$w<=-SZPu!Q zxcB(OU;MdWaU2(Wp$()lWa2E`wcF=wD>n~6{bdseJsKf-ghb_0qf6hq0P@PWaA*>>y?W>eDB{$O*{{hfUM)NU0q)j3<6+ic-Zs&u)ZQCCPYNSvL|LWA%V!o z<8#Ng(Um(FP`aF8c>wAevy_7EldzEP9zv3@U?dhv&}DnkgeEowhFMb&ssRX0qSXX~ z0w5-9Ng8$r@D{es9&^g=C8R45AtF;RN+Yt|llFuXOw0sRpGfNQ0sxj0pv6k` z2nzrrln7F(pxeHF=6gD53ARBH1wkZ%T?ve;R47!N&1NSSQszA8_+y_q@z`^Pfsx|C zFp>H2#owDba7qX_dGo{wGO?jV8_D(alLsHZ`QCSrJoOW7<5UoPH`cR^(D!>iA44(sk(4UNGAd2DV2;Udd9Wq*i(ACZ~6MHNy@r# zFSfcb>5)XSz1V{>A^t6cO{fiGORR}vvX+unAO^4u)+Y8F5RnL8{rdkNR_+wDnO3XO z2_kKDKA+1LiyMtrt<$}5@$z8KAIiuN&R_oiTkn74kA7=&?OqbX>R8Vnd~jpwPTXxK z={u%0ER=v!CddGc)dwH>+}#Urf(c}g%OwV45R9DiJG`B=g6#-$J9l|s{-H-o^zswQ z8ug;(lmJbWh?H;>YzfkbXSU3SSQ|3LhR9gfmJPxFC%$AdW9wUujO*lGH9JxY+AZDf zWF5J2?Xu~%8;xcTqT_ofJSp$qz4gK${qJmbLMf4OCib4Zec=tEoWw0Bj1H&FM*@?` z%jTvIK63rN?@Gr5U;#*smRTX(hvt%0Tq^w3%gSv3FZaGl4Lql5R8CDUr0_E(Re4)k zfe8UAN%JBC6QydFKy(m{fWxx~2B-JVJRXm|{ms$v?(G{3-L>_bYt6xob7W>>ppawY zJ*7gEGYAZ*uDtt$clI27v+t45-nsCW)!js?iPG#`KZ6PP zJBw|az7LtlPy+uiP!d&>(OwXzqUn6TZ*2!YJJwZ2x{SSP^9a1cggJC8LsE3I0kPq1^!5@0Xi z2SnD(6s8Y7a^uX4Qn`lMGAQg$&-2)=?4xHQ6YRTdgJvnONd5Gy_xnT8&z5b46PbTMCVh zj14t)wTTRAo4`VdVmv3ySD22-FlN3q%BgwU4k8!~s^{`z5lO(JKB9L1ew!&ArtR*2_cd6PM%6cnd zxYp?&I=uhDu_Ia6W#WwE@$w*y8FKU#4ClujqKWS>~UgtJ@?jy zw?F!;-yaNDM+b5<`wzj)!-WGMSB|pQqCg+=`Rgg0Js#}06Ql#I333{LG^ zyZ&Ccy5g0_n5YMg)5l3zbqf&NsJ!ENW%1_v-3uxSEVdaW03j4gnZz%tLm?&;$8l(* zcF?W`t!88Gc6DX`-i<4_K72Kk%jJs$FMRWlzj$ow$G`CT@4WWG#dpsy{osS_#EZ{< z{>%BH$sp31kr|XO*;u4$1n8NeR45RTQcnd^d0TCLYq8R3w}g^e1#>$m-#B}{(TTJ+ z$`ubDI}}uFx0WitBQw4iRH`etE)1;SU8KR;!;cgPMu~a)$YaY_-kN*-%a)>kWY;f$ z>6Lx~8MEW$C$7EnN2;gceNqbRTdP5{VNKMkZ3W$CeX|lY>ulp@ZOeq6IA{f7WGs!1 zjvhR)*I4byC~B;a4V0dJ?2+0=eRXv!m+``uY2QBgzx}IsfAwGfYOuEa&bMzI`^4WF z-S;4q=`BUS#)1KWg%l`+iG=bnsI+dsz4OGQE0^EyhVlGHvz*U7eq!H8*YAZS1k+p|X>dGz_eUmoAPa^*~WV?H}L%|vj&=j+=< z1ZH+KrOD&ZCP5eXKom$Nzw>+l>(b>9i=}+G)6s^k)=UP4^punmt@H#?&~6p8s|~&(x+{01?)Op3r+(}gr;k3deCf^oPyZz8IE8;dBpWIB zXssQd+OJa3*YoXU=}W)x&))v#|52-~lk_Kt@_um$GUd|X$n?~dpUIJ53L2YiO{O$d z8XURt)?eLw^BY^cCRWxrUU}=?a;Z2yF>!Lo#O1l2R~MGP_@y76ni`8@bM)BJMt$q8 z7rwS}@9L2!KR@7vk;s+yJPK@(^vZ{Sy>aEug~g?Iqy=L)j4Ji!NGVs$=Nt*cd2h4X z>2$);?7(vm?28=X_@y8=nT(Gpwp!h0C$IpYc>M5-uU`$-K(XM>fAso}Ll5mf{Lsp^ zGp)7T`H@{f=7-H-N=jmyC?S;=M5d&;V5UNO^wU58F9k{zGEJc-$u6cOuF4Ni03wsM zLXGV_*ts(77ooYiMM{y7-LP$fcH5wz$sRp^qF5+)f*_3S*yz~3mFmUwXYbv)4Yr)bV4dkG%Wd#jSSu zr!SmocDkM;32?1iZ?=Qg^~&i}$8OzSxq-Xpm(C9l=FfcR4=48=nmzjT{D&_d{P-?I zh;UyrrUw`MJe9&8TY$g}j0|ioBNAA)b{qSNG<(Xx1U3Z)%-NBdq1kwT? zD$)!?l2h-6?I2RV90-7zgkPL___L=@AN$HL{PeH?+ONzUKDZXs#MJ1crw)JN`Oj6> zR<2yR^2*DvD&--7CDz6OKy_Jx z$z-m)^}Q%;O&@t`_1e3L{es8sBwi01_k36X1^v*v=h6TL3;5gE3VYckL}a!!wD)!c zUw`AB_us!j#G^yyhfY8E;OPe?#r3Vq!qWQAz5A8x3*|PNZ6eFeI*yyoR&2S}Xf+zm zIF1|j`s!+BWv#NYS!Kk0)*T-%q7cXT?Krh}rtGVk;qvL@`|mC+KeTuBr#?1AjDy+C z?S)li?bKjVAgnChjic_To_Qo+9Ikep%Em@x?cU;z3&r7SuQ;@I?~+sw{4iMRX9zJZ zB)jh!p`Rkf9^1TK!H^bEQOduhp|_GwNRn6xJT`FtuaX%EJ8O zt*eb@v(@e<@TuJm7nfIVUAa=Z9}ul%^aRg)l)Kstb;v z)p4}d?3PP~xV^z)tKDb_9n{vBUi#j5Uw`AxvsZ6v69X_20fN!-rNynKN+Sq5LI|y` zF*XPyW9??WC8fM^cdc4$9Nsl{x3c;6mH7w92bWe>m6yv-9(d_o#dVz9>lGnzpj7k< zMMp@#Q0{a)j*=gHU`|O<7@Tz+xqR)TmHF$rfwBD1#OB>gQn~#a*!znv072Y-?GK^3 z?D-iboun8*q@|!jiiB0(&U6X@g(8Xc({g+eHfmX>2xQFC&lhHgGN`*-cQ1D9o5hS1 zhuxd^RyOLbDALvtGv)IcM=B!ryi649PP>~JbFDQISz|Y9Eyq#aC^SSPBjwXa_P#&A z_U~SKUnz&lI)2{Em9;gK8K>a6VHD3VEwhw2@65mU`deqteAsDhWilC5`ORwGn&{kX z-(fOSN1j~2{SmXiKM{xhN(@Gk-+A)<+b`UD^$)RIaXnv&w2D}vkeC*z%WcjMImsRO z>aU56%#V)FofsGxEDQ{edT~)k9JV&Ajg^%}YOc9TO4kdbh$wkX| z8-!gY1(6}LmTadJj2HcKF0*TLxZ90J2gFXVM6b{&@)97pb+ z8rp1y6GMfiN@Jx`i?vbG5y}}H8f(@zeAhXC@_|BWpmX7SgE+SH@X7DJ{OV@4i2~nx z``wkrrOlOFrBO37HU&1QZEd{wy>CqIJ}`agiK{>O#|Kh5vfHfw$6x)gx0dE3ZBnkA zNhu|yGKMS}1XN1JvCcYZnDT?;lRI{o$EF{6`g1RT^LMYl|7H|M4<6n7g-7?#S6ah^ z`7@W6>aEuD`c|_YmIg*2dF+YnH}4D$6o2XGesssw=#{hY&+R*K``&84;ID2rfAhEh zuv)9_**UEcuidLo?cCGem=D8HI?j*(H~(($;fL;g^pX^6^6(Q@N82$kAYhW<0X8NU z2s`#aI6Zgr-qm-{zWhhyW7E^eo)Nhrqct*dkCEtSKM_(2u$LUQq-|+n^yFjDe(Se> z!_Q`cZNEU*S|f#0QiXABEi&1`*?kW@`I*zte6cVvlB5ZK@!vf1$N%A1zyD`{FfmlH zL>W)6Y&1tp-ask0ytd{0nc~3k$4>1$dT4HQt3ErFadh+W{y9TtVklcG5AK;eFt>Nl zzxtp4m-UT}XPt`+PK(aclvqlsq?!004%0e*_}S0D{++LN znl&M1TIUYH#EfQqcwlX_+G(}-9y{?%|K>mEOXG1A>R9*q8!@~8q`*HKEcjupt+89J z;MA^>4{ujiDm5>YJvK4fja6i*Q>%RQ_kX7>L*+=3&E*HjT&W1$!r<8FpMCuIzW(jE z-Z@h$6;~H-=JOsiTcU-lXSXWLLu0e0sr{?h-kmx2wAB&xSvLgHXJeD(F##B@5peI( z#~=LSSK@5>>`TA5^uc$O4IJ0)H5()?9GE1#5t0@hOahRh@f|nI1>r zT-Nt9zEV!uYTmncP9Ti#IaOc2Zo*FT5J~$6rsZ+HHWE%Nj{$(Ki51GZqfZ_G+%JYs z_PxLU?X`2?Qxr(o@7IAsubRG3_6bBC$HzbR`Ct0=|9_!a47vePLhuNIg30bV`N-e< zXaB!^VaytX{g$cZ9*A@0!E83y3d~Za(de2Tqoqn)Z`L~i;^fH(95?&nd+(lm|Bbv;a^IYf|- zH@xTg%GGz2{c9@OzaB zTr3Ud^ZBi2x7H5!PL@hJXQR>XhEX|V_hoLhE`9yRhcBJ|;H|CoTiLAJ=r%GVLzfrU z-@9;8plsBdw^r72`OL?T9sk$=^q)Tc@u!E%`6!H}kjilw@b-mw8?_B!8lQWxxw#m3 zYQ3-t08m{1?th6^uX>&I$(mau7Lb)=*-cl_hWKKl>5=)Ct=|7GF*?+DXX zj@QqLP_OSNNsj6`8kw5=nP2&zfAYWmAExIHe)8}BSO48_eD!lb^WP%6WKAjw+sv&% zKxWG3i>^pZ^DczgWy0V;t9Y9mjM1c5UP4xwl=<6Nuvn9$vcmhIIP zJa_Hf54?-7?mhKfX8eFPmf2u0Zl~>ZJ{uED;p}_xlRJ(*e(&PjAAa|@#-`?`jy?@u ziA>yYL+I!J$eJiH4?g{4r=I)~r5vMe90i=(7T&HDxQ`}qTwh2>qMaNr0+~kFZr0mM z%9YLbg{yalGjwGC^v`GWZ(LaU-Vff_QM|EL%L*#x^BbMm@f;#{q*E9lcB4>$-nai? zWvhk&38*g?if6y~r?Yd1N2m9W%pP37ekN?JyZJ#T0$?#bzUzU{{le(cPhNQM`&YmB zo3?&WIvxte_cvMg7%pNqaV$~oec%&EKl{sF%)a-+|F!u3pQG+Lj@PRn-;VYf!04DQ z$5BjV`f20->i$r&L!gd>pxYV9X9tTulIezev)W+bY{uK#sNK4;s(>ClK67F^x3RXd ze|F4qouY`wcyqcStF^|)=H|VHd->rZH##Wx%Q3!_-h~h z%{M&XpFH&F^5r*_a?-K@Ypsdnk;%Oee&LtP2cA0n)?eLv<+r)D>bPD~rP}Lh>38@t zGf*5Wl>1LVck=mPik!lSFaFNrdw-6mBb;73qu*$BA3K(mey2=8uSR>jR8@#}eJcvu z!{z*N!H+DjZnc{2j?sE@xcI^4jap>U1lF2IADo*U$k`|;Vu&_GFfytS50rKfWInob zDPj%;c5BU{!NKv7QDd#=dB&IN>rE}l? z?TwHALdFg0`q1k!<|O0&LrKLxVaD5uF93<6y1s1VxLn9uhOM@()H?uBDwms`;KoYL z%N2L**&~%(EO?{Cg|{!>*{If8c#D-EauG`(1mr5#mU*jSrz1tK6;EsJKXXj2V-u&RgtA8>yG&Xbm8JQo|T4Pf2LA||A z^@WU%5yZ^FMI`8ImxiRrAPo__Fw=O1}w zYh^V8)2eUf#)d}+ius+>pLz0$ufO=>#P}F6wYwcO5D@vUgS!2f|L4Cx@x-TW^!C1I z{+<#-AhSS`G=a+g{gFWd`wl;{`{3z&SKq(z>Yok_j?5f?7PG??Yee%yt@}M7V08=t z)B7Hp+xCeDvyzyLXMBe&C4e^$aG} zVW~Sn{L%RM^w{B#i|mlmF|)ldYuaNq8LZU+FtO+4 z*sf!1_pV%d_l3cHZuH<2&frdCEsz2DA)ydb#qD)fd3Algwtx2y%h2qGH}9>kR;rG2 zW_IoU-1A@j@ZDD&M=ox*fA5t$2Zy~5W;eU$ookn}cf6B__sooTzV^x+t#0%BoqJd2 z7f#L0mWL-Yg_6?=5AWOW6+d6E*6yyhNbBK(w{uj@PP*d*nVeJJGnL)7vrusV3SYjm zCNOQvK}2i8G9{~Hh%l|iM?+Rf&zN}O`Ue{~J{%dJ8bAD$$PHT)r~N&>s?{IskC8b(hx^v~vf8*i(rTL}m%0?%$RBv={EUd4tZ4}BQ z2aX(j@WkUy=Yb8O^pR*<#f-#`Ui1XogB@KkK{)Nav7o4RvXo3XJ)LN&A3ry6eSf#f+ZsZ zO2Np2h*3f!Ey>e4Qd%1$z|KRD&FnwDeB*;FKlqcO@u``kPs-eI6l+V?+P}33002ye z8Ahk}kId}fTDo=X;@b}YVCvvw*@=S|1Q{)ry7|$oZ@v4`o!iBub5j}5?KC@`AgtCJ z1enbiq?3E=&DTapMm@jKXx25EOg0;a;q=sG&}rndxpFD*Ds}YGA=3Kn#T!}4I=a_v z#iw@^2i!NZ(ba;X=|=~I7zP3xTdPg8+pcU?gIIUEt^lKrRcQ}N(!t9>0>Ez9wqR0H z$iT!>N-|b~&bZ>>iO0>6hnB8?c;V$g9vqvRJ^D0ehpmYerh^EQypkng0wK{_s|^oN z?irok->57sTzy+yesf~(baD2WRBmZ_EI%;%{-vuEBg2y;rPW%i9YosL(!j{hxkGUr z7DuLrhD&q%4!`yG>%*gij_X@%o9%!M6{OCQna;w#0iGT8zi|A-%vfP$An!UZu&}W; zrfVV##B42-O?m=!Y>bVPDo|^swMG$v5WtMqaIaexgcQ9J>+MDe08mo+4gdmE>q}|4=!E%;OxtPJTyAB_r#NKam?sgfMq~yfg~j(A~b@qASDaX-q_Um)K7J4YfD$( zS-$$t)ZB?DpZk(j&f35G=R%^Zq}En2mj}w@Q#<$WJACiXjW7&Ycv=)kCiV@KawR|B zIp)rd*#oo1xryxbcqyOFIIasUfXXJ;rmX{tnJF>K37oabo-#25Qw(gRAV*puw6!t5 zaBh`=MX%f{Y2xlxfuVmR2^u`ZX zVKdyiJ)CV%7J_|K?#@wfVmLQY$Vurih&VH@f&&=U0=0>9l3P*J|49cmt6(pCJlRA|1n#)Y%fS>~&5`HDXq~&wDZt|yAGaOym{fnAN+;m`Uf9+W^i&38*8mG42VDs5`h3w2&Dw0 zuok2cS!h3Zd}3ph5-`4aVJ&Gs z>DvU6HVQK{^hX}a-WV8SWCkUZ>S<042LNb8oVInLP!0pH&0o89_BGFubElpzOw2)A zBQ6lZ>X@T;*jVdsEStu{>5(mJ-&?Iz>y0pu_0(8q$JA)nce)*|8~|WPu@K4;WQ|f1 zv~^lt>PGRUrDcwy6SID)`cyTT1D(bhK5&!VK0`B(!ZbzR<=^+9n(>s{Jr064U^s_8bIMPvT z3peJ^zdG#eoyVVPwc~JWF{&=a^<~qp=yuKO?xTmx#k_O&YNgR(A;nNZAJ{cKI+*ty zt~SHrVpdyjb?s^`Q(0SI-RP{gsAM zC^=Eq!A4-$yJUq^@LeecGy9J6T$RaqvxiO%&CHF=>{i=d%*@Pa`)#ra+^^H#E(qm* zj}j+?lt5TU11!^_MuLdY#yM{4##Xmh*)KOgb(+=}Z+!5*_v)<>thUw&Ab}8ejqh+S zm%Y1Mi>zBNyAt$J$wlPF^=@TLUt6pTW1B(T3H0iE%T)|40xLpI03tNTS~7-L>v6)L z8e(Wi#VlxIH2NQZJo|;uJgK4Z#%mva|NWIK8>T!m-syG=8I_f2z2c!GbI<+K|LA50 zV{H`lL?Ef(W=^JyaN2p-FR<-J%mji^0)rysao6XTO97B=Y0<53#?AGt-8?YZZqznz z%&(Sx9vIKvTejzJwZb4oN~WBEKmY==c6_qfXoWYH>F#2a$S6U+w8bo-BP^+?sRJ_I zSl3&o6WG{5NR}w^f2?HE48(w}iGh)bA`#md(aD^glRx#@l3xIe@uwayG^_4X!#w)H zV@)8rao0Ec{X46JyH7c8q1){MgR-1f-Sc*>aNq8xec!$LOF*C;4#t9OC9_;Gm8{(o z5XPG4S8BmVGw)**#WSPXg*$p>t38}|$A*frbyl|8Mn~fVw9%F;8*v=RQ5d%Z=o+!K zu{vG`qj@AF*qTll2XS1f*=n0w9SUNri4nk&GHvI??GPo@mKh8sGrxpVLJ7HZ&)%Q< z?DM-$KX&h0uwN0bOVMqWR>-eEheR1=RZ+-aQ2Q!C{KJ>Yt)Y?d3l&B~b zc1}_iJqm>(nd6lVf=a7SrKh)!H~zS27Go_KtVRlBx;9_iY;=zADMZFHq zN|{uoFfeGcqL+vU6v}ZOSG6P4j?BJ^k%2-MfMZk#Po63cm%ej1{Oh+G>vgc2LY5tn zR=D|r0f=;*8(E9_7yt1;6UwJ3X5eHx5cEcpr71lGPNysY5>v0f3K8tl&ELt>dTV37(&|PAm|FqOj;ZNkzg+0d&8VOY)?_n2Gy?c=v%9*{sdj9= z6SpH{ECrgF%$p=D0w@>^?*W@PFMU-{=Fvj=n( zNC^@oASxohuiH0u7s%;Eopg#wq8@SM&5!O@R%@EtQ5camVZexNw3!<94o#O1O%LX? zGS+-&DI6He1>N?|h3@=nb9F0fbSc!NEr_JZ1kg}2{ZI&`=Z_Vhdw9osjjt>;&fVUa z-|9vvgzI>&lk>b>*3J2z=SoLODUu0!X^KfoHIm5zpmH2P<9ohu?rq&(UfVaf4~?yH z_Md#A^qJ3m`uuAz|9}6YeS0yqV4Ka_$kbsp-ih*?^LZ1RLZDw)`e_2pYt7L&ruoAky1(tO1V-g9 zAS#(s_Kuf`8vp=XiU5?51)qm@9vR(zP#b&UjlW6KVDDUOEmmrsm;uo+=3E6C&v%ul zWX5+r-}7A8bzSMXo}Y0&&v9MlIF92g~4KBEvIw!{Rk5{Xy#6uMq!u#g$bDs)uIgWXR&edO=_>Xo4zVF|AXIYZ!8x_#)(KEC$X8r z?Z$4v`yf-V9~M)>nHf>}4}Rs7d%ryU;cFj$=lAB^E#de}8=V`=jkQ+T)mAE*afBg= zv^4}E#O~3OBiSnq2`SrTr?b?F^amfF-}RYhy;gHPFO$oml#ZW$c*xx`>YX{aa%n^5 z#%G08`C_(MER@Ov*=)|w=6pZvc^(Q$%s~)#IzelrURm2%-`HGUS=-#Ig<)W=nHnFS z+p*I$nwDXtq?A;kBeLWvbd{{EE@mEmSb<%?eyuz{4G;-rCPXBK_sc+kFiesjO=ocR z=JEjuic15-AN%~zK2W@UM7+7O$uGY{Q52&T1|$)+0^>QV;5(LV7)RF@H$2}TjYKw^ z^;}iVQZ>XN(f}kuk4;VF3nk@9t4%?M`4*3$1tR~{DFNX&Tw{G1g z6BP#vn+vxOz_ZN45;1@TRPaNMTKypasnNuKXMexShRH^OYTY}3cd^rT>J1&mMuOda z_;|Bko4-3Rh3$s66@#I@@;TAlXZT{DaKHYzvH4ei)3yqvX`SS!3g#qh(O zujwlEw4`rc93|h6PygjN|JoDC%88Bja;0ZK`-SE8jrV@zKe&>Vl%XM;G(r;)n$fJm zP9p{(GWqPluoObD$rQ@%ZoIj$9&Ro^F>O3o4VT^Tymjr(7d|K!vXr#`Flw!>wMr`2 zk*@19qZEROl1YHpS^zfMDCrtw$A<>9zDFaY8CPT+Lg6NJaFBJmko6TV-?%!vbBEn( zUcGT~ZT>>3JTkWDaCT@)iTew0{}#>O8+y=RqogO+#PO*w{KFxq*?j-&Z(gW6yB>b( zb6+w+>uX>AdcDzbT{oBUCI-u`PD@H9gz`Oi{{s&kdibf~ncV~9JBov2U}@~YL!S4s zfB(<_$r~@f^6;+wXZIh@=T)2sS8yidXEPaVE%w^~bR0{Cj^h|(g^)?OVXY-1YpvF? zHnB27DKT6gKx@EtGQL|bxXglx8CkGa0%oShg05A9t2cOMlyx;%Y*Kggp<~WWeE@hlj z-q&%2C<#F#KlG^|f8_a}ih>SVB1_C>U}P%jG`7}PM@EMBOnIeZCYzPr3}k&bo9z$G zMF4OdM>#5AC^(LcBcqg*3IV_pA%L|uiehW5HX6_==5t}U;|!0La&F!y0>$Zw5UX`O zF+QrbE_vSVjq1Y_E1@_rG<|Sr`f#^hRWcd0lI-_(4_FUEaIY~Q`uz?G$_TT;y>0~K z1zcahb>`lUH(q~3DaFj=laoL4+av>3{&X`N0vR; zG}|5JJ0{3FmO+5E%m~b^jfr*0oQtD_pZbwD`XcCBl|cqSQ&5gT?vLXU+#9aK+dW{A zG_3Ud{)ka};g%b1RvN8Z11h5R?eD%ql$Ig<_|N|Q-}}n1c&>cuPky%*W4T!PiJ$wg z_nvsf=r|ce((7Oo7v6u{XssM)X3Q%U3MOtUg+icGN(k1m7D6~mIZ|~x!Sd32ChNJb zR0(O73}PS%f~eaG5y2Yk`QF4pnd9*4^*i^L79W}bV9PcJwhFnrwb_geNAX)Py>aAc zzA}2~ppHUJ&@-|S2uh%ms3i7A3uA9y7Yd0e`_*$PXNf2>;Zo44t*+H-E%(fO?>Czb zr4#@^`OGJN>>vCiYxu~6k4_919KlB){Mg5T`u`2-;2jpgDTxwCmp^z$mPKT)A{* zty*zq!om{~F<4?fIX;NK{Qk{bZ@=@lBa_h`EG0=nN(ctPWNIWQE&kk_9F(T2xHl6w znLW-dq-ft;sWjHs!b(>}#%6p!=ys=OX8*~r{aeTJm~DM!Nuof6Y;nLEgZE_v1%T^$ zH_pFbSz2&Cr|9wMpr@o{Hi={=mHLT%^@ni@0iNp_6Gu@bgvj`w)|#1I&#`2f4NH`W z@4x@i!f>hK7D~QQk`a@k&L{-tGQJQ}qVwe+`Q&?7FLtYInPMrSoO0fYb{sL$La&9<%8n`@P@vSr#vDn}}%q>{havvhNoQr6yYjEao7YIZQwa;WSVx z&F+}#wmVBptLLuX+}D+Jx?7suEgj!lqqN-yQpuR~Booz}jF2|E3t$w8z|v7`w=Rdx zEh$7sky4@?bPgOm_)EX?&#f_n83l&zMyI+am0}`9P?8B5mFhO??|tvv=ihy!xw^1_ z$L!4Lp6|YMeZ1W9Gd?5sW)L$G`L0725WsO1GYcsO2Fi#~uh)fuOg57YUesC}V;sjZ zVMI(h*KGyeDB}o_LnC>_HuQ(AAV*<$bF)6bu<+VDAANBC>Ze71e9!)+SHC?tGBLjA zs8TYS8cONxMeg-L_ZpZ85h)q7$4be1wSMm9KO1Eo8M4}vWykgY#jpRH;h~{84wH#4 zjoPLTT1p}UDS--smFl#bfA)X3cgzh=Oe?p?Rwcva zDD|NIQN##fh~N4C3#z$<9vf|&UDJv1E5GvJAAR^^K@=w3q9bK(ZGob0k_=EnWIcCf zZTY2deeL|2SA8@W=hq*3@c2+U|N4!40K9kHRmusqiH#058Dj-ou+-^vI$g~~IZu>x znUZ5Acbm0lBPMGM5f$>eOeW(A41p|BR(eXI(R6!tWu?}7?r0YG3@0Ln0Inl1T)JA? zefW__PL35#9MPTIm#&_BuiNcbHma44O{MPqRpmCehh`^s9d3~ou5T$eW%o~+^*N(oc3Qu=JAImxS)x@;m|~5F#r_-M)J1 zao){bzLNdi{t=5cyrL0F}+JHnPkfcxo*#Q9u z6!_~~lsr;|)L z)+!AXg`F^#$q5>Og<@&f{)6hj{K>01S119-a>18VMa77*l~T!x0S{0yNFkJ`C^NBV zRZUdamv%g$hldC2>vcdt+iku12P#{_OhNetH{;8Ep}u%6nY5^+sID%4^|$`x*$>Y( zw^QVIeD(m4lahx|L6t*vG-oRd2Td_%m} z=$1<5=YQa4t>5_fu4E}x#t}JBm2z%1c3jV+Fcd;z zp|`ch9}-xbqB(*#eWcD5W`aWbf$sOd*%e z=X_!jL^d|0wcV^%ZQVp+tJ7}P>aC#Lj$>=dB0!^26H+N5$Y=(ZLMR-=v>=q2fh?6> zQCBpO_v$vD9vhiEa6}s`Ojx;bW@F`^64GU6W0UD?+>GJEa)8K=qqaiYS`L!Q(4YL# zPnOE1-}~dg_^F4yQnA>s8_$K|0=CxdUtMb|fdWxV6ikD8|GDE68Ak|UN5JY@ec{SV zHsg;JytlsfRfE!Zv$Ea(*8ll$R~zBMnT&fNPh`nxYit;C5ZWB^%{yyx9JQLQdZTHr z?sftJ#6~N@N-75lLHwhC@vjaZKQTBksC9hf>ZPgKot~q@AVBGGw?2CF*R0j3h)FY9 zV<|Z}q0)Mr$UBi9OF_YlT3|92(4n^c*4mjLZrZs;Dp-yKF6KV{8=Lg{|&|)sOZLy7ka(G^29HE%|D?q&%fE zj;l!+%L2$SPPVSNH)>7C^K!+b!vl&MuE;vW88?&NxU;0tFeoOr>C|2zVzh>Y5GdkC z<;GjzhRopJCw?N6&0DP%_vYLtGn0FMG?{Ioh6e_?`F8zV^m@!$oKBOx}>P z##+PHk}-B}&&=59sB+ZGN`;7b?VM~Cb6z%IZ7)ZDOJ$3BqJ@QpmDTkqipd&l?e%L& zOswtbz#yVRlEe%^k>Nw5!yA#!`67a7N{FGX<47T-v_#f&WXLik#V6)TIjFK5G@l+YZ7`p_T#Om@&Wqx7TZV_C6J(Rx4)pv!Y1tj+k$^!WZm>OOj^Kczc8enm?8)YDJhyuI-9%P)62 z9cy`R!f};qcDk)*x89)54mUgD{5v0Xx?vp0LI^-SFj+V;;`)BR*+NaC1=(B6HPt&v zhOG5mS2JrJJ4mBt1)!3s&sbu{8>^d_o0N0pNDi{X3IIcZK)Xw7XW8*L&9$u<5EO`& z4w!C#dMa~LJ;j17eYxN#6srW#%n29N3hdEo@5xzub;%%#SMHg7)_vxwhhlH=H^2R! zl1f>l0g#d$MQ=&0wR`vNZ*_uemoK(jO#sl^?3;AsIBqoJW-Z=o*=j(BpdExdj$PNy zWHOfc;e8_`SrL)E8--mXn=um+i{umo?1tgf(<3F{e{E?kG~I!`6In29*=jIE1nZ%0 zX?7)M9khluQ)Fl|FNc)PInMqOXQ>gkV?>Ec028E#X$jFgJA#UNuTi5QvL#?5osS`FBICK@ z{<7iTX$9o@ncaK#wwsN+cWzm0jWG-`Q1tV@T-l1&D{-rfktISA+3k{aV#qkcQy5um zEU=}7FX=BdF$>^=q-tyi95G#xolw`BomRUYL|SXBEd~T_O+^=ru5biP0j>ZCh?qQO zU1@d=N?$SphEe=W5AOWKfAIV#$BLoR#!?W+hHR^=yM_?B8^xj4)>1OU+mYhprjA$+ zm+i@3MwY&Bw%9Y+{2VbJ9;=9Xc@ zc04D^#*RQX#7ves<3b_NLOLX%^@9v$A zN(C@UZbA@*j_xaZt6*mewLrk_2kd1}q?gBgMUHytCvCc(`*k zyZg{CPf^ZuJy)rX%DPfYN}1G@PfSkj**mwnzBWI9#}YGG1YrzWqelxlA%xY|Sk{)3 zb857qxqbVsHA4eqtxhwe>XBhf?7Q&zUgy#p2eCF$ZXsa8{?JN)H)Y-S* zIkT`;Q=(ji_Ll9KHe%axz#$z3f&hI5fLQ60t58RVYE6x}88@5lKxwVD)&NYz*=%m_ z-nmYzar5R4tu-Pb$Ru$90HZ~RESZRP(&%hBwqS`D7ZwnqUTdPD!oDJFp_Gh6&;-L- z5ot0$tpQMl8B_ly&97URl0B|@b zErBH>Vr^_-pc9j2wsFMF-EKGQ3ttU%B61uV2C&$&p%sy_X*UZobIO=AP>dj)gezwO z7#ZH|ax*lMhPjD6nNB03yelmLqR^l;J3_J$7(|+ZCWdlXt}Z^XW5P6-FW*@yyn5~@ zpEz{&y=&*1y6AdM(7#tmiAB#dK*5X*!G8TAQehBXKCJEMqeBI~g9F>~>-xn6aF9GD@{bJ4)2M-04yn z^RImJfz4X0ZFyyFv(}A<9hvuCR4V2;wl-`AozQj-bVFNh>s@TVYNxWk>6SCQAK15ee*V_ot#++0 z5hhB`pbxc>mpY=})pw#`qUiaa$|`gmxqCdHab-K81UxAKu-1-^Pwd${x3#%3fA?;3 zY8o?ZZQ?jRejw+GVn!ur9%6Df5nwyAy)gk`3Cq4jmd1txMWtgJEsBht=J@1L!5V7k z+|ZaN@rMC(tn-e#xD+%xH6n-^4Y4(L?!mF+`^OxBiRshpmFl-%yxfRv$#?Enyw@+q zm1ex&q`Pa;_02fj?fmTDIbGRkmh)=Z$K{yLy?tfZv3*~E`SPQOCJ>+*(Sf}8^x^X2 zmhN0{uGP9tYuDPk9++$dCkF-*QGXQxppa?*;oc<&_pd0w>n{HtVYp_^j2t=+u=rR86oG3 zqHDVmHAC3FdvyQ)>Cv%?`c|b`Z+!Uf&FQjVjH%KNfB)SL^ELy6l%kwfu~h9=pd@ZK zqCff8nFDj9duE4KRuzigFaG@}8>`h?r6#2vDEb4_qu@4YSKFq?AGk01kra{Pfx6_Wf9rISlhE}vg zdmkR<(Sn1V46KwhMZsEhJ5sSAAtKMQh(v4v5nw3eMyR(!GdqxZcwhF~O84x1`?+UM zbvnVd8~1V!E2ZB0;M(1-z)_fUl+1~kh!}u?wE6iTfBf#sX15!B_R&4pKDhePt@RN2 zn>Sbg^`6P&yC%MUc|i!Vd&1j4o$1!9Xu=cwMqAuj-fEQ!xj?s+5CG_^ zX)r6Q9o4GQuf2Y?=qk@uQV7qHd8Gzj

#rjP6e?$G-R}iTs_v z{GKuC;4Wiq9P7W;ve%C3+Lk>&DvZTyK(&xIH|tTX`-6B8p%uXVT5Y3cG==ZPs?s41 z&Pt=TLhJ}sLI8q;0wrcW$CL7@lha4{ms-tw-qpuvb7UyC))6>AUwQq^t)XIJ#xW%1 zM%QRd?a(F|Ghln`?%L>3v9{IBFl7|}hhP12wHfX7r6XW=YVff~4-B@Jqd-HMGY#6eEwmDCBLNv^l2=0qBRn}8q{rU?Vn_KBAqKJu^_*)-RN(6jjZy_XccA<0U zPCJNVDP-@=A_j(wYvCS}>$R?BUT!$$LbjtBh=mZo!rrMDsF;gN?w>CF_!E;6+b;QS zW6F60rb%2BQiX;VR%_1eaCcyEajD{wWhpkALA4z@0;b1G67Bg*ce15C2YwK@2a1{T z@v&OH5y!FP_<#2^4_tr8R#q!YirAuN(TQlQW3(m}bI7tx!fcnmh zuU)%-QwT|9r4(@--`}=~y`mM%xML(cw{N^r3zn-)8FMS7j>c;%HDbu*5QrSdaU7>tbEDOJADm0bUe|S_D2!u$|M4+N48{P@9-H`? zkMAU2%7Lzi@y(5nCd#_1oN=>)Zd|;#Z)T7z z-MYI`$T&(u#+PP=(8-{U%S9=`S*V)V?sNl-zVBFLV+#a0R?5td4_7;}Qp$DRq^u>m ze<6foqJ)gnS{w62Ev&tG6voQ_PaG?_w$qM9%Vsv3t%D<(3#DwO-svBg$iUW^gFC18 zP7lY?TFtmj{qx9#08C0@JS9hRzGb*l(bYhQ+BPGO32Fi5T*riQE3l~Hk(0ARG zYpvJ1+eve#&px{U(+|yUE!^6ywW%<6cCqvJM^~8H5m<1PBTyX2*V5@_e>7BuCvh$rzQs0H`^wPEm1DxF$hhdtxXC(EG1Dt2^2(RtWAy(NX}0X z2(IHQB>=!$PGJtSwN~8cCbkb`17K#A&;-eo_m$#4%|~XILafwHC+fzAn_6sjZ9|Jt zQ+mJ`Gck`8+-LWfYU_(NEkfb9D{H%kN3IW+!$@a5H|IG@Im&T4<5r}Kft_qNB-QQ;N(Ki7)FX5pg~*(`{oYZeoDRgY)+C3oC9H=r8i1{}$#HB% lHW}-4zb@QY438jP)9 z#6Jjt0YHH4fC#|Mh{()JDPa&1X09Jf?uDI?0uey~5JCW8X1fYfZf^dE^;CnO`)LSQ zOH%|O2w?9dBCWMwto_7}i%?&7G$JDLuwpy10YpR+4j)?o8x9h}nF&CE>{LzgAS5CH zFd)n<_Mt?oWI+%F_m#M9B0^!dfbx4MA_PiA7$kT;7?5gHz;CzxgoN#L{Rqq~A`!(E z;J5~|-B%>2!01s$(Fn|JUyY`vET}cV*!a0`zG}z3T>gybzvj(q(+$wx{bRrTq3-%BGc+U;9OWdbYK!5Lhmp!cTzp2aIRB@+4V zb5jDqY9s6iViAJH01L1fV?5CYq&|sN9^As<|BPlfA`A|l?H~jsfGi+_EMOQ6gSfYL z#vwlfAONyA2(w+kof0$G;3EK(8aHZ~k`RKTib^dsC}e^YKk;h}cO_hGcv&77g2m3w z(^YtLu&A+z`qy@D7+m!|0qu#1pa_D97!XEc1V%T4-}neg5g~vuYhVU3AO-{g5ClO$ zSgJ`*?5sTl!Z~aUD+TV)ua1KO5z*p;5jM<*Spbm8lZ5|?$1%g1!ND}fg=r55E7R@9 zYk%?gS60{0+<)hn-}C!>56wd{6dWXm<2V*=DT@6rupf&jw=BHD!VMf_1*K4gEvEWA z9i9M)+fDG3BqAZSe~5}G@*5XtQTObr#p_@AYyW)n*~gEZKKbN{+de+Kf47uH5re3N zI4$!6BZD%D5=3bKB0^>c!xCHozyKaS{7k}20V08lNGTN|MM!=v)r%UnDAK5*c&ti-Yz0Aj4k)jmQ2k^mUiAUY5| z?B|MWnQj6UA!``JV{sE!fC>cB|Ban4evLwCS)Kv0|5o3QVVhrF45W!n0gx2ajrm6& zJo9Zo`u(r|u{W=+_rCP$e;;iunZ-q5!wR7&QmiE-KYs?taJTaG5b}r!z)&4aq?i;V z8AJhK5hG%lwFokU7BKMt7|jutz;x2Mc=YVmFT5$4yTo)hzVdIsclyq+&g|Ypd4GhI zkfNB&!bl`0XN46V5Hd~-XpP{ zk435wCHxN@r4u1q+!yjU!8o^ukf)GSF}C8p`w6PC=WRhs^iU87vWXj@A{9rXf$TGe ztbxEbF_RiVUg(EOmgEJBt-q=X!~R^*vlvA58dmsR{~~0&gzlsW5yO7P2!3Df9wVZ1 z8v$1AurvxH_EvwSiwL5sNCGU8J6Kh0)T=6IF)-Hj5lkBZBsk~* zz)rx8iA>|Cp92V@hyh?kv1h53Yb^XHz-XsRnpr4U1sX8Mq)Bx0vDN3j=7%mUcQPD) z`Y(TNZhk5^ltWT*DseJN64fNP5}t@~rTbXC_p@bYf8hK3y->plh>lRh!Tl9JrHpU@ zkZL-227l!3|CG(n4c3ZJyyv&4r(?h@WutMZnL$wmX%rC=DYSIX^`DrCls_rMjqp2I ze`N*vKoJH26hHyLiPgS|eb9t@9wL80Wr8S#f+Qg_R4?5;C)g{D<0`>+6dqmy6d7;~ z=MWwFZ3Xn?Z^!0s7YYW%Fmtel^3=aYAP_{TC+xu;4(^zs<#g4-IDtA?8BHd|qAiSE z7(7cI+q`kA2tRSsklS(r056nU(wZRAa7(ss`BspE2m)J3S>|F@OAAR2VnDR87Pi7d z1XjAW;s!HODN^n!_32AM3RQJgOh-J6K@=je${8RdiDxCAn_y+Fvpeo3 zcBq83N{VNT6%)kbAcgLv^&d_U0yA4Jon)M%YBJukG5!WW99Gt#G0u&4?vt$D-9{LW z=6+&lZErvk7`IJW4!mV1_TOzy683&SF}pE#9CuG8&eRs!@vCC{+rR(00gKovM;ss)PYBy(40|GkajObjMa_-yvm6v z;c?=HnwpM;gwa|4f$YCTk<|?qjbk{1-(N>%<4EEqmx@Wc!;Bo$m5S8?VITtey@B8~ zu-|$+sJ~$w+77q28ri;cM;r^Vpfz@avpicYaDY(gtmM_&>{LT@NvJ+xz(?!(JcVW# zPEUY&T=Ms8d8!pbB~64tfWWI2VX&RS9>DnCgab}|Tg}rkQ-x%OT5AM#R0;ryP$5XT zcZg`9wsCkm*(%5S$A@5%F%>#+zM>O7y!0^vB1Ej2xb??X-VUoBjzL~TL}C-GSPv-? zR;dz|W@xxI)x2>2?3K@Z#g$LH4jDf6e}8ju;n?h4J1Ke-J6Ml~PvwkrJT}~12P7*uMHaz zr4%AsAHGt`Qe$XYLaeQQSjBw+kn`F~-?w$3ut6%T^X{%6;WU=uia{aRQhHn26Xg9}*BT32`u ze=KfH>B6*-%R~g?gbl}7GR15>{O#OSwN{Jg&JFE$9#oBNJV~O#5KFQTa7m_T<25l(>@#tF?=b z34t*|m8*Fwuo}Cy@Ik=gU<-??Boh~DV*e%%aW7~(?oL2N;)j}0#8h& zwZV@AlqzpJ)E2u*rCp>}B_NQe+^p1Clv+4LJM~7=*gU;4f62}pzVRi!jr_#jpY0!g ze0s+LDmR-AF$!Z%N{m(*YihCCY6SrB){}afB(T+H=f9QhBv$m>HZDtwSWQIv-2?!o z6d3OHdcX0Zk8doklwEb#M}NOvUWgYPrHBz`<~WZ1X_x1@6&Wztk?nokPMcL$S_}g( z!g+c+-!fa70FHe-5q9JOW9`Zw4*s`+C7VO3)_Ukk$Wel+{JC1j)QWAdaPO%y{#oMm z@oCkLvb9P&ZDowA1M@_%g&}H^gl|KsgcWSVH754JiU>>cl9}r+H^1u-%kp^hYn2S%W zzvRdNH_3Ef_TT&aZ-hAQQ>7z%Fe(+C!ZEDB7&Lzo=wP#04AwU#GaU2^h z77+5(5tUW~u=;PPQUFx%gGu@g2LO+9Hg4WtTwi1Iog0N6($*vsa9FD+2F_b&rV&=c z%JElrliS9910fTs;O;;1TY}?EyNGB`bKY|i6kS+n(k zbJhH40`lk-%$l>HlWc9Yi;V}3w)kQuqC6_+Vz8U_v&N_Dx54AL$2J@9V1?nW`{<*^ z?KnOFHw3c>LalJ-hQRSmi}T%CaJb?**`6%J(=s?6G0;NDn*7gDbAjkk*3HIXzT;ol6K2NZ48b^-ca+w98 z9o2_yhQSvk00F@`hs72}H<-^h4n_N7jFoKRgMPw35TKsx7XJ$Xc+ti`vC`n2U%>Cy z1h}@&eBzUEJ0}k)gpj}Z`|YExbqRfP2i7X2g{M`ECZ1@|t_i3HQ=LQ<`v$h0_Vr{c zzmf;$BpyS}=_cSGVzV)(WHTWqBVxu=ibPFkryoD}g4e!kaqrbdPv7>jzfNJuS!y(? zh*eB5Dw|QHN>MJ{Q$i<5_#%w%BfuaMWNaf4G4QZev{YSEOP%Zm%nA4?q*z4;&4r7- zZ+hKNtv$IkJ3IUFcl|o6foipeqY)ArW&|Z7MNufFd@Qn5me=|kb3)W~5rTBWNABo# zi~0jKlGo6l;7#L$Y|(`$Byp^QZ2$lhz@(3j-34X{t5iZo^0^jC|k1gE&+q3C4&-M6A*~v0q!CVSnyI zqk=mySO{X3bAVOmfwljWRI4LKW@Jx_KHMXUA`IA>1)YdsDR>+83E_}}_xA6wiz-RTdqBoiQm$mB_-j<~JXWw7$}`K@PgDYf2X8nWFn zuxu66#^o}FWaI7;9WS$BR7ERp$gv~m{>yLu>h<67;X0}xad@P~*t^DvzEWN3H#~EY%$Ubq)fz=2p^qRkgqRq&3dx_*D z&;Zfm=$NMl06~fMHH8y!`(Qu|!MmBgy~8_ttwTLr6ElZEaGBpwBB)TD zVPp}BFbZO|?T#1#Ac@^utc4P+672!NfU&Eqy-SueIyMDX_^RM%&~5DVL+&N*a6Ullc>I--r4X%>l9}; zws41tWflbsB2r=6)4RIskNxaRul+|XFq@gu+ci9xqlkfku%`k0*&Kx!$^ zSyduJc5z++AjE(ugrPZtY72nd%4(`sfI<)gLS{y=<#b8 zhysQ{S@{LofCvH#k$~8fTu4MvsyIa=W<9ny$dwV!DIFeq_6{H40YXL`$3#Rl8ofQG z!ikbiqO3emUW5i``5M@M;yil!H4MyHo@D&O?3zq;`opL^d|Pd5^QXa-Beaao8- zpebTB4U#B@fhuCHh^%ACmZV8k1OUiJbE(-7Mk11CF$ggdX)=(j6j&n3F(oKrWRh4F zW2I+g+^1==Jw#L`s)!{6>W=im%XU5S*$@2VAO3Ql4`25`fAOkUyom6BUnUd71FguOMBd!KtB)>D+Gbjh=}jMZixOA*D~U5M zWIzzdN{W#ZokVd_=z>rcU=(H3)6GVjZ?3JJIlgk?_{!>ev%1#l_PgCKh}rB9N{)-7 zNI+k4Xm78(zPxniokDv~Gy zL@>-6Bod_zK_Z}qAyNd)+7Jt{Vdf%==jZ2>OeL`z^^2u5D~cp*8iOR#R#GZmAT~x+ zCMlUdfzlx-^?)^XxaJrg5el)^u_B|odH1=`J^ahB`@Z`=`;q6q@cA!z_3N*`>2=E& zPqmw^VUZiaI9ALqQo$0k8w$Xu3)$0zg49YGJJF4csFbZ{5en&Og1!JF01+swj|(WexO)bxnhZ_-_?knY^m!`%*pL*A=oA|FDUIlmFopU0N8*HFM|1C!BEm06a0)leCi2s73>NqB*0kL zxPyRbef((syD^_@jWN7Mue2DkbzclfjFoR*fQ|Dji--}&wrmiomlG!-79LZUwfWOb z7;9iw9H!dDI&RQQ&NeR7T5ZI}iB?wQJkMJRjfiv(DaH`VP!EWrIGf7aEg25_=Z_wL z`0MA6J=%e?3bu=n&eEtisc>1*$FP7&{ty7f8(o&H&DletUj7mxZ#p7wm6PV`d zwuY);ETS2N7)VTJcwY}+grWVW49-=Q$rR2MshnTE-=^gZ1mF$Ol35#l8jGC>yFg;sArXWby@1~E)@?W{h3;CZg+XP~Je!7GsK{8qp zl`$qwxzedLb*+Pdj?QJ5mt84OoeH+7-QvS>EqC`!_1 zn*gpod{DUj)aO2Xc6|j|Hr6%|9z1;GGoE$JEngMGR%vUd&HiXKB6anZ2Tnb3_P{j< ze)B#5@YsFlEJHWKMNyEbByFm+nKW=Z%M?MBL9R@P(5#vgW)CW>z6}J!Ec`l@3PZAQiI72U& zWh>ro;f0Pc;dO#{`gwT#w$t@ze5Eqjkt3*rrov6y8p!Mh_jo_PqkdN=OK+jePZ_%q zah$Dy*8v$=p;ZYZ10tdoXo6U~tYfJjT`v}Dx22V6yu7+)BaJ`Ptlk%f$ySrEZQfwD zNU4ykij@@+h?T7x;u&1M*l_`~;(f-ORKUucu4({cz{vb-at1kzzFfI21yO*+7^76o z2CPI@mN~>24Iyf@WTDMvZ}o`>&p&WyzOvkG#h7G6hNYGDL9ZuV8j~aOY`ZZtGh;9o zN-o*Idt>?hEw|nN$de};M(xMFD9(%?f{B=n#I38Yy5^Aw zA9(sRZurf2{_%s4EE`Ctr;}zo69R3dvvH=gvbMasys^G~;mM;H9(!c2+2{{DcisQ> zwe#mBYO8cw3v)5-ZmtRF>z{Mu@4WkiS6y}Z(Fe{*kwlp?q)iDegi&yCj^i_hvZ06z z_9TICOJS+gp01z1a)dXAFV*Rl?XXvJ> z?l&&~&2xuw@leLYE=fQ`1@OvUg_T7{#E^ihthxRvA@*~VM=9jgOis~ zJTRc*;#pf`?E&!@lSl>$tNGr8jXYd_f1XBEH3PxnQkx4wURU@jE7~RWu&|Mo;z? zwqJbJH$LZuHypU(rtkdU?^-@}Hi{Aqeha`=&{Lf(?$2YW^b||dSVj5*0HR{nj;+}6 zyqZ|Z%g7#ukc2Alg?+APD@eu%0U#=W2)RUuT2v9sm{5_3;QMYrQ#}C3q^T-CvSJD~ zud8FpD)L}vZ&~nSl7$W%FRj(8khgSNR1VmR`2G?GYWRB87pb!5Duu-ugF*3LtgJj< zhV(|CYWvBuGeV36ZP2D` zYy|@5;#q8P4f+HN01<1-wSe`?qq0pN2uyg8dXfl) z9cLne%2aHnGBFz5A?)y`aw?HO^n`7qmlWk7O&g69O*K0=HPvV`GbL%9W?brLU9tD!yKlY!iIY!U=q`0ehc7wQ z*<8n%h*2>CK!HV^#rddIh-sWC6%PiwEXyQCqIgvH=h{2=UVTj~j_$qv_E9<7yL(53 zAcAY%eoGO=c=+mypy|Mmy(Ubyxp{iA19GpYBhjyf|oo~YIdYz!AOkWvUP2&F0jQpKd8HH$b@ zww6$(JZ;+Zqat@#!LK5E13(}VlBq~vz;3pPXLB|XCS)rF$jtb@+fIieryevIxTAa( zNJR97COtrXI=@1K3i*6sg_6KIUCAca0%#}apB|F}05NWR?69uOk3Fav3I&l704ND! z2KN1iDn%*6%JfLc)|bRW6(bXXSkQV;!r)p&v~eX=*&5t{L?RHe0Dva290Y*e)3F+& zGQ`-YU?8|TGpb=$ZfiaJ@TwCA1||<%A4d~1*1%3|HIzzQ6meft*|cAh?U z;^NuUqhWuPmkc00jG`#d^G2fqK)qgXFc{@|9z~!v&(F^{vsSCsO0w9dIF`fSFgM+P zXL09_JMO&a7k=xVKk!qpKk@LXsG0e5G!!3vkf6nO1(j#e9kc6V>f3lR~hL^NAB^%#?#zn`SFr(CIGDg@i~JGvD{MGp;MxggY}#2ns@> ze3gKS8!b?MJg#Re6i|-;0fDRXY_ZmU9NEIUR(T*OODb5w_%1*=k34uLB39suFEB`?@Jt&NIRWH^r+vB|}h%nGO4 zJLeZbiRpCvn;T0{-ub1+?z(&Kz@-G`-lm$F%0BzG2YPwhnr@Y*vuk#4|IYSwyJ?Wx zS=ve)IWp?eGoJB`$M3uA7k}%WGdm8huPohg?WG%?&QL?vn8{KEpmw`G8jVDxC<=?p zk;(>xfrvC3O|A84G#U;E&1Ms#NX5y{9lIM@0=igRT^<#Asd>=v_WS)bO>NP}>FMb> zjvI|e9LLOD6vb#XGR7dPqA0X6BGT_{HfH9IKmO=*Ui|WRy!Sm1+;cKc6JOvWtg7M5 zOh^DaPe0Z65kOrQUkvxleDA@74}9rk?|Q>)pZkr67It6#y}$dRv&WYjVlasWS&Pa* z_4Hn~aljfcag5Byz%lW;4S2Kk7vAalX~_F9RGYCDh`F5KFPR8+0T!|SpfixZbYq; z3swe<0g6S`j+vR!+JpCe`R2zTerRQVU`E`SZKzhOx2fARGy4~&o_pEsH(Yn=7wUJVLhOMed170{|G-3R6a-5CCF9 zO`w7hTL>ak5i#;*%EQ)^wMPT|o$j{EXXc+MfgRem!a!8cDl9G_r2>7;8X)B82;Mw~ zyhR=afQqdIyC@WJF&O7QXpaf^6cenDl)?#l2Vfg%#jn~+D<0JLg^!~Pw*vr(V$^*| zQVpVXUPwGYKY#Y&uf6*>-*C1!Oq%V8A&wfWok12wQyD^(UVY8w!%|H_@tPN1xp&v@ zTOM0}y06@SY;ig(hkZ;kjI%}@&9t)V z>1m9UZnvwoR?5{<8xDsehA<{`+saqX*5w`IV&A z>Kfg|NJQ-ZROhh+I!^dkL~8U7+#NOHFjL8+B)#g=*6W^q>4BZo2lmar{OA8}dhyC( zcatJy4O-;Ba`mmE%Gyc=QhFAF8ddttw+0P=FZ@Jwo+j+&@r7--T+pCjswe zA1k~n0a<#>v;QP~bVvpsgQ2z^fCyU?t>bVYE(uwXL4bwEGY0)o*2GIlP_q~D0L99M z5#$oMHz7|60f8(Ma25`N4j>>Z|J!Zfx~9>UtYpoa{_LosWYvi36B1)QRRc~;%wB3j zN_>h-`O;6d{gp&SIYXl`Qh$0w8QNQ13 zW+Gr=t+f?2k|gQ(`;BI*NR9H6h*Sg1G9Qf~j-%z}<>~2Z!qITl7m13Zu!$7Lm?TMx zqC~{eXhf7DD#NBM%c3Y+t?c2u9{9z-{>#Rm9jEU)H8nL=3O69C;6%0n>jW?%q>D;h z_iP=i(+d#+OOi;UM44h~BYD#we&l`s=O@k_KlizReaBC{_1)cva)oIWM`+@biOB6y zn9@->iGbO;SpjTvjLlTuKCr+t20J0gQ=C1)J1fC9VX*^7v<*cB2bD8r)J7Q@XDf(k zV@$#!D%=VTjaBe?E%ty{Egn^+7|+s1;Q*j(gl7#qdm z0tiD91}e=6D)A&_eATcOF1jCzza0_BW0_pHL(f3fc;kd{T_I-?5w;~l$##kba*cFc zuWZYbD;zLvkYYl_Vt~|$OtJZ?k9_dC-}=pIZxqMTdO7NJIy0@gwUzazD7B+~V9%b_ z4Q0eEUATCn+kDGkeDtNyeB{O7c+>Rm8`BM<13OKc(s20IuiY}!Z0G$oj1nM-WT+J8 zhS$0S+~^T;yWJ)tW27jGiY%!t^C*f0jEay&lLE_M5V6-Bn36g3)IS(;Y6 z5yfOosT4BD#+cD)6vr_UOixeu`~A_V7z{>Hl(6PBj!!;$IPJYA7pSgPM2jBghzZK8i`_Sh; zdc||Td-dc66A3UXEK1N3WD^+cP_C-02^Os^8jcGATgCHGDhKOPs#jy}8WSdn$ucgJ zmRK%#=IUZ2w>N*ZNZ7I{~6Srl40CV?at zA}Y%=&r1NX#+1rwDq0sJ#u%fN(pnFP!_jDfNR39L-`mKGVUomoS$4Z!U9vDnQPgO( zcJACcGc%KAnU!&vx!>={ajdmw=0>9dVn)M$pXHbS=nu{u?KG6q7_;uj5}Vqd1rqk4 zsA_0Y@Q0OQE8G-M)foaAh>WH}x3GEYk?s(z#uM-nw>ZHis>?fCiAC96u@3L>SB`bMSecrGxFR614U4H>PdwkpIH+o; z0l?=2j%}`6uk(UY&~7~#jv`zB)*`D^WuHN_mO?l}NQS0sRDsf*p4qw9>9-qk){1BM z>}(`)BcdJcnS<#r-Irdc-=1xt(V0=RbFEVHRA%mf;`FU|-Fy2Nzb27HX0&nc?7qtm zL@Mj`Mv6F2BZ<>2ZKtLnGwKzE6)Tw8dV-WvQ4|$LA!4G)Vy|K5R;y)Quv%+via3sk z{a&xvOOmA5>#we^cDp?`h@g~;r>CbE7Z-Qz*m3yq;dZ;3BnbdaP0h^DFS0HX<)H`e zd*eHP?~=Jg7ne3DZYwSnC4fe%P6nP#i~wy zv3++cUoBLwF+>z$gMz@q0))n9`kIQZs>*fZx@`am3kxD@5)rXQ39N1^A~7n15JC#9 z6Q9A@c~Z^S3`l5N^JZouvTl9~AV`1+$SBq@tP$KH1i%W26!KV0t7-xWz!Te`fQ(qb zRdr+`+9+6;BvGY;+7Au5C_xdE#LD$1ViW{+@daQ&Y=uR%1~{t<+KQZj%El(KFrkR3 zP(T$Bk(h{Vl%z#{5f%U<6tSU5HtP)qm^D$9aJ)%rlML$3nBxAbe)NJhD9u~PwPfzuS#oFdM-pma58q}0BYR@+EkuHl~5@$JU)LcdyGfYan z|Bm}!@k2lHec%7Xci(li*_vi40R+h)DzbsBRc0I_MnC`pLWYg6-b6%bEwborgaDw( znISBi32+?6dBIAWm~$hg&GSoppMLnt7k}4B{`Suwx#h#r)cgxx`-bJ?t1VJKDf^cE)I7sj7|#%!I*p z3bZqsu)NsA398JZpyr*8#+wMzhp-Aka1z?$gW)X0OPE8Oq)la0mHF1%KD}SrAJ?)) zcwt}GXpBvwuKEsRP`9xv@*-PQ#Cr0fh$xj+I6Q^I*p~3O6EC1rYPLeg0-%;Osx_h` z@3H#(;YC4Eu3fj~R}$D$UFq1ME~)>jeo$~*IA=s;TPqBR(Mi!(HeoQi#IVub(I7W^)E_8@ zkv17;E5pst+;;bD+`9F%H}|xD=>7-3_m!{K{nd8dW-f-TTXCb195GfA8e@uF+nu%X zY$7H}5{sgMVLOzoKd)xUbjV~?GWq6A!Z0cHL19)Q*^ZX26nV6Y2Md#m+V z&6~N3Ui0lMqCC%Sq#pp2f-K8U96kS|zw*c5{O#X$_TuWDANng^J-@hPrZnAw$rwyQ zCe}s~0COsklQ4Swy|-GBh^ukIY>Y9!0muaXhJ;`njM-cP+(N?s&X9;4qX^3Gh6q+C zuzJ^*ZMq4Sp|-}?s_&d%hWAQ@v@}71uK%O$eIc$*3C;jxJnSGFq0U{cWM#JH7 zFc=^J5v{MUA3JtTDYd@7ZndN&NtksMMM|l%G-=w1qDYtdaL`SlD0QwBwW7w9+M$xU z4);uCVv?P+vyCZ<5FWq#wg(a&gqWUG>$HwyWYpj!vcYE zaDp=^a9D~Gk@`$hI7UUFfWanYBUY8DRmst zKo@A(d!8mqK?Fcto7&(aig4>i0sv%74p$uus%zUuD3t-OMlEk-Av*%WIJ2qsZ5gW= zT%G5E_c1KKZ|dR>;r$_SLv*)9!j&Y62;>T?Roo}ANjt@AyqG~fIR+3!AYrg&mW4q% zcktvDibdNqm*7Jg&+iJq!tr!31KzS_b?mXtu+vm73lS$K=2z-}K*Gp%p63KSpX%HQ z4hjhETw7q^E;H7zYRrz~p3C4v9|2&%I6{CZ27naeMwuniOffT?C2^kTB4Ui$+}!N- zI~yBogTY{PeSK8qagrHh6w=hx)YNnXg(Zq(r6{(3VDz*h8c_z02mI3Yilcr*lIQQ@87?;IG^XkX0wTi zWm$@tvdpdXSZgh;lPHb|(^$o+GO7^iOP)(%=GxPlYHV(tcm9A*;UOsonzAfE^na0IcFkZZy}WV7#GH_|S>x!Z!Oo*4on9w8m#1AVGb3wD)c@ zRpr%_K!nszl1xY_FIAtYFjC}LWND027K6du%q%l+Y^?PMgQC#=!3c6e6&V85+|JVZ zg_(tg`AhciiDD|s(VoTWTkd}7D|g)cgWvb+6Q|Gr#e3fS^yfb7MVDT7y0aRmQEJ#Q z^zyDXFPCLmmSwNsmrAc34u@-NYxY1}U0qd5&Cky#NkV|u{;HHpl4Lj>mSw3T9F0bg zojY2LHWt&JNiy45$l`?8&z`&gwy%!#+z!p%}1#j4GEIiTpJ<+LanvUUDjH+8|kro&cF88 zf8|?V`TwmhE#Le3zde25*XMWbAN324R3b5{BsV%{$dQXt?jP#AM2+J^jUFUpZ*5Pzn={>JOnRafi+V4qIF>NL!*3MI;F74S_}D#bUM| z-Qghh!UIc?GxpZf&=|Et6%rDD;Gw#_Gz> z9Se^?{%EJuGYtJfm$jK~wTaOCW{Aye5#e*_0`XR z^UGg#_ucn@@I8MzL)o|d*nhwAy6XqMVVX8Z(vM6-P>V=e=63I*D6&GhBAA((NsNQ(S_!<pPJ zkwJ3NC$3Q;Kwxc#rjt>K-&Wp$37swv4-?>xYHr67llV zxOh@SY70!qxW@Rp9ETe3eM62nZol@Lq_GMoL9`7$0GyBj$boKU0KfnWDMoT>kSHPm zra)Q0QjZXvQNiQJ7Pxg?UgfrLZC9}1enN_>MiLCbR*EjltdMpiT`WzbCwc$u-H(3k z-8*TRWtq*^i=v2`lUT(nib!eE?Pd$u7|`>J3&mi#Fg?Gy(d~8e`MGwk2d%92#FOXV z{hs&y(OcgfA^q;}z2haXe#NzWcde|fVI$Fbmlz6DXsr=ZDV3%vGi%K@=$6^qIAAas zD5c7>td5PywJ{@UHk)N>y8Yh5j-5O9&E z>!*(2dwO$o9g`$#i)v1lNU6@^I3^+igCs=cY}Xba0*l)=*1bBCfKk|+Akjt=ShuaP z9T^dA9=2@`O9V(M#87s6lFa}1Ki@Lw4bL5U{N7Lh<-*P>l7f{GiWt@kK^yN^w$lpJ zfG0c>V_p&0^)ZmSw!5sUL;wT`#{1;>{Hckbc#+Ljcm&%h%$}SA%#2kQF9QJpRYhX_ z>FPO-gQiW&A2$sm*m@I|PJEcUAR;TIsyQ1oQ&l7l>&k{>b9Dn+c}Ylu`FNh?VM#94)l<7TW4N1e$N?u9WOxFIh*Xij0$lay@D~A0me+F&bh7~@o6umkXet0g zBHGvYtl(~l$lk8!dT{7DdmII5z#jU^IKzFJOmP< zI7>W0kdtL$?vR5>Pyomn2B5MuLWB{5BGF`+kOs?VPha)2>z@7bSAXLDfA#oh|6VO# z{;cP`@ZyDyD9b>RVk3&;0t`)Tu_Y5L5V@uwHs^$>N_?pgRA2eYgmJW!=QpC2o`FLg z8>1lWnI=MJE3Keou`W@|fH9IAE{rbOXkixZs@*vt5|Q$Pf(Tf1iLP82A%$BLmIly5 zTFAJr-z>-}kadj;gQCa^CS-&aY80;6oE^bQmMmgeM6e3onGg-e2(vO`ApsDGGnEP; zu{e9AlV1Q0*4-EV4!J-kwqit8KB{_RLWnjSgU|(!S>B+?fG}&jR;ldc2FW#e)gsye z017dKsDvqIh`ba50OBeeaD)IxlWU`Got!1IVb+WwNC@QmkfQJ0UKxzVhSk(Da#o~r ze+Db3vSY+H$&eTj^biaf6-kOLoTS%Xwc~-meak=p{14r6^LtM{`9LPTxckta{a0jD z(jDfbV#v&Ct34d)ER7LaVl<}g=S4p+vaEUL%$cDv7gjefE^U;>Xh0*82A15&pjXoG zzVn@b^qa5WHQ)N9x4d)zHB&$R!!Nn}>-SDIr#OnZ&~Y3iqEc~^WL8r^#3YI-g3WGc zW1|b8ilWT(QIb*=(O@(*q8XVX(uEZHu;1S_#Jdme`^$fL|9Ai74?Oz#$#V~1Fq)>C zaXZ4838cix0LB?5iDC^osg$`y6gD6tr~s64F=L>7w_rlF{d$yA3PnY>0EeYLHo^v- zU9+MHhbkfntiYB&0j(m55J*V0Wk_r=N~8411DoIa)4z4emCsvRUB3IDe;tsT%|)dR zL17ppiU`@7GLS%k0fm&S$fJ}36kwx}W5P&@A_xQ%5Usm2d*V+We4G_j= z{M0AB^NG1s=Bn^Y$oQ43cOythQnQaLQ=z^y^l*nu8hSBmfCmYlD}z0*TE2 zQoJlvJ%YhS$5&Pid#7Ql49qq?kVQa{t7I^8_0}BMGg3{=7pDfmpchA29a2P8L>jC% z$2R29o1VP<=(vjAuu5Y0i*>iv#jfiQ0f12DFq-0I6vYJ$2BqBi^r>HY<#&AGqn{yw z5&<9rAi!d#HT{Qw_D@G2JJm`uQx;t6W+Me-fLTRRnU_WwkvhE|0iHg4W_`0`7$O2) zN;5Mu$hzIl%da}{hky7FKlLNu_loa);gcsWe#f)^>uvYH>#_TeefG%NXI^^6`e1!H z9A;St0A*Q5QPgZU?SawnkK!oR#uQ~4B{3>h7A8rO(rBHdGF+HmmSwY3`!AkZQp4V! z%MY%vT|9MUJ%J`<8LQr?NFx(LT0~@ZfvO^}h%uAt5fvhc2V|uHS(#A~l(1mUM{IQ! zWS8;gb|E<7!_ln@ zK_kdOM$p(uZ9pJp4{6811S{WJZNdNmBT^b;|Ll2c1hp;b>pKiE`U>CxwS$@kDqDvz zyZ)~tVSAKGHwPlhSkfa-*k47&^^;@?V_rf#j70|bXaqs94mJnfN;|F7od~7sXKal; z?$1ge)h7UK%^V_9=asvXKj^jGU}?yLhF#YX+%uHNMC`n@N6iTT=m%4Py zY*7@AMx);wMp4pew5H>!(&$Djn{LEqZ}W*qzW&k|KkGHGc+us%_B`>GFQ2~c)~Ovk zMleWKf@q3VTNe*0s8Ynas!LL{ctj+Oz>0=oE00!HS3(sMtmn0kHV~;ZGd!YNEVHI7 zpCZ^0;D3GWMEOV|5LSG%ypC$HlOH+m`+y-NfDhf#d4NBHW<`?Kv1<5Dq zxn-l}IE&I{GSs}ur9RewV3b% zn2cc=o4hZ@7P!nq%JJN1g2?K;auGJ;ApjsQ`La?Yr7}r)0YQTM6%tjG=PFA*#J@11 znTR4JRbyU_UjD@MtFuEH6T|@qkxLL9hSiU#vbmY9<5--SU^<8xQAtw7(NKsfu-KR+9rL^Z8pZLsIFT3*6rOxtqJ^#8dKlt$c z^M2${f9v%>{L&Xa@ZhN^ibTY^ilI{0h)4vrW<=ClkA{U$2vU)>r=pF~=Fu}tgQ0%G z3vQy;?2@#z_SEhJyH6gyk5WhtqXq?ei%JK4ArKN9c?8P4OVpNL>k~Y}?_(&*PTb(J zP9zMFa3mI_yaNCmV;U4eq=0Z_idgXcBNyKIm;Z3dH#~RcV&}{M`mSNVp6rfsm~n5U{YxsfJh-R$U2~Al8y1fK~Amf2Tdk0I@2vV8g{pJn!-G zc2JWg1Mrw1H;jYba#^}^$-8K)QxE_MqXA(9Y${oX(He$fyLkDDRlycEF>#xF9n8)8 zlI-6EejvK$gcA>%7}Gw_KGJei)-0wHgsK?%DlfxNm2B2BV?AeP)^#Ck8yXLmw^lDu z2X44Z8me`iDyu@o^-ct9P_zfHW9e1QxDNr`f;Y7f(tYLri%FP(a3j9@89RUN|GfF) zne%UW{f~a~1Ap<*S3dn?uX@$VlgIz&-`;=g0}rcg${1nR{eFK`8rY>yh}mo8p?^(8>iySRDv zcl`8Ey!ty|{(@(oJ$p7y)0vr>X0zF7G^{Z`%d#|0i=xP~%$7&dn#J(!^wiQ|>B&bQ zhRy!fhxT88)#1x`%$2Jvx83@+mCa=lDLcc9kAHo7&z`;+0H&o@m6ZXUh%MxdT-9Y^ zzLXZg1&{9{w;>UjV+|lk} zef49%{|mDVQ;BGk=b0KQB!%1L7J}3`RPQp^GZ8^ggCJw6l3}!I$f*EmoZiPYYX=Sl zMu@5kOJlBSZ=PXbbX|U3rhgPgAz4C|fDm z)y5R}6hvDZW(;aUHr<4P5Ef+d-a&9&9K_aJ2!AV7shtoZd2g3S0yD;8 zu`=Qq1@WLL(*wm`SUz_8vZOIN!PF{;%Es<*!g{`oJYuTtC07H|P$BL)GmR zW!~v*^m;u?(kyG9Iej)#R1~Al&bkN-vjEIZ&vd(8T^OZMj6FNlaM+_LLV(t^I(B4j zJ|C2{*oZP<+A*{HV)y(Xf8fJ!`^_JH&UgNt6;G<56|ibi6h%=K^?IFlyG<0uDq2~( zAYv94roR0ZuX^T{SITH(bGW|JyLj25U01MV?bcwabNZos8_ImkFW=Zqrl8O!j+K~@ zS=1YWTpbw*05$~^5pCA%xKs$CF5p~te#c?^tKK5C<)EAsr&J2HG)O7L-l(h63{zS^ zvvle8&-sbB|MsmPdiU49@R8?!+xH%P-pkG%KTlDN!Ld<^w}@5Acl&vmGFGp&o{Ni+ z3h@(NNrbJJ6GU7#5Fm_=XZvA6R4vY-Zyhx+3_uR)(cfTf1C0-nRQU@kD7gx0#Z2Lw3{4wq`E`2yCS6*ANjs-`Q+b!q7lhTZy7jBqq3JbDN-V$l(JEPi*-}{X3 z{rb&!-TmQzeC9X*P^Z6~g4Ps;@FxXUr$urs-$ut^mDIN_t=V9&3i(k$#A1ir(lL9X z{Rn}iO6dtGSxTc@Dpp21I!~G{DK$sXjMUj z-PIu`r5%u~37{=GD9+&>a7=`XAjg@a)RtVZly6rYPMe$&7wi-2NC}UwmZ{m+7x+)N zov(f-;(KBWXwh{}B)Ac!(j+AyLOb;;OpqrYCRKc$r!il+D2O#4YmtSBwjn4AFmvR5 z$!2RtHG*u3ICUf)MmL69r$Hi1u$6}&Tq2+2kA4o-<0LTm1frR$QwRNngk1zM`XEcs zH9`p@P)2;Sq?)1F$i=Fim4~^5bAYBH^KwD6LDUlfu+%kcE1Ql3Mzp1dm=*QYJ-gxL z*H=IL-jCdR>+HS*&;5pPYPDLTH`mwJ$~=miO+sS;lj-Shr*r!B>X|dAHdmKA{a&24 z#6aF#zx3e2BPWh6?AkT#kMhBAc4jI@Q3@A!EE>Turxedg&#HwAMB%zbFc=bx{;r7f}=q zhr`v?b=Ky_>z{u0Rfl)&n7i-T+AF{LS&6zB1cIJNIAxhQI#w#mBpf zMo=9X6 z$nUxivTc1vmPMdT2WGasSt=V4p(QBg9u)LOutmRvA}Qf2D#{Nyj<|jBtjE#oxX}!Q zLBt+RA^_ksUzs_q(H}w>(bjTteFf_&S&t!rhzXOGq4KpOKxOB5SfY`j@~_!)pEfi~ zm8i0^(2_`+QyBxvi1gn1>E+vQeaG*7`GH5Sc>cG4+l!u?wWl6``0i%fB$I&C!{D28XIrlwkL)I2>km8S9P zV6Bxj*O%8XE-#&5U4=-o!I`O95jE_uU%GSldtdSLlS>zKjkkX7j^h_E95{R^jpFH6 z+b*-nwJwT5uRkb<-A;eAw|3>BYhLie=k3|G_~_&324J3fXud4_@A%t)9(9;mU$KAB zP?_#}j+EbU?TxGJOQ+9Xy!y)hZ~v!H57oY6^(bM;N8KapZLuIBJB&OI8;toPPj<2Z%fhY~&+TA(A z1rfH!M8#F4Ts0YQY8>-hyVM_MgCrvOe{VfoK}~9lDL~~u*uE+%R(aw!V;umnc8^Y; z$rEoq5mrN4=wn4fR>>Nx2n)yx6o9MDQ~?6O*4hq^6mo)(1DEGwPBH>e;Eaw8{i~mO=&hrDkiGraTy|Q3NSavO4T-bk_II&E~y9&lI{S z(=;oJVye|#oL|WEypg1(FGM^CW=MbOL| zmt1i;=H|>yD^ApCG}6Ybu5XBn);2e~%jcu0b@kJ)oSof091fm&sJ*t9pIGg$Z(O)| zY-LSvv>NS&x%v5-WW$*Cb4znO79=kh78Z9lS{ZAjGojJ|fI_fkszby47$S`$-o$0u ziaiCZwRnf7yI_+LQ~UPsS?_N2r05l+D2hg9(Ho6Q(7>Rz=?@1{5)aGr%+iJB^|fA^ z4?Ba+ZXe^gI~)?Jg}M1Cit=)VM)fua^YaV)4(#5ud*`m5d#0!6i@xSz-w5;tHb;Y{ zX{Q0A2PHlY4cYSNxXmDAKMx%{x zG2LjlqQ=VdCN-ve8=Ku>d2r9Zd%tq)Xa4C=XXbY3LR6yqgH2^*)Xj%wuF(v;U97B_ zew*AzSQACSQ>wf$8{ACWkf|lqDwvC^s@9fqCQG$AF75#0$1Y4ONs-jaismPBoxfR~n!L>wc zr^MUUqk>x8sI5F=m40EVB&>-nlRRSxy5XH(uNf4!<=V_KadgRDm z=ZZ^q&c&ndFwfUlHwWt-14Ul+AWkt(xF`|TRMzZwRxaH$cW!BUqoYe!BV8htRE%~` z&6b*3#c7%XK)>I2y+=!JLsw^~=SKZOZ#d9wvMfuZcy?xnS$F!qIF2{Fo#}RaV{L8F z@1vq9Yrt$`^k{JYg7gQ&eAFvO#QG)IKke|P2Wd2l2HiQ8KP~Ajb}n9?mBZn%JKDVE z)-Opxpdvw@i(69$XLjs>f|r+8NyR5FuJrT#hU=gC_kZ&CyFd4_>BYUB^_97q3=|~I zMv^2VlEf_wNdZDot{^QasfFnJJg$p=fZNB4-PgxB+e&T5_(-{s5WpQ&7FS)kop({Y z*ClI=5{d@>&9a@n{KtRh-0_Y4;)##^>2IVvrbx5W8drP`aC{~~|HH`r!yfd8ZxIcV*5*_=ML4KM#M&wJMM@A%4HYs;&H_4Vb>%Eh8z7#S2C#|=@jK^hX9Bmx*Moj=!3 z8*9BjF_zsycYTA6QABLY1V|}0H8tgs4-i<|%_bKmvatp9l~MpQJ2!Li;J%sZw#cC0 z?dTGkt+{48#ib_2VuofQqhi=EyQ9%4lcp--`JFq~v3qf?v#ZsbkMgT8nHjEau65RL zKYO7yFt5GtlIwQuD~mobPBTu;(0XTc=X9&t%%JEjcl${K7dN|kF}(U|*Z$tm|8T!^ zI?J-v<&Gx8O!2UYMyQk63ZD?3rj?Pcf9r|->|mSyZgiMS$*P>=Kj5Tfrf-wBeL+YPW~8k zbku@^*Hl9E_GhTi%MYlJ!<)zBgM&bPTTRBJYzT<079Ee=RzHA%^@O)=6+p%~WyqV& zHo-*`EXj?JAP5uj@3xo&){Y*RWd~5Pq^kEqm670}>O%~}XjNN(v5w6fODcE#%SWtP z=zI1IPsuB>{!he~t+Vp7E3M6dLAeU3gtc%x>^w~nKIaR0L8Yi$M07u_xRTdtZ4#O= zBY>*%2^qoF5Q$?EW*|1&l=^H3-~P$Z|G~d}=J2zhyMFfE>O)V+=Ela##sH&XK3HF0 z*F}kJ2EFcO2X{Ym?99f-#(HOMy4}j!&7p!$UXJoIPUE5J+xDYblA2QIdCsMdNX4WY z)9v{ki_KbafMI&MdzgbShwF)DN(ZZ3{Xn*MVm zqATq($WR%JjTV=gQ8@>#rjJ~u{8mACD0W|@LSv88hzd{%0xH8OqrOh!*0~d_Z}^LM zHD?cQteyGFd)`rOE@Qh*TpH1l&U7VZgmhFLem32AjHgIdPzpgIC=kmjT=l6*dJJBK z$Eq(3Fb0B}8Uky;#3-KlpNfxcy}9Pbfp5jVuE4#O8BGk1C=n@Dr!wpOtpuI`DD6iA&6EoEUMG$n&!QK=#V9&HXzJ(=&i z_QwD5Cx7-E|N3uJyAFQxQ(rjp@I!QANw|ne`kNgY4R_Bi_Scuc_Rw+4nx!7@Xk|F) z%(POLT#ws-!-pa0&k zAJ{kB?QKR9OO}olCychnjis(Ib)!aC7az^;uhu~QaW$sti1mb2+K1cFDnFpEb_4){ zeciRP0wAMu7)FASHqjUu>SGu4m;d-r_qcWbiQ7N)$G^O1-?SJlq<}KP)FWXvAIGO^ z`4{VB46L!G_Qp;s^5bF)0006*rKZ{NVr#gmkgWn4j0+&!CI@dz|4${Mpa6n1h=x2K zf)fA)d|?+DXINA@o54C_d_+vujnw*D1xgSkaQ>ir`>MOE7k)1h0Ei;X)x^sSW6M+! z=sqG|r*@f&MkJ1+DmwuY(e`VwNmgzU%Tj{;> zutmhm&t)|rqctH$NJM~u#u|YXu^uJqPSDFGL!??L15nXaG2&P{7mh8RZSB~9-S2+v zqo4hk4}A2m|LDGZzq-D5;IgM(fAHXeq3IjdidE4W!2a26I4W+u_L9ru zl-T7dlO@v;u^Hy&#f?GS+j!Y?uH8S|><#pNr_P=3^e+rX-L?KRZ@BtZ&%a^U?!AMx z?mOT2x!s3Gzj6E8U;e}InAV~9{nBr!B2QsTM|rQmmd<6Zthr;peQxE#+$V6w792v1F7Mud zf{4~LX*G2KsB(hQmkJay%oTds$O9Y2D{gB=T)i|DOl3Nc3NpP|B@rcqQ>!<8=d0c{ z?EmAR{e1brSB~F(>z->~&^dKBpOR?ATvaml8ksn|iBwUp0AM5R6;%3Ih5Yu6bn`%x zAiKU^<5-aZPYCwgfBf3Hkga$;c7bhS9+a)R!U9{{vKl6e#6A+Ms)Ydwc~OelCPb=< zW}{=H)$|<3!cce*R0Qkw8*r3dE#8e^!_*2#3JqIr&>{mm$F>1P01WM?3YkodM{Fi_Ex0#)L4<9`6-7mi3@rNE;=T>iXbfLe}Grd`uH&H1=4Gmb| zD9SQV%E5*~84c2`oy1B+YhV!7NSlNHW>F5Cjdr5ajlp1grjeE=jk9EG`pnYO#m(;P zF5mUMOXtq_Mn^7=PUm#j@nfwt>F4_LeLH{tzr6g^YUll*eK23YaP_6r`ww38!XJ1I zaenTRQ-Aq4f3vi_e)!V;jhK3Dc1*Q4y2ay1Pc>Vuh56|s9{|Ag^vrbJghq7#*YA1# zZ~wtf-}ai$*|l^w%UW}2gi&lnuhkrNY2rAxrF*Oh!RSIUkOfp^>waQIjv`_}Vu&=l z)68#E#NgX|TC(zW=M1|>mr~hctg+p=PJv8BQRU}lF#8Yh{HbSL-B?)OdElzozU6&W z=^ouXFREe6h?6{tV$fo!RGK14b5#&aRhpxgs)Vshsvctkh)VR(mLFwsr3e67k$@;4 z{~#_Fokhet$j35|Vf;{?_z^~vp{Aw4^=mk|vjHj+T9L!aspeVe1e;S=3O=tUI(@O>4<^BOlF1>*9R0Du`- ztRK$Z99)$xeG3Fcd>UhA8n3>x%9iCqrplZ|sY~;TYbxemZgw zxOjeLG(2liH;5Xhlk3>@yTvp|>vF~3ozQ4;XMQyw?Aw3M%K77)T`9{5^Ik6I>}sdc zo=Q}@+{dKsMhSNM#dM^K;bxY_=T^HfIndOj-m&v0H=03hb~>4Y;joM&-4MR(;d7t- z>TL$(c~>3Oz0SFnmF0WxzvS9yWbJkV<+$Xw?BW?ue|m2!&z*KD2*VZaxiKo?c9_YgSJ(r>Jys60)|xg9T~ajV;+9Mw#-s;Fhhvw>L-jAZ9C zQNP?xTf{ZS^?*WDMNz~_Vi1Z_H?lSY9yxj@P2;Pd`=W1n$%`5w%)F5cV$?<$6*`-Z zZvMdg-}>KQJHLPLw>|G!os~h})H|kbIClPl=RfD$=V#iV{K6M_F8=;Q2bPQeuAMgy zx<$mMEc(lx4ugHs;)~<-mftmD%Yg$+D-1Xq`^WBnXrrM)? z$WhdY6Qa1)mJzAX-gei{nZ?=JM09`e;*QTfetdrS&f{fq{Nb->QJ#PlQC2XWE;n&x z+S$~?-0XU%+wb><(@QQnc>eqaAzbZj%(SLkjn?fqe^gCPz2p_&ck#@L)z#I-#YF%h zA{N&Sli5`#1#rbDm7_Lv0R|2`LUSt3SG>Rrb#KZuw#3Du^y3j--sUpi=&8{k{ z0bKfxM>Fwgi7qSW?70)q{-$TY_y>RP^KbZ}hvz@G>x0)_^U~K2&aOsWa#=}8nj>8YOCND?kN|1y|yjV$~G4=ZYN(%9Oj_` z*hSg^05G8K$UY%%Y*Uq2#_eGu&R`X+XUw`uNQh`8P=Z3Q-q;TV-x%2hNSYkH(lsCe zQ#iM=xl+w??7!qEW|1$1A1Xu$dpr8a8IXzaLUEasuHgd;hyj~K?bbEkq=aOf5_wJp zLl6+=G1C=*1J1|dnC{eB-2+G)F` zXX0e~xlh}BzF0hb#lh2!jmG}zqjx{}EiZa8ar(heef)}pm#e{rU;NoufA#LufBV5N z=-%-B`uZ>*wqjXm#7nE2t4wK+Gg61 zRkg-(xw|$B8!g34Wn!@SfdFiTl>>EV5D^Ul5V{ch8e0jf>^*T+|HisFF@h3b#@JcS z>{nA!>)3M!52_qu1yEM7jixY=xET~lD(uy)lnia~>mTX0gj zcyy_ZBVaRfEOm?>U=};Q*U~w#D|k4Jtb;YjFw!aKfzlpQ)C4OD@zvIFfH8@_cCUDL zFfpKi;>7swo^b=s|MLpOiBR4Vf5_LLfC*`T?DdvMg&v$>Z7yuVzOe2nTe`-JE#Tfd z(1#PS++@PVE=V@8AarY{w( z0t_=7VOwg15kxp5jEPvZkaXk7WCV@GBx%%WnS9t(&}wGA-dfoiE?qc#_`n`a%cGpF!Hw14hd*=Y7w^97!s(-XAU~X?2c{Oe zNJfSRT2?x}2Tz}UXlZFgn4(!|5JI_83{Pwp>+73YOi2{)o0_?NernHbIu*&GbpB}z zjr}dU@{+x~7aF}-I(hNk>C`9R^m7mV+MnKW>7`3$+0aECrE%6uBh$j*-I}u(C_zc%RvqT3tgets=x{W z8$g!HWO2Kl;Z-9JFp~HJM-v^I+}L+b%6Q3AnH;{@aL8o|UkGQPp~#xZ&Z z7BMKc)}+`biojxn8adz~+w{T+W*f%Qh=`zV*X!!sjLA(fM!@wJjF;owdWF5_Kq#oT zH=*Cv9%v&nepF!a7ec9u#R&VcA&AJxl%~{Dn3BLCDB%cz6(l7p28AX@6gGvF)Mx=T zb48F2AAa!OeGAFziQ@?AV-G#Cd#3%J&;KTU?8NW>$#3;HR(|>WfB3@D{_B7J-Jk#B zEjJz9{k+4MEvPIR4G_walAfWZ)y>UrXDVsTMUADE^&>t0{F(lJD@8#GMVLjZHyBMd zTQ}@qc-G$Om)vmmP1jvLJzpef79i$9@BG?wcd$7eZ5+B}&*f_yj~@L}<40b%r#XeD z)zSSaW#*GijIlMMTdl^-@SQpZ_}V8lBkg;K!Y?a zHL5!HmxxH!Aq%$fe~6zTvT2+OBjp^o=+dicdG(pMo6e7lUgk51O0t797 zpX&Z_OaK6|<#TEsG9-hsI~@bsI+Ck970%P%U;jK%cI37Sw+ioryOcJF%L?p^=#>8Z2l zH})M|IC%MC-Q=Hq`x`&?&!0SUV~j^(ZRM{M?t1oISbH zRTPUUzyD=7UA}An-iID|`0VPFXV(_mS#Pv9Y-f9#afG-wZ#|3*Y$Tm1)p zhOtEp@2Th&#dKnDs=0FH{7tWUO)^^hzkm8WM{d3C!nb~9ZvQjamijFu1O`F{D9lLK zL_<(#y1Lakmjp7@o?NO-I#B6N6w%uENhHNbs>`cM-8BZxO0oEn@{VpW6BD z=U|s_gX_YDcy_k+C*$k&F_ghoC9ol)Z`|tu4;bc>NEL%;mqtVr<2#vlGQX+bKBH~E zrH!=;v_&?ML>fd2tqd4M4WJm)I7cwKKo89*B2ta01n6z*-k>!zckJZ3n_l=WQ;X(v zU-FoLQV}fBc_6_=S5P{q)x#`|w9^K}q69yuO|ZZ_7} zS1tU9!yyq7F)(2Sh#;WK&CQ$N_uf5MUv}ik5mV}!nHk$Lo(POFVAD1cOa)Tc0#vMj zq;@zcrHFt~QVj;Av`#}pjz|boK_AO+cicz!W1s@NTzPQxHGz!=L;%`pQRIvy%_uqk z`07nR{F4T<)yui@6ndxF8foE`uc0Tv)XK1dwgrOpkkE>E#-k8EseW zaf+)CyvbC<`o}?+kdTaRbqD~kLFM)t!brYT9W-{C%V&40rz z0YnAt>OWaCk=XduD%Q>oL}C*O1sR2^p)5C1m0lJ}UC2u@L=0#I9GwdhC`5&dfNYRE zB8toStg7K6DqkmjtZ1%snO6Wr6xnnMs6;0jD>lw9>et;C07Q`>fkeW}XQrVruptl^ zc})s~c8=lpv1@Pd8X>s8zZO8^Of$^v#6YT>E@%eANCU7jgvbg@6wTUlFvbj!VntGz zM3f#43vF06pp``tL}e$({&Syk<>ODD9z{`^>-+C~;=P~#`bWNa|AS9FzSxdwsxeqA zF7($240)a}Gp+X}%d))C-CXwV+|?L~A~|@VaYZA&vOU#k;B1`D#H1yeo}P{KZWek~PK-*h+aK;olRf(m&h6NF zZe^{&h|*?~B%?upIP6DJL`0gP(;p>*`EZ!b&bC@pX*uMzubBu z#YTKouPZ*}O3^*#P)~6=+6E`)1+vM+!43d`J`^MzA-Jb5_r3kJQ9_IEQ3p%T34)0sIuyoIQ zKOg4HfBUaDpI__pu-9zHQ*-lqr`HEu?+>9Ah+@>;IpA-;;j&R)jtXgIjRuq(gF=Js zZM2w^R;(AZc!r1@rkV2Y-8(WfYGsSre56;+xupv|mdkd}sc4@r*JI4UjLzr$+1vWZ zj{kEnPK%B8XD#kH2c3jV1?l4#P9JVHb~G_9`fXrNA|M4L$V=Twn}g91GTP{N0C{b7 zX_)tBXR~6kRurRFYr*t7qcUgY7?m;X+ctTGae6B{JvzKWIn0?*Q~_KkIk!asWIz-m z1m3ZMTEgd>ktUrz~d?&j%<1xivX%xB-!S| zI$H_67K8r}a`LZb-1_jJylNF-JBw2SEH!;nv;l} zf*Z~`T&9&}JOlv1I~}+E6W5cV?p{zm*Nk(?Dt!Q$z@-QvqHM=~!fV_rT$#bx&T;n8Gm1hZ=9e8h{I$F8{p1&KJ9Ym2 zY#V3ir*b5Qexpeo!J0M#@l<>E;Oxwe2j;GvrWkqoe zPdu@@bSkCci6{{Byqd$G-8Kzl+bV{rjK%`e?K| zyW`Sfv6+}8DYSL*IQ#^#-Af`;5faw%{*Kv<(Ju>I*-C_M_Y;qIgtjCA3XuWDxIJ6h zma3oX+J^i5C$12u?I&Z3z*BLSEflnss(lTFiJ4UJsw#2^?y8B$+54*)$Z=LR?pDFE z!-Tz|4r=jo?9QipR%L4OZ`oUU0wt?1^7_`=Hvt7+l*xOvq|R zc2l(2f>0PGDXGY?Xv0Qevp<~5(zvy-u(&5Lhak}-4?l5oX?^G1+>T~^+5VjkNaLtI zJ-2hVfjBHquCBAuyBpDgxz;7E_NBA!-A%k?D!pQU;j$e~CFM+lSM8nOH#c+H-hG$v zJJ6uC)o5oFO(jjylE#T4h??2=zV!KfTYEou@-?#h~+T0Q^&{pivU{?)r)@qfPa zi~qX+yY|f%BLp<4)%VGwq7!HekR6-iXrC}lGxnmInV#F0`MRaw{$4=G?kP{%Sdc+6et%zPp1!SL5 z5o`@q{K%Qr$3j||0XRlr16m=P0yUl782#!8{+-nP`HSan{_Ed>q==)mz+4kseZ?yE z_BljAiWR|FV-Ru{AC$nWTj4fi?WU~T(SR^WiCiL2R-^JH;bF7||F(nIQ&7%RT^?!^ zKim%8EAV;lT^TqgZkTwqcedLOC#2- zGtA3Tp#_zw2;!;E#>(vMd_?W3sp$)AD~AqWnh)|$cerow!MUlK-bfKqi;P6gOsSI_ z-S*7%OcpDJN-NXv@0f2K-ZOvoWtYsyjm5ol&%E){OZV>CG1b)F-hB@rF7hEr73bj^l{Xl7={7!f7_>~;o))%_PnZGpfh{ zBqW)Vjs8jxUiJFlEP8`uw|w-j558^p{<(4psY#q)Spkx%)G%8qs;;fcAYNQ`D+2H} z?d_Z_wipmw9!wi!nTvHbjD@{z%QZrzoL~nNNWb;NZEu^nbAqF7dx;N|3r=7^pQoD4 z_QV--sx2Re(vVe8!rk@v&=$A07YBU479vWJbFu9LL%st}eU-6TlCkP`#tU}oy%4oqow9iB+{HmDxh{2H?B2a|rj;$uPno=!Uua*w z{{X}&P|gG1e*a?}#ZNzc5J=i{GifvJ_13xgwr1q0cNu%L6dVz_3M{vqqa3_UzoBqXK|ow~t8*05BRsB-7K=xha-cE*f2o zMk5h4C7(QbYG!uf@dxgN-i4^$WQMX}Mlb>fM1%Nxi}otHYEcMa01cq;)oH7LI@SRo zwageGrw{Po8?gkH7jSf8m`+Pp^IL z;~zME?!@e##gTMvh;A6UDk4&+9ae>GE&sApB0y9GD*Vt}4}3PAwGX61snGUH5NzQl zP_fC0d;XK$wNo^ium!R7bp(LOVWlCJ4V`6t<4)2~EoLh;22ji8`27@SK@= zXf=NuwNm?-CxEtfjT2<-7KPp?Cn!^`DN+h$m( zY_$;|XlE@%F{vaXa(?wn^&^^V&*p$2ylb%?G4I(qhq~OiYtMIGyZGBb_{}R9H=?Qi z*Y2H<7$PHgoVd`%w$|m&-BX?JW&%V4i_O;JzJ*qrBqJ(&y^ZeLl7_3VxV+VlcP=ih zUR=NM#Ocn73y(ka*s&Amzk1~OUw!d`^B0SS-Rb4~cFZnJf9$~%|Ma%<^Ka|_&RZV+ z#OEK-ph}wBD7uC8d&TI2Hn-h-Pk*B`JvB>73?dq&U*DiiV2o&S5zS+2i=eQw zrWp;W0X2#*f$DFwv2YV|io*dGWTOBhB(1gWC(BaWglU4I5c z@8e`96SM>XK2RmRe_Pft_R09SURZi6wgOO>gqRpJ_z72cZCl`X@(MleVU;}ym@$NQ z3c{^ILFnR{;1r>t;nlH;KL|AEfO&1lSnX)04w(}GLAEB`Z6O2W@O&zOF*Z!F>U&IDCv9SjU8U{A*ao$?*f}2FPs{eM`wh6pzQ&Wkzk!Ob#Bp-v5>TB$* z9)=TL+#N*%$av0M$X~V|o@m>T1yHLAQmRiVBvHY-ikjyj2<{ZMLDCE$A_iS$8qL;m z?Lv=P#2^4D@206{Yu8s7$+y9;cIKfy-p-dSh=ZatH4;>t9r;yP^H*TWRmS{l0Jixc zpk0V^ecfzh!tor!sv34&RmyR*Mu4h)kbr&d7ez!t*sdSSo&jzn>~to!EPR1(16Yf& z!htRbkcdo~QyeEr3?m#GjY+eawT_%!`rV&?&13i7_2sO!(d&Qhh^%a^TzT-&v0EQj zvop{v27`FI>^-#HecG9WL8Dre=QJenYmc&Mp2&8=N@0$SeR`sb9%nhpMnfmJD1*g!v}uu&HbV3HfKhAcl4Lej&yGH zP?Hip)a9_9?LL>Ut`2jRw3I3vfU#gc1R#SFmm1qyl(550mw?nwV{M zxv(@JK38eNVHN>!{=gsK zam&M}PS3}!Orki>BFtEu_MVx1Sd4mLbhNg%(&?{d%?3x*UQ8QVQkLal(6g2C6;TvL z{ci7}hwr`o+0Tv^4<38$&PTrRf!5(!-D~C1hKLYRSsF{*glaU*RU8R}(QH_ZafQ|^ ziI0esYZ1e&*%)KA)>b}oil>h-sKQcsJUrTf7|ljkZ5ybzaA(N)XFmJItLrNt{l~YO;#Az6RY#2>z zSQ!El1;#g&83>dZR!Z5@`GN>Q0~wS_8tFBSX;JnY&DN(raq|cM{qdjvn|JS+SI5td zrZZSTx#rN0Ge=M4`G^1p!``S=M>l)J96B5M(%RbcdUq(g$0CK<*xcMKiuE!lG;{Ma zl1vYCj!30hyw=}b8gKlro%&BUqN9T&~HW4+Nuk-`9K z&XO2FPM$n@YID7VdaW2pqov}cCCzyaQ3<`WOvKDZQP_fMo6v|= zs*>5h@G}ow-XE1WJ^Q(z`GdE~p#Ph{@Iz-;muPQc}rBl0)7O+%Gk4)NB^X+zbB*#{JBcrHS zf;OV!QV$H69kV;W{K-2%_Tdk=H(CdF?(P8GbM9QHC>yaFl|2z?sp%b4izZc+(_pmnvA6$BV^3mo0YYX* ziRow}!s7}adpZhZX)&CkBmC7Oa@98w>nx{j7Xsi2b?bU%tkTu?dg!(c z{Xf2HJqPQzFb=(~&S@{!d2V8ip(ti%Te=YunR%f-#~b}2v@b;rB;%S6BDU%HQ|BPe1eYR9D@)@!S(X$La~o0i-S;ICA4Ft|{b~XQm5@ z2+GzC6yO!TEK3{%K$%vllBbq3NAjg-CiBP3TDn1shBc*GIgQxN_KE7OMh-3(vW0T4 zQr8U=LM$UWAmB9-X?L$*Rsezuw2T42vSeqU+Vj{b^evQUPfVZ2rX}eFF$P2oF~SG{ zN%9GnR@2hyvLM2M4`9Y9K{kZ4AQH+=o0KZmo}M0A5Frv8rm35_kY0#ieEDr3|7NME z<<2~npExR}1`NZ%q5uG}Db-CALTFP0F=C7{09|V0jCOOtJ@RJL+l2x~feUV8Q%YDn zJC#F&nq;VT#RceTQXj?`<%BWYQLQ?WVi++98DlnK5>P1OwxL1}{C+WeDtqmF|LPE)>tgK(cP=HqnKQS+)i~Gf!&<8lkCI~taj9%LBoj=erNCDtYJ=gK+U^o6RmJq^K{Cb+L;v9l* z_ZtQ}v`($zYU;uuc6eJ1A(SKuI7ch7kfO-AsO$AYLyJU{!(r(}x_bEBM0hl@Ddk@% zS+)f<6O2UTfXTUhY1?Sr)GcnZrAl>HV+~tAzLb5wpidXf8VPkY*7M5iPdY3+H#?i z+w$5sQr!=LapInDtEwfdimT-SO?8t5Kr=sNR{!_>5dAa9x> z)Vk9AFa~T!31gH)3IO4pF-|!njItKL<3wFJCjf-dW;$mAMnvaa;Rwon>0EusZSO## z^@olhyYIg~8d9o;+5;FzHly5fEkwMh>0Xzs zZC$TYz`d*E*9-6kD|bcHx%q;R@Rr*M0FDNfJBZ+IZO8?lZTBBE_XhU3w7WVT=HWYd zJD`Ji=vF{e7~w7u)CyfpM|lBq3|t1vqr-Mz(e~48jn#!%ye{(U*3iDc)1lg}%1hL| z7hMmoX15u0*b_$xl0}ga0vIPqGclH7(ne}b^h3e)$6`ULetwcMy#BV!H}r=LA_N4G zw{3GaS4ijTU;ON+1pqZ$qqC| zSP-C4U8-NTan0*?Z@XlCjj01$@LP;ht>#k%86!!=>&8Y?@mM?(u2;&EBAS{xapd_P zf22{;Z~nXgDHq}N;XV6*_uZakk`O`&!5E98Y??MD?Yy)Ac%l#)oOcM@W zpGHq`27=I&`L~Td$S7ychJ%d@(Q;|~#xB);05#@D+WzLBu+7cISjpQr% zQ$M?NCJO_bRF6SNYHDWeqfI0O$T*;^ zt@2#CcD<{uV>xy$Yp*$N#VOiByf5(7%KhNp)B1GhKkNz!Zu2{<7=YT`gcAhV)t%<~ z&`NgQbwkIPFmErI_w8Pe(Y>cDIV5kLD!`+(g9vpt2Dm0+cTC!3qO(a|W8O7%9lGHa zwjnnnq3d1lndy#0(|QFV2LLtUR3YcwQQu-9Gn+72<yyPme#6 zuVfGw1*lpk6GcIkAfcqLnLD>6`ulrlmh+iHw#P4sn!$9=P@q4V4EhzVUI{2-*zZTo zjzq&q#C1f|^Aquqm_0Xt(OYhQ@f+TkS(-lm#4ib>lA-_zEn=IdDTt2v(47q1L0VgB zihF@I<9u(*O06C4nfexc+l(EB000Jo>%-D=#e|en)223q+(tEGL+m3AS(CWFaQWb9yd0Ox*X4%l>_M2 zvmzuw4iN34VlA4@M*@X_i`W88r?!Yh(L_X0 zRngBxYTA%-JDo2+^wWp95ZJQ4FV+*SS~jwgFA{pqj-8J`@#Oda@m&!c5XQ2#vdu-( zsEMkqS@vQ<5Bd^_o2FGQ)GGlZBm%fU6dVa9eH6U)l5HQ_z3Y9KU3UH2wGy)wTT`)F zS)AE%)3*2h=yM;w`>$pf;I*SlhJrE z(JT5?F`x!(8a=;|o~acP0LU^y)#3&%E>BAln~OA}Iqt`Z0|(-kF^)OIZX7vxb8iEI0|9UWaD)JMOvbJ$(_JU-8Nf(0 z(G^4-aKt!d95W^|iU7onL*9aBZ>e|yhZs|vGM#foIWhsmf^XtvW#e_Pz48^WPA}E| z^0WUEX%0%NWikXgW|&eN10--rph>X+OsA<3a7Y*@j8hH>05<28aVOlAP)ex7>~Pki zFRKbc$UW{*%Ua-47SI0amU`MJVH-fW{VQx~E1T4%jUeZKfD2#Nc>;G>uv+_yI@Fr4 z_&8Ll9gfxQP}7an&j8@4yQvU5;CCLSCW3Ba(+ds4D*atiR-RgJJuX3SWkhA(N4!sU zaVwm2#|FVUhk&&s+(Gv#>UpYLH{3b{n;{XcS2Udf-50qxBZOp8!i*xofkX(yrd0=A z4EIEGb)Bm{VX@-lSzYNbSBmSyY8f_0#9Rt`9Xyi~4R0*(V=FdR+S z3g>c*$#7Dnh(J3MQ)PimN}xX`U$HeAkI14dS=7i>GnQpc+1lP*?cLx0)Cd3Pqc^?l z1H}@#<(1d29U5)a>R1$rh=3#z&SJ4xtx_2qPQ790z(qs8OuZ3`_XQ&HQ;V5WiC(-W zwr*&MaS{thsAdHHe$#^Cpwcrix_8gMqleS6kyI_e5E~hP>ED0aC>4%9eP{0A6VblG zhGhtH2vQC)fY4kyFI~&YnGW|WV+62fo?q6qAUlvi9Z3k?d55?izux3)TC9ZkqPv{j zsdR;uCI^I#OiugI5AP7T_}p{H7oYmUa3nw>6&Nu@h>*{=DCb-tl3^HJ0uUmXQE>cj zY}+=8OYfa2#`*X7Wz9o%5#KUOVWvtMCqf!OQ2b?m@>_;W7YdS#Uh`qPe@8 zT%va@nkbX3ttjo>w=HiXuPKLl;b1xE?uKjwDLDVN+RU3$8*0f}h&KVVJ1j(A`ttu% z@jTK&?7Dp6USOXC4>@jXnp*?dxddT{78VHz2_AjQDj{7LDk~|=s{eL0l{F7cEXa&n z0*59eq8xz8CWIQgp78rhnc7#r{@WMdbY(>Xq(-<*EEF^fU`;%5)1|}D9GTj8=;@o{ z(Yk5WkeRM)bqG}~jzkjk%ZpV@kA=bkL56^-K{cd`^`iF7MDfpO7S7l7xn=##{4%tS zm*22^>Rf5-nvwa;d7n)c!NQWjIhU{mDf0=EB4EnFVm`n3*ty11VX>hBNs7xr@dLn= zI`ON0W7pnvT{s#p*i4aOfL2Ens3)qVb6J}S%L~T?A&DVTGHba-^P<;%s9d4?;`D<* z`AT3giWxEqMbH$vAFOuFbc}!7nF9gP32x&2>M8@6p{RH=P1PdxqK? zhxQWhe0fITGgh zQHQ{X#Nk+vVHk7yLY@%61Sp4uFrUC=WS6x@y`f9Q2+AS?V1BVavT@TteCNlv|KxY$ z>k~>e04NhhQ4j=KmbFGh#DXMHI^G6Oo_82Jw9O@N)$$TNZ`Ieh+ddqv$OSyB8-eKxB z5{iW=vw;Cn`SkErdZ4F=`c<1 zPYfd9w+Ms&z|3@Y=bJysY~k#&vj_k9nf{1w z*R1XYU^2#S&TP(H1xNR4TXosFL)JA$0JsJ-vYy1=p+ZI`;o5Y*c!SHObOV*le$tTgRR7Ut~3N0CyYYDkEq_TRY?&A zjt$F5M1tXX{J(Gi#jP*e`m=A}^{)56#?0gcKFDS%-FqsOWL$C@)ZkJQn#gGD4#8yf8@u#^m%TV6d|xUX}#e;zOU(mkv~6m z$Il{3Kd>q1vW=_@p8>04iL?)-<`P(8=5wdX5tH3z4`JJ(p<`V!ckWz$e`|_cTo30Q zfown)3{Fv{P!~df^PLA`;n7F`^p}Naf1cNFL*p$nfl*Rc8 zQqF11HQR-galQCk;qcyVutL$;RlJ)GLg;Y`K>#qoIAf&wYs>c9b+o#Fy>1|@`h$4h zh4_V4&b(!>0lYCL^?=w2Ql)PZ#@Ij{y>PEG_IMt z3~|`BQ(btFyB*}393P|2E;W5RFXX?|Rb2YMGUy3;Wac(ZQRh(`@09ZY9c4qH^srS5Q=lL^>uXy824jiB1L2;p>Z;6M0`^=#|6X~~KzkRt>+x6<# z-gNmEj0xvd*XYc0A#0hO*byW~{r=O7C4);nK_4JyzHG;Ue9b^&OFTN%8;cCYLJYj) zwwM0rosajX=N|dXBNN9@V^OT>hRs3UFabag1tf$VTSyP~^yrdYAqs7nvMR|+&;q~? z26pY*kQ^K=o7zGtdpf;Tucdo?Bbkatv5v63Z{I$#G9#V5tG|2SjcveinI5Thr#Eo7(fsU zb4w8!38;bMbanLQm+gA#&BteoPu=yiQ01^1kSr$45Qu_`0VkYw=yW`CS^EnB+;Nuy zka6rNZ&%%x3;@suyyR`GkXJRZ$|If3PIKA8Hn6rsaX?E`?PYL0{%I?T6mRXH4(70V z1Mj9p&6c-{%dj~7h`Wnib5)mg>giO|T;284g&2YV$FkxrT|eQL8K}e4q}d>ZR<1d> zAjkgQ8b-&6zVa3BxHWU6tb+f-0RV1NJ5M4v4k2{Hm)$`_j6w-nHbttWNKug!fPI9s zrAlG@)*avY>939PEhkSL-L-kcjx__zWv&R)kzD=Cb!!S`y?@O*Uq1hdzxjMpFn<4) zuZjVg2?8Q;IiEKb3=j{h%5X$jT$nbn?2jZO5v9g-t|}Xc;#&tp!(84t-21@3h08|A z?whS%ciCnUaub5OspoQeqMLwFjBy|ooX=!lylD^^jm2!bsx`16Sz3*oH4cHOnKCoC zM}ou_s&zUV4i63vDK_6S5U~N1F)C-5)X;d5hi2v`BIU>H&pq?1&-~+M*S+D%eMe3{ z@)J1>fCweB3Z$Deo1JHmXUf`ZtR?9HCz)qQo+;2W33=wZ9Z&4_SZPl&>L4ZE6iNV9 z)Zmyj5Rm0c?O=WTE&s52`<1iPnP-0ZFMgj1)etx92B6r+)T03MR_IVLm!>q^Pl21L z7rB-Q08lgC2W5cKrdFk;8gv}~TmUO~aXX!440Ro$j1k9T!Wl$RaKULfbR-n$HF zi>9f?x!BTXBiJI1Ub_iI{f(RhYA#(gRc+;WCad@X3vX%v^trF%QRa48(<&Dea zkhctbup`FL%X7NqskOHOZux+4*nAg;kb$OI%1O2B*!?)C4mb+}yeTy^2UQdUAh^lW zU=u|NLei$K&QGp;PHU2+WD#iO77+$ZJS z?3we+OX<>Nx>z@CLtv6DsG|07-~QabJ$Jl!>m@3r29^xdE|=>iU9UHE!54`1^vo|V z5g>?al#6PiQZF@ZpD6V8L`oWo`<6HC7`(2n3LylX15Od(7^3D>0_-_aJix{PfM&FP+rHVNGTdb_n}%fSU6#-#OinQ= z@j(evi4zWSutw{mB7W>Qd&)K8&ksLU-}k-2sLgPrvl08p6c=WnUcI_SxE3y~~V;M=gQX`|IG{f0KS+^*I!u0$?rc^OFk|ME^ z&Ci!j2z6$&kxiT5^xA87j*p~k*-{BrDizbFhGkL+1PMb)s8mWD){Ui7$x=EyzHaTN zbwe^1Y@0&f01zIUS*V|!ujVq>?7pZ{Eyh*Js+Ne&^turWg+lSD#p#c}|HFY)uO!M8 zf*j?=`E&W~+_!)G&_Ddkmw)lq4;Yi@qdmPgw-I4}hExGt80d&01SCu601!eTgp2}= zF@jCN8VH+-^j6NYci2~~_WzgSI)$$OxMf+Kb0@bnLMWTfA`UKo>&L2%#-n%qta1A1 z;Y6y6bjxBAhp?4y+?jpOxoz7BA$R?_Y#Ik`Xa5xueS8(|UCSt#9D9h5=2kvXNPUf%l)~OrRD3)A#5@f93q$ZS(RdXHRaB@d464i6WSh@W=|o6 zt_ZnOx8=B~IA^T0wwx6S08o6e#wkNVp_YdmUUAiR*MU+&QR4%H{hUd<-e>^wojV`; z!r#1g|9wxq{_nnV>e)MPyu4Qj@^h!ot5Wd9xykX({Xh8L?T61Uj9$BGW1>ed>lS4g zL2gvFT(xeIvS!o`TPJKblU=UV&n~3r%Z*%BJ6UK@(f7zx$8UYlzh}!;zyj%V-ZU-4 zG7Zb(gou&^5XS;)REo=s%e_f|CYz51WAU&^8G{5ViuCAX2c{S5#fIMJ4~4_wdR-Iz zf+7Wifq-FJrCNP>WbF9ylQWakf*>JL0#%a%T+A(e`5!(&14BX}weOezC5A9FF(ne? zvT0ESxBwA=BF6#7fY0Y^9!RV?5pzn5Lq@JoZ)Ye+kTFp<|`n$ot|@2ob?dBZkk0Rx3WJzsFj8`F@1hvaS=kO*Bh#;*6Y=&*_pw?enpaHtTrmSrI|TxX<>5a^w_SOg8|{h z6L*eX9`&dE0AdJr#;TM<&Lm=6qAb%j>OJ&fTg>kFWE}{ery*N!Qe` zT+eRh4(r4zlLQd}z!{Szl@L~0MB8qDr--8GPM$dMhY!a5l=6te4FG^32ri%Qo-5v$ zy8pWGLN4p#ZAX`8dqrEjZ~*>~1cocIJirPm{ikv2;16nH@LH@nRdA<$l5w=jsR$`003|{MiYJoA(aOuW^>aH_C;r&e)#OkV`sDZiRHzB zPcW@o#WV=B0|DiZrw=ES@njhM{&#mvp>S9U4fge%o}VcjqRk_miMhpzH@yFo+js7o zoj#o_ z1PnrmAhc~;Rn?2OZVUSSMa}XB{O4!qgOMnscEh3o00Gd$3z?ZhMH0}(n>HU?%$Ss2 zwrLy+!0%HLArx%aXCCyw5I=bvu>)5PQ% zp+^mm42R+=U~7<8MS)Rji?Y;IrmVK@Ti;ux9JWtKckoxA{{O#fzb{(z#A>*czH+^J8|)}mk+V&@n(n-^z0*AVe8u(y z-p;p3T08fWH`Bd08{dj&n+%2b{#CY3C-eb?u;pyRJl3C%ZPU`Tb_acTy<)}QZA(n3 zRod-AO|Ht70ilOcTtOuv_YQ9>Q9l7q~!i z`|1vqTNUn<0lQw|)Q>T4vt|Gg%g_;FnwpFXg(JtZ#me0D43_*9FzgF61}qF>J~nae z^zhJnq$J;V)qp=KRcbsE3)-@8Ib$p?E=Obj{&nM-T-i4^^56gRQAQCA;2trg1_KxZ z0bv0{O00Udh9Rdj3k7|6Bvq+2_8;1R=0{&S`MrPp)jz)b{vUj2aegT| zKA0Fua?)^+t{uf3#uxzL?95ds&RwD_XRQrb3EHW_SvQ&hXdjao;O-seD};~`;xq|D zNEio_P@7rYa_!Z9>$WVPuReA6k7Jc1lGx|KWx`fi5NN4QyB3(+W=AvKEd=qFzSpG- zt3C`m_vdN@v5GTDCyn4M*c{~D%&qI)E_PnA1pwd#UN@Nt*C-B};zOrT8eb`ibdpZy zfWlbSeKwg@54DGW$}@nCNtee~#_$LcfwQMO@wqRv`Hot4z*5BOzyNSi6kx&3i z7K?G&rixFtDWQx}29SVF)3gjzzz`tz=SP1W=^g9u+fb=5Y}&XBVw(g6QI-LMvMdRL zAR-(LMRdcEWqDwrKbI{xEIKsOZ`q(&sRu)lSpaaLMnb7%4{I2SKu`{Z7IQ`2W=7qh zgbSint=RGrO0|K(5ydYY$uIxqx%~py z7xw=C2Y-L-H~#KJho3tf-aH-%1S<8K!|1^lU=0?#wnlT=G-1O~H~Zi9F95)!b8KS< zU8r~;cT#p3mZ_?$6DEkUD2gItqM(RVQ}ydU_RTGqU;E5~V~2kAtwbCMg5Z!9*1Wk1 zQP8x#t_bCU07HPA!n2o5I`{_7#Roc0ao)KKp(nwOS0ZqiwIfVXtRS?6ogE zUak9`J5dvI0#M$9&uP}sk~>!JF3?S_ybiEUgVd_;U6pV^2t95E(EZ|K_5ct01^{pl z-TW^rR#M0HR+?Ve%5>Sn=5pQWyfr}P{ntYpI(p69sLQL+>#im-#sop)9xE7SHpZB0 zHUvuFu=LbVKmWj?xJL4WUdq9gxL!LqmjvgXeUqP=*{} zL0--jXX-XZKAnKxNVs4&D%JW!&p!3ZzkW--Yzj6BM}nd(grgzK0RtS10>(HP^c#lp zmRoL>0BlLQtuf>rXuT7^FBNS&C1VR7qA9%d!MnvM2$77m8)mB%(->?621hQQ#Ki zh!8~;FcZ%#%&L;CS4<$wX3Z??^vrTG9*)Ffv6|K>*Q)VQgj?jukt69`_S0Yfil7Ds zft;9JIy*ZZf_Q0m;^M6refd+Ly7t;j4xc+AMf-Pd+XPETZ~yFjfAqH>r_|oKaZJ-R zha5ZFH_NiRS7XyPfA!?H008f~wQ_d4b+@inx_fa#002Tr6h&RvWm#^=OASO1R+N*P2wFy5JKpJMR*6w zh0?*GZNzabR?XTFZ6ObK*lr+%HswwKMc@gfh7ce}<%=oDPBa(egahjOJ2rdaAnkJG zN_h}M2(ZhtLQi~s^9uwFaKdcIFNss;xV0+Ik#f4ILVh8VI4 zQ?N=+3nAfEZ+PW>ckliD2S5G#E8{1QJbrTG_;_Ln3x1y@CL<9*>_EuR7?mMLKoArk zLBJnMRV$Un>;eN^GeJ+#Dlq;#^kZ#e2(O5sy z_w}!T{kAu}dwS;lQ~OWD@J1Vu?DDeBQ7oC-x_kS{naSSd_~y~gd!KnES1I`-qg&T+ znK^RrSKssUqZ8BPYljTYk|ahU27qDIMIg8vckZas(o`^`Lv~H$kVo$EE^0F?5-1V({0T0Q$pPRG$DdP zN7B-c0b#3fCC!uH9j+E}X$~MIjM$vEl{F3$dV2~TDQT72X^$I(&4KW$hnyQQ7GcY? zrhU`^0ErC&fk3xuMZ}f}h(M@J1OSL2Q7B3_)S;1nf8l{Y9Q@M{PUV)2 zbov`V`Sr-fYtBuWI4F-PQp6uwn@HA}b>-%@&pm&#W`oau_|3n&?=KSIrJ|P4=i@yw z%d!e)gWF;%mXIXY7mEGp5BEYqh6lro>AY@KB`lbXBzhwPwCY9`V3i3Z?#t~;DR_0`#lCKqxHxgJbJAZrrhJZ1c|1;Z48!(_emf z&%^zR)a=CBjhog^96Nr;oqztzLk}pbbj9wgXXlox)rw*3ef>is)INRd?(^AvVlbvP zL>UpABSdkRBWR1e?_g#-?R_1n8eXQ}U9sM?(7TMA^|#!nT{muT156BpC}4>&EaCK2 z`n_NL>iSErJ875D;Y|76{}b)h*LGFS`O<0chfVbjWxsH{k`y$|}El zYG~oJc?>aLLu~V6r`^=8(5=U%Zr-OZR6N9}<&W25E4p3#FW8Z`qPI1#-7$F0pB$3N zm{&o|c@uE7^*HQO2{~=^Qa~rYHiWCkHDL$^gfQd?FbpATx)imCv1?4+W^{I;TP%ky z!?PQT;iW_X08#KW3t>@(K(Gl%0=5BTBETF014E-C0-LL#0Z2fK`RwKYea+bCKKRbx zK09Typ5HumY}c)?+w+YtZnE~jl$OB|k`ifACFe zToi-AFsaOg4BM?!7P?E_J6~NCzhn8WW_jo?jCa{LyVeP4ooemx7+NNU1aL?=gP9`R z`O3H1Lj2@&56nOC@1X%jH-QCBLt%)6AohU^?8;{2I~Ka(7t{kJT?Fw3yW#yDN2hEKb zY~ol7yZ}PX@xu za8%Yb&D2O-jVM9oqRs2>dved6_dhAfY}1xCAA843^Chjguspq-4@UynCZGS*8yrqO*o}rB3YCwNg!^9CCAJy5ZL43MrHt)mm|RdC@eqOnSl48@;`~NWlI5{nc{5f6e9fh3Eh4 zo$p_>C5}XH@+<;=Hz3Bf5;S8(RxHRDARjA!TGcn|g`RSkgb1M^OEMHSfnrf8Pvv)B z^YZt7_Un^V_51I=W1w`z7wX5hjj5;+U_;ZpoH{F~oYS^^;r(spzb{PoT2x>aY*+3n z&~;$HfJSy@B`cfW_29}wdu3BQ=sg5Zh)|aosrM;w+LoY-2rp_L=tb;a8eu%fgM!*-jReRqf$R&0P77A(dY1cVS>*Cjzj z7}yR(kR@3w;FgVjqnp-j8SfPXBU8%)hs?00 zOUJhS&0Ambx|i(UIyN8?YS#?LfMyy55(Nndj~t%QE`?N81sIB0@F^k^Z2}l4CMEfj zmd)l;$yhKFo}QUcCR36iQrq;azEC)-`U41|-11VkIGc*EiKN!$8f9J68diAc)vxB9 zYFZs3R4Nr1F^YvuwNi+4E7YE=y*t$Z2I%ghv-9f#34Iu=$%}m{3L>5#CIhIgnF2Cz#Z$kdw6Q@r+{>Ojr zSJHK*SE7arA%ap@ds+oMr)n2`W3uqAwXTHXyH;E_mIXHUGfx%YHoGPoV&oyE0ncXqRx7|=bJn!t1bIno)1 zh^^KZ2;1m8O+8UF2%~)rcoiipkB%;)13Z)BE@jZ=CCmZmTml3$8&H6e%z=P04n+eE zqht{i0G}9^Z3R0ay7SEkLmu}R|OJ071VSxJ{yldU? zCYu&d%uQZ-%{8xmW zW2Yw+MFEx>jm31E>9txUs78Z|DhYvrFP+WV3=6VvF_W$2m(qoz8tokzS&uNb8alJ# zt}AXZZN8k&6T&DXe!p5Smvgxs03Z+_2&#%+$*kFZ**)L+^vryDus3DZHR!>Lc13l- zI{0`m3Gmt$yBr$2b_8$A?<*-zTjuRL1v%#kKp;XvK(KBjgs4Oqp|v{u*Snt^NL}*a zJxAvs{O-oMP$i-Whz+cjySs_S>nzSyU*qYRdwmtx`xX6nl@V=OqYJCm>k)V1m##{T zfID6Q01v!SJJ8c5#w!_?|Fg7Sy2^xgxr#KW2Mg{82_nQorw4%-#=*;)x^$sCjG!g% zK?DHeO@mJJ#fY~el{*}u?yVS0S@e(CmRU`%aBpy>FfwEGu+B>u+ zT3;HR{FV6R_XB5tE+6?SUOW^Z9<3o!@CO2_f4R^=*q1ixl(vT$qtcrSg&YeDbNI=Yi65aR145@i6zR8@FG^Ept;`Iy7@$6%-K&@jzH+ zoanW%50@)Be{V8dOyBm>m;C4tPuzCNj;$L;IDlK$T>6f`d4I1@J-UB>bG-E2=_CGt zI=M94-#3b&{`zxl+Rr5YDSof5ySP}8OmNQxX( z{gjx9BVQnr$z;L-nGym3P+I%^;NNg+zdIE}U z>$a`*U$XlK0?=|gT`boO!wN@KpI>4eGNYR6AJ;9cC{ijU{`f!sCAcP_5LTW=q@Sc7GRWEq9_7@5E3YcrA#)Mh+OyHzdv#`_u%7q zHBSAYCmFAs#NXB-x@)ZE&PoBJKrV3u74c?}KeA#$oW> zv|+&c3i*X{bgtYsw8L8FY7HQSpaWzWIGGJDK$y7enAlRYgEk5GR0{wA$a$$VtGuIm zkh?8xJ??maAh?RF3xpgvkrcdL3Fiy}z%k&>+=&1J2%3bWsrV3D0Cu1S3d|9Al5BF1 zwGKe$7?r()vy$$3uej8`b9`V#?F|Wy#{8km{IObkT2{mJ1${1%7)&zLW>u|GDByt9 zziUHcX7UsSQpvPfMXNLI7bj<~yLj#PErXLeFq)bPWdY?yZlsETdY*HhFQrAED;=u_-&ylS8W_P zeaarUw7s1N>B_Bgd3z**Cl|@otxXXWrJVUO6B^| zBZmT!9={x@*bS{=EG#UPYerxH2+;I$qheSVU;sm;5fdOF0TA)|8b+fh8MSMA)bF<` zwK$frqBrU_-4rDp4h8a43tQH2G{yLdBWEKUBetm87Vs+}Bmjn>3d~JgcAPk}w@|DS z%KL+An6oqqE6 zFaGZ9|NB?}w$tprq?XN#;wq{J&bi~NhG1LY(c~sTGmVa;N<u( z0BH+@)60OcmhA&$46y(ZcGJgkAnc57aSKj z!!0{hvswr1ghIrrtDvDA*bGntC~0veHsus>j+(+7<(%0Jx;8?{IG_kH+|KeO>BGbrKjq<|GWBblL zKV1vijcua?NRWYv7R&ImD>j{*IrGHP#ix!PCHmsu8-m(TEo-+!$3g;Y^ z%Pby{>%b@#@>guxxGo~rIF3Z3K_$57?6J4L`U-54E4K{)$GcwiwlDqbrYkSWotQF; zlj&?4q+$YAm5`77(U~JB3l(d0bacz6O+(3qW?ER14YQu9+kVN10Sm|&5gT%jMPyhy z2LhJlYNINuYAE85he8oalw>rWu3%=EdV>m3MASC)rA&cAWiFll%%}b_UzjgeW&kq- z{-~x`YK?SFH<_G_CDsqESywJK6okx%dE(UBdZQo*d81bMahl23OaVk9!teg|BTIQJ z7>xkPI5ZHzfXe_N%1q9@=HX7iZPt-*$eC*hFyOQ$dUseJSLOFzg3`gWIJ+D&Xi-EV zmPCe}MGiPXf+{sm8L#~0-@(M%XAYe@aL1R1>Zgb=fw;k_2r&SV2wb9~4+0CdU^xChgSCo26~Yc%hWnFCBGC#h z4~HE`w?m2AGTI7TvirEV0qtL#PAi@8_gy?~^BD#p>%#i=Hn{y1Z=zMT*UgFw5%PG{ zc4}Cf?POiYh>NEKIH!bI6i`7##N=4=`-o*@5lR8X%kfmaaNxJU`qn3(JAC+D+D;9u zt(1zzhLtYTNT9b!=%KTx3_=qTUw=rhWtSg*^2w)8oL|t4DmSvlLOj)5s+S(xe{{-3 zMN0z$iua|y_)lM%oNEkhTsJblHV1u=ADCy9ma=(C5l_r5%uFs7>*m4bsr7w>44d;t zEf5O5Xk;M0m`m45kW?GCje@eF;RpB6BqBYBPfU*wuFXC-bM9y<1C%}cCIZ3HZ~o`^ z2%!N{Ks6itgNw^c3-gPrsunBdN~J<66%dwX+2Yh7mS&lv>XTJf(=^5yC4`u!iBYv) z$41>J*#vT3Gbz)8!EjF`67s3Ra107!O>c;jboj*Sc*6gs|M=H42lv?J$zuMP&!_ms zsASe^4MU9%_V;aus63Zlo}Dl3-n`>)-gd*s-v6#QzWL@izWvoh0};sd(J%Cno0_1lp!0NBnCeGA-GnEgzd*v;=d!M_nK9 z9hFo+7B;R=oZ5F7s%k(EJvMuwXM3_(KHBTY|MdBf{p9Y)x2zcwkj+&o5S0P6WLchD zTv|wHHg4V|%d%~ovLe>&bt#}!P2Hr-A_OB*k`#=wDoY|%t%_!9X3y}(l1aC3+G00! zSqzs7`G5$4R?nxG2_d2Y5ad3^cjUPv?|A3Cw(Z(5b?&I3lckws>FjX`aY!&t1Eq#8 zU$^x|g;Fi3ps~@!!i@3lZ~gKQf4JwthaTU$We2hBvQ899p5ObI2Y-Ek|E6K0A*>Jy zpoT>tGl5`t#bLOM4Z8YGH$>i9&aK7i*(_+~T3z|Y{qBui^&V-?zdAOV+)L|)tDc3)I&P)MjP3aW%`AOHzbjHG7rKaZ6Jwx}KUC`4D;9__b!(yV!$ z|2mYHP~BV)TS+dW?>*v^d`wtW46?ks1jb~e3nq<8zkpkCC1zPK7lKrAY%dgk0jrB?R`0-Q30kVR}! zkvZqKZR?up^0*uVUsUc%CZ?Abm*%I|#zH+kJ%#`{t;OQu{$y-86(3CXLe6B&<3ZKZ zwDSwoHsOEso)1*>ISyHB=wg3(a5=kwVeY`}#69WU>EZP+_WOg4T74;B`^BH`Ua07t zf+NqLeDsOuQoTcrA|?o<8;Ab<-G855Du<#zz0LqYShfI0$njM0#Ikp;sE)Pg-4ShB zsd;}}@bAQ{?^gUZ&mjPjhsSBZmvf&b$TmkvI6X15?xM?I`r(f)%;)}a$L-?MqoGI= zA!bm>nFs-;P$JZBR_I}wUg(%z)$RpA+nEA(Hftqc<7G5nfHUgay{pyP4hZOO%!+2O zEUOZ7uO#9cbh?^-p;xS|&2vk~`fx*jnmzS^Q9_JbxYq3(x+w3e|2pJ@twyYyH?)-V zE^@k~8X^!&m*b~8v}O^5n? zdFEL~Q4m6FyqcLm zb9VpcojVLho;!5JVzMttDarTsMhAxa6(jkTfBknw3KU z0K`q-90&mC9^E^4>hSo{x3ddz&)l@$)#7WqE%YtgLCLB#i$eswU~WzJuw_DX8IY!C z7E<2E*YS1{LfrJHLx=-FDMbJxggK|g(0#o_z0f#(|Btz8om-eH6l!bNk2k7112|u9 z$O;}E=?ByVHl#Km?HwYfRc+K80QiG`4DdiaY;&Vj$fSBxn31JrJwA{M09aj4D+9@G z1MvbV7V4!8gZn2YFC87P6KDZY(Ya>w*_kQi6E50(5jL_Y7ZocAU zUpV{x{^#}{h{yhO?Hj(iKqMcoLm0G;iUok=khu%KenplU2v^T)*u_&^s2g2p`-M(d z*Sg-k-_6d^nKlS=2cGu-wI!Zy2E803=X`}-+3kL}=+V`qRzXc@`;HMqUYY`cdzf(7 zXtko_z)c;wsuTBWr9(D!6XkLT@t}!}+2s(}S~|=g7*2ug!~;M0=Y5Yo@$fTGojkM1 zZEJRZ(bBY3NRFx`Ame@Ah1raDVq4z!YWN%Nx=a-Qf zxaZLaH*Fs|eEMv*pcytfGdqPzaWEdp+6ep1QwxRnf8;|S_}JIq@mKG=`PMJ}{d+%f z_Vj5giv6;3<&H=tT^S#VQ4=D_F~-Sc(y~acS`7vQ7~@*4A&N2<1wllrs$jrH0ZEd? z8H5-UL7HElx#fn7uDYl{lUocY`ej+pWO5A4`MN$cKPO;p7?vXytl7GaLuS@tGCumB z|Ml5MeXdc+aJ!&urGk+yv661pB7xCZa)Y3X%z%&z2!^W_y-}~4HXR&Vx)zR=F$9Z~=VEr&D!+VE>Gv*#u->P~qBSFzFH6?!QT)7bIq=2hNJ z7TY~yt9sUgmN(=orHeK-o75TE>>aUH#{_l?q%OR!H&3&-DhE!1L(qcu0vs|zNO+|0 zz(c>i{R{6{IDa-eG$L|ED`Y2Ua_3JjK7W4ZnWKlTYPr6&e0Jje;mH|W@NbCs#07t? zRF!03L)RL5wb5wQ>)QBm&)Tsu14@Nb*^ii_DEYdPE0?WC9auvDhTT~cjt+&YW@RB; zj#0)i&u1!23&m6U96?B+!nvinL^7)DX2RcFEtrG-$xWL!W7D`|)R*i_Cc=JZ=zVez zH0bTWzi0FAm!7%fiHonkBrmKR935#8-IVwjfBhH@$cl-A0YEI<$zOs5(}$7#bIQKZ3_}sDeX-SJhA^oFwk4hm2-tCrfJ3#Jw6Frjk1Jz zEFSLf58rwBpT&Se{q|fw^Rkz`>3d)M(cJVI&8`tDaN7{D3_-AN79&I3Kq!j!T7lNG z)e_{iT**w$P0dcH!-0fh+BKu>>+d(0&prS6GpV(4OXEznAq{y}k;_4}j<-(A;w>Cd z$7{Z|TG}CaU8N;8ckkJ@M~G)HFvd8LDdLzR6-za)2Njf?U&5)u-7kB`>{RB_`)}tn zPX^1$a3(lVDqaZDPC08Cv|DBx1`t~%-(VNiQ?q4|xj>2#!WM=bb&RqM zZ~=Ev9FL=ihuk2D1PlAb@MLdY2#2g3ybfB?h-=bSPJq#JU|Yz_f~0tXI6KE(oqfI!XwgNTce z3j!4ch9Czr7GwfM4k-kbJGmt>V}RPkX$V3BhE3F1mz^PmI1r&tddGnPIEDaYh!DUW z2nb`?q1eV4Lm&g-vs?JYhyYt-eH(kG@BQhW-~N&T0%w;Ble3F!hKFvw@tP|y-7+xV zTghb?v-PEATjOBomhs7jsiz-#e9QXO){Dn_QiG+!;?BX;r5EjzB&Dx6WEwCQhEbms zi6$zV9`MP-u|QJ93YKk^N|jUF`ux*IeYihauhelckcuTOTPtYxaIzLjLyYK=xR?Ek|zzvK@xgeBYXAwg?y9KakH-6%oCr>>0z>kLH zG=xDL1At>6fc5VY$37sCDoD_9ukJFx+T5DV0@3;>WU`4JKXOl3qd;G6=; zFyojoDoBt5jU!G4P7osq=+tH+vKWOBLSQ3EL`-E7s*KmFF%G%QZ#7N}O1IeFsn{ynG9 zhGcm4c`lnfuBlk+O~lJO-LiSDh^v*n_TZjFJ{bVOAwn2qL69f~y~)(8Z+T^-TDWA#ronhG zrOfB^VT=!xSObxi2NT85WqtO z5P(e;Ct#@4k+(i)-thL1rmN)f$Nn;X&wmX@Rh_6I!zLgKxaRO$9k#0`J%i9|b9sR- zb#)JP^6>3F+4=y`1S9Gqt1Z;nj?rmnO7^LyS~LZHy2HwkSeDKq7`1BF303iUQ!6 zE078{#S{t_;|2gaCq&>FV>dyRGZYTl6;M0wsX0U~NX_PLEl=VmE+14hQU=%}(59xI z7#lix=iT4>{AZ@qh2`mNbuoYB{PF|O94sy@A3Jzz-^q!+2Mlt!QuFFt}b&QuWHYnK5v}O6bGzN{`#M;{m4HbO|HNGrGGzncBaUP;*%}ee`acK zbZAW^B(F;a49&D`YMXkbC&U<&krD~}8`VNUFnUywjK$04Isib%LVBT8ESE&ixr}eP za@Vnm^A!5e&&^=S7-Jj)2yr0bo0yn*?BR!G0S+W%z|{92*b`60D!BsJ4aui|{8Rt< z{&&6W{K+S=ZHP$ZP=m-2xh&yOU;l8amd~WqNE{gTAU(NTm`Sb5+9CnmN32 z?Cx)VRgOEZMv&2_hnshUcn>!3W?c1vZ?1XReJFyK9%E%$wesdg4xA*J-fe{tYE&7B z#=m;!AzPP!ch_BD>h8XtNSz84QW2`{(73J^tn`#!^^pHRi_j5g*tA=C2gUoVn`_ql zt2g&^yQ`g*bXM37`MXd%SIox?+}{THgWT1kwQ)(CxmFdy_ z6aW_lAP|e%CgBtzBv1}*i&7f^5pn@w5m|=L841f$0AXa?05mh&I;;w49b8>4Z;eqK z^``Y}GwCAc7=t)NTn8qI44Ju^gFpP-CEI#09`4<;am|kP8~)|JZ~dQt`rCi`_}~A{ zd*5-(rQ2c>mJ~TRKNH8nPk-Q@H*V?w!>|85b87ZwFWNpkzc@896YcBQ@{MpTE=bba zHNCQgB4MAZ%KiProHDIZ3HeoG)=y8(R(MNB&XsmB+ zC;ZL+*Q9!Sfd1H3ZyQR+pE_{#=l4D`b)LTJnrlq6YCwb(DZ8*BLag`|gvG(sz?y;H z;dnSAh~ZG&fKpnkb04;uxO3CU#K|*@rRt?Sb{su(T2-S6AyLAF5R9=bsr9;c^)=T3 zjuh1&4atA}%})hLOw$Ymf&+u2x(?oO>l^*dtmmg~s{uifF&}`GP!{jmXxrG(AjXm? zigR;Ii!*Zzvyfh_YQY3clNuMt9Na9 z6JP?5E^@Ua8LjkF^SotcR3U&6ipZ|a&91#>`}P;V=kdp@TJ5#3fCiB?LHU9ia&jv~+R^>YNMI5`tpL7iy&c_?tDOgUQWXCB`e1CH!$gFa!(* zfD_3CughcO!+sSNnP``dJtq#o;>MfSZQDLIpBwFq=sKsA&KK%_isl!WP1{<`SGTPl zkYrnxAg7W^IYYpvL=eVENR>IADI1eQgQ zEi8WGeK(KF@{r5vU=n5+(Xblu`ASK3ldBtjw*v7i8GIER^{C=1K!i!Q$W#V%xp{JFW-yRaBXJ;z7HjB$1K)5eF7HaYtyMl!~f zcS|lWaAbD>^a30W=Pfa(WB;~K_m)dbM|}WLBuk2L`egOxpZq)&Do;N8I}p14hF*-0VV!LzJN7d-M>4h!S>Z8VgXAF!%6` z?q3KYY37875JC*wV5JQNMM0%jH5$W9M-QDjb-M4et9!z0eeqsj$gjquLNH0CAd!T* z^wLzimM)d%PS4_6F-Ftdx2>OEu7o4LZEKR%Liyr#Yiji(XACH!MD|3wJQ@^{BtXUyqd<}*Swc230jJL&J+^h{Md#<1AP_NO z58iv<$eJ;W*&I-evCZ`BZ+LYnx5Vlbg@rxPUX*|p4)}dxzZ&UHB%^*wNyYnjUw!e= z_&|C&d-YY9z2cRxtmJd12^3Wv9346R)Z^zTX2Vg@Fbv-G>SE389|Ckx6jqj1C`hZI zmgCgQCGA=G+zo$qLteVrVb6AI?@G=fU;uHZ=rv~ZH+}rexwQG*BZo3ieJSjxwireb z!cHHJ7s6HXTwjH2Y8N-l1X{%e%Q>f@I}L?PO1xh{=VordrEN&-hVt&BJ}Vh6Z)jFW z`&Ub;vnqy@^PMpUA%*}UgbdSMw{GCjp@ZM~=J%`s^Cbhiou`d*LTdEbTCZJ6=oPJ+ ztrX^}*(q!me7xr8HN7xjoIlM3Ci%p0JS?d`UnJT)JP?Q`C{iHg0C3B)5C&L)&R7A^ zitgzgC?|0XK$rpOFwdw377?~!-7<0iNUl=M?7MHx`d$UVhMBGw7Sbmt4m|P1$)g7| z%L}KEpU|?ps7e`&lwyNswYpR&Gm*y?eSW^uw`NOfU{Ju|qODsujSg^1Ow&%!oUhhv z1zitD`?qbsC>WB`>7_zm8)SgH*D)NIWu;Er%Y$ z`rK=uzjJ#2xUqYvU$Tb=;;Ehlrg(5DdF1R=MK^+~;*+JG-c+?(_sL?uR?bxO8`iBE zO2oB#IS>wFEb0vs3G^Up?>lx%1c(au;<>44BFu;#i6|VRa=FYH8SYOedLqC2)$Q{O zOVxSuby`Fg>xij09j|M2^LYoo3zz?oSm z6t)W<6ae7nwort|+agvI?$qdwI9qEIYPL>v}Vd5k~+3f;fi~L*Z&|u}al< z|IeMe27d92-{hw5j>bh{HLp|VC{?@L+T2d7OLq`)(3BUNat?24cbpqRD}QiPv+Sq| z5O9D(S_}*3&|&Ah_p`$U!Y13|!4pB=#nGY2 zFFI)-1k6?yg+s1sh9zMC!kqc?m%n0h`s|lK@V-KN?%IC;8(w=uKn(+E1JF-7<3?j) zapu{_9{$VYkIu|YUUKDbQ!|62x>U^C91pD7xKOCa2ghPVn zD{jsw#+c*52oZP8gq(9g0pna!WC(z5+muj@F@zj&4j`~YF~7(_VUnKw%XnO>0m;nH z)N4y~^W_a=>*kB;MuC>I<#u#pH-j z%oU1hKSL!7=1$*rT|ho}lTdr_Cw!N`8&am}fmgqL*SVt$`;VWI<8ly(XC`M(o}CHD zg3F7`p?EAFj@XvQR5)GHwg)5IDH^1`bL4oIUb31nIxc|v#4nDVUJn zibtQ?&z0y*mgW=#DRcYWwK7Z~WLMfkV=S z5o&H`1OPw?GQgX)yGmeyc+=wB4D<6yD+qB4CEE*{&95YmVM2Kry~!H;j*KCp1ET&WU4 zu>tZ6mPrHxmtq6itZ@}K6%|+WXOcaU+fTWscgG(T@oQJ@QrH1a^^Ze}c@|#|~JswsjvgKM~ zsh%!F=-YMCntY+Kc1_Zx8F%)MD+qB{(aAVyY3>-OjtNEsNW#R@fgk#r z-+ca9>E?HR;@tiNPd@i}@V;+1vktDV%jneoI-w;9btscsZ9ou%W~gkFhO9zGTW39R zMyJ^Y#Q6$9R|WxYZvM7;8$#$r9zpK4qSG#+2><{glYyxdDoH+@>H?Lpo5WUdU}9Z5 z-3%B4P-GUvqEs_=0gFJDY{~)PGEyvVN|MBF2p|^#W)>8JlBfg&e(=O^e=;%>KJxHG z-^u4}nm@!+8oL1oWBeBxtk#?}rDkG!`v*Sq z=6C+hZ-4cZAN=44|8wWQo?z4$PNv3Mi96N$ zOaV*bfxgA%Y;bUGy`Yuz22|pcRu$GQpPHOrF4pQZQ1His;jkh#061{$`H@6VCSU&D zp5xbEx+PdMXG=yE+sv{~oSEtw9o8G!kfKCI(e{Ue5~8vY3HggtlK@~}z^_{rAt4qd z{V_2tCi;6TN6wr!4bYbgFI6jHF(Rw@*^_fo1-$+xI}Hs!`uyqoLJou?m2$C=$p@p6 z%6vMk`XyBe1p^CI=kYb6GJcm&@hxwZrAA-rGOK zOk-hbF_lW?ss^b6A%+&KmGPc|L`ZHVdVlcC-~8@(HxI`AJ9~Y(bG2g!p7wwE{lwOT zM1_i4saJ*u`mVn6(!Gbz`Yl~RIwv@q91O=_KXLZyWPd8XFn{XAX+twNtRHREbJ>jE zlT0YVSY~Ni_1CfAA4~K;_xSzSzvnAv zo#vOW4aPa=oxw+4EPVUH3wWpn0U#R1qQU{lE`qOt_y7uV@o)#`tsmnxl zl`b0O3QCMIFN~wNAFiO-ZZ3309e^AQQA=3>VG~dU0_0$k0O0C8oR+oi`MfzcEzYnr z+L%eIU|GxnKoCqMnA}E!jEF^96ZN(|Mvx;U0L)~Y3y?DjAkGCQ6Usyh>b8w32izb4 z=?&7G3J#Blm$Rk+`q0}>AAQa$W+iG2CZf#m4+s7IJ-xb}@If||@-r-l)m@JsJo%eH z-!nMWPYM0vm%dzUXg~hZk81V$7e4oIfBojSy!8!lDO76T{KnV5{y*Pt6w`{d_0aF` z9!kY=W0$`-_NKpn{oTL4>!z_^{^P%Y`SQ!IKDqx0k}(Fh0&J}MMSnu}2YpJaa`@pb zzU86Yu6g#Md*!+EXI_2vKi~J{_2X*}-}%!8EuGV(!g7VPIuLR3nG+$OU)Rl)Dp9{A z3yNRhD5_|-WY!BBtCC2xQnd&BhBMhEHKd%LDmKc`UAAL=JlZ4Jq0^J|g-T{bj0M7d znfyG3z!y#|%}s6UjrST-bS5%+enyfcAc%g|zjG|$k4X8d*{GSlq3DB|Ls%fYwrxt! zW*$Cy>iNdG!Bo#K;NNrj`B*sGA095&G-B~cIILGnmtMa4q4Vb?S$_235m{CPK3}EW zFvU=fT8Y7d*S`NT6E_A^eNdv4a($hiv5}SZCpHGeH@`o1%_mRbpikgpP{ovo8^rBZ^K6=$f!@qs-*-Xj){6By4OaJ|iEyHUvnT%%HpZ?S*F5k9I6veTf zTR#0?|NXcB@X^2fz{me|-y;`YvVj`((P#FJuj^f(P2BRj*QICYKmY!BY~6j?2j2a* z+39qxTpCb>6K5u3$+(p-7?Qd^RWC(+_dRycay?uDiGeK}fAjITpStw2VzC%RjOZd% z1Eo?eHR!*1+ZciJY+*9xm#_|*U|M#)Hx%AFwk=b)OPYD&$YG0sBGwa;aIH`X_{AE* zM@~$@FBX7Hgmb2$)rt39L5G2ZgZkn}nr9V8D zuNr6PvIYp`bLB)bHkV%7v1M~O5l_m2tFGMqo4*{MoPR)8lz<>nAfzirKtY3X39v1r zBQlm|7v>ZG1Q+dvtR7R5CffOoxg{(|t_<&e-sH#_O-N6j(0qMmKp-$Tzu2goCykm< z@g-9|#ahWU%~(88&6jvc5G>md!MHy@Wlx?vdHRa0cK_xl-|rh8onJn=G&h%shjpW> z0%}0iFxcj8TX$|*cj)v&EF7y>%VK1prWb8pj6^o{4`_392W$1(o~Mpq^`hODRY@<* zmkPzvwHubE&Ifz?u;f=E*!RGFZ~mLVW_g4p3p2mX49GFyCIB5Rq|J_gC{MH-yK6}Up=3t*P zd15-fREt>4!-Ii;`@wf=#Qw~`eR-gNm}8-L=kD9y_qVDq92whU8`ih)I`Qv+IQGN8 z)E+xDJG+zt41DivU*6ak-JDF_xOL06Z382H+SXo}IX`>!)VbN?M+c-}*_O?qw0^_L znxVmb(K>!^Ar$Zp^hB?}=K8BI+pgCegsHOR8|w>IYlU3HJb38H+|pvQueY49?%lKR z)VZ@!$*+{OrMcP3#VnEohTRNX6$AkQP%4$i)@_A^p+@D@^jrntxX+*L>EHkSVXVsQ zh7*ZY3$3Ki~jnw)@P^mkA~L;JE(QVt40u4438^+L zGm>1J8eA8QL@9w26BEHmw7(~|FnK|3s3<8U(91DPIenmn4m=fw?3un`JeO-R~ zrxz8U`s3IC?v*!P|GV#eXE-r9Fuo1~WaykQF(J}~;7j-f3K39fP+@_v#3Z54;Pi6w zk>`&5_MXRn{`>oF3mHbaQ7H<7w4BKh(-`|lqnddCMgZOAac&*xh=y6#06t$WpFTh{u8 za8ODwRWN`e!_S?}onI)gU)$?f@wxLe%L_Av6dASO@v4_0%(I3)KC(u^pin6%W3ie+ zvbkcmUXLW=)m-JwxrIP1KGr|7T&x-d2$I~;3{BSqe!p$olB$S4d1mrd2w4Mtsa&a) zEmnvDdqdHf4+FpWar_8!p{;;P6Z} z*9iN4a#Vt{qSyH8#p1qWhegDzh5Yq9Hobi3w&mqHy;cncg5^?y1A!s2kjpS)hkSkk z15nXEdh2UKlC))b*tD$Cp}wW1%+q`K#Y2JqkQ7$X)by;N_$K$#=J+3GX}H1&**suj0G@LU##wL|T_d#byV3lOXeDONb=3c~;}i`kC5 zxg-k+K~WaCZi(UEj&^vzw_1Q!eQU=R*hcYyEs$yfq9&rMmzuOF$O=_Xo2GEiyVx1$ z*=AZvmrp@W1DKQW9C_|^Dn|hKCE~rQfwg13s^n+T5=7PHbY+InW`7X{%u;+kP>!(l z1bpzD%40v=-NO>&J#YW&55Dx12Zxe<<7-DL(<63mG%8)zn;aB`H7Wnb@li1tDCQd{ zCiBzNwPWYzA3HRC^jtRV4{aVv1p-(JNPGA0Svx+mCLXaYSSl5C+$Qmxg0bI-l!51&|=E4=8kov(iJD`rm5Jb&Pjv0MsA6VDw!`GxQQczQYm0%&Z* z7KB0#CEyz!d(lm=+;RC1F+eBgX0q90STX^&8dy+7j7_UtC8s9lwFW6_rI%j2`=V`| zopPaBVDhtJ! z8DGDSGE-Ir2w^l9Joc9d7MB(R;egE`VT=&czpvf0PfrFmXb!GKew0z{e z*CB)k<3QjLQH(@FsBIEJA%I+f2(kuRy{-$Wc09DE3j?A9*O&7S(%6;03jlyUo+&Nz zi2wj@s(DB9YS)0fynIKoChO({}XpTbJra|{+}=0fB*e`V@X{js*Mm7 zT4;|5w6x`n0$C7oB%m!E5gz|;&*5LjU}oEku6pA5xvOsd)bsm~4GzWYA}^H6$$+oF zFV++ESs*J$pWfrwvR$}c_kzylIMSM0v>smJaMghQ`;>wB)g za@SyQ-#32#A3yNmGZVSHe|`5yKl!m#Z|q;b^1oXyx^gfs96NDxX=$k} z;q@EGKmMr?-|~u^lwb%UluE`_fDfHGzW14@AARQNa?K8gV}f0O{JBFpLw(65+h4YE z$O2@cSPX|^PyXc@od*WiZ_MU@$&mTAg@Xi!^L#dBw8_Z~}^3?7d8Ok4dDZl4UulwLT-yn(truAK$HpEi#8!o$eU21SCo1=hAqO8V!+#*mCKt%|K zLz_c`_kaJ5zxl+~gA(Edo|s(FRdn>OKa*;SaMCa>Q@3r~ym)Kx#xGp^?BgHWk{CL> zIC;rs>u58VC$?YATYhKJTNejS(*_g7zzae;*tn04tf|lbM|B` z8MaIeL?DYIyfB$?bPZiQu8VwlPyMd&H}CH2VEH}ucM$>SHZBSP5=<#0oI(VV1dwDS zU?Q190|^Mb9+{kTC+Cg}iw^*61G2Rt-2p_@4V(o4aE6coxfA!vAmFG4d%$fB3FL@7 zo+RACRHcAZ#+YMS#-3D!j6)1L0MyC(hMQ^>#1R4*FvvM?I1nKq&WkW$5J8MM zWCml2p7_MxNB-qKZ$5nQFYmei`*pn@lW_w!m@EMVDYq$=EF`HE*nrj$Rd8JNA!&klJ#`XRFOD^B^?tlGez!&`V+iyL2;5n*gClBrU z+Sk5nHOfEw!B4kcdiCgrZPSa3k3aCJB*WR+Sy7hOt{sw9WLp-qOo0)fFETSXb$)90 zmv=liS%%@T|G?9aoSZM}!DPaxsB+}s#7tpo*8vqiZ2)0%kh{Xje z#v=ZKfv9GJQobPw!nvvC`O`DmYM}@70Dx09>DhxPBrM*1>DZaM1#XmWU7uWTOlEVj zpb`xQgAtWbP}P>B{*)nr!*fd)-Ec)=5+6t#m#>ot`jh9+P4y)PmuHVopE?={^@oFa z&G@(uqVt9Fn{W8V%inMpP9?T&+^AC09||mImNSjg024QD8HxJok<-)fc+1P;K6#;{ z4~D|=!9Hqf{;-eXppSAXfHQ^sT&{R_W42*O$BG;vP`T-btCA|J<;yCdf(!vf3RD@FEU1PO zL*c$&1OchnGx?fHK{i|I?Ojt^&IUuVVzv{kd0 zs3rSw$N*}>*&^tqb7+I5dkBQrFT~{on)}st-gSu-1dIdfe44qaIN+3F$^aFq;A0{X zAc2rm#stfjI0MX&+Pa1n+|f!gtb+$>6#xJS6o3{O8{!ag0X7qxH7n=9m5;n%n#@9r z8gt%^sB#1x2W}DoHxOisrhPC~FbMtirqy3f%ImMH6XP9Y=lFaOe z3UxoTqF6}=f|5ddqLGE=Of(XPkd`V{NyLgTAW3o|UmF@t5o&9V#`yXTm4@-?6A!)k z$`@_8_;Q(OFTU#HFMa=R-6pSk`R>WZ?CCSpM~)o-#3%n@;{4g~eEWxmVlEH}Vhk3S zbKACTA(rj;`*-f#83+cUAO<4QVy#9Y$QBC>;ln3S>WuBa=GsiLdg%D6nW7$uMVQ5( zKX9UNeBFVG*@qrKuyOqW*+3@0rsT-L*}2)-Om@@GEx*6(56kJ?jW4-TKnP=K+jcsg z5d_h)3|W-`0H$e&dtv}8m0A@EP{68f5kVG<<@!P)n<>;6=F?`Q8c^g&DAYHUi1dc$ zGfSyhxLU7e)AJFGq*`(NhOz4}zSJkidsP8d@&&VD>wKgyt|-c>Y;GZ^omF_O-{+OoJyqxK`<jmJ#w?Ca0m?oh0OJu93k2c@p{8!s()ni|{!5`yh{k)vp%{Y< zW3jX_bN3(qkQfMZ3L&Ei0XL5&0Ki4|aDYTEB*i$|2p5UHQ{dxlVu13NOoyGwmj1orCO4JfBh4ZpT%}tDu>t^D%9%Br=?PJ^>0t6vmAp}9jQ7c23 zd&#{CLFg4Gn-3N5DIloN=7pe{Ap0lz*N|(*ROc-#qa;p&;I=Ae0*?dNHYwZ16|Wf)k;Ix*010Ar@QXG^Nv3wLCt2%ONHul2M$}h zA&T;Ees|~WQvU4Z%#mY9mlo$mQNI25U+mep=hd%!?N5LDi*mXC@|$i71bn(?8ajax ztEwU^Lb+UK6im;|F)j#_ViDW2phW-&G#rk`Loq_+hQ-9NBu9c20vSWUD2bvp81Y?s z=}t-HI)v$RX|_;e0$wOB3qHw4c8I}-L1jVIZ7%0Z@4M=fZ{K$1Uth5S3cNoRQhoT< zullPQ%|?Lx{lW2#n=`pWEE+}#GF8Y)cLg9653{;15)b)rO_bFD#RqIXv9+ z=z(Kr7wR10L_oGBU)g{@K$i=}a6ryYIZqFZ+=Tw2i09o&#Zt(p%zh|_L|TL~+gL+0^$0|1D! zATUM&q_QAMl1BBx(dhQgLnvaLH^AntSDrh!6qc3xP|aecTBB51v(VjDhK&@ez^=hT4*}Pt@^(AAP zZW?;ynf=d2BRz(uXR>+2(5PwV^To|OFDW-nM$E}0$FF<+ZJKV4CIcmn-F@HQHEa6o zmFn3ur~drsKfm_1ulEP!`T2QbS-NI#+_?GM-}`PppTFwrt29k3l}a-+Gn3QP*<68J zjOaEa{JFjRUUB11@klJQw7g@-j)7zZak!A4?}_=>kBkk3!kg9)aR~iD@NsF()hY{Km75JmowSXu@O-e0m6lRiBf9YwkV1aVwRA^MQ z6Kk%RYr<6~d_!(C-g~ z1T}cm4O{;CPg6U-@c7IA@$u+e{(SSrqXEvIoK0Uc9J==M_3=dCTdvx=G3h%$zbp`A z|Fiod5t$K~E!7d^A`}*jxxT)kk>T;V^*Xhqm;mkM&_ zuU(X7C7S~}6)xU7JMKiV>Ga`u9iQa(7Lh7O0+C8*h7Mv zAe!x5Hr%BkUG;m5J0gxY#}&A`+*{m+)tHVU#5Mo`Rt>9vy#L`lfA+eIH|KNnOY@Uc z`}V%}n#&%#`=?V&dp~l^8yha8!k)3H6ilthYp@C z)!+5bHxKm>>{vf?^Gh$e@x~X!5UJO!-5b{Vv6`Kke${*4u=UdISR?l2vQV=E$%J6h zN~H#ULXDIZXiL;$R>c?K$L3~!@$lZoVnd>J#+YW=wT2cA`3SMsj*VWv`^tJa7cI=qFD~TX^yXW$ z*-|>4=1c&HM&q$N|8$omN!zz?pPrccfT)sOH4y_#>hTQb2G7^MPFltkaQhRi4ZK+bfx_ZKJsKF6joKg zWm%a_1|j70`5KJ|rK~s6ZyGhzG;Nb$!~kQKZijpcdcE!o1Sx@xqG$FT zDC8Gq5r~QyPbF=_PoJJFd>|FWqwqMn|a{J`68TRXD;)SvEtbYeMg zTesbOO;0>}et9X73}9;8l=Ke_?cIOKABf1JB1w{n@Z{v={8DYhU`Qb5;>-f8F5a{$ z{Hkrqa;bXhmaUH*KKsMFo;|-jd&4E070ig>Q~Z&7K8q>8bk|Nr!Umy$vXke|pI@E@ zm@aGP&wl%(xL?pLa^hUIQmAbk9eCH9U;pbrK41yb_?mvJ*5C{y5m|&V+oqIo3Tl#zw|- zxk4ZiDpxBAV`y6s{q|SUA-`?^e}uhvm>p+%HvYEhGpCpBO}px{EX$UwY}_!WITX_q zYJiV~P(L6EH6ejOAf%Cy&=R^0*bu&2_H5 z&dyFdv*)?zDfe?fF$5ClDrCPYX3gs-2#1R#fKwKfkfY+u3L7eAL#weP(DjhElB z_l<9RW+?6W+$)CO#VzVoq-uJ-&EX=j#7V1tJ-NZ~t$p@xRe(mkAYMohHoLI^i6IHkF zSC>sWN?LN0)A>?!u$Wwkk(`C8ME-xy)_?NwqZUvZlG;Mk8G{7X>P{kM0RV2e;RaPz zTei)46h{a`%sHik)|yUN&&YEEwIUZUER75eloyv4=BKxBA3S+{>YANf+=wpN&aZ#< z+n2p^Z~tIVv)Ket?ds0kcFT3$qeqYK+;LG?p}4%Xl*{G1x_YY3mYFaB0J@=Z7T20J zCvYog8^=x@M+lfk*0yciE+GMkY#@SGc@z?(F40Lxg?uT&&|zSmgTyx zVOh580stgQ2^#h3>6wc*cEgD!(=_AAQ6#z5uF49Iy|~a-JaYPs6Gj|}FpdmaSIyK_ zS6{2CycF`o6KD5q-~Pn2PY(@lXtrCHne-fZd~#Xdk}LG)o&9k_lY!@c=E0-)w`s!j zF1d8)S3dvw_kQq0akrz&iA+X1JwATb-k03)lIwo*;~x$U=ToUn!)|nU_Xc6>WfyP! z#lDkoyMAM{9=>vC@>j=8l*m$OzxApyA+d?^h3hZgeE-48(DsXkzLOJE`C=}WPcB!& z%Le;TEzT&U@R}R0dg{3&twxI&*0aYaUcTo_1q4qWJ@~ShUH9HU{ZRjCU$ar^Xs~lZ zIp&keLwl}e?NcllrZ}iY$S#fx#DnlS1OE~V#XGk_gaLqqT|JdntGcvQ%w~_D zIP&l#&+NST;&!{exG-I-HdIN~RsFeV_qn#+-QDf`eiX;GdL1DY1OXywad9yWV%PC? z-C_ddyL;=YM|x(4yDYQc|PQ zz>sJXX_l8?deikiUERAbxeO95VI?ufM^BEIO7=isu~$n@Oa@c+c)THz5aXTNhkMXS2@y@J>MrLVRwT&+6U+AkJ4b_BmYDZd~t)btrIrHP`_Vtc@&L zy(w_5d3yc0j@27PAtEtHfD4HSfhROs?#k3WU$fE>#h?A~pUg~z>gjFOYwcP?f;ep@OiA&Zc6Y8TbfVeG>2}kh%x|~LNT^98 z%VQ4$;Z|$Hi(mv0rIqqB0))tN7za^A!_b!%Vp_UkShZS12#5)k6xntnFNiclg+L0! zn9^8Q^pug5F%cNbx*qY!@ofwR;_T`xFT!fDu}9lEki25o;Kq#`+SL*Ual7R=E6ZJl z;ZtSL2pEXjm3ww>xOnKV-*oM7zVP1DKmO#~uiyUogU@nLieg;zDg=u?yLSH9cfTVM z(%U~=X|w>8(&;o-jj-j~QT*XQehJp%y6e4eYvP|@S=^K|A3nD5mERqCeA;znB;#1c z$d9?9sT=ZLp3fs6n1hkjR^ zpDq?g1~zQB?ZA+vk*)#Lu*T1vgTxqqv zz#mzc)I<>C^(n};T*=z{^8jM?JpdgV+Ieic3kpf#0`LOsWpNPz2x0^=LQrtP83IB8 z#s%%{(2*VIKnTb?nbgil!UT2(iF9mU0LnRM5HN@sVhjm{5F}Utz&K}AAim;vQNcOq zP$01OX$67+uN2!+XTT!_2zEyKtV*D*N#mjHL_44a~@}nR9<;ULt<-hr8cd>x%`ZquO;hwZPzSuGh zkp7zy7Z4UwZkDGYjq6v!!qR-;YB@skd!45RkI~ zhMdKK$#&HAnJ8q^%S#J@U=}f1l{n`N#FUF52xSTK7+?stY~NK{Sdt)XyMeBm8~T&Y z`jU{+$Cs9-m&(0`o~;{ur_M}=EcW~u0qnA1v06SkF?sUj$)DZvB-i<`OCX| zPmVtq01raw+CCKe$UrZM>i0cx*Pbgb+q`iL=Rnb{g;Kc`210PRDqerZ<%gHs-#_DB zcG=dHs$8~V@D1B@?PkLuP=MId5*Vp&+r8_t2XP$l7~4Q(mKqw|)SF3a>TIb#xwx=v z%V=*;Z_}yUAbt4cxMo_Fx_#vM>3bi0IzmX2Bq#tOBxV6(m_;#Tv2H1G%;%;`p68vN zt;zse2^CAagpqCA-M!m4Y`j%Z^-Y}FZ<#q&QYwyLi)|U8Trwf+5Mty;alNsO7_*Y3 zIXU&eV6E9_^-N%`$^!sE7_902o^#CBov0HYu6Zvjm3eU9(uwfam$^EX>2)Dgz^SJL zVIqhSX}9^Fn{HWXH=v5JNS>XU%x-$w)wgW97&R0=dlM*%+4+~<_ktz zS7T+Q$I29#<7T+B^`$p#y>1WAW`LnYF`F)VatwR>iW76qV@Jynlq&>)l`bR4Ic&1J1bt5Py_ zokj7P$?=|^9!XLpqK9GR`>x$=N<^tQJwS|uC#EJQXTSQ@uWO3dWLW6l|1j1wZYT5V0!bzNtSbp*2z9A6d{BpVF0+QE5%|SL0{8EHkVK}5(Hix`mXD?8qNNpqNO4%1$#!aP*bkD z?&`j-R67cFAgB*pzJs86_{gbO+{xY3UL=MB zFIdoOlp$8!hfYT0{H@nt+=~n_TsiB7gx?NuigoVE+ItQ!)UsW_BNFG1yt;4(&c#7% z(U1@V0D_dHPXVKzq_TRccG)#IboULLAQH~B+iErr-E;QA2L{0`0p9GHr~8T<)TH&B z-#`7Y|MK1Uyz>p;{oc2rjBdSd&%0iA)BE1`uCM&nm!?mRzyDoteCW~Vm)+>|U-{Zh zx&EcE{9|9Pf1y0DCi>jKIrZ36sF*1Q-t5DND^H)yclUJlcUPC{PaZgppseP)Wlf!) zneu`#M4Cc$OgUHL&h3rkm@zicI{+Z|Y{&I|RaH3$2q8(5;+P2`bWLg0>RrXYk*!-= zwW=gIWs!vC?yfFNBK_T65 z?FNmbI0{;=Rnj&-Eut7YqeTL zmec8MquOZMZB^GK({yJS4?g~IcDN8UngSyR1OSedR^@`^mRHcf+;2$4@RY!P5C8uQe`u?QNI7>)k^yeaWWV zUw$HRC-xssE7~F#R95p{#n2I3_guWdLlgtxc~z%|kWK}NqDWF?z=h|Bx~?mV0s!J1 zFe-!)aYUmq#u!IY#28al$+n#xyLZL+SZWu0FV}1FPhYvn*@9&AOLujUzl$A_KI;vI|J;#TfojEhPb?a7) zBmjUc%LpMLL=XhYWI76|=Xt9<07Qt~XtV$TQ512`qbPy^6GEEJCg(hkX{}LL6a@+- z6B5QTB$DU4Py{q`D2o)`kR%ldX?eM7sFq=+T^=7eR-UicgCHPoaLJBg2oO^V$Du&F zoYZVTs{7H)HuPLEI#%ERWODOZYoSs3)~^bt(Tqb3;)^aCuvAM{lt_YS7ne4Tj5OxP zM|!ihwwEpBFWtVQR*&q4w|Uc!@BZ@fqf60T8y!92m71=hLgv%C=5h;$8*{ls$B&!E zbX2FMQW@&lb%M9O=H{iT>f&N!aj8_PwYY>s-@{m%UtS8F_U4fd$z;;=ye(U{q|@n% ziHRTm=tr_F_YVv-+pQ=HWI_lKEQ$yKtwyc9yeMImHBHWZ0HLC!lj*dc#84m>MV=o( zEZHq*VQ#+FYBd@)O_p5O*E9{#7#ngFa=+%tDebr4{-!I@oUXw*762hcVhAAx3X=a9 zEcjoS72?}!TW^Oi0s%(r=6S8nSLN#!Oa7mSyPD zZbO9d3%sVakO!_#EEMaz9>j&+j~6(27yMO4v@?G}_-z?Mpk}9**)6Yr!)O2Rr#|PRBTaqRm(09;X|cDp?|JPNSv1tFFQ!dTH%4*AB7eS?F8hMowb zNL6sXu^iLTG)*rII1sf)!%8GJZyX&O?7QiP>#n_ekDOGQa2kGm`sh)wVTatnoThZy z_d-U+rcIk{+Xeu5zT>(!gglC(&aGAy1tAoMVI0TnZ+;xdlu`&GA%rm|5k=FKAPVD% zF~$TSB8I(%u8qT8TeptXT4h~VWEtzGoX%)FwvJ*I!60^NTg&yxP|^!}d%3k^Yp-8% z^2wwr%YuN2hIilp)aaIxr)Q^*mSMYpZ0L=*U;g!%tvU&R-+J!Ac_Rk`Ee|D`aRrGX#EF#;G6 zi3ud=IUdH^$a8SM=OR|SXTZ8J;)0+=tk#d$bOP3ldhLKOiU#MEU)HT(k(@f$ymP*v z0^o|rfgo9b`t_$=e*_4iSnri}Dm!qk(!A~>SIh2eSi@Ht`%FLt_zU!HK_^kmR|UP1 z00KeG5kiCm7;}aNvl0evE~CbooiDlQsRtj?6YA*juGxmSbbS8svF93%)^!(e*^t(L z{m9Wrp4uOwXyav9eeh!+J#%=UWk@Lm>*G%vSec%l{@uO5{`iMKVyeoL?;2RMtf=kM z6Q`FEbR?8EifJlKmglgR2h*wJ&mMMWs-wd_EcA+rXTk_q#kL_D5S6hwcNh6hOwQNn(1_lO}mX-`%4FVqkplO=pv|ZO#RaKIt z4zif0&N;vss;U}0At8iP+5s0D$C88@W0Z1PmOVR6rmW2yh8L$MD8!~})NRjk9D%6o zwY%~u8l#K}2dr$V*(4x5Jy#vtxY5ezzdiml%&{iNGE+hg&CfP}ZMz9evJTF$sqa2? z?|-nnAN}~>e*ChR?7Hza@18p|AJE38ft@{tqLf^db)xRSNN@aGaE16@X%Aozx~8~f5EhEcJb!XV(b7071=s7+Zax!a^1Mz5K59% z8*LnkL^7#J5*Lv2K-D!jW^;2h$y`?=opHUO)w28g`Xou3pP&Epzxd>zeelDnR3@UG z8SGq9lo*JSk&zp3xwh(5F!uxt?e?;nNLgAQ5O1+G+pztCp|LZk4s@bpBdN5SbxxUX z8g;iObK!YT1i3dKrxNPnyM8_Y_P48+hB=8Cqfv}ySuoK_oP+aqW8sTU0R$_xo)vpt zF?7HLhn)$0LWqvU>-w=C=j?qoR(MgAxd4gKTKuwhDR7m*xBgG(Q0@F7H5Z`Q3s10a zi}i$k2v<4>AOu)&*m?0)F@z4Li1QE9Slka`@p6{0Cr@rvzPwc(! z)<65u$BewW>&i>t_o2W1@F(B@ikIIe>z3`&G?6gk&AN{u{o%t0>^tv)&;6E{utWs>7}7SG>9?AkP8KC0|Ud$OJzu)l{BM>R!dDo zO(28>rGe*5lEff6dg{#4ljDbvpZd-3A8fZ;?|R4UUvb0LCr&lCj_!E>8+YA&-58M4 zw%zi5cXsw{e}8`>k?0J!3xZIRuwiPh>qa4EOb8(eL4M#;NW0EAraU2E# z5{Pq72uUW>LNJIJ1d%24q3(2lSB5|?Lz2{!Ni&mT3$5nY7&bXPiMB}}W!h+G;s5kx(J6C1gWa)Ck5jiU5a&e13fL>~9`^JekP0S}jT` zL`2mz$8oaRY_V9JoSgj5cfOO$7l|YRLiAM1%4YMuy)S>&tF5f18-(+~t}QH{In-P_ z%iLRAQ5Aw(+@w&$wLYt6@SC6-yUos~EP^0kcWd7!g?SvT)|J*J%Z=KtBByprl$ z?+YP>xZq;dPr;qs$|_2otM8tFL5y{pn9Vi^N8es>$N`6IR|CLW$g|>}aU`IC zV!c}=gaB|&C?BjuhiLVZFcY3+5kujP)8W)U+bv5JXN!G4#Ye`^Oij+Cz)vUmpsM)o zU}~{cEjb5HJZEOi+u!!i<5Q;|ee&Ty{?J=K`O)_jvgyzM%~za|zx|Kj@Z1y6Dv($pNiX}$JO0m>gaA|pQm7jieStHwM>*v zDS*IQsgml=`w@s`Kc7}TuPh}}-MvZVb{{LXncFrD$wi>FxR6a_k=B~^<#N3xGU<=~ z@V+aj7bC>l&4#5)M%oAwZ#Mj_Ra`93ZW-IqoN9-5OGt((hpnb(X1k*D05{UMBeS2M+x5m%se*hdwwye$uc^ zj4{Tz>4l8L(o%$=vb;Fy)JmaS6$+8ee8du6eItWI8fQAes)koA_h7UsaUBJ3Ij0003L5P)<1 z^$xYaoDKoDju#0Ut<6XL<9i|u8ielV3mFYIe=Wk7()ms;~X#n z0F;0rA_9Ox2q6;;Qpf@U){EC%Ttd%jj@t7(nuy4MuPc_F#$YX$FJB_gr@w%h5EhC_z^!kfzeDO$Xdx} ztK8K$n9inK&_%S~UTo_|g17xm+b>P`4KjrQ2wJTc;3A;hu+k4d^JMI~lycv9vEY`e zwVRdl(wtjwh7A``%3{wj@~V!hAC8V}s5YAjshr160XFswI+2TIfq_CKsH>b1A_SuV zPyk{9Vh*#pq5xPT$THQ0sz3SIeZRQom2baw$BuNne#w=Cijn-UV~*6fA)U;Ku$7U~)w?dy zRD7nkm+5dsiAj3FwL<48fxqNSiiqkhA$>&q%Sm%MWab1Q3KsdZX_4cK7u6 zcmM8pzq#-J2e00Hb4ZDzB;tt4vOIO<$d7*X6Dy%2L<9yj4D983RWuv(r%oLFS;gkN z_uN=5Er-nJA!xRnVZh?Z*G)O0S&FLrp5uB==!N}*8*{Mny?^~maf225vVs5v5*G>r z7y$?b>d;$PH4X4e;ii+mgdC!d0E-YD1Hb{K08$_j5FM67XI?M|0Iaq0tzd*8aHaOj zk>C(;1Xjjtt@u9Yzd8;B5{!cvL^>-FBM<<9t#%2%sJC+gTwQRGwXkK4%ijrM#Oj>1 zwS@r&Aw&dA5+r~@76BxR1RP-|C8IVEC!c8?{h2d=Fch)g z+dJE)ci!{B_^GoPLfLO;WO{mbZr_nJZZpt`Hq)#zAAk8@zj%85_!qwMpMU??pa0Y+ z{^Z)XzI}YM`8Qwr(zn0*4b7Bpz4qE5a;rgHkPSmg>5AlYtWpieQjF}OJRHmvGKPsw zrS6szMs~az0Ro0o`n+9EXN>t$YqnfXX~b?cRL4CV`O}NdYp>f2A?(CEl+t7}DM?Zo zhC&ECW~ypTo;u-F=6$<_k+7Z63qfGlO+ttS`bGvK7Iy6Eu`kVDie}C`f)TCpFCyt*g<_pC_ z=ID`=UfbJq?2$n39t=idf3#b0KpCl(&wci>eY`Mb@dLruR`%w=EymfK$Q z^4F$hDMn;qxL*lLwy&SIrG8l1fT#i#-qS@%L^sJplRff9o|3KKWyqM z_d`o1X<2VKTa-o!p>k!pUavcjqw9KiZ;#imC;JATxbwdkOMb#sLrO6a2(4$tqO}&D z&dPHMg!46SUvycSGP*9W+M%k!wVoS*1VHDMVgHwPQ^orpL>j7mT-$X&>S5XQj37>meZ z7*T}e*2z~CX64eO(ZZaSD(J4^ zBky_RYoB@g`4cBjKlbeYTVDH?%U}A6&wTVRw&apGUbSoI=l~8>*?0cvhRbW^a+{NZ zw5)2#$9_lvq@XulG$aXVgk^9NkWE9I=Xl^n-e6ZR24SAZE+vO1X2*tlBjLw}OcdB& zbei>MN=fg$_!8T;xq$7qy?y(3T{ElIsw7E}I*`RQ&E+On9Cb1W}{a zP!vtq^&pHFn%=Rw`ic2QJ!I{6Thp{oEao^~qtTLOS(fD}3Ob?(jImCAN>$}fKokT4 zrL;qnV~i0(6h*_7%+H(_BGk2npfQx#)(u5Jf)=)xOSJ$qpon5N*`2g_7`OfCqQM^6 zZq;k;XZIhNt2TXNrgRhf&1633$59XjGzb%_P6W*4vYZe`A!k5Vw6$gtO;H(RvZ`Hq z#on%B??VsX_xKYJjgD?0xI?@Ojvsw?*Y1rQH;tS*aky`&XKbi%bZqRwU;K2-uFKy4 z&b=olrvK>mH+=fK;e|#6TUNiKC`8(SY_i=d=}Gh0!otbf#_{sPZywrz%`2`c*UOA? z*&>3pCT|h>9+a*_MvU2&JOK#qK-L6Y^s;Vjg>wx>{=%^nA zOG`_&T0H_J$E4ri{oTSa6tM=N#QADZ&{_@!0*J(Vk=6_B?%b~cLx}%3b3T8^^Yj;1 zaH70C%aa?oZ`gR*HSc=e0y5ZU@r;Z+6TA01FNbX zPEMMAy&wu=NghZgZ@g%9Zhk2vXLOcIA;os;d-rU}m?R{Ci#Qo*S}K2ZZXu4t&D|Nv zX#*NZs{Y9Hr~10`2^oW`uSRVvEgh=a30+@Ys`m8sbaiztFE4We1cxoV-FfUI5pwDg zSy7C`3VWwHDFDAP+z<9IXs@5Ad+XfOMGWOj-$0{ON zRuxH-6h$F~NRosxj)HJtaIAlzc*SK`)GC2(+vUn)CQ}fc6G`*jV0?VS^_V6nB*T<3 zG0cq3SY>fy;PTsN0$AI+p=;0958Zm{)bjK||IX&g$w%(~pL8+%+M93ALw@Sy)bZof zZ@KOIpa1urx4-80>FF5)fk6xvvN&Q`B2gU2R1hLd5;hGjjzbJd%vcabqoboUbF=S% z-yc8q^phu!9TklGq2q-f1`sd-goF@|!uIU+iKVIiXO2CIquSF?JvcD9VQPB9j~zvY z{XN}-1AUw_RaGH`b8~a;cH0g7p1xk{*R53ci68vXkGXD00rN59YeuzZN9$9c{~M;f z(0@Uz^ZYsoSucw8zt}bEOlW=KF#!Ms11MHFF?_w&0qT^B;p(wgeXSRbeZcwQ&w1~8 zCrNZ32UiFI@T!xts;1Z3hCmEB6^zGNaDo8Fm?J5Kt*EJIGDOzd()7~d2bbs1W_$a{ z(u}B=qOe^F)8n%xgZP5PwOV8Qt{sv{tdnp81VKKCxIeS)-(O9u7}UV92+ZiZ|cu3)oZ?~k0KIDiRWj^nyUIV zux&eE=&IFfjIqsIx3n5fS(c+HqCyQ~$~lz?;xy!}?FZrVOvT8I<+=wYS#4C7cU-*X z?qB{Ow0+1?rB-$QpsTCPOr)yKs_VL%A*rg4F`{v(>YC>|xm->N76bu+Tvn83vsow< zns%E~3Wy|YdK^ZffEZ%{5a%3Y?EAjwd6`T`QIt52<2Zy65<&<;aU4fcBr8M+!5GtZ z9YW#zK4XFqdFQ}jTGhvAYJrA*7I*jMtR!*ku@0S7Mh%49lj|Qpd~D~~MpMqzn+yX~ zZ^e<6@a)>yP*26?ex#l}yHrSY=Zjr)^NT>j2tY-a1dSlUsZ^@fYDH0GnmXqc0Dv(B z03_2H%EDq#_tjV3xbM09ckSK*@pA0PlwsEkd;5kKmRgSInU+4bc~hg-#LZTwd*q7C z?Ja-wKJty{=e@A2KfgsslXG)2kmJ4|CPIU-*NX{ z(^Jd6-Knmgl)`X3iUB|vLO%*I6oLsTP#iN!QC#0krBc~!cIxaj1~8XSlqwZ2LJEbd zn2L;}up`CH!_eRV=r664gCoPE^;%Wmb9JtNMx|~^Df*2=QJ zrE=}VbN~cGwD#&Gq=mT9@i~V?Ab^k)8OgFt1cnrGD!71Fu7G@2dFofqW5234mxyTr z7S)@y3DDu`=|**;PlK&S@aVCLnW|$zBr6I+6ag5;oI`$gW@2!7;In`A&)@su&)@mx zcYfu|f2YRrN8bIXMLpe~pP!s6UA=9n_sq{OxqRoy^}FtPVE^2Ft=g_V_x#LMY5q-b ze)9)k_tFE8{I-P%l1vvSPd1&Dsf{Fx)n?OXv<>C`$It8>7~Q&Y*r7a-p;ue-0`*Ie zJS9sYr)UPklu|_|l+t3cc*`xfOq>}X9O$dpDum)z(?zOE3N3giw}cT~|BVpHApODFpy@#DRh!z!-Ok zEw!3^;>>BR2}%Qq!R{^nm~n|}eq=39x6-nuA=WdPHVYDxnF5)LXyA)Vvuabg)VAwU zz0r=EWoNFmWcxu^q1c_yWeuYzmjg(&15Z_DRh2tRx}0-G(K@dCo8I!4R5~{|HSTf_a|jKnN`( zZ4`POKT73>_Fn%otfYoFUot(poJb`Ff@dZt<2WuBi^1gunwIRJcB^9^u#p{ugCkK^BCa!5c^b0ufn7*3RdlP{cw| zN@2`oz%4^I(waSSaPHpk9(?Gx^NrBdbjqUHhFh&IV-e@_Tjm#A$4^c!lxmc7LWs~& z0w*j}ZaI}>N1t|`b|%|Xuh)ZCYh(AoZU5(kKYD84Ti^YMCk`IW?%X-JV?)wjIQ;A} zKUuu}_IKWN?XE4?T?wq@U%u;2-~apf-hIbUxAhet`_(ZvE6d2V9caF;DHEtzCA5<+m!|L_n0P{M?B zZWuQLW3W#Q=)}|M<}}nwd3=S)!s`HjM-aL~Eys%iRRNOeMwd;=C z#vB-u)M*jWb-mL(r6^huL>MW_L~glMbA3u^Ih26kMkYG%-^f8uMOCbi`=OU)ZLW!``NRz06=v{brO{m2);4ElD~mXEf$ zzxC={zW%j;y6esdGDO_-(|`HtxBuleH{Bdost?_Fe|>(Tm`a#3kRj!i#WCZYQXV3N z;+P4}3C4*;YH_L5a@-A@Hsy=my}iAW=NJZ-B?ysNE-iKydoJ327$kx7`AsT-MeI${x0;=uD`Zbo(I*?aEE49LD8uJE|lKjQ_0 z0(G?V*W3I#gGFnFVSdgkSG5Y(A4&*u?jQnmG#Nn0HCU+}t?0a*YdC@sz}2BSD|_UA2Q)#l&~w zL{eK0{boBHE#{B{=IYByfV+yBsgQo>uE(5eWz)9Jr)Q??3(XkOk%9hB2K3r%ubrEp zNu^Sy#U)*lDUKQI|+HA5UGR5fXk`l4fMsY*2> zhNdMSvdk9_8HAAj`z zryqLY!D9#Z-FVAOOO&% zt$OI_8PAV18EJNIs?BLnDyyctbt}~htM!nKY`bLt(I@xpzSOo|)zE9Tnh+wHOacH( zON&xM29X@au3ehz9qWGVr$4^=&2LF3Q(+i(+I$ccqBFD{LI9BHcqN_8#(I-Pod5y~ zzy*gwaEJr|0--gD761U^jIZdk04@MqQ)>hr%Gm05|C?KckPv9~Zyk~=002USA;JJ+ zvC^=^5C8xYfgl$cKnWlTAR-`MK?UB?a{`P*z6u`#U=Ao}9Efuh*U&25a3PRjfMEnB zOn^X`5Dqv&FbaIeqOR_&jLDPtKk)c3f9#Au6{;3SYQ5@?PfpdBXA5aV(d6-yCm(tG z>Co2+hENCy6vZ(0bixUnXU|SePR%wOr9=A;IMvqM-uli*$7f&um!CR)+MA!44_Z}O zD_;4kSGSupcBxGUN0nkmfq|2{cHVl;l;1Vp3=W^1fB3!!H}_^40F74r*%OnjIP_G_ znGniih=eIkE;c8Q&*iWoIA`@rPLhFM{Py9cZ~Xjmw_46*dV>g@ot|8>mv9_jdFhbn zhn&GEc0Jn@jdE>iv1_ncS+1uH39$C5@zYe&jeJhlRD~&mM{!tD1U4x28_iUgAtkgz zHtV|cdp2#AkjgOwNJy%ot5WPm0t43z7!#CI0Jvc)x~9Z_)O4FP25}HOZY`v3Ro6Mg zM1rQ31eArLAJH(50@wBRL^7A{LI?@3>G>=MJf=V<2BSCiLUvm9*1jni=M-hvuT5nd8Kzhw>uldB+zp*s_e0SFvG!xD8w4|d})A`7U zKV8^9WMmW1KJ>)4*IxTq`zH6k@jc^D-c?_mNu~>k28i0no_lKd-ksa8ygc%0DwV3t z>|0u_UUk(a-~0Jb{_1c3?(VyP-BlQ#sI{{>j(A+C8F%K^qZ_C=`G&Ab^MgW-;h9jAtJCWL?pAtVt=_v4rgrXh|f7Xa4- zUn!)mV&UZBAHII_~B-w8b!gx#N^2nldi|o>7=HRm?&y0 zQ7{Uc9F(dvTTZ(z|>6#2(YFUOv8VmODZSU*f zdaC`Qn}>i zRjH8T%e6@sg?*zN=M?R0_djv;@KHpRd|z5o!ghH+ay-}$Klbi7^GG^<`s9ZGp<;Kz z_Pu7qOBjk00^7C?!#H*7bQs3@e7@6$gE5pC14NBz6a;}6M$Bv8dc`G!L+QDt=Iqjf z<9P^?F!VbT!k*uhup~*6U>qUj`+mn4g&1Qb0f1NYWu3`Qoe6WX8+we1(2pR&^?I#d zuC!eb38eu^LgIj;RMG+@0x*&&ra<_<3lI!q8isM_pKIu9+4IlN%^Di1H){i9qsz@E zz(BX;AY3XS>BR9fQ!`6rU8(E$ZfV9KZiVG~Ef9dHIt!>0i4>9)j**Zwf+8+VBU@T* zv?yP$H%&}H6l#hLu?B$>M6j>FUjQ1>7z=6q^!#t``h9o*P$H42_^zp@ue$D~3*`n4 zg`!GB{kfZNy>WD;|CxJ#lSpQzp)JnLF>2RMIpxtHJ<=;l-A^BM-}2YLdc~i954i53 z-`y!;Suy2uWwEl{nw^@A+f#l_X2ws3!2;20#wOZHjq(s&u09eU^ zh&7sw5CTJj04A$`i~wA$%qN8qaj`~*fe@h8n$_BQAp~L_U_ppF6cq#@2V(uTQA#_e zvN95_lL5lmVUd9(5k-cqGQlG%C}*l=6#LS%XXc;1?|z6{X8CajjCVhf}qhJvYCwb!haHpZL@buYRqTP5VqN&DN)< zOPZ!B2^a~XF(?z#h(puPT=SW)9{=$_J^hPcUUtI`j7Aa=rr5JE_@%&8y%b_8ND2Z7s;L&_MW6l8Si;<3Tw2M@Xo#j!TnXN|X8R?dP9 zSIc72p)rpwO--7~N~4kKE(jh(ekdEVM_Hv(f`ALk%v5T;v{>_f3{}A)z`m&@JiE;S z`O}Yn;=BL)ABL2E^IPAwW5<@azTuDhy9+noa9!otp~Z!nkg`2jTwhx}5pZeGo{JlE zC&%|K?|IoP6Mena(@#?`;9SRq%eLpm!TbO94gq<%R2zTnM}YG}_plSkmD*C3s!Fo> z%ul|(ZS%HPtyRMD?yEB_hL>G;ecy2T#ObA?hJSbOeb?N6+viR5FaG?`H(qj)38C+} zKqj)JbIvIX2qCWP=JT0mv-#-54{h7Mw|8(TXg3d?IOS91E;o@@(MTo;d^Huj?Yb(b z1!Yl0kz#6!($TenDkg+Djzb8M6{X}r$EC{k43aPvHZLd7aijk=dJ1Z0qZPwohJg}TA0JZ`3;SNuVd~b$k*nA2?T^7 zo$Sd9ZA}P{Rx-LAu7z3WHAk-<3L+s^f9td=ubVEk>eoO(2oXZ?bq8SpFh(`P8I4p) zk7GtTgEGqJazPM2aOW=;#t*0>8XMcRuv|X&>=T8q{Pv4?C$!nSe(}q8vu2ql6iAU3 z2%(iq>T=GH?D|qWi2Y+HPvx}4H~#T^FMaPj{0Pj=Hk_HVEMbIs!jvH7Kp;p7z`zkP z!QAyL{H%G=yFLK`ie^uw$2L>T5MHD0w2EV6dv3XP!{%+dzI=Jt=1Xo1-~E?hYH{xS z|NfnxWUgWdvO=<|+TGKC$`ubCJ5^np9~tiJ>E2YXRNnRnS0gU>_hwSMvbfYLw`=!2 zb*fpeT(o1;*Z<|~_y6uUsYGTnY62i9`$py_#+R3ulgYGhSc!C6l4ZlxDWx%pAXTEE zYFX*!M!nUT+1THGeEiJQPaaIBEY3pL_EL$2<3^lIF^gyrN{URQ7$Kw}ISj*<{^KY@ z82i4fD9Xy0Mj%2Uf{@2laDXsjT&RkU3``*_mlw8d*t0NmN-JP)BMudYJRukqBBASy z(kP^qG9gKf$GWDpn+;7jap*w+cW)iDE6XWewUMq>A||0`o99wV2AQnui;E{3bsI*Z zChHtwEMS+FX~*?gHCt|w%;%4fPmf-++jrW!EHOZ~UA&{bxNv6tl$KB+po*d`ER=Ta z+O5d!>dQBF=jCty({7@zhDv>wWdx-pnzaJ9r8@ z4wqudj3Dk(QYk&1a>}z4haQxSk`TXIlZhh(ZhK4@$uJ>m9irCax6bC^B5z|yzP{er11Roq4 zot-;#^vU~Q^R_>}^4c5Am8Ea~;8y@+-)<4h2t6ms(#?7`laeG^V;m&&{d14rw{f#s zuhzP|dwt)R2(ld~Eh(m|#oVP)teci4Nv&!F2m*x*x(;x_YPkmR6+xB@o1_IgPta}cPaH4+!HV3$ z8lz^7O4BJgApyX;CTqA_$G%`eFP^voo$_QH#~sog=bQ?TF@X>wDl-BC>OlsQxg50M zfhQh0bnovPrL&ptzDU)dc;c~znX|)V8{75fUH|pH`T4n2DwRrPXcU`P5@Rf@5*6{x z?2G_tX>r!BHQxTV4}R*K-*9Dg{6vNNLN%e4fQTswV8(qUK|iJ@kv&0WC_zjpqLGNJ zr|TEJ{o_ym%O`_{Ww|%EWz&@m^Wo9q!$*(&oa))J%Ws-2m8YktO*5fthLuVoEa|!_Axr^fk|YP7=Tiq%&x{`ls&&V0PA;|E zjkZ=)B#ZhAR zik>WHi?-Y5017t-0I8PdyUj3E?|lB*&%F0T_x#|W`ZjL%5J~XZkTeE;-*E{c5P)e69@iYDQpH(xm0s)P{g zl9tf*rq_xfr(qN}+PazSDx?8m>4aLcE)(o7XWbp1;+ifl2&4Ip^~wRFhb2{NkznwA!w8y_~p;v@$UDIA6+s{opJ_&5P~Y4Mu@za zK_URzdh_gyD$>?#5PVj9fOOvWtY0iB$k?d=)+{$o#!?%jIySUYU{ zdYV8SLu86bFyO~H7Kp?#mjrfLfCvVZawe#P1r-9~(yV>uJN{<>cm7(;%r#R*z~jZ) z30A8@N!|CzV`ooIUVhc(azcOZ*{401YO0=QVL=hk%+!{hsAy?0ZOXY#586&6ry12+v%Xl-byL$#E0xl8 zQ6n2R8asdM_s-2B-Y-}MyBaAG1DH!TBW>^P31sA1$2Nx_(4j5&>x2~*RQ zX0w^g7dxH{r#yzc$Fe3>kG^67Lb(~uUgen@h&79>eSTCP;9 zHf!bXB-%VWOkrknVR1t)T~s9%MO+m@Dcl^O}rF8VriCj)STUy}Eim~GPtyCeWKqIQfTlZ|c z=4CIbEHqooje#EZqTPGUOb_EKwWk_(14%M-W56WO4NNzwcbiI~zex0SF6ndEYqsjM zXX^EReiTxdLPTNcI-y_8jp#!|wMJP}1y2lkJQ zZIKn#^L#~8F~*cKildDqgGZjPA9&*7`T4UAA4&iKzyXvYH~O29N?l0Kfo-LO>`afG7lLg&=oMz+}bQfnZfJc71#V09b{a zRii(rMnG0Wn{~w7&YUoQ9;84BIRpR*0uWy(ley+%aVS<;c&j1VDq9}{3>jl<#Hx;j zqRAt3pu6F?5B%^4%g6WSOxexoR+Ldp4JIo~yJ=0Jnic$i{s)W=q^ksq|#Eb(h5eirYA_d41 zqmG0nCJYKFkURnm7I?08@gINk$uIq7c!WF%RZR0UxS2@IovQ-&#z;?~JCXAc}aVk#mGaWb7aHoJrr z?0BIpleXtYG&Xe!fH;*%H(Pctn}mY5n~gXQJ0fqgsxUzrTk#M(Kf}-`1V9$pzE{X~ zx1ClPIJ!hMqF4RKb#Hq6iJ4e8hd+P!qxb#ie-a1_8%B2R-2SCce~S5@p0KLbs-h?> zZSIs6OwDnlOfjRlt>L`cELRgodjEk#9!sZ-=~~$jA{xc@gC|?WNQN;?Cv`PuLxsE- zxssv8k)O+^BtuiM|MYP`6lh>%6r5Qo_VvlCzEH6_(SgJAy?u?A9WHnpGzYTEi4#wI zE_>kM>Hdvl_0r5txnY_GzXZDSJ@wYvJ}X%%*O4yuq|+V3*xdAU(L%NEv=Q@Jz)&jN zw`F@**G7&ZZI%P-HmYam&Q!y8Tc#om+(ytOF-FLtECv*Yu6N+f$)tVJU~vr8nUXZ2 z?OECEC6`}1KY6xA`D0H%e$Smh|GB5pVT%os}*G4D= z=*V^;gaiZ<%wa&FhGI?-2smXZN+J?4Lfa)F($|0XuYU6vALz>H!Y~#W=OsyMxs6OA zEn-ANlD0B(>;V|3yRu&3=33QAlg~^{TTm|M)3wmG+Y$6#St%BiFk;wY45f^@$)y{& z3|)HbO~1V3TY4rf18%#XFw%%Y7tpL}5=l{XgA2h~BynuF+nH24s8k+)_~9e7^8}be zMN#BgMyga<6bQOOh$MnAmWUF@fvzbui~wc2ti0h3uY366hmW2dFICEhVKBitM_~{f zCZREh5@#%?G?HYK#*UFPTTO>x3=q#}GmB^IH{N*L-dA5Wd!$jv;IggP-uV4%y%;bC z26LcZS^UCBKRP_rFRK!zREQ4eh1cUil9cMiiI-o0DImxZnAz^g@Ia|49hrJQZzZ_n zj}2`)RGD3#tMzTn%IUoDJx`XHVo^*|$=u@E1&&Ary#DU4v-1ll&rVb-WeQ2hq~PSt(Xuar{NUEV} zEOs2X_1wMFG)90oF+qx^V*uOpQ-DUxrw=&gQV`Vx2eU8|)DC>7ZC7PgHj>!{*SxH$ zA{pt5mKjcHdgKSyDJwhBts-*tz`?8b?Eb*}-}SBU|1_EHibG#E z%`glhVXo^VfpSBmkKX(HTFeE~_FjI?69=BeNCm)CR0%R31(7PNf!jQJd_Iv#v~3#_ zNz*h<(-s%!ySjRUATSLjVq7NDVr4F?WhG6Qbdr{f!l)nq?fqk0_oj-&ez}emh@rw6 zKr8&)3svI)6o4xN#sD%X6act@k>KQlMk2V<^#fo>n&bl9fvXIe7qC|##1H~PF-HKC zm_n{lnKSOmP@@Pa6jFpJfDpotd>iA81JS8ebwniBa4+T@ARsd+1ac4qh)6s}7*iw& zhXi5)1Z5(KA}eKBmT~mx@$mmRM47Rl8I!d(lHHV9z!A_z$1VJ5D;(- zh2Q`|!XSqb07!(01P~M;h%pA(X8>ZL5Git7Hp$%d7yowV7yth3!w;#d%ppuBtq!{k zVIBsKZkf4sw_>J+m}7(yn{68&t+X0|;H;W&x_+%*4n3y?KtG&9foQNGr^=Elr;?VeC^3Y9L+&vY3oW7k^p1xP9y)F&a-l@a6_(2A z)!NeRe0!w7m@rg?Koo|Oj3JOI1+j>*s?9BzYW2!R+cx%hWtwgK)Wq52%kvCdGU0$j z$8+_h2%}h6%_xd!5QU5&3B)uulU=21E14;N@}K^#z7R9QR9T*1EQtl7C`u3nl?DCs zH@~m3L8&!XDDIh@1mFLteOss!Y~u0dS{s(wf#B8eDCi26bZjHGI{D#;|1%O(=3 zdaa`CdQb0Qqgg8y3Q-USzMaWrY}+0h8?M*u5Q5Nc2kjuhbEoh6(q-@adr3}kTH|U8 zihv7%AQXaiL=J#Jj50zb0b&3t5S{8zyaEJhMHr#us&vS;u+ttWR%$qa3jojxV`U{m z0^%Hx7lFo%LwOsU~G(Qu(pmo$&&;&BWO zH4IypmGWFy2qDX|fEWctHdRR{gIzKJtt@FS)dIuJ937ucCam6};cKZ0Az3mM@`~5vXz3*8~HB?>m z{Fd#uRaK3=VD}}LzT~B^{OaF)_W0y1ReBYwmX0N?}OVmpFVcN^JLxdA;E5~p3df-az#kOh^QY# z{r$y-g#{p(V6;{#cX#)8TCoTr`}glJ6bj4B%gJO?S1m?y7=e=ye0SGdKWZOQxaw*` zp_mIHFqS&`2M7_ySO^B-3ytDMothU>X6aOnI~xeLGTCgstFlu61%LoGM}w**MG*`* zmLw?0AdV%ThyqA357*OXI^wJl!jK~#1(--wxC&JQ>~l#|sR)51 z1W9#e>UQh?U;TXIiTf15wr#n*;+1)TFb@_hB_*IBa4Ypj$YCLuBZMT>43fxHX{NE< zii4(?d3DulfSc-+2P3Ex-WM_H& z)HsM@Mns{1Alp5V&Zd6-n_pUn$q)fNqG6;ecxY(o`@g)WtC&?31!LT9w=u?&Bmn>d z&sS9yBM}6_V^2IiKQr6i-90igv~Bz5Og^(|%h=SJsYbnRTFJ-@35Jw&MbU!LRgHWU zJExA!-2O)&|M=g3vFw6+!&5Nsa9h^oG5`Rc=WX0ne93LE_}Rbxt1+DF497zNOPWG_ zyV8yVhPG|nHg){K;J~n=Hy+$SP9jzu>^VDA**n}Na5OtxzGSqxKE3j;}p)pA=^v<+h$ z=a%bV{kI=Ia_{e*S~=a5xb}wIH*dH|&7_Y$KY8ZxA+3J)$Z{;gKqiE6s_FDL{+E^g(T^EDkKSviN_=WNxL+y{+fI?v=h?OFe24uoT)KkcAzx;Biw3H;~)WKteBO?=2^NJM-IgW&cgj$_(3p-MU zRO0yIXY}Hh8{Y8Yf`rS{3$0)g@+3q#!(gTydSNw_8f>)Mh)7`^<#M@ty=oeUY3h_x zgixhYQ53~-oOC+f-`{UpRwP2(X}Ka-zwiFSwrg^IyG6O=D*|)U(HLH1*$Rln>I9$j zO2z*_Y9}0kbH(J2y}tl&_zDaO1d)IMkbZcgmD}2G^H?~T#fSo~CLnElASnPvAi{tX zh>?H{G6tx$GHB$&qB2AbV-*Poxf&vhkq!iJ(y)-vX(@8((Pxj|cTaZ;W^*Zz;l;`{ z4;4SAk+4&01OnPMl*z z5}M9c6mW-Ntuw!dK*U!>_ErM43xy{jUu6WXb~mj1GowC|E=y6~+M&F$9drhSzGp`2!z4 z`pAzTx%a8zO*Qlw%MMb?F?*8fn;q z%{x)DJ9Fr0ooG0LX^)lgozi7HHh8**nw5;E%utZF%sqRqnydcm$dO}F9A^rNefy6+ z`q;h=8~U?Zb7pQiVX1A$x#5PFzxFk+A%rY0m$J#^Ti^G`Fo<0mg`t=}eX_PpD|P>g zXYNTR@j$*yRWmYy0|m3&QY=kUOkKx7%NjzGWMW}Orw9fSKp~O|^}|37B52POui`Zq zYeBUAq+dHUXG~9%o@pQL`EO)6Wshj67e9Lp`nW$Y%|2I%N$E4(CQU{@@4S z`r!NiByGCITz)!63PMV$uIr9%OOn!Rw<)E)y}g>I6^q46rIJV_>h-!LVL;=O5!#VCQ-07oKLP(u10?Y*foIwIs_;2U=Ke7N3 zXA}r1Fam@|R5hfrk*@J$r|VN8*#5`x;)rv#d+wNPvBKXz1>j8vwebJ`RU7IG9wn>P-R74Y}I_MNd?-w8r5 z3VcmB8m&dzo|AJ{y|j4d(Egamy+b2PGHF?9yY1!j*}wX`F9$JY002RqfC@&pZQ1(W zU;d`6m`4($ls219!_tEw2*Z#dtZAAMFbtTgst5sD#Snv3ChfV=*|TS3D7N9(|yp1B3&w;P1gh4O4Fv*qp+_jL{B_dj0J(+I~3)+l45^OWn*tPuj8#=_8vMz&o{ zRNZyj9o!KhmKBZpQpDn(TtX-D=*ZyAa&%&1rtUI{6x8|FaKbmzN!Vx^GBQ%8kc^_D z#wap@+SP?!H{MjRox?y$cAy+1>H9gHm1b{xj(5#ow zViBXjug)y#s^NsTGe5I&({OU6NR}33d(jhgXlTcuf98u{{?rFAf63MJv+}{&nW0R= z@!%rGDM*1a0_%7F#V22R^{y-SPuHqdZ(wxKQ~U1C6&2qXI!C#DF_SRucAL>ahyunK z5s3jv1rS2$y6(De5Cnxnp<1n0DwWaE(I|A|NVKan;If_Jk!Mf*=8n;;-|S8-3tjC@ zM_BzoB7VMN_KP|x5W-H&@)~fU6~%*d7}TM$tQ_pZ@yDPuG=u#8ou}XJKiN0)NKdXW zGxXYe;i?!!w(zsDz!)$D0R)I4Ab@~SAiUOny^;}zKmdURh!FsBC=*klh{dpLG+Qr~ zzx9QGtxt~k_GJdIxO954wOCnH0KaUcSYoB!nVW)>emQ}dnB)QF~%I21BbgE*2TO9;Oc*L3RNYrH#zR>Wz} z!Rl(Mef>QZt0Pf3P!J0lfCQL<%Yg%^6x5s5tgiMA^bu|@1&RJq>E1OTw2T!#SXD_vcl8}{~%bPWwd5kxeO;}{W4Ai}Z| zEY-Vmxxu7*qOvRn4I`ECXDaE0l0eFqjoZ$Yg8iq?Z0qmq(o9LCLQ020OH%=plb%b{ z5?*YEzkT>IQK`jhDi&OmB1P9)AAAF)maUxHXKKl$ zSxg)GJW;H`cOewI0#qw+6^9$sN1gc-eSQ5Rh8$5WY!+iBk-qe*D^xR~%O=$g1>?rt z92RJ~UL8MjXmaA@rlFx)6nGCk-xzDW>VqHLd)r%%J^sthS6snj@AyJRGPHgxd+5Ye zpLX)G|Nh9sC%ixUyZdqAx$PjmV^b=bY%bXOK3G~hnz58EW21f;JDwXyf#dr5d|`HG z9tf1l2fU~9CL7gFQSvDa1?vT*jyhyV7>-k0p&H8As%iyBv4 zF|GIPJ`xQt!6fFsELmG@u^fBuvVFzNZhOaP zzR?_h^ZEX28$ z9U;7xo`;x53IegD{?-<@?$N(W!s+8N!w&VFBhX6pwaXb!C3^k}WXb@9j zVSU?f*F z>qRbv;69R&0E+7XBf%jTwQAFiysO{!wjbR2i)6wKU;L~5V9(?M8*^9BseXhA zLkL7%;*g&cA!Uq02s=8DcFQSrS;c{Yxx>#&l0+k>D2iYpj-z}goiT8&Qq?p=h-%>b zmu?B~7JmyY1LZbB=F^{iCB{ zKtmq-v>8z*rL)OY*5YjcuYdJS=xIqS+f}&qipv#KUT)RSjclr?XHPC=_w^q= za)|pNHC$XcdHkDy_Gj<>`+t7xgTHAWnkZ&_^RDYM=yh--gb+FrR3J!kDleb<@pq#6`CT_%ow8Da*DNnCdx0M^77KtF05S7v z2to%CIh9o9h$vL8lxwA0Ibj;pOBFe2#|T6ru`*dR4(juBGEpF;vL?+=E<_##I<_)e z+DOL|Ari*W2XVwb*B9|cS6uv#cfS35-}~O^VBeXUM=nilzyWGI+J&+}wi#aJQ)1%U?uM_i=L?2=o3_L;-mcU||_-}~>x zU|)MCI`(`$siKfbF^wgzF^O@`WsW(5ojD#tu#W2&1VJ3foH0d$qgyv0xbL29Z=cih zIYWkN&ej^CA6#Z+?e+Zh?^lXz^mS`-=! z%Vi0$b~!Q>E0<9KU@beYSC&Xj^+J~$F;iDZ`?@dMvME_CgmGAzpT6g@U)LAs6-mxM z-8VQqnn|`?8`L1?k{Z5`d-q+U`R9zZn3z_Wk!%w{a&0nj0`>X2n z|8xAFmyBKc>QbqOI0x&-s55qsxqqI!Ap|(rRd`--)QO?iHUt;?6Biz;^FSiZD%|v% z+itt<=D+{Ur@!}a|Mb;=pWS$0-{#S-SDapW+1_0Ry?0K#xhkg{VLQcD4(k!pU<8Pa z5$7}(PzG2~Doi0FilL%wVJIH>)h}`e46*vnZ~e!uH@;Gav^$%hv8P7!#Y-;T^xUz9 zmJmqLcC|{G3wa7@lG|=nXBS?6{gv0h^#fPG;r6m8PCUD)5``q>P?4a+FySy-{U9;G z2r|G4z!a|NFd&2=7eD|E5kEg8)p?IQJ=wf7h81=O2_PUif*e84AVguL6-Ye0|Lz}O za?Pb(sbbuAJ=gaG7DXtGIfU4!KH#w;7}639fp!ojk_iJsOD2`3T`8A9$YR~D#9q_*@9(dhZl}NQ+Ktqb-z28LorGs@cf~P zTW;F@?Bn|qxkSd$WeH>E0RXpMdgbJy!{(0elXG@`*~@3Mr<&8{GevURb)uuCHs4G(EM%Uc%u_ODx@znP}o!z)w)3QT)XswB%g{lJ&L{)Ufzt~`G5 zU%v3MhaWz8-;>8*a^>V3Z{4_Q;L!GwSJTwcSzv&uM|YNhZit8qf*=M6@JNDk>=1}3 z6`CMv#Ih-~=7V%^-V4~-nfa|_{gV@?>ZSSZH(&EVk383Id5M&SX-fh|gbbTD)Vk-@ z!uNdiqkr<*zpeVw#Q0Lgv7AUi!7WC)=W~JufE+{20RVtw09Kj|RszD#vO2c8Q}TY1 z!n+WHA%J-&a}7BZF$5R@%t3@`?4r?OFn!+xg2=u78~yS^OTZwE0vg4P#k3=m1|&&B zk{qzubA2bbX*Qc- z=ogFmDD*oN1^@tKD>d^@LRtu+s%jX9sZ`1|O~F76*}>-y<@!d8#ln$em(7K_D^>FGz_ zaKn|)?K`~x;QV)g{L7OE4;*;tepmLczxigjO6ThfM6;5n2IJ6B5>Z(1wUP^sHj(sf zw%1j&fa^cH>+a;{4P(2v$tNaNN!B-SdFG)fdItNSN6CRgVQVqdW#wEDdQ(%s{mIXK z{L}yQsn5A~`^FD^=((T#Oo>m=4{q<(tq;HHB9YgQo;ZES{!$y#;hd>z-KvJn(AjhS z<@Zkdo`MkZu@|Hg#j?Iocl?4RDXJ_<(o;`86$C+N?&bXaQnA?G(MgDCj3F{jQ`0m; z2#-V@@$zC#qr6^fzUkjT-Fp4O+3`o7`|;Oa{`s#hp0H!iBmrfh`i!a)W@4?mdDUl! zFK{`|3zW{YqZc)+bqilf*$SM%R>YV#eypLHc=+P$zWv(+-~ac&`|Zzvdg9d77bZ?` z-ZuK0D;9Td?%I&+Y35$LnCaynCD;cvkY&P|B>Mo!?U3n=5E?KqAa8j0M?U%FlL=Cn zW!Z}tW|y?n(-WD#-1CP|BMD~I&|;XY`8j97t+jGS;cXxJ!qsnho6(gzFy4ldFJsM; z)hG@l$Orh*T4})zo*aU;gVF_zsmu1<&W`@nPadMu~jM6Aeox7{M;tlVQk~Ert-@Cv5 z%}A|kslvJEp8K8ed?d zrkZQiCO`GP&jumYG{QnaDP=79->pQ>X(mU^1sJ&!003i5N*T4+0FnoY2#ItWc&w>u zNgevj`c_R&&$iGwRKUcE(YDr%C>O0n+-yr6?cdw7asvB({lSlaIW<`sIx@o)7>lc6g9SXWy~@bYsLy30k${F)%{R+s zZ{y}m4j(*ZH)_lJ)=f=kuYLLZ|Med#Hg;|M)6N$Ut1U4KV-$tD z*4?|NEw^lyC{GO^c-iP!Y9w=>XV(MPx^_+P@bR%`EgtaJ*491ScV2YSC2Q8KGjlEF znVF-bWBdCL1$DV;<(g`9`s(X%z40xt>t4R*fjjSfW#*OhKmXC~-~9Q=(@)AJoySV$ z{Vxt3XgDmcu8g+>3|g`{)#0;^wIeboRo2z*M&OECjbCQlbj15 zDPfFC5`wXz*`p)*cYo|N_r9|C(cOc6tJgpN_^TiN>U}?bWYJ_;1gfHW8cwhKK}Rlrha%|Nnt!rRRmxVH~{}Z0KY0m6rw-^WLg5NLP{us zfG62Fb2>FZNFW5163R#*905q=DJw99FlvnkA;b~@02q`EN+bah006*%1YFToaBR<0 zJhV1;6Z3WP!_ImDPKcGC$wUvgpBt18BJrHCPMoWOUz z&;ILYio?g6)smS`)(7{NtJ992I9dvtK#ONmIHe#RDhR5CYgRkLNkvma5!Pzn$aHad z_=KV;PdxENcXv<2aS*}jblUSggh<>pUAO7`zU#W2b1ns?lrg4hTBIheX!BscYGq8*hK}^rVe(hye~m|Ev~qS`L2kw^B-sk?Xor%Bc5PEEYou1#W#+PdDIn z#+HL)MTBY7u3Xrfvx2I}9TO$X6|xW>%Qu$YqD(_uQuC%^bq8V3Iisa&e1B}0kc(XsUP zZ@9gqqb=9k;xaGr?0Tto$t4$+>!AZ=wj=%g3%g(Qn%92!$3NA3);JhWO;rM{hSHdx z8o%q_$A0$huRnAD0~1G&4es173bR=RR<&hTcJ^Y`0II>7;s5;f*GpHg_CTn$#iu6+ zS~vEj(guR+=Bv*=JbLK)UHhJY?x`-96-Q6p^r823;;{PbN8a+ye_Ia01~v1uM|b~W z|KWkr8I;fGR4bdxgJJADC>+_~K}ROB^U+5~CK?yK<%SDy{g_)XJooc|uT`ea zM3#G1hJ*-oxu}W9X;1mAmT>A@C&Qe@?|@T-T!?&#d;+l>2XQ}eBozYSgrQ0(4jw8Z z%#-QF4=F z5Ckxj$poGYpwJBk1Js&M>_2<}*2NdU{?&=0Jwux*=jZWv~@Q2gMhzXlr}C7Wy8cT17K2QsSz_#sND#ILyVniWOu>#dNQS{@-kALOMU$=r!Yp$FK-_{ zw*5Q%u>y#arLVq%fMUBI5J0H3FPoGQhHj&K+ksqrrguq4(rQ0&@No9LwYKm+`p=(W zK<1kjt0j?c+QI|bo}Sr>f~?gyT(o(7XynTG-8y*qka@wDkz+%bz5AV=Gqte7j5ogF zlH&Nke*as4_|g|%&6g5sv~S0b$rE~Z<&qIEU!Scso26B&&dtxvwk3^9sT>A@z@l!4 z*<8$Z-Bc?`lYAp^zx_fy7WXEk=WwU!Co*sjJSt;+of7TPc0(Uq5*B6AwT4 z>Px$>x&G1(tA;K==S1I<<+OE!le=W7t_inHF;=t?+9?8qf&OBuV;$1)KmOpHzxnsi zeBrHE^e(&db6@?6ELXqtlV4o_p^vQHvT1rKnA$%}iIz-g0drjj452m&q5uL68Rtkc z4I+RcP=H_n3IGuV06-xzgbdAX<=}jEHjpRD_-86h%_o%sl7LDm0pN2PwMeg9)Dhaa zQWV)lXZWqY(j0eE6rmx3M#VJ-C?`WyKq^lDP&npgF+0J+zWl}0jw5Dh>Lu9>D zdg!4?n$4!BWrPSFr`gidQmIrFMZp+y0iwR82w_So5yB4xAQ8q;*Y(Ju_`V;t_@I<} zZc~rvZg}gPCx>m_Ae=*p0Fr0%DQAwwWJ-)N4@5GVQdM0_Nfk{9!NQQzSUhLWlncEb z+2KOoE>?RxIwmVM!V-4iRvXQwOJYt{_zu_2gdhUBkSTn4wybhg4SbKgA^>5?vsPQB zHUj};E>z;F!^d|2=8t!N<}=?Io-SKD34oZWOuqE|wvqASwd>a$-#<`l6sCudg`7$5 zxLzF~+SSpg$KqZX5ZY7-4Q#KgrNeXFBmD=vyL*(lZtBT;ts;>epOBb)mtT5*qNlSw zbYlCCeF3FOBQY>j**{xU4jgOL*u@uY>{N_{!~G3qTG7mN&O7hvr=Pv$U2h-Sbtqpg zSuL@Nrn_O~nk3aK(@y$^t17z=wp@P6Q2%h*tyAC86=m$$Vc4_mwYR#-s%&r#R{Pwr)-nGA4JeX=t`+lhFdZXFY4TE!DC=@c8 zOe_`?Kx&E#0Ei?hqnfNB2&5nwxP;3RXW5>X!;k)H_B|g0lx%s&zrOI~_0!Kky3NXM zc+1E9k-DlXjMX8eXFPYb(3fMeo0u~IgutSW(*Lq)nnQnK2q7TI7f_)-50C&5X#h$A zIOUAthDzhAz=g^D$ccDY8hrClPkr#f@Ytb)KmNDBeRgZn$>q^faD zfU2s3%ftQs|MG>ex~6*Io_qVc`+Nv2;5P*xEtX?AdvtbCgrFmt&X>J`$s=1<^{wpa z9h;o?1I$Bap zRE;(pE~V7>14>mPL=Xgwv3NXw^ytvPfA^R1*39JI*+g6qI12<;gpl}@PL~T7b6VBe z!cQqtUDr({tzFxSS46HY2__AFo0 zbPCHeNW_q+6(rLWDI>IxS85GHLA74Rka&KmC_I_&D$h>A(2r>>p<5O*#s|kHiWAi1 z*mXB!_(G7D zWG?Vsh!j(JPOX3`ogSY4#NWR8{U80@;iCfsM|X8zaovCXXU`qK`NQ46{iC6ZMADLg z+LhVX?#|)Ck=E`mBa_sy_WghTTHHt9dwzFIUvJOazKu&)PF0-!+qUb=y9Aa{!P(9h zSu0F6iapiP_gKsN-tpOTfK)a-SZoG*3@J=|&!<0YE?x5R4}EI+r5D&$Kati+%!-vE zZq)YfdbOjY!xMq$Lq*knKk&RD+V3#N6h-mFAaW?a??-iELWnR7n?b%kmY%hzlD4Y1 zbX1GgDZA7wvt5^8{_cPI#-4}1`_et%TYJ;(TC%lSt!SDVGPWSUw8%{?Vy~RZ!Cfq> zat6A8s-etb{6tX#&X535Qi+A9F(DA;D2AYHatA}KDltQGs}8O2Ew?0Btb6;itFOEE z8=w8yeSf^?zkmJkxvQ5O^vLUn8&_^#*|B`{Xza3zmkE8rNCQb3Q-#Z6X-0nfdp~*d ze|`hcf8DujFR%~0K5Q6@#t~;Q&io)ogcO>~I93$|1rL27Bm{&Z$RQ>i5P$%X3<3fm zk$@opq{ISn1dxp6d!d9tN+Dq6Bp~Ko1^@s8LnI}t!&tVj zvn@6;dn`s<;#yj&l`w!1D~gISrBF%?0g#+PL@2}%8LFo1DpIkgX?u3>yX=ZO@MVkP;N_u611x{XkV!DP=4c3&W5Q zL@CA?H=E6_&b~Lk{~ZHEHKKyhXA)sR2@QC#sJq>n8=o`Rr7#RtLu1T_2w_TeU61TL zP>AXR>Bxo)CdQ_SVI7;Uh@i$}@&5j)E3Y}%Zq(8+uHbOo_Ld}5$Hz*nNH(0eVsIqb ze{e5EDkaF!RmSScRQHC<*1iAs^C|Ljnclqz#%6|}!hSeY%g>C>j!!zauVC1C@7u2{ zm&cxbc7I#SSh=)y@4@4t4G@HZTb?cFYsLD~?)KKMHp*nPR%vO~+A?jW{OsoSONv#v zYxnktANk?UH^2Y7tFOwnwm<#swtTU%VnzG4mtFK9U;9GbLV@d)o1i{DRtSItWOX=? zs=}7Q_I)Lnuo7CWd}8cGmV&sp-PRq?POD-aKhpJyU)4tmV?Lh*ZtF{4sW}! zJa}-|&%Sfnr@uyBQ;I5tIsiaQ6v>*Lx!YYl7E;lt1v01Yl}`CqA}J-|f&wHWH79`q zz*q_iq$efN5Rp<=CO2>AkPn zeAT*rxt?tsFZp2A?40%!t^+tO=$=-uG}c~p!{xW!kk7N)Pyy^zU?|FKU&y|WC8#p@I)78u*>nO3DTgo! zJqm$j&~-VbLkQ4*S2@_mV02k36 zSZq3rV-|qs3O4}&k_$nQ!Z@)Mtm@J(>#A03&g#_2>Ty3PV#-)kw#Tj6@{ECXo=A+3 zXIV*@B&bW8{`gK|GPLj*m>gd}6AzZ%IqsI&o}_P?by8+LlaHxw5Tg?TWsh zY)8GGU-R0_1fO-?iVZf89XamW&M&qe@3ySFe*VM3ir1QHA0It}3w^?S`9C_>OE?d9i>Z>llZt2Rtj<%d>+}zS?oS3K- zYWCAV`&lBFyzu;sMOe$UwyQIx>n^(X#Q4D_J>8j1=Hl~LA2~2`-~GSuTd_Q$s&3Pf z4w&&~kB^SOwQ^%4tn-P00Dskmm)N^@hcX{)jcr=FIU{(iW0?yZyzVJ+HEh7pcRv5^ zZ|uJ6Vgr%!$^-!FQBT}rYS4^H z$yHU4y4evz3`0u~U!Drhp7nP=yZ57C{%qIUHIF~G*-6IcV z)dp5_^(3$nuTPbZ92|GbHEaed(xBj!FaWU(F%5t!Fbg4uSUX#L#|3)DnQrwoWz5Cj zoaFp0Xo5ok0N_AIV!d;O#f7|57-%YKlxJ&8E+HFks1yefZ`Pa!Q$wmM6j1`WN?_0o zB}WaXRuR16d451joHd+ku~PMPeDKNNr{YGYCA)IT%7_2_^1uJ+cN0FFw9AJNO*)=? z;LwrBwmyCI=!gVbD3KGov1H74y++&8B5p zoQKI|QV6E7kTV$q0g;zXQ!{A-42bZk5|nw0;7{Hielut}=x#~5qzvYcXBh!W+ zCyE;JZlvT+K7?MzvP#>FqF7k}|Mx@{(d**EIkJkMv6j9Ts3sVL@1&39ne!`TloZ zK_n~{YOV_lr4vs5NS~ z>nL&ENSF`;ohot7WR=+_M9`Phg=%|8HXc(9!-)I9t`;y<3FTA}gh9|~Sh?(XzxT_^ zOo0FageO8aGgIqc(&e+JZdj5F45UU$xl|w+x^A=4Xk=Ql(X$J~5MyYX=E8K~PB-gFa#c zAq}3TG4ok$WAQr+jlkyKnQy>~07d|x6x}{0pECEm5I9-$Jhz|64pS5Ytz=X1v9bK* zr2FAdf8lq}zWkFHcOek&+BfjoFFpLj-|l^3&!g&*A8rhuTWRflZ}dwki74mFYZ{CDsAk2@bZQ7j+pKXLT1y zKOi~|MKSNOprPw=j6^v<%gmPF{CVs6L_=~#0uuNvvWAF6x8N-Pn)NI;1_^;msqg!y zX|}ev1p(8w7{Qn^k#1|9D%OW*EA?h@Xn0U!TxvRFlf_CH>M>KJS~lG}I#G~BZ|mwc z+gorTnCqu=EveSl<3mG~QY3}spw9{~IxocpZTQWQS4zd<_H+lOYT(uFS~Xvo9xvMK zSES#4(^`8he`u=NWQ~dGVyWCDI@aSbrXwgpqaJ!r_1txR81Q()N|{UUV}}yUmc&|e z&6x@^R8Co_%EoNTpPglzX6kw(VTG=@`P$cXwWW%ag|?P9uUX!A=*Y6=s{jPLPE|@n z&sPXa#0<_nO{I(lnqhdJrzk{G2qDC_?TB@)syafb<}^qsRE?IaB@F`%{M}DHCKU*T zK7EW`_U2c;$|-lh#mR0>cnctP+X{M2G-D{u<%n|7R>-c%p?`in$o)EGXS# zvs;*@5JChs#uSNMm6EusVjc`1tL)k{l}oR_cgNoE|N6IY`^bl$+O_AK|NZFSf9V&G z|M4ec`VnvEmzVg1DKpX3lWqVx4<%MO3IPU^b0h#{4ZB=M!6|Nh;NKld^rLP}{!n9u;V zuwiHrLRD20iR9GO)GIr7&K8P^R0}4Gbkgvbtx?8W_vZ6&v>WYF5;ey$zX-c3@V-O1r_)MvB$0HBq>oQ*`E1C`f zKo~#>qg2u2hx`9&I-GL=p{mkEGR;G#=vu>eh(bFvIqv&Tll!&MFi4|PgG@`AR=cIG z>uQl+t)nx`At;tBg^8KkY+<5SDc7nix_Y6@Sr|eEIiXJ!ddcNiRGQ72Q%a@V3gahS zJ3KntKRwuwsKSw=bDq@Hp>ccl`c?0^b(LSQ&d!!R9%3R?RfR&yB@#<}a~Gbs;+=1~ zDVtGJ*?1tJ&F10u%m$554n) zAAFbNRy@z??C32NW*nzcsm}iFM|bx3_kaCAzVwIRKM_yR<;$1na@l&lp35c;jQ{`u zf@-#<9J81VacJW0a^tz+ze~nY60aDumGY{V3TSi5{rBo=v38gw>jB zuh$!+lM|U_TsG<+fV!asxad^rt^eoi5^DRl-E-){`>iDwa2X0@2$+b75&}eJ>t|+y zz~ZQXk=e{sB~M+77F9N%T)2>g3#~lnL#&hB?FCgR4+R9MkhxM&K!`-XLSs4#MoZ&H zK6T-3Z@cXvxc*c>@2(jw{ z6;q~g#0cSqg1&sC6hZUQ6B1d($~6MrcWuS>DI$*R<3M-9DJz~z_bfZQV{4ZdbZ^@H z>7U+vVxn@>HRs%Z^Tpfu9zA@dFgjIiNZ-O5(89nA8bK{#S*>lEo}M1p@$GuUZZ=d^ zjhR;D&vv{Ja%kod{zixW(*9F z3j+m^@u{gF-}}&-HRtR*G?U3>!Z4T*QUvD$A+iX|KKnRJGDrf(IfQ^iV47yL+0<0E z(WuFwZW=8J$+)GXm=-n~YRq(8Z^^3On5oCH=2hx(tb`B;fzKd<)gX~dZr$~2_tMpk zZH^2DF?84WzVqt`-u~t*-i$^M{pIOPUVFpR_MWlP!>P86&#+S-Z8Yo(MgZ`E!CA!B zTdwT6|JhMZGcd-COTgS4ue+eDCyjJu(ySNMJb{9GHPdRkf^L28WdOjj!I4-TKl=Dq z&Cu4ZTeD~Hu8)57qs#hwo_h8#zxmD2RIL5MmzQ06Jrgw^G(EQef{QLpSf;AzfTPZ? z>@&Qiduu8lF!Ph7$Rl>3XbI zulsJ`yFogg1^`5Y^ATQw5Hd{@ASoncj;HDIWIEfdH(R?}s*|%jo_gVuE6=wF^x2W} zrEh#am^kt9PriEaf&bd_+Se(W1oDB)97WMMlSqaE#&ao-Gd;^0knL$TAE$f-$*4gP znX{Rb`v@qa4)PLm03ehLfD4oW0)ZrukU>I`L`*P@2@|fwNLP}&2WAe;dVplx)&Kg# zeV-mL4jw-E{m*>-vB#cWxw_;1Z+ipY_UGlBuHAgz`x?X=3KIa?AaqqM0Aqlto{$m= zDzTWyKF&Pvxdii~D_=}@lTjjBf`uLcCw<2pI&;#mEOt5o00e}ggiRj-g7r{_godTE z(RaR{>zUp2BrgvkFG!}kq@Hk^wa#2yL&^@LHxt+Qzx3?-l`9X8PXE*2f8D7DZJGEz zk3QYo-jg!)*S-ES6`+@P93HI{3$ulFYgaQCMxueUv$Iaq)Abm^(DxilOoh^PG8Kj) zV*zJEB1kDUP19~RqPEJ0VR)Wvv$}~>FYh@}tocL3BNPOtrY6#vj@D$c)+m*l6H{Y9 z`0XRt-*(IPS7yy*%=4T`#8k@YInIgtNO|^R*3-tq{im3?(i0HqX3S7EFYq&_hB0pK zXgzprWIR8+Z0VXtqfsc=Gf6BUgc3JgZUet9k)0eF*DZrGsY2CkPZh>SF$cR}d^Pl$ z9*;>NcqrRD5{HhDwbrKI{ed@jtX?y;XNRepKEh)Yhq$Zw1X3gBcvXpDOItgrRC{yv zn$G}`nu;&I;u13kD&?w1tQlvR#Nw68Y)@9>F#gKUGcA4=k9%vJ@`a6mC~r*s5c*a@c#81`>wj;$_F2L=EUgK;NUe07$2 zIm!K6Nr@%6g1Hw^1%(o=*|0I0%Z-i)(Fhp}mb?{r-WS&weXD`qN9;Q%ejGkX(S10cbQuRnb&~Q816@ zKnNq5$i3x(ve|UXVNgBs{BwJseg5fZw}yd#=RJ>n;oCpz>}*}Jx>Ll0 zmRPP>tM55>@Mr(?r(fOsXMp6U-tJT?MODRhz2nCRip7%eyMzLWMLcfA+oN3lk!RI+V5XT+tX)A{_71A8o;Yq}EC=<=*dLPi0_BK*)l zf9W;vxaHNC3PzgxTo9s2DOE+m2=P!TgmTX3z44i~g^TXkT!r_X-3Sp?3CPyYRzeW~ zpjNAyR*d_oRBC1tZR2Az+!jPACehoHmI4CHR5S$}K(<;jO~D=yW~#NBiM;2B$z0av zj7z}7AaFfH)xy9_!Qkq*T)OJg>)diF8MBH`p)fHCc@pbHkLO}S%e1FgUvxocdDg_N z%Tl}AQ>)kHwrpHN0XlJ_JU;CV4@^3)KR!NgsHCrN=}gUg;xEraDWsH$U_U_5JhyGr zmJM+;IW?2FZF~Q|!+kxSEol=2nTT7OqHo@O4ursNRCE(>IqzJ6;MczPlN+wPePUv4 z@4h{kT(&7`X)9JNbA68sQLoz=;N?qKfDnM5j+o>quJyF%CTB`O(SW66s%T>)+OpN4 zFr!gI0f9)MfL9w$s;H}0tr1N2cD1*)XFM;+q+8P|lLsF2gGSw^lp=zql+C7{PQ?L0 zwrxk8eN|O$yQXOxrIc#A4i#xAF->35)3ajTs$4SJ)!VgkiTTKnew6Hix|VWEQz6K_ z?q7ZpPqz0zdH3Me3jjb=YOttJP()~-Z-*L* z*W>x#iVJQ@GLI023xJ^tLqP$D0szDT6qvyvR3u>nLp7%Oe&t9%jK!_L`Radu^jqKP z-*e!zZ@uNY!Py5M-TmRec|&*Z*_FM^uesr!Bdn)vw)=kI+ciU>i)khZAqW8kC{o9t zXZtPM(TTIQZawuDz{T2mQ3QlwlsKO|06+mGkVrr*5f1>T5DO^?qJlHgs4@pw?Y*(3 zE6l$2<$)t+CSH)d^@`W5*tj_$iMG|<%Z?m*_UbG06EoTNbi&Ane$$GZ2I;EQ$^iz4 zho|%PdN!_1j88QhPRvNCy5jkcswxNx0ie zfv)QT)^Nx}m+P7phBRT2m_{p3qwX_c^K}#yy9Q)@&C5b=$UAZo2t8m8MUO4!W)zGpuHDGLuUrO~DvD zblmCc$&3t6p0jC-7lxX~a=F~>WbMyeAA8%|Z##NqX!Dk}p&NLfSFYA`ZHZE~A^}t} z0RiK>Ix{}lcH#Q`_#|ja!^&*Z(wy$K^ywYuHJ672M+{GRK`5c;Gl7xfG})Wpc=MBg zc{~w|70VR>y_l)CBvYO3?M*kBnw}jW9}hVLP=ujBH8a`P)>f?M(&wk9lFtl7PNT^!R-s?~Z*%~G7y$q{i1teWLI?$d5C|b;q&NwH z6j2p`L=p)ogkV5Q5e3u;&hMd6h#`Ui2`qsKg@8ykG=N=s{7?ZZIM=oA_xr}~_|AU} z?|b#Z$DbD<=vp%VuCd~|YmHpb7Pa?{<7#U&0Em|rz<@A(DKQZmQbNBDpdkU1m;ivN z06+>xBxB6LRB{L);6S2zdYgcPBR~X-)>w(4gmbmcQ3Ybe@fY(qOF%H+PKiSau>=wb z1QD21kbqdqpl*i^9B*5m>{*Qv!W33#1FWLBf=9Lwuef;a#jk(uGrzeDXr>B7i9y&1 z3_@Cx37`-uMZ%;IN(iB@;5lh)p(sQ_2uU6Q#u1lTQQW}ClvHXJO;a7u4T6BGN~usx zr_+k2CetY?B@28Gf_l@_6%g`JjVIe$k~4*ZiPSMnzw@mhj!z00s0vj>)RaiaVs7In zApk(ktM!oRd=@MtG6>1VeAW%fMX(wKFyu@jK!(K1^ptB=GnK7-LHDv02;Fii#4)Wq z5s!IJtj!=ShJ=C5n962DX|PHmn~050Rnn>Cp@T2$9ZLsCr^+?Y69E8>2x=GxhKNaa z_{7*%Z~MacK6cf$H@~iIS3-zTz+69{=v>{*kBlAPEyEz$+DB)m%|!Mc?|Z}GmwzXn z`rBW(W~S)dfo#`fB(6am4@3ON2cF)wx4*lyJ)4eYEp^3>7e4>W;lT+TNiaS-8X%&m zdZWqA7UPM>2IX)PisqZQEVCfO0d^bMp#T$gX120=MbEyyho%Z8D6B*KcGeq{*Is|+ z;T;D9-xi?`kis!|b=ScUzYn)|_tAW*S+VQKPjp^l!_?V`w$Ysp6y`3wTcLpIoaQJv2WHMjuIoi3skC5}V!Hc*qfP+)B(Te$H3GNP_z|-b9xAKE9;!N-r^jPT)6U+*`hOt zlqaPVPGaB)LVzR}Zqu>LwQ8|c%~t|e%g>CDkB>SYmrZcZEpJ2+Ste<^4wrDYP?(-A z*sf<**M z+FW=dPrcYhy&6u}B8;LH#<-$rmSsilq6txKzo`&4u>EGG#yIfG0x7Dfjcd)RRHiqf zstLfCa%gP))m?`l{_~#i z|NQq)J^xA~t%s~3dC;iUg%fUC(|h&h=Oz;I7hZZvS5((=0@n|G`Nc1M?svcb%^&Z+ z|6jg)$8&#qDim@ZV!!?P-(P>(<;%v-G7~e04kC`tB`IfW#%Np7OyPLN@p_k{N)4}B9ZsAOqX!GKGgC9uvt>`w z)LNzb{ty2x=Uj3?R0U&vd~o2<;ln4!Ck!)w)6KW!+S*H{QsA?wg?1Q*j^l)3==*-L zSZp?%jIpK5R#}=kH8B;>v|!ETp>$n`B&hIMy*k<2xADkJkNort-)Ud1gS=z)cE0yJ z_ca^d;O-}xu>c_e=W7k*DOm;#I1iyDD%HXe^^eR3l6Q11`Tnc>wmtH& zJw0;wcfRw)z}O$!Qtv!Ga_iMATjI~)m2a!Zvb7KaR0EJB?lP$A5DLJ!f?Oa4M;fsN z#0>ii;B(E!;K`nBK!Syqv-4-T7yw@EU*UX%q0`eJbLnac5P*2zJpnGHL`XnN)nrC* z3P4k8al+kn(X~*sc)(Sa`o5b?CS}N)jz<*CLl!zksLZMDs;X)w6Ta^ohM}se5mSLg zj^l(O({U)+A+=D+=i z>)&|ebFYlXtW?N71T~B~6A(Zk;ptn#GZQ_Fptv*SP63b*V1$Gaih?VZij=ZmZ)l3D zDQev=L)p+(sUnpkzG?N68f6lpveQ6iXG` zwv$QA3w)EtD5OWW@10nenpoRr7mp%6YsHg;H8IRlbx^Bi1rALk) z8#ytS%yeOl+uPfH-*;TMwY7C@Y)ne&I1T_n2$9KTR;^l9sg;wd80X5w*r;hHArCyS zNigi_=_LU=F+HBiv>$r#cYj{FFs_*?HC~-4jUBQtIRCY8`tT=y^5mU9JBr_;lu-lQO~-B3j|e`yhpsOaNDart908*5qY3zjk_JGM!48nu?*M6vtxv zoNTp1g%F%`MNtq!P|9R7k;$Y@Lw8)aT&+}URo8V1A+4>gs;UM-FgZCHy<^+<^z^Ln zhe5yyQ8*7VLWn9*A`)v-C60Z?Bm85Z`}ae~>ZWN*&Vf|83?PzFA_-5sn$u|rr(FK2 zCbIZ|Fyxe~ECg}OYC4`_7?Bi%ta44!3D#qVrRgLTemaq^GR3auX}~KU8Xg>gZu3b0 z(9BrA6xab1j_*|}l}e>jtyYssE24onrBIN6=U3nKsjqyfT{m%})h^byKl+&KO3yc} z7E?1s>++@9?qpYM_r~)tNOtyeuBQ4nzT<-zyycP&9SEPlHh0~no7!?ogb?!?^97f* z=>#tKbrbirwYl|*7E9iI%dKG;_VjjAEEB1OVl>(n2)yzipBvJ!TO2+xT4{Xk_jlg) zz3*(WQg6TNikq&ze9zt^s%BK|d`ml>oSZb1maeIUpy}e2l`;lT3=9k$%XW280YbrB z+j0g)98f@YKw;_lFw_vJ&MN7pyw)TV_qMkIw;lumMRfSY$UENgF6IY_U??P)5@Bj4 z)0LY2_!C>}^*Uor2q7h8j71RyA;d6@&d$!3mdy0@WZ*lJ0NZXR^w^qJtCy`?*V57D zl)TCO#7tv4^lNpu+LqIIZhhR$!sVUWcf9`_8{YKp-W96~yPtdImtV;&Q;6TB5)z_t zE+eotN)gC;hW=j}V(|;Yh>s%X&=LWmfOC^DQZr#d%6UG>!p6%ELIp?qLxIe^_?z2|`k2J%09ZtqO_#NB_|@wxx_ z&2NAEUf_A2X~wg;7A}BoH%p~TK3`N-l`+<6G#tlM zRW*{W=A3Iv%+ORtCk!)1wKRo}%=G`@zDG?fU8}oLLW-b5D1I6Gw>JPj0*Y5@#)#7>$K^7SuS2I=9jjm;<{0-+B`%+qQG->jbOqsadbrm z+FSU_q&tXa=IQW6AG+b3e#g&DnyXSeZ9@f=J1AT`yPb-MzgVHf$)D3WN|%(;&n`h(tQA>oMQ= zS>OWz{4h{;EeHZ#*W>YcA`#a##kOq#fT>#5s^|X~o8K5!SPb zL^9D9Q+2ClX=5fob>Qf858wCFum9zDKl$lRpZ>9c-q4?aJ$_;&)1I>(8Fk2v9EuQP z!E(-UC1)Qe0bK~eLc}YiiI@-7xDRyD49+;ov%3#CPrzL`_{=#5&zOD?!Y~Xao4Zmj z!~#qOL4(17g#m;TVo@(uR&H4J;eY;Q|G;o6mC{s|hYSIzA_9a!1R)?X1TkGtS{5N> zYGy_V;dvf}D49$FiSo0>N~Mx76zqnhDte>gFeW0W7sW~lAx+bylmLb>)CJ|bK?Vm; zeC@}-z3~k<5A@H(<5a~l&SVhO5mFFB@Kp7KlUFK1lv|%)Vfg&oAWnUq9Ff)}2huc6 z$7u+_g%A+Q(aJ2NL8CT>xKF87v>U)Fb!DuPshO$|S`!J6`%~qbs-;UJ$$_sJX1-Kw zG+h9IuIu%B-83zP5Wr5)vgE$;f=JNGfx-NV@pMmevQm8b_b+h=Ocz~NuN`{jv0|;< zxxC}hju$o4Y>DMG5OyxVw)OH0&Og6*dBfABbJ4jQG((YC>IS7q06)-G19-6EHkK`E zz44~&e)OXs6=n)gKJ|QW&oZy!FE!z@6LsG+x0rEVQ;7^8Qz!OS*N#E^S)X9mi;FW+IzZ(@;G) zF<6)w{@hz{KXK%x%Wiwi!RH@2wC7N!_v(Pvw*U9%LvGWoY&4rxQ6g`EF`i3zoW*!f z+QykXpoMo36cR}UC4v%238S=;Je%kSICV9H5YG9L1#hzG4=i{VE*XG=5(I?A5CH(V zlnh8t384}TqRBYb6d(FyL$if4y8Z9}^^q5L-0}P~iSCXc{NcGb-2N}m?6)6%{QlB& z|GYB)hd%GPrp9cP2!lWbRT;woMhPr%8m0r!$dD~Mc?ltqi~D>4iB972G?)DG8Yx|A~%Z!Bq@Od2|Ochmv07y*&cIFBLC0LN#KxeXP|B4OG^*Mwx za;%D?sH!TtAV5!Mf%7C@?bocM?Z*R$6f9)H&!)xWq z$A0&#$$`C{9jyeiR4QY3v@Tnl<}(#uoyjB(z`2m2u3{ub7ytka*JDe2yAJK!+qb0W z!t>9Yo}FoJNsSH-Ocsy-?Q6HJw&D=8Ovbl`rzUY0;|{7DzNDB;6lYaxZdl%V!&O_> ztXg@|>Ylaj$t5lNs;s%OJ-wT-r#W~hLxa;`w z(r7`&BA?H{wDm7;y?Nzj*H26iy#D`uoy1lKGcRoY`4_rd5_8}Nj-Xkwh;x9z*_d*R zoyaL)1ArhTP$CEb0)R;gBjFMW(L9EGD%6J}P!=xYIGmiTP%g%K0hpt@LO}q4f&+{( z=L`a%A|M3Al6zQqh{hNfGLSm9B$Cbg^bs}=X-C_#yI$P8ZR>*v_V4)39e?13*@xB+ zzwg#Yg8u2et8VRSyD~4^N;2W`s)9lg6%0wZaQ$bxMF{73FN<57NO&@Jb=no3w0ndk zXLGgCLP7|!lu`f&0fZ1?DMdg8!)nZ}`GqF<@vnb%-9?w^S}d7NO2NE_lgVakZiD*_ zAkIP#FtQs?z&M0ZZ~!FdoGXe70N|YKx*o~%M6;I=LRAF-5OshJfi^)MJvFk+u(;17DkWz9H zQc9TM(V0Z-pEzQ?3G2*Wy*$>@X7Wa}76R9C1 z$XcZiB=B7?nlN_5sT6AGu32{dxyzq<^0C$x?Uf2U{%XUn$xr$Cgztz5Hy`<_=5*(C6}uIrUbMb;Yu5WoJ;&o+Jb69#LbM9i6-29k3w zT_gkyX(MFaOM`7Ylv^*=0BBB4(O5FX*x`%-*lf60Uw!R`=bih)-o5Q@aU@x_T8&#t z&bjaV7(gRtGKo0nmSu4%EWV1B4_*eC|d`2mq3ZM0tA{!ASrB zfFM9nil`+m5JCcsB|;2H1_g-t#{vMU27tgI&@^RoEZ?#5;_v?USHOwJH zaH{oMxyh$0&P?4dlx!Iw5e5izC=o$85w~QdZ-Suj`#}&;OfW^dY8ZwAB!Ut#7KEIc zMw~%n#K@jKJ9@Wl`PJiF4-C|}kU9ZELLmT#NHPea6d;5^S!4u4N-iarP|Vd+%|DgX zJP1SxoUsG}05B0)08GThv4bkcekg&IJ`Yq1mn}&w?aoRy9bn|L29+kwDvF|L8q@Wd zZ8tP*fWWhim?u~xbOIJMns(Io$n~8t^f?PQoVVedU;OZz?nLkU)zf)?_x9o6Z+&UQ zIo(so2Zl}zY`Wmm!tCJS_CF5|>}v8LX|?Uz^O8SP=ApCq;Hy)`iJ{{oygHNXZt2RU zm*tX0QC6t}A$55O0#<7TjeyVAoRwM6N~RPIX#_Ymzfg7h(k+t5Y`;lUG`qYr)!OO? zK#u`kfi<7K=AxyfUbSM%pB{f|==j+FJ-gL7jw4_S$%&|hk^sj*IKMF_fyZ&`u~%Mc zT50K1Gnoz??n8kQgjP000hyTgIyxtJ{#i)fRtW^89&kW~1rG`}~uIj3mO=l2*5ep%5I=&YIzz~3#l0f>UdO7g;@K%)~K{`-r^y+%s3Sb)9$BH7z}t`^BNHKm16sR!YU< zP>L|*K!pTA5W;|P1m-3$002TfA5br(deA~R6IqJ@QUDpjGw8JCT}mJpF)yPJ%;#eT z05S?(=DV~axCm;ARFM{$>;IOZI%7Z&xflW!s45_}+3NIIxvjPJr+439r6saLs#q)^73K-XW9~UQ>ZwHbPyaO`2fHF?=&*5F<}u<^iHh zEo5SzraV`x7?mwVUEZS;h4aM@CokY67!1H74}IF+5n0nGPQ408iDQ*x-*Y^-YoN+}_B$AP+z2~?{haw;vk{kyZ z7(Ddr!p)e*?C|8K^Depf=fDs|XtnAi`?#b(@q&dy@1)7M5QW0_0TG1|#9-kN*KHA!nb|&&b0kyGd2tm>? zXRU;PXn-uaJiT!(E=*@SmKTogq=xDHfnYvD1OZ&DJ8Rah27+OXr4*Pbj^n7hW?9Kf zsT^Ubo)?5+7(G_UaWqW>P`IuWhG9CLC8S=hR$bSPYRVKvx!{8H&p-E^!NKEu_wJva z9?!LRbmTf(GC3e5!vbyGJh*@RyKcDkC-*(|y?0zOF}Ul<1ON4!Pk+6#*VE$xU@C;6 z;I4)e5<1{yg`HS1uz9l)i?;}ou>b(jN#G7d&D-Y^L#J8Qf|)K>PFk2$r|cjkiZmCb zfKdMbB&ecafudKG(}EZX03jl#)l#ko2ae4Y2EvQ3fBXGA2j2bBkM=I z7k_r={s-<5$G)5oXe$JPfD#}WIg|NS zdk)HtbXZQiu7z#EDT3ptW{w~L7-K`n{euP9O}*_aKmPG6FTUf-3s$f?HC1j}R1lpQ zVaN%CzVA9PWKw8`E`)#(Mtr9T!f+giP-&o~rbA97;3}cQ=d3-`a{S0)U)6qm-y`KRrKPkXp$KFZUcGR73E3iH9t=qMq?ZP*B*WFjt@k;t~C za_Np#GOqf%8A8ZYF${(DQJ7kh<7v#XtQPBjJ#1E8FJJ&5E(Jn_1)&);B7IhY{Fss6 zv|;6%9?_lhyV_Fu*%DR~R(Bil-1VEz>+0xy;m=P$eCO|0Z(g(cvWwC^>7^^y>w2tS zD9nt{5Sm!N@xsmxORu@^ys^Py3KT?u7Y1P<38n~wrXyM??`olIj6#0mgzGz*o+U<_ z0!afM)`0rLOJllL?@lYFkt!ff!a}7dy?ceRy>k178;3&Ws zUU$7WJNT!&?)}i$epRvE6R-SfdgtCmPmK8pV_yJFA!4k0nr%eL%e-|hp2(*R0wG*n z@4I*@UOasOnCtY60R&PCIM>n`ELz7;gEJQYwuJ*u&cm|=F$e$vLJ0w8KtM!<1V8{q zQ2_!(ac0f4ujB=O23L`5Uiaw=}owmTR}IU$?Om-!ziw zmA2uBK|n$U3l0$vB#|f}j6why0a5}Oo%}+80>S@#(LR+?22Orr@dZMl#sRt*BGm2txTP}7)r!}#Q*Q8P@Yly^Zz*@gud@< zy3(lIGn3=G5%am!bX^3#9%Df98&#p9QhQ4rgwPk#ij$dAv%O=PU3SMuCpuPT0}98B zB|zila={N604Q2}ecy)=OCfH(V(n`#?yXK$D?Vtt&eE=qnel=Wqg0S&tAP zi*LN?E$?)hm_D+%E!TT&@L(dHG1T1n)QHV$s@j4OT7U7{u7?jCE7eApPP>R)B zOrr|0Km7J;2=9rT!c>1N_bS@SoiX>$0!YvQXko#R${+zaU6~Ov*Jo6v8P!Y%jmExx`71v0r7!>K z$=573W(E(rzHjLXRU*cHfI=iaLP*j|03b9?^Zk$zq9_W+SXGeCFrf%|4Hm+rl{#|x z(9Q3B_k*vza%`kpnY9(oaD9hJMx}(9LIePmXZrEeF6ykjf&b@)0{|!{L?Pv3sWLM| zHI?&F*L4a|GMN@mxTK>im8A{G_Ua*&;oxXvdYu2|>Aep;_Cm1&o_}&LtAUQzY+I+{ z2dvp_5<-Mv7~?SHgpjt@wyUqWB* z)+h}eK753;Kuz~fPme-wmS*y!BZn+QPsS()HMhdXi`8@2cKg0>JB<5YrB>AqHSk$q zThAxH^W2AiyzTY>{$Sa`p1Cw`1XJL_~*a*ouB18TfAbZcv3T(f#TMD-))2jG;$rri_bnmp$wUuUb2*V zK+Sb^bghH&6i&xv)7HCtj9j|7{}5P`fdV^C+YcCkICL9Qgr#cr!p-MzTC-+0KP|b4 zlDqMEETU7U)9FadLP}{E24jp;noK4cV^P=Ih&idMYQ0`nRaFRqDYBhrERo9Rt3Ukl z9rr)@2vwE8|HQ{GzwB!1G_ADh6lV&xN;;c4zJJH#k3GBPhA)Qk`18O2a%&&tBBacP zEYt%Cz{1w!WVE};NKOw`;CyB8Su)RXzFxG#zP-a=}GPdJ`@;3LIq|70?91Nw0Nkc*yLa#KdY@?ec3zy?&IHm z=+Bp3bbh1J_~9L|{NS-;|MjzPO+NM2_G3TmY3{&k0OPqjr%eecle%g~XNv)ZbI#=~ zo&}uZA5YDxFM?X;?hp`i(Y|GBoblht168Og`7r{sxd^M zsV;OZi2w*likX;i8GHK4pFH!^kf1ZJ`0QihS9zYN8+3Mhf=Cw8XQ--i76#>NE~k0{ zBgBfQ(%J5oL{@E0#XHRA8(*{fs*79G8M|$%zIR~n_{_}k@I;j1Vl2QI`@V+|V2l+? z<$w9gub38Od$I=(j}MQHwdNE-^gI9X;_{7~m4>5AUiJ$l)tPN;1uzZ*Z^OBluG_R_ z<+2M>@vNyMh3GLomu|^oWVCjgmM$9=Pg0$zkU%s#Q~AyxpWS`zSa*x@k=r)!+;(tk zvf1>Su3~zSJ-fGD#R(t5R6^jI4;xMZ><~(d(O99mn-VIN0d)2{JO9QVy zyKQCPvZiDBp-Et%0O0vj(&W>}Mh0I#=3Av1MN!Dq)D(m; zl3W1-vVb>jC-OeJt^y#MO`B3$C=|jloSYh_DxS$t7fLm%S-bZf{O#TM_YaI+e(Cv_ zo`3%Ii4ktlX45g^#zp5|Y{2k)|NU?0U-_=7Lp%08^l)o$r^6Ujbf&-sm68Z7ICG&w z2qAR3Mg1?>$Jxh%#B`+Fcp7g7MvXP#e0OySB?J+iAt74$(f<;}@Q@KgAV!P>fgnIQ zQW%G!FOePq0RatdsZz;7b*4}`_(CJuzWy7Jy!6y)@k8JE!p>**-+AZrpZ)$lzxl(X z2cP+E=kz^2g~wMV17fN*4m{5b0bpPbJem`oJe%g`sb_c^JOROcVfe|;tqXn{p@26i z^cWXF5)}!mi287OqNsiNhd;k(&#q7Y!{2UPYUyH%7bnZZ#YVNl13xN%fEZbpC8Y#H zx`D3=*_Q1pxut^xC;t1+`#=2IkH5Ne9CD;63V@7{*@vbOMA6vr5S0QGB@BHggs!M9={PZjmD95+t6rII_;$YNx^~dDYTZ~N5VUP+ z&)T&;oo~DOdP;D`0hCfr(_*n$DwWD+Gl@jPvMk2fYhJrq(PEQj+vmJoWu=^7#Bm03wKhuxhGE$7!^_TDiKM6n zqS0s^J27)$aLkH}>OIBq=KW3B4-_8c zc{3K{oI8#)Gc)7+zNQ(CMkABSKq%|=dNz}Z#bUKujSv!q9v2}dP)Zqu^*=K~%BA?5>= zx${3Y9Fq%tj=zFWFGOVXVcMeZe*$PAA)ty?#SbLI3Q!TyROs2qcGW<7<9q-62S0iF z$SNtqo~8Nf?zBb8y`4o#k2Rk{M^;=dDF`~ zCM+XEDD?mXKvIOb5DGvv5FrH%P;!(4Tzt2qi2Afm&|h;OXFQ;}5e7b?P*D`l0x4w# zEmKO()>KPkwO#NEg%Uw=D%34GCW$XR2`%sBb8Ikj1f;BJT%;qOgrU@wWd8p*h;kp^~UVT(9rPy z-Mju$otX^lrQ+m~+3`srM9Cg()@KP#w8mqJlvZmr6s%8{eBDwFr2Bp-Bz*qa9p|(; zJ>AP&lR4@HX)Czx>J`gdw9V_6=(1{PSeik`GE*vGmD;kE>vY_loEVV^);z8MaOKql z`?Ec%hDVxB90*zS97j4Kz@Fz|1wHV}Gwqt8CzCJ?ne7Pgwoj$t5A;wPJl~Umrp!41KC#E=4Mp5(1QqCDSze`g%F%nyS`n zwW!n(0t_K26!P_Yy-+AbmQ%0SgCI~z+-Z0KKmd8nRLhlowO-L<+T+i@c;x7TWnIf} zyy~K`GTLk^ux&}RmRDok(8OP!d1cM|i?=`Y>(*8sAp{(J+DZfGHzIRE|KcA(xWFbs zRARvi7Xl#&0Tn+QGw?Z5r-z9&BOy`K?1_U&Ih`1PMZ|K!fn-M{*7eB|dBOy7H6 zOy!BVj|~w}i6DSM2oMkfz#$GX6%Y~0k%;`VfEdiFM!-mafrGiOLvzc&gg`<#KaHbj zz!8%G3Lz+kk~0|sC^$xn9)ottKEAIwHtYhUW9@ZsyZsA4{nm4@JicScwHII5zh^Hd zMm*UdJuw`)F(1V+Z!OFF*hAj>)8+@&p4?Vt}LsTu4eOLqHHj!#Rdgv;&VK z*T}ykgrb@zu<-6YGv%+1r~oPgk`}@ojuaY`V|%wFH7=NRVVH}zBy)*sL%OvhHhI9E zn$sv2YQRV7q&3<a<(R|fUEV|#6CDn@LZ+bJbx;5bGg4l$K7 zX~yzyt$buqrN-3cOnthbnE-@1tQSY7M}ziktnT`W40E{-g@Osisw!M1<_pJfvr=gl zHGR9oNH>_UjBFreTu}^36^eCB$xlsZ!3?&B=Q3cLP8X~d0kFUG@-6*|ooM!3`#X?X*A*Cvq;5q{m<3P~>7XV5Q z2xp99E(H`)0vI&_fpck~v({prJ|KiZN(iBl3fok=$<9n>cM+`HFMihV|3=Az~Pb zjajb5F%7g>tU+bdn`9-=GPD3G*+jPhwnP#LH3*x!$pvG6v)E*^ueYtcySwz#ah3>q zC_=yq#18c>ssThxj39uU^{HA7Uw85O58n5D5$sAp21(pjKySimw^}JH6vsfpspj*1 z`NoSjZb=}(>VfjqGh3JCRE`=*M}~{B3G2nM0}^1kfGK{UPv#xpNk3CcM7wOro-*&x8DA?dtd&GZP$c~ z0xBI408A`1c4+V6^=mp-Eb9X570+zj?FAxmeT=c|a6**!_JkQXn$3oyDcNjGtx~rv zOV@RTK(pCQCX+E;i;AIjRd>VS)jhkszT7!?pPAu$VZz@@~h5~xA|fe?fQK?r~p5lSEB#ZEiJlN6V; zZ8TscxF&hvcR;`dEd35N&#?_h2ZoJ^~>tDBN^>R=tq$~`eW)gDV zIcsd2`a)>bWSpoP*tpDsNudD5klT@ws#pE}U5^QjTQ{vb_{e>dQ$J{`L``*e$J3cg zrBta@%v=irFkm6TBAdvlipha=eXmxtXGczSXR`0O{jHPZBY^RS?-pvMFyMj9Oe^-L zH@#_Mbg(m*Rw!hQWm>YSR4Q)ieSN*@bhcJ)Vx&=`6^liH1g1#QXb=Qe%%m7eDWjIV zdOQ}7$Cvc<0FVGD=j6$yRU&xm5-m<Tsh_FGDIjwam zoSvO&Yw0Z4!9@Q=OjWoN;}js~C{l+|1mg54DZ*d55DOsJUyDZ;pV*lm=QM8~m9atq zIOkGIRne6Md+)hxH639^_nqdll`C$#^!zNy<7BHwh*Ow$dDz>!w0~%nkf2cWLjwEz zC(Lx)czFtAYF8Q#aI3RHwN}O$gJ=`S0zwF5OhVAnnKMnZ6}YQbE^jgh-Rj|i*(I??e5S*(JfTyZ4szOi5exO&6|T zwsdK0ZdLc1?gt-y@bSl=%`IO!F*DQK(=O`O^4^^Yk{QP{OxURiLJ-u_fKW_Hpb)#} z9ojoNa&%WSm;B1L*MH&3t*+OUfub6Ey*j$@k;mZX=tz1n!ax8363tJj1rT`7vxvEU%qfO@8oIf# zs0tt#gxe$+i(SluBS8p4dD74qeif4v%TwltoS#%eNC*gm2qOp}=RD+sNvMV>REQF) zpgfW@=(Ppmr^cl#?Tpl1kRt#|Gv2p0thZ6)fS0Pl87*Lgir#e5GyD(oXH(1W}5lQ zS&Vcu75Cgwr2!NG3PWNZFbUz=$%_B~@fg*T{A-?)tvV zxokGeRO4#HJwD#wowZuxiIKr0dq-wgFX@=FS)n)+P&|6#xT&D=siJCRu*47p*L6+P zfDjM_5(!IJR6>XkMKYFzym{kImu`FcaBJVvB|T{m#AZW28CF|?>pIl{08Nd1v$GDU z<+N2ZmC@nhUFnt{RY_n)0w8A_)4&&TOTXlzbD!BcG|}Xa-4KLGKh#2q35C$v(xV$1&3&yeKDxELmn65Y-gVEtipvBg&-~!qZclD$ul4kwo(S#o#IWBCJxf>4U6npG{Nnm0wjnvJ_rTC2uf9mwp6lxP4D>Q+gdI@_lm2p z_|8v%zJB8tuTcUhG_CmGzwa$O_aEDPY(N0kmh0$S(mgsl5&FKW#Vt#XTd_)|Ocewn z(lyg{eLbdxKw^kuvDn-a0->twxL&W-YN3#^SWH*cxDgWqxS`u<`Y}^etac8az^x&b z_GFefD`VgOhqu0S_}^~-_y_xU%ut;G-V~6kC=k$)z=tS^7BT=y#MeiF0|3ab9e1ys ze6nTbZEo9DWtjjd!Q=$xuEYj_0G`A{BE0smCJ`2@flrxN2qLjiJa7tsciyLRiU7K_W5E}fW}^_hYAdOh8~ZZDJx-U zdb#cmoj82{`W1pE92}mQ7(R5MymIZb=bszA_1bj`2|QLw8Qn9*Nd>8(8I+XiOjmEM zR;$-4agB$etH-TOq8q~?UzqYLWh!~SUTw=}hNossO+^G(e6p@nS-Cp?=n-=Mx^~x@ zZmwK+UiZ@1ZWwvX7q{Q^hRbxQ-uL)3)v~>O)rHW|-B9e?RJl};QW~)= z51A2JVC%g)y#FDlMs0)pj!(!W~qzp_IkE!h5?r zx|jAo_}wq-Ex8-s{`!F(vs#=34hcsB1rh=dsQ>^-goMPBBgt%RV#&z={Bdyg((0zw zcieczv+FuHjvH5vsy#snXjm6gLJ8o>Wlf6HWE|(e5Q) zhb0I-VM5JM1l97fd=OG{!Rz04$t|}j2>$3RpZWRM|Lfkz)vXuwowu&z;`4{kJ@-7C z-Z&OtHOzIxt49tI&7B~C5F!i(m-Be^qJ7*MlNQdG1xUaUifG3WA_(TV&w#;zA_xTu zIfDv;K;gm@0t>_yDUpnM+!sVpY6M{uiFrQW87_Ds@PPB*&Yjg6#iA!q>`ZuQvD07l zHRl{)ZV+W=X1a4pTvJus_H`X+v)Ou~Rx3A~n(H-{dbtUjr6N*B2Zkg}4wqQWVj9Hl zeM@b}#ux^IdbXWPr6MLVm!Mp)m8+Fzv$?cm#pM^D6Qkx&e{uIMZ@gw|c-#rFhGeBw z-1+p07EKardZBGWR0$j&guL0DD3)8gRyKVSkkHVx80mgcZ#JE5Tc<5&yEz>644vk#(k?UU0kG}cVD_+=sEVSjOjtp4YN8fpq@xse^>G_E^bu#a_UvP~#HDSnU zE3ZsF{POM>zBcSkUig6z>Fu2zM-LAl?C)OE+blGD+PbFW79?c^A&uz@)>O?Bka=D` z2#{SV8^wy5Ye~hF;rvYR+U4(j*Zc4M#ZQvSwY8de;|(inwlg_XQ8cYBmmVJf+HkUi+(hH95n@pw? zp@$&g0B|IP0E9u}6E#4HAz(@X1%qGt?pMC?sjp(izwX+LFJIGj$z>~Oa$QTymVR@Y zU|jN0a9fXdE!W+_J!yZWuj%cwk^~ zVDC#?e{;v!!NXXMU2)OMo3HFOlifY*-#%@vu|2QpdjJ3>!fmCH^O`h^y~t@%!@{LS z{|UelB7)Cl(*Tr$O9(N7l1I`i7>7KBSU^C5KnRMENel^=0xHmx0hWYH9EuRlXNVW} zn2QnD#ee;@gwEod5B^GV5QK2?os57R$8lC|X#dIAzy8S2{P{+5*1oyiK8X14<~3a(c;8jWCudBJ7OM^H`^bpf zO+S&!CKIhhV%Munl`#ZOEjv?~>ar4ld13G;k3O@0$y_thm>n9r>iTN~yV%*%ePm=ho>2lX+;Z+kyAJQ^&ZXnN z;nc=^Nn(F_pXP=iz3R={b=Sm~p2Hzy`NG(v504((GqYz$y;kWwZ{v8Wxcr)H)+}9J z#2G#_ZX|MDm#lM6OvKyTiup=$wv&WnNLp89j1BrleTQljmyyYzor+MFf z_f@Oa*4B0ealKxL5GuOH7>gL#ilW40X4D!H2<~}~N)-SQAtdC?FpMNoTqYp&6x9q6 z<|62!sXa4;zRwWU;+9$+D}V7k6I3}N znt%Z(SPBkBv>O9*t{ssC2rkqyEILC7p_o(hhEaQ606^p-&vI3#UfOBnui+z$-#HUL zUwlk-TZHrpGyoPP6hf-LA^~q8skHZ*|M~v+?%VM^F%5&pQoO!$*>XybbqV+{aiTd|v^Ep0!2>MocSiW#TEI zphO}>IH)-s0!;KmWu`GB9@=?gyf|CS7pLkh0h|-}yz{H~4!9ywOZ6JNx9=MmUVhsR z2{YZD%r`KJ>&;L9<;k98G8iws`J=aPIOn{M zjhA3-27C8RJ^xf=e9*1gekz^1C5-O^ltIoBX z^>Q=p$;C%!rrTQkv{X((AfQke$PEJ3GRepZP;-=vF0Ov#KZiOxN=Vss-OX>f?YdpB z?6~-{Eua428y+@78J-zr6LUk z6|6u-#kyV0PlXNyM1ktWMEUj)fAX3S{B6Mo2_K|*Jr@qQ@PPyvrLX`*LvkMo)%Q5* z-(TrmcFt$McaaFful&=;{`|XN?6-@|Ipj*RV$9JXn*>mYirXa*zp)_!XC!mTr z6fzW4K!Gs8Py#8WfQ(B4A;K6%f)e5kNE-qq=Qdd=awrSX#{cd<{@VC!2b>NDoh@$# zAylCr2&OQ_1HMpPEQJzUp5w(Zf`F@%j*jP(M)vdn_V&9TeC9}pkwc{ER!J(WhH9%j zJX2;XdRpvS@z}r#6+_LktW=v_u6B2~1we!jXapXmR8y3u-Qdxhflwxssd{c#I`#3J zH$Hgpb4QDXo31@SN%f1?FM0OZ%=A=a-^j7aDf;zGmWLoHyFrLNUInJ56M~F*R!Ui{ z<_(Av$*dbTeb>dIv+r=Vw_|CcGB)VQ7UUj&=)-sYcE|4?9_Z;IYtD~tdiT4xOicgo zPY-mgUVUKjNh906_UcWJT`iZ(Yu2xz93UYtIe*>mqemb7&7U@|?O2i`Z~4a0dV4lX z4>_;A7{2%{OsQ5+*6Qh|?OiI4hrV0d`sm>EFA!C%zVX^=f;Q)pj^hLqLrhP`;@Ntm zP!`;EIE9ixk4cqq#4#|aN(e33l~~0Ut_`V(DY01uinZDA{^x&PdF7QonH1&5E6*Kx z(`zn1JUG#|wD;J-!?9E!EC-oXTNv^}VRq|F&tH7rmbcz`>ocis&%gR|cS{G-OaqWg ztpSmp&Za#c5QM22BMyg(X!v0(#gms_!4S zXLb~L{rmMF^O@X#9k-H368Ep@4i+3e4?_Zeg3*N1Na2W7-fsKWuc4<$P|c*=qRNOe{iTy8JM_J>J3(`?U|cz z%^ZB<1Mhj;p2v?~edUf9oWCK`6EC>_Z6lj5Zs6DfQ&f}&mL;S{2}43E7U0PGh%vEX z)DomZ2W%apLm`(0RTg=1XLTN*Ihu#Nwl1I7Fa(RGj>jBlS#A~2wZmUznT*|VAWxlg zJTedatyNjtlo0g}76(?rJ-7W}?;SsU;@+R_eQ0mrQ02E@fAi%RtqgkCWgA{nbA^R= z$*NeHmPWx+fl~?tb_-4cu!rC=0<)9C2_S1N$l!TShB-f(v%$+V>&O2OC-T1{+{+Jp z?i)UD{{E}4 zyy0TcHVZ}1T5qOF*q%7uXf%>w!>U#Lj~^=eJ@d`Rq2mY6-L^eWY5&uQt9|9^g_ai5 zTAL<`F(wQ{KL}Q@UL6E5U0e9f-@fZ#KloplUVr(cd!If%*|ew+jGySQtgHm~-UA2! z8%`{n7rBm1jx$ctbLLgW4^cMT>z3a}O|L9AXUwGy3zW+nN^(!y!lfhIYzW4Ct zlMg+(ar?Rm{isxIwil$7WMRvOjhE~?Z)#@tb6@;QSnVGj>_-!)*3{y(r*FWu;=UmC z`n=w#baG-Y3cYsHh>FD`P_M4^l&B_ge7e?K93G#YOWG|kaa8c5a?h@fD|T+^+qHgR z#l}7;XE1*n8nZ4e0DBAppadi^4h@3=lZwPx{L#Kyr4$Ru$QeVS7Y+^<47I-fmCrtT z+kZcC-|b;_?53+%U%q8%^V+p`;~U$-)=aDUq~)_Mb3!&Wf(4+FAfy8Ta6$kH$pj*E zLZL%EI~a@+fSy5MdB$$ZU_m}}xi6Xl;j{9cUu;)PHQ#5O7Bho*#;fwsKnQ^*B@9p* zX9%2RlmjbDmHsqoi7$Tm_rLI|fA7@H4KKWWUDbQ(%P-xseoYo$@U(YcorMAmXaJ zJufj9alTylwnOP7z2>dvXYmEkiUTC&_rIRE90CKL<(_@S) z7K@oh!wWmtja;>L!*p}{yLUY;{6b6`+Ox5-QK49C)>lN8Uw`qAp#jlsp>j#o6JWrI z=x3x?RDp$VnueiPC*E|)jvs&f{ttZSXV+bI;mfuUeCL6u9(nA{KYjG?HV%)SbJ3M2 zkMFpdJjih+P$iR;teDZjsbHUbi6)9xaS~uo5ZKYZfFMaDfHl6=s1K@RP zves;;3-Lt##K~i>Io_Zjo zQ$BzFz5^##4~^b;|F?g5>rcLS*8^{S)s=tnn%#HZ{=`8!bla^z73BPCCDMgzUwP`( zxaa$)=caaT-+bK_SM5J?`0(M=L@RS>ik(<>_7@ z51u$#)T&tWk~AF~9^N>*YW3Q|;nBerL8I&=VvLkh(#kYq=-Gl7836#u5`+Yn#)2^h z$jXG{)ZmYfHC)&0ce%(I+LPo8Zd`4EdGOUR>= zUx1GsvDNn4Gg9MIbR?>wt}u z&lgrO^E16(_f&!K7c`guA`ImhVJ`T5vvQYi!gC`d38}>p$peT(5|ypxfBDt-j^F#4 z7u|T(vANp8gU9D;@~ZO(Zo7AXu{!))Z~o=S_B^(!PkWVq&&(b;F*n)Z;ok7bnVHiw z?Pj~RY4bVvKlo_5ua|(c%w!qHaf*m}SGwnU#bVJ~+v#+G<$1ez3_6fFbJeSV>4)F_ z5wx~mSpU)+Yu}xcg zJoVk5ey)s-(P;R}*T22y?C|h#P*_#ld-UK#Kbajr(Vm}|O@`E4#yQjzJ&fm6e)mZhmyB|1j{dqt7kAHmN@h3*ArIUyDVDDHs z*wY_G)zzZ|eIpBv8BckY!9iz*6M*9<#B7}}INyBn^R08vU6`2bz3hVf?s<5)Z|vxq zJ@5O?->8YgYcC#s-`~Db6<7Y?lV5ox>;2bHeSXKTO`%_i+lkV;5czSl(aMsZ-ttSY ze8ErezGtBkTM|L2`pbc{DOvEm5^FPOxEhq_PtJwX_C_Y|%nuHhuD@v0hE1#2u3K3O z0wr{Fj&6M6JoYvFHq2(e zh!-LuoFEW@$cPf0U}lM36@{~7Oc%G|eBfUG3@sK$CE!w(r%Rg1r7;()KhG1P2<*bQ z&$5tQIC5DFa{iu5NVf0bq(vrw-XJ0X0RR9=L_t&rHmEch?s#6ru@KgFY{#nx zgMB>|biVrUpEz*$Lr3rVd060cww?Q{FTJuaNQ2RxV%;l_B~ja^IyLArUkj&1$6ANz z2`L>d(e9sR#t49m>^Sb?KFrS-k}SRId83v82O;HoW0_~gYiG}mEs_Ecp%eF?JM?dV z@@Iv;U)r{7`;UIU|75*mTRh#gF#&PsRX1IJ%|$y7PmEtMva$hk_Qb)RJJ|J=` z>4V4i9Y7VHn44SGS50kaq^H{8Y;vmpnyb&>wz^Q8Ikmd4zkd4onoX-(jiv#we_+4{ zhyx7dLu`3qvb57YEmuT%p-o7zx$XfyYXN zJ;N`$5vnV`@zIZ=YW~{ae{$nF=SuSSpV)ih=!R42Oyf-bhqr#~-{1fK-~Pu>9Q@HY zl2TCzmDMAwhqkT-jEa$*o0%_Hdkf{NaWT@_4Gm3ajiI> zs>Pjjyf&u*q;pYkRK^~^$CsF?V!>(8^MXtm577w(){X@_l9}Z!<;1bWMv^3{!Ji#Y zY^S9I*>(n%Cs`t8B#DhuzH-f`_0&MQuYaZc>Gyv6quaiI;_=51KJfS@7j1mi%P#Iy zvtwIdS=|2Ww%4cO-#v*DXMJk8@Bq;z|ZriXF-Ba#9L>^b@l*08Tu=MJXZk`PE&Ek|G~k4gl;Z)EQzY4MRwjvCm3Zyk;w&n3zTqmv7np)~l~ub>5Dfe*N9=|KRVcRR8V&{i2P} zJouAuZXVhcr7@hGf?nm6NINZka9<~oEpp!2@W6{+?nK`c|NX76e(K{rYe#z558e8` z#}n!xhR!*YB^tz?k38~&pMU4+|M+ZS-PWtFyyW&fe!OFi6MN}0 zzw%|5kE~w(&Ch;2+Irp}{K>~Ru3J?d8e}363`%;I+l?lbf))-mzhL%L+YG1S7dlqE>T_w(S5A*3zVrbO2=x6cs832tq%NbV&qJT$JqF z2mlBSNR>$tDN~BZh)mdwtszU!IZD%1IorTUC&YpB1X*SSsL%$fO2}khNwJBh>kF+p zykPgWH{N_x#_$h+^m||Zug^d5!EarC-sW?Uf9uNH;VsodxB#X=S-<7i8nSm@2G(T- zR}0A!h6xJsY=OAE`XeBZppZcTQ{GtQ06>Fq`C3O>Qp!BbJbR{Xz%#8hFFBvBVm}w2 z1aRD~UU$nb04`UTA=ZH(8w(&LDwR23YO%q37b*qUt^QLKceqMsU7 z@W2_5D>^h;C913#9<43RJUnsi?$7=kP^u6%Z8-11gO99T74DzZ5C6}PcfPd<)PaqI zuzIa2RU!`U^*2SR0+5ZF*5CcnhyLq7K6$3LaLHvme{JW@kA45=x)7c?IajDkqK-4B z(mP!4?LF}LksTLZviAqKxf`w>jQVHiXGgbg4*enu$13oJN<0pYm%g!xpwrvDI03jd-sJH0QR6<#Rkfurp zipV5RWv*n=M=3*c>0BqQ7DP}KC>c#5PF*__ff5;jr@_RG5*WxLSqtnC2*D+mVrLYi zLIMTI%now_YzC3$&9cr)Pg+lEQZzkLn>g))pzjYp_>aH$fxmw8!3Tfi?Qi~{yB|OQ zX9upnV#~Fc*SBoGWbMGvq<7gF)t|<3!K9vUI4dPa7L*b-%bsV8G1P?Uak8c<5KbE) z5=OH_Mqp*SqPfq2B4|laZkf3)9kA4;gNUdEGrPRXv^bc0X;02ymD5Am5khxX5&*CO zYpoOrjLZyDSO8#_oPiKVGqaG0MIAV;XJ%*TCq-0Q^}<)Zb=#$v{r6|TG_m*5fBMo7 zMtWAR{lQ~ze%6b%|*G7%!6BOx$% zvH~QF#bNS`!k~+^x@Wivem*wJFRCRke<k)X4ARr?ZHExPAN56$#fWsQu!)QMIeSzx7rE&qLGFmXnHbH{i<(ezgO&FoHXK2 zLZGC7>hxq77PJfim9pS_N_o0iDqHKUwE_g7zTt&8KlIQYqrF;iHqoqC`v#m8D+UXG zWyAdBu^t|n=``04SJw}(i~w4ezWv}+YghKJn^rql4iyY(t!J8@u~oe*Mu)z4@6%r3 z^++7pl`zUGeKRMIRYPLSPamShqr!Q{&)PC*Y z(aOQ;W8-tpnJ0E^-SUmE|7XGX?tk#>4WhOOATe83Zd_Z+2s7nfmO6=$77R+UiN~UUwG-o_doRL)#q-XKJ|2MZhma* zbwBv-H?P0^1$RF1Xl$eQ83)N^N!l_dgO%o={`RhQtK-$1)(@;%fuYZtt>BLIN!1IkU44`~1z+^EC-;?B|ucVYNb54k~xgb$_o?vDI zW@7DynTY`va7X3J`_EdP`7c7q+!~2oE=Pc#$IRfS4n0-!B604;SKs;Q!Eb%(Q+pq} z=To2k?iap!$188zd(*Ds=Fxk0Y=7BYu)c+Tbxaj?q_QHVI0`j$>RN&Hn$YqglxPuC zNtRey^pFJrF6w0>AehRN45efAr@)f&BWXKl9-ayywX~_~`iDIXj20*!0+qFSGNWn#_3n?7=MV4&J;wmY3TDpaCkq1|km zPHb7U+i|H7R!XJE9(^LqY-ZdAyDmO)^5jCj-qSxkKReeGsY+Cq{j1`3vlVBx#1Y`m7)Hjg9o1+=nJ2?_s)klj!f2?ho3t5(f_-r zx2L#zbZqsy;ri^+;p*56)~}nZr6(p1yyeZWdUVg3uYU9Cm)&&n_@Sey>d4$~w?PVq zv{LSU`rv`t@$;f{ck)kvGI7sOcU*J5ty$jel?$tth19pwS*Ly94`qEOl`Vf^!RrPdGQ$s?Cuz2b*opNXJ;uGc?#@bMe2 zU48ZL-pel<9a>qCgT3IFpc8Prj!3oj_S3_D!58j+T?#FqH4 z`QNf4C1=hYIyzM@^}hD)fAEgq_=DXye*WVhc>mXK+jIXz;p)-juekZ>`9rjN^!lj{ zzuXKe?GE`~qr{wM*i^EtWE3X`FjG=!V3vvm0Sa_X){sKX(Uh|`BbN0vKg)g=OD8!Z zy6hUb>n#?Y!=n3mZZfs(eV+BUu#Bg-6b0l}q*NG<{g9FAG>`6$(Kk2!%G7X*tS!`oLge{jr_&*_jYbWHV&=8$*RR{KNqfP@jT^u4`Onm9wF@u2=-h2P9(<_r z*q(i}lM@>@ZJw{yJ*nCa!=2fgMzf$Mndb&M5J4#0 zfA#Hu`o8~s{cpYXOyksT_qbwTpCbyxP_S(@8d1rwR;&B>?=NoNG}POFur+z_XTQ4R z-M=xqZD%~caN_BwR-d!W>kUx~l%mi(Lc&)tPgw_hmDC=TG^=8#yi<>ROc)+ElP(_6#eA zOi~8mSV_4ky^BjWgNz9LJiG3aQ+V$4_#EqcE>`*rjZ-}Dr!DIVAW{jCQs5i1Nu^YF zCOxumLWPx)k;?CXNz#eJvJRER@*O|>x%U0krkRC-p;bw%Ei>as!9s2N z#N-)ZkdcRVTBy~7sL(4_z0oozLBt>kaujR6UfVS|{Gyv*`r&{0$coXiLZNW@;Ql}U z)MRo_7AMK@jjnJ<3*`WxT&p}+a) zNdL&Adyc&P`ugn;Kk)0(TUL+ukI&8wYOh$XwA)RxHi{ypRHxIaR4VWOy+66*XWu{n z=3hRwXWzu551zbw_fNj?@z&wPy0GrN7hZk-3$F=R49c0hmpBTGFi@3Swt1YIkzqAc zH;ME7QY)yoPbV9%T>ZPhbR)q-FZ!iz)uGJY`zM#|aB%IV&~ab`d$@++G5S_!su1aD zm5?0(a40k7gb=7S5s^!k6M_NRNy9ERlsT7VF3r+3F~-@{=B1Qe4}cJ*lzHD4VlG!o z&H=MyM^Y>2oR*5LLqsOV#R)(7^TYGYx8k!N1W^DWo+ZWR2asf!IVCg@3n5U7*kqPi zi-OX6?HcvuBlkV^;2od-(7&HKar8wmy!guV`!C!!y6(aop>p0yxvFk7W^>5OBV&XF z)RH7K8@7zBP%LrnpJ^nxgrzLEA6Qn3ewO8*6<&HK6Ktt+v{avZz8=U$($D_2IWZq6S*fRIc+Kc{zJK@R$psEgr<4BSJKsEU z>bMU4H5LEl^z_d4qX6)Od-t@1QXut#W2bA4MjXdd=zJK^!oq^Jc5L<9iRqc)p_S`5 zpVMi_YuBy0C6 zr#5Zb*l4uk%nkNRFlasR{)e9YlizyVzkKnVzx9@v-*xB14?cBlN$Q8q_mZ7VIfC<~yKQM)F>Dy1E( zdIffr4z1B5@SN}*ppXtSC4>;fK+MiK=Y#+jAX1yp@B|{xEM<VTASUVp;wRIc5={awb6mLTVRK5ZHr1{oK%C zNqDN>PJHwiT1lqCrM&SaBAo0l1@g`4U^R8q^<-} z(bk&_^%^sHo@X-Wc^&~v16)e#Ky^RsWbaMz(jhn{|F@1OnIpN))++;z{xl|rz4 z_wF^b8@~LNFEkeBRt*m~o6VrCvLtTT<`hP)#DsyIpKPoi?rAiQ?X+FWS{5^x4G)Y> zj?cH+?VwoFLQT)kt>3cUNlaU_MAT}viUogUWTf40_x1Ii;1fUl+0WLlUAtk!hA6a6 zgN-<=WUyuR!1&aer;Z+Y|fTF z7~!}ml-ZNeD0;QPa!dJTR~-?ZC@T*7;4Jkf8dQXoDQQ_k%7y?k0n%EF#KNI;ESan^ z4ih30kXK`g1v|l3vJe6s0g@0vgv`!bs}Qn`jVDz;NLfhcyt~~h-v?mfp23Q6&M-?R zQfK27aFC0$;c~F^Y>bdES}CP_d7u7ivri=E3PS|1r%Bt$|=M(PG4 z`B28CX?@FWXz|RD0pS@!miffOvG?!3@cHZB^sWPU-E-UbZfPHRYHa;u@BEc|f6&^p z?P@pj!V_-oLJ}s>CYPcVDSCo=NnL#DL!XPRKhuSDZ^^Sx__>Z45k0L^!@dAdtISer zd1O_z?+0HlySb=u)zrd7r`6<`YiVoU&`MV5EZ%YFgVOUEG9|RJI5#&}E*Ddii7*U; z!rt*(#kX@7=1-mm)6v=s{HRu|SxZF5IBQ)-?0i2^N~dXZaQ~i@r_T)Z_jOvW)2Gi^ zGI!qjK)F=9{^pn5_rU%4-F2*9U&u0>r8>}5PZE}-!8AJdylq>AF5J8K-nFBvPM(_f z%Me6iwO9>AU~Q77sg!LAW}#K@>mQiy%$G{VByP9caiI_n3=CM8tz5aXR4N@ic5Hfj zdY~|J{`NKXc5>?E@lLidK7I_X-nwSYIrH7yZjD6p+FyRn8{Y7$MlFqsmD|2^i|kub z!04j$Hf`RrZX{~g>uISlobuuE6GwOK+C4ozEu;iM-}jMRmc};CMuz(ef`53|0~H2lb+W9svtfBB1U@U{<^-Ckqv4PJKKpMEb4A!h)ApXxAF z8%Z(DE9mtV1eq=qYmsOxGp!99t$l%xsgo&8O=5jNw2%tYHci1=3Cv`j%`CE%5*>@K zXc0RAM_{Zo)-g+Cj3q(=0vX6TvRZpx&bkC4gqBLM1!7IasgkW$)g!u;}5;-Nil^|x$g_CkaWm*&mVj-n;Oop74jHuHTeJPky2R^%uY{J~T zetXawUwGueV?X@T=fD2B&yVzl?|SK_S6;qy?b=AK0MlA91&UVl&`4l09wU&7q)g@0NG*Sw_R%|SS zCL}-yD43B!S)GKI$oiy7NSDM8tf-DfBmH`|W&imP-}{qWKA+CUFTd%$1k(4s_Ocz@ zx75oQpY|`D$%Ld#n+&mg6~D->EWZ`cc%Gk)F+aB`hKLLqV342*T*eqdx!68%cl_Xg zTSHbri(NX^NH}iR8-2wd)$2X<#PRzcI#8&TMb^pKwVU)Sueq+Lr})I0u? zqpMb(oS2zgm~GX@7iOlsAgVRnV||5${ARQ6c~I|U7o4+s|KpEEBcrpkZDZ0(IZ}Rk z?Yd0|4?aF|`gC8ZBDF4;{Nmuo&6~ET^W(KvqqnaR$Lak1Y_(b)8Xj0!SZFqz&1Q3a zd|XI<{-$&08uJv1SG?q={f{5G<(uEyv2|;lCXPi#c^B>4Ik>X4a&+aU&F7xj_vqJd z{c&UB#O_Ns{N0DY@!BhP9k=@2&8z?8*4uygS6{#X@NvV~sMVEH)^XNpd+4;{h7qmQ zO*C6}p)+ifKY#CU53L(M{Eg3Vx?=UZtH#z3mZC6Aqn1Cjz!oS9WZDbNi5~_mtWpl! zUQ)3oW}}sefdYp@8AxRkux}^>>$DWkT2LApDN7_dYg2YcqFBuB1p|6s;3y*^AV3yO z(pfT&tff|*b&@p6veaY%;Ax>HYAwT{6l$e}QwTcLLI}!*Xmf;7dPHDS%gn|)$K*1R zWGUWxbb2}RclJTZ>_C8!fQ!9?M7&6$5ajX@W~pFg^P$Vc`6R<`P204&TrKqU`%wU2 z`@$D*|LeqdDQ8_{;)1%Rov z3PhYifS@HB0VV?=5E6tyAR@Uaww6<7mnTrq3O7Vol9ypQ^JTHfg3CQxUVM2@?@ZTF zsq1&1HzQ+a6tEN@N-2pUOHyZTu^bJpEj!?ao*sJutH{Rfv3( z8YUSPyelt0FNCzWP??XXtI_EAsnbE(+kfPAO9*FhzCJIEBF+-ZOeWindS*I311m~i zf=e!?jvnfGm`vqsI<)I<3bZeR#0i*J-!1Ea~m- zt&~bxmd(t~S>nFFzRcjn+>D11hM^x;R8`t)BE@K=|Xwq>e-Vg*R9#GW#fibt9#z_e?EBehLQfU zjm>%zCkt0zxIK;AvQP@WVC|aK!y~=EfzN#A+wcGDcmMVK|Mt)$oN<(8NhgJN*3PUHh*G#f6+9oo#pO~l2t0{Opp>2j!CBVvL_op{ zi4;32f|NN;V>DTZWRhg+xjSChJ|*VueM~^iLU7*b>zqXt%(T?d#|Qw#n2%D-=T?D$ zoFfZ<8QQI8yX7&q)(Vqz<;UuTdA+hjQnYR7xOkoL_c=iUq zya>Gv>lH#g`?dM;Vks4SW_4zKh(DdG`;W`o^bv#@6;xi3e)qC#GfQ z1)gZjq}o5iQtdl(ve~RNvye(D%u-84%|>f%{i?Z{mI|s5A3e#;vT0FW~R&^d#MIdLC>1TYE|C7_b0cDrJqYtbWSfLBE;o1{Id&GVBX!IbJ%DJtUx3nbRePtopX-mi4zN* zq`hX{hHu|{&-CQ<{olRqFMi{l4?lkE1CO4*;fA%F=Z|h)vsG8G+2^ls$cnUe&!p&w zl#!I7LS}~+G2CMhH z{*~vh9UbdiaqUd+c{U!Lw6&5EN~MNtQU?gqfdz%+tdf$52uS3i7(aW5IQva4_Avs0 zu(qx01r;bSe(LA9X3&{GeRAsXk&PFhui3G}S|^cy*dGN_B}b+gI!RorL{dsa2Bnb7 zt2LV13o~d;P0V*X@w)!r_CoE@p@ZwzZme)^oy z!{vcYiCsH)DNo*c$L-^1j&9w2&gKoHZ+pkvhxP2LOY|K-w zzqs;=M;@Ko`*1W=@~i%Nmu~sq-TUvj=Kur5n62m)qwdMS_={~D)`er|l>64N1Rs4<+)(HS6R8HpBg-QZ4Qa<@ABO-w;*r6ll46V!4aD@UKOOf+H zSt6o935tP;taIR)5LvJgC6F=+U*SKKg@OS@QS({XahT z$G_dECl8+-f9>w=j6Z(;Yu}Lh1HA(`ju*mKgMF-nKt~A*KP3k;VW$KXgh*jojePlI z>GBz9>7P!81(h|MG=SmAh1cKqk3Zb8edW+tb@Hi4b*U$Hs8FpL?UTMaHC;P>~N z8>gK#O#`oxwqrzOfqVBo4N7M0HmzETS;JZC4a2GV+00UwauBMxL7uOZv<(a{vr0NA z&B9Fk@*B_D_w-={a^3pP58iRd^wf$Cn>NKd-q_!(m9LZ@92&~fR0tJDMFwF_Y8^WQ zYe_248X8$S*i6$7h<0bekwJ34?-xrwNzw>{C`md3#r(qTsx&T!rV3s; z>;-bZKZR`P(gmyJ=`&~gs>Ab><9l0sAGzztyLVr``qCZ!J(Yj>L5~zGcIXb>+<$tl7A0M3sHhX=_)6W@?Wn zjxv-U21-U{2(>4D5ZaQk>{x(8Mc|lO0Rmfe#@TL6;jCk3l!!p&oB%{3XGjQKC zjscm^Hr5M}?F>A_x?)$gIy7 zDS$1}0%sBb0Ou&N(TBMu8qmRGqZdcqADa*t+Zg?0C=bz5T=Q z`|EFi<7@x-;}7=sw649R_PUom8W_3ug7+jnJ8J~>P8L#Tlm!4mI&i>(2#9RnR)9=- z*=Sh_HV+~g;5nKuOO|)m(GV~3S)P>~J?qbB8S2?Ld=BsK*-2LDb85j_rG%9P0!-5J zr1VA6_Qng7wYf7wM!~Cp?VXq3@am_Yxc8}_{`})#`E=ZhAK8EAC0CzQs(kbO>whgP z4js1}I#w_?z{Z~RBzg!Um8s`5Xdt&;^ zYqw3!w(8BPu@!4QU34PbcWBlmPFB&hjOPVFtdy|i8uixZ(NPkjGcnbPo8ak_(>3Ks z?bKP)%%&Z8gwk<4@w8kux@mfPnhYk=FxpzJcJRslXxn3>Ym;5g%dfh;KHK=te}8qy z_AN}J+BcA-?b(?r1of&{y!y_&Za;kZV1D{R5J&-@)=83C1{4~l0%Bszj6FU5^K(Xf z9wL)<*4pXG@!_F?=}s#s6oVkJNuoh^+J*={Vo|lIxu*iA^ z%(EC~24dSaDnjhyvMj&@R!m4lLXylUk_u)KW^vQ=<$(3r!MI(e{v~|GR&S3 z0+6ulVaYn>X=j}97>xtblepL>+|#RvDuL>8-}~mz@BHDvP3$|yle2xJc>TreFTQwt zsb?%$_v$${*o2^!WxieTGJ#%@fkVWlX<^Gxa(U>~y<|k|NDJ_tC)fG^=aNfQM4Q&F z*|cHkYhVBIORw6sah0y8k}v|GuYC8u6KAHXy#+#WhMXY+E>(&n{XJ`k2AWwjv^vfj zg#JS84jemD^h>ktIBPe8$nUf>0n)l`-Nr3*bMkPIP@xw+Vs)G2ZYC- zRQ1{Ud++)2o8R=hb!)fQ8!hdLef#!yTFL9*@R|>N-~+{CG(J8~WOL15-}ifZhIP5( z;@G4uYhple?baQp70*teNo`sxdTg+hrmNR&(n{6mjzxu@sNnesLAl&&CtApdAANA3 zR9MxsqHo2jC-*+Eb?estYEN&e_>FJ=H>Y&r&UGrNR)$CKzVA?T;lwZBxMje%D~978 z8~y%)uxHZ<80}h<7*)l1$7roNqzF^xhmoWX3IV{r;Y<@bF9HA(7Bg1PfL1OBCk-1b zEem3@j=-@%of%sRuJEN!PQ%Q65^C_G;VK~T^+&sO@L)DrW+f*qmfk;wp^ z<;?f}Bqr^ZTCt7em>HGqHU}@QJcN8qAR|EDtq)zTB%tTpy1R4s^3o4OJ_thO!5AVq zG}eIUg+%0eV7bG9M5<&&%b*Mj0hUzp?k8{r5j~-_IZX=fC`e|Md8+ zzjX6eyNc_k4xhW_^16TSbYZLw5o8VDWs;4xP6~yH1Y%JSjs+qyFHI9#0*RK})w6uc zk_9gJ8ow~jc|LCI*%#(9!s1+krQ?-GbeM;p3KbYfp4S1V0yNe{7_l&lY`KRTiET7n z{wZ@llaU1O#4CCSELo`E50Aa2bI zshHVW%F95{MSPZjcg!3rk+}?d%AF(oDVxr8b)>&%rj^YuWIuZ7a6K7YH`3E;9P|3d zPAxRbq0(Bl!jUK!A(HC5C@q-Wi{D~(YJAdbHrNhHV z_P1KCEt|GCn(aojxoXv_@$qq`JR(x6OSba8z&J2z8$bv^=Uh}Qx@jzyD}oIP=_2?oP1S35uZ|*kZ2!Iop4@-+)z^)zJ@-dH|7jtk z4I`({A2|J*xAYH>j`eSZDCh%EgN?|ha3;wBJhhq|DO86wiA)(nP*SBG^3ez43^MSA zGmvG?z#-%$w9HzW3Db1}j0{qu5a2kk1q;L+K*?2sq*O{ug6^M6DF8qroVB@&0f2lo zI8!%oKtzFP$uToBG82&%LLf?J&Or}go1-eskdGB-$d9*+;jLp9L>aM==nKsAAOZk( zU@c2bgwo8aAj_S0(oU>v+qu~~2PQE_b&HW_4I|GV7*R8KT?w;$Cevfdz#y-FA)+JM zeFs+DtsF5R7z%@c$;Q4!b_h=7%G8-a9cXGNo`xtEu_^|>n?F8he7$-5Wfxzu`{L`a z`N}6h_1!&pf9qS*+b^uX`<2HA2C6I8>@q`F&4(LW&bq9vXvrgx6zG6~SqMad))91j zgU;g6u&goQ+1_C>t~u*0azsfy50`8y40%={zYG;Y(AW36HQ7Tmg7-#KO*Is|w<=3>5*;~Kz)yh!cp?wG9j%##ko?%ze zuYc>?i^^rrw%hIF$Bz#Tt^j~?xx8)Lwr_p=8-+rt(QKj=##+zww9-N-6ih^6Pyo)d z7~)RWZnt}fDnrA=t#+ePU(n!J`v-$4>U28H&Sk0ev~l2vnrtGN`}_Nk9y%!lZk;)? zaI(EZ^)@D-4o=-P#QsiCGcu3At;;1C_yq<5E?)^LMa@PBg4!{tfd0uQh}aQ$mr4x z5tTv!HfffRh%m+oAt=pq^c@i$Q`eJ7iOa~VCZ90A!wzGNZnRT0j`kY%>XewAV zKI+zYuuFF1yoZ?qKz5n$ykw=_S`Xx^1jvj+kaL{(lI3H_5X@rq<%9rYL?Hng8JMln zg0qwrI0jS@qC-Rxc^>h^nVInk+PZDqKY!`poS{Gb{onuQ7rt=cL${v0X65Bq9DT%$@%1%($e4&L_~DR0)Qk6CYG3Ac~{E@nSDYaasa43B5=q8 zGXj)?8GMo;pkq)fMo$iMm7K8$_bm8pjLE-OLieKorHuCUMSf*QWQ9q8DI&3|KA*@`nX8X2HfA{xKZ`rc-(MKQhwI^9Bi5aKGsA!ePPGErO z6@ggU1x(wm_H5`?Y(_Rr8EmK1DHVEvn{j4}0hB$*p>U`h?VyupCr_PX6c69};MNzN ze&zY%ybH%RuD^=Q1qiCB0ooxqk~DQvYSbYkNZBV!h%$0Sp6hWEfsoPX1Uz;QGh_lH za26~}wm{@uOQ1m~EeKyr=bX#ScbrHF*%2ZE5(*Sb69^$uQA(BpAP9VBCUQhBHzEKb zgc1T!=S&ts1cH$qb=g}=1AuWhmwXq3ZAJjfSD|yx*hFio2sZ z)y8#V)zRurZ69r%aT@!|D$pbgtUCcY0F;7>vy|C|4s9HgZ$L6XL+rkLVgg_}HJ%|4 zYnXC3E?^OSVPay=5$xP8m>#3{7iT;)akk~1h6jm z(Mp12liA6M*@;Pe%}ZYM(l@`xv}zxE@1NZ9-{1Vym+s!SbxlbfxcvD2s!$!=a^s2@ z+z6GvmZWT=AxxsZg3TC+oiCYKI^oC~PkX3*KVSzlvr`DcP;a3&)l8BkGiGXP$_v8T zx#@Oip`gDv5|v>CBicv*mKJ5?8F_EVF5v`a$UXq1K*X z^Z;;bYHHg>*9Y1T_x4o!s)dlbedhSS!#BP1O#`Fr8nyWgcW(O5ZQl)axu`4DruxkM zb+3Nmo~NE%vtqPWYh;Oy`~Wp5Q3zD%0fkP5p>`q=)F~=#dqI*UJ&A|?A*6K;;G7F0 z4yw}U-jh?|WL+K_Z&H#BhNnk^_GK&R;y0CcUU+F~<4|vjfjw|VXjyE^Yyn8KL?cz` zl|4WO00=?>0U)y$Kupd#$BYUEA~RV*mYKm>ba}bIn_Ym^5`oBO2>?Le44fq(HG@Eb z0zeS3a}E~=donvjnOnpX;%<$T(jiK+KxCCtAhcAaQsKnJj23#Jw zp|`&hl@(Iw^pR70o?Q6RV{b)Fiyr6x zWJ%y;aT^EfDljZU9KZ~a59rHNKEaHO0!ttSNI*aY?9$;02@!3^UZ|Zh4B+`eoVmyM zjz>|n_M$7l|I|~r-}>JV-E-du|Mk;f`Oe-eFIw}$t5%-7ZQn@mxu*Amfk3SgR2Cx0)U^bPTv2v;=_kL;yvcIW2NE(cl0cn1RK)Io9fDl)t&4sW z7rU5XvA(@5L!!mC(#?Dq?{>cDoK3RM)~)gokh=kqloIlaAgfcSr{!TA^+mt`>3{jH zkAHIfkthH1*WP|&|6}|19NcyB`8`LDuRic2Bl>n+_WHqH=d?P#$$S$t2!u|uj06cO zHF(03Q=JZiFHKKSI_#w#G!H-ckn&``*+AhjvoWbL>BbG~YPAJpgd?!lw%f5EdO;9m zSsMC&k|YZY3t5&0LDWtXtu^8ja?esqPX~cgKvCp~@D*%;5c ziwt2@$0yKq!k&X9m~_Aha&$m9TS;4A^uj z!N#R86u_him?27~BW8A#N{K-bG1#tH%Gz_Gvx90tZY$%!B-(WJt2u`57oEi)=sZR;bYVW(ZI&$kDw zBO2SO&1&s-$LYFb-;)-FoX!zVxsEdHB#?zdf~c$Cm3~ zv|-zzT6Mv%%=BE?B1#N-wkc4hToTG4wShuwNqTGnT$ZP4%cshBGdW;hoVW-}WiR<` zVV3Sau}p~F#AfMv<}Rt9uuI#?55`;x6JRlB$%i!ZV)S=b9CPtxmJ%-n=ra3I03u2z zLz=7;Hc`GR4flqM@A>h!P9HjX>wo=2YjS4Y%F515FYKH?x#K0TS-0&4edk<&QDt(f zEsRl)l=d%(oU_~PyQ{PUv1hAPSmU>O<)Iz*Xctx}`_g!2m8 zqC-qkqi|YFWdfF2{$&vnTbE;u2*4%=vIryq**Yf6>=;NYwMfyBaCWak#}YBG#VG5X z%SULlKp>Ptpvt#iBFa}tUIT)~;-;VY_X9`&VE8y{~?|T#ByWz4e@JBd@t}`^c*E&h))} zGV|Mx0GbtcXgJe82?Uc#q|7N?RJmOolSqqqAeX|+%U)fjmAu1f$&K!vfjSixRWa$puH^LiN(6HqsS5=+qU;%KC2xa=jwSBbOFFi6b^WUR!@F|r6W^!5ck z{rbrRhrjvZj~qWeo*jK$1yI&v!>0AF<8FTEdj~JR-k_g0)3g-}K#-QPWL#@tpzp+i zyME=`Yj^M3e)90)(JgC>BR&6{R^|rx4!v}Z+*ub9)0q_4}bP(-@vNg zN*@T^f8gPT`KhdJ6@ceuN^?o7CP5ojmi3-GRqxDC`RUZA0lZ{q;k*rW*}1`*4Sl*= z!b*|r7ER<@8C$`GtVo8ErII2*(9RiykwQVncp?D7iKz$Hx70rST{vRbZMTuiu_FP5 zEWi;n3jrw6cJ~Mbp_aa^MQjnBb0|p&!6XrpktA@pfv%g9lN4QX0&#W$I7CF^Zka}e z#0eq_41t`G85wl4>-)&ECyXOaokQ!vEHG< z-lnsyq0yBOJ^Z8>_8dJ?+jC$hh3aEZPfW~r=4x><@;#{vUNBS*dMiLSW9utU(Th4w zOG*jpeBojQfj*>035oZdn0powySSa6?Vu$hTw+@-!vt^<$Hm2vh8EX;9-}aGch?00 zARNnXv*IE^y_iH0VK?mxwC9|I+ya@k*3LO8F||VLz;n$^W+o2WiR-TxR;(!0W+(SN zw&yoq^Xg7L-MV4mH(z_*hP%3ub0QAu*p!kGV$>G7p0s3I-Mg3?P`m!Q#wm2hT~z5b-R4 zkBBH7VlLx{nMO>l&x!?R!Z~1HC{CTV+3;YoI;4Em-~aJ_pZcSBpFVnc>xMDE7?hm3 z^4iPw(Aph0zDW&?Wihl{HEd|2eX1eH7SRzj4dm_uT%Hm%a3&T^H|v^s#;W9{uqh-#0DO zTP;sc&fa(bLx+wW`-{K)&|BZ}oAb?uxziJk#$+ifWNB6LYZCh5XEYaw%rXx6!4* z1mG+Q0GmK$zkJcQa$6O|kPp>Ze92;S3IedQ1eB-U2qN!gz&t!)fG#x32Bbs+0wgJ= z$(#@pKq#OTVyy}Y9;c}Z6sCq91wk;d(yz_eZ~5$3Z~xvc_uqNf*l_i&uej*i-NVD1 zuhZ3?GdMh_`)m@2CQbx+pmJ}C$U^2hPS;roA#&9`A|l4cRg&KmbY0dTEM0i+Y5>6H zS_CL^wQgQSVe{6%CD#c6EF`gH0?T9_GqVEe&gNp2e;y)&vmliaLI@DuUi~s?Zx;-;)V|nfY&h{olU)(T}vJW_Mn=cCcE~VRX$)UhDO) z-+1YZvXbAKZDy^^M!}}F1=Hf;65jsPAHV+k7wx)m=ix)gSFKt#vSMJ*o;?Cpxm?an zqLflXHX4mWso2-o*Q_@@sQ_Im475~H6wNHuPESq~%W|dn=+UD$Tzm7t+N}eFmADyy z^UMGI>No$NZ96Y5^$pe<?*N6J!8!qs7tW(?1DGhBYd%aa*D^QOiX4esh z*$aHjq?AG-SO!jbS}{@JP%=6s2L{j)f-{IBlbmFpFO1aAvF|K_Wm8jHfLOv)^J(IO z*|8uHib3RcPXHpez7%=bnXmmkqY*%z#70phfvw9BQ3}mM5|IQZqNTCoNZruJxn-OH z?k*bv06@mEROkqj4o0ERkV#|`S^&rGX19r7U{oDQnyp$QA@j;5N?WlGSVmb;9Ke#W zBvdeOPcPj5^XW(SB>Rp|ADC*-O`8Q_2M5bN1sE=c73~$hpcAKrkO^ZmjI@uKGN6S* z+nB_~g+PnUkOF1|)|p`sL|fV(fu2-&*RifJg2+YC^1a#-i?gXvzI$T#zl*MCY2RMV zJGe_^Ms+eB}jWJ%!?yS5Nou7_ViP ztu%>}sFV}}1lT-%#YIJXT#U}Tvouf&v|T6Cy*o=Xac7-No*TJ5p(BAyGhW#NP`8m( zFn7DY&@s4Np_ouXu5061ob}pBETlt}2m+A1j?Mv-Aj!lM1fsC$K(X|tVC*9S)}Vdhq@SEfp7P?IaN^`wCuiYSU11 z$vMH!?YM1wR2nFPUj+^!C1`=|WQ045hpVc7z(HpU<# z3iil3kO)j^D-r0E){O=YNMz-l17ev|wuB^RM&X&1$N(Z}O(1gLfk@W6JnkR|oHL#g z0Khq4Dk5iql@P*FiXc(=#A)tMmX;6z2tX7b5iu{$>ZEQbEt3@rQR;fVU0Z05j;Q1-hF(QnE_ZzDVNC<<>*7#i7uu`0>}upls8hha?_ zwc?bBGU+7vj=)GKNpiv>6l^k?-x}Kx9Cs1B?Lsy-JHx7J{cX;QdbiqI%WY~a$@&|;w-y&>8r_OF6UhTKxMEu zK!RI7_xW#p=D+4nA1#Dj?F-hgS=k$T!>i8U_2%~ll~Sx>{?zoAa|YgVhy08<+ zCtmuZS6qGb4RM-PN<(+t^~0nwfAx*8J$UR;820t{_J)C2J-RaVix%+Y_{nCcIdQyk z@bHnxpZNKieS4~`cCIaMSZ`nag4J8M>d`Uj_xFPrWF`>}RISJg21trAQ%I1BL}!5`eMNmR-dpS%)zQQ$X&d#xM)%`KDiDX%9^Euv#5eBbZ1sDrM6{#>Im z(353et=hX0hYdEHwG+u>k9WR(=kcFCFy88jp0aS@m3zZ#*&8T$zK|p_OPzKq3%C%5 zWL>-6b_m9iKxH%Au&x|>LJ3J=jI~S!p-@rgR6Fg2QBd^5G)bhC#O$220@+d5tsugp zk$0DGH z#{iL#j_GVgh9Pfv0O(Rbh;h-j*)cg|h17Wfi9lJ}@sy}mdy8dp{NS0pfA~Lt{CmFx zq_%JDd(Z1%aOEYJ^bLBU%sk_Pwd9k|T%sf+$duXB8lYS5 z?T%qxWSJ~QYy_N(S|Lg$*)cJpREYWfQvfMt-rYfrL||#jtt@4i06>7Guv{v0bYf;Ly2mW#s`=OCoHLzP=zGKKS6T@!?tJo3{`C9*{+}t@Yp%S& zk7#7wno94;_6u$t*>P30GWe@^zWuBJ_4(bqFOCYq`0*!>CwDk|fJA zUwipy_4NDgmgosR#b#o*kvi)uuhGQNmpgWxbNwq`*NL0!SC8#Kbo9qRyzRP|zG~vg z+=-Kq%*~FkUbn5-w^BsKrw>m~ok@FTC!OD0$@cHsAg{V&<&JZU{VO9;8GzI%3o!2> z3muVDne=?bf)HegL4gnzV;I?JE@!dveVeaMfxgY!h{AB9y#Q>QWfmO*4WS$XADRYF)TC0`M%w(PRRhFhwtNeyYP()yD z21rtYHOyd@2$k}z&74Dx9-uQ|eU{c!Hmhm&S*Mg(P34YiMIvDKjIln-45-5v$jLV7 z(6IDCdP$bXZQ<*Hc(S*r-~?ewddfjjxcTC@zH?~LlZPD#$Ii@;&%$)mgcUJ3PznGe zA42d@31dvTP;|hpxI;o{ty_&49QnQ)QywKARa zJP#3Dt(F#2mj{OX@YLSNfAYg0ee;{&eCnYGhs)7>-g()^RYMnEa$~Ua(nc`WR61Fh zR*asss7uz8bunnk7L`&~vcIV3l1BziX!HIkTEtKgiO9KbZkz+aIl_n7WpLziU+##B z(K1l3TLA#%WIib&$B!40+-?o-ESS@Mx-d1fal^X3`}TM! zJ0_J%k+GRI<#O3MH!(30hT*1-TV`ix5itmLqt+@#aBjiN zsfpkJy}uNKO1*u@P9J~h;h&s;@r^U{ldW3z@PkjyPEMw^>9tijZ#3S%9(V1iZr)O; ztf)ZH3sykKxKy}=5s5_R1czP#giI1u2tsp~F{9yFVkCf_YoSr>fLQ>d^w3!$g^@y- z%*Ab1fs;K+EMmnH_gU3@IN6+&iqQWUJ~5P{Z`AOA*H1fhZT<~76r>qJbe;>{=lJ``Syd4&7U}) zG+bK*Js!$JMU+ZjQHY{ICd{UksxxNxecC zCkZrhCCpa58jw9)a3kz>+~p68K}txj7Bi70_McPq;V053_sE^CwPF6c#9 zvh=%HB!J|-N{enKM+v%P!}8Vd2ppjyX68@}jVzE=PX5nvbpS^|AS|*ZP=Z9xD1$Dy z#evIx3b1gCC2wX{62XyDN(f<$ae#SvV=T1O#FM;guu`prg5WRy=+Az5+jrZw*@A=} z8!K;r)wyG1TjaT~FZOOZl|tg?3+#l+gb1wiQCOEXqaft0E4h$^%S=dsmMQ;~rSLds z8K}HFVfjr1$g6{j(E&3EMs$`9F%#whdyec%kuP7$6DTPq;@RO%OVH0^<5oBM6pP!z zS#eBWE?&V%3=&x6ag%b66Pp&xrD`?mIQY^hKQei6-_b|z1Fmmbz5S9Gz2bu(`sl{> z>!L6`a^#RP##+nRB}4>4kYyQJ*Wce??x{TS_?}8{kBd!I3aY)uih|zAd+4d-8+Km4 zX5FUGf99Y1%H==$^FJ%}4D^&%ojE=Iz@v{Pdd2FVAfWM*eSA>u-MziIW_4BcmLLo% z_FRk@CkoJzL}!$y%-0^289G*4fuT)230i1MsS{)lg>_a57I`5yHf@PTt9c}!|D&LnxF z5cmKcQwP7S(6t-RXGFA_>KJqagkw)jleUYYjss;_o_O%1Y*cKU;#1o zg!FAf9_2Iz$H zG9xqy#~nbQ*d|G*TrMG`$boZhwvL>Z!m$I3ZOZ|nSM2F`F0+{jJwBU$X4jgIiQAQE z-WTLdtm+mA#GI(y``z1L{;s!V$My={?U-X`av%|jNNZ$fYY9LYW1>RXX}6<7fvsa$ zf>*m006+p3Qu@rs*c8|Upe)M}bfHl0G$!g?<({&i%w;iE3euT2*oYyEdVGsEhy(;c zNjx<%(>POm^nm&156?_C{YI;8ZN_AS$RF(QkIF4ymXTG#19GUf7Q*wD_9E<6|3Si){U6nGq~*k~s}%376>}U>DI` z?oK;%vRl~|NFo;|LFht`lt=M^0ud>PV{#z@Ktdt{kq>KyZcTZl1H@_e)bS||qOY&-$M@eie(b>ieB;Z1`6quvB(6B`=vBLacEb(V zoqyitX?fc*e;^J>rVZg@)W&iEyyS2Z;TZ!!0046pKE(XySZ9G*2q6Jc;PO%zELPBv zK}ZmgGxzf}u>02CJH9MP;b-+OF$g&8ak@Dv}bmcbx`)?lqf#Y6&t z!mQzB?a2I5Lw&=eAN|o!RAwK)_wMiA_JjVx0c&j-h9^&-3jKhsYc}hF4w!jlWTe?_ z%+AlXI_*MOXgAx%o`N(nDs%snhc3A0mHngV{{A0-Y;0_FE|p^ivpf6QuJ)0Tjtmda z=yO+b&ln~pFa!-SvmGZURSwr0x7_X422qzf+($7&m)MzS!T@+oz%`+nSpSg z^PARKUpb!xsU+D(nzD0g$=5=K&J$T0$&jSz4GS7%$r?iG`B^=kY@a^#`0%=QG=F@> zpzqqTsgxA@X+?(x+c`Erd(33XOgGjha*J#&DHSr1vqY?fc1%i%x!Kv?-d>X>KuB!6 z32#1FSacycOKk+rB!tX}1%uE&r&&!GdghND3WJNZ6Qx4SiQdxS7>Fha&Sq!ZGx46u z_<_g9pFUz9Khm_u2hV0;OBGx*R0M-cC=zYwTAV}~WdNpcplZAXNb111Nk?(XQP5v4 z1zwgU2`Fr$=rH{W6Q?MUl}NIVjT?a~1X1D1eNPP!t>`P4v=W@9Ah4apS(dTZ zfs~erEDJ^!+QbR_9x-|-Jqe%ySojuDD$9iTotj_fR?d=B1TJHpbeFea%2~GEb-xJl zcDeb>1zB?LV7D~FkdHvj*(h2nBo@d^Q;U{NPB4&=lJoIMV1V)_KP1d6cf8bm%r2jG zlF`{jjKv~n5ryTp!h$6iXcWe=rwpZ`GtJa_vL~#BYx?{L?z#Pedmp&v%inn7iH8Tv z;VZ7+eDez~xor3KrgwF%cY9p~)S3t|u$*64mv>KRuW=cBZSgKS=af=XfZ!YjXC`#c zfE%(ZZf{1cQsTOI#1_228xxVXX-co@;inr#XG3@ix-!yG!Bpfk+Q z^CbZjnx;YE6A_{y2TG~TBsqYv zbmUUH?tyGkN^R(2~rjC$75egxMl^L)tx~SAsi3Frlwb~1+faE-r2oJHZ;Q3PP zKsK_hV`Qs7k4lDN=+cacP(q;?#O;>SQM(m8z$l8+G#xkOWWcyBU$EFmI|Cgvaop8cPwW5^ekmVL|i1r z0ZIYH>`1CbQamDC7U*!1#fytG#w4HvCm94tVz$U2P@)iyg=7MD47}K?Cx9iO#T>is z%1-4-s+7`z^Vlaa0*fAqvXd#27jg+Lc> z9Q^gyUA$)f&S=vs#K?wPJI=B!GiK49G2~sv)a`y+oUF81mmUe$m>Qry1D@7S4bU5nIq28W=ph=kX8!&hLnV_w>HKz2#D7jbxT6RUvH6*S2gs zr_$GZ6XWsUr|Z`)wOa~hBUI{pS+!VFvaPy14F}Q~rvwuQrCNQey8em- zr?VC9drCw7lyT-n&_hQboBYWS54`hFuS=#DJkPVv;Uf60mF!X&nVlmkWtL@86j^dY zAal3RL!ioc7wrihX^Mj+wuTa=nE<>9{GuXfnQGFi@uLeryQ}fo$(iF%%^ANi)2xAv z7-gZT{Q@&`mNJ589B1s<1w_Eo88T96Un)V4WXQsLpc+}+Ur@sHtGcg~ET|$o;}MZ1 zmu4Upgps671n3N#FbX^tjYcyl^~9Oulu2b0qytPdBR$Pbf}DpE*dR(5D6mEn315R1 z2?{~36=#k?FqH}(uqpbXNr@1ol*%nukP!j7n`Iy(Q{FU{_X_5vP_{XZ3j`5^$a`W4 zKoIj;Djg#+IdVb>6a={^=$4kcp^Xp(q=;DLkgUwx^T;7cl7L__M{Bz!BFHy=K*4N@ z7z80YkOp9BQUP$cI>QK-oEDPN5f~-s9;Vj?8&J6NgB|CBMvMj zC_Eu88Us@2xte5V3kC@QJVq;XhszSXvp$%Sh-4RP&4q!x*TDc9g>{akL)z`l7PG%mQ(X2BLQ0^0tRpbBr*yH$BGH0&<+uRPzk{-0gaHX4FCaC#!6ZhIP_3W zG-ivju0%l)d8TPRKlD7W*=f&NJal5J^T6Yn)cg8^zUJJYziR!)wWSrMu&-K_o-;ht zx8aJG)mfWW(yU-qD6d3JJUF8@I+l{qVdP^0Y{sOO&`HvgC=Im`wX-#eB`FGsiI!lk zN(^bmByF74eXqkE)j%7CMZa2Iut|G1Jv?1iDJ@JbjPwmI96MZ0Ti0B5k?gfqu^?qX zA~{+}9Sc&135Bu-QFyFGyS-2m#PSJuVt+ikp+Jg^6}ri?C<{d^=MeflJA+<9IzW+G zKroSUsCAN>OqKe5k`A~On3!wQX57)K1XT_K<(r1iv;t@?)M)k$mWE0zic?Y8G;)

9_b?ETIQzu*Xgen6iSuTXAR+Ls6;j+v+D;Db3O5Mqf5-b^F7Kla& z;W8ouDJdfZ0AQNsV#Qb|QPr)qNs@*0#V{}lWLcJ(1QCKLY&2V~SSl~D9wZ6X;ur<7 z!$K$XWM&*%(us=$DLW?SjH^)zeGfCksL7L@8FUV`5)OeW6r4EfFKIN)zHeOysaH#Z zob?^Bg1pu6Y>d5BV(3XvfUC?%dM0y!dOLF%hxM zxo`?aUT4ldf?j<2^4MqT_ilAI9|f?8WiIXHXR{K3oMO<0ne&lOi#=V7wep;_=A3hY z#8Rvr87OM8P@Db7zxu0x|JXlE0oUw4Z`+`J zR1Ew;QxF=di$+W7M~|>SiwPpg+;q)g#dl6Yq_CpZl1Av&67#AjCS+Z2xw@ zvK9zcv)Z;(ojw$%Ti99tw?F$yb?5NzXI}bq|L8kf$(4=Oll#7GM^5rnqrP1!CxJa$ zs!MEV>=>aoVk3*!=)KWK>1G(AyK9T?SHJ7m{=+Z+@rO^h{9%b?b=O?`%;n$t&4WMt z&%Um?@tjq;P4aPw8S0dxgIY?yGzq%4AQ2TWEv>YPJx^>mpYA~^<^G-h{&HfvaU-)v z^R22L?wykH_ORUl>7To~c~BjWlbC3m(-P6ziKTw7$4#|&I1EwEvUn=*6LIMix3t!4qO zHmPBSI&g!EMpaC6D-dIhU7aRL;=RuR1G!w!14W{jkYp(YU_hPc<*Y{45b8QqZJQd% zJ&F%iJ8ew`U10q9&Ar8T8SXRRL}dPEmfR6MU zkw^pL-0*zPpN%mpG(OY>VYS;?TWUZ1ho8Ru-19&BV?TQ3waaHV=+Axa+fJW6@s%I= z`h4Rpo4vH1`t- zIWD$3fQa*82RM~$5)f9*z!9B} ziov1=Mnu)PnP7F{;+g;Td;UMa_DdhKsL~`kdt&*CGs|D~o;RL7-D{_gIo7jgR`R55 zqMv3wv*>%>m>8QG-N`K|YPWhUF-a8}D_|k2)MRBdCG=^MIM+nRD3dfJh#e7(G9@vt z)laaoe|`~BrUHjOWGRY3m$D*{D=F^WmD zz0qVmt34t+HlA3UMz2{KAqn1io){^tfnKT`S1P46DX|Jz1Slm!1(AJ* z8Wq(cT5U>>4vjY0V!dBFNHh$FThsC8{?O(YKYnxXeknO#{~WPD;}Mj5bfN#5;oJNG zD*%wA#n8L}fd~>ESBaz5(h+4y7)eMLPT~w2-~s;F>nSr{=8EbgV$%Euf#;f|2Ojo) zk;VM;nUAbw=JZ@eaKw25n9Bu>(!2dzhjI+;xeSs+IZMx`Y{nj;tvwSkanM8rm-L@*2$q9&yk zX#|A(h1nc=<%$R&1-QUsF;5}R^-yz9EHI?P97KAcREsREv5*J=1wkQcMIb&LfXL%u$=sC9m3Gd`h`hW0B0)MYDZVKX%fXRi4gbdEk*+ z+C-g}P`2wV=1xO{UYt&vAa?zn8=y)C1@|4?j^m-xOM5~8_J8~Bul?31JMELI8Je|q zh&7)`4u0YGul>$%e%Ign?&jt_Fi8s~3J2X%M8n}BNuITkA=*SznSiMvVJGJH<*jSi z!+-yr+}PyXo1<&{lb$WsGF&=stV~v0>l>@RhaWzxl_J7duim_Rd$)`}23tZxL{*&F z!X(k&y1f&Pib^$+gZGIxz6p^Lvsne%dFMP8+^sYMr8s}|QD_zIAkDS&@DH0*!NFa>B zW zXoW3u-6+)nw*V2yHnGW6Q+H39=*;0=cKvaytthF=?T$6J%}Oc1dNMuLfx$M6Cgq{r ze17Ytd)Kafe)s2p{pOdwBmdDK|I@SWEzn8y*1J$BkE9R7Y^|$Fd#rbdyYYovp>*c< zmVDyb$+I^Oc88;-Zjupy<-0C)QtmIGRtm$cJahK!m210~@7&%Q9SU`gkvE(_&%xc= z9!y;g412R_9b!9cO}>G)EuLXF*@f&L@AYJsZuIY zT12cS=CIuF5rRgFF=}mm8C3XKH%Dk|c?1 zrdr$iW|t8nGl6bG46#mpIQQtugd3ss&;7|i`8)sl=Nk&|d;9rwCwqVSkH5cv>dgMx z50w3f(MOJZL2O$%gUCb;nmC^&ktIfjNb@Dpd;}r{Av!ivnD50H=aNNupoOF(?V?B| zWsdjG$CMU?IYhyVXs+A!F$7^C#2AZBSxm!5+moAp=r_V*=%0h?sNY_Gm{U~S%Ac6B}Mc6tG38m(4n6GRqcGLGVW<2YJs zH>%VU63HXdXtKM!@fEthKJ#}t_LP-eEe~}}y=PLf9@ElO>E0E;d?t)uQRKmU=1?Nt3*2`&m=h^2 zx12e1=9#y?@f;3As&?#l!bK@ zBas~ACm@W+&jpDVWRv@2CL%2`uemCMkLn8o#5rY4c<#(hTB#&8)|x^PtZ!hlKOWq> zw}0c()|HnJwr+J=5dadajgmYg3^n=+y$7nmHwJl9j{5z+0%eRDOvf#2Aho`6S}OJ$ZH45E{wsb{Ll(e!SUs5?QFc&TzISWdiZ-kKiDcaV|;$He*5N*I=J08>F<8-3zzSXz5o#ggh3H}Elmte8*pT7%uO^Qs?#yd!POI=Eq(gK6|^YxfRCKiIP*R$x6p`T4|;f zM^9)g&tNH+rT!A6R!{bvDP-2vZ+z@cH}75l!a28q8YLns@3_b*wHdM3Sg$Ux5QU-hDQJgSj@_(@hh?b(*Aml> z^A|2W^5`?qeA)ZH=9#D8gbEq}5g8A|!D!kvGXgM_JC13dsEDY*N5PNZ8q6C3Qpc{W zbD|mmz+$X8t|muck3C96@JJ5F$NxJoH}kIubH9=!Nz2g(tU3ymjv&JKGcn|dYI-~- zEd1ynBt!$u%>fiG1kLlK#eAQt2S^CmXp4su=A?jcpoXO_O7(j_1(|D_L|FXr>lcq_u)VI z<Zan zkh}YCkDGQ%r@NV?R21#tC*X`qEQMHlF%SaP5DcNW?JPR=$ok6pvrCunZ10a|hkN@$ z_s*T@D|UBwC);JY(pp<>sX<+Bji;Vtik6X38;-I!8OIoxv_HL+oGv8c#waUF`{-<< zd=r&TY7e6(R7|C(NfVrxL1V|WvI?$fw-sV$be<-@aVzaCgpg#mswxhVNNZ9})AU!` zi4G=aR+~vR*&O)^!|lD@hq{Fg!Ap~)&WobGzHwr0y|vW)`~U7gc|^}J8i&2%IC!rZ zoOm>8TOn%-%vu2p#)jEMo?Fd)b4t<_vDdVI^rP0It2^8Gam5dkwE z8(i~!KU$B+o-#*ktob>g)9~l5g4{1{^XKOmVL%7~5;+7`?R2f*Su*hB|NduwMY?rD>506Y;szK{p1q zgeY1s`a_h6tRs%WDY9*)eJBxnWJJL^tzp=DF5VWrAQ-gTbJ&UV?5Ku^1c%XyQ1{GtH^7Kt?Az z07zBOIK-uHj{u9z_~5eEvNb{weHC)s?YE0gS3r%KgVr4tE0y#^L^0lnsftM(RSh!+ z5+T7T;L#gKtDp&hIt|7rY+4eO$a2zS?jku8J=0|t#n%A zN~@vWPHx=T8PD9|*x%Y5x0aSf(bcP)XU?pO#MG*+m1(W(+J(AFv=NFva`bFbMd6?j zBHF0BE86+cQ~5+HAD#Hzk$&dn@@u^L{9X|HsX%J=gA z`chjf*97l@HB!&C$g}54x6EMXo0=$ilO#_2ovrPuBU?5i^0jY%=utVT;E@C%tb`L zm@m6H{I4HP9!xisBUZaCkW;vCm4b+fF)n}@S_FeE{`iQ+F2`kMzWV?G5>^EBva$e- z7gW8)f^rjze_%lY>_eZDyHZ@t;EL)OG&!P;cS|wxdjl< zt_dPd&2_3xr?3&CF-C(V#!hD=r3&ZE>2#bXHq+WDeB|OoB2g)WNLi9(saONRo)l{Z z7(^>-!qj&Al{!n4y|mdCogxK~EyE21YQcy>Fw{PzS}^UzX+$$ab$X`E?DDnQ_Lcnc zuUbiV%4;1R%ByP8v$ z+tV9qj-hLN%bm1)s?&`sO`+dzSEZ|}W-u9V?(R>gk%^qs6$hPwO%o0R96PPFaoTE{ zPKJ}oD9LhbQloWr9uzH9vLqg35C&8LdfNC-URZ@z84h@|pG%nCnoKr2Cq$~-cSc*j z>h+R~y{<~lwJW0vT@-hEY5B%pRfX!^XZV?RGJ-gsRCxkBDefN)S694{J$EjG@dYRWlBL>M>v+dozyvRvA?;j~UHxVqUn8b3En>oq7F>kzAz@n;_G zfTCKY&AJXTLTH(k8*&MYs;J4l_nf9_1B0e+W7B@Els2r4&NGdoVuOTHAOtT}=~V_zHEm~Unx=taR@PaTfe5Mq4j6R}A|&XY z24Uu%5>}D~v~QgoPlzvj`0pL%1^U6r4fv=0(zcWylQ@sHp6+@0%#LAyv39|yry(*qTa zk70j2gFN4!Gy$?{RZk{OXYJ(8&AWqrS!#v#Gs~4D?O|mlcfnyyMI!*baDD41zVExg z=}o8ac!SwWy>qWAue~;GPMtb+p{YK5b?3QhuG4t=^7fZ~|DXTXZ}`TSZc$L9f=wV5 zq|}lep%$;372pW{z(2gXHz&|9j^z=&dJLc*I||X~9*kw+-yFZaF+OwnU z9D_ahx*~X=Vl(feLXrV%YXde z|JyGuH}HY4eezA`m;T6?e0A^9XZDxh7LpSnP|oi3R1(2N6(MQ?%^;e*j@}EfO@cU^ z7YQXYdppk!FJGIDcbaNnW0@qFB*rSu%n%tlUcLItqmMlt*fTSs;Sd$F za8L?;a3aDY-m?NL#-n(v6E~f<3lyCIwP%BgoB8O23)R7-R7skp*veY{)ox*?2%(iH zMsuFpEHzq5k)@UpL}{&Uq7>>Fh=`2|5hX#acE{akmVD!W>L2~LzxX%4%QXXWE;Y$a zoiI?>(nuAW*jQi>*0z??KI7rUPET%0@xlN8%I3sYPHs`-+lEf&S&kIOSShc z-~Z@^cRo$A4^;xvFw}&&z zTL2KW0!d8mOf9fC84hMq`pJ zZFvFJ;B;p@o!%MW+q!$_(qQ*)qQ%R&XEUV`wZ!P1b0}aCh$FU=>EVz9ulAP8YRV)5 z0;7;90@MltqEbpJ#LgN7c!2>+<2Lhq}*S*}JzF zOHF&zxY6q@_jMWt6vY@r1ZMUethIGr_mjMKRTD%|(1fb?2-a9)Aw{SdobwD61)_wa zC|U%ASu?6U3q=uB03w#r8nS635_=kY?cTNFEP`!`56o|T<5{U?Yc$!N&f>W2bj;qU zo;AL9*?Z5Fr}HA|-TB@h{9{>Y^ROngsN2kmSW6c9aKKdwtyHPPuJ!%?tH1q=pZe&t zdH=L^v%S4Z07|Fh(ls26Ijo~I#Y>lN|E2H!{vY~3f8<~OtAFvM|NIBP;oa~4_=~S> zPU3~7wQgTKma?vblkHjgcfRfEH}}WAH?*Xw&OY+Z-}=L!|LjM8t<_)tvakHwH}*~1 zL+(Cw>9halnWx^e@%F!Z{_Rg6Z13C5&U4H~!53ZTTn&%C6kzehSA1Lsp0-KW(7B1VU3 z0SHahH3y{Ng;PjbNmY~Pn$WJSb{^`|%lG#F&wufcuf2Bn#vlIbxt@II*MG^o-+iWY z@ypu}f5SxD_mGTHiK1MYQ1f)}uG_xaY+cLtZ)@7K1zoxxl{Gd=8Y~fl7s42n1QeFJ zb{vrM0HHu$zn}Yq?VtIvuYKmRR@fsraT2$Os+g6-r_|o7^-1;87w-I* z58ml@{jYrH(xs90hfM1yjPXyd1^`wlBWux zR+5+1$e4^Mjobu@95Dn4Dp*AvqA`i{v25J@ebvg_4MgTpC>^3}kZUi%s+)`Ew&+H& zt8`~lkKb|TM73W}OgcQ=&3c(eaw@xfFgcayM!8ATjO+559$r`){k4Df4!GUpS;N^< znNY5K#A(zRk+c|{79;fO!Cj7K8n>5E(sRFa`M>_kK6J9&!$5fI$7Ms6Lnm(1(DoRg ze8)5Y^zZywRqlV~H-1$c&Dnn6^fUI5byfhxMyzJ_)y*5<_h;WN*KfQ^>sxmx=g)6^ z@OM5w9E>*}Ih&~dH@y3?NnQG-wfC&>AN|U6|KSt#ZQu9z4tEd0hm&P_n(Wo16x5GbCTJTBI^_i2kX!V?69NFConB>A7zk@?th)Y>cp z$XGA}9_4ZY02t(BlI{_wRvv63j(B1aqQn@rIS$a6t7eYqDnxTbw#9gL)T#wO0(TgX z?PK9+g#`2G`~aFepS>Q0oaUP*4d^3@QBY1XF%nfHG1E{>%l)Obllq%3bgy1+TIbgjJo&Nn^znHR2( zhx>EVs<4WHb!f<1&&I{lQcEip>XC>joz|gN3MELV)2hqH1rKDMbFC~@N(sc;1s{VV zu!!^6X=a!?Nm3shNKM-$p+M?oEoi}DuN*fr814nM-89w2+p|~{Sun8X{6jeXTmS0p zO6XNi*~#? zyJoas-D)TMd*iS9z?Xo^f9p5@aO!4%^-sM2kAB;qp(Xp5zU%wj)$aHG^v`_tw|@KH z)h$kQ3l!Kt2yY%sYT`ms3;5}~17>E3usBpG0t9Lvi#+cY?!yxg?0pvP48mNYW7Pe| zWZv7%8-d8LBf`NkMuWI0E24N2ULk;-p(uq54an@F{ZW965PKF;_q93nK*MtER}dv0)h}S?D6A0zL8_7}`aCcj z6XrBK1==-HSSzd03{i7b6_}?UG98RJUb*(+pZfdndGf-EM^3CElsh-}2X22F2i_|k zPV};+JT=A`sj)~u`l)+9!GK~Mq$;hbik=ikA^{9BMiQVX;)7~s!nn1)m$v$MxAq5z zQxJXep^Lxz@lQN);q03pee~*USF*H~CuvcrNwhDXh>_y|YUwL)swZZIf ze$V;I{@~hc2EjjiE`9$WJ9G2y@I${op82php5D0Y#wDIvO|h$20NE-V%l%o=&{)@G zEO;`&k;TtSuVT#Fy+xqITsQ?ZolX(a7={0l*+>OMUq7s4$uck-!JZCbJvacovNxOx?=5 z3Zu<`^22wk#43xknbe-j*o+O^gpM3Jd#dBviR&Lzc|McfS|7BnO zW&iTu{+IJ-^gsH)e)h5Vyl-cFGpDwwnG>z=`*?uJT=I${66SHCpS*MUMF;*tNOg{z z@&mrvb0ZtZ1&^PAk6c>th*`gYOyP-%n`y& z$HNz*Vi6ZPG9#)~9smAFTQJ8}1M_`)E$|WjpW|EzjDkRb8W2GgGm~Wqf=5nw^H&E1 z)f{G43uhQ+1YwAcB0aa`WMB{hk_a3lrc7waw1dWm06k@+QC?nOciV}(zT^(M?P-0P zM|5y^XMele3mgJh)f5F+`$;>|NfH%OYE{Yj2Vc1ErInLrIS|Z^g$OYrTbGqFNfSIX zS5>8z@?IS_I6Iu}9h7Tp)b8c$y`&knZf{?D`mImzT)UO8uGS;BvbH*#>`o_po&E}h z=$nw{nFmi8vGyxPZ$@0jCLvHULPAPm!V+Smb-i2^lwpEKWw9H&4hP-t=}s#%Cp75} z=U_%Se!hHVZ}T&sybP6lWw7(%&y8=_(SlDj>|vH!SxK|ET`6FNcZEMTtvwrrWfAddo z-X4uQ^&2icHJvnNoLQZ#I^5pg{_9_I?u~DF%7xhrcg8>WvF9P9JJ+wCK5_a@Z+KJ9 zVP&c9X5)`N_uQMl>W}_^fAq&+xUrR*#0Ust6fq0ZDGOs12}~>qq5vEb>Em4CFS;xy zg#~|g@tgS%GT-dX2b=|+?Ed=s{*`%7I8kzh(HyrQNEG3SlZJD%d?p~^g;4Mq<2bt2 z2Ubi+kcU=^2xQ^>!i31jcrwp%mSZ<>kYi9*D}{pd+y@{aoQE5VuMiBe0bl_K076R0 zC{Y0c5VS-j?==e(Mo1Z%LyR#Jh|*+Ll9(hllNe}1es?b!U0)qu+VJ;!*%UG#l1M5_ zq`@v9+&uI%tht^=UZ%pcOgtxy9Hb6|_&3N9Z>pOos;zV>PQ%n-Tfx ztWL8+t9&-Cbb;s2c4x}#@ubx*PA@e>NidC%o;Wqy-A)S%Jl)t>IvCtZ4KFRFVLDTZ z(k-pQn~0z@-|ZX}nQ?VtlTc5&X~?ldVXeq>ckQuMC0NMm!Y`eDiwU=VYaM)dvNL+| z5AIA~o&Leg_da@O|JfV6H7F8_5D{pt-+p+hDQr4&cqYckTG zCfFi#(Ia4%iaNX(dKT>bh=s`ZF%4)3OEKSru8DUSVxvCgWOjb~rh-?X(YazPoOV zc-YJewCB2-l$=}6+qQjgZ@00n(cbO>%BP;&`tnEdvB#epZB4U$0~|B&&qA669fC8e zGp(jBc$?;4CP=27PP(o3!NH!fP)#ah%=YFi&5Ea=RG+*eX&!5=5?b=JVP0%xwmZ0X zYh~%g#))&=o<4v3)vx`!_fLj{PH!z!X;t}ZS|ur;yKu&pv)}s2C%0&Mx!W0+lgFNX zYX9EO=Hbpt-oClBHQU)e`_6a$sqgzkwO@#EhMf`A|b!Lg;REJ*nRfXBqy1$r_+?ZTX!2O|SURD;*MUe<2M4B6WM~EUI63J)= zlmLRFQY2D{irKMGg^0)zL6rD5bt7Qa%$6ZEo@TBx0!`y9!dc_JcP!45Dy+)UEu~hIVF{wE*kEX6_i$Q=NtDiT zFgdx>;TZFz=YvbMI(f1`D@Ta3yrOGAsjp3Y=T7ElmvMWb%dDkLWu{h%Q`qLLBpyt* z@;qx?$f`sV$IMjnEMn4IMU#qmalf?7d912VXSuesdOTg;SnaK^^z(K$+N~yezq|DE z7hZVwKYy@l{FR-pTQ_zt?UggZS_c&%0-RY>>z)3|M0D0Vv0k(lDS)Mn_U|5^Jh%3x z?>PJLGf%W03XmumL388Q=C!S#{>59r_|cn}cZNz^o%E6{to7SZzw=b^#LVofD5`dD zH1Xbhg+vHxnkK-$tlLG_L^r7&lL7{Vx@8Rr`^b$8o@!x2t$YkEjk)F+q9S6HS?Py| zCF!J>K&wcMF#y0(d=m%}H3VNOlI3n^Je%zg_R_p%HCEMBY27XI;HyrS`G(MO(bBqY zq6cmlnLw-6T1i7`^ol|vy*1V<_+Rj#s$4C}lMg+fnPE_;H%@JWMqd(yJyy97 zGozD3aU@VrucyXVZW=)&b)1`s(c{Xy=%VMQn)#@zl3)Vwc|5;*d20wgj#CUJqH?2M zec_=jh^dF0_jdlsw|&=*@xathnPggyl6FxjJJad;cDj_cMqEnT{=-XGm$h17+Zect z62sArq}sZAcZB>aKlPsuM)m$+lBz@#x#+c_5a%Sd*L5Ta3`m4b2>Od!jAIbw0W{>O z)+{=R#k_+HR?Fhl{=bO?7qtmR6V2x-L_lCoc)!1q`z7oMA_XLwFLv?BXB!cjaL&uc zg;o6`Ztjbo#TQ}Jiy@8>0Bm4D5hBneLW_n8AqgM~qFSVaS%@Y&f+nF((2Yaq!9?ze z&Hw-19@inRl~6 zEl`Xjhq6YCiPA-)GS#Z<(WSi^S|FP+!|L+V{_N&(+TA#L=68SZ^ADXnyS~x#ZUPXa zXOPC#HN;R9MO9V(_WG@IZ|9{;Z+Z7y_jwDllwFzo7!o0`r!H`&4QL)#kao&W--MZq zeg;k6Z+oqIGULJiN=g|l;cV7TuzT@Q4(JE|))%%ueCPS+FWvp*i+kJ6Fh&B!BrW7= z*Ffh?_QvH?OKDgs`b$LWgkI?`b$U8&%8M7)TUpXQwQ=%P1&0mjC12v5JCk4fg=c^3 z_g~o?)T4@m)(zS-53RiYOjoNcIM1=FYaHzD815O13RGf9>*zwr^VCHsU33j==FzN5 zLB|+1m;|tnzUI2Fqc$0{a3rmwv9VJW94W-wM`kk4hcr!#!V;8;HKaKN@Qx$v;A4`e z#>%vsSZgK5X;W!qy4`l=d}KD(#2AQlmSt5v%TgVKZyHW)25J@rOz1e~I|8$PABNL$ zJ%q@4$9~Oseajo){eedxT0is1MHB50{I$Qff8D*~(X`r|P8*wRr4cmPNp)yZHNMI6 zc3szL)}2nv#FW1Fd>Qm zCw8;Wd%ol=-}^_t;xB#mS6_U`Q;}6tvK0ArcJS^ePMJ=Bw!4FgdF0eeg32Vx&TBXO z{qA@?T7IiHNWhGQuwZ-Wqsiv}AzWN- zvBZ(%t?vBr(gTFDW9)+$cB>DRlcUCGp3Z?sW-E)uGtGVZ7gwGe-p)fV365=2j&5sF zN{%n{pf6O2gcMnhuRAY`3s&!gwk!+!93ocZOxTGp;5z048f)wcN=%x!5etl$cC6sv-_)Oqv>p zp>}1}I2{644XJc#XK%b+9(EF$?lxuVyl+xlTzqJa$gcI;LYPZpx&F#`J^te5s^R)u zzWJ*GV7PNhfnsKBb%;SL?6=z@k|&7+zIbuz$AA7451m~#Ydsv)Mh75drxT${GmJ_$ z#6{+!^oo?IMZWB6Xlc^U95(csm-pWLRbR5X89w{k?r>6pzW(`3S8i_ag0KSc0ESez zj~CkQR=2g1(us3Vx0TKKpr53+!1bbtOX}>Iq zz|qwaigsszIO?{$9GC&R?acYg=%Vp5n^kFIR{FWCE9V@5GL{eoM0tlzUFE56wF~F! z*$ixMr`6P&l&qRmrH$5k+Mkt{l$wAP}9)v~A?RQOW#> z&1JNN1OWPrb~ML_@QbjLMM1$MECc>uFUKT!Txf_FMF0O0e8C59XTHW>RG;HP2k!ST z^T)4HU>+6p0F;ZbZ&&6&#`}}q{Of{Q7Z(}w3s6=>JhDjCY*5fzWl}>nVa8a7Fbm1x z(kgE+$7@Bnhi>L;-f!?Q#Jy=TnMEHkZ<|hH5(=baB$QAEq8K!Z78L{#1#}FRZjGyI zcQCxQJGymkH!~@VOKAG^KdBfF#?HN{fx(6Fs}WHXhNv)1x5EuCtdUS40@ z{ld%V-}59^;%5yL{_@8^{&T zDJD8*X4vg=uRF<4E}!b8C(oZMO_jC!h`qg!zk1`BZdVR}^YfdZyFNbHY3fw@UZ-#1 z^o9OoovbdsF_wf4CQao~ftUc4iJwq30M2_6-O`<@1T!mVjxk2-g;F4Hd`m+sF_Z$b zoa}X-moj+K*3HUJo}l-cR%tJpPG-YFwAM1J7(E6s+WM+YOyXD?FHzCd0eGoKbwoZ$zx;RLbD#DPJw6`oM>m-q&X$+Y0O82)Mf)_99-3?brl3=#orkN|oVxFqt$7+WSV3m?AqGTkjLkA_NmO*ossKCd# z)z-Zm`@^QL9VN?I|NKUZC(oRs7PowrCZc890_VnKZJ>-+l&(<)(2^xZKZ#Wv%9x1H zFjhiIYMpc5M0FfeD8%9uGSGY_qPB5#i(k~v2bd=d*H+9ndfQ&WkVID*=`ohMBEb`~9^imhyxp zMBu!V@?bw?nVVE)4Wv{jOQJ>_#26QV`a3v4R9!JxkLoHx5l#|=sC>}kDa6M6{YENC zTvMi68Dt{rx3kFJrWS%r(_%E8Xsd0SM9_{d6hf${4mc{C1IT37 zhya3CL=n7zOF=0^A_0N3EFq$*s*EvHuBSCNlN!0U32vM{V{NKpe+Bh3kDR-*eSP!Z zJ_Oh&`RsrFFHdCJ9XzIdFj>2|R*Z*x*>d;RjROiiWOH{XJCTIOg5$aw4Et*^6r(_8dbX0XwJ;cc!&z#Vj?U1cQK}YB_A@*~lsJ2+-637-3esb8mjz z?f?2AoVrn%z4obRzwDd7>bw8?5B$d;`Jwl`^IiY-w}1E1C!ZJ(Cw8?pi4l{8fU2p} zTH7M+^t!+OYrpjmfBL5+z_1Q$iDkk%dInKsSR?>IqA=VCR*uotxQHIG?MuS=t00n?za!eJ}k#4dhVsj3s6@`03s95=`9R+tQ43}Oa#X~7d(;_fe@0& z+#eWtVGaifN-EXB1NjSNo_(z=p;_2le>G9$2!V$ZW_7*FH0YG!4qTs8A`Cqbpj7?l`EbksUJZt9A+qo3ZMw2JPi zECp4&;O(t@>^n?&uh z@#|~(tt(|jqNF?H3O&^9lO)v&A%wcBz4wCfC%)zFeZN@4mWI^j3zO zd#MHq+3&pa+H~{t&t1K~JDT1bm2+khMqMC2d9rhIDLJ{aa%#1mCVqW+x!-GfS83-H zfD*ljrUhg>absDXJGa)#NX2Bf-`u!<`Ndbtk9}eA+3Pn;&PuOljqh*tmd`EaEkp>~ z6+Q^g2E%FFD{i@_N>$!Y;#5*?T~tl*&CE<@1IoMI%-CkAeIAL#1Wxs=NigiEzAXR< zWVK-qojd^mB)3!)5xur5gutOjFl7S)^l&ufNJhvQ%yQu1kufs0w6H{56r<^ch=>$` zV!`OW(V9pRk!wO^KmQ!|M78%sBuTPgR!s;Ygw&+yL#NkUJ>NNhYSsDra8zeWo)P$H z4q!SP#OYuX4J^Ou?I-t!>CP@tbOty&?SnuwIM}**?Io9{Hf^?RsHiOWSFG{vHr%+@ z9Nw`zHQ(Mmn0Odff+h*EO0`1pF_6wo+9wHEmr+?7)B%hpF|#QJJvUbeNkFm8q? zI_s0sw8pU9EiOOz;+K5$H+Go z=%X|SH99o=Bwaz6+LCp&O2q(q`~dmybqQoY7G*bnIPod zWu5faH%{24B`(zqFW$Rx>E`)|mp^xN|1%%__nWgiLEJi+3b0^cL=ZW>oSp5q&YtSE z`rRi^EuCsJi%YsETB|EJU-&|yTy;1gBHcoh!V2nMZu=dR5)Fsp!@s`&@#nT*xH;Y$ zhjHBm4z0HBw0rGbEhlJH9()Lmn^cq<9SE2uQU(w-a!F0fww}zU-eEH8?#O@fOe zjzGHx>L!XHC{chj=h-=JEeVhaDPRp+fV~6KF~%&>O7yU-M?UJvnr8N!o~LteTEXX6|NL)6~>yhk8nT)>_i4ArUb2_{?C?EY_|MgG)`RU%5 zJ^kii{-Gc9&2D=y9e7^Nmt0!Z(<(Q~WdG2$T0i!mf9cBZNM#v=L(x`g@ggE($OFn; z8FRl+k>gKp1PUMkpy?>&>$o++dFi;%5P99@!LZK2_j zF{n6E5CFyjnL#7uBwmRKlVHlI5WS#Jf(S-0;1L)TRtb|4RH*=QpirgJt4Wn`lVb^C zoL8e#K+;vGQc{5B5C&I9cduSQ9J#Vepjj?_o@!buSQVvf8nOifbz_#P1dIfoNCbk~ zO+y2dD(+0{NkzAJW_5^#YK$q{Y2NGBkM)Y}3^t5Q^C(a*=|tM>%6$PdD0eD}At66I z^Z@{M#i+9^OE5MwU~1beW0xgK6~H7d13vghL)z5Qu{99t=vYyaw91)C+HM+O;LH>) zl_kraGZd5kUHsIucelqkCI>rQ4=*1MZjI_tMbAorTG}5ltgq6!_4Qt+A9?uH+R}0s z-CCPD)BUxRqkCJ-&U-Ak4~N5XD>X|UQ{-uGHH501#QhO{;xjv!?rk58^|hUY!z!ey zU1^(*4SUk20Z>JQ1jvFOf``bFNT-IH;1z@jsI6Iu#RV}*FvON3V0I9JB>_wj#ad*f z7=m{ZV`^-gX$?peH6=aM(UN_g+LK`WOIF z8x|>>T7fo1hFualNeqLy$O+jxDwTSJVKS(90*b{<{NpG3D(!UFS6{q*V{o|p<~Kau zOU?31w~1p303egmxYz3fz;rsb*34!zA~G6LbW$jU*gf^o=BGaO=#!6{a#SX*+^Sh! zw=i6O<%U*io>P(*BdlKCPRE1U7q(}{v1voc<&8P;J_~_av{AvR%AnTZSY-&_s)U2_ zL84-}XR0!k$@3aQHbH+|XH`AMxGIkENPvu8?)qT=AN#F!S{bZPkt#KZk7 z%hq1HwfW-Rdy*~ZO`Q7bY5A6Y3?E_Bz^9E+VWfFKj#EXoeEU;o?1g{)N zGSWsOZ-WXIgKp-2Qc|OEV2OakA|*&nOmu`Ia9-`Un!BmrE2@1hrIr!K0qL6kj12gu zV$Ro87iO`kWINMk$vR08JrAe$KkB8{V z?@!?1psH98#!aToTAsFw*egUS1&VEqv38FxE$uhkaTWoK1_N8tVr8oG*a(~2SckPs zDEOEdn-MilNzxRlMG8R~4|U7*6(3q|sl-=J>I3*_XVoD~@UA|6{^9-#SFYsVc60l*sI*m9`+ljGPB&lrl-iNP4w&nhk8^qWv$vb51(3oo;OY|>pO}9Bv(4#^>8)#c@ri0WCS~_;UEe>L zrmfa2j(w(6fS0y3Db-5LOt+%NDmv$Lgg{^|1aK_WsrY^k8WPNa{f!+cKLIk8Sft|P<}M5yc6dn)GF!GbRR2wW%J-h zRrRT=W>vq_zI@}>g}1)zE57;LuiU!RZnu35TBk8Kh=8aV76Hifq2<9dee}!wMMsMO z!@@6eeoX?tzeSe&DC|Ovcf8g)f^NTv?GlCIsMb6{NR#8GF$y3`gj{2-z=>!L-y7@Eq%f%fz-mF=PL5nEXj)d)jjDqNhf^uGW|WtlW>i2ZXU*Oy62WX5Ce?J> zl$In{eyIgTmihpN=qVdx`nk;Z>zOj#AudBCaDvGsvPx24kE_5gqGz2s=Oii(rPQEB zp>k06vy@N;XFQJ4blXeqD GX__eVrBG{jSnli&F5Q^Ebbavomp8BP>|egKGY&xr zKp+En!+LRItuNrL!B$c{@zBY$kDb>l?Etj9=E`fA3ebvRG#=UIqOm$qb7HLyhA7-ZLyFk3XV!ueImBQAJp?eB zN>uQXBn088Sqx14tPthrxuK+)rE;+ewbmxroX%6RB{G4w8c8`9o3d79rjvTsG@y-1 zY?fyIqF67WopxL!cenSp_x%3;U{u}>Mhy=~O{@{|@rTb;ZUDmF*0LfKeVygGbFOKc z`P*A-Gv7^e1ST{|0U}_M?(#|#!7?X;o>43a>RD)_QlM25AhvQI_gjYIaBHe`GGQM^ z#x6C{1o8@%R!*x%b0)P>Y8DVJXoo&hL{=Vz0mv%HSu3}NPE85-_IAGMFa8+=G#>7( zte<{0$tphuj^oj2dEBIyYBF(aI@#IE()?OE01e%4e{*|_tNrZUxu5y{-`g6^T9gS> zG!$G=4@3{beOm4RWK%3hDG~FsB8z~{MVA6|9nDbz!uz(6ud7UpA|eP6*jdZbzt>Gh zulti@(hJo-GKdyL6i|qs*lC{PvLFy+9-H;?;mYiGx4xSAZINA69*p-nPPG-4C}?SB zl(~65AXq=+JZ-yjBEgO0Y}gRwB%*Y`nE8K=3Rfats0_fjwD(zzC2k)QCFM zK?Kkvk^uTBmFG#*jLS(BG&V_<>UOhUr+YHhy;hMV2@w|y0_Qv^i)gPrXpk^AZ@g=8dw1% zRpM;c;+$dJkS3wvrVJQ!3?ph6hq0xGd);>Ormy_EGZ)^nwS8xGX_>Sxi44d=S+%`$ zyZiFcNkudrAG*4J)rY!B-un2HuU)-UPA5^|r+(=dw(DkcFio{Vqspe%I$^%=YW+Wq zK8p>>(f5EL3J3u7-24sa`nvgsc)?Sng%W4c-oRW}GY>yK`V&|{Ghb9Vjxs$KR&uWs zQ6Ci=5l2cvROl+*+E;Dsq36nt+3mvZ^qU)nzd>p*8pk&CdG66_&_+@vmqX{+0= z&`((!3K60%B{=6w&~>Faqy~JRX-bWgtY=LElD(((hyUOwdfn5$+u?2O&^fbwemHr) z;I*sIzuLHP`jPYAc3qM-GpI!$%x)VpOWo}3+0}mHbHizx>PUBn&8uJ7eD+SY^}_T2 z`7<{Qnq6^02}RB5VI|jJ{^Xep8{OretaLi9jFz(8CMbke?aF2*fZ|>B6{@towB%{| zr7ygCrZ1fnZBANz;Fj0g&9u39`(S&wY(U-FIXsxSNZIaq*3@2nP_3k$L6K85+Hu^i z*r3G7O$3jQF<7m&7m%PySx3y#LsSa2LZwuUo*CE+J2%gSNvyI7HK34%h=^%kO^l{A zNypW!g@zc*rcBe+1dI{LB$anoDWfPxX}oZNMt~zmhRQWjfB`gWLqH*ROy*;mrWprO zKruuC6(x?#>gE)a)Bs0;(COxnom$V+#8}lyT9JSV2$(|D$beE;!>%=6r8~3nTi<-) zkqhhhc1N#Xxjil2o1ZxO#2X&D{L1y!PD>mI0iyvFTa&oPN@($fsuORgeT9u?Jb{t zZGZII-S>Z!R@1DmLi;S(UN;VBKpI@Rcm0)($1cS2Fi-KuV7w1`$$Ez>8mAR7m`0O(h2{7_;;`n@Z zBu6aR`|}Y1%r{7QEb==lA`2@61bVROpwOaON?a3E*Xi5=cP59APA;!BFC}=5_y8!d zRgmQYTP#XMM4BYb(>!o)xH7*(28wZNO`f7M1j^ua@8J6N?cnsva_5B?u1AHfe%{Sp zFXe8cl$vU*$tf!|ob1=Wwl<3q%2>Qgp> z_U>W*+U1?CyStZe@4a|qXLED13tj|}0f{#HnK#*c`mN0J#giv{opuLD7uHt;PO=0v znOf?yo;mRXQR}?ZN{JvSLRLW=7<$R}FTSC5s^9DMy7%tx?~LJNpS!ZPH!DM^nSD$G zDy7nTQj#)h*BGF&n*|>y!TToG4r=AArbbK*wIRebMUOmoGe*#&lr$Q|Xhe#LlIEFW zl^Cn;YqABsVgLX<#+-g^=W0b&#;3g}sRIPDNh+j7`v{_}_8bUEYmEw! zB{CS&M3C8{NJN0D(WqGK)^$mTgGt^Cb<{X2gB(^o(G`9D}$SsBitFn&62P8oIb&2QSdd#7INl)iv}c0em|T8%@_R}6 zLewE9EdtvlE5HxbMGl&y5{N_$zz8TF*%Lq%#X{C}>#3hj97?f$X@7q>+8^%N1a9t{ z%XhB7_g!z*va{0B7C|XCX%Zxo;o7-|71<6MR7xwfMv()ykp(d{21<0^a}zy_BvfN) zL(~60UTiqn^Wtp&Ps1K zJUsJICrTtpXAMmoos?)&L$PAlxTawWCQnvYJHBB^tPX1TAne|$KfU|Pwe8)*y=g;? zpiO41(Zn$^O>_!?S}mB2XM=GgU}r%hph7h&XG$4WqcGTvBA1hLwpw&@orDPof!fyr zBNAz)5|dg~bpu5bU8ohtG||>tAH+8zLe@Bg~Jr!Zb;}_nt$R=EMn0v?}%00RR}Kl~t;fyBoK|xr@qGKv0*1+94xx+fHr` zB9$jqrd9RuiS>^@clpIz2NyOr-u6(IHI)oPnoS;k=gAXKJl$G*BuXpn+`aYLOJBHl zn=8y9NsAa`Q#wAo)`sr6@BaJ$Zg=y>o1S{|^7R{k_*=j5zW2SqRm92mVgHdgG{c?Y zY_L7ZP8rh-4pl}GJEtzZ{Uaaw`LmCP+2P^s`tTqBA3r^*p{~5`n!r)jF(HkLRE%B% z9x>P+q`e(mj=-EshD5-4#Aee(T0~gx)7Ta@YWm};`)si7m@I?ok|-w)dM`?1AVRNzgY#%(B&|bm zV09xH1L%|+2@*7@!^z>@tsr)C@9sn;A!&Dv))>YL(l@_(O`5$dv5|aCT#lJRFlZ++ zhzJTB(TTM{Q5#ektfEG0B=o^oES_)-2p+YHsU(;UXI_$exd4Z-{Ez?kW47q}JBO=n zxqUF`m?y3cPh20x&;IJ?KlqVPZkATOeV3cupC#>1x=J?&C=p&+0U zT@)99G-{hTS65bv8l?$TBVG0PHRqd5<=9d1c*^uHjOdXUYI3D8BE7MinTUT zcDdgz@+8Yml9)V6C4!z?r!=+7jEi0nwbq&lSk&Tu$V4iN5QrfofFVQ{P*f{xA&>${ z3EQona}Jv^X(eD3N)*s}%cGEDLpmyCKxI;9@DWKTg-HR}1t&IJ?kAsk?hr#TdE%Vy zyy@}n-};S>ula_to`8hJw)=&;yu17K$&0t+o+v$nIB8@_)W%AiL;Z?!6Xo_NyaUjy;dV;BF!S5|A!{^pAx`qev}rmY-xXJhT1-}29X=%1=g zCt0zzznv((xw)y78v7=3b6 z{S%-3)K2NQ_O~fZI0z!K0cHV?9sz~sAz{oc$OK3P3NdhiBQLb$*+&FD2>@teT+AyV za1R^fmfPfXCp*2ApIfy{Yi;J#aM^2#!--Cym13S!yGw0bv~{M)meG+;r+eJg zv1xo=yV6f7p_L7-RTcw{hIs3x!&_enFTQr;v#$+y>WNdpAeLgQ)j4_MG?8-7X;J{< ztExeE!6Pa_WlZWqND}Qr@SIw#CGU=+SO_PIewtJ|&2&Umx^|AVLB!EymStItTsg1M zA|_5`Qo6t_kppv0f&!xF`fxz4<|&H`4G~3anRGWwp*-7vKPAkPR5J8e^M*>8K$N~Wi zBC&!WF!zlrly$2N6(8Gqbe`09ch0k*|GfQ^#>0cS8!4PDEJig}b2Ei`E_y zl`_g`O-iNxdz(9-diIm=diOi_Zf%<7J}8TIrIJ)4g+x=DZr{H3)SE6gG3Yw1^p`wG z6_^wSl7~*7+qzYsc$fS0fA_7hb+UF9ErA~Gm#v?`@s4bX#`xZ^d;HNihky3NcQ*&w z%0+DIah@l=)#dTnm$TBVL6%hmeyv6hsq>3?(P2BzwkYu z|Kuk=_1nMwJ>UP=JmdD(ey7Np7a4zuIM>k4&o6&bT(ZnZ76nH>CjbCK3!hm50MIJ}U04%*yVP9`s7)xG+0H<6ZPV!_g}s7^0Ut$?ClP>Mnl)|Rnb)pX6IVJlf3WE4{h|J z*UGx7%@lQdU1hX2+)9zaC5eqvsxV11*6pPXTe&uo8mk2=J)26Ec{He_$y8_+*y|-M z8nBpDom+QjH?G91R}QaU-MzXy8J88MTIZCcy|vXf4Kex}sPz`LRoh~_QZ*{7y#Z}}kg})Cx)Gr!Wb>xS0VS<8 zfC?PF_f7DDl%NHK7-JOHq!pkxFq=({G1^$IbzM17R!A~S9AXGAMlr@}Lg!qJaT2(x zD`ZgwX=<|~TVL+A+eKz`qg7(G(inmxh)TQ?t^ylEWt2uBLI6+-$!Jx%1?O8KAdt*C z-J+BNKxP)gxtM+7sIKVP-PmX|&zwO-v?7EktdwGArIglsR#llYiwO&4E>A;55Q-F1 zRA@8w9jiR%4g8wRfqQ9o>E^<`0|g-|{5;FYgt3=kI^tsekZ;H}Bn4Pd`a3 z>uXt&b7*E|nD~jd#->T0=dN-ggq4#UpZ>zL?Tr_Me^%uW#2J4If2RdhnYSj+C+G4Iord*S(O zw_kqo=5RQC`R>7iz(TzQ+F)2ov$JROukNQCExNeez3_%pvSNlKjBa**IV)6y#5Tj= zocBJmp7?QEA|(mhlw4&=Rdt!00?^l{>i619og$K7I=d92?B3bE^r9P&!wWC$ynK0h zb9>?l!G`{7jwjRe>1qNZ-Zeq_vYynnQQ1UFrBo0hO~M>PKp}0lQp%F{J|ZgSpokDK zO{|oRLKl10o0r=~+bR=UHuVA^?P=Y3D_&p{e7l{y#8zeFb5&PT1*|<*ft$!0z-UU6 zylK1(+{)7^by=1QsYtUJm9RxHb>&#f7-PHDRZ1~OAT%a%&eh`?AkQ}lChxAStQ2X| zYUgR50*N7{C{CKxs^BW6EOR6xjT#tJgG7We#>g5qL-Y|j#8hcPU|Ilzi-}&Ch?o}* z3lT2tPmvK&Yt0eJ5KBPB2x6^?F(z4xfl<*s1k1-*v^&746at}0MABMonA^CrC#{NB z%O>`<=btCgEY8&~o?gYtxFOU!bAt&n26kE6*2~Mc$0HIrck=YDgB{R{f}b?<x8PG0mMg)1M`VWe7VyPpnr4s zqh5EZEX&Dk+RfTztue`9FzD_dz(sB*(}T&V*Xz?HT)VMp37sXI zoU3OR#~v5zUlC!(qqr`lV|k#U5E7s!jPo0UqrjbcNqLZ@&x@eKV`9q#aLrMpf=3<& zi^l2r8wxtqrC93pbnHL1`I%z&YL%bD-P-`8KwQ77MWqurjixhIh}uz-&&tM9nxA~b zqfIqwX#hWiXhBk^T+%@3uGHNN*{$EJMTLeEF(nGrBdUOexd|ETl9OQ8dc6~^!s6s* zw6zPh0h6R1X)2YerIubWtsoXZlpg}RT z8PA{Yzim0acrriP&qu>)zhAxg&5x&@?%u3kOZ-C{OO>j9D8p1qPUMBSKPp|6%3z3S zh858?ofM@Zhr{X0n$41SN9(LhrTp(N>GVmAVMECQWPi~mn3sqkU$I?34pzCM3F=P za3b$H)m~{4wUVXIN?vqFnIsi9-Va8TS>2pExxBkKs4yswWh6?p);dkdG_G+mu=t2o zU}X&=HH`=6R$d^3b3U+FO1Z$KRiaH*&j#ax2oVX2Sd%7Jcly0FwM9|nMXH0>X#xO* zV%UQhWptpJAY$~1(Hw&@hKNXrLnI;;5C%d;S|JmOC{&^%3Mgh^AwVRI8Y7@uEHo9V zIqyLP7b(F+#K5RfqXhtv2n1=K%^$qA7IEI|0ie>_%sow&vZ$2^f)ElWoY>4TLXu~n z{>VoaOLt>sG933-R%tdP(10+VPLm|@9H({F&vU8DTI6srvRNKDD#8%xTxO!zpt)j7 zj1@07S?BHN)XsC&(g_lz+1%Yp()_F6G-x8^-DR1v24;t8qE2tDhsqa4Aufgx zvUdB_nbX^M?~Esp_j)})oKmkJgp+m~A}fVlhg2XKB@lrF02rjXlQ^&}X!1uVS`fnA zb@qtkDsvP1`_Kmqf+pa3g`O7(oNsSgj#9)P=ui+qNG%)&UN>&d-wnsO3Ihqt+>0S= zbkHG5@<(3%nG=&wS6Iy?>ShAjB=~-!r$tKzQo)u$Es1HNO3`KA7$KnyUCkZS|>a0)YhlZX6MhI zDom0nm$%(gr*~oFth6oH<0f>d=Dp#;IG9X#n=pkP@l*z?ogjx8%2-t;Ak~>&GInh} zT|3d49qO!=K?n!e@BG5YbKiB=QBy}MgF%CvT&4haz$vV^tsBrFQ+z|2TSBM~zr z1Ch3Aa8>R6eEQ%RV`lSCr$e+bqFC%Z4+y)6HxQ`2LXsk2*MHti3ABzP$^JE2uegiNGX}y8>0XMAt|Ni-hoU|TBdhwW3+#w;AWr+xBk|+XFibN*mtkr6rJahW) z-P?OR+ue3&R?kF(Wi%0+|36*-87)hiT?b;veBT!?GjCq|yeaRiy+bz|Xc)o~B!B@a zjxZb$q$p{qS>lMKG$V;aaWq=eERANk$RR0`qH8um5FQ|WqXD$5_FdgoUH;Yc{`xeL z5%HOG^ds`Vdey+>k6QQTjf(3ccAT@%K6`fwP}y+kz3xGQ?vWV1@B~y9r8k^i25;#R z&|PRyL$Dd+uwNg)|+qczOsn(1d5OYj0bM8TH$84axD8!w(A+59lQ0R%L^=5 z%;~cm!Os((Do1WylxtNz7>*2$Q18fJ1F&7Rm($Nvs~K+{B!5 zFg%eBsFlvN*1PHU-si6#{15-)g)X^_;;dc>r%~xPPpod9A2Et!X}vZr?d%27wkVAf zh!Q(%9Y?=tq0K2Tl@u&8Hj*^>p_q;|k!Vy>FxFb61gf(1+*3^=DMpAfDVsKgXNLKs z8zmZV2dibi+7Q*)*wh+ZOeP1Cz+m(tS(5|8>9faf-QG?KyDlmyO32ufwaz(MbjYTQ z7*miKR1yFZn$$!DuFA3~v%KJ8m1kL|0)$9}kR+y#6^t`6q9mW?7E`jSf~1y+h>{fK zgt@0|m{~O;BScNmqZOniK%gpY8Ifa5Rtzv|AO6D3h$6xOg34rcDT7#sSTVCI8Rx9E zAqp{zqO(la#I_k^MU0@DMk}iT&?T^0i76RpiAV*A2+eX)fY2(Y)s1x`JoI7v?#%}t zen>Lo=ko*1ir}-#4XZr)E^`A(Nqh*?`rIQAk2f~Et{ZZZQb>_kPxQ5cYG{DQE;qB# z+VXUI;2qiaH9zUHY9NOd3yC5f}lKYmLZfXkg|>iLu-A39oc#bE405el1d z^F8G-OX;dhR}(O39`bT zKaqdWX12Dnf#9a|1*>R#=uCX{@pH#kn={9&W5=>Vk$bg^knbLLO)w@YV8ZA|p-~2F{qS4RzaeF}5MGEryvZV9^zXk~eh(Vl-;32_Y8QFvJ$rC21t7Y9OMd z9@&s6C!hq@u^>#EI;B+R1tM9)61=t6dmoY-av{p&r!p0jQcaRSn9h0K_Rp5U`G-R{$}_kTaRHOeQPJ;gFmw3}z`MXCR4} zCeby$xhvbYbvB12%&fp{jLBR~ zU5bfFGgAsAB2q{oYLY5JLQp2R^jIP2$=zQ6tze8p#F&)G5OJQn1POqu;Xo3RVQX!Y z4iSgtASk;GhzJzHnIoN;GMkiA?y|CSzFidKmEovlGjQXTl#@p>)+UKCosT%niYnAK ziJ8^Y#&?-@?R-{Pvy$g;w)L5nfns4Su^(xnK9P0KVrF@Yd)t&51Y@&um}0g*zy8uj z(t~5`Pwu`E9a?Lum~?;t&^V?Vl12d7-Q8VXUFjDN4i69Kc^N66Fr?kk{R0yVKuY6m3jEen!@&^;m-06_bx zPwzF|^W;SImKLsO-uq$*m!L)py{b+WG)3#@;5{Zx4nqr8HPP7Tu3E&l05n4gHEGRY zMM{s!S4iq`Qs*X8ZggOgHoxOfpM3w%d=I_e(d*B%_04wv&9A<4>tOoX3zMJygV$F} zzcY8nKP>c zRB~eY;aNMK*VEbET@?$VLmjp1*BDJkWr^$gB3hH=F~!}O>NeQPq#3TvwvLshJym37 zQy&~0x^Fyp<%L&vuH0DM+;4Vfbuv6&Un^F1KxUOks@k?~^4?jy=sL$q5I8H6h;tkz z?JpuIBT~g!mV`thf^P&754*V$Yg{B(pemw?F&Z3(5VAb?-XnsK-WXGrMF>Gb>Y&j# zY+V%-XvXSeSC<$!Mk~<}WW&%l%nZVyFl~HNQ(A;15&Wrm zBq<^a0$T%0Y;f7b$K*^V5?yASkPIU-iHHF!#9+B&%>@92`j9y`h-eZRVi0FujAV$ww#$=d?!5|`$$;bk!0vKbE`qT;}GK>__8;kQrI=->q zT)p+V-~Xj&-ucnkA`XXf$FqWuK6YS-%uWt&-YVCIb%MQ%SI4VcclU3nVKz@-ksdn% z=s7WhTO^xXDU=W8I>$RhH&E^`a{|Ka?;+jM@@9)fjqbf!^;~6;u>KA@zmd(O4Od z_IMOg6j5}kO3|hJBN$0b=lwpS@V@Et5&LpfnEZ*`ipyjAk`0!Ex+5_z^r;~LpkNXh zH-8OQ)>Aew=b~KA&6=4bz+{?Nc2+M|&ySjy+Y~%0lc|Wys)8z)ha4589nm5zuC<^3 zjhA2g+SS_^ZXbw!;qKkrv#udC69!7BSF~E$c=VL4m3Zv*`sT^A<#59uTpRJ^#3`4J z%i&rs(*^50U%Wf}D<_-BA8(tzK|@8Y&>Y~DQdJC+-_aBsAG3V8H*M>A2M|zirhNU( z%CPlvE5<8#-Klf@%Q4H%aXFx+_*~J7&f-we^V~|dgrlcXL2ofP;R6#^yU_?Z9 znHAOEnZ2(R5-90%WvnM+0g?M*!Tl5;B|q9qFT> znH7kcNrjpFZ-1`@gMup2fs(+%#j^bs|UN+AOZtYZXNRG*2%V>SLLavpM7@w^;b_H z-@5Q}*eC|Z7&eQQs)9%ka!7^~f@5Ku+={x4(ACq6-b+qJRl37Nu;m<^us&W@AwX$sG7#kLLSg#w5p)} z*0J=Zf*cXG0Ol?0lO>2K5{hU~ir)i)UL`98(EF|6QVX{n7!gRX*M~p$x`!I~;|>7e zeRHGx7ZFu8k2-NM2_gt{v{p*G9v4oOX|&e@v;ivCGO1 z19mIs#FpDS8_peDXB=s{+C*4cla)b!`jN7-ym#mJT*_dyo!zdgQ)6p@th(u})&?Xb zX#Cx(S`pD09_cdsYJKeShtHhdYM-)b?X9#k`O@WcpZ<5RzIL&`eC^=Y-9t9{V8qo( zPMjYxqxY?K(seVkWlCAXjAQ}~K@ur25(tuEms@g0=$h6CL^K2`MnbgC=4O2nTQ6Q> zmo#ED1~fwhR(gezF@_+KF;OJ?(90`QO4eEd72ufAr?jA!TXM+OT0o(gMDz85N=&w3 z04Rzg#%PG=^(>~8A&0;*#z-pBgH!RH_^=4IPu{W#KCuF_2^54_M3JLHgBqA|mV=QA9?SUgX(JOe_(BC^9h;8tyq4 zYb+cYcl25kW(FlPOhhRqLC8&cPiE4e9SG4_7J)uL{3u1d&kQ0|W~U+mz{V&53b1tu zk%)-7M;t~X230i*kV=!wU@-c{U;L%3uYBbZj zJGZts&z)J^yuSA2W0((#NTTMBPi!WDq?VIVio(%@pJ#>Rj^lK~bU@tsK(lGHm@bZQ zy60bO%!zh1GP%oQlFm<0pEBk<4(UUdC6{KDfL0D0aMl_RuB8NozTqP;WSj*HQq?P*!GXC8USU}Zks*t&CY=U4vi8()0>`fPu? zx7Xa?gXgN#Iq zY+%q$XOlhOB89Orbsbj6#Ac8ppmOX|LJ<*GR>i}*&W#n%4Y#Vv)Ev?@&$GHsUDq*K zXVHpjLjX)ntSUr6N+4cSVrEoCW>6AHh=`hw$_*xB&`8MrJb{-kqN0jN>+aB_3Lp|9 zF(*|b>7`6z6%srz*h}`?13IY*X;?(Qk z*z~sW);Rz$D3XATAu<5KME#lzqV0>I{yHaA#6C?}5Q?HqY%L)FjlcC@0<$9>J@nY6 zPyJQ`uzA+bXB)@1F5TSAb9ecxFTMXG@4kKULXN0{h>%3ej7DWlK_y2ZgNTr#gjNlC z3_w2PV~I$u5DmsB++wkCw9sZz&F^acl#?-`U$8te-mZ_($G5@!xpqjjhus z*Nbx2bk$&R)ZY-0Ps=m$}&8W*X8@4nRGCDpAU1F=haSa4rcWyzxiUs_VR0= zyuRz_V7E51m2!A&wK#Kr7=nsV8c{nAFOy&gE@4v*IdiiFww}8^JvfH`hhn6 zsQoyzzW|Lfz`$n7ZU8c%5CMX<)*elWw68%d8xP=2KS?skL_~t1s+RaIQ7rW!1${4L zkoy8hmbOerRf%lG)Ymnt$jp7~+Jhe-?ezi(ArgUtW6Lo}kk(rM`Cs_;5C7qHTZ-ug5teeFqlxp8L>2^rVL5PC5JqMI zLr3X?N7nhv0MvWsh8~e7_jfn<07&&n=HI6bQeWxZD_FE#w>Mt?{NMfy|KrtNx?0aY zz!=H{SQG_=+LdPKUC#_o4%gN;N>|WI0?N&R^WlmEnhYyBcDkrmaC3FMIWS-xES#6< zh7Uu`ZruW(@ttX1*I`=gtc^ocy5Z_)e&r9|_g$wSd+2GGVFR`_&Q;iXaA>{X?|{iq zRs`?ZVtY1QgL=iS+i|&3<==XJ@Aseo^edOHUA~M5^`-p<9WpJ$^_6Ef9Gldo z+;yM0>%`S(E0!(?2Ivt8oHDKlu}Zp6j4`H^ zL=lb*9NBV#p)S{RHHEvLMQh|1jW!!j`%V+;}iA*F-}N9~UiDheVRHp~_P zm=TB(m`o2M3T%*_RW;7_;KE3Vm>a{)eOFG%z~rn!#NHlZdEF8(yCF14y^k0WnLcx` z$0-8r@umM%&+b}7xU5<3u>p((2o#edDiMi@bNSsnHw|YlwRO{F#fo$@AU@pRubV}g z7gHsY2td=iJvJP6?cBHSp!Vli$rKy0v5KNVNd*vy#1byK}aisgJym z5jzcpyl6X<^@`Shm#-g_Ap)XALPXLdzY_&OrDfpHw-F_XNFs2LTe;t}7MHEgQ9=hC z!N$_%_I}ITrR@SDfePHCc>Didl>oZ79-V%6|MD=b%l*8Qe{Ja~t zgJvbmPOQLSbG)|hHa5fY(aNB5A*|ctSPI^!g$<^jHZ3)p(6yPbyID7Pu;NW?fxJ3S zEn(iZhoAe@Yw!KYM~W<`byj!fHy1S*&oq00a*F(5~vh%qBFKuznNb4a97+O{Rz7oaMM)f%$OMpFu1 z(V3g2Yim1*J2MApxO@hY8q$vcO5_9Gf%Wx859A*1Do?6aa>!^%LhGdGZ!8^@qKku0ImYsHomMLH}@Q=dw~&zxJii{hxp02am1Z9m%Nm+P3=@rH3Ef zICIRNIk%aOvvI`;6^Mgmy7{a=XjQS>-)09s)c}KNb&O>zCaa8>I%DXj^Lc0!a|rPO zZ6?qNf8b+hUw>sWSX~|G+jDcx5nkWv3|DKLTaAJBwax9r>Hb|kIK29)ubAKd+|})a z+9FEb&1`o3Y;nvS)4+rsaP(;{Nour7UH|VOV$oD9ds%7ywh-wyFs*GT63Da}@)c zlQF1^)Mi9%jk9b`)=xtgi}r9jX}Vynn@;OCX;J`ymEp+d)tS|@TFFc~7#j+ZYib3> zw+STIgHQzOFK58!`FNH7sB~ zVC1#c;l;z9!Pdk3wgfPFo)ah4l&>5^#^8yIl>o_|efmQmxbv&O`s=gF^!T|m$4A32 z|JEnJ|3CjLMU7n>(J%lan108YQ4xg!05suJPT5x!y$h`>GAtuIj=EI<01%D6>7;;# z-fKyg!#@<2DE;2rTaC{VOG^<{L2#+wW$ZODnvmc~@Ur|4KmlNgabFsLH~yJ-EN<_| zN6+$bgGc3wDz7(>om1uHAuV{Z+a;+*TZbvJqG3=*QiWv92)N3y4oXN_8#@mgJl5L8 zfGNzIS?K1eOHQMn*x-#1ET+3=@Jv;>)x4-~>W$U&PygyCUwPxo;?Dl18mbEA4Rgf7FlSGyNxIlI5el1EHWO^6s1Meoja%}cMtZu+VAX7Og1{TwW^~NcW=HlUR__yX=~+#t#puEGy|3Kr8S6}T7(DIo+?fkW$|xe*q{*@ow1};M2V&1QsU+b}yC`MDb!pi|DNK~a5*gXg z=fu`x2t`(8#>`^Ovy!Yh&5$5z6i}Px0Dwx)IikR53=&xbOi_i13>Bzgtp!4Bts*0$ zfI8vmc&ol)hU^W1h((;P}0FqH8R5S)G_gga7WRz0sSF4z9*S4x!6h$`7*N>ea4|4~g zj@M6YLX>p;k@nh)X>@FIbw@xdYthANH(M_T4K!WwDQK7Y95TYk*#R?}S z740}gSjlAp6%MDj zAAA41-ucKAH*VZ`_PtNI6DRhc`{L{`|7A6+UQH+ao(kQtFvjQdLwi zphzJ7;F$VSS$k$pM43cY%{>wXfB;Ygdj2IHWsDs)R7YjW(ZY1Ee!1s$**9GYfb^{; zBLYlpTc3~M*7tw36DuZRZEC-nhA&T;&a) zE`eE3ZDzx2J%yN3N|=NJ6h(!|7$N|ODM?HSq9)6Mguz&=UaTnsM59Wax)3Enmy)%G zWl~ii+BTS$QsFEFLF6LKN#h{5+Vr9v?iS>-Od0n6OP zV6&1irD_%B&TP+1bN9yfBrYP2*T)Y$_~;|=c*j%kd+%e9KKs!RzW0wl{iXls|KTT} zc;@|G(~`Cdp_yw|Hc3!5T5Cj@2*6MVe)nF`b(bS8YL;$-kPC8En)^KKw z2#ILaNJ=Kw9TOKZ&&<3Lph%pM9jO5j3p5&y7Ij@1$AFASGzlUR^>qXQut8LFZ-k7@ zh-|I3v?NWLZBljC3TV%gk~O4D=16)c19qU0kx@dTd$Kg`SrW@eKmnJ7A5GG?MT&`; znGryZ0J9=OLLp#R6yCSYjL61-s$?0X5;CQfnAv+z#3`j7ol{DK!C*LCt%k#*D2Zr3 zpVza)FMsLz*I#>WymtKV_TIDadKwVG<(0tQz1_Wb&Z2WoRbciBPgG@D9@O>lBk@Z>c@X}asA@y zGY=gskbFiY%W;ls&(V_VvV2@RNggf!^xcTak)=W3$_Ov383Kf5`L|?AmbD4>c@g)( zWLX(5y=?ohApj%*q$tZwkLA&VM6%S}CQzRu6_wdfydMr{`;-c6dFSB9`uYZ8ZpjfK zBBj`9*H|(w6r9u|fux`bF-OphQ_}*dZ<3l67m6hp`HK ze(A;S-FkrzFon@*jEGU1a+IBa=A;eDi8d(^W=W`NJxJ#=$~cb!eQe;Kk{B2e*qVMu z<0U%75;|q*l3L;rI?jzCWLbvDA~J7UWDwLmw-cEwKTs3VB|3UDvN-I+vvsQfyP36u=l4La^3uZXPSk zvM35Y^9dJRKSBDdS_X|obg`i284YNTq zHgl=&=CjbuV03)#R=5Eew~h^8el0xr#ozpivybJ@T9ZI&zH_j?z6oHICHlqg_V&j6 zpRIoDH!u9rZ;yZae_JF$$N~NRRl|Mt#FDXsx4$fLU(?+a+%G4{dPLd65ihGp=j{pY zdzkX=8r!~FK>+~i^Ox_NI_uI3_br|n*Hv*E>blOF95{mQVMawYP9+R7Y2=No^RIpG@})PMFWi3V!lj$&$T~8LCpR|E zpRR~Q2pLmiM$l0Rp_|l^h_hVDk!6Wdg^a0UOd$kjwAS{|s~Dq*5HS#n2q{O7kbJ?m zmBiLSOvaclprMRVQ)rWlF|2HSh)rlM=Y}$}nxqNR8DpH}DJ8}cQKzOlXpp$Gj2x9s zo;yjTDWT~!rLfm52raY|%H_kG>mT{Er>c3i@#FvR$3ONX6G#$hyOTE$rns1f5WsK< z>Cv?l4Fu!a8gv!T+x~tGl8A_`$zqH>cP1%WlL3I*tlHSz-@WWRCcu$PgSai)QAohP^8$#&6KSZSzoMB}0z6AguW7D?t#Uju1@o05891d4jjInIZbTYqw{raV^ zedE%MA_=_)R#a1r9b!QA*O}GV9_;zJRGd1)MOT$y#u3-Nyki9 z)i1u>T;JO|v)b%U$=D;^%3Hp0{hbbd6@g138!7ZNuO(D3A)>EHpwBfl_l?SyZI9?% zh06USq-D+WZSc}tMcq%^ybbCACF_!qE>v5ed=n3~oehTT8bu;7F}dDy2OJwolr*as zO_Wd!GUDJy;MPwi54(5u#%nvTU*CV@^_}1Q!b|%*ZOVawH-`M+gQXAF*#fi)P`7Q? zMM*Kb%7jH27*i5wSw8L3_938l8*78o8I3K9dXfkx5`$kLqBUddgB*$g zA);Y5L8#F<1k)pFOriu))DR^R3@I6aW?t9=0U`o8x9}Z>E+DeO+zEumcvvZDO0k~T zVDm^kk73^UieY`lyysZ{$kyhG(Rlrg9p}Zq;`$?$KlfMv=(WF9{>kt7|K(YavENEC++8 zX^k~x4FVfxLPLniz>LNiMK;C|8PiXKnV4A+n0jRjQI;7IChY$yXslVHhFyx#h{&?N zcTF#+#keFFk&I!qsHq<+6Om;~DdCZ1&KT48G9oZ<4nqjVpjufQ4Tq~`F(`^cM0R#3 zH*Vj4=?_2q^{;*7&YinAZ{9pSoTLN_Fc_34PMkP%u5iw2lCJ9*X?yqj`e-e)yqHfP ze)#-jPrM_}!w@YDR!o^uW^cZ6u^3mA-F=5bDn(Y-vGZtGh6591-gZ9I;{(u+$Wmei zwTzU=hoa=snRw^A!~i1w4}ypiQ(;{;oR3F?+(vSgWm!t8D60B!iVy|RcTrLtuCE2} zBZMcPeP%lSrzr@tCNGP&xpnJGaefV)5+ESbQG;ywF3uR=hriU zkjw-^x1iF(qM1cC$Tq15H3k;e8}dmb)|RcqvUJV?fQ{{HH{@8>y}3JCCpvcO zk=ccR{MdWHfBde;rq?DpbA%|UBFP#ollp@?fVXS{J2D6ngGaU~A~9)^Wul_0#^$PO ztrCL{H@fL$>!G#7yH`bW9yjOsGzK1BC^KLsB}pZxz;c8sze@6r|oEC zL#yX+(g?Ol&P1V_e3}N@&p#5iz1+0&vC&ATtB9bq+L{ z-hsRKf&>77<@89@S{vnF2?GGYOIb?Trf;E?m8K<-)~F^LdK|B9Q0#`uh6WbL%N3mld6Fl1f?RO}$W+ zrfG&{H5#08Hssdq&ksNJp^pGqFNd=8X0~!F)ca39_(+mYX4EtdF!aRD%4oB`ac#JM z!nmSoXAogLP)*KqRBckCmWkO}54y96Nx1i7AYkA|=c_6# zclwqXqW2aJ5fPAdX?P1qD?dxs>=9^~`{4tQ24;#qSMoNDz@xpdKUSCATP64t4p3+b zH$pe{X^-`6yXc4kj9JVWQPDPowS0AT6Ur<_f3UZB>8m>jQ~Nug{My&Qwc{h#-Naa3 zT^p^Q%+}cj%_Yh_!hDV%JH%j-QV2=2l#DS(laM4w3F5Go1|5J21UO%W!ez!7)|6G5 zNNm0AAErFFgPii*3l=fP5V~IHL%oIqQ8grDPRUrRrOVh*QtuQgqa+l?cR@s~VPy!K z{9zy5O{|79*RpS&ePgebxYO6VtZ92iwiU?fIY zloSo2NA9@>kVL)dmxvmZwOyo425aM5)9Bj`*ub>+eHXV*8jLcB8`kNXuN zYqJo-VzC$^7MTmNo6Tlx<1sVOW^xb!?nNR6S_djB!o)r%<18B$Nsw3|rTmaQiKr$Kk>2sZ z?w<$6MlkUCw-#Wll_rV0y^!eGF7p z2>~F9SRgr4zTK;6`u>&xkNo@ft=*(QiGTqBkP1{mUF&mc8#OXmT`e+{qGPwIt$FkE z;UD~m*IvIgy>+K~{gqq0UF3*sEBR>Eg4H2xbqT!4v<1~9Q3WESi7H8wqD@RbhI#TV z*80HEv9q->;Q|D;WmdJ=mYGW_MokH{@I*q+<;tmP{p6s_tg|==0Hx*HD-y*d!F!Wu zPSE9pX=r29wcR2sa|U}DA_A1TEk#7cCy78&d6Y+0o=2D;&Nef37Mch}K3+RcPaZpM zjI&u*Zmv9dw)p;P-rc-)aptcd)H?_5aBXW}T5Iy5Q7+;J(A9$&gKy&G#>AZ5i03xn z@x?#DC8mrCMLYng z8p8wt+63fe2URsZ?%^P}-Ecgl%%T!@qG+}!b(psUu#-d8R3hqdrRbWZ;@|zjr%N;1 z-#^66s&W#wnPWCFX(BSlp(N_}a10x3**YR(1ZLa!KE1kLnU+q4#wZYw0YqyIYGPvr zK@*T=!!}3)C1ZgJjLqJ9sxgs5-`4=7q)Al=Rk412bv#}pV}Ph#=&jo~UwQQ#SFc}x z_0?A|U%s-pJL!T~@h*3x(P+FjD$7z;nWPxf?83Ac$SoF&!C(LwRea_shNj3(X00&S zbqqb-Z9s`fKam-JK`q9 z`E!5xdyhZ$T`pTi4hgNLm9E}><)w@7(dyyNwO_w|aQtH*-no8bg|gchUVZTCr~9XA z|NCTwUUEwSy-)4^d&j*;9KPkff_mfv*E48{XnI6QN7YFGzY&rw%_Z+Uoq{5=^p(<5 zxB^G2f7R4?x%YFu03y&C*HOgLs!0!Tu*twTKldwlFTA!N)Mvln;sLp*5x@tbi}4N z-9#`(jB%E*$~2|eC5-`zD9=n$ph1|-Q?Dj$+RkKIOyX<-ra0K$-rQIlRM~2mR!f@9 zQjin^ZWLuL^UmAM+NfdS8&haIqn4r;JCEdp?|7p4;A&AX79%?dp{Qef zad+~C3x^iUW7YVq4Ih21oh`b!cc5i>_40H+TtD^T=4)?W4=cmdE9HJL(fb!K&P+w) zwbiQ^zWUA|{oW=eGS(xaU;<|Z=FQY$mV|OtvYGcS7(s(ZVhm!e4H62M$7qmDP_dk1 zppg5@t&l0YQ6;lWgRRHPQ^%M&1wWfitN5!a~ zw?%GKN-2u96q6{cEgd_XTW;Hs=LHd^q)dj$uyL755x@{yw>5A45nfgfJeBhQr}4DMI!D0RR9=L_t*XCpQJKX`0z=hD-q1E?R@c)>z29 zpopB3_M~s{sqrb#^UNAm6iIoW1A&M%P2*fPX{XKYcyQ<;PBz!K07C3^@5;q-v9|S{ z-*fUivhK~7ZohUVhm{Nu>NrP0CCo&qnXGX+kCKlTngyRskx5M6kRlskPSk{$9C%HQ zYFfs7nNmrZf`L4Q#9pjs)A>G*R#%-pdF=S*3on4Qjl>L*#M_Qv`No${o_Xktk3KZ$ z?tagWI~UJ9TK>@Y)|12e%dP*R{d^#uFEg(h+d0<`2b{WSloR$txBI&gR8^5)+^>oW zKq2)5W^T*4JKXLGL??ps?=rSIyq8}a4aI`>lUt>k+$}oeS2Os`>KYi!i$=^;k ze&T0$Yn7~ANq#ok7@T|ZRBkXPCK%4T#39Tjq1Y))lNznOpLd>?vrCC^{!r88A zA!%ibHY`Yl5t%vqKnO9#P}gL57Q4*2BF_whCV&J^v5Dv`7LG(33*7ib>>8mgBVtS; z4hEyTZR@7n**l1eE*~7+Ieg>x-P3EMnP@hEeJ;%JyW&2~C>YvR9fqvF!Ob7lW0 zKJe)Gzw^%5UpTz79ahGJ?Etr?gnl;WEI)p1c6awuQ!HNm76XWgh#)F62}v6zG>hD2 zh^W2B%sMtm1~d_)0HSf0m{G(yi>eHoWtBfj8j~4d23&L&j8j$beb+2RL=@W2hY(_n zF1N4<3Dn0>6vgQ?CwK1NG3=C(nLG#pWNa)O1nsrPMLtqVY;9HLDFq-gRuh?$Y$Qs| zRycuzb2dhTEH_JKinGqLNstJ!%p533QNR?VsFFtQqC{PkRaI5P_4SQGHB!adtiF8t z@~f|Z<>JMQuf6`t#Wyc|??VV18=I@Et0zyMtcC-Z=llEnA%v7t-E|=ZR4~SLU1u}b zU%K9VV~k-;Q31qQLQ+kdS)-ba$r06Bn^GDSRZ2;d)^**Dyk_85hYzlgO)&uC>CSb{ zs3^IX)%G`kwfxX~*8JCAO(QH^Ubi06WI0z=p3>UN=y2LpnL|X&%*atSCDkBE+E;2R zwxF0&Zw+ahHig)UCQV^|E$d91m#Gbs<&e-K;toSFu3qof4LA`^!}KZ#(R5AE!7_qT#8(@Xud< z<6B?;@-P0v%EMo2uY4_sy|WKJ)F3rtZ|+Z~&B2&$Uf2O~#7qrS?Nis@rLlzW(S4LoL>pK95N-;)j zEFxQHd){!rZVhpU9D+1$M6!p?!~<-WV@nJkV{&ia*)^6jS2Xj6ER`9*=hTD7uz)28 zU}@71%HxZjyBD9o2LJFgZ#@28AMpO*xXTWin`z6HHlZ;fN!htV{hciXX=SiKXu3|n zaO>C?f3tb&)XAr|ZoRWWnz+MIf8ou&?RJEZtQ3><&?6huH*eLud)>V0XBj|{Qcxf< z)XZmPUXV2gwG+&&6G%#6jRm59^|m*HW@{1DqHVfB8RtZ7Gn)n&6?4$uF2Fd;RzlP! z;BxOY_3JaSi^K6+RaNscV{nZ!0R?D9$XH9pASzR4tc%HKWzi=Av18{9k{TjlG5{<> z##lCtN`(sn7$D0sW+DJ&WaW@j-A+?PYcnOS%Hi>2XR4|KjBWC5(@m%IU-`A)eC6fW zE?l^DcYAklZw~>=(p6Qpy1F(T4xMvEhMDu+bzK)yYJGje>!!mzBI(n?;;T50Ds6oUw8uU}pD4m5LJ zo!;>zUViiB(;qRv`MV2a9F+`p6x2K$v@WMOUv%q(A&@4;#E8bJGfJ$)3Xnhzxe(MD ztKfqVM37|}2_P1Riu<6Mnx2WloEjuDgjW*D%c7h&braS zWCkk77@A|Nm4wIwi+QuLQq?{_bo#`kSx_@0!{l7uEV8Un!W;XCwkS{Aahvw9>~v2* zxb@T{C$8SO8^mwV@6a?~H059UFXW+5o4@m0U;I<=dtxzw)%8)eo<|>Nx?q>AkM)#d zwh|OA9G0GyjD#4ez1H;6`f`*MM8v-)!Xrmv9$PqDfQkhK`Ya3%%6`6D}T7m5a0h40| z0!^ygHcjkeJhB2HRBH&4=h7O-KE|@DqEAL4sd7fB#EjN4BN#?7icA2~KrO#6vm#;) z_pT?sngA`6kaG^4BLx%yV{^kw8dKwg7-Kjhuo(@+033 zufP8Cw_bi@(Kg4AAOHH-UvQ3$G2`+0p@+{|c0?3oRMo{|(fe8zMKPVv*xHprmHAAf z zt}K9H2<(%9Ac0R91k3>Z{Lof8hKr*i4D+pX$HM1csH)+u%P&2tKct+` zl?zKD1k^B)AscC8L?$9DB68H7hzJoXEooatP!Uy)ApP>80r(Rz1&(Oi`=JE>*!=i@ zD)*0nwtq#*vyPgAW9RR@KEG)Kn!(ls&;H$i{fYmw3)jB+l`nnj4}a?ye&OPaFD0at zC$_5f5mLmdPMXPTA?H@iiIpnTh0Dmgd|tPs@!GAWbaautj# z+Ac`MVUagYV-;duU)Y`6s>nz6^KahXxjTR7Q{#tE9^boNJ$rihwNd!$^VL6lasLP2 z`|NP59133B-OIqOW(DQ00jP3%;P~qEZ`_Glwq-NN!Bm{5;GF}(_pj8)=+=cj`q`f! z9((ZY(`W1h$6#lgeAE~3%&Fe*ErXabX>YWMB4C(oo&{n+b=ESXL9&J-QE$iw0R5c< z0II3nDnb%#N@+YmtZc`Yl(UqQiePWmgP7-e*F;T1pd`{Oyh8~2uxR|$mU$LUh~ku! zqAEDz-q}QrA+|9_V@#H1DN3G~$OML2$W)oliq!coL|f#cZjCc}X4ls@hr?l64a>6f zU7XKmuf1{g`R9MADsG`q45X9vMNR zi1ZL+v#iI*6k@k%M`cx6*NFD={3U@BLQ1K~95Y*+8D0>X7$dVqCGR7%U0U!}wryKu zjD$$c^>h|Ny(zEUy!=x?{sX=3e25Eu_Hp^<7k6%4n_jce*UIYvW-z zZ`~+w7j=X3wa-5H?q|NU(`XsD5B65ad>Ht|syneUy7R`t!4p66D7dVU8aLX@ucqDI zGb_gq0sZ{YDt5sDxGC;A9;l|MqN@FzNDzQbf}}^AK9WRY>TiFyG{O2~P#}H8q{6*P z(Az${Oh0=INB&p`bZ^550AN8~kq=D}%$gimk}RT|?d~t`+)cxBxcU;ej$|9|@YXFm1D3m0;$r_P=^H7L#wyNwYKUAwlrJ~+AQY(AOHjVnV;YxOv^ zo$tD;k+us4rLY==r*#WP9g{~kDoL?RDF&I&7ZMXISj*0u!Z~T0%rdfd&Z$B>ITQrW z91|6e4iBd*j&dtttWg+N1CbQO4+f^H)<7{xvp1KpUtgH-y$I=3ua8jpT@S8Y>rQ6X z=w0LW51qL)$p&e^-OK4zkxs7EuiSxFsGGNlc;)W?dNso2 z?%mrWv8fj+r6e#K6g@$eBl4~kN=5;_{HbG^x04Re}CzB zK7C?6J-BJf*<)*?l_#Gfuc29tDprXY)f7b|P#Omu^HP8prPt|fVTW1Pr6#2sAcTYp zCd-PjmIIM4IVmDSP4+H`NSm^XlY;o_y|KJ)qU*2Y&~c>T(ytJA{?BKG!0nahTw z6<3hqOjX;k0F4S1Q>g11B2FfU*4n{n7=y531`vU=EJT)SPZ5bChR`@W%nC)2ej@<@ z4F?q;QZ*dZvw7Eb*4jMJ0eT^8>i0PmB}(d@7DYmq6#&pTS=QPRf-{^F5RoC?7_QAm zqm$L^3+2cPd)Poqce|JCqJ}${%IH_>9p&b&Nm?hW5^f-f+BKn%zQ+>Eh~(^BsAaV zLHlhD4!nJ^#DEdBuUYu0dHUm&?Y%P~q8O?=Pj=8LqJ|Ws0SVc#NZ81%ba^U7Vi)$) z^E(H_Z1u7CedJ@`{lUL-bMF`a&d=og&sVEk<;r+^chUH+a$FEGVxrV(GR~w3l-oG3 zv&%VOuNqD(O)(5xJik03;`SzzL5LPCrF)JVA)bEGDvQ|l*Qd*k+V-?;Gf1LtPl z&i=~b1H%Wu_=P5A8)Mf^yCw!9x6-9#Rf?uoWC;tCmox7lF#5jlJ4H`?-_`%|@9$1$ z581P`nLoXG@}=!NJiYF6Z4b6HYXdCnd?jRq(TCi+vdm*%#n6&5&X`^=rYVsjh-!>M z=X~spwWxp)m`e>v<*+RCMZ0U(Mvex(7&nB_bN>A%YDx(#w{>g5xXg6*BC{^fbB)HD z+&Ys|5RtA6P3J=tU>c6{rfKqOaN^WM%qE0Xl`HdE{rdIYtDk-T#aCV?V?O)o=d|A< zIa)ax4hC$UKthA061y%0D~XB7pi7b#wI7dG7W2CEacgUfi1zpQ%d+fzgNOp5>!k1X z5Ltl`F~-!k9kyXO9%H{t%@kCU$h{(EI2<-j)7x%3XGH`_d(Y>-lM;;xERg!mL+^d> zD{eUlgaj!f-9Fq~J-K=Efd{DR@Xq#Tv7T?7n7wv$Qo1pW;r4}Q-lgV1vQxvgvTo)p zRb_}4lFpqtrRf_*X%HERiHzY4$pKhIR>3GLiw@a(-yyPL*ZHY26k}K9w#vvExP4>q zi8IffK7H!Vs|SNB>(FIc`MbaLZ~n8t`QQ3(F*aeLnHipi`i&2L_?^G>i5nmMp&#el zwF&+}eAqN*qg2n@Xk6!gLtL^O!1phzi& zB|8JXq(uNANm7#wBnD9hvK9b1wgTwbCPSUVGPxG~tH*Oyy zES`R9N*=G=tZz;0=FkkXWBCSkZS6zK&43C}Pc_ptn`cquB4x}0iPGG;+*LAi_r>3N?q@&o%16KJaXWO*_+l{p-nA`f3+9zCfBB0Ci#R@B&?G8* zO`$Ba?Zd-@nW{CAE-woZaJFRT7^BHaRoRh9bcPXKuODfBEL~_JBbyOO)^<|& zci-JPxN-OJ-+cVvz4F>cW)2}K5FXh|k47VA&hxzMy1pw(D#^D10KU%y=}jtHQnDtC z$s;9pkel3xrmCvNVxf?d1Ol|i_OWD3dWEUS$6nGBQ6Z%&%jXab5;M1Liz=?jC*d&0 zSe9j$WiiGO!eY@>MX5Uy%$X>L;`6;U@(TVneS$yO*VgI%e9cM9PWMinfJZ&=C7q}g}k0U zdGeK)zk!HdO8Lh5L9;lRT#_O)C!PZTZ?{fvf+=45`G0xv%)>wP(|`JfOILsTfA@cS z$9MnW?AEp4{-uBaiGTg`r_O!#osXZPU-`s)-uu2r2Os>g|K-~G2S)|8(^)&Ojar`~ zWe$^SOl&wIMgp5K#(0#>rYZn^OgsS4y-MUyDnq`lBw31fA@%=cM7VEGl+xRr{onfd zexcocqbMrVxJ++;{xj3v8-tPjm7m&rKb#&;ilQK6t5G?vYa)96 z>S6Qhwe#nWtq$CYt(B)9c}PLJW--Wo-6ZmD!Q;JY{bCsW-ao(e+pm1tDt+zXTW3#R zJAUTD{aHQLpt`8m**NXmd5QyLb0L>PLc|n2GZ~wysI?Z9EL$Q1LICN9BT2|O%W70H zCb14x**WWx8NGyg?#@_TmM5f;R5kU_4gf++RlpgllzDXs^>lvM#}1NRU0*+O_Mxq< zV=8>{@|EBD{m;Dd#+z5JT-iOCudc4&+1?fv!^XNi#<;q+X07dOW<(7k^qP?nLI~mB zc=4!hU&?d(pU8V}j2n$|MC>|mE!TDJz3(sJegQAf%YM1G2NWX8ihTJKWlm$lUh_Vm znIw*Qp1hyO!C-K3a8TEEKZ7SC=Umryy#PIg;Dd){;SkbyZbmM*H*W z%Ep5RKyKaj4?o!L+)b$)73&r|w|NSE$T)lG{Hf>e*6n1MTwY`YOLl)}_px&it7Wdp zDa9zzdNdhW5CkPeXA)svm2ERYCP_esRZv1S##EV^dAxaBMmybazxIo3g-Nlwc_GoqP_O5*VpZ%k+{N^9N^34~wwl-e){BJzlb?C- zjY~Iv_TOB+HD4L6u@RcJfg>Wa#hjH|S7#~rO(T>QgEFg1i1F^h)He&tT^*Z#3DBtX zz74TOO32Q~NWH`Zl&y&|_6D(t8U-{YRn!=PSOt%QA5}$@f=ChpMpgwvlb1Ol9!w_2 zGGz6DbVR`|;v(2?lU{K?77RZPR0FZ$QlAuDKWx{qcIeg&!BO51=FJ{e+ zTZez}`7htNaqG)p`s(%TcMw5g8QFF7?wz72h(IA_g^gZfssXC5Yw|o#DXD6VzK<*I zy6y;2mLiD$a_s*i2;iJsEEenQ>(kkxF-8fQIZ5bo$~X&3qRO~jQCYe(99?hTdnmG| zX{gLnh+=FPgUy_T2mpO$H=R!Vnx)T>Us_}z@ihGjnJJ}I6h%Lr27o?oz96_8n(?V~ z2vEx+GHO$Nc(lnrfI9Q^S2efMlJIsVYOpZiz;As?K8 z>Fk+j-~Ai^?qB>L{~zC373JJQ)}{t2jbQ!M)@S~IAAj^8{Oen-T>R{pZ?C`O%+t@T zP~6>L)Xk)D>!*KgCCdie*YAw7RbqcfZ+x(fBHFv;V~TyMUQ%6dCn{+aKnzJt6z@F{ zM@e1N+?*U1`O3NDCk0_#qRlpL-QIrb@|{<%>fDK|~mdY>`qZN2_rXJ5w+ZRC|^mKov!is1MUo1T~(LNdd{=+UDv%{)a!e z-!$L));IU|YeYm;QK^O{0Q9DfhB%K_Rih*k3~frH#xQ^!E$>+oA)=Im$XgS}o@(u_ zJVeD>OK5tHd{tF-U8mUlb8-c92|Fm;_ap zcbdSNWl^?VZd`iV&gb(i%c`pC`zZlcxp$odh&p3cwa>J6&Xrl-wrxZ;i}2*r&nAG9 zF}3~}6*qQoZH-SyGs{u{sIue~wdi?gu*m2X`+dHjL3Q z&230Bo9ymx&+KUR2fy!!bAtDN^!s1^#wYLIn*7WE;lFwQD_^~J<;AQx`NAK*f8+ea z=bnA{yTAKmfPfKZ+j}u~mX9KrmZ;H7@_WCPvMH7&F&-X`GKS1r&q`7$9}symZ-sEhPeNfhFdtj-4bnWHlyh zlo(>?ZRS)pv~5|IF@)aawa>?lq9OnUAteF|At8|>CIMApggz=XYKrxuH$>|>8kdVI;AAR}7!RFe-YlF4*(c0Fqbb|sRTG6g<0B8GtZI~sd zMJtu7);CX9dBwnzKsAX(RSoT|x1{d#_z2J%7C~kdfuwq*Cg5JCL4A}K>B7%*(q{Jt z7@L@iLc6qN_3`NMvkZkHB?6aG1X*1j{;N;^VW$=;uCA`;d5)+t`l8IbuIsuk>rI%L zQ<98rUzk|60MLH}{g~D{cjO-O?blNOKq4enO&UbRSTe>mP2-$v+jeC%SS%JjJKKw= z^TMT+0Q!ltG)=P<7K_w%oms=yrj!_=%yN~`c3o~A5p`YH%UgO_F~)fBi=ybeo{(Y) zsV8?+N-3t^a~GK;rmpMyl0VN%X~K|+03?X3rw8?nAAQhOnFZV2*$JaTRSnkW^E?~X zc^kx+5pHAJ+}sRZJh8sIGTtYTCaFaOLI7%W8;OG^$jm@1NbN$E`vuZ|{(_Jd?3@|c zX5Kb~we^GD?ZqPg=#T#B7r*k~J#g-PR@pO;KaO8}e){h3tVVg47D_m|v-j{j9;;~; z=64FF_3_sCeC+$}Fb4_gaB5erqA)_9k1)`|)tiCbpd5bq2Y<3s_|SKK?^i$b$%}8k z@Ebq(i7$Wa`FFhI3H|wh@%|5fbmPoJkAL7JPk-P;#M*V?ttRL`?d+2KKtxcbqpd;& zxaYa>wsz)dQ{djy=55!<9|IaVI;_VUURq`d04OK~?7HyYhlkywX}dXRHa18m9o9RG zIJnbHIRd}}u{gZ*>eguR(RZzW>6P74nIBFQ0brO>F|50NNGqV6Wh?@1+h&gU4|m_Z z+LgwgJ$u$B4y{_lZS*M7Xe6-^Q^6S~C2I?pt7?X(m6Q~H@Ldd~5F{m0WDavikEdS8sTQ?`^;@<8zF7LqWYO}H^ zvS-c>vvPfXNX}(lGOFN3i!2+ny@t}ZixibO*CdE+Gm~VIWtlO?A_5_ZF!#?0vX(>7 zJI0vh780pyUDsVRHKz2^&gKX4@P?}jz|^Leq*z*g5lWOJ9s~(plsPbJB2|C<*zm^Q zR0)}4Rhq8r4gFhC$X%hT%qB=+jF!!KG)gJ8Z3_TCEaxvx(=c=IVng({p5^|=*?XVo zx%Ym3eZ6TKQJK&EnbW7ct^t6uEGLsmmSq4SF%@~fOk8G2A@+524?PhydY^2_R|h+X z2gcem%gHb^_p~kmv~3$>?59G#b7hpIvaGUuVXI2avdm`AdvC1qZPzr7F(yXQf!jKD zLe;y`*vxCUTC~btyU2#a#m?Q0m2O}`q2ND%5pGW>=-tqjOtKVqkMIx*r}&=X&%^IeeuE@ zkH2#2*6UaQ?oa-u3*Y?gx%Yf0pfeWdX>#n$`EJqOO!*+(*}i=1jmvMo@BI7n@#x^n z?qKtbwryeEUfbkqATKq+ewZF!oOsin+&cZ|{__921o-Hm{;7+veDmrn-~10h`|;Ob zdi9xSpZeY3`1$vL^t6|he&nq@!+t3JZ3E`}j_LpW_uxZ__#Wcin`2AA z=6D|x;c|`oNS?cNhgsH2ppY_lF^<{anjQujOmE(aK1J_R7oy&%(b*U=WCzoQZ|!wE zNb}~zS-5d~p@ag;27P@XX?1~}L8HNE$rzC7{P4shkNxle=3o2x-~R`%UbrA#;B3%{ zilMS9G_=8kGg)5bZMz6jf@loFL;^;ce3yI>MG=uM38;Ytst|mLiy?&c%a%!VIu|6!U z9Sm|)WNqs9I+GBHwa*I{Nxdz3+jeG|`$x#6%d~Tgh>FCRG{rK{=Ci4YWLdU(?8Mso z6IL@jaPvC7`q{b3vy_s_xVOhxPM42}1yo2$iac+9%8m8ffN1oIEiBq*!)-QQV>8Fd zZQH7# z5U~wD#*kEvu}!1XdJ-bH(>a5cHePt;&2N4A`Hmqx{NSU{KKkZw{XVj(R#)#>7~i?G zx0tL}$D2EMInv>^ou@wd=rpv?oLIA2d}(L$$k$#UJ@xR3)$#2nRz#^yY%@eFsYyAB zAeF92?#{vF=62&ad*Ay$@PQ9~2O)gVPyXe}&h0OL>bHOS-~7v0zxMSN+YytUfzjOfGe!RUu5e==b-rTIqxUcJ7(J!$K-FL(1w8)%NC)(wfG*OJ8VsqzuwT2=Hq&|3D1ys}K`ufVs%H&{wZFN|bC7FDCd*=&Z zdEr}Me(Ca^+ZSGc^S}F{aM3_ z@y=9eCK=KKg#Gwe)Pva^r!yJbUyjgFaC?qeEPG7kHq$%+8g#C zLB^5K-2F8ODk;E`z;1aIf2>3!_5HgG3mcgVvRH+7`wLwHhf2XcPuXgn8+7 znEAATbDQtoM*8BHUY*};e(=x!=#T$Le|{mw;biBRfB6qS_Z$Dd%7@XXCg_Gk@V>|g zn%WS(QpJ`erQ`!zS2ryX4yp0`wGc--C)!Y)Y4V7W5KQJ4wTIp^9RO1o!Iqh+Lt)JnFfw7101%CZdbTLC zEXJfNNs{3rMU6pJVNk6oB=3EZTi?Yj%bKR?@8l3VBGVI)eZ|*nShLKENc6GHt7o5m z$EQB^`=gbOuAQ*SQqs^iMV5i+qHbeM{kmY^wy-4-WdcIe+n)C)jGz!TLY&3U4D+HX zCbKCzE1?g7jVUGPT#uo~7$PRX;CrMqDQO>K)A(*sRm>c^t~XUjYg9#D#%&>KM(k|1 zkzV^9IQ1}_LOjJ`gvni6wA(MeWFJH_qdHtGvz##!ySd9pg~w}O06n(6#SAJkz%myt zAt*8%j0<2gDSB09%A&TGqiF!g#rhP@f#ib`;DVFAy*GLA!3Q6E*8_LAcL$BF4;nam zOkaN77O zL}395Ne~EP0*2gW0D^#bw)bwtSQOcJ|EV7>GWdxf|6ly-ul~Hbcel$?goiH8_rKj3 z;ocw!?oEa60mj?wm!mrNEhxGFJpG$=-)HZykkCger8xJ#PksECZ`e`TTLhSPnsU}Q zkT@M$lZiA*N_HK`7+ng|_!O(=6KpahYWENbakz)JLaUWO#p z&)y|r?|4QipsGMfproJ#N=%5$#9tL<<5q_oRuZG;?1)7{qVraB(}(8-12HPJCYq$U zVq2_S=UuAji|reiE_8dllTl^{)i6kCSe3-OoIO~t>sy;Aj-Q$|&7Ixd%h#{hhx6UT z$t3tyHyk~%@?ZVEfA#cJ4`I3Tp=aN#r9YW(mCn|oJDfEeD+576Kw_6th%p$psOX!J z&|78#h&~Vz8N-6TmZaxLh)myWG;N*dIRG+C2!T*Th+P+oq5y!!VqqQk?rnXYQkG@k zYan6>sjpDH?}u^A`ltt*r=EJ|#TQ=6+(2ZWxlvj1Y&ILNtc!XgV&i7BnfJcG5Mzu@ z*P11rvV8Sm5o2UP=UnoEnJw|K8i)v5*S2k6(e&r(uf(D#thE3QGshUL%~cZ-1i!S} z2BeyS}zLZ4YC_EYDA$ zS{tGPO`wLEfsDKraNox~npDp;g?sTkA`*kNA+)JORA&gm-`%+j0Q0>GSf20heBfR0 z+r7P$XGSsu4?IAxeQg?NW3Y*>8MZvFoF?5*wagdvp-S2`-gtOfA+I*Zey)l zOgyW}hlvA3<)s6*s`fLSW$X__N{Rq%95RI@*YDi%zICJV5C6yi$#TG~_ccI&oRNh; z>9zc+aj6sd6ENaF5c!|`?6<)U0C1-BUHb5!`%i!Fzx}_P|378_9d288REOeKHNy%! zo^;R6ougtaA|xak1IE~3f`Mmnw!y}j2L=b(Cj1CCBH7q}z!R7(Bm@{C5FnHVl0dmz zYN_+h`NSPonxX3V$K3ne)7`S4y}t9^Z{NMo4rlE(YgUaKHO9(rrfm#9Q(}Et6ZWLj zDtaRuWIERY@f4gjru7^YiOLC;_Z^UU(Gj}-ps$Mj#*OQ@-}9>H-+T0ze&!9EYs?JJ zn9af9dgX6ip8U7hzivKpDV5%yb$1+H*_+KwIj*~4bYUro2okQ2hIP|$kwcmZ77&q$ ztQ7GQ8UO%wFrRrKL@q*vLg>7|v^}}!*lnEef)DbuAIC5jB6*$*V_mnw#kA5zFnP0SqFifICd;zfyjDsXV`h_SQ4}KRyXD-UqE z^N#10ci*X=d<;JJXx+6eC}4)e;q>e@>sMebqLzdz39twJwe3SE`2o-5% zvjTZGf#4~1<&7GRhoixeR49gPo2NH-&tKU;cQHKYONtvaJ>BzzRgEwUPP+Z!=B;mi z<8NO1llPx~%?sqlBx=gMIkk4%)w5T-p2@KRvNz=ome#0=w;y8^NT%3Lzi@3L+whNQzL=VHSo`${+xmD)6jpMrafP z8w_4XI#(7v=y_dN)$abJp0-AWMl0EUFMaJ_d(n&Ud(jK-ed#OL*47Z91=yXs?b$RJ zF);xjus@wV@Y+`^3->+$1)IlD71k0$K-iylyEp1$1 z1cmbq0M&_=92BLigCr&~YX}`j1|Wr+Nf1#H#KbEis}yP_2V_&{Tv?XD;+(TKubU<* z*p_8k)iV(!B97p_2Npn;xNvn{C)$2mw5gDU1L87(wU&skUb}GQ$l9e#S3vBkTW+6C zTLn-zUXvUIf`u7SOa{@3Zkwm_C;iSs@xW$H+cL*qua}UoG%lDlu2TM$A`t*27Jphl zOYD{+dnL*k6Fkpmv%z4%9A~pxzu#vE>-{2v&4SJhOFnLPuOD9-*B%6=wZ*x|&V>gb zKduLZV<&ucF?vxcBoc@|c60zdhxB&DLV~CWH7G?|qtc*I6h$IldTuZ#p%V-ol~wh8 zcRrgu@5L|Py?8y(Y}&BRxT?T0aei>Z&aL0z=V1ijBL;(=ym}Qw>7Bo-8QxD+V z!->W-Qj%w8DCzwU$2M51B;4ddEcJDh(_Ly^r{6z#9dh~5UU+Kq_22fL)%k1R`xCGK zqGLx_jt;8M4Z2Qi>t}wH7ePnTKKRgi9RLDH3z!)@o7Y*{lNx&3o;tl+bNANwJ@_5} z;&m^6@ymYfKYsT;ciq}fNE>Olr)L{A*_*xpJ#Xz5C6l9|f{y`wUDqosD~;4jcy~J6 z1<-0b9`tiVCOPs^5*a{B7H%lC_+x+&be^SaG_AVy$69N9{oDsBE$wb^4RwE`#Ei|v z`74(%?6uN_&>Qt{x$FMte%XEZzv|U@zU;Mk9a$@T!$xo(VLF*yJHHhei7Qegn;Vqq zAh3|JrgN<^`D_0A*VfKo*`G_d4eE?n6xwUgQ$;i}k)-(|qPOX?$ zL~C8-JvQ7qzxY0xK`0?01WH6$7VZlQleyD^c$Rkf)^(Z;Eae5#YG>x;G!%Uhku1x6 zU;t>_Hp>kn8e_VyLsWyoAORfd6+#H(@wl$*UUB5=)ypq?`Cb3uAAQ4jefv-K`dQ$1 zWpy+7F133w!z?r2dxS-3NcLt2m+rzCggFRnguroH&C9Z!))jGb3tG@47TQQ5Fmtch z!^QMTTx_Ljo`D1)Dy6(&ij3CU5JH*dqs>h%K@Ha8)eA1`t!cS-@myxn)mvBVZuZDi zpLyron)kfp8~(|+Y@9w7nufuULhn5}U-mjUs4j^Z7v^fq)gdu@HyS}w`<;L#z&c^y0iuPSyl({X(Sns2j?!?h-dd<}n&|VGuV>9^J{-;0uj`w|O z=ubcVG5ww20XsMJ_Bpxjwj+aay;otTdLV%oi*&JmX_3AV5wI+DSd<}5uqyyCAnQYM z4*LGQEpEy7J)*t)-9ede*zxc1e z=gcG5o^#g`7*KArQ$miR0u_7{^2UZdXw&V_j4nFw(5TkCmBFyz&pvbR%EhN9|MK-e z^>6>pcf9^be(Kf}tKN}`p@nExkB=Jq*`NGDh!7P87j@`zt83?H`!k#8%xtwHt#sD! zs_Fi0e){;4alaRXTTJ&ZeGHQHgeH~#8e?r5D)HV+V2G#!Y5O86yCBSsY>&MCT;|sM=6n*Xd<5Ys<1s`@gnk zHlL*0Ck7FbJTDVuu+UpFMkE$Rk@#$BWBxmT_nSZXXCHFD-Ry0;IQ6apSg5N?WR!(F zDgb0zCXo;FL=j1tPM+sJhE$n`5Qf81*LB1iB8$l3ZgdKi2L~b@409tQ^THo8%Z>Nm zTC26nj5)u51xzotdv%cwp4>9eyQ}%oN6mBZ8LXXn&s%=;;mWl1qKDPShUvhbJxiT9RHGmTldU~xP2*HPx4+2m;FkA*ONGPPE zh&oi%9DeTSDUD!}U_1?`f>kTfBEbF?w5Vl z-~Fwh`N5C=+>hLS&+&FrD<{_KBIxL|O0?K+=ITLdyC{K489nY5PdxEN)7aZz_R=5y z%{RRIMR)(oo8A`U)vk9>Ilob(1sd)P{P+LzW7W2zTc zv1=PrI3Dy?Mgxsf+HyJMT5Cj9gh&uY5v6JG<$f!K!o&s@ED^7wv|44hfGd-ozw^59 z|I)Ae#+8$|^%+~@Sx5WR_K{0BxQhldrP(M=ri5G}b2L_CpwN0Dq@XnkYhWj_)nqvY z0fjlB2ZqSZ2&@4i0wRbK=_t2xuY*s1@}uLGfq`Pm4S=u{L>k%<*l25&5^eI<)k-Oj z*f}2~3L=q;F=(w>6iYxvl!(Hl6<`!WYpwU)f|C}Zsw!iZ2q#@Q0O*=F%Q9w;lAjUzC+=wNj}LN|0?rkyGXpqY!FR z3j{b>$1hlgxB#s~@FHC1d0tqn0(B6e<13fr%U(8l>hbkgzOui5dOn}`39fBjf9$DC z8ymOHrt@B1LzJ%Wvdn0mdAGH`Zh{9!h|vS8q%W{+dxIb4>&zP2?iOOD=8+9w}=Dyr_%-hSM^0Hmd-x1 z702|=-}U`6om3xs+i3Nek8I{MwVqiT%%zy`&tXzQ%Z+mYWFY_CV;8>TUw!Wv{k?BL zeeB2#=$)VX%$ZO8#l834!d*3Io9F%E@sq#shBrR-=tINd*tJbmio%?97CCxGrPO3H zwYpGB8zlr$d|Vm!tU^tQB7p3@PZy|KGR-0KLeo(zMAV9c7nGP9?ZPZNQ=Q9-fzJ52 z9liWTUp_p3`pzdXBf`NT%QuA-$vOxIM9=lM#{RFP4H6n4%@ zh!R;O2^f$9ieUj~#wd}4)gU6mfryMDA3!MuXsT*%ZI%~#WDfvYW~-|1x{d&iF?C%h z>KYM-5JOm)mZk(D`DUbHqOu%~Mx(Cl%ChXbZa6p+f}2ccCr=$|lbkS{%)|)5%nP%D z<>i;=0}o`z4lX<*@@+R93}TGHqDcWuzt@|0jVwtk31dEVSuS+N6TlooP)Y&Dwry=T z0)RArotBn!uG!za=dSzEJ3=8*Jg*HEzxS5k{nD@f=6hc8f)9NB!QOl~4{rbB6Kl7h zY9uHm0IKR)@UXGIUiPkWT&@>HmYA?xNaaSt?>MHZQ6dr|i5x-*7)4k!49mQh*oAIu zZ@ZnDBG|(E?skEbhQ7aUonVb zPI&)U|D6XPd8BvbwxSIU3`l&kD@?0CNxK0+;K;&6ltvx_L=k}?g0MK!gqn~N?I}iJ z1`r}7Kom$n1eksjV3UxMXXhr*2u#n27tg#?ZvLbrrL10{@Cu*>F&=^$lv1^qJ)Zh2 ze{*a9%>BH6yW-dsD+;}fHz$aHKuGsCiY;?eVe_jNz{PyY1}M>YER zg=Vtfed70i`@}6nfVOhX(UdEjA9~OG-}(06$&789DsWW70Tu*&4W6|Es}%+2koXj$ zLG`l?!>H8#@o1$#hKxY#7&*9r09q?d+EKy?pp{C@QKb|Rl2%!kaYQku^=;QU4Bh!_ zTa5=I7;0PgbCnww3CI!|odAkNP+9|ekN`jmNn#8^F_1#i)W#TD6hcH$>AZ=EKx8F? ztU-)|0G$G|>j65FLfAXzgx3V_g+S(nBi{Kkc^gME+6bL1avg8b( zhMv*|7B~iu3AIU3Yu9z6X)>8a=S9FtoOktbIKUVIAXTg>h%6HXY+V!B`_Ls$aJqUG z8EbOyVj|`O$bwoT(jeS>`fiY*Bf#>x5Y=cHxrbQg(8gh&iUMD~CS$K~J~60NimiUJ-0A|Pv}lvx{=xePIL zV6d%bXPMb(d8IRbFT_=U@ zx<zP!d6od$hA~Pr=U`>ieAVxq;o)Jir^3}{LAkro)Grd>MV+>jYgUGWoxWoSf@&Z#Q zK}kJqSwLvd-~laQa$Y_ToQ5zWqtTY(_uu))*f`ZM#>%$c6p_%_!V+^poqLun122?x z&teEMasn%33^B&Y0Tn3)h{zBX3K?a!PCNOmny1-30I2Ia)jJ2LH)$GVSr(&@(I~4} zdZ;AAl=Oh3aX{}?7X(%z}GQD082$^D%t@Yk9bDrnp;i&4m6o3-S-gRAy zP)ex;{SZc2D!(M@?7E&8MUmx(SpZlmEdrJ>27o-nt*d@*q(rM4{M!X)hGsz0=RaXF2nQDEuQd?-4w9F#WCh%C=|{>}f2 z$mc6?{`ZeXtWk`|);l*I{p2HC4}bFbUAI)75V{)eQ1##TyT7`%b!|Kvr^&Ze6Q=H_ zF~+$r71%MxL@v*=OslM*(6@Ots|Ycmg04k&hy(C$L4 z3Y`lfib$53Im6MF!YD*z1%Smdk|Bj!V`S3A5k#Xz0E)mdk^lpch(rafj7~jZrIgLG z>({TRlUWqSd_GT`f7`Z2-b;~1Ydste6^F)kK6E5D&x}H^te;NmyYIfY-_JkwsZTM} z_Rd6Wy7kuEU-8N>e)O>?j~zS85mH1*ffW(8F~%wmF%jNUGZ>RGrGT*uE7gP1WKs&P zO_@kh6cS=rH(8eLRdWW>*0{M#$|>oBcijR%PS>_ECbIXT1JPRBGys{|1v9v9+ZHwZ z8#3I0YagV0UtllaIQqf|=sh2wHFdQ=fJK4QiOsWVhAqzc#wc5Go4MFy3TVOTI#|a5DUZ1-5qz{I^Es5aeW(S_0;X} z&*yQwj?*jmoxaoOSL6K;01V^951KFj%PaQe$`_p8-jj2FJ`8zl0;HxjBwnn@0yF)5 z2mn1n!oI1%Gw()DS+h>Ai#IRg&l@|Qb^pOV3qs1_>;C`nct(ko?py%+f+(_}VQJ)u z`TWD08(M|X6-9Bh3bSi_ZS}eN#@hN1e)s&JeBbV@zoy%X*QaxiT|aMk>i53oO(%|> zHf-n8SX1`KtM7Tw2R``04~$n<+PV>uEX$JCVM=tBQd*m~by{m?NV8KJLg%&L0I zFi^c5B$9we1WfV{yg1Pk1(Q-z6c%2#Cq!6KOmOuajAaA5n@;k~M2?5g!{YW*Rv1B0 zZZ_bTRrex!k!8<*A)_S#s17~}EHQNw3cx+1e1wmE-~*%4&=7((S*|qEm6_ih8s-=S zn?egmPzv1Bpm&j_gCZ6GC{cEJ&m$!wmLM3m)5YwbH%lx2)Dv&C#S8;wTO z=`=NB>v?TfODl$&sKi%oyT4aI|K2+S@Iw!M+Br{XN8>?N&EEJMzj@)}m7_wE{V2n zdwH?7w@*Zi=|mACavr_+Nz~4JM3R)}E(CF#u8N2|m+(^i)a+w;Y5T4(xWAB()zit^ zaHG+xhqfu()gzm2-Gs{O>9kNrqXrh8L0OPD$@E4}WyFkH8KK^jozpeXoBM2dj5I|4z&X#?RafpW9P@J2v|d zJ~*?+&gOXH_Sxz&Rl5|F7lLZ3-VDI%|6>&&?1%ypfFc1TDZNA2&@-!&|5@H~`2Np} zr2Ky#|I=S%(Fm}ZL(>4{DbIuCRYkqEnbyT{eNIy9VOHv4FYNBNS6%;g-!#TqnA)tU z9g^y;^gi&LzrET+)91h~f-Z`+ix)5d_M3jiSnE5Nnx<**IuH;#pjQDvLJ+mqND&PX z$AFQY+nw%SK7Y}BU*vga7nhWzv0p(30Z!c*PQd3uv?Nf$Iq|-0ntEOtQj~bm$e66r zaKQ;60R^ZD6pBJhsFp$^5S3;=Sq?BMSsnn81_siVfuIl;z0%FKwGaO3pEvdN$lAJ( zA-ATgs?ate@X?_GMJQyou~sjHi+G-u8{nb{tKL6rG?dhFPVOBZh# zW70%Vp63ZPJs8wZr}dx#0SkIAYONFa+I3wjh*BQvz3;j%f^=Oso6Wl56Bb)lRns(k zdwZz@O7VyhX0xhkx}BZ9-QC^Y-QE5DeU6Z4WlMy15s?@B7~71rwq4z6ve&O%J$K=3Rqywr+;;yR zr=R=E@!Ezej>Pp7@TRw2`0*cVp$Y%}cZMJS_-}mw5C6`uz2%qQ_Pfqzxo9HIOJThz z3!IzuGlPmeJyU+xs$Oi0ifF#o^KWuEaU3Lqi!0=6NP zp!7<;ZQ+`;x7%vMW>92D_WhMfeHF8js)OH|42I>a?{)8(AQ;%x{3q}G$n4_BkKJ)s zJ-e<(YXdVHj`BDD#IJE12BTi8y3=)^+K@@+E77r%ELgf6l~Sa5XKQz-$}?*G!UdkcM#E#g225Rex={#5HwDqvH6 zBYaaiYte;=gPj24Vz?Y{rZdZ5=akAQzzq1)pM7|>?A5_(Qk{3bqRch*@;Iz%hKDx3?m+1_wCOw2T6vJ@%mx=!`T zfrLUDDNf^sX+KC)MJbY|Z}R{w*xTDn1&-D_4c?^6r6`J|9p@rP6hxfQ=hoWGm#;3+ zdJ&P-x^k(`DvBa6^180mZj`R2`Fy^9%gM~Uv!DH3yyceRsy&;HKk$yX>!WMcpFR}t z!!-FHLZik5zI1KWKSmnSkgGc%xNY|Vy~cjao3>;V_NKmX2vPlZGDDTDl zV6FB0RrH;6sxS~@x4N>?8=Ff{oVhSV&zAFHg|}x2Biuwu zmXQTMZ*ckFJpOl*ooC!H1OY%1L=X~I$Y4~te^$jhYC|fL>UG+iDXih3YjSAXv3e*Uq~{KY$e=Z&}CdP-HTAUKZFF%Sk}q7-K_`M%)->Pnq= zfD|`H1%ON`L(=Qzu4*=po|reaCS0fh3m_mJoEZU>g)ohRXMC@RzYYR`1ad8lVqO9= z3zk7}eyC}Ge}A^OU9OiFfg@v#fv_mEjn#g~9U{8m(PXPDV?u=hQIZ=Di;)%6h(qLR{P_LcRrX?t4E%9_xex$%A2*; zx1KuHHclz=zAMXeb93|Z<*Rv~r&Pu0EXx*|$l@(X6d58Ch-pw@k+TpQZR)yinkJED zQ_7j94O2Oj=XqHc(R+!klv-O`yL$C%Du8eN@3^R+sY*wYV5lYf_ z5NosR*Kd6BYhL^A|Nb+NKYXcN8xbf6w0mJog|eO2oeTMRKr!=MH_9U$SEthzA6ozr z$aLnl;(BguQSS%SFCu#pqscm96hu)BN|*(}d56A}=$fVi5+a&TXAo6cmV49{Ic1so z#3w)c*qIx*9652L+8Y)5(UWVcnBCet{{CwJO_Mz^{WpH<)2=*q_Os_+{lMD)`{O^l z_QDsx{O;TKxFYGZpkhjlL1c*pk)uYU z?>wV=`n*8N|7lD?L>7!3tU_TSj3j}SGPA4Dv&^7FKXf(eDLOy#7l89!H<>j11ViWB zbsAnY<{j^QM?UtVO!PgJcyxW^BcFcikACOvwkQA>3Oxt=7%qiB(iNOGsMIw~pX&I@ zJHF)~{loWu=#IDi%r6|jU5Kd-T@bL8MPdblh%pg80136QCqg7bfP=Ch0YF&*DKa~q zPH(&8_Kjn!*RO7mwF;0b8UX-8JUGP!2tptLlqAd3S!F)N>3;gb3UE0zvABl>dYPSv z?oZ$Kp0(4*$5}ZERRJcDs@uBrYz=j-^z)3#G%=RibUsApbSf6}2M_@ugrJGECX|RE z$`YZ>s%khKg%GkVGsd)S+tl@VJk0YP5sc9;IR|&0v3Yv68(WvxR*xJxb*ieW_kZw1 zqhZAiK!T)5iLwkz={(E(v8zSv*g;CrI^Wi9qqQ#b zusvx6q@JU9bnMvXeb0N<@s)mm+&5A0&ad^0qg^w<^T?@{W1BwO*24*-Pdx9ge1Ow4 z)dzm{SHJKlf275tJXnT@)~+n~MSAAmh6#KjV0rqKj}ZU>668c5#D#nm-x?<8Umr`qqjEY?F%poFJ#r?s`erro>>_5? zDeBqs*lOM%Dy|j%eqrojcx=q;1>LrJL@sXs?SK8R zS_scnt3 zt}8d2PDQ-;{>F{%bLY;qZEH2x}sPDB_YH&rbHq)i(_RX2UB2feax>$+`FAO@GN|CExXdJs{(@03~y$ZM@x z*oUAsdEWwY08g%)Po~>@+b8e6KU}|*uZ+)LdOSL%vSM%NY?+M*Yh@Lh+14~$89LWi zuC3=)RJb~@o+1$i^c;O;PXxd*Mj?>Qup;)}M+9w+$qQ{tt8;~V+#mMyLP&b09rleb zd{<3*-rauk=&Wn5UAnPb?*+!$bbBv!x#|D47u~zRx06}CaQ((}ZojvgH+S51&&MD7 z_}13W+PH|p0RRbg5G@cROY~ZhSZLXA)#hSYMbFLPEf zh-lJz0bK~*F-ooq?>j;jy>XokhE{89b24Tqp!2-9v9b9(Z~3h=Pd?cn46L<_j{Knp z*1^KZW7m0x-0%KoVA zqiYUM-Ob$~HQMB9V;F)E2_iwFBt;NLIQ%03k-z|nNTUHn@`!WxYop@vPd>7@bE9n= z*MzE`gHg^oC+7G#Lvo%FJXo!~1c=l&f`&Jb^{s*68X|Hb#vl#_07}&tX;3Q`H!KGf zTvb(_YYx72O{#0!*=)8y*{z#-hz$*g`pU>xsE^wf7kUq@917Hc_h7gl)VF+P= zf1iCYT7fJIv{VEkBCw1{gMPn%a7iarWjP4|h)4_AWkC+kY~Fe&%q%!uU!Cnvl+wM` zm9DCLqv3paI-l&fjoUnSR5?FBajczJ$B!S+v{6>=Z|_a3J&Q^iZIuS0wBxEJ0F-&C2jG5xTzBofYL9Fl z```yZ{I<9LzEL{$Wl+(fE$HU?N?)O|ISOPX9&DjvgV~S%*z2!1ah&(|ws*(JPQK@f zr)CX&>|>uHmR2EE-3WBo)nEjGi=_PQ5Ijb9Av%cAL|$>@T-s|=9JWun4sS|Yc*pa*MzFS1jUV8Y(4fmn6g*F z?HgOyuV1fRJ?^atwxCQ*I-!CGEQ`dx(`jbJC{Y+tqA-UDkPGtL}}$Oa`I(z zalqo&m?|Mamh{||uBX7Uj2&`te|nvpR;|lgj4-ATEM$!MsIZt`C{*R57G#hpA^LX4 z-Z|ekooBX9?W$>$^^L)jNLMvS@irgs?o5C4O>buA;h=|zRaG5ud!Ai_dG7(0LJFPh z4_3}Tb@m^8+dqHF%U^t9=LP_kS$X-~wchblk5Ber_oM&e;@Qi`8-H|^2V1WUXicFZ zZwswSv{4F(nE@3dD&nInE34}^ySNI(dk#6ekZ?e2f-BY!p=tpKXXo%gYBntoX_(B?4b5J3kv zk-gGUi58^|qFP%kZohr371uM!P0wWBH?GX>{%o6+)mm#oZ6FX%r_*D{jxqBBokbiD zheV{c%JXbI9*st$lP6F1dc6x5E)bFLtgs4!tsMksf~U3(ggF4v(o`@>;ivGCQjY^Y zl!F?c0FBa`6e6Z@P*v4rGC7b|NZ3#UZWkfR+Pbcnu*;$kc>VhIQ>RWLV%Id*8s|D| z4FP!9DgtAbQYuwa#weu_Br2sqWZ{WIs%e_7t)2Ci)hk!7Oxm{Sm1{>fR8f@U)uwCv zd5)k%?3g+4_uHL`LYT)RA}r)O_WzTkn|-tf~uusz+qzPr^Q zls;nF@11+_)0dyR(eIbexx}l@iahPUS(c}~7$DIdL=zYUMUa-WO6h?t_*RI>5E)_$ z#D{H|kw6GAp&{wD2$+`nMY;zM{iHWbaRHDI7etFphh*{4^7c*sC@$V;`79;^Af<#O z3NW(5a{4&vNMI2$*1p}}oy~U|*Lv@TsP39}t~NJTcPBoEuE={tdN3Lv-8}XizwzeF zm#>b;<0KRa0KCLg4*$LB#$YrAiOOg+d2IIAZ@uTIUjHL!Z%jxT5>lRJ)VqFd_wwc4 z*ZrG+_k+LqbCc;-PT}~`mGzafG*DV=2=mNn@*;v{qCg<-k$UViXCC-VU-4f*_^E&K zAAahook^H?JzW|{CJ1nwz!niz5h^Ro)C6$8m;cZ|UXb4$sh+3pmpcri9 z`)kAZf9NCA>7~^p$ELFykqNXh#&;cSeX_5b;3&_*1p*EVG*aveqri|kvbu8H?Z#%a zdKQ2|X}vxu)(QYm4$zTw+(9&}tE)<>s;bhA@@O=IC50qWLaM5o&1O|qO{RO3=^lc1 zyx;K+Ko=uhOG-P2I`xSX;FZ#W!12vqcYkR%kcsO)zo8C9ZXtidJUDrxvl{QSovX>i8%qit2 zrBD!@bKd)UUJV8V>*D6=TW)M`ftS76H0EVHpGi}pqQ-U6cOgo@DBbq%_|&oK_6{ip z!BgH_A1KhIkVq>$+=>-UVNoeeyY<0X_5pK8Bn42=s{eSrT<9@CQ0a-$H95Z83 zz_|{92^f%5k)art>BXUakZ#iS)37rUfLaENcM(2u=E66A(?2d=aJz)~qC4(Be)0qqR%-)oWKvm18UW2sZMMTT-${Zw6=gjWw~oR4$&x`ytwM+5u87OInvr2jYdkT zVi@wQudojo%RWa=MF*#Le(J}iZ)B-ZD+h2`%2^f+x6CGorUL*Tm##nwtW5}U%0uV# zxkPr(^@~y|)igCKB5j-xK$lu;3nEtaOdBdPqY!#UmRp;2okS$E=MY7t*UQs# zN<(Dn^N5JDMortbTW;Oo-IEZ{U%oPlad&&i2y*lq$rweXZ+Y&0S08?KaP&wsnN6qj z(6&bB#Fb-7LKX-L6{VSUAtY@{832fy*bpm>!f6g0BdRFMvN4Mh@Lpj;teM$>pgR zfh+Ts&0%r+_G8<-?$%rG9kxp&$Gg)1B)9uv9piR-^{+eCr=>UA#Oj3yQ*y zNh@Gch`ClOffdW^EwM3yZ_+X0(1D1HBIstB12?CZZ~mS<^9BH?=x}qNWC3{Q=9yTb zbg20R%W#yc$Aec$tL0#IAxFhYuNhbw>pNXP26i2d)uIq|8(dS(XLIL7S!Dspw_TB~ z&f2*#hK$+RSPNbF#b5Zjys%kjQqMR=Q#s^Bv-m(niC75$fU4K)ojdpBSAFB({>m@^ z;)l;%D~ci@qmLR;fpnU!QOe{F)W&dQw{E6N`u(8^h(chLAU92u=cNyUSbF8)=;n#9 z|BiogapJn!yq6cffSra;2jW1?L79V3au8e=r<#`oRtF~#`Dw8RF(FU~{35`_=>p9V z2_!Sf@bSO+^FiK62xXRU?@q0?5~H!X63nw0g~*dK6ws@rpGJ`Yiwv{|CBtJI8+YGb z9y<=JW9*e-Q0UQETf*o%&BnF!e!rJchUs+L@Apv=DzbT|wNa>Yn| zqCD#buZp4{f+$+hg%B|f2Oo@pF9OPv?Rhhs<9LuE1N&E2}e5#LUTC z8WV=XoNA)ZI}?OFV_*)EnOPxgVof0gk;uVog+@_ZH&?D)>Gy^>9Ih#yX}J-@&h^QW z@#^Mnx1BqC`PlJWpZ9_n{G+e=>YeQ?qoQYZMoL_qzUvR(esraW0?ll0bS5E^uy9Cd zsY>wywZIwY|5GiEEpsTwqx4F$i2uL;#hdz=r z0W5*@rFcR?HbR+>?r=W`fS2c-Q?`eariugrUiR@50q_{H7Z@YDP^z>>@ z!3J3=$OVs@P;j8AEX%5zXIZwszMgz}66BdyUDtJao~J<@?|ldX7^~Ubxt1AXl*#@q zt@-J6T2+l4n(j;M9F~|%f?Cq;15xV*GYwH9r?(3s)ODR|qU3OviX%vqdzw;qljk`Q zDy3ZTUDs7rwYjnG+b+rL7FhL^U>YNaf@X@eRG0M|x+tqBwXTBTKPa+Luw``~54qDs6zrBs4%(#07FIKd5s zM5G1LM{a#?3qUSXuEYC`{eRf4rBxkU!DMworQdiS07wfihK4LX*r7P!^o7JW_ z7_V&{d&_UV^|KFu`q;5!3wAbuL{3u7M5K^7L}eBNLPW$N7Fk}iKf1p8#1oJH!Jm9k zY_`?xjBG*7sEC-E09c~}3V}5`0(O2h>=}fvX%SI0^u68>5kz#ML^yB+mduvJ@%qd) z7Pa_*c9EW-W}Sp>4o!-~Qr4W1gi_{zDBTlLax=;wsD!2Jr{pjNl_`v(C}LOLxb^PS zzxr$M+&S~PTVDG7*x-ED3{)Os+r=OO#|Bqby^CUV6qU!|QS0DZgOq0_kU=uQtWay4 zxiBxw$Vd9I>4v-gg8a28{meK!uBuuDs;WX%A`)EeoXZRFU9eWC#!3i5kwGx2(P3<_ zEc3Q)NlB60`E)uQ4n;66X=d)aF7eZvrXeK)5kN0b#S(b|EdbV%)+&Uct>(ZnvLRuP z9e`45KA)!`<-Ip~Mu9QL;2J>i(Hm_zvTGVu_I7snjvYI(v$a(!(^=a@*22nW8tA~o zNRu+g071V$NCgq3Mz}TOm4VTqylqve(GSQ@8_(=|y`zJ?D>UrxUfF=#q7TQ9t#3X5 zzG`c0<>Zm=ix-Y;p5FP)_2PsOngW^7Be@9Vzj zwm1Lg?*7#I=)M2=M?dz)pZS0O`A_~>TUV+IthiLBRc#QlWQ2h*urQ%jf{C@VxDZ+3 zV8i2u1G1KzAz%rzE`A{em7B!bgZlOv81*v}p@W{aES?pg_UIuV=;tNdBG!lsq`(-1 z1Ur9HzzY`6F`}xQNmcK+ZP#>xNO$$bg04fTT$nepbM@xw;%9&Tjs5W`(eo3aO+=}4 zncg+I+a?QlK`44<;5Zlz&Oi0&H~z!#{A*wOioZB>Wu;d-q2MbGHcd4x39w?7@h;A0 zla28(FEf^)G>SsxMG6<4CyjZYN8z}n4jn#GpXa0^?{WFMBTY6 z&&otgN#fXAXUv$Ucy2;nF{Zp@Fz9iF7$h(3VlxjSEJ_hWBqHZL0C*pa(nQ1vsMSGw zvK&m;fmsu2qYDHtEDjKfeN2q%x~@;1JW-ZqnrJ7Y)QTq5T5IQmwV2qtS}WgmsW!3J zrr@=@x;hNhDL*s?DOIDtb>+wtpZ=IKW;7bL?KHMdM6x2^+1YvQsVBFBzwQ3}p8UvP z+<)q|Roxs}8HKh3+ZSfhPj4v*Ad3R@5(%VB&O{KJ&I3S<(K$eaQEGvW(5{I1(?uTLEv4f{TLlbdO^r?%;9zUHe$1hrmSS^2#`dC&Vk z^4U`SyauZ^V&$Yj&U&B;VgLXxh>@XVg#s3&TU?NIGiV$f`esV=^as?_=MNE|Iga@3 z1#och&FdcV^NNE!`*YlUUmYb7pwR?uw4FR0m}7*_g}P~ia8*w_?+Ej{tAKPH;%wfL zba5$Mv4w?&sKw2fkaHUk>xVE;&!Ec_r=M6vilaE}V z4zzS=a`E%v3Y$ryhAi>r%-HGPZZ)5pqig+sKh3bARazONuw_XwJqnOES*cB?p?;d8 z&EfO*jI%0F$H4w~j`Z9pflN_C(*||)NdNNo-kX2+mmYuYp!vftlw}cuw>C=}OV;N5`}<1UOzYWfMnt30h=``sDI%6- znQ9}^|>PeBu1%1WM)A~ZxlE>=X$+foK$ONzapr1_f=uCa*&s0 z(>dX|bA31OjX1pAybzI=6k^JKNXBQXT$`vVKYA`(*ykHjEK1PLY8ZlK6o z-&Dq!wy6`=43ZSO=Cetb75m%UJ2!58;j3T!!4H4t+#`>lICbyb*HPuVYqHD)_JKvAkmBGY zkR}a2%oJvDC@d`gU0&C8_;LpTU`{`WLENXcg!m@^?I!2#Fy}=C4oTrAZ-oOk?X#D} zGY;O^DI z*0syvV{gzWA|HZMiU0-C7!!r9(j?HfEf8TEEoV4<(hjS6KJ)xPD_;CB&KNAtw}=R; z9I%NMjK<})N&SW&{^5RE_sX%hs_Ps8_Ikb4_iejwXKykth(KIesL}&K+tivAK+KGi z=wtL&Yl#aFb3=+Wkx;|l2#O{{%K1oyWmz^&Ls}JO)^>GjCliq|CeJga)Ob7=ky9s+ zTVr@>S2&-~o2IF%86xiQ@9*qvC3J2!n20hXsiX^96G%4fqc(%GSI=jtRrF!rcI#!S zvRoHdVx+*u(1gxw22rH7)<_6SmKGHu1Q%Tz&i39fJu4{BO9B#PVqG_JK5I{&IMKH4 zbbpGFJ^IMw`+L*r6(IJGk+Xwq@AF=|i;4zV76Ikv^E^*0Cm}4%Y?QXz01Gf4J9gp|AOHA2{FZU>-_fiZf-Lo@-avR0$E5)(?Ve=iDy3J z@BLpylV=2xXWa9Q6!PGu1VINS1dxd*3uo*L6)(?eFjI?CeaYdrez8 z-=?Z_Fc=Jn!(?fC&~;8DLh19Q`ZsN-X?kEbo6YMwCTct`oP*L{g@}8*`=ik)nHe68 z3otVx@3mGTKB+E$l>qg#a%ge<#rbPs^iVUGjp+{vT z;IxJ^*VFyI)_W9)k@LLl^#)qoEGx>guTZay*R09gx@+q$5s~*M`(5yj>vpHJYnRXc zy?^vo?e&lLC=B}>gEE)A#L&@PyLRSi23m0k)CH}8jK0~DTav$Fp#*}6sBxjw0RZ^y z>or};hZX`RtCI8(9lG?BtKUM2atS9QCROeQO8jgQ)@L7$>ofoYvF+*k7V#dN^$@cF zZPf)pUDaLP1_{u#(A2>P6s29PNh@9Sm{gYMMQ-QwIT593QrC6rDdc$$EJ>}0nX{~? zwZ3-s#>tbn{MR@9mnSdp_VcV^DX{TS8gO0-fC7jLF)sR=Yz=K+zpNRCgRvsRfvqwM zSX(WD(>xkuGOUS~v{DPA{W74YbGm?957Nm0(*qC74KS+M?!iPC}xz#K^`NB1}7wh}3Pjva&L{Fg3<>Q8)x5vYHfPmf2)Sua)*bCZ*mGVvNzX zO}{MiVcB$HvcG%e*3&x*wys~(%G7NonLcsyRA^gGkOE)1zP5McI)v`%=E<&Vdqa~E z3)o12T{K1NENIqRnGB7MjEG2{-MrJvatMNi2R@LIV`#OF!oJ9QWtnx&{K)1gx*0Po zElCuDNTP(Qsf;l(cC* zhEzvrqn~R41F$ol9WfcA>Si-oI^z{ZarW%l#79Wx;Wp2#wa&T8WOB=`x1T-#*+2it zM_PociTOy8j}eiuW5|2Cj=o#ML`bOv4shktMN1H(+uqtDLXe0kL4+BAMN-z6JeVUh z13!I+^Kf8248DF|DEC>>Bt1zE%E}b%Fu=*v!}q=W6L0_3pI+S@E9hOh_E}PxXPGiZ zUDrw}L?&gOa|l#u4k3Wbqp&98C>lW}EJP_Zs%xWCDss?ThvHOeT}E-}BzPE(oJ{Q7Hfb zYxC5c77-!^0B!3;1hGh7U(BL47Da(VUFS6^K(?7qY{r93M=6zf{Q!_PJtTj)3>=Td|ad)RINmHzwP?VTv z;b3|3c<)Jw1({iKWRRw<6vR}uX|27hbCXwoMwlrK{j$d#=JUC+r7(_1t1-s9nJ-}J zW(ENBNdo{b_z=SM+Kt(~sylb}`n4~9;7ecgb>DFA?4@t|o;RQBz4@=c_Rc%+y!EwT z`jz&USFRkp-<9j0rPt0m#M*gN4k(0!=FH(MIh}J&km&YwD60VxxT$>bG*o7hVbW40 z;UGo?U|1GKi)X=dXb7M7#*3u|h|d<<1&C1$2uN@Yw%%1XGq#Lg1V*;6wsNM8b4Y?rdG}_iT**!i950)GJEN zOtrhKw6>sQjG|Bo3!63j^R9C`v%YCRZ~N@?;^OeEuzqlx06C49!$L?og_Yx{*8lje z?|T0q{KoOyj_=>Nj*)lguFOlVHG=rgfk90IAZ^=<$avJ_*+OV0y#gy@X74=__*DKN zE@tW)5Q(TD*Gi50SzUM0LBN##iAbV{&F6FHoK+eTqi}koglDBRp>5k`nhf*ltZ7}A zWdNj!*dKAQ^TGN`BjB>VwrWLBZ!0&d${5?&i?8ElYH* z2QC081fEQ$M6^Gd04x+_P}#O^TNiyBcK4^3E?vos^6I6nKl{Vq{^|ev1{C-o?|sh` zk6(D#@BHe|{^xhU>>ZzZ#q)a4fAK5sfAN=W-tkH{8~u$ji_B-!*hP?=RnIemi;jW_ zm=@0A(1W;8#KPs6hKK;71_S{X5P^uY5Y$~7_kn;~iiQgcASP2$ILMb|G2(#;F|x2E zjf~||5hc+leJve%My}PVV}+w z0t%(7Nf!PGNdtiyVIik@Fm9Lriq1ysYyh+*DhA*XM6aJ*fA1gt>7Ts$r|!7_MZ5c3 zplv7J)l27A`gz+{>^e{+9;Ih`hS*ou=0nx(a@2W5-NO(wgD%F1VKh3*LfV)vR7f$; zbTe;Am0-KtP2gSQuNDWn$3*U~pZgbgqq{R9)AKl!1ri)oNBNgYnY;|It9K*clTE>PtLz!^LTy!O2@uu)!vmYg;p0v>waUp zA}ANR50RW|3}(09cK%Zj-~OsEdgSqkRz@#0eHAAi>)e1zwk_;DWf@Dd`$AHJVgMC` z07q+72s)td4TiCGQ>q5z7*(jQ&NSp#uigL*v*~u^u60gpTV!Qj*Ij65)%?cxZtJ@3 zo!QpTt`9PqcRRb=4C<3lJhAglf9v(X{^q~*+Q0nOf4Tcx|KRUjd+c-Xe&@UY(~o{{ z?*qSos`s{APud4w`DHJB>DLWU-nntd-S*TdwyvF3uIm&aV@?`HDv}J6I^45SKw07l zA`7cwM2sS70w@L419bNwSRD>ph&Lry7q3h=M>Aoer9`PvTD(z$d}2VQLqyHGnLW;3 z5z(@QrfZ$Y&UGS)N-G6*-TKgZA4A$}jVg-5`L?d>;b4P7QYqW(^)6q&oMo9N<($(- zhrpOg2w`_``pysjW>v9|p)ff!%M$QU>bAGtax1e>q68{3fapALnr6DUyRos+RQ3LR zYKy#aoz!(ICzVn{K>ZES*9lWqJ!x>Yi-fk zDsOsDi)5{M0d@N1>L0)ToqzOOzkK)YcXMdQeRKZCRIY3{Zl3ox3UaN;+GsT2ore%S zY0##~bZ#`FBGjZ((Mp7*F;4cbdw0wrYE}3cJ2yiVWd$7=!ZI&rQ)g{Xx(+RSb_Ag) ziY&`__I8z0X@*#9ox0Ac{!bE+seLOVS(epxJsjA6zdxHzjLA~m1XzeCB?u;jV2ntX?YMRD-pLle2T_-XmGatB%1n29zF~(#?!A;A| zfEYj!B=|r?xmJ_C{mqT_+1~EFYiyBstxv3Q@1jwd^^DiC_1<>06J1UyZCK5V`!?6E z)Lq?$yf@DK>&?VJ_SoZhY#c9DKLT_vFtdc%#b}jrO?&*-(@#D0P>XEFtJmf?2vN|s z-WAGeB2YwHC7+<#&;OPeJntX>%kSQuPXFou^(~jGvk$)coz;K&tyjJJIrlvO3+}!53ywecl_&1K zXRr<-glgKwc~j`DV$_=SA#mhrG392KjtAlez|5ivWWgIx5#n%SpVrLbqUPo+^G)|= zOsu$rfurRCA__908h}&-&F%#cnj$zaolmw%=v|CKqDTlVjLH<~8WwI{ht0gtOhmI; zMH)|?I+f@7`Sa(?vYhTuD9;g*qu1J$gZ_ml&i?x!{izq-f6s>>zr4EA2L?ps7zNO3 z6Jv}%v9r)Ki$)8&b8SAf)A??*x4+TT2fZGnEvPghsUSo~$`k8#OR2H<9d#mVEt-+I$;edrI~a{AP%SyyAA z45_Mi^8Tn);#}RiW;h&7CKF^))^^@c+wR2rF|8u|l#F^1iHS17fgo3aF?bdS5MuOz z#6l80utXn>?!_Q15SdYU56r)GMdDY#-%DKGc--79CoeuE?R4iRRB<5zEYNDQ537I ztIoNqst_^hu%wHsD2mx^=Dp9e%qkt?f*`83ZbJYCWm#Gd^I1)R+FGSlS(ekLiVKfn z=$dZ0F&OlE+uJt+Lu0XM8q{P-Uv1|b>!YIHUp=|Hvv<)9MGy1JJKNK>ufc;uH$Hak zvS4gFAw@*!f?XZ2^m@lPkL)uJ=U4OMw!N*G5Bo&IOU!^nBt_r?p)j)sl|;`Yz(PC- zg_HmRDVSGvHNg<7@u=T*&9DB(uU&ZRss3=#`oK~8MgPW)?P}hbJnvj=e0S~4c3uo_ zT)o=1Ehqp|-o@wK`+{Ho^`F1<1uyx=fA9~^oxgnLs?WI7Wxi&6fAGg|=>h!KTYvZc zAAa|ffA;6U{bRpy`t&cHICA=y(|5o0wO{eFzw))?@%YFs>$@Evq2RsO&#sG7SfQs& zavj_b%*)?ING+bl!$3=BEi94RXW+vrgIR9Ni(b+J7|Z}EYIWX0vBL99vAI#(vGLiw zn<;JEuJPUjkk;hdF150&c|EUNt@D1~BV$|$>EmszA0^b+uHKl>=P1zY^^$qGBDQvT zc_p zF-8`*xw?Ax($%8SA`(TAg%l95XD>dWwY1RFPk$^NoEBQLg3_=AV>+eD_m-6+08xq% z!Fy+|^%6lD6`dz_+wuNy{QB>F^bddM`01mq>q3Yqw0-@?#_B+6YU^s;@1yF?>$>Z@ zQd`${E33n6)4I@}FhETFhzLqS1R_)*8%K^7MN!v{aL|TK(Be6OvPBMc#lSH}=c4ai zkq?bv+jKkIyDo0{%x4W6qy@%U-_-AtYe9-|r{Y{*<09WOR)wilS=jx~`*& zpeXfm(vVSrC=wwuZ~(F~hHE!&*vzI4Lu*4s665avByiL=kKR#YYv-!%e3|$D+6!J5 zu3cSw&6n4c#&>&E6rq~M+PSiYA?sjel2Ld(udum$|L|>6!D5$}*XQ)tEkt zjmgy;L;?sY0;K|BWYS$A2tk-bj8Om*7nzjCZrY(%{hk?CipaHfbL{wum12ZSh3H}o zZ9Olo_8gVcXRho|FJA!2&tJT#t+CmVrHX-K0f99_|LDnEr4=z*X3Boh&UIIs9lL8C zc7N$BzUHg`#@C*?_SD5QS0DQ1BX9hP?+)!lkDqz$9l!DaKJemKpMKG6?t0M+_u_DK zb5k!GI?Kp$@b3~3K|It3f;1|+2r-BC=oZW_g&+dLfUq!^C&E-nsb_7-iz*Pp(qN2=zVwN#&%I;X4i~{{gsuK$z(E}PFGh~4=$}VOi&a> zp6A2iaCdh%NhM@i1^}ro7~2{E+qMlMkYL@mL`Z|4MU~s!h1k|LX$=7Lx<$mCFauF# zPRN#^ZQOCyD?*rF+aC4Hi(K~yMLV07nOf`jrqft;H5hYbbKOn%x%1W*!Lb8%{Lb5| zv*%9U_uS#+@hIqg9QL%Z6AMU604T76h~fc2hsZ_%nDBs7K!nys*S2kD?4<5G*SL06 z&8HEoyYIex_wv=&yWQPgM9gge+Lg)H&hDd+Kf!r6pVjkv5=CMgl2D@}vbLN}uRQSF zyI=jquQ_{hPpil@uO0B9SQ+-RD2!dMP)E4s`0aNdyX~*P>Q%4$vcLWD4}ah<{`eQm zQU3N%e0=kBpFZ}7<6ryT4?p+0_a9Q;KXe>qo(bm-X#hEemCwiIqC<^Q0t5mlN=bCfiU5ei~9vYIoSxy-c1^QIgG8Oht%>$!iEyOlKw@+5UR3V-B`r|G@%m^CITX|Ht#|dX|vp1xx8oWD1@L;S)&Gnaoe^#J3FJ5anm&YL75VP-QC?Z zVv{&5-upC3xU;j9`lE@b*Se0G`~5x$rD@mIdwXwhudZw7 zx3z2S` zR%xZR^4=@N5CV!AW0EC@1D7ChPr9n&bRgTE2q7W%6jj!yjMKu&e@Zfd3b#FSgwRXi9^W)dLPgj zP#_8eCAJrmp;*s3IQBk77h;SYnIU)}A_OIbHd7EuDJ2|swzkh-x}r1N8x3cZ>Go{< z*lou<kHt2t6nBMV^~p<-B=2O|SX z^4}uSs4T15rH2{;3MgbkoMl-)o$R{0(U@6lBX{hBCMfdE)h@;eKrzN-1UsKkwbotJ zYOM!@!DKSgT5D^vEIW7R$(OzQOTPW@f726JrfcIoxJ=PpGXzYUcn|}N$YKs(sR%K~ zsEn~d9U>uY-?*MD%*tYSe-8k(QoTGofA*YkEQ-ukomL8gAVMS(X5ZAK@oJr9v6emc zXTwhCtgMViqxpQE)NoTHIW>%9*D_!f(L~V)g@gjpcRI7la6^+u=aclV zCGA2ej0qe?q9#xpbr7XgG^At9Rc>j`vYezqM z{@SS{$70#;-l%WA>JFaRs$jyZtVB8tHTp#p4xd=_uz~M#iqRJCx3SJ#Of>WzxzM^ z=byOk-jgqT#fx_yf8@%iXD@x#OP_n|Uq5^PvtRd3f93D}^V3I&lw1UU+%mbL}jBj2xXTm-#}*ci3aw)?Gj3S?z8n(Ws^$_FL_5%Inw zLanuDZrg6K)a*&bO|5mhI(-aTmUZ5Do&W9Me#`!>1?+rRf}Sa#1G)53Au=JL(!vs= zS4s&-W%Iy+m`js+=1W&ETG=W%E#~6SamMbaqe8#?+s_Jw$?%n+dGpZ!@{|}G>d?!SwJK*2a{)+ z0gh3l7F~v@B6?O5#Q_6w@Gca^Kv$~iS};X5tJ|g?4o4|cG)=RzvVsUAL_~wZAdzd^ zrcLha#+dnho)nwXnop7?sfiv#M8vA9+SY~Ok_dLfdJ*y5nX_was~Z~|$B!RBdUW#+ zf5+pGKT+59XjqhG=|iWrc5T$*mkEHhDZo!Mlf z+IB9P()Y4V382zIs8B6L>O#zn6%i94YEY$tRuI^_&~f+VgJb)e~#=j(z?ur^5a;&-zBVC9x3+ zkO4?5(He~9Bs$}gHBXvjl^8us5RtBHGSg%4IT}_~WHy%AyDJxVx3_n%-`LsTpWe8> zePerjFdnhU+PHV^TGLy|$Y&n?$VWc5zP_&2GdjV`+ffo%cBx_uAj~Ahcz^={95i%92qm$_ z5D|bA8D^o8anSTZz$6(jtVvKvSU_Q7Wf??ZCg^mz($>w^wMRx9Cz))^Q!AFSGp3Xf z6#_t1gb=({uEo|x6g>G6+k)@3tdmi-qfumL!kgS%h7+ikhk+=Mh-`_a0`uI~9 z2BR^v2Qf?@(GWzX4k&6#JqU#(q89cUX~u?hui2lwYfomGDf0olmK#^(#%qZVRE&m< zGb*;7Aq42G@9%|LTp%K5Nw3c=DQ!VS0z!fqqXHCxm@IEth)5JdaGBC|$C-uGr#9aA zz7Krh_uoc&kh&SHo~mY73#F@ur+YW5&i7GD-ySWd_ulvNKYr4M`Q(}{wV#{*V0UL{ zeE`%O*L5}Rrt_|`{eGw-+CC^k2wGe3jYT`i3bYg&(YfXZNd!?bE9AW9d2Wl4ad4rT zI@2qQ%7tK8$E#IUu@8Bc=^Tee&vRtv%p@KPIOnn=2LL4qKzW%t=UniuYy18FvCU(X z$=+;MiOLWp%Zj?LbDIT)v-u>;&3JX#@AoE?$<+HRSFWBve`#gq+%31S}YP^d|6{Lb#R;(1X zuFGFo9nwrPoBJfefRz6ZWxnS z)z+zF8!vy+E2L_7E9cIi+4b)J`<~ZLXM>(Gy6;clzW2!E!z>Uh4d^f^woIuuP{M-2 zLj;AIPxp0ZLNdE(B^hUwbgJe?|T17cem@$K6Uog@wLYu zyZ-#=9Qo^C^Oe8(`|tk7uYB2{*t~fD)BpAde&8Q|>koa`xBaC%ZoBtA@A`{>`K|Z< zm*1Ul0KnQ8s?{gfc0T^4wj#0Xn#6zr0EJ|fHKdGI!pUtnrQx<|owat*>j#YDV;iAVF)!E8pLyhK zzUCXg@~gk{-1)16!Qh|<(OMs9{2tUJhdwh{rJ18=>6h8wWH#H|wRzF8tDAjxtQ9hF zKy5Njo*;PA*pUQbB2ofv+eU?lCo`Y^11Vh(!5ghO1_U7l6sJ+wA*}S`>gLKDe)-q` z=*>TW$6cGfqHm0Jvg1N~?dtXE&aRr(E86>Mm1Xq{?zn3&yZbW_{b@fp)XgP;2|hz* zjM?AWo^I_NA00imvTn5m&xomgM`>yXLG^m2CY4()kvWE@X#g<|1jEu&Bjr%ZX|&(( zr#Zd;Xqd_k&oS9tWSK!+_^790<-PqKR3P3PAObwHezd9G)hpXggT{Ri9Qe-r08ND0_C|SQ+KV44rGCIi3lPn1XgGSt)LeM ztt_bw6(bQ*vKFw`B8knkCfMEG-rC)I)vI3#z(tXdMuYdh_uZE+Ua@)h-VeOO z@4x>$ANbSn|L3p%q3`?Y-RZ?|_zn`VO~3bcI(gfl|Ka=J_})KXZ9n(myMM1( zAOFBV`@z_5fAwGg#!o%?_;-KLcRc>YL*MjGf4i;a_kaKGr*Ap_UElRx3B!8b>t6TZ zgAd+&&;4(D)0;l~nTJoGJT+Qdy?p(e#-y%%Ga@XhD4ZDoH3HA@pqjx%k*_8`fhDBjw%v$f2$u&Fg8~|vgRMJ{t50Mer zC^U+qw%Qg*Dlo6F9l3t>DgiP|t}#Fs>q%Lz{>rcX(rnsjYxt1i@j;>SjL*Dy6e6M_ z<;7ty*Pq+D-c9xgh3;!bO2jDCkfHWb2V{@vqemjeM3nS~W8*q~sAhaNI-U?&rBo{1 zM8tbv7+nQe8?5*q{p^4FZ(E=F*j@MDKVDs%Zr4p!k0^q8#90ic`s@pv3K?(Xim;F-B7iV%YL{>YJyrfC`n z0=5eWO?OMWn9xyQXQHCRx~`qR0^mM5GPgpi+QU*J%K3g9a2* zD7x&Scg)NRk$vp1Xm*S!F?vNxBr1)m_D~2$FN~RcMdrN^olBtdx%20NP-nI%GNnvY zO`rF?=k?0b8{YK?U-82Gv&k;pb@zYygFpPy7d>!p>+1Z<#UK8;U;n9}IPqow^mLvQkRm}lVfkxMQEfJG$G5Q$9AQJ(xWJ+0W zTnvFD`=BHhhEFp>S==KLB?5*eIx zGlM{k!W`Hrt&of{S*|#EYc#T?0pir?ZT-TQFrBw#Z|u4Tkh`uMjD}6qq~xV6%XwW* zCKDh`&vjj}Cf8c;@9)oMv)a{N@FvrUJR0O1tAn+k-5BK2#f#_9f8;MdDba7PjaNs# zer}t|{bhxaR7z_dy+f2dFWR>CK6b8A8q)|B0S~f* z37Ifc+5iQPUGRn>&+`ynW)_OsT2s=L$cmDPoO8yi5JH~kpZ(n9S#Nav@r_UK-uPt1 z+V&rK$@AXx-nZ_be=@V>@uB(1``+Kqrneto=~_)XLj%8h~5PdL{u^Q z5FLU_&d`KxbnZM9MKK!NAUg)3ordS1T>0XAil?scZ|L}%=bZSt|N6$CeEl!Wu=h(p z|6jx0f7weP_>DKb>!a`e_<#7ZuekQPhaZZ!O;-QLw|)0-zx~rIzxA$<{^9ML;u~N3vKRm3Z~1$l{p@ERdhn5rjm-~z=+D3I>%U=j zedD3ee0n;ae%Y6O$s>Wxhv;gbXD?XmqR@yS?cmjWYmf|Xsu?m*?4_??b77Y2j6}C=rMxAhq_<(5_v(h4&+6Iqku-E zlz45Fn$2hZUhYFatLCFydm>RFYEq6nZh7t4@iUJ-{MWwvoBpqF_#020x!NC%4lE-M z^plz6({r(>WfP2oi>`3Ee&t-DNo$>DSzUQ!G0m!L!w6Q}Hm}K(L;^v`fHEQWjx5o_ zsbsm&AA0?R$|AK(0JyH3m5mL^)i3S$DRcjfwabNSk-FZ!ZC`1s>o@13-3*KeG?`|i8ie(AZ{*_#gYVYPEz0YC5F+uCj> z$sJ88ACh#VE3LJLL>3az)>;-9#77WowHgd&^Crf~oK~gIa}_565wBdi^8Y97Kf^4& zigSOss#bW?4xJ}xqDC{y8YLtFlE}$`uz_O>Fwq!HIGA99i4Np!q5vbJKtxF(6p>I4 zqd21p8cmKpop*e~O7;J+M?%==df)w_ukQWyzIumVwQ5z>{kx5`(o|ZjcDMTkY^m4l zA&5pj>GgVp|FKxdNTJ!R56ZN*v{h*i?Ag<4bsazw$wdlRT{Hk z+6xP98AnN)DWyosM5ruFPdMTD6{}V(EG!&4ba-mIv;TmNG-<5|@O|b`tJRc@n3=1* z0s}yqih`+fWMne6$BzjP8mqHAZ zvH;RCNU4JdV+;sdXty8U_{dA2d%?9E?(#`;&%L*gj5bbv`qQea_~NDCJ#FoAtCz0) z=?}m0_NSgLqLwzcM~RA^pac;aLI{D`Bgg^GlmvPwgBL^t#jFV4hu{EmQ?SsHOpPha zMAWhLONJdeJ~TI+bYj}=g-shb?Avi*&2yf1_uU(Az3r~~xtTN0 zSU){AdG9?pb-Tq&Uh-Fa_Uw7+p@$A1J{$s!k1yJ~b*q%rDGOB<@`+s|_{TrF zbaG}PN)vCL(rS>Z4B|@&0Z}~hs{hAp)5=9 z$TNFI(z@U(Lk&q%7-JOuW%cSwz2pHcZE(N$M`bU=!^4i?3-A9(Ilqe{b^k*TZ@Bf2 zh56p=-u8xb*RP$ZkMDZ))-m+8k;d?7&Gu|(;h-^`C2F#nw5IkwWBuB*R!r0X7b|OU69_EOdqfa| z$axqPgGiEas#X_$-?7HePx(;lpo*pA_{V$nnWt2r_=lAr{Mwx-9{aMF{PnYLxhMJK z6*o$>Ro7qp#|>Zk&LzM4&E+#+`_pX?wpw)WgAd>N-5-4SqyP3FTXt{x*_HqO-LGG^ z?UC#L>7U-bVZ(+iuef^CriYg;pSa<=8y|jXGYN1$zw55M0N{~F9;vEoVq&7QrkLv^ z;>6f^r_+&7UQ172QBT~@kB{LF4$n9cyF6($_kV<>Cr70x*3y$tMMtj^u!BJ}SPEoj zZ|u@#i@*4lZzpbMxDE$fF2;VpB7qKtutgsoc>1D_c|@!{cl$jHR$vrIzA@x1$eDv&`om9KeD00| zEnE6C&${sXo9@~A$fo;ly6qKjdFP{>?pt!qc-flY`QV;wCih%$_Q_o{Ghejtx$Exj zkM4TPQ_jBqH}`FQaL4pq=h2C+QozCII zhsVdqoio!%rjc=cVpN95ojRV#%lUDGxTEWTU{&*Y*#Y^9dc@$Q69x|*5cdB}X*udg z@ua}?q#FPPV5x{0Ebsupvvld=`|p2f>!ycZdErUZTd$SF#|VZXD$@gyg#ynA90smT zft^F=&@(fd!XTqHzSrx~$Vd{UQIu3gIXXVFbK9e@dBYoDanTEQ?4M3kVOdacPf+-o zIoKez7>wcm`SW{HOeCQ^Iu1feDoWE|nA2J+fmsyy^R^>W0w{RlND<}E3qZ#KBPpZ^ z&V%zv?C@ym>WQ})o-_q#%1S5+0L~fbTvE%v{`IdXSnb=_-nQ|YGtM}D&vd)PhtD{9 z{gv*jNAB9Vc+K*85uZ35+6%o$cFe9n1|}>2rw1nA{+bK-7QA9bx_o?sy~ixxcW~dz z;Ux$5&a7NAs!30E4ucx%_1cRgZO9J1IF$rsp_I~*1OTNJKoC+oZ-tV<8)iR%bsA$x z>3Y33IcJzzA{G5^lr|yoK-5#O*S)XGyhxH{Vth0N-(HxTnVHP<+z&pbgfPaCQpSZONm%RLTi-7liHdXrgh{NBU?Pi3=gR^{E z11(zGn3|qfb2E)bLkyEK)k3=+MNyGg&beBeF%TlMXIq##YqKaxb?l9GmYc(=^`KM~ zeBYS>5kyjX1du{IL4Yh!L@H6zc_e})PI{fLb8ddVWe31I5uzyBy>s&X^UnbYDDf%h zp5wh=I+4nxSYU)z&LOz zWsiZ`6QT1}a0N0ENe+ecg)xSdtBjLUv4Jd2<38AmOIN0GykYbF^^-AX;;P$jefXY7 zEkj=>vFvW!vm*)WCqMb&)bzm%pL+JhXl>Vyoy(W6y6oq_O_&ZJocjJFKdohA&D!NN zyLL=m>g97stF3bM$U43v>91W?1T8%%N&LGL7Z6F>fM~^Ni z0%Y(NAR<|pXVGxKmH*_2-(J1uSS${*kToHF%DNJidJLI`9y>=Qd_e1&NX8OeSyjP0 zfs9&6tjZp+6bj~BGiefaTHVFVS6upoOQ+_0LLh<=Y=H*+gQG832xJcg0)j)J$ES^t zk_nFf$P~CrqoK*EX|QboQZh;FD&N4vLg z6+g0bkMdQmk^JVKIqG#+Ez!H@`wvd`o_#F8`zx2e_O%~afBf;YeP{Ejp*~5n)$FXZ zVfU^B6GPELCA(2MiY%O?C`K~m5+X8KmSr!~!+_2?aJEDU!~lVV=gNW&=)rml&O*`c zR-k2w3v^1zevzB1&{7ThIQ3>-3FRu&o1dOrXjj&GW~nHOqSE$cBru=^V0+ZX&a+2} z5Fio}5dlyLA<{}J0(7L*ApPNh-6HhLJWFF!87)aFQlh9aXraeaeRyHEGwYVDSZsUU zR;!govDVSh@F+PKGj&}RJQ1R^PLl8rg9)9ouxY4kT_1^zb+Ojo^(BWa(OOUyDaGue z=$9@d6%g7}^BvG2?Qlwa4$Z7N;kcPY`_Df6)Tci4yxae{;oJ+(dhp@hANkPPTBJ~q zkDXZYxo>>!wSWJ573S}`YvYB_yx@*|?tgUq{=a+GOHWz9?tTCK$sHPA^2XO5J1+a3 zt7^4I0Fc~5R}b2SAf(nl5Q9gwlBD+l6-P2N(gEA5ESUvCB7|ozgzVIkkxEUK6zAQt z@=C>==j!*{51n$_iS7B}%YSndmEnR5&nUYK58Sh@eWV*Nnb^GLQ4e_EUH5qJ<7B9w zWe_lF)`@8C>SK59+A%+~n}`_0?A$y69J^-q^5x4nZrr&1xRtF|>$xxfo43FH9Wyg? zOP4Hr%Gpn8wL8sb0|7jPF?`fE?a3_2qbbH;T5kM#qkXcq9zN-t@WihWVt67$3Gle~ z|8W`JV=EXQkG}H7Z)9m4M_Sl6gAW0u6C6N<644pwg%F+%4CW@lb@M(s z+wWC@WNEw)BtUWyMebo(CaE~MbJxwc-i`pBZYxP^IG`wjcv9(SkedA&{D3%MfDhO* zM`J8O@XSdh=I7`0&O$?KA}abN(NQ=%Fhk(rJU%`;=#Ogo!%==BKvflKni7$FQhC@~ zE0h!{*%nczl9AC{?|NX%!yBgdJUl-)d)i57V3e$mm$xUUn! zBQe`or4mU1Cj^3^;DVHLV1dlR5AabOf|QckBSD&`uIx%lLU58utqI9`Ym>AgJWeb> zW?^pLdz;3(-ELPTJFPYZ6Dd7lDWRaMs-)2UUO$Qi5gFr{AxRQvD(`&~#Q{8X5UB7d zp%MutF#@v-KGiA&hYY2u2CN@rOhZIodU6~gt(q7=-0}za?QAYtrU4Cv04NbuJ*y<7 zrC<(4QAh;0_;};h7+JM?9 z-3;z1THYEDQnCjn$vNj8M}nL;Ap``LQUY*g3^OByI7%ziE3J#-EKY2%;{+i>_oS8U zPG2{2&!&A7jZ}=dt(*5$zPjzH~({_>Z-{Nl}o?5hMQM=t985%M+prn4UIyALFMBPI(Wm!tC znOXjiqYD3Jh2{Uz8G%1{O#Y(A`DeT4fgb2_u*zV)K5j|(L@T$ll~5YVRynU;vG}U1 zuG_V1%d%x-TG-G#f}}yjgCv53GtPt%*t-yd_dc*2NS1`uNdlGUD3-QU3U86Tcjd@% z{ecG``rwB?bioDZ@7;GW%Q7S}uml;be0U-odEz+^>?AN89TNaQc%`;7NlkZ)GLC3= zW|D$;j7cr&nX>Y(@K%!mDIf$>T95*~2c|)=cFrNAHo zp94yPgSxdFm>DAlgu+$~Aq3a&7e0VB!C7|J8iN2q2=um$bTV~le^r!Gl1?9<6iSh> zO0X9s1tAJ+ZJ8HRD_2xX(ctY>N>-H%p@_ATB)s=Rf|L>k;o!gtV&Jvf;}>(74X+gTmn`o?#CebB#TDJm#fe#P0=eNRc^TdzRjCAA9KtxtyXJfWaNSi zE+C`;)G5p}p8YHaXt#U4-dwF#J2E>@M7#D+BI3~|H%VqB9o6E*IfIDuFH(~K)n@*G z1Q!}i@57V6{zU}gqvogd_%9s2iu~s(H~|5i1da0f`4n1322L0bpV&ZB?O=65+s}UFdB9>4PQD6j%dACcVrp{zSts3j=bb}o zEwJqM+6h+Dly!{zvVL&;o)cHDJbBfUjgM>{8(Q?pq4_L;L|VK!9T|!Kc>lf&&pV~; zWyg%9buBtQ6)Bj{yNS|ECWf~kJW_~sF7NR6&CgkcWvjw^j41r$08t{6NCL@mU<^nA z9tNvj2w`odl=8j=AY*J%@zGpkrZAP2q*kt6Srk=ORYku$JUm>5LPaVtn4%yefk;Fy z1ji7Gqh#+@tCr7BO&58UCK?evghU>-iBLp{M-R=$S*@gJr<4EyC6I%c0Hj>R(tslf z5vo*33;oq~-H76Y^E0;FY4>JDnmNEmqd`p#U5a8}SeOeT%y)ZFd)m2o-F5GAYge8B z^mFguaQDkz_R@adZZxv>r=4@lZFm0TTVM6l%YOZ$i(YiX+SNb#{?AW+$;Frb;@c;j zcCgJY$1mLX;LY!N*E0}vDq z?7mD@34p*(h=?H^b(jYrU|W?-RxO&F>HX^0zuL2H!?DLNaaidn>n`M6Gjo+wWwm^D zv+VVU1CG>(8>TvKk(g?&=qyZUS;`QW9n(CxP~Ej@-}5e7ow7)3ad%!Nqy1W()UejA zTHg6~cY3s`re=GIFXA)>%fY3J$hjaCNK%zGnZ`6ukboEnsS1Gylos&dU95D^bQpp_ z%(5(mjt^8-)?1%DH#(H|dimt!q>8lEvGGny$p~dp##&ozbsPf;Q#p^s%*I$|Xf~UR z$0r`R|A9D;U1bo^dqxg|keP{52n6<^r6)A($@?VIAq2rdf+AwY;DM0AR92G^64{0< zxV0l=#)KoS_T-U;Sc0Ysu9dcXPAwL;u{%BgFcLsZM^|MI>M0{WTR*%!a;)q8f|0T%@CJ&BOS0~#I=l!Ts1 z3MB|Q_~2Oy@i+vQhzNLq;0o6I5CTGQ0n<3qguPa-IEX^b&4iJ9bn3c^>5d(#rxWR_ z?|kEzs^~e{cx2mj%_bS6fZluNqeSi}Jq-pvirNPrXh;A2ikP`5ia61(s;Uqcj_lP! z2IrMhijgWuARu^h0fWtIhYKI*B;^o;e7Sj6@mp2uT{OC`I7ldECcx3Nnad@r- z1t+XIpKqS4_f9<$M|nFF0{q`7tPt|hgRMUk56bv%_avB(jT9QYIy zV5Fs#k})uN4xR?AVjltqX7t`N2LYTVX{Y7t_4?uYzL10<0CAz+x7DnavX|#smL+kv z(4QLwAaAVJI$-GK6%hf85F8>XB{x01833|G<-Ja=R?}Lu4~Qhiz!L~bDPt`mrb(m$ zwN&VWmQrwtwQ{E4r_z_eRav51=vab63n|8iVirNc%Ec>Jqk8(&FSz-RJKy$_mmarn zO;r^K4(?sKa@F!>D<>8WpK<2;+i&~Rh37vNIs5FFKKhJjJY#NQ_LyZWe|h<}fBMtL zn{T=L^Pl;*k+Ea{?PDMK+?PIZ*^f5-;g7!_A76wtb^gKm8-{O1Su9(mr&UiRA`UR8CwS6%ht@bS4>K@^-9A_4{_m;*Z(0ECbv zv>1T0Nsa84QYwjq zq(MD`hzjGXU|3S{fe2g(11SR$lw}F**?W{q$I(%|Uf`vR$8Nv%&L4gM8&ALR+#}Od z+c#|=iHAkQf$%Ojdlu|jOKHW@k%LAoKl}Xlp?%Xcdk!!3cW%!A`ivE?d%?NCy?gV< z#}1Ge1^AKCMii?(dk#PE=}%ca%$pzez0lpYw|!{p(8{5Vvaon$Xnz;xrxwmx-2fRp zfMQTmd(PRBM2eIYLWJOugLj^p2SOgH#K71X0w|#ux;e9Iwc1)IeQ%UN3W9@AlEgXF zXx5S>8G!XA3IQmih?$LdN}#j;@sJ8cRLf!{AV8AD%+6GZOo%9zl8OkBq$J&lwJUQW zk*!lo_PSj}o}SCc<8)}GKAV^6#AtVB;euDb%wnlsIb$O>;i(s#-!6(3OPBxt4_E*F zJKqV;|KP_z`shbK5htTNcRjXZ)p4zE=kj0v_8;H;_V0Y_JKJ||`}Gz7eap>{JhE~3 zRlob?@yDOA?xg0s-uJV+?|S%cZ~bsm<6CZj^v1g`J>$G}58pR=#TDQF=YRghW4q>d zZ{LM1U;mH)@X+Q1E-$aR?YFOe{cAq*@4s;Jw#iFEMr;~#!^(fFdkm6qE1Dw0FO&Z3Y?rjf3p zkj%dF00x-AT1PZ!-+1r6*3wnRf)Wyowo|(K*!I02{oW7^?b)AXO=fV;4m>3Q zU{K2l|9|qSLFPgL0qH1JvobEt)a*i=s~&4A;qAz zHi#h3*vH?cFc=j<#A6nZ{pqG#zjMjA&Nz9r5y#M+c&K;Z&I7y0#>Uu(6=Nft7q+%d z&FKc@GfPG&Q^$Vqch~!9>`3SEcW3%zLww?zm3t4*$B|f=p8Y?6{a0&N9y7b|;GXHd zq9fCXBtnF6%86@_9GM&)AFDO8V~<_6 zV}aK_^|UoP-f{Qsm;U6+k9_)TPhWr5S*IU=-~F3UJ@4%6e*cG|IDOW|&wAj2heEGE zb7blTFMeTZ@fCmj`uD!?U6=m&N828KEHr#gBnX5kkxpy3U!t1_$$%iKQ>>bYfmwflCjrTvCn=rm? zj4W<_WbcCyZ``$gcdI?~+Bg2)uYT~>wAnai?b^+o56#WDR~$FqnOpe%@2)Y%34uCd z$7QWPv~|nDNINM+qah*qK!x{05FmIW#SFk~t=F+e5JHH7h&q4=A3S&=1hW)M`%qR0 zgYxa@=&-58)MUS^s>Vodysp~ag(|MsFqM)hiZ>lRynJ!}L+^R(Z?AjchTHBrcKPCE z$1Df*l`Ao~&H@H05ip=8iF`D0Snr64@F=M8iPI1XKbV>qng*~Q5;W+vGP9IIdshk( zdFzA@4AKS^%A#X|+EWO?B8Y$t5CjvT7l{5S|3G@r#3)Dvf!^Bz3Lymdghhi4kP8&Z zu~kl1fdqqbg0FY$+HgKoy>bqQZ3A1gyxaV59=?Kp~eZVrPsGMA8}; z0w|@tbqro3%=b+};DT>^Ty^ZRbKMq2^}%6{qyi!i0f_=2qagM;Ai}`o!sRd+&pVo* z4ba$+XD+}3IG7|I+O~Ns<{eUzcgDL)1EF`7H8zkELb6p5DrO)J-Ux{vy<#jeSO@?j z0EnO}Z_!sPmoB^d`a3WC!8g|&x4bltH4Ke64d}g(?vIY=w&_{x&OYuqu$}sNthzk4 z;eqKTE4zZ8ed^MEhuj0Z<_oJ+b9l%Dr0MA=t${pD&$m}CZ;&sKOirdoWTK~!t2g7? zLJ#5~7m6M3NFQZ9BCNC>#smehD}vt&J(Y7ooB? zgnsD)#vw3-JRqR1G87a^A+o}DIYKWYEL)&sK`}z8e83Qw`4mX-Dlm&!iWFFgm;?E` zbz*b>?JR|`s!;=J+8U-#1*&S`@ju1{{Fs)9{&5+zieTq`!DbL_~EIYPkZ`P zjy?X=U0XJN;j^Fo{*OMl>4EK4zxSg{zy0~o{$R(hjX(VHH-CEB4Zpqe<~7GHlQJBc zn!NsoJMMev5tsM9g=%@F#`}_CqYu|hK_L){|_l_NJdHXxQ@TJc`<7wyLe*2wQ zU-w&E1@Ae_qHlibTRzpQo?dp@Wr$*Ayq34iS{$J>N=m6BV(RqzhFqG8(V-;o8RaYp zNhpAAu*%1UXI4OxP-v>a3z=AJ13DcXBLjLu6r`*-Wz~zc?sox$3M%T&w(_ouv*Bi8 zy6qs?)^$yWxG!=QS#vOd!CA*e;}akH(l1>;bNt#hl`DFtLl~v?xNLVr=nA2#sv^Ze z8Umxl0KtQC(OW10X+vRq#&l?0fJLP47O#WKO=H2Kt#&8yoj9eHEqk> z;nHP84{g}^;6oeM9(QbRyE5pY3LkW$a)4f%%B&WpNtzcu9ka9EIScGaa8(&=EPyh` z7*ir*KiFCv3fEW&jM7?zksQ*{_8yKV)q`TJ^8`S|0mS3&AVNS!fdls*Uj%r3P!I9w zY%#NR7Eoxwxg)!o`+zD=Q=yBgvM68>D{@Tm=Zkq98&; ztCueQ&9&G6>e6qYaO~pX^Qmrs;_&Q>RjWK#4;|QLq+6V39bX=qF6*j9Fvm~Sxwj9e z^eHEvv+u~^yn;rZ^GUX?|M_X_PkH+J zXGi+sk)b8+?vbMG4K*j8asG>!EUV?ci7$Woi(mid=i_+sKfd{`fB*W6{^o@*)3q$` zt9QQrUoLv_*}wYvH@^PO-`xG+5AM0^?r(hayX{W?-5*?b$+y4po$p-r%}c)j<^THp z@2-5{(jWZnuP%DY$jHdHE!%!}`LB9Kd*8<0r=D`=_{8`vx83{D{aY|3*IoOkn{N7( zs@L7W1nmm@=Czy0-{;|;2zz&y;iGtTJ1)y-fFe-zOC2m%_SrK`CMuA z-lQ_EXEmXq>RDzSQkX{IRK=x?lA$u`MK%t^BxPj{u_g?mDia+63D2^s^56q-Ft!vD zA`p4mWpF|wfQu8^>saIafkDTJTJ11^x7!X`3zTKynSC6`Rnf1UGNrIqWSX-u6emLk=awviU?v3NnE?c& zBLYTG1DGbz(V8>|4u}j$(Vq<%VZb*6WGY4O0Z`-I>@2VbPed{=Y*m?o0jLXPg9Spz zTzOA|17H*-5TtWn64;^u2tbu!TDf%S^55Nj;}!pX>d8wd=X~i)H{aU+;I=GDMw%Hp zb^pfQ=dE3`awyu|K6KZ7$gA+&V~1Bx#IA-HUU1gWe)7=N;m()8`uTZhKk~01J!|c; zRtqZKADikSRu&uhP=?Z#SkMikx zS>-*F0;%CdL3wUGahk+4y}rr4)MjSh1607!%;1GjTYq37de(X4rft@pa?T?M_MUyh zv8SAQ{+GV+&0k%4uU|7VIzBOR?1l}OzTmH3bm(yX zs;e%2<^>nMQ=k5iAOGmGm%ZYm#~zz}#mk>{&bgn5kBYPI-^-~J9EI(T50(gH!Zj?5g`x8E2u*J`UI-nemNBq-saM~8Rs+dHV54IpHV zdcE82vY^E)7thbk4h=W7RGA)grp(f)9;YLti3z2ORn;@4uUH6`P+GcDRX$G1s)$SL z3o;Bzi~+s(N@xbhioEib?aE*SNF+nV!H3|10Rk(n>P{EE!VpH@A_r1370MV4UKwe# zQej@szUb*|j$L)i4}X2t>eZ`^vBHB4Tp|P#DN!QO2=2D#8p{{;LKsk{g%I8cM4`~x z;2nYx3O#~1Kpq&qj0z|t020BL97jWu7X6*KVzqhy?jw@8K$ZX=p>Qq=0fZ`18w@dd zA5bEa#sGL!Ut4*vb!4qWuS3w@vsBW!DnbuJ5VLoI!3!cJWUg4N++rqJfzGibAz3^e zeeH8!PE`^`ne$y=5ktQ@G@@kaPHkCK*R?boZH!3~dv6`PMq?y0zT28iB4usK%tzsJ zA&`=_!gk||rLaPGLI)ASS1iE?hbVx(b3sZeKq6qUP+|pe)I5Y35W>KYejpe*O7iA` z@){XI`PjJ}A@t@B3baIJq$0tTmjw|8>s(+V1cPwi3n7$soH_PcHxc&3E5st@-4K|NXqDpKm6Qj4c{IklQ2RQ{a}-tOolQ&Cz@$OF`yN>&7HNQP3p$_y z5B~JBtKRm1-!eQr{QB3v{kxZbnZbSatCu{q`Jp@SzV|bqdan@plb>Gov&(+b?e^dD zmbct_`@?tNbKAmv@6G@4x}W^;=kxQe=e^*mKmNflr=|}tS-SYDtNyTS$HC#@;lM75 z8wAuY+nsJViQ{H1nQtv{6zN2w1~f1>GCuGUXw)-yw%uu+vTm)-yUn4Yi8v~XB1&Sd zWnjma zvXQF%aMi2Pq9K~2u{_dGZhDn zpmIS;9X#j3h$zYfdhY>+hy(x#B)~ysIL(4L#ALBB@oCGlD}Hy!k-a^~ zqUhQ6r#$F~tjIJC-^G6GUL)19`^;ECWImA^-@$9Kazn5)%^ZAhCiVz(Yj@1Aj89B0w^>bj}H(O3F>y{JN;s|J$LJ!x14dp@qy##pRsJ?&b`-e*zuHQi)ty?<8roLy!g4# z`uz-6)2C|9jrGd;DicogF%+3KIhF>eC}(j++IB6RR?zc?k)fLw(nlKVe7`9edH4#`obrF{lb?# zvB?>%>Y{1g9r_Sxr6 z&$M>$-5waf{*CV#*I&AH>7|$c@DIQLBkqjyZNSD zHa)T}OXE0^_uY5@fdhw{L)pC>9xMu4w4|nW)NNI(S0202nyJ@obMrIJ+87B@%d&pI zFQr_*e7SS3)9u#k^^r(SP0g%Zv&I->s^Zv{YdM(86zMU`%A!gYax)_)V~n#t&#S)m zW#0fvq2n#vxA#gwf~Di5^M!o*TJ_3jub!GSq??w&dml&AJ0>DqRS+12X5^TZ@8>=s z5=(*hXxUo@cE(yC7%&JK;)I4r;@085wE;aRN_M*KH7kbq?K*gPzCALLWRR9cS7&ML zxlkd zGk70tghEJFS%p9X14xpR0Kin0wUt01U?Ezm8tcbUVOSB-&gsKfUiYK3)-3DwozmKe zXdz!X_w1+Mdi%{=AHMyRf4pe+(B3Q^j#L~%AXzB@$3Bg9+2`QARHP$OmX(s=SlFs6 z*%Dv~5OV7(>S;o)R5A=oKwu!00+_)&2W*MJgbDy4AR$r!4vqp4`#^(knTMmpaYs8` zg3CxYiYlL)+LL5)=x{_?M4G4sBCT|gsu)nF1X*JRsiB&xjJIGkOIw*ZQRvL>JAOa4 z<+et1i7`EEZQb$yBYS=B=knQ_O-f2b7 z8zI?P!@{u_pi}OJCAH?B*{L|u-G01h+wQ|#KKqHUU3c$|w|wbqU%vjf2cCN7aW~(- z;T^Ai*;jt>>tlvjtUl$L|M|r?e&F42K5_jS=bW+f!5e?`sn34r;j2FRfp8yj!_?6TiH_|OASIp@sxz572mZ+U3-vCEDeUSOGg z>jyu(_xAgxKmeGTojou)Jv5SdBa5=U{f@hmS{7%ucBk9vv>Prt*q$Yn%!h%vF*Mw4 zHs|LKs{qF=S+ac1N)hSaLO072C4?)h9zb(!Xui8p^!kAO(B_9tRR|$4c*n4?P}J&4 zCoke8jk8*sW!<7!Iy_z*Nq6kq^OZ|}kPZ!tyP+o3hMnawv%vW*09#uOltIkFS&$S3QIw?zkim;0HzUInVqv=1 zZ!bA!@jaXNJ?-KbUw`F|ql+iCNzZrN@kG`y&wTBIL8nR>dYNck^lmt1cbqE zi_C-pLoS&ZDUdasGb-d1bWOWL&feOax(^#G|M0utZNLAK+KEqp`Fk&0fBZ7J0-gVP;S8i=(GH&|L!u0%X&jN&K zu9)e%A%+Eru-`M2c*qZp-uBpRM7(+o|M;i7KK`MPoqEDaTlel;ykyzao_+oemwzv= z4Fks2xNNoON2Gku|9#u0hd1unvEvuNx#Fz#=iPeOo!4A@)m2yhboZviLs>d@%!)YG zzBJaAorU(YrDKHPDig&ql2i(r*jLq1y)ic0$IKzdAVg^Phcd%ih+{fAQNlzwYWscP!la`%B;X`hR?Q%g(=l z%^Q=H3X@&?yK5H?Z$I_4MO!yr`K6D3;oWch;H+G}5?x!}hOy^^%jHvOZR0TQ}cw*PXvwzy5>}!rT7oy+cDo*WLIl?{Mjo z<=0<-{h4Q-+UxbVY}t}!_1T$)?|u7Qr3otLdJUVk;mr?iQA*X~fz5;S#*B}Qncxo{ zI8v_D=7>bDsTzB}7po&>%aZoe|zf<4>)ft z6Jiz6Ij`#s#`Q~zrS*`caiUcuRhOXbbq2GMFc|b2NW=^v#K5JJ&HsS8Ma%!{)o*?4hcJ(JADol_^~=uqs$e}G*z1KfBSR8{J7$SI zarx4uG15GBesMjBI88K5Q9(!^o#c)89N2T9K#`fM+iWELysA8v?lGl-{aw}yhy;6=%Od&m{z4@2*eAA2`R|cdcq(Br1 zL!vb@GP9NnKoTlJ5s^X=B!!fcQ4bgC(`^bE#*{(UTWBb1S2a2j{b>~}o8C(QmN=4u- za5w}Y5k#ct%g9st<~P0Z?VtF!#}@i39cp)rlTJK+MWcD!pYCd2@B%P6wRy|%si!@( zV{+^6gE}7D|LE46uD$*ZZ+OKgKK8kPdgljky77kVuKvYM*W55OpG$zT@k}ue*R&E| zNoMlK7(^i@d25E7^;#Ocs$gaY>w`h^N;LfeuYLB1-@E>Mzx>6kFMiRhUw!e1KlYiK z1N)bsbV^s9aO-W4?CtaxOyh&^|M$Os{<9uCxbU;ffArIzeE*VfUB+Cz0IhaoNBB`+x2~u%j&ev!C;-OTYi~g@yLXr>xoW*uGjV-M4>FnyIZ@cKA>> zM^u@6W4suWz6xCWC{Cxlvx1UHjfHYDj?|J-(yEsCtV-j1?%6cdSTsB`QhFOp0pKrs z*-Lw+UuYN3hPB6?`LTaL6P-CYdGOXdZl69h)f``R`uWeE>3M6q0KgoilHY6etz-yH#M8A~wc&D{EO}vU{j8 z98DN~Xs%_9^EO_-X7Q2TliPRgT)kp-%v~*`3WP1oT0}{fbxmKZ$XVNUCX%U^QNPz2 z%xX!b06qjyBme*;IZF`GqmZhWYN4wA)BEq~9NLuDSO4@!zbtY;e%84!_`5fswC4EP z$*P3;S|Vj*ahTa}M<(j4j+sLeMHy1X(+6i~_mZKu(uoMp{rESJY(qa!>kg|E+Lxg z&RqDk7hQApRrlWcut9P15~;lt^)VDd=|C78%NV@#eG=AJfXu;E#OWp_3bsg*d~Y!v+-GYA#M#yEzGW#NKDsuT4Dv5^uh zY$)%8IpWSfw{y-hD}5RRtrGpG^_8$@vg)T5INybZjtRiNv?BCok1RN}yAK~;d-_E$ zy7=?m!qiEd47jASqEG+fjxW9Z1sm?YKB_n8o3PZ(EIPb-`{d4cC-z0&2rx5khK~c= z$X@e`i(m7~i>Hm>a_>D~|Ms^w-*?Bsefvi>^$eH*St(2ggu;Y?5=XPT$qg!jwhWF1 zdhdgv$hfXjIwGv4wrGy3!3k&Va6_HXC<17CD;@2aB2UuvgZE2+t!7~ts z%1I($npQQ#%nJb>_>@J(C@@su8>4#1zTH{F_FH~@(XwO47Bg41vC$))u(Wmm@VV*i zf@{WF^DMYwt2~4*h)Bf=dMjkw^^}e_BE`nCgS6Qk^3D`xr!u(pTF0Vh$uc z3uY6cG{GTI0Ig8fRHp}18xA~n6Ezp@-#dTPjlVwbtP3xE{;SV@$}_DEdk!9nqfE*) z07!|jWO=gCnHz~}P`WHtNk(%83WqBa+iD-_SZFRvl$NdjOg2m-3d+JMQA5+uxl&ms zZ16hx0s#VHV74R8I4=ts>)L2jm;H9<(Adfq(OJ(khqetX=({4)&0eQG5Sg2p^XEs#vGAi$KR4k1_)G6%@L?@J*oE{V`PN2sh7sAS&nmtH1g z(upo7?Q~>V^+SOmV_9Qcc?-^UJKb5C#5qa@E*O>&2(1C|k$^}wtmLTbva9UKqO7w0 zqL>l_(Pyps?g`I=ky_36XMu$4hc@(!qA#V^Dh2>s8Ag&)=Do6l2s~1Zj<_~w zN*f%!NE;A2NJswtyKAv;9Hj`AXOOT8Tg~Zy+3jH{s@1hWik(NCmQ#~0-Dt*Y`LmvR z?6aQv9q@Ad_M7g$;(JG?4m8x5jY{u9O3mz>St=w7f!0+@pew6bC9GUO?{FX?g!2Ul zLPT3u&Vf>q3q@I0QbvT}NzGR?bv{^|nE2A?KKmd4`l;{y^y~liy`SEF=gpt{>{oa0 z*>U&%4`2VgU%vXHm;UY#cied6jRNq5wa4w>vwQoa+dlWXPk;XNpYQj2$E{oa#cy9S z-S)};qXJ|#}N&I@BHjh!T^M1sUVXeWU@)P)X6RbDxZaedsh z=X|JKWsopQ66dO3uUD3(sTe$F$%qZ$T>&7Y2;w029o|1aI_3%!Yh8KA%mO);%Gn~Y z3+zL1mVu-gO5>5C(YeWodKEM!D=sY^_SGt z^G_T<@fh7{x64j%TvAbR(a%t}OD-cyYMO$8;6&sKDI*m}{h}8n1aF7axGXAW982U> z(LL_sk3{*QxbB^(v*D`hW^rw3c*(zi{9Vmvb7rnwy?AuRBI(*yTptyYaBQTm0mP7{ zwlXesg%w(*qa#VH)nP^@&{Pta;4nY!WZ#wKDFe%|#mex?LH*alky1QQ`98+%kGe|ZFXh5S|*|;f2n=?@M z<2a<58lA`-K(A|hT|Yf7Rii+Wj@3n^dPQF*8bRndt1-w%LWwL9Hr0pYnI1P~@6;2r zsXu)wU#MkkvW)A^rK^JI+;_K>izaqG1no+luz2at1CR7{l}4JZ^xj&R$BlZuo(M_S z1n;fZ+Ei6l}0-zJA0%t@Vge5aSF|zF1o3^bQi(q2n7eD^t zbZhqMr(KXD|Ka*amoJN!y3ua#rN}@CkDy2nyhF8!3a-dY(omE|BuOC4Ob6SmOrexe zN?B7mZvj|q&CE)L&V1KcQ(DgZRgrVvw;VM_8rk+;Q`zV^_!68?;+Q0MyM3iZ2&@4# zfFfW9Z>kVQvy%s^`3}{`M7ba&DxPCZDU)#(%DMaE_O?g1Y}hyBc5dCebMKGe^zrYn zTX)LJr7Mqgy4z-^>q5%_L3?t>iwJ-vv^Dyf6IP9xUb$<*-h0S;kV7#qis8WY{G~JP z7oKy%g(s|{`NIOC5vhK^NNm~*Tmt}nKyC79?%L?+6D=uBvtLo_4`nc)NA>|l#o%W zc!0JJ5VY2Tu{1V>AP`sv=|p6OR4P)?O`wK;s;rvBikh)yG2CRXjd<7g7DuJTO1E8~ zMH~uWhjez|oKR^TkIWVA#cPJ8Nas7Y)di&+E%eYq+qV%MCcsE52zl3fEfSrV*^)&D z{iE|QxnXyH>_NPK$HI!{+zH1m`R1kfp8s^Z=DI%~ziP#&-ncv+)tv=~9$}0^2x*~_ zrcw##3?TwgRhClNfpVcHQ@Wc$w=jJ0E8e2fKf;Ri~uA85ciqV*rDOHpZg<|d!F~~SO4roAKSKV%ehZIXJTY@)25w& zy8ln_x%hd1`0zVMM%S9WAEl_`C^#>K3Pin5FKR@@P&zN`O+tYv%PP0sJdT@)%$~H4 zMZluyg%ETkjqOGW#4JdY(pHq9Be_s^vm}ZmT@_s|P>Yz2tE^Q@c?=u^2%!b2qc(~` z#*sbPE?jFY9qF|VM`@~SeT>qXZLV|Y)ZRl6Y}`^CUv|~cEiSyC1}iNT#dL|4ixxE@cz69h+pfNghlYl< zkc~#|m1osJ>zXL)uCx_2YM`1EzkP7>rcDQYaE+lnuX3e?F)jqiTkVx=k9*HE|7PE# z_sXmx19)I-W>*|{@}7PBZn@=_70Z^F#)aTdSe|B?>dp!QRWKsbHE)@HzcMyB8%!0T z2p&jqzh9J9As{I#SCqEjimE`BaY!L)s)r9&Gdm$0(p}dlsg!N4Tr>nIjVVRa*tB!5 z=k!<-Po#CNgG^MO_iQh3CRwu=&hTtE?6YV4IR&Di;%nDe=gCT0#qL7=Zm$yx+< z;JB(1-^T?voz6Dyp7osZ`aRPNthpxGdJsY@ z$ir~GHXsiZgAX8tKy(B1HX@RdVCP%?eh7gB#9GgpJ~M`a7#vZQEf&$QubbRIeJEAU z%YXc9kI=z+d$=V-WZ4g;@$~epVH+h)BWy6XmL^fG@?O6dDdFtsaMM<1Y*EuY76c6~ z(nOH(DnKS;gj|SvUbzsc(=M|}3PrgsCD(-Z%q4=26ls~D6?u=HO0-CVv%n%a8$}Ah zK?o73Qh7m3J6LgUhe@IKF9$m-m6{Pc8o4R7NaQEDwK24S-q?-rT+4J+g2Afw`Vi zT+>nBnpX(YNg)ZO$l3qfhyJBEwa$+FS+y!}0?7Ts=FCvg_X=bSw1 zd;K(P1}_+uCkk4!BSkutc`c3$OsvVHNC8G#iS}GCtzi-;RZ+~$6%h<|yS;HrADU_1 z^XR^GJad)cfpOxFju1i#R5o(BkOC{Zr-bOb?=tL8M1VCYE_P|qr=-#0l_P$wH&{_vss>9*dt zYscTd_SL`K_~>|=KL15;x&G}Rx%x+M{pi1aW%Q&AVg(Q1^#J7l>~qdsJ~ndYwKulf zv-=p(NoZ6WC~_nN zN~t4~8iWTSK}J9kh?qqbSOg$M4!{9u;KCP0N1T6^J}Z zZ&-tefsqdTu5~^&lRF;L&wl||m0Z|R%MWci(7O5d-^I1XyQlIOzT$l!dCm*wI@Qcf z$8%4Mnu=v8e2{@uh^-G*C6fqk*CH^my!lYU^LeudIkb8XM2NH}h?_mz)}$&2zT2xs zs`RR2RrlS}^hO~p%)!@xvr(q#EDWcOOmPUo86T$&<6Y0VOeQl^)9-!v`@87s{ z(a=b*3QPfu?)lGn-Vc9r>4C|qp+=)B11W9G>El<$&Z1HZbWKOHunv(>XTn1$3;*hp2X1r3RPr031X>YA8bPetLn}< z1QDErz(j%tjWyFmLc6^{QIw|jyx(HZ3ybYm@I5uMbRrt6x!rjrf+A&%v0fdueFqo- z1gsCvRfj;PnZ{NmjkAG7n@pZ)Yz zWAU;}zyIy^=brM|jX!$R2R?Z7-G|@vt`ENKr7x`4((8VA`=-qizZKE}JXOQTJ< z`Gyo!feYT1W~`Q`v1azhlo4qn2=3?{ptZI#!37@>q?ER-0z#D+goDjJ_+XuLl^yRE zGm9JbI8p76G=&*%#s^!pWHdAwmE6)fZpX zPIzQR1vY zL>E9Hgn)=5iXsMDSXfAFNy}BEqr-V&0HD!mKDxWt@7cwfkCX&(S_>UO3c(@@!9`jZ zj;!(Cflv%60zl7s=i}YGwklVwn7?V$;av~hzmv#y!dQK1%@;rV$-?r1g_f?oNR;3q z1D&LZtuz5cW+Vg|BJ!cq9Dkx{J6EJmM_uBeuri+O%*87qXh)7E@GjsUdbJxG*qGvpC?>$TF^#u!YL@v*l z9Cy;8Bm1xT-E}g`oH1E8T38b+TD_tUHpEF{FbAqb6`bp5nd}w)s_d7Qt*XNLvNDx1 zPEgeEw!OjGBQ4P;nF%dbTpvz{#-iGMeqd7MB2)_|s&QckvANJZ9y zjyNiT6D4&-hI_uKr}f(p`bQ4;8-?!niW<&ZL)Ll^A&zu9;=jYMFJ5N3MHduh-~85+De4iWG5RA52k3 zk#-zViXlOUt0;y@DE6{q?pX$f!(8hYRija_%7ReKSe79O&kZtN0Wh^4^y&$yCL*QVY)EQXi6hop*E6T=V?hkj;*xNn;BQvx2x##Xtt2|~79)u!m zE9d%%Xl*5gkm#Iq0RaOeL-1A;DMAz`ah9`2WQ(|F_tU8dADg)3`hyp};90-=-8Fg1 z?|q*8+3WsIjx@ndgmJDau2hxiSc=4Y2Z8HxWK5A#!cihfXhA_Is8Xm{N0BS4EUO1s zn#x5{RF!=n{P1wodCbyAS^3J8L&ND~dnc2uNreppC4et0CCP*L!AJ;osS0BS%Mv&c z2_cB2U}76cjug8dn&0*FYi_t^?J4JTGQRh~-go``k478WTy8DmSd?L^14GxDIxiOE zA$Aa$41y34Ssws`64HaSAk(ZK$MHaE1^}U|0MkaKLkMMAGBc59=B~9-6mga3&ZTJ@ zicpls*p}AXIq!?M)-e$~R|OEJq|bb|p>-@LY!nj5pd#l>L0Tw7 zB4DTo%0YW7gd-26l?qjv3zQPsasebIQ?QloYA;0BEm*GtG;FnC%FqoeAUu4aKbCq6 za7>;4{jF^RU zB(wo1l%DJM0U&U|qEu2!6auku#yhJqVh|O3L6OQURFaVluyZyQu~1Z6=LC7zNwn+- z5!eyM31D5BKu8J=2j_jHH8Nm8p_TD2Vyf6Pf)J^--T@MzMwx4teNa*sz5+pleXPN= zkQ7StQBSaPAWF+iO3}!1E*%l~Zawnm7oE6o+m6XjNcz#qXVfAHIkqxhQO)(L#V!G> zH4%{VlC=UthZ0oeD`)a~6p=t3ocG>wu*Bq?ms0u422aKTAbV$OX*S*J*G5O{vk!KmZ1RQC=;WLM-O)D&Ev)wt@z@d?yRE1s`{5kKf5YiOoU~9F(4A+t)-j~ivc8YZX=(kK1*NhF$d8wSU0H6R^2oAvw zHX{Z&X2_2vgZ_`#q4^s@W!f8bs3de;}e@P&_l^rMeG_ShYF+%Y;j zIyO47VZ(-L}0zQp0lhz=$?Ad7$5*6C?QnY?>O(vvh*C- z+2D*;)a`?fto4Mxp|$gf0F5BZyg;q!RqIU_>AiE=-@N45o%tN(+$Z6jn=4e&<7_-G z0D_ctqLdVgpadxnfzYz6Y{;}ChN{Q`Ac`W#frPBAiI5yx_RfYN01?nvrP9iHZ>&R- z-6GGO^I8E1A(R$aSp$K!RHgG;YY#Srkczr@&RXM%Dqo`SWic^h&*xfb5oA?52q=Uw zy{^!rsEV4Zdv;Z56H&&s;IQO&6i{iGTMncn64GKQ0d-FnDwYCLANnm9kBrEwU#NCf zg|=C@X5_hRl1=xQi${ipGjSr>^j9{AckbA`{jfXlsl&Y%`BYe~Jb+{lAut3Wp_C%T z%K5V2uf-`4n#v3qX+BsOaMzW0&H&52G#rF+*zfgy@HG_#npj1d6OIaxsfHG!^vF<^ zqgmFsRcTQXs#wP)6h`3k&;H8?mW(Z4JG`vBe^b)L*vqI8SOU}D0z9)1rrg-d0IQIP zWHdGwMTothZ^Y^T$L8xJ@rOdo?Sl{8o9IYJ+LUFduX%k%temt4AN z(IO8hrK&2M=Q#v-`bjBzBZ;IGLTY9T{SF&0&8!Ahh6r62euEnIRwsK`1~f z>#8JfnyPK`E?d_eZ8lVF$p@E4I;~76iXslWvV~M6kgT=H5P%2nMGyv^NQMEtlMxY# zj+Pku#tg?;mZlU@B@|_GR7;A5Hfj0k$vnVAN=0;zVpBX5B&c3zrXlZuUWV5 zcmPsz;O-C9-E9e3QZckf^VqDK2!** z=(%3e&C+;kU*AIvT!qRORSN)4T~vGXb0$t4ul4ivyc4ls^l-s7>(qAn^6_-RSNh#1pD)PWG zifpe`N`=5AgbyLFObCGk20#c5GB6+rQp#6>!3LL8+=vB5c_`V2SPolj2sulLi1L1! z#F~N9x-3c#o+6P27fK&=BvTNEML=DHg3wt(MP(&MWtkPiPLOI@HALln(8;QGH(Y=7 z8LO5bKGI(|3bT`YyaqCrs2!(0^|_Ufj6bkz_K2-DGMUc@6%x_YQpZc|K z*^h>0DAMg4cdmcVna_LUJE4MuGhI1g(-%b%LKwi1t+fM>?m@^HxR!(v2BTkEYeXE_ zfDQ1s14y@&Qiy2xuD$Elt$WiOU-$6+_cUq=d$68EaJ8Y)I8HzM(U0mR)lzf{TRB@) zMyh&EC+D0#;flUMDDoVnL2d--c^3U%k1(=ft|sSYr- zQKS&@5OTR16;DZnD-nHw()srckS7|Z{LBEe3|Mu(?x&lErGEuPyA1rgCM9*1?ghyFq&J=w~0tK=4NMOqC*Fw6$gMU=ReRi6JazS7~NkWKPd&~OgylU6> zNn1`mwCMrqoO8}4Nn))XY}ZMWR8@7<1{7dGejc1|LI{KSGH}2dK&Ov-Ycd-DAL5P*o-n?cep#h_xUjN`eoEJ{hjHWsCETF!SDI=&|m90w_h zC^DuDT(MOwl-5+{MZ4V|85se>5CV`W%Tfr%jMn?ny{O-4%bM6`c_uX$fJUN^9&E~CJ{^L{cd+@%^AN=5l zhK7c2zWL@;PC0pgetzrLtt(cnh@!-M4gv1G^KNVH;>8oqW^?b}y^lPyW#B+GG&FSN z$dNdXrPRB2?Rw&KM8tl-A4QS(zSHSUESYH3)xyHu(z@!{oY+Z~c<+%=Nvb7DRrX1x zX`BdB{e`JS$s)H(OJfF(<reUgm-jAsI&`4Jsi7l9&nZ|v zGS)7t${GwhHzFa^98GJDA>q}yHbSDYmb8wPily+@5Cq8xf{2i95K5ZLDyaf+qBH~! zTK4j)SJ+5X6tiL%Nz^C;1*KHwOsr+EDiWK&jVD`X?qL?ziOnn=}iYQe+j)%>m?RLvlab9GD1J51= z36uqJO+_%Ub3kH1W@1KSWC8~N_%42QsSY|M%)tkNRO_~##1O~cy0TUudh zUv&$Rz8)oe?%t#8{#{@B!lJe3KelPx^vte@Zr`Gbq_tKEQ53}>e8A&``X_8U1|fr) z2W5zXukaH=Cp_*`6-CkJ&71%8KRzxn%+Jo&vjj;f!fro5cIBEakM6wW+uyC%>oaq$ zI7*^K_xqKKxZ5euJ0=zhaSOfo2p}lV^Bf?F^+>}S{mj`t#~fpP^;Ny^TW=YI)9KilkuR)Wd)0d%*j`^-OLS(tFTd-HzU`AA`{WP( z@DJ?m>_70?&#sUCYhHQb7yr#a^S-|Gj`JV=$b+A}{}W&L_22ldU;p*L^2`6>vkyP9 zoaZ0-Ti^Sw-}PO8>qma*b+3QJ-~X|n{f_VW4kCH)d*Ao%-~R3Y?w5b-!3Xa@efsoQ zedSlb``zz;``h3C@sEGl4MZG`s^xO&oH;ny zTdkH=HDXRN_G@d?)oQi9y*(a}ePMIX=P#U1DOqcbF+j!3&W#S+I31Oc+1B2&XbPpX zg)f364!KXAFKp<000|)&LvTDs+Oa67;GA1!6Jh0%7THSM{nB^6Q~UDT;r3H|M^EqX zf8yC&Pu@D%IJ34^k2QxiED-%{%+0E2cFt8j$B8eM01~Sx(lHYkl?!HLXi(V^0;=(@ z==(k>C&6kwnG|N^(+FYY-O=IFR23)oqbg?^9QcN1W)xG-%Ho|#AqnBJr*fMj5F4!^ zi&nq?)Zu9<%kk-q89B~Ic3j}v8cFCXw7?=2YBnhaGKs;Y;QGL2;m>>DV%!9Etj3^{ z1_`YOFYAz3t*d5svQ1lidk=r@7k&PF?|=C7@40ttx}VnEbV65kWU^KxI5Qgk{2x8l zxL}|sv?}ZbjONPPEQ~XVT2xf=X|I zE^jw~_d73e>;A+2*XjJlDsjZfB9I|DSf$8FMd4y(BPOe;c4J=yTWgF!mY^f5#bsF; zYeR`PGXr4Gfnz(awssfo^#`AM!`Hp)9Y6GU7K{49kALv;<;&dq{`w0yZ#-`Y5&94{ z0APq6LvAuaw!^E#dK(6X3=>0!UYOy|^WH!E>~mlGrEmYrFMsriM;T2G3snhR%(k%CpIh!RJnQc}EqoIpr{ z5IH3gRsfJx+43+aSC%h4`@)5DTfQ9g>Tq=0-*M^gr=EFgx&Zn>1;0M&`_-+RyVLRJ z=BE3yuX@*4eA!nl7K``2?|s*<-+bnoXSTMs#*;dxs@WP zI&*e|gS_>NzU1=r4~=K1o5lWxt;sV_Kk<)#>>vNm@Bioj%Rw}0q|U-QaW{HuTYbN}Qge)7w{bcEoT0sHv-Ic;m?p|&H=9?#V*K55?$;k*u13M(43t{n zF;go!8rX0y00xzb0%vb+t!)^cCs3z}QX3Q)r{20E2JM@cMLCLI{TqN{==Tk9TzSf&(ImN4`WQuc?c7OAj%jsq|*Z*bW#T$ z&k_uRZ_l)8pBC%1}5&KRPtdZ~3+_`ftDg>A(Kh zzdry+F$*v-~Bz``49fVkNxW(`R9M_ z@BGN8KKr>3{K0#^_UpdweSh$K-|;Pf`Ct6(yMOdYf8hW9SN={L@;83tJO9JK|J^@$ z--o~DTfXj9FMsXT7oY#|Q%~8_e)Tv0rQiIm-x|%$ zU;IUH|G)=7)VA@#2Oq5K+IzpVvon+}A%wClhtj6&y0R>D&Wpv->8-8RYGtiGd-iP3 zVK!UKIrn|Pxw#oq%sH23NoYgt92F_00frw$1I!Y$qJeX#{M7?8&Rb!%WlTJplyzC4 zDx!kqx~!HEIVuVoq^#h*?_xp&u?s!1Yl*V7ZIE0;>gsXDulm9#KK!R=UV8VVANqK@ zdDuDq?zg^O-CzEzzxq3OS7)J+giDvx8cw&ehOS+k5l4d#II|HX1dE_7s^E)a)pm-M z5@(SFSv4s_>{Cft76_1PKQ0A~q@pl&v4+&_uhOinl{AVb7FARhV~q-MA4~5HVip9^ zmbq|6mFZ@J^!|LMOladhM6)ypqD zi7OXYTR-)y|BdU>?gBP$ENxK~1G6&(3v2BFoeZOb0if%;;XTZ3tvx(i%x1Hy^nKqW zV%IgZ+3c~$pZK|-{g)u2n;(tGV*v~~%=Zsp`jS_E{J}?m^}qht+1mQ%=H_a(>igI> z%TuRL@9*6b>x}C8yQU_c@@A2oRZ^6-z6}6G>NE3l*??r_kT~U(IR_9mR7G6@v9R5k z(7b~~fKE+DmetKKe%ZxKuYT*!u6C(EJUTjm?)2aPU;jUM-ErYhKl<6<{+$m$_0;t* z`O>$2-8eqkcKmPcS|M)NcpTGD`-}J6Gz3Gh?FJ64- zU;2jc|NifP{oB5LV>bQNr#}6+|Mp+`{r9~8d%ov8{^?Kun}708{>h*I$p>F}_VTa) z-tYXt-}sxCU%dA6d+)t+bAP_OfBEY3Z-3JpH0NLa55M-A&ph~!ul&;Yz5l%l_22%| zzkBqNC+oU??6D^-nU8<`BVE@_CX?-*>x8zh>!xW2YzEl!)rOUf*2n zyXd_aP-Z1(r^TpgBY+Uuqr-)WjK^bUF-XRehyV--l&T^*Jw2t*y?AIX(Z~Q=W7OCz zfIt>8F@QjH#%Gz6Q5Iy)i0H|s93|zdmoRlX8e7(OvCKAwtnk1mB#FJ5+Wn)$p8e@l z@BipW?!NDDe=Pn7q==rm|$kDp)Dn&`h)h0ChyDAc_X8rNnH#wL+93 zlLArfB9kj^XO*x^=Vw&~p`Ml!WWBOt^V!Mtk!wf$A*PJZQh~fJ>a5Ko%It5>F&T3LUy?fN7K~%#vZ2EHn0g}GT;Bu$L{Mb{tFsXzDuQaQEOk*EW2U1p42>RY z|I^II(yDE%s_vT>*Dt-|C3ih?{{ydh{YyXosZXApu2C`CIJbUrv~hjW-?2ewHoHe2 zjF-1{^N#vC**2KD%;ix<7GqFTATZd{X3nhWs06V961Wo)RR)8cG8%AX5OV2=U_=0` zc4cw3OyTV5@iWh*o^aIh!v`|z+h-T)&eOB+{gU&)`|$k!D{*r=&diy~YM;TUNE5U{ zsM5lVPN(I6c<|_<&pufdrJqb^Ku4)rv=NNG@51`tt%FOFe(qm;KZ%f6*7d;iG@@ho?`?`aT1hwu`5?&MoH6Pyh5!tKju6Mob;~#zSa5u-!e&`SX=h-u}-Rg&BaY^&&ZQVu{8=>nSY6 zWW#+EIij(3#^*ov@&3urJ-k>JqjI&gjMtk-=eswa{hi6#t>Hqu(pV;EA%(seSKgVz zS`aa$NGd6lvqZK{Ap%%N=gXkMBBq?NFclha#sIKx?bfIqW7e2wWj*p{$z4$vZ+YeU zkA3dyK?|5s?5Hl9n3RYRL75GrA`IJP0Sb~OXJK)sbOr~h1dxgxlb$&ZV7&l}8_cXg z2Q39Eh)9f45fW!sAtDCNDk|uT0uhgzpch+Sks(ss4zPRh9_dfiAKQ!nP@G^w0 zty5dYsn0y|SuJrG6lshhMUkAu09Zs+l$8asVXF&?T$B9NAS?|dtdXituq^XZTs5AyDxm%>uKjOJl8KPH>yF1EU_x$ zp`;B_B@YopjBhzRdiuibtkGo;bGTKz+2%&QD9n?WuYBVd*Z=xo{=M@TzVP~Ex2|1% z`0ABg*A9xm@zXy?jjS(eOv%*@3|L}ghvO>_G6>C@-V z^*K9VZk;_Rii^eK^y$<8+yDB*4?Xblt<5z6AnTT^J|r#c>A!jRFV2sagK_!w>({;a z=JtrPgxoad^eJ=ko^rWpA|}XceW6)cQp$Zwp^qu1-1gn7J2VDE==#`+WC9V*-kN$; znCy4<+qy32VGq_O?|taJJ0XF#t4R~X!!q?vAK9mk8mA3{p=b`i# zk+SsGIO8mXT1O(1ghNOovg{)w-*J9ZxB<;-1T7vl^Kmt97TW;rTV8f%gZeJy5IbuO zBBmTsVN{n0LV)ACvSh~2m)@7&I0OqYAcli-VgRHHUs>ykETRLonHA6wT0)D1E}O_0 z_B$lzxjqA~8KJlrwjrFeUPM_M^Jv=x%IvQ7F<0uHd z*s%d1RCI(m@S4^Rc!EU`PNDA&5vN>uYYmp(i3sX(o52w2R5iw^5u!ifYoU zsf*fs@4ctN*ZPFh8IE-r>pkp*&bfisDvDw>8a@8VBRK?TEyplhn{032{HdS(i5oYr zAtyv^y0lm}i&eY6vGx1!eeVMgK6K&2g@Kb!DOu~ArWrgRO(C4p%g+U#8JKlb2^6_P2|H)d;VYnLuv+S=OM z+S)pQ{`_Pz8IQ-K(P$WU8)F>ogAfdn9YC((5)98{jMn;-x_WqGttFz>YPDD_nAuvN zbMCs1S%)pb0Jeco*T4m(-T(@qLDkT=rFTP8GsqpSbIRcDM8%1Kwcc6_$Vk~3E!j=$ zS{;Z1ju}}5lJx}E^}C1NsylURnmj&!bXfYLWW$WcnA*F-qRFu=oI`K`wY4aTK&`b| z(@=l|kS7xXLI}vpNLfsZ8dN7mQEE2rsw7hySNX!_G;ua{oiWHFWnO*3eRnv=s9D$m zfFpyX%*i=p9Sx)A4X8C}ELwxeVvrK&fuJ2GSPo1DfH-H0!VP!nu-ZXMRe{hrD+0`_ zg2Ett9G+A)3zwt%#mmoiZM(iPlbk{1QxATI1qhHiX_8l7@DDt`w-;LYh?-JT5fK4AzWPIC5fQMiK$CNQL%gE zuzl`Gxg*7)V%$MG~iMeHqEvLf}tSM&B^jT*TZ4N1w-n#6TIZ0WWx$5(eU4J#+ z`rLbe@cVxL7p~p^^e6xDw~I_Ly6`RE_wx@uz0AVnktNZBSfNW;4Pk@^JjFRToWbK@ zLo~$BzVBD7)ll<`$mPqI4|leAZ(P6n{Ih-6Jod=L-}T+!@sgL^ef8Q4lgX^_5{tQU zYp))U9((MuKl-CTI(L36r4&M7=E-C>T&E#A6fC0c+h0;^s44|-3u~=mHKd@V;$zP- zHEnkoV+a4idOvM?^lGs*!rJrvJHO~9_xRmso_PL${J{ra^Lej( z`K7YbjUKX>tc|LuJoI5cf+!b6{as4VJs)faxWbLDxWdVMl8 z67~-o1!@j=LZVofVqk4%?z*^nX=C%N-gM`?f9n7B+E>1m@c#j04bWB2qJo95`YsY$ zYkba<5;Lo{-WWG9GDT5XYl&=(@g%7nO4NasYuk3HWQQbva3UJQF%pC%=Vt~{`yPPQ z7(2YhTc;ol3Jl612nLYIA}SmYs3t@uA|(TfGq=4oY8;_wPJn#+)Yid6m(wbUrR(j% z-f~e>K`|U@BqBgDAcmwgP&iA%b>$5yfe!!HkR$5^Fl8|;sDQ>YGg_O}kgbSp34Cf? z7TzIRMzc6NuqfUD$0b8G_BFXGhWQu1{5%N6#Fh|2yfr6E&LL`BYlk3GSU0Lm@9piz zl;d2(SR?{tP&8%^*7}q4IfR~ptT0H<*rF)BbB2fjhhG* zU3qXg-`Lpj4h>Ol?JLfV{`hmd<#>dN5z&&XOJ|4xgpWT6`o14NaZr03#Nmq&!EjMT zvnuBthpy{BcE*5&QDThwa6GB5Txsi4O9NEZP0|lN)udQqGAX6eq`2pF`MP_j^JS-6 zkT-!;QjSTOTo1VFb)3^VIaO}?XI_lq#V6nW58wFE_HW+vh41*>AAjP}kN@{Kz4T>2 z@h^Yn@BGYr`obHFE?o#!vm!PyE3L{#ZnQ z;>Umb!uhkm{O|tlcsBXfU;Tsq{q)(-ezp&Xv)SxV{^U>Q^ZA)GFWcVU-QM19nr?j^ zKJbALj;1442z_Q~`{??R)4b)>0*-+@Obn1-^^!9py-oJ~g;O&%8+ArEHGXEQ;^5}< zgIPE7!4eSLFxch_2LtyRUo@11kZGNijh%hf>t6X^-Q z4nV3>6xDDAhtmZBECQmE*%vOcTsr3sb4Yn)3L~Hdqe)#AURcpsV2wFsz|uhKTfk^g zBBzvCL;xr!j8;fZRk?g5EP$R&g0#1=Yok@@Ww+(p9OU4wwT3BYL`76j$e^R5L=w%4 zswiBRRY)NDcJd8 z@25^Jj5XgeaZfILL~jX?3mia>v8pN%2A~jg0%jIeh%pY__A#&s%A64l(x93*dYkUN z9io`Ia0duE=gge}vsT#>?Bm<3$;eBe z)Tl5hgrU?FF@!kW7{07>LSK|IF_7uIZg3q&0ul|9htVsbl2Sq?QOE0@!XZXBf#KC|=X_lZxndiuPREWG!-3DR<(0zSn>BXU=`<(WfTU(az3I z75~OJe8CfsKlA+aFIo$$=HT9Y-}cdue&nt@&wuJupJM55ZSVWC%2_`9*$1ks+}^&q zw|B6%Hl0jH2M2rJ`!-_i59f2RfC}=mOY4OXb+kh5`gVvvp%av z1$7AtFemYVgAHAbQIHh0?RrnPbUtTxL~315(KySZ){=sZ3nsc#F@RG-BFZ@d z$dHeaA%sB!r^0gF_%@tzRX{{IE_nn10mv8x8NSh_OP89X#r)_%ogu(cIhrq)RZ$@r z<6Yn6OBXh7?!==e)RVdav;+V^D5x+L3Is$5a(r*pF=RA`MXdFTpn?b_!bEl`eshS- zT#QTXmziTCzT-m80q4D*n!tg2f<_o8m3;KMuEOqXzVv0Edt#4^wdbC{RudzTQMcw1 zfV1#OjMjyhkmgeDU;Wv?^?5)1uYW~%j1gPZ@&cd~vX7o_qerU3b6q(MKPB_UUKpYHZMCNhi9PLA*PF z2t*VluTi~rYD{Xq+SK+fwIdrFMb5w(fu-#ZyRHj8pqS8yK1thWO#%U9n-zJnU?pCL zf)fGHOAD`k?VW%1-48tXx!v{Cy)`TXySJ7(KlS|c&#lik@4538FTQwrwQ8?kz1DSL zeZd(OLQdUuHeM_iDW%h=PYtsDqI5?`i~CMbU;d{1PS1*$TpAx;4& z&C2X-#mwXJxa&FvMld2G60P-J*E#2m^G(|>o0TC)MBbruZb)Ya*k#yB0RUp&I#p{( zXaN9>LI&{OIAc{2fJCwaD2XbIG8r}^IUQFApp;XB6pYlG5m69W09YlYJ}o*(oZ9Yr z;hjVPfveF`D`bESa>_(xkQC541CVnTV{Fbj6Ogq4CS)dStjbZ&F(#4>zLGXq%B0#d z^+|1EEAJQK@YHA=Tz&mub@A*ZIT@81WO8t@G7gQguPVn!ZCI^VWj#(3W6ZuPz4I}~ zRU2*XLrkN>r<9zZoZR!wX((`ryeg;~Llz(qAprpBaHSOB&*&Q>W**vz5wQ#X%g>x~ zga-!)uYJp#ZoGJPZEdX$y|EsM6wPwAdc{lY7q?rLOa|1DYBqq@kue64nHhkQP&6Z1 zPXI*B3`As2PJ)Oil#d(tlB%d`QB=m3%g~es*Qrdgk+sEBwhZ#ywJ7#6?x zelSTG_SNA6jK^al+FU>V!gDXa?Tg>?ZQuH>ANT0~by|c47zjpQ3`uh6ztt$_F z=Ki885iq8NU}MaxY7EAhoGV77QL{Q)TbtcHf-iqX*mC6`ef-wj-?FIzsZf!Tr2|c2_l2eTAMSgYR*zsqami>#!cM*-ivpg-F(NJ-?BZQ>wI|- z^WL)EZx^vKZ9Gh)a?6#@qJ~b2aRxO)ll8cLAfOfnjVM@C6M{ocLD+#UQ(U41!a}jh z8BuG17D+*U$_rz`VY!s(3r`(4;!2x~teQbKm!i7@r}BU>&0SGly!^|5??r9 z94y!X9_6)(B?#0!Vp`|0}it94zkR=y~Tfr{$;UPNZI*#M_EO>^<$`Q>WvoIABSPAmqr zsJ0^0#QELr!`U6Pt5*+SeBs90+D6m$ZQs?^#8)WTS7jX&38*!|nK^-EL`;?_u~+~} z$-6SLNY1q{ZUb-h=ljBk=R;UO-T;}0)8$+-*lVSfoO1(M3jhHH(Cr>H)O%U+cru$7 z=3Lj;aPKXn7cDVZj`R&^x<#a0Fs21OaNn7>fW9la@s# zsY#*$S`f~gmNO_zS$QSMnG3XSX3?YwXpIN#`@V9vw1kqXXbNxF$K$Cfw1^6QspxBa zG}q;#6)*@Tg0W7SW8w6^d(J<%y!y;RGd9+Epo9?vS!WGJVMN2AS=2h0^Rg^8=e{Tk z)t*oU5SWD7*sd^|Gl?bRA?GMU8BnPwoa7{BRfq^Mka5f9^4yuT7cN}bxpixEedGGo z7wf{Vx>e~5VL*Vv#-EccLt_9H!5K>`0@=9Z=9nxjs07Xy$E5q(+Ir5C7?w?2787Pr zu*#B|RLGiA1w}J}oqEl-+&(%O&1z@+*>nwdZpEGeh>R!YW!E`hA1%|fFCM=70-f{4 zVY@m#t1hLq*NYKCRiFD%df{HzCZG79AN`xJf9-#J;l@Ay)rUWOYiECN@2=BZHsSIx z`AHe_%H%wqZ4Bi$fD(Z*#ux-pW6A+?rm`&Sx*pKkWA@h=@BQGDFr?N4RkXgoe&fcC zpZS@eIXF1j-QS-tm$$Ze_jV6*meZ$Cz4*clw{Bh^x)m&$At86p42hyKMkF5-PelP) zCdO}EI(24k)*S2+Kod*)$)g#=eWm8gIxW8q|!WMlEDCpxy$i)uc(GlkA+cfEKB1R)z%#Z`N=}M|hD0RfOvX}OBw?ePQph=S65|R0 zilR{@rBE6edpj;Hq4*jL5+Lp*Xoi?*p6vdSW^IipW6b8}T3u67qASQ&Hs#gpFKj7F z@396XvdL3r9H|m&h#fN<=a$PQqKZU9%USy_fjWc&)Eo3DHbwwj^elmyfGm6MtmSFW znaJiOETl*&CCU8C`(FOo!;cZ#?d|QNaAj5XeQzxxqDXc~;EZNDY&!ysSxAA%Miwy& zf~1sGl7cGqU6&FwbF*0XeXokn4hvI>VBmn5QmT6BxnmD(>68MyBY8jr`e z<(4@X4s1uY1~0}8}!%m{4HZFc2o(d_)KAO7dR@}6fu zaR0+O=X2-I^`ULk$`w}GJ3lj3u~;mcreRJ)Doq1aWFYY(@q$uHZQI5eLkI&{JM55c z+XIlQE|<%brT)YdPkik=-|_0td(8u%`Rr=hK6m+r#bVWU;oP|kZPPsPz^4(B2%WRX zLLzzs0dU6UoUHM{GRVx;YCa>t8=C( ze)8(oTf67)J~stSO~+(=qltngC((L0DvDxIw;F@Spop*}RdqOVIHBQIK1ouBE%dhe zn+$yCKnaBq*4Ea3;0J!-Pyh5!6XzGMT%9jhUDvIxtr5XP4?WoTy)nl^m_b%YL<2k- zV=RiIX&L}wR#g2j75#pk!=@joKaO070uqEs*0MyA`ih4I%5kYMFId( zWswZ13QPo9I4+vNl0hkQQt>E>a~5u5N(Pg#2P`YMO6aJhC@?2e6?A2Le%*rT+2tk!|S^k+1nR6~DP{BceIC$0pD3TUx zOEto|&u!q$k+l)q+nfS|ltm#qNlst%g>OE$bt-o4#^&0_#>Ub9K~?%;U6kI5Ko*W& zuc|48#939640{d(Kv_A}Vg?IGpZUyZZf@VIs%qd$MRbsRF>_T_ zBHe%v491SuI~Fj8?!Dx=?Ts6=3Q%@92lExg-nP^ z3_I%`+m;1MQqp_x+`MwwT-iQs58DeDF4T41b=}2_7n`Ozk-8gWio&g}tr3y4833xP zQdLz#bgDpxgp+fHGhyl=bB|c8R;_SyROZBpMrE+Sf*}<)#u^icsYGPK5s@e}2hKw1 zoU6*R@Rch(5h3bHu}MTZ^H4SpKTo31AZtEh;z9_+U&RRZbj?kx*sM-}(HpKlf9qAY zc*~?}aJtChXw~0b#*trbzU<=9{_&$1%j$~e^%AJ`O%t##*^og+BI^iX7ytmq8c8{) z?7WX@g{a1u%yGclQ`W5FjSW86^^{h1(1S#3JDDXiz8!-qHkJf$rJ?JekeMO9{C5JiQY#JM6S zE{b&pW=^VFx+>(*=jf58U53i;cDQ z?W1K;SH>7L(D(hw7yu}W0up#q5H(0>(1=A4QXnwSYDz2!jz~orL6u!mhm=_*93B?c zSYl>QWS!~|po2kXPQp2*gh^ARuIacpQL#(5+MMn^b3|n8()aVZF_tn}gI&9rt&Lxp zw=W#J+C}s*o^3)EPA|i)7Mw@%wgj3tt4)AeX^)nz%abOqrcNL#8tbbvMOMHB3B?T- zw0hficDQxRqK0969ds+VwSC74xvHeZ#+aO=we|DQKmX2mzH@E1@dqDx9}y{_HNMa7 z+S=MP&ph?$W1k&z3g?`P#u$N66^7I(=UkS@3#Kf~kWvO2G1Rk-)hf0r;)F70?KFeN z462wS_kD<&g%Q*Wi?O3Ku{P2u(`q)`9Iu&if%{+b(yjk=f49dPliM6o zN}T#&>PiII86dDoF(l_)lEkqu5D|6R%?)Rkj9{4uQ*;w@5|LHck4(AUg-B2+^;vo= zu@5ovk-|}E*_AV}ZGvS+lG!*lbwN#k_XutrwaHY( z5(8A;hY(!t1F|Dyi8ymvmVLi81S-inC)@+5E36W}5QW=FiivU#-K?H%P9_M^s|;=- zs@nIx3MiWKWV%=$K7Zx<7kvKfR5go+n8#yK#6g;6W(euX+wv&I(i;L?9VAXMGn294B|-Go z3Hhu`BvTjk)Q-O4-iksyKMJCU_nxWe&o)^RviKxqpimu`^^L`<6j>`onIjSy?YfnN z%D6I8rgUt#bdq@>ptEk+|Bj2GlfW|&Ktu9U6a|1xCX<7Mg9{ffee*Yc>jyvh!NJRx z5SPp4*|TS3>_7dPPYng(&;*c$1=QIhgfLh`4c!c@)oR#vjWMct$0p|Ts1N8JsyL&Y z4lF}O#-*H7;w%bEeIHk=rt3lsTohGGtbn7@WIP@Z(B@=T_Z%&_Ea}#6duHZNuh-*g z)%X37h72{%U=}kB7&wvtAtI3_BIiv?$rv2uA%jk&uDwy2FQRpm&@1O*+;Ub*Njrd` z34sYT<=9FD1EseB&Ja;yXb9oTEFqP4NC1!X6l0ECB~HL5V@y?5L$NueC&NVy+VU$SDnzATH#uT82&^cS;*!!|Xqw8BX z=YD;hUUF%?G0wgmwM}HjD5&B?FJ(1LA!Edwd(G3=`??;JNIeldbMmxxr>JL znA+v6#*D09aWYDd$W085jmo%;s4L0fz3;>En1q2I8NgHLjq~O>TNzf}5O@anpp+Oj zk;S8f`Tp+FkNmxV_>l+h&p8`chS*sL&eExktv`6*``cAhmo5vYmDvRfs=vs=^MD~H8sqscv{q*f+n(Wb;m z`^y7iTN+q!Yef_sH^ykIW_q-<_n+U0-}((-0voSPld~?0Ea$Yhm#zQYt)uUJ--oXD z?$oTxkuBvLw{CpNTfXT}|K}&YFW>OG^?r)_?(33a?Qzni% zMe|IIlt+N;qj3F*GE?QuO8X<1 zH;PjlNJfjetPxzNE3a7DDrAtDyz?OhZ=GPa=n@#i8d7u?iAx})A@LrH`QiS)tvCcV zNTU1u```Q5zwcmg|KRFPSC?(OGRB0E?!EWk`|tnc&hCu?;2d^?6VsN#T_L43w718e zopYJ{oV4(@a=LgynDtRA14jYEn~kwLvutFqrT-U1po9%11zrY7ay5UJv{TC?h%)v7R- z6p7KI5|S^IfEpWPLdC)O4sznqqpE>e8VC{_ZOO2y{4#W7!qK{QWlZtt)OuYPuytx& zO~}?Vo|Nlb#>>j^zB!k6dApz(hmoSe*s89VCa$e25OE_1CW5_`Sv#V- zuAT9tYK&yB-`eX#8aT_rk>sS-7}hEP46Q}Ou?+<4z#kLn;KpEv5h^jP)h@==_c8Qo z7;@r^imW4N(O83I5QpN?8(TWt^$jADH2|Q(gv!c7npt8WhISaF<1jRw;S;!PI8MDi zOi%yqLdsqg3qf zJ~Qr?K@pOYk};47tNoN^^v(CN5ZUz?Pb8BeRkqB;_unHj|xeU^>7;4DlE zm`Wx@9HzKfgVs`ARjzc_+5(NOs?wEXM>}1*v#9>s>o#s(x#rAT-7V|+QAty4H#ziO z)BN#gp4q$p?BD&$zpz>@*+CKV`lx>3;Ya$_>dkNd!jxBWO4nX^aif~9v6~i^D8%5B zjrG_BU=A9|p^|8)s)nSnMg&#0`00IE#)Cv|f zPDdS$#|dEuZsKt-FD)5nHb~z4$Qj8xSHvt$R;5aeIgBk>=SsJ^S!_*UG@9kUEi262 z1MAH71t^MlWiL*Fj3N=`5o&WdPfJkBeP}v~MmbY1JS9kIKrHogdyp{Q9?CHk3#w|p zSUx1v21LhWV-q zNDcP^A+s*$ZI5<5s>qc|l6PKmj5gTvZ$B%dGQmJ|CaG_Ti7YOLxsvoHQj{g(h-o#6%3X zRr6(MPyd&PuT4k3Sw$iM7Eo^?fuJg<&;b*HL95IRima`(KIepBKq==;Xc1X6XYQB_ z1WyR6SyZioqvk*fQ_isuSWm7jm&Q0CM#O0$-f3pxm<_WlP442P8ZUDbGZ%&Lau!5I zlM(}2ltjv!5?F7Y=bVI<1;&M4roODJ(iVaGy;V2Mnk7g;BYr>jQDO|aaHc4&_fga` zMmHTTZ|rZ3G)b{q@X%9rg4TXUpCcH?HkZt4~Om zlepD$Po>pC*Coc0l<%J0@#gmXw-PTW6ns;>Ozg+1(ra?LseZoZ3J5jH|}m z!Lo0B5H!wkbc+BE3+r<1t1Q+_%t|2EQ0P^VLKHMsM7od=83~G_DtsAZ7($FOCU8`Z zDBOq68L;HDrtx?zsZEt~kE441`m*O`&e5e5lu7_l5g@~?ed;RjD5o6LxE$>*=AXM- z-f?<5a&V*7@g$vDUtg_~ADPTtk*&RpxC8y9C|qb;SC`DN=mI(NB;HVAeBfN$GJ1Qoq$k7(oL`KvzTU*aQefieS>&^q> zknixIDGVXF!Y>w!f!4Oxc3oGN){%Pek&5MV_l|qkcdnSVa-R8gQPx_|L@Z|wSy+q& z$Vo$pxff0dN~vK^yy$yG8=|Jns8AOr1vUsUsT?`UX|Y-+RSg+Y%fhn=-u9{(K;oge z13-XdZ7aYaSRz#z{5Uu!P-R3zGV#JpMd6rp*R_aPIICHcFsI;+%`pv;tLgikvp2TO zNtG2q)to5Y5Rm{+)PSlAN+++LPMVsmE7Ss5gV zB&)=n%dy+sn6+(d(GnVC3Vr$n;}U2D`>8&e`@v5TxCIWseQml4X+ zLkkhSlYDkP2c2b1fmhAbpGs`7@Q2+|JlJWYTv^hUBbkqn*6ZT>t@-O;ac@?C_2AZ9 z-}Kt2KKI1gv!@nhZnl$()7`L|nWBYsr1A1f-JKWila?W8qPgKoDy&gs140fxWL8n| z#wi&EqY8!fNAtztOd?5$NU~$;3h$cGA)o;DnGMlme+jhTMjFK;#CcPcV_*6bFCVpn z=)7@c02K^EL_unSj7>HyS@34xt}XP=*;*u(RF$+?NROdL=a^FVJSjJvA#jF@iT9-u zAZgY%PcXyLmq#GLI(5j0dWL&il!S6{sL)$jU7CEC4yV>GEl z7fS0}PN&bFZQJ&#ryehzCBoQ6>xwP}5;vTMw(pL0--vD7S_@r>B@rlv6ep9>*)tdS zUc7nf+_VpF*+s;tpvux^;Ut+u*R=a)RtDvs$*!3V}(G6(SL#x*(lVTa?9Wm54B9j@b&M8?%u<*`lxj zMcd?4r^fTxhh7j^#1_`2lnUntYQ}mu$cS9fh*EkV&B|ot`C;l4?RPr$#i(lAJ{~lP z^c+Ky9=p$#&&n8Cat1!#{(~my%gKJlQ=-DtF5mmCX0hG*Hk}=L16Js2K7qpY22v!%t zUSFiy?m-04pqI|ntjnF1_Lxo;YTM=W&w~~nWt)tL*bzj5RK0LBfdlCffKZ)RgPAP9bMIT<`qukDdH-}g>Qm<}93CFl(@E~-fzRCEHcM{} zA|Y529Y&xJYBtDGQY5t?O03FkN+2;sT}eaAl5!qZK56szWSbL_BtcMge0SXq%;Sw!qW{b%pmuWqp(TG&IGBln)Q)=!|i`>w6I~ zt`O;fEFy?RG6h?sNFbmwD+?M>`SUKMe^#2E=!k}_r-F!VESl09JEo< z6b2I%6$M%DtnIgneOC zjs!0A@m88rYGSO!>c~ppCQf5Gb1>g4-8gj#H*JU=cde6j(02iNk=q>!ul8#<#GIK- z|L~9gbTX}9fA_sR2YV}!&pz|)Z@%2T{jYpey5p^M&r1#f;KsG)#r>~&6TY~3-yeVC zf&0dlsY?xJthPfTA|yzCPQVDtIU%4}LQEn8eL8WK?qfVUTAV*OJ8GJ;C}N7rKx9<4 zDq-11kz6=`xZ9=%I#{hjp{l;{q0gMND;%Q9F8&5H?TlC3H);9q^d#ybe ziVN$PcKbst)Gy=ree-LxL)Lf-40}DjHl5wMiR)$E^sx_Xo71l8?s@4;`lIFAna$L6 zXD(ihO&Z^^)$Z&b5AHZwV8aH!SS-Hp2Y=|PhaR&?9VEz6Vys5xsnc7JJ@DAh_O>%% z5R^4%DZFE46(ypWlOl9omr_D7*4n07B!juRYG{(@gVzP{jrelRD5`!p#HRN2e41=oxfdNn*?5R`{KyUB= zSpyoy zGn1&xYD6FjmWG6|z--|}eLv{p{|sJ1L=h%B4k&|Z)_?>Ll@Z^5h{$+6PDOH%K`~o( zRfUu4;MR6gYpEYFK%>;)t#?;*^~eCqfcGkeZ$tsnV2$`#e^&yK&q##fovIb zPXt7Ug<=v#0yJe=^r7+IhAyIU*3e-yXEtRqW{&l^N{hKKEzjGv>A1$Ci%V6F9G4kM zNhC`!W`qXGk!#x)6b)il2EcIM3W_{i6#X&S}cw_Xja*B2J4vFIv3i( z1K4Q}hNvvd0_iBmi9v^xqZZOy5jYr}gvBbO2DXj3HudvG?)q}f$td+qN6p^o^!ZEY z&hG3jp1Zoct@?u#_m%g(;=x6+rQ>rKUQyTQ4)Ejir=Qt*e7>_dj4lg@BIwdWQ7X4X zS0}e^NDfpj0FpqGSon&NkWf_*4-bo?FvbAOd_Es!We8aWM6$E8n1ih}A@Wj2=+B)l z`c=CZGa{xG5Ybu^%`nUw1_0>6WM(sVr8m$v&@8JnQ{Gw|k14C`)~h+y2nI zBy!wXFD=u}zP@AQ<*S!nd;Z0|R^h7MdGcA>+o!cn1hE*6OqzKticY8^ zw2qR8Qb03c)OSip9 zBRdw$JN;^ekWf>`>4+?Jm~C0hZt7z!Mg?@KPvR$JM5LFP6(UC>1P~G0Y4e522xHge z(1Jr6OKhc(*!Z?hc4F^bpEy`@?MLh5LYk7s(5-Ai0L%hqSsAi@-<8>>)Ne2PWk_eo z<539N1GlCHHPLJt*8mH;q{#szTFM-JuC4DWJ7Sb&(HT2ClTEad!1&Otq!hD&E=UK# zNYoP#SOu}SRC0*y3MUXKJH$Q)XKYp5ES5z91kfr+XDKFUF|{2R z*+&gR!BV-3c}yjc5)@=sk|-uaMi597hRV&7i;m}vVQ8_CQ*VCjmwfb3KJxjW|GF2Sd+M&c@4j{G z*4pN&w&`3^7QVW2<;umo?|$ZJX=6;@l{pVuPkZCW%{RXJ3-7t--cNtx6NAK)8B$6c z8yh*tC!c<*Pw_-x#+;bJTJO+yT{F;0eGKdC>)w?^YhPWtwu_@`N-nBO)1^M!^Xnu^ zpoY0n9I@IM1IL_{Q_!{zIp&lYA*)E_jA+WDJZ$HPMp??D>e}RuBWF}atw)qTBqBs2 zWpQ?ha(JSM`|~2#lWgTAUogfXY3enVrjSNcHPNw9x3Xv#i{bkYcs554Ar(bIUL9fE ziD%bh&s0#Z(M zG7v&A)-0FH0qhw*G*CbhYg9DHc3Bl==3azBvLvx&p;(>2IJ@;+Ga0!QTJ3pj?d;Wg z!^C8wC)2$eO8fP&|WE-TgCWl_tvBW;c(pMOg6D^z%ENYD+kaszzLN$Rf z2{$c*EZR=E$Y`p%==ufPY%3>$C!@8Hwd-lUE}b)d5EY7=LXsSdgl=o2ihBzz+-y>e zkM?(cP6?9AGHRd2fDTqN5t*|niNj&wfwU>88Apj2W96zr18tE~&R|Ur0moBp%f1Pd zb^pjyPpqHWeC+z=Q={oDb%n26;BJ2>UVTVyB>{CeA8qddm5a7<#>Nj8}RshZ4IP4oD*?F(n?y`4krX|h&9qk^)(=u>s} zk!K!*;?%sa@u{N&-FWKRrJ3FP@dvM-x%ldjK6>lYrTaeq$n&rMywCf{$3J=ROJ4TG zv+1+@^A}!t!8%*yJm}x3ZQHfk#yj8n&Lgv$gMUz}J4+^0arCn+YO1=duci07|z_WI=_xs;CJXzv?4sCz)E4 z2#W$hm^mvOw9Z%(PB{V$#|eapNFc}T8_)^Pq9>HxaD<$rQQ#b#wjC@>NDXoJK&@ts zd2e^8X&NfZ=N^6N!e98xC%4WlZ!8k@Hx@_q$mMc!YTbG)L({e~8t=U*6yAyfLnsVn zG$vMM5K#qH=PT=dTt$$q0z{@+wqShM^V!)JYuazySv~DmX)m?OK&{xu&_zB~ zRcwh@{oU(j*LB$@HKr&kQRz6LDZRHNRN!QtWmN#m5{5y^&0=-w%vM1Ji{4mA7Og7* z4D~F1jH_im;i{e`(xiF5N&*T{woL%!6n#Cl)wpPTpc-{SP1DWBo6fDcHO?ZlI%gHA z6UmV4!UN{A8WrBAkgX?9YAqHq21)8oxi*H-h)9SDL_-LIwCo!otb18)DN9v_uw3;o z9z1_`<5UZ#pybIN_7a1@eBnn{dI(21Ag4A+2U=QVt%C6kXu$PA2sv^#84USeGzM-3 z(2*tE94&eUR~LQPHf>v#Wlp+Utp+0`P{_=zYK#l{NTM;>O72o)VY1vkSoX#fv1TR$ zE1;+b#agSV9*seUEQN6q&9aMZ;e~lxpZLkRTr~SlFI~iHR30??KR)p2Vdy{CU7qjn zPG-~j(Q3A_G()?}gEvA5h)?t}gg^QW0JsOA2_YGOIrPb@qI1U)VRwJ;@BHl_yngLQ z2*EneDO>A{qF7&FfA;dzS6{p`!1VwaLKvC=L_`rz#9dWY^)U>~wXW+JW8Wq;GM@Nm z)q6J%sRsiW#&T<~i_w{i$eO|vQXy6-72LBRsrEvM3f==L$BB6fr^Cq#6HvqJzeAgDvHBMw%zAWA?=x0{%) z`SWSZNmxlKc~6c^$Z0yA#>{;eV~Xq3st`>CN4&qNs8N`!l1B6D$+FasdADGQmtkH(wInUahH=|ghf<`_VA zG^#>KoZ{K(Byz_oZjMHsnxoKj=Cx5tfTf{XU2JV&({;tDPC0kII-;Dj_vEb006FvF zK}S@xU5=`$vvAbMQxpHpc7JPsjRS+=0^*MC5@HywQa3Mk0cf-UOvS!)C)x&Vd zdR00nJ!GO)-%QS}J^td0*3ykzH%HE0xw&7UyLRi)fRLfYLUen83;+=%69J&_cswrc zZH85@T)X;JU-{Lu+4^(OJycay459E<-}hTvXI^;Wg)1+-*mkSqaU8}NX9++!DGLw` zZA2$(3udm$QPVVo-nXGAqMX>E zt?N-}J7c_eh5!>p0;!!7PRT;dEb55JTVf>TsOknQAi6y%25x)J;ccI!KVNGMP1^wA zfR}mVVI^+r+-`l!Uc0`s;0uhN~N`tN+5K+D7CcNvd<&~>D&%EHDTCtLNq6lO_ z5z*o?5Ps6bDZ`Kr5dahcRT(f^xjl^Lq&h=HUl#4ENx(720SM=;UE7|Yt{G>Qdo%J> z`eHwt3!B>XWuk!a#LXLq_S`in3_m_pT}&qA0X&&p84Bk`=)o zQd;(ndqbGB2!SAH*qDxzfz}YOIyIbfoF6Tnak=eA8{=ml-&-`x)<_SCrh4W1 z$!r3UyMpX)-RLzE)6ahxY z>c-|Iaa>y)-@LKP94=fqJ3rjB>-QHoVDw)AZ;^v&|CQkPRUa zjU816m&+!npa_5nGC0lvl3%R4zU}PD_H96=tPw=|HZ&oswIOt_93S0kF~)`f0$A7s zgseh91~{d8A3VcoTmw>Sn-KeO#F7vSgT@e{8Uht8HD{EVDJLi7RfD81gpd$(=21C9 zqP|^OYuwPHNmdp6+$%7!AOaDk$OK(~SaHlNET`7*G%J`E*Kf8;Hbq8&!a8NK7Jvqc zqeO>l9U0~*mN10iVe<6e;_Pg^HC;crdL>{D22UG96vFE z;CMokB||_k%&f^7Z;w?SmhsWi(NLFeOegDWXL4K-f<@CbvBp??cdIMJVi%)^6>H)Q zoRfeqmiqvpio%eAV@#n9oHepdF*b{SR8=ex5~r-bs2MG1MZ}O~QkC^+#M)a6dRy~$ z@^grBC#BhmymjccLCDHw;boC-)Pmm5^3JWvxJDf0bH_+(Oo=&o?-fnxnm51Z+#BCM ze)6%M=O60tf094j)8?qmnsXLKB&(v3Kw=;Ec6SfYIl5cMdMzX{AMD z97_^G0SO@}kP@jdf&qY(^3Vq+Lc;cNxiUnSsA)Q*$0ky@Zp?QAG*Ak8Ng0XjsvLuv zm5W8@#H~S-K#_HGHoEiF_~TDp7qo~3xpxEwA~_HQObBRVO1ALBxR$~?PX;TZoJON@ zBYA$f^u|w1A9ARzO*wlqg~y&-vSzWGkJrZ5+GVrnYa3%Q#&umkt||j*gq2jK0d3~H z8|ODNt%TVXUOKVD09abEAS{3g*1Du(6v?U~2qB}X3hN~F)nuAOmXrq%4K+4VcFq-Q zT4X>3@`gfIZ=9#TA<)AmjHlx~?RCRAJ`Q7sg=XYzzQc<&nca*FJaH=4A_RdDvwW2)Jy0RWZK6@##1=!70tOIenu&usZ>)SF0ZSyBr5+|*cW z=LgA;BLz&5Q4?^HB?%iOYpu;GAz;n|%;ZQwW>wKO?CJ^t+SS4;8r77fg#v&|UoYl+ zH*f6jY-395tYpUjKc)Q1uO~9OKc5|tqR>jkA#|@izjhFoN33pQa^{?KAHpyI{N!K7 z+-c{x4?|(X=32e+7JSu9#@oNUcerX??EwKqIprjbWPz1vkQxF30U@#uK<$Yt@6Q9b zLp5Z{0D!f=?^}a98&!2t8YClx-X31PwmeH_I%>DKajn{Y{PFty{LE(U6xL_6{g8B( z9N40A0OD+cnvF5OD2($~G>Hh~kp3GYL%_@k){ytUc7{^yLLVnpsmdYd0JWruNFgRr z^unW}SnVGbzB-CIrWC;%AOe2R<#xwGw0h@9_FY`h$dx@oCLHqx#?mP zsZ06H*7`FyZ=IcOtX7Z(ye}~YPO+*-nK7nR)Ri%*i!OxDpjh%~Oj%Y(tETp}1U-mx zeN3@4E9IQpQzLh@P*YkTAQ6-fVNx3fUrr6HhzxydecyLom$PIZ2F@E>*Y%^1Klbh4 z@xQFP{^;mvZ90unnbT^OE?v6#!t>AX@9(ab^TDaCbjM?86jVS4TMpx_{NIHAXS`*3 zRVI$FwZ37e6K=kht2$NZhNc^6keoqK5Opw&W0>HW9RrSGRGblY#xNtJqa!9%1QBS! z#)byE8|XmiuCDINxo+Kf?nyg*qKhR7i3n^PbCHu0A_lnRkc4qd#(&8dZY#48G70c58dE1qPz;$6nTWo2RzOn2 zuDSW?cHZYSuvdsWnenqT!_adMb`=Bwp!kaRG*bnDLI~s#VuTP#a?>;skq|kubIw_2 zgy4LVs=;6sqgPsLfGo=yz`76-IkCGt-BG|Zt1CsSoUIjtvEDij(zrm70{P$}d&euL zYg2I#(9-#ju4foY~V;tJa#sEZX-Hm>zh#> zw~0=c%h*H;eXlHI5k(Wk8YwNrJc0 zPaN@MjL47(NtrC`idbxpMyPhw303U0P7tc-HYjo2*tz-c zW`AXEV|im^gM{)vdLJ9GQVNJMu+>_-5T%sXSSfXsl;p)`f5^;vT8u`6{nuQvvA$B- zkVA$iAu5N}@e(aAd4B9$aWjCBZD*R_yXn;|(w6xLs~fVZah)ns><&x=k7A+Qg? zN>XJ!2%%X8ZFTU=Wd#60BE~2gLS!L?l;pja^5Q<1$#Bpm=4Fy-CS`(9hzUz3AE4Mb z+b#Wg-MVfFNUG+jwQQ`5F$y7>nHa{l2||r*oQrLs(lmBFlGpC-zI5mEm)2T+$kdL{1Iv2V})4?Z@WZ)M(*Y6ZW6Ue2Qk(Rl{opdh#7Mo6g>vWr7Q5P^tT!xh1{Be|AV~!R#nsqXr9nV2CVv0ByMOf8 ze=d``2mb7@?*8P1FX?W}yK9XL)OF*Hrc<0Ix@$BRZ*daI@+9M<%ufh7IH8pgCHsdRuCmC+%?@j+aJ%jl4?B8 zS}o&zjt*ihLui~wNJ1EkNCAn65HI=#NSurT`N>UDcBZ=D@-5$T@ZiBTO_fqjQ!%p^ zQcAhBw0!2&X}&c7tCSJ~o2HpKZcng=OVf!_po%_t?^~@_RgYV(R*Z3Zd0hzQdGZ=2 zh(L1tOuMup6UtSM2owQ15{4LjP=G>D><;5(SSH3GG({I71_ChFB6D=sM~1*8(8UnK zWV(+L5J3P$U=RYmw-bl&$=u_m3S}~I2s#ll`Xp5`#v~Dj*)~nWL6F(GIFADtlGKrA z<2(ZZB}szdCp{4nO?C1M>!k_aN8gl{09i*^AB=9jzWvPDIG&E|(6V7u=s0n5xtw^~ z0(UO&&MpU8gb=|y0@hj*0x>pC(@L{3__?C(SJs0w!)ok6N&#*#PCA*7;l|xFy+dt1 zH&?B1SR2pRV)rf6QJ{Q|V%y$y8(eDv;sSTiXw# zCr@PC)4V?tW$hCMmMXUi97OGA*N~Sh=r0icw=jcf`g1ck`Iv~PLv>0 zcpDfPn28DTqCRDk0-C0o><*Ku~+o zNsw8)fHSt=vdqY@GPSR8x!nW{Cn&f+R#F_(K} zu?O8ij!7p;j1Z%VUPg-$ZA52`5F&W*d_<{?bpR9@QcQw(-i0jHWmy+lzTWHUByG4U zMdE#KZO~*zs1lF;o}Zse4~-1~Xpq*qsUqD8gTZ)B7wu|HMS>*AAhC|YdBHL=Geyr{ z2mug-vCJ3|BFM<>e5kEKA)IHD1f+Eu*qHUc8&xpBz2(NfD6)R-34`<8o#`?}W6aF# zeA6_6vE3HHs=sbG#}%dI2T=o!oJCNSC>*CVQ?^1tOq9rNv-xx1dBgkv@(;m}mX9o- zUK*5NI}kB-aoR#9n5}iAx~Wa5fmu-Wp|$<0Z+q9?U-{9uUj5o{UirL3=jZvW{S*2h9Q-%iOO0kqg6?q zNeY$di0-Pz1=m~6&_{>?P)l}f0ibcds{FR;ZitoA!dRE*d5A7D2-Ol2mq%;cW_RqL z&kroG?a8)Hy7UQ>0OUDFC6mZ5cz^lV_<1e76hfRof1%s$j)tQw%dBe_=H`1FYbz@& zo12>x6ZVVav(aT)Y8uZMW58c_vxSLsEi>13y=Tv!)zww!f>KEc4p77xfEke)!c+oP zL;z4}!a|^u-r5kOB(latAH0t-0HSctS?dC!w|-&)7XyI2=ui90U%8^02D=cf{Z3f;(S9y##oBvT@)e%CN0w#0x0QY01|2n&#$;FNhm6vrPaXI zbsZy2N}mWUrBrFc{n9(i^K{jXgN1IlTMk@Z$D8)$hx?850<%VvV94WTnm72T$K~wL zIyOM;fgjux1tyYH$)YtNh-ulax* z0WLuEqFuVNPH;>Ug;FCjyTC$DZX_`<5fGq25*LlEtAI$-*r23>@z`_3;6+_GKEmqe zs#LkeL`u2b?^l9#l;g$(lm(L|f5ndO;$Vk5jVz01>V$@d1m`Q(e2J(xnr=>%cmAz+{(ZC9cRCM_dQ;1d*JT7zxu`JD$J%*-)g1RrrGT4 zq9bV75T~|D6A;ctk}_J>kV1$g*wi&6Gyoc9kQmu#B*)$)fKrGmh^n%kZk8w9Zl$ZE z{@mQmw%O^^8)t*yRHe)e-g_ISRc64JPHfh`lWCCFR!&1g5gG&`SqK3!o6q#NZgOZN zo9vniJRu|nN9Vm&DhV6~spqLTC-=*X)k?qLZ@1f%2{&tPr`s8i$7jx*xp3jUl(eN! z1%NEeyr19;f`}aZz)>qz*R{1lCwYv~7?bBY0Q7pBTLHt91>!^@hHgTIrco191`5oC z(fMc{Mh1v%BhZ8lj#7$AE(jqQ?_z-AxFizRhsFixFW%n(h&eVf#1KdT2_{4-q##X1 zbzNKQ>)HSSB208=TcIn22%)+77a1Xdu^?G@3R+piZYu?ua$q4aNfJUylYvY|Aw=ap z5ZXF;V@amgSy$D*45f`}Azrehb&9Z@NA z=*gl~DoWH~z4sgwm0TPM6d0`&O1q{B(IXH?j6ozx;f#Yc@1X2-I>*kggup1Y?q&^SET-8k@+W`szXy3!G=2<{Vi{_=3C|C!(a)7O8;H|?C+xc`Iq|Jj$$ z{pNLhJSds41@A1lNiYGVQi0<2UhnEFI!6v1+kVB~)$vA$I1)${oCHw^y{6S*1f0dm2<1} z-PYPDwztis>DU=7xv&99M8xQvkt_rephz&2fEt4o!80|1458zw0ZNmM8#~j<+X*3r zemU|Scg)R=&z?ylCsIZ01AAShX_C|E;t)P42X0|w~CA$I5GkNPMVHpYZ++pp1ADg5ZcB@H!b%05<;D$-F(?vJoRAm{$|M)oI&U4(#3o%hYo(yFL825` z#Za?Ps%jI`!Wt9uv}!`X_8z2UlWPHyO$Ezkc=hi2UwH8yUw-(ZyT5p_E$Hk31FofF zq0odjwo)~2jASJkGnsAzgY!yiiPCz9Bm<%`CPW|xB}gLK;DLc7bFe5<@?M}C4*i}h z+i8pIP0cZOraNVAr;{1$j26;)6Ch-HYqZ%+bz1$^QM)zkRW)>`Uo~2E(R%iB4B#nt zx3%JP&IA13`|oxBW1D7b^)Em3hF4tq#k276>R8nbmnjV*9ULC?Us#Z{P!RF^gG`0wtZL4 ze(9b^A9{TG>Kk`_`s0tj_|8MW`=8(Q$|#h(dwX(i=xaj-@~_Y|%5Y8hFk`iV%E2C8;R#jo}bMP0x0Ax1rN)24|F%5V1&8 z&%W{&g$4oaA|P~ILi$F9b^t{Jq6jH99$JyD$mqb?p`(}xhs;x*Y&f)1kq`nIh!}|g zE}H63V2~{#*p?gF#bTI942Q#Q+ZIlqI=-^9G8hbmK<`;f5o7c|Oi)jy#YB5{v7usL zRn=s0+gb~rI-SmFRF-9ZDMBKEA`WoTqX2evGtUG7B2lO$NfAR7B%=?J5fLWck`w~s z7TiEmQdQML2o7e`i4Q%!WwT-cbN<{46zGXkOnw8$Y? z>6FS6h+YW^l9^ehi2%_wO;%*b&zzf{YNtsOW30-$-RTbdBPoU1Fd@mE_aqbr4hCYN z(;I8s=DK&@^_A;yu;2dnSGMNne)MhgXYYMyAUVVcRg`-I`39%k#~wIRTzY_v<^5*3Pi}E!FZ@1Ca2C6TpT>Udfl$6AnloR zk3W6@UiF&mSDt?O_#u-{xBmP$KY05~Z-2qu&L{u+u4krZmqWP!)bW`ED;KKqjvX`q z?Y`5$_ID5N*{1i-6v|nWqPJQeDc5efbzhJxt1Fw{#_84+N2`#*m|SZ_F#;zzhKW`- zg0vn?lS2oab=^x~57$k*Y*i-T;FCTL>Z z!0hMe=llKs`Sa(8!yzJC>x9IKN<~TyK!_OpC6$MB0DvfBG!sQX5n1b|r)STfzrZ~4 zX5$zk#vld2#R*{Orc@g%fSs`^>0oU15eb5^A`$~A5)B6gu+9NbaAYwtRi7;GI&$R5M5=O0v;xPEAHD3Ei$+Qy0X;FmbjmMS zc;TJP=XqZTBn0mSpb#Pk&{~odB!sm#OUS0uc;{k}q@6V!DUi$~T_{JgyV&j4%l#@O zBGkZKKQD<~o=UbJZxPG@pp_DWAViYFTSr6)C{S?9BU1|@*tnQbTIA7IO>dydTa`;= z!*ByS?P-lVBZdJ;AL?4R(IIP+NzrN?i%0^9fl+JkV+a8ix)8{@y;m&Q#sHKAsB{=t z7L{}{Qd`&7a+j&(~XvspSNRRv<#j=c{Ye0KYSc<$*{_j2C_(VcExXf}JBcISL1 zVjMcy@S6*rHaqaq5ov4cBuOHoV{pzQVj?6dB_rC#X+g{&NhFFWHrMPb1c6ARjqELD z3_F}w)``Qn@>AD-YHG`*D^K+rer+g=;JYj3As`1uoEjNP^WY_yv zYVSrW2CRpSPGNKAm2dpM^k2R9SFU=`^$QDcx#5=Hsa4Zi+}t0ItO=5?)0*n7m)$g} z7q?`c6j{V3K#V{GYcSOnXF?OVD9EZv#YHb{D+cm{~JKb$b+zi3^$iSJD!UZod1#nvl1uu>4 zd>yi#s6Rspd7eId@Tqb%F3XXWlEkEN03t;st!nD$BZBZ= zCxjxXnJKd}e(ub4eU9vkS(d3(C94dLESW9e0}8XGO-(M_5>N_BT9GEp2Cyg zXg~`eRJ$w#8#6znMD%s48y|^Cr_$67DJ`To4J1lLDSQ=#mdjqlZ5J*;v=T$Db=V(QOM`wed%JS!{94*hcw=pRZtC}red-_I_x`h=Iym>z7mhD1ZLYcU?3tCZ zJEjLiciz|)z0(`D5P><7p`#1(p6yWP+C@yV6s!-? z2U3yIGL#Or%7pPs#%x-W4OtqMyemoxU+PUY*(L-x%YrS4iDdD{b{CZU~`4cXh%R`K;6RnhLnueLv zROUqrOigY3{XW1&73pLH4k5?jh$J&J!nST6V?zLw^?i;x#(;>x(R&|akff_%1t}3? z3}Ah5&PgGf5WM#jOIid%yx3MW&URW_sdQ3wNGW5C;gTYL3#!DzdD!)_|*Qw2`g%?7#-M-W!Y1q8yCzA(BLk zp4pL!%|HK*6KIyCm}~`xOMq^S5hkBMMnIf&umTW-&{_rWbSjN$n!0SaTcgn^EwC!f z(u%gt)|Xc2zvJyMZ_OS#c(_4*e$>nf8QCKWB|&IXNnve+2$0Am6T$l6qmm-pXuahG zS}`>oYiB`W7*-j86;XBbvf3;g?*c~81&b_8dnPQ^jo0ejX0Ph`#}A#`J5w}f-|ku1 zQ}wHQZo}3f)OKxGvbN@C)HsKkU`#D|7t>4^2(@pL?bA@YD)=&rz6;~X0YEVZp?kHL zbrhU#pAn;yRR<-?glD82M269L+-?_no(GQO+O}GGRgWA~(V7*Apu&dDPoEuis56d? z+G|e>t@Z^RPE+#S!;hRjx5SLSm9=9>PuV(s{Osn^x$54({>VNmPJr9mG$d}cK%@;f?v`~VWMbmWD zv_9DE$7Z&jXBo+8*t=9G0348`4U&U#OHl(R`(F`FIf-Ch>*OGk`!Df;Jlo6m^?2h7ENU}T3T9KTU(nfjvxZ-tde3PRAuHEDRK-U zj7QaE8&;>&;fSO0plKQ+>UL&_!|^1c@TGxWLi0ZmGzl}{#ZdQI~>Z@P6@A_-5Dw5OFcCDj#Zwxunu)bclQk@7u z)JZ0RT!KlLqOEIgX&IeXB!yIpLR}|{vUaOCEMs(?Jj0X*WfLf}8Bfn`d*sNeqvr-B zR|Rk(bj)F1rPFP(t(`2ivyPPATxg=Y3LTNPr&AB!8|V)zrIRE9M1kNsmPggFn!A;RTCxXW+eh$yoxlTsc%dUWN&GG5Zj#TW@MdL~Q+ zV<7}YitM*-+cqJR`~Cjx>}-;%jg5_>D4cUuRc-kUT&~ebv~C|vTR zeqLd6Im>(b0{{pDNFapxqFqVoc||%#Kf(axKpek13Br5lqeDcJ7dH)RB5*zvLIYI8 zMn-J|x&VM0rHD1S@R!;UVjE(cFlXRc7|)YLxBLnJAp#yQPW#1J(r zkRs8oYCLFnIIwGUKISUlo}RfdV#ZD;24xd8qbDg6SqKN|{de836CPXhzybY<2cH_7 zvQ;FtHIJShKlrzIz3f}x`VSxazmD!HW;>_v`Uf???|Xjw7wo{i`$zp~Tn*P&&YxTF zHRTfzA2|5Xy zVb4cC_~&>2!`bP@9ignNrczc>LRDlNg|Ev%kVr98h(e3n`WR#?ZILI1QZkW+m4s5r zL>Ebrx*VDQ#wby;W+yW>jPAnv#zHGs6b&&2sGDYWY|r%KqY)FO3r(FuW|F&EzJF(D zbJ-`P_bp^QcBTZ@R}2PyH?P5F3c(JY&XRO?Q8cfA(VkzqdmKeeAWI3cVP?*fEO4BB zMJIL2MX?+J0L174ShkD&`0)L2R80_K48|rxyT(dDPcW%Z ztT7Hz_z021#0d@nn3y7>02d^I^?<;H62ZZxrPy0l$>r%|E7?7d@eS;e37K!cvg?LO z?1H73Xk+b!EMuS;VZ!*rWC*~;JXHkQ#>QAC+DA1QJ7<_k8c$=#m{JJnAO*!xF+*fv zL;)a>;+AjI<)7zbS|RitH3K0s2lnhS3KoI@iAHrj4P1??+EuP{wRJK^m}aB z>S9xGH0C@+n4|ds&e_PGqZLX*L?sjSp>ZyFPaz7T5CR|tBp(Ah3jmY7kH&Zx*t+OK zL`rmUV~|9h;rY|AdHe0DuY1pH{`QkkeBnz& zJ)JBy@`{~lbwS>E^LAH`pM18z`NYbLuinuv#)WHQL#1OfL3lG}-)q7+5^)r4XFJ(k z9vxUj)QQORj#bc3$hf#M?sbwPPg7f4V&h{y8dZX(i(ECPuY~qavcqSOt(;iLzNeOi zmSpB+#Wqug+}X)}-a6hZ4;(-3MNzu?m9M$^9dEwoSAOQBtHw@E>2LV1-~FD~eEF?! zx%!X)=xFtK(auKG+S zGB)TL0(cYxl>)$V9h$OS>`EJ9T>F`wY1+*)l2Q70r){jShm~~fgNqPBLc*d5G3ew} z&qsz-U5sc!G?VJu`ROzh95fjuNh)VsO?P-@0AG3PR0%0yoS*N^b@UZ8t+@=keI;5t z24nq9@Yv4a=?(mg-#>DCZL{0$QnS|Bm?s&K49*1xtt4Ki%mW!Br(m9UFf%a-LdUVYm~0&LGC@gNBPH#2U74z?nbC(B!(?xZ zRvJ~RN^h$`RaE4-HyAZQX^!Fp$R)#{+{@LkkX7U}=dNxBVjU3dp-l#EZi7FE$!8fLI zXecR13?;7h>l#8^&3b5!tZ|z)unoaUt>zaxLR%8Sdj*b~QNu0BvKjYuGhQ8Nl{VQ- z(HND=WiL*zhC?eSU0Kh_EEc?T=Rz2*cJLKH{v$8=9_y2I;buWH$+kETfAO6jiFTdg3vlqIHQ!T`o zzG~;6{?&7@ddr*F2Fp|J8Fu}-nFaM&{{$aCS>tE#e=1e-ReKgkO{qk5AxUys4ojJ$ z4HdCYwM4VjY?r z5E2mk00>GcW1LP@w2=@oAV|^?V;ur12tLN>C%z{FV_*oOX>5|_4jmu`KqQhV!euKn zwn(qb`@nymaRenoFaY{oYz&&Tl}Jbxh+eN(*9`zc1W77uaNo&`#IKdP;ORbv-C|%}uKqCq5_+0y0dD{@Dj+Ha-x_ z##$mG5I%4nI3SLM9-BGVJN6WFi@SnrTazuX4b2EfgShGHs%H-zhXZHV-?g(lBkgC`=_}`#ckP@$zqy{N zJSEudRdch6XScmOAJ;dNUGw+d`Kj;!(Qke4Z~e+ui|=l|bk8%Ne{XB=i!{)0{_=1B z-0y!G?q7oF{>w{lJ%8-6e6M)?p=WNt{Y7Wbo0(Jg>4WPx3%cuTr*>VpV;IcQ$Ir~) zw4GYymQJ5rU-^-rf5(Ua%U!*zX3^{I3kwxg-iIPd0`!eUlSya=8U0e~D1hvV^h!rDkV0YO5HF$S2lLU4)GVP@xCUZmc8>l!5! zL`>6sZEbC3A|ODi5xqc|DMaJEmJpniMMp^;fGJ9;(X)@9i781E!vU32 z5t%$n)s#(2vc@Qc04Br`I1(Tck&r?J09f1H3>+n)w?0*pBjNM8=HYOpm2x&fj8e)FVi{rt*I17z zI7UYD0G_GuEiL{yp*1ck^#DCdcVsR1@2AyV_XJoM~iyKcDUxd*@0K74p)Vew^e ze)pTMxpDvQ_w_emaP)kpx6SO>vu*cvcmC}!7cJdSrhn?2e`xL0%FA|c|Ki#6+p?@k z1vA^mj>hh(-ulBQd#yrk>vUeQsJG827-HLNofNa_YH-ur_7xsFG4SlHi>Y$!LM-ff z;)%7V?;Jn!+|lE!&FS@K9HR$w)ztQp9*oz<>ndKUu6*Ov&Myp4CA#sBcXeCku-tRy zu1=cn*g5^wv&S}0pSkXe?bU|OuZsI`pGMqq(@k@~^hftxf5rBly9Rp~u6ghOeBY~Y z|Fboof9|=%d(x2~bJA|>>)^LP_VnqqtB))V`iPGmT`!RzID2fRADgcn>L;7myzoq3x%8!V3s)dO>Zeo#Sj$93tlIoXLk)>0e zVzb|GwL0xiJ{&tsu3NO5(Wq`35+GBN=epxuZ%iaYp6f)mG&hn2I4;t*U{H#t(m0oA zn?Zf@*|p7~0dJF3xF}_k8E*(jAp!~#5`93WwW7APN%R$x2tgou1RdD1)W}WFs)Xsu%)jzY<-X{s#ChQr|& zjPg7{^db`!nIrg&JcJqPW>s;rZf2$vfxzAfrCqSrc`cKP#X)QU5l|3ww5FDx19QnP zfRD^R28V$pMh8gJf|r`Xi=|=II?d2o*{VaCqTovv+9aD!&U|vtmxCH#zYqY%%9b+w zF+MdwH7`}F(FJSGBv($PJxXaQB_W~0xuhjzv`s>qBZ;aEJy)})B!(t51E*AI1wV{y z^@)4`ny$DhE+5!~L{zdvDkBb5A{T$2ULw%)<|~+btDKg4onD#{z`+5uvJ@b8F4X3*0W!9n;0mj$Uk>sFPE5a^C|p zs~7suo!DF%g5&JexeF`Jb4%sWIoHl*n!)b9MR2i^g`o0kF=acVw&ugMzdcXHLhd&dm>h?63dwPk-Vke&T=s=I{Q^ z=kEQrdq4AumutNCrEi#DUVg_nzO^d*@Bg`98}yFtykh5^@-gVnsUU|jz-0aW& z%15MXi9xeUa=E|84{hq#ziYL(J~xv9oVwwb7yRKD?%J^{O#~@ZEeE|*%IHT%Wja(D zLZ}re<)Co^d~DS+N=!N}uED8%%2Q0DDrS5~Y_rXnlA;j8R;7s%*=a_dHM(xiNyk_# zNz?!Sm)L;_+)%fQg>WjY$eAp$^> zrPc!gXdxTt1rfo;It2wXNe@hs8HKkFnLRU4_8CTud@*Zm?eV}%7TJ8s5pL@$5K%xz zkK4Dk=38C#Lk)n4Rz#U-*+}Pt)_UtwAW=l*=tpcQ(%6Qku@ty+F}JcJLY3yux!}tb z9l?p;`{N7!(MZ#t7r!JCv(smez4$E)y!~~}iNlLO_(O2z&Uo&;UYv*4VzYEIJbnM# z#>sT*+U1kSPHwITCC}GxV-SaHqm>JN+B4T%DfjKBYkvHDmp<@+vr0A{?Mdb%ZY8ae zpbS!P{Me3bnNkQCeAO#kdS)!@K^Ztn=PS2C2pXXid;M z`Uu`ZjU%nKC)KjLZxa%}D2lnoJ?yb5n-Bi)&%g1Uf930MK6vyi-fkOi;_a_}&C==P zhYy~*`9)WcdMi_MrsNCjr`K*+@ z&F}j!pS$JC^gsUaFaGRLeMdSo^@0ELbFX>RxBS8f-+$`q=XUPC>22@0cjesS`|mx; z8~5)@hM#!07Z4O_Av-&&syuqcV`s6m9nP#1jP|8|&FDg@s+J3=e6Sj&G0{f>TDjC~ z@XE0SIR=|qKdS7Gb|Hjjl}F7$93Yb@5}mPMjR4Gu;I$BWR_@Xniq5%mpkxxGjRr|7 z*NRgjCxvUnW_i62D792EIFdSgdpQ-eg$4*Iy>(j?$uUL(p*0eUt-+s3k)hN@rQi{kEOB4k$*3Jq<30OBxkkT^;j_iVqf%)R@3uKBs(SVRdW)j}} zREmkUwic4(WjZ&aEs$tS!oSr7ZFNafpf}Cp_L+LvRE?QSIzBQh5o_n1Ba$Il1gfek z%`zcG;K&RSgk!c5%El?71FC^z(UvyCrtmVubO>=ityC3fwGICCqxU;z^l|+!eq-(U zy4-o?)koLQoIMrlm`rt{8i5iXC3rXU!kNtr)wR>xW_InWs%B%@du(|4jW56P8{hDf z^4JNQS^T3vdTjq&UU7x9!SeEjP33TQ*X&F+c4hDCnfW(gxv+z1CAf!{Hh0bLSswR2 z8a7z~)*R1aB^pvdh9FSLfFg6lQIj;DAsCKmj8Bp%5S4&5K@ik4Ra>jo!z%rz_@_Ve_J8-qzy0#fuiAax_q}ZS#9fbm*}vkg|K`ER9{#KU_Qjw0 z??2Brg{Zqbr`PzbvhiqmH{d)ds|(&0P4`8%M&`#=77Eik|Lk^5ivqN{AB-}_4+e(Rg| z&+WS6H-GJae(%w5k1e|U_y72tf8-|`=>Gfnz8lRZKFeek|F{@{CF_LgtB^&9_dz3)n}x@wn+?>20h>i`5ClMpdU`i>RQ46_#5 z8FbsX&1;nlruS-hwD-z?0Hs7H zAs<)}L_}auF}et7Cv;{Yir#x-iZK!rN`WXi*cc-adV#?P=L0h!BL~13NnkEz9RZlN z*31EUvUZfWoNJ#aSJ`TUCYg1sa-#?WAWh9izxG;{^*Bp9QUWI=r8LHofXJ-%MJI4% zK!yP90D?zjnPqe)W=dOITWhglLnMOohDfJ0DBG>+s%Vc_&hMGIVlZ6SS9f;p+S5Dm ztlTE1_H8pgYg@WlwdZU(wWB!w&?B84(+@m!G*M4%_$Fleb#t?p-2eXbznAUS#@f?Q zSF5iWzw^139SN+;kPVwYX-!`?gyb3)jii?>_SE|9$rGyvnm8F%DbY ztWcuC<+|-d^pU*_5j_A@)>RBd8i<)AplS>gkQD*7XoH<)wJGFU$?yFBcfcGhhAM|N5!-(jS(GaMk&#Y;K3R;5|y_s5e#v#WlJZkJ%6CTK2>DLH%D_tiwD)P9Gwbd zgRL_cMuIs)d(XD%qeo7m-2+#>;GzHXf8tf!?*HoVzu=WW`pm)A`Aj`sZ;tLc_}146 z+n#&hAN}d~z2~imee0Kg=6}5B-7kLPt~+me?WUC7-&O3|^WKvO6}B&&JaOd1 zA3t~JgLnL=+n*`Fw%_XAcf9hYS5Ef^qnVtuMrY};B<1T|DG=nnX2OYk8Ak>qYBu5e ztL0L)5@A|b15;D%j+W1DWQh_i1(>EPn`&4TB6zvFy0LBd-W^j@hfZ8*PtQmt#{)=H zsv7GcF`yh1I432d6b=X=Y$?mOf`TtOkZi#PeA$f8*DLx<>E`nBpRK@$i(AGcBFYJ3 z0vClUJTYFG_#RAh3IODJUe`4MSZf!z&2L-W@#v$EKQBc8#ZqPR0tU}ON?}II|Mo=QZoY#<^LgaAm) z;bJPeyv^CVKwH=1dFhW)Ai`po1_LLOB++RAZW>#TN@L7~SqdS9;FL&-sBw;B^cWfd zBBj$@2qC=n011)KvJ~{HscOaSLx$0o8|WI)X0ndyBE%kdvCdI|GyF0Kb||^R7M0|S0b~*6NS(?WI;qR28@D8 zBYOl!k070EgcKaLg8=OPm~6?5bIBuLJ^kK)|LUt>wC{<(zVpW0UpiIl8((;Re`zV4 zTz%J%{ik<+-#4$UtbFnhKK9AK`0DH4@!Dq|{D<%S!GHbH_>n*O^^g4UufH1_zU%*d zZqLnotDb2oJ#^5?Jy_!aWm`+>sgaS=(ln(DE^vq_rG|j)12P2k<2Yh)l7wJM5rtxs zV1b7X11A{#bS4SB7o-(pT{S_?x6H!|ugtxwKv;X*G1E&ZeUofz`{a zy;iG(V1fflN@vnG&F*WqH4N|m_BZ_UfBT!@%OaZ{Q6Hl)J{W*uRRchtrKTQTRHE_M zZ~t3u%T`7ZVq(ueStIjL22Cb+)5+qF=l$sNdSddnG--1}2-ez(2wUGv7Op)NCGRm7K|FwyowndZDd5xoD>}lk2IrmWs8O zNu~iki|E-h#t_M=*5|*v^tzYL|L7mS|K$CLmsihk+c9@!?Ku-W`L11G{qo~@Zl!;? zY3qIe@fUyQhkxt)hD*aEUw!m*fATlq{R=;E_|eaYvh}scj-7h;ROoV5T1Z4L<#Zuc z8>X{#R#U4GMXu9Cwenm`RU}#iBn+bv3_Fy8QMiy-zSUsb_suvM$I(ZKE=miEl!eKQ z*}F(Sw(_)<6uNMkr}5g_fxEx=MOV$A?+sMO5oD1o$o#;?$Bv!Ssme2b;oPX=;Va+p z9bm>vE&C&)` zRcR^_DW99$wzzlDYer+c=f*3NLC7KQYR`pned}#EjK!tj+{I$&XjNb_E)Lg z{oS8_s(s~*h-8Q(sx(p7sjQQxMAEn}K}$e}v6IM!b~@L}ik4;#omQfRj6p~cIy&h~ z@CH#4Dx%y%TC#8yA~#Bs1X7t!1z8iufiw_7=(Li!x_ZMkxBupkykkca>%Q+fszSVG z=k9IWd<1>`+%j~F6Gu+B)YROSFa6R-KYYdOuKMHmeb?sFg_&0LvaJdtz?WUO<7NA| zZ%=S@H0lTOhaW%srRVD5nE&FzgGa~BQ+;{gQ_r3m$bY!!k)y}YKltdmN569Tikn`v zbo}Y5>FL+J^VhDr{r0uh{wPVKil}L^HSYjJ8j*+XTs7V=UpRNwbn=V;{?$uozqTGL z#R%x44gs(p4N5zT6hoZEiPH%)h1x}c;kKE5P4CPrUUZExhq+*mnnNWq45a zvTiPmTu!Aj(?TYqok|&FLb02s3q`jr-IR)Lz->kL`#( ztV4rl`ow|9WHO~I5keSm)~n;ez-et*0$M>@q7W=(OvukF>Qb1^lOmC`I&`}krP?W? zjPhJ&B$B8a6Dm_Rb`V3If|5a|LMZ9kIM3dps2dp7b(_)%b&I6Uid$Z^{oLTgfB2hk z7G=ebUU=OO9_p80zgLh9rvB=^ht`+RZk~LuTQqmP>wDkwo^R|m_O>bY+ExrsCpjKn zYL+Trc4%Sy!nM;2+vi)K{QN@~oYP7A$TR0`82;l!!y_Am)BULr-Fy6o#m=Wb@`>^0 z+S2hOH@)H;Xm~guZHNrf*Cbp!%~yv*Mk#8My7swMyX^Sezx%bn`ok}2Z9HZB7c8|q zodP5xwLs*yo!i)=G((lU5E)XUIi*tsI_Cf)LrBxqm_}<^RpY9v#-pmPOgXO4pTDq`keDYsH~z_j^sN%;^1lku zk*!1C(-tCFCgW`uVjY4H(F%bWF?g0VSv1R$eA86O5P{G{S2qr0h(35{;mN`t#?ocTT zK>{$?z#v7EHNi(<8^E%V5Cx?XLS&N%7DCltX^ah8V8tK^Eh*mmlAD<<%E9dv>s~ z`-<&$T>u0vgpIibW3X~v6(MY@@~GHY;R2|ge(Q6g-#t%ZDZD}W8O)pll({D|C!AL zPwl?#_Iv;6Pp^CRjc@z$H~rzS{?{M;jo(^2wKTQo#%;T=e&)GDO z>8OS2e4(KIGud>jC_qnZxlqVL=v3xKM-^SzHkWh?J*D!N(3l`6Q3^tqI)*Y5c&9?N zkxTE~*!F!Rbo=n(^YwD_{Xg~Vd)nf{`udr(92u+76CQ5aw|K^cQI(80&ffI8x7JVI z{}(_1yWjYdg_U!?SMS=kKk;bgdKjHLe>NG}<M3EEoZuOSKV;%bDv$W)>P~`%gVE)S_?m`5|Q}x8-s1zr$79w zZ@FV;c=Xh9JKa(3nWRPIJlFXS&QebD>Go`zc7`_DSQ{rZ3rrehRE0wJKCn=>X=*lZ zxtC?UJ`BKC7GB&B0!pSKwINKTBX+75F#^MYqKoN z^E^${vMevxr~dyvF6%ok=AHSe0#Fk$KunUXo#wepB+|s$9}tz+B!mjGRdi#TTRP39)+$9UV}QyuHH0M1C%xOGT7gRr1Cz8mIjJqd-d6cHNx+j_!xvSy z1|tU{kRbspWWtW3H!=bu8V)uFXKZY&Kw`vT>ku261d^`MFx$$sf+$=_iLGyu6k1pe zQYWcM>atOoCuuiSqLRv781|wtLR5yaYD_s^?e{_}ak=VMgWA@?*3Uk3@FlN)d1tnD z*GK+-+Nx@GB}tf2UpRQ~=K1-TzTo!HKE3p@Pkj0MG`V^I-Ukn_eD&1f6r&rB-|~WM z^I2#MWn^qr;lL{(k}_e+2uG#QIhmhrcQPr_&d;>dgl1-1RW+*WzSh)ILJ=ae&?*L( zf>0nUZy&as2M?Sia;MhL{J;;!xmd;(j@2bUXueCI^wF>=sc4#-oweyW{#-nO})49Dmn7p<9lwu zHLTM=eBs^~e# z{M3U_mX(RZ43$i>y!ocrU3>SVPc5A}@wJEU+kfk8#*ZI9vUKP-KmE~+2Ete?^3foK z*i_B%{2FgxX>77J*R9SawQHo5ZMQds%&$=RSCwbOwb!WHl2$r&` zh2BOPAPG@XEJ717Hr#B8u(5pVnaA(_(hK%o_1}Kyw^l9;6;|DhB#Qpp+1qc}|I(ZG zd2~L+Buxb<3;_tGR^d_=GYO4J{`A-EbUk58E-PGi%OY?*m z2L+3wVCE!A27|%H6{-K9$Mfo<$q_IRNkGv}VrhbsB(z|l7y>c~2H+5#FU!&xQ(IG6 zzcw6(s;SFT60-4zuy%eNo#Pl0O!QLdRI0X2I%!_{FdSE~g+(*P7=66BzkLFTOf+nh z)75W?A?_KJay#I#^x%e0YepoqZo2h(7561Mv-Lu z_UvkG{fawYdg18FNACOD!WBE^yzb7-ubl45P`>j$uZUOf`@_HblpJo}aO3tq`=?JY z=>%SM)pZz~S1;_i@7W8-H!K&9y+~28ke)#UleHvyR4H5AZsH}H#kr!>Qkmu~;rY4t z>`c2TltR!1f^p?gyQ6x#MV^ghd^qxI);lab_?tb#jKRkWmi^oO>SF5vwvez$Dz=cd?(JE%8gqEBu zY^g*^sk6K@J+(MLw_{;er4;Jgj-xeP*JTs^rX62(-L?zI56&)hQEfZ)_}%~d7ykWy zcmKvczxJA+`w!nTBU@K*pIAJQ)hvahwlH(n_hp%>%QZ=jy|*W`hWeCLv4BXj(k;y5bLCeJXjxn|2toD{@61X((cSv4l}2cXk}@^ zqmE8iRWw2ikw<~v$6;;j${#(ocJ?vi>4gaJee~^6=T>r!QBl zZ@qxL_gjuCLWq994@{FuLg(CsTCS>EDK)`R{>9{S`IB2+*4FdZdIVf(Ym|imW^l$h zV@z3=!{M+gs}O<|LI@#|fQTRpB#(jX8m$|caSTC52-Z0p>S0+KTX|ctuT2$YW?j@O zy$CEJ5=jyYFU@FA1S}J>8U877dpSd7JYKh}({^E0MTd|pnwsvQkV%pd3K1_hP#2dd z`p`rWk+5zwq7oFNCnDv!4OH72G$FAB)JYN$5~X{tn(DR(tAo|m)jc;~H=9mrW3uT^ zDOt7KsI;j}J8y5_x$EfpGp+6OYwNuWC(rMgpH)fHui)JJg)48|bKOf`cIN|!Klayu z_qtc#nik4%wqevJibm4P+No|keQZ>nuibQ#h5_q9fI|+p?~Nx}3v?+&)FlO-i|M(R z&T-q0`NeHBQ`1F}rFlZRmXuJcsYQ|^M?xE6G&0V|1}W6b74>%S*g1Og%MU;L_!G}P z{)zwbf3AP&o^SsCH@x>}-uG{R_(xuO)h!?U?LYYD@B4Qx2iM+y<8ZKa;E^v6t9to! zpG`WdCBeF}PRS7VBW`*c+2Fiq@LEIB>UO)`ZYQ56euy*)D|LHTm z17|nSZaT}NmB|)a2;;WmnN-i{GzWy>0f0H|Ug*5=n&~SR#qPP*LU&q;&gR%&x&NAi z>KzN$QY`yijGsGt+pAvuz&-u<{pZi$c(wVafAh+(9=-e3w_JJT*m-;Q_}u>eGkb3Q z?C*W(6Mufs=J^BP^n3sQ-+t`JZko%^ua$0ESH>V>UD=bzpZT8ey8XJ>zvGS$j^OmdHTZMTX#!47&USKjn|gX9eVYvuB=BR0W3ghfYYm+ zFSs$?yP$eCL!LzI95)a}%#skIid1>)f{(T94Trs6PYBWJO!>f_R+n_L*&B`q76>8fE_P3&mnF#$;x&`-voGG6&>bVE%du z^Z(uTT>khHiUAA+92B^5?EpXk1_mDjz@_BJtb~LB5HUe+3l&pDi6~Kb^VzH@(u6>G zL9A4fb+ph?YY&VrjEx)D4FGHrSd(LmMV@5b$%o&P@jUM!B8c&-J?*erXK6bU8gGWS zQHU*-0DwdG5FvQ-F?i>YePo58Bu=$;uAs;ht*}TXV(b=~K(6Y#hR|S?fQ3h7=(am< zROMNgSr&miB6Gd578->d8I#Y?=5upvgTcbwcJxs8>*<{{HKMIJmctjl;6*B*x$8@x zYdN@W|IJSyJ*kq6iJfg`P%c1vWvjDIP1k7l&*d#?@)jn!+MTwqot>S_x)d{xLb5M{ zpHiqqB3i|ct9E+=P}rJkEzZx)&U9uv?U|`orevPyL?i|IfTd%@SRj1<-s89IyRJ38 z{o^11_z(Wt554G?y<;lA?L57U$Q+PVnifh-r(!O_#Uk5Q&_YXY@95c<%oWeHvYiXFv)z2Y zrKj>%ccxXNJ-xblYNdB}bz@@?BqYYhvKq)(O;ecRkU?OG<9-AuX7l!rsqT*1ba5VM zW{cw|muIq6Y}@(pKip%-gQ>ae&OLeqQ7d zarPPpp|M%3$feZnR6tA;!BN*Hf}(L# z2c;aMwN#lXvevU-dv?#R?tC)do{Q-=%!;tBJEI2cx9r_U!dIs9-U0_?vfgdsj9dLq z48Rd5$zfuFG7;PZz{G9#d64B6h#6y~$v=~ls;Zj2XrlRtOU!XumJ_xpUPiC{e;m)_ zoPv*lm}`-1?PCx+!N}f6WRy}SN=p*nI$JlcF~R%bydgGK!!`ya+qkASz3~`?Y#4`) z@qkrhBY;HO)}9_2XI)Seu15+X1OZNroF=C;*_Zmf55Ls`!4?=ExjPj{jqtA40BLu6 zY+YzZ$wi7;ibbY!KxN}}yOXOFUC?>9 z6h@V67F$#8P9_A|ss?X1O|vjN2OgN2r0_A0O${W71O%jMIUa*xF+H2_-gCuMj~qDi z^nshMxdDks_Sl4Cc z#vaXT=~wJ{excLdG0Qp~R7U2ltlMS);~j%1;BJ}-^q>hf=@_Gp7FFeKYf69i;pgfz z_Qcr-HqV@V_3OSFb9KedFZItpeCKB#eD}A#J6_m$=!=i++p)`^IdIQs?mqGC;V*pt z+^(1JTYTMX#%Jx_AN$6VmN=Bqm5n%U&anQTWZn@v zshCP|TT9N*PtSLY9UXb?e0J4*Yp#`{(mg{PeYeuL7e;Qm;SmxgNk_{$i9_SZ9BMTE z(Xbk0Q}Rq}>fD*Lrp9KqJbL<}7rpVdE2~Ri`upGg$)Eq_?|l8@{LF2qpLp!ZQ-A)8 z|MNdT{m==S&76Ab%j;{${`LEQ^S}Q3uYTp8r;=iB`JOMz_2#$U_nvlEEuGt3tNi8| z&TWRnPaO+>S!?MNv+>xMzu~nnJ9O&6Pyf5Co_Kr}tWO}7J9cl5R(D_dvIAfFte&5` z?sfn1l`p-1*c($uOI4Uk=(^cO(?n~H^UxbK3KZ0pi57#!y0Yx*@rW7|oh_0ad>9S} zgTbb$%ciLXvPu;DAcZK$eW|GSjno1nS?j^|$EM7YR_gq7&ukt%TdveFotn9JYc|Dg z3)29crfG~(lGv)~w+868f@O-GAV z=9|U;O9r!*O`gXJO&(|^ZGc1wEwu@?4=5QjopE%wG2Z(affzhbyxFiZHf9MJCYdy~ zWg7>GF;OrO5rt?%L<#{&A~FIZ;v}bRA=sCX9d2EXfQ-^nU~DJZR4W@T_li8XwT%ouhQ`#)tc*3D4L8ndYik(;At*uYtPGM=(wWYN zYeQmc>vU8Ohg$4d+&)~|2*i-5L-fKqC-ga6kAsP2W3A07AJ@&OsnaZL8mpDqz1S*b z(QE8dZ)tY6vyg5-wY&-`ZfosZIz5si%dyezbZOXM=+4*8pv4R^kLcIp2+FLB@JgKwIIpb=3kN`XUg7L+qKQ~F#8 z{UjWJ{LnXl|9^_h$Ci$t+9CB{9DMTpX1Z&ZzHrwk4<9=--WY#nR}h_ez8Ajsl}GP= z?AIUr%fCEy=#cEp?z?I3nXj&>PBEXTg$!o9q>|JIZS2K`Q;6iO(&+ z`>wm~vA_MxJ-6SM&0Y1~Z+n%TomyQvxH!N4_FLZc^0(gw?askxzx1ltzCo1rr~mR} zYYEI2;(TZJq5D4l?zdd??Z5Dm+jf4(Gb>GNcXsr|%CDYWX}3Q5DUGC4D(|g|WFZM@> z=$(fcwGz%pAaKml#kw}8w!!%5F7{)isw}IfKO9uXHfzIy^Cy;<%gS#WXHBq;FQc>0 zJ0Hh&6G>o14U~`^Rb}v=FB8APWxBwNq}QdKxJ4&#U6id_E`r*-y}eeBqSTZ1=N*Bk zFDwr>dUey3-kRv6^|7(exuI)@wWFwf>53$;jkQ1=Az?|1Hk#2!Gp?gIh^4pFDtX)0 zFTAzg&b#uKJ@Yp#OkbB5?OnyTsrIzuBGEJLR=3kqk{H75>`aWYXt!>hnZ)X4C%<+!+f~Sx65xW2J_~i4MrM8Tej zSg0Zrn-UQjD2f0OW8*tg?CR=nVi#IrXPd5QtL?cW3`Sua8VSL=GK#uk8-wVt4fgH5 zy1408hYsHVs<(X)gyQ}OPN&J0oHWMg=bn2!o8mmZ`NQx39|xXU-Z*mpmYZ&D&o6xS zgMamVfAu%VFYpF#H=FAb?5X8-*-QWDpa1Gk8Ft3zJKuTjH@`H4ZYF%6N(2f8^PU@yiPL1 zR)sQ2C6iZ9ED$aue^Dx>q?9S<&bf)`YqH^VJg$s!c*#;>OA_0*8*g4>3jd);lnffD*7VGR7E#lZvu_Z!O)?0RTbSuxgT&Z0%aW zAxlbMiYPs|qZVgY6+(>B0!YCzAjKH8lw0{?3*Vlc`PPW{)@Z{-0mu|?Zqt!7S|pXB z$c6@Mo3`;%sQ^qw&c_gKl4J_eIVanBwhDCRxEVZo>;5ZdByR8I^KBg)%(JekY8@%nN!Af8q;88Sa7^-%jg>ogUQyTM zX9uIXuDW`D>QwE)Fe&M6Lmpa5_d>nVQknL?xMDk^xa-L?sxGg-Zrdo#x3Xj7paj=N z%p#3ZPBcZ!FyMMBuT2QEJRj9 zF@N@x5B|t6y~|hSm+yV}Cx7ajKKGTQKlh)$PfLryu;TAOF^Ozv=kb9zHa;W1k`*A%eH91na8KraP*! zjxYe%Kq$Y+&PLgo+9{(AW#@UsVx_U)0ky#3p& zgQJh!dC$x>8RdhWbGJS4>AOyp=bt@!*Bf8_eV_i=XWo3rHxymHbMMQ?Wp#}xe&k1Q zeBH}lc-y<*gT1p~dFarGKJnFe{ksqS&7c4H;+4CzV&C|}>9w=HZ~wPH`I+DQ-QjxE zoto|US9i|tSUGU=+~!li{DD6ipZczQ9$PCV@1%U)7^PkGDIq!ZQPjdl5ULrDqV-WF zC5}*;-k?e#aWcd8S_n{*n&7~Dfx$thTxGprGAN>KoR7j;P?QgVp{7<)3+)am00O5d z1YdLBYc?j9(8L5l9C6FRnh1OVDTVjmxk+JivHO`Ar);g?+R7%Ax@OBJj+y187@3G( zCIwGjms3+yo12^Q(ni(il|cWmg5`O2%H%-|VefCq7eg2B(Xze zY%_LN%ftmQkeb0@ZpV(&)WQ44jH6W5$QMG_GM3Rw=rqO@EzcC!*Ee;dq?FEmeX8>7 zt<68pbkFz3@dt4ulgjjLP1d?|9W_H3ppt%Yu1ub`_Xi(_|9tc%(}& zi5o3glBi9>$(n1Blw+CHGEM6Ld(U(K<@bK-(XT!#{Mv2r`n9ioO*4@WzD?8&E(JpARQ&FU4e*#Cv!{(~=m=05IrKmA;N@O^jwhd1v(eeh%= zOsnYj>LyF`5X|`2cLQ1XMgS2$LE&UzxXuhjzaKALMYYik8~!ybj~8W zq2T{tXa5-|*;Snj<7>wgD_4ifL8FW^NfJ>60|tXJ7>tc=u5Fy|b-2dHTwmK<2aLhj z5gTkU#$Zg&0)!9}5-4YBq>*Mcd3rimI&tU!huu>&1AhPS@2wC0tLmz*K6UEswby!{ zXFV&jm?0)-TFl_EP->WEyHkrpwmvEVxRe$Vu%M7b7!wbwDy1DbYps$dinvlnDdXpS z!5M9fM-@CK>7j-xb7a8*09DFR$_XKPo-@XTsFEaEUtgcvVo#Ui59TR9G;P`ZO6MG= zK()?`{Lt9!<>=(fL^I5EfK7%lj-3$|S_Nh-aVC&LY8rwCp`Ed{k``&~XjGKKk!Dp6 zYm5rQ8??U@>OiJ|+snT8e4x+M_WPG#3Tye@_ujuSUcTwOV=STa2@7&Iv5q7zII$B!rIWFicaVKqR31&9s>`W5yC1 zatsqw4JX3tsGxX|<=IFT4k(yH$U69>L^?JKU`+Sr-1)d#-x$QSQFbnxzb-}drfeelrY<#TY|>)&LK zt$yfZA1y@i`2B19?|AsC7ro$)>;E&_yoI8c|9|Nd*g2JD@0 zc-GZ}_x{6=zUd_ik8L4FE2FEf{gDU1`^}f^+%_B>OFG@{d$t|8>BeFrp7Z>@U%%@} zbkW?}Xh0iXfF#sXE-i(v*b%OUiqc3FN*Oz|b$*nMkX0;zsIq`eNMNK2>P{J@pmS|C66c><6O(fEqg5lU$$C25U#`|{mZ+-H3R>>&sX<1>gm?CGu7=>|Y zj6sNCgss(t5a*D!5QecIh-|G>${>UhB2)Xf4Q&g-&VaENBMbl=?aRIcNNefA2q~o> z-Ru*a4Ta2qty}qjxM4>%(g0}x=~A_I7>2TtW1$1hN`#cbRvV3KjuE4{b`VwCS*-yH zt7~BqFo&H$U~NWlN*&|8b`j?!0gI0eYNbpV#?F~(w*s6QtN4TI)%2k~Gvq-kkf<(ksR#?CoJMC=1 z7^7Nx!`c92oM#FF6h(%>A#fN0PXb14$U+QM7uq3fz!|4GA}pd@p)PLJT11RSh6`Py zBo(!G6kCVKMIDB*hVVosCMzcs8aPV{VF4qBH4_KMm|gAg)JlEi$)o@6XI}WaH~+-3 zJHBq}iiMa0)x^*Sr<`F$v{WbvEVZ#SAyJyxx?~V{UU=ns1NXrF51pFZ)@ie(hRNi} zus64}f6he@-15=uzjp7Qi+9|!uy)lYyPmxLribpoxBlQ4-u143_~2I``lA-> zzw*T|-nD!70}p-PV7@Sv=WN}UQgQUYW2nQNpa2MkD8?l12M!0iQ7Nk}LfC~Y;HTGD z2eoxF7nm#;)@Fr*vcMtObW{zgCXBIwvM^vqN#snWwQ?v)plKSF5h73=!(^N%aevp- zI;W49jPsM@rR%PJ`5<4v;Tw0K|AOC?#rTn@4nO$7p&x(Cb=fU!K*U1Yd{^yG4YMwg&WuVSryy8~R)yjf_$A z^Yd$KYt}l9zt;ghwHNsR5LT4cF$+p*A77fZ8Vz+SLkJbrFnuUQ!qD-0(num)*G5P~P+`lM(g?II$b=hHi-6FK32RdWqd3C%Jy_xH;%p~6 zaO+1o;C|ApnXa|5lsTtMtvCX}$bq8RF$>Bng8-6{J17Pcm9KfZUY~sagSWr+f4=7W8&0D7!A`sJ(NBH)zx~Q@_qO+*b;+JDe)#i&o?LX^ z1)uxmjR#LvX}{5H@#X^k&{yt%#S8b+en;wYu5}QHV8%lcb;mR_g{f-ngtJJBJPt{w z@)8A1gR>B^XhNJc&7zdJ9<~~Ua{yxow)H!>tFr+r_Z@y$E&aSk;fi?pv3s#cuf13{>PvE z!j3C1f6HJ0-D}Uk_P_td@67VxmapH~oo%;xvzuO??41AHXWsk5pL|0Ts8@l?pM2n3V`(E#h&Rm4@cS0?~jdJ%NG{Tv=j@u`{7o zqNqfvV_TGraV53njOR{<)L0#YOk+-IdStbD;PHjYa0mepkrTSM6cfM{GXn@Lj^o&! zX~cETMNw4O(wMTYOQnc&rrmC~S`DR?);iB~t+nsz#~5QuHt=2R0AM2}3Byo`s@LmD zDT|^&XrrQ;woCv2t$=>;wgD8gK+2l2U}-%+*-ZLjTogsxYK}~R1kTH0b zD}jKAlxhP)i$)YWr6{FkDLJP~j$I8S9Xh#m?Nyf@85HZItlOWJqH@j=!Vq%PM$BpQ zvKe04eAYH&%+}gC3Y0<0Kv{{>3Y_5r+q|qKV5t;wgdnimYUd0>(r8AI5<0J94R}(B z$!e|@o*+J~%z9oCLMaW1mSH12Dg*(H0w#o*HVc4p+6n|<8D)ehA)|m$#Hd43)r?Ta zK^kxjW92v{Afd7nYp0YO)FLJX8wU}G+LXE$&HxaT=>WU1(^alwy5=D{eqwm>*7P&~ z@RnoCO9vi(H0d^g5``Tp1m?)pnmB4Gvd$q)ox~Iw$&fQ4IB7w0;_SFOaql-@^{n%@ zbvplg!$Sz{z4z_^<#+t{7e4-xJ(u-f{^mFS!~5RA0FE77YR$IKefBdSz2$lW(QWq~ zQiVLVy1aeM{HbdF>5chQs|#roSz%7EC?a%Iz~Y+5hy@m*1dqpAA*3}794)o64oh7o zX}CI&-8j&YjMpc*b>rOhxtZ_K(}g@yprWj6tt+Kz6c{NOMKNcb(`-UDh+g7~39FU; zi648>m;dt%6~edPcH;c4?Of@{9=!iezwnB?zJ6$!KlS#%{jkDj|9#6d_P*q&e*VyxzWKnxjFQIsq`c{~pZVqY{4yy1k$?Qlm%Z_gx{$qAyUvWN z%hLy*=wGvU;ER~J9_7L_cYEC`Djo@{WfE$DmkfzrZJif%9tgZLMh1V+N9Hi4wd6F z97{bHXc+{@*5tA(=3_RGq-7dWQsjA%z@SD-=vr7pk*Q1zLK?>REl(alzTkwi$e|>m z)>y3=kCX%BEXSZgs(Pc%r><*{v??VyXBemMjCWj76pRyN4G-8{GUq`SAyi%0LI~%a zwg!VYHZg{Cu9QfUgmXR^42*H!zGMn&P8*OmYN>GLv7cuUP(n?fCX*C+w1x0bEzk?zDvX?#+ z#l2g;do1ZB)(BtS8Dq4z2oc7JwrVQ-+Z^Tqz&Bz}mAKy{h78UNX@RT`2W6J$Rhj3d zRHjr~8W74>wXTH{)|SRp#t36G35S5#AN)2%Z4Q#D7 z$XX=`0)oKTYlvXL2odItx7u_}8*ZJ`3IGg2p^j}$7z7E9lyVW}jUdDr(wI=JQ9u+2 zr*uRZ#SAmfLmfwiG6n=ElyXi+6Sb?$KlM*Pf9TZeWc6g)n+3~=04qb>?gc?WSs3ty z2MM6SLQqIh5E^5Q(gCI+4YkH7YzA@b(1FL#-9}&a(n}l*uDN`d5#o`@?|;*;{EziV z?_OA3l{EP5hi`nwbDsyJ@xg;9_8&Z9n>`u!ue4<* zJG*aYQPxkangfB5We_Z&KWa1bH8Z+r0I z{Rf}(!e?O@-uK8uzxaE@{pq_& z8|3P#yKlK(&t$u|o%fBed_@JrwY8(&xn@v6RENW2?eJ(azjxmgpZSWIN6&cP<(FJ> z#l_dX;>eQ=JPf1m%*v^gS6=txp)41VE$ux20&w)m$@SYFej?4vTCwAD%py%BI<-Dw zJX~Lslm~_8JZ?k*3Bp(_m4*mAn>mG2(T|V}BPhWpXujPsBBUX8j7h72lOT)%Gi@B$ z$jHb6%dx1q8;(mWAPQI%TC4?9%22AcWey<%l_&`yj4|ijov}nvTKMH(*U~xUoC|`0 zF@}JE!%C^LD5lnt{eFLLZfd68F+P*K-Dl46Xp1putTR$v51!f2tiwNP4AJ9=VRtrYfHCQps!iLqD_nByST z%vvasX@q<>ZkSpU3z&ombFHnl5HOC6BMyPl*gEWN6i^;fAl8853{&e0UeW*nYhf!# zL0DO1tXLZ?Uvl9kPu%hKJ8rrAH{bcX8J9ovg5KdH3lAJ!I&Vk3 zIF^7LD0Bk>!*$l_G_#O&=4a-1%>Z%zVrx|AL zCdP%)gEf(1+mU#6Wpa8nKCmi}F1eM7Ta#wJG}h2K;KI26Zm=eOgxOl^d#v)FG%`}!fV?wi;e%@@k0sul3rwXRHt!hJ5IFy!X>m;c( zs)Uo82+i^u6`GC(j1{UXtC2jF#m6W7)W9s1Rv{cAYb=z=)WpP`c5Tq^x9>SRu>e|2 zF9<>iNv!}QhX63LzGn&%DGXRdG1JNcqDUeP3`?YxVG03Y0;qOIfYwM;?35BfFjl}M zI6|!{QH?Yu#$?77Pb^hGcHQ=E9d`QUkt9j9);x|K)>*BK3KyeQq=hY{%`+y!R6#E5 zLUAdOGEzzF1j_SLSXn|{Dm5vzZG;O;gO~1r3!Zi5vBk%pap^M_7w`Gpf8P14zxWKW zc*zpCW6QU1x%1-7_P*@ZZ#sP9(6JM%r%#OLW`c1YOPNH5)%f!^9r zlQuU%ue|hfUT8fry2B6np+4!Q>~?ujFXU<%eOav^wKTQxOo1Bdy}W_ zX>MtExeJU&W#uSD2y{-Yt6g(s{_@?=3SsV(fA!w?{_{`wY3s5JcFUR_ICSLIKXD}m zanr5$JnOp4Z~M}B|KVS5>}?6w4;=sPzyDCSIy(RAt54m2+jsB0{=fg@AHU@%eiCb^ zVeyqe`z{StKEb8psf74Tf?#fS{{QSp0t0@Uu!I7f}u6o%Et5N;e|K~41 z_>R9FjGy@UEuU#^n>+Z#lbvo?_h;AFPK-<8GI!v>gD-jQYt8A=*7MHWckLBx!#b<& zks++BG7|-}!a@)?q*3d{Vn>}x*y)j4TvwApkt@15LYXU%th-U}q^$Qe7zQD9Rx8@z zVU2<#gSDd>Or*($Vl)tztd8@ds1ORYL7d3iAW3lO03&BO6P0F!8Yewx3}BT~0gou* z;5K{@JZ}SFIj0B_U(I-sQX3;>ZLPLe4~OeQW?42KkB8X%_cO+%&{9mvl3kC?enzN;rj` zd-e%S4?J-gfes^DRjL@3%G6TIx~|KzEY95WJkRrd`p~ysdJwy+sy0S|8Vo+%zq}8spYEWiQC=OU#XB-eF zLFkFn){tH|U6D?$ECKqSS@#bnP}4q9U!74(jn>*(hmpewF=8-ia0Y`x4!iF;k6LTd z8T<>Zt+1X?=sAr>r`6uV^lP4b@rjd1uvK-HStSF4j22o+qokH%JRaARwK`YID(2Mk z>GfPx91~@nL)a0D7|*J@s0E@7FcCVbbLh3w;iC_R%@$9BFMZ&vdoP*!-+%R+fMMb2 zSRYszSH)|7`t>Isct|olw{=?`G!H(p5aCc6EbNNHrWT<@yDoj0Ez|1ax&GlN?*7TQyrtK?^b_y@*kg|` z_2*|g;dx*G(x)Cjb>tU*{|`qGedn8Z9=P)h_dfFQJ^P-%>zwU-a7kxpXOn2_hyLWx zpZVg8GnJit^|RCNY*q9RKC<6B9Tf8P;yCHFPdxI-)(fthJ@2XqZu;)6H$Oas(wQwu zHK|dM93Hrl7%mN|gC$AGi!Z7qtc?rMILA+tU zwN}cyuItHUVvPT_nyOclB*7Rn#ioV8^088k+nvCuwy3^T^q3ir(?mtS>p zo04)gDcW(GtBkoQq+wZ=3Rw++5tUM52NV;F3By=eX`G7@K|uWAGzBbTEFxu0(7EUD z`{KhZ6@u8|3VokKZ+d<(#^4Mkm>>j%SZg_>wUrcMMzFP(0tRCd0*nzs7{G3LZuvoI zQ-LFiKvfBIoH|#cOLw(HJ1E8o9Cq3{YsPh95w_NX!*yMQ)HsNY#${PUM63;Mh3ZO) zS|h}=Nv@1`2x_gd(S(NUb(tExyeuDo=#iiOKfiv@uB;#zXvQinVYYE zU`Q7yjJa|LJFcl?M5Xp8wo``shQweOo^8zAubQ+;5f_zvyLBxX*py{U7?ocVx5K ziW=)jkM2L3J^Qk2fbaS8$3A%3^V<(U@~ums|Fjpr=ov>J9pm0$bxD2qn}63mckWeh z{iC}+{k~prhu(kW5C7~#Z+yd+OD=uR=ajqaFI5DXZ;O0a~DJQDNVNlm}XAOlU-P>VO8+0j(<| zsB4G8AQPG(#yG>oRahI{>2>ZuF;N>tMFHh*Po*Ft>zbbhZ_^*VlLr4#pHAB!qCz z_|^oiE!x!40MqHwPMkQME$!>Z9EjxWxeIBl){{~ZW0A8+TZggL-izEC$^xYg0A>Mo zh>n%UN(VqBm4*U@y{&CwyZtZU{&k>G8-)OG4rJ4FWwmht4bJ@hrwvR80dbZ9b=F}A z)}umz5g@xsYPb=koI?mHgJa4YaZ*~FRpQz`{kkejNiDX3vDMNz1Q<)9t%V>6fJkd? zSq?eH(n$@~!tu$@?c1!yS}BJRP+k;8S!+OrwUwiE;w+BBQwst~_1FWqKJV49S8DLU z-S+~RBx(NVe}40w@BCG5vM+t^j+pZOk3D$F1sC;GbnoNG3IsEt5u#xdFDU;oDG;=Vusz`wljH~#g~D}UtF$<q6N^ zTVfr!1JjcPiv*^h@D5K zZo)bDa|F}0+39pJhPtks&8F5m&&xPYqbL9XDdliD48w3d9=F?V=gelW>($j&Yi(6k z2%#uyIOixKB+oLXOc;hzPA&M)c4j&&O#EP!*?b>uvcE@XEMffWjpNY&?HCHGNheNo zElpVyR!R~^2x%+BfNSguQE80`rrhD$x?`u-qDJbRQPLtx!Y~B2K~7DfA0N3qX%z%a zYpu19WfTB8=c=mm*@}O$|D^t}qL5{(eE879D=yhpWUCp~b*-qQg{TQ(&N*<%8N1OL zO>kK&LQzbq6tf@trLi14WiSP$6sBBh z=@1r`1dBvfubo=tttO9YD~PROBxzTfmX$OPoJHDLYjGGRN`ujg8ak+pMkl`i&O2}a zM4KedXJ32W1Gj$dv3rl4cTs2MM0s?9?%i|2$)%&mpL*i2KmC{MzxdZr9XN5}&Pz_N zEU=IQu@YINT%qiqZflZ_QkF!xG0W;=JXtt>5>ot%z5PegD5)@}qk|bl(5r zn||`?mmfKF@MW)l#rW_OpZ?dc9awnanrB~k${%! zmS1}2%YN?QTYk43S4Zw&+j@oi^S}7pSD*jmAN!|wz2ooxVCC@m+c#hT<8OK1+LOz- z+;GF%`r)7asaITa&NH5P@X%lX@!#G4@UiW&ebw*${M_DGyzO28_VPE_`|i8>{A->! zK7FFy-MMh$@cfp&TQ1tS^qp@r-V<3J*K~1FcKVaB(K@!0jf+f_x*F!%NJkI}SY1h? z0|f>%y{K5o6pM>xNXE4eMI|Xh;EaWsk~L?WVO$#9>(5!otSi@slYXo5>}OxKx;8;L zbc}KA;yCt~jdN~19y7**AQ%irb8~Zxiwi*zj7B4jah~UMb8|veS(XJs;As8_fK)o=jZ3O*48NiM6CfSCFlIiY$5IcCEBzrZZ2V_OX94u2yJq;gzv|9 z-5J*@@IZjmgrZtd!ze-`0*z`Kp8k(g36ptg3w|WAkqlqQp@thm{9cJUU}{3pU7^z z{dS2s*bE3D`@^e9Oi8QNswWvmSSbT|W1u@l*ei>`It+*)gdB1h`B_NlOlP)p4!{Bs zOZ7Ru4#$oMG@aO#8XU$(mB=AyY#0RAA)rVb7cf>#GD^*^y>l9PDaU1wUhZck}HdYt&x7(rF-xg zEw8Vg7#OTeL0K)U8QL9byF96e<#wf=rVe1*U{`0#|=H}kJ?>}(cqrdd~zj*M;$F8{cW&3Zw{*#~j+|T{-PhENQ{f?#E z_Fg@F;D+T1S}98*Cl^2cish#kA9?8h#!T~)XFk`i48Qz^Pw}lnKTKvCc72XLc^ZxI z=+trzR$@Y|HOK-sp#*W+#XFv{eb-{Ka?aM7TOV0?>eP@?j4__&w9%Leqr^I`wIYOW z+rHCkH_6A0vB6-VH9d4{{fXmiL6UNU8I{e1Sq-DnD2k$@D3nsoX0s@Y_4W0wTeqJ+ zeR^$ewb$#lS}g$F>2!=Sl=37=q9|gFO(qjUNSdZvTOmXkhF(F}YPHT_lqd`vgb+F& zXVwBj)(c?#Htv1lesFjFp#jC;yfwpg3!SFu)_#w8CInfl9RdIgHrm>V!%(1vCha~T zZ7WTUDHPELa7b5{#|BUs=^zM>uGCV=(-!I2d;#fShHArK)U=M-V9w6Ut) zOqCYaIOb3shL(WQ@+DW#z2NGr{^iEwpa0gaz&Te)VIT&~_n)(A9JB_kwbm$$ypb?q z3zk5XcB6d*H@B88u3V3<(xBCTZg0& zN!$o|ph{^mQKgPCSCli18etGdEdjMsI*bDvM&qIq+7Ql!QkJoR;PEJnn&)-q&T01# zJ^AQkh0;I!JHPg^_kX+}*_Z`4eC=*2)pK6-!nG5JlNhW_ZoF+j0F}ZJLNTVEH$JkQ z?dYVDu8M(b?Tog>?!^~gc67CR>c}x|uyC$0F;|+{&OOVEqe9Xs4T1nGt%aqGpukPI zNl`asf`INcqb*z;OJ$8F!*$2fcF&%hw!V1(sp4(#`oA~)Y5DlEFWm6;UHdM)|-+k2;^UGuS#upxY`>$TN?fmE8`p+NwkFV}O zw6qok=4taOTF1A2_x{?j;;CczeC3AQZo1?5-~Lmd{ku>9#^3zdm6tsK=8t?${!I43 zo%daG?KRiF_}6a!z)N2H`+ry*T7BEEynQ&4+js8Zv^~@6>_2teiKE~BiP!$lmW$?& z{^uuO_Zx4lC-%ob^oP&5dV9Ay%#Y_oLV9r!%+25uk~9k9pvr^;IWxcQz{;?Y@$t1X zlYD-r-3pr$v8r5QK}ZM#hfpmIvL~K8qA6t#0R;riDB0??5u?u7X0showz{;eOzkE0 zN~!tzc>svxI0yoSP`llno$cp&9)_WFXfPNw8VvxTl#fQEIF3D^P}h<%7KWiQh7b$@ zRaG&@gisjcwY3#O2=zus)2zh%;MaBiUm3yuKVY|KkC`abQ6j&>TYRlylz9%K3fX z7d04rft>d7?o2YZ5m3;k>Gm58y2*ofQ&|#7_x{h`@SXja{P?ryE}oyTB#Z*MC<<#)dde>_u{D{(+*FDLMmTMxa0n=;WnM_-vs5RwDnwPe zG{g)bo13xkd-FT4c+1Q0zwz_ez2^09xN!97(XF#HwJ1LHtkVh3D*>k?QdyO9z*-N(YPCc#u;X6sl+* z7_23U-O*_eg|@1SB5n+dIgNQF9a}M38wlf8TGvoXfK(LJ?X-hHjZT{=t+ww;zW&JK zi$C_nHCNAB6yEpXk+_qbx4ZlB{ySgu=0D62KKYfeemj|;T|QRCX=EUJ{J@dtUfx=H za`5P_e{y)~MB0nTxnrE4d(rOqyz}D+tEC^m?!3c~j~~6~umAL;?_GRial)FW^P&_$aKRkt=&S>c~+0JwAY@fTqF$3@wS8n%|Qn5<-z z5YseG0YE9$>-B^XURKLkkfy0p!a3)47*eW6qoI^)G#Ww(jH%YT(P(IGIfv=4j1VOR zB}u{<3qxnE4T1;&=;mwGU-90tXem+lVS}zGd$>GbGnvs64@asNo5m5tH@PWam*s?q;^!5JWXmLFhT-r zM644AoN)vJ;Hl6CA%ro7Gr+BnDVtI!A8|Iun0#Mx2Vf+rOWkWX;tKCSaQMXW1Hb(0 ztGD!)OJk{_3L`8;z$i9YfhsI0tQt5@0tdLBthpCobNQh)`H!FfkBzrQ00y)Z0D!Rb z;LheVx$!bgjB*sUHCI~z05r`>L_t(hO~9!d83)eQ7A#l*Al5QKU@XGeZJ3O51VSoA z2nmrZ2(q%4j_vAWgis%~Mn%>@MQr9kXpAd{dXubXj9Fo&R?Yx+m>`S*YS7x*hz3Jl zAg%k%>PqFRb|#_`tF^?AGAe~}aTEo@DX2JWEG!&&-T!)1v)4@>e$Lfb-h1!Y))%B8 z7>E4$+QJLY-`QvsU;NI!{hd31?{{AIwmTyh4EUstgwfqa6_f8cnonC4mE)M6s?U4j3Y+XRL6BI8+ywP*Jcd z*CY&*Le<^Goxh7exJG9ME4Ot&{4duZe6aVX|M9}le&hzD=l|_rzW&>P@FwcS4PX4s z{g3T`#Velo_P_jQI}RUt>QtlQcJ4UmFF)|S=rE z)c#L=`#tac*?+&`_9y@T?_by7alv1|?fvIo+$XABIdJ$#e)dNnyX&4`dgr@&T>SD| z{&WSl{{4HtG%ndi#na#Yfk$pQByaubsYieB9d+fRI2C_?cUk3O7*M#AG1l#Nk&@P0 zZDg%w3$v)xu?V%p?r1bpST%XX0Bl4o#vR^d-Hvmvste9SYi$^E=Zv-77y>--W+;S^ zFyz+hG-`07$HW;PG62U}B&9qv`42FLG)<+HN(p0pk8xlOQc8s(M~Hg%hcPB) zt+hAtL*7j#2m-AQ=aF+R&r3o`6h%tq&bjX!{j*t?VmCQ6PM3jCXQu5o{xeK}p7mCK z6)7tLb4;<-b;JlF0FIJeT)2AMXcN*Pr( z<5*cRiZQSjIe|E17D=bR=L9ibu~YYl=_lGu=x%=fR2oMJ)>3u5{qcBRS{_$-;aE>w7N;x25fWTk_Hb6*IHWTo3gU&*otB9JyHj}28 zipfnEV=9tss^f-U@Gq$X)ES+406=R24fepdhRtO3q5r_V4 zdr%a;h$n;+P#i>*Fo_JNE;3-lMu41A-m97B#15-GbkV?wxxQ{&Uf|W8v%QliPp4a& zFMr)HoVb5wX(|7=uOE8v1d*kstkNl6{cqP|YeDUVrXm2^^i9;3o*lqVcS)wTV{r~ad z@7y`0vt;Edxc#?2c;K;<1lwlE{?T84xvH?mHzC5ec3ThJcYnLvYPSf&l1I##bdrtx z&Fvc&(FTGbD9bY7y4~vf3&vPAH#b+;by?QnSg+Sn(nD!xT2=UFKPhX*Sdt`lT_c3T zFw|PdajcYDTwJUw(Q36878bhQHX(!uY%-YuKp2Kn$~@20G%m|B2!hFE5{4nhScobJ zHj)>NajVtRT64}BBSlekI-RmCz0Sp2>(#TqCaS7RYmG4_=XukVe49&%f;a+Jux$ESRuKx$9Ib6-E<;Kp_D{ zL=9>xC$wb|j+z)wilEt&m;rWJSc(H!SS~E)|ia5(KGfrA8 zIOB9Vg_+KZ(P!Vm&VjWK8Kc55^plH})tqpp1P%hB6k`k{WVCh|BjgwgrJ4jZsSHGj zR$9$9SpgPX$Bfhw7o~{8ggH$~VwGmVjIo1J2AC7T>$=j`)kMiUNSTrdjcZfnqu$I6 zLvCd((;!(GOmb1rb(_Oc<|uPY5lv*`vdW%&7jvdtU<*)rkdH9J!X1CvSJ8Rt z1f#XV8(;UlOLkrKrGNbFI_vCTAzRNs_qzw`g6;gf53l|DyMNX8&&_%CzyI+bVISWRfKPe!r?JquqEs?sQttxuvD0Uawb{ zB|@Yu%Wk)=lrqLeQGziH27~c<%mdo%&J{()7z+Z{?{|YBpp=eABR^PO2!Rml_q$qa z#+X!f7>1nlBxw{y;rj$JhRI~Ym|={K$HOE^!Z55Vk*3YEEbF?~S~C`Sg4KtrEXx=R z05IJg0ydZZQ>Dq~yxqTS>Nn@3&lIFME1*obW+^Hl)@o?FCQW*W4nO(&OXufy?mK++ zNQyxh2Gm+%*2Q?us0uW4x(paFq&cT?oC;@lb>l~zGIhZTt11T;FeAoF#xYnn?cecH zeA*j1jf}ny@-n$;QuYHkh~mHj&X;ol5;7u{R4x=0W;kg_jd7`^)LR?PqAJ0enqpj( zEDE$pW@gFV$CipR4`?J!`9pJ)9}Kt~cdSDMGfc3v$U1NiHlSQZ2{C%ZXVhrzC*V?w zjaC>FWowKmAWlrZwbLvyID$E{;HVU)tkk$xfVkBTm2!}0Gz_pbLX^rHWF0_Q*}O7t zGPII}oi5Fmj+O#6?Vc_N+J*s-Cdw89bL%iw4xzYe^gHYO?|AGZAN*9vXlUf;|M?@Y z`04BR?d|;Q{v)N8S3hg_Q^)H^4y{p!!Zd#JktaX>AHVvt*Z;FbKuM%sRyyuLFAdb8 z)yZovnR$GvJe9i>)nu*Ggc2boU>rsXE{li-duQ5tnK8zkRtV{cVn-+dDy*&z^g>o6 zM;zmhDGZ8`i1Y0cKna$tv6@($2W4k6CZ))j&g#?Ewta1$%-(hLo#(&k#gju@zx0_; z-*t2iCCTkOvWt|c6RSXz!}w9COFmYe(&M^hY4ZZw)dBpmkC8#M&mGEII$dtsg*g8 z4PzX@IEy?(QdL#6*^Hvd80ElJRaw{da5(h*%l7TtmzS3bA#-zcd7k(C{VeUk5`sAAX_{&y*Vfj$-R@*EDvH9}5_n=`%a)E1!dEduh$Km}EQ{g@Bbw*=WHM38 z#BrSGc^rpc-jZcmk|c2)d*AZa)zv79{M8!-K~+`0O;IV8B#AKwAxD3(*nv%x-#2r{ z(#shZ6%M0p#35TqNL>hu zO=&`5Ma&RbgbXG@7y#l@x@)hv?92C@QUWuIFjPE=Wo-}!sR{xSY{E^ZEdra5VVXYJ zO+C$<v?(p zd@l<4U@##8r4(b!Bq4cW7XrCnuFheu8P4;X)lP$Dgc8JSCxz4@gGY`#&g@Ln9XML{ zTMfg4s;VTm2%&5uN+Xerxk6z`d7=io;sWL`Zv63Y{q_gm_EDrXuB$iy%qwsC+&3zO zU-8l_vX$Yd?l@k5UR`m)Y?9hX?|tZHuYcKZ{?Avv_iw*KsCAM$E{OnLP0sC}zpdTA z{hos-D#%Ncg44FF0okbGkZd*6D2f;(jYwO^!YDFebzQa722=&LrWH_P^)?d70M_+{ zI+e0kC2TXKrb!&#$LU~w>n^L4^oo_g|JOTli60xpMh2rL^{qd-kEmd2^xHwCyUjL_ zks9+Jsh6e7#A6|09N~Ri8lxH|VGs%zxEaY7hvSm4*|fnB@1Z=eSY##zR;tx%H`~A^ zCqb~jwz9CkJ~!VT4c4nl_2)y*60PNyt+RxX)z!6jyS@9I9a)xzAue)Z4XW$9(P&_d z%QEkDoA%5!&M1x$g27-&3E#VSujhcbY}wLmHpko^M!@Qcfo*7ex`ru>)g_34$PwV_(Sljk&5SLQyMfIp@aX zG3TN8nuIeQc3$1TdDe$9N-0avN-j2IE7MRieGNirilIzPkEtK`=G*sz3n5rr3`Q9` z85M7S*#&QY?ek7egdR+wURM|>3#4;V+mgCdSi!h-hzM;UM93OK&`iI*`;wi%^D{60 zyMO!Ax~9E;RAysKLX1LULQJvmhVsYbFC&Y3}AP#5xW-_h-*p_a8X*5h+Sn8THItE2tG#M^pObi%jzc=g|et?_*VL@eM zKrNyKAw&^2##jrOAd0Azaw@SQga|FdFa)C*Vh&lUMa=28Mp}s)69NF*7$vmjcxj?Y z5*e#=YgAPO4lC`DrP6{l4j~>yfP=UaE4^kpIpA?+H>Krt+m>KVJcF%q8PV&Y;}?eYge*7Ah^q2?mCnxCF;4RIj@~e z>SABN#c;a3SWp_4%1VMFf*?vNVj|#@H;yS);7CW^nNIhfqpP$%6JvAt!qSe}UUzQm z(IX3;c9@gym+v?nbC#`6XyTHHby;v^?WujU^Ee6^qM;#Gs0URMU}z_eq!ZbR9jXdY z#F_=k7$c&>wl~{QGS`}Qh;kKOha=Sg8$bK9IwPHtjBLswSs9mkM$Wk0g>cTuyo_%u zCC*4k#vwUm92sYyos2p%v(5^Il8z*mRr!5>?|u-jdGS#y8H+``=BJ za74UhfzqEKVKX@RtQ1%pp~P&OsXf>cN8|>#FvI>750qw38p!hYMRr^kO93h#477>u z)p~z>CHLPuwW<6K?=au`B9Nhd)Jw9%O&y69P3*^yToA-MSuRz-;!N~m)XP@~e+!=l zwVaK<{otaZB(~0TSbRYA9Z|M?H^YPVHNsr+^AyCll)EnBPWa}x>+TH!Ov?6s=8fo0 zy2V*thCl713q0+jc%mL(kfYPU1E(80He7~>zX3aG$v-!cAnA)Vk%zDt7B*SlrY{6A z?ESo+N^%3;xn!{h(W5S7EJEc6n;x2QvdIbyhbDT?93Rv7nw7Z z0isM5rRZi!E|h~E;qf9MV*zc_V1p0RrPAwK)hT3yI|&bY5+SOIkgyZ~INn1bP|ugh zR_AGnR<%zkXSd#Z9^37VVN6e0VoejHaOCNdQ`QcGE9~VeecVJkf%-@c3r?yKZGrIS z47y$DsB=4i{JHk{lLmjytK*NCIuEFLi5jxY?NRF+^H=?!HY-+DPb@@k8$^ftAMfnE zQRzEvP;xYX_T}c|8+Uc&JK1?eWc#miRYjjs`}C{%+?lL4FD(tU5e4~bnyGMoo?6a$ ztRSx#6g;vJ-_uz)T>s^TsqEIx#acRPrg z>5XwE2|Axd*t3@ZE-`-Wcm&P9@A&lI?QTjM%gsWAXoCxnU*6q|Pf`CwuJZN_h1Smr zTObEYw^|dbn8=bjj!gR29j)Q`;a;B%kE-4#Un_SYEnGP@)=r;#t1q^*(!OzA;HIj| zuwp^}1`t@p%g8BF{2GoiL)!OJA7&~%u|Q7wHIcYM&j+d+R4285p|50|>k=0Mkwowk zFfkB_LJIqq^~3*}-a4Ur(1F$XnfBj|8f5{q@!J~VB5&}4#9fxyFue$UX@U!nV^_Qk zH}8s$2;hARs=Kl3F2mIPRDN5KNSnz574v6$;w}j;{o@h+L8pj_}sERPE2a2d7GY z>XcTy`yV;l8_~}Wzph8*oW1`e?CinF=~w?k zx*`@zjHP2|@xPjQF3b#B%*6bxqmTbaKG9e!t)|#|rw=~#7&%imFlFiRz8Xp2wbtzeTSnt|UY%c4Fzi2LJGh@!r2N|urT z?ip^*o41t#;Gx){5cvJ2z&ayfs)AHmf1i3rzli}t4REuJW5Oiq_|C2__ZiBy_lKr&kc_gC9*?@# z)&Z9o$k%W&!*;dv=LO-#iaT9zVkfL&Z@iON5n=B#w$CbtxWj=kKn0O(j zH3jW>-m%x9cJ|=)UyE$cB!6|C)JcOe0Xjdgj)i(Xo#jw^IxUPJ@|xhVSxw7VbT+?E zWL@ukSVq$Zvp(^S7vKUZ$-%*vL;~Xaw;!Ja6Wolu{VX0$#x%?)@C&IZxB!Wc$>S1< zy)vO%r#Z}3)k7O9#UyU_M@C?nI1{kfsH9u%f5SmnTrQ{reg-H~(=~=UV4l24gqbve zo3E80{}nPaI{M@Lr?&9^UjEe&7OA{lJ0V89-))!uC~Io3{(TO)zy2;|kN@~z-^_LJ ze}N&#UnVbL&epvAPMJC5=3=R*Nr4w0Pt4qHY$UKdAeJv=tRMJkeGS(`JUaL7oK^Vd zzQCI5x&ildN!g>Et2h@a7TbIB-q=|tP`wBP z!`Nh$Rw{>fjwcN^R4YG(aLbCZKfD<8XeM5e_`<;)49g`dQnn&z6nKaNM7*;zuzMeB zPzRdh<$7!)sX-NJc_DSdE!N;&LrHXY)SpyXb91vuJRe+u{%(6O{so~LBxNN?AvF%@ zLv<1fkbi@ZieKxODykj+y$?-bbo-RBf$R}c9f3El12l5ye7i)*ifS?G`f^t#?Eyo# zoc0E4fm|W88oB;WbY+6_EK>h|2-4KL%PC|=S2T^0+LSQiSFayz7XeHrFe+tmbT~>To(DjWjsFQz}P&{A^2z?Wn8v zz3${qD;z{Sk43oU#H}S>UfT!31!vWxS)>WluH08-o0F^1VxvJ7qUS!{(J?Fh>0{Q)`&>rgl^I3hi&I5y;WY;XaF}SWYmqtOi2A*2GVTrS zZGm0KoWIe*l;>a{&4eHH(p^aRG5ynG%kjo&)zZ%?wX+ZCPyU`2YH3ux`!6fUwN>F6 zGkD{t{+_{4LQ0+jN(5%g7_j2Nq4{Uzt)BW-uqI%r##6d{@X6k>Z?qu!?N>gCPx633nu_a3ZtuDqMvY~I_EPXD}61b zhDySJm8e0Ns!6TiVg?}NI_ZaV4YOHY{>%Tn&v!fuN0~s>#Yi|?C9{F3Hop1q z%gdqBG?WbCU2ad6Z^#A|yS&;Ob<71SOt1|otPp(7MIEU~sRyBs51+XHq*oR9TXc#F z*8^g|23kK@P6uR4Yc?}P6?-Mg5(t8>EA#W1JcOkz$+6vm&cgVh${l5_pmWoHe_i}! zfh51L*rEd}ua|qt=58S^q5M^DueoCx8Do5cZS>)P-i82zeR=}?|fRlA{y(Xg?z4GG?dbu8fqioYwX54 zJ9l8$vf$=+mea&kG>oBP-rVHt{`auZ#qh_JZ*Xg$$J1@~*OQZRpDG)K;}861{hCaj zo93lWB*=o(rQ?203yWDaX}R|gB$(mC{r_Tj-V=bK!?l*#J{d%%^7pEH6Jd7bu*F~t zM8zo$x+&5a%;J5?=H%Gz5z8V9$^Y45*@9iU;|#4u{?;hK6qH4-$vE8- zcP2$X^8{VTN^qS_npg#c>OO|?CJ3fNDk}%ARTSp@f`d~(7zgCz&=URbBQY>-x}|gY z{9l8y6K13d#lRHv5-AV7#A&mSuk@uWeN=En*6!6Om#x5aEg{RmM@D}F{wdIN+Go}C z$-`G9k;1x(I^|&fv9|ry9j@m3C$T$d&K_P+=@cPNCzWM$eqM~Cm5M#d6FM3X0mJA? z{a$xpF5NcZ1ks=7RPF!Mc#^?x@_{S_m$O>Cx+<*pyX^#J)_TL>?T4%R4_aZVOjX`D zy6ar=e>_}Fe5>D<%?;+t1+l<3t0Rr;B1T269lQ+pt#gnf@=4WMLl{RJ4rWst4&hh9 z0ir7!d*nA_#FaqO>*{Qt@-$&{EK?c#C~^>O6)LQ4Jt~G;{4;x)f3@IS*`si}P?InJ z=evy-+Uv5D`jL}$QzL9=&tOfcp?&`1^VK4juhzF}JAQNm!lj>~FGDTod0Cw4s2Xl9 zne(c~9!w%1T0c0r<0f((W}T-M20f>;%mjZkq?B~Lcrh^WXN?o68m z>wZ_~rU;XJQdwu!d9wt&kVEC#F>If^?P+Ss&1K_t=Rq%=QD%A{E2^ELKibLRN4tAQ z5r5Jhhcrj%&PUDn_PQ*tap;(`EF`&r`;;>w6F%J%MFZoJJOx3(_h$$h0_L6iDN-D!O}bNHBvTNBi=Wx_pt({R z;a@%}%Ac4Yc~Uz3>y*-2sm@{|4E`m7@0sx7RE6Vckx%2?_8u)8@By;}_W+^+ZPO|! z_DNT~XKQ;q_W&I*wj}4~IwDS)l{JC;-ud}br{@nLpD(kNlE{-7VMaTuu^;e@IRRI+ zU2wjf;UH(_ki{zPG}AEdXQ|YeHWP7K@(DmEh0&QzCcv?)VWO(~plBd0nuh7(Om{HH zf1ob*sSDGdC1_G<$4|wBw377JJKIa=0`C+IoXtyg14|qB?@YLW9z%Xy*xlZ?tu9{? zES$rSmMA`zM;VHq*emPotDXWK#?$>z< z$XV)wg5JK_eG7OjGUCTe#pB~AYv|RJns+8{(gr(OHzi~+(&(8{|6aIFv1|5J2Bu9} zMTNrdq87fzrKy=wq9~IVde39CD|#9J{yD}OEWQ1J0|ZX@chTi>ug??9ANy$>TT=JXssWGgn1Ym5+rzR&_g*wtNRAq<_)n*UTlWT}wqLVc zTl?uj)?%w(4Xz9lX7_xCL))|sSK3rHFcw<2U0rnEipmh2a7tMldq)VR6;)R4p17zb zS63B#qR(~BN-41%bUF`()%^3J=4#JVhiaA+{<^ZB16d9vDJ-uEDv5i1KzIG`+)F=J z5CMW0)DVNfOQ+&`V#34-!@5JzTIRfv_rGU9?^&RMYyu==vQ&u@s3o?f(^W&EY4f(F zWi9W*=*6g)OJvfmh=?qQO%+*YIOO{{@T^-PwZu|@Mh__rUA1euC}!8bSOfcJgYOws zF0_#+$4h#$}r63vxh)D>@Jcl1uR2wY*J)=Nz8b}s2d|9XTX)^txA@^;hk$kuA%%I*n zFj7$mtQ9ZepU+l_{GnKa9~DatY3TI1Y0&STjY+%(s7pNDZ>*AcYg7Psm;`nh8n6BS zv%lMnVY~F%s!_yH`OM3cMw4|b#{m%buBI^Gr*5@mY4CLd%r8y{FNm$?J*-!0`TIU= z1L2%#6^L{8j+pSv1nJ*oGlqKxRfma4n}#Z;P)#fN9O&F^D71Yu!{^-BK|LRlRidfi_7{r-OZM9?d^UhbvzFkVTuHSj!2V!-$>k zFJfTWrCF_e&1;%Y_*c!0RJBhQisDL+y247Q>G%n$*#y@o3UZ#E%XwoIQ#%_@-Li8x zn|fSK5viNwnQ=WHLCkD))yVHH{p$EmMa4c6fkap&*x1kv(iWYR#8J6lXmHb{xaZUU zjEM6TGKuL)o*i6`VljUYwNRwl_2d0WzpXihzF*_l*&I(+9g3CuL7+fRQM3T=?{KV? zpZ5Iyo}=_GB21caIwR@?y|*{E_DQkgqhe#QwDXyM7+oh%*b~Z)#*3IE*VgAlC1nVM z4a2)giGEKgYm9;^2u*i+rggQzzzXcIowsD};p3BF|5{hU*XPHb-tp5W_aW_&SiAxu zsYD*&Kh-5{E_VE<4)vbXNtPK5E$}5k5Hd=*Y}{y@e=4NE-oYz6Iy(5iIYN{Z?|gy| zmiN&5p_FhuV5r4z;-Pl?T@Iv_)aza1_~b)(q8qV3%x6CAF@!sIVDJ1<;b^Xw?NCFw z_)@iaT=2c=RZYXf0aN6fW=VTZ%3QZ`r*YX_pp5A~FCLKJSBsN*v-Zc|d{2ay@8EU~ zjp74)WY|(;QAyYmuo+_EM%0Gx%xLk6GMJnTgJWFD_bH(pV%GYCcrg{oU&j<-#Bt#< zE+2{0H4}8j0D>5zgcxCdtvjuc);yx4r;ER2)cy`kj=1gUVv-k^w6I2B(KPR@xCRdS zYUe=|U2Q+}YiId^kX_4J{YZjReC%_lA=grShxDw^3Rb{j;LvDX*V;btA0cme7mwkO;S%RDcWG zq>(Hm2P|bYa{Z`z)TZ=igYmkaMi9YsHc*=@?xBTyQv>~}>QHmgLIOnZ{-4&I`xnLd zj!#_;`+=&$QL`#*DsLJLilrpbJ`@q8z`bclotMXUwQS?0l2$zM`1EnVy>I#Or19JD z{InAKJPWe6A{QEsB+EUBQHw8W3>Qi%Tg>9!<{VtEtuo+-;FjeR5z#j8$IhaXT)Ys_ zkLUf+o&M<)Zr!zH{JZKfaMEQh+82XMa)r%$pja(Xch{#eHaZBfHfWq5`GghJgNUr_9%I*@I2fl z8%GX%RqyO<1_n%1AZvdjc5lRIH3GS*xcF@v`-fzoj{guEl3{^_ z>iA04+FPHbYR#;)rW3#&7$scY#plydeP1V#@aj7+(@ekSO3l1*_wU${ecJL-3E)D( zu330s%k~~Uo*U-=Sg!{nQ<}D_x&}2d&NNp6yRHo8h$m?F63%m zp~$FKZC9kA&u2Y_)U(U9AZ>AK`G_3~gL8(=p$wLDOERY=EM26@)ariTCeKbB*ANqW z>GR=Y`*{{2x|)2^#a@gxn7;%HTF)H5AT5VOeuBy-i~Gmm2dL>3b!qFtTP`@6n{}w# z%VO93B?h@k!p_d$)8wpHE_`dsCE+9!)!_DSIa`q(l{{C|OZ~s9W;k%8Paqu;F!`>! zH%~R_o$OVL4R$T35+0H!{5EAry*27l+erx0t%K5W@0EkhO2`CgoSJD?O{bUA#K03M| zrT;GT?YR#X$8;)<^qRyp7)yaQYVP9DhnzPEX-=Pj+wcc=q&kdQ*xvhlsGJNBxGcJk zI1pYqsC=C`uX$@W9CPuSa}iCOuHNkVLE|Dq)E|cf=E$-pGy8SC8!tFXRtg9u%!KV_6XwP8HEOT}(lS*Ie=F+_} zEVfnpKmUe}Uupoc59lOzdlA&&jDH`i_JXh4LNwS=R$CAstq&}3X042gFa=tlr9gE6 zo1hhuY%=i`u83TaIX$nOk~aQaLIk6m#1nL9nIL!yv@9Gq3hP~ol~=sqgm}3MZOFg$SuG>W z`TP*isju^BcjziHS?52P^Z7?&Bh|t=g^fqAy?@p`IFRS7%gA6+0h~_o!3- zTq?HWW^C?*`m;V7`ufa;K2WGcBDiDRPnf;uHi?FzMs{rNS#7s}6CL^Jw-~bNPOG4u zuX=we&mUK!Gy8?(k~o`Ha(aiaobGpS$qS~i8ju;4CiN%VX{N_TKT9Q`$< zOHqEF8{OQj9?P5G`n|K-pEvTkEBdH2RCdgItkRBlZd95=nsA1`zW>49ZKzmLKGUjY zq2F{S>nFeL2uh5d#n%(*WVmt^(gVGsF3z7gTIBxf*P2kGSPH6qQb2t2wpGb^vMeVw zB|+#y$Zj_4{D7AK-LEqjLPE}X5V>QYj{_=&K1H9Xf&dNz1RLKNC~MI-3N=$IBLP(t zhXu4jKNTteX79D1Hn-%CgK^ZEA#LO-G{$UlBUJj5rsisZ&<#5cdTd%2uB}t)} z#;84*?-`n;{z?F7xA3&(8!);j)UF{tEY;)lSY?FX*tvCR+Qny~cA^%=k^EMG1QbdM zN1zYy%V2r)iwE-@GyEZQ#Ab3lPvGd~DX{ZE7UZa?sN@F^$sPbNoV5ZVb_5&%vk%@c$sR5}x*}Ra zLL%(Mhjor6ImF{1)q=s&p0872=K9C)hP0w1xA=0Sr&h_S{a7L%X)~b})UpuaR$vfy z%g3i|`sV+?TQeiL<#JI+(1Nzor@oU$2A3Z~Lr9?e2&W7!Kw_ESdVdh!vFPUJ7BPyq zqmAoFZhn_(`qgvGZ3eR-l{CNQ6yQe6*y_|RP&OL4{xr!I{Q;dV*>!XdHC0m)gA#G)!{iUVy9pQ2k!`V(N{km z98i&3)?nlyZz2;jTy6belB%Q{12!Nq*Ro3p`XF5!&_mtOP35W1)TzDE|v4om{ zUZ74P4t{uHnbR0+rA1DQNAWKVs;IP#GC?>Xb&I{6;Q=-S>bJU!X6~20=9`bZWk5Dc zpU8Jm_e(-|@~0e^*tOGzKXY{kUa}zjUxZP6K=<;_n6rcQt2S*EqSgcUjHMO(7-HCb zhQ0L`xrbdxhW^lT6eRee&V+R9VfDr5`68oMR|1ss!v>)gd`(hhDnz?x&Gps6_noY` z{@tcip5^NEPmLaYmJNL8Y-mQNi#;gx@=6QitX4Y1e(r?Q^0a1TT zcIebeeB<;bF}eExN&qS7 z^i1&W65E;+psK1NDk>m48n`n0>tEZm8@}xwktX7}P6B2QS@>81ju=!`)_N2=+ub7- z;x&A?ho{i^;9mcYw1RxV$-SJ^ys-7wTZUqj@j^8q1oc|iHp4VPU(z?qaIPyl04cn> zGyC71{h;o;bH22c8S)K&lO;)=H%=Boe_4>EQ)z(%*2bOQcaq4!yKyI(JEaVqunu;hxnBr&QkE93ur7-N6On zuXc-Ct*|Hnca{Tbe@=|LoOdDVp7+k21#m^IA4to+<>aT3jFa|vHy)!vuGv5rnIBv- zO|C~3xEq*Rz29h3uXiv;`xd-69cY=idKJGf(wS24vpl3T>Lj#e`lDKAAZVrN(y^d4 zyT_T{0V$C2@I$S}m-LXAFGhrWgwX+6c+3^Gb5e;gDFQ)*y{^ep$3oTKChVQL;Nzw@ zTG#ARf}X}PStarI>|DeWho0>F%%#5aCk2CjNw*TT)1>tILVXW4sAj&` z93O$RdwG`9!rxc+pzTP||J(YBKcA9_;puK3i+87PTn|s5G43bu7Pu|XHFzky1 zpIkf&QAif7wIe6j{z=%@bYJJi4u_Eu73Q)^$-U*Aj15%d>X7Kkv-_pvuvu9eg^awd7#?JZXKdF+Z{=n8E zLXASQ$|&{UtW}XVg_w4Me=q?~`@F+Fj;5f6=Hg)kd4xJ(vJgstJ6F5DbDLadyS%C_xHyxMOar?dziXNg15jXJr5s zV$$8oUJ#ZH8Qg7{z6qmOPXn;vmfa+}nDo64wr+4i-cjv@G%wb5I@h5xJ+ic#CCV|C zPq#4VI{=14ugvZ@pE=2PNsNz5YkW}?Q0skV7A&p4LXa}%lr;zM)NoAxWs^RgQ18IK z$~V=!$CB@<*5!L+8+VHwQhIx`dik2WZKXNzW@LL diff --git a/cv/super_resolution/ttsr/pytorch/train.sh b/cv/super_resolution/ttsr/pytorch/train.sh deleted file mode 100755 index 7b5b312d9..000000000 --- a/cv/super_resolution/ttsr/pytorch/train.sh +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - - -### training TTSR -GPUS=$1 - -python3 -m torch.distributed.launch --nproc_per_node=$GPUS --use_env \ - main.py --save_dir checkpoints \ - --reset True \ - --log_file_name train.log \ - --num_gpu 4 \ - --num_workers 4 \ - --dataset CUFED \ - --dataset_dir data/CUFED \ - --n_feats 64 \ - --lr_rate 1e-4 \ - --lr_rate_dis 1e-4 \ - --lr_rate_lte 1e-5 \ - --rec_w 1 \ - --per_w 1e-2 \ - --tpl_w 1e-2 \ - --adv_w 1e-3 \ - --batch_size 4 \ - --num_init_epochs 0 \ - --num_epochs 50 \ - --print_every 100 \ - --save_every 10 \ - --val_every 10 - -# ### training TTSR -# python main.py --save_dir ./train/CUFED/TTSR \ -# --reset True \ -# --log_file_name train.log \ -# --num_gpu 1 \ -# --num_workers 9 \ -# --dataset CUFED \ -# --dataset_dir /home/v-fuyang/Data/CUFED/ \ -# --n_feats 64 \ -# --lr_rate 1e-4 \ -# --lr_rate_dis 1e-4 \ -# --lr_rate_lte 1e-5 \ -# --rec_w 1 \ -# --per_w 1e-2 \ -# --tpl_w 1e-2 \ -# --adv_w 1e-3 \ -# --batch_size 9 \ -# --num_init_epochs 2 \ -# --num_epochs 50 \ -# --print_every 600 \ -# --save_every 10 \ -# --val_every 10 - - -# ### training TTSR-rec -# python main.py --save_dir ./train/CUFED/TTSR-rec \ -# --reset True \ -# --log_file_name train.log \ -# --num_gpu 1 \ -# --num_workers 9 \ -# --dataset CUFED \ -# --dataset_dir /home/v-fuyang/Data/CUFED/ \ -# --n_feats 64 \ -# --lr_rate 1e-4 \ -# --lr_rate_dis 1e-4 \ -# --lr_rate_lte 1e-5 \ -# --rec_w 1 \ -# --per_w 0 \ -# --tpl_w 0 \ -# --adv_w 0 \ -# --batch_size 9 \ -# --num_init_epochs 0 \ -# --num_epochs 200 \ -# --print_every 600 \ -# --save_every 10 \ -# --val_every 10 diff --git a/cv/super_resolution/ttsr/pytorch/trainer.py b/cv/super_resolution/ttsr/pytorch/trainer.py deleted file mode 100755 index e41495818..000000000 --- a/cv/super_resolution/ttsr/pytorch/trainer.py +++ /dev/null @@ -1,231 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - -from utils import calc_psnr_and_ssim -from model import Vgg19 - -import os -import numpy as np -from imageio import imread, imsave -from PIL import Image -import time - -import torch -import torch.nn as nn -import torch.nn.functional as F -import torch.optim as optim -import torchvision.utils as utils - - -class Trainer(): - def __init__(self, args, logger, dataloader, model, loss_all): - self.args = args - self.logger = logger - self.dataloader = dataloader - self.model = model - self.loss_all = loss_all - self.master_device = True - if args.cpu: - self.device = torch.device('cpu') - elif args.num_gpu > 1: - cur_gpu = int(os.environ['LOCAL_RANK']) - self.device = torch.device('cuda:%d'%(cur_gpu)) - env_rank = int(os.environ["RANK"]) - if env_rank != 0: - self.master_device = False - else: - self.device = torch.device('cuda') - - self.vgg19 = Vgg19.Vgg19(requires_grad=False).to(self.device) - - self.params = [ - {"params": filter(lambda p: p.requires_grad, self.model.MainNet.parameters() if - args.num_gpu==1 else self.model.module.MainNet.parameters()), - "lr": args.lr_rate - }, - {"params": filter(lambda p: p.requires_grad, self.model.LTE.parameters() if - args.num_gpu==1 else self.model.module.LTE.parameters()), - "lr": args.lr_rate_lte - } - ] - self.optimizer = optim.Adam(self.params, betas=(args.beta1, args.beta2), eps=args.eps) - self.scheduler = optim.lr_scheduler.StepLR( - self.optimizer, step_size=self.args.decay, gamma=self.args.gamma) - self.max_psnr = 0. - self.max_psnr_epoch = 0 - self.max_ssim = 0. - self.max_ssim_epoch = 0 - - def load(self, model_path=None): - if (model_path): - self.logger.info('load_model_path: ' + model_path) - #model_state_dict_save = {k.replace('module.',''):v for k,v in torch.load(model_path).items()} - model_state_dict_save = {k:v for k,v in torch.load(model_path, map_location=self.device).items()} - model_state_dict = self.model.state_dict() - model_state_dict.update(model_state_dict_save) - self.model.load_state_dict(model_state_dict) - - def prepare(self, sample_batched): - for key in sample_batched.keys(): - sample_batched[key] = sample_batched[key].to(self.device) - return sample_batched - - def train(self, current_epoch=0, is_init=False): - self.model.train() - if (not is_init): - self.scheduler.step() - if self.master_device: - self.logger.info('Current epoch learning rate: %e' %(self.optimizer.param_groups[0]['lr'])) - - start = time.time() - for i_batch, sample_batched in enumerate(self.dataloader['train']): - self.optimizer.zero_grad() - - sample_batched = self.prepare(sample_batched) - lr = sample_batched['LR'] - lr_sr = sample_batched['LR_sr'] - hr = sample_batched['HR'] - ref = sample_batched['Ref'] - ref_sr = sample_batched['Ref_sr'] - sr, S, T_lv3, T_lv2, T_lv1 = self.model(lr=lr, lrsr=lr_sr, ref=ref, refsr=ref_sr) - - ### calc loss - is_print = ((i_batch + 1) % self.args.print_every == 0) ### flag of print - - ### print fps - if is_print and self.master_device: - torch.cuda.synchronize() - fps = (self.args.print_every * self.args.batch_size * torch.distributed.get_world_size()) / (time.time()-start) - start = time.time() - self.logger.info('training fps = {}'.format(fps)) - - rec_loss = self.args.rec_w * self.loss_all['rec_loss'](sr, hr) - loss = rec_loss - if (is_print) and self.master_device: - self.logger.info( ('init ' if is_init else '') + 'epoch: ' + str(current_epoch) + - '\t batch: ' + str(i_batch+1) ) - self.logger.info( 'rec_loss: %.10f' %(rec_loss.item()) ) - - if (not is_init): - if ('per_loss' in self.loss_all): - sr_relu5_1 = self.vgg19((sr + 1.) / 2.) - with torch.no_grad(): - hr_relu5_1 = self.vgg19((hr.detach() + 1.) / 2.) - per_loss = self.args.per_w * self.loss_all['per_loss'](sr_relu5_1, hr_relu5_1) - loss += per_loss - if (is_print) and self.master_device: - self.logger.info( 'per_loss: %.10f' %(per_loss.item()) ) - if ('tpl_loss' in self.loss_all): - sr_lv1, sr_lv2, sr_lv3 = self.model(sr=sr) - tpl_loss = self.args.tpl_w * self.loss_all['tpl_loss'](sr_lv3, sr_lv2, sr_lv1, - S, T_lv3, T_lv2, T_lv1) - loss += tpl_loss - if (is_print) and self.master_device: - self.logger.info( 'tpl_loss: %.10f' %(tpl_loss.item()) ) - if ('adv_loss' in self.loss_all): - adv_loss = self.args.adv_w * self.loss_all['adv_loss'](sr, hr) - loss += adv_loss - if (is_print) and self.master_device: - self.logger.info( 'adv_loss: %.10f' %(adv_loss.item()) ) - - loss.backward() - self.optimizer.step() - - end = time.time() - print('training fps = {}'.format(1000 / (end - start))) - - if ((not is_init) and current_epoch % self.args.save_every == 0) and self.master_device: - self.logger.info('saving the model...') - tmp = self.model.state_dict() - model_state_dict = {key.replace('module.',''): tmp[key] for key in tmp if - (('SearchNet' not in key) and ('_copy' not in key))} - model_name = self.args.save_dir.strip('/')+'/model/model_'+str(current_epoch).zfill(5)+'.pt' - torch.save(model_state_dict, model_name) - - def evaluate(self, current_epoch=0): - self.logger.info('Epoch ' + str(current_epoch) + ' evaluation process...') - - if (self.args.dataset == 'CUFED'): - self.model.eval() - with torch.no_grad(): - psnr, ssim, cnt = 0., 0., 0 - for i_batch, sample_batched in enumerate(self.dataloader['test']['1']): - cnt += 1 - sample_batched = self.prepare(sample_batched) - lr = sample_batched['LR'] - lr_sr = sample_batched['LR_sr'] - hr = sample_batched['HR'] - ref = sample_batched['Ref'] - ref_sr = sample_batched['Ref_sr'] - - sr, _, _, _, _ = self.model(lr=lr, lrsr=lr_sr, ref=ref, refsr=ref_sr) - if (self.args.eval_save_results): - sr_save = (sr+1.) * 127.5 - sr_save = np.transpose(sr_save.squeeze().round().cpu().numpy(), (1, 2, 0)).astype(np.uint8) - imsave(os.path.join(self.args.save_dir, 'save_results', str(i_batch).zfill(5)+'.png'), sr_save) - - ### calculate psnr and ssim - _psnr, _ssim = calc_psnr_and_ssim(sr.detach(), hr.detach()) - - psnr += _psnr - ssim += _ssim - - psnr_ave = psnr / cnt - ssim_ave = ssim / cnt - self.logger.info('Ref PSNR (now): %.3f \t SSIM (now): %.4f' %(psnr_ave, ssim_ave)) - if (psnr_ave > self.max_psnr): - self.max_psnr = psnr_ave - self.max_psnr_epoch = current_epoch - if (ssim_ave > self.max_ssim): - self.max_ssim = ssim_ave - self.max_ssim_epoch = current_epoch - self.logger.info('Ref PSNR (max): %.3f (%d) \t SSIM (max): %.4f (%d)' - %(self.max_psnr, self.max_psnr_epoch, self.max_ssim, self.max_ssim_epoch)) - - self.logger.info('Evaluation over.') - - def test(self): - self.logger.info('Test process...') - self.logger.info('lr path: %s' %(self.args.lr_path)) - self.logger.info('ref path: %s' %(self.args.ref_path)) - - ### LR and LR_sr - LR = imread(self.args.lr_path) - h1, w1 = LR.shape[:2] - LR_sr = np.array(Image.fromarray(LR).resize((w1*4, h1*4), Image.BICUBIC)) - - ### Ref and Ref_sr - Ref = imread(self.args.ref_path) - h2, w2 = Ref.shape[:2] - h2, w2 = h2//4*4, w2//4*4 - Ref = Ref[:h2, :w2, :] - Ref_sr = np.array(Image.fromarray(Ref).resize((w2//4, h2//4), Image.BICUBIC)) - Ref_sr = np.array(Image.fromarray(Ref_sr).resize((w2, h2), Image.BICUBIC)) - - ### change type - LR = LR.astype(np.float32) - LR_sr = LR_sr.astype(np.float32) - Ref = Ref.astype(np.float32) - Ref_sr = Ref_sr.astype(np.float32) - - ### rgb range to [-1, 1] - LR = LR / 127.5 - 1. - LR_sr = LR_sr / 127.5 - 1. - Ref = Ref / 127.5 - 1. - Ref_sr = Ref_sr / 127.5 - 1. - - ### to tensor - LR_t = torch.from_numpy(LR.transpose((2,0,1))).unsqueeze(0).float().to(self.device) - LR_sr_t = torch.from_numpy(LR_sr.transpose((2,0,1))).unsqueeze(0).float().to(self.device) - Ref_t = torch.from_numpy(Ref.transpose((2,0,1))).unsqueeze(0).float().to(self.device) - Ref_sr_t = torch.from_numpy(Ref_sr.transpose((2,0,1))).unsqueeze(0).float().to(self.device) - - self.model.eval() - with torch.no_grad(): - sr, _, _, _, _ = self.model(lr=LR_t, lrsr=LR_sr_t, ref=Ref_t, refsr=Ref_sr_t) - sr_save = (sr+1.) * 127.5 - sr_save = np.transpose(sr_save.squeeze().round().cpu().numpy(), (1, 2, 0)).astype(np.uint8) - save_path = os.path.join(self.args.save_dir, 'save_results', os.path.basename(self.args.lr_path)) - imsave(save_path, sr_save) - self.logger.info('output path: %s' %(save_path)) - - self.logger.info('Test over.') diff --git a/cv/super_resolution/ttsr/pytorch/utils.py b/cv/super_resolution/ttsr/pytorch/utils.py deleted file mode 100755 index 685b2713f..000000000 --- a/cv/super_resolution/ttsr/pytorch/utils.py +++ /dev/null @@ -1,163 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. - - -import math -import numpy as np -import logging -import cv2 -import os -import shutil - -import torch -import torch.nn as nn -import torch.nn.functional as F - - -class Logger(object): - def __init__(self, log_file_name, logger_name, log_level=logging.DEBUG): - ### create a logger - self.__logger = logging.getLogger(logger_name) - - ### set the log level - self.__logger.setLevel(log_level) - - ### create a handler to write log file - file_handler = logging.FileHandler(log_file_name) - - ### create a handler to print on console - console_handler = logging.StreamHandler() - - ### define the output format of handlers - formatter = logging.Formatter('[%(asctime)s] - [%(filename)s file line:%(lineno)d] - %(levelname)s: %(message)s') - file_handler.setFormatter(formatter) - console_handler.setFormatter(formatter) - - ### add handler to logger - self.__logger.addHandler(file_handler) - self.__logger.addHandler(console_handler) - - def get_log(self): - return self.__logger - - -def mkExpDir(args): - if (os.path.exists(args.save_dir)): - if (not args.reset): - raise SystemExit('Error: save_dir "' + args.save_dir + '" already exists! Please set --reset True to delete the folder.') - else: - shutil.rmtree(args.save_dir) - - os.makedirs(args.save_dir) - # os.makedirs(os.path.join(args.save_dir, 'img')) - - if ((not args.eval) and (not args.test)): - os.makedirs(os.path.join(args.save_dir, 'model')) - - if ((args.eval and args.eval_save_results) or args.test): - os.makedirs(os.path.join(args.save_dir, 'save_results')) - - args_file = open(os.path.join(args.save_dir, 'args.txt'), 'w') - for k, v in vars(args).items(): - args_file.write(k.rjust(30,' ') + '\t' + str(v) + '\n') - - return - - -class MeanShift(nn.Conv2d): - def __init__(self, rgb_range, rgb_mean, rgb_std, sign=-1): - super(MeanShift, self).__init__(3, 3, kernel_size=1) - std = torch.Tensor(rgb_std) - self.weight.data = torch.eye(3).view(3, 3, 1, 1) - self.weight.data.div_(std.view(3, 1, 1, 1)) - self.bias.data = sign * rgb_range * torch.Tensor(rgb_mean) - self.bias.data.div_(std) - # self.requires_grad = False - self.weight.requires_grad = False - self.bias.requires_grad = False - - -def calc_psnr(img1, img2): - ### args: - # img1: [h, w, c], range [0, 255] - # img2: [h, w, c], range [0, 255] - diff = (img1 - img2) / 255.0 - diff[:,:,0] = diff[:,:,0] * 65.738 / 256.0 - diff[:,:,1] = diff[:,:,1] * 129.057 / 256.0 - diff[:,:,2] = diff[:,:,2] * 25.064 / 256.0 - - diff = np.sum(diff, axis=2) - mse = np.mean(np.power(diff, 2)) - return -10 * math.log10(mse) - - -def calc_ssim(img1, img2): - def ssim(img1, img2): - C1 = (0.01 * 255)**2 - C2 = (0.03 * 255)**2 - - img1 = img1.astype(np.float64) - img2 = img2.astype(np.float64) - kernel = cv2.getGaussianKernel(11, 1.5) - window = np.outer(kernel, kernel.transpose()) - - mu1 = cv2.filter2D(img1, -1, window)[5:-5, 5:-5] # valid - mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5] - mu1_sq = mu1**2 - mu2_sq = mu2**2 - mu1_mu2 = mu1 * mu2 - sigma1_sq = cv2.filter2D(img1**2, -1, window)[5:-5, 5:-5] - mu1_sq - sigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sq - sigma12 = cv2.filter2D(img1 * img2, -1, window)[5:-5, 5:-5] - mu1_mu2 - - ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) * - (sigma1_sq + sigma2_sq + C2)) - return ssim_map.mean() - - ### args: - # img1: [h, w, c], range [0, 255] - # img2: [h, w, c], range [0, 255] - # the same outputs as MATLAB's - border = 0 - img1_y = np.dot(img1, [65.738,129.057,25.064])/256.0+16.0 - img2_y = np.dot(img2, [65.738,129.057,25.064])/256.0+16.0 - if not img1.shape == img2.shape: - raise ValueError('Input images must have the same dimensions.') - h, w = img1.shape[:2] - img1_y = img1_y[border:h-border, border:w-border] - img2_y = img2_y[border:h-border, border:w-border] - - if img1_y.ndim == 2: - return ssim(img1_y, img2_y) - elif img1.ndim == 3: - if img1.shape[2] == 3: - ssims = [] - for i in range(3): - ssims.append(ssim(img1, img2)) - return np.array(ssims).mean() - elif img1.shape[2] == 1: - return ssim(np.squeeze(img1), np.squeeze(img2)) - else: - raise ValueError('Wrong input image dimensions.') - - -def calc_psnr_and_ssim(sr, hr): - ### args: - # sr: pytorch tensor, range [-1, 1] - # hr: pytorch tensor, range [-1, 1] - - ### prepare data - sr = (sr+1.) * 127.5 - hr = (hr+1.) * 127.5 - if (sr.size() != hr.size()): - h_min = min(sr.size(2), hr.size(2)) - w_min = min(sr.size(3), hr.size(3)) - sr = sr[:, :, :h_min, :w_min] - hr = hr[:, :, :h_min, :w_min] - - img1 = np.transpose(sr.squeeze().round().cpu().numpy(), (1,2,0)) - img2 = np.transpose(hr.squeeze().round().cpu().numpy(), (1,2,0)) - - psnr = calc_psnr(img1, img2) - ssim = calc_ssim(img1, img2) - - return psnr, ssim \ No newline at end of file -- Gitee